aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2004-12-11 16:22:39 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-07 21:06:01 -0700
commitf95ebd59459788b9a4dee3db440cebfb6ec55222 (patch)
tree77fbff782f4d2c7fbd3cf2e08845b45450ac1312 /example.c
parentOops. Didn't remember about BINCMP ops when doing the commutative (diff)
downloadsparse-f95ebd59459788b9a4dee3db440cebfb6ec55222.tar.gz
sparse-f95ebd59459788b9a4dee3db440cebfb6ec55222.tar.bz2
sparse-f95ebd59459788b9a4dee3db440cebfb6ec55222.zip
Teach code generator about commutative operations.
It can select the order based on whether one of the sources is dead, or on the target register choice.
Diffstat (limited to 'example.c')
-rw-r--r--example.c69
1 files changed, 63 insertions, 6 deletions
diff --git a/example.c b/example.c
index fe8f7a5..fc3467c 100644
--- a/example.c
+++ b/example.c
@@ -590,11 +590,11 @@ static struct hardreg *target_copy_reg(struct bb_state *state, struct hardreg *s
return copy_reg(state, src, target);
}
-static void generate_binop(struct bb_state *state, struct instruction *insn)
+static void do_binop(struct bb_state *state, struct instruction *insn, pseudo_t val1, pseudo_t val2)
{
const char *op = opcodes[insn->opcode];
- struct hardreg *src = getreg(state, insn->src1, insn->target);
- const char *src2 = generic(state, insn->src2);
+ struct hardreg *src = getreg(state, val1, insn->target);
+ const char *src2 = generic(state, val2);
struct hardreg *dst;
dst = target_copy_reg(state, src, insn->target);
@@ -602,6 +602,52 @@ static void generate_binop(struct bb_state *state, struct instruction *insn)
add_pseudo_reg(state, insn->target, dst);
}
+static void generate_binop(struct bb_state *state, struct instruction *insn)
+{
+ do_binop(state, insn, insn->src1, insn->src2);
+}
+
+static int is_dead_reg(struct bb_state *state, pseudo_t pseudo, struct hardreg *reg)
+{
+ pseudo_t p;
+ FOR_EACH_PTR(reg->contains, p) {
+ if (p == pseudo)
+ return CURRENT_TAG(p) & TAG_DEAD;
+ } END_FOR_EACH_PTR(p);
+ return 0;
+}
+
+/*
+ * Commutative binops are much more flexible, since we can switch the
+ * sources around to satisfy the target register, or to avoid having
+ * to load one of them into a register..
+ */
+static void generate_commutative_binop(struct bb_state *state, struct instruction *insn)
+{
+ pseudo_t src1 = insn->src1, src2 = insn->src2;
+ struct hardreg *reg1, *reg2 = find_in_reg(state, src2);
+
+ if (!reg2)
+ goto dont_switch;
+ reg1 = find_in_reg(state, src1);
+ if (!reg1)
+ goto do_switch;
+ if (!is_dead_reg(state, src2, reg2))
+ goto dont_switch;
+ if (!is_dead_reg(state, src1, reg1))
+ goto do_switch;
+
+ /* Both are dead. Is one preferrable? */
+ if (reg2 != preferred_reg(state, insn->target))
+ goto dont_switch;
+
+do_switch:
+ src1 = src2;
+ src2 = insn->src1;
+dont_switch:
+ do_binop(state, insn, src1, src2);
+}
+
/*
* This marks a pseudo dead. It still stays on the hardreg list (the hardreg
* still has its value), but it's scheduled to be killed after the next
@@ -825,9 +871,20 @@ static void generate_one_insn(struct instruction *insn, struct bb_state *state)
mark_pseudo_dead(state, insn->target);
return;
- case OP_BINARY ... OP_BINARY_END:
- case OP_BINCMP ... OP_BINCMP_END:
- generate_binop(state, insn);
+ case OP_ADD: case OP_MUL:
+ case OP_AND: case OP_OR: case OP_XOR:
+ case OP_AND_BOOL: case OP_OR_BOOL:
+ case OP_SET_EQ: case OP_SET_NE:
+ generate_commutative_binop(state, insn);
+ break;
+
+ case OP_SUB: case OP_DIV: case OP_MOD:
+ case OP_SHL: case OP_SHR:
+ 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:
+ generate_binop(state, insn);
break;
case OP_CAST: case OP_PTRCAST: