[Openmp-commits] [openmp] 0555926 - [OpenMP] Tile/stripe followed by reverse (#222401)
via Openmp-commits
openmp-commits at lists.llvm.org
Wed Oct 7 05:02:28 PDT 2026
Author: Zahira Ammarguellat
Date: 2026-10-07T12:02:17Z
New Revision: 05559260278ec9d911ab9396f3258a313d229cbd
URL: https://github.com/llvm/llvm-project/commit/05559260278ec9d911ab9396f3258a313d229cbd
DIFF: https://github.com/llvm/llvm-project/commit/05559260278ec9d911ab9396f3258a313d229cbd.diff
LOG: [OpenMP] Tile/stripe followed by reverse (#222401)
`Clang` rejects `#pragma omp tile` or `#pragma omp stripe` immediately
followed by `#pragma omp reverse` on the same loop nest, emitting the
error:
`error: statement after '#pragma omp tile' must be a for loop`
See https://godbolt.org/z/d1fsfaMe9
The correct behavior is to accept it. Bare`reverse`, bare
`tile`/`stripe`, and `tile` followed by `stripe` all work. Only
`tile`/`stripe` and `reverse` fails.
This PR fixes the issue.
Added:
clang/test/OpenMP/tile_stripe_reverse_ast_print.cpp
openmp/runtime/test/transform/tile/tile-stripe-reverse.c
Modified:
clang/include/clang/AST/StmtOpenMP.h
clang/lib/AST/StmtOpenMP.cpp
clang/lib/Sema/SemaOpenMP.cpp
clang/test/OpenMP/stripe_messages.cpp
clang/test/OpenMP/tile_messages.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/AST/StmtOpenMP.h b/clang/include/clang/AST/StmtOpenMP.h
index 773e1153ccdfc..a070d97a6db1f 100644
--- a/clang/include/clang/AST/StmtOpenMP.h
+++ b/clang/include/clang/AST/StmtOpenMP.h
@@ -905,6 +905,10 @@ class OMPLoopBasedDirective : public OMPExecutableDirective {
return ignoreIntraTileHint(const_cast<Stmt *>(S));
}
+ /// Returns the statements of \p Body that precede its last statement (the
+ /// nested loop), such as the user counter update `omp reverse` emits.
+ static ArrayRef<Stmt *> getStmtsBeforeNestedLoop(Stmt *Body);
+
/// Calls the specified callback function for all the loops in \p CurStmt,
/// from the outermost to the innermost.
///
@@ -913,6 +917,13 @@ class OMPLoopBasedDirective : public OMPExecutableDirective {
/// nullptr if the loop has no such hint. Callers that need the hint (see
/// checkOpenMPIterationSpace) can peel \p HintWrapper themselves; everyone
/// else can ignore it.
+ ///
+ /// \p RelaxNestForPeeledTransformation allows a loop generated by a peeled
+ /// loop-transformation directive to hold statements before the next loop,
+ /// such as the user counter update `omp reverse` emits, so e.g. `omp tile`
+ /// accepts a nested `omp reverse` on a non-innermost loop. Only set it if the
+ /// caller re-emits those statements in the loops it generates; otherwise they
+ /// are dropped.
static bool
doForAllLoops(Stmt *CurStmt, bool TryImperfectlyNestedLoops,
unsigned NumLoops,
@@ -920,25 +931,29 @@ class OMPLoopBasedDirective : public OMPExecutableDirective {
Stmt * /*HintWrapper*/)>
Callback,
llvm::function_ref<void(OMPLoopTransformationDirective *)>
- OnTransformationCallback);
+ OnTransformationCallback,
+ bool RelaxNestForPeeledTransformation = false);
static bool
doForAllLoops(Stmt *CurStmt, bool TryImperfectlyNestedLoops,
unsigned NumLoops,
llvm::function_ref<bool(unsigned, Stmt *)> Callback,
llvm::function_ref<void(OMPLoopTransformationDirective *)>
- OnTransformationCallback) {
+ OnTransformationCallback,
+ bool RelaxNestForPeeledTransformation = false) {
auto &&NewCallback = [Callback](unsigned Cnt, Stmt *Loop, Stmt *) {
return Callback(Cnt, Loop);
};
return doForAllLoops(CurStmt, TryImperfectlyNestedLoops, NumLoops,
- NewCallback, OnTransformationCallback);
+ NewCallback, OnTransformationCallback,
+ RelaxNestForPeeledTransformation);
}
static bool
doForAllLoops(const Stmt *CurStmt, bool TryImperfectlyNestedLoops,
unsigned NumLoops,
llvm::function_ref<bool(unsigned, const Stmt *)> Callback,
llvm::function_ref<void(const OMPLoopTransformationDirective *)>
- OnTransformationCallback) {
+ OnTransformationCallback,
+ bool RelaxNestForPeeledTransformation = false) {
auto &&NewCallback = [Callback](unsigned Cnt, Stmt *CurStmt) {
return Callback(Cnt, CurStmt);
};
@@ -947,28 +962,31 @@ class OMPLoopBasedDirective : public OMPExecutableDirective {
OnTransformationCallback(A);
};
return doForAllLoops(const_cast<Stmt *>(CurStmt), TryImperfectlyNestedLoops,
- NumLoops, NewCallback, NewTransformCb);
+ NumLoops, NewCallback, NewTransformCb,
+ RelaxNestForPeeledTransformation);
}
/// Calls the specified callback function for all the loops in \p CurStmt,
/// from the outermost to the innermost.
- static bool
- doForAllLoops(Stmt *CurStmt, bool TryImperfectlyNestedLoops,
- unsigned NumLoops,
- llvm::function_ref<bool(unsigned, Stmt *)> Callback) {
+ static bool doForAllLoops(Stmt *CurStmt, bool TryImperfectlyNestedLoops,
+ unsigned NumLoops,
+ llvm::function_ref<bool(unsigned, Stmt *)> Callback,
+ bool RelaxNestForPeeledTransformation = false) {
auto &&TransformCb = [](OMPLoopTransformationDirective *) {};
return doForAllLoops(CurStmt, TryImperfectlyNestedLoops, NumLoops, Callback,
- TransformCb);
+ TransformCb, RelaxNestForPeeledTransformation);
}
static bool
doForAllLoops(const Stmt *CurStmt, bool TryImperfectlyNestedLoops,
unsigned NumLoops,
- llvm::function_ref<bool(unsigned, const Stmt *)> Callback) {
+ llvm::function_ref<bool(unsigned, const Stmt *)> Callback,
+ bool RelaxNestForPeeledTransformation = false) {
auto &&NewCallback = [Callback](unsigned Cnt, const Stmt *CurStmt) {
return Callback(Cnt, CurStmt);
};
return doForAllLoops(const_cast<Stmt *>(CurStmt), TryImperfectlyNestedLoops,
- NumLoops, NewCallback);
+ NumLoops, NewCallback,
+ RelaxNestForPeeledTransformation);
}
/// Calls the specified callback function for all the loop bodies in \p
diff --git a/clang/lib/AST/StmtOpenMP.cpp b/clang/lib/AST/StmtOpenMP.cpp
index 555e3454816ce..5ba11b2d8c42f 100644
--- a/clang/lib/AST/StmtOpenMP.cpp
+++ b/clang/lib/AST/StmtOpenMP.cpp
@@ -34,6 +34,13 @@ Stmt *OMPLoopBasedDirective::ignoreIntraTileHint(Stmt *S) {
return S;
}
+ArrayRef<Stmt *> OMPLoopBasedDirective::getStmtsBeforeNestedLoop(Stmt *Body) {
+ auto *CS = dyn_cast_or_null<CompoundStmt>(Body);
+ if (!CS || CS->size() < 2)
+ return {};
+ return ArrayRef(CS->body_begin(), CS->size() - 1);
+}
+
/// Like `Stmt::IgnoreContainers`, but stops at (and preserves) an intra-tile
/// hint wrapper so that the enclosing loop walker can deliver the hint to
/// loop-associated analysis (e.g. `collapse`). Non-hint attributed statements
@@ -133,10 +140,12 @@ OMPLoopBasedDirective::tryToFindNextInnerLoop(Stmt *CurStmt,
for (Stmt *S : CS->body()) {
if (!S)
continue;
- // Peek past an OMPCanonicalLoop wrapper and/or an intra-tile hint to
- // check whether this child is loop-like; keep the original (wrapped)
- // node in CurStmt so the hint still reaches the loop-analysis
+ // Peek past a single-child container so a nested loop or
+ // loop-transformation directive (e.g. `#pragma omp reverse` inside
+ // the body of an outer loop) is recognized here. Preserve any
+ // intra-tile hint wrapper so it still reaches the loop-analysis
// callback.
+ S = ignoreContainersKeepingIntraTileHint(S);
Stmt *Inner = S;
if (auto *CanonLoop = dyn_cast<OMPCanonicalLoop>(Inner))
Inner = CanonLoop->getLoopStmt();
@@ -152,7 +161,7 @@ OMPLoopBasedDirective::tryToFindNextInnerLoop(Stmt *CurStmt,
CurStmt = S;
continue;
}
- S = S->IgnoreContainers();
+ S = OMPLoopBasedDirective::ignoreIntraTileHint(S);
if (auto *InnerCS = dyn_cast_or_null<CompoundStmt>(S))
NextStatements.push_back(InnerCS);
}
@@ -174,9 +183,15 @@ bool OMPLoopBasedDirective::doForAllLoops(
Stmt *CurStmt, bool TryImperfectlyNestedLoops, unsigned NumLoops,
llvm::function_ref<bool(unsigned, Stmt *, Stmt *)> Callback,
llvm::function_ref<void(OMPLoopTransformationDirective *)>
- OnTransformationCallback) {
+ OnTransformationCallback,
+ bool RelaxNestForPeeledTransformation) {
CurStmt = ignoreContainersKeepingIntraTileHint(CurStmt);
for (unsigned Cnt = 0; Cnt < NumLoops; ++Cnt) {
+ // A loop generated by a peeled transformation may hold statements before
+ // the next loop (e.g. `reverse` recomputes the user counter there). They
+ // are not intervening user code, so callers that re-emit them may look
+ // through them; the rest of the nest is still checked as usual.
+ bool PeeledTransformation = false;
while (true) {
auto *Dir = dyn_cast<OMPLoopTransformationDirective>(CurStmt);
if (!Dir)
@@ -202,6 +217,7 @@ bool OMPLoopBasedDirective::doForAllLoops(
}
CurStmt = TransformedStmt;
+ PeeledTransformation = true;
}
if (auto *CanonLoop = dyn_cast<OMPCanonicalLoop>(CurStmt))
CurStmt = CanonLoop->getLoopStmt();
@@ -223,6 +239,9 @@ bool OMPLoopBasedDirective::doForAllLoops(
"Expected canonical for or range-based for loops.");
CurStmt = cast<CXXForRangeStmt>(LoopStmt)->getBody();
}
+ if (RelaxNestForPeeledTransformation && PeeledTransformation &&
+ !getStmtsBeforeNestedLoop(CurStmt).empty())
+ CurStmt = cast<CompoundStmt>(CurStmt)->body_back();
CurStmt = OMPLoopBasedDirective::tryToFindNextInnerLoop(
CurStmt, TryImperfectlyNestedLoops);
}
diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp
index efc570a8933f1..d0cf844acf6db 100644
--- a/clang/lib/Sema/SemaOpenMP.cpp
+++ b/clang/lib/Sema/SemaOpenMP.cpp
@@ -15376,6 +15376,11 @@ bool SemaOpenMP::checkTransformableLoopNest(
SmallVectorImpl<OMPLoopBasedDirective::HelperExprs> &LoopHelpers,
Stmt *&Body, SmallVectorImpl<SmallVector<Stmt *>> &OriginalInits) {
OriginalInits.emplace_back();
+ // Only tile and stripe re-emit the statements before the nested loop in a
+ // nested transformation's generated loop (see appendStmtsBeforeNestedLoop);
+ // other constructs would silently drop them.
+ bool RelaxNestForPeeledTransformation =
+ Kind == OMPD_tile || Kind == OMPD_stripe;
bool Result = OMPLoopBasedDirective::doForAllLoops(
AStmt->IgnoreContainers(), /*TryImperfectlyNestedLoops=*/false, NumLoops,
[this, &LoopHelpers, &Body, &OriginalInits,
@@ -15411,7 +15416,8 @@ bool SemaOpenMP::checkTransformableLoopNest(
},
[&OriginalInits](OMPLoopTransformationDirective *Transform) {
updatePreInits(Transform, OriginalInits.back());
- });
+ },
+ RelaxNestForPeeledTransformation);
assert(OriginalInits.back().empty() && "No preinit after innermost loop");
OriginalInits.pop_back();
return Result;
@@ -15713,7 +15719,8 @@ static void addLoopPreInits(ASTContext &Context,
/// Collect the loop statements (ForStmt or CXXRangeForStmt) of the affected
/// loop of a construct.
-static void collectLoopStmts(Stmt *AStmt, MutableArrayRef<Stmt *> LoopStmts) {
+static void collectLoopStmts(Stmt *AStmt, MutableArrayRef<Stmt *> LoopStmts,
+ bool RelaxNestForPeeledTransformation = false) {
size_t NumLoops = LoopStmts.size();
OMPLoopBasedDirective::doForAllLoops(
AStmt, /*TryImperfectlyNestedLoops=*/false, NumLoops,
@@ -15721,11 +15728,25 @@ static void collectLoopStmts(Stmt *AStmt, MutableArrayRef<Stmt *> LoopStmts) {
assert(!LoopStmts[Cnt] && "Loop statement must not yet be assigned");
LoopStmts[Cnt] = CurStmt;
return false;
- });
+ },
+ RelaxNestForPeeledTransformation);
assert(!is_contained(LoopStmts, nullptr) &&
"Expecting a loop statement for each affected loop");
}
+/// A non-innermost affected loop generated by a nested loop transformation
+/// (e.g. `omp reverse`) computes the user counter in statements ahead of the
+/// next loop. A construct that rebuilds the nest only keeps the innermost
+/// body, so it must re-emit these statements in the matching loop.
+static void appendStmtsBeforeNestedLoop(Stmt *LoopStmt,
+ SmallVectorImpl<Stmt *> &BodyParts) {
+ Stmt *Body = isa<ForStmt>(LoopStmt)
+ ? cast<ForStmt>(LoopStmt)->getBody()
+ : cast<CXXForRangeStmt>(LoopStmt)->getBody();
+ llvm::append_range(BodyParts,
+ OMPLoopBasedDirective::getStmtsBeforeNestedLoop(Body));
+}
+
/// Build and return a DeclRefExpr for the floor induction variable using the
/// SemaRef and the provided parameters.
static Expr *makeFloorIVRef(Sema &SemaRef, ArrayRef<VarDecl *> FloorIndVars,
@@ -15774,7 +15795,7 @@ StmtResult SemaOpenMP::ActOnOpenMPTileDirective(ArrayRef<OMPClause *> Clauses,
// Collect all affected loop statements.
SmallVector<Stmt *> LoopStmts(NumLoops, nullptr);
- collectLoopStmts(AStmt, LoopStmts);
+ collectLoopStmts(AStmt, LoopStmts, /*RelaxNestForPeeledTransformation=*/true);
SmallVector<Stmt *, 4> PreInits;
CaptureVars CopyTransformer(SemaRef);
@@ -16023,6 +16044,8 @@ StmtResult SemaOpenMP::ActOnOpenMPTileDirective(ArrayRef<OMPClause *> Clauses,
BodyParts.append(LoopHelper.Updates.begin(), LoopHelper.Updates.end());
if (auto *SourceCXXFor = dyn_cast<CXXForRangeStmt>(LoopStmt))
BodyParts.push_back(SourceCXXFor->getLoopVarStmt());
+ if (static_cast<unsigned>(I) + 1 < NumLoops)
+ appendStmtsBeforeNestedLoop(LoopStmt, BodyParts);
BodyParts.push_back(Inner);
Inner = CompoundStmt::Create(Context, BodyParts, FPOptionsOverride(),
Inner->getBeginLoc(), Inner->getEndLoc());
@@ -16131,7 +16154,7 @@ StmtResult SemaOpenMP::ActOnOpenMPStripeDirective(ArrayRef<OMPClause *> Clauses,
// Collect all affected loop statements.
SmallVector<Stmt *> LoopStmts(NumLoops, nullptr);
- collectLoopStmts(AStmt, LoopStmts);
+ collectLoopStmts(AStmt, LoopStmts, /*RelaxNestForPeeledTransformation=*/true);
SmallVector<Stmt *, 4> PreInits;
CaptureVars CopyTransformer(SemaRef);
@@ -16293,6 +16316,8 @@ StmtResult SemaOpenMP::ActOnOpenMPStripeDirective(ArrayRef<OMPClause *> Clauses,
BodyParts.append(LoopHelper.Updates.begin(), LoopHelper.Updates.end());
if (auto *SourceCXXFor = dyn_cast<CXXForRangeStmt>(LoopStmt))
BodyParts.push_back(SourceCXXFor->getLoopVarStmt());
+ if (static_cast<unsigned>(I) + 1 < NumLoops)
+ appendStmtsBeforeNestedLoop(LoopStmt, BodyParts);
BodyParts.push_back(Inner);
Inner = CompoundStmt::Create(Context, BodyParts, FPOptionsOverride(),
Inner->getBeginLoc(), Inner->getEndLoc());
diff --git a/clang/test/OpenMP/stripe_messages.cpp b/clang/test/OpenMP/stripe_messages.cpp
index 3f171f40d71d9..2e4675c35cc6c 100644
--- a/clang/test/OpenMP/stripe_messages.cpp
+++ b/clang/test/OpenMP/stripe_messages.cpp
@@ -169,3 +169,16 @@ void f() {
;
}
} // namespace GH139433
+
+// The loop generated by 'reverse' may be a non-innermost affected loop, but
+// user code between it and the next affected loop is still intervening code.
+void stripe_of_reverse_intervening() {
+ // expected-error at +3 {{statement after '#pragma omp stripe' must be a for loop}}
+#pragma omp stripe sizes(2, 2)
+#pragma omp reverse
+ for (int i = 0; i < 7; ++i) {
+ int k = 3;
+ for (int j = 0; j < 7; ++j)
+ ;
+ }
+}
diff --git a/clang/test/OpenMP/tile_messages.cpp b/clang/test/OpenMP/tile_messages.cpp
index 021262ac9a54a..72ceef71566d6 100644
--- a/clang/test/OpenMP/tile_messages.cpp
+++ b/clang/test/OpenMP/tile_messages.cpp
@@ -210,3 +210,16 @@ void unroll_of_tile() {
;
}
} // namespace consumed_by_transformation
+
+// The loop generated by 'reverse' may be a non-innermost affected loop, but
+// user code between it and the next affected loop is still intervening code.
+void tile_of_reverse_intervening() {
+ // expected-error at +3 {{statement after '#pragma omp tile' must be a for loop}}
+#pragma omp tile sizes(2, 2)
+#pragma omp reverse
+ for (int i = 0; i < 7; ++i) {
+ int k = 3;
+ for (int j = 0; j < 7; ++j)
+ ;
+ }
+}
diff --git a/clang/test/OpenMP/tile_stripe_reverse_ast_print.cpp b/clang/test/OpenMP/tile_stripe_reverse_ast_print.cpp
new file mode 100644
index 0000000000000..5eee992f50483
--- /dev/null
+++ b/clang/test/OpenMP/tile_stripe_reverse_ast_print.cpp
@@ -0,0 +1,79 @@
+// Check no warnings/errors
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=60 \
+// RUN: -fsyntax-only -verify %s
+// expected-no-diagnostics
+
+// Check AST and unparsing
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=60 \
+// RUN: -ast-print %s | FileCheck %s --check-prefix=PRINT
+
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=60 \
+// RUN: -emit-pch -o %t %s
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=60 \
+// RUN: -include-pch %t -ast-print %s | FileCheck %s --check-prefix=PRINT
+
+#ifndef HEADER
+#define HEADER
+
+extern "C" void body(...);
+
+// PRINT-LABEL: void tile_then_reverse(
+void tile_then_reverse() {
+ // PRINT: #pragma omp tile sizes(2, 2)
+ // PRINT: #pragma omp reverse
+#pragma omp tile sizes(2, 2)
+#pragma omp reverse
+ for (int i = 0; i < 20; ++i)
+ for (int j = 0; j < 20; ++j)
+ body(i, j);
+}
+
+// PRINT-LABEL: void stripe_then_reverse(
+void stripe_then_reverse() {
+ // PRINT: #pragma omp stripe sizes(2, 2)
+ // PRINT: #pragma omp reverse
+#pragma omp stripe sizes(2, 2)
+#pragma omp reverse
+ for (int i = 0; i < 20; ++i)
+ for (int j = 0; j < 20; ++j)
+ body(i, j);
+}
+
+// PRINT-LABEL: void tile_over_inner_reverse(
+void tile_over_inner_reverse() {
+ // PRINT: #pragma omp tile sizes(2, 2)
+#pragma omp tile sizes(2, 2)
+#pragma omp reverse
+ for (int j = 0; j < 20; ++j) {
+ // PRINT: #pragma omp reverse
+#pragma omp reverse
+ for (int i = 0; i < 20; ++i)
+ body(j, i);
+ }
+}
+
+// PRINT-LABEL: void stripe_over_inner_reverse(
+void stripe_over_inner_reverse() {
+ // PRINT: #pragma omp stripe sizes(2, 2)
+#pragma omp stripe sizes(2, 2)
+#pragma omp reverse
+ for (int j = 0; j < 20; ++j) {
+ // PRINT: #pragma omp reverse
+#pragma omp reverse
+ for (int i = 0; i < 20; ++i)
+ body(j, i);
+ }
+}
+
+// PRINT-LABEL: void for_collapse_then_reverse(
+void for_collapse_then_reverse() {
+ // PRINT: #pragma omp for collapse(2)
+ // PRINT: #pragma omp reverse
+#pragma omp for collapse(2)
+#pragma omp reverse
+ for (int j = 0; j < 20; j += 2)
+ for (int i = 0; i < 20; i += 3)
+ body(j, i);
+}
+
+#endif
diff --git a/openmp/runtime/test/transform/tile/tile-stripe-reverse.c b/openmp/runtime/test/transform/tile/tile-stripe-reverse.c
new file mode 100644
index 0000000000000..7f599737b1e8a
--- /dev/null
+++ b/openmp/runtime/test/transform/tile/tile-stripe-reverse.c
@@ -0,0 +1,94 @@
+// RUN: %libomp-compile -fopenmp-version=60 && %libomp-run \
+// RUN: | FileCheck %s --match-full-lines
+
+// tile/stripe over a reverse that is not the innermost affected loop must
+// keep the reversal (regression test for #222401).
+
+#ifndef HEADER
+#define HEADER
+
+#include <stdio.h>
+#include <stdlib.h>
+
+int main() {
+ printf("tile\n");
+#pragma omp tile sizes(2, 2)
+#pragma omp reverse
+ for (int i = 0; i < 4; ++i)
+ for (int j = 0; j < 4; ++j)
+ printf("i=%d j=%d\n", i, j);
+
+ printf("stripe\n");
+#pragma omp stripe sizes(2, 2)
+#pragma omp reverse
+ for (int i = 0; i < 4; ++i)
+ for (int j = 0; j < 4; ++j)
+ printf("i=%d j=%d\n", i, j);
+
+ printf("nested\n");
+#pragma omp tile sizes(2, 2)
+#pragma omp reverse
+ for (int j = 0; j < 4; ++j) {
+#pragma omp reverse
+ for (int i = 0; i < 4; ++i)
+ printf("j=%d i=%d\n", j, i);
+ }
+
+ printf("done\n");
+ return EXIT_SUCCESS;
+}
+
+#endif /* HEADER */
+
+// CHECK: tile
+// CHECK-NEXT: i=3 j=0
+// CHECK-NEXT: i=3 j=1
+// CHECK-NEXT: i=2 j=0
+// CHECK-NEXT: i=2 j=1
+// CHECK-NEXT: i=3 j=2
+// CHECK-NEXT: i=3 j=3
+// CHECK-NEXT: i=2 j=2
+// CHECK-NEXT: i=2 j=3
+// CHECK-NEXT: i=1 j=0
+// CHECK-NEXT: i=1 j=1
+// CHECK-NEXT: i=0 j=0
+// CHECK-NEXT: i=0 j=1
+// CHECK-NEXT: i=1 j=2
+// CHECK-NEXT: i=1 j=3
+// CHECK-NEXT: i=0 j=2
+// CHECK-NEXT: i=0 j=3
+// CHECK-NEXT: stripe
+// CHECK-NEXT: i=3 j=0
+// CHECK-NEXT: i=3 j=1
+// CHECK-NEXT: i=2 j=0
+// CHECK-NEXT: i=2 j=1
+// CHECK-NEXT: i=3 j=2
+// CHECK-NEXT: i=3 j=3
+// CHECK-NEXT: i=2 j=2
+// CHECK-NEXT: i=2 j=3
+// CHECK-NEXT: i=1 j=0
+// CHECK-NEXT: i=1 j=1
+// CHECK-NEXT: i=0 j=0
+// CHECK-NEXT: i=0 j=1
+// CHECK-NEXT: i=1 j=2
+// CHECK-NEXT: i=1 j=3
+// CHECK-NEXT: i=0 j=2
+// CHECK-NEXT: i=0 j=3
+// CHECK-NEXT: nested
+// CHECK-NEXT: j=3 i=3
+// CHECK-NEXT: j=3 i=2
+// CHECK-NEXT: j=2 i=3
+// CHECK-NEXT: j=2 i=2
+// CHECK-NEXT: j=3 i=1
+// CHECK-NEXT: j=3 i=0
+// CHECK-NEXT: j=2 i=1
+// CHECK-NEXT: j=2 i=0
+// CHECK-NEXT: j=1 i=3
+// CHECK-NEXT: j=1 i=2
+// CHECK-NEXT: j=0 i=3
+// CHECK-NEXT: j=0 i=2
+// CHECK-NEXT: j=1 i=1
+// CHECK-NEXT: j=1 i=0
+// CHECK-NEXT: j=0 i=1
+// CHECK-NEXT: j=0 i=0
+// CHECK-NEXT: done
More information about the Openmp-commits
mailing list