What's the Difference Between a Compiler's '-O0' Option and '-Og' Option

What's the difference between a compiler's `-O0` option and `-Og` option?

@kaylum just provided some great insight in their comment under my question! And the key part I really care about the most is this:

[-Og] is a better choice than -O0 for producing debuggable code because some compiler passes that collect debug information are disabled at -O0.

https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html#Optimize-Options

So, from now on I'm using -Og (NOT -O0) in addition to -ggdb.


UDPATE 13 Aug. 2020:

Heck with this! Nevermind. I'm sticking with -O0.

With -Og I get <optimized out> and Can't take address of "var" which isn't an lvalue. errors all over the place! I can't print my variables or examine their memory anymore! Ex:

(gdb) print &angle
Can't take address of "angle" which isn't an lvalue.
(gdb) print angle_fixed_p
$6 = <optimized out>

With -O0, however, everything works fine!

(gdb) print angle
$7 = -1.34869879e+20
(gdb) print &angle
$8 = (float *) 0x7ffffffefbbc
(gdb) x angle
0x8000000000000000: Cannot access memory at address 0x8000000000000000
(gdb) x &angle
0x7ffffffefbbc: 0xe0e9f642

So, back to using -O0 instead of -Og it is!

Related:

  1. [they also recommend -O0, and I concur] What does <value optimized out> mean in gdb?

GCC: Difference between -O3 and -Os

The GCC documentation describes what these options do very explicitly.

-O3 tries to optimize code very heavily for performance. It includes all of the optimizations -O2 includes, plus some more.

-Os, on the other hand, instructs GCC to "optimize for size." It enables all -O2 optimizations which do not increase the size of the executable, and then it also toggles some optimization flags to further reduce executable size.

Note that I've been deliberately a bit vague with my descriptions - read the GCC documentation for a more in-depth discussion of exactly which flags are enabled for either optimization level.

I believe the -O* optimization levels are just that - mutually exclusive, distinct levels of optimization. It doesn't really make sense to mix them, since two levels will enable or leave out flags that the other one intentionally leaves out or enables (respectively). If you want to mix and match (you probably don't actually want to do this, unless you have a really good reason to want a specific set of flags), you are best off reading the documentation and mixing and matching the flags each level enables by hand.

I think I'll also link this article from the Gentoo Linux Wiki, which talks about optimization flags as they relate to building the packages for the operating system. Obviously not all of this is applicable, but it still contains some interesting information - for one:

Compiling with -O3 is not a guaranteed way to improve performance, and in fact in many cases can slow down a system due to larger binaries and increased memory usage. -O3 is also known to break several packages. Therefore, using -O3 is not recommended.

According to that article, -O2 is, most of the time, "as good as" -O3, and is safer to use, regarding broken executable output.

GCC 4.8: Does -Og imply -g?

Short answer: No, you must still add -g manually.

Long answer:

I have struggled to find a hard answer straight from the source, so I decided to test it out myself using the methods described here: How to check if program was compiled with debug symbols?

I built an executable with the -O3 flag and without -g. Using objdump --syms <file> | grep debug yielded nothing, as expected.

I then built an executable with -g and without any optimization flags. The same objdump command yielded six results such as this:

0000000000000000 l d .debug_info 0000000000000000 .debug_info

I finally built an executable with the -Og flag and without -g. The objdump command yielded nothing. That implies that debug symbols are not present in this case.

While I can't find any explicit documentation from GCC itself, the Gentoo Wiki (as mentioned before by Marco Scannadinari) confirms my assertion that -Og does not imply -g.

Variables optimized out with g++ and the -Og option

This is normal behaviour when compiling with the -Og option. At this optimisation level the compiler is allowed to optimize as long as it adheres to the as-if rule. This could include the removal of variables (or conversion to constants), as well as the dropping of unused functions.

The recommendation is either to get used to the skipping or to compile with the -O0option.

What is the -o option in GCC?

See https://man7.org/linux/man-pages/man1/gcc.1.html. Under the Synopsis section, you will see the -o option.

Regarding

Well OK, the compiler knows where to look for stdio.h, but the executable must be linked to stdio.o, mustn't it? Where does the stdio.o file reside?

The answer to the first question is "No". Since the answer to the first question is "No", the second question is not relevant.

The functions and variables declared in stdio.h need not be in stdio.o. They are usually in a library (.a or .so) which are found in one of the directories where the linker looks for library files.

In general, there is no rule that each .h file has a corresponding .o file. It is possible to have the functions and variables declared in a .h file to be implemented in multiple .c files that will result in multiple .o files. It is also possible to have functions and variables declared in multiple .h files to be implemented in one .c file. How these are organized varies from project to project.

Each .c file, on the other hand, has a corresponding .o file (I haven't seen any platforms where multiple .c files can be compiled to create one .o file). All the .o files resulting from compiling the .c files are linked together to create an executable.

What is the difference between -c opt and --copt=-O3 in Bazel build (or GCC)

--copt is for passing args to to the compiler.

-c is a short form of --compilation-mode.
Its effect is described in the user-manual:

  • It sets compiler options (e.g. -c opt implies -O2 -DNDEBUG)
  • There are different output directories per compilation mode, so you can switch between debug and optimized builds without full recompilation.

So usually, -c optis enough. If you want the behaviour of -c opt but with a different optimization level, you combine the two options like in -c opt --copt=-O3 and the compiler will get both options -O2 and -O3, but the last one will win.

And watch out, there is a third similar option:

--config=configname is for selecting a configuration. You can have a .bazelrc which defines default options. Some of them are not always active, but some only if you activate them by the --config=configname command line option. Now opt is a popular configname, so if you have a .bazelrc that contains

build:opt --copt=-O3

then bazel build --config=opt has the same effect as bazel build --copt=-O3

Meaning of gcc -O2

Optimization level 2.

From the GCC man page:

-O1 Optimize. Optimizing compilation takes somewhat more time, and a lot
more memory for a large function.

-O2 Optimize even more. GCC performs nearly all supported optimizations
that do not involve a space-speed
tradeoff. The compiler does not
perform loop unrolling or function
inlining when you specify -O2. As
compared to -O, this option increases
both compilation time and the
performance of the generated code.

-O3 Optimize yet more. -O3 turns on all optimizations specified by -O2 and
also turns on the -finline-functions,
-funswitch-loops, -fpredictive-commoning, -fgcse-after-reload and -ftree-vectorize options.

-O0 Reduce compilation time and make debugging produce the expected
results. This is the default.

-Os Optimize for size. -Os enables all -O2 optimizations that do not typically increase code size. It also
performs further optimizations
designed to reduce code size.

What's the meaning of gcc '-c' and gcc '-o'?

Those options do very different things:

  • -c tells GCC to compile a source file into a .o object file. Without that option, it'll default to compiling and linking the code into a complete executable program, which only works if you give it all your .c files at the same time. To compile files individually so they can be linked later, you need -c.
  • -o sets the name of the output file that GCC produces. You're using it when linking object files to make a complete program, and the default output filename for that is a.out. If you don't want your program to be called a.out, you use -o to specify a different name.


Related Topics



Leave a reply



Submit