8

I have a cpp file containing only the following:

void f(int* const x) { (*x)*= 2; } 

I compile with:

g++ -S -masm=intel -O3 -fno-exceptions -fno-asynchronous-unwind-tables f.cpp 

This results in f.s containing:

 .section __TEXT,__text,regular,pure_instructions .macosx_version_min 10, 12 .intel_syntax noprefix .globl __Z1fPi .p2align 4, 0x90 __Z1fPi: ## @_Z1fPi ## BB#0: push rbp mov rbp, rsp shl dword ptr [rdi] pop rbp ret .subsections_via_symbols 

If I remove the push, mov, and pop instructions and assemble (on a mac, I'm using Clang), the resulting object file is 4 bytes smaller. Linking and executing results in the same behaviour and the same sized executable.

This suggests that those instructions are superfluous - why does the compiler bother putting them in? Is this simply an optimization that is left to the linker?

3
  • 3
    They are standard stack frame that can be useful for debugging. If you compile with -fomit-frame-pointer it should remove them when it can. Commented May 4, 2017 at 20:48
  • 1
    @MichaelPetch, as indeed it does thank you. Interesting to find that this is default for GNU under -O3, but not for Clang. Commented May 4, 2017 at 21:06
  • 1
    Hmm, that is interesting, since it doesn't match with my experience. I've never seen a frame pointer used when compiling at -O3 (or -O2) in Clang. I can't repro it here, either, even on an old version of Clang. Something is wrong with your configuration. Are you sure that that optimization flag is getting passed? Apple does that weird g++ == Clang redirection thing, maybe that's messing it up? Commented May 5, 2017 at 13:37

2 Answers 2

4

CLANG/CLANG++ is both a native compiler and a cross compiler that supports multiple targets. On OS/X the targets by default are usually a variant of x86_64-apple-darwin for 64-bit code and i386-apple-darwin for 32-bit code. The code you are seeing that resembles this form:

push rbp mov rbp, rsp [snip] pop rbp ret 

Is produced to introduce stack frames. By default CLANG++ implicitly enables stack frames for the Apple Darwin targets. This differs from the Linux targets like x86_64-linux-gnu and i386-linux-gnu. Stack frames can come in handy for some profiling and unwind libraries and can aid debugging on the OS/X platforms which is why I believe they opt to turn them on by default.

You can explicitly omit frame pointers with CLANG++ using the option -fomit-frame-pointer. If you use the build command

g++ -S -masm=intel -O3 -fno-exceptions -fno-asynchronous-unwind-tables \ -fomit-frame-pointer f.cpp 

The output would be something similar to:

 shl dword ptr [rdi] ret 

A Look at Code with Different Targets

If you use different targets with CLANG++ you'd discover the behavior is different. This is an x86-64 Linux target where we don't explicitly omit the frame pointer:

clang++ -target x86_64-linux-gnu -S -masm=intel -O3 -fno-exceptions \ -fno-asynchronous-unwind-tables f.cpp 

Which generates:

 shl dword ptr [rdi] ret 

This is your original x86-64 Apple Darwin target:

clang++ -target x86_64-apple-darwin -S -masm=intel -O3 -fno-exceptions \ -fno-asynchronous-unwind-tables f.cpp 

Which generates:

 push rbp mov rbp, rsp shl dword ptr [rdi] pop rbp ret 

And then the x86-64 Apple target with frame pointers omitted:

clang++ -target x86_64-apple-darwin -S -masm=intel -O3 -fno-exceptions \ -fno-asynchronous-unwind-tables -fomit-frame-pointer f.cpp 

Which generates:

 shl dword ptr [rdi] ret 

You can do a comparison of these targets on Godbolt. The first column of generated code is similar to the question - Apple target with implicit frame pointers. The second is Apple target without frame pointers and the third is an x86-64 Linux target.

Sign up to request clarification or add additional context in comments.

1 Comment

Interesting, thanks for investigating this and posting an answer. (I also saw your comment notifying me on the question.) I'm quite surprised to see that frame pointers are always used on OS X builds. I'm typing this on a notebook running OS X, but I don't do any development work in that environment. I suppose that makes some of what I've posted about this being a clear indication of a debugging build not exactly true.
0

Those instructions are used to access the stack in function bodies. It is a generalization and used for debugging purposes.

1 Comment

He isn't really asking what they're for (although that is reasonable to include in an answer). He's asking why they're being generated, despite being superfluous, and if he can make them go away. ("This suggests that those instructions are superfluous - why does the compiler bother putting them in? Is this simply an optimization that is left to the linker?")

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.