diff options
28 files changed, 543 insertions, 18 deletions
@@ -71,7 +71,8 @@ static void clean_up_one_instruction(struct basic_block *bb, struct instruction case OP_SET_B: case OP_SET_A: case OP_SET_BE: case OP_SET_AE: - /* floating-point arithmetic */ + /* floating-point arithmetic & comparison */ + case OP_FPCMP ... OP_FPCMP_END: case OP_FADD: case OP_FSUB: case OP_FMUL: @@ -214,6 +215,7 @@ static int insn_compare(const void *_i1, const void *_i2) case OP_SET_BE: case OP_SET_AE: /* floating-point arithmetic */ + case OP_FPCMP ... OP_FPCMP_END: case OP_FADD: case OP_FSUB: case OP_FMUL: @@ -1776,14 +1776,18 @@ static struct symbol *evaluate_dereference(struct expression *expr) if (ctype->type == SYM_NODE) ctype = ctype->ctype.base_type; - node = alloc_symbol(expr->pos, SYM_NODE); target = ctype->ctype.base_type; + examine_symbol_type(target); switch (ctype->type) { default: expression_error(expr, "cannot dereference this type"); return NULL; + case SYM_FN: + *expr = *op; + return expr->ctype; case SYM_PTR: + node = alloc_symbol(expr->pos, SYM_NODE); node->ctype.modifiers = target->ctype.modifiers & MOD_SPECIFIER; merge_type(node, ctype); break; @@ -1801,6 +1805,7 @@ static struct symbol *evaluate_dereference(struct expression *expr) * When an array is dereferenced, we need to pick * up the attributes of the original node too.. */ + node = alloc_symbol(expr->pos, SYM_NODE); merge_type(node, op->ctype); merge_type(node, ctype); break; diff --git a/expression.c b/expression.c index e5ebad65..6f4300b9 100644 --- a/expression.c +++ b/expression.c @@ -62,7 +62,10 @@ static struct token *comma_expression(struct token *, struct expression **); struct token *parens_expression(struct token *token, struct expression **expr, const char *where) { + struct token *p; + token = expect(token, '(', where); + p = token; if (match_op(token, '{')) { struct expression *e = alloc_expression(token->pos, EXPR_STATEMENT); struct statement *stmt = alloc_statement(token->pos, STMT_COMPOUND); @@ -74,6 +77,9 @@ struct token *parens_expression(struct token *token, struct expression **expr, c token = expect(token, '}', "at end of statement expression"); } else token = parse_expression(token, expr); + + if (token == p) + sparse_error(token->pos, "an expression is expected before ')'"); return expect(token, ')', where); } diff --git a/linearize.c b/linearize.c index eff7d95f..deed3ca2 100644 --- a/linearize.c +++ b/linearize.c @@ -1279,15 +1279,10 @@ static pseudo_t linearize_call_expression(struct entrypoint *ep, struct expressi } fn = expr->fn; - - if (fn->ctype) - ctype = &fn->ctype->ctype; - fntype = fn->ctype; - if (fntype) { - if (fntype->type == SYM_NODE) - fntype = fntype->ctype.base_type; - } + ctype = &fntype->ctype; + if (fntype->type == SYM_NODE) + fntype = fntype->ctype.base_type; add_symbol(&insn->fntypes, fntype); FOR_EACH_PTR(expr->args, arg) { @@ -1296,13 +1291,9 @@ static pseudo_t linearize_call_expression(struct entrypoint *ep, struct expressi add_symbol(&insn->fntypes, arg->ctype); } END_FOR_EACH_PTR(arg); - if (fn->type == EXPR_PREOP) { - if (fn->unop->type == EXPR_SYMBOL) { - struct symbol *sym = fn->unop->symbol; - if (sym->ctype.base_type->type == SYM_FN) - fn = fn->unop; - } - } + if (fn->type == EXPR_PREOP && fn->op == '*' && is_func_type(fn->ctype)) + fn = fn->unop; + if (fn->type == EXPR_SYMBOL) { call = symbol_pseudo(ep, fn->symbol); } else { @@ -1954,6 +1945,8 @@ static pseudo_t linearize_switch(struct entrypoint *ep, struct statement *stmt) pseudo_t pseudo; pseudo = linearize_expression(ep, expr); + if (pseudo == VOID) + return pseudo; active = ep->active; if (!bb_reachable(active)) @@ -160,6 +160,8 @@ static void kill_dominated_stores(struct basic_block *bb) pseudo_t pseudo = insn->src; int local; + if (!insn->type) + continue; if (insn->type->ctype.modifiers & MOD_VOLATILE) continue; @@ -364,6 +364,23 @@ static struct expression *get_symbol_initializer(struct symbol *sym) return NULL; } +static unsigned int implicit_array_size(struct symbol *node, unsigned int count) +{ + struct symbol *arr_ori = node->ctype.base_type; + struct symbol *arr_new = alloc_symbol(node->pos, SYM_ARRAY); + struct symbol *elem_type = arr_ori->ctype.base_type; + struct expression *size = alloc_const_expression(node->pos, count); + unsigned int bit_size = array_element_offset(elem_type->bit_size, count); + + *arr_new = *arr_ori; + arr_new->bit_size = bit_size; + arr_new->array_size = size; + node->array_size = size; + node->ctype.base_type = arr_new; + + return bit_size; +} + static struct symbol * examine_node_type(struct symbol *sym) { struct symbol *base_type = examine_base_type(sym); @@ -393,7 +410,7 @@ static struct symbol * examine_node_type(struct symbol *sym) int count = count_array_initializer(node_type, initializer); if (node_type && node_type->bit_size >= 0) - bit_size = array_element_offset(node_type->bit_size, count); + bit_size = implicit_array_size(sym, count); } } diff --git a/validation/Waddress-weak.c b/validation/Waddress-weak.c index 1fe8d33c..ad2cb13a 100644 --- a/validation/Waddress-weak.c +++ b/validation/Waddress-weak.c @@ -9,11 +9,13 @@ int test_addr_weak_fun(void) if ( &arr) return 1; if ( fun) return 1; if ( &fun) return 1; + if ( *fun) return 1; if (!&var) return 0; if (! arr) return 0; if (!&arr) return 0; if (! fun) return 0; if (!&fun) return 0; + if (!*fun) return 0; return -1; } diff --git a/validation/Waddress.c b/validation/Waddress.c index 10556c3a..441cdb1c 100644 --- a/validation/Waddress.c +++ b/validation/Waddress.c @@ -15,6 +15,7 @@ lab: if (&arr) return 1; if (fun) return 1; if (&fun) return 1; + if (*fun) return 1; if (&var) return 1; if (&arg) return 1; if (&&lab) return 1; @@ -35,6 +36,7 @@ lab: if (!&arr) return 0; if (!fun) return 0; if (!&fun) return 0; + if (!*fun) return 0; if (!&var) return 0; if (!&arg) return 0; if (!&&lab) return 0; @@ -62,6 +64,8 @@ lab: if (0 == fun) return 0; if (&fun == 0) return 0; if (0 == &fun) return 0; + if (*fun == 0) return 0; + if (0 == *fun) return 0; if (&var == 0) return 0; if (0 == &var) return 0; if (&arg == 0) return 0; diff --git a/validation/array-implicit-size.c b/validation/array-implicit-size.c new file mode 100644 index 00000000..7011008b --- /dev/null +++ b/validation/array-implicit-size.c @@ -0,0 +1,26 @@ +static int array[] = { 0, 1, 2, 3, }; +_Static_assert(sizeof(array) == 4 * sizeof(int), "size of array"); + + +typedef int table_t[]; +static table_t tbl2 = { + 0, + 1, +}; +_Static_assert(sizeof(tbl2) == 2 * sizeof(int), "size of tbl2"); + +static table_t tbl1 = { + 0, +}; +_Static_assert(sizeof(tbl1) == 1 * sizeof(int), "size of tbl1"); + +static table_t tbl3 = { + 0, + 1, + 2, +}; +_Static_assert(sizeof(tbl3) == 3 * sizeof(int), "size of tbl3"); + +/* + * check-name: array-implicit-size + */ diff --git a/validation/bug-bad-type.c b/validation/bug-bad-type.c new file mode 100644 index 00000000..0e00efef --- /dev/null +++ b/validation/bug-bad-type.c @@ -0,0 +1,18 @@ +struct s { + int i; +}; + +long a; +void foo(void) +{ + (struct s) { .i = (foo - a), }; +} + +/* + * check-name: bug-bad-type + * + * check-error-start +bug-bad-type.c:5:6: warning: symbol 'a' was not declared. Should it be static? +bug-bad-type.c:8:32: error: arithmetics on pointers to functions + * check-error-end + */ diff --git a/validation/builtin-arith.c b/validation/builtin-arith.c new file mode 100644 index 00000000..d08c93da --- /dev/null +++ b/validation/builtin-arith.c @@ -0,0 +1,52 @@ + + +void test(void (*fun)(void)); +void test(void (*fun)(void)) +{ + typedef typeof(__builtin_trap) t; // OK + void (*f)(void); + int i; + + f = __builtin_trap; + f = &__builtin_trap; + f = *__builtin_trap; // OK for GCC + f = __builtin_trap + 0; + f = __builtin_trap + 1; + f = __builtin_trap - 1; + + // (void) __builtin_trap; + f = (void*) __builtin_trap; + f = (unsigned long) __builtin_trap; + + i = !__builtin_trap; + i = (__builtin_trap > fun); + i = (__builtin_trap == fun); + i = (fun < __builtin_trap); + i = (fun == __builtin_trap); + + __builtin_trap - fun; + fun - __builtin_trap; +} + +/* + * 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:27:24: error: subtraction of functions? Share your drugs +builtin-arith.c:28:13: error: subtraction of functions? Share your drugs + * check-error-end + */ diff --git a/validation/constexpr-preop.c b/validation/constexpr-preop.c index 4b54defd..3fd57745 100644 --- a/validation/constexpr-preop.c +++ b/validation/constexpr-preop.c @@ -25,5 +25,7 @@ constexpr-preop.c:8:4: error: bad constant expression constexpr-preop.c:9:4: error: bad constant expression constexpr-preop.c:14:4: error: bad integer constant expression constexpr-preop.c:15:4: error: bad integer constant expression +constexpr-preop.c:10:4: error: index out of bounds in initializer +constexpr-preop.c:11:4: error: index out of bounds in initializer * check-error-end */ diff --git a/validation/empty-expr.c b/validation/empty-expr.c new file mode 100644 index 00000000..61decf73 --- /dev/null +++ b/validation/empty-expr.c @@ -0,0 +1,26 @@ +static int foo(void) +{ + switch () { + case 0: + return 0; + default: + return 1; + } +} + +static int bar(void) +{ + if () + return 0; + else + return 1; +} + +/* + * check-name: empty expression + * + * check-error-start +empty-expr.c:3:17: error: an expression is expected before ')' +empty-expr.c:13:13: error: an expression is expected before ')' + * check-error-end + */ diff --git a/validation/function-pointer-type.c b/validation/function-pointer-type.c new file mode 100644 index 00000000..ebc4007b --- /dev/null +++ b/validation/function-pointer-type.c @@ -0,0 +1,12 @@ +extern int fun(void); + +void fa(void) { int (*f)(void); f = &fun; } +void f0(void) { int (*f)(void); f = fun; } // C99,C11 6.3.2.1p4 +void f1(void) { int (*f)(void); f = *fun; } // C99,C11 6.5.3.2p4 +void f2(void) { int (*f)(void); f = **fun; } // C99,C11 6.5.3.2p4 +void f3(void) { int (*f)(void); f = ***fun; } // C99,C11 6.5.3.2p4 + +/* + * check-name: type of function pointers + * check-command: sparse -Wno-decl $file + */ diff --git a/validation/linear/call-basic.c b/validation/linear/call-basic.c new file mode 100644 index 00000000..60517e2e --- /dev/null +++ b/validation/linear/call-basic.c @@ -0,0 +1,57 @@ +extern int fun(int a); + +int symbol(int a) +{ + fun(a); +} + +int pointer0(int a, int (*fun)(int)) +{ + fun(a); +} + +int pointer1(int a, int (*fun)(int)) +{ + (*fun)(a); +} + +int builtin(int a) +{ + __builtin_popcount(a); +} + +/* + * check-name: basic function calls + * check-command: test-linearize -Wno-decl $file + * + * check-output-start +symbol: +.L0: + <entry-point> + call.32 %r2 <- fun, %arg1 + ret.32 %r2 + + +pointer0: +.L2: + <entry-point> + call.32 %r5 <- %arg2, %arg1 + ret.32 %r5 + + +pointer1: +.L4: + <entry-point> + call.32 %r8 <- %arg2, %arg1 + ret.32 %r8 + + +builtin: +.L6: + <entry-point> + call.32 %r11 <- __builtin_popcount, %arg1 + ret.32 %r11 + + + * check-output-end + */ diff --git a/validation/linear/call-builtin.c b/validation/linear/call-builtin.c new file mode 100644 index 00000000..b1511359 --- /dev/null +++ b/validation/linear/call-builtin.c @@ -0,0 +1,17 @@ +typedef unsigned int u32; + +u32 ff(u32 a) { return __builtin_popcount(a); } + +u32 f0(u32 a) { return (__builtin_popcount)(a); } +u32 f1(u32 a) { return (*__builtin_popcount)(a); } // C99,C11 6.5.3.2p4 +u32 f2(u32 a) { return (**__builtin_popcount)(a); } // C99,C11 6.5.3.2p4 +u32 f3(u32 a) { return (***__builtin_popcount)(a); } // C99,C11 6.5.3.2p4 + +/* + * check-name: builtin calls + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-excludes: load + * check-output-pattern(5): call\..*__builtin_.*, %arg1 + */ diff --git a/validation/linear/call-casted-pointer.c b/validation/linear/call-casted-pointer.c new file mode 100644 index 00000000..610d6748 --- /dev/null +++ b/validation/linear/call-casted-pointer.c @@ -0,0 +1,31 @@ +typedef int (*fun_t)(void*); + +int foo(void *a, void *fun) +{ + return ((fun_t)fun)(a); +} + +int bar(void *a, void *fun) +{ + return ((int (*)(void *))fun)(a); +} + +int qux(void *a, void *fun) +{ + return (*(fun_t)fun)(a); +} + +int quz(void *a, void *fun) +{ + return (*(int (*)(void *))fun)(a); +} + +/* + * check-name: call via casted function pointer + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-excludes: load + * check-output-pattern(4): ptrcast\..* %arg2 + * check-output-pattern(4): call\..* %arg1 + */ diff --git a/validation/linear/call-complex-pointer.c b/validation/linear/call-complex-pointer.c new file mode 100644 index 00000000..ea8232f1 --- /dev/null +++ b/validation/linear/call-complex-pointer.c @@ -0,0 +1,32 @@ +int foo(int p, int (*f0)(int), int (*f1)(int), int arg) +{ + return (p ? f0 : f1)(arg); +} + +/* + * check-name: call-complex-pointer + * check-command: test-linearize -m64 -Wno-decl $file + * + * check-output-start +foo: +.L0: + <entry-point> + cbr %arg1, .L2, .L3 + +.L2: + phisrc.64 %phi1 <- %arg2 + br .L4 + +.L3: + ptrcast.64 %r5 <- (64) %arg3 + phisrc.64 %phi2 <- %r5 + br .L4 + +.L4: + phi.64 %r6 <- %phi1, %phi2 + call.32 %r7 <- %r6, %arg4 + ret.32 %r7 + + + * check-output-end + */ diff --git a/validation/linear/call-direct.c b/validation/linear/call-direct.c new file mode 100644 index 00000000..52f86306 --- /dev/null +++ b/validation/linear/call-direct.c @@ -0,0 +1,17 @@ +extern int fun(void); + +int ff(void) { return fun(); } + +int f0(void) { return (fun)(); } +int f1(void) { return (*fun)(); } // C99,C11 6.5.3.2p4 +int f2(void) { return (**fun)(); } // C99,C11 6.5.3.2p4 +int f3(void) { return (***fun)(); } // C99,C11 6.5.3.2p4 + +/* + * check-name: direct calls + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-excludes: load + * check-output-pattern(5): call\..* fun + */ diff --git a/validation/linear/call-indirect.c b/validation/linear/call-indirect.c new file mode 100644 index 00000000..1275910c --- /dev/null +++ b/validation/linear/call-indirect.c @@ -0,0 +1,15 @@ +int gg(int (*fun)(void)) { return fun(); } + +int g0(int (*fun)(void)) { return (fun)(); } +int g1(int (*fun)(void)) { return (*fun)(); } // C99,C11 6.5.3.2p4 +int g2(int (*fun)(void)) { return (**fun)(); } // C99,C11 6.5.3.2p4 +int g3(int (*fun)(void)) { return (***fun)(); } // C99,C11 6.5.3.2p4 + +/* + * check-name: indirect calls + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-excludes: load + * check-output-pattern(5): call\..* %arg1 + */ diff --git a/validation/linear/call-inline.c b/validation/linear/call-inline.c new file mode 100644 index 00000000..a33f0a1c --- /dev/null +++ b/validation/linear/call-inline.c @@ -0,0 +1,18 @@ +static inline int fun(void) { return 42; } + +int fi(void) { return fun(); } + +int i0(void) { return (fun)(); } +int i1(void) { return (*fun)(); } // C99,C11 6.5.3.2p4 +int i2(void) { return (**fun)(); } // C99,C11 6.5.3.2p4 +int i3(void) { return (***fun)(); } // C99,C11 6.5.3.2p4 + +/* + * check-name: inline calls + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-excludes: load + * check-output-excludes: call + * check-output-pattern(5): ret\..* \\$42 + */ diff --git a/validation/linear/degen-function.c b/validation/linear/degen-function.c index 6dd3123b..4fb2d564 100644 --- a/validation/linear/degen-function.c +++ b/validation/linear/degen-function.c @@ -4,6 +4,7 @@ typedef int (*fun_t)(int); fun_t fa(void) { return &fun; } fun_t f0(void) { return fun; } +fun_t f1(void) { return *fun; } /* * check-name: degen-function @@ -34,5 +35,17 @@ f0: ret.64 %r3 +f1: +.L4: + <entry-point> + symaddr.64 %r5 <- fun + phisrc.64 %phi3(return) <- %r5 + br .L5 + +.L5: + phi.64 %r6 <- %phi3(return) + ret.64 %r5 + + * check-output-end */ diff --git a/validation/linear/deref-ptr-ptr.c b/validation/linear/deref-ptr-ptr.c new file mode 100644 index 00000000..963acd36 --- /dev/null +++ b/validation/linear/deref-ptr-ptr.c @@ -0,0 +1,26 @@ +char *foo(char **pfmt) +{ + return ++*pfmt; +} + +/* + * check-name: deref-ptr-ptr + * check-command: test-linearize -m64 -Wno-decl $file + * + * check-output-excludes: load[^.] + * check-output-contains: load\. + * check-output-excludes: store[^.] + * check-output-contains: store\. + * + * check-output-start +foo: +.L0: + <entry-point> + load.64 %r2 <- 0[%arg1] + add.64 %r3 <- %r2, $1 + store.64 %r3 -> 0[%arg1] + ret.64 %r3 + + + * check-output-end + */ diff --git a/validation/linear/unexamined-base-type.c b/validation/linear/unexamined-base-type.c new file mode 100644 index 00000000..96aee3f0 --- /dev/null +++ b/validation/linear/unexamined-base-type.c @@ -0,0 +1,36 @@ +# define __force __attribute__((force)) + +struct s { + int a; +}; + +static int foo(struct s *s) +{ + return (*((typeof(s->a) __force *) &s->a)) & 1; +} + +static void bar(struct s *d, struct s *s1, struct s *s2) +{ + *d = *s1, *d = *s2; +} + +/* + * check-name: unexamined base type + * check-command: test-linearize -Wno-decl $file + * check-description: + * Test case for missing examine in evaluate_dereference()'s + * target base type. In this case, the loaded value has a + * a null size, giving the wrongly generated code for foo(): + * ptrcast.64 %r3 <- (64) %arg1 + * load %r4 <- 0[%r3] + * ^^^ !! WRONG !! + * cast.32 %r5 <- (0) %r4 + * ^^^ !! WRONG !! + * and.32 %r6 <- %r5, $1 + * ret.32 %r6 + * + * check-output-ignore + * check-output-excludes: load[^.] + * check-output-excludes: cast\..*(0) + * check-output-excludes: store[^.] + */ diff --git a/validation/optim/call-complex-pointer.c b/validation/optim/call-complex-pointer.c new file mode 100644 index 00000000..6cfeb6ab --- /dev/null +++ b/validation/optim/call-complex-pointer.c @@ -0,0 +1,13 @@ +int foo(int p, int (*f0)(int), int (*f1)(int), int arg) +{ + return (p ? f0 : f1)(arg); +} +/* + * check-name: call-complex-pointer + * check-command: test-linearize -Wno-decl $file + * check-known-to-fail + * + * check-output-ignore + * check-output-excludes: ptrcast\. + * check-output-contains: select\. + */ diff --git a/validation/optim/cse-fcmp.c b/validation/optim/cse-fcmp.c new file mode 100644 index 00000000..f2a73f57 --- /dev/null +++ b/validation/optim/cse-fcmp.c @@ -0,0 +1,19 @@ +extern void fun(void); + +int foo(double a, double b) +{ + if (a < b) + fun(); + if (a < b) + return 1; + + return 0; +} + +/* + * check-name: cse-fcmp + * check-command: test-linearize -Wno-decl $file + * + * check-output-ignore + * check-output-pattern(1): fcmp + */ diff --git a/validation/sizeof-builtin.c b/validation/sizeof-builtin.c new file mode 100644 index 00000000..7123e4de --- /dev/null +++ b/validation/sizeof-builtin.c @@ -0,0 +1,15 @@ +int test(void); +int test(void) +{ + return sizeof &__builtin_trap; +} + +/* + * check-name: sizeof-builtin + * check-command: sparse -Wno-decl $file + * check-known-to-fail + * + * check-error-start +sizeof-function.c:4:16: error: expression using addressof on a builtin function + * check-error-end + */ diff --git a/validation/sizeof-function.c b/validation/sizeof-function.c new file mode 100644 index 00000000..27d535d4 --- /dev/null +++ b/validation/sizeof-function.c @@ -0,0 +1,49 @@ +extern int fun(void); +extern int (*ptr)(void); + +static inline int inl(int *a) +{ + return *a + 1; +} + + +int test(void); +int test(void) +{ + unsigned int s = 0; + + // OK + s += sizeof &fun; + s += sizeof ptr; + s += sizeof &ptr; + s += sizeof &inl; + + // KO + s += sizeof fun; + s += sizeof *fun; + + s += sizeof *ptr; + + s += sizeof inl; + s += sizeof *inl; + + s += sizeof __builtin_trap; + s += sizeof *__builtin_trap; + + return s; +} + +/* + * check-name: sizeof-function + * check-command: sparse -Wno-decl $file + * + * check-error-start +sizeof-function.c:22:14: warning: expression using sizeof on a function +sizeof-function.c:23:14: warning: expression using sizeof on a function +sizeof-function.c:25:14: warning: expression using sizeof on a function +sizeof-function.c:27:14: warning: expression using sizeof on a function +sizeof-function.c:28:14: warning: expression using sizeof on a function +sizeof-function.c:30:14: warning: expression using sizeof on a function +sizeof-function.c:31:14: warning: expression using sizeof on a function + * check-error-end + */ |
