diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2022-08-23 11:38:27 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2022-08-23 11:38:27 -0700 |
commit | dbaf1d1fe95efa2c2d9360a09a76e0e94b7bee85 (patch) | |
tree | a7eac41c8a1af91957f11ec591ed7de87e379ea3 | |
parent | d277dd67844973e8c81c25a7016b0cdc86d41766 (diff) | |
download | sparse-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.c | 8 | ||||
-rw-r--r-- | expand.c | 28 | ||||
-rw-r--r-- | simplify.h | 1 |
3 files changed, 31 insertions, 6 deletions
@@ -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)) @@ -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) @@ -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); |