From 14988f5dda131cb532ad13a901c85c227c6ace81 Mon Sep 17 00:00:00 2001 From: acqn Date: Wed, 16 Feb 2022 20:10:54 +0800 Subject: [PATCH 1/3] Fixed bitwise shift with numeric constant operand(s). --- src/cc65/shiftexpr.c | 54 ++++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 29 deletions(-) diff --git a/src/cc65/shiftexpr.c b/src/cc65/shiftexpr.c index 168574a1b..f7385ace1 100644 --- a/src/cc65/shiftexpr.c +++ b/src/cc65/shiftexpr.c @@ -64,11 +64,11 @@ void ShiftExpr (struct ExprDesc* Expr) CodeMark Mark1; CodeMark Mark2; token_t Tok; /* The operator token */ - const Type* EffType; /* Effective lhs type */ const Type* ResultType; /* Type of the result */ unsigned ExprBits; /* Bits of the lhs operand */ unsigned GenFlags; /* Generator flags */ unsigned ltype; + int lconst; /* Operand is a constant */ int rconst; /* Operand is a constant */ @@ -92,7 +92,7 @@ void ShiftExpr (struct ExprDesc* Expr) NextToken (); /* Get the type of the result */ - ResultType = EffType = IntPromotion (Expr->Type); + ResultType = IntPromotion (Expr->Type); /* Prepare the code generator flags */ GenFlags = TypeOf (ResultType); @@ -103,7 +103,8 @@ void ShiftExpr (struct ExprDesc* Expr) /* Get the lhs on stack */ GetCodePos (&Mark1); ltype = TypeOf (Expr->Type); - if (ED_IsConstAbs (Expr)) { + lconst = ED_IsConstAbs (Expr); + if (lconst) { /* Constant value */ GetCodePos (&Mark2); g_push (ltype | CF_CONST, Expr->IVal); @@ -115,7 +116,7 @@ void ShiftExpr (struct ExprDesc* Expr) } /* Get the right hand side */ - ExprWithCheck (hie8, &Expr2); + MarkedExprWithCheck (hie8, &Expr2); /* Check the type of the rhs */ if (!IsClassInt (Expr2.Type)) { @@ -124,7 +125,7 @@ void ShiftExpr (struct ExprDesc* Expr) } /* Check for a constant right side expression */ - rconst = ED_IsConstAbs (&Expr2); + rconst = ED_IsConstAbs (&Expr2) && ED_CodeRangeIsEmpty (&Expr2); if (!rconst) { /* Not constant, load into the primary */ @@ -154,31 +155,32 @@ void ShiftExpr (struct ExprDesc* Expr) } - /* If the shift count is zero, nothing happens */ - if (Expr2.IVal == 0) { + /* If the shift count is zero, nothing happens. If the left hand + ** side is a constant, the result is constant. + */ + if (Expr2.IVal == 0 || lconst) { - /* Result is already in Expr, remove the generated code */ - RemoveCode (&Mark1); + /* Set the type */ + Expr->Type = ResultType; - /* Done */ - goto Next; - } + if (lconst) { - /* If the left hand side is a constant, the result is constant */ - if (ED_IsConstAbs (Expr)) { + /* Evaluate the result */ + switch (Tok) { + case TOK_SHL: Expr->IVal <<= Expr2.IVal; break; + case TOK_SHR: Expr->IVal >>= Expr2.IVal; break; + default: /* Shutup gcc */ break; + } - /* Evaluate the result */ - switch (Tok) { - case TOK_SHL: Expr->IVal <<= Expr2.IVal; break; - case TOK_SHR: Expr->IVal >>= Expr2.IVal; break; - default: /* Shutup gcc */ break; + /* Limit the calculated value to the range of its type */ + LimitExprValue (Expr); } - /* Both operands are constant, remove the generated code */ + /* Result is already got, remove the generated code */ RemoveCode (&Mark1); /* Done */ - goto Next; + continue; } /* If we're shifting an integer or unsigned to the right, the lhs @@ -188,13 +190,12 @@ void ShiftExpr (struct ExprDesc* Expr) ** shift count is zero, we're done. */ if (Tok == TOK_SHR && - IsTypeInt (Expr->Type) && + IsClassInt (Expr->Type) && + SizeOf (Expr->Type) == SIZEOF_INT && ED_IsLVal (Expr) && ED_IsLocQuasiConst (Expr) && Expr2.IVal >= 8) { - const Type* OldType; - /* Increase the address by one and decrease the shift count */ ++Expr->IVal; Expr2.IVal -= 8; @@ -202,7 +203,6 @@ void ShiftExpr (struct ExprDesc* Expr) /* Replace the type of the expression temporarily by the ** corresponding char type. */ - OldType = Expr->Type; if (IsSignUnsigned (Expr->Type)) { Expr->Type = type_uchar; } else { @@ -215,9 +215,6 @@ void ShiftExpr (struct ExprDesc* Expr) /* Generate again code for the load, this time with the new type */ LoadExpr (CF_NONE, Expr); - /* Reset the type */ - Expr->Type = OldType; - /* If the shift count is now zero, we're done */ if (Expr2.IVal == 0) { /* Be sure to mark the value as in the primary */ @@ -238,7 +235,6 @@ MakeRVal: /* We have an rvalue in the primary now */ ED_FinalizeRValLoad (Expr); -Next: /* Set the type of the result */ Expr->Type = ResultType; } From 2bda128ef183c6b1a26a5d8a1586af25114e629f Mon Sep 17 00:00:00 2001 From: acqn Date: Sat, 26 Feb 2022 23:02:51 +0800 Subject: [PATCH 2/3] Fixed LimitExprValue() for 64-bit long env. --- src/cc65/expr.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/cc65/expr.c b/src/cc65/expr.c index 3b9307a37..8920d5c40 100644 --- a/src/cc65/expr.c +++ b/src/cc65/expr.c @@ -216,8 +216,11 @@ void LimitExprValue (ExprDesc* Expr) break; case T_LONG: + Expr->IVal = (int32_t)Expr->IVal; + break; + case T_ULONG: - /* No need to do anything */ + Expr->IVal = (uint32_t)Expr->IVal; break; case T_SCHAR: From 904a77e03c960cd226882d579f2b3365f62bd6c7 Mon Sep 17 00:00:00 2001 From: acqn Date: Fri, 18 Feb 2022 15:20:01 +0800 Subject: [PATCH 3/3] Testcase for #1675. --- test/val/bug1675-ub.c | 60 +++++++++++++++++++++++++ test/val/bug1675.c | 101 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 161 insertions(+) create mode 100644 test/val/bug1675-ub.c create mode 100644 test/val/bug1675.c diff --git a/test/val/bug1675-ub.c b/test/val/bug1675-ub.c new file mode 100644 index 000000000..72e372308 --- /dev/null +++ b/test/val/bug1675-ub.c @@ -0,0 +1,60 @@ +/* #1675 - Some UB cases of bit-shifts */ + +#include + +int unexpected = 0; + +void Test_UB(void) +{ + { + /* UB per standard, lhs expected in cc65: (int)-32768 */ + if (!((0x4000 << 1) < 0)) { + ++unexpected; + printf("Expected: (0x4000 << 1) < 0, got lhs: %ld\n", (long)(0x4000 << 1)); + } + } + + { + /* UB per standard, lhs expected in cc65: (long)-2147483648L */ + if (!((0x40000000 << 1) < 0)) { + ++unexpected; + printf("Expected: (0x40000000 << 1) < 0, got lhs: %ld\n", (long)(0x40000000 << 1)); + } + } + + { + /* UB per standard, lhs expected in cc65: (int)-32768 */ + if (!(((unsigned char)0x80 << 8) < 0)) { + ++unexpected; + printf("Expected: ((unsigned char)0x80 << 8) < 0, got lhs: %ld\n", (long)((unsigned char)0x80 << 8)); + } + } + + { + /* UB per standard, lhs expected in cc65: (int)-32768 */ + if (!(((short)0x4000L << 1) < 0)) { + ++unexpected; + printf("Expected: ((short)0x4000L << 1) < 0, got lhs: %ld\n", (long)((short)0x4000L << 1)); + } + } + + { + const signed short x = 0x4000; + /* UB per standard, lhs expected in cc65: (int)-32768 */ + if (!((x << 1) < 0)) { + ++unexpected; + printf("Expected: (x << 1) < 0, got lhs: %ld\n", (long)(x << 1)); + } + } +} + +int main(void) +{ + Test_UB(); + + if (unexpected != 0) { + printf("Unexpected: %d\n", unexpected); + } + + return unexpected; +} diff --git a/test/val/bug1675.c b/test/val/bug1675.c new file mode 100644 index 000000000..ee24425df --- /dev/null +++ b/test/val/bug1675.c @@ -0,0 +1,101 @@ +/* #1675 - Some corner cases of bit-shifts */ + +#include + +int failures = 0; + +void Test_Defined(void) +{ + { + /* Well-defined per standard, lhs expected in cc65: (int)-256 */ + if (!(((signed char)0x80 << 1) < 0)) { + ++failures; + printf("Expected: ((signed char)0x80 << 1) < 0, got lhs: %ld\n", (long)((signed char)0x80 << 1)); + } + } + + { + /* Implementation-defined per standard, lhs expected in cc65: (int)-128 */ + if (!(((signed char)0x80 >> 1 << 1) < 0)) { + ++failures; + printf("Expected: ((signed char)0x80 >> 1 << 1) < 0, got lhs: %ld\n", (long)((signed char)0x80 >> 1 << 1)); + } + } + + { + int x = 0; + /* Well-defined per standard, lhs expected in cc65: (int)1 */ + if (!((1 << (x++, 0)) == 1)) { + ++failures; + x = 0; + printf("Expected: (1 << (x++, 0)) == 1, got lhs: %ld\n", (long)(1 << (x++, 0))); + } + + /* Well-defined per standard, lhs expected in cc65: (int)1 */ + if (!(x == 1)) { + ++failures; + printf("Expected: (1 << (x++, 0)) == 1 && x == 1, got x: %d\n", x); + } + } + + { + int x = 0, y = 0x100; + /* Well-defined per standard, lhs expected in cc65: (int)128 */ + if (!((y >> (x++, 0) >> 1) == 0x80)) { + ++failures; + x = 0; + printf("Expected: (y >> (x++, 0) >> 1) == 0x80, got lhs: %ld\n", (long)(y >> (x++, 0) >> 1)); + } + + /* Well-defined per standard, lhs expected in cc65: (int)1 */ + if (!(x == 1)) { + ++failures; + printf("Expected: (y >> (x++, 0) >> 1) == 0x80 && x == 1, got x: %d\n", x); + } + } + + { + int x = 0, y = 0x100; + /* Well-defined per standard, lhs expected in cc65: (int)1 */ + if (!((y >> (x++, 8)) == 1)) { + ++failures; + x = 0; + printf("Expected: (y >> (x++, 8)) == 1, got lhs: %ld\n", (long)(y >> (x++, 8))); + } + + /* Well-defined per standard, lhs expected in cc65: (int)1 */ + if (!(x == 1)) { + ++failures; + printf("Expected: (y >> (x++, 8)) == 1 && x == 1, got x: %d\n", x); + } + } + + { + const signed char x = 0x80; + /* Well-defined per standard, lhs expected in cc65: (int)-256 */ + if (!((x << 1) < 0)) { + ++failures; + printf("Expected: (x << 1) < 0, got lhs: %ld\n", (long)(x << 1)); + } + } + + { + const signed char x = 0x40; + /* Well-defined per standard, lhs expected in cc65: (int)128 */ + if (!((x << 1) >= 0)) { + ++failures; + printf("Expected: (x << 1) >= 0, got lhs: %ld\n", (long)(x << 1)); + } + } +} + +int main(void) +{ + Test_Defined(); + + if (failures != 0) { + printf("Failures: %d\n", failures); + } + + return failures; +}