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