]> git.sur5r.net Git - cc65/blob - src/cc65/stdfunc.c
Reenable compile time evaluation of strlen for string literals.
[cc65] / src / cc65 / stdfunc.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                 stdfunc.c                                 */
4 /*                                                                           */
5 /*         Handle inlining of known functions for the cc65 compiler          */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 1998-2006 Ullrich von Bassewitz                                       */
10 /*               Römerstrasse 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_strcpy (FuncDesc*, ExprDesc*);
69 static void StdFunc_strlen (FuncDesc*, ExprDesc*);
70
71
72
73 /*****************************************************************************/
74 /*                                   Data                                    */
75 /*****************************************************************************/
76
77
78
79 /* Table with all known functions and their handlers. Must be sorted
80  * alphabetically!
81  */
82 static struct StdFuncDesc {
83     const char*         Name;
84     void                (*Handler) (FuncDesc*, ExprDesc*);
85 } StdFuncs[] = {
86     {   "memcpy",       StdFunc_memcpy          },
87     {   "memset",       StdFunc_memset          },
88     {   "strcpy",       StdFunc_strcpy          },
89     {   "strlen",       StdFunc_strlen          },
90
91 };
92 #define FUNC_COUNT      (sizeof (StdFuncs) / sizeof (StdFuncs[0]))
93
94 typedef struct ArgDesc ArgDesc;
95 struct ArgDesc {
96     const Type* ArgType;        /* Required argument type */
97     ExprDesc    Expr;           /* Argument expression */
98     const Type* Type;           /* The original type before conversion */
99     CodeMark    Start;          /* Start of the code for calculation */
100     CodeMark    Push;           /* Start of argument push code */
101     CodeMark    End;            /* End of the code for calculation+push */
102     unsigned    Flags;          /* Code generation flags */
103 };
104
105
106
107 /*****************************************************************************/
108 /*                             Helper functions                              */
109 /*****************************************************************************/
110
111
112
113 static int CmpFunc (const void* Key, const void* Elem)
114 /* Compare function for bsearch */
115 {
116     return strcmp ((const char*) Key, ((const struct StdFuncDesc*) Elem)->Name);
117 }
118
119
120
121 static long ArrayElementCount (const ArgDesc* Arg)
122 /* Check if the type of the given argument is an array. If so, and if the
123  * element count is known, return it. In all other cases, return UNSPECIFIED.
124  */
125 {
126     long Count;
127
128     if (IsTypeArray (Arg->Type)) {
129         Count = GetElementCount (Arg->Type);
130         if (Count == FLEXIBLE) {
131             /* Treat as unknown */
132             Count = UNSPECIFIED;
133         }
134     } else {
135         Count = UNSPECIFIED;
136     }
137     return Count;
138 }
139
140
141
142 static void ParseArg (ArgDesc* Arg, Type* Type)
143 /* Parse one argument but do not push it onto the stack. Make all fields in
144  * Arg valid.
145  */
146 {
147     /* We have a prototype, so chars may be pushed as chars */
148     Arg->Flags = CF_FORCECHAR;
149
150     /* Remember the required argument type */
151     Arg->ArgType = Type;
152
153     /* Remember the current code position */
154     GetCodePos (&Arg->Start);
155
156     /* Read the expression we're going to pass to the function */
157     MarkedExprWithCheck (hie1, &Arg->Expr);
158
159     /* Remember the actual argument type */
160     Arg->Type = Arg->Expr.Type;
161
162     /* Convert this expression to the expected type */
163     TypeConversion (&Arg->Expr, Type);
164
165     /* If the value is a constant, set the flag, otherwise load it into the
166      * primary register.
167      */
168     if (ED_IsConstAbsInt (&Arg->Expr) && ED_CodeRangeIsEmpty (&Arg->Expr)) {
169         /* Remember that we have a constant value */
170         Arg->Flags |= CF_CONST;
171     } else {
172         /* Load into the primary */
173         LoadExpr (CF_NONE, &Arg->Expr);
174     }
175
176     /* Remember the following code position */
177     GetCodePos (&Arg->Push);
178     GetCodePos (&Arg->End);
179
180     /* Use the type of the argument for the push */
181     Arg->Flags |= TypeOf (Arg->Expr.Type);
182 }
183
184
185
186 /*****************************************************************************/
187 /*                                  memcpy                                   */
188 /*****************************************************************************/
189
190
191
192 static void StdFunc_memcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
193 /* Handle the memcpy function */
194 {
195     /* Argument types: (void*, const void*, size_t) */
196     static Type Arg1Type[] = { TYPE(T_PTR), TYPE(T_VOID), TYPE(T_END) };
197     static Type Arg2Type[] = { TYPE(T_PTR), TYPE(T_VOID|T_QUAL_CONST), TYPE(T_END) };
198     static Type Arg3Type[] = { TYPE(T_SIZE_T), TYPE(T_END) };
199
200     CodeMark Start;
201     ArgDesc  Arg1, Arg2, Arg3;
202     unsigned ParamSize = 0;
203     unsigned Label;
204
205     /* Remember where we are now */
206     GetCodePos (&Start);
207
208     /* Argument #1 */
209     ParseArg (&Arg1, Arg1Type);
210     g_push (Arg1.Flags, Arg1.Expr.IVal);
211     GetCodePos (&Arg1.End);
212     ParamSize += SizeOf (Arg1Type);
213     ConsumeComma ();
214
215     /* Argument #2 */
216     ParseArg (&Arg2, Arg2Type);
217     g_push (Arg2.Flags, Arg2.Expr.IVal);
218     GetCodePos (&Arg2.End);
219     ParamSize += SizeOf (Arg2Type);
220     ConsumeComma ();
221
222     /* Argument #3. Since memcpy is a fastcall function, we must load the
223      * arg into the primary if it is not already there. This parameter is
224      * also ignored for the calculation of the parameter size, since it is
225      * not passed via the stack.
226      */
227     ParseArg (&Arg3, Arg3Type);
228     if (Arg3.Flags & CF_CONST) {
229         LoadExpr (CF_NONE, &Arg3.Expr);
230     }
231
232     /* Emit the actual function call. This will also cleanup the stack. */
233     g_call (CF_FIXARGC, Func_memcpy, ParamSize);
234
235     if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal == 0) {
236
237         /* memcpy has been called with a count argument of zero */
238         Warning ("Call to memcpy has no effect");
239
240         /* Remove all of the generated code but the load of the first
241          * argument, which is what memcpy returns.
242          */
243         RemoveCode (&Arg1.Push);
244
245         /* Set the function result to the first argument */
246         *Expr = Arg1.Expr;
247
248         /* Bail out, no need for further improvements */
249         goto ExitPoint;
250     }
251
252     /* We've generated the complete code for the function now and know the
253      * types of all parameters. Check for situations where better code can
254      * be generated. If such a situation is detected, throw away the
255      * generated, and emit better code.
256      */
257     if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
258         ((ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr)) ||
259          (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr))) &&
260         ((ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) ||
261          (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr)))) {
262
263         int Reg1 = ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr);
264         int Reg2 = ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr);
265
266         /* Drop the generated code */
267         RemoveCode (&Start);
268
269         /* We need a label */
270         Label = GetLocalLabel ();
271
272         /* Generate memcpy code */
273         if (Arg3.Expr.IVal <= 127) {
274
275             AddCodeLine ("ldy #$%02X", (unsigned char) (Arg3.Expr.IVal-1));
276             AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
277             g_defcodelabel (Label);
278             if (Reg2) {
279                 AddCodeLine ("lda (%s),y", ED_GetLabelName (&Arg2.Expr, 0));
280             } else {
281                 AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg2.Expr, 0));
282             }
283             if (Reg1) {
284                 AddCodeLine ("sta (%s),y", ED_GetLabelName (&Arg1.Expr, 0));
285             } else {
286                 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, 0));
287             }
288             AddCodeLine ("dey");
289             AddCodeLine ("bpl %s", LocalLabelName (Label));
290
291         } else {
292
293             AddCodeLine ("ldy #$00");
294             AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
295             g_defcodelabel (Label);
296             if (Reg2) {
297                 AddCodeLine ("lda (%s),y", ED_GetLabelName (&Arg2.Expr, 0));
298             } else {
299                 AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg2.Expr, 0));
300             }
301             if (Reg1) {
302                 AddCodeLine ("sta (%s),y", ED_GetLabelName (&Arg1.Expr, 0));
303             } else {
304                 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, 0));
305             }
306             AddCodeLine ("iny");
307             AddCodeLine ("cpy #$%02X", (unsigned char) Arg3.Expr.IVal);
308             AddCodeLine ("bne %s", LocalLabelName (Label));
309
310         }
311
312         /* memcpy returns the address, so the result is actually identical
313          * to the first argument.
314          */
315         *Expr = Arg1.Expr;
316
317     } else if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
318                ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr) &&
319                ED_IsRVal (&Arg1.Expr) && ED_IsLocStack (&Arg1.Expr) &&
320                (Arg1.Expr.IVal - StackPtr) + Arg3.Expr.IVal < 256) {
321
322         /* It is possible to just use one index register even if the stack
323          * offset is not zero, by adjusting the offset to the constant
324          * address accordingly. But we cannot do this if the data in
325          * question is in the register space or at an absolute address less
326          * than 256. Register space is zero page, which means that the
327          * address calculation could overflow in the linker.
328          */
329         int AllowOneIndex = !ED_IsLocRegister (&Arg2.Expr) &&
330                             !(ED_IsLocAbs (&Arg2.Expr) && Arg2.Expr.IVal < 256);
331
332         /* Calculate the real stack offset */
333         int Offs = ED_GetStackOffs (&Arg1.Expr, 0);
334
335         /* Drop the generated code */
336         RemoveCode (&Start);
337
338         /* We need a label */
339         Label = GetLocalLabel ();
340
341         /* Generate memcpy code */
342         if (Arg3.Expr.IVal <= 127 && !AllowOneIndex) {
343
344             if (Offs == 0) {
345                 AddCodeLine ("ldy #$%02X", (unsigned char) (Offs + Arg3.Expr.IVal - 1));
346                 g_defcodelabel (Label);
347                 AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg2.Expr, -Offs));
348                 AddCodeLine ("sta (sp),y");
349                 AddCodeLine ("dey");
350                 AddCodeLine ("bpl %s", LocalLabelName (Label));
351             } else {
352                 AddCodeLine ("ldx #$%02X", (unsigned char) (Arg3.Expr.IVal-1));
353                 AddCodeLine ("ldy #$%02X", (unsigned char) (Offs + Arg3.Expr.IVal - 1));
354                 g_defcodelabel (Label);
355                 AddCodeLine ("lda %s,x", ED_GetLabelName (&Arg2.Expr, 0));
356                 AddCodeLine ("sta (sp),y");
357                 AddCodeLine ("dey");
358                 AddCodeLine ("dex");
359                 AddCodeLine ("bpl %s", LocalLabelName (Label));
360             }
361
362         } else {
363
364             if (Offs == 0 || AllowOneIndex) {
365                 AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
366                 g_defcodelabel (Label);
367                 AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg2.Expr, -Offs));
368                 AddCodeLine ("sta (sp),y");
369                 AddCodeLine ("iny");
370                 AddCodeLine ("cpy #$%02X", (unsigned char) (Offs + Arg3.Expr.IVal));
371                 AddCodeLine ("bne %s", LocalLabelName (Label));
372             } else {
373                 AddCodeLine ("ldx #$00");
374                 AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
375                 g_defcodelabel (Label);
376                 AddCodeLine ("lda %s,x", ED_GetLabelName (&Arg2.Expr, 0));
377                 AddCodeLine ("sta (sp),y");
378                 AddCodeLine ("iny");
379                 AddCodeLine ("inx");
380                 AddCodeLine ("cpx #$%02X", (unsigned char) Arg3.Expr.IVal);
381                 AddCodeLine ("bne %s", LocalLabelName (Label));
382             }
383
384         }
385
386         /* memcpy returns the address, so the result is actually identical
387          * to the first argument.
388          */
389         *Expr = Arg1.Expr;
390
391     } else if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
392                ED_IsRVal (&Arg2.Expr) && ED_IsLocStack (&Arg2.Expr) &&
393                (Arg2.Expr.IVal - StackPtr) + Arg3.Expr.IVal < 256 &&
394                ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) {
395
396         /* It is possible to just use one index register even if the stack
397          * offset is not zero, by adjusting the offset to the constant
398          * address accordingly. But we cannot do this if the data in
399          * question is in the register space or at an absolute address less
400          * than 256. Register space is zero page, which means that the
401          * address calculation could overflow in the linker.
402          */
403         int AllowOneIndex = !ED_IsLocRegister (&Arg1.Expr) &&
404                             !(ED_IsLocAbs (&Arg1.Expr) && Arg1.Expr.IVal < 256);
405
406         /* Calculate the real stack offset */
407         int Offs = ED_GetStackOffs (&Arg2.Expr, 0);
408
409         /* Drop the generated code */
410         RemoveCode (&Start);
411
412         /* We need a label */
413         Label = GetLocalLabel ();
414
415         /* Generate memcpy code */
416         if (Arg3.Expr.IVal <= 127 && !AllowOneIndex) {
417
418             if (Offs == 0) {
419                 AddCodeLine ("ldy #$%02X", (unsigned char) (Arg3.Expr.IVal - 1));
420                 g_defcodelabel (Label);
421                 AddCodeLine ("lda (sp),y");
422                 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, 0));
423                 AddCodeLine ("dey");
424                 AddCodeLine ("bpl %s", LocalLabelName (Label));
425             } else {
426                 AddCodeLine ("ldx #$%02X", (unsigned char) (Arg3.Expr.IVal-1));
427                 AddCodeLine ("ldy #$%02X", (unsigned char) (Offs + Arg3.Expr.IVal - 1));
428                 g_defcodelabel (Label);
429                 AddCodeLine ("lda (sp),y");
430                 AddCodeLine ("sta %s,x", ED_GetLabelName (&Arg1.Expr, 0));
431                 AddCodeLine ("dey");
432                 AddCodeLine ("dex");
433                 AddCodeLine ("bpl %s", LocalLabelName (Label));
434             }
435
436         } else {
437
438             if (Offs == 0 || AllowOneIndex) {
439                 AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
440                 g_defcodelabel (Label);
441                 AddCodeLine ("lda (sp),y");
442                 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, -Offs));
443                 AddCodeLine ("iny");
444                 AddCodeLine ("cpy #$%02X", (unsigned char) (Offs + Arg3.Expr.IVal));
445                 AddCodeLine ("bne %s", LocalLabelName (Label));
446             } else {
447                 AddCodeLine ("ldx #$00");
448                 AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
449                 g_defcodelabel (Label);
450                 AddCodeLine ("lda (sp),y");
451                 AddCodeLine ("sta %s,x", ED_GetLabelName (&Arg1.Expr, 0));
452                 AddCodeLine ("iny");
453                 AddCodeLine ("inx");
454                 AddCodeLine ("cpx #$%02X", (unsigned char) Arg3.Expr.IVal);
455                 AddCodeLine ("bne %s", LocalLabelName (Label));
456             }
457
458         }
459
460         /* memcpy returns the address, so the result is actually identical
461          * to the first argument.
462          */
463         *Expr = Arg1.Expr;
464
465     } else {
466
467         /* The function result is an rvalue in the primary register */
468         ED_MakeRValExpr (Expr);
469         Expr->Type = GetFuncReturn (Expr->Type);
470
471     }
472
473 ExitPoint:
474     /* We expect the closing brace */
475     ConsumeRParen ();
476 }
477
478
479
480 /*****************************************************************************/
481 /*                                  memset                                   */
482 /*****************************************************************************/
483
484
485
486 static void StdFunc_memset (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
487 /* Handle the memset function */
488 {
489     /* Argument types: (void*, int, size_t) */
490     static Type Arg1Type[] = { TYPE(T_PTR), TYPE(T_VOID), TYPE(T_END) };
491     static Type Arg2Type[] = { TYPE(T_INT), TYPE(T_END) };
492     static Type Arg3Type[] = { TYPE(T_SIZE_T), TYPE(T_END) };
493
494     CodeMark Start;
495     ArgDesc  Arg1, Arg2, Arg3;
496     int      MemSet    = 1;             /* Use real memset if true */
497     unsigned ParamSize = 0;
498     unsigned Label;
499
500     /* Remember where we are now */
501     GetCodePos (&Start);
502
503     /* Argument #1 */
504     ParseArg (&Arg1, Arg1Type);
505     g_push (Arg1.Flags, Arg1.Expr.IVal);
506     GetCodePos (&Arg1.End);
507     ParamSize += SizeOf (Arg1Type);
508     ConsumeComma ();
509
510     /* Argument #2. This argument is special in that we will call another
511      * function if it is a constant zero.
512      */
513     ParseArg (&Arg2, Arg2Type);
514     if ((Arg2.Flags & CF_CONST) != 0 && Arg2.Expr.IVal == 0) {
515         /* Don't call memset, call bzero instead */
516         MemSet = 0;
517     } else {
518         /* Push the argument */
519         g_push (Arg2.Flags, Arg2.Expr.IVal);
520         GetCodePos (&Arg2.End);
521         ParamSize += SizeOf (Arg2Type);
522     }
523     ConsumeComma ();
524
525     /* Argument #3. Since memset is a fastcall function, we must load the
526      * arg into the primary if it is not already there. This parameter is
527      * also ignored for the calculation of the parameter size, since it is
528      * not passed via the stack.
529      */
530     ParseArg (&Arg3, Arg3Type);
531     if (Arg3.Flags & CF_CONST) {
532         LoadExpr (CF_NONE, &Arg3.Expr);
533     }
534
535     /* Emit the actual function call. This will also cleanup the stack. */
536     g_call (CF_FIXARGC, MemSet? Func_memset : Func__bzero, ParamSize);
537
538     if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal == 0) {
539
540         /* memset has been called with a count argument of zero */
541         Warning ("Call to memset has no effect");
542
543         /* Remove all of the generated code but the load of the first
544          * argument, which is what memset returns.
545          */
546         RemoveCode (&Arg1.Push);
547
548         /* Set the function result to the first argument */
549         *Expr = Arg1.Expr;
550
551         /* Bail out, no need for further improvements */
552         goto ExitPoint;
553     }
554
555     /* We've generated the complete code for the function now and know the
556      * types of all parameters. Check for situations where better code can
557      * be generated. If such a situation is detected, throw away the
558      * generated, and emit better code.
559      * Note: Lots of improvements would be possible here, but I will
560      * concentrate on the most common case: memset with arguments 2 and 3
561      * being constant numerical values. Some checks have shown that this
562      * covers nearly 90% of all memset calls.
563      */
564     if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
565         ED_IsConstAbsInt (&Arg2.Expr) &&
566         ((ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) ||
567          (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr)))) {
568
569         int Reg = ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr);
570
571         /* Drop the generated code */
572         RemoveCode (&Start);
573
574         /* We need a label */
575         Label = GetLocalLabel ();
576
577         /* Generate memset code */
578         if (Arg3.Expr.IVal <= 127) {
579
580             AddCodeLine ("ldy #$%02X", (unsigned char) (Arg3.Expr.IVal-1));
581             AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
582             g_defcodelabel (Label);
583             if (Reg) {
584                 AddCodeLine ("sta (%s),y", ED_GetLabelName (&Arg1.Expr, 0));
585             } else {
586                 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, 0));
587             }
588             AddCodeLine ("dey");
589             AddCodeLine ("bpl %s", LocalLabelName (Label));
590
591         } else {
592
593             AddCodeLine ("ldy #$00");
594             AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
595             g_defcodelabel (Label);
596             if (Reg) {
597                 AddCodeLine ("sta (%s),y", ED_GetLabelName (&Arg1.Expr, 0));
598             } else {
599                 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, 0));
600             }
601             AddCodeLine ("iny");
602             AddCodeLine ("cpy #$%02X", (unsigned char) Arg3.Expr.IVal);
603             AddCodeLine ("bne %s", LocalLabelName (Label));
604
605         }
606
607         /* memset returns the address, so the result is actually identical
608          * to the first argument.
609          */
610         *Expr = Arg1.Expr;
611
612     } else if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
613                ED_IsConstAbsInt (&Arg2.Expr) &&
614                ED_IsRVal (&Arg1.Expr) && ED_IsLocStack (&Arg1.Expr) &&
615                (Arg1.Expr.IVal - StackPtr) + Arg3.Expr.IVal < 256) {
616
617         /* Calculate the real stack offset */
618         int Offs = ED_GetStackOffs (&Arg1.Expr, 0);
619
620         /* Drop the generated code */
621         RemoveCode (&Start);
622
623         /* We need a label */
624         Label = GetLocalLabel ();
625
626         /* Generate memset code */
627         AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
628         AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
629         g_defcodelabel (Label);
630         AddCodeLine ("sta (sp),y");
631         AddCodeLine ("iny");
632         AddCodeLine ("cpy #$%02X", (unsigned char) (Offs + Arg3.Expr.IVal));
633         AddCodeLine ("bne %s", LocalLabelName (Label));
634
635         /* memset returns the address, so the result is actually identical
636          * to the first argument.
637          */
638         *Expr = Arg1.Expr;
639
640     } else if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
641                ED_IsConstAbsInt (&Arg2.Expr) &&
642                (Arg2.Expr.IVal != 0 || IS_Get (&CodeSizeFactor) > 200)) {
643
644         /* Remove all of the generated code but the load of the first
645          * argument.
646          */
647         RemoveCode (&Arg1.Push);
648
649         /* We need a label */
650         Label = GetLocalLabel ();
651
652         /* Generate code */
653         AddCodeLine ("sta ptr1");
654         AddCodeLine ("stx ptr1+1");
655         if (Arg3.Expr.IVal <= 127) {
656             AddCodeLine ("ldy #$%02X", (unsigned char) (Arg3.Expr.IVal-1));
657             AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
658             g_defcodelabel (Label);
659             AddCodeLine ("sta (ptr1),y");
660             AddCodeLine ("dey");
661             AddCodeLine ("bpl %s", LocalLabelName (Label));
662         } else {
663             AddCodeLine ("ldy #$00");
664             AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
665             g_defcodelabel (Label);
666             AddCodeLine ("sta (ptr1),y");
667             AddCodeLine ("iny");
668             AddCodeLine ("cpy #$%02X", (unsigned char) Arg3.Expr.IVal);
669             AddCodeLine ("bne %s", LocalLabelName (Label));
670         }
671
672         /* Load the function result pointer into a/x (x is still valid). This
673          * code will get removed by the optimizer if it is not used later.
674          */
675         AddCodeLine ("lda ptr1");
676
677         /* The function result is an rvalue in the primary register */
678         ED_MakeRValExpr (Expr);
679         Expr->Type = GetFuncReturn (Expr->Type);
680
681     } else {
682
683         /* The function result is an rvalue in the primary register */
684         ED_MakeRValExpr (Expr);
685         Expr->Type = GetFuncReturn (Expr->Type);
686
687     }
688
689 ExitPoint:
690     /* We expect the closing brace */
691     ConsumeRParen ();
692 }
693
694
695
696 /*****************************************************************************/
697 /*                                  strcpy                                   */
698 /*****************************************************************************/
699
700
701
702 static void StdFunc_strcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
703 /* Handle the strcpy function */
704 {
705     /* Argument types: (char*, const char*) */
706     static Type Arg1Type[] = { TYPE(T_PTR), TYPE(T_CHAR), TYPE(T_END) };
707     static Type Arg2Type[] = { TYPE(T_PTR), TYPE(T_CHAR|T_QUAL_CONST), TYPE(T_END) };
708
709     CodeMark Start;
710     ArgDesc  Arg1, Arg2;
711     unsigned ParamSize = 0;
712     long     ECount;
713     unsigned L1;
714
715     /* Setup the argument type string */
716     Arg1Type[1].C = GetDefaultChar ();
717     Arg2Type[1].C = GetDefaultChar () | T_QUAL_CONST;
718
719     /* Remember where we are now */
720     GetCodePos (&Start);
721
722     /* Argument #1 */
723     ParseArg (&Arg1, Arg1Type);
724     g_push (Arg1.Flags, Arg1.Expr.IVal);
725     GetCodePos (&Arg1.End);
726     ParamSize += SizeOf (Arg1Type);
727     ConsumeComma ();
728
729     /* Argument #2. Since strcpy is a fastcall function, we must load the
730      * arg into the primary if it is not already there. This parameter is
731      * also ignored for the calculation of the parameter size, since it is
732      * not passed via the stack.
733      */
734     ParseArg (&Arg2, Arg2Type);
735     if (Arg2.Flags & CF_CONST) {
736         LoadExpr (CF_NONE, &Arg2.Expr);
737     }
738
739     /* Emit the actual function call. This will also cleanup the stack. */
740     g_call (CF_FIXARGC, Func_strcpy, ParamSize);
741
742     /* Get the element count of argument 2 if it is an array */
743     ECount = ArrayElementCount (&Arg1);
744
745     /* We've generated the complete code for the function now and know the
746      * types of all parameters. Check for situations where better code can
747      * be generated. If such a situation is detected, throw away the
748      * generated, and emit better code.
749      */
750     if (((ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr)) ||
751          (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr))) &&
752         ((ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) ||
753          (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr))) &&
754         (IS_Get (&InlineStdFuncs) ||
755         (ECount != UNSPECIFIED && ECount < 256))) {
756
757         const char* Load;
758         const char* Store;
759         if (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr)) {
760             Load = "lda (%s),y";
761         } else {
762             Load = "lda %s,y";
763         }
764         if (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr)) {
765             Store = "sta (%s),y";
766         } else {
767             Store = "sta %s,y";
768         }
769
770         /* Drop the generated code */
771         RemoveCode (&Start);
772
773         /* We need labels */
774         L1 = GetLocalLabel ();
775
776         /* Generate strcpy code */
777         AddCodeLine ("ldy #$FF");
778         g_defcodelabel (L1);
779         AddCodeLine ("iny");
780         AddCodeLine (Load, ED_GetLabelName (&Arg2.Expr, 0));
781         AddCodeLine (Store, ED_GetLabelName (&Arg1.Expr, 0));
782         AddCodeLine ("bne %s", LocalLabelName (L1));
783
784         /* strcpy returns argument #1 */
785         *Expr = Arg1.Expr;
786
787     } else if (ED_IsRVal (&Arg2.Expr) && ED_IsLocStack (&Arg2.Expr) &&
788                StackPtr >= -255 &&
789                ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) {
790
791         /* It is possible to just use one index register even if the stack
792          * offset is not zero, by adjusting the offset to the constant
793          * address accordingly. But we cannot do this if the data in
794          * question is in the register space or at an absolute address less
795          * than 256. Register space is zero page, which means that the
796          * address calculation could overflow in the linker.
797          */
798         int AllowOneIndex = !ED_IsLocRegister (&Arg1.Expr) &&
799                             !(ED_IsLocAbs (&Arg1.Expr) && Arg1.Expr.IVal < 256);
800
801         /* Calculate the real stack offset */
802         int Offs = ED_GetStackOffs (&Arg2.Expr, 0);
803
804         /* Drop the generated code */
805         RemoveCode (&Start);
806
807         /* We need labels */
808         L1 = GetLocalLabel ();
809
810         /* Generate strcpy code */
811         AddCodeLine ("ldy #$%02X", (unsigned char) (Offs - 1));
812         if (Offs == 0 || AllowOneIndex) {
813             g_defcodelabel (L1);
814             AddCodeLine ("iny");
815             AddCodeLine ("lda (sp),y");
816             AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, -Offs));
817         } else {
818             AddCodeLine ("ldx #$FF");
819             g_defcodelabel (L1);
820             AddCodeLine ("iny");
821             AddCodeLine ("inx");
822             AddCodeLine ("lda (sp),y");
823             AddCodeLine ("sta %s,x", ED_GetLabelName (&Arg1.Expr, 0));
824         }
825         AddCodeLine ("bne %s", LocalLabelName (L1));
826
827         /* strcpy returns argument #1 */
828         *Expr = Arg1.Expr;
829
830     } else if (ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr) &&
831                ED_IsRVal (&Arg1.Expr) && ED_IsLocStack (&Arg1.Expr) &&
832                StackPtr >= -255) {
833
834         /* It is possible to just use one index register even if the stack
835          * offset is not zero, by adjusting the offset to the constant
836          * address accordingly. But we cannot do this if the data in
837          * question is in the register space or at an absolute address less
838          * than 256. Register space is zero page, which means that the
839          * address calculation could overflow in the linker.
840          */
841         int AllowOneIndex = !ED_IsLocRegister (&Arg2.Expr) &&
842                             !(ED_IsLocAbs (&Arg2.Expr) && Arg2.Expr.IVal < 256);
843
844         /* Calculate the real stack offset */
845         int Offs = ED_GetStackOffs (&Arg1.Expr, 0);
846
847         /* Drop the generated code */
848         RemoveCode (&Start);
849
850         /* We need labels */
851         L1 = GetLocalLabel ();
852
853         /* Generate strcpy code */
854         AddCodeLine ("ldy #$%02X", (unsigned char) (Offs - 1));
855         if (Offs == 0 || AllowOneIndex) {
856             g_defcodelabel (L1);
857             AddCodeLine ("iny");
858             AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg2.Expr, -Offs));
859             AddCodeLine ("sta (sp),y");
860         } else {
861             AddCodeLine ("ldx #$FF");
862             g_defcodelabel (L1);
863             AddCodeLine ("iny");
864             AddCodeLine ("inx");
865             AddCodeLine ("lda %s,x", ED_GetLabelName (&Arg2.Expr, 0));
866             AddCodeLine ("sta (sp),y");
867         }
868         AddCodeLine ("bne %s", LocalLabelName (L1));
869
870         /* strcpy returns argument #1 */
871         *Expr = Arg1.Expr;
872
873     } else {
874
875         /* The function result is an rvalue in the primary register */
876         ED_MakeRValExpr (Expr);
877         Expr->Type = GetFuncReturn (Expr->Type);
878
879     }
880
881     /* We expect the closing brace */
882     ConsumeRParen ();
883 }
884
885
886
887 /*****************************************************************************/
888 /*                                  strlen                                   */
889 /*****************************************************************************/
890
891
892
893 static void StdFunc_strlen (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
894 /* Handle the strlen function */
895 {
896     static Type ArgType[] = { TYPE(T_PTR), TYPE(T_CHAR|T_QUAL_CONST), TYPE(T_END) };
897     ExprDesc    Arg;
898     int         IsArray;
899     int         IsPtr;
900     int         IsByteIndex;
901     long        ECount;
902     unsigned    L;
903
904
905
906     /* Setup the argument type string */
907     ArgType[1].C = GetDefaultChar () | T_QUAL_CONST;
908
909     /* Evaluate the parameter */
910     hie1 (&Arg);
911
912     /* Check if the argument is an array. If so, remember the element count.
913      * Otherwise set the element count to undefined.
914      */
915     IsArray = IsTypeArray (Arg.Type);
916     if (IsArray) {
917         ECount = GetElementCount (Arg.Type);
918         if (ECount == FLEXIBLE) {
919             /* Treat as unknown */
920             ECount = UNSPECIFIED;
921         }
922         IsPtr = 0;
923     } else {
924         ECount = UNSPECIFIED;
925         IsPtr  = IsTypePtr (Arg.Type);
926     }
927
928     /* Check if the elements of an array can be addressed by a byte sized
929      * index. This is true if the size of the array is known and less than
930      * 256.
931      */
932     IsByteIndex = (ECount != UNSPECIFIED && ECount < 256);
933
934     /* Do type conversion */
935     TypeConversion (&Arg, ArgType);
936
937     /* If the expression is a literal, and if string literals are read
938      * only, we can calculate the length of the string and remove it
939      * from the literal pool. Otherwise we have to calculate the length
940      * at runtime.
941      */
942     if (ED_IsLocLiteral (&Arg) && IS_Get (&WritableStrings) == 0) {
943
944         /* Constant string literal */
945         ED_MakeConstAbs (Expr, GetLiteralSize (Arg.LVal) - 1, type_size_t);
946
947         /* We don't need the literal any longer */
948         ReleaseLiteral (Arg.LVal);
949
950     /* We will inline strlen for arrays with constant addresses, if either the
951      * inlining was forced on the command line, or the array is smaller than
952      * 256, so the inlining is considered safe.
953      */
954     } else if (ED_IsLocConst (&Arg) && IsArray &&
955                (IS_Get (&InlineStdFuncs) || IsByteIndex)) {
956
957         /* Generate the strlen code */
958         L = GetLocalLabel ();
959         AddCodeLine ("ldy #$FF");
960         g_defcodelabel (L);
961         AddCodeLine ("iny");
962         AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg, 0));
963         AddCodeLine ("bne %s", LocalLabelName (L));
964         AddCodeLine ("tax");
965         AddCodeLine ("tya");
966
967         /* The function result is an rvalue in the primary register */
968         ED_MakeRValExpr (Expr);
969         Expr->Type = type_size_t;
970
971     /* We will inline strlen for arrays on the stack, if the array is
972      * completely within the reach of a byte sized index register.
973      */
974     } else if (ED_IsLocStack (&Arg) && IsArray && IsByteIndex &&
975                (Arg.IVal - StackPtr) + ECount < 256) {
976
977         /* Calculate the true stack offset */
978         int Offs = ED_GetStackOffs (&Arg, 0);
979
980         /* Generate the strlen code */
981         L = GetLocalLabel ();
982         AddCodeLine ("ldx #$FF");
983         AddCodeLine ("ldy #$%02X", (unsigned char) (Offs-1));
984         g_defcodelabel (L);
985         AddCodeLine ("inx");
986         AddCodeLine ("iny");
987         AddCodeLine ("lda (sp),y");
988         AddCodeLine ("bne %s", LocalLabelName (L));
989         AddCodeLine ("txa");
990         AddCodeLine ("ldx #$00");
991
992         /* The function result is an rvalue in the primary register */
993         ED_MakeRValExpr (Expr);
994         Expr->Type = type_size_t;
995
996     /* strlen for a string that is pointed to by a register variable will only
997      * get inlined if requested on the command line, since we cannot know how
998      * big the buffer actually is, so inlining is not always safe.
999      */
1000     } else if (ED_IsLocRegister (&Arg) && ED_IsLVal (&Arg) && IsPtr &&
1001                IS_Get (&InlineStdFuncs)) {
1002
1003         /* Generate the strlen code */
1004         L = GetLocalLabel ();
1005         AddCodeLine ("ldy #$FF");
1006         g_defcodelabel (L);
1007         AddCodeLine ("iny");
1008         AddCodeLine ("lda (%s),y", ED_GetLabelName (&Arg, 0));
1009         AddCodeLine ("bne %s", LocalLabelName (L));
1010         AddCodeLine ("tax");
1011         AddCodeLine ("tya");
1012
1013         /* The function result is an rvalue in the primary register */
1014         ED_MakeRValExpr (Expr);
1015         Expr->Type = type_size_t;
1016
1017     /* Last check: We will inline a generic strlen routine if inlining was
1018      * requested on the command line, and the code size factor is more than
1019      * 400 (code is 13 bytes vs. 3 for a jsr call).
1020      */
1021     } else if (IS_Get (&CodeSizeFactor) > 400 && IS_Get (&InlineStdFuncs)) {
1022
1023         /* Load the expression into the primary */
1024         LoadExpr (CF_NONE, &Arg);
1025
1026         /* Inline the function */
1027         L = GetLocalLabel ();
1028         AddCodeLine ("sta ptr1");
1029         AddCodeLine ("stx ptr1+1");
1030         AddCodeLine ("ldy #$FF");
1031         g_defcodelabel (L);
1032         AddCodeLine ("iny");
1033         AddCodeLine ("lda (ptr1),y");
1034         AddCodeLine ("bne %s", LocalLabelName (L));
1035         AddCodeLine ("tax");
1036         AddCodeLine ("tya");
1037
1038         /* The function result is an rvalue in the primary register */
1039         ED_MakeRValExpr (Expr);
1040         Expr->Type = type_size_t;
1041
1042     } else {
1043
1044         /* Load the expression into the primary */
1045         LoadExpr (CF_NONE, &Arg);
1046
1047         /* Call the strlen function */
1048         AddCodeLine ("jsr _%s", Func_strlen);
1049
1050         /* The function result is an rvalue in the primary register */
1051         ED_MakeRValExpr (Expr);
1052         Expr->Type = type_size_t;
1053
1054     }
1055
1056     /* We expect the closing brace */
1057     ConsumeRParen ();
1058 }
1059
1060
1061
1062 /*****************************************************************************/
1063 /*                                   Code                                    */
1064 /*****************************************************************************/
1065
1066
1067
1068 int FindStdFunc (const char* Name)
1069 /* Determine if the given function is a known standard function that may be
1070  * called in a special way. If so, return the index, otherwise return -1.
1071  */
1072 {
1073     /* Look into the table for known names */
1074     struct StdFuncDesc* D =
1075         bsearch (Name, StdFuncs, FUNC_COUNT, sizeof (StdFuncs[0]), CmpFunc);
1076
1077     /* Return the function index or -1 */
1078     if (D == 0) {
1079         return -1;
1080     } else {
1081         return D - StdFuncs;
1082     }
1083 }
1084
1085
1086
1087 void HandleStdFunc (int Index, FuncDesc* F, ExprDesc* lval)
1088 /* Generate code for a known standard function. */
1089 {
1090     struct StdFuncDesc* D;
1091
1092     /* Get a pointer to the table entry */
1093     CHECK (Index >= 0 && Index < (int)FUNC_COUNT);
1094     D = StdFuncs + Index;
1095
1096     /* Call the handler function */
1097     D->Handler (F, lval);
1098 }
1099
1100
1101