]> git.sur5r.net Git - cc65/blob - doc/coding.sgml
Moved GEOS VLIR assembler sample from 'samples' to 'testcode' because:
[cc65] / doc / coding.sgml
1 <!doctype linuxdoc system>
2
3 <article>
4 <title>cc65 coding hints
5 <author>Ullrich von Bassewitz, <htmlurl url="mailto:uz@cc65.org" name="uz@cc65.org">
6 <date>2000-12-03, 2009-09-01
7
8 <abstract>
9 How to generate the most effective code with cc65.
10 </abstract>
11
12
13
14 <sect>Use prototypes<p>
15
16 This will not only help to find errors between separate modules, it will also
17 generate better code, since the compiler must not assume that a variable sized
18 parameter list is in place and must not pass the argument count to the called
19 function. This will lead to shorter and faster code.
20
21
22
23 <sect>Don't declare auto variables in nested function blocks<p>
24
25 Variable declarations in nested blocks are usually a good thing. But with
26 cc65, there is a drawback: Since the compiler generates code in one pass, it
27 must create the variables on the stack each time the block is entered and
28 destroy them when the block is left. This causes a speed penalty and larger
29 code.
30
31
32
33 <sect>Remember that the compiler does no high level optimizations<p>
34
35 The compiler needs hints from you about the code to generate. It will try to
36 optimize the generated code, but follow the outline you gave in your C
37 program. So for example, when accessing indexed data structures, get a pointer
38 to the element and use this pointer instead of calculating the index again and
39 again. If you want to have your loops unrolled, or loop invariant code moved
40 outside the loop, you have to do that yourself.
41
42
43
44 <sect>Longs are slow!<p>
45
46 While long support is necessary for some things, it's really, really slow on
47 the 6502. Remember that any long variable will use 4 bytes of memory, and any
48 operation works on double the data compared to an int.
49
50
51
52 <sect>Use unsigned types wherever possible<p>
53
54 The 6502 CPU has no opcodes to handle signed values greater than 8 bit. So
55 sign extension, test of signedness etc. has to be done with extra code. As a
56 consequence, the code to handle signed operations is usually a bit larger and
57 slower than the same code for unsigned types.
58
59
60
61 <sect>Use chars instead of ints if possible<p>
62
63 While in arithmetic operations, chars are immidiately promoted to ints, they
64 are passed as chars in parameter lists and are accessed as chars in variables.
65 The code generated is usually not much smaller, but it is faster, since
66 accessing chars is faster. For several operations, the generated code may be
67 better if intermediate results that are known not to be larger than 8 bit are
68 casted to chars.
69
70 You should especially use unsigned chars for loop control variables if the
71 loop is known not to execute more than 255 times.
72
73
74
75 <sect>Make the size of your array elements one of 1, 2, 4, 8<p>
76
77 When indexing into an array, the compiler has to calculate the byte offset
78 into the array, which is the index multiplied by the size of one element. When
79 doing the multiplication, the compiler will do a strength reduction, that is,
80 replace the multiplication by a shift if possible. For the values 2, 4 and 8,
81 there are even more specialized subroutines available. So, array access is
82 fastest when using one of these sizes.
83
84
85
86 <sect>Expressions are evaluated from left to right<p>
87
88 Since cc65 is not building an explicit expression tree when parsing an
89 expression, constant subexpressions may not be detected and optimized properly
90 if you don't help. Look at this example:
91
92 <tscreen><verb>
93       #define OFFS   4
94       int  i;
95       i = i + OFFS + 3;
96 </verb></tscreen>
97
98 The expression is parsed from left to right, that means, the compiler sees 'i',
99 and puts it contents into the secondary register. Next is OFFS, which is
100 constant. The compiler emits code to add a constant to the secondary register.
101 Same thing again for the constant 3. So the code produced contains a fetch
102 of 'i', two additions of constants, and a store (into 'i'). Unfortunately, the
103 compiler does not see, that "OFFS + 3" is a constant for itself, since it does
104 its evaluation from left to right. There are some ways to help the compiler
105 to recognize expression like this:
106
107 <enum>
108
109 <item>Write "i = OFFS + 3 + i;". Since the first and second operand are
110 constant, the compiler will evaluate them at compile time reducing the code to
111 a fetch, one addition (secondary + constant) and one store.
112
113 <item>Write "i = i + (OFFS + 3)". When seeing the opening parenthesis, the
114 compiler will start a new expression evaluation for the stuff in the braces,
115 and since all operands in the subexpression are constant, it will detect this
116 and reduce the code to one fetch, one addition and one store.
117
118 </enum>
119
120
121 <sect>Use the preincrement and predecrement operators<p>
122
123 The compiler is not always smart enough to figure out, if the rvalue of an
124 increment is used or not. So it has to save and restore that value when
125 producing code for the postincrement and postdecrement operators, even if this
126 value is never used. To avoid the additional overhead, use the preincrement
127 and predecrement operators if you don't need the resulting value. That means,
128 use
129
130 <tscreen><verb>
131         ...
132         ++i;
133         ...
134 </verb></tscreen>
135
136     instead of
137
138 <tscreen><verb>
139         ...
140         i++;
141         ...
142 </verb></tscreen>
143
144
145
146 <sect>Use constants to access absolute memory locations<p>
147
148 The compiler produces optimized code, if the value of a pointer is a constant.
149 So, to access direct memory locations, use
150
151 <tscreen><verb>
152         #define VDC_STATUS 0xD601
153         *(char*)VDC_STATUS = 0x01;
154 </verb></tscreen>
155
156 That will be translated to
157
158 <tscreen><verb>
159         lda     #$01
160         sta     $D601
161 </verb></tscreen>
162
163 The constant value detection works also for struct pointers and arrays, if the
164 subscript is a constant. So
165
166 <tscreen><verb>
167         #define VDC     ((unsigned char*)0xD600)
168         #define STATUS  0x01
169         VDC[STATUS] = 0x01;
170 </verb></tscreen>
171
172 will also work.
173
174 If you first load the constant into a variable and use that variable to access
175 an absolute memory location, the generated code will be much slower, since the
176 compiler does not know anything about the contents of the variable.
177
178
179
180 <sect>Use initialized local variables<p>
181
182 Initialization of local variables when declaring them gives shorter and faster
183 code. So, use
184
185 <tscreen><verb>
186         int i = 1;
187 </verb></tscreen>
188
189 instead of
190
191 <tscreen><verb>
192         int i;
193         i = 1;
194 </verb></tscreen>
195
196 But beware: To maximize your savings, don't mix uninitialized and initialized
197 variables. Create one block of initialized variables and one of uniniitalized
198 ones. The reason for this is, that the compiler will sum up the space needed
199 for uninitialized variables as long as possible, and then allocate the space
200 once for all these variables. If you mix uninitialized and initialized
201 variables, you force the compiler to allocate space for the uninitialized
202 variables each time, it parses an initialized one. So do this:
203
204 <tscreen><verb>
205         int i, j;
206         int a = 3;
207         int b = 0;
208 </verb></tscreen>
209
210 instead of
211
212 <tscreen><verb>
213         int i;
214         int a = 3;
215         int j;
216         int b = 0;
217 </verb></tscreen>
218
219 The latter will work, but will create larger and slower code.
220
221
222
223 <sect>Use the array operator &lsqb;&rsqb; even for pointers<p>
224
225 When addressing an array via a pointer, don't use the plus and dereference
226 operators, but the array operator. This will generate better code in some
227 common cases.
228
229 Don't use
230
231 <tscreen><verb>
232         char* a;
233         char b, c;
234         char b = *(a + c);
235 </verb></tscreen>
236
237 Use
238
239 <tscreen><verb>
240         char* a;
241         char b, c;
242         char b = a[c];
243 </verb></tscreen>
244
245 instead.
246
247
248
249 <sect>Use register variables with care<p>
250
251 Register variables may give faster and shorter code, but they do also have an
252 overhead. Register variables are actually zero page locations, so using them
253 saves roughly one cycle per access. The calling routine may also use register
254 variables, so the old values have to be saved on function entry and restored
255 on exit. Saving an d restoring has an overhead of about 70 cycles per 2 byte
256 variable. It is easy to see, that - apart from the additional code that is
257 needed to save and restore the values - you need to make heavy use of a
258 variable to justify the overhead.
259
260 As a general rule: Use register variables only for pointers that are
261 dereferenced several times in your function, or for heavily used induction
262 variables in a loop (with several 100 accesses).
263
264 When declaring register variables, try to keep them together, because this
265 will allow the compiler to save and restore the old values in one chunk, and
266 not in several.
267
268 And remember: Register variables must be enabled with <tt/-r/ or <tt/-Or/.
269
270
271
272 <sect>Decimal constants greater than 0x7FFF are actually long ints<p>
273
274 The language rules for constant numeric values specify that decimal constants
275 without a type suffix that are not in integer range must be of type long int
276 or unsigned long int. So a simple constant like 40000 is of type long int!
277 This is often unexpected and may cause an expression to be evaluated with 32
278 bits. While in many cases the compiler takes care about it, in some places it
279 can't. So be careful when you get a warning like
280
281 <tscreen><verb>
282         test.c(7): Warning: Constant is long
283 </verb></tscreen>
284
285 Use the <tt/U/, <tt/L/ or <tt/UL/ suffixes to tell the compiler the desired
286 type of a numeric constant.
287
288
289
290 <sect>Access to parameters in variadic functions is expensive<p>
291
292 Since cc65 has the "wrong" calling order, the location of the fixed parameters
293 in a variadic function (a function with a variable parameter list) depends on
294 the number and size of variable arguments passed. Since this number and size
295 is unknown at compile time, the compiler will generate code to calculate the
296 location on the stack when needed.
297
298 Because of this additional code, accessing the fixed parameters in a variadic
299 function is much more expensive than access to parameters in a "normal"
300 function. Unfortunately, this additional code is also invisible to the
301 programmer, so it is easy to forget.
302
303 As a rule of thumb, if you access such a parameter more than once, you should
304 think about copying it into a normal variable and using this variable instead.
305
306
307 </article>
308