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