From 5adb29ce3165db1c5f525dc9a0b8160170e4613c Mon Sep 17 00:00:00 2001 From: acqn Date: Sat, 22 May 2021 19:15:47 +0800 Subject: [PATCH] Made "bit-field-ness" a type property instead of a SymbolEntry or ExprDesc property. Fixed integer promotion and result type in certain operations. Fixed bit-fields 'op=' and postfix inc/dec operators. --- src/cc65/assignment.c | 811 +++++++++++++++++++++++++++++++++++------- src/cc65/assignment.h | 28 +- src/cc65/datatype.c | 74 +++- src/cc65/datatype.h | 108 ++++-- src/cc65/declare.c | 20 +- src/cc65/expr.c | 720 +++++++++++-------------------------- src/cc65/expr.h | 8 + src/cc65/exprdesc.c | 44 +-- src/cc65/exprdesc.h | 40 +-- src/cc65/loadexpr.c | 66 ++-- src/cc65/stdfunc.c | 10 +- src/cc65/symentry.h | 7 - src/cc65/symtab.c | 12 +- src/cc65/typecmp.c | 15 + src/cc65/typeconv.c | 12 +- 15 files changed, 1171 insertions(+), 804 deletions(-) diff --git a/src/cc65/assignment.c b/src/cc65/assignment.c index 7ebd2c4e1..be6a8116f 100644 --- a/src/cc65/assignment.c +++ b/src/cc65/assignment.c @@ -42,19 +42,35 @@ #include "expr.h" #include "loadexpr.h" #include "scanner.h" +#include "stackptr.h" #include "stdnames.h" #include "typecmp.h" #include "typeconv.h" +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Map a generator function and its attributes to a token */ +typedef struct GenDesc { + token_t Tok; /* Token to map to */ + unsigned Flags; /* Flags for generator function */ + void (*Func) (unsigned, unsigned long); /* Generator func */ +} GenDesc; + + + /*****************************************************************************/ /* Code */ /*****************************************************************************/ -static int CopyStruct (ExprDesc* LExpr, ExprDesc* RExpr) +static void CopyStruct (ExprDesc* LExpr, ExprDesc* RExpr) /* Copy the struct/union represented by RExpr to the one represented by LExpr */ { /* If the size is that of a basic type (char, int, long), we will copy @@ -127,14 +143,519 @@ static int CopyStruct (ExprDesc* LExpr, ExprDesc* RExpr) ** to a boolean in C, but there is no harm to be future-proof. */ ED_MarkAsUntested (LExpr); - - return 1; } -void Assignment (ExprDesc* Expr) -/* Parse an assignment */ +void DoIncDecBitField (ExprDesc* Expr, long Val, unsigned KeepResult) +/* Process inc/dec for bit-field */ +{ + int AddrSP; + unsigned Flags; /* Internal codegen flags */ + unsigned Mask; + unsigned ChunkFlags; + const Type* ChunkType; + const Type* ResType; + + /* If the bit-field fits within one byte, do the following operations + ** with bytes. + */ + if ((Expr->Type->A.B.Width - 1U) / CHAR_BITS == + (Expr->Type->A.B.Offs + Expr->Type->A.B.Width - 1U) / CHAR_BITS) { + ChunkType = GetUnderlyingType (Expr->Type); + } else { + /* We use the declarartion integer type as the chunk type. + ** Note: A bit-field will not occupy bits located in bytes more than + ** that of its declaration type in cc65. So this is OK. + */ + ChunkType = Expr->Type + 1; + } + + /* Determine code generator flags */ + Flags = TypeOf (Expr->Type) | CF_FORCECHAR; + ChunkFlags = TypeOf (ChunkType); + if ((ChunkFlags & CF_TYPEMASK) == CF_CHAR) { + ChunkFlags |= CF_FORCECHAR; + } + + /* Get the address on stack for the store */ + PushAddr (Expr); + + /* We may need the pushed address later */ + AddrSP = StackPtr; + + /* Get bit mask to limit the range of the value */ + Mask = (0x0001U << Expr->Type->A.B.Width) - 1U; + + /* Fetch the lhs into the primary register if needed */ + LoadExpr (CF_NONE, Expr); + + if (KeepResult == OA_NEED_OLD) { + /* Save the original expression value */ + g_save (Flags | CF_FORCECHAR); + } + + /* Handle for add and sub */ + if (Val > 0) { + g_inc (Flags | CF_CONST, Val); + } else if (Val < 0) { + g_dec (Flags | CF_CONST, -Val); + } + + /* Apply the mask */ + g_and (Flags | CF_CONST, Mask); + + if (KeepResult == OA_NEED_NEW) { + /* Save the result value */ + g_save (Flags | CF_FORCECHAR); + } + + /* Do integral promotion without sign-extension if needed */ + g_typecast (ChunkFlags | CF_UNSIGNED, Flags); + + /* Shift it into the right position */ + g_asl (ChunkFlags | CF_CONST, Expr->Type->A.B.Offs); + + /* Push the interim result on stack */ + g_push (ChunkFlags & ~CF_FORCECHAR, 0); + + /* If the original lhs was using the primary, it is now accessible only via + ** the pushed address. Reload that address. + */ + if (ED_IsLocPrimaryOrExpr (Expr)) { + g_getlocal (CF_PTR, AddrSP); + } + + /* Load the whole data chunk containing the bits to be changed */ + LoadExpr (ChunkFlags, Expr); + + /* Get the bits that are not to be affected */ + g_and (ChunkFlags | CF_CONST, ~(Mask << Expr->Type->A.B.Offs)); + + /* Restore the bits that are not to be affected */ + g_or (ChunkFlags & ~CF_FORCECHAR, 0); + + /* Store the whole data chunk containing the changed bits back */ + Store (Expr, ChunkType); + + /* Cache the expression result type */ + ResType = IntPromotion (Expr->Type); + + if (KeepResult != OA_NEED_NONE) { + /* Restore the expression result value */ + g_restore (Flags | CF_FORCECHAR); + + /* Promote if needed */ + if (KeepResult != OA_NEED_OLD) { + /* Do unsigned promotion first */ + g_typecast (TypeOf (ResType) | CF_UNSIGNED, Flags); + + /* Then do sign-extension */ + if (IsSignSigned (Expr->Type) && + Expr->Type->A.B.Width < CHAR_BITS * SizeOf (ResType)) { + /* The way is: + ** x = bits & bit_mask + ** m = 1 << (bit_width - 1) + ** r = (x ^ m) - m + ** Since we have already masked bits with bit_mask, we may skip the + ** first step. + */ + g_xor (Flags | CF_CONST, 1U << (Expr->Type->A.B.Width - 1U)); + g_dec ((Flags & ~CF_FORCECHAR) | CF_CONST, 1U << (Expr->Type->A.B.Width - 1U)); + } + } else { + /* Do promotion with sign-extension */ + g_typecast (TypeOf (ResType), Flags); + } + } + + /* Get the expression result type */ + Expr->Type = ResType; +} + + + +static void OpAssignBitField (const GenDesc* Gen, ExprDesc* Expr, const char* Op) +/* Parse an "=" (if 'Gen' is 0) or "op=" operation for bit-field lhs */ +{ + ExprDesc Expr2; + CodeMark PushPos; + int AddrSP; + unsigned Mask; + unsigned Flags; + unsigned ChunkFlags; + const Type* ChunkType; + const Type* ResType; + + /* Cache the expression result type */ + ResType = IntPromotion (Expr->Type); + + ED_Init (&Expr2); + Expr2.Flags |= Expr->Flags & E_MASK_KEEP_SUBEXPR; + + /* If the bit-field fits within one byte, do the following operations + ** with bytes. + */ + if ((Expr->Type->A.B.Width - 1U) / CHAR_BITS == + (Expr->Type->A.B.Offs + Expr->Type->A.B.Width - 1U) / CHAR_BITS) { + ChunkType = GetUnderlyingType (Expr->Type); + } else { + /* We use the declarartion integer type as the chunk type. + ** Note: A bit-field will not occupy bits located in bytes more than + ** that of its declaration type in cc65. So this is OK. + */ + ChunkType = Expr->Type + 1; + } + + /* Determine code generator flags */ + Flags = TypeOf (Expr->Type) | CF_FORCECHAR; + ChunkFlags = TypeOf (ChunkType); + if ((ChunkFlags & CF_TYPEMASK) == CF_CHAR) { + ChunkFlags |= CF_FORCECHAR; + } + + /* Get the address on stack for the store */ + PushAddr (Expr); + + /* We may need the pushed address later */ + AddrSP = StackPtr; + + /* Get bit mask to limit the range of the value */ + Mask = (0x0001U << Expr->Type->A.B.Width) - 1U; + + if (Gen != 0) { + + /* Fetch the lhs into the primary register if needed */ + LoadExpr (CF_NONE, Expr); + + /* Backup them on stack */ + GetCodePos (&PushPos); + g_push (Flags & ~CF_FORCECHAR, 0); + + } + + /* Read the expression on the right side of the '=' or 'op=' */ + 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. + */ + } + + /* Special treatment if the value is constant. + ** Beware: Expr2 may contain side effects, so there must not be + ** code generated for Expr2. + */ + if (ED_IsConstAbsInt (&Expr2) && ED_CodeRangeIsEmpty (&Expr2)) { + + if (Gen == 0) { + + /* Get the value and apply the mask */ + unsigned Val = (unsigned)(Expr2.IVal & Mask); + + /* Load the whole data chunk containing the bits to be changed */ + LoadExpr (ChunkFlags, Expr); + + /* If the value is equal to the mask now, all bits are one, and we + ** can skip the mask operation. + */ + if (Val != Mask) { + /* Get the bits that are not to be affected */ + g_and (ChunkFlags | CF_CONST, ~(Mask << Expr->Type->A.B.Offs)); + } + + /* Restore the bits that are not to be affected */ + g_or (ChunkFlags | CF_CONST, Val << Expr->Type->A.B.Offs); + + /* Store the whole data chunk containing the changed bits back */ + Store (Expr, ChunkType); + + /* Load the expression result value */ + if (IsSignSigned (Expr->Type)) { + unsigned SignExtensionMask = 1 << (Expr->Type->A.B.Width - 1); + Val = (Val^ SignExtensionMask) - SignExtensionMask; + } + ED_MakeConstAbs (Expr, Val, ResType); + LimitExprValue (Expr); + LoadExpr (CF_NONE, Expr); + + /* Done */ + goto Done; + + } else { + + /* Since we will operate with a constant, we can remove the push if + ** the generator has the NOPUSH flag set. + */ + if (Gen->Flags & GEN_NOPUSH) { + RemoveCode (&PushPos); + } + + /* Special handling for add and sub - some sort of a hack, but short code */ + if (Gen->Func == g_add) { + g_inc (Flags | CF_CONST, Expr2.IVal); + } else if (Gen->Func == g_sub) { + g_dec (Flags | CF_CONST, Expr2.IVal); + } else { + if (Expr2.IVal == 0) { + /* Check for div by zero/mod by zero */ + if (Gen->Func == g_div) { + Error ("Division by zero"); + } else if (Gen->Func == g_mod) { + Error ("Modulo operation with zero"); + } + } + + /* Adjust the types of the operands if needed */ + if (Gen->Func == g_div || Gen->Func == g_mod) { + unsigned AdjustedFlags = Flags; + if (Expr->Type->A.B.Width < INT_BITS || IsSignSigned (Expr->Type)) { + AdjustedFlags = (Flags & ~CF_UNSIGNED) | CF_CONST; + AdjustedFlags = g_typeadjust (AdjustedFlags, TypeOf (Expr2.Type) | CF_CONST); + } + Gen->Func (g_typeadjust (Flags, AdjustedFlags) | CF_CONST, Expr2.IVal); + } else { + Gen->Func ((Flags & ~CF_FORCECHAR) | CF_CONST, Expr2.IVal); + } + } + + } + + } else { + + /* Do 'op' if provided */ + if (Gen != 0) { + + /* Load rhs into the primary */ + LoadExpr (CF_NONE, &Expr2); + + /* Adjust the types of the operands if needed */ + if (Gen->Func == g_div || Gen->Func == g_mod) { + unsigned AdjustedFlags = Flags; + if (Expr->Type->A.B.Width < INT_BITS || IsSignSigned (Expr->Type)) { + AdjustedFlags = (Flags & ~CF_UNSIGNED) | CF_CONST; + AdjustedFlags = g_typeadjust (AdjustedFlags, TypeOf (Expr2.Type) | CF_CONST); + } + Gen->Func (g_typeadjust (Flags, AdjustedFlags), 0); + } else { + Gen->Func (g_typeadjust (Flags, TypeOf (Expr2.Type)), 0); + } + + } else { + + /* Do type conversion if necessary */ + TypeConversion (&Expr2, Expr->Type); + + /* If necessary, load rhs into the primary register */ + LoadExpr (CF_NONE, &Expr2); + + } + + } + + /* Apply the mask */ + g_and (Flags | CF_CONST, Mask); + + /* Save the expression result value */ + g_save (Flags); + + /* Do integral promotion without sign-extension if needed */ + g_typecast (ChunkFlags | CF_UNSIGNED, Flags); + + /* Shift it into the right position */ + g_asl (ChunkFlags | CF_CONST, Expr->Type->A.B.Offs); + + /* Push the interim result on stack */ + g_push (ChunkFlags & ~CF_FORCECHAR, 0); + + /* If the original lhs was using the primary, it is now accessible only via + ** the pushed address. Reload that address. + */ + if (ED_IsLocPrimaryOrExpr (Expr)) { + g_getlocal (CF_PTR, AddrSP); + } + + /* Load the whole data chunk containing the bits to be changed */ + LoadExpr (ChunkFlags, Expr); + + /* Get the bits that are not to be affected */ + g_and (ChunkFlags | CF_CONST, ~(Mask << Expr->Type->A.B.Offs)); + + /* Restore the bits that are not to be affected */ + g_or (ChunkFlags & ~CF_FORCECHAR, 0); + + /* Store the whole data chunk containing the changed bits back */ + Store (Expr, ChunkType); + + /* Restore the expression result value */ + g_restore (Flags); + + /* Do unsigned promotion first */ + g_typecast (TypeOf (ResType) | CF_UNSIGNED, Flags); + + /* Then do sign-extension */ + if (IsSignSigned (Expr->Type) && + Expr->Type->A.B.Width < CHAR_BITS * SizeOf (ResType)) { + /* The way is: + ** x = bits & bit_mask + ** m = 1 << (bit_width - 1) + ** r = (x ^ m) - m + ** Since we have already masked bits with bit_mask, we may skip the + ** first step. + */ + g_xor (Flags | CF_CONST, 1U << (Expr->Type->A.B.Width - 1U)); + g_dec ((Flags & ~CF_FORCECHAR) | CF_CONST, 1U << (Expr->Type->A.B.Width - 1U)); + } + +Done: + + /* Get the expression result type */ + Expr->Type = ResType; + + /* Value is in primary as an rvalue */ + ED_FinalizeRValLoad (Expr); +} + + + +static void OpAssignArithmetic (const GenDesc* Gen, ExprDesc* Expr, const char* Op) +/* Parse an "=" (if 'Gen' is 0) or "op=" operation for arithmetic lhs */ +{ + ExprDesc Expr2; + CodeMark PushPos; + + unsigned Flags; + int MustScale; + + ED_Init (&Expr2); + Expr2.Flags |= Expr->Flags & E_MASK_KEEP_SUBEXPR; + + /* Determine code generator flags */ + Flags = TypeOf (Expr->Type); + + /* Determine the type of the lhs */ + MustScale = Gen != 0 && (Gen->Func == g_add || Gen->Func == g_sub) && + IsTypePtr (Expr->Type); + + /* Get the address on stack for the store */ + PushAddr (Expr); + + if (Gen == 0) { + + /* Read the expression on the right side of the '=' */ + MarkedExprWithCheck (hie1, &Expr2); + + /* Do type conversion if necessary. Beware: Do not use char type + ** here! + */ + TypeConversion (&Expr2, Expr->Type); + + /* If necessary, load the value into the primary register */ + LoadExpr (CF_NONE, &Expr2); + + } else { + + /* Load the original value if necessary */ + LoadExpr (CF_NONE, Expr); + + /* Push lhs on stack */ + GetCodePos (&PushPos); + g_push (Flags, 0); + + /* Read the expression on the right side of the '=' or 'op=' */ + 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. + */ + } + + /* Special treatment if the value is constant. + ** Beware: Expr2 may contain side effects, so there must not be + ** code generated for Expr2. + */ + if (ED_IsConstAbsInt (&Expr2) && ED_CodeRangeIsEmpty (&Expr2)) { + + /* Since we will operate with a constant, we can remove the push if + ** the generator has the NOPUSH flag set. + */ + if (Gen->Flags & GEN_NOPUSH) { + RemoveCode (&PushPos); + } + if (MustScale) { + /* lhs is a pointer, scale rhs */ + Expr2.IVal *= CheckedSizeOf (Expr->Type+1); + } + + /* If the lhs is character sized, the operation may be later done + ** with characters. + */ + if (CheckedSizeOf (Expr->Type) == SIZEOF_CHAR) { + Flags |= CF_FORCECHAR; + } + + /* Special handling for add and sub - some sort of a hack, but short code */ + if (Gen->Func == g_add) { + g_inc (Flags | CF_CONST, Expr2.IVal); + } else if (Gen->Func == g_sub) { + g_dec (Flags | CF_CONST, Expr2.IVal); + } else { + if (Expr2.IVal == 0) { + /* Check for div by zero/mod by zero */ + if (Gen->Func == g_div) { + Error ("Division by zero"); + } else if (Gen->Func == g_mod) { + Error ("Modulo operation with zero"); + } + } + Gen->Func (Flags | CF_CONST, Expr2.IVal); + } + + } else { + + /* If necessary, load the value into the primary register */ + LoadExpr (CF_NONE, &Expr2); + + if (MustScale) { + /* lhs is a pointer, scale rhs */ + g_scale (TypeOf (Expr2.Type), CheckedSizeOf (Expr->Type+1)); + } + + /* If the lhs is character sized, the operation may be later done + ** with characters. + */ + if (CheckedSizeOf (Expr->Type) == SIZEOF_CHAR) { + Flags |= CF_FORCECHAR; + } + + /* Adjust the types of the operands if needed */ + Gen->Func (g_typeadjust (Flags, TypeOf (Expr2.Type)), 0); + + } + } + + /* Generate a store instruction */ + Store (Expr, 0); + + /* Get the expression result type */ + if (IsClassInt (Expr->Type)) { + Expr->Type = IntPromotion (Expr->Type); + } + + /* Value is in primary as an rvalue */ + ED_FinalizeRValLoad (Expr); +} + + + +void OpAssign (const GenDesc* Gen, ExprDesc* Expr, const char* Op) +/* Parse an "=" (if 'Gen' is 0) or "op=" operation */ { const Type* ltype = Expr->Type; @@ -142,28 +663,32 @@ void Assignment (ExprDesc* Expr) ED_Init (&Expr2); Expr2.Flags |= Expr->Flags & E_MASK_KEEP_SUBEXPR; - /* We must have an lvalue for an assignment */ - if (ED_IsRVal (Expr)) { - if (IsTypeArray (Expr->Type)) { - Error ("Array type '%s' is not assignable", GetFullTypeName (Expr->Type)); - } else if (IsTypeFunc (Expr->Type)) { - Error ("Function type '%s' is not assignable", GetFullTypeName (Expr->Type)); - } else { - Error ("Assignment to rvalue"); + /* Only "=" accept struct/union */ + if (IsClassStruct (ltype) ? Gen != 0 : !IsClassScalar (ltype)) { + Error ("Invalid left 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. + */ + } else { + /* Check for assignment to incomplete type */ + if (IsIncompleteESUType (ltype)) { + Error ("Assignment to incomplete type '%s'", GetFullTypeName (ltype)); + } else if (ED_IsRVal (Expr)) { + /* Assignment can only be used with lvalues */ + if (IsTypeArray (ltype)) { + Error ("Array type '%s' is not assignable", GetFullTypeName (ltype)); + } else if (IsTypeFunc (ltype)) { + Error ("Function type '%s' is not assignable", GetFullTypeName (ltype)); + } else { + Error ("Assignment to rvalue"); + } + } else if (IsQualConst (ltype)) { + /* Check for assignment to const */ + Error ("Assignment to const"); } } - /* Check for assignment to const */ - if (IsQualConst (ltype)) { - Error ("Assignment to const"); - } - - /* Check for assignment to incomplete type */ - if (IsIncompleteESUType (ltype)) { - Error ("Assignment to incomplete type '%s'", GetFullTypeName (ltype)); - } - - /* Skip the '=' token */ + /* Skip the '=' or 'op=' token */ NextToken (); /* cc65 does not have full support for handling structs or unions. Since @@ -174,114 +699,136 @@ void Assignment (ExprDesc* Expr) if (IsClassStruct (ltype)) { /* Copy the struct or union by value */ CopyStruct (Expr, &Expr2); - - } else if (ED_IsBitField (Expr)) { - - CodeMark AndPos; - CodeMark PushPos; - - unsigned Mask; - unsigned Flags; - - /* If the bit-field fits within one byte, do the following operations - ** with bytes. - */ - if (Expr->BitOffs / CHAR_BITS == (Expr->BitOffs + Expr->BitWidth - 1) / CHAR_BITS) { - Expr->Type = type_uchar; - } - - /* Determine code generator flags */ - Flags = TypeOf (Expr->Type); - - /* Assignment to a bit field. Get the address on stack for the store. */ - PushAddr (Expr); - - /* Load the value from the location */ - Expr->Flags &= ~E_BITFIELD; - LoadExpr (CF_NONE, Expr); - - /* Mask unwanted bits */ - Mask = (0x0001U << Expr->BitWidth) - 1U; - GetCodePos (&AndPos); - g_and (Flags | CF_CONST, ~(Mask << Expr->BitOffs)); - - /* Push it on stack */ - GetCodePos (&PushPos); - g_push (Flags, 0); - - /* Read the expression on the right side of the '=' */ - MarkedExprWithCheck (hie1, &Expr2); - - /* Do type conversion if necessary. Beware: Do not use char type - ** here! - */ - TypeConversion (&Expr2, ltype); - - /* Special treatment if the value is constant. */ - /* Beware: Expr2 may contain side effects, so there must not be - ** code generated for Expr2. - */ - if (ED_IsConstAbsInt (&Expr2) && ED_CodeRangeIsEmpty (&Expr2)) { - - /* Get the value and apply the mask */ - unsigned Val = (unsigned) (Expr2.IVal & Mask); - - /* Since we will do the OR with a constant, we can remove the push */ - RemoveCode (&PushPos); - - /* If the value is equal to the mask now, all bits are one, and we - ** can remove the mask operation from above. - */ - if (Val == Mask) { - RemoveCode (&AndPos); - } - - /* Generate the or operation */ - g_or (Flags | CF_CONST, Val << Expr->BitOffs); - - } else { - - /* If necessary, load the value into the primary register */ - LoadExpr (CF_NONE, &Expr2); - - /* Apply the mask */ - g_and (Flags | CF_CONST, Mask); - - /* Shift it into the right position */ - g_asl (Flags | CF_CONST, Expr->BitOffs); - - /* Or both values */ - g_or (Flags, 0); - } - - /* Generate a store instruction */ - Store (Expr, 0); - - /* Restore the expression type */ - Expr->Type = ltype; - - /* Value is in primary as an rvalue */ - ED_FinalizeRValLoad (Expr); - + } else if (IsTypeBitField (ltype)) { + /* Special care is needed for bit-field 'op=' */ + OpAssignBitField (Gen, Expr, Op); } else { - - /* Get the address on stack if needed */ - PushAddr (Expr); - - /* Read the expression on the right side of the '=' */ - hie1 (&Expr2); - - /* Do type conversion if necessary */ - TypeConversion (&Expr2, ltype); - - /* If necessary, load the value into the primary register */ - LoadExpr (CF_NONE, &Expr2); - - /* Generate a store instruction */ - Store (Expr, 0); - - /* Value is in primary as an rvalue */ - ED_FinalizeRValLoad (Expr); - + /* Normal straight 'op=' */ + OpAssignArithmetic (Gen, Expr, Op); } } + + + +void OpAddSubAssign (const GenDesc* Gen, ExprDesc *Expr, const char* Op) +/* Parse a "+=" or "-=" operation */ +{ + ExprDesc Expr2; + unsigned lflags; + unsigned rflags; + int MustScale; + + /* We currently only handle non-bit-fields in some addressing modes here */ + if (IsTypeBitField (Expr->Type) || ED_IsLocPrimaryOrExpr (Expr)) { + /* Use generic routine instead */ + OpAssign (Gen, Expr, Op); + return; + } + + /* There must be an integer or pointer on the left side */ + if (!IsClassInt (Expr->Type) && !IsTypePtr (Expr->Type)) { + Error ("Invalid left 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. + */ + } else { + /* We must have an lvalue */ + if (ED_IsRVal (Expr)) { + Error ("Invalid lvalue in assignment"); + } else if (IsQualConst (Expr->Type)) { + /* The left side must not be const qualified */ + Error ("Assignment to const"); + } + } + + /* Skip the operator */ + NextToken (); + + /* Check if we have a pointer expression and must scale rhs */ + MustScale = IsTypePtr (Expr->Type); + + /* Initialize the code generator flags */ + lflags = 0; + rflags = 0; + + ED_Init (&Expr2); + Expr2.Flags |= Expr->Flags & E_MASK_KEEP_SUBEXPR; + + /* 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. + */ + } + + /* Setup the code generator flags */ + lflags |= TypeOf (Expr->Type) | GlobalModeFlags (Expr) | CF_FORCECHAR; + rflags |= TypeOf (Expr2.Type) | CF_FORCECHAR; + + if (ED_IsConstAbs (&Expr2)) { + /* The resulting value is a constant */ + rflags |= CF_CONST; + lflags |= CF_CONST; + + /* Scale it */ + if (MustScale) { + Expr2.IVal *= CheckedSizeOf (Indirect (Expr->Type)); + } + } else { + /* Not constant, load into the primary */ + LoadExpr (CF_NONE, &Expr2); + + /* Convert the type of the rhs to that of the lhs */ + g_typecast (lflags, rflags & ~CF_FORCECHAR); + + if (MustScale) { + /* lhs is a pointer, scale rhs */ + g_scale (TypeOf (Expr2.Type), CheckedSizeOf (Indirect (Expr->Type))); + } + } + + /* Output apropriate code depending on the location */ + switch (ED_GetLoc (Expr)) { + + case E_LOC_ABS: + case E_LOC_GLOBAL: + case E_LOC_STATIC: + case E_LOC_REGISTER: + case E_LOC_LITERAL: + case E_LOC_CODE: + /* Absolute numeric addressed variable, global variable, local + ** static variable, register variable, pooled literal or code + ** label location. + */ + if (Gen->Tok == TOK_PLUS_ASSIGN) { + g_addeqstatic (lflags, Expr->Name, Expr->IVal, Expr2.IVal); + } else { + g_subeqstatic (lflags, Expr->Name, Expr->IVal, Expr2.IVal); + } + break; + + case E_LOC_STACK: + /* Value on the stack */ + if (Gen->Tok == TOK_PLUS_ASSIGN) { + g_addeqlocal (lflags, Expr->IVal, Expr2.IVal); + } else { + g_subeqlocal (lflags, Expr->IVal, Expr2.IVal); + } + break; + + default: + Internal ("Invalid location in Store(): 0x%04X", ED_GetLoc (Expr)); + } + + /* Get the expression result type */ + if (IsClassInt (Expr->Type)) { + Expr->Type = IntPromotion (Expr->Type); + } + + /* Expression is an rvalue in the primary now */ + ED_FinalizeRValLoad (Expr); +} diff --git a/src/cc65/assignment.h b/src/cc65/assignment.h index b2cc1548b..6098118b4 100644 --- a/src/cc65/assignment.h +++ b/src/cc65/assignment.h @@ -43,14 +43,38 @@ +/*****************************************************************************/ +/* Data */ +/*****************************************************************************/ + + + +/* Whether to save/restore the original lhs or result value */ +enum { + OA_NEED_NONE, + OA_NEED_OLD, + OA_NEED_NEW, +}; + +/* Forward */ +struct GenDesc; + + + /*****************************************************************************/ /* Code */ /*****************************************************************************/ -void Assignment (ExprDesc* lval); -/* Parse an assignment */ +void DoIncDecBitField (ExprDesc* Expr, long Val, unsigned KeepResult); +/* Process inc/dec for bit-field */ + +void OpAssign (const struct GenDesc* Gen, ExprDesc* lval, const char* Op); +/* Parse an "=" (if 'Gen' is 0) or "op=" operation */ + +void OpAddSubAssign (const struct GenDesc* Gen, ExprDesc *Expr, const char* Op); +/* Parse a "+=" or "-=" operation */ diff --git a/src/cc65/datatype.c b/src/cc65/datatype.c index 6465c27a6..e5d3f8d96 100644 --- a/src/cc65/datatype.c +++ b/src/cc65/datatype.c @@ -207,7 +207,11 @@ static struct StrBuf* GetFullTypeNameWestEast (struct StrBuf* West, struct StrBu } } - SB_AppendStr (&Buf, GetSymTypeName (T)); + if (!IsTypeBitField (T)) { + SB_AppendStr (&Buf, GetSymTypeName (T)); + } else { + SB_AppendStr (&Buf, GetBasicTypeName (T + 1)); + } if (!SB_IsEmpty (West)) { SB_AppendChar (&Buf, ' '); @@ -231,6 +235,7 @@ const char* GetBasicTypeName (const Type* T) { switch (GetRawType (T)) { case T_TYPE_ENUM: return "enum"; + case T_TYPE_BITFIELD: return "bit-field"; case T_TYPE_FLOAT: return "float"; case T_TYPE_DOUBLE: return "double"; case T_TYPE_VOID: return "void"; @@ -581,6 +586,18 @@ const Type* GetUnderlyingType (const Type* Type) if (Type->A.S->V.E.Type != 0) { return Type->A.S->V.E.Type; } + } else if (IsTypeBitField (Type)) { + /* We consider the smallest type that can represent all values of the + ** bit-field, instead of the type used in the declaration, the truly + ** underlying of the bit-field. + */ + unsigned Size = (int)(Type->A.B.Width - 1) / (int)CHAR_BITS + 1; + switch (Size) { + case SIZEOF_CHAR: Type = IsSignSigned (Type) ? type_schar : type_uchar; break; + case SIZEOF_INT: Type = IsSignSigned (Type) ? type_int : type_uint; break; + case SIZEOF_LONG: Type = IsSignSigned (Type) ? type_long : type_ulong; break; + default: Type = IsSignSigned (Type) ? type_int : type_uint; break; + } } return Type; @@ -594,13 +611,14 @@ TypeCode GetUnderlyingTypeCode (const Type* Type) */ { TypeCode Underlying = UnqualifiedType (Type->C); - TypeCode TCode; if (IsISOChar (Type)) { return IS_Get (&SignedChars) ? T_SCHAR : T_UCHAR; } else if (IsTypeEnum (Type)) { + TypeCode TCode; + /* This should not happen, but just in case */ if (Type->A.S == 0) { Internal ("Enum tag type error in GetUnderlyingTypeCode"); @@ -623,6 +641,21 @@ TypeCode GetUnderlyingTypeCode (const Type* Type) case T_SIZE_LONGLONG: Underlying |= T_TYPE_LONGLONG; break; default: Underlying |= T_TYPE_INT; break; } + } else if (IsTypeBitField (Type)) { + /* We consider the smallest type that can represent all values of the + ** bit-field, instead of the type used in the declaration, the truly + ** underlying of the bit-field. + */ + unsigned Size = (int)(Type->A.B.Width - 1) / (int)CHAR_BITS + 1; + switch (Size) { + case SIZEOF_CHAR: Underlying = T_CHAR; break; + case SIZEOF_INT: Underlying = T_INT; break; + case SIZEOF_LONG: Underlying = T_LONG; break; + case SIZEOF_LONGLONG: Underlying = T_LONGLONG; break; + default: Underlying = T_INT; break; + } + Underlying &= ~T_MASK_SIGN; + Underlying |= Type->C & T_MASK_SIGN; } return Underlying; @@ -933,7 +966,11 @@ const Type* IntPromotion (const Type* T) ** These are called the integral promotions. */ - if (IsTypeChar (T)) { + if (IsTypeBitField (T)) { + /* The standard rule is OK for now as we don't support bit-fields with widths > 16. + */ + return T->A.B.Width >= INT_BITS && IsSignUnsigned (T) ? type_uint : type_int; + } else if (IsTypeChar (T)) { /* An integer can represent all values from either signed or unsigned char, so convert ** chars to int. */ @@ -1059,6 +1096,37 @@ const Type* UnsignedType (const Type* T) +Type* NewBitFieldType (const Type* T, unsigned BitOffs, unsigned BitWidth) +/* Return a type string that is "T : BitWidth" aligned on BitOffs. The type +** string is allocated on the heap and may be freed after use. +*/ +{ + Type* P; + + /* The type specifier must be integeral */ + CHECK (IsClassInt (T)); + + /* Allocate the new type string */ + P = TypeAlloc (3); + + /* Create the return type... */ + P[0].C = IsSignSigned (T) ? T_SBITFIELD : T_UBITFIELD; + P[0].C |= (T[0].C & T_QUAL_ADDRSIZE); + P[0].A.B.Offs = BitOffs; + P[0].A.B.Width = BitWidth; + + /* Get the declaration type */ + memcpy (&P[1], GetUnderlyingType (T), sizeof (P[1])); + + /* Get done... */ + P[2].C = T_END; + + /* ...and return it */ + return P; +} + + + int IsClassObject (const Type* T) /* Return true if this is a fully described object type */ { diff --git a/src/cc65/datatype.h b/src/cc65/datatype.h index 4729284bd..e36d7c82e 100644 --- a/src/cc65/datatype.h +++ b/src/cc65/datatype.h @@ -78,54 +78,55 @@ enum { T_TYPE_INT = 0x000003, T_TYPE_LONG = 0x000004, T_TYPE_LONGLONG = 0x000005, - T_TYPE_ENUM = 0x000006, - T_TYPE_FLOAT = 0x000007, - T_TYPE_DOUBLE = 0x000008, - T_TYPE_VOID = 0x000009, - T_TYPE_STRUCT = 0x00000A, - T_TYPE_UNION = 0x00000B, - T_TYPE_ARRAY = 0x00000C, - T_TYPE_PTR = 0x00000D, - T_TYPE_FUNC = 0x00000E, - T_MASK_TYPE = 0x00000F, + T_TYPE_ENUM = 0x000008, + T_TYPE_BITFIELD = 0x000009, + T_TYPE_FLOAT = 0x00000A, + T_TYPE_DOUBLE = 0x00000B, + T_TYPE_VOID = 0x000010, + T_TYPE_STRUCT = 0x000011, + T_TYPE_UNION = 0x000012, + T_TYPE_ARRAY = 0x000018, + T_TYPE_PTR = 0x000019, + T_TYPE_FUNC = 0x00001A, + T_MASK_TYPE = 0x00001F, /* Type classes */ T_CLASS_NONE = 0x000000, - T_CLASS_INT = 0x000010, - T_CLASS_FLOAT = 0x000020, - T_CLASS_PTR = 0x000030, - T_CLASS_STRUCT = 0x000040, - T_CLASS_FUNC = 0x000050, - T_MASK_CLASS = 0x000070, + T_CLASS_INT = 0x000020, + T_CLASS_FLOAT = 0x000040, + T_CLASS_PTR = 0x000060, + T_CLASS_STRUCT = 0x000080, + T_CLASS_FUNC = 0x0000A0, + T_MASK_CLASS = 0x0000E0, /* Type signedness */ T_SIGN_NONE = 0x000000, - T_SIGN_UNSIGNED = 0x000080, - T_SIGN_SIGNED = 0x000100, - T_MASK_SIGN = 0x000180, + T_SIGN_UNSIGNED = 0x000100, + T_SIGN_SIGNED = 0x000200, + T_MASK_SIGN = 0x000300, /* Type size modifiers */ T_SIZE_NONE = 0x000000, - T_SIZE_CHAR = 0x000200, - T_SIZE_SHORT = 0x000400, - T_SIZE_INT = 0x000600, - T_SIZE_LONG = 0x000800, - T_SIZE_LONGLONG = 0x000A00, - T_MASK_SIZE = 0x000E00, + T_SIZE_CHAR = 0x001000, + T_SIZE_SHORT = 0x002000, + T_SIZE_INT = 0x003000, + T_SIZE_LONG = 0x004000, + T_SIZE_LONGLONG = 0x005000, + T_MASK_SIZE = 0x00F000, /* Type qualifiers */ T_QUAL_NONE = 0x000000, - T_QUAL_CONST = 0x001000, - T_QUAL_VOLATILE = 0x002000, - T_QUAL_RESTRICT = 0x004000, + T_QUAL_CONST = 0x010000, + T_QUAL_VOLATILE = 0x020000, + T_QUAL_RESTRICT = 0x040000, T_QUAL_CVR = T_QUAL_CONST | T_QUAL_VOLATILE | T_QUAL_RESTRICT, - T_QUAL_NEAR = 0x008000, - T_QUAL_FAR = 0x010000, + T_QUAL_NEAR = 0x080000, + T_QUAL_FAR = 0x100000, T_QUAL_ADDRSIZE = T_QUAL_NEAR | T_QUAL_FAR, - T_QUAL_FASTCALL = 0x020000, - T_QUAL_CDECL = 0x040000, + T_QUAL_FASTCALL = 0x200000, + T_QUAL_CDECL = 0x400000, T_QUAL_CCONV = T_QUAL_FASTCALL | T_QUAL_CDECL, - T_MASK_QUAL = 0x07F000, + T_MASK_QUAL = 0x7F0000, /* Types */ T_CHAR = T_TYPE_CHAR | T_CLASS_INT | T_SIGN_NONE | T_SIZE_CHAR, @@ -140,6 +141,8 @@ enum { T_LONGLONG = T_TYPE_LONGLONG | T_CLASS_INT | T_SIGN_SIGNED | T_SIZE_LONGLONG, T_ULONGLONG = T_TYPE_LONGLONG | T_CLASS_INT | T_SIGN_UNSIGNED | T_SIZE_LONGLONG, T_ENUM = T_TYPE_ENUM | T_CLASS_INT | T_SIGN_NONE | T_SIZE_NONE, + T_SBITFIELD = T_TYPE_BITFIELD | T_CLASS_INT | T_SIGN_SIGNED | T_SIZE_NONE, + T_UBITFIELD = T_TYPE_BITFIELD | T_CLASS_INT | T_SIGN_UNSIGNED | T_SIZE_NONE, T_FLOAT = T_TYPE_FLOAT | T_CLASS_FLOAT | T_SIGN_NONE | T_SIZE_NONE, T_DOUBLE = T_TYPE_DOUBLE | T_CLASS_FLOAT | T_SIGN_NONE | T_SIZE_NONE, T_VOID = T_TYPE_VOID | T_CLASS_NONE | T_SIGN_NONE | T_SIZE_NONE, @@ -171,6 +174,10 @@ struct Type { struct SymEntry* S; /* Enum/struct/union tag symbol entry pointer */ long L; /* Numeric attribute value */ unsigned long U; /* Dito, unsigned */ + struct { + unsigned Offs; /* Bit offset into storage unit */ + unsigned Width; /* Width in bits */ + } B; /* Data for bit fields */ } A; /* Type attribute if necessary */ }; @@ -372,6 +379,11 @@ const Type* SignedType (const Type* T); const Type* UnsignedType (const Type* T); /* Get unsigned counterpart of the integral type */ +Type* NewBitFieldType (const Type* T, unsigned BitOffs, unsigned BitWidth); +/* Return a type string that is "T : BitWidth" aligned on BitOffs. The type +** string is allocated on the heap and may be freed after use. +*/ + #if defined(HAVE_INLINE) INLINE TypeCode GetRawType (const Type* T) /* Get the raw type */ @@ -514,6 +526,36 @@ INLINE int IsTypeEnum (const Type* T) # define IsTypeEnum(T) (GetRawType (T) == T_TYPE_ENUM) #endif +#if defined(HAVE_INLINE) +INLINE int IsTypeSignedBitField (const Type* T) +/* Return true if this is a signed bit-field */ +{ + return (UnqualifiedType (T->C) == T_SBITFIELD); +} +#else +# define IsTypeSignedBitField(T) (UnqualifiedType ((T)->C) == T_SBITFIELD) +#endif + +#if defined(HAVE_INLINE) +INLINE int IsTypeUnsignedBitField (const Type* T) +/* Return true if this is an unsigned bit-field */ +{ + return (UnqualifiedType (T->C) == T_UBITFIELD); +} +#else +# define IsTypeUnsignedBitField(T) (UnqualifiedType ((T)->C) == T_UBITFIELD) +#endif + +#if defined(HAVE_INLINE) +INLINE int IsTypeBitField (const Type* T) +/* Return true if this is a bit-field (either signed or unsigned) */ +{ + return IsTypeSignedBitField (T) || IsTypeUnsignedBitField (T); +} +#else +# define IsTypeBitField(T) (IsTypeSignedBitField (T) || IsTypeUnsignedBitField (T)) +#endif + #if defined(HAVE_INLINE) INLINE int IsTypeStruct (const Type* T) /* Return true if this is a struct type */ diff --git a/src/cc65/declare.c b/src/cc65/declare.c index 2aa620a29..c1346e872 100644 --- a/src/cc65/declare.c +++ b/src/cc65/declare.c @@ -2533,7 +2533,7 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) ** example two consecutive 10 bit fields. These will be packed ** into 3 bytes. */ - SI.ValBits += Sym->V.B.BitWidth; + SI.ValBits += Sym->Type->A.B.Width; /* TODO: Generalize this so any type can be used. */ CHECK (SI.ValBits <= CHAR_BITS + INT_BITS - 2); while (SI.ValBits >= CHAR_BITS) { @@ -2560,14 +2560,14 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) unsigned Shift; /* Calculate the bitmask from the bit-field data */ - unsigned Mask = (1U << Sym->V.B.BitWidth) - 1U; + unsigned Mask = (1U << Sym->Type->A.B.Width) - 1U; /* Safety ... */ - CHECK (Sym->V.B.Offs * CHAR_BITS + Sym->V.B.BitOffs == - SI.Offs * CHAR_BITS + SI.ValBits); + CHECK (Sym->V.Offs * CHAR_BITS + Sym->Type->A.B.Offs == + SI.Offs * CHAR_BITS + SI.ValBits); /* Read the data, check for a constant integer, do a range check */ - ED = ParseScalarInitInternal (Sym->Type); + ED = ParseScalarInitInternal (IntPromotion (Sym->Type)); if (!ED_IsConstAbsInt (&ED)) { Error ("Constant initializer expected"); ED_MakeConstAbsInt (&ED, 1); @@ -2582,26 +2582,26 @@ static unsigned ParseStructInit (Type* T, int* Braces, int AllowFlexibleMembers) Warning ("Implicit truncation from '%s' to '%s : %u' in bit-field initializer" " changes value from %ld to %u", GetFullTypeName (ED.Type), GetFullTypeName (Sym->Type), - Sym->V.B.BitWidth, ED.IVal, Val); + Sym->Type->A.B.Width, ED.IVal, Val); } } else { /* Sign extend back to full width of host long. */ - unsigned ShiftBits = sizeof (long) * CHAR_BIT - Sym->V.B.BitWidth; + unsigned ShiftBits = sizeof (long) * CHAR_BIT - Sym->Type->A.B.Width; long RestoredVal = asr_l(asl_l (Val, ShiftBits), ShiftBits); if (ED.IVal != RestoredVal) { Warning ("Implicit truncation from '%s' to '%s : %u' in bit-field initializer " "changes value from %ld to %ld", GetFullTypeName (ED.Type), GetFullTypeName (Sym->Type), - Sym->V.B.BitWidth, ED.IVal, RestoredVal); + Sym->Type->A.B.Width, ED.IVal, RestoredVal); } } /* Add the value to the currently stored bit-field value */ - Shift = (Sym->V.B.Offs - SI.Offs) * CHAR_BITS + Sym->V.B.BitOffs; + Shift = (Sym->V.Offs - SI.Offs) * CHAR_BITS + Sym->Type->A.B.Offs; SI.BitVal |= (Val << Shift); /* Account for the data and output any full bytes we have. */ - SI.ValBits += Sym->V.B.BitWidth; + SI.ValBits += Sym->Type->A.B.Width; /* Make sure unsigned is big enough to hold the value, 22 bits. ** This is 22 bits because the most we can have is 7 bits left ** over from the previous OutputBitField call, plus 15 bits diff --git a/src/cc65/expr.c b/src/cc65/expr.c index 729b26942..c45005d65 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -47,13 +47,8 @@ -/* Generator attributes */ -#define GEN_NOPUSH 0x01 /* Don't push lhs */ -#define GEN_COMM 0x02 /* Operator is commutative */ -#define GEN_NOFUNC 0x04 /* Not allowed for function pointers */ - /* Map a generator function and its attributes to a token */ -typedef struct { +typedef struct GenDesc { token_t Tok; /* Token to map to */ unsigned Flags; /* Flags for generator function */ void (*Func) (unsigned, unsigned long); /* Generator func */ @@ -91,7 +86,7 @@ static void PostDec (ExprDesc* Expr); -static unsigned GlobalModeFlags (const ExprDesc* Expr) +unsigned GlobalModeFlags (const ExprDesc* Expr) /* Return the addressing mode flags for the given expression */ { switch (ED_GetLoc (Expr)) { @@ -390,117 +385,175 @@ static void DeferDec (const ExprDesc* Expr) -static void DeferredInc (ExprDesc* Expr) -/* Do the deferred post-inc */ +static void DoInc (ExprDesc* Expr, unsigned KeepResult) +/* Do increment */ { unsigned Flags; - unsigned long Val; - - /* Get the flags */ - Flags = TypeOf (Expr->Type) | GlobalModeFlags (Expr) | CF_FORCECHAR | CF_CONST | CF_NOKEEP; + long Val; /* Get the increment value in bytes */ Val = IsTypePtr (Expr->Type) ? CheckedSizeOf (Expr->Type + 1) : 1; - /* Check the location of the data */ - switch (ED_GetLoc (Expr)) { + /* Special treatment is needed for bit-fields */ + if (IsTypeBitField (Expr->Type)) { + DoIncDecBitField (Expr, Val, KeepResult); + return; + } - case E_LOC_ABS: - /* Absolute: numeric address or const */ - g_addeqstatic (Flags, Expr->IVal, 0, Val); - break; + /* Get the flags */ + Flags = TypeOf (Expr->Type) | GlobalModeFlags (Expr) | CF_FORCECHAR | CF_CONST; + if (KeepResult != OA_NEED_NEW) { + /* No need to get the result */ + Flags |= CF_NOKEEP; + } - case E_LOC_GLOBAL: - /* Global variable */ - g_addeqstatic (Flags, Expr->Name, Expr->IVal, Val); - break; + if (KeepResult == OA_NEED_OLD) { - case E_LOC_STATIC: - case E_LOC_LITERAL: - /* Static variable or literal in the literal pool */ - g_addeqstatic (Flags, Expr->Name, Expr->IVal, Val); - break; + Flags |= CF_FORCECHAR; - case E_LOC_REGISTER: - /* Register variable */ - g_addeqstatic (Flags, Expr->Name, Expr->IVal, Val); - break; + /* Push the address if needed */ + PushAddr (Expr); - case E_LOC_STACK: - /* Value on the stack */ - g_addeqlocal (Flags, Expr->IVal, Val); - break; + /* Save the original value */ + LoadExpr (CF_NONE, Expr); + g_save (Flags); - case E_LOC_PRIMARY: - /* The primary register */ - g_inc (Flags, Val); - break; + /* Do the increment */ + g_inc (Flags | CF_CONST, Val); - case E_LOC_EXPR: - /* An expression in the primary register */ - g_addeqind (Flags, Expr->IVal, Val); - break; + /* Store the result back */ + Store (Expr, 0); + + /* Restore the original value */ + g_restore (Flags); + + } else { + + /* Check the location of the data */ + switch (ED_GetLoc (Expr)) { + + case E_LOC_ABS: + /* Absolute numeric addressed variable */ + g_addeqstatic (Flags, Expr->IVal, 0, Val); + break; + + case E_LOC_GLOBAL: + case E_LOC_STATIC: + case E_LOC_REGISTER: + case E_LOC_LITERAL: + case E_LOC_CODE: + /* Global variabl, static variable, register variable, pooled + ** literal or code label location. + */ + g_addeqstatic (Flags, Expr->Name, Expr->IVal, Val); + break; + + case E_LOC_STACK: + /* Value on the stack */ + g_addeqlocal (Flags, Expr->IVal, Val); + break; + + case E_LOC_PRIMARY: + /* The primary register */ + g_inc (Flags, Val); + break; + + case E_LOC_EXPR: + /* An expression referenced in the primary register */ + g_addeqind (Flags, Expr->IVal, Val); + break; + + default: + Internal ("Invalid location in DoInc(): 0x%04X", ED_GetLoc (Expr)); + } - default: - Internal ("Invalid location in DeferredInc(): 0x%04X", ED_GetLoc (Expr)); } } -static void DeferredDec (ExprDesc* Expr) -/* Do the deferred post-dec */ +static void DoDec (ExprDesc* Expr, unsigned KeepResult) +/* Do decrement */ { unsigned Flags; - unsigned long Val; + long Val; - /* Get the flags */ - Flags = TypeOf (Expr->Type) | GlobalModeFlags (Expr) | CF_FORCECHAR | CF_CONST | CF_NOKEEP; - - /* Get the increment value in bytes */ + /* Get the decrement value in bytes */ Val = IsTypePtr (Expr->Type) ? CheckedSizeOf (Expr->Type + 1) : 1; - /* Check the location of the data */ - switch (ED_GetLoc (Expr)) { + /* Special treatment is needed for bit-fields */ + if (IsTypeBitField (Expr->Type)) { + DoIncDecBitField (Expr, -Val, KeepResult); + return; + } - case E_LOC_ABS: - /* Absolute: numeric address or const */ - g_subeqstatic (Flags, Expr->IVal, 0, Val); - break; + /* Get the flags */ + Flags = TypeOf (Expr->Type) | GlobalModeFlags (Expr) | CF_FORCECHAR | CF_CONST; + if (KeepResult != OA_NEED_NEW) { + /* No need to get the result */ + Flags |= CF_NOKEEP; + } - case E_LOC_GLOBAL: - /* Global variable */ - g_subeqstatic (Flags, Expr->Name, Expr->IVal, Val); - break; + if (KeepResult == OA_NEED_OLD) { - case E_LOC_STATIC: - case E_LOC_LITERAL: - /* Static variable or literal in the literal pool */ - g_subeqstatic (Flags, Expr->Name, Expr->IVal, Val); - break; + Flags |= CF_FORCECHAR; - case E_LOC_REGISTER: - /* Register variable */ - g_subeqstatic (Flags, Expr->Name, Expr->IVal, Val); - break; + /* Push the address if needed */ + PushAddr (Expr); - case E_LOC_STACK: - /* Value on the stack */ - g_subeqlocal (Flags, Expr->IVal, Val); - break; + /* Save the original value */ + LoadExpr (CF_NONE, Expr); + g_save (Flags); - case E_LOC_PRIMARY: - /* The primary register */ - g_dec (Flags, Val); - break; + /* Do the decrement */ + g_dec (Flags | CF_CONST, Val); - case E_LOC_EXPR: - /* An expression in the primary register */ - g_subeqind (Flags, Expr->IVal, Val); - break; + /* Store the result back */ + Store (Expr, 0); + + /* Restore the original value */ + g_restore (Flags); + + } else { + + /* Check the location of the data */ + switch (ED_GetLoc (Expr)) { + + case E_LOC_ABS: + /* Absolute numeric addressed variable */ + g_subeqstatic (Flags, Expr->IVal, 0, Val); + break; + + case E_LOC_GLOBAL: + case E_LOC_STATIC: + case E_LOC_REGISTER: + case E_LOC_LITERAL: + case E_LOC_CODE: + /* Global variabl, static variable, register variable, pooled + ** literal or code label location. + */ + g_subeqstatic (Flags, Expr->Name, Expr->IVal, Val); + break; + + case E_LOC_STACK: + /* Value on the stack */ + g_subeqlocal (Flags, Expr->IVal, Val); + break; + + case E_LOC_PRIMARY: + /* The primary register */ + g_dec (Flags, Val); + break; + + case E_LOC_EXPR: + /* An expression referenced in the primary register */ + g_subeqind (Flags, Expr->IVal, Val); + break; + + default: + Internal ("Invalid location in DoDec(): 0x%04X", ED_GetLoc (Expr)); + } - default: - Internal ("Invalid location in DeferredDec(): 0x%04X", ED_GetLoc (Expr)); } } @@ -564,11 +617,11 @@ void DoDeferred (unsigned Flags, ExprDesc* Expr) switch (Op->OpType) { case DOT_INC: - DeferredInc (&Op->Expr); + DoInc (&Op->Expr, OA_NEED_NONE); break; case DOT_DEC: - DeferredDec (&Op->Expr); + DoDec (&Op->Expr, OA_NEED_NONE); break; } xfree (&Op->Expr); @@ -595,6 +648,7 @@ void DoDeferred (unsigned Flags, ExprDesc* Expr) } + static unsigned FunctionArgList (FuncDesc* Func, int IsFastcall, ExprDesc* ED) /* Parse the argument list of the called function and pass the arguments to it. ** Depending on several criteria, this may be done by just pushing into each @@ -724,14 +778,17 @@ static unsigned FunctionArgList (FuncDesc* Func, int IsFastcall, ExprDesc* ED) if (IsClassStruct (Expr.Type)) { /* Use the replacement type */ Flags |= TypeOf (GetStructReplacementType (Expr.Type)); + + /* Load the value into the primary if it is not already there */ + LoadExpr (Flags, &Expr); } else { + /* Load the value into the primary if it is not already there */ + LoadExpr (CF_NONE, &Expr); + /* Use the type of the argument for the push */ Flags |= TypeOf (Expr.Type); } - /* Load the value into the primary if it is not already there */ - LoadExpr (Flags, &Expr); - /* If this is a fastcall function, don't push the last argument */ if ((CurTok.Tok == TOK_COMMA && NextTok.Tok != TOK_RPAREN) || !IsFastcall) { unsigned ArgSize = sizeofarg (Flags); @@ -1023,7 +1080,7 @@ static void Primary (ExprDesc* E) /* Floating point constant */ if (CurTok.Tok == TOK_FCONST) { - E->FVal = CurTok.FVal; + E->V.FVal = CurTok.FVal; E->Flags |= E_LOC_NONE | E_RTYPE_RVAL; E->Type = CurTok.Type; NextToken (); @@ -1198,9 +1255,9 @@ static void Primary (ExprDesc* E) case TOK_WCSCONST: /* String literal */ if ((Flags & E_EVAL_UNEVAL) != E_EVAL_UNEVAL) { - E->LVal = UseLiteral (CurTok.SVal); + E->V.LVal = UseLiteral (CurTok.SVal); } else { - E->LVal = CurTok.SVal; + E->V.LVal = CurTok.SVal; } E->Type = GetCharArrayType (GetLiteralSize (CurTok.SVal)); E->Flags = E_LOC_LITERAL | E_RTYPE_RVAL | E_ADDRESS_OF; @@ -1400,14 +1457,14 @@ static void StructRef (ExprDesc* Expr) */ BitOffs = Field.V.Offs * CHAR_BITS; if (SymIsBitField (&Field)) { - BitOffs += Field.V.B.BitOffs; + BitOffs += Field.Type->A.B.Offs; 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 != FieldSize * CHAR_BITS) { + if (BitOffs + Field.Type->A.B.Width != FieldSize * CHAR_BITS) { g_and (CF_INT | CF_UNSIGNED | CF_CONST, - (0x0001U << Field.V.B.BitWidth) - 1U); + (0x0001U << Field.Type->A.B.Width) - 1U); } } else { g_asr (Flags, BitOffs); @@ -1434,12 +1491,7 @@ static void StructRef (ExprDesc* Expr) ED_AddrExpr (Expr); } - /* Make the expression a bit field if necessary */ - if (SymIsBitField (&Field)) { - ED_MakeBitField (Expr, Field.V.B.BitOffs, Field.V.B.BitWidth); - } } - } @@ -1582,9 +1634,6 @@ void Store (ExprDesc* Expr, const Type* StoreType) static void PreInc (ExprDesc* Expr) /* Handle the preincrement operators */ { - unsigned Flags; - unsigned long Val; - /* Skip the operator token */ NextToken (); @@ -1600,49 +1649,8 @@ static void PreInc (ExprDesc* Expr) Error ("Increment of read-only variable"); } - /* Get the data type */ - Flags = TypeOf (Expr->Type) | GlobalModeFlags (Expr) | CF_FORCECHAR | CF_CONST; - - /* Get the increment value in bytes */ - Val = IsTypePtr (Expr->Type)? CheckedPSizeOf (Expr->Type) : 1; - - /* Check the location of the data */ - switch (ED_GetLoc (Expr)) { - - case E_LOC_ABS: - /* Absolute numeric addressed variable */ - g_addeqstatic (Flags, Expr->IVal, 0, Val); - break; - - case E_LOC_GLOBAL: - case E_LOC_STATIC: - case E_LOC_REGISTER: - case E_LOC_LITERAL: - case E_LOC_CODE: - /* Global variabl, static variable, register variable, pooled - ** literal or code label location. - */ - g_addeqstatic (Flags, Expr->Name, Expr->IVal, Val); - break; - - case E_LOC_STACK: - /* Value on the stack */ - g_addeqlocal (Flags, Expr->IVal, Val); - break; - - case E_LOC_PRIMARY: - /* The primary register */ - g_inc (Flags, Val); - break; - - case E_LOC_EXPR: - /* An expression referenced in the primary register */ - g_addeqind (Flags, Expr->IVal, Val); - break; - - default: - Internal ("Invalid location in PreInc(): 0x%04X", ED_GetLoc (Expr)); - } + /* Do the increment */ + DoInc (Expr, OA_NEED_NEW); /* Result is an expression, no reference */ ED_FinalizeRValLoad (Expr); @@ -1653,9 +1661,6 @@ static void PreInc (ExprDesc* Expr) static void PreDec (ExprDesc* Expr) /* Handle the predecrement operators */ { - unsigned Flags; - unsigned long Val; - /* Skip the operator token */ NextToken (); @@ -1671,49 +1676,8 @@ static void PreDec (ExprDesc* Expr) Error ("Decrement of read-only variable"); } - /* Get the data type */ - Flags = TypeOf (Expr->Type) | GlobalModeFlags (Expr) | CF_FORCECHAR | CF_CONST; - - /* Get the increment value in bytes */ - Val = IsTypePtr (Expr->Type)? CheckedPSizeOf (Expr->Type) : 1; - - /* Check the location of the data */ - switch (ED_GetLoc (Expr)) { - - case E_LOC_ABS: - /* Absolute numeric addressed variable */ - g_subeqstatic (Flags, Expr->IVal, 0, Val); - break; - - case E_LOC_GLOBAL: - case E_LOC_STATIC: - case E_LOC_REGISTER: - case E_LOC_LITERAL: - case E_LOC_CODE: - /* Global variabl, static variable, register variable, pooled - ** literal or code label location. - */ - g_subeqstatic (Flags, Expr->Name, Expr->IVal, Val); - break; - - case E_LOC_STACK: - /* Value on the stack */ - g_subeqlocal (Flags, Expr->IVal, Val); - break; - - case E_LOC_PRIMARY: - /* The primary register */ - g_dec (Flags, Val); - break; - - case E_LOC_EXPR: - /* An expression in the primary register */ - g_subeqind (Flags, Expr->IVal, Val); - break; - - default: - Internal ("Invalid location in PreDec(): 0x%04X", ED_GetLoc (Expr)); - } + /* Do the decrement */ + DoDec (Expr, OA_NEED_NEW); /* Result is an expression, no reference */ ED_FinalizeRValLoad (Expr); @@ -1724,7 +1688,7 @@ static void PreDec (ExprDesc* Expr) static void PostInc (ExprDesc* Expr) /* Handle the postincrement operator */ { - unsigned Flags, Loc; + unsigned Flags; NextToken (); @@ -1748,34 +1712,17 @@ static void PostInc (ExprDesc* Expr) */ /* Emit smaller code if a char variable is at a constant location */ - if ((Flags & CF_CHAR) == CF_CHAR && ED_IsLocConst (Expr)) { + if ((Flags & CF_TYPEMASK) == CF_CHAR && ED_IsLocConst (Expr) && !IsTypeBitField (Expr->Type)) { LoadExpr (CF_NONE, Expr); AddCodeLine ("inc %s", ED_GetLabelName (Expr, 0)); } else { - Loc = ED_GetLoc (Expr); - if (Loc == E_LOC_PRIMARY || Loc == E_LOC_EXPR) { - /* Push the address if needed */ - PushAddr (Expr); + if (ED_IsLocPrimaryOrExpr (Expr)) { - /* Fetch the value and save it (since it's the result of the expression) */ - LoadExpr (CF_NONE, Expr); - g_save (Flags | CF_FORCECHAR); - - /* If we have a pointer expression, increment by the size of the type */ - if (IsTypePtr (Expr->Type)) { - g_inc (Flags | CF_CONST | CF_FORCECHAR, CheckedSizeOf (Expr->Type + 1)); - } else { - g_inc (Flags | CF_CONST | CF_FORCECHAR, 1); - } - - /* Store the result back */ - Store (Expr, 0); - - /* Restore the original value in the primary register */ - g_restore (Flags | CF_FORCECHAR); + /* Do the increment */ + DoInc (Expr, OA_NEED_OLD); } else { @@ -1787,6 +1734,11 @@ static void PostInc (ExprDesc* Expr) } } + /* Adjust the type of the expression */ + if (IsClassInt (Expr->Type)) { + Expr->Type = IntPromotion (Expr->Type); + } + /* The result is always an expression, no reference */ ED_FinalizeRValLoad (Expr); } @@ -1796,11 +1748,11 @@ static void PostInc (ExprDesc* Expr) static void PostDec (ExprDesc* Expr) /* Handle the postdecrement operator */ { - unsigned Flags, Loc; + unsigned Flags; NextToken (); - /* The expression to increment must be an lvalue */ + /* The expression to decrement must be an lvalue */ if (!ED_IsLVal (Expr)) { Error ("Invalid lvalue"); return; @@ -1815,34 +1767,17 @@ static void PostDec (ExprDesc* Expr) Flags = TypeOf (Expr->Type); /* Emit smaller code if a char variable is at a constant location */ - if ((Flags & CF_CHAR) == CF_CHAR && ED_IsLocConst (Expr)) { + if ((Flags & CF_TYPEMASK) == CF_CHAR && ED_IsLocConst (Expr) && !IsTypeBitField (Expr->Type)) { LoadExpr (CF_NONE, Expr); AddCodeLine ("dec %s", ED_GetLabelName (Expr, 0)); } else { - Loc = ED_GetLoc (Expr); - if (Loc == E_LOC_PRIMARY || Loc == E_LOC_EXPR) { - /* Push the address if needed */ - PushAddr (Expr); + if (ED_IsLocPrimaryOrExpr (Expr)) { - /* Fetch the value and save it (since it's the result of the expression) */ - LoadExpr (CF_NONE, Expr); - g_save (Flags | CF_FORCECHAR); - - /* If we have a pointer expression, increment by the size of the type */ - if (IsTypePtr (Expr->Type)) { - g_dec (Flags | CF_CONST | CF_FORCECHAR, CheckedSizeOf (Expr->Type + 1)); - } else { - g_dec (Flags | CF_CONST | CF_FORCECHAR, 1); - } - - /* Store the result back */ - Store (Expr, 0); - - /* Restore the original value in the primary register */ - g_restore (Flags | CF_FORCECHAR); + /* Do the decrement */ + DoDec (Expr, OA_NEED_OLD); } else { @@ -1854,6 +1789,11 @@ static void PostDec (ExprDesc* Expr) } } + /* Adjust the type of the expression */ + if (IsClassInt (Expr->Type)) { + Expr->Type = IntPromotion (Expr->Type); + } + /* The result is always an expression, no reference */ ED_FinalizeRValLoad (Expr); } @@ -1863,8 +1803,6 @@ static void PostDec (ExprDesc* Expr) static void UnaryOp (ExprDesc* Expr) /* Handle unary -/+ and ~ */ { - unsigned Flags; - /* Remember the operator token and skip it */ token_t Tok = CurTok.Tok; NextToken (); @@ -1888,15 +1826,24 @@ static void UnaryOp (ExprDesc* Expr) default: Internal ("Unexpected token: %d", Tok); } + /* Adjust the type of the expression */ + Expr->Type = IntPromotion (Expr->Type); + /* Limit the calculated value to the range of its type */ LimitExprValue (Expr); } else { + unsigned Flags; + /* Value is not constant */ LoadExpr (CF_NONE, Expr); - /* Adjust the type of the value */ - Flags = g_typeadjust (TypeOf (Expr->Type), TypeOf (type_int) | CF_CONST); + /* Adjust the type of the expression */ + Expr->Type = IntPromotion (Expr->Type); + TypeConversion (Expr, Expr->Type); + + /* Get code generation flags */ + Flags = TypeOf (Expr->Type); /* Handle the operation */ switch (Tok) { @@ -1909,9 +1856,6 @@ static void UnaryOp (ExprDesc* Expr) /* The result is an rvalue in the primary */ ED_FinalizeRValLoad (Expr); } - - /* Adjust the type of the expression */ - Expr->Type = IntPromotion (Expr->Type); } @@ -2003,13 +1947,13 @@ void hie10 (ExprDesc* Expr) if (!IsTypeFunc (Expr->Type) && !IsTypeArray (Expr->Type)) { if (ED_IsRVal (Expr)) { Error ("Illegal address"); - break; + /* Continue anyway, just to avoid further warnings */ } - if (ED_IsBitField (Expr)) { + if (IsTypeBitField (Expr->Type)) { Error ("Cannot take address of bit-field"); - /* Do it anyway, just to avoid further warnings */ - ED_DisBitField (Expr); + /* Continue anyway, just to avoid further warnings */ + Expr->Type = GetUnderlyingType (Expr->Type); } /* The & operator yields an rvalue address */ ED_AddrExpr (Expr); @@ -2034,7 +1978,7 @@ void hie10 (ExprDesc* Expr) ED_Init (&Uneval); ED_MarkForUneval (&Uneval); hie10 (&Uneval); - if (ED_IsBitField (&Uneval)) { + if (IsTypeBitField (Uneval.Type)) { Error ("Cannot apply 'sizeof' to bit-field"); Size = 0; } else { @@ -2536,6 +2480,8 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ /* Determine the signedness of the operands */ int LeftSigned = IsSignSigned (Expr->Type); int RightSigned = IsSignSigned (Expr2.Type); + int CmpSigned = IsClassInt (Expr->Type) && IsClassInt (Expr2.Type) && + IsSignSigned (ArithmeticConvert (Expr->Type, 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 @@ -2630,6 +2576,7 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ */ flags |= (CF_CHAR | CF_FORCECHAR); if (!LeftSigned || !RightSigned) { + CmpSigned = 0; flags |= CF_UNSIGNED; } @@ -2644,10 +2591,15 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ flags |= CF_FORCECHAR; } if (!LeftSigned || !RightSigned) { + CmpSigned = 0; flags |= CF_UNSIGNED; } } else { unsigned rtype = TypeOf (Expr2.Type) | (flags & CF_CONST); + if (CmpSigned) { + ltype &= ~CF_UNSIGNED; + rtype &= ~CF_UNSIGNED; + } flags |= g_typeadjust (ltype, rtype); } @@ -2655,7 +2607,7 @@ static void hie_compare (const GenDesc* Ops, /* List of generators */ ** constant, we may be able to change the compares to something more ** effective. */ - if ((!LeftSigned || !RightSigned) && rconst) { + if (!CmpSigned && rconst) { switch (Tok) { @@ -4121,248 +4073,6 @@ static void hieQuest (ExprDesc* Expr) -static void opeq (const GenDesc* Gen, ExprDesc* Expr, const char* Op) -/* Process "op=" operators. */ -{ - unsigned flags; - CodeMark Mark; - int MustScale; - - /* op= can only be used with lvalues */ - if (ED_IsRVal (Expr)) { - Error ("Invalid lvalue in assignment"); - return; - } - - /* The left side must not be const qualified */ - if (IsQualConst (Expr->Type)) { - Error ("Assignment to const"); - } - - /* There must be an integer or pointer on the left side */ - if (!IsClassInt (Expr->Type) && !IsTypePtr (Expr->Type)) { - Error ("Invalid left 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. - */ - } - - /* Skip the operator token */ - NextToken (); - - /* Determine the type of the lhs */ - flags = TypeOf (Expr->Type); - MustScale = (Gen->Func == g_add || Gen->Func == g_sub) && IsTypePtr (Expr->Type); - - /* Get the lhs address on stack (if needed) */ - PushAddr (Expr); - - /* Fetch the lhs into the primary register if needed */ - LoadExpr (CF_NONE, Expr); - - /* Bring the lhs on stack */ - 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); - - /* 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. - */ - if (Gen->Flags & GEN_NOPUSH) { - RemoveCode (&Mark); - } - if (MustScale) { - /* lhs is a pointer, scale rhs */ - Expr2.IVal *= CheckedSizeOf (Expr->Type+1); - } - - /* If the lhs is character sized, the operation may be later done - ** with characters. - */ - if (CheckedSizeOf (Expr->Type) == SIZEOF_CHAR) { - flags |= CF_FORCECHAR; - } - - /* Special handling for add and sub - some sort of a hack, but short code */ - if (Gen->Func == g_add) { - g_inc (flags | CF_CONST, Expr2.IVal); - } else if (Gen->Func == g_sub) { - g_dec (flags | CF_CONST, Expr2.IVal); - } else { - if (Expr2.IVal == 0) { - /* Check for div by zero/mod by zero */ - if (Gen->Func == g_div) { - Error ("Division by zero"); - } else if (Gen->Func == g_mod) { - Error ("Modulo operation with zero"); - } - } - Gen->Func (flags | CF_CONST, Expr2.IVal); - } - } else { - - /* 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)); - } - - /* If the lhs is character sized, the operation may be later done - ** with characters. - */ - if (CheckedSizeOf (Expr->Type) == SIZEOF_CHAR) { - flags |= CF_FORCECHAR; - } - - /* Adjust the types of the operands if needed */ - Gen->Func (g_typeadjust (flags, TypeOf (Expr2.Type)), 0); - } - Store (Expr, 0); - ED_FinalizeRValLoad (Expr); -} - - - -static void addsubeq (const GenDesc* Gen, ExprDesc *Expr, const char* Op) -/* Process the += and -= operators */ -{ - ExprDesc Expr2; - unsigned lflags; - 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) { - /* Use generic routine */ - opeq (Gen, Expr, Op); - return; - } - - /* We must have an lvalue */ - if (ED_IsRVal (Expr)) { - Error ("Invalid lvalue in assignment"); - return; - } - - /* The left side must not be const qualified */ - if (IsQualConst (Expr->Type)) { - Error ("Assignment to const"); - } - - /* There must be an integer or pointer on the left side */ - if (!IsClassInt (Expr->Type) && !IsTypePtr (Expr->Type)) { - Error ("Invalid left 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. - */ - } - - /* Skip the operator */ - NextToken (); - - /* Check if we have a pointer expression and must scale rhs */ - MustScale = IsTypePtr (Expr->Type); - - /* Initialize the code generator flags */ - lflags = 0; - rflags = 0; - - /* 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. - */ - } - - /* Setup the code generator flags */ - lflags |= TypeOf (Expr->Type) | GlobalModeFlags (Expr) | CF_FORCECHAR; - rflags |= TypeOf (Expr2.Type) | CF_FORCECHAR; - - if (ED_IsConstAbs (&Expr2)) { - /* The resulting value is a constant */ - rflags |= CF_CONST; - lflags |= CF_CONST; - - /* Scale it */ - if (MustScale) { - Expr2.IVal *= CheckedSizeOf (Indirect (Expr->Type)); - } - } else { - /* Not constant, load into the primary */ - LoadExpr (CF_NONE, &Expr2); - - /* Convert the type of the rhs to that of the lhs */ - g_typecast (lflags, rflags & ~CF_FORCECHAR); - - if (MustScale) { - /* lhs is a pointer, scale rhs */ - g_scale (TypeOf (Expr2.Type), CheckedSizeOf (Indirect (Expr->Type))); - } - } - - /* Output apropriate code depending on the location */ - switch (ED_GetLoc (Expr)) { - - case E_LOC_ABS: - case E_LOC_GLOBAL: - case E_LOC_STATIC: - case E_LOC_REGISTER: - case E_LOC_LITERAL: - case E_LOC_CODE: - /* Absolute numeric addressed variable, global variable, local - ** static variable, register variable, pooled literal or code - ** label location. - */ - if (Gen->Tok == TOK_PLUS_ASSIGN) { - g_addeqstatic (lflags, Expr->Name, Expr->IVal, Expr2.IVal); - } else { - g_subeqstatic (lflags, Expr->Name, Expr->IVal, Expr2.IVal); - } - break; - - case E_LOC_STACK: - /* Value on the stack */ - if (Gen->Tok == TOK_PLUS_ASSIGN) { - g_addeqlocal (lflags, Expr->IVal, Expr2.IVal); - } else { - g_subeqlocal (lflags, Expr->IVal, Expr2.IVal); - } - break; - - default: - Internal ("Invalid location in Store(): 0x%04X", ED_GetLoc (Expr)); - } - - /* Expression is an rvalue in the primary now */ - ED_FinalizeRValLoad (Expr); -} - - - void hie1 (ExprDesc* Expr) /* Parse first level of expression hierarchy. */ { @@ -4370,47 +4080,47 @@ void hie1 (ExprDesc* Expr) switch (CurTok.Tok) { case TOK_ASSIGN: - Assignment (Expr); + OpAssign (0, Expr, "="); break; case TOK_PLUS_ASSIGN: - addsubeq (&GenPASGN, Expr, "+="); + OpAddSubAssign (&GenPASGN, Expr, "+="); break; case TOK_MINUS_ASSIGN: - addsubeq (&GenSASGN, Expr, "-="); + OpAddSubAssign (&GenSASGN, Expr, "-="); break; case TOK_MUL_ASSIGN: - opeq (&GenMASGN, Expr, "*="); + OpAssign (&GenMASGN, Expr, "*="); break; case TOK_DIV_ASSIGN: - opeq (&GenDASGN, Expr, "/="); + OpAssign (&GenDASGN, Expr, "/="); break; case TOK_MOD_ASSIGN: - opeq (&GenMOASGN, Expr, "%="); + OpAssign (&GenMOASGN, Expr, "%="); break; case TOK_SHL_ASSIGN: - opeq (&GenSLASGN, Expr, "<<="); + OpAssign (&GenSLASGN, Expr, "<<="); break; case TOK_SHR_ASSIGN: - opeq (&GenSRASGN, Expr, ">>="); + OpAssign (&GenSRASGN, Expr, ">>="); break; case TOK_AND_ASSIGN: - opeq (&GenAASGN, Expr, "&="); + OpAssign (&GenAASGN, Expr, "&="); break; case TOK_XOR_ASSIGN: - opeq (&GenXOASGN, Expr, "^="); + OpAssign (&GenXOASGN, Expr, "^="); break; case TOK_OR_ASSIGN: - opeq (&GenOASGN, Expr, "|="); + OpAssign (&GenOASGN, Expr, "|="); break; default: diff --git a/src/cc65/expr.h b/src/cc65/expr.h index 4909815ee..841edcb62 100644 --- a/src/cc65/expr.h +++ b/src/cc65/expr.h @@ -28,6 +28,11 @@ #define SQP_KEEP_EAX 0x02U #define SQP_KEEP_EXPR 0x03U /* SQP_KEEP_TEST | SQP_KEEP_EAX */ +/* Generator attributes */ +#define GEN_NOPUSH 0x01 /* Don't push lhs */ +#define GEN_COMM 0x02 /* Operator is commutative */ +#define GEN_NOFUNC 0x04 /* Not allowed for function pointers */ + /*****************************************************************************/ @@ -36,6 +41,9 @@ +unsigned GlobalModeFlags (const ExprDesc* Expr); +/* Return the addressing mode flags for the given expression */ + void ExprWithCheck (void (*Func) (ExprDesc*), ExprDesc* Expr); /* Call an expression function with checks. */ diff --git a/src/cc65/exprdesc.c b/src/cc65/exprdesc.c index 1d4fd6872..3d7b7c384 100644 --- a/src/cc65/exprdesc.c +++ b/src/cc65/exprdesc.c @@ -56,30 +56,17 @@ ExprDesc* ED_Init (ExprDesc* Expr) /* Initialize an ExprDesc */ { - Expr->Sym = 0; Expr->Type = 0; Expr->Flags = E_NEED_EAX; Expr->Name = 0; + Expr->Sym = 0; Expr->IVal = 0; - Expr->FVal = FP_D_Make (0.0); - Expr->LVal = 0; - Expr->BitOffs = 0; - Expr->BitWidth = 0; + memset (&Expr->V, 0, sizeof (Expr->V)); return Expr; } -void ED_MakeBitField (ExprDesc* Expr, unsigned BitOffs, unsigned BitWidth) -/* Make this expression a bit field expression */ -{ - Expr->Flags |= E_BITFIELD; - Expr->BitOffs = BitOffs; - Expr->BitWidth = BitWidth; -} - - - #if !defined(HAVE_INLINE) int ED_IsLocQuasiConst (const ExprDesc* Expr) /* Return true if the expression is a constant location of some sort or on the @@ -231,12 +218,12 @@ int ED_GetStackOffs (const ExprDesc* Expr, int Offs) ExprDesc* ED_MakeConstAbs (ExprDesc* Expr, long Value, const Type* Type) /* Replace Expr with an absolute const with the given value and type */ { - Expr->Sym = 0; Expr->Type = Type; Expr->Flags = E_LOC_NONE | E_RTYPE_RVAL | (Expr->Flags & E_MASK_KEEP_MAKE); Expr->Name = 0; + Expr->Sym = 0; Expr->IVal = Value; - Expr->FVal = FP_D_Make (0.0); + memset (&Expr->V, 0, sizeof (Expr->V)); return Expr; } @@ -245,12 +232,12 @@ ExprDesc* ED_MakeConstAbs (ExprDesc* Expr, long Value, const Type* Type) ExprDesc* ED_MakeConstAbsInt (ExprDesc* Expr, long Value) /* Replace Expr with a constant integer expression with the given 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->Sym = 0; Expr->IVal = Value; - Expr->FVal = FP_D_Make (0.0); + memset (&Expr->V, 0, sizeof (Expr->V)); return Expr; } @@ -264,7 +251,7 @@ ExprDesc* ED_MakeConstBool (ExprDesc* Expr, long Value) 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); + memset (&Expr->V, 0, sizeof (Expr->V)); return Expr; } @@ -273,13 +260,13 @@ ExprDesc* ED_MakeConstBool (ExprDesc* Expr, long Value) ExprDesc* ED_FinalizeRValLoad (ExprDesc* Expr) /* Finalize the result of LoadExpr to be an rvalue in the primary register */ { - Expr->Sym = 0; - Expr->Flags &= ~(E_MASK_LOC | E_MASK_RTYPE | E_BITFIELD | E_ADDRESS_OF); + Expr->Flags &= ~(E_MASK_LOC | E_MASK_RTYPE | E_ADDRESS_OF); Expr->Flags &= ~E_CC_SET; Expr->Flags |= (E_LOC_PRIMARY | E_RTYPE_RVAL); + Expr->Sym = 0; Expr->Name = 0; Expr->IVal = 0; /* No offset */ - Expr->FVal = FP_D_Make (0.0); + memset (&Expr->V, 0, sizeof (Expr->V)); return Expr; } @@ -464,8 +451,8 @@ int ED_IsQuasiConstAddr (const ExprDesc* Expr) int ED_IsNullPtr (const ExprDesc* Expr) /* Return true if the given expression is a NULL pointer constant */ { - return (Expr->Flags & (E_MASK_LOC|E_MASK_RTYPE|E_BITFIELD)) == - (E_LOC_NONE|E_RTYPE_RVAL) && + return (Expr->Flags & (E_MASK_LOC|E_MASK_RTYPE)) == + (E_LOC_NONE|E_RTYPE_RVAL) && Expr->IVal == 0 && IsClassInt (Expr->Type); } @@ -503,7 +490,7 @@ void PrintExprDesc (FILE* F, ExprDesc* E) "Raw type: (unknown)\n"); } fprintf (F, "IVal: 0x%08lX\n", E->IVal); - fprintf (F, "FVal: %f\n", FP_D_ToFloat (E->FVal)); + fprintf (F, "FVal: %f\n", FP_D_ToFloat (E->V.FVal)); Flags = E->Flags; Sep = '('; @@ -558,11 +545,6 @@ void PrintExprDesc (FILE* F, ExprDesc* E) Flags &= ~E_LOC_CODE; Sep = ','; } - if (Flags & E_BITFIELD) { - fprintf (F, "%cE_BITFIELD", Sep); - Flags &= ~E_BITFIELD; - Sep = ','; - } if (Flags & E_NEED_TEST) { fprintf (F, "%cE_NEED_TEST", Sep); Flags &= ~E_NEED_TEST; diff --git a/src/cc65/exprdesc.h b/src/cc65/exprdesc.h index a46685b59..a1487a0bd 100644 --- a/src/cc65/exprdesc.h +++ b/src/cc65/exprdesc.h @@ -114,7 +114,6 @@ enum { 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 */ @@ -198,17 +197,15 @@ struct Literal; /* Describe the result of an expression */ typedef struct ExprDesc ExprDesc; struct ExprDesc { - struct SymEntry* Sym; /* Symbol table entry if known */ - const Type* Type; /* Type array of expression */ - unsigned Flags; + const Type* Type; /* C type of the expression */ + unsigned Flags; /* Properties of the expression */ uintptr_t Name; /* Name pointer or label number */ + struct SymEntry* Sym; /* Symbol table entry if any */ long IVal; /* Integer value if expression constant */ - Double FVal; /* Floating point value */ - struct Literal* LVal; /* Literal value */ - - /* Bit field stuff */ - unsigned BitOffs; /* Bit offset for bit fields */ - unsigned BitWidth; /* Bit width for bit fields */ + union { + Double FVal; /* Floating point value */ + struct Literal* LVal; /* Literal value */ + } V; /* Start and end of generated code */ CodeMark Start; @@ -331,29 +328,6 @@ int ED_IsLocQuasiConst (const ExprDesc* Expr); */ #endif -#if defined(HAVE_INLINE) -INLINE int ED_IsBitField (const ExprDesc* Expr) -/* Return true if the expression is a bit field */ -{ - return (Expr->Flags & E_BITFIELD) != 0; -} -#else -# define ED_IsBitField(Expr) (((Expr)->Flags & E_BITFIELD) != 0) -#endif - -#if defined(HAVE_INLINE) -INLINE void ED_DisBitField (ExprDesc* Expr) -/* Make the expression no longer a bit field */ -{ - Expr->Flags &= ~E_BITFIELD; -} -#else -# define ED_DisBitField(Expr) ((Expr)->Flags &= ~E_BITFIELD) -#endif - -void ED_MakeBitField (ExprDesc* Expr, unsigned BitOffs, unsigned BitWidth); -/* Make this expression a bit field expression */ - #if defined(HAVE_INLINE) INLINE void ED_RequireTest (ExprDesc* Expr) /* Mark the expression for a test. */ diff --git a/src/cc65/loadexpr.c b/src/cc65/loadexpr.c index 95617f596..a742087b7 100644 --- a/src/cc65/loadexpr.c +++ b/src/cc65/loadexpr.c @@ -124,38 +124,40 @@ void LoadExpr (unsigned Flags, struct ExprDesc* Expr) */ int AdjustBitField = 0; unsigned BitFieldFullWidthFlags = 0; - if (ED_IsBitField (Expr)) { - unsigned EndBit = Expr->BitOffs + Expr->BitWidth; - AdjustBitField = Expr->BitOffs != 0 || (EndBit != CHAR_BITS && EndBit != INT_BITS); + if ((Flags & CF_TYPEMASK) == 0) { + if (IsTypeBitField (Expr->Type)) { + unsigned EndBit = Expr->Type->A.B.Offs + Expr->Type->A.B.Width; + AdjustBitField = Expr->Type->A.B.Offs != 0 || (EndBit != CHAR_BITS && EndBit != INT_BITS); - /* TODO: This probably needs to be guarded by AdjustBitField when long bit-fields are - ** supported. - */ - Flags |= (EndBit <= CHAR_BITS) ? CF_CHAR : CF_INT; - if (IsSignUnsigned (Expr->Type)) { - Flags |= CF_UNSIGNED; - } - - /* Flags we need operate on the whole bit-field, without CF_FORCECHAR. */ - BitFieldFullWidthFlags = Flags; - - /* If we're adjusting, then only load a char (not an int) and do only char ops; - ** We will clear the high byte in the adjustment. CF_FORCECHAR does nothing if the - ** type is not CF_CHAR. - */ - if (AdjustBitField) { - /* If adjusting, then we're sign extending manually, so do everything unsigned - ** to make shifts faster. + /* TODO: This probably needs to be guarded by AdjustBitField when long bit-fields are + ** supported. */ - Flags |= CF_UNSIGNED | CF_FORCECHAR; - BitFieldFullWidthFlags |= CF_UNSIGNED; + Flags |= (EndBit <= CHAR_BITS) ? CF_CHAR : CF_INT; + if (IsSignUnsigned (Expr->Type)) { + Flags |= CF_UNSIGNED; + } + + /* Flags we need operate on the whole bit-field, without CF_FORCECHAR. */ + BitFieldFullWidthFlags = Flags; + + /* If we're adjusting, then only load a char (not an int) and do only char ops; + ** We will clear the high byte in the adjustment. CF_FORCECHAR does nothing if the + ** type is not CF_CHAR. + */ + if (AdjustBitField) { + /* If adjusting, then we're sign extending manually, so do everything unsigned + ** to make shifts faster. + */ + Flags |= CF_UNSIGNED | CF_FORCECHAR; + BitFieldFullWidthFlags |= CF_UNSIGNED; + } + } else { + /* If Expr is an incomplete ESY type, bail out */ + if (IsIncompleteESUType (Expr->Type)) { + return; + } + Flags |= TypeOf (Expr->Type); } - } else if ((Flags & CF_TYPEMASK) == 0) { - /* If Expr is an incomplete ESY type, bail out */ - if (IsIncompleteESUType (Expr->Type)) { - return; - } - Flags |= TypeOf (Expr->Type); } if (ED_YetToTest (Expr)) { @@ -254,13 +256,13 @@ void LoadExpr (unsigned Flags, struct ExprDesc* Expr) /* We always need to do something with the low byte, so there is no opportunity ** for optimization by skipping it. */ - CHECK (Expr->BitOffs < CHAR_BITS); + CHECK (Expr->Type->A.B.Offs < CHAR_BITS); if (ED_YetToTest (Expr)) { - g_testbitfield (Flags, Expr->BitOffs, Expr->BitWidth); + g_testbitfield (Flags, Expr->Type->A.B.Offs, Expr->Type->A.B.Width); } else { g_extractbitfield (Flags, BitFieldFullWidthFlags, IsSignSigned (Expr->Type), - Expr->BitOffs, Expr->BitWidth); + Expr->Type->A.B.Offs, Expr->Type->A.B.Width); } } diff --git a/src/cc65/stdfunc.c b/src/cc65/stdfunc.c index bdc7be006..37566a455 100644 --- a/src/cc65/stdfunc.c +++ b/src/cc65/stdfunc.c @@ -832,8 +832,8 @@ static void StdFunc_strcmp (FuncDesc* F attribute ((unused)), ExprDesc* Expr) */ if (ED_IsLocLiteral (&Arg2.Expr) && IS_Get (&WritableStrings) == 0 && - GetLiteralSize (Arg2.Expr.LVal) == 1 && - GetLiteralStr (Arg2.Expr.LVal)[0] == '\0') { + GetLiteralSize (Arg2.Expr.V.LVal) == 1 && + GetLiteralStr (Arg2.Expr.V.LVal)[0] == '\0') { /* Drop the generated code so we have the first argument in the ** primary @@ -841,7 +841,7 @@ static void StdFunc_strcmp (FuncDesc* F attribute ((unused)), ExprDesc* Expr) RemoveCode (&Arg1.Push); /* We don't need the literal any longer */ - ReleaseLiteral (Arg2.Expr.LVal); + ReleaseLiteral (Arg2.Expr.V.LVal); /* We do now have Arg1 in the primary. Load the first character from ** this string and cast to int. This is the function result. @@ -1232,10 +1232,10 @@ static void StdFunc_strlen (FuncDesc* F attribute ((unused)), ExprDesc* Expr) if (ED_IsLocLiteral (&Arg) && IS_Get (&WritableStrings) == 0) { /* Constant string literal */ - ED_MakeConstAbs (Expr, GetLiteralSize (Arg.LVal) - 1, type_size_t); + ED_MakeConstAbs (Expr, GetLiteralSize (Arg.V.LVal) - 1, type_size_t); /* We don't need the literal any longer */ - ReleaseLiteral (Arg.LVal); + ReleaseLiteral (Arg.V.LVal); /* Bail out, no need for further improvements */ goto ExitPoint; diff --git a/src/cc65/symentry.h b/src/cc65/symentry.h index 56d884bb6..bb87c7472 100644 --- a/src/cc65/symentry.h +++ b/src/cc65/symentry.h @@ -183,13 +183,6 @@ struct SymEntry { const Type* Type; /* Underlying type */ } E; - /* Data for bit fields */ - struct { - unsigned Offs; /* Byte offset into struct */ - unsigned BitOffs; /* Bit offset into storage unit */ - unsigned BitWidth; /* Width in bits */ - } B; - /* Data for functions */ struct { struct Segments* Seg; /* Segments for this function */ diff --git a/src/cc65/symtab.c b/src/cc65/symtab.c index 4073a38bc..5d7bd1436 100644 --- a/src/cc65/symtab.c +++ b/src/cc65/symtab.c @@ -881,10 +881,8 @@ SymEntry* AddBitField (const char* Name, const Type* T, unsigned Offs, Entry = NewSymEntry (Name, SC_BITFIELD); /* Set the symbol attributes. Bit-fields are always integral types. */ - Entry->Type = TypeDup (T); - Entry->V.B.Offs = Offs; - Entry->V.B.BitOffs = BitOffs; - Entry->V.B.BitWidth = BitWidth; + Entry->Type = NewBitFieldType (T, BitOffs, BitWidth); + Entry->V.Offs = Offs; if (!SignednessSpecified) { /* int is treated as signed int everywhere except bit-fields; switch it to unsigned, @@ -896,8 +894,10 @@ SymEntry* AddBitField (const char* Name, const Type* T, unsigned Offs, */ CHECK ((Entry->Type->C & T_MASK_SIGN) == T_SIGN_SIGNED || IsTypeChar (Entry->Type)); - Entry->Type->C &= ~T_MASK_SIGN; - Entry->Type->C |= T_SIGN_UNSIGNED; + Entry->Type[0].C &= ~T_MASK_SIGN; + Entry->Type[0].C |= T_SIGN_UNSIGNED; + Entry->Type[1].C &= ~T_MASK_SIGN; + Entry->Type[1].C |= T_SIGN_UNSIGNED; } /* Add the entry to the symbol table */ diff --git a/src/cc65/typecmp.c b/src/cc65/typecmp.c index 8c9da3445..6052f4a84 100644 --- a/src/cc65/typecmp.c +++ b/src/cc65/typecmp.c @@ -278,6 +278,21 @@ static void DoCompare (const Type* lhs, const Type* rhs, typecmp_t* Result) SetResult (Result, TC_STRICT_COMPATIBLE); } + /* Bit-fields are considered compatible if they have the same + ** signedness, bit-offset and bit-width. + */ + if (IsTypeBitField (lhs) || IsTypeBitField (rhs)) { + if (!IsTypeBitField (lhs) || + !IsTypeBitField (rhs) || + lhs->A.B.Offs != rhs->A.B.Offs || + lhs->A.B.Width != rhs->A.B.Width) { + SetResult (Result, TC_INCOMPATIBLE); + } + if (LeftType != RightType) { + SetResult (Result, TC_STRICT_COMPATIBLE); + } + } + /* If the underlying types are not identical, the types are incompatible */ if (LeftType != RightType) { SetResult (Result, TC_INCOMPATIBLE); diff --git a/src/cc65/typeconv.c b/src/cc65/typeconv.c index 16f173cc4..a7528a2f8 100644 --- a/src/cc65/typeconv.c +++ b/src/cc65/typeconv.c @@ -83,11 +83,16 @@ static void DoConversion (ExprDesc* Expr, const Type* NewType) /* Get the sizes of the types. Since we've excluded void types, checking ** for known sizes makes sense here. */ - if (ED_IsBitField (Expr)) { - OldBits = Expr->BitWidth; + if (IsTypeBitField (OldType)) { + OldBits = OldType->A.B.Width; } else { OldBits = CheckedSizeOf (OldType) * CHAR_BITS; } + + /* If the new type is a bit-field, we use its underlying type instead */ + if (IsTypeBitField (NewType)) { + NewType = GetUnderlyingType (NewType); + } NewBits = CheckedSizeOf (NewType) * CHAR_BITS; /* lvalue? */ @@ -167,9 +172,6 @@ static void DoConversion (ExprDesc* Expr, const Type* NewType) ExitPoint: /* The expression has always the new type */ ReplaceType (Expr, NewType); - - /* Bit-fields are converted to integers */ - ED_DisBitField (Expr); }