diff options
46 files changed, 1357 insertions, 95 deletions
@@ -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 @@ -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 */ @@ -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; } @@ -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 }, @@ -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; } @@ -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; } @@ -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. @@ -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) @@ -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 + */ |
