aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--Makefile1
-rw-r--r--linearize.h2
-rw-r--r--parse.c2
-rw-r--r--simplify.c98
-rw-r--r--ssa.c171
-rw-r--r--sset.c28
-rw-r--r--sset.h56
-rw-r--r--validation/knr-attr-crash.c12
-rw-r--r--validation/mem2reg/not-same-memop0.c48
-rw-r--r--validation/mem2reg/packed-bitfield.c35
-rw-r--r--validation/optim/cmpe-and0.c10
-rw-r--r--validation/optim/cmpe-or0.c10
-rw-r--r--validation/optim/cmps-and0.c21
-rw-r--r--validation/optim/cmps-minmax.c8
-rw-r--r--validation/optim/cmps-or0.c21
-rw-r--r--validation/optim/cmps0-and0.c12
-rw-r--r--validation/optim/cmpu-and0.c17
-rw-r--r--validation/optim/cmpu-or0.c18
-rwxr-xr-xvalidation/test-suite11
19 files changed, 403 insertions, 178 deletions
diff --git a/Makefile b/Makefile
index a0178a65..69d20e72 100644
--- a/Makefile
+++ b/Makefile
@@ -63,7 +63,6 @@ LIB_OBJS += show-parse.o
LIB_OBJS += simplify.o
LIB_OBJS += sort.o
LIB_OBJS += ssa.o
-LIB_OBJS += sset.o
LIB_OBJS += stats.o
LIB_OBJS += storage.o
LIB_OBJS += symbol.o
diff --git a/linearize.h b/linearize.h
index 7909b01f..86ae119c 100644
--- a/linearize.h
+++ b/linearize.h
@@ -18,7 +18,7 @@ struct pseudo_user {
DECLARE_ALLOCATOR(pseudo_user);
DECLARE_PTR_LIST(pseudo_user_list, struct pseudo_user);
-DECLARE_PTRMAP(phi_map, struct symbol *, pseudo_t);
+DECLARE_PTRMAP(phi_map, struct symbol *, struct instruction *);
enum pseudo_type {
diff --git a/parse.c b/parse.c
index 70be616c..bc1c0602 100644
--- a/parse.c
+++ b/parse.c
@@ -1653,7 +1653,7 @@ static bool match_attribute(struct token *token)
if (token_type(token) != TOKEN_IDENT)
return false;
sym = lookup_keyword(token->ident, NS_TYPEDEF);
- if (!sym)
+ if (!sym || !sym->op)
return false;
return sym->op->type & KW_ATTRIBUTE;
}
diff --git a/simplify.c b/simplify.c
index 207af8ed..9e3514d8 100644
--- a/simplify.c
+++ b/simplify.c
@@ -1258,6 +1258,104 @@ static int simplify_compare_constant(struct instruction *insn, long long value)
src2 = insn->src2;
value = src2->value;
switch (DEF_OPCODE(def, src1)) {
+ case OP_AND:
+ if (!constant(def->src2))
+ break;
+ bits = def->src2->value;
+ switch (insn->opcode) {
+ case OP_SET_EQ:
+ if ((value & bits) != value)
+ return replace_with_value(insn, 0);
+ break;
+ case OP_SET_NE:
+ if ((value & bits) != value)
+ return replace_with_value(insn, 1);
+ break;
+ case OP_SET_LE:
+ value = sign_extend(value, def->size);
+ if (bits & sign_bit(def->size))
+ break;
+ if (value < 0)
+ return replace_with_value(insn, 0);
+ if (value >= (long long)bits)
+ return replace_with_value(insn, 1);
+ if (value == 0)
+ return replace_opcode(insn, OP_SET_EQ);
+ break;
+ case OP_SET_GT:
+ value = sign_extend(value, def->size);
+ if (bits & sign_bit(def->size))
+ break;
+ if (value < 0)
+ return replace_with_value(insn, 1);
+ if (value >= (long long)bits)
+ return replace_with_value(insn, 0);
+ if (value == 0)
+ return replace_opcode(insn, OP_SET_NE);
+ break;
+ case OP_SET_B:
+ if (value > bits)
+ return replace_with_value(insn, 1);
+ break;
+ case OP_SET_BE:
+ if (value >= bits)
+ return replace_with_value(insn, 1);
+ break;
+ case OP_SET_AE:
+ if (value > bits)
+ return replace_with_value(insn, 0);
+ break;
+ case OP_SET_A:
+ if (value >= bits)
+ return replace_with_value(insn, 0);
+ break;
+ }
+ break;
+ case OP_OR:
+ if (!constant(def->src2))
+ break;
+ bits = def->src2->value;
+ switch (insn->opcode) {
+ case OP_SET_EQ:
+ if ((value & bits) != bits)
+ return replace_with_value(insn, 0);
+ break;
+ case OP_SET_NE:
+ if ((value & bits) != bits)
+ return replace_with_value(insn, 1);
+ break;
+ case OP_SET_B:
+ if (bits >= value)
+ return replace_with_value(insn, 0);
+ break;
+ case OP_SET_BE:
+ if (bits > value)
+ return replace_with_value(insn, 0);
+ break;
+ case OP_SET_AE:
+ if (bits > value)
+ return replace_with_value(insn, 1);
+ break;
+ case OP_SET_A:
+ if (bits >= value)
+ return replace_with_value(insn, 1);
+ break;
+ case OP_SET_LE:
+ value = sign_extend(value, def->size);
+ if (bits & sign_bit(def->size)) {
+ if (value >= -1)
+ return replace_with_value(insn, 1);
+ }
+ break;
+ case OP_SET_GT:
+ value = sign_extend(value, def->size);
+ if (bits & sign_bit(def->size)) {
+ if (value >= -1)
+ return replace_with_value(insn, 0);
+ }
+ break;
+ }
+ break;
case OP_SEXT: // sext(x) cmp C --> x cmp trunc(C)
osize = def->orig_type->bit_size;
if (is_signed_constant(value, osize, size)) {
diff --git a/ssa.c b/ssa.c
index b9044207..de4216fb 100644
--- a/ssa.c
+++ b/ssa.c
@@ -7,7 +7,6 @@
#include <assert.h>
#include "ssa.h"
#include "lib.h"
-#include "sset.h"
#include "dominate.h"
#include "flowgraph.h"
#include "linearize.h"
@@ -33,6 +32,9 @@ static inline bool is_promotable(struct symbol *type)
case SYM_STRUCT:
// we allow a single scalar field
// but a run of bitfields count for 1
+ // (and packed bifields are excluded).
+ if (type->packed)
+ return 0;
nbr = 0;
bf_seen = 0;
FOR_EACH_PTR(type->symbol_list, member) {
@@ -72,21 +74,6 @@ static inline bool is_promotable(struct symbol *type)
return 0;
}
-static bool insn_before(struct instruction *a, struct instruction *b)
-{
- struct basic_block *bb = a->bb;
- struct instruction *insn;
-
- assert(b->bb == bb);
- FOR_EACH_PTR(bb->insns, insn) {
- if (insn == a)
- return true;
- if (insn == b)
- return false;
- } END_FOR_EACH_PTR(insn);
- assert(0);
-}
-
static void kill_store(struct instruction *insn)
{
remove_use(&insn->src);
@@ -94,10 +81,22 @@ static void kill_store(struct instruction *insn)
insn->bb = NULL;
}
+static bool same_memop(struct instruction *a, struct instruction *b)
+{
+ if (a->size != b->size || a->offset != b->offset)
+ return false;
+ if (is_integral_type(a->type) && is_float_type(b->type))
+ return false;
+ if (is_float_type(a->type) && is_integral_type(b->type))
+ return false;
+ return true;
+}
+
static void rewrite_local_var(struct basic_block *bb, pseudo_t addr, int nbr_stores, int nbr_uses)
{
+ struct instruction *store = NULL;
struct instruction *insn;
- pseudo_t val = NULL;
+ bool remove = false;
if (!bb)
return;
@@ -108,71 +107,32 @@ static void rewrite_local_var(struct basic_block *bb, pseudo_t addr, int nbr_sto
continue;
switch (insn->opcode) {
case OP_LOAD:
- if (!val)
- val = undef_pseudo();
- replace_with_pseudo(insn, val);
+ if (!store)
+ replace_with_pseudo(insn, undef_pseudo());
+ else if (same_memop(store, insn))
+ replace_with_pseudo(insn, store->target);
+ else
+ remove = false;
break;
case OP_STORE:
- val = insn->target;
- // can't use kill_instruction() unless
- // we add a fake user to val
- kill_store(insn);
+ store = insn;
+ remove = true;
break;
}
} END_FOR_EACH_PTR(insn);
+ if (remove)
+ kill_store(store);
}
-static bool rewrite_single_store(struct instruction *store)
-{
- pseudo_t addr = store->src;
- struct pseudo_user *pu;
-
- FOR_EACH_PTR(addr->users, pu) {
- struct instruction *insn = pu->insn;
-
- if (insn->opcode != OP_LOAD)
- continue;
-
- // Let's try to replace the value of the load
- // by the value from the store. This is only valid
- // if the store dominate the load.
-
- if (insn->bb == store->bb) {
- // the load and the store are in the same BB
- // we can convert if the load is after the store.
- if (!insn_before(store, insn))
- continue;
- } else if (!domtree_dominates(store->bb, insn->bb)) {
- // we can't convert this load
- continue;
- }
-
- // OK, we can rewrite this load
-
- // undefs ?
-
- replace_with_pseudo(insn, store->target);
- } END_FOR_EACH_PTR(pu);
-
- // is there some unconverted loads?
- if (pseudo_user_list_size(addr->users) > 1)
- return false;
-
- kill_store(store);
- return true;
-}
-
-static struct sset *processed;
-
// we would like to know:
// is there one or more stores?
// are all loads & stores local/done in a single block?
static void ssa_convert_one_var(struct entrypoint *ep, struct symbol *var)
{
+ unsigned long generation = ++bb_generation;
struct basic_block_list *alpha = NULL;
struct basic_block_list *idf = NULL;
struct basic_block *samebb = NULL;
- struct instruction *store = NULL;
struct basic_block *bb;
struct pseudo_user *pu;
unsigned long mod = var->ctype.modifiers;
@@ -199,7 +159,6 @@ static void ssa_convert_one_var(struct entrypoint *ep, struct symbol *var)
return;
// 1) insert in the worklist all BBs that may modify var
- sset_reset(processed);
FOR_EACH_PTR(addr->users, pu) {
struct instruction *insn = pu->insn;
struct basic_block *bb = insn->bb;
@@ -207,9 +166,10 @@ static void ssa_convert_one_var(struct entrypoint *ep, struct symbol *var)
switch (insn->opcode) {
case OP_STORE:
nbr_stores++;
- store = insn;
- if (!sset_testset(processed, bb->nr))
+ if (bb->generation != generation) {
+ bb->generation = generation;
add_bb(&alpha, bb);
+ }
/* fall through */
case OP_LOAD:
if (local) {
@@ -229,11 +189,6 @@ static void ssa_convert_one_var(struct entrypoint *ep, struct symbol *var)
}
} END_FOR_EACH_PTR(pu);
- if (nbr_stores == 1) {
- if (rewrite_single_store(store))
- return;
- }
-
// if all uses are local to a single block
// they can easily be rewritten and doesn't need phi-nodes
// FIXME: could be done for extended BB too
@@ -255,21 +210,40 @@ external_visibility:
kill_dead_stores(ep, addr, !mod);
}
-static pseudo_t lookup_var(struct basic_block *bb, struct symbol *var)
+static struct instruction *lookup_var(struct basic_block *bb, struct symbol *var)
{
do {
- pseudo_t val = phi_map_lookup(bb->phi_map, var);
- if (val)
- return val;
+ struct instruction *insn = phi_map_lookup(bb->phi_map, var);
+ if (insn)
+ return insn;
} while ((bb = bb->idom));
- return undef_pseudo();
+ return NULL;
}
static struct instruction_list *phis_all;
static struct instruction_list *phis_used;
+static struct instruction_list *stores;
+
+static bool matching_load(struct instruction *def, struct instruction *insn)
+{
+ if (insn->size != def->size)
+ return false;
+ switch (def->opcode) {
+ case OP_STORE:
+ case OP_LOAD:
+ if (insn->offset != def->offset)
+ return false;
+ case OP_PHI:
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
static void ssa_rename_insn(struct basic_block *bb, struct instruction *insn)
{
+ struct instruction *def;
struct symbol *var;
pseudo_t addr;
pseudo_t val;
@@ -282,8 +256,8 @@ static void ssa_rename_insn(struct basic_block *bb, struct instruction *insn)
var = addr->sym;
if (!var || !var->torename)
break;
- phi_map_update(&bb->phi_map, var, insn->target);
- kill_store(insn);
+ phi_map_update(&bb->phi_map, var, insn);
+ add_instruction(&stores, insn);
break;
case OP_LOAD:
addr = insn->src;
@@ -292,14 +266,22 @@ static void ssa_rename_insn(struct basic_block *bb, struct instruction *insn)
var = addr->sym;
if (!var || !var->torename)
break;
- val = lookup_var(bb, var);
+ def = lookup_var(bb, var);
+ if (!def) {
+ val = undef_pseudo();
+ } else if (!matching_load(def, insn)) {
+ var->torename = false;
+ break;
+ } else {
+ val = def->target;
+ }
replace_with_pseudo(insn, val);
break;
case OP_PHI:
var = insn->type;
if (!var || !var->torename)
break;
- phi_map_update(&bb->phi_map, var, insn->target);
+ phi_map_update(&bb->phi_map, var, insn);
add_instruction(&phis_all, insn);
break;
}
@@ -346,7 +328,8 @@ static void ssa_rename_phi(struct instruction *insn)
return;
FOR_EACH_PTR(insn->bb->parents, par) {
struct instruction *term = delete_last_instruction(&par->insns);
- pseudo_t val = lookup_var(par, var);
+ struct instruction *def = lookup_var(par, var);
+ pseudo_t val = def ? def->target : undef_pseudo();
pseudo_t phi = alloc_phi(par, val, var);
phi->ident = var->ident;
add_instruction(&par->insns, term);
@@ -374,6 +357,18 @@ static void ssa_rename_phis(struct entrypoint *ep)
} END_FOR_EACH_PTR(phi);
}
+static void remove_dead_stores(struct instruction_list *stores)
+{
+ struct instruction *store;
+
+ FOR_EACH_PTR(stores, store) {
+ struct symbol *var = store->addr->sym;
+
+ if (var->torename)
+ kill_store(store);
+ } END_FOR_EACH_PTR(store);
+}
+
void ssa_convert(struct entrypoint *ep)
{
struct basic_block *bb;
@@ -390,9 +385,8 @@ void ssa_convert(struct entrypoint *ep)
bb->phi_map = NULL;
} END_FOR_EACH_PTR(bb);
- processed = sset_init(first, last);
-
// try to promote memory accesses to pseudos
+ stores = NULL;
FOR_EACH_PTR(ep->accesses, pseudo) {
ssa_convert_one_var(ep, pseudo->sym);
} END_FOR_EACH_PTR(pseudo);
@@ -401,4 +395,7 @@ void ssa_convert(struct entrypoint *ep)
phis_all = phis_used = NULL;
ssa_rename_insns(ep);
ssa_rename_phis(ep);
+
+ // remove now dead stores
+ remove_dead_stores(stores);
}
diff --git a/sset.c b/sset.c
deleted file mode 100644
index e9681e00..00000000
--- a/sset.c
+++ /dev/null
@@ -1,28 +0,0 @@
-// SPDX-License-Identifier: MIT
-//
-// sset.c - an all O(1) implementation of sparse sets as presented in:
-// "An Efficient Representation for Sparse Sets"
-// by Preston Briggs and Linda Torczon
-//
-// Copyright (C) 2017 - Luc Van Oostenryck
-
-#include "sset.h"
-#include "lib.h"
-#include <stdlib.h>
-
-
-struct sset *sset_init(unsigned int first, unsigned int last)
-{
- unsigned int size = last - first + 1;
- struct sset *s = malloc(sizeof(*s) + size * 2 * sizeof(s->sets[0]));
-
- s->size = size;
- s->off = first;
- s->nbr = 0;
- return s;
-}
-
-void sset_free(struct sset *s)
-{
- free(s);
-}
diff --git a/sset.h b/sset.h
deleted file mode 100644
index 69cee18a..00000000
--- a/sset.h
+++ /dev/null
@@ -1,56 +0,0 @@
-// SPDX-License-Identifier: MIT
-
-#ifndef SSET_H
-#define SSET_H
-
-/*
- * sset.h - an all O(1) implementation of sparse sets as presented in:
- * "An Efficient Representation for Sparse Sets"
- * by Preston Briggs and Linda Torczon
- *
- * Copyright (C) 2017 - Luc Van Oostenryck
- */
-
-#include <stdbool.h>
-
-struct sset {
- unsigned int nbr;
- unsigned int off;
- unsigned int size;
- unsigned int sets[0];
-};
-
-extern struct sset *sset_init(unsigned int size, unsigned int off);
-extern void sset_free(struct sset *s);
-
-
-static inline void sset_reset(struct sset *s)
-{
- s->nbr = 0;
-}
-
-static inline void sset_add(struct sset *s, unsigned int idx)
-{
- unsigned int __idx = idx - s->off;
- unsigned int n = s->nbr++;
- s->sets[__idx] = n;
- s->sets[s->size + n] = __idx;
-}
-
-static inline bool sset_test(struct sset *s, unsigned int idx)
-{
- unsigned int __idx = idx - s->off;
- unsigned int n = s->sets[__idx];
-
- return (n < s->nbr) && (s->sets[s->size + n] == __idx);
-}
-
-static inline bool sset_testset(struct sset *s, unsigned int idx)
-{
- if (sset_test(s, idx))
- return true;
- sset_add(s, idx);
- return false;
-}
-
-#endif
diff --git a/validation/knr-attr-crash.c b/validation/knr-attr-crash.c
new file mode 100644
index 00000000..176ff503
--- /dev/null
+++ b/validation/knr-attr-crash.c
@@ -0,0 +1,12 @@
+typedef int word;
+
+void foo(word x);
+
+void foo(x)
+ word x;
+{ }
+
+/*
+ * check-name: knr-attr-crash
+ * check-command: sparse -Wno-old-style-definition $file
+ */
diff --git a/validation/mem2reg/not-same-memop0.c b/validation/mem2reg/not-same-memop0.c
new file mode 100644
index 00000000..4de98195
--- /dev/null
+++ b/validation/mem2reg/not-same-memop0.c
@@ -0,0 +1,48 @@
+struct s {
+ int:16;
+ short f:6;
+};
+
+static short local(struct s s)
+{
+ return s.f;
+}
+
+static void foo(struct s s)
+{
+ while (s.f) ;
+}
+
+/*
+ * check-name: not-same-memop0
+ * check-command: test-linearize -Wno-decl -fdump-ir=mem2reg $file
+ *
+ * check-output-start
+local:
+.L0:
+ <entry-point>
+ store.32 %arg1 -> 0[s]
+ load.16 %r1 <- 2[s]
+ trunc.6 %r2 <- (16) %r1
+ sext.16 %r3 <- (6) %r2
+ ret.16 %r3
+
+
+foo:
+.L2:
+ <entry-point>
+ store.32 %arg1 -> 0[s]
+ br .L6
+
+.L6:
+ load.16 %r5 <- 2[s]
+ trunc.6 %r6 <- (16) %r5
+ setne.1 %r7 <- %r6, $0
+ cbr %r7, .L6, .L5
+
+.L5:
+ ret
+
+
+ * check-output-end
+ */
diff --git a/validation/mem2reg/packed-bitfield.c b/validation/mem2reg/packed-bitfield.c
new file mode 100644
index 00000000..f3ee259a
--- /dev/null
+++ b/validation/mem2reg/packed-bitfield.c
@@ -0,0 +1,35 @@
+struct s {
+ int:16;
+ int f:16;
+} __attribute__((__packed__));
+
+static void foo(struct s s)
+{
+ while (s.f)
+ ;
+}
+
+/*
+ * check-name: packed-bitfield
+ * check-command: test-linearize -fmem2reg $file
+ *
+ * check-output-contains: store.32
+ * check-output-contains: load.16
+ *
+ * check-output-start
+foo:
+.L0:
+ <entry-point>
+ store.32 %arg1 -> 0[s]
+ br .L4
+
+.L4:
+ load.16 %r1 <- 2[s]
+ cbr %r1, .L4, .L3
+
+.L3:
+ ret
+
+
+ * check-output-end
+ */
diff --git a/validation/optim/cmpe-and0.c b/validation/optim/cmpe-and0.c
new file mode 100644
index 00000000..75af7752
--- /dev/null
+++ b/validation/optim/cmpe-and0.c
@@ -0,0 +1,10 @@
+int cmpe_and_eq(int a) { return ((a & 0xff00) == 0xff01) + 1; }
+int cmpe_and_ne(int a) { return ((a & 0xff00) != 0xff01) + 0; }
+
+/*
+ * check-name: cmpe-and0
+ * check-command: test-linearize -Wno-decl $file
+ *
+ * check-output-ignore
+ * check-output-returns: 1
+ */
diff --git a/validation/optim/cmpe-or0.c b/validation/optim/cmpe-or0.c
new file mode 100644
index 00000000..2e89d611
--- /dev/null
+++ b/validation/optim/cmpe-or0.c
@@ -0,0 +1,10 @@
+int cmp_eq(int a) { return ((a | 1) != 0) + 0; }
+int cmp_ne(int a) { return ((a | 1) == 0) + 1; }
+
+/*
+ * check-name: cmpe-or0
+ * check-command: test-linearize -Wno-decl $file
+ *
+ * check-output-ignore
+ * check-output-returns: 1
+ */
diff --git a/validation/optim/cmps-and0.c b/validation/optim/cmps-and0.c
new file mode 100644
index 00000000..962fbd2d
--- /dev/null
+++ b/validation/optim/cmps-and0.c
@@ -0,0 +1,21 @@
+#define MINUS_ONE -1
+#define MASK 32
+
+
+int cmps_and_lt_lt0(int a) { return ((a & MASK) < MINUS_ONE) + 1; }
+int cmps_and_lt_gtm(int a) { return ((a & MASK) < (MASK + 1)) + 0; }
+int cmps_and_le_lt0(int a) { return ((a & MASK) <= MINUS_ONE) + 1; }
+int cmps_and_le_gtm(int a) { return ((a & MASK) <= (MASK + 1)) + 0; }
+
+int cmps_and_gt_lt0(int a) { return ((a & MASK) > MINUS_ONE) + 0; }
+int cmps_and_gt_gtm(int a) { return ((a & MASK) > (MASK + 1)) + 1; }
+int cmps_and_ge_lt0(int a) { return ((a & MASK) >= MINUS_ONE) + 0; }
+int cmps_and_ge_gtm(int a) { return ((a & MASK) >= (MASK + 1)) + 1; }
+
+/*
+ * check-name: cmps-and0
+ * check-command: test-linearize -Wno-decl $file
+ *
+ * check-output-ignore
+ * check-output-returns: 1
+ */
diff --git a/validation/optim/cmps-minmax.c b/validation/optim/cmps-minmax.c
index 5802cdbc..0b1a0a09 100644
--- a/validation/optim/cmps-minmax.c
+++ b/validation/optim/cmps-minmax.c
@@ -1,11 +1,11 @@
#define SMAX __INT_MAX__
#define SMIN (-__INT_MAX__-1)
-int lt_smin(int a) { return (a < SMIN) == 0; }
-int le_smax(int a) { return (a <= SMAX) == 1; }
+int lt_smin(int a) { return (a < SMIN) + 1; }
+int le_smax(int a) { return (a <= SMAX) + 0; }
-int ge_smin(int a) { return (a >= SMIN) == 1; }
-int gt_smax(int a) { return (a > SMAX) == 0; }
+int ge_smin(int a) { return (a >= SMIN) + 0; }
+int gt_smax(int a) { return (a > SMAX) + 1; }
/*
* check-name: cmps-minmax
diff --git a/validation/optim/cmps-or0.c b/validation/optim/cmps-or0.c
new file mode 100644
index 00000000..70fcb024
--- /dev/null
+++ b/validation/optim/cmps-or0.c
@@ -0,0 +1,21 @@
+#define EQ(X) + (X == 0)
+#define SIGN (1 << 31)
+#define MASK (SIGN | 32)
+
+
+int cmps_ior_lt_x(int a) { return ((a | MASK) < 4) EQ(1); }
+int cmps_ior_lt_0(int a) { return ((a | MASK) < 0) EQ(1); }
+int cmps_ior_le_x(int a) { return ((a | MASK) <= 4) EQ(1); }
+int cmps_ior_le_0(int a) { return ((a | MASK) <= 0) EQ(1); }
+int cmps_ior_ge_x(int a) { return ((a | MASK) >= 4) EQ(0); }
+int cmps_ior_ge_0(int a) { return ((a | MASK) >= 0) EQ(0); }
+int cmps_ior_gt_x(int a) { return ((a | MASK) > 4) EQ(0); }
+int cmps_ior_gt_0(int a) { return ((a | MASK) > 0) EQ(0); }
+
+/*
+ * check-name: cmps-or0
+ * check-command: test-linearize -Wno-decl $file
+ *
+ * check-output-ignore
+ * check-output-returns: 1
+ */
diff --git a/validation/optim/cmps0-and0.c b/validation/optim/cmps0-and0.c
new file mode 100644
index 00000000..8316916a
--- /dev/null
+++ b/validation/optim/cmps0-and0.c
@@ -0,0 +1,12 @@
+#define M 32
+
+int cmps_and_sle0(int a) { return ((a & M) <= 0) == ((a & M) == 0); }
+int cmps_and_sgt0(int a) { return ((a & M) > 0) == ((a & M) != 0); }
+
+/*
+ * check-name: cmps0-and
+ * check-command: test-linearize -Wno-decl $file
+ *
+ * check-output-ignore
+ * check-output-returns: 1
+ */
diff --git a/validation/optim/cmpu-and0.c b/validation/optim/cmpu-and0.c
new file mode 100644
index 00000000..927b9fb6
--- /dev/null
+++ b/validation/optim/cmpu-and0.c
@@ -0,0 +1,17 @@
+#define MASK 32U
+
+
+int cmps_and_ltu_gt(int a) { return ((a & MASK) < (MASK + 1)) + 0; }
+int cmps_and_leu_gt(int a) { return ((a & MASK) <= (MASK + 1)) + 0; }
+int cmps_and_leu_eq(int a) { return ((a & MASK) <= (MASK + 0)) + 0; }
+int cmps_and_geu_gt(int a) { return ((a & MASK) >= (MASK + 1)) + 1; }
+int cmps_and_gtu_gt(int a) { return ((a & MASK) > (MASK + 1)) + 1; }
+int cmps_and_gtu_eq(int a) { return ((a & MASK) > (MASK + 0)) + 1; }
+
+/*
+ * check-name: cmpu-and0
+ * check-command: test-linearize -Wno-decl $file
+ *
+ * check-output-ignore
+ * check-output-returns: 1
+ */
diff --git a/validation/optim/cmpu-or0.c b/validation/optim/cmpu-or0.c
new file mode 100644
index 00000000..e97e9180
--- /dev/null
+++ b/validation/optim/cmpu-or0.c
@@ -0,0 +1,18 @@
+#define EQ(X) + (X == 0)
+#define MASK 32U
+
+
+int cmpu_ior_lt_lt(int a) { return ((a | MASK) < (MASK - 1)) EQ(0); }
+int cmpu_ior_lt_eq(int a) { return ((a | MASK) < (MASK )) EQ(0); }
+int cmpu_ior_le_lt(int a) { return ((a | MASK) <= (MASK - 1)) EQ(0); }
+int cmpu_ior_ge_lt(int a) { return ((a | MASK) >= (MASK - 1)) EQ(1); }
+int cmpu_ior_ge_eq(int a) { return ((a | MASK) >= (MASK )) EQ(1); }
+int cmpu_ior_gt_lt(int a) { return ((a | MASK) > (MASK - 1)) EQ(1); }
+
+/*
+ * check-name: cmpu-or0
+ * check-command: test-linearize -Wno-decl $file
+ *
+ * check-output-ignore
+ * check-output-returns: 1
+ */
diff --git a/validation/test-suite b/validation/test-suite
index 2f7950ef..305edd1f 100755
--- a/validation/test-suite
+++ b/validation/test-suite
@@ -515,6 +515,7 @@ echo "options:"
echo " -a append the created test to the input file"
echo " -f write a test known to fail"
echo " -l write a test for linearized code"
+echo " -r write a test for linearized code returning 1"
echo " -p write a test for pre-processing"
echo " -s write a test for symbolic checking"
echo
@@ -532,6 +533,7 @@ do_format()
append=0
linear=0
fail=0
+ ret=''
while [ $# -gt 0 ] ; do
case "$1" in
@@ -542,6 +544,9 @@ do_format()
-l)
def_cmd='test-linearize -Wno-decl $file'
linear=1 ;;
+ -r)
+ def_cmd='test-linearize -Wno-decl $file'
+ ret=1 ;;
-p)
def_cmd='sparse -E $file' ;;
-s)
@@ -588,6 +593,12 @@ _EOF
if [ $fail != 0 ]; then
echo " * check-known-to-fail"
fi
+ if [ "$ret" != '' ]; then
+ echo ' *'
+ echo ' * check-output-ignore'
+ echo " * check-output-returns: $ret"
+ rm -f "$file.output.got"
+ fi
if [ $linear != 0 ]; then
echo ' *'
echo ' * check-output-ignore'