#include "coretypes.h"
#include "alloc-pool.h"
#include "tm.h"
+#include "toplev.h"
#include "tree.h"
#include "gimple.h"
#include "cgraph.h"
return XOBFINISH (&name_obstack, char *);
}
-/* Helper function for build_ref_for_offset.
+/* Construct a MEM_REF that would reference a part of aggregate BASE of type
+ EXP_TYPE at the given OFFSET. If BASE is something for which
+ get_addr_base_and_unit_offset returns NULL, gsi must be non-NULL and is used
+ to insert new statements either before or below the current one as specified
+ by INSERT_AFTER. This function is not capable of handling bitfields. */
- FIXME: Eventually this should be rewritten to either re-use the
- original access expression unshared (which is good for alias
- analysis) or to build a MEM_REF expression. */
+tree
+build_ref_for_offset (tree base, HOST_WIDE_INT offset,
+ tree exp_type, gimple_stmt_iterator *gsi,
+ bool insert_after)
+{
+ tree prev_base = base;
+ tree off;
+ location_t loc = EXPR_LOCATION (base);
+ HOST_WIDE_INT base_offset;
+
+ gcc_checking_assert (offset % BITS_PER_UNIT == 0);
+
+ base = get_addr_base_and_unit_offset (base, &base_offset);
+
+ /* get_addr_base_and_unit_offset returns NULL for references with a variable
+ offset such as array[var_index]. */
+ if (!base)
+ {
+ gimple stmt;
+ tree tmp, addr;
+
+ gcc_checking_assert (gsi);
+ tmp = create_tmp_reg (build_pointer_type (TREE_TYPE (prev_base)), NULL);
+ add_referenced_var (tmp);
+ tmp = make_ssa_name (tmp, NULL);
+ addr = build_fold_addr_expr (unshare_expr (prev_base));
+ stmt = gimple_build_assign (tmp, addr);
+ SSA_NAME_DEF_STMT (tmp) = stmt;
+ if (insert_after)
+ gsi_insert_after (gsi, stmt, GSI_NEW_STMT);
+ else
+ gsi_insert_before (gsi, stmt, GSI_SAME_STMT);
+ update_stmt (stmt);
+
+ off = build_int_cst (reference_alias_ptr_type (prev_base),
+ offset / BITS_PER_UNIT);
+ base = tmp;
+ }
+ else if (TREE_CODE (base) == MEM_REF)
+ {
+ off = build_int_cst (TREE_TYPE (TREE_OPERAND (base, 1)),
+ base_offset + offset / BITS_PER_UNIT);
+ off = int_const_binop (PLUS_EXPR, TREE_OPERAND (base, 1), off, 0);
+ base = unshare_expr (TREE_OPERAND (base, 0));
+ }
+ else
+ {
+ off = build_int_cst (reference_alias_ptr_type (base),
+ base_offset + offset / BITS_PER_UNIT);
+ base = build_fold_addr_expr (unshare_expr (base));
+ }
+
+ return fold_build2_loc (loc, MEM_REF, exp_type, base, off);
+}
+
+/* Construct a memory reference to a part of an aggregate BASE at the given
+ OFFSET and of the same type as MODEL. In case this is a reference to a
+ bit-field, the function will replicate the last component_ref of model's
+ expr to access it. GSI and INSERT_AFTER have the same meaning as in
+ build_ref_for_offset. */
+
+static tree
+build_ref_for_model (tree base, HOST_WIDE_INT offset,
+ struct access *model, gimple_stmt_iterator *gsi,
+ bool insert_after)
+{
+ if (TREE_CODE (model->expr) == COMPONENT_REF
+ && DECL_BIT_FIELD (TREE_OPERAND (model->expr, 1)))
+ {
+ /* This access represents a bit-field. */
+ tree t, exp_type;
+
+ offset -= int_bit_position (TREE_OPERAND (model->expr, 1));
+ exp_type = TREE_TYPE (TREE_OPERAND (model->expr, 0));
+ t = build_ref_for_offset (base, offset, exp_type, gsi, insert_after);
+ return fold_build3_loc (EXPR_LOCATION (base), COMPONENT_REF,
+ model->type, t, TREE_OPERAND (model->expr, 1),
+ NULL_TREE);
+ }
+ else
+ return build_ref_for_offset (base, offset, model->type, gsi, insert_after);
+}
+
+/* Construct a memory reference consisting of component_refs and array_refs to
+ a part of an aggregate *RES (which is of type TYPE). The requested part
+ should have type EXP_TYPE at be the given OFFSET. This function might not
+ succeed, it returns true when it does and only then *RES points to something
+ meaningful. This function should be used only to build expressions that we
+ might need to present to user (e.g. in warnings). In all other situations,
+ build_ref_for_model or build_ref_for_offset should be used instead. */
static bool
-build_ref_for_offset_1 (tree *res, tree type, HOST_WIDE_INT offset,
- tree exp_type)
+build_user_friendly_ref_for_offset (tree *res, tree type, HOST_WIDE_INT offset,
+ tree exp_type)
{
while (1)
{
else if (pos > offset || (pos + size) <= offset)
continue;
- if (res)
+ expr = build3 (COMPONENT_REF, TREE_TYPE (fld), *res, fld,
+ NULL_TREE);
+ expr_ptr = &expr;
+ if (build_user_friendly_ref_for_offset (expr_ptr, TREE_TYPE (fld),
+ offset - pos, exp_type))
{
- expr = build3 (COMPONENT_REF, TREE_TYPE (fld), *res, fld,
- NULL_TREE);
- expr_ptr = &expr;
- }
- else
- expr_ptr = NULL;
- if (build_ref_for_offset_1 (expr_ptr, TREE_TYPE (fld),
- offset - pos, exp_type))
- {
- if (res)
- *res = expr;
+ *res = expr;
return true;
}
}
minidx = TYPE_MIN_VALUE (TYPE_DOMAIN (type));
if (TREE_CODE (minidx) != INTEGER_CST || el_size == 0)
return false;
- if (res)
- {
- index = build_int_cst (TYPE_DOMAIN (type), offset / el_size);
- if (!integer_zerop (minidx))
- index = int_const_binop (PLUS_EXPR, index, minidx, 0);
- *res = build4 (ARRAY_REF, TREE_TYPE (type), *res, index,
- NULL_TREE, NULL_TREE);
- }
+ index = build_int_cst (TYPE_DOMAIN (type), offset / el_size);
+ if (!integer_zerop (minidx))
+ index = int_const_binop (PLUS_EXPR, index, minidx, 0);
+ *res = build4 (ARRAY_REF, TREE_TYPE (type), *res, index,
+ NULL_TREE, NULL_TREE);
offset = offset % el_size;
type = TREE_TYPE (type);
break;
}
}
-/* Construct an expression that would reference a part of aggregate *EXPR of
- type TYPE at the given OFFSET of the type EXP_TYPE. If EXPR is NULL, the
- function only determines whether it can build such a reference without
- actually doing it, otherwise, the tree it points to is unshared first and
- then used as a base for furhter sub-references. */
-
-bool
-build_ref_for_offset (tree *expr, tree type, HOST_WIDE_INT offset,
- tree exp_type, bool allow_ptr)
-{
- location_t loc = expr ? EXPR_LOCATION (*expr) : UNKNOWN_LOCATION;
-
- if (expr)
- *expr = unshare_expr (*expr);
-
- if (allow_ptr && POINTER_TYPE_P (type))
- {
- type = TREE_TYPE (type);
- if (expr)
- *expr = build_simple_mem_ref_loc (loc, *expr);
- }
-
- return build_ref_for_offset_1 (expr, type, offset, exp_type);
-}
-
/* Return true iff TYPE is stdarg va_list type. */
static inline bool
if (allow_replacements && scalar && !root->first_child
&& (root->grp_hint
- || (root->grp_write && (direct_read || root->grp_assignment_read)))
- /* We must not ICE later on when trying to build an access to the
- original data within the aggregate even when it is impossible to do in
- a defined way like in the PR 42703 testcase. Therefore we check
- pre-emptively here that we will be able to do that. */
- && build_ref_for_offset (NULL, TREE_TYPE (root->base), root->offset,
- root->type, false))
+ || (root->grp_write && (direct_read || root->grp_assignment_read))))
{
if (dump_file && (dump_flags & TDF_DETAILS))
{
{
struct access *access;
struct access **child;
- tree expr = parent->base;;
+ tree expr = parent->base;
gcc_assert (!model->grp_unscalarizable_region);
-
- if (!build_ref_for_offset (&expr, TREE_TYPE (expr), new_offset,
- model->type, false))
+ if (!build_user_friendly_ref_for_offset (&expr, TREE_TYPE (expr), new_offset,
+ model->type))
return NULL;
access = (struct access *) pool_alloc (access_pool);
{
tree t = lacc->base;
- if (build_ref_for_offset (&t, TREE_TYPE (t), lacc->offset, racc->type,
- false))
+ if (build_user_friendly_ref_for_offset (&t, TREE_TYPE (t), lacc->offset,
+ racc->type))
{
lacc->expr = t;
lacc->type = racc->type;
continue;
}
- /* If a (part of) a union field is on the RHS of an assignment, it can
- have sub-accesses which do not make sense on the LHS (PR 40351).
- Check that this is not the case. */
- if (!build_ref_for_offset (NULL, TREE_TYPE (lacc->base), norm_offset,
- rchild->type, false))
- continue;
-
rchild->grp_hint = 1;
new_acc = create_artificial_child_access (lacc, rchild, norm_offset);
if (new_acc)
return false;
}
-/* Return true iff a reference statement into aggregate AGG can be built for
- every single to-be-replaced accesses that is a child of ACCESS, its sibling
- or a child of its sibling. TOP_OFFSET is the offset from the processed
- access subtree that has to be subtracted from offset of each access. */
-
-static bool
-ref_expr_for_all_replacements_p (struct access *access, tree agg,
- HOST_WIDE_INT top_offset)
-{
- do
- {
- if (access->grp_to_be_replaced
- && !build_ref_for_offset (NULL, TREE_TYPE (agg),
- access->offset - top_offset,
- access->type, false))
- return false;
-
- if (access->first_child
- && !ref_expr_for_all_replacements_p (access->first_child, agg,
- top_offset))
- return false;
-
- access = access->next_sibling;
- }
- while (access);
-
- return true;
-}
-
/* Generate statements copying scalar replacements of accesses within a subtree
into or out of AGG. ACCESS is the first child of the root of the subtree to
be processed. AGG is an aggregate type expression (can be a declaration but
- does not have to be, it can for example also be an indirect_ref).
- TOP_OFFSET is the offset of the processed subtree which has to be subtracted
- from offsets of individual accesses to get corresponding offsets for AGG.
- If CHUNK_SIZE is non-null, copy only replacements in the interval
- <start_offset, start_offset + chunk_size>, otherwise copy all. GSI is a
- statement iterator used to place the new statements. WRITE should be true
- when the statements should write from AGG to the replacement and false if
- vice versa. if INSERT_AFTER is true, new statements will be added after the
- current statement in GSI, they will be added before the statement
- otherwise. */
+ does not have to be, it can for example also be a mem_ref or a series of
+ handled components). TOP_OFFSET is the offset of the processed subtree
+ which has to be subtracted from offsets of individual accesses to get
+ corresponding offsets for AGG. If CHUNK_SIZE is non-null, copy only
+ replacements in the interval <start_offset, start_offset + chunk_size>,
+ otherwise copy all. GSI is a statement iterator used to place the new
+ statements. WRITE should be true when the statements should write from AGG
+ to the replacement and false if vice versa. if INSERT_AFTER is true, new
+ statements will be added after the current statement in GSI, they will be
+ added before the statement otherwise. */
static void
generate_subtree_copies (struct access *access, tree agg,
{
do
{
- tree expr = agg;
-
if (chunk_size && access->offset >= start_offset + chunk_size)
return;
&& (chunk_size == 0
|| access->offset + access->size > start_offset))
{
- tree repl = get_access_replacement (access);
- bool ref_found;
+ tree expr, repl = get_access_replacement (access);
gimple stmt;
- ref_found = build_ref_for_offset (&expr, TREE_TYPE (agg),
- access->offset - top_offset,
- access->type, false);
- gcc_assert (ref_found);
+ expr = build_ref_for_model (agg, access->offset - top_offset,
+ access, gsi, insert_after);
if (write)
{
in assembler statements (see PR42398). */
if (!useless_type_conversion_p (type, access->type))
{
- tree ref = access->base;
- bool ok;
+ tree ref;
- ok = build_ref_for_offset (&ref, TREE_TYPE (ref),
- access->offset, access->type, false);
- gcc_assert (ok);
+ ref = build_ref_for_model (access->base, access->offset, access,
+ NULL, false);
if (write)
{
lhs, old_gsi);
if (*refreshed == SRA_UDH_LEFT)
- {
- bool repl_found;
-
- rhs = lacc->base;
- repl_found = build_ref_for_offset (&rhs, TREE_TYPE (rhs),
- lacc->offset, lacc->type,
- false);
- gcc_assert (repl_found);
- }
+ rhs = build_ref_for_model (lacc->base, lacc->offset, lacc,
+ new_gsi, true);
else
- {
- bool repl_found;
-
- rhs = top_racc->base;
- repl_found = build_ref_for_offset (&rhs,
- TREE_TYPE (top_racc->base),
- offset, lacc->type, false);
- gcc_assert (repl_found);
- }
+ rhs = build_ref_for_model (top_racc->base, offset, lacc,
+ new_gsi, true);
}
stmt = gimple_build_assign (get_access_replacement (lacc), rhs);
if (AGGREGATE_TYPE_P (TREE_TYPE (lhs))
&& !access_has_children_p (lacc))
{
- tree expr = lhs;
- if (build_ref_for_offset (&expr, TREE_TYPE (lhs), 0,
- TREE_TYPE (rhs), false))
- {
- lhs = expr;
- gimple_assign_set_lhs (*stmt, expr);
- }
+ lhs = build_ref_for_offset (lhs, 0, TREE_TYPE (rhs), gsi, false);
+ gimple_assign_set_lhs (*stmt, lhs);
}
else if (AGGREGATE_TYPE_P (TREE_TYPE (rhs))
+ && !contains_view_convert_expr_p (rhs)
&& !access_has_children_p (racc))
- {
- tree expr = rhs;
- if (build_ref_for_offset (&expr, TREE_TYPE (rhs), 0,
- TREE_TYPE (lhs), false))
- rhs = expr;
- }
+ rhs = build_ref_for_offset (rhs, 0, TREE_TYPE (lhs), gsi, false);
+
if (!useless_type_conversion_p (TREE_TYPE (lhs), TREE_TYPE (rhs)))
{
- rhs = fold_build1_loc (loc, VIEW_CONVERT_EXPR, TREE_TYPE (lhs), rhs);
+ rhs = fold_build1_loc (loc, VIEW_CONVERT_EXPR, TREE_TYPE (lhs),
+ rhs);
if (is_gimple_reg_type (TREE_TYPE (lhs))
&& TREE_CODE (lhs) != SSA_NAME)
force_gimple_rhs = true;
if (gimple_has_volatile_ops (*stmt)
|| contains_view_convert_expr_p (rhs)
- || contains_view_convert_expr_p (lhs)
- || (access_has_children_p (racc)
- && !ref_expr_for_all_replacements_p (racc, lhs, racc->offset))
- || (access_has_children_p (lacc)
- && !ref_expr_for_all_replacements_p (lacc, rhs, lacc->offset)))
+ || contains_view_convert_expr_p (lhs))
{
if (access_has_children_p (racc))
generate_subtree_copies (racc->first_child, racc->base, 0, 0, 0,