10

I'm a total noob at assembly, just poking around a bit to see what's going on. Anyway, I wrote a very simple function:

void multA(double *x,long size)
{
  long i;
  for(i=0; i<size; ++i){
    x[i] = 2.4*x[i];
  }
}

I compiled it with:

gcc -S -m64 -O2 fun.c

And I get this:

    .file   "fun.c"
    .text
    .p2align 4,,15
    .globl  multA
    .type   multA, @function
multA:
.LFB34:
    .cfi_startproc
    testq   %rsi, %rsi
    jle .L1
    movsd   .LC0(%rip), %xmm1
    xorl    %eax, %eax
    .p2align 4,,10
    .p2align 3
.L3:
    movsd   (%rdi,%rax,8), %xmm0
    mulsd   %xmm1, %xmm0
    movsd   %xmm0, (%rdi,%rax,8)
    addq    $1, %rax
    cmpq    %rsi, %rax
    jne .L3
.L1:
    rep
    ret
    .cfi_endproc
.LFE34:
    .size   multA, .-multA
    .section    .rodata.cst8,"aM",@progbits,8
    .align 8
.LC0:
    .long   858993459
    .long   1073951539
    .ident  "GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3"
    .section    .note.GNU-stack,"",@progbits

The assembly output makes sense to me (mostly) except for the line xorl %eax, %eax. From googling, I gather that the purpose of this is simply to set %eax to zero, which in this case corresponds to my iterator long i;.

However, unless I am mistaken, %eax is a 32-bit register. So it seems to me that this should actually be xorq %rax, %rax, particularly since this is holding a 64-bit long int. Moreover, further down in the code, it actually uses the 64-bit register %rax to do the iterating, which never gets initialized outside of xorl %eax %eax, which would seem to only zero out the lower 32 bits of the register.

Am I missing something?

Also, out of curiosity, why are there two .long constants there at the bottom? The first one, 858993459 is equal to the double floating-point representation of 2.4 but I can't figure out what the second number is or why it is there.

mrip
  • 14,913
  • 4
  • 40
  • 58

2 Answers2

9

I gather that the purpose of this is simply to set %eax to zero

Yes.

which in this case corresponds to my iterator long i;.

No. Your i is uninitialized in the declaration. Strictly speaking, that operation corresponds to the i = 0 expression in the for loop.

However, unless I am mistaken, %eax is a 32-bit register. So it seems to me that this should actually be xorq %rax, %rax, particularly since this is holding a 64-bit long int.

But clearing the lower double word of the register clears the entire register. This is not intuitive, but it's implicit.

  • 1
    Thanks. I figured that must be the case, but I couldn't find it documented anywhere. Like I said, I'm a total noob. – mrip Sep 29 '13 at 19:21
  • 1
    Learned something new today, didn't know that clearing EAX would clear RAX. However, I'm wondering whether 'clearing the lower word' is the right way to put it, isn't the word size 64 bits on an AMD64 architecture? – us2012 Sep 29 '13 at 19:21
  • 2
    @us2012 the usual definition of "word size" are simply ignored by Intel. A word is 16 bits, always. 32 = dword, 64 = qword. – harold Sep 29 '13 at 19:23
  • 2
    @us2012 I'm not a great hardware hacker, so you may actually be right, but in my read (apart from picky C terminology which I don't always apply in general cases), byte = 1 byte or 8 bits, word = 2 bytes or 16 bits, double word = 4 bytes or 32 bits, quad word = 8 bytes or 64 bits. –  Sep 29 '13 at 19:23
  • 1
    @mrip No problem, as I've stated, this is not obvious, so this is a legitimate beginner question. (I rarely say this so be proud :P) –  Sep 29 '13 at 19:24
  • @us2012 http://stackoverflow.com/questions/11177137/why-do-most-x64-instructions-zero-the-upper-part-of-a-32-bit-register – phuclv Jan 03 '14 at 13:37
  • @user529758 I gather that the purpose of this is simply to set %eax to zero Yes. which in this case corresponds to my iterator long i;. No. Your i is uninitialized in the declaration. Strictly speaking, that operation corresponds to the i = 0 expression in the for loop. However, unless I am mistaken, %eax is a 32-bit register. So it seems to me that this should actually be xorq %rax, %rax, particularly since this is holding a 64-bit long int. But clearing the lower double word of the register clears the entire register. This is not intuitive, but it's implicit. why is it so? – Andrew Nov 04 '16 at 20:58
  • @andrewFromNowhere https://stackoverflow.com/questions/11177137/why-do-most-x64-instructions-zero-the-upper-part-of-a-32-bit-register – Navin Nov 07 '16 at 05:20
2

Just to answer the second part: .long means 32 bit, and the two integral constants side-by-side form the IEEE-754 representation of the double 2.4:

Dec: 1073951539  858993459
Hex: 0x40033333 0x33333333

     400 3333333333333
     S+E Mantissa

The exponent is offset by 1023, so the actual exponent is 0x400 − 1023 = 1. The leading "one" in the mantissa is implied, so it's 21 × 0b1.001100110011... (You recognize this periodic expansion as 3/15, i.e. 0.2. Sure enough, 2 × 1.2 = 2.4.)

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084