[Clang][OpenMP] Add reverse directive (#92916)

Add the reverse directive which will be introduced in the upcoming
OpenMP 6.0 specification. A preview has been published in [Technical
Report 12](https://www.openmp.org/wp-content/uploads/openmp-TR12.pdf).

---------

Co-authored-by: Alexey Bataev <a.bataev@outlook.com>
This commit is contained in:
Michael Kruse 2024-07-18 10:35:32 +02:00 committed by GitHub
parent 26cb88e321
commit 80865c01e1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
30 changed files with 2799 additions and 3 deletions

View File

@ -2146,6 +2146,10 @@ enum CXCursorKind {
*/
CXCursor_OMPScopeDirective = 306,
/** OpenMP reverse directive.
*/
CXCursor_OMPReverseDirective = 307,
/** OpenACC Compute Construct.
*/
CXCursor_OpenACCComputeConstruct = 320,

View File

@ -3032,6 +3032,9 @@ DEF_TRAVERSE_STMT(OMPTileDirective,
DEF_TRAVERSE_STMT(OMPUnrollDirective,
{ TRY_TO(TraverseOMPExecutableDirective(S)); })
DEF_TRAVERSE_STMT(OMPReverseDirective,
{ TRY_TO(TraverseOMPExecutableDirective(S)); })
DEF_TRAVERSE_STMT(OMPForDirective,
{ TRY_TO(TraverseOMPExecutableDirective(S)); })

View File

@ -1007,8 +1007,9 @@ public:
Stmt *getPreInits() const;
static bool classof(const Stmt *T) {
return T->getStmtClass() == OMPTileDirectiveClass ||
T->getStmtClass() == OMPUnrollDirectiveClass;
Stmt::StmtClass C = T->getStmtClass();
return C == OMPTileDirectiveClass || C == OMPUnrollDirectiveClass ||
C == OMPReverseDirectiveClass;
}
};
@ -5711,6 +5712,70 @@ public:
}
};
/// Represents the '#pragma omp reverse' loop transformation directive.
///
/// \code
/// #pragma omp reverse
/// for (int i = 0; i < n; ++i)
/// ...
/// \endcode
class OMPReverseDirective final : public OMPLoopTransformationDirective {
friend class ASTStmtReader;
friend class OMPExecutableDirective;
/// Offsets of child members.
enum {
PreInitsOffset = 0,
TransformedStmtOffset,
};
explicit OMPReverseDirective(SourceLocation StartLoc, SourceLocation EndLoc)
: OMPLoopTransformationDirective(OMPReverseDirectiveClass,
llvm::omp::OMPD_reverse, StartLoc,
EndLoc, 1) {}
void setPreInits(Stmt *PreInits) {
Data->getChildren()[PreInitsOffset] = PreInits;
}
void setTransformedStmt(Stmt *S) {
Data->getChildren()[TransformedStmtOffset] = S;
}
public:
/// Create a new AST node representation for '#pragma omp reverse'.
///
/// \param C Context of the AST.
/// \param StartLoc Location of the introducer (e.g. the 'omp' token).
/// \param EndLoc Location of the directive's end (e.g. the tok::eod).
/// \param AssociatedStmt The outermost associated loop.
/// \param TransformedStmt The loop nest after tiling, or nullptr in
/// dependent contexts.
/// \param PreInits Helper preinits statements for the loop nest.
static OMPReverseDirective *
Create(const ASTContext &C, SourceLocation StartLoc, SourceLocation EndLoc,
Stmt *AssociatedStmt, Stmt *TransformedStmt, Stmt *PreInits);
/// Build an empty '#pragma omp reverse' AST node for deserialization.
///
/// \param C Context of the AST.
/// \param NumClauses Number of clauses to allocate.
static OMPReverseDirective *CreateEmpty(const ASTContext &C);
/// Gets/sets the associated loops after the transformation, i.e. after
/// de-sugaring.
Stmt *getTransformedStmt() const {
return Data->getChildren()[TransformedStmtOffset];
}
/// Return preinits statement.
Stmt *getPreInits() const { return Data->getChildren()[PreInitsOffset]; }
static bool classof(const Stmt *T) {
return T->getStmtClass() == OMPReverseDirectiveClass;
}
};
/// This represents '#pragma omp scan' directive.
///
/// \code

View File

@ -230,6 +230,7 @@ def OMPSimdDirective : StmtNode<OMPLoopDirective>;
def OMPLoopTransformationDirective : StmtNode<OMPLoopBasedDirective, 1>;
def OMPTileDirective : StmtNode<OMPLoopTransformationDirective>;
def OMPUnrollDirective : StmtNode<OMPLoopTransformationDirective>;
def OMPReverseDirective : StmtNode<OMPLoopTransformationDirective>;
def OMPForDirective : StmtNode<OMPLoopDirective>;
def OMPForSimdDirective : StmtNode<OMPLoopDirective>;
def OMPSectionsDirective : StmtNode<OMPExecutableDirective>;

View File

@ -423,6 +423,9 @@ public:
StmtResult ActOnOpenMPUnrollDirective(ArrayRef<OMPClause *> Clauses,
Stmt *AStmt, SourceLocation StartLoc,
SourceLocation EndLoc);
/// Called on well-formed '#pragma omp reverse'.
StmtResult ActOnOpenMPReverseDirective(Stmt *AStmt, SourceLocation StartLoc,
SourceLocation EndLoc);
/// Called on well-formed '\#pragma omp for' after parsing
/// of the associated statement.
StmtResult

View File

@ -1895,6 +1895,7 @@ enum StmtCode {
STMT_OMP_SIMD_DIRECTIVE,
STMT_OMP_TILE_DIRECTIVE,
STMT_OMP_UNROLL_DIRECTIVE,
STMT_OMP_REVERSE_DIRECTIVE,
STMT_OMP_FOR_DIRECTIVE,
STMT_OMP_FOR_SIMD_DIRECTIVE,
STMT_OMP_SECTIONS_DIRECTIVE,

View File

@ -449,6 +449,24 @@ OMPUnrollDirective *OMPUnrollDirective::CreateEmpty(const ASTContext &C,
SourceLocation(), SourceLocation());
}
OMPReverseDirective *
OMPReverseDirective::Create(const ASTContext &C, SourceLocation StartLoc,
SourceLocation EndLoc, Stmt *AssociatedStmt,
Stmt *TransformedStmt, Stmt *PreInits) {
OMPReverseDirective *Dir = createDirective<OMPReverseDirective>(
C, std::nullopt, AssociatedStmt, TransformedStmtOffset + 1, StartLoc,
EndLoc);
Dir->setTransformedStmt(TransformedStmt);
Dir->setPreInits(PreInits);
return Dir;
}
OMPReverseDirective *OMPReverseDirective::CreateEmpty(const ASTContext &C) {
return createEmptyDirective<OMPReverseDirective>(
C, /*NumClauses=*/0, /*HasAssociatedStmt=*/true,
TransformedStmtOffset + 1, SourceLocation(), SourceLocation());
}
OMPForSimdDirective *
OMPForSimdDirective::Create(const ASTContext &C, SourceLocation StartLoc,
SourceLocation EndLoc, unsigned CollapsedNum,

View File

@ -763,6 +763,11 @@ void StmtPrinter::VisitOMPUnrollDirective(OMPUnrollDirective *Node) {
PrintOMPExecutableDirective(Node);
}
void StmtPrinter::VisitOMPReverseDirective(OMPReverseDirective *Node) {
Indent() << "#pragma omp reverse";
PrintOMPExecutableDirective(Node);
}
void StmtPrinter::VisitOMPForDirective(OMPForDirective *Node) {
Indent() << "#pragma omp for";
PrintOMPExecutableDirective(Node);

View File

@ -985,6 +985,10 @@ void StmtProfiler::VisitOMPUnrollDirective(const OMPUnrollDirective *S) {
VisitOMPLoopTransformationDirective(S);
}
void StmtProfiler::VisitOMPReverseDirective(const OMPReverseDirective *S) {
VisitOMPLoopTransformationDirective(S);
}
void StmtProfiler::VisitOMPForDirective(const OMPForDirective *S) {
VisitOMPLoopDirective(S);
}

View File

@ -684,7 +684,7 @@ bool clang::isOpenMPLoopBoundSharingDirective(OpenMPDirectiveKind Kind) {
}
bool clang::isOpenMPLoopTransformationDirective(OpenMPDirectiveKind DKind) {
return DKind == OMPD_tile || DKind == OMPD_unroll;
return DKind == OMPD_tile || DKind == OMPD_unroll || DKind == OMPD_reverse;
}
bool clang::isOpenMPCombinedParallelADirective(OpenMPDirectiveKind DKind) {

View File

@ -222,6 +222,9 @@ void CodeGenFunction::EmitStmt(const Stmt *S, ArrayRef<const Attr *> Attrs) {
case Stmt::OMPUnrollDirectiveClass:
EmitOMPUnrollDirective(cast<OMPUnrollDirective>(*S));
break;
case Stmt::OMPReverseDirectiveClass:
EmitOMPReverseDirective(cast<OMPReverseDirective>(*S));
break;
case Stmt::OMPForDirectiveClass:
EmitOMPForDirective(cast<OMPForDirective>(*S));
break;

View File

@ -187,6 +187,8 @@ class OMPLoopScope : public CodeGenFunction::RunCleanupsScope {
PreInits = Tile->getPreInits();
} else if (const auto *Unroll = dyn_cast<OMPUnrollDirective>(&S)) {
PreInits = Unroll->getPreInits();
} else if (const auto *Reverse = dyn_cast<OMPReverseDirective>(&S)) {
PreInits = Reverse->getPreInits();
} else {
llvm_unreachable("Unknown loop-based directive kind.");
}
@ -2762,6 +2764,12 @@ void CodeGenFunction::EmitOMPTileDirective(const OMPTileDirective &S) {
EmitStmt(S.getTransformedStmt());
}
void CodeGenFunction::EmitOMPReverseDirective(const OMPReverseDirective &S) {
// Emit the de-sugared statement.
OMPTransformDirectiveScopeRAII ReverseScope(*this, &S);
EmitStmt(S.getTransformedStmt());
}
void CodeGenFunction::EmitOMPUnrollDirective(const OMPUnrollDirective &S) {
bool UseOMPIRBuilder = CGM.getLangOpts().OpenMPIRBuilder;

View File

@ -3817,6 +3817,7 @@ public:
void EmitOMPSimdDirective(const OMPSimdDirective &S);
void EmitOMPTileDirective(const OMPTileDirective &S);
void EmitOMPUnrollDirective(const OMPUnrollDirective &S);
void EmitOMPReverseDirective(const OMPReverseDirective &S);
void EmitOMPForDirective(const OMPForDirective &S);
void EmitOMPForSimdDirective(const OMPForSimdDirective &S);
void EmitOMPSectionsDirective(const OMPSectionsDirective &S);

View File

@ -2885,6 +2885,7 @@ StmtResult Parser::ParseOpenMPDeclarativeOrExecutableDirective(
}
break;
}
case OMPD_reverse:
case OMPD_declare_target: {
SourceLocation DTLoc = ConsumeAnyToken();
bool HasClauses = Tok.isNot(tok::annot_pragma_openmp_end);

View File

@ -1466,6 +1466,7 @@ CanThrowResult Sema::canThrow(const Stmt *S) {
case Stmt::OMPSimdDirectiveClass:
case Stmt::OMPTileDirectiveClass:
case Stmt::OMPUnrollDirectiveClass:
case Stmt::OMPReverseDirectiveClass:
case Stmt::OMPSingleDirectiveClass:
case Stmt::OMPTargetDataDirectiveClass:
case Stmt::OMPTargetDirectiveClass:

View File

@ -4405,6 +4405,7 @@ void SemaOpenMP::ActOnOpenMPRegionStart(OpenMPDirectiveKind DKind,
case OMPD_section:
case OMPD_tile:
case OMPD_unroll:
case OMPD_reverse:
break;
default:
processCapturedRegions(SemaRef, DKind, CurScope,
@ -6284,6 +6285,11 @@ StmtResult SemaOpenMP::ActOnOpenMPExecutableDirective(
Res = ActOnOpenMPUnrollDirective(ClausesWithImplicit, AStmt, StartLoc,
EndLoc);
break;
case OMPD_reverse:
assert(ClausesWithImplicit.empty() &&
"reverse directive does not support any clauses");
Res = ActOnOpenMPReverseDirective(AStmt, StartLoc, EndLoc);
break;
case OMPD_for:
Res = ActOnOpenMPForDirective(ClausesWithImplicit, AStmt, StartLoc, EndLoc,
VarsWithInheritedDSA);
@ -14040,6 +14046,8 @@ bool SemaOpenMP::checkTransformableLoopNest(
DependentPreInits = Dir->getPreInits();
else if (auto *Dir = dyn_cast<OMPUnrollDirective>(Transform))
DependentPreInits = Dir->getPreInits();
else if (auto *Dir = dyn_cast<OMPReverseDirective>(Transform))
DependentPreInits = Dir->getPreInits();
else
llvm_unreachable("Unhandled loop transformation");
@ -14658,6 +14666,193 @@ StmtResult SemaOpenMP::ActOnOpenMPUnrollDirective(ArrayRef<OMPClause *> Clauses,
buildPreInits(Context, PreInits));
}
StmtResult SemaOpenMP::ActOnOpenMPReverseDirective(Stmt *AStmt,
SourceLocation StartLoc,
SourceLocation EndLoc) {
ASTContext &Context = getASTContext();
Scope *CurScope = SemaRef.getCurScope();
// Empty statement should only be possible if there already was an error.
if (!AStmt)
return StmtError();
constexpr unsigned NumLoops = 1;
Stmt *Body = nullptr;
SmallVector<OMPLoopBasedDirective::HelperExprs, NumLoops> LoopHelpers(
NumLoops);
SmallVector<SmallVector<Stmt *, 0>, NumLoops + 1> OriginalInits;
if (!checkTransformableLoopNest(OMPD_reverse, AStmt, NumLoops, LoopHelpers,
Body, OriginalInits))
return StmtError();
// Delay applying the transformation to when template is completely
// instantiated.
if (SemaRef.CurContext->isDependentContext())
return OMPReverseDirective::Create(Context, StartLoc, EndLoc, AStmt,
nullptr, nullptr);
assert(LoopHelpers.size() == NumLoops &&
"Expecting a single-dimensional loop iteration space");
assert(OriginalInits.size() == NumLoops &&
"Expecting a single-dimensional loop iteration space");
OMPLoopBasedDirective::HelperExprs &LoopHelper = LoopHelpers.front();
// Find the loop statement.
Stmt *LoopStmt = nullptr;
collectLoopStmts(AStmt, {LoopStmt});
// Determine the PreInit declarations.
SmallVector<Stmt *> PreInits;
addLoopPreInits(Context, LoopHelper, LoopStmt, OriginalInits[0], PreInits);
auto *IterationVarRef = cast<DeclRefExpr>(LoopHelper.IterationVarRef);
QualType IVTy = IterationVarRef->getType();
uint64_t IVWidth = Context.getTypeSize(IVTy);
auto *OrigVar = cast<DeclRefExpr>(LoopHelper.Counters.front());
// Iteration variable SourceLocations.
SourceLocation OrigVarLoc = OrigVar->getExprLoc();
SourceLocation OrigVarLocBegin = OrigVar->getBeginLoc();
SourceLocation OrigVarLocEnd = OrigVar->getEndLoc();
// Locations pointing to the transformation.
SourceLocation TransformLoc = StartLoc;
SourceLocation TransformLocBegin = StartLoc;
SourceLocation TransformLocEnd = EndLoc;
// Internal variable names.
std::string OrigVarName = OrigVar->getNameInfo().getAsString();
SmallString<64> ForwardIVName(".forward.iv.");
ForwardIVName += OrigVarName;
SmallString<64> ReversedIVName(".reversed.iv.");
ReversedIVName += OrigVarName;
// LoopHelper.Updates will read the logical iteration number from
// LoopHelper.IterationVarRef, compute the value of the user loop counter of
// that logical iteration from it, then assign it to the user loop counter
// variable. We cannot directly use LoopHelper.IterationVarRef as the
// induction variable of the generated loop because it may cause an underflow:
// \code{.c}
// for (unsigned i = 0; i < n; ++i)
// body(i);
// \endcode
//
// Naive reversal:
// \code{.c}
// for (unsigned i = n-1; i >= 0; --i)
// body(i);
// \endcode
//
// Instead, we introduce a new iteration variable representing the logical
// iteration counter of the original loop, convert it to the logical iteration
// number of the reversed loop, then let LoopHelper.Updates compute the user's
// loop iteration variable from it.
// \code{.cpp}
// for (auto .forward.iv = 0; .forward.iv < n; ++.forward.iv) {
// auto .reversed.iv = n - .forward.iv - 1;
// i = (.reversed.iv + 0) * 1; // LoopHelper.Updates
// body(i); // Body
// }
// \endcode
// Subexpressions with more than one use. One of the constraints of an AST is
// that every node object must appear at most once, hence we define a lambda
// that creates a new AST node at every use.
CaptureVars CopyTransformer(SemaRef);
auto MakeNumIterations = [&CopyTransformer, &LoopHelper]() -> Expr * {
return AssertSuccess(
CopyTransformer.TransformExpr(LoopHelper.NumIterations));
};
// Create the iteration variable for the forward loop (from 0 to n-1).
VarDecl *ForwardIVDecl =
buildVarDecl(SemaRef, {}, IVTy, ForwardIVName, nullptr, OrigVar);
auto MakeForwardRef = [&SemaRef = this->SemaRef, ForwardIVDecl, IVTy,
OrigVarLoc]() {
return buildDeclRefExpr(SemaRef, ForwardIVDecl, IVTy, OrigVarLoc);
};
// Iteration variable for the reversed induction variable (from n-1 downto 0):
// Reuse the iteration variable created by checkOpenMPLoop.
auto *ReversedIVDecl = cast<VarDecl>(IterationVarRef->getDecl());
ReversedIVDecl->setDeclName(
&SemaRef.PP.getIdentifierTable().get(ReversedIVName));
// For init-statement:
// \code{.cpp}
// auto .forward.iv = 0;
// \endcode
auto *Zero = IntegerLiteral::Create(Context, llvm::APInt::getZero(IVWidth),
ForwardIVDecl->getType(), OrigVarLoc);
SemaRef.AddInitializerToDecl(ForwardIVDecl, Zero, /*DirectInit=*/false);
StmtResult Init = new (Context)
DeclStmt(DeclGroupRef(ForwardIVDecl), OrigVarLocBegin, OrigVarLocEnd);
if (!Init.isUsable())
return StmtError();
// Forward iv cond-expression:
// \code{.cpp}
// .forward.iv < MakeNumIterations()
// \endcode
ExprResult Cond =
SemaRef.BuildBinOp(CurScope, LoopHelper.Cond->getExprLoc(), BO_LT,
MakeForwardRef(), MakeNumIterations());
if (!Cond.isUsable())
return StmtError();
// Forward incr-statement:
// \code{.c}
// ++.forward.iv
// \endcode
ExprResult Incr = SemaRef.BuildUnaryOp(CurScope, LoopHelper.Inc->getExprLoc(),
UO_PreInc, MakeForwardRef());
if (!Incr.isUsable())
return StmtError();
// Reverse the forward-iv:
// \code{.cpp}
// auto .reversed.iv = MakeNumIterations() - 1 - .forward.iv
// \endcode
auto *One = IntegerLiteral::Create(Context, llvm::APInt(IVWidth, 1), IVTy,
TransformLoc);
ExprResult Minus = SemaRef.BuildBinOp(CurScope, TransformLoc, BO_Sub,
MakeNumIterations(), One);
if (!Minus.isUsable())
return StmtError();
Minus = SemaRef.BuildBinOp(CurScope, TransformLoc, BO_Sub, Minus.get(),
MakeForwardRef());
if (!Minus.isUsable())
return StmtError();
StmtResult InitReversed = new (Context) DeclStmt(
DeclGroupRef(ReversedIVDecl), TransformLocBegin, TransformLocEnd);
if (!InitReversed.isUsable())
return StmtError();
SemaRef.AddInitializerToDecl(ReversedIVDecl, Minus.get(),
/*DirectInit=*/false);
// The new loop body.
SmallVector<Stmt *, 4> BodyStmts;
BodyStmts.reserve(LoopHelper.Updates.size() + 2 +
(isa<CXXForRangeStmt>(LoopStmt) ? 1 : 0));
BodyStmts.push_back(InitReversed.get());
llvm::append_range(BodyStmts, LoopHelper.Updates);
if (auto *CXXRangeFor = dyn_cast<CXXForRangeStmt>(LoopStmt))
BodyStmts.push_back(CXXRangeFor->getLoopVarStmt());
BodyStmts.push_back(Body);
auto *ReversedBody =
CompoundStmt::Create(Context, BodyStmts, FPOptionsOverride(),
Body->getBeginLoc(), Body->getEndLoc());
// Finally create the reversed For-statement.
auto *ReversedFor = new (Context)
ForStmt(Context, Init.get(), Cond.get(), nullptr, Incr.get(),
ReversedBody, LoopHelper.Init->getBeginLoc(),
LoopHelper.Init->getBeginLoc(), LoopHelper.Inc->getEndLoc());
return OMPReverseDirective::Create(Context, StartLoc, EndLoc, AStmt,
ReversedFor,
buildPreInits(Context, PreInits));
}
OMPClause *SemaOpenMP::ActOnOpenMPSingleExprClause(OpenMPClauseKind Kind,
Expr *Expr,
SourceLocation StartLoc,

View File

@ -9239,6 +9239,17 @@ TreeTransform<Derived>::TransformOMPUnrollDirective(OMPUnrollDirective *D) {
return Res;
}
template <typename Derived>
StmtResult
TreeTransform<Derived>::TransformOMPReverseDirective(OMPReverseDirective *D) {
DeclarationNameInfo DirName;
getDerived().getSema().OpenMP().StartOpenMPDSABlock(
D->getDirectiveKind(), DirName, nullptr, D->getBeginLoc());
StmtResult Res = getDerived().TransformOMPExecutableDirective(D);
getDerived().getSema().OpenMP().EndOpenMPDSABlock(Res.get());
return Res;
}
template <typename Derived>
StmtResult
TreeTransform<Derived>::TransformOMPForDirective(OMPForDirective *D) {

View File

@ -2445,6 +2445,10 @@ void ASTStmtReader::VisitOMPUnrollDirective(OMPUnrollDirective *D) {
VisitOMPLoopTransformationDirective(D);
}
void ASTStmtReader::VisitOMPReverseDirective(OMPReverseDirective *D) {
VisitOMPLoopTransformationDirective(D);
}
void ASTStmtReader::VisitOMPForDirective(OMPForDirective *D) {
VisitOMPLoopDirective(D);
D->setHasCancel(Record.readBool());
@ -3464,6 +3468,15 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) {
break;
}
case STMT_OMP_REVERSE_DIRECTIVE: {
assert(Record[ASTStmtReader::NumStmtFields] == 1 &&
"Reverse directive accepts only a single loop");
assert(Record[ASTStmtReader::NumStmtFields + 1] == 0 &&
"Reverse directive has no clauses");
S = OMPReverseDirective::CreateEmpty(Context);
break;
}
case STMT_OMP_FOR_DIRECTIVE: {
unsigned CollapsedNum = Record[ASTStmtReader::NumStmtFields];
unsigned NumClauses = Record[ASTStmtReader::NumStmtFields + 1];

View File

@ -2437,6 +2437,11 @@ void ASTStmtWriter::VisitOMPUnrollDirective(OMPUnrollDirective *D) {
Code = serialization::STMT_OMP_UNROLL_DIRECTIVE;
}
void ASTStmtWriter::VisitOMPReverseDirective(OMPReverseDirective *D) {
VisitOMPLoopTransformationDirective(D);
Code = serialization::STMT_OMP_REVERSE_DIRECTIVE;
}
void ASTStmtWriter::VisitOMPForDirective(OMPForDirective *D) {
VisitOMPLoopDirective(D);
Record.writeBool(D->hasCancel());

View File

@ -0,0 +1,159 @@
// Check no warnings/errors
// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=60 -fsyntax-only -verify %s
// expected-no-diagnostics
// Check AST and unparsing
// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=60 -ast-dump %s | FileCheck %s --check-prefix=DUMP
// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=60 -ast-print %s | FileCheck %s --check-prefix=PRINT
// Check same results after serialization round-trip
// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=60 -emit-pch -o %t %s
// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=60 -include-pch %t -ast-dump-all %s | FileCheck %s --check-prefix=DUMP
// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -fopenmp-version=60 -include-pch %t -ast-print %s | FileCheck %s --check-prefix=PRINT
#ifndef HEADER
#define HEADER
// placeholder for loop body code.
extern "C" void body(...);
// PRINT-LABEL: void foo1(
// DUMP-LABEL: FunctionDecl {{.*}} foo1
void foo1() {
// PRINT: #pragma omp reverse
// DUMP: OMPReverseDirective
#pragma omp reverse
// PRINT: for (int i = 7; i < 17; i += 3)
// DUMP-NEXT: ForStmt
for (int i = 7; i < 17; i += 3)
// PRINT: body(i);
// DUMP: CallExpr
body(i);
}
// PRINT-LABEL: void foo2(
// DUMP-LABEL: FunctionDecl {{.*}} foo2
void foo2(int start, int end, int step) {
// PRINT: #pragma omp reverse
// DUMP: OMPReverseDirective
#pragma omp reverse
// PRINT: for (int i = start; i < end; i += step)
// DUMP-NEXT: ForStmt
for (int i = start; i < end; i += step)
// PRINT: body(i);
// DUMP: CallExpr
body(i);
}
// PRINT-LABEL: void foo3(
// DUMP-LABEL: FunctionDecl {{.*}} foo3
void foo3() {
// PRINT: #pragma omp for
// DUMP: OMPForDirective
// DUMP-NEXT: CapturedStmt
// DUMP-NEXT: CapturedDecl
#pragma omp for
// PRINT: #pragma omp reverse
// DUMP-NEXT: OMPReverseDirective
#pragma omp reverse
for (int i = 7; i < 17; i += 3)
// PRINT: body(i);
// DUMP: CallExpr
body(i);
}
// PRINT-LABEL: void foo4(
// DUMP-LABEL: FunctionDecl {{.*}} foo4
void foo4() {
// PRINT: #pragma omp for collapse(2)
// DUMP: OMPForDirective
// DUMP-NEXT: OMPCollapseClause
// DUMP-NEXT: ConstantExpr
// DUMP-NEXT: value: Int 2
// DUMP-NEXT: IntegerLiteral {{.*}} 2
// DUMP-NEXT: CapturedStmt
// DUMP-NEXT: CapturedDecl
#pragma omp for collapse(2)
// PRINT: #pragma omp reverse
// DUMP: OMPReverseDirective
#pragma omp reverse
// PRINT: for (int i = 7; i < 17; i += 1)
// DUMP-NEXT: ForStmt
for (int i = 7; i < 17; i += 1)
// PRINT: for (int j = 7; j < 17; j += 1)
// DUMP: ForStmt
for (int j = 7; j < 17; j += 1)
// PRINT: body(i, j);
// DUMP: CallExpr
body(i, j);
}
// PRINT-LABEL: void foo5(
// DUMP-LABEL: FunctionDecl {{.*}} foo5
void foo5(int start, int end, int step) {
// PRINT: #pragma omp for collapse(2)
// DUMP: OMPForDirective
// DUMP-NEXT: OMPCollapseClause
// DUMP-NEXT: ConstantExpr
// DUMP-NEXT: value: Int 2
// DUMP-NEXT: IntegerLiteral {{.*}} 2
// DUMP-NEXT: CapturedStmt
// DUMP-NEXT: CapturedDecl
#pragma omp for collapse(2)
// PRINT: for (int i = 7; i < 17; i += 1)
// DUMP-NEXT: ForStmt
for (int i = 7; i < 17; i += 1)
// PRINT: #pragma omp reverse
// DUMP: OMPReverseDirective
#pragma omp reverse
// PRINT: for (int j = 7; j < 17; j += 1)
// DUMP-NEXT: ForStmt
for (int j = 7; j < 17; j += 1)
// PRINT: body(i, j);
// DUMP: CallExpr
body(i, j);
}
// PRINT-LABEL: void foo6(
// DUMP-LABEL: FunctionTemplateDecl {{.*}} foo6
template<typename T, T Step>
void foo6(T start, T end) {
// PRINT: #pragma omp reverse
// DUMP: OMPReverseDirective
#pragma omp reverse
// PRINT-NEXT: for (T i = start; i < end; i += Step)
// DUMP-NEXT: ForStmt
for (T i = start; i < end; i += Step)
// PRINT-NEXT: body(i);
// DUMP: CallExpr
body(i);
}
// Also test instantiating the template.
void tfoo6() {
foo6<int,3>(0, 42);
}
// PRINT-LABEL: void foo7(
// DUMP-LABEL: FunctionDecl {{.*}} foo7
void foo7() {
double arr[128];
// PRINT: #pragma omp reverse
// DUMP: OMPReverseDirective
#pragma omp reverse
// PRINT-NEXT: for (auto &&v : arr)
// DUMP-NEXT: CXXForRangeStmt
for (auto &&v : arr)
// PRINT-NEXT: body(v);
// DUMP: CallExpr
body(v);
}
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,40 @@
// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -std=c++20 -fopenmp -fopenmp-version=60 -fsyntax-only -Wuninitialized -verify %s
void func() {
// expected-error@+2 {{statement after '#pragma omp reverse' must be a for loop}}
#pragma omp reverse
;
// expected-error@+2 {{statement after '#pragma omp reverse' must be a for loop}}
#pragma omp reverse
int b = 0;
// expected-error@+2 {{statement after '#pragma omp reverse' must be a for loop}}
#pragma omp reverse
#pragma omp for
for (int i = 0; i < 7; ++i)
;
{
// expected-error@+2 {{expected statement}}
#pragma omp reverse
}
// expected-error@+2 {{condition of OpenMP for loop must be a relational comparison ('<', '<=', '>', '>=', or '!=') of loop variable 'i'}}
#pragma omp reverse
for (int i = 0; i/3<7; ++i)
;
// expected-error@+1 {{unexpected OpenMP clause 'sizes' in directive '#pragma omp reverse'}}
#pragma omp reverse sizes(5)
for (int i = 0; i < 7; ++i)
;
// expected-warning@+1 {{extra tokens at the end of '#pragma omp reverse' are ignored}}
#pragma omp reverse foo
for (int i = 0; i < 7; ++i)
;
}

View File

@ -2182,6 +2182,7 @@ public:
VisitOMPLoopTransformationDirective(const OMPLoopTransformationDirective *D);
void VisitOMPTileDirective(const OMPTileDirective *D);
void VisitOMPUnrollDirective(const OMPUnrollDirective *D);
void VisitOMPReverseDirective(const OMPReverseDirective *D);
void VisitOMPForDirective(const OMPForDirective *D);
void VisitOMPForSimdDirective(const OMPForSimdDirective *D);
void VisitOMPSectionsDirective(const OMPSectionsDirective *D);
@ -3228,6 +3229,10 @@ void EnqueueVisitor::VisitOMPUnrollDirective(const OMPUnrollDirective *D) {
VisitOMPLoopTransformationDirective(D);
}
void EnqueueVisitor::VisitOMPReverseDirective(const OMPReverseDirective *D) {
VisitOMPLoopTransformationDirective(D);
}
void EnqueueVisitor::VisitOMPForDirective(const OMPForDirective *D) {
VisitOMPLoopDirective(D);
}
@ -6097,6 +6102,8 @@ CXString clang_getCursorKindSpelling(enum CXCursorKind Kind) {
return cxstring::createRef("OMPTileDirective");
case CXCursor_OMPUnrollDirective:
return cxstring::createRef("OMPUnrollDirective");
case CXCursor_OMPReverseDirective:
return cxstring::createRef("OMPReverseDirective");
case CXCursor_OMPForDirective:
return cxstring::createRef("OMPForDirective");
case CXCursor_OMPForSimdDirective:

View File

@ -673,6 +673,9 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent,
case Stmt::OMPUnrollDirectiveClass:
K = CXCursor_OMPUnrollDirective;
break;
case Stmt::OMPReverseDirectiveClass:
K = CXCursor_OMPReverseDirective;
break;
case Stmt::OMPForDirectiveClass:
K = CXCursor_OMPForDirective;
break;

View File

@ -837,6 +837,10 @@ def OMP_Requires : Directive<"requires"> {
let association = AS_None;
let category = CA_Informational;
}
def OMP_Reverse : Directive<"reverse"> {
let association = AS_Loop;
let category = CA_Executable;
}
def OMP_Scan : Directive<"scan"> {
let allowedClauses = [
VersionedClause<OMPC_Exclusive, 50>,

View File

@ -0,0 +1,162 @@
// RUN: %libomp-cxx20-compile-and-run | FileCheck %s --match-full-lines
#ifndef HEADER
#define HEADER
#include <cstdlib>
#include <cstdarg>
#include <cstdio>
#include <vector>
struct Reporter {
const char *name;
Reporter(const char *name) : name(name) { print("ctor"); }
Reporter() : name("<anon>") { print("ctor"); }
Reporter(const Reporter &that) : name(that.name) { print("copy ctor"); }
Reporter(Reporter &&that) : name(that.name) { print("move ctor"); }
~Reporter() { print("dtor"); }
const Reporter &operator=(const Reporter &that) {
print("copy assign");
this->name = that.name;
return *this;
}
const Reporter &operator=(Reporter &&that) {
print("move assign");
this->name = that.name;
return *this;
}
struct Iterator {
const Reporter *owner;
int pos;
Iterator(const Reporter *owner, int pos) : owner(owner), pos(pos) {}
Iterator(const Iterator &that) : owner(that.owner), pos(that.pos) {
owner->print("iterator copy ctor");
}
Iterator(Iterator &&that) : owner(that.owner), pos(that.pos) {
owner->print("iterator move ctor");
}
~Iterator() { owner->print("iterator dtor"); }
const Iterator &operator=(const Iterator &that) {
owner->print("iterator copy assign");
this->owner = that.owner;
this->pos = that.pos;
return *this;
}
const Iterator &operator=(Iterator &&that) {
owner->print("iterator move assign");
this->owner = that.owner;
this->pos = that.pos;
return *this;
}
bool operator==(const Iterator &that) const {
owner->print("iterator %d == %d", 2 - this->pos, 2 - that.pos);
return this->pos == that.pos;
}
Iterator &operator++() {
owner->print("iterator prefix ++");
pos -= 1;
return *this;
}
Iterator operator++(int) {
owner->print("iterator postfix ++");
auto result = *this;
pos -= 1;
return result;
}
int operator*() const {
int result = 2 - pos;
owner->print("iterator deref: %i", result);
return result;
}
size_t operator-(const Iterator &that) const {
int result = (2 - this->pos) - (2 - that.pos);
owner->print("iterator distance: %d", result);
return result;
}
Iterator operator+(int steps) const {
owner->print("iterator advance: %i += %i", 2 - this->pos, steps);
return Iterator(owner, pos - steps);
}
void print(const char *msg) const { owner->print(msg); }
};
Iterator begin() const {
print("begin()");
return Iterator(this, 2);
}
Iterator end() const {
print("end()");
return Iterator(this, -1);
}
void print(const char *msg, ...) const {
va_list args;
va_start(args, msg);
printf("[%s] ", name);
vprintf(msg, args);
printf("\n");
va_end(args);
}
};
int main() {
printf("do\n");
#pragma omp reverse
for (Reporter c{"init-stmt"}; auto &&v : Reporter("range"))
printf("v=%d\n", v);
printf("done\n");
return EXIT_SUCCESS;
}
#endif /* HEADER */
// CHECK: do
// CHECK-NEXT: [init-stmt] ctor
// CHECK-NEXT: [range] ctor
// CHECK-NEXT: [range] end()
// CHECK-NEXT: [range] begin()
// CHECK-NEXT: [range] begin()
// CHECK-NEXT: [range] iterator distance: 3
// CHECK-NEXT: [range] iterator advance: 0 += 2
// CHECK-NEXT: [range] iterator move assign
// CHECK-NEXT: [range] iterator deref: 2
// CHECK-NEXT: v=2
// CHECK-NEXT: [range] iterator dtor
// CHECK-NEXT: [range] iterator advance: 0 += 1
// CHECK-NEXT: [range] iterator move assign
// CHECK-NEXT: [range] iterator deref: 1
// CHECK-NEXT: v=1
// CHECK-NEXT: [range] iterator dtor
// CHECK-NEXT: [range] iterator advance: 0 += 0
// CHECK-NEXT: [range] iterator move assign
// CHECK-NEXT: [range] iterator deref: 0
// CHECK-NEXT: v=0
// CHECK-NEXT: [range] iterator dtor
// CHECK-NEXT: [range] iterator dtor
// CHECK-NEXT: [range] iterator dtor
// CHECK-NEXT: [range] iterator dtor
// CHECK-NEXT: [range] dtor
// CHECK-NEXT: [init-stmt] dtor
// CHECK-NEXT: done

View File

@ -0,0 +1,25 @@
// RUN: %libomp-compile-and-run | FileCheck %s --match-full-lines
#ifndef HEADER
#define HEADER
#include <stdlib.h>
#include <stdio.h>
int main() {
printf("do\n");
#pragma omp reverse
for (int i = 7; i < 19; i += 3)
printf("i=%d\n", i);
printf("done\n");
return EXIT_SUCCESS;
}
#endif /* HEADER */
// CHECK: do
// CHECK-NEXT: i=16
// CHECK-NEXT: i=13
// CHECK-NEXT: i=10
// CHECK-NEXT: i=7
// CHECK-NEXT: done

View File

@ -0,0 +1,164 @@
// RUN: %libomp-cxx20-compile-and-run | FileCheck %s --match-full-lines
#ifndef HEADER
#define HEADER
#include <cstdlib>
#include <cstdarg>
#include <cstdio>
#include <vector>
struct Reporter {
const char *name;
Reporter(const char *name) : name(name) { print("ctor"); }
Reporter() : name("<anon>") { print("ctor"); }
Reporter(const Reporter &that) : name(that.name) { print("copy ctor"); }
Reporter(Reporter &&that) : name(that.name) { print("move ctor"); }
~Reporter() { print("dtor"); }
const Reporter &operator=(const Reporter &that) {
print("copy assign");
this->name = that.name;
return *this;
}
const Reporter &operator=(Reporter &&that) {
print("move assign");
this->name = that.name;
return *this;
}
struct Iterator {
const Reporter *owner;
int pos;
Iterator(const Reporter *owner, int pos) : owner(owner), pos(pos) {}
Iterator(const Iterator &that) : owner(that.owner), pos(that.pos) {
owner->print("iterator copy ctor");
}
Iterator(Iterator &&that) : owner(that.owner), pos(that.pos) {
owner->print("iterator move ctor");
}
~Iterator() { owner->print("iterator dtor"); }
const Iterator &operator=(const Iterator &that) {
owner->print("iterator copy assign");
this->owner = that.owner;
this->pos = that.pos;
return *this;
}
const Iterator &operator=(Iterator &&that) {
owner->print("iterator move assign");
this->owner = that.owner;
this->pos = that.pos;
return *this;
}
bool operator==(const Iterator &that) const {
owner->print("iterator %d == %d", 2 - this->pos, 2 - that.pos);
return this->pos == that.pos;
}
bool operator!=(const Iterator &that) const {
owner->print("iterator %d != %d", 2 - this->pos, 2 - that.pos);
return this->pos != that.pos;
}
Iterator &operator++() {
owner->print("iterator prefix ++");
pos -= 1;
return *this;
}
Iterator operator++(int) {
owner->print("iterator postfix ++");
auto result = *this;
pos -= 1;
return result;
}
int operator*() const {
int result = 2 - pos;
owner->print("iterator deref: %i", result);
return result;
}
size_t operator-(const Iterator &that) const {
int result = (2 - this->pos) - (2 - that.pos);
owner->print("iterator distance: %d", result);
return result;
}
Iterator operator+(int steps) const {
owner->print("iterator advance: %i += %i", 2 - this->pos, steps);
return Iterator(owner, pos - steps);
}
};
Iterator begin() const {
print("begin()");
return Iterator(this, 2);
}
Iterator end() const {
print("end()");
return Iterator(this, -1);
}
void print(const char *msg, ...) const {
va_list args;
va_start(args, msg);
printf("[%s] ", name);
vprintf(msg, args);
printf("\n");
va_end(args);
}
};
int main() {
printf("do\n");
Reporter range("range");
#pragma omp reverse
for (auto it = range.begin(); it != range.end(); ++it)
printf("v=%d\n", *it);
printf("done\n");
return EXIT_SUCCESS;
}
#endif /* HEADER */
// CHECK: do
// CHECK-NEXT: [range] ctor
// CHECK-NEXT: [range] begin()
// CHECK-NEXT: [range] begin()
// CHECK-NEXT: [range] end()
// CHECK-NEXT: [range] iterator distance: 3
// CHECK-NEXT: [range] iterator advance: 0 += 2
// CHECK-NEXT: [range] iterator move assign
// CHECK-NEXT: [range] iterator deref: 2
// CHECK-NEXT: v=2
// CHECK-NEXT: [range] iterator dtor
// CHECK-NEXT: [range] iterator advance: 0 += 1
// CHECK-NEXT: [range] iterator move assign
// CHECK-NEXT: [range] iterator deref: 1
// CHECK-NEXT: v=1
// CHECK-NEXT: [range] iterator dtor
// CHECK-NEXT: [range] iterator advance: 0 += 0
// CHECK-NEXT: [range] iterator move assign
// CHECK-NEXT: [range] iterator deref: 0
// CHECK-NEXT: v=0
// CHECK-NEXT: [range] iterator dtor
// CHECK-NEXT: [range] iterator dtor
// CHECK-NEXT: [range] iterator dtor
// CHECK-NEXT: done
// CHECK-NEXT: [range] iterator dtor
// CHECK-NEXT: [range] dtor

View File

@ -0,0 +1,285 @@
// RUN: %libomp-cxx20-compile-and-run | FileCheck %s --match-full-lines
#ifndef HEADER
#define HEADER
#include <cstdlib>
#include <cstdarg>
#include <cstdio>
#include <vector>
struct Reporter {
const char *name;
Reporter(const char *name) : name(name) { print("ctor"); }
Reporter() : name("<anon>") { print("ctor"); }
Reporter(const Reporter &that) : name(that.name) { print("copy ctor"); }
Reporter(Reporter &&that) : name(that.name) { print("move ctor"); }
~Reporter() { print("dtor"); }
const Reporter &operator=(const Reporter &that) {
print("copy assign");
this->name = that.name;
return *this;
}
const Reporter &operator=(Reporter &&that) {
print("move assign");
this->name = that.name;
return *this;
}
struct Iterator {
const Reporter *owner;
int pos;
Iterator(const Reporter *owner, int pos) : owner(owner), pos(pos) {}
Iterator(const Iterator &that) : owner(that.owner), pos(that.pos) {
owner->print("iterator copy ctor");
}
Iterator(Iterator &&that) : owner(that.owner), pos(that.pos) {
owner->print("iterator move ctor");
}
~Iterator() { owner->print("iterator dtor"); }
const Iterator &operator=(const Iterator &that) {
owner->print("iterator copy assign");
this->owner = that.owner;
this->pos = that.pos;
return *this;
}
const Iterator &operator=(Iterator &&that) {
owner->print("iterator move assign");
this->owner = that.owner;
this->pos = that.pos;
return *this;
}
bool operator==(const Iterator &that) const {
owner->print("iterator %d == %d", 2 - this->pos, 2 - that.pos);
return this->pos == that.pos;
}
Iterator &operator++() {
owner->print("iterator prefix ++");
pos -= 1;
return *this;
}
Iterator operator++(int) {
owner->print("iterator postfix ++");
auto result = *this;
pos -= 1;
return result;
}
int operator*() const {
int result = 2 - pos;
owner->print("iterator deref: %i", result);
return result;
}
size_t operator-(const Iterator &that) const {
int result = (2 - this->pos) - (2 - that.pos);
owner->print("iterator distance: %d", result);
return result;
}
Iterator operator+(int steps) const {
owner->print("iterator advance: %i += %i", 2 - this->pos, steps);
return Iterator(owner, pos - steps);
}
void print(const char *msg) const { owner->print(msg); }
};
Iterator begin() const {
print("begin()");
return Iterator(this, 2);
}
Iterator end() const {
print("end()");
return Iterator(this, -1);
}
void print(const char *msg, ...) const {
va_list args;
va_start(args, msg);
printf("[%s] ", name);
vprintf(msg, args);
printf("\n");
va_end(args);
}
};
int main() {
printf("do\n");
#pragma omp parallel for collapse(3) num_threads(1)
for (int i = 0; i < 3; ++i)
#pragma omp reverse
for (Reporter c{"init-stmt"}; auto &&v : Reporter("range"))
for (int k = 0; k < 3; ++k)
printf("i=%d j=%d k=%d\n", i, v, k);
printf("done\n");
return EXIT_SUCCESS;
}
#endif /* HEADER */
// CHECK: do
// CHECK-NEXT: [init-stmt] ctor
// CHECK-NEXT: [range] ctor
// CHECK-NEXT: [range] end()
// CHECK-NEXT: [range] begin()
// CHECK-NEXT: [range] begin()
// CHECK-NEXT: [range] iterator distance: 3
// CHECK-NEXT: [range] iterator advance: 0 += 2
// CHECK-NEXT: [range] iterator move assign
// CHECK-NEXT: [range] iterator deref: 2
// CHECK-NEXT: i=0 j=2 k=0
// CHECK-NEXT: [range] iterator dtor
// CHECK-NEXT: [range] iterator advance: 0 += 2
// CHECK-NEXT: [range] iterator move assign
// CHECK-NEXT: [range] iterator deref: 2
// CHECK-NEXT: i=0 j=2 k=1
// CHECK-NEXT: [range] iterator dtor
// CHECK-NEXT: [range] iterator advance: 0 += 2
// CHECK-NEXT: [range] iterator move assign
// CHECK-NEXT: [range] iterator deref: 2
// CHECK-NEXT: i=0 j=2 k=2
// CHECK-NEXT: [range] iterator dtor
// CHECK-NEXT: [range] iterator advance: 0 += 1
// CHECK-NEXT: [range] iterator move assign
// CHECK-NEXT: [range] iterator deref: 1
// CHECK-NEXT: i=0 j=1 k=0
// CHECK-NEXT: [range] iterator dtor
// CHECK-NEXT: [range] iterator advance: 0 += 1
// CHECK-NEXT: [range] iterator move assign
// CHECK-NEXT: [range] iterator deref: 1
// CHECK-NEXT: i=0 j=1 k=1
// CHECK-NEXT: [range] iterator dtor
// CHECK-NEXT: [range] iterator advance: 0 += 1
// CHECK-NEXT: [range] iterator move assign
// CHECK-NEXT: [range] iterator deref: 1
// CHECK-NEXT: i=0 j=1 k=2
// CHECK-NEXT: [range] iterator dtor
// CHECK-NEXT: [range] iterator advance: 0 += 0
// CHECK-NEXT: [range] iterator move assign
// CHECK-NEXT: [range] iterator deref: 0
// CHECK-NEXT: i=0 j=0 k=0
// CHECK-NEXT: [range] iterator dtor
// CHECK-NEXT: [range] iterator advance: 0 += 0
// CHECK-NEXT: [range] iterator move assign
// CHECK-NEXT: [range] iterator deref: 0
// CHECK-NEXT: i=0 j=0 k=1
// CHECK-NEXT: [range] iterator dtor
// CHECK-NEXT: [range] iterator advance: 0 += 0
// CHECK-NEXT: [range] iterator move assign
// CHECK-NEXT: [range] iterator deref: 0
// CHECK-NEXT: i=0 j=0 k=2
// CHECK-NEXT: [range] iterator dtor
// CHECK-NEXT: [range] iterator advance: 0 += 2
// CHECK-NEXT: [range] iterator move assign
// CHECK-NEXT: [range] iterator deref: 2
// CHECK-NEXT: i=1 j=2 k=0
// CHECK-NEXT: [range] iterator dtor
// CHECK-NEXT: [range] iterator advance: 0 += 2
// CHECK-NEXT: [range] iterator move assign
// CHECK-NEXT: [range] iterator deref: 2
// CHECK-NEXT: i=1 j=2 k=1
// CHECK-NEXT: [range] iterator dtor
// CHECK-NEXT: [range] iterator advance: 0 += 2
// CHECK-NEXT: [range] iterator move assign
// CHECK-NEXT: [range] iterator deref: 2
// CHECK-NEXT: i=1 j=2 k=2
// CHECK-NEXT: [range] iterator dtor
// CHECK-NEXT: [range] iterator advance: 0 += 1
// CHECK-NEXT: [range] iterator move assign
// CHECK-NEXT: [range] iterator deref: 1
// CHECK-NEXT: i=1 j=1 k=0
// CHECK-NEXT: [range] iterator dtor
// CHECK-NEXT: [range] iterator advance: 0 += 1
// CHECK-NEXT: [range] iterator move assign
// CHECK-NEXT: [range] iterator deref: 1
// CHECK-NEXT: i=1 j=1 k=1
// CHECK-NEXT: [range] iterator dtor
// CHECK-NEXT: [range] iterator advance: 0 += 1
// CHECK-NEXT: [range] iterator move assign
// CHECK-NEXT: [range] iterator deref: 1
// CHECK-NEXT: i=1 j=1 k=2
// CHECK-NEXT: [range] iterator dtor
// CHECK-NEXT: [range] iterator advance: 0 += 0
// CHECK-NEXT: [range] iterator move assign
// CHECK-NEXT: [range] iterator deref: 0
// CHECK-NEXT: i=1 j=0 k=0
// CHECK-NEXT: [range] iterator dtor
// CHECK-NEXT: [range] iterator advance: 0 += 0
// CHECK-NEXT: [range] iterator move assign
// CHECK-NEXT: [range] iterator deref: 0
// CHECK-NEXT: i=1 j=0 k=1
// CHECK-NEXT: [range] iterator dtor
// CHECK-NEXT: [range] iterator advance: 0 += 0
// CHECK-NEXT: [range] iterator move assign
// CHECK-NEXT: [range] iterator deref: 0
// CHECK-NEXT: i=1 j=0 k=2
// CHECK-NEXT: [range] iterator dtor
// CHECK-NEXT: [range] iterator advance: 0 += 2
// CHECK-NEXT: [range] iterator move assign
// CHECK-NEXT: [range] iterator deref: 2
// CHECK-NEXT: i=2 j=2 k=0
// CHECK-NEXT: [range] iterator dtor
// CHECK-NEXT: [range] iterator advance: 0 += 2
// CHECK-NEXT: [range] iterator move assign
// CHECK-NEXT: [range] iterator deref: 2
// CHECK-NEXT: i=2 j=2 k=1
// CHECK-NEXT: [range] iterator dtor
// CHECK-NEXT: [range] iterator advance: 0 += 2
// CHECK-NEXT: [range] iterator move assign
// CHECK-NEXT: [range] iterator deref: 2
// CHECK-NEXT: i=2 j=2 k=2
// CHECK-NEXT: [range] iterator dtor
// CHECK-NEXT: [range] iterator advance: 0 += 1
// CHECK-NEXT: [range] iterator move assign
// CHECK-NEXT: [range] iterator deref: 1
// CHECK-NEXT: i=2 j=1 k=0
// CHECK-NEXT: [range] iterator dtor
// CHECK-NEXT: [range] iterator advance: 0 += 1
// CHECK-NEXT: [range] iterator move assign
// CHECK-NEXT: [range] iterator deref: 1
// CHECK-NEXT: i=2 j=1 k=1
// CHECK-NEXT: [range] iterator dtor
// CHECK-NEXT: [range] iterator advance: 0 += 1
// CHECK-NEXT: [range] iterator move assign
// CHECK-NEXT: [range] iterator deref: 1
// CHECK-NEXT: i=2 j=1 k=2
// CHECK-NEXT: [range] iterator dtor
// CHECK-NEXT: [range] iterator advance: 0 += 0
// CHECK-NEXT: [range] iterator move assign
// CHECK-NEXT: [range] iterator deref: 0
// CHECK-NEXT: i=2 j=0 k=0
// CHECK-NEXT: [range] iterator dtor
// CHECK-NEXT: [range] iterator advance: 0 += 0
// CHECK-NEXT: [range] iterator move assign
// CHECK-NEXT: [range] iterator deref: 0
// CHECK-NEXT: i=2 j=0 k=1
// CHECK-NEXT: [range] iterator dtor
// CHECK-NEXT: [range] iterator advance: 0 += 0
// CHECK-NEXT: [range] iterator move assign
// CHECK-NEXT: [range] iterator deref: 0
// CHECK-NEXT: i=2 j=0 k=2
// CHECK-NEXT: [range] iterator dtor
// CHECK-NEXT: [range] iterator dtor
// CHECK-NEXT: [range] iterator dtor
// CHECK-NEXT: [range] iterator dtor
// CHECK-NEXT: [range] dtor
// CHECK-NEXT: [init-stmt] dtor
// CHECK-NEXT: done

View File

@ -0,0 +1,51 @@
// RUN: %libomp-cxx-compile-and-run | FileCheck %s --match-full-lines
#ifndef HEADER
#define HEADER
#include <cstdlib>
#include <cstdio>
int main() {
printf("do\n");
#pragma omp parallel for collapse(3) num_threads(1)
for (int i = 0; i < 3; ++i)
#pragma omp reverse
for (int j = 0; j < 3; ++j)
for (int k = 0; k < 3; ++k)
printf("i=%d j=%d k=%d\n", i, j, k);
printf("done\n");
return EXIT_SUCCESS;
}
#endif /* HEADER */
// CHECK: do
// CHECK-NEXT: i=0 j=2 k=0
// CHECK-NEXT: i=0 j=2 k=1
// CHECK-NEXT: i=0 j=2 k=2
// CHECK-NEXT: i=0 j=1 k=0
// CHECK-NEXT: i=0 j=1 k=1
// CHECK-NEXT: i=0 j=1 k=2
// CHECK-NEXT: i=0 j=0 k=0
// CHECK-NEXT: i=0 j=0 k=1
// CHECK-NEXT: i=0 j=0 k=2
// CHECK-NEXT: i=1 j=2 k=0
// CHECK-NEXT: i=1 j=2 k=1
// CHECK-NEXT: i=1 j=2 k=2
// CHECK-NEXT: i=1 j=1 k=0
// CHECK-NEXT: i=1 j=1 k=1
// CHECK-NEXT: i=1 j=1 k=2
// CHECK-NEXT: i=1 j=0 k=0
// CHECK-NEXT: i=1 j=0 k=1
// CHECK-NEXT: i=1 j=0 k=2
// CHECK-NEXT: i=2 j=2 k=0
// CHECK-NEXT: i=2 j=2 k=1
// CHECK-NEXT: i=2 j=2 k=2
// CHECK-NEXT: i=2 j=1 k=0
// CHECK-NEXT: i=2 j=1 k=1
// CHECK-NEXT: i=2 j=1 k=2
// CHECK-NEXT: i=2 j=0 k=0
// CHECK-NEXT: i=2 j=0 k=1
// CHECK-NEXT: i=2 j=0 k=2
// CHECK-NEXT: done