#include "tree-dfa.h"
#include "dumpfile.h"
#include "tree-affine.h"
+#include "gimplify.h"
/* FIXME: We compute address costs using RTL. */
#include "tree-ssa-address.h"
aff_combination_remove_elt (addr, i);
}
-/* If ADDR contains an instance of BASE_HINT, move it to PARTS->base. */
+/* Return true if ADDR contains an instance of BASE_HINT and it's moved to
+ PARTS->base. */
-static void
+static bool
move_hint_to_base (tree type, struct mem_address *parts, tree base_hint,
aff_tree *addr)
{
}
if (i == addr->n)
- return;
+ return false;
/* Cast value to appropriate pointer type. We cannot use a pointer
to TYPE directly, as the back-end will assume registers of pointer
type = build_qualified_type (void_type_node, qual);
parts->base = fold_convert (build_pointer_type (type), val);
aff_combination_remove_elt (addr, i);
+ return true;
}
/* If ADDR contains an address of a dereferenced pointer, move it to
if (POINTER_TYPE_P (type))
parts->base = fold_build_pointer_plus (parts->base, elt);
else
- parts->base = fold_build2 (PLUS_EXPR, type,
- parts->base, elt);
+ parts->base = fold_build2 (PLUS_EXPR, type, parts->base, elt);
}
/* Returns true if multiplying by RATIO is allowed in an address. Test the
/* Splits address ADDR for a memory access of type TYPE into PARTS.
If BASE_HINT is non-NULL, it specifies an SSA name to be used
preferentially as base of the reference, and IV_CAND is the selected
- iv candidate used in ADDR.
+ iv candidate used in ADDR. Store true to VAR_IN_BASE if variant
+ part of address is split to PARTS.base.
TODO -- be more clever about the distribution of the elements of ADDR
to PARTS. Some architectures do not support anything but single
addressing modes is useless. */
static void
-addr_to_parts (tree type, aff_tree *addr, tree iv_cand,
- tree base_hint, struct mem_address *parts,
- bool speed)
+addr_to_parts (tree type, aff_tree *addr, tree iv_cand, tree base_hint,
+ struct mem_address *parts, bool *var_in_base, bool speed)
{
tree part;
unsigned i;
/* Try to find a symbol. */
move_fixed_address_to_symbol (parts, addr);
- /* No need to do address parts reassociation if the number of parts
- is <= 2 -- in that case, no loop invariant code motion can be
- exposed. */
-
- if (!base_hint && (addr->n > 2))
+ /* Since at the moment there is no reliable way to know how to
+ distinguish between pointer and its offset, we decide if var
+ part is the pointer based on guess. */
+ *var_in_base = (base_hint != NULL && parts->symbol == NULL);
+ if (*var_in_base)
+ *var_in_base = move_hint_to_base (type, parts, base_hint, addr);
+ else
move_variant_to_index (parts, addr, iv_cand);
- /* First move the most expensive feasible multiplication
- to index. */
+ /* First move the most expensive feasible multiplication to index. */
if (!parts->index)
most_expensive_mult_to_index (type, parts, addr, speed);
- /* Try to find a base of the reference. Since at the moment
- there is no reliable way how to distinguish between pointer and its
- offset, this is just a guess. */
- if (!parts->symbol && base_hint)
- move_hint_to_base (type, parts, base_hint, addr);
+ /* Move pointer into base. */
if (!parts->symbol && !parts->base)
move_pointer_to_base (parts, addr);
create_mem_ref (gimple_stmt_iterator *gsi, tree type, aff_tree *addr,
tree alias_ptr_type, tree iv_cand, tree base_hint, bool speed)
{
+ bool var_in_base;
tree mem_ref, tmp;
struct mem_address parts;
- addr_to_parts (type, addr, iv_cand, base_hint, &parts, speed);
+ addr_to_parts (type, addr, iv_cand, base_hint, &parts, &var_in_base, speed);
gimplify_mem_ref_parts (gsi, &parts);
mem_ref = create_mem_ref_raw (type, alias_ptr_type, &parts, true);
if (mem_ref)
/* The expression is too complicated. Try making it simpler. */
+ /* Merge symbol into other parts. */
+ if (parts.symbol)
+ {
+ tmp = parts.symbol;
+ parts.symbol = NULL_TREE;
+ gcc_assert (is_gimple_val (tmp));
+
+ if (parts.base)
+ {
+ gcc_assert (useless_type_conversion_p (sizetype,
+ TREE_TYPE (parts.base)));
+
+ if (parts.index)
+ {
+ /* Add the symbol to base, eventually forcing it to register. */
+ tmp = fold_build_pointer_plus (tmp, parts.base);
+ tmp = force_gimple_operand_gsi_1 (gsi, tmp,
+ is_gimple_mem_ref_addr,
+ NULL_TREE, true,
+ GSI_SAME_STMT);
+ }
+ else
+ {
+ /* Move base to index, then move the symbol to base. */
+ parts.index = parts.base;
+ }
+ parts.base = tmp;
+ }
+ else
+ parts.base = tmp;
+
+ mem_ref = create_mem_ref_raw (type, alias_ptr_type, &parts, true);
+ if (mem_ref)
+ return mem_ref;
+ }
+
+ /* Move multiplication to index by transforming address expression:
+ [... + index << step + ...]
+ into:
+ index' = index << step;
+ [... + index' + ,,,]. */
if (parts.step && !integer_onep (parts.step))
{
- /* Move the multiplication to index. */
gcc_assert (parts.index);
parts.index = force_gimple_operand_gsi (gsi,
fold_build2 (MULT_EXPR, sizetype,
return mem_ref;
}
- if (parts.symbol)
+ /* Add offset to invariant part by transforming address expression:
+ [base + index + offset]
+ into:
+ base' = base + offset;
+ [base' + index]
+ or:
+ index' = index + offset;
+ [base + index']
+ depending on which one is invariant. */
+ if (parts.offset && !integer_zerop (parts.offset))
{
- tmp = parts.symbol;
- gcc_assert (is_gimple_val (tmp));
+ tree old_base = unshare_expr (parts.base);
+ tree old_index = unshare_expr (parts.index);
+ tree old_offset = unshare_expr (parts.offset);
- /* Add the symbol to base, eventually forcing it to register. */
- if (parts.base)
+ tmp = parts.offset;
+ parts.offset = NULL_TREE;
+ /* Add offset to invariant part. */
+ if (!var_in_base)
{
- gcc_assert (useless_type_conversion_p
- (sizetype, TREE_TYPE (parts.base)));
-
- if (parts.index)
+ if (parts.base)
{
- parts.base = force_gimple_operand_gsi_1 (gsi,
- fold_build_pointer_plus (tmp, parts.base),
- is_gimple_mem_ref_addr, NULL_TREE, true, GSI_SAME_STMT);
+ tmp = fold_build_pointer_plus (parts.base, tmp);
+ tmp = force_gimple_operand_gsi_1 (gsi, tmp,
+ is_gimple_mem_ref_addr,
+ NULL_TREE, true,
+ GSI_SAME_STMT);
}
- else
+ parts.base = tmp;
+ }
+ else
+ {
+ if (parts.index)
{
- parts.index = parts.base;
- parts.base = tmp;
+ tmp = fold_build_pointer_plus (parts.index, tmp);
+ tmp = force_gimple_operand_gsi_1 (gsi, tmp,
+ is_gimple_mem_ref_addr,
+ NULL_TREE, true,
+ GSI_SAME_STMT);
}
+ parts.index = tmp;
}
- else
- parts.base = tmp;
- parts.symbol = NULL_TREE;
mem_ref = create_mem_ref_raw (type, alias_ptr_type, &parts, true);
if (mem_ref)
return mem_ref;
+
+ /* Restore parts.base, index and offset so that we can check if
+ [base + offset] addressing mode is supported in next step.
+ This is necessary for targets only support [base + offset],
+ but not [base + index] addressing mode. */
+ parts.base = old_base;
+ parts.index = old_index;
+ parts.offset = old_offset;
}
+ /* Transform [base + index + ...] into:
+ base' = base + index;
+ [base' + ...]. */
if (parts.index)
{
+ tmp = parts.index;
+ parts.index = NULL_TREE;
/* Add index to base. */
if (parts.base)
{
- parts.base = force_gimple_operand_gsi_1 (gsi,
- fold_build_pointer_plus (parts.base, parts.index),
- is_gimple_mem_ref_addr, NULL_TREE, true, GSI_SAME_STMT);
+ tmp = fold_build_pointer_plus (parts.base, tmp);
+ tmp = force_gimple_operand_gsi_1 (gsi, tmp,
+ is_gimple_mem_ref_addr,
+ NULL_TREE, true, GSI_SAME_STMT);
}
- else
- parts.base = parts.index;
- parts.index = NULL_TREE;
+ parts.base = tmp;
mem_ref = create_mem_ref_raw (type, alias_ptr_type, &parts, true);
if (mem_ref)
return mem_ref;
}
+ /* Transform [base + offset] into:
+ base' = base + offset;
+ [base']. */
if (parts.offset && !integer_zerop (parts.offset))
{
- /* Try adding offset to base. */
+ tmp = parts.offset;
+ parts.offset = NULL_TREE;
+ /* Add offset to base. */
if (parts.base)
{
- parts.base = force_gimple_operand_gsi_1 (gsi,
- fold_build_pointer_plus (parts.base, parts.offset),
- is_gimple_mem_ref_addr, NULL_TREE, true, GSI_SAME_STMT);
+ tmp = fold_build_pointer_plus (parts.base, tmp);
+ tmp = force_gimple_operand_gsi_1 (gsi, tmp,
+ is_gimple_mem_ref_addr,
+ NULL_TREE, true, GSI_SAME_STMT);
}
- else
- parts.base = parts.offset;
-
- parts.offset = NULL_TREE;
+ parts.base = tmp;
mem_ref = create_mem_ref_raw (type, alias_ptr_type, &parts, true);
if (mem_ref)