#include "check.h"
/* cc65 */
+#include "asmcode.h"
#include "asmlabel.h"
#include "codegen.h"
#include "error.h"
#include "global.h"
#include "litpool.h"
#include "scanner.h"
+#include "stackptr.h"
#include "stdfunc.h"
#include "stdnames.h"
#include "typeconv.h"
+static void StdFunc_memcpy (FuncDesc*, ExprDesc*);
static void StdFunc_memset (FuncDesc*, ExprDesc*);
+static void StdFunc_strcpy (FuncDesc*, ExprDesc*);
static void StdFunc_strlen (FuncDesc*, ExprDesc*);
/*****************************************************************************/
-/* Data */
+/* Data */
/*****************************************************************************/
const char* Name;
void (*Handler) (FuncDesc*, ExprDesc*);
} StdFuncs[] = {
+ { "memcpy", StdFunc_memcpy },
{ "memset", StdFunc_memset },
+ { "strcpy", StdFunc_strcpy },
{ "strlen", StdFunc_strlen },
};
#define FUNC_COUNT (sizeof (StdFuncs) / sizeof (StdFuncs[0]))
+typedef struct ArgDesc ArgDesc;
+struct ArgDesc {
+ const type* ArgType; /* Required argument type */
+ ExprDesc Expr; /* Argument expression */
+ const type* Type; /* The original type before conversion */
+ CodeMark Start; /* Start of the code for calculation */
+ CodeMark Push; /* Start of argument push code */
+ CodeMark End; /* End of the code for calculation+push */
+ unsigned Flags; /* Code generation flags */
+};
+
/*****************************************************************************/
-static unsigned ParseArg (type* Type, ExprDesc* Arg)
-/* Parse one argument but do not push it onto the stack. Return the code
- * generator flags needed to do the actual push.
+static long ArrayElementCount (const ArgDesc* Arg)
+/* Check if the type of the given argument is an array. If so, and if the
+ * element count is known, return it. In all other cases, return UNSPECIFIED.
+ */
+{
+ long Count;
+
+ if (IsTypeArray (Arg->Type)) {
+ Count = GetElementCount (Arg->Type);
+ if (Count == FLEXIBLE) {
+ /* Treat as unknown */
+ Count = UNSPECIFIED;
+ }
+ } else {
+ Count = UNSPECIFIED;
+ }
+ return Count;
+}
+
+
+
+static void ParseArg (ArgDesc* Arg, type* Type)
+/* Parse one argument but do not push it onto the stack. Make all fields in
+ * Arg valid.
*/
{
/* We have a prototype, so chars may be pushed as chars */
- unsigned Flags = CF_FORCECHAR;
+ Arg->Flags = CF_FORCECHAR;
+
+ /* Remember the required argument type */
+ Arg->ArgType = Type;
+
+ /* Remember the current code position */
+ Arg->Start = GetCodePos ();
/* Read the expression we're going to pass to the function */
- hie1 (Arg);
+ ExprWithCheck (hie1, &Arg->Expr);
+
+ /* Remember the actual argument type */
+ Arg->Type = Arg->Expr.Type;
/* Convert this expression to the expected type */
- TypeConversion (Arg, Type);
+ TypeConversion (&Arg->Expr, Type);
/* If the value is a constant, set the flag, otherwise load it into the
* primary register.
*/
- if (ED_IsConstAbsInt (Arg)) {
+ if (ED_IsConstAbsInt (&Arg->Expr)) {
/* Remember that we have a constant value */
- Flags |= CF_CONST;
+ Arg->Flags |= CF_CONST;
} else {
/* Load into the primary */
- ExprLoad (CF_NONE, Arg);
- ED_MakeRVal (Arg);
+ ExprLoad (CF_NONE, &Arg->Expr);
}
+ /* Remember the following code position */
+ Arg->End = Arg->Push = GetCodePos ();
+
/* Use the type of the argument for the push */
- return (Flags | TypeOf (Arg->Type));
+ Arg->Flags |= TypeOf (Arg->Expr.Type);
+}
+
+
+
+/*****************************************************************************/
+/* memcpy */
+/*****************************************************************************/
+
+
+
+static void StdFunc_memcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
+/* Handle the memcpy function */
+{
+ /* Argument types */
+ static type Arg1Type[] = { T_PTR, T_VOID, T_END }; /* void* */
+ static type Arg2Type[] = { T_PTR, T_VOID|T_QUAL_CONST, T_END }; /* const void* */
+ static type Arg3Type[] = { T_SIZE_T, T_END }; /* size_t */
+
+ CodeMark Start;
+ ArgDesc Arg1, Arg2, Arg3;
+ unsigned ParamSize = 0;
+ unsigned Label;
+
+ /* Remember where we are now */
+ Start = GetCodePos ();
+
+ /* Argument #1 */
+ ParseArg (&Arg1, Arg1Type);
+ g_push (Arg1.Flags, Arg1.Expr.Val);
+ Arg1.End = GetCodePos ();
+ ParamSize += SizeOf (Arg1Type);
+ ConsumeComma ();
+
+ /* Argument #2 */
+ ParseArg (&Arg2, Arg2Type);
+ g_push (Arg2.Flags, Arg2.Expr.Val);
+ Arg2.End = GetCodePos ();
+ ParamSize += SizeOf (Arg2Type);
+ ConsumeComma ();
+
+ /* Argument #3. Since memcpy is a fastcall function, we must load the
+ * arg into the primary if it is not already there. This parameter is
+ * also ignored for the calculation of the parameter size, since it is
+ * not passed via the stack.
+ */
+ ParseArg (&Arg3, Arg3Type);
+ if (Arg3.Flags & CF_CONST) {
+ ExprLoad (CF_FORCECHAR, &Arg3.Expr);
+ }
+
+ /* Emit the actual function call. This will also cleanup the stack. */
+ g_call (CF_FIXARGC, Func_memcpy, ParamSize);
+
+ if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.Val == 0) {
+
+ /* memcpy has been called with a count argument of zero */
+ Warning ("Call to memcpy has no effect");
+
+ /* Remove all of the generated code but the load of the first
+ * argument, which is what memcpy returns.
+ */
+ RemoveCode (Arg1.Push);
+
+ /* Set the function result to the first argument */
+ *Expr = Arg1.Expr;
+
+ /* Bail out, no need for further improvements */
+ goto ExitPoint;
+ }
+
+ /* We've generated the complete code for the function now and know the
+ * types of all parameters. Check for situations where better code can
+ * be generated. If such a situation is detected, throw away the
+ * generated, and emit better code.
+ */
+ if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.Val <= 256 &&
+ ((ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr)) ||
+ (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr))) &&
+ ((ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) ||
+ (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr)))) {
+
+ int Reg1 = ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr);
+ int Reg2 = ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr);
+
+ /* Drop the generated code */
+ RemoveCode (Start);
+
+ /* We need a label */
+ Label = GetLocalLabel ();
+
+ /* Generate memcpy code */
+ if (Arg3.Expr.Val <= 127) {
+
+ AddCodeLine ("ldy #$%02X", (unsigned char) (Arg3.Expr.Val-1));
+ AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.Val);
+ g_defcodelabel (Label);
+ if (Reg2) {
+ AddCodeLine ("lda (%s),y", ED_GetLabelName (&Arg2.Expr, 0));
+ } else {
+ AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg2.Expr, 0));
+ }
+ if (Reg1) {
+ AddCodeLine ("sta (%s),y", ED_GetLabelName (&Arg1.Expr, 0));
+ } else {
+ AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, 0));
+ }
+ AddCodeLine ("dey");
+ AddCodeLine ("bpl %s", LocalLabelName (Label));
+
+ } else {
+
+ AddCodeLine ("ldy #$00");
+ AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.Val);
+ g_defcodelabel (Label);
+ if (Reg2) {
+ AddCodeLine ("lda (%s),y", ED_GetLabelName (&Arg2.Expr, 0));
+ } else {
+ AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg2.Expr, 0));
+ }
+ if (Reg1) {
+ AddCodeLine ("sta (%s),y", ED_GetLabelName (&Arg1.Expr, 0));
+ } else {
+ AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, 0));
+ }
+ AddCodeLine ("iny");
+ AddCodeLine ("cpy #$%02X", (unsigned char) Arg3.Expr.Val);
+ AddCodeLine ("bne %s", LocalLabelName (Label));
+
+ }
+
+ /* memcpy returns the address, so the result is actually identical
+ * to the first argument.
+ */
+ *Expr = Arg1.Expr;
+
+ } else if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.Val <= 256 &&
+ ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr) &&
+ ED_IsRVal (&Arg1.Expr) && ED_IsLocStack (&Arg1.Expr) &&
+ (Arg1.Expr.Val - StackPtr) + Arg3.Expr.Val < 256) {
+
+ /* It is possible to just use one index register even if the stack
+ * offset is not zero, by adjusting the offset to the constant
+ * address accordingly. But we cannot do this if the data in
+ * question is in the register space or at an absolute address less
+ * than 256. Register space is zero page, which means that the
+ * address calculation could overflow in the linker.
+ */
+ int AllowOneIndex = !ED_IsLocRegister (&Arg2.Expr) &&
+ !(ED_IsLocAbs (&Arg2.Expr) && Arg2.Expr.Val < 256);
+
+ /* Calculate the real stack offset */
+ int Offs = ED_GetStackOffs (&Arg1.Expr, 0);
+
+ /* Drop the generated code */
+ RemoveCode (Start);
+
+ /* We need a label */
+ Label = GetLocalLabel ();
+
+ /* Generate memcpy code */
+ if (Arg3.Expr.Val <= 127) {
+
+ if (Offs == 0 || AllowOneIndex) {
+ AddCodeLine ("ldy #$%02X", (unsigned char) (Offs + Arg3.Expr.Val - 1));
+ g_defcodelabel (Label);
+ AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg2.Expr, -Offs));
+ AddCodeLine ("sta (sp),y");
+ AddCodeLine ("dey");
+ AddCodeLine ("bpl %s", LocalLabelName (Label));
+ } else {
+ AddCodeLine ("ldx #$%02X", (unsigned char) (Arg3.Expr.Val-1));
+ AddCodeLine ("ldy #$%02X", (unsigned char) (Offs + Arg3.Expr.Val - 1));
+ g_defcodelabel (Label);
+ AddCodeLine ("lda %s,x", ED_GetLabelName (&Arg2.Expr, 0));
+ AddCodeLine ("sta (sp),y");
+ AddCodeLine ("dey");
+ AddCodeLine ("dex");
+ AddCodeLine ("bpl %s", LocalLabelName (Label));
+ }
+
+ } else {
+
+ if (Offs == 0 || AllowOneIndex) {
+ AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
+ g_defcodelabel (Label);
+ AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg2.Expr, -Offs));
+ AddCodeLine ("sta (sp),y");
+ AddCodeLine ("iny");
+ AddCodeLine ("cpy #$%02X", (unsigned char) (Offs + Arg3.Expr.Val));
+ AddCodeLine ("bne %s", LocalLabelName (Label));
+ } else {
+ AddCodeLine ("ldx #$00");
+ AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
+ g_defcodelabel (Label);
+ AddCodeLine ("lda %s,x", ED_GetLabelName (&Arg2.Expr, 0));
+ AddCodeLine ("sta (sp),y");
+ AddCodeLine ("iny");
+ AddCodeLine ("inx");
+ AddCodeLine ("cpx #$%02X", (unsigned char) Arg3.Expr.Val);
+ AddCodeLine ("bne %s", LocalLabelName (Label));
+ }
+
+ }
+
+ /* memcpy returns the address, so the result is actually identical
+ * to the first argument.
+ */
+ *Expr = Arg1.Expr;
+
+ } else if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.Val <= 256 &&
+ ED_IsRVal (&Arg2.Expr) && ED_IsLocStack (&Arg2.Expr) &&
+ (Arg2.Expr.Val - StackPtr) + Arg3.Expr.Val < 256 &&
+ ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) {
+
+ /* It is possible to just use one index register even if the stack
+ * offset is not zero, by adjusting the offset to the constant
+ * address accordingly. But we cannot do this if the data in
+ * question is in the register space or at an absolute address less
+ * than 256. Register space is zero page, which means that the
+ * address calculation could overflow in the linker.
+ */
+ int AllowOneIndex = !ED_IsLocRegister (&Arg1.Expr) &&
+ !(ED_IsLocAbs (&Arg1.Expr) && Arg1.Expr.Val < 256);
+
+ /* Calculate the real stack offset */
+ int Offs = ED_GetStackOffs (&Arg2.Expr, 0);
+
+ /* Drop the generated code */
+ RemoveCode (Start);
+
+ /* We need a label */
+ Label = GetLocalLabel ();
+
+ /* Generate memcpy code */
+ if (Arg3.Expr.Val <= 127) {
+
+ if (Offs == 0 || AllowOneIndex) {
+ AddCodeLine ("ldy #$%02X", (unsigned char) (Offs + Arg3.Expr.Val - 1));
+ g_defcodelabel (Label);
+ AddCodeLine ("lda (sp),y");
+ AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, -Offs));
+ AddCodeLine ("dey");
+ AddCodeLine ("bpl %s", LocalLabelName (Label));
+ } else {
+ AddCodeLine ("ldx #$%02X", (unsigned char) (Arg3.Expr.Val-1));
+ AddCodeLine ("ldy #$%02X", (unsigned char) (Offs + Arg3.Expr.Val - 1));
+ g_defcodelabel (Label);
+ AddCodeLine ("lda (sp),y");
+ AddCodeLine ("sta %s,x", ED_GetLabelName (&Arg1.Expr, 0));
+ AddCodeLine ("dey");
+ AddCodeLine ("dex");
+ AddCodeLine ("bpl %s", LocalLabelName (Label));
+ }
+
+ } else {
+
+ if (Offs == 0 || AllowOneIndex) {
+ AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
+ g_defcodelabel (Label);
+ AddCodeLine ("lda (sp),y");
+ AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, -Offs));
+ AddCodeLine ("iny");
+ AddCodeLine ("cpy #$%02X", (unsigned char) (Offs + Arg3.Expr.Val));
+ AddCodeLine ("bne %s", LocalLabelName (Label));
+ } else {
+ AddCodeLine ("ldx #$00");
+ AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
+ g_defcodelabel (Label);
+ AddCodeLine ("lda (sp),y");
+ AddCodeLine ("sta %s,x", ED_GetLabelName (&Arg1.Expr, 0));
+ AddCodeLine ("iny");
+ AddCodeLine ("inx");
+ AddCodeLine ("cpx #$%02X", (unsigned char) Arg3.Expr.Val);
+ AddCodeLine ("bne %s", LocalLabelName (Label));
+ }
+
+ }
+
+ /* memcpy returns the address, so the result is actually identical
+ * to the first argument.
+ */
+ *Expr = Arg1.Expr;
+
+ } else {
+
+ /* The function result is an rvalue in the primary register */
+ ED_MakeRValExpr (Expr);
+ Expr->Type = GetFuncReturn (Expr->Type);
+
+ }
+
+ExitPoint:
+ /* We expect the closing brace */
+ ConsumeRParen ();
}
/*****************************************************************************/
-/* Handle known functions */
+/* memset */
/*****************************************************************************/
/* Argument types */
static type Arg1Type[] = { T_PTR, T_VOID, T_END }; /* void* */
static type Arg2Type[] = { T_INT, T_END }; /* int */
- static type Arg3Type[] = { T_UINT, T_END }; /* size_t */
+ static type Arg3Type[] = { T_SIZE_T, T_END }; /* size_t */
- unsigned Flags;
- ExprDesc Arg;
+ CodeMark Start;
+ ArgDesc Arg1, Arg2, Arg3;
int MemSet = 1; /* Use real memset if true */
unsigned ParamSize = 0;
+ unsigned Label;
+
+ /* Remember where we are now */
+ Start = GetCodePos ();
/* Argument #1 */
- Flags = ParseArg (Arg1Type, &Arg);
- g_push (Flags, Arg.Val);
+ ParseArg (&Arg1, Arg1Type);
+ g_push (Arg1.Flags, Arg1.Expr.Val);
+ Arg1.End = GetCodePos ();
ParamSize += SizeOf (Arg1Type);
ConsumeComma ();
/* Argument #2. This argument is special in that we will call another
* function if it is a constant zero.
*/
- Flags = ParseArg (Arg2Type, &Arg);
- if ((Flags & CF_CONST) != 0 && Arg.Val == 0) {
+ ParseArg (&Arg2, Arg2Type);
+ if ((Arg2.Flags & CF_CONST) != 0 && Arg2.Expr.Val == 0) {
/* Don't call memset, call bzero instead */
MemSet = 0;
} else {
/* Push the argument */
- g_push (Flags, Arg.Val);
+ g_push (Arg2.Flags, Arg2.Expr.Val);
+ Arg2.End = GetCodePos ();
ParamSize += SizeOf (Arg2Type);
}
ConsumeComma ();
* also ignored for the calculation of the parameter size, since it is
* not passed via the stack.
*/
- Flags = ParseArg (Arg3Type, &Arg);
- if (Flags & CF_CONST) {
- if (Arg.Val == 0) {
- Warning ("Call to memset has no effect");
- }
- ExprLoad (CF_FORCECHAR, &Arg);
+ ParseArg (&Arg3, Arg3Type);
+ if (Arg3.Flags & CF_CONST) {
+ ExprLoad (CF_FORCECHAR, &Arg3.Expr);
+ }
+
+ /* Emit the actual function call. This will also cleanup the stack. */
+ g_call (CF_FIXARGC, MemSet? Func_memset : Func__bzero, ParamSize);
+
+ if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.Val == 0) {
+
+ /* memset has been called with a count argument of zero */
+ Warning ("Call to memset has no effect");
+
+ /* Remove all of the generated code but the load of the first
+ * argument, which is what memset returns.
+ */
+ RemoveCode (Arg1.Push);
+
+ /* Set the function result to the first argument */
+ *Expr = Arg1.Expr;
+
+ /* Bail out, no need for further improvements */
+ goto ExitPoint;
}
- /* Emit the actual function call */
- g_call (CF_NONE, MemSet? Func_memset : Func__bzero, ParamSize);
+ /* We've generated the complete code for the function now and know the
+ * types of all parameters. Check for situations where better code can
+ * be generated. If such a situation is detected, throw away the
+ * generated, and emit better code.
+ * Note: Lots of improvements would be possible here, but I will
+ * concentrate on the most common case: memset with arguments 2 and 3
+ * being constant numerical values. Some checks have shown that this
+ * covers nearly 90% of all memset calls.
+ */
+ if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.Val <= 256 &&
+ ED_IsConstAbsInt (&Arg2.Expr) &&
+ ((ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) ||
+ (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr)))) {
+
+ int Reg = ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr);
+
+ /* Drop the generated code */
+ RemoveCode (Start);
+
+ /* We need a label */
+ Label = GetLocalLabel ();
+
+ /* Generate memset code */
+ if (Arg3.Expr.Val <= 127) {
+
+ AddCodeLine ("ldy #$%02X", (unsigned char) (Arg3.Expr.Val-1));
+ AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.Val);
+ g_defcodelabel (Label);
+ if (Reg) {
+ AddCodeLine ("sta (%s),y", ED_GetLabelName (&Arg1.Expr, 0));
+ } else {
+ AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, 0));
+ }
+ AddCodeLine ("dey");
+ AddCodeLine ("bpl %s", LocalLabelName (Label));
+
+ } else {
+
+ AddCodeLine ("ldy #$00");
+ AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.Val);
+ g_defcodelabel (Label);
+ if (Reg) {
+ AddCodeLine ("sta (%s),y", ED_GetLabelName (&Arg1.Expr, 0));
+ } else {
+ AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, 0));
+ }
+ AddCodeLine ("iny");
+ AddCodeLine ("cpy #$%02X", (unsigned char) Arg3.Expr.Val);
+ AddCodeLine ("bne %s", LocalLabelName (Label));
+
+ }
+
+ /* memset returns the address, so the result is actually identical
+ * to the first argument.
+ */
+ *Expr = Arg1.Expr;
+
+ } else if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.Val <= 256 &&
+ ED_IsConstAbsInt (&Arg2.Expr) &&
+ ED_IsRVal (&Arg1.Expr) && ED_IsLocStack (&Arg1.Expr) &&
+ (Arg1.Expr.Val - StackPtr) + Arg3.Expr.Val < 256) {
+
+ /* Calculate the real stack offset */
+ int Offs = ED_GetStackOffs (&Arg1.Expr, 0);
+
+ /* Drop the generated code */
+ RemoveCode (Start);
+
+ /* We need a label */
+ Label = GetLocalLabel ();
+
+ /* Generate memset code */
+ AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
+ AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.Val);
+ g_defcodelabel (Label);
+ AddCodeLine ("sta (sp),y");
+ AddCodeLine ("iny");
+ AddCodeLine ("cpy #$%02X", (unsigned char) (Offs + Arg3.Expr.Val));
+ AddCodeLine ("bne %s", LocalLabelName (Label));
+
+ /* memset returns the address, so the result is actually identical
+ * to the first argument.
+ */
+ *Expr = Arg1.Expr;
+
+ } else if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.Val <= 256 &&
+ ED_IsConstAbsInt (&Arg2.Expr) &&
+ (Arg2.Expr.Val != 0 || CodeSizeFactor > 200)) {
+
+ /* Remove all of the generated code but the load of the first
+ * argument.
+ */
+ RemoveCode (Arg1.Push);
+
+ /* We need a label */
+ Label = GetLocalLabel ();
+
+ /* Generate code */
+ AddCodeLine ("sta ptr1");
+ AddCodeLine ("stx ptr1+1");
+ if (Arg3.Expr.Val <= 127) {
+ AddCodeLine ("ldy #$%02X", (unsigned char) (Arg3.Expr.Val-1));
+ AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.Val);
+ g_defcodelabel (Label);
+ AddCodeLine ("sta (ptr1),y");
+ AddCodeLine ("dey");
+ AddCodeLine ("bpl %s", LocalLabelName (Label));
+ } else {
+ AddCodeLine ("ldy #$00");
+ AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.Val);
+ g_defcodelabel (Label);
+ AddCodeLine ("sta (ptr1),y");
+ AddCodeLine ("iny");
+ AddCodeLine ("cpy #$%02X", (unsigned char) Arg3.Expr.Val);
+ AddCodeLine ("bne %s", LocalLabelName (Label));
+ }
+
+ /* Load the function result pointer into a/x (x is still valid). This
+ * code will get removed by the optimizer if it is not used later.
+ */
+ AddCodeLine ("lda ptr1");
+
+ /* The function result is an rvalue in the primary register */
+ ED_MakeRValExpr (Expr);
+ Expr->Type = GetFuncReturn (Expr->Type);
+
+ } else {
+
+ /* The function result is an rvalue in the primary register */
+ ED_MakeRValExpr (Expr);
+ Expr->Type = GetFuncReturn (Expr->Type);
+
+ }
+ExitPoint:
/* We expect the closing brace */
ConsumeRParen ();
+}
+
+
+
+/*****************************************************************************/
+/* strcpy */
+/*****************************************************************************/
+
+
+
+static void StdFunc_strcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
+/* Handle the strcpy function */
+{
+ /* Argument types */
+ static type Arg1Type[] = { T_PTR, T_CHAR, T_END }; /* char* */
+ static type Arg2Type[] = { T_PTR, T_CHAR|T_QUAL_CONST, T_END }; /* const char* */
+
+ CodeMark Start;
+ ArgDesc Arg1, Arg2;
+ unsigned ParamSize = 0;
+ long ECount;
+ unsigned L1;
+
+ /* Setup the argument type string */
+ Arg1Type[1] = GetDefaultChar ();
+ Arg2Type[1] = GetDefaultChar () | T_QUAL_CONST;
+
+ /* Remember where we are now */
+ Start = GetCodePos ();
+
+ /* Argument #1 */
+ ParseArg (&Arg1, Arg1Type);
+ g_push (Arg1.Flags, Arg1.Expr.Val);
+ Arg1.End = GetCodePos ();
+ ParamSize += SizeOf (Arg1Type);
+ ConsumeComma ();
+
+ /* Argument #2. Since strcpy is a fastcall function, we must load the
+ * arg into the primary if it is not already there. This parameter is
+ * also ignored for the calculation of the parameter size, since it is
+ * not passed via the stack.
+ */
+ ParseArg (&Arg2, Arg2Type);
+ if (Arg2.Flags & CF_CONST) {
+ ExprLoad (CF_FORCECHAR, &Arg2.Expr);
+ }
+
+ /* Emit the actual function call. This will also cleanup the stack. */
+ g_call (CF_FIXARGC, Func_strcpy, ParamSize);
+
+ /* Get the element count of argument 2 if it is an array */
+ ECount = ArrayElementCount (&Arg1);
+
+ /* We've generated the complete code for the function now and know the
+ * types of all parameters. Check for situations where better code can
+ * be generated. If such a situation is detected, throw away the
+ * generated, and emit better code.
+ */
+ if (((ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr)) ||
+ (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr))) &&
+ ((ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) ||
+ (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr))) &&
+ (IS_Get (&InlineStdFuncs) ||
+ (ECount != UNSPECIFIED && ECount < 256))) {
+
+ const char* Load;
+ const char* Store;
+ if (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr)) {
+ Load = "lda (%s),y";
+ } else {
+ Load = "lda %s,y";
+ }
+ if (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr)) {
+ Store = "sta (%s),y";
+ } else {
+ Store = "sta %s,y";
+ }
+
+ /* Drop the generated code */
+ RemoveCode (Start);
+
+ /* We need labels */
+ L1 = GetLocalLabel ();
+
+ /* Generate strcpy code */
+ AddCodeLine ("ldy #$FF");
+ g_defcodelabel (L1);
+ AddCodeLine ("iny");
+ AddCodeLine (Load, ED_GetLabelName (&Arg2.Expr, 0));
+ AddCodeLine (Store, ED_GetLabelName (&Arg1.Expr, 0));
+ AddCodeLine ("bne %s", LocalLabelName (L1));
+
+ /* strcpy returns argument #1 */
+ *Expr = Arg1.Expr;
+
+ } else if (ED_IsRVal (&Arg2.Expr) && ED_IsLocStack (&Arg2.Expr) &&
+ StackPtr >= -255 &&
+ ED_IsRVal (&Arg1.Expr) && ED_IsLocConst (&Arg1.Expr)) {
+
+ /* It is possible to just use one index register even if the stack
+ * offset is not zero, by adjusting the offset to the constant
+ * address accordingly. But we cannot do this if the data in
+ * question is in the register space or at an absolute address less
+ * than 256. Register space is zero page, which means that the
+ * address calculation could overflow in the linker.
+ */
+ int AllowOneIndex = !ED_IsLocRegister (&Arg1.Expr) &&
+ !(ED_IsLocAbs (&Arg1.Expr) && Arg1.Expr.Val < 256);
+
+ /* Calculate the real stack offset */
+ int Offs = ED_GetStackOffs (&Arg2.Expr, 0);
+
+ /* Drop the generated code */
+ RemoveCode (Start);
+
+ /* We need labels */
+ L1 = GetLocalLabel ();
+
+ /* Generate strcpy code */
+ AddCodeLine ("ldy #$%02X", (unsigned char) (Offs - 1));
+ if (Offs == 0 || AllowOneIndex) {
+ g_defcodelabel (L1);
+ AddCodeLine ("iny");
+ AddCodeLine ("lda (sp),y");
+ AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, -Offs));
+ } else {
+ AddCodeLine ("ldx #$FF");
+ g_defcodelabel (L1);
+ AddCodeLine ("iny");
+ AddCodeLine ("inx");
+ AddCodeLine ("lda (sp),y");
+ AddCodeLine ("sta %s,x", ED_GetLabelName (&Arg1.Expr, 0));
+ }
+ AddCodeLine ("bne %s", LocalLabelName (L1));
+
+ /* strcpy returns argument #1 */
+ *Expr = Arg1.Expr;
+
+ } else if (ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr) &&
+ ED_IsRVal (&Arg1.Expr) && ED_IsLocStack (&Arg1.Expr) &&
+ StackPtr >= -255) {
+
+ /* It is possible to just use one index register even if the stack
+ * offset is not zero, by adjusting the offset to the constant
+ * address accordingly. But we cannot do this if the data in
+ * question is in the register space or at an absolute address less
+ * than 256. Register space is zero page, which means that the
+ * address calculation could overflow in the linker.
+ */
+ int AllowOneIndex = !ED_IsLocRegister (&Arg2.Expr) &&
+ !(ED_IsLocAbs (&Arg2.Expr) && Arg2.Expr.Val < 256);
+
+ /* Calculate the real stack offset */
+ int Offs = ED_GetStackOffs (&Arg1.Expr, 0);
+
+ /* Drop the generated code */
+ RemoveCode (Start);
- /* The function result is an rvalue in the primary register */
- ED_MakeRValExpr (Expr);
- Expr->Type = GetFuncReturn (Expr->Type);
+ /* We need labels */
+ L1 = GetLocalLabel ();
+
+ /* Generate strcpy code */
+ AddCodeLine ("ldy #$%02X", (unsigned char) (Offs - 1));
+ if (Offs == 0 || AllowOneIndex) {
+ g_defcodelabel (L1);
+ AddCodeLine ("iny");
+ AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg2.Expr, -Offs));
+ AddCodeLine ("sta (sp),y");
+ } else {
+ AddCodeLine ("ldx #$FF");
+ g_defcodelabel (L1);
+ AddCodeLine ("iny");
+ AddCodeLine ("inx");
+ AddCodeLine ("lda %s,x", ED_GetLabelName (&Arg2.Expr, 0));
+ AddCodeLine ("sta (sp),y");
+ }
+ AddCodeLine ("bne %s", LocalLabelName (L1));
+
+ /* strcpy returns argument #1 */
+ *Expr = Arg1.Expr;
+
+ } else {
+
+ /* The function result is an rvalue in the primary register */
+ ED_MakeRValExpr (Expr);
+ Expr->Type = GetFuncReturn (Expr->Type);
+
+ }
+
+ /* We expect the closing brace */
+ ConsumeRParen ();
}
+/*****************************************************************************/
+/* strlen */
+/*****************************************************************************/
+
+
+
static void StdFunc_strlen (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
/* Handle the strlen function */
{
static type ArgType[] = { T_PTR, T_SCHAR, T_END };
ExprDesc Arg;
+ int IsArray;
+ int IsPtr;
+ int IsByteIndex;
+ long ECount;
unsigned L;
+
/* Setup the argument type string */
ArgType[1] = GetDefaultChar () | T_QUAL_CONST;
/* Evaluate the parameter */
hie1 (&Arg);
- /* We can generate special code for several locations */
- if (ED_IsLocConst (&Arg) && IsTypeArray (Arg.Type)) {
+ /* Check if the argument is an array. If so, remember the element count.
+ * Otherwise set the element count to undefined.
+ */
+ IsArray = IsTypeArray (Arg.Type);
+ if (IsArray) {
+ ECount = GetElementCount (Arg.Type);
+ if (ECount == FLEXIBLE) {
+ /* Treat as unknown */
+ ECount = UNSPECIFIED;
+ }
+ IsPtr = 0;
+ } else {
+ ECount = UNSPECIFIED;
+ IsPtr = IsTypePtr (Arg.Type);
+ }
- /* Do type conversion */
- TypeConversion (&Arg, ArgType);
+ /* Check if the elements of an array can be addressed by a byte sized
+ * index. This is true if the size of the array is known and less than
+ * 256.
+ */
+ IsByteIndex = (ECount != UNSPECIFIED && ECount < 256);
- /* If the expression is a literal, and if string literals are read
- * only, we can calculate the length of the string and remove it
- * from the literal pool. Otherwise we have to calculate the length
- * at runtime.
- */
- if (ED_IsLocLiteral (&Arg) && IS_Get (&WritableStrings)) {
+ /* Do type conversion */
+ TypeConversion (&Arg, ArgType);
- /* Constant string literal */
- ED_MakeConstAbs (Expr, strlen (GetLiteral (Arg.Val)), type_size_t);
- ResetLiteralPoolOffs (Arg.Val);
+ /* If the expression is a literal, and if string literals are read
+ * only, we can calculate the length of the string and remove it
+ * from the literal pool. Otherwise we have to calculate the length
+ * at runtime.
+ */
+ if (ED_IsLocLiteral (&Arg) && IS_Get (&WritableStrings) == 0) {
- } else {
+ /* Constant string literal */
+ ED_MakeConstAbs (Expr, strlen (GetLiteral (Arg.Val)), type_size_t);
+ ResetLiteralPoolOffs (Arg.Val);
- /* Generate the strlen code */
- L = GetLocalLabel ();
- AddCodeLine ("ldy #$FF");
- g_defcodelabel (L);
- AddCodeLine ("iny");
- AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg, 0));
- AddCodeLine ("bne %s", LocalLabelName (L));
- AddCodeLine ("tax");
- AddCodeLine ("tya");
+ /* We will inline strlen for arrays with constant addresses, if either the
+ * inlining was forced on the command line, or the array is smaller than
+ * 256, so the inlining is considered safe.
+ */
+ } else if (ED_IsLocConst (&Arg) && IsArray &&
+ (IS_Get (&InlineStdFuncs) || IsByteIndex)) {
- /* The function result is an rvalue in the primary register */
- ED_MakeRValExpr (Expr);
- Expr->Type = type_size_t;
+ /* Generate the strlen code */
+ L = GetLocalLabel ();
+ AddCodeLine ("ldy #$FF");
+ g_defcodelabel (L);
+ AddCodeLine ("iny");
+ AddCodeLine ("lda %s,y", ED_GetLabelName (&Arg, 0));
+ AddCodeLine ("bne %s", LocalLabelName (L));
+ AddCodeLine ("tax");
+ AddCodeLine ("tya");
- }
+ /* The function result is an rvalue in the primary register */
+ ED_MakeRValExpr (Expr);
+ Expr->Type = type_size_t;
- } else if (ED_IsLocStack (&Arg) && StackPtr >= -255 && IsTypeArray (Arg.Type)) {
+ /* We will inline strlen for arrays on the stack, if the array is
+ * completely within the reach of a byte sized index register.
+ */
+ } else if (ED_IsLocStack (&Arg) && IsArray && IsByteIndex &&
+ (Arg.Val - StackPtr) + ECount < 256) {
/* Calculate the true stack offset */
- unsigned Offs = (unsigned) (Arg.Val - StackPtr);
-
- /* Do type conversion */
- TypeConversion (&Arg, ArgType);
+ int Offs = ED_GetStackOffs (&Arg, 0);
/* Generate the strlen code */
L = GetLocalLabel ();
ED_MakeRValExpr (Expr);
Expr->Type = type_size_t;
- } else if (ED_IsLocRegister (&Arg) && ED_IsLVal (&Arg) && IsTypePtr (Arg.Type)) {
-
- /* Do type conversion */
- TypeConversion (&Arg, ArgType);
+ /* strlen for a string that is pointed to by a register variable will only
+ * get inlined if requested on the command line, since we cannot know how
+ * big the buffer actually is, so inlining is not always safe.
+ */
+ } else if (ED_IsLocRegister (&Arg) && ED_IsLVal (&Arg) && IsPtr &&
+ IS_Get (&InlineStdFuncs)) {
/* Generate the strlen code */
L = GetLocalLabel ();
ED_MakeRValExpr (Expr);
Expr->Type = type_size_t;
- } else {
+ /* Last check: We will inline a generic strlen routine if inlining was
+ * requested on the command line, and the code size factor is more than
+ * 400 (code is 13 bytes vs. 3 for a jsr call).
+ */
+ } else if (CodeSizeFactor > 400 && IS_Get (&InlineStdFuncs)) {
- /* Do type conversion */
- TypeConversion (&Arg, ArgType);
+ /* Inline the function */
+ L = GetLocalLabel ();
+ AddCodeLine ("sta ptr1");
+ AddCodeLine ("stx ptr1+1");
+ AddCodeLine ("ldy #$FF");
+ g_defcodelabel (L);
+ AddCodeLine ("iny");
+ AddCodeLine ("lda (ptr1),y");
+ AddCodeLine ("bne %s", LocalLabelName (L));
+ AddCodeLine ("tax");
+ AddCodeLine ("tya");
+
+ /* The function result is an rvalue in the primary register */
+ ED_MakeRValExpr (Expr);
+ Expr->Type = type_size_t;
+
+ } else {
/* Load the expression into the primary */
ExprLoad (CF_NONE, &Arg);