1 | /*****************************************************************************/ |
2 | /* */ |
3 | /* stdfunc.c */ |
4 | /* */ |
5 | /* Handle inlining of known functions for the cc65 compiler */ |
6 | /* */ |
7 | /* */ |
8 | /* */ |
9 | /* (C) 1998-2010 Ullrich von Bassewitz */ |
10 | /* Roemerstrasse 52 */ |
11 | /* D-70794 Filderstadt */ |
12 | /* EMail: uz@cc65.org */ |
13 | /* */ |
14 | /* */ |
15 | /* This software is provided 'as-is', without any expressed or implied */ |
16 | /* warranty. In no event will the authors be held liable for any damages */ |
17 | /* arising from the use of this software. */ |
18 | /* */ |
19 | /* Permission is granted to anyone to use this software for any purpose, */ |
20 | /* including commercial applications, and to alter it and redistribute it */ |
21 | /* freely, subject to the following restrictions: */ |
22 | /* */ |
23 | /* 1. The origin of this software must not be misrepresented; you must not */ |
24 | /* claim that you wrote the original software. If you use this software */ |
25 | /* in a product, an acknowledgment in the product documentation would be */ |
26 | /* appreciated but is not required. */ |
27 | /* 2. Altered source versions must be plainly marked as such, and must not */ |
28 | /* be misrepresented as being the original software. */ |
29 | /* 3. This notice may not be removed or altered from any source */ |
30 | /* distribution. */ |
31 | /* */ |
32 | /*****************************************************************************/ |
33 | |
34 | |
35 | |
36 | #include <stdlib.h> |
37 | #include <string.h> |
38 | |
39 | /* common */ |
40 | #include "attrib.h" |
41 | #include "check.h" |
42 | |
43 | /* cc65 */ |
44 | #include "asmcode.h" |
45 | #include "asmlabel.h" |
46 | #include "codegen.h" |
47 | #include "error.h" |
48 | #include "funcdesc.h" |
49 | #include "global.h" |
50 | #include "litpool.h" |
51 | #include "loadexpr.h" |
52 | #include "scanner.h" |
53 | #include "stackptr.h" |
54 | #include "stdfunc.h" |
55 | #include "stdnames.h" |
56 | #include "typeconv.h" |
57 | |
58 | |
59 | |
60 | /*****************************************************************************/ |
61 | /* Function forwards */ |
62 | /*****************************************************************************/ |
63 | |
64 | |
65 | |
66 | static void StdFunc_memcpy (FuncDesc*, ExprDesc*); |
67 | static void StdFunc_memset (FuncDesc*, ExprDesc*); |
68 | static void StdFunc_strcmp (FuncDesc*, ExprDesc*); |
69 | static void StdFunc_strcpy (FuncDesc*, ExprDesc*); |
70 | static void StdFunc_strlen (FuncDesc*, ExprDesc*); |
71 | |
72 | |
73 | |
74 | /*****************************************************************************/ |
75 | /* Data */ |
76 | /*****************************************************************************/ |
77 | |
78 | |
79 | |
80 | /* Table with all known functions and their handlers. Must be sorted |
81 | ** alphabetically! |
82 | */ |
83 | static struct StdFuncDesc { |
84 | const char* Name; |
85 | void (*Handler) (FuncDesc*, ExprDesc*); |
86 | } StdFuncs[] = { |
87 | { "memcpy" , StdFunc_memcpy }, |
88 | { "memset" , StdFunc_memset }, |
89 | { "strcmp" , StdFunc_strcmp }, |
90 | { "strcpy" , StdFunc_strcpy }, |
91 | { "strlen" , StdFunc_strlen }, |
92 | |
93 | }; |
94 | #define FUNC_COUNT (sizeof (StdFuncs) / sizeof (StdFuncs[0])) |
95 | |
96 | typedef struct ArgDesc ArgDesc; |
97 | struct ArgDesc { |
98 | const Type* ArgType; /* Required argument type */ |
99 | ExprDesc Expr; /* Argument expression */ |
100 | const Type* Type; /* The original type before conversion */ |
101 | CodeMark Load; /* Start of argument load code */ |
102 | CodeMark Push; /* Start of argument push code */ |
103 | CodeMark End; /* End of the code for calculation+push */ |
104 | unsigned Flags; /* Code generation flags */ |
105 | }; |
106 | |
107 | |
108 | |
109 | /*****************************************************************************/ |
110 | /* Helper functions */ |
111 | /*****************************************************************************/ |
112 | |
113 | |
114 | |
115 | static int CmpFunc (const void* Key, const void* Elem) |
116 | /* Compare function for bsearch */ |
117 | { |
118 | return strcmp ((const char*) Key, ((const struct StdFuncDesc*) Elem)->Name); |
119 | } |
120 | |
121 | |
122 | |
123 | static long ArrayElementCount (const ArgDesc* Arg) |
124 | /* Check if the type of the given argument is an array. If so, and if the |
125 | ** element count is known, return it. In all other cases, return UNSPECIFIED. |
126 | */ |
127 | { |
128 | long Count; |
129 | |
130 | if (IsTypeArray (Arg->Type)) { |
131 | Count = GetElementCount (Arg->Type); |
132 | if (Count == FLEXIBLE) { |
133 | /* Treat as unknown */ |
134 | Count = UNSPECIFIED; |
135 | } |
136 | } else { |
137 | Count = UNSPECIFIED; |
138 | } |
139 | return Count; |
140 | } |
141 | |
142 | |
143 | |
144 | static void ParseArg (ArgDesc* Arg, Type* Type) |
145 | /* Parse one argument but do not push it onto the stack. Make all fields in |
146 | ** Arg valid. |
147 | */ |
148 | { |
149 | /* We have a prototype, so chars may be pushed as chars */ |
150 | Arg->Flags = CF_FORCECHAR; |
151 | |
152 | /* Remember the required argument type */ |
153 | Arg->ArgType = Type; |
154 | |
155 | /* Read the expression we're going to pass to the function */ |
156 | MarkedExprWithCheck (hie1, &Arg->Expr); |
157 | |
158 | /* Remember the actual argument type */ |
159 | Arg->Type = Arg->Expr.Type; |
160 | |
161 | /* Convert this expression to the expected type */ |
162 | TypeConversion (&Arg->Expr, Type); |
163 | |
164 | /* Remember the following code position */ |
165 | GetCodePos (&Arg->Load); |
166 | |
167 | /* If the value is a constant, set the flag, otherwise load it into the |
168 | ** primary register. |
169 | */ |
170 | if (ED_IsConstAbsInt (&Arg->Expr) && ED_CodeRangeIsEmpty (&Arg->Expr)) { |
171 | /* Remember that we have a constant value */ |
172 | Arg->Flags |= CF_CONST; |
173 | } else { |
174 | /* Load into the primary */ |
175 | LoadExpr (CF_NONE, &Arg->Expr); |
176 | } |
177 | |
178 | /* Remember the following code position */ |
179 | GetCodePos (&Arg->Push); |
180 | GetCodePos (&Arg->End); |
181 | |
182 | /* Use the type of the argument for the push */ |
183 | Arg->Flags |= TypeOf (Arg->Expr.Type); |
184 | } |
185 | |
186 | |
187 | |
188 | void AddCmpCodeIfSizeNot256 (const char* Code, long Size) |
189 | /* Add a line of Assembly code that compares an index register |
190 | ** only if it isn't comparing to #<256. (If the next line |
191 | ** is "bne", then this will avoid a redundant line.) |
192 | */ |
193 | { |
194 | if (Size != 256) { |
195 | AddCodeLine (Code, (unsigned int)Size); |
196 | } |
197 | } |
198 | |
199 | |
200 | |
201 | /*****************************************************************************/ |
202 | /* memcpy */ |
203 | /*****************************************************************************/ |
204 | |
205 | |
206 | |
207 | static void StdFunc_memcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr) |
208 | /* Handle the memcpy function */ |
209 | { |
210 | /* Argument types: (void*, const void*, size_t) */ |
211 | static Type Arg1Type[] = { TYPE(T_PTR), TYPE(T_VOID), TYPE(T_END) }; |
212 | static Type Arg2Type[] = { TYPE(T_PTR), TYPE(T_VOID|T_QUAL_CONST), TYPE(T_END) }; |
213 | static Type Arg3Type[] = { TYPE(T_SIZE_T), TYPE(T_END) }; |
214 | |
215 | ArgDesc Arg1, Arg2, Arg3; |
216 | unsigned ParamSize = 0; |
217 | unsigned Label; |
218 | int Offs; |
219 | |
220 | /* Argument #1 */ |
221 | ParseArg (&Arg1, Arg1Type); |
222 | g_push (Arg1.Flags, Arg1.Expr.IVal); |
223 | GetCodePos (&Arg1.End); |
224 | ParamSize += SizeOf (Arg1Type); |
225 | ConsumeComma (); |
226 | |
227 | /* Argument #2 */ |
228 | ParseArg (&Arg2, Arg2Type); |
229 | g_push (Arg2.Flags, Arg2.Expr.IVal); |
230 | GetCodePos (&Arg2.End); |
231 | ParamSize += SizeOf (Arg2Type); |
232 | ConsumeComma (); |
233 | |
234 | /* Argument #3. Since memcpy is a fastcall function, we must load the |
235 | ** arg into the primary if it is not already there. This parameter is |
236 | ** also ignored for the calculation of the parameter size, since it is |
237 | ** not passed via the stack. |
238 | */ |
239 | ParseArg (&Arg3, Arg3Type); |
240 | if (Arg3.Flags & CF_CONST) { |
241 | LoadExpr (CF_NONE, &Arg3.Expr); |
242 | } |
243 | |
244 | /* Emit the actual function call. This will also cleanup the stack. */ |
245 | g_call (CF_FIXARGC, Func_memcpy, ParamSize); |
246 | |
247 | if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal == 0) { |
248 | |
249 | /* memcpy has been called with a count argument of zero */ |
250 | Warning ("Call to memcpy has no effect" ); |
251 | |
252 | /* Remove all of the generated code but the load of the first |
253 | ** argument, which is what memcpy returns. |
254 | */ |
255 | RemoveCode (&Arg1.Push); |
256 | |
257 | /* Set the function result to the first argument */ |
258 | *Expr = Arg1.Expr; |
259 | |
260 | /* Bail out, no need for further improvements */ |
261 | goto ExitPoint; |
262 | } |
263 | |
264 | if (IS_Get (&InlineStdFuncs)) { |
265 | |
266 | /* We've generated the complete code for the function now and know the |
267 | ** types of all parameters. Check for situations where better code can |
268 | ** be generated. If such a situation is detected, throw away the |
269 | ** generated, and emit better code. |
270 | */ |
271 | if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 && |
272 | ((ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr)) || |
273 | (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr))) && |
274 | ((ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) || |
275 | (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr)))) { |
276 | |
277 | int Reg1 = ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr); |
278 | int Reg2 = ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr); |
279 | |
280 | /* Drop the generated code */ |
281 | RemoveCode (&Arg1.Expr.Start); |
282 | |
283 | /* We need a label */ |
284 | Label = GetLocalLabel (); |
285 | |
286 | /* Generate memcpy code */ |
287 | if (Arg3.Expr.IVal <= 129) { |
288 | |
289 | AddCodeLine ("ldy #$%02X" , (unsigned char) (Arg3.Expr.IVal-1)); |
290 | g_defcodelabel (Label); |
291 | if (Reg2) { |
292 | AddCodeLine ("lda (%s),y" , ED_GetLabelName (&Arg2.Expr, 0)); |
293 | } else { |
294 | AddCodeLine ("lda %s,y" , ED_GetLabelName (&Arg2.Expr, 0)); |
295 | } |
296 | if (Reg1) { |
297 | AddCodeLine ("sta (%s),y" , ED_GetLabelName (&Arg1.Expr, 0)); |
298 | } else { |
299 | AddCodeLine ("sta %s,y" , ED_GetLabelName (&Arg1.Expr, 0)); |
300 | } |
301 | AddCodeLine ("dey" ); |
302 | AddCodeLine ("bpl %s" , LocalLabelName (Label)); |
303 | |
304 | } else { |
305 | |
306 | AddCodeLine ("ldy #$00" ); |
307 | g_defcodelabel (Label); |
308 | if (Reg2) { |
309 | AddCodeLine ("lda (%s),y" , ED_GetLabelName (&Arg2.Expr, 0)); |
310 | } else { |
311 | AddCodeLine ("lda %s,y" , ED_GetLabelName (&Arg2.Expr, 0)); |
312 | } |
313 | if (Reg1) { |
314 | AddCodeLine ("sta (%s),y" , ED_GetLabelName (&Arg1.Expr, 0)); |
315 | } else { |
316 | AddCodeLine ("sta %s,y" , ED_GetLabelName (&Arg1.Expr, 0)); |
317 | } |
318 | AddCodeLine ("iny" ); |
319 | AddCmpCodeIfSizeNot256 ("cpy #$%02X" , Arg3.Expr.IVal); |
320 | AddCodeLine ("bne %s" , LocalLabelName (Label)); |
321 | |
322 | } |
323 | |
324 | /* memcpy returns the address, so the result is actually identical |
325 | ** to the first argument. |
326 | */ |
327 | *Expr = Arg1.Expr; |
328 | |
329 | /* Bail out, no need for further processing */ |
330 | goto ExitPoint; |
331 | } |
332 | |
333 | if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 && |
334 | ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr) && |
335 | ED_IsRVal (&Arg1.Expr) && ED_IsLocStack (&Arg1.Expr) && |
336 | (Arg1.Expr.IVal - StackPtr) + Arg3.Expr.IVal < 256) { |
337 | |
338 | /* It is possible to just use one index register even if the stack |
339 | ** offset is not zero, by adjusting the offset to the constant |
340 | ** address accordingly. But we cannot do this if the data in |
341 | ** question is in the register space or at an absolute address less |
342 | ** than 256. Register space is zero page, which means that the |
343 | ** address calculation could overflow in the linker. |
344 | */ |
345 | int AllowOneIndex = !ED_IsLocRegister (&Arg2.Expr) && |
346 | !(ED_IsLocAbs (&Arg2.Expr) && Arg2.Expr.IVal < 256); |
347 | |
348 | /* Calculate the real stack offset */ |
349 | Offs = ED_GetStackOffs (&Arg1.Expr, 0); |
350 | |
351 | /* Drop the generated code */ |
352 | RemoveCode (&Arg1.Expr.Start); |
353 | |
354 | /* We need a label */ |
355 | Label = GetLocalLabel (); |
356 | |
357 | /* Generate memcpy code */ |
358 | if (Arg3.Expr.IVal <= 129 && !AllowOneIndex) { |
359 | |
360 | if (Offs == 0) { |
361 | AddCodeLine ("ldy #$%02X" , (unsigned char) (Offs + Arg3.Expr.IVal - 1)); |
362 | g_defcodelabel (Label); |
363 | AddCodeLine ("lda %s,y" , ED_GetLabelName (&Arg2.Expr, -Offs)); |
364 | AddCodeLine ("sta (sp),y" ); |
365 | AddCodeLine ("dey" ); |
366 | AddCodeLine ("bpl %s" , LocalLabelName (Label)); |
367 | } else { |
368 | AddCodeLine ("ldx #$%02X" , (unsigned char) (Arg3.Expr.IVal-1)); |
369 | AddCodeLine ("ldy #$%02X" , (unsigned char) (Offs + Arg3.Expr.IVal - 1)); |
370 | g_defcodelabel (Label); |
371 | AddCodeLine ("lda %s,x" , ED_GetLabelName (&Arg2.Expr, 0)); |
372 | AddCodeLine ("sta (sp),y" ); |
373 | AddCodeLine ("dey" ); |
374 | AddCodeLine ("dex" ); |
375 | AddCodeLine ("bpl %s" , LocalLabelName (Label)); |
376 | } |
377 | |
378 | } else { |
379 | |
380 | if (Offs == 0 || AllowOneIndex) { |
381 | AddCodeLine ("ldy #$%02X" , (unsigned char) Offs); |
382 | g_defcodelabel (Label); |
383 | AddCodeLine ("lda %s,y" , ED_GetLabelName (&Arg2.Expr, -Offs)); |
384 | AddCodeLine ("sta (sp),y" ); |
385 | AddCodeLine ("iny" ); |
386 | AddCmpCodeIfSizeNot256 ("cpy #$%02X" , Offs + Arg3.Expr.IVal); |
387 | AddCodeLine ("bne %s" , LocalLabelName (Label)); |
388 | } else { |
389 | AddCodeLine ("ldx #$00" ); |
390 | AddCodeLine ("ldy #$%02X" , (unsigned char) Offs); |
391 | g_defcodelabel (Label); |
392 | AddCodeLine ("lda %s,x" , ED_GetLabelName (&Arg2.Expr, 0)); |
393 | AddCodeLine ("sta (sp),y" ); |
394 | AddCodeLine ("iny" ); |
395 | AddCodeLine ("inx" ); |
396 | AddCmpCodeIfSizeNot256 ("cpx #$%02X" , Arg3.Expr.IVal); |
397 | AddCodeLine ("bne %s" , LocalLabelName (Label)); |
398 | } |
399 | |
400 | } |
401 | |
402 | /* memcpy returns the address, so the result is actually identical |
403 | ** to the first argument. |
404 | */ |
405 | *Expr = Arg1.Expr; |
406 | |
407 | /* Bail out, no need for further processing */ |
408 | goto ExitPoint; |
409 | } |
410 | |
411 | if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 && |
412 | ED_IsRVal (&Arg2.Expr) && ED_IsLocStack (&Arg2.Expr) && |
413 | (Arg2.Expr.IVal - StackPtr) + Arg3.Expr.IVal < 256 && |
414 | ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) { |
415 | |
416 | /* It is possible to just use one index register even if the stack |
417 | ** offset is not zero, by adjusting the offset to the constant |
418 | ** address accordingly. But we cannot do this if the data in |
419 | ** question is in the register space or at an absolute address less |
420 | ** than 256. Register space is zero page, which means that the |
421 | ** address calculation could overflow in the linker. |
422 | */ |
423 | int AllowOneIndex = !ED_IsLocRegister (&Arg1.Expr) && |
424 | !(ED_IsLocAbs (&Arg1.Expr) && Arg1.Expr.IVal < 256); |
425 | |
426 | /* Calculate the real stack offset */ |
427 | Offs = ED_GetStackOffs (&Arg2.Expr, 0); |
428 | |
429 | /* Drop the generated code */ |
430 | RemoveCode (&Arg1.Expr.Start); |
431 | |
432 | /* We need a label */ |
433 | Label = GetLocalLabel (); |
434 | |
435 | /* Generate memcpy code */ |
436 | if (Arg3.Expr.IVal <= 129 && !AllowOneIndex) { |
437 | |
438 | if (Offs == 0) { |
439 | AddCodeLine ("ldy #$%02X" , (unsigned char) (Arg3.Expr.IVal - 1)); |
440 | g_defcodelabel (Label); |
441 | AddCodeLine ("lda (sp),y" ); |
442 | AddCodeLine ("sta %s,y" , ED_GetLabelName (&Arg1.Expr, 0)); |
443 | AddCodeLine ("dey" ); |
444 | AddCodeLine ("bpl %s" , LocalLabelName (Label)); |
445 | } else { |
446 | AddCodeLine ("ldx #$%02X" , (unsigned char) (Arg3.Expr.IVal-1)); |
447 | AddCodeLine ("ldy #$%02X" , (unsigned char) (Offs + Arg3.Expr.IVal - 1)); |
448 | g_defcodelabel (Label); |
449 | AddCodeLine ("lda (sp),y" ); |
450 | AddCodeLine ("sta %s,x" , ED_GetLabelName (&Arg1.Expr, 0)); |
451 | AddCodeLine ("dey" ); |
452 | AddCodeLine ("dex" ); |
453 | AddCodeLine ("bpl %s" , LocalLabelName (Label)); |
454 | } |
455 | |
456 | } else { |
457 | |
458 | if (Offs == 0 || AllowOneIndex) { |
459 | AddCodeLine ("ldy #$%02X" , (unsigned char) Offs); |
460 | g_defcodelabel (Label); |
461 | AddCodeLine ("lda (sp),y" ); |
462 | AddCodeLine ("sta %s,y" , ED_GetLabelName (&Arg1.Expr, -Offs)); |
463 | AddCodeLine ("iny" ); |
464 | AddCmpCodeIfSizeNot256 ("cpy #$%02X" , Offs + Arg3.Expr.IVal); |
465 | AddCodeLine ("bne %s" , LocalLabelName (Label)); |
466 | } else { |
467 | AddCodeLine ("ldx #$00" ); |
468 | AddCodeLine ("ldy #$%02X" , (unsigned char) Offs); |
469 | g_defcodelabel (Label); |
470 | AddCodeLine ("lda (sp),y" ); |
471 | AddCodeLine ("sta %s,x" , ED_GetLabelName (&Arg1.Expr, 0)); |
472 | AddCodeLine ("iny" ); |
473 | AddCodeLine ("inx" ); |
474 | AddCmpCodeIfSizeNot256 ("cpx #$%02X" , Arg3.Expr.IVal); |
475 | AddCodeLine ("bne %s" , LocalLabelName (Label)); |
476 | } |
477 | |
478 | } |
479 | |
480 | /* memcpy returns the address, so the result is actually identical |
481 | ** to the first argument. |
482 | */ |
483 | *Expr = Arg1.Expr; |
484 | |
485 | /* Bail out, no need for further processing */ |
486 | goto ExitPoint; |
487 | } |
488 | |
489 | if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 && |
490 | ED_IsRVal (&Arg2.Expr) && ED_IsLocStack (&Arg2.Expr) && |
491 | (Offs = ED_GetStackOffs (&Arg2.Expr, 0)) == 0) { |
492 | |
493 | /* Drop the generated code but leave the load of the first argument*/ |
494 | RemoveCode (&Arg1.Push); |
495 | |
496 | /* We need a label */ |
497 | Label = GetLocalLabel (); |
498 | |
499 | /* Generate memcpy code */ |
500 | AddCodeLine ("sta ptr1" ); |
501 | AddCodeLine ("stx ptr1+1" ); |
502 | if (Arg3.Expr.IVal <= 129) { |
503 | AddCodeLine ("ldy #$%02X" , (unsigned char) (Arg3.Expr.IVal - 1)); |
504 | g_defcodelabel (Label); |
505 | AddCodeLine ("lda (sp),y" ); |
506 | AddCodeLine ("sta (ptr1),y" ); |
507 | AddCodeLine ("dey" ); |
508 | AddCodeLine ("bpl %s" , LocalLabelName (Label)); |
509 | } else { |
510 | AddCodeLine ("ldy #$00" ); |
511 | g_defcodelabel (Label); |
512 | AddCodeLine ("lda (sp),y" ); |
513 | AddCodeLine ("sta (ptr1),y" ); |
514 | AddCodeLine ("iny" ); |
515 | AddCmpCodeIfSizeNot256 ("cpy #$%02X" , Arg3.Expr.IVal); |
516 | AddCodeLine ("bne %s" , LocalLabelName (Label)); |
517 | } |
518 | |
519 | /* Reload result - X hasn't changed by the code above */ |
520 | AddCodeLine ("lda ptr1" ); |
521 | |
522 | /* The function result is an rvalue in the primary register */ |
523 | ED_MakeRValExpr (Expr); |
524 | Expr->Type = GetFuncReturn (Expr->Type); |
525 | |
526 | /* Bail out, no need for further processing */ |
527 | goto ExitPoint; |
528 | } |
529 | } |
530 | |
531 | /* The function result is an rvalue in the primary register */ |
532 | ED_MakeRValExpr (Expr); |
533 | Expr->Type = GetFuncReturn (Expr->Type); |
534 | |
535 | ExitPoint: |
536 | /* We expect the closing brace */ |
537 | ConsumeRParen (); |
538 | } |
539 | |
540 | |
541 | |
542 | /*****************************************************************************/ |
543 | /* memset */ |
544 | /*****************************************************************************/ |
545 | |
546 | |
547 | |
548 | static void StdFunc_memset (FuncDesc* F attribute ((unused)), ExprDesc* Expr) |
549 | /* Handle the memset function */ |
550 | { |
551 | /* Argument types: (void*, int, size_t) */ |
552 | static Type Arg1Type[] = { TYPE(T_PTR), TYPE(T_VOID), TYPE(T_END) }; |
553 | static Type Arg2Type[] = { TYPE(T_INT), TYPE(T_END) }; |
554 | static Type Arg3Type[] = { TYPE(T_SIZE_T), TYPE(T_END) }; |
555 | |
556 | ArgDesc Arg1, Arg2, Arg3; |
557 | int MemSet = 1; /* Use real memset if true */ |
558 | unsigned ParamSize = 0; |
559 | unsigned Label; |
560 | |
561 | /* Argument #1 */ |
562 | ParseArg (&Arg1, Arg1Type); |
563 | g_push (Arg1.Flags, Arg1.Expr.IVal); |
564 | GetCodePos (&Arg1.End); |
565 | ParamSize += SizeOf (Arg1Type); |
566 | ConsumeComma (); |
567 | |
568 | /* Argument #2. This argument is special in that we will call another |
569 | ** function if it is a constant zero. |
570 | */ |
571 | ParseArg (&Arg2, Arg2Type); |
572 | if ((Arg2.Flags & CF_CONST) != 0 && Arg2.Expr.IVal == 0) { |
573 | /* Don't call memset, call bzero instead */ |
574 | MemSet = 0; |
575 | } else { |
576 | /* Push the argument */ |
577 | g_push (Arg2.Flags, Arg2.Expr.IVal); |
578 | GetCodePos (&Arg2.End); |
579 | ParamSize += SizeOf (Arg2Type); |
580 | } |
581 | ConsumeComma (); |
582 | |
583 | /* Argument #3. Since memset is a fastcall function, we must load the |
584 | ** arg into the primary if it is not already there. This parameter is |
585 | ** also ignored for the calculation of the parameter size, since it is |
586 | ** not passed via the stack. |
587 | */ |
588 | ParseArg (&Arg3, Arg3Type); |
589 | if (Arg3.Flags & CF_CONST) { |
590 | LoadExpr (CF_NONE, &Arg3.Expr); |
591 | } |
592 | |
593 | /* Emit the actual function call. This will also cleanup the stack. */ |
594 | g_call (CF_FIXARGC, MemSet? Func_memset : Func__bzero, ParamSize); |
595 | |
596 | if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal == 0) { |
597 | |
598 | /* memset has been called with a count argument of zero */ |
599 | Warning ("Call to memset has no effect" ); |
600 | |
601 | /* Remove all of the generated code but the load of the first |
602 | ** argument, which is what memset returns. |
603 | */ |
604 | RemoveCode (&Arg1.Push); |
605 | |
606 | /* Set the function result to the first argument */ |
607 | *Expr = Arg1.Expr; |
608 | |
609 | /* Bail out, no need for further improvements */ |
610 | goto ExitPoint; |
611 | } |
612 | |
613 | if (IS_Get (&InlineStdFuncs)) { |
614 | |
615 | /* We've generated the complete code for the function now and know the |
616 | ** types of all parameters. Check for situations where better code can |
617 | ** be generated. If such a situation is detected, throw away the |
618 | ** generated, and emit better code. |
619 | ** Note: Lots of improvements would be possible here, but I will |
620 | ** concentrate on the most common case: memset with arguments 2 and 3 |
621 | ** being constant numerical values. Some checks have shown that this |
622 | ** covers nearly 90% of all memset calls. |
623 | */ |
624 | if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 && |
625 | ED_IsConstAbsInt (&Arg2.Expr) && |
626 | ((ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) || |
627 | (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr)))) { |
628 | |
629 | int Reg = ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr); |
630 | |
631 | /* Drop the generated code */ |
632 | RemoveCode (&Arg1.Expr.Start); |
633 | |
634 | /* We need a label */ |
635 | Label = GetLocalLabel (); |
636 | |
637 | /* Generate memset code */ |
638 | if (Arg3.Expr.IVal <= 129) { |
639 | |
640 | AddCodeLine ("ldy #$%02X" , (unsigned char) (Arg3.Expr.IVal-1)); |
641 | AddCodeLine ("lda #$%02X" , (unsigned char) Arg2.Expr.IVal); |
642 | g_defcodelabel (Label); |
643 | if (Reg) { |
644 | AddCodeLine ("sta (%s),y" , ED_GetLabelName (&Arg1.Expr, 0)); |
645 | } else { |
646 | AddCodeLine ("sta %s,y" , ED_GetLabelName (&Arg1.Expr, 0)); |
647 | } |
648 | AddCodeLine ("dey" ); |
649 | AddCodeLine ("bpl %s" , LocalLabelName (Label)); |
650 | |
651 | } else { |
652 | |
653 | AddCodeLine ("ldy #$00" ); |
654 | AddCodeLine ("lda #$%02X" , (unsigned char) Arg2.Expr.IVal); |
655 | g_defcodelabel (Label); |
656 | if (Reg) { |
657 | AddCodeLine ("sta (%s),y" , ED_GetLabelName (&Arg1.Expr, 0)); |
658 | } else { |
659 | AddCodeLine ("sta %s,y" , ED_GetLabelName (&Arg1.Expr, 0)); |
660 | } |
661 | AddCodeLine ("iny" ); |
662 | AddCmpCodeIfSizeNot256 ("cpy #$%02X" , Arg3.Expr.IVal); |
663 | AddCodeLine ("bne %s" , LocalLabelName (Label)); |
664 | |
665 | } |
666 | |
667 | /* memset returns the address, so the result is actually identical |
668 | ** to the first argument. |
669 | */ |
670 | *Expr = Arg1.Expr; |
671 | |
672 | /* Bail out, no need for further processing */ |
673 | goto ExitPoint; |
674 | } |
675 | |
676 | if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 && |
677 | ED_IsConstAbsInt (&Arg2.Expr) && |
678 | ED_IsRVal (&Arg1.Expr) && ED_IsLocStack (&Arg1.Expr) && |
679 | (Arg1.Expr.IVal - StackPtr) + Arg3.Expr.IVal < 256) { |
680 | |
681 | /* Calculate the real stack offset */ |
682 | int Offs = ED_GetStackOffs (&Arg1.Expr, 0); |
683 | |
684 | /* Drop the generated code */ |
685 | RemoveCode (&Arg1.Expr.Start); |
686 | |
687 | /* We need a label */ |
688 | Label = GetLocalLabel (); |
689 | |
690 | /* Generate memset code */ |
691 | AddCodeLine ("ldy #$%02X" , (unsigned char) Offs); |
692 | AddCodeLine ("lda #$%02X" , (unsigned char) Arg2.Expr.IVal); |
693 | g_defcodelabel (Label); |
694 | AddCodeLine ("sta (sp),y" ); |
695 | AddCodeLine ("iny" ); |
696 | AddCmpCodeIfSizeNot256 ("cpy #$%02X" , Offs + Arg3.Expr.IVal); |
697 | AddCodeLine ("bne %s" , LocalLabelName (Label)); |
698 | |
699 | /* memset returns the address, so the result is actually identical |
700 | ** to the first argument. |
701 | */ |
702 | *Expr = Arg1.Expr; |
703 | |
704 | /* Bail out, no need for further processing */ |
705 | goto ExitPoint; |
706 | } |
707 | |
708 | if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 && |
709 | ED_IsConstAbsInt (&Arg2.Expr) && |
710 | (Arg2.Expr.IVal != 0 || IS_Get (&CodeSizeFactor) > 200)) { |
711 | |
712 | /* Remove all of the generated code but the load of the first |
713 | ** argument. |
714 | */ |
715 | RemoveCode (&Arg1.Push); |
716 | |
717 | /* We need a label */ |
718 | Label = GetLocalLabel (); |
719 | |
720 | /* Generate code */ |
721 | AddCodeLine ("sta ptr1" ); |
722 | AddCodeLine ("stx ptr1+1" ); |
723 | if (Arg3.Expr.IVal <= 129) { |
724 | AddCodeLine ("ldy #$%02X" , (unsigned char) (Arg3.Expr.IVal-1)); |
725 | AddCodeLine ("lda #$%02X" , (unsigned char) Arg2.Expr.IVal); |
726 | g_defcodelabel (Label); |
727 | AddCodeLine ("sta (ptr1),y" ); |
728 | AddCodeLine ("dey" ); |
729 | AddCodeLine ("bpl %s" , LocalLabelName (Label)); |
730 | } else { |
731 | AddCodeLine ("ldy #$00" ); |
732 | AddCodeLine ("lda #$%02X" , (unsigned char) Arg2.Expr.IVal); |
733 | g_defcodelabel (Label); |
734 | AddCodeLine ("sta (ptr1),y" ); |
735 | AddCodeLine ("iny" ); |
736 | AddCmpCodeIfSizeNot256 ("cpy #$%02X" , Arg3.Expr.IVal); |
737 | AddCodeLine ("bne %s" , LocalLabelName (Label)); |
738 | } |
739 | |
740 | /* Load the function result pointer into a/x (x is still valid). This |
741 | ** code will get removed by the optimizer if it is not used later. |
742 | */ |
743 | AddCodeLine ("lda ptr1" ); |
744 | |
745 | /* The function result is an rvalue in the primary register */ |
746 | ED_MakeRValExpr (Expr); |
747 | Expr->Type = GetFuncReturn (Expr->Type); |
748 | |
749 | /* Bail out, no need for further processing */ |
750 | goto ExitPoint; |
751 | } |
752 | } |
753 | |
754 | /* The function result is an rvalue in the primary register */ |
755 | ED_MakeRValExpr (Expr); |
756 | Expr->Type = GetFuncReturn (Expr->Type); |
757 | |
758 | ExitPoint: |
759 | /* We expect the closing brace */ |
760 | ConsumeRParen (); |
761 | } |
762 | |
763 | |
764 | |
765 | /*****************************************************************************/ |
766 | /* strcmp */ |
767 | /*****************************************************************************/ |
768 | |
769 | |
770 | |
771 | static void StdFunc_strcmp (FuncDesc* F attribute ((unused)), ExprDesc* Expr) |
772 | /* Handle the strcmp function */ |
773 | { |
774 | /* Argument types: (const char*, const char*) */ |
775 | static Type Arg1Type[] = { TYPE(T_PTR), TYPE(T_CHAR|T_QUAL_CONST), TYPE(T_END) }; |
776 | static Type Arg2Type[] = { TYPE(T_PTR), TYPE(T_CHAR|T_QUAL_CONST), TYPE(T_END) }; |
777 | |
778 | ArgDesc Arg1, Arg2; |
779 | unsigned ParamSize = 0; |
780 | long ECount1; |
781 | long ECount2; |
782 | int IsArray; |
783 | int Offs; |
784 | |
785 | /* Setup the argument type string */ |
786 | Arg1Type[1].C = GetDefaultChar () | T_QUAL_CONST; |
787 | Arg2Type[1].C = GetDefaultChar () | T_QUAL_CONST; |
788 | |
789 | /* Argument #1 */ |
790 | ParseArg (&Arg1, Arg1Type); |
791 | g_push (Arg1.Flags, Arg1.Expr.IVal); |
792 | ParamSize += SizeOf (Arg1Type); |
793 | ConsumeComma (); |
794 | |
795 | /* Argument #2. */ |
796 | ParseArg (&Arg2, Arg2Type); |
797 | |
798 | /* Since strcmp is a fastcall function, we must load the |
799 | ** arg into the primary if it is not already there. This parameter is |
800 | ** also ignored for the calculation of the parameter size, since it is |
801 | ** not passed via the stack. |
802 | */ |
803 | if (Arg2.Flags & CF_CONST) { |
804 | LoadExpr (CF_NONE, &Arg2.Expr); |
805 | } |
806 | |
807 | /* Emit the actual function call. This will also cleanup the stack. */ |
808 | g_call (CF_FIXARGC, Func_strcmp, ParamSize); |
809 | |
810 | /* Get the element counts of the arguments. Then get the larger of the |
811 | ** two into ECount1. This removes FLEXIBLE and UNSPECIFIED automatically |
812 | */ |
813 | ECount1 = ArrayElementCount (&Arg1); |
814 | ECount2 = ArrayElementCount (&Arg2); |
815 | if (ECount2 > ECount1) { |
816 | ECount1 = ECount2; |
817 | } |
818 | |
819 | if (IS_Get (&InlineStdFuncs)) { |
820 | |
821 | /* If the second argument is the empty string literal, we can generate |
822 | ** more efficient code. |
823 | */ |
824 | if (ED_IsLocLiteral (&Arg2.Expr) && |
825 | IS_Get (&WritableStrings) == 0 && |
826 | GetLiteralSize (Arg2.Expr.LVal) == 1 && |
827 | GetLiteralStr (Arg2.Expr.LVal)[0] == '\0') { |
828 | |
829 | /* Drop the generated code so we have the first argument in the |
830 | ** primary |
831 | */ |
832 | RemoveCode (&Arg1.Push); |
833 | |
834 | /* We don't need the literal any longer */ |
835 | ReleaseLiteral (Arg2.Expr.LVal); |
836 | |
837 | /* We do now have Arg1 in the primary. Load the first character from |
838 | ** this string and cast to int. This is the function result. |
839 | */ |
840 | IsArray = IsTypeArray (Arg1.Type) && ED_IsRVal (&Arg1.Expr); |
841 | if (IsArray && ED_IsLocStack (&Arg1.Expr) && |
842 | (Offs = ED_GetStackOffs (&Arg1.Expr, 0) < 256)) { |
843 | /* Drop the generated code */ |
844 | RemoveCode (&Arg1.Load); |
845 | |
846 | /* Generate code */ |
847 | AddCodeLine ("ldy #$%02X" , Offs); |
848 | AddCodeLine ("ldx #$00" ); |
849 | AddCodeLine ("lda (sp),y" ); |
850 | } else if (IsArray && ED_IsLocConst (&Arg1.Expr)) { |
851 | /* Drop the generated code */ |
852 | RemoveCode (&Arg1.Load); |
853 | |
854 | /* Generate code */ |
855 | AddCodeLine ("ldx #$00" ); |
856 | AddCodeLine ("lda %s" , ED_GetLabelName (&Arg1.Expr, 0)); |
857 | } else { |
858 | /* Drop part of the generated code so we have the first argument |
859 | ** in the primary |
860 | */ |
861 | RemoveCode (&Arg1.Push); |
862 | |
863 | /* Fetch the first char */ |
864 | g_getind (CF_CHAR | CF_UNSIGNED, 0); |
865 | } |
866 | |
867 | } else if ((IS_Get (&CodeSizeFactor) >= 165) && |
868 | ((ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr)) || |
869 | (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr))) && |
870 | ((ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) || |
871 | (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr))) && |
872 | (IS_Get (&EagerlyInlineFuncs) || (ECount1 > 0 && ECount1 < 256))) { |
873 | |
874 | unsigned Entry, Loop, Fin; /* Labels */ |
875 | const char* Load; |
876 | const char* Compare; |
877 | |
878 | if (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr)) { |
879 | Load = "lda (%s),y" ; |
880 | } else { |
881 | Load = "lda %s,y" ; |
882 | } |
883 | if (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr)) { |
884 | Compare = "cmp (%s),y" ; |
885 | } else { |
886 | Compare = "cmp %s,y" ; |
887 | } |
888 | |
889 | /* Drop the generated code */ |
890 | RemoveCode (&Arg1.Expr.Start); |
891 | |
892 | /* We need labels */ |
893 | Entry = GetLocalLabel (); |
894 | Loop = GetLocalLabel (); |
895 | Fin = GetLocalLabel (); |
896 | |
897 | /* Generate strcmp code */ |
898 | AddCodeLine ("ldy #$00" ); |
899 | AddCodeLine ("beq %s" , LocalLabelName (Entry)); |
900 | g_defcodelabel (Loop); |
901 | AddCodeLine ("tax" ); |
902 | AddCodeLine ("beq %s" , LocalLabelName (Fin)); |
903 | AddCodeLine ("iny" ); |
904 | g_defcodelabel (Entry); |
905 | AddCodeLine (Load, ED_GetLabelName (&Arg1.Expr, 0)); |
906 | AddCodeLine (Compare, ED_GetLabelName (&Arg2.Expr, 0)); |
907 | AddCodeLine ("beq %s" , LocalLabelName (Loop)); |
908 | AddCodeLine ("ldx #$01" ); |
909 | AddCodeLine ("bcs %s" , LocalLabelName (Fin)); |
910 | AddCodeLine ("ldx #$FF" ); |
911 | g_defcodelabel (Fin); |
912 | |
913 | } else if ((IS_Get (&CodeSizeFactor) > 190) && |
914 | ((ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr)) || |
915 | (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr))) && |
916 | (IS_Get (&EagerlyInlineFuncs) || (ECount1 > 0 && ECount1 < 256))) { |
917 | |
918 | unsigned Entry, Loop, Fin; /* Labels */ |
919 | const char* Compare; |
920 | |
921 | if (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr)) { |
922 | Compare = "cmp (%s),y" ; |
923 | } else { |
924 | Compare = "cmp %s,y" ; |
925 | } |
926 | |
927 | /* Drop the generated code */ |
928 | RemoveCode (&Arg1.Push); |
929 | |
930 | /* We need labels */ |
931 | Entry = GetLocalLabel (); |
932 | Loop = GetLocalLabel (); |
933 | Fin = GetLocalLabel (); |
934 | |
935 | /* Store Arg1 into ptr1 */ |
936 | AddCodeLine ("sta ptr1" ); |
937 | AddCodeLine ("stx ptr1+1" ); |
938 | |
939 | /* Generate strcmp code */ |
940 | AddCodeLine ("ldy #$00" ); |
941 | AddCodeLine ("beq %s" , LocalLabelName (Entry)); |
942 | g_defcodelabel (Loop); |
943 | AddCodeLine ("tax" ); |
944 | AddCodeLine ("beq %s" , LocalLabelName (Fin)); |
945 | AddCodeLine ("iny" ); |
946 | g_defcodelabel (Entry); |
947 | AddCodeLine ("lda (ptr1),y" ); |
948 | AddCodeLine (Compare, ED_GetLabelName (&Arg2.Expr, 0)); |
949 | AddCodeLine ("beq %s" , LocalLabelName (Loop)); |
950 | AddCodeLine ("ldx #$01" ); |
951 | AddCodeLine ("bcs %s" , LocalLabelName (Fin)); |
952 | AddCodeLine ("ldx #$FF" ); |
953 | g_defcodelabel (Fin); |
954 | } |
955 | } |
956 | |
957 | /* The function result is an rvalue in the primary register */ |
958 | ED_MakeRValExpr (Expr); |
959 | Expr->Type = GetFuncReturn (Expr->Type); |
960 | |
961 | /* We expect the closing brace */ |
962 | ConsumeRParen (); |
963 | } |
964 | |
965 | |
966 | |
967 | /*****************************************************************************/ |
968 | /* strcpy */ |
969 | /*****************************************************************************/ |
970 | |
971 | |
972 | |
973 | static void StdFunc_strcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr) |
974 | /* Handle the strcpy function */ |
975 | { |
976 | /* Argument types: (char*, const char*) */ |
977 | static Type Arg1Type[] = { TYPE(T_PTR), TYPE(T_CHAR), TYPE(T_END) }; |
978 | static Type Arg2Type[] = { TYPE(T_PTR), TYPE(T_CHAR|T_QUAL_CONST), TYPE(T_END) }; |
979 | |
980 | ArgDesc Arg1, Arg2; |
981 | unsigned ParamSize = 0; |
982 | long ECount; |
983 | unsigned L1; |
984 | |
985 | /* Setup the argument type string */ |
986 | Arg1Type[1].C = GetDefaultChar (); |
987 | Arg2Type[1].C = GetDefaultChar () | T_QUAL_CONST; |
988 | |
989 | /* Argument #1 */ |
990 | ParseArg (&Arg1, Arg1Type); |
991 | g_push (Arg1.Flags, Arg1.Expr.IVal); |
992 | GetCodePos (&Arg1.End); |
993 | ParamSize += SizeOf (Arg1Type); |
994 | ConsumeComma (); |
995 | |
996 | /* Argument #2. Since strcpy is a fastcall function, we must load the |
997 | ** arg into the primary if it is not already there. This parameter is |
998 | ** also ignored for the calculation of the parameter size, since it is |
999 | ** not passed via the stack. |
1000 | */ |
1001 | ParseArg (&Arg2, Arg2Type); |
1002 | if (Arg2.Flags & CF_CONST) { |
1003 | LoadExpr (CF_NONE, &Arg2.Expr); |
1004 | } |
1005 | |
1006 | /* Emit the actual function call. This will also cleanup the stack. */ |
1007 | g_call (CF_FIXARGC, Func_strcpy, ParamSize); |
1008 | |
1009 | /* Get the element count of argument 1 if it is an array */ |
1010 | ECount = ArrayElementCount (&Arg1); |
1011 | |
1012 | if (IS_Get (&InlineStdFuncs)) { |
1013 | |
1014 | /* We've generated the complete code for the function now and know the |
1015 | ** types of all parameters. Check for situations where better code can |
1016 | ** be generated. If such a situation is detected, throw away the |
1017 | ** generated, and emit better code. |
1018 | */ |
1019 | if (((ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr)) || |
1020 | (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr))) && |
1021 | ((ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) || |
1022 | (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr))) && |
1023 | (IS_Get (&EagerlyInlineFuncs) || |
1024 | (ECount != UNSPECIFIED && ECount < 256))) { |
1025 | |
1026 | const char* Load; |
1027 | const char* Store; |
1028 | if (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr)) { |
1029 | Load = "lda (%s),y" ; |
1030 | } else { |
1031 | Load = "lda %s,y" ; |
1032 | } |
1033 | if (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr)) { |
1034 | Store = "sta (%s),y" ; |
1035 | } else { |
1036 | Store = "sta %s,y" ; |
1037 | } |
1038 | |
1039 | /* Drop the generated code */ |
1040 | RemoveCode (&Arg1.Expr.Start); |
1041 | |
1042 | /* We need labels */ |
1043 | L1 = GetLocalLabel (); |
1044 | |
1045 | /* Generate strcpy code */ |
1046 | AddCodeLine ("ldy #$FF" ); |
1047 | g_defcodelabel (L1); |
1048 | AddCodeLine ("iny" ); |
1049 | AddCodeLine (Load, ED_GetLabelName (&Arg2.Expr, 0)); |
1050 | AddCodeLine (Store, ED_GetLabelName (&Arg1.Expr, 0)); |
1051 | AddCodeLine ("bne %s" , LocalLabelName (L1)); |
1052 | |
1053 | /* strcpy returns argument #1 */ |
1054 | *Expr = Arg1.Expr; |
1055 | |
1056 | /* Bail out, no need for further processing */ |
1057 | goto ExitPoint; |
1058 | } |
1059 | |
1060 | if (ED_IsRVal (&Arg2.Expr) && ED_IsLocStack (&Arg2.Expr) && |
1061 | StackPtr >= -255 && |
1062 | ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) { |
1063 | |
1064 | /* It is possible to just use one index register even if the stack |
1065 | ** offset is not zero, by adjusting the offset to the constant |
1066 | ** address accordingly. But we cannot do this if the data in |
1067 | ** question is in the register space or at an absolute address less |
1068 | ** than 256. Register space is zero page, which means that the |
1069 | ** address calculation could overflow in the linker. |
1070 | */ |
1071 | int AllowOneIndex = !ED_IsLocRegister (&Arg1.Expr) && |
1072 | !(ED_IsLocAbs (&Arg1.Expr) && Arg1.Expr.IVal < 256); |
1073 | |
1074 | /* Calculate the real stack offset */ |
1075 | int Offs = ED_GetStackOffs (&Arg2.Expr, 0); |
1076 | |
1077 | /* Drop the generated code */ |
1078 | RemoveCode (&Arg1.Expr.Start); |
1079 | |
1080 | /* We need labels */ |
1081 | L1 = GetLocalLabel (); |
1082 | |
1083 | /* Generate strcpy code */ |
1084 | AddCodeLine ("ldy #$%02X" , (unsigned char) (Offs - 1)); |
1085 | if (Offs == 0 || AllowOneIndex) { |
1086 | g_defcodelabel (L1); |
1087 | AddCodeLine ("iny" ); |
1088 | AddCodeLine ("lda (sp),y" ); |
1089 | AddCodeLine ("sta %s,y" , ED_GetLabelName (&Arg1.Expr, -Offs)); |
1090 | } else { |
1091 | AddCodeLine ("ldx #$FF" ); |
1092 | g_defcodelabel (L1); |
1093 | AddCodeLine ("iny" ); |
1094 | AddCodeLine ("inx" ); |
1095 | AddCodeLine ("lda (sp),y" ); |
1096 | AddCodeLine ("sta %s,x" , ED_GetLabelName (&Arg1.Expr, 0)); |
1097 | } |
1098 | AddCodeLine ("bne %s" , LocalLabelName (L1)); |
1099 | |
1100 | /* strcpy returns argument #1 */ |
1101 | *Expr = Arg1.Expr; |
1102 | |
1103 | /* Bail out, no need for further processing */ |
1104 | goto ExitPoint; |
1105 | } |
1106 | |
1107 | if (ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr) && |
1108 | ED_IsRVal (&Arg1.Expr) && ED_IsLocStack (&Arg1.Expr) && |
1109 | StackPtr >= -255) { |
1110 | |
1111 | /* It is possible to just use one index register even if the stack |
1112 | ** offset is not zero, by adjusting the offset to the constant |
1113 | ** address accordingly. But we cannot do this if the data in |
1114 | ** question is in the register space or at an absolute address less |
1115 | ** than 256. Register space is zero page, which means that the |
1116 | ** address calculation could overflow in the linker. |
1117 | */ |
1118 | int AllowOneIndex = !ED_IsLocRegister (&Arg2.Expr) && |
1119 | !(ED_IsLocAbs (&Arg2.Expr) && Arg2.Expr.IVal < 256); |
1120 | |
1121 | /* Calculate the real stack offset */ |
1122 | int Offs = ED_GetStackOffs (&Arg1.Expr, 0); |
1123 | |
1124 | /* Drop the generated code */ |
1125 | RemoveCode (&Arg1.Expr.Start); |
1126 | |
1127 | /* We need labels */ |
1128 | L1 = GetLocalLabel (); |
1129 | |
1130 | /* Generate strcpy code */ |
1131 | AddCodeLine ("ldy #$%02X" , (unsigned char) (Offs - 1)); |
1132 | if (Offs == 0 || AllowOneIndex) { |
1133 | g_defcodelabel (L1); |
1134 | AddCodeLine ("iny" ); |
1135 | AddCodeLine ("lda %s,y" , ED_GetLabelName (&Arg2.Expr, -Offs)); |
1136 | AddCodeLine ("sta (sp),y" ); |
1137 | } else { |
1138 | AddCodeLine ("ldx #$FF" ); |
1139 | g_defcodelabel (L1); |
1140 | AddCodeLine ("iny" ); |
1141 | AddCodeLine ("inx" ); |
1142 | AddCodeLine ("lda %s,x" , ED_GetLabelName (&Arg2.Expr, 0)); |
1143 | AddCodeLine ("sta (sp),y" ); |
1144 | } |
1145 | AddCodeLine ("bne %s" , LocalLabelName (L1)); |
1146 | |
1147 | /* strcpy returns argument #1 */ |
1148 | *Expr = Arg1.Expr; |
1149 | |
1150 | /* Bail out, no need for further processing */ |
1151 | goto ExitPoint; |
1152 | } |
1153 | } |
1154 | |
1155 | /* The function result is an rvalue in the primary register */ |
1156 | ED_MakeRValExpr (Expr); |
1157 | Expr->Type = GetFuncReturn (Expr->Type); |
1158 | |
1159 | ExitPoint: |
1160 | /* We expect the closing brace */ |
1161 | ConsumeRParen (); |
1162 | } |
1163 | |
1164 | |
1165 | |
1166 | /*****************************************************************************/ |
1167 | /* strlen */ |
1168 | /*****************************************************************************/ |
1169 | |
1170 | |
1171 | |
1172 | static void StdFunc_strlen (FuncDesc* F attribute ((unused)), ExprDesc* Expr) |
1173 | /* Handle the strlen function */ |
1174 | { |
1175 | static Type ArgType[] = { TYPE(T_PTR), TYPE(T_CHAR|T_QUAL_CONST), TYPE(T_END) }; |
1176 | ExprDesc Arg; |
1177 | int IsArray; |
1178 | int IsPtr; |
1179 | int IsByteIndex; |
1180 | long ECount; |
1181 | unsigned L; |
1182 | |
1183 | /* Setup the argument type string */ |
1184 | ArgType[1].C = GetDefaultChar () | T_QUAL_CONST; |
1185 | |
1186 | /* Evaluate the parameter */ |
1187 | hie1 (&Arg); |
1188 | |
1189 | /* Check if the argument is an array. If so, remember the element count. |
1190 | ** Otherwise set the element count to undefined. |
1191 | */ |
1192 | IsArray = IsTypeArray (Arg.Type); |
1193 | if (IsArray) { |
1194 | ECount = GetElementCount (Arg.Type); |
1195 | if (ECount == FLEXIBLE) { |
1196 | /* Treat as unknown */ |
1197 | ECount = UNSPECIFIED; |
1198 | } |
1199 | IsPtr = 0; |
1200 | } else { |
1201 | ECount = UNSPECIFIED; |
1202 | IsPtr = IsTypePtr (Arg.Type); |
1203 | } |
1204 | |
1205 | /* Check if the elements of an array can be addressed by a byte sized |
1206 | ** index. This is true if the size of the array is known and less than |
1207 | ** 256. |
1208 | */ |
1209 | IsByteIndex = (ECount != UNSPECIFIED && ECount < 256); |
1210 | |
1211 | /* Do type conversion */ |
1212 | TypeConversion (&Arg, ArgType); |
1213 | |
1214 | if (IS_Get (&Optimize)) { |
1215 | |
1216 | /* If the expression is a literal, and if string literals are read |
1217 | ** only, we can calculate the length of the string and remove it |
1218 | ** from the literal pool. Otherwise we have to calculate the length |
1219 | ** at runtime. |
1220 | */ |
1221 | if (ED_IsLocLiteral (&Arg) && IS_Get (&WritableStrings) == 0) { |
1222 | |
1223 | /* Constant string literal */ |
1224 | ED_MakeConstAbs (Expr, GetLiteralSize (Arg.LVal) - 1, type_size_t); |
1225 | |
1226 | /* We don't need the literal any longer */ |
1227 | ReleaseLiteral (Arg.LVal); |
1228 | |
1229 | /* Bail out, no need for further improvements */ |
1230 | goto ExitPoint; |
1231 | } |
1232 | } |
1233 | |
1234 | if (IS_Get (&InlineStdFuncs)) { |
1235 | |
1236 | /* We will inline strlen for arrays with constant addresses, if either |
1237 | ** requested on the command line, or the array is smaller than 256, |
1238 | ** so the inlining is considered safe. |
1239 | */ |
1240 | if (ED_IsLocConst (&Arg) && IsArray && |
1241 | (IS_Get (&EagerlyInlineFuncs) || IsByteIndex)) { |
1242 | |
1243 | /* Generate the strlen code */ |
1244 | L = GetLocalLabel (); |
1245 | AddCodeLine ("ldy #$FF" ); |
1246 | g_defcodelabel (L); |
1247 | AddCodeLine ("iny" ); |
1248 | AddCodeLine ("ldx %s,y" , ED_GetLabelName (&Arg, 0)); |
1249 | AddCodeLine ("bne %s" , LocalLabelName (L)); |
1250 | AddCodeLine ("tya" ); |
1251 | |
1252 | /* The function result is an rvalue in the primary register */ |
1253 | ED_MakeRValExpr (Expr); |
1254 | Expr->Type = type_size_t; |
1255 | |
1256 | /* Bail out, no need for further processing */ |
1257 | goto ExitPoint; |
1258 | } |
1259 | |
1260 | /* We will inline strlen for arrays on the stack, if the array is |
1261 | ** completely within the reach of a byte sized index register. |
1262 | */ |
1263 | if (ED_IsLocStack (&Arg) && IsArray && IsByteIndex && |
1264 | (Arg.IVal - StackPtr) + ECount < 256) { |
1265 | |
1266 | /* Calculate the true stack offset */ |
1267 | int Offs = ED_GetStackOffs (&Arg, 0); |
1268 | |
1269 | /* Generate the strlen code */ |
1270 | L = GetLocalLabel (); |
1271 | AddCodeLine ("ldx #$FF" ); |
1272 | AddCodeLine ("ldy #$%02X" , (unsigned char) (Offs-1)); |
1273 | g_defcodelabel (L); |
1274 | AddCodeLine ("inx" ); |
1275 | AddCodeLine ("iny" ); |
1276 | AddCodeLine ("lda (sp),y" ); |
1277 | AddCodeLine ("bne %s" , LocalLabelName (L)); |
1278 | AddCodeLine ("txa" ); |
1279 | AddCodeLine ("ldx #$00" ); |
1280 | |
1281 | /* The function result is an rvalue in the primary register */ |
1282 | ED_MakeRValExpr (Expr); |
1283 | Expr->Type = type_size_t; |
1284 | |
1285 | /* Bail out, no need for further processing */ |
1286 | goto ExitPoint; |
1287 | } |
1288 | |
1289 | /* strlen for a string that is pointed to by a register variable will only |
1290 | ** get inlined if requested on the command line, since we cannot know how |
1291 | ** big the buffer actually is, so inlining is not always safe. |
1292 | */ |
1293 | if (ED_IsLocRegister (&Arg) && ED_IsLVal (&Arg) && IsPtr && |
1294 | IS_Get (&EagerlyInlineFuncs)) { |
1295 | |
1296 | /* Generate the strlen code */ |
1297 | L = GetLocalLabel (); |
1298 | AddCodeLine ("ldy #$FF" ); |
1299 | g_defcodelabel (L); |
1300 | AddCodeLine ("iny" ); |
1301 | AddCodeLine ("lda (%s),y" , ED_GetLabelName (&Arg, 0)); |
1302 | AddCodeLine ("bne %s" , LocalLabelName (L)); |
1303 | AddCodeLine ("tax" ); |
1304 | AddCodeLine ("tya" ); |
1305 | |
1306 | /* The function result is an rvalue in the primary register */ |
1307 | ED_MakeRValExpr (Expr); |
1308 | Expr->Type = type_size_t; |
1309 | |
1310 | /* Bail out, no need for further processing */ |
1311 | goto ExitPoint; |
1312 | } |
1313 | |
1314 | /* Last check: We will inline a generic strlen routine if inlining was |
1315 | ** requested on the command line, and the code size factor is more than |
1316 | ** 400 (code is 13 bytes vs. 3 for a jsr call). |
1317 | */ |
1318 | if (IS_Get (&CodeSizeFactor) > 400 && IS_Get (&EagerlyInlineFuncs)) { |
1319 | |
1320 | /* Load the expression into the primary */ |
1321 | LoadExpr (CF_NONE, &Arg); |
1322 | |
1323 | /* Inline the function */ |
1324 | L = GetLocalLabel (); |
1325 | AddCodeLine ("sta ptr1" ); |
1326 | AddCodeLine ("stx ptr1+1" ); |
1327 | AddCodeLine ("ldy #$FF" ); |
1328 | g_defcodelabel (L); |
1329 | AddCodeLine ("iny" ); |
1330 | AddCodeLine ("lda (ptr1),y" ); |
1331 | AddCodeLine ("bne %s" , LocalLabelName (L)); |
1332 | AddCodeLine ("tax" ); |
1333 | AddCodeLine ("tya" ); |
1334 | |
1335 | /* The function result is an rvalue in the primary register */ |
1336 | ED_MakeRValExpr (Expr); |
1337 | Expr->Type = type_size_t; |
1338 | |
1339 | /* Bail out, no need for further processing */ |
1340 | goto ExitPoint; |
1341 | } |
1342 | } |
1343 | |
1344 | /* Load the expression into the primary */ |
1345 | LoadExpr (CF_NONE, &Arg); |
1346 | |
1347 | /* Call the strlen function */ |
1348 | AddCodeLine ("jsr _%s" , Func_strlen); |
1349 | |
1350 | /* The function result is an rvalue in the primary register */ |
1351 | ED_MakeRValExpr (Expr); |
1352 | Expr->Type = type_size_t; |
1353 | |
1354 | ExitPoint: |
1355 | /* We expect the closing brace */ |
1356 | ConsumeRParen (); |
1357 | } |
1358 | |
1359 | |
1360 | |
1361 | /*****************************************************************************/ |
1362 | /* Code */ |
1363 | /*****************************************************************************/ |
1364 | |
1365 | |
1366 | |
1367 | int FindStdFunc (const char* Name) |
1368 | /* Determine if the given function is a known standard function that may be |
1369 | ** called in a special way. If so, return the index, otherwise return -1. |
1370 | */ |
1371 | { |
1372 | /* Look into the table for known names */ |
1373 | struct StdFuncDesc* D = |
1374 | bsearch (Name, StdFuncs, FUNC_COUNT, sizeof (StdFuncs[0]), CmpFunc); |
1375 | |
1376 | /* Return the function index or -1 */ |
1377 | if (D == 0) { |
1378 | return -1; |
1379 | } else { |
1380 | return D - StdFuncs; |
1381 | } |
1382 | } |
1383 | |
1384 | |
1385 | |
1386 | void HandleStdFunc (int Index, FuncDesc* F, ExprDesc* lval) |
1387 | /* Generate code for a known standard function. */ |
1388 | { |
1389 | struct StdFuncDesc* D; |
1390 | |
1391 | /* Get a pointer to the table entry */ |
1392 | CHECK (Index >= 0 && Index < (int)FUNC_COUNT); |
1393 | D = StdFuncs + Index; |
1394 | |
1395 | /* Call the handler function */ |
1396 | D->Handler (F, lval); |
1397 | } |
1398 | |