diff options
31 files changed, 561 insertions, 117 deletions
diff --git a/Documentation/TODO.md b/Documentation/TODO.md index e2043e48..4dc9e63a 100644 --- a/Documentation/TODO.md +++ b/Documentation/TODO.md @@ -4,13 +4,13 @@ TODO Essential --------- * SSA is broken by simplify_loads() & branches rewriting/simplification -* attributes of struct, union & enums are ignored (and possibly in other - cases too). -* add support for bitwise enums +* attributes of struct, union & enums are ignored (and maybe others too). + This requires correct support for __packed which itself needs partial + and unaligned loads & stores (wip) +* add support for bitwise enums (wip) Documentation ------------- -* document the extensions * document the API * document the limitations of modifying ptrlists during list walking * document the data structures @@ -27,7 +27,7 @@ Core Testsuite --------- -* there are more than 50 failing tests. They should be fixed +* there are 60 failing tests. They should be fixed (but most are non-trivial to fix). Misc @@ -36,15 +36,26 @@ Misc * parse __attribute_((fallthrough)) * add support for format(printf()) (WIP by Ben Dooks) * make use of UNDEFs (issues warnings, simplification, ... ?) -* add a pass to inline small functions during simplification. +* make memory accesses more explicit: add EXPR_ACCESS (wip) +* it would be nice to do our own parsing of floating point (wip) +* some header files needed for crypto/ need __vector or __fp16 +* some even need __complex Optimization ------------ +* a lot of small simplifications are waiting to be upstreamed +* the domtree need to be rebuilt (or updated) +* critical edges need to be split * the current way of doing CSE uses a lot of time * add SSA based DCE * add SSA based PRE * Add SSA based SCCP +* add a pass to inline small functions during simplification. * use better/more systematic use of internal verification framework +* tracking of operands size should be improved (WIP) +* OP_INLINE is sometimes in the way +* would be nice to strictly separate phases that don't changes the + CFG and thus the dominance tree. IR -- @@ -60,13 +71,15 @@ LLVM Internal backends ----------------- -* add some basic register allocation +* it would be nice the upstream the code generator * add a pass to transform 3-addresses code to 2-addresses +* add some basic register allocation +* add a pass to order the BBs and changes 2-ways CBR into one-way branches * what can be done for x86? +* add support to add constraints in the MD rules Longer term/to investigate -------------------------- -* better architecture handling than current machine.h + target.c * attributes are represented as ctypes's alignment, modifiers & contexts but plenty of attributes doesn't fit, for example they need arguments. * format(printf, ...), diff --git a/Documentation/release-notes/index.rst b/Documentation/release-notes/index.rst index aa026653..f886470e 100644 --- a/Documentation/release-notes/index.rst +++ b/Documentation/release-notes/index.rst @@ -5,6 +5,7 @@ Release Notes .. toctree:: :maxdepth: 1 + v0.6.3 v0.6.2 v0.6.1 v0.6.0 diff --git a/Documentation/release-notes/v0.6.3.rst b/Documentation/release-notes/v0.6.3.rst index 1aae742e..7ec59eff 100644 --- a/Documentation/release-notes/v0.6.3.rst +++ b/Documentation/release-notes/v0.6.3.rst @@ -1,5 +1,56 @@ -v0.6.3 (2020-xx-xy) +v0.6.3 (2020-10-17) =================== -* Changes in warnings: - "warning: cast to union type" [disable with -Wno-union-cast] +Bug fixes: + * fix missing inlining of _Generic expression + * fix evaluation error with assignment of qualified arrays + * delay 'empty character constant' warning to phase 5 + * simplify & fix parsing of array declarators + * accept whitespace after option -U + * teach dissect about _Generic + * reset locale after gtk_init() to workaround problems with strtold() + * fix linearization of shift-assign + * force to 0 expressions which are erroneously non-constant + * fix evaluate_ptr_add() when sizeof(offset) != sizeof(pointer) + * fix access to defining instruction in simplify_unop() + * fix evaluation of pointer to bool conversions + * fix usual conversion of integers + * fix null pointer deref on return expression with invalid type + +New features: + * add support for arch specific asm constraints + * add memory asm constraint for PPC & S390 + * prepend diagnostics with source's path and include chain + * add support for h8300, microblaze, nds32, openrisc, sh & xtensa + * add support for '-march=....' and use it for riscv + * add an option to specify the OS: --os=$OS + * add predefines for OS identification + * add predefines for __INT_LEAST${N}_TYPE__ & __INT_FAST${N}_TYPE__ + * document the sparse's extensions + * sindex/semind: allow indexing outside the project tree + * rename tool 'sindex' to 'semind' + * add builtin support for __sync_{bool,val}_compare_and_swap() + * add support for wide strings + * union-cast: teach sparse about union casts + * add support for a new instruction: OP_FMADD + * add various warnings for dangerous usage of flexible array members + * add builtin support for __builtin_ia32_pause() + +Misc changes: + * cleanup the handling of options flags + * avoid multiple warnings when inlining undeclared calls + * small fixes for alpha, arm, nios2, ppc, sparc & x86 + * add missing predefines for endianness on arm, arm64, mips + * add various missing arch-specific predefines + * add the predefines '__cdecl', ... on cygwin + * warn on empty assignments & initializations + * reorganize the keyword parsing table + * the message in _Static_assert() is now optional (C2x) + * small fixes & improvement to the [online] documentation + * allow [*] in array declarators + * do not accept comma expressions in array declarator + * simplify parsing of attributes & storage class + * bad-shift: wait dead code elimination to warn about bad shifts + * fix is_scalar_type(): fouled types are scalars too + * better support for linearization of builtins + * remove definition of removed OP_{AND,OR}_BOOL @@ -1,4 +1,4 @@ -VERSION=0.6.2 +VERSION=0.6.3 ######################################################################## # The following variables can be overwritten from the command line @@ -273,7 +273,7 @@ version.h: FORCE check: all $(Q)cd validation && ./test-suite -validation/%: $(PROGRAMS) +validation/%: $(PROGRAMS) FORCE $(Q)validation/test-suite $* @@ -31,6 +31,14 @@ #include "compat/bswap.h" #include <stdarg.h> +#define dyntype incomplete_ctype +static bool is_dynamic_type(struct symbol *t) +{ + if (t->type == SYM_NODE) + t = t->ctype.base_type; + return t == &dyntype; +} + static int evaluate_to_int_const_expr(struct expression *expr) { expr->ctype = &int_ctype; @@ -83,6 +91,13 @@ error: return 0; } +static int args_prototype(struct expression *expr) +{ + struct symbol *fntype = expr->fn->ctype->ctype.base_type; + int n = symbol_list_size(fntype->arguments); + return eval_args(expr, n); +} + static int args_triadic(struct expression *expr) { return eval_args(expr, 3); @@ -355,29 +370,32 @@ static struct symbol_op overflow_p_op = { }; -static int eval_sync_compare_and_swap(struct expression *expr) +static int eval_atomic_common(struct expression *expr) { + struct symbol *fntype = expr->fn->ctype->ctype.base_type; struct symbol_list *types = NULL; struct symbol *ctype = NULL; + struct symbol *t; struct expression *arg; int n = 0; - /* the first arg is a pointer type; we'd already verified that */ + // The number of arguments has already be verified. + // The first arg must be a pointer to an integral type. + PREPARE_PTR_LIST(fntype->arguments, t); FOR_EACH_PTR(expr->args, arg) { - struct symbol *t = arg->ctype; + struct symbol *ptrtype = NULL; - if (!t) - return 0; - - // 2nd & 3rd args must be a basic integer type or a pointer - // 1st arg must be a pointer to such a type. if (++n == 1) { + t = arg->ctype; + if (!t) + return 0; if (t->type == SYM_NODE) t = t->ctype.base_type; if (!t) return 0; if (t->type != SYM_PTR) goto err; + ptrtype = t; t = t->ctype.base_type; if (!t) return 0; @@ -388,13 +406,18 @@ static int eval_sync_compare_and_swap(struct expression *expr) if (t->type != SYM_PTR && t->ctype.base_type != &int_type) goto err; ctype = t; - add_ptr_list(&types, arg->ctype); - } else { - add_ptr_list(&types, ctype); + t = ptrtype; + } else if (is_dynamic_type(t)) { + t = ctype; + } else if (t == &ptr_ctype) { + t = ptrtype; } + add_ptr_list(&types, t); + NEXT_PTR_LIST(t); } END_FOR_EACH_PTR(arg); + FINISH_PTR_LIST(t); - if (!expr->ctype) // __sync_val_compare_and_swap() + if (!expr->ctype) // set the return type, if needed expr->ctype = ctype; return evaluate_arguments(types, expr->args); @@ -405,9 +428,9 @@ err: return 0; } -static struct symbol_op sync_compare_and_swap_op = { - .args = args_triadic, - .evaluate = eval_sync_compare_and_swap, +static struct symbol_op atomic_op = { + .args = args_prototype, + .evaluate = eval_atomic_common, }; @@ -457,6 +480,33 @@ static void declare_builtins(int stream, const struct builtin_fn tbl[]) static const struct builtin_fn builtins_common[] = { #define size_t_ctype &size_t_alias #define va_list_ctype &ptr_ctype +#define vol_ptr &volatile_ptr_ctype + { "__atomic_add_fetch", NULL, 0, { vol_ptr, &dyntype, &int_ctype }, .op = &atomic_op }, + { "__atomic_always_lock_free", &bool_ctype, 0, { size_t_ctype, vol_ptr }}, + { "__atomic_and_fetch", NULL, 0, { vol_ptr, &dyntype, &int_ctype }, .op = &atomic_op }, + { "__atomic_clear", &void_ctype, 0, { &volatile_bool_ptr_ctype, &int_ctype }}, + { "__atomic_compare_exchange", &bool_ctype, 0, { vol_ptr, &ptr_ctype, &ptr_ctype, &bool_ctype, &int_ctype, &int_ctype }, .op = &atomic_op }, + { "__atomic_compare_exchange_n", &bool_ctype, 0, { vol_ptr, &ptr_ctype, &dyntype, &bool_ctype, &int_ctype, &int_ctype }, .op = &atomic_op }, + { "__atomic_exchange", &void_ctype, 0, { vol_ptr, &ptr_ctype, &ptr_ctype, &int_ctype }, .op = &atomic_op }, + { "__atomic_exchange_n", NULL, 0, { vol_ptr, &dyntype, &int_ctype }, .op = &atomic_op }, + { "__atomic_fetch_add", NULL, 0, { vol_ptr, &dyntype, &int_ctype }, .op = &atomic_op }, + { "__atomic_fetch_and", NULL, 0, { vol_ptr, &dyntype, &int_ctype }, .op = &atomic_op }, + { "__atomic_fetch_nand",NULL, 0, { vol_ptr, &dyntype, &int_ctype }, .op = &atomic_op }, + { "__atomic_fetch_or", NULL, 0, { vol_ptr, &dyntype, &int_ctype }, .op = &atomic_op }, + { "__atomic_fetch_sub", NULL, 0, { vol_ptr, &dyntype, &int_ctype }, .op = &atomic_op }, + { "__atomic_fetch_xor", NULL, 0, { vol_ptr, &dyntype, &int_ctype }, .op = &atomic_op }, + { "__atomic_is_lock_free", &bool_ctype, 0, { size_t_ctype, vol_ptr }}, + { "__atomic_load", &void_ctype, 0, { vol_ptr, &ptr_ctype, &int_ctype }, .op = &atomic_op }, + { "__atomic_load_n", NULL, 0, { vol_ptr, &int_ctype }, .op = &atomic_op }, + { "__atomic_nand_fetch",NULL, 0, { vol_ptr, &dyntype, &int_ctype }, .op = &atomic_op }, + { "__atomic_or_fetch", NULL, 0, { vol_ptr, &dyntype, &int_ctype }, .op = &atomic_op }, + { "__atomic_signal_fence", &void_ctype, 0, { &int_ctype }}, + { "__atomic_store", &void_ctype, 0, { vol_ptr, &ptr_ctype, &int_ctype }, .op = &atomic_op }, + { "__atomic_store_n", &void_ctype, 0, { vol_ptr, &dyntype, &int_ctype }, .op = &atomic_op }, + { "__atomic_sub_fetch", NULL, 0, { vol_ptr, &dyntype, &int_ctype }, .op = &atomic_op }, + { "__atomic_test_and_set", &bool_ctype, 0, { vol_ptr, &int_ctype }}, + { "__atomic_thread_fence", &void_ctype, 0, { &int_ctype }}, + { "__atomic_xor_fetch", NULL, 0, { vol_ptr, &dyntype, &int_ctype }, .op = &atomic_op }, { "__builtin_choose_expr", NULL, 1, .op = &choose_op }, { "__builtin_constant_p", NULL, 1, .op = &constant_p_op }, { "__builtin_expect", &long_ctype, 0, { &long_ctype ,&long_ctype }, .op = &expect_op }, @@ -605,23 +655,23 @@ static const struct builtin_fn builtins_common[] = { { "__builtin___vsnprintf_chk", &int_ctype, 0, { &string_ctype, size_t_ctype, &int_ctype, size_t_ctype, &const_string_ctype, va_list_ctype }}, { "__builtin___vsprintf_chk", &int_ctype, 0, { &string_ctype, &int_ctype, size_t_ctype, &const_string_ctype, va_list_ctype }}, - { "__sync_add_and_fetch", &int_ctype, 1, { &ptr_ctype }}, - { "__sync_and_and_fetch", &int_ctype, 1, { &ptr_ctype }}, - { "__sync_bool_compare_and_swap", &bool_ctype, 1, { &ptr_ctype }, .op = &sync_compare_and_swap_op}, - { "__sync_fetch_and_add", &int_ctype, 1, { &ptr_ctype }}, - { "__sync_fetch_and_and", &int_ctype, 1, { &ptr_ctype }}, - { "__sync_fetch_and_nand", &int_ctype, 1, { &ptr_ctype }}, - { "__sync_fetch_and_or", &int_ctype, 1, { &ptr_ctype }}, - { "__sync_fetch_and_sub", &int_ctype, 1, { &ptr_ctype }}, - { "__sync_fetch_and_xor", &int_ctype, 1, { &ptr_ctype }}, - { "__sync_lock_release", &void_ctype, 1, { &ptr_ctype }}, - { "__sync_lock_test_and_set", &int_ctype, 1, { &ptr_ctype }}, - { "__sync_nand_and_fetch", &int_ctype, 1, { &ptr_ctype }}, - { "__sync_or_and_fetch", &int_ctype, 1, { &ptr_ctype }}, - { "__sync_sub_and_fetch", &int_ctype, 1, { &ptr_ctype }}, - { "__sync_synchronize", &void_ctype, 0 }, - { "__sync_val_compare_and_swap", NULL, 1, { &ptr_ctype }, .op = &sync_compare_and_swap_op }, - { "__sync_xor_and_fetch", &int_ctype, 1, { &ptr_ctype }}, + { "__sync_add_and_fetch", NULL, 1, { vol_ptr, &dyntype }, .op = &atomic_op }, + { "__sync_and_and_fetch", NULL, 1, { vol_ptr, &dyntype }, .op = &atomic_op }, + { "__sync_bool_compare_and_swap", &bool_ctype, 1, { vol_ptr, &dyntype, &dyntype }, .op = &atomic_op}, + { "__sync_fetch_and_add", NULL, 1, { vol_ptr, &dyntype }, .op = &atomic_op }, + { "__sync_fetch_and_and", NULL, 1, { vol_ptr, &dyntype }, .op = &atomic_op }, + { "__sync_fetch_and_nand", NULL, 1, { vol_ptr, &dyntype }, .op = &atomic_op }, + { "__sync_fetch_and_or", NULL, 1, { vol_ptr, &dyntype }, .op = &atomic_op }, + { "__sync_fetch_and_sub", NULL, 1, { vol_ptr, &dyntype }, .op = &atomic_op }, + { "__sync_fetch_and_xor", NULL, 1, { vol_ptr, &dyntype }, .op = &atomic_op }, + { "__sync_lock_release", &void_ctype, 1, { vol_ptr }, .op = &atomic_op }, + { "__sync_lock_test_and_set", NULL, 1, { vol_ptr, &dyntype }, .op = &atomic_op }, + { "__sync_nand_and_fetch", NULL, 1, { vol_ptr, &dyntype }, .op = &atomic_op }, + { "__sync_or_and_fetch", NULL, 1, { vol_ptr, &dyntype }, .op = &atomic_op }, + { "__sync_sub_and_fetch", NULL, 1, { vol_ptr, &dyntype }, .op = &atomic_op }, + { "__sync_synchronize", &void_ctype, 1 }, + { "__sync_val_compare_and_swap", NULL, 1, { vol_ptr, &dyntype, &dyntype }, .op = &atomic_op }, + { "__sync_xor_and_fetch", NULL, 1, { vol_ptr, &dyntype }, .op = &atomic_op }, { } }; @@ -164,49 +164,52 @@ static inline struct symbol *integer_promotion(struct symbol *type) } /* - * integer part of usual arithmetic conversions: - * integer promotions are applied - * if left and right are identical, we are done - * if signedness is the same, convert one with lower rank - * unless unsigned argument has rank lower than signed one, convert the - * signed one. - * if signed argument is bigger than unsigned one, convert the unsigned. - * otherwise, convert signed. - * - * Leaving aside the integer promotions, that is equivalent to - * if identical, don't convert - * if left is bigger than right, convert right - * if right is bigger than left, convert right - * otherwise, if signedness is the same, convert one with lower rank - * otherwise convert the signed one. + * After integer promotons: + * If both types are the same + * -> no conversion needed + * If the types have the same signedness (their rank must be different) + * -> convert to the type of the highest rank + * If rank(unsigned type) >= rank(signed type) + * -> convert to the unsigned type + * If size(signed type) > size(unsigned type) + * -> convert to the signed type + * Otherwise + * -> convert to the unsigned type corresponding to the signed type. */ static struct symbol *bigger_int_type(struct symbol *left, struct symbol *right) { + static struct symbol *unsigned_types[] = { + [0] = &uint_ctype, + [1] = &ulong_ctype, + [2] = &ullong_ctype, + [3] = &uint128_ctype, + }; unsigned long lmod, rmod; + struct symbol *stype, *utype; left = integer_promotion(left); right = integer_promotion(right); if (left == right) - goto left; - - if (left->bit_size > right->bit_size) - goto left; - - if (right->bit_size > left->bit_size) - goto right; + return left; lmod = left->ctype.modifiers; rmod = right->ctype.modifiers; - if ((lmod ^ rmod) & MOD_UNSIGNED) { - if (lmod & MOD_UNSIGNED) - goto left; - } else if (left->rank > right->rank) - goto left; -right: - left = right; -left: - return left; + if (((lmod ^ rmod) & MOD_UNSIGNED) == 0) + return (left->rank > right->rank) ? left : right; + if (lmod & MOD_UNSIGNED) { + utype = left; + stype = right; + } else { + stype = left; + utype = right; + } + if (utype->rank >= stype->rank) + return utype; + if (stype->bit_size > utype->bit_size) + return stype; + utype = unsigned_types[stype->rank]; + return utype; } static int same_cast_type(struct symbol *orig, struct symbol *new) @@ -1784,6 +1787,8 @@ static struct symbol *degenerate(struct expression *expr) expression_error(expr, "strange non-value function or array"); return &bad_ctype; } + if (ctype->builtin) + sparse_error(expr->pos, "taking the address of built-in function '%s'", show_ident(ctype->ident)); *expr = *expr->unop; ctype = create_pointer(expr, ctype, 1); expr->ctype = ctype; @@ -1804,6 +1809,8 @@ static struct symbol *evaluate_addressof(struct expression *expr) return NULL; } ctype = op->ctype; + if (ctype->builtin) + sparse_error(expr->pos, "taking the address of built-in function '%s'", show_ident(ctype->ident)); *expr = *op->unop; mark_addressable(expr); @@ -2253,6 +2260,9 @@ static struct symbol *evaluate_sizeof(struct expression *expr) size = bits_in_char; } + if (has_flexible_array(type) && Wflexible_array_sizeof) + warning(expr->pos, "using sizeof on a flexible structure"); + if (is_array_type(type) && size < 0) { // VLA, 1-dimension only struct expression *base, *size; struct symbol *base_type; @@ -2883,6 +2893,8 @@ static struct symbol *cast_to_bool(struct expression *expr) return NULL; zero = alloc_const_expression(expr->pos, 0); + if (oclass & TYPE_PTR) + zero->ctype = otype; expr->op = SPECIAL_NOTEQUAL; ctype = usual_conversions(expr->op, old, zero, oclass, TYPE_NUM, otype, zero->ctype); @@ -3603,7 +3615,7 @@ static struct symbol *evaluate_return_expression(struct statement *stmt) fntype = current_fn->ctype.base_type; rettype = fntype->ctype.base_type; if (!rettype || rettype == &void_ctype) { - if (expr && !is_void_type(expr->ctype)) + if (expr && expr->ctype && !is_void_type(expr->ctype)) expression_error(expr, "return expression in %s function", rettype?"void":"typeless"); if (expr && Wreturn_void) warning(stmt->pos, "returning void-valued expression"); @@ -44,7 +44,7 @@ #define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0])) #endif -#ifndef PATH_MAX +#ifdef __gnu_hurd__ #define PATH_MAX 4096 // Hurd doesn't define this #endif @@ -33,8 +33,6 @@ OPCODE(FMUL, BADOP, BADOP, BADOP, 2, OPF_TARGET) OPCODE(FDIV, BADOP, BADOP, BADOP, 2, OPF_TARGET) /* Logical */ -OPCODE(AND_BOOL, BADOP, BADOP, BADOP, 2, OPF_TARGET) -OPCODE(OR_BOOL, BADOP, BADOP, BADOP, 2, OPF_TARGET) OPCODE(AND, BADOP, BADOP, BADOP, 2, OPF_TARGET) OPCODE(OR, BADOP, BADOP, BADOP, 2, OPF_TARGET) OPCODE(XOR, BADOP, BADOP, BADOP, 2, OPF_TARGET) @@ -101,6 +101,10 @@ int Wdesignated_init = 1; int Wdo_while = 0; int Wenum_mismatch = 1; int Wexternal_function_has_definition = 1; +int Wflexible_array_array = 1; +int Wflexible_array_nested = 0; +int Wflexible_array_sizeof = 0; +int Wflexible_array_union = 0; int Wimplicit_int = 1; int Winit_cstring = 0; int Wint_to_pointer_cast = 1; @@ -843,6 +847,10 @@ static const struct flag warnings[] = { { "do-while", &Wdo_while }, { "enum-mismatch", &Wenum_mismatch }, { "external-function-has-definition", &Wexternal_function_has_definition }, + { "flexible-array-array", &Wflexible_array_array }, + { "flexible-array-nested", &Wflexible_array_nested }, + { "flexible-array-sizeof", &Wflexible_array_sizeof }, + { "flexible-array-union", &Wflexible_array_union }, { "implicit-int", &Wimplicit_int }, { "init-cstring", &Winit_cstring }, { "int-to-pointer-cast", &Wint_to_pointer_cast }, @@ -100,6 +100,10 @@ extern int Wdesignated_init; extern int Wdo_while; extern int Wenum_mismatch; extern int Wexternal_function_has_definition; +extern int Wflexible_array_array; +extern int Wflexible_array_nested; +extern int Wflexible_array_sizeof; +extern int Wflexible_array_union; extern int Wimplicit_int; extern int Winit_cstring; extern int Wint_to_pointer_cast; diff --git a/predefine.c b/predefine.c index f898cdfa..98e38a04 100644 --- a/predefine.c +++ b/predefine.c @@ -179,6 +179,13 @@ void predefined_macros(void) if (arch_target->has_int128) predefined_sizeof("INT128", "", 128); + predefine("__ATOMIC_RELAXED", 0, "0"); + predefine("__ATOMIC_CONSUME", 0, "1"); + predefine("__ATOMIC_ACQUIRE", 0, "3"); + predefine("__ATOMIC_RELEASE", 0, "4"); + predefine("__ATOMIC_ACQ_REL", 0, "7"); + predefine("__ATOMIC_SEQ_CST", 0, "8"); + predefine("__ORDER_LITTLE_ENDIAN__", 1, "1234"); predefine("__ORDER_BIG_ENDIAN__", 1, "4321"); predefine("__ORDER_PDP_ENDIAN__", 1, "3412"); @@ -1311,13 +1311,11 @@ static int simplify_unop(struct instruction *insn) struct instruction *def; case OP_NOT: - def = insn->src->def; - if (def && def->opcode == OP_NOT) + if (DEF_OPCODE(def, insn->src) == OP_NOT) return replace_with_pseudo(insn, def->src); break; case OP_NEG: - def = insn->src->def; - if (def && def->opcode == OP_NEG) + if (DEF_OPCODE(def, insn->src) == OP_NEG) return replace_with_pseudo(insn, def->src); break; default: @@ -257,6 +257,36 @@ Sparse issues these warnings by default. To turn them off, use \fB\-Wno\-external\-function\-has\-definition\fR. . .TP +.B -Wflexible-array-array +Warn about arrays of structures containing a flexible array. + +Sparse issues these warnings by default. To turn them off, use +\fB-Wno-flexible-array-array\fR. +. +.TP +.B -Wflexible-array-nested +Warn about structures containing a flexible array being contained into +another structure, union or array. + +Sparse does not issue these warnings by default. +. +.TP +.B -Wflexible-array-sizeof +Warn about using the sizeof operator on a structure containing a flexible array, +possibly recursively. + +Sparse does not issue these warnings by default. +. +.TP +.B -Wflexible-array-union +Enable the warnings regarding flexible arrays and unions. +To have any effect, at least one of \fB-Wflexible-array-array\fR, +\fB-Wflexible-array-nested\fR or \fB-Wflexible-array-sizeof\fR must also +be enabled. + +Sparse does issue these warnings by default. +. +.TP .B \-Winit\-cstring Warn about initialization of a char array with a too long constant C string. @@ -87,6 +87,8 @@ struct struct_union_info { unsigned long max_align; unsigned long bit_size; int align_size; + char has_flex_array; + struct symbol *flex_array; }; /* @@ -94,13 +96,8 @@ struct struct_union_info { */ static void lay_out_union(struct symbol *sym, struct struct_union_info *info) { - examine_symbol_type(sym); - - // Unnamed bitfields do not affect alignment. - if (sym->ident || !is_bitfield_type(sym)) { - if (sym->ctype.alignment > info->max_align) - info->max_align = sym->ctype.alignment; - } + if (sym->bit_size < 0 && is_array_type(sym)) + sparse_error(sym->pos, "flexible array member '%s' in a union", show_ident(sym->ident)); if (sym->bit_size > info->bit_size) info->bit_size = sym->bit_size; @@ -125,24 +122,18 @@ static void lay_out_struct(struct symbol *sym, struct struct_union_info *info) unsigned long bit_size, align_bit_mask; int base_size; - examine_symbol_type(sym); - - // Unnamed bitfields do not affect alignment. - if (sym->ident || !is_bitfield_type(sym)) { - if (sym->ctype.alignment > info->max_align) - info->max_align = sym->ctype.alignment; - } - bit_size = info->bit_size; base_size = sym->bit_size; /* - * Unsized arrays cause us to not align the resulting - * structure size + * If the member is unsized, either it's a flexible array or + * it's invalid and a warning has already been issued. */ if (base_size < 0) { - info->align_size = 0; + if (!is_array_type(sym)) + return; base_size = 0; + info->flex_array = sym; } align_bit_mask = bytes_to_bits(sym->ctype.alignment) - 1; @@ -196,6 +187,20 @@ static struct symbol * examine_struct_union_type(struct symbol *sym, int advance sparse_error(member->pos, "member '%s' has __auto_type", show_ident(member->ident)); member->ctype.base_type = &incomplete_ctype; } + if (info.flex_array) + sparse_error(info.flex_array->pos, "flexible array member '%s' is not last", show_ident(info.flex_array->ident)); + examine_symbol_type(member); + + if (member->ctype.alignment > info.max_align) { + // Unnamed bitfields do not affect alignment. + if (member->ident || !is_bitfield_type(member)) + info.max_align = member->ctype.alignment; + } + + if (has_flexible_array(member)) + info.has_flex_array = 1; + if (has_flexible_array(member) && Wflexible_array_nested) + warning(member->pos, "nested flexible array"); fn(member, &info); } END_FOR_EACH_PTR(member); @@ -206,6 +211,11 @@ static struct symbol * examine_struct_union_type(struct symbol *sym, int advance bit_align = bytes_to_bits(sym->ctype.alignment)-1; bit_size = (bit_size + bit_align) & ~bit_align; } + if (info.flex_array) { + info.has_flex_array = 1; + } + if (info.has_flex_array && (!is_union_type(sym) || Wflexible_array_union)) + sym->has_flex_array = 1; sym->bit_size = bit_size; return sym; } @@ -261,6 +271,8 @@ static struct symbol * examine_array_type(struct symbol *sym) bit_size = -1; } } + if (has_flexible_array(base_type) && Wflexible_array_array) + warning(sym->pos, "array of flexible structures"); alignment = base_type->ctype.alignment; if (!sym->ctype.alignment) sym->ctype.alignment = alignment; @@ -782,14 +794,19 @@ struct symbol bool_ctype, void_ctype, type_ctype, incomplete_ctype, label_ctype, bad_ctype, null_ctype; struct symbol autotype_ctype; +struct symbol schar_ptr_ctype, short_ptr_ctype; struct symbol int_ptr_ctype, uint_ptr_ctype; struct symbol long_ptr_ctype, ulong_ptr_ctype; struct symbol llong_ptr_ctype, ullong_ptr_ctype; +struct symbol size_t_ptr_ctype, intmax_ptr_ctype, ptrdiff_ptr_ctype; struct symbol float32_ctype, float32x_ctype; struct symbol float64_ctype, float64x_ctype; struct symbol float128_ctype; struct symbol const_void_ctype, const_char_ctype; struct symbol const_ptr_ctype, const_string_ctype; +struct symbol const_wchar_ctype, const_wstring_ctype; +struct symbol volatile_void_ctype, volatile_ptr_ctype; +struct symbol volatile_bool_ctype, volatile_bool_ptr_ctype; struct symbol zero_int; @@ -876,17 +893,28 @@ static const struct ctype_declare { { &null_ctype, T_PTR(&void_ctype) }, { &label_ctype, T_PTR(&void_ctype) }, { &lazy_ptr_ctype, T_PTR(&void_ctype) }, + { &schar_ptr_ctype, T_PTR(&schar_ctype) }, + { &short_ptr_ctype, T_PTR(&short_ctype) }, { &int_ptr_ctype, T_PTR(&int_ctype) }, { &uint_ptr_ctype, T_PTR(&uint_ctype) }, { &long_ptr_ctype, T_PTR(&long_ctype) }, { &ulong_ptr_ctype, T_PTR(&ulong_ctype) }, { &llong_ptr_ctype, T_PTR(&llong_ctype) }, { &ullong_ptr_ctype, T_PTR(&ullong_ctype) }, + { &size_t_ptr_ctype, T_PTR(&void_ctype) }, // will be adjusted + { &intmax_ptr_ctype, T_PTR(&void_ctype) }, // will be adjusted + { &ptrdiff_ptr_ctype, T_PTR(&void_ctype) }, // will be adjusted { &const_ptr_ctype, T_PTR(&const_void_ctype) }, { &const_string_ctype, T_PTR(&const_char_ctype) }, + { &const_wstring_ctype,T_PTR(&const_wchar_ctype) }, { &const_void_ctype, T_CONST(&void_ctype, NULL, NULL) }, { &const_char_ctype, T_CONST(&char_ctype, &bits_in_char, &max_int_alignment)}, + { &const_wchar_ctype, T_CONST(&int_ctype, NULL, NULL) }, + { &volatile_void_ctype,T_NODE(MOD_VOLATILE, &void_ctype, NULL, NULL) }, + { &volatile_ptr_ctype, T_PTR(&volatile_void_ctype) }, + { &volatile_bool_ctype,T_NODE(MOD_VOLATILE, &bool_ctype, NULL, NULL) }, + { &volatile_bool_ptr_ctype, T_PTR(&volatile_bool_ctype) }, { NULL, } }; @@ -931,4 +959,13 @@ void init_ctype(void) intptr_ctype = ssize_t_ctype; if (!uintptr_ctype) uintptr_ctype = size_t_ctype; + + size_t_ptr_ctype.ctype.base_type = size_t_ctype; + intmax_ptr_ctype.ctype.base_type = intmax_ctype; + ptrdiff_ptr_ctype.ctype.base_type = ptrdiff_ctype; + + const_wchar_ctype.ctype.base_type = wchar_ctype; + const_wchar_ctype.rank = wchar_ctype->rank; + const_wchar_ctype.ctype.alignment = wchar_ctype->ctype.alignment; + const_wchar_ctype.bit_size = wchar_ctype->bit_size; } @@ -185,6 +185,7 @@ struct symbol { examined:1, expanding:1, evaluated:1, + has_flex_array:1, string:1, designated_init:1, forced_arg:1, @@ -298,14 +299,19 @@ extern struct symbol bool_ctype, void_ctype, type_ctype, incomplete_ctype, label_ctype, bad_ctype, null_ctype; extern struct symbol autotype_ctype; +extern struct symbol schar_ptr_ctype, short_ptr_ctype; extern struct symbol int_ptr_ctype, uint_ptr_ctype; extern struct symbol long_ptr_ctype, ulong_ptr_ctype; extern struct symbol llong_ptr_ctype, ullong_ptr_ctype; +extern struct symbol size_t_ptr_ctype, intmax_ptr_ctype, ptrdiff_ptr_ctype; extern struct symbol float32_ctype, float32x_ctype; extern struct symbol float64_ctype, float64x_ctype; extern struct symbol float128_ctype; extern struct symbol const_void_ctype, const_char_ctype; extern struct symbol const_ptr_ctype, const_string_ctype; +extern struct symbol const_wchar_ctype, const_wstring_ctype; +extern struct symbol volatile_void_ctype, volatile_ptr_ctype; +extern struct symbol volatile_bool_ctype, volatile_bool_ptr_ctype; /* Special internal symbols */ extern struct symbol zero_int; @@ -421,6 +427,20 @@ static inline int is_array_type(struct symbol *type) return type->type == SYM_ARRAY; } +static inline int is_struct_type(struct symbol *type) +{ + if (type->type == SYM_NODE) + type = type->ctype.base_type; + return type->type == SYM_STRUCT; +} + +static inline int is_union_type(struct symbol *type) +{ + if (type->type == SYM_NODE) + type = type->ctype.base_type; + return type->type == SYM_UNION; +} + static inline int is_float_type(struct symbol *type) { if (type->type == SYM_NODE) @@ -506,6 +526,13 @@ static inline int is_extern_inline(struct symbol *sym) is_function(sym->ctype.base_type); } +static inline int has_flexible_array(struct symbol *type) +{ + if (type->type == SYM_NODE) + type = type->ctype.base_type; + return type->has_flex_array; +} + static inline int get_sym_type(struct symbol *type) { if (type->type == SYM_NODE) diff --git a/target-x86.c b/target-x86.c index b7ff8f2a..9d82869a 100644 --- a/target-x86.c +++ b/target-x86.c @@ -1,6 +1,7 @@ #include "symbol.h" #include "target.h" #include "machine.h" +#include "builtin.h" static void predefine_i386(const struct target *self) @@ -39,6 +40,11 @@ static void init_x86_common(const struct target *target) } } +static const struct builtin_fn builtins_x86_common[] = { + { "__builtin_ia32_pause", &void_ctype, 0, }, + { } +}; + static void init_i386(const struct target *target) { @@ -64,6 +70,7 @@ const struct target target_i386 = { .init = init_i386, .predefine = predefine_i386, + .builtins = builtins_x86_common, }; @@ -159,4 +166,5 @@ const struct target target_x86_64 = { .init = init_x86_64, .predefine = predefine_x86_64, + .builtins = builtins_x86_common, }; diff --git a/validation/builtin-arith.c b/validation/builtin-arith.c index d08c93da..3ce59304 100644 --- a/validation/builtin-arith.c +++ b/validation/builtin-arith.c @@ -31,22 +31,27 @@ void test(void (*fun)(void)) /* * check-name: builtin arithmetic * check-command: sparse -Wno-decl $file - * check-known-to-fail * * check-error-start -builtin-arith.c:10:xx: error: ... -builtin-arith.c:11:xx: error: ... -builtin-arith.c:13:xx: error: arithmetics on pointers to functions -builtin-arith.c:14:xx: error: arithmetics on pointers to functions -builtin-arith.c:15:xx: error: arithmetics on pointers to functions -builtin-arith.c:18:xx: error: ... -builtin-arith.c:19:xx: error: ... -builtin-arith.c:21:xx: error: ... -builtin-arith.c:22:xx: error: ... -builtin-arith.c:23:xx: error: ... -builtin-arith.c:24:xx: error: ... -builtin-arith.c:25:xx: error: ... +builtin-arith.c:10:14: error: taking the address of built-in function '__builtin_trap' +builtin-arith.c:11:13: error: taking the address of built-in function '__builtin_trap' +builtin-arith.c:12:14: error: taking the address of built-in function '__builtin_trap' +builtin-arith.c:13:14: error: taking the address of built-in function '__builtin_trap' +builtin-arith.c:13:29: error: arithmetics on pointers to functions +builtin-arith.c:14:14: error: taking the address of built-in function '__builtin_trap' +builtin-arith.c:14:29: error: arithmetics on pointers to functions +builtin-arith.c:15:14: error: taking the address of built-in function '__builtin_trap' +builtin-arith.c:15:29: error: arithmetics on pointers to functions +builtin-arith.c:18:21: error: taking the address of built-in function '__builtin_trap' +builtin-arith.c:19:29: error: taking the address of built-in function '__builtin_trap' +builtin-arith.c:21:14: error: taking the address of built-in function '__builtin_trap' +builtin-arith.c:22:14: error: taking the address of built-in function '__builtin_trap' +builtin-arith.c:23:14: error: taking the address of built-in function '__builtin_trap' +builtin-arith.c:24:21: error: taking the address of built-in function '__builtin_trap' +builtin-arith.c:25:21: error: taking the address of built-in function '__builtin_trap' +builtin-arith.c:27:9: error: taking the address of built-in function '__builtin_trap' builtin-arith.c:27:24: error: subtraction of functions? Share your drugs +builtin-arith.c:28:15: error: taking the address of built-in function '__builtin_trap' builtin-arith.c:28:13: error: subtraction of functions? Share your drugs * check-error-end */ diff --git a/validation/builtin-atomic-clear.c b/validation/builtin-atomic-clear.c new file mode 100644 index 00000000..ef430c64 --- /dev/null +++ b/validation/builtin-atomic-clear.c @@ -0,0 +1,15 @@ +void foo(void *ptr, _Bool *bptr, volatile void *vptr, volatile _Bool *vbptr, int mo) +{ + __atomic_clear(ptr, mo); + __atomic_clear(bptr, mo); + __atomic_clear(vptr, mo); + __atomic_clear(vbptr, mo); +} + +/* + * check-name: builtin-atomic-clear + * + * check-error-start +builtin-atomic-clear.c:1:6: warning: symbol 'foo' was not declared. Should it be static? + * check-error-end + */ diff --git a/validation/builtin-sync-fetch.c b/validation/builtin-sync-fetch.c new file mode 100644 index 00000000..45139a3c --- /dev/null +++ b/validation/builtin-sync-fetch.c @@ -0,0 +1,24 @@ +static int ok_int(int *ptr, int val) +{ + return __sync_add_and_fetch(ptr, val); +} + +static long* ok_ptr(long **ptr, long *val) +{ + return __sync_add_and_fetch(ptr, val); +} + +static void chk_ret_ok(long *ptr, long val) +{ + _Static_assert([typeof(__sync_add_and_fetch(ptr, val))] == [long], ""); +} + +static int chk_val(int *ptr, long val) +{ + // OK: val is converted to an int + return __sync_add_and_fetch(ptr, val); +} + +/* + * check-name: builtin-sync-fetch + */ diff --git a/validation/crash-undef-in-parens.c b/validation/crash-undef-in-parens.c new file mode 100644 index 00000000..5f05f88a --- /dev/null +++ b/validation/crash-undef-in-parens.c @@ -0,0 +1,9 @@ +void foo(void) { return (UNDEF_STUFF_IN_PARENS); } + +/* + * check-name: crash-undef-in-parens + * + * check-error-start +crash-undef-in-parens.c:1:26: error: undefined identifier 'UNDEF_STUFF_IN_PARENS' + * check-error-end + */ diff --git a/validation/flex-array-align.c b/validation/flex-array-align.c new file mode 100644 index 00000000..9f28942a --- /dev/null +++ b/validation/flex-array-align.c @@ -0,0 +1,18 @@ +struct s { + __INT32_TYPE__ x; + __INT16_TYPE__ y; + unsigned char f[]; +}; + +static int foo(struct s *s) +{ + return (sizeof(*s) << 16) | __builtin_offsetof(typeof(*s), f); +} + +/* + * check-name: flex-array-align + * check-command: test-linearize -Wno-flexible-array-sizeof $file + * + * check-output-ignore + * check-output-contains: ret\\..*\\$0x80006 + */ diff --git a/validation/flex-array-array.c b/validation/flex-array-array.c new file mode 100644 index 00000000..921a0698 --- /dev/null +++ b/validation/flex-array-array.c @@ -0,0 +1,15 @@ +struct s { + int i; + long f[]; +}; + +static struct s a[2]; + +/* + * check-name: flex-array-array + * check-command: sparse -Wflexible-array-array $file + * + * check-error-start +flex-array-array.c:6:18: warning: array of flexible structures + * check-error-end + */ diff --git a/validation/flex-array-error.c b/validation/flex-array-error.c new file mode 100644 index 00000000..2b7e6953 --- /dev/null +++ b/validation/flex-array-error.c @@ -0,0 +1,26 @@ +struct s { + int i; + long f[]; + int j; +}; + +union u { + int i; + long f[]; +}; + +// trigger the examination of the offending types +static int foo(struct s *s, union u *u) +{ + return __builtin_offsetof(typeof(*s), i) + + __builtin_offsetof(typeof(*u), i); +} + +/* + * check-name: flex-array-error + * + * check-error-start +flex-array-error.c:3:14: error: flexible array member 'f' is not last +flex-array-error.c:9:14: error: flexible array member 'f' in a union + * check-error-end + */ diff --git a/validation/flex-array-nested.c b/validation/flex-array-nested.c new file mode 100644 index 00000000..094de2fb --- /dev/null +++ b/validation/flex-array-nested.c @@ -0,0 +1,29 @@ +struct f { + int i; + long f[]; +}; + +struct s { + struct f f; +}; + +union u { + struct f f; +}; + +// trigger the examination of the offending types +static int foo(struct s *s, union u *u) +{ + return __builtin_offsetof(typeof(*s), f) + + __builtin_offsetof(typeof(*u), f); +} + +/* + * check-name: flex-array-nested + * check-command: sparse -Wflexible-array-nested $file + * + * check-error-start +flex-array-nested.c:7:18: warning: nested flexible array +flex-array-nested.c:11:18: warning: nested flexible array + * check-error-end + */ diff --git a/validation/flex-array-sizeof.c b/validation/flex-array-sizeof.c new file mode 100644 index 00000000..05394e19 --- /dev/null +++ b/validation/flex-array-sizeof.c @@ -0,0 +1,18 @@ +struct s { + int i; + long f[]; +}; + +static int foo(struct s *s) +{ + return sizeof(*s); +} + +/* + * check-name: flex-array-sizeof + * check-command: sparse -Wflexible-array-sizeof $file + * + * check-error-start +flex-array-sizeof.c:8:16: warning: using sizeof on a flexible structure + * check-error-end + */ diff --git a/validation/flex-array-union-array-no.c b/validation/flex-array-union-array-no.c new file mode 100644 index 00000000..5a1de787 --- /dev/null +++ b/validation/flex-array-union-array-no.c @@ -0,0 +1,9 @@ +#include "flex-array-union-array.h" + +/* + * check-name: flex-array-union-no + * check-command: sparse -Wflexible-array-array -Wno-flexible-array-union $file + * + * check-error-start + * check-error-end + */ diff --git a/validation/flex-array-union-array-yes.c b/validation/flex-array-union-array-yes.c new file mode 100644 index 00000000..c2b71d65 --- /dev/null +++ b/validation/flex-array-union-array-yes.c @@ -0,0 +1,11 @@ +#include "flex-array-union-array.h" + +/* + * check-name: flex-array-union-yes + * check-command: sparse -Wflexible-array-array -Wflexible-array-union $file + * + * check-error-start +flex-array-union-array-yes.c: note: in included file: +flex-array-union-array.h:11:17: warning: array of flexible structures + * check-error-end + */ diff --git a/validation/flex-array-union-array.h b/validation/flex-array-union-array.h new file mode 100644 index 00000000..b2a74d1a --- /dev/null +++ b/validation/flex-array-union-array.h @@ -0,0 +1,11 @@ +struct s_flex { + int i; + long f[]; +}; + +union s { + struct s_flex flex; + char buf[200]; +}; + +static union s a[2]; diff --git a/validation/linear/bool-cast-lp32.c b/validation/linear/bool-cast-lp32.c index 44a650f4..7aab31dd 100644 --- a/validation/linear/bool-cast-lp32.c +++ b/validation/linear/bool-cast-lp32.c @@ -12,7 +12,6 @@ static _Bool ffun_e(void) { return (_Bool)ffun; } /* * check-name: bool-cast-pointer * check-command: test-linearize -m32 -fdump-ir $file - * check-known-to-fail * * check-output-ignore * check-output-excludes: ptrtu\\. diff --git a/validation/optim/canonical-mul.c b/validation/optim/canonical-mul.c index 3ae9e3a6..0c14226f 100644 --- a/validation/optim/canonical-mul.c +++ b/validation/optim/canonical-mul.c @@ -7,7 +7,7 @@ uint xtc_umul_ytc(uint x, uint y) { return (x * 3) * (y * 2); } * check-description: * 1) verify that constants in mul chains are * pushed at the right of the whole chain. - * For example '(a * 3) * b' must be canonicalized into '(a * b) * 1' + * For example '(a * 3) * b' must be canonicalized into '(a * b) * 3' * This is needed in general for constant simplification; * for example, for: * '(a * 3) * (b * 2)' diff --git a/validation/usual-conv-lp32.c b/validation/usual-conv-lp32.c new file mode 100644 index 00000000..7f91288e --- /dev/null +++ b/validation/usual-conv-lp32.c @@ -0,0 +1,11 @@ +extern long l; +extern unsigned int u; + +#if __SIZEOF_LONG__ == __SIZEOF_INT__ +_Static_assert([typeof(l + u)] == [unsigned long], "ulong"); +#endif + +/* + * check-name: usual-conversions + * check-command: sparse -m32 $file + */ |
