aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Botcazou <ebotcazou@adacore.com>2021-04-28 09:43:02 +0200
committerEric Botcazou <ebotcazou@adacore.com>2021-04-28 09:46:22 +0200
commit852dd866e2faba95cb407c98d31a48b6aae66677 (patch)
tree5fdf04f62c9004fc0406ef82636e0f496ff35fab
parentd91e7eab3a2c3957c2220ad71e62d9fc78cccb9b (diff)
downloadgcc-852dd866e2faba95cb407c98d31a48b6aae66677.zip
gcc-852dd866e2faba95cb407c98d31a48b6aae66677.tar.gz
gcc-852dd866e2faba95cb407c98d31a48b6aae66677.tar.bz2
Fix loss of optimization of array iteration due to inlining
This helps loop-invariant motion to hoist complicated offset computations. gcc/ada/ * gcc-interface/trans.c (language_function): Add comment. (loop_info_d): Add fndecl and invariants fields. (find_loop_for): Test fndecl instead of the context of var. (find_loop): New function. (Regular_Loop_to_gnu): Fold back into... (Loop_Statement_to_gnu): ...this. Emit invariants on entry, if any. (gnat_to_gnu) <N_Selected_Component>: Record nonconstant invariant offset computations in loops when optimization is enabled. * gcc-interface/utils2.c (gnat_invariant_expr): Handle BIT_AND_EXPR. gcc/testsuite/ * gnat.dg/opt93.ads, gnat.dg/opt93.adb: New test.
-rw-r--r--gcc/ada/gcc-interface/trans.c134
-rw-r--r--gcc/ada/gcc-interface/utils2.c11
-rw-r--r--gcc/testsuite/gnat.dg/opt93.adb20
-rw-r--r--gcc/testsuite/gnat.dg/opt93.ads14
4 files changed, 121 insertions, 58 deletions
diff --git a/gcc/ada/gcc-interface/trans.c b/gcc/ada/gcc-interface/trans.c
index 5a55ca4..4e533ce 100644
--- a/gcc/ada/gcc-interface/trans.c
+++ b/gcc/ada/gcc-interface/trans.c
@@ -112,7 +112,7 @@ struct GTY (()) parm_attr_d {
typedef struct parm_attr_d *parm_attr;
-
+/* Structure used to record information for a function. */
struct GTY(()) language_function {
vec<parm_attr, va_gc> *parm_attr_cache;
bitmap named_ret_val;
@@ -194,9 +194,9 @@ struct GTY(()) range_check_info_d {
typedef struct range_check_info_d *range_check_info;
-
/* Structure used to record information for a loop. */
struct GTY(()) loop_info_d {
+ tree fndecl;
tree stmt;
tree loop_var;
tree low_bound;
@@ -205,11 +205,11 @@ struct GTY(()) loop_info_d {
tree omp_construct_clauses;
enum tree_code omp_code;
vec<range_check_info, va_gc> *checks;
+ vec<tree, va_gc> *invariants;
};
typedef struct loop_info_d *loop_info;
-
/* Stack of loop_info structures associated with LOOP_STMT nodes. */
static GTY(()) vec<loop_info, va_gc> *gnu_loop_stack;
@@ -2768,13 +2768,27 @@ find_loop_for (tree expr, tree *disp, bool *neg_p)
if (TREE_CODE (var) != VAR_DECL)
return NULL;
- if (decl_function_context (var) != current_function_decl)
- return NULL;
+ gcc_checking_assert (vec_safe_length (gnu_loop_stack) > 0);
+
+ FOR_EACH_VEC_ELT_REVERSE (*gnu_loop_stack, i, iter)
+ if (iter->loop_var == var && iter->fndecl == current_function_decl)
+ break;
+
+ return iter;
+}
- gcc_assert (vec_safe_length (gnu_loop_stack) > 0);
+/* Return the innermost enclosing loop in the current function. */
+
+static struct loop_info_d *
+find_loop (void)
+{
+ struct loop_info_d *iter = NULL;
+ unsigned int i;
+
+ gcc_checking_assert (vec_safe_length (gnu_loop_stack) > 0);
FOR_EACH_VEC_ELT_REVERSE (*gnu_loop_stack, i, iter)
- if (var == iter->loop_var)
+ if (iter->fndecl == current_function_decl)
break;
return iter;
@@ -2924,26 +2938,30 @@ independent_iterations_p (tree stmt_list)
return true;
}
-/* Helper for Loop_Statement_to_gnu, to translate the body of a loop not
- subject to any sort of parallelization directive or restriction, designated
- by GNAT_NODE.
-
- We expect the top of gnu_loop_stack to hold a pointer to the loop info
- setup for the translation, which holds a pointer to the initial gnu loop
- stmt node. We return the new gnu loop statement to use.
-
- We might also set *GNU_COND_EXPR_P to request a variant of the translation
- scheme in Loop_Statement_to_gnu. */
+/* Subroutine of gnat_to_gnu to translate gnat_node, an N_Loop_Statement,
+ to a GCC tree, which is returned. */
static tree
-Regular_Loop_to_gnu (Node_Id gnat_node, tree *gnu_cond_expr_p)
+Loop_Statement_to_gnu (Node_Id gnat_node)
{
const Node_Id gnat_iter_scheme = Iteration_Scheme (gnat_node);
- struct loop_info_d *const gnu_loop_info = gnu_loop_stack->last ();
- tree gnu_loop_stmt = gnu_loop_info->stmt;
- tree gnu_loop_label = LOOP_STMT_LABEL (gnu_loop_stmt);
- tree gnu_cond_expr = *gnu_cond_expr_p;
- tree gnu_low = NULL_TREE, gnu_high = NULL_TREE;
+ struct loop_info_d *gnu_loop_info = ggc_cleared_alloc<loop_info_d> ();
+ tree gnu_loop_stmt = build4 (LOOP_STMT, void_type_node, NULL_TREE,
+ NULL_TREE, NULL_TREE, NULL_TREE);
+ tree gnu_loop_label = create_artificial_label (input_location);
+ tree gnu_cond_expr = NULL_TREE, gnu_low = NULL_TREE, gnu_high = NULL_TREE;
+ tree gnu_result;
+
+ /* Push the loop_info structure associated with the LOOP_STMT. */
+ gnu_loop_info->fndecl = current_function_decl;
+ gnu_loop_info->stmt = gnu_loop_stmt;
+ vec_safe_push (gnu_loop_stack, gnu_loop_info);
+
+ /* Set location information for statement and end label. */
+ set_expr_location_from_node (gnu_loop_stmt, gnat_node);
+ Sloc_to_locus (Sloc (End_Label (gnat_node)),
+ &DECL_SOURCE_LOCATION (gnu_loop_label));
+ LOOP_STMT_LABEL (gnu_loop_stmt) = gnu_loop_label;
/* Set the condition under which the loop must keep going. If we have an
explicit condition, use it to set the location information throughout
@@ -3277,7 +3295,16 @@ Regular_Loop_to_gnu (Node_Id gnat_node, tree *gnu_cond_expr_p)
}
}
- /* Second, if loop vectorization is enabled and the iterations of the
+ /* Second, if we have recorded invariants to be hoisted, emit them. */
+ if (vec_safe_length (gnu_loop_info->invariants) > 0)
+ {
+ tree *iter;
+ unsigned int i;
+ FOR_EACH_VEC_ELT (*gnu_loop_info->invariants, i, iter)
+ add_stmt_with_node_force (*iter, gnat_node);
+ }
+
+ /* Third, if loop vectorization is enabled and the iterations of the
loop can easily be proved as independent, mark the loop. */
if (optimize >= 3
&& independent_iterations_p (LOOP_STMT_BODY (gnu_loop_stmt)))
@@ -3288,40 +3315,6 @@ Regular_Loop_to_gnu (Node_Id gnat_node, tree *gnu_cond_expr_p)
gnu_loop_stmt = end_stmt_group ();
}
- *gnu_cond_expr_p = gnu_cond_expr;
-
- return gnu_loop_stmt;
-}
-
-/* Subroutine of gnat_to_gnu to translate gnat_node, an N_Loop_Statement,
- to a GCC tree, which is returned. */
-
-static tree
-Loop_Statement_to_gnu (Node_Id gnat_node)
-{
- struct loop_info_d *gnu_loop_info = ggc_cleared_alloc<loop_info_d> ();
-
- tree gnu_loop_stmt = build4 (LOOP_STMT, void_type_node, NULL_TREE,
- NULL_TREE, NULL_TREE, NULL_TREE);
- tree gnu_cond_expr = NULL_TREE;
- tree gnu_loop_label = create_artificial_label (input_location);
- tree gnu_result;
-
- /* Push the loop_info structure associated with the LOOP_STMT. */
- vec_safe_push (gnu_loop_stack, gnu_loop_info);
-
- /* Set location information for statement and end label. */
- set_expr_location_from_node (gnu_loop_stmt, gnat_node);
- Sloc_to_locus (Sloc (End_Label (gnat_node)),
- &DECL_SOURCE_LOCATION (gnu_loop_label));
- LOOP_STMT_LABEL (gnu_loop_stmt) = gnu_loop_label;
-
- /* Save the statement for later reuse. */
- gnu_loop_info->stmt = gnu_loop_stmt;
-
- /* Perform the core loop body translation. */
- gnu_loop_stmt = Regular_Loop_to_gnu (gnat_node, &gnu_cond_expr);
-
/* If we have an outer COND_EXPR, that's our result and this loop is its
"true" statement. Otherwise, the result is the LOOP_STMT. */
if (gnu_cond_expr)
@@ -6731,6 +6724,8 @@ gnat_to_gnu (Node_Id gnat_node)
else
{
tree gnu_field = gnat_to_gnu_field_decl (gnat_field);
+ tree gnu_offset;
+ struct loop_info_d *loop;
gnu_result
= build_component_ref (gnu_prefix, gnu_field,
@@ -6738,6 +6733,29 @@ gnat_to_gnu (Node_Id gnat_node)
== N_Attribute_Reference)
&& lvalue_required_for_attribute_p
(Parent (gnat_node)));
+
+ /* If optimization is enabled and we are inside a loop, we try to
+ hoist nonconstant but invariant offset computations outside of
+ the loop, since they very likely contain loads that could turn
+ out to be hard to move if they end up in active EH regions. */
+ if (optimize
+ && inside_loop_p ()
+ && TREE_CODE (gnu_result) == COMPONENT_REF
+ && (gnu_offset = component_ref_field_offset (gnu_result))
+ && !TREE_CONSTANT (gnu_offset)
+ && (gnu_offset = gnat_invariant_expr (gnu_offset))
+ && (loop = find_loop ()))
+ {
+ tree invariant
+ = build1 (SAVE_EXPR, TREE_TYPE (gnu_offset), gnu_offset);
+ vec_safe_push (loop->invariants, invariant);
+ tree field = TREE_OPERAND (gnu_result, 1);
+ tree factor
+ = size_int (DECL_OFFSET_ALIGN (field) / BITS_PER_UNIT);
+ /* Divide the offset by its alignment. */
+ TREE_OPERAND (gnu_result, 2)
+ = size_binop (EXACT_DIV_EXPR, invariant, factor);
+ }
}
gnu_result_type = get_unpadded_type (Etype (gnat_node));
diff --git a/gcc/ada/gcc-interface/utils2.c b/gcc/ada/gcc-interface/utils2.c
index 316033b..83cc794 100644
--- a/gcc/ada/gcc-interface/utils2.c
+++ b/gcc/ada/gcc-interface/utils2.c
@@ -2946,6 +2946,17 @@ gnat_invariant_expr (tree expr)
if (TREE_CONSTANT (expr))
return fold_convert (type, expr);
+ /* Deal with aligning patterns. */
+ if (TREE_CODE (expr) == BIT_AND_EXPR
+ && TREE_CONSTANT (TREE_OPERAND (expr, 1)))
+ {
+ tree op0 = gnat_invariant_expr (TREE_OPERAND (expr, 0));
+ if (op0)
+ return fold_build2 (BIT_AND_EXPR, type, op0, TREE_OPERAND (expr, 1));
+ else
+ return NULL_TREE;
+ }
+
/* Deal with addition or subtraction of constants. */
if (is_simple_additive_expression (expr, &add, &cst, &minus_p))
{
diff --git a/gcc/testsuite/gnat.dg/opt93.adb b/gcc/testsuite/gnat.dg/opt93.adb
new file mode 100644
index 0000000..5c82ad2
--- /dev/null
+++ b/gcc/testsuite/gnat.dg/opt93.adb
@@ -0,0 +1,20 @@
+-- { dg-do compile }
+-- { dg-options "-O2 -fdump-tree-optimized" }
+
+package body Opt93 is
+
+ function Worker (Obj : T) return Boolean is
+ begin
+ return (for some J in 1 .. Obj.D2 => Obj.A (J) = 0);
+ end;
+
+ function Contains_Zero (Obj : T) return Boolean is
+ begin
+ return Worker (Obj);
+ exception
+ when Others => raise Program_Error;
+ end;
+
+end Opt93;
+
+-- { dg-final { scan-tree-dump "ivtmp.\[0-9_]+ = ivtmp.\[0-9_]+ \\+ 2" "optimized" } }
diff --git a/gcc/testsuite/gnat.dg/opt93.ads b/gcc/testsuite/gnat.dg/opt93.ads
new file mode 100644
index 0000000..61bc236
--- /dev/null
+++ b/gcc/testsuite/gnat.dg/opt93.ads
@@ -0,0 +1,14 @@
+package Opt93 is
+
+ type Arr is array (Natural range <>) of Short_Integer;
+
+ type Rec (D1, D2 : Natural) is record
+ S : String (1 .. D1);
+ A : Arr (1 .. D2);
+ end record;
+
+ type T is access Rec;
+
+ function Contains_Zero (Obj : T) return Boolean;
+
+end Opt93;