Reducing code size using compiler options

By default, Embeetle projects use gcc options-Og -g3, which improves the debugging experience but slightly increases code size.

If your target MCU flash memory is a bit too tight, you might want to change these options into something that optimizes code size.

You can change these options in the makefile, which you can find in the config folder of your project. Just search for COMMONFLAGS =, remove -Og -g3 and add -Os -fomit-frame-pointer -flto. Then scroll down a bit, to the definition of LDFLAGS = and add -flto.

You will need to clean your project (press the clean button) before recompiling for these options to take effect.

For me, these changes resulted in a code size that was almost 10% smaller.

Here’s some background provided by ChatGPT:

Johan:
What do the gcc options -Og and -g3 do?

ChatGPT:

The GCC compiler provides various optimization and debugging options. Here’s what the -Og and -g3 options do:

  1. -Og: This option stands for “optimize for debugging”. It enables optimizations that do not interfere with debugging and makes debugging easier by ensuring that the generated code closely resembles the source code. However, it does not perform all possible optimizations like higher optimization levels (-O1, -O2, -O3). -Og typically includes optimizations such as variable name preservation and limited code rearrangement to aid debugging without sacrificing too much performance.
  2. -g3: This option enables maximum debugging information generation. It includes debugging symbols and information about source files, line numbers, variable names, and types, making it easier to debug the program with tools like GDB (GNU Debugger). -g3 requests the maximum level of debug information, which can be helpful when debugging complex programs but may result in larger executable files.

So, when you compile your code with -Og -g3, you’re optimizing it for debugging while generating extensive debugging information to aid in the debugging process. This combination is often used during development to balance between debugging convenience and performance.

Johan:

What options should I use to minimize code size?

ChatGPT:

To minimize code size with the GCC compiler, you can use the following options:

  1. -Os: This option optimizes the code for size. It enables optimizations that prioritize reducing the size of the generated executable, even if it means sacrificing some runtime performance. -Os typically includes optimizations such as function inlining, dead code elimination, and instruction scheduling to minimize code size.
  2. -fomit-frame-pointer: This option tells the compiler to omit frame pointers for functions that don’t require them for debugging. This can save a few bytes per function, reducing code size.
  3. -fdata-sections -ffunction-sections: These options instruct the compiler to place each function and data object into its own section in the output file. This allows the linker to discard unused functions and data, reducing the final executable size.
  4. -flto: Link-Time Optimization optimizes across files during the linking phase. It can result in more aggressive optimizations and further reduction in code size.

Combining these options (-Os -fomit-frame-pointer -fdata-sections -ffunction-sections -flto) can help you minimize code size while still maintaining reasonable performance. However, keep in mind that optimizing for size may lead to some performance degradation, so it’s essential to balance size and performance according to your requirements.

Johan:

Should the -flto option be passed to the compiler or to the linker?

ChatGPT

The -flto (Link-Time Optimization) option should be passed to both the compiler and the linker.

When passed to the compiler (gcc or g++), it enables Link-Time Optimization during the compilation phase. This allows the compiler to perform more aggressive optimizations across different source files, producing intermediate object files with optimized code.

When passed to the linker (gcc or g++), it enables the linker to perform further optimizations across these intermediate object files during the linking phase, resulting in a final executable with even more optimized code.

So, to fully utilize Link-Time Optimization, you should include -flto both when compiling your source files and when linking them together.