1 /*****************************************************************************/
5 /* Handle inlining of known functions for the cc65 compiler */
9 /* (C) 1998-2010 Ullrich von Bassewitz */
10 /* Roemerstrasse 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_strcmp (FuncDesc*, ExprDesc*);
69 static void StdFunc_strcpy (FuncDesc*, ExprDesc*);
70 static void StdFunc_strlen (FuncDesc*, ExprDesc*);
74 /*****************************************************************************/
76 /*****************************************************************************/
80 /* Table with all known functions and their handlers. Must be sorted
83 static struct StdFuncDesc {
85 void (*Handler) (FuncDesc*, ExprDesc*);
87 { "memcpy", StdFunc_memcpy },
88 { "memset", StdFunc_memset },
89 { "strcmp", StdFunc_strcmp },
90 { "strcpy", StdFunc_strcpy },
91 { "strlen", StdFunc_strlen },
94 #define FUNC_COUNT (sizeof (StdFuncs) / sizeof (StdFuncs[0]))
96 typedef struct ArgDesc 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 */
109 /*****************************************************************************/
110 /* Helper functions */
111 /*****************************************************************************/
115 static int CmpFunc (const void* Key, const void* Elem)
116 /* Compare function for bsearch */
118 return strcmp ((const char*) Key, ((const struct StdFuncDesc*) Elem)->Name);
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.
130 if (IsTypeArray (Arg->Type)) {
131 Count = GetElementCount (Arg->Type);
132 if (Count == FLEXIBLE) {
133 /* Treat as unknown */
144 static void ParseArg (ArgDesc* Arg, Type* Type)
145 /* Parse one argument but do not push it onto the stack. Make all fields in
149 /* We have a prototype, so chars may be pushed as chars */
150 Arg->Flags = CF_FORCECHAR;
152 /* Remember the required argument type */
155 /* Read the expression we're going to pass to the function */
156 MarkedExprWithCheck (hie1, &Arg->Expr);
158 /* Remember the actual argument type */
159 Arg->Type = Arg->Expr.Type;
161 /* Convert this expression to the expected type */
162 TypeConversion (&Arg->Expr, Type);
164 /* Remember the following code position */
165 GetCodePos (&Arg->Load);
167 /* If the value is a constant, set the flag, otherwise load it into the
170 if (ED_IsConstAbsInt (&Arg->Expr) && ED_CodeRangeIsEmpty (&Arg->Expr)) {
171 /* Remember that we have a constant value */
172 Arg->Flags |= CF_CONST;
174 /* Load into the primary */
175 LoadExpr (CF_NONE, &Arg->Expr);
178 /* Remember the following code position */
179 GetCodePos (&Arg->Push);
180 GetCodePos (&Arg->End);
182 /* Use the type of the argument for the push */
183 Arg->Flags |= TypeOf (Arg->Expr.Type);
188 /*****************************************************************************/
190 /*****************************************************************************/
194 static void StdFunc_memcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
195 /* Handle the memcpy function */
197 /* Argument types: (void*, const void*, size_t) */
198 static Type Arg1Type[] = { TYPE(T_PTR), TYPE(T_VOID), TYPE(T_END) };
199 static Type Arg2Type[] = { TYPE(T_PTR), TYPE(T_VOID|T_QUAL_CONST), TYPE(T_END) };
200 static Type Arg3Type[] = { TYPE(T_SIZE_T), TYPE(T_END) };
202 ArgDesc Arg1, Arg2, Arg3;
203 unsigned ParamSize = 0;
207 ParseArg (&Arg1, Arg1Type);
208 g_push (Arg1.Flags, Arg1.Expr.IVal);
209 GetCodePos (&Arg1.End);
210 ParamSize += SizeOf (Arg1Type);
214 ParseArg (&Arg2, Arg2Type);
215 g_push (Arg2.Flags, Arg2.Expr.IVal);
216 GetCodePos (&Arg2.End);
217 ParamSize += SizeOf (Arg2Type);
220 /* Argument #3. Since memcpy is a fastcall function, we must load the
221 * arg into the primary if it is not already there. This parameter is
222 * also ignored for the calculation of the parameter size, since it is
223 * not passed via the stack.
225 ParseArg (&Arg3, Arg3Type);
226 if (Arg3.Flags & CF_CONST) {
227 LoadExpr (CF_NONE, &Arg3.Expr);
230 /* Emit the actual function call. This will also cleanup the stack. */
231 g_call (CF_FIXARGC, Func_memcpy, ParamSize);
233 if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal == 0) {
235 /* memcpy has been called with a count argument of zero */
236 Warning ("Call to memcpy has no effect");
238 /* Remove all of the generated code but the load of the first
239 * argument, which is what memcpy returns.
241 RemoveCode (&Arg1.Push);
243 /* Set the function result to the first argument */
246 /* Bail out, no need for further improvements */
250 /* We've generated the complete code for the function now and know the
251 * types of all parameters. Check for situations where better code can
252 * be generated. If such a situation is detected, throw away the
253 * generated, and emit better code.
255 if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
256 ((ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr)) ||
257 (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr))) &&
258 ((ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) ||
259 (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr)))) {
261 int Reg1 = ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr);
262 int Reg2 = ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr);
264 /* Drop the generated code */
265 RemoveCode (&Arg1.Expr.Start);
267 /* We need a label */
268 Label = GetLocalLabel ();
270 /* Generate memcpy code */
271 if (Arg3.Expr.IVal <= 127) {
273 AddCodeLine ("ldy #$%02X", (unsigned char) (Arg3.Expr.IVal-1));
274 AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
275 g_defcodelabel (Label);
277 AddCodeLine ("lda (%s),y", ED_GetLabelName (&Arg2.Expr, 0));
279 AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg2.Expr, 0));
282 AddCodeLine ("sta (%s),y", ED_GetLabelName (&Arg1.Expr, 0));
284 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, 0));
287 AddCodeLine ("bpl %s", LocalLabelName (Label));
291 AddCodeLine ("ldy #$00");
292 AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
293 g_defcodelabel (Label);
295 AddCodeLine ("lda (%s),y", ED_GetLabelName (&Arg2.Expr, 0));
297 AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg2.Expr, 0));
300 AddCodeLine ("sta (%s),y", ED_GetLabelName (&Arg1.Expr, 0));
302 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, 0));
305 AddCodeLine ("cpy #$%02X", (unsigned char) Arg3.Expr.IVal);
306 AddCodeLine ("bne %s", LocalLabelName (Label));
310 /* memcpy returns the address, so the result is actually identical
311 * to the first argument.
315 } else if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
316 ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr) &&
317 ED_IsRVal (&Arg1.Expr) && ED_IsLocStack (&Arg1.Expr) &&
318 (Arg1.Expr.IVal - StackPtr) + Arg3.Expr.IVal < 256) {
320 /* It is possible to just use one index register even if the stack
321 * offset is not zero, by adjusting the offset to the constant
322 * address accordingly. But we cannot do this if the data in
323 * question is in the register space or at an absolute address less
324 * than 256. Register space is zero page, which means that the
325 * address calculation could overflow in the linker.
327 int AllowOneIndex = !ED_IsLocRegister (&Arg2.Expr) &&
328 !(ED_IsLocAbs (&Arg2.Expr) && Arg2.Expr.IVal < 256);
330 /* Calculate the real stack offset */
331 int Offs = ED_GetStackOffs (&Arg1.Expr, 0);
333 /* Drop the generated code */
334 RemoveCode (&Arg1.Expr.Start);
336 /* We need a label */
337 Label = GetLocalLabel ();
339 /* Generate memcpy code */
340 if (Arg3.Expr.IVal <= 127 && !AllowOneIndex) {
343 AddCodeLine ("ldy #$%02X", (unsigned char) (Offs + Arg3.Expr.IVal - 1));
344 g_defcodelabel (Label);
345 AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg2.Expr, -Offs));
346 AddCodeLine ("sta (sp),y");
348 AddCodeLine ("bpl %s", LocalLabelName (Label));
350 AddCodeLine ("ldx #$%02X", (unsigned char) (Arg3.Expr.IVal-1));
351 AddCodeLine ("ldy #$%02X", (unsigned char) (Offs + Arg3.Expr.IVal - 1));
352 g_defcodelabel (Label);
353 AddCodeLine ("lda %s,x", ED_GetLabelName (&Arg2.Expr, 0));
354 AddCodeLine ("sta (sp),y");
357 AddCodeLine ("bpl %s", LocalLabelName (Label));
362 if (Offs == 0 || AllowOneIndex) {
363 AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
364 g_defcodelabel (Label);
365 AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg2.Expr, -Offs));
366 AddCodeLine ("sta (sp),y");
368 AddCodeLine ("cpy #$%02X", (unsigned char) (Offs + Arg3.Expr.IVal));
369 AddCodeLine ("bne %s", LocalLabelName (Label));
371 AddCodeLine ("ldx #$00");
372 AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
373 g_defcodelabel (Label);
374 AddCodeLine ("lda %s,x", ED_GetLabelName (&Arg2.Expr, 0));
375 AddCodeLine ("sta (sp),y");
378 AddCodeLine ("cpx #$%02X", (unsigned char) Arg3.Expr.IVal);
379 AddCodeLine ("bne %s", LocalLabelName (Label));
384 /* memcpy returns the address, so the result is actually identical
385 * to the first argument.
389 } else if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
390 ED_IsRVal (&Arg2.Expr) && ED_IsLocStack (&Arg2.Expr) &&
391 (Arg2.Expr.IVal - StackPtr) + Arg3.Expr.IVal < 256 &&
392 ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) {
394 /* It is possible to just use one index register even if the stack
395 * offset is not zero, by adjusting the offset to the constant
396 * address accordingly. But we cannot do this if the data in
397 * question is in the register space or at an absolute address less
398 * than 256. Register space is zero page, which means that the
399 * address calculation could overflow in the linker.
401 int AllowOneIndex = !ED_IsLocRegister (&Arg1.Expr) &&
402 !(ED_IsLocAbs (&Arg1.Expr) && Arg1.Expr.IVal < 256);
404 /* Calculate the real stack offset */
405 int Offs = ED_GetStackOffs (&Arg2.Expr, 0);
407 /* Drop the generated code */
408 RemoveCode (&Arg1.Expr.Start);
410 /* We need a label */
411 Label = GetLocalLabel ();
413 /* Generate memcpy code */
414 if (Arg3.Expr.IVal <= 127 && !AllowOneIndex) {
417 AddCodeLine ("ldy #$%02X", (unsigned char) (Arg3.Expr.IVal - 1));
418 g_defcodelabel (Label);
419 AddCodeLine ("lda (sp),y");
420 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, 0));
422 AddCodeLine ("bpl %s", LocalLabelName (Label));
424 AddCodeLine ("ldx #$%02X", (unsigned char) (Arg3.Expr.IVal-1));
425 AddCodeLine ("ldy #$%02X", (unsigned char) (Offs + Arg3.Expr.IVal - 1));
426 g_defcodelabel (Label);
427 AddCodeLine ("lda (sp),y");
428 AddCodeLine ("sta %s,x", ED_GetLabelName (&Arg1.Expr, 0));
431 AddCodeLine ("bpl %s", LocalLabelName (Label));
436 if (Offs == 0 || AllowOneIndex) {
437 AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
438 g_defcodelabel (Label);
439 AddCodeLine ("lda (sp),y");
440 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, -Offs));
442 AddCodeLine ("cpy #$%02X", (unsigned char) (Offs + Arg3.Expr.IVal));
443 AddCodeLine ("bne %s", LocalLabelName (Label));
445 AddCodeLine ("ldx #$00");
446 AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
447 g_defcodelabel (Label);
448 AddCodeLine ("lda (sp),y");
449 AddCodeLine ("sta %s,x", ED_GetLabelName (&Arg1.Expr, 0));
452 AddCodeLine ("cpx #$%02X", (unsigned char) Arg3.Expr.IVal);
453 AddCodeLine ("bne %s", LocalLabelName (Label));
458 /* memcpy returns the address, so the result is actually identical
459 * to the first argument.
465 /* The function result is an rvalue in the primary register */
466 ED_MakeRValExpr (Expr);
467 Expr->Type = GetFuncReturn (Expr->Type);
472 /* We expect the closing brace */
478 /*****************************************************************************/
480 /*****************************************************************************/
484 static void StdFunc_memset (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
485 /* Handle the memset function */
487 /* Argument types: (void*, int, size_t) */
488 static Type Arg1Type[] = { TYPE(T_PTR), TYPE(T_VOID), TYPE(T_END) };
489 static Type Arg2Type[] = { TYPE(T_INT), TYPE(T_END) };
490 static Type Arg3Type[] = { TYPE(T_SIZE_T), TYPE(T_END) };
492 ArgDesc Arg1, Arg2, Arg3;
493 int MemSet = 1; /* Use real memset if true */
494 unsigned ParamSize = 0;
498 ParseArg (&Arg1, Arg1Type);
499 g_push (Arg1.Flags, Arg1.Expr.IVal);
500 GetCodePos (&Arg1.End);
501 ParamSize += SizeOf (Arg1Type);
504 /* Argument #2. This argument is special in that we will call another
505 * function if it is a constant zero.
507 ParseArg (&Arg2, Arg2Type);
508 if ((Arg2.Flags & CF_CONST) != 0 && Arg2.Expr.IVal == 0) {
509 /* Don't call memset, call bzero instead */
512 /* Push the argument */
513 g_push (Arg2.Flags, Arg2.Expr.IVal);
514 GetCodePos (&Arg2.End);
515 ParamSize += SizeOf (Arg2Type);
519 /* Argument #3. Since memset is a fastcall function, we must load the
520 * arg into the primary if it is not already there. This parameter is
521 * also ignored for the calculation of the parameter size, since it is
522 * not passed via the stack.
524 ParseArg (&Arg3, Arg3Type);
525 if (Arg3.Flags & CF_CONST) {
526 LoadExpr (CF_NONE, &Arg3.Expr);
529 /* Emit the actual function call. This will also cleanup the stack. */
530 g_call (CF_FIXARGC, MemSet? Func_memset : Func__bzero, ParamSize);
532 if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal == 0) {
534 /* memset has been called with a count argument of zero */
535 Warning ("Call to memset has no effect");
537 /* Remove all of the generated code but the load of the first
538 * argument, which is what memset returns.
540 RemoveCode (&Arg1.Push);
542 /* Set the function result to the first argument */
545 /* Bail out, no need for further improvements */
549 /* We've generated the complete code for the function now and know the
550 * types of all parameters. Check for situations where better code can
551 * be generated. If such a situation is detected, throw away the
552 * generated, and emit better code.
553 * Note: Lots of improvements would be possible here, but I will
554 * concentrate on the most common case: memset with arguments 2 and 3
555 * being constant numerical values. Some checks have shown that this
556 * covers nearly 90% of all memset calls.
558 if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
559 ED_IsConstAbsInt (&Arg2.Expr) &&
560 ((ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) ||
561 (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr)))) {
563 int Reg = ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr);
565 /* Drop the generated code */
566 RemoveCode (&Arg1.Expr.Start);
568 /* We need a label */
569 Label = GetLocalLabel ();
571 /* Generate memset code */
572 if (Arg3.Expr.IVal <= 127) {
574 AddCodeLine ("ldy #$%02X", (unsigned char) (Arg3.Expr.IVal-1));
575 AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
576 g_defcodelabel (Label);
578 AddCodeLine ("sta (%s),y", ED_GetLabelName (&Arg1.Expr, 0));
580 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, 0));
583 AddCodeLine ("bpl %s", LocalLabelName (Label));
587 AddCodeLine ("ldy #$00");
588 AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
589 g_defcodelabel (Label);
591 AddCodeLine ("sta (%s),y", ED_GetLabelName (&Arg1.Expr, 0));
593 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, 0));
596 AddCodeLine ("cpy #$%02X", (unsigned char) Arg3.Expr.IVal);
597 AddCodeLine ("bne %s", LocalLabelName (Label));
601 /* memset returns the address, so the result is actually identical
602 * to the first argument.
606 } else if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
607 ED_IsConstAbsInt (&Arg2.Expr) &&
608 ED_IsRVal (&Arg1.Expr) && ED_IsLocStack (&Arg1.Expr) &&
609 (Arg1.Expr.IVal - StackPtr) + Arg3.Expr.IVal < 256) {
611 /* Calculate the real stack offset */
612 int Offs = ED_GetStackOffs (&Arg1.Expr, 0);
614 /* Drop the generated code */
615 RemoveCode (&Arg1.Expr.Start);
617 /* We need a label */
618 Label = GetLocalLabel ();
620 /* Generate memset code */
621 AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
622 AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
623 g_defcodelabel (Label);
624 AddCodeLine ("sta (sp),y");
626 AddCodeLine ("cpy #$%02X", (unsigned char) (Offs + Arg3.Expr.IVal));
627 AddCodeLine ("bne %s", LocalLabelName (Label));
629 /* memset returns the address, so the result is actually identical
630 * to the first argument.
634 } else if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
635 ED_IsConstAbsInt (&Arg2.Expr) &&
636 (Arg2.Expr.IVal != 0 || IS_Get (&CodeSizeFactor) > 200)) {
638 /* Remove all of the generated code but the load of the first
641 RemoveCode (&Arg1.Push);
643 /* We need a label */
644 Label = GetLocalLabel ();
647 AddCodeLine ("sta ptr1");
648 AddCodeLine ("stx ptr1+1");
649 if (Arg3.Expr.IVal <= 127) {
650 AddCodeLine ("ldy #$%02X", (unsigned char) (Arg3.Expr.IVal-1));
651 AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
652 g_defcodelabel (Label);
653 AddCodeLine ("sta (ptr1),y");
655 AddCodeLine ("bpl %s", LocalLabelName (Label));
657 AddCodeLine ("ldy #$00");
658 AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
659 g_defcodelabel (Label);
660 AddCodeLine ("sta (ptr1),y");
662 AddCodeLine ("cpy #$%02X", (unsigned char) Arg3.Expr.IVal);
663 AddCodeLine ("bne %s", LocalLabelName (Label));
666 /* Load the function result pointer into a/x (x is still valid). This
667 * code will get removed by the optimizer if it is not used later.
669 AddCodeLine ("lda ptr1");
671 /* The function result is an rvalue in the primary register */
672 ED_MakeRValExpr (Expr);
673 Expr->Type = GetFuncReturn (Expr->Type);
677 /* The function result is an rvalue in the primary register */
678 ED_MakeRValExpr (Expr);
679 Expr->Type = GetFuncReturn (Expr->Type);
684 /* We expect the closing brace */
690 /*****************************************************************************/
692 /*****************************************************************************/
696 static void StdFunc_strcmp (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
697 /* Handle the strcmp function */
699 /* Argument types: (const char*, const char*) */
700 static Type Arg1Type[] = { TYPE(T_PTR), TYPE(T_CHAR|T_QUAL_CONST), TYPE(T_END) };
701 static Type Arg2Type[] = { TYPE(T_PTR), TYPE(T_CHAR|T_QUAL_CONST), TYPE(T_END) };
704 unsigned ParamSize = 0;
710 /* Setup the argument type string */
711 Arg1Type[1].C = GetDefaultChar () | T_QUAL_CONST;
712 Arg2Type[1].C = GetDefaultChar () | T_QUAL_CONST;
715 ParseArg (&Arg1, Arg1Type);
716 g_push (Arg1.Flags, Arg1.Expr.IVal);
717 ParamSize += SizeOf (Arg1Type);
721 ParseArg (&Arg2, Arg2Type);
723 /* Since strcmp is a fastcall function, we must load the
724 * arg into the primary if it is not already there. This parameter is
725 * also ignored for the calculation of the parameter size, since it is
726 * not passed via the stack.
728 if (Arg2.Flags & CF_CONST) {
729 LoadExpr (CF_NONE, &Arg2.Expr);
732 /* Emit the actual function call. This will also cleanup the stack. */
733 g_call (CF_FIXARGC, Func_strcmp, ParamSize);
735 /* Get the element counts of the arguments. Then get the larger of the
736 * two into ECount1. This removes FLEXIBLE and UNSPECIFIED automatically
738 ECount1 = ArrayElementCount (&Arg1);
739 ECount2 = ArrayElementCount (&Arg2);
740 if (ECount2 > ECount1) {
744 /* If the second argument is the empty string literal, we can generate
745 * more efficient code.
747 if (ED_IsLocLiteral (&Arg2.Expr) &&
748 IS_Get (&WritableStrings) == 0 &&
749 GetLiteralSize (Arg2.Expr.LVal) == 1 &&
750 GetLiteralStr (Arg2.Expr.LVal)[0] == '\0') {
752 /* Drop the generated code so we have the first argument in the
755 RemoveCode (&Arg1.Push);
757 /* We don't need the literal any longer */
758 ReleaseLiteral (Arg2.Expr.LVal);
760 /* We do now have Arg1 in the primary. Load the first character from
761 * this string and cast to int. This is the function result.
763 IsArray = IsTypeArray (Arg1.Type) && ED_IsRVal (&Arg1.Expr);
764 if (IsArray && ED_IsLocStack (&Arg1.Expr) &&
765 (Offs = ED_GetStackOffs (&Arg1.Expr, 0) < 256)) {
766 /* Drop the generated code */
767 RemoveCode (&Arg1.Load);
770 AddCodeLine ("ldy #$%02X", Offs);
771 AddCodeLine ("ldx #$00");
772 AddCodeLine ("lda (sp),y");
773 } else if (IsArray && ED_IsLocConst (&Arg1.Expr)) {
774 /* Drop the generated code */
775 RemoveCode (&Arg1.Load);
778 AddCodeLine ("ldx #$00");
779 AddCodeLine ("lda %s", ED_GetLabelName (&Arg1.Expr, 0));
781 /* Drop part of the generated code so we have the first argument
784 RemoveCode (&Arg1.Push);
786 /* Fetch the first char */
787 g_getind (CF_CHAR | CF_UNSIGNED, 0);
790 } else if ((IS_Get (&CodeSizeFactor) >= 165) &&
791 ((ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr)) ||
792 (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr))) &&
793 ((ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) ||
794 (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr))) &&
795 (IS_Get (&InlineStdFuncs) || (ECount1 > 0 && ECount1 < 256))) {
798 unsigned Entry, Loop, Fin; /* Labels */
802 if (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr)) {
807 if (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr)) {
808 Compare = "cmp (%s),y";
810 Compare = "cmp %s,y";
813 /* Drop the generated code */
814 RemoveCode (&Arg1.Expr.Start);
817 Entry = GetLocalLabel ();
818 Loop = GetLocalLabel ();
819 Fin = GetLocalLabel ();
821 /* Generate strcmp code */
822 AddCodeLine ("ldy #$00");
823 AddCodeLine ("beq %s", LocalLabelName (Entry));
824 g_defcodelabel (Loop);
826 AddCodeLine ("beq %s", LocalLabelName (Fin));
828 g_defcodelabel (Entry);
829 AddCodeLine (Load, ED_GetLabelName (&Arg1.Expr, 0));
830 AddCodeLine (Compare, ED_GetLabelName (&Arg2.Expr, 0));
831 AddCodeLine ("beq %s", LocalLabelName (Loop));
832 AddCodeLine ("ldx #$01");
833 AddCodeLine ("bcs %s", LocalLabelName (Fin));
834 AddCodeLine ("ldx #$FF");
835 g_defcodelabel (Fin);
837 } else if ((IS_Get (&CodeSizeFactor) > 190) &&
838 ((ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr)) ||
839 (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr))) &&
840 (IS_Get (&InlineStdFuncs) || (ECount1 > 0 && ECount1 < 256))) {
843 unsigned Entry, Loop, Fin; /* Labels */
846 if (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr)) {
847 Compare = "cmp (%s),y";
849 Compare = "cmp %s,y";
852 /* Drop the generated code */
853 RemoveCode (&Arg1.Push);
856 Entry = GetLocalLabel ();
857 Loop = GetLocalLabel ();
858 Fin = GetLocalLabel ();
860 /* Store Arg1 into ptr1 */
861 AddCodeLine ("sta ptr1");
862 AddCodeLine ("stx ptr1+1");
864 /* Generate strcmp code */
865 AddCodeLine ("ldy #$00");
866 AddCodeLine ("beq %s", LocalLabelName (Entry));
867 g_defcodelabel (Loop);
869 AddCodeLine ("beq %s", LocalLabelName (Fin));
871 g_defcodelabel (Entry);
872 AddCodeLine ("lda (ptr1),y");
873 AddCodeLine (Compare, ED_GetLabelName (&Arg2.Expr, 0));
874 AddCodeLine ("beq %s", LocalLabelName (Loop));
875 AddCodeLine ("ldx #$01");
876 AddCodeLine ("bcs %s", LocalLabelName (Fin));
877 AddCodeLine ("ldx #$FF");
878 g_defcodelabel (Fin);
882 /* The function result is an rvalue in the primary register */
883 ED_MakeRValExpr (Expr);
884 Expr->Type = GetFuncReturn (Expr->Type);
886 /* We expect the closing brace */
892 /*****************************************************************************/
894 /*****************************************************************************/
898 static void StdFunc_strcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
899 /* Handle the strcpy function */
901 /* Argument types: (char*, const char*) */
902 static Type Arg1Type[] = { TYPE(T_PTR), TYPE(T_CHAR), TYPE(T_END) };
903 static Type Arg2Type[] = { TYPE(T_PTR), TYPE(T_CHAR|T_QUAL_CONST), TYPE(T_END) };
906 unsigned ParamSize = 0;
910 /* Setup the argument type string */
911 Arg1Type[1].C = GetDefaultChar ();
912 Arg2Type[1].C = GetDefaultChar () | T_QUAL_CONST;
915 ParseArg (&Arg1, Arg1Type);
916 g_push (Arg1.Flags, Arg1.Expr.IVal);
917 GetCodePos (&Arg1.End);
918 ParamSize += SizeOf (Arg1Type);
921 /* Argument #2. Since strcpy is a fastcall function, we must load the
922 * arg into the primary if it is not already there. This parameter is
923 * also ignored for the calculation of the parameter size, since it is
924 * not passed via the stack.
926 ParseArg (&Arg2, Arg2Type);
927 if (Arg2.Flags & CF_CONST) {
928 LoadExpr (CF_NONE, &Arg2.Expr);
931 /* Emit the actual function call. This will also cleanup the stack. */
932 g_call (CF_FIXARGC, Func_strcpy, ParamSize);
934 /* Get the element count of argument 1 if it is an array */
935 ECount = ArrayElementCount (&Arg1);
937 /* We've generated the complete code for the function now and know the
938 * types of all parameters. Check for situations where better code can
939 * be generated. If such a situation is detected, throw away the
940 * generated, and emit better code.
942 if (((ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr)) ||
943 (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr))) &&
944 ((ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) ||
945 (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr))) &&
946 (IS_Get (&InlineStdFuncs) ||
947 (ECount != UNSPECIFIED && ECount < 256))) {
951 if (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr)) {
956 if (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr)) {
957 Store = "sta (%s),y";
962 /* Drop the generated code */
963 RemoveCode (&Arg1.Expr.Start);
966 L1 = GetLocalLabel ();
968 /* Generate strcpy code */
969 AddCodeLine ("ldy #$FF");
972 AddCodeLine (Load, ED_GetLabelName (&Arg2.Expr, 0));
973 AddCodeLine (Store, ED_GetLabelName (&Arg1.Expr, 0));
974 AddCodeLine ("bne %s", LocalLabelName (L1));
976 /* strcpy returns argument #1 */
979 } else if (ED_IsRVal (&Arg2.Expr) && ED_IsLocStack (&Arg2.Expr) &&
981 ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) {
983 /* It is possible to just use one index register even if the stack
984 * offset is not zero, by adjusting the offset to the constant
985 * address accordingly. But we cannot do this if the data in
986 * question is in the register space or at an absolute address less
987 * than 256. Register space is zero page, which means that the
988 * address calculation could overflow in the linker.
990 int AllowOneIndex = !ED_IsLocRegister (&Arg1.Expr) &&
991 !(ED_IsLocAbs (&Arg1.Expr) && Arg1.Expr.IVal < 256);
993 /* Calculate the real stack offset */
994 int Offs = ED_GetStackOffs (&Arg2.Expr, 0);
996 /* Drop the generated code */
997 RemoveCode (&Arg1.Expr.Start);
1000 L1 = GetLocalLabel ();
1002 /* Generate strcpy code */
1003 AddCodeLine ("ldy #$%02X", (unsigned char) (Offs - 1));
1004 if (Offs == 0 || AllowOneIndex) {
1005 g_defcodelabel (L1);
1006 AddCodeLine ("iny");
1007 AddCodeLine ("lda (sp),y");
1008 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, -Offs));
1010 AddCodeLine ("ldx #$FF");
1011 g_defcodelabel (L1);
1012 AddCodeLine ("iny");
1013 AddCodeLine ("inx");
1014 AddCodeLine ("lda (sp),y");
1015 AddCodeLine ("sta %s,x", ED_GetLabelName (&Arg1.Expr, 0));
1017 AddCodeLine ("bne %s", LocalLabelName (L1));
1019 /* strcpy returns argument #1 */
1022 } else if (ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr) &&
1023 ED_IsRVal (&Arg1.Expr) && ED_IsLocStack (&Arg1.Expr) &&
1026 /* It is possible to just use one index register even if the stack
1027 * offset is not zero, by adjusting the offset to the constant
1028 * address accordingly. But we cannot do this if the data in
1029 * question is in the register space or at an absolute address less
1030 * than 256. Register space is zero page, which means that the
1031 * address calculation could overflow in the linker.
1033 int AllowOneIndex = !ED_IsLocRegister (&Arg2.Expr) &&
1034 !(ED_IsLocAbs (&Arg2.Expr) && Arg2.Expr.IVal < 256);
1036 /* Calculate the real stack offset */
1037 int Offs = ED_GetStackOffs (&Arg1.Expr, 0);
1039 /* Drop the generated code */
1040 RemoveCode (&Arg1.Expr.Start);
1042 /* We need labels */
1043 L1 = GetLocalLabel ();
1045 /* Generate strcpy code */
1046 AddCodeLine ("ldy #$%02X", (unsigned char) (Offs - 1));
1047 if (Offs == 0 || AllowOneIndex) {
1048 g_defcodelabel (L1);
1049 AddCodeLine ("iny");
1050 AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg2.Expr, -Offs));
1051 AddCodeLine ("sta (sp),y");
1053 AddCodeLine ("ldx #$FF");
1054 g_defcodelabel (L1);
1055 AddCodeLine ("iny");
1056 AddCodeLine ("inx");
1057 AddCodeLine ("lda %s,x", ED_GetLabelName (&Arg2.Expr, 0));
1058 AddCodeLine ("sta (sp),y");
1060 AddCodeLine ("bne %s", LocalLabelName (L1));
1062 /* strcpy returns argument #1 */
1067 /* The function result is an rvalue in the primary register */
1068 ED_MakeRValExpr (Expr);
1069 Expr->Type = GetFuncReturn (Expr->Type);
1073 /* We expect the closing brace */
1079 /*****************************************************************************/
1081 /*****************************************************************************/
1085 static void StdFunc_strlen (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
1086 /* Handle the strlen function */
1088 static Type ArgType[] = { TYPE(T_PTR), TYPE(T_CHAR|T_QUAL_CONST), TYPE(T_END) };
1098 /* Setup the argument type string */
1099 ArgType[1].C = GetDefaultChar () | T_QUAL_CONST;
1101 /* Evaluate the parameter */
1104 /* Check if the argument is an array. If so, remember the element count.
1105 * Otherwise set the element count to undefined.
1107 IsArray = IsTypeArray (Arg.Type);
1109 ECount = GetElementCount (Arg.Type);
1110 if (ECount == FLEXIBLE) {
1111 /* Treat as unknown */
1112 ECount = UNSPECIFIED;
1116 ECount = UNSPECIFIED;
1117 IsPtr = IsTypePtr (Arg.Type);
1120 /* Check if the elements of an array can be addressed by a byte sized
1121 * index. This is true if the size of the array is known and less than
1124 IsByteIndex = (ECount != UNSPECIFIED && ECount < 256);
1126 /* Do type conversion */
1127 TypeConversion (&Arg, ArgType);
1129 /* If the expression is a literal, and if string literals are read
1130 * only, we can calculate the length of the string and remove it
1131 * from the literal pool. Otherwise we have to calculate the length
1134 if (ED_IsLocLiteral (&Arg) && IS_Get (&WritableStrings) == 0) {
1136 /* Constant string literal */
1137 ED_MakeConstAbs (Expr, GetLiteralSize (Arg.LVal) - 1, type_size_t);
1139 /* We don't need the literal any longer */
1140 ReleaseLiteral (Arg.LVal);
1142 /* We will inline strlen for arrays with constant addresses, if either the
1143 * inlining was forced on the command line, or the array is smaller than
1144 * 256, so the inlining is considered safe.
1146 } else if (ED_IsLocConst (&Arg) && IsArray &&
1147 (IS_Get (&InlineStdFuncs) || IsByteIndex)) {
1149 /* Generate the strlen code */
1150 L = GetLocalLabel ();
1151 AddCodeLine ("ldy #$FF");
1153 AddCodeLine ("iny");
1154 AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg, 0));
1155 AddCodeLine ("bne %s", LocalLabelName (L));
1156 AddCodeLine ("tax");
1157 AddCodeLine ("tya");
1159 /* The function result is an rvalue in the primary register */
1160 ED_MakeRValExpr (Expr);
1161 Expr->Type = type_size_t;
1163 /* We will inline strlen for arrays on the stack, if the array is
1164 * completely within the reach of a byte sized index register.
1166 } else if (ED_IsLocStack (&Arg) && IsArray && IsByteIndex &&
1167 (Arg.IVal - StackPtr) + ECount < 256) {
1169 /* Calculate the true stack offset */
1170 int Offs = ED_GetStackOffs (&Arg, 0);
1172 /* Generate the strlen code */
1173 L = GetLocalLabel ();
1174 AddCodeLine ("ldx #$FF");
1175 AddCodeLine ("ldy #$%02X", (unsigned char) (Offs-1));
1177 AddCodeLine ("inx");
1178 AddCodeLine ("iny");
1179 AddCodeLine ("lda (sp),y");
1180 AddCodeLine ("bne %s", LocalLabelName (L));
1181 AddCodeLine ("txa");
1182 AddCodeLine ("ldx #$00");
1184 /* The function result is an rvalue in the primary register */
1185 ED_MakeRValExpr (Expr);
1186 Expr->Type = type_size_t;
1188 /* strlen for a string that is pointed to by a register variable will only
1189 * get inlined if requested on the command line, since we cannot know how
1190 * big the buffer actually is, so inlining is not always safe.
1192 } else if (ED_IsLocRegister (&Arg) && ED_IsLVal (&Arg) && IsPtr &&
1193 IS_Get (&InlineStdFuncs)) {
1195 /* Generate the strlen code */
1196 L = GetLocalLabel ();
1197 AddCodeLine ("ldy #$FF");
1199 AddCodeLine ("iny");
1200 AddCodeLine ("lda (%s),y", ED_GetLabelName (&Arg, 0));
1201 AddCodeLine ("bne %s", LocalLabelName (L));
1202 AddCodeLine ("tax");
1203 AddCodeLine ("tya");
1205 /* The function result is an rvalue in the primary register */
1206 ED_MakeRValExpr (Expr);
1207 Expr->Type = type_size_t;
1209 /* Last check: We will inline a generic strlen routine if inlining was
1210 * requested on the command line, and the code size factor is more than
1211 * 400 (code is 13 bytes vs. 3 for a jsr call).
1213 } else if (IS_Get (&CodeSizeFactor) > 400 && IS_Get (&InlineStdFuncs)) {
1215 /* Load the expression into the primary */
1216 LoadExpr (CF_NONE, &Arg);
1218 /* Inline the function */
1219 L = GetLocalLabel ();
1220 AddCodeLine ("sta ptr1");
1221 AddCodeLine ("stx ptr1+1");
1222 AddCodeLine ("ldy #$FF");
1224 AddCodeLine ("iny");
1225 AddCodeLine ("lda (ptr1),y");
1226 AddCodeLine ("bne %s", LocalLabelName (L));
1227 AddCodeLine ("tax");
1228 AddCodeLine ("tya");
1230 /* The function result is an rvalue in the primary register */
1231 ED_MakeRValExpr (Expr);
1232 Expr->Type = type_size_t;
1236 /* Load the expression into the primary */
1237 LoadExpr (CF_NONE, &Arg);
1239 /* Call the strlen function */
1240 AddCodeLine ("jsr _%s", Func_strlen);
1242 /* The function result is an rvalue in the primary register */
1243 ED_MakeRValExpr (Expr);
1244 Expr->Type = type_size_t;
1248 /* We expect the closing brace */
1254 /*****************************************************************************/
1256 /*****************************************************************************/
1260 int FindStdFunc (const char* Name)
1261 /* Determine if the given function is a known standard function that may be
1262 * called in a special way. If so, return the index, otherwise return -1.
1265 /* Look into the table for known names */
1266 struct StdFuncDesc* D =
1267 bsearch (Name, StdFuncs, FUNC_COUNT, sizeof (StdFuncs[0]), CmpFunc);
1269 /* Return the function index or -1 */
1273 return D - StdFuncs;
1279 void HandleStdFunc (int Index, FuncDesc* F, ExprDesc* lval)
1280 /* Generate code for a known standard function. */
1282 struct StdFuncDesc* D;
1284 /* Get a pointer to the table entry */
1285 CHECK (Index >= 0 && Index < (int)FUNC_COUNT);
1286 D = StdFuncs + Index;
1288 /* Call the handler function */
1289 D->Handler (F, lval);