2020-09-08 Sandra Loosemore <san...@codesourcery.com>
gcc/c-family/
* c-common.h (c_oacc_annotate_loops_in_kernels_regions): Declare.
* c-omp.c: Include tree-iterator.h.
(enum annotation_state): New.
(struct annotation_info): New.
(do_not_annotate_loop): New.
(do_not_annotate_loop_nest): New.
(annotation_error): New.
(c_finish_omp_for_internal): New.
(c_finish_omp_for): Use c_finish_omp_for_internal.
(is_local_var): New.
(end_test_ok_for_annotation_r): New.
(end_test_ok_for_annotation): New.
(lang_specific_unwrap_initializer): New.
(annotate_for_loop): New.
(annotate_and_check_for_loop): New.
(annotate_loops_in_kernels_regions): New.
(c_oacc_annotate_loops_in_kernels_regions): New.
* c.opt (Wopenacc-kernels-annotate-loops): New.
(fopenacc-kernels-annotate-loops): New.
gcc/c/
* c-decl.c (c_unwrap_for_init): New.
(finish_function): Call c_oacc_annotate_loops_in_kernels_regions.
* c-parser.c (c_parser_oacc_loop): Set OACC_LOOP_COMBINED.
gcc/cp/
* decl.c (cp_unwrap_for_init): New.
(finish_function): Call c_oacc_annotate_loops_in_kernels_regions.
* parser.c (cp_parser_oacc_loop): Set OACC_LOOP_COMBINED.
* semantics.c (handle_omp_array_sections_1): Call STRIP_NOPS
on length and bound.
(handle_omp_array_sections): Likewise.
gcc/
* doc/invoke.texi (Option Summary): Add entries for
-Wopenacc-kernels-annotate-loops and
-fno-openacc-kernels-annotate-loops.
(Warning Options): Document -Wopenacc-kernels-annotate-loops.
(Optimization Options): Document
-fno-openacc-kernels-annotate-loops.
* tree.h (OACC_LOOP_COMBINED): New.
gcc/testsuite/
* c-c++-common/goacc/classify-kernels-unparallelized.c: Add
-fno-openacc-kernels-annotate-loops option.
* c-c++-common/goacc/classify-kernels.c: Likewise.
* c-c++-common/goacc/combined-directives.c: Likewise.
* c-c++-common/goacc/kernels-counter-var-redundant-load.c:
Likewise.
* c-c++-common/goacc/kernels-counter-vars-function-scope.c:
Likewise.
* c-c++-common/goacc/kernels-double-reduction-n.c: Likewise.
* c-c++-common/goacc/kernels-double-reduction.c: Likewise.
* c-c++-common/goacc/kernels-loop-2.c: Likewise.
* c-c++-common/goacc/kernels-loop-3.c: Likewise.
* c-c++-common/goacc/kernels-loop-data-2.c: Likewise.
* c-c++-common/goacc/kernels-loop-data-enter-exit-2.c: Likewise.
* c-c++-common/goacc/kernels-loop-data-enter-exit.c: Likewise.
* c-c++-common/goacc/kernels-loop-data-update.c: Likewise.
* c-c++-common/goacc/kernels-loop-data.c: Likewise.
* c-c++-common/goacc/kernels-loop-g.c: Likewise.
* c-c++-common/goacc/kernels-loop-mod-not-zero.c: Likewise.
* c-c++-common/goacc/kernels-loop-n.c: Likewise.
* c-c++-common/goacc/kernels-loop-nest.c: Likewise.
* c-c++-common/goacc/kernels-loop.c: Likewise.
* c-c++-common/goacc/kernels-one-counter-var.c: Likewise.
* c-c++-common/goacc/kernels-parallel-loop-data-enter-exit.c:
Likewise.
* c-c++-common/goacc/kernels-reduction.c: Likewise.
* c-c++-common/goacc/kernels-loop-annotation-1.c: New.
* c-c++-common/goacc/kernels-loop-annotation-2.c: New.
* c-c++-common/goacc/kernels-loop-annotation-3.c: New.
* c-c++-common/goacc/kernels-loop-annotation-4.c: New.
* c-c++-common/goacc/kernels-loop-annotation-5.c: New.
* c-c++-common/goacc/kernels-loop-annotation-6.c: New.
* c-c++-common/goacc/kernels-loop-annotation-7.c: New.
* c-c++-common/goacc/kernels-loop-annotation-8.c: New.
* c-c++-common/goacc/kernels-loop-annotation-9.c: New.
* c-c++-common/goacc/kernels-loop-annotation-10.c: New.
* c-c++-common/goacc/kernels-loop-annotation-11.c: New.
* c-c++-common/goacc/kernels-loop-annotation-12.c: New.
* c-c++-common/goacc/kernels-loop-annotation-13.c: New.
* c-c++-common/goacc/kernels-loop-annotation-14.c: New.
* c-c++-common/goacc/kernels-loop-annotation-15.c: New.
* c-c++-common/goacc/kernels-loop-annotation-16.c: New.
* c-c++-common/goacc/kernels-loop-annotation-17.c: New.
* c-c++-common/goacc/kernels-loop-annotation-18.c: New.
* c-c++-common/goacc/kernels-loop-annotation-19.c: New.
* c-c++-common/goacc/kernels-loop-annotation-20.c: New.
* c-c++-common/goacc/kernels-loop-annotation-21.c: New.
* c-c++-common/goacc/kernels-loop-annotation-22.c: New.
---
gcc/c-family/c-common.h | 1 +
gcc/c-family/c-omp.c | 916 +++++++++++++++++++--
gcc/c-family/c.opt | 8 +
gcc/c/c-decl.c | 28 +
gcc/c/c-parser.c | 3 +
gcc/cp/decl.c | 44 +
gcc/cp/parser.c | 3 +
gcc/cp/semantics.c | 9 +
gcc/doc/invoke.texi | 34 +-
.../goacc/classify-kernels-unparallelized.c | 1 +
.../c-c++-common/goacc/classify-kernels.c | 1 +
.../c-c++-common/goacc/combined-directives.c | 2 +-
.../goacc/kernels-counter-var-redundant-load.c | 1 +
.../goacc/kernels-counter-vars-function-scope.c | 1 +
.../goacc/kernels-double-reduction-n.c | 1 +
.../c-c++-common/goacc/kernels-double-reduction.c | 1 +
gcc/testsuite/c-c++-common/goacc/kernels-loop-2.c | 1 +
gcc/testsuite/c-c++-common/goacc/kernels-loop-3.c | 1 +
.../c-c++-common/goacc/kernels-loop-annotation-1.c | 26 +
.../goacc/kernels-loop-annotation-10.c | 32 +
.../goacc/kernels-loop-annotation-11.c | 27 +
.../goacc/kernels-loop-annotation-12.c | 28 +
.../goacc/kernels-loop-annotation-13.c | 27 +
.../goacc/kernels-loop-annotation-14.c | 22 +
.../goacc/kernels-loop-annotation-15.c | 22 +
.../goacc/kernels-loop-annotation-16.c | 26 +
.../goacc/kernels-loop-annotation-17.c | 26 +
.../goacc/kernels-loop-annotation-18.c | 18 +
.../goacc/kernels-loop-annotation-19.c | 19 +
.../c-c++-common/goacc/kernels-loop-annotation-2.c | 21 +
.../goacc/kernels-loop-annotation-20.c | 23 +
.../goacc/kernels-loop-annotation-21.c | 42 +
.../goacc/kernels-loop-annotation-22.c | 41 +
.../c-c++-common/goacc/kernels-loop-annotation-3.c | 24 +
.../c-c++-common/goacc/kernels-loop-annotation-4.c | 34 +
.../c-c++-common/goacc/kernels-loop-annotation-5.c | 27 +
.../c-c++-common/goacc/kernels-loop-annotation-6.c | 27 +
.../c-c++-common/goacc/kernels-loop-annotation-7.c | 26 +
.../c-c++-common/goacc/kernels-loop-annotation-8.c | 27 +
.../c-c++-common/goacc/kernels-loop-annotation-9.c | 26 +
.../c-c++-common/goacc/kernels-loop-data-2.c | 1 +
.../goacc/kernels-loop-data-enter-exit-2.c | 1 +
.../goacc/kernels-loop-data-enter-exit.c | 1 +
.../c-c++-common/goacc/kernels-loop-data-update.c | 1 +
.../c-c++-common/goacc/kernels-loop-data.c | 1 +
gcc/testsuite/c-c++-common/goacc/kernels-loop-g.c | 1 +
.../c-c++-common/goacc/kernels-loop-mod-not-zero.c | 1 +
gcc/testsuite/c-c++-common/goacc/kernels-loop-n.c | 1 +
.../c-c++-common/goacc/kernels-loop-nest.c | 1 +
gcc/testsuite/c-c++-common/goacc/kernels-loop.c | 1 +
.../c-c++-common/goacc/kernels-one-counter-var.c | 1 +
.../goacc/kernels-parallel-loop-data-enter-exit.c | 1 +
.../c-c++-common/goacc/kernels-reduction.c | 1 +
gcc/tree.h | 5 +
54 files changed, 1603 insertions(+), 62 deletions(-)
create mode 100644
gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-1.c
create mode 100644
gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-10.c
create mode 100644
gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-11.c
create mode 100644
gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-12.c
create mode 100644
gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-13.c
create mode 100644
gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-14.c
create mode 100644
gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-15.c
create mode 100644
gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-16.c
create mode 100644
gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-17.c
create mode 100644
gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-18.c
create mode 100644
gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-19.c
create mode 100644
gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-2.c
create mode 100644
gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-20.c
create mode 100644
gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-21.c
create mode 100644
gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-22.c
create mode 100644
gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-3.c
create mode 100644
gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-4.c
create mode 100644
gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-5.c
create mode 100644
gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-6.c
create mode 100644
gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-7.c
create mode 100644
gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-8.c
create mode 100644
gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-9.c
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index 6abfe4b..d7938ba 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -1220,6 +1220,7 @@ extern enum omp_clause_default_kind
c_omp_predetermined_sharing (tree);
extern enum omp_clause_defaultmap_kind c_omp_predetermined_mapping (tree);
extern tree c_omp_check_context_selector (location_t, tree);
extern void c_omp_mark_declare_variant (location_t, tree, tree);
+extern void c_oacc_annotate_loops_in_kernels_regions (tree, tree (*) (tree));
extern const char *c_omp_map_clause_name (tree, bool);
/* Return next tree in the chain for chain_next walking of tree nodes. */
diff --git a/gcc/c-family/c-omp.c b/gcc/c-family/c-omp.c
index d7cff0f..3c86f3f 100644
--- a/gcc/c-family/c-omp.c
+++ b/gcc/c-family/c-omp.c
@@ -35,7 +35,7 @@ along with GCC; see the file COPYING3. If not see
#include "attribs.h"
#include "gimplify.h"
#include "langhooks.h"
-
+#include "tree-iterator.h"
/* Complete a #pragma oacc wait construct. LOC is the location of
the #pragma. */
@@ -694,6 +694,110 @@ c_omp_for_incr_canonicalize_ptr (location_t loc, tree
decl, tree incr)
return incr;
}
+/* State of annotation traversal for FOR loops in kernels regions,
+ used to control processing and diagnostic messages that are deferred until
+ the entire loop has been scanned. */
+enum annotation_state {
+ as_outer,
+ as_in_kernels_region,
+ as_in_kernels_loop,
+ /* The remaining state values represent conversion failures caught
+ while in as_in_kernels_loop state. To test whether the traversal is
+ in the body of a kernels loop, use (state >= as_in_kernels_loop). */
+ as_invalid_variable_type,
+ as_missing_initializer,
+ as_invalid_initializer,
+ as_missing_predicate,
+ as_invalid_predicate,
+ as_missing_increment,
+ as_invalid_increment,
+ as_explicit_annotation,
+ as_invalid_control_flow,
+ as_invalid_break,
+ as_invalid_return,
+ as_invalid_call,
+ as_invalid_modification
+};
+
+/* Structure used to hold state for automatic annotation of FOR loops
+ in kernels regions. LOOP is the nearest enclosing loop, or
+ NULL_TREE if outside of a loop context. VARS is a tree_list
+ containing the variables controlling LOOP's termination (the
+ induction variable and a possible limit variable). STATE keeps
+ track of whether loop satisfies all criteria making it legal to
+ parallelize. Otherwise, REASON is a statement that blocks
+ automatic parallelization, such as an unstructured jump or an
+ assignment to a variable in VARS, used for printing diagnostics.
+
+ These structures are chained through NEXT, which points to the
+ next-closest enclosing loop's or the kernels region's annotation info, if
+ any. */
+
+struct annotation_info
+{
+ tree loop;
+ tree vars;
+ bool break_ok;
+ enum annotation_state state;
+ tree reason;
+ struct annotation_info *next;
+};
+
+/* Mark the current loop's INFO as not OK to annotate, recording STATE
+ and REASON for producing diagnostics later. */
+
+static void
+do_not_annotate_loop (struct annotation_info *info,
+ enum annotation_state state, tree reason)
+{
+ if (info->state == as_in_kernels_loop)
+ {
+ info->state = state;
+ info->reason = reason;
+ }
+}
+
+/* Mark the current loop identified by INFO and all of its ancestors (i.e.,
+ enclosing loops) as not OK to annotate. Arguments are the same as
+ for do_not_annotate_loop. */
+
+static void
+do_not_annotate_loop_nest (struct annotation_info *info,
+ enum annotation_state state, tree reason)
+{
+ while (info != NULL)
+ {
+ do_not_annotate_loop (info, state, reason);
+ info = info->next;
+ }
+}
+
+/* If INFO is non-null, call do_not_annotate_loop with STATE and REASON
+ to record info for diagnosing an error later. Otherwise emit an error now
+ at ELOCUS with message MSG and the optional arguments. */
+
+static void annotation_error (struct annotation_info *,
+ enum annotation_state, tree, location_t,
+ const char *, ...) ATTRIBUTE_GCC_DIAG(5,6);
+static
+void annotation_error (struct annotation_info *info,
+ enum annotation_state state,
+ tree reason,
+ location_t elocus,
+ const char *msg, ...)
+{
+ if (info)
+ do_not_annotate_loop (info, state, reason);
+ else
+ {
+ auto_diagnostic_group d;
+ va_list ap;
+ va_start (ap, msg);
+ emit_diagnostic_valist (DK_ERROR, elocus, -1, msg, &ap);
+ va_end (ap);
+ }
+}
+
/* Validate and generate OMP_FOR.
DECLV is a vector of iteration variables, for each collapsed loop.
@@ -703,12 +807,19 @@ c_omp_for_incr_canonicalize_ptr (location_t loc, tree decl, tree incr)
INITV, CONDV and INCRV are vectors containing initialization
expressions, controlling predicates and increment expressions.
BODY is the body of the loop and PRE_BODY statements that go before
- the loop. */
+ the loop. FINAL_P is true if not inside a C++ template.
-tree
-c_finish_omp_for (location_t locus, enum tree_code code, tree declv,
- tree orig_declv, tree initv, tree condv, tree incrv,
- tree body, tree pre_body, bool final_p)
+ INFO is null if called to parse an explicitly-annotated OMP for
+ loop, otherwise it holds state information for automatically
+ annotating a regular FOR loop in a kernels region. In the former case,
+ malformed loops are hard errors; otherwise we just record the annotation
+ failure in INFO. */
+
+static tree
+c_finish_omp_for_internal (location_t locus, enum tree_code code, tree declv,
+ tree orig_declv, tree initv, tree condv, tree incrv,
+ tree body, tree pre_body, bool final_p,
+ struct annotation_info *info)
{
location_t elocus;
bool fail = false;
@@ -732,12 +843,14 @@ c_finish_omp_for (location_t locus, enum tree_code code,
tree declv,
if (!INTEGRAL_TYPE_P (TREE_TYPE (decl))
&& TREE_CODE (TREE_TYPE (decl)) != POINTER_TYPE)
{
- error_at (elocus, "invalid type for iteration variable %qE", decl);
+ annotation_error (info, as_invalid_variable_type, decl, elocus,
+ "invalid type for iteration variable %qE", decl);
fail = true;
}
else if (TYPE_ATOMIC (TREE_TYPE (decl)))
{
- error_at (elocus, "%<_Atomic%> iteration variable %qE", decl);
+ annotation_error (info, as_invalid_variable_type, decl, elocus,
+ "%<_Atomic%> iteration variable %qE", decl);
fail = true;
/* _Atomic iterator confuses stuff too much, so we risk ICE
trying to diagnose it further. */
@@ -753,7 +866,8 @@ c_finish_omp_for (location_t locus, enum tree_code code,
tree declv,
init = DECL_INITIAL (decl);
if (init == NULL)
{
- error_at (elocus, "%qE is not initialized", decl);
+ annotation_error (info, as_missing_initializer, decl, elocus,
+ "%qE is not initialized", decl);
init = integer_zero_node;
fail = true;
}
@@ -774,7 +888,8 @@ c_finish_omp_for (location_t locus, enum tree_code code,
tree declv,
if (cond == NULL_TREE)
{
- error_at (elocus, "missing controlling predicate");
+ annotation_error (info, as_missing_predicate, NULL_TREE, elocus,
+ "missing controlling predicate");
fail = true;
}
else
@@ -790,12 +905,14 @@ c_finish_omp_for (location_t locus, enum tree_code code,
tree declv,
if (EXPR_HAS_LOCATION (cond))
elocus = EXPR_LOCATION (cond);
- if (TREE_CODE (cond) == LT_EXPR
- || TREE_CODE (cond) == LE_EXPR
- || TREE_CODE (cond) == GT_EXPR
- || TREE_CODE (cond) == GE_EXPR
- || TREE_CODE (cond) == NE_EXPR
- || TREE_CODE (cond) == EQ_EXPR)
+ enum tree_code condcode = TREE_CODE (cond);
+
+ if (condcode == LT_EXPR
+ || condcode == LE_EXPR
+ || condcode == GT_EXPR
+ || condcode == GE_EXPR
+ || condcode == NE_EXPR
+ || condcode == EQ_EXPR)
{
tree op0 = TREE_OPERAND (cond, 0);
tree op1 = TREE_OPERAND (cond, 1);
@@ -815,79 +932,88 @@ c_finish_omp_for (location_t locus, enum tree_code code,
tree declv,
if (TREE_CODE (op0) == NOP_EXPR
&& decl == TREE_OPERAND (op0, 0))
{
- TREE_OPERAND (cond, 0) = TREE_OPERAND (op0, 0);
- TREE_OPERAND (cond, 1)
- = fold_build1_loc (elocus, NOP_EXPR, TREE_TYPE (decl),
- TREE_OPERAND (cond, 1));
+ op0 = TREE_OPERAND (op0, 0);
+ op1 = fold_build1_loc (elocus, NOP_EXPR, TREE_TYPE (decl),
+ op1);
}
else if (TREE_CODE (op1) == NOP_EXPR
&& decl == TREE_OPERAND (op1, 0))
{
- TREE_OPERAND (cond, 1) = TREE_OPERAND (op1, 0);
- TREE_OPERAND (cond, 0)
- = fold_build1_loc (elocus, NOP_EXPR, TREE_TYPE (decl),
- TREE_OPERAND (cond, 0));
+ op1 = TREE_OPERAND (op1, 0);
+ op0 = fold_build1_loc (elocus, NOP_EXPR, TREE_TYPE (decl),
+ op0);
}
- if (decl == TREE_OPERAND (cond, 0))
+ if (decl == op0)
cond_ok = true;
- else if (decl == TREE_OPERAND (cond, 1))
+ else if (decl == op1)
{
- TREE_SET_CODE (cond,
- swap_tree_comparison (TREE_CODE (cond)));
- TREE_OPERAND (cond, 1) = TREE_OPERAND (cond, 0);
- TREE_OPERAND (cond, 0) = decl;
+ condcode = swap_tree_comparison (condcode);
+ op1 = op0;
+ op0 = decl;
cond_ok = true;
}
- if (TREE_CODE (cond) == NE_EXPR
- || TREE_CODE (cond) == EQ_EXPR)
+ if (condcode == NE_EXPR || condcode == EQ_EXPR)
{
if (!INTEGRAL_TYPE_P (TREE_TYPE (decl)))
{
- if (code == OACC_LOOP || TREE_CODE (cond) == EQ_EXPR)
+ if (code == OACC_LOOP || condcode == EQ_EXPR)
cond_ok = false;
}
- else if (operand_equal_p (TREE_OPERAND (cond, 1),
+ else if (operand_equal_p (op1,
TYPE_MIN_VALUE (TREE_TYPE (decl)),
0))
- TREE_SET_CODE (cond, TREE_CODE (cond) == NE_EXPR
- ? GT_EXPR : LE_EXPR);
- else if (operand_equal_p (TREE_OPERAND (cond, 1),
+ condcode = (condcode == NE_EXPR ? GT_EXPR : LE_EXPR);
+ else if (operand_equal_p (op1,
TYPE_MAX_VALUE (TREE_TYPE (decl)),
0))
- TREE_SET_CODE (cond, TREE_CODE (cond) == NE_EXPR
- ? LT_EXPR : GE_EXPR);
- else if (code == OACC_LOOP || TREE_CODE (cond) == EQ_EXPR)
+ condcode = (condcode == NE_EXPR ? LT_EXPR : GE_EXPR);
+ else if (code == OACC_LOOP || condcode == EQ_EXPR)
cond_ok = false;
}
- if (cond_ok && TREE_VEC_ELT (condv, i) != cond)
+ if (cond_ok)
{
- tree ce = NULL_TREE, *pce = &ce;
- tree type = TREE_TYPE (TREE_OPERAND (cond, 1));
- for (tree c = TREE_VEC_ELT (condv, i); c != cond;
- c = TREE_OPERAND (c, 1))
+ /* We postponed destructive changes to canonicalize
+ cond until we're sure it is OK. In the !error_p
+ case where we are trying to transform a regular FOR_STMT
+ to OMP_FOR, we don't want to destroy the original
+ condition if we aren't going to be able to do the
+ transformation anyway. */
+ TREE_SET_CODE (cond, condcode);
+ TREE_OPERAND (cond, 0) = op0;
+ TREE_OPERAND (cond, 1) = op1;
+
+ if (TREE_VEC_ELT (condv, i) != cond)
{
- *pce = build2 (COMPOUND_EXPR, type, TREE_OPERAND (c, 0),
- TREE_OPERAND (cond, 1));
- pce = &TREE_OPERAND (*pce, 1);
+ tree ce = NULL_TREE, *pce = &ce;
+ tree type = TREE_TYPE (op1);
+ for (tree c = TREE_VEC_ELT (condv, i); c != cond;
+ c = TREE_OPERAND (c, 1))
+ {
+ *pce = build2 (COMPOUND_EXPR, type,
+ TREE_OPERAND (c, 0), op1);
+ pce = &TREE_OPERAND (*pce, 1);
+ }
+ op1 = ce;
+ TREE_VEC_ELT (condv, i) = cond;
}
- TREE_OPERAND (cond, 1) = ce;
- TREE_VEC_ELT (condv, i) = cond;
}
}
if (!cond_ok)
{
- error_at (elocus, "invalid controlling predicate");
+ annotation_error (info, as_invalid_predicate, cond, elocus,
+ "invalid controlling predicate");
fail = true;
}
}
if (incr == NULL_TREE)
{
- error_at (elocus, "missing increment expression");
+ annotation_error (info, as_missing_increment, NULL_TREE, elocus,
+ "missing increment expression");
fail = true;
}
else
@@ -986,9 +1112,11 @@ c_finish_omp_for (location_t locus, enum tree_code code,
tree declv,
if (i == NULL_TREE
|| !operand_equal_p (unit, i, 0))
{
- error_at (elocus,
- "increment is not constant 1 or "
- "-1 for %<!=%> condition");
+ annotation_error (info,
+ as_invalid_increment,
+ incr, elocus,
+ "increment is not constant 1 or
"
+ "-1 for %<!=%> condition");
fail = true;
}
}
@@ -1004,9 +1132,10 @@ c_finish_omp_for (location_t locus, enum tree_code code,
tree declv,
{
if (!integer_onep (i) && !integer_minus_onep (i))
{
- error_at (elocus,
- "increment is not constant 1 or -1 for"
- " %<!=%> condition");
+ annotation_error (info, as_invalid_increment,
+ incr, elocus,
+ "increment is not constant 1 or -1
for"
+ " %<!=%> condition");
fail = true;
}
}
@@ -1018,7 +1147,8 @@ c_finish_omp_for (location_t locus, enum tree_code code,
tree declv,
}
if (!incr_ok)
{
- error_at (elocus, "invalid increment expression");
+ annotation_error (info, as_invalid_increment, incr,
+ elocus, "invalid increment expression");
fail = true;
}
}
@@ -1046,6 +1176,20 @@ c_finish_omp_for (location_t locus, enum tree_code code,
tree declv,
}
}
+/* External entry point to c_finish_omp_for_internal, called from the
+ parsers. See above for description of the arguments. */
+
+tree
+c_finish_omp_for (location_t locus, enum tree_code code, tree declv,
+ tree orig_declv, tree initv, tree condv, tree incrv,
+ tree body, tree pre_body, bool final_p)
+{
+ return c_finish_omp_for_internal (locus, code, declv,
+ orig_declv, initv, condv, incrv,
+ body, pre_body, final_p, NULL);
+}
+
+
/* Type for passing data in between c_omp_check_loop_iv and
c_omp_check_loop_iv_r. */
@@ -2579,3 +2723,657 @@ c_omp_map_clause_name (tree clause, bool oacc)
}
return omp_clause_code_name[OMP_CLAUSE_CODE (clause)];
}
+
+
+/* The following functions implement automatic recognition and annotation of
+ for loops in OpenACC kernels regions. Inside a kernels region, a nest of
+ for loops that does not contain any annotated OpenACC loops, nor break
+ or goto statements or assignments to the variables controlling loop
+ termination, is converted to an OMP_FOR node with an "acc loop auto"
+ annotation on each loop. This feature is controlled by
+ flag_openacc_kernels_annotate_loops. */
+
+/* Check whether DECL is the declaration of a local variable (or function
+ parameter) of integral type that does not have its address taken. */
+
+static bool
+is_local_var (tree decl)
+{
+ return ((TREE_CODE (decl) == VAR_DECL || TREE_CODE (decl) == PARM_DECL)
+ && DECL_CONTEXT (decl) != NULL
+ && TREE_CODE (DECL_CONTEXT (decl)) == FUNCTION_DECL
+ && INTEGRAL_TYPE_P (TREE_TYPE (decl))
+ && !TREE_ADDRESSABLE (decl));
+}
+
+/* EXP is a loop bound expression for a comparison against local
+ variable DECL. Check whether this is potentially valid in an OpenACC loop
+ context, namely that it can be precomputed when entering the loop
+ construct per the OpenACC specification. Local variables referenced
+ in both DECL and EXP that may not be modified in the body of the loop
+ are added to the list in INFO to be checked later.
+
+ FIXME: Ideally we would like to make this test permissive rather than
+ restrictive, and allow the later conversion of the "auto" attribute to
+ either "seq" or "independent" to make the determination using dataflow,
+ alias analysis, etc rather than a tree traversal. But presently it does
+ not do that and always just hoists the loop bound expression. So the
+ current implementation only considers expressions involving unmodified
+ local variables and constants, using a tree walk. */
+
+static tree
+end_test_ok_for_annotation_r (tree *tp, int *walk_subtrees,
+ void *data)
+{
+ tree exp = *tp;
+ struct annotation_info *info = (struct annotation_info *) data;
+
+ switch (TREE_CODE_CLASS (TREE_CODE (exp)))
+ {
+ case tcc_constant:
+ /* Constants are trivially known to be invariant. */
+ return NULL_TREE;
+
+ case tcc_declaration:
+ if (is_local_var (exp))
+ {
+ tree t;
+ /* Add it to the list of variables that can't be modified in the
+ loop, only if not already present. */
+ for (t = info->vars; t && TREE_VALUE (t) != exp;
+ t = TREE_CHAIN (t))
+ ;
+ if (!t)
+ info->vars = tree_cons (NULL_TREE, exp, info->vars);
+ return NULL_TREE;
+ }
+ else if (TREE_CODE (exp) == VAR_DECL && TREE_READONLY (exp))
+ return NULL_TREE;
+ else if (TREE_CODE (exp) == FUNCTION_DECL)
+ return NULL_TREE;
+ break;
+
+ case tcc_unary:
+ case tcc_binary:
+ case tcc_comparison:
+ /* Allow arithmetic expressions and comparisons provided
+ that the operands are good. */
+ return NULL_TREE;
+
+ default:
+ /* Handle some special cases. */
+ switch (TREE_CODE (exp))
+ {
+ case COND_EXPR:
+ case TRUTH_ANDIF_EXPR:
+ case TRUTH_ORIF_EXPR:
+ case TRUTH_AND_EXPR:
+ case TRUTH_OR_EXPR:
+ case TRUTH_XOR_EXPR:
+ case TRUTH_NOT_EXPR:
+ /* ?: and boolean operators are OK. */
+ return NULL_TREE;
+
+ case CALL_EXPR:
+ /* Allow calls to constant functions with invariant operands. */
+ {
+ tree fndecl = get_callee_fndecl (exp);
+ if (fndecl && TREE_READONLY (fndecl))
+ return NULL_TREE;
+ }
+ break;
+
+ case ADDR_EXPR:
+ /* We can expect addresses of things to be invariant. */
+ return NULL_TREE;
+
+ default:
+ break;
+ }
+ }
+
+ /* Reject anything else. */
+ *walk_subtrees = 0;
+ return exp;
+}
+
+static bool
+end_test_ok_for_annotation (tree decl, tree exp,
+ struct annotation_info *info)
+{
+ /* Traversal returns NULL_TREE if all is well. */
+ if (!walk_tree (&exp, end_test_ok_for_annotation_r, info, NULL))
+ {
+ /* So far, so good. Check the decl against any variables collected
+ in the exp. */
+ tree t;
+ for (t = info->vars; t; t = TREE_CHAIN (t))
+ if (TREE_VALUE (t) == decl)
+ return false;
+ info->vars = tree_cons (NULL_TREE, decl, info->vars);
+ return true;
+ }
+ return false;
+}
+
+/* The initializer for a FOR_STMT is sometimes wrapped in various other
+ language-specific tree structures. We need a hook to unwrap them.
+ This function takes a tree argument and should return either a
+ MODIFY_EXPR, VAR_DECL, or NULL_TREE. */
+
+static tree (*lang_specific_unwrap_initializer) (tree);
+
+/* Try to annotate the given NODE, which must be a FOR_STMT, with a
+ "#pragma acc loop auto" annotation. In practice, this means
+ building an OMP_FOR node for it. DECL and INIT are the
+ previously-verified iteration variable and initializer. Annotating
+ the loop may fail, in which case INFO is used to record the cause
+ of the failure and the original loop remains unchanged. This
+ function returns the transformed loop if the transformation
+ succeeded, the original node otherwise. */
+
+static tree
+annotate_for_loop (tree node, tree decl, tree init,
+ struct annotation_info *info)
+{
+ gcc_checking_assert (TREE_CODE (node) == FOR_STMT);
+
+ location_t loc = EXPR_LOCATION (node);
+ tree cond = FOR_COND (node);
+ tree incr = FOR_EXPR (node);
+
+ gcc_assert (decl);
+ gcc_assert (cond);
+ gcc_assert (decl && TREE_CODE (decl) == VAR_DECL);
+
+ /* The C++ frontend can wrap the increment two levels deep inside a
+ cleanup expression, but c_finish_omp_for does not care about that. */
+ if (incr != NULL_TREE && TREE_CODE (incr) == CLEANUP_POINT_EXPR)
+ incr = TREE_OPERAND (TREE_OPERAND (incr, 0), 0);
+ tree body = FOR_BODY (node);
+
+ tree declv = make_tree_vec (1);
+ tree initv = make_tree_vec (1);
+ tree condv = make_tree_vec (1);
+ tree incrv = make_tree_vec (1);
+ TREE_VEC_ELT (declv, 0) = decl;
+ TREE_VEC_ELT (initv, 0) = init;
+ TREE_VEC_ELT (condv, 0) = cond;
+ TREE_VEC_ELT (incrv, 0) = incr;
+
+ /* Do the actual transformation. This can still fail because
+ c_finish_omp_for has some stricter checks than we have performed up to
+ this point. */
+ tree omp_for = c_finish_omp_for_internal (loc, OACC_LOOP, declv, NULL_TREE,
+ initv, condv, incrv, body,
+ NULL_TREE, false, info);
+ if (omp_for != NULL_TREE)
+ {
+ /* Add an auto clause, then return the new loop. */
+ tree auto_clause = build_omp_clause (loc, OMP_CLAUSE_AUTO);
+ OMP_CLAUSE_CHAIN (auto_clause) = OMP_FOR_CLAUSES (omp_for);
+ OMP_FOR_CLAUSES (omp_for) = auto_clause;
+ return omp_for;
+ }
+
+ return node;
+}
+
+/* Forward declaration. */
+static tree annotate_loops_in_kernels_regions (tree *, int *, void *);
+
+/* Given a FOR_STMT NODE that is a candidate for parallelization, check its
+ body for validity, then try to annotate it with
+ "#pragma oacc loop auto", possibly modifying the current node in place.
+ The INFO argument contains the traversal state at the point the loop
+ appears. */
+
+static void
+check_and_annotate_for_loop (tree *nodeptr, tree_stmt_iterator *prev_tsi,
+ struct annotation_info *info)
+{
+ tree node = *nodeptr;
+ gcc_assert (TREE_CODE (node) == FOR_STMT);
+ tree init = FOR_INIT_STMT (node);
+ tree cond = FOR_COND (node);
+ tree prev_stmt = NULL_TREE;
+ tree decl = NULL_TREE;
+ bool unlink_prev = false;
+ bool fix_decl = false;
+
+ /* This structure describes the current loop statement. */
+ struct annotation_info loop_info
+ = { node, NULL_TREE, false, as_in_kernels_loop, NULL_TREE, info };
+
+ /* If we are in the body of an explicitly-annotated loop, do not add
+ annotations to this loop or any other nested loops. */
+ if (info->state == as_explicit_annotation)
+ do_not_annotate_loop (&loop_info, as_explicit_annotation, info->reason);
+
+ /* We need to find the controlling variable for the loop in order
+ to detect whether it is modified in the body of the loop.
+ That is why we are doing some checks on the loop condition
+ that duplicate what c_finish_omp_for is doing. */
+
+ /* First we need to find the decl and initializer for the
+ controlling variable. Both the C and C++ front ends normally put
+ the initializer in the statement list just before the FOR_STMT
+ instead of in FOR_INIT_STMT. If FOR_INIT_STMT happens to exist
+ but isn't a MODIFY_EXPR, give up.
+ handle it. */
+
+ else if (init != NULL_TREE && TREE_CODE (init) != MODIFY_EXPR)
+ do_not_annotate_loop (&loop_info, as_invalid_initializer, NULL_TREE);
+
+ /* Examine the statement before the loop to see if it is a
+ valid initializer. It must be either a MODIFY_EXPR or VAR_DECL,
+ possibly wrapped in language-specific structure. */
+ else if (init == NULL_TREE && prev_tsi != NULL && tsi_stmt (*prev_tsi))
+ {
+ prev_stmt = tsi_stmt (*prev_tsi);
+
+ /* Call the language-specific hook to unwrap prev_stmt. */
+ prev_stmt = (*lang_specific_unwrap_initializer) (prev_stmt);
+
+ /* See if we have a valid MODIFY_EXPR. */
+ if (TREE_CODE (prev_stmt) == MODIFY_EXPR
+ && is_local_var (TREE_OPERAND (prev_stmt, 0))
+ && !TREE_SIDE_EFFECTS (TREE_OPERAND (prev_stmt, 1)))
+ {
+ decl = TREE_OPERAND (prev_stmt, 0);
+ init = prev_stmt;
+ unlink_prev = true;
+ }
+ else if (is_local_var (prev_stmt)
+ && !TREE_SIDE_EFFECTS (DECL_INITIAL (prev_stmt)))
+ {
+ /* If the preceding statement is the declaration of the loop
+ variable with its initialization, build an assignment
+ expression for the loop's initializer. */
+ decl = prev_stmt;
+ init = build2 (MODIFY_EXPR, TREE_TYPE (decl), decl,
+ DECL_INITIAL (decl));
+ /* We need to remove the initializer from the decl if we
+ end up using the init we just built instead. */
+ fix_decl = true;
+ }
+ }
+
+ if (init == NULL_TREE || decl == NULL_TREE)
+ /* There is nothing we can do to find the correct init statement for
+ this loop. */
+ do_not_annotate_loop (&loop_info, as_missing_initializer, NULL_TREE);
+
+ /* The condition must be a comparison of the decl we found in
+ the initializer against an expression that can be hoisted
+ outside the loop. */
+ if (loop_info.state > as_in_kernels_loop)
+ /* Skip validating condition if we've already got an error. */
+ ;
+ else if (cond == NULL_TREE)
+ do_not_annotate_loop (&loop_info, as_missing_predicate, NULL_TREE);
+ else if (TREE_CODE_CLASS (TREE_CODE (cond)) != tcc_comparison)
+ do_not_annotate_loop (&loop_info, as_invalid_predicate, cond);
+ else
+ {
+ tree limit_exp = NULL_TREE;
+
+ if (TREE_OPERAND (cond, 0) == decl)
+ limit_exp = TREE_OPERAND (cond, 1);
+ else if (TREE_OPERAND (cond, 1) == decl)
+ limit_exp = TREE_OPERAND (cond, 0);
+
+ if (!limit_exp
+ || !end_test_ok_for_annotation (decl, limit_exp, &loop_info))
+ do_not_annotate_loop (&loop_info, as_invalid_predicate, cond);
+ }
+
+ /* Walk the body. This will process any nested loops, so we have to do it
+ even if we have already rejected this loop as a candidate for
+ annotation. */
+ walk_tree (&FOR_BODY (node), annotate_loops_in_kernels_regions,
+ (void *) &loop_info, NULL);
+
+ if (loop_info.state == as_in_kernels_loop)
+ {
+ /* If the traversal of the loop and all nested loops didn't hit
+ any problems, attempt the actual transformation. If it
+ succeeds, replace this node with the annotated loop. */
+ tree result = annotate_for_loop (node, decl, init, &loop_info);
+ if (result != node)
+ {
+ /* Success! */
+ *nodeptr = result;
+
+ if (unlink_prev)
+ /* We don't need the previous statement that we consumed
+ as an initializer in the new OMP_FOR any more. */
+ tsi_delink (prev_tsi);
+
+ if (fix_decl)
+ /* We no longer need the initializer expression on the
+ decl of the loop variable and don't want to duplicate
+ it. The kernels conversion pass would interpret it as
+ a stray assignment in a gang-single region. */
+ DECL_INITIAL (decl) = NULL_TREE;
+
+ return;
+ }
+ }
+
+ /* If we got here, we have a FOR_STMT we could not convert to an
+ OMP loop. */
+
+ if (loop_info.state == as_invalid_return)
+ /* This is diagnosed elsewhere as a hard error, so no warning is
+ needed here. */
+ return;
+
+ /* Issue warnings about other problems. */
+ auto_diagnostic_group d;
+ if (warning_at (EXPR_LOCATION (node),
+ OPT_Wopenacc_kernels_annotate_loops,
+ "loop cannot be annotated for OpenACC parallelization"))
+ {
+ location_t locus;
+ if (loop_info.reason && EXPR_HAS_LOCATION (loop_info.reason))
+ locus = EXPR_LOCATION (loop_info.reason);
+ else
+ locus = EXPR_LOCATION (node);
+ switch (loop_info.state)
+ {
+ case as_invalid_variable_type:
+ inform (locus, "invalid type for iteration variable %qE",
+ loop_info.reason);
+ break;
+ case as_missing_initializer:
+ inform (locus, "missing iteration variable initializer");
+ break;
+ case as_invalid_initializer:
+ inform (locus, "unrecognized initializer");
+ break;
+ case as_missing_predicate:
+ inform (locus, "missing controlling predicate");
+ break;
+ case as_invalid_predicate:
+ inform (locus, "invalid controlling predicate");
+ break;
+ case as_missing_increment:
+ inform (locus, "missing increment expression");
+ break;
+ case as_invalid_increment:
+ inform (locus, "invalid increment expression");
+ break;
+ case as_explicit_annotation:
+ inform (locus, "explicit OpenACC annotation in loop nest");
+ break;
+ case as_invalid_control_flow:
+ inform (locus, "loop contains unstructured control flow");
+ break;
+ case as_invalid_break:
+ inform (locus, "loop contains %<break%> statement");
+ break;
+ case as_invalid_call:
+ inform (locus, "loop contains call to non-oacc function");
+ break;
+ case as_invalid_modification:
+ inform (locus, "invalid modification of controlling variable");
+ break;
+ default:
+ gcc_unreachable ();
+ }
+ }
+}
+
+/* Traversal function for walk_tree. Visit the tree, finding OpenACC
+ kernels regions. DATA is NULL if we are outside of a kernels region,
+ otherwise it is a pointer to the enclosing kernels region's
+ annotation_info struct. If the traversal encounters a for loop inside a
+ kernels region that is a candidate for parallelization, annotate it
+ with OpenACC loop directives. */
+
+static tree
+annotate_loops_in_kernels_regions (tree *nodeptr, int *walk_subtrees,
+ void *data)
+{
+ tree node = *nodeptr;
+ struct annotation_info *info = (struct annotation_info *) data;
+ gcc_assert (info);
+
+ switch (TREE_CODE (node))
+ {
+ case OACC_KERNELS:
+ /* Recursively process the body of the kernels region in a new info
+ scope. */
+ if (info->state == as_outer)
+ {
+ struct annotation_info nested_info
+ = { NULL_TREE, NULL_TREE, true,
+ as_in_kernels_region, NULL_TREE, info };
+ walk_tree (&OMP_BODY (node), annotate_loops_in_kernels_regions,
+ (void *) &nested_info, NULL);
+ *walk_subtrees = 0;
+ }
+ break;
+
+ case OACC_LOOP:
+ /* Do not try to add automatic OpenACC annotations inside manually
+ annotated loops. Presumably, the user avoided doing it on
+ purpose; for example, all available levels of parallelism may
+ have been used up. However, assume that the combined construct
+ "#pragma acc kernels loop" means to try to process the whole
+ loop nest.
+ Note that a single OACC_LOOP construct represents an entire set
+ of collapsed loops so we do not have to deal explicitly with the
+ collapse clause here, as the Fortran front end does. */
+ if (info->state == as_in_kernels_region && OACC_LOOP_COMBINED (node))
+ {
+ walk_tree (&OMP_BODY (node), annotate_loops_in_kernels_regions,
+ (void *) info, NULL);
+ *walk_subtrees = 0;
+ }
+ else
+ {
+ struct annotation_info nested_info
+ = { NULL_TREE, NULL_TREE, false, as_explicit_annotation,
+ node, info };
+ if (info->state >= as_in_kernels_region)
+ do_not_annotate_loop_nest (info, as_explicit_annotation,
+ node);
+ walk_tree (&OMP_BODY (node), annotate_loops_in_kernels_regions,
+ (void *) &nested_info, NULL);
+ *walk_subtrees = 0;
+ }
+ break;
+
+ case FOR_STMT:
+ /* Try to annotate the loop if we are in a kernels region.
+ This will do a recursive traversal of the loop body in a new
+ info scope. */
+ if (info->state >= as_in_kernels_region)
+ {
+ check_and_annotate_for_loop (nodeptr, NULL, info);
+ *walk_subtrees = 0;
+ }
+ break;
+
+ case LABEL_EXPR:
+ /* Possibly unstructured control flow. Unless we perform further
+ analyses, we must assume that such control flow may enter the
+ current loop. In this case, we must not parallelize the loop. */
+ if (info->state >= as_in_kernels_loop
+ && TREE_USED (LABEL_EXPR_LABEL (node)))
+ do_not_annotate_loop_nest (info, as_invalid_control_flow, node);
+ break;
+
+ case GOTO_EXPR:
+ /* Possibly unstructured control flow. Unless we perform further
+ analyses, we must assume that such control flow may leave the
+ current loop. In this case, we must not parallelize the loop. */
+ if (info->state >= as_in_kernels_loop)
+ do_not_annotate_loop_nest (info, as_invalid_control_flow, node);
+ break;
+
+ case BREAK_STMT:
+ /* A break statement. Whether or not this is valid depends on the
+ enclosing context. */
+ if (info->state >= as_in_kernels_loop && !info->break_ok)
+ do_not_annotate_loop (info, as_invalid_break, node);
+ break;
+
+ case RETURN_EXPR:
+ /* A return leaves the entire loop nest. */
+ if (info->state >= as_in_kernels_loop)
+ do_not_annotate_loop_nest (info, as_invalid_return, node);
+ break;
+
+ case CALL_EXPR:
+ /* Direct function calls to builtins and functions marked as
+ OpenACC routines are allowed. Reject indirect calls or calls
+ to non-routines. */
+ if (info->state >= as_in_kernels_loop)
+ {
+ tree fn = CALL_EXPR_FN (node), fn_decl = NULL_TREE;
+ if (fn != NULL_TREE && TREE_CODE (fn) == FUNCTION_DECL)
+ fn_decl = fn;
+ else if (fn != NULL_TREE && TREE_CODE (fn) == ADDR_EXPR)
+ {
+ tree fn_op = TREE_OPERAND (fn, 0);
+ if (fn_op != NULL_TREE && TREE_CODE (fn_op) == FUNCTION_DECL)
+ fn_decl = fn_op;
+ }
+ if (fn_decl == NULL_TREE)
+ do_not_annotate_loop_nest (info, as_invalid_call, node);
+ else if (!fndecl_built_in_p (fn_decl, BUILT_IN_NORMAL)
+ && !lookup_attribute ("oacc function",
+ DECL_ATTRIBUTES (fn_decl)))
+ do_not_annotate_loop_nest (info, as_invalid_call, node);
+ }
+ break;
+
+ case MODIFY_EXPR:
+ /* See if this assignment's LHS is one of the variables that must
+ not be modified in the loop body because they control termination
+ of the loop (or an enclosing loop in the nest). */
+ if (info->state >= as_in_kernels_loop)
+ {
+ tree lhs = TREE_OPERAND (node, 0);
+ if (!is_local_var (lhs))
+ /* Early exit: This cannot be a variable we care about. */
+ break;
+ /* Walk up the loop stack. Invalidate the ones controlled by this
+ variable. There may be several, if this variable is the common
+ iteration limit for several nested loops. */
+ for (struct annotation_info *outer_loop = info; outer_loop != NULL;
+ outer_loop = outer_loop->next)
+ for (tree t = outer_loop->vars; t != NULL_TREE; t = TREE_CHAIN (t))
+ if (TREE_VALUE (t) == lhs)
+ {
+ do_not_annotate_loop (outer_loop,
+ as_invalid_modification,
+ node);
+ break;
+ }
+ }
+ break;
+
+ case SWITCH_STMT:
+ /* Needs special handling to allow break in the body. */
+ if (info->state >= as_in_kernels_loop)
+ {
+ bool save_break_ok = info->break_ok;
+
+ walk_tree (&SWITCH_STMT_COND (node),
+ annotate_loops_in_kernels_regions,
+ (void *) info, NULL);
+ info->break_ok = true;
+ walk_tree (&SWITCH_STMT_BODY (node),
+ annotate_loops_in_kernels_regions,
+ (void *) info, NULL);
+ info->break_ok = save_break_ok;
+ *walk_subtrees = 0;
+ }
+ break;
+
+ case WHILE_STMT:
+ /* Needs special handling to allow break in the body. */
+ if (info->state >= as_in_kernels_loop)
+ {
+ bool save_break_ok = info->break_ok;
+
+ walk_tree (&WHILE_COND (node), annotate_loops_in_kernels_regions,
+ (void *) info, NULL);
+ info->break_ok = true;
+ walk_tree (&WHILE_BODY (node), annotate_loops_in_kernels_regions,
+ (void *) info, NULL);
+ info->break_ok = save_break_ok;
+ *walk_subtrees = 0;
+ }
+ break;
+
+ case DO_STMT:
+ /* Needs special handling to allow break in the body. */
+ if (info->state >= as_in_kernels_loop)
+ {
+ bool save_break_ok = info->break_ok;
+
+ walk_tree (&DO_COND (node), annotate_loops_in_kernels_regions,
+ (void *) info, NULL);
+ info->break_ok = true;
+ walk_tree (&DO_BODY (node), annotate_loops_in_kernels_regions,
+ (void *) info, NULL);
+ info->break_ok = save_break_ok;
+ *walk_subtrees = 0;
+ }
+ break;
+
+ case STATEMENT_LIST:
+ /* We iterate over these explicitly so that we can track the previous
+ statement in the chain. It may be the initializer for a following
+ FOR_STMT node. */
+ if (info->state >= as_in_kernels_region)
+ {
+ tree_stmt_iterator i = tsi_start (node);
+ tree_stmt_iterator prev, *prev_tsi = NULL;
+ while (!tsi_end_p (i))
+ {
+ tree *stmtptr = tsi_stmt_ptr (i);
+ if (TREE_CODE (*stmtptr) == FOR_STMT)
+ {
+ check_and_annotate_for_loop (stmtptr, prev_tsi, info);
+ *walk_subtrees = 0;
+ }
+ else
+ walk_tree (stmtptr, annotate_loops_in_kernels_regions,
+ (void *) info, NULL);
+ prev = i;
+ prev_tsi = &prev;
+ tsi_next (&i);
+ }
+ *walk_subtrees = 0;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return NULL_TREE;
+}
+
+/* Find for loops in OpenACC kernels regions that do not have OpenACC
+ annotations but look like they might benefit from automatic
+ parallelization. Convert them from FOR_STMT to OMP_FOR nodes and
+ add the equivalent of "#pragma acc loop auto" annotations for them.
+ Assumes flag_openacc_kernels_annotate_loops is set. */
+
+void
+c_oacc_annotate_loops_in_kernels_regions (tree decl,
+ tree (*unwrap_fn) (tree))
+{
+ struct annotation_info info
+ = { NULL_TREE, NULL_TREE, true, as_outer, NULL_TREE, NULL };
+ lang_specific_unwrap_initializer = unwrap_fn;
+ break;
+
+ default:
+ return NULL_TREE;
+ }
+}
/* Finish up a function declaration and compile that function
all the way to assembler language output. Then free the storage
@@ -9987,6 +10010,11 @@ finish_function (location_t end_loc)
if (warn_unused_parameter)
do_warn_unused_parameter (fndecl);
+ /* If requested, automatically annotate suitable loops in OpenACC kernels
+ regions with OpenACC loop annotations to allow auto-parallelization. */
+ if (flag_openacc && flag_openacc_kernels_annotate_loops)
+ c_oacc_annotate_loops_in_kernels_regions (fndecl, c_unwrap_for_init);
+
/* Store the end of the function, so that we get good line number
info for the epilogue. */
cfun->function_end_locus = end_loc;
diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c
index 2e6775a..12fb4b7 100644
--- a/gcc/c/c-parser.c
+++ b/gcc/c/c-parser.c
@@ -16831,6 +16831,7 @@ c_parser_oacc_loop (location_t loc, c_parser *parser,
char *p_name,
omp_clause_mask mask, tree *cclauses, bool *if_p)
{
bool is_parallel = ((mask >> PRAGMA_OACC_CLAUSE_REDUCTION) & 1) == 1;
+ bool is_combined = (cclauses != NULL);
strcat (p_name, " loop");
mask |= OACC_LOOP_CLAUSE_MASK;
@@ -16849,6 +16850,8 @@ c_parser_oacc_loop (location_t loc, c_parser *parser,
char *p_name,
tree block = c_begin_compound_stmt (true);
tree stmt = c_parser_omp_for_loop (loc, parser, OACC_LOOP, clauses, NULL,
if_p);
+ if (stmt && stmt != error_mark_node)
+ OACC_LOOP_COMBINED (stmt) = is_combined;
block = c_end_compound_stmt (loc, block, true);
add_stmt (block);
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index 31d6874..2d83c41 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -16960,6 +16960,45 @@ emit_coro_helper (tree helper)
expand_or_defer_fn (helper);
}
+
+/* Function passed to c_oacc_annotate_loop_in_kernels_regions to do
+ language-specific unwrapping of an initializer expression. */
+static tree
+cp_unwrap_for_init (tree x)
+{
+ if (!x)
+ return NULL_TREE;
+
+ while (true)
+ switch (TREE_CODE (x))
+ {
+ case MODIFY_EXPR:
+ case VAR_DECL:
+ return x;
+
+ case CLEANUP_POINT_EXPR:
+ x = TREE_OPERAND (x, 0);
+ break;
+
+ case EXPR_STMT:
+ x = TREE_OPERAND (x, 0);
+ break;
+
+ case DECL_EXPR:
+ x = TREE_OPERAND (x, 0);
+ break;
+
+ case CONVERT_EXPR:
+ if (TREE_TYPE (x) != void_type_node)
+ return NULL_TREE;
+ x = TREE_OPERAND (x, 0);
+ break;
+
+ default:
+ return NULL_TREE;
+ }
+}
+
/* Finish up a function declaration and compile that function
all the way to assembler language output. The free the storage
for the function definition. INLINE_P is TRUE if we just
@@ -17264,6 +17303,11 @@ finish_function (bool inline_p)
&& !DECL_CLONED_FUNCTION_P (fndecl))
do_warn_unused_parameter (fndecl);
+ /* If requested, automatically annotate suitable loops in OpenACC kernels
+ regions with OpenACC loop annotations to allow auto-parallelization. */
+ if (flag_openacc && flag_openacc_kernels_annotate_loops)
+ c_oacc_annotate_loops_in_kernels_regions (fndecl, cp_unwrap_for_init);
+
/* Genericize before inlining. */
if (!processing_template_decl
&& !DECL_IMMEDIATE_FUNCTION_P (fndecl)
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 9849e59..1a11f2b 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -41281,6 +41281,7 @@ cp_parser_oacc_loop (cp_parser *parser, cp_token
*pragma_tok, char *p_name,
omp_clause_mask mask, tree *cclauses, bool *if_p)
{
bool is_parallel = ((mask >> PRAGMA_OACC_CLAUSE_REDUCTION) & 1) == 1;
+ bool is_combined = (cclauses != NULL);
strcat (p_name, " loop");
mask |= OACC_LOOP_CLAUSE_MASK;
@@ -41299,6 +41300,8 @@ cp_parser_oacc_loop (cp_parser *parser, cp_token
*pragma_tok, char *p_name,
tree block = begin_omp_structured_block ();
int save = cp_parser_begin_omp_structured_block (parser);
tree stmt = cp_parser_omp_for_loop (parser, OACC_LOOP, clauses, NULL, if_p);
+ if (stmt && stmt != error_mark_node)
+ OACC_LOOP_COMBINED (stmt) = is_combined;
cp_parser_end_omp_structured_block (parser, save);
add_stmt (finish_omp_structured_block (block));
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 107d39d..efdb393 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -4900,6 +4900,10 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree>
&types,
length = mark_rvalue_use (length);
/* We need to reduce to real constant-values for checks below. */
if (length)
+ STRIP_NOPS (length);
+ if (low_bound)
+ STRIP_NOPS (low_bound);
+ if (length)
length = fold_simple (length);
if (low_bound)
low_bound = fold_simple (low_bound);
@@ -5204,6 +5208,11 @@ handle_omp_array_sections (tree c, enum
c_omp_region_type ort)
tree low_bound = TREE_PURPOSE (t);
tree length = TREE_VALUE (t);
+ if (length)
+ STRIP_NOPS (length);
+ if (low_bound)
+ STRIP_NOPS (low_bound);
+
i--;
if (low_bound
&& TREE_CODE (low_bound) == INTEGER_CST
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index bca8c85..af83a5f 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -348,7 +348,8 @@ Objective-C and Objective-C++ Dialects}.
-Wmissing-include-dirs -Wmissing-noreturn -Wno-missing-profile @gol
-Wno-multichar -Wmultistatement-macros -Wnonnull -Wnonnull-compare @gol
-Wnormalized=@r{[}none@r{|}id@r{|}nfc@r{|}nfkc@r{]} @gol
--Wnull-dereference -Wno-odr -Wopenmp-simd @gol
+-Wnull-dereference -Wno-odr @gol
+-Wopenacc-kernels-annotate-loops -Wopenmp-simd @gol
-Wno-overflow -Woverlength-strings -Wno-override-init-side-effects @gol
-Wpacked -Wno-packed-bitfield-compat -Wpacked-not-aligned -Wpadded @gol
-Wparentheses -Wno-pedantic-ms-format @gol
@@ -500,7 +501,8 @@ Objective-C and Objective-C++ Dialects}.
-fmerge-constants -fmodulo-sched -fmodulo-sched-allow-regmoves @gol
-fmove-loop-invariants -fno-branch-count-reg @gol
-fno-defer-pop -fno-fp-int-builtin-inexact -fno-function-cse @gol
--fno-guess-branch-probability -fno-inline -fno-math-errno -fno-peephole @gol
+-fno-guess-branch-probability -fno-inline -fno-math-errno @gol
+-fno-openacc-kernels-annotate-loops -fno-peephole @gol
-fno-peephole2 -fno-printf-return-value -fno-sched-interblock @gol
-fno-sched-spec -fno-signed-zeros @gol
-fno-toplevel-reorder -fno-trapping-math -fno-zero-initialized-in-bss @gol
@@ -8305,6 +8307,13 @@ Do not warn about compile-time overflow in constant
expressions.
Warn about One Definition Rule violations during link-time optimization.
Enabled by default.
+@item -Wopenacc-kernels-annotate-loops
+@opindex Wopenacc-kernels-annotate-loops
+@opindex Wno-Wopenacc-kernels-annotate-loops
+Warn about @code{for} (C/C++) or @code{DO} (Fortran) loops in OpenACC
+kernels regions that cannot be automatically annotated for
+parallelization with @option{-fopenacc-kernels-annotate-loops}.
+
@item -Wopenmp-simd
@opindex Wopenmp-simd
@opindex Wno-openmp-simd
@@ -13647,6 +13656,27 @@ approximation is enabled. The default value is 2.
@end table
+@item -fno-openacc-kernels-annotate-loops
+@opindex fno-openacc-kernels-annotate-loops
+@opindex fopenacc-kernels-annotate-loops
+@cindex kernels regions, OpenACC
+Disable automatic parallelization of unannotated loops in OpenACC
+kernels regions. The default is to attempt to add implicit
+@code{acc loop auto} annotations to loops in kernels regions if
+@option{-fopenacc} is enabled.
+
+Note that you can use @option{-Wopenacc-kernels-annotate-loops} to
+diagnose @code{for} loops that cannot be automatically annotated
+(@pxref{Warning Options}). Reasons why automatic loop annotations
+cannot be applied include premature exits, calls to functions without
+an @code{openacc routine} annotation, or unstructured control flow in
+the loop body. In C and C++, the loop variable initialization, end
+test, and increment expressions must additionally conform to
+restrictions similar to those for explicitly-annotated loops, and the
+loop variable must not be otherwise modified in the body of the loop.
+An explicit @code{acc loop} annotation disables automatic annotations
+on any nested or containing loops.
+
@end table
@node Instrumentation Options
diff --git a/gcc/testsuite/c-c++-common/goacc/classify-kernels-unparallelized.c
b/gcc/testsuite/c-c++-common/goacc/classify-kernels-unparallelized.c
index d4c4b2c..a6e2d0b 100644
--- a/gcc/testsuite/c-c++-common/goacc/classify-kernels-unparallelized.c
+++ b/gcc/testsuite/c-c++-common/goacc/classify-kernels-unparallelized.c
@@ -2,6 +2,7 @@
OpenACC kernels. */
/* { dg-additional-options "-O2" }
+ { dg-additional-options "-fno-openacc-kernels-annotate-loops" }
{ dg-additional-options "-fopt-info-optimized-omp" }
{ dg-additional-options "-fdump-tree-ompexp" }
{ dg-additional-options "-fdump-tree-parloops1-all" }
diff --git a/gcc/testsuite/c-c++-common/goacc/classify-kernels.c
b/gcc/testsuite/c-c++-common/goacc/classify-kernels.c
index 16e9b9e..bb21c9c 100644
--- a/gcc/testsuite/c-c++-common/goacc/classify-kernels.c
+++ b/gcc/testsuite/c-c++-common/goacc/classify-kernels.c
@@ -2,6 +2,7 @@
kernels. */
/* { dg-additional-options "-O2" }
+ { dg-additional-options "-fno-openacc-kernels-annotate-loops" }
{ dg-additional-options "-fopt-info-optimized-omp" }
{ dg-additional-options "-fdump-tree-ompexp" }
{ dg-additional-options "-fdump-tree-parloops1-all" }
diff --git a/gcc/testsuite/c-c++-common/goacc/combined-directives.c
b/gcc/testsuite/c-c++-common/goacc/combined-directives.c
index c2a3c57..2519f23 100644
--- a/gcc/testsuite/c-c++-common/goacc/combined-directives.c
+++ b/gcc/testsuite/c-c++-common/goacc/combined-directives.c
@@ -110,7 +110,7 @@ test ()
// { dg-final { scan-tree-dump-times "acc loop worker" 2 "gimple" } }
// { dg-final { scan-tree-dump-times "acc loop vector" 2 "gimple" } }
// { dg-final { scan-tree-dump-times "acc loop seq" 2 "gimple" } }
-// { dg-final { scan-tree-dump-times "acc loop auto" 2 "gimple" } }
+// { dg-final { scan-tree-dump-times "acc loop auto" 6 "gimple" } }
// { dg-final { scan-tree-dump-times "acc loop tile.2, 3" 2 "gimple" } }
// { dg-final { scan-tree-dump-times "acc loop independent private.i" 2
"gimple" } }
// { dg-final { scan-tree-dump-times "private.z" 2 "gimple" } }
diff --git
a/gcc/testsuite/c-c++-common/goacc/kernels-counter-var-redundant-load.c
b/gcc/testsuite/c-c++-common/goacc/kernels-counter-var-redundant-load.c
index 0304254..c37152c 100644
--- a/gcc/testsuite/c-c++-common/goacc/kernels-counter-var-redundant-load.c
+++ b/gcc/testsuite/c-c++-common/goacc/kernels-counter-var-redundant-load.c
@@ -1,4 +1,5 @@
/* { dg-additional-options "-O2" } */
+/* { dg-additional-options "-fno-openacc-kernels-annotate-loops" } */
/* { dg-additional-options "-fdump-tree-dom3" } */
#include <stdlib.h>
diff --git
a/gcc/testsuite/c-c++-common/goacc/kernels-counter-vars-function-scope.c
b/gcc/testsuite/c-c++-common/goacc/kernels-counter-vars-function-scope.c
index c475333..b1f4302 100644
--- a/gcc/testsuite/c-c++-common/goacc/kernels-counter-vars-function-scope.c
+++ b/gcc/testsuite/c-c++-common/goacc/kernels-counter-vars-function-scope.c
@@ -1,4 +1,5 @@
/* { dg-additional-options "-O2" } */
+/* { dg-additional-options "-fno-openacc-kernels-annotate-loops" } */
/* { dg-additional-options "-fdump-tree-parloops1-all" } */
/* { dg-additional-options "-fdump-tree-optimized" } */
diff --git a/gcc/testsuite/c-c++-common/goacc/kernels-double-reduction-n.c b/gcc/testsuite/c-c++-common/goacc/kernels-double-reduction-n.c
index 8f7f415..e87aab3 100644
--- a/gcc/testsuite/c-c++-common/goacc/kernels-double-reduction-n.c
+++ b/gcc/testsuite/c-c++-common/goacc/kernels-double-reduction-n.c
@@ -1,4 +1,5 @@
/* { dg-additional-options "-O2" } */
+/* { dg-additional-options "-fno-openacc-kernels-annotate-loops" } */
/* { dg-additional-options "-fopt-info-optimized-omp" } */
/* { dg-additional-options "-fdump-tree-parloops1-all" } */
/* { dg-additional-options "-fdump-tree-optimized" } */
diff --git a/gcc/testsuite/c-c++-common/goacc/kernels-double-reduction.c
b/gcc/testsuite/c-c++-common/goacc/kernels-double-reduction.c
index c11d36f..2323857 100644
--- a/gcc/testsuite/c-c++-common/goacc/kernels-double-reduction.c
+++ b/gcc/testsuite/c-c++-common/goacc/kernels-double-reduction.c
@@ -1,4 +1,5 @@
/* { dg-additional-options "-O2" } */
+/* { dg-additional-options "-fno-openacc-kernels-annotate-loops" } */
/* { dg-additional-options "-fopt-info-optimized-omp" } */
/* { dg-additional-options "-fdump-tree-parloops1-all" } */
/* { dg-additional-options "-fdump-tree-optimized" } */
diff --git a/gcc/testsuite/c-c++-common/goacc/kernels-loop-2.c
b/gcc/testsuite/c-c++-common/goacc/kernels-loop-2.c
index acef6a1..adca30b 100644
--- a/gcc/testsuite/c-c++-common/goacc/kernels-loop-2.c
+++ b/gcc/testsuite/c-c++-common/goacc/kernels-loop-2.c
@@ -1,4 +1,5 @@
/* { dg-additional-options "-O2" } */
+/* { dg-additional-options "-fno-openacc-kernels-annotate-loops" } */
/* { dg-additional-options "-fdump-tree-parloops1-all" } */
/* { dg-additional-options "-fdump-tree-optimized" } */
diff --git a/gcc/testsuite/c-c++-common/goacc/kernels-loop-3.c b/gcc/testsuite/c-c++-common/goacc/kernels-loop-3.c
index 75e2bb7..5f16085 100644
--- a/gcc/testsuite/c-c++-common/goacc/kernels-loop-3.c
+++ b/gcc/testsuite/c-c++-common/goacc/kernels-loop-3.c
@@ -1,4 +1,5 @@
/* { dg-additional-options "-O2" } */
+/* { dg-additional-options "-fno-openacc-kernels-annotate-loops" } */
/* { dg-additional-options "-fdump-tree-parloops1-all" } */
/* { dg-additional-options "-fdump-tree-optimized" } */
diff --git a/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-1.c b/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-1.c
new file mode 100644
index 0000000..c7b5ac8
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-1.c
@@ -0,0 +1,26 @@
+/* { dg-additional-options "-fopenacc -fopenacc-kernels-annotate-loops" } */
+/* { dg-additional-options "-Wopenacc-kernels-annotate-loops" } */
+/* { dg-additional-options "-fdump-tree-original" } */
+/* { dg-do compile } */
+
+/* Test that all loops in the nest are annotated. */
+
+void f (float a[16][16], float b[16][16], float c[16][16])
+{
+ int i, j, k;
+
+#pragma acc kernels copyin(a[0:16][0:16], b[0:16][0:16]) copyout(c[0:16][0:16])
+ {
+ for (i = 0; i < 16; i++) {
+ for (j = 0; j < 16; j++) {
+ float t = 0;
+ for (k = 0; k < 16; k++)
+ t += a[i][k] * b[k][j];
+ c[i][j] = t;
+ }
+ }
+ }
+
+}
+
+/* { dg-final { scan-tree-dump-times "acc loop auto" 3 "original" } } */
diff --git a/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-10.c
b/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-10.c
new file mode 100644
index 0000000..58b41d2
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-10.c
@@ -0,0 +1,32 @@
+/* { dg-additional-options "-fopenacc -fopenacc-kernels-annotate-loops" } */
+/* { dg-additional-options "-Wopenacc-kernels-annotate-loops" } */
+/* { dg-additional-options "-fdump-tree-original" } */
+/* { dg-do compile } */
+
+/* Test that a loop with a random goto in the body can't be annotated. */
+
+#define n 16
+
+float f (float *a, float *b)
+{
+ float t = 0;
+ int i;
+
+#pragma acc kernels
+ {
+ for (i = 0; i < n; i++) /* { dg-warning "loop cannot be annotated" } */
+ {
+ if (a[i] < 0)
+ {
+ t = 0;
+ goto bad;
+ }
+ t += a[i] * b[i];
+ }
+ bad:
+ ;
+ }
+ return t;
+}
+
+/* { dg-final { scan-tree-dump-times "acc loop auto" 0 "original" } } */
diff --git a/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-11.c
b/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-11.c
new file mode 100644
index 0000000..e9d2ef4
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-11.c
@@ -0,0 +1,27 @@
+/* { dg-additional-options "-fopenacc -fopenacc-kernels-annotate-loops" } */
+/* { dg-additional-options "-Wopenacc-kernels-annotate-loops" } */
+/* { dg-additional-options "-fdump-tree-original" } */
+/* { dg-do compile } */
+
+/* Test that a loop with a random label in the body triggers a warning. */
+
+#define n 16
+
+float f (float *a, float *b)
+{
+ float t = 0;
+ int i = n - 1;
+
+#pragma acc kernels
+ {
+ goto spaghetti;
+ for (i = 0; i < n; i++) /* { dg-warning "loop cannot be annotated" } */
+ {
+ spaghetti:
+ t += a[i] * b[i];
+ }
+ }
+ return t;
+}
+
+/* { dg-final { scan-tree-dump-times "acc loop auto" 0 "original" } } */
diff --git a/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-12.c
b/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-12.c
new file mode 100644
index 0000000..ba408bc
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-12.c
@@ -0,0 +1,28 @@
+/* { dg-additional-options "-fopenacc -fopenacc-kernels-annotate-loops" } */
+/* { dg-additional-options "-Wopenacc-kernels-annotate-loops" } */
+/* { dg-additional-options "-fdump-tree-original" } */
+/* { dg-do compile } */
+
+/* Test that in a situation with nested loops, a problem that prevents
+ annotation of the inner loop only still allows the outer loop to be
+ annotated. */
+
+float f (float *a, float *b, int n)
+{
+ float t = 0;
+
+#pragma acc kernels
+ {
+ for (int i = 0; i < n; i++)
+ for (int j = 0; j <= i; j++) /* { dg-warning "loop cannot be annotated"
} */
+ {
+ if (a[i] < 0 || b[j] < 0)
+ j = i;
+ else
+ t += a[i] * b[j];
+ }
+ }
+ return t;
+}
+
+/* { dg-final { scan-tree-dump-times "acc loop auto" 1 "original" } } */
diff --git a/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-13.c
b/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-13.c
new file mode 100644
index 0000000..64433e8
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-13.c
@@ -0,0 +1,27 @@
+/* { dg-additional-options "-fopenacc -fopenacc-kernels-annotate-loops" } */
+/* { dg-additional-options "-Wopenacc-kernels-annotate-loops" } */
+/* { dg-additional-options "-fdump-tree-original" } */
+/* { dg-do compile } */
+
+/* Test that in a situation with nested loops, a problem that prevents
+ annotation of the outer loop only still allows the inner loop to be
+ annotated. */
+
+float f (float *a, float *b, int n)
+{
+ float t = 0;
+
+#pragma acc kernels
+ {
+ for (int i = 0; i < n; i++) /* { dg-warning "loop cannot be
annotated" } */
+ {
+ if (a[i] < 0)
+ n = i;
+ for (int j = 0; j <= i; j++)
+ t += a[i] * b[j];
+ }
+ }
+ return t;
+}
+
+/* { dg-final { scan-tree-dump-times "acc loop auto" 1 "original" } } */
diff --git a/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-14.c
b/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-14.c
new file mode 100644
index 0000000..379e6ba
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-14.c
@@ -0,0 +1,22 @@
+/* { dg-additional-options "-fopenacc -fopenacc-kernels-annotate-loops" } */
+/* { dg-additional-options "-Wopenacc-kernels-annotate-loops" } */
+/* { dg-additional-options "-fdump-tree-original" } */
+/* { dg-do compile } */
+
+/* Test that an explicit annotation on an outer loop suppresses annotation
+ of inner loops, and produces a diagnostic. */
+
+void f (float *a, float *b)
+{
+ float t = 0;
+
+#pragma acc kernels
+ {
+#pragma acc loop seq
+ for (int l = 0; l < 20; l++)
+ for (int m = 0; m < 20; m++) /* { dg-warning "loop cannot be annotated"
} */
+ b[m] = a[m];
+ }
+}
+
+/* { dg-final { scan-tree-dump-times "acc loop auto" 0 "original" } } */
diff --git a/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-15.c
b/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-15.c
new file mode 100644
index 0000000..9a2a7ca
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-15.c
@@ -0,0 +1,22 @@
+/* { dg-additional-options "-fopenacc -fopenacc-kernels-annotate-loops" } */
+/* { dg-additional-options "-Wopenacc-kernels-annotate-loops" } */
+/* { dg-additional-options "-fdump-tree-original" } */
+/* { dg-do compile } */
+
+/* Test that an explicit annotation on an inner loop suppresses annotation
+ of outer loops, and produces a diagnostic. */
+
+void f (float *a, float *b)
+{
+ float t = 0;
+
+#pragma acc kernels
+ {
+ for (int l = 0; l < 20; l++) /* { dg-warning "loop cannot be annotated"
} */
+#pragma acc loop seq
+ for (int m = 0; m < 20; m++)
+ b[m] = a[m];
+ }
+}
+
+/* { dg-final { scan-tree-dump-times "acc loop auto" 0 "original" } } */
diff --git a/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-16.c
b/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-16.c
new file mode 100644
index 0000000..075f897
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-16.c
@@ -0,0 +1,26 @@
+/* { dg-additional-options "-fopenacc -fopenacc-kernels-annotate-loops" } */
+/* { dg-additional-options "-Wopenacc-kernels-annotate-loops" } */
+/* { dg-additional-options "-fdump-tree-original" } */
+/* { dg-do compile } */
+
+/* Test that a loop with a modification of the loop variable in the
+ body cannot be annotated. */
+
+float f (float *a, float *b, int n)
+{
+ float t = 0;
+
+#pragma acc kernels
+ {
+ for (int i = 0; i < n; i++) /* { dg-warning "loop cannot be annotated"
} */
+ {
+ if (a[i] < 0 || b[i] < 0)
+ i = n;
+ else
+ t += a[i] * b[i];
+ }
+ }
+ return t;
+}
+
+/* { dg-final { scan-tree-dump-times "acc loop auto" 0 "original" } } */
diff --git a/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-17.c
b/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-17.c
new file mode 100644
index 0000000..5076789
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-17.c
@@ -0,0 +1,26 @@
+/* { dg-additional-options "-fopenacc -fopenacc-kernels-annotate-loops" } */
+/* { dg-additional-options "-Wopenacc-kernels-annotate-loops" } */
+/* { dg-additional-options "-fdump-tree-original" } */
+/* { dg-do compile } */
+
+/* Test that a loop with a modification of the loop iteration count
+ variable in the body cannot be annotated. */
+
+float f (float *a, float *b, int n)
+{
+ float t = 0;
+
+#pragma acc kernels
+ {
+ for (int i = 0; i < n; i++) /* { dg-warning "loop cannot be annotated"
} */
+ {
+ if (a[i] < 0 || b[i] < 0)
+ n = i;
+ else
+ t += a[i] * b[i];
+ }
+ }
+ return t;
+}
+
+/* { dg-final { scan-tree-dump-times "acc loop auto" 0 "original" } } */
diff --git a/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-18.c
b/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-18.c
new file mode 100644
index 0000000..89ec644
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-18.c
@@ -0,0 +1,18 @@
+/* { dg-additional-options "-fopenacc -fopenacc-kernels-annotate-loops" } */
+/* { dg-additional-options "-Wopenacc-kernels-annotate-loops" } */
+/* { dg-additional-options "-fdump-tree-original" } */
+/* { dg-do compile } */
+
+/* Test that "acc kernels loop" directive causes annotation of the entire
+ loop nest. */
+
+void f (float *a, float *b)
+{
+#pragma acc kernels loop
+ for (int k = 0; k < 20; k++)
+ for (int l = 0; l < 20; l++)
+ for (int m = 0; m < 20; m++)
+ b[m] = a[m];
+}
+
+/* { dg-final { scan-tree-dump-times "acc loop auto" 2 "original" } } */
diff --git a/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-19.c
b/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-19.c
new file mode 100644
index 0000000..77a3b7a
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-19.c
@@ -0,0 +1,19 @@
+/* { dg-additional-options "-fopenacc -fopenacc-kernels-annotate-loops" } */
+/* { dg-additional-options "-Wopenacc-kernels-annotate-loops" } */
+/* { dg-additional-options "-fdump-tree-original" } */
+/* { dg-do compile } */
+
+/* Test that "acc kernels loop" directive causes annotation of the entire
+ loop nest in the presence of a collapse clause. */
+
+void f (float *a, float *b)
+{
+#pragma acc kernels loop collapse(2)
+ for (int k = 0; k < 20; k++)
+ for (int l = 0; l < 20; l++)
+ for (int m = 0; m < 20; m++)
+ b[m] = a[m];
+}
+
+/* { dg-final { scan-tree-dump-times "acc loop collapse.2." 1 "original" } } */
+/* { dg-final { scan-tree-dump-times "acc loop auto" 1 "original" } } */
diff --git a/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-2.c
b/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-2.c
new file mode 100644
index 0000000..9e0a946
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-2.c
@@ -0,0 +1,21 @@
+/* { dg-additional-options "-fopenacc -fopenacc-kernels-annotate-loops" } */
+/* { dg-additional-options "-Wopenacc-kernels-annotate-loops" } */
+/* { dg-additional-options "-fdump-tree-original" } */
+/* { dg-do compile } */
+
+/* Test that a loop with a variable bound can be annotated. */
+
+float f (float *a, float *b, int n)
+{
+ float t = 0;
+ int i;
+
+#pragma acc kernels
+ {
+ for (i = 0; i < n; i++)
+ t += a[i] * b[i];
+ }
+ return t;
+}
+
+/* { dg-final { scan-tree-dump-times "acc loop auto" 1 "original" } } */
diff --git a/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-20.c
b/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-20.c
new file mode 100644
index 0000000..5e3f028
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-20.c
@@ -0,0 +1,23 @@
+/* { dg-additional-options "-fopenacc -fopenacc-kernels-annotate-loops" } */
+/* { dg-additional-options "-Wopenacc-kernels-annotate-loops" } */
+/* { dg-additional-options "-fdump-tree-original" } */
+/* { dg-do compile } */
+
+/* Test that calls to built-in functions don't inhibit kernels loop
+ annotation. */
+
+void foo (int n, int *input, int *out1, int *out2)
+{
+#pragma acc kernels
+ {
+ int i;
+
+ for (i = 0; i < n; i++)
+ {
+ out1[i] = __builtin_clz (input[i]);
+ out2[i] = __builtin_popcount (input[i]);
+ }
+ }
+}
+
+/* { dg-final { scan-tree-dump-times "acc loop auto" 1 "original" } } */
diff --git a/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-21.c
b/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-21.c
new file mode 100644
index 0000000..f87444e
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-21.c
@@ -0,0 +1,42 @@
+/* { dg-additional-options "-fopenacc -fopenacc-kernels-annotate-loops" } */
+/* { dg-additional-options "-Wopenacc-kernels-annotate-loops" } */
+/* { dg-additional-options "-fdump-tree-original" } */
+/* { dg-do compile } */
+
+/* Test for rejecting annotation on loops that have various subexpressions
+ in the loop end test that are not loop-invariant. */
+
+extern int g (int);
+extern int x;
+extern int gg (int, int) __attribute__ ((const));
+
+void f (float *a, float *b, int n)
+{
+
+ int j;
+#pragma acc kernels
+ {
+ /* Non-constant function call. */
+ for (int i = 0; i < g(n); i++) /* { dg-warning "loop cannot be annotated"
} */
+ a[i] = b[i];
+
+ /* Global variable. */
+ for (int i = x; i < n + x; i++) /* { dg-warning "loop cannot be annotated"
} */
+ a[i] = b[i];
+
+ /* Explicit reference to the loop variable. */
+ for (int i = 0; i < gg (i, n); i++) /* { dg-warning "loop cannot be
annotated" } */
+ a[i] = b[i];
+
+ /* Reference to a variable that is modified in the body of the loop. */
+ j = 0;
+ for (int i = 0; i < gg (j, n); i++) /* { dg-warning "loop cannot be
annotated" } */
+ {
+ a[i] = b[i];
+ j = i;
+ }
+
+ }
+}
+
+/* { dg-final { scan-tree-dump-times "acc loop auto" 0 "original" } } */
diff --git a/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-22.c
b/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-22.c
new file mode 100644
index 0000000..6a5099d
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-22.c
@@ -0,0 +1,41 @@
+/* { dg-additional-options "-fopenacc -fopenacc-kernels-annotate-loops" } */
+/* { dg-additional-options "-Wopenacc-kernels-annotate-loops" } */
+/* { dg-additional-options "-fdump-tree-original" } */
+/* { dg-do compile } */
+
+/* Test for accepting annotation on loops that have various forms of
+ loop-invariant expressions in their end test. */
+
+extern const int x;
+extern int g (int) __attribute__ ((const));
+
+void f (float *a, float *b, int n)
+{
+
+ int j;
+#pragma acc kernels
+ {
+ /* Reversed form of comparison. */
+ for (int i = 0; n >= i; i++)
+ a[i] = b[i];
+
+ /* Constant function call. */
+ for (int i = 0; i < g(n); i++)
+ a[i] = b[i];
+
+ /* Constant global variable. */
+ for (int i = 0; i < x; i++)
+ a[i] = b[i];
+
+ /* Complicated expression involving conditionals, etc. */
+ for (int i = 0; i < ((x == 4) ? (n << 2) : (n << 3)); i++)
+ a[i] = b[i];
+
+ /* Reference to a local variable not modified in the loop. */
+ j = ((x == 4) ? (n << 2) : (n << 3));
+ for (int i = 0; i < j; i++)
+ a[i] = b[i];
+ }
+}
+
+/* { dg-final { scan-tree-dump-times "acc loop auto" 5 "original" } } */
diff --git a/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-3.c
b/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-3.c
new file mode 100644
index 0000000..f60070e
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-3.c
@@ -0,0 +1,24 @@
+/* { dg-additional-options "-fopenacc -fopenacc-kernels-annotate-loops" } */
+/* { dg-additional-options "-Wopenacc-kernels-annotate-loops" } */
+/* { dg-additional-options "-fdump-tree-original" } */
+/* { dg-do compile } */
+
+/* Test that a loop with a conditional in the body can be annotated. */
+
+#define n 16
+
+float f (float *a, float *b)
+{
+ float t = 0;
+ int i;
+
+#pragma acc kernels
+ {
+ for (i = 0; i < n; i++)
+ if (a[i] > 0 && b[i] > 0)
+ t += a[i] * b[i];
+ }
+ return t;
+}
+
+/* { dg-final { scan-tree-dump-times "acc loop auto" 1 "original" } } */
diff --git a/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-4.c
b/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-4.c
new file mode 100644
index 0000000..949871c
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-4.c
@@ -0,0 +1,34 @@
+/* { dg-additional-options "-fopenacc -fopenacc-kernels-annotate-loops" } */
+/* { dg-additional-options "-Wopenacc-kernels-annotate-loops" } */
+/* { dg-additional-options "-fdump-tree-original" } */
+/* { dg-do compile } */
+
+/* Test that a loop with a switch and break in the body can be annotated. */
+
+#define n 16
+
+float f (float *a, float *b, int state)
+{
+ float t = 0;
+ int i;
+
+#pragma acc kernels
+ {
+ for (i = 0; i < n; i++)
+ switch (state)
+ {
+ case 0:
+ default:
+ t += a[i] * b[i];
+ break;
+
+ case 1:
+ if (a[i] > 0 && b[i] > 0)
+ t += a[i] * b[i];
+ break;
+ }
+ }
+ return t;
+}
+
+/* { dg-final { scan-tree-dump-times "acc loop auto" 1 "original" } } */
diff --git a/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-5.c
b/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-5.c
new file mode 100644
index 0000000..03dfe8f
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-5.c
@@ -0,0 +1,27 @@
+/* { dg-additional-options "-fopenacc -fopenacc-kernels-annotate-loops" } */
+/* { dg-additional-options "-Wopenacc-kernels-annotate-loops" } */
+/* { dg-additional-options "-fdump-tree-original" } */
+/* { dg-do compile } */
+
+/* Test that a loop with a continue statement in the body can be annotated. */
+
+#define n 16
+
+float f (float *a, float *b)
+{
+ float t = 0;
+ int i;
+
+#pragma acc kernels
+ {
+ for (i = 0; i < n; i++)
+ {
+ if (a[i] < 0 || b[i] < 0)
+ continue;
+ t += a[i] * b[i];
+ }
+ }
+ return t;
+}
+
+/* { dg-final { scan-tree-dump-times "acc loop auto" 1 "original" } } */
diff --git a/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-6.c
b/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-6.c
new file mode 100644
index 0000000..ede6b3c
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-6.c
@@ -0,0 +1,27 @@
+/* { dg-additional-options "-fopenacc -fopenacc-kernels-annotate-loops" } */
+/* { dg-additional-options "-Wopenacc-kernels-annotate-loops" } */
+/* { dg-additional-options "-fdump-tree-original" } */
+/* { dg-do compile } */
+
+/* Test that a loop with a break statement in the body cannot be annotated. */
+
+#define n 16
+
+float f (float *a, float *b)
+{
+ float t = 0;
+ int i;
+
+#pragma acc kernels
+ {
+ for (i = 0; i < n; i++) /* { dg-warning "loop cannot be annotated" } */
+ {
+ if (a[i] < 0 || b[i] < 0)
+ break;
+ t += a[i] * b[i];
+ }
+ }
+ return t;
+}
+
+/* { dg-final { scan-tree-dump-times "acc loop auto" 0 "original" } } */
diff --git a/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-7.c
b/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-7.c
new file mode 100644
index 0000000..20ee299
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-7.c
@@ -0,0 +1,26 @@
+/* { dg-additional-options "-fopenacc -fopenacc-kernels-annotate-loops" } */
+/* { dg-additional-options "-Wopenacc-kernels-annotate-loops" } */
+/* { dg-additional-options "-fdump-tree-original" } */
+/* { dg-do compile } */
+
+/* Test that a loop with a random function call in the body cannot be
+ annotated. */
+
+extern float g (float);
+
+#define n 16
+
+float f (float *a, float *b)
+{
+ float t = 0;
+ int i;
+
+#pragma acc kernels
+ {
+ for (i = 0; i < n; i++) /* { dg-warning "loop cannot be annotated" } */
+ t += g (a[i] * b[i]);
+ }
+ return t;
+}
+
+/* { dg-final { scan-tree-dump-times "acc loop auto" 0 "original" } } */
diff --git a/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-8.c
b/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-8.c
new file mode 100644
index 0000000..796f048
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-8.c
@@ -0,0 +1,27 @@
+/* { dg-additional-options "-fopenacc -fopenacc-kernels-annotate-loops" } */
+/* { dg-additional-options "-Wopenacc-kernels-annotate-loops" } */
+/* { dg-additional-options "-fdump-tree-original" } */
+/* { dg-do compile } */
+
+/* Test that a loop with an openacc function call in the body can be
+ annotated. */
+
+#pragma acc routine worker
+extern float g (float);
+
+#define n 16
+
+float f (float *a, float *b)
+{
+ float t = 0;
+ int i;
+
+#pragma acc kernels
+ {
+ for (i = 0; i < n; i++)
+ t += g (a[i] * b[i]);
+ }
+ return t;
+}
+
+/* { dg-final { scan-tree-dump-times "acc loop auto" 1 "original" } } */
diff --git a/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-9.c
b/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-9.c
new file mode 100644
index 0000000..048f1b0
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/goacc/kernels-loop-annotation-9.c
@@ -0,0 +1,26 @@
+/* { dg-additional-options "-fopenacc -fopenacc-kernels-annotate-loops" } */
+/* { dg-additional-options "-Wopenacc-kernels-annotate-loops" } */
+/* { dg-additional-options "-fdump-tree-original" } */
+/* { dg-do compile } */
+
+/* Test that a kernels loop with a return in the body triggers a hard
+ error. */
+
+#define n 16
+
+float f (float *a, float *b)
+{
+ float t = 0;
+ int i;
+
+#pragma acc kernels
+ {
+ for (i = 0; i < n; i++)
+ {
+ if (a[i] < 0 || b[i] < 0)
+ return 0.0; /* { dg-error "invalid branch" } */
+ t += a[i] * b[i];
+ }
+ }
+ return t;
+}
diff --git a/gcc/testsuite/c-c++-common/goacc/kernels-loop-data-2.c
b/gcc/testsuite/c-c++-common/goacc/kernels-loop-data-2.c
index 7180021..9a97de6 100644
--- a/gcc/testsuite/c-c++-common/goacc/kernels-loop-data-2.c
+++ b/gcc/testsuite/c-c++-common/goacc/kernels-loop-data-2.c
@@ -1,4 +1,5 @@
/* { dg-additional-options "-O2" } */
+/* { dg-additional-options "-fno-openacc-kernels-annotate-loops" } */
/* { dg-additional-options "-fdump-tree-parloops1-all" } */
/* { dg-additional-options "-fdump-tree-optimized" } */
diff --git a/gcc/testsuite/c-c++-common/goacc/kernels-loop-data-enter-exit-2.c b/gcc/testsuite/c-c++-common/goacc/kernels-loop-data-enter-exit-2.c
index 0c9f833..31e8378 100644
--- a/gcc/testsuite/c-c++-common/goacc/kernels-loop-data-enter-exit-2.c
+++ b/gcc/testsuite/c-c++-common/goacc/kernels-loop-data-enter-exit-2.c
@@ -1,4 +1,5 @@
/* { dg-additional-options "-O2" } */
+/* { dg-additional-options "-fno-openacc-kernels-annotate-loops" } */
/* { dg-additional-options "-fdump-tree-parloops1-all" } */
/* { dg-additional-options "-fdump-tree-optimized" } */
diff --git a/gcc/testsuite/c-c++-common/goacc/kernels-loop-data-enter-exit.c b/gcc/testsuite/c-c++-common/goacc/kernels-loop-data-enter-exit.c
index 0bd21b6..ad59155 100644
--- a/gcc/testsuite/c-c++-common/goacc/kernels-loop-data-enter-exit.c
+++ b/gcc/testsuite/c-c++-common/goacc/kernels-loop-data-enter-exit.c
@@ -1,4 +1,5 @@
/* { dg-additional-options "-O2" } */
+/* { dg-additional-options "-fno-openacc-kernels-annotate-loops" } */
/* { dg-additional-options "-fdump-tree-parloops1-all" } */
/* { dg-additional-options "-fdump-tree-optimized" } */
diff --git a/gcc/testsuite/c-c++-common/goacc/kernels-loop-data-update.c b/gcc/testsuite/c-c++-common/goacc/kernels-loop-data-update.c
index dd5a841..4acffef 100644
--- a/gcc/testsuite/c-c++-common/goacc/kernels-loop-data-update.c
+++ b/gcc/testsuite/c-c++-common/goacc/kernels-loop-data-update.c
@@ -1,4 +1,5 @@
/* { dg-additional-options "-O2" } */
+/* { dg-additional-options "-fno-openacc-kernels-annotate-loops" } */
/* { dg-additional-options "-fdump-tree-parloops1-all" } */
/* { dg-additional-options "-fdump-tree-optimized" } */
diff --git a/gcc/testsuite/c-c++-common/goacc/kernels-loop-data.c b/gcc/testsuite/c-c++-common/goacc/kernels-loop-data.c
index a658182..327aa05 100644
--- a/gcc/testsuite/c-c++-common/goacc/kernels-loop-data.c
+++ b/gcc/testsuite/c-c++-common/goacc/kernels-loop-data.c
@@ -1,4 +1,5 @@
/* { dg-additional-options "-O2" } */
+/* { dg-additional-options "-fno-openacc-kernels-annotate-loops" } */
/* { dg-additional-options "-fdump-tree-parloops1-all" } */
/* { dg-additional-options "-fdump-tree-optimized" } */
diff --git a/gcc/testsuite/c-c++-common/goacc/kernels-loop-g.c b/gcc/testsuite/c-c++-common/goacc/kernels-loop-g.c
index 73b469d..26c65fe 100644
--- a/gcc/testsuite/c-c++-common/goacc/kernels-loop-g.c
+++ b/gcc/testsuite/c-c++-common/goacc/kernels-loop-g.c
@@ -1,5 +1,6 @@
/* { dg-additional-options "-O2" } */
/* { dg-additional-options "-g" } */
+/* { dg-additional-options "-fno-openacc-kernels-annotate-loops" } */
/* { dg-additional-options "-fdump-tree-parloops1-all" } */
/* { dg-additional-options "-fdump-tree-optimized" } */
diff --git a/gcc/testsuite/c-c++-common/goacc/kernels-loop-mod-not-zero.c b/gcc/testsuite/c-c++-common/goacc/kernels-loop-mod-not-zero.c
index 5592623..8955cf2 100644
--- a/gcc/testsuite/c-c++-common/goacc/kernels-loop-mod-not-zero.c
+++ b/gcc/testsuite/c-c++-common/goacc/kernels-loop-mod-not-zero.c
@@ -1,4 +1,5 @@
/* { dg-additional-options "-O2" } */
+/* { dg-additional-options "-fno-openacc-kernels-annotate-loops" } */
/* { dg-additional-options "-fdump-tree-parloops1-all" } */
/* { dg-additional-options "-fdump-tree-optimized" } */
diff --git a/gcc/testsuite/c-c++-common/goacc/kernels-loop-n.c b/gcc/testsuite/c-c++-common/goacc/kernels-loop-n.c
index e86be1b..d88a61d 100644
--- a/gcc/testsuite/c-c++-common/goacc/kernels-loop-n.c
+++ b/gcc/testsuite/c-c++-common/goacc/kernels-loop-n.c
@@ -1,4 +1,5 @@
/* { dg-additional-options "-O2" } */
+/* { dg-additional-options "-fno-openacc-kernels-annotate-loops" } */
/* { dg-additional-options "-fdump-tree-parloops1-all" } */
/* { dg-additional-options "-fdump-tree-optimized" } */
diff --git a/gcc/testsuite/c-c++-common/goacc/kernels-loop-nest.c b/gcc/testsuite/c-c++-common/goacc/kernels-loop-nest.c
index 2b0e186..5943d56 100644
--- a/gcc/testsuite/c-c++-common/goacc/kernels-loop-nest.c
+++ b/gcc/testsuite/c-c++-common/goacc/kernels-loop-nest.c
@@ -1,4 +1,5 @@
/* { dg-additional-options "-O2" } */
+/* { dg-additional-options "-fno-openacc-kernels-annotate-loops" } */
/* { dg-additional-options "-fdump-tree-parloops1-all" } */
/* { dg-additional-options "-fdump-tree-optimized" } */
diff --git a/gcc/testsuite/c-c++-common/goacc/kernels-loop.c b/gcc/testsuite/c-c++-common/goacc/kernels-loop.c
index 9619d53..ad525cd 100644
--- a/gcc/testsuite/c-c++-common/goacc/kernels-loop.c
+++ b/gcc/testsuite/c-c++-common/goacc/kernels-loop.c
@@ -1,4 +1,5 @@
/* { dg-additional-options "-O2" } */
+/* { dg-additional-options "-fno-openacc-kernels-annotate-loops" } */
/* { dg-additional-options "-fdump-tree-parloops1-all" } */
/* { dg-additional-options "-fdump-tree-optimized" } */
diff --git a/gcc/testsuite/c-c++-common/goacc/kernels-one-counter-var.c b/gcc/testsuite/c-c++-common/goacc/kernels-one-counter-var.c
index 69539b2..f799baf 100644
--- a/gcc/testsuite/c-c++-common/goacc/kernels-one-counter-var.c
+++ b/gcc/testsuite/c-c++-common/goacc/kernels-one-counter-var.c
@@ -1,4 +1,5 @@
/* { dg-additional-options "-O2" } */
+/* { dg-additional-options "-fno-openacc-kernels-annotate-loops" } */
/* { dg-additional-options "-fdump-tree-parloops1-all" } */
/* { dg-additional-options "-fdump-tree-optimized" } */
diff --git a/gcc/testsuite/c-c++-common/goacc/kernels-parallel-loop-data-enter-exit.c b/gcc/testsuite/c-c++-common/goacc/kernels-parallel-loop-data-enter-exit.c
index 81b0fee..b8093b5 100644
--- a/gcc/testsuite/c-c++-common/goacc/kernels-parallel-loop-data-enter-exit.c
+++ b/gcc/testsuite/c-c++-common/goacc/kernels-parallel-loop-data-enter-exit.c
@@ -1,4 +1,5 @@
/* { dg-additional-options "-O2" } */
+/* { dg-additional-options "-fno-openacc-kernels-annotate-loops" } */
/* { dg-additional-options "-fdump-tree-parloops1-all" } */
/* { dg-additional-options "-fdump-tree-optimized" } */
diff --git a/gcc/testsuite/c-c++-common/goacc/kernels-reduction.c b/gcc/testsuite/c-c++-common/goacc/kernels-reduction.c
index 5921b88..105cbcf 100644
--- a/gcc/testsuite/c-c++-common/goacc/kernels-reduction.c
+++ b/gcc/testsuite/c-c++-common/goacc/kernels-reduction.c
@@ -1,4 +1,5 @@
/* { dg-additional-options "-O2" } */
+/* { dg-additional-options "-fno-openacc-kernels-annotate-loops" } */
/* { dg-additional-options "-fdump-tree-parloops1-all" } */
/* { dg-additional-options "-fdump-tree-optimized" } */
diff --git a/gcc/tree.h b/gcc/tree.h
index 9ec24a3..9ee9124 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -1492,6 +1492,11 @@ class auto_suppress_location_wrappers
#define OMP_TARGET_COMBINED(NODE) \
(OMP_TARGET_CHECK (NODE)->base.private_flag)
+/* True on an OACC_LOOP statement if it is part of a combined construct,
+ for example "#pragma acc kernels loop". */
+#define OACC_LOOP_COMBINED(NODE) \
+ (OACC_LOOP_CHECK (NODE)->base.private_flag)
+
/* Memory order for OMP_ATOMIC*. */
#define OMP_ATOMIC_MEMORY_ORDER(NODE) \
(TREE_RANGE_CHECK (NODE, OMP_ATOMIC, \