Reputation: 71
I'm studying for a retake and I'm having problems reading assembly. Question:
Starting with C code of the form
1 int test(int x, int y) {
2 int val = ;
3 if ( ) {
4 if ( )
5 val = ;
6 else
7 val = ;
8 } else if ( )
9 val = ;
10 return val;
11}
gcc generates the following assembly code:
x at %ebp+8, y at %ebp+12
1 movl 8(%ebp), %eax
2 movl 12(%ebp), %edx
3 cmpl $-3, %eax
4 jge .L2
5 cmpl %edx, %eax
6 jle .L3
7 imull %edx, %eax
8 jmp .L4
9 .L3:
10 leal (%edx,%eax), %eax
11 jmp .L4
12 .L2:
Section 3.6 Control 197
13 cmpl $2, %eax
14 jg .L5
15 xorl %edx, %eax
16 jmp .L4
17 .L5:
18 subl %edx, %eax
19 .L4:
Fill in the missing expressions in the C code. To make the code fit into the C code template, you will need to undo some of the reordering of computations done by gcc.
So I tried this, and I though it would be:
int val = x+y
if -3>x
if 2>x
val = x^y
else
val = x*y
else if y<x
val = x-y
It compares -3 > x and then it jumps to L2, so I thought that's where we continue (and everytime it jumps, that's where I continued reading). However, it just continues reading it from top to bottom (why?). Next, when -3 > x, I assumed 2 > x, but now they're turning it around in x>2. Yet, it still is y < x and not x < y. So I basically don't get why my entire order of reading the code is wrong, and why they sometimes compare the second argument with the first, and sometimes the other way around. The correct answer is:
int val = x^y
if x<-3
if y<x
val = x*y
else
val = x+y
else if x>2
val = x-y
Upvotes: 1
Views: 1113
Reputation: 46960
Most C implementations push arguments on the stack last first, and x86 stacks grow downward. From this:
1 movl 8(%ebp), %eax
2 movl 12(%ebp), %edx
we can verfiy edx
holds the word pushed first because it has the higher address. So it's y
. And eax
is x
.
This comparison is x
? -3
. Note the reversal of arguments due to AT&T assembly conventions.
3 cmpl $-3, %eax
So here we jump to L2
if x >= -3
.
4 jge .L2
Similarly, here we jump if x <= y
5 cmpl %edx, %eax
6 jle .L3
Here we compute a returnValue = x * y
and jump to the end. Note the compiler has determined that x
is no longer needed, so it can use the eax
register for the return value starting here.
7 imull %edx, %eax
8 jmp .L4
Here we are computing returnValue = x + y
.
9 .L3:
10 leal (%edx,%eax), %eax
11 jmp .L4
Here we are jumping if x > 2
.
12 .L2:
13 cmpl $2, %eax
14 jg .L5
Here we compute returnValue = x ^ y
.
15 xorl %edx, %eax
16 jmp .L4
Here we have returnValue = x - y
.
17 .L5:
18 subl %edx, %eax
19 .L4:
Translate this into C that uses gotos as an intermediate step:
if (x >= -3) goto L2;
if (x <= y) goto L3;
val = x * y;
goto L4
L3: val = x + y;
goto L4
L2: if (x > 2) goto L5;
val = x ^ y;
goto L4
L5: val = x - y;
L4: return val;
Note that most compilers seeing if (x > y) x *= 3; else y -= 4;
will reverse the sense of the comparison and produce the assembly-level logic
if (x <= y) goto L1;
x *= 3;
goto L2;
L1: y -= 4;
L2:
Using this convention to re-arrange the above, we'd get:
int test(int x, int y) {
int val = x; // Compiler uses eax for both x and return
if (x < -3) {
if (x > y)
val = x * y;
else
val = x + y;
} else {
if (x <= 2)
val = x ^ y;
else
val = x - y;
}
return val;
}
The last else
clause is missing from the provided original C. Our only choice is to infer that the original code put x-y
in val
initially, and the compiler did an optimization so the value isn't computed unless its actually returned:
int test(int x, int y) {
int val = x - y; // Compiler doesn't compute x-y unless it's returned!
if (x < -3) {
if (x > y)
val = x * y;
else
val = x + y;
} else if (x <= 2)
val = x ^ y;
return val;
}
Upvotes: 4