@nataliapc/mcp-openmsx 1.2.10 → 1.2.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -2
- package/dist/chunker.js +187 -0
- package/dist/embedder.js +250 -0
- package/dist/server.js +6 -1
- package/dist/server_tools.js +6 -5
- package/dist/vectordb.js +94 -35
- package/package.json +4 -8
- package/resources/audio/chipsfmpacpr1_en.md +209 -0
- package/resources/audio/chipsfmpacpr2_en.md +170 -0
- package/resources/audio/toc.json +12 -0
- package/resources/book--msx-top-secret-3/MTS3-Appendix-English-Upd2.pdf +0 -0
- package/resources/book--msx-top-secret-3/MTS3-Complete-English.pdf +0 -0
- package/resources/book--msx-top-secret-3/mts3-appendix-english-upd2.md +25863 -0
- package/resources/book--msx-top-secret-3/mts3-complete-english.md +44895 -0
- package/resources/book--msx2-technical-handbook/toc.json +1 -1
- package/resources/book--the-msx-red-book/Chapter1_Programmable_Peripheral_Interface.md +112 -0
- package/resources/book--the-msx-red-book/Chapter2_Video_Display_Processor.md +308 -0
- package/resources/book--the-msx-red-book/Chapter3_Programmable_Sound_Generator.md +168 -0
- package/resources/book--the-msx-red-book/Chapter4_ROM_BIOS.md +2528 -0
- package/resources/book--the-msx-red-book/Chapter5_ROM_BASIC_Interpreter.md +3975 -0
- package/resources/book--the-msx-red-book/Chapter6_Memory_Map.md +1963 -0
- package/resources/book--the-msx-red-book/Chapter7_Machine_Code_Programs.md +1238 -0
- package/resources/book--the-msx-red-book/Introduction.md +104 -0
- package/resources/book--the-msx-red-book/toc.json +38 -3
- package/resources/processors/toc.json +3 -3
- package/resources/processors/z80-undocumented.md +141 -0
- package/resources/programming/asm_develop_a_program_in_cartridge_rom.md +1881 -0
- package/resources/programming/toc.json +6 -0
- package/resources/sdcc/1_Introduction.md +199 -0
- package/resources/sdcc/2_Installing_SDCC.md +533 -0
- package/resources/sdcc/3_Using_SDCC.md +1758 -0
- package/resources/sdcc/4_Notes_on_supported_Processors.md +1638 -0
- package/resources/sdcc/5_Debugging.md +210 -0
- package/resources/sdcc/6_Tips_and_Support.md +258 -0
- package/resources/sdcc/7_SDCC_Technical_Data.md +489 -0
- package/resources/sdcc/8_Compiler_internals.md +477 -0
- package/resources/sdcc/toc.json +44 -2
- package/resources/system/how_to_detect_ram.md +14 -0
- package/resources/system/mrc_wiki_megarom_mappers.md +533 -0
- package/resources/system/the_memory.md +118 -0
- package/resources/system/toc.json +18 -0
- package/vector-db/__manifest/_transactions/0-675ee228-bffb-4636-80e5-cdfde25cc4fe.txn +2 -0
- package/vector-db/__manifest/_versions/18446744073709551614.manifest +0 -0
- package/vector-db/__manifest/_versions/latest_version_hint.json +1 -0
- package/vector-db/msxdocs.lance/_indices/37194b01-2a25-40d1-ac38-7fbe254df5ea/metadata.lance +0 -0
- package/vector-db/msxdocs.lance/_indices/37194b01-2a25-40d1-ac38-7fbe254df5ea/part_2_docs.lance +0 -0
- package/vector-db/msxdocs.lance/_indices/37194b01-2a25-40d1-ac38-7fbe254df5ea/part_2_invert.lance +0 -0
- package/vector-db/msxdocs.lance/_indices/37194b01-2a25-40d1-ac38-7fbe254df5ea/part_2_tokens.lance +0 -0
- package/vector-db/msxdocs.lance/_transactions/0-dd155672-40e6-4c6a-942f-7fcbe8c3dbd0.txn +0 -0
- package/vector-db/msxdocs.lance/_transactions/1-e7230cbd-ce8e-465c-9b85-b91443862427.txn +0 -0
- package/vector-db/msxdocs.lance/_versions/18446744073709551613.manifest +0 -0
- package/vector-db/msxdocs.lance/_versions/18446744073709551614.manifest +0 -0
- package/vector-db/msxdocs.lance/_versions/latest_version_hint.json +1 -0
- package/vector-db/msxdocs.lance/data/000100110110001011110001fc578141d296825d0bea11c95d.lance +0 -0
- package/resources/book--the-msx-red-book/the_msx_red_book.md +0 -10349
- package/resources/processors/z80-undocumented.tex +0 -5617
- package/resources/sdcc/lyx2md.py +0 -745
- package/resources/sdcc/sdccman.lyx +0 -81574
- package/resources/sdcc/sdccman.md +0 -5557
- package/vector-db/index.json +0 -1
|
@@ -0,0 +1,489 @@
|
|
|
1
|
+
# SDCC Compiler User Guide
|
|
2
|
+
|
|
3
|
+
## SDCC Technical Data
|
|
4
|
+
|
|
5
|
+
### Optimizations Optimizations
|
|
6
|
+
|
|
7
|
+
SDCC performs a host of standard optimizations in addition to some MCU specific optimizations.
|
|
8
|
+
|
|
9
|
+
#### Sub-expression Elimination Subexpression elimination
|
|
10
|
+
|
|
11
|
+
The compiler does local and *g* lobal *c* ommon *s* ubexpression *e* limination, e.g.:
|
|
12
|
+
|
|
13
|
+
i = x + y + 1;
|
|
14
|
+
j = x + y;
|
|
15
|
+
|
|
16
|
+
will be translated to
|
|
17
|
+
|
|
18
|
+
iTemp = x + y;
|
|
19
|
+
i = iTemp + 1;
|
|
20
|
+
j = iTemp;
|
|
21
|
+
|
|
22
|
+
Some subexpressions are not as obvious as the above example, e.g.:
|
|
23
|
+
|
|
24
|
+
a->b[i].c = 10;
|
|
25
|
+
a->b[i].d = 11;
|
|
26
|
+
|
|
27
|
+
In this case the address arithmetic a->b[i] will be computed only once; the equivalent code in C would be.
|
|
28
|
+
|
|
29
|
+
iTemp = a->b[i];
|
|
30
|
+
iTemp.c = 10;
|
|
31
|
+
iTemp.d = 11;
|
|
32
|
+
|
|
33
|
+
The compiler will try to keep these temporary variables in registers.
|
|
34
|
+
|
|
35
|
+
#### Dead-Code Elimination Dead-code elimination
|
|
36
|
+
|
|
37
|
+
int global;
|
|
38
|
+
|
|
39
|
+
void f () {
|
|
40
|
+
int i;
|
|
41
|
+
i = 1; /* dead store */
|
|
42
|
+
global = 1; /* dead store */
|
|
43
|
+
global = 2;
|
|
44
|
+
return;
|
|
45
|
+
global = 3; /* unreachable */
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
will be changed to
|
|
49
|
+
|
|
50
|
+
int global;
|
|
51
|
+
|
|
52
|
+
void f () {
|
|
53
|
+
global = 2;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
#### Copy-Propagation Copy propagation
|
|
57
|
+
|
|
58
|
+
int f() {
|
|
59
|
+
int i, j;
|
|
60
|
+
i = 10;
|
|
61
|
+
j = i;
|
|
62
|
+
return j;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
will be changed to
|
|
66
|
+
|
|
67
|
+
int f() {
|
|
68
|
+
int i, j;
|
|
69
|
+
i = 10;
|
|
70
|
+
j = 10;
|
|
71
|
+
return 10;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
Note: the dead stores created by this copy propagation will be eliminated by dead-code elimination.
|
|
75
|
+
|
|
76
|
+
#### Loop Optimizations Loop optimization
|
|
77
|
+
|
|
78
|
+
Two types of loop optimizations are done by SDCC *loop invariant* lifting and *strength reduction* of loop induction variables. In addition to the strength reduction the optimizer marks the induction variables and the register allocator tries to keep the induction variables in registers for the duration of the loop. Because of this preference of the register allocator Register allocation, loop induction optimization causes an increase in register pressure, which may cause unwanted spilling of other temporary variables into the stack stack / data space. The compiler will generate a warning message when it is forced to allocate extra space either on the stack or data space. If this extra space allocation is undesirable then induction optimization can be eliminated either for the entire source file (with --noinduction option) or for a given function only using #pragma noinduction pragma noinduction.
|
|
79
|
+
|
|
80
|
+
Loop Invariant:
|
|
81
|
+
|
|
82
|
+
for (i = 0; i < 100; i ++)
|
|
83
|
+
f += k + l;
|
|
84
|
+
|
|
85
|
+
changed to
|
|
86
|
+
|
|
87
|
+
itemp = k + l;
|
|
88
|
+
for (i = 0; i < 100; i++)
|
|
89
|
+
f += itemp;
|
|
90
|
+
|
|
91
|
+
As mentioned previously some loop invariants are not as apparent, all static address computations are also moved out of the loop.
|
|
92
|
+
|
|
93
|
+
Strength Reduction Strength reduction, this optimization substitutes an expression by a cheaper expression:
|
|
94
|
+
|
|
95
|
+
for (i=0;i < 100; i++)
|
|
96
|
+
ar[i*5] = i*3;
|
|
97
|
+
|
|
98
|
+
changed to
|
|
99
|
+
|
|
100
|
+
itemp1 = 0;
|
|
101
|
+
itemp2 = 0;
|
|
102
|
+
for (i=0;i< 100;i++) {
|
|
103
|
+
ar[itemp1] = itemp2;
|
|
104
|
+
itemp1 += 5;
|
|
105
|
+
itemp2 += 3;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
The more expensive multiplication Multiplication is changed to a less expensive addition.
|
|
109
|
+
|
|
110
|
+
#### Loop Reversing Loop reversing
|
|
111
|
+
|
|
112
|
+
This optimization is done to reduce the overhead of checking loop boundaries for every iteration. Some simple loops can be reversed and implemented using a“ decrement and jump if not zero” instruction. SDCC checks for the following criterion to determine if a loop is reversible (note: more sophisticated compilers use data-dependency analysis to make this determination, SDCC uses a more simple minded analysis).
|
|
113
|
+
|
|
114
|
+
- The 'for' loop is of the form
|
|
115
|
+
|
|
116
|
+
for(<symbol> = <expression>; [<sym>++ | <sym> += 1])
|
|
117
|
+
- The <for body> does not contain“ continue” or 'break”.
|
|
118
|
+
- All goto's are contained within the loop.
|
|
119
|
+
- No function calls within the loop.
|
|
120
|
+
- The loop control variable <sym> is not assigned any value within the loop
|
|
121
|
+
- The loop control variable does NOT participate in any arithmetic operation within the loop.
|
|
122
|
+
- There are NO switch statements in the loop.
|
|
123
|
+
|
|
124
|
+
#### Algebraic Simplifications
|
|
125
|
+
|
|
126
|
+
SDCC does numerous algebraic simplifications, the following is a small sub-set of these optimizations.
|
|
127
|
+
|
|
128
|
+
i = j + 0; /* changed to: */ i = j;
|
|
129
|
+
i /= 2; /* for unsigned i changed to: */ i >>= 1;
|
|
130
|
+
i = j - j; /* changed to: */ i = 0;
|
|
131
|
+
i = j / 1; /* changed to: */ i = j;
|
|
132
|
+
|
|
133
|
+
Note the subexpressions Subexpression given above are generally introduced by macro expansions or as a result of copy/constant propagation.
|
|
134
|
+
|
|
135
|
+
#### 'switch' Statements switch statement
|
|
136
|
+
|
|
137
|
+
SDCC can optimize switch statements to jump tables jump tables. It makes the decision based on an estimate of the generated code size. SDCC is quite liberal in the requirements for jump table generation:
|
|
138
|
+
|
|
139
|
+
- The labels need not be in order, and the starting number need not be one or zero, the case labels are in numerical sequence or not too many case labels are missing.
|
|
140
|
+
switch(i) { switch (i) {
|
|
141
|
+
case 4:... case 0:...
|
|
142
|
+
case 5:... case 1:...
|
|
143
|
+
case 3:...
|
|
144
|
+
case 6:... case 3:...
|
|
145
|
+
case 7:... case 4:...
|
|
146
|
+
case 8:... case 5:...
|
|
147
|
+
case 9:... case 6:...
|
|
148
|
+
case 10:... case 7:...
|
|
149
|
+
case 11:... case 8:...
|
|
150
|
+
} }
|
|
151
|
+
|
|
152
|
+
Both the above switch statements will be implemented using a jump-table. The example to the right side is slightly more efficient as the check for the lower boundary of the jump-table is not needed.
|
|
153
|
+
|
|
154
|
+
- The number of case labels is not larger than supported by the target architecture.
|
|
155
|
+
- If the case labels are not in numerical sequence ('gaps' between cases) SDCC checks whether a jump table with additionally inserted dummy cases is still attractive.
|
|
156
|
+
- If the starting number is not zero and a check for the lower boundary of the jump-table can thus be eliminated SDCC might insert dummy cases 0,....
|
|
157
|
+
Switch statements which have large gaps in the numeric sequence or those that have too many case labels can be split into more than one switch statement for efficient code generation, e.g.:
|
|
158
|
+
|
|
159
|
+
switch (i) {
|
|
160
|
+
case 1:...
|
|
161
|
+
case 2:...
|
|
162
|
+
case 3:...
|
|
163
|
+
case 4:...
|
|
164
|
+
case 5:...
|
|
165
|
+
case 6:...
|
|
166
|
+
case 7:...
|
|
167
|
+
case 101:...
|
|
168
|
+
case 102:...
|
|
169
|
+
case 103:...
|
|
170
|
+
case 104:...
|
|
171
|
+
case 105:...
|
|
172
|
+
case 106:...
|
|
173
|
+
case 107:...
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
If the above switch statement is broken down into two switch statements
|
|
177
|
+
|
|
178
|
+
switch (i) {
|
|
179
|
+
case 1:...
|
|
180
|
+
case 2:...
|
|
181
|
+
case 3:...
|
|
182
|
+
case 4:...
|
|
183
|
+
case 5:...
|
|
184
|
+
case 6:...
|
|
185
|
+
case 7:...
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
and
|
|
189
|
+
|
|
190
|
+
switch (i) {
|
|
191
|
+
case 101:...
|
|
192
|
+
case 102:...
|
|
193
|
+
case 103:...
|
|
194
|
+
case 104:...
|
|
195
|
+
case 105:...
|
|
196
|
+
case 106:...
|
|
197
|
+
case 107:...
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
then both the switch statements will be implemented using jump-tables whereas the unmodified switch statement will not be.
|
|
201
|
+
|
|
202
|
+
There might be reasons which SDCC cannot know about to either favour or not favour jump tables. If the target system has to be as quick for the last switch case as for the first (pro jump table), or if the switch argument is known to be zero in the majority of the cases (contra jump table).
|
|
203
|
+
|
|
204
|
+
#### Bit-shifting Operations Bit shifting.
|
|
205
|
+
|
|
206
|
+
Bit shifting is one of the most frequently used operation in embedded programming. SDCC tries to implement bit-shift operations in the most efficient way possible, e.g.:
|
|
207
|
+
|
|
208
|
+
unsigned char i;
|
|
209
|
+
...
|
|
210
|
+
i >>= 4;
|
|
211
|
+
...
|
|
212
|
+
|
|
213
|
+
generates the following code:
|
|
214
|
+
|
|
215
|
+
mov a,_i
|
|
216
|
+
swap a
|
|
217
|
+
anl a,#0x0f
|
|
218
|
+
mov _i,a
|
|
219
|
+
|
|
220
|
+
Typically, SDCC will not setup a loop if the shift count is known. Another example:
|
|
221
|
+
|
|
222
|
+
unsigned int i;
|
|
223
|
+
...
|
|
224
|
+
i >>= 9;
|
|
225
|
+
...
|
|
226
|
+
|
|
227
|
+
will generate:
|
|
228
|
+
|
|
229
|
+
mov a,(_i + 1)
|
|
230
|
+
mov (_i + 1),#0x00
|
|
231
|
+
clr c
|
|
232
|
+
rrc a
|
|
233
|
+
mov _i,a
|
|
234
|
+
|
|
235
|
+
#### Bit-rotation Bit rotation
|
|
236
|
+
|
|
237
|
+
A special case of the bit-shift operation is bit rotation rotating bits, SDCC recognizes the following expression to be a left bit-rotation:
|
|
238
|
+
|
|
239
|
+
**unsigned** char i; /* unsigned is needed for rotation */
|
|
240
|
+
...
|
|
241
|
+
i = ((i << 1) | (i >> 7));
|
|
242
|
+
...
|
|
243
|
+
|
|
244
|
+
will generate the following code:
|
|
245
|
+
|
|
246
|
+
mov a,_i
|
|
247
|
+
rl a
|
|
248
|
+
mov _i,a
|
|
249
|
+
|
|
250
|
+
SDCC uses pattern matching on the parse tree to determine this operation.Variations of this case will also be recognized as bit-rotation, i.e.:
|
|
251
|
+
|
|
252
|
+
i = ((i >> 7) | (i << 1)); /* left-bit rotation */
|
|
253
|
+
|
|
254
|
+
#### Nibble and Byte Swapping
|
|
255
|
+
|
|
256
|
+
Other special cases of the bit-shift operations are nibble or byte swapping swapping nibbles/bytes, SDCC recognizes the following expressions:
|
|
257
|
+
|
|
258
|
+
**unsigned** char i;
|
|
259
|
+
**unsigned** int j;
|
|
260
|
+
...
|
|
261
|
+
i = ((i << 4) | (i >> 4));
|
|
262
|
+
j = ((j << 8) | (j >> 8));
|
|
263
|
+
|
|
264
|
+
and generates a swap instruction for the nibble swapping Nibble swapping or move instructions for the byte swapping Byte swapping. The" j" example can be used to convert from little to big-endian or vice versa. If you want to change the endianness of a *signed* integer you have to cast to (unsigned int) first.
|
|
265
|
+
|
|
266
|
+
Note that SDCC stores numbers in little-endian Usually 8-bit processors don't care much about endianness. This is not the case for the standard 8051 which only has an instruction to increment its *dptr* DPTR-datapointer so little-endian is the more efficient byte order. little-endian Endianness format (i.e. lowest order first) for most backends. However, the hc08, s08 and stm8 backends are big-endian.
|
|
267
|
+
|
|
268
|
+
#### Getting a Bit Any Order Bit
|
|
269
|
+
|
|
270
|
+
It is frequently required to obtain the highest order bit of an integral type (long, int, short or char types). Also obtaining any other order bit is not uncommon. SDCC recognizes the following expressions to yield the highest order bit and generates optimized code for it, e.g.:
|
|
271
|
+
|
|
272
|
+
unsigned int gint;
|
|
273
|
+
|
|
274
|
+
foo () {
|
|
275
|
+
unsigned char hob1, aob1;
|
|
276
|
+
bit hob2, hob3, aob2, aob3;
|
|
277
|
+
...
|
|
278
|
+
hob1 = (gint >> 15) & 1;
|
|
279
|
+
hob2 = (gint >> 15) & 1;
|
|
280
|
+
hob3 = gint & 0x8000;
|
|
281
|
+
aob1 = (gint >> 9) & 1;
|
|
282
|
+
aob2 = (gint >> 8) & 1;
|
|
283
|
+
aob3 = gint & 0x0800;
|
|
284
|
+
...
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
will generate the following code:
|
|
288
|
+
|
|
289
|
+
61; hob.c 7
|
|
290
|
+
000A E5*01 62 mov a,(_gint + 1)
|
|
291
|
+
000C 23 63 rl a
|
|
292
|
+
000D 54 01 64 anl a,#0x01
|
|
293
|
+
000F F5*02 65 mov _foo_hob1_1_1,a
|
|
294
|
+
66; hob.c 8
|
|
295
|
+
0011 E5*01 67 mov a,(_gint + 1)
|
|
296
|
+
0013 33 68 rlc a
|
|
297
|
+
0014 92*00 69 mov _foo_hob2_1_1,c
|
|
298
|
+
66; hob.c 9
|
|
299
|
+
0016 E5*01 67 mov a,(_gint + 1)
|
|
300
|
+
0018 33 68 rlc a
|
|
301
|
+
0019 92*01 69 mov _foo_hob3_1_1,c
|
|
302
|
+
70; hob.c 10
|
|
303
|
+
001B E5*01 71 mov a,(_gint + 1)
|
|
304
|
+
001D 03 72 rr a
|
|
305
|
+
001E 54 01 73 anl a,#0x01
|
|
306
|
+
0020 F5*03 74 mov _foo_aob1_1_1,a
|
|
307
|
+
75; hob.c 11
|
|
308
|
+
0022 E5*01 76 mov a,(_gint + 1)
|
|
309
|
+
0024 13 77 rrc a
|
|
310
|
+
0025 92*02 78 mov _foo_aob2_1_1,c
|
|
311
|
+
79; hob.c 12
|
|
312
|
+
0027 E5*01 80 mov a,(_gint + 1)
|
|
313
|
+
0029 A2 E3 81 mov c,acc[3]
|
|
314
|
+
002B 92*03 82 mov _foo_aob3_1_1,c
|
|
315
|
+
|
|
316
|
+
Other variations of these cases however will *not* be recognized. They are standard C expressions, so I heartily recommend these be the only way to get the highest order bit, (it is portable). Of course it will be recognized even if it is embedded in other expressions, e.g.:
|
|
317
|
+
|
|
318
|
+
xyz = gint + ((gint >> 15) & 1);
|
|
319
|
+
|
|
320
|
+
will still be recognized.
|
|
321
|
+
|
|
322
|
+
#### Higher Order Byte Higher Order Byte / Higher Order Word Higher Order Word
|
|
323
|
+
|
|
324
|
+
It is also frequently required to obtain a higher order byte or word of a larger integral type (long, int or short types). For mcs51, SDCC recognizes the following expressions to yield the higher order byte or word and generates optimized code for it, e.g.:
|
|
325
|
+
|
|
326
|
+
unsigned int gint;
|
|
327
|
+
unsigned long int glong;
|
|
328
|
+
|
|
329
|
+
foo () {
|
|
330
|
+
unsigned char hob1, hob2;
|
|
331
|
+
unsigned int how1, how2;
|
|
332
|
+
...
|
|
333
|
+
hob1 = (gint >> 8) & 0xFF;
|
|
334
|
+
hob2 = glong >> 24;
|
|
335
|
+
how1 = (glong >> 16) & 0xFFFF;
|
|
336
|
+
how2 = glong >> 8;
|
|
337
|
+
...
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
will generate the following code:
|
|
341
|
+
|
|
342
|
+
91; hob.c 15
|
|
343
|
+
0037 85*01*06 92 mov _foo_hob1_1_1,(_gint + 1)
|
|
344
|
+
93; hob.c 16
|
|
345
|
+
003A 85*05*07 94 mov _foo_hob2_1_1,(_glong + 3)
|
|
346
|
+
95; hob.c 17
|
|
347
|
+
003D 85*04*08 96 mov _foo_how1_1_1,(_glong + 2)
|
|
348
|
+
0040 85*05*09 97 mov (_foo_how1_1_1 + 1),(_glong + 3)
|
|
349
|
+
0043 85*03*0A 98 mov _foo_how2_1_1,(_glong + 1)
|
|
350
|
+
0046 85*04*0B 99 mov (_foo_how2_1_1 + 1),(_glong + 2)
|
|
351
|
+
|
|
352
|
+
Again, variations of these cases may *not* be recognized. They are standard C expressions, so I heartily recommend these be the only way to get the higher order byte/word, (it is portable). Of course it will be recognized even if it is embedded in other expressions, e.g.:
|
|
353
|
+
|
|
354
|
+
xyz = gint + ((gint >> 8) & 0xFF);
|
|
355
|
+
|
|
356
|
+
will still be recognized.
|
|
357
|
+
|
|
358
|
+
#### Placement of Bank-Selection Instructions
|
|
359
|
+
|
|
360
|
+
For non-intrinsic named address spaces, SDCC will place the bank selection instructions optimally. For details see Philipp Klaus Krause," Optimal Placement of Bank Selection Instructions in Polynomial Time", Proceedings of the 16th International Workshop on Software and Compilers for Embedded Systems, M-SCOPES ’13, pp 23-30. Association for Computing Machinery, 2013.
|
|
361
|
+
|
|
362
|
+
#### Lifetime-Optimal Speculative Partial Redundancy Elimination
|
|
363
|
+
|
|
364
|
+
SDCC has an implementation of lifetime-optimal speculative partial redundancy elimination based on tree-decompositions.
|
|
365
|
+
|
|
366
|
+
#### Register Allocation Register-Allocation
|
|
367
|
+
|
|
368
|
+
SDCC currently has two register allocators. One of them is optimal when optimizing for code size. This register allocator is used by default on all ports except for mcs51, ds390, pic14 and pic16. With the exception of hc08 and s08, it is also the only available register allocator for these ports. Even though it runs in polynomial time, it can be quite slow; therefore the --max-allocs-per-node command line option can be used for a trade-off between compilation speed and quality of the generated code: Lower values result in faster compilation, higher values can result in better code being generated.
|
|
369
|
+
|
|
370
|
+
It first creates a tree-decomposition of the control-flow graph, and then uses dynamic programming bottom-up along the tree-decomposition. Optimality is achieved through the use of a cost function, which gives cost for instructions under register assignments. The cost function is target-specific and has to be implemented for each port; in all current SDCC ports the cost function is integrated into code generation.
|
|
371
|
+
|
|
372
|
+
For more details on how this register allocator works, see: Philipp Klaus Krause," Optimal Register Allocation in Polynomial Time", Compiler Construction - 22nd International Conference, CC 2013, Held as Part of the European Joint Conferences on Theory and Practice of Software, ETAPS 2013. Proceedings, Lecture Notes in Computer Science, volume 7791, pp. 1-20. Springer, 2013. Also: Philipp Klaus Krause," Bytewise Register Allocation", Proceedings of the 18th International Workshop on Software and Compilers for Embedded Systems, SCOPES ’15, pp 22-27. Association for Computing Machinery, 2015.
|
|
373
|
+
|
|
374
|
+
#### Peephole Optimizer Peephole optimizer
|
|
375
|
+
|
|
376
|
+
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 --peep-file* option. The rule language is best illustrated with examples.
|
|
377
|
+
|
|
378
|
+
replace {
|
|
379
|
+
mov %1,a
|
|
380
|
+
mov a,%1
|
|
381
|
+
} by {
|
|
382
|
+
mov %1,a
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
The above rule will change the following assembly Assembler routines sequence:
|
|
386
|
+
|
|
387
|
+
mov r1,a
|
|
388
|
+
mov a,r1
|
|
389
|
+
|
|
390
|
+
to
|
|
391
|
+
|
|
392
|
+
mov r1,a
|
|
393
|
+
|
|
394
|
+
Note: All occurrences of a *%n* (pattern variable) must denote the same string. With the above rule, the assembly sequence:
|
|
395
|
+
|
|
396
|
+
mov r1,a
|
|
397
|
+
mov a,r2
|
|
398
|
+
|
|
399
|
+
will remain unmodified.
|
|
400
|
+
|
|
401
|
+
Other special case optimizations may be added by the user (via *--peep-file option*). E.g. some variants of the 8051 MCU MCS51 variants allow only ajmp and acall. The following two rules will change all ljmp and lcall to ajmp and acall
|
|
402
|
+
|
|
403
|
+
replace { lcall %1 } by { acall %1 }
|
|
404
|
+
replace { ljmp %1 } by { ajmp %1 }
|
|
405
|
+
|
|
406
|
+
(NOTE: from version 2.7.3 on, you can use option - **-acall-ajmp --acall-ajmp, which also takes care of aligning the interrupt vectors properly.)
|
|
407
|
+
|
|
408
|
+
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.
|
|
409
|
+
|
|
410
|
+
The syntax for a rule is as follows:
|
|
411
|
+
|
|
412
|
+
rule:= replace [restart] '{' <assembly sequence> '\ n'
|
|
413
|
+
'}' by '{' '\ n'
|
|
414
|
+
\ n'
|
|
415
|
+
'}' [if <functionName>] '\ n'
|
|
416
|
+
|
|
417
|
+
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:
|
|
418
|
+
|
|
419
|
+
replace restart {
|
|
420
|
+
pop %1
|
|
421
|
+
push %1 } by {
|
|
422
|
+
; nop
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
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.:
|
|
426
|
+
|
|
427
|
+
pop ar1
|
|
428
|
+
pop ar2
|
|
429
|
+
push ar2
|
|
430
|
+
push ar1
|
|
431
|
+
|
|
432
|
+
would result in:
|
|
433
|
+
|
|
434
|
+
pop ar1
|
|
435
|
+
; nop
|
|
436
|
+
push ar1
|
|
437
|
+
|
|
438
|
+
*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:
|
|
439
|
+
|
|
440
|
+
; nop
|
|
441
|
+
; nop
|
|
442
|
+
|
|
443
|
+
A conditional function can be attached to a rule. Attaching rules are somewhat more involved, let's illustrate this with an example.
|
|
444
|
+
|
|
445
|
+
replace {
|
|
446
|
+
ljmp %5
|
|
447
|
+
%2:
|
|
448
|
+
} by {
|
|
449
|
+
sjmp %5
|
|
450
|
+
%2:
|
|
451
|
+
} if labelInRange
|
|
452
|
+
|
|
453
|
+
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.
|
|
454
|
+
|
|
455
|
+
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.
|
|
456
|
+
|
|
457
|
+
### Cyclomatic Complexity Cyclomatic complexity
|
|
458
|
+
|
|
459
|
+
Cyclomatic complexity of a function is defined as the number of independent paths the program can take during execution of the function. This is an important number since it defines the number test cases you have to generate to validate the function. The accepted industry standard for complexity number is 10, if the cyclomatic complexity reported by SDCC exceeds 10 you should think about simplification of the function logic. Note that the complexity level is not related to the number of lines of code in a function. Large functions can have low complexity, and small functions can have large complexity levels.
|
|
460
|
+
|
|
461
|
+
SDCC uses the following formula to compute the complexity:
|
|
462
|
+
|
|
463
|
+
complexity = (number of edges in control flow graph) - (number of nodes in control flow graph) + 2;
|
|
464
|
+
|
|
465
|
+
Having said that the industry standard is 10, you should be aware that in some cases it be may unavoidable to have a complexity level of less than 10. For example if you have switch statement with more than 10 case labels, each case label adds one to the complexity level. The complexity level is by no means an absolute measure of the algorithmic complexity of the function, it does however provide a good starting point for which functions you might look at for further optimization.
|
|
466
|
+
|
|
467
|
+
### Retargetting for other Processors
|
|
468
|
+
|
|
469
|
+
The issues for retargetting the compiler are far too numerous to be covered by this document. What follows is a brief description of each of the phases of the compiler and its MCU dependency.
|
|
470
|
+
|
|
471
|
+
- Parsing the source and building the annotated parse tree. This phase is largely MCU independent (except for the language extensions). Syntax & semantic checks are also done in this phase, along with some initial optimizations like back patching labels and the pattern matching optimizations like bit-rotation etc.
|
|
472
|
+
- The second phase involves generating an intermediate code which can be easy manipulated during the later phases. This phase is entirely MCU independent. The intermediate code generation assumes the target machine has unlimited number of registers, and designates them with the name iTemp. The compiler can be made to dump a human readable form of the code generated by using the --dumpraw option.
|
|
473
|
+
- This phase does the bulk of the standard optimizations and is also MCU independent. This phase can be broken down into several sub-phases:
|
|
474
|
+
|
|
475
|
+
Break down intermediate code (iCode) into basic blocks.
|
|
476
|
+
Do control flow & data flow analysis on the basic blocks.
|
|
477
|
+
Do local common subexpression elimination, then global subexpression elimination
|
|
478
|
+
Dead code elimination
|
|
479
|
+
Loop optimizations
|
|
480
|
+
If loop optimizations caused any changes then do 'global subexpression elimination' and 'dead code elimination' again.
|
|
481
|
+
- This phase determines the live-ranges; by live range I mean those iTemp variables defined by the compiler that still survive after all the optimizations. Live range analysis Live range analysis is essential for register allocation, since these computation determines which of these iTemps will be assigned to registers, and for how long.
|
|
482
|
+
- Phase five is register allocation. For new ports register allocator described above in Register Allocation should be used in most cases, since it can result in substantially better code. In the old register allocator, there are two parts to register allocation.
|
|
483
|
+
|
|
484
|
+
The first part I call 'register packing' (for lack of a better term). In this case several MCU specific expression folding is done to reduce register pressure.
|
|
485
|
+
|
|
486
|
+
The second part is more MCU independent and deals with allocating registers to the remaining live ranges. A lot of MCU specific code does creep into this phase because of the limited number of index registers available in the 8051.
|
|
487
|
+
- The Code generation phase is (unhappily), entirely MCU dependent and very little (if any at all) of this code can be reused for other MCU. However the scheme for allocating a homogenized assembler operand for each iCode operand may be reused.
|
|
488
|
+
- As mentioned in the optimization section the peep-hole optimizer is rule based system, which can reprogrammed for other MCUs.
|
|
489
|
+
More information is available on SDCC Wiki wiki (preliminary link https://sourceforge.net/p/sdcc/wiki/Adding%20a%20port/) and in the thread http://sourceforge.net/mailarchive/message.php?msg_id=13954144.
|