-2

I have a simple c program that multiplies and adds three variables and returns the results. I have compiled the code into assembly language (ATT format) on a 32 bit machine. I am trying to learn assembly code, and while I can understand some of the lines, I need help understanding why for example leal (%edx, %edx, 2), %edx, would be done in the following code, like what would be the result to that, what would it accomplish?

Assembly code

.file   "calc.c"
        .text
.globl calc
        .type   calc, @function
calc:
        pushl   %ebp                   #prolog
        movl    %esp, %ebp             #prolog
        movl    8(%ebp), %edx          #move %ebp +8 into %edx 
        movl    16(%ebp), %ecx         #move %ebp +16 into %ecx
        leal    (%edx,%edx,2), %edx
        movl    12(%ebp), %eax
        leal    (%edx,%eax,2), %eax
        movl    %ecx, %edx
        sall    $4, %edx
        subl    %ecx, %edx
        addl    %edx, %eax
        popl    %ebp
        ret
        .size   calc, .-calc
        .ident  "GCC: (Ubuntu 4.3.3-5ubuntu4) 4.3.3"
        .section        .note.GNU-stack,"",@progbits

C program

#include <stdio.h>
int calc(int x, int y, int z){
    return 3*x + 2*y + 15*z;
}
int main(void) {
    int x = 2;
    int y = 6;
    int z = 11;

    int result = calc(x,y,z);

    printf("x=%d, y=%d, z=%d, result=%d\n", x,y,z,result);
    return 0;
}

Could someone help me trace the problem starting from the prolog.

Jester
  • 56,577
  • 4
  • 81
  • 125
below_avg_st
  • 187
  • 15

2 Answers2

1

Maybe it can be best explained with the C function code in mind:

int calc(int x, int y, int z){
    return 3*x + 2*y + 15*z;
}

Look at parameter x. It is multiplied by 3 so it is easily recognizable in the assembly code:

movl    8(%ebp), %edx

....

leal    (%edx,%edx,2), %edx
movl    12(%ebp), %eax
leal    (%edx,%eax,2), %eax

It moves the value of argument x into edx. The address of argument x in the stack frame is ebp+8. Then later on with leal it adds edx to 2*edx and stores the value in edx. That equals 3*x.
Then loads argument y into eax, wich you can recognize easily because y is a 32bit int so it is 4 bytes past the start address of x.
Next leal adds edx (i.e. 3*x) with 2*eax (i.e. 2*y) and stores the result in eax, thus in eax you have 3*x + 2*y.
And so on...

-1

I suggest you start by not optimizing your code. Your optimizer is playing games on you. If your system is pushing arguments in reverse order

8(%ebp) = x = edx
12(%ebp) = y = eax
16(%ebp) = z = ecx

With assembler games:

(%edx,%edx,2) = edx + 2 x edx = 3 x edx  (3 x X)
(%edx,%eax,2)  = edx + 2 x eax = (3 x X + 2 * y)

  movl    %ecx, %edx
  sall    $4, %edx = (16 x Z)
  subl    %ecx, %edx = 15 x Z
user3344003
  • 20,574
  • 3
  • 26
  • 62
  • 2
    That is `gcc -O0` output. You can tell by the spill/reload between every C statement to support changing C variables with a debugger, and using gdb's `jump` command. `lea` *is* how you multiply by 3 in x86. If optimizing for code-size, you could `imul $3, (mem), %eax`, but gcc chooses not to do that at `-O0`and uses its normal method for multiplying by small constants. – Peter Cordes Oct 31 '17 at 17:14