return 1;
}
-#define ACCUMULATE_RELATIVE 1
-
-#if !ACCUMULATE_RELATIVE
-
/*
- * Scan back through the current block, looking for a relative adjustment
- * that involves "value" as its destination. Returns NULL if no such
- * instruction was found, or it is blocked by a later use of "value".
- * "addrof" will be set to a non-NULL value if the instruction just before
- * the relative adjustment took the address of a local frame variable.
- * This instruction is a candidate for being moved down to where the
- * "load_relative" or "store_relative" occurs.
+ * Scan back through the current block, looking for an address instruction that
+ * involves "value" as its destination. Returns NULL if no such instruction was
+ * found, or it is blocked by a later use of "value".
+ *
+ * The instruction found may then be combined into a new single instruction with
+ * the following "load_relative", "store_relative", or another "relative_add".
+ *
+ * For instance, consider the code like this:
+ *
+ * i) y = address_of(x)
+ * ...
+ * j) z = add_relative(y, a)
+ *
+ * Let's suppose that we need to add a "store_realtive(z, b, v)" instruction.
+ * The "find_base_insn()" call will return the instruction "j" and we will be
+ * able to emit the instruction "store_relative(y, a + b, v)" instead. If "z"
+ * is not used elsewhere then "j" will be optimized away by the dead code
+ * elimination pass.
+ *
+ * Repetitive use of this procedure for a chain of "add_relative" instructions
+ * converts it into a series of indpenedent instructions each using the very
+ * first address in the chain as its base. Therefore regardless of the initial
+ * chain length it is always enough to make single "find_base_insn()" call to
+ * get the base address of the entire chain (think induction).
+ *
+ * Note that in this situation the second "find_base_insn()" call will return
+ * the instruction "i" that obtains the base address as the address of a local
+ * frame variable. This instruction is a candidate for being moved down to
+ * where the "load_relative" or "store_relative" occurs. This might make it
+ * easier for the code generator to handle field accesses whitin local
+ * variables.
+ *
+ * The "plast" argument indicates if the found instruction is already the last
+ * one, so there is no need to move it down.
*/
-static jit_insn_t previous_relative(jit_function_t func, jit_value_t value,
- jit_insn_t *addrof)
+static jit_insn_t
+find_base_insn(
+ jit_function_t func,
+ jit_insn_iter_t iter,
+ jit_value_t value,
+ int *plast)
{
- jit_insn_iter_t iter;
+ int last;
jit_insn_t insn;
+ jit_insn_iter_t iter2;
jit_insn_t insn2;
- jit_insn_t insn3;
- /* Clear "addrof" first */
- *addrof = 0;
-
- /* If the value is not temporary, then it isn't a candidate */
- if(!(value->is_temporary))
+ /* The "value" could be vulnerable to aliasing effects so we cannot
+ optimize it */
+ if(value->is_addressable || value->is_volatile)
{
return 0;
}
- /* Iterate back through the block looking for a suitable adjustment */
- jit_insn_iter_init_last(&iter, func->builder->current_block);
+ /* We are about to check the last instruction before the current one */
+ last = 1;
+
+ /* Iterate back through the block looking for a suitable instruction */
while((insn = jit_insn_iter_previous(&iter)) != 0)
{
- if(insn->opcode == JIT_OP_ADD_RELATIVE && insn->dest == value)
+ /* This instruction uses "value" in some way */
+ if(insn->dest == value)
{
- /* See if the instruction just before the "add_relative"
- is an "address_of" that is being used by the add */
- insn3 = jit_insn_iter_previous(&iter);
- if(insn3)
+ /* This is the instruction we were looking for */
+ if(insn->opcode == JIT_OP_ADDRESS_OF)
{
- jit_insn_iter_next(&iter);
- if(insn3->opcode != JIT_OP_ADDRESS_OF ||
- insn3->dest != insn->value1 ||
- !(insn3->dest->is_temporary))
- {
- insn3 = 0;
- }
+ *plast = last;
+ return insn;
}
-
- /* Scan forwards to ensure that "insn->value1" is not
- used anywhere in the instructions that follow */
- jit_insn_iter_next(&iter);
- while((insn2 = jit_insn_iter_next(&iter)) != 0)
+ if(insn->opcode == JIT_OP_ADD_RELATIVE)
{
- if(insn2->dest == insn->value1 ||
- insn2->value1 == insn->value1 ||
- insn2->value2 == insn->value1)
+ value = insn->value1;
+ if(value->is_addressable || value->is_volatile)
{
return 0;
}
- if(insn3)
+
+ /* Scan forwards to ensure that "insn->value1"
+ is not modified anywhere in the instructions
+ that follow */
+ iter2 = iter;
+ jit_insn_iter_next(&iter2);
+ while((insn2 = jit_insn_iter_next(&iter2)) != 0)
{
- /* We may need to disable the "address_of" instruction
- if any of its values are used further on */
- if(insn2->dest == insn3->dest ||
- insn2->value1 == insn3->dest ||
- insn2->value2 == insn3->dest ||
- insn2->dest == insn3->value1 ||
- insn2->value1 == insn3->value1 ||
- insn2->value2 == insn3->value1)
+ if(insn2->dest == value
+ && (insn2->flags & JIT_INSN_DEST_IS_VALUE) == 0)
{
- insn3 = 0;
+ return 0;
}
}
- }
- if(insn3)
- {
- *addrof = insn3;
- }
- return insn;
- }
- if(insn->dest == value || insn->value1 == value ||
- insn->value2 == value)
- {
- /* This instruction uses "value" in some way, so it
- blocks any previous "add_relative" instructions */
- return 0;
- }
- }
- return 0;
-}
-#else
-
-static int
-accumulate_relative_offset(
- jit_function_t func,
- jit_value_t value,
- jit_nint offset,
- jit_value_t *addrof_ptr,
- jit_value_t *value_ptr,
- jit_nint *offset_ptr)
-{
- jit_insn_iter_t iter;
- jit_insn_t insn;
-
- if(addrof_ptr)
- {
- *addrof_ptr = 0;
- }
- if(value_ptr)
- {
- *value_ptr = 0;
- }
- if(offset_ptr)
- {
- *offset_ptr = 0;
- }
-
- jit_insn_iter_init_last(&iter, func->builder->current_block);
- while((insn = jit_insn_iter_previous(&iter)) != 0)
- {
- if(!(value->is_temporary))
- {
- break;
- }
- if(insn->dest != value)
- {
- continue;
- }
- if(insn->opcode == JIT_OP_ADDRESS_OF)
- {
- if(addrof_ptr)
- {
- *addrof_ptr = insn->value1;
+ *plast = last;
+ return insn;
}
- break;
- }
- if(insn->opcode != JIT_OP_ADD_RELATIVE)
- {
+
+ /* Oops. This instruction modifies "value" and blocks
+ any previous address_of or add_relative instructions */
if((insn->flags & JIT_INSN_DEST_IS_VALUE) == 0)
{
- /* This instruction modifies "value" in some way,
- so it blocks any previous "add_relative"
- instructions */
- return 0;
+ break;
}
- continue;
}
- offset += jit_value_get_nint_constant(insn->value2);
- value = insn->value1;
- }
- if(value_ptr)
- {
- *value_ptr = value;
+ /* We are to check instructions that preceed the last one */
+ last = 0;
}
- if(offset_ptr)
- {
- *offset_ptr = offset;
- }
-
- return 1;
+ return 0;
}
-#endif
-
/*@
* @deftypefun jit_value_t jit_insn_load_relative (jit_function_t func, jit_value_t value, jit_nint offset, jit_type_t type)
* Load a value of the specified @code{type} from the effective address
(jit_function_t func, jit_value_t value,
jit_nint offset, jit_type_t type)
{
-#if ACCUMULATE_RELATIVE
- jit_value_t addrof;
- jit_value_t new_value;
- jit_nint new_offset;
-#else
+ jit_insn_iter_t iter;
jit_insn_t insn;
- jit_insn_t addrof;
-#endif
+ int last;
+
if(!value)
{
return 0;
{
return 0;
}
-#if ACCUMULATE_RELATIVE
- if(accumulate_relative_offset(func, value, offset, &addrof, &new_value, &new_offset))
- {
- value = new_value;
- offset = new_offset;
- }
-#else
- insn = previous_relative(func, value, &addrof);
- if(insn)
+
+ jit_insn_iter_init_last(&iter, func->builder->current_block);
+ insn = find_base_insn(func, iter, value, &last);
+ if(insn && insn->opcode == JIT_OP_ADD_RELATIVE)
{
/* We have a previous "add_relative" instruction for this
- pointer. Remove it from the instruction stream and
- adjust the current offset accordingly */
+ pointer. Adjust the current offset accordingly */
offset += jit_value_get_nint_constant(insn->value2);
value = insn->value1;
- insn->opcode = JIT_OP_NOP;
- insn->dest = 0;
- insn->value1 = 0;
- insn->value2 = 0;
- if(addrof)
- {
- /* Shift the "address_of" instruction down too, to make
- it easier for the code generator to handle field
- accesses within local and global variables */
- value = jit_insn_address_of(func, addrof->value1);
- if(!value)
- {
- return 0;
- }
- addrof->opcode = JIT_OP_NOP;
- addrof->dest = 0;
- addrof->value1 = 0;
- addrof->value2 = 0;
+ insn = find_base_insn(func, iter, value, &last);
+ last = 0;
+ }
+ if(insn && insn->opcode == JIT_OP_ADDRESS_OF && !last)
+ {
+ /* Shift the "address_of" instruction down, to make
+ it easier for the code generator to handle field
+ accesses within local and global variables */
+ value = jit_insn_address_of(func, insn->value1);
+ if(!value)
+ {
+ return 0;
}
}
-#endif
return apply_binary
(func, _jit_load_opcode(JIT_OP_LOAD_RELATIVE_SBYTE, type, 0, 0), value,
jit_value_create_nint_constant(func, jit_type_nint, offset), type);
(jit_function_t func, jit_value_t dest,
jit_nint offset, jit_value_t value)
{
+ jit_insn_iter_t iter;
jit_insn_t insn;
+ int last;
jit_value_t offset_value;
-#if ACCUMULATE_RELATIVE
- jit_value_t addrof;
- jit_value_t new_dest;
- jit_nint new_offset;
-#else
- jit_insn_t addrof;
-#endif
+
if(!dest || !value)
{
return 0;
{
return 0;
}
-#if ACCUMULATE_RELATIVE
- if(accumulate_relative_offset(func, dest, offset, &addrof, &new_dest, &new_offset))
- {
- dest = new_dest;
- offset = new_offset;
- }
-#else
- insn = previous_relative(func, dest, &addrof);
- if(insn)
+
+ jit_insn_iter_init_last(&iter, func->builder->current_block);
+ insn = find_base_insn(func, iter, dest, &last);
+ if(insn && insn->opcode == JIT_OP_ADD_RELATIVE)
{
/* We have a previous "add_relative" instruction for this
- pointer. Remove it from the instruction stream and
- adjust the current offset accordingly */
+ pointer. Adjust the current offset accordingly */
offset += jit_value_get_nint_constant(insn->value2);
dest = insn->value1;
- insn->opcode = JIT_OP_NOP;
- insn->dest = 0;
- insn->value1 = 0;
- insn->value2 = 0;
- if(addrof)
- {
- /* Shift the "address_of" instruction down too, to make
- it easier for the code generator to handle field
- accesses within local and global variables */
- dest = jit_insn_address_of(func, addrof->value1);
- if(!dest)
- {
- return 0;
- }
- addrof->opcode = JIT_OP_NOP;
- addrof->dest = 0;
- addrof->value1 = 0;
- addrof->value2 = 0;
+ insn = find_base_insn(func, iter, value, &last);
+ last = 0;
+ }
+ if(insn && insn->opcode == JIT_OP_ADDRESS_OF && !last)
+ {
+ /* Shift the "address_of" instruction down, to make
+ it easier for the code generator to handle field
+ accesses within local and global variables */
+ dest = jit_insn_address_of(func, insn->value1);
+ if(!dest)
+ {
+ return 0;
}
}
-#endif
+
offset_value = jit_value_create_nint_constant(func, jit_type_nint, offset);
if(!offset_value)
{
}
jit_value_ref(func, dest);
jit_value_ref(func, value);
- insn->opcode = (short)_jit_store_opcode
- (JIT_OP_STORE_RELATIVE_BYTE, 0, value->type);
+ insn->opcode = (short)_jit_store_opcode(JIT_OP_STORE_RELATIVE_BYTE, 0, value->type);
insn->flags = JIT_INSN_DEST_IS_VALUE;
insn->dest = dest;
insn->value1 = value;
jit_value_t jit_insn_add_relative
(jit_function_t func, jit_value_t value, jit_nint offset)
{
-#if ACCUMULATE_RELATIVE
- jit_value_t new_value;
- jit_nint new_offset;
+ jit_insn_iter_t iter;
+ jit_insn_t insn;
+ int last;
+
if(!value)
{
return 0;
{
return 0;
}
- if(accumulate_relative_offset(func, value, offset, 0, &new_value, &new_offset))
+
+ jit_insn_iter_init_last(&iter, func->builder->current_block);
+ insn = find_base_insn(func, iter, value, &last);
+ if(insn && insn->opcode == JIT_OP_ADD_RELATIVE)
{
- value = new_value;
- offset = new_offset;
+ /* We have a previous "add_relative" instruction for this
+ pointer. Adjust the current offset accordingly */
+ offset += jit_value_get_nint_constant(insn->value2);
+ value = insn->value1;
}
+
return apply_binary(func, JIT_OP_ADD_RELATIVE, value,
jit_value_create_nint_constant(func, jit_type_nint, offset),
jit_type_void_ptr);
-#else
- jit_insn_t insn;
- jit_insn_t addrof;
- if(!value)
- {
- return 0;
- }
- if(!_jit_function_ensure_builder(func))
- {
- return 0;
- }
- insn = previous_relative(func, value, &addrof);
- if(insn)
- {
- /* Back-patch the "add_relative" instruction to adjust the offset */
- insn->value2 = jit_value_create_nint_constant
- (func, jit_type_nint,
- jit_value_get_nint_constant(insn->value2) + offset);
- return value;
- }
- else
- {
- /* Create a new "add_relative" instruction */
- return apply_binary(func, JIT_OP_ADD_RELATIVE, value,
- jit_value_create_nint_constant
- (func, jit_type_nint, offset),
- jit_type_void_ptr);
- }
-#endif
}
/*@