aboutsummaryrefslogtreecommitdiff
path: root/gcc/cexp.y
diff options
context:
space:
mode:
authorRichard Stallman <rms@gnu.org>1992-09-13 17:36:33 +0000
committerRichard Stallman <rms@gnu.org>1992-09-13 17:36:33 +0000
commit4cf8de9f3eb5c333a2d73fa813e2cf0769b12b02 (patch)
tree1c312f669ad21b99b7383b829460e4c96311d1cd /gcc/cexp.y
parent0bbb7f4d247de49a2a322c384acddd11337b2e71 (diff)
downloadgcc-4cf8de9f3eb5c333a2d73fa813e2cf0769b12b02.zip
gcc-4cf8de9f3eb5c333a2d73fa813e2cf0769b12b02.tar.gz
gcc-4cf8de9f3eb5c333a2d73fa813e2cf0769b12b02.tar.bz2
(exp): Detect signed integer overflow.
Replace `(unsigned)' with `(unsigned long)'. When evaluating << and >>, compute the correct answer without shifting by more than HOST_BITS_PER_LONG, or by a negative quantity. (integer_overflow, left_shift, right_shift): New functions. From-SVN: r2118
Diffstat (limited to 'gcc/cexp.y')
-rw-r--r--gcc/cexp.y107
1 files changed, 88 insertions, 19 deletions
diff --git a/gcc/cexp.y b/gcc/cexp.y
index 293d990..5041f4b 100644
--- a/gcc/cexp.y
+++ b/gcc/cexp.y
@@ -98,6 +98,14 @@ extern int traditional;
#ifndef WCHAR_TYPE_SIZE
#define WCHAR_TYPE_SIZE INT_TYPE_SIZE
#endif
+
+/* Yield nonzero if adding two numbers with A's and B's signs can yield a
+ number with SUM's sign, where A, B, and SUM are all C integers. */
+#define possible_sum_sign(a, b, sum) ((((a) ^ (b)) | ~ ((a) ^ (sum))) < 0)
+
+static void integer_overflow ();
+static long left_shift ();
+static long right_shift ();
%}
%union {
@@ -147,6 +155,8 @@ exp1 : exp
/* Expressions, not including the comma operator. */
exp : '-' exp %prec UNARY
{ $$.value = - $2.value;
+ if (($$.value & $2.value) < 0 && ! $2.unsignedp)
+ integer_overflow ();
$$.unsignedp = $2.unsignedp; }
| '!' exp %prec UNARY
{ $$.value = ! $2.value;
@@ -175,9 +185,15 @@ exp : '-' exp %prec UNARY
exp : exp '*' exp
{ $$.unsignedp = $1.unsignedp || $3.unsignedp;
if ($$.unsignedp)
- $$.value = (unsigned) $1.value * $3.value;
+ $$.value = (unsigned long) $1.value * $3.value;
else
- $$.value = $1.value * $3.value; }
+ {
+ $$.value = $1.value * $3.value;
+ if ($1.value
+ && ($$.value / $1.value != $3.value
+ || ($$.value & $1.value & $3.value) < 0))
+ integer_overflow ();
+ } }
| exp '/' exp
{ if ($3.value == 0)
{
@@ -186,9 +202,13 @@ exp : exp '*' exp
}
$$.unsignedp = $1.unsignedp || $3.unsignedp;
if ($$.unsignedp)
- $$.value = (unsigned) $1.value / $3.value;
+ $$.value = (unsigned long) $1.value / $3.value;
else
- $$.value = $1.value / $3.value; }
+ {
+ $$.value = $1.value / $3.value;
+ if (($$.value & $1.value & $3.value) < 0)
+ integer_overflow ();
+ } }
| exp '%' exp
{ if ($3.value == 0)
{
@@ -197,27 +217,35 @@ exp : exp '*' exp
}
$$.unsignedp = $1.unsignedp || $3.unsignedp;
if ($$.unsignedp)
- $$.value = (unsigned) $1.value % $3.value;
+ $$.value = (unsigned long) $1.value % $3.value;
else
$$.value = $1.value % $3.value; }
| exp '+' exp
{ $$.value = $1.value + $3.value;
- $$.unsignedp = $1.unsignedp || $3.unsignedp; }
+ $$.unsignedp = $1.unsignedp || $3.unsignedp;
+ if (! $$.unsignedp
+ && ! possible_sum_sign ($1.value, $3.value,
+ $$.value))
+ integer_overflow (); }
| exp '-' exp
{ $$.value = $1.value - $3.value;
- $$.unsignedp = $1.unsignedp || $3.unsignedp; }
+ $$.unsignedp = $1.unsignedp || $3.unsignedp;
+ if (! $$.unsignedp
+ && ! possible_sum_sign ($$.value, $3.value,
+ $1.value))
+ integer_overflow (); }
| exp LSH exp
{ $$.unsignedp = $1.unsignedp;
- if ($$.unsignedp)
- $$.value = (unsigned) $1.value << $3.value;
+ if ($3.value < 0 && ! $3.unsignedp)
+ $$.value = right_shift (&$1, -$3.value);
else
- $$.value = $1.value << $3.value; }
+ $$.value = left_shift (&$1, $3.value); }
| exp RSH exp
{ $$.unsignedp = $1.unsignedp;
- if ($$.unsignedp)
- $$.value = (unsigned) $1.value >> $3.value;
+ if ($3.value < 0 && ! $3.unsignedp)
+ $$.value = left_shift (&$1, -$3.value);
else
- $$.value = $1.value >> $3.value; }
+ $$.value = right_shift (&$1, $3.value); }
| exp EQUAL exp
{ $$.value = ($1.value == $3.value);
$$.unsignedp = 0; }
@@ -227,25 +255,25 @@ exp : exp '*' exp
| exp LEQ exp
{ $$.unsignedp = 0;
if ($1.unsignedp || $3.unsignedp)
- $$.value = (unsigned) $1.value <= $3.value;
+ $$.value = (unsigned long) $1.value <= $3.value;
else
$$.value = $1.value <= $3.value; }
| exp GEQ exp
{ $$.unsignedp = 0;
if ($1.unsignedp || $3.unsignedp)
- $$.value = (unsigned) $1.value >= $3.value;
+ $$.value = (unsigned long) $1.value >= $3.value;
else
$$.value = $1.value >= $3.value; }
| exp '<' exp
{ $$.unsignedp = 0;
if ($1.unsignedp || $3.unsignedp)
- $$.value = (unsigned) $1.value < $3.value;
+ $$.value = (unsigned long) $1.value < $3.value;
else
$$.value = $1.value < $3.value; }
| exp '>' exp
{ $$.unsignedp = 0;
if ($1.unsignedp || $3.unsignedp)
- $$.value = (unsigned) $1.value > $3.value;
+ $$.value = (unsigned long) $1.value > $3.value;
else
$$.value = $1.value > $3.value; }
| exp '&' exp
@@ -548,10 +576,10 @@ yylex ()
if (lookup ("__CHAR_UNSIGNED__", sizeof ("__CHAR_UNSIGNED__")-1, -1)
|| ((result >> (num_bits - 1)) & 1) == 0)
yylval.integer.value
- = result & ((unsigned) ~0 >> (HOST_BITS_PER_INT - num_bits));
+ = result & ((unsigned long) ~0 >> (HOST_BITS_PER_LONG - num_bits));
else
yylval.integer.value
- = result | ~((unsigned) ~0 >> (HOST_BITS_PER_INT - num_bits));
+ = result | ~((unsigned long) ~0 >> (HOST_BITS_PER_LONG - num_bits));
}
else
{
@@ -785,6 +813,47 @@ yyerror (s)
error (s);
longjmp (parse_return_error, 1);
}
+
+static void
+integer_overflow ()
+{
+ pedwarn ("integer overflow in preprocessor expression");
+}
+
+static long
+left_shift (a, b)
+ struct constant *a;
+ unsigned long b;
+{
+ if (b >= HOST_BITS_PER_LONG)
+ {
+ if (! a->unsignedp && a->value != 0)
+ integer_overflow ();
+ return 0;
+ }
+ else if (a->unsignedp)
+ return (unsigned long) a->value << b;
+ else
+ {
+ long l = a->value << b;
+ if (l >> b != a->value)
+ integer_overflow ();
+ return l;
+ }
+}
+
+static long
+right_shift (a, b)
+ struct constant *a;
+ unsigned long b;
+{
+ if (b >= HOST_BITS_PER_LONG)
+ return a->unsignedp ? 0 : a->value >> (HOST_BITS_PER_LONG - 1);
+ else if (a->unsignedp)
+ return (unsigned long) a->value >> b;
+ else
+ return a->value >> b;
+}
/* This page contains the entry point to this file. */