4.12 Optimizations

The MPLAB XC8 compiler can perform a variety of optimizations. Optimizations can be controlled using the -O option (described in 3.6.6 Options for Controlling Optimization). In Free mode, some of these optimizations are disabled. Even if they are enabled, optimizations might only be applied if very specific conditions are met. As a result, you might see that some lines of code are optimized, but other similar lines are not. When debugging code, you may wish to reduce the optimization level to ensure expected program flow.

The optimization level determines the available optimizations, which are listed in the table below.
Table 4-13. Optimization Level Sets
Level Optimization sets available
O0 Rudimentary optimization
O1 Minimal optimizations
O2 All generic optimizations
O3
 (Licensed only) All generic optimizations plus those targeting speed
Os
 (Licensed only) All generic optimizations plus those targeting space
The minimal code generator optimizations consist of the following.
  • Simplification and folding of constant expressions to simplify expressions.
  • Expression tree optimizations to ensure efficient assembly generation.
  • Propagation of constants is performed where the numerical contents of a variable or expression can be determined at compile time. Variables which are not volatile and whose value can be exactly determined are replaced with the numerical value. Uninitialized global variables are assumed to contain zero prior to any assignment to them.
  • Unreachable code is removed. C Statements that cannot be reached are removed before they generate assembly code. This allows subsequent optimizations to be applied at the C level.
The following is a list of more advanced C-level optimizations, which simplify C expressions or code produced from C expressions.
  • Strength reductions and expression transformations are applied to all expression trees before code is generated. This involves replacing expressions with equivalent, but less costly operations.
  • Unused variables in a program are removed. This applies to all variables. Variables removed will not have memory reserved for them, will not appear in any list or map file, and will not be present in debug information (will not be observable in the debugger). A warning is produced if an unused variable is encountered. Taking the address of a variable or referencing its assembly-domain symbol in hand-written assembly code also constitutes use of the variable.
  • Redundant assignments to variables not subsequently used are removed, unless the variable is volatile. The assignment statement is completely removed, as if it was never present in the original source code. No code will be produced for it and you will not be able to set a breakpoint on that line in the debugger.
  • Unused functions in a program are removed. A function is considered unused if it is not called, directly or indirectly, nor has had its address taken. The entire function is removed, as if it was never present in the original source code. No code will be produced for it and you will not be able to set a breakpoint on any line in the function in the debugger. Referencing a function’s assembly-domain symbol in a separate hand-written assembly module will prevent it being removed. The assembly code need only use the symbol in the .global directive.
  • Dereferencing pointers with only target can be replaced with direct access of the target object. This applies to data and function pointers.
The following is a list of more advanced assembly-level optimizations, which simplify the assembly code generated by the compiler. These optimizations do not apply to hand-written assembly code, whether that be placed in-line with C code or as separate assembly modules.
  • Inlining of small routines is done so that a call to the routine is not required. Only very small routines (typically a few instructions) that are called only once will be changed so that code size is not adversely impacted. This speeds code execution without a significant increase in code size.
  • Explicit inlining of functions that use the inline specifier (see 4.7.1.1 Inline Specifier).
  • Procedural abstraction is performed on assembly code sequences that appear more than once. This is essentially a reverse inlining process. The code sequences are abstracted into callable routines. A call to this routine will replace every instance of the original code sequence. This optimization reduces code size considerably, with a small impact on code speed. It can, however, adversely impact debugging.
  • Replacement of long-form call and jump instructions with their shorter relative forms, either directly or via trampoline constructs.
  • Peephole optimizations are performed on every instruction. These optimizations consider the state of execution at and immediately around each instruction – hence the name. They either alter or delete one or more instructions at each step.