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;
208 ParseArg (&Arg1, Arg1Type);
209 g_push (Arg1.Flags, Arg1.Expr.IVal);
210 GetCodePos (&Arg1.End);
211 ParamSize += SizeOf (Arg1Type);
215 ParseArg (&Arg2, Arg2Type);
216 g_push (Arg2.Flags, Arg2.Expr.IVal);
217 GetCodePos (&Arg2.End);
218 ParamSize += SizeOf (Arg2Type);
221 /* Argument #3. Since memcpy is a fastcall function, we must load the
222 * arg into the primary if it is not already there. This parameter is
223 * also ignored for the calculation of the parameter size, since it is
224 * not passed via the stack.
226 ParseArg (&Arg3, Arg3Type);
227 if (Arg3.Flags & CF_CONST) {
228 LoadExpr (CF_NONE, &Arg3.Expr);
231 /* Emit the actual function call. This will also cleanup the stack. */
232 g_call (CF_FIXARGC, Func_memcpy, ParamSize);
234 if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal == 0) {
236 /* memcpy has been called with a count argument of zero */
237 Warning ("Call to memcpy has no effect");
239 /* Remove all of the generated code but the load of the first
240 * argument, which is what memcpy returns.
242 RemoveCode (&Arg1.Push);
244 /* Set the function result to the first argument */
247 /* Bail out, no need for further improvements */
251 /* We've generated the complete code for the function now and know the
252 * types of all parameters. Check for situations where better code can
253 * be generated. If such a situation is detected, throw away the
254 * generated, and emit better code.
256 if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
257 ((ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr)) ||
258 (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr))) &&
259 ((ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) ||
260 (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr)))) {
262 int Reg1 = ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr);
263 int Reg2 = ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr);
265 /* Drop the generated code */
266 RemoveCode (&Arg1.Expr.Start);
268 /* We need a label */
269 Label = GetLocalLabel ();
271 /* Generate memcpy code */
272 if (Arg3.Expr.IVal <= 127) {
274 AddCodeLine ("ldy #$%02X", (unsigned char) (Arg3.Expr.IVal-1));
275 AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
276 g_defcodelabel (Label);
278 AddCodeLine ("lda (%s),y", ED_GetLabelName (&Arg2.Expr, 0));
280 AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg2.Expr, 0));
283 AddCodeLine ("sta (%s),y", ED_GetLabelName (&Arg1.Expr, 0));
285 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, 0));
288 AddCodeLine ("bpl %s", LocalLabelName (Label));
292 AddCodeLine ("ldy #$00");
293 AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
294 g_defcodelabel (Label);
296 AddCodeLine ("lda (%s),y", ED_GetLabelName (&Arg2.Expr, 0));
298 AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg2.Expr, 0));
301 AddCodeLine ("sta (%s),y", ED_GetLabelName (&Arg1.Expr, 0));
303 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, 0));
306 AddCodeLine ("cpy #$%02X", (unsigned char) Arg3.Expr.IVal);
307 AddCodeLine ("bne %s", LocalLabelName (Label));
311 /* memcpy returns the address, so the result is actually identical
312 * to the first argument.
316 } else if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
317 ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr) &&
318 ED_IsRVal (&Arg1.Expr) && ED_IsLocStack (&Arg1.Expr) &&
319 (Arg1.Expr.IVal - StackPtr) + Arg3.Expr.IVal < 256) {
321 /* It is possible to just use one index register even if the stack
322 * offset is not zero, by adjusting the offset to the constant
323 * address accordingly. But we cannot do this if the data in
324 * question is in the register space or at an absolute address less
325 * than 256. Register space is zero page, which means that the
326 * address calculation could overflow in the linker.
328 int AllowOneIndex = !ED_IsLocRegister (&Arg2.Expr) &&
329 !(ED_IsLocAbs (&Arg2.Expr) && Arg2.Expr.IVal < 256);
331 /* Calculate the real stack offset */
332 Offs = ED_GetStackOffs (&Arg1.Expr, 0);
334 /* Drop the generated code */
335 RemoveCode (&Arg1.Expr.Start);
337 /* We need a label */
338 Label = GetLocalLabel ();
340 /* Generate memcpy code */
341 if (Arg3.Expr.IVal <= 127 && !AllowOneIndex) {
344 AddCodeLine ("ldy #$%02X", (unsigned char) (Offs + Arg3.Expr.IVal - 1));
345 g_defcodelabel (Label);
346 AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg2.Expr, -Offs));
347 AddCodeLine ("sta (sp),y");
349 AddCodeLine ("bpl %s", LocalLabelName (Label));
351 AddCodeLine ("ldx #$%02X", (unsigned char) (Arg3.Expr.IVal-1));
352 AddCodeLine ("ldy #$%02X", (unsigned char) (Offs + Arg3.Expr.IVal - 1));
353 g_defcodelabel (Label);
354 AddCodeLine ("lda %s,x", ED_GetLabelName (&Arg2.Expr, 0));
355 AddCodeLine ("sta (sp),y");
358 AddCodeLine ("bpl %s", LocalLabelName (Label));
363 if (Offs == 0 || AllowOneIndex) {
364 AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
365 g_defcodelabel (Label);
366 AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg2.Expr, -Offs));
367 AddCodeLine ("sta (sp),y");
369 AddCodeLine ("cpy #$%02X", (unsigned char) (Offs + Arg3.Expr.IVal));
370 AddCodeLine ("bne %s", LocalLabelName (Label));
372 AddCodeLine ("ldx #$00");
373 AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
374 g_defcodelabel (Label);
375 AddCodeLine ("lda %s,x", ED_GetLabelName (&Arg2.Expr, 0));
376 AddCodeLine ("sta (sp),y");
379 AddCodeLine ("cpx #$%02X", (unsigned char) Arg3.Expr.IVal);
380 AddCodeLine ("bne %s", LocalLabelName (Label));
385 /* memcpy returns the address, so the result is actually identical
386 * to the first argument.
390 } else if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
391 ED_IsRVal (&Arg2.Expr) && ED_IsLocStack (&Arg2.Expr) &&
392 (Arg2.Expr.IVal - StackPtr) + Arg3.Expr.IVal < 256 &&
393 ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) {
395 /* It is possible to just use one index register even if the stack
396 * offset is not zero, by adjusting the offset to the constant
397 * address accordingly. But we cannot do this if the data in
398 * question is in the register space or at an absolute address less
399 * than 256. Register space is zero page, which means that the
400 * address calculation could overflow in the linker.
402 int AllowOneIndex = !ED_IsLocRegister (&Arg1.Expr) &&
403 !(ED_IsLocAbs (&Arg1.Expr) && Arg1.Expr.IVal < 256);
405 /* Calculate the real stack offset */
406 Offs = ED_GetStackOffs (&Arg2.Expr, 0);
408 /* Drop the generated code */
409 RemoveCode (&Arg1.Expr.Start);
411 /* We need a label */
412 Label = GetLocalLabel ();
414 /* Generate memcpy code */
415 if (Arg3.Expr.IVal <= 127 && !AllowOneIndex) {
418 AddCodeLine ("ldy #$%02X", (unsigned char) (Arg3.Expr.IVal - 1));
419 g_defcodelabel (Label);
420 AddCodeLine ("lda (sp),y");
421 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, 0));
423 AddCodeLine ("bpl %s", LocalLabelName (Label));
425 AddCodeLine ("ldx #$%02X", (unsigned char) (Arg3.Expr.IVal-1));
426 AddCodeLine ("ldy #$%02X", (unsigned char) (Offs + Arg3.Expr.IVal - 1));
427 g_defcodelabel (Label);
428 AddCodeLine ("lda (sp),y");
429 AddCodeLine ("sta %s,x", ED_GetLabelName (&Arg1.Expr, 0));
432 AddCodeLine ("bpl %s", LocalLabelName (Label));
437 if (Offs == 0 || AllowOneIndex) {
438 AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
439 g_defcodelabel (Label);
440 AddCodeLine ("lda (sp),y");
441 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, -Offs));
443 AddCodeLine ("cpy #$%02X", (unsigned char) (Offs + Arg3.Expr.IVal));
444 AddCodeLine ("bne %s", LocalLabelName (Label));
446 AddCodeLine ("ldx #$00");
447 AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
448 g_defcodelabel (Label);
449 AddCodeLine ("lda (sp),y");
450 AddCodeLine ("sta %s,x", ED_GetLabelName (&Arg1.Expr, 0));
453 AddCodeLine ("cpx #$%02X", (unsigned char) Arg3.Expr.IVal);
454 AddCodeLine ("bne %s", LocalLabelName (Label));
459 /* memcpy returns the address, so the result is actually identical
460 * to the first argument.
464 } else if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
465 ED_IsRVal (&Arg2.Expr) && ED_IsLocStack (&Arg2.Expr) &&
466 (Offs = ED_GetStackOffs (&Arg2.Expr, 0)) == 0) {
468 /* Drop the generated code but leave the load of the first argument*/
469 RemoveCode (&Arg1.Push);
471 /* We need a label */
472 Label = GetLocalLabel ();
474 /* Generate memcpy code */
475 AddCodeLine ("sta ptr1");
476 AddCodeLine ("stx ptr1+1");
477 if (Arg3.Expr.IVal <= 127) {
478 AddCodeLine ("ldy #$%02X", (unsigned char) (Arg3.Expr.IVal - 1));
479 g_defcodelabel (Label);
480 AddCodeLine ("lda (sp),y");
481 AddCodeLine ("sta (ptr1),y");
483 AddCodeLine ("bpl %s", LocalLabelName (Label));
485 AddCodeLine ("ldy #$00");
486 g_defcodelabel (Label);
487 AddCodeLine ("lda (sp),y");
488 AddCodeLine ("sta (ptr1),y");
490 AddCodeLine ("cpy #$%02X", (unsigned char) Arg3.Expr.IVal);
491 AddCodeLine ("bne %s", LocalLabelName (Label));
494 /* Reload result - X hasn't changed by the code above */
495 AddCodeLine ("lda ptr1");
497 /* The function result is an rvalue in the primary register */
498 ED_MakeRValExpr (Expr);
499 Expr->Type = GetFuncReturn (Expr->Type);
503 /* The function result is an rvalue in the primary register */
504 ED_MakeRValExpr (Expr);
505 Expr->Type = GetFuncReturn (Expr->Type);
510 /* We expect the closing brace */
516 /*****************************************************************************/
518 /*****************************************************************************/
522 static void StdFunc_memset (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
523 /* Handle the memset function */
525 /* Argument types: (void*, int, size_t) */
526 static Type Arg1Type[] = { TYPE(T_PTR), TYPE(T_VOID), TYPE(T_END) };
527 static Type Arg2Type[] = { TYPE(T_INT), TYPE(T_END) };
528 static Type Arg3Type[] = { TYPE(T_SIZE_T), TYPE(T_END) };
530 ArgDesc Arg1, Arg2, Arg3;
531 int MemSet = 1; /* Use real memset if true */
532 unsigned ParamSize = 0;
536 ParseArg (&Arg1, Arg1Type);
537 g_push (Arg1.Flags, Arg1.Expr.IVal);
538 GetCodePos (&Arg1.End);
539 ParamSize += SizeOf (Arg1Type);
542 /* Argument #2. This argument is special in that we will call another
543 * function if it is a constant zero.
545 ParseArg (&Arg2, Arg2Type);
546 if ((Arg2.Flags & CF_CONST) != 0 && Arg2.Expr.IVal == 0) {
547 /* Don't call memset, call bzero instead */
550 /* Push the argument */
551 g_push (Arg2.Flags, Arg2.Expr.IVal);
552 GetCodePos (&Arg2.End);
553 ParamSize += SizeOf (Arg2Type);
557 /* Argument #3. Since memset is a fastcall function, we must load the
558 * arg into the primary if it is not already there. This parameter is
559 * also ignored for the calculation of the parameter size, since it is
560 * not passed via the stack.
562 ParseArg (&Arg3, Arg3Type);
563 if (Arg3.Flags & CF_CONST) {
564 LoadExpr (CF_NONE, &Arg3.Expr);
567 /* Emit the actual function call. This will also cleanup the stack. */
568 g_call (CF_FIXARGC, MemSet? Func_memset : Func__bzero, ParamSize);
570 if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal == 0) {
572 /* memset has been called with a count argument of zero */
573 Warning ("Call to memset has no effect");
575 /* Remove all of the generated code but the load of the first
576 * argument, which is what memset returns.
578 RemoveCode (&Arg1.Push);
580 /* Set the function result to the first argument */
583 /* Bail out, no need for further improvements */
587 /* We've generated the complete code for the function now and know the
588 * types of all parameters. Check for situations where better code can
589 * be generated. If such a situation is detected, throw away the
590 * generated, and emit better code.
591 * Note: Lots of improvements would be possible here, but I will
592 * concentrate on the most common case: memset with arguments 2 and 3
593 * being constant numerical values. Some checks have shown that this
594 * covers nearly 90% of all memset calls.
596 if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
597 ED_IsConstAbsInt (&Arg2.Expr) &&
598 ((ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) ||
599 (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr)))) {
601 int Reg = ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr);
603 /* Drop the generated code */
604 RemoveCode (&Arg1.Expr.Start);
606 /* We need a label */
607 Label = GetLocalLabel ();
609 /* Generate memset code */
610 if (Arg3.Expr.IVal <= 127) {
612 AddCodeLine ("ldy #$%02X", (unsigned char) (Arg3.Expr.IVal-1));
613 AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
614 g_defcodelabel (Label);
616 AddCodeLine ("sta (%s),y", ED_GetLabelName (&Arg1.Expr, 0));
618 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, 0));
621 AddCodeLine ("bpl %s", LocalLabelName (Label));
625 AddCodeLine ("ldy #$00");
626 AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
627 g_defcodelabel (Label);
629 AddCodeLine ("sta (%s),y", ED_GetLabelName (&Arg1.Expr, 0));
631 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, 0));
634 AddCodeLine ("cpy #$%02X", (unsigned char) Arg3.Expr.IVal);
635 AddCodeLine ("bne %s", LocalLabelName (Label));
639 /* memset returns the address, so the result is actually identical
640 * to the first argument.
644 } else if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
645 ED_IsConstAbsInt (&Arg2.Expr) &&
646 ED_IsRVal (&Arg1.Expr) && ED_IsLocStack (&Arg1.Expr) &&
647 (Arg1.Expr.IVal - StackPtr) + Arg3.Expr.IVal < 256) {
649 /* Calculate the real stack offset */
650 int Offs = ED_GetStackOffs (&Arg1.Expr, 0);
652 /* Drop the generated code */
653 RemoveCode (&Arg1.Expr.Start);
655 /* We need a label */
656 Label = GetLocalLabel ();
658 /* Generate memset code */
659 AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
660 AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
661 g_defcodelabel (Label);
662 AddCodeLine ("sta (sp),y");
664 AddCodeLine ("cpy #$%02X", (unsigned char) (Offs + Arg3.Expr.IVal));
665 AddCodeLine ("bne %s", LocalLabelName (Label));
667 /* memset returns the address, so the result is actually identical
668 * to the first argument.
672 } else if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
673 ED_IsConstAbsInt (&Arg2.Expr) &&
674 (Arg2.Expr.IVal != 0 || IS_Get (&CodeSizeFactor) > 200)) {
676 /* Remove all of the generated code but the load of the first
679 RemoveCode (&Arg1.Push);
681 /* We need a label */
682 Label = GetLocalLabel ();
685 AddCodeLine ("sta ptr1");
686 AddCodeLine ("stx ptr1+1");
687 if (Arg3.Expr.IVal <= 127) {
688 AddCodeLine ("ldy #$%02X", (unsigned char) (Arg3.Expr.IVal-1));
689 AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
690 g_defcodelabel (Label);
691 AddCodeLine ("sta (ptr1),y");
693 AddCodeLine ("bpl %s", LocalLabelName (Label));
695 AddCodeLine ("ldy #$00");
696 AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
697 g_defcodelabel (Label);
698 AddCodeLine ("sta (ptr1),y");
700 AddCodeLine ("cpy #$%02X", (unsigned char) Arg3.Expr.IVal);
701 AddCodeLine ("bne %s", LocalLabelName (Label));
704 /* Load the function result pointer into a/x (x is still valid). This
705 * code will get removed by the optimizer if it is not used later.
707 AddCodeLine ("lda ptr1");
709 /* The function result is an rvalue in the primary register */
710 ED_MakeRValExpr (Expr);
711 Expr->Type = GetFuncReturn (Expr->Type);
715 /* The function result is an rvalue in the primary register */
716 ED_MakeRValExpr (Expr);
717 Expr->Type = GetFuncReturn (Expr->Type);
722 /* We expect the closing brace */
728 /*****************************************************************************/
730 /*****************************************************************************/
734 static void StdFunc_strcmp (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
735 /* Handle the strcmp function */
737 /* Argument types: (const char*, const char*) */
738 static Type Arg1Type[] = { TYPE(T_PTR), TYPE(T_CHAR|T_QUAL_CONST), TYPE(T_END) };
739 static Type Arg2Type[] = { TYPE(T_PTR), TYPE(T_CHAR|T_QUAL_CONST), TYPE(T_END) };
742 unsigned ParamSize = 0;
748 /* Setup the argument type string */
749 Arg1Type[1].C = GetDefaultChar () | T_QUAL_CONST;
750 Arg2Type[1].C = GetDefaultChar () | T_QUAL_CONST;
753 ParseArg (&Arg1, Arg1Type);
754 g_push (Arg1.Flags, Arg1.Expr.IVal);
755 ParamSize += SizeOf (Arg1Type);
759 ParseArg (&Arg2, Arg2Type);
761 /* Since strcmp is a fastcall function, we must load the
762 * arg into the primary if it is not already there. This parameter is
763 * also ignored for the calculation of the parameter size, since it is
764 * not passed via the stack.
766 if (Arg2.Flags & CF_CONST) {
767 LoadExpr (CF_NONE, &Arg2.Expr);
770 /* Emit the actual function call. This will also cleanup the stack. */
771 g_call (CF_FIXARGC, Func_strcmp, ParamSize);
773 /* Get the element counts of the arguments. Then get the larger of the
774 * two into ECount1. This removes FLEXIBLE and UNSPECIFIED automatically
776 ECount1 = ArrayElementCount (&Arg1);
777 ECount2 = ArrayElementCount (&Arg2);
778 if (ECount2 > ECount1) {
782 /* If the second argument is the empty string literal, we can generate
783 * more efficient code.
785 if (ED_IsLocLiteral (&Arg2.Expr) &&
786 IS_Get (&WritableStrings) == 0 &&
787 GetLiteralSize (Arg2.Expr.LVal) == 1 &&
788 GetLiteralStr (Arg2.Expr.LVal)[0] == '\0') {
790 /* Drop the generated code so we have the first argument in the
793 RemoveCode (&Arg1.Push);
795 /* We don't need the literal any longer */
796 ReleaseLiteral (Arg2.Expr.LVal);
798 /* We do now have Arg1 in the primary. Load the first character from
799 * this string and cast to int. This is the function result.
801 IsArray = IsTypeArray (Arg1.Type) && ED_IsRVal (&Arg1.Expr);
802 if (IsArray && ED_IsLocStack (&Arg1.Expr) &&
803 (Offs = ED_GetStackOffs (&Arg1.Expr, 0) < 256)) {
804 /* Drop the generated code */
805 RemoveCode (&Arg1.Load);
808 AddCodeLine ("ldy #$%02X", Offs);
809 AddCodeLine ("ldx #$00");
810 AddCodeLine ("lda (sp),y");
811 } else if (IsArray && ED_IsLocConst (&Arg1.Expr)) {
812 /* Drop the generated code */
813 RemoveCode (&Arg1.Load);
816 AddCodeLine ("ldx #$00");
817 AddCodeLine ("lda %s", ED_GetLabelName (&Arg1.Expr, 0));
819 /* Drop part of the generated code so we have the first argument
822 RemoveCode (&Arg1.Push);
824 /* Fetch the first char */
825 g_getind (CF_CHAR | CF_UNSIGNED, 0);
828 } else if ((IS_Get (&CodeSizeFactor) >= 165) &&
829 ((ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr)) ||
830 (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr))) &&
831 ((ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) ||
832 (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr))) &&
833 (IS_Get (&InlineStdFuncs) || (ECount1 > 0 && ECount1 < 256))) {
836 unsigned Entry, Loop, Fin; /* Labels */
840 if (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr)) {
845 if (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr)) {
846 Compare = "cmp (%s),y";
848 Compare = "cmp %s,y";
851 /* Drop the generated code */
852 RemoveCode (&Arg1.Expr.Start);
855 Entry = GetLocalLabel ();
856 Loop = GetLocalLabel ();
857 Fin = GetLocalLabel ();
859 /* Generate strcmp code */
860 AddCodeLine ("ldy #$00");
861 AddCodeLine ("beq %s", LocalLabelName (Entry));
862 g_defcodelabel (Loop);
864 AddCodeLine ("beq %s", LocalLabelName (Fin));
866 g_defcodelabel (Entry);
867 AddCodeLine (Load, ED_GetLabelName (&Arg1.Expr, 0));
868 AddCodeLine (Compare, ED_GetLabelName (&Arg2.Expr, 0));
869 AddCodeLine ("beq %s", LocalLabelName (Loop));
870 AddCodeLine ("ldx #$01");
871 AddCodeLine ("bcs %s", LocalLabelName (Fin));
872 AddCodeLine ("ldx #$FF");
873 g_defcodelabel (Fin);
875 } else if ((IS_Get (&CodeSizeFactor) > 190) &&
876 ((ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr)) ||
877 (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr))) &&
878 (IS_Get (&InlineStdFuncs) || (ECount1 > 0 && ECount1 < 256))) {
881 unsigned Entry, Loop, Fin; /* Labels */
884 if (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr)) {
885 Compare = "cmp (%s),y";
887 Compare = "cmp %s,y";
890 /* Drop the generated code */
891 RemoveCode (&Arg1.Push);
894 Entry = GetLocalLabel ();
895 Loop = GetLocalLabel ();
896 Fin = GetLocalLabel ();
898 /* Store Arg1 into ptr1 */
899 AddCodeLine ("sta ptr1");
900 AddCodeLine ("stx ptr1+1");
902 /* Generate strcmp code */
903 AddCodeLine ("ldy #$00");
904 AddCodeLine ("beq %s", LocalLabelName (Entry));
905 g_defcodelabel (Loop);
907 AddCodeLine ("beq %s", LocalLabelName (Fin));
909 g_defcodelabel (Entry);
910 AddCodeLine ("lda (ptr1),y");
911 AddCodeLine (Compare, ED_GetLabelName (&Arg2.Expr, 0));
912 AddCodeLine ("beq %s", LocalLabelName (Loop));
913 AddCodeLine ("ldx #$01");
914 AddCodeLine ("bcs %s", LocalLabelName (Fin));
915 AddCodeLine ("ldx #$FF");
916 g_defcodelabel (Fin);
920 /* The function result is an rvalue in the primary register */
921 ED_MakeRValExpr (Expr);
922 Expr->Type = GetFuncReturn (Expr->Type);
924 /* We expect the closing brace */
930 /*****************************************************************************/
932 /*****************************************************************************/
936 static void StdFunc_strcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
937 /* Handle the strcpy function */
939 /* Argument types: (char*, const char*) */
940 static Type Arg1Type[] = { TYPE(T_PTR), TYPE(T_CHAR), TYPE(T_END) };
941 static Type Arg2Type[] = { TYPE(T_PTR), TYPE(T_CHAR|T_QUAL_CONST), TYPE(T_END) };
944 unsigned ParamSize = 0;
948 /* Setup the argument type string */
949 Arg1Type[1].C = GetDefaultChar ();
950 Arg2Type[1].C = GetDefaultChar () | T_QUAL_CONST;
953 ParseArg (&Arg1, Arg1Type);
954 g_push (Arg1.Flags, Arg1.Expr.IVal);
955 GetCodePos (&Arg1.End);
956 ParamSize += SizeOf (Arg1Type);
959 /* Argument #2. Since strcpy is a fastcall function, we must load the
960 * arg into the primary if it is not already there. This parameter is
961 * also ignored for the calculation of the parameter size, since it is
962 * not passed via the stack.
964 ParseArg (&Arg2, Arg2Type);
965 if (Arg2.Flags & CF_CONST) {
966 LoadExpr (CF_NONE, &Arg2.Expr);
969 /* Emit the actual function call. This will also cleanup the stack. */
970 g_call (CF_FIXARGC, Func_strcpy, ParamSize);
972 /* Get the element count of argument 1 if it is an array */
973 ECount = ArrayElementCount (&Arg1);
975 /* We've generated the complete code for the function now and know the
976 * types of all parameters. Check for situations where better code can
977 * be generated. If such a situation is detected, throw away the
978 * generated, and emit better code.
980 if (((ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr)) ||
981 (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr))) &&
982 ((ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) ||
983 (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr))) &&
984 (IS_Get (&InlineStdFuncs) ||
985 (ECount != UNSPECIFIED && ECount < 256))) {
989 if (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr)) {
994 if (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr)) {
995 Store = "sta (%s),y";
1000 /* Drop the generated code */
1001 RemoveCode (&Arg1.Expr.Start);
1003 /* We need labels */
1004 L1 = GetLocalLabel ();
1006 /* Generate strcpy code */
1007 AddCodeLine ("ldy #$FF");
1008 g_defcodelabel (L1);
1009 AddCodeLine ("iny");
1010 AddCodeLine (Load, ED_GetLabelName (&Arg2.Expr, 0));
1011 AddCodeLine (Store, ED_GetLabelName (&Arg1.Expr, 0));
1012 AddCodeLine ("bne %s", LocalLabelName (L1));
1014 /* strcpy returns argument #1 */
1017 } else if (ED_IsRVal (&Arg2.Expr) && ED_IsLocStack (&Arg2.Expr) &&
1019 ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) {
1021 /* It is possible to just use one index register even if the stack
1022 * offset is not zero, by adjusting the offset to the constant
1023 * address accordingly. But we cannot do this if the data in
1024 * question is in the register space or at an absolute address less
1025 * than 256. Register space is zero page, which means that the
1026 * address calculation could overflow in the linker.
1028 int AllowOneIndex = !ED_IsLocRegister (&Arg1.Expr) &&
1029 !(ED_IsLocAbs (&Arg1.Expr) && Arg1.Expr.IVal < 256);
1031 /* Calculate the real stack offset */
1032 int Offs = ED_GetStackOffs (&Arg2.Expr, 0);
1034 /* Drop the generated code */
1035 RemoveCode (&Arg1.Expr.Start);
1037 /* We need labels */
1038 L1 = GetLocalLabel ();
1040 /* Generate strcpy code */
1041 AddCodeLine ("ldy #$%02X", (unsigned char) (Offs - 1));
1042 if (Offs == 0 || AllowOneIndex) {
1043 g_defcodelabel (L1);
1044 AddCodeLine ("iny");
1045 AddCodeLine ("lda (sp),y");
1046 AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, -Offs));
1048 AddCodeLine ("ldx #$FF");
1049 g_defcodelabel (L1);
1050 AddCodeLine ("iny");
1051 AddCodeLine ("inx");
1052 AddCodeLine ("lda (sp),y");
1053 AddCodeLine ("sta %s,x", ED_GetLabelName (&Arg1.Expr, 0));
1055 AddCodeLine ("bne %s", LocalLabelName (L1));
1057 /* strcpy returns argument #1 */
1060 } else if (ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr) &&
1061 ED_IsRVal (&Arg1.Expr) && ED_IsLocStack (&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 (&Arg2.Expr) &&
1072 !(ED_IsLocAbs (&Arg2.Expr) && Arg2.Expr.IVal < 256);
1074 /* Calculate the real stack offset */
1075 int Offs = ED_GetStackOffs (&Arg1.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 %s,y", ED_GetLabelName (&Arg2.Expr, -Offs));
1089 AddCodeLine ("sta (sp),y");
1091 AddCodeLine ("ldx #$FF");
1092 g_defcodelabel (L1);
1093 AddCodeLine ("iny");
1094 AddCodeLine ("inx");
1095 AddCodeLine ("lda %s,x", ED_GetLabelName (&Arg2.Expr, 0));
1096 AddCodeLine ("sta (sp),y");
1098 AddCodeLine ("bne %s", LocalLabelName (L1));
1100 /* strcpy returns argument #1 */
1105 /* The function result is an rvalue in the primary register */
1106 ED_MakeRValExpr (Expr);
1107 Expr->Type = GetFuncReturn (Expr->Type);
1111 /* We expect the closing brace */
1117 /*****************************************************************************/
1119 /*****************************************************************************/
1123 static void StdFunc_strlen (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
1124 /* Handle the strlen function */
1126 static Type ArgType[] = { TYPE(T_PTR), TYPE(T_CHAR|T_QUAL_CONST), TYPE(T_END) };
1136 /* Setup the argument type string */
1137 ArgType[1].C = GetDefaultChar () | T_QUAL_CONST;
1139 /* Evaluate the parameter */
1142 /* Check if the argument is an array. If so, remember the element count.
1143 * Otherwise set the element count to undefined.
1145 IsArray = IsTypeArray (Arg.Type);
1147 ECount = GetElementCount (Arg.Type);
1148 if (ECount == FLEXIBLE) {
1149 /* Treat as unknown */
1150 ECount = UNSPECIFIED;
1154 ECount = UNSPECIFIED;
1155 IsPtr = IsTypePtr (Arg.Type);
1158 /* Check if the elements of an array can be addressed by a byte sized
1159 * index. This is true if the size of the array is known and less than
1162 IsByteIndex = (ECount != UNSPECIFIED && ECount < 256);
1164 /* Do type conversion */
1165 TypeConversion (&Arg, ArgType);
1167 /* If the expression is a literal, and if string literals are read
1168 * only, we can calculate the length of the string and remove it
1169 * from the literal pool. Otherwise we have to calculate the length
1172 if (ED_IsLocLiteral (&Arg) && IS_Get (&WritableStrings) == 0) {
1174 /* Constant string literal */
1175 ED_MakeConstAbs (Expr, GetLiteralSize (Arg.LVal) - 1, type_size_t);
1177 /* We don't need the literal any longer */
1178 ReleaseLiteral (Arg.LVal);
1180 /* We will inline strlen for arrays with constant addresses, if either the
1181 * inlining was forced on the command line, or the array is smaller than
1182 * 256, so the inlining is considered safe.
1184 } else if (ED_IsLocConst (&Arg) && IsArray &&
1185 (IS_Get (&InlineStdFuncs) || IsByteIndex)) {
1187 /* Generate the strlen code */
1188 L = GetLocalLabel ();
1189 AddCodeLine ("ldy #$FF");
1191 AddCodeLine ("iny");
1192 AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg, 0));
1193 AddCodeLine ("bne %s", LocalLabelName (L));
1194 AddCodeLine ("tax");
1195 AddCodeLine ("tya");
1197 /* The function result is an rvalue in the primary register */
1198 ED_MakeRValExpr (Expr);
1199 Expr->Type = type_size_t;
1201 /* We will inline strlen for arrays on the stack, if the array is
1202 * completely within the reach of a byte sized index register.
1204 } else if (ED_IsLocStack (&Arg) && IsArray && IsByteIndex &&
1205 (Arg.IVal - StackPtr) + ECount < 256) {
1207 /* Calculate the true stack offset */
1208 int Offs = ED_GetStackOffs (&Arg, 0);
1210 /* Generate the strlen code */
1211 L = GetLocalLabel ();
1212 AddCodeLine ("ldx #$FF");
1213 AddCodeLine ("ldy #$%02X", (unsigned char) (Offs-1));
1215 AddCodeLine ("inx");
1216 AddCodeLine ("iny");
1217 AddCodeLine ("lda (sp),y");
1218 AddCodeLine ("bne %s", LocalLabelName (L));
1219 AddCodeLine ("txa");
1220 AddCodeLine ("ldx #$00");
1222 /* The function result is an rvalue in the primary register */
1223 ED_MakeRValExpr (Expr);
1224 Expr->Type = type_size_t;
1226 /* strlen for a string that is pointed to by a register variable will only
1227 * get inlined if requested on the command line, since we cannot know how
1228 * big the buffer actually is, so inlining is not always safe.
1230 } else if (ED_IsLocRegister (&Arg) && ED_IsLVal (&Arg) && IsPtr &&
1231 IS_Get (&InlineStdFuncs)) {
1233 /* Generate the strlen code */
1234 L = GetLocalLabel ();
1235 AddCodeLine ("ldy #$FF");
1237 AddCodeLine ("iny");
1238 AddCodeLine ("lda (%s),y", ED_GetLabelName (&Arg, 0));
1239 AddCodeLine ("bne %s", LocalLabelName (L));
1240 AddCodeLine ("tax");
1241 AddCodeLine ("tya");
1243 /* The function result is an rvalue in the primary register */
1244 ED_MakeRValExpr (Expr);
1245 Expr->Type = type_size_t;
1247 /* Last check: We will inline a generic strlen routine if inlining was
1248 * requested on the command line, and the code size factor is more than
1249 * 400 (code is 13 bytes vs. 3 for a jsr call).
1251 } else if (IS_Get (&CodeSizeFactor) > 400 && IS_Get (&InlineStdFuncs)) {
1253 /* Load the expression into the primary */
1254 LoadExpr (CF_NONE, &Arg);
1256 /* Inline the function */
1257 L = GetLocalLabel ();
1258 AddCodeLine ("sta ptr1");
1259 AddCodeLine ("stx ptr1+1");
1260 AddCodeLine ("ldy #$FF");
1262 AddCodeLine ("iny");
1263 AddCodeLine ("lda (ptr1),y");
1264 AddCodeLine ("bne %s", LocalLabelName (L));
1265 AddCodeLine ("tax");
1266 AddCodeLine ("tya");
1268 /* The function result is an rvalue in the primary register */
1269 ED_MakeRValExpr (Expr);
1270 Expr->Type = type_size_t;
1274 /* Load the expression into the primary */
1275 LoadExpr (CF_NONE, &Arg);
1277 /* Call the strlen function */
1278 AddCodeLine ("jsr _%s", Func_strlen);
1280 /* The function result is an rvalue in the primary register */
1281 ED_MakeRValExpr (Expr);
1282 Expr->Type = type_size_t;
1286 /* We expect the closing brace */
1292 /*****************************************************************************/
1294 /*****************************************************************************/
1298 int FindStdFunc (const char* Name)
1299 /* Determine if the given function is a known standard function that may be
1300 * called in a special way. If so, return the index, otherwise return -1.
1303 /* Look into the table for known names */
1304 struct StdFuncDesc* D =
1305 bsearch (Name, StdFuncs, FUNC_COUNT, sizeof (StdFuncs[0]), CmpFunc);
1307 /* Return the function index or -1 */
1311 return D - StdFuncs;
1317 void HandleStdFunc (int Index, FuncDesc* F, ExprDesc* lval)
1318 /* Generate code for a known standard function. */
1320 struct StdFuncDesc* D;
1322 /* Get a pointer to the table entry */
1323 CHECK (Index >= 0 && Index < (int)FUNC_COUNT);
1324 D = StdFuncs + Index;
1326 /* Call the handler function */
1327 D->Handler (F, lval);