6.2 Assembly-Level Optimizations

The assembler performs optimizations on assembly code, in addition to those optimizations performed by the code generator directly on the C code.

The assembler only optimizes hand-written assembly source modules if the -fasmfile driver optimization setting is enabled, see 4.6.6.7 Asmfile Option. Assembly added in-line (see 5.12.2 Inline Assembly) with C code is never optimized.

The optimizations that can be performed by the assembler are listed below. Unless indicated, these optimizations are only enabled in optimization levels 3 and s, thus preventing them from being used by an unlicensed compiler (see 4.6.6 Options for Controlling Optimization).

Assembly-level optimizations include:

  • 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 5.8.1.2 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 that use a label, PLx, where x is a number. 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.
  • Jump-to-jump type optimizations are made primarily to tidy the output related to conditional code sequences that follow a generic template. Jump-to-jump optimizations can remove jump instructions whose destinations are also jump instructions. This optimization is enabled at optimization level 2, making it accessible to unlicensed compilers.
  • Unreachable code is removed. Code can become orphaned by other optimizations and cannot be reached during normal execution, e.g., instructions after a return instruction. The presence of any label is considered a possible entry point and code following a label is always considered reachable.
  • 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. For example, if W is known to contain the value 0, and an instruction moves W to an address (movwf), this might be replaceable with a clrf instruction.
  • Psect merging can be performed to allow other optimizations to take place. Code within the same psect is guaranteed to be located in the same program memory page. Calls and jumps within the psect do not need to have the page selection bits set before executing. Code using the ljmp and fcall instructions will benefit from this optimization (see 6.1.1 Assembly Instruction Deviations).

Assembly optimizations can often interfere with debugging in some tools, such as MPLAB X IDE. When debugging code, it might be necessary to select a lower optimization level that disables them, if possible (see 4.6.6 Options for Controlling Optimization). The assembler optimizations can drastically reduce code size. However, they typically have little effect on RAM usage.