aboutsummaryrefslogtreecommitdiff
path: root/gcc/ada/gcc-interface/trans.c
diff options
context:
space:
mode:
authorEric Botcazou <ebotcazou@adacore.com>2015-11-18 21:55:11 +0000
committerEric Botcazou <ebotcazou@gcc.gnu.org>2015-11-18 21:55:11 +0000
commit642357660ae1f36651519fb41f81a997f5fdae53 (patch)
tree1b06a2606d86dae56336dcdd2fd3e60bee7c93db /gcc/ada/gcc-interface/trans.c
parent5d306e55e09af218e1698a8e427638f717af4f3d (diff)
downloadgcc-642357660ae1f36651519fb41f81a997f5fdae53.zip
gcc-642357660ae1f36651519fb41f81a997f5fdae53.tar.gz
gcc-642357660ae1f36651519fb41f81a997f5fdae53.tar.bz2
ada-tree.h (DECL_INVARIANT_P): New macro.
* gcc-interface/ada-tree.h (DECL_INVARIANT_P): New macro. * gcc-interface/gigi.h (enum standard_datatypes): Remove ADT_longjmp_decl and add ADT_not_handled_by_others_decl. (longjmp_decl): Delete. (not_handled_by_others_decl): New macro. (build_simple_component_ref): Delete. (build_component_ref): Adjust prototype. * gcc-interface/decl.c (gnat_to_gnu_entity): Adjust calls to build_component_ref. (gnat_to_gnu_field): Set DECL_INVARIANT_P on discriminants without default value. * gcc-interface/trans.c (gigi): Reorder initialization sequence and add not_handled_by_others_decl. (Attribute_to_gnu): Adjust calls to build_component_ref. (Subprogram_Body_to_gnu): Likewise. (Call_to_gnu): Likewise. (Exception_Handler_to_gnu_sjlj): Likewise. (gnat_to_gnu): Likewise. (range_check_info_d): Add inserted_cond field. (Loop_Statement_to_gnu): Make two passes on the recorded range checks. (build_noreturn_cond): New static function. (Raise_Error_to_gnu): Record range checks in loops at -O1 and above. (make_invariant): New static function. (Loop_Statement_to_gnu): Use it to compute invariant expressions for the loop bounds if possible, but do not require it if loop unswitching is enabled. * gcc-interface/utils.c (convert_to_fat_pointer): Likewise. (convert): Likewise. (maybe_unconstrained_array): Likewise. Call it instead of build_simple_component_ref and add guard for CONSTRUCTORs. (unchecked_convert): Likewise. * gcc-interface/utils2.c (compare_fat_pointers): Likewise. (build_simple_component_ref): Remove COMPONENT parameter, unify code dealing with VIEW_CONVERT_EXPR and make it more general, remove special treatment for CONSTRUCTORs of template types. (build_component_ref): Remove COMPONENT parameter and adjust call to build_simple_component_ref. (maybe_wrap_malloc): Likewise. (build_allocator): Likewise. (gnat_invariant_expr): Look through overflow checks, deal with addition and subtraction of constants and take into account DECL_INVARIANT_P for the COMPONENT_REF case. From-SVN: r230575
Diffstat (limited to 'gcc/ada/gcc-interface/trans.c')
-rw-r--r--gcc/ada/gcc-interface/trans.c353
1 files changed, 217 insertions, 136 deletions
diff --git a/gcc/ada/gcc-interface/trans.c b/gcc/ada/gcc-interface/trans.c
index 0d10019..5f2c1dc 100644
--- a/gcc/ada/gcc-interface/trans.c
+++ b/gcc/ada/gcc-interface/trans.c
@@ -33,6 +33,7 @@
#include "gimple-expr.h"
#include "stringpool.h"
#include "cgraph.h"
+#include "predict.h"
#include "diagnostic.h"
#include "alias.h"
#include "fold-const.h"
@@ -181,6 +182,7 @@ struct GTY(()) range_check_info_d {
tree high_bound;
tree type;
tree invariant_cond;
+ tree inserted_cond;
};
typedef struct range_check_info_d *range_check_info;
@@ -423,6 +425,8 @@ gigi (Node_Id gnat_root,
= get_identifier ("system__standard_library__exception_data");
/* Make the types and functions used for exception processing. */
+ except_type_node = gnat_to_gnu_type (Base_Type (standard_exception_type));
+
jmpbuf_type
= build_array_type (gnat_type_for_mode (Pmode, 0),
build_index_type (size_int (5)));
@@ -443,6 +447,22 @@ gigi (Node_Id gnat_root,
NULL_TREE),
NULL_TREE, is_disabled, true, true, true, false, NULL, Empty);
+ get_excptr_decl
+ = create_subprog_decl
+ (get_identifier ("system__soft_links__get_gnat_exception"), NULL_TREE,
+ build_function_type_list (build_pointer_type (except_type_node),
+ NULL_TREE),
+ NULL_TREE, is_disabled, true, true, true, false, NULL, Empty);
+
+ not_handled_by_others_decl = get_identifier ("not_handled_by_others");
+ for (t = TYPE_FIELDS (except_type_node); t; t = DECL_CHAIN (t))
+ if (DECL_NAME (t) == not_handled_by_others_decl)
+ {
+ not_handled_by_others_decl = t;
+ break;
+ }
+ gcc_assert (DECL_P (not_handled_by_others_decl));
+
/* setjmp returns an integer and has one operand, which is a pointer to
a jmpbuf. */
setjmp_decl
@@ -464,6 +484,39 @@ gigi (Node_Id gnat_root,
DECL_BUILT_IN_CLASS (update_setjmp_buf_decl) = BUILT_IN_NORMAL;
DECL_FUNCTION_CODE (update_setjmp_buf_decl) = BUILT_IN_UPDATE_SETJMP_BUF;
+ raise_nodefer_decl
+ = create_subprog_decl
+ (get_identifier ("__gnat_raise_nodefer_with_msg"), NULL_TREE,
+ build_function_type_list (void_type_node,
+ build_pointer_type (except_type_node),
+ NULL_TREE),
+ NULL_TREE, is_disabled, true, true, true, false, NULL, Empty);
+
+ /* Indicate that it never returns. */
+ TREE_THIS_VOLATILE (raise_nodefer_decl) = 1;
+ TREE_SIDE_EFFECTS (raise_nodefer_decl) = 1;
+ TREE_TYPE (raise_nodefer_decl)
+ = build_qualified_type (TREE_TYPE (raise_nodefer_decl),
+ TYPE_QUAL_VOLATILE);
+
+ reraise_zcx_decl
+ = create_subprog_decl (get_identifier ("__gnat_reraise_zcx"), NULL_TREE,
+ ftype, NULL_TREE,
+ is_disabled, true, true, true, false,
+ NULL, Empty);
+ /* Indicate that these never return. */
+ TREE_THIS_VOLATILE (reraise_zcx_decl) = 1;
+ TREE_SIDE_EFFECTS (reraise_zcx_decl) = 1;
+ TREE_TYPE (reraise_zcx_decl)
+ = build_qualified_type (TREE_TYPE (reraise_zcx_decl), TYPE_QUAL_VOLATILE);
+
+ set_exception_parameter_decl
+ = create_subprog_decl
+ (get_identifier ("__gnat_set_exception_parameter"), NULL_TREE,
+ build_function_type_list (void_type_node, ptr_type_node, ptr_type_node,
+ NULL_TREE),
+ NULL_TREE, is_disabled, true, true, true, false, NULL, Empty);
+
/* Hooks to call when entering/leaving an exception handler. */
ftype = build_function_type_list (void_type_node, ptr_type_node, NULL_TREE);
@@ -485,16 +538,29 @@ gigi (Node_Id gnat_root,
is_disabled, true, true, true, false,
NULL, Empty);
- reraise_zcx_decl
- = create_subprog_decl (get_identifier ("__gnat_reraise_zcx"), NULL_TREE,
- ftype, NULL_TREE,
- is_disabled, true, true, true, false,
- NULL, Empty);
- /* Indicate that these never return. */
- TREE_THIS_VOLATILE (reraise_zcx_decl) = 1;
- TREE_SIDE_EFFECTS (reraise_zcx_decl) = 1;
- TREE_TYPE (reraise_zcx_decl)
- = build_qualified_type (TREE_TYPE (reraise_zcx_decl), TYPE_QUAL_VOLATILE);
+ /* Dummy objects to materialize "others" and "all others" in the exception
+ tables. These are exported by a-exexpr-gcc.adb, so see this unit for
+ the types to use. */
+ others_decl
+ = create_var_decl (get_identifier ("OTHERS"),
+ get_identifier ("__gnat_others_value"),
+ unsigned_char_type_node, NULL_TREE,
+ true, false, true, false, true, false,
+ NULL, Empty);
+
+ all_others_decl
+ = create_var_decl (get_identifier ("ALL_OTHERS"),
+ get_identifier ("__gnat_all_others_value"),
+ unsigned_char_type_node, NULL_TREE,
+ true, false, true, false, true, false,
+ NULL, Empty);
+
+ unhandled_others_decl
+ = create_var_decl (get_identifier ("UNHANDLED_OTHERS"),
+ get_identifier ("__gnat_unhandled_others_value"),
+ unsigned_char_type_node, NULL_TREE,
+ true, false, true, false, true, false,
+ NULL, Empty);
/* If in no exception handlers mode, all raise statements are redirected to
__gnat_last_chance_handler. No need to redefine raise_nodefer_decl since
@@ -530,39 +596,6 @@ gigi (Node_Id gnat_root,
? exception_range : exception_column);
}
- /* Set the types that GCC and Gigi use from the front end. */
- except_type_node = gnat_to_gnu_type (Base_Type (standard_exception_type));
-
- /* Make other functions used for exception processing. */
- get_excptr_decl
- = create_subprog_decl
- (get_identifier ("system__soft_links__get_gnat_exception"), NULL_TREE,
- build_function_type_list (build_pointer_type (except_type_node),
- NULL_TREE),
- NULL_TREE, is_disabled, true, true, true, false, NULL, Empty);
-
- set_exception_parameter_decl
- = create_subprog_decl
- (get_identifier ("__gnat_set_exception_parameter"), NULL_TREE,
- build_function_type_list (void_type_node, ptr_type_node, ptr_type_node,
- NULL_TREE),
- NULL_TREE, is_disabled, true, true, true, false, NULL, Empty);
-
- raise_nodefer_decl
- = create_subprog_decl
- (get_identifier ("__gnat_raise_nodefer_with_msg"), NULL_TREE,
- build_function_type_list (void_type_node,
- build_pointer_type (except_type_node),
- NULL_TREE),
- NULL_TREE, is_disabled, true, true, true, false, NULL, Empty);
-
- /* Indicate that it never returns. */
- TREE_THIS_VOLATILE (raise_nodefer_decl) = 1;
- TREE_SIDE_EFFECTS (raise_nodefer_decl) = 1;
- TREE_TYPE (raise_nodefer_decl)
- = build_qualified_type (TREE_TYPE (raise_nodefer_decl),
- TYPE_QUAL_VOLATILE);
-
/* Build the special descriptor type and its null node if needed. */
if (TARGET_VTABLE_USES_DESCRIPTORS)
{
@@ -596,30 +629,6 @@ gigi (Node_Id gnat_root,
longest_float_type_node
= get_unpadded_type (Base_Type (standard_long_long_float));
- /* Dummy objects to materialize "others" and "all others" in the exception
- tables. These are exported by a-exexpr-gcc.adb, so see this unit for
- the types to use. */
- others_decl
- = create_var_decl (get_identifier ("OTHERS"),
- get_identifier ("__gnat_others_value"),
- unsigned_char_type_node, NULL_TREE,
- true, false, true, false, true, false,
- NULL, Empty);
-
- all_others_decl
- = create_var_decl (get_identifier ("ALL_OTHERS"),
- get_identifier ("__gnat_all_others_value"),
- unsigned_char_type_node, NULL_TREE,
- true, false, true, false, true, false,
- NULL, Empty);
-
- unhandled_others_decl
- = create_var_decl (get_identifier ("UNHANDLED_OTHERS"),
- get_identifier ("__gnat_unhandled_others_value"),
- unsigned_char_type_node, NULL_TREE,
- true, false, true, false, true, false,
- NULL, Empty);
-
main_identifier_node = get_identifier ("main");
/* Install the builtins we might need, either internally or as
@@ -2450,8 +2459,8 @@ Attribute_to_gnu (Node_Id gnat_node, tree *gnu_result_type_p, int attribute)
gnu_result
= build_compound_expr (gnu_result_type, asm_expr,
- build_component_ref (rec_val, NULL_TREE,
- field, false));
+ build_component_ref (rec_val, field,
+ false));
}
break;
@@ -2718,6 +2727,24 @@ can_be_lower_p (tree val1, tree val2)
return tree_int_cst_lt (val1, val2);
}
+/* Replace EXPR1 and EXPR2 by invariant expressions if possible. Return
+ true if both expressions have been replaced and false otherwise. */
+
+static bool
+make_invariant (tree *expr1, tree *expr2)
+{
+ tree inv_expr1 = gnat_invariant_expr (*expr1);
+ tree inv_expr2 = gnat_invariant_expr (*expr2);
+
+ if (inv_expr1)
+ *expr1 = inv_expr1;
+
+ if (inv_expr2)
+ *expr2 = inv_expr2;
+
+ return inv_expr1 && inv_expr2;
+}
+
/* Helper function for walk_tree, used by independent_iterations_p below. */
static tree
@@ -3082,48 +3109,60 @@ Loop_Statement_to_gnu (Node_Id gnat_node)
the LOOP_STMT to it, finish it and make it the "loop". */
if (Present (gnat_iter_scheme) && No (Condition (gnat_iter_scheme)))
{
- struct range_check_info_d *rci;
- unsigned n_checks = vec_safe_length (gnu_loop_info->checks);
- unsigned int i;
-
- /* First, if we have computed a small number of invariant conditions for
- range checks applied to the iteration variable, then initialize these
- conditions in front of the loop. Otherwise, leave them set to true.
-
- ??? The heuristics need to be improved, by taking into account the
- following datapoints:
- - loop unswitching is disabled for big loops. The cap is the
- parameter PARAM_MAX_UNSWITCH_INSNS (50).
- - loop unswitching can only be applied a small number of times
- to a given loop. The cap is PARAM_MAX_UNSWITCH_LEVEL (3).
- - the front-end quickly generates useless or redundant checks
- that can be entirely optimized away in the end. */
- if (1 <= n_checks && n_checks <= 4)
- FOR_EACH_VEC_ELT (*gnu_loop_info->checks, i, rci)
- {
- tree low_ok
- = rci->low_bound
- ? build_binary_op (GE_EXPR, boolean_type_node,
- convert (rci->type, gnu_low),
- rci->low_bound)
- : boolean_true_node;
-
- tree high_ok
- = rci->high_bound
- ? build_binary_op (LE_EXPR, boolean_type_node,
- convert (rci->type, gnu_high),
- rci->high_bound)
- : boolean_true_node;
-
- tree range_ok
- = build_binary_op (TRUTH_ANDIF_EXPR, boolean_type_node,
- low_ok, high_ok);
-
- TREE_OPERAND (rci->invariant_cond, 0)
- = build_unary_op (TRUTH_NOT_EXPR, boolean_type_node, range_ok);
-
- add_stmt_with_node_force (rci->invariant_cond, gnat_node);
- }
+ /* First, if we have computed invariant conditions for range (or index)
+ checks applied to the iteration variable, find out whether they can
+ be evaluated to false at compile time; otherwise, if there are not
+ too many of them, combine them with the original checks. If loop
+ unswitching is enabled, do not require the loop bounds to be also
+ invariant, as their evaluation will still be ahead of the loop. */
+ if (vec_safe_length (gnu_loop_info->checks) > 0
+ && (make_invariant (&gnu_low, &gnu_high) || flag_unswitch_loops))
+ {
+ struct range_check_info_d *rci;
+ unsigned int i, n_remaining_checks = 0;
+
+ FOR_EACH_VEC_ELT (*gnu_loop_info->checks, i, rci)
+ {
+ tree low_ok
+ = rci->low_bound
+ ? build_binary_op (GE_EXPR, boolean_type_node,
+ convert (rci->type, gnu_low),
+ rci->low_bound)
+ : boolean_true_node;
+
+ tree high_ok
+ = rci->high_bound
+ ? build_binary_op (LE_EXPR, boolean_type_node,
+ convert (rci->type, gnu_high),
+ rci->high_bound)
+ : boolean_true_node;
+
+ tree range_ok
+ = build_binary_op (TRUTH_ANDIF_EXPR, boolean_type_node,
+ low_ok, high_ok);
+
+ rci->invariant_cond
+ = build_unary_op (TRUTH_NOT_EXPR, boolean_type_node, range_ok);
+
+ if (rci->invariant_cond == boolean_false_node)
+ TREE_OPERAND (rci->inserted_cond, 0) = rci->invariant_cond;
+ else
+ n_remaining_checks++;
+ }
+
+ /* Note that loop unswitching can only be applied a small number of
+ times to a given loop (PARAM_MAX_UNSWITCH_LEVEL default to 3). */
+ if (0 < n_remaining_checks && n_remaining_checks <= 3
+ && optimize > 1 && !optimize_size)
+ FOR_EACH_VEC_ELT (*gnu_loop_info->checks, i, rci)
+ if (rci->invariant_cond != boolean_false_node)
+ {
+ TREE_OPERAND (rci->inserted_cond, 0) = rci->invariant_cond;
+
+ if (flag_unswitch_loops)
+ add_stmt_with_node_force (rci->inserted_cond, gnat_node);
+ }
+ }
/* Second, if loop vectorization is enabled and the iterations of the
loop can easily be proved as independent, mark the loop. */
@@ -3865,8 +3904,7 @@ Subprogram_Body_to_gnu (Node_Id gnat_node)
for (t = TREE_CHAIN (gnu_cico_list); t; t = TREE_CHAIN (t))
{
tree gnu_field_deref
- = build_component_ref (gnu_ret_deref, NULL_TREE,
- TREE_PURPOSE (t), true);
+ = build_component_ref (gnu_ret_deref, TREE_PURPOSE (t), true);
gnu_result = build2 (MODIFY_EXPR, void_type_node,
gnu_field_deref, TREE_VALUE (t));
add_stmt_with_node (gnu_result, gnat_end_label);
@@ -4698,8 +4736,8 @@ Call_to_gnu (Node_Id gnat_node, tree *gnu_result_type_p, tree gnu_target,
tree gnu_result
= length == 1
? gnu_call
- : build_component_ref (gnu_call, NULL_TREE,
- TREE_PURPOSE (gnu_cico_list), false);
+ : build_component_ref (gnu_call, TREE_PURPOSE (gnu_cico_list),
+ false);
/* If the actual is a conversion, get the inner expression, which
will be the real destination, and convert the result to the
@@ -4786,8 +4824,8 @@ Call_to_gnu (Node_Id gnat_node, tree *gnu_result_type_p, tree gnu_target,
if (TYPE_CI_CO_LIST (gnu_subprog_type))
{
tree gnu_elmt = TYPE_CI_CO_LIST (gnu_subprog_type);
- gnu_call = build_component_ref (gnu_call, NULL_TREE,
- TREE_PURPOSE (gnu_elmt), false);
+ gnu_call
+ = build_component_ref (gnu_call, TREE_PURPOSE (gnu_elmt), false);
gnu_result_type = TREE_TYPE (gnu_call);
}
@@ -5142,7 +5180,7 @@ Exception_Handler_to_gnu_sjlj (Node_Id gnat_node)
(build_unary_op
(INDIRECT_REF, NULL_TREE,
gnu_except_ptr_stack->last ()),
- get_identifier ("not_handled_by_others"), NULL_TREE,
+ not_handled_by_others_decl,
false)),
integer_zero_node);
}
@@ -5396,6 +5434,31 @@ Compilation_Unit_to_gnu (Node_Id gnat_node)
process_deferred_decl_context (true);
}
+/* Mark COND, a boolean expression, as predicating a call to a noreturn
+ function, i.e. predict that it is very likely false, and return it.
+
+ The compiler will automatically predict the last edge leading to a call
+ to a noreturn function as very unlikely taken. This function makes it
+ possible to expand the prediction to predecessors in case the condition
+ is made up of several short-circuit operators. */
+
+static tree
+build_noreturn_cond (tree cond)
+{
+ tree fn = builtin_decl_explicit (BUILT_IN_EXPECT);
+ tree arg_types = TYPE_ARG_TYPES (TREE_TYPE (fn));
+ tree pred_type = TREE_VALUE (arg_types);
+ tree expected_type = TREE_VALUE (TREE_CHAIN (arg_types));
+
+ tree t = build_call_expr (fn, 3,
+ fold_convert (pred_type, cond),
+ build_int_cst (expected_type, 0),
+ build_int_cst (integer_type_node,
+ PRED_NORETURN));
+
+ return build1 (NOP_EXPR, boolean_type_node, t);
+}
+
/* Subroutine of gnat_to_gnu to translate gnat_node, an N_Raise_xxx_Error,
to a GCC tree, which is returned. GNU_RESULT_TYPE_P is a pointer to where
we should place the result type. LABEL_P is true if there is a label to
@@ -5467,18 +5530,29 @@ Raise_Error_to_gnu (Node_Id gnat_node, tree *gnu_result_type_p)
= build_call_raise_range (reason, gnat_node, gnu_index,
gnu_low_bound, gnu_high_bound);
- /* If loop unswitching is enabled, we try to compute invariant
- conditions for checks applied to iteration variables, i.e.
- conditions that are both independent of the variable and
- necessary in order for the check to fail in the course of
- some iteration, and prepend them to the original condition
- of the checks. This will make it possible later for the
- loop unswitching pass to replace the loop with two loops,
- one of which has the checks eliminated and the other has
- the original checks reinstated, and a run time selection.
- The former loop will be suitable for vectorization. */
+ /* If optimization is enabled and we are inside a loop, we try to
+ compute invariant conditions for checks applied to the iteration
+ variable, i.e. conditions that are independent of the variable
+ and necessary in order for the checks to fail in the course of
+ some iteration. If we succeed, we consider an alternative:
+
+ 1. If loop unswitching is enabled, we prepend these conditions
+ to the original conditions of the checks. This will make it
+ possible for the loop unswitching pass to replace the loop
+ with two loops, one of which has the checks eliminated and
+ the other has the original checks reinstated, and a prologue
+ implementing a run-time selection. The former loop will be
+ for example suitable for vectorization.
+
+ 2. Otherwise, we instead append the conditions to the original
+ conditions of the checks. At worse, if the conditions cannot
+ be evaluated at compile time, they will be evaluated as true
+ at run time only when the checks have already failed, thus
+ contributing negatively only to the size of the executable.
+ But the hope is that these invariant conditions be evaluated
+ at compile time to false, thus taking away the entire checks
+ with them. */
if (optimize
- && flag_unswitch_loops
&& inside_loop_p ()
&& (!gnu_low_bound
|| (gnu_low_bound = gnat_invariant_expr (gnu_low_bound)))
@@ -5490,14 +5564,21 @@ Raise_Error_to_gnu (Node_Id gnat_node, tree *gnu_result_type_p)
rci->low_bound = gnu_low_bound;
rci->high_bound = gnu_high_bound;
rci->type = get_unpadded_type (gnat_type);
- rci->invariant_cond = build1 (SAVE_EXPR, boolean_type_node,
- boolean_true_node);
+ rci->inserted_cond
+ = build1 (SAVE_EXPR, boolean_type_node, boolean_true_node);
vec_safe_push (loop->checks, rci);
loop->has_checks = true;
- gnu_cond = build_binary_op (TRUTH_ANDIF_EXPR,
- boolean_type_node,
- rci->invariant_cond,
- gnat_to_gnu (gnat_cond));
+ gnu_cond = build_noreturn_cond (gnat_to_gnu (gnat_cond));
+ if (flag_unswitch_loops)
+ gnu_cond = build_binary_op (TRUTH_ANDIF_EXPR,
+ boolean_type_node,
+ rci->inserted_cond,
+ gnu_cond);
+ else
+ gnu_cond = build_binary_op (TRUTH_ANDIF_EXPR,
+ boolean_type_node,
+ gnu_cond,
+ rci->inserted_cond);
}
/* Or else, if aggressive loop optimizations are enabled, we just
@@ -6256,7 +6337,7 @@ gnat_to_gnu (Node_Id gnat_node)
gnu_field = gnat_to_gnu_field_decl (gnat_field);
gnu_result
- = build_component_ref (gnu_prefix, NULL_TREE, gnu_field,
+ = build_component_ref (gnu_prefix, gnu_field,
(Nkind (Parent (gnat_node))
== N_Attribute_Reference)
&& lvalue_required_for_attribute_p