X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=src%2Fcc65%2Fexpr.c;h=b0bc18d1481c5a4c3148c2506737e88002fc3e6c;hb=112ae0e3db511ddd92e769c11328646ebe2a6240;hp=7f30bb11bdb24f719b8f57b4899c41b107a12b29;hpb=0c20177fce7256c342f55166594b4693d4e3e796;p=cc65 diff --git a/src/cc65/expr.c b/src/cc65/expr.c index 7f30bb11b..b0bc18d14 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -109,12 +109,13 @@ void ExprWithCheck (void (*Func) (ExprDesc*), ExprDesc* Expr) /* Do some checks if code generation is still constistent */ if (StackPtr != OldSP) { if (Debug) { - fprintf (stderr, - "Code generation messed up!\n" - "StackPtr is %d, should be %d", - StackPtr, OldSP); + Error ("Code generation messed up: " + "StackPtr is %d, should be %d", + StackPtr, OldSP); } else { - Internal ("StackPtr is %d, should be %d\n", StackPtr, OldSP); + Internal ("Code generation messed up: " + "StackPtr is %d, should be %d", + StackPtr, OldSP); } } } @@ -255,6 +256,18 @@ void PushAddr (const ExprDesc* Expr) +static void WarnConstCompareResult (void) +/* If the result of a comparison is constant, this is suspicious when not in + * preprocessor mode. + */ +{ + if (!Preprocessing) { + Warning ("Result of comparison is constant"); + } +} + + + /*****************************************************************************/ /* code */ /*****************************************************************************/ @@ -486,6 +499,12 @@ static void FunctionCall (ExprDesc* Expr) } } else { + /* Check function attributes */ + if (Expr->Sym && SymHasAttr (Expr->Sym, atNoReturn)) { + /* For now, handle as if a return statement was encountered */ + F_ReturnFound (CurrentFunc); + } + /* Check for known standard functions and inline them */ if (Expr->Name != 0) { int StdFunc = FindStdFunc ((const char*) Expr->Name); @@ -547,7 +566,7 @@ static void FunctionCall (ExprDesc* Expr) /* If we have a pointer on stack, remove it */ if (PtrOnStack) { - g_space (- (int) sizeofarg (CF_PTR)); + g_drop (SIZEOF_PTR); pop (CF_PTR); } @@ -740,11 +759,13 @@ static void Primary (ExprDesc* E) break; case TOK_SCONST: + case TOK_WCSCONST: /* String literal */ - E->Type = GetCharArrayType (GetLiteralPoolOffs () - CurTok.IVal); + E->LVal = UseLiteral (CurTok.SVal); + E->Type = GetCharArrayType (GetLiteralSize (CurTok.SVal)); E->Flags = E_LOC_LITERAL | E_RTYPE_RVAL; - E->IVal = CurTok.IVal; - E->Name = LiteralPoolLabel; + E->IVal = 0; + E->Name = GetLiteralLabel (CurTok.SVal); NextToken (); break; @@ -796,6 +817,7 @@ static void ArrayRef (ExprDesc* Expr) ExprDesc Subscript; CodeMark Mark1; CodeMark Mark2; + TypeCode Qualifiers; Type* ElementType; Type* tptr1; @@ -828,7 +850,7 @@ static void ArrayRef (ExprDesc* Expr) } /* TOS now contains ptr to array elements. Get the subscript. */ - ExprWithCheck (hie0, &Subscript); + MarkedExprWithCheck (hie0, &Subscript); /* Check the types of array and subscript. We can either have a * pointer/array to the left, in which case the subscript must be of an @@ -837,12 +859,16 @@ static void ArrayRef (ExprDesc* Expr) * Since we do the necessary checking here, we can rely later on the * correct types. */ + Qualifiers = T_QUAL_NONE; if (IsClassPtr (Expr->Type)) { if (!IsClassInt (Subscript.Type)) { Error ("Array subscript is not an integer"); /* To avoid any compiler errors, make the expression a valid int */ ED_MakeConstAbsInt (&Subscript, 0); } + if (IsTypeArray (Expr->Type)) { + Qualifiers = GetQualifier (Expr->Type); + } ElementType = Indirect (Expr->Type); } else if (IsClassInt (Expr->Type)) { if (!IsClassPtr (Subscript.Type)) { @@ -851,6 +877,8 @@ static void ArrayRef (ExprDesc* Expr) * address 0. */ ED_MakeConstAbs (&Subscript, 0, GetCharArrayType (1)); + } else if (IsTypeArray (Subscript.Type)) { + Qualifiers = GetQualifier (Subscript.Type); } ElementType = Indirect (Subscript.Type); } else { @@ -863,6 +891,14 @@ static void ArrayRef (ExprDesc* Expr) ElementType = Indirect (Expr->Type); } + /* The element type has the combined qualifiers from itself and the array, + * it is a member of (if any). + */ + if (GetQualifier (ElementType) != (GetQualifier (ElementType) | Qualifiers)) { + ElementType = TypeDup (ElementType); + ElementType->C |= Qualifiers; + } + /* If the subscript is a bit-field, load it and make it an rvalue */ if (ED_IsBitField (&Subscript)) { LoadExpr (CF_NONE, &Subscript); @@ -870,7 +906,7 @@ static void ArrayRef (ExprDesc* Expr) } /* Check if the subscript is constant absolute value */ - if (ED_IsConstAbs (&Subscript)) { + if (ED_IsConstAbs (&Subscript) && ED_CodeRangeIsEmpty (&Subscript)) { /* The array subscript is a numeric constant. If we had pushed the * array base address onto the stack before, we can remove this value, @@ -1069,12 +1105,14 @@ static void StructRef (ExprDesc* Expr) { ident Ident; SymEntry* Field; + TypeCode Q; /* Skip the token and check for an identifier */ NextToken (); if (CurTok.Tok != TOK_IDENT) { Error ("Identifier expected"); - Expr->Type = type_int; + /* Make the expression an integer at address zero */ + ED_MakeConstAbs (Expr, 0, type_int); return; } @@ -1084,7 +1122,8 @@ static void StructRef (ExprDesc* Expr) Field = FindStructField (Expr->Type, Ident); if (Field == 0) { Error ("Struct/union has no field named `%s'", Ident); - Expr->Type = type_int; + /* Make the expression an integer at address zero */ + ED_MakeConstAbs (Expr, 0, type_int); return; } @@ -1103,8 +1142,18 @@ static void StructRef (ExprDesc* Expr) /* Set the struct field offset */ Expr->IVal += Field->V.Offs; - /* The type is now the type of the field */ - Expr->Type = Field->Type; + /* The type is the type of the field plus any qualifiers from the struct */ + if (IsClassStruct (Expr->Type)) { + Q = GetQualifier (Expr->Type); + } else { + Q = GetQualifier (Indirect (Expr->Type)); + } + if (GetQualifier (Field->Type) == (GetQualifier (Field->Type) | Q)) { + Expr->Type = Field->Type; + } else { + Expr->Type = TypeDup (Field->Type); + Expr->Type->C |= Q; + } /* An struct member is actually a variable. So the rules for variables * with respect to the reference type apply: If it's an array, it is @@ -1591,9 +1640,12 @@ void hie10 (ExprDesc* Expr) ED_MakeRValExpr (Expr); } /* If the expression is already a pointer to function, the - * additional dereferencing operator must be ignored. + * 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)) { + if (IsTypeFuncPtr (Expr->Type) || IsTypeFunc (Expr->Type)) { /* Expression not storable */ ED_MakeRVal (Expr); } else { @@ -1721,7 +1773,14 @@ static void hie_internal (const GenDesc* Ops, /* List of generators */ } /* Get the right hand side */ - rconst = (evalexpr (CF_NONE, hienext, &Expr2) == 0); + 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)) { @@ -1858,18 +1917,23 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ /* Helper function for the compare operators */ { ExprDesc Expr2; + CodeMark Mark0; CodeMark Mark1; CodeMark Mark2; const GenDesc* Gen; - token_t Tok; /* The operator token */ + token_t Tok; /* The operator token */ unsigned ltype; int rconst; /* Operand is a constant */ + GetCodePos (&Mark0); hienext (Expr); while ((Gen = FindGen (CurTok.Tok, Ops)) != 0) { + /* Remember the generator function */ + void (*GenFunc) (unsigned, unsigned long) = Gen->Func; + /* Remember the operator token, then skip it */ Tok = CurTok.Tok; NextToken (); @@ -1889,7 +1953,14 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ } /* Get the right hand side */ - rconst = (evalexpr (CF_NONE, hienext, &Expr2) == 0); + 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); + } /* Make sure, the types are compatible */ if (IsClassInt (Expr->Type)) { @@ -1918,9 +1989,7 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ /* If the result is constant, this is suspicious when not in * preprocessor mode. */ - if (!Preprocessing) { - Warning ("Result of comparison is constant"); - } + WarnConstCompareResult (); /* Both operands are constant, remove the generated code */ RemoveCode (&Mark1); @@ -1958,7 +2027,11 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ } } - } else { + } else { + + /* Determine the signedness of the operands */ + int LeftSigned = IsSignSigned (Expr->Type); + int RightSigned = IsSignSigned (Expr2.Type); /* If the right hand side is constant, and the generator function * expects the lhs in the primary, remove the push of the primary @@ -1973,27 +2046,180 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ } } - /* Determine the type of the operation result. If the left - * operand is of type char and the right is a constant, or - * if both operands are of type char, we will encode the - * operation as char operation. Otherwise the default - * promotions are used. - */ - if (IsTypeChar (Expr->Type) && (IsTypeChar (Expr2.Type) || rconst)) { - flags |= CF_CHAR; - if (IsSignUnsigned (Expr->Type) || IsSignUnsigned (Expr2.Type)) { - flags |= CF_UNSIGNED; - } - if (rconst) { - flags |= CF_FORCECHAR; - } + /* Determine the type of the operation. */ + if (IsTypeChar (Expr->Type) && rconst) { + + /* Left side is unsigned char, right side is constant. + * Determine the minimum and maximum values + */ + int LeftMin, LeftMax; + if (LeftSigned) { + LeftMin = -128; + LeftMax = 127; + } else { + LeftMin = 0; + LeftMax = 255; + } + /* An integer value is always represented as a signed in the + * ExprDesc structure. This may lead to false results below, + * if it is actually unsigned, but interpreted as signed + * because of the representation. Fortunately, in this case, + * the actual value doesn't matter, since it's always greater + * than what can be represented in a char. So correct the + * value accordingly. + */ + if (!RightSigned && Expr2.IVal < 0) { + /* Correct the value so it is an unsigned. It will then + * anyway match one of the cases below. + */ + Expr2.IVal = LeftMax + 1; + } + + /* Comparing a char against a constant may have a constant + * result. + */ + switch (Tok) { + + case TOK_EQ: + if (Expr2.IVal < LeftMin || Expr2.IVal > LeftMax) { + ED_MakeConstAbsInt (Expr, 0); + WarnConstCompareResult (); + RemoveCode (&Mark0); + goto Done; + } + break; + + case TOK_NE: + if (Expr2.IVal < LeftMin || Expr2.IVal > LeftMax) { + ED_MakeConstAbsInt (Expr, 1); + WarnConstCompareResult (); + RemoveCode (&Mark0); + goto Done; + } + break; + + case TOK_LT: + if (Expr2.IVal <= LeftMin || Expr2.IVal > LeftMax) { + ED_MakeConstAbsInt (Expr, Expr2.IVal > LeftMax); + WarnConstCompareResult (); + RemoveCode (&Mark0); + goto Done; + } + break; + + case TOK_LE: + if (Expr2.IVal < LeftMin || Expr2.IVal >= LeftMax) { + ED_MakeConstAbsInt (Expr, Expr2.IVal >= LeftMax); + WarnConstCompareResult (); + RemoveCode (&Mark0); + goto Done; + } + break; + + case TOK_GE: + if (Expr2.IVal <= LeftMin || Expr2.IVal > LeftMax) { + ED_MakeConstAbsInt (Expr, Expr2.IVal <= LeftMin); + WarnConstCompareResult (); + RemoveCode (&Mark0); + goto Done; + } + break; + + case TOK_GT: + if (Expr2.IVal < LeftMin || Expr2.IVal >= LeftMax) { + ED_MakeConstAbsInt (Expr, Expr2.IVal < LeftMin); + WarnConstCompareResult (); + RemoveCode (&Mark0); + goto Done; + } + break; + + default: + Internal ("hie_compare: got token 0x%X\n", Tok); + } + + /* If the result is not already constant (as evaluated in the + * switch above), we can execute the operation as a char op, + * since the right side constant is in a valid range. + */ + flags |= (CF_CHAR | CF_FORCECHAR); + if (!LeftSigned) { + flags |= CF_UNSIGNED; + } + + } else if (IsTypeChar (Expr->Type) && IsTypeChar (Expr2.Type) && + GetSignedness (Expr->Type) == GetSignedness (Expr2.Type)) { + + /* Both are chars with the same signedness. We can encode the + * operation as a char operation. + */ + flags |= CF_CHAR; + if (rconst) { + flags |= CF_FORCECHAR; + } + if (!LeftSigned) { + flags |= CF_UNSIGNED; + } } else { - unsigned rtype = TypeOf (Expr2.Type) | (flags & CF_CONST); + unsigned rtype = TypeOf (Expr2.Type) | (flags & CF_CONST); flags |= g_typeadjust (ltype, rtype); } + /* If the left side is an unsigned and the right is a constant, + * we may be able to change the compares to something more + * effective. + */ + if (!LeftSigned && rconst) { + + switch (Tok) { + + case TOK_LT: + if (Expr2.IVal == 1) { + /* An unsigned compare to one means that the value + * must be zero. + */ + GenFunc = g_eq; + Expr2.IVal = 0; + } + break; + + case TOK_LE: + if (Expr2.IVal == 0) { + /* An unsigned compare to zero means that the value + * must be zero. + */ + GenFunc = g_eq; + } + break; + + case TOK_GE: + if (Expr2.IVal == 1) { + /* An unsigned compare to one means that the value + * must not be zero. + */ + GenFunc = g_ne; + Expr2.IVal = 0; + } + break; + + case TOK_GT: + if (Expr2.IVal == 0) { + /* An unsigned compare to zero means that the value + * must not be zero. + */ + GenFunc = g_ne; + } + break; + + default: + break; + + } + + } + /* Generate code */ - Gen->Func (flags, Expr2.IVal); + GenFunc (flags, Expr2.IVal); /* The result is an rvalue in the primary */ ED_MakeRValExpr (Expr); @@ -2002,7 +2228,7 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ /* Result type is always int */ Expr->Type = type_int; - /* Condition codes are set */ +Done: /* Condition codes are set */ ED_TestDone (Expr); } } @@ -2057,21 +2283,21 @@ static void parseadd (ExprDesc* Expr) /* Both expressions are constants. Check for pointer arithmetic */ if (IsClassPtr (lhst) && IsClassInt (rhst)) { - /* Left is pointer, right is int, must scale rhs */ + /* Left is pointer, right is int, must scale rhs */ Expr->IVal += Expr2.IVal * CheckedPSizeOf (lhst); - /* Result type is a pointer */ + /* Result type is a pointer */ } else if (IsClassInt (lhst) && IsClassPtr (rhst)) { - /* Left is int, right is pointer, must scale lhs */ + /* Left is int, right is pointer, must scale lhs */ Expr->IVal = Expr->IVal * CheckedPSizeOf (rhst) + Expr2.IVal; - /* Result type is a pointer */ - Expr->Type = Expr2.Type; + /* Result type is a pointer */ + Expr->Type = Expr2.Type; } else if (IsClassInt (lhst) && IsClassInt (rhst)) { - /* Integer addition */ - Expr->IVal += Expr2.IVal; - typeadjust (Expr, &Expr2, 1); + /* Integer addition */ + Expr->IVal += Expr2.IVal; + typeadjust (Expr, &Expr2, 1); } else { /* OOPS */ - Error ("Invalid operands for binary operator `+'"); + Error ("Invalid operands for binary operator `+'"); } } else { @@ -2169,7 +2395,10 @@ static void parseadd (ExprDesc* Expr) g_push (TypeOf (Expr->Type), 0); /* --> stack */ /* Evaluate the rhs */ - if (evalexpr (CF_NONE, hie9, &Expr2) == 0) { + MarkedExprWithCheck (hie9, &Expr2); + + /* Check for a constant rhs expression */ + if (ED_IsConstAbs (&Expr2) && ED_CodeRangeIsEmpty (&Expr2)) { /* Right hand side is a constant. Get the rhs type */ rhst = Expr2.Type; @@ -2203,6 +2432,9 @@ static void parseadd (ExprDesc* Expr) } else { + /* Not constant, load into the primary */ + LoadExpr (CF_NONE, &Expr2); + /* lhs and rhs are not constant. Get the rhs type. */ rhst = Expr2.Type; @@ -2282,7 +2514,10 @@ static void parsesub (ExprDesc* Expr) g_push (TypeOf (lhst), 0); /* --> stack */ /* Parse the right hand side */ - if (evalexpr (CF_NONE, hie9, &Expr2) == 0) { + MarkedExprWithCheck (hie9, &Expr2); + + /* Check for a constant rhs expression */ + if (ED_IsConstAbs (&Expr2) && ED_CodeRangeIsEmpty (&Expr2)) { /* The right hand side is constant. Get the rhs type. */ rhst = Expr2.Type; @@ -2354,38 +2589,41 @@ static void parsesub (ExprDesc* Expr) /* Do the subtraction */ g_dec (flags | CF_CONST, Expr2.IVal); - /* If this was a pointer subtraction, we must scale the result */ - if (rscale != 1) { - g_scale (flags, -rscale); - } + /* If this was a pointer subtraction, we must scale the result */ + if (rscale != 1) { + g_scale (flags, -rscale); + } - /* Result is a rvalue in the primary register */ - ED_MakeRValExpr (Expr); - ED_MarkAsUntested (Expr); + /* Result is a rvalue in the primary register */ + ED_MakeRValExpr (Expr); + ED_MarkAsUntested (Expr); - } + } } else { - /* Right hand side is not constant. Get the rhs type. */ - rhst = Expr2.Type; + /* Not constant, load into the primary */ + LoadExpr (CF_NONE, &Expr2); + + /* Right hand side is not constant. Get the rhs type. */ + rhst = Expr2.Type; /* Check for pointer arithmetic */ - if (IsClassPtr (lhst) && IsClassInt (rhst)) { + if (IsClassPtr (lhst) && IsClassInt (rhst)) { /* Left is pointer, right is int, must scale rhs */ - g_scale (CF_INT, CheckedPSizeOf (lhst)); - /* Operate on pointers, result type is a pointer */ - flags = CF_PTR; - } else if (IsClassPtr (lhst) && IsClassPtr (rhst)) { - /* Left is pointer, right is pointer, must scale result */ - if (TypeCmp (Indirect (lhst), Indirect (rhst)) < TC_QUAL_DIFF) { - Error ("Incompatible pointer types"); - } else { - rscale = CheckedPSizeOf (lhst); - } - /* Operate on pointers, result type is an integer */ - flags = CF_PTR; - Expr->Type = type_int; + g_scale (CF_INT, CheckedPSizeOf (lhst)); + /* Operate on pointers, result type is a pointer */ + flags = CF_PTR; + } else if (IsClassPtr (lhst) && IsClassPtr (rhst)) { + /* Left is pointer, right is pointer, must scale result */ + if (TypeCmp (Indirect (lhst), Indirect (rhst)) < TC_QUAL_DIFF) { + Error ("Incompatible pointer types"); + } else { + rscale = CheckedPSizeOf (lhst); + } + /* Operate on pointers, result type is an integer */ + flags = CF_PTR; + Expr->Type = type_int; } else if (IsClassInt (lhst) && IsClassInt (rhst)) { /* Integer subtraction. If the left hand side descriptor says that * the lhs is const, we have to remove this mark, since this is no @@ -2816,7 +3054,7 @@ static void hieQuest (ExprDesc* Expr) -static void opeq (const GenDesc* Gen, ExprDesc* Expr) +static void opeq (const GenDesc* Gen, ExprDesc* Expr, const char* Op) /* Process "op=" operators. */ { ExprDesc Expr2; @@ -2861,7 +3099,18 @@ static void opeq (const GenDesc* Gen, ExprDesc* Expr) g_push (flags, 0); /* Evaluate the rhs */ - if (evalexpr (CF_NONE, hie1, &Expr2) == 0) { + MarkedExprWithCheck (hie1, &Expr2); + + /* The rhs must be an integer (or a float, but we don't support that yet */ + if (!IsClassInt (Expr2.Type)) { + Error ("Invalid right operand for binary operator `%s'", Op); + /* Continue. Wrong code will be generated, but the compiler won't + * break, so this is the best error recovery. + */ + } + + /* Check for a constant expression */ + if (ED_IsConstAbs (&Expr2) && ED_CodeRangeIsEmpty (&Expr2)) { /* The resulting value is a constant. If the generator has the NOPUSH * flag set, don't push the lhs. */ @@ -2897,7 +3146,9 @@ static void opeq (const GenDesc* Gen, ExprDesc* Expr) Gen->Func (flags | CF_CONST, Expr2.IVal); } } else { - /* rhs is not constant and already in the primary register */ + + /* rhs is not constant. Load into the primary */ + LoadExpr (CF_NONE, &Expr2); if (MustScale) { /* lhs is a pointer, scale rhs */ g_scale (TypeOf (Expr2.Type), CheckedSizeOf (Expr->Type+1)); @@ -2919,7 +3170,7 @@ static void opeq (const GenDesc* Gen, ExprDesc* Expr) -static void addsubeq (const GenDesc* Gen, ExprDesc *Expr) +static void addsubeq (const GenDesc* Gen, ExprDesc *Expr, const char* Op) /* Process the += and -= operators */ { ExprDesc Expr2; @@ -2931,7 +3182,7 @@ static void addsubeq (const GenDesc* Gen, ExprDesc *Expr) /* We're currently only able to handle some adressing modes */ if (ED_GetLoc (Expr) == E_LOC_EXPR || ED_GetLoc (Expr) == E_LOC_PRIMARY) { /* Use generic routine */ - opeq (Gen, Expr); + opeq (Gen, Expr, Op); return; } @@ -2964,8 +3215,16 @@ static void addsubeq (const GenDesc* Gen, ExprDesc *Expr) lflags = 0; rflags = 0; - /* Evaluate the rhs */ + /* Evaluate the rhs. We expect an integer here, since float is not + * supported + */ hie1 (&Expr2); + if (!IsClassInt (Expr2.Type)) { + Error ("Invalid right operand for binary operator `%s'", Op); + /* Continue. Wrong code will be generated, but the compiler won't + * break, so this is the best error recovery. + */ + } if (ED_IsConstAbs (&Expr2)) { /* The resulting value is a constant. Scale it. */ if (MustScale) { @@ -3059,43 +3318,43 @@ void hie1 (ExprDesc* Expr) break; case TOK_PLUS_ASSIGN: - addsubeq (&GenPASGN, Expr); + addsubeq (&GenPASGN, Expr, "+="); break; case TOK_MINUS_ASSIGN: - addsubeq (&GenSASGN, Expr); + addsubeq (&GenSASGN, Expr, "-="); break; case TOK_MUL_ASSIGN: - opeq (&GenMASGN, Expr); + opeq (&GenMASGN, Expr, "*="); break; case TOK_DIV_ASSIGN: - opeq (&GenDASGN, Expr); + opeq (&GenDASGN, Expr, "/="); break; case TOK_MOD_ASSIGN: - opeq (&GenMOASGN, Expr); + opeq (&GenMOASGN, Expr, "%="); break; case TOK_SHL_ASSIGN: - opeq (&GenSLASGN, Expr); + opeq (&GenSLASGN, Expr, "<<="); break; case TOK_SHR_ASSIGN: - opeq (&GenSRASGN, Expr); + opeq (&GenSRASGN, Expr, ">>="); break; case TOK_AND_ASSIGN: - opeq (&GenAASGN, Expr); + opeq (&GenAASGN, Expr, "&="); break; case TOK_XOR_ASSIGN: - opeq (&GenXOASGN, Expr); + opeq (&GenXOASGN, Expr, "^="); break; case TOK_OR_ASSIGN: - opeq (&GenOASGN, Expr); + opeq (&GenOASGN, Expr, "|="); break; default: