5.13 Optimizations

The optimizations in the MPLAB XC8 compiler can be broadly grouped into C-level optimizations performed on the source code before conversion into assembly and assembly-level optimizations performed on the assembly code generated by the compiler.

The C-level optimizations are performed early during the code generation phase and so have flow-on benefits: performing one optimization might mean that another can then be applied. As these optimizations are applied before the debug information has been produced, they have less impact on source-level debugging of programs.

Some of these optimizations are integral to the code generation process and so cannot be disabled via an option. Suggestions as to how specific optimizations can be defeated are given in the sections below.

If your compiler is unlicensed, some of the optimization levels are disabled (see 4.6.6 Options for Controlling Optimization). Even if they are enabled, optimizations can 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.

The optimization level determines the available optimizations, which are listed in the Table 5-13 table.

Table 5-13. Optimization Level Sets
Level Optimization sets available
O0
  • Rudimentary Optimization
O1
  • Minimal code generator optimizations
O2
  • All generic code generator optimizations
  • Minimal assembly optimizations
O3
 (Licensed only)
  • All generic and speed-specific code generator optimizations
  • All generic and speed-specific assembler optimizations
Os
 (Licensed only)
  • All generic and space-specific code generator optimizations
  • All generic and space-specific assembler optimizations

Assembly-level optimizations are described in 6.2 Assembly-Level Optimizations.

The minimal code generator optimizations consist of the following.

  • Whole-program analysis for object allocation into data banks without having to use non-standard keywords or compiler directives.
  • 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 can be determined. 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 code generation (C-level) optimizations, which simplify C expressions or code produced from C expressions. These are applied across the entire program, not just on a module-by-module basis.

  • Tracking of the current data bank is performed by the compiler as it generates assembly code. This allows the compiler to reduce the number of bank-selection instructions generated.
  • 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. Global objects qualified volatile will never be removed (see 5.3.8.2 Volatile Type Qualifier). 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.
  • Unused return expressions in a function are removed. The return value is considered unused if the result of all calls to that function discard the return value. The code associated with calculation of the return value will be removed and the function will be encoded as if its return type was void.
  • Variables assigned a value before being read are not cleared or initialized by the runtime startup code. Only non-auto variables are considered and if they are assigned a value before other code can read their value, they are treated as being __persistent (see 5.3.9.5 Persistent Type Qualifier. All __persistent objects are not cleared by the runtime startup code, so this optimization will speed execution of the program startup.
  • Pointer sizes are optimized to suit the target objects they can access. The compiler tracks all assignments to pointer variables and keeps a list of targets each pointer can access. As the memory space of each target is known, the size and dereference method used can be customized for each pointer.
  • Dereferencing pointers with only target can be replaced with direct access of the target object. This applies to data and function pointers.
  • Objects qualified const are considered for placement into data memory if any such memory is otherwise unused by the program.

MPLAB X IDE or other IDEs can indicate incorrect values when watching variables if optimizations hold a variable in a register. Try to use the ELF/DWARF debug file format to minimize such occurrences. Check the assembly list file to see if registers are used in the routine that is being debugged.