I'm compiling the following code:
#include <stdio.h>
#include <stdint.h>
uint32_t fibonacci(uint32_t pos) {
long next = 1, current = 0, tmp;
for (long n = 1; n <= pos; n++) {
tmp = current;
current = next;
next += tmp;
}
return current;
}
int main() {
uint32_t target_pos, result;
scanf("%u", &target_pos);
result = fibonacci(target_pos);
printf("%u\n", result);
return 0;
}
Which results in the following assembly code:
0000000000001189 <fibonacci>:
1189: endbr64
118d: push %rbp
118e: mov %rsp,%rbp
1191: mov %edi,-0x24(%rbp)
1194: movq $0x1,-0x20(%rbp)
119c: movq $0x0,-0x18(%rbp)
11a4: movq $0x1,-0x10(%rbp)
11ac: jmp 11cb <fibonacci+0x42>
11ae: mov -0x18(%rbp),%rax
11b2: mov %rax,-0x8(%rbp)
11b6: mov -0x20(%rbp),%rax
11ba: mov %rax,-0x18(%rbp)
11be: mov -0x8(%rbp),%rax
11c2: add %rax,-0x20(%rbp)
11c6: addq $0x1,-0x10(%rbp)
11cb: mov -0x24(%rbp),%eax
11ce: cmp %rax,-0x10(%rbp)
11d2: jle 11ae <fibonacci+0x25>
11d4: mov -0x18(%rbp),%rax
11d8: pop %rbp
11d9: retq
Now, I did some research and this is what I have:
%rbp
is the base of the stackWith that information, we have this diagram:
I've tried to understand it, but some operations are completely nonsense:
%eax
(that for what I've found it's used for returning data) gets saved (not loaded) unchanged
%edi
is loaded at the start, and it doesn't change/get queried from that point
The asm performs a "variable++" on -0x10(%rbp)
; so you expect that in -0x10(%rbp)
n
, that is the only variable that gets incremented by 1, is stored. But in one instruction %rax
gets compared "less than or equal" with -0x10(%rbp)
(looking at the original C code, I assume that the asm is doing pos <= n
, when it should be the other way)
And like that more and more...
Someone can explain me what the hell is happening? I've compiled the C code with an AMD 3950X without optimizations.
Since this apparently turned out to be the problem, I'll go ahead and post it as an answer so this question can get closed.
%edi is loaded at the start, and it doesn't change/get queried from that point
I believe you've got that backwards. In at&t syntax, mov %edi,-0x24(%rbp)
means move edi to the memory address 0x24 before rbp. Which would make sense, since in the x64 calling convention (for linux), edi contains the first parameter.
If you prefer reading intel syntax (and what logical person doesn't?), tell your disassembler that that's the output format you want. If you are using objdump to produce your output, that's done using -M intel
.
While I haven't reviewed the rest of your concerns, it's likely this accounts for all of them.
As for why intel syntax isn't the default, I'll just say that some people like the at&t syntax better (probably the same people who think tabs should be 2 spaces <sniff>). I expect most linux tools use this format by default. At least now you'll know to keep an eye out for it.