2015-05-26 Eric Botcazou <ebotcazou@adacore.com>
+ * gcc-interface/ada-tree.h (DECL_GLOBAL_NONCONSTANT_RENAMING_P): Delete
+ (DECL_RENAMED_OBJECT): Adjust comment.
+ * gcc-interface/gigi.h (record_global_nonconstant_renaming): Delete.
+ (invalidate_global_nonconstant_renamings): Likewise.
+ (gnat_constant_reference_p): Likewise.
+ (rewrite_fn): New function type.
+ (gnat_rewrite_reference): Declare.
+ (call_is_atomic_load): New inline predicate.
+ * gcc-interface/decl.c (elaborate_reference_1): New function.
+ (elaborate_reference): Likewise.
+ (gnat_to_gnu_entity): Call elaborate_reference to elaborate renamings
+ and simplify associated code. Set const_flag to true consistently in
+ conjunction with used_by_ref.
+ * gcc-interface/trans.c (Identifier_to_gnu): Always replace renaming
+ pointers by renamed objects.
+ (outer_atomic_access_required_p): Deal with renamings.
+ (Compilation_Unit_to_gnu): Do not call
+ invalidate_global_nonconstant_renamings.
+ (gnat_to_gnu) <N_Object_Renaming_Declaration>: Adjust comment.
+ (gnat_gimplify_expr): Deal with atomic loads.
+ * gcc-interface/utils.c (global_nonconstant_renamings): Delete.
+ (destroy_gnat_utils): Do not call
+ invalidate_global_nonconstant_renamings.
+ (record_global_nonconstant_renaming): Delete.
+ (invalidate_global_nonconstant_renamings): Likewise.
+ * gcc-interface/utils2.c (call_is_atomic_load): Move to gigi.h.
+ (build_load_modify_store): Build a copy of the destination.
+ (gnat_stabilize_reference_1): Adjust.
+ (gnat_stabilize_reference): Call gnat_stabilize_reference_1 through
+ gnat_rewrite_reference and move bulk of code to...
+ (gnat_rewrite_reference): ...here. New global function.
+ (gnat_constant_reference_p): Delete.
+
+2015-05-26 Eric Botcazou <ebotcazou@adacore.com>
+
* gcc-interface/gigi.h (gnat_stabilize_reference): Adjust prototype.
* gcc-interface/decl.c (gnat_to_gnu_entity): Do not rely on const_flag
to detect constant renamings. Be prepared for specific pattern of
is readonly. */
#define DECL_POINTS_TO_READONLY_P(NODE) DECL_LANG_FLAG_4 (NODE)
-/* Nonzero in a VAR_DECL if it is a global non-constant renaming. */
-#define DECL_GLOBAL_NONCONSTANT_RENAMING_P(NODE) \
- DECL_LANG_FLAG_5 (VAR_DECL_CHECK (NODE))
-
/* In a FIELD_DECL corresponding to a discriminant, contains the
discriminant number. */
#define DECL_DISCRIMINANT_NUMBER(NODE) DECL_INITIAL (FIELD_DECL_CHECK (NODE))
SET_DECL_LANG_SPECIFIC (VAR_DECL_CHECK (NODE), X)
/* In a VAR_DECL without the DECL_LOOP_PARM_P flag set and that is a renaming
- pointer, points to the object being renamed, if any. Note that this object
- is guaranteed to be protected against multiple evaluations. */
+ pointer, points to the object being renamed, if any. */
#define DECL_RENAMED_OBJECT(NODE) \
GET_DECL_LANG_SPECIFIC (VAR_DECL_CHECK (NODE))
#define SET_DECL_RENAMED_OBJECT(NODE, X) \
static tree elaborate_expression_1 (tree, Entity_Id, const char *, bool, bool);
static tree elaborate_expression_2 (tree, Entity_Id, const char *, bool, bool,
unsigned int);
+static tree elaborate_reference (tree, Entity_Id, bool);
static tree gnat_to_gnu_component_type (Entity_Id, bool, bool);
static tree gnat_to_gnu_param (Entity_Id, Mechanism_Type, Entity_Id, bool,
bool *);
break;
}
- /* If we have an external constant that we are not defining, get the
- expression that is was defined to represent. We may throw it away
- later if it is not a constant. But do not retrieve the expression
- if it is an allocator because the designated type might be dummy
- at this point. */
+ /* If we have a constant that we are not defining, get the expression it
+ was defined to represent. This is necessary to avoid generating dumb
+ elaboration code in simple cases, but we may throw it away later if it
+ is not a constant. But do not retrieve it if it is an allocator since
+ the designated type might still be dummy at this point. */
if (!definition
&& !No_Initialization (Declaration_Node (gnat_entity))
&& Present (Expression (Declaration_Node (gnat_entity)))
before the call to "=". */
if (TREE_CODE (inner) == TRUTH_ANDIF_EXPR)
inner = TREE_OPERAND (inner, 1);
- if (TREE_CODE (inner) == CALL_EXPR
- || TREE_CODE (inner) == NULL_EXPR
- || TREE_CODE (inner) == CONSTRUCTOR
+ if ((TREE_CODE (inner) == CALL_EXPR
+ && !call_is_atomic_load (inner))
+ || TREE_CODE (inner) == NULL_EXPR
+ || TREE_CODE (inner) == CONSTRUCTOR
|| CONSTANT_CLASS_P (inner))
;
/* Case 2: if the renaming entity need not be materialized, use
- the stabilized renamed expression for the renaming. At the
- global level, we can do this only if we know no SAVE_EXPRs
- need be made, because otherwise the expression would be tied
- to a specific elaboration routine. */
- else if (!Materialize_Entity (gnat_entity)
- && (!global_bindings_p ()
- || (staticp (gnu_expr)
- && !TREE_SIDE_EFFECTS (gnu_expr))))
+ the elaborated renamed expression for the renaming. But this
+ means that the caller is responsible for evaluating the address
+ of the renaming at the correct spot in the definition case to
+ instantiate the SAVE_EXPRs. */
+ else if (!Materialize_Entity (gnat_entity))
{
- gnu_decl = gnat_stabilize_reference (gnu_expr, true);
+ gnu_decl
+ = elaborate_reference (gnu_expr, gnat_entity, definition);
- /* ??? No DECL_EXPR is created so we need to mark
- the expression manually lest it is shared. */
+ /* No DECL_EXPR will be created so the expression needs to be
+ marked manually because it will likely be shared. */
if (global_bindings_p ())
MARK_VISITED (gnu_decl);
- /* This assertion will fail if the renamed object isn't
- aligned enough as to make it possible to honor the
- alignment set on the renaming. */
+ /* This assertion will fail if the renamed object isn't aligned
+ enough as to make it possible to honor the alignment set on
+ the renaming. */
if (align)
{
unsigned int ralign = DECL_P (gnu_decl)
}
/* Case 3: otherwise, make a constant pointer to the object we
- are to rename and attach the object to the pointer after it
- is stabilized.
-
- From the proper scope, attached objects will be referenced
- directly instead of indirectly via the pointer to avoid
- subtle aliasing problems with non-addressable entities.
- They have to be stable because we must not evaluate the
- variables in the expression every time the renaming is used.
- The pointer is called a "renaming" pointer in this case.
-
- Note that we need to preserve the volatility of the renamed
- object through the indirection. */
+ are renaming and attach the object to the pointer after it is
+ elaborated. The object will be referenced directly instead
+ of indirectly via the pointer to avoid aliasing problems with
+ non-addressable entities. The pointer is called a "renaming"
+ pointer in this case. Note that we also need to preserve the
+ volatility of the renamed object through the indirection. */
else
{
if (TREE_THIS_VOLATILE (gnu_expr) && !TYPE_VOLATILE (gnu_type))
gnu_type
= change_qualified_type (gnu_type, TYPE_QUAL_VOLATILE);
+
gnu_type = build_reference_type (gnu_type);
- inner_const_flag = TREE_READONLY (gnu_expr);
+ used_by_ref = true;
const_flag = true;
+ inner_const_flag = TREE_READONLY (gnu_expr);
+ gnu_size = NULL_TREE;
- /* Stabilize and attach the expression to the pointer.
-
- Note that this might introduce SAVE_EXPRs and we don't
- check whether we are at the global level or not. This
- is fine since we are building a pointer initializer and
- neither the pointer nor the initializing expression can
- be accessed before the pointer elaboration has taken
- place in a correct program.
+ renamed_obj
+ = elaborate_reference (gnu_expr, gnat_entity, definition);
- These SAVE_EXPRs will be evaluated at the right place
- by either the evaluation of the initializer for the
- non-global case or the elaboration code for the global
- case, and will be attached to the elaboration procedure
- in the latter case. */
- renamed_obj = gnat_stabilize_reference (gnu_expr, true);
+ /* If we are not defining the entity, the expression will not
+ be attached through DECL_INITIAL so it needs to be marked
+ manually because it will likely be shared. Likewise for a
+ dereference as it will be folded by the ADDR_EXPR below. */
+ if ((!definition || TREE_CODE (renamed_obj) == INDIRECT_REF)
+ && global_bindings_p ())
+ MARK_VISITED (renamed_obj);
if (type_annotate_only
- && TREE_CODE (renamed_obj) == ERROR_MARK)
+ && TREE_CODE (renamed_obj) == ERROR_MARK)
gnu_expr = NULL_TREE;
else
gnu_expr
= build_unary_op (ADDR_EXPR, gnu_type, renamed_obj);
-
- gnu_size = NULL_TREE;
- used_by_ref = true;
}
}
save_gnu_tree (gnat_entity, NULL_TREE, false);
- /* Ignore the size. It's either meaningless or was handled
- above. */
- gnu_size = NULL_TREE;
/* Convert the type of the object to a reference type that can
alias everything as per 13.3(19). */
gnu_type
const_flag
= !Is_Public (gnat_entity)
|| compile_time_known_address_p (gnat_expr);
+ gnu_size = NULL_TREE;
/* If this is a deferred constant, the initializer is attached to
the full view. */
alias everything as per 13.3(19). */
gnu_type
= build_reference_type_for_mode (gnu_type, ptr_mode, true);
+ used_by_ref = true;
gnu_size = NULL_TREE;
/* No point in taking the address of an initializing expression
const_flag = true;
}
}
-
- used_by_ref = true;
}
/* If we are at top level and this object is of variable size,
|| static_p)))
{
gnu_type = build_reference_type (gnu_type);
- gnu_size = NULL_TREE;
used_by_ref = true;
+ const_flag = true;
+ gnu_size = NULL_TREE;
/* In case this was a aliased object whose nominal subtype is
unconstrained, the pointer above will be a thin pointer and
gnu_expr
= build_allocator (gnu_alloc_type, gnu_expr, gnu_type,
Empty, Empty, gnat_entity, mutable_p);
- const_flag = true;
}
else
- {
- gnu_expr = NULL_TREE;
- const_flag = false;
- }
+ gnu_expr = NULL_TREE;
}
/* If this object would go into the stack and has an alignment larger
build_component_ref (gnu_new_var, NULL_TREE,
TYPE_FIELDS (gnu_new_type), false));
- gnu_size = NULL_TREE;
used_by_ref = true;
const_flag = true;
+ gnu_size = NULL_TREE;
}
/* If this is an aliased object with an unconstrained nominal subtype,
= build_unary_op (ADDR_EXPR, NULL_TREE, gnu_unc_var);
TREE_CONSTANT (gnu_expr) = 1;
- gnu_size = NULL_TREE;
used_by_ref = true;
- inner_const_flag = TREE_READONLY (gnu_unc_var);
const_flag = true;
+ inner_const_flag = TREE_READONLY (gnu_unc_var);
+ gnu_size = NULL_TREE;
}
gnu_type
else if (kind == E_Loop_Parameter)
DECL_LOOP_PARM_P (gnu_decl) = 1;
- /* If this is a renaming pointer, attach the renamed object to it and
- register it if we are at the global level and the renamed object
- is a non-constant reference. */
+ /* If this is a renaming pointer, attach the renamed object to it. */
if (renamed_obj)
- {
- SET_DECL_RENAMED_OBJECT (gnu_decl, renamed_obj);
-
- if (global_bindings_p ()
- && !gnat_constant_reference_p (renamed_obj))
- {
- DECL_GLOBAL_NONCONSTANT_RENAMING_P (gnu_decl) = 1;
- record_global_nonconstant_renaming (gnu_decl);
- }
- }
+ SET_DECL_RENAMED_OBJECT (gnu_decl, renamed_obj);
/* If this is a constant and we are defining it or it generates a real
symbol at the object level and we are referencing it, we may want
/* Given a GNAT tree GNAT_EXPR, for an expression which is a value within a
type definition (either a bound or a discriminant value) for GNAT_ENTITY,
return the GCC tree to use for that expression. S is the suffix to use
- if a variable needs to be created and DEFINITION is true if this is made
+ if a variable needs to be created and DEFINITION is true if this is done
for a definition of GNAT_ENTITY. If NEED_VALUE is true, we need a result;
otherwise, we are just elaborating the expression for side-effects. If
NEED_DEBUG is true, we need a variable for debugging purposes even if it
need_debug),
unit_align);
}
+
+/* Structure to hold internal data for elaborate_reference. */
+
+struct er_data
+{
+ Entity_Id entity;
+ bool definition;
+};
+
+/* Wrapper function around elaborate_expression_1 for elaborate_reference. */
+
+static tree
+elaborate_reference_1 (tree ref, void *data, int n)
+{
+ struct er_data *er = (struct er_data *)data;
+ char suffix[16];
+
+ /* This is what elaborate_expression_1 does if NEED_DEBUG is false. */
+ if (TREE_CONSTANT (ref))
+ return ref;
+
+ /* If this is a COMPONENT_REF of a fat pointer, elaborate the entire fat
+ pointer. This may be more efficient, but will also allow us to more
+ easily find the match for the PLACEHOLDER_EXPR. */
+ if (TREE_CODE (ref) == COMPONENT_REF
+ && TYPE_IS_FAT_POINTER_P (TREE_TYPE (TREE_OPERAND (ref, 0))))
+ return build3 (COMPONENT_REF, TREE_TYPE (ref),
+ elaborate_reference_1 (TREE_OPERAND (ref, 0), data, n),
+ TREE_OPERAND (ref, 1), TREE_OPERAND (ref, 2));
+
+ sprintf (suffix, "EXP%d", n);
+ return
+ elaborate_expression_1 (ref, er->entity, suffix, er->definition, false);
+}
+
+/* Elaborate the reference REF to be used as renamed object for GNAT_ENTITY.
+ DEFINITION is true if this is done for a definition of GNAT_ENTITY. */
+
+static tree
+elaborate_reference (tree ref, Entity_Id gnat_entity, bool definition)
+{
+ struct er_data er = { gnat_entity, definition };
+ return gnat_rewrite_reference (ref, elaborate_reference_1, &er);
+}
\f
/* Given a GNU tree and a GNAT list of choices, generate an expression to test
the value passed against the list of choices. */
const_flag, public_flag, extern_flag, \
static_flag, false, attr_list, gnat_node)
-/* Record DECL as a global non-constant renaming. */
-extern void record_global_nonconstant_renaming (tree decl);
-
-/* Invalidate the global non-constant renamings. */
-extern void invalidate_global_nonconstant_renamings (void);
-
/* Return a FIELD_DECL node. FIELD_NAME is the field's name, FIELD_TYPE is
its type and RECORD_TYPE is the type of the enclosing record. If SIZE is
nonzero, it is the specified size of the field. If POS is nonzero, it is
force evaluation of everything. */
extern tree gnat_stabilize_reference (tree ref, bool force);
+/* Rewrite reference REF and call FUNC on each expression within REF in the
+ process. DATA is passed unmodified to FUNC and N is bumped each time it
+ is passed to FUNC, so FUNC is guaranteed to see a given N only once per
+ reference to be rewritten. */
+typedef tree (*rewrite_fn) (tree, void *, int);
+extern tree gnat_rewrite_reference (tree ref, rewrite_fn func, void *data,
+ int n = 1);
+
/* This is equivalent to get_inner_reference in expr.c but it returns the
ultimate containing object only if the reference (lvalue) is constant,
i.e. if it doesn't depend on the context in which it is evaluated. */
extern tree get_inner_constant_reference (tree exp);
-/* Return true if REF is a constant reference, i.e. a reference (lvalue) that
- doesn't depend on the context in which it is evaluated. */
-extern bool gnat_constant_reference_p (tree ref);
-
/* If EXPR is an expression that is invariant in the current function, in the
sense that it can be evaluated anywhere in the function and any number of
times, return EXPR or an equivalent expression. Otherwise return NULL. */
{
return (unsigned HOST_WIDE_INT) 1 << (floor_log2 (x - 1) + 1);
}
+
+/* Return true if EXP, a CALL_EXPR, is an atomic load. */
+
+static inline bool
+call_is_atomic_load (tree exp)
+{
+ tree fndecl = get_callee_fndecl (exp);
+
+ if (!(fndecl && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL))
+ return false;
+
+ enum built_in_function code = DECL_FUNCTION_CODE (fndecl);
+ return BUILT_IN_ATOMIC_LOAD_N <= code && code <= BUILT_IN_ATOMIC_LOAD_16;
+}
true, false)))
gnu_result = DECL_INITIAL (gnu_result);
- /* If it's a renaming pointer and not a global non-constant renaming or
- we are at the global level, the we can reference the renamed object
- directly, since it is either constant or has been protected against
- multiple evaluations. */
+ /* If it's a renaming pointer, get to the renamed object. */
if (TREE_CODE (gnu_result) == VAR_DECL
&& !DECL_LOOP_PARM_P (gnu_result)
- && DECL_RENAMED_OBJECT (gnu_result)
- && (!DECL_GLOBAL_NONCONSTANT_RENAMING_P (gnu_result)
- || global_bindings_p ()))
+ && DECL_RENAMED_OBJECT (gnu_result))
gnu_result = DECL_RENAMED_OBJECT (gnu_result);
/* Otherwise, do the final dereference. */
{
gnat_node = gnat_strip_type_conversion (gnat_node);
- while (Nkind (gnat_node) == N_Indexed_Component
- || Nkind (gnat_node) == N_Selected_Component
- || Nkind (gnat_node) == N_Slice)
+ while (true)
{
- gnat_node = gnat_strip_type_conversion (Prefix (gnat_node));
- if (node_has_volatile_full_access (gnat_node))
- return true;
+ switch (Nkind (gnat_node))
+ {
+ case N_Identifier:
+ case N_Expanded_Name:
+ if (No (Renamed_Object (Entity (gnat_node))))
+ return false;
+ gnat_node
+ = gnat_strip_type_conversion (Renamed_Object (Entity (gnat_node)));
+ break;
+
+ case N_Indexed_Component:
+ case N_Selected_Component:
+ case N_Slice:
+ gnat_node = gnat_strip_type_conversion (Prefix (gnat_node));
+ if (node_has_volatile_full_access (gnat_node))
+ return true;
+ break;
+
+ default:
+ return false;
+ }
}
- return false;
+ gcc_unreachable ();
}
/* Return true if GNAT_NODE requires atomic access and set SYNC according to
info->gnat_node = gnat_node;
elab_info_list = info;
- /* Invalidate the global non-constant renamings. This is necessary because
- stabilization of the renamed entities may create SAVE_EXPRs which have
- been tied to a specific elaboration routine just above. */
- invalidate_global_nonconstant_renamings ();
-
/* Force the processing for all nodes that remain in the queue. */
process_deferred_decl_context (true);
}
tree gnu_temp
= gnat_to_gnu_entity (gnat_temp,
gnat_to_gnu (Renamed_Object (gnat_temp)), 1);
- /* We need to make sure that the side-effects of the renamed object
- are evaluated at this point, so we evaluate its address. */
+ /* See case 2 of renaming in gnat_to_gnu_entity. */
if (TREE_SIDE_EFFECTS (gnu_temp))
gnu_result = build_unary_op (ADDR_EXPR, NULL_TREE, gnu_temp);
}
return GS_ALL_DONE;
}
+ /* Replace atomic loads with their first argument. That's necessary
+ because the gimplifier would create a temporary otherwise. */
+ if (TREE_SIDE_EFFECTS (op))
+ while (handled_component_p (op) || CONVERT_EXPR_P (op))
+ {
+ tree inner = TREE_OPERAND (op, 0);
+ if (TREE_CODE (inner) == CALL_EXPR && call_is_atomic_load (inner))
+ {
+ tree t = CALL_EXPR_ARG (inner, 0);
+ if (TREE_CODE (t) == NOP_EXPR)
+ t = TREE_OPERAND (t, 0);
+ if (TREE_CODE (t) == ADDR_EXPR)
+ TREE_OPERAND (op, 0) = TREE_OPERAND (t, 0);
+ else
+ TREE_OPERAND (op, 0) = build_fold_indirect_ref (t);
+ }
+ else
+ op = inner;
+ }
+
return GS_UNHANDLED;
case VIEW_CONVERT_EXPR:
/* An array of builtin function declarations. */
static GTY(()) vec<tree, va_gc> *builtin_decls;
-/* An array of global non-constant renamings. */
-static GTY(()) vec<tree, va_gc> *global_nonconstant_renamings;
-
/* A chain of unused BLOCK nodes. */
static GTY((deletable)) tree free_block_chain;
/* Destroy the hash table of padded types. */
pad_type_hash_table->empty ();
pad_type_hash_table = NULL;
-
- /* Invalidate the global non-constant renamings. */
- invalidate_global_nonconstant_renamings ();
}
\f
/* GNAT_ENTITY is a GNAT tree node for an entity. Associate GNU_DECL, a GCC
*attr_list = NULL;
}
-\f
-/* Record DECL as a global non-constant renaming. */
-
-void
-record_global_nonconstant_renaming (tree decl)
-{
- gcc_assert (!DECL_LOOP_PARM_P (decl) && DECL_RENAMED_OBJECT (decl));
- vec_safe_push (global_nonconstant_renamings, decl);
-}
-
-/* Invalidate the global non-constant renamings, lest their renamed object
- contains SAVE_EXPRs tied to an elaboration routine. */
-
-void
-invalidate_global_nonconstant_renamings (void)
-{
- unsigned int i;
- tree iter;
-
- if (global_nonconstant_renamings == NULL)
- return;
-
- FOR_EACH_VEC_ELT (*global_nonconstant_renamings, i, iter)
- SET_DECL_RENAMED_OBJECT (iter, NULL_TREE);
-
- vec_free (global_nonconstant_renamings);
-}
/* Return true if VALUE is a known to be a multiple of FACTOR, which must be
a power of 2. */
return build_call_expr (t, 3, addr, src, mem_model);
}
-/* Return true if EXP, a CALL_EXPR, is an atomic load. */
-
-static bool
-call_is_atomic_load (tree exp)
-{
- tree fndecl = get_callee_fndecl (exp);
-
- if (!(fndecl && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL))
- return false;
-
- enum built_in_function code = DECL_FUNCTION_CODE (fndecl);
- return BUILT_IN_ATOMIC_LOAD_N <= code && code <= BUILT_IN_ATOMIC_LOAD_16;
-}
-
/* Build a load-modify-store sequence from SRC to DEST. GNAT_NODE is used for
- the location of the sequence. Note that, even if the load and the store are
- both atomic, the sequence itself is not atomic. */
+ the location of the sequence. Note that, even though the load and the store
+ are both atomic, the sequence itself is not atomic. */
tree
build_load_modify_store (tree dest, tree src, Node_Id gnat_node)
{
+ /* We will be modifying DEST below so we build a copy. */
+ dest = copy_node (dest);
tree ref = dest;
while (handled_component_p (ref))
}
}
+ TREE_OPERAND (ref, 0) = copy_node (TREE_OPERAND (ref, 0));
ref = TREE_OPERAND (ref, 0);
}
argument to force evaluation of everything. */
static tree
-gnat_stabilize_reference_1 (tree e, bool force)
+gnat_stabilize_reference_1 (tree e, void *data, int n)
{
+ const bool force = *(bool *)data;
enum tree_code code = TREE_CODE (e);
tree type = TREE_TYPE (e);
tree result;
&& TYPE_IS_FAT_POINTER_P (TREE_TYPE (TREE_OPERAND (e, 0))))
result
= build3 (code, type,
- gnat_stabilize_reference_1 (TREE_OPERAND (e, 0), force),
+ gnat_stabilize_reference_1 (TREE_OPERAND (e, 0), data, n),
TREE_OPERAND (e, 1), TREE_OPERAND (e, 2));
/* If the expression has side-effects, then encase it in a SAVE_EXPR
so that it will only be evaluated once. */
/* Recursively stabilize each operand. */
result
= build2 (code, type,
- gnat_stabilize_reference_1 (TREE_OPERAND (e, 0), force),
- gnat_stabilize_reference_1 (TREE_OPERAND (e, 1), force));
+ gnat_stabilize_reference_1 (TREE_OPERAND (e, 0), data, n),
+ gnat_stabilize_reference_1 (TREE_OPERAND (e, 1), data, n));
break;
case tcc_unary:
/* Recursively stabilize each operand. */
result
= build1 (code, type,
- gnat_stabilize_reference_1 (TREE_OPERAND (e, 0), force));
+ gnat_stabilize_reference_1 (TREE_OPERAND (e, 0), data, n));
break;
default:
tree
gnat_stabilize_reference (tree ref, bool force)
{
+ return gnat_rewrite_reference (ref, gnat_stabilize_reference_1, &force);
+}
+
+/* Rewrite reference REF and call FUNC on each expression within REF in the
+ process. DATA is passed unmodified to FUNC and N is bumped each time it
+ is passed to FUNC, so FUNC is guaranteed to see a given N only once per
+ reference to be rewritten. */
+
+tree
+gnat_rewrite_reference (tree ref, rewrite_fn func, void *data, int n)
+{
tree type = TREE_TYPE (ref);
enum tree_code code = TREE_CODE (ref);
tree result;
case VIEW_CONVERT_EXPR:
result
= build1 (code, type,
- gnat_stabilize_reference (TREE_OPERAND (ref, 0), force));
+ gnat_rewrite_reference (TREE_OPERAND (ref, 0), func, data,
+ n));
break;
case INDIRECT_REF:
case UNCONSTRAINED_ARRAY_REF:
- result = build1 (code, type,
- gnat_stabilize_reference_1 (TREE_OPERAND (ref, 0),
- force));
+ result = build1 (code, type, func (TREE_OPERAND (ref, 0), data, n));
break;
case COMPONENT_REF:
result = build3 (COMPONENT_REF, type,
- gnat_stabilize_reference (TREE_OPERAND (ref, 0), force),
+ gnat_rewrite_reference (TREE_OPERAND (ref, 0), func,
+ data, n),
TREE_OPERAND (ref, 1), NULL_TREE);
break;
case BIT_FIELD_REF:
result = build3 (BIT_FIELD_REF, type,
- gnat_stabilize_reference (TREE_OPERAND (ref, 0), force),
+ gnat_rewrite_reference (TREE_OPERAND (ref, 0), func,
+ data, n),
TREE_OPERAND (ref, 1), TREE_OPERAND (ref, 2));
break;
case ARRAY_RANGE_REF:
result
= build4 (code, type,
- gnat_stabilize_reference (TREE_OPERAND (ref, 0), force),
- gnat_stabilize_reference_1 (TREE_OPERAND (ref, 1), force),
+ gnat_rewrite_reference (TREE_OPERAND (ref, 0), func, data,
+ n + 1),
+ func (TREE_OPERAND (ref, 1), data, n),
TREE_OPERAND (ref, 2), TREE_OPERAND (ref, 3));
break;
t = TREE_OPERAND (t, 0);
if (TREE_CODE (t) == ADDR_EXPR)
t = build1 (ADDR_EXPR, TREE_TYPE (t),
- gnat_stabilize_reference (TREE_OPERAND (t, 0), force));
+ gnat_rewrite_reference (TREE_OPERAND (t, 0), func, data,
+ n));
else
- t = gnat_stabilize_reference_1 (t, force);
+ t = func (t, data, n);
t = fold_convert (TREE_TYPE (CALL_EXPR_ARG (ref, 0)), t);
result = build_call_expr (TREE_OPERAND (CALL_EXPR_FN (ref), 0), 2,
return exp;
}
-/* Return true if REF is a constant reference, i.e. a reference (lvalue) that
- doesn't depend on the context in which it is evaluated. */
-
-bool
-gnat_constant_reference_p (tree ref)
-{
- if (handled_component_p (ref))
- {
- ref = get_inner_constant_reference (ref);
- if (!ref)
- return false;
- }
-
- return DECL_P (ref);
-}
-
/* If EXPR is an expression that is invariant in the current function, in the
sense that it can be evaluated anywhere in the function and any number of
times, return EXPR or an equivalent expression. Otherwise return NULL. */