1
0
mirror of https://github.com/cc65/cc65.git synced 2024-06-02 19:42:23 +00:00
cc65/src/cc65/expr.c
Greg King a0db846a97 Allowed old-style (K and R) function declarations to be fastcall.
That lets them match old-style definitions.  It avoids "Type conflict" error messages.  It allows shorter function calls.

Fixed the types of some variables in "test/ref/otccex.c".  It avoids crashes on 64-bit Windows (32-bit Windows with 64-bit pointers).
2019-07-22 09:26:23 -04:00

3684 lines
120 KiB
C

/* expr.c
**
** 1998-06-21, Ullrich von Bassewitz
** 2017-12-05, Greg King
*/
#include <stdio.h>
#include <stdlib.h>
/* common */
#include "check.h"
#include "debugflag.h"
#include "xmalloc.h"
/* cc65 */
#include "asmcode.h"
#include "asmlabel.h"
#include "asmstmt.h"
#include "assignment.h"
#include "codegen.h"
#include "declare.h"
#include "error.h"
#include "funcdesc.h"
#include "function.h"
#include "global.h"
#include "litpool.h"
#include "loadexpr.h"
#include "macrotab.h"
#include "preproc.h"
#include "scanner.h"
#include "shiftexpr.h"
#include "stackptr.h"
#include "standard.h"
#include "stdfunc.h"
#include "symtab.h"
#include "typecmp.h"
#include "typeconv.h"
#include "expr.h"
/*****************************************************************************/
/* Data */
/*****************************************************************************/
/* Generator attributes */
#define GEN_NOPUSH 0x01 /* Don't push lhs */
#define GEN_COMM 0x02 /* Operator is commutative */
#define GEN_NOFUNC 0x04 /* Not allowed for function pointers */
/* Map a generator function and its attributes to a token */
typedef struct {
token_t Tok; /* Token to map to */
unsigned Flags; /* Flags for generator function */
void (*Func) (unsigned, unsigned long); /* Generator func */
} GenDesc;
/* Descriptors for the operations */
static GenDesc GenPASGN = { TOK_PLUS_ASSIGN, GEN_NOPUSH, g_add };
static GenDesc GenSASGN = { TOK_MINUS_ASSIGN, GEN_NOPUSH, g_sub };
static GenDesc GenMASGN = { TOK_MUL_ASSIGN, GEN_NOPUSH, g_mul };
static GenDesc GenDASGN = { TOK_DIV_ASSIGN, GEN_NOPUSH, g_div };
static GenDesc GenMOASGN = { TOK_MOD_ASSIGN, GEN_NOPUSH, g_mod };
static GenDesc GenSLASGN = { TOK_SHL_ASSIGN, GEN_NOPUSH, g_asl };
static GenDesc GenSRASGN = { TOK_SHR_ASSIGN, GEN_NOPUSH, g_asr };
static GenDesc GenAASGN = { TOK_AND_ASSIGN, GEN_NOPUSH, g_and };
static GenDesc GenXOASGN = { TOK_XOR_ASSIGN, GEN_NOPUSH, g_xor };
static GenDesc GenOASGN = { TOK_OR_ASSIGN, GEN_NOPUSH, g_or };
/*****************************************************************************/
/* Helper functions */
/*****************************************************************************/
static unsigned GlobalModeFlags (const ExprDesc* Expr)
/* Return the addressing mode flags for the given expression */
{
switch (ED_GetLoc (Expr)) {
case E_LOC_ABS: return CF_ABSOLUTE;
case E_LOC_GLOBAL: return CF_EXTERNAL;
case E_LOC_STATIC: return CF_STATIC;
case E_LOC_REGISTER: return CF_REGVAR;
case E_LOC_STACK: return CF_NONE;
case E_LOC_PRIMARY: return CF_NONE;
case E_LOC_EXPR: return CF_NONE;
case E_LOC_LITERAL: return CF_STATIC; /* Same as static */
default:
Internal ("GlobalModeFlags: Invalid location flags value: 0x%04X", Expr->Flags);
/* NOTREACHED */
return 0;
}
}
void ExprWithCheck (void (*Func) (ExprDesc*), ExprDesc* Expr)
/* Call an expression function with checks. */
{
/* Remember the stack pointer */
int OldSP = StackPtr;
/* Call the expression function */
(*Func) (Expr);
/* Do some checks to see if code generation is still consistent */
if (StackPtr != OldSP) {
if (Debug) {
Error ("Code generation messed up: "
"StackPtr is %d, should be %d",
StackPtr, OldSP);
} else {
Internal ("Code generation messed up: "
"StackPtr is %d, should be %d",
StackPtr, OldSP);
}
}
}
void MarkedExprWithCheck (void (*Func) (ExprDesc*), ExprDesc* Expr)
/* Call an expression function with checks and record start and end of the
** generated code.
*/
{
CodeMark Start, End;
GetCodePos (&Start);
ExprWithCheck (Func, Expr);
GetCodePos (&End);
ED_SetCodeRange (Expr, &Start, &End);
}
static Type* promoteint (Type* lhst, Type* rhst)
/* In an expression with two ints, return the type of the result */
{
/* Rules for integer types:
** - If one of the values is a long, the result is long.
** - If one of the values is unsigned, the result is also unsigned.
** - Otherwise the result is an int.
*/
if (IsTypeLong (lhst) || IsTypeLong (rhst)) {
if (IsSignUnsigned (lhst) || IsSignUnsigned (rhst)) {
return type_ulong;
} else {
return type_long;
}
} else {
if (IsSignUnsigned (lhst) || IsSignUnsigned (rhst)) {
return type_uint;
} else {
return type_int;
}
}
}
static unsigned typeadjust (ExprDesc* lhs, ExprDesc* rhs, int NoPush)
/* Adjust the two values for a binary operation. lhs is expected on stack or
** to be constant, rhs is expected to be in the primary register or constant.
** The function will put the type of the result into lhs and return the
** code generator flags for the operation.
** If NoPush is given, it is assumed that the operation does not expect the lhs
** to be on stack, and that lhs is in a register instead.
** Beware: The function does only accept int types.
*/
{
unsigned ltype, rtype;
unsigned flags;
/* Get the type strings */
Type* lhst = lhs->Type;
Type* rhst = rhs->Type;
/* Generate type adjustment code if needed */
ltype = TypeOf (lhst);
if (ED_IsLocAbs (lhs)) {
ltype |= CF_CONST;
}
if (NoPush) {
/* Value is in primary register*/
ltype |= CF_REG;
}
rtype = TypeOf (rhst);
if (ED_IsLocAbs (rhs)) {
rtype |= CF_CONST;
}
flags = g_typeadjust (ltype, rtype);
/* Set the type of the result */
lhs->Type = promoteint (lhst, rhst);
/* Return the code generator flags */
return flags;
}
static const GenDesc* FindGen (token_t Tok, const GenDesc* Table)
/* Find a token in a generator table */
{
while (Table->Tok != TOK_INVALID) {
if (Table->Tok == Tok) {
return Table;
}
++Table;
}
return 0;
}
static int TypeSpecAhead (void)
/* Return true if some sort of type is waiting (helper for cast and sizeof()
** in hie10).
*/
{
SymEntry* Entry;
/* There's a type waiting if:
**
** We have an opening paren, and
** a. the next token is a type, or
** b. the next token is a type qualifier, or
** c. the next token is a typedef'd type
*/
return CurTok.Tok == TOK_LPAREN && (
TokIsType (&NextTok) ||
TokIsTypeQual (&NextTok) ||
(NextTok.Tok == TOK_IDENT &&
(Entry = FindSym (NextTok.Ident)) != 0 &&
SymIsTypeDef (Entry)));
}
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
** sorts of implicit or explicit assignment functions where the lvalue
** must be saved if it's not constant, before evaluating the rhs.
*/
{
/* Get the address on stack if needed */
if (ED_IsLocExpr (Expr)) {
/* Push the address (always a pointer) */
g_push (CF_PTR, 0);
}
}
static void WarnConstCompareResult (void)
/* If the result of a comparison is constant, this is suspicious when not in
** preprocessor mode.
*/
{
if (!Preprocessing && IS_Get (&WarnConstComparison) != 0) {
Warning ("Result of comparison is constant");
}
}
/*****************************************************************************/
/* code */
/*****************************************************************************/
static unsigned FunctionParamList (FuncDesc* Func, int IsFastcall)
/* Parse a function parameter list and pass the parameters to the called
** function. Depending on several criteria this may be done by just pushing
** each parameter separately, or creating the parameter frame once and then
** storing into this frame.
** The function returns the size of the parameters pushed.
*/
{
ExprDesc Expr;
/* Initialize variables */
SymEntry* Param = 0; /* Keep gcc silent */
unsigned ParamSize = 0; /* Size of parameters pushed */
unsigned ParamCount = 0; /* Number of parameters pushed */
unsigned FrameSize = 0; /* Size of parameter frame */
unsigned FrameParams = 0; /* Number of params in frame */
int FrameOffs = 0; /* Offset into parameter frame */
int Ellipsis = 0; /* Function is variadic */
/* As an optimization, we may allocate the complete parameter frame at
** once instead of pushing each parameter as it comes. We may do that,
** if...
**
** - optimizations that increase code size are enabled (allocating the
** stack frame at once gives usually larger code).
** - we have more than one parameter to push (don't count the last param
** for __fastcall__ functions).
**
** The FrameSize variable will contain a value > 0 if storing into a frame
** (instead of pushing) is enabled.
**
*/
if (IS_Get (&CodeSizeFactor) >= 200) {
/* Calculate the number and size of the parameters */
FrameParams = Func->ParamCount;
FrameSize = Func->ParamSize;
if (FrameParams > 0 && IsFastcall) {
/* Last parameter is not pushed */
FrameSize -= CheckedSizeOf (Func->LastParam->Type);
--FrameParams;
}
/* Do we have more than one parameter in the frame? */
if (FrameParams > 1) {
/* Okeydokey, setup the frame */
FrameOffs = StackPtr;
g_space (FrameSize);
StackPtr -= FrameSize;
} else {
/* Don't use a preallocated frame */
FrameSize = 0;
}
}
/* Parse the actual parameter list */
while (CurTok.Tok != TOK_RPAREN) {
unsigned Flags;
/* Count arguments */
++ParamCount;
/* Fetch the pointer to the next argument, check for too many args */
if (ParamCount <= Func->ParamCount) {
/* Beware: If there are parameters with identical names, they
** cannot go into the same symbol table, which means that in this
** case of errorneous input, the number of nodes in the symbol
** table and ParamCount are NOT equal. We have to handle this case
** below to avoid segmentation violations. Since we know that this
** problem can only occur if there is more than one parameter,
** we will just use the last one.
*/
if (ParamCount == 1) {
/* First argument */
Param = Func->SymTab->SymHead;
} else if (Param->NextSym != 0) {
/* Next argument */
Param = Param->NextSym;
CHECK ((Param->Flags & SC_PARAM) != 0);
}
} else if (!Ellipsis) {
/* Too many arguments. Do we have an open or empty param. list? */
if ((Func->Flags & (FD_VARIADIC | FD_EMPTY)) == 0) {
/* End of param list reached, no ellipsis */
Error ("Too many arguments in function call");
}
/* Assume an ellipsis even in case of errors to avoid an error
** message for each other argument.
*/
Ellipsis = 1;
}
/* Evaluate the parameter expression */
hie1 (&Expr);
/* If we don't have an argument spec, accept anything, otherwise
** convert the actual argument to the type needed.
*/
Flags = CF_NONE;
if (!Ellipsis) {
/* Convert the argument to the parameter type if needed */
TypeConversion (&Expr, Param->Type);
/* If we have a prototype, chars may be pushed as chars */
Flags |= CF_FORCECHAR;
} else {
/* No prototype available. Convert array to "pointer to first
** element", and function to "pointer to function".
*/
Expr.Type = PtrConversion (Expr.Type);
}
/* Load the value into the primary if it is not already there */
LoadExpr (Flags, &Expr);
/* Use the type of the argument for the push */
Flags |= TypeOf (Expr.Type);
/* If this is a fastcall function, don't push the last argument */
if ((CurTok.Tok == TOK_COMMA && NextTok.Tok != TOK_RPAREN) || !IsFastcall) {
unsigned ArgSize = sizeofarg (Flags);
if (FrameSize > 0) {
/* We have the space already allocated, store in the frame.
** Because of invalid type conversions (that have produced an
** error before), we can end up here with a non-aligned stack
** frame. Since no output will be generated anyway, handle
** these cases gracefully instead of doing a CHECK.
*/
if (FrameSize >= ArgSize) {
FrameSize -= ArgSize;
} else {
FrameSize = 0;
}
FrameOffs -= ArgSize;
/* Store */
g_putlocal (Flags | CF_NOKEEP, FrameOffs, Expr.IVal);
} else {
/* Push the argument */
g_push (Flags, Expr.IVal);
}
/* Calculate total parameter size */
ParamSize += ArgSize;
}
/* Check for end of argument list */
if (CurTok.Tok != TOK_COMMA) {
break;
}
NextToken ();
}
/* Check if we had enough parameters */
if (ParamCount < Func->ParamCount) {
Error ("Too few arguments in function call");
}
/* The function returns the size of all parameters pushed onto the stack.
** However, if there are parameters missing (which is an error and was
** flagged by the compiler) AND a stack frame was preallocated above,
** we would loose track of the stackpointer and generate an internal error
** later. So we correct the value by the parameters that should have been
** pushed to avoid an internal compiler error. Since an error was
** generated before, no code will be output anyway.
*/
return ParamSize + FrameSize;
}
static void FunctionCall (ExprDesc* Expr)
/* Perform a function call. */
{
FuncDesc* Func; /* Function descriptor */
int IsFuncPtr; /* Flag */
unsigned ParamSize; /* Number of parameter bytes */
CodeMark Mark;
int PtrOffs = 0; /* Offset of function pointer on stack */
int IsFastcall = 0; /* True if it's a fast-call function */
int PtrOnStack = 0; /* True if a pointer copy is on stack */
/* Skip the left paren */
NextToken ();
/* Get a pointer to the function descriptor from the type string */
Func = GetFuncDesc (Expr->Type);
/* Handle function pointers transparently */
IsFuncPtr = IsTypeFuncPtr (Expr->Type);
if (IsFuncPtr) {
/* Check whether it's a fastcall function that has parameters.
** Note: if a function is forward-declared in the old K & R style, then
** it may be called with any number of arguments, even though its
** parameter count is zero. Handle K & R functions as though there are
** parameters.
*/
IsFastcall = (Func->Flags & FD_VARIADIC) == 0 &&
(Func->ParamCount > 0 || (Func->Flags & FD_EMPTY)) &&
(AutoCDecl ?
IsQualFastcall (Expr->Type + 1) :
!IsQualCDecl (Expr->Type + 1));
/* Things may be difficult, depending on where the function pointer
** resides. If the function pointer is an expression of some sort
** (not a local or global variable), we have to evaluate this
** expression now and save the result for later. Since calls to
** function pointers may be nested, we must save it onto the stack.
** For fastcall functions we do also need to place a copy of the
** pointer on stack, since we cannot use a/x.
*/
PtrOnStack = IsFastcall || !ED_IsConst (Expr);
if (PtrOnStack) {
/* Not a global or local variable, or a fastcall function. Load
** the pointer into the primary and mark it as an expression.
*/
LoadExpr (CF_NONE, Expr);
ED_MakeRValExpr (Expr);
/* Remember the code position */
GetCodePos (&Mark);
/* Push the pointer onto the stack and remember the offset */
g_push (CF_PTR, 0);
PtrOffs = StackPtr;
}
} else {
/* Check function attributes */
if (Expr->Sym && SymHasAttr (Expr->Sym, atNoReturn)) {
/* For now, handle as if a return statement was encountered */
F_ReturnFound (CurrentFunc);
}
/* Check for known standard functions and inline them */
if (Expr->Name != 0) {
int StdFunc = FindStdFunc ((const char*) Expr->Name);
if (StdFunc >= 0) {
/* Inline this function */
HandleStdFunc (StdFunc, Func, Expr);
return;
}
}
/* If we didn't inline the function, get fastcall info */
IsFastcall = (Func->Flags & FD_VARIADIC) == 0 &&
(AutoCDecl ?
IsQualFastcall (Expr->Type) :
!IsQualCDecl (Expr->Type));
}
/* Parse the parameter list */
ParamSize = FunctionParamList (Func, IsFastcall);
/* We need the closing paren here */
ConsumeRParen ();
/* Special handling for function pointers */
if (IsFuncPtr) {
if (Func->WrappedCall) {
Warning ("Calling a wrapped function via a pointer, wrapped-call will not be used");
}
/* If the function is not a fastcall function, load the pointer to
** the function into the primary.
*/
if (!IsFastcall) {
/* Not a fastcall function - we may use the primary */
if (PtrOnStack) {
/* If we have no parameters, the pointer is still in the
** primary. Remove the code to push it and correct the
** stack pointer.
*/
if (ParamSize == 0) {
RemoveCode (&Mark);
PtrOnStack = 0;
} else {
/* Load from the saved copy */
g_getlocal (CF_PTR, PtrOffs);
}
} else {
/* Load from original location */
LoadExpr (CF_NONE, Expr);
}
/* Call the function */
g_callind (TypeOf (Expr->Type+1), ParamSize, PtrOffs);
} else {
/* Fastcall function. We cannot use the primary for the function
** pointer and must therefore use an offset to the stack location.
** Since fastcall functions may never be variadic, we can use the
** index register for this purpose.
*/
g_callind (CF_LOCAL, ParamSize, PtrOffs);
}
/* If we have a pointer on stack, remove it */
if (PtrOnStack) {
g_drop (SIZEOF_PTR);
pop (CF_PTR);
}
/* Skip T_PTR */
++Expr->Type;
} else {
/* Normal function */
if (Func->WrappedCall) {
char tmp[64];
StrBuf S = AUTO_STRBUF_INITIALIZER;
/* Store the WrappedCall data in tmp4 */
sprintf(tmp, "ldy #%u", Func->WrappedCallData);
SB_AppendStr (&S, tmp);
g_asmcode (&S);
SB_Clear(&S);
SB_AppendStr (&S, "sty tmp4");
g_asmcode (&S);
SB_Clear(&S);
/* Store the original function address in ptr4 */
SB_AppendStr (&S, "ldy #<(_");
SB_AppendStr (&S, (const char*) Expr->Name);
SB_AppendChar (&S, ')');
g_asmcode (&S);
SB_Clear(&S);
SB_AppendStr (&S, "sty ptr4");
g_asmcode (&S);
SB_Clear(&S);
SB_AppendStr (&S, "ldy #>(_");
SB_AppendStr (&S, (const char*) Expr->Name);
SB_AppendChar (&S, ')');
g_asmcode (&S);
SB_Clear(&S);
SB_AppendStr (&S, "sty ptr4+1");
g_asmcode (&S);
SB_Clear(&S);
SB_Done (&S);
g_call (TypeOf (Expr->Type), Func->WrappedCall->Name, ParamSize);
} else {
g_call (TypeOf (Expr->Type), (const char*) Expr->Name, ParamSize);
}
}
/* The function result is an rvalue in the primary register */
ED_MakeRValExpr (Expr);
Expr->Type = GetFuncReturn (Expr->Type);
}
static void Primary (ExprDesc* E)
/* This is the lowest level of the expression parser. */
{
SymEntry* Sym;
/* Initialize fields in the expression stucture */
ED_Init (E);
/* Character and integer constants. */
if (CurTok.Tok == TOK_ICONST || CurTok.Tok == TOK_CCONST) {
E->IVal = CurTok.IVal;
E->Flags = E_LOC_ABS | E_RTYPE_RVAL;
E->Type = CurTok.Type;
NextToken ();
return;
}
/* Floating point constant */
if (CurTok.Tok == TOK_FCONST) {
E->FVal = CurTok.FVal;
E->Flags = E_LOC_ABS | E_RTYPE_RVAL;
E->Type = CurTok.Type;
NextToken ();
return;
}
/* Process parenthesized subexpression by calling the whole parser
** recursively.
*/
if (CurTok.Tok == TOK_LPAREN) {
NextToken ();
hie0 (E);
ConsumeRParen ();
return;
}
/* If we run into an identifier in preprocessing mode, we assume that this
** is an undefined macro and replace it by a constant value of zero.
*/
if (Preprocessing && CurTok.Tok == TOK_IDENT) {
NextToken ();
ED_MakeConstAbsInt (E, 0);
return;
}
/* All others may only be used if the expression evaluation is not called
** recursively by the preprocessor.
*/
if (Preprocessing) {
/* Illegal expression in PP mode */
Error ("Preprocessor expression expected");
ED_MakeConstAbsInt (E, 1);
return;
}
switch (CurTok.Tok) {
case TOK_BOOL_AND:
/* A computed goto label address */
if (IS_Get (&Standard) >= STD_CC65) {
SymEntry* Entry;
NextToken ();
Entry = AddLabelSym (CurTok.Ident, SC_REF | SC_GOTO_IND);
/* output its label */
E->Flags = E_RTYPE_RVAL | E_LOC_STATIC;
E->Name = Entry->V.L.Label;
E->Type = PointerTo (type_void);
NextToken ();
} else {
Error ("Computed gotos are a C extension, not supported with this --standard");
ED_MakeConstAbsInt (E, 1);
}
break;
case TOK_IDENT:
/* Identifier. Get a pointer to the symbol table entry */
Sym = E->Sym = FindSym (CurTok.Ident);
/* Is the symbol known? */
if (Sym) {
/* We found the symbol - skip the name token */
NextToken ();
/* Check for illegal symbol types */
CHECK ((Sym->Flags & SC_LABEL) != SC_LABEL);
if (Sym->Flags & SC_TYPE) {
/* Cannot use type symbols */
Error ("Variable identifier expected");
/* Assume an int type to make E valid */
E->Flags = E_LOC_STACK | E_RTYPE_LVAL;
E->Type = type_int;
return;
}
/* Mark the symbol as referenced */
Sym->Flags |= SC_REF;
/* The expression type is the symbol type */
E->Type = Sym->Type;
/* Check for legal symbol types */
if ((Sym->Flags & SC_CONST) == SC_CONST) {
/* Enum or some other numeric constant */
E->Flags = E_LOC_ABS | E_RTYPE_RVAL;
E->IVal = Sym->V.ConstVal;
} else if ((Sym->Flags & SC_FUNC) == SC_FUNC) {
/* Function */
E->Flags = E_LOC_GLOBAL | E_RTYPE_LVAL;
E->Name = (uintptr_t) Sym->Name;
} else if ((Sym->Flags & SC_AUTO) == SC_AUTO) {
/* Local variable. If this is a parameter for a variadic
** function, we have to add some address calculations, and the
** address is not const.
*/
if ((Sym->Flags & SC_PARAM) == SC_PARAM && F_IsVariadic (CurrentFunc)) {
/* Variadic parameter */
g_leavariadic (Sym->V.Offs - F_GetParamSize (CurrentFunc));
E->Flags = E_LOC_EXPR | E_RTYPE_LVAL;
} else {
/* Normal parameter */
E->Flags = E_LOC_STACK | E_RTYPE_LVAL;
E->IVal = Sym->V.Offs;
}
} else if ((Sym->Flags & SC_REGISTER) == SC_REGISTER) {
/* Register variable, zero page based */
E->Flags = E_LOC_REGISTER | E_RTYPE_LVAL;
E->Name = Sym->V.R.RegOffs;
} else if ((Sym->Flags & SC_STATIC) == SC_STATIC) {
/* Static variable */
if (Sym->Flags & (SC_EXTERN | SC_STORAGE)) {
E->Flags = E_LOC_GLOBAL | E_RTYPE_LVAL;
E->Name = (uintptr_t) Sym->Name;
} else {
E->Flags = E_LOC_STATIC | E_RTYPE_LVAL;
E->Name = Sym->V.L.Label;
}
} else {
/* Local static variable */
E->Flags = E_LOC_STATIC | E_RTYPE_LVAL;
E->Name = Sym->V.Offs;
}
/* We've made all variables lvalues above. However, this is
** not always correct: An array is actually the address of its
** first element, which is a rvalue, and a function is a
** rvalue, too, because we cannot store anything in a function.
** So fix the flags depending on the type.
*/
if (IsTypeArray (E->Type) || IsTypeFunc (E->Type)) {
ED_MakeRVal (E);
}
} else {
/* We did not find the symbol. Remember the name, then skip it */
ident Ident;
strcpy (Ident, CurTok.Ident);
NextToken ();
/* IDENT is either an auto-declared function or an undefined variable. */
if (CurTok.Tok == TOK_LPAREN) {
/* C99 doesn't allow calls to undefined functions, so
** generate an error and otherwise a warning. Declare a
** function returning int. For that purpose, prepare a
** function signature for a function having an empty param
** list and returning int.
*/
if (IS_Get (&Standard) >= STD_C99) {
Error ("Call to undefined function '%s'", Ident);
} else {
Warning ("Call to undefined function '%s'", Ident);
}
Sym = AddGlobalSym (Ident, GetImplicitFuncType(), SC_EXTERN | SC_REF | SC_FUNC);
E->Type = Sym->Type;
E->Flags = E_LOC_GLOBAL | E_RTYPE_RVAL;
E->Name = (uintptr_t) Sym->Name;
} else {
/* Undeclared Variable */
Sym = AddLocalSym (Ident, type_int, SC_AUTO | SC_REF, 0);
E->Flags = E_LOC_STACK | E_RTYPE_LVAL;
E->Type = type_int;
Error ("Undefined symbol: '%s'", Ident);
}
}
break;
case TOK_SCONST:
case TOK_WCSCONST:
/* String literal */
E->LVal = UseLiteral (CurTok.SVal);
E->Type = GetCharArrayType (GetLiteralSize (CurTok.SVal));
E->Flags = E_LOC_LITERAL | E_RTYPE_RVAL;
E->IVal = 0;
E->Name = GetLiteralLabel (CurTok.SVal);
NextToken ();
break;
case TOK_ASM:
/* ASM statement */
AsmStatement ();
E->Flags = E_LOC_EXPR | E_RTYPE_RVAL;
E->Type = type_void;
break;
case TOK_A:
/* Register pseudo variable */
E->Type = type_uchar;
E->Flags = E_LOC_PRIMARY | E_RTYPE_LVAL;
NextToken ();
break;
case TOK_AX:
/* Register pseudo variable */
E->Type = type_uint;
E->Flags = E_LOC_PRIMARY | E_RTYPE_LVAL;
NextToken ();
break;
case TOK_EAX:
/* Register pseudo variable */
E->Type = type_ulong;
E->Flags = E_LOC_PRIMARY | E_RTYPE_LVAL;
NextToken ();
break;
default:
/* Illegal primary. Be sure to skip the token to avoid endless
** error loops.
*/
Error ("Expression expected");
NextToken ();
ED_MakeConstAbsInt (E, 1);
break;
}
}
static void ArrayRef (ExprDesc* Expr)
/* Handle an array reference. This function needs a rewrite. */
{
int ConstBaseAddr;
ExprDesc Subscript;
CodeMark Mark1;
CodeMark Mark2;
TypeCode Qualifiers;
Type* ElementType;
Type* tptr1;
/* Skip the bracket */
NextToken ();
/* Get the type of left side */
tptr1 = Expr->Type;
/* We can apply a special treatment for arrays that have a const base
** address. This is true for most arrays and will produce a lot better
** code. Check if this is a const base address.
*/
ConstBaseAddr = ED_IsRVal (Expr) &&
(ED_IsLocConst (Expr) || ED_IsLocStack (Expr));
/* If we have a constant base, we delay the address fetch */
GetCodePos (&Mark1);
if (!ConstBaseAddr) {
/* Get a pointer to the array into the primary */
LoadExpr (CF_NONE, Expr);
/* Get the array pointer on stack. Do not push more than 16
** bit, even if this value is greater, since we cannot handle
** other than 16bit stuff when doing indexing.
*/
GetCodePos (&Mark2);
g_push (CF_PTR, 0);
}
/* TOS now contains ptr to array elements. Get the subscript. */
MarkedExprWithCheck (hie0, &Subscript);
/* Check the types of array and subscript. We can either have a
** pointer/array to the left, in which case the subscript must be of an
** integer type, or we have an integer to the left, in which case the
** subscript must be a pointer/array.
** Since we do the necessary checking here, we can rely later on the
** correct types.
*/
Qualifiers = T_QUAL_NONE;
if (IsClassPtr (Expr->Type)) {
if (!IsClassInt (Subscript.Type)) {
Error ("Array subscript is not an integer");
/* To avoid any compiler errors, make the expression a valid int */
ED_MakeConstAbsInt (&Subscript, 0);
}
if (IsTypeArray (Expr->Type)) {
Qualifiers = GetQualifier (Expr->Type);
}
ElementType = Indirect (Expr->Type);
} else if (IsClassInt (Expr->Type)) {
if (!IsClassPtr (Subscript.Type)) {
Error ("Subscripted value is neither array nor pointer");
/* To avoid compiler errors, make the subscript a char[] at
** address 0.
*/
ED_MakeConstAbs (&Subscript, 0, GetCharArrayType (1));
} else if (IsTypeArray (Subscript.Type)) {
Qualifiers = GetQualifier (Subscript.Type);
}
ElementType = Indirect (Subscript.Type);
} else {
Error ("Cannot subscript");
/* To avoid compiler errors, fake both the array and the subscript, so
** we can just proceed.
*/
ED_MakeConstAbs (Expr, 0, GetCharArrayType (1));
ED_MakeConstAbsInt (&Subscript, 0);
ElementType = Indirect (Expr->Type);
}
/* The element type has the combined qualifiers from itself and the array,
** it is a member of (if any).
*/
if (GetQualifier (ElementType) != (GetQualifier (ElementType) | Qualifiers)) {
ElementType = TypeDup (ElementType);
ElementType->C |= Qualifiers;
}
/* If the subscript is a bit-field, load it and make it an rvalue */
if (ED_IsBitField (&Subscript)) {
LoadExpr (CF_NONE, &Subscript);
ED_MakeRValExpr (&Subscript);
}
/* Check if the subscript is constant absolute value */
if (ED_IsConstAbs (&Subscript) && ED_CodeRangeIsEmpty (&Subscript)) {
/* The array subscript is a numeric constant. If we had pushed the
** array base address onto the stack before, we can remove this value,
** since we can generate expression+offset.
*/
if (!ConstBaseAddr) {
RemoveCode (&Mark2);
} else {
/* Get an array pointer into the primary */
LoadExpr (CF_NONE, Expr);
}
if (IsClassPtr (Expr->Type)) {
/* Lhs is pointer/array. Scale the subscript value according to
** the element size.
*/
Subscript.IVal *= CheckedSizeOf (ElementType);
/* Remove the address load code */
RemoveCode (&Mark1);
/* In case of an array, we can adjust the offset of the expression
** already in Expr. If the base address was a constant, we can even
** remove the code that loaded the address into the primary.
*/
if (IsTypeArray (Expr->Type)) {
/* Adjust the offset */
Expr->IVal += Subscript.IVal;
} else {
/* It's a pointer, so we do have to load it into the primary
** first (if it's not already there).
*/
if (ConstBaseAddr || ED_IsLVal (Expr)) {
LoadExpr (CF_NONE, Expr);
ED_MakeRValExpr (Expr);
}
/* Use the offset */
Expr->IVal = Subscript.IVal;
}
} else {
/* Scale the rhs value according to the element type */
g_scale (TypeOf (tptr1), CheckedSizeOf (ElementType));
/* Add the subscript. Since arrays are indexed by integers,
** we will ignore the true type of the subscript here and
** use always an int. #### Use offset but beware of LoadExpr!
*/
g_inc (CF_INT | CF_CONST, Subscript.IVal);
}
} else {
/* Array subscript is not constant. Load it into the primary */
GetCodePos (&Mark2);
LoadExpr (CF_NONE, &Subscript);
/* Do scaling */
if (IsClassPtr (Expr->Type)) {
/* Indexing is based on unsigneds, so we will just use the integer
** portion of the index (which is in (e)ax, so there's no further
** action required).
*/
g_scale (CF_INT, CheckedSizeOf (ElementType));
} else {
/* Get the int value on top. If we come here, we're sure, both
** values are 16 bit (the first one was truncated if necessary
** and the second one is a pointer). Note: If ConstBaseAddr is
** true, we don't have a value on stack, so to "swap" both, just
** push the subscript.
*/
if (ConstBaseAddr) {
g_push (CF_INT, 0);
LoadExpr (CF_NONE, Expr);
ConstBaseAddr = 0;
} else {
g_swap (CF_INT);
}
/* Scale it */
g_scale (TypeOf (tptr1), CheckedSizeOf (ElementType));
}
/* The offset is now in the primary register. It we didn't have a
** constant base address for the lhs, the lhs address is already
** on stack, and we must add the offset. If the base address was
** constant, we call special functions to add the address to the
** offset value.
*/
if (!ConstBaseAddr) {
/* The array base address is on stack and the subscript is in the
** primary. Add both.
*/
g_add (CF_INT, 0);
} else {
/* The subscript is in the primary, and the array base address is
** in Expr. If the subscript has itself a constant address, it is
** often a better idea to reverse again the order of the
** evaluation. This will generate better code if the subscript is
** a byte sized variable. But beware: This is only possible if the
** subscript was not scaled, that is, if this was a byte array
** or pointer.
*/
if ((ED_IsLocConst (&Subscript) || ED_IsLocStack (&Subscript)) &&
CheckedSizeOf (ElementType) == SIZEOF_CHAR) {
unsigned Flags;
/* Reverse the order of evaluation */
if (CheckedSizeOf (Subscript.Type) == SIZEOF_CHAR) {
Flags = CF_CHAR;
} else {
Flags = CF_INT;
}
RemoveCode (&Mark2);
/* Get a pointer to the array into the primary. */
LoadExpr (CF_NONE, Expr);
/* Add the variable */
if (ED_IsLocStack (&Subscript)) {
g_addlocal (Flags, Subscript.IVal);
} else {
Flags |= GlobalModeFlags (&Subscript);
g_addstatic (Flags, Subscript.Name, Subscript.IVal);
}
} else {
if (ED_IsLocAbs (Expr)) {
/* Constant numeric address. Just add it */
g_inc (CF_INT, Expr->IVal);
} else if (ED_IsLocStack (Expr)) {
/* Base address is a local variable address */
if (IsTypeArray (Expr->Type)) {
g_addaddr_local (CF_INT, Expr->IVal);
} else {
g_addlocal (CF_PTR, Expr->IVal);
}
} else {
/* Base address is a static variable address */
unsigned Flags = CF_INT | GlobalModeFlags (Expr);
if (ED_IsRVal (Expr)) {
/* Add the address of the location */
g_addaddr_static (Flags, Expr->Name, Expr->IVal);
} else {
/* Add the contents of the location */
g_addstatic (Flags, Expr->Name, Expr->IVal);
}
}
}
}
/* The result is an expression in the primary */
ED_MakeRValExpr (Expr);
}
/* Result is of element type */
Expr->Type = ElementType;
/* An array element is actually a variable. So the rules for variables
** with respect to the reference type apply: If it's an array, it is
** a rvalue, otherwise it's an lvalue. (A function would also be a rvalue,
** but an array cannot contain functions).
*/
if (IsTypeArray (Expr->Type)) {
ED_MakeRVal (Expr);
} else {
ED_MakeLVal (Expr);
}
/* Consume the closing bracket */
ConsumeRBrack ();
}
static void StructRef (ExprDesc* Expr)
/* Process struct field after . or ->. */
{
ident Ident;
SymEntry* Field;
Type* FinalType;
TypeCode Q;
/* Skip the token and check for an identifier */
NextToken ();
if (CurTok.Tok != TOK_IDENT) {
Error ("Identifier expected");
/* Make the expression an integer at address zero */
ED_MakeConstAbs (Expr, 0, type_int);
return;
}
/* Get the symbol table entry and check for a struct field */
strcpy (Ident, CurTok.Ident);
NextToken ();
Field = FindStructField (Expr->Type, Ident);
if (Field == 0) {
Error ("Struct/union has no field named '%s'", Ident);
/* Make the expression an integer at address zero */
ED_MakeConstAbs (Expr, 0, type_int);
return;
}
/* If we have a struct pointer that is an lvalue and not already in the
** primary, load it now.
*/
if (ED_IsLVal (Expr) && IsTypePtr (Expr->Type)) {
/* Load into the primary */
LoadExpr (CF_NONE, Expr);
/* Make it an lvalue expression */
ED_MakeLValExpr (Expr);
}
/* The type is the type of the field plus any qualifiers from the struct */
if (IsClassStruct (Expr->Type)) {
Q = GetQualifier (Expr->Type);
} else {
Q = GetQualifier (Indirect (Expr->Type));
}
if (GetQualifier (Field->Type) == (GetQualifier (Field->Type) | Q)) {
FinalType = Field->Type;
} else {
FinalType = TypeDup (Field->Type);
FinalType->C |= Q;
}
/* A struct is usually an lvalue. If not, it is a struct in the primary
** register.
*/
if (ED_IsRVal (Expr) && ED_IsLocExpr (Expr) && !IsTypePtr (Expr->Type)) {
unsigned Flags = 0;
unsigned BitOffs;
/* Get the size of the type */
unsigned Size = SizeOf (Expr->Type);
/* Safety check */
CHECK (Field->V.Offs + Size <= SIZEOF_LONG);
/* The type of the operation depends on the type of the struct */
switch (Size) {
case 1: Flags = CF_CHAR | CF_UNSIGNED | CF_CONST; break;
case 2: Flags = CF_INT | CF_UNSIGNED | CF_CONST; break;
case 3: /* FALLTHROUGH */
case 4: Flags = CF_LONG | CF_UNSIGNED | CF_CONST; break;
default: Internal ("Invalid struct size: %u", Size); break;
}
/* Generate a shift to get the field in the proper position in the
** primary. For bit fields, mask the value.
*/
BitOffs = Field->V.Offs * CHAR_BITS;
if (SymIsBitField (Field)) {
BitOffs += Field->V.B.BitOffs;
g_asr (Flags, BitOffs);
/* Mask the value. This is unnecessary if the shift executed above
** moved only zeroes into the value.
*/
if (BitOffs + Field->V.B.BitWidth != Size * CHAR_BITS) {
g_and (CF_INT | CF_UNSIGNED | CF_CONST,
(0x0001U << Field->V.B.BitWidth) - 1U);
}
} else {
g_asr (Flags, BitOffs);
}
/* Use the new type */
Expr->Type = FinalType;
} else {
/* Set the struct field offset */
Expr->IVal += Field->V.Offs;
/* Use the new type */
Expr->Type = FinalType;
/* An struct member is actually a variable. So the rules for variables
** with respect to the reference type apply: If it's an array, it is
** a rvalue, otherwise it's an lvalue. (A function would also be a rvalue,
** but a struct field cannot be a function).
*/
if (IsTypeArray (Expr->Type)) {
ED_MakeRVal (Expr);
} else {
ED_MakeLVal (Expr);
}
/* Make the expression a bit field if necessary */
if (SymIsBitField (Field)) {
ED_MakeBitField (Expr, Field->V.B.BitOffs, Field->V.B.BitWidth);
}
}
}
static void hie11 (ExprDesc *Expr)
/* Handle compound types (structs and arrays) */
{
/* Name value used in invalid function calls */
static const char IllegalFunc[] = "illegal_function_call";
/* Evaluate the lhs */
Primary (Expr);
/* Check for a rhs */
while (CurTok.Tok == TOK_LBRACK || CurTok.Tok == TOK_LPAREN ||
CurTok.Tok == TOK_DOT || CurTok.Tok == TOK_PTR_REF) {
switch (CurTok.Tok) {
case TOK_LBRACK:
/* Array reference */
ArrayRef (Expr);
break;
case TOK_LPAREN:
/* Function call. */
if (!IsTypeFunc (Expr->Type) && !IsTypeFuncPtr (Expr->Type)) {
/* Not a function */
Error ("Illegal function call");
/* Force the type to be a implicitly defined function, one
** returning an int and taking any number of arguments.
** Since we don't have a name, invent one.
*/
ED_MakeConstAbs (Expr, 0, GetImplicitFuncType ());
Expr->Name = (uintptr_t) IllegalFunc;
}
/* Call the function */
FunctionCall (Expr);
break;
case TOK_DOT:
if (!IsClassStruct (Expr->Type)) {
Error ("Struct expected");
}
StructRef (Expr);
break;
case TOK_PTR_REF:
/* If we have an array, convert it to pointer to first element */
if (IsTypeArray (Expr->Type)) {
Expr->Type = ArrayToPtr (Expr->Type);
}
if (!IsClassPtr (Expr->Type) || !IsClassStruct (Indirect (Expr->Type))) {
Error ("Struct pointer expected");
}
StructRef (Expr);
break;
default:
Internal ("Invalid token in hie11: %d", CurTok.Tok);
}
}
}
void Store (ExprDesc* Expr, const Type* StoreType)
/* Store the primary register into the location denoted by Expr. If StoreType
** is given, use this type when storing instead of Expr->Type. If StoreType
** is NULL, use Expr->Type instead.
*/
{
unsigned Flags;
/* If StoreType was not given, use Expr->Type instead */
if (StoreType == 0) {
StoreType = Expr->Type;
}
/* Prepare the code generator flags */
Flags = TypeOf (StoreType) | GlobalModeFlags (Expr);
/* Do the store depending on the location */
switch (ED_GetLoc (Expr)) {
case E_LOC_ABS:
/* Absolute: numeric address or const */
g_putstatic (Flags, Expr->IVal, 0);
break;
case E_LOC_GLOBAL:
/* Global variable */
g_putstatic (Flags, Expr->Name, Expr->IVal);
break;
case E_LOC_STATIC:
case E_LOC_LITERAL:
/* Static variable or literal in the literal pool */
g_putstatic (Flags, Expr->Name, Expr->IVal);
break;
case E_LOC_REGISTER:
/* Register variable */
g_putstatic (Flags, Expr->Name, Expr->IVal);
break;
case E_LOC_STACK:
/* Value on the stack */
g_putlocal (Flags, Expr->IVal, 0);
break;
case E_LOC_PRIMARY:
/* The primary register (value is already there) */
break;
case E_LOC_EXPR:
/* An expression in the primary register */
g_putind (Flags, Expr->IVal);
break;
default:
Internal ("Invalid location in Store(): 0x%04X", ED_GetLoc (Expr));
}
/* Assume that each one of the stores will invalidate CC */
ED_MarkAsUntested (Expr);
}
static void PreInc (ExprDesc* Expr)
/* Handle the preincrement operators */
{
unsigned Flags;
unsigned long Val;
/* Skip the operator token */
NextToken ();
/* Evaluate the expression and check that it is an lvalue */
hie10 (Expr);
if (!ED_IsLVal (Expr)) {
Error ("Invalid lvalue");
return;
}
/* We cannot modify const values */
if (IsQualConst (Expr->Type)) {
Error ("Increment of read-only variable");
}
/* Get the data type */
Flags = TypeOf (Expr->Type) | GlobalModeFlags (Expr) | CF_FORCECHAR | CF_CONST;
/* Get the increment value in bytes */
Val = IsTypePtr (Expr->Type)? CheckedPSizeOf (Expr->Type) : 1;
/* Check the location of the data */
switch (ED_GetLoc (Expr)) {
case E_LOC_ABS:
/* Absolute: numeric address or const */
g_addeqstatic (Flags, Expr->IVal, 0, Val);
break;
case E_LOC_GLOBAL:
/* Global variable */
g_addeqstatic (Flags, Expr->Name, Expr->IVal, Val);
break;
case E_LOC_STATIC:
case E_LOC_LITERAL:
/* Static variable or literal in the literal pool */
g_addeqstatic (Flags, Expr->Name, Expr->IVal, Val);
break;
case E_LOC_REGISTER:
/* Register variable */
g_addeqstatic (Flags, Expr->Name, Expr->IVal, Val);
break;
case E_LOC_STACK:
/* Value on the stack */
g_addeqlocal (Flags, Expr->IVal, Val);
break;
case E_LOC_PRIMARY:
/* The primary register */
g_inc (Flags, Val);
break;
case E_LOC_EXPR:
/* An expression in the primary register */
g_addeqind (Flags, Expr->IVal, Val);
break;
default:
Internal ("Invalid location in PreInc(): 0x%04X", ED_GetLoc (Expr));
}
/* Result is an expression, no reference */
ED_MakeRValExpr (Expr);
}
static void PreDec (ExprDesc* Expr)
/* Handle the predecrement operators */
{
unsigned Flags;
unsigned long Val;
/* Skip the operator token */
NextToken ();
/* Evaluate the expression and check that it is an lvalue */
hie10 (Expr);
if (!ED_IsLVal (Expr)) {
Error ("Invalid lvalue");
return;
}
/* We cannot modify const values */
if (IsQualConst (Expr->Type)) {
Error ("Decrement of read-only variable");
}
/* Get the data type */
Flags = TypeOf (Expr->Type) | GlobalModeFlags (Expr) | CF_FORCECHAR | CF_CONST;
/* Get the increment value in bytes */
Val = IsTypePtr (Expr->Type)? CheckedPSizeOf (Expr->Type) : 1;
/* Check the location of the data */
switch (ED_GetLoc (Expr)) {
case E_LOC_ABS:
/* Absolute: numeric address or const */
g_subeqstatic (Flags, Expr->IVal, 0, Val);
break;
case E_LOC_GLOBAL:
/* Global variable */
g_subeqstatic (Flags, Expr->Name, Expr->IVal, Val);
break;
case E_LOC_STATIC:
case E_LOC_LITERAL:
/* Static variable or literal in the literal pool */
g_subeqstatic (Flags, Expr->Name, Expr->IVal, Val);
break;
case E_LOC_REGISTER:
/* Register variable */
g_subeqstatic (Flags, Expr->Name, Expr->IVal, Val);
break;
case E_LOC_STACK:
/* Value on the stack */
g_subeqlocal (Flags, Expr->IVal, Val);
break;
case E_LOC_PRIMARY:
/* The primary register */
g_inc (Flags, Val);
break;
case E_LOC_EXPR:
/* An expression in the primary register */
g_subeqind (Flags, Expr->IVal, Val);
break;
default:
Internal ("Invalid location in PreDec(): 0x%04X", ED_GetLoc (Expr));
}
/* Result is an expression, no reference */
ED_MakeRValExpr (Expr);
}
static void PostInc (ExprDesc* Expr)
/* Handle the postincrement operator */
{
unsigned Flags;
NextToken ();
/* The expression to increment must be an lvalue */
if (!ED_IsLVal (Expr)) {
Error ("Invalid lvalue");
return;
}
/* We cannot modify const values */
if (IsQualConst (Expr->Type)) {
Error ("Increment of read-only variable");
}
/* Get the data type */
Flags = TypeOf (Expr->Type);
/* Emit smaller code if a char variable is at a constant location */
if ((Flags & CF_CHAR) == CF_CHAR && ED_IsLocConst(Expr)) {
LoadExpr (CF_NONE, Expr);
AddCodeLine ("inc %s", ED_GetLabelName(Expr, 0));
} else {
/* Push the address if needed */
PushAddr (Expr);
/* Fetch the value and save it (since it's the result of the expression) */
LoadExpr (CF_NONE, Expr);
g_save (Flags | CF_FORCECHAR);
/* If we have a pointer expression, increment by the size of the type */
if (IsTypePtr (Expr->Type)) {
g_inc (Flags | CF_CONST | CF_FORCECHAR, CheckedSizeOf (Expr->Type + 1));
} else {
g_inc (Flags | CF_CONST | CF_FORCECHAR, 1);
}
/* Store the result back */
Store (Expr, 0);
/* Restore the original value in the primary register */
g_restore (Flags | CF_FORCECHAR);
}
/* The result is always an expression, no reference */
ED_MakeRValExpr (Expr);
}
static void PostDec (ExprDesc* Expr)
/* Handle the postdecrement operator */
{
unsigned Flags;
NextToken ();
/* The expression to increment must be an lvalue */
if (!ED_IsLVal (Expr)) {
Error ("Invalid lvalue");
return;
}
/* We cannot modify const values */
if (IsQualConst (Expr->Type)) {
Error ("Decrement of read-only variable");
}
/* Get the data type */
Flags = TypeOf (Expr->Type);
/* Emit smaller code if a char variable is at a constant location */
if ((Flags & CF_CHAR) == CF_CHAR && ED_IsLocConst(Expr)) {
LoadExpr (CF_NONE, Expr);
AddCodeLine ("dec %s", ED_GetLabelName(Expr, 0));
} else {
/* Push the address if needed */
PushAddr (Expr);
/* Fetch the value and save it (since it's the result of the expression) */
LoadExpr (CF_NONE, Expr);
g_save (Flags | CF_FORCECHAR);
/* If we have a pointer expression, increment by the size of the type */
if (IsTypePtr (Expr->Type)) {
g_dec (Flags | CF_CONST | CF_FORCECHAR, CheckedSizeOf (Expr->Type + 1));
} else {
g_dec (Flags | CF_CONST | CF_FORCECHAR, 1);
}
/* Store the result back */
Store (Expr, 0);
/* Restore the original value in the primary register */
g_restore (Flags | CF_FORCECHAR);
}
/* The result is always an expression, no reference */
ED_MakeRValExpr (Expr);
}
static void UnaryOp (ExprDesc* Expr)
/* Handle unary -/+ and ~ */
{
unsigned Flags;
/* Remember the operator token and skip it */
token_t Tok = CurTok.Tok;
NextToken ();
/* Get the expression */
hie10 (Expr);
/* We can only handle integer types */
if (!IsClassInt (Expr->Type)) {
Error ("Argument must have integer type");
ED_MakeConstAbsInt (Expr, 1);
}
/* Check for a constant expression */
if (ED_IsConstAbs (Expr)) {
/* Value is constant */
switch (Tok) {
case TOK_MINUS: Expr->IVal = -Expr->IVal; break;
case TOK_PLUS: break;
case TOK_COMP: Expr->IVal = ~Expr->IVal; break;
default: Internal ("Unexpected token: %d", Tok);
}
} else {
/* Value is not constant */
LoadExpr (CF_NONE, Expr);
/* Get the type of the expression */
Flags = TypeOf (Expr->Type);
/* Handle the operation */
switch (Tok) {
case TOK_MINUS: g_neg (Flags); break;
case TOK_PLUS: break;
case TOK_COMP: g_com (Flags); break;
default: Internal ("Unexpected token: %d", Tok);
}
/* The result is a rvalue in the primary */
ED_MakeRValExpr (Expr);
}
}
void hie10 (ExprDesc* Expr)
/* Handle ++, --, !, unary - etc. */
{
unsigned long Size;
switch (CurTok.Tok) {
case TOK_INC:
PreInc (Expr);
break;
case TOK_DEC:
PreDec (Expr);
break;
case TOK_PLUS:
case TOK_MINUS:
case TOK_COMP:
UnaryOp (Expr);
break;
case TOK_BOOL_NOT:
NextToken ();
if (evalexpr (CF_NONE, hie10, Expr) == 0) {
/* Constant expression */
Expr->IVal = !Expr->IVal;
} else {
g_bneg (TypeOf (Expr->Type));
ED_MakeRValExpr (Expr);
ED_TestDone (Expr); /* bneg will set cc */
}
break;
case TOK_STAR:
NextToken ();
ExprWithCheck (hie10, Expr);
if (ED_IsLVal (Expr) || !(ED_IsLocConst (Expr) || ED_IsLocStack (Expr))) {
/* Not a const, load it into the primary and make it a
** calculated value.
*/
LoadExpr (CF_NONE, Expr);
ED_MakeRValExpr (Expr);
}
/* If the expression is already a pointer to function, the
** additional dereferencing operator must be ignored. A function
** itself is represented as "pointer to function", so any number
** of dereference operators is legal, since the result will
** always be converted to "pointer to function".
*/
if (IsTypeFuncPtr (Expr->Type) || IsTypeFunc (Expr->Type)) {
/* Expression not storable */
ED_MakeRVal (Expr);
} else {
if (IsClassPtr (Expr->Type)) {
Expr->Type = Indirect (Expr->Type);
} else {
Error ("Illegal indirection");
}
/* If the expression points to an array, then don't convert the
** address -- it already is the location of the first element.
*/
if (!IsTypeArray (Expr->Type)) {
/* The * operator yields an lvalue */
ED_MakeLVal (Expr);
}
}
break;
case TOK_AND:
NextToken ();
ExprWithCheck (hie10, Expr);
/* The & operator may be applied to any lvalue, and it may be
** applied to functions, even if they're no lvalues.
*/
if (ED_IsRVal (Expr) && !IsTypeFunc (Expr->Type) && !IsTypeArray (Expr->Type)) {
Error ("Illegal address");
} else {
if (ED_IsBitField (Expr)) {
Error ("Cannot take address of bit-field");
/* Do it anyway, just to avoid further warnings */
Expr->Flags &= ~E_BITFIELD;
}
Expr->Type = PointerTo (Expr->Type);
/* The & operator yields an rvalue */
ED_MakeRVal (Expr);
}
break;
case TOK_SIZEOF:
NextToken ();
if (TypeSpecAhead ()) {
Type T[MAXTYPELEN];
NextToken ();
Size = CheckedSizeOf (ParseType (T));
ConsumeRParen ();
} else {
/* Remember the output queue pointer */
CodeMark Mark;
GetCodePos (&Mark);
hie10 (Expr);
/* If the expression is a literal string, release it, so it
** won't be output as data if not used elsewhere.
*/
if (ED_IsLocLiteral (Expr)) {
ReleaseLiteral (Expr->LVal);
}
/* Calculate the size */
Size = CheckedSizeOf (Expr->Type);
/* Remove any generated code */
RemoveCode (&Mark);
}
ED_MakeConstAbs (Expr, Size, type_size_t);
ED_MarkAsUntested (Expr);
break;
default:
if (TypeSpecAhead ()) {
/* A typecast */
TypeCast (Expr);
} else {
/* An expression */
hie11 (Expr);
/* Handle post increment */
switch (CurTok.Tok) {
case TOK_INC: PostInc (Expr); break;
case TOK_DEC: PostDec (Expr); break;
default: break;
}
}
break;
}
}
static void hie_internal (const GenDesc* Ops, /* List of generators */
ExprDesc* Expr,
void (*hienext) (ExprDesc*),
int* UsedGen)
/* Helper function */
{
ExprDesc Expr2;
CodeMark Mark1;
CodeMark Mark2;
const GenDesc* Gen;
token_t Tok; /* The operator token */
unsigned ltype, type;
int lconst; /* Left operand is a constant */
int rconst; /* Right operand is a constant */
ExprWithCheck (hienext, Expr);
*UsedGen = 0;
while ((Gen = FindGen (CurTok.Tok, Ops)) != 0) {
/* Tell the caller that we handled it's ops */
*UsedGen = 1;
/* All operators that call this function expect an int on the lhs */
if (!IsClassInt (Expr->Type)) {
Error ("Integer expression expected");
/* To avoid further errors, make Expr a valid int expression */
ED_MakeConstAbsInt (Expr, 1);
}
/* Remember the operator token, then skip it */
Tok = CurTok.Tok;
NextToken ();
/* Get the lhs on stack */
GetCodePos (&Mark1);
ltype = TypeOf (Expr->Type);
lconst = ED_IsConstAbs (Expr);
if (lconst) {
/* Constant value */
GetCodePos (&Mark2);
/* If the operator is commutative, don't push the left side, if
** it's a constant, since we will exchange both operands.
*/
if ((Gen->Flags & GEN_COMM) == 0) {
g_push (ltype | CF_CONST, Expr->IVal);
}
} else {
/* Value not constant */
LoadExpr (CF_NONE, Expr);
GetCodePos (&Mark2);
g_push (ltype, 0);
}
/* Get the right hand side */
MarkedExprWithCheck (hienext, &Expr2);
/* Check for a constant expression */
rconst = (ED_IsConstAbs (&Expr2) && ED_CodeRangeIsEmpty (&Expr2));
if (!rconst) {
/* Not constant, load into the primary */
LoadExpr (CF_NONE, &Expr2);
}
/* Check the type of the rhs */
if (!IsClassInt (Expr2.Type)) {
Error ("Integer expression expected");
}
/* Check for const operands */
if (lconst && rconst) {
/* Both operands are constant, remove the generated code */
RemoveCode (&Mark1);
/* Get the type of the result */
Expr->Type = promoteint (Expr->Type, Expr2.Type);
/* Handle the op differently for signed and unsigned types */
if (IsSignSigned (Expr->Type)) {
/* Evaluate the result for signed operands */
signed long Val1 = Expr->IVal;
signed long Val2 = Expr2.IVal;
switch (Tok) {
case TOK_OR:
Expr->IVal = (Val1 | Val2);
break;
case TOK_XOR:
Expr->IVal = (Val1 ^ Val2);
break;
case TOK_AND:
Expr->IVal = (Val1 & Val2);
break;
case TOK_STAR:
Expr->IVal = (Val1 * Val2);
break;
case TOK_DIV:
if (Val2 == 0) {
Error ("Division by zero");
Expr->IVal = 0x7FFFFFFF;
} else {
Expr->IVal = (Val1 / Val2);
}
break;
case TOK_MOD:
if (Val2 == 0) {
Error ("Modulo operation with zero");
Expr->IVal = 0;
} else {
Expr->IVal = (Val1 % Val2);
}
break;
default:
Internal ("hie_internal: got token 0x%X\n", Tok);
}
} else {
/* Evaluate the result for unsigned operands */
unsigned long Val1 = Expr->IVal;
unsigned long Val2 = Expr2.IVal;
switch (Tok) {
case TOK_OR:
Expr->IVal = (Val1 | Val2);
break;
case TOK_XOR:
Expr->IVal = (Val1 ^ Val2);
break;
case TOK_AND:
Expr->IVal = (Val1 & Val2);
break;
case TOK_STAR:
Expr->IVal = (Val1 * Val2);
break;
case TOK_DIV:
if (Val2 == 0) {
Error ("Division by zero");
Expr->IVal = 0xFFFFFFFF;
} else {
Expr->IVal = (Val1 / Val2);
}
break;
case TOK_MOD:
if (Val2 == 0) {
Error ("Modulo operation with zero");
Expr->IVal = 0;
} else {
Expr->IVal = (Val1 % Val2);
}
break;
default:
Internal ("hie_internal: got token 0x%X\n", Tok);
}
}
} else if (lconst && (Gen->Flags & GEN_COMM) && !rconst) {
/* The left side is constant, the right side is not, and the
** operator allows swapping the operands. We haven't pushed the
** left side onto the stack in this case, and will reverse the
** operation because this allows for better code.
*/
unsigned rtype = ltype | CF_CONST;
ltype = TypeOf (Expr2.Type); /* Expr2 is now left */
type = CF_CONST;
if ((Gen->Flags & GEN_NOPUSH) == 0) {
g_push (ltype, 0);
} else {
ltype |= CF_REG; /* Value is in register */
}
/* Determine the type of the operation result. */
type |= g_typeadjust (ltype, rtype);
Expr->Type = promoteint (Expr->Type, Expr2.Type);
/* Generate code */
Gen->Func (type, Expr->IVal);
/* We have a rvalue in the primary now */
ED_MakeRValExpr (Expr);
} else {
/* If the right hand side is constant, and the generator function
** expects the lhs in the primary, remove the push of the primary
** now.
*/
unsigned rtype = TypeOf (Expr2.Type);
type = 0;
if (rconst) {
/* Second value is constant - check for div */
type |= CF_CONST;
rtype |= CF_CONST;
if (Tok == TOK_DIV && Expr2.IVal == 0) {
Error ("Division by zero");
} else if (Tok == TOK_MOD && Expr2.IVal == 0) {
Error ("Modulo operation with zero");
}
if ((Gen->Flags & GEN_NOPUSH) != 0) {
RemoveCode (&Mark2);
ltype |= CF_REG; /* Value is in register */
}
}
/* Determine the type of the operation result. */
type |= g_typeadjust (ltype, rtype);
Expr->Type = promoteint (Expr->Type, Expr2.Type);
/* Generate code */
Gen->Func (type, Expr2.IVal);
/* We have a rvalue in the primary now */
ED_MakeRValExpr (Expr);
}
}
}
static void hie_compare (const GenDesc* Ops, /* List of generators */
ExprDesc* Expr,
void (*hienext) (ExprDesc*))
/* Helper function for the compare operators */
{
ExprDesc Expr2;
CodeMark Mark0;
CodeMark Mark1;
CodeMark Mark2;
const GenDesc* Gen;
token_t Tok; /* The operator token */
unsigned ltype;
int rconst; /* Operand is a constant */
GetCodePos (&Mark0);
ExprWithCheck (hienext, Expr);
while ((Gen = FindGen (CurTok.Tok, Ops)) != 0) {
/* Remember the generator function */
void (*GenFunc) (unsigned, unsigned long) = Gen->Func;
/* Remember the operator token, then skip it */
Tok = CurTok.Tok;
NextToken ();
/* If lhs is a function, convert it to pointer to function */
if (IsTypeFunc (Expr->Type)) {
Expr->Type = PointerTo (Expr->Type);
}
/* Get the lhs on stack */
GetCodePos (&Mark1);
ltype = TypeOf (Expr->Type);
if (ED_IsConstAbs (Expr)) {
/* Constant value */
GetCodePos (&Mark2);
g_push (ltype | CF_CONST, Expr->IVal);
} else {
/* Value not constant */
LoadExpr (CF_NONE, Expr);
GetCodePos (&Mark2);
g_push (ltype, 0);
}
/* Get the right hand side */
MarkedExprWithCheck (hienext, &Expr2);
/* If rhs is a function, convert it to pointer to function */
if (IsTypeFunc (Expr2.Type)) {
Expr2.Type = PointerTo (Expr2.Type);
}
/* Check for a constant expression */
rconst = (ED_IsConstAbs (&Expr2) && ED_CodeRangeIsEmpty (&Expr2));
if (!rconst) {
/* Not constant, load into the primary */
LoadExpr (CF_NONE, &Expr2);
}
/* Some operations aren't allowed on function pointers */
if ((Gen->Flags & GEN_NOFUNC) != 0) {
/* Output only one message even if both sides are wrong */
if (IsTypeFuncPtr (Expr->Type)) {
Error ("Invalid left operand for relational operator");
/* Avoid further errors */
ED_MakeConstAbsInt (Expr, 0);
ED_MakeConstAbsInt (&Expr2, 0);
} else if (IsTypeFuncPtr (Expr2.Type)) {
Error ("Invalid right operand for relational operator");
/* Avoid further errors */
ED_MakeConstAbsInt (Expr, 0);
ED_MakeConstAbsInt (&Expr2, 0);
}
}
/* Make sure, the types are compatible */
if (IsClassInt (Expr->Type)) {
if (!IsClassInt (Expr2.Type) && !(IsClassPtr(Expr2.Type) && ED_IsNullPtr(Expr))) {
Error ("Incompatible types");
}
} else if (IsClassPtr (Expr->Type)) {
if (IsClassPtr (Expr2.Type)) {
/* Both pointers are allowed in comparison if they point to
** the same type, or if one of them is a void pointer.
*/
Type* left = Indirect (Expr->Type);
Type* right = Indirect (Expr2.Type);
if (TypeCmp (left, right) < TC_QUAL_DIFF && left->C != T_VOID && right->C != T_VOID) {
/* Incompatible pointers */
Error ("Incompatible types");
}
} else if (!ED_IsNullPtr (&Expr2)) {
Error ("Incompatible types");
}
}
/* Check for const operands */
if (ED_IsConstAbs (Expr) && rconst) {
/* If the result is constant, this is suspicious when not in
** preprocessor mode.
*/
WarnConstCompareResult ();
/* Both operands are constant, remove the generated code */
RemoveCode (&Mark1);
/* Determine if this is a signed or unsigned compare */
if (IsClassInt (Expr->Type) && IsSignSigned (Expr->Type) &&
IsClassInt (Expr2.Type) && IsSignSigned (Expr2.Type)) {
/* Evaluate the result for signed operands */
signed long Val1 = Expr->IVal;
signed long Val2 = Expr2.IVal;
switch (Tok) {
case TOK_EQ: Expr->IVal = (Val1 == Val2); break;
case TOK_NE: Expr->IVal = (Val1 != Val2); break;
case TOK_LT: Expr->IVal = (Val1 < Val2); break;
case TOK_LE: Expr->IVal = (Val1 <= Val2); break;
case TOK_GE: Expr->IVal = (Val1 >= Val2); break;
case TOK_GT: Expr->IVal = (Val1 > Val2); break;
default: Internal ("hie_compare: got token 0x%X\n", Tok);
}
} else {
/* Evaluate the result for unsigned operands */
unsigned long Val1 = Expr->IVal;
unsigned long Val2 = Expr2.IVal;
switch (Tok) {
case TOK_EQ: Expr->IVal = (Val1 == Val2); break;
case TOK_NE: Expr->IVal = (Val1 != Val2); break;
case TOK_LT: Expr->IVal = (Val1 < Val2); break;
case TOK_LE: Expr->IVal = (Val1 <= Val2); break;
case TOK_GE: Expr->IVal = (Val1 >= Val2); break;
case TOK_GT: Expr->IVal = (Val1 > Val2); break;
default: Internal ("hie_compare: got token 0x%X\n", Tok);
}
}
} else {
/* Determine the signedness of the operands */
int LeftSigned = IsSignSigned (Expr->Type);
int RightSigned = IsSignSigned (Expr2.Type);
/* If the right hand side is constant, and the generator function
** expects the lhs in the primary, remove the push of the primary
** now.
*/
unsigned flags = 0;
if (rconst) {
flags |= CF_CONST;
if ((Gen->Flags & GEN_NOPUSH) != 0) {
RemoveCode (&Mark2);
ltype |= CF_REG; /* Value is in register */
}
}
/* Determine the type of the operation. */
if (IsTypeChar (Expr->Type) && rconst) {
/* Left side is unsigned char, right side is constant.
** Determine the minimum and maximum values
*/
int LeftMin, LeftMax;
if (LeftSigned) {
LeftMin = -128;
LeftMax = 127;
} else {
LeftMin = 0;
LeftMax = 255;
}
/* An integer value is always represented as a signed in the
** ExprDesc structure. This may lead to false results below,
** if it is actually unsigned, but interpreted as signed
** because of the representation. Fortunately, in this case,
** the actual value doesn't matter, since it's always greater
** than what can be represented in a char. So correct the
** value accordingly.
*/
if (!RightSigned && Expr2.IVal < 0) {
/* Correct the value so it is an unsigned. It will then
** anyway match one of the cases below.
*/
Expr2.IVal = LeftMax + 1;
}
/* Comparing a char against a constant may have a constant
** result. Please note: It is not possible to remove the code
** for the compare alltogether, because it may have side
** effects.
*/
switch (Tok) {
case TOK_EQ:
if (Expr2.IVal < LeftMin || Expr2.IVal > LeftMax) {
ED_MakeConstAbsInt (Expr, 0);
WarnConstCompareResult ();
goto Done;
}
break;
case TOK_NE:
if (Expr2.IVal < LeftMin || Expr2.IVal > LeftMax) {
ED_MakeConstAbsInt (Expr, 1);
WarnConstCompareResult ();
goto Done;
}
break;
case TOK_LT:
if (Expr2.IVal <= LeftMin || Expr2.IVal > LeftMax) {
ED_MakeConstAbsInt (Expr, Expr2.IVal > LeftMax);
WarnConstCompareResult ();
goto Done;
}
break;
case TOK_LE:
if (Expr2.IVal < LeftMin || Expr2.IVal >= LeftMax) {
ED_MakeConstAbsInt (Expr, Expr2.IVal >= LeftMax);
WarnConstCompareResult ();
goto Done;
}
break;
case TOK_GE:
if (Expr2.IVal <= LeftMin || Expr2.IVal > LeftMax) {
ED_MakeConstAbsInt (Expr, Expr2.IVal <= LeftMin);
WarnConstCompareResult ();
goto Done;
}
break;
case TOK_GT:
if (Expr2.IVal < LeftMin || Expr2.IVal >= LeftMax) {
ED_MakeConstAbsInt (Expr, Expr2.IVal < LeftMin);
WarnConstCompareResult ();
goto Done;
}
break;
default:
Internal ("hie_compare: got token 0x%X\n", Tok);
}
/* If the result is not already constant (as evaluated in the
** switch above), we can execute the operation as a char op,
** since the right side constant is in a valid range.
*/
flags |= (CF_CHAR | CF_FORCECHAR);
if (!LeftSigned) {
flags |= CF_UNSIGNED;
}
} else if (IsTypeChar (Expr->Type) && IsTypeChar (Expr2.Type) &&
GetSignedness (Expr->Type) == GetSignedness (Expr2.Type)) {
/* Both are chars with the same signedness. We can encode the
** operation as a char operation.
*/
flags |= CF_CHAR;
if (rconst) {
flags |= CF_FORCECHAR;
}
if (!LeftSigned) {
flags |= CF_UNSIGNED;
}
} else {
unsigned rtype = TypeOf (Expr2.Type) | (flags & CF_CONST);
flags |= g_typeadjust (ltype, rtype);
}
/* If the left side is an unsigned and the right is a constant,
** we may be able to change the compares to something more
** effective.
*/
if (!LeftSigned && rconst) {
switch (Tok) {
case TOK_LT:
if (Expr2.IVal == 1) {
/* An unsigned compare to one means that the value
** must be zero.
*/
GenFunc = g_eq;
Expr2.IVal = 0;
}
break;
case TOK_LE:
if (Expr2.IVal == 0) {
/* An unsigned compare to zero means that the value
** must be zero.
*/
GenFunc = g_eq;
}
break;
case TOK_GE:
if (Expr2.IVal == 1) {
/* An unsigned compare to one means that the value
** must not be zero.
*/
GenFunc = g_ne;
Expr2.IVal = 0;
}
break;
case TOK_GT:
if (Expr2.IVal == 0) {
/* An unsigned compare to zero means that the value
** must not be zero.
*/
GenFunc = g_ne;
}
break;
default:
break;
}
}
/* Generate code */
GenFunc (flags, Expr2.IVal);
/* The result is an rvalue in the primary */
ED_MakeRValExpr (Expr);
}
/* Result type is always int */
Expr->Type = type_int;
Done: /* Condition codes are set */
ED_TestDone (Expr);
}
}
static void hie9 (ExprDesc *Expr)
/* Process * and / operators. */
{
static const GenDesc hie9_ops[] = {
{ TOK_STAR, GEN_NOPUSH | GEN_COMM, g_mul },
{ TOK_DIV, GEN_NOPUSH, g_div },
{ TOK_MOD, GEN_NOPUSH, g_mod },
{ TOK_INVALID, 0, 0 }
};
int UsedGen;
hie_internal (hie9_ops, Expr, hie10, &UsedGen);
}
static void parseadd (ExprDesc* Expr)
/* Parse an expression with the binary plus operator. Expr contains the
** unprocessed left hand side of the expression and will contain the
** result of the expression on return.
*/
{
ExprDesc Expr2;
unsigned flags; /* Operation flags */
CodeMark Mark; /* Remember code position */
Type* lhst; /* Type of left hand side */
Type* rhst; /* Type of right hand side */
/* Skip the PLUS token */
NextToken ();
/* Get the left hand side type, initialize operation flags */
lhst = Expr->Type;
flags = 0;
/* Check for constness on both sides */
if (ED_IsConst (Expr)) {
/* The left hand side is a constant of some sort. Good. Get rhs */
ExprWithCheck (hie9, &Expr2);
if (ED_IsConstAbs (&Expr2)) {
/* Right hand side is a constant numeric value. Get the rhs type */
rhst = Expr2.Type;
/* Both expressions are constants. Check for pointer arithmetic */
if (IsClassPtr (lhst) && IsClassInt (rhst)) {
/* Left is pointer, right is int, must scale rhs */
Expr->IVal += Expr2.IVal * CheckedPSizeOf (lhst);
/* Result type is a pointer */
} else if (IsClassInt (lhst) && IsClassPtr (rhst)) {
/* Left is int, right is pointer, must scale lhs */
Expr->IVal = Expr->IVal * CheckedPSizeOf (rhst) + Expr2.IVal;
/* Result type is a pointer */
Expr->Type = Expr2.Type;
} else if (IsClassInt (lhst) && IsClassInt (rhst)) {
/* Integer addition */
Expr->IVal += Expr2.IVal;
typeadjust (Expr, &Expr2, 1);
} else {
/* OOPS */
Error ("Invalid operands for binary operator '+'");
}
} else {
/* lhs is a constant and rhs is not constant. Load rhs into
** the primary.
*/
LoadExpr (CF_NONE, &Expr2);
/* Beware: The check above (for lhs) lets not only pass numeric
** constants, but also constant addresses (labels), maybe even
** with an offset. We have to check for that here.
*/
/* First, get the rhs type. */
rhst = Expr2.Type;
/* Setup flags */
if (ED_IsLocAbs (Expr)) {
/* A numerical constant */
flags |= CF_CONST;
} else {
/* Constant address label */
flags |= GlobalModeFlags (Expr) | CF_CONSTADDR;
}
/* Check for pointer arithmetic */
if (IsClassPtr (lhst) && IsClassInt (rhst)) {
/* Left is pointer, right is int, must scale rhs */
g_scale (CF_INT, CheckedPSizeOf (lhst));
/* Operate on pointers, result type is a pointer */
flags |= CF_PTR;
/* Generate the code for the add */
if (ED_GetLoc (Expr) == E_LOC_ABS) {
/* Numeric constant */
g_inc (flags, Expr->IVal);
} else {
/* Constant address */
g_addaddr_static (flags, Expr->Name, Expr->IVal);
}
} else if (IsClassInt (lhst) && IsClassPtr (rhst)) {
/* Left is int, right is pointer, must scale lhs. */
unsigned ScaleFactor = CheckedPSizeOf (rhst);
/* Operate on pointers, result type is a pointer */
flags |= CF_PTR;
Expr->Type = Expr2.Type;
/* Since we do already have rhs in the primary, if lhs is
** not a numeric constant, and the scale factor is not one
** (no scaling), we must take the long way over the stack.
*/
if (ED_IsLocAbs (Expr)) {
/* Numeric constant, scale lhs */
Expr->IVal *= ScaleFactor;
/* Generate the code for the add */
g_inc (flags, Expr->IVal);
} else if (ScaleFactor == 1) {
/* Constant address but no need to scale */
g_addaddr_static (flags, Expr->Name, Expr->IVal);
} else {
/* Constant address that must be scaled */
g_push (TypeOf (Expr2.Type), 0); /* rhs --> stack */
g_getimmed (flags, Expr->Name, Expr->IVal);
g_scale (CF_PTR, ScaleFactor);
g_add (CF_PTR, 0);
}
} else if (IsClassInt (lhst) && IsClassInt (rhst)) {
/* Integer addition */
flags |= typeadjust (Expr, &Expr2, 1);
/* Generate the code for the add */
if (ED_IsLocAbs (Expr)) {
/* Numeric constant */
g_inc (flags, Expr->IVal);
} else {
/* Constant address */
g_addaddr_static (flags, Expr->Name, Expr->IVal);
}
} else {
/* OOPS */
Error ("Invalid operands for binary operator '+'");
flags = CF_INT;
}
/* Result is a rvalue in primary register */
ED_MakeRValExpr (Expr);
}
} else {
/* Left hand side is not constant. Get the value onto the stack. */
LoadExpr (CF_NONE, Expr); /* --> primary register */
GetCodePos (&Mark);
g_push (TypeOf (Expr->Type), 0); /* --> stack */
/* Evaluate the rhs */
MarkedExprWithCheck (hie9, &Expr2);
/* Check for a constant rhs expression */
if (ED_IsConstAbs (&Expr2) && ED_CodeRangeIsEmpty (&Expr2)) {
/* Right hand side is a constant. Get the rhs type */
rhst = Expr2.Type;
/* Remove pushed value from stack */
RemoveCode (&Mark);
/* Check for pointer arithmetic */
if (IsClassPtr (lhst) && IsClassInt (rhst)) {
/* Left is pointer, right is int, must scale rhs */
Expr2.IVal *= CheckedPSizeOf (lhst);
/* Operate on pointers, result type is a pointer */
flags = CF_PTR;
} else if (IsClassInt (lhst) && IsClassPtr (rhst)) {
/* Left is int, right is pointer, must scale lhs (ptr only) */
g_scale (CF_INT | CF_CONST, CheckedPSizeOf (rhst));
/* Operate on pointers, result type is a pointer */
flags = CF_PTR;
Expr->Type = Expr2.Type;
} else if (IsClassInt (lhst) && IsClassInt (rhst)) {
/* Integer addition */
flags = typeadjust (Expr, &Expr2, 1);
} else {
/* OOPS */
Error ("Invalid operands for binary operator '+'");
flags = CF_INT;
}
/* Generate code for the add */
g_inc (flags | CF_CONST, Expr2.IVal);
} else {
/* Not constant, load into the primary */
LoadExpr (CF_NONE, &Expr2);
/* lhs and rhs are not constant. Get the rhs type. */
rhst = Expr2.Type;
/* Check for pointer arithmetic */
if (IsClassPtr (lhst) && IsClassInt (rhst)) {
/* Left is pointer, right is int, must scale rhs */
g_scale (CF_INT, CheckedPSizeOf (lhst));
/* Operate on pointers, result type is a pointer */
flags = CF_PTR;
} else if (IsClassInt (lhst) && IsClassPtr (rhst)) {
/* Left is int, right is pointer, must scale lhs */
g_tosint (TypeOf (lhst)); /* Make sure TOS is int */
g_swap (CF_INT); /* Swap TOS and primary */
g_scale (CF_INT, CheckedPSizeOf (rhst));
/* Operate on pointers, result type is a pointer */
flags = CF_PTR;
Expr->Type = Expr2.Type;
} else if (IsClassInt (lhst) && IsClassInt (rhst)) {
/* Integer addition. Note: Result is never constant.
** Problem here is that typeadjust does not know if the
** variable is an rvalue or lvalue, so if both operands
** are dereferenced constant numeric addresses, typeadjust
** thinks the operation works on constants. Removing
** CF_CONST here means handling the symptoms, however, the
** whole parser is such a mess that I fear to break anything
** when trying to apply another solution.
*/
flags = typeadjust (Expr, &Expr2, 0) & ~CF_CONST;
} else {
/* OOPS */
Error ("Invalid operands for binary operator '+'");
flags = CF_INT;
}
/* Generate code for the add */
g_add (flags, 0);
}
/* Result is a rvalue in primary register */
ED_MakeRValExpr (Expr);
}
/* Condition codes not set */
ED_MarkAsUntested (Expr);
}
static void parsesub (ExprDesc* Expr)
/* Parse an expression with the binary minus operator. Expr contains the
** unprocessed left hand side of the expression and will contain the
** result of the expression on return.
*/
{
ExprDesc Expr2;
unsigned flags; /* Operation flags */
Type* lhst; /* Type of left hand side */
Type* rhst; /* Type of right hand side */
CodeMark Mark1; /* Save position of output queue */
CodeMark Mark2; /* Another position in the queue */
int rscale; /* Scale factor for the result */
/* lhs cannot be function or pointer to function */
if (IsTypeFunc (Expr->Type) || IsTypeFuncPtr (Expr->Type)) {
Error ("Invalid left operand for binary operator '-'");
/* Make it pointer to char to avoid further errors */
Expr->Type = type_uchar;
}
/* Skip the MINUS token */
NextToken ();
/* Get the left hand side type, initialize operation flags */
lhst = Expr->Type;
rscale = 1; /* Scale by 1, that is, don't scale */
/* Remember the output queue position, then bring the value onto the stack */
GetCodePos (&Mark1);
LoadExpr (CF_NONE, Expr); /* --> primary register */
GetCodePos (&Mark2);
g_push (TypeOf (lhst), 0); /* --> stack */
/* Parse the right hand side */
MarkedExprWithCheck (hie9, &Expr2);
/* rhs cannot be function or pointer to function */
if (IsTypeFunc (Expr2.Type) || IsTypeFuncPtr (Expr2.Type)) {
Error ("Invalid right operand for binary operator '-'");
/* Make it pointer to char to avoid further errors */
Expr2.Type = type_uchar;
}
/* Check for a constant rhs expression */
if (ED_IsConstAbs (&Expr2) && ED_CodeRangeIsEmpty (&Expr2)) {
/* The right hand side is constant. Get the rhs type. */
rhst = Expr2.Type;
/* Check left hand side */
if (ED_IsConstAbs (Expr)) {
/* Both sides are constant, remove generated code */
RemoveCode (&Mark1);
/* Check for pointer arithmetic */
if (IsClassPtr (lhst) && IsClassInt (rhst)) {
/* Left is pointer, right is int, must scale rhs */
Expr->IVal -= Expr2.IVal * CheckedPSizeOf (lhst);
/* Operate on pointers, result type is a pointer */
} else if (IsClassPtr (lhst) && IsClassPtr (rhst)) {
/* Left is pointer, right is pointer, must scale result */
if (TypeCmp (Indirect (lhst), Indirect (rhst)) < TC_QUAL_DIFF) {
Error ("Incompatible pointer types");
} else {
Expr->IVal = (Expr->IVal - Expr2.IVal) /
CheckedPSizeOf (lhst);
}
/* Operate on pointers, result type is an integer */
Expr->Type = type_int;
} else if (IsClassInt (lhst) && IsClassInt (rhst)) {
/* Integer subtraction */
typeadjust (Expr, &Expr2, 1);
Expr->IVal -= Expr2.IVal;
} else {
/* OOPS */
Error ("Invalid operands for binary operator '-'");
}
/* Result is constant, condition codes not set */
ED_MarkAsUntested (Expr);
} else {
/* Left hand side is not constant, right hand side is.
** Remove pushed value from stack.
*/
RemoveCode (&Mark2);
if (IsClassPtr (lhst) && IsClassInt (rhst)) {
/* Left is pointer, right is int, must scale rhs */
Expr2.IVal *= CheckedPSizeOf (lhst);
/* Operate on pointers, result type is a pointer */
flags = CF_PTR;
} else if (IsClassPtr (lhst) && IsClassPtr (rhst)) {
/* Left is pointer, right is pointer, must scale result */
if (TypeCmp (Indirect (lhst), Indirect (rhst)) < TC_QUAL_DIFF) {
Error ("Incompatible pointer types");
} else {
rscale = CheckedPSizeOf (lhst);
}
/* Operate on pointers, result type is an integer */
flags = CF_PTR;
Expr->Type = type_int;
} else if (IsClassInt (lhst) && IsClassInt (rhst)) {
/* Integer subtraction */
flags = typeadjust (Expr, &Expr2, 1);
} else {
/* OOPS */
Error ("Invalid operands for binary operator '-'");
flags = CF_INT;
}
/* Do the subtraction */
g_dec (flags | CF_CONST, Expr2.IVal);
/* If this was a pointer subtraction, we must scale the result */
if (rscale != 1) {
g_scale (flags, -rscale);
}
/* Result is a rvalue in the primary register */
ED_MakeRValExpr (Expr);
ED_MarkAsUntested (Expr);
}
} else {
/* Not constant, load into the primary */
LoadExpr (CF_NONE, &Expr2);
/* Right hand side is not constant. Get the rhs type. */
rhst = Expr2.Type;
/* Check for pointer arithmetic */
if (IsClassPtr (lhst) && IsClassInt (rhst)) {
/* Left is pointer, right is int, must scale rhs */
g_scale (CF_INT, CheckedPSizeOf (lhst));
/* Operate on pointers, result type is a pointer */
flags = CF_PTR;
} else if (IsClassPtr (lhst) && IsClassPtr (rhst)) {
/* Left is pointer, right is pointer, must scale result */
if (TypeCmp (Indirect (lhst), Indirect (rhst)) < TC_QUAL_DIFF) {
Error ("Incompatible pointer types");
} else {
rscale = CheckedPSizeOf (lhst);
}
/* Operate on pointers, result type is an integer */
flags = CF_PTR;
Expr->Type = type_int;
} else if (IsClassInt (lhst) && IsClassInt (rhst)) {
/* Integer subtraction. If the left hand side descriptor says that
** the lhs is const, we have to remove this mark, since this is no
** longer true, lhs is on stack instead.
*/
if (ED_IsLocAbs (Expr)) {
ED_MakeRValExpr (Expr);
}
/* Adjust operand types */
flags = typeadjust (Expr, &Expr2, 0);
} else {
/* OOPS */
Error ("Invalid operands for binary operator '-'");
flags = CF_INT;
}
/* Generate code for the sub (the & is a hack here) */
g_sub (flags & ~CF_CONST, 0);
/* If this was a pointer subtraction, we must scale the result */
if (rscale != 1) {
g_scale (flags, -rscale);
}
/* Result is a rvalue in the primary register */
ED_MakeRValExpr (Expr);
ED_MarkAsUntested (Expr);
}
}
void hie8 (ExprDesc* Expr)
/* Process + and - binary operators. */
{
ExprWithCheck (hie9, Expr);
while (CurTok.Tok == TOK_PLUS || CurTok.Tok == TOK_MINUS) {
if (CurTok.Tok == TOK_PLUS) {
parseadd (Expr);
} else {
parsesub (Expr);
}
}
}
static void hie6 (ExprDesc* Expr)
/* Handle greater-than type comparators */
{
static const GenDesc hie6_ops [] = {
{ TOK_LT, GEN_NOPUSH | GEN_NOFUNC, g_lt },
{ TOK_LE, GEN_NOPUSH | GEN_NOFUNC, g_le },
{ TOK_GE, GEN_NOPUSH | GEN_NOFUNC, g_ge },
{ TOK_GT, GEN_NOPUSH | GEN_NOFUNC, g_gt },
{ TOK_INVALID, 0, 0 }
};
hie_compare (hie6_ops, Expr, ShiftExpr);
}
static void hie5 (ExprDesc* Expr)
/* Handle == and != */
{
static const GenDesc hie5_ops[] = {
{ TOK_EQ, GEN_NOPUSH, g_eq },
{ TOK_NE, GEN_NOPUSH, g_ne },
{ TOK_INVALID, 0, 0 }
};
hie_compare (hie5_ops, Expr, hie6);
}
static void hie4 (ExprDesc* Expr)
/* Handle & (bitwise and) */
{
static const GenDesc hie4_ops[] = {
{ TOK_AND, GEN_NOPUSH | GEN_COMM, g_and },
{ TOK_INVALID, 0, 0 }
};
int UsedGen;
hie_internal (hie4_ops, Expr, hie5, &UsedGen);
}
static void hie3 (ExprDesc* Expr)
/* Handle ^ (bitwise exclusive or) */
{
static const GenDesc hie3_ops[] = {
{ TOK_XOR, GEN_NOPUSH | GEN_COMM, g_xor },
{ TOK_INVALID, 0, 0 }
};
int UsedGen;
hie_internal (hie3_ops, Expr, hie4, &UsedGen);
}
static void hie2 (ExprDesc* Expr)
/* Handle | (bitwise or) */
{
static const GenDesc hie2_ops[] = {
{ TOK_OR, GEN_NOPUSH | GEN_COMM, g_or },
{ TOK_INVALID, 0, 0 }
};
int UsedGen;
hie_internal (hie2_ops, Expr, hie3, &UsedGen);
}
static void hieAndPP (ExprDesc* Expr)
/* Process "exp && exp" in preprocessor mode (that is, when the parser is
** called recursively from the preprocessor.
*/
{
ExprDesc Expr2;
ConstAbsIntExpr (hie2, Expr);
while (CurTok.Tok == TOK_BOOL_AND) {
/* Skip the && */
NextToken ();
/* Get rhs */
ConstAbsIntExpr (hie2, &Expr2);
/* Combine the two */
Expr->IVal = (Expr->IVal && Expr2.IVal);
}
}
static void hieOrPP (ExprDesc *Expr)
/* Process "exp || exp" in preprocessor mode (that is, when the parser is
** called recursively from the preprocessor.
*/
{
ExprDesc Expr2;
ConstAbsIntExpr (hieAndPP, Expr);
while (CurTok.Tok == TOK_BOOL_OR) {
/* Skip the && */
NextToken ();
/* Get rhs */
ConstAbsIntExpr (hieAndPP, &Expr2);
/* Combine the two */
Expr->IVal = (Expr->IVal || Expr2.IVal);
}
}
static void hieAnd (ExprDesc* Expr, unsigned TrueLab, int* BoolOp)
/* Process "exp && exp" */
{
int FalseLab;
ExprDesc Expr2;
ExprWithCheck (hie2, Expr);
if (CurTok.Tok == TOK_BOOL_AND) {
/* Tell our caller that we're evaluating a boolean */
*BoolOp = 1;
/* Get a label that we will use for false expressions */
FalseLab = GetLocalLabel ();
/* If the expr hasn't set condition codes, set the force-test flag */
if (!ED_IsTested (Expr)) {
ED_MarkForTest (Expr);
}
/* Load the value */
LoadExpr (CF_FORCECHAR, Expr);
/* Generate the jump */
g_falsejump (CF_NONE, FalseLab);
/* Parse more boolean and's */
while (CurTok.Tok == TOK_BOOL_AND) {
/* Skip the && */
NextToken ();
/* Get rhs */
hie2 (&Expr2);
if (!ED_IsTested (&Expr2)) {
ED_MarkForTest (&Expr2);
}
LoadExpr (CF_FORCECHAR, &Expr2);
/* Do short circuit evaluation */
if (CurTok.Tok == TOK_BOOL_AND) {
g_falsejump (CF_NONE, FalseLab);
} else {
/* Last expression - will evaluate to true */
g_truejump (CF_NONE, TrueLab);
}
}
/* Define the false jump label here */
g_defcodelabel (FalseLab);
/* The result is an rvalue in primary */
ED_MakeRValExpr (Expr);
ED_TestDone (Expr); /* Condition codes are set */
}
}
static void hieOr (ExprDesc *Expr)
/* Process "exp || exp". */
{
ExprDesc Expr2;
int BoolOp = 0; /* Did we have a boolean op? */
int AndOp; /* Did we have a && operation? */
unsigned TrueLab; /* Jump to this label if true */
unsigned DoneLab;
/* Get a label */
TrueLab = GetLocalLabel ();
/* Call the next level parser */
hieAnd (Expr, TrueLab, &BoolOp);
/* Any boolean or's? */
if (CurTok.Tok == TOK_BOOL_OR) {
/* If the expr hasn't set condition codes, set the force-test flag */
if (!ED_IsTested (Expr)) {
ED_MarkForTest (Expr);
}
/* Get first expr */
LoadExpr (CF_FORCECHAR, Expr);
/* For each expression jump to TrueLab if true. Beware: If we
** had && operators, the jump is already in place!
*/
if (!BoolOp) {
g_truejump (CF_NONE, TrueLab);
}
/* Remember that we had a boolean op */
BoolOp = 1;
/* while there's more expr */
while (CurTok.Tok == TOK_BOOL_OR) {
/* skip the || */
NextToken ();
/* Get a subexpr */
AndOp = 0;
hieAnd (&Expr2, TrueLab, &AndOp);
if (!ED_IsTested (&Expr2)) {
ED_MarkForTest (&Expr2);
}
LoadExpr (CF_FORCECHAR, &Expr2);
/* If there is more to come, add shortcut boolean eval. */
g_truejump (CF_NONE, TrueLab);
}
/* The result is an rvalue in primary */
ED_MakeRValExpr (Expr);
ED_TestDone (Expr); /* Condition codes are set */
}
/* If we really had boolean ops, generate the end sequence */
if (BoolOp) {
DoneLab = GetLocalLabel ();
g_getimmed (CF_INT | CF_CONST, 0, 0); /* Load FALSE */
g_falsejump (CF_NONE, DoneLab);
g_defcodelabel (TrueLab);
g_getimmed (CF_INT | CF_CONST, 1, 0); /* Load TRUE */
g_defcodelabel (DoneLab);
}
}
static void hieQuest (ExprDesc* Expr)
/* Parse the ternary operator */
{
int FalseLab;
int TrueLab;
CodeMark TrueCodeEnd;
ExprDesc Expr2; /* Expression 2 */
ExprDesc Expr3; /* Expression 3 */
int Expr2IsNULL; /* Expression 2 is a NULL pointer */
int Expr3IsNULL; /* Expression 3 is a NULL pointer */
Type* ResultType; /* Type of result */
/* Call the lower level eval routine */
if (Preprocessing) {
ExprWithCheck (hieOrPP, Expr);
} else {
ExprWithCheck (hieOr, Expr);
}
/* Check if it's a ternary expression */
if (CurTok.Tok == TOK_QUEST) {
NextToken ();
if (!ED_IsTested (Expr)) {
/* Condition codes not set, request a test */
ED_MarkForTest (Expr);
}
LoadExpr (CF_NONE, Expr);
FalseLab = GetLocalLabel ();
g_falsejump (CF_NONE, FalseLab);
/* Parse second expression. Remember for later if it is a NULL pointer
** expression, then load it into the primary.
*/
ExprWithCheck (hie1, &Expr2);
Expr2IsNULL = ED_IsNullPtr (&Expr2);
if (!IsTypeVoid (Expr2.Type)) {
/* Load it into the primary */
LoadExpr (CF_NONE, &Expr2);
ED_MakeRValExpr (&Expr2);
Expr2.Type = PtrConversion (Expr2.Type);
}
/* Remember the current code position */
GetCodePos (&TrueCodeEnd);
/* Jump around the evaluation of the third expression */
TrueLab = GetLocalLabel ();
ConsumeColon ();
g_jump (TrueLab);
/* Jump here if the first expression was false */
g_defcodelabel (FalseLab);
/* Parse third expression. Remember for later if it is a NULL pointer
** expression, then load it into the primary.
*/
ExprWithCheck (hie1, &Expr3);
Expr3IsNULL = ED_IsNullPtr (&Expr3);
if (!IsTypeVoid (Expr3.Type)) {
/* Load it into the primary */
LoadExpr (CF_NONE, &Expr3);
ED_MakeRValExpr (&Expr3);
Expr3.Type = PtrConversion (Expr3.Type);
}
/* Check if any conversions are needed, if so, do them.
** Conversion rules for ?: expression are:
** - if both expressions are int expressions, default promotion
** rules for ints apply.
** - if both expressions are pointers of the same type, the
** result of the expression is of this type.
** - if one of the expressions is a pointer and the other is
** a zero constant, the resulting type is that of the pointer
** type.
** - if both expressions are void expressions, the result is of
** type void.
** - all other cases are flagged by an error.
*/
if (IsClassInt (Expr2.Type) && IsClassInt (Expr3.Type)) {
CodeMark CvtCodeStart;
CodeMark CvtCodeEnd;
/* Get common type */
ResultType = promoteint (Expr2.Type, Expr3.Type);
/* Convert the third expression to this type if needed */
TypeConversion (&Expr3, ResultType);
/* Emit conversion code for the second expression, but remember
** where it starts end ends.
*/
GetCodePos (&CvtCodeStart);
TypeConversion (&Expr2, ResultType);
GetCodePos (&CvtCodeEnd);
/* If we had conversion code, move it to the right place */
if (!CodeRangeIsEmpty (&CvtCodeStart, &CvtCodeEnd)) {
MoveCode (&CvtCodeStart, &CvtCodeEnd, &TrueCodeEnd);
}
} else if (IsClassPtr (Expr2.Type) && IsClassPtr (Expr3.Type)) {
/* Must point to same type */
if (TypeCmp (Indirect (Expr2.Type), Indirect (Expr3.Type)) < TC_EQUAL) {
Error ("Incompatible pointer types");
}
/* Result has the common type */
ResultType = Expr2.Type;
} else if (IsClassPtr (Expr2.Type) && Expr3IsNULL) {
/* Result type is pointer, no cast needed */
ResultType = Expr2.Type;
} else if (Expr2IsNULL && IsClassPtr (Expr3.Type)) {
/* Result type is pointer, no cast needed */
ResultType = Expr3.Type;
} else if (IsTypeVoid (Expr2.Type) && IsTypeVoid (Expr3.Type)) {
/* Result type is void */
ResultType = Expr3.Type;
} else {
Error ("Incompatible types");
ResultType = Expr2.Type; /* Doesn't matter here */
}
/* Define the final label */
g_defcodelabel (TrueLab);
/* Setup the target expression */
ED_MakeRValExpr (Expr);
Expr->Type = ResultType;
}
}
static void opeq (const GenDesc* Gen, ExprDesc* Expr, const char* Op)
/* Process "op=" operators. */
{
ExprDesc Expr2;
unsigned flags;
CodeMark Mark;
int MustScale;
/* op= can only be used with lvalues */
if (!ED_IsLVal (Expr)) {
Error ("Invalid lvalue in assignment");
return;
}
/* The left side must not be const qualified */
if (IsQualConst (Expr->Type)) {
Error ("Assignment to const");
}
/* There must be an integer or pointer on the left side */
if (!IsClassInt (Expr->Type) && !IsTypePtr (Expr->Type)) {
Error ("Invalid left operand type");
/* Continue. Wrong code will be generated, but the compiler won't
** break, so this is the best error recovery.
*/
}
/* Skip the operator token */
NextToken ();
/* Determine the type of the lhs */
flags = TypeOf (Expr->Type);
MustScale = (Gen->Func == g_add || Gen->Func == g_sub) && IsTypePtr (Expr->Type);
/* Get the lhs address on stack (if needed) */
PushAddr (Expr);
/* Fetch the lhs into the primary register if needed */
LoadExpr (CF_NONE, Expr);
/* Bring the lhs on stack */
GetCodePos (&Mark);
g_push (flags, 0);
/* Evaluate the rhs */
MarkedExprWithCheck (hie1, &Expr2);
/* The rhs must be an integer (or a float, but we don't support that yet */
if (!IsClassInt (Expr2.Type)) {
Error ("Invalid right operand for binary operator '%s'", Op);
/* Continue. Wrong code will be generated, but the compiler won't
** break, so this is the best error recovery.
*/
}
/* Check for a constant expression */
if (ED_IsConstAbs (&Expr2) && ED_CodeRangeIsEmpty (&Expr2)) {
/* The resulting value is a constant. If the generator has the NOPUSH
** flag set, don't push the lhs.
*/
if (Gen->Flags & GEN_NOPUSH) {
RemoveCode (&Mark);
}
if (MustScale) {
/* lhs is a pointer, scale rhs */
Expr2.IVal *= CheckedSizeOf (Expr->Type+1);
}
/* If the lhs is character sized, the operation may be later done
** with characters.
*/
if (CheckedSizeOf (Expr->Type) == SIZEOF_CHAR) {
flags |= CF_FORCECHAR;
}
/* Special handling for add and sub - some sort of a hack, but short code */
if (Gen->Func == g_add) {
g_inc (flags | CF_CONST, Expr2.IVal);
} else if (Gen->Func == g_sub) {
g_dec (flags | CF_CONST, Expr2.IVal);
} else {
if (Expr2.IVal == 0) {
/* Check for div by zero/mod by zero */
if (Gen->Func == g_div) {
Error ("Division by zero");
} else if (Gen->Func == g_mod) {
Error ("Modulo operation with zero");
}
}
Gen->Func (flags | CF_CONST, Expr2.IVal);
}
} else {
/* rhs is not constant. Load into the primary */
LoadExpr (CF_NONE, &Expr2);
if (MustScale) {
/* lhs is a pointer, scale rhs */
g_scale (TypeOf (Expr2.Type), CheckedSizeOf (Expr->Type+1));
}
/* If the lhs is character sized, the operation may be later done
** with characters.
*/
if (CheckedSizeOf (Expr->Type) == SIZEOF_CHAR) {
flags |= CF_FORCECHAR;
}
/* Adjust the types of the operands if needed */
Gen->Func (g_typeadjust (flags, TypeOf (Expr2.Type)), 0);
}
Store (Expr, 0);
ED_MakeRValExpr (Expr);
}
static void addsubeq (const GenDesc* Gen, ExprDesc *Expr, const char* Op)
/* Process the += and -= operators */
{
ExprDesc Expr2;
unsigned lflags;
unsigned rflags;
int MustScale;
/* We're currently only able to handle some adressing modes */
if (ED_GetLoc (Expr) == E_LOC_EXPR || ED_GetLoc (Expr) == E_LOC_PRIMARY) {
/* Use generic routine */
opeq (Gen, Expr, Op);
return;
}
/* We must have an lvalue */
if (ED_IsRVal (Expr)) {
Error ("Invalid lvalue in assignment");
return;
}
/* The left side must not be const qualified */
if (IsQualConst (Expr->Type)) {
Error ("Assignment to const");
}
/* There must be an integer or pointer on the left side */
if (!IsClassInt (Expr->Type) && !IsTypePtr (Expr->Type)) {
Error ("Invalid left operand type");
/* Continue. Wrong code will be generated, but the compiler won't
** break, so this is the best error recovery.
*/
}
/* Skip the operator */
NextToken ();
/* Check if we have a pointer expression and must scale rhs */
MustScale = IsTypePtr (Expr->Type);
/* Initialize the code generator flags */
lflags = 0;
rflags = 0;
/* Evaluate the rhs. We expect an integer here, since float is not
** supported
*/
hie1 (&Expr2);
if (!IsClassInt (Expr2.Type)) {
Error ("Invalid right operand for binary operator '%s'", Op);
/* Continue. Wrong code will be generated, but the compiler won't
** break, so this is the best error recovery.
*/
}
if (ED_IsConstAbs (&Expr2)) {
/* The resulting value is a constant. Scale it. */
if (MustScale) {
Expr2.IVal *= CheckedSizeOf (Indirect (Expr->Type));
}
rflags |= CF_CONST;
lflags |= CF_CONST;
} else {
/* Not constant, load into the primary */
LoadExpr (CF_NONE, &Expr2);
if (MustScale) {
/* lhs is a pointer, scale rhs */
g_scale (TypeOf (Expr2.Type), CheckedSizeOf (Indirect (Expr->Type)));
}
}
/* Setup the code generator flags */
lflags |= TypeOf (Expr->Type) | GlobalModeFlags (Expr) | CF_FORCECHAR;
rflags |= TypeOf (Expr2.Type) | CF_FORCECHAR;
/* Convert the type of the lhs to that of the rhs */
g_typecast (lflags, rflags);
/* Output apropriate code depending on the location */
switch (ED_GetLoc (Expr)) {
case E_LOC_ABS:
/* Absolute: numeric address or const */
if (Gen->Tok == TOK_PLUS_ASSIGN) {
g_addeqstatic (lflags, Expr->Name, Expr->IVal, Expr2.IVal);
} else {
g_subeqstatic (lflags, Expr->Name, Expr->IVal, Expr2.IVal);
}
break;
case E_LOC_GLOBAL:
/* Global variable */
if (Gen->Tok == TOK_PLUS_ASSIGN) {
g_addeqstatic (lflags, Expr->Name, Expr->IVal, Expr2.IVal);
} else {
g_subeqstatic (lflags, Expr->Name, Expr->IVal, Expr2.IVal);
}
break;
case E_LOC_STATIC:
case E_LOC_LITERAL:
/* Static variable or literal in the literal pool */
if (Gen->Tok == TOK_PLUS_ASSIGN) {
g_addeqstatic (lflags, Expr->Name, Expr->IVal, Expr2.IVal);
} else {
g_subeqstatic (lflags, Expr->Name, Expr->IVal, Expr2.IVal);
}
break;
case E_LOC_REGISTER:
/* Register variable */
if (Gen->Tok == TOK_PLUS_ASSIGN) {
g_addeqstatic (lflags, Expr->Name, Expr->IVal, Expr2.IVal);
} else {
g_subeqstatic (lflags, Expr->Name, Expr->IVal, Expr2.IVal);
}
break;
case E_LOC_STACK:
/* Value on the stack */
if (Gen->Tok == TOK_PLUS_ASSIGN) {
g_addeqlocal (lflags, Expr->IVal, Expr2.IVal);
} else {
g_subeqlocal (lflags, Expr->IVal, Expr2.IVal);
}
break;
default:
Internal ("Invalid location in Store(): 0x%04X", ED_GetLoc (Expr));
}
/* Expression is a rvalue in the primary now */
ED_MakeRValExpr (Expr);
}
void hie1 (ExprDesc* Expr)
/* Parse first level of expression hierarchy. */
{
hieQuest (Expr);
switch (CurTok.Tok) {
case TOK_ASSIGN:
Assignment (Expr);
break;
case TOK_PLUS_ASSIGN:
addsubeq (&GenPASGN, Expr, "+=");
break;
case TOK_MINUS_ASSIGN:
addsubeq (&GenSASGN, Expr, "-=");
break;
case TOK_MUL_ASSIGN:
opeq (&GenMASGN, Expr, "*=");
break;
case TOK_DIV_ASSIGN:
opeq (&GenDASGN, Expr, "/=");
break;
case TOK_MOD_ASSIGN:
opeq (&GenMOASGN, Expr, "%=");
break;
case TOK_SHL_ASSIGN:
opeq (&GenSLASGN, Expr, "<<=");
break;
case TOK_SHR_ASSIGN:
opeq (&GenSRASGN, Expr, ">>=");
break;
case TOK_AND_ASSIGN:
opeq (&GenAASGN, Expr, "&=");
break;
case TOK_XOR_ASSIGN:
opeq (&GenXOASGN, Expr, "^=");
break;
case TOK_OR_ASSIGN:
opeq (&GenOASGN, Expr, "|=");
break;
default:
break;
}
}
void hie0 (ExprDesc *Expr)
/* Parse comma operator. */
{
hie1 (Expr);
while (CurTok.Tok == TOK_COMMA) {
NextToken ();
hie1 (Expr);
}
}
int evalexpr (unsigned Flags, void (*Func) (ExprDesc*), ExprDesc* Expr)
/* Will evaluate an expression via the given function. If the result is a
** constant, 0 is returned and the value is put in the Expr struct. If the
** result is not constant, LoadExpr is called to bring the value into the
** primary register and 1 is returned.
*/
{
/* Evaluate */
ExprWithCheck (Func, Expr);
/* Check for a constant expression */
if (ED_IsConstAbs (Expr)) {
/* Constant expression */
return 0;
} else {
/* Not constant, load into the primary */
LoadExpr (Flags, Expr);
return 1;
}
}
void Expression0 (ExprDesc* Expr)
/* Evaluate an expression via hie0 and put the result into the primary register */
{
ExprWithCheck (hie0, Expr);
LoadExpr (CF_NONE, Expr);
}
void ConstExpr (void (*Func) (ExprDesc*), ExprDesc* Expr)
/* Will evaluate an expression via the given function. If the result is not
** a constant of some sort, a diagnostic will be printed, and the value is
** replaced by a constant one to make sure there are no internal errors that
** result from this input error.
*/
{
ExprWithCheck (Func, Expr);
if (!ED_IsConst (Expr)) {
Error ("Constant expression expected");
/* To avoid any compiler errors, make the expression a valid const */
ED_MakeConstAbsInt (Expr, 1);
}
}
void BoolExpr (void (*Func) (ExprDesc*), ExprDesc* Expr)
/* Will evaluate an expression via the given function. If the result is not
** something that may be evaluated in a boolean context, a diagnostic will be
** printed, and the value is replaced by a constant one to make sure there
** are no internal errors that result from this input error.
*/
{
ExprWithCheck (Func, Expr);
if (!ED_IsBool (Expr)) {
Error ("Boolean expression expected");
/* To avoid any compiler errors, make the expression a valid int */
ED_MakeConstAbsInt (Expr, 1);
}
}
void ConstAbsIntExpr (void (*Func) (ExprDesc*), ExprDesc* Expr)
/* Will evaluate an expression via the given function. If the result is not
** a constant numeric integer value, a diagnostic will be printed, and the
** value is replaced by a constant one to make sure there are no internal
** errors that result from this input error.
*/
{
ExprWithCheck (Func, Expr);
if (!ED_IsConstAbsInt (Expr)) {
Error ("Constant integer expression expected");
/* To avoid any compiler errors, make the expression a valid const */
ED_MakeConstAbsInt (Expr, 1);
}
}