/*****************************************************************************/
/* */
-/* stdfunc.c */
+/* stdfunc.c */
/* */
-/* Handle inlining of known functions for the cc65 compiler */
+/* Handle inlining of known functions for the cc65 compiler */
/* */
/* */
/* */
-/* (C) 1998-2004 Ullrich von Bassewitz */
-/* Römerstrasse 52 */
+/* (C) 1998-2010 Ullrich von Bassewitz */
+/* Roemerstrasse 52 */
/* D-70794 Filderstadt */
/* EMail: uz@cc65.org */
/* */
#include "check.h"
/* cc65 */
+#include "asmcode.h"
+#include "asmlabel.h"
#include "codegen.h"
#include "error.h"
#include "funcdesc.h"
#include "global.h"
#include "litpool.h"
+#include "loadexpr.h"
#include "scanner.h"
+#include "stackptr.h"
#include "stdfunc.h"
#include "stdnames.h"
#include "typeconv.h"
/*****************************************************************************/
-/* Function forwards */
+/* Function forwards */
/*****************************************************************************/
+static void StdFunc_memcpy (FuncDesc*, ExprDesc*);
static void StdFunc_memset (FuncDesc*, ExprDesc*);
+static void StdFunc_strcmp (FuncDesc*, ExprDesc*);
+static void StdFunc_strcpy (FuncDesc*, ExprDesc*);
static void StdFunc_strlen (FuncDesc*, ExprDesc*);
/*****************************************************************************/
-/* Data */
+/* Data */
/*****************************************************************************/
/* Table with all known functions and their handlers. Must be sorted
- * alphabetically!
- */
+** alphabetically!
+*/
static struct StdFuncDesc {
- const char* Name;
- void (*Handler) (FuncDesc*, ExprDesc*);
-} StdFuncs [] = {
- { "memset", StdFunc_memset },
- { "strlen", StdFunc_strlen },
+ const char* Name;
+ void (*Handler) (FuncDesc*, ExprDesc*);
+} StdFuncs[] = {
+ { "memcpy", StdFunc_memcpy },
+ { "memset", StdFunc_memset },
+ { "strcmp", StdFunc_strcmp },
+ { "strcpy", StdFunc_strcpy },
+ { "strlen", StdFunc_strlen },
};
-#define FUNC_COUNT (sizeof (StdFuncs) / sizeof (StdFuncs [0]))
+#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 Load; /* Start of argument load code */
+ CodeMark Push; /* Start of argument push code */
+ CodeMark End; /* End of the code for calculation+push */
+ unsigned Flags; /* Code generation flags */
+};
+
/*****************************************************************************/
-/* Helper functions */
+/* Helper functions */
/*****************************************************************************/
-static struct StdFuncDesc* FindFunc (const char* Name)
-/* Find a function with the given name. Return a pointer to the descriptor if
- * found, return NULL otherwise.
- */
+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.
+*/
{
- return bsearch (Name, StdFuncs, FUNC_COUNT, sizeof (StdFuncs [0]), CmpFunc);
+ long Count;
+
+ if (IsTypeArray (Arg->Type)) {
+ Count = GetElementCount (Arg->Type);
+ if (Count == FLEXIBLE) {
+ /* Treat as unknown */
+ Count = UNSPECIFIED;
+ }
+ } else {
+ Count = UNSPECIFIED;
+ }
+ return Count;
}
-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 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;
/* Read the expression we're going to pass to the function */
- hie1 (Arg);
+ MarkedExprWithCheck (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);
+
+ /* Remember the following code position */
+ GetCodePos (&Arg->Load);
/* If the value is a constant, set the flag, otherwise load it into the
- * primary register.
- */
- if (ED_IsConstAbsInt (Arg)) {
+ ** primary register.
+ */
+ if (ED_IsConstAbsInt (&Arg->Expr) && ED_CodeRangeIsEmpty (&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);
+ LoadExpr (CF_NONE, &Arg->Expr);
}
+ /* Remember the following code position */
+ GetCodePos (&Arg->Push);
+ GetCodePos (&Arg->End);
+
/* Use the type of the argument for the push */
- return (Flags | TypeOf (Arg->Type));
+ Arg->Flags |= TypeOf (Arg->Expr.Type);
+}
+
+
+
+void AddCmpCodeIfSizeNot256 (const char* Code, long Size)
+/* Add a line of Assembly code that compares an index register
+** only if it isn't comparing to #<256. (If the next line
+** is "bne", then this will avoid a redundant line.)
+*/
+{
+ if (Size != 256) {
+ AddCodeLine (Code, (unsigned int)Size);
+ }
+}
+
+
+
+/*****************************************************************************/
+/* memcpy */
+/*****************************************************************************/
+
+
+
+static void StdFunc_memcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
+/* Handle the memcpy function */
+{
+ /* Argument types: (void*, const void*, size_t) */
+ static Type Arg1Type[] = { TYPE(T_PTR), TYPE(T_VOID), TYPE(T_END) };
+ static Type Arg2Type[] = { TYPE(T_PTR), TYPE(T_VOID|T_QUAL_CONST), TYPE(T_END) };
+ static Type Arg3Type[] = { TYPE(T_SIZE_T), TYPE(T_END) };
+
+ ArgDesc Arg1, Arg2, Arg3;
+ unsigned ParamSize = 0;
+ unsigned Label;
+ int Offs;
+
+ /* Argument #1 */
+ ParseArg (&Arg1, Arg1Type);
+ g_push (Arg1.Flags, Arg1.Expr.IVal);
+ GetCodePos (&Arg1.End);
+ ParamSize += SizeOf (Arg1Type);
+ ConsumeComma ();
+
+ /* Argument #2 */
+ ParseArg (&Arg2, Arg2Type);
+ g_push (Arg2.Flags, Arg2.Expr.IVal);
+ GetCodePos (&Arg2.End);
+ 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) {
+ LoadExpr (CF_NONE, &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.IVal == 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;
+ }
+
+ if (IS_Get (&InlineStdFuncs)) {
+
+ /* 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.IVal <= 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 (&Arg1.Expr.Start);
+
+ /* We need a label */
+ Label = GetLocalLabel ();
+
+ /* Generate memcpy code */
+ if (Arg3.Expr.IVal <= 129) {
+
+ AddCodeLine ("ldy #$%02X", (unsigned char) (Arg3.Expr.IVal-1));
+ 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");
+ 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");
+ AddCmpCodeIfSizeNot256 ("cpy #$%02X", Arg3.Expr.IVal);
+ AddCodeLine ("bne %s", LocalLabelName (Label));
+
+ }
+
+ /* memcpy returns the address, so the result is actually identical
+ ** to the first argument.
+ */
+ *Expr = Arg1.Expr;
+
+ /* Bail out, no need for further processing */
+ goto ExitPoint;
+ }
+
+ if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
+ ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr) &&
+ ED_IsRVal (&Arg1.Expr) && ED_IsLocStack (&Arg1.Expr) &&
+ (Arg1.Expr.IVal - StackPtr) + Arg3.Expr.IVal < 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.IVal < 256);
+
+ /* Calculate the real stack offset */
+ Offs = ED_GetStackOffs (&Arg1.Expr, 0);
+
+ /* Drop the generated code */
+ RemoveCode (&Arg1.Expr.Start);
+
+ /* We need a label */
+ Label = GetLocalLabel ();
+
+ /* Generate memcpy code */
+ if (Arg3.Expr.IVal <= 129 && !AllowOneIndex) {
+
+ if (Offs == 0) {
+ AddCodeLine ("ldy #$%02X", (unsigned char) (Offs + Arg3.Expr.IVal - 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.IVal-1));
+ AddCodeLine ("ldy #$%02X", (unsigned char) (Offs + Arg3.Expr.IVal - 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");
+ AddCmpCodeIfSizeNot256 ("cpy #$%02X", Offs + Arg3.Expr.IVal);
+ 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");
+ AddCmpCodeIfSizeNot256 ("cpx #$%02X", Arg3.Expr.IVal);
+ AddCodeLine ("bne %s", LocalLabelName (Label));
+ }
+
+ }
+
+ /* memcpy returns the address, so the result is actually identical
+ ** to the first argument.
+ */
+ *Expr = Arg1.Expr;
+
+ /* Bail out, no need for further processing */
+ goto ExitPoint;
+ }
+
+ if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
+ ED_IsRVal (&Arg2.Expr) && ED_IsLocStack (&Arg2.Expr) &&
+ (Arg2.Expr.IVal - StackPtr) + Arg3.Expr.IVal < 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.IVal < 256);
+
+ /* Calculate the real stack offset */
+ Offs = ED_GetStackOffs (&Arg2.Expr, 0);
+
+ /* Drop the generated code */
+ RemoveCode (&Arg1.Expr.Start);
+
+ /* We need a label */
+ Label = GetLocalLabel ();
+
+ /* Generate memcpy code */
+ if (Arg3.Expr.IVal <= 129 && !AllowOneIndex) {
+
+ if (Offs == 0) {
+ AddCodeLine ("ldy #$%02X", (unsigned char) (Arg3.Expr.IVal - 1));
+ g_defcodelabel (Label);
+ AddCodeLine ("lda (sp),y");
+ AddCodeLine ("sta %s,y", ED_GetLabelName (&Arg1.Expr, 0));
+ AddCodeLine ("dey");
+ AddCodeLine ("bpl %s", LocalLabelName (Label));
+ } else {
+ AddCodeLine ("ldx #$%02X", (unsigned char) (Arg3.Expr.IVal-1));
+ AddCodeLine ("ldy #$%02X", (unsigned char) (Offs + Arg3.Expr.IVal - 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");
+ AddCmpCodeIfSizeNot256 ("cpy #$%02X", Offs + Arg3.Expr.IVal);
+ 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");
+ AddCmpCodeIfSizeNot256 ("cpx #$%02X", Arg3.Expr.IVal);
+ AddCodeLine ("bne %s", LocalLabelName (Label));
+ }
+
+ }
+
+ /* memcpy returns the address, so the result is actually identical
+ ** to the first argument.
+ */
+ *Expr = Arg1.Expr;
+
+ /* Bail out, no need for further processing */
+ goto ExitPoint;
+ }
+
+ if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
+ ED_IsRVal (&Arg2.Expr) && ED_IsLocStack (&Arg2.Expr) &&
+ (Offs = ED_GetStackOffs (&Arg2.Expr, 0)) == 0) {
+
+ /* Drop the generated code but leave the load of the first argument*/
+ RemoveCode (&Arg1.Push);
+
+ /* We need a label */
+ Label = GetLocalLabel ();
+
+ /* Generate memcpy code */
+ AddCodeLine ("sta ptr1");
+ AddCodeLine ("stx ptr1+1");
+ if (Arg3.Expr.IVal <= 129) {
+ AddCodeLine ("ldy #$%02X", (unsigned char) (Arg3.Expr.IVal - 1));
+ g_defcodelabel (Label);
+ AddCodeLine ("lda (sp),y");
+ AddCodeLine ("sta (ptr1),y");
+ AddCodeLine ("dey");
+ AddCodeLine ("bpl %s", LocalLabelName (Label));
+ } else {
+ AddCodeLine ("ldy #$00");
+ g_defcodelabel (Label);
+ AddCodeLine ("lda (sp),y");
+ AddCodeLine ("sta (ptr1),y");
+ AddCodeLine ("iny");
+ AddCmpCodeIfSizeNot256 ("cpy #$%02X", Arg3.Expr.IVal);
+ AddCodeLine ("bne %s", LocalLabelName (Label));
+ }
+
+ /* Reload result - X hasn't changed by the code above */
+ AddCodeLine ("lda ptr1");
+
+ /* The function result is an rvalue in the primary register */
+ ED_MakeRValExpr (Expr);
+ Expr->Type = GetFuncReturn (Expr->Type);
+
+ /* Bail out, no need for further processing */
+ goto ExitPoint;
+ }
+ }
+
+ /* 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 */
/*****************************************************************************/
-static void StdFunc_memset (FuncDesc* F attribute ((unused)),
- ExprDesc* lval attribute ((unused)))
+static void StdFunc_memset (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
/* Handle the memset function */
{
- /* 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 */
+ /* Argument types: (void*, int, size_t) */
+ static Type Arg1Type[] = { TYPE(T_PTR), TYPE(T_VOID), TYPE(T_END) };
+ static Type Arg2Type[] = { TYPE(T_INT), TYPE(T_END) };
+ static Type Arg3Type[] = { TYPE(T_SIZE_T), TYPE(T_END) };
- unsigned Flags;
- ExprDesc Arg;
+ ArgDesc Arg1, Arg2, Arg3;
int MemSet = 1; /* Use real memset if true */
unsigned ParamSize = 0;
+ unsigned Label;
/* Argument #1 */
- Flags = ParseArg (Arg1Type, &Arg);
- g_push (Flags, Arg.Val);
+ ParseArg (&Arg1, Arg1Type);
+ g_push (Arg1.Flags, Arg1.Expr.IVal);
+ GetCodePos (&Arg1.End);
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) {
+ ** function if it is a constant zero.
+ */
+ ParseArg (&Arg2, Arg2Type);
+ if ((Arg2.Flags & CF_CONST) != 0 && Arg2.Expr.IVal == 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.IVal);
+ GetCodePos (&Arg2.End);
ParamSize += SizeOf (Arg2Type);
}
ConsumeComma ();
/* Argument #3. Since memset 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.
- */
- Flags = ParseArg (Arg3Type, &Arg);
- if (Flags & CF_CONST) {
- if (Arg.Val == 0) {
- Warning ("Call to memset has no effect");
- }
- ExprLoad (CF_FORCECHAR, &Arg);
+ ** 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) {
+ LoadExpr (CF_NONE, &Arg3.Expr);
}
- /* Emit the actual function call */
- g_call (CF_NONE, MemSet? Func_memset : Func__bzero, ParamSize);
+ /* 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.IVal == 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;
+ }
+
+ if (IS_Get (&InlineStdFuncs)) {
+
+ /* 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.IVal <= 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 (&Arg1.Expr.Start);
+
+ /* We need a label */
+ Label = GetLocalLabel ();
+
+ /* Generate memset code */
+ if (Arg3.Expr.IVal <= 129) {
+
+ AddCodeLine ("ldy #$%02X", (unsigned char) (Arg3.Expr.IVal-1));
+ AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
+ 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.IVal);
+ 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");
+ AddCmpCodeIfSizeNot256 ("cpy #$%02X", Arg3.Expr.IVal);
+ AddCodeLine ("bne %s", LocalLabelName (Label));
+
+ }
+
+ /* memset returns the address, so the result is actually identical
+ ** to the first argument.
+ */
+ *Expr = Arg1.Expr;
+
+ /* Bail out, no need for further processing */
+ goto ExitPoint;
+ }
+
+ if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
+ ED_IsConstAbsInt (&Arg2.Expr) &&
+ ED_IsRVal (&Arg1.Expr) && ED_IsLocStack (&Arg1.Expr) &&
+ (Arg1.Expr.IVal - StackPtr) + Arg3.Expr.IVal < 256) {
+
+ /* Calculate the real stack offset */
+ int Offs = ED_GetStackOffs (&Arg1.Expr, 0);
+
+ /* Drop the generated code */
+ RemoveCode (&Arg1.Expr.Start);
+
+ /* We need a label */
+ Label = GetLocalLabel ();
+
+ /* Generate memset code */
+ AddCodeLine ("ldy #$%02X", (unsigned char) Offs);
+ AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
+ g_defcodelabel (Label);
+ AddCodeLine ("sta (sp),y");
+ AddCodeLine ("iny");
+ AddCmpCodeIfSizeNot256 ("cpy #$%02X", Offs + Arg3.Expr.IVal);
+ AddCodeLine ("bne %s", LocalLabelName (Label));
+
+ /* memset returns the address, so the result is actually identical
+ ** to the first argument.
+ */
+ *Expr = Arg1.Expr;
+
+ /* Bail out, no need for further processing */
+ goto ExitPoint;
+ }
+
+ if (ED_IsConstAbsInt (&Arg3.Expr) && Arg3.Expr.IVal <= 256 &&
+ ED_IsConstAbsInt (&Arg2.Expr) &&
+ (Arg2.Expr.IVal != 0 || IS_Get (&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.IVal <= 129) {
+ AddCodeLine ("ldy #$%02X", (unsigned char) (Arg3.Expr.IVal-1));
+ AddCodeLine ("lda #$%02X", (unsigned char) Arg2.Expr.IVal);
+ 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.IVal);
+ g_defcodelabel (Label);
+ AddCodeLine ("sta (ptr1),y");
+ AddCodeLine ("iny");
+ AddCmpCodeIfSizeNot256 ("cpy #$%02X", Arg3.Expr.IVal);
+ 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);
+
+ /* Bail out, no need for further processing */
+ goto ExitPoint;
+ }
+ }
+
+ /* 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 ();
}
-static void StdFunc_strlen (FuncDesc* F attribute ((unused)),
- ExprDesc* lval attribute ((unused)))
-/* Handle the strlen function */
+/*****************************************************************************/
+/* strcmp */
+/*****************************************************************************/
+
+
+
+static void StdFunc_strcmp (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
+/* Handle the strcmp function */
{
- static type ParamType[] = { T_PTR, T_SCHAR, T_END };
- ExprDesc Param;
- unsigned CodeFlags;
- unsigned long ParamName;
+ /* Argument types: (const char*, const char*) */
+ static Type Arg1Type[] = { TYPE(T_PTR), TYPE(T_CHAR|T_QUAL_CONST), TYPE(T_END) };
+ static Type Arg2Type[] = { TYPE(T_PTR), TYPE(T_CHAR|T_QUAL_CONST), TYPE(T_END) };
+
+ ArgDesc Arg1, Arg2;
+ unsigned ParamSize = 0;
+ long ECount1;
+ long ECount2;
+ int IsArray;
+ int Offs;
/* Setup the argument type string */
- ParamType[1] = GetDefaultChar () | T_QUAL_CONST;
-
- /* Fetch the parameter and convert it to the type needed */
- hie1 (&Param);
- TypeConversion (&Param, ParamType);
-
- /* Check if the parameter is a constant array of some type, or a numeric
- * address cast to a pointer.
- */
- CodeFlags = 0;
- ParamName = Param.Name;
- if ((ED_IsLocConst (&Param) && IsTypeArray (Param.Type)) ||
- (ED_IsLocAbs (&Param) && IsTypePtr (Param.Type))) {
-
- /* Check which type of constant it is */
- switch (ED_GetLoc (&Param)) {
-
- case E_LOC_ABS:
- /* Numerical address */
- CodeFlags |= CF_CONST | CF_ABSOLUTE;
- break;
-
- case E_LOC_GLOBAL:
- /* Global label */
- CodeFlags |= CF_CONST | CF_EXTERNAL;
- break;
-
- case E_LOC_STATIC:
- /* Local symbol */
- CodeFlags |= CF_CONST | CF_STATIC;
- break;
-
- case E_LOC_REGISTER:
- /* Register variable */
- CodeFlags |= CF_CONST | CF_REGVAR;
- break;
-
- case E_LOC_LITERAL:
- /* A literal of some kind. 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 (!WriteableStrings) {
- /* String literals are const */
- ExprDesc Length;
- ED_MakeConstAbsInt (&Length, strlen (GetLiteral (Param.Val)));
- ResetLiteralPoolOffs (Param.Val);
- ExprLoad (CF_NONE, &Length);
- goto ExitPoint;
- } else {
- CodeFlags |= CF_CONST | CF_STATIC;
- ParamName = LiteralPoolLabel;
- }
- break;
+ Arg1Type[1].C = GetDefaultChar () | T_QUAL_CONST;
+ Arg2Type[1].C = GetDefaultChar () | T_QUAL_CONST;
+
+ /* Argument #1 */
+ ParseArg (&Arg1, Arg1Type);
+ g_push (Arg1.Flags, Arg1.Expr.IVal);
+ ParamSize += SizeOf (Arg1Type);
+ ConsumeComma ();
- default:
- Internal ("Unknown constant type: %04X", Param.Flags);
+ /* Argument #2. */
+ ParseArg (&Arg2, Arg2Type);
+
+ /* Since strcmp 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.
+ */
+ if (Arg2.Flags & CF_CONST) {
+ LoadExpr (CF_NONE, &Arg2.Expr);
+ }
+
+ /* Emit the actual function call. This will also cleanup the stack. */
+ g_call (CF_FIXARGC, Func_strcmp, ParamSize);
+
+ /* Get the element counts of the arguments. Then get the larger of the
+ ** two into ECount1. This removes FLEXIBLE and UNSPECIFIED automatically
+ */
+ ECount1 = ArrayElementCount (&Arg1);
+ ECount2 = ArrayElementCount (&Arg2);
+ if (ECount2 > ECount1) {
+ ECount1 = ECount2;
+ }
+
+ if (IS_Get (&InlineStdFuncs)) {
+
+ /* If the second argument is the empty string literal, we can generate
+ ** more efficient code.
+ */
+ if (ED_IsLocLiteral (&Arg2.Expr) &&
+ IS_Get (&WritableStrings) == 0 &&
+ GetLiteralSize (Arg2.Expr.LVal) == 1 &&
+ GetLiteralStr (Arg2.Expr.LVal)[0] == '\0') {
+
+ /* Drop the generated code so we have the first argument in the
+ ** primary
+ */
+ RemoveCode (&Arg1.Push);
+
+ /* We don't need the literal any longer */
+ ReleaseLiteral (Arg2.Expr.LVal);
+
+ /* We do now have Arg1 in the primary. Load the first character from
+ ** this string and cast to int. This is the function result.
+ */
+ IsArray = IsTypeArray (Arg1.Type) && ED_IsRVal (&Arg1.Expr);
+ if (IsArray && ED_IsLocStack (&Arg1.Expr) &&
+ (Offs = ED_GetStackOffs (&Arg1.Expr, 0) < 256)) {
+ /* Drop the generated code */
+ RemoveCode (&Arg1.Load);
+
+ /* Generate code */
+ AddCodeLine ("ldy #$%02X", Offs);
+ AddCodeLine ("ldx #$00");
+ AddCodeLine ("lda (sp),y");
+ } else if (IsArray && ED_IsLocConst (&Arg1.Expr)) {
+ /* Drop the generated code */
+ RemoveCode (&Arg1.Load);
+
+ /* Generate code */
+ AddCodeLine ("ldx #$00");
+ AddCodeLine ("lda %s", ED_GetLabelName (&Arg1.Expr, 0));
+ } else {
+ /* Drop part of the generated code so we have the first argument
+ ** in the primary
+ */
+ RemoveCode (&Arg1.Push);
+
+ /* Fetch the first char */
+ g_getind (CF_CHAR | CF_UNSIGNED, 0);
+ }
+
+ } else if ((IS_Get (&CodeSizeFactor) >= 165) &&
+ ((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 (&EagerlyInlineFuncs) || (ECount1 > 0 && ECount1 < 256))) {
+
+ unsigned Entry, Loop, Fin; /* Labels */
+ const char* Load;
+ const char* Compare;
+
+ if (ED_IsLVal (&Arg1.Expr) && ED_IsLocRegister (&Arg1.Expr)) {
+ Load = "lda (%s),y";
+ } else {
+ Load = "lda %s,y";
+ }
+ if (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr)) {
+ Compare = "cmp (%s),y";
+ } else {
+ Compare = "cmp %s,y";
+ }
+
+ /* Drop the generated code */
+ RemoveCode (&Arg1.Expr.Start);
+
+ /* We need labels */
+ Entry = GetLocalLabel ();
+ Loop = GetLocalLabel ();
+ Fin = GetLocalLabel ();
+
+ /* Generate strcmp code */
+ AddCodeLine ("ldy #$00");
+ AddCodeLine ("beq %s", LocalLabelName (Entry));
+ g_defcodelabel (Loop);
+ AddCodeLine ("tax");
+ AddCodeLine ("beq %s", LocalLabelName (Fin));
+ AddCodeLine ("iny");
+ g_defcodelabel (Entry);
+ AddCodeLine (Load, ED_GetLabelName (&Arg1.Expr, 0));
+ AddCodeLine (Compare, ED_GetLabelName (&Arg2.Expr, 0));
+ AddCodeLine ("beq %s", LocalLabelName (Loop));
+ AddCodeLine ("ldx #$01");
+ AddCodeLine ("bcs %s", LocalLabelName (Fin));
+ AddCodeLine ("ldx #$FF");
+ g_defcodelabel (Fin);
+
+ } else if ((IS_Get (&CodeSizeFactor) > 190) &&
+ ((ED_IsRVal (&Arg2.Expr) && ED_IsLocConst (&Arg2.Expr)) ||
+ (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr))) &&
+ (IS_Get (&EagerlyInlineFuncs) || (ECount1 > 0 && ECount1 < 256))) {
+
+ unsigned Entry, Loop, Fin; /* Labels */
+ const char* Compare;
+
+ if (ED_IsLVal (&Arg2.Expr) && ED_IsLocRegister (&Arg2.Expr)) {
+ Compare = "cmp (%s),y";
+ } else {
+ Compare = "cmp %s,y";
+ }
+
+ /* Drop the generated code */
+ RemoveCode (&Arg1.Push);
+
+ /* We need labels */
+ Entry = GetLocalLabel ();
+ Loop = GetLocalLabel ();
+ Fin = GetLocalLabel ();
+
+ /* Store Arg1 into ptr1 */
+ AddCodeLine ("sta ptr1");
+ AddCodeLine ("stx ptr1+1");
+
+ /* Generate strcmp code */
+ AddCodeLine ("ldy #$00");
+ AddCodeLine ("beq %s", LocalLabelName (Entry));
+ g_defcodelabel (Loop);
+ AddCodeLine ("tax");
+ AddCodeLine ("beq %s", LocalLabelName (Fin));
+ AddCodeLine ("iny");
+ g_defcodelabel (Entry);
+ AddCodeLine ("lda (ptr1),y");
+ AddCodeLine (Compare, ED_GetLabelName (&Arg2.Expr, 0));
+ AddCodeLine ("beq %s", LocalLabelName (Loop));
+ AddCodeLine ("ldx #$01");
+ AddCodeLine ("bcs %s", LocalLabelName (Fin));
+ AddCodeLine ("ldx #$FF");
+ g_defcodelabel (Fin);
}
+ }
+
+ /* The function result is an rvalue in the primary register */
+ ED_MakeRValExpr (Expr);
+ Expr->Type = GetFuncReturn (Expr->Type);
+
+ /* We expect the closing brace */
+ ConsumeRParen ();
+}
+
+
+/*****************************************************************************/
+/* strcpy */
+/*****************************************************************************/
+
+
+
+static void StdFunc_strcpy (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
+/* Handle the strcpy function */
+{
+ /* Argument types: (char*, const char*) */
+ static Type Arg1Type[] = { TYPE(T_PTR), TYPE(T_CHAR), TYPE(T_END) };
+ static Type Arg2Type[] = { TYPE(T_PTR), TYPE(T_CHAR|T_QUAL_CONST), TYPE(T_END) };
+
+ ArgDesc Arg1, Arg2;
+ unsigned ParamSize = 0;
+ long ECount;
+ unsigned L1;
+
+ /* Setup the argument type string */
+ Arg1Type[1].C = GetDefaultChar ();
+ Arg2Type[1].C = GetDefaultChar () | T_QUAL_CONST;
+
+ /* Argument #1 */
+ ParseArg (&Arg1, Arg1Type);
+ g_push (Arg1.Flags, Arg1.Expr.IVal);
+ GetCodePos (&Arg1.End);
+ 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) {
+ LoadExpr (CF_NONE, &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 1 if it is an array */
+ ECount = ArrayElementCount (&Arg1);
+
+ if (IS_Get (&InlineStdFuncs)) {
+
+ /* 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 (&EagerlyInlineFuncs) ||
+ (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 (&Arg1.Expr.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;
+
+ /* Bail out, no need for further processing */
+ goto ExitPoint;
+ }
+
+ 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.IVal < 256);
+
+ /* Calculate the real stack offset */
+ int Offs = ED_GetStackOffs (&Arg2.Expr, 0);
+
+ /* Drop the generated code */
+ RemoveCode (&Arg1.Expr.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;
+
+ /* Bail out, no need for further processing */
+ goto ExitPoint;
+ }
+
+ 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.IVal < 256);
+
+ /* Calculate the real stack offset */
+ int Offs = ED_GetStackOffs (&Arg1.Expr, 0);
+
+ /* Drop the generated code */
+ RemoveCode (&Arg1.Expr.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 %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;
+
+ /* Bail out, no need for further processing */
+ goto ExitPoint;
+ }
+ }
+
+ /* 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 ();
+}
+
+
+
+/*****************************************************************************/
+/* strlen */
+/*****************************************************************************/
+
+
+
+static void StdFunc_strlen (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
+/* Handle the strlen function */
+{
+ static Type ArgType[] = { TYPE(T_PTR), TYPE(T_CHAR|T_QUAL_CONST), TYPE(T_END) };
+ ExprDesc Arg;
+ int IsArray;
+ int IsPtr;
+ int IsByteIndex;
+ long ECount;
+ unsigned L;
+
+ /* Setup the argument type string */
+ ArgType[1].C = GetDefaultChar () | T_QUAL_CONST;
+
+ /* Evaluate the parameter */
+ hie1 (&Arg);
+
+ /* 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);
+ }
+
+ /* 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);
+
+ /* Do type conversion */
+ TypeConversion (&Arg, ArgType);
+
+ if (IS_Get (&Optimize)) {
+
+ /* 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) {
- /* Not an array with a constant address. Load parameter into primary */
- ExprLoad (CF_NONE, &Param);
+ /* Constant string literal */
+ ED_MakeConstAbs (Expr, GetLiteralSize (Arg.LVal) - 1, type_size_t);
+ /* We don't need the literal any longer */
+ ReleaseLiteral (Arg.LVal);
+
+ /* Bail out, no need for further improvements */
+ goto ExitPoint;
+ }
}
- /* Generate the strlen code */
- g_strlen (CodeFlags, ParamName, Param.Val);
+ if (IS_Get (&InlineStdFuncs)) {
+
+ /* We will inline strlen for arrays with constant addresses, if either
+ ** requested on the command line, or the array is smaller than 256,
+ ** so the inlining is considered safe.
+ */
+ if (ED_IsLocConst (&Arg) && IsArray &&
+ (IS_Get (&EagerlyInlineFuncs) || IsByteIndex)) {
+
+ /* Generate the strlen code */
+ L = GetLocalLabel ();
+ AddCodeLine ("ldy #$FF");
+ g_defcodelabel (L);
+ AddCodeLine ("iny");
+ AddCodeLine ("ldx %s,y", ED_GetLabelName (&Arg, 0));
+ AddCodeLine ("bne %s", LocalLabelName (L));
+ AddCodeLine ("tya");
+
+ /* The function result is an rvalue in the primary register */
+ ED_MakeRValExpr (Expr);
+ Expr->Type = type_size_t;
+
+ /* Bail out, no need for further processing */
+ goto ExitPoint;
+ }
+
+ /* We will inline strlen for arrays on the stack, if the array is
+ ** completely within the reach of a byte sized index register.
+ */
+ if (ED_IsLocStack (&Arg) && IsArray && IsByteIndex &&
+ (Arg.IVal - StackPtr) + ECount < 256) {
+
+ /* Calculate the true stack offset */
+ int Offs = ED_GetStackOffs (&Arg, 0);
+
+ /* Generate the strlen code */
+ L = GetLocalLabel ();
+ AddCodeLine ("ldx #$FF");
+ AddCodeLine ("ldy #$%02X", (unsigned char) (Offs-1));
+ g_defcodelabel (L);
+ AddCodeLine ("inx");
+ AddCodeLine ("iny");
+ AddCodeLine ("lda (sp),y");
+ AddCodeLine ("bne %s", LocalLabelName (L));
+ AddCodeLine ("txa");
+ AddCodeLine ("ldx #$00");
+
+ /* The function result is an rvalue in the primary register */
+ ED_MakeRValExpr (Expr);
+ Expr->Type = type_size_t;
+
+ /* Bail out, no need for further processing */
+ goto ExitPoint;
+ }
+
+ /* 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.
+ */
+ if (ED_IsLocRegister (&Arg) && ED_IsLVal (&Arg) && IsPtr &&
+ IS_Get (&EagerlyInlineFuncs)) {
+
+ /* 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;
+
+ /* Bail out, no need for further processing */
+ goto ExitPoint;
+ }
+
+ /* 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).
+ */
+ if (IS_Get (&CodeSizeFactor) > 400 && IS_Get (&EagerlyInlineFuncs)) {
+
+ /* Load the expression into the primary */
+ LoadExpr (CF_NONE, &Arg);
+
+ /* 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;
+
+ /* Bail out, no need for further processing */
+ goto ExitPoint;
+ }
+ }
+
+ /* Load the expression into the primary */
+ LoadExpr (CF_NONE, &Arg);
+
+ /* Call the strlen function */
+ AddCodeLine ("jsr _%s", Func_strlen);
+
+ /* The function result is an rvalue in the primary register */
+ ED_MakeRValExpr (Expr);
+ Expr->Type = type_size_t;
ExitPoint:
/* We expect the closing brace */
/*****************************************************************************/
-/* Code */
+/* Code */
/*****************************************************************************/
-int IsStdFunc (const char* Name)
+int FindStdFunc (const char* Name)
/* Determine if the given function is a known standard function that may be
- * called in a special way.
- */
+** called in a special way. If so, return the index, otherwise return -1.
+*/
{
/* Look into the table for known names */
- return FindFunc (Name) != 0;
+ struct StdFuncDesc* D =
+ bsearch (Name, StdFuncs, FUNC_COUNT, sizeof (StdFuncs[0]), CmpFunc);
+
+ /* Return the function index or -1 */
+ if (D == 0) {
+ return -1;
+ } else {
+ return D - StdFuncs;
+ }
}
-void HandleStdFunc (FuncDesc* F, ExprDesc* lval)
+void HandleStdFunc (int Index, FuncDesc* F, ExprDesc* lval)
/* Generate code for a known standard function. */
{
+ struct StdFuncDesc* D;
+
/* Get a pointer to the table entry */
- struct StdFuncDesc* D = FindFunc ((const char*) lval->Name);
- CHECK (D != 0);
+ CHECK (Index >= 0 && Index < (int)FUNC_COUNT);
+ D = StdFuncs + Index;
/* Call the handler function */
D->Handler (F, lval);
}
-
-
-