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 {The above rule will change the following assembly sequence:
mov %1,a
mov a,%1
} by {
mov %1,a
}
mov r1,ato
mov a,r1
mov r1,aNote: All occurrences of a %n (pattern variable) must denote the same string. With the above rule, the assembly sequence:
mov r1,awill remain unmodified.
mov a,r2
replace { lcall %1 } by { acall %1 }(NOTE: from version 2.7.3 on, you can use option --acall-ajmp, which also takes care of aligning the interrupt vectors properly.)
replace { ljmp %1 } by { ajmp %1 }
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'<assembly sequence> := assembly instruction (each instruction including labels must be on a separate line).
'}' by '{' '\n'
<assembly sequence> '\n'
'}' [if <functionName> ] '\n'
replace restart {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 %1
push %1 } by {
; nop
}
pop ar1would result in:
pop ar2
push ar2
push ar1
pop ar1with 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
push ar1
; nopA conditional function can be attached to a rule. Attaching rules are somewhat more involved, let me illustrate this with an example.
; nop
replace {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 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, labelIsReturnOnly, operandsNotSame, xramMovcOption, 24bitMode, portIsDS390, 24bitModeAndPortDS390 and notVolatile.
ljmp %5
%2:
} by {
sjmp %5
%2:
} if labelInRange
I know this whole thing is a little kludgey, but maybe some day we will have some better means. If you are looking at this file, 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.