aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
-rw-r--r--bits.h61
-rw-r--r--evaluate.c5
-rw-r--r--expand.c52
-rw-r--r--lib.c4
-rw-r--r--lib.h3
-rw-r--r--linearize.c89
-rw-r--r--linearize.h5
-rw-r--r--show-parse.c2
-rw-r--r--simplify.c246
-rw-r--r--sparse.112
-rw-r--r--symbol.h8
-rw-r--r--unssa.c7
-rw-r--r--validation/check_access-store.c21
-rw-r--r--validation/eval-typeof-vla.c26
-rw-r--r--validation/fp-ops.c2
-rw-r--r--validation/linear/bitfield-inc.c17
-rw-r--r--validation/linear/bitfield-preinc.c18
-rw-r--r--validation/linear/bitfield-store.c22
-rw-r--r--validation/linear/cast-constant-to-float.c6
-rw-r--r--validation/linear/cast-constants.c20
-rw-r--r--validation/optim/and-extend.c27
-rw-r--r--validation/optim/and-extendx.c24
-rw-r--r--validation/optim/and-lsr.c16
-rw-r--r--validation/optim/and-trunc.c20
-rw-r--r--validation/optim/bool-context-fp.c16
-rw-r--r--validation/optim/ext-trunc-greater.c17
-rw-r--r--validation/optim/ext-trunc-same.c19
-rw-r--r--validation/optim/ext-trunc-smaller.c18
-rw-r--r--validation/optim/lsr-asr.c42
-rw-r--r--validation/optim/mask-lsr.c15
-rw-r--r--validation/optim/mask-out.c13
-rw-r--r--validation/optim/sext-sext.c12
-rw-r--r--validation/optim/sext.c15
-rw-r--r--validation/optim/shift-big.c82
-rw-r--r--validation/optim/shift-shift.c149
-rw-r--r--validation/optim/shift-zext.c13
-rw-r--r--validation/optim/store-load-bitfield.c50
-rw-r--r--validation/optim/trunc-mask-zext.c13
-rw-r--r--validation/optim/zext-and.c12
-rw-r--r--validation/optim/zext-and1.c12
-rw-r--r--validation/optim/zext-asr.c13
-rw-r--r--validation/optim/zext-sext.c13
-rw-r--r--validation/optim/zext-zext.c13
-rw-r--r--validation/shift-negative.c17
-rw-r--r--validation/shift-undef-long.c21
-rw-r--r--validation/shift-undef.c164
46 files changed, 1357 insertions, 95 deletions
diff --git a/bits.h b/bits.h
new file mode 100644
index 00000000..c0dc952e
--- /dev/null
+++ b/bits.h
@@ -0,0 +1,61 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Helper functions for manipulation & testing of integer values
+ * like zero or sign-extensions.
+ *
+ * Copyright (C) 2017 Luc Van Oostenryck
+ *
+ */
+
+#ifndef BITS_H
+#define BITS_H
+
+static inline unsigned long long sign_bit(unsigned size)
+{
+ return 1ULL << (size - 1);
+}
+
+static inline unsigned long long sign_mask(unsigned size)
+{
+ unsigned long long sbit = sign_bit(size);
+ return sbit - 1;
+}
+
+static inline unsigned long long bits_mask(unsigned size)
+{
+ unsigned long long sbit = sign_bit(size);
+ return sbit | (sbit - 1);
+}
+
+
+static inline long long zero_extend(long long val, unsigned size)
+{
+ return val & bits_mask(size);
+}
+
+static inline long long sign_extend(long long val, unsigned size)
+{
+ if (val & sign_bit(size))
+ val |= ~sign_mask(size);
+ return val;
+}
+
+///
+// sign extend @val but only if exactly representable
+static inline long long sign_extend_safe(long long val, unsigned size)
+{
+ unsigned long long mask = bits_mask(size);
+ if (!(val & ~mask))
+ val = sign_extend(val, size);
+ return val;
+}
+
+static inline long long bits_extend(long long val, unsigned size, int is_signed)
+{
+ val = zero_extend(val, size);
+ if (is_signed)
+ val = sign_extend(val, size);
+ return val;
+}
+
+#endif
diff --git a/evaluate.c b/evaluate.c
index 194b9721..7ab7db81 100644
--- a/evaluate.c
+++ b/evaluate.c
@@ -1325,6 +1325,11 @@ static int evaluate_assign_op(struct expression *expr)
goto Cast;
if (!restricted_value(expr->right, t))
return 1;
+ } else if (op == SPECIAL_SHR_ASSIGN || op == SPECIAL_SHL_ASSIGN) {
+ // shifts do integer promotions, but that's it.
+ unrestrict(expr->right, sclass, &s);
+ target = integer_promotion(s);
+ goto Cast;
} else if (!(sclass & TYPE_RESTRICT))
goto usual;
/* source and target would better be identical restricted */
diff --git a/expand.c b/expand.c
index cd05ce11..45e6f95e 100644
--- a/expand.c
+++ b/expand.c
@@ -158,11 +158,25 @@ Float:
expr->type = EXPR_FVALUE;
}
-static int check_shift_count(struct expression *expr, struct symbol *ctype, unsigned int count)
+static void check_shift_count(struct expression *expr, struct expression *right)
{
- warning(expr->pos, "shift too big (%u) for type %s", count, show_typename(ctype));
- count &= ctype->bit_size-1;
- return count;
+ struct symbol *ctype = expr->ctype;
+ long long count = get_longlong(right);
+
+ if (count < 0) {
+ if (!Wshift_count_negative)
+ return;
+ warning(expr->pos, "shift count is negative (%lld)", count);
+ return;
+ }
+ if (count < ctype->bit_size)
+ return;
+ if (ctype->type == SYM_NODE)
+ ctype = ctype->ctype.base_type;
+
+ if (!Wshift_count_overflow)
+ return;
+ warning(expr->pos, "shift too big (%llu) for type %s", count, show_typename(ctype));
}
/*
@@ -183,12 +197,9 @@ static int simplify_int_binop(struct expression *expr, struct symbol *ctype)
return 0;
r = right->value;
if (expr->op == SPECIAL_LEFTSHIFT || expr->op == SPECIAL_RIGHTSHIFT) {
- if (r >= ctype->bit_size) {
- if (conservative)
- return 0;
- r = check_shift_count(expr, ctype, r);
- right->value = r;
- }
+ if (conservative)
+ return 0;
+ check_shift_count(expr, right);
}
if (left->type != EXPR_VALUE)
return 0;
@@ -559,11 +570,30 @@ static int expand_conditional(struct expression *expr)
return cost + cond_cost + BRANCH_COST;
}
-
+
+static void check_assignment(struct expression *expr)
+{
+ struct expression *right;
+
+ switch (expr->op) {
+ case SPECIAL_SHL_ASSIGN:
+ case SPECIAL_SHR_ASSIGN:
+ right = expr->right;
+ if (right->type != EXPR_VALUE)
+ break;
+ check_shift_count(expr, right);
+ break;
+ }
+ return;
+}
+
static int expand_assignment(struct expression *expr)
{
expand_expression(expr->left);
expand_expression(expr->right);
+
+ if (!conservative)
+ check_assignment(expr);
return SIDE_EFFECTS;
}
diff --git a/lib.c b/lib.c
index 308f8f69..2d87b567 100644
--- a/lib.c
+++ b/lib.c
@@ -274,6 +274,8 @@ int Wpointer_to_int_cast = 1;
int Wptr_subtraction_blows = 0;
int Wreturn_void = 0;
int Wshadow = 0;
+int Wshift_count_negative = 1;
+int Wshift_count_overflow = 1;
int Wsizeof_bool = 0;
int Wtautological_compare = 0;
int Wtransparent_union = 0;
@@ -701,6 +703,8 @@ static const struct flag warnings[] = {
{ "ptr-subtraction-blows", &Wptr_subtraction_blows },
{ "return-void", &Wreturn_void },
{ "shadow", &Wshadow },
+ { "shift-count-negative", &Wshift_count_negative },
+ { "shift-count-overflow", &Wshift_count_overflow },
{ "sizeof-bool", &Wsizeof_bool },
{ "pointer-arith", &Wpointer_arith },
{ "sparse-error", &Wsparse_error },
diff --git a/lib.h b/lib.h
index b0453bb6..3cd8560a 100644
--- a/lib.h
+++ b/lib.h
@@ -34,6 +34,7 @@
#include "compat.h"
#include "ptrlist.h"
#include "utils.h"
+#include "bits.h"
#define DO_STRINGIFY(x) #x
#define STRINGIFY(x) DO_STRINGIFY(x)
@@ -163,6 +164,8 @@ extern int Wpointer_to_int_cast;
extern int Wptr_subtraction_blows;
extern int Wreturn_void;
extern int Wshadow;
+extern int Wshift_count_negative;
+extern int Wshift_count_overflow;
extern int Wsizeof_bool;
extern int Wtautological_compare;
extern int Wtransparent_union;
diff --git a/linearize.c b/linearize.c
index 194afe66..016b853b 100644
--- a/linearize.c
+++ b/linearize.c
@@ -368,7 +368,7 @@ const char *show_instruction(struct instruction *insn)
buf += sprintf(buf, "%lld", expr->value);
break;
case EXPR_FVALUE:
- buf += sprintf(buf, "%Lf", expr->fvalue);
+ buf += sprintf(buf, "%Le", expr->fvalue);
break;
case EXPR_STRING:
buf += sprintf(buf, "%.40s", show_string(expr->string));
@@ -386,7 +386,7 @@ const char *show_instruction(struct instruction *insn)
}
case OP_SETFVAL:
buf += sprintf(buf, "%s <- ", show_pseudo(insn->target));
- buf += sprintf(buf, "%Lf", insn->fvalue);
+ buf += sprintf(buf, "%Le", insn->fvalue);
break;
case OP_SWITCH: {
@@ -776,7 +776,6 @@ static void add_branch(struct entrypoint *ep, struct expression *expr, pseudo_t
}
}
-/* Dummy pseudo allocator */
pseudo_t alloc_pseudo(struct instruction *def)
{
static int nr = 0;
@@ -881,13 +880,8 @@ struct access_data {
struct symbol *type; // ctype
pseudo_t address; // pseudo containing address ..
unsigned int offset; // byte offset
- struct position pos;
};
-static void finish_address_gen(struct entrypoint *ep, struct access_data *ad)
-{
-}
-
static int linearize_simple_address(struct entrypoint *ep,
struct expression *addr,
struct access_data *ad)
@@ -930,7 +924,6 @@ static int linearize_address_gen(struct entrypoint *ep,
if (!ctype)
return 0;
- ad->pos = expr->pos;
ad->type = ctype;
if (expr->type == EXPR_PREOP && expr->op == '*')
return linearize_simple_address(ep, expr->unop, ad);
@@ -973,6 +966,24 @@ static void add_store(struct entrypoint *ep, struct access_data *ad, pseudo_t va
add_one_insn(ep, store);
}
+static pseudo_t linearize_bitfield_insert(struct entrypoint *ep,
+ pseudo_t ori, pseudo_t val, struct symbol *ctype, struct symbol *btype)
+{
+ unsigned int shift = ctype->bit_offset;
+ unsigned int size = ctype->bit_size;
+ unsigned long long mask = ((1ULL << size) - 1);
+
+ val = add_cast(ep, btype, ctype, OP_ZEXT, val);
+ if (shift) {
+ val = add_binary_op(ep, btype, OP_SHL, val, value_pseudo(shift));
+ mask <<= shift;
+ }
+ ori = add_binary_op(ep, btype, OP_AND, ori, value_pseudo(~mask));
+ val = add_binary_op(ep, btype, OP_OR, ori, val);
+
+ return val;
+}
+
static pseudo_t linearize_store_gen(struct entrypoint *ep,
pseudo_t value,
struct access_data *ad)
@@ -985,23 +996,30 @@ static pseudo_t linearize_store_gen(struct entrypoint *ep,
return VOID;
if (type_size(btype) != type_size(ctype)) {
- unsigned int shift = ctype->bit_offset;
- unsigned int size = ctype->bit_size;
pseudo_t orig = add_load(ep, ad);
- unsigned long long mask = (1ULL << size) - 1;
-
- store = add_cast(ep, btype, ctype, OP_ZEXT, store);
- if (shift) {
- store = add_binary_op(ep, btype, OP_SHL, store, value_pseudo(shift));
- mask <<= shift;
- }
- orig = add_binary_op(ep, btype, OP_AND, orig, value_pseudo(~mask));
- store = add_binary_op(ep, btype, OP_OR, orig, store);
+ store = linearize_bitfield_insert(ep, orig, value, ctype, btype);
}
add_store(ep, ad, store);
return value;
}
+static void taint_undefined_behaviour(struct instruction *insn)
+{
+ pseudo_t src2;
+
+ switch (insn->opcode) {
+ case OP_LSR:
+ case OP_ASR:
+ case OP_SHL:
+ src2 = insn->src2;
+ if (src2->type != PSEUDO_VAL)
+ break;
+ if ((unsigned long long)src2->value >= insn->size)
+ insn->tainted = 1;
+ break;
+ }
+}
+
static pseudo_t add_binary_op(struct entrypoint *ep, struct symbol *ctype, int op, pseudo_t left, pseudo_t right)
{
struct instruction *insn = alloc_typed_instruction(op, ctype);
@@ -1044,6 +1062,19 @@ static pseudo_t add_symbol_address(struct entrypoint *ep, struct symbol *sym)
return target;
}
+static pseudo_t linearize_bitfield_extract(struct entrypoint *ep,
+ pseudo_t val, struct symbol *ctype, struct symbol *btype)
+{
+ unsigned int off = ctype->bit_offset;
+
+ if (off) {
+ pseudo_t shift = value_pseudo(off);
+ val = add_binary_op(ep, btype, OP_LSR, val, shift);
+ }
+ val = cast_pseudo(ep, val, btype, ctype);
+ return val;
+}
+
static pseudo_t linearize_load_gen(struct entrypoint *ep, struct access_data *ad)
{
struct symbol *ctype = ad->type;
@@ -1054,13 +1085,8 @@ static pseudo_t linearize_load_gen(struct entrypoint *ep, struct access_data *ad
return VOID;
new = add_load(ep, ad);
- if (ctype->bit_offset) {
- pseudo_t shift = value_pseudo(ctype->bit_offset);
- pseudo_t newval = add_binary_op(ep, btype, OP_LSR, new, shift);
- new = newval;
- }
if (ctype->bit_size != type_size(btype))
- new = cast_pseudo(ep, new, btype, ctype);
+ new = linearize_bitfield_extract(ep, new, ctype, btype);
return new;
}
@@ -1072,7 +1098,6 @@ static pseudo_t linearize_access(struct entrypoint *ep, struct expression *expr)
if (!linearize_address_gen(ep, expr, &ad))
return VOID;
value = linearize_load_gen(ep, &ad);
- finish_address_gen(ep, &ad);
return value;
}
@@ -1093,7 +1118,6 @@ static pseudo_t linearize_inc_dec(struct entrypoint *ep, struct expression *expr
one = value_pseudo(expr->op_value);
new = add_binary_op(ep, expr->ctype, op, old, one);
linearize_store_gen(ep, new, &ad);
- finish_address_gen(ep, &ad);
return postop ? old : new;
}
@@ -1407,10 +1431,10 @@ static pseudo_t linearize_assignment(struct entrypoint *ep, struct expression *e
oldvalue = cast_pseudo(ep, oldvalue, target->ctype, ctype);
opcode = map_opcode(op_trans[expr->op - SPECIAL_BASE], ctype);
dst = add_binary_op(ep, ctype, opcode, oldvalue, value);
+ taint_undefined_behaviour(dst->def);
value = cast_pseudo(ep, dst, ctype, expr->ctype);
}
value = linearize_store_gen(ep, value, &ad);
- finish_address_gen(ep, &ad);
return value;
}
@@ -1512,6 +1536,7 @@ static pseudo_t linearize_binop(struct entrypoint *ep, struct expression *expr)
src2 = linearize_expression(ep, expr->right);
op = map_opcode(opcode[expr->op], expr->ctype);
dst = add_binary_op(ep, expr->ctype, op, src1, src2);
+ taint_undefined_behaviour(dst->def);
return dst;
}
@@ -1720,7 +1745,6 @@ static pseudo_t linearize_position(struct entrypoint *ep, struct expression *pos
struct expression *init_expr = pos->init_expr;
ad->offset = pos->init_offset;
- ad->type = init_expr->ctype;
return linearize_initializer(ep, init_expr, ad);
}
@@ -1755,7 +1779,6 @@ static void linearize_argument(struct entrypoint *ep, struct symbol *arg, int nr
ad.type = arg;
ad.address = symbol_pseudo(ep, arg);
linearize_store_gen(ep, argument_pseudo(ep, nr), &ad);
- finish_address_gen(ep, &ad);
}
static pseudo_t linearize_expression(struct entrypoint *ep, struct expression *expr)
@@ -1859,15 +1882,12 @@ static pseudo_t linearize_one_symbol(struct entrypoint *ep, struct symbol *sym)
// only the existing fields need to be initialized.
// FIXME: this init the whole aggregate even if
// all fields arelater explicitely initialized.
- struct expression *expr = sym->initializer;
- ad.pos = expr->pos;
ad.type = sym;
ad.address = symbol_pseudo(ep, sym);
linearize_store_gen(ep, value_pseudo(0), &ad);
}
value = linearize_initializer(ep, sym->initializer, &ad);
- finish_address_gen(ep, &ad);
return value;
}
@@ -1967,7 +1987,6 @@ static void add_asm_output(struct entrypoint *ep, struct instruction *insn, stru
if (!expr || !linearize_address_gen(ep, expr, &ad))
return;
linearize_store_gen(ep, pseudo, &ad);
- finish_address_gen(ep, &ad);
rule = __alloc_asm_constraint(0);
rule->ident = ident;
rule->constraint = constraint;
diff --git a/linearize.h b/linearize.h
index 092e1ac2..2bd6185a 100644
--- a/linearize.h
+++ b/linearize.h
@@ -338,6 +338,11 @@ static inline int has_users(pseudo_t p)
return pseudo_user_list_size(p->users) != 0;
}
+static inline int nbr_users(pseudo_t p)
+{
+ return pseudo_user_list_size(p->users);
+}
+
static inline struct pseudo_user *alloc_pseudo_user(struct instruction *insn, pseudo_t *pp)
{
struct pseudo_user *user = __alloc_pseudo_user(0);
diff --git a/show-parse.c b/show-parse.c
index 72d3f385..6328439c 100644
--- a/show-parse.c
+++ b/show-parse.c
@@ -987,7 +987,7 @@ static int show_fvalue(struct expression *expr)
int new = new_pseudo();
long double value = expr->fvalue;
- printf("\tmovf.%d\t\tv%d,$%Lf\n", expr->ctype->bit_size, new, value);
+ printf("\tmovf.%d\t\tv%d,$%Le\n", expr->ctype->bit_size, new, value);
return new;
}
diff --git a/simplify.c b/simplify.c
index 412bfb6a..b28d9375 100644
--- a/simplify.c
+++ b/simplify.c
@@ -378,6 +378,13 @@ static inline int def_opcode(pseudo_t p)
return p->def->opcode;
}
+//
+// return the opcode of the instruction defining ``SRC`` if existing
+// and OP_BADOP if not. It also assigns the defining instruction
+// to ``DEF``.
+#define DEF_OPCODE(DEF, SRC) \
+ (((SRC)->type == PSEUDO_REG && (DEF = (SRC)->def)) ? DEF->opcode : OP_BADOP)
+
static unsigned int value_size(long long value)
{
value >>= 8;
@@ -472,12 +479,18 @@ static pseudo_t eval_insn(struct instruction *insn)
res = left % right;
break;
case OP_SHL:
+ if (ur >= size)
+ goto undef;
res = left << right;
break;
case OP_LSR:
+ if (ur >= size)
+ goto undef;
res = ul >> ur;
break;
case OP_ASR:
+ if (ur >= size)
+ goto undef;
res = left >> right;
break;
/* Logical */
@@ -533,18 +546,117 @@ undef:
return NULL;
}
+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;
+
+ 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 sval;
+}
-static int simplify_asr(struct instruction *insn, pseudo_t pseudo, long long value)
+static int simplify_shift(struct instruction *insn, pseudo_t pseudo, long long value)
{
- unsigned int size = operand_size(insn, pseudo);
+ struct instruction *def;
+ unsigned long long nval;
+ unsigned int size;
+ pseudo_t src2;
- 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);
+ value = check_shift_count(insn, value);
+ if (value < 0)
+ return 0;
+
+ size = insn->size;
+ switch (insn->opcode) {
+ case OP_ASR:
+ if (value >= size)
+ return 0;
+ if (pseudo->type != PSEUDO_REG)
+ break;
+ def = pseudo->def;
+ switch (def->opcode) {
+ case OP_LSR:
+ case OP_ASR:
+ if (def == insn) // cyclic DAG!
+ break;
+ src2 = def->src2;
+ if (src2->type != PSEUDO_VAL)
+ break;
+ nval = src2->value;
+ if (nval > insn->size || nval == 0)
+ break;
+ value += nval;
+ if (def->opcode == OP_LSR)
+ insn->opcode = OP_LSR;
+ else if (value >= size)
+ value = size - 1;
+ goto new_value;
+
+ case OP_ZEXT:
+ // transform:
+ // zext.N %t <- (O) %a
+ // asr.N %r <- %t, C
+ // into
+ // zext.N %t <- (O) %a
+ // lsr.N %r <- %t, C
+ insn->opcode = OP_LSR;
+ return REPEAT_CSE;
+ }
+ break;
+ case OP_LSR:
+ size = operand_size(insn, pseudo);
+ /* fall through */
+ case OP_SHL:
+ if (value >= size)
+ goto zero;
+ if (pseudo->type != PSEUDO_REG)
+ break;
+ def = pseudo->def;
+ if (def->opcode == insn->opcode) {
+ if (def == insn) // cyclic DAG!
+ break;
+ src2 = def->src2;
+ if (src2->type != PSEUDO_VAL)
+ break;
+ nval = src2->value;
+ if (nval > insn->size)
+ break;
+ value += nval;
+ goto new_value;
+ }
+ break;
+ }
return 0;
+
+new_value:
+ if (value < size) {
+ insn->src2 = value_pseudo(value);
+ return replace_pseudo(insn, &insn->src1, pseudo->def->src1);
+ }
+zero:
+ return replace_with_pseudo(insn, value_pseudo(0));
}
static int simplify_mul_div(struct instruction *insn, long long value)
@@ -631,6 +743,32 @@ static int simplify_seteq_setne(struct instruction *insn, long long value)
return 0;
}
+static int simplify_constant_mask(struct instruction *insn, unsigned long long mask)
+{
+ pseudo_t old = insn->src1;
+ unsigned long long omask;
+ unsigned long long nmask;
+ struct instruction *def;
+ int osize;
+
+ switch (DEF_OPCODE(def, old)) {
+ case OP_ZEXT:
+ osize = def->orig_type->bit_size;
+ omask = (1ULL << osize) - 1;
+ nmask = mask & omask;
+ if (nmask == omask)
+ // the AND mask is redundant
+ return replace_with_pseudo(insn, old);
+ if (nmask != mask) {
+ // can use a smaller mask
+ insn->src2 = value_pseudo(nmask);
+ return REPEAT_CSE;
+ }
+ break;
+ }
+ return 0;
+}
+
static int simplify_constant_rightside(struct instruction *insn)
{
long long value = insn->src2->value;
@@ -658,14 +796,14 @@ static int simplify_constant_rightside(struct instruction *insn)
}
/* Fall through */
case OP_ADD:
- case OP_SHL:
- case OP_LSR:
case_neutral_zero:
if (!value)
return replace_with_pseudo(insn, insn->src1);
return 0;
case OP_ASR:
- return simplify_asr(insn, insn->src1, value);
+ case OP_SHL:
+ case OP_LSR:
+ return simplify_shift(insn, insn->src1, value);
case OP_MODU: case OP_MODS:
if (value == 1)
@@ -681,7 +819,7 @@ static int simplify_constant_rightside(struct instruction *insn)
return replace_with_pseudo(insn, insn->src2);
if ((value & bits) == bits)
return replace_with_pseudo(insn, insn->src1);
- return 0;
+ return simplify_constant_mask(insn, value);
case OP_SET_NE:
case OP_SET_EQ:
@@ -830,7 +968,7 @@ static int simplify_associative_binop(struct instruction *insn)
return 0;
if (!simple_pseudo(def->src2))
return 0;
- if (pseudo_user_list_size(def->target->users) != 1)
+ if (nbr_users(def->target) != 1)
return 0;
switch_pseudo(def, &def->src1, insn, &insn->src2);
return REPEAT_CSE;
@@ -959,8 +1097,10 @@ static int simplify_memop(struct instruction *insn)
static int simplify_cast(struct instruction *insn)
{
+ unsigned long long mask;
struct instruction *def;
pseudo_t src;
+ pseudo_t val;
int osize;
int size;
@@ -978,13 +1118,48 @@ static int simplify_cast(struct instruction *insn)
def = src->def;
switch (def_opcode(src)) {
case OP_AND:
+ val = def->src2;
+ if (val->type != PSEUDO_VAL)
+ break;
/* A cast of a AND might be a no-op.. */
- if (def->size >= size) {
- pseudo_t val = def->src2;
- if (val->type == PSEUDO_VAL) {
- if (!(val->value >> (size-1)))
- goto simplify;
- }
+ switch (insn->opcode) {
+ case OP_TRUNC:
+ if (nbr_users(src) > 1)
+ break;
+ def->opcode = OP_TRUNC;
+ def->orig_type = def->type;
+ def->type = insn->type;
+ def->size = size;
+
+ insn->opcode = OP_AND;
+ mask = val->value;
+ mask &= (1ULL << size) - 1;
+ insn->src2 = value_pseudo(mask);
+ return REPEAT_CSE;
+
+ case OP_SEXT:
+ if (val->value & (1 << (def->size - 1)))
+ break;
+ // OK, sign bit is 0
+ case OP_ZEXT:
+ if (nbr_users(src) > 1)
+ break;
+ // transform:
+ // and.n %b <- %a, M
+ // *ext.m %c <- (n) %b
+ // into:
+ // zext.m %b <- %a
+ // and.m %c <- %b, M
+ // For ZEXT, the mask will always be small
+ // enough. For SEXT, it can only be done if
+ // the mask force the sign bit to 0.
+ def->opcode = OP_ZEXT;
+ def->orig_type = insn->orig_type;
+ def->type = insn->type;
+ def->size = insn->size;
+ insn->opcode = OP_AND;
+ insn->src2 = val;
+ return REPEAT_CSE;
}
break;
case OP_TRUNC:
@@ -995,12 +1170,36 @@ static int simplify_cast(struct instruction *insn)
return replace_pseudo(insn, &insn->src1, def->src);
}
break;
+ case OP_ZEXT:
+ switch (insn->opcode) {
+ case OP_SEXT:
+ insn->opcode = OP_ZEXT;
+ /* fall through */
+ case OP_ZEXT:
+ insn->orig_type = def->orig_type;
+ return replace_pseudo(insn, &insn->src, def->src);
+ }
+ /* fall through */
+ case OP_SEXT:
+ switch (insn->opcode) {
+ case OP_TRUNC:
+ osize = def->orig_type->bit_size;
+ if (size == osize)
+ return replace_with_pseudo(insn, def->src);
+ if (size > osize)
+ insn->opcode = def->opcode;
+ insn->orig_type = def->orig_type;
+ return replace_pseudo(insn, &insn->src, def->src);
+ }
+ switch (insn->opcode) {
+ case OP_SEXT:
+ insn->orig_type = def->orig_type;
+ return replace_pseudo(insn, &insn->src, def->src);
+ }
+ break;
}
return 0;
-
-simplify:
- return replace_with_pseudo(insn, src);
}
static int simplify_select(struct instruction *insn)
@@ -1256,6 +1455,13 @@ int simplify_instruction(struct instruction *insn)
return simplify_switch(insn);
case OP_RANGE:
return simplify_range(insn);
+ case OP_FADD:
+ case OP_FSUB:
+ case OP_FMUL:
+ case OP_FDIV:
+ if (dead_insn(insn, &insn->src1, &insn->src2, NULL))
+ return REPEAT_CSE;
+ break;
}
return 0;
}
diff --git a/sparse.1 b/sparse.1
index 806fb0cf..8a14a6be 100644
--- a/sparse.1
+++ b/sparse.1
@@ -339,6 +339,18 @@ Such declarations can lead to error-prone code.
Sparse does not issue these warnings by default.
.
.TP
+.B \-Wshift-count-negative
+Warn if a shift count is negative.
+
+Sparse issues these warnings by default.
+.
+.TP
+.B \-Wshift-count-overflow
+Warn if a shift count is bigger than the operand's width.
+
+Sparse issues these warnings by default.
+.
+.TP
.B \-Wsizeof-bool
Warn when checking the sizeof a _Bool.
diff --git a/symbol.h b/symbol.h
index 6b891edd..1cad6d05 100644
--- a/symbol.h
+++ b/symbol.h
@@ -453,6 +453,14 @@ static inline int get_sym_type(struct symbol *type)
return type->type;
}
+static inline long long extend_value(long long val, struct symbol *ctype)
+{
+ int is_signed = !(ctype->ctype.modifiers & MOD_UNSIGNED);
+ unsigned size = ctype->bit_size;
+
+ return bits_extend(val, size, is_signed);
+}
+
static inline struct symbol *lookup_keyword(struct ident *ident, enum namespace ns)
{
if (!ident->keyword)
diff --git a/unssa.c b/unssa.c
index e7c9154d..334ffa47 100644
--- a/unssa.c
+++ b/unssa.c
@@ -34,11 +34,6 @@
#include <assert.h>
-static inline int nbr_pseudo_users(pseudo_t p)
-{
- return ptr_list_size((struct ptr_list *)p->users);
-}
-
static int simplify_phi_node(struct instruction *phi, pseudo_t tmp)
{
pseudo_t target = phi->target;
@@ -95,7 +90,7 @@ static void replace_phi_node(struct instruction *phi)
src = def->phi_src;
if (src->type != PSEUDO_REG)
continue;
- switch (nbr_pseudo_users(src)) {
+ switch (nbr_users(src)) {
struct instruction *insn;
case 1:
insn = src->def;
diff --git a/validation/check_access-store.c b/validation/check_access-store.c
new file mode 100644
index 00000000..489507fd
--- /dev/null
+++ b/validation/check_access-store.c
@@ -0,0 +1,21 @@
+extern int a[1];
+
+static int r(void)
+{
+ return a[1];
+}
+
+static void w(void)
+{
+ a[1] = 2;
+}
+
+/*
+ * check-name: check_access-store
+ * check-known-to-fail
+ *
+ * check-error-start
+check_access-store.c:5:17: warning: invalid access past the end of 'a' (4 4)
+check_access-store.c:10:17: warning: invalid access past the end of 'a' (4 4)
+ * check-error-end
+ */
diff --git a/validation/eval-typeof-vla.c b/validation/eval-typeof-vla.c
new file mode 100644
index 00000000..56a9ec20
--- /dev/null
+++ b/validation/eval-typeof-vla.c
@@ -0,0 +1,26 @@
+extern int a[1];
+
+static int foo(int n)
+{
+ int i = 0;
+ int (*p)[1] = (typeof(++i, (int (*)[n])a)) &a;
+
+ (void) p;
+
+ return i;
+}
+
+/*
+ * check-name: eval-typeof-vla
+ * check-command: test-linearize -Wno-vla $file
+ * check-known-to-fail
+ *
+ * check-output-start
+foo:
+.L0:
+ <entry-point>
+ ret.32 $1
+
+
+ * check-output-end
+ */
diff --git a/validation/fp-ops.c b/validation/fp-ops.c
index 81f73384..96c246f8 100644
--- a/validation/fp-ops.c
+++ b/validation/fp-ops.c
@@ -48,7 +48,7 @@ fneg:
ftst:
.L10:
<entry-point>
- setfval.64 %r21 <- 0.000000
+ setfval.64 %r21 <- 0.000000e+00
fcmpoeq.1 %r23 <- %arg1, %r21
ret.1 %r23
diff --git a/validation/linear/bitfield-inc.c b/validation/linear/bitfield-inc.c
new file mode 100644
index 00000000..ed8efe7d
--- /dev/null
+++ b/validation/linear/bitfield-inc.c
@@ -0,0 +1,17 @@
+struct s {
+ int f:5;
+};
+
+void inc(struct s *p)
+{
+ p->f++;
+}
+
+/*
+ * check-name: bitfield-inc
+ * check-command: test-linearize -Wno-decl $file
+ * check-known-to-fail
+ *
+ * check-output-ignore
+ * check-output-excludes: add\\.5
+ */
diff --git a/validation/linear/bitfield-preinc.c b/validation/linear/bitfield-preinc.c
new file mode 100644
index 00000000..783327ae
--- /dev/null
+++ b/validation/linear/bitfield-preinc.c
@@ -0,0 +1,18 @@
+struct s {
+ int f:3;
+};
+
+int preinc(void)
+{
+ struct s s = { 7 };
+ return ++s.f;
+}
+
+/*
+ * check-name: bitfield-preinc
+ * check-description: ++X is equivalent to X+=1
+ * check-command: test-linearize -Wno-decl $file
+ *
+ * check-output-ignore
+ * check-output-contains: ret.32 *\\$0
+ */
diff --git a/validation/linear/bitfield-store.c b/validation/linear/bitfield-store.c
new file mode 100644
index 00000000..3d952c8d
--- /dev/null
+++ b/validation/linear/bitfield-store.c
@@ -0,0 +1,22 @@
+int foo(void)
+{
+ struct {
+ int a:8;
+ int b:16;
+ int c:8;
+ } s = { 0xff, 0x0000, 0xff };
+
+ return s.b = 0x56781234;
+}
+
+/*
+ * check-name: bitfield-store
+ * check-command: test-linearize -Wno-decl $file
+ *
+ * check-output-ignore
+ * check-output-contains: ret\\..*\\$0x1234
+ *
+ * check-error-start
+linear/bitfield-store.c:9:22: warning: cast truncates bits from constant value (56781234 becomes 1234)
+ * check-error-end
+ */
diff --git a/validation/linear/cast-constant-to-float.c b/validation/linear/cast-constant-to-float.c
index ef7892f1..ac4eff0f 100644
--- a/validation/linear/cast-constant-to-float.c
+++ b/validation/linear/cast-constant-to-float.c
@@ -13,21 +13,21 @@ double f3(void) { return -1.0; }
f1:
.L0:
<entry-point>
- setfval.64 %r1 <- -1.000000
+ setfval.64 %r1 <- -1.000000e+00
ret.64 %r1
f2:
.L2:
<entry-point>
- setfval.64 %r3 <- -1.000000
+ setfval.64 %r3 <- -1.000000e+00
ret.64 %r3
f3:
.L4:
<entry-point>
- setfval.64 %r5 <- -1.000000
+ setfval.64 %r5 <- -1.000000e+00
ret.64 %r5
diff --git a/validation/linear/cast-constants.c b/validation/linear/cast-constants.c
index 9e200672..c1fdab41 100644
--- a/validation/linear/cast-constants.c
+++ b/validation/linear/cast-constants.c
@@ -286,70 +286,70 @@ vptr_2_iptr:
int_2_float:
.L76:
<entry-point>
- setfval.32 %r39 <- 123.000000
+ setfval.32 %r39 <- 1.230000e+02
ret.32 %r39
uint_2_float:
.L78:
<entry-point>
- setfval.32 %r41 <- 123.000000
+ setfval.32 %r41 <- 1.230000e+02
ret.32 %r41
long_2_float:
.L80:
<entry-point>
- setfval.32 %r43 <- 123.000000
+ setfval.32 %r43 <- 1.230000e+02
ret.32 %r43
ulong_2_float:
.L82:
<entry-point>
- setfval.32 %r45 <- 123.000000
+ setfval.32 %r45 <- 1.230000e+02
ret.32 %r45
double_2_float:
.L84:
<entry-point>
- setfval.32 %r47 <- 1.123000
+ setfval.32 %r47 <- 1.123000e+00
ret.32 %r47
int_2_double:
.L86:
<entry-point>
- setfval.64 %r49 <- 123.000000
+ setfval.64 %r49 <- 1.230000e+02
ret.64 %r49
uint_2_double:
.L88:
<entry-point>
- setfval.64 %r51 <- 123.000000
+ setfval.64 %r51 <- 1.230000e+02
ret.64 %r51
long_2_double:
.L90:
<entry-point>
- setfval.64 %r53 <- 123.000000
+ setfval.64 %r53 <- 1.230000e+02
ret.64 %r53
ulong_2_double:
.L92:
<entry-point>
- setfval.64 %r55 <- 123.000000
+ setfval.64 %r55 <- 1.230000e+02
ret.64 %r55
float_2_double:
.L94:
<entry-point>
- setfval.64 %r57 <- 1.123000
+ setfval.64 %r57 <- 1.123000e+00
ret.64 %r57
diff --git a/validation/optim/and-extend.c b/validation/optim/and-extend.c
new file mode 100644
index 00000000..eb589236
--- /dev/null
+++ b/validation/optim/and-extend.c
@@ -0,0 +1,27 @@
+typedef unsigned short u16;
+typedef short s16;
+typedef unsigned int u32;
+typedef int s32;
+
+u32 ufoo(u32 x)
+{
+ u16 i = ((u16)x) & 0x7fffU;
+ return i;
+}
+
+u32 sfoo(u32 x)
+{
+ s16 i = ((s16)x) & 0x7fff;
+ return i;
+}
+
+/*
+ * check-name: and-extend
+ * check-command: test-linearize -Wno-decl $file
+ *
+ * check-output-ignore
+ * check-output-excludes: trunc\\.
+ * check-output-excludes: zext\\.
+ * check-output-excludes: sext\\.
+ * check-output-contains: and\\.32.*0x7fff
+ */
diff --git a/validation/optim/and-extendx.c b/validation/optim/and-extendx.c
new file mode 100644
index 00000000..5c181c93
--- /dev/null
+++ b/validation/optim/and-extendx.c
@@ -0,0 +1,24 @@
+typedef unsigned short u16;
+typedef short s16;
+typedef unsigned int u32;
+typedef int s32;
+typedef unsigned long long u64;
+typedef long long s64;
+
+u64 ufoo(int x)
+{
+ return x & 0x7fff;
+}
+
+u64 sfoo(int x)
+{
+ return x & 0x7fff;
+}
+
+/*
+ * check-name: and-extend
+ * check-command: test-linearize -Wno-decl $file
+ *
+ * check-output-ignore
+ * check-output-contains: and\\.64.*0x7fff
+ */
diff --git a/validation/optim/and-lsr.c b/validation/optim/and-lsr.c
new file mode 100644
index 00000000..df6b72f3
--- /dev/null
+++ b/validation/optim/and-lsr.c
@@ -0,0 +1,16 @@
+// (x & M) >> S to (x >> S) & (M >> S)
+
+unsigned int foo(unsigned int x)
+{
+ return (x & 0xffff) >> 12;
+}
+
+/*
+ * check-name: and-lsr
+ * check-command: test-linearize -Wno-decl $file
+ * check-known-to-fail
+ *
+ * check-output-ignore
+ * check-output-contains: and\\..*\\$15
+ * check-output-excludes: and\\..*\\$0xffff
+ */
diff --git a/validation/optim/and-trunc.c b/validation/optim/and-trunc.c
new file mode 100644
index 00000000..df1e4d03
--- /dev/null
+++ b/validation/optim/and-trunc.c
@@ -0,0 +1,20 @@
+short smask(short x)
+{
+ return x & (short) 0x7fff;
+}
+
+short umask(unsigned short x)
+{
+ return x & (unsigned short) 0x7fff;
+}
+
+/*
+ * check-name: and-trunc
+ * check-command: test-linearize -Wno-decl $file
+ *
+ * check-output-ignore
+ * check-output-excludes: sext\\.
+ * check-output-excludes: zext\\.
+ * check-output-excludes: trunc\\.
+ * check-output-contains: and\\.16
+ */
diff --git a/validation/optim/bool-context-fp.c b/validation/optim/bool-context-fp.c
index 6325ee36..c41370ba 100644
--- a/validation/optim/bool-context-fp.c
+++ b/validation/optim/bool-context-fp.c
@@ -18,7 +18,7 @@ int ifand(float a, float b) { return a && b; }
bfimp:
.L0:
<entry-point>
- setfval.32 %r2 <- 0.000000
+ setfval.32 %r2 <- 0.000000e+00
fcmpune.1 %r3 <- %arg1, %r2
ret.1 %r3
@@ -26,7 +26,7 @@ bfimp:
bfexp:
.L2:
<entry-point>
- setfval.32 %r6 <- 0.000000
+ setfval.32 %r6 <- 0.000000e+00
fcmpune.1 %r7 <- %arg1, %r6
ret.1 %r7
@@ -34,7 +34,7 @@ bfexp:
bfnot:
.L4:
<entry-point>
- setfval.32 %r10 <- 0.000000
+ setfval.32 %r10 <- 0.000000e+00
fcmpoeq.1 %r12 <- %arg1, %r10
ret.1 %r12
@@ -42,7 +42,7 @@ bfnot:
ifnot:
.L6:
<entry-point>
- setfval.32 %r15 <- 0.000000
+ setfval.32 %r15 <- 0.000000e+00
fcmpoeq.32 %r16 <- %arg1, %r15
ret.32 %r16
@@ -50,7 +50,7 @@ ifnot:
bfior:
.L8:
<entry-point>
- setfval.32 %r19 <- 0.000000
+ setfval.32 %r19 <- 0.000000e+00
fcmpune.1 %r20 <- %arg1, %r19
fcmpune.1 %r23 <- %arg2, %r19
or.1 %r24 <- %r20, %r23
@@ -60,7 +60,7 @@ bfior:
ifior:
.L10:
<entry-point>
- setfval.32 %r29 <- 0.000000
+ setfval.32 %r29 <- 0.000000e+00
fcmpune.1 %r30 <- %arg1, %r29
fcmpune.1 %r33 <- %arg2, %r29
or.1 %r34 <- %r30, %r33
@@ -71,7 +71,7 @@ ifior:
bfand:
.L12:
<entry-point>
- setfval.32 %r38 <- 0.000000
+ setfval.32 %r38 <- 0.000000e+00
fcmpune.1 %r39 <- %arg1, %r38
fcmpune.1 %r42 <- %arg2, %r38
and.1 %r43 <- %r39, %r42
@@ -81,7 +81,7 @@ bfand:
ifand:
.L14:
<entry-point>
- setfval.32 %r48 <- 0.000000
+ setfval.32 %r48 <- 0.000000e+00
fcmpune.1 %r49 <- %arg1, %r48
fcmpune.1 %r52 <- %arg2, %r48
and.1 %r53 <- %r49, %r52
diff --git a/validation/optim/ext-trunc-greater.c b/validation/optim/ext-trunc-greater.c
new file mode 100644
index 00000000..b682fc5d
--- /dev/null
+++ b/validation/optim/ext-trunc-greater.c
@@ -0,0 +1,17 @@
+short sgt(char x)
+{
+ return (int) x;
+}
+
+short ugt(unsigned char x)
+{
+ return (int) x;
+}
+
+/*
+ * check-name: ext-trunc-greater
+ * check-command: test-linearize -Wno-decl $file
+ *
+ * check-output-ignore
+ * check-output-excludes: trunc\\.
+ */
diff --git a/validation/optim/ext-trunc-same.c b/validation/optim/ext-trunc-same.c
new file mode 100644
index 00000000..2bfcf030
--- /dev/null
+++ b/validation/optim/ext-trunc-same.c
@@ -0,0 +1,19 @@
+short seq(short x)
+{
+ return (int) x;
+}
+
+short ueq(unsigned short x)
+{
+ return (int) x;
+}
+
+/*
+ * check-name: ext-trunc-same
+ * check-command: test-linearize -Wno-decl $file
+ *
+ * check-output-ignore
+ * check-output-excludes: trunc\\.
+ * check-output-excludes: sext\\.
+ * check-output-excludes: zext\\.
+ */
diff --git a/validation/optim/ext-trunc-smaller.c b/validation/optim/ext-trunc-smaller.c
new file mode 100644
index 00000000..194c98d7
--- /dev/null
+++ b/validation/optim/ext-trunc-smaller.c
@@ -0,0 +1,18 @@
+char slt(short x)
+{
+ return (int) x;
+}
+
+char ult(unsigned short x)
+{
+ return (int) x;
+}
+
+/*
+ * check-name: ext-trunc-smaller
+ * check-command: test-linearize -Wno-decl $file
+ *
+ * check-output-ignore
+ * check-output-excludes: sext\\.
+ * check-output-excludes: zext\\.
+ */
diff --git a/validation/optim/lsr-asr.c b/validation/optim/lsr-asr.c
new file mode 100644
index 00000000..aee46940
--- /dev/null
+++ b/validation/optim/lsr-asr.c
@@ -0,0 +1,42 @@
+int lsrasr0(unsigned int x)
+{
+ return ((int) (x >> 15)) >> 15;
+}
+
+int lsrasr1(unsigned int x)
+{
+ return ((int) (x >> 16)) >> 15;
+}
+
+int lsrasr2(unsigned int x)
+{
+ return ((int) (x >> 16)) >> 16;
+}
+
+/*
+ * check-name: lsr-asr
+ * check-command: test-linearize -Wno-decl $file
+ *
+ * check-output-start
+lsrasr0:
+.L0:
+ <entry-point>
+ lsr.32 %r3 <- %arg1, $30
+ ret.32 %r3
+
+
+lsrasr1:
+.L2:
+ <entry-point>
+ lsr.32 %r7 <- %arg1, $31
+ ret.32 %r7
+
+
+lsrasr2:
+.L4:
+ <entry-point>
+ ret.32 $0
+
+
+ * check-output-end
+ */
diff --git a/validation/optim/mask-lsr.c b/validation/optim/mask-lsr.c
new file mode 100644
index 00000000..1d37c91e
--- /dev/null
+++ b/validation/optim/mask-lsr.c
@@ -0,0 +1,15 @@
+// ((x & M) | y) >> S to (y >> S) when (M >> S) == 0
+
+unsigned int foo(unsigned int x, unsigned int y)
+{
+ return ((x & 0xff) | y) >> 8;
+}
+
+/*
+ * check-name: mask-lsr
+ * check-command: test-linearize -Wno-decl $file
+ * check-known-to-fail
+ *
+ * check-output-ignore
+ * check-output-excludes: %arg1
+ */
diff --git a/validation/optim/mask-out.c b/validation/optim/mask-out.c
new file mode 100644
index 00000000..bf116873
--- /dev/null
+++ b/validation/optim/mask-out.c
@@ -0,0 +1,13 @@
+unsigned mask(unsigned a, unsigned b)
+{
+ return ((a & 0xffff0000) | b) & 0x0000ffff;
+}
+
+/*
+ * check-name: mask-out
+ * check-command: test-linearize -Wno-decl $file
+ * check-known-to-fail
+ *
+ * check-output-ignore
+ * check-output-excludes: %arg1
+ */
diff --git a/validation/optim/sext-sext.c b/validation/optim/sext-sext.c
new file mode 100644
index 00000000..604a7dd4
--- /dev/null
+++ b/validation/optim/sext-sext.c
@@ -0,0 +1,12 @@
+int foo(signed char offset)
+{
+ return (int)(short) offset;
+}
+
+/*
+ * check-name: sext-sext
+ * check-command: test-linearize -Wno-decl $file
+ *
+ * check-output-ignore
+ * check-output-pattern(1): sext\\.
+ */
diff --git a/validation/optim/sext.c b/validation/optim/sext.c
new file mode 100644
index 00000000..719730d5
--- /dev/null
+++ b/validation/optim/sext.c
@@ -0,0 +1,15 @@
+int sext(int x)
+{
+ return (x << 5) >> 5;
+}
+
+/*
+ * check-name: sext
+ * check-command: test-linearize -Wno-decl $file
+ * check-known-to-fail
+ *
+ * check-output-ignore
+ * check-output-contains: sext\\.$27
+ * check-output-excludes: asr\\.
+ * check-output-excludes: shl\\.
+ */
diff --git a/validation/optim/shift-big.c b/validation/optim/shift-big.c
new file mode 100644
index 00000000..84bcd2ce
--- /dev/null
+++ b/validation/optim/shift-big.c
@@ -0,0 +1,82 @@
+typedef unsigned int u32;
+typedef int s32;
+
+s32 asr31(s32 a) { return a >> 31; }
+s32 asr32(s32 a) { return a >> 32; }
+s32 asr33(s32 a) { return a >> 33; }
+
+u32 lsr31(u32 a) { return a >> 31; }
+u32 lsr32(u32 a) { return a >> 32; }
+u32 lsr33(u32 a) { return a >> 33; }
+
+u32 shl31(u32 a) { return a << 31; }
+u32 shl32(u32 a) { return a << 32; }
+u32 shl33(u32 a) { return a << 33; }
+
+/*
+ * check-name: optim/shift-big.c
+ * check-command: test-linearize -Wno-decl -m64 $file
+ *
+ * check-error-ignore
+ * check-output-start
+asr31:
+.L0:
+ <entry-point>
+ asr.32 %r2 <- %arg1, $31
+ ret.32 %r2
+
+
+asr32:
+.L2:
+ <entry-point>
+ asr.32 %r5 <- %arg1, $32
+ ret.32 %r5
+
+
+asr33:
+.L4:
+ <entry-point>
+ asr.32 %r8 <- %arg1, $33
+ ret.32 %r8
+
+
+lsr31:
+.L6:
+ <entry-point>
+ lsr.32 %r11 <- %arg1, $31
+ ret.32 %r11
+
+
+lsr32:
+.L8:
+ <entry-point>
+ ret.32 $0
+
+
+lsr33:
+.L10:
+ <entry-point>
+ ret.32 $0
+
+
+shl31:
+.L12:
+ <entry-point>
+ shl.32 %r20 <- %arg1, $31
+ ret.32 %r20
+
+
+shl32:
+.L14:
+ <entry-point>
+ ret.32 $0
+
+
+shl33:
+.L16:
+ <entry-point>
+ ret.32 $0
+
+
+ * check-output-end
+ */
diff --git a/validation/optim/shift-shift.c b/validation/optim/shift-shift.c
new file mode 100644
index 00000000..12a4b7d4
--- /dev/null
+++ b/validation/optim/shift-shift.c
@@ -0,0 +1,149 @@
+unsigned int shl0(unsigned int x)
+{
+ return x << 15 << 15;
+}
+
+unsigned int shl1(unsigned int x)
+{
+ return x << 16 << 15;
+}
+
+unsigned int shl2(unsigned int x)
+{
+ return x << 16 << 16;
+}
+
+unsigned int shl3(unsigned int x)
+{
+ return x << 12 << 10 << 10;
+}
+
+
+unsigned int lsr0(unsigned int x)
+{
+ return x >> 15 >> 15;
+}
+
+unsigned int lsr1(unsigned int x)
+{
+ return x >> 16 >> 15;
+}
+
+unsigned int lsr2(unsigned int x)
+{
+ return x >> 16 >> 16;
+}
+
+unsigned int lsr3(unsigned int x)
+{
+ return x >> 12 >> 10 >> 10;
+}
+
+
+int asr0(int x)
+{
+ return x >> 15 >> 15;
+}
+
+int asr1(int x)
+{
+ return x >> 16 >> 15;
+}
+
+int asr2(int x)
+{
+ return x >> 16 >> 16;
+}
+
+int asr3(int x)
+{
+ return x >> 12 >> 10 >> 10;
+}
+
+/*
+ * check-name: shift-shift
+ * check-command: test-linearize -Wno-decl $file
+ *
+ * check-output-start
+shl0:
+.L0:
+ <entry-point>
+ shl.32 %r3 <- %arg1, $30
+ ret.32 %r3
+
+
+shl1:
+.L2:
+ <entry-point>
+ shl.32 %r7 <- %arg1, $31
+ ret.32 %r7
+
+
+shl2:
+.L4:
+ <entry-point>
+ ret.32 $0
+
+
+shl3:
+.L6:
+ <entry-point>
+ ret.32 $0
+
+
+lsr0:
+.L8:
+ <entry-point>
+ lsr.32 %r20 <- %arg1, $30
+ ret.32 %r20
+
+
+lsr1:
+.L10:
+ <entry-point>
+ lsr.32 %r24 <- %arg1, $31
+ ret.32 %r24
+
+
+lsr2:
+.L12:
+ <entry-point>
+ ret.32 $0
+
+
+lsr3:
+.L14:
+ <entry-point>
+ ret.32 $0
+
+
+asr0:
+.L16:
+ <entry-point>
+ asr.32 %r37 <- %arg1, $30
+ ret.32 %r37
+
+
+asr1:
+.L18:
+ <entry-point>
+ asr.32 %r41 <- %arg1, $31
+ ret.32 %r41
+
+
+asr2:
+.L20:
+ <entry-point>
+ asr.32 %r45 <- %arg1, $31
+ ret.32 %r45
+
+
+asr3:
+.L22:
+ <entry-point>
+ asr.32 %r50 <- %arg1, $31
+ ret.32 %r50
+
+
+ * check-output-end
+ */
diff --git a/validation/optim/shift-zext.c b/validation/optim/shift-zext.c
new file mode 100644
index 00000000..070416f3
--- /dev/null
+++ b/validation/optim/shift-zext.c
@@ -0,0 +1,13 @@
+unsigned int foo(unsigned int x)
+{
+ return (x << 20) >> 20;
+}
+
+/*
+ * check-name: shift-zext
+ * check-command: test-linearize -Wno-decl $file
+ * check-known-to-fail
+ *
+ * check-output-ignore
+ * check-output-contains: and\\..*%arg1, \\$0xfff
+ */
diff --git a/validation/optim/store-load-bitfield.c b/validation/optim/store-load-bitfield.c
new file mode 100644
index 00000000..1d8ff240
--- /dev/null
+++ b/validation/optim/store-load-bitfield.c
@@ -0,0 +1,50 @@
+int ufoo(unsigned int a)
+{
+ struct u {
+ unsigned int :2;
+ unsigned int a:3;
+ } bf;
+
+ bf.a = a;
+ return bf.a;
+}
+
+int sfoo(int a)
+{
+ struct s {
+ signed int :2;
+ signed int a:3;
+ } bf;
+
+ bf.a = a;
+ return bf.a;
+}
+
+/*
+ * check-name: optim store/load bitfields
+ * check-command: test-linearize -Wno-decl $file
+ *
+ * check-output-start
+ufoo:
+.L0:
+ <entry-point>
+ and.32 %r4 <- %arg1, $7
+ shl.32 %r5 <- %r4, $2
+ lsr.32 %r9 <- %r5, $2
+ and.32 %r11 <- %r9, $7
+ ret.32 %r11
+
+
+sfoo:
+.L2:
+ <entry-point>
+ and.32 %r16 <- %arg1, $7
+ shl.32 %r17 <- %r16, $2
+ lsr.32 %r21 <- %r17, $2
+ trunc.3 %r22 <- (32) %r21
+ sext.32 %r23 <- (3) %r22
+ ret.32 %r23
+
+
+ * check-output-end
+ */
diff --git a/validation/optim/trunc-mask-zext.c b/validation/optim/trunc-mask-zext.c
new file mode 100644
index 00000000..9b604174
--- /dev/null
+++ b/validation/optim/trunc-mask-zext.c
@@ -0,0 +1,13 @@
+unsigned long long foo(unsigned long long x)
+{
+ return (((unsigned int) x) & 0x7ffU);
+}
+
+/*
+ * check-name: trunc-mask-zext
+ * check-command: test-linearize -Wno-decl $file
+ *
+ * check-output-ignore
+ * check-output-excludes: trunc\\.
+ * check-output-excludes: zext\\.
+ */
diff --git a/validation/optim/zext-and.c b/validation/optim/zext-and.c
new file mode 100644
index 00000000..a3153bf7
--- /dev/null
+++ b/validation/optim/zext-and.c
@@ -0,0 +1,12 @@
+unsigned int foo(unsigned char x)
+{
+ return (unsigned int)x & 0xffffU;
+}
+
+/*
+ * check-name: zext-and
+ * check-command: test-linearize -Wno-decl $file
+ *
+ * check-output-ignore
+ * check-output-excludes: and\\.
+ */
diff --git a/validation/optim/zext-and1.c b/validation/optim/zext-and1.c
new file mode 100644
index 00000000..c99a0e62
--- /dev/null
+++ b/validation/optim/zext-and1.c
@@ -0,0 +1,12 @@
+unsigned int bar(unsigned char x)
+{
+ return (unsigned int)x & 0xff01U;
+}
+
+/*
+ * check-name: zext-and1
+ * check-command: test-linearize -Wno-decl $file
+ *
+ * check-output-ignore
+ * check-output-contains: and\\..*\\$1
+ */
diff --git a/validation/optim/zext-asr.c b/validation/optim/zext-asr.c
new file mode 100644
index 00000000..5f235ad8
--- /dev/null
+++ b/validation/optim/zext-asr.c
@@ -0,0 +1,13 @@
+unsigned short foo(unsigned short a)
+{
+ return a >> 16;
+}
+
+/*
+ * check-name: zext-asr
+ * check-command: test-linearize -Wno-decl $file
+ *
+ * check-output-ignore
+ * check-output-contains: ret\\..*\\$0
+ * check-output-excludes: asr\\.
+ */
diff --git a/validation/optim/zext-sext.c b/validation/optim/zext-sext.c
new file mode 100644
index 00000000..1fe3900d
--- /dev/null
+++ b/validation/optim/zext-sext.c
@@ -0,0 +1,13 @@
+int foo(unsigned char offset)
+{
+ return (int)(short) offset;
+}
+
+/*
+ * check-name: zext-sext
+ * check-command: test-linearize -Wno-decl $file
+ *
+ * check-output-ignore
+ * check-output-excludes: sext\\.
+ * check-output-pattern(1): zext\\.
+ */
diff --git a/validation/optim/zext-zext.c b/validation/optim/zext-zext.c
new file mode 100644
index 00000000..986d2242
--- /dev/null
+++ b/validation/optim/zext-zext.c
@@ -0,0 +1,13 @@
+int foo(unsigned char offset)
+{
+ return (int)(unsigned short) offset;
+}
+
+/*
+ * check-name: zext-zext
+ * check-command: test-linearize -Wno-decl $file
+ *
+ * check-output-ignore
+ * check-output-excludes: sext\\.
+ * check-output-pattern(1): zext\\.
+ */
diff --git a/validation/shift-negative.c b/validation/shift-negative.c
new file mode 100644
index 00000000..fff5cf12
--- /dev/null
+++ b/validation/shift-negative.c
@@ -0,0 +1,17 @@
+unsigned int fn1(unsigned int a) { return a >> -1; }
+unsigned int fn2(unsigned int a) { return a >> ~0; }
+
+unsigned int fo1(unsigned int a) { return a >> ((a & 0) | -1); }
+unsigned int fo2(unsigned int a) { return a >> ((a & 0) ^ ~0); }
+
+/*
+ * check-name: shift-negative
+ * check-command: sparse -Wno-decl $file
+ *
+ * check-error-start
+shift-negative.c:1:45: warning: shift count is negative (-1)
+shift-negative.c:2:45: warning: shift count is negative (-1)
+shift-negative.c:4:59: warning: shift count is negative (-1)
+shift-negative.c:5:59: warning: shift count is negative (-1)
+ * check-error-end
+ */
diff --git a/validation/shift-undef-long.c b/validation/shift-undef-long.c
new file mode 100644
index 00000000..32626743
--- /dev/null
+++ b/validation/shift-undef-long.c
@@ -0,0 +1,21 @@
+static unsigned very_big_shift(unsigned int a)
+{
+ unsigned r = 0;
+ r |= a << (0ULL ^ ~0U);
+ r |= a << ((( signed long long) ~0U) + 1);
+ r |= a << (((unsigned long long) ~0U) + 1);
+ r |= a << (~((unsigned long long) ~0U));
+ return r;
+}
+
+/*
+ * check-name: shift-undef-long
+ * check-command: sparse -m64 $file
+ *
+ * check-error-start
+shift-undef-long.c:4:16: warning: shift too big (4294967295) for type unsigned int
+shift-undef-long.c:5:16: warning: shift too big (4294967296) for type unsigned int
+shift-undef-long.c:6:16: warning: shift too big (4294967296) for type unsigned int
+shift-undef-long.c:7:16: warning: shift count is negative (-4294967296)
+ * check-error-end
+ */
diff --git a/validation/shift-undef.c b/validation/shift-undef.c
new file mode 100644
index 00000000..4e94fa23
--- /dev/null
+++ b/validation/shift-undef.c
@@ -0,0 +1,164 @@
+int simple(int s, unsigned int u, int p)
+{
+ s = s >> 100;
+ u = u >> 101;
+ u = u << 102;
+ s = s >> -1;
+ u = u >> -2;
+ u = u << -3;
+ if (0) return s >> 103;
+ if (0) return u >> 104;
+ if (0) return u << 105;
+ if (0) return s >> -4;
+ if (0) return u >> -5;
+ if (0) return u << -6;
+ if (p && 0) return s >> 106;
+ if (p && 0) return u >> 107;
+ if (p && 0) return u << 108;
+ if (p && 0) return s >> -7;
+ if (p && 0) return u >> -8;
+ if (p && 0) return u << -9;
+ s = s >> ((p & 0) + 109); u ^= p; // reloaded because now == 0
+ u = u >> ((p & 0) + 110); u ^= p; // reloaded because now == 0
+ u = u << ((p & 0) + 111); u ^= p; // reloaded because now == 0
+ s = s >> ((p & 0) + -10);
+ u = u >> ((p & 0) + -11); u ^= p; // reloaded because now == 0
+ u = u << ((p & 0) + -12); u ^= p; // reloaded because now == 0
+ return s + u;
+}
+
+int compound(int s, unsigned int u, int p)
+{
+ s >>= 100;
+ u >>= 101;
+ u <<= 102;
+ s >>= -1;
+ u >>= -2;
+ u <<= -3;
+ if (0) return s >>= 103;
+ if (0) return u >>= 104;
+ if (0) return u <<= 105;
+ if (0) return s >>= -4;
+ if (0) return u >>= -5;
+ if (0) return u <<= -6;
+ if (p && 0) return s >>= 106;
+ if (p && 0) return u >>= 107;
+ if (p && 0) return u <<= 108;
+ if (p && 0) return s >>= -7;
+ if (p && 0) return u >>= -8;
+ if (p && 0) return u <<= -9;
+ s >>= ((p & 0) + 109); u ^= p; // reloaded because now == 0
+ u >>= ((p & 0) + 110); u ^= p; // reloaded because now == 0
+ u <<= ((p & 0) + 111); u ^= p; // reloaded because now == 0
+ s >>= ((p & 0) + -10);
+ u >>= ((p & 0) + -11); u ^= p; // reloaded because now == 0
+ u <<= ((p & 0) + -12); u ^= p; // reloaded because now == 0
+ return s + u;
+}
+
+int ok(int s, unsigned int u, int p)
+{
+ // GCC doesn't warn on these
+ if (0 && (s >> 100)) return 0;
+ if (0 && (u >> 101)) return 0;
+ if (0 && (u << 102)) return 0;
+ if (0 && (s >> -1)) return 0;
+ if (0 && (u >> -2)) return 0;
+ if (0 && (u << -3)) return 0;
+ if (0 && (s >>= 103)) return 0;
+ if (0 && (u >>= 104)) return 0;
+ if (0 && (u <<= 105)) return 0;
+ if (0 && (s >>= -4)) return 0;
+ if (0 && (u >>= -5)) return 0;
+ if (0 && (u <<= -6)) return 0;
+ return 1;
+}
+
+struct bf {
+ unsigned int u:8;
+ int s:8;
+};
+
+int bf(struct bf *p)
+{
+ unsigned int r = 0;
+ r += p->s << 8;
+ r += p->s >> 8;
+ r += p->u >> 8;
+ return r;
+}
+
+/*
+ * The following is used in the kernel at several places
+ * It shouldn't emit any warnings.
+ */
+typedef unsigned long long u64;
+typedef unsigned int u32;
+
+extern void hw_w32x2(u32 hi, u32 lo);
+
+inline void hw_w64(u64 val)
+{
+ hw_w32x2(val >> 32, (u32) val);
+}
+
+void hw_write(u32 val)
+{
+ hw_w64(val);
+}
+
+/*
+ * check-name: shift too big or negative
+ * check-command: sparse -Wno-decl $file
+ *
+ * check-error-start
+shift-undef.c:3:15: warning: shift too big (100) for type int
+shift-undef.c:4:15: warning: shift too big (101) for type unsigned int
+shift-undef.c:5:15: warning: shift too big (102) for type unsigned int
+shift-undef.c:6:15: warning: shift count is negative (-1)
+shift-undef.c:7:15: warning: shift count is negative (-2)
+shift-undef.c:8:15: warning: shift count is negative (-3)
+shift-undef.c:9:25: warning: shift too big (103) for type int
+shift-undef.c:10:25: warning: shift too big (104) for type unsigned int
+shift-undef.c:11:25: warning: shift too big (105) for type unsigned int
+shift-undef.c:12:25: warning: shift count is negative (-4)
+shift-undef.c:13:25: warning: shift count is negative (-5)
+shift-undef.c:14:25: warning: shift count is negative (-6)
+shift-undef.c:15:30: warning: shift too big (106) for type int
+shift-undef.c:16:30: warning: shift too big (107) for type unsigned int
+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 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
+shift-undef.c:35:11: warning: shift count is negative (-1)
+shift-undef.c:36:11: warning: shift count is negative (-2)
+shift-undef.c:37:11: warning: shift count is negative (-3)
+shift-undef.c:38:25: warning: shift too big (103) for type int
+shift-undef.c:39:25: warning: shift too big (104) for type unsigned int
+shift-undef.c:40:25: warning: shift too big (105) for type unsigned int
+shift-undef.c:41:25: warning: shift count is negative (-4)
+shift-undef.c:42:25: warning: shift count is negative (-5)
+shift-undef.c:43:25: warning: shift count is negative (-6)
+shift-undef.c:44:30: warning: shift too big (106) for type int
+shift-undef.c:45:30: warning: shift too big (107) for type unsigned int
+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 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
+ */