diff --git a/src/cc65/asmstmt.c b/src/cc65/asmstmt.c index ae4308522..9dd1f906a 100644 --- a/src/cc65/asmstmt.c +++ b/src/cc65/asmstmt.c @@ -129,14 +129,13 @@ static SymEntry* AsmGetSym (unsigned Arg, unsigned Type) static void ParseByteArg (StrBuf* T, unsigned Arg) /* Parse the %b format specifier */ { - ExprDesc Expr; char Buf [16]; /* We expect an argument separated by a comma */ ConsumeComma (); /* Evaluate the expression */ - ConstAbsIntExpr (hie1, &Expr); + ExprDesc Expr = NoCodeConstAbsIntExpr (hie1); /* Check the range but allow negative values if the type is signed */ if (IsSignUnsigned (Expr.Type)) { @@ -163,14 +162,13 @@ static void ParseByteArg (StrBuf* T, unsigned Arg) static void ParseWordArg (StrBuf* T, unsigned Arg) /* Parse the %w format specifier */ { - ExprDesc Expr; char Buf [16]; /* We expect an argument separated by a comma */ ConsumeComma (); /* Evaluate the expression */ - ConstAbsIntExpr (hie1, &Expr); + ExprDesc Expr = NoCodeConstAbsIntExpr (hie1); /* Check the range but allow negative values if the type is signed */ if (IsSignUnsigned (Expr.Type)) { @@ -197,14 +195,13 @@ static void ParseWordArg (StrBuf* T, unsigned Arg) static void ParseLongArg (StrBuf* T, unsigned Arg attribute ((unused))) /* Parse the %l format specifier */ { - ExprDesc Expr; char Buf [16]; /* We expect an argument separated by a comma */ ConsumeComma (); /* Evaluate the expression */ - ConstAbsIntExpr (hie1, &Expr); + ExprDesc Expr = NoCodeConstAbsIntExpr (hie1); /* Convert into a hex number */ xsprintf (Buf, sizeof (Buf), "$%08lX", Expr.IVal & 0xFFFFFFFF); @@ -328,7 +325,7 @@ static void ParseStrArg (StrBuf* T, unsigned Arg attribute ((unused))) break; default: - ConstAbsIntExpr (hie1, &Expr); + Expr = NoCodeConstAbsIntExpr (hie1); xsprintf (Buf, sizeof (Buf), "%ld", Expr.IVal); SB_AppendStr (T, Buf); break; diff --git a/src/cc65/assignment.c b/src/cc65/assignment.c index 633083669..1ada3af0b 100644 --- a/src/cc65/assignment.c +++ b/src/cc65/assignment.c @@ -139,6 +139,7 @@ void Assignment (ExprDesc* Expr) ExprDesc Expr2; Type* ltype = Expr->Type; + ED_Init (&Expr2); /* We must have an lvalue for an assignment */ if (ED_IsRVal (Expr)) { diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index c5fb784a2..e5e20ac1f 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -66,6 +66,7 @@ Type type_int[] = { TYPE(T_INT), TYPE(T_END) }; Type type_uint[] = { TYPE(T_UINT), TYPE(T_END) }; Type type_long[] = { TYPE(T_LONG), TYPE(T_END) }; Type type_ulong[] = { TYPE(T_ULONG), TYPE(T_END) }; +Type type_bool[] = { TYPE(T_INT), TYPE(T_END) }; Type type_void[] = { TYPE(T_VOID), TYPE(T_END) }; Type type_size_t[] = { TYPE(T_SIZE_T), TYPE(T_END) }; Type type_float[] = { TYPE(T_FLOAT), TYPE(T_END) }; diff --git a/src/cc65/datatype.h b/src/cc65/datatype.h index 8e9ee4c33..5c3e71da5 100644 --- a/src/cc65/datatype.h +++ b/src/cc65/datatype.h @@ -196,6 +196,7 @@ extern Type type_int[]; extern Type type_uint[]; extern Type type_long[]; extern Type type_ulong[]; +extern Type type_bool[]; extern Type type_void[]; extern Type type_size_t[]; extern Type type_float[]; diff --git a/src/cc65/declare.c b/src/cc65/declare.c index f65702e91..6953afa3c 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -621,9 +621,8 @@ static SymEntry* ParseEnumDecl (const char* Name, unsigned* DSFlags) /* Check for an assigned value */ if (CurTok.Tok == TOK_ASSIGN) { - ExprDesc Expr; NextToken (); - ConstAbsIntExpr (hie1, &Expr); + ExprDesc Expr = NoCodeConstAbsIntExpr (hie1); EnumVal = Expr.IVal; MemberType = Expr.Type; IsSigned = IsSignSigned (MemberType); @@ -750,8 +749,6 @@ static int ParseFieldWidth (Declaration* Decl) ** otherwise the width of the field. */ { - ExprDesc Expr; - if (CurTok.Tok != TOK_COLON) { /* No bit-field declaration */ return -1; @@ -775,7 +772,7 @@ static int ParseFieldWidth (Declaration* Decl) /* Read the width */ NextToken (); - ConstAbsIntExpr (hie1, &Expr); + ExprDesc Expr = NoCodeConstAbsIntExpr (hie1); if (Expr.IVal < 0) { Error ("Negative width in bit-field"); @@ -1894,8 +1891,7 @@ static void Declarator (const DeclSpec* Spec, Declaration* D, declmode_t Mode) /* Read the size if it is given */ if (CurTok.Tok != TOK_RBRACK) { - ExprDesc Expr; - ConstAbsIntExpr (hie1, &Expr); + ExprDesc Expr = NoCodeConstAbsIntExpr (hie1); if (Expr.IVal <= 0) { if (D->Ident[0] != '\0') { Error ("Size of array '%s' is invalid", D->Ident); @@ -2240,7 +2236,7 @@ static void OutputBitFieldData (StructInitData* SI) -static void ParseScalarInitInternal (Type* T, ExprDesc* ED) +static ExprDesc ParseScalarInitInternal (Type* T) /* Parse initializaton for scalar data types. This function will not output the ** data but return it in ED. */ @@ -2256,11 +2252,13 @@ static void ParseScalarInitInternal (Type* T, ExprDesc* ED) } /* Get the expression and convert it to the target type */ - ConstExpr (hie1, ED); - TypeConversion (ED, T); + ExprDesc ED = NoCodeConstExpr (hie1); + TypeConversion (&ED, T); /* Close eventually opening braces */ ClosingCurlyBraces (BraceCount); + + return ED; } @@ -2268,10 +2266,8 @@ static void ParseScalarInitInternal (Type* T, ExprDesc* ED) static unsigned ParseScalarInit (Type* T) /* Parse initializaton for scalar data types. Return the number of data bytes. */ { - ExprDesc ED; - /* Parse initialization */ - ParseScalarInitInternal (T, &ED); + ExprDesc ED = ParseScalarInitInternal (T); /* Output the data */ DefineData (&ED); @@ -2289,8 +2285,7 @@ static unsigned ParsePointerInit (Type* T) unsigned BraceCount = OpeningCurlyBraces (0); /* Expression */ - ExprDesc ED; - ConstExpr (hie1, &ED); + ExprDesc ED = NoCodeConstExpr (hie1); TypeConversion (&ED, T); /* Output the data */ @@ -2516,6 +2511,7 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) ** handling. */ ExprDesc ED; + ED_Init (&ED); unsigned Val; unsigned Shift; @@ -2527,7 +2523,7 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) SI.Offs * CHAR_BITS + SI.ValBits); /* Read the data, check for a constant integer, do a range check */ - ParseScalarInitInternal (Entry->Type, &ED); + ED = ParseScalarInitInternal (Entry->Type); if (!ED_IsConstAbsInt (&ED)) { Error ("Constant initializer expected"); ED_MakeConstAbsInt (&ED, 1); @@ -2637,7 +2633,6 @@ static unsigned ParseVoidInit (Type* T) ** Return the number of bytes initialized. */ { - ExprDesc Expr; unsigned Size; /* Opening brace */ @@ -2646,7 +2641,7 @@ static unsigned ParseVoidInit (Type* T) /* Allow an arbitrary list of values */ Size = 0; do { - ConstExpr (hie1, &Expr); + ExprDesc Expr = NoCodeConstExpr (hie1); switch (GetUnderlyingTypeCode (&Expr.Type[0])) { case T_SCHAR: diff --git a/src/cc65/expr.c b/src/cc65/expr.c index 75d695623..d1dfa62ec 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -311,13 +311,15 @@ void PushAddr (const ExprDesc* Expr) -static void WarnConstCompareResult (void) +static void WarnConstCompareResult (const ExprDesc* Expr) /* If the result of a comparison is constant, this is suspicious when not in ** preprocessor mode. */ { - if (!Preprocessing && IS_Get (&WarnConstComparison) != 0) { - Warning ("Result of comparison is constant"); + if (!Preprocessing && + !ED_NeedsConst (Expr) && + IS_Get (&WarnConstComparison) != 0) { + Warning ("Result of comparison is always %s", Expr->IVal != 0 ? "true" : "false"); } } @@ -390,6 +392,7 @@ static unsigned FunctionParamList (FuncDesc* Func, int IsFastcall) unsigned Flags; ExprDesc Expr; + ED_Init (&Expr); /* Count arguments */ ++PushedCount; @@ -733,23 +736,20 @@ static void Primary (ExprDesc* E) { SymEntry* Sym; - /* Initialize fields in the expression stucture */ - ED_Init (E); - /* Character and integer constants. */ if (CurTok.Tok == TOK_ICONST || CurTok.Tok == TOK_CCONST) { - E->IVal = CurTok.IVal; - E->Flags = E_LOC_NONE | E_RTYPE_RVAL; - E->Type = CurTok.Type; + E->IVal = CurTok.IVal; + E->Flags |= E_LOC_NONE | E_RTYPE_RVAL; + E->Type = CurTok.Type; NextToken (); return; } /* Floating point constant */ if (CurTok.Tok == TOK_FCONST) { - E->FVal = CurTok.FVal; - E->Flags = E_LOC_NONE | E_RTYPE_RVAL; - E->Type = CurTok.Type; + E->FVal = CurTok.FVal; + E->Flags |= E_LOC_NONE | E_RTYPE_RVAL; + E->Type = CurTok.Type; NextToken (); return; } @@ -783,6 +783,8 @@ static void Primary (ExprDesc* E) return; } + unsigned Flags = E->Flags & E_MASK_KEEP_MAKE; + switch (CurTok.Tok) { case TOK_BOOL_AND: @@ -818,8 +820,8 @@ static void Primary (ExprDesc* E) /* 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; + E->Flags |= E_LOC_STACK | E_RTYPE_LVAL; + E->Type = type_int; return; } @@ -930,7 +932,7 @@ static void Primary (ExprDesc* E) case TOK_ASM: /* ASM statement */ AsmStatement (); - E->Flags = E_RTYPE_RVAL; + E->Flags = E_RTYPE_RVAL | E_EVAL_MAYBE_UNUSED; E->Type = type_void; break; @@ -1000,6 +1002,8 @@ static void Primary (ExprDesc* E) ED_MakeConstAbsInt (E, 1); break; } + + E->Flags |= Flags; } @@ -1015,6 +1019,8 @@ static void ArrayRef (ExprDesc* Expr) Type* ElementType; Type* tptr1; + ED_Init (&Subscript); + Subscript.Flags |= Expr->Flags & E_MASK_KEEP_SUBEXPR; /* Skip the bracket */ NextToken (); @@ -2047,7 +2053,6 @@ static void hie_internal (const GenDesc* Ops, /* List of generators */ int* UsedGen) /* Helper function */ { - ExprDesc Expr2; CodeMark Mark1; CodeMark Mark2; const GenDesc* Gen; @@ -2062,6 +2067,10 @@ static void hie_internal (const GenDesc* Ops, /* List of generators */ *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; @@ -2266,7 +2275,6 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ void (*hienext) (ExprDesc*)) /* Helper function for the compare operators */ { - ExprDesc Expr2; CodeMark Mark0; CodeMark Mark1; CodeMark Mark2; @@ -2281,6 +2289,10 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ 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; @@ -2380,11 +2392,6 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ /* Check for const operands */ if (ED_IsConstAbs (Expr) && rconst) { - /* If the result is constant, this is suspicious when not in - ** preprocessor mode. - */ - WarnConstCompareResult (); - /* Both operands are constant, remove the generated code */ RemoveCode (&Mark1); @@ -2421,6 +2428,11 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ } } + /* If the result is constant, this is suspicious when not in + ** preprocessor mode. + */ + WarnConstCompareResult (Expr); + } else { /* Determine the signedness of the operands */ @@ -2479,7 +2491,7 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ case TOK_EQ: if (Expr2.IVal < LeftMin || Expr2.IVal > LeftMax) { ED_MakeConstAbsInt (Expr, 0); - WarnConstCompareResult (); + WarnConstCompareResult (Expr); goto Done; } break; @@ -2487,7 +2499,7 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ case TOK_NE: if (Expr2.IVal < LeftMin || Expr2.IVal > LeftMax) { ED_MakeConstAbsInt (Expr, 1); - WarnConstCompareResult (); + WarnConstCompareResult (Expr); goto Done; } break; @@ -2495,7 +2507,7 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ case TOK_LT: if (Expr2.IVal <= LeftMin || Expr2.IVal > LeftMax) { ED_MakeConstAbsInt (Expr, Expr2.IVal > LeftMax); - WarnConstCompareResult (); + WarnConstCompareResult (Expr); goto Done; } break; @@ -2503,7 +2515,7 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ case TOK_LE: if (Expr2.IVal < LeftMin || Expr2.IVal >= LeftMax) { ED_MakeConstAbsInt (Expr, Expr2.IVal >= LeftMax); - WarnConstCompareResult (); + WarnConstCompareResult (Expr); goto Done; } break; @@ -2511,7 +2523,7 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ case TOK_GE: if (Expr2.IVal <= LeftMin || Expr2.IVal > LeftMax) { ED_MakeConstAbsInt (Expr, Expr2.IVal <= LeftMin); - WarnConstCompareResult (); + WarnConstCompareResult (Expr); goto Done; } break; @@ -2519,7 +2531,7 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ case TOK_GT: if (Expr2.IVal < LeftMin || Expr2.IVal >= LeftMax) { ED_MakeConstAbsInt (Expr, Expr2.IVal < LeftMin); - WarnConstCompareResult (); + WarnConstCompareResult (Expr); goto Done; } break; @@ -2613,13 +2625,13 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ /* The result is an rvalue in the primary */ ED_FinalizeRValLoad (Expr); + + /* Condition codes are set */ + ED_TestDone (Expr); } - /* Result type is always int */ - Expr->Type = type_int; - -Done: /* Condition codes are set */ - ED_TestDone (Expr); + /* Result type is always boolean */ +Done: Expr->Type = type_bool; } } @@ -2653,6 +2665,9 @@ static void parseadd (ExprDesc* Expr) Type* lhst; /* Type of left hand side */ Type* rhst; /* Type of right hand side */ + ED_Init (&Expr2); + Expr2.Flags |= Expr->Flags & E_MASK_KEEP_SUBEXPR; + /* Skip the PLUS token */ NextToken (); @@ -2887,6 +2902,8 @@ static void parsesub (ExprDesc* Expr) CodeMark Mark2; /* Another position in the queue */ int rscale; /* Scale factor for the result */ + 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)) { @@ -3148,16 +3165,14 @@ static void hieAndPP (ExprDesc* Expr) ** called recursively from the preprocessor. */ { - ExprDesc Expr2; - - ConstAbsIntExpr (hie2, Expr); + *Expr = NoCodeConstAbsIntExpr (hie2); while (CurTok.Tok == TOK_BOOL_AND) { /* Skip the && */ NextToken (); /* Get rhs */ - ConstAbsIntExpr (hie2, &Expr2); + ExprDesc Expr2 = NoCodeConstAbsIntExpr (hie2); /* Combine the two */ Expr->IVal = (Expr->IVal && Expr2.IVal); @@ -3171,16 +3186,14 @@ static void hieOrPP (ExprDesc *Expr) ** called recursively from the preprocessor. */ { - ExprDesc Expr2; - - ConstAbsIntExpr (hieAndPP, Expr); + *Expr = NoCodeConstAbsIntExpr (hieAndPP); while (CurTok.Tok == TOK_BOOL_OR) { /* Skip the && */ NextToken (); /* Get rhs */ - ConstAbsIntExpr (hieAndPP, &Expr2); + ExprDesc Expr2 = NoCodeConstAbsIntExpr (hieAndPP); /* Combine the two */ Expr->IVal = (Expr->IVal || Expr2.IVal); @@ -3189,61 +3202,133 @@ static void hieOrPP (ExprDesc *Expr) -static void hieAnd (ExprDesc* Expr, unsigned TrueLab, int* BoolOp) -/* Process "exp && exp" */ +static int hieAnd (ExprDesc* Expr, unsigned* TrueLab, int* TrueLabAllocated) +/* Process "exp && exp". This should only be called within hieOr. +** Return true if logic AND does occur. +*/ { - int FalseLab; - ExprDesc Expr2; + unsigned Flags = Expr->Flags & E_MASK_KEEP_SUBEXPR; + int HasFalseJump = 0, HasTrueJump = 0; + CodeMark Start; + /* Get a label that we will use for false expressions */ + int FalseLab = GetLocalLabel (); + + /* Get lhs */ + GetCodePos (&Start); ExprWithCheck (hie2, Expr); + if ((Flags & E_EVAL_UNEVAL) == E_EVAL_UNEVAL) { + RemoveCode (&Start); + } + if (CurTok.Tok == TOK_BOOL_AND) { - /* Tell our caller that we're evaluating a boolean */ - *BoolOp = 1; + ExprDesc Expr2; - /* Get a label that we will use for false expressions */ - FalseLab = GetLocalLabel (); + /* 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_IsConstAbs (Expr)) { + /* Set the test flag */ + ED_RequireTest (Expr); - /* If the expr hasn't set condition codes, set the force-test flag */ - if (!ED_IsTested (Expr)) { - ED_MarkForTest (Expr); + /* Load the value */ + LoadExpr (CF_FORCECHAR, Expr); + + /* Remember that the jump is used */ + HasFalseJump = 1; + + /* Generate the jump */ + g_falsejump (CF_NONE, FalseLab); + } else if (Expr->IVal == 0) { + /* Skip remaining */ + Flags |= E_EVAL_UNEVAL; + } } - /* Load the value */ - LoadExpr (CF_FORCECHAR, Expr); - - /* Generate the jump */ - g_falsejump (CF_NONE, FalseLab); - /* Parse more boolean and's */ while (CurTok.Tok == TOK_BOOL_AND) { + ED_Init (&Expr2); + Expr2.Flags = Flags; + /* Skip the && */ NextToken (); /* Get rhs */ + GetCodePos (&Start); hie2 (&Expr2); - if (!ED_IsTested (&Expr2)) { - ED_MarkForTest (&Expr2); + if ((Flags & E_EVAL_UNEVAL) == E_EVAL_UNEVAL) { + RemoveCode (&Start); } - LoadExpr (CF_FORCECHAR, &Expr2); - /* Do short circuit evaluation */ - if (CurTok.Tok == TOK_BOOL_AND) { - g_falsejump (CF_NONE, FalseLab); - } else { - /* Last expression - will evaluate to true */ - g_truejump (CF_NONE, TrueLab); + /* 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_IsConstAbs (&Expr2)) { + ED_RequireTest (&Expr2); + LoadExpr (CF_FORCECHAR, &Expr2); + + /* Do short circuit evaluation */ + if (CurTok.Tok == TOK_BOOL_AND) { + HasFalseJump = 1; + g_falsejump (CF_NONE, FalseLab); + } else { + /* We need the true label for the last expression */ + HasTrueJump = 1; + } + } else if (Expr2.IVal == 0) { + /* Skip remaining */ + Flags |= E_EVAL_UNEVAL; + /* The value of the expression will be false */ + ED_MakeConstBool (Expr, 0); + } } } - /* Define the false jump label here */ - g_defcodelabel (FalseLab); + /* Last expression */ + if ((Flags & E_EVAL_UNEVAL) != E_EVAL_UNEVAL) { + if (HasFalseJump || HasTrueJump) { + if (*TrueLabAllocated == 0) { + /* Get a label that we will use for true expressions */ + *TrueLab = GetLocalLabel (); + *TrueLabAllocated = 1; + } + if (!ED_IsConstAbs (&Expr2)) { + /* Will branch to true and fall to false */ + g_truejump (CF_NONE, *TrueLab); + } else { + /* Will jump away */ + g_jump (*TrueLab); + } + /* The result is an rvalue in primary */ + ED_FinalizeRValLoad (Expr); + /* No need to test as the result will be jumped to */ + ED_TestDone (Expr); + } + } - /* The result is an rvalue in primary */ - ED_FinalizeRValLoad (Expr); - ED_TestDone (Expr); /* Condition codes are set */ + if (HasFalseJump) { + /* Define the false jump label here */ + g_defcodelabel (FalseLab); + } + + /* Convert to bool */ + if (ED_IsConstAbs (Expr) && Expr->IVal != 0) { + ED_MakeConstBool (Expr, 1); + } else { + Expr->Type = type_bool; + } + + /* Tell our caller that we're evaluating a boolean */ + return 1; } + + return 0; } @@ -3251,71 +3336,127 @@ static void hieAnd (ExprDesc* Expr, unsigned TrueLab, int* BoolOp) static void hieOr (ExprDesc *Expr) /* Process "exp || exp". */ { - ExprDesc Expr2; - int BoolOp = 0; /* Did we have a boolean op? */ - int AndOp; /* Did we have a && operation? */ + unsigned Flags = Expr->Flags & E_MASK_KEEP_SUBEXPR; + int AndOp; /* Did we have a && operation? */ unsigned TrueLab; /* Jump to this label if true */ unsigned DoneLab; - - /* Get a label */ - TrueLab = GetLocalLabel (); + int HasTrueJump = 0; + CodeMark Start; /* Call the next level parser */ - hieAnd (Expr, TrueLab, &BoolOp); + GetCodePos (&Start); + AndOp = hieAnd (Expr, &TrueLab, &HasTrueJump); + if ((Flags & E_EVAL_UNEVAL) == E_EVAL_UNEVAL) { + RemoveCode (&Start); + } /* Any boolean or's? */ if (CurTok.Tok == TOK_BOOL_OR) { - /* If the expr hasn't set condition codes, set the force-test flag */ - if (!ED_IsTested (Expr)) { - ED_MarkForTest (Expr); + /* 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_IsConstAbs (Expr)) { + /* Test the lhs if we haven't had && operators. If we had them, the + ** jump is already in place and there's no need to do the test. + */ + if (!AndOp) { + /* Set the test flag */ + ED_RequireTest (Expr); + + /* Get first expr */ + LoadExpr (CF_FORCECHAR, Expr); + + if (HasTrueJump == 0) { + /* Get a label that we will use for true expressions */ + TrueLab = GetLocalLabel(); + HasTrueJump = 1; + } + + /* Jump to TrueLab if true */ + g_truejump (CF_NONE, TrueLab); + } + } else if (Expr->IVal != 0) { + /* Skip remaining */ + Flags |= E_EVAL_UNEVAL; + } } - /* Get first expr */ - LoadExpr (CF_FORCECHAR, Expr); - - /* For each expression jump to TrueLab if true. Beware: If we - ** had && operators, the jump is already in place! - */ - if (!BoolOp) { - g_truejump (CF_NONE, TrueLab); - } - - /* Remember that we had a boolean op */ - BoolOp = 1; - /* while there's more expr */ while (CurTok.Tok == TOK_BOOL_OR) { + ExprDesc Expr2; + ED_Init (&Expr2); + Expr2.Flags = Flags; + /* skip the || */ NextToken (); - /* Get a subexpr */ - AndOp = 0; - hieAnd (&Expr2, TrueLab, &AndOp); - if (!ED_IsTested (&Expr2)) { - ED_MarkForTest (&Expr2); + /* Get rhs subexpression */ + GetCodePos (&Start); + AndOp = hieAnd (&Expr2, &TrueLab, &HasTrueJump); + if ((Flags & E_EVAL_UNEVAL) == E_EVAL_UNEVAL) { + RemoveCode (&Start); } - LoadExpr (CF_FORCECHAR, &Expr2); - /* If there is more to come, add shortcut boolean eval. */ - g_truejump (CF_NONE, TrueLab); + /* 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_IsConstAbs (&Expr2)) { + /* If there is more to come, add shortcut boolean eval */ + if (!AndOp) { + ED_RequireTest (&Expr2); + LoadExpr (CF_FORCECHAR, &Expr2); + + if (HasTrueJump == 0) { + TrueLab = GetLocalLabel(); + HasTrueJump = 1; + } + g_truejump (CF_NONE, TrueLab); + } + } else if (Expr2.IVal != 0) { + /* Skip remaining */ + Flags |= E_EVAL_UNEVAL; + /* The result is always true */ + ED_MakeConstBool (Expr, 1); + } + } } - /* The result is an rvalue in primary */ - ED_FinalizeRValLoad (Expr); - ED_TestDone (Expr); /* Condition codes are set */ + /* Convert to bool */ + if (ED_IsConstAbs (Expr) && Expr->IVal != 0) { + ED_MakeConstBool (Expr, 1); + } else { + Expr->Type = type_bool; + } } - /* If we really had boolean ops, generate the end sequence */ - if (BoolOp) { + /* If we really had boolean ops, generate the end sequence if necessary */ + if (HasTrueJump) { + /* False case needs to jump over true case */ DoneLab = GetLocalLabel (); - g_getimmed (CF_INT | CF_CONST, 0, 0); /* Load FALSE */ - g_falsejump (CF_NONE, DoneLab); + if ((Flags & E_EVAL_UNEVAL) != E_EVAL_UNEVAL) { + /* Load false only if the result is not true */ + g_getimmed (CF_INT | CF_CONST, 0, 0); /* Load FALSE */ + g_falsejump (CF_NONE, DoneLab); + } + + /* Load the true value */ g_defcodelabel (TrueLab); g_getimmed (CF_INT | CF_CONST, 1, 0); /* Load TRUE */ g_defcodelabel (DoneLab); + + /* The result is an rvalue in primary */ + ED_FinalizeRValLoad (Expr); + /* Condition codes are set */ + ED_TestDone (Expr); } } @@ -3326,6 +3467,7 @@ static void hieQuest (ExprDesc* Expr) { int FalseLab; int TrueLab; + CodeMark SkippedBranch; CodeMark TrueCodeEnd; ExprDesc Expr2; /* Expression 2 */ ExprDesc Expr3; /* Expression 3 */ @@ -3333,7 +3475,6 @@ static void hieQuest (ExprDesc* Expr) int Expr3IsNULL; /* Expression 3 is a NULL pointer */ Type* ResultType; /* Type of result */ - /* Call the lower level eval routine */ if (Preprocessing) { ExprWithCheck (hieOrPP, Expr); @@ -3343,14 +3484,27 @@ static void hieQuest (ExprDesc* Expr) /* Check if it's a ternary expression */ if (CurTok.Tok == TOK_QUEST) { + + int ConstantCond = ED_IsConstAbsInt (Expr); + unsigned Flags = Expr->Flags & E_MASK_KEEP_RESULT; + + ED_Init (&Expr2); + Expr2.Flags = Flags; + ED_Init (&Expr3); + Expr3.Flags = Flags; + NextToken (); - if (!ED_IsTested (Expr)) { + + if (!ConstantCond) { /* Condition codes not set, request a test */ - ED_MarkForTest (Expr); + ED_RequireTest (Expr); + LoadExpr (CF_NONE, Expr); + FalseLab = GetLocalLabel (); + g_falsejump (CF_NONE, FalseLab); + } else if (Expr->IVal == 0) { + /* Remember the current code position */ + GetCodePos (&SkippedBranch); } - LoadExpr (CF_NONE, Expr); - FalseLab = GetLocalLabel (); - g_falsejump (CF_NONE, FalseLab); /* Parse second expression. Remember for later if it is a NULL pointer ** expression, then load it into the primary. @@ -3358,22 +3512,37 @@ static void hieQuest (ExprDesc* Expr) ExprWithCheck (hie1, &Expr2); Expr2IsNULL = ED_IsNullPtr (&Expr2); if (!IsTypeVoid (Expr2.Type)) { - /* Load it into the primary */ - LoadExpr (CF_NONE, &Expr2); - ED_FinalizeRValLoad (&Expr2); + if (!ConstantCond || !ED_IsConst (&Expr2)) { + /* Load it into the primary */ + LoadExpr (CF_NONE, &Expr2); + ED_FinalizeRValLoad (&Expr2); + } Expr2.Type = PtrConversion (Expr2.Type); } - /* Remember the current code position */ - GetCodePos (&TrueCodeEnd); + if (!ConstantCond) { + /* Remember the current code position */ + GetCodePos (&TrueCodeEnd); - /* Jump around the evaluation of the third expression */ - TrueLab = GetLocalLabel (); - ConsumeColon (); - g_jump (TrueLab); + /* Jump around the evaluation of the third expression */ + TrueLab = GetLocalLabel (); - /* Jump here if the first expression was false */ - g_defcodelabel (FalseLab); + ConsumeColon (); + + g_jump (TrueLab); + + /* Jump here if the first expression was false */ + g_defcodelabel (FalseLab); + } else { + if (Expr->IVal == 0) { + /* 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. @@ -3381,12 +3550,19 @@ static void hieQuest (ExprDesc* Expr) ExprWithCheck (hie1, &Expr3); Expr3IsNULL = ED_IsNullPtr (&Expr3); if (!IsTypeVoid (Expr3.Type)) { - /* Load it into the primary */ - LoadExpr (CF_NONE, &Expr3); - ED_FinalizeRValLoad (&Expr3); + if (!ConstantCond || !ED_IsConst (&Expr3)) { + /* Load it into the primary */ + LoadExpr (CF_NONE, &Expr3); + ED_FinalizeRValLoad (&Expr3); + } Expr3.Type = PtrConversion (Expr3.Type); } + if (ConstantCond && Expr->IVal != 0) { + /* 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 @@ -3419,9 +3595,11 @@ static void hieQuest (ExprDesc* Expr) TypeConversion (&Expr2, ResultType); GetCodePos (&CvtCodeEnd); - /* If we had conversion code, move it to the right place */ - if (!CodeRangeIsEmpty (&CvtCodeStart, &CvtCodeEnd)) { - MoveCode (&CvtCodeStart, &CvtCodeEnd, &TrueCodeEnd); + 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)) { @@ -3441,16 +3619,33 @@ static void hieQuest (ExprDesc* Expr) /* Result type is void */ ResultType = Expr3.Type; } else { - TypeCompatibilityDiagnostic (Expr2.Type, Expr3.Type, 1, - "Incompatible types in ternary '%s' with '%s'"); - ResultType = Expr2.Type; /* Doesn't matter here */ + if (IsClassStruct (Expr2.Type) && IsClassStruct (Expr3.Type) && + TypeCmp (Expr2.Type, Expr3.Type) == TC_IDENTICAL) { + /* Result type is struct/union */ + ResultType = Expr2.Type; + } else { + TypeCompatibilityDiagnostic (Expr2.Type, Expr3.Type, 1, + "Incompatible types in ternary '%s' with '%s'"); + ResultType = Expr2.Type; /* Doesn't matter here */ + } } - /* Define the final label */ - g_defcodelabel (TrueLab); + 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 */ - ED_FinalizeRValLoad (Expr); Expr->Type = ResultType; } } @@ -3460,7 +3655,6 @@ static void hieQuest (ExprDesc* Expr) static void opeq (const GenDesc* Gen, ExprDesc* Expr, const char* Op) /* Process "op=" operators. */ { - ExprDesc Expr2; unsigned flags; CodeMark Mark; int MustScale; @@ -3501,6 +3695,10 @@ static void opeq (const GenDesc* Gen, ExprDesc* Expr, const char* Op) GetCodePos (&Mark); g_push (flags, 0); + ExprDesc Expr2; + ED_Init (&Expr2); + Expr2.Flags |= Expr->Flags & E_MASK_KEEP_SUBEXPR; + /* Evaluate the rhs */ MarkedExprWithCheck (hie1, &Expr2); @@ -3581,6 +3779,8 @@ static void addsubeq (const GenDesc* Gen, ExprDesc *Expr, const char* Op) unsigned rflags; int MustScale; + ED_Init (&Expr2); + Expr2.Flags |= Expr->Flags & E_MASK_KEEP_SUBEXPR; /* We're currently only able to handle some addressing modes */ if (ED_GetLoc (Expr) == E_LOC_EXPR || ED_GetLoc (Expr) == E_LOC_PRIMARY) { @@ -3774,8 +3974,33 @@ void hie1 (ExprDesc* Expr) 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) { + /* If the expression didn't generate code or isn't cast to type void, + ** emit a warning. + */ + GetCodePos (&End); + if (!ED_MayHaveNoEffect (Expr) && + CodeRangeIsEmpty (&Start, &End) && + IS_Get (&WarnNoEffect) && + PrevErrorCount == ErrorCount) { + Warning ("Expression result unused"); + } + + PrevErrorCount = ErrorCount; + /* Remember the current code position */ + GetCodePos (&Start); + + /* Reset the expression */ + ED_Init (Expr); + Expr->Flags = Flags; NextToken (); hie1 (Expr); } @@ -3809,24 +4034,17 @@ int evalexpr (unsigned Flags, void (*Func) (ExprDesc*), ExprDesc* Expr) void Expression0 (ExprDesc* Expr) /* Evaluate an expression via hie0 and put the result into the primary register */ { + unsigned Flags = Expr->Flags & E_MASK_KEEP_RESULT; + + /* Only check further after the expression is evaluated */ ExprWithCheck (hie0, Expr); - LoadExpr (CF_NONE, Expr); -} + if ((Expr->Flags & Flags & E_MASK_EVAL) != (Flags & E_MASK_EVAL)) { + Internal ("Expression flags tampered: %08X", Flags); + } - -void ConstExpr (void (*Func) (ExprDesc*), ExprDesc* Expr) -/* Will evaluate an expression via the given function. If the result is not -** a constant of some sort, a diagnostic will be printed, and the value is -** replaced by a constant one to make sure there are no internal errors that -** result from this input error. -*/ -{ - ExprWithCheck (Func, Expr); - if (!ED_IsConst (Expr)) { - Error ("Constant expression expected"); - /* To avoid any compiler errors, make the expression a valid const */ - ED_MakeConstAbsInt (Expr, 1); + if (ED_YetToLoad (Expr)) { + LoadExpr (CF_NONE, Expr); } } @@ -3841,25 +4059,56 @@ void BoolExpr (void (*Func) (ExprDesc*), ExprDesc* Expr) { ExprWithCheck (Func, Expr); if (!ED_IsBool (Expr)) { - Error ("Boolean expression expected"); + Error ("Scalar expression expected"); /* To avoid any compiler errors, make the expression a valid int */ - ED_MakeConstAbsInt (Expr, 1); + ED_MakeConstBool (Expr, 1); } } -void ConstAbsIntExpr (void (*Func) (ExprDesc*), ExprDesc* Expr) -/* Will evaluate an expression via the given function. If the result is not -** a constant numeric integer value, a diagnostic will be printed, and the -** value is replaced by a constant one to make sure there are no internal -** errors that result from this input error. +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. */ { - ExprWithCheck (Func, Expr); - if (!ED_IsConstAbsInt (Expr)) { + 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 */ + ED_MakeConstAbsInt (&Expr, 1); + } + + /* 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); + ED_MakeConstAbsInt (&Expr, 1); } + + /* Return by value */ + return Expr; } diff --git a/src/cc65/expr.h b/src/cc65/expr.h index a6c183cbd..806a376bb 100644 --- a/src/cc65/expr.h +++ b/src/cc65/expr.h @@ -54,13 +54,6 @@ int evalexpr (unsigned flags, void (*Func) (ExprDesc*), ExprDesc* Expr); void Expression0 (ExprDesc* Expr); /* Evaluate an expression via hie0 and put the result into the primary register */ -void ConstExpr (void (*Func) (ExprDesc*), ExprDesc* Expr); -/* Will evaluate an expression via the given function. If the result is not -** a constant of some sort, a diagnostic will be printed, and the value is -** replaced by a constant one to make sure there are no internal errors that -** result from this input error. -*/ - 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 @@ -68,11 +61,18 @@ void BoolExpr (void (*Func) (ExprDesc*), ExprDesc* Expr); ** are no internal errors that result from this input error. */ -void ConstAbsIntExpr (void (*Func) (ExprDesc*), ExprDesc* Expr); -/* Will evaluate an expression via the given function. If the result is not -** a constant numeric integer value, a diagnostic will be printed, and the -** value is replaced by a constant one to make sure there are no internal -** errors that result from this input error. +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 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. */ void hie10 (ExprDesc* lval); diff --git a/src/cc65/exprdesc.c b/src/cc65/exprdesc.c index 5ff848fb7..ac19873e7 100644 --- a/src/cc65/exprdesc.c +++ b/src/cc65/exprdesc.c @@ -58,7 +58,7 @@ ExprDesc* ED_Init (ExprDesc* Expr) { Expr->Sym = 0; Expr->Type = 0; - Expr->Flags = 0; + Expr->Flags = E_NEED_EAX; Expr->Name = 0; Expr->IVal = 0; Expr->FVal = FP_D_Make (0.0); @@ -113,6 +113,24 @@ int ED_IsIndExpr (const ExprDesc* Expr) +int ED_YetToLoad (const ExprDesc* Expr) +/* Check if the expression needs to be loaded somehow. */ +{ + return ED_NeedsPrimary (Expr) || + ED_YetToTest (Expr) || + (ED_IsLVal (Expr) && IsQualVolatile (Expr->Type)); +} + + + +void ED_MarkForUneval (ExprDesc* Expr) +/* Mark the expression as not to be evaluated */ +{ + Expr->Flags = (Expr->Flags & ~E_MASK_EVAL) | E_EVAL_UNEVAL; +} + + + void ED_SetCodeRange (ExprDesc* Expr, const CodeMark* Start, const CodeMark* End) /* Set the code range for this expression */ { @@ -206,7 +224,7 @@ ExprDesc* ED_MakeConstAbs (ExprDesc* Expr, long Value, Type* Type) { Expr->Sym = 0; Expr->Type = Type; - Expr->Flags = E_LOC_NONE | E_RTYPE_RVAL | (Expr->Flags & E_HAVE_MARKS); + Expr->Flags = E_LOC_NONE | E_RTYPE_RVAL | (Expr->Flags & E_MASK_KEEP_MAKE); Expr->Name = 0; Expr->IVal = Value; Expr->FVal = FP_D_Make (0.0); @@ -220,6 +238,20 @@ ExprDesc* ED_MakeConstAbsInt (ExprDesc* Expr, long Value) { Expr->Sym = 0; Expr->Type = type_int; + Expr->Flags = E_LOC_NONE | E_RTYPE_RVAL | (Expr->Flags & E_MASK_KEEP_MAKE); + Expr->Name = 0; + Expr->IVal = Value; + Expr->FVal = FP_D_Make (0.0); + return Expr; +} + + + +ExprDesc* ED_MakeConstBool (ExprDesc* Expr, long Value) +/* Replace Expr with a constant boolean expression with the given value */ +{ + Expr->Sym = 0; + Expr->Type = type_bool; Expr->Flags = E_LOC_NONE | E_RTYPE_RVAL | (Expr->Flags & E_HAVE_MARKS); Expr->Name = 0; Expr->IVal = Value; @@ -234,7 +266,7 @@ ExprDesc* ED_FinalizeRValLoad (ExprDesc* Expr) { Expr->Sym = 0; Expr->Flags &= ~(E_MASK_LOC | E_MASK_RTYPE | E_BITFIELD | E_ADDRESS_OF); - Expr->Flags &= ~(E_NEED_TEST | E_CC_SET); + Expr->Flags &= ~E_CC_SET; Expr->Flags |= (E_LOC_PRIMARY | E_RTYPE_RVAL); Expr->Name = 0; Expr->IVal = 0; /* No offset */ @@ -398,7 +430,8 @@ int ED_IsBool (const ExprDesc* Expr) /* Either ints, floats, or pointers can be used in a boolean context */ return IsClassInt (Expr->Type) || IsClassFloat (Expr->Type) || - IsClassPtr (Expr->Type); + IsClassPtr (Expr->Type) || + IsClassFunc (Expr->Type); } diff --git a/src/cc65/exprdesc.h b/src/cc65/exprdesc.h index 1541c2e4e..f04be6a8d 100644 --- a/src/cc65/exprdesc.h +++ b/src/cc65/exprdesc.h @@ -65,16 +65,16 @@ enum { ** - E_LOC_ refers to any other than E_LOC_NONE and E_LOC_PRIMARY. ** - E_LOC_EXPR can be regarded as a generalized E_LOC_. ** - E_LOC_NONE can be regarded as E_LOC_PRIMARY + E_ADDRESS_OF unless - ** remarked otherwise (see below). + ** remarked otherwise (see below). ** - An E_LOC_NONE value is not considered to be an "address". ** - ref-load doesn't change the location, while rval-load puts into the - ** primary register a "temporary" that is the straight integer rvalue or - ** a "delegate" to the real rvalue somewhere else. + ** primary register a "temporary" that is the straight integer rvalue or + ** a "delegate" to the real rvalue somewhere else. ** - ref-load doesn't change the rval/lval category of the expression, - ** while rval-load converts it to an rvalue if it wasn't. + ** while rval-load converts it to an rvalue if it wasn't. ** - In practice, ref-load is unimplemented, and can be simulated with - ** adding E_ADDRESS_OF temporaily through LoadExpr + FinalizeLoad, - ** whilst val-load is done with LoadExpr + FinalizeRValLoad. + ** adding E_ADDRESS_OF temporaily through LoadExpr + FinalizeLoad, + ** whilst val-load is done with LoadExpr + FinalizeRValLoad. ** ** E_LOC_NONE -- ref-load -> + E_LOADED (int rvalue) ** E_LOC_PRIMARY -- ref-load -> + E_LOADED (unchanged) @@ -94,7 +94,7 @@ enum { ** E_LOC_ -- dereference -> E_LOC_EXPR (pointed-to-value, must load) ** + E_ADDRESS_OF -- dereference -> (lvalue reference) */ - E_MASK_LOC = 0x00FF, + E_MASK_LOC = 0x01FF, E_LOC_NONE = 0x0000, /* Pure rvalue with no storage */ E_LOC_ABS = 0x0001, /* Absolute numeric addressed variable */ E_LOC_GLOBAL = 0x0002, /* Global variable */ @@ -104,29 +104,92 @@ enum { E_LOC_PRIMARY = 0x0020, /* Temporary in primary register */ E_LOC_EXPR = 0x0040, /* A location that the primary register points to */ E_LOC_LITERAL = 0x0080, /* Literal in the literal pool */ + E_LOC_CODE = 0x0100, /* C code label location (&&Label) */ - /* Constant location of some sort (only if rval) */ + /* Immutable location addresses (immutable bases and offsets) */ E_LOC_CONST = E_LOC_NONE | E_LOC_ABS | E_LOC_GLOBAL | E_LOC_STATIC | - E_LOC_REGISTER | E_LOC_LITERAL, + E_LOC_REGISTER | E_LOC_LITERAL | E_LOC_CODE, + + /* Not-so-immutable location addresses (stack offsets may change dynamically) */ + E_LOC_QUASICONST = E_LOC_CONST | E_LOC_STACK, + + /* Expression type modifiers */ + E_BITFIELD = 0x0200, /* Expression is a bit-field */ + E_ADDRESS_OF = 0x0400, /* Expression is the address of the lvalue */ /* lvalue/rvalue in C language's sense */ - E_MASK_RTYPE = 0x0100, + E_MASK_RTYPE = 0x0800, E_RTYPE_RVAL = 0x0000, - E_RTYPE_LVAL = 0x0100, + E_RTYPE_LVAL = 0x0800, - /* Bit-field? */ - E_BITFIELD = 0x0200, + /* Expression status */ + E_LOADED = 0x1000, /* Expression is loaded in primary */ + E_CC_SET = 0x2000, /* Condition codes are set */ + E_HAVE_MARKS = 0x4000, /* Code marks are valid */ - /* Test */ - E_NEED_TEST = 0x0400, /* Expression needs a test to set cc */ - E_CC_SET = 0x0800, /* Condition codes are set */ + /* Optimization hints */ + E_MASK_NEED = 0x030000, + E_NEED_EAX = 0x000000, /* Expression result needs to be loaded in Primary */ + E_NEED_NONE = 0x010000, /* Expression result is unused */ + E_NEED_TEST = 0x020000, /* Expression needs a test to set cc */ - E_HAVE_MARKS = 0x1000, /* Code marks are valid */ + /* Expression evaluation requirements. + ** Usage: (Flags & E_EVAL_) == E_EVAL_ + ** + ** Remark: + ** - Expression result, that is the "final value" of the expression, is no + ** more than one of the effects of the whole expression. Effects other + ** than it are usually consided "side-effects" in this regard. + ** - The compiler front end cannot know things determined by the linker, + ** such as the actual address of an object with static storage. What it + ** can know is categorized as "compiler-known" here. + ** - The concept "immutable" here means that once something is determined + ** (not necessarily by the compiler), it will never change. This is not + ** the same meaning as the "constant" word in the C standard. + ** - The concept "compile-time" ( not to be confued with "compiler-known"), + ** or "static" (compared to "run-time" as in "_Static_assert" in C, not + ** to be confused with the "static" storage) here means that something + ** has no run-time behaviors, enforced by the fact that it generates no + ** target code (hence "no-code"). It is closely related to the concepts + ** above but not the same. + ** - An "unevaluated" expression is special and different from the above: + ** while it generates no code, cannot change its "value" (it actually has + ** no value), and must be completely handled by the compiler front-end, + ** it is unique in that it is not "evaluated" while the others are, and + ** the codegen routine of such an expression is usually separated from + ** the normally evaluated ones. Therefore it is treated differently from + ** the above and uses a separate flag that implies none of the above. + ** - The "maybe-unused" flag is to suppress the checking and warning on + ** expressions with no effects. It doesn't have any special meanings + ** beyond that, and is independent from the E_NEED_s. All + ** "unevaluated" expressions are flagged as "maybe-unused" just to + ** avoid unnecessary warnings. + ** + ** Relationship of some concepts: + ** - "no-code" implies "no-side-effects" + ** - "immutable" = "compiler-known" OR "no-code" + ** - "constant expression" in C = "compiler-known" AND "no-code", with minor differences + */ + E_MASK_EVAL = 0xFC0000, + E_EVAL_NONE = 0x000000, /* No requirements */ + E_EVAL_IMMUTABLE_RESULT = 0x040000, /* Expression result must be immutable */ + E_EVAL_COMPILER_KNOWN = 0x0C0000, /* Expression result must be known to the compiler */ + E_EVAL_NO_SIDE_EFFECTS = 0x100000, /* Evaluation must have no side effects */ + E_EVAL_NO_CODE = 0x340000, /* Evaluation must generate no code */ + E_EVAL_MAYBE_UNUSED = 0x400000, /* Expression result may be unused */ + E_EVAL_UNEVAL = 0xC00000, /* Expression is unevaluated */ - E_LOADED = 0x4000, /* Expression is loaded in primary */ + /* Expression result must be known to the compiler and generate no code to load */ + E_EVAL_C_CONST = E_EVAL_COMPILER_KNOWN | E_EVAL_NO_CODE, - E_ADDRESS_OF = 0x8000, /* Expression is the address of the lvalue */ + /* Flags to keep in subexpressions of most operations other than ternary */ + E_MASK_KEEP_SUBEXPR = E_MASK_EVAL, + /* Flags to keep for the two result subexpressions of the ternary operation */ + E_MASK_KEEP_RESULT = E_MASK_NEED | E_MASK_EVAL, + + /* Flags to keep when using the ED_Make functions */ + E_MASK_KEEP_MAKE = E_HAVE_MARKS | E_MASK_KEEP_RESULT, }; /* Forward */ @@ -292,13 +355,33 @@ void ED_MakeBitField (ExprDesc* Expr, unsigned BitOffs, unsigned BitWidth); /* Make this expression a bit field expression */ #if defined(HAVE_INLINE) -INLINE void ED_MarkForTest (ExprDesc* Expr) +INLINE void ED_RequireTest (ExprDesc* Expr) /* Mark the expression for a test. */ { Expr->Flags |= E_NEED_TEST; } #else -# define ED_MarkForTest(Expr) do { (Expr)->Flags |= E_NEED_TEST; } while (0) +# define ED_RequireTest(Expr) do { (Expr)->Flags |= E_NEED_TEST; } while (0) +#endif + +#if defined(HAVE_INLINE) +INLINE int ED_GetNeeds (const ExprDesc* Expr) +/* Get flags about what the expression needs. */ +{ + return (Expr->Flags & E_MASK_NEED); +} +#else +# define ED_GetNeeds(Expr) ((Expr)->Flags & E_MASK_NEED) +#endif + +#if defined(HAVE_INLINE) +INLINE int ED_NeedsPrimary (const ExprDesc* Expr) +/* Check if the expression needs to be in Primary. */ +{ + return (Expr->Flags & E_MASK_NEED) == E_NEED_EAX; +} +#else +# define ED_NeedsPrimary(Expr) (((Expr)->Flags & E_MASK_NEED) == E_NEED_EAX) #endif #if defined(HAVE_INLINE) @@ -311,15 +394,25 @@ INLINE int ED_NeedsTest (const ExprDesc* Expr) # define ED_NeedsTest(Expr) (((Expr)->Flags & E_NEED_TEST) != 0) #endif +#if defined(HAVE_INLINE) +INLINE int ED_YetToTest (const ExprDesc* Expr) +/* Check if the expression needs to be tested but not yet. */ +{ + return ((Expr)->Flags & (E_NEED_TEST | E_CC_SET)) == E_NEED_TEST; +} +#else +# define ED_YetToTest(Expr) (((Expr)->Flags & (E_NEED_TEST | E_CC_SET)) == E_NEED_TEST) +#endif + #if defined(HAVE_INLINE) INLINE void ED_TestDone (ExprDesc* Expr) /* Mark the expression as tested and condition codes set. */ { - Expr->Flags = (Expr->Flags & ~E_NEED_TEST) | E_CC_SET; + Expr->Flags |= E_CC_SET; } #else # define ED_TestDone(Expr) \ - do { (Expr)->Flags = ((Expr)->Flags & ~E_NEED_TEST) | E_CC_SET; } while (0) + do { (Expr)->Flags |= E_CC_SET; } while (0) #endif #if defined(HAVE_INLINE) @@ -354,6 +447,42 @@ INLINE int ED_IsLoaded (const ExprDesc* Expr) # define ED_IsLoaded(Expr) (((Expr)->Flags & E_LOADED) != 0) #endif +int ED_YetToLoad (const ExprDesc* Expr); +/* Check if the expression is yet to be loaded somehow. */ + +#if defined(HAVE_INLINE) +INLINE int ED_NeedsConst (const ExprDesc* Expr) +/* Check if the expression need be immutable */ +{ + return (Expr->Flags & E_EVAL_IMMUTABLE_RESULT) == E_EVAL_IMMUTABLE_RESULT; +} +#else +# define ED_NeedsConst(Expr) (((Expr)->Flags & E_EVAL_IMMUTABLE_RESULT) == E_EVAL_IMMUTABLE_RESULT) +#endif + +void ED_MarkForUneval (ExprDesc* Expr); +/* Mark the expression as not to be evaluated */ + +#if defined(HAVE_INLINE) +INLINE int ED_MayBeUneval (const ExprDesc* Expr) +/* Check if the expression may be uevaluated */ +{ + return (Expr->Flags & E_EVAL_UNEVAL) == E_EVAL_UNEVAL; +} +#else +# define ED_MayBeUneval(Expr) (((Expr)->Flags & E_EVAL_UNEVAL) == E_EVAL_UNEVAL) +#endif + +#if defined(HAVE_INLINE) +INLINE int ED_MayHaveNoEffect (const ExprDesc* Expr) +/* Check if the expression may be present without effects */ +{ + return (Expr->Flags & E_EVAL_MAYBE_UNUSED) == E_EVAL_MAYBE_UNUSED; +} +#else +# define ED_MayHaveNoEffect(Expr) (((Expr)->Flags & E_EVAL_MAYBE_UNUSED) == E_EVAL_MAYBE_UNUSED) +#endif + #if defined(HAVE_INLINE) INLINE int ED_IsLocPrimaryOrExpr (const ExprDesc* Expr) /* Return true if the expression is E_LOC_PRIMARY or E_LOC_EXPR */ @@ -411,6 +540,9 @@ ExprDesc* ED_MakeConstAbs (ExprDesc* Expr, long Value, Type* Type); ExprDesc* ED_MakeConstAbsInt (ExprDesc* Expr, long Value); /* Replace Expr with an constant integer with the given value */ +ExprDesc* ED_MakeConstBool (ExprDesc* Expr, long Value); +/* Replace Expr with a constant boolean expression with the given value */ + ExprDesc* ED_FinalizeRValLoad (ExprDesc* Expr); /* Finalize the result of LoadExpr to be an rvalue in the primary register */ @@ -519,7 +651,7 @@ int ED_IsNullPtr (const ExprDesc* Expr); int ED_IsBool (const ExprDesc* Expr); /* Return true of the expression can be treated as a boolean, that is, it can -** be an operand to a compare operation. +** be an operand to a compare operation with 0/NULL. */ void PrintExprDesc (FILE* F, ExprDesc* Expr); diff --git a/src/cc65/goto.c b/src/cc65/goto.c index f1fb725d2..06364068f 100644 --- a/src/cc65/goto.c +++ b/src/cc65/goto.c @@ -80,6 +80,8 @@ void GotoStatement (void) CodeEntry *E; unsigned char val; + ED_Init (&desc); + NextToken (); /* arr[foo], we only support simple foo for now */ diff --git a/src/cc65/loadexpr.c b/src/cc65/loadexpr.c index 07f88acbc..17b0dd0fe 100644 --- a/src/cc65/loadexpr.c +++ b/src/cc65/loadexpr.c @@ -149,7 +149,7 @@ void LoadExpr (unsigned Flags, struct ExprDesc* Expr) Flags |= TypeOf (Expr->Type); } - if (ED_NeedsTest (Expr)) { + if (ED_YetToTest (Expr)) { /* If we're only testing, we do not need to promote char to int. ** CF_FORCECHAR does nothing if the type is not CF_CHAR. */ @@ -238,7 +238,7 @@ void LoadExpr (unsigned Flags, struct ExprDesc* Expr) */ CHECK (Expr->BitOffs < CHAR_BITS); - if (ED_NeedsTest (Expr)) { + if (ED_YetToTest (Expr)) { g_testbitfield (Flags, Expr->BitOffs, Expr->BitWidth); } else { g_extractbitfield (Flags, BitFieldFullWidthFlags, IsSignSigned (Expr->Type), @@ -256,7 +256,7 @@ void LoadExpr (unsigned Flags, struct ExprDesc* Expr) LoadAddress (Flags, Expr); /* Are we testing this value? */ - if (ED_NeedsTest (Expr)) { + if (ED_YetToTest (Expr)) { /* Yes, force a test */ g_test (Flags); ED_TestDone (Expr); diff --git a/src/cc65/locals.c b/src/cc65/locals.c index 4323943a1..255a23b6d 100644 --- a/src/cc65/locals.c +++ b/src/cc65/locals.c @@ -122,8 +122,6 @@ static void ParseRegisterDecl (Declaration* Decl, int Reg) /* Check for an optional initialization */ if (CurTok.Tok == TOK_ASSIGN) { - ExprDesc Expr; - /* Skip the '=' */ NextToken (); @@ -152,6 +150,9 @@ static void ParseRegisterDecl (Declaration* Decl, int Reg) } else { + ExprDesc Expr; + ED_Init (&Expr); + /* Parse the expression */ hie1 (&Expr); @@ -207,8 +208,6 @@ static void ParseAutoDecl (Declaration* Decl) /* Check for an optional initialization */ if (CurTok.Tok == TOK_ASSIGN) { - ExprDesc Expr; - /* Skip the '=' */ NextToken (); @@ -246,6 +245,9 @@ static void ParseAutoDecl (Declaration* Decl) } else { + ExprDesc Expr; + ED_Init (&Expr); + /* Allocate previously reserved local space */ F_AllocLocalSpace (CurrentFunc); @@ -307,8 +309,6 @@ static void ParseAutoDecl (Declaration* Decl) /* Allow assignments */ if (CurTok.Tok == TOK_ASSIGN) { - ExprDesc Expr; - /* Skip the '=' */ NextToken (); @@ -332,6 +332,9 @@ static void ParseAutoDecl (Declaration* Decl) } else { + ExprDesc Expr; + ED_Init (&Expr); + /* Allocate space for the variable */ AllocStorage (DataLabel, g_usebss, Size); diff --git a/src/cc65/preproc.c b/src/cc65/preproc.c index 1c4837d94..cc160c1c3 100644 --- a/src/cc65/preproc.c +++ b/src/cc65/preproc.c @@ -1042,8 +1042,6 @@ static void DoError (void) static int DoIf (int Skip) /* Process #if directive */ { - ExprDesc Expr; - /* We're about to abuse the compiler expression parser to evaluate the ** #if expression. Save the current tokens to come back here later. ** NOTE: Yes, this is a hack, but it saves a complete separate expression @@ -1078,7 +1076,7 @@ static int DoIf (int Skip) NextToken (); /* Call the expression parser */ - ConstExpr (hie1, &Expr); + ExprDesc Expr = NoCodeConstExpr (hie1); /* End preprocessing mode */ Preprocessing = 0; diff --git a/src/cc65/preproc.h b/src/cc65/preproc.h index 8d7b3c374..1487179f4 100644 --- a/src/cc65/preproc.h +++ b/src/cc65/preproc.h @@ -44,7 +44,7 @@ -/* Set when the preprocessor calls ConstExpr() recursively */ +/* Set when the preprocessor calls NoCodeConstExpr() recursively */ extern unsigned char Preprocessing; diff --git a/src/cc65/shiftexpr.c b/src/cc65/shiftexpr.c index 9fe9d1188..0c69e96e5 100644 --- a/src/cc65/shiftexpr.c +++ b/src/cc65/shiftexpr.c @@ -61,7 +61,6 @@ void ShiftExpr (struct ExprDesc* Expr) /* Parse the << and >> operators. */ { - ExprDesc Expr2; CodeMark Mark1; CodeMark Mark2; token_t Tok; /* The operator token */ @@ -78,6 +77,10 @@ void ShiftExpr (struct ExprDesc* Expr) while (CurTok.Tok == TOK_SHL || CurTok.Tok == TOK_SHR) { + ExprDesc Expr2; + ED_Init (&Expr2); + Expr2.Flags |= Expr->Flags & E_MASK_KEEP_SUBEXPR; + /* All operators that call this function expect an int on the lhs */ if (!IsClassInt (Expr->Type)) { Error ("Integer expression expected"); diff --git a/src/cc65/staticassert.c b/src/cc65/staticassert.c index 68f0a41bc..1bf8dd4c5 100644 --- a/src/cc65/staticassert.c +++ b/src/cc65/staticassert.c @@ -65,7 +65,7 @@ void ParseStaticAssert () } /* Parse assertion condition */ - ConstAbsIntExpr (hie1, &Expr); + Expr = NoCodeConstAbsIntExpr (hie1); failed = !Expr.IVal; /* If there is a comma, we also have an error message. The message is optional because we diff --git a/src/cc65/stdfunc.c b/src/cc65/stdfunc.c index 94e56a625..8335177bb 100644 --- a/src/cc65/stdfunc.c +++ b/src/cc65/stdfunc.c @@ -152,6 +152,9 @@ static void ParseArg (ArgDesc* Arg, Type* Type) /* Remember the required argument type */ Arg->ArgType = Type; + /* Init expression */ + ED_Init (&Arg->Expr); + /* Read the expression we're going to pass to the function */ MarkedExprWithCheck (hie1, &Arg->Expr); @@ -1180,6 +1183,8 @@ static void StdFunc_strlen (FuncDesc* F attribute ((unused)), ExprDesc* Expr) long ECount; unsigned L; + ED_Init (&Arg); + /* Setup the argument type string */ ArgType[1].C = T_CHAR | T_QUAL_CONST; diff --git a/src/cc65/stmt.c b/src/cc65/stmt.c index 0925c6d3d..74b841493 100644 --- a/src/cc65/stmt.c +++ b/src/cc65/stmt.c @@ -312,6 +312,7 @@ static void ReturnStatement (void) ExprDesc Expr; const Type* ReturnType; + ED_Init (&Expr); NextToken (); if (CurTok.Tok != TOK_SEMI) { @@ -434,8 +435,6 @@ static void ContinueStatement (void) static void ForStatement (void) /* Handle a 'for' statement */ { - ExprDesc lval1; - ExprDesc lval3; int HaveIncExpr; CodeMark IncExprStart; CodeMark IncExprEnd; @@ -460,6 +459,10 @@ static void ForStatement (void) /* Parse the initializer expression */ if (CurTok.Tok != TOK_SEMI) { + /* The value of the expression is unused */ + ExprDesc lval1; + ED_Init (&lval1); + lval1.Flags = E_NEED_NONE; Expression0 (&lval1); } ConsumeSemi (); @@ -485,6 +488,10 @@ static void ForStatement (void) /* Parse the increment expression */ HaveIncExpr = (CurTok.Tok != TOK_RPAREN); if (HaveIncExpr) { + /* The value of the expression is unused */ + ExprDesc lval3; + ED_Init (&lval3); + lval3.Flags = E_NEED_NONE; Expression0 (&lval3); } @@ -590,8 +597,10 @@ int Statement (int* PendingToken) { ExprDesc Expr; int GotBreak; + unsigned PrevErrorCount; CodeMark Start, End; - unsigned PrevErrorCount = ErrorCount; + + ED_Init (&Expr); /* Assume no pending token */ if (PendingToken) { @@ -676,25 +685,24 @@ int Statement (int* PendingToken) break; default: - /* Remember the current code position */ + /* Remember the current error count and code position */ + PrevErrorCount = ErrorCount; GetCodePos (&Start); + /* Actual statement */ - ExprWithCheck (hie0, &Expr); - /* Load the result only if it is an lvalue and the type is - ** marked as volatile. Otherwise the load is useless. - */ - if (ED_IsLVal (&Expr) && IsQualVolatile (Expr.Type)) { - LoadExpr (CF_NONE, &Expr); - } + Expr.Flags |= E_NEED_NONE; + Expression0 (&Expr); + /* If the statement didn't generate code, and is not of type ** void, emit a warning. */ GetCodePos (&End); - if (CodeRangeIsEmpty (&Start, &End) && - !IsTypeVoid (Expr.Type) && + if (!ED_YetToLoad (&Expr) && + !ED_MayHaveNoEffect (&Expr) && + CodeRangeIsEmpty (&Start, &End) && IS_Get (&WarnNoEffect) && PrevErrorCount == ErrorCount) { - Warning ("Statement has no effect"); + Warning ("Expression result unused"); } CheckSemi (PendingToken); } diff --git a/src/cc65/swstmt.c b/src/cc65/swstmt.c index 8cab93bba..3878f7b67 100644 --- a/src/cc65/swstmt.c +++ b/src/cc65/swstmt.c @@ -97,7 +97,6 @@ void SwitchStatement (void) SwitchCtrl* OldSwitch; /* Pointer to old switch control data */ SwitchCtrl SwitchData; /* New switch data */ - /* Eat the "switch" token */ NextToken (); @@ -105,6 +104,8 @@ void SwitchStatement (void) ** integer type. */ ConsumeLParen (); + + ED_Init (&SwitchExpr); Expression0 (&SwitchExpr); if (!IsClassInt (SwitchExpr.Type)) { Error ("Switch quantity is not an integer"); @@ -211,12 +212,11 @@ void CaseLabel (void) long Val; /* Case label value */ unsigned CodeLabel; /* Code label for this case */ - /* Skip the "case" token */ NextToken (); /* Read the selector expression */ - ConstAbsIntExpr (hie1, &CaseExpr); + CaseExpr = NoCodeConstAbsIntExpr (hie1); Val = CaseExpr.IVal; /* Now check if we're inside a switch statement */ diff --git a/src/cc65/testexpr.c b/src/cc65/testexpr.c index 5bb7bf7e4..b924eaa40 100644 --- a/src/cc65/testexpr.c +++ b/src/cc65/testexpr.c @@ -58,6 +58,8 @@ unsigned Test (unsigned Label, int Invert) ExprDesc Expr; unsigned Result; + ED_Init (&Expr); + /* Read a boolean expression */ BoolExpr (hie0, &Expr); @@ -80,10 +82,8 @@ unsigned Test (unsigned Label, int Invert) /* Result is unknown */ Result = TESTEXPR_UNKNOWN; - /* If the expr hasn't set condition codes, set the force-test flag */ - if (!ED_IsTested (&Expr)) { - ED_MarkForTest (&Expr); - } + /* Set the test flag */ + ED_RequireTest (&Expr); /* Load the value into the primary register */ LoadExpr (CF_FORCECHAR, &Expr); diff --git a/src/cc65/typeconv.c b/src/cc65/typeconv.c index 0f67db107..930aef8d4 100644 --- a/src/cc65/typeconv.c +++ b/src/cc65/typeconv.c @@ -371,6 +371,11 @@ void TypeCast (ExprDesc* Expr) GetBasicTypeName (NewType)); } + /* If the new type is void, the cast expression can have no effects */ + if (IsTypeVoid (NewType)) { + Expr->Flags |= E_EVAL_MAYBE_UNUSED; + } + /* The result is always an rvalue */ ED_MarkExprAsRVal (Expr); } diff --git a/test/misc/Makefile b/test/misc/Makefile index 1f7a788ba..735f83065 100644 --- a/test/misc/Makefile +++ b/test/misc/Makefile @@ -58,12 +58,6 @@ $(ISEQUAL): ../isequal.c | $(WORKDIR) define PRG_template -# should compile, but gives an error -$(WORKDIR)/bug250.$1.$2.prg: bug250.c | $(WORKDIR) - @echo "FIXME: " $$@ "currently does not compile." - $(if $(QUIET),echo misc/bug250.$1.$2.prg) - $(NOT) $(CC65) -t sim$2 -$1 -o $$@ $$< $(NULLERR) - # should compile, but gives an error $(WORKDIR)/bug760.$1.$2.prg: bug760.c | $(WORKDIR) @echo "FIXME: " $$@ "currently does not compile." diff --git a/test/ref/pr1220.c b/test/ref/pr1220.c new file mode 100644 index 000000000..4f88ada73 --- /dev/null +++ b/test/ref/pr1220.c @@ -0,0 +1,360 @@ +/* PR #1220 - test constant ternary, AND and OR */ + +#include + +/* test AND/OR, results as integers */ +#define CONTEXT_A(x) do {\ + s = 0, flags = 0, t = (x),\ + printf("%3d %2X: %d\n", s, flags, t);\ + } while (0) + +/* test AND/OR in ternary context */ +#define CONTEXT_B(x) do {\ + s = 0, flags = 0,\ + (x ? printf("%3d %2X: 1\n", s, flags) : printf("%3d %2X: 0\n", s, flags));\ + } while (0) + +int s, t; +unsigned flags; + +int f(x) +/* The call to this function should be and only be skipped strictly according to +** the short-circuit evaluation rule. +*/ +{ + flags |= (x != 0) << s; + ++s; + return x; +} + +#define _A f(a) +#define _B f(b) +#define _C f(c) +#define _D f(d) +#define _T (f(0), -1) +#define _F (f(-1), 0) + +void f0() +/* constant short-circuit */ +{ + printf("f0()\n"); + + CONTEXT_A(_T && _T && _T); + CONTEXT_A(_F && _F && _F); + + CONTEXT_A(_T || _T || _T); + CONTEXT_A(_F || _F || _F); + + CONTEXT_A(_T && _T || _T && _T); + CONTEXT_A(_F && _F || _F && _F); + CONTEXT_A(_T && _F || _T && _F); + CONTEXT_A(_F && _T || _F && _T); + + CONTEXT_A((_T && _T) || (_T && _T)); + CONTEXT_A((_F && _F) || (_F && _F)); + CONTEXT_A((_T && _F) || (_T && _F)); + CONTEXT_A((_F && _T) || (_F && _T)); + + CONTEXT_A((_T || _T) && (_T || _T)); + CONTEXT_A((_F || _F) && (_F || _F)); + CONTEXT_A((_T || _F) && (_T || _F)); + CONTEXT_A((_F || _T) && (_F || _T)); + + printf("\n"); +} + +void f1(int a, int b, int c) +/* AND */ +{ + printf("f1(%d, %d, %d)\n", a, b, c); + + CONTEXT_A(_A && _B && _C); + + CONTEXT_A(_T && _B && _C); + CONTEXT_A(_A && _T && _C); + CONTEXT_A(_A && _B && _T); + + CONTEXT_A(_F && _B && _C); + CONTEXT_A(_A && _F && _C); + CONTEXT_A(_A && _B && _F); + + CONTEXT_A(_T && _T && _C); + CONTEXT_A(_A && _T && _T); + CONTEXT_A(_T && _B && _T); + + printf("\n"); +} + +void f2(int a, int b, int c) +/* OR */ +{ + printf("f2(%d, %d, %d)\n", a, b, c); + + CONTEXT_A(_A || _B || _C); + + CONTEXT_A(_T || _B || _C); + CONTEXT_A(_A || _T || _C); + CONTEXT_A(_A || _B || _T); + + CONTEXT_A(_F || _B || _C); + CONTEXT_A(_A || _F || _C); + CONTEXT_A(_A || _B || _F); + + CONTEXT_A(_F || _F || _C); + CONTEXT_A(_A || _F || _F); + CONTEXT_A(_F || _B || _F); + + printf("\n"); +} + +void f3(int a, int b, int c, int d) +/* AND and OR */ +{ + printf("f3(%d, %d, %d, %d)\n", a, b, c, d); + + CONTEXT_A(_A && _B || _C && _D); + CONTEXT_A(_T && _T || _C && _D); + CONTEXT_A(_A && _B || _T && _T); + + CONTEXT_A(_T && _B || _C && _D); + CONTEXT_A(_A && _T || _C && _D); + CONTEXT_A(_A && _B || _T && _D); + CONTEXT_A(_A && _B || _C && _T); + + CONTEXT_A(_F && _B || _C && _D); + CONTEXT_A(_A && _F || _C && _D); + CONTEXT_A(_A && _B || _F && _D); + CONTEXT_A(_A && _B || _C && _F); + + printf("\n"); +} + +void f4(int a, int b, int c, int d) +/* AND as top-level expression inside OR context */ +{ + printf("f4(%d, %d, %d, %d)\n", a, b, c, d); + + CONTEXT_A((_A && _B) || (_C && _D)); + CONTEXT_A((_T && _T) || (_C && _D)); + CONTEXT_A((_A && _B) || (_T && _T)); + + CONTEXT_A((_T && _B) || (_C && _D)); + CONTEXT_A((_A && _T) || (_C && _D)); + CONTEXT_A((_A && _B) || (_T && _D)); + CONTEXT_A((_A && _B) || (_C && _T)); + + CONTEXT_A((_F && _B) || (_C && _D)); + CONTEXT_A((_A && _F) || (_C && _D)); + CONTEXT_A((_A && _B) || (_F && _D)); + CONTEXT_A((_A && _B) || (_C && _F)); + + printf("\n"); +} + +void f5(int a, int b, int c, int d) +/* OR as top-level expression inside AND context */ +{ + printf("f5(%d, %d, %d, %d)\n", a, b, c, d); + + CONTEXT_A((_A || _B) && (_C || _D)); + CONTEXT_A((_F || _F) && (_C || _D)); + CONTEXT_A((_A || _B) && (_F || _F)); + + CONTEXT_A((_T || _B) && (_C || _D)); + CONTEXT_A((_A || _T) && (_C || _D)); + CONTEXT_A((_A || _B) && (_T || _D)); + CONTEXT_A((_A || _B) && (_C || _T)); + + CONTEXT_A((_F || _B) && (_C || _D)); + CONTEXT_A((_A || _F) && (_C || _D)); + CONTEXT_A((_A || _B) && (_F || _D)); + CONTEXT_A((_A || _B) && (_C || _F)); + + printf("\n"); +} + +void f0_B() +/* constant short-circuit */ +{ + printf("f0()\n"); + + CONTEXT_B(_T && _T && _T); + CONTEXT_B(_F && _F && _F); + + CONTEXT_B(_T || _T || _T); + CONTEXT_B(_F || _F || _F); + + CONTEXT_B(_T && _T || _T && _T); + CONTEXT_B(_F && _F || _F && _F); + CONTEXT_B(_T && _F || _T && _F); + CONTEXT_B(_F && _T || _F && _T); + + CONTEXT_B((_T && _T) || (_T && _T)); + CONTEXT_B((_F && _F) || (_F && _F)); + CONTEXT_B((_T && _F) || (_T && _F)); + CONTEXT_B((_F && _T) || (_F && _T)); + + CONTEXT_B((_T || _T) && (_T || _T)); + CONTEXT_B((_F || _F) && (_F || _F)); + CONTEXT_B((_T || _F) && (_T || _F)); + CONTEXT_B((_F || _T) && (_F || _T)); + + printf("\n"); +} + +void f1_B(int a, int b, int c) +/* AND */ +{ + printf("f1(%d, %d, %d)\n", a, b, c); + + CONTEXT_B(_A && _B && _C); + + CONTEXT_B(_T && _B && _C); + CONTEXT_B(_A && _T && _C); + CONTEXT_B(_A && _B && _T); + + CONTEXT_B(_F && _B && _C); + CONTEXT_B(_A && _F && _C); + CONTEXT_B(_A && _B && _F); + + CONTEXT_B(_T && _T && _C); + CONTEXT_B(_A && _T && _T); + CONTEXT_B(_T && _B && _T); + + printf("\n"); +} + +void f2_B(int a, int b, int c) +/* OR */ +{ + printf("f2(%d, %d, %d)\n", a, b, c); + + CONTEXT_B(_A || _B || _C); + + CONTEXT_B(_T || _B || _C); + CONTEXT_B(_A || _T || _C); + CONTEXT_B(_A || _B || _T); + + CONTEXT_B(_F || _B || _C); + CONTEXT_B(_A || _F || _C); + CONTEXT_B(_A || _B || _F); + + CONTEXT_B(_F || _F || _C); + CONTEXT_B(_A || _F || _F); + CONTEXT_B(_F || _B || _F); + + printf("\n"); +} + +void f3_B(int a, int b, int c, int d) +/* AND and OR */ +{ + printf("f3(%d, %d, %d, %d)\n", a, b, c, d); + + CONTEXT_B(_A && _B || _C && _D); + CONTEXT_B(_T && _T || _C && _D); + CONTEXT_B(_A && _B || _T && _T); + + CONTEXT_B(_T && _B || _C && _D); + CONTEXT_B(_A && _T || _C && _D); + CONTEXT_B(_A && _B || _T && _D); + CONTEXT_B(_A && _B || _C && _T); + + CONTEXT_B(_F && _B || _C && _D); + CONTEXT_B(_A && _F || _C && _D); + CONTEXT_B(_A && _B || _F && _D); + CONTEXT_B(_A && _B || _C && _F); + + printf("\n"); +} + +void f4_B(int a, int b, int c, int d) +/* AND as top-level expression inside OR context */ +{ + printf("f4(%d, %d, %d, %d)\n", a, b, c, d); + + CONTEXT_B((_A && _B) || (_C && _D)); + CONTEXT_B((_T && _T) || (_C && _D)); + CONTEXT_B((_A && _B) || (_T && _T)); + + CONTEXT_B((_T && _B) || (_C && _D)); + CONTEXT_B((_A && _T) || (_C && _D)); + CONTEXT_B((_A && _B) || (_T && _D)); + CONTEXT_B((_A && _B) || (_C && _T)); + + CONTEXT_B((_F && _B) || (_C && _D)); + CONTEXT_B((_A && _F) || (_C && _D)); + CONTEXT_B((_A && _B) || (_F && _D)); + CONTEXT_B((_A && _B) || (_C && _F)); + + printf("\n"); +} + +void f5_B(int a, int b, int c, int d) +/* OR as top-level expression inside AND context */ +{ + printf("f5(%d, %d, %d, %d)\n", a, b, c, d); + + CONTEXT_B((_A || _B) && (_C || _D)); + CONTEXT_B((_F || _F) && (_C || _D)); + CONTEXT_B((_A || _B) && (_F || _F)); + + CONTEXT_B((_T || _B) && (_C || _D)); + CONTEXT_B((_A || _T) && (_C || _D)); + CONTEXT_B((_A || _B) && (_T || _D)); + CONTEXT_B((_A || _B) && (_C || _T)); + + CONTEXT_B((_F || _B) && (_C || _D)); + CONTEXT_B((_A || _F) && (_C || _D)); + CONTEXT_B((_A || _B) && (_F || _D)); + CONTEXT_B((_A || _B) && (_C || _F)); + + printf("\n"); +} + +int main() +{ + f0(); + + f1(0, 0, 0); + f2(0, 0, 0); + f3(0, 0, 0, 0); + f4(0, 0, 0, 0); + f5(0, 0, 0, 0); + + f1(1, 1, 1); + f2(1, 1, 1); + f3(1, 1, 1, 1); + f4(1, 1, 1, 1); + f5(1, 1, 1, 1); + + f3(1, 0, 1, 0); + f4(1, 0, 1, 0); + f5(1, 0, 1, 0); + f3(0, 1, 0, 1); + f4(0, 1, 0, 1); + f5(0, 1, 0, 1); + + f0_B(); + + f1_B(0, 0, 0); + f2_B(0, 0, 0); + f3_B(0, 0, 0, 0); + f4_B(0, 0, 0, 0); + f5_B(0, 0, 0, 0); + + f1_B(1, 1, 1); + f2_B(1, 1, 1); + f3_B(1, 1, 1, 1); + f4_B(1, 1, 1, 1); + f5_B(1, 1, 1, 1); + + f3_B(1, 0, 1, 0); + f4_B(1, 0, 1, 0); + f5_B(1, 0, 1, 0); + f3_B(0, 1, 0, 1); + f4_B(0, 1, 0, 1); + f5_B(0, 1, 0, 1); + + return 0; +} diff --git a/test/misc/bug250.c b/test/val/bug250.c similarity index 100% rename from test/misc/bug250.c rename to test/val/bug250.c