aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2022-08-23 11:38:27 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2022-08-23 11:38:27 -0700
commitdbaf1d1fe95efa2c2d9360a09a76e0e94b7bee85 (patch)
treea7eac41c8a1af91957f11ec591ed7de87e379ea3
parentd277dd67844973e8c81c25a7016b0cdc86d41766 (diff)
downloadsparse-dbaf1d1fe95efa2c2d9360a09a76e0e94b7bee85.tar.gz
simplify signed constants early
A constant like '-5' isn't actually a "constant" in the C parser, it ends up being an expression that is the negation of the constant 5. That's very inconvenient for the bitwise type evaluation that wants to treat a constant with all bits set as a special case (exactly the same way zero - "all bits clear" - is a special case). For the same reason, we also want to handle "~0" early. So make the constant simplification case for these cases available early, turning these trivial "preop of a simple constant" into simple constants at type evaluation time. Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--evaluate.c8
-rw-r--r--expand.c28
-rw-r--r--simplify.h1
3 files changed, 31 insertions, 6 deletions
diff --git a/evaluate.c b/evaluate.c
index fe716f63..5697128a 100644
--- a/evaluate.c
+++ b/evaluate.c
@@ -35,6 +35,7 @@
#include <limits.h>
#include "evaluate.h"
+#include "simplify.h"
#include "lib.h"
#include "allocate.h"
#include "parse.h"
@@ -1924,6 +1925,13 @@ Normal:
*expr = *expr->unop;
expr->flags = flags;
expr->ctype = ctype;
+
+ /*
+ * We simplify sign ops of constants early, turning
+ * a constant with a sign into just a plain constant
+ * rather than a preop.
+ */
+ early_simplify_preop(expr);
return ctype;
Restr:
if (restricted_unop(expr->op, &ctype))
diff --git a/expand.c b/expand.c
index f14e7181..c0b87a7f 100644
--- a/expand.c
+++ b/expand.c
@@ -765,7 +765,16 @@ static int expand_dereference(struct expression *expr)
return UNSAFE;
}
-static int simplify_preop(struct expression *expr)
+/*
+ * We want to do certain trivial constant simplifications
+ * early at the type evaluation stage, because those can
+ * depend on specific constants (ie ~0 is ok for restricted
+ * types).
+ *
+ * Return >0 for successful simplification, 0 for none,
+ * and <0 for "not simplified due to overflow".
+ */
+int early_simplify_preop(struct expression *expr)
{
struct expression *op = expr->unop;
unsigned long long v, mask;
@@ -779,7 +788,7 @@ static int simplify_preop(struct expression *expr)
case '+': break;
case '-':
if (v == mask && !(expr->ctype->ctype.modifiers & MOD_UNSIGNED))
- goto Overflow;
+ return -1;
v = -v;
break;
case '!': v = !v; break;
@@ -791,11 +800,18 @@ static int simplify_preop(struct expression *expr)
expr->type = EXPR_VALUE;
expr->taint = op->taint;
return 1;
+}
-Overflow:
- if (!conservative)
- warning(expr->pos, "constant integer operation overflow");
- return 0;
+static int simplify_preop(struct expression *expr)
+{
+ int simplified = early_simplify_preop(expr);
+
+ if (simplified < 0) {
+ if (!conservative)
+ warning(expr->pos, "constant integer operation overflow");
+ simplified = 0;
+ }
+ return simplified;
}
static int simplify_float_preop(struct expression *expr)
diff --git a/simplify.h b/simplify.h
index ed3dd971..15c88737 100644
--- a/simplify.h
+++ b/simplify.h
@@ -4,6 +4,7 @@
#include "linearize.h"
int simplify_instruction(struct instruction *insn);
+int early_simplify_preop(struct expression *expr);
int replace_with_pseudo(struct instruction *insn, pseudo_t pseudo);