cc65/src/cc65/expr.c

4247 lines
142 KiB
C
Raw Normal View History

/* 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)) {
2020-07-15 12:22:28 +00:00
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;
}
2020-07-15 12:22:28 +00:00
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;
}
2020-07-15 12:22:28 +00:00
if (ED_IsLocNone (rhs)) {
rtype |= CF_CONST;
}
flags = g_typeadjust (ltype, rtype);
/* Set the type of the result */
Use C89 semantics for integer conversions Previously, the following rules were used for binary operators: * If one of the values is a long, the result is long. * If one of the values is unsigned, the result is also unsigned. * Otherwise the result is an int. C89 specifies the "usual arithmetic conversions" as: * The integral promotions are performed on both operands. * Then the following rules are applied: * If either operand has type unsigned long int, the other operand is converted to unsigned long int. * Otherwise, if one operand has type long int and the other has type unsigned int, if a long int can represent all values of an unsigned int, the operand of type unsigned int is converted to long int; if a long int cannot represent all the values of an unsigned int, both operands are converted to unsigned long int. * Otherwise, if either operand has type long int, the other operand is converted to long int. * Otherwise, if either operand has type unsigned int, the other operand is converted to unsigned int. * Otherwise, both operands have type int. https://port70.net/~nsz/c/c89/c89-draft.html#3.2.1.5 As one example, these rules give a different result for an operator with one long operand and one unsigned int operand. Previously, the result type was unsigned long. With C89 semantics, it is just long, since long can represent all unsigned ints. Integral promotions convert types shorter than int to int (or unsigned int). Both char and unsigned char are promoted to int since int can represent all unsigned chars. https://port70.net/~nsz/c/c89/c89-draft.html#3.2.1.1 Rename promoteint to ArithmeticConvert, since this is more accurate. Fixes #170
2020-08-14 12:54:10 +00:00
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) {
2021-04-17 03:43:03 +00:00
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);
2020-07-15 12:22:28 +00:00
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) {
2017-10-23 16:22:51 +00:00
Warning ("Calling a wrapped function via a pointer, wrapped-call will not be used");
2017-05-02 16:06:19 +00:00
}
/* 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) {
2017-05-02 16:06:19 +00:00
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);
}
2017-05-02 16:06:19 +00:00
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);
2017-05-02 16:06:19 +00:00
} else {
g_call (FuncTypeOf (Expr->Type), (const char*) Expr->Name, ArgSize);
2017-05-02 16:06:19 +00:00
}
}
/* The function result is an rvalue in the primary register */
2020-07-15 12:22:28 +00:00
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) {
2019-04-14 16:45:53 +00:00
SymEntry* Entry;
NextToken ();
2019-04-14 16:45:53 +00:00
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 */
2020-07-15 12:22:28 +00:00
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
2020-07-15 12:22:28 +00:00
** 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)) {
2020-07-15 12:22:28 +00:00
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));
2020-07-15 12:22:28 +00:00
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:
2022-08-17 14:28:00 +00:00
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.
*/
2020-07-15 12:22:28 +00:00
LoadExpr (CF_NONE, Expr);
ED_FinalizeRValLoad (Expr);
2020-07-15 12:22:28 +00:00
}
/* Dereference the address expression */
2020-07-15 12:22:28 +00:00
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);
2020-07-15 12:22:28 +00:00
} 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);
}
2020-08-14 00:32:22 +00:00
/* 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
2020-07-15 12:22:28 +00:00
** virtually an rvalue address, otherwise it's an lvalue reference. (A
** function would also be an rvalue address, but a struct/union cannot
2020-07-15 12:22:28 +00:00
** contain functions).
*/
if (IsTypeArray (Expr->Type)) {
2020-07-15 12:22:28 +00:00
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:
2020-07-15 12:22:28 +00:00
/* 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:
2020-07-15 12:22:28 +00:00
/* An expression referenced in the primary register */
g_putind (Flags, Expr->IVal);
break;
2020-07-15 12:22:28 +00:00
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 */
2020-07-15 12:22:28 +00:00
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 */
2020-07-15 12:22:28 +00:00
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)) {
2017-05-06 10:20:55 +00:00
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);
2017-05-06 10:20:55 +00:00
/* Expression has had side effects */
Expr->Flags |= E_SIDE_EFFECTS;
2017-05-06 10:20:55 +00:00
} 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 */
2020-07-15 12:22:28 +00:00
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)) {
2017-05-06 10:23:50 +00:00
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);
2017-05-06 10:23:50 +00:00
/* Expression has had side effects */
Expr->Flags |= E_SIDE_EFFECTS;
2017-05-06 10:23:50 +00:00
} 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 */
2020-07-15 12:22:28 +00:00
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);
}
2020-07-15 12:22:28 +00:00
/* 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 ();
2020-08-19 23:52:17 +00:00
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 {
2020-08-19 23:52:17 +00:00
/* Not constant, load into the primary */
LoadExpr (CF_NONE, Expr);
g_bneg (TypeOf (Expr->Type));
2020-07-15 12:22:28 +00:00
ED_FinalizeRValLoad (Expr);
ED_TestDone (Expr); /* bneg will set cc */
}
2020-08-19 23:52:17 +00:00
/* The result type is always boolean */
Expr->Type = type_bool;
break;
case TOK_STAR:
NextToken ();
ExprWithCheck (hie10, Expr);
2020-07-15 12:22:28 +00:00
/* 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 */
2020-07-15 12:22:28 +00:00
ED_MarkExprAsRVal (Expr);
} else {
2020-09-08 13:36:38 +00:00
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)) {
2020-07-15 12:22:28 +00:00
/* 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);
2020-07-17 07:10:27 +00:00
}
/* 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 */
Use C89 semantics for integer conversions Previously, the following rules were used for binary operators: * If one of the values is a long, the result is long. * If one of the values is unsigned, the result is also unsigned. * Otherwise the result is an int. C89 specifies the "usual arithmetic conversions" as: * The integral promotions are performed on both operands. * Then the following rules are applied: * If either operand has type unsigned long int, the other operand is converted to unsigned long int. * Otherwise, if one operand has type long int and the other has type unsigned int, if a long int can represent all values of an unsigned int, the operand of type unsigned int is converted to long int; if a long int cannot represent all the values of an unsigned int, both operands are converted to unsigned long int. * Otherwise, if either operand has type long int, the other operand is converted to long int. * Otherwise, if either operand has type unsigned int, the other operand is converted to unsigned int. * Otherwise, both operands have type int. https://port70.net/~nsz/c/c89/c89-draft.html#3.2.1.5 As one example, these rules give a different result for an operator with one long operand and one unsigned int operand. Previously, the result type was unsigned long. With C89 semantics, it is just long, since long can represent all unsigned ints. Integral promotions convert types shorter than int to int (or unsigned int). Both char and unsigned char are promoted to int since int can represent all unsigned chars. https://port70.net/~nsz/c/c89/c89-draft.html#3.2.1.1 Rename promoteint to ArithmeticConvert, since this is more accurate. Fixes #170
2020-08-14 12:54:10 +00:00
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);
Use C89 semantics for integer conversions Previously, the following rules were used for binary operators: * If one of the values is a long, the result is long. * If one of the values is unsigned, the result is also unsigned. * Otherwise the result is an int. C89 specifies the "usual arithmetic conversions" as: * The integral promotions are performed on both operands. * Then the following rules are applied: * If either operand has type unsigned long int, the other operand is converted to unsigned long int. * Otherwise, if one operand has type long int and the other has type unsigned int, if a long int can represent all values of an unsigned int, the operand of type unsigned int is converted to long int; if a long int cannot represent all the values of an unsigned int, both operands are converted to unsigned long int. * Otherwise, if either operand has type long int, the other operand is converted to long int. * Otherwise, if either operand has type unsigned int, the other operand is converted to unsigned int. * Otherwise, both operands have type int. https://port70.net/~nsz/c/c89/c89-draft.html#3.2.1.5 As one example, these rules give a different result for an operator with one long operand and one unsigned int operand. Previously, the result type was unsigned long. With C89 semantics, it is just long, since long can represent all unsigned ints. Integral promotions convert types shorter than int to int (or unsigned int). Both char and unsigned char are promoted to int since int can represent all unsigned chars. https://port70.net/~nsz/c/c89/c89-draft.html#3.2.1.1 Rename promoteint to ArithmeticConvert, since this is more accurate. Fixes #170
2020-08-14 12:54:10 +00:00
Expr->Type = ArithmeticConvert (Expr->Type, Expr2.Type);
/* Generate code */
Gen->Func (type, Expr->IVal);
2020-07-15 12:22:28 +00:00
/* 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);
Use C89 semantics for integer conversions Previously, the following rules were used for binary operators: * If one of the values is a long, the result is long. * If one of the values is unsigned, the result is also unsigned. * Otherwise the result is an int. C89 specifies the "usual arithmetic conversions" as: * The integral promotions are performed on both operands. * Then the following rules are applied: * If either operand has type unsigned long int, the other operand is converted to unsigned long int. * Otherwise, if one operand has type long int and the other has type unsigned int, if a long int can represent all values of an unsigned int, the operand of type unsigned int is converted to long int; if a long int cannot represent all the values of an unsigned int, both operands are converted to unsigned long int. * Otherwise, if either operand has type long int, the other operand is converted to long int. * Otherwise, if either operand has type unsigned int, the other operand is converted to unsigned int. * Otherwise, both operands have type int. https://port70.net/~nsz/c/c89/c89-draft.html#3.2.1.5 As one example, these rules give a different result for an operator with one long operand and one unsigned int operand. Previously, the result type was unsigned long. With C89 semantics, it is just long, since long can represent all unsigned ints. Integral promotions convert types shorter than int to int (or unsigned int). Both char and unsigned char are promoted to int since int can represent all unsigned chars. https://port70.net/~nsz/c/c89/c89-draft.html#3.2.1.1 Rename promoteint to ArithmeticConvert, since this is more accurate. Fixes #170
2020-08-14 12:54:10 +00:00
Expr->Type = ArithmeticConvert (Expr->Type, Expr2.Type);
/* Generate code */
Gen->Func (type, Expr2.IVal);
2020-07-15 12:22:28 +00:00
/* 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 */
2020-07-15 12:22:28 +00:00
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);
}
}
2020-07-15 12:22:28 +00:00
/* 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);
}
2020-07-15 12:22:28 +00:00
/* 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);
}
2020-07-15 12:22:28 +00:00
/* 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);
2020-07-15 12:22:28 +00:00
/* 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)
2020-08-19 23:52:15 +00:00
/* Process "exp && exp". This should only be called within hieOr.
** Return true if logic AND does occur.
*/
{
2020-08-19 23:52:15 +00:00
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;
2020-08-19 23:52:15 +00:00
/* Get lhs */
GetCodePos (&Start);
ExprWithCheck (hie2, Expr);
2020-08-19 23:52:15 +00:00
if ((Flags & E_EVAL_UNEVAL) == E_EVAL_UNEVAL) {
RemoveCode (&Start);
}
if (CurTok.Tok == TOK_BOOL_AND) {
2020-08-19 23:52:15 +00:00
ExprDesc Expr2;
2020-08-19 23:52:15 +00:00
/* 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)) {
2020-08-19 23:52:15 +00:00
/* Set the test flag */
ED_RequireTest (Expr);
2020-08-19 23:52:15 +00:00
/* 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 ();
}
2020-08-19 23:52:15 +00:00
/* 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;
}
2020-08-19 23:52:15 +00:00
}
}
/* Parse more boolean and's */
while (CurTok.Tok == TOK_BOOL_AND) {
ED_Init (&Expr2);
2020-08-19 23:52:15 +00:00
Expr2.Flags = Flags;
/* Skip the && */
NextToken ();
/* Get rhs */
2020-08-19 23:52:15 +00:00
GetCodePos (&Start);
hie2 (&Expr2);
2020-08-19 23:52:15 +00:00
if ((Flags & E_EVAL_UNEVAL) == E_EVAL_UNEVAL) {
RemoveCode (&Start);
}
2020-08-19 23:52:15 +00:00
/* 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)) {
2020-08-19 23:52:15 +00:00
ED_RequireTest (&Expr2);
LoadExpr (CF_FORCECHAR, &Expr2);
/* Append deferred inc/dec at sequence point */
DoDeferred (SQP_KEEP_TEST, &Expr2);
2020-08-19 23:52:15 +00:00
/* 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 ();
}
2020-08-19 23:52:15 +00:00
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);
}
2020-08-19 23:52:15 +00:00
}
}
/* Propagate viral flags */
if ((Expr->Flags & E_EVAL_UNEVAL) != E_EVAL_UNEVAL) {
ED_PropagateFrom (Expr, &Expr2);
}
}
2020-08-19 23:52:15 +00:00
/* 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;
}
2020-08-19 23:52:15 +00:00
if (!ED_IsConstAbs (&Expr2)) {
/* Will branch to true and fall to false */
g_truejump (CF_NONE, *TrueLab);
2020-08-19 23:52:15 +00:00
} else {
/* Will jump away */
g_jump (*TrueLab);
2020-08-19 23:52:15 +00:00
}
/* The result is an rvalue in primary */
ED_FinalizeRValLoad (Expr);
/* No need to test as the result will be jumped to */
ED_TestDone (Expr);
}
}
2020-08-19 23:52:15 +00:00
if (HasFalseJump) {
/* Define the false jump label here */
g_defcodelabel (FalseLab);
}
/* Convert to bool */
if ((ED_IsConstAbs (Expr) && Expr->IVal != 0) ||
ED_IsAddrExpr (Expr)) {
2020-08-19 23:52:15 +00:00
ED_MakeConstBool (Expr, 1);
} else {
Expr->Type = type_bool;
}
/* Tell our caller that we're evaluating a boolean */
return 1;
}
2020-08-19 23:52:15 +00:00
return 0;
}
static void hieOr (ExprDesc *Expr)
/* Process "exp || exp". */
{
2020-08-19 23:52:15 +00:00
unsigned Flags = Expr->Flags & E_MASK_KEEP_SUBEXPR;
int AndOp; /* Did we have a && operation? */
unsigned TrueLab; /* Jump to this label if true */
2020-08-19 23:52:15 +00:00
int HasTrueJump = 0;
CodeMark Start;
/* Call the next level parser */
2020-08-19 23:52:15 +00:00
GetCodePos (&Start);
AndOp = hieAnd (Expr, &TrueLab, &HasTrueJump);
2020-08-19 23:52:15 +00:00
if ((Flags & E_EVAL_UNEVAL) == E_EVAL_UNEVAL) {
RemoveCode (&Start);
}
/* Any boolean or's? */
if (CurTok.Tok == TOK_BOOL_OR) {
2020-08-19 23:52:15 +00:00
/* 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)) {
2020-08-19 23:52:15 +00:00
/* 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);
2020-08-19 23:52:15 +00:00
/* 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;
}
2020-08-19 23:52:15 +00:00
/* 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;
}
2020-08-19 23:52:15 +00:00
}
}
/* while there's more expr */
while (CurTok.Tok == TOK_BOOL_OR) {
ExprDesc Expr2;
ED_Init (&Expr2);
2020-08-19 23:52:15 +00:00
Expr2.Flags = Flags;
/* skip the || */
NextToken ();
2020-08-19 23:52:15 +00:00
/* Get rhs subexpression */
GetCodePos (&Start);
AndOp = hieAnd (&Expr2, &TrueLab, &HasTrueJump);
2020-08-19 23:52:15 +00:00
if ((Flags & E_EVAL_UNEVAL) == E_EVAL_UNEVAL) {
RemoveCode (&Start);
}
2020-08-19 23:52:15 +00:00
/* 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)) {
2020-08-19 23:52:15 +00:00
/* 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;
}
2020-08-19 23:52:15 +00:00
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);
}
2020-08-19 23:52:15 +00:00
}
}
/* Propagate viral flags */
if ((Expr->Flags & E_EVAL_UNEVAL) != E_EVAL_UNEVAL) {
ED_PropagateFrom (Expr, &Expr2);
}
}
2020-08-19 23:52:15 +00:00
/* Convert to bool */
if ((ED_IsConstAbs (Expr) && Expr->IVal != 0) ||
ED_IsAddrExpr (Expr)) {
2020-08-19 23:52:15 +00:00
ED_MakeConstBool (Expr, 1);
} else {
Expr->Type = type_bool;
}
}
2020-08-19 23:52:15 +00:00
/* 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 ();
2020-08-19 23:52:15 +00:00
/* Load false only if the result is not true */
g_getimmed (CF_INT | CF_CONST, 0, 0); /* Load FALSE */
g_falsejump (CF_NONE, DoneLab);
2022-04-17 14:07:09 +00:00
/* 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 */
2020-08-19 23:52:15 +00:00
}
/* 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 */
{
2020-11-18 00:01:17 +00:00
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.
*/
2021-03-13 19:25:40 +00:00
ExprWithCheck (hie0, &Expr2);
Expr2IsNULL = ED_IsNullPtr (&Expr2);
2021-03-13 18:39:40 +00:00
if (!IsTypeVoid (Expr2.Type) &&
ED_YetToLoad (&Expr2) &&
(!ConstantCond || !ED_IsConst (&Expr2))) {
/* Load it into the primary */
LoadExpr (CF_NONE, &Expr2);
2021-03-13 18:39:40 +00:00
/* Append deferred inc/dec at sequence point */
DoDeferred (SQP_KEEP_EXPR, &Expr2);
2021-03-13 18:39:40 +00:00
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);
}
2021-03-13 18:39:40 +00:00
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) {
2021-03-13 18:39:40 +00:00
/* 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);
2021-03-13 18:39:40 +00:00
if (!IsTypeVoid (Expr3.Type) &&
ED_YetToLoad (&Expr3) &&
(!ConstantCond || !ED_IsConst (&Expr3))) {
/* Load it into the primary */
LoadExpr (CF_NONE, &Expr3);
2021-03-13 18:39:40 +00:00
/* Append deferred inc/dec at sequence point */
DoDeferred (SQP_KEEP_EXPR, &Expr3);
2021-03-13 18:39:40 +00:00
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);
}
2021-03-13 18:39:40 +00:00
Expr3.Type = PtrConversion (Expr3.Type);
if (ConstantCond && Expr->IVal != 0) {
2021-03-13 18:39:40 +00:00
/* 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 */
2021-03-30 08:47:57 +00:00
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 */
2021-03-30 08:47:57 +00:00
ResultType = TypeDup (Expr2.Type);
} else if (Expr2IsNULL && IsClassPtr (Expr3.Type)) {
/* Result type is pointer, no cast needed */
2021-03-30 08:47:57 +00:00
ResultType = TypeDup (Expr3.Type);
} else if (IsTypeVoid (Expr2.Type) && IsTypeVoid (Expr3.Type)) {
/* Result type is void */
2021-03-30 08:47:57 +00:00
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 */
2021-03-30 08:47:57 +00:00
ResultType = TypeDup (Expr2.Type);
} else {
TypeCompatibilityDiagnostic (Expr2.Type, Expr3.Type, 1,
"Incompatible types in ternary '%s' with '%s'");
2021-03-30 08:47:57 +00:00
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;
}