#include "jit-internal.h"
+#define USE_FORWARD_PROPAGATION 1
+#define USE_BACKWARD_PROPAGATION 1
+
/*
* Compute liveness information for a basic block.
*/
-static void compute_liveness_for_block(jit_block_t block)
+static void
+compute_liveness_for_block(jit_block_t block)
{
jit_insn_iter_t iter;
jit_insn_t insn;
value2->next_use = 1;
}
}
+}
+
+#if USE_FORWARD_PROPAGATION || USE_BACKWARD_PROPAGATION
+static int
+is_copy_opcode(int opcode)
+{
+ switch(opcode)
+ {
+ case JIT_OP_COPY_INT:
+ case JIT_OP_COPY_LONG:
+ case JIT_OP_COPY_FLOAT32:
+ case JIT_OP_COPY_FLOAT64:
+ case JIT_OP_COPY_NFLOAT:
+ /*case JIT_OP_STRUCT:*/
+ return 1;
+ }
+ return 0;
+}
+#endif
+
+#if USE_FORWARD_PROPAGATION
+/*
+ * Perform simple copy propagation within basic block. Replaces instructions
+ * that look like this:
+ *
+ * i) t = x
+ * ...
+ * j) y = op(t)
+ *
+ * with the folowing:
+ *
+ * i) t = x
+ * ...
+ * j) y = op(x)
+ *
+ * If "t" is not used after the instruction "j" then further liveness analysis
+ * may replace the instruction "i" with a noop:
+ *
+ * i) noop
+ * ...
+ * j) y = op(x)
+ *
+ * The propagation stops as soon as either "t" or "x" are changed (used as a
+ * dest in a different instruction).
+ */
+static int
+forward_propagation(jit_block_t block)
+{
+ int optimized;
+ jit_insn_iter_t iter, iter2;
+ jit_insn_t insn, insn2;
+ jit_value_t dest, value;
+ int flags2;
+
+ optimized = 0;
+
+ jit_insn_iter_init(&iter, block);
+ while((insn = jit_insn_iter_next(&iter)) != 0)
+ {
+ if(!is_copy_opcode(insn->opcode))
+ {
+ continue;
+ }
+
+ dest = insn->dest;
+ if(dest == 0 || dest->is_constant)
+ {
+ continue;
+ }
+
+ value = insn->value1;
+ if(value == 0)
+ {
+ continue;
+ }
+
+ /* Copy to itself could be safely discarded */
+ if(dest == value)
+ {
+ insn->opcode = (short)JIT_OP_NOP;
+ optimized = 1;
+ continue;
+ }
+
+ /* Not smart enough to tell when it is safe to optimize copying
+ to a value used in other basic block. So just give up. */
+ if(!dest->is_temporary)
+ {
+ continue;
+ }
+
+ iter2 = iter;
+ while((insn2 = jit_insn_iter_next(&iter2)) != 0)
+ {
+ flags2 = insn2->flags;
+
+ if((flags2 & JIT_INSN_DEST_OTHER_FLAGS) == 0)
+ {
+ if((flags2 & JIT_INSN_DEST_IS_VALUE) == 0)
+ {
+ if(insn2->dest == dest || insn2->dest == value)
+ {
+ break;
+ }
+ }
+ else if(insn2->dest == dest)
+ {
+ insn2->dest = value;
+ optimized = 1;
+ }
+ }
+ if((flags2 & JIT_INSN_VALUE1_OTHER_FLAGS) == 0)
+ {
+ if(insn2->value1 == dest)
+ {
+ insn2->value1 = value;
+ optimized = 1;
+ }
+ }
+ if((flags2 & JIT_INSN_VALUE2_OTHER_FLAGS) == 0)
+ {
+ if(insn2->value2 == dest)
+ {
+ insn2->value2 = value;
+ optimized = 1;
+ }
+ }
+ }
+ }
+
+ return optimized;
+}
+#endif
+
+#if USE_BACKWARD_PROPAGATION
+/*
+ * Perform simple copy propagation within basic block for the case when a
+ * temporary value is stored to another value. This replaces instructions
+ * that look like this:
+ *
+ * i) t = op(x)
+ * ...
+ * j) y = t
+ *
+ * with the following
+ *
+ * i) y = op(x)
+ * ...
+ * j) noop
+ *
+ * This is only allowed if "t" is used only in the instructions "i" and "j"
+ * and "y" is not used between "i" and "j" (but can be used after "j").
+ */
+static int
+backward_propagation(jit_block_t block)
+{
+ int optimized;
+ jit_insn_iter_t iter, iter2;
+ jit_insn_t insn, insn2;
+ jit_value_t dest, value;
+ int flags2;
+
+ optimized = 0;
- /* Re-scan the block to reset the liveness flags on all non-temporaries
- because we need them in the original state for the next block */
jit_insn_iter_init_last(&iter, block);
while((insn = jit_insn_iter_previous(&iter)) != 0)
{
- flags = insn->flags;
- if((flags & JIT_INSN_DEST_OTHER_FLAGS) == 0)
+ if(!is_copy_opcode(insn->opcode))
{
- dest = insn->dest;
- if(dest && !(dest->is_constant) && !(dest->is_temporary))
+ continue;
+ }
+
+ dest = insn->dest;
+ if(dest == 0 || dest->is_constant)
+ {
+ continue;
+ }
+
+ value = insn->value1;
+ if(value == 0)
+ {
+ continue;
+ }
+
+ /* Copy to itself could be safely discarded */
+ if(dest == value)
+ {
+ insn->opcode = (short)JIT_OP_NOP;
+ optimized = 1;
+ continue;
+ }
+
+ /* "value" is used afterwards so we cannot eliminate it here */
+ if((insn->flags & (JIT_INSN_VALUE1_LIVE | JIT_INSN_VALUE1_NEXT_USE)) != 0)
+ {
+ continue;
+ }
+
+ iter2 = iter;
+ while((insn2 = jit_insn_iter_previous(&iter2)) != 0)
+ {
+ flags2 = insn2->flags;
+
+ if((flags2 & JIT_INSN_DEST_OTHER_FLAGS) == 0)
{
- dest->live = 1;
- dest->next_use = 0;
+ if(insn2->dest == dest)
+ {
+ break;
+ }
+ if(insn2->dest == value)
+ {
+ if((flags2 & JIT_INSN_DEST_IS_VALUE) == 0)
+ {
+ insn->opcode = (short)JIT_OP_NOP;
+ insn2->dest = dest;
+ optimized = 1;
+ }
+ break;
+ }
+ }
+ if((flags2 & JIT_INSN_VALUE1_OTHER_FLAGS) == 0)
+ {
+ if(insn2->value1 == dest || insn2->value1 == value)
+ {
+ break;
+ }
+ }
+ if((flags2 & JIT_INSN_VALUE2_OTHER_FLAGS) == 0)
+ {
+ if(insn2->value2 == dest || insn2->value1 == value)
+ {
+ break;
+ }
}
}
+ }
+
+ return optimized;
+}
+#endif
+
+/* Reset value liveness flags. */
+static void
+reset_value_liveness(jit_value_t value)
+{
+ if(value)
+ {
+ if (!value->is_constant && !value->is_temporary)
+ {
+ value->live = 1;
+ }
+ else
+ {
+ value->live = 0;
+ }
+ value->next_use = 0;
+ }
+}
+
+/*
+ * Re-scan the block to reset the liveness flags on all non-temporaries
+ * because we need them in the original state for the next block.
+ */
+static void
+reset_liveness_flags(jit_block_t block, int reset_all)
+{
+ jit_insn_iter_t iter;
+ jit_insn_t insn;
+ int flags;
+
+ jit_insn_iter_init(&iter, block);
+ while((insn = jit_insn_iter_next(&iter)) != 0)
+ {
+ flags = insn->flags;
+ if((flags & JIT_INSN_DEST_OTHER_FLAGS) == 0)
+ {
+ reset_value_liveness(insn->dest);
+ }
if((flags & JIT_INSN_VALUE1_OTHER_FLAGS) == 0)
{
- value1 = insn->value1;
- if(value1 && !(value1->is_constant) && !(value1->is_temporary))
- {
- value1->live = 1;
- value1->next_use = 0;
- }
+ reset_value_liveness(insn->value1);
}
if((flags & JIT_INSN_VALUE2_OTHER_FLAGS) == 0)
{
- value2 = insn->value2;
- if(value2 && !(value2->is_constant) && !(value2->is_temporary))
- {
- value2->live = 1;
- value2->next_use = 0;
- }
+ reset_value_liveness(insn->value2);
+ }
+ if(reset_all)
+ {
+ flags &= ~(JIT_INSN_DEST_LIVE | JIT_INSN_DEST_NEXT_USE
+ |JIT_INSN_VALUE1_LIVE | JIT_INSN_VALUE1_NEXT_USE
+ |JIT_INSN_VALUE2_LIVE | JIT_INSN_VALUE2_NEXT_USE);
}
}
}
/* Perform peephole optimization on branches to branches */
_jit_block_peephole_branch(block);
+#if USE_FORWARD_PROPAGATION
+ /* Perform forward copy propagation for the block */
+ forward_propagation(block);
+#endif
+
+ /* Reset the liveness flags for the next block */
+ reset_liveness_flags(block, 0);
+
/* Compute the liveness flags for the block */
compute_liveness_for_block(block);
+#if USE_BACKWARD_PROPAGATION
+ /* Perform backward copy propagation for the block */
+ if(backward_propagation(block))
+ {
+ /* Reset the liveness flags and compute them again */
+ reset_liveness_flags(block, 1);
+ compute_liveness_for_block(block);
+ }
+#endif
+
/* Move on to the next block in the function */
block = block->next;
}