Move the compiler stack pointer into its own module.
Improved the inlining of standard C functions. Added more standard functions to inline. git-svn-id: svn://svn.cc65.org/cc65/trunk@3095 b7a2c559-68d2-44c3-8de9-860c34a00d81
This commit is contained in:
@@ -46,6 +46,7 @@
|
||||
#include "function.h"
|
||||
#include "litpool.h"
|
||||
#include "scanner.h"
|
||||
#include "stackptr.h"
|
||||
#include "symtab.h"
|
||||
#include "asmstmt.h"
|
||||
|
||||
|
||||
@@ -54,23 +54,13 @@
|
||||
#include "error.h"
|
||||
#include "global.h"
|
||||
#include "segments.h"
|
||||
#include "stackptr.h"
|
||||
#include "textseg.h"
|
||||
#include "util.h"
|
||||
#include "codegen.h"
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Data */
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
|
||||
/* Compiler relative stack pointer */
|
||||
int StackPtr = 0;
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Helpers */
|
||||
/*****************************************************************************/
|
||||
|
||||
@@ -85,9 +85,6 @@
|
||||
|
||||
|
||||
|
||||
/* Compiler relative stackpointer */
|
||||
extern int StackPtr;
|
||||
|
||||
/* Forward */
|
||||
struct StrBuf;
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "macrotab.h"
|
||||
#include "preproc.h"
|
||||
#include "scanner.h"
|
||||
#include "stackptr.h"
|
||||
#include "stdfunc.h"
|
||||
#include "symtab.h"
|
||||
#include "typecmp.h"
|
||||
@@ -97,7 +98,7 @@ static unsigned GlobalModeFlags (unsigned Flags)
|
||||
|
||||
|
||||
|
||||
static void ExprWithCheck (void (*Func) (ExprDesc*), ExprDesc *Expr)
|
||||
void ExprWithCheck (void (*Func) (ExprDesc*), ExprDesc *Expr)
|
||||
/* Call an expression function with checks. */
|
||||
{
|
||||
/* Remember the stack pointer */
|
||||
|
||||
@@ -23,6 +23,9 @@
|
||||
|
||||
|
||||
|
||||
void ExprWithCheck (void (*Func) (ExprDesc*), ExprDesc *Expr);
|
||||
/* Call an expression function with checks. */
|
||||
|
||||
void PushAddr (const ExprDesc* Expr);
|
||||
/* If the expression contains an address that was somehow evaluated,
|
||||
* push this address on the stack. This is a helper function for all
|
||||
|
||||
@@ -34,12 +34,14 @@
|
||||
|
||||
|
||||
/* common */
|
||||
#include "check.h"
|
||||
#include "xsprintf.h"
|
||||
|
||||
/* cc65 */
|
||||
#include "asmlabel.h"
|
||||
#include "datatype.h"
|
||||
#include "error.h"
|
||||
#include "stackptr.h"
|
||||
#include "symentry.h"
|
||||
#include "exprdesc.h"
|
||||
|
||||
@@ -123,6 +125,19 @@ const char* ED_GetLabelName (const ExprDesc* Expr, long Offs)
|
||||
|
||||
|
||||
|
||||
int ED_GetStackOffs (const ExprDesc* Expr, int Offs)
|
||||
/* Get the stack offset of an address on the stack in Expr taking into account
|
||||
* an additional offset in Offs.
|
||||
*/
|
||||
{
|
||||
PRECONDITION (ED_IsLocStack (Expr));
|
||||
Offs += ((int) Expr->Val) - StackPtr;
|
||||
CHECK (Offs >= 0); /* Cannot handle negative stack offsets */
|
||||
return Offs;
|
||||
}
|
||||
|
||||
|
||||
|
||||
ExprDesc* ED_MakeConstAbs (ExprDesc* Expr, long Value, type* Type)
|
||||
/* Make Expr an absolute const with the given value and type. */
|
||||
{
|
||||
|
||||
@@ -229,6 +229,11 @@ const char* ED_GetLabelName (const ExprDesc* Expr, long Offs);
|
||||
* call to the function.
|
||||
*/
|
||||
|
||||
int ED_GetStackOffs (const ExprDesc* Expr, int Offs);
|
||||
/* Get the stack offset of an address on the stack in Expr taking into account
|
||||
* an additional offset in Offs.
|
||||
*/
|
||||
|
||||
ExprDesc* ED_MakeConstAbs (ExprDesc* Expr, long Value, type* Type);
|
||||
/* Make Expr an absolute const with the given value and type. */
|
||||
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
#include "locals.h"
|
||||
#include "scanner.h"
|
||||
#include "segments.h"
|
||||
#include "stackptr.h"
|
||||
#include "stmt.h"
|
||||
#include "symtab.h"
|
||||
#include "function.h"
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
#include "function.h"
|
||||
#include "global.h"
|
||||
#include "locals.h"
|
||||
#include "stackptr.h"
|
||||
#include "symtab.h"
|
||||
#include "typeconv.h"
|
||||
|
||||
|
||||
@@ -78,6 +78,7 @@ OBJS = anonname.o \
|
||||
scanner.o \
|
||||
scanstrbuf.o \
|
||||
segments.o \
|
||||
stackptr.o \
|
||||
stdfunc.o \
|
||||
stdnames.o \
|
||||
stmt.o \
|
||||
|
||||
@@ -112,6 +112,7 @@ OBJS = anonname.obj \
|
||||
scanner.obj \
|
||||
scanstrbuf.obj \
|
||||
segments.obj \
|
||||
stackptr.obj \
|
||||
stdfunc.obj \
|
||||
stdnames.obj \
|
||||
stmt.obj \
|
||||
|
||||
57
src/cc65/stackptr.c
Normal file
57
src/cc65/stackptr.c
Normal file
@@ -0,0 +1,57 @@
|
||||
/*****************************************************************************/
|
||||
/* */
|
||||
/* stackptr.c */
|
||||
/* */
|
||||
/* Manage the parameter stack pointer */
|
||||
/* */
|
||||
/* */
|
||||
/* */
|
||||
/* (C) 2004 Ullrich von Bassewitz */
|
||||
/* R<>merstra<72>e 52 */
|
||||
/* D-70794 Filderstadt */
|
||||
/* EMail: uz@cc65.org */
|
||||
/* */
|
||||
/* */
|
||||
/* This software is provided 'as-is', without any expressed or implied */
|
||||
/* warranty. In no event will the authors be held liable for any damages */
|
||||
/* arising from the use of this software. */
|
||||
/* */
|
||||
/* Permission is granted to anyone to use this software for any purpose, */
|
||||
/* including commercial applications, and to alter it and redistribute it */
|
||||
/* freely, subject to the following restrictions: */
|
||||
/* */
|
||||
/* 1. The origin of this software must not be misrepresented; you must not */
|
||||
/* claim that you wrote the original software. If you use this software */
|
||||
/* in a product, an acknowledgment in the product documentation would be */
|
||||
/* appreciated but is not required. */
|
||||
/* 2. Altered source versions must be plainly marked as such, and must not */
|
||||
/* be misrepresented as being the original software. */
|
||||
/* 3. This notice may not be removed or altered from any source */
|
||||
/* distribution. */
|
||||
/* */
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
|
||||
/* cc65 */
|
||||
#include "stackptr.h"
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Data */
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
|
||||
/* Compiler relative stackpointer */
|
||||
int StackPtr = 0;
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Code */
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
|
||||
63
src/cc65/stackptr.h
Normal file
63
src/cc65/stackptr.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/*****************************************************************************/
|
||||
/* */
|
||||
/* stackptr.h */
|
||||
/* */
|
||||
/* Manage the parameter stack pointer */
|
||||
/* */
|
||||
/* */
|
||||
/* */
|
||||
/* (C) 2004 Ullrich von Bassewitz */
|
||||
/* R<>merstra<72>e 52 */
|
||||
/* D-70794 Filderstadt */
|
||||
/* EMail: uz@cc65.org */
|
||||
/* */
|
||||
/* */
|
||||
/* This software is provided 'as-is', without any expressed or implied */
|
||||
/* warranty. In no event will the authors be held liable for any damages */
|
||||
/* arising from the use of this software. */
|
||||
/* */
|
||||
/* Permission is granted to anyone to use this software for any purpose, */
|
||||
/* including commercial applications, and to alter it and redistribute it */
|
||||
/* freely, subject to the following restrictions: */
|
||||
/* */
|
||||
/* 1. The origin of this software must not be misrepresented; you must not */
|
||||
/* claim that you wrote the original software. If you use this software */
|
||||
/* in a product, an acknowledgment in the product documentation would be */
|
||||
/* appreciated but is not required. */
|
||||
/* 2. Altered source versions must be plainly marked as such, and must not */
|
||||
/* be misrepresented as being the original software. */
|
||||
/* 3. This notice may not be removed or altered from any source */
|
||||
/* distribution. */
|
||||
/* */
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
|
||||
#ifndef STACKPTR_H
|
||||
#define STACKPTR_H
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Data */
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
|
||||
/* Compiler relative stackpointer */
|
||||
extern int StackPtr;
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Code */
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
|
||||
/* End of stackptr.h */
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
#include "check.h"
|
||||
|
||||
/* cc65 */
|
||||
#include "asmcode.h"
|
||||
#include "asmlabel.h"
|
||||
#include "codegen.h"
|
||||
#include "error.h"
|
||||
@@ -48,6 +49,7 @@
|
||||
#include "global.h"
|
||||
#include "litpool.h"
|
||||
#include "scanner.h"
|
||||
#include "stackptr.h"
|
||||
#include "stdfunc.h"
|
||||
#include "stdnames.h"
|
||||
#include "typeconv.h"
|
||||
@@ -60,7 +62,9 @@
|
||||
|
||||
|
||||
|
||||
static void StdFunc_memcpy (FuncDesc*, ExprDesc*);
|
||||
static void StdFunc_memset (FuncDesc*, ExprDesc*);
|
||||
static void StdFunc_strcpy (FuncDesc*, ExprDesc*);
|
||||
static void StdFunc_strlen (FuncDesc*, ExprDesc*);
|
||||
|
||||
|
||||
@@ -78,12 +82,25 @@ static struct StdFuncDesc {
|
||||
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 */
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
@@ -100,40 +117,366 @@ static int CmpFunc (const void* Key, const void* Elem)
|
||||
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Handle known functions */
|
||||
/* 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 ();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* memset */
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
@@ -144,29 +487,35 @@ static void StdFunc_memset (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
|
||||
/* 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 ();
|
||||
@@ -176,43 +525,409 @@ static void StdFunc_memset (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
|
||||
* 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) {
|
||||
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");
|
||||
}
|
||||
ExprLoad (CF_FORCECHAR, &Arg);
|
||||
|
||||
/* 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)))) {
|
||||
|
||||
/* We expect the closing brace */
|
||||
ConsumeRParen ();
|
||||
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);
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
/* 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);
|
||||
@@ -222,13 +937,18 @@ static void StdFunc_strlen (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
|
||||
* from the literal pool. Otherwise we have to calculate the length
|
||||
* at runtime.
|
||||
*/
|
||||
if (ED_IsLocLiteral (&Arg) && IS_Get (&WritableStrings)) {
|
||||
if (ED_IsLocLiteral (&Arg) && IS_Get (&WritableStrings) == 0) {
|
||||
|
||||
/* Constant string literal */
|
||||
ED_MakeConstAbs (Expr, strlen (GetLiteral (Arg.Val)), type_size_t);
|
||||
ResetLiteralPoolOffs (Arg.Val);
|
||||
|
||||
} else {
|
||||
/* 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)) {
|
||||
|
||||
/* Generate the strlen code */
|
||||
L = GetLocalLabel ();
|
||||
@@ -244,15 +964,14 @@ static void StdFunc_strlen (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
|
||||
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 ();
|
||||
@@ -270,10 +989,12 @@ static void StdFunc_strlen (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
|
||||
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 ();
|
||||
@@ -289,10 +1010,29 @@ static void StdFunc_strlen (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
|
||||
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);
|
||||
|
||||
@@ -55,6 +55,7 @@
|
||||
#include "loop.h"
|
||||
#include "pragma.h"
|
||||
#include "scanner.h"
|
||||
#include "stackptr.h"
|
||||
#include "swstmt.h"
|
||||
#include "symtab.h"
|
||||
#include "stmt.h"
|
||||
@@ -516,7 +517,7 @@ int Statement (int* PendingToken)
|
||||
* NULL, the function will skip the token.
|
||||
*/
|
||||
{
|
||||
ExprDesc lval;
|
||||
ExprDesc Expr;
|
||||
int GotBreak;
|
||||
|
||||
/* Assume no pending token */
|
||||
@@ -590,7 +591,13 @@ int Statement (int* PendingToken)
|
||||
|
||||
default:
|
||||
/* Actual statement */
|
||||
Expression0 (&lval);
|
||||
ExprWithCheck (hie0, &Expr);
|
||||
/* Load the result only if it is an lvalue and the type is
|
||||
* marked as volatile. Otherwise the load is useless.
|
||||
*/
|
||||
if (ED_IsLVal (&Expr) && IsQualVolatile (Expr.Type)) {
|
||||
ExprLoad (CF_NONE, &Expr);
|
||||
}
|
||||
CheckSemi (PendingToken);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
/* */
|
||||
/* */
|
||||
/* (C) 1998-2004 Ullrich von Bassewitz */
|
||||
/* Wacholderweg 14 */
|
||||
/* D-70597 Stuttgart */
|
||||
/* R<EFBFBD>merstra<EFBFBD>e 52 */
|
||||
/* D-70794 Filderstadt */
|
||||
/* EMail: uz@cc65.org */
|
||||
/* */
|
||||
/* */
|
||||
@@ -50,6 +50,7 @@
|
||||
#include "global.h"
|
||||
#include "loop.h"
|
||||
#include "scanner.h"
|
||||
#include "stackptr.h"
|
||||
#include "stmt.h"
|
||||
#include "swstmt.h"
|
||||
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
#include "error.h"
|
||||
#include "funcdesc.h"
|
||||
#include "global.h"
|
||||
#include "stackptr.h"
|
||||
#include "symentry.h"
|
||||
#include "typecmp.h"
|
||||
#include "symtab.h"
|
||||
@@ -672,7 +673,7 @@ SymEntry* AddLocalSym (const char* Name, const type* Type, unsigned Flags, int O
|
||||
Entry->V.Offs = Offs;
|
||||
} else if ((Flags & SC_REGISTER) == SC_REGISTER) {
|
||||
Entry->V.R.RegOffs = Offs;
|
||||
Entry->V.R.SaveOffs = StackPtr; /* ### Cleaner! */
|
||||
Entry->V.R.SaveOffs = StackPtr;
|
||||
} else if ((Flags & SC_STATIC) == SC_STATIC) {
|
||||
/* Generate the assembler name from the label number */
|
||||
Entry->V.Label = Offs;
|
||||
|
||||
Reference in New Issue
Block a user