From cbefb85d47255a314e5aef80195ef719c4489a43 Mon Sep 17 00:00:00 2001 From: uz Date: Fri, 20 Jan 2012 16:01:25 +0000 Subject: [PATCH] Fixed a problem reported by Greg King. structs returned from functions in the 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 | 88 ++++++++++++++++++++++++++++++++++++---------- src/cc65/preproc.c | 6 ++-- 2 files changed, 73 insertions(+), 21 deletions(-) diff --git a/src/cc65/expr.c b/src/cc65/expr.c index 8b5ebd06f..0bafb8ccf 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -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) { diff --git a/src/cc65/preproc.c b/src/cc65/preproc.c index 8edeacdd7..e5e592886 100644 --- a/src/cc65/preproc.c +++ b/src/cc65/preproc.c @@ -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 -- 2.39.2