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 /* We've generated the complete code for the function now and know the
265 ** types of all parameters. Check for situations where better code can
266 ** be generated. If such a situation is detected, throw away the
267 ** generated, and emit better code.
269 if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
270 ((ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr)) ||
271 (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr))) &&
272 ((ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) ||
273 (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr)))) {
275 int Reg1 = ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr);
276 int Reg2 = ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr);
278 /* Drop the generated code */
279 RemoveCode (&Arg1.Expr.Start);
281 /* We need a label */
282 Label = GetLocalLabel ();
284 /* Generate memcpy code */
285 if (Arg3.Expr.IVal <= 127) {
287 AddCodeLine ("ldy #$%02X", (unsigned char) (Arg3.Expr.IVal-1));
288 g_defcodelabel (Label);
290 AddCodeLine ("lda (%s),y", ED_GetLabelName (&Arg2.Expr, 0));
292 AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg2.Expr, 0));
295 AddCodeLine ("sta (%s),y", ED_GetLabelName (&Arg1.Expr, 0));
297 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, 0));
300 AddCodeLine ("bpl %s", LocalLabelName (Label));
304 AddCodeLine ("ldy #$00");
305 g_defcodelabel (Label);
307 AddCodeLine ("lda (%s),y", ED_GetLabelName (&Arg2.Expr, 0));
309 AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg2.Expr, 0));
312 AddCodeLine ("sta (%s),y", ED_GetLabelName (&Arg1.Expr, 0));
314 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, 0));
317 AddCmpCodeIfSizeNot256 ("cpy #$%02X", Arg3.Expr.IVal);
318 AddCodeLine ("bne %s", LocalLabelName (Label));
322 /* memcpy returns the address, so the result is actually identical
323 ** to the first argument.
327 } else if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
328 ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr) &&
329 ED_IsRVal (&Arg1.Expr) && ED_IsLocStack (&Arg1.Expr) &&
330 (Arg1.Expr.IVal - StackPtr) + Arg3.Expr.IVal < 256) {
332 /* It is possible to just use one index register even if the stack
333 ** offset is not zero, by adjusting the offset to the constant
334 ** address accordingly. But we cannot do this if the data in
335 ** question is in the register space or at an absolute address less
336 ** than 256. Register space is zero page, which means that the
337 ** address calculation could overflow in the linker.
339 int AllowOneIndex = !ED_IsLocRegister (&Arg2.Expr) &&
340 !(ED_IsLocAbs (&Arg2.Expr) && Arg2.Expr.IVal < 256);
342 /* Calculate the real stack offset */
343 Offs = ED_GetStackOffs (&Arg1.Expr, 0);
345 /* Drop the generated code */
346 RemoveCode (&Arg1.Expr.Start);
348 /* We need a label */
349 Label = GetLocalLabel ();
351 /* Generate memcpy code */
352 if (Arg3.Expr.IVal <= 127 && !AllowOneIndex) {
355 AddCodeLine ("ldy #$%02X", (unsigned char) (Offs + Arg3.Expr.IVal - 1));
356 g_defcodelabel (Label);
357 AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg2.Expr, -Offs));
358 AddCodeLine ("sta (sp),y");
360 AddCodeLine ("bpl %s", LocalLabelName (Label));
362 AddCodeLine ("ldx #$%02X", (unsigned char) (Arg3.Expr.IVal-1));
363 AddCodeLine ("ldy #$%02X", (unsigned char) (Offs + Arg3.Expr.IVal - 1));
364 g_defcodelabel (Label);
365 AddCodeLine ("lda %s,x", ED_GetLabelName (&Arg2.Expr, 0));
366 AddCodeLine ("sta (sp),y");
369 AddCodeLine ("bpl %s", LocalLabelName (Label));
374 if (Offs == 0 || AllowOneIndex) {
375 AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
376 g_defcodelabel (Label);
377 AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg2.Expr, -Offs));
378 AddCodeLine ("sta (sp),y");
380 AddCmpCodeIfSizeNot256 ("cpy #$%02X", Offs + Arg3.Expr.IVal);
381 AddCodeLine ("bne %s", LocalLabelName (Label));
383 AddCodeLine ("ldx #$00");
384 AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
385 g_defcodelabel (Label);
386 AddCodeLine ("lda %s,x", ED_GetLabelName (&Arg2.Expr, 0));
387 AddCodeLine ("sta (sp),y");
390 AddCmpCodeIfSizeNot256 ("cpx #$%02X", Arg3.Expr.IVal);
391 AddCodeLine ("bne %s", LocalLabelName (Label));
396 /* memcpy returns the address, so the result is actually identical
397 ** to the first argument.
401 } else if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
402 ED_IsRVal (&Arg2.Expr) && ED_IsLocStack (&Arg2.Expr) &&
403 (Arg2.Expr.IVal - StackPtr) + Arg3.Expr.IVal < 256 &&
404 ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) {
406 /* It is possible to just use one index register even if the stack
407 ** offset is not zero, by adjusting the offset to the constant
408 ** address accordingly. But we cannot do this if the data in
409 ** question is in the register space or at an absolute address less
410 ** than 256. Register space is zero page, which means that the
411 ** address calculation could overflow in the linker.
413 int AllowOneIndex = !ED_IsLocRegister (&Arg1.Expr) &&
414 !(ED_IsLocAbs (&Arg1.Expr) && Arg1.Expr.IVal < 256);
416 /* Calculate the real stack offset */
417 Offs = ED_GetStackOffs (&Arg2.Expr, 0);
419 /* Drop the generated code */
420 RemoveCode (&Arg1.Expr.Start);
422 /* We need a label */
423 Label = GetLocalLabel ();
425 /* Generate memcpy code */
426 if (Arg3.Expr.IVal <= 127 && !AllowOneIndex) {
429 AddCodeLine ("ldy #$%02X", (unsigned char) (Arg3.Expr.IVal - 1));
430 g_defcodelabel (Label);
431 AddCodeLine ("lda (sp),y");
432 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, 0));
434 AddCodeLine ("bpl %s", LocalLabelName (Label));
436 AddCodeLine ("ldx #$%02X", (unsigned char) (Arg3.Expr.IVal-1));
437 AddCodeLine ("ldy #$%02X", (unsigned char) (Offs + Arg3.Expr.IVal - 1));
438 g_defcodelabel (Label);
439 AddCodeLine ("lda (sp),y");
440 AddCodeLine ("sta %s,x", ED_GetLabelName (&Arg1.Expr, 0));
443 AddCodeLine ("bpl %s", LocalLabelName (Label));
448 if (Offs == 0 || AllowOneIndex) {
449 AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
450 g_defcodelabel (Label);
451 AddCodeLine ("lda (sp),y");
452 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, -Offs));
454 AddCmpCodeIfSizeNot256 ("cpy #$%02X", Offs + Arg3.Expr.IVal);
455 AddCodeLine ("bne %s", LocalLabelName (Label));
457 AddCodeLine ("ldx #$00");
458 AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
459 g_defcodelabel (Label);
460 AddCodeLine ("lda (sp),y");
461 AddCodeLine ("sta %s,x", ED_GetLabelName (&Arg1.Expr, 0));
464 AddCmpCodeIfSizeNot256 ("cpx #$%02X", Arg3.Expr.IVal);
465 AddCodeLine ("bne %s", LocalLabelName (Label));
470 /* memcpy returns the address, so the result is actually identical
471 ** to the first argument.
475 } else if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
476 ED_IsRVal (&Arg2.Expr) && ED_IsLocStack (&Arg2.Expr) &&
477 (Offs = ED_GetStackOffs (&Arg2.Expr, 0)) == 0) {
479 /* Drop the generated code but leave the load of the first argument*/
480 RemoveCode (&Arg1.Push);
482 /* We need a label */
483 Label = GetLocalLabel ();
485 /* Generate memcpy code */
486 AddCodeLine ("sta ptr1");
487 AddCodeLine ("stx ptr1+1");
488 if (Arg3.Expr.IVal <= 127) {
489 AddCodeLine ("ldy #$%02X", (unsigned char) (Arg3.Expr.IVal - 1));
490 g_defcodelabel (Label);
491 AddCodeLine ("lda (sp),y");
492 AddCodeLine ("sta (ptr1),y");
494 AddCodeLine ("bpl %s", LocalLabelName (Label));
496 AddCodeLine ("ldy #$00");
497 g_defcodelabel (Label);
498 AddCodeLine ("lda (sp),y");
499 AddCodeLine ("sta (ptr1),y");
501 AddCmpCodeIfSizeNot256 ("cpy #$%02X", Arg3.Expr.IVal);
502 AddCodeLine ("bne %s", LocalLabelName (Label));
505 /* Reload result - X hasn't changed by the code above */
506 AddCodeLine ("lda ptr1");
508 /* The function result is an rvalue in the primary register */
509 ED_MakeRValExpr (Expr);
510 Expr->Type = GetFuncReturn (Expr->Type);
514 /* The function result is an rvalue in the primary register */
515 ED_MakeRValExpr (Expr);
516 Expr->Type = GetFuncReturn (Expr->Type);
521 /* We expect the closing brace */
527 /*****************************************************************************/
529 /*****************************************************************************/
533 static void StdFunc_memset (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
534 /* Handle the memset function */
536 /* Argument types: (void*, int, size_t) */
537 static Type Arg1Type[] = { TYPE(T_PTR), TYPE(T_VOID), TYPE(T_END) };
538 static Type Arg2Type[] = { TYPE(T_INT), TYPE(T_END) };
539 static Type Arg3Type[] = { TYPE(T_SIZE_T), TYPE(T_END) };
541 ArgDesc Arg1, Arg2, Arg3;
542 int MemSet = 1; /* Use real memset if true */
543 unsigned ParamSize = 0;
547 ParseArg (&Arg1, Arg1Type);
548 g_push (Arg1.Flags, Arg1.Expr.IVal);
549 GetCodePos (&Arg1.End);
550 ParamSize += SizeOf (Arg1Type);
553 /* Argument #2. This argument is special in that we will call another
554 ** function if it is a constant zero.
556 ParseArg (&Arg2, Arg2Type);
557 if ((Arg2.Flags & CF_CONST) != 0 && Arg2.Expr.IVal == 0) {
558 /* Don't call memset, call bzero instead */
561 /* Push the argument */
562 g_push (Arg2.Flags, Arg2.Expr.IVal);
563 GetCodePos (&Arg2.End);
564 ParamSize += SizeOf (Arg2Type);
568 /* Argument #3. Since memset is a fastcall function, we must load the
569 ** arg into the primary if it is not already there. This parameter is
570 ** also ignored for the calculation of the parameter size, since it is
571 ** not passed via the stack.
573 ParseArg (&Arg3, Arg3Type);
574 if (Arg3.Flags & CF_CONST) {
575 LoadExpr (CF_NONE, &Arg3.Expr);
578 /* Emit the actual function call. This will also cleanup the stack. */
579 g_call (CF_FIXARGC, MemSet? Func_memset : Func__bzero, ParamSize);
581 if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal == 0) {
583 /* memset has been called with a count argument of zero */
584 Warning ("Call to memset has no effect");
586 /* Remove all of the generated code but the load of the first
587 ** argument, which is what memset returns.
589 RemoveCode (&Arg1.Push);
591 /* Set the function result to the first argument */
594 /* Bail out, no need for further improvements */
598 /* We've generated the complete code for the function now and know the
599 ** types of all parameters. Check for situations where better code can
600 ** be generated. If such a situation is detected, throw away the
601 ** generated, and emit better code.
602 ** Note: Lots of improvements would be possible here, but I will
603 ** concentrate on the most common case: memset with arguments 2 and 3
604 ** being constant numerical values. Some checks have shown that this
605 ** covers nearly 90% of all memset calls.
607 if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
608 ED_IsConstAbsInt (&Arg2.Expr) &&
609 ((ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) ||
610 (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr)))) {
612 int Reg = ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr);
614 /* Drop the generated code */
615 RemoveCode (&Arg1.Expr.Start);
617 /* We need a label */
618 Label = GetLocalLabel ();
620 /* Generate memset code */
621 if (Arg3.Expr.IVal <= 127) {
623 AddCodeLine ("ldy #$%02X", (unsigned char) (Arg3.Expr.IVal-1));
624 AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
625 g_defcodelabel (Label);
627 AddCodeLine ("sta (%s),y", ED_GetLabelName (&Arg1.Expr, 0));
629 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, 0));
632 AddCodeLine ("bpl %s", LocalLabelName (Label));
636 AddCodeLine ("ldy #$00");
637 AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
638 g_defcodelabel (Label);
640 AddCodeLine ("sta (%s),y", ED_GetLabelName (&Arg1.Expr, 0));
642 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, 0));
645 AddCmpCodeIfSizeNot256 ("cpy #$%02X", Arg3.Expr.IVal);
646 AddCodeLine ("bne %s", LocalLabelName (Label));
650 /* memset returns the address, so the result is actually identical
651 ** to the first argument.
655 } else if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
656 ED_IsConstAbsInt (&Arg2.Expr) &&
657 ED_IsRVal (&Arg1.Expr) && ED_IsLocStack (&Arg1.Expr) &&
658 (Arg1.Expr.IVal - StackPtr) + Arg3.Expr.IVal < 256) {
660 /* Calculate the real stack offset */
661 int Offs = ED_GetStackOffs (&Arg1.Expr, 0);
663 /* Drop the generated code */
664 RemoveCode (&Arg1.Expr.Start);
666 /* We need a label */
667 Label = GetLocalLabel ();
669 /* Generate memset code */
670 AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
671 AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
672 g_defcodelabel (Label);
673 AddCodeLine ("sta (sp),y");
675 AddCmpCodeIfSizeNot256 ("cpy #$%02X", Offs + Arg3.Expr.IVal);
676 AddCodeLine ("bne %s", LocalLabelName (Label));
678 /* memset returns the address, so the result is actually identical
679 ** to the first argument.
683 } else if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
684 ED_IsConstAbsInt (&Arg2.Expr) &&
685 (Arg2.Expr.IVal != 0 || IS_Get (&CodeSizeFactor) > 200)) {
687 /* Remove all of the generated code but the load of the first
690 RemoveCode (&Arg1.Push);
692 /* We need a label */
693 Label = GetLocalLabel ();
696 AddCodeLine ("sta ptr1");
697 AddCodeLine ("stx ptr1+1");
698 if (Arg3.Expr.IVal <= 127) {
699 AddCodeLine ("ldy #$%02X", (unsigned char) (Arg3.Expr.IVal-1));
700 AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
701 g_defcodelabel (Label);
702 AddCodeLine ("sta (ptr1),y");
704 AddCodeLine ("bpl %s", LocalLabelName (Label));
706 AddCodeLine ("ldy #$00");
707 AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
708 g_defcodelabel (Label);
709 AddCodeLine ("sta (ptr1),y");
711 AddCmpCodeIfSizeNot256 ("cpy #$%02X", Arg3.Expr.IVal);
712 AddCodeLine ("bne %s", LocalLabelName (Label));
715 /* Load the function result pointer into a/x (x is still valid). This
716 ** code will get removed by the optimizer if it is not used later.
718 AddCodeLine ("lda ptr1");
720 /* The function result is an rvalue in the primary register */
721 ED_MakeRValExpr (Expr);
722 Expr->Type = GetFuncReturn (Expr->Type);
726 /* The function result is an rvalue in the primary register */
727 ED_MakeRValExpr (Expr);
728 Expr->Type = GetFuncReturn (Expr->Type);
733 /* We expect the closing brace */
739 /*****************************************************************************/
741 /*****************************************************************************/
745 static void StdFunc_strcmp (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
746 /* Handle the strcmp function */
748 /* Argument types: (const char*, const char*) */
749 static Type Arg1Type[] = { TYPE(T_PTR), TYPE(T_CHAR|T_QUAL_CONST), TYPE(T_END) };
750 static Type Arg2Type[] = { TYPE(T_PTR), TYPE(T_CHAR|T_QUAL_CONST), TYPE(T_END) };
753 unsigned ParamSize = 0;
759 /* Setup the argument type string */
760 Arg1Type[1].C = GetDefaultChar () | T_QUAL_CONST;
761 Arg2Type[1].C = GetDefaultChar () | T_QUAL_CONST;
764 ParseArg (&Arg1, Arg1Type);
765 g_push (Arg1.Flags, Arg1.Expr.IVal);
766 ParamSize += SizeOf (Arg1Type);
770 ParseArg (&Arg2, Arg2Type);
772 /* Since strcmp is a fastcall function, we must load the
773 ** arg into the primary if it is not already there. This parameter is
774 ** also ignored for the calculation of the parameter size, since it is
775 ** not passed via the stack.
777 if (Arg2.Flags & CF_CONST) {
778 LoadExpr (CF_NONE, &Arg2.Expr);
781 /* Emit the actual function call. This will also cleanup the stack. */
782 g_call (CF_FIXARGC, Func_strcmp, ParamSize);
784 /* Get the element counts of the arguments. Then get the larger of the
785 ** two into ECount1. This removes FLEXIBLE and UNSPECIFIED automatically
787 ECount1 = ArrayElementCount (&Arg1);
788 ECount2 = ArrayElementCount (&Arg2);
789 if (ECount2 > ECount1) {
793 /* If the second argument is the empty string literal, we can generate
794 ** more efficient code.
796 if (ED_IsLocLiteral (&Arg2.Expr) &&
797 IS_Get (&WritableStrings) == 0 &&
798 GetLiteralSize (Arg2.Expr.LVal) == 1 &&
799 GetLiteralStr (Arg2.Expr.LVal)[0] == '\0') {
801 /* Drop the generated code so we have the first argument in the
804 RemoveCode (&Arg1.Push);
806 /* We don't need the literal any longer */
807 ReleaseLiteral (Arg2.Expr.LVal);
809 /* We do now have Arg1 in the primary. Load the first character from
810 ** this string and cast to int. This is the function result.
812 IsArray = IsTypeArray (Arg1.Type) && ED_IsRVal (&Arg1.Expr);
813 if (IsArray && ED_IsLocStack (&Arg1.Expr) &&
814 (Offs = ED_GetStackOffs (&Arg1.Expr, 0) < 256)) {
815 /* Drop the generated code */
816 RemoveCode (&Arg1.Load);
819 AddCodeLine ("ldy #$%02X", Offs);
820 AddCodeLine ("ldx #$00");
821 AddCodeLine ("lda (sp),y");
822 } else if (IsArray && ED_IsLocConst (&Arg1.Expr)) {
823 /* Drop the generated code */
824 RemoveCode (&Arg1.Load);
827 AddCodeLine ("ldx #$00");
828 AddCodeLine ("lda %s", ED_GetLabelName (&Arg1.Expr, 0));
830 /* Drop part of the generated code so we have the first argument
833 RemoveCode (&Arg1.Push);
835 /* Fetch the first char */
836 g_getind (CF_CHAR | CF_UNSIGNED, 0);
839 } else if ((IS_Get (&CodeSizeFactor) >= 165) &&
840 ((ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr)) ||
841 (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr))) &&
842 ((ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) ||
843 (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr))) &&
844 (IS_Get (&InlineStdFuncs) || (ECount1 > 0 && ECount1 < 256))) {
847 unsigned Entry, Loop, Fin; /* Labels */
851 if (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr)) {
856 if (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr)) {
857 Compare = "cmp (%s),y";
859 Compare = "cmp %s,y";
862 /* Drop the generated code */
863 RemoveCode (&Arg1.Expr.Start);
866 Entry = GetLocalLabel ();
867 Loop = GetLocalLabel ();
868 Fin = GetLocalLabel ();
870 /* Generate strcmp code */
871 AddCodeLine ("ldy #$00");
872 AddCodeLine ("beq %s", LocalLabelName (Entry));
873 g_defcodelabel (Loop);
875 AddCodeLine ("beq %s", LocalLabelName (Fin));
877 g_defcodelabel (Entry);
878 AddCodeLine (Load, ED_GetLabelName (&Arg1.Expr, 0));
879 AddCodeLine (Compare, ED_GetLabelName (&Arg2.Expr, 0));
880 AddCodeLine ("beq %s", LocalLabelName (Loop));
881 AddCodeLine ("ldx #$01");
882 AddCodeLine ("bcs %s", LocalLabelName (Fin));
883 AddCodeLine ("ldx #$FF");
884 g_defcodelabel (Fin);
886 } else if ((IS_Get (&CodeSizeFactor) > 190) &&
887 ((ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr)) ||
888 (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr))) &&
889 (IS_Get (&InlineStdFuncs) || (ECount1 > 0 && ECount1 < 256))) {
892 unsigned Entry, Loop, Fin; /* Labels */
895 if (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr)) {
896 Compare = "cmp (%s),y";
898 Compare = "cmp %s,y";
901 /* Drop the generated code */
902 RemoveCode (&Arg1.Push);
905 Entry = GetLocalLabel ();
906 Loop = GetLocalLabel ();
907 Fin = GetLocalLabel ();
909 /* Store Arg1 into ptr1 */
910 AddCodeLine ("sta ptr1");
911 AddCodeLine ("stx ptr1+1");
913 /* Generate strcmp code */
914 AddCodeLine ("ldy #$00");
915 AddCodeLine ("beq %s", LocalLabelName (Entry));
916 g_defcodelabel (Loop);
918 AddCodeLine ("beq %s", LocalLabelName (Fin));
920 g_defcodelabel (Entry);
921 AddCodeLine ("lda (ptr1),y");
922 AddCodeLine (Compare, ED_GetLabelName (&Arg2.Expr, 0));
923 AddCodeLine ("beq %s", LocalLabelName (Loop));
924 AddCodeLine ("ldx #$01");
925 AddCodeLine ("bcs %s", LocalLabelName (Fin));
926 AddCodeLine ("ldx #$FF");
927 g_defcodelabel (Fin);
931 /* The function result is an rvalue in the primary register */
932 ED_MakeRValExpr (Expr);
933 Expr->Type = GetFuncReturn (Expr->Type);
935 /* We expect the closing brace */
941 /*****************************************************************************/
943 /*****************************************************************************/
947 static void StdFunc_strcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
948 /* Handle the strcpy function */
950 /* Argument types: (char*, const char*) */
951 static Type Arg1Type[] = { TYPE(T_PTR), TYPE(T_CHAR), TYPE(T_END) };
952 static Type Arg2Type[] = { TYPE(T_PTR), TYPE(T_CHAR|T_QUAL_CONST), TYPE(T_END) };
955 unsigned ParamSize = 0;
959 /* Setup the argument type string */
960 Arg1Type[1].C = GetDefaultChar ();
961 Arg2Type[1].C = GetDefaultChar () | T_QUAL_CONST;
964 ParseArg (&Arg1, Arg1Type);
965 g_push (Arg1.Flags, Arg1.Expr.IVal);
966 GetCodePos (&Arg1.End);
967 ParamSize += SizeOf (Arg1Type);
970 /* Argument #2. Since strcpy is a fastcall function, we must load the
971 ** arg into the primary if it is not already there. This parameter is
972 ** also ignored for the calculation of the parameter size, since it is
973 ** not passed via the stack.
975 ParseArg (&Arg2, Arg2Type);
976 if (Arg2.Flags & CF_CONST) {
977 LoadExpr (CF_NONE, &Arg2.Expr);
980 /* Emit the actual function call. This will also cleanup the stack. */
981 g_call (CF_FIXARGC, Func_strcpy, ParamSize);
983 /* Get the element count of argument 1 if it is an array */
984 ECount = ArrayElementCount (&Arg1);
986 /* We've generated the complete code for the function now and know the
987 ** types of all parameters. Check for situations where better code can
988 ** be generated. If such a situation is detected, throw away the
989 ** generated, and emit better code.
991 if (((ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr)) ||
992 (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr))) &&
993 ((ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) ||
994 (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr))) &&
995 (IS_Get (&InlineStdFuncs) ||
996 (ECount != UNSPECIFIED && ECount < 256))) {
1000 if (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr)) {
1001 Load = "lda (%s),y";
1005 if (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr)) {
1006 Store = "sta (%s),y";
1011 /* Drop the generated code */
1012 RemoveCode (&Arg1.Expr.Start);
1014 /* We need labels */
1015 L1 = GetLocalLabel ();
1017 /* Generate strcpy code */
1018 AddCodeLine ("ldy #$FF");
1019 g_defcodelabel (L1);
1020 AddCodeLine ("iny");
1021 AddCodeLine (Load, ED_GetLabelName (&Arg2.Expr, 0));
1022 AddCodeLine (Store, ED_GetLabelName (&Arg1.Expr, 0));
1023 AddCodeLine ("bne %s", LocalLabelName (L1));
1025 /* strcpy returns argument #1 */
1028 } else if (ED_IsRVal (&Arg2.Expr) && ED_IsLocStack (&Arg2.Expr) &&
1030 ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) {
1032 /* It is possible to just use one index register even if the stack
1033 ** offset is not zero, by adjusting the offset to the constant
1034 ** address accordingly. But we cannot do this if the data in
1035 ** question is in the register space or at an absolute address less
1036 ** than 256. Register space is zero page, which means that the
1037 ** address calculation could overflow in the linker.
1039 int AllowOneIndex = !ED_IsLocRegister (&Arg1.Expr) &&
1040 !(ED_IsLocAbs (&Arg1.Expr) && Arg1.Expr.IVal < 256);
1042 /* Calculate the real stack offset */
1043 int Offs = ED_GetStackOffs (&Arg2.Expr, 0);
1045 /* Drop the generated code */
1046 RemoveCode (&Arg1.Expr.Start);
1048 /* We need labels */
1049 L1 = GetLocalLabel ();
1051 /* Generate strcpy code */
1052 AddCodeLine ("ldy #$%02X", (unsigned char) (Offs - 1));
1053 if (Offs == 0 || AllowOneIndex) {
1054 g_defcodelabel (L1);
1055 AddCodeLine ("iny");
1056 AddCodeLine ("lda (sp),y");
1057 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, -Offs));
1059 AddCodeLine ("ldx #$FF");
1060 g_defcodelabel (L1);
1061 AddCodeLine ("iny");
1062 AddCodeLine ("inx");
1063 AddCodeLine ("lda (sp),y");
1064 AddCodeLine ("sta %s,x", ED_GetLabelName (&Arg1.Expr, 0));
1066 AddCodeLine ("bne %s", LocalLabelName (L1));
1068 /* strcpy returns argument #1 */
1071 } else if (ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr) &&
1072 ED_IsRVal (&Arg1.Expr) && ED_IsLocStack (&Arg1.Expr) &&
1075 /* It is possible to just use one index register even if the stack
1076 ** offset is not zero, by adjusting the offset to the constant
1077 ** address accordingly. But we cannot do this if the data in
1078 ** question is in the register space or at an absolute address less
1079 ** than 256. Register space is zero page, which means that the
1080 ** address calculation could overflow in the linker.
1082 int AllowOneIndex = !ED_IsLocRegister (&Arg2.Expr) &&
1083 !(ED_IsLocAbs (&Arg2.Expr) && Arg2.Expr.IVal < 256);
1085 /* Calculate the real stack offset */
1086 int Offs = ED_GetStackOffs (&Arg1.Expr, 0);
1088 /* Drop the generated code */
1089 RemoveCode (&Arg1.Expr.Start);
1091 /* We need labels */
1092 L1 = GetLocalLabel ();
1094 /* Generate strcpy code */
1095 AddCodeLine ("ldy #$%02X", (unsigned char) (Offs - 1));
1096 if (Offs == 0 || AllowOneIndex) {
1097 g_defcodelabel (L1);
1098 AddCodeLine ("iny");
1099 AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg2.Expr, -Offs));
1100 AddCodeLine ("sta (sp),y");
1102 AddCodeLine ("ldx #$FF");
1103 g_defcodelabel (L1);
1104 AddCodeLine ("iny");
1105 AddCodeLine ("inx");
1106 AddCodeLine ("lda %s,x", ED_GetLabelName (&Arg2.Expr, 0));
1107 AddCodeLine ("sta (sp),y");
1109 AddCodeLine ("bne %s", LocalLabelName (L1));
1111 /* strcpy returns argument #1 */
1116 /* The function result is an rvalue in the primary register */
1117 ED_MakeRValExpr (Expr);
1118 Expr->Type = GetFuncReturn (Expr->Type);
1122 /* We expect the closing brace */
1128 /*****************************************************************************/
1130 /*****************************************************************************/
1134 static void StdFunc_strlen (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
1135 /* Handle the strlen function */
1137 static Type ArgType[] = { TYPE(T_PTR), TYPE(T_CHAR|T_QUAL_CONST), TYPE(T_END) };
1147 /* Setup the argument type string */
1148 ArgType[1].C = GetDefaultChar () | T_QUAL_CONST;
1150 /* Evaluate the parameter */
1153 /* Check if the argument is an array. If so, remember the element count.
1154 ** Otherwise set the element count to undefined.
1156 IsArray = IsTypeArray (Arg.Type);
1158 ECount = GetElementCount (Arg.Type);
1159 if (ECount == FLEXIBLE) {
1160 /* Treat as unknown */
1161 ECount = UNSPECIFIED;
1165 ECount = UNSPECIFIED;
1166 IsPtr = IsTypePtr (Arg.Type);
1169 /* Check if the elements of an array can be addressed by a byte sized
1170 ** index. This is true if the size of the array is known and less than
1173 IsByteIndex = (ECount != UNSPECIFIED && ECount < 256);
1175 /* Do type conversion */
1176 TypeConversion (&Arg, ArgType);
1178 /* If the expression is a literal, and if string literals are read
1179 ** only, we can calculate the length of the string and remove it
1180 ** from the literal pool. Otherwise we have to calculate the length
1183 if (ED_IsLocLiteral (&Arg) && IS_Get (&WritableStrings) == 0) {
1185 /* Constant string literal */
1186 ED_MakeConstAbs (Expr, GetLiteralSize (Arg.LVal) - 1, type_size_t);
1188 /* We don't need the literal any longer */
1189 ReleaseLiteral (Arg.LVal);
1191 /* We will inline strlen for arrays with constant addresses, if either the
1192 ** inlining was forced on the command line, or the array is smaller than
1193 ** 256, so the inlining is considered safe.
1195 } else if (ED_IsLocConst (&Arg) && IsArray &&
1196 (IS_Get (&InlineStdFuncs) || IsByteIndex)) {
1198 /* Generate the strlen code */
1199 L = GetLocalLabel ();
1200 AddCodeLine ("ldy #$FF");
1202 AddCodeLine ("iny");
1203 AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg, 0));
1204 AddCodeLine ("bne %s", LocalLabelName (L));
1205 AddCodeLine ("tax");
1206 AddCodeLine ("tya");
1208 /* The function result is an rvalue in the primary register */
1209 ED_MakeRValExpr (Expr);
1210 Expr->Type = type_size_t;
1212 /* We will inline strlen for arrays on the stack, if the array is
1213 ** completely within the reach of a byte sized index register.
1215 } else if (ED_IsLocStack (&Arg) && IsArray && IsByteIndex &&
1216 (Arg.IVal - StackPtr) + ECount < 256) {
1218 /* Calculate the true stack offset */
1219 int Offs = ED_GetStackOffs (&Arg, 0);
1221 /* Generate the strlen code */
1222 L = GetLocalLabel ();
1223 AddCodeLine ("ldx #$FF");
1224 AddCodeLine ("ldy #$%02X", (unsigned char) (Offs-1));
1226 AddCodeLine ("inx");
1227 AddCodeLine ("iny");
1228 AddCodeLine ("lda (sp),y");
1229 AddCodeLine ("bne %s", LocalLabelName (L));
1230 AddCodeLine ("txa");
1231 AddCodeLine ("ldx #$00");
1233 /* The function result is an rvalue in the primary register */
1234 ED_MakeRValExpr (Expr);
1235 Expr->Type = type_size_t;
1237 /* strlen for a string that is pointed to by a register variable will only
1238 ** get inlined if requested on the command line, since we cannot know how
1239 ** big the buffer actually is, so inlining is not always safe.
1241 } else if (ED_IsLocRegister (&Arg) && ED_IsLVal (&Arg) && IsPtr &&
1242 IS_Get (&InlineStdFuncs)) {
1244 /* Generate the strlen code */
1245 L = GetLocalLabel ();
1246 AddCodeLine ("ldy #$FF");
1248 AddCodeLine ("iny");
1249 AddCodeLine ("lda (%s),y", ED_GetLabelName (&Arg, 0));
1250 AddCodeLine ("bne %s", LocalLabelName (L));
1251 AddCodeLine ("tax");
1252 AddCodeLine ("tya");
1254 /* The function result is an rvalue in the primary register */
1255 ED_MakeRValExpr (Expr);
1256 Expr->Type = type_size_t;
1258 /* Last check: We will inline a generic strlen routine if inlining was
1259 ** requested on the command line, and the code size factor is more than
1260 ** 400 (code is 13 bytes vs. 3 for a jsr call).
1262 } else if (IS_Get (&CodeSizeFactor) > 400 && IS_Get (&InlineStdFuncs)) {
1264 /* Load the expression into the primary */
1265 LoadExpr (CF_NONE, &Arg);
1267 /* Inline the function */
1268 L = GetLocalLabel ();
1269 AddCodeLine ("sta ptr1");
1270 AddCodeLine ("stx ptr1+1");
1271 AddCodeLine ("ldy #$FF");
1273 AddCodeLine ("iny");
1274 AddCodeLine ("lda (ptr1),y");
1275 AddCodeLine ("bne %s", LocalLabelName (L));
1276 AddCodeLine ("tax");
1277 AddCodeLine ("tya");
1279 /* The function result is an rvalue in the primary register */
1280 ED_MakeRValExpr (Expr);
1281 Expr->Type = type_size_t;
1285 /* Load the expression into the primary */
1286 LoadExpr (CF_NONE, &Arg);
1288 /* Call the strlen function */
1289 AddCodeLine ("jsr _%s", Func_strlen);
1291 /* The function result is an rvalue in the primary register */
1292 ED_MakeRValExpr (Expr);
1293 Expr->Type = type_size_t;
1297 /* We expect the closing brace */
1303 /*****************************************************************************/
1305 /*****************************************************************************/
1309 int FindStdFunc (const char* Name)
1310 /* Determine if the given function is a known standard function that may be
1311 ** called in a special way. If so, return the index, otherwise return -1.
1314 /* Look into the table for known names */
1315 struct StdFuncDesc* D =
1316 bsearch (Name, StdFuncs, FUNC_COUNT, sizeof (StdFuncs[0]), CmpFunc);
1318 /* Return the function index or -1 */
1322 return D - StdFuncs;
1328 void HandleStdFunc (int Index, FuncDesc* F, ExprDesc* lval)
1329 /* Generate code for a known standard function. */
1331 struct StdFuncDesc* D;
1333 /* Get a pointer to the table entry */
1334 CHECK (Index >= 0 && Index < (int)FUNC_COUNT);
1335 D = StdFuncs + Index;
1337 /* Call the handler function */
1338 D->Handler (F, lval);