1
0
mirror of https://github.com/cc65/cc65.git synced 2025-02-05 20:31:53 +00:00

Merge pull request #1675 from acqn/ShiftFix

[cc65] Fixed bitwise shift with numeric constant operand(s)
This commit is contained in:
Bob Andrews 2022-03-01 19:50:21 +01:00 committed by GitHub
commit 0587d9f5d4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 190 additions and 30 deletions

View File

@ -216,8 +216,11 @@ void LimitExprValue (ExprDesc* Expr)
break; break;
case T_LONG: case T_LONG:
Expr->IVal = (int32_t)Expr->IVal;
break;
case T_ULONG: case T_ULONG:
/* No need to do anything */ Expr->IVal = (uint32_t)Expr->IVal;
break; break;
case T_SCHAR: case T_SCHAR:

View File

@ -64,11 +64,11 @@ void ShiftExpr (struct ExprDesc* Expr)
CodeMark Mark1; CodeMark Mark1;
CodeMark Mark2; CodeMark Mark2;
token_t Tok; /* The operator token */ token_t Tok; /* The operator token */
const Type* EffType; /* Effective lhs type */
const Type* ResultType; /* Type of the result */ const Type* ResultType; /* Type of the result */
unsigned ExprBits; /* Bits of the lhs operand */ unsigned ExprBits; /* Bits of the lhs operand */
unsigned GenFlags; /* Generator flags */ unsigned GenFlags; /* Generator flags */
unsigned ltype; unsigned ltype;
int lconst; /* Operand is a constant */
int rconst; /* Operand is a constant */ int rconst; /* Operand is a constant */
@ -92,7 +92,7 @@ void ShiftExpr (struct ExprDesc* Expr)
NextToken (); NextToken ();
/* Get the type of the result */ /* Get the type of the result */
ResultType = EffType = IntPromotion (Expr->Type); ResultType = IntPromotion (Expr->Type);
/* Prepare the code generator flags */ /* Prepare the code generator flags */
GenFlags = TypeOf (ResultType); GenFlags = TypeOf (ResultType);
@ -103,7 +103,8 @@ void ShiftExpr (struct ExprDesc* Expr)
/* Get the lhs on stack */ /* Get the lhs on stack */
GetCodePos (&Mark1); GetCodePos (&Mark1);
ltype = TypeOf (Expr->Type); ltype = TypeOf (Expr->Type);
if (ED_IsConstAbs (Expr)) { lconst = ED_IsConstAbs (Expr);
if (lconst) {
/* Constant value */ /* Constant value */
GetCodePos (&Mark2); GetCodePos (&Mark2);
g_push (ltype | CF_CONST, Expr->IVal); g_push (ltype | CF_CONST, Expr->IVal);
@ -115,7 +116,7 @@ void ShiftExpr (struct ExprDesc* Expr)
} }
/* Get the right hand side */ /* Get the right hand side */
ExprWithCheck (hie8, &Expr2); MarkedExprWithCheck (hie8, &Expr2);
/* Check the type of the rhs */ /* Check the type of the rhs */
if (!IsClassInt (Expr2.Type)) { if (!IsClassInt (Expr2.Type)) {
@ -124,7 +125,7 @@ void ShiftExpr (struct ExprDesc* Expr)
} }
/* Check for a constant right side expression */ /* Check for a constant right side expression */
rconst = ED_IsConstAbs (&Expr2); rconst = ED_IsConstAbs (&Expr2) && ED_CodeRangeIsEmpty (&Expr2);
if (!rconst) { if (!rconst) {
/* Not constant, load into the primary */ /* Not constant, load into the primary */
@ -154,31 +155,32 @@ void ShiftExpr (struct ExprDesc* Expr)
} }
/* If the shift count is zero, nothing happens */ /* If the shift count is zero, nothing happens. If the left hand
if (Expr2.IVal == 0) { ** side is a constant, the result is constant.
*/
if (Expr2.IVal == 0 || lconst) {
/* Result is already in Expr, remove the generated code */ /* Set the type */
RemoveCode (&Mark1); Expr->Type = ResultType;
/* Done */ if (lconst) {
goto Next;
}
/* If the left hand side is a constant, the result is constant */ /* Evaluate the result */
if (ED_IsConstAbs (Expr)) { 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 */ /* Limit the calculated value to the range of its type */
switch (Tok) { LimitExprValue (Expr);
case TOK_SHL: Expr->IVal <<= Expr2.IVal; break;
case TOK_SHR: Expr->IVal >>= Expr2.IVal; break;
default: /* Shutup gcc */ break;
} }
/* Both operands are constant, remove the generated code */ /* Result is already got, remove the generated code */
RemoveCode (&Mark1); RemoveCode (&Mark1);
/* Done */ /* Done */
goto Next; continue;
} }
/* If we're shifting an integer or unsigned to the right, the lhs /* 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. ** shift count is zero, we're done.
*/ */
if (Tok == TOK_SHR && if (Tok == TOK_SHR &&
IsTypeInt (Expr->Type) && IsClassInt (Expr->Type) &&
SizeOf (Expr->Type) == SIZEOF_INT &&
ED_IsLVal (Expr) && ED_IsLVal (Expr) &&
ED_IsLocQuasiConst (Expr) && ED_IsLocQuasiConst (Expr) &&
Expr2.IVal >= 8) { Expr2.IVal >= 8) {
const Type* OldType;
/* Increase the address by one and decrease the shift count */ /* Increase the address by one and decrease the shift count */
++Expr->IVal; ++Expr->IVal;
Expr2.IVal -= 8; Expr2.IVal -= 8;
@ -202,7 +203,6 @@ void ShiftExpr (struct ExprDesc* Expr)
/* Replace the type of the expression temporarily by the /* Replace the type of the expression temporarily by the
** corresponding char type. ** corresponding char type.
*/ */
OldType = Expr->Type;
if (IsSignUnsigned (Expr->Type)) { if (IsSignUnsigned (Expr->Type)) {
Expr->Type = type_uchar; Expr->Type = type_uchar;
} else { } else {
@ -215,9 +215,6 @@ void ShiftExpr (struct ExprDesc* Expr)
/* Generate again code for the load, this time with the new type */ /* Generate again code for the load, this time with the new type */
LoadExpr (CF_NONE, Expr); LoadExpr (CF_NONE, Expr);
/* Reset the type */
Expr->Type = OldType;
/* If the shift count is now zero, we're done */ /* If the shift count is now zero, we're done */
if (Expr2.IVal == 0) { if (Expr2.IVal == 0) {
/* Be sure to mark the value as in the primary */ /* Be sure to mark the value as in the primary */
@ -238,7 +235,6 @@ MakeRVal:
/* We have an rvalue in the primary now */ /* We have an rvalue in the primary now */
ED_FinalizeRValLoad (Expr); ED_FinalizeRValLoad (Expr);
Next:
/* Set the type of the result */ /* Set the type of the result */
Expr->Type = ResultType; Expr->Type = ResultType;
} }

60
test/val/bug1675-ub.c Normal file
View File

@ -0,0 +1,60 @@
/* #1675 - Some UB cases of bit-shifts */
#include <stdio.h>
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;
}

101
test/val/bug1675.c Normal file
View File

@ -0,0 +1,101 @@
/* #1675 - Some corner cases of bit-shifts */
#include <stdio.h>
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;
}