diff options
author | 2008-04-24 14:47:04 -0700 | |
---|---|---|
committer | 2008-04-24 19:27:57 -0700 | |
commit | f43b0e9a874c86eeeb9ea6af89e39bae20d96be2 (patch) | |
tree | af2b0c15f92c2de6d2a51e396cfc2b01956c0b7a /simplify.c | |
parent | Fix cast instruction generation (diff) | |
download | sparse-f43b0e9a874c86eeeb9ea6af89e39bae20d96be2.tar.gz sparse-f43b0e9a874c86eeeb9ea6af89e39bae20d96be2.tar.bz2 sparse-f43b0e9a874c86eeeb9ea6af89e39bae20d96be2.zip |
Simplify (and warn about) right shifts that result in zero
..due to limited source sizes.
Yeah, should do this for left shifts too.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'simplify.c')
-rw-r--r-- | simplify.c | 57 |
1 files changed, 56 insertions, 1 deletions
@@ -256,6 +256,59 @@ static int replace_with_pseudo(struct instruction *insn, pseudo_t pseudo) return REPEAT_CSE; } +static unsigned int value_size(long long value) +{ + value >>= 8; + if (!value) + return 8; + value >>= 8; + if (!value) + return 16; + value >>= 16; + if (!value) + return 32; + return 64; +} + +/* + * Try to determine the maximum size of bits in a pseudo. + * + * Right now this only follow casts and constant values, but we + * could look at things like logical 'and' instructions etc. + */ +static unsigned int operand_size(struct instruction *insn, pseudo_t pseudo) +{ + unsigned int size = insn->size; + + if (pseudo->type == PSEUDO_REG) { + struct instruction *src = pseudo->def; + if (src && src->opcode == OP_CAST && src->orig_type) { + unsigned int orig_size = src->orig_type->bit_size; + if (orig_size < size) + size = orig_size; + } + } + if (pseudo->type == PSEUDO_VAL) { + unsigned int orig_size = value_size(pseudo->value); + if (orig_size < size) + size = orig_size; + } + return size; +} + +static int simplify_asr(struct instruction *insn, pseudo_t pseudo, long long value) +{ + unsigned int size = operand_size(insn, pseudo); + + if (value >= size) { + warning(insn->pos, "right shift by bigger than source value"); + return replace_with_pseudo(insn, value_pseudo(0)); + } + if (!value) + return replace_with_pseudo(insn, pseudo); + return 0; +} + static int simplify_constant_rightside(struct instruction *insn) { long long value = insn->src2->value; @@ -272,10 +325,12 @@ static int simplify_constant_rightside(struct instruction *insn) case OP_OR: case OP_XOR: case OP_OR_BOOL: case OP_SHL: - case OP_LSR: case OP_ASR: + case OP_LSR: if (!value) return replace_with_pseudo(insn, insn->src1); return 0; + case OP_ASR: + return simplify_asr(insn, insn->src1, value); case OP_MULU: case OP_MULS: case OP_AND_BOOL: |