For a short homework assignment in CS Architecture, we were made to translate the following IA-32 assembly into C. I've translated it properly (as far as I know) but the code doesn't appear to do anything particularly useful. My professor typically gives us problems like this which end up doing something: our last assignment like this was a bit pop_count. Looking at the C code below, does this function do anything useful? Some algorithm maybe?
The code appears below (I've added comments to each ASM line).
// The following variables x, y, z are located at (%ebp) +8, +12, +16 respectively
// x, y, and z, are scanned from the terminal and passed into the function
movl 12(%ebp), %edx // moves long y to register %edx
subl 16(%ebp), %edx // subtracts longs: y = y - z
movl %edx, %eax // moves long y to register %eax
sall $31, %eax // left shift all bits in long y by 31 places
sarl $31, %eax // arithmetic right shift long y by 31 places
imull 8(%ebp), %edx // multiply longs: unshifted y = y * x
xorl %edx, %eax // XOR operation: shifted y = shifted y ^ unshifted y
// The returned value is stored in register %eax
The effective takeaway is we subtract z from y, then populate every bit with the least significant bit to form either zero or MAX_UINT. This is XOR'd with the product of (y - z) * x and returned.
My translation into C:
return (((y - z) << 31) >> 31) ^ ((y - z) * x);
// In more words:
int f;
y -= z;
f = y << 31; // These two lines populate all bits with the lsb
f = f >> 31; // Effectively, if y-z is odd: f is ~0; else f is 0
y *= x;
return y ^ f;
// Or, using people logic:
y -= z;
if (y % 2 == 0) return y * x;
return -(y * x) - 1;
// If the y-z is odd, the result will be: -1 * ((y - z) * x) - 1
// If the y-z is even, the result will be: (y - z) * x
For clarification, this is not part of the HW assignment; I've completed the assignment by translating the code, but I'm interested in knowing why he gave us this code to begin with.
An assembly language is a programming language that communicates with the hardware of a computer directly. An assembly language allows a software developer to code using words and expressions that can be easier to understand and interpret than the binary or hexadecimal data the computer stores and reads.
Assemblers. In order to convert assembly language into machine code it needs to be translated using an assembler . This converts each statement into the specific machine code needed for the hardware on which it is being run.
Assembly language makes it faster to run complex code. It is incredibly difficult to write code in assembly language compared to high-level languages. Simpler for humans to understand than machine-level language. The syntax is hard to learn and can be difficult to remember or use quickly.
Assembly language has a very strong correspondence with the architecture's machine code instruction and is specific only to that machine. Therefore, different machines have different assembly languages. This type of language makes use of symbols to represent an operation or instruction.
My guess is that your professor was trying to illustrate how you can use bit-shifting/masking operations to avoid branches. If you were to naively translate
y -= z;
if (y % 2 == 0) return y * x;
return -(y * x) - 1;
into machine code, you would use a branch instruction for the conditional. Branches can dramatically slow down your code, especially if they're inside a tight loop*. The assembly code and your preceding C code have the effect of using a conditional on whether or not y - z
is even, but only using arithmetic instructions.
*If you're not already familiar with this fact, this SO answer has one of my favorite illustrations of it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With