diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2004-12-11 16:22:39 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-07 21:06:01 -0700 |
commit | f95ebd59459788b9a4dee3db440cebfb6ec55222 (patch) | |
tree | 77fbff782f4d2c7fbd3cf2e08845b45450ac1312 /example.c | |
parent | Oops. Didn't remember about BINCMP ops when doing the commutative (diff) | |
download | sparse-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.c | 69 |
1 files changed, 63 insertions, 6 deletions
@@ -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: |