cc65/src/cc65/expr.c

4247 lines
142 KiB
C

/* expr.c
**
** 1998-06-21, Ullrich von Bassewitz
** 2020-11-20, 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 "initdata.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 */
/*****************************************************************************/
/* 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 };
/*****************************************************************************/
/* Forward declarations */
/*****************************************************************************/
static void parseadd (ExprDesc* Expr, int DoArrayRef);
static void PostInc (ExprDesc* Expr);
static void PostDec (ExprDesc* Expr);
/*****************************************************************************/
/* Helper functions */
/*****************************************************************************/
unsigned GlobalModeFlags (const ExprDesc* Expr)
/* Return the addressing mode flags for the given expression */
{
switch (ED_GetLoc (Expr)) {
case E_LOC_NONE: return CF_IMM;
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_STACK;
case E_LOC_PRIMARY: return CF_PRIMARY;
case E_LOC_EXPR: return CF_EXPR;
case E_LOC_LITERAL: return CF_LITERAL;
case E_LOC_CODE: return CF_CODE;
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 unsigned typeadjust (ExprDesc* lhs, const 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 */
const Type* lhst = lhs->Type;
const Type* rhst = rhs->Type;
/* Generate type adjustment code if needed */
ltype = TypeOf (lhst);
if (ED_IsConstAbsInt (lhs) && ltype == CF_INT && lhs->IVal >= 0 && lhs->IVal < 256) {
/* If the lhs is a int constant that fits in an unsigned char, use unsigned char.
** g_typeadjust will either promote this to int or unsigned int as appropriate
** based on the other operand. See comment in hie_internal.
*/
ltype = CF_CHAR | CF_UNSIGNED;
}
if (ED_IsLocNone (lhs)) {
ltype |= CF_CONST;
}
if (NoPush) {
/* Value is in primary register*/
ltype |= CF_PRIMARY;
}
rtype = TypeOf (rhst);
if (ED_IsConstAbsInt (rhs) && rtype == CF_INT && rhs->IVal >= 0 && rhs->IVal < 256) {
rtype = CF_CHAR | CF_UNSIGNED;
}
if (ED_IsLocNone (rhs)) {
rtype |= CF_CONST;
}
flags = g_typeadjust (ltype, rtype);
/* Set the type of the result */
lhs->Type = ArithmeticConvert (lhst, rhst);
/* Return the code generator flags */
return flags;
}
void LimitExprValue (ExprDesc* Expr)
/* Limit the constant value of the expression to the range of its type */
{
switch (GetUnderlyingTypeCode (Expr->Type)) {
case T_INT:
case T_SHORT:
Expr->IVal = (int16_t)Expr->IVal;
break;
case T_UINT:
case T_USHORT:
case T_PTR:
case T_ARRAY:
Expr->IVal = (uint16_t)Expr->IVal;
break;
case T_LONG:
Expr->IVal = (int32_t)Expr->IVal;
break;
case T_ULONG:
Expr->IVal = (uint32_t)Expr->IVal;
break;
case T_SCHAR:
Expr->IVal = (int8_t)Expr->IVal;
break;
case T_UCHAR:
Expr->IVal = (uint8_t)Expr->IVal;
break;
default:
Internal ("hie_internal: constant result type %s\n", GetFullTypeName (Expr->Type));
}
}
static const GenDesc* FindGen (token_t Tok, const GenDesc* Table)
/* Find a token in a generator table */
{
while (Table->Tok != TOK_INVALID) {
if ((token_t)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)));
}
static unsigned ExprCheckedSizeOf (const Type* T)
/* Specially checked SizeOf() used in 'sizeof' expressions */
{
unsigned Size = SizeOf (T);
SymEntry* Sym;
if (Size == 0) {
Sym = GetSymType (T);
if (Sym == 0 || !SymIsDef (Sym)) {
Error ("Cannot apply 'sizeof' to incomplete type '%s'", GetFullTypeName (T));
}
}
return Size;
}
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 (const ExprDesc* Expr)
/* If the result of a comparison is constant, this is suspicious */
{
if (!ED_NeedsConst (Expr) && IS_Get (&WarnConstComparison) != 0) {
Warning ("Result of comparison is always %s", Expr->IVal != 0 ? "true" : "false");
}
}
/*****************************************************************************/
/* code */
/*****************************************************************************/
typedef enum {
DOT_INC,
DOT_DEC,
} DeferredOpType;
typedef struct {
ExprDesc Expr;
DeferredOpType OpType;
} DeferredOp;
Collection DeferredOps;
void InitDeferredOps (void)
/* Init the collection for storing deferred ops */
{
InitCollection (&DeferredOps);
}
void DoneDeferredOps (void)
/* Deinit the collection for storing deferred ops */
{
DoneCollection (&DeferredOps);
}
static void DeferInc (const ExprDesc* Expr)
/* Defer the post-inc and put it in a queue */
{
if (ED_IsUneval (Expr)) {
return;
}
DeferredOp* Op = xmalloc (sizeof (DeferredOp));
memcpy (&Op->Expr, Expr, sizeof (ExprDesc));
Op->OpType = DOT_INC;
CollAppend (&DeferredOps, Op);
}
static void DeferDec (const ExprDesc* Expr)
/* Defer the post-dec and put it in a queue */
{
if (ED_IsUneval (Expr)) {
return;
}
DeferredOp* Op = xmalloc (sizeof (DeferredOp));
memcpy (&Op->Expr, Expr, sizeof (ExprDesc));
Op->OpType = DOT_DEC;
CollAppend (&DeferredOps, Op);
}
static void DoInc (ExprDesc* Expr, unsigned KeepResult)
/* Do increment */
{
unsigned Flags;
long Val;
/* Get the increment value in bytes */
Val = IsTypePtr (Expr->Type) ? CheckedSizeOf (Expr->Type + 1) : 1;
/* Special treatment is needed for bit-fields */
if (IsTypeFragBitField (Expr->Type)) {
DoIncDecBitField (Expr, Val, KeepResult);
return;
}
/* Get the flags */
Flags = TypeOf (Expr->Type) | GlobalModeFlags (Expr) | CF_FORCECHAR | CF_CONST;
if (KeepResult != OA_NEED_NEW) {
/* No need to get the result */
Flags |= CF_NOKEEP;
}
if (KeepResult == OA_NEED_OLD) {
Flags |= CF_FORCECHAR;
/* Push the address if needed */
PushAddr (Expr);
/* Save the original value */
LoadExpr (CF_NONE, Expr);
g_save (Flags);
/* Do the increment */
g_inc (Flags | CF_CONST, Val);
/* Store the result back */
Store (Expr, 0);
/* Restore the original value */
g_restore (Flags);
} else {
/* Check the location of the data */
switch (ED_GetLoc (Expr)) {
case E_LOC_ABS:
/* Absolute numeric addressed variable */
g_addeqstatic (Flags, Expr->IVal, 0, Val);
break;
case E_LOC_GLOBAL:
case E_LOC_STATIC:
case E_LOC_REGISTER:
case E_LOC_LITERAL:
case E_LOC_CODE:
/* Global variabl, static variable, register variable, pooled
** literal or code label location.
*/
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 referenced in the primary register */
g_addeqind (Flags, Expr->IVal, Val);
break;
default:
Internal ("Invalid location in DoInc(): 0x%04X", ED_GetLoc (Expr));
}
}
}
static void DoDec (ExprDesc* Expr, unsigned KeepResult)
/* Do decrement */
{
unsigned Flags;
long Val;
/* Get the decrement value in bytes */
Val = IsTypePtr (Expr->Type) ? CheckedSizeOf (Expr->Type + 1) : 1;
/* Special treatment is needed for bit-fields */
if (IsTypeFragBitField (Expr->Type)) {
DoIncDecBitField (Expr, -Val, KeepResult);
return;
}
/* Get the flags */
Flags = TypeOf (Expr->Type) | GlobalModeFlags (Expr) | CF_FORCECHAR | CF_CONST;
if (KeepResult != OA_NEED_NEW) {
/* No need to get the result */
Flags |= CF_NOKEEP;
}
if (KeepResult == OA_NEED_OLD) {
Flags |= CF_FORCECHAR;
/* Push the address if needed */
PushAddr (Expr);
/* Save the original value */
LoadExpr (CF_NONE, Expr);
g_save (Flags);
/* Do the decrement */
g_dec (Flags | CF_CONST, Val);
/* Store the result back */
Store (Expr, 0);
/* Restore the original value */
g_restore (Flags);
} else {
/* Check the location of the data */
switch (ED_GetLoc (Expr)) {
case E_LOC_ABS:
/* Absolute numeric addressed variable */
g_subeqstatic (Flags, Expr->IVal, 0, Val);
break;
case E_LOC_GLOBAL:
case E_LOC_STATIC:
case E_LOC_REGISTER:
case E_LOC_LITERAL:
case E_LOC_CODE:
/* Global variabl, static variable, register variable, pooled
** literal or code label location.
*/
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_dec (Flags, Val);
break;
case E_LOC_EXPR:
/* An expression referenced in the primary register */
g_subeqind (Flags, Expr->IVal, Val);
break;
default:
Internal ("Invalid location in DoDec(): 0x%04X", ED_GetLoc (Expr));
}
}
}
int GetDeferredOpCount (void)
/* Return how many deferred operations are still waiting in the queque */
{
return (int)CollCount (&DeferredOps);
}
void CheckDeferredOpAllDone (void)
/* Check if all deferred operations are done at sequence points.
** Die off if check fails.
*/
{
if (GetDeferredOpCount () > 0) {
Internal ("Code generation messed up: missing operations past sequence points.");
}
}
void DoDeferred (unsigned Flags, ExprDesc* Expr)
/* Do deferred operations such as post-inc/dec at sequence points */
{
int I;
unsigned Size = 0;
int Count = GetDeferredOpCount ();
/* Nothing to be done */
if (Count <= 0) {
return;
}
/* Backup some regs/processor flags around the inc/dec */
if ((Flags & SQP_KEEP_TEST) != 0 && ED_NeedsTest (Expr)) {
/* Sufficient to add a pair of PHP/PLP for all cases */
AddCodeLine ("php");
}
/* Backup the content of EAX around the inc/dec */
if ((Flags & SQP_KEEP_EAX) != 0 && ED_NeedsPrimary (Expr)) {
/* Get the size */
Size = CheckedSizeOf (Expr->Type);
if (Size < 2) {
AddCodeLine ("pha");
} else if (Size < 3) {
AddCodeLine ("sta regsave");
AddCodeLine ("stx regsave+1");
} else {
AddCodeLine ("jsr saveeax");
}
}
for (I = 0; I < Count; ++I) {
DeferredOp* Op = CollAtUnchecked (&DeferredOps, I);
switch (Op->OpType) {
case DOT_INC:
DoInc (&Op->Expr, OA_NEED_NONE);
break;
case DOT_DEC:
DoDec (&Op->Expr, OA_NEED_NONE);
break;
}
xfree (&Op->Expr);
}
CollDeleteAll (&DeferredOps);
/* Restore the content of EAX around the inc/dec */
if ((Flags & SQP_KEEP_EAX) != 0 && ED_NeedsPrimary (Expr)) {
if (Size < 2) {
AddCodeLine ("pla");
} else if (Size < 3) {
AddCodeLine ("lda regsave");
AddCodeLine ("ldx regsave+1");
} else {
AddCodeLine ("jsr resteax");
}
}
/* Restore the regs/processor flags around the inc/dec */
if ((Flags & SQP_KEEP_TEST) != 0 && ED_NeedsTest (Expr)) {
/* Sufficient to pop the processor flags */
AddCodeLine ("plp");
}
/* Expression has had side effects */
Expr->Flags |= E_SIDE_EFFECTS;
}
static unsigned FunctionArgList (FuncDesc* Func, int IsFastcall, ExprDesc* ED)
/* Parse the argument list of the called function and pass the arguments to it.
** Depending on several criteria, this may be done by just pushing into each
** parameter separately, or creating the parameter frame once and then storing
** arguments into this frame one by one.
** The function returns the size of the arguments pushed in bytes.
*/
{
ExprDesc Expr;
/* Initialize variables */
SymEntry* Param = 0; /* Keep gcc silent */
unsigned PushedSize = 0; /* Size of arguments pushed */
unsigned PushedCount = 0; /* Number of arguments pushed */
unsigned FrameSize = 0; /* Size of parameter frame */
unsigned FrameParams = 0; /* Number of parameters in frame */
int FrameOffs = 0; /* Offset into parameter frame */
int Ellipsis = 0; /* Function is variadic */
/* Make sure the size of all parameters are known */
int ParamComplete = F_CheckParamList (Func, 1);
/* As an optimization, we may allocate the complete parameter frame at
** once instead of pushing into 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 into (don't count the last
** parameter for __fastcall__ functions).
**
** The FrameSize variable will contain a value > 0 if storing into a frame
** (instead of pushing) is enabled.
**
*/
if (ParamComplete && 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 into */
FrameSize -= CheckedSizeOf (Func->LastParam->Type);
--FrameParams;
}
/* Do we have more than one parameter in the frame? */
if (FrameParams > 1) {
/* Okeydokey, set up the frame */
FrameOffs = StackPtr;
g_space (FrameSize);
StackPtr -= FrameSize;
} else {
/* Don't use a preallocated frame */
FrameSize = 0;
}
}
/* Parse the actual argument list */
while (CurTok.Tok != TOK_RPAREN) {
unsigned Flags; /* Code generator flags, not expression flags */
ED_Init (&Expr);
/* This way, the info of the last parameter won't be cleared */
Expr.Flags |= ED->Flags & E_MASK_KEEP_SUBEXPR;
/* Count arguments */
++PushedCount;
/* Fetch the pointer to the next argument, check for too many args */
if (PushedCount <= 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 PushedCount 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 (PushedCount == 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 argument expression */
hie1 (&Expr);
/* Skip to the next parameter if there are any incomplete types */
if (ParamComplete) {
/* 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", function to "pointer to function" and do integral
** promotion if necessary.
*/
TypeConversion (&Expr, StdConversion (Expr.Type));
}
/* Handle struct/union specially */
if (IsClassStruct (Expr.Type)) {
/* Use the replacement type */
Flags |= TypeOf (GetStructReplacementType (Expr.Type));
/* Load the value into the primary if it is not already there */
LoadExpr (Flags, &Expr);
} else {
/* Load the value into the primary if it is not already there */
LoadExpr (CF_NONE, &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 */
PushedSize += ArgSize;
}
}
/* Propagate viral flags */
ED_PropagateFrom (ED, &Expr);
/* Check for end of argument list */
if (CurTok.Tok != TOK_COMMA) {
break;
}
NextToken ();
/* Check for stray comma */
if (CurTok.Tok == TOK_RPAREN) {
Error ("Argument expected after comma");
break;
}
DoDeferred (SQP_KEEP_NONE, &Expr);
}
/* Append last deferred inc/dec before the function is called.
** The last parameter needs to be preserved if it is passed in AX/EAX Regs.
*/
DoDeferred (IsFastcall && PushedCount > 0 ? SQP_KEEP_EAX : SQP_KEEP_NONE, &Expr);
/* Check if we had enough arguments */
if (PushedCount < Func->ParamCount) {
Error ("Too few arguments in function call");
}
/* The function returns the size of all arguments pushed onto the stack.
** However, if there are parameters missed (which is an error, and was
** flagged by the compiler), AND a stack frame was preallocated above,
** we would lose track of the stackpointer, and generate an internal error
** later. So we correct the value by the parameters that should have been
** pushed into, to avoid an internal compiler error. Since an error was
** generated before, no code will be output anyway.
*/
return PushedSize + FrameSize;
}
static void FunctionCall (ExprDesc* Expr)
/* Perform a function call. */
{
FuncDesc* Func; /* Function descriptor */
int IsFuncPtr; /* Flag */
unsigned ArgSize; /* Number of arguments bytes */
CodeMark Mark;
int PtrOffs = 0; /* Offset of function pointer on stack */
int IsFastcall = 0; /* True if we are fast-calling the function */
int PtrOnStack = 0; /* True if a pointer copy is on stack */
const Type* ReturnType;
/* 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->ParamCount > 0 || (Func->Flags & FD_EMPTY) != 0) &&
IsFastcallFunc (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_IsConstAddr (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_FinalizeRValLoad (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 && !ED_IsUneval (Expr)) {
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->ParamCount > 0 || (Func->Flags & FD_EMPTY) != 0) &&
IsFastcallFunc (Expr->Type);
}
/* Parse the argument list and pass them to the called function */
ArgSize = FunctionArgList (Func, IsFastcall, Expr);
/* 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 arguments, the pointer is still in the
** primary. Remove the code to push it and correct the
** stack pointer.
*/
if (ArgSize == 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 (FuncTypeOf (Expr->Type+1), ArgSize, 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_STACK, ArgSize, 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;
if (Func->WrappedCallData == WRAPPED_CALL_USE_BANK) {
/* Store the bank attribute in tmp4 */
SB_AppendStr (&S, "ldy #<.bank(_");
SB_AppendStr (&S, (const char*) Expr->Name);
SB_AppendChar (&S, ')');
} else {
/* 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 (FuncTypeOf (Expr->Type), Func->WrappedCall->Name, ArgSize);
} else {
g_call (FuncTypeOf (Expr->Type), (const char*) Expr->Name, ArgSize);
}
}
/* The function result is an rvalue in the primary register */
ED_FinalizeRValLoad (Expr);
ReturnType = GetFuncReturn (Expr->Type);
/* Handle struct/union specially */
if (IsClassStruct (ReturnType)) {
/* If there is no replacement type, then it is just the address */
if (ReturnType == GetStructReplacementType (ReturnType)) {
/* Dereference it */
ED_IndExpr (Expr);
ED_MarkExprAsRVal (Expr);
}
}
Expr->Type = ReturnType;
/* We assume all function calls had side effects */
Expr->Flags |= E_SIDE_EFFECTS;
}
static void Primary (ExprDesc* E)
/* This is the lowest level of the expression parser. */
{
SymEntry* Sym;
unsigned Flags = E->Flags & E_MASK_KEEP_MAKE;
switch (CurTok.Tok) {
case TOK_LPAREN:
/* Process parenthesized subexpression by calling the whole parser
** recursively.
*/
NextToken ();
hie0 (E);
ConsumeRParen ();
break;
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_CODE | E_ADDRESS_OF;
E->Name = Entry->V.L.Label;
E->Type = type_void_p;
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_ESUTYPEMASK) {
/* 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;
break;
}
/* 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_NONE | E_RTYPE_RVAL;
E->IVal = Sym->V.ConstVal;
} 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_FUNC) == SC_FUNC) {
/* Function */
E->Flags = E_LOC_GLOBAL | E_RTYPE_LVAL;
E->Name = (uintptr_t) Sym->Name;
} 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 | SC_DECL)) {
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 an rvalue, and a function is an
** 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_AddrExpr (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 undeclared 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 undeclared function '%s'", Ident);
} else {
Warning ("Call to undeclared 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 */
if ((Flags & E_EVAL_UNEVAL) != E_EVAL_UNEVAL) {
E->V.LVal = UseLiteral (CurTok.SVal);
/* Translate into target charset */
TranslateLiteral (E->V.LVal);
} else {
E->V.LVal = CurTok.SVal;
}
E->Type = GetCharArrayType (GetLiteralSize (CurTok.SVal));
E->Flags = E_LOC_LITERAL | E_RTYPE_RVAL | E_ADDRESS_OF;
E->IVal = 0;
E->Name = GetLiteralLabel (CurTok.SVal);
NextToken ();
break;
case TOK_ICONST:
case TOK_CCONST:
case TOK_WCCONST:
/* Character and integer constants */
E->IVal = CurTok.IVal;
E->Flags = E_LOC_NONE | E_RTYPE_RVAL;
E->Type = CurTok.Type;
NextToken ();
break;
case TOK_FCONST:
/* Floating point constant */
E->V.FVal = CurTok.FVal;
E->Flags = E_LOC_NONE | E_RTYPE_RVAL;
E->Type = CurTok.Type;
NextToken ();
break;
case TOK_ASM:
/* ASM statement */
AsmStatement ();
E->Flags = E_RTYPE_RVAL | E_EVAL_MAYBE_UNUSED | E_SIDE_EFFECTS;
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.
*/
if (CurTok.Tok == TOK_LCURLY) {
/* Statement block */
NextToken ();
Error ("Expression expected");
E->Flags |= E_EVAL_MAYBE_UNUSED;
hie0 (E);
if (CurTok.Tok == TOK_RCURLY) {
NextToken ();
}
break;
} else {
/* Let's see if this is a C99-style declaration */
DeclSpec Spec;
InitDeclSpec (&Spec);
ParseDeclSpec (&Spec, -1, T_QUAL_NONE);
if (Spec.Type->C != T_END) {
Error ("Mixed declarations and code are not supported in cc65");
while (CurTok.Tok != TOK_SEMI) {
Declaration Decl;
/* Parse one declaration */
ParseDecl (&Spec, &Decl, DM_ACCEPT_IDENT);
if (CurTok.Tok == TOK_ASSIGN) {
NextToken ();
ParseInit (Decl.Type);
}
if (CurTok.Tok == TOK_COMMA) {
NextToken ();
} else {
break;
}
}
} else {
Error ("Expression expected");
E->Flags |= E_EVAL_MAYBE_UNUSED;
NextToken ();
}
}
ED_MakeConstAbsInt (E, 1);
break;
}
E->Flags |= Flags;
}
static void StructRef (ExprDesc* Expr)
/* Process struct/union field after . or ->. */
{
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/union field */
NextToken ();
const SymEntry Field = FindStructField (Expr->Type, CurTok.Ident);
if (Field.Type == 0) {
Error ("No field named '%s' found in '%s'", CurTok.Ident, GetFullTypeName (Expr->Type));
/* Make the expression an integer at address zero */
ED_MakeConstAbs (Expr, 0, type_int);
return;
}
if (IsTypePtr (Expr->Type)) {
/* pointer->field */
if (!ED_IsQuasiConst (Expr) && !ED_IsLocPrimary (Expr)) {
/* If we have a non-const struct/union pointer that is not in the
** primary yet, load its content now to get the base address.
*/
LoadExpr (CF_NONE, Expr);
ED_FinalizeRValLoad (Expr);
}
/* Dereference the address expression */
ED_IndExpr (Expr);
} else if (ED_IsRVal (Expr) &&
ED_IsLocPrimary (Expr) &&
Expr->Type == GetStructReplacementType (Expr->Type)) {
/* A struct/union is usually an lvalue. If not, it is a struct/union
** passed in the primary register, which is usually the result returned
** from a function. However, it is possible that this rvalue is the
** result of certain kind of operations on an lvalue such as assignment,
** and there are no reasons to disallow such use cases. So we just rely
** on the check upon function returns to catch the unsupported cases and
** dereference the rvalue address of the struct/union here all the time.
*/
ED_IndExpr (Expr);
} else if (!ED_IsLocQuasiConst (Expr) && !ED_IsLocPrimaryOrExpr (Expr)) {
/* Load the base address into the primary (and use it as a reference
** later) if it's not quasi-const or in the primary already.
*/
LoadExpr (CF_NONE, Expr);
}
/* Clear the tested flag set during loading */
ED_MarkAsUntested (Expr);
/* The type is the field type plus any qualifiers from the struct/union */
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;
}
if (ED_IsRVal (Expr) && ED_IsLocPrimary (Expr) && !IsTypePtr (Expr->Type)) {
unsigned Flags = 0;
unsigned BitOffs;
/* Get the size of the type */
unsigned StructSize = SizeOf (Expr->Type);
unsigned FieldSize = SizeOf (Field.Type);
/* Safety check */
CHECK (Field.V.Offs + FieldSize <= StructSize);
/* The type of the operation depends on the type of the struct/union */
switch (StructSize) {
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 '%s' size: %u", GetFullTypeName (Expr->Type), StructSize);
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.Type->A.B.Offs;
g_asr (Flags, BitOffs);
/* Mask the value. This is unnecessary if the shift executed above
** moved only zeroes into the value.
*/
if (BitOffs + Field.Type->A.B.Width != FieldSize * CHAR_BITS) {
g_and (CF_INT | CF_UNSIGNED | CF_CONST,
(0x0001U << Field.Type->A.B.Width) - 1U);
}
} else {
g_asr (Flags, BitOffs);
}
/* Use the new type */
Expr->Type = FinalType;
} else {
/* Set the struct/union field offset */
Expr->IVal += Field.V.Offs;
/* Use the new type */
Expr->Type = FinalType;
/* The usual rules for variables with respect to the reference types
** apply to struct/union fields as well: If a field is an array, it is
** virtually an rvalue address, otherwise it's an lvalue reference. (A
** function would also be an rvalue address, but a struct/union cannot
** contain functions).
*/
if (IsTypeArray (Expr->Type)) {
ED_AddrExpr (Expr);
}
}
}
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_INC || CurTok.Tok == TOK_DEC ||
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 */
parseadd (Expr, 1);
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 or union 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 or union pointer expected");
}
StructRef (Expr);
break;
case TOK_INC:
PostInc (Expr);
break;
case TOK_DEC:
PostDec (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 addressed variable */
g_putstatic (Flags, Expr->IVal, 0);
break;
case E_LOC_GLOBAL:
case E_LOC_STATIC:
case E_LOC_REGISTER:
case E_LOC_LITERAL:
case E_LOC_CODE:
/* Global variabl, static variable, register variable, pooled
** literal or code label location.
*/
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 referenced in the primary register */
g_putind (Flags, Expr->IVal);
break;
case E_LOC_NONE:
/* We may get here as a result of previous compiler errors */
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 */
{
/* 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");
}
/* Do the increment */
DoInc (Expr, OA_NEED_NEW);
/* Result is an expression, no reference */
ED_FinalizeRValLoad (Expr);
/* Expression has had side effects */
Expr->Flags |= E_SIDE_EFFECTS;
}
static void PreDec (ExprDesc* Expr)
/* Handle the predecrement operators */
{
/* 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");
}
/* Do the decrement */
DoDec (Expr, OA_NEED_NEW);
/* Result is an expression, no reference */
ED_FinalizeRValLoad (Expr);
/* Expression has had side effects */
Expr->Flags |= E_SIDE_EFFECTS;
}
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);
/* We are allowed by the C standard to defer the inc operation until after
** the expression is used, so that we don't need to save and reload
** the original value.
*/
/* Emit smaller code if a char variable is at a constant location */
if ((Flags & CF_TYPEMASK) == CF_CHAR && ED_IsLocConst (Expr) && !IsTypeBitField (Expr->Type)) {
LoadExpr (CF_NONE, Expr);
AddCodeLine ("inc %s", ED_GetLabelName (Expr, 0));
/* Expression has had side effects */
Expr->Flags |= E_SIDE_EFFECTS;
} else {
if (ED_IsLocPrimaryOrExpr (Expr)) {
/* Do the increment */
DoInc (Expr, OA_NEED_OLD);
/* Expression has had side effects */
Expr->Flags |= E_SIDE_EFFECTS;
} else {
/* Defer the increment until after the value of this expression is used */
DeferInc (Expr);
/* Just return */
return;
}
}
/* The result is always an expression, no reference */
ED_FinalizeRValLoad (Expr);
}
static void PostDec (ExprDesc* Expr)
/* Handle the postdecrement operator */
{
unsigned Flags;
NextToken ();
/* The expression to decrement 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_TYPEMASK) == CF_CHAR && ED_IsLocConst (Expr) && !IsTypeBitField (Expr->Type)) {
LoadExpr (CF_NONE, Expr);
AddCodeLine ("dec %s", ED_GetLabelName (Expr, 0));
/* Expression has had side effects */
Expr->Flags |= E_SIDE_EFFECTS;
} else {
if (ED_IsLocPrimaryOrExpr (Expr)) {
/* Do the decrement */
DoDec (Expr, OA_NEED_OLD);
/* Expression has had side effects */
Expr->Flags |= E_SIDE_EFFECTS;
} else {
/* Defer the decrement until after the value of this expression is used */
DeferDec (Expr);
/* Just return */
return;
}
}
/* The result is always an expression, no reference */
ED_FinalizeRValLoad (Expr);
}
static void UnaryOp (ExprDesc* Expr)
/* Handle unary -/+ and ~ */
{
/* 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 numeric expression */
if (ED_IsConstAbs (Expr)) {
/* Value is numeric */
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);
}
/* Adjust the type of the expression */
Expr->Type = IntPromotion (Expr->Type);
/* Limit the calculated value to the range of its type */
LimitExprValue (Expr);
} else {
unsigned Flags;
/* Value is not constant */
LoadExpr (CF_NONE, Expr);
/* Adjust the type of the expression */
Expr->Type = IntPromotion (Expr->Type);
TypeConversion (Expr, Expr->Type);
/* Get code generation flags */
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 an rvalue in the primary */
ED_FinalizeRValLoad (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 ();
BoolExpr (hie10, Expr);
if (ED_IsConstAbs (Expr)) {
/* Constant numeric expression */
Expr->IVal = !Expr->IVal;
} else if (ED_IsAddrExpr (Expr)) {
/* Address != NULL, so !Address == 0 */
ED_MakeConstBool (Expr, 0);
} else {
/* Not constant, load into the primary */
LoadExpr (CF_NONE, Expr);
g_bneg (TypeOf (Expr->Type));
ED_FinalizeRValLoad (Expr);
ED_TestDone (Expr); /* bneg will set cc */
}
/* The result type is always boolean */
Expr->Type = type_bool;
break;
case TOK_STAR:
NextToken ();
ExprWithCheck (hie10, 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_MarkExprAsRVal (Expr);
} else {
if (!ED_IsQuasiConstAddr (Expr)) {
/* Not a constant address, load the pointer into the primary
** and make it a calculated value.
*/
LoadExpr (CF_NONE, Expr);
ED_FinalizeRValLoad (Expr);
}
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 reference */
ED_IndExpr (Expr);
}
}
break;
case TOK_AND:
NextToken ();
ExprWithCheck (hie10, Expr);
/* The & operator may be applied to any lvalue, and it may be
** applied to functions and arrays, even if they're not lvalues.
*/
if (!IsTypeFunc (Expr->Type) && !IsTypeArray (Expr->Type)) {
if (ED_IsRVal (Expr)) {
Error ("Illegal address");
/* Continue anyway, just to avoid further warnings */
}
if (IsTypeBitField (Expr->Type)) {
Error ("Cannot take address of bit-field");
/* Continue anyway, just to avoid further warnings */
Expr->Type = GetUnderlyingType (Expr->Type);
}
/* The & operator yields an rvalue address */
ED_AddrExpr (Expr);
}
Expr->Type = AddressOf (Expr->Type);
break;
case TOK_SIZEOF:
NextToken ();
if (TypeSpecAhead ()) {
Type T[MAXTYPELEN];
NextToken ();
Size = ExprCheckedSizeOf (ParseType (T));
ConsumeRParen ();
} else {
/* Remember the output queue pointer */
CodeMark Mark;
GetCodePos (&Mark);
/* The expression shall be unevaluated */
ExprDesc Uneval;
ED_Init (&Uneval);
ED_MarkForUneval (&Uneval);
hie10 (&Uneval);
if (IsTypeBitField (Uneval.Type)) {
Error ("Cannot apply 'sizeof' to bit-field");
Size = 0;
} else {
/* Calculate the size */
Size = ExprCheckedSizeOf (Uneval.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);
}
break;
}
}
static void hie_internal (const GenDesc* Ops, /* List of generators */
ExprDesc* Expr,
void (*hienext) (ExprDesc*),
int* UsedGen)
/* Helper function */
{
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) {
ExprDesc Expr2;
ED_Init (&Expr2);
Expr2.Flags |= Expr->Flags & E_MASK_KEEP_SUBEXPR;
/* 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 = ArithmeticConvert (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);
}
}
/* Limit the calculated value to the range of its type */
LimitExprValue (Expr);
} else if (lconst && (Gen->Flags & GEN_COMM) && !rconst) {
/* If the LHS constant is an int that fits into an unsigned char, change the
** codegen type to unsigned char. If the RHS is also an unsigned char, then
** g_typeadjust will return unsigned int (instead of int, which would be
** returned without this modification). This allows more efficient operations,
** but does not affect correctness for the same reasons explained in g_typeadjust.
*/
if (ltype == CF_INT && Expr->IVal >= 0 && Expr->IVal < 256) {
ltype = CF_CHAR | CF_UNSIGNED;
}
/* 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_PRIMARY; /* Value is in register */
}
/* Determine the type of the operation result. */
type |= g_typeadjust (ltype, rtype);
Expr->Type = ArithmeticConvert (Expr->Type, Expr2.Type);
/* Generate code */
Gen->Func (type, Expr->IVal);
/* We have an rvalue in the primary now */
ED_FinalizeRValLoad (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) {
/* As above, but for the RHS. */
if (rtype == CF_INT && Expr2.IVal >= 0 && Expr2.IVal < 256) {
rtype = CF_CHAR | CF_UNSIGNED;
}
/* 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_PRIMARY; /* Value is in register */
}
}
/* Determine the type of the operation result. */
type |= g_typeadjust (ltype, rtype);
Expr->Type = ArithmeticConvert (Expr->Type, Expr2.Type);
/* Generate code */
Gen->Func (type, Expr2.IVal);
/* We have an rvalue in the primary now */
ED_FinalizeRValLoad (Expr);
}
/* Propagate viral flags */
ED_PropagateFrom (Expr, &Expr2);
}
}
static void hie_compare (const GenDesc* Ops, /* List of generators */
ExprDesc* Expr,
void (*hienext) (ExprDesc*))
/* Helper function for the compare operators */
{
CodeMark Mark1;
CodeMark Mark2;
const GenDesc* Gen;
token_t Tok; /* The operator token */
unsigned ltype;
int rconst; /* Operand is a constant */
ExprWithCheck (hienext, Expr);
while ((Gen = FindGen (CurTok.Tok, Ops)) != 0) {
ExprDesc Expr2;
ED_Init (&Expr2);
Expr2.Flags |= Expr->Flags & E_MASK_KEEP_SUBEXPR;
/* 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 the address of the function */
if (IsTypeFunc (Expr->Type)) {
Expr->Type = AddressOf (Expr->Type);
}
/* Get the lhs on stack */
GetCodePos (&Mark1);
ltype = TypeOf (Expr->Type);
if (ED_IsConstAbs (Expr)) {
/* Numeric constant value */
GetCodePos (&Mark2);
g_push (ltype | CF_CONST, Expr->IVal);
} else {
/* Value not numeric 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 the address of the function */
if (IsTypeFunc (Expr2.Type)) {
Expr2.Type = AddressOf (Expr2.Type);
}
/* Check for a numeric constant expression */
rconst = (ED_IsConstAbs (&Expr2) && ED_CodeRangeIsEmpty (&Expr2));
if (!rconst) {
/* Not numeric constant, load into the primary */
LoadExpr (CF_NONE, &Expr2);
}
/* Check if operands have allowed types for this operation */
if (!IsRelationType (Expr->Type) || !IsRelationType (Expr2.Type)) {
/* Output only one message even if both sides are wrong */
TypeCompatibilityDiagnostic (Expr->Type, Expr2.Type, 1,
"Comparing types '%s' with '%s' is invalid");
/* Avoid further errors */
ED_MakeConstAbsInt (Expr, 0);
ED_MakeConstAbsInt (&Expr2, 0);
}
/* Some operations aren't allowed on function pointers */
if ((Gen->Flags & GEN_NOFUNC) != 0) {
if ((IsTypeFuncPtr (Expr->Type) || IsTypeFuncPtr (Expr2.Type))) {
/* Output only one message even if both sides are wrong */
Error ("Cannot use function pointers in this relation operation");
/* 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) && !ED_IsNullPtr (Expr)) {
if (IsClassPtr (Expr2.Type)) {
TypeCompatibilityDiagnostic (Expr->Type, PtrConversion (Expr2.Type), 0,
"Comparing integer '%s' with pointer '%s'");
} else {
TypeCompatibilityDiagnostic (Expr->Type, Expr2.Type, 1,
"Comparing types '%s' with '%s' is invalid");
}
}
} else if (IsClassPtr (Expr->Type)) {
if (IsClassPtr (Expr2.Type)) {
/* Pointers are allowed in comparison */
if (TypeCmp (Expr->Type, Expr2.Type).C < TC_VOID_PTR) {
/* Warn about distinct pointer types */
TypeCompatibilityDiagnostic (PtrConversion (Expr->Type), PtrConversion (Expr2.Type), 0,
"Distinct pointer types comparing '%s' with '%s'");
}
} else if (!ED_IsNullPtr (&Expr2)) {
if (IsClassInt (Expr2.Type)) {
TypeCompatibilityDiagnostic (PtrConversion (Expr->Type), Expr2.Type, 0,
"Comparing pointer type '%s' with integer type '%s'");
} else {
TypeCompatibilityDiagnostic (Expr->Type, Expr2.Type, 1,
"Comparing types '%s' with '%s' is invalid");
}
}
}
/* Check for numeric constant operands */
if ((ED_IsAddrExpr (Expr) && ED_IsNullPtr (&Expr2)) ||
(ED_IsNullPtr (Expr) && ED_IsAddrExpr (&Expr2))) {
/* Object addresses are inequal to null pointer */
Expr->IVal = (Tok != TOK_EQ);
if (ED_IsNullPtr (&Expr2)) {
if (Tok == TOK_LT || Tok == TOK_LE) {
Expr->IVal = 0;
}
} else {
if (Tok == TOK_GT || Tok == TOK_GE) {
Expr->IVal = 0;
}
}
/* Get rid of unwanted flags */
ED_MakeConstBool (Expr, Expr->IVal);
/* The result is constant, this is suspicious when not in
** preprocessor mode.
*/
WarnConstCompareResult (Expr);
if (ED_CodeRangeIsEmpty (&Expr2)) {
/* Both operands are static, remove the load code */
RemoveCode (&Mark1);
} else {
/* Drop pushed lhs */
g_drop (sizeofarg (ltype));
pop (ltype);
}
} else if (ED_IsAddrExpr (Expr) &&
ED_IsAddrExpr (&Expr2) &&
Expr->Sym == Expr2.Sym) {
/* Evaluate the result for static addresses */
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);
}
/* Get rid of unwanted flags */
ED_MakeConstBool (Expr, Expr->IVal);
/* If the result is constant, this is suspicious when not in
** preprocessor mode.
*/
WarnConstCompareResult (Expr);
if (ED_CodeRangeIsEmpty (&Expr2)) {
/* Both operands are static, remove the load code */
RemoveCode (&Mark1);
} else {
/* Drop pushed lhs */
g_drop (sizeofarg (ltype));
pop (ltype);
}
} else if (ED_IsConstAbs (Expr) && rconst) {
/* Both operands are numeric 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);
}
}
/* Get rid of unwanted flags */
ED_MakeConstBool (Expr, Expr->IVal);
/* If the result is constant, this is suspicious when not in
** preprocessor mode.
*/
WarnConstCompareResult (Expr);
} else {
/* Determine the signedness of the operands */
int LeftSigned = IsSignSigned (Expr->Type);
int RightSigned = IsSignSigned (Expr2.Type);
int CmpSigned = IsClassInt (Expr->Type) && IsClassInt (Expr2.Type) &&
IsSignSigned (ArithmeticConvert (Expr->Type, 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_PRIMARY; /* Value is in register */
}
}
/* Determine the type of the operation. */
if (IsTypeChar (Expr->Type) && rconst && (!LeftSigned || RightSigned)) {
/* 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;
}
/* 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_MakeConstBool (Expr, 0);
WarnConstCompareResult (Expr);
goto Done;
}
break;
case TOK_NE:
if (Expr2.IVal < LeftMin || Expr2.IVal > LeftMax) {
ED_MakeConstBool (Expr, 1);
WarnConstCompareResult (Expr);
goto Done;
}
break;
case TOK_LT:
if (Expr2.IVal <= LeftMin || Expr2.IVal > LeftMax) {
ED_MakeConstBool (Expr, Expr2.IVal > LeftMax);
WarnConstCompareResult (Expr);
goto Done;
}
break;
case TOK_LE:
if (Expr2.IVal < LeftMin || Expr2.IVal >= LeftMax) {
ED_MakeConstBool (Expr, Expr2.IVal >= LeftMax);
WarnConstCompareResult (Expr);
goto Done;
}
break;
case TOK_GE:
if (Expr2.IVal <= LeftMin || Expr2.IVal > LeftMax) {
ED_MakeConstBool (Expr, Expr2.IVal <= LeftMin);
WarnConstCompareResult (Expr);
goto Done;
}
break;
case TOK_GT:
if (Expr2.IVal < LeftMin || Expr2.IVal >= LeftMax) {
ED_MakeConstBool (Expr, Expr2.IVal < LeftMin);
WarnConstCompareResult (Expr);
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 || !RightSigned) {
CmpSigned = 0;
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 || !RightSigned) {
CmpSigned = 0;
flags |= CF_UNSIGNED;
}
} else {
unsigned rtype = TypeOf (Expr2.Type) | (flags & CF_CONST);
flags |= g_typeadjust (ltype, rtype);
}
/* If the comparison is made as unsigned types and the right is a
** constant, we may be able to change the compares to something more
** effective.
*/
if (!CmpSigned && 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_FinalizeRValLoad (Expr);
/* Condition codes are set */
ED_TestDone (Expr);
}
/* Result type is always boolean */
Done: Expr->Type = type_bool;
/* Propagate viral flags */
ED_PropagateFrom (Expr, &Expr2);
}
}
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, int DoArrayRef)
/* Parse an expression with the binary plus or subscript operator. Expr contains
** the unprocessed left hand side of the expression and will contain the result
** of the expression on return. If DoArrayRef is zero, this evaluates the binary
** plus operation. Otherwise, this evaluates the subscript operation.
*/
{
ExprDesc Expr2;
unsigned flags; /* Operation flags */
CodeMark Mark; /* Remember code position */
const Type* lhst; /* Type of left hand side */
const Type* rhst; /* Type of right hand side */
int lscale;
int rscale;
int AddDone; /* No need to generate runtime code */
ED_Init (&Expr2);
Expr2.Flags |= Expr->Flags & E_MASK_KEEP_SUBEXPR;
/* Skip the PLUS or opening bracket token */
NextToken ();
/* Get the left hand side type, initialize operation flags */
lhst = Expr->Type;
flags = 0;
lscale = rscale = 1;
AddDone = 0;
/* We can only do constant expressions for:
** - integer addition:
** - numeric + numeric
** - (integer)(base + offset) + numeric
** - numeric + (integer)(base + offset)
** - pointer offset:
** - (pointer)numeric + numeric * scale
** - (base + offset) + numeric * scale
** - (pointer)numeric + (integer)(base + offset) * 1
** - numeric * scale + (pointer)numeric
** - numeric * scale + (base + offset)
** - (integer)(base + offset) * 1 + (pointer)numeric
*/
if (ED_IsQuasiConst (Expr)) {
/* The left hand side is a constant of some sort. Good. Get rhs */
ExprWithCheck (DoArrayRef ? hie0 : hie9, &Expr2);
/* Right hand side is constant. Get the rhs type */
rhst = Expr2.Type;
if (ED_IsQuasiConst (&Expr2)) {
/* Both expressions are constants. Check for pointer arithmetic */
if (IsClassPtr (lhst) && IsClassInt (rhst)) {
/* Left is pointer, right is int, must scale rhs */
rscale = 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 */
lscale = CheckedPSizeOf (rhst);
/* Operate on pointers, result type is a pointer */
flags = CF_PTR;
} else if (!DoArrayRef && IsClassInt (lhst) && IsClassInt (rhst)) {
/* Integer addition */
flags = CF_INT;
} else {
/* OOPS */
AddDone = -1;
/* Avoid further errors */
ED_MakeConstAbsInt (Expr, 0);
}
if (!AddDone) {
/* Do constant calculation if we can */
if (ED_IsAbs (&Expr2) &&
(ED_IsAbs (Expr) || lscale == 1)) {
if (IsClassInt (lhst) && IsClassInt (rhst)) {
Expr->Type = ArithmeticConvert (Expr->Type, Expr2.Type);
}
Expr->IVal = Expr->IVal * lscale + Expr2.IVal * rscale;
AddDone = 1;
} else if (ED_IsAbs (Expr) &&
(ED_IsAbs (&Expr2) || rscale == 1)) {
if (IsClassInt (lhst) && IsClassInt (rhst)) {
Expr2.Type = ArithmeticConvert (Expr2.Type, Expr->Type);
}
Expr2.IVal = Expr->IVal * lscale + Expr2.IVal * rscale;
/* Adjust the flags */
Expr2.Flags |= Expr->Flags & ~E_MASK_KEEP_SUBEXPR;
/* Get the symbol and the name */
*Expr = Expr2;
AddDone = 1;
}
}
if (AddDone) {
/* Adjust the result for addition */
if (!DoArrayRef) {
if (IsClassPtr (lhst)) {
/* Result type is a pointer */
Expr->Type = lhst;
} else if (IsClassPtr (rhst)) {
/* Result type is a pointer */
Expr->Type = rhst;
} else {
/* Limit the calculated value to the range of its type */
LimitExprValue (Expr);
}
/* The result is always an rvalue */
ED_MarkExprAsRVal (Expr);
}
} else {
/* Decide the order */
if (!ED_IsAbs (&Expr2) && rscale > 1) {
/* Rhs needs scaling but is not numeric. Load it. */
LoadExpr (CF_NONE, &Expr2);
/* Scale rhs */
g_scale (CF_INT, rscale);
/* Generate the code for the add */
if (ED_IsAbs (Expr)) {
/* Numeric constant */
g_inc (flags | CF_CONST, Expr->IVal);
} else if (ED_IsLocStack (Expr)) {
/* Local stack address */
g_addaddr_local (flags, Expr->IVal);
} else {
/* Static address */
g_addaddr_static (flags | GlobalModeFlags (Expr), Expr->Name, Expr->IVal);
}
} else {
/* Lhs is not numeric. Load it. */
LoadExpr (CF_NONE, Expr);
/* Scale lhs if necessary */
if (lscale != 1) {
g_scale (CF_INT, lscale);
}
/* Generate the code for the add */
if (ED_IsAbs (&Expr2)) {
/* Numeric constant */
g_inc (flags | CF_CONST, Expr2.IVal);
} else if (ED_IsLocStack (&Expr2)) {
/* Local stack address */
g_addaddr_local (flags, Expr2.IVal);
} else {
/* Static address */
g_addaddr_static (flags | GlobalModeFlags (&Expr2), Expr2.Name, Expr2.IVal);
}
}
/* Result is an rvalue in primary register */
ED_FinalizeRValLoad (Expr);
}
} else {
/* lhs is constant and rhs is not constant. Load rhs into the
** primary.
*/
GetCodePos (&Mark);
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.
*/
/* Setup flags */
if (ED_IsAbs (Expr)) {
/* A numerical constant */
flags |= CF_CONST;
} else {
/* Constant address label */
flags |= GlobalModeFlags (Expr);
}
/* Check for pointer arithmetic */
if (IsClassPtr (lhst) && IsClassInt (rhst)) {
/* Left is pointer, right is int, must scale rhs */
rscale = CheckedPSizeOf (lhst);
g_scale (CF_INT, rscale);
/* 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. */
lscale = CheckedPSizeOf (rhst);
/* Operate on pointers, result type is a pointer */
flags |= CF_PTR;
Expr->Type = Expr2.Type;
} else if (!DoArrayRef && IsClassInt (lhst) && IsClassInt (rhst)) {
/* Integer addition */
flags |= typeadjust (Expr, &Expr2, 1);
} else {
/* OOPS */
AddDone = -1;
}
/* Generate the code for the add */
if (!AddDone) {
if (ED_IsAbs (Expr) &&
Expr->IVal >= 0 &&
Expr->IVal * lscale < 256) {
/* Numeric constant */
g_inc (flags, Expr->IVal * lscale);
AddDone = 1;
}
}
if (!AddDone) {
if (ED_IsLocQuasiConst (&Expr2) &&
rscale == 1 &&
CheckedSizeOf (rhst) == SIZEOF_CHAR) {
/* Change the order back */
RemoveCode (&Mark);
/* Load lhs */
LoadExpr (CF_NONE, Expr);
/* Use new flags */
flags = CF_CHAR | GlobalModeFlags (&Expr2);
/* Add the variable */
if (ED_IsLocStack (&Expr2)) {
g_addlocal (flags, Expr2.IVal);
} else {
g_addstatic (flags, Expr2.Name, Expr2.IVal);
}
} else if (ED_IsAbs (Expr)) {
/* Numeric constant */
g_inc (flags, Expr->IVal * lscale);
} else if (lscale == 1) {
if (ED_IsLocStack (Expr)) {
/* Constant address */
g_addaddr_local (flags, Expr->IVal);
} else {
g_addaddr_static (flags, Expr->Name, Expr->IVal);
}
} else {
/* 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.
*/
g_push (TypeOf (Expr2.Type), 0); /* rhs --> stack */
LoadExpr (CF_NONE, Expr);
g_scale (CF_PTR, lscale);
g_add (CF_PTR, 0);
}
}
/* Result is an rvalue in primary register */
ED_FinalizeRValLoad (Expr);
}
} else {
/* Left hand side is not constant. Get the value onto the stack. */
LoadExpr (CF_NONE, Expr); /* --> primary register */
GetCodePos (&Mark);
flags = TypeOf (Expr->Type); /* default codegen type */
g_push (flags, 0); /* --> stack */
/* Evaluate the rhs */
MarkedExprWithCheck (DoArrayRef ? hie0 : hie9, &Expr2);
/* Get the rhs type */
rhst = Expr2.Type;
/* Check for a constant rhs expression */
if (ED_IsConstAbs (&Expr2) && ED_CodeRangeIsEmpty (&Expr2)) {
/* Rhs is a numeric constant. Remove pushed lhs 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 (!DoArrayRef && IsClassInt (lhst) && IsClassInt (rhst)) {
/* Integer addition */
flags = typeadjust (Expr, &Expr2, 1);
} else {
/* OOPS */
AddDone = -1;
}
/* Generate code for the add */
g_inc (flags | CF_CONST, Expr2.IVal);
} else {
/* Lhs and rhs are not so "numeric constant". Check for pointer arithmetic. */
if (IsClassPtr (lhst) && IsClassInt (rhst)) {
/* Left is pointer, right is int, must scale rhs */
rscale = CheckedPSizeOf (lhst);
if (ED_IsAbs (&Expr2)) {
Expr2.IVal *= rscale;
/* Load rhs into the primary */
LoadExpr (CF_NONE, &Expr2);
} else {
/* Load rhs into the primary */
LoadExpr (CF_NONE, &Expr2);
g_scale (CF_INT, rscale);
}
/* 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 */
lscale = CheckedPSizeOf (rhst);
if (ED_CodeRangeIsEmpty (&Expr2)) {
RemoveCode (&Mark); /* Remove pushed value from stack */
g_scale (CF_INT, lscale);
g_push (CF_PTR, 0); /* --> stack */
LoadExpr (CF_NONE, &Expr2); /* Load rhs into primary register */
} else {
g_tosint (TypeOf (lhst)); /* Make sure TOS is int */
LoadExpr (CF_NONE, &Expr2); /* Load rhs into primary register */
if (lscale != 1) {
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 (!DoArrayRef && IsClassInt (lhst) && IsClassInt (rhst)) {
/* Integer addition */
flags = typeadjust (Expr, &Expr2, 0);
/* Load rhs into the primary */
LoadExpr (CF_NONE, &Expr2);
} else {
/* OOPS */
AddDone = -1;
/* We can't just goto End as that would leave the stack unbalanced */
}
/* Generate code for the add (the & is a hack here) */
g_add (flags & ~CF_CONST, 0);
}
/* Result is an rvalue in primary register */
ED_FinalizeRValLoad (Expr);
}
/* Deal with array ref */
if (DoArrayRef) {
/* Check the types of array and subscript */
if (IsClassPtr (lhst)) {
if (!IsClassInt (rhst)) {
Error ("Array subscript is not an integer");
ED_MakeConstAbs (Expr, 0, GetCharArrayType (1));
}
} else if (IsClassInt (lhst)) {
if (!IsClassPtr (rhst)) {
Error ("Subscripted value is neither array nor pointer");
ED_MakeConstAbs (Expr, 0, GetCharArrayType (1));
}
} else {
Error ("Cannot subscript");
ED_MakeConstAbs (Expr, 0, GetCharArrayType (1));
}
/* The final result is usually an lvalue expression of element type
** referenced in the primary, unless it is once again an array. We can just
** assume the usual case first, and change it later if necessary.
*/
ED_IndExpr (Expr);
Expr->Type = Indirect (Expr->Type);
/* 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 virtually
** an rvalue address, otherwise it's an lvalue reference. (A function would
** also be an rvalue address, but an array cannot contain functions).
*/
if (IsTypeArray (Expr->Type)) {
ED_AddrExpr (Expr);
}
/* Consume the closing bracket */
ConsumeRBrack ();
} else {
if (AddDone < 0) {
Error ("Invalid operands for binary operator '+'");
} else {
/* Array and function types must be converted to pointer types */
Expr->Type = StdConversion (Expr->Type);
}
}
/* Condition code not set */
ED_MarkAsUntested (Expr);
/* Propagate viral flags */
ED_PropagateFrom (Expr, &Expr2);
}
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 */
const Type* lhst; /* Type of left hand side */
const 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 pointer arithmetics */
int SubDone; /* No need to generate runtime code */
ED_Init (&Expr2);
Expr2.Flags |= Expr->Flags & E_MASK_KEEP_SUBEXPR;
/* 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;
flags = CF_INT; /* Default result type */
rscale = 1; /* Scale by 1, that is, don't scale */
SubDone = 0; /* Generate runtime code by default */
/* 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;
}
/* Get the rhs type */
rhst = Expr2.Type;
if (IsClassPtr (lhst)) {
/* We'll have to scale the result */
rscale = PSizeOf (lhst);
/* We cannot scale by 0-size or unknown-size */
if (rscale == 0 && (IsClassPtr (rhst) || IsClassInt (rhst))) {
TypeCompatibilityDiagnostic (lhst, rhst,
1, "Invalid pointer types in subtraction: '%s' and '%s'");
/* Avoid further errors */
rscale = 1;
}
/* Generate code for pointer subtraction */
flags = CF_PTR;
}
/* We can only do constant expressions for:
** - integer subtraction:
** - numeric - numeric
** - (integer)(base + offset) - numeric
** - (integer)(same_base + offset) - (integer)(same_base + offset)
** - pointer offset:
** - (pointer)numeric - numeric * scale
** - (base + offset) - numeric * scale
** - (same_base + offset) - (integer)(same_base + offset) * 1
** - pointer diff:
** - (numeric - numeric) / scale
** - ((same_base + offset) - (same_base + offset)) / scale
** - ((base + offset) - (pointer)numeric) / 1
*/
if (IsClassPtr (lhst) && IsClassPtr (rhst)) {
/* Pointer Diff. We've got the scale factor and flags above */
typecmp_t Cmp = TypeCmp (lhst, rhst);
if (Cmp.C < TC_STRICT_COMPATIBLE) {
TypeCompatibilityDiagnostic (lhst, rhst,
1, "Incompatible pointer types in subtraction: '%s' and '%s'");
}
/* Operate on pointers, result type is an integer */
Expr->Type = type_int;
/* Check for a constant rhs expression */
if (ED_IsQuasiConst (&Expr2) && ED_CodeRangeIsEmpty (&Expr2)) {
/* The right hand side is constant. Check left hand side. */
if (ED_IsQuasiConst (Expr)) {
/* We can't do all 'ptr1 - ptr2' constantly at the moment */
if (Expr->Sym == Expr2.Sym) {
Expr->IVal = (Expr->IVal - Expr2.IVal) / rscale;
/* Get rid of unneeded flags etc. */
ED_MakeConstAbsInt (Expr, Expr->IVal);
/* No runtime code */
SubDone = 1;
} else if (rscale == 1 && ED_IsConstAbs (&Expr2)) {
Expr->IVal = (Expr->IVal - Expr2.IVal) / rscale;
/* No runtime code */
SubDone = 1;
}
}
}
if (!SubDone) {
/* We'll do runtime code */
if (ED_IsConstAbs (&Expr2) && ED_CodeRangeIsEmpty (&Expr2)) {
/* Remove pushed value from stack */
RemoveCode (&Mark2);
/* Do the subtraction */
g_dec (CF_INT | CF_CONST, Expr2.IVal);
} else {
/* load into the primary */
LoadExpr (CF_NONE, &Expr2);
/* Generate code for the sub */
g_sub (CF_INT, 0);
}
/* We must scale the result */
if (rscale != 1) {
g_scale (CF_INT, -rscale);
}
/* Result is an rvalue in the primary register */
ED_FinalizeRValLoad (Expr);
} else {
/* Remove pushed value from stack */
RemoveCode (&Mark1);
/* The result is always an rvalue */
ED_MarkExprAsRVal (Expr);
}
} else if (ED_IsQuasiConst (&Expr2) && ED_CodeRangeIsEmpty (&Expr2)) {
/* Right hand side is constant. Check left hand side. */
if (ED_IsQuasiConst (Expr)) {
/* Both sides are constant. Check for pointer arithmetic */
if (IsClassPtr (lhst) && IsClassInt (rhst)) {
/* Pointer subtraction. We've got the scale factor and flags above */
} else if (IsClassInt (lhst) && IsClassInt (rhst)) {
/* Integer subtraction. We'll adjust the types later */
} else {
/* OOPS */
Error ("Invalid operands for binary operator '-'");
}
/* We can't make all subtraction expressions constant */
if (ED_IsConstAbs (&Expr2)) {
Expr->IVal -= Expr2.IVal * rscale;
/* No runtime code */
SubDone = 1;
} else if (rscale == 1 && Expr->Sym == Expr2.Sym) {
/* The result is the diff of the offsets */
Expr->IVal -= Expr2.IVal;
/* Get rid of unneeded bases and flags etc. */
ED_MakeConstAbs (Expr, Expr->IVal, Expr->Type);
/* No runtime code */
SubDone = 1;
}
if (SubDone) {
/* Remove loaded and pushed value from stack */
RemoveCode (&Mark1);
if (IsClassInt (lhst)) {
/* Just adjust the result type */
Expr->Type = ArithmeticConvert (Expr->Type, Expr2.Type);
/* And limit the calculated value to the range of it */
LimitExprValue (Expr);
}
/* The result is always an rvalue */
ED_MarkExprAsRVal (Expr);
} else {
if (ED_IsConstAbs (&Expr2)) {
/* Remove pushed value from stack */
RemoveCode (&Mark2);
if (IsClassInt (lhst)) {
/* Adjust the types */
flags = typeadjust (Expr, &Expr2, 1);
}
/* Do the subtraction */
g_dec (flags | CF_CONST, Expr2.IVal * rscale);
} else {
if (IsClassInt (lhst)) {
/* Adjust the types */
flags = typeadjust (Expr, &Expr2, 0);
}
/* Load rhs into the primary */
LoadExpr (CF_NONE, &Expr2);
g_scale (TypeOf (rhst), rscale);
/* Generate code for the sub (the & is a hack here) */
g_sub (flags & ~CF_CONST, 0);
}
/* Result is an rvalue in the primary register */
ED_FinalizeRValLoad (Expr);
}
} else {
/* Left hand side is not constant, right hand side is */
if (IsClassPtr (lhst) && IsClassInt (rhst)) {
/* Pointer subtraction. We've got the scale factor and flags above */
} else if (IsClassInt (lhst) && IsClassInt (rhst)) {
/* Integer subtraction. We'll adjust the types later */
} else {
/* OOPS */
Error ("Invalid operands for binary operator '-'");
flags = CF_INT;
}
if (ED_IsConstAbs (&Expr2)) {
/* Remove pushed value from stack */
RemoveCode (&Mark2);
if (IsClassInt (lhst)) {
/* Adjust the types */
flags = typeadjust (Expr, &Expr2, 1);
}
/* Do the subtraction */
g_dec (flags | CF_CONST, Expr2.IVal * rscale);
} else {
if (IsClassInt (lhst)) {
/* Adjust the types */
flags = typeadjust (Expr, &Expr2, 0);
}
/* Load rhs into the primary */
LoadExpr (CF_NONE, &Expr2);
g_scale (TypeOf (rhst), rscale);
/* Generate code for the sub (the & is a hack here) */
g_sub (flags & ~CF_CONST, 0);
}
/* Result is an rvalue in the primary register */
ED_FinalizeRValLoad (Expr);
}
} else {
/* We'll use the pushed lhs on stack instead of the original source */
ED_FinalizeRValLoad (Expr);
/* Right hand side is not constant, load into the primary */
LoadExpr (CF_NONE, &Expr2);
/* Check for pointer arithmetic */
if (IsClassPtr (lhst) && IsClassInt (rhst)) {
/* Left is pointer, right is int, must scale rhs */
g_scale (CF_INT, rscale);
} else if (IsClassInt (lhst) && IsClassInt (rhst)) {
/* Adjust operand types */
flags = typeadjust (Expr, &Expr2, 0);
} else {
/* OOPS */
Error ("Invalid operands for binary operator '-'");
}
/* Generate code for the sub (the & is a hack here) */
g_sub (flags & ~CF_CONST, 0);
/* Result is an rvalue in the primary register */
ED_FinalizeRValLoad (Expr);
}
/* Result type is either a pointer or an integer */
Expr->Type = StdConversion (Expr->Type);
/* Condition code not set */
ED_MarkAsUntested (Expr);
/* Propagate viral flags */
ED_PropagateFrom (Expr, &Expr2);
}
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, 0);
} 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 int hieAnd (ExprDesc* Expr, unsigned* TrueLab, int* TrueLabAllocated)
/* Process "exp && exp". This should only be called within hieOr.
** Return true if logic AND does occur.
*/
{
unsigned Flags = Expr->Flags & E_MASK_KEEP_SUBEXPR;
int HasFalseJump = 0, HasTrueJump = 0;
CodeMark Start;
/* The label that we will use for false expressions */
int FalseLab = 0;
/* Get lhs */
GetCodePos (&Start);
ExprWithCheck (hie2, Expr);
if ((Flags & E_EVAL_UNEVAL) == E_EVAL_UNEVAL) {
RemoveCode (&Start);
}
if (CurTok.Tok == TOK_BOOL_AND) {
ExprDesc Expr2;
/* Check type */
if (!ED_IsBool (Expr)) {
Error ("Scalar expression expected");
ED_MakeConstBool (Expr, 0);
} else if ((Flags & E_EVAL_UNEVAL) != E_EVAL_UNEVAL) {
if (!ED_IsConstBool (Expr)) {
/* Set the test flag */
ED_RequireTest (Expr);
/* Load the value */
LoadExpr (CF_FORCECHAR, Expr);
/* Append deferred inc/dec at sequence point */
DoDeferred (SQP_KEEP_TEST, Expr);
/* Clear the test flag */
ED_RequireNoTest (Expr);
if (HasFalseJump == 0) {
/* Remember that the jump is used */
HasFalseJump = 1;
/* Get a label for false expressions */
FalseLab = GetLocalLabel ();
}
/* Generate the jump */
g_falsejump (CF_NONE, FalseLab);
} else {
/* Constant boolean subexpression could still have deferred inc/dec
** operations, so just flush their side-effects at this sequence
** point.
*/
DoDeferred (SQP_KEEP_NONE, Expr);
if (ED_IsConstFalse (Expr)) {
/* Skip remaining */
Flags |= E_EVAL_UNEVAL;
}
}
}
/* Parse more boolean and's */
while (CurTok.Tok == TOK_BOOL_AND) {
ED_Init (&Expr2);
Expr2.Flags = Flags;
/* Skip the && */
NextToken ();
/* Get rhs */
GetCodePos (&Start);
hie2 (&Expr2);
if ((Flags & E_EVAL_UNEVAL) == E_EVAL_UNEVAL) {
RemoveCode (&Start);
}
/* Check type */
if (!ED_IsBool (&Expr2)) {
Error ("Scalar expression expected");
ED_MakeConstBool (&Expr2, 0);
} else if ((Flags & E_EVAL_UNEVAL) != E_EVAL_UNEVAL) {
if (!ED_IsConstBool (&Expr2)) {
ED_RequireTest (&Expr2);
LoadExpr (CF_FORCECHAR, &Expr2);
/* Append deferred inc/dec at sequence point */
DoDeferred (SQP_KEEP_TEST, &Expr2);
/* Do short circuit evaluation */
if (CurTok.Tok == TOK_BOOL_AND) {
if (HasFalseJump == 0) {
/* Remember that the jump is used */
HasFalseJump = 1;
/* Get a label for false expressions */
FalseLab = GetLocalLabel ();
}
g_falsejump (CF_NONE, FalseLab);
} else {
/* We need the true label for the last expression */
HasTrueJump = 1;
}
} else {
/* Constant boolean subexpression could still have deferred inc/
** dec operations, so just flush their side-effects at this
** sequence point.
*/
DoDeferred (SQP_KEEP_NONE, &Expr2);
if (ED_IsConstFalse (&Expr2)) {
/* Skip remaining */
Flags |= E_EVAL_UNEVAL;
/* The value of the expression will be false */
ED_MakeConstBool (Expr, 0);
}
}
}
/* Propagate viral flags */
if ((Expr->Flags & E_EVAL_UNEVAL) != E_EVAL_UNEVAL) {
ED_PropagateFrom (Expr, &Expr2);
}
}
/* Last expression */
if ((Flags & E_EVAL_UNEVAL) != E_EVAL_UNEVAL) {
if (HasFalseJump || HasTrueJump) {
if (*TrueLabAllocated == 0) {
/* Get a label that we will use for true expressions */
*TrueLab = GetLocalLabel ();
*TrueLabAllocated = 1;
}
if (!ED_IsConstAbs (&Expr2)) {
/* Will branch to true and fall to false */
g_truejump (CF_NONE, *TrueLab);
} else {
/* Will jump away */
g_jump (*TrueLab);
}
/* The result is an rvalue in primary */
ED_FinalizeRValLoad (Expr);
/* No need to test as the result will be jumped to */
ED_TestDone (Expr);
}
}
if (HasFalseJump) {
/* Define the false jump label here */
g_defcodelabel (FalseLab);
}
/* Convert to bool */
if ((ED_IsConstAbs (Expr) && Expr->IVal != 0) ||
ED_IsAddrExpr (Expr)) {
ED_MakeConstBool (Expr, 1);
} else {
Expr->Type = type_bool;
}
/* Tell our caller that we're evaluating a boolean */
return 1;
}
return 0;
}
static void hieOr (ExprDesc *Expr)
/* Process "exp || exp". */
{
unsigned Flags = Expr->Flags & E_MASK_KEEP_SUBEXPR;
int AndOp; /* Did we have a && operation? */
unsigned TrueLab; /* Jump to this label if true */
int HasTrueJump = 0;
CodeMark Start;
/* Call the next level parser */
GetCodePos (&Start);
AndOp = hieAnd (Expr, &TrueLab, &HasTrueJump);
if ((Flags & E_EVAL_UNEVAL) == E_EVAL_UNEVAL) {
RemoveCode (&Start);
}
/* Any boolean or's? */
if (CurTok.Tok == TOK_BOOL_OR) {
/* Check type */
if (!ED_IsBool (Expr)) {
Error ("Scalar expression expected");
ED_MakeConstBool (Expr, 0);
} else if ((Flags & E_EVAL_UNEVAL) != E_EVAL_UNEVAL) {
if (!ED_IsConstBool (Expr)) {
/* Test the lhs if we haven't had && operators. If we had them, the
** jump is already in place and there's no need to do the test.
*/
if (!AndOp) {
/* Set the test flag */
ED_RequireTest (Expr);
/* Get first expr */
LoadExpr (CF_FORCECHAR, Expr);
/* Append deferred inc/dec at sequence point */
DoDeferred (SQP_KEEP_TEST, Expr);
/* Clear the test flag */
ED_RequireNoTest (Expr);
if (HasTrueJump == 0) {
/* Get a label that we will use for true expressions */
TrueLab = GetLocalLabel();
HasTrueJump = 1;
}
/* Jump to TrueLab if true */
g_truejump (CF_NONE, TrueLab);
}
} else {
/* Constant boolean subexpression could still have deferred inc/dec
** operations, so just flush their side-effects at this sequence
** point.
*/
DoDeferred (SQP_KEEP_NONE, Expr);
if (ED_IsConstTrue (Expr)) {
/* Skip remaining */
Flags |= E_EVAL_UNEVAL;
}
}
}
/* while there's more expr */
while (CurTok.Tok == TOK_BOOL_OR) {
ExprDesc Expr2;
ED_Init (&Expr2);
Expr2.Flags = Flags;
/* skip the || */
NextToken ();
/* Get rhs subexpression */
GetCodePos (&Start);
AndOp = hieAnd (&Expr2, &TrueLab, &HasTrueJump);
if ((Flags & E_EVAL_UNEVAL) == E_EVAL_UNEVAL) {
RemoveCode (&Start);
}
/* Check type */
if (!ED_IsBool (&Expr2)) {
Error ("Scalar expression expected");
ED_MakeConstBool (&Expr2, 0);
} else if ((Flags & E_EVAL_UNEVAL) != E_EVAL_UNEVAL) {
if (!ED_IsConstBool (&Expr2)) {
/* If there is more to come, add shortcut boolean eval */
if (!AndOp) {
ED_RequireTest (&Expr2);
LoadExpr (CF_FORCECHAR, &Expr2);
/* Append deferred inc/dec at sequence point */
DoDeferred (SQP_KEEP_TEST, &Expr2);
if (HasTrueJump == 0) {
TrueLab = GetLocalLabel();
HasTrueJump = 1;
}
g_truejump (CF_NONE, TrueLab);
}
} else {
/* Constant boolean subexpression could still have deferred inc/
** dec operations, so just flush their side-effects at this
** sequence point.
*/
DoDeferred (SQP_KEEP_NONE, &Expr2);
if (ED_IsConstTrue (&Expr2)) {
/* Skip remaining */
Flags |= E_EVAL_UNEVAL;
/* The result is always true */
ED_MakeConstBool (Expr, 1);
}
}
}
/* Propagate viral flags */
if ((Expr->Flags & E_EVAL_UNEVAL) != E_EVAL_UNEVAL) {
ED_PropagateFrom (Expr, &Expr2);
}
}
/* Convert to bool */
if ((ED_IsConstAbs (Expr) && Expr->IVal != 0) ||
ED_IsAddrExpr (Expr)) {
ED_MakeConstBool (Expr, 1);
} else {
Expr->Type = type_bool;
}
}
/* If we really had boolean ops, generate the end sequence if necessary */
if (HasTrueJump) {
if ((Flags & E_EVAL_UNEVAL) != E_EVAL_UNEVAL) {
/* False case needs to jump over true case */
unsigned DoneLab = GetLocalLabel ();
/* Load false only if the result is not true */
g_getimmed (CF_INT | CF_CONST, 0, 0); /* Load FALSE */
g_falsejump (CF_NONE, DoneLab);
/* Load the true value */
g_defcodelabel (TrueLab);
g_getimmed (CF_INT | CF_CONST, 1, 0); /* Load TRUE */
g_defcodelabel (DoneLab);
} else {
/* Load the true value */
g_defcodelabel (TrueLab);
g_getimmed (CF_INT | CF_CONST, 1, 0); /* Load TRUE */
}
/* The result is an rvalue in primary */
ED_FinalizeRValLoad (Expr);
/* Condition codes are set */
ED_TestDone (Expr);
}
}
static void hieQuest (ExprDesc* Expr)
/* Parse the ternary operator */
{
int FalseLab = 0;
int TrueLab = 0;
CodeMark SkippedBranch;
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 */
ExprWithCheck (hieOr, Expr);
/* Check if it's a ternary expression */
if (CurTok.Tok == TOK_QUEST) {
/* The constant condition must be compile-time known as well */
int ConstantCond = ED_IsConstBool (Expr);
unsigned Flags = Expr->Flags & E_MASK_KEEP_RESULT;
ED_Init (&Expr2);
Expr2.Flags = Flags;
ED_Init (&Expr3);
Expr3.Flags = Flags;
NextToken ();
/* Convert non-integer constant to boolean constant, so that we may just
** check it in the same way.
*/
if (ED_IsConstTrue (Expr)) {
ED_MakeConstBool (Expr, 1);
} else if (ED_IsConstFalse (Expr)) {
ED_MakeConstBool (Expr, 0);
}
if (!ConstantCond) {
/* Condition codes not set, request a test */
ED_RequireTest (Expr);
LoadExpr (CF_NONE, Expr);
/* Append deferred inc/dec at sequence point */
DoDeferred (SQP_KEEP_TEST, Expr);
FalseLab = GetLocalLabel ();
g_falsejump (CF_NONE, FalseLab);
} else {
/* Constant boolean subexpression could still have deferred inc/dec
** operations, so just flush their side-effects at this sequence point.
*/
DoDeferred (SQP_KEEP_NONE, Expr);
if (Expr->IVal == 0) {
/* Remember the current code position */
GetCodePos (&SkippedBranch);
}
}
/* Parse second expression. Remember for later if it is a NULL pointer
** expression, then load it into the primary.
*/
ExprWithCheck (hie0, &Expr2);
Expr2IsNULL = ED_IsNullPtr (&Expr2);
if (!IsTypeVoid (Expr2.Type) &&
ED_YetToLoad (&Expr2) &&
(!ConstantCond || !ED_IsConst (&Expr2))) {
/* Load it into the primary */
LoadExpr (CF_NONE, &Expr2);
/* Append deferred inc/dec at sequence point */
DoDeferred (SQP_KEEP_EXPR, &Expr2);
ED_FinalizeRValLoad (&Expr2);
} else {
/* Constant boolean subexpression could still have deferred inc/
** dec operations, so just flush their side-effects at this
** sequence point.
*/
DoDeferred (SQP_KEEP_NONE, &Expr2);
}
Expr2.Type = PtrConversion (Expr2.Type);
if (!ConstantCond) {
/* 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);
} else {
if (Expr->IVal == 0) {
/* Expr2 is unevaluated when the condition is false */
Expr2.Flags |= E_EVAL_UNEVAL;
/* Remove the load code of Expr2 */
RemoveCode (&SkippedBranch);
} else {
/* Remember the current code position */
GetCodePos (&SkippedBranch);
}
ConsumeColon();
}
/* 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) &&
ED_YetToLoad (&Expr3) &&
(!ConstantCond || !ED_IsConst (&Expr3))) {
/* Load it into the primary */
LoadExpr (CF_NONE, &Expr3);
/* Append deferred inc/dec at sequence point */
DoDeferred (SQP_KEEP_EXPR, &Expr3);
ED_FinalizeRValLoad (&Expr3);
} else {
/* Constant boolean subexpression could still have deferred inc/
** dec operations, so just flush their side-effects at this
** sequence point.
*/
DoDeferred (SQP_KEEP_NONE, &Expr3);
}
Expr3.Type = PtrConversion (Expr3.Type);
if (ConstantCond && Expr->IVal != 0) {
/* Expr3 is unevaluated when the condition is true */
Expr3.Flags |= E_EVAL_UNEVAL;
/* Remove the load code of Expr3 */
RemoveCode (&SkippedBranch);
}
/* 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 have the same structure, union or void type,
** the result has the same type.
** - if both expressions are pointers to compatible types (possibly
** qualified differently), the result of the expression is an
** appropriately qualified version of the composite type.
** - if one of the expressions is a pointer and the other is a
** pointer to (possibly qualified) void, the resulting type is a
** pointer to appropriately qualified void.
** - if one of the expressions is a pointer and the other is
** a null pointer constant, the resulting type is that of the
** pointer type.
** - all other cases are flagged by an error.
*/
if (IsClassInt (Expr2.Type) && IsClassInt (Expr3.Type)) {
CodeMark CvtCodeStart;
CodeMark CvtCodeEnd;
/* Get common type */
ResultType = TypeDup (ArithmeticConvert (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 (!ConstantCond) {
/* 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)) {
/* If one of the two is 'void *', the result type is a pointer to
** appropriately qualified void.
*/
if (IsTypeVoid (Indirect (Expr2.Type))) {
ResultType = NewPointerTo (Indirect (Expr2.Type));
ResultType[1].C |= GetQualifier (Indirect (Expr3.Type));
} else if (IsTypeVoid (Indirect (Expr3.Type))) {
ResultType = NewPointerTo (Indirect (Expr3.Type));
ResultType[1].C |= GetQualifier (Indirect (Expr2.Type));
} else {
/* Must point to compatible types */
if (TypeCmp (Expr2.Type, Expr3.Type).C < TC_VOID_PTR) {
TypeCompatibilityDiagnostic (Expr2.Type, Expr3.Type,
1, "Incompatible pointer types in ternary: '%s' and '%s'");
/* Avoid further errors */
ResultType = NewPointerTo (type_void);
} else {
/* Result has the composite type */
ResultType = TypeDup (Expr2.Type);
TypeComposition (ResultType, Expr3.Type);
}
}
} else if (IsClassPtr (Expr2.Type) && Expr3IsNULL) {
/* Result type is pointer, no cast needed */
ResultType = TypeDup (Expr2.Type);
} else if (Expr2IsNULL && IsClassPtr (Expr3.Type)) {
/* Result type is pointer, no cast needed */
ResultType = TypeDup (Expr3.Type);
} else if (IsTypeVoid (Expr2.Type) && IsTypeVoid (Expr3.Type)) {
/* Result type is void */
ResultType = TypeDup (type_void);
} else {
if (IsClassStruct (Expr2.Type) && IsClassStruct (Expr3.Type) &&
TypeCmp (Expr2.Type, Expr3.Type).C == TC_IDENTICAL) {
/* Result type is struct/union */
ResultType = TypeDup (Expr2.Type);
} else {
TypeCompatibilityDiagnostic (Expr2.Type, Expr3.Type, 1,
"Incompatible types in ternary '%s' with '%s'");
ResultType = TypeDup (Expr2.Type); /* Doesn't matter here */
}
}
if (!ConstantCond) {
/* Define the final label */
g_defcodelabel (TrueLab);
/* Set up the result expression type */
ED_FinalizeRValLoad (Expr);
/* Restore the original evaluation flags */
Expr->Flags = (Expr->Flags & ~E_MASK_KEEP_RESULT) | Flags;
} else {
if (Expr->IVal != 0) {
*Expr = Expr2;
} else {
*Expr = Expr3;
}
}
/* Setup the target expression */
Expr->Type = ResultType;
/* Propagate viral flags */
if ((Expr2.Flags & E_EVAL_UNEVAL) != E_EVAL_UNEVAL) {
ED_PropagateFrom (Expr, &Expr2);
}
if ((Expr3.Flags & E_EVAL_UNEVAL) != E_EVAL_UNEVAL) {
ED_PropagateFrom (Expr, &Expr3);
}
}
}
void hie1 (ExprDesc* Expr)
/* Parse first level of expression hierarchy. */
{
hieQuest (Expr);
switch (CurTok.Tok) {
case TOK_ASSIGN:
OpAssign (0, Expr, "=");
break;
case TOK_PLUS_ASSIGN:
OpAddSubAssign (&GenPASGN, Expr, "+=");
break;
case TOK_MINUS_ASSIGN:
OpAddSubAssign (&GenSASGN, Expr, "-=");
break;
case TOK_MUL_ASSIGN:
OpAssign (&GenMASGN, Expr, "*=");
break;
case TOK_DIV_ASSIGN:
OpAssign (&GenDASGN, Expr, "/=");
break;
case TOK_MOD_ASSIGN:
OpAssign (&GenMOASGN, Expr, "%=");
break;
case TOK_SHL_ASSIGN:
OpAssign (&GenSLASGN, Expr, "<<=");
break;
case TOK_SHR_ASSIGN:
OpAssign (&GenSRASGN, Expr, ">>=");
break;
case TOK_AND_ASSIGN:
OpAssign (&GenAASGN, Expr, "&=");
break;
case TOK_XOR_ASSIGN:
OpAssign (&GenXOASGN, Expr, "^=");
break;
case TOK_OR_ASSIGN:
OpAssign (&GenOASGN, Expr, "|=");
break;
default:
break;
}
}
void hie0 (ExprDesc *Expr)
/* Parse comma operator. */
{
unsigned Flags = Expr->Flags & E_MASK_KEEP_MAKE;
unsigned PrevErrorCount = ErrorCount;
CodeMark Start, End;
/* Remember the current code position */
GetCodePos (&Start);
hie1 (Expr);
while (CurTok.Tok == TOK_COMMA) {
/* Append deferred inc/dec at sequence point */
DoDeferred (SQP_KEEP_NONE, Expr);
/* If the expression has no observable effect and isn't cast to type
** void, emit a warning and remove useless code if any.
*/
GetCodePos (&End);
if (CodeRangeIsEmpty (&Start, &End) ||
(Expr->Flags & E_SIDE_EFFECTS) == 0) {
if (!ED_MayHaveNoEffect (Expr) &&
IS_Get (&WarnNoEffect) &&
PrevErrorCount == ErrorCount) {
Warning ("Left-hand operand of comma expression has no effect");
}
/* Remove code with no effect */
RemoveCode (&Start);
}
PrevErrorCount = ErrorCount;
/* Remember the current code position */
GetCodePos (&Start);
/* Keep viral flags propagated from subexpressions */
Flags |= Expr->Flags & E_MASK_VIRAL;
/* Reset the expression */
ED_Init (Expr);
Expr->Flags = Flags & ~E_MASK_VIRAL;
NextToken ();
hie1 (Expr);
/* Propagate viral flags */
Expr->Flags |= Flags & E_MASK_VIRAL;
}
}
void Expression0 (ExprDesc* Expr)
/* Evaluate an expression via hie0 and put the result into the primary register.
** The expression is completely evaluated and all side effects complete.
*/
{
unsigned Flags = Expr->Flags & E_MASK_KEEP_RESULT;
/* Only check further after the expression is evaluated */
ExprWithCheck (hie0, Expr);
if ((Expr->Flags & Flags & E_MASK_EVAL) != (Flags & E_MASK_EVAL)) {
Internal ("Expression flags tampered: %08X", Flags);
}
if (ED_YetToLoad (Expr)) {
LoadExpr (CF_NONE, Expr);
}
/* Append deferred inc/dec at sequence point */
DoDeferred (SQP_KEEP_EXPR, Expr);
}
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 ("Scalar expression expected");
/* To avoid any compiler errors, make the expression a valid int */
ED_MakeConstBool (Expr, 1);
}
}
ExprDesc NoCodeConstExpr (void (*Func) (ExprDesc*))
/* Get an expression evaluated via the given function. If the result is not a
** constant expression without runtime code generated, 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.
*/
{
ExprDesc Expr;
ED_Init (&Expr);
Expr.Flags |= E_EVAL_C_CONST;
MarkedExprWithCheck (Func, &Expr);
if (!ED_IsConst (&Expr) || !ED_CodeRangeIsEmpty (&Expr)) {
Error ("Constant expression expected");
/* To avoid any compiler errors, make the expression a valid const */
Expr.Flags &= E_MASK_RTYPE | E_MASK_KEEP_RESULT;
Expr.Flags |= E_LOC_NONE;
}
/* Return by value */
return Expr;
}
ExprDesc NoCodeConstAbsIntExpr (void (*Func) (ExprDesc*))
/* Get an expression evaluated via the given function. If the result is not a
** constant numeric integer value without runtime code generated, 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.
*/
{
ExprDesc Expr;
ED_Init (&Expr);
Expr.Flags |= E_EVAL_C_CONST;
MarkedExprWithCheck (Func, &Expr);
if (!ED_IsConstAbsInt (&Expr) || !ED_CodeRangeIsEmpty (&Expr)) {
Error ("Constant integer expression expected");
/* To avoid any compiler errors, make the expression a valid const */
ED_MakeConstAbsInt (&Expr, 1);
}
/* Return by value */
return Expr;
}