2

So I'm pretty much a noob in IA32 assembly language. I tried compiling this C function into IA32 assembly (-mpreferred-stack-boundary=2):

__attribute__((cdecl))
int odd_sum(int a[], int n, int sum) {
  if (n == 0) return sum;
  else if ((a[n-1] % 2) == 0)
    return odd_sum(a, n-1, sum);
  else return odd_sum(a, n-1, sum + a[n-1]);
}

and the GCC outputs this:

    .file   "test.c"
    .text
    .globl  _odd_sum
    .def    _odd_sum;   .scl    2;  .type   32; .endef
_odd_sum:
LFB0:
    .cfi_startproc
    pushl   %ebp
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp
    .cfi_def_cfa_register 5
    subl    $12, %esp
    cmpl    $0, 12(%ebp)
    jne L2
    movl    16(%ebp), %eax
    jmp L3
L2:
    movl    12(%ebp), %eax
    addl    $1073741823, %eax
    leal    0(,%eax,4), %edx
    movl    8(%ebp), %eax
    addl    %edx, %eax
    movl    (%eax), %eax
    andl    $1, %eax
    testl   %eax, %eax
    jne L4
    movl    12(%ebp), %eax
    leal    -1(%eax), %edx
    movl    16(%ebp), %eax
    movl    %eax, 8(%esp)
    movl    %edx, 4(%esp)
    movl    8(%ebp), %eax
    movl    %eax, (%esp)
    call    _odd_sum
    jmp L3
L4:
    movl    12(%ebp), %eax
    addl    $1073741823, %eax
    leal    0(,%eax,4), %edx
    movl    8(%ebp), %eax
    addl    %edx, %eax
    movl    (%eax), %edx
    movl    16(%ebp), %eax
    addl    %eax, %edx
    movl    12(%ebp), %eax
    subl    $1, %eax
    movl    %edx, 8(%esp)
    movl    %eax, 4(%esp)
    movl    8(%ebp), %eax
    movl    %eax, (%esp)
    call    _odd_sum
L3:
    leave
    .cfi_restore 5
    .cfi_def_cfa 4, 4
    ret
    .cfi_endproc
LFE0:
    .ident  "GCC: (MinGW.org GCC-8.2.0-3) 8.2.0"

What I am not able to comprehend are these 2 lines:

   addl    $1073741823, %eax
   leal    0(,%eax,4), %edx

I understand those 2 lines should have something to do with the a[n-1], but I can't seem to be able to understand what exactly they do in the process. Can someone help me with this problem please?

  • 2
    As a general hint, if trying to understand assembly output, you should enable optimization. You'll get code that's much more concise, sensible, and overall a better model to learn from. – Nate Eldredge Jan 07 '21 at 06:31
  • @NateEldredge great advice bro! I just googled how to turn it on and it looks much simpler. Thanks bro! – Nguyễn Nam Jan 07 '21 at 06:46
  • Expanding on Nate's point: [How to remove "noise" from GCC/clang assembly output?](https://stackoverflow.com/q/38552116) – Peter Cordes Jan 07 '21 at 20:36

2 Answers2

9

It is just a fancy way of computing the offset into the array a[n-1].

1073741823 is 0x3fffffff. If n is 3, for example, it will add them and get 0x40000002. Then it multiplies by 4 with the second instruction, which results in 0x00000008, discarding the top bits.

So we are left with an offset of 8 bytes, which is exactly the offset (in bytes) that you need for a[n-1], i.e. a[2] (when the size of an int is 4 bytes).

Acorn
  • 24,970
  • 5
  • 40
  • 69
0

To get a more understandable output with the -S flag:

create assembler code:

c++ -S -fverbose-asm -g -O2 (other optimizaton flags) test.cc -o test.s

create asm interlaced with source lines:

as -alhnd test.s > test.lst

Arthur Kalliokoski
  • 1,627
  • 12
  • 12
  • 1
    Or look at it on Godbolt which colour-codes source and asm and highlights on mouseover to help you match them up. See [How to remove "noise" from GCC/clang assembly output?](https://stackoverflow.com/q/38552116) – Peter Cordes Jan 07 '21 at 20:37
  • @PeterCordes Thanks for the link! I'll try it out later – Arthur Kalliokoski Jan 07 '21 at 23:11