diff options
120 files changed, 3250 insertions, 409 deletions
diff --git a/Documentation/api.rst b/Documentation/api.rst index 1270551c..cb8a0982 100644 --- a/Documentation/api.rst +++ b/Documentation/api.rst @@ -20,3 +20,8 @@ Typing ~~~~~~ .. c:autodoc:: evaluate.h + +Optimization +~~~~~~~~~~~~ + +.. c:autodoc:: simplify.c diff --git a/Documentation/sphinx/cdoc.py b/Documentation/sphinx/cdoc.py index 2718c86a..318e9b23 100755 --- a/Documentation/sphinx/cdoc.py +++ b/Documentation/sphinx/cdoc.py @@ -212,12 +212,14 @@ def convert_to_rst(info): lst.append((n, l)) if 'desc' in info: desc = info['desc'] - n = desc[0] - r = '' - for l in desc[1:]: - r += l + '\n' - lst.append((n, r)) - lst.append((n+1, '\n')) + n = desc[0] - 1 + desc.append('') + for i in range(1, len(desc)): + l = desc[i] + lst.append((n+i, l)) + # auto add a blank line for a list + if re.search(r":$", desc[i]) and re.search(r"\S", desc[i+1]): + lst.append((n+i, '')) elif typ == 'func': (n, l) = info['func'] @@ -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 @@ -94,14 +94,12 @@ void cse_collect(struct instruction *insn) case OP_TRUNC: case OP_PTRCAST: case OP_UTPTR: case OP_PTRTU: - /* - * This is crap! Many "orig_types" are the - * same as far as casts go, we should generate - * some kind of "type hash" that is identical - * for identical casts - */ - hash += hashval(insn->orig_type); + if (!insn->orig_type || insn->orig_type->bit_size < 0) + return; hash += hashval(insn->src); + + // Note: see corresponding line in insn_compare() + hash += hashval(insn->orig_type->bit_size); break; /* Other */ @@ -164,6 +162,7 @@ static int insn_compare(const void *_i1, const void *_i2) { const struct instruction *i1 = _i1; const struct instruction *i2 = _i2; + int size1, size2; int diff; if (i1->opcode != i2->opcode) @@ -241,13 +240,18 @@ static int insn_compare(const void *_i1, const void *_i2) case OP_TRUNC: case OP_PTRCAST: case OP_UTPTR: case OP_PTRTU: - /* - * This is crap! See the comments on hashing. - */ - if (i1->orig_type != i2->orig_type) - return i1->orig_type < i2->orig_type ? -1 : 1; if (i1->src != i2->src) return i1->src < i2->src ? -1 : 1; + + // Note: if it can be guaranted that identical ->src + // implies identical orig_type->bit_size, then this + // test and the hashing of the original size in + // cse_collect() are not needed. + // It must be generaly true but it isn't guaranted (yet). + size1 = i1->orig_type->bit_size; + size2 = i2->orig_type->bit_size; + if (size1 != size2) + return size1 < size2 ? -1 : 1; break; default: @@ -359,6 +363,8 @@ static struct instruction * try_to_cse(struct entrypoint *ep, struct instruction i1 = cse_one_instruction(i2, i1); remove_instruction(&b1->insns, i1, 1); add_instruction_to_end(i1, common); + } else { + i1 = i2; } return i1; @@ -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; } @@ -278,7 +278,7 @@ int simplify_flow(struct entrypoint *ep) static inline void concat_user_list(struct pseudo_user_list *src, struct pseudo_user_list **dst) { - concat_ptr_list((struct ptr_list *)src, (struct ptr_list **)dst); + copy_ptr_list((struct ptr_list **)dst, (struct ptr_list *)src); } void convert_instruction_target(struct instruction *insn, pseudo_t src) @@ -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..2e9a5638 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; @@ -879,15 +878,11 @@ pseudo_t alloc_phi(struct basic_block *source, pseudo_t pseudo, struct symbol *t */ struct access_data { struct symbol *type; // ctype + struct symbol *btype; // base type of bitfields 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 +925,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); @@ -941,14 +935,13 @@ static int linearize_address_gen(struct entrypoint *ep, static pseudo_t add_load(struct entrypoint *ep, struct access_data *ad) { - struct symbol *btype = bitfield_base_type(ad->type); struct instruction *insn; pseudo_t new; if (!ep->active) return VOID; - insn = alloc_typed_instruction(OP_LOAD, btype); + insn = alloc_typed_instruction(OP_LOAD, ad->btype); new = alloc_pseudo(insn); insn->target = new; @@ -966,42 +959,69 @@ static void add_store(struct entrypoint *ep, struct access_data *ad, pseudo_t va if (!bb) return; - store = alloc_typed_instruction(OP_STORE, bitfield_base_type(ad->type)); + store = alloc_typed_instruction(OP_STORE, ad->btype); store->offset = ad->offset; use_pseudo(store, value, &store->target); use_pseudo(store, ad->address, &store->src); 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); + unsigned long long smask= bits_mask(btype->bit_size); + + 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 & smask)); + 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) { struct symbol *ctype = ad->type; - struct symbol *btype = bitfield_base_type(ctype); + struct symbol *btype; pseudo_t store = value; if (!ep->active) return VOID; + btype = ad->btype = bitfield_base_type(ctype); 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,23 +1064,32 @@ 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; - struct symbol *btype = bitfield_base_type(ctype); + struct symbol *btype; pseudo_t new; if (!ep->active) return VOID; + btype = ad->btype = bitfield_base_type(ctype); 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,14 +1101,13 @@ 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; } static pseudo_t linearize_inc_dec(struct entrypoint *ep, struct expression *expr, int postop) { struct access_data ad = { NULL, }; - pseudo_t old, new, one; + pseudo_t old, new, one; int op = expr->op == SPECIAL_INCREMENT ? OP_ADD : OP_SUB; if (!linearize_address_gen(ep, expr->unop, &ad)) @@ -1091,9 +1119,12 @@ static pseudo_t linearize_inc_dec(struct entrypoint *ep, struct expression *expr one = add_setfval(ep, expr->ctype, expr->op_value); else one = value_pseudo(expr->op_value); - new = add_binary_op(ep, expr->ctype, op, old, one); + if (ad.btype != ad.type) + old = cast_pseudo(ep, old, ad.type, ad.btype); + new = add_binary_op(ep, ad.btype, op, old, one); + if (ad.btype != ad.type) + new = cast_pseudo(ep, new, ad.btype, ad.type); linearize_store_gen(ep, new, &ad); - finish_address_gen(ep, &ad); return postop ? old : new; } @@ -1289,6 +1320,7 @@ static int get_cast_opcode(struct symbol *dst, struct symbol *src) static pseudo_t cast_pseudo(struct entrypoint *ep, pseudo_t src, struct symbol *from, struct symbol *to) { + const struct position pos = current_pos; pseudo_t result; struct instruction *insn; int opcode; @@ -1309,7 +1341,7 @@ static pseudo_t cast_pseudo(struct entrypoint *ep, pseudo_t src, struct symbol * if (src == value_pseudo(0)) break; if (Wint_to_pointer_cast) - warning(to->pos, "non size-preserving integer to pointer cast"); + warning(pos, "non size-preserving integer to pointer cast"); src = cast_pseudo(ep, src, from, size_t_ctype); from = size_t_ctype; break; @@ -1317,7 +1349,7 @@ static pseudo_t cast_pseudo(struct entrypoint *ep, pseudo_t src, struct symbol * if (from->bit_size == to->bit_size) break; if (Wpointer_to_int_cast) - warning(to->pos, "non size-preserving pointer to integer cast"); + warning(pos, "non size-preserving pointer to integer cast"); src = cast_pseudo(ep, src, from, size_t_ctype); return cast_pseudo(ep, src, size_t_ctype, to); case OP_BADOP: @@ -1352,8 +1384,12 @@ static inline pseudo_t add_convert_to_bool(struct entrypoint *ep, pseudo_t src, pseudo_t zero; int op; + if (!type || src == VOID) + return VOID; if (is_bool_type(type)) return src; + if (src->type == PSEUDO_VAL && (src->value == 0 || src->value == 1)) + return src; if (is_float_type(type)) { zero = add_setfval(ep, type, 0.0); op = map_opcode(OP_SET_NE, type); @@ -1407,10 +1443,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 +1548,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; } @@ -1617,13 +1654,39 @@ static pseudo_t linearize_conditional(struct entrypoint *ep, struct expression * static pseudo_t linearize_logical(struct entrypoint *ep, struct expression *expr) { - struct expression *shortcut; + struct basic_block *other, *merge; + pseudo_t phi1, phi2; - shortcut = alloc_const_expression(expr->pos, expr->op == SPECIAL_LOGICAL_OR); - shortcut->ctype = expr->ctype; - if (expr->op == SPECIAL_LOGICAL_OR) - return linearize_conditional(ep, expr, expr->left, shortcut, expr->right); - return linearize_conditional(ep, expr, expr->left, expr->right, shortcut); + if (!ep->active || !expr->left || !expr->right) + return VOID; + + other = alloc_basic_block(ep, expr->right->pos); + merge = alloc_basic_block(ep, expr->pos); + + if (expr->op == SPECIAL_LOGICAL_OR) { + pseudo_t src2; + + phi1 = alloc_phi(ep->active, value_pseudo(1), expr->ctype); + linearize_cond_branch(ep, expr->left, merge, other); + + set_activeblock(ep, other); + src2 = linearize_expression_to_bool(ep, expr->right); + src2 = cast_pseudo(ep, src2, &bool_ctype, expr->ctype); + phi2 = alloc_phi(ep->active, src2, expr->ctype); + } else { + pseudo_t src1; + + phi2 = alloc_phi(ep->active, value_pseudo(0), expr->ctype); + linearize_cond_branch(ep, expr->left, other, merge); + + set_activeblock(ep, other); + src1 = linearize_expression_to_bool(ep, expr->right); + src1 = cast_pseudo(ep, src1, &bool_ctype, expr->ctype); + phi1 = alloc_phi(ep->active, src1, expr->ctype); + } + + set_activeblock(ep, merge); + return add_join_conditional(ep, expr, phi1, phi2); } static pseudo_t linearize_compare(struct entrypoint *ep, struct expression *expr) @@ -1679,7 +1742,7 @@ static pseudo_t linearize_cond_branch(struct entrypoint *ep, struct expression * return linearize_cond_branch(ep, expr->unop, bb_false, bb_true); /* fall through */ default: { - cond = linearize_expression(ep, expr); + cond = linearize_expression_to_bool(ep, expr); add_branch(ep, expr, cond, bb_true, bb_false); return VOID; @@ -1715,15 +1778,6 @@ static pseudo_t linearize_cast(struct entrypoint *ep, struct expression *expr) return cast_pseudo(ep, src, orig->ctype, expr->ctype); } -static pseudo_t linearize_position(struct entrypoint *ep, struct expression *pos, struct access_data *ad) -{ - struct expression *init_expr = pos->init_expr; - - ad->offset = pos->init_offset; - ad->type = init_expr->ctype; - return linearize_initializer(ep, init_expr, ad); -} - static pseudo_t linearize_initializer(struct entrypoint *ep, struct expression *initializer, struct access_data *ad) { switch (initializer->type) { @@ -1735,7 +1789,8 @@ static pseudo_t linearize_initializer(struct entrypoint *ep, struct expression * break; } case EXPR_POS: - linearize_position(ep, initializer, ad); + ad->offset = initializer->init_offset; + linearize_initializer(ep, initializer->init_expr, ad); break; default: { pseudo_t value = linearize_expression(ep, initializer); @@ -1755,7 +1810,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 +1913,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 +2018,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..1184df98 100644 --- a/linearize.h +++ b/linearize.h @@ -303,6 +303,11 @@ static inline int remove_pseudo(struct pseudo_list **list, pseudo_t pseudo) return delete_ptr_list_entry((struct ptr_list **)list, pseudo, 0) != 0; } +static inline int pseudo_in_list(struct pseudo_list *list, pseudo_t pseudo) +{ + return lookup_ptr_list_entry((struct ptr_list *)list, pseudo); +} + static inline int bb_terminated(struct basic_block *bb) { struct instruction *insn; @@ -333,9 +338,24 @@ static inline int pseudo_user_list_size(struct pseudo_user_list *list) return ptr_list_size((struct ptr_list *)list); } +static inline bool pseudo_user_list_empty(struct pseudo_user_list *list) +{ + return ptr_list_empty((struct ptr_list *)list); +} + static inline int has_users(pseudo_t p) { - return pseudo_user_list_size(p->users) != 0; + return !pseudo_user_list_empty(p->users); +} + +static inline bool multi_users(pseudo_t p) +{ + return ptr_list_multiple((struct ptr_list *)(p->users)); +} + +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) @@ -139,15 +139,6 @@ static void track_instruction_usage(struct basic_block *bb, struct instruction * } } -int pseudo_in_list(struct pseudo_list *list, pseudo_t pseudo) -{ - pseudo_t old; - FOR_EACH_PTR(list,old) { - if (old == pseudo) - return 1; - } END_FOR_EACH_PTR(old); - return 0; -} static int liveness_changed; @@ -37,6 +37,46 @@ int ptr_list_size(struct ptr_list *head) } /// +// test if a list is empty +// @head: the head of the list +// @return: ``true`` if the list is empty, ``false`` otherwise. +bool ptr_list_empty(const struct ptr_list *head) +{ + const struct ptr_list *list = head; + + if (!head) + return true; + + do { + if (list->nr - list->rm) + return false; + } while ((list = list->next) != head); + + return true; +} + +/// +// test is a list contains more than one element +// @head: the head of the list +// @return: ``true`` if the list has more than 1 element, ``false`` otherwise. +bool ptr_list_multiple(const struct ptr_list *head) +{ + const struct ptr_list *list = head; + int nr = 0; + + if (!head) + return false; + + do { + nr += list->nr - list->rm; + if (nr > 1) + return true; + } while ((list = list->next) != head); + + return false; +} + +/// // get the first element of a ptrlist // @head: the head of the list // @return: the first element of the list or ``NULL`` if the list is empty @@ -233,6 +273,27 @@ void **__add_ptr_list_tag(struct ptr_list **listp, void *ptr, unsigned long tag) } /// +// test if some entry is already present in a ptrlist +// @list: the head of the list +// @entry: the entry to test +// @return: ``true`` if the entry is already present, ``false`` otherwise. +bool lookup_ptr_list_entry(const struct ptr_list *head, const void *entry) +{ + const struct ptr_list *list = head; + + if (!head) + return false; + do { + int nr = list->nr; + int i; + for (i = 0; i < nr; i++) + if (list->list[i] == entry) + return true; + } while ((list = list->next) != head); + return false; +} + +/// // delete an entry from a ptrlist // @list: a pointer to the list // @entry: the item to be deleted @@ -341,6 +402,55 @@ void concat_ptr_list(struct ptr_list *a, struct ptr_list **b) } /// +// copy the elements of a list at the end of another list. +// @listp: a pointer to the destination list. +// @src: the head of the source list. +void copy_ptr_list(struct ptr_list **listp, struct ptr_list *src) +{ + struct ptr_list *head, *tail; + struct ptr_list *cur = src; + int idx; + + if (!src) + return; + head = *listp; + if (!head) { + *listp = src; + return; + } + + tail = head->prev; + idx = tail->nr; + do { + struct ptr_list *next; + int nr = cur->nr; + int i; + for (i = 0; i < nr;) { + void *ptr = cur->list[i++]; + if (!ptr) + continue; + if (idx >= LIST_NODE_NR) { + struct ptr_list *prev = tail; + tail = __alloc_ptrlist(0); + prev->next = tail; + tail->prev = prev; + prev->nr = idx; + idx = 0; + } + tail->list[idx++] = ptr; + } + + next = cur->next; + __free_ptrlist(cur); + cur = next; + } while (cur != src); + + tail->nr = idx; + head->prev = tail; + tail->next = head; +} + +/// // free a ptrlist // @listp: a pointer to the list // Each blocks of the list are freed (but the entries @@ -2,6 +2,7 @@ #define PTR_LIST_H #include <stdlib.h> +#include <stdbool.h> /* * Generic pointer list manipulation code. @@ -32,10 +33,14 @@ void * undo_ptr_list_last(struct ptr_list **head); void * delete_ptr_list_last(struct ptr_list **head); int delete_ptr_list_entry(struct ptr_list **, void *, int); int replace_ptr_list_entry(struct ptr_list **, void *old, void *new, int); +bool lookup_ptr_list_entry(const struct ptr_list *head, const void *entry); extern void sort_list(struct ptr_list **, int (*)(const void *, const void *)); extern void concat_ptr_list(struct ptr_list *a, struct ptr_list **b); +extern void copy_ptr_list(struct ptr_list **h, struct ptr_list *t); extern int ptr_list_size(struct ptr_list *); +extern bool ptr_list_empty(const struct ptr_list *head); +extern bool ptr_list_multiple(const struct ptr_list *head); extern int linearize_ptr_list(struct ptr_list *, void **, int); extern void *first_ptr_list(struct ptr_list *); extern void *last_ptr_list(struct ptr_list *); 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; } @@ -4,6 +4,41 @@ * Copyright (C) 2004 Linus Torvalds */ +/// +// Instruction simplification +// -------------------------- +// +// Notation +// ^^^^^^^^ +// The following conventions are used to describe the simplications: +// * Uppercase letters are reserved for constants: +// * `M` for a constant mask, +// * `S` for a constant shift, +// * `N` for a constant number of bits (usually other than a shift), +// * `C` or 'K' for others constants. +// * Lowercase letters `a`, `b`, `x`, `y`, ... are used for non-constants +// or when it doesn't matter if the pseudo is a constant or not. +// * Primes are used if needed to distinguish symbols (`M`, `M'`, ...). +// * Expressions or sub-expressions involving only constants are +// understood to be evaluated. +// * `$mask(N)` is used for `((1 << N) -1)` +// * `$trunc(x, N)` is used for `(x & $mask(N))` +// * Expressions like `(-1 << S)`, `(-1 >> S)` and others formulae are +// understood to be truncated to the size of the current instruction +// (needed, since in general this size is not the same as the one used +// by sparse for the evaluation of arithmetic operations). +// * `TRUNC(x, N)` is used for a truncation *to* a size of `N` bits +// * `ZEXT(x, N)` is used for a zero-extension *from* a size of `N` bits +// * `OP(x, C)` is used to represent some generic operation using a constant, +// including when the constant is implicit (e.g. `TRUNC(x, N)`). +// * `MASK(x, M)` is used to respresent a 'masking' instruction: +// - `AND(x, M)` +// - `LSR(x, S)`, with `M` = (-1 << S) +// - `SHL(x, S)`, with `M` = (-1 >> S) +// - `TRUNC(x, N)`, with `M` = $mask(N) +// - `ZEXT(x, N)`, with `M` = $mask(N) +// * `SHIFT(x, S)` is used for `LSR(x, S)` or `SHL(x, S)`. + #include <assert.h> #include "parse.h" @@ -12,7 +47,12 @@ #include "flow.h" #include "symbol.h" -/* Find the trivial parent for a phi-source */ +/// +// Utilities +// ^^^^^^^^^ + +/// +// find the trivial parent for a phi-source static struct basic_block *phi_parent(struct basic_block *source, pseudo_t pseudo) { /* Can't go upwards if the pseudo is defined in the bb it came from.. */ @@ -26,16 +66,15 @@ static struct basic_block *phi_parent(struct basic_block *source, pseudo_t pseud return first_basic_block(source->parents); } -/* - * Copy the phi-node's phisrcs into to given array. - * Returns 0 if the the list contained the expected - * number of element, a positive number if there was - * more than expected and a negative one if less. - * - * Note: we can't reuse a function like linearize_ptr_list() - * because any VOIDs in the phi-list must be ignored here - * as in this context they mean 'entry has been removed'. - */ +/// +// copy the phi-node's phisrcs into to given array +// @return: 0 if the the list contained the expected +// number of element, a positive number if there was +// more than expected and a negative one if less. +// +// :note: we can't reuse a function like linearize_ptr_list() +// because any VOIDs in the phi-list must be ignored here +// as in this context they mean 'entry has been removed'. static int get_phisources(struct instruction *sources[], int nbr, struct instruction *insn) { pseudo_t phi; @@ -179,7 +218,7 @@ static int delete_pseudo_user_list_entry(struct pseudo_user_list **list, pseudo_ } END_FOR_EACH_PTR(pu); assert(count <= 0); out: - if (pseudo_user_list_size(*list) == 0) + if (pseudo_user_list_empty(*list)) *list = NULL; return count; } @@ -227,15 +266,16 @@ static void kill_use_list(struct pseudo_list *list) } END_FOR_EACH_PTR(p); } -/* - * kill an instruction: - * - remove it from its bb - * - remove the usage of all its operands - * If forse is zero, the normal case, the function only for - * instructions free of (possible) side-effects. Otherwise - * the function does that unconditionally (must only be used - * for unreachable instructions. - */ +/// +// kill an instruction +// @insn: the instruction to be killed +// @force: if unset, the normal case, the instruction is not killed +// if not free of possible side-effect; if set the instruction +// is unconditionally killed. +// +// The killed instruction is removed from its BB and the usage +// of all its operands are removed. The instruction is also +// marked as killed by setting its ->bb to NULL. int kill_insn(struct instruction *insn, int force) { if (!insn || !insn->bb) @@ -270,6 +310,7 @@ int kill_insn(struct instruction *insn, int force) break; case OP_CBR: + case OP_SWITCH: case OP_COMPUTEDGOTO: kill_use(&insn->cond); break; @@ -314,9 +355,8 @@ int kill_insn(struct instruction *insn, int force) return repeat_phase |= REPEAT_CSE; } -/* - * Kill trivially dead instructions - */ +/// +// kill trivially dead instructions static int dead_insn(struct instruction *insn, pseudo_t *src1, pseudo_t *src2, pseudo_t *src3) { if (has_users(insn->target)) @@ -335,7 +375,7 @@ static inline int constant(pseudo_t pseudo) } /// -// replace one operand by a new value +// replace the operand of an instruction // @insn: the instruction // @pp: the address of the instruction's operand // @new: the new value for the operand @@ -377,6 +417,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; @@ -391,12 +438,11 @@ static unsigned int value_size(long long value) return 64; } -/* - * Try to determine the maximum size of bits in a pseudo. - * - * Right now this only follow casts and constant values, but we - * could look at things like logical 'and' instructions etc. - */ +/// +// try to determine the maximum size of bits in a pseudo +// +// Right now this only follow casts and constant values, but we +// could look at things like AND instructions, etc. static unsigned int operand_size(struct instruction *insn, pseudo_t pseudo) { unsigned int size = insn->size; @@ -471,12 +517,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 */ @@ -532,18 +584,296 @@ undef: return NULL; } +/// +// Simplifications +// ^^^^^^^^^^^^^^^ -static int simplify_asr(struct instruction *insn, pseudo_t pseudo, long long value) +/// +// try to simplify MASK(OR(AND(x, M'), b), M) +// @insn: the masking instruction +// @mask: the associated mask (M) +// @ora: one of the OR's operands, guaranteed to be PSEUDO_REG +// @orb: the other OR's operand +// @return: 0 if no changes have been made, one or more REPEAT_* flags otherwise. +static int simplify_mask_or_and(struct instruction *insn, unsigned long long mask, + pseudo_t ora, pseudo_t orb) { - unsigned int size = operand_size(insn, pseudo); + unsigned long long omask, nmask; + struct instruction *and = ora->def; + pseudo_t src2 = and->src2; - if (value >= size) { - warning(insn->pos, "right shift by bigger than source value"); - return replace_with_pseudo(insn, value_pseudo(0)); + if (and->opcode != OP_AND) + return 0; + if (!constant(src2)) + return 0; + omask = src2->value; + nmask = omask & mask; + if (nmask == 0) { + // if (M' & M) == 0: ((a & M') | b) -> b + return replace_pseudo(insn, &insn->src1, orb); + } + if (multi_users(insn->src1)) + return 0; // can't modify anything inside the OR + if (nmask == mask) { + struct instruction *or = insn->src1->def; + pseudo_t *arg = (ora == or->src1) ? &or->src1 : &or->src2; + // if (M' & M) == M: ((a & M') | b) -> (a | b) + return replace_pseudo(or, arg, and->src1); + } + if (nmask != omask && !multi_users(ora)) { + // if (M' & M) != M': AND(a, M') -> AND(a, (M' & M)) + and->src2 = value_pseudo(nmask); + return REPEAT_CSE; + } + return 0; +} + +/// +// try to simplify MASK(OR(a, b), M) +// @insn: the masking instruction +// @mask: the associated mask (M) +// @or: the OR instruction +// @return: 0 if no changes have been made, one or more REPEAT_* flags otherwise. +static int simplify_mask_or(struct instruction *insn, unsigned long long mask, struct instruction *or) +{ + pseudo_t src1 = or->src1; + pseudo_t src2 = or->src2; + int rc; + + if (src1->type == PSEUDO_REG) { + if ((rc = simplify_mask_or_and(insn, mask, src1, src2))) + return rc; + } + if (src2->type == PSEUDO_REG) { + if ((rc = simplify_mask_or_and(insn, mask, src2, src1))) + return rc; + } else if (src2->type == PSEUDO_VAL) { + unsigned long long oval = src2->value; + unsigned long long nval = oval & mask; + // Try to simplify: + // MASK(OR(x, C), M) + if (nval == 0) { + // if (C & M) == 0: OR(x, C) -> x + return replace_pseudo(insn, &insn->src1, src1); + } + if (nval == mask) { + // if (C & M) == M: OR(x, C) -> M + return replace_pseudo(insn, &insn->src1, value_pseudo(mask)); + } + if (nval != oval && !multi_users(or->target)) { + // if (C & M) != C: OR(x, C) -> OR(x, (C & M)) + return replace_pseudo(or, &or->src2, value_pseudo(nval)); + } + } + return 0; +} + +/// +// try to simplify MASK(SHIFT(OR(a, b), S), M) +// @sh: the shift instruction +// @or: the OR instruction +// @mask: the mask associated to MASK (M): +// @return: 0 if no changes have been made, one or more REPEAT_* flags otherwise. +static int simplify_mask_shift_or(struct instruction *sh, struct instruction *or, unsigned long long mask) +{ + unsigned long long smask = bits_mask(sh->size); + int shift = sh->src2->value; + + if (sh->opcode == OP_LSR) + mask <<= shift; + else + mask >>= shift; + return simplify_mask_or(sh, smask & mask, or); +} + +static int simplify_mask_shift(struct instruction *sh, unsigned long long mask) +{ + struct instruction *inner; + + if (!constant(sh->src2) || sh->tainted) + return 0; + switch (DEF_OPCODE(inner, sh->src1)) { + case OP_OR: + if (!multi_users(sh->target)) + return simplify_mask_shift_or(sh, inner, mask); + break; } + return 0; +} + +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_shift(struct instruction *insn, pseudo_t pseudo, long long value) +{ + struct instruction *def; + unsigned long long mask, omask, nmask; + unsigned long long nval; + unsigned int size; + pseudo_t src2; + 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); + if (value >= size) + goto zero; + switch(DEF_OPCODE(def, pseudo)) { + case OP_AND: + // replace (A & M) >> S + // by (A >> S) & (M >> S) + if (!constant(def->src2)) + break; + mask = bits_mask(insn->size - value) << value; + omask = def->src2->value; + nmask = omask & mask; + if (nmask == 0) + return replace_with_pseudo(insn, value_pseudo(0)); + if (nmask == mask) + return replace_pseudo(insn, &insn->src1, def->src1); + if (nbr_users(pseudo) > 1) + break; + def->opcode = OP_LSR; + def->src2 = insn->src2; + insn->opcode = OP_AND; + insn->src2 = value_pseudo(omask >> value); + return REPEAT_CSE; + case OP_LSR: + goto case_shift_shift; + case OP_OR: + mask = bits_mask(size); + return simplify_mask_shift_or(insn, def, mask); + case OP_SHL: + // replace ((x << S) >> S) + // by (x & (-1 >> S)) + if (def->src2 != insn->src2) + break; + mask = bits_mask(insn->size - value); + goto replace_mask; + } + break; + case OP_SHL: + if (value >= size) + goto zero; + switch(DEF_OPCODE(def, pseudo)) { + case OP_AND: + // simplify (A & M) << S + if (!constant(def->src2)) + break; + mask = bits_mask(insn->size) >> value; + omask = def->src2->value; + nmask = omask & mask; + if (nmask == 0) + return replace_with_pseudo(insn, value_pseudo(0)); + if (nmask == mask) + return replace_pseudo(insn, &insn->src1, def->src1); + // do not simplify into ((A << S) & (M << S)) + break; + case OP_LSR: + // replace ((x >> S) << S) + // by (x & (-1 << S)) + if (def->src2 != insn->src2) + break; + mask = bits_mask(insn->size - value) << value; + goto replace_mask; + case OP_OR: + mask = bits_mask(size); + return simplify_mask_shift_or(insn, def, mask); + case OP_SHL: + case_shift_shift: // also for LSR - LSR + 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)); +replace_mask: + insn->opcode = OP_AND; + insn->src2 = value_pseudo(mask); + return replace_pseudo(insn, &insn->src1, def->src1); } static int simplify_mul_div(struct instruction *insn, long long value) @@ -577,7 +907,7 @@ static int simplify_seteq_setne(struct instruction *insn, long long value) { pseudo_t old = insn->src1; struct instruction *def; - pseudo_t src1, src2; + unsigned osize; int inverse; int opcode; @@ -591,8 +921,26 @@ static int simplify_seteq_setne(struct instruction *insn, long long value) return 0; inverse = (insn->opcode == OP_SET_NE) == value; + if (!inverse && def->size == 1 && insn->size == 1) { + // Replace: + // setne %r <- %s, $0 + // or: + // seteq %r <- %s, $1 + // by %s when boolean + return replace_with_pseudo(insn, old); + } opcode = def->opcode; switch (opcode) { + case OP_AND: + if (inverse) + break; + if (def->size != insn->size) + break; + if (def->src2->type != PSEUDO_VAL) + break; + if (def->src2->value != 1) + break; + return replace_with_pseudo(insn, old); case OP_FPCMP ... OP_BINCMP_END: // Convert: // setcc.n %t <- %a, %b @@ -601,23 +949,74 @@ static int simplify_seteq_setne(struct instruction *insn, long long value) // setcc.n %t <- %a, %b // setcc.m %r <- %a, $b // and similar for setne/eq ... 0/1 - src1 = def->src1; - src2 = def->src2; insn->opcode = inverse ? opcode_table[opcode].negate : opcode; - use_pseudo(insn, src1, &insn->src1); - use_pseudo(insn, src2, &insn->src2); + use_pseudo(insn, def->src1, &insn->src1); + use_pseudo(insn, def->src2, &insn->src2); remove_usage(old, &insn->src1); return REPEAT_CSE; + case OP_SEXT: + if (value && (def->orig_type->bit_size == 1)) + break; + /* Fall through */ case OP_ZEXT: - if (def->orig_type->bit_size == 1) { - // Convert: - // zext.m %s <- (1) %a - // setne.1 %r <- %s, $0 - // into: - // setne.1 %s <- %a, $0 - // and same for setne/eq ... 0/1 - return replace_pseudo(insn, &insn->src1, def->src1); + // Convert: + // *ext.m %s <- (1) %a + // setne.1 %r <- %s, $0 + // into: + // setne.1 %s <- %a, $0 + // and same for setne/eq ... 0/1 + return replace_pseudo(insn, &insn->src1, def->src); + case OP_TRUNC: + if (multi_users(old)) + break; + // convert + // trunc.n %s <- (o) %a + // setne.m %r <- %s, $0 + // into: + // and.o %s <- %a, $((1 << o) - 1) + // setne.m %r <- %s, $0 + // and same for setne/eq ... 0/1 + osize = def->size; + def->opcode = OP_AND; + def->type = def->orig_type; + def->size = def->type->bit_size; + def->src2 = value_pseudo(bits_mask(osize)); + return REPEAT_CSE; + } + 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_FPCMP ... OP_BINCMP_END: + osize = 1; + goto oldsize; + case OP_OR: + return simplify_mask_or(insn, mask, def); + case OP_LSR: + case OP_SHL: + return simplify_mask_shift(def, mask); + case OP_ZEXT: + osize = def->orig_type->bit_size; + /* fall through */ + oldsize: + 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; } @@ -651,14 +1050,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) @@ -674,7 +1073,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: @@ -823,7 +1222,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 (multi_users(def->target)) return 0; switch_pseudo(def, &def->src1, insn, &insn->src2); return REPEAT_CSE; @@ -914,7 +1313,7 @@ static int simplify_one_memop(struct instruction *insn, pseudo_t orig) offset: /* Invalid code */ - if (new == orig) { + if (new == orig || new == addr) { if (new == VOID) return 0; /* @@ -926,18 +1325,20 @@ offset: */ if (repeat_phase & REPEAT_CFG_CLEANUP) return 0; - new = VOID; warning(insn->pos, "crazy programmer"); + replace_pseudo(insn, &insn->src, VOID); + return 0; } insn->offset += off->value; replace_pseudo(insn, &insn->src, new); return REPEAT_CSE | REPEAT_SYMBOL_CLEANUP; } -/* - * We walk the whole chain of adds/subs backwards. That's not - * only more efficient, but it allows us to find loops. - */ +/// +// simplify memops instructions +// +// :note: We walk the whole chain of adds/subs backwards. +// That's not only more efficient, but it allows us to find loops. static int simplify_memop(struct instruction *insn) { int one, ret = 0; @@ -952,8 +1353,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; @@ -971,29 +1374,125 @@ 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 (multi_users(src)) + 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 (multi_users(src)) + 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_FPCMP ... OP_BINCMP_END: + switch (insn->opcode) { + case OP_SEXT: + if (insn->size == 1) + break; + /* fall through */ + case OP_ZEXT: + case OP_TRUNC: + // simplify: + // setcc.n %t <- %a, %b + // zext.m %r <- (n) %t + // into: + // setcc.m %r <- %a, %b + // and same for s/zext/trunc/ + insn->opcode = def->opcode; + use_pseudo(insn, def->src2, &insn->src2); + return replace_pseudo(insn, &insn->src1, def->src1); + } + break; + case OP_OR: + switch (insn->opcode) { + case OP_TRUNC: + mask = bits_mask(insn->size); + return simplify_mask_or(insn, mask, def); } break; + case OP_LSR: + case OP_SHL: + if (insn->opcode != OP_TRUNC) + break; + mask = bits_mask(insn->size); + return simplify_mask_shift(def, mask); case OP_TRUNC: - osize = def->orig_type->bit_size; - if (insn->opcode == OP_ZEXT && size == osize) { + switch (insn->opcode) { + case OP_TRUNC: + insn->orig_type = def->orig_type; + return replace_pseudo(insn, &insn->src1, def->src); + case OP_ZEXT: + if (size != def->orig_type->bit_size) + break; insn->opcode = OP_AND; insn->src2 = value_pseudo((1ULL << def->size) - 1); 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) @@ -1070,9 +1569,8 @@ static int simplify_range(struct instruction *insn) return 0; } -/* - * Simplify "set_ne/eq $0 + br" - */ +/// +// simplify SET_NE/EQ $0 + BR static int simplify_cond_branch(struct instruction *br, struct instruction *def, pseudo_t newcond) { replace_pseudo(br, &br->cond, newcond); @@ -1215,12 +1713,22 @@ int simplify_instruction(struct instruction *insn) case OP_FCVTU: case OP_FCVTS: case OP_UCVTF: case OP_SCVTF: case OP_FCVTF: + case OP_PTRCAST: + if (dead_insn(insn, &insn->src, NULL, NULL)) + return REPEAT_CSE; + break; case OP_UTPTR: case OP_PTRTU: - case OP_PTRCAST: + return replace_with_pseudo(insn, insn->src); + case OP_SLICE: if (dead_insn(insn, &insn->src, NULL, NULL)) return REPEAT_CSE; break; + case OP_SETVAL: + case OP_SETFVAL: + if (dead_insn(insn, NULL, NULL, NULL)) + return REPEAT_CSE; + break; case OP_PHI: if (dead_insn(insn, NULL, NULL, NULL)) { kill_use_list(insn->phi_list); @@ -1239,6 +1747,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-llvm.c b/sparse-llvm.c index d28eb6e7..58811309 100644 --- a/sparse-llvm.c +++ b/sparse-llvm.c @@ -636,6 +636,10 @@ static void output_op_compare(struct function *fn, struct instruction *insn) case LLVMIntegerTypeKind: { LLVMIntPredicate op = translate_op(insn->opcode); + if (LLVMGetTypeKind(LLVMTypeOf(rhs)) == LLVMPointerTypeKind) { + LLVMTypeRef ltype = LLVMTypeOf(lhs); + rhs = LLVMBuildPtrToInt(fn->builder, rhs, ltype, ""); + } target = LLVMBuildICmp(fn->builder, op, lhs, rhs, target_name); break; } @@ -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/cast-kinds-check.c b/validation/cast-kinds-check.c index 0fe705f5..7eb1ca1c 100644 --- a/validation/cast-kinds-check.c +++ b/validation/cast-kinds-check.c @@ -1,19 +1,19 @@ -#include "linear/cast-kinds.c" +#include "optim/cast-kinds.c" /* * check-name: cast-kinds check * check-command: sparse -m64 -v -Wno-pointer-to-int-cast $file * * check-error-start -linear/cast-kinds.c:5:45: warning: cast drops bits -linear/cast-kinds.c:6:47: warning: cast drops bits -linear/cast-kinds.c:7:46: warning: cast drops bits -linear/cast-kinds.c:8:45: warning: cast drops bits -linear/cast-kinds.c:12:48: warning: cast drops bits -linear/cast-kinds.c:13:50: warning: cast drops bits -linear/cast-kinds.c:14:49: warning: cast drops bits -linear/cast-kinds.c:15:48: warning: cast drops bits -linear/cast-kinds.c:37:42: warning: non size-preserving integer to pointer cast -linear/cast-kinds.c:38:44: warning: non size-preserving integer to pointer cast +optim/cast-kinds.c:5:45: warning: cast drops bits +optim/cast-kinds.c:6:47: warning: cast drops bits +optim/cast-kinds.c:7:46: warning: cast drops bits +optim/cast-kinds.c:8:45: warning: cast drops bits +optim/cast-kinds.c:12:48: warning: cast drops bits +optim/cast-kinds.c:13:50: warning: cast drops bits +optim/cast-kinds.c:14:49: warning: cast drops bits +optim/cast-kinds.c:15:48: warning: cast drops bits +optim/cast-kinds.c:37:48: warning: non size-preserving integer to pointer cast +optim/cast-kinds.c:38:50: warning: non size-preserving integer to pointer cast * check-error-end */ diff --git a/validation/cast-weirds.c b/validation/cast-weirds.c index a99c65d2..7d028882 100644 --- a/validation/cast-weirds.c +++ b/validation/cast-weirds.c @@ -9,43 +9,10 @@ static void * uint_2_vptr(uint a) { return (void *)a; } /* * check-name: cast-weirds - * check-command: test-linearize -m64 $file + * check-command: sparse -m64 $file * * check-error-start -cast-weirds.c:4:42: warning: non size-preserving integer to pointer cast -cast-weirds.c:5:44: warning: non size-preserving integer to pointer cast +cast-weirds.c:4:48: warning: non size-preserving integer to pointer cast +cast-weirds.c:5:50: warning: non size-preserving integer to pointer cast * check-error-end - * - * check-output-start -int_2_iptr: -.L0: - <entry-point> - sext.64 %r2 <- (32) %arg1 - utptr.64 %r3 <- (64) %r2 - ret.64 %r3 - - -uint_2_iptr: -.L2: - <entry-point> - zext.64 %r6 <- (32) %arg1 - utptr.64 %r7 <- (64) %r6 - ret.64 %r7 - - -int_2_vptr: -.L4: - <entry-point> - sext.64 %r10 <- (32) %arg1 - ret.64 %r10 - - -uint_2_vptr: -.L6: - <entry-point> - zext.64 %r13 <- (32) %arg1 - ret.64 %r13 - - - * check-output-end */ 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/kill-switch.c b/validation/kill-switch.c new file mode 100644 index 00000000..1316a775 --- /dev/null +++ b/validation/kill-switch.c @@ -0,0 +1,17 @@ +extern int i; + +static void foo(void) +{ + switch (i) { + case 0: + ; + } +} + +/* + * check-name: kill-switch + * check-command: test-linearize $file + * + * check-output-ignore + * check-output-excludes: load\\. + */ diff --git a/validation/linear/bitfield-inc.c b/validation/linear/bitfield-inc.c new file mode 100644 index 00000000..56997592 --- /dev/null +++ b/validation/linear/bitfield-inc.c @@ -0,0 +1,16 @@ +struct s { + int f:5; +}; + +void inc(struct s *p) +{ + p->f++; +} + +/* + * check-name: bitfield-inc + * check-command: test-linearize -Wno-decl $file + * + * 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-size.c b/validation/linear/bitfield-size.c index 963f6e28..841bdd0a 100644 --- a/validation/linear/bitfield-size.c +++ b/validation/linear/bitfield-size.c @@ -49,41 +49,45 @@ upostinc: load.64 %r1 <- 0[x] load.32 %r2 <- 0[%r1] trunc.3 %r3 <- (32) %r2 - add.3 %r4 <- %r3, $1 - load.32 %r5 <- 0[%r1] - zext.32 %r6 <- (3) %r4 - and.32 %r7 <- %r5, $-8 - or.32 %r8 <- %r7, %r6 - store.32 %r8 -> 0[%r1] - zext.32 %r9 <- (3) %r3 - phisrc.32 %phi1(return) <- %r9 + zext.32 %r4 <- (3) %r3 + add.32 %r5 <- %r4, $1 + trunc.3 %r6 <- (32) %r5 + load.32 %r7 <- 0[%r1] + zext.32 %r8 <- (3) %r6 + and.32 %r9 <- %r7, $0xfffffff8 + or.32 %r10 <- %r9, %r8 + store.32 %r10 -> 0[%r1] + zext.32 %r11 <- (3) %r4 + phisrc.32 %phi1(return) <- %r11 br .L1 .L1: - phi.32 %r10 <- %phi1(return) - ret.32 %r10 + phi.32 %r12 <- %phi1(return) + ret.32 %r12 upreinc: .L2: <entry-point> store.64 %arg1 -> 0[x] - load.64 %r11 <- 0[x] - load.32 %r12 <- 0[%r11] - trunc.3 %r13 <- (32) %r12 - add.3 %r14 <- %r13, $1 - load.32 %r15 <- 0[%r11] - zext.32 %r16 <- (3) %r14 - and.32 %r17 <- %r15, $-8 - or.32 %r18 <- %r17, %r16 - store.32 %r18 -> 0[%r11] - zext.32 %r19 <- (3) %r14 - phisrc.32 %phi2(return) <- %r19 + load.64 %r13 <- 0[x] + load.32 %r14 <- 0[%r13] + trunc.3 %r15 <- (32) %r14 + zext.32 %r16 <- (3) %r15 + add.32 %r17 <- %r16, $1 + trunc.3 %r18 <- (32) %r17 + load.32 %r19 <- 0[%r13] + zext.32 %r20 <- (3) %r18 + and.32 %r21 <- %r19, $0xfffffff8 + or.32 %r22 <- %r21, %r20 + store.32 %r22 -> 0[%r13] + zext.32 %r23 <- (3) %r18 + phisrc.32 %phi2(return) <- %r23 br .L3 .L3: - phi.32 %r20 <- %phi2(return) - ret.32 %r20 + phi.32 %r24 <- %phi2(return) + ret.32 %r24 ucpy: @@ -91,15 +95,15 @@ ucpy: <entry-point> store.64 %arg1 -> 0[d] store.64 %arg2 -> 0[s] - load.64 %r21 <- 0[s] - load.32 %r22 <- 0[%r21] - trunc.3 %r23 <- (32) %r22 - load.64 %r24 <- 0[d] - load.32 %r25 <- 0[%r24] - zext.32 %r26 <- (3) %r23 - and.32 %r27 <- %r25, $-8 - or.32 %r28 <- %r27, %r26 - store.32 %r28 -> 0[%r24] + load.64 %r25 <- 0[s] + load.32 %r26 <- 0[%r25] + trunc.3 %r27 <- (32) %r26 + load.64 %r28 <- 0[d] + load.32 %r29 <- 0[%r28] + zext.32 %r30 <- (3) %r27 + and.32 %r31 <- %r29, $0xfffffff8 + or.32 %r32 <- %r31, %r30 + store.32 %r32 -> 0[%r28] br .L5 .L5: @@ -110,44 +114,48 @@ spostinc: .L6: <entry-point> store.64 %arg1 -> 0[x] - load.64 %r29 <- 0[x] - load.32 %r30 <- 0[%r29] - trunc.3 %r31 <- (32) %r30 - add.3 %r32 <- %r31, $1 - load.32 %r33 <- 0[%r29] - zext.32 %r34 <- (3) %r32 - and.32 %r35 <- %r33, $-8 - or.32 %r36 <- %r35, %r34 - store.32 %r36 -> 0[%r29] - zext.32 %r37 <- (3) %r31 - phisrc.32 %phi3(return) <- %r37 + load.64 %r33 <- 0[x] + load.32 %r34 <- 0[%r33] + trunc.3 %r35 <- (32) %r34 + zext.32 %r36 <- (3) %r35 + add.32 %r37 <- %r36, $1 + trunc.3 %r38 <- (32) %r37 + load.32 %r39 <- 0[%r33] + zext.32 %r40 <- (3) %r38 + and.32 %r41 <- %r39, $0xfffffff8 + or.32 %r42 <- %r41, %r40 + store.32 %r42 -> 0[%r33] + zext.32 %r43 <- (3) %r36 + phisrc.32 %phi3(return) <- %r43 br .L7 .L7: - phi.32 %r38 <- %phi3(return) - ret.32 %r38 + phi.32 %r44 <- %phi3(return) + ret.32 %r44 spreinc: .L8: <entry-point> store.64 %arg1 -> 0[x] - load.64 %r39 <- 0[x] - load.32 %r40 <- 0[%r39] - trunc.3 %r41 <- (32) %r40 - add.3 %r42 <- %r41, $1 - load.32 %r43 <- 0[%r39] - zext.32 %r44 <- (3) %r42 - and.32 %r45 <- %r43, $-8 - or.32 %r46 <- %r45, %r44 - store.32 %r46 -> 0[%r39] - zext.32 %r47 <- (3) %r42 - phisrc.32 %phi4(return) <- %r47 + load.64 %r45 <- 0[x] + load.32 %r46 <- 0[%r45] + trunc.3 %r47 <- (32) %r46 + zext.32 %r48 <- (3) %r47 + add.32 %r49 <- %r48, $1 + trunc.3 %r50 <- (32) %r49 + load.32 %r51 <- 0[%r45] + zext.32 %r52 <- (3) %r50 + and.32 %r53 <- %r51, $0xfffffff8 + or.32 %r54 <- %r53, %r52 + store.32 %r54 -> 0[%r45] + zext.32 %r55 <- (3) %r50 + phisrc.32 %phi4(return) <- %r55 br .L9 .L9: - phi.32 %r48 <- %phi4(return) - ret.32 %r48 + phi.32 %r56 <- %phi4(return) + ret.32 %r56 scpy: @@ -155,15 +163,15 @@ scpy: <entry-point> store.64 %arg1 -> 0[d] store.64 %arg2 -> 0[s] - load.64 %r49 <- 0[s] - load.32 %r50 <- 0[%r49] - trunc.3 %r51 <- (32) %r50 - load.64 %r52 <- 0[d] - load.32 %r53 <- 0[%r52] - zext.32 %r54 <- (3) %r51 - and.32 %r55 <- %r53, $-8 - or.32 %r56 <- %r55, %r54 - store.32 %r56 -> 0[%r52] + load.64 %r57 <- 0[s] + load.32 %r58 <- 0[%r57] + trunc.3 %r59 <- (32) %r58 + load.64 %r60 <- 0[d] + load.32 %r61 <- 0[%r60] + zext.32 %r62 <- (3) %r59 + and.32 %r63 <- %r61, $0xfffffff8 + or.32 %r64 <- %r63, %r62 + store.32 %r64 -> 0[%r60] br .L11 .L11: 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/call-complex-pointer.c b/validation/linear/call-complex-pointer.c index ea8232f1..dc0ab07b 100644 --- a/validation/linear/call-complex-pointer.c +++ b/validation/linear/call-complex-pointer.c @@ -18,14 +18,14 @@ foo: br .L4 .L3: - ptrcast.64 %r5 <- (64) %arg3 - phisrc.64 %phi2 <- %r5 + ptrcast.64 %r6 <- (64) %arg3 + phisrc.64 %phi2 <- %r6 br .L4 .L4: - phi.64 %r6 <- %phi1, %phi2 - call.32 %r7 <- %r6, %arg4 - ret.32 %r7 + phi.64 %r7 <- %phi1, %phi2 + call.32 %r8 <- %r7, %arg4 + ret.32 %r8 * check-output-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/linear/logical.c b/validation/linear/logical.c new file mode 100644 index 00000000..148ad427 --- /dev/null +++ b/validation/linear/logical.c @@ -0,0 +1,259 @@ +struct S { + int :1; + signed int s:2; + unsigned int u:3; + long l; + double d; +}; + +int os(int i, struct S *b) { return i || b->s; } +int ou(int i, struct S *b) { return i || b->u; } +int ol(int i, struct S *b) { return i || b->l; } +int od(int i, struct S *b) { return i || b->d; } + +int as(int i, struct S *b) { return i && b->s; } +int au(int i, struct S *b) { return i && b->u; } +int al(int i, struct S *b) { return i && b->l; } +int ad(int i, struct S *b) { return i && b->d; } + +/* + * check-name: logical + * check-command: test-linearize -m64 -fdump-ir -Wno-decl $file + * + * check-output-start +os: +.L0: + <entry-point> + store.32 %arg1 -> 0[i] + store.64 %arg2 -> 0[b] + phisrc.32 %phi1 <- $1 + load.32 %r1 <- 0[i] + setne.1 %r2 <- %r1, $0 + cbr %r2, .L3, .L2 + +.L2: + load.64 %r3 <- 0[b] + load.32 %r4 <- 0[%r3] + lsr.32 %r5 <- %r4, $1 + trunc.2 %r6 <- (32) %r5 + setne.1 %r7 <- %r6, $0 + zext.32 %r8 <- (1) %r7 + phisrc.32 %phi2 <- %r8 + br .L3 + +.L3: + phi.32 %r9 <- %phi1, %phi2 + phisrc.32 %phi3(return) <- %r9 + br .L1 + +.L1: + phi.32 %r10 <- %phi3(return) + ret.32 %r10 + + +ou: +.L4: + <entry-point> + store.32 %arg1 -> 0[i] + store.64 %arg2 -> 0[b] + phisrc.32 %phi5 <- $1 + load.32 %r11 <- 0[i] + setne.1 %r12 <- %r11, $0 + cbr %r12, .L7, .L6 + +.L6: + load.64 %r13 <- 0[b] + load.32 %r14 <- 0[%r13] + lsr.32 %r15 <- %r14, $3 + trunc.3 %r16 <- (32) %r15 + setne.1 %r17 <- %r16, $0 + zext.32 %r18 <- (1) %r17 + phisrc.32 %phi6 <- %r18 + br .L7 + +.L7: + phi.32 %r19 <- %phi5, %phi6 + phisrc.32 %phi7(return) <- %r19 + br .L5 + +.L5: + phi.32 %r20 <- %phi7(return) + ret.32 %r20 + + +ol: +.L8: + <entry-point> + store.32 %arg1 -> 0[i] + store.64 %arg2 -> 0[b] + phisrc.32 %phi9 <- $1 + load.32 %r21 <- 0[i] + setne.1 %r22 <- %r21, $0 + cbr %r22, .L11, .L10 + +.L10: + load.64 %r23 <- 0[b] + load.64 %r24 <- 8[%r23] + setne.1 %r25 <- %r24, $0 + zext.32 %r26 <- (1) %r25 + phisrc.32 %phi10 <- %r26 + br .L11 + +.L11: + phi.32 %r27 <- %phi9, %phi10 + phisrc.32 %phi11(return) <- %r27 + br .L9 + +.L9: + phi.32 %r28 <- %phi11(return) + ret.32 %r28 + + +od: +.L12: + <entry-point> + store.32 %arg1 -> 0[i] + store.64 %arg2 -> 0[b] + phisrc.32 %phi13 <- $1 + load.32 %r29 <- 0[i] + setne.1 %r30 <- %r29, $0 + cbr %r30, .L15, .L14 + +.L14: + load.64 %r31 <- 0[b] + load.64 %r32 <- 16[%r31] + setfval.64 %r33 <- 0.000000e+00 + fcmpune.1 %r34 <- %r32, %r33 + zext.32 %r35 <- (1) %r34 + phisrc.32 %phi14 <- %r35 + br .L15 + +.L15: + phi.32 %r36 <- %phi13, %phi14 + phisrc.32 %phi15(return) <- %r36 + br .L13 + +.L13: + phi.32 %r37 <- %phi15(return) + ret.32 %r37 + + +as: +.L16: + <entry-point> + store.32 %arg1 -> 0[i] + store.64 %arg2 -> 0[b] + phisrc.32 %phi17 <- $0 + load.32 %r38 <- 0[i] + setne.1 %r39 <- %r38, $0 + cbr %r39, .L18, .L19 + +.L18: + load.64 %r40 <- 0[b] + load.32 %r41 <- 0[%r40] + lsr.32 %r42 <- %r41, $1 + trunc.2 %r43 <- (32) %r42 + setne.1 %r44 <- %r43, $0 + zext.32 %r45 <- (1) %r44 + phisrc.32 %phi18 <- %r45 + br .L19 + +.L19: + phi.32 %r46 <- %phi18, %phi17 + phisrc.32 %phi19(return) <- %r46 + br .L17 + +.L17: + phi.32 %r47 <- %phi19(return) + ret.32 %r47 + + +au: +.L20: + <entry-point> + store.32 %arg1 -> 0[i] + store.64 %arg2 -> 0[b] + phisrc.32 %phi21 <- $0 + load.32 %r48 <- 0[i] + setne.1 %r49 <- %r48, $0 + cbr %r49, .L22, .L23 + +.L22: + load.64 %r50 <- 0[b] + load.32 %r51 <- 0[%r50] + lsr.32 %r52 <- %r51, $3 + trunc.3 %r53 <- (32) %r52 + setne.1 %r54 <- %r53, $0 + zext.32 %r55 <- (1) %r54 + phisrc.32 %phi22 <- %r55 + br .L23 + +.L23: + phi.32 %r56 <- %phi22, %phi21 + phisrc.32 %phi23(return) <- %r56 + br .L21 + +.L21: + phi.32 %r57 <- %phi23(return) + ret.32 %r57 + + +al: +.L24: + <entry-point> + store.32 %arg1 -> 0[i] + store.64 %arg2 -> 0[b] + phisrc.32 %phi25 <- $0 + load.32 %r58 <- 0[i] + setne.1 %r59 <- %r58, $0 + cbr %r59, .L26, .L27 + +.L26: + load.64 %r60 <- 0[b] + load.64 %r61 <- 8[%r60] + setne.1 %r62 <- %r61, $0 + zext.32 %r63 <- (1) %r62 + phisrc.32 %phi26 <- %r63 + br .L27 + +.L27: + phi.32 %r64 <- %phi26, %phi25 + phisrc.32 %phi27(return) <- %r64 + br .L25 + +.L25: + phi.32 %r65 <- %phi27(return) + ret.32 %r65 + + +ad: +.L28: + <entry-point> + store.32 %arg1 -> 0[i] + store.64 %arg2 -> 0[b] + phisrc.32 %phi29 <- $0 + load.32 %r66 <- 0[i] + setne.1 %r67 <- %r66, $0 + cbr %r67, .L30, .L31 + +.L30: + load.64 %r68 <- 0[b] + load.64 %r69 <- 16[%r68] + setfval.64 %r70 <- 0.000000e+00 + fcmpune.1 %r71 <- %r69, %r70 + zext.32 %r72 <- (1) %r71 + phisrc.32 %phi30 <- %r72 + br .L31 + +.L31: + phi.32 %r73 <- %phi30, %phi29 + phisrc.32 %phi31(return) <- %r73 + br .L29 + +.L29: + phi.32 %r74 <- %phi31(return) + ret.32 %r74 + + + * check-output-end + */ diff --git a/validation/loop-linearization.c b/validation/loop-linearization.c index 25c6dfb8..051e0453 100644 --- a/validation/loop-linearization.c +++ b/validation/loop-linearization.c @@ -56,8 +56,8 @@ ffor: br .L7 .L2: - add.32 %r7 <- %r1(i), $1 - phisrc.32 %phi6(i) <- %r7 + add.32 %r8 <- %r1(i), $1 + phisrc.32 %phi6(i) <- %r8 br .L4 .L3: @@ -65,8 +65,8 @@ ffor: br .L7 .L7: - phi.32 %r5 <- %phi1(return), %phi2(return) - ret.32 %r5 + phi.32 %r6 <- %phi1(return), %phi2(return) + ret.32 %r6 fwhile: @@ -76,21 +76,21 @@ fwhile: br .L12 .L12: - phi.32 %r8(i) <- %phi11(i), %phi12(i) - setlt.32 %r9 <- %r8(i), $10 - cbr %r9, .L9, .L11 + phi.32 %r9(i) <- %phi11(i), %phi12(i) + setlt.32 %r10 <- %r9(i), $10 + cbr %r10, .L9, .L11 .L9: - call.32 %r11 <- p, %r8(i) - cbr %r11, .L14, .L13 + call.32 %r12 <- p, %r9(i) + cbr %r12, .L14, .L13 .L13: phisrc.32 %phi7(return) <- $0 br .L15 .L14: - add.32 %r14 <- %r8(i), $1 - phisrc.32 %phi12(i) <- %r14 + add.32 %r16 <- %r9(i), $1 + phisrc.32 %phi12(i) <- %r16 br .L12 .L11: @@ -98,8 +98,8 @@ fwhile: br .L15 .L15: - phi.32 %r12 <- %phi7(return), %phi8(return) - ret.32 %r12 + phi.32 %r14 <- %phi7(return), %phi8(return) + ret.32 %r14 fdo: @@ -109,27 +109,27 @@ fdo: br .L17 .L17: - phi.32 %r15(i) <- %phi16(i), %phi17(i) - call.32 %r16 <- p, %r15(i) - cbr %r16, .L18, .L20 + phi.32 %r17(i) <- %phi16(i), %phi17(i) + call.32 %r18 <- p, %r17(i) + cbr %r18, .L18, .L20 .L20: phisrc.32 %phi13(return) <- $0 br .L22 .L18: - add.32 %r19 <- %r15(i), $1 - setlt.32 %r20 <- %r15(i), $10 - phisrc.32 %phi17(i) <- %r19 - cbr %r20, .L17, .L19 + add.32 %r22 <- %r17(i), $1 + setlt.32 %r23 <- %r17(i), $10 + phisrc.32 %phi17(i) <- %r22 + cbr %r23, .L17, .L19 .L19: phisrc.32 %phi14(return) <- $1 br .L22 .L22: - phi.32 %r17 <- %phi13(return), %phi14(return) - ret.32 %r17 + phi.32 %r20 <- %phi13(return), %phi14(return) + ret.32 %r20 * check-output-end 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..439eb822 --- /dev/null +++ b/validation/optim/and-lsr.c @@ -0,0 +1,15 @@ +// (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-output-ignore + * check-output-contains: and\\..*\\$15 + * check-output-excludes: and\\..*\\$0xffff + */ diff --git a/validation/optim/and-or-bf0.c b/validation/optim/and-or-bf0.c new file mode 100644 index 00000000..cfaff4f2 --- /dev/null +++ b/validation/optim/and-or-bf0.c @@ -0,0 +1,24 @@ +struct s { + int f:3; +}; + +void foo(struct s *p, int a) +{ + p->f = 1; + p->f = a; +} + +void bar(struct s *p, int a) +{ + p->f = a; + p->f = 1; +} + +/* + * check-name: and-or-bf0 + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-pattern(3): and\\. + * check-output-pattern(2): or\\. + */ diff --git a/validation/optim/and-or-bf1.c b/validation/optim/and-or-bf1.c new file mode 100644 index 00000000..23477ff3 --- /dev/null +++ b/validation/optim/and-or-bf1.c @@ -0,0 +1,18 @@ +struct s { + int :2; + int f:3; +}; + +void foo(struct s *d, const struct s *s, int a) +{ + d->f = s->f | a; +} + +/* + * check-name: and-or-bf1 + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-pattern(2): and\\. + * check-output-pattern(2): or\\. + */ diff --git a/validation/optim/and-or-bf2.c b/validation/optim/and-or-bf2.c new file mode 100644 index 00000000..2296da12 --- /dev/null +++ b/validation/optim/and-or-bf2.c @@ -0,0 +1,27 @@ +struct s { + char a:3; + char b:3; + char c:2; +}; + +void foo(struct s *p) +{ + p->a = 1; + p->b = 2; + p->c = 3; +} + +/* + * check-name: and-or-bf2 + * check-command: test-linearize -Wno-decl $file + * + * check-output-start +foo: +.L0: + <entry-point> + store.8 $209 -> 0[%arg1] + ret + + + * check-output-end + */ diff --git a/validation/optim/and-or-bfs.c b/validation/optim/and-or-bfs.c new file mode 100644 index 00000000..f3f33204 --- /dev/null +++ b/validation/optim/and-or-bfs.c @@ -0,0 +1,23 @@ +struct s { + signed int :2; + signed int f:3; +}; + +int bfs(struct s s, int a) +{ + s.f = a; + return s.f; +} + +/* + * check-name: and-or-bfs + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-pattern(1): trunc\\. + * check-output-pattern(1): sext\\. + * check-output-excludes: and\\. + * check-output-excludes: or\\. + * check-output-excludes: shl\\. + * check-output-excludes: lsr\\. + */ diff --git a/validation/optim/and-or-bfu.c b/validation/optim/and-or-bfu.c new file mode 100644 index 00000000..b6a080bd --- /dev/null +++ b/validation/optim/and-or-bfu.c @@ -0,0 +1,21 @@ +struct u { + unsigned int :2; + unsigned int f:3; +}; + +int bfu(struct u s, int a) +{ + s.f = a; + return s.f; +} + +/* + * check-name: and-or-bfu + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-pattern(1): and\\. + * check-output-excludes: or\\. + * check-output-excludes: shl\\. + * check-output-excludes: lsr\\. + */ diff --git a/validation/optim/and-or-bfx.c b/validation/optim/and-or-bfx.c new file mode 100644 index 00000000..57a54cf5 --- /dev/null +++ b/validation/optim/and-or-bfx.c @@ -0,0 +1,18 @@ +struct s { + int f:3; +}; + +void foo(struct s *p, int a, int b) +{ + p->f = a; + p->f = b; +} + +/* + * check-name: and-or-bfx + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-pattern(2): and\\. + * check-output-pattern(1): or\\. + */ diff --git a/validation/optim/and-or-constant0.c b/validation/optim/and-or-constant0.c new file mode 100644 index 00000000..dcf440f3 --- /dev/null +++ b/validation/optim/and-or-constant0.c @@ -0,0 +1,12 @@ +int foo(int x) +{ + return (x | 0xfffff000) & 0xfff; +} + +/* + * check-name: and-or-constant0 + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-excludes: or\\. + */ diff --git a/validation/optim/and-or-constant1.c b/validation/optim/and-or-constant1.c new file mode 100644 index 00000000..49823d5c --- /dev/null +++ b/validation/optim/and-or-constant1.c @@ -0,0 +1,14 @@ +int foo(int x) +{ + return (x | 0x000fffff) & 0xfff; +} + +/* + * check-name: or-and-constant1 + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-contains: ret\\..*\\$0xfff + * check-output-excludes: and\\. + * check-output-excludes: or\\. + */ diff --git a/validation/optim/and-or-constant2.c b/validation/optim/and-or-constant2.c new file mode 100644 index 00000000..d7e66f9c --- /dev/null +++ b/validation/optim/and-or-constant2.c @@ -0,0 +1,13 @@ +int foo(int x) +{ + return (x | 0xfffffff0) & 0xfff; +} + +/* + * check-name: and-or-constant2 + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-contains: or\\..*\\$0xff0 + * check-output-excludes: or\\..*\\$0xfffffff0 + */ diff --git a/validation/optim/and-or-crash.c b/validation/optim/and-or-crash.c new file mode 100644 index 00000000..98a8a9b8 --- /dev/null +++ b/validation/optim/and-or-crash.c @@ -0,0 +1,5 @@ +static unsigned a(unsigned b, unsigned c) { (c << 1 | b & 1 << 1) >> 1; } + +/* + * check-name: catch crashes during AND-OR simplifications + */ diff --git a/validation/optim/and-or-lsr0.c b/validation/optim/and-or-lsr0.c new file mode 100644 index 00000000..227c5aed --- /dev/null +++ b/validation/optim/and-or-lsr0.c @@ -0,0 +1,13 @@ +int foo(int a, int b) +{ + return ((a & 0x00000fff) | b) >> 12; +} + +/* + * check-name: and-or-lsr0 + * check-command: test-linearize -Wno-decl $file + * check-known-to-fail + * + * check-output-ignore + * check-output-excludes: or\\. + */ diff --git a/validation/optim/and-or-lsr1.c b/validation/optim/and-or-lsr1.c new file mode 100644 index 00000000..bd1dbc8a --- /dev/null +++ b/validation/optim/and-or-lsr1.c @@ -0,0 +1,13 @@ +int foo(int a, int b) +{ + return ((a & 0xfffff000) | b) >> 12; +} + +/* + * check-name: and-or-lsr1 + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-pattern(0): and\\. + * check-output-pattern(1): or\\. + */ diff --git a/validation/optim/and-or-lsr2.c b/validation/optim/and-or-lsr2.c new file mode 100644 index 00000000..d1af0135 --- /dev/null +++ b/validation/optim/and-or-lsr2.c @@ -0,0 +1,13 @@ +int foo(int x, int y) +{ + return ((x & 0xf0ffffff) | y) >> 12; +} + +/* + * check-name: and-or-lsr2 + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-contains: and\\..*\\$0xf0fff + * check-output-excludes: and\\..*\\$0xf0ffffff + */ diff --git a/validation/optim/and-or-lsrx.c b/validation/optim/and-or-lsrx.c new file mode 100644 index 00000000..31adca92 --- /dev/null +++ b/validation/optim/and-or-lsrx.c @@ -0,0 +1,13 @@ +unsigned int foo(unsigned int x, unsigned int y, unsigned int a) +{ + return ((x & y) | (a & 0x0fff)) >> 12; +} + +/* + * check-name: and-or-lsrx + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-pattern(1): and\\. + * check-output-excludes: or\\. + */ diff --git a/validation/optim/and-or-mask.c b/validation/optim/and-or-mask.c new file mode 100644 index 00000000..46803789 --- /dev/null +++ b/validation/optim/and-or-mask.c @@ -0,0 +1,18 @@ +int foo(int a, int b) +{ + return ((a & 7) | (b & 3)) & 8; +} + +/* + * check-name: and-or-mask + * check-command: test-linearize -Wno-decl $file + * + * check-output-start +foo: +.L0: + <entry-point> + ret.32 $0 + + + * check-output-end + */ diff --git a/validation/optim/and-or-mask0.c b/validation/optim/and-or-mask0.c new file mode 100644 index 00000000..2d2245ea --- /dev/null +++ b/validation/optim/and-or-mask0.c @@ -0,0 +1,12 @@ +int foo(int a, int b) +{ + return ((a & 0xfffff000) | b) & 0xfff; +} + +/* + * check-name: and-or-mask0 + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-excludes: or\\. + */ diff --git a/validation/optim/and-or-mask1.c b/validation/optim/and-or-mask1.c new file mode 100644 index 00000000..bff3a89f --- /dev/null +++ b/validation/optim/and-or-mask1.c @@ -0,0 +1,13 @@ +int foo(int a, int b) +{ + return ((a & 0x0fffffff) | b) & 0xfff; +} + +/* + * check-name: and-or-mask1 + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-pattern(1): and\\. + * check-output-pattern(1): or\\. + */ diff --git a/validation/optim/and-or-mask2.c b/validation/optim/and-or-mask2.c new file mode 100644 index 00000000..cab802a6 --- /dev/null +++ b/validation/optim/and-or-mask2.c @@ -0,0 +1,13 @@ +int foo(int x, int y) +{ + return ((x & 0xffffff0f) | y) & 0xfff; +} + +/* + * check-name: and-or-mask2 + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-contains: and\\..*\\$0xf0f + * check-output-excludes: and\\..*\\$0xffffff0f + */ diff --git a/validation/optim/and-or-mask3s.c b/validation/optim/and-or-mask3s.c new file mode 100644 index 00000000..cf264723 --- /dev/null +++ b/validation/optim/and-or-mask3s.c @@ -0,0 +1,25 @@ +#define W 3 +#define S 8 +#define M (W << S) + +static inline int fun(unsigned int x, unsigned int y) +{ + return ((x & M) | (y << S)) >> S; +} + +short foo(unsigned int x, unsigned int y) +{ + return fun(x, y) & W; +} + +/* + * check-name: and-or-mask3s + * check-command: test-linearize -Wno-decl $file + * check-known-to-fail + * + * check-output-ignore + * check-output-pattern(1): lsr\\. + * check-output-pattern(1): or\\. + * check-output-pattern(1): and\\. + * check-output-excludes: shl\\. + */ diff --git a/validation/optim/and-or-mask3u.c b/validation/optim/and-or-mask3u.c new file mode 100644 index 00000000..c5b6c5fa --- /dev/null +++ b/validation/optim/and-or-mask3u.c @@ -0,0 +1,25 @@ +#define W 3 +#define S 8 +#define M (W << S) + +static inline int fun(unsigned int x, unsigned int y) +{ + return ((x & M) | (y << S)) >> S; +} + +int foo(unsigned int x, unsigned int y) +{ + return fun(x, y) & W; +} + +/* + * check-name: and-or-mask3u + * check-command: test-linearize -Wno-decl $file + * check-known-to-fail + * + * check-output-ignore + * check-output-pattern(1): lsr\\. + * check-output-pattern(1): or\\. + * check-output-pattern(1): and\\. + * check-output-excludes: shl\\. + */ diff --git a/validation/optim/and-or-mask4.c b/validation/optim/and-or-mask4.c new file mode 100644 index 00000000..1c4bc01a --- /dev/null +++ b/validation/optim/and-or-mask4.c @@ -0,0 +1,25 @@ +#define W 3 +#define S 8 +#define M (W << S) + +static inline int fun(unsigned int x, unsigned int y) +{ + return ((x & W) | (y >> S)) << S; +} + +int foo(unsigned int x, unsigned int y) +{ + return fun(x, y) & M; +} + +/* + * check-name: and-or-mask4 + * check-command: test-linearize -Wno-decl $file + * check-known-to-fail + * + * check-output-ignore + * check-output-pattern(1): shl\\. + * check-output-pattern(1): or\\. + * check-output-pattern(1): and\\. + * check-output-excludes: lsr\\. + */ diff --git a/validation/optim/and-or-maskx.c b/validation/optim/and-or-maskx.c new file mode 100644 index 00000000..21d44e8d --- /dev/null +++ b/validation/optim/and-or-maskx.c @@ -0,0 +1,13 @@ +int foo(int x, int y, int a) +{ + return ((x & y) | (a & 0xf000)) & 0x0fff; +} + +/* + * check-name: and-or-maskx + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-pattern(2): and\\. + * check-output-excludes: or\\. + */ diff --git a/validation/optim/and-or-shl0.c b/validation/optim/and-or-shl0.c new file mode 100644 index 00000000..4850b326 --- /dev/null +++ b/validation/optim/and-or-shl0.c @@ -0,0 +1,12 @@ +int foo(int a, int b) +{ + return ((a & 0xfff00000) | b) << 12; +} + +/* + * check-name: and-or-shl0 + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-excludes: or\\. + */ diff --git a/validation/optim/and-or-shl1.c b/validation/optim/and-or-shl1.c new file mode 100644 index 00000000..bea22245 --- /dev/null +++ b/validation/optim/and-or-shl1.c @@ -0,0 +1,13 @@ +int foo(int a, int b) +{ + return ((a & 0x000fffff) | b) << 12; +} + +/* + * check-name: and-or-shl1 + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-pattern(0): and\\. + * check-output-pattern(1): or\\. + */ diff --git a/validation/optim/and-or-shl2.c b/validation/optim/and-or-shl2.c new file mode 100644 index 00000000..f5f62758 --- /dev/null +++ b/validation/optim/and-or-shl2.c @@ -0,0 +1,13 @@ +int foo(int x, int y) +{ + return ((x & 0xffffff0f) | y) << 12; +} + +/* + * check-name: and-or-shl2 + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-contains: and\\..*\\$0xfff0f + * check-output-excludes: and\\..*\\$0xffffff0f + */ diff --git a/validation/optim/and-or-shlx.c b/validation/optim/and-or-shlx.c new file mode 100644 index 00000000..ec2a2ced --- /dev/null +++ b/validation/optim/and-or-shlx.c @@ -0,0 +1,13 @@ +unsigned int foo(unsigned int x, unsigned int y, unsigned int a) +{ + return ((x & y) | (a & 0xfff00000)) << 12; +} + +/* + * check-name: and-or-shlx + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-pattern(1): and\\. + * check-output-excludes: or\\. + */ diff --git a/validation/optim/and-or-trunc0.c b/validation/optim/and-or-trunc0.c new file mode 100644 index 00000000..3d326b6a --- /dev/null +++ b/validation/optim/and-or-trunc0.c @@ -0,0 +1,13 @@ +char foo(int x, int y) +{ + return (x & 0xff00) | y; +} + +/* + * check-name: and-or-trunc0 + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-excludes: and\\. + * check-output-excludes: or\\. + */ diff --git a/validation/optim/and-or-trunc1.c b/validation/optim/and-or-trunc1.c new file mode 100644 index 00000000..6bbe8a8c --- /dev/null +++ b/validation/optim/and-or-trunc1.c @@ -0,0 +1,12 @@ +char foo(int x, int y) +{ + return (x & 0xffff) | y; +} + +/* + * check-name: and-or-trunc1 + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-excludes: and\\. + */ diff --git a/validation/optim/and-or-trunc2.c b/validation/optim/and-or-trunc2.c new file mode 100644 index 00000000..e66c1f14 --- /dev/null +++ b/validation/optim/and-or-trunc2.c @@ -0,0 +1,13 @@ +char foo(int x, int y) +{ + return (x & 0xff07) | y; +} + +/* + * check-name: and-or-trunc2 + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-pattern(1): and\\. + * check-output-pattern(1): and\\..*\\$7 + */ diff --git a/validation/optim/and-or-truncx.c b/validation/optim/and-or-truncx.c new file mode 100644 index 00000000..ef8249a1 --- /dev/null +++ b/validation/optim/and-or-truncx.c @@ -0,0 +1,13 @@ +char foo(int x, int y, int b) +{ + return (x & y) | (b & 0xff00); +} + +/* + * check-name: and-or-truncx + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-pattern(1): and\\. + * check-output-excludes: or\\. + */ 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/bitfield-store-load0.c b/validation/optim/bitfield-store-load0.c new file mode 100644 index 00000000..f68cb600 --- /dev/null +++ b/validation/optim/bitfield-store-load0.c @@ -0,0 +1,44 @@ +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 %r11 <- %arg1, $7 + ret.32 %r11 + + +sfoo: +.L2: + <entry-point> + trunc.3 %r16 <- (32) %arg1 + sext.32 %r23 <- (3) %r16 + ret.32 %r23 + + + * check-output-end + */ diff --git a/validation/optim/bitfield-store-loads.c b/validation/optim/bitfield-store-loads.c new file mode 100644 index 00000000..dc625131 --- /dev/null +++ b/validation/optim/bitfield-store-loads.c @@ -0,0 +1,23 @@ +struct s { + char :2; + char f:3; +}; + +int foo(struct s s, int a) +{ + s.f = a; + return s.f; +} + +/* + * check-name: bitfield-store-load signed + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-excludes: shl\\. + * check-output-excludes: lsr\\. + * check-output-excludes: or\\. + * check-output-excludes: [sz]ext\\. + * check-output-excludes: trunc\\. + * check-output-pattern(1): and\\. + */ diff --git a/validation/optim/bitfield-store-loadu.c b/validation/optim/bitfield-store-loadu.c new file mode 100644 index 00000000..7fa1593d --- /dev/null +++ b/validation/optim/bitfield-store-loadu.c @@ -0,0 +1,21 @@ +struct s { + unsigned int :2; + unsigned int f:3; +}; + +int foo(struct s s, int a) +{ + s.f = a; + return s.f; +} + +/* + * check-name: bitfield-store-load unsigned + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-excludes: shl\\. + * check-output-excludes: lsr\\. + * check-output-excludes: or\\. + * check-output-pattern(1): and\\. + */ diff --git a/validation/optim/bool-context-fp.c b/validation/optim/bool-context-fp.c index 50e96825..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,18 +50,17 @@ 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 - setne.1 %r26 <- %r24, $0 - ret.1 %r26 + ret.1 %r24 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 @@ -72,18 +71,17 @@ 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 - setne.1 %r45 <- %r43, $0 - ret.1 %r45 + ret.1 %r43 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/bool-sext-test.c b/validation/optim/bool-sext-test.c new file mode 100644 index 00000000..bd85e06e --- /dev/null +++ b/validation/optim/bool-sext-test.c @@ -0,0 +1,12 @@ +_Bool eqs0( signed char a) { return a == 0; } +_Bool eqs1( signed char a) { return a == 1; } +_Bool nes0( signed char a) { return a != 0; } +_Bool nes1( signed char a) { return a != 1; } + +/* + * check-name: bool-sext-test + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-excludes: sext\\. + */ diff --git a/validation/optim/bool-simplify.c b/validation/optim/bool-simplify.c index 68aabb78..fe8ce88b 100644 --- a/validation/optim/bool-simplify.c +++ b/validation/optim/bool-simplify.c @@ -43,17 +43,15 @@ and_0: and_1: .L2: <entry-point> - setne.1 %r8 <- %arg1, $0 - zext.32 %r11 <- (1) %r8 - ret.32 %r11 + setne.32 %r9 <- %arg1, $0 + ret.32 %r9 or_0: .L4: <entry-point> - setne.1 %r14 <- %arg1, $0 - zext.32 %r17 <- (1) %r14 - ret.32 %r17 + setne.32 %r14 <- %arg1, $0 + ret.32 %r14 or_1: @@ -65,9 +63,8 @@ or_1: and_2: .L8: <entry-point> - setne.1 %r26 <- %arg1, $0 - zext.32 %r29 <- (1) %r26 - ret.32 %r29 + setne.32 %r25 <- %arg1, $0 + ret.32 %r25 or_2: diff --git a/validation/optim/bool-simplify2.c b/validation/optim/bool-simplify2.c index c94b4412..a089fe62 100644 --- a/validation/optim/bool-simplify2.c +++ b/validation/optim/bool-simplify2.c @@ -27,7 +27,7 @@ static bool babbb(bool a, bool b, bool c) { return a && b && c; } * check-name: bool-simplify2 * check-command: test-linearize $file * - * check-output-pattern(36): setne\\. + * check-output-pattern(20): setne\\. * check-output-pattern(4): seteq\\. * check-output-pattern(8): zext\\. * check-output-pattern(12): and @@ -75,8 +75,7 @@ boii: setne.1 %r23 <- %arg1, $0 setne.1 %r25 <- %arg2, $0 or.1 %r26 <- %r23, %r25 - setne.1 %r28 <- %r26, $0 - ret.1 %r28 + ret.1 %r26 baii: @@ -85,8 +84,7 @@ baii: setne.1 %r31 <- %arg1, $0 setne.1 %r33 <- %arg2, $0 and.1 %r34 <- %r31, %r33 - setne.1 %r36 <- %r34, $0 - ret.1 %r36 + ret.1 %r34 ioiii: @@ -95,9 +93,8 @@ ioiii: setne.1 %r39 <- %arg1, $0 setne.1 %r41 <- %arg2, $0 or.1 %r42 <- %r39, %r41 - setne.1 %r44 <- %r42, $0 setne.1 %r46 <- %arg3, $0 - or.1 %r47 <- %r44, %r46 + or.1 %r47 <- %r42, %r46 zext.32 %r48 <- (1) %r47 ret.32 %r48 @@ -108,9 +105,8 @@ iaiii: setne.1 %r51 <- %arg1, $0 setne.1 %r53 <- %arg2, $0 and.1 %r54 <- %r51, %r53 - setne.1 %r56 <- %r54, $0 setne.1 %r58 <- %arg3, $0 - and.1 %r59 <- %r56, %r58 + and.1 %r59 <- %r54, %r58 zext.32 %r60 <- (1) %r59 ret.32 %r60 @@ -121,11 +117,9 @@ boiii: setne.1 %r63 <- %arg1, $0 setne.1 %r65 <- %arg2, $0 or.1 %r66 <- %r63, %r65 - setne.1 %r68 <- %r66, $0 setne.1 %r70 <- %arg3, $0 - or.1 %r71 <- %r68, %r70 - setne.1 %r73 <- %r71, $0 - ret.1 %r73 + or.1 %r71 <- %r66, %r70 + ret.1 %r71 baiii: @@ -134,11 +128,9 @@ baiii: setne.1 %r76 <- %arg1, $0 setne.1 %r78 <- %arg2, $0 and.1 %r79 <- %r76, %r78 - setne.1 %r81 <- %r79, $0 setne.1 %r83 <- %arg3, $0 - and.1 %r84 <- %r81, %r83 - setne.1 %r86 <- %r84, $0 - ret.1 %r86 + and.1 %r84 <- %r79, %r83 + ret.1 %r84 inb: @@ -175,24 +167,21 @@ bobb: .L28: <entry-point> or.1 %r107 <- %arg1, %arg2 - setne.1 %r109 <- %r107, $0 - ret.1 %r109 + ret.1 %r107 babb: .L30: <entry-point> and.1 %r113 <- %arg1, %arg2 - setne.1 %r115 <- %r113, $0 - ret.1 %r115 + ret.1 %r113 iobbb: .L32: <entry-point> or.1 %r119 <- %arg1, %arg2 - setne.1 %r121 <- %r119, $0 - or.1 %r123 <- %r121, %arg3 + or.1 %r123 <- %r119, %arg3 zext.32 %r124 <- (1) %r123 ret.32 %r124 @@ -201,8 +190,7 @@ iabbb: .L34: <entry-point> and.1 %r128 <- %arg1, %arg2 - setne.1 %r130 <- %r128, $0 - and.1 %r132 <- %r130, %arg3 + and.1 %r132 <- %r128, %arg3 zext.32 %r133 <- (1) %r132 ret.32 %r133 @@ -211,20 +199,16 @@ bobbb: .L36: <entry-point> or.1 %r137 <- %arg1, %arg2 - setne.1 %r139 <- %r137, $0 - or.1 %r141 <- %r139, %arg3 - setne.1 %r143 <- %r141, $0 - ret.1 %r143 + or.1 %r141 <- %r137, %arg3 + ret.1 %r141 babbb: .L38: <entry-point> and.1 %r147 <- %arg1, %arg2 - setne.1 %r149 <- %r147, $0 - and.1 %r151 <- %r149, %arg3 - setne.1 %r153 <- %r151, $0 - ret.1 %r153 + and.1 %r151 <- %r147, %arg3 + ret.1 %r151 * check-output-end diff --git a/validation/optim/bool-zext-test.c b/validation/optim/bool-zext-test.c new file mode 100644 index 00000000..138938b0 --- /dev/null +++ b/validation/optim/bool-zext-test.c @@ -0,0 +1,12 @@ +_Bool equ0(unsigned char a) { return a == 0; } +_Bool equ1(unsigned char a) { return a == 1; } +_Bool neu0(unsigned char a) { return a != 0; } +_Bool neu1(unsigned char a) { return a != 1; } + +/* + * check-name: bool-zext-test + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-excludes: zext\\. + */ diff --git a/validation/optim/call-inlined.c b/validation/optim/call-inlined.c index 00698a4b..f21b3294 100644 --- a/validation/optim/call-inlined.c +++ b/validation/optim/call-inlined.c @@ -22,8 +22,8 @@ int foo(int a, int b, int p) foo: .L0: <entry-point> - select.32 %r9 <- %arg3, %arg3, $0 - ret.32 %r9 + select.32 %r10 <- %arg3, %arg3, $0 + ret.32 %r10 * check-output-end diff --git a/validation/linear/cast-kinds.c b/validation/optim/cast-kinds.c index 5df307bc..b144dc7e 100644 --- a/validation/linear/cast-kinds.c +++ b/validation/optim/cast-kinds.c @@ -88,8 +88,7 @@ vptr_2_int: iptr_2_int: .L8: <entry-point> - ptrtu.64 %r13 <- (64) %arg1 - trunc.32 %r14 <- (64) %r13 + trunc.32 %r14 <- (64) %arg1 ret.32 %r14 @@ -137,8 +136,7 @@ vptr_2_uint: iptr_2_uint: .L22: <entry-point> - ptrtu.64 %r34 <- (64) %arg1 - trunc.32 %r35 <- (64) %r34 + trunc.32 %r35 <- (64) %arg1 ret.32 %r35 @@ -185,8 +183,7 @@ vptr_2_long: iptr_2_long: .L36: <entry-point> - ptrtu.64 %r54 <- (64) %arg1 - ret.64 %r54 + ret.64 %arg1 float_2_long: @@ -232,8 +229,7 @@ vptr_2_ulong: iptr_2_ulong: .L50: <entry-point> - ptrtu.64 %r73 <- (64) %arg1 - ret.64 %r73 + ret.64 %arg1 float_2_ulong: @@ -286,30 +282,26 @@ int_2_iptr: .L66: <entry-point> sext.64 %r94 <- (32) %arg1 - utptr.64 %r95 <- (64) %r94 - ret.64 %r95 + ret.64 %r94 uint_2_iptr: .L68: <entry-point> zext.64 %r98 <- (32) %arg1 - utptr.64 %r99 <- (64) %r98 - ret.64 %r99 + ret.64 %r98 long_2_iptr: .L70: <entry-point> - utptr.64 %r102 <- (64) %arg1 - ret.64 %r102 + ret.64 %arg1 ulong_2_iptr: .L72: <entry-point> - utptr.64 %r105 <- (64) %arg1 - ret.64 %r105 + ret.64 %arg1 vptr_2_iptr: diff --git a/validation/optim/cast-nop.c b/validation/optim/cast-nop.c new file mode 100644 index 00000000..7741b7a7 --- /dev/null +++ b/validation/optim/cast-nop.c @@ -0,0 +1,18 @@ +static long p2l(long *p) +{ + return (long) p; +} + +static long *l2p(long l) +{ + return (long*)l; +} + +/* + * check-name: cast-nop + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-excludes: utptr\\. + * check-output-excludes: ptrtu\\. + */ diff --git a/validation/optim/cse-cmp-next.c b/validation/optim/cse-cmp-next.c new file mode 100644 index 00000000..50fdbac0 --- /dev/null +++ b/validation/optim/cse-cmp-next.c @@ -0,0 +1,15 @@ +void foo(int p, int i, int f, int *ref, int *dst, int *src) +{ + if (p) + f = ref[i]; + if (f) + dst[i] = src[i]; +} + +/* + * check-name: cse-cmp-next + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-pattern(1,2): mul\\. + */ 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-and0.c b/validation/optim/lsr-and0.c new file mode 100644 index 00000000..94310ba8 --- /dev/null +++ b/validation/optim/lsr-and0.c @@ -0,0 +1,13 @@ +unsigned lsr_and0(unsigned x) +{ + unsigned t = (x & 0x00000fff); + return (t >> 12) & t; +} + +/* + * check-name: lsr-and0 + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-contains: ret\\..*\\$0$ + */ diff --git a/validation/optim/lsr-and1.c b/validation/optim/lsr-and1.c new file mode 100644 index 00000000..393679e3 --- /dev/null +++ b/validation/optim/lsr-and1.c @@ -0,0 +1,18 @@ +// If (t >> S) is simplified into (x >> S) +// then the whole expression will be 0. +// The test is only interesting if the sub-expression +// (x & M) is referenced more than once +// (because otherwise other simplifications apply). +unsigned lsr_and1(unsigned x) +{ + unsigned t = (x & 0xfffff000); + return ((t >> 12) ^ (x >> 12)) & t; +} + +/* + * check-name: lsr-and1 + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-contains: ret\\..*\\$0$ + */ 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/lsr-shl0.c b/validation/optim/lsr-shl0.c new file mode 100644 index 00000000..952baa6b --- /dev/null +++ b/validation/optim/lsr-shl0.c @@ -0,0 +1,14 @@ +unsigned mask(unsigned x) +{ + return (x << 15) >> 15; +} + +/* + * check-name: lsr-shl0 + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-contains: and\\..*0x1ffff + * check-output-excludes: lsr\\. + * check-output-excludes: shl\\. + */ diff --git a/validation/optim/mask-lsr.c b/validation/optim/mask-lsr.c new file mode 100644 index 00000000..ec636444 --- /dev/null +++ b/validation/optim/mask-lsr.c @@ -0,0 +1,14 @@ +// ((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-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..ac85aec8 --- /dev/null +++ b/validation/optim/mask-out.c @@ -0,0 +1,12 @@ +unsigned mask(unsigned a, unsigned b) +{ + return ((a & 0xffff0000) | b) & 0x0000ffff; +} + +/* + * check-name: mask-out + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-excludes: %arg1 + */ diff --git a/validation/optim/mask1-setne0.c b/validation/optim/mask1-setne0.c new file mode 100644 index 00000000..1e599dc8 --- /dev/null +++ b/validation/optim/mask1-setne0.c @@ -0,0 +1,28 @@ +struct s { + unsigned i:1; +}; + +int foo(struct s x) +{ + unsigned int i = x.i; + + if (i == 0) + return 1; + else if (i == 1) + return 1; + return 0; +} + +/* + * check-name: mask1-setne0 + * check-command: test-linearize -Wno-decl $file + * + * check-output-start +foo: +.L0: + <entry-point> + ret.32 $1 + + + * check-output-end + */ diff --git a/validation/optim/or-and-constant1.c b/validation/optim/or-and-constant1.c new file mode 100644 index 00000000..aa673b90 --- /dev/null +++ b/validation/optim/or-and-constant1.c @@ -0,0 +1,29 @@ +unsigned int and_or_equ(unsigned int a) +{ + return (a | 3) & 3; +} + +int and_or_eqs(int a) +{ + return (a | 3) & 3; +} + +unsigned int or_and_equ(unsigned int a) +{ + return (a & 3) | 3; +} + +int or_and_eqs(int a) +{ + return (a & 3) | 3; +} + +/* + * check-name: or-and-constant1 + * check-command: test-linearize -Wno-decl $file + * check-known-to-fail + * + * check-output-ignore + * check-output-pattern(4): ret\\..*\\$3 + * check-output-excludes: or\\. + */ diff --git a/validation/optim/setcc-mask.c b/validation/optim/setcc-mask.c new file mode 100644 index 00000000..5d271788 --- /dev/null +++ b/validation/optim/setcc-mask.c @@ -0,0 +1,18 @@ +int foo (int a) +{ + return ((a == 0) & 1) == (a == 0); +} + +/* + * check-name: setcc-mask + * check-command: test-linearize -Wno-decl $file + * + * check-output-start +foo: +.L0: + <entry-point> + ret.32 $1 + + + * check-output-end + */ diff --git a/validation/optim/setne0-sext.c b/validation/optim/setne0-sext.c new file mode 100644 index 00000000..4167979b --- /dev/null +++ b/validation/optim/setne0-sext.c @@ -0,0 +1,9 @@ +long foo(int a) { return a != 0; } + +/* + * check-name: setne0-sext + * check-command: test-linearize -m64 -Wno-decl $file + * + * check-output-ignore + * check-output-excludes: sext\\. + */ diff --git a/validation/optim/setne0-trunc.c b/validation/optim/setne0-trunc.c new file mode 100644 index 00000000..6c5494ec --- /dev/null +++ b/validation/optim/setne0-trunc.c @@ -0,0 +1,9 @@ +char foo(int a) { return a != 0; } + +/* + * check-name: setne0-trunc + * check-command: test-linearize -m64 -Wno-decl $file + * + * check-output-ignore + * check-output-excludes: trunc\\. + */ diff --git a/validation/optim/setne0-zext.c b/validation/optim/setne0-zext.c new file mode 100644 index 00000000..8a074f03 --- /dev/null +++ b/validation/optim/setne0-zext.c @@ -0,0 +1,9 @@ +unsigned long foo(int a) { return (unsigned int) (a != 0); } + +/* + * check-name: setne0-zext + * check-command: test-linearize -m64 -Wno-decl $file + * + * check-output-ignore + * check-output-excludes: zext\\. + */ 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/sh-or-and0.c b/validation/optim/sh-or-and0.c new file mode 100644 index 00000000..02f0cb03 --- /dev/null +++ b/validation/optim/sh-or-and0.c @@ -0,0 +1,20 @@ +unsigned lsr_or_and0(unsigned x, unsigned b) +{ + return (((x & 0x00000fff) | b) >> 12); +} + +unsigned shl_or_and0(unsigned x, unsigned b) +{ + return (((x & 0xfff00000) | b) << 12); +} + +/* + * check-name: sh-or-and0 + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-pattern(1): lsr\\. + * check-output-pattern(1): shl\\. + * check-output-excludes: or\\. + * check-output-excludes: and\\. + */ diff --git a/validation/optim/sh-or-and1.c b/validation/optim/sh-or-and1.c new file mode 100644 index 00000000..7b79bbf3 --- /dev/null +++ b/validation/optim/sh-or-and1.c @@ -0,0 +1,20 @@ +unsigned lsr_or_and1(unsigned x, unsigned b) +{ + return (((x & 0xfffff000) | b) >> 12); +} + +unsigned shl_or_and1(unsigned x, unsigned b) +{ + return (((x & 0x000fffff) | b) << 12); +} + +/* + * check-name: sh-or-and1 + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-pattern(1): lsr\\. + * check-output-pattern(1): shl\\. + * check-output-pattern(2): or\\. + * check-output-excludes: and\\. + */ diff --git a/validation/optim/sh-or-and2.c b/validation/optim/sh-or-and2.c new file mode 100644 index 00000000..241aeaff --- /dev/null +++ b/validation/optim/sh-or-and2.c @@ -0,0 +1,21 @@ +unsigned lsr_or_and2(unsigned x, unsigned b) +{ + return (((x & 0xf0ffffff) | b) >> 12); +} + +unsigned shl_or_and2(unsigned x, unsigned b) +{ + return (((x & 0xffffff0f) | b) << 12); +} + +/* + * check-name: sh-or-and2 + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-pattern(1): lsr\\. + * check-output-pattern(1): shl\\. + * check-output-pattern(2): or\\. + * check-output-pattern(1): and\\..*\\$0xf0fff000 + * check-output-pattern(1): and\\..*\\$0xfff0f + */ 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..30409bec --- /dev/null +++ b/validation/optim/shift-zext.c @@ -0,0 +1,12 @@ +unsigned int foo(unsigned int x) +{ + return (x << 20) >> 20; +} + +/* + * check-name: shift-zext + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-contains: and\\..*%arg1, \\$0xfff + */ diff --git a/validation/optim/shl-and0.c b/validation/optim/shl-and0.c new file mode 100644 index 00000000..894bd882 --- /dev/null +++ b/validation/optim/shl-and0.c @@ -0,0 +1,13 @@ +unsigned shl_and0(unsigned x) +{ + unsigned t = (x & 0xfff00000); + return (t << 12) & t; +} + +/* + * check-name: shl-and0 + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-contains: ret\\..*\\$0$ + */ diff --git a/validation/optim/shl-and1.c b/validation/optim/shl-and1.c new file mode 100644 index 00000000..13a1675b --- /dev/null +++ b/validation/optim/shl-and1.c @@ -0,0 +1,18 @@ +// If (t << S) is simplified into (x << S) +// then the whole expression will be 0. +// The test is only interesting if the sub-expression +// (x & M) is referenced more than once +// (because otherwise other simplifications apply). +unsigned shl_and1(unsigned x) +{ + unsigned t = (x & 0x000fffff); + return ((t << 12) ^ (x << 12)) & t; +} + +/* + * check-name: shl-and1 + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-contains: ret\\..*\\$0$ + */ diff --git a/validation/optim/shl-lsr0.c b/validation/optim/shl-lsr0.c new file mode 100644 index 00000000..fc2561bd --- /dev/null +++ b/validation/optim/shl-lsr0.c @@ -0,0 +1,14 @@ +unsigned mask(unsigned x) +{ + return (x >> 15) << 15; +} + +/* + * check-name: shl-lsr0 + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-contains: and\\..*0xffff8000 + * check-output-excludes: lsr\\. + * check-output-excludes: shl\\. + */ 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/trunc-or-shl.c b/validation/optim/trunc-or-shl.c new file mode 100644 index 00000000..70d8bd1d --- /dev/null +++ b/validation/optim/trunc-or-shl.c @@ -0,0 +1,13 @@ +char foo(int a, int b) +{ + return (a << 8) | b; +} + +/* + * check-name: trunc-or-shl + * check-command: test-linearize -Wno-decl $file + * check-known-to-fail + * + * check-output-ignore + * check-output-contains: ret\\..*%arg2 + */ diff --git a/validation/optim/trunc-seteq0.c b/validation/optim/trunc-seteq0.c new file mode 100644 index 00000000..5994b17c --- /dev/null +++ b/validation/optim/trunc-seteq0.c @@ -0,0 +1,18 @@ +struct S { + int :1; + signed int s:2; + unsigned int u:3; +}; + +int os(int i, struct S *b) { return i || b->s; } +int ou(int i, struct S *b) { return i || b->u; } + +/* + * check-name: trunc-seteq0 + * 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/trunc-setne0.c b/validation/optim/trunc-setne0.c new file mode 100644 index 00000000..878c05fa --- /dev/null +++ b/validation/optim/trunc-setne0.c @@ -0,0 +1,20 @@ +struct s { + unsigned int u:1; +}; + +unsigned int foo(struct s x) +{ + if (x.u) + return 1; + else + return 0; +} + +/* + * check-name: trunc-setne0 + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-contains: and\\. + * check-output-excludes: trunc\\. + */ diff --git a/validation/optim/trunc-trunc.c b/validation/optim/trunc-trunc.c new file mode 100644 index 00000000..6dc50aee --- /dev/null +++ b/validation/optim/trunc-trunc.c @@ -0,0 +1,12 @@ +char foo(int a) +{ + return ((((short) a) + 1) - 1); +} + +/* + * check-name: trunc-trunc + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-pattern(1): trunc\\. + */ 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 + */ |
