In order to preserve shifts with negative count and give a proper warning for them, we first need to identify when the shift must be considered as negative. This identification is made complicated by the fact that PSEUDO_VAL are typeless. The solution chosen here is to try to first sign extend the shift count (with the size of an int and the size of the instruction). If after this extension the count appears as negative it is treated as such (and this new value replace the old one to avoid to have to redo this at each simplification run), otherwise it is considered as unsigned. The only case where a negative count could be wrongly interpreted as a big unsigned value would be if: size target int < instruction's size < size host value which can't happen for now since sizeof(int) == 4 on all targets and sizeof(value) is 8. Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@xxxxxxxxx> --- simplify.c | 23 ++++++++++++++++++----- validation/shift-negative.c | 1 - validation/shift-undef.c | 24 ++++++++++++------------ 3 files changed, 30 insertions(+), 18 deletions(-) diff --git a/simplify.c b/simplify.c index 5fb1a8cd3..1b258eb63 100644 --- a/simplify.c +++ b/simplify.c @@ -542,17 +542,30 @@ undef: static long long check_shift_count(struct instruction *insn, unsigned long long uval) { unsigned int size = insn->size; + long long sval = uval; if (uval < size) return uval; - if (insn->tainted) - return uval; - if (Wshift_count_overflow) { - warning(insn->pos, "shift by bigger than operand's width"); + sval = sign_extend_safe(sval, size); + sval = sign_extend_safe(sval, bits_in_int); + if (sval < 0) + insn->src2 = value_pseudo(sval); + if (insn->tainted) + return sval; + + if (sval < 0 && Wshift_count_negative) + warning(insn->pos, "shift count is negative (%lld)", sval); + if (sval > 0 && Wshift_count_overflow) { + struct symbol *ctype = insn->type; + const char *tname; + if (ctype->type == SYM_NODE) + ctype = ctype->ctype.base_type; + tname = show_typename(ctype); + warning(insn->pos, "shift too big (%llu) for type %s", sval, tname); } insn->tainted = 1; - return uval; + return sval; } static int simplify_shift(struct instruction *insn, pseudo_t pseudo, long long value) diff --git a/validation/shift-negative.c b/validation/shift-negative.c index 533fc68a4..fff5cf123 100644 --- a/validation/shift-negative.c +++ b/validation/shift-negative.c @@ -7,7 +7,6 @@ unsigned int fo2(unsigned int a) { return a >> ((a & 0) ^ ~0); } /* * check-name: shift-negative * check-command: sparse -Wno-decl $file - * check-known-to-fail * * check-error-start shift-negative.c:1:45: warning: shift count is negative (-1) diff --git a/validation/shift-undef.c b/validation/shift-undef.c index 54c8359b2..4e94fa23d 100644 --- a/validation/shift-undef.c +++ b/validation/shift-undef.c @@ -130,12 +130,12 @@ shift-undef.c:17:30: warning: shift too big (108) for type unsigned int shift-undef.c:18:30: warning: shift count is negative (-7) shift-undef.c:19:30: warning: shift count is negative (-8) shift-undef.c:20:30: warning: shift count is negative (-9) -shift-undef.c:21:29: warning: shift by bigger than operand's width -shift-undef.c:22:29: warning: shift by bigger than operand's width -shift-undef.c:23:29: warning: shift by bigger than operand's width -shift-undef.c:24:29: warning: shift by bigger than operand's width -shift-undef.c:25:29: warning: shift by bigger than operand's width -shift-undef.c:26:29: warning: shift by bigger than operand's width +shift-undef.c:21:29: warning: shift too big (109) for type int +shift-undef.c:22:29: warning: shift too big (110) for type unsigned int +shift-undef.c:23:29: warning: shift too big (111) for type unsigned int +shift-undef.c:24:29: warning: shift count is negative (-10) +shift-undef.c:25:29: warning: shift count is negative (-11) +shift-undef.c:26:29: warning: shift count is negative (-12) shift-undef.c:32:11: warning: shift too big (100) for type int shift-undef.c:33:11: warning: shift too big (101) for type unsigned int shift-undef.c:34:11: warning: shift too big (102) for type unsigned int @@ -154,11 +154,11 @@ shift-undef.c:46:30: warning: shift too big (108) for type unsigned int shift-undef.c:47:30: warning: shift count is negative (-7) shift-undef.c:48:30: warning: shift count is negative (-8) shift-undef.c:49:30: warning: shift count is negative (-9) -shift-undef.c:50:26: warning: shift by bigger than operand's width -shift-undef.c:51:26: warning: shift by bigger than operand's width -shift-undef.c:52:26: warning: shift by bigger than operand's width -shift-undef.c:53:26: warning: shift by bigger than operand's width -shift-undef.c:54:26: warning: shift by bigger than operand's width -shift-undef.c:55:26: warning: shift by bigger than operand's width +shift-undef.c:50:26: warning: shift too big (109) for type int +shift-undef.c:51:26: warning: shift too big (110) for type int +shift-undef.c:52:26: warning: shift too big (111) for type int +shift-undef.c:53:26: warning: shift count is negative (-10) +shift-undef.c:54:26: warning: shift count is negative (-11) +shift-undef.c:55:26: warning: shift count is negative (-12) * check-error-end */ -- 2.18.0 -- To unsubscribe from this list: send the line "unsubscribe linux-sparse" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html