0

I know that the push instruction in x86_64 can push the value of various source operands onto the stack. So far I've compiled a lot of different C programs and then inspected the executables. None of them uses the push instructions other than for "pushq %rbp".

Why is that ?

alessio solari
  • 313
  • 1
  • 6
  • 1
    Commonly the `%rbp` register is the function stack frame register. Think of it like a pointer to somewhere on the stack. When a function is called, its arguments are fetched using offsets from `%rbp`. That means it needs to be saved frequently, which is what the `push %rbp` instruction is doing. – Some programmer dude Aug 22 '23 at 13:59
  • 1
    Or are you wondering why not arguments, local variables, etc. are using the `push` instruction to push the values on to the stack? Then it's because the generated code uses offsets from the function stack frame register instead. The code copies values onto the stack using `%rbp` instead of `push`. Similarly it copies values from the stack using offsets from `%rbp` instead of using `pop`. That way, you don't need to push and pop values all the time, when you need a variable in the middle of the stack. And once the function returns, it's a simple `mov` operation to "remove" the stack frame. – Some programmer dude Aug 22 '23 at 14:02
  • 1
    Nevertheless you should see other uses. For example the PLT is normally full of `push immediate` instructions. – Jester Aug 22 '23 at 14:04
  • @Jester, what is the PLT ? – alessio solari Aug 22 '23 at 14:06
  • @Jester, how can I demonstrate that ? – alessio solari Aug 22 '23 at 14:07
  • `objdump -d a.out`. If it uses some shared library you should see stuff like `` with some `push`es. – Jester Aug 22 '23 at 14:11
  • Using this command, I find many uses of `push`. While `push ebp` is the most common, it certainly isn't the only one: `objdump -d --no-show-raw-insn --no-addresses /bin/bash | grep -E '^\s+push' | sort | uniq -c | sort -nk1,1` Replace /bin/bash with whatever you want to disassemble – Homer512 Aug 22 '23 at 14:29
  • 3
    Note that 32 bit code uses `push` more frequently since arguments are passed on the stack. Also it typically needs to save registers more. – Jester Aug 22 '23 at 14:37
  • C compilers typically allocate all the temporary memory they need in the stack frame when the block is entered, they don't use push/pop to save intermediate values. Also, many calculations can be done entirely in registers. – Barmar Aug 22 '23 at 15:28
  • 1
    Are you building without optimization? With optimization enabled, I typically see several registers pushed at the beginning of every function. With optimization disabled, the compiler keeps all variables in memory so it doesn't use as many registers. – prl Aug 22 '23 at 17:36
  • See [Why does clang produce inefficient asm with -O0 (for this simple floating point sum)?](https://stackoverflow.com/q/53366394) and [How to remove "noise" from GCC/clang assembly output?](https://stackoverflow.com/q/38552116) - probably @prl's guess is right. Unoptimized code won't use call-preserved registers to keep values in registers across function calls, because it keeps them in memory instead, unless you used `register int foo;`. And x86-64 SysV has enough call-clobbered registers for evaluating most individual statements separately. – Peter Cordes Aug 22 '23 at 19:25
  • See also [What are callee and caller saved registers?](https://stackoverflow.com/a/56178078) re: why call-preserved regs are useful. – Peter Cordes Aug 22 '23 at 19:27
  • 1
    Closed as a duplicate because all signs point to unoptimized code (push/pop of only RBP), and there's no specific example to be answered or any details like build options so in that sense it's missing a [mcve]. Hmm, maybe it's not obvious *why* exactly an unoptimized build wouldn't use more registers, and that this specific consequence would result. It seems obvious after @prl stated it clearly, but that could be posted as an answer. – Peter Cordes Aug 22 '23 at 19:31

0 Answers0