]> git.sur5r.net Git - cc65/commitdiff
Fixed a problem reported by Greg King. structs returned from functions in the
authoruz <uz@b7a2c559-68d2-44c3-8de9-860c34a00d81>
Fri, 20 Jan 2012 16:01:25 +0000 (16:01 +0000)
committeruz <uz@b7a2c559-68d2-44c3-8de9-860c34a00d81>
Fri, 20 Jan 2012 16:01:25 +0000 (16:01 +0000)
primary register are actually rvalues and need special treatment.

git-svn-id: svn://svn.cc65.org/cc65/trunk@5413 b7a2c559-68d2-44c3-8de9-860c34a00d81

src/cc65/expr.c
src/cc65/preproc.c

index 8b5ebd06f21aa8cd503bce54f65389c0c48c3daf..0bafb8ccfdfb8cedb6f1ff4822c841c52fdcb99c 100644 (file)
@@ -1105,6 +1105,7 @@ static void StructRef (ExprDesc* Expr)
 {
     ident Ident;
     SymEntry* Field;
+    Type* FinalType;
     TypeCode Q;
 
     /* Skip the token and check for an identifier */
@@ -1139,9 +1140,6 @@ static void StructRef (ExprDesc* Expr)
         ED_MakeLValExpr (Expr);
     }
 
-    /* Set the struct field offset */
-    Expr->IVal += Field->V.Offs;
-
     /* The type is the type of the field plus any qualifiers from the struct */
     if (IsClassStruct (Expr->Type)) {
         Q = GetQualifier (Expr->Type);
@@ -1149,27 +1147,81 @@ static void StructRef (ExprDesc* Expr)
         Q = GetQualifier (Indirect (Expr->Type));
     }
     if (GetQualifier (Field->Type) == (GetQualifier (Field->Type) | Q)) {
-        Expr->Type = Field->Type;
+        FinalType = Field->Type;
     } else {
-        Expr->Type = TypeDup (Field->Type);
-        Expr->Type->C |= Q;
+        FinalType = TypeDup (Field->Type);
+        FinalType->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
-     * a rvalue, otherwise it's an lvalue. (A function would also be a rvalue,
-     * but a struct field cannot be a function).
+    /* A struct is usually an lvalue. If not, it is a struct in the primary
+     * register.
      */
-    if (IsTypeArray (Expr->Type)) {
-        ED_MakeRVal (Expr);
+    if (ED_IsRVal (Expr) && ED_IsLocExpr (Expr) && !IsTypePtr (Expr->Type)) {
+
+        unsigned Flags = 0;
+        unsigned BitOffs;
+
+        /* Get the size of the type */
+        unsigned Size = SizeOf (Expr->Type);
+
+        /* Safety check */
+        CHECK (Field->V.Offs + Size <= SIZEOF_LONG);
+
+        /* The type of the operation depends on the type of the struct */
+        switch (Size) {
+            case 1:     Flags = CF_CHAR | CF_UNSIGNED | CF_CONST;       break;
+            case 2:     Flags = CF_INT  | CF_UNSIGNED | CF_CONST;       break;
+            case 3:     /* FALLTHROUGH */
+            case 4:     Flags = CF_LONG | CF_UNSIGNED | CF_CONST;       break;
+            default:    Internal ("Invalid struct size: %u", Size);     break;
+        }
+
+        /* Generate a shift to get the field in the proper position in the
+         * primary. For bit fields, mask the value.
+         */
+        BitOffs = Field->V.Offs * CHAR_BITS;
+        if (SymIsBitField (Field)) {
+            BitOffs += Field->V.B.BitOffs;
+            g_asr (Flags, BitOffs);
+            /* Mask the value. This is unnecessary if the shift executed above
+             * moved only zeroes into the value.
+             */
+            if (BitOffs + Field->V.B.BitWidth != Size * CHAR_BITS) {
+                g_and (CF_INT | CF_UNSIGNED | CF_CONST,
+                       (0x0001U << Field->V.B.BitWidth) - 1U);
+            }
+        } else {
+            g_asr (Flags, BitOffs);
+        }
+
+        /* Use the new type */
+        Expr->Type = FinalType;
+
     } else {
-        ED_MakeLVal (Expr);
-    }
 
-    /* Make the expression a bit field if necessary */
-    if (SymIsBitField (Field)) {
-        ED_MakeBitField (Expr, Field->V.B.BitOffs, Field->V.B.BitWidth);
+        /* Set the struct field offset */
+        Expr->IVal += Field->V.Offs;
+
+        /* Use the new type */
+        Expr->Type = FinalType;
+
+        /* 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
+         * a rvalue, otherwise it's an lvalue. (A function would also be a rvalue,
+         * but a struct field cannot be a function).
+         */
+        if (IsTypeArray (Expr->Type)) {
+            ED_MakeRVal (Expr);
+        } else {
+            ED_MakeLVal (Expr);
+        }
+
+        /* Make the expression a bit field if necessary */
+        if (SymIsBitField (Field)) {
+            ED_MakeBitField (Expr, Field->V.B.BitOffs, Field->V.B.BitWidth);
+        }
     }
+
 }
 
 
@@ -2077,7 +2129,7 @@ static void hie_compare (const GenDesc* Ops,    /* List of generators */
 
                 /* Comparing a char against a constant may have a constant
                  * result. Please note: It is not possible to remove the code
-                * for the compare alltogether, because it may have side 
+                * for the compare alltogether, because it may have side
                 * effects.
                  */
                 switch (Tok) {
index 8edeacdd79bb20ab8761b88f402feb3445358986..e5e592886674514e30d177dc5994ccfd4195ab3b 100644 (file)
@@ -688,7 +688,7 @@ static void MacroCall (StrBuf* Target, Macro* M)
 static void ExpandMacro (StrBuf* Target, Macro* M)
 /* Expand a macro into Target */
 {
-#if 0
+#if 1
     static unsigned V = 0;
     printf ("Expanding %s(%u)\n", M->Name, ++V);
 #endif
@@ -727,7 +727,7 @@ static void ExpandMacro (StrBuf* Target, Macro* M)
         DoneMacroExp (&E);
 
     }
-#if 0
+#if 1
     printf ("Done with %s(%u)\n", M->Name, V--);
 #endif
 }
@@ -845,7 +845,7 @@ static void DefineMacro (void)
     while (IsSpace (SB_LookAtLast (&M->Replacement))) {
         SB_Drop (&M->Replacement, 1);
     }
-#if 0
+#if 1
     printf ("%s: <%.*s>\n", M->Name, SB_GetLen (&M->Replacement), SB_GetConstBuf (&M->Replacement));
 #endif