2

Im tasked with converting x86-64 Assembly code back into a simple C function. The C function that I am working with take 4 arguments (long a, long b, long c, long d). As expected the beginning of the Assembly code starts with

movq   %rdi, -32(%rbp)
movq   %rsi, -40(%rbp)
movq   %rdx, -48(%rbp)
movq   %rcx, -56(%rbp)

Later in the code these registers are used again for example

movq   -40(%rbp), %rax
imulq  -48(%rbp), %rax
movq   %rax, %rdx
movq   -32(%rbp), %rax
addq   %rdx, %rax
movq   %rax, -24(%rbp)

My question is if the above code, on the third line, is assigning a value for (long c) or if this register was emptied during the first code snippet and is simply being used as a general purpose register in this situation.

LeapyNick
  • 103
  • 6
  • This is basically a duplicate of [Why does clang produce inefficient asm with -O0 (for this simple floating point sum)?](https://stackoverflow.com/q/53366394) - the compiler basically forgets about registers between statements, when you compile an anti-optimized debug build (`-O0` is the default for gcc and clang). Registers can't get "emptied", and `mov` doesn't zero the source operand either. Look at optimized code if you want it to make sense / look like what a human might do. – Peter Cordes Mar 04 '20 at 21:11
  • Well, it *is* a general purpose register, but to fully answer your question you have to check if the caller is using `rdx` the way it was left by the callee. I'm not sure what call convention that is, seems weird that you're receiving parameters in registers and then moving them to the stack, instead of simply having them already there in the first place. Maybe the optimizer is doing a number in your code. – Havenard Mar 04 '20 at 21:13
  • 1
    @Havenard: Spilling register args to the stack on function entry is 100% normal for "debug" builds (gcc/clang default to `-O0`, and you can specify that manually for `icc`, too). Looking at debug asm is usually a waste of time - [How to remove "noise" from GCC/clang assembly output?](https://stackoverflow.com/a/38552509). The calling convention is x86-64 System V as you can see from RDI and RSI being arg-passing regs. The return value would only include RDX for `__int128` or a struct larger than 8 bytes but <= 16. – Peter Cordes Mar 04 '20 at 21:15
  • @PeterCordes Interesting. I'm not familiar with x86-64 calling conventions, I would not suspect that C would use something other than cdecl unless explicitly told to. – Havenard Mar 04 '20 at 21:26
  • @Havenard: I think you're stuck on Windows terminology. "cdecl" is a 32-bit Windows thing. [Windows x64 uses](//stackoverflow.com/q/4429398) "the Windows x64" convention by default (sometimes called x64 fastcall - it's different but derived from 32-bit fastcall). Other OSes use the calling convention documented in the x86-64 System V ABI doc. It doesn't have a name other than that, it's just *the* calling convention for everything except Windows. (Which like I said this obviously is not; Windows x64 passes the first 4 args in RCX, RDX, R8, R9 if they're integer.) – Peter Cordes Mar 04 '20 at 21:32
  • 3
    No, it is not assigning a value to `c`. The values of the parameters were moved to the stack. Specifically, the value in `rdx` was copied to `-48(%rbp)`. So when the compiler wants to change the value of `c`, it will write to `-48(%rbp)`. In the later code, `rdx` is just being used as a scratch register. (Note: I'm assuming that you are correct that `rdx` corresponds to the argument `c`.) – user3386109 Mar 04 '20 at 21:34
  • 1
    @user3386109 thanks this is the answer I was looking for – LeapyNick Mar 04 '20 at 21:36
  • @user3386109: Well spotted, I hadn't picked up on that phrasing in the question as the crux of what was really being asked. You could post that as an answer. – Peter Cordes Mar 04 '20 at 21:39
  • @PeterCordes Ok, will do. I'm a little rusty on my x86 assembly, so I hesitated to post an answer. – user3386109 Mar 04 '20 at 21:40
  • 1
    @user3386109: "I have the only x86-64 gold badge on SO and I approve this message" :P – Peter Cordes Mar 04 '20 at 21:41
  • @PeterCordes Lol, thanks! – user3386109 Mar 04 '20 at 21:42
  • @PeterCordes Pretty sure it's not Windows terminology. Windows uses stdcall by the way, not cdecl. cdecl originated with the C programming language, which is why it is named like that. https://en.wikipedia.org/wiki/X86_calling_conventions#cdecl – Havenard Mar 04 '20 at 22:57
  • @Havenard: On Linux / MacOS / *BSD, the (only) 32-bit x86 calling convention is similar to what MSVC calls "cdecl" (stack args, caller pops) but differs in how 64-bit structs are returned. But nobody calls it cdecl on Linux; that terminology is only used on Windows (I don't mean for WinAPI functions, yes those are stdcall). I.e. the i386 System V ABI's calling convention happens to match cdecl in most respects. https://godbolt.org/z/WYk3Yx shows some differences. Apparently GCC's `__attribute__((ms_abi))` doesn't actually match MSVC cdecl for struct returns; that's probably a GCC bug. – Peter Cordes Mar 04 '20 at 23:45

1 Answers1

4

No, it is not assigning a value to c.

The values of the parameters were moved to the stack. Specifically, the value in rdx was copied to -48(%rbp). So when the compiler wants to change the value of c, it will write to -48(%rbp).

In the later code, rdx is just being used as a scratch register.

user3386109
  • 34,287
  • 7
  • 49
  • 68