1 /*****************************************************************************/
5 /* Handle inlining of known functions for the cc65 compiler */
9 /* (C) 1998-2006 Ullrich von Bassewitz */
10 /* Römerstrasse 52 */
11 /* D-70794 Filderstadt */
12 /* EMail: uz@cc65.org */
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. */
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: */
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 */
32 /*****************************************************************************/
60 /*****************************************************************************/
61 /* Function forwards */
62 /*****************************************************************************/
66 static void StdFunc_memcpy (FuncDesc*, ExprDesc*);
67 static void StdFunc_memset (FuncDesc*, ExprDesc*);
68 static void StdFunc_strcpy (FuncDesc*, ExprDesc*);
69 static void StdFunc_strlen (FuncDesc*, ExprDesc*);
73 /*****************************************************************************/
75 /*****************************************************************************/
79 /* Table with all known functions and their handlers. Must be sorted
82 static struct StdFuncDesc {
84 void (*Handler) (FuncDesc*, ExprDesc*);
86 { "memcpy", StdFunc_memcpy },
87 { "memset", StdFunc_memset },
88 { "strcpy", StdFunc_strcpy },
89 { "strlen", StdFunc_strlen },
92 #define FUNC_COUNT (sizeof (StdFuncs) / sizeof (StdFuncs[0]))
94 typedef struct ArgDesc ArgDesc;
96 const Type* ArgType; /* Required argument type */
97 ExprDesc Expr; /* Argument expression */
98 const Type* Type; /* The original type before conversion */
99 CodeMark Start; /* Start of the code for calculation */
100 CodeMark Push; /* Start of argument push code */
101 CodeMark End; /* End of the code for calculation+push */
102 unsigned Flags; /* Code generation flags */
107 /*****************************************************************************/
108 /* Helper functions */
109 /*****************************************************************************/
113 static int CmpFunc (const void* Key, const void* Elem)
114 /* Compare function for bsearch */
116 return strcmp ((const char*) Key, ((const struct StdFuncDesc*) Elem)->Name);
121 static long ArrayElementCount (const ArgDesc* Arg)
122 /* Check if the type of the given argument is an array. If so, and if the
123 * element count is known, return it. In all other cases, return UNSPECIFIED.
128 if (IsTypeArray (Arg->Type)) {
129 Count = GetElementCount (Arg->Type);
130 if (Count == FLEXIBLE) {
131 /* Treat as unknown */
142 static void ParseArg (ArgDesc* Arg, Type* Type)
143 /* Parse one argument but do not push it onto the stack. Make all fields in
147 /* We have a prototype, so chars may be pushed as chars */
148 Arg->Flags = CF_FORCECHAR;
150 /* Remember the required argument type */
153 /* Remember the current code position */
154 GetCodePos (&Arg->Start);
156 /* Read the expression we're going to pass to the function */
157 MarkedExprWithCheck (hie1, &Arg->Expr);
159 /* Remember the actual argument type */
160 Arg->Type = Arg->Expr.Type;
162 /* Convert this expression to the expected type */
163 TypeConversion (&Arg->Expr, Type);
165 /* If the value is a constant, set the flag, otherwise load it into the
168 if (ED_IsConstAbsInt (&Arg->Expr) && ED_CodeRangeIsEmpty (&Arg->Expr)) {
169 /* Remember that we have a constant value */
170 Arg->Flags |= CF_CONST;
172 /* Load into the primary */
173 LoadExpr (CF_NONE, &Arg->Expr);
176 /* Remember the following code position */
177 GetCodePos (&Arg->Push);
178 GetCodePos (&Arg->End);
180 /* Use the type of the argument for the push */
181 Arg->Flags |= TypeOf (Arg->Expr.Type);
186 /*****************************************************************************/
188 /*****************************************************************************/
192 static void StdFunc_memcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
193 /* Handle the memcpy function */
195 /* Argument types: (void*, const void*, size_t) */
196 static Type Arg1Type[] = { TYPE(T_PTR), TYPE(T_VOID), TYPE(T_END) };
197 static Type Arg2Type[] = { TYPE(T_PTR), TYPE(T_VOID|T_QUAL_CONST), TYPE(T_END) };
198 static Type Arg3Type[] = { TYPE(T_SIZE_T), TYPE(T_END) };
201 ArgDesc Arg1, Arg2, Arg3;
202 unsigned ParamSize = 0;
205 /* Remember where we are now */
209 ParseArg (&Arg1, Arg1Type);
210 g_push (Arg1.Flags, Arg1.Expr.IVal);
211 GetCodePos (&Arg1.End);
212 ParamSize += SizeOf (Arg1Type);
216 ParseArg (&Arg2, Arg2Type);
217 g_push (Arg2.Flags, Arg2.Expr.IVal);
218 GetCodePos (&Arg2.End);
219 ParamSize += SizeOf (Arg2Type);
222 /* Argument #3. Since memcpy is a fastcall function, we must load the
223 * arg into the primary if it is not already there. This parameter is
224 * also ignored for the calculation of the parameter size, since it is
225 * not passed via the stack.
227 ParseArg (&Arg3, Arg3Type);
228 if (Arg3.Flags & CF_CONST) {
229 LoadExpr (CF_NONE, &Arg3.Expr);
232 /* Emit the actual function call. This will also cleanup the stack. */
233 g_call (CF_FIXARGC, Func_memcpy, ParamSize);
235 if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal == 0) {
237 /* memcpy has been called with a count argument of zero */
238 Warning ("Call to memcpy has no effect");
240 /* Remove all of the generated code but the load of the first
241 * argument, which is what memcpy returns.
243 RemoveCode (&Arg1.Push);
245 /* Set the function result to the first argument */
248 /* Bail out, no need for further improvements */
252 /* We've generated the complete code for the function now and know the
253 * types of all parameters. Check for situations where better code can
254 * be generated. If such a situation is detected, throw away the
255 * generated, and emit better code.
257 if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
258 ((ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr)) ||
259 (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr))) &&
260 ((ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) ||
261 (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr)))) {
263 int Reg1 = ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr);
264 int Reg2 = ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr);
266 /* Drop the generated code */
269 /* We need a label */
270 Label = GetLocalLabel ();
272 /* Generate memcpy code */
273 if (Arg3.Expr.IVal <= 127) {
275 AddCodeLine ("ldy #$%02X", (unsigned char) (Arg3.Expr.IVal-1));
276 AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
277 g_defcodelabel (Label);
279 AddCodeLine ("lda (%s),y", ED_GetLabelName (&Arg2.Expr, 0));
281 AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg2.Expr, 0));
284 AddCodeLine ("sta (%s),y", ED_GetLabelName (&Arg1.Expr, 0));
286 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, 0));
289 AddCodeLine ("bpl %s", LocalLabelName (Label));
293 AddCodeLine ("ldy #$00");
294 AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
295 g_defcodelabel (Label);
297 AddCodeLine ("lda (%s),y", ED_GetLabelName (&Arg2.Expr, 0));
299 AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg2.Expr, 0));
302 AddCodeLine ("sta (%s),y", ED_GetLabelName (&Arg1.Expr, 0));
304 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, 0));
307 AddCodeLine ("cpy #$%02X", (unsigned char) Arg3.Expr.IVal);
308 AddCodeLine ("bne %s", LocalLabelName (Label));
312 /* memcpy returns the address, so the result is actually identical
313 * to the first argument.
317 } else if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
318 ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr) &&
319 ED_IsRVal (&Arg1.Expr) && ED_IsLocStack (&Arg1.Expr) &&
320 (Arg1.Expr.IVal - StackPtr) + Arg3.Expr.IVal < 256) {
322 /* It is possible to just use one index register even if the stack
323 * offset is not zero, by adjusting the offset to the constant
324 * address accordingly. But we cannot do this if the data in
325 * question is in the register space or at an absolute address less
326 * than 256. Register space is zero page, which means that the
327 * address calculation could overflow in the linker.
329 int AllowOneIndex = !ED_IsLocRegister (&Arg2.Expr) &&
330 !(ED_IsLocAbs (&Arg2.Expr) && Arg2.Expr.IVal < 256);
332 /* Calculate the real stack offset */
333 int Offs = ED_GetStackOffs (&Arg1.Expr, 0);
335 /* Drop the generated code */
338 /* We need a label */
339 Label = GetLocalLabel ();
341 /* Generate memcpy code */
342 if (Arg3.Expr.IVal <= 127 && !AllowOneIndex) {
345 AddCodeLine ("ldy #$%02X", (unsigned char) (Offs + Arg3.Expr.IVal - 1));
346 g_defcodelabel (Label);
347 AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg2.Expr, -Offs));
348 AddCodeLine ("sta (sp),y");
350 AddCodeLine ("bpl %s", LocalLabelName (Label));
352 AddCodeLine ("ldx #$%02X", (unsigned char) (Arg3.Expr.IVal-1));
353 AddCodeLine ("ldy #$%02X", (unsigned char) (Offs + Arg3.Expr.IVal - 1));
354 g_defcodelabel (Label);
355 AddCodeLine ("lda %s,x", ED_GetLabelName (&Arg2.Expr, 0));
356 AddCodeLine ("sta (sp),y");
359 AddCodeLine ("bpl %s", LocalLabelName (Label));
364 if (Offs == 0 || AllowOneIndex) {
365 AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
366 g_defcodelabel (Label);
367 AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg2.Expr, -Offs));
368 AddCodeLine ("sta (sp),y");
370 AddCodeLine ("cpy #$%02X", (unsigned char) (Offs + Arg3.Expr.IVal));
371 AddCodeLine ("bne %s", LocalLabelName (Label));
373 AddCodeLine ("ldx #$00");
374 AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
375 g_defcodelabel (Label);
376 AddCodeLine ("lda %s,x", ED_GetLabelName (&Arg2.Expr, 0));
377 AddCodeLine ("sta (sp),y");
380 AddCodeLine ("cpx #$%02X", (unsigned char) Arg3.Expr.IVal);
381 AddCodeLine ("bne %s", LocalLabelName (Label));
386 /* memcpy returns the address, so the result is actually identical
387 * to the first argument.
391 } else if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
392 ED_IsRVal (&Arg2.Expr) && ED_IsLocStack (&Arg2.Expr) &&
393 (Arg2.Expr.IVal - StackPtr) + Arg3.Expr.IVal < 256 &&
394 ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) {
396 /* It is possible to just use one index register even if the stack
397 * offset is not zero, by adjusting the offset to the constant
398 * address accordingly. But we cannot do this if the data in
399 * question is in the register space or at an absolute address less
400 * than 256. Register space is zero page, which means that the
401 * address calculation could overflow in the linker.
403 int AllowOneIndex = !ED_IsLocRegister (&Arg1.Expr) &&
404 !(ED_IsLocAbs (&Arg1.Expr) && Arg1.Expr.IVal < 256);
406 /* Calculate the real stack offset */
407 int Offs = ED_GetStackOffs (&Arg2.Expr, 0);
409 /* Drop the generated code */
412 /* We need a label */
413 Label = GetLocalLabel ();
415 /* Generate memcpy code */
416 if (Arg3.Expr.IVal <= 127 && !AllowOneIndex) {
419 AddCodeLine ("ldy #$%02X", (unsigned char) (Arg3.Expr.IVal - 1));
420 g_defcodelabel (Label);
421 AddCodeLine ("lda (sp),y");
422 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, 0));
424 AddCodeLine ("bpl %s", LocalLabelName (Label));
426 AddCodeLine ("ldx #$%02X", (unsigned char) (Arg3.Expr.IVal-1));
427 AddCodeLine ("ldy #$%02X", (unsigned char) (Offs + Arg3.Expr.IVal - 1));
428 g_defcodelabel (Label);
429 AddCodeLine ("lda (sp),y");
430 AddCodeLine ("sta %s,x", ED_GetLabelName (&Arg1.Expr, 0));
433 AddCodeLine ("bpl %s", LocalLabelName (Label));
438 if (Offs == 0 || AllowOneIndex) {
439 AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
440 g_defcodelabel (Label);
441 AddCodeLine ("lda (sp),y");
442 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, -Offs));
444 AddCodeLine ("cpy #$%02X", (unsigned char) (Offs + Arg3.Expr.IVal));
445 AddCodeLine ("bne %s", LocalLabelName (Label));
447 AddCodeLine ("ldx #$00");
448 AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
449 g_defcodelabel (Label);
450 AddCodeLine ("lda (sp),y");
451 AddCodeLine ("sta %s,x", ED_GetLabelName (&Arg1.Expr, 0));
454 AddCodeLine ("cpx #$%02X", (unsigned char) Arg3.Expr.IVal);
455 AddCodeLine ("bne %s", LocalLabelName (Label));
460 /* memcpy returns the address, so the result is actually identical
461 * to the first argument.
467 /* The function result is an rvalue in the primary register */
468 ED_MakeRValExpr (Expr);
469 Expr->Type = GetFuncReturn (Expr->Type);
474 /* We expect the closing brace */
480 /*****************************************************************************/
482 /*****************************************************************************/
486 static void StdFunc_memset (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
487 /* Handle the memset function */
489 /* Argument types: (void*, int, size_t) */
490 static Type Arg1Type[] = { TYPE(T_PTR), TYPE(T_VOID), TYPE(T_END) };
491 static Type Arg2Type[] = { TYPE(T_INT), TYPE(T_END) };
492 static Type Arg3Type[] = { TYPE(T_SIZE_T), TYPE(T_END) };
495 ArgDesc Arg1, Arg2, Arg3;
496 int MemSet = 1; /* Use real memset if true */
497 unsigned ParamSize = 0;
500 /* Remember where we are now */
504 ParseArg (&Arg1, Arg1Type);
505 g_push (Arg1.Flags, Arg1.Expr.IVal);
506 GetCodePos (&Arg1.End);
507 ParamSize += SizeOf (Arg1Type);
510 /* Argument #2. This argument is special in that we will call another
511 * function if it is a constant zero.
513 ParseArg (&Arg2, Arg2Type);
514 if ((Arg2.Flags & CF_CONST) != 0 && Arg2.Expr.IVal == 0) {
515 /* Don't call memset, call bzero instead */
518 /* Push the argument */
519 g_push (Arg2.Flags, Arg2.Expr.IVal);
520 GetCodePos (&Arg2.End);
521 ParamSize += SizeOf (Arg2Type);
525 /* Argument #3. Since memset is a fastcall function, we must load the
526 * arg into the primary if it is not already there. This parameter is
527 * also ignored for the calculation of the parameter size, since it is
528 * not passed via the stack.
530 ParseArg (&Arg3, Arg3Type);
531 if (Arg3.Flags & CF_CONST) {
532 LoadExpr (CF_NONE, &Arg3.Expr);
535 /* Emit the actual function call. This will also cleanup the stack. */
536 g_call (CF_FIXARGC, MemSet? Func_memset : Func__bzero, ParamSize);
538 if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal == 0) {
540 /* memset has been called with a count argument of zero */
541 Warning ("Call to memset has no effect");
543 /* Remove all of the generated code but the load of the first
544 * argument, which is what memset returns.
546 RemoveCode (&Arg1.Push);
548 /* Set the function result to the first argument */
551 /* Bail out, no need for further improvements */
555 /* We've generated the complete code for the function now and know the
556 * types of all parameters. Check for situations where better code can
557 * be generated. If such a situation is detected, throw away the
558 * generated, and emit better code.
559 * Note: Lots of improvements would be possible here, but I will
560 * concentrate on the most common case: memset with arguments 2 and 3
561 * being constant numerical values. Some checks have shown that this
562 * covers nearly 90% of all memset calls.
564 if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
565 ED_IsConstAbsInt (&Arg2.Expr) &&
566 ((ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) ||
567 (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr)))) {
569 int Reg = ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr);
571 /* Drop the generated code */
574 /* We need a label */
575 Label = GetLocalLabel ();
577 /* Generate memset code */
578 if (Arg3.Expr.IVal <= 127) {
580 AddCodeLine ("ldy #$%02X", (unsigned char) (Arg3.Expr.IVal-1));
581 AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
582 g_defcodelabel (Label);
584 AddCodeLine ("sta (%s),y", ED_GetLabelName (&Arg1.Expr, 0));
586 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, 0));
589 AddCodeLine ("bpl %s", LocalLabelName (Label));
593 AddCodeLine ("ldy #$00");
594 AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
595 g_defcodelabel (Label);
597 AddCodeLine ("sta (%s),y", ED_GetLabelName (&Arg1.Expr, 0));
599 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, 0));
602 AddCodeLine ("cpy #$%02X", (unsigned char) Arg3.Expr.IVal);
603 AddCodeLine ("bne %s", LocalLabelName (Label));
607 /* memset returns the address, so the result is actually identical
608 * to the first argument.
612 } else if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
613 ED_IsConstAbsInt (&Arg2.Expr) &&
614 ED_IsRVal (&Arg1.Expr) && ED_IsLocStack (&Arg1.Expr) &&
615 (Arg1.Expr.IVal - StackPtr) + Arg3.Expr.IVal < 256) {
617 /* Calculate the real stack offset */
618 int Offs = ED_GetStackOffs (&Arg1.Expr, 0);
620 /* Drop the generated code */
623 /* We need a label */
624 Label = GetLocalLabel ();
626 /* Generate memset code */
627 AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
628 AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
629 g_defcodelabel (Label);
630 AddCodeLine ("sta (sp),y");
632 AddCodeLine ("cpy #$%02X", (unsigned char) (Offs + Arg3.Expr.IVal));
633 AddCodeLine ("bne %s", LocalLabelName (Label));
635 /* memset returns the address, so the result is actually identical
636 * to the first argument.
640 } else if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
641 ED_IsConstAbsInt (&Arg2.Expr) &&
642 (Arg2.Expr.IVal != 0 || IS_Get (&CodeSizeFactor) > 200)) {
644 /* Remove all of the generated code but the load of the first
647 RemoveCode (&Arg1.Push);
649 /* We need a label */
650 Label = GetLocalLabel ();
653 AddCodeLine ("sta ptr1");
654 AddCodeLine ("stx ptr1+1");
655 if (Arg3.Expr.IVal <= 127) {
656 AddCodeLine ("ldy #$%02X", (unsigned char) (Arg3.Expr.IVal-1));
657 AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
658 g_defcodelabel (Label);
659 AddCodeLine ("sta (ptr1),y");
661 AddCodeLine ("bpl %s", LocalLabelName (Label));
663 AddCodeLine ("ldy #$00");
664 AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
665 g_defcodelabel (Label);
666 AddCodeLine ("sta (ptr1),y");
668 AddCodeLine ("cpy #$%02X", (unsigned char) Arg3.Expr.IVal);
669 AddCodeLine ("bne %s", LocalLabelName (Label));
672 /* Load the function result pointer into a/x (x is still valid). This
673 * code will get removed by the optimizer if it is not used later.
675 AddCodeLine ("lda ptr1");
677 /* The function result is an rvalue in the primary register */
678 ED_MakeRValExpr (Expr);
679 Expr->Type = GetFuncReturn (Expr->Type);
683 /* The function result is an rvalue in the primary register */
684 ED_MakeRValExpr (Expr);
685 Expr->Type = GetFuncReturn (Expr->Type);
690 /* We expect the closing brace */
696 /*****************************************************************************/
698 /*****************************************************************************/
702 static void StdFunc_strcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
703 /* Handle the strcpy function */
705 /* Argument types: (char*, const char*) */
706 static Type Arg1Type[] = { TYPE(T_PTR), TYPE(T_CHAR), TYPE(T_END) };
707 static Type Arg2Type[] = { TYPE(T_PTR), TYPE(T_CHAR|T_QUAL_CONST), TYPE(T_END) };
711 unsigned ParamSize = 0;
715 /* Setup the argument type string */
716 Arg1Type[1].C = GetDefaultChar ();
717 Arg2Type[1].C = GetDefaultChar () | T_QUAL_CONST;
719 /* Remember where we are now */
723 ParseArg (&Arg1, Arg1Type);
724 g_push (Arg1.Flags, Arg1.Expr.IVal);
725 GetCodePos (&Arg1.End);
726 ParamSize += SizeOf (Arg1Type);
729 /* Argument #2. Since strcpy is a fastcall function, we must load the
730 * arg into the primary if it is not already there. This parameter is
731 * also ignored for the calculation of the parameter size, since it is
732 * not passed via the stack.
734 ParseArg (&Arg2, Arg2Type);
735 if (Arg2.Flags & CF_CONST) {
736 LoadExpr (CF_NONE, &Arg2.Expr);
739 /* Emit the actual function call. This will also cleanup the stack. */
740 g_call (CF_FIXARGC, Func_strcpy, ParamSize);
742 /* Get the element count of argument 2 if it is an array */
743 ECount = ArrayElementCount (&Arg1);
745 /* We've generated the complete code for the function now and know the
746 * types of all parameters. Check for situations where better code can
747 * be generated. If such a situation is detected, throw away the
748 * generated, and emit better code.
750 if (((ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr)) ||
751 (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr))) &&
752 ((ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) ||
753 (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr))) &&
754 (IS_Get (&InlineStdFuncs) ||
755 (ECount != UNSPECIFIED && ECount < 256))) {
759 if (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr)) {
764 if (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr)) {
765 Store = "sta (%s),y";
770 /* Drop the generated code */
774 L1 = GetLocalLabel ();
776 /* Generate strcpy code */
777 AddCodeLine ("ldy #$FF");
780 AddCodeLine (Load, ED_GetLabelName (&Arg2.Expr, 0));
781 AddCodeLine (Store, ED_GetLabelName (&Arg1.Expr, 0));
782 AddCodeLine ("bne %s", LocalLabelName (L1));
784 /* strcpy returns argument #1 */
787 } else if (ED_IsRVal (&Arg2.Expr) && ED_IsLocStack (&Arg2.Expr) &&
789 ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) {
791 /* It is possible to just use one index register even if the stack
792 * offset is not zero, by adjusting the offset to the constant
793 * address accordingly. But we cannot do this if the data in
794 * question is in the register space or at an absolute address less
795 * than 256. Register space is zero page, which means that the
796 * address calculation could overflow in the linker.
798 int AllowOneIndex = !ED_IsLocRegister (&Arg1.Expr) &&
799 !(ED_IsLocAbs (&Arg1.Expr) && Arg1.Expr.IVal < 256);
801 /* Calculate the real stack offset */
802 int Offs = ED_GetStackOffs (&Arg2.Expr, 0);
804 /* Drop the generated code */
808 L1 = GetLocalLabel ();
810 /* Generate strcpy code */
811 AddCodeLine ("ldy #$%02X", (unsigned char) (Offs - 1));
812 if (Offs == 0 || AllowOneIndex) {
815 AddCodeLine ("lda (sp),y");
816 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, -Offs));
818 AddCodeLine ("ldx #$FF");
822 AddCodeLine ("lda (sp),y");
823 AddCodeLine ("sta %s,x", ED_GetLabelName (&Arg1.Expr, 0));
825 AddCodeLine ("bne %s", LocalLabelName (L1));
827 /* strcpy returns argument #1 */
830 } else if (ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr) &&
831 ED_IsRVal (&Arg1.Expr) && ED_IsLocStack (&Arg1.Expr) &&
834 /* It is possible to just use one index register even if the stack
835 * offset is not zero, by adjusting the offset to the constant
836 * address accordingly. But we cannot do this if the data in
837 * question is in the register space or at an absolute address less
838 * than 256. Register space is zero page, which means that the
839 * address calculation could overflow in the linker.
841 int AllowOneIndex = !ED_IsLocRegister (&Arg2.Expr) &&
842 !(ED_IsLocAbs (&Arg2.Expr) && Arg2.Expr.IVal < 256);
844 /* Calculate the real stack offset */
845 int Offs = ED_GetStackOffs (&Arg1.Expr, 0);
847 /* Drop the generated code */
851 L1 = GetLocalLabel ();
853 /* Generate strcpy code */
854 AddCodeLine ("ldy #$%02X", (unsigned char) (Offs - 1));
855 if (Offs == 0 || AllowOneIndex) {
858 AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg2.Expr, -Offs));
859 AddCodeLine ("sta (sp),y");
861 AddCodeLine ("ldx #$FF");
865 AddCodeLine ("lda %s,x", ED_GetLabelName (&Arg2.Expr, 0));
866 AddCodeLine ("sta (sp),y");
868 AddCodeLine ("bne %s", LocalLabelName (L1));
870 /* strcpy returns argument #1 */
875 /* The function result is an rvalue in the primary register */
876 ED_MakeRValExpr (Expr);
877 Expr->Type = GetFuncReturn (Expr->Type);
881 /* We expect the closing brace */
887 /*****************************************************************************/
889 /*****************************************************************************/
893 static void StdFunc_strlen (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
894 /* Handle the strlen function */
896 static Type ArgType[] = { TYPE(T_PTR), TYPE(T_CHAR|T_QUAL_CONST), TYPE(T_END) };
906 /* Setup the argument type string */
907 ArgType[1].C = GetDefaultChar () | T_QUAL_CONST;
909 /* Evaluate the parameter */
912 /* Check if the argument is an array. If so, remember the element count.
913 * Otherwise set the element count to undefined.
915 IsArray = IsTypeArray (Arg.Type);
917 ECount = GetElementCount (Arg.Type);
918 if (ECount == FLEXIBLE) {
919 /* Treat as unknown */
920 ECount = UNSPECIFIED;
924 ECount = UNSPECIFIED;
925 IsPtr = IsTypePtr (Arg.Type);
928 /* Check if the elements of an array can be addressed by a byte sized
929 * index. This is true if the size of the array is known and less than
932 IsByteIndex = (ECount != UNSPECIFIED && ECount < 256);
934 /* Do type conversion */
935 TypeConversion (&Arg, ArgType);
937 /* If the expression is a literal, and if string literals are read
938 * only, we can calculate the length of the string and remove it
939 * from the literal pool. Otherwise we have to calculate the length
942 if (ED_IsLocLiteral (&Arg) && IS_Get (&WritableStrings) == 0) {
944 /* Constant string literal */
945 ED_MakeConstAbs (Expr, strlen (GetLiteral (Arg.IVal)), type_size_t);
946 ResetLiteralPoolOffs (Arg.IVal);
948 /* We will inline strlen for arrays with constant addresses, if either the
949 * inlining was forced on the command line, or the array is smaller than
950 * 256, so the inlining is considered safe.
952 } else if (ED_IsLocConst (&Arg) && IsArray &&
953 (IS_Get (&InlineStdFuncs) || IsByteIndex)) {
955 /* Generate the strlen code */
956 L = GetLocalLabel ();
957 AddCodeLine ("ldy #$FF");
960 AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg, 0));
961 AddCodeLine ("bne %s", LocalLabelName (L));
965 /* The function result is an rvalue in the primary register */
966 ED_MakeRValExpr (Expr);
967 Expr->Type = type_size_t;
969 /* We will inline strlen for arrays on the stack, if the array is
970 * completely within the reach of a byte sized index register.
972 } else if (ED_IsLocStack (&Arg) && IsArray && IsByteIndex &&
973 (Arg.IVal - StackPtr) + ECount < 256) {
975 /* Calculate the true stack offset */
976 int Offs = ED_GetStackOffs (&Arg, 0);
978 /* Generate the strlen code */
979 L = GetLocalLabel ();
980 AddCodeLine ("ldx #$FF");
981 AddCodeLine ("ldy #$%02X", (unsigned char) (Offs-1));
985 AddCodeLine ("lda (sp),y");
986 AddCodeLine ("bne %s", LocalLabelName (L));
988 AddCodeLine ("ldx #$00");
990 /* The function result is an rvalue in the primary register */
991 ED_MakeRValExpr (Expr);
992 Expr->Type = type_size_t;
994 /* strlen for a string that is pointed to by a register variable will only
995 * get inlined if requested on the command line, since we cannot know how
996 * big the buffer actually is, so inlining is not always safe.
998 } else if (ED_IsLocRegister (&Arg) && ED_IsLVal (&Arg) && IsPtr &&
999 IS_Get (&InlineStdFuncs)) {
1001 /* Generate the strlen code */
1002 L = GetLocalLabel ();
1003 AddCodeLine ("ldy #$FF");
1005 AddCodeLine ("iny");
1006 AddCodeLine ("lda (%s),y", ED_GetLabelName (&Arg, 0));
1007 AddCodeLine ("bne %s", LocalLabelName (L));
1008 AddCodeLine ("tax");
1009 AddCodeLine ("tya");
1011 /* The function result is an rvalue in the primary register */
1012 ED_MakeRValExpr (Expr);
1013 Expr->Type = type_size_t;
1015 /* Last check: We will inline a generic strlen routine if inlining was
1016 * requested on the command line, and the code size factor is more than
1017 * 400 (code is 13 bytes vs. 3 for a jsr call).
1019 } else if (IS_Get (&CodeSizeFactor) > 400 && IS_Get (&InlineStdFuncs)) {
1021 /* Load the expression into the primary */
1022 LoadExpr (CF_NONE, &Arg);
1024 /* Inline the function */
1025 L = GetLocalLabel ();
1026 AddCodeLine ("sta ptr1");
1027 AddCodeLine ("stx ptr1+1");
1028 AddCodeLine ("ldy #$FF");
1030 AddCodeLine ("iny");
1031 AddCodeLine ("lda (ptr1),y");
1032 AddCodeLine ("bne %s", LocalLabelName (L));
1033 AddCodeLine ("tax");
1034 AddCodeLine ("tya");
1036 /* The function result is an rvalue in the primary register */
1037 ED_MakeRValExpr (Expr);
1038 Expr->Type = type_size_t;
1042 /* Load the expression into the primary */
1043 LoadExpr (CF_NONE, &Arg);
1045 /* Call the strlen function */
1046 AddCodeLine ("jsr _%s", Func_strlen);
1048 /* The function result is an rvalue in the primary register */
1049 ED_MakeRValExpr (Expr);
1050 Expr->Type = type_size_t;
1054 /* We expect the closing brace */
1060 /*****************************************************************************/
1062 /*****************************************************************************/
1066 int FindStdFunc (const char* Name)
1067 /* Determine if the given function is a known standard function that may be
1068 * called in a special way. If so, return the index, otherwise return -1.
1071 /* Look into the table for known names */
1072 struct StdFuncDesc* D =
1073 bsearch (Name, StdFuncs, FUNC_COUNT, sizeof (StdFuncs[0]), CmpFunc);
1075 /* Return the function index or -1 */
1079 return D - StdFuncs;
1085 void HandleStdFunc (int Index, FuncDesc* F, ExprDesc* lval)
1086 /* Generate code for a known standard function. */
1088 struct StdFuncDesc* D;
1090 /* Get a pointer to the table entry */
1091 CHECK (Index >= 0 && Index < (int)FUNC_COUNT);
1092 D = StdFuncs + Index;
1094 /* Call the handler function */
1095 D->Handler (F, lval);