1 /*****************************************************************************/
5 /* Handle inlining of known functions for the cc65 compiler */
9 /* (C) 1998-2004 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 Arg->Start = GetCodePos ();
156 /* Read the expression we're going to pass to the function */
157 ExprWithCheck (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)) {
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 Arg->End = Arg->Push = GetCodePos ();
179 /* Use the type of the argument for the push */
180 Arg->Flags |= TypeOf (Arg->Expr.Type);
185 /*****************************************************************************/
187 /*****************************************************************************/
191 static void StdFunc_memcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
192 /* Handle the memcpy function */
195 static type Arg1Type[] = { T_PTR, T_VOID, T_END }; /* void* */
196 static type Arg2Type[] = { T_PTR, T_VOID|T_QUAL_CONST, T_END }; /* const void* */
197 static type Arg3Type[] = { T_SIZE_T, T_END }; /* size_t */
200 ArgDesc Arg1, Arg2, Arg3;
201 unsigned ParamSize = 0;
204 /* Remember where we are now */
205 Start = GetCodePos ();
208 ParseArg (&Arg1, Arg1Type);
209 g_push (Arg1.Flags, Arg1.Expr.IVal);
210 Arg1.End = GetCodePos ();
211 ParamSize += SizeOf (Arg1Type);
215 ParseArg (&Arg2, Arg2Type);
216 g_push (Arg2.Flags, Arg2.Expr.IVal);
217 Arg2.End = GetCodePos ();
218 ParamSize += SizeOf (Arg2Type);
221 /* Argument #3. Since memcpy is a fastcall function, we must load the
222 * arg into the primary if it is not already there. This parameter is
223 * also ignored for the calculation of the parameter size, since it is
224 * not passed via the stack.
226 ParseArg (&Arg3, Arg3Type);
227 if (Arg3.Flags & CF_CONST) {
228 LoadExpr (CF_NONE, &Arg3.Expr);
231 /* Emit the actual function call. This will also cleanup the stack. */
232 g_call (CF_FIXARGC, Func_memcpy, ParamSize);
234 if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal == 0) {
236 /* memcpy has been called with a count argument of zero */
237 Warning ("Call to memcpy has no effect");
239 /* Remove all of the generated code but the load of the first
240 * argument, which is what memcpy returns.
242 RemoveCode (Arg1.Push);
244 /* Set the function result to the first argument */
247 /* Bail out, no need for further improvements */
251 /* We've generated the complete code for the function now and know the
252 * types of all parameters. Check for situations where better code can
253 * be generated. If such a situation is detected, throw away the
254 * generated, and emit better code.
256 if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
257 ((ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr)) ||
258 (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr))) &&
259 ((ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) ||
260 (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr)))) {
262 int Reg1 = ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr);
263 int Reg2 = ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr);
265 /* Drop the generated code */
268 /* We need a label */
269 Label = GetLocalLabel ();
271 /* Generate memcpy code */
272 if (Arg3.Expr.IVal <= 127) {
274 AddCodeLine ("ldy #$%02X", (unsigned char) (Arg3.Expr.IVal-1));
275 AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
276 g_defcodelabel (Label);
278 AddCodeLine ("lda (%s),y", ED_GetLabelName (&Arg2.Expr, 0));
280 AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg2.Expr, 0));
283 AddCodeLine ("sta (%s),y", ED_GetLabelName (&Arg1.Expr, 0));
285 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, 0));
288 AddCodeLine ("bpl %s", LocalLabelName (Label));
292 AddCodeLine ("ldy #$00");
293 AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
294 g_defcodelabel (Label);
296 AddCodeLine ("lda (%s),y", ED_GetLabelName (&Arg2.Expr, 0));
298 AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg2.Expr, 0));
301 AddCodeLine ("sta (%s),y", ED_GetLabelName (&Arg1.Expr, 0));
303 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, 0));
306 AddCodeLine ("cpy #$%02X", (unsigned char) Arg3.Expr.IVal);
307 AddCodeLine ("bne %s", LocalLabelName (Label));
311 /* memcpy returns the address, so the result is actually identical
312 * to the first argument.
316 } else if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
317 ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr) &&
318 ED_IsRVal (&Arg1.Expr) && ED_IsLocStack (&Arg1.Expr) &&
319 (Arg1.Expr.IVal - StackPtr) + Arg3.Expr.IVal < 256) {
321 /* It is possible to just use one index register even if the stack
322 * offset is not zero, by adjusting the offset to the constant
323 * address accordingly. But we cannot do this if the data in
324 * question is in the register space or at an absolute address less
325 * than 256. Register space is zero page, which means that the
326 * address calculation could overflow in the linker.
328 int AllowOneIndex = !ED_IsLocRegister (&Arg2.Expr) &&
329 !(ED_IsLocAbs (&Arg2.Expr) && Arg2.Expr.IVal < 256);
331 /* Calculate the real stack offset */
332 int Offs = ED_GetStackOffs (&Arg1.Expr, 0);
334 /* Drop the generated code */
337 /* We need a label */
338 Label = GetLocalLabel ();
340 /* Generate memcpy code */
341 if (Arg3.Expr.IVal <= 127) {
343 if (Offs == 0 || AllowOneIndex) {
344 AddCodeLine ("ldy #$%02X", (unsigned char) (Offs + Arg3.Expr.IVal - 1));
345 g_defcodelabel (Label);
346 AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg2.Expr, -Offs));
347 AddCodeLine ("sta (sp),y");
349 AddCodeLine ("bpl %s", LocalLabelName (Label));
351 AddCodeLine ("ldx #$%02X", (unsigned char) (Arg3.Expr.IVal-1));
352 AddCodeLine ("ldy #$%02X", (unsigned char) (Offs + Arg3.Expr.IVal - 1));
353 g_defcodelabel (Label);
354 AddCodeLine ("lda %s,x", ED_GetLabelName (&Arg2.Expr, 0));
355 AddCodeLine ("sta (sp),y");
358 AddCodeLine ("bpl %s", LocalLabelName (Label));
363 if (Offs == 0 || AllowOneIndex) {
364 AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
365 g_defcodelabel (Label);
366 AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg2.Expr, -Offs));
367 AddCodeLine ("sta (sp),y");
369 AddCodeLine ("cpy #$%02X", (unsigned char) (Offs + Arg3.Expr.IVal));
370 AddCodeLine ("bne %s", LocalLabelName (Label));
372 AddCodeLine ("ldx #$00");
373 AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
374 g_defcodelabel (Label);
375 AddCodeLine ("lda %s,x", ED_GetLabelName (&Arg2.Expr, 0));
376 AddCodeLine ("sta (sp),y");
379 AddCodeLine ("cpx #$%02X", (unsigned char) Arg3.Expr.IVal);
380 AddCodeLine ("bne %s", LocalLabelName (Label));
385 /* memcpy returns the address, so the result is actually identical
386 * to the first argument.
390 } else if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
391 ED_IsRVal (&Arg2.Expr) && ED_IsLocStack (&Arg2.Expr) &&
392 (Arg2.Expr.IVal - StackPtr) + Arg3.Expr.IVal < 256 &&
393 ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) {
395 /* It is possible to just use one index register even if the stack
396 * offset is not zero, by adjusting the offset to the constant
397 * address accordingly. But we cannot do this if the data in
398 * question is in the register space or at an absolute address less
399 * than 256. Register space is zero page, which means that the
400 * address calculation could overflow in the linker.
402 int AllowOneIndex = !ED_IsLocRegister (&Arg1.Expr) &&
403 !(ED_IsLocAbs (&Arg1.Expr) && Arg1.Expr.IVal < 256);
405 /* Calculate the real stack offset */
406 int Offs = ED_GetStackOffs (&Arg2.Expr, 0);
408 /* Drop the generated code */
411 /* We need a label */
412 Label = GetLocalLabel ();
414 /* Generate memcpy code */
415 if (Arg3.Expr.IVal <= 127) {
417 if (Offs == 0 || AllowOneIndex) {
418 AddCodeLine ("ldy #$%02X", (unsigned char) (Offs + Arg3.Expr.IVal - 1));
419 g_defcodelabel (Label);
420 AddCodeLine ("lda (sp),y");
421 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, -Offs));
423 AddCodeLine ("bpl %s", LocalLabelName (Label));
425 AddCodeLine ("ldx #$%02X", (unsigned char) (Arg3.Expr.IVal-1));
426 AddCodeLine ("ldy #$%02X", (unsigned char) (Offs + Arg3.Expr.IVal - 1));
427 g_defcodelabel (Label);
428 AddCodeLine ("lda (sp),y");
429 AddCodeLine ("sta %s,x", ED_GetLabelName (&Arg1.Expr, 0));
432 AddCodeLine ("bpl %s", LocalLabelName (Label));
437 if (Offs == 0 || AllowOneIndex) {
438 AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
439 g_defcodelabel (Label);
440 AddCodeLine ("lda (sp),y");
441 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, -Offs));
443 AddCodeLine ("cpy #$%02X", (unsigned char) (Offs + Arg3.Expr.IVal));
444 AddCodeLine ("bne %s", LocalLabelName (Label));
446 AddCodeLine ("ldx #$00");
447 AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
448 g_defcodelabel (Label);
449 AddCodeLine ("lda (sp),y");
450 AddCodeLine ("sta %s,x", ED_GetLabelName (&Arg1.Expr, 0));
453 AddCodeLine ("cpx #$%02X", (unsigned char) Arg3.Expr.IVal);
454 AddCodeLine ("bne %s", LocalLabelName (Label));
459 /* memcpy returns the address, so the result is actually identical
460 * to the first argument.
466 /* The function result is an rvalue in the primary register */
467 ED_MakeRValExpr (Expr);
468 Expr->Type = GetFuncReturn (Expr->Type);
473 /* We expect the closing brace */
479 /*****************************************************************************/
481 /*****************************************************************************/
485 static void StdFunc_memset (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
486 /* Handle the memset function */
489 static type Arg1Type[] = { T_PTR, T_VOID, T_END }; /* void* */
490 static type Arg2Type[] = { T_INT, T_END }; /* int */
491 static type Arg3Type[] = { T_SIZE_T, T_END }; /* size_t */
494 ArgDesc Arg1, Arg2, Arg3;
495 int MemSet = 1; /* Use real memset if true */
496 unsigned ParamSize = 0;
499 /* Remember where we are now */
500 Start = GetCodePos ();
503 ParseArg (&Arg1, Arg1Type);
504 g_push (Arg1.Flags, Arg1.Expr.IVal);
505 Arg1.End = GetCodePos ();
506 ParamSize += SizeOf (Arg1Type);
509 /* Argument #2. This argument is special in that we will call another
510 * function if it is a constant zero.
512 ParseArg (&Arg2, Arg2Type);
513 if ((Arg2.Flags & CF_CONST) != 0 && Arg2.Expr.IVal == 0) {
514 /* Don't call memset, call bzero instead */
517 /* Push the argument */
518 g_push (Arg2.Flags, Arg2.Expr.IVal);
519 Arg2.End = GetCodePos ();
520 ParamSize += SizeOf (Arg2Type);
524 /* Argument #3. Since memset is a fastcall function, we must load the
525 * arg into the primary if it is not already there. This parameter is
526 * also ignored for the calculation of the parameter size, since it is
527 * not passed via the stack.
529 ParseArg (&Arg3, Arg3Type);
530 if (Arg3.Flags & CF_CONST) {
531 LoadExpr (CF_NONE, &Arg3.Expr);
534 /* Emit the actual function call. This will also cleanup the stack. */
535 g_call (CF_FIXARGC, MemSet? Func_memset : Func__bzero, ParamSize);
537 if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal == 0) {
539 /* memset has been called with a count argument of zero */
540 Warning ("Call to memset has no effect");
542 /* Remove all of the generated code but the load of the first
543 * argument, which is what memset returns.
545 RemoveCode (Arg1.Push);
547 /* Set the function result to the first argument */
550 /* Bail out, no need for further improvements */
554 /* We've generated the complete code for the function now and know the
555 * types of all parameters. Check for situations where better code can
556 * be generated. If such a situation is detected, throw away the
557 * generated, and emit better code.
558 * Note: Lots of improvements would be possible here, but I will
559 * concentrate on the most common case: memset with arguments 2 and 3
560 * being constant numerical values. Some checks have shown that this
561 * covers nearly 90% of all memset calls.
563 if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
564 ED_IsConstAbsInt (&Arg2.Expr) &&
565 ((ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) ||
566 (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr)))) {
568 int Reg = ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr);
570 /* Drop the generated code */
573 /* We need a label */
574 Label = GetLocalLabel ();
576 /* Generate memset code */
577 if (Arg3.Expr.IVal <= 127) {
579 AddCodeLine ("ldy #$%02X", (unsigned char) (Arg3.Expr.IVal-1));
580 AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
581 g_defcodelabel (Label);
583 AddCodeLine ("sta (%s),y", ED_GetLabelName (&Arg1.Expr, 0));
585 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, 0));
588 AddCodeLine ("bpl %s", LocalLabelName (Label));
592 AddCodeLine ("ldy #$00");
593 AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
594 g_defcodelabel (Label);
596 AddCodeLine ("sta (%s),y", ED_GetLabelName (&Arg1.Expr, 0));
598 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, 0));
601 AddCodeLine ("cpy #$%02X", (unsigned char) Arg3.Expr.IVal);
602 AddCodeLine ("bne %s", LocalLabelName (Label));
606 /* memset returns the address, so the result is actually identical
607 * to the first argument.
611 } else if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
612 ED_IsConstAbsInt (&Arg2.Expr) &&
613 ED_IsRVal (&Arg1.Expr) && ED_IsLocStack (&Arg1.Expr) &&
614 (Arg1.Expr.IVal - StackPtr) + Arg3.Expr.IVal < 256) {
616 /* Calculate the real stack offset */
617 int Offs = ED_GetStackOffs (&Arg1.Expr, 0);
619 /* Drop the generated code */
622 /* We need a label */
623 Label = GetLocalLabel ();
625 /* Generate memset code */
626 AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
627 AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
628 g_defcodelabel (Label);
629 AddCodeLine ("sta (sp),y");
631 AddCodeLine ("cpy #$%02X", (unsigned char) (Offs + Arg3.Expr.IVal));
632 AddCodeLine ("bne %s", LocalLabelName (Label));
634 /* memset returns the address, so the result is actually identical
635 * to the first argument.
639 } else if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
640 ED_IsConstAbsInt (&Arg2.Expr) &&
641 (Arg2.Expr.IVal != 0 || IS_Get (&CodeSizeFactor) > 200)) {
643 /* Remove all of the generated code but the load of the first
646 RemoveCode (Arg1.Push);
648 /* We need a label */
649 Label = GetLocalLabel ();
652 AddCodeLine ("sta ptr1");
653 AddCodeLine ("stx ptr1+1");
654 if (Arg3.Expr.IVal <= 127) {
655 AddCodeLine ("ldy #$%02X", (unsigned char) (Arg3.Expr.IVal-1));
656 AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
657 g_defcodelabel (Label);
658 AddCodeLine ("sta (ptr1),y");
660 AddCodeLine ("bpl %s", LocalLabelName (Label));
662 AddCodeLine ("ldy #$00");
663 AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
664 g_defcodelabel (Label);
665 AddCodeLine ("sta (ptr1),y");
667 AddCodeLine ("cpy #$%02X", (unsigned char) Arg3.Expr.IVal);
668 AddCodeLine ("bne %s", LocalLabelName (Label));
671 /* Load the function result pointer into a/x (x is still valid). This
672 * code will get removed by the optimizer if it is not used later.
674 AddCodeLine ("lda ptr1");
676 /* The function result is an rvalue in the primary register */
677 ED_MakeRValExpr (Expr);
678 Expr->Type = GetFuncReturn (Expr->Type);
682 /* The function result is an rvalue in the primary register */
683 ED_MakeRValExpr (Expr);
684 Expr->Type = GetFuncReturn (Expr->Type);
689 /* We expect the closing brace */
695 /*****************************************************************************/
697 /*****************************************************************************/
701 static void StdFunc_strcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
702 /* Handle the strcpy function */
705 static type Arg1Type[] = { T_PTR, T_CHAR, T_END }; /* char* */
706 static type Arg2Type[] = { T_PTR, T_CHAR|T_QUAL_CONST, T_END }; /* const char* */
710 unsigned ParamSize = 0;
714 /* Setup the argument type string */
715 Arg1Type[1] = GetDefaultChar ();
716 Arg2Type[1] = GetDefaultChar () | T_QUAL_CONST;
718 /* Remember where we are now */
719 Start = GetCodePos ();
722 ParseArg (&Arg1, Arg1Type);
723 g_push (Arg1.Flags, Arg1.Expr.IVal);
724 Arg1.End = GetCodePos ();
725 ParamSize += SizeOf (Arg1Type);
728 /* Argument #2. Since strcpy is a fastcall function, we must load the
729 * arg into the primary if it is not already there. This parameter is
730 * also ignored for the calculation of the parameter size, since it is
731 * not passed via the stack.
733 ParseArg (&Arg2, Arg2Type);
734 if (Arg2.Flags & CF_CONST) {
735 LoadExpr (CF_NONE, &Arg2.Expr);
738 /* Emit the actual function call. This will also cleanup the stack. */
739 g_call (CF_FIXARGC, Func_strcpy, ParamSize);
741 /* Get the element count of argument 2 if it is an array */
742 ECount = ArrayElementCount (&Arg1);
744 /* We've generated the complete code for the function now and know the
745 * types of all parameters. Check for situations where better code can
746 * be generated. If such a situation is detected, throw away the
747 * generated, and emit better code.
749 if (((ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr)) ||
750 (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr))) &&
751 ((ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) ||
752 (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr))) &&
753 (IS_Get (&InlineStdFuncs) ||
754 (ECount != UNSPECIFIED && ECount < 256))) {
758 if (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr)) {
763 if (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr)) {
764 Store = "sta (%s),y";
769 /* Drop the generated code */
773 L1 = GetLocalLabel ();
775 /* Generate strcpy code */
776 AddCodeLine ("ldy #$FF");
779 AddCodeLine (Load, ED_GetLabelName (&Arg2.Expr, 0));
780 AddCodeLine (Store, ED_GetLabelName (&Arg1.Expr, 0));
781 AddCodeLine ("bne %s", LocalLabelName (L1));
783 /* strcpy returns argument #1 */
786 } else if (ED_IsRVal (&Arg2.Expr) && ED_IsLocStack (&Arg2.Expr) &&
788 ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) {
790 /* It is possible to just use one index register even if the stack
791 * offset is not zero, by adjusting the offset to the constant
792 * address accordingly. But we cannot do this if the data in
793 * question is in the register space or at an absolute address less
794 * than 256. Register space is zero page, which means that the
795 * address calculation could overflow in the linker.
797 int AllowOneIndex = !ED_IsLocRegister (&Arg1.Expr) &&
798 !(ED_IsLocAbs (&Arg1.Expr) && Arg1.Expr.IVal < 256);
800 /* Calculate the real stack offset */
801 int Offs = ED_GetStackOffs (&Arg2.Expr, 0);
803 /* Drop the generated code */
807 L1 = GetLocalLabel ();
809 /* Generate strcpy code */
810 AddCodeLine ("ldy #$%02X", (unsigned char) (Offs - 1));
811 if (Offs == 0 || AllowOneIndex) {
814 AddCodeLine ("lda (sp),y");
815 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, -Offs));
817 AddCodeLine ("ldx #$FF");
821 AddCodeLine ("lda (sp),y");
822 AddCodeLine ("sta %s,x", ED_GetLabelName (&Arg1.Expr, 0));
824 AddCodeLine ("bne %s", LocalLabelName (L1));
826 /* strcpy returns argument #1 */
829 } else if (ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr) &&
830 ED_IsRVal (&Arg1.Expr) && ED_IsLocStack (&Arg1.Expr) &&
833 /* It is possible to just use one index register even if the stack
834 * offset is not zero, by adjusting the offset to the constant
835 * address accordingly. But we cannot do this if the data in
836 * question is in the register space or at an absolute address less
837 * than 256. Register space is zero page, which means that the
838 * address calculation could overflow in the linker.
840 int AllowOneIndex = !ED_IsLocRegister (&Arg2.Expr) &&
841 !(ED_IsLocAbs (&Arg2.Expr) && Arg2.Expr.IVal < 256);
843 /* Calculate the real stack offset */
844 int Offs = ED_GetStackOffs (&Arg1.Expr, 0);
846 /* Drop the generated code */
850 L1 = GetLocalLabel ();
852 /* Generate strcpy code */
853 AddCodeLine ("ldy #$%02X", (unsigned char) (Offs - 1));
854 if (Offs == 0 || AllowOneIndex) {
857 AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg2.Expr, -Offs));
858 AddCodeLine ("sta (sp),y");
860 AddCodeLine ("ldx #$FF");
864 AddCodeLine ("lda %s,x", ED_GetLabelName (&Arg2.Expr, 0));
865 AddCodeLine ("sta (sp),y");
867 AddCodeLine ("bne %s", LocalLabelName (L1));
869 /* strcpy returns argument #1 */
874 /* The function result is an rvalue in the primary register */
875 ED_MakeRValExpr (Expr);
876 Expr->Type = GetFuncReturn (Expr->Type);
880 /* We expect the closing brace */
886 /*****************************************************************************/
888 /*****************************************************************************/
892 static void StdFunc_strlen (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
893 /* Handle the strlen function */
895 static type ArgType[] = { T_PTR, T_SCHAR, T_END };
905 /* Setup the argument type string */
906 ArgType[1] = GetDefaultChar () | T_QUAL_CONST;
908 /* Evaluate the parameter */
911 /* Check if the argument is an array. If so, remember the element count.
912 * Otherwise set the element count to undefined.
914 IsArray = IsTypeArray (Arg.Type);
916 ECount = GetElementCount (Arg.Type);
917 if (ECount == FLEXIBLE) {
918 /* Treat as unknown */
919 ECount = UNSPECIFIED;
923 ECount = UNSPECIFIED;
924 IsPtr = IsTypePtr (Arg.Type);
927 /* Check if the elements of an array can be addressed by a byte sized
928 * index. This is true if the size of the array is known and less than
931 IsByteIndex = (ECount != UNSPECIFIED && ECount < 256);
933 /* Do type conversion */
934 TypeConversion (&Arg, ArgType);
936 /* If the expression is a literal, and if string literals are read
937 * only, we can calculate the length of the string and remove it
938 * from the literal pool. Otherwise we have to calculate the length
941 if (ED_IsLocLiteral (&Arg) && IS_Get (&WritableStrings) == 0) {
943 /* Constant string literal */
944 ED_MakeConstAbs (Expr, strlen (GetLiteral (Arg.IVal)), type_size_t);
945 ResetLiteralPoolOffs (Arg.IVal);
947 /* We will inline strlen for arrays with constant addresses, if either the
948 * inlining was forced on the command line, or the array is smaller than
949 * 256, so the inlining is considered safe.
951 } else if (ED_IsLocConst (&Arg) && IsArray &&
952 (IS_Get (&InlineStdFuncs) || IsByteIndex)) {
954 /* Generate the strlen code */
955 L = GetLocalLabel ();
956 AddCodeLine ("ldy #$FF");
959 AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg, 0));
960 AddCodeLine ("bne %s", LocalLabelName (L));
964 /* The function result is an rvalue in the primary register */
965 ED_MakeRValExpr (Expr);
966 Expr->Type = type_size_t;
968 /* We will inline strlen for arrays on the stack, if the array is
969 * completely within the reach of a byte sized index register.
971 } else if (ED_IsLocStack (&Arg) && IsArray && IsByteIndex &&
972 (Arg.IVal - StackPtr) + ECount < 256) {
974 /* Calculate the true stack offset */
975 int Offs = ED_GetStackOffs (&Arg, 0);
977 /* Generate the strlen code */
978 L = GetLocalLabel ();
979 AddCodeLine ("ldx #$FF");
980 AddCodeLine ("ldy #$%02X", (unsigned char) (Offs-1));
984 AddCodeLine ("lda (sp),y");
985 AddCodeLine ("bne %s", LocalLabelName (L));
987 AddCodeLine ("ldx #$00");
989 /* The function result is an rvalue in the primary register */
990 ED_MakeRValExpr (Expr);
991 Expr->Type = type_size_t;
993 /* strlen for a string that is pointed to by a register variable will only
994 * get inlined if requested on the command line, since we cannot know how
995 * big the buffer actually is, so inlining is not always safe.
997 } else if (ED_IsLocRegister (&Arg) && ED_IsLVal (&Arg) && IsPtr &&
998 IS_Get (&InlineStdFuncs)) {
1000 /* Generate the strlen code */
1001 L = GetLocalLabel ();
1002 AddCodeLine ("ldy #$FF");
1004 AddCodeLine ("iny");
1005 AddCodeLine ("lda (%s),y", ED_GetLabelName (&Arg, 0));
1006 AddCodeLine ("bne %s", LocalLabelName (L));
1007 AddCodeLine ("tax");
1008 AddCodeLine ("tya");
1010 /* The function result is an rvalue in the primary register */
1011 ED_MakeRValExpr (Expr);
1012 Expr->Type = type_size_t;
1014 /* Last check: We will inline a generic strlen routine if inlining was
1015 * requested on the command line, and the code size factor is more than
1016 * 400 (code is 13 bytes vs. 3 for a jsr call).
1018 } else if (IS_Get (&CodeSizeFactor) > 400 && IS_Get (&InlineStdFuncs)) {
1020 /* Load the expression into the primary */
1021 LoadExpr (CF_NONE, &Arg);
1023 /* Inline the function */
1024 L = GetLocalLabel ();
1025 AddCodeLine ("sta ptr1");
1026 AddCodeLine ("stx ptr1+1");
1027 AddCodeLine ("ldy #$FF");
1029 AddCodeLine ("iny");
1030 AddCodeLine ("lda (ptr1),y");
1031 AddCodeLine ("bne %s", LocalLabelName (L));
1032 AddCodeLine ("tax");
1033 AddCodeLine ("tya");
1035 /* The function result is an rvalue in the primary register */
1036 ED_MakeRValExpr (Expr);
1037 Expr->Type = type_size_t;
1041 /* Load the expression into the primary */
1042 LoadExpr (CF_NONE, &Arg);
1044 /* Call the strlen function */
1045 AddCodeLine ("jsr _%s", Func_strlen);
1047 /* The function result is an rvalue in the primary register */
1048 ED_MakeRValExpr (Expr);
1049 Expr->Type = type_size_t;
1053 /* We expect the closing brace */
1059 /*****************************************************************************/
1061 /*****************************************************************************/
1065 int FindStdFunc (const char* Name)
1066 /* Determine if the given function is a known standard function that may be
1067 * called in a special way. If so, return the index, otherwise return -1.
1070 /* Look into the table for known names */
1071 struct StdFuncDesc* D =
1072 bsearch (Name, StdFuncs, FUNC_COUNT, sizeof (StdFuncs[0]), CmpFunc);
1074 /* Return the function index or -1 */
1078 return D - StdFuncs;
1084 void HandleStdFunc (int Index, FuncDesc* F, ExprDesc* lval)
1085 /* Generate code for a known standard function. */
1087 struct StdFuncDesc* D;
1089 /* Get a pointer to the table entry */
1090 CHECK (Index >= 0 && Index < (int)FUNC_COUNT);
1091 D = StdFuncs + Index;
1093 /* Call the handler function */
1094 D->Handler (F, lval);