1

the test is on 32-bit x86. I compiled the code with gcc 4.2, optimization level o2. I compiled the C code into binary, and then use objdump to disassemble it.

Here are two sequences of instructions used for the function prologue:

0804a6f0 <quotearg_n>:
804a6f0:       8b 44 24 04             mov    0x4(%esp),%eax
804a6f4:       b9 ff ff ff ff          mov    $0xffffffff,%ecx
804a6f9:       8b 54 24 08             mov    0x8(%esp),%edx
804a6fd:       c7 44 24 04 40 e1 04    movl   $0x804e140,0x4(%esp)
804a704:       08 
804a705:       e9 c6 fa ff ff          jmp    804a1d0 <quotearg_n_options>
804a70a:       8d b6 00 00 00 00       lea    0x0(%esi),%esi


0804a730 <quotearg>:
804a730:       83 ec 1c                sub    $0x1c,%esp
804a733:       8b 44 24 20             mov    0x20(%esp),%eax
804a737:       c7 04 24 00 00 00 00    movl   $0x0,(%esp)
804a73e:       89 44 24 04             mov    %eax,0x4(%esp)
804a742:       e8 a9 ff ff ff          call   804a6f0 <quotearg_n>
804a747:       83 c4 1c                add    $0x1c,%esp
804a74a:       c3                      ret
804a74b:       90                      nop
804a74c:       8d 74 26 00             lea    0x0(%esi,%eiz,1),%esi

Note that in function quotearg, register esp is decreased with 0x1c before it is used to access the stack and get some arguments. Accutually according to my experience, I think the sub then access pattern is quite common for instructions compiled with O2.

However, note that in function quotearg_n, register esp is directly added with 0x4 to access the stack. (I think the meaning of instruction at address 0x804a6f0 is to put the return address of call site to register eax, am I right..?) According to my observation, the pattern used by the first function is rare, around 5% for gcc compiled middle size C program with O2.

So here is my question:

Why does compiler generate function prologue instructions in a way similar to quoterag_n? What is the exact meaning of the first three instructions start from address 0x804a6f0?

Why doesn't compiler always generate function prologue instructions following the sub then access pattern? (such as quoterag)

Am I clear? thanks a lot

1
  • Is there a reason for you to use an 8-year-old compiler?
    – phuclv
    Commented Sep 29, 2015 at 14:03

1 Answer 1

4

It seems like the functions are defined like this:

int quotearg_n(int a, int b) {
    return quotearg_n_options(a, b, -1, "some_string");
}

int quotearg(int a) {
    return quotearg_n(0, a);
}

(the ints might as well be pointers, can't tell this from your snippets, and the "some string" might be a pointer to a pre-initialized structure)

These functions have the normal ABI, which means they pass all arguments on the stack, while quotearg_n_options receives the first three of its arguments in registers, and only the last one on the stack. This might be due to a modifier in the function's prototype, and it might also be due to the function being declared static, so the compiler knows it can't be called from outside the current source file so it's safe to change it's ABI.

Now, from quotearg to quotearg_n, the number of parameters on the stack increases, so the compiler needs to make room for them, initializes them, calls the subroutine, and returns.

From quotearg_n to quotearg_n_options, the number of parameters increases again (from 2 to 4), but since three parameters are passed in registers eax, edx and ecx, only the string has to be on the stack. Which means the number of parameter on the stack decreases, so the call needs less stack space, which allows the compiler to recycle the current stack. So what the compiler does is something called tail call elimination: instead of calling the function, then returning, it sets up the stack in the way the callee expects it, then jumps to it instead of using a call. That function (quotearg_n_options) will execute, and when it returns, it will directly return to the function that called quotearg_n, the original one.

So to answer your question: The compiler uses the tail call elimination optimization, which it can do only if the number of parameters on the stack of the called function is lower than the number of parameters on the stack of the caller.

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.