]> git.sur5r.net Git - cc65/blobdiff - src/cc65/expr.c
Renamed the defines in symdefs.h to something more meaningful. They were named
[cc65] / src / cc65 / expr.c
index 7f30bb11bdb24f719b8f57b4899c41b107a12b29..b0bc18d1481c5a4c3148c2506737e88002fc3e6c 100644 (file)
@@ -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: