aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
-rw-r--r--Documentation/api.rst3
-rw-r--r--Documentation/doc-guide.rst2
-rwxr-xr-xDocumentation/sphinx/cdoc.py5
-rw-r--r--evaluate.c22
-rw-r--r--flow.c314
-rw-r--r--flow.h1
-rw-r--r--flowgraph.h20
-rw-r--r--linearize.c5
-rw-r--r--memops.c2
-rw-r--r--optimize.c27
-rw-r--r--ptrlist.c12
-rw-r--r--simplify.c2
-rw-r--r--validation/call-inlined.c4
-rw-r--r--validation/eval/unqual-cast.c14
-rw-r--r--validation/eval/unqual-comma.c12
-rw-r--r--validation/eval/unqual-postop.c23
-rw-r--r--validation/eval/unqual-stmt-expr.c12
-rw-r--r--validation/eval/unqual02.c26
-rw-r--r--validation/expand/builtin_constant_inline0.c1
-rw-r--r--validation/inline_base0.c3
-rw-r--r--validation/linear/builtin_unreachable0.c4
-rw-r--r--validation/linear/builtin_unreachable1.c4
-rw-r--r--validation/linear/call-inline.c2
-rw-r--r--validation/mem2reg/cond-expr.c4
-rw-r--r--validation/mem2reg/cond-expr5.c5
-rw-r--r--validation/optim/cse-size.c5
-rw-r--r--validation/optim/memops-missed01.c23
-rw-r--r--validation/optim/memops-missed02.c14
-rw-r--r--validation/optim/merge_bbe-adjust_phi.c23
29 files changed, 537 insertions, 57 deletions
diff --git a/Documentation/api.rst b/Documentation/api.rst
index cb8a0982..22b7dfd2 100644
--- a/Documentation/api.rst
+++ b/Documentation/api.rst
@@ -10,6 +10,7 @@ Utilities
.. c:autodoc:: ptrlist.c
.. c:autodoc:: utils.h
+.. c:autodoc:: flowgraph.h
Parsing
~~~~~~~
@@ -24,4 +25,6 @@ Typing
Optimization
~~~~~~~~~~~~
+.. c:autodoc:: optimize.c
+.. c:autodoc:: flow.c
.. c:autodoc:: simplify.c
diff --git a/Documentation/doc-guide.rst b/Documentation/doc-guide.rst
index 29f39aab..fb4cb322 100644
--- a/Documentation/doc-guide.rst
+++ b/Documentation/doc-guide.rst
@@ -138,7 +138,7 @@ For example, a doc-block like::
will be displayed like this:
.. c:function:: int inc(int val)
- :noindex:
+ :noindexentry:
:param val: the value to increment
:return: the incremented value
diff --git a/Documentation/sphinx/cdoc.py b/Documentation/sphinx/cdoc.py
index 73c128cb..cca5ad28 100755
--- a/Documentation/sphinx/cdoc.py
+++ b/Documentation/sphinx/cdoc.py
@@ -228,8 +228,9 @@ def convert_to_rst(info):
if 'short' in info:
(n, l) = info['short']
l = l[0].capitalize() + l[1:].strip('.')
- l = '\t' + l + '.'
- lst.append((n, l + '\n'))
+ if l[-1] != '?':
+ l = l + '.'
+ lst.append((n, '\t' + l + '\n'))
if 'tags' in info:
for (n, name, l) in info.get('tags', []):
if name != 'return':
diff --git a/evaluate.c b/evaluate.c
index 43a61169..41871e18 100644
--- a/evaluate.c
+++ b/evaluate.c
@@ -61,6 +61,20 @@ static inline int valid_subexpr_type(struct expression *expr)
&& valid_expr_type(expr->right);
}
+static struct symbol *unqualify_type(struct symbol *ctype)
+{
+ if (!ctype)
+ return ctype;
+ if (ctype->type == SYM_NODE && (ctype->ctype.modifiers & MOD_QUALIFIER)) {
+ struct symbol *unqual = alloc_symbol(ctype->pos, 0);
+
+ *unqual = *ctype;
+ unqual->ctype.modifiers &= ~MOD_QUALIFIER;
+ return unqual;
+ }
+ return ctype;
+}
+
static struct symbol *evaluate_symbol_expression(struct expression *expr)
{
struct expression *addr;
@@ -1014,7 +1028,7 @@ static struct symbol *evaluate_binop(struct expression *expr)
static struct symbol *evaluate_comma(struct expression *expr)
{
- expr->ctype = degenerate(expr->right);
+ expr->ctype = unqualify_type(degenerate(expr->right));
if (expr->ctype == &null_ctype)
expr->ctype = &ptr_ctype;
expr->flags &= expr->left->flags & expr->right->flags;
@@ -1907,8 +1921,7 @@ static struct symbol *evaluate_postop(struct expression *expr)
return NULL;
}
- if ((class & TYPE_RESTRICT) && restricted_unop(expr->op, &ctype))
- unrestrict(expr, class, &ctype);
+ unrestrict(expr, class, &ctype);
if (class & TYPE_NUM) {
multiply = 1;
@@ -3025,6 +3038,7 @@ static struct symbol *evaluate_cast(struct expression *expr)
return evaluate_compound_literal(expr, source);
ctype = examine_symbol_type(expr->cast_type);
+ ctype = unqualify_type(ctype);
expr->ctype = ctype;
expr->cast_type = ctype;
@@ -3935,7 +3949,7 @@ struct symbol *evaluate_statement(struct statement *stmt)
return NULL;
if (stmt->expression->ctype == &null_ctype)
stmt->expression = cast_to(stmt->expression, &ptr_ctype);
- return degenerate(stmt->expression);
+ return unqualify_type(degenerate(stmt->expression));
case STMT_COMPOUND: {
struct statement *s;
diff --git a/flow.c b/flow.c
index ef8d04e5..162c2734 100644
--- a/flow.c
+++ b/flow.c
@@ -1,10 +1,11 @@
/*
- * Flow - walk the linearized flowgraph, simplifying it as we
- * go along.
- *
* Copyright (C) 2004 Linus Torvalds
*/
+///
+// Flow simplification
+// -------------------
+
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
@@ -17,6 +18,7 @@
#include "linearize.h"
#include "flow.h"
#include "target.h"
+#include "flowgraph.h"
unsigned long bb_generation;
@@ -43,6 +45,25 @@ static int rewrite_branch(struct basic_block *bb,
return 1;
}
+///
+// returns the phi-node corresponding to a phi-source
+static struct instruction *get_phinode(struct instruction *phisrc)
+{
+ struct pseudo_user *pu;
+
+ FOR_EACH_PTR(phisrc->target->users, pu) {
+ struct instruction *user;
+
+ if (!pu)
+ continue;
+ user = pu->insn;
+ assert(user->opcode == OP_PHI);
+ return user;
+ } END_FOR_EACH_PTR(pu);
+ assert(0);
+}
+
+
/*
* Return the known truth value of a pseudo, or -1 if
* it's not known.
@@ -66,6 +87,34 @@ static int pseudo_truth_value(pseudo_t pseudo)
}
}
+///
+// check if the BB is empty or only contains phi-sources
+static int bb_is_trivial(struct basic_block *bb)
+{
+ struct instruction *insn;
+ int n = 0;
+
+ FOR_EACH_PTR(bb->insns, insn) {
+ if (!insn->bb)
+ continue;
+ switch (insn->opcode) {
+ case OP_TERMINATOR ... OP_TERMINATOR_END:
+ return n ? -1 : 1;
+ case OP_NOP:
+ case OP_INLINED_CALL:
+ continue;
+ case OP_PHISOURCE:
+ n++;
+ continue;
+ default:
+ goto out;
+ }
+ } END_FOR_EACH_PTR(insn);
+
+out:
+ return 0;
+}
+
/*
* Does a basic block depend on the pseudos that "src" defines?
*/
@@ -102,6 +151,106 @@ static int bb_depends_on_phi(struct basic_block *target, struct basic_block *src
return 0;
}
+///
+// does the BB contains ignorable instructions but a final branch?
+// :note: something could be done for phi-sources but ... we'll see.
+static bool bb_is_forwarder(struct basic_block *bb)
+{
+ struct instruction *insn;
+
+ FOR_EACH_PTR(bb->insns, insn) {
+ if (!insn->bb)
+ continue;
+ switch (insn->opcode) {
+ case OP_NOP:
+ case OP_INLINED_CALL:
+ continue;
+ case OP_CBR:
+ case OP_BR:
+ return true;
+ default:
+ goto out;
+ }
+ } END_FOR_EACH_PTR(insn);
+out:
+ return false;
+}
+
+///
+// do jump threading in dominated BBs
+// @dom: the BB which must dominate the modified BBs.
+// @old: the old target BB
+// @new: the new target BB
+// @return: 0 if no chnages have been made, 1 otherwise.
+//
+// In all BB dominated by @dom, rewrite branches to @old into branches to @new
+static int retarget_bb(struct basic_block *dom, struct basic_block *old, struct basic_block *new)
+{
+ struct basic_block *bb;
+ int changed = 0;
+
+ if (new == old)
+ return 0;
+
+restart:
+ FOR_EACH_PTR(old->parents, bb) {
+ struct instruction *last;
+ struct multijmp *jmp;
+
+ if (!domtree_dominates(dom, bb))
+ continue;
+ last = last_instruction(bb->insns);
+ switch (last->opcode) {
+ case OP_BR:
+ changed |= rewrite_branch(bb, &last->bb_true, old, new);
+ break;
+ case OP_CBR:
+ changed |= rewrite_branch(bb, &last->bb_true, old, new);
+ changed |= rewrite_branch(bb, &last->bb_false, old, new);
+ break;
+ case OP_SWITCH:
+ case OP_COMPUTEDGOTO:
+ FOR_EACH_PTR(last->multijmp_list, jmp) {
+ changed |= rewrite_branch(bb, &jmp->target, old, new);
+ } END_FOR_EACH_PTR(jmp);
+ break;
+ default:
+ continue;
+ }
+
+ // since rewrite_branch() will modify old->parents() the list
+ // iteration won't work correctly. Several solution exist for
+ // this but in this case the simplest is to restart the loop.
+ goto restart;
+ } END_FOR_EACH_PTR(bb);
+ return changed;
+}
+
+static int simplify_cbr_cbr(struct instruction *insn)
+{
+ struct instruction *last;
+ struct basic_block *bot = insn->bb;
+ struct basic_block *top = bot->idom;
+ int changed = 0;
+ int trivial;
+
+ if (!top)
+ return 0;
+
+ trivial = bb_is_trivial(bot);
+ if (trivial == 0)
+ return 0;
+ if (trivial < 0)
+ return 0;
+ last = last_instruction(top->insns);
+ if (last->opcode != OP_CBR || last->cond != insn->cond)
+ return 0;
+
+ changed |= retarget_bb(last->bb_true , bot, insn->bb_true);
+ changed |= retarget_bb(last->bb_false, bot, insn->bb_false);
+ return changed;
+}
+
/*
* When we reach here, we have:
* - a basic block that ends in a conditional branch and
@@ -249,6 +398,8 @@ static int simplify_one_branch(struct basic_block *bb, struct instruction *br)
{
if (simplify_phi_branch(bb, br))
return 1;
+ if (simplify_cbr_cbr(br))
+ return 1;
return simplify_branch_branch(bb, br, &br->bb_true, 1) |
simplify_branch_branch(bb, br, &br->bb_false, 0);
}
@@ -723,13 +874,145 @@ void vrfy_flow(struct entrypoint *ep)
assert(!entry);
}
+static int retarget_parents(struct basic_block *bb, struct basic_block *target)
+{
+ struct basic_block *parent;
+
+ /*
+ * We can't do FOR_EACH_PTR() here, because the parent list
+ * may change when we rewrite the parent.
+ */
+ while ((parent = first_basic_block(bb->parents))) {
+ if (!rewrite_parent_branch(parent, bb, target))
+ return 0;
+ }
+ kill_bb(bb);
+ return REPEAT_CFG_CLEANUP;
+}
+
+static void remove_merging_phisrc(struct basic_block *top, struct instruction *insn)
+{
+ struct instruction *user = get_phinode(insn);
+ pseudo_t phi;
+
+ FOR_EACH_PTR(user->phi_list, phi) {
+ struct instruction *phisrc;
+
+ if (phi == VOID)
+ continue;
+ phisrc = phi->def;
+ if (phisrc->bb != top)
+ continue;
+ REPLACE_CURRENT_PTR(phi, VOID);
+ kill_instruction(phisrc);
+ } END_FOR_EACH_PTR(phi);
+}
+
+static void remove_merging_phi(struct basic_block *top, struct instruction *insn)
+{
+ pseudo_t phi;
+
+ FOR_EACH_PTR(insn->phi_list, phi) {
+ struct instruction *def;
+
+ if (phi == VOID)
+ continue;
+
+ def = phi->def;
+ if (def->bb != top)
+ continue;
+
+ convert_instruction_target(insn, def->src);
+ kill_instruction(def);
+ kill_instruction(insn);
+ } END_FOR_EACH_PTR(phi);
+}
+
+///
+// merge two BBs
+// @top: the first BB to be merged
+// @bot: the second BB to be merged
+static int merge_bb(struct basic_block *top, struct basic_block *bot)
+{
+ struct instruction *insn;
+ struct basic_block *bb;
+
+ if (top == bot)
+ return 0;
+
+ top->children = bot->children;
+ bot->children = NULL;
+ bot->parents = NULL;
+
+ FOR_EACH_PTR(top->children, bb) {
+ replace_bb_in_list(&bb->parents, bot, top, 1);
+ } END_FOR_EACH_PTR(bb);
+
+ kill_instruction(delete_last_instruction(&top->insns));
+ FOR_EACH_PTR(bot->insns, insn) {
+ if (!insn->bb)
+ continue;
+ assert(insn->bb == bot);
+ switch (insn->opcode) {
+ case OP_PHI:
+ remove_merging_phi(top, insn);
+ continue;
+ case OP_PHISOURCE:
+ remove_merging_phisrc(top, insn);
+ break;
+ }
+ insn->bb = top;
+ add_instruction(&top->insns, insn);
+ } END_FOR_EACH_PTR(insn);
+ bot->insns = NULL;
+ bot->ep = NULL;
+ return REPEAT_CFG_CLEANUP;
+}
+
+///
+// early simplification of the CFG
+// Three things are done here:
+// # inactive BB are removed
+// # branches to a 'forwarder' BB are redirected to the forwardee.
+// # merge single-child/single-parent BBs.
+int simplify_cfg_early(struct entrypoint *ep)
+{
+ struct basic_block *bb;
+ int changed = 0;
+
+ FOR_EACH_PTR_REVERSE(ep->bbs, bb) {
+ struct instruction *insn;
+ struct basic_block *tgt;
+
+ if (!bb->ep) {
+ DELETE_CURRENT_PTR(bb);
+ changed = REPEAT_CFG_CLEANUP;
+ continue;
+ }
+
+ insn = last_instruction(bb->insns);
+ if (!insn)
+ continue;
+ switch (insn->opcode) {
+ case OP_BR:
+ tgt = insn->bb_true;
+ if (bb_is_forwarder(bb))
+ changed |= retarget_parents(bb, tgt);
+ else if (bb_list_size(tgt->parents) == 1)
+ changed |= merge_bb(bb, tgt);
+ break;
+ }
+ } END_FOR_EACH_PTR_REVERSE(bb);
+ return changed;
+}
+
void pack_basic_blocks(struct entrypoint *ep)
{
struct basic_block *bb;
/* See if we can merge a bb into another one.. */
FOR_EACH_PTR(ep->bbs, bb) {
- struct instruction *first, *insn;
+ struct instruction *first;
struct basic_block *parent, *child, *last;
if (!bb_reachable(bb))
@@ -786,28 +1069,7 @@ out:
goto no_merge;
} END_FOR_EACH_PTR(child);
- /*
- * Merge the two.
- */
- repeat_phase |= REPEAT_CFG_CLEANUP;
-
- parent->children = bb->children;
- bb->children = NULL;
- bb->parents = NULL;
-
- FOR_EACH_PTR(parent->children, child) {
- replace_bb_in_list(&child->parents, bb, parent, 0);
- } END_FOR_EACH_PTR(child);
-
- kill_instruction(delete_last_instruction(&parent->insns));
- FOR_EACH_PTR(bb->insns, insn) {
- if (!insn->bb)
- continue;
- assert(insn->bb == bb);
- insn->bb = parent;
- add_instruction(&parent->insns, insn);
- } END_FOR_EACH_PTR(insn);
- bb->insns = NULL;
+ repeat_phase |= merge_bb(parent, bb);
no_merge:
/* nothing to do */;
diff --git a/flow.h b/flow.h
index 099767d4..19a743c8 100644
--- a/flow.h
+++ b/flow.h
@@ -18,6 +18,7 @@ extern void kill_dead_stores(struct entrypoint *ep, pseudo_t addr, int local);
extern void simplify_symbol_usage(struct entrypoint *ep);
extern void simplify_memops(struct entrypoint *ep);
extern void pack_basic_blocks(struct entrypoint *ep);
+extern int simplify_cfg_early(struct entrypoint *ep);
extern void convert_instruction_target(struct instruction *insn, pseudo_t src);
extern void remove_dead_insns(struct entrypoint *);
diff --git a/flowgraph.h b/flowgraph.h
index 7226c55f..5a9c2607 100644
--- a/flowgraph.h
+++ b/flowgraph.h
@@ -1,13 +1,33 @@
#ifndef FLOWGRAPH_H
#define FLOWGRAPH_H
+///
+// Utilities for flowgraphs
+// ------------------------
+
#include <stdbool.h>
struct entrypoint;
struct basic_block;
+///
+// Set the BB's reverse postorder links
+// Each BB will also have its 'order number' set.
int cfg_postorder(struct entrypoint *ep);
+
+///
+// Build the dominance tree.
+// Each BB will then have:
+// - a link to its immediate dominator (::idom)
+// - the list of BB it immediately dominates (::doms)
+// - its level in the dominance tree (::dom_level)
void domtree_build(struct entrypoint *ep);
+
+///
+// Test the dominance between two basic blocks.
+// @a: the basic block expected to dominate
+// @b: the basic block expected to be dominated
+// @return: ``true`` if @a dominates @b, ``false`` otherwise.
bool domtree_dominates(struct basic_block *a, struct basic_block *b);
#endif
diff --git a/linearize.c b/linearize.c
index 4391f09c..8a3cf09b 100644
--- a/linearize.c
+++ b/linearize.c
@@ -28,12 +28,8 @@ static pseudo_t linearize_expression(struct entrypoint *ep, struct expression *e
static pseudo_t add_cast(struct entrypoint *ep, struct symbol *to, struct symbol *from, int op, pseudo_t src);
static pseudo_t add_binary_op(struct entrypoint *ep, struct symbol *ctype, int op, pseudo_t left, pseudo_t right);
-static pseudo_t add_setval(struct entrypoint *ep, struct symbol *ctype, struct expression *val);
static pseudo_t linearize_one_symbol(struct entrypoint *ep, struct symbol *sym);
-struct access_data;
-static pseudo_t add_load(struct entrypoint *ep, struct access_data *);
-static pseudo_t linearize_initializer(struct entrypoint *ep, struct expression *initializer, struct access_data *);
static pseudo_t cast_pseudo(struct entrypoint *ep, pseudo_t src, struct symbol *from, struct symbol *to);
struct pseudo void_pseudo = {};
@@ -732,6 +728,7 @@ void insert_branch(struct basic_block *bb, struct instruction *jmp, struct basic
remove_parent(child, bb);
} END_FOR_EACH_PTR(child);
PACK_PTR_LIST(&bb->children);
+ repeat_phase |= REPEAT_CFG_CLEANUP;
}
diff --git a/memops.c b/memops.c
index f071e556..badcdbbb 100644
--- a/memops.c
+++ b/memops.c
@@ -146,10 +146,12 @@ static void simplify_loads(struct basic_block *bb)
}
rewrite_load_instruction(insn, dominators);
} else { // cleanup pending phi-sources
+ int repeat = repeat_phase;
pseudo_t phi;
FOR_EACH_PTR(dominators, phi) {
kill_instruction(phi->def);
} END_FOR_EACH_PTR(phi);
+ repeat_phase = repeat;
}
}
next_load:
diff --git a/optimize.c b/optimize.c
index bfab74c0..9b754831 100644
--- a/optimize.c
+++ b/optimize.c
@@ -1,10 +1,12 @@
// SPDX-License-Identifier: MIT
//
-// optimize.c - main optimization loop
-//
// Copyright (C) 2004 Linus Torvalds
// Copyright (C) 2004 Christopher Li
+///
+// Optimization main loop
+// ----------------------
+
#include <assert.h>
#include "optimize.h"
#include "flowgraph.h"
@@ -45,6 +47,14 @@ static void clean_up_insns(struct entrypoint *ep)
} END_FOR_EACH_PTR(bb);
}
+static void cleanup_cfg(struct entrypoint *ep)
+{
+ kill_unreachable_bbs(ep);
+ domtree_build(ep);
+}
+
+///
+// optimization main loop
void optimize(struct entrypoint *ep)
{
if (fdump_ir & PASS_LINEARIZE)
@@ -57,6 +67,11 @@ void optimize(struct entrypoint *ep)
kill_unreachable_bbs(ep);
ir_validate(ep);
+ cfg_postorder(ep);
+ if (simplify_cfg_early(ep))
+ kill_unreachable_bbs(ep);
+ ir_validate(ep);
+
domtree_build(ep);
/*
@@ -84,13 +99,11 @@ repeat:
kill_unreachable_bbs(ep);
cse_eliminate(ep);
-
- if (repeat_phase & REPEAT_SYMBOL_CLEANUP)
- simplify_memops(ep);
+ simplify_memops(ep);
} while (repeat_phase);
pack_basic_blocks(ep);
if (repeat_phase & REPEAT_CFG_CLEANUP)
- kill_unreachable_bbs(ep);
+ cleanup_cfg(ep);
} while (repeat_phase);
vrfy_flow(ep);
@@ -110,7 +123,7 @@ repeat:
if (simplify_flow(ep)) {
clear_liveness(ep);
if (repeat_phase & REPEAT_CFG_CLEANUP)
- kill_unreachable_bbs(ep);
+ cleanup_cfg(ep);
goto repeat;
}
diff --git a/ptrlist.c b/ptrlist.c
index 3af0b2c5..0f0b3f6d 100644
--- a/ptrlist.c
+++ b/ptrlist.c
@@ -7,6 +7,18 @@
///
// Pointer list manipulation
// -------------------------
+//
+// The data structure handled here is designed to hold pointers
+// but two special cases need to be avoided or need special care:
+// * NULL is used by {PREPARE,NEXT}_PTR_LIST() to indicate the end-of-list.
+// Thus, NULL can't be stored in lists using this API but is fine to
+// use with FOR_EACH_PTR() and its variants.
+// * VOID is used to replace a removed pseudo 'usage'. Since phi-nodes
+// (OP_PHI) use a list to store their operands, a VOID in a phi-node
+// list must be ignored since it represents a removed operand. As
+// consequence, VOIDs must never be used as phi-node operand.
+// This is fine since phi-nodes make no sense with void values
+// but VOID is also used for invalid types and in case of errors.
#include <stdlib.h>
#include <string.h>
diff --git a/simplify.c b/simplify.c
index 01882b50..2e002cec 100644
--- a/simplify.c
+++ b/simplify.c
@@ -2049,7 +2049,7 @@ static int simplify_branch(struct instruction *insn)
kill_use(&insn->cond);
insn->cond = NULL;
insn->opcode = OP_BR;
- return REPEAT_CSE;
+ return REPEAT_CSE|REPEAT_CFG_CLEANUP;
}
/* Conditional on a SETNE $0 or SETEQ $0 */
diff --git a/validation/call-inlined.c b/validation/call-inlined.c
index 3612c5c4..a6cb4b5b 100644
--- a/validation/call-inlined.c
+++ b/validation/call-inlined.c
@@ -28,12 +28,14 @@ foo:
<entry-point>
add.32 %r3 <- %arg1, %arg2
add.32 %r5 <- %r3, $1
+ # call %r6 <- add, %r3, $1
ret.32 %r5
bar:
.L3:
<entry-point>
+ # call %r13 <- add, %r10, $1
ret
@@ -41,6 +43,7 @@ bas:
.L6:
<entry-point>
add.64 %r16 <- "abc", $1
+ # call %r17 <- lstrip, %r14
ret.64 %r16
@@ -48,6 +51,7 @@ qus:
.L9:
<entry-point>
add.64 %r21 <- messg, $1
+ # call %r22 <- lstrip, %r19
ret.64 %r21
diff --git a/validation/eval/unqual-cast.c b/validation/eval/unqual-cast.c
new file mode 100644
index 00000000..4106ec3b
--- /dev/null
+++ b/validation/eval/unqual-cast.c
@@ -0,0 +1,14 @@
+#define cvr const volatile restrict
+
+_Static_assert([typeof((cvr int) 0)] == [int]);
+_Static_assert([typeof((cvr int *) 0)] == [cvr int *]);
+
+static int *function(volatile int x)
+{
+ extern typeof((typeof(x)) (x)) y;
+ return &y;
+}
+
+/*
+ * check-name: unqual-cast
+ */
diff --git a/validation/eval/unqual-comma.c b/validation/eval/unqual-comma.c
new file mode 100644
index 00000000..11546d22
--- /dev/null
+++ b/validation/eval/unqual-comma.c
@@ -0,0 +1,12 @@
+#define __unqual_typeof(x) typeof(((void)0, (x)))
+
+int *foo(volatile int x);
+int *foo(volatile int x)
+{
+ extern __unqual_typeof(x) y;
+ return &y;
+}
+
+/*
+ * check-name: unqual-comma
+ */
diff --git a/validation/eval/unqual-postop.c b/validation/eval/unqual-postop.c
new file mode 100644
index 00000000..fb3082dc
--- /dev/null
+++ b/validation/eval/unqual-postop.c
@@ -0,0 +1,23 @@
+static void test_volatile(void)
+{
+ volatile int x = 0;
+ int *pp;
+
+ typeof(++x) v1; pp = &v1; // KO
+ typeof(x++) v2; pp = &v2; // KO
+}
+
+/*
+ * check-name: unqual-postop
+ * check-command: sparse -Wno-declaration-after-statement $file
+ * check-known-to-fail
+ *
+ * check-error-start
+eval/unqual-postop.c:6:40: warning: incorrect type in assignment (different modifiers)
+eval/unqual-postop.c:6:40: expected int *pp
+eval/unqual-postop.c:6:40: got int volatile *
+eval/unqual-postop.c:7:40: warning: incorrect type in assignment (different modifiers)
+eval/unqual-postop.c:7:40: expected int *pp
+eval/unqual-postop.c:7:40: got int volatile *
+ * check-error-end
+ */
diff --git a/validation/eval/unqual-stmt-expr.c b/validation/eval/unqual-stmt-expr.c
new file mode 100644
index 00000000..280c2fe8
--- /dev/null
+++ b/validation/eval/unqual-stmt-expr.c
@@ -0,0 +1,12 @@
+#define __unqual_typeof(x) typeof(({ x; }))
+
+int *foo(volatile int x);
+int *foo(volatile int x)
+{
+ extern __unqual_typeof(x) y;
+ return &y;
+}
+
+/*
+ * check-name: unqual-stmt-expr
+ */
diff --git a/validation/eval/unqual02.c b/validation/eval/unqual02.c
new file mode 100644
index 00000000..f136cbd5
--- /dev/null
+++ b/validation/eval/unqual02.c
@@ -0,0 +1,26 @@
+static void test_const(volatile int x)
+{
+ const int x = 0;
+ typeof(1?x:x) i3; i3 = 0; // should be OK
+ typeof(+x) i4; i4 = 0; // should be OK
+ typeof(-x) i5; i5 = 0; // should be OK
+ typeof(!x) i6; i6 = 0; // should be OK
+ typeof(x+x) i7; i7 = 0; // should be OK
+}
+
+static void test_volatile(void)
+{
+ volatile int x = 0;
+ int *pp;
+
+ typeof(1?x:x) i3; pp = &i3; // should be OK
+ typeof(+x) i4; pp = &i4; // should be OK
+ typeof(-x) i5; pp = &i5; // should be OK
+ typeof(!x) i6; pp = &i6; // should be OK
+ typeof(x+x) i7; pp = &i7; // should be OK
+}
+
+/*
+ * check-name: unqual02
+ * check-command: sparse -Wno-declaration-after-statement $file
+ */
diff --git a/validation/expand/builtin_constant_inline0.c b/validation/expand/builtin_constant_inline0.c
index a0057f20..d72a211f 100644
--- a/validation/expand/builtin_constant_inline0.c
+++ b/validation/expand/builtin_constant_inline0.c
@@ -16,6 +16,7 @@ int foo(void)
foo:
.L0:
<entry-point>
+ # call %r1 <- is_const, $42
ret.32 $42
diff --git a/validation/inline_base0.c b/validation/inline_base0.c
index 517ee972..698c760f 100644
--- a/validation/inline_base0.c
+++ b/validation/inline_base0.c
@@ -27,6 +27,7 @@ foo0:
.L0:
<entry-point>
add.32 %r5 <- %arg1, %arg2
+ # call %r6 <- add, %r1, %r2
ret.32 %r5
@@ -34,12 +35,14 @@ foo1:
.L3:
<entry-point>
add.32 %r10 <- %arg1, $1
+ # call %r11 <- add, %r8, $1
ret.32 %r10
foo2:
.L6:
<entry-point>
+ # call %r13 <- add, $1, $2
ret.32 $3
diff --git a/validation/linear/builtin_unreachable0.c b/validation/linear/builtin_unreachable0.c
index 911ed7f9..4fc56473 100644
--- a/validation/linear/builtin_unreachable0.c
+++ b/validation/linear/builtin_unreachable0.c
@@ -14,12 +14,12 @@ foo:
.L0:
<entry-point>
seteq.32 %r2 <- %arg1, $3
- cbr %r2, .L1, .L3
+ cbr %r2, .L1, .L2
.L1:
unreachable
-.L3:
+.L2:
ret.32 %arg1
diff --git a/validation/linear/builtin_unreachable1.c b/validation/linear/builtin_unreachable1.c
index 70f6674c..2fc1d728 100644
--- a/validation/linear/builtin_unreachable1.c
+++ b/validation/linear/builtin_unreachable1.c
@@ -16,9 +16,9 @@ int foo(int c)
foo:
.L0:
<entry-point>
- cbr %arg1, .L3, .L2
+ cbr %arg1, .L1, .L2
-.L3:
+.L1:
ret.32 $1
.L2:
diff --git a/validation/linear/call-inline.c b/validation/linear/call-inline.c
index dfd49b62..1ad785ee 100644
--- a/validation/linear/call-inline.c
+++ b/validation/linear/call-inline.c
@@ -13,6 +13,6 @@ int i3(void) { return (***fun)(); } // C99,C11 6.5.3.2p4
*
* check-output-ignore
* check-output-excludes: load
- * check-output-excludes: call
+ * check-output-excludes: \\tcall
* check-output-pattern(5): ret\\..* \\$42
*/
diff --git a/validation/mem2reg/cond-expr.c b/validation/mem2reg/cond-expr.c
index 8acb00ac..2474d65d 100644
--- a/validation/mem2reg/cond-expr.c
+++ b/validation/mem2reg/cond-expr.c
@@ -9,6 +9,6 @@ int foo(int a, int b, int c)
* check-name: cond-expr
* check-command: test-linearize -Wno-decl -fdump-ir=mem2reg $file
* check-output-ignore
- * check-output-pattern(2): phi\\.
- * check-output-pattern(3): phisrc\\.
+ * check-output-pattern(1): phi\\.
+ * check-output-pattern(2): phisrc\\.
*/
diff --git a/validation/mem2reg/cond-expr5.c b/validation/mem2reg/cond-expr5.c
index a3ce5e3a..beef8f25 100644
--- a/validation/mem2reg/cond-expr5.c
+++ b/validation/mem2reg/cond-expr5.c
@@ -15,7 +15,6 @@ int foo(int p, int q, int a)
* check-output-ignore
* check-output-excludes: load\\.
* check-output-excludes: store\\.
- * check-output-excludes: phi\\..*, .*, .*
- * check-output-pattern(3): phi\\.
- * check-output-pattern(5): phisrc\\.
+ * check-output-pattern(2): phi\\.
+ * check-output-pattern(4): phisrc\\.
*/
diff --git a/validation/optim/cse-size.c b/validation/optim/cse-size.c
index 5b31420c..0c0c2d14 100644
--- a/validation/optim/cse-size.c
+++ b/validation/optim/cse-size.c
@@ -1,7 +1,7 @@
static void foo(void)
{
unsigned short p = 0;
- int x;
+ int x = 1;
for (;;)
if (p)
@@ -13,5 +13,6 @@ static void foo(void)
* check-command: test-linearize -Wno-decl $file
*
* check-output-ignore
- * check-output-pattern(2): phi\\.
+ * check-output-excludes: phi\\.
+ * check-output-excludes: cbr
*/
diff --git a/validation/optim/memops-missed01.c b/validation/optim/memops-missed01.c
new file mode 100644
index 00000000..fc616f19
--- /dev/null
+++ b/validation/optim/memops-missed01.c
@@ -0,0 +1,23 @@
+void bar(int);
+
+void foo(void)
+{
+ char buf[1] = { 42 };
+ const char *p = buf;
+ const char **q = &p;
+ int ch = 0;
+ switch (**q) {
+ case 4:
+ ch = 2;
+ }
+ bar(ch);
+}
+
+/*
+ * check-name: memops-missed01
+ * check-command: test-linearize -Wno-decl $file
+ *
+ * check-output-ignore
+ * check-output-excludes: store\\.
+ * check-output-excludes: load\\.
+ */
diff --git a/validation/optim/memops-missed02.c b/validation/optim/memops-missed02.c
new file mode 100644
index 00000000..6f028649
--- /dev/null
+++ b/validation/optim/memops-missed02.c
@@ -0,0 +1,14 @@
+void foo(int a[])
+{
+ int i, val;
+ for (;; i++)
+ val = a[i] ? a[i] : val;
+}
+
+/*
+ * check-name: memops-missed02
+ * check-command: test-linearize -Wno-decl $file
+ *
+ * check-output-ignore
+ * check-output-pattern(1): load\\.
+ */
diff --git a/validation/optim/merge_bbe-adjust_phi.c b/validation/optim/merge_bbe-adjust_phi.c
new file mode 100644
index 00000000..6a8ebb73
--- /dev/null
+++ b/validation/optim/merge_bbe-adjust_phi.c
@@ -0,0 +1,23 @@
+extern int array[2];
+
+static inline int stupid_select(int idx)
+{
+ if (idx)
+ idx = 0;
+ return array[idx];
+}
+
+int select(void)
+{
+ int d = stupid_select(-1);
+ return d;
+}
+
+/*
+ * check-name: merge_bbe-adjust_phi
+ * check-command: test-linearize -Wno-decl $file
+ *
+ * check-output-ignore
+ * check-output-excludes: phisrc\\.
+ * check-output-excludes: phi\\.
+ */