Merge branches 'dump-macros-v2', 'fix-predefined-size', 'fix-bool-context', 'fix-missing-reload', 'fix-insert-branch', 'fix-NULL-type', 'testsuite-clean', 'fix-bitfield-init-v3' and 'fdump-linearize' into tip
* fix missing reload
* fix boolean context for OP_AND_BOOL & OP_OR_BOOL
* fix bitfields implicit zero initializer.
* fix: kill old branch in insert_branch()
* returns the correct type when evaluating NULL
* use internal size for __INT_MAX__ & friends
* testsuite: cleanup result files
* add support for '-fdump-linearize[=only]'
* add support for '-fmem-report'
* add support for '-dD'
Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
diff --git a/Makefile b/Makefile
index 76902b7..b32fc96 100644
--- a/Makefile
+++ b/Makefile
@@ -106,6 +106,7 @@
expression.o show-parse.o evaluate.o expand.o inline.o linearize.o \
char.o sort.o allocate.o compat-$(OS).o ptrlist.o \
builtin.o \
+ stats.o \
flow.o cse.o simplify.o memops.o liveness.o storage.o unssa.o dissect.o
LIB_FILE= libsparse.a
diff --git a/allocate.c b/allocate.c
index f597caf..daa4ac2 100644
--- a/allocate.c
+++ b/allocate.c
@@ -127,6 +127,14 @@
(double) x->useful_bytes / x->allocations);
}
+void get_allocator_stats(struct allocator_struct *x, struct allocator_stats *s)
+{
+ s->name = x->name;
+ s->allocations = x->allocations;
+ s->useful_bytes = x->useful_bytes;
+ s->total_bytes = x->total_bytes;
+}
+
ALLOCATOR(ident, "identifiers");
ALLOCATOR(token, "tokens");
ALLOCATOR(context, "contexts");
diff --git a/allocate.h b/allocate.h
index 9e51ed7..64bb6dd 100644
--- a/allocate.h
+++ b/allocate.h
@@ -17,16 +17,25 @@
unsigned int allocations, total_bytes, useful_bytes;
};
+struct allocator_stats {
+ const char *name;
+ unsigned int allocations;
+ unsigned long total_bytes, useful_bytes;
+};
+
extern void protect_allocations(struct allocator_struct *desc);
extern void drop_all_allocations(struct allocator_struct *desc);
extern void *allocate(struct allocator_struct *desc, unsigned int size);
extern void free_one_entry(struct allocator_struct *desc, void *entry);
extern void show_allocations(struct allocator_struct *);
+extern void get_allocator_stats(struct allocator_struct *, struct allocator_stats *);
+extern void show_allocation_stats(void);
#define __DECLARE_ALLOCATOR(type, x) \
extern type *__alloc_##x(int); \
extern void __free_##x(type *); \
extern void show_##x##_alloc(void); \
+ extern void get_##x##_stats(struct allocator_stats *); \
extern void clear_##x##_alloc(void); \
extern void protect_##x##_alloc(void);
#define DECLARE_ALLOCATOR(x) __DECLARE_ALLOCATOR(struct x, x)
@@ -48,6 +57,10 @@
{ \
show_allocations(&x##_allocator); \
} \
+ void get_##x##_stats(struct allocator_stats *s) \
+ { \
+ get_allocator_stats(&x##_allocator, s); \
+ } \
void clear_##x##_alloc(void) \
{ \
drop_all_allocations(&x##_allocator); \
diff --git a/char.c b/char.c
index e2b3863..c52521b 100644
--- a/char.c
+++ b/char.c
@@ -84,7 +84,7 @@
end = p + type - TOKEN_WIDE_CHAR;
}
p = parse_escape(p, &v, end,
- type < TOKEN_WIDE_CHAR ? bits_in_char : 32, token->pos);
+ type < TOKEN_WIDE_CHAR ? bits_in_char : bits_in_wchar, token->pos);
if (p != end)
warning(token->pos,
"multi-character character constant");
@@ -113,7 +113,7 @@
done = next;
}
}
- bits = is_wide ? 32 : bits_in_char;
+ bits = is_wide ? bits_in_wchar : bits_in_char;
while (token != done) {
unsigned v;
const char *p = token->string->data;
diff --git a/evaluate.c b/evaluate.c
index 9768579..87e2862 100644
--- a/evaluate.c
+++ b/evaluate.c
@@ -1345,6 +1345,12 @@
return 1;
} else if (!(sclass & TYPE_RESTRICT))
goto Cast;
+ if (t == &bool_ctype) {
+ if (is_fouled_type(s))
+ warning((*rp)->pos, "%s degrades to integer",
+ show_typename(s->ctype.base_type));
+ goto Cast;
+ }
*typediff = "different base types";
return 0;
}
@@ -2851,7 +2857,7 @@
expr->type = EXPR_VALUE;
expr->ctype = &null_ctype;
expr->value = 0;
- return ctype;
+ return expr->ctype;
}
}
}
diff --git a/flow.c b/flow.c
index 4b32f5c..e0932ed 100644
--- a/flow.c
+++ b/flow.c
@@ -226,7 +226,6 @@
if (bb_list_size(target->parents) != 1)
return retval;
insert_branch(target, insn, final);
- kill_instruction(insn);
return 1;
}
@@ -834,6 +833,8 @@
DELETE_CURRENT_PTR(bb);
} END_FOR_EACH_PTR(bb);
PACK_PTR_LIST(&ep->bbs);
+
+ repeat_phase &= ~REPEAT_CFG_CLEANUP;
}
static int rewrite_parent_branch(struct basic_block *bb, struct basic_block *old, struct basic_block *new)
diff --git a/flow.h b/flow.h
index 31ed80d..b592ad4 100644
--- a/flow.h
+++ b/flow.h
@@ -7,6 +7,7 @@
#define REPEAT_CSE 1
#define REPEAT_SYMBOL_CLEANUP 2
+#define REPEAT_CFG_CLEANUP 3
struct entrypoint;
struct instruction;
diff --git a/ident-list.h b/ident-list.h
index 8cc66a5..1308757 100644
--- a/ident-list.h
+++ b/ident-list.h
@@ -25,76 +25,35 @@
IDENT(volatile); IDENT(__volatile); IDENT(__volatile__);
IDENT(double);
+/* C storage classes. They get marked as reserved when initialized */
+IDENT(static);
+
+/* C99 keywords */
+IDENT(restrict); IDENT(__restrict); IDENT(__restrict__);
+IDENT(_Bool);
+IDENT_RESERVED(_Complex);
+IDENT_RESERVED(_Imaginary);
+
+/* C11 keywords */
+IDENT(_Alignas);
+IDENT_RESERVED(_Alignof);
+IDENT_RESERVED(_Atomic);
+IDENT_RESERVED(_Generic);
+IDENT(_Noreturn);
+IDENT_RESERVED(_Static_assert);
+IDENT(_Thread_local);
+
/* Special case for L'\t' */
IDENT(L);
/* Extended gcc identifiers */
IDENT(asm); IDENT_RESERVED(__asm); IDENT_RESERVED(__asm__);
IDENT(alignof); IDENT_RESERVED(__alignof); IDENT_RESERVED(__alignof__);
-IDENT_RESERVED(_Alignof);
IDENT_RESERVED(__sizeof_ptr__);
IDENT_RESERVED(__builtin_types_compatible_p);
IDENT_RESERVED(__builtin_offsetof);
IDENT_RESERVED(__label__);
-/* Attribute names */
-IDENT(packed); IDENT(__packed__);
-IDENT(aligned); IDENT(__aligned__);
-IDENT(nocast);
-IDENT(noderef);
-IDENT(safe);
-IDENT(force);
-IDENT(address_space);
-IDENT(context);
-IDENT(mode); IDENT(__mode__);
-IDENT(QI); IDENT(__QI__);
-IDENT(HI); IDENT(__HI__);
-IDENT(SI); IDENT(__SI__);
-IDENT(DI); IDENT(__DI__);
-IDENT(word); IDENT(__word__);
-IDENT(format); IDENT(__format__);
-IDENT(section); IDENT(__section__);
-IDENT(unused); IDENT(__unused__);
-IDENT(const); IDENT(__const); IDENT(__const__);
-IDENT(used); IDENT(__used__);
-IDENT(warn_unused_result); IDENT(__warn_unused_result__);
-IDENT(noinline); IDENT(__noinline__);
-IDENT(deprecated); IDENT(__deprecated__);
-IDENT(noreturn); IDENT(__noreturn__);
-IDENT(regparm); IDENT(__regparm__);
-IDENT(weak); IDENT(__weak__);
-IDENT(no_instrument_function); IDENT(__no_instrument_function__);
-IDENT(sentinel); IDENT(__sentinel__);
-IDENT(alias); IDENT(__alias__);
-IDENT(pure); IDENT(__pure__);
-IDENT(always_inline); IDENT(__always_inline__);
-IDENT(syscall_linkage); IDENT(__syscall_linkage__);
-IDENT(visibility); IDENT(__visibility__);
-IDENT(bitwise); IDENT(__bitwise__);
-IDENT(model); IDENT(__model__);
-IDENT(format_arg); IDENT(__format_arg__);
-IDENT(nothrow); IDENT(__nothrow); IDENT(__nothrow__);
-IDENT(__transparent_union__);
-IDENT(malloc);
-IDENT(__malloc__);
-IDENT(nonnull); IDENT(__nonnull); IDENT(__nonnull__);
-IDENT(constructor); IDENT(__constructor__);
-IDENT(destructor); IDENT(__destructor__);
-IDENT(cold); IDENT(__cold__);
-IDENT(hot); IDENT(__hot__);
-IDENT(cdecl); IDENT(__cdecl__);
-IDENT(stdcall); IDENT(__stdcall__);
-IDENT(fastcall); IDENT(__fastcall__);
-IDENT(dllimport); IDENT(__dllimport__);
-IDENT(dllexport); IDENT(__dllexport__);
-IDENT(restrict); IDENT(__restrict); IDENT(__restrict__);
-IDENT(artificial); IDENT(__artificial__);
-IDENT(leaf); IDENT(__leaf__);
-IDENT(vector_size); IDENT(__vector_size__);
-IDENT(error); IDENT(__error__);
-IDENT(static);
-
-
/* Preprocessor idents. Direct use of __IDENT avoids mentioning the keyword
* itself by name, preventing these tokens from expanding when compiling
* sparse. */
diff --git a/lib.c b/lib.c
index 05f37a0..e1e6a1c 100644
--- a/lib.c
+++ b/lib.c
@@ -248,9 +248,14 @@
int Wunknown_attribute = 1;
int Wvla = 1;
+int dump_macro_defs = 0;
+
int dbg_entry = 0;
int dbg_dead = 0;
+int fmem_report = 0;
+int fdump_linearize;
+
int preprocess_only;
static enum { STANDARD_C89,
@@ -453,6 +458,25 @@
}
+static int handle_simple_switch(const char *arg, const char *name, int *flag)
+{
+ int val = 1;
+
+ // Prefixe "no-" mean to turn flag off.
+ if (strncmp(arg, "no-", 3) == 0) {
+ arg += 3;
+ val = 0;
+ }
+
+ if (strcmp(arg, name) == 0) {
+ *flag = val;
+ return 1;
+ }
+
+ // not handled
+ return 0;
+}
+
static char **handle_switch_o(char *arg, char **next)
{
if (!strcmp (arg, "o")) { // "-o foo"
@@ -569,6 +593,19 @@
return next;
}
+static struct warning dumps[] = {
+ { "D", &dump_macro_defs},
+};
+
+static char **handle_switch_d(char *arg, char **next)
+{
+ char ** ret = handle_onoff_switch(arg, next, dumps, ARRAY_SIZE(dumps));
+ if (ret)
+ return ret;
+
+ return next;
+}
+
static void handle_onoff_switch_finalize(const struct warning warnings[], int n)
{
@@ -647,19 +684,38 @@
return next;
}
+static char **handle_switch_fdump(char *arg, char **next)
+{
+ if (!strncmp(arg, "linearize", 9)) {
+ arg += 9;
+ if (*arg == '\0')
+ fdump_linearize = 1;
+ else if (!strcmp(arg, "=only"))
+ fdump_linearize = 2;
+ else
+ goto err;
+ }
+
+ /* ignore others flags */
+ return next;
+
+err:
+ die("error: unknown flag \"-fdump-%s\"", arg);
+}
+
static char **handle_switch_f(char *arg, char **next)
{
arg++;
if (!strncmp(arg, "tabstop=", 8))
return handle_switch_ftabstop(arg+8, next);
+ if (!strncmp(arg, "dump-", 5))
+ return handle_switch_fdump(arg+5, next);
/* handle switches w/ arguments above, boolean and only boolean below */
+ if (handle_simple_switch(arg, "mem-report", &fmem_report))
+ return next;
- if (!strncmp(arg, "no-", 3)) {
- arg += 3;
- }
- /* handle switch here.. */
return next;
}
@@ -802,6 +858,7 @@
switch (*arg) {
case 'a': return handle_switch_a(arg, next);
case 'D': return handle_switch_D(arg, next);
+ case 'd': return handle_switch_d(arg, next);
case 'E': return handle_switch_E(arg, next);
case 'f': return handle_switch_f(arg, next);
case 'g': return handle_switch_g(arg, next);
@@ -834,11 +891,16 @@
add_pre_buffer("#weak_define __SIZEOF_%s__ %d\n", name, bits/8);
}
-static void predefined_type_size(const char *name, const char *suffix, unsigned bits)
+static void predefined_max(const char *name, const char *suffix, unsigned bits)
{
unsigned long long max = (1ULL << (bits - 1 )) - 1;
add_pre_buffer("#weak_define __%s_MAX__ %#llx%s\n", name, max, suffix);
+}
+
+static void predefined_type_size(const char *name, const char *suffix, unsigned bits)
+{
+ predefined_max(name, suffix, bits);
predefined_sizeof(name, bits);
}
@@ -847,6 +909,10 @@
add_pre_buffer("#define __CHECKER__ 1\n");
predefined_sizeof("SHORT", bits_in_short);
+ predefined_max("SHRT", "", bits_in_short);
+ predefined_max("SCHAR", "", bits_in_char);
+ predefined_max("WCHAR", "", bits_in_wchar);
+ add_pre_buffer("#weak_define __CHAR_BIT__ %d\n", bits_in_char);
predefined_type_size("INT", "", bits_in_int);
predefined_type_size("LONG", "L", bits_in_long);
@@ -1070,23 +1136,18 @@
add_pre_buffer("#define __OPTIMIZE__ 1\n");
if (optimize_size)
add_pre_buffer("#define __OPTIMIZE_SIZE__ 1\n");
-
- /* GCC defines these for limits.h */
- add_pre_buffer("#weak_define __SHRT_MAX__ " STRINGIFY(__SHRT_MAX__) "\n");
- add_pre_buffer("#weak_define __SCHAR_MAX__ " STRINGIFY(__SCHAR_MAX__) "\n");
- add_pre_buffer("#weak_define __INT_MAX__ " STRINGIFY(__INT_MAX__) "\n");
- add_pre_buffer("#weak_define __LONG_MAX__ " STRINGIFY(__LONG_MAX__) "\n");
- add_pre_buffer("#weak_define __LONG_LONG_MAX__ " STRINGIFY(__LONG_LONG_MAX__) "\n");
- add_pre_buffer("#weak_define __WCHAR_MAX__ " STRINGIFY(__WCHAR_MAX__) "\n");
- add_pre_buffer("#weak_define __SIZEOF_POINTER__ " STRINGIFY(__SIZEOF_POINTER__) "\n");
- add_pre_buffer("#weak_define __CHAR_BIT__ " STRINGIFY(__CHAR_BIT__) "\n");
}
static struct symbol_list *sparse_tokenstream(struct token *token)
{
+ int builtin = token && !token->pos.stream;
+
// Preprocess the stream
token = preprocess(token);
+ if (dump_macro_defs && !builtin)
+ dump_macro_definitions();
+
if (preprocess_only) {
while (!eof_token(token)) {
int prec = 1;
diff --git a/lib.h b/lib.h
index 0bf8c97..2c8529f 100644
--- a/lib.h
+++ b/lib.h
@@ -75,6 +75,7 @@
DECLARE_PTR_LIST(instruction_list, struct instruction);
DECLARE_PTR_LIST(multijmp_list, struct multijmp);
DECLARE_PTR_LIST(pseudo_list, struct pseudo);
+DECLARE_PTR_LIST(ident_list, struct ident);
DECLARE_PTR_LIST(string_list, char);
typedef struct pseudo *pseudo_t;
@@ -134,17 +135,24 @@
extern int Wunknown_attribute;
extern int Wvla;
+extern int dump_macro_defs;
+
extern int dbg_entry;
extern int dbg_dead;
+extern int fmem_report;
+extern int fdump_linearize;
+
extern int arch_m64;
extern void declare_builtin_functions(void);
extern void create_builtin_stream(void);
+extern void dump_macro_definitions(void);
extern struct symbol_list *sparse_initialize(int argc, char **argv, struct string_list **files);
extern struct symbol_list *__sparse(char *filename);
extern struct symbol_list *sparse_keep_tokens(char *filename);
extern struct symbol_list *sparse(char *filename);
+extern void report_stats(void);
static inline int symbol_list_size(struct symbol_list *list)
{
@@ -245,6 +253,11 @@
add_ptr_list(list, expr);
}
+static inline void add_ident(struct ident_list **list, struct ident *ident)
+{
+ add_ptr_list(list, ident);
+}
+
#define hashval(x) ((unsigned long)(x))
#endif
diff --git a/linearize.c b/linearize.c
index a9f36b8..7313e72 100644
--- a/linearize.c
+++ b/linearize.c
@@ -643,10 +643,10 @@
{
remove_bb_from_list(&child->parents, parent, 1);
if (!child->parents)
- kill_bb(child);
+ repeat_phase |= REPEAT_CFG_CLEANUP;
}
-/* Change a "switch" into a branch */
+/* Change a "switch" or a conditional branch into a branch */
void insert_branch(struct basic_block *bb, struct instruction *jmp, struct basic_block *target)
{
struct instruction *br, *old;
@@ -655,6 +655,7 @@
/* Remove the switch */
old = delete_last_instruction(&bb->insns);
assert(old == jmp);
+ kill_instruction(old);
br = alloc_instruction(OP_BR, 0);
br->bb = bb;
@@ -670,6 +671,9 @@
remove_parent(child, bb);
} END_FOR_EACH_PTR(child);
PACK_PTR_LIST(&bb->children);
+
+ if (repeat_phase & REPEAT_CFG_CLEANUP)
+ kill_unreachable_bbs(bb->ep);
}
@@ -843,9 +847,7 @@
struct symbol *result_type; // result ctype
struct symbol *source_type; // source ctype
pseudo_t address; // pseudo containing address ..
- pseudo_t origval; // pseudo for original value ..
- unsigned int offset, alignment; // byte offset
- unsigned int bit_size, bit_offset; // which bits
+ unsigned int offset; // byte offset
struct position pos;
};
@@ -898,9 +900,6 @@
ad->pos = expr->pos;
ad->result_type = ctype;
ad->source_type = base_type(ctype);
- ad->bit_size = ctype->bit_size;
- ad->alignment = ctype->ctype.alignment;
- ad->bit_offset = ctype->bit_offset;
if (expr->type == EXPR_PREOP && expr->op == '*')
return linearize_simple_address(ep, expr->unop, ad);
@@ -913,13 +912,8 @@
struct instruction *insn;
pseudo_t new;
- new = ad->origval;
- if (0 && new)
- return new;
-
insn = alloc_typed_instruction(OP_LOAD, ad->source_type);
new = alloc_pseudo(insn);
- ad->origval = new;
insn->target = new;
insn->offset = ad->offset;
@@ -948,9 +942,11 @@
pseudo_t store = value;
if (type_size(ad->source_type) != type_size(ad->result_type)) {
+ struct symbol *ctype = ad->result_type;
+ unsigned int shift = ctype->bit_offset;
+ unsigned int size = ctype->bit_size;
pseudo_t orig = add_load(ep, ad);
- int shift = ad->bit_offset;
- unsigned long long mask = (1ULL << ad->bit_size)-1;
+ unsigned long long mask = (1ULL << size) - 1;
if (shift) {
store = add_binary_op(ep, ad->source_type, OP_SHL, value, value_pseudo(shift));
@@ -997,14 +993,15 @@
static pseudo_t linearize_load_gen(struct entrypoint *ep, struct access_data *ad)
{
+ struct symbol *ctype = ad->result_type;
pseudo_t new = add_load(ep, ad);
- if (ad->bit_offset) {
- pseudo_t shift = value_pseudo(ad->bit_offset);
+ if (ctype->bit_offset) {
+ pseudo_t shift = value_pseudo(ctype->bit_offset);
pseudo_t newval = add_binary_op(ep, ad->source_type, OP_LSR, new, shift);
new = newval;
}
- if (ad->bit_size != type_size(ad->source_type))
+ if (ctype->bit_size != type_size(ad->source_type))
new = cast_pseudo(ep, new, ad->source_type, ad->result_type);
return new;
}
@@ -1156,6 +1153,26 @@
return opcode;
}
+static inline pseudo_t add_convert_to_bool(struct entrypoint *ep, pseudo_t src, struct symbol *type)
+{
+ pseudo_t zero;
+ int op;
+
+ if (is_bool_type(type))
+ return src;
+ zero = value_pseudo(0);
+ op = OP_SET_NE;
+ return add_binary_op(ep, &bool_ctype, op, src, zero);
+}
+
+static pseudo_t linearize_expression_to_bool(struct entrypoint *ep, struct expression *expr)
+{
+ pseudo_t dst;
+ dst = linearize_expression(ep, expr);
+ dst = add_convert_to_bool(ep, dst, expr->ctype);
+ return dst;
+}
+
static pseudo_t linearize_assignment(struct entrypoint *ep, struct expression *expr)
{
struct access_data ad = { NULL, };
@@ -1276,6 +1293,19 @@
return retval;
}
+static pseudo_t linearize_binop_bool(struct entrypoint *ep, struct expression *expr)
+{
+ pseudo_t src1, src2, dst;
+ int op = (expr->op == SPECIAL_LOGICAL_OR) ? OP_OR_BOOL : OP_AND_BOOL;
+
+ src1 = linearize_expression_to_bool(ep, expr->left);
+ src2 = linearize_expression_to_bool(ep, expr->right);
+ dst = add_binary_op(ep, &bool_ctype, op, src1, src2);
+ if (expr->ctype != &bool_ctype)
+ dst = cast_pseudo(ep, dst, &bool_ctype, expr->ctype);
+ return dst;
+}
+
static pseudo_t linearize_binop(struct entrypoint *ep, struct expression *expr)
{
pseudo_t src1, src2, dst;
@@ -1286,8 +1316,6 @@
['|'] = OP_OR, ['^'] = OP_XOR,
[SPECIAL_LEFTSHIFT] = OP_SHL,
[SPECIAL_RIGHTSHIFT] = OP_LSR,
- [SPECIAL_LOGICAL_AND] = OP_AND_BOOL,
- [SPECIAL_LOGICAL_OR] = OP_OR_BOOL,
};
int op;
@@ -1570,6 +1598,8 @@
return linearize_call_expression(ep, expr);
case EXPR_BINOP:
+ if (expr->op == SPECIAL_LOGICAL_AND || expr->op == SPECIAL_LOGICAL_OR)
+ return linearize_binop_bool(ep, expr);
return linearize_binop(ep, expr);
case EXPR_LOGICAL:
@@ -1634,6 +1664,21 @@
sym->initialized = 1;
ad.address = symbol_pseudo(ep, sym);
+
+ if (sym->initializer && !is_scalar_type(sym)) {
+ // default zero initialization [6.7.9.21]
+ // FIXME: this init the whole aggregate while
+ // 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.result_type = sym;
+ ad.source_type = base_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;
@@ -2170,6 +2215,12 @@
add_one_insn(ep, insn);
}
+ if (fdump_linearize) {
+ if (fdump_linearize == 2)
+ return ep;
+ show_entry(ep);
+ }
+
/*
* Do trivial flow simplification - branches to
* branches, kill dead basicblocks etc
diff --git a/parse.c b/parse.c
index 80f0337..b08d4ac 100644
--- a/parse.c
+++ b/parse.c
@@ -73,6 +73,7 @@
static struct token *parse_range_statement(struct token *token, struct statement *stmt);
static struct token *parse_asm_statement(struct token *token, struct statement *stmt);
static struct token *toplevel_asm_declaration(struct token *token, struct symbol_list **list);
+static struct token *parse_static_assert(struct token *token, struct symbol_list **unused);
typedef struct token *attr_t(struct token *, struct symbol *,
struct decl_state *);
@@ -328,6 +329,10 @@
.toplevel = toplevel_asm_declaration,
};
+static struct symbol_op static_assert_op = {
+ .toplevel = parse_static_assert,
+};
+
static struct symbol_op packed_op = {
.attribute = attribute_packed,
};
@@ -402,6 +407,7 @@
.to_mode = to_word_mode
};
+/* Using NS_TYPEDEF will also make the keyword a reserved one */
static struct init_keyword {
const char *name;
enum namespace ns;
@@ -466,6 +472,9 @@
{ "__restrict", NS_TYPEDEF, .op = &restrict_op},
{ "__restrict__", NS_TYPEDEF, .op = &restrict_op},
+ /* Static assertion */
+ { "_Static_assert", NS_KEYWORD, .op = &static_assert_op },
+
/* Storage class */
{ "auto", NS_TYPEDEF, .op = &auto_op },
{ "register", NS_TYPEDEF, .op = ®ister_op },
@@ -1945,6 +1954,10 @@
static struct token *struct_declaration_list(struct token *token, struct symbol_list **list)
{
while (!match_op(token, '}')) {
+ if (match_ident(token, &_Static_assert_ident)) {
+ token = parse_static_assert(token, NULL);
+ continue;
+ }
if (!match_op(token, ';'))
token = declaration_list(token, list);
if (!match_op(token, ';')) {
@@ -2093,6 +2106,33 @@
return token;
}
+static struct token *parse_static_assert(struct token *token, struct symbol_list **unused)
+{
+ struct expression *cond = NULL, *message = NULL;
+
+ token = expect(token->next, '(', "after _Static_assert");
+ token = constant_expression(token, &cond);
+ if (!cond)
+ sparse_error(token->pos, "Expected constant expression");
+ token = expect(token, ',', "after conditional expression in _Static_assert");
+ token = parse_expression(token, &message);
+ if (!message || message->type != EXPR_STRING) {
+ struct position pos;
+
+ pos = message ? message->pos : token->pos;
+ sparse_error(pos, "bad or missing string literal");
+ cond = NULL;
+ }
+ token = expect(token, ')', "after diagnostic message in _Static_assert");
+
+ token = expect(token, ';', "after _Static_assert()");
+
+ if (cond && !const_expression_value(cond) && cond->type == EXPR_VALUE)
+ sparse_error(cond->pos, "static assertion failed: %s",
+ show_string(message->string));
+ return token;
+}
+
/* Make a statement out of an expression */
static struct statement *make_statement(struct expression *expr)
{
@@ -2474,6 +2514,10 @@
break;
if (match_op(token, '}'))
break;
+ if (match_ident(token, &_Static_assert_ident)) {
+ token = parse_static_assert(token, NULL);
+ continue;
+ }
if (lookup_type(token)) {
if (seen_statement) {
warning(token->pos, "mixing declarations and code");
@@ -2819,7 +2863,7 @@
unsigned long mod;
int is_typedef;
- /* Top-level inline asm? */
+ /* Top-level inline asm or static assertion? */
if (token_type(token) == TOKEN_IDENT) {
struct symbol *s = lookup_keyword(token->ident, NS_KEYWORD);
if (s && s->op->toplevel)
diff --git a/pre-process.c b/pre-process.c
index 7c57ba1..74414df 100644
--- a/pre-process.c
+++ b/pre-process.c
@@ -44,6 +44,7 @@
#include "expression.h"
#include "scope.h"
+static struct ident_list *macros; // only needed for -dD
static int false_nesting = 0;
static int counter_macro = 0; // __COUNTER__ expansion
@@ -1351,6 +1352,7 @@
if (!sym || sym->scope != file_scope) {
sym = alloc_symbol(left->pos, SYM_NODE);
bind_symbol(sym, name, NS_MACRO);
+ add_ident(¯os, name);
ret = 0;
}
@@ -2010,3 +2012,56 @@
return token;
}
+
+static void dump_macro(struct symbol *sym)
+{
+ int nargs = sym->arglist ? sym->arglist->count.normal : 0;
+ struct token *args[nargs];
+ struct token *token;
+
+ printf("#define %s", show_ident(sym->ident));
+ token = sym->arglist;
+ if (token) {
+ const char *sep = "";
+ int narg = 0;
+ putchar('(');
+ for (; !eof_token(token); token = token->next) {
+ if (token_type(token) == TOKEN_ARG_COUNT)
+ continue;
+ printf("%s%s", sep, show_token(token));
+ args[narg++] = token;
+ sep = ", ";
+ }
+ putchar(')');
+ }
+ putchar(' ');
+
+ token = sym->expansion;
+ while (!eof_token(token)) {
+ struct token *next = token->next;
+ switch (token_type(token)) {
+ case TOKEN_UNTAINT:
+ break;
+ case TOKEN_MACRO_ARGUMENT:
+ token = args[token->argnum];
+ /* fall-through */
+ default:
+ printf("%s", show_token(token));
+ if (next->pos.whitespace)
+ putchar(' ');
+ }
+ token = next;
+ }
+ putchar('\n');
+}
+
+void dump_macro_definitions(void)
+{
+ struct ident *name;
+
+ FOR_EACH_PTR(macros, name) {
+ struct symbol *sym = lookup_macro(name);
+ if (sym)
+ dump_macro(sym);
+ } END_FOR_EACH_PTR(name);
+}
diff --git a/simplify.c b/simplify.c
index 5d00937..a141ddd 100644
--- a/simplify.c
+++ b/simplify.c
@@ -26,23 +26,52 @@
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'.
+ */
+static int get_phisources(struct instruction *sources[], int nbr, struct instruction *insn)
+{
+ pseudo_t phi;
+ int i = 0;
+
+ assert(insn->opcode == OP_PHI);
+ FOR_EACH_PTR(insn->phi_list, phi) {
+ struct instruction *def;
+ if (phi == VOID)
+ continue;
+ if (i >= nbr)
+ return 1;
+ def = phi->def;
+ assert(def->opcode == OP_PHISOURCE);
+ sources[i++] = def;
+ } END_FOR_EACH_PTR(phi);
+ return i - nbr;
+}
+
static int if_convert_phi(struct instruction *insn)
{
- pseudo_t array[3];
+ struct instruction *array[2];
struct basic_block *parents[3];
struct basic_block *bb, *bb1, *bb2, *source;
struct instruction *br;
pseudo_t p1, p2;
bb = insn->bb;
- if (linearize_ptr_list((struct ptr_list *)insn->phi_list, (void **)array, 3) != 2)
+ if (get_phisources(array, 2, insn))
return 0;
if (linearize_ptr_list((struct ptr_list *)bb->parents, (void **)parents, 3) != 2)
return 0;
- p1 = array[0]->def->src1;
- bb1 = array[0]->def->bb;
- p2 = array[1]->def->src1;
- bb2 = array[1]->def->bb;
+ p1 = array[0]->src1;
+ bb1 = array[0]->bb;
+ p2 = array[1]->src1;
+ bb2 = array[1]->bb;
/* Only try the simple "direct parents" case */
if ((bb1 != parents[0] || bb2 != parents[1]) &&
diff --git a/sparse-llvm.c b/sparse-llvm.c
index 9f362b3..29fb65f 100644
--- a/sparse-llvm.c
+++ b/sparse-llvm.c
@@ -1166,5 +1166,6 @@
LLVMDisposeModule(module);
+ report_stats();
return 0;
}
diff --git a/sparse.1 b/sparse.1
index 85d6e64..c924b3a 100644
--- a/sparse.1
+++ b/sparse.1
@@ -344,6 +344,16 @@
The \fIdir\fR name would normally take the form of the target's
normalized GNU triplet. (e.g. i386-linux-gnu).
.
+.SH DEBUG OPTIONS
+.TP
+.B \-fdump-linearize[=only]
+Dump the IR code of a function directly after its linearization,
+before any simplifications is made. If the argument \fB=only\fR is
+also given no further processing is done on the function.
+.
+.B \-fmem-report
+Report some statistics about memory allocation used by the tool.
+.
.SH OTHER OPTIONS
.TP
.B \-ftabstop=WIDTH
diff --git a/sparse.c b/sparse.c
index 6b3324c..02ab977 100644
--- a/sparse.c
+++ b/sparse.c
@@ -302,5 +302,7 @@
FOR_EACH_PTR_NOTAG(filelist, file) {
check_symbols(sparse(file));
} END_FOR_EACH_PTR_NOTAG(file);
+
+ report_stats();
return 0;
}
diff --git a/stats.c b/stats.c
new file mode 100644
index 0000000..cda18fc
--- /dev/null
+++ b/stats.c
@@ -0,0 +1,62 @@
+#include <stdio.h>
+#include "allocate.h"
+#include "linearize.h"
+#include "storage.h"
+
+__DECLARE_ALLOCATOR(struct ptr_list, ptrlist);
+
+
+typedef void (*get_t)(struct allocator_stats*);
+
+static void show_stats(get_t get, struct allocator_stats * tot)
+{
+ struct allocator_stats x;
+
+ if (get)
+ get(&x);
+ else
+ x = *tot;
+ fprintf(stderr, "%16s: %8d, %10ld, %10ld, %6.2f%%, %8.2f\n",
+ x.name, x.allocations, x.useful_bytes, x.total_bytes,
+ 100 * (double) x.useful_bytes / (x.total_bytes ? : 1),
+ (double) x.useful_bytes / (x.allocations ? : 1));
+
+ tot->allocations += x.allocations;
+ tot->useful_bytes += x.useful_bytes;
+ tot->total_bytes += x.total_bytes;
+}
+
+void show_allocation_stats(void)
+{
+ struct allocator_stats tot = { .name = "total", };
+
+ fprintf(stderr, "%16s: %8s, %10s, %10s, %7s, %8s\n", "allocator", "allocs",
+ "bytes", "total", "%usage", "average");
+ show_stats(get_token_stats, &tot);
+ show_stats(get_ident_stats, &tot);
+ show_stats(get_symbol_stats, &tot);
+ show_stats(get_expression_stats, &tot);
+ show_stats(get_statement_stats, &tot);
+ show_stats(get_scope_stats, &tot);
+ show_stats(get_basic_block_stats, &tot);
+ show_stats(get_instruction_stats, &tot);
+ show_stats(get_pseudo_stats, &tot);
+ show_stats(get_pseudo_user_stats, &tot);
+ show_stats(get_ptrlist_stats, &tot);
+ show_stats(get_multijmp_stats, &tot);
+ show_stats(get_asm_rules_stats, &tot);
+ show_stats(get_asm_constraint_stats, &tot);
+ show_stats(get_context_stats, &tot);
+ show_stats(get_string_stats, &tot);
+ show_stats(get_bytes_stats, &tot);
+ //show_stats(get_storage_stats, &tot);
+ //show_stats(get_storage_hash_stats, &tot);
+
+ show_stats(NULL, &tot);
+}
+
+void report_stats(void)
+{
+ if (fmem_report)
+ show_allocation_stats();
+}
diff --git a/target.c b/target.c
index 17b228a..86a9e2e 100644
--- a/target.c
+++ b/target.c
@@ -22,6 +22,8 @@
int bits_in_longlong = 64;
int bits_in_longlonglong = 128;
+int bits_in_wchar = 32;
+
int max_int_alignment = 4;
/*
diff --git a/target.h b/target.h
index 78c85cf..8326fa2 100644
--- a/target.h
+++ b/target.h
@@ -20,6 +20,8 @@
extern int bits_in_longlong;
extern int bits_in_longlonglong;
+extern int bits_in_wchar;
+
extern int max_int_alignment;
/*
diff --git a/test-linearize.c b/test-linearize.c
index dd2eaec..fe0673b 100644
--- a/test-linearize.c
+++ b/test-linearize.c
@@ -61,5 +61,7 @@
FOR_EACH_PTR_NOTAG(filelist, file) {
clean_up_symbols(sparse(file));
} END_FOR_EACH_PTR_NOTAG(file);
+
+ report_stats();
return 0;
}
diff --git a/test-unssa.c b/test-unssa.c
index f7a5c3a..240d996 100644
--- a/test-unssa.c
+++ b/test-unssa.c
@@ -82,5 +82,6 @@
compile(sparse(file));
} END_FOR_EACH_PTR_NOTAG(file);
+ report_stats();
return 0;
}
diff --git a/validation/bool-cast-bad.c b/validation/bool-cast-bad.c
index b7e7c05..a0b091e 100644
--- a/validation/bool-cast-bad.c
+++ b/validation/bool-cast-bad.c
@@ -15,9 +15,6 @@
* check-command: sparse $file
*
* check-error-start
-bool-cast-bad.c:8:41: warning: incorrect type in return expression (different base types)
-bool-cast-bad.c:8:41: expected bool
-bool-cast-bad.c:8:41: got restricted le16 [usertype] a
bool-cast-bad.c:9:42: warning: cast from restricted le16
bool-cast-bad.c:10:41: warning: incorrect type in return expression (different base types)
bool-cast-bad.c:10:41: expected bool
diff --git a/validation/bool-cast-implicit.c b/validation/bool-cast-implicit.c
index ee8b705..9d89443 100644
--- a/validation/bool-cast-implicit.c
+++ b/validation/bool-cast-implicit.c
@@ -21,8 +21,5 @@
* check-output-excludes: cast\\.
*
* check-error-start
-bool-cast-implicit.c:15:36: warning: incorrect type in return expression (different base types)
-bool-cast-implicit.c:15:36: expected bool
-bool-cast-implicit.c:15:36: got restricted le16 [usertype] a
* check-error-end
*/
diff --git a/validation/bool-cast-restricted.c b/validation/bool-cast-restricted.c
new file mode 100644
index 0000000..f6776b0
--- /dev/null
+++ b/validation/bool-cast-restricted.c
@@ -0,0 +1,25 @@
+typedef unsigned int __attribute__((bitwise)) large_t;
+#define LBIT ((__attribute__((force)) large_t) 1)
+
+_Bool lfoo(large_t x) { return x; }
+_Bool lbar(large_t x) { return ~x; }
+_Bool lbaz(large_t x) { return !x; }
+_Bool lqux(large_t x) { return x & LBIT; }
+
+
+typedef unsigned short __attribute__((bitwise)) small_t;
+#define SBIT ((__attribute__((force)) small_t) 1)
+
+_Bool sfoo(small_t x) { return x; }
+_Bool sbar(small_t x) { return ~x; }
+_Bool sbaz(small_t x) { return !x; }
+_Bool squx(small_t x) { return x & SBIT; }
+
+/*
+ * check-name: bool-cast-restricted.c
+ * check-command: sparse -Wno-decl $file
+ *
+ * check-error-start
+bool-cast-restricted.c:14:32: warning: restricted small_t degrades to integer
+ * check-error-end
+ */
diff --git a/validation/crazy02-not-so.c b/validation/crazy02-not-so.c
new file mode 100644
index 0000000..fe71335
--- /dev/null
+++ b/validation/crazy02-not-so.c
@@ -0,0 +1,22 @@
+int foo(int *ptr, int i)
+{
+ int *p;
+
+ switch (i - i) { // will be optimized to 0
+ case 0:
+ return 0;
+ case 1: // will be optimized away
+ p = ptr;
+ do { // will be an unreachable loop
+ *p++ = 123;
+ } while (--i);
+ break;
+ }
+
+ return 1;
+}
+
+/*
+ * check-name: crazy02-not-so.c
+ * check-command: sparse -Wno-decl $file
+ */
diff --git a/validation/empty-file b/validation/empty-file
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/validation/empty-file
diff --git a/validation/kill-insert-branch.c b/validation/kill-insert-branch.c
new file mode 100644
index 0000000..e59b5bb
--- /dev/null
+++ b/validation/kill-insert-branch.c
@@ -0,0 +1,22 @@
+void foo(int a)
+{
+ int b = 1;
+ if (a)
+ b++;
+ if (b)
+ ;
+}
+
+void bar(int a)
+{
+ if (a ? 1 : 2)
+ ;
+}
+
+/*
+ * check-name: kill insert-branch
+ * check-command: test-linearize -Wno-decl $file
+ *
+ * check-output-ignore
+ * check-output-excludes: select\\.
+ */
diff --git a/validation/linear/bitfield-init-mask.c b/validation/linear/bitfield-init-mask.c
new file mode 100644
index 0000000..94afa40
--- /dev/null
+++ b/validation/linear/bitfield-init-mask.c
@@ -0,0 +1,27 @@
+struct bfu {
+ unsigned int a:11;
+ unsigned int f:9;
+ unsigned int z:3;
+};
+
+struct bfu bfu_init_00_11(int a)
+{
+ struct bfu bfu = { .a = a, };
+ return bfu;
+}
+
+struct bfu bfu_init_20_23(int a)
+{
+ struct bfu bfu = { .z = a, };
+ return bfu;
+}
+
+/*
+ * check-name: bitfield initializer mask
+ * check-command: test-linearize -fdump-linearize=only -Wno-decl $file
+ * check-output-ignore
+ *
+ * check-output-contains: and\\..*fffff800\$
+ * check-output-contains: shl\\..* \\$20
+ * check-output-contains: and\\..*ff8fffff\$
+ */
diff --git a/validation/linear/bitfield-init-zero.c b/validation/linear/bitfield-init-zero.c
new file mode 100644
index 0000000..39a6434
--- /dev/null
+++ b/validation/linear/bitfield-init-zero.c
@@ -0,0 +1,102 @@
+struct bfu {
+ unsigned int a:11;
+ unsigned int f:9;
+ unsigned int :2;
+ unsigned int z:3;
+};
+
+struct bfu bfuu_init(unsigned int a)
+{
+ struct bfu bf = { .f = a, };
+ return bf;
+}
+
+struct bfu bfus_init(int a)
+{
+ struct bfu bf = { .f = a, };
+ return bf;
+}
+
+unsigned int bfu_get0(void)
+{
+ struct bfu bf = { };
+ return bf.f;
+}
+
+
+struct bfs {
+ signed int a:11;
+ signed int f:9;
+ signed int :2;
+ signed int z:3;
+};
+
+struct bfs bfsu_init(unsigned int a)
+{
+ struct bfs bf = { .f = a, };
+ return bf;
+}
+
+struct bfs bfss_init(int a)
+{
+ struct bfs bf = { .f = a, };
+ return bf;
+}
+
+int bfs_get0(void)
+{
+ struct bfs bf = { };
+ return bf.f;
+}
+
+/*
+ * check-name: bitfield implicit init zero
+ * check-command: test-linearize -Wno-decl $file
+ *
+ * check-output-start
+bfuu_init:
+.L0:
+ <entry-point>
+ cast.9 %r2 <- (32) %arg1
+ shl.32 %r4 <- %r2, $11
+ ret.32 %r4
+
+
+bfus_init:
+.L2:
+ <entry-point>
+ scast.9 %r10 <- (32) %arg1
+ shl.32 %r12 <- %r10, $11
+ ret.32 %r12
+
+
+bfu_get0:
+.L4:
+ <entry-point>
+ ret.32 $0
+
+
+bfsu_init:
+.L6:
+ <entry-point>
+ cast.9 %r23 <- (32) %arg1
+ shl.32 %r25 <- %r23, $11
+ ret.32 %r25
+
+
+bfss_init:
+.L8:
+ <entry-point>
+ scast.9 %r31 <- (32) %arg1
+ shl.32 %r33 <- %r31, $11
+ ret.32 %r33
+
+
+bfs_get0:
+.L10:
+ <entry-point>
+ ret.32 $0
+
+
+ * check-output-end
+ */
diff --git a/validation/linear/struct-init-full.c b/validation/linear/struct-init-full.c
new file mode 100644
index 0000000..f1b03db
--- /dev/null
+++ b/validation/linear/struct-init-full.c
@@ -0,0 +1,28 @@
+struct s {
+ int a, b, c;
+};
+
+struct s s_init_all(int a)
+{
+ struct s s = { .a = a, .b = 42, .c = 123, };
+ return s;
+}
+
+/*
+ * check-name: struct implicit init zero not needed
+ * check-command: test-linearize -Wno-decl $file
+ * check-known-to-fail
+ *
+ * check-output-start
+s_init_all:
+.L4:
+ <entry-point>
+ store.32 %arg1 -> 0[s]
+ store.32 $42 -> 4[s]
+ store.32 $123 -> 8[s]
+ load.96 %r8 <- 0[s]
+ ret.96 %r8
+
+
+ * check-output-end
+ */
diff --git a/validation/linear/struct-init-partial.c b/validation/linear/struct-init-partial.c
new file mode 100644
index 0000000..1f5078b
--- /dev/null
+++ b/validation/linear/struct-init-partial.c
@@ -0,0 +1,41 @@
+struct s {
+ int a, b, c;
+};
+
+struct s s_init_first(int a)
+{
+ struct s s = { .a = a, };
+ return s;
+}
+
+struct s s_init_third(int a)
+{
+ struct s s = { .c = a, };
+ return s;
+}
+
+/*
+ * check-name: struct implicit init zero needed
+ * check-command: test-linearize -Wno-decl $file
+ *
+ * check-output-start
+s_init_first:
+.L0:
+ <entry-point>
+ store.96 $0 -> 0[s]
+ store.32 %arg1 -> 0[s]
+ load.96 %r2 <- 0[s]
+ ret.96 %r2
+
+
+s_init_third:
+.L2:
+ <entry-point>
+ store.96 $0 -> 0[s]
+ store.32 %arg1 -> 8[s]
+ load.96 %r5 <- 0[s]
+ ret.96 %r5
+
+
+ * check-output-end
+ */
diff --git a/validation/optim/bool-context.c b/validation/optim/bool-context.c
new file mode 100644
index 0000000..11326d3
--- /dev/null
+++ b/validation/optim/bool-context.c
@@ -0,0 +1,12 @@
+#define bool _Bool
+
+bool bool_ior(int a, int b) { return a || b; }
+bool bool_and(int a, int b) { return a && b; }
+
+/*
+ * check-name: bool-context
+ * check-command: test-linearize -Wno-decl $file
+ * check-output-ignore
+ *
+ * check-output-pattern-4-times: setne\\..* %arg[12]
+ */
diff --git a/validation/optim/bool-simplify.c b/validation/optim/bool-simplify.c
index e0ff1c2..05be114 100644
--- a/validation/optim/bool-simplify.c
+++ b/validation/optim/bool-simplify.c
@@ -32,13 +32,17 @@
and_1:
.L2:
<entry-point>
- ret.32 %arg1
+ setne.1 %r8 <- %arg1, $0
+ cast.32 %r11 <- (1) %r8
+ ret.32 %r11
or_0:
.L4:
<entry-point>
- ret.32 %arg1
+ setne.1 %r14 <- %arg1, $0
+ cast.32 %r17 <- (1) %r14
+ ret.32 %r17
or_1:
diff --git a/validation/optim/void-if-convert.c b/validation/optim/void-if-convert.c
new file mode 100644
index 0000000..66513c4
--- /dev/null
+++ b/validation/optim/void-if-convert.c
@@ -0,0 +1,19 @@
+int foo(int a)
+{
+ if (a)
+ return 0;
+ else
+ return 1;
+ return 2;
+}
+
+/*
+ * check-name: Ignore VOID in if-convert
+ * check-command: test-linearize -Wno-decl $file
+ * check-output-ignore
+ *
+ * check-output-excludes: phisrc\\.
+ * check-output-excludes: phi\\.
+ * check-output-excludes: VOID
+ * check-output-contains: seteq\\.
+ */
diff --git a/validation/preprocessor/dump-macros-empty.c b/validation/preprocessor/dump-macros-empty.c
new file mode 100644
index 0000000..672c66c
--- /dev/null
+++ b/validation/preprocessor/dump-macros-empty.c
@@ -0,0 +1,7 @@
+/*
+ * check-name: dump-macros with empty file
+ * check-command: sparse -E -dD empty-file
+ *
+ * check-output-ignore
+check-output-pattern-1-times: #define __CHECKER__ 1
+ */
diff --git a/validation/preprocessor/dump-macros-multi.c b/validation/preprocessor/dump-macros-multi.c
new file mode 100644
index 0000000..2f6e8d0
--- /dev/null
+++ b/validation/preprocessor/dump-macros-multi.c
@@ -0,0 +1,7 @@
+/*
+ * check-name: dump-macros with multiple files
+ * check-command: sparse -E -dD empty-file $file
+ *
+ * check-output-ignore
+check-output-pattern-2-times: #define __CHECKER__ 1
+ */
diff --git a/validation/preprocessor/dump-macros.c b/validation/preprocessor/dump-macros.c
new file mode 100644
index 0000000..79f3de6
--- /dev/null
+++ b/validation/preprocessor/dump-macros.c
@@ -0,0 +1,18 @@
+#define ABC abc
+#undef ABC
+
+#define DEF def
+#undef DEF
+#define DEF xyz
+
+#define NYDEF ydef
+/*
+ * check-name: dump-macros
+ * check-command: sparse -E -dD -DIJK=ijk -UNDEF -UNYDEF $file
+ *
+ * check-output-ignore
+check-output-pattern-1-times: #define __CHECKER__ 1
+check-output-contains: #define IJK ijk
+check-output-contains: #define DEF xyz
+check-output-contains: #define NYDEF ydef
+ */
diff --git a/validation/reserved.c b/validation/reserved.c
index e5d7af8..2955456 100644
--- a/validation/reserved.c
+++ b/validation/reserved.c
@@ -1,41 +1,158 @@
-static int (struct);
-static int (union);
-static int (enum);
-static int (volatile);
-static int (__volatile);
-static int (__volatile__);
+static int (auto);
+static int (break);
+static int (case);
+static int (char);
static int (const);
static int (__const);
static int (__const__);
-static int (restrict);
-static int (__restrict);
-static int (__restrict__);
-static int (typedef);
-static int (__typeof);
-static int (__typeof__);
+static int (continue);
+static int (default);
+static int (do);
+static int (double);
+static int (else);
+static int (enum);
+static int (extern);
+static int (float);
+static int (for);
+static int (goto);
+static int (if);
static int (inline);
static int (__inline);
static int (__inline__);
+static int (int);
+static int (long);
+static int (register);
+static int (restrict);
+static int (__restrict);
+static int (__restrict__);
+static int (return);
+static int (short);
+static int (signed);
+static int (sizeof);
+static int (static);
+static int (struct);
+static int (switch);
+static int (typedef);
+static int (union);
+static int (unsigned);
+static int (void);
+static int (volatile);
+static int (volatile);
+static int (__volatile);
+static int (__volatile__);
+static int (while);
+
+static int (_Alignas);
+static int (_Alignof);
+static int (_Atomic);
+static int (_Bool);
+static int (_Complex);
+static int (_Generic);
+static int (_Imaginary);
+static int (_Noreturn);
+static int (_Static_assert);
+static int (_Thread_local);
+
+// Sparse extensions
+static int (__context__);
+static int (__range__);
+static int (__sizeof_ptr__);
+
+// GCC extensions
+static int (__alignof);
+static int (__alignof__);
+static int (asm); // not reserved!
+static int (__asm);
+static int (__asm__);
+static int (__label__);
+static int (__thread);
+static int (typeof);
+static int (__typeof);
+static int (__typeof__);
+
+static int (__int128);
+static int (__int128_t);
+static int (__uint128_t);
+
+static int (__builtin_ms_va_list);
+static int (__builtin_offsetof);
+static int (__builtin_types_compatible_p);
+static int (__builtin_va_list);
+
/*
* check-name: const et.al. are reserved identifiers
* check-error-start:
-reserved.c:1:12: error: Trying to use reserved word 'struct' as identifier
-reserved.c:2:12: error: Trying to use reserved word 'union' as identifier
-reserved.c:3:12: error: Trying to use reserved word 'enum' as identifier
-reserved.c:4:12: error: Trying to use reserved word 'volatile' as identifier
-reserved.c:5:12: error: Trying to use reserved word '__volatile' as identifier
-reserved.c:6:12: error: Trying to use reserved word '__volatile__' as identifier
-reserved.c:7:12: error: Trying to use reserved word 'const' as identifier
-reserved.c:8:12: error: Trying to use reserved word '__const' as identifier
-reserved.c:9:12: error: Trying to use reserved word '__const__' as identifier
-reserved.c:10:12: error: Trying to use reserved word 'restrict' as identifier
-reserved.c:11:12: error: Trying to use reserved word '__restrict' as identifier
-reserved.c:12:12: error: Trying to use reserved word '__restrict__' as identifier
-reserved.c:13:12: error: Trying to use reserved word 'typedef' as identifier
-reserved.c:14:12: error: Trying to use reserved word '__typeof' as identifier
-reserved.c:15:12: error: Trying to use reserved word '__typeof__' as identifier
-reserved.c:16:12: error: Trying to use reserved word 'inline' as identifier
-reserved.c:17:12: error: Trying to use reserved word '__inline' as identifier
-reserved.c:18:12: error: Trying to use reserved word '__inline__' as identifier
+reserved.c:1:12: error: Trying to use reserved word 'auto' as identifier
+reserved.c:2:12: error: Trying to use reserved word 'break' as identifier
+reserved.c:3:12: error: Trying to use reserved word 'case' as identifier
+reserved.c:4:12: error: Trying to use reserved word 'char' as identifier
+reserved.c:5:12: error: Trying to use reserved word 'const' as identifier
+reserved.c:6:12: error: Trying to use reserved word '__const' as identifier
+reserved.c:7:12: error: Trying to use reserved word '__const__' as identifier
+reserved.c:8:12: error: Trying to use reserved word 'continue' as identifier
+reserved.c:9:12: error: Trying to use reserved word 'default' as identifier
+reserved.c:10:12: error: Trying to use reserved word 'do' as identifier
+reserved.c:11:12: error: Trying to use reserved word 'double' as identifier
+reserved.c:12:12: error: Trying to use reserved word 'else' as identifier
+reserved.c:13:12: error: Trying to use reserved word 'enum' as identifier
+reserved.c:14:12: error: Trying to use reserved word 'extern' as identifier
+reserved.c:15:12: error: Trying to use reserved word 'float' as identifier
+reserved.c:16:12: error: Trying to use reserved word 'for' as identifier
+reserved.c:17:12: error: Trying to use reserved word 'goto' as identifier
+reserved.c:18:12: error: Trying to use reserved word 'if' as identifier
+reserved.c:19:12: error: Trying to use reserved word 'inline' as identifier
+reserved.c:20:12: error: Trying to use reserved word '__inline' as identifier
+reserved.c:21:12: error: Trying to use reserved word '__inline__' as identifier
+reserved.c:22:12: error: Trying to use reserved word 'int' as identifier
+reserved.c:23:12: error: Trying to use reserved word 'long' as identifier
+reserved.c:24:12: error: Trying to use reserved word 'register' as identifier
+reserved.c:25:12: error: Trying to use reserved word 'restrict' as identifier
+reserved.c:26:12: error: Trying to use reserved word '__restrict' as identifier
+reserved.c:27:12: error: Trying to use reserved word '__restrict__' as identifier
+reserved.c:28:12: error: Trying to use reserved word 'return' as identifier
+reserved.c:29:12: error: Trying to use reserved word 'short' as identifier
+reserved.c:30:12: error: Trying to use reserved word 'signed' as identifier
+reserved.c:31:12: error: Trying to use reserved word 'sizeof' as identifier
+reserved.c:32:12: error: Trying to use reserved word 'static' as identifier
+reserved.c:33:12: error: Trying to use reserved word 'struct' as identifier
+reserved.c:34:12: error: Trying to use reserved word 'switch' as identifier
+reserved.c:35:12: error: Trying to use reserved word 'typedef' as identifier
+reserved.c:36:12: error: Trying to use reserved word 'union' as identifier
+reserved.c:37:12: error: Trying to use reserved word 'unsigned' as identifier
+reserved.c:38:12: error: Trying to use reserved word 'void' as identifier
+reserved.c:39:12: error: Trying to use reserved word 'volatile' as identifier
+reserved.c:40:12: error: Trying to use reserved word 'volatile' as identifier
+reserved.c:41:12: error: Trying to use reserved word '__volatile' as identifier
+reserved.c:42:12: error: Trying to use reserved word '__volatile__' as identifier
+reserved.c:43:12: error: Trying to use reserved word 'while' as identifier
+reserved.c:45:12: error: Trying to use reserved word '_Alignas' as identifier
+reserved.c:46:12: error: Trying to use reserved word '_Alignof' as identifier
+reserved.c:47:12: error: Trying to use reserved word '_Atomic' as identifier
+reserved.c:48:12: error: Trying to use reserved word '_Bool' as identifier
+reserved.c:49:12: error: Trying to use reserved word '_Complex' as identifier
+reserved.c:50:12: error: Trying to use reserved word '_Generic' as identifier
+reserved.c:51:12: error: Trying to use reserved word '_Imaginary' as identifier
+reserved.c:52:12: error: Trying to use reserved word '_Noreturn' as identifier
+reserved.c:53:12: error: Trying to use reserved word '_Static_assert' as identifier
+reserved.c:54:12: error: Trying to use reserved word '_Thread_local' as identifier
+reserved.c:57:12: error: Trying to use reserved word '__context__' as identifier
+reserved.c:58:12: error: Trying to use reserved word '__range__' as identifier
+reserved.c:59:12: error: Trying to use reserved word '__sizeof_ptr__' as identifier
+reserved.c:62:12: error: Trying to use reserved word '__alignof' as identifier
+reserved.c:63:12: error: Trying to use reserved word '__alignof__' as identifier
+reserved.c:65:12: error: Trying to use reserved word '__asm' as identifier
+reserved.c:66:12: error: Trying to use reserved word '__asm__' as identifier
+reserved.c:67:12: error: Trying to use reserved word '__label__' as identifier
+reserved.c:68:12: error: Trying to use reserved word '__thread' as identifier
+reserved.c:69:12: error: Trying to use reserved word 'typeof' as identifier
+reserved.c:70:12: error: Trying to use reserved word '__typeof' as identifier
+reserved.c:71:12: error: Trying to use reserved word '__typeof__' as identifier
+reserved.c:73:12: error: Trying to use reserved word '__int128' as identifier
+reserved.c:74:12: error: Trying to use reserved word '__int128_t' as identifier
+reserved.c:75:12: error: Trying to use reserved word '__uint128_t' as identifier
+reserved.c:77:12: error: Trying to use reserved word '__builtin_ms_va_list' as identifier
+reserved.c:78:12: error: Trying to use reserved word '__builtin_offsetof' as identifier
+reserved.c:79:12: error: Trying to use reserved word '__builtin_types_compatible_p' as identifier
+reserved.c:80:12: error: Trying to use reserved word '__builtin_va_list' as identifier
* check-error-end:
*/
diff --git a/validation/static_assert.c b/validation/static_assert.c
new file mode 100644
index 0000000..d9e9629
--- /dev/null
+++ b/validation/static_assert.c
@@ -0,0 +1,71 @@
+_Static_assert(1, "global ok");
+
+struct foo {
+ _Static_assert(1, "struct ok");
+};
+
+void bar(void)
+{
+ _Static_assert(1, " func1 ok");
+ int i;
+ i = 0;
+ _Static_assert(1, " func2 ok");
+
+ if (1) {
+ _Static_assert(1, " func3 ok");
+ }
+}
+
+_Static_assert(0, "expected assertion failure");
+
+static int f;
+_Static_assert(f, "non-constant expression");
+
+static int *p;
+_Static_assert(p, "non-integer expression");
+
+_Static_assert(0.1, "float expression");
+
+_Static_assert(!0 == 1, "non-trivial expression");
+
+static char array[4];
+_Static_assert(sizeof(array) == 4, "sizeof expression");
+
+static const char non_literal_string[] = "non literal string";
+_Static_assert(0, non_literal_string);
+
+_Static_assert(1 / 0, "invalid expression: should not show up?");
+
+struct s {
+ char arr[16];
+ _Static_assert(1, "inside struct");
+};
+
+union u {
+ char c;
+ int i;
+ _Static_assert(1, "inside union");
+};
+
+_Static_assert(sizeof(struct s) == 16, "sizeof assertion");
+
+_Static_assert(1, );
+_Static_assert(, "");
+_Static_assert(,);
+
+/*
+ * check-name: static assertion
+ *
+ * check-error-start
+static_assert.c:19:16: error: static assertion failed: "expected assertion failure"
+static_assert.c:22:16: error: bad constant expression
+static_assert.c:25:16: error: bad constant expression
+static_assert.c:27:16: error: bad constant expression
+static_assert.c:35:19: error: bad or missing string literal
+static_assert.c:37:18: error: bad constant expression
+static_assert.c:52:19: error: bad or missing string literal
+static_assert.c:53:16: error: Expected constant expression
+static_assert.c:54:16: error: Expected constant expression
+static_assert.c:54:17: error: bad or missing string literal
+ * check-error-end
+ */
diff --git a/validation/test-suite b/validation/test-suite
index a5f5e25..904a2db 100755
--- a/validation/test-suite
+++ b/validation/test-suite
@@ -270,6 +270,7 @@
ko_tests=`expr $ko_tests + 1`
else
ok_tests=`expr $ok_tests + 1`
+ rm -f $file.{error,output}.{expected,got,diff}
fi
return $test_failed
}