8.1.16 Peephole Optimizer
Keywords: sdccman

##
8.1.16 Peephole Optimizer

The compiler uses a rule based, pattern matching and re-writing mechanism for peep-hole optimization. It is inspired by copt a peep-hole optimizer by Christopher W. Fraser (cwfraser @ microsoft.com). A default set of rules are compiled into the compiler, additional rules may be added with the —peep-file <filename> option. The rule language is best illustrated with examples.

> replace {
> mov %1,a
> mov a,%1
> } by {
> mov %1,a
> }

The above rule will change the following assembly sequence:

> mov r1,a
> mov a,r1

to

> mov r1,a

Note: All occurrences of a %n (pattern variable) must denote the same string. With the above rule, the assembly sequence:

> mov r1,a
> mov a,r2

will remain unmodified.
Other special case optimizations may be added by the user (via —peep-file option). E.g. some variants of the 8051 MCU allow only ajmp and acall. The following two rules will change all ljmp and lcall to ajmp and acall

> replace { lcall %1 } by { acall %1 }
> replace { ljmp %1 } by { ajmp %1 }

(NOTE: from version 2.7.3 on, you can use option --acall-ajmp, which also takes care of aligning the interrupt vectors properly.)

The inline-assembler code is also passed through the peep hole optimizer, thus the peephole optimizer can also be used as an assembly level macro expander. The rules themselves are MCU dependent whereas the rule language infra-structure is MCU independent. Peephole optimization rules for other MCU can be easily programmed using the rule language.
The syntax for a rule is as follows:

> rule := replace [ restart ] '{' <assembly sequence> '\n'
> '}' by '{' '\n'
> <assembly sequence> '\n'
> '}' [if <functionName> ] '\n'

<assembly sequence> := assembly instruction (each instruction including labels must be on a separate line).
The optimizer will apply to the rules one by one from the top in the sequence of their appearance, it will terminate when all rules are exhausted. If the 'restart' option is specified, then the optimizer will start matching the rules again from the top, this option for a rule is expensive (performance), it is intended to be used in situations where a transformation will trigger the same rule again. An example of this (not a good one, it has side effects) is the following rule:

> replace restart {
> pop %1
> push %1 } by {
> ; nop
> }

Note that the replace pattern cannot be a blank, but can be a comment line. Without the 'restart' option only the innermost 'pop' 'push' pair would be eliminated, i.e.:

> pop ar1
> pop ar2
> push ar2
> push ar1

would result in:

> pop ar1
> ; nop
> push ar1

with the restart option the rule will be applied again to the resulting code and then all the pop-push pairs will be eliminated to yield:

> ; nop
> ; nop

A conditional function can be attached to a rule. Attaching rules are somewhat more involved, let's illustrate this with an example.

> replace {
> ljmp %5
> %2:
> } by {
> sjmp %5
> %2:
> } if labelInRange

The optimizer does a look-up of a function name table defined in function callFuncByName in the source file SDCCpeeph.c, with the name labelInRange. If it finds a corresponding entry the function is called. Note there can be no parameters specified for some of these functions, in this case the use of %5 is crucial, since the function labelInRange expects to find the label in that particular variable (the hash table containing the variable bindings is passed as a parameter). If you want to code more such functions, take a close look at the function labelInRange and the calling mechanism in source file SDCCpeeph.c. Currently implemented are labelInRange, labelRefCount, labelRefCountChange, labelIsReturnOnly, xramMovcOption, portIsDS390, 24bitMode, notVolatile. notUsed, notSame, operandsNotRelated, labelJTInRange, canAssign, optimizeReturn, notUsedFrom, labelIsReturnOnly, operandsLiteral, labelIsUncondJump, deadMove, useAcallAjmp and okToRemoveSLOC.

This whole thing is a little kludgy, but maybe some day SDCC will have some better means. If you are looking at the peeph*.def files, you will see the default rules that are compiled into the compiler, you can add your own rules in the default set there if you get tired of specifying the —peep-file option.