aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorLuc Van Oostenryck <luc.vanoostenryck@gmail.com>2020-10-22 06:42:53 +0200
committerLuc Van Oostenryck <luc.vanoostenryck@gmail.com>2020-10-22 06:42:53 +0200
commitcfca7b4c6cb48283cb554fc91dc859ff669f2547 (patch)
tree422731d0d1ae6d02002846fcc9a3eef796410e47
parent10fd1d83ecd96e67e2a8f392a9c41f5252b7752d (diff)
parenta02413d4763bf2ddff981996f4236b2654477f6b (diff)
downloadsparse-cfca7b4c6cb48283cb554fc91dc859ff669f2547.tar.gz
Merge branch 'optim-base' into next
* essential OP_ADD & OP_SUB simplifications
-rw-r--r--opcode.def50
-rw-r--r--opcode.h5
-rw-r--r--simplify.c250
-rw-r--r--validation/optim/canonical-sub-cte.c9
-rw-r--r--validation/optim/reassoc-op-op1.c14
-rw-r--r--validation/optim/simplify-add-neg.c9
-rw-r--r--validation/optim/simplify-cte-sub-addl.c9
-rw-r--r--validation/optim/simplify-cte-sub-addr.c9
-rw-r--r--validation/optim/simplify-cte-sub-subr.c9
-rw-r--r--validation/optim/simplify-neg-add.c9
-rw-r--r--validation/optim/simplify-same-add-subl.c15
-rw-r--r--validation/optim/simplify-same-add-subr.c15
-rw-r--r--validation/optim/simplify-same-addl-sub.c9
-rw-r--r--validation/optim/simplify-same-sub-addl.c9
-rw-r--r--validation/optim/simplify-same-subl-add.c11
-rw-r--r--validation/optim/simplify-same-subr-add.c11
-rw-r--r--validation/optim/simplify-sub-neg.c9
-rw-r--r--validation/optim/simplify-zero-sub.c9
18 files changed, 397 insertions, 64 deletions
diff --git a/opcode.def b/opcode.def
index 1401d988..c65722f0 100644
--- a/opcode.def
+++ b/opcode.def
@@ -15,16 +15,16 @@ OPCODE(COMPUTEDGOTO, BADOP, BADOP, BADOP, 1, OPF_NONE)
OPCODE_RANGE(TERMINATOR, RET, COMPUTEDGOTO)
/* Binary */
-OPCODE(ADD, BADOP, BADOP, FADD, 2, OPF_TARGET)
-OPCODE(SUB, BADOP, BADOP, FSUB, 2, OPF_TARGET)
-OPCODE(MUL, BADOP, BADOP, FMUL, 2, OPF_TARGET)
-OPCODE(DIVU, BADOP, BADOP, FDIV, 2, OPF_TARGET)
-OPCODE(DIVS, BADOP, BADOP, FDIV, 2, OPF_TARGET)
-OPCODE(MODU, BADOP, BADOP, BADOP, 2, OPF_TARGET)
-OPCODE(MODS, BADOP, BADOP, BADOP, 2, OPF_TARGET)
-OPCODE(SHL, BADOP, BADOP, BADOP, 2, OPF_TARGET)
-OPCODE(LSR, BADOP, BADOP, BADOP, 2, OPF_TARGET)
-OPCODE(ASR, BADOP, BADOP, BADOP, 2, OPF_TARGET)
+OPCODE(ADD, BADOP, BADOP, FADD, 2, OPF_TARGET|OPF_COMMU|OPF_ASSOC|OPF_BINOP)
+OPCODE(MUL, BADOP, BADOP, FMUL, 2, OPF_TARGET|OPF_COMMU|OPF_ASSOC|OPF_BINOP)
+OPCODE(SUB, BADOP, BADOP, FSUB, 2, OPF_TARGET|OPF_BINOP)
+OPCODE(DIVU, BADOP, BADOP, FDIV, 2, OPF_TARGET|OPF_BINOP)
+OPCODE(DIVS, BADOP, BADOP, FDIV, 2, OPF_TARGET|OPF_BINOP)
+OPCODE(MODU, BADOP, BADOP, BADOP, 2, OPF_TARGET|OPF_BINOP)
+OPCODE(MODS, BADOP, BADOP, BADOP, 2, OPF_TARGET|OPF_BINOP)
+OPCODE(SHL, BADOP, BADOP, BADOP, 2, OPF_TARGET|OPF_BINOP)
+OPCODE(LSR, BADOP, BADOP, BADOP, 2, OPF_TARGET|OPF_BINOP)
+OPCODE(ASR, BADOP, BADOP, BADOP, 2, OPF_TARGET|OPF_BINOP)
/* Floating-point binops */
OPCODE(FADD, BADOP, BADOP, BADOP, 2, OPF_TARGET)
@@ -33,9 +33,9 @@ OPCODE(FMUL, BADOP, BADOP, BADOP, 2, OPF_TARGET)
OPCODE(FDIV, BADOP, BADOP, BADOP, 2, OPF_TARGET)
/* Logical */
-OPCODE(AND, BADOP, BADOP, BADOP, 2, OPF_TARGET)
-OPCODE(OR, BADOP, BADOP, BADOP, 2, OPF_TARGET)
-OPCODE(XOR, BADOP, BADOP, BADOP, 2, OPF_TARGET)
+OPCODE(AND, BADOP, BADOP, BADOP, 2, OPF_TARGET|OPF_COMMU|OPF_ASSOC|OPF_BINOP)
+OPCODE(OR, BADOP, BADOP, BADOP, 2, OPF_TARGET|OPF_COMMU|OPF_ASSOC|OPF_BINOP)
+OPCODE(XOR, BADOP, BADOP, BADOP, 2, OPF_TARGET|OPF_COMMU|OPF_ASSOC|OPF_BINOP)
OPCODE_RANGE(BINARY, ADD, XOR)
/* floating-point comparison */
@@ -56,21 +56,21 @@ OPCODE(FCMP_UNO, FCMP_ORD, FCMP_UNO, BADOP, 2, OPF_TARGET)
OPCODE_RANGE(FPCMP, FCMP_ORD, FCMP_UNO)
/* Binary comparison */
-OPCODE(SET_EQ, SET_NE, SET_EQ, FCMP_OEQ, 2, OPF_TARGET)
-OPCODE(SET_LT, SET_GE, SET_GT, FCMP_OLT, 2, OPF_TARGET)
-OPCODE(SET_LE, SET_GT, SET_GE, FCMP_OLE, 2, OPF_TARGET)
-OPCODE(SET_GE, SET_LT, SET_LE, FCMP_OGE, 2, OPF_TARGET)
-OPCODE(SET_GT, SET_LE, SET_LT, FCMP_OGT, 2, OPF_TARGET)
-OPCODE(SET_B, SET_AE, SET_A, FCMP_OLT, 2, OPF_TARGET)
-OPCODE(SET_BE, SET_A, SET_AE, FCMP_OLE, 2, OPF_TARGET)
-OPCODE(SET_AE, SET_B, SET_BE, FCMP_OGE, 2, OPF_TARGET)
-OPCODE(SET_A, SET_BE, SET_B, FCMP_OGT, 2, OPF_TARGET)
-OPCODE(SET_NE, SET_EQ, SET_NE, FCMP_UNE, 2, OPF_TARGET)
+OPCODE(SET_EQ, SET_NE, SET_EQ, FCMP_OEQ, 2, OPF_TARGET|OPF_BINOP|OPF_COMPARE|OPF_COMMU)
+OPCODE(SET_LT, SET_GE, SET_GT, FCMP_OLT, 2, OPF_TARGET|OPF_BINOP|OPF_COMPARE)
+OPCODE(SET_LE, SET_GT, SET_GE, FCMP_OLE, 2, OPF_TARGET|OPF_BINOP|OPF_COMPARE)
+OPCODE(SET_GE, SET_LT, SET_LE, FCMP_OGE, 2, OPF_TARGET|OPF_BINOP|OPF_COMPARE)
+OPCODE(SET_GT, SET_LE, SET_LT, FCMP_OGT, 2, OPF_TARGET|OPF_BINOP|OPF_COMPARE)
+OPCODE(SET_B, SET_AE, SET_A, FCMP_OLT, 2, OPF_TARGET|OPF_BINOP|OPF_COMPARE)
+OPCODE(SET_BE, SET_A, SET_AE, FCMP_OLE, 2, OPF_TARGET|OPF_BINOP|OPF_COMPARE)
+OPCODE(SET_AE, SET_B, SET_BE, FCMP_OGE, 2, OPF_TARGET|OPF_BINOP|OPF_COMPARE)
+OPCODE(SET_A, SET_BE, SET_B, FCMP_OGT, 2, OPF_TARGET|OPF_BINOP|OPF_COMPARE)
+OPCODE(SET_NE, SET_EQ, SET_NE, FCMP_UNE, 2, OPF_TARGET|OPF_BINOP|OPF_COMPARE|OPF_COMMU)
OPCODE_RANGE(BINCMP, SET_EQ, SET_NE)
/* Uni */
-OPCODE(NOT, BADOP, BADOP, BADOP, 1, OPF_TARGET)
-OPCODE(NEG, BADOP, BADOP, FNEG, 1, OPF_TARGET)
+OPCODE(NOT, BADOP, BADOP, BADOP, 1, OPF_TARGET|OPF_UNOP)
+OPCODE(NEG, BADOP, BADOP, FNEG, 1, OPF_TARGET|OPF_UNOP)
OPCODE(FNEG, BADOP, BADOP, BADOP, 1, OPF_TARGET)
OPCODE(TRUNC, BADOP, BADOP, BADOP, 1, OPF_TARGET)
OPCODE(ZEXT, BADOP, BADOP, BADOP, 1, OPF_TARGET)
diff --git a/opcode.h b/opcode.h
index e426bed4..bb94ec81 100644
--- a/opcode.h
+++ b/opcode.h
@@ -20,6 +20,11 @@ extern const struct opcode_table {
unsigned int flags:6;
#define OPF_NONE 0
#define OPF_TARGET (1 << 0)
+#define OPF_COMMU (1 << 1)
+#define OPF_ASSOC (1 << 2)
+#define OPF_UNOP (1 << 3)
+#define OPF_BINOP (1 << 4)
+#define OPF_COMPARE (1 << 5)
} opcode_table[];
diff --git a/simplify.c b/simplify.c
index 15452a58..6caf6cbc 100644
--- a/simplify.c
+++ b/simplify.c
@@ -470,6 +470,20 @@ static inline int replace_with_value(struct instruction *insn, long long val)
return replace_with_pseudo(insn, value_pseudo(val));
}
+///
+// replace a binop with an unop
+// @insn: the instruction to be replaced
+// @op: the instruction's new opcode
+// @src: the instruction's new operand
+// @return: REPEAT_CSE
+static inline int replace_with_unop(struct instruction *insn, int op, pseudo_t src)
+{
+ insn->opcode = op;
+ replace_pseudo(insn, &insn->src1, src);
+ remove_usage(insn->src2, &insn->src2);
+ return REPEAT_CSE;
+}
+
static inline int def_opcode(pseudo_t p)
{
if (p->type != PSEUDO_REG)
@@ -516,12 +530,11 @@ static unsigned int operand_size(struct instruction *insn, pseudo_t pseudo)
return size;
}
-static pseudo_t eval_insn(struct instruction *insn)
+static pseudo_t eval_op(int op, unsigned size, pseudo_t src1, pseudo_t src2)
{
/* FIXME! Verify signs and sizes!! */
- unsigned int size = insn->size;
- long long left = insn->src1->value;
- long long right = insn->src2->value;
+ long long left = src1->value;
+ long long right = src2->value;
unsigned long long ul, ur;
long long res, mask, bits;
@@ -535,7 +548,14 @@ static pseudo_t eval_insn(struct instruction *insn)
ul = left & bits;
ur = right & bits;
- switch (insn->opcode) {
+ switch (op) {
+ case OP_NEG:
+ res = -left;
+ break;
+ case OP_NOT:
+ res = ~ul;
+ break;
+
case OP_ADD:
res = left + right;
break;
@@ -637,6 +657,11 @@ undef:
return NULL;
}
+static inline pseudo_t eval_unop(int op, unsigned size, pseudo_t src)
+{
+ return eval_op(op, size, src, VOID);
+}
+
///
// Simplifications
// ^^^^^^^^^^^^^^^
@@ -754,6 +779,11 @@ static int simplify_mask_shift(struct instruction *sh, unsigned long long mask)
return 0;
}
+static pseudo_t eval_insn(struct instruction *insn)
+{
+ return eval_op(insn->opcode, insn->size, insn->src1, insn->src2);
+}
+
static long long check_shift_count(struct instruction *insn, unsigned long long uval)
{
unsigned int size = insn->size;
@@ -1066,11 +1096,30 @@ static int simplify_constant_mask(struct instruction *insn, unsigned long long m
return 0;
}
+static int simplify_const_rightadd(struct instruction *def, struct instruction *insn)
+{
+ unsigned size = insn->size;
+ pseudo_t src2 = insn->src2;
+
+ switch (def->opcode) {
+ case OP_SUB:
+ if (constant(def->src1)) { // (C - y) + D --> eval(C+D) - y
+ pseudo_t val = eval_op(OP_ADD, size, def->src1, src2);
+ insn->opcode = OP_SUB;
+ use_pseudo(insn, def->src2, &insn->src2);
+ return replace_pseudo(insn, &insn->src1, val);
+ }
+ break;
+ }
+ return 0;
+}
+
static int simplify_constant_rightside(struct instruction *insn)
{
long long value = insn->src2->value;
long long sbit = 1ULL << (insn->size - 1);
long long bits = sbit | (sbit - 1);
+ int changed = 0;
switch (insn->opcode) {
case OP_OR:
@@ -1083,20 +1132,23 @@ static int simplify_constant_rightside(struct instruction *insn)
insn->opcode = OP_NOT;
return REPEAT_CSE;
}
- goto case_neutral_zero;
+ /* fallthrough */
+ case_neutral_zero:
+ if (!value)
+ return replace_with_pseudo(insn, insn->src1);
+ return 0;
case OP_SUB:
- if (value) {
- insn->opcode = OP_ADD;
- insn->src2 = value_pseudo(-value);
- return REPEAT_CSE;
- }
- /* Fall through */
+ insn->opcode = OP_ADD;
+ insn->src2 = eval_unop(OP_NEG, insn->size, insn->src2);
+ changed = REPEAT_CSE;
+ /* fallthrough */
case OP_ADD:
- case_neutral_zero:
if (!value)
return replace_with_pseudo(insn, insn->src1);
- return 0;
+ if (insn->src1->type == PSEUDO_REG) // (x # y) + z
+ changed |= simplify_const_rightadd(insn->src1->def, insn);
+ return changed;
case OP_ASR:
case OP_SHL:
case OP_LSR:
@@ -1125,6 +1177,30 @@ static int simplify_constant_rightside(struct instruction *insn)
return 0;
}
+static int simplify_const_leftsub(struct instruction *insn, struct instruction *def)
+{
+ unsigned size = insn->size;
+ pseudo_t src1 = insn->src1;
+
+ switch (def->opcode) {
+ case OP_ADD:
+ if (constant(def->src2)) { // C - (y + D) --> eval(C-D) - y
+ insn->src1 = eval_op(OP_SUB, size, src1, def->src2);
+ return replace_pseudo(insn, &insn->src2, def->src1);
+ }
+ break;
+ case OP_SUB:
+ if (constant(def->src1)) { // C - (D - z) --> z + eval(C-D)
+ pseudo_t val = eval_op(OP_SUB, size, src1, def->src1);
+ insn->opcode = OP_ADD;
+ use_pseudo(insn, def->src2, &insn->src1);
+ return replace_pseudo(insn, &insn->src2, val);
+ }
+ break;
+ }
+ return 0;
+}
+
static int simplify_constant_leftside(struct instruction *insn)
{
long long value = insn->src1->value;
@@ -1142,6 +1218,12 @@ static int simplify_constant_leftside(struct instruction *insn)
if (!value)
return replace_with_pseudo(insn, insn->src1);
return 0;
+ case OP_SUB:
+ if (!value) // (0 - x) --> -x
+ return replace_with_unop(insn, OP_NEG, insn->src2);
+ if (insn->src2->type == PSEUDO_REG)
+ return simplify_const_leftsub(insn, insn->src2->def);
+ break;
}
return 0;
}
@@ -1202,7 +1284,7 @@ static int simplify_binop(struct instruction *insn)
return 0;
}
-static void switch_pseudo(struct instruction *insn1, pseudo_t *pp1, struct instruction *insn2, pseudo_t *pp2)
+static int switch_pseudo(struct instruction *insn1, pseudo_t *pp1, struct instruction *insn2, pseudo_t *pp2)
{
pseudo_t p1 = *pp1, p2 = *pp2;
@@ -1210,6 +1292,7 @@ static void switch_pseudo(struct instruction *insn1, pseudo_t *pp1, struct instr
use_pseudo(insn2, p1, pp2);
remove_usage(p1, pp1);
remove_usage(p2, pp2);
+ return REPEAT_CSE;
}
static int canonical_order(pseudo_t p1, pseudo_t p2)
@@ -1264,12 +1347,78 @@ static int simplify_associative_binop(struct instruction *insn)
return 0;
if (!simple_pseudo(def->src2))
return 0;
+ if (constant(def->src2) && constant(insn->src2)) {
+ // (x # C) # K --> x # eval(C # K)
+ insn->src2 = eval_op(insn->opcode, insn->size, insn->src2, def->src2);
+ return replace_pseudo(insn, &insn->src1, def->src1);
+ }
if (multi_users(def->target))
return 0;
switch_pseudo(def, &def->src1, insn, &insn->src2);
return REPEAT_CSE;
}
+static int simplify_add(struct instruction *insn)
+{
+ pseudo_t src1 = insn->src1;
+ pseudo_t src2 = insn->src2;
+ struct instruction *def;
+
+ switch (DEF_OPCODE(def, src1)) {
+ case OP_NEG: // (-x + y) --> (y - x)
+ switch_pseudo(insn, &insn->src1, insn, &insn->src2);
+ insn->opcode = OP_SUB;
+ return replace_pseudo(insn, &insn->src2, def->src);
+
+ case OP_SUB:
+ if (def->src2 == src2) // (x - y) + y --> x
+ return replace_with_pseudo(insn, def->src1);
+ break;
+ }
+
+ switch (DEF_OPCODE(def, src2)) {
+ case OP_NEG: // (x + -y) --> (x - y)
+ insn->opcode = OP_SUB;
+ return replace_pseudo(insn, &insn->src2, def->src);
+ case OP_SUB:
+ if (src1 == def->src2) // x + (y - x) --> y
+ return replace_with_pseudo(insn, def->src1);
+ break;
+ }
+
+ return 0;
+}
+
+static int simplify_sub(struct instruction *insn)
+{
+ pseudo_t src1 = insn->src1;
+ pseudo_t src2 = insn->src2;
+ struct instruction *def;
+
+ switch (DEF_OPCODE(def, src1)) {
+ case OP_ADD:
+ if (def->src1 == src2) // (x + y) - x --> y
+ return replace_with_pseudo(insn, def->src2);
+ if (def->src2 == src2) // (x + y) - y --> x
+ return replace_with_pseudo(insn, def->src1);
+ break;
+ }
+
+ switch (DEF_OPCODE(def, src2)) {
+ case OP_ADD:
+ if (src1 == def->src1) // x - (x + z) --> -z
+ return replace_with_unop(insn, OP_NEG, def->src2);
+ if (src1 == def->src2) // x - (y + x) --> -y
+ return replace_with_unop(insn, OP_NEG, def->src1);
+ break;
+ case OP_NEG: // (x - -y) --> (x + y)
+ insn->opcode = OP_ADD;
+ return replace_pseudo(insn, &insn->src2, def->src);
+ }
+
+ return 0;
+}
+
static int simplify_constant_unop(struct instruction *insn)
{
long long val = insn->src1->value;
@@ -1705,35 +1854,57 @@ found:
int simplify_instruction(struct instruction *insn)
{
+ unsigned flags;
+ int changed = 0;
+
if (!insn->bb)
return 0;
- switch (insn->opcode) {
- case OP_ADD: case OP_MUL:
- case OP_AND: case OP_OR: case OP_XOR:
- canonicalize_commutative(insn);
- if (simplify_binop(insn))
- return REPEAT_CSE;
- return simplify_associative_binop(insn);
- case OP_SET_EQ: case OP_SET_NE:
- canonicalize_commutative(insn);
- return simplify_binop(insn);
+ flags = opcode_table[insn->opcode].flags;
+ if (flags & OPF_COMMU)
+ canonicalize_commutative(insn) ;
+ if (flags & OPF_COMPARE)
+ canonicalize_compare(insn) ;
+ if (flags & OPF_BINOP) {
+ if ((changed = simplify_binop(insn)))
+ return changed;
+ }
+ if (flags & OPF_ASSOC) {
+ if ((changed = simplify_associative_binop(insn)))
+ return changed;
+ }
+ if (flags & OPF_UNOP) {
+ if ((changed = simplify_unop(insn)))
+ return changed;
+ }
- case OP_SET_LE: case OP_SET_GE:
- case OP_SET_LT: case OP_SET_GT:
- case OP_SET_B: case OP_SET_A:
- case OP_SET_BE: case OP_SET_AE:
- canonicalize_compare(insn);
- /* fall through */
- case OP_SUB:
- case OP_DIVU: case OP_DIVS:
- case OP_MODU: case OP_MODS:
+ switch (insn->opcode) {
+ case OP_ADD: return simplify_add(insn);
+ case OP_SUB: return simplify_sub(insn);
+ case OP_MUL:
+ case OP_AND:
+ case OP_OR:
+ case OP_XOR:
case OP_SHL:
- case OP_LSR: case OP_ASR:
- return simplify_binop(insn);
-
- case OP_NOT: case OP_NEG: case OP_FNEG:
- return simplify_unop(insn);
+ case OP_LSR:
+ case OP_ASR:
+ case OP_NOT:
+ case OP_NEG:
+ case OP_DIVU:
+ case OP_DIVS:
+ case OP_MODU:
+ case OP_MODS:
+ case OP_SET_EQ:
+ case OP_SET_NE:
+ case OP_SET_LE:
+ case OP_SET_GE:
+ case OP_SET_LT:
+ case OP_SET_GT:
+ case OP_SET_B:
+ case OP_SET_A:
+ case OP_SET_BE:
+ case OP_SET_AE:
+ break;
case OP_LOAD:
if (!has_users(insn->target))
return kill_instruction(insn);
@@ -1747,6 +1918,7 @@ int simplify_instruction(struct instruction *insn)
case OP_SEXT: case OP_ZEXT:
case OP_TRUNC:
return simplify_cast(insn);
+ case OP_FNEG:
case OP_FCVTU: case OP_FCVTS:
case OP_UCVTF: case OP_SCVTF:
case OP_FCVTF:
diff --git a/validation/optim/canonical-sub-cte.c b/validation/optim/canonical-sub-cte.c
new file mode 100644
index 00000000..c0072884
--- /dev/null
+++ b/validation/optim/canonical-sub-cte.c
@@ -0,0 +1,9 @@
+int sub_cte(int x) { return (x - 1) != (x + -1); }
+
+/*
+ * check-name: canonical-sub-cte
+ * check-command: test-linearize -Wno-decl $file
+ *
+ * check-output-ignore
+ * check-output-contains: ret\\..*\\$0
+ */
diff --git a/validation/optim/reassoc-op-op1.c b/validation/optim/reassoc-op-op1.c
new file mode 100644
index 00000000..3d050c14
--- /dev/null
+++ b/validation/optim/reassoc-op-op1.c
@@ -0,0 +1,14 @@
+int foo(int x, int *ptr)
+{
+ int t = x + 1;
+ *ptr = t;
+ return t + -1;
+}
+
+/*
+ * check-name: reassoc-op-op1
+ * check-command: test-linearize -Wno-decl $file
+ *
+ * check-output-ignore
+ * check-output-pattern(1): add\\.
+ */
diff --git a/validation/optim/simplify-add-neg.c b/validation/optim/simplify-add-neg.c
new file mode 100644
index 00000000..c24b8e19
--- /dev/null
+++ b/validation/optim/simplify-add-neg.c
@@ -0,0 +1,9 @@
+int add_neg(int x, int y) { return x + -y; }
+
+/*
+ * check-name: simplify-add-neg
+ * check-command: test-linearize -Wno-decl $file
+ *
+ * check-output-ignore
+ * check-output-contains: sub\\..*%arg1, %arg2
+ */
diff --git a/validation/optim/simplify-cte-sub-addl.c b/validation/optim/simplify-cte-sub-addl.c
new file mode 100644
index 00000000..13702963
--- /dev/null
+++ b/validation/optim/simplify-cte-sub-addl.c
@@ -0,0 +1,9 @@
+int cte_sub_addl(int x) { return (1 - x) + 1; }
+
+/*
+ * check-name: simplify-cte-sub-addl
+ * check-command: test-linearize -Wno-decl $file
+ *
+ * check-output-ignore
+ * check-output-contains: sub\\..*\\$2, %arg1
+ */
diff --git a/validation/optim/simplify-cte-sub-addr.c b/validation/optim/simplify-cte-sub-addr.c
new file mode 100644
index 00000000..bf36c8a0
--- /dev/null
+++ b/validation/optim/simplify-cte-sub-addr.c
@@ -0,0 +1,9 @@
+int cte_sub_addr(int x) { return 2 - (x + 1); }
+
+/*
+ * check-name: simplify-cte-sub-addr
+ * check-command: test-linearize -Wno-decl $file
+ *
+ * check-output-ignore
+ * check-output-contains: sub\\..*\\$1, %arg1
+ */
diff --git a/validation/optim/simplify-cte-sub-subr.c b/validation/optim/simplify-cte-sub-subr.c
new file mode 100644
index 00000000..10fdbbc8
--- /dev/null
+++ b/validation/optim/simplify-cte-sub-subr.c
@@ -0,0 +1,9 @@
+int cte_sub_subr(int x) { return 1 - (1 - x); }
+
+/*
+ * check-name: simplify-cte-sub-subr
+ * check-command: test-linearize -Wno-decl $file
+ *
+ * check-output-ignore
+ * check-output-contains: ret\\..* %arg1
+ */
diff --git a/validation/optim/simplify-neg-add.c b/validation/optim/simplify-neg-add.c
new file mode 100644
index 00000000..6223b4f9
--- /dev/null
+++ b/validation/optim/simplify-neg-add.c
@@ -0,0 +1,9 @@
+int neg_add(int x, int y) { return -x + y; }
+
+/*
+ * check-name: simplify-neg-add
+ * check-command: test-linearize -Wno-decl $file
+ *
+ * check-output-ignore
+ * check-output-contains: sub\\..*%arg2, %arg1
+ */
diff --git a/validation/optim/simplify-same-add-subl.c b/validation/optim/simplify-same-add-subl.c
new file mode 100644
index 00000000..394e7dc5
--- /dev/null
+++ b/validation/optim/simplify-same-add-subl.c
@@ -0,0 +1,15 @@
+int add_subl(int x, int y) { return (x + y) - x; }
+
+/*
+ * check-name: simplify-same-add-subl
+ * check-command: test-linearize -Wno-decl $file
+ *
+ * check-output-start
+add_subl:
+.L0:
+ <entry-point>
+ ret.32 %arg2
+
+
+ * check-output-end
+ */
diff --git a/validation/optim/simplify-same-add-subr.c b/validation/optim/simplify-same-add-subr.c
new file mode 100644
index 00000000..071021c4
--- /dev/null
+++ b/validation/optim/simplify-same-add-subr.c
@@ -0,0 +1,15 @@
+int add_subr(int x, int y) { return (x + y) - y; }
+
+/*
+ * check-name: simplify-same-add-subr
+ * check-command: test-linearize -Wno-decl $file
+ *
+ * check-output-start
+add_subr:
+.L0:
+ <entry-point>
+ ret.32 %arg1
+
+
+ * check-output-end
+ */
diff --git a/validation/optim/simplify-same-addl-sub.c b/validation/optim/simplify-same-addl-sub.c
new file mode 100644
index 00000000..9cca1d12
--- /dev/null
+++ b/validation/optim/simplify-same-addl-sub.c
@@ -0,0 +1,9 @@
+int foo(int x, int y) { return x + (y - x); }
+
+/*
+ * check-name: simplify-same-addl-sub
+ * check-command: test-linearize -Wno-decl $file
+ *
+ * check-output-ignore
+ * check-output-contains: ret\\..*%arg2
+ */
diff --git a/validation/optim/simplify-same-sub-addl.c b/validation/optim/simplify-same-sub-addl.c
new file mode 100644
index 00000000..56b6fcc2
--- /dev/null
+++ b/validation/optim/simplify-same-sub-addl.c
@@ -0,0 +1,9 @@
+int foo(int x, int y) { return (x - y) + y; }
+
+/*
+ * check-name: simplify-same-sub-addl
+ * check-command: test-linearize -Wno-decl $file
+ *
+ * check-output-ignore
+ * check-output-contains: ret\\..*%arg1
+ */
diff --git a/validation/optim/simplify-same-subl-add.c b/validation/optim/simplify-same-subl-add.c
new file mode 100644
index 00000000..e7ae0809
--- /dev/null
+++ b/validation/optim/simplify-same-subl-add.c
@@ -0,0 +1,11 @@
+int subl_add(int x, int y) { return x - (x + y); }
+
+/*
+ * check-name: simplify-same-subl-add
+ * check-command: test-linearize -Wno-decl $file
+ *
+ * check-output-ignore
+ * check-output-contains: neg\\..* %arg2
+ * check-output-excludes: add\\.
+ * check-output-excludes: sub\\.
+ */
diff --git a/validation/optim/simplify-same-subr-add.c b/validation/optim/simplify-same-subr-add.c
new file mode 100644
index 00000000..950c55f6
--- /dev/null
+++ b/validation/optim/simplify-same-subr-add.c
@@ -0,0 +1,11 @@
+int subr_add(int x, int y) { return x - (y + x); }
+
+/*
+ * check-name: simplify-same-subr-add
+ * check-command: test-linearize -Wno-decl $file
+ *
+ * check-output-ignore
+ * check-output-contains: neg\\..* %arg2
+ * check-output-excludes: add\\.
+ * check-output-excludes: sub\\.
+ */
diff --git a/validation/optim/simplify-sub-neg.c b/validation/optim/simplify-sub-neg.c
new file mode 100644
index 00000000..49be847c
--- /dev/null
+++ b/validation/optim/simplify-sub-neg.c
@@ -0,0 +1,9 @@
+int sub_neg(int x, int y) { return x - -y; }
+
+/*
+ * check-name: simplify-sub-neg
+ * check-command: test-linearize -Wno-decl $file
+ *
+ * check-output-ignore
+ * check-output-contains: add\\..*%arg., %arg.
+ */
diff --git a/validation/optim/simplify-zero-sub.c b/validation/optim/simplify-zero-sub.c
new file mode 100644
index 00000000..70ce7c90
--- /dev/null
+++ b/validation/optim/simplify-zero-sub.c
@@ -0,0 +1,9 @@
+int zero_sub(int x) { return 0 - x; }
+
+/*
+ * check-name: simplify-zero-sub
+ * check-command: test-linearize -Wno-decl $file
+ *
+ * check-output-ignore
+ * check-output-contains: neg\\..* %arg1
+ */