mirror of https://github.com/cc65/cc65.git
1418 lines
51 KiB
C
1418 lines
51 KiB
C
/*****************************************************************************/
|
|
/* */
|
|
/* stdfunc.c */
|
|
/* */
|
|
/* Handle inlining of known functions for the cc65 compiler */
|
|
/* */
|
|
/* */
|
|
/* */
|
|
/* (C) 1998-2010 Ullrich von Bassewitz */
|
|
/* Roemerstrasse 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. */
|
|
/* */
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
/* common */
|
|
#include "attrib.h"
|
|
#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 */
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
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 */
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
/* Table with all known functions and their handlers. Must be sorted
|
|
** alphabetically!
|
|
*/
|
|
static struct StdFuncDesc {
|
|
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]))
|
|
|
|
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 */
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
static int CmpFunc (const void* Key, const void* Elem)
|
|
/* Compare function for bsearch */
|
|
{
|
|
return strcmp ((const char*) Key, ((const struct StdFuncDesc*) Elem)->Name);
|
|
}
|
|
|
|
|
|
|
|
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, const Type* Type, ExprDesc* Expr)
|
|
/* 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 */
|
|
Arg->Flags = CF_FORCECHAR;
|
|
|
|
/* Remember the required argument type */
|
|
Arg->ArgType = Type;
|
|
|
|
/* Init expression */
|
|
ED_Init (&Arg->Expr);
|
|
Arg->Expr.Flags |= Expr->Flags & E_MASK_KEEP_SUBEXPR;
|
|
|
|
/* Read the expression we're going to pass to the function */
|
|
MarkedExprWithCheck (hie1, &Arg->Expr);
|
|
|
|
/* Remember the actual argument type */
|
|
Arg->Type = Arg->Expr.Type;
|
|
|
|
/* Convert this expression to the expected 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->Expr) && ED_CodeRangeIsEmpty (&Arg->Expr)) {
|
|
/* Remember that we have a constant value */
|
|
Arg->Flags |= CF_CONST;
|
|
} else {
|
|
/* Load into the primary */
|
|
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 */
|
|
Arg->Flags |= TypeOf (Arg->Expr.Type);
|
|
|
|
/* Propagate from subexpressions */
|
|
Expr->Flags |= Arg->Expr.Flags & E_MASK_VIRAL;
|
|
}
|
|
|
|
|
|
|
|
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 const Type* Arg1Type = type_void_p;
|
|
static const Type* Arg2Type = type_c_void_p;
|
|
static const Type* Arg3Type = type_size_t;
|
|
|
|
ArgDesc Arg1, Arg2, Arg3;
|
|
unsigned ParamSize = 0;
|
|
unsigned Label;
|
|
int Offs;
|
|
|
|
/* Argument #1 */
|
|
ParseArg (&Arg1, Arg1Type, Expr);
|
|
g_push (Arg1.Flags, Arg1.Expr.IVal);
|
|
GetCodePos (&Arg1.End);
|
|
ParamSize += SizeOf (Arg1Type);
|
|
ConsumeComma ();
|
|
|
|
/* Argument #2 */
|
|
ParseArg (&Arg2, Arg2Type, Expr);
|
|
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, Expr);
|
|
if (Arg3.Flags & CF_CONST) {
|
|
LoadExpr (CF_NONE, &Arg3.Expr);
|
|
}
|
|
|
|
/* We still need to append deferred inc/dec before calling into the function */
|
|
DoDeferred (SQP_KEEP_EAX, &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_IsLocNone (&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_IsLocNone (&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_FinalizeRValLoad (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_FinalizeRValLoad (Expr);
|
|
Expr->Type = GetFuncReturn (Expr->Type);
|
|
|
|
ExitPoint:
|
|
/* We expect the closing brace */
|
|
ConsumeRParen ();
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
/* memset */
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
static void StdFunc_memset (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
|
|
/* Handle the memset function */
|
|
{
|
|
/* Argument types: (void*, int, size_t) */
|
|
static const Type* Arg1Type = type_void_p;
|
|
static const Type* Arg2Type = type_int;
|
|
static const Type* Arg3Type = type_size_t;
|
|
|
|
ArgDesc Arg1, Arg2, Arg3;
|
|
int MemSet = 1; /* Use real memset if true */
|
|
unsigned ParamSize = 0;
|
|
unsigned Label;
|
|
|
|
/* Argument #1 */
|
|
ParseArg (&Arg1, Arg1Type, Expr);
|
|
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.
|
|
*/
|
|
ParseArg (&Arg2, Arg2Type, Expr);
|
|
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 (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.
|
|
*/
|
|
ParseArg (&Arg3, Arg3Type, Expr);
|
|
if (Arg3.Flags & CF_CONST) {
|
|
LoadExpr (CF_NONE, &Arg3.Expr);
|
|
}
|
|
|
|
/* We still need to append deferred inc/dec before calling into the function */
|
|
DoDeferred (SQP_KEEP_EAX, &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.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_FinalizeRValLoad (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_FinalizeRValLoad (Expr);
|
|
Expr->Type = GetFuncReturn (Expr->Type);
|
|
|
|
ExitPoint:
|
|
/* We expect the closing brace */
|
|
ConsumeRParen ();
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
/* strcmp */
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
static void StdFunc_strcmp (FuncDesc* F attribute ((unused)), ExprDesc* Expr)
|
|
/* Handle the strcmp function */
|
|
{
|
|
/* Argument types: (const char*, const char*) */
|
|
static const Type* Arg1Type = type_c_char_p;
|
|
static const Type* Arg2Type = type_c_char_p;
|
|
|
|
ArgDesc Arg1, Arg2;
|
|
unsigned ParamSize = 0;
|
|
long ECount1;
|
|
long ECount2;
|
|
int IsArray;
|
|
int Offs;
|
|
|
|
/* Argument #1 */
|
|
ParseArg (&Arg1, Arg1Type, Expr);
|
|
g_push (Arg1.Flags, Arg1.Expr.IVal);
|
|
ParamSize += SizeOf (Arg1Type);
|
|
ConsumeComma ();
|
|
|
|
/* Argument #2. */
|
|
ParseArg (&Arg2, Arg2Type, Expr);
|
|
|
|
/* 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);
|
|
}
|
|
|
|
/* We still need to append deferred inc/dec before calling into the function */
|
|
DoDeferred (SQP_KEEP_EAX, &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.V.LVal) == 1 &&
|
|
GetLiteralStr (Arg2.Expr.V.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.V.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_FinalizeRValLoad (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 const Type* Arg1Type = type_char_p;
|
|
static const Type* Arg2Type = type_c_char_p;
|
|
|
|
ArgDesc Arg1, Arg2;
|
|
unsigned ParamSize = 0;
|
|
long ECount;
|
|
unsigned L1;
|
|
|
|
/* Argument #1 */
|
|
ParseArg (&Arg1, Arg1Type, Expr);
|
|
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, Expr);
|
|
if (Arg2.Flags & CF_CONST) {
|
|
LoadExpr (CF_NONE, &Arg2.Expr);
|
|
}
|
|
|
|
/* We still need to append deferred inc/dec before calling into the function */
|
|
DoDeferred (SQP_KEEP_EAX, &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_IsLocNone (&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_IsLocNone (&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_FinalizeRValLoad (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 const Type* ArgType = type_c_char_p;
|
|
ExprDesc Arg;
|
|
int IsArray;
|
|
int IsPtr;
|
|
int IsByteIndex;
|
|
long ECount;
|
|
unsigned L;
|
|
|
|
ED_Init (&Arg);
|
|
Arg.Flags |= Expr->Flags & E_MASK_KEEP_SUBEXPR;
|
|
|
|
/* Evaluate the parameter */
|
|
hie1 (&Arg);
|
|
|
|
/* We still need to append deferred inc/dec before calling into the function */
|
|
DoDeferred (SQP_KEEP_EAX, &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) {
|
|
|
|
/* Constant string literal */
|
|
ED_MakeConstAbs (Expr, GetLiteralSize (Arg.V.LVal) - 1, type_size_t);
|
|
|
|
/* We don't need the literal any longer */
|
|
ReleaseLiteral (Arg.V.LVal);
|
|
|
|
/* Bail out, no need for further improvements */
|
|
goto ExitPoint;
|
|
}
|
|
}
|
|
|
|
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_FinalizeRValLoad (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_FinalizeRValLoad (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_FinalizeRValLoad (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_FinalizeRValLoad (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_FinalizeRValLoad (Expr);
|
|
Expr->Type = type_size_t;
|
|
|
|
ExitPoint:
|
|
/* We expect the closing brace */
|
|
ConsumeRParen ();
|
|
|
|
/* Propagate from subexpressions */
|
|
Expr->Flags |= Arg.Flags & E_MASK_VIRAL;
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
/* Code */
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
int FindStdFunc (const char* Name)
|
|
/* Determine if the given function is a known standard function that may be
|
|
** called in a special way. If so, return the index, otherwise return -1.
|
|
*/
|
|
{
|
|
/* Look into the table for known names */
|
|
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 (int Index, FuncDesc* F, ExprDesc* lval)
|
|
/* Generate code for a known standard function. */
|
|
{
|
|
struct StdFuncDesc* D;
|
|
|
|
/* Get a pointer to the table entry */
|
|
CHECK (Index >= 0 && Index < (int)FUNC_COUNT);
|
|
D = StdFuncs + Index;
|
|
|
|
/* Call the handler function */
|
|
D->Handler (F, lval);
|
|
|
|
/* We assume all function calls had side effects */
|
|
lval->Flags |= E_SIDE_EFFECTS;
|
|
}
|