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 void AddCmpCodeIfSizeNot256 (const char* Code, long Size)
189 /* Add a line of Assembly code that compares an index register
190 ** only if it isn't comparing to #<256. (If the next line
191 ** is "bne", then this will avoid a redundant line.)
195 AddCodeLine (Code, (unsigned int)Size);
201 /*****************************************************************************/
203 /*****************************************************************************/
207 static void StdFunc_memcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
208 /* Handle the memcpy function */
210 /* Argument types: (void*, const void*, size_t) */
211 static Type Arg1Type[] = { TYPE(T_PTR), TYPE(T_VOID), TYPE(T_END) };
212 static Type Arg2Type[] = { TYPE(T_PTR), TYPE(T_VOID|T_QUAL_CONST), TYPE(T_END) };
213 static Type Arg3Type[] = { TYPE(T_SIZE_T), TYPE(T_END) };
215 ArgDesc Arg1, Arg2, Arg3;
216 unsigned ParamSize = 0;
221 ParseArg (&Arg1, Arg1Type);
222 g_push (Arg1.Flags, Arg1.Expr.IVal);
223 GetCodePos (&Arg1.End);
224 ParamSize += SizeOf (Arg1Type);
228 ParseArg (&Arg2, Arg2Type);
229 g_push (Arg2.Flags, Arg2.Expr.IVal);
230 GetCodePos (&Arg2.End);
231 ParamSize += SizeOf (Arg2Type);
234 /* Argument #3. Since memcpy is a fastcall function, we must load the
235 ** arg into the primary if it is not already there. This parameter is
236 ** also ignored for the calculation of the parameter size, since it is
237 ** not passed via the stack.
239 ParseArg (&Arg3, Arg3Type);
240 if (Arg3.Flags & CF_CONST) {
241 LoadExpr (CF_NONE, &Arg3.Expr);
244 /* Emit the actual function call. This will also cleanup the stack. */
245 g_call (CF_FIXARGC, Func_memcpy, ParamSize);
247 if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal == 0) {
249 /* memcpy has been called with a count argument of zero */
250 Warning ("Call to memcpy has no effect");
252 /* Remove all of the generated code but the load of the first
253 ** argument, which is what memcpy returns.
255 RemoveCode (&Arg1.Push);
257 /* Set the function result to the first argument */
260 /* Bail out, no need for further improvements */
264 if (IS_Get (&InlineStdFuncs)) {
266 /* We've generated the complete code for the function now and know the
267 ** types of all parameters. Check for situations where better code can
268 ** be generated. If such a situation is detected, throw away the
269 ** generated, and emit better code.
271 if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
272 ((ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr)) ||
273 (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr))) &&
274 ((ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) ||
275 (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr)))) {
277 int Reg1 = ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr);
278 int Reg2 = ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr);
280 /* Drop the generated code */
281 RemoveCode (&Arg1.Expr.Start);
283 /* We need a label */
284 Label = GetLocalLabel ();
286 /* Generate memcpy code */
287 if (Arg3.Expr.IVal <= 129) {
289 AddCodeLine ("ldy #$%02X", (unsigned char) (Arg3.Expr.IVal-1));
290 g_defcodelabel (Label);
292 AddCodeLine ("lda (%s),y", ED_GetLabelName (&Arg2.Expr, 0));
294 AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg2.Expr, 0));
297 AddCodeLine ("sta (%s),y", ED_GetLabelName (&Arg1.Expr, 0));
299 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, 0));
302 AddCodeLine ("bpl %s", LocalLabelName (Label));
306 AddCodeLine ("ldy #$00");
307 g_defcodelabel (Label);
309 AddCodeLine ("lda (%s),y", ED_GetLabelName (&Arg2.Expr, 0));
311 AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg2.Expr, 0));
314 AddCodeLine ("sta (%s),y", ED_GetLabelName (&Arg1.Expr, 0));
316 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, 0));
319 AddCmpCodeIfSizeNot256 ("cpy #$%02X", Arg3.Expr.IVal);
320 AddCodeLine ("bne %s", LocalLabelName (Label));
324 /* memcpy returns the address, so the result is actually identical
325 ** to the first argument.
329 /* Bail out, no need for further processing */
333 if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
334 ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr) &&
335 ED_IsRVal (&Arg1.Expr) && ED_IsLocStack (&Arg1.Expr) &&
336 (Arg1.Expr.IVal - StackPtr) + Arg3.Expr.IVal < 256) {
338 /* It is possible to just use one index register even if the stack
339 ** offset is not zero, by adjusting the offset to the constant
340 ** address accordingly. But we cannot do this if the data in
341 ** question is in the register space or at an absolute address less
342 ** than 256. Register space is zero page, which means that the
343 ** address calculation could overflow in the linker.
345 int AllowOneIndex = !ED_IsLocRegister (&Arg2.Expr) &&
346 !(ED_IsLocAbs (&Arg2.Expr) && Arg2.Expr.IVal < 256);
348 /* Calculate the real stack offset */
349 Offs = ED_GetStackOffs (&Arg1.Expr, 0);
351 /* Drop the generated code */
352 RemoveCode (&Arg1.Expr.Start);
354 /* We need a label */
355 Label = GetLocalLabel ();
357 /* Generate memcpy code */
358 if (Arg3.Expr.IVal <= 129 && !AllowOneIndex) {
361 AddCodeLine ("ldy #$%02X", (unsigned char) (Offs + Arg3.Expr.IVal - 1));
362 g_defcodelabel (Label);
363 AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg2.Expr, -Offs));
364 AddCodeLine ("sta (sp),y");
366 AddCodeLine ("bpl %s", LocalLabelName (Label));
368 AddCodeLine ("ldx #$%02X", (unsigned char) (Arg3.Expr.IVal-1));
369 AddCodeLine ("ldy #$%02X", (unsigned char) (Offs + Arg3.Expr.IVal - 1));
370 g_defcodelabel (Label);
371 AddCodeLine ("lda %s,x", ED_GetLabelName (&Arg2.Expr, 0));
372 AddCodeLine ("sta (sp),y");
375 AddCodeLine ("bpl %s", LocalLabelName (Label));
380 if (Offs == 0 || AllowOneIndex) {
381 AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
382 g_defcodelabel (Label);
383 AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg2.Expr, -Offs));
384 AddCodeLine ("sta (sp),y");
386 AddCmpCodeIfSizeNot256 ("cpy #$%02X", Offs + Arg3.Expr.IVal);
387 AddCodeLine ("bne %s", LocalLabelName (Label));
389 AddCodeLine ("ldx #$00");
390 AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
391 g_defcodelabel (Label);
392 AddCodeLine ("lda %s,x", ED_GetLabelName (&Arg2.Expr, 0));
393 AddCodeLine ("sta (sp),y");
396 AddCmpCodeIfSizeNot256 ("cpx #$%02X", Arg3.Expr.IVal);
397 AddCodeLine ("bne %s", LocalLabelName (Label));
402 /* memcpy returns the address, so the result is actually identical
403 ** to the first argument.
407 /* Bail out, no need for further processing */
411 if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
412 ED_IsRVal (&Arg2.Expr) && ED_IsLocStack (&Arg2.Expr) &&
413 (Arg2.Expr.IVal - StackPtr) + Arg3.Expr.IVal < 256 &&
414 ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) {
416 /* It is possible to just use one index register even if the stack
417 ** offset is not zero, by adjusting the offset to the constant
418 ** address accordingly. But we cannot do this if the data in
419 ** question is in the register space or at an absolute address less
420 ** than 256. Register space is zero page, which means that the
421 ** address calculation could overflow in the linker.
423 int AllowOneIndex = !ED_IsLocRegister (&Arg1.Expr) &&
424 !(ED_IsLocAbs (&Arg1.Expr) && Arg1.Expr.IVal < 256);
426 /* Calculate the real stack offset */
427 Offs = ED_GetStackOffs (&Arg2.Expr, 0);
429 /* Drop the generated code */
430 RemoveCode (&Arg1.Expr.Start);
432 /* We need a label */
433 Label = GetLocalLabel ();
435 /* Generate memcpy code */
436 if (Arg3.Expr.IVal <= 129 && !AllowOneIndex) {
439 AddCodeLine ("ldy #$%02X", (unsigned char) (Arg3.Expr.IVal - 1));
440 g_defcodelabel (Label);
441 AddCodeLine ("lda (sp),y");
442 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, 0));
444 AddCodeLine ("bpl %s", LocalLabelName (Label));
446 AddCodeLine ("ldx #$%02X", (unsigned char) (Arg3.Expr.IVal-1));
447 AddCodeLine ("ldy #$%02X", (unsigned char) (Offs + Arg3.Expr.IVal - 1));
448 g_defcodelabel (Label);
449 AddCodeLine ("lda (sp),y");
450 AddCodeLine ("sta %s,x", ED_GetLabelName (&Arg1.Expr, 0));
453 AddCodeLine ("bpl %s", LocalLabelName (Label));
458 if (Offs == 0 || AllowOneIndex) {
459 AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
460 g_defcodelabel (Label);
461 AddCodeLine ("lda (sp),y");
462 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, -Offs));
464 AddCmpCodeIfSizeNot256 ("cpy #$%02X", Offs + Arg3.Expr.IVal);
465 AddCodeLine ("bne %s", LocalLabelName (Label));
467 AddCodeLine ("ldx #$00");
468 AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
469 g_defcodelabel (Label);
470 AddCodeLine ("lda (sp),y");
471 AddCodeLine ("sta %s,x", ED_GetLabelName (&Arg1.Expr, 0));
474 AddCmpCodeIfSizeNot256 ("cpx #$%02X", Arg3.Expr.IVal);
475 AddCodeLine ("bne %s", LocalLabelName (Label));
480 /* memcpy returns the address, so the result is actually identical
481 ** to the first argument.
485 /* Bail out, no need for further processing */
489 if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
490 ED_IsRVal (&Arg2.Expr) && ED_IsLocStack (&Arg2.Expr) &&
491 (Offs = ED_GetStackOffs (&Arg2.Expr, 0)) == 0) {
493 /* Drop the generated code but leave the load of the first argument*/
494 RemoveCode (&Arg1.Push);
496 /* We need a label */
497 Label = GetLocalLabel ();
499 /* Generate memcpy code */
500 AddCodeLine ("sta ptr1");
501 AddCodeLine ("stx ptr1+1");
502 if (Arg3.Expr.IVal <= 129) {
503 AddCodeLine ("ldy #$%02X", (unsigned char) (Arg3.Expr.IVal - 1));
504 g_defcodelabel (Label);
505 AddCodeLine ("lda (sp),y");
506 AddCodeLine ("sta (ptr1),y");
508 AddCodeLine ("bpl %s", LocalLabelName (Label));
510 AddCodeLine ("ldy #$00");
511 g_defcodelabel (Label);
512 AddCodeLine ("lda (sp),y");
513 AddCodeLine ("sta (ptr1),y");
515 AddCmpCodeIfSizeNot256 ("cpy #$%02X", Arg3.Expr.IVal);
516 AddCodeLine ("bne %s", LocalLabelName (Label));
519 /* Reload result - X hasn't changed by the code above */
520 AddCodeLine ("lda ptr1");
522 /* The function result is an rvalue in the primary register */
523 ED_MakeRValExpr (Expr);
524 Expr->Type = GetFuncReturn (Expr->Type);
526 /* Bail out, no need for further processing */
531 /* The function result is an rvalue in the primary register */
532 ED_MakeRValExpr (Expr);
533 Expr->Type = GetFuncReturn (Expr->Type);
536 /* We expect the closing brace */
542 /*****************************************************************************/
544 /*****************************************************************************/
548 static void StdFunc_memset (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
549 /* Handle the memset function */
551 /* Argument types: (void*, int, size_t) */
552 static Type Arg1Type[] = { TYPE(T_PTR), TYPE(T_VOID), TYPE(T_END) };
553 static Type Arg2Type[] = { TYPE(T_INT), TYPE(T_END) };
554 static Type Arg3Type[] = { TYPE(T_SIZE_T), TYPE(T_END) };
556 ArgDesc Arg1, Arg2, Arg3;
557 int MemSet = 1; /* Use real memset if true */
558 unsigned ParamSize = 0;
562 ParseArg (&Arg1, Arg1Type);
563 g_push (Arg1.Flags, Arg1.Expr.IVal);
564 GetCodePos (&Arg1.End);
565 ParamSize += SizeOf (Arg1Type);
568 /* Argument #2. This argument is special in that we will call another
569 ** function if it is a constant zero.
571 ParseArg (&Arg2, Arg2Type);
572 if ((Arg2.Flags & CF_CONST) != 0 && Arg2.Expr.IVal == 0) {
573 /* Don't call memset, call bzero instead */
576 /* Push the argument */
577 g_push (Arg2.Flags, Arg2.Expr.IVal);
578 GetCodePos (&Arg2.End);
579 ParamSize += SizeOf (Arg2Type);
583 /* Argument #3. Since memset is a fastcall function, we must load the
584 ** arg into the primary if it is not already there. This parameter is
585 ** also ignored for the calculation of the parameter size, since it is
586 ** not passed via the stack.
588 ParseArg (&Arg3, Arg3Type);
589 if (Arg3.Flags & CF_CONST) {
590 LoadExpr (CF_NONE, &Arg3.Expr);
593 /* Emit the actual function call. This will also cleanup the stack. */
594 g_call (CF_FIXARGC, MemSet? Func_memset : Func__bzero, ParamSize);
596 if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal == 0) {
598 /* memset has been called with a count argument of zero */
599 Warning ("Call to memset has no effect");
601 /* Remove all of the generated code but the load of the first
602 ** argument, which is what memset returns.
604 RemoveCode (&Arg1.Push);
606 /* Set the function result to the first argument */
609 /* Bail out, no need for further improvements */
613 if (IS_Get (&InlineStdFuncs)) {
615 /* We've generated the complete code for the function now and know the
616 ** types of all parameters. Check for situations where better code can
617 ** be generated. If such a situation is detected, throw away the
618 ** generated, and emit better code.
619 ** Note: Lots of improvements would be possible here, but I will
620 ** concentrate on the most common case: memset with arguments 2 and 3
621 ** being constant numerical values. Some checks have shown that this
622 ** covers nearly 90% of all memset calls.
624 if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
625 ED_IsConstAbsInt (&Arg2.Expr) &&
626 ((ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) ||
627 (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr)))) {
629 int Reg = ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr);
631 /* Drop the generated code */
632 RemoveCode (&Arg1.Expr.Start);
634 /* We need a label */
635 Label = GetLocalLabel ();
637 /* Generate memset code */
638 if (Arg3.Expr.IVal <= 129) {
640 AddCodeLine ("ldy #$%02X", (unsigned char) (Arg3.Expr.IVal-1));
641 AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
642 g_defcodelabel (Label);
644 AddCodeLine ("sta (%s),y", ED_GetLabelName (&Arg1.Expr, 0));
646 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, 0));
649 AddCodeLine ("bpl %s", LocalLabelName (Label));
653 AddCodeLine ("ldy #$00");
654 AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
655 g_defcodelabel (Label);
657 AddCodeLine ("sta (%s),y", ED_GetLabelName (&Arg1.Expr, 0));
659 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, 0));
662 AddCmpCodeIfSizeNot256 ("cpy #$%02X", Arg3.Expr.IVal);
663 AddCodeLine ("bne %s", LocalLabelName (Label));
667 /* memset returns the address, so the result is actually identical
668 ** to the first argument.
672 /* Bail out, no need for further processing */
676 if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
677 ED_IsConstAbsInt (&Arg2.Expr) &&
678 ED_IsRVal (&Arg1.Expr) && ED_IsLocStack (&Arg1.Expr) &&
679 (Arg1.Expr.IVal - StackPtr) + Arg3.Expr.IVal < 256) {
681 /* Calculate the real stack offset */
682 int Offs = ED_GetStackOffs (&Arg1.Expr, 0);
684 /* Drop the generated code */
685 RemoveCode (&Arg1.Expr.Start);
687 /* We need a label */
688 Label = GetLocalLabel ();
690 /* Generate memset code */
691 AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
692 AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
693 g_defcodelabel (Label);
694 AddCodeLine ("sta (sp),y");
696 AddCmpCodeIfSizeNot256 ("cpy #$%02X", Offs + Arg3.Expr.IVal);
697 AddCodeLine ("bne %s", LocalLabelName (Label));
699 /* memset returns the address, so the result is actually identical
700 ** to the first argument.
704 /* Bail out, no need for further processing */
708 if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
709 ED_IsConstAbsInt (&Arg2.Expr) &&
710 (Arg2.Expr.IVal != 0 || IS_Get (&CodeSizeFactor) > 200)) {
712 /* Remove all of the generated code but the load of the first
715 RemoveCode (&Arg1.Push);
717 /* We need a label */
718 Label = GetLocalLabel ();
721 AddCodeLine ("sta ptr1");
722 AddCodeLine ("stx ptr1+1");
723 if (Arg3.Expr.IVal <= 129) {
724 AddCodeLine ("ldy #$%02X", (unsigned char) (Arg3.Expr.IVal-1));
725 AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
726 g_defcodelabel (Label);
727 AddCodeLine ("sta (ptr1),y");
729 AddCodeLine ("bpl %s", LocalLabelName (Label));
731 AddCodeLine ("ldy #$00");
732 AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
733 g_defcodelabel (Label);
734 AddCodeLine ("sta (ptr1),y");
736 AddCmpCodeIfSizeNot256 ("cpy #$%02X", Arg3.Expr.IVal);
737 AddCodeLine ("bne %s", LocalLabelName (Label));
740 /* Load the function result pointer into a/x (x is still valid). This
741 ** code will get removed by the optimizer if it is not used later.
743 AddCodeLine ("lda ptr1");
745 /* The function result is an rvalue in the primary register */
746 ED_MakeRValExpr (Expr);
747 Expr->Type = GetFuncReturn (Expr->Type);
749 /* Bail out, no need for further processing */
754 /* The function result is an rvalue in the primary register */
755 ED_MakeRValExpr (Expr);
756 Expr->Type = GetFuncReturn (Expr->Type);
759 /* We expect the closing brace */
765 /*****************************************************************************/
767 /*****************************************************************************/
771 static void StdFunc_strcmp (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
772 /* Handle the strcmp function */
774 /* Argument types: (const char*, const char*) */
775 static Type Arg1Type[] = { TYPE(T_PTR), TYPE(T_CHAR|T_QUAL_CONST), TYPE(T_END) };
776 static Type Arg2Type[] = { TYPE(T_PTR), TYPE(T_CHAR|T_QUAL_CONST), TYPE(T_END) };
779 unsigned ParamSize = 0;
785 /* Setup the argument type string */
786 Arg1Type[1].C = GetDefaultChar () | T_QUAL_CONST;
787 Arg2Type[1].C = GetDefaultChar () | T_QUAL_CONST;
790 ParseArg (&Arg1, Arg1Type);
791 g_push (Arg1.Flags, Arg1.Expr.IVal);
792 ParamSize += SizeOf (Arg1Type);
796 ParseArg (&Arg2, Arg2Type);
798 /* Since strcmp is a fastcall function, we must load the
799 ** arg into the primary if it is not already there. This parameter is
800 ** also ignored for the calculation of the parameter size, since it is
801 ** not passed via the stack.
803 if (Arg2.Flags & CF_CONST) {
804 LoadExpr (CF_NONE, &Arg2.Expr);
807 /* Emit the actual function call. This will also cleanup the stack. */
808 g_call (CF_FIXARGC, Func_strcmp, ParamSize);
810 /* Get the element counts of the arguments. Then get the larger of the
811 ** two into ECount1. This removes FLEXIBLE and UNSPECIFIED automatically
813 ECount1 = ArrayElementCount (&Arg1);
814 ECount2 = ArrayElementCount (&Arg2);
815 if (ECount2 > ECount1) {
819 if (IS_Get (&InlineStdFuncs)) {
821 /* If the second argument is the empty string literal, we can generate
822 ** more efficient code.
824 if (ED_IsLocLiteral (&Arg2.Expr) &&
825 IS_Get (&WritableStrings) == 0 &&
826 GetLiteralSize (Arg2.Expr.LVal) == 1 &&
827 GetLiteralStr (Arg2.Expr.LVal)[0] == '\0') {
829 /* Drop the generated code so we have the first argument in the
832 RemoveCode (&Arg1.Push);
834 /* We don't need the literal any longer */
835 ReleaseLiteral (Arg2.Expr.LVal);
837 /* We do now have Arg1 in the primary. Load the first character from
838 ** this string and cast to int. This is the function result.
840 IsArray = IsTypeArray (Arg1.Type) && ED_IsRVal (&Arg1.Expr);
841 if (IsArray && ED_IsLocStack (&Arg1.Expr) &&
842 (Offs = ED_GetStackOffs (&Arg1.Expr, 0) < 256)) {
843 /* Drop the generated code */
844 RemoveCode (&Arg1.Load);
847 AddCodeLine ("ldy #$%02X", Offs);
848 AddCodeLine ("ldx #$00");
849 AddCodeLine ("lda (sp),y");
850 } else if (IsArray && ED_IsLocConst (&Arg1.Expr)) {
851 /* Drop the generated code */
852 RemoveCode (&Arg1.Load);
855 AddCodeLine ("ldx #$00");
856 AddCodeLine ("lda %s", ED_GetLabelName (&Arg1.Expr, 0));
858 /* Drop part of the generated code so we have the first argument
861 RemoveCode (&Arg1.Push);
863 /* Fetch the first char */
864 g_getind (CF_CHAR | CF_UNSIGNED, 0);
867 } else if ((IS_Get (&CodeSizeFactor) >= 165) &&
868 ((ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr)) ||
869 (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr))) &&
870 ((ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) ||
871 (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr))) &&
872 (IS_Get (&EagerlyInlineFuncs) || (ECount1 > 0 && ECount1 < 256))) {
874 unsigned Entry, Loop, Fin; /* Labels */
878 if (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr)) {
883 if (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr)) {
884 Compare = "cmp (%s),y";
886 Compare = "cmp %s,y";
889 /* Drop the generated code */
890 RemoveCode (&Arg1.Expr.Start);
893 Entry = GetLocalLabel ();
894 Loop = GetLocalLabel ();
895 Fin = GetLocalLabel ();
897 /* Generate strcmp code */
898 AddCodeLine ("ldy #$00");
899 AddCodeLine ("beq %s", LocalLabelName (Entry));
900 g_defcodelabel (Loop);
902 AddCodeLine ("beq %s", LocalLabelName (Fin));
904 g_defcodelabel (Entry);
905 AddCodeLine (Load, ED_GetLabelName (&Arg1.Expr, 0));
906 AddCodeLine (Compare, ED_GetLabelName (&Arg2.Expr, 0));
907 AddCodeLine ("beq %s", LocalLabelName (Loop));
908 AddCodeLine ("ldx #$01");
909 AddCodeLine ("bcs %s", LocalLabelName (Fin));
910 AddCodeLine ("ldx #$FF");
911 g_defcodelabel (Fin);
913 } else if ((IS_Get (&CodeSizeFactor) > 190) &&
914 ((ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr)) ||
915 (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr))) &&
916 (IS_Get (&EagerlyInlineFuncs) || (ECount1 > 0 && ECount1 < 256))) {
918 unsigned Entry, Loop, Fin; /* Labels */
921 if (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr)) {
922 Compare = "cmp (%s),y";
924 Compare = "cmp %s,y";
927 /* Drop the generated code */
928 RemoveCode (&Arg1.Push);
931 Entry = GetLocalLabel ();
932 Loop = GetLocalLabel ();
933 Fin = GetLocalLabel ();
935 /* Store Arg1 into ptr1 */
936 AddCodeLine ("sta ptr1");
937 AddCodeLine ("stx ptr1+1");
939 /* Generate strcmp code */
940 AddCodeLine ("ldy #$00");
941 AddCodeLine ("beq %s", LocalLabelName (Entry));
942 g_defcodelabel (Loop);
944 AddCodeLine ("beq %s", LocalLabelName (Fin));
946 g_defcodelabel (Entry);
947 AddCodeLine ("lda (ptr1),y");
948 AddCodeLine (Compare, ED_GetLabelName (&Arg2.Expr, 0));
949 AddCodeLine ("beq %s", LocalLabelName (Loop));
950 AddCodeLine ("ldx #$01");
951 AddCodeLine ("bcs %s", LocalLabelName (Fin));
952 AddCodeLine ("ldx #$FF");
953 g_defcodelabel (Fin);
957 /* The function result is an rvalue in the primary register */
958 ED_MakeRValExpr (Expr);
959 Expr->Type = GetFuncReturn (Expr->Type);
961 /* We expect the closing brace */
967 /*****************************************************************************/
969 /*****************************************************************************/
973 static void StdFunc_strcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
974 /* Handle the strcpy function */
976 /* Argument types: (char*, const char*) */
977 static Type Arg1Type[] = { TYPE(T_PTR), TYPE(T_CHAR), TYPE(T_END) };
978 static Type Arg2Type[] = { TYPE(T_PTR), TYPE(T_CHAR|T_QUAL_CONST), TYPE(T_END) };
981 unsigned ParamSize = 0;
985 /* Setup the argument type string */
986 Arg1Type[1].C = GetDefaultChar ();
987 Arg2Type[1].C = GetDefaultChar () | T_QUAL_CONST;
990 ParseArg (&Arg1, Arg1Type);
991 g_push (Arg1.Flags, Arg1.Expr.IVal);
992 GetCodePos (&Arg1.End);
993 ParamSize += SizeOf (Arg1Type);
996 /* Argument #2. Since strcpy is a fastcall function, we must load the
997 ** arg into the primary if it is not already there. This parameter is
998 ** also ignored for the calculation of the parameter size, since it is
999 ** not passed via the stack.
1001 ParseArg (&Arg2, Arg2Type);
1002 if (Arg2.Flags & CF_CONST) {
1003 LoadExpr (CF_NONE, &Arg2.Expr);
1006 /* Emit the actual function call. This will also cleanup the stack. */
1007 g_call (CF_FIXARGC, Func_strcpy, ParamSize);
1009 /* Get the element count of argument 1 if it is an array */
1010 ECount = ArrayElementCount (&Arg1);
1012 if (IS_Get (&InlineStdFuncs)) {
1014 /* We've generated the complete code for the function now and know the
1015 ** types of all parameters. Check for situations where better code can
1016 ** be generated. If such a situation is detected, throw away the
1017 ** generated, and emit better code.
1019 if (((ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr)) ||
1020 (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr))) &&
1021 ((ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) ||
1022 (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr))) &&
1023 (IS_Get (&EagerlyInlineFuncs) ||
1024 (ECount != UNSPECIFIED && ECount < 256))) {
1028 if (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr)) {
1029 Load = "lda (%s),y";
1033 if (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr)) {
1034 Store = "sta (%s),y";
1039 /* Drop the generated code */
1040 RemoveCode (&Arg1.Expr.Start);
1042 /* We need labels */
1043 L1 = GetLocalLabel ();
1045 /* Generate strcpy code */
1046 AddCodeLine ("ldy #$FF");
1047 g_defcodelabel (L1);
1048 AddCodeLine ("iny");
1049 AddCodeLine (Load, ED_GetLabelName (&Arg2.Expr, 0));
1050 AddCodeLine (Store, ED_GetLabelName (&Arg1.Expr, 0));
1051 AddCodeLine ("bne %s", LocalLabelName (L1));
1053 /* strcpy returns argument #1 */
1056 /* Bail out, no need for further processing */
1060 if (ED_IsRVal (&Arg2.Expr) && ED_IsLocStack (&Arg2.Expr) &&
1062 ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) {
1064 /* It is possible to just use one index register even if the stack
1065 ** offset is not zero, by adjusting the offset to the constant
1066 ** address accordingly. But we cannot do this if the data in
1067 ** question is in the register space or at an absolute address less
1068 ** than 256. Register space is zero page, which means that the
1069 ** address calculation could overflow in the linker.
1071 int AllowOneIndex = !ED_IsLocRegister (&Arg1.Expr) &&
1072 !(ED_IsLocAbs (&Arg1.Expr) && Arg1.Expr.IVal < 256);
1074 /* Calculate the real stack offset */
1075 int Offs = ED_GetStackOffs (&Arg2.Expr, 0);
1077 /* Drop the generated code */
1078 RemoveCode (&Arg1.Expr.Start);
1080 /* We need labels */
1081 L1 = GetLocalLabel ();
1083 /* Generate strcpy code */
1084 AddCodeLine ("ldy #$%02X", (unsigned char) (Offs - 1));
1085 if (Offs == 0 || AllowOneIndex) {
1086 g_defcodelabel (L1);
1087 AddCodeLine ("iny");
1088 AddCodeLine ("lda (sp),y");
1089 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, -Offs));
1091 AddCodeLine ("ldx #$FF");
1092 g_defcodelabel (L1);
1093 AddCodeLine ("iny");
1094 AddCodeLine ("inx");
1095 AddCodeLine ("lda (sp),y");
1096 AddCodeLine ("sta %s,x", ED_GetLabelName (&Arg1.Expr, 0));
1098 AddCodeLine ("bne %s", LocalLabelName (L1));
1100 /* strcpy returns argument #1 */
1103 /* Bail out, no need for further processing */
1107 if (ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr) &&
1108 ED_IsRVal (&Arg1.Expr) && ED_IsLocStack (&Arg1.Expr) &&
1111 /* It is possible to just use one index register even if the stack
1112 ** offset is not zero, by adjusting the offset to the constant
1113 ** address accordingly. But we cannot do this if the data in
1114 ** question is in the register space or at an absolute address less
1115 ** than 256. Register space is zero page, which means that the
1116 ** address calculation could overflow in the linker.
1118 int AllowOneIndex = !ED_IsLocRegister (&Arg2.Expr) &&
1119 !(ED_IsLocAbs (&Arg2.Expr) && Arg2.Expr.IVal < 256);
1121 /* Calculate the real stack offset */
1122 int Offs = ED_GetStackOffs (&Arg1.Expr, 0);
1124 /* Drop the generated code */
1125 RemoveCode (&Arg1.Expr.Start);
1127 /* We need labels */
1128 L1 = GetLocalLabel ();
1130 /* Generate strcpy code */
1131 AddCodeLine ("ldy #$%02X", (unsigned char) (Offs - 1));
1132 if (Offs == 0 || AllowOneIndex) {
1133 g_defcodelabel (L1);
1134 AddCodeLine ("iny");
1135 AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg2.Expr, -Offs));
1136 AddCodeLine ("sta (sp),y");
1138 AddCodeLine ("ldx #$FF");
1139 g_defcodelabel (L1);
1140 AddCodeLine ("iny");
1141 AddCodeLine ("inx");
1142 AddCodeLine ("lda %s,x", ED_GetLabelName (&Arg2.Expr, 0));
1143 AddCodeLine ("sta (sp),y");
1145 AddCodeLine ("bne %s", LocalLabelName (L1));
1147 /* strcpy returns argument #1 */
1150 /* Bail out, no need for further processing */
1155 /* The function result is an rvalue in the primary register */
1156 ED_MakeRValExpr (Expr);
1157 Expr->Type = GetFuncReturn (Expr->Type);
1160 /* We expect the closing brace */
1166 /*****************************************************************************/
1168 /*****************************************************************************/
1172 static void StdFunc_strlen (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
1173 /* Handle the strlen function */
1175 static Type ArgType[] = { TYPE(T_PTR), TYPE(T_CHAR|T_QUAL_CONST), TYPE(T_END) };
1183 /* Setup the argument type string */
1184 ArgType[1].C = GetDefaultChar () | T_QUAL_CONST;
1186 /* Evaluate the parameter */
1189 /* Check if the argument is an array. If so, remember the element count.
1190 ** Otherwise set the element count to undefined.
1192 IsArray = IsTypeArray (Arg.Type);
1194 ECount = GetElementCount (Arg.Type);
1195 if (ECount == FLEXIBLE) {
1196 /* Treat as unknown */
1197 ECount = UNSPECIFIED;
1201 ECount = UNSPECIFIED;
1202 IsPtr = IsTypePtr (Arg.Type);
1205 /* Check if the elements of an array can be addressed by a byte sized
1206 ** index. This is true if the size of the array is known and less than
1209 IsByteIndex = (ECount != UNSPECIFIED && ECount < 256);
1211 /* Do type conversion */
1212 TypeConversion (&Arg, ArgType);
1214 if (IS_Get (&Optimize)) {
1216 /* If the expression is a literal, and if string literals are read
1217 ** only, we can calculate the length of the string and remove it
1218 ** from the literal pool. Otherwise we have to calculate the length
1221 if (ED_IsLocLiteral (&Arg) && IS_Get (&WritableStrings) == 0) {
1223 /* Constant string literal */
1224 ED_MakeConstAbs (Expr, GetLiteralSize (Arg.LVal) - 1, type_size_t);
1226 /* We don't need the literal any longer */
1227 ReleaseLiteral (Arg.LVal);
1229 /* Bail out, no need for further improvements */
1234 if (IS_Get (&InlineStdFuncs)) {
1236 /* We will inline strlen for arrays with constant addresses, if either
1237 ** requested on the command line, or the array is smaller than 256,
1238 ** so the inlining is considered safe.
1240 if (ED_IsLocConst (&Arg) && IsArray &&
1241 (IS_Get (&EagerlyInlineFuncs) || IsByteIndex)) {
1243 /* Generate the strlen code */
1244 L = GetLocalLabel ();
1245 AddCodeLine ("ldy #$FF");
1247 AddCodeLine ("iny");
1248 AddCodeLine ("ldx %s,y", ED_GetLabelName (&Arg, 0));
1249 AddCodeLine ("bne %s", LocalLabelName (L));
1250 AddCodeLine ("tya");
1252 /* The function result is an rvalue in the primary register */
1253 ED_MakeRValExpr (Expr);
1254 Expr->Type = type_size_t;
1256 /* Bail out, no need for further processing */
1260 /* We will inline strlen for arrays on the stack, if the array is
1261 ** completely within the reach of a byte sized index register.
1263 if (ED_IsLocStack (&Arg) && IsArray && IsByteIndex &&
1264 (Arg.IVal - StackPtr) + ECount < 256) {
1266 /* Calculate the true stack offset */
1267 int Offs = ED_GetStackOffs (&Arg, 0);
1269 /* Generate the strlen code */
1270 L = GetLocalLabel ();
1271 AddCodeLine ("ldx #$FF");
1272 AddCodeLine ("ldy #$%02X", (unsigned char) (Offs-1));
1274 AddCodeLine ("inx");
1275 AddCodeLine ("iny");
1276 AddCodeLine ("lda (sp),y");
1277 AddCodeLine ("bne %s", LocalLabelName (L));
1278 AddCodeLine ("txa");
1279 AddCodeLine ("ldx #$00");
1281 /* The function result is an rvalue in the primary register */
1282 ED_MakeRValExpr (Expr);
1283 Expr->Type = type_size_t;
1285 /* Bail out, no need for further processing */
1289 /* strlen for a string that is pointed to by a register variable will only
1290 ** get inlined if requested on the command line, since we cannot know how
1291 ** big the buffer actually is, so inlining is not always safe.
1293 if (ED_IsLocRegister (&Arg) && ED_IsLVal (&Arg) && IsPtr &&
1294 IS_Get (&EagerlyInlineFuncs)) {
1296 /* Generate the strlen code */
1297 L = GetLocalLabel ();
1298 AddCodeLine ("ldy #$FF");
1300 AddCodeLine ("iny");
1301 AddCodeLine ("lda (%s),y", ED_GetLabelName (&Arg, 0));
1302 AddCodeLine ("bne %s", LocalLabelName (L));
1303 AddCodeLine ("tax");
1304 AddCodeLine ("tya");
1306 /* The function result is an rvalue in the primary register */
1307 ED_MakeRValExpr (Expr);
1308 Expr->Type = type_size_t;
1310 /* Bail out, no need for further processing */
1314 /* Last check: We will inline a generic strlen routine if inlining was
1315 ** requested on the command line, and the code size factor is more than
1316 ** 400 (code is 13 bytes vs. 3 for a jsr call).
1318 if (IS_Get (&CodeSizeFactor) > 400 && IS_Get (&EagerlyInlineFuncs)) {
1320 /* Load the expression into the primary */
1321 LoadExpr (CF_NONE, &Arg);
1323 /* Inline the function */
1324 L = GetLocalLabel ();
1325 AddCodeLine ("sta ptr1");
1326 AddCodeLine ("stx ptr1+1");
1327 AddCodeLine ("ldy #$FF");
1329 AddCodeLine ("iny");
1330 AddCodeLine ("lda (ptr1),y");
1331 AddCodeLine ("bne %s", LocalLabelName (L));
1332 AddCodeLine ("tax");
1333 AddCodeLine ("tya");
1335 /* The function result is an rvalue in the primary register */
1336 ED_MakeRValExpr (Expr);
1337 Expr->Type = type_size_t;
1339 /* Bail out, no need for further processing */
1344 /* Load the expression into the primary */
1345 LoadExpr (CF_NONE, &Arg);
1347 /* Call the strlen function */
1348 AddCodeLine ("jsr _%s", Func_strlen);
1350 /* The function result is an rvalue in the primary register */
1351 ED_MakeRValExpr (Expr);
1352 Expr->Type = type_size_t;
1355 /* We expect the closing brace */
1361 /*****************************************************************************/
1363 /*****************************************************************************/
1367 int FindStdFunc (const char* Name)
1368 /* Determine if the given function is a known standard function that may be
1369 ** called in a special way. If so, return the index, otherwise return -1.
1372 /* Look into the table for known names */
1373 struct StdFuncDesc* D =
1374 bsearch (Name, StdFuncs, FUNC_COUNT, sizeof (StdFuncs[0]), CmpFunc);
1376 /* Return the function index or -1 */
1380 return D - StdFuncs;
1386 void HandleStdFunc (int Index, FuncDesc* F, ExprDesc* lval)
1387 /* Generate code for a known standard function. */
1389 struct StdFuncDesc* D;
1391 /* Get a pointer to the table entry */
1392 CHECK (Index >= 0 && Index < (int)FUNC_COUNT);
1393 D = StdFuncs + Index;
1395 /* Call the handler function */
1396 D->Handler (F, lval);