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 b3a657c..e0932ed 100644
--- a/flow.c
+++ b/flow.c
@@ -313,6 +313,15 @@
 	return	a->offset == b->offset && a->size == b->size;
 }
 
+static inline int distinct_symbols(pseudo_t a, pseudo_t b)
+{
+	if (a->type != PSEUDO_SYM)
+		return 0;
+	if (b->type != PSEUDO_SYM)
+		return 0;
+	return a->sym != b->sym;
+}
+
 /*
  * Return 1 if "dom" dominates the access to "pseudo"
  * in "insn".
@@ -331,7 +340,7 @@
 		if (local)
 			return 0;
 		/* We don't think two explicitly different symbols ever alias */
-		if (dom->src->type == PSEUDO_SYM)
+		if (distinct_symbols(insn->src, dom->src))
 			return 0;
 		/* We could try to do some alias analysis here */
 		return -1;
@@ -824,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 97c9a11..7313e72 100644
--- a/linearize.c
+++ b/linearize.c
@@ -643,7 +643,7 @@
 {
 	remove_bb_from_list(&child->parents, parent, 1);
 	if (!child->parents)
-		kill_bb(child);
+		repeat_phase |= REPEAT_CFG_CLEANUP;
 }
 
 /* Change a "switch" or a conditional branch into a branch */
@@ -671,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);
 }
 	
 
@@ -844,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;
 };
 
@@ -899,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);
 
@@ -914,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;
@@ -949,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));
@@ -998,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;
 }
@@ -1157,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, };
@@ -1277,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;
@@ -1287,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;
 
@@ -1571,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:
@@ -1635,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;
@@ -2171,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 = &register_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(&macros, 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/alias-distinct.c b/validation/alias-distinct.c
new file mode 100644
index 0000000..42937b2
--- /dev/null
+++ b/validation/alias-distinct.c
@@ -0,0 +1,17 @@
+extern int g;
+extern int h;
+
+static int foo(void)
+{
+	g = 1;
+	h = 2;
+	return g == 1;
+}
+
+/*
+ * check-name: alias distinct symbols
+ * check-command: test-linearize $file
+ * check-output-ignore
+ *
+ * check-output-contains: ret\\..* *\\$1
+ */
diff --git a/validation/alias-mixed.c b/validation/alias-mixed.c
new file mode 100644
index 0000000..4293047
--- /dev/null
+++ b/validation/alias-mixed.c
@@ -0,0 +1,30 @@
+extern int g;
+
+
+static int foo(int *p)
+{
+	*p = 1;
+	g = 2;
+	return *p == 1;
+}
+
+static int bar(int *p)
+{
+	g = 1;
+	*p = 2;
+	return g == 1;
+}
+
+static test(void)
+{
+	foo(&g);
+	bar(&g);
+}
+
+/*
+ * check-name: alias symbol/pointer
+ * check-command: test-linearize $file
+ * check-output-ignore
+ *
+ * check-output-excludes: ret\\..* *\\$1
+ */
diff --git a/validation/alias-same.c b/validation/alias-same.c
new file mode 100644
index 0000000..55cf424
--- /dev/null
+++ b/validation/alias-same.c
@@ -0,0 +1,17 @@
+extern int g;
+
+
+static int foo(void)
+{
+	g = 1;
+	g = 2;
+	return g != 1;
+}
+
+/*
+ * check-name: alias same symbols
+ * check-command: test-linearize $file
+ * check-output-ignore
+ *
+ * check-output-contains: ret\\..* *\\$1
+ */
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/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
 }