forked from Imagelibrary/binutils-gdb
gas: special-case division / modulo by ±1
Dividing the largest possible negative value by -1 generally is UB, for the result not being representable at least in commonly used binary notation. This UB on x86, for example, is a Floating Point Exception on Linux, i.e. resulting in an internal error (albeit only when sizeof(valueT) == sizeof(void *); the library routine otherwise involved apparently deals with the inputs quite okay). Leave original values unaltered for division by 1; this may matter down the road, in case we start including X_unsigned and X_extrabit in arithmetic. For the same reason treat modulo by 1 the same as modulo by -1. The quad and octa tests have more relaxed expecations than intended, for X_unsigned and X_extrabit not being taken into account [yet]. The upper halves can wrongly end up as all ones (for .octa, when !BFD64, even the upper three quarters). Yet it makes little sense to address this just for div/mod by ±1. quad-div2 is yet more special, to cover for most 32-bit targets being unable to deal with forward-ref expressions in .quad even when BFD64; even ones being able to (like x86) then still don't get the values right.
This commit is contained in:
42
gas/expr.c
42
gas/expr.c
@@ -2015,8 +2015,32 @@ expr (int rankarg, /* Larger # is higher rank. */
|
||||
bits of the result. */
|
||||
resultP->X_add_number *= (valueT) v;
|
||||
break;
|
||||
case O_divide: resultP->X_add_number /= v; break;
|
||||
case O_modulus: resultP->X_add_number %= v; break;
|
||||
|
||||
case O_divide:
|
||||
if (v == 1)
|
||||
break;
|
||||
if (v == -1)
|
||||
{
|
||||
/* Dividing the largest negative value representable in offsetT
|
||||
by -1 has a non-representable result in common binary
|
||||
notation. Treat it as negation instead, carried out as an
|
||||
unsigned operation to avoid UB. */
|
||||
resultP->X_add_number = - (valueT) resultP->X_add_number;
|
||||
}
|
||||
else
|
||||
resultP->X_add_number /= v;
|
||||
break;
|
||||
|
||||
case O_modulus:
|
||||
/* See above for why in particular -1 needs special casing.
|
||||
While the operation is UB in C, mathematically it has a well-
|
||||
defined result. */
|
||||
if (v == 1 || v == -1)
|
||||
resultP->X_add_number = 0;
|
||||
else
|
||||
resultP->X_add_number %= v;
|
||||
break;
|
||||
|
||||
case O_left_shift:
|
||||
case O_right_shift:
|
||||
/* We always use unsigned shifts. According to the ISO
|
||||
@@ -2372,12 +2396,22 @@ resolve_expression (expressionS *expressionP)
|
||||
case O_divide:
|
||||
if (right == 0)
|
||||
return 0;
|
||||
left = (offsetT) left / (offsetT) right;
|
||||
/* See expr() for reasons of the special casing. */
|
||||
if (right == 1)
|
||||
break;
|
||||
if ((offsetT) right == -1)
|
||||
left = -left;
|
||||
else
|
||||
left = (offsetT) left / (offsetT) right;
|
||||
break;
|
||||
case O_modulus:
|
||||
if (right == 0)
|
||||
return 0;
|
||||
left = (offsetT) left % (offsetT) right;
|
||||
/* Again, see expr() for reasons of the special casing. */
|
||||
if (right == 1 || (offsetT) right == -1)
|
||||
left = 0;
|
||||
else
|
||||
left = (offsetT) left % (offsetT) right;
|
||||
break;
|
||||
case O_left_shift:
|
||||
if (right >= sizeof (left) * CHAR_BIT)
|
||||
|
||||
Reference in New Issue
Block a user