[Clang][OpenMP] Add interchange directive (#93022)

Add the interchange 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).
This commit is contained in:
Michael Kruse 2024-07-19 09:24:40 +02:00 committed by GitHub
parent e6668b1be8
commit 5c93a94f5a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
31 changed files with 3465 additions and 2 deletions

View File

@ -2150,6 +2150,10 @@ enum CXCursorKind {
*/
CXCursor_OMPReverseDirective = 307,
/** OpenMP interchange directive.
*/
CXCursor_OMPInterchangeDirective = 308,
/** OpenACC Compute Construct.
*/
CXCursor_OpenACCComputeConstruct = 320,

View File

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

View File

@ -1009,7 +1009,7 @@ public:
static bool classof(const Stmt *T) {
Stmt::StmtClass C = T->getStmtClass();
return C == OMPTileDirectiveClass || C == OMPUnrollDirectiveClass ||
C == OMPReverseDirectiveClass;
C == OMPReverseDirectiveClass || C == OMPInterchangeDirectiveClass;
}
};
@ -5776,6 +5776,80 @@ public:
}
};
/// Represents the '#pragma omp interchange' loop transformation directive.
///
/// \code{c}
/// #pragma omp interchange
/// for (int i = 0; i < m; ++i)
/// for (int j = 0; j < n; ++j)
/// ..
/// \endcode
class OMPInterchangeDirective final : public OMPLoopTransformationDirective {
friend class ASTStmtReader;
friend class OMPExecutableDirective;
/// Offsets of child members.
enum {
PreInitsOffset = 0,
TransformedStmtOffset,
};
explicit OMPInterchangeDirective(SourceLocation StartLoc,
SourceLocation EndLoc, unsigned NumLoops)
: OMPLoopTransformationDirective(OMPInterchangeDirectiveClass,
llvm::omp::OMPD_interchange, StartLoc,
EndLoc, NumLoops) {
setNumGeneratedLoops(3 * NumLoops);
}
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 interchange'.
///
/// \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 Clauses The directive's clauses.
/// \param NumLoops Number of affected loops
/// (number of items in the 'permutation' clause if present).
/// \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 OMPInterchangeDirective *
Create(const ASTContext &C, SourceLocation StartLoc, SourceLocation EndLoc,
ArrayRef<OMPClause *> Clauses, unsigned NumLoops, Stmt *AssociatedStmt,
Stmt *TransformedStmt, Stmt *PreInits);
/// Build an empty '#pragma omp interchange' AST node for deserialization.
///
/// \param C Context of the AST.
/// \param NumClauses Number of clauses to allocate.
/// \param NumLoops Number of associated loops to allocate.
static OMPInterchangeDirective *
CreateEmpty(const ASTContext &C, unsigned NumClauses, unsigned NumLoops);
/// Gets the associated loops after the transformation. This is the de-sugared
/// replacement or nullptr in dependent contexts.
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() == OMPInterchangeDirectiveClass;
}
};
/// This represents '#pragma omp scan' directive.
///
/// \code

View File

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

View File

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

View File

@ -1896,6 +1896,7 @@ enum StmtCode {
STMT_OMP_TILE_DIRECTIVE,
STMT_OMP_UNROLL_DIRECTIVE,
STMT_OMP_REVERSE_DIRECTIVE,
STMT_OMP_INTERCHANGE_DIRECTIVE,
STMT_OMP_FOR_DIRECTIVE,
STMT_OMP_FOR_SIMD_DIRECTIVE,
STMT_OMP_SECTIONS_DIRECTIVE,

View File

@ -467,6 +467,26 @@ OMPReverseDirective *OMPReverseDirective::CreateEmpty(const ASTContext &C) {
TransformedStmtOffset + 1, SourceLocation(), SourceLocation());
}
OMPInterchangeDirective *OMPInterchangeDirective::Create(
const ASTContext &C, SourceLocation StartLoc, SourceLocation EndLoc,
ArrayRef<OMPClause *> Clauses, unsigned NumLoops, Stmt *AssociatedStmt,
Stmt *TransformedStmt, Stmt *PreInits) {
OMPInterchangeDirective *Dir = createDirective<OMPInterchangeDirective>(
C, Clauses, AssociatedStmt, TransformedStmtOffset + 1, StartLoc, EndLoc,
NumLoops);
Dir->setTransformedStmt(TransformedStmt);
Dir->setPreInits(PreInits);
return Dir;
}
OMPInterchangeDirective *
OMPInterchangeDirective::CreateEmpty(const ASTContext &C, unsigned NumClauses,
unsigned NumLoops) {
return createEmptyDirective<OMPInterchangeDirective>(
C, NumClauses, /*HasAssociatedStmt=*/true, TransformedStmtOffset + 1,
SourceLocation(), SourceLocation(), NumLoops);
}
OMPForSimdDirective *
OMPForSimdDirective::Create(const ASTContext &C, SourceLocation StartLoc,
SourceLocation EndLoc, unsigned CollapsedNum,

View File

@ -768,6 +768,11 @@ void StmtPrinter::VisitOMPReverseDirective(OMPReverseDirective *Node) {
PrintOMPExecutableDirective(Node);
}
void StmtPrinter::VisitOMPInterchangeDirective(OMPInterchangeDirective *Node) {
Indent() << "#pragma omp interchange";
PrintOMPExecutableDirective(Node);
}
void StmtPrinter::VisitOMPForDirective(OMPForDirective *Node) {
Indent() << "#pragma omp for";
PrintOMPExecutableDirective(Node);

View File

@ -989,6 +989,11 @@ void StmtProfiler::VisitOMPReverseDirective(const OMPReverseDirective *S) {
VisitOMPLoopTransformationDirective(S);
}
void StmtProfiler::VisitOMPInterchangeDirective(
const OMPInterchangeDirective *S) {
VisitOMPLoopTransformationDirective(S);
}
void StmtProfiler::VisitOMPForDirective(const OMPForDirective *S) {
VisitOMPLoopDirective(S);
}

View File

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

View File

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

View File

@ -189,6 +189,9 @@ class OMPLoopScope : public CodeGenFunction::RunCleanupsScope {
PreInits = Unroll->getPreInits();
} else if (const auto *Reverse = dyn_cast<OMPReverseDirective>(&S)) {
PreInits = Reverse->getPreInits();
} else if (const auto *Interchange =
dyn_cast<OMPInterchangeDirective>(&S)) {
PreInits = Interchange->getPreInits();
} else {
llvm_unreachable("Unknown loop-based directive kind.");
}
@ -2770,6 +2773,13 @@ void CodeGenFunction::EmitOMPReverseDirective(const OMPReverseDirective &S) {
EmitStmt(S.getTransformedStmt());
}
void CodeGenFunction::EmitOMPInterchangeDirective(
const OMPInterchangeDirective &S) {
// Emit the de-sugared statement.
OMPTransformDirectiveScopeRAII InterchangeScope(*this, &S);
EmitStmt(S.getTransformedStmt());
}
void CodeGenFunction::EmitOMPUnrollDirective(const OMPUnrollDirective &S) {
bool UseOMPIRBuilder = CGM.getLangOpts().OpenMPIRBuilder;

View File

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

View File

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

View File

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

View File

@ -40,6 +40,7 @@
#include "llvm/ADT/IndexedMap.h"
#include "llvm/ADT/PointerEmbeddedInt.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/Sequence.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Frontend/OpenMP/OMPAssume.h"
@ -4406,6 +4407,7 @@ void SemaOpenMP::ActOnOpenMPRegionStart(OpenMPDirectiveKind DKind,
case OMPD_tile:
case OMPD_unroll:
case OMPD_reverse:
case OMPD_interchange:
break;
default:
processCapturedRegions(SemaRef, DKind, CurScope,
@ -6290,6 +6292,10 @@ StmtResult SemaOpenMP::ActOnOpenMPExecutableDirective(
"reverse directive does not support any clauses");
Res = ActOnOpenMPReverseDirective(AStmt, StartLoc, EndLoc);
break;
case OMPD_interchange:
Res = ActOnOpenMPInterchangeDirective(ClausesWithImplicit, AStmt, StartLoc,
EndLoc);
break;
case OMPD_for:
Res = ActOnOpenMPForDirective(ClausesWithImplicit, AStmt, StartLoc, EndLoc,
VarsWithInheritedDSA);
@ -14048,6 +14054,8 @@ bool SemaOpenMP::checkTransformableLoopNest(
DependentPreInits = Dir->getPreInits();
else if (auto *Dir = dyn_cast<OMPReverseDirective>(Transform))
DependentPreInits = Dir->getPreInits();
else if (auto *Dir = dyn_cast<OMPInterchangeDirective>(Transform))
DependentPreInits = Dir->getPreInits();
else
llvm_unreachable("Unhandled loop transformation");
@ -14853,6 +14861,158 @@ StmtResult SemaOpenMP::ActOnOpenMPReverseDirective(Stmt *AStmt,
buildPreInits(Context, PreInits));
}
StmtResult SemaOpenMP::ActOnOpenMPInterchangeDirective(
ArrayRef<OMPClause *> Clauses, Stmt *AStmt, SourceLocation StartLoc,
SourceLocation EndLoc) {
ASTContext &Context = getASTContext();
DeclContext *CurContext = SemaRef.CurContext;
Scope *CurScope = SemaRef.getCurScope();
// Empty statement should only be possible if there already was an error.
if (!AStmt)
return StmtError();
// interchange without permutation clause swaps two loops.
constexpr size_t NumLoops = 2;
// Verify and diagnose loop nest.
SmallVector<OMPLoopBasedDirective::HelperExprs, 4> LoopHelpers(NumLoops);
Stmt *Body = nullptr;
SmallVector<SmallVector<Stmt *, 0>, 2> OriginalInits;
if (!checkTransformableLoopNest(OMPD_interchange, AStmt, NumLoops,
LoopHelpers, Body, OriginalInits))
return StmtError();
// Delay interchange to when template is completely instantiated.
if (CurContext->isDependentContext())
return OMPInterchangeDirective::Create(Context, StartLoc, EndLoc, Clauses,
NumLoops, AStmt, nullptr, nullptr);
assert(LoopHelpers.size() == NumLoops &&
"Expecting loop iteration space dimensionaly to match number of "
"affected loops");
assert(OriginalInits.size() == NumLoops &&
"Expecting loop iteration space dimensionaly to match number of "
"affected loops");
// Decode the permutation clause.
constexpr uint64_t Permutation[] = {1, 0};
// Find the affected loops.
SmallVector<Stmt *> LoopStmts(NumLoops, nullptr);
collectLoopStmts(AStmt, LoopStmts);
// Collect pre-init statements on the order before the permuation.
SmallVector<Stmt *> PreInits;
for (auto I : llvm::seq<int>(NumLoops)) {
OMPLoopBasedDirective::HelperExprs &LoopHelper = LoopHelpers[I];
assert(LoopHelper.Counters.size() == 1 &&
"Single-dimensional loop iteration space expected");
auto *OrigCntVar = cast<DeclRefExpr>(LoopHelper.Counters.front());
std::string OrigVarName = OrigCntVar->getNameInfo().getAsString();
addLoopPreInits(Context, LoopHelper, LoopStmts[I], OriginalInits[I],
PreInits);
}
SmallVector<VarDecl *> PermutedIndVars(NumLoops);
CaptureVars CopyTransformer(SemaRef);
// Create the permuted loops from the inside to the outside of the
// interchanged loop nest. Body of the innermost new loop is the original
// innermost body.
Stmt *Inner = Body;
for (auto TargetIdx : llvm::reverse(llvm::seq<int>(NumLoops))) {
// Get the original loop that belongs to this new position.
uint64_t SourceIdx = Permutation[TargetIdx];
OMPLoopBasedDirective::HelperExprs &SourceHelper = LoopHelpers[SourceIdx];
Stmt *SourceLoopStmt = LoopStmts[SourceIdx];
assert(SourceHelper.Counters.size() == 1 &&
"Single-dimensional loop iteration space expected");
auto *OrigCntVar = cast<DeclRefExpr>(SourceHelper.Counters.front());
// Normalized loop counter variable: From 0 to n-1, always an integer type.
DeclRefExpr *IterVarRef = cast<DeclRefExpr>(SourceHelper.IterationVarRef);
QualType IVTy = IterVarRef->getType();
assert(IVTy->isIntegerType() &&
"Expected the logical iteration counter to be an integer");
std::string OrigVarName = OrigCntVar->getNameInfo().getAsString();
SourceLocation OrigVarLoc = IterVarRef->getExprLoc();
// Make a copy of the NumIterations expression for each use: By the AST
// constraints, every expression object in a DeclContext must be unique.
auto MakeNumIterations = [&CopyTransformer, &SourceHelper]() -> Expr * {
return AssertSuccess(
CopyTransformer.TransformExpr(SourceHelper.NumIterations));
};
// Iteration variable for the permuted loop. Reuse the one from
// checkOpenMPLoop which will also be used to update the original loop
// variable.
SmallString<64> PermutedCntName(".permuted_");
PermutedCntName.append({llvm::utostr(TargetIdx), ".iv.", OrigVarName});
auto *PermutedCntDecl = cast<VarDecl>(IterVarRef->getDecl());
PermutedCntDecl->setDeclName(
&SemaRef.PP.getIdentifierTable().get(PermutedCntName));
PermutedIndVars[TargetIdx] = PermutedCntDecl;
auto MakePermutedRef = [this, PermutedCntDecl, IVTy, OrigVarLoc]() {
return buildDeclRefExpr(SemaRef, PermutedCntDecl, IVTy, OrigVarLoc);
};
// For init-statement:
// \code
// auto .permuted_{target}.iv = 0
// \endcode
ExprResult Zero = SemaRef.ActOnIntegerConstant(OrigVarLoc, 0);
if (!Zero.isUsable())
return StmtError();
SemaRef.AddInitializerToDecl(PermutedCntDecl, Zero.get(),
/*DirectInit=*/false);
StmtResult InitStmt = new (Context)
DeclStmt(DeclGroupRef(PermutedCntDecl), OrigCntVar->getBeginLoc(),
OrigCntVar->getEndLoc());
if (!InitStmt.isUsable())
return StmtError();
// For cond-expression:
// \code
// .permuted_{target}.iv < MakeNumIterations()
// \endcode
ExprResult CondExpr =
SemaRef.BuildBinOp(CurScope, SourceHelper.Cond->getExprLoc(), BO_LT,
MakePermutedRef(), MakeNumIterations());
if (!CondExpr.isUsable())
return StmtError();
// For incr-statement:
// \code
// ++.tile.iv
// \endcode
ExprResult IncrStmt = SemaRef.BuildUnaryOp(
CurScope, SourceHelper.Inc->getExprLoc(), UO_PreInc, MakePermutedRef());
if (!IncrStmt.isUsable())
return StmtError();
SmallVector<Stmt *, 4> BodyParts(SourceHelper.Updates.begin(),
SourceHelper.Updates.end());
if (auto *SourceCXXFor = dyn_cast<CXXForRangeStmt>(SourceLoopStmt))
BodyParts.push_back(SourceCXXFor->getLoopVarStmt());
BodyParts.push_back(Inner);
Inner = CompoundStmt::Create(Context, BodyParts, FPOptionsOverride(),
Inner->getBeginLoc(), Inner->getEndLoc());
Inner = new (Context) ForStmt(
Context, InitStmt.get(), CondExpr.get(), nullptr, IncrStmt.get(), Inner,
SourceHelper.Init->getBeginLoc(), SourceHelper.Init->getBeginLoc(),
SourceHelper.Inc->getEndLoc());
}
return OMPInterchangeDirective::Create(Context, StartLoc, EndLoc, Clauses,
NumLoops, AStmt, Inner,
buildPreInits(Context, PreInits));
}
OMPClause *SemaOpenMP::ActOnOpenMPSingleExprClause(OpenMPClauseKind Kind,
Expr *Expr,
SourceLocation StartLoc,

View File

@ -9250,6 +9250,17 @@ TreeTransform<Derived>::TransformOMPReverseDirective(OMPReverseDirective *D) {
return Res;
}
template <typename Derived>
StmtResult TreeTransform<Derived>::TransformOMPInterchangeDirective(
OMPInterchangeDirective *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

@ -2449,6 +2449,10 @@ void ASTStmtReader::VisitOMPReverseDirective(OMPReverseDirective *D) {
VisitOMPLoopTransformationDirective(D);
}
void ASTStmtReader::VisitOMPInterchangeDirective(OMPInterchangeDirective *D) {
VisitOMPLoopTransformationDirective(D);
}
void ASTStmtReader::VisitOMPForDirective(OMPForDirective *D) {
VisitOMPLoopDirective(D);
D->setHasCancel(Record.readBool());
@ -3477,6 +3481,13 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) {
break;
}
case STMT_OMP_INTERCHANGE_DIRECTIVE: {
unsigned NumLoops = Record[ASTStmtReader::NumStmtFields];
unsigned NumClauses = Record[ASTStmtReader::NumStmtFields + 1];
S = OMPInterchangeDirective::CreateEmpty(Context, NumClauses, NumLoops);
break;
}
case STMT_OMP_FOR_DIRECTIVE: {
unsigned CollapsedNum = Record[ASTStmtReader::NumStmtFields];
unsigned NumClauses = Record[ASTStmtReader::NumStmtFields + 1];

View File

@ -2442,6 +2442,11 @@ void ASTStmtWriter::VisitOMPReverseDirective(OMPReverseDirective *D) {
Code = serialization::STMT_OMP_REVERSE_DIRECTIVE;
}
void ASTStmtWriter::VisitOMPInterchangeDirective(OMPInterchangeDirective *D) {
VisitOMPLoopTransformationDirective(D);
Code = serialization::STMT_OMP_INTERCHANGE_DIRECTIVE;
}
void ASTStmtWriter::VisitOMPForDirective(OMPForDirective *D) {
VisitOMPLoopDirective(D);
Record.writeBool(D->hasCancel());

View File

@ -1813,6 +1813,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::OMPTargetTeamsDistributeSimdDirectiveClass:
case Stmt::OMPReverseDirectiveClass:
case Stmt::OMPTileDirectiveClass:
case Stmt::OMPInterchangeDirectiveClass:
case Stmt::OMPInteropDirectiveClass:
case Stmt::OMPDispatchDirectiveClass:
case Stmt::OMPMaskedDirectiveClass:

View File

@ -0,0 +1,135 @@
// Check no warnings/errors
// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -std=c++20 -fopenmp -fopenmp-version=60 -fsyntax-only -verify %s
// expected-no-diagnostics
// Check AST and unparsing
// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -std=c++20 -fopenmp -fopenmp-version=60 -ast-dump %s | FileCheck %s --check-prefix=DUMP
// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -std=c++20 -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 -std=c++20 -fopenmp -fopenmp-version=60 -emit-pch -o %t %s
// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -std=c++20 -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 -std=c++20 -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 interchange
// DUMP: OMPInterchangeDirective
#pragma omp interchange
// PRINT: for (int i = 7; i < 17; i += 3)
// DUMP-NEXT: ForStmt
for (int i = 7; i < 17; i += 3)
// PRINT: for (int j = 7; j < 17; j += 3)
// DUMP: ForStmt
for (int j = 7; j < 17; j += 3)
// PRINT: body(i, j);
// DUMP: CallExpr
body(i, j);
}
// PRINT-LABEL: void foo3(
// DUMP-LABEL: FunctionDecl {{.*}} foo3
void foo3() {
// PRINT: #pragma omp for collapse(3)
// DUMP: OMPForDirective
// DUMP-NEXT: OMPCollapseClause
// DUMP-NEXT: ConstantExpr
// DUMP-NEXT: value: Int 3
// DUMP-NEXT: IntegerLiteral {{.*}} 3
// DUMP-NEXT: CapturedStmt
// DUMP-NEXT: CapturedDecl
#pragma omp for collapse(3)
// PRINT: #pragma omp interchange
// DUMP: OMPInterchangeDirective
#pragma omp interchange
// 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: for (int k = 7; k < 17; k += 1)
// DUMP: ForStmt
for (int k = 7; k < 17; k += 1)
// PRINT: body(i, j, k);
// DUMP: CallExpr
body(i, j, k);
}
// PRINT-LABEL: void foo6(
// DUMP-LABEL: FunctionTemplateDecl {{.*}} foo6
template<int Tile>
void foo6() {
// PRINT: #pragma omp interchange
// DUMP: OMPInterchangeDirective
#pragma omp interchange
// PRINT-NEXT: for (int i = 0; i < 11; i += 2)
// DUMP-NEXT: ForStmt
for (int i = 0; i < 11; i += 2)
// PRINT-NEXT: #pragma omp tile sizes(Tile)
// DUMP: OMPTileDirective
#pragma omp tile sizes(Tile)
// PRINT-NEXT: for (int j = 0; j < 13; j += 2)
// DUMP: ForStmt
for (int j = 0; j < 13; j += 2)
// PRINT-NEXT: body(i, j);
// DUMP: CallExpr
body(i, j);
}
// Also test instantiating the template.
void tfoo6() {
foo6<32>();
}
// PRINT-LABEL: void foo7(
// DUMP-LABEL: FunctionDecl {{.*}} foo7
void foo7() {
double arr[128];
// PRINT: #pragma omp interchange
// DUMP: OMPInterchangeDirective
#pragma omp interchange
// PRINT-NEXT: for (double c = 42; auto &&v : arr)
// DUMP-NEXT: CXXForRangeStmt
for (double c = 42; auto &&v : arr)
// PRINT-NEXT: for (int i = 0; i < 42; i += 2)
// DUMP: ForStmt
for (int i = 0; i < 42; i += 2)
// PRINT-NEXT: body(c, v, i);
// DUMP: CallExpr
body(c, v, i);
}
// PRINT-LABEL: void foo8(
// DUMP-LABEL: FunctionDecl {{.*}} foo8
void foo8() {
double arr[128];
// PRINT: #pragma omp interchange
// DUMP: OMPInterchangeDirective
#pragma omp interchange
// PRINT-NEXT: for (int i = 0; i < 42; i += 2)
// DUMP-NEXT: ForStmt
for (int i = 0; i < 42; i += 2)
// PRINT-NEXT: for (double c = 42; auto &&v : arr)
// DUMP: CXXForRangeStmt
for (double c = 42; auto &&v : arr)
// PRINT-NEXT: body(i, c, v);
// DUMP: CallExpr
body(i, c, v);
}
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,77 @@
// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -std=c++17 -fopenmp -fopenmp-version=60 -fsyntax-only -Wuninitialized -verify %s
void func() {
// expected-warning@+1 {{extra tokens at the end of '#pragma omp interchange' are ignored}}
#pragma omp interchange foo
for (int i = 0; i < 7; ++i)
for (int j = 0; j < 13; ++j)
;
// expected-error@+1 {{unexpected OpenMP clause 'collapse' in directive '#pragma omp interchange'}}
#pragma omp interchange collapse(2)
for (int i = 0; i < 7; ++i)
for (int j = 0; j < 13; ++j)
;
{
// expected-error@+2 {{expected statement}}
#pragma omp interchange
}
// expected-error@+2 {{statement after '#pragma omp interchange' must be a for loop}}
#pragma omp interchange
int b = 0;
// expected-error@+3 {{statement after '#pragma omp interchange' must be a for loop}}
#pragma omp interchange
for (int i = 0; i < 7; ++i)
;
// expected-error@+2 {{statement after '#pragma omp interchange' must be a for loop}}
#pragma omp interchange
for (int i = 0; i < 7; ++i) {
int k = 3;
for (int j = 0; j < 7; ++j)
;
}
// expected-error@+3 {{expected loop invariant expression}}
#pragma omp interchange
for (int i = 0; i < 7; ++i)
for (int j = i; j < 7; ++j)
;
// expected-error@+3 {{expected loop invariant expression}}
#pragma omp interchange
for (int i = 0; i < 7; ++i)
for (int j = 0; j < i; ++j)
;
// expected-error@+3 {{expected loop invariant expression}}
#pragma omp interchange
for (int i = 0; i < 7; ++i)
for (int j = 0; j < i; ++j)
;
// expected-error@+6 {{expected 3 for loops after '#pragma omp for', but found only 2}}
// expected-note@+1 {{as specified in 'collapse' clause}}
#pragma omp for collapse(3)
#pragma omp interchange
for (int i = 0; i < 7; ++i)
for (int j = 0; j < 13; ++j)
;
// expected-error@+2 {{statement after '#pragma omp interchange' must be a for loop}}
#pragma omp interchange
#pragma omp for
for (int i = 0; i < 7; ++i)
;
// expected-error@+3 {{condition of OpenMP for loop must be a relational comparison ('<', '<=', '>', '>=', or '!=') of loop variable 'j'}}
#pragma omp interchange
for (int i = 0; i < 7; ++i)
for (int j = 0; j/3<7; ++j)
;
}

View File

@ -2183,6 +2183,7 @@ public:
void VisitOMPTileDirective(const OMPTileDirective *D);
void VisitOMPUnrollDirective(const OMPUnrollDirective *D);
void VisitOMPReverseDirective(const OMPReverseDirective *D);
void VisitOMPInterchangeDirective(const OMPInterchangeDirective *D);
void VisitOMPForDirective(const OMPForDirective *D);
void VisitOMPForSimdDirective(const OMPForSimdDirective *D);
void VisitOMPSectionsDirective(const OMPSectionsDirective *D);
@ -3233,6 +3234,11 @@ void EnqueueVisitor::VisitOMPReverseDirective(const OMPReverseDirective *D) {
VisitOMPLoopTransformationDirective(D);
}
void EnqueueVisitor::VisitOMPInterchangeDirective(
const OMPInterchangeDirective *D) {
VisitOMPLoopTransformationDirective(D);
}
void EnqueueVisitor::VisitOMPForDirective(const OMPForDirective *D) {
VisitOMPLoopDirective(D);
}
@ -6104,6 +6110,8 @@ CXString clang_getCursorKindSpelling(enum CXCursorKind Kind) {
return cxstring::createRef("OMPUnrollDirective");
case CXCursor_OMPReverseDirective:
return cxstring::createRef("OMPReverseDirective");
case CXCursor_OMPInterchangeDirective:
return cxstring::createRef("OMPInterchangeDirective");
case CXCursor_OMPForDirective:
return cxstring::createRef("OMPForDirective");
case CXCursor_OMPForSimdDirective:

View File

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

View File

@ -735,6 +735,10 @@ def OMP_For : Directive<"for"> {
let association = AS_Loop;
let category = CA_Executable;
}
def OMP_Interchange : Directive<"interchange"> {
let association = AS_Loop;
let category = CA_Executable;
}
def OMP_interop : Directive<"interop"> {
let allowedClauses = [
VersionedClause<OMPC_Depend>,

View File

@ -0,0 +1,216 @@
// 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 interchange
for (Reporter c{"C"}; auto &&v : Reporter("A"))
for (Reporter d{"D"}; auto &&w : Reporter("B"))
printf("v=%d w=%d\n", v, w);
printf("done\n");
return EXIT_SUCCESS;
}
#endif /* HEADER */
// CHECK: do
// CHECK-NEXT: [C] ctor
// CHECK-NEXT: [A] ctor
// CHECK-NEXT: [A] end()
// CHECK-NEXT: [A] begin()
// CHECK-NEXT: [A] begin()
// CHECK-NEXT: [A] iterator distance: 3
// CHECK-NEXT: [D] ctor
// CHECK-NEXT: [B] ctor
// CHECK-NEXT: [B] end()
// CHECK-NEXT: [B] begin()
// CHECK-NEXT: [B] begin()
// CHECK-NEXT: [B] iterator distance: 3
// CHECK-NEXT: [B] iterator advance: 0 += 0
// CHECK-NEXT: [B] iterator move assign
// CHECK-NEXT: [B] iterator deref: 0
// CHECK-NEXT: [A] iterator advance: 0 += 0
// CHECK-NEXT: [A] iterator move assign
// CHECK-NEXT: [A] iterator deref: 0
// CHECK-NEXT: v=0 w=0
// CHECK-NEXT: [A] iterator dtor
// CHECK-NEXT: [A] iterator advance: 0 += 1
// CHECK-NEXT: [A] iterator move assign
// CHECK-NEXT: [A] iterator deref: 1
// CHECK-NEXT: v=1 w=0
// CHECK-NEXT: [A] iterator dtor
// CHECK-NEXT: [A] iterator advance: 0 += 2
// CHECK-NEXT: [A] iterator move assign
// CHECK-NEXT: [A] iterator deref: 2
// CHECK-NEXT: v=2 w=0
// CHECK-NEXT: [A] iterator dtor
// CHECK-NEXT: [B] iterator dtor
// CHECK-NEXT: [B] iterator advance: 0 += 1
// CHECK-NEXT: [B] iterator move assign
// CHECK-NEXT: [B] iterator deref: 1
// CHECK-NEXT: [A] iterator advance: 0 += 0
// CHECK-NEXT: [A] iterator move assign
// CHECK-NEXT: [A] iterator deref: 0
// CHECK-NEXT: v=0 w=1
// CHECK-NEXT: [A] iterator dtor
// CHECK-NEXT: [A] iterator advance: 0 += 1
// CHECK-NEXT: [A] iterator move assign
// CHECK-NEXT: [A] iterator deref: 1
// CHECK-NEXT: v=1 w=1
// CHECK-NEXT: [A] iterator dtor
// CHECK-NEXT: [A] iterator advance: 0 += 2
// CHECK-NEXT: [A] iterator move assign
// CHECK-NEXT: [A] iterator deref: 2
// CHECK-NEXT: v=2 w=1
// CHECK-NEXT: [A] iterator dtor
// CHECK-NEXT: [B] iterator dtor
// CHECK-NEXT: [B] iterator advance: 0 += 2
// CHECK-NEXT: [B] iterator move assign
// CHECK-NEXT: [B] iterator deref: 2
// CHECK-NEXT: [A] iterator advance: 0 += 0
// CHECK-NEXT: [A] iterator move assign
// CHECK-NEXT: [A] iterator deref: 0
// CHECK-NEXT: v=0 w=2
// CHECK-NEXT: [A] iterator dtor
// CHECK-NEXT: [A] iterator advance: 0 += 1
// CHECK-NEXT: [A] iterator move assign
// CHECK-NEXT: [A] iterator deref: 1
// CHECK-NEXT: v=1 w=2
// CHECK-NEXT: [A] iterator dtor
// CHECK-NEXT: [A] iterator advance: 0 += 2
// CHECK-NEXT: [A] iterator move assign
// CHECK-NEXT: [A] iterator deref: 2
// CHECK-NEXT: v=2 w=2
// CHECK-NEXT: [A] iterator dtor
// CHECK-NEXT: [B] iterator dtor
// CHECK-NEXT: [B] iterator dtor
// CHECK-NEXT: [B] iterator dtor
// CHECK-NEXT: [B] iterator dtor
// CHECK-NEXT: [B] dtor
// CHECK-NEXT: [D] dtor
// CHECK-NEXT: [A] iterator dtor
// CHECK-NEXT: [A] iterator dtor
// CHECK-NEXT: [A] iterator dtor
// CHECK-NEXT: [A] dtor
// CHECK-NEXT: [C] dtor
// CHECK-NEXT: done

View File

@ -0,0 +1,38 @@
// 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 interchange
for (int i = 7; i < 17; i += 3)
for (int j = 8; j < 18; j += 3)
printf("i=%d j=%d\n", i, j);
printf("done\n");
return EXIT_SUCCESS;
}
#endif /* HEADER */
// CHECK: do
// CHECK-NEXT: i=7 j=8
// CHECK-NEXT: i=10 j=8
// CHECK-NEXT: i=13 j=8
// CHECK-NEXT: i=16 j=8
// CHECK-NEXT: i=7 j=11
// CHECK-NEXT: i=10 j=11
// CHECK-NEXT: i=13 j=11
// CHECK-NEXT: i=16 j=11
// CHECK-NEXT: i=7 j=14
// CHECK-NEXT: i=10 j=14
// CHECK-NEXT: i=13 j=14
// CHECK-NEXT: i=16 j=14
// CHECK-NEXT: i=7 j=17
// CHECK-NEXT: i=10 j=17
// CHECK-NEXT: i=13 j=17
// CHECK-NEXT: i=16 j=17
// CHECK-NEXT: done

View File

@ -0,0 +1,222 @@
// 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 A("A"), B("B");
#pragma omp interchange
for (auto it = A.begin(); it != A.end(); ++it)
for (auto jt = B.begin(); jt != B.end(); ++jt)
printf("i=%d j=%d\n", *it, *jt);
printf("done\n");
return EXIT_SUCCESS;
}
#endif /* HEADER */
// CHECK: do
// CHECK-NEXT: [A] ctor
// CHECK-NEXT: [B] ctor
// CHECK-NEXT: [A] begin()
// CHECK-NEXT: [A] begin()
// CHECK-NEXT: [A] end()
// CHECK-NEXT: [A] iterator distance: 3
// CHECK-NEXT: [B] begin()
// CHECK-NEXT: [B] begin()
// CHECK-NEXT: [B] end()
// CHECK-NEXT: [B] iterator distance: 3
// CHECK-NEXT: [B] iterator advance: 0 += 0
// CHECK-NEXT: [B] iterator move assign
// CHECK-NEXT: [A] iterator advance: 0 += 0
// CHECK-NEXT: [A] iterator move assign
// CHECK-NEXT: [A] iterator deref: 0
// CHECK-NEXT: [B] iterator deref: 0
// CHECK-NEXT: i=0 j=0
// CHECK-NEXT: [A] iterator dtor
// CHECK-NEXT: [A] iterator advance: 0 += 1
// CHECK-NEXT: [A] iterator move assign
// CHECK-NEXT: [A] iterator deref: 1
// CHECK-NEXT: [B] iterator deref: 0
// CHECK-NEXT: i=1 j=0
// CHECK-NEXT: [A] iterator dtor
// CHECK-NEXT: [A] iterator advance: 0 += 2
// CHECK-NEXT: [A] iterator move assign
// CHECK-NEXT: [A] iterator deref: 2
// CHECK-NEXT: [B] iterator deref: 0
// CHECK-NEXT: i=2 j=0
// CHECK-NEXT: [A] iterator dtor
// CHECK-NEXT: [B] iterator dtor
// CHECK-NEXT: [B] iterator advance: 0 += 1
// CHECK-NEXT: [B] iterator move assign
// CHECK-NEXT: [A] iterator advance: 0 += 0
// CHECK-NEXT: [A] iterator move assign
// CHECK-NEXT: [A] iterator deref: 0
// CHECK-NEXT: [B] iterator deref: 1
// CHECK-NEXT: i=0 j=1
// CHECK-NEXT: [A] iterator dtor
// CHECK-NEXT: [A] iterator advance: 0 += 1
// CHECK-NEXT: [A] iterator move assign
// CHECK-NEXT: [A] iterator deref: 1
// CHECK-NEXT: [B] iterator deref: 1
// CHECK-NEXT: i=1 j=1
// CHECK-NEXT: [A] iterator dtor
// CHECK-NEXT: [A] iterator advance: 0 += 2
// CHECK-NEXT: [A] iterator move assign
// CHECK-NEXT: [A] iterator deref: 2
// CHECK-NEXT: [B] iterator deref: 1
// CHECK-NEXT: i=2 j=1
// CHECK-NEXT: [A] iterator dtor
// CHECK-NEXT: [B] iterator dtor
// CHECK-NEXT: [B] iterator advance: 0 += 2
// CHECK-NEXT: [B] iterator move assign
// CHECK-NEXT: [A] iterator advance: 0 += 0
// CHECK-NEXT: [A] iterator move assign
// CHECK-NEXT: [A] iterator deref: 0
// CHECK-NEXT: [B] iterator deref: 2
// CHECK-NEXT: i=0 j=2
// CHECK-NEXT: [A] iterator dtor
// CHECK-NEXT: [A] iterator advance: 0 += 1
// CHECK-NEXT: [A] iterator move assign
// CHECK-NEXT: [A] iterator deref: 1
// CHECK-NEXT: [B] iterator deref: 2
// CHECK-NEXT: i=1 j=2
// CHECK-NEXT: [A] iterator dtor
// CHECK-NEXT: [A] iterator advance: 0 += 2
// CHECK-NEXT: [A] iterator move assign
// CHECK-NEXT: [A] iterator deref: 2
// CHECK-NEXT: [B] iterator deref: 2
// CHECK-NEXT: i=2 j=2
// CHECK-NEXT: [A] iterator dtor
// CHECK-NEXT: [B] iterator dtor
// CHECK-NEXT: [B] iterator dtor
// CHECK-NEXT: [B] iterator dtor
// CHECK-NEXT: [A] iterator dtor
// CHECK-NEXT: [A] iterator dtor
// CHECK-NEXT: done
// CHECK-NEXT: [B] iterator dtor
// CHECK-NEXT: [A] iterator dtor
// CHECK-NEXT: [B] dtor
// CHECK-NEXT: [A] dtor

View File

@ -0,0 +1,340 @@
// 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);
}
};
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 < 2; ++i)
#pragma omp interchange
for (Reporter c{"C"}; auto &&v : Reporter("A"))
for (Reporter d{"D"}; auto &&w : Reporter("B"))
for (int k = 0; k < 2; ++k)
printf("i=%d v=%d w=%d k=%d\n", i, v, w, k);
printf("done\n");
return EXIT_SUCCESS;
}
#endif /* HEADER */
// CHECK: do
// CHECK-NEXT: [C] ctor
// CHECK-NEXT: [A] ctor
// CHECK-NEXT: [A] end()
// CHECK-NEXT: [A] begin()
// CHECK-NEXT: [A] begin()
// CHECK-NEXT: [A] iterator distance: 3
// CHECK-NEXT: [D] ctor
// CHECK-NEXT: [B] ctor
// CHECK-NEXT: [B] end()
// CHECK-NEXT: [B] begin()
// CHECK-NEXT: [B] begin()
// CHECK-NEXT: [B] iterator distance: 3
// CHECK-NEXT: [B] iterator advance: 0 += 0
// CHECK-NEXT: [B] iterator move assign
// CHECK-NEXT: [B] iterator deref: 0
// CHECK-NEXT: [A] iterator advance: 0 += 0
// CHECK-NEXT: [A] iterator move assign
// CHECK-NEXT: [A] iterator deref: 0
// CHECK-NEXT: i=0 v=0 w=0 k=0
// CHECK-NEXT: i=0 v=0 w=0 k=1
// CHECK-NEXT: [A] iterator dtor
// CHECK-NEXT: [B] iterator dtor
// CHECK-NEXT: [B] iterator advance: 0 += 0
// CHECK-NEXT: [B] iterator move assign
// CHECK-NEXT: [B] iterator deref: 0
// CHECK-NEXT: [A] iterator advance: 0 += 1
// CHECK-NEXT: [A] iterator move assign
// CHECK-NEXT: [A] iterator deref: 1
// CHECK-NEXT: i=0 v=1 w=0 k=0
// CHECK-NEXT: i=0 v=1 w=0 k=1
// CHECK-NEXT: [A] iterator dtor
// CHECK-NEXT: [B] iterator dtor
// CHECK-NEXT: [B] iterator advance: 0 += 0
// CHECK-NEXT: [B] iterator move assign
// CHECK-NEXT: [B] iterator deref: 0
// CHECK-NEXT: [A] iterator advance: 0 += 2
// CHECK-NEXT: [A] iterator move assign
// CHECK-NEXT: [A] iterator deref: 2
// CHECK-NEXT: i=0 v=2 w=0 k=0
// CHECK-NEXT: i=0 v=2 w=0 k=1
// CHECK-NEXT: [A] iterator dtor
// CHECK-NEXT: [B] iterator dtor
// CHECK-NEXT: [B] iterator advance: 0 += 1
// CHECK-NEXT: [B] iterator move assign
// CHECK-NEXT: [B] iterator deref: 1
// CHECK-NEXT: [A] iterator advance: 0 += 0
// CHECK-NEXT: [A] iterator move assign
// CHECK-NEXT: [A] iterator deref: 0
// CHECK-NEXT: i=0 v=0 w=1 k=0
// CHECK-NEXT: i=0 v=0 w=1 k=1
// CHECK-NEXT: [A] iterator dtor
// CHECK-NEXT: [B] iterator dtor
// CHECK-NEXT: [B] iterator advance: 0 += 1
// CHECK-NEXT: [B] iterator move assign
// CHECK-NEXT: [B] iterator deref: 1
// CHECK-NEXT: [A] iterator advance: 0 += 1
// CHECK-NEXT: [A] iterator move assign
// CHECK-NEXT: [A] iterator deref: 1
// CHECK-NEXT: i=0 v=1 w=1 k=0
// CHECK-NEXT: i=0 v=1 w=1 k=1
// CHECK-NEXT: [A] iterator dtor
// CHECK-NEXT: [B] iterator dtor
// CHECK-NEXT: [B] iterator advance: 0 += 1
// CHECK-NEXT: [B] iterator move assign
// CHECK-NEXT: [B] iterator deref: 1
// CHECK-NEXT: [A] iterator advance: 0 += 2
// CHECK-NEXT: [A] iterator move assign
// CHECK-NEXT: [A] iterator deref: 2
// CHECK-NEXT: i=0 v=2 w=1 k=0
// CHECK-NEXT: i=0 v=2 w=1 k=1
// CHECK-NEXT: [A] iterator dtor
// CHECK-NEXT: [B] iterator dtor
// CHECK-NEXT: [B] iterator advance: 0 += 2
// CHECK-NEXT: [B] iterator move assign
// CHECK-NEXT: [B] iterator deref: 2
// CHECK-NEXT: [A] iterator advance: 0 += 0
// CHECK-NEXT: [A] iterator move assign
// CHECK-NEXT: [A] iterator deref: 0
// CHECK-NEXT: i=0 v=0 w=2 k=0
// CHECK-NEXT: i=0 v=0 w=2 k=1
// CHECK-NEXT: [A] iterator dtor
// CHECK-NEXT: [B] iterator dtor
// CHECK-NEXT: [B] iterator advance: 0 += 2
// CHECK-NEXT: [B] iterator move assign
// CHECK-NEXT: [B] iterator deref: 2
// CHECK-NEXT: [A] iterator advance: 0 += 1
// CHECK-NEXT: [A] iterator move assign
// CHECK-NEXT: [A] iterator deref: 1
// CHECK-NEXT: i=0 v=1 w=2 k=0
// CHECK-NEXT: i=0 v=1 w=2 k=1
// CHECK-NEXT: [A] iterator dtor
// CHECK-NEXT: [B] iterator dtor
// CHECK-NEXT: [B] iterator advance: 0 += 2
// CHECK-NEXT: [B] iterator move assign
// CHECK-NEXT: [B] iterator deref: 2
// CHECK-NEXT: [A] iterator advance: 0 += 2
// CHECK-NEXT: [A] iterator move assign
// CHECK-NEXT: [A] iterator deref: 2
// CHECK-NEXT: i=0 v=2 w=2 k=0
// CHECK-NEXT: i=0 v=2 w=2 k=1
// CHECK-NEXT: [A] iterator dtor
// CHECK-NEXT: [B] iterator dtor
// CHECK-NEXT: [B] iterator advance: 0 += 0
// CHECK-NEXT: [B] iterator move assign
// CHECK-NEXT: [B] iterator deref: 0
// CHECK-NEXT: [A] iterator advance: 0 += 0
// CHECK-NEXT: [A] iterator move assign
// CHECK-NEXT: [A] iterator deref: 0
// CHECK-NEXT: i=1 v=0 w=0 k=0
// CHECK-NEXT: i=1 v=0 w=0 k=1
// CHECK-NEXT: [A] iterator dtor
// CHECK-NEXT: [B] iterator dtor
// CHECK-NEXT: [B] iterator advance: 0 += 0
// CHECK-NEXT: [B] iterator move assign
// CHECK-NEXT: [B] iterator deref: 0
// CHECK-NEXT: [A] iterator advance: 0 += 1
// CHECK-NEXT: [A] iterator move assign
// CHECK-NEXT: [A] iterator deref: 1
// CHECK-NEXT: i=1 v=1 w=0 k=0
// CHECK-NEXT: i=1 v=1 w=0 k=1
// CHECK-NEXT: [A] iterator dtor
// CHECK-NEXT: [B] iterator dtor
// CHECK-NEXT: [B] iterator advance: 0 += 0
// CHECK-NEXT: [B] iterator move assign
// CHECK-NEXT: [B] iterator deref: 0
// CHECK-NEXT: [A] iterator advance: 0 += 2
// CHECK-NEXT: [A] iterator move assign
// CHECK-NEXT: [A] iterator deref: 2
// CHECK-NEXT: i=1 v=2 w=0 k=0
// CHECK-NEXT: i=1 v=2 w=0 k=1
// CHECK-NEXT: [A] iterator dtor
// CHECK-NEXT: [B] iterator dtor
// CHECK-NEXT: [B] iterator advance: 0 += 1
// CHECK-NEXT: [B] iterator move assign
// CHECK-NEXT: [B] iterator deref: 1
// CHECK-NEXT: [A] iterator advance: 0 += 0
// CHECK-NEXT: [A] iterator move assign
// CHECK-NEXT: [A] iterator deref: 0
// CHECK-NEXT: i=1 v=0 w=1 k=0
// CHECK-NEXT: i=1 v=0 w=1 k=1
// CHECK-NEXT: [A] iterator dtor
// CHECK-NEXT: [B] iterator dtor
// CHECK-NEXT: [B] iterator advance: 0 += 1
// CHECK-NEXT: [B] iterator move assign
// CHECK-NEXT: [B] iterator deref: 1
// CHECK-NEXT: [A] iterator advance: 0 += 1
// CHECK-NEXT: [A] iterator move assign
// CHECK-NEXT: [A] iterator deref: 1
// CHECK-NEXT: i=1 v=1 w=1 k=0
// CHECK-NEXT: i=1 v=1 w=1 k=1
// CHECK-NEXT: [A] iterator dtor
// CHECK-NEXT: [B] iterator dtor
// CHECK-NEXT: [B] iterator advance: 0 += 1
// CHECK-NEXT: [B] iterator move assign
// CHECK-NEXT: [B] iterator deref: 1
// CHECK-NEXT: [A] iterator advance: 0 += 2
// CHECK-NEXT: [A] iterator move assign
// CHECK-NEXT: [A] iterator deref: 2
// CHECK-NEXT: i=1 v=2 w=1 k=0
// CHECK-NEXT: i=1 v=2 w=1 k=1
// CHECK-NEXT: [A] iterator dtor
// CHECK-NEXT: [B] iterator dtor
// CHECK-NEXT: [B] iterator advance: 0 += 2
// CHECK-NEXT: [B] iterator move assign
// CHECK-NEXT: [B] iterator deref: 2
// CHECK-NEXT: [A] iterator advance: 0 += 0
// CHECK-NEXT: [A] iterator move assign
// CHECK-NEXT: [A] iterator deref: 0
// CHECK-NEXT: i=1 v=0 w=2 k=0
// CHECK-NEXT: i=1 v=0 w=2 k=1
// CHECK-NEXT: [A] iterator dtor
// CHECK-NEXT: [B] iterator dtor
// CHECK-NEXT: [B] iterator advance: 0 += 2
// CHECK-NEXT: [B] iterator move assign
// CHECK-NEXT: [B] iterator deref: 2
// CHECK-NEXT: [A] iterator advance: 0 += 1
// CHECK-NEXT: [A] iterator move assign
// CHECK-NEXT: [A] iterator deref: 1
// CHECK-NEXT: i=1 v=1 w=2 k=0
// CHECK-NEXT: i=1 v=1 w=2 k=1
// CHECK-NEXT: [A] iterator dtor
// CHECK-NEXT: [B] iterator dtor
// CHECK-NEXT: [B] iterator advance: 0 += 2
// CHECK-NEXT: [B] iterator move assign
// CHECK-NEXT: [B] iterator deref: 2
// CHECK-NEXT: [A] iterator advance: 0 += 2
// CHECK-NEXT: [A] iterator move assign
// CHECK-NEXT: [A] iterator deref: 2
// CHECK-NEXT: i=1 v=2 w=2 k=0
// CHECK-NEXT: i=1 v=2 w=2 k=1
// CHECK-NEXT: [A] iterator dtor
// CHECK-NEXT: [B] iterator dtor
// CHECK-NEXT: [B] iterator dtor
// CHECK-NEXT: [B] iterator dtor
// CHECK-NEXT: [B] iterator dtor
// CHECK-NEXT: [B] dtor
// CHECK-NEXT: [D] dtor
// CHECK-NEXT: [A] iterator dtor
// CHECK-NEXT: [A] iterator dtor
// CHECK-NEXT: [A] iterator dtor
// CHECK-NEXT: [A] dtor
// CHECK-NEXT: [C] dtor
// CHECK-NEXT: done

View File

@ -0,0 +1,106 @@
// 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(4) num_threads(1)
for (int i = 0; i < 3; ++i)
#pragma omp interchange
for (int j = 0; j < 3; ++j)
for (int k = 0; k < 3; ++k)
for (int l = 0; l < 3; ++l)
printf("i=%d j=%d k=%d l=%d\n", i, j, k, l);
printf("done\n");
return EXIT_SUCCESS;
}
#endif /* HEADER */
// CHECK: do
// CHECK-NEXT: i=0 j=0 k=0 l=0
// CHECK-NEXT: i=0 j=0 k=0 l=1
// CHECK-NEXT: i=0 j=0 k=0 l=2
// CHECK-NEXT: i=0 j=1 k=0 l=0
// CHECK-NEXT: i=0 j=1 k=0 l=1
// CHECK-NEXT: i=0 j=1 k=0 l=2
// CHECK-NEXT: i=0 j=2 k=0 l=0
// CHECK-NEXT: i=0 j=2 k=0 l=1
// CHECK-NEXT: i=0 j=2 k=0 l=2
// CHECK-NEXT: i=0 j=0 k=1 l=0
// CHECK-NEXT: i=0 j=0 k=1 l=1
// CHECK-NEXT: i=0 j=0 k=1 l=2
// CHECK-NEXT: i=0 j=1 k=1 l=0
// CHECK-NEXT: i=0 j=1 k=1 l=1
// CHECK-NEXT: i=0 j=1 k=1 l=2
// CHECK-NEXT: i=0 j=2 k=1 l=0
// CHECK-NEXT: i=0 j=2 k=1 l=1
// CHECK-NEXT: i=0 j=2 k=1 l=2
// CHECK-NEXT: i=0 j=0 k=2 l=0
// CHECK-NEXT: i=0 j=0 k=2 l=1
// CHECK-NEXT: i=0 j=0 k=2 l=2
// CHECK-NEXT: i=0 j=1 k=2 l=0
// CHECK-NEXT: i=0 j=1 k=2 l=1
// CHECK-NEXT: i=0 j=1 k=2 l=2
// CHECK-NEXT: i=0 j=2 k=2 l=0
// CHECK-NEXT: i=0 j=2 k=2 l=1
// CHECK-NEXT: i=0 j=2 k=2 l=2
// CHECK-NEXT: i=1 j=0 k=0 l=0
// CHECK-NEXT: i=1 j=0 k=0 l=1
// CHECK-NEXT: i=1 j=0 k=0 l=2
// CHECK-NEXT: i=1 j=1 k=0 l=0
// CHECK-NEXT: i=1 j=1 k=0 l=1
// CHECK-NEXT: i=1 j=1 k=0 l=2
// CHECK-NEXT: i=1 j=2 k=0 l=0
// CHECK-NEXT: i=1 j=2 k=0 l=1
// CHECK-NEXT: i=1 j=2 k=0 l=2
// CHECK-NEXT: i=1 j=0 k=1 l=0
// CHECK-NEXT: i=1 j=0 k=1 l=1
// CHECK-NEXT: i=1 j=0 k=1 l=2
// CHECK-NEXT: i=1 j=1 k=1 l=0
// CHECK-NEXT: i=1 j=1 k=1 l=1
// CHECK-NEXT: i=1 j=1 k=1 l=2
// CHECK-NEXT: i=1 j=2 k=1 l=0
// CHECK-NEXT: i=1 j=2 k=1 l=1
// CHECK-NEXT: i=1 j=2 k=1 l=2
// CHECK-NEXT: i=1 j=0 k=2 l=0
// CHECK-NEXT: i=1 j=0 k=2 l=1
// CHECK-NEXT: i=1 j=0 k=2 l=2
// CHECK-NEXT: i=1 j=1 k=2 l=0
// CHECK-NEXT: i=1 j=1 k=2 l=1
// CHECK-NEXT: i=1 j=1 k=2 l=2
// CHECK-NEXT: i=1 j=2 k=2 l=0
// CHECK-NEXT: i=1 j=2 k=2 l=1
// CHECK-NEXT: i=1 j=2 k=2 l=2
// CHECK-NEXT: i=2 j=0 k=0 l=0
// CHECK-NEXT: i=2 j=0 k=0 l=1
// CHECK-NEXT: i=2 j=0 k=0 l=2
// CHECK-NEXT: i=2 j=1 k=0 l=0
// CHECK-NEXT: i=2 j=1 k=0 l=1
// CHECK-NEXT: i=2 j=1 k=0 l=2
// CHECK-NEXT: i=2 j=2 k=0 l=0
// CHECK-NEXT: i=2 j=2 k=0 l=1
// CHECK-NEXT: i=2 j=2 k=0 l=2
// CHECK-NEXT: i=2 j=0 k=1 l=0
// CHECK-NEXT: i=2 j=0 k=1 l=1
// CHECK-NEXT: i=2 j=0 k=1 l=2
// CHECK-NEXT: i=2 j=1 k=1 l=0
// CHECK-NEXT: i=2 j=1 k=1 l=1
// CHECK-NEXT: i=2 j=1 k=1 l=2
// CHECK-NEXT: i=2 j=2 k=1 l=0
// CHECK-NEXT: i=2 j=2 k=1 l=1
// CHECK-NEXT: i=2 j=2 k=1 l=2
// CHECK-NEXT: i=2 j=0 k=2 l=0
// CHECK-NEXT: i=2 j=0 k=2 l=1
// CHECK-NEXT: i=2 j=0 k=2 l=2
// CHECK-NEXT: i=2 j=1 k=2 l=0
// CHECK-NEXT: i=2 j=1 k=2 l=1
// CHECK-NEXT: i=2 j=1 k=2 l=2
// CHECK-NEXT: i=2 j=2 k=2 l=0
// CHECK-NEXT: i=2 j=2 k=2 l=1
// CHECK-NEXT: i=2 j=2 k=2 l=2
// CHECK-NEXT: done