/* Base (tree) -> Vector (vec<access_p> *) map. */
static hash_map<tree, auto_vec<access_p> > *base_access_vec;
+/* Hash to limit creation of artificial accesses */
+static hash_map<tree, unsigned> *propagation_budget;
+
/* Candidate hash table helpers. */
struct uid_decl_hasher : nofree_ptr_hash <tree_node>
size = max_size;
unscalarizable_region = true;
}
+ if (size == 0)
+ return NULL;
if (size < 0)
{
disqualify_candidate (base, "Encountered an unconstrained access.");
/* Create a variable for the given ACCESS which determines the type, name and a
few other properties. Return the variable declaration and store it also to
ACCESS->replacement. REG_TREE is used when creating a declaration to base a
- default-definition SSA name on on in order to facilitate an uninitialized
+ default-definition SSA name on in order to facilitate an uninitialized
warning. It is used instead of the actual ACCESS type if that is not of a
gimple register type. */
variant. This avoids issues with weirdo ABIs like AAPCS. */
repl = create_tmp_var (build_qualified_type (TYPE_MAIN_VARIANT (type),
TYPE_QUALS (type)), "SR");
- if (TREE_CODE (type) == COMPLEX_TYPE
- || TREE_CODE (type) == VECTOR_TYPE)
- {
- if (!access->grp_partial_lhs)
- DECL_GIMPLE_REG_P (repl) = 1;
- }
- else if (access->grp_partial_lhs
- && is_gimple_reg_type (type))
- TREE_ADDRESSABLE (repl) = 1;
+ if (access->grp_partial_lhs
+ && is_gimple_reg_type (type))
+ DECL_NOT_GIMPLE_REG_P (repl) = 1;
DECL_SOURCE_LOCATION (repl) = DECL_SOURCE_LOCATION (access->base);
DECL_ARTIFICIAL (repl) = 1;
print_generic_expr (dump_file, access->base);
fprintf (dump_file, " offset: %u, size: %u: ",
(unsigned) access->offset, (unsigned) access->size);
- print_generic_expr (dump_file, repl);
+ print_generic_expr (dump_file, repl, TDF_UID);
fprintf (dump_file, "\n");
}
}
gcc_assert (base == first_base);
gcc_assert (offset == access->offset);
gcc_assert (access->grp_unscalarizable_region
+ || access->grp_total_scalarization
|| size == max_size);
- gcc_assert (max_size == access->size);
+ gcc_assert (access->grp_unscalarizable_region
+ || !is_gimple_reg_type (access->type)
+ || size == access->size);
gcc_assert (reverse == access->reverse);
if (access->first_child)
subtree_mark_written_and_rhs_enqueue (child);
}
+/* If there is still budget to create a propagation access for DECL, return
+ true and decrement the budget. Otherwise return false. */
+
+static bool
+budget_for_propagation_access (tree decl)
+{
+ unsigned b, *p = propagation_budget->get (decl);
+ if (p)
+ b = *p;
+ else
+ b = param_sra_max_propagations;
+
+ if (b == 0)
+ return false;
+ b--;
+
+ if (b == 0 && dump_file && (dump_flags & TDF_DETAILS))
+ {
+ fprintf (dump_file, "The propagation budget of ");
+ print_generic_expr (dump_file, decl);
+ fprintf (dump_file, " (UID: %u) has been exhausted.\n", DECL_UID (decl));
+ }
+ propagation_budget->put (decl, b);
+ return true;
+}
+
/* Propagate subaccesses and grp_write flags of RACC across an assignment link
to LACC. Enqueue sub-accesses as necessary so that the write flag is
propagated transitively. Return true if anything changed. Additionally, if
continue;
}
- if (rchild->grp_unscalarizable_region)
+ if (rchild->grp_unscalarizable_region
+ || !budget_for_propagation_access (lacc->base))
{
if (rchild->grp_write && !lacc->grp_write)
{
if (lchild->grp_unscalarizable_region
|| child_would_conflict_in_acc (racc, norm_offset, lchild->size,
- &matching_acc))
+ &matching_acc)
+ || !budget_for_propagation_access (racc->base))
{
if (matching_acc
&& propagate_subaccesses_from_lhs (lchild, matching_acc))
static void
propagate_all_subaccesses (void)
{
+ propagation_budget = new hash_map<tree, unsigned>;
while (rhs_work_queue_head)
{
struct access *racc = pop_access_from_rhs_work_queue ();
add_access_to_lhs_work_queue (racc);
}
}
+ delete propagation_budget;
}
/* Return true if the forest beginning with ROOT does not contain
return NULL;
}
- if (!bitmap_bit_p (candidate_bitmap, DECL_UID (base)))
+ if (max_size == 0
+ || !bitmap_bit_p (candidate_bitmap, DECL_UID (base)))
return NULL;
return get_var_base_offset_size_access (base, offset, max_size);
location_t loc;
struct access *access;
tree type, bfr, orig_expr;
+ bool partial_cplx_access = false;
if (TREE_CODE (*expr) == BIT_FIELD_REF)
{
bfr = NULL_TREE;
if (TREE_CODE (*expr) == REALPART_EXPR || TREE_CODE (*expr) == IMAGPART_EXPR)
- expr = &TREE_OPERAND (*expr, 0);
+ {
+ expr = &TREE_OPERAND (*expr, 0);
+ partial_cplx_access = true;
+ }
access = get_access_for_expr (*expr);
if (!access)
return false;
be accessed as a different type too, potentially creating a need for
type conversion (see PR42196) and when scalarized unions are involved
in assembler statements (see PR42398). */
- if (!useless_type_conversion_p (type, access->type))
+ if (!bfr && !useless_type_conversion_p (type, access->type))
{
tree ref;
ref = build_ref_for_model (loc, orig_expr, 0, access, gsi, false);
- if (write)
+ if (partial_cplx_access)
+ {
+ /* VIEW_CONVERT_EXPRs in partial complex access are always fine in
+ the case of a write because in such case the replacement cannot
+ be a gimple register. In the case of a load, we have to
+ differentiate in between a register an non-register
+ replacement. */
+ tree t = build1 (VIEW_CONVERT_EXPR, type, repl);
+ gcc_checking_assert (!write || access->grp_partial_lhs);
+ if (!access->grp_partial_lhs)
+ {
+ tree tmp = make_ssa_name (type);
+ gassign *stmt = gimple_build_assign (tmp, t);
+ /* This is always a read. */
+ gsi_insert_before (gsi, stmt, GSI_SAME_STMT);
+ t = tmp;
+ }
+ *expr = t;
+ }
+ else if (write)
{
gassign *stmt;