* *
* C Implementation File *
* *
- * Copyright (C) 1992-2009, Free Software Foundation, Inc. *
+ * Copyright (C) 1992-2010, Free Software Foundation, Inc. *
* *
* GNAT is free software; you can redistribute it and/or modify it under *
* terms of the GNU General Public License as published by the Free Soft- *
tree gnu_field_list = NULL_TREE;
/* Non-null for subprograms containing parameters passed by copy-in
copy-out (Ada In Out or Out parameters not passed by reference),
- in which case it is the list of nodes used to specify the values of
- the in out/out parameters that are returned as a record upon
+ in which case it is the list of nodes used to specify the values
+ of the In Out/Out parameters that are returned as a record upon
procedure return. The TREE_PURPOSE of an element of this list is
a field of the record and the TREE_VALUE is the PARM_DECL
corresponding to that field. This list will be saved in the
TYPE_CI_CO_LIST field of the FUNCTION_TYPE node we create. */
- tree gnu_return_list = NULL_TREE;
+ tree gnu_cico_list = NULL_TREE;
/* If an import pragma asks to map this subprogram to a GCC builtin,
this is the builtin DECL node. */
tree gnu_builtin_decl = NULL_TREE;
&& Is_Pure (gnat_entity));
bool volatile_flag = No_Return (gnat_entity);
- bool returns_by_ref = false;
- bool returns_unconstrained = false;
- bool returns_by_target_ptr = false;
+ bool return_by_direct_ref_p = false;
+ bool return_by_invisi_ref_p = false;
+ bool return_unconstrained_p = false;
bool has_copy_in_out = false;
bool has_stub = false;
int parmnum;
if (kind == E_Function || kind == E_Subprogram_Type)
gnu_return_type = gnat_to_gnu_type (Etype (gnat_entity));
- /* If this function returns by reference, make the actual
- return type of this function the pointer and mark the decl. */
+ /* If this function returns by reference, make the actual return
+ type of this function the pointer and mark the decl. */
if (Returns_By_Ref (gnat_entity))
{
- returns_by_ref = true;
gnu_return_type = build_pointer_type (gnu_return_type);
+ return_by_direct_ref_p = true;
}
- /* If the Mechanism is By_Reference, ensure the return type uses
- the machine's by-reference mechanism, which may not the same
- as above (e.g., it might be by passing a fake parameter). */
- else if (kind == E_Function
- && Mechanism (gnat_entity) == By_Reference)
- {
- TREE_ADDRESSABLE (gnu_return_type) = 1;
-
- /* We expect this bit to be reset by gigi shortly, so can avoid a
- type node copy here. This actually also prevents troubles with
- the generation of debug information for the function, because
- we might have issued such info for this type already, and would
- be attaching a distinct type node to the function if we made a
- copy here. */
- }
-
- /* If we are supposed to return an unconstrained array,
- actually return a fat pointer and make a note of that. Return
- a pointer to an unconstrained record of variable size. */
+ /* If the Mechanism is By_Reference, ensure this function uses the
+ target's by-invisible-reference mechanism, which may not be the
+ same as above (e.g. it might be passing an extra parameter).
+
+ Prior to GCC 4, this was handled by just setting TREE_ADDRESSABLE
+ on the result type. Everything required to pass by invisible
+ reference using the target's mechanism (e.g. an extra parameter)
+ was handled at RTL expansion time.
+
+ This doesn't work with GCC 4 any more for several reasons. First,
+ the gimplification process might need to create temporaries of this
+ type and the gimplifier ICEs on such attempts; that's why the flag
+ is now set on the function type instead. Second, the middle-end
+ now also relies on a different attribute, DECL_BY_REFERENCE on the
+ RESULT_DECL, and expects the by-invisible-reference-ness to be made
+ explicit in the function body. */
+ else if (kind == E_Function && Mechanism (gnat_entity) == By_Reference)
+ return_by_invisi_ref_p = true;
+
+ /* If we are supposed to return an unconstrained array, actually return
+ a fat pointer and make a note of that. */
else if (TREE_CODE (gnu_return_type) == UNCONSTRAINED_ARRAY_TYPE)
{
gnu_return_type = TREE_TYPE (gnu_return_type);
- returns_unconstrained = true;
+ return_unconstrained_p = true;
}
/* If the type requires a transient scope, the result is allocated
else if (Requires_Transient_Scope (Etype (gnat_entity)))
{
gnu_return_type = build_pointer_type (gnu_return_type);
- returns_unconstrained = true;
+ return_unconstrained_p = true;
}
/* If the type is a padded type and the underlying type would not
|| Has_Foreign_Convention (gnat_entity)))
gnu_return_type = TREE_TYPE (TYPE_FIELDS (gnu_return_type));
- /* If the return type has a non-constant size, we convert the function
- into a procedure and its caller will pass a pointer to an object as
- the first parameter when we call the function. This can happen for
- an unconstrained type with a maximum size or a constrained type with
- a size not known at compile time. */
- if (TYPE_SIZE_UNIT (gnu_return_type)
- && !TREE_CONSTANT (TYPE_SIZE_UNIT (gnu_return_type)))
+ /* If the return type is unconstrained, that means it must have a
+ maximum size. Use the padded type as the effective return type.
+ And ensure the function uses the target's by-invisible-reference
+ mechanism to avoid copying too much data when it returns. */
+ if (CONTAINS_PLACEHOLDER_P (TYPE_SIZE (gnu_return_type)))
{
- returns_by_target_ptr = true;
- gnu_param_list
- = create_param_decl (get_identifier ("TARGET"),
- build_reference_type (gnu_return_type),
- true);
- gnu_return_type = void_type_node;
+ gnu_return_type
+ = maybe_pad_type (gnu_return_type,
+ max_size (TYPE_SIZE (gnu_return_type), true),
+ 0, gnat_entity, false, false, false, true);
+ return_by_invisi_ref_p = true;
}
/* If the return type has a size that overflows, we cannot have
&DECL_SOURCE_LOCATION (gnu_field));
TREE_CHAIN (gnu_field) = gnu_field_list;
gnu_field_list = gnu_field;
- gnu_return_list = tree_cons (gnu_field, gnu_param,
- gnu_return_list);
+ gnu_cico_list
+ = tree_cons (gnu_field, gnu_param, gnu_cico_list);
}
}
/* If we have a CICO list but it has only one entry, we convert
this function into a function that simply returns that one
object. */
- if (list_length (gnu_return_list) == 1)
- gnu_return_type = TREE_TYPE (TREE_PURPOSE (gnu_return_list));
+ if (list_length (gnu_cico_list) == 1)
+ gnu_return_type = TREE_TYPE (TREE_PURPOSE (gnu_cico_list));
if (Has_Stdcall_Convention (gnat_entity))
prepend_one_attribute_to
gnu_param_list = nreverse (gnu_param_list);
if (has_stub)
gnu_stub_param_list = nreverse (gnu_stub_param_list);
- gnu_return_list = nreverse (gnu_return_list);
+ gnu_cico_list = nreverse (gnu_cico_list);
if (Ekind (gnat_entity) == E_Function)
- Set_Mechanism (gnat_entity,
- (returns_by_ref || returns_unconstrained
- ? By_Reference : By_Copy));
+ Set_Mechanism (gnat_entity, return_unconstrained_p
+ || return_by_direct_ref_p
+ || return_by_invisi_ref_p
+ ? By_Reference : By_Copy);
gnu_type
= create_subprog_type (gnu_return_type, gnu_param_list,
- gnu_return_list, returns_unconstrained,
- returns_by_ref, returns_by_target_ptr);
+ gnu_cico_list, return_unconstrained_p,
+ return_by_direct_ref_p,
+ return_by_invisi_ref_p);
if (has_stub)
gnu_stub_type
= create_subprog_type (gnu_return_type, gnu_stub_param_list,
- gnu_return_list, returns_unconstrained,
- returns_by_ref, returns_by_target_ptr);
+ gnu_cico_list, return_unconstrained_p,
+ return_by_direct_ref_p,
+ return_by_invisi_ref_p);
/* A subprogram (something that doesn't return anything) shouldn't
be considered const since there would be no reason for such a
* *
* C Implementation File *
* *
- * Copyright (C) 1992-2009, Free Software Foundation, Inc. *
+ * Copyright (C) 1992-2010, Free Software Foundation, Inc. *
* *
* GNAT is free software; you can redistribute it and/or modify it under *
* terms of the GNU General Public License as published by the Free Soft- *
? Corresponding_Spec (gnat_node) : Defining_Entity (gnat_node));
/* The FUNCTION_DECL node corresponding to the subprogram spec. */
tree gnu_subprog_decl;
+ /* Its RESULT_DECL node. */
+ tree gnu_result_decl;
/* The FUNCTION_TYPE node corresponding to the subprogram spec. */
tree gnu_subprog_type;
tree gnu_cico_list;
= gnat_to_gnu_entity (gnat_subprog_id, NULL_TREE,
Acts_As_Spec (gnat_node)
&& !present_gnu_tree (gnat_subprog_id));
-
+ gnu_result_decl = DECL_RESULT (gnu_subprog_decl);
gnu_subprog_type = TREE_TYPE (gnu_subprog_decl);
+ /* If the function returns by invisible reference, make it explicit in the
+ function body. See gnat_to_gnu_entity, E_Subprogram_Type case. */
+ if (TREE_ADDRESSABLE (gnu_subprog_type))
+ {
+ TREE_TYPE (gnu_result_decl)
+ = build_reference_type (TREE_TYPE (gnu_result_decl));
+ relayout_decl (gnu_result_decl);
+ }
+
/* Propagate the debug mode. */
if (!Needs_Debug_Info (gnat_subprog_id))
DECL_IGNORED_P (gnu_subprog_decl) = 1;
gnu_result = end_stmt_group ();
}
- /* If we made a special return label, we need to make a block that contains
- the definition of that label and the copying to the return value. That
- block first contains the function, then the label and copy statement. */
+ /* If we are dealing with a return from an Ada procedure with parameters
+ passed by copy-in/copy-out, we need to return a record containing the
+ final values of these parameters. If the list contains only one entry,
+ return just that entry though.
+
+ For a full description of the copy-in/copy-out parameter mechanism, see
+ the part of the gnat_to_gnu_entity routine dealing with the translation
+ of subprograms.
+
+ We need to make a block that contains the definition of that label and
+ the copying of the return value. It first contains the function, then
+ the label and copy statement. */
if (TREE_VALUE (gnu_return_label_stack))
{
tree gnu_retval;
gnu_retval = gnat_build_constructor (TREE_TYPE (gnu_subprog_type),
gnu_cico_list);
- if (DECL_P (gnu_retval) && DECL_BY_REF_P (gnu_retval))
- gnu_retval = build_unary_op (INDIRECT_REF, NULL_TREE, gnu_retval);
-
- add_stmt_with_node
- (build_return_expr (DECL_RESULT (gnu_subprog_decl), gnu_retval),
- End_Label (Handled_Statement_Sequence (gnat_node)));
+ add_stmt_with_node (build_return_expr (gnu_result_decl, gnu_retval),
+ End_Label (Handled_Statement_Sequence (gnat_node)));
gnat_poplevel ();
gnu_result = end_stmt_group ();
}
tree gnu_subprog_node = gnat_to_gnu (Name (gnat_node));
/* The FUNCTION_TYPE node giving the GCC type of the subprogram. */
tree gnu_subprog_type = TREE_TYPE (gnu_subprog_node);
- tree gnu_subprog_addr = build_unary_op (ADDR_EXPR, NULL_TREE,
- gnu_subprog_node);
+ tree gnu_subprog_addr
+ = build_unary_op (ADDR_EXPR, NULL_TREE, gnu_subprog_node);
Entity_Id gnat_formal;
Node_Id gnat_actual;
tree gnu_actual_list = NULL_TREE;
}
}
- /* If we are calling by supplying a pointer to a target, set up that pointer
- as the first argument. Use GNU_TARGET if one was passed; otherwise, make
- a target by building a variable and use the maximum size of the type if
- it has self-referential size. */
- if (TYPE_RETURNS_BY_TARGET_PTR_P (gnu_subprog_type))
- {
- tree gnu_ret_type
- = TREE_TYPE (TREE_VALUE (TYPE_ARG_TYPES (gnu_subprog_type)));
-
- if (!gnu_target)
- {
- tree gnu_obj_type;
-
- if (CONTAINS_PLACEHOLDER_P (TYPE_SIZE (gnu_ret_type)))
- gnu_obj_type
- = maybe_pad_type (gnu_ret_type,
- max_size (TYPE_SIZE (gnu_ret_type), true),
- 0, Etype (Name (gnat_node)), false, false,
- false, true);
- else
- gnu_obj_type = gnu_ret_type;
-
- /* ??? We may be about to create a static temporary if we happen to
- be at the global binding level. That's a regression from what
- the 3.x back-end would generate in the same situation, but we
- don't have a mechanism in Gigi for creating automatic variables
- in the elaboration routines. */
- gnu_target
- = create_var_decl (create_tmp_var_name ("LR"), NULL, gnu_obj_type,
- NULL, false, false, false, false, NULL,
- gnat_node);
-
- *gnu_result_type_p = gnu_ret_type;
- }
-
- gnu_actual_list
- = tree_cons (NULL_TREE,
- build_unary_op (ADDR_EXPR, NULL_TREE,
- unchecked_convert (gnu_ret_type,
- gnu_target,
- false)),
- NULL_TREE);
-
- }
-
/* The only way we can be making a call via an access type is if Name is an
explicit dereference. In that case, get the list of formal args from the
type the access type is pointing to. Otherwise, get the formals from
nreverse (gnu_actual_list));
set_expr_location_from_node (gnu_subprog_call, gnat_node);
- /* If we return by passing a target, the result is the target after the
- call. We must not emit the call directly here because this might be
- evaluated as part of an expression with conditions to control whether
- the call should be emitted or not. */
- if (TYPE_RETURNS_BY_TARGET_PTR_P (gnu_subprog_type))
- {
- /* Conceptually, what we need is a COMPOUND_EXPR of the call followed by
- the target object. Doing so would potentially be inefficient though,
- as this expression might be wrapped up into a SAVE_EXPR later, which
- would incur a pointless temporary copy of the whole object.
-
- What we do instead is build a COMPOUND_EXPR returning the address of
- the target, and then dereference. Wrapping up the COMPOUND_EXPR into
- a SAVE_EXPR then only incurs a mere pointer copy. */
- tree gnu_target_addr = build_unary_op (ADDR_EXPR, NULL_TREE, gnu_target);
- set_expr_location_from_node (gnu_target_addr, gnat_node);
- gnu_result = build2 (COMPOUND_EXPR, TREE_TYPE (gnu_target_addr),
- gnu_subprog_call, gnu_target_addr);
- return build_unary_op (INDIRECT_REF, NULL_TREE, gnu_result);
- }
-
- /* If it is a function call, the result is the call expression unless
- a target is specified, in which case we copy the result into the target
- and return the assignment statement. */
- else if (Nkind (gnat_node) == N_Function_Call)
+ /* If it's a function call, the result is the call expression unless a target
+ is specified, in which case we copy the result into the target and return
+ the assignment statement. */
+ if (Nkind (gnat_node) == N_Function_Call)
{
gnu_result = gnu_subprog_call;
+ enum tree_code op_code;
- /* If the function returns an unconstrained array or by reference,
- we have to de-dereference the pointer. */
- if (TYPE_RETURNS_UNCONSTRAINED_P (gnu_subprog_type)
- || TYPE_RETURNS_BY_REF_P (gnu_subprog_type))
+ /* If the function returns an unconstrained array or by direct reference,
+ we have to dereference the pointer. */
+ if (TYPE_RETURN_UNCONSTRAINED_P (gnu_subprog_type)
+ || TYPE_RETURN_BY_DIRECT_REF_P (gnu_subprog_type))
gnu_result = build_unary_op (INDIRECT_REF, NULL_TREE, gnu_result);
if (gnu_target)
- gnu_result = build_binary_op (MODIFY_EXPR, NULL_TREE,
- gnu_target, gnu_result);
+ {
+ /* ??? If the return type has non-constant size, then force the
+ return slot optimization as we would not be able to generate
+ a temporary. That's what has been done historically. */
+ if (TREE_CONSTANT (TYPE_SIZE (TREE_TYPE (gnu_subprog_type))))
+ op_code = MODIFY_EXPR;
+ else
+ op_code = INIT_EXPR;
+
+ gnu_result
+ = build_binary_op (op_code, NULL_TREE, gnu_target, gnu_result);
+ }
else
*gnu_result_type_p = get_unpadded_type (Etype (gnat_node));
return gnu_result;
}
- /* If this is the case where the GNAT tree contains a procedure call
- but the Ada procedure has copy in copy out parameters, the special
- parameter passing mechanism must be used. */
- else if (TYPE_CI_CO_LIST (gnu_subprog_type) != NULL_TREE)
+ /* If this is the case where the GNAT tree contains a procedure call but the
+ Ada procedure has copy-in/copy-out parameters, then the special parameter
+ passing mechanism must be used. */
+ if (TYPE_CI_CO_LIST (gnu_subprog_type))
{
/* List of FIELD_DECLs associated with the PARM_DECLs of the copy
in copy out parameters. */
case N_Return_Statement:
{
- /* The gnu function type of the subprogram currently processed. */
- tree gnu_subprog_type = TREE_TYPE (current_function_decl);
- /* The return value from the subprogram. */
- tree gnu_ret_val = NULL_TREE;
- /* The place to put the return value. */
- tree gnu_lhs;
-
- /* If we are dealing with a "return;" from an Ada procedure with
- parameters passed by copy in copy out, we need to return a record
- containing the final values of these parameters. If the list
- contains only one entry, return just that entry.
-
- For a full description of the copy in copy out parameter mechanism,
- see the part of the gnat_to_gnu_entity routine dealing with the
- translation of subprograms.
-
- But if we have a return label defined, convert this into
- a branch to that label. */
+ tree gnu_ret_val, gnu_ret_obj;
+ /* If we have a return label defined, convert this into a branch to
+ that label. The return proper will be handled elsewhere. */
if (TREE_VALUE (gnu_return_label_stack))
{
gnu_result = build1 (GOTO_EXPR, void_type_node,
break;
}
- else if (TYPE_CI_CO_LIST (gnu_subprog_type))
- {
- gnu_lhs = DECL_RESULT (current_function_decl);
- if (list_length (TYPE_CI_CO_LIST (gnu_subprog_type)) == 1)
- gnu_ret_val = TREE_VALUE (TYPE_CI_CO_LIST (gnu_subprog_type));
- else
- gnu_ret_val
- = gnat_build_constructor (TREE_TYPE (gnu_subprog_type),
- TYPE_CI_CO_LIST (gnu_subprog_type));
- }
-
- /* If the Ada subprogram is a function, we just need to return the
- expression. If the subprogram returns an unconstrained
- array, we have to allocate a new version of the result and
- return it. If we return by reference, return a pointer. */
-
- else if (Present (Expression (gnat_node)))
+ /* If the subprogram is a function, we must return the expression. */
+ if (Present (Expression (gnat_node)))
{
- /* If the current function returns by target pointer and we
- are doing a call, pass that target to the call. */
- if (TYPE_RETURNS_BY_TARGET_PTR_P (gnu_subprog_type)
- && Nkind (Expression (gnat_node)) == N_Function_Call)
+ tree gnu_subprog_type = TREE_TYPE (current_function_decl);
+ tree gnu_result_decl = DECL_RESULT (current_function_decl);
+ gnu_ret_val = gnat_to_gnu (Expression (gnat_node));
+
+ /* Do not remove the padding from GNU_RET_VAL if the inner type is
+ self-referential since we want to allocate the fixed size. */
+ if (TREE_CODE (gnu_ret_val) == COMPONENT_REF
+ && TYPE_IS_PADDING_P
+ (TREE_TYPE (TREE_OPERAND (gnu_ret_val, 0)))
+ && CONTAINS_PLACEHOLDER_P
+ (TYPE_SIZE (TREE_TYPE (gnu_ret_val))))
+ gnu_ret_val = TREE_OPERAND (gnu_ret_val, 0);
+
+ /* If the subprogram returns by direct reference, return a pointer
+ to the return value. */
+ if (TYPE_RETURN_BY_DIRECT_REF_P (gnu_subprog_type)
+ || By_Ref (gnat_node))
+ gnu_ret_val = build_unary_op (ADDR_EXPR, NULL_TREE, gnu_ret_val);
+
+ /* Otherwise, if it returns an unconstrained array, we have to
+ allocate a new version of the result and return it. */
+ else if (TYPE_RETURN_UNCONSTRAINED_P (gnu_subprog_type))
{
- gnu_lhs
- = build_unary_op (INDIRECT_REF, NULL_TREE,
- DECL_ARGUMENTS (current_function_decl));
- gnu_result = call_to_gnu (Expression (gnat_node),
- &gnu_result_type, gnu_lhs);
+ gnu_ret_val = maybe_unconstrained_array (gnu_ret_val);
+ gnu_ret_val = build_allocator (TREE_TYPE (gnu_ret_val),
+ gnu_ret_val,
+ TREE_TYPE (gnu_subprog_type),
+ Procedure_To_Call (gnat_node),
+ Storage_Pool (gnat_node),
+ gnat_node, false);
}
- else
+
+ /* If the subprogram returns by invisible reference, dereference
+ the pointer it is passed using the type of the return value
+ and build the copy operation manually. This ensures that we
+ don't copy too much data, for example if the return type is
+ unconstrained with a maximum size. */
+ if (TREE_ADDRESSABLE (gnu_subprog_type))
{
- gnu_ret_val = gnat_to_gnu (Expression (gnat_node));
-
- if (TYPE_RETURNS_BY_TARGET_PTR_P (gnu_subprog_type))
- /* The original return type was unconstrained so dereference
- the TARGET pointer in the actual return value's type. */
- gnu_lhs
- = build_unary_op (INDIRECT_REF, TREE_TYPE (gnu_ret_val),
- DECL_ARGUMENTS (current_function_decl));
- else
- gnu_lhs = DECL_RESULT (current_function_decl);
-
- /* Do not remove the padding from GNU_RET_VAL if the inner
- type is self-referential since we want to allocate the fixed
- size in that case. */
- if (TREE_CODE (gnu_ret_val) == COMPONENT_REF
- && TYPE_IS_PADDING_P
- (TREE_TYPE (TREE_OPERAND (gnu_ret_val, 0)))
- && CONTAINS_PLACEHOLDER_P
- (TYPE_SIZE (TREE_TYPE (gnu_ret_val))))
- gnu_ret_val = TREE_OPERAND (gnu_ret_val, 0);
-
- if (TYPE_RETURNS_BY_REF_P (gnu_subprog_type)
- || By_Ref (gnat_node))
- gnu_ret_val
- = build_unary_op (ADDR_EXPR, NULL_TREE, gnu_ret_val);
-
- else if (TYPE_RETURNS_UNCONSTRAINED_P (gnu_subprog_type))
- {
- gnu_ret_val = maybe_unconstrained_array (gnu_ret_val);
- gnu_ret_val
- = build_allocator (TREE_TYPE (gnu_ret_val),
- gnu_ret_val,
- TREE_TYPE (gnu_subprog_type),
- Procedure_To_Call (gnat_node),
- Storage_Pool (gnat_node),
- gnat_node, false);
- }
+ gnu_ret_obj
+ = build_unary_op (INDIRECT_REF, TREE_TYPE (gnu_ret_val),
+ gnu_result_decl);
+ gnu_result = build_binary_op (MODIFY_EXPR, NULL_TREE,
+ gnu_ret_obj, gnu_ret_val);
+ add_stmt_with_node (gnu_result, gnat_node);
+ gnu_ret_val = NULL_TREE;
+ gnu_ret_obj = gnu_result_decl;
}
+
+ /* Otherwise, build a regular return. */
+ else
+ gnu_ret_obj = gnu_result_decl;
}
else
- /* If the Ada subprogram is a regular procedure, just return. */
- gnu_lhs = NULL_TREE;
-
- if (TYPE_RETURNS_BY_TARGET_PTR_P (gnu_subprog_type))
{
- if (gnu_ret_val)
- gnu_result = build_binary_op (MODIFY_EXPR, NULL_TREE,
- gnu_lhs, gnu_ret_val);
- add_stmt_with_node (gnu_result, gnat_node);
- gnu_lhs = NULL_TREE;
+ gnu_ret_val = NULL_TREE;
+ gnu_ret_obj = NULL_TREE;
}
- gnu_result = build_return_expr (gnu_lhs, gnu_ret_val);
+ gnu_result = build_return_expr (gnu_ret_obj, gnu_ret_val);
}
break;
else
t = gnu_decl;
- gnu_stmt = build_binary_op (MODIFY_EXPR, NULL_TREE, t, gnu_init);
+ gnu_stmt = build_binary_op (INIT_EXPR, NULL_TREE, t, gnu_init);
DECL_INITIAL (gnu_decl) = NULL_TREE;
if (TREE_READONLY (gnu_decl))
#include "tm.h"
#include "tree.h"
#include "rtl.h"
+#include "varray.h"
#include "gimple.h"
#include "tree-iterator.h"
#include "tree-inline.h"
#include "diagnostic.h"
-#include "tree-pretty-print.h"
#include "langhooks.h"
#include "langhooks-def.h"
#include "tree-flow.h"
#include "except.h"
#include "hashtab.h"
#include "flags.h"
+#include "real.h"
#include "function.h"
#include "output.h"
#include "expr.h"
enum omp_region_type
{
ORT_WORKSHARE = 0,
+ ORT_TASK = 1,
ORT_PARALLEL = 2,
- ORT_COMBINED_PARALLEL = 3,
- ORT_TASK = 4,
- ORT_UNTIED_TASK = 5
+ ORT_COMBINED_PARALLEL = 3
};
struct gimplify_omp_ctx
During gimplification, we need to manipulate statement sequences
before the def/use vectors have been constructed. */
-void
+static void
gimplify_seq_add_stmt (gimple_seq *seq_p, gimple gs)
{
gimple_stmt_iterator si;
c->privatized_types = pointer_set_create ();
c->location = input_location;
c->region_type = region_type;
- if ((region_type & ORT_TASK) == 0)
+ if (region_type != ORT_TASK)
c->default_kind = OMP_CLAUSE_DEFAULT_SHARED;
else
c->default_kind = OMP_CLAUSE_DEFAULT_UNSPECIFIED;
static void omp_add_variable (struct gimplify_omp_ctx *, tree, unsigned int);
static bool omp_notice_variable (struct gimplify_omp_ctx *, tree, bool);
+/* A subroutine of append_to_statement_list{,_force}. T is not NULL. */
+
+static void
+append_to_statement_list_1 (tree t, tree *list_p)
+{
+ tree list = *list_p;
+ tree_stmt_iterator i;
+
+ if (!list)
+ {
+ if (t && TREE_CODE (t) == STATEMENT_LIST)
+ {
+ *list_p = t;
+ return;
+ }
+ *list_p = list = alloc_stmt_list ();
+ }
+
+ i = tsi_last (list);
+ tsi_link_after (&i, t, TSI_CONTINUE_LINKING);
+}
+
+/* Add T to the end of the list container pointed to by LIST_P.
+ If T is an expression with no effects, it is ignored. */
+
+void
+append_to_statement_list (tree t, tree *list_p)
+{
+ if (t && TREE_SIDE_EFFECTS (t))
+ append_to_statement_list_1 (t, list_p);
+}
+
+/* Similar, but the statement is always added, regardless of side effects. */
+
+void
+append_to_statement_list_force (tree t, tree *list_p)
+{
+ if (t != NULL_TREE)
+ append_to_statement_list_1 (t, list_p);
+}
+
/* Both gimplify the statement T and append it to *SEQ_P. This function
behaves exactly as gimplify_stmt, but you don't have to pass T as a
reference. */
return tmp_var;
}
-/* Create a new temporary variable declaration of type TYPE by calling
- create_tmp_var and if TYPE is a vector or a complex number, mark the new
- temporary as gimple register. */
-
-tree
-create_tmp_reg (tree type, const char *prefix)
-{
- tree tmp;
-
- tmp = create_tmp_var (type, prefix);
- if (TREE_CODE (type) == COMPLEX_TYPE
- || TREE_CODE (type) == VECTOR_TYPE)
- DECL_GIMPLE_REG_P (tmp) = 1;
-
- return tmp;
-}
-
/* Create a temporary with a name derived from VAL. Subroutine of
lookup_tmp_var; nobody else should call this function. */
annotate_one_with_location (gs, location);
}
}
-\f
-/* This page contains routines to unshare tree nodes, i.e. to duplicate tree
- nodes that are referenced more than once in GENERIC functions. This is
- necessary because gimplification (translation into GIMPLE) is performed
- by modifying tree nodes in-place, so gimplication of a shared node in a
- first context could generate an invalid GIMPLE form in a second context.
-
- This is achieved with a simple mark/copy/unmark algorithm that walks the
- GENERIC representation top-down, marks nodes with TREE_VISITED the first
- time it encounters them, duplicates them if they already have TREE_VISITED
- set, and finally removes the TREE_VISITED marks it has set.
-
- The algorithm works only at the function level, i.e. it generates a GENERIC
- representation of a function with no nodes shared within the function when
- passed a GENERIC function (except for nodes that are allowed to be shared).
-
- At the global level, it is also necessary to unshare tree nodes that are
- referenced in more than one function, for the same aforementioned reason.
- This requires some cooperation from the front-end. There are 2 strategies:
-
- 1. Manual unsharing. The front-end needs to call unshare_expr on every
- expression that might end up being shared across functions.
-
- 2. Deep unsharing. This is an extension of regular unsharing. Instead
- of calling unshare_expr on expressions that might be shared across
- functions, the front-end pre-marks them with TREE_VISITED. This will
- ensure that they are unshared on the first reference within functions
- when the regular unsharing algorithm runs. The counterpart is that
- this algorithm must look deeper than for manual unsharing, which is
- specified by LANG_HOOKS_DEEP_UNSHARING.
-
- If there are only few specific cases of node sharing across functions, it is
- probably easier for a front-end to unshare the expressions manually. On the
- contrary, if the expressions generated at the global level are as widespread
- as expressions generated within functions, deep unsharing is very likely the
- way to go. */
-
-/* Similar to copy_tree_r but do not copy SAVE_EXPR or TARGET_EXPR nodes.
+
+
+/* Similar to copy_tree_r() but do not copy SAVE_EXPR or TARGET_EXPR nodes.
These nodes model computations that should only be done once. If we
were to unshare something like SAVE_EXPR(i++), the gimplification
process would create wrong code. */
static tree
mostly_copy_tree_r (tree *tp, int *walk_subtrees, void *data)
{
- tree t = *tp;
- enum tree_code code = TREE_CODE (t);
-
- /* Do not copy SAVE_EXPR or TARGET_EXPR nodes themselves, but copy
- their subtrees if we can make sure to do it only once. */
- if (code == SAVE_EXPR || code == TARGET_EXPR)
- {
- if (data && !pointer_set_insert ((struct pointer_set_t *)data, t))
- ;
- else
- *walk_subtrees = 0;
- }
-
- /* Stop at types, decls, constants like copy_tree_r. */
- else if (TREE_CODE_CLASS (code) == tcc_type
- || TREE_CODE_CLASS (code) == tcc_declaration
- || TREE_CODE_CLASS (code) == tcc_constant
- /* We can't do anything sensible with a BLOCK used as an
- expression, but we also can't just die when we see it
- because of non-expression uses. So we avert our eyes
- and cross our fingers. Silly Java. */
- || code == BLOCK)
+ enum tree_code code = TREE_CODE (*tp);
+ /* Don't unshare types, decls, constants and SAVE_EXPR nodes. */
+ if (TREE_CODE_CLASS (code) == tcc_type
+ || TREE_CODE_CLASS (code) == tcc_declaration
+ || TREE_CODE_CLASS (code) == tcc_constant
+ || code == SAVE_EXPR || code == TARGET_EXPR
+ /* We can't do anything sensible with a BLOCK used as an expression,
+ but we also can't just die when we see it because of non-expression
+ uses. So just avert our eyes and cross our fingers. Silly Java. */
+ || code == BLOCK)
*walk_subtrees = 0;
-
- /* Cope with the statement expression extension. */
- else if (code == STATEMENT_LIST)
- ;
-
- /* Leave the bulk of the work to copy_tree_r itself. */
else
{
gcc_assert (code != BIND_EXPR);
- copy_tree_r (tp, walk_subtrees, NULL);
+ copy_tree_r (tp, walk_subtrees, data);
}
return NULL_TREE;
/* Callback for walk_tree to unshare most of the shared trees rooted at
*TP. If *TP has been visited already (i.e., TREE_VISITED (*TP) == 1),
- then *TP is deep copied by calling mostly_copy_tree_r. */
+ then *TP is deep copied by calling copy_tree_r.
+
+ This unshares the same trees as copy_tree_r with the exception of
+ SAVE_EXPR nodes. These nodes model computations that should only be
+ done once. If we were to unshare something like SAVE_EXPR(i++), the
+ gimplification process would create wrong code. */
static tree
-copy_if_shared_r (tree *tp, int *walk_subtrees, void *data)
+copy_if_shared_r (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED,
+ void *data ATTRIBUTE_UNUSED)
{
tree t = *tp;
enum tree_code code = TREE_CODE (t);
any deeper. */
else if (TREE_VISITED (t))
{
- walk_tree (tp, mostly_copy_tree_r, data, NULL);
+ walk_tree (tp, mostly_copy_tree_r, NULL, NULL);
*walk_subtrees = 0;
}
- /* Otherwise, mark the node as visited and keep looking. */
+ /* Otherwise, mark the tree as visited and keep looking. */
else
TREE_VISITED (t) = 1;
return NULL_TREE;
}
-/* Unshare most of the shared trees rooted at *TP. */
-
-static inline void
-copy_if_shared (tree *tp)
+static tree
+unmark_visited_r (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED,
+ void *data ATTRIBUTE_UNUSED)
{
- /* If the language requires deep unsharing, we need a pointer set to make
- sure we don't repeatedly unshare subtrees of unshareable nodes. */
- struct pointer_set_t *visited
- = lang_hooks.deep_unsharing ? pointer_set_create () : NULL;
- walk_tree (tp, copy_if_shared_r, visited, NULL);
- if (visited)
- pointer_set_destroy (visited);
+ if (TREE_VISITED (*tp))
+ TREE_VISITED (*tp) = 0;
+ else
+ *walk_subtrees = 0;
+
+ return NULL_TREE;
}
/* Unshare all the trees in BODY_P, a pointer into the body of FNDECL, and the
{
struct cgraph_node *cgn = cgraph_node (fndecl);
- copy_if_shared (body_p);
-
+ walk_tree (body_p, copy_if_shared_r, NULL, NULL);
if (body_p == &DECL_SAVED_TREE (fndecl))
for (cgn = cgn->nested; cgn; cgn = cgn->next_nested)
unshare_body (&DECL_SAVED_TREE (cgn->decl), cgn->decl);
}
-/* Callback for walk_tree to unmark the visited trees rooted at *TP.
- Subtrees are walked until the first unvisited node is encountered. */
-
-static tree
-unmark_visited_r (tree *tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED)
-{
- tree t = *tp;
-
- /* If this node has been visited, unmark it and keep looking. */
- if (TREE_VISITED (t))
- TREE_VISITED (t) = 0;
-
- /* Otherwise, don't look any deeper. */
- else
- *walk_subtrees = 0;
-
- return NULL_TREE;
-}
-
-/* Unmark the visited trees rooted at *TP. */
-
-static inline void
-unmark_visited (tree *tp)
-{
- walk_tree (tp, unmark_visited_r, NULL, NULL);
-}
-
/* Likewise, but mark all trees as not visited. */
static void
{
struct cgraph_node *cgn = cgraph_node (fndecl);
- unmark_visited (body_p);
-
+ walk_tree (body_p, unmark_visited_r, NULL, NULL);
if (body_p == &DECL_SAVED_TREE (fndecl))
for (cgn = cgn->nested; cgn; cgn = cgn->next_nested)
unvisit_body (&DECL_SAVED_TREE (cgn->decl), cgn->decl);
hard_function_value generates a PARALLEL, we'll die during normal
expansion of structure assignments; there's special code in expand_return
to handle this case that does not exist in expand_expr. */
- if (!result_decl)
- result = NULL_TREE;
- else if (aggregate_value_p (result_decl, TREE_TYPE (current_function_decl)))
- {
- if (TREE_CODE (DECL_SIZE (result_decl)) != INTEGER_CST)
- {
- if (!TYPE_SIZES_GIMPLIFIED (TREE_TYPE (result_decl)))
- gimplify_type_sizes (TREE_TYPE (result_decl), pre_p);
- /* Note that we don't use gimplify_vla_decl because the RESULT_DECL
- should be effectively allocated by the caller, i.e. all calls to
- this function must be subject to the Return Slot Optimization. */
- gimplify_one_sizepos (&DECL_SIZE (result_decl), pre_p);
- gimplify_one_sizepos (&DECL_SIZE_UNIT (result_decl), pre_p);
- }
- result = result_decl;
- }
+ if (!result_decl
+ || aggregate_value_p (result_decl, TREE_TYPE (current_function_decl)))
+ result = result_decl;
else if (gimplify_ctxp->return_temp)
result = gimplify_ctxp->return_temp;
else
{
- result = create_tmp_reg (TREE_TYPE (result_decl), NULL);
+ result = create_tmp_var (TREE_TYPE (result_decl), NULL);
+ if (TREE_CODE (TREE_TYPE (result)) == COMPLEX_TYPE
+ || TREE_CODE (TREE_TYPE (result)) == VECTOR_TYPE)
+ DECL_GIMPLE_REG_P (result) = 1;
/* ??? With complex control flow (usually involving abnormal edges),
we can wind up warning about an uninitialized value for this. Due
{
tree *p;
VEC(tree,heap) *stack;
- enum gimplify_status ret = GS_ALL_DONE, tret;
+ enum gimplify_status ret = GS_OK, tret;
int i;
location_t loc = EXPR_LOCATION (*expr_p);
- tree expr = *expr_p;
/* Create a stack of the subexpressions so later we can walk them in
order from inner to outer. */
if ((fallback & fb_rvalue) && TREE_CODE (*expr_p) == COMPONENT_REF)
{
canonicalize_component_ref (expr_p);
+ ret = MIN (ret, GS_OK);
}
VEC_free (tree, heap, stack);
- gcc_assert (*expr_p == expr || ret != GS_ALL_DONE);
-
return ret;
}
gimplify_cond_expr (tree *expr_p, gimple_seq *pre_p, fallback_t fallback)
{
tree expr = *expr_p;
- tree type = TREE_TYPE (expr);
- location_t loc = EXPR_LOCATION (expr);
- tree tmp, arm1, arm2;
+ tree tmp, type, arm1, arm2;
enum gimplify_status ret;
tree label_true, label_false, label_cont;
bool have_then_clause_p, have_else_clause_p;
gimple gimple_cond;
enum tree_code pred_code;
gimple_seq seq = NULL;
+ location_t loc = EXPR_LOCATION (*expr_p);
+
+ type = TREE_TYPE (expr);
/* If this COND_EXPR has a value, copy the values into a temporary within
the arms. */
- if (!VOID_TYPE_P (type))
+ if (! VOID_TYPE_P (type))
{
- tree then_ = TREE_OPERAND (expr, 1), else_ = TREE_OPERAND (expr, 2);
tree result;
- /* If either an rvalue is ok or we do not require an lvalue, create the
- temporary. But we cannot do that if the type is addressable. */
- if (((fallback & fb_rvalue) || !(fallback & fb_lvalue))
+ /* If an rvalue is ok or we do not require an lvalue, avoid creating
+ an addressable temporary. */
+ if (((fallback & fb_rvalue)
+ || !(fallback & fb_lvalue))
&& !TREE_ADDRESSABLE (type))
{
if (gimplify_ctxp->allow_rhs_cond_expr
/* If either branch has side effects or could trap, it can't be
evaluated unconditionally. */
- && !TREE_SIDE_EFFECTS (then_)
- && !generic_expr_could_trap_p (then_)
- && !TREE_SIDE_EFFECTS (else_)
- && !generic_expr_could_trap_p (else_))
+ && !TREE_SIDE_EFFECTS (TREE_OPERAND (*expr_p, 1))
+ && !generic_expr_could_trap_p (TREE_OPERAND (*expr_p, 1))
+ && !TREE_SIDE_EFFECTS (TREE_OPERAND (*expr_p, 2))
+ && !generic_expr_could_trap_p (TREE_OPERAND (*expr_p, 2)))
return gimplify_pure_cond_expr (expr_p, pre_p);
- tmp = create_tmp_var (type, "iftmp");
- result = tmp;
+ result = tmp = create_tmp_var (TREE_TYPE (expr), "iftmp");
+ ret = GS_ALL_DONE;
}
-
- /* Otherwise, only create and copy references to the values. */
else
{
- type = build_pointer_type (type);
+ tree type = build_pointer_type (TREE_TYPE (expr));
- if (!VOID_TYPE_P (TREE_TYPE (then_)))
- then_ = build_fold_addr_expr_loc (loc, then_);
+ if (TREE_TYPE (TREE_OPERAND (expr, 1)) != void_type_node)
+ TREE_OPERAND (expr, 1) =
+ build_fold_addr_expr_loc (loc, TREE_OPERAND (expr, 1));
- if (!VOID_TYPE_P (TREE_TYPE (else_)))
- else_ = build_fold_addr_expr_loc (loc, else_);
-
- expr
- = build3 (COND_EXPR, type, TREE_OPERAND (expr, 0), then_, else_);
+ if (TREE_TYPE (TREE_OPERAND (expr, 2)) != void_type_node)
+ TREE_OPERAND (expr, 2) =
+ build_fold_addr_expr_loc (loc, TREE_OPERAND (expr, 2));
tmp = create_tmp_var (type, "iftmp");
+
+ expr = build3 (COND_EXPR, void_type_node, TREE_OPERAND (expr, 0),
+ TREE_OPERAND (expr, 1), TREE_OPERAND (expr, 2));
+
result = build_fold_indirect_ref_loc (loc, tmp);
}
- /* Build the new then clause, `tmp = then_;'. But don't build the
- assignment if the value is void; in C++ it can be if it's a throw. */
- if (!VOID_TYPE_P (TREE_TYPE (then_)))
- TREE_OPERAND (expr, 1) = build2 (MODIFY_EXPR, type, tmp, then_);
+ /* Build the then clause, 't1 = a;'. But don't build an assignment
+ if this branch is void; in C++ it can be, if it's a throw. */
+ if (TREE_TYPE (TREE_OPERAND (expr, 1)) != void_type_node)
+ TREE_OPERAND (expr, 1)
+ = build2 (MODIFY_EXPR, TREE_TYPE (tmp), tmp, TREE_OPERAND (expr, 1));
- /* Similarly, build the new else clause, `tmp = else_;'. */
- if (!VOID_TYPE_P (TREE_TYPE (else_)))
- TREE_OPERAND (expr, 2) = build2 (MODIFY_EXPR, type, tmp, else_);
+ /* Build the else clause, 't1 = b;'. */
+ if (TREE_TYPE (TREE_OPERAND (expr, 2)) != void_type_node)
+ TREE_OPERAND (expr, 2)
+ = build2 (MODIFY_EXPR, TREE_TYPE (tmp), tmp, TREE_OPERAND (expr, 2));
TREE_TYPE (expr) = void_type_node;
recalculate_side_effects (expr);
&& num_nonzero_elements > 1
&& !can_move_by_pieces (size, align))
{
+ tree new_tree;
+
if (notify_temp_creation)
return GS_ERROR;
- walk_tree (&ctor, force_labels_r, NULL, NULL);
- ctor = tree_output_constant_def (ctor);
- if (!useless_type_conversion_p (type, TREE_TYPE (ctor)))
- ctor = build1 (VIEW_CONVERT_EXPR, type, ctor);
- TREE_OPERAND (*expr_p, 1) = ctor;
+ new_tree = create_tmp_var_raw (type, "C");
+
+ gimple_add_tmp_var (new_tree);
+ TREE_STATIC (new_tree) = 1;
+ TREE_READONLY (new_tree) = 1;
+ DECL_INITIAL (new_tree) = ctor;
+ if (align > DECL_ALIGN (new_tree))
+ {
+ DECL_ALIGN (new_tree) = align;
+ DECL_USER_ALIGN (new_tree) = 1;
+ }
+ walk_tree (&DECL_INITIAL (new_tree), force_labels_r, NULL, NULL);
+
+ TREE_OPERAND (*expr_p, 1) = new_tree;
/* This is no longer an assignment of a CONSTRUCTOR, but
we still may have processing to do on the LHS. So
if (notify_temp_creation)
return GS_OK;
- /* If there are nonzero elements and if needed, pre-evaluate to capture
- elements overlapping with the lhs into temporaries. We must do this
- before clearing to fetch the values before they are zeroed-out. */
- if (num_nonzero_elements > 0 && TREE_CODE (*expr_p) != INIT_EXPR)
+ /* If there are nonzero elements, pre-evaluate to capture elements
+ overlapping with the lhs into temporaries. We must do this before
+ clearing to fetch the values before they are zeroed-out. */
+ if (num_nonzero_elements > 0)
{
preeval_data.lhs_base_decl = get_base_address (object);
if (!DECL_P (preeval_data.lhs_base_decl))
gimple_seq *pre_p, gimple_seq *post_p,
bool want_value)
{
- enum gimplify_status ret = GS_UNHANDLED;
- bool changed;
+ enum gimplify_status ret = GS_OK;
- do
- {
- changed = false;
- switch (TREE_CODE (*from_p))
- {
- case VAR_DECL:
- /* If we're assigning from a read-only variable initialized with
- a constructor, do the direct assignment from the constructor,
- but only if neither source nor target are volatile since this
- latter assignment might end up being done on a per-field basis. */
- if (DECL_INITIAL (*from_p)
- && TREE_READONLY (*from_p)
- && !TREE_THIS_VOLATILE (*from_p)
- && !TREE_THIS_VOLATILE (*to_p)
- && TREE_CODE (DECL_INITIAL (*from_p)) == CONSTRUCTOR)
- {
- tree old_from = *from_p;
- enum gimplify_status subret;
-
- /* Move the constructor into the RHS. */
- *from_p = unshare_expr (DECL_INITIAL (*from_p));
-
- /* Let's see if gimplify_init_constructor will need to put
- it in memory. */
- subret = gimplify_init_constructor (expr_p, NULL, NULL,
- false, true);
- if (subret == GS_ERROR)
- {
- /* If so, revert the change. */
- *from_p = old_from;
- }
- else
- {
- ret = GS_OK;
- changed = true;
- }
- }
- break;
- case INDIRECT_REF:
+ while (ret != GS_UNHANDLED)
+ switch (TREE_CODE (*from_p))
+ {
+ case VAR_DECL:
+ /* If we're assigning from a read-only variable initialized with
+ a constructor, do the direct assignment from the constructor,
+ but only if neither source nor target are volatile since this
+ latter assignment might end up being done on a per-field basis. */
+ if (DECL_INITIAL (*from_p)
+ && TREE_READONLY (*from_p)
+ && !TREE_THIS_VOLATILE (*from_p)
+ && !TREE_THIS_VOLATILE (*to_p)
+ && TREE_CODE (DECL_INITIAL (*from_p)) == CONSTRUCTOR)
{
- /* If we have code like
+ tree old_from = *from_p;
- *(const A*)(A*)&x
+ /* Move the constructor into the RHS. */
+ *from_p = unshare_expr (DECL_INITIAL (*from_p));
- where the type of "x" is a (possibly cv-qualified variant
- of "A"), treat the entire expression as identical to "x".
- This kind of code arises in C++ when an object is bound
- to a const reference, and if "x" is a TARGET_EXPR we want
- to take advantage of the optimization below. */
- tree t = gimple_fold_indirect_ref_rhs (TREE_OPERAND (*from_p, 0));
- if (t)
+ /* Let's see if gimplify_init_constructor will need to put
+ it in memory. If so, revert the change. */
+ ret = gimplify_init_constructor (expr_p, NULL, NULL, false, true);
+ if (ret == GS_ERROR)
{
- *from_p = t;
- ret = GS_OK;
- changed = true;
+ *from_p = old_from;
+ /* Fall through. */
}
- break;
- }
-
- case TARGET_EXPR:
- {
- /* If we are initializing something from a TARGET_EXPR, strip the
- TARGET_EXPR and initialize it directly, if possible. This can't
- be done if the initializer is void, since that implies that the
- temporary is set in some non-trivial way.
-
- ??? What about code that pulls out the temp and uses it
- elsewhere? I think that such code never uses the TARGET_EXPR as
- an initializer. If I'm wrong, we'll die because the temp won't
- have any RTL. In that case, I guess we'll need to replace
- references somehow. */
- tree init = TARGET_EXPR_INITIAL (*from_p);
-
- if (init
- && !VOID_TYPE_P (TREE_TYPE (init)))
+ else
{
- *from_p = init;
ret = GS_OK;
- changed = true;
+ break;
}
}
- break;
-
- case COMPOUND_EXPR:
- /* Remove any COMPOUND_EXPR in the RHS so the following cases will be
- caught. */
- gimplify_compound_expr (from_p, pre_p, true);
- ret = GS_OK;
- changed = true;
- break;
+ ret = GS_UNHANDLED;
+ break;
+ case INDIRECT_REF:
+ {
+ /* If we have code like
- case CONSTRUCTOR:
- /* If we're initializing from a CONSTRUCTOR, break this into
- individual MODIFY_EXPRs. */
- return gimplify_init_constructor (expr_p, pre_p, post_p, want_value,
- false);
+ *(const A*)(A*)&x
- case COND_EXPR:
- /* If we're assigning to a non-register type, push the assignment
- down into the branches. This is mandatory for ADDRESSABLE types,
- since we cannot generate temporaries for such, but it saves a
- copy in other cases as well. */
- if (!is_gimple_reg_type (TREE_TYPE (*from_p)))
+ where the type of "x" is a (possibly cv-qualified variant
+ of "A"), treat the entire expression as identical to "x".
+ This kind of code arises in C++ when an object is bound
+ to a const reference, and if "x" is a TARGET_EXPR we want
+ to take advantage of the optimization below. */
+ tree t = gimple_fold_indirect_ref_rhs (TREE_OPERAND (*from_p, 0));
+ if (t)
{
- /* This code should mirror the code in gimplify_cond_expr. */
- enum tree_code code = TREE_CODE (*expr_p);
- tree cond = *from_p;
- tree result = *to_p;
-
- ret = gimplify_expr (&result, pre_p, post_p,
- is_gimple_lvalue, fb_lvalue);
- if (ret != GS_ERROR)
- ret = GS_OK;
-
- if (TREE_TYPE (TREE_OPERAND (cond, 1)) != void_type_node)
- TREE_OPERAND (cond, 1)
- = build2 (code, void_type_node, result,
- TREE_OPERAND (cond, 1));
- if (TREE_TYPE (TREE_OPERAND (cond, 2)) != void_type_node)
- TREE_OPERAND (cond, 2)
- = build2 (code, void_type_node, unshare_expr (result),
- TREE_OPERAND (cond, 2));
-
- TREE_TYPE (cond) = void_type_node;
- recalculate_side_effects (cond);
-
- if (want_value)
- {
- gimplify_and_add (cond, pre_p);
- *expr_p = unshare_expr (result);
- }
- else
- *expr_p = cond;
- return ret;
+ *from_p = t;
+ ret = GS_OK;
}
+ else
+ ret = GS_UNHANDLED;
break;
+ }
- case CALL_EXPR:
- /* For calls that return in memory, give *to_p as the CALL_EXPR's
- return slot so that we don't generate a temporary. */
- if (!CALL_EXPR_RETURN_SLOT_OPT (*from_p)
- && aggregate_value_p (*from_p, *from_p))
+ case TARGET_EXPR:
+ {
+ /* If we are initializing something from a TARGET_EXPR, strip the
+ TARGET_EXPR and initialize it directly, if possible. This can't
+ be done if the initializer is void, since that implies that the
+ temporary is set in some non-trivial way.
+
+ ??? What about code that pulls out the temp and uses it
+ elsewhere? I think that such code never uses the TARGET_EXPR as
+ an initializer. If I'm wrong, we'll die because the temp won't
+ have any RTL. In that case, I guess we'll need to replace
+ references somehow. */
+ tree init = TARGET_EXPR_INITIAL (*from_p);
+
+ if (init
+ && !VOID_TYPE_P (TREE_TYPE (init)))
{
- bool use_target;
-
- if (!(rhs_predicate_for (*to_p))(*from_p))
- /* If we need a temporary, *to_p isn't accurate. */
- use_target = false;
- else if (TREE_CODE (*to_p) == RESULT_DECL
- && DECL_NAME (*to_p) == NULL_TREE
- && needs_to_live_in_memory (*to_p))
- /* It's OK to use the return slot directly unless it's an NRV. */
- use_target = true;
- else if (is_gimple_reg_type (TREE_TYPE (*to_p))
- || (DECL_P (*to_p) && DECL_REGISTER (*to_p)))
- /* Don't force regs into memory. */
- use_target = false;
- else if (TREE_CODE (*expr_p) == INIT_EXPR)
- /* It's OK to use the target directly if it's being
- initialized. */
- use_target = true;
- else if (!is_gimple_non_addressable (*to_p))
- /* Don't use the original target if it's already addressable;
- if its address escapes, and the called function uses the
- NRV optimization, a conforming program could see *to_p
- change before the called function returns; see c++/19317.
- When optimizing, the return_slot pass marks more functions
- as safe after we have escape info. */
- use_target = false;
- else
- use_target = true;
-
- if (use_target)
- {
- CALL_EXPR_RETURN_SLOT_OPT (*from_p) = 1;
- mark_addressable (*to_p);
- }
+ *from_p = init;
+ ret = GS_OK;
}
- break;
+ else
+ ret = GS_UNHANDLED;
+ }
+ break;
- case WITH_SIZE_EXPR:
- /* Likewise for calls that return an aggregate of non-constant size,
- since we would not be able to generate a temporary at all. */
- if (TREE_CODE (TREE_OPERAND (*from_p, 0)) == CALL_EXPR)
- {
- *from_p = TREE_OPERAND (*from_p, 0);
- /* We don't change ret in this case because the
- WITH_SIZE_EXPR might have been added in
- gimplify_modify_expr, so returning GS_OK would lead to an
- infinite loop. */
- changed = true;
- }
- break;
+ case COMPOUND_EXPR:
+ /* Remove any COMPOUND_EXPR in the RHS so the following cases will be
+ caught. */
+ gimplify_compound_expr (from_p, pre_p, true);
+ ret = GS_OK;
+ break;
- /* If we're initializing from a container, push the initialization
- inside it. */
- case CLEANUP_POINT_EXPR:
- case BIND_EXPR:
- case STATEMENT_LIST:
+ case CONSTRUCTOR:
+ /* If we're initializing from a CONSTRUCTOR, break this into
+ individual MODIFY_EXPRs. */
+ return gimplify_init_constructor (expr_p, pre_p, post_p, want_value,
+ false);
+
+ case COND_EXPR:
+ /* If we're assigning to a non-register type, push the assignment
+ down into the branches. This is mandatory for ADDRESSABLE types,
+ since we cannot generate temporaries for such, but it saves a
+ copy in other cases as well. */
+ if (!is_gimple_reg_type (TREE_TYPE (*from_p)))
{
- tree wrap = *from_p;
- tree t;
+ /* This code should mirror the code in gimplify_cond_expr. */
+ enum tree_code code = TREE_CODE (*expr_p);
+ tree cond = *from_p;
+ tree result = *to_p;
- ret = gimplify_expr (to_p, pre_p, post_p, is_gimple_min_lval,
- fb_lvalue);
+ ret = gimplify_expr (&result, pre_p, post_p,
+ is_gimple_lvalue, fb_lvalue);
if (ret != GS_ERROR)
ret = GS_OK;
- t = voidify_wrapper_expr (wrap, *expr_p);
- gcc_assert (t == *expr_p);
+ if (TREE_TYPE (TREE_OPERAND (cond, 1)) != void_type_node)
+ TREE_OPERAND (cond, 1)
+ = build2 (code, void_type_node, result,
+ TREE_OPERAND (cond, 1));
+ if (TREE_TYPE (TREE_OPERAND (cond, 2)) != void_type_node)
+ TREE_OPERAND (cond, 2)
+ = build2 (code, void_type_node, unshare_expr (result),
+ TREE_OPERAND (cond, 2));
+
+ TREE_TYPE (cond) = void_type_node;
+ recalculate_side_effects (cond);
if (want_value)
{
- gimplify_and_add (wrap, pre_p);
- *expr_p = unshare_expr (*to_p);
+ gimplify_and_add (cond, pre_p);
+ *expr_p = unshare_expr (result);
}
else
- *expr_p = wrap;
- return GS_OK;
+ *expr_p = cond;
+ return ret;
}
+ else
+ ret = GS_UNHANDLED;
+ break;
- case COMPOUND_LITERAL_EXPR:
+ case CALL_EXPR:
+ /* For calls that return in memory, give *to_p as the CALL_EXPR's
+ return slot so that we don't generate a temporary. */
+ if (!CALL_EXPR_RETURN_SLOT_OPT (*from_p)
+ && aggregate_value_p (*from_p, *from_p))
{
- tree complit = TREE_OPERAND (*expr_p, 1);
- tree decl_s = COMPOUND_LITERAL_EXPR_DECL_EXPR (complit);
- tree decl = DECL_EXPR_DECL (decl_s);
- tree init = DECL_INITIAL (decl);
-
- /* struct T x = (struct T) { 0, 1, 2 } can be optimized
- into struct T x = { 0, 1, 2 } if the address of the
- compound literal has never been taken. */
- if (!TREE_ADDRESSABLE (complit)
- && !TREE_ADDRESSABLE (decl)
- && init)
+ bool use_target;
+
+ if (!(rhs_predicate_for (*to_p))(*from_p))
+ /* If we need a temporary, *to_p isn't accurate. */
+ use_target = false;
+ else if (TREE_CODE (*to_p) == RESULT_DECL
+ && DECL_NAME (*to_p) == NULL_TREE
+ && needs_to_live_in_memory (*to_p))
+ /* It's OK to use the return slot directly unless it's an NRV. */
+ use_target = true;
+ else if (is_gimple_reg_type (TREE_TYPE (*to_p))
+ || (DECL_P (*to_p) && DECL_REGISTER (*to_p)))
+ /* Don't force regs into memory. */
+ use_target = false;
+ else if (TREE_CODE (*expr_p) == INIT_EXPR)
+ /* It's OK to use the target directly if it's being
+ initialized. */
+ use_target = true;
+ else if (!is_gimple_non_addressable (*to_p))
+ /* Don't use the original target if it's already addressable;
+ if its address escapes, and the called function uses the
+ NRV optimization, a conforming program could see *to_p
+ change before the called function returns; see c++/19317.
+ When optimizing, the return_slot pass marks more functions
+ as safe after we have escape info. */
+ use_target = false;
+ else
+ use_target = true;
+
+ if (use_target)
{
- *expr_p = copy_node (*expr_p);
- TREE_OPERAND (*expr_p, 1) = init;
- return GS_OK;
+ CALL_EXPR_RETURN_SLOT_OPT (*from_p) = 1;
+ mark_addressable (*to_p);
}
}
- default:
- break;
+ ret = GS_UNHANDLED;
+ break;
+
+ case WITH_SIZE_EXPR:
+ /* Likewise for calls that return an aggregate of non-constant size,
+ since we would not be able to generate a temporary at all. */
+ if (TREE_CODE (TREE_OPERAND (*from_p, 0)) == CALL_EXPR)
+ {
+ *from_p = TREE_OPERAND (*from_p, 0);
+ ret = GS_OK;
+ }
+ else
+ ret = GS_UNHANDLED;
+ break;
+
+ /* If we're initializing from a container, push the initialization
+ inside it. */
+ case CLEANUP_POINT_EXPR:
+ case BIND_EXPR:
+ case STATEMENT_LIST:
+ {
+ tree wrap = *from_p;
+ tree t;
+
+ ret = gimplify_expr (to_p, pre_p, post_p, is_gimple_min_lval,
+ fb_lvalue);
+ if (ret != GS_ERROR)
+ ret = GS_OK;
+
+ t = voidify_wrapper_expr (wrap, *expr_p);
+ gcc_assert (t == *expr_p);
+
+ if (want_value)
+ {
+ gimplify_and_add (wrap, pre_p);
+ *expr_p = unshare_expr (*to_p);
+ }
+ else
+ *expr_p = wrap;
+ return GS_OK;
}
- }
- while (changed);
+
+ case COMPOUND_LITERAL_EXPR:
+ {
+ tree complit = TREE_OPERAND (*expr_p, 1);
+ tree decl_s = COMPOUND_LITERAL_EXPR_DECL_EXPR (complit);
+ tree decl = DECL_EXPR_DECL (decl_s);
+ tree init = DECL_INITIAL (decl);
+
+ /* struct T x = (struct T) { 0, 1, 2 } can be optimized
+ into struct T x = { 0, 1, 2 } if the address of the
+ compound literal has never been taken. */
+ if (!TREE_ADDRESSABLE (complit)
+ && !TREE_ADDRESSABLE (decl)
+ && init)
+ {
+ *expr_p = copy_node (*expr_p);
+ TREE_OPERAND (*expr_p, 1) = init;
+ return GS_OK;
+ }
+ }
+
+ default:
+ ret = GS_UNHANDLED;
+ break;
+ }
return ret;
}
splay_tree_insert (ctx->variables, (splay_tree_key)decl, flags);
}
-/* Notice a threadprivate variable DECL used in OpenMP context CTX.
- This just prints out diagnostics about threadprivate variable uses
- in untied tasks. If DECL2 is non-NULL, prevent this warning
- on that variable. */
-
-static bool
-omp_notice_threadprivate_variable (struct gimplify_omp_ctx *ctx, tree decl,
- tree decl2)
-{
- splay_tree_node n;
-
- if (ctx->region_type != ORT_UNTIED_TASK)
- return false;
- n = splay_tree_lookup (ctx->variables, (splay_tree_key)decl);
- if (n == NULL)
- {
- error ("threadprivate variable %qE used in untied task", DECL_NAME (decl));
- error_at (ctx->location, "enclosing task");
- splay_tree_insert (ctx->variables, (splay_tree_key)decl, 0);
- }
- if (decl2)
- splay_tree_insert (ctx->variables, (splay_tree_key)decl2, 0);
- return false;
-}
-
/* Record the fact that DECL was used within the OpenMP context CTX.
IN_CODE is true when real code uses DECL, and false when we should
merely emit default(none) errors. Return true if DECL is going to
if (is_global_var (decl))
{
if (DECL_THREAD_LOCAL_P (decl))
- return omp_notice_threadprivate_variable (ctx, decl, NULL_TREE);
+ return false;
if (DECL_HAS_VALUE_EXPR_P (decl))
{
tree value = get_base_address (DECL_VALUE_EXPR (decl));
if (value && DECL_P (value) && DECL_THREAD_LOCAL_P (value))
- return omp_notice_threadprivate_variable (ctx, decl, value);
+ return false;
}
}
case OMP_CLAUSE_DEFAULT_NONE:
error ("%qE not specified in enclosing parallel",
DECL_NAME (decl));
- if ((ctx->region_type & ORT_TASK) != 0)
- error_at (ctx->location, "enclosing task");
- else
- error_at (ctx->location, "enclosing parallel");
+ error_at (ctx->location, "enclosing parallel");
/* FALLTHRU */
case OMP_CLAUSE_DEFAULT_SHARED:
flags |= GOVD_SHARED;
break;
case OMP_CLAUSE_DEFAULT_UNSPECIFIED:
/* decl will be either GOVD_FIRSTPRIVATE or GOVD_SHARED. */
- gcc_assert ((ctx->region_type & ORT_TASK) != 0);
+ gcc_assert (ctx->region_type == ORT_TASK);
if (ctx->outer_context)
omp_notice_variable (ctx->outer_context, decl, in_code);
for (octx = ctx->outer_context; octx; octx = octx->outer_context)
gimple_seq body = NULL;
struct gimplify_ctx gctx;
- gimplify_scan_omp_clauses (&OMP_TASK_CLAUSES (expr), pre_p,
- find_omp_clause (OMP_TASK_CLAUSES (expr),
- OMP_CLAUSE_UNTIED)
- ? ORT_UNTIED_TASK : ORT_TASK);
+ gimplify_scan_omp_clauses (&OMP_TASK_CLAUSES (expr), pre_p, ORT_TASK);
push_gimplify_context (&gctx);
tree type = TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (addr)));
tree tmp_load;
- tmp_load = create_tmp_reg (type, NULL);
+ tmp_load = create_tmp_var (type, NULL);
+ if (TREE_CODE (type) == COMPLEX_TYPE || TREE_CODE (type) == VECTOR_TYPE)
+ DECL_GIMPLE_REG_P (tmp_load) = 1;
if (goa_stabilize_expr (&rhs, pre_p, addr, tmp_load) < 0)
return GS_ERROR;
else if (ret != GS_UNHANDLED)
break;
- /* Make sure that all the cases set 'ret' appropriately. */
- ret = GS_UNHANDLED;
+ ret = GS_OK;
switch (TREE_CODE (*expr_p))
{
/* First deal with the special cases. */
{
*expr_p = get_initialized_tmp_var (*expr_p, pre_p, post_p);
mark_addressable (*expr_p);
- ret = GS_OK;
}
break;
{
*expr_p = get_initialized_tmp_var (*expr_p, pre_p, post_p);
mark_addressable (*expr_p);
- ret = GS_OK;
}
break;
/* Just strip a conversion to void (or in void context) and
try again. */
*expr_p = TREE_OPERAND (*expr_p, 0);
- ret = GS_OK;
break;
}
case INDIRECT_REF:
*expr_p = fold_indirect_ref_loc (input_location, *expr_p);
if (*expr_p != save_expr)
- {
- ret = GS_OK;
- break;
- }
+ break;
/* else fall through. */
case ALIGN_INDIRECT_REF:
case MISALIGNED_INDIRECT_REF:
if (fallback & fb_lvalue)
ret = GS_ALL_DONE;
else
- {
- *expr_p = DECL_INITIAL (*expr_p);
- ret = GS_OK;
- }
+ *expr_p = DECL_INITIAL (*expr_p);
break;
case DECL_EXPR:
}
gimplify_seq_add_stmt (pre_p,
gimple_build_goto (GOTO_DESTINATION (*expr_p)));
- ret = GS_ALL_DONE;
break;
case PREDICT_EXPR:
append_to_statement_list (ce->value, &temp);
*expr_p = temp;
- ret = temp ? GS_OK : GS_ALL_DONE;
+ ret = GS_OK;
}
/* C99 code may assign to an array in a constructed
structure or union, and this has undefined behavior only
{
*expr_p = get_initialized_tmp_var (*expr_p, pre_p, post_p);
mark_addressable (*expr_p);
- ret = GS_OK;
}
else
ret = GS_ALL_DONE;
gimple_test_f, fallback);
gimplify_expr (&TREE_OPERAND (*expr_p, 1), pre_p, post_p,
is_gimple_val, fb_rvalue);
- ret = GS_ALL_DONE;
}
break;
TREE_TYPE (*expr_p))))
{
*expr_p = tmp;
- ret = GS_OK;
break;
}
/* Convert (void *)&a + 4 into (void *)&a[1]. */
0)))))
{
*expr_p = fold_convert (TREE_TYPE (*expr_p), tmp);
- ret = GS_OK;
break;
}
/* FALLTHRU */
break;
}
- gcc_assert (*expr_p || ret != GS_OK);
+ /* If we replaced *expr_p, gimplify again. */
+ if (ret == GS_OK && (*expr_p == NULL || *expr_p == save_expr))
+ ret = GS_ALL_DONE;
}
while (ret == GS_OK);
}
if (need_temp)
{
- tree temp = create_tmp_reg (TREE_TYPE (lhs), NULL);
+ tree temp = create_tmp_var (TREE_TYPE (lhs), NULL);
+ if (TREE_CODE (TREE_TYPE (lhs)) == COMPLEX_TYPE
+ || TREE_CODE (TREE_TYPE (lhs)) == VECTOR_TYPE)
+ DECL_GIMPLE_REG_P (temp) = 1;
if (TREE_CODE (orig_lhs) == SSA_NAME)
orig_lhs = SSA_NAME_VAR (orig_lhs);