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