[Clang][OpenMP][LoopTransformations] Implement "#pragma omp fuse" loop transformation directive and "looprange" clause (#139293)
This change implements the fuse directive, `#pragma omp fuse`, as specified in the OpenMP 6.0, along with the `looprange` clause in clang. This change also adds minimal stubs so flang keeps compiling (a full implementation in flang of this directive is still pending). --------- Co-authored-by: Roger Ferrer Ibanez <roger.ferrer@bsc.es>
This commit is contained in:
parent
98563d850d
commit
cd4c5280c7
@ -1446,6 +1446,9 @@ class CursorKind(BaseEnumeration):
|
||||
# OpenMP stripe directive.
|
||||
OMP_STRIPE_DIRECTIVE = 310
|
||||
|
||||
# OpenMP fuse directive.
|
||||
OMP_FUSE_DIRECTIVE = 311
|
||||
|
||||
# OpenACC Compute Construct.
|
||||
OPEN_ACC_COMPUTE_DIRECTIVE = 320
|
||||
|
||||
|
||||
@ -482,6 +482,8 @@ implementation.
|
||||
+-------------------------------------------------------------+---------------------------+---------------------------+--------------------------------------------------------------------------+
|
||||
| loop transformation apply clause | :none:`unclaimed` | :none:`unclaimed` | |
|
||||
+-------------------------------------------------------------+---------------------------+---------------------------+--------------------------------------------------------------------------+
|
||||
| loop fuse transformation | :good:`done` | :none:`unclaimed` | |
|
||||
+-------------------------------------------------------------+---------------------------+---------------------------+--------------------------------------------------------------------------+
|
||||
| workdistribute construct | | :none:`in progress` | @skc7, @mjklemm |
|
||||
+-------------------------------------------------------------+---------------------------+---------------------------+--------------------------------------------------------------------------+
|
||||
| task_iteration | :none:`unclaimed` | :none:`unclaimed` | |
|
||||
|
||||
@ -599,6 +599,7 @@ OpenMP Support
|
||||
- Added support for ``defaultmap`` directive implicit-behavior ``storage``.
|
||||
- Added support for ``defaultmap`` directive implicit-behavior ``private``.
|
||||
- Added parsing and semantic analysis support for ``groupprivate`` directive.
|
||||
- Added support for 'omp fuse' directive.
|
||||
|
||||
Improvements
|
||||
^^^^^^^^^^^^
|
||||
|
||||
@ -2162,6 +2162,10 @@ enum CXCursorKind {
|
||||
*/
|
||||
CXCursor_OMPStripeDirective = 310,
|
||||
|
||||
/** OpenMP fuse directive
|
||||
*/
|
||||
CXCursor_OMPFuseDirective = 311,
|
||||
|
||||
/** OpenACC Compute Construct.
|
||||
*/
|
||||
CXCursor_OpenACCComputeConstruct = 320,
|
||||
|
||||
@ -1149,6 +1149,80 @@ public:
|
||||
static OMPFullClause *CreateEmpty(const ASTContext &C);
|
||||
};
|
||||
|
||||
/// This class represents the 'looprange' clause in the
|
||||
/// '#pragma omp fuse' directive
|
||||
///
|
||||
/// \code {c}
|
||||
/// #pragma omp fuse looprange(1,2)
|
||||
/// {
|
||||
/// for(int i = 0; i < 64; ++i)
|
||||
/// for(int j = 0; j < 256; j+=2)
|
||||
/// for(int k = 127; k >= 0; --k)
|
||||
/// \endcode
|
||||
class OMPLoopRangeClause final : public OMPClause {
|
||||
friend class OMPClauseReader;
|
||||
/// Location of '('
|
||||
SourceLocation LParenLoc;
|
||||
|
||||
/// Location of first and count expressions
|
||||
SourceLocation FirstLoc, CountLoc;
|
||||
|
||||
/// Number of looprange arguments (always 2: first, count)
|
||||
enum { FirstExpr, CountExpr, NumArgs };
|
||||
Stmt *Args[NumArgs] = {nullptr, nullptr};
|
||||
|
||||
/// Set looprange 'first' expression
|
||||
void setFirst(Expr *E) { Args[FirstExpr] = E; }
|
||||
|
||||
/// Set looprange 'count' expression
|
||||
void setCount(Expr *E) { Args[CountExpr] = E; }
|
||||
|
||||
/// Build an empty clause for deserialization.
|
||||
explicit OMPLoopRangeClause()
|
||||
: OMPClause(llvm::omp::OMPC_looprange, {}, {}) {}
|
||||
|
||||
public:
|
||||
/// Build a 'looprange' clause AST node.
|
||||
static OMPLoopRangeClause *
|
||||
Create(const ASTContext &C, SourceLocation StartLoc, SourceLocation LParenLoc,
|
||||
SourceLocation FirstLoc, SourceLocation CountLoc,
|
||||
SourceLocation EndLoc, Expr *First, Expr *Count);
|
||||
|
||||
/// Build an empty 'looprange' clause node.
|
||||
static OMPLoopRangeClause *CreateEmpty(const ASTContext &C);
|
||||
|
||||
// Location getters/setters
|
||||
SourceLocation getLParenLoc() const { return LParenLoc; }
|
||||
SourceLocation getFirstLoc() const { return FirstLoc; }
|
||||
SourceLocation getCountLoc() const { return CountLoc; }
|
||||
|
||||
void setLParenLoc(SourceLocation Loc) { LParenLoc = Loc; }
|
||||
void setFirstLoc(SourceLocation Loc) { FirstLoc = Loc; }
|
||||
void setCountLoc(SourceLocation Loc) { CountLoc = Loc; }
|
||||
|
||||
/// Get looprange 'first' expression
|
||||
Expr *getFirst() const { return cast_or_null<Expr>(Args[FirstExpr]); }
|
||||
|
||||
/// Get looprange 'count' expression
|
||||
Expr *getCount() const { return cast_or_null<Expr>(Args[CountExpr]); }
|
||||
|
||||
child_range children() { return child_range(Args, Args + NumArgs); }
|
||||
const_child_range children() const {
|
||||
return const_child_range(Args, Args + NumArgs);
|
||||
}
|
||||
|
||||
child_range used_children() {
|
||||
return child_range(child_iterator(), child_iterator());
|
||||
}
|
||||
const_child_range used_children() const {
|
||||
return const_child_range(const_child_iterator(), const_child_iterator());
|
||||
}
|
||||
|
||||
static bool classof(const OMPClause *T) {
|
||||
return T->getClauseKind() == llvm::omp::OMPC_looprange;
|
||||
}
|
||||
};
|
||||
|
||||
/// Representation of the 'partial' clause of the '#pragma omp unroll'
|
||||
/// directive.
|
||||
///
|
||||
|
||||
@ -3177,6 +3177,9 @@ DEF_TRAVERSE_STMT(OMPUnrollDirective,
|
||||
DEF_TRAVERSE_STMT(OMPReverseDirective,
|
||||
{ TRY_TO(TraverseOMPExecutableDirective(S)); })
|
||||
|
||||
DEF_TRAVERSE_STMT(OMPFuseDirective,
|
||||
{ TRY_TO(TraverseOMPExecutableDirective(S)); })
|
||||
|
||||
DEF_TRAVERSE_STMT(OMPInterchangeDirective,
|
||||
{ TRY_TO(TraverseOMPExecutableDirective(S)); })
|
||||
|
||||
@ -3494,6 +3497,14 @@ bool RecursiveASTVisitor<Derived>::VisitOMPFullClause(OMPFullClause *C) {
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Derived>
|
||||
bool RecursiveASTVisitor<Derived>::VisitOMPLoopRangeClause(
|
||||
OMPLoopRangeClause *C) {
|
||||
TRY_TO(TraverseStmt(C->getFirst()));
|
||||
TRY_TO(TraverseStmt(C->getCount()));
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename Derived>
|
||||
bool RecursiveASTVisitor<Derived>::VisitOMPPartialClause(OMPPartialClause *C) {
|
||||
TRY_TO(TraverseStmt(C->getFactor()));
|
||||
|
||||
@ -21,6 +21,7 @@
|
||||
#include "clang/AST/StmtCXX.h"
|
||||
#include "clang/Basic/OpenMPKinds.h"
|
||||
#include "clang/Basic/SourceLocation.h"
|
||||
#include "llvm/Support/Casting.h"
|
||||
|
||||
namespace clang {
|
||||
|
||||
@ -677,6 +678,10 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
// Forward declaration of a generic loop transformation. Used in the declaration
|
||||
// of OMPLoopBasedDirective.
|
||||
class OMPLoopTransformationDirective;
|
||||
|
||||
/// The base class for all loop-based directives, including loop transformation
|
||||
/// directives.
|
||||
class OMPLoopBasedDirective : public OMPExecutableDirective {
|
||||
@ -889,24 +894,23 @@ public:
|
||||
|
||||
/// Calls the specified callback function for all the loops in \p CurStmt,
|
||||
/// from the outermost to the innermost.
|
||||
static bool doForAllLoops(
|
||||
Stmt *CurStmt, bool TryImperfectlyNestedLoops, unsigned NumLoops,
|
||||
llvm::function_ref<bool(unsigned, Stmt *)> Callback,
|
||||
llvm::function_ref<void(OMPCanonicalLoopNestTransformationDirective *)>
|
||||
OnTransformationCallback);
|
||||
static bool
|
||||
doForAllLoops(Stmt *CurStmt, bool TryImperfectlyNestedLoops,
|
||||
unsigned NumLoops,
|
||||
llvm::function_ref<bool(unsigned, Stmt *)> Callback,
|
||||
llvm::function_ref<void(OMPLoopTransformationDirective *)>
|
||||
OnTransformationCallback);
|
||||
static bool
|
||||
doForAllLoops(const Stmt *CurStmt, bool TryImperfectlyNestedLoops,
|
||||
unsigned NumLoops,
|
||||
llvm::function_ref<bool(unsigned, const Stmt *)> Callback,
|
||||
llvm::function_ref<
|
||||
void(const OMPCanonicalLoopNestTransformationDirective *)>
|
||||
llvm::function_ref<void(const OMPLoopTransformationDirective *)>
|
||||
OnTransformationCallback) {
|
||||
auto &&NewCallback = [Callback](unsigned Cnt, Stmt *CurStmt) {
|
||||
return Callback(Cnt, CurStmt);
|
||||
};
|
||||
auto &&NewTransformCb =
|
||||
[OnTransformationCallback](
|
||||
OMPCanonicalLoopNestTransformationDirective *A) {
|
||||
[OnTransformationCallback](OMPLoopTransformationDirective *A) {
|
||||
OnTransformationCallback(A);
|
||||
};
|
||||
return doForAllLoops(const_cast<Stmt *>(CurStmt), TryImperfectlyNestedLoops,
|
||||
@ -919,7 +923,7 @@ public:
|
||||
doForAllLoops(Stmt *CurStmt, bool TryImperfectlyNestedLoops,
|
||||
unsigned NumLoops,
|
||||
llvm::function_ref<bool(unsigned, Stmt *)> Callback) {
|
||||
auto &&TransformCb = [](OMPCanonicalLoopNestTransformationDirective *) {};
|
||||
auto &&TransformCb = [](OMPLoopTransformationDirective *) {};
|
||||
return doForAllLoops(CurStmt, TryImperfectlyNestedLoops, NumLoops, Callback,
|
||||
TransformCb);
|
||||
}
|
||||
@ -957,9 +961,11 @@ public:
|
||||
};
|
||||
|
||||
/// Common class of data shared between
|
||||
/// OMPCanonicalLoopNestTransformationDirective and transformations over
|
||||
/// canonical loop sequences.
|
||||
/// OMPCanonicalLoopNestTransformationDirective and
|
||||
/// OMPCanonicalLoopSequenceTransformationDirective
|
||||
class OMPLoopTransformationDirective {
|
||||
friend class ASTStmtReader;
|
||||
|
||||
/// Number of (top-level) generated loops.
|
||||
/// This value is 1 for most transformations as they only map one loop nest
|
||||
/// into another.
|
||||
@ -969,15 +975,39 @@ class OMPLoopTransformationDirective {
|
||||
/// generate more than one loop nest, so the value would be >= 1.
|
||||
unsigned NumGeneratedTopLevelLoops = 1;
|
||||
|
||||
/// We need this because we cannot easily make OMPLoopTransformationDirective
|
||||
/// a proper Stmt.
|
||||
Stmt *S = nullptr;
|
||||
|
||||
protected:
|
||||
void setNumGeneratedTopLevelLoops(unsigned N) {
|
||||
NumGeneratedTopLevelLoops = N;
|
||||
}
|
||||
|
||||
explicit OMPLoopTransformationDirective(Stmt *S) : S(S) {}
|
||||
|
||||
public:
|
||||
unsigned getNumGeneratedTopLevelLoops() const {
|
||||
return NumGeneratedTopLevelLoops;
|
||||
}
|
||||
|
||||
/// Returns the specific directive related to this loop transformation.
|
||||
Stmt *getDirective() const { return S; }
|
||||
|
||||
/// Get the de-sugared statements after the loop transformation.
|
||||
///
|
||||
/// Might be nullptr if either the directive generates no loops and is handled
|
||||
/// directly in CodeGen, or resolving a template-dependence context is
|
||||
/// required.
|
||||
Stmt *getTransformedStmt() const;
|
||||
|
||||
/// Return preinits statement.
|
||||
Stmt *getPreInits() const;
|
||||
|
||||
static bool classof(const Stmt *T) {
|
||||
return isa<OMPCanonicalLoopNestTransformationDirective,
|
||||
OMPCanonicalLoopSequenceTransformationDirective>(T);
|
||||
}
|
||||
};
|
||||
|
||||
/// The base class for all transformation directives of canonical loop nests.
|
||||
@ -990,7 +1020,8 @@ protected:
|
||||
explicit OMPCanonicalLoopNestTransformationDirective(
|
||||
StmtClass SC, OpenMPDirectiveKind Kind, SourceLocation StartLoc,
|
||||
SourceLocation EndLoc, unsigned NumAssociatedLoops)
|
||||
: OMPLoopBasedDirective(SC, Kind, StartLoc, EndLoc, NumAssociatedLoops) {}
|
||||
: OMPLoopBasedDirective(SC, Kind, StartLoc, EndLoc, NumAssociatedLoops),
|
||||
OMPLoopTransformationDirective(this) {}
|
||||
|
||||
public:
|
||||
/// Return the number of associated (consumed) loops.
|
||||
@ -5928,6 +5959,112 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/// The base class for all transformation directives of canonical loop
|
||||
/// sequences (currently only 'fuse')
|
||||
class OMPCanonicalLoopSequenceTransformationDirective
|
||||
: public OMPExecutableDirective,
|
||||
public OMPLoopTransformationDirective {
|
||||
friend class ASTStmtReader;
|
||||
|
||||
protected:
|
||||
explicit OMPCanonicalLoopSequenceTransformationDirective(
|
||||
StmtClass SC, OpenMPDirectiveKind Kind, SourceLocation StartLoc,
|
||||
SourceLocation EndLoc)
|
||||
: OMPExecutableDirective(SC, Kind, StartLoc, EndLoc),
|
||||
OMPLoopTransformationDirective(this) {}
|
||||
|
||||
public:
|
||||
/// Get the de-sugared statements after the loop transformation.
|
||||
///
|
||||
/// Might be nullptr if either the directive generates no loops and is handled
|
||||
/// directly in CodeGen, or resolving a template-dependence context is
|
||||
/// required.
|
||||
Stmt *getTransformedStmt() const;
|
||||
|
||||
/// Return preinits statement.
|
||||
Stmt *getPreInits() const;
|
||||
|
||||
static bool classof(const Stmt *T) {
|
||||
Stmt::StmtClass C = T->getStmtClass();
|
||||
return C == OMPFuseDirectiveClass;
|
||||
}
|
||||
};
|
||||
|
||||
/// Represents the '#pragma omp fuse' loop transformation directive
|
||||
///
|
||||
/// \code{c}
|
||||
/// #pragma omp fuse
|
||||
/// {
|
||||
/// for(int i = 0; i < m1; ++i) {...}
|
||||
/// for(int j = 0; j < m2; ++j) {...}
|
||||
/// ...
|
||||
/// }
|
||||
/// \endcode
|
||||
class OMPFuseDirective final
|
||||
: public OMPCanonicalLoopSequenceTransformationDirective {
|
||||
friend class ASTStmtReader;
|
||||
friend class OMPExecutableDirective;
|
||||
|
||||
// Offsets of child members.
|
||||
enum {
|
||||
PreInitsOffset = 0,
|
||||
TransformedStmtOffset,
|
||||
};
|
||||
|
||||
explicit OMPFuseDirective(SourceLocation StartLoc, SourceLocation EndLoc)
|
||||
: OMPCanonicalLoopSequenceTransformationDirective(
|
||||
OMPFuseDirectiveClass, llvm::omp::OMPD_fuse, StartLoc, EndLoc) {}
|
||||
|
||||
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 fuse'
|
||||
///
|
||||
/// \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 Total number of loops in the canonical loop sequence.
|
||||
/// \param NumGeneratedTopLevelLoops Number of top-level generated loops.
|
||||
// Typically 1 but looprange clause can
|
||||
// change this.
|
||||
/// \param AssociatedStmt The outermost associated loop
|
||||
/// \param TransformedStmt The loop nest after fusion, or nullptr in
|
||||
/// dependent
|
||||
/// \param PreInits Helper preinits statements for the loop nest
|
||||
static OMPFuseDirective *
|
||||
Create(const ASTContext &C, SourceLocation StartLoc, SourceLocation EndLoc,
|
||||
ArrayRef<OMPClause *> Clauses, unsigned NumGeneratedTopLevelLoops,
|
||||
Stmt *AssociatedStmt, Stmt *TransformedStmt, Stmt *PreInits);
|
||||
|
||||
/// Build an empty '#pragma omp fuse' AST node for deserialization
|
||||
///
|
||||
/// \param C Context of the AST
|
||||
/// \param NumClauses Number of clauses to allocate
|
||||
/// \param NumLoops Number of top level loops to allocate
|
||||
static OMPFuseDirective *CreateEmpty(const ASTContext &C,
|
||||
unsigned NumClauses);
|
||||
|
||||
/// Gets the associated loops after the transformation. This is the de-sugared
|
||||
/// replacement or nulltpr 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() == OMPFuseDirectiveClass;
|
||||
}
|
||||
};
|
||||
|
||||
/// This represents '#pragma omp scan' directive.
|
||||
///
|
||||
/// \code
|
||||
@ -6596,4 +6733,37 @@ public:
|
||||
|
||||
} // end namespace clang
|
||||
|
||||
namespace llvm {
|
||||
// Allow a Stmt* be casted correctly to an OMPLoopTransformationDirective*.
|
||||
// The default routines would just use a C-style cast which won't work well
|
||||
// for the multiple inheritance here. We have to use a static cast from the
|
||||
// corresponding subclass.
|
||||
template <>
|
||||
struct CastInfo<clang::OMPLoopTransformationDirective, clang::Stmt *>
|
||||
: public NullableValueCastFailed<clang::OMPLoopTransformationDirective *>,
|
||||
public DefaultDoCastIfPossible<
|
||||
clang::OMPLoopTransformationDirective *, clang::Stmt *,
|
||||
CastInfo<clang::OMPLoopTransformationDirective, clang::Stmt *>> {
|
||||
static bool isPossible(const clang::Stmt *T) {
|
||||
return clang::OMPLoopTransformationDirective::classof(T);
|
||||
}
|
||||
|
||||
static clang::OMPLoopTransformationDirective *doCast(clang::Stmt *T) {
|
||||
if (auto *D =
|
||||
dyn_cast<clang::OMPCanonicalLoopNestTransformationDirective>(T))
|
||||
return static_cast<clang::OMPLoopTransformationDirective *>(D);
|
||||
if (auto *D =
|
||||
dyn_cast<clang::OMPCanonicalLoopSequenceTransformationDirective>(T))
|
||||
return static_cast<clang::OMPLoopTransformationDirective *>(D);
|
||||
llvm_unreachable("unexpected type");
|
||||
}
|
||||
};
|
||||
template <>
|
||||
struct CastInfo<clang::OMPLoopTransformationDirective, const clang::Stmt *>
|
||||
: public ConstStrippingForwardingCast<
|
||||
clang::OMPLoopTransformationDirective, const clang::Stmt *,
|
||||
CastInfo<clang::OMPLoopTransformationDirective, clang::Stmt *>> {};
|
||||
|
||||
} // namespace llvm
|
||||
|
||||
#endif
|
||||
|
||||
@ -11761,6 +11761,18 @@ def note_omp_implicit_dsa : Note<
|
||||
"implicitly determined as %0">;
|
||||
def err_omp_loop_var_dsa : Error<
|
||||
"loop iteration variable in the associated loop of 'omp %1' directive may not be %0, predetermined as %2">;
|
||||
def err_omp_not_a_loop_sequence
|
||||
: Error<"statement after '#pragma omp %0' must be a loop sequence "
|
||||
"containing canonical loops or loop-generating constructs">;
|
||||
def err_omp_empty_loop_sequence
|
||||
: Error<"loop sequence after '#pragma omp %0' must contain at least 1 "
|
||||
"canonical loop or loop-generating construct">;
|
||||
def err_omp_invalid_looprange
|
||||
: Error<"looprange clause selects loops from %1 to %2 but this exceeds the "
|
||||
"number of loops (%3) in the loop sequence">;
|
||||
def warn_omp_redundant_fusion : Warning<"looprange clause selects a single "
|
||||
"loop, resulting in redundant fusion">,
|
||||
InGroup<OpenMPClauses>;
|
||||
def err_omp_not_for : Error<
|
||||
"%select{statement after '#pragma omp %1' must be a for loop|"
|
||||
"expected %2 for loops after '#pragma omp %1'%select{|, but found only %4}3}0">;
|
||||
|
||||
@ -391,6 +391,13 @@ bool isOpenMPLoopBoundSharingDirective(OpenMPDirectiveKind Kind);
|
||||
bool isOpenMPCanonicalLoopNestTransformationDirective(
|
||||
OpenMPDirectiveKind DKind);
|
||||
|
||||
/// Checks if the specified directive is a loop transformation directive that
|
||||
/// applies to a canonical loop sequence.
|
||||
/// \param DKind Specified directive.
|
||||
/// \return True iff the directive is a loop transformation.
|
||||
bool isOpenMPCanonicalLoopSequenceTransformationDirective(
|
||||
OpenMPDirectiveKind DKind);
|
||||
|
||||
/// Checks if the specified directive is a loop transformation directive.
|
||||
/// \param DKind Specified directive.
|
||||
/// \return True iff the directive is a loop transformation.
|
||||
|
||||
@ -238,6 +238,10 @@ def OMPUnrollDirective : StmtNode<OMPCanonicalLoopNestTransformationDirective>;
|
||||
def OMPReverseDirective : StmtNode<OMPCanonicalLoopNestTransformationDirective>;
|
||||
def OMPInterchangeDirective
|
||||
: StmtNode<OMPCanonicalLoopNestTransformationDirective>;
|
||||
def OMPCanonicalLoopSequenceTransformationDirective
|
||||
: StmtNode<OMPExecutableDirective, 1>;
|
||||
def OMPFuseDirective
|
||||
: StmtNode<OMPCanonicalLoopSequenceTransformationDirective>;
|
||||
def OMPForDirective : StmtNode<OMPLoopDirective>;
|
||||
def OMPForSimdDirective : StmtNode<OMPLoopDirective>;
|
||||
def OMPSectionsDirective : StmtNode<OMPExecutableDirective>;
|
||||
|
||||
@ -6767,6 +6767,9 @@ private:
|
||||
OpenMPClauseKind Kind,
|
||||
bool ParseOnly);
|
||||
|
||||
/// Parses the 'looprange' clause of a '#pragma omp fuse' directive.
|
||||
OMPClause *ParseOpenMPLoopRangeClause();
|
||||
|
||||
/// Parses the 'sizes' clause of a '#pragma omp tile' directive.
|
||||
OMPClause *ParseOpenMPSizesClause();
|
||||
|
||||
|
||||
@ -463,6 +463,13 @@ public:
|
||||
Stmt *AStmt,
|
||||
SourceLocation StartLoc,
|
||||
SourceLocation EndLoc);
|
||||
|
||||
/// Called on well-formed '#pragma omp fuse' after parsing of its
|
||||
/// clauses and the associated statement.
|
||||
StmtResult ActOnOpenMPFuseDirective(ArrayRef<OMPClause *> Clauses,
|
||||
Stmt *AStmt, SourceLocation StartLoc,
|
||||
SourceLocation EndLoc);
|
||||
|
||||
/// Called on well-formed '\#pragma omp for' after parsing
|
||||
/// of the associated statement.
|
||||
StmtResult
|
||||
@ -921,6 +928,12 @@ public:
|
||||
SourceLocation StartLoc,
|
||||
SourceLocation LParenLoc,
|
||||
SourceLocation EndLoc);
|
||||
|
||||
/// Called on well-form 'looprange' clause after parsing its arguments.
|
||||
OMPClause *
|
||||
ActOnOpenMPLoopRangeClause(Expr *First, Expr *Count, SourceLocation StartLoc,
|
||||
SourceLocation LParenLoc, SourceLocation FirstLoc,
|
||||
SourceLocation CountLoc, SourceLocation EndLoc);
|
||||
/// Called on well-formed 'ordered' clause.
|
||||
OMPClause *
|
||||
ActOnOpenMPOrderedClause(SourceLocation StartLoc, SourceLocation EndLoc,
|
||||
@ -1485,7 +1498,81 @@ private:
|
||||
bool checkTransformableLoopNest(
|
||||
OpenMPDirectiveKind Kind, Stmt *AStmt, int NumLoops,
|
||||
SmallVectorImpl<OMPLoopBasedDirective::HelperExprs> &LoopHelpers,
|
||||
Stmt *&Body, SmallVectorImpl<SmallVector<Stmt *, 0>> &OriginalInits);
|
||||
Stmt *&Body, SmallVectorImpl<SmallVector<Stmt *>> &OriginalInits);
|
||||
|
||||
/// Holds the result of the analysis of a (possibly canonical) loop.
|
||||
struct LoopAnalysis {
|
||||
/// The analyzed loop or loop transformation.
|
||||
Stmt *AStmt = nullptr;
|
||||
/// Loop analyses results.
|
||||
OMPLoopBasedDirective::HelperExprs HelperExprs;
|
||||
/// The for-statement of the loop. TheForStmt equals AStmt only when the
|
||||
/// latter is a canonical loop (i.e. not a loop transformation).
|
||||
Stmt *TheForStmt = nullptr;
|
||||
/// Initialization statements before transformations.
|
||||
SmallVector<Stmt *> OriginalInits;
|
||||
/// Initialization statements required after transformation of this loop.
|
||||
SmallVector<Stmt *> TransformsPreInits;
|
||||
|
||||
explicit LoopAnalysis(Stmt *S) : AStmt(S) {}
|
||||
|
||||
bool isRegularLoop() const { return isRegularLoop(AStmt); }
|
||||
bool isLoopTransformation() const { return isLoopTransformation(AStmt); }
|
||||
|
||||
// Convenience functions used when building LoopSequenceAnalysis.
|
||||
static bool isRegularLoop(Stmt *S) {
|
||||
return isa<ForStmt, CXXForRangeStmt>(S);
|
||||
}
|
||||
static bool isLoopTransformation(Stmt *S) {
|
||||
return isa<OMPLoopTransformationDirective>(S);
|
||||
}
|
||||
};
|
||||
|
||||
/// Holds the result of the analysis of a (possibly canonical) loop sequence.
|
||||
struct LoopSequenceAnalysis {
|
||||
/// Number of top level canonical loops.
|
||||
unsigned LoopSeqSize = 0;
|
||||
/// For each loop results of the analysis.
|
||||
SmallVector<LoopAnalysis, 2> Loops;
|
||||
/// Additional code required before entering the transformed loop sequence.
|
||||
SmallVector<Stmt *> LoopSequencePreInits;
|
||||
|
||||
// Convenience function used when building the LoopSequenceAnalysis.
|
||||
static bool isLoopSequenceDerivation(Stmt *S) {
|
||||
return LoopAnalysis::isRegularLoop(S) ||
|
||||
LoopAnalysis::isLoopTransformation(S);
|
||||
}
|
||||
};
|
||||
|
||||
/// The main recursive process of `checkTransformableLoopSequence` that
|
||||
/// performs grammatical parsing of a canonical loop sequence. It extracts
|
||||
/// key information, such as the number of top-level loops, loop statements,
|
||||
/// helper expressions, and other relevant loop-related data, all in a single
|
||||
/// execution to avoid redundant traversals. This analysis flattens inner
|
||||
/// Loop Sequences
|
||||
///
|
||||
/// \param LoopSeqStmt The AST of the original statement.
|
||||
/// \param SeqAnalysis [out] Result of the analysis of \p LoopSeqStmt
|
||||
/// \param Context
|
||||
/// \param Kind The loop transformation directive kind.
|
||||
/// \return Whether the original statement is both syntactically and
|
||||
/// semantically correct according to OpenMP 6.0 canonical loop
|
||||
/// sequence definition.
|
||||
bool analyzeLoopSequence(Stmt *LoopSeqStmt, LoopSequenceAnalysis &SeqAnalysis,
|
||||
ASTContext &Context, OpenMPDirectiveKind Kind);
|
||||
|
||||
/// Validates and checks whether a loop sequence can be transformed according
|
||||
/// to the given directive, providing necessary setup and initialization
|
||||
/// (Driver function) before recursion using `analyzeLoopSequence`.
|
||||
///
|
||||
/// \param Kind The loop transformation directive kind.
|
||||
/// \param AStmt The AST of the original statement
|
||||
/// \param SeqAnalysis [out] Result of the analysis of \p LoopSeqStmt
|
||||
/// \param Context
|
||||
/// \return Whether there was an absence of errors or not
|
||||
bool checkTransformableLoopSequence(OpenMPDirectiveKind Kind, Stmt *AStmt,
|
||||
LoopSequenceAnalysis &SeqAnalysis,
|
||||
ASTContext &Context);
|
||||
|
||||
/// Helper to keep information about the current `omp begin/end declare
|
||||
/// variant` nesting.
|
||||
|
||||
@ -1951,6 +1951,7 @@ enum StmtCode {
|
||||
STMT_OMP_UNROLL_DIRECTIVE,
|
||||
STMT_OMP_REVERSE_DIRECTIVE,
|
||||
STMT_OMP_INTERCHANGE_DIRECTIVE,
|
||||
STMT_OMP_FUSE_DIRECTIVE,
|
||||
STMT_OMP_FOR_DIRECTIVE,
|
||||
STMT_OMP_FOR_SIMD_DIRECTIVE,
|
||||
STMT_OMP_SECTIONS_DIRECTIVE,
|
||||
|
||||
@ -1024,6 +1024,26 @@ OMPPartialClause *OMPPartialClause::CreateEmpty(const ASTContext &C) {
|
||||
return new (C) OMPPartialClause();
|
||||
}
|
||||
|
||||
OMPLoopRangeClause *
|
||||
OMPLoopRangeClause::Create(const ASTContext &C, SourceLocation StartLoc,
|
||||
SourceLocation LParenLoc, SourceLocation FirstLoc,
|
||||
SourceLocation CountLoc, SourceLocation EndLoc,
|
||||
Expr *First, Expr *Count) {
|
||||
OMPLoopRangeClause *Clause = CreateEmpty(C);
|
||||
Clause->setLocStart(StartLoc);
|
||||
Clause->setLParenLoc(LParenLoc);
|
||||
Clause->setFirstLoc(FirstLoc);
|
||||
Clause->setCountLoc(CountLoc);
|
||||
Clause->setLocEnd(EndLoc);
|
||||
Clause->setFirst(First);
|
||||
Clause->setCount(Count);
|
||||
return Clause;
|
||||
}
|
||||
|
||||
OMPLoopRangeClause *OMPLoopRangeClause::CreateEmpty(const ASTContext &C) {
|
||||
return new (C) OMPLoopRangeClause();
|
||||
}
|
||||
|
||||
OMPAllocateClause *OMPAllocateClause::Create(
|
||||
const ASTContext &C, SourceLocation StartLoc, SourceLocation LParenLoc,
|
||||
Expr *Allocator, Expr *Alignment, SourceLocation ColonLoc,
|
||||
@ -1964,6 +1984,21 @@ void OMPClausePrinter::VisitOMPPartialClause(OMPPartialClause *Node) {
|
||||
}
|
||||
}
|
||||
|
||||
void OMPClausePrinter::VisitOMPLoopRangeClause(OMPLoopRangeClause *Node) {
|
||||
OS << "looprange";
|
||||
|
||||
Expr *First = Node->getFirst();
|
||||
Expr *Count = Node->getCount();
|
||||
|
||||
if (First && Count) {
|
||||
OS << "(";
|
||||
First->printPretty(OS, nullptr, Policy, 0);
|
||||
OS << ",";
|
||||
Count->printPretty(OS, nullptr, Policy, 0);
|
||||
OS << ")";
|
||||
}
|
||||
}
|
||||
|
||||
void OMPClausePrinter::VisitOMPAllocatorClause(OMPAllocatorClause *Node) {
|
||||
OS << "allocator(";
|
||||
Node->getAllocator()->printPretty(OS, nullptr, Policy, 0);
|
||||
|
||||
@ -125,13 +125,12 @@ OMPLoopBasedDirective::tryToFindNextInnerLoop(Stmt *CurStmt,
|
||||
bool OMPLoopBasedDirective::doForAllLoops(
|
||||
Stmt *CurStmt, bool TryImperfectlyNestedLoops, unsigned NumLoops,
|
||||
llvm::function_ref<bool(unsigned, Stmt *)> Callback,
|
||||
llvm::function_ref<void(OMPCanonicalLoopNestTransformationDirective *)>
|
||||
llvm::function_ref<void(OMPLoopTransformationDirective *)>
|
||||
OnTransformationCallback) {
|
||||
CurStmt = CurStmt->IgnoreContainers();
|
||||
for (unsigned Cnt = 0; Cnt < NumLoops; ++Cnt) {
|
||||
while (true) {
|
||||
auto *Dir =
|
||||
dyn_cast<OMPCanonicalLoopNestTransformationDirective>(CurStmt);
|
||||
auto *Dir = dyn_cast<OMPLoopTransformationDirective>(CurStmt);
|
||||
if (!Dir)
|
||||
break;
|
||||
|
||||
@ -371,6 +370,22 @@ OMPForDirective *OMPForDirective::Create(
|
||||
return Dir;
|
||||
}
|
||||
|
||||
Stmt *OMPLoopTransformationDirective::getTransformedStmt() const {
|
||||
if (auto *D = dyn_cast<OMPCanonicalLoopNestTransformationDirective>(S))
|
||||
return D->getTransformedStmt();
|
||||
if (auto *D = dyn_cast<OMPCanonicalLoopSequenceTransformationDirective>(S))
|
||||
return D->getTransformedStmt();
|
||||
llvm_unreachable("unexpected object type");
|
||||
}
|
||||
|
||||
Stmt *OMPLoopTransformationDirective::getPreInits() const {
|
||||
if (auto *D = dyn_cast<OMPCanonicalLoopNestTransformationDirective>(S))
|
||||
return D->getPreInits();
|
||||
if (auto *D = dyn_cast<OMPCanonicalLoopSequenceTransformationDirective>(S))
|
||||
return D->getPreInits();
|
||||
llvm_unreachable("unexpected object type");
|
||||
}
|
||||
|
||||
Stmt *OMPCanonicalLoopNestTransformationDirective::getTransformedStmt() const {
|
||||
switch (getStmtClass()) {
|
||||
#define STMT(CLASS, PARENT)
|
||||
@ -380,7 +395,7 @@ Stmt *OMPCanonicalLoopNestTransformationDirective::getTransformedStmt() const {
|
||||
return static_cast<const CLASS *>(this)->getTransformedStmt();
|
||||
#include "clang/AST/StmtNodes.inc"
|
||||
default:
|
||||
llvm_unreachable("Not a loop transformation");
|
||||
llvm_unreachable("Not a loop transformation for canonical loop nests");
|
||||
}
|
||||
}
|
||||
|
||||
@ -393,7 +408,34 @@ Stmt *OMPCanonicalLoopNestTransformationDirective::getPreInits() const {
|
||||
return static_cast<const CLASS *>(this)->getPreInits();
|
||||
#include "clang/AST/StmtNodes.inc"
|
||||
default:
|
||||
llvm_unreachable("Not a loop transformation");
|
||||
llvm_unreachable("Not a loop transformation for canonical loop nests");
|
||||
}
|
||||
}
|
||||
|
||||
Stmt *
|
||||
OMPCanonicalLoopSequenceTransformationDirective::getTransformedStmt() const {
|
||||
switch (getStmtClass()) {
|
||||
#define STMT(CLASS, PARENT)
|
||||
#define ABSTRACT_STMT(CLASS)
|
||||
#define OMPCANONICALLOOPSEQUENCETRANSFORMATIONDIRECTIVE(CLASS, PARENT) \
|
||||
case Stmt::CLASS##Class: \
|
||||
return static_cast<const CLASS *>(this)->getTransformedStmt();
|
||||
#include "clang/AST/StmtNodes.inc"
|
||||
default:
|
||||
llvm_unreachable("Not a loop transformation for canonical loop sequences");
|
||||
}
|
||||
}
|
||||
|
||||
Stmt *OMPCanonicalLoopSequenceTransformationDirective::getPreInits() const {
|
||||
switch (getStmtClass()) {
|
||||
#define STMT(CLASS, PARENT)
|
||||
#define ABSTRACT_STMT(CLASS)
|
||||
#define OMPCANONICALLOOPSEQUENCETRANSFORMATIONDIRECTIVE(CLASS, PARENT) \
|
||||
case Stmt::CLASS##Class: \
|
||||
return static_cast<const CLASS *>(this)->getPreInits();
|
||||
#include "clang/AST/StmtNodes.inc"
|
||||
default:
|
||||
llvm_unreachable("Not a loop transformation for canonical loop sequences");
|
||||
}
|
||||
}
|
||||
|
||||
@ -510,6 +552,27 @@ OMPInterchangeDirective::CreateEmpty(const ASTContext &C, unsigned NumClauses,
|
||||
SourceLocation(), SourceLocation(), NumLoops);
|
||||
}
|
||||
|
||||
OMPFuseDirective *OMPFuseDirective::Create(
|
||||
const ASTContext &C, SourceLocation StartLoc, SourceLocation EndLoc,
|
||||
ArrayRef<OMPClause *> Clauses, unsigned NumGeneratedTopLevelLoops,
|
||||
Stmt *AssociatedStmt, Stmt *TransformedStmt, Stmt *PreInits) {
|
||||
|
||||
OMPFuseDirective *Dir = createDirective<OMPFuseDirective>(
|
||||
C, Clauses, AssociatedStmt, TransformedStmtOffset + 1, StartLoc, EndLoc);
|
||||
Dir->setTransformedStmt(TransformedStmt);
|
||||
Dir->setPreInits(PreInits);
|
||||
Dir->setNumGeneratedTopLevelLoops(NumGeneratedTopLevelLoops);
|
||||
return Dir;
|
||||
}
|
||||
|
||||
OMPFuseDirective *OMPFuseDirective::CreateEmpty(const ASTContext &C,
|
||||
unsigned NumClauses) {
|
||||
OMPFuseDirective *Dir = createEmptyDirective<OMPFuseDirective>(
|
||||
C, NumClauses, /*HasAssociatedStmt=*/true, TransformedStmtOffset + 1,
|
||||
SourceLocation(), SourceLocation());
|
||||
return Dir;
|
||||
}
|
||||
|
||||
OMPForSimdDirective *
|
||||
OMPForSimdDirective::Create(const ASTContext &C, SourceLocation StartLoc,
|
||||
SourceLocation EndLoc, unsigned CollapsedNum,
|
||||
|
||||
@ -795,6 +795,11 @@ void StmtPrinter::VisitOMPInterchangeDirective(OMPInterchangeDirective *Node) {
|
||||
PrintOMPExecutableDirective(Node);
|
||||
}
|
||||
|
||||
void StmtPrinter::VisitOMPFuseDirective(OMPFuseDirective *Node) {
|
||||
Indent() << "#pragma omp fuse";
|
||||
PrintOMPExecutableDirective(Node);
|
||||
}
|
||||
|
||||
void StmtPrinter::VisitOMPForDirective(OMPForDirective *Node) {
|
||||
Indent() << "#pragma omp for";
|
||||
PrintOMPExecutableDirective(Node);
|
||||
|
||||
@ -510,6 +510,13 @@ void OMPClauseProfiler::VisitOMPPartialClause(const OMPPartialClause *C) {
|
||||
Profiler->VisitExpr(Factor);
|
||||
}
|
||||
|
||||
void OMPClauseProfiler::VisitOMPLoopRangeClause(const OMPLoopRangeClause *C) {
|
||||
if (const Expr *First = C->getFirst())
|
||||
Profiler->VisitExpr(First);
|
||||
if (const Expr *Count = C->getCount())
|
||||
Profiler->VisitExpr(Count);
|
||||
}
|
||||
|
||||
void OMPClauseProfiler::VisitOMPAllocatorClause(const OMPAllocatorClause *C) {
|
||||
if (C->getAllocator())
|
||||
Profiler->VisitStmt(C->getAllocator());
|
||||
@ -1025,6 +1032,15 @@ void StmtProfiler::VisitOMPInterchangeDirective(
|
||||
VisitOMPCanonicalLoopNestTransformationDirective(S);
|
||||
}
|
||||
|
||||
void StmtProfiler::VisitOMPCanonicalLoopSequenceTransformationDirective(
|
||||
const OMPCanonicalLoopSequenceTransformationDirective *S) {
|
||||
VisitOMPExecutableDirective(S);
|
||||
}
|
||||
|
||||
void StmtProfiler::VisitOMPFuseDirective(const OMPFuseDirective *S) {
|
||||
VisitOMPCanonicalLoopSequenceTransformationDirective(S);
|
||||
}
|
||||
|
||||
void StmtProfiler::VisitOMPForDirective(const OMPForDirective *S) {
|
||||
VisitOMPLoopDirective(S);
|
||||
}
|
||||
|
||||
@ -282,6 +282,7 @@ unsigned clang::getOpenMPSimpleClauseType(OpenMPClauseKind Kind, StringRef Str,
|
||||
case OMPC_affinity:
|
||||
case OMPC_when:
|
||||
case OMPC_append_args:
|
||||
case OMPC_looprange:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@ -627,6 +628,7 @@ const char *clang::getOpenMPSimpleClauseTypeName(OpenMPClauseKind Kind,
|
||||
case OMPC_affinity:
|
||||
case OMPC_when:
|
||||
case OMPC_append_args:
|
||||
case OMPC_looprange:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@ -755,9 +757,14 @@ bool clang::isOpenMPCanonicalLoopNestTransformationDirective(
|
||||
DKind == OMPD_interchange || DKind == OMPD_stripe;
|
||||
}
|
||||
|
||||
bool clang::isOpenMPCanonicalLoopSequenceTransformationDirective(
|
||||
OpenMPDirectiveKind DKind) {
|
||||
return DKind == OMPD_fuse;
|
||||
}
|
||||
|
||||
bool clang::isOpenMPLoopTransformationDirective(OpenMPDirectiveKind DKind) {
|
||||
// FIXME: There will be more cases when we implement 'fuse'.
|
||||
return isOpenMPCanonicalLoopNestTransformationDirective(DKind);
|
||||
return isOpenMPCanonicalLoopNestTransformationDirective(DKind) ||
|
||||
isOpenMPCanonicalLoopSequenceTransformationDirective(DKind);
|
||||
}
|
||||
|
||||
bool clang::isOpenMPCombinedParallelADirective(OpenMPDirectiveKind DKind) {
|
||||
|
||||
@ -234,6 +234,9 @@ void CodeGenFunction::EmitStmt(const Stmt *S, ArrayRef<const Attr *> Attrs) {
|
||||
case Stmt::OMPInterchangeDirectiveClass:
|
||||
EmitOMPInterchangeDirective(cast<OMPInterchangeDirective>(*S));
|
||||
break;
|
||||
case Stmt::OMPFuseDirectiveClass:
|
||||
EmitOMPFuseDirective(cast<OMPFuseDirective>(*S));
|
||||
break;
|
||||
case Stmt::OMPForDirectiveClass:
|
||||
EmitOMPForDirective(cast<OMPForDirective>(*S));
|
||||
break;
|
||||
|
||||
@ -201,6 +201,24 @@ class OMPLoopScope : public CodeGenFunction::RunCleanupsScope {
|
||||
} else {
|
||||
llvm_unreachable("Unknown loop-based directive kind.");
|
||||
}
|
||||
doEmitPreinits(PreInits);
|
||||
PreCondVars.restore(CGF);
|
||||
}
|
||||
|
||||
void
|
||||
emitPreInitStmt(CodeGenFunction &CGF,
|
||||
const OMPCanonicalLoopSequenceTransformationDirective &S) {
|
||||
const Stmt *PreInits;
|
||||
if (const auto *Fuse = dyn_cast<OMPFuseDirective>(&S)) {
|
||||
PreInits = Fuse->getPreInits();
|
||||
} else {
|
||||
llvm_unreachable(
|
||||
"Unknown canonical loop sequence transform directive kind.");
|
||||
}
|
||||
doEmitPreinits(PreInits);
|
||||
}
|
||||
|
||||
void doEmitPreinits(const Stmt *PreInits) {
|
||||
if (PreInits) {
|
||||
// CompoundStmts and DeclStmts are used as lists of PreInit statements and
|
||||
// declarations. Since declarations must be visible in the the following
|
||||
@ -222,7 +240,6 @@ class OMPLoopScope : public CodeGenFunction::RunCleanupsScope {
|
||||
CGF.EmitStmt(S);
|
||||
}
|
||||
}
|
||||
PreCondVars.restore(CGF);
|
||||
}
|
||||
|
||||
public:
|
||||
@ -230,6 +247,11 @@ public:
|
||||
: CodeGenFunction::RunCleanupsScope(CGF) {
|
||||
emitPreInitStmt(CGF, S);
|
||||
}
|
||||
OMPLoopScope(CodeGenFunction &CGF,
|
||||
const OMPCanonicalLoopSequenceTransformationDirective &S)
|
||||
: CodeGenFunction::RunCleanupsScope(CGF) {
|
||||
emitPreInitStmt(CGF, S);
|
||||
}
|
||||
};
|
||||
|
||||
class OMPSimdLexicalScope : public CodeGenFunction::LexicalScope {
|
||||
@ -1929,6 +1951,15 @@ public:
|
||||
CGSI = new CodeGenFunction::CGCapturedStmtInfo(CR_OpenMP);
|
||||
CapInfoRAII = new CodeGenFunction::CGCapturedStmtRAII(CGF, CGSI);
|
||||
}
|
||||
if (const auto *Dir =
|
||||
dyn_cast<OMPCanonicalLoopSequenceTransformationDirective>(S)) {
|
||||
// For simplicity we reuse the loop scope similarly to what we do with
|
||||
// OMPCanonicalLoopNestTransformationDirective do by being a subclass
|
||||
// of OMPLoopBasedDirective.
|
||||
Scope = new OMPLoopScope(CGF, *Dir);
|
||||
CGSI = new CodeGenFunction::CGCapturedStmtInfo(CR_OpenMP);
|
||||
CapInfoRAII = new CodeGenFunction::CGCapturedStmtRAII(CGF, CGSI);
|
||||
}
|
||||
}
|
||||
~OMPTransformDirectiveScopeRAII() {
|
||||
if (!Scope)
|
||||
@ -1956,8 +1987,7 @@ static void emitBody(CodeGenFunction &CGF, const Stmt *S, const Stmt *NextLoop,
|
||||
return;
|
||||
}
|
||||
if (SimplifiedS == NextLoop) {
|
||||
if (auto *Dir =
|
||||
dyn_cast<OMPCanonicalLoopNestTransformationDirective>(SimplifiedS))
|
||||
if (auto *Dir = dyn_cast<OMPLoopTransformationDirective>(SimplifiedS))
|
||||
SimplifiedS = Dir->getTransformedStmt();
|
||||
if (const auto *CanonLoop = dyn_cast<OMPCanonicalLoop>(SimplifiedS))
|
||||
SimplifiedS = CanonLoop->getLoopStmt();
|
||||
@ -2952,6 +2982,12 @@ void CodeGenFunction::EmitOMPInterchangeDirective(
|
||||
EmitStmt(S.getTransformedStmt());
|
||||
}
|
||||
|
||||
void CodeGenFunction::EmitOMPFuseDirective(const OMPFuseDirective &S) {
|
||||
// Emit the de-sugared statement
|
||||
OMPTransformDirectiveScopeRAII FuseScope(*this, &S);
|
||||
EmitStmt(S.getTransformedStmt());
|
||||
}
|
||||
|
||||
void CodeGenFunction::EmitOMPUnrollDirective(const OMPUnrollDirective &S) {
|
||||
bool UseOMPIRBuilder = CGM.getLangOpts().OpenMPIRBuilder;
|
||||
|
||||
|
||||
@ -3861,6 +3861,7 @@ public:
|
||||
void EmitOMPUnrollDirective(const OMPUnrollDirective &S);
|
||||
void EmitOMPReverseDirective(const OMPReverseDirective &S);
|
||||
void EmitOMPInterchangeDirective(const OMPInterchangeDirective &S);
|
||||
void EmitOMPFuseDirective(const OMPFuseDirective &S);
|
||||
void EmitOMPForDirective(const OMPForDirective &S);
|
||||
void EmitOMPForSimdDirective(const OMPForSimdDirective &S);
|
||||
void EmitOMPScopeDirective(const OMPScopeDirective &S);
|
||||
|
||||
@ -2968,6 +2968,39 @@ OMPClause *Parser::ParseOpenMPSizesClause() {
|
||||
OpenLoc, CloseLoc);
|
||||
}
|
||||
|
||||
OMPClause *Parser::ParseOpenMPLoopRangeClause() {
|
||||
SourceLocation ClauseNameLoc = ConsumeToken();
|
||||
SourceLocation FirstLoc, CountLoc;
|
||||
|
||||
BalancedDelimiterTracker T(*this, tok::l_paren, tok::annot_pragma_openmp_end);
|
||||
if (T.consumeOpen()) {
|
||||
Diag(Tok, diag::err_expected) << tok::l_paren;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FirstLoc = Tok.getLocation();
|
||||
ExprResult FirstVal = ParseConstantExpression();
|
||||
if (!FirstVal.isUsable()) {
|
||||
T.skipToEnd();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ExpectAndConsume(tok::comma);
|
||||
|
||||
CountLoc = Tok.getLocation();
|
||||
ExprResult CountVal = ParseConstantExpression();
|
||||
if (!CountVal.isUsable()) {
|
||||
T.skipToEnd();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
T.consumeClose();
|
||||
|
||||
return Actions.OpenMP().ActOnOpenMPLoopRangeClause(
|
||||
FirstVal.get(), CountVal.get(), ClauseNameLoc, T.getOpenLocation(),
|
||||
FirstLoc, CountLoc, T.getCloseLocation());
|
||||
}
|
||||
|
||||
OMPClause *Parser::ParseOpenMPPermutationClause() {
|
||||
SourceLocation ClauseNameLoc, OpenLoc, CloseLoc;
|
||||
SmallVector<Expr *> ArgExprs;
|
||||
@ -3473,6 +3506,9 @@ OMPClause *Parser::ParseOpenMPClause(OpenMPDirectiveKind DKind,
|
||||
}
|
||||
Clause = ParseOpenMPClause(CKind, WrongDirective);
|
||||
break;
|
||||
case OMPC_looprange:
|
||||
Clause = ParseOpenMPLoopRangeClause();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1493,6 +1493,7 @@ CanThrowResult Sema::canThrow(const Stmt *S) {
|
||||
case Stmt::OMPUnrollDirectiveClass:
|
||||
case Stmt::OMPReverseDirectiveClass:
|
||||
case Stmt::OMPInterchangeDirectiveClass:
|
||||
case Stmt::OMPFuseDirectiveClass:
|
||||
case Stmt::OMPSingleDirectiveClass:
|
||||
case Stmt::OMPTargetDataDirectiveClass:
|
||||
case Stmt::OMPTargetDirectiveClass:
|
||||
|
||||
@ -4569,6 +4569,7 @@ void SemaOpenMP::ActOnOpenMPRegionStart(OpenMPDirectiveKind DKind,
|
||||
case OMPD_unroll:
|
||||
case OMPD_reverse:
|
||||
case OMPD_interchange:
|
||||
case OMPD_fuse:
|
||||
case OMPD_assume:
|
||||
break;
|
||||
default:
|
||||
@ -6410,6 +6411,10 @@ StmtResult SemaOpenMP::ActOnOpenMPExecutableDirective(
|
||||
Res = ActOnOpenMPInterchangeDirective(ClausesWithImplicit, AStmt, StartLoc,
|
||||
EndLoc);
|
||||
break;
|
||||
case OMPD_fuse:
|
||||
Res =
|
||||
ActOnOpenMPFuseDirective(ClausesWithImplicit, AStmt, StartLoc, EndLoc);
|
||||
break;
|
||||
case OMPD_for:
|
||||
Res = ActOnOpenMPForDirective(ClausesWithImplicit, AStmt, StartLoc, EndLoc,
|
||||
VarsWithInheritedDSA);
|
||||
@ -9488,7 +9493,9 @@ static bool checkOpenMPIterationSpace(
|
||||
// sharing attributes.
|
||||
VarsWithImplicitDSA.erase(LCDecl);
|
||||
|
||||
assert(isOpenMPLoopDirective(DKind) && "DSA for non-loop vars");
|
||||
assert((isOpenMPLoopDirective(DKind) ||
|
||||
isOpenMPCanonicalLoopSequenceTransformationDirective(DKind)) &&
|
||||
"DSA for non-loop vars");
|
||||
|
||||
// Check test-expr.
|
||||
HasErrors |= ISC.checkAndSetCond(For ? For->getCond() : CXXFor->getCond());
|
||||
@ -9916,7 +9923,8 @@ checkOpenMPLoop(OpenMPDirectiveKind DKind, Expr *CollapseLoopCountExpr,
|
||||
unsigned NumLoops = std::max(OrderedLoopCount, NestedLoopCount);
|
||||
SmallVector<LoopIterationSpace, 4> IterSpaces(NumLoops);
|
||||
if (!OMPLoopBasedDirective::doForAllLoops(
|
||||
AStmt->IgnoreContainers(!isOpenMPLoopTransformationDirective(DKind)),
|
||||
AStmt->IgnoreContainers(
|
||||
!isOpenMPCanonicalLoopNestTransformationDirective(DKind)),
|
||||
SupportsNonPerfectlyNested, NumLoops,
|
||||
[DKind, &SemaRef, &DSA, NumLoops, NestedLoopCount,
|
||||
CollapseLoopCountExpr, OrderedLoopCountExpr, &VarsWithImplicitDSA,
|
||||
@ -9938,8 +9946,7 @@ checkOpenMPLoop(OpenMPDirectiveKind DKind, Expr *CollapseLoopCountExpr,
|
||||
}
|
||||
return false;
|
||||
},
|
||||
[&SemaRef,
|
||||
&Captures](OMPCanonicalLoopNestTransformationDirective *Transform) {
|
||||
[&SemaRef, &Captures](OMPLoopTransformationDirective *Transform) {
|
||||
Stmt *DependentPreInits = Transform->getPreInits();
|
||||
if (!DependentPreInits)
|
||||
return;
|
||||
@ -9954,7 +9961,8 @@ checkOpenMPLoop(OpenMPDirectiveKind DKind, Expr *CollapseLoopCountExpr,
|
||||
auto *D = cast<VarDecl>(C);
|
||||
DeclRefExpr *Ref = buildDeclRefExpr(
|
||||
SemaRef, D, D->getType().getNonReferenceType(),
|
||||
Transform->getBeginLoc());
|
||||
cast<OMPExecutableDirective>(Transform->getDirective())
|
||||
->getBeginLoc());
|
||||
Captures[Ref] = Ref;
|
||||
}
|
||||
}
|
||||
@ -14404,10 +14412,34 @@ StmtResult SemaOpenMP::ActOnOpenMPTargetTeamsDistributeSimdDirective(
|
||||
getASTContext(), StartLoc, EndLoc, NestedLoopCount, Clauses, AStmt, B);
|
||||
}
|
||||
|
||||
/// Updates OriginalInits by checking Transform against loop transformation
|
||||
/// directives and appending their pre-inits if a match is found.
|
||||
static void updatePreInits(OMPLoopTransformationDirective *Transform,
|
||||
SmallVectorImpl<Stmt *> &PreInits) {
|
||||
Stmt *Dir = Transform->getDirective();
|
||||
switch (Dir->getStmtClass()) {
|
||||
#define STMT(CLASS, PARENT)
|
||||
#define ABSTRACT_STMT(CLASS)
|
||||
#define COMMON_OMP_LOOP_TRANSFORMATION(CLASS, PARENT) \
|
||||
case Stmt::CLASS##Class: \
|
||||
appendFlattenedStmtList(PreInits, \
|
||||
static_cast<const CLASS *>(Dir)->getPreInits()); \
|
||||
break;
|
||||
#define OMPCANONICALLOOPNESTTRANSFORMATIONDIRECTIVE(CLASS, PARENT) \
|
||||
COMMON_OMP_LOOP_TRANSFORMATION(CLASS, PARENT)
|
||||
#define OMPCANONICALLOOPSEQUENCETRANSFORMATIONDIRECTIVE(CLASS, PARENT) \
|
||||
COMMON_OMP_LOOP_TRANSFORMATION(CLASS, PARENT)
|
||||
#include "clang/AST/StmtNodes.inc"
|
||||
#undef COMMON_OMP_LOOP_TRANSFORMATION
|
||||
default:
|
||||
llvm_unreachable("Not a loop transformation");
|
||||
}
|
||||
}
|
||||
|
||||
bool SemaOpenMP::checkTransformableLoopNest(
|
||||
OpenMPDirectiveKind Kind, Stmt *AStmt, int NumLoops,
|
||||
SmallVectorImpl<OMPLoopBasedDirective::HelperExprs> &LoopHelpers,
|
||||
Stmt *&Body, SmallVectorImpl<SmallVector<Stmt *, 0>> &OriginalInits) {
|
||||
Stmt *&Body, SmallVectorImpl<SmallVector<Stmt *>> &OriginalInits) {
|
||||
OriginalInits.emplace_back();
|
||||
bool Result = OMPLoopBasedDirective::doForAllLoops(
|
||||
AStmt->IgnoreContainers(), /*TryImperfectlyNestedLoops=*/false, NumLoops,
|
||||
@ -14433,29 +14465,268 @@ bool SemaOpenMP::checkTransformableLoopNest(
|
||||
OriginalInits.emplace_back();
|
||||
return false;
|
||||
},
|
||||
[&OriginalInits](OMPLoopBasedDirective *Transform) {
|
||||
Stmt *DependentPreInits;
|
||||
if (auto *Dir = dyn_cast<OMPTileDirective>(Transform))
|
||||
DependentPreInits = Dir->getPreInits();
|
||||
else if (auto *Dir = dyn_cast<OMPStripeDirective>(Transform))
|
||||
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 if (auto *Dir = dyn_cast<OMPInterchangeDirective>(Transform))
|
||||
DependentPreInits = Dir->getPreInits();
|
||||
else
|
||||
llvm_unreachable("Unhandled loop transformation");
|
||||
|
||||
appendFlattenedStmtList(OriginalInits.back(), DependentPreInits);
|
||||
[&OriginalInits](OMPLoopTransformationDirective *Transform) {
|
||||
updatePreInits(Transform, OriginalInits.back());
|
||||
});
|
||||
assert(OriginalInits.back().empty() && "No preinit after innermost loop");
|
||||
OriginalInits.pop_back();
|
||||
return Result;
|
||||
}
|
||||
|
||||
/// Add preinit statements that need to be propageted from the selected loop.
|
||||
/// Counts the total number of OpenMP canonical nested loops, including the
|
||||
/// outermost loop (the original loop). PRECONDITION of this visitor is that it
|
||||
/// must be invoked from the original loop to be analyzed. The traversal stops
|
||||
/// for Decl's and Expr's given that they may contain inner loops that must not
|
||||
/// be counted.
|
||||
///
|
||||
/// Example AST structure for the code:
|
||||
///
|
||||
/// int main() {
|
||||
/// #pragma omp fuse
|
||||
/// {
|
||||
/// for (int i = 0; i < 100; i++) { <-- Outer loop
|
||||
/// []() {
|
||||
/// for(int j = 0; j < 100; j++) {} <-- NOT A LOOP (1)
|
||||
/// };
|
||||
/// for(int j = 0; j < 5; ++j) {} <-- Inner loop
|
||||
/// }
|
||||
/// for (int r = 0; i < 100; i++) { <-- Outer loop
|
||||
/// struct LocalClass {
|
||||
/// void bar() {
|
||||
/// for(int j = 0; j < 100; j++) {} <-- NOT A LOOP (2)
|
||||
/// }
|
||||
/// };
|
||||
/// for(int k = 0; k < 10; ++k) {} <-- Inner loop
|
||||
/// {x = 5; for(k = 0; k < 10; ++k) x += k; x}; <-- NOT A LOOP (3)
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// (1) because in a different function (here: a lambda)
|
||||
/// (2) because in a different function (here: class method)
|
||||
/// (3) because considered to be intervening-code of non-perfectly nested loop
|
||||
/// Result: Loop 'i' contains 2 loops, Loop 'r' also contains 2 loops.
|
||||
class NestedLoopCounterVisitor final : public DynamicRecursiveASTVisitor {
|
||||
private:
|
||||
unsigned NestedLoopCount = 0;
|
||||
|
||||
public:
|
||||
explicit NestedLoopCounterVisitor() = default;
|
||||
|
||||
unsigned getNestedLoopCount() const { return NestedLoopCount; }
|
||||
|
||||
bool VisitForStmt(ForStmt *FS) override {
|
||||
++NestedLoopCount;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VisitCXXForRangeStmt(CXXForRangeStmt *FRS) override {
|
||||
++NestedLoopCount;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TraverseStmt(Stmt *S) override {
|
||||
if (!S)
|
||||
return true;
|
||||
|
||||
// Skip traversal of all expressions, including special cases like
|
||||
// LambdaExpr, StmtExpr, BlockExpr, and RequiresExpr. These expressions
|
||||
// may contain inner statements (and even loops), but they are not part
|
||||
// of the syntactic body of the surrounding loop structure.
|
||||
// Therefore must not be counted.
|
||||
if (isa<Expr>(S))
|
||||
return true;
|
||||
|
||||
// Only recurse into CompoundStmt (block {}) and loop bodies.
|
||||
if (isa<CompoundStmt, ForStmt, CXXForRangeStmt>(S)) {
|
||||
return DynamicRecursiveASTVisitor::TraverseStmt(S);
|
||||
}
|
||||
|
||||
// Stop traversal of the rest of statements, that break perfect
|
||||
// loop nesting, such as control flow (IfStmt, SwitchStmt...).
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TraverseDecl(Decl *D) override {
|
||||
// Stop in the case of finding a declaration, it is not important
|
||||
// in order to find nested loops (Possible CXXRecordDecl, RecordDecl,
|
||||
// FunctionDecl...).
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
bool SemaOpenMP::analyzeLoopSequence(Stmt *LoopSeqStmt,
|
||||
LoopSequenceAnalysis &SeqAnalysis,
|
||||
ASTContext &Context,
|
||||
OpenMPDirectiveKind Kind) {
|
||||
VarsWithInheritedDSAType TmpDSA;
|
||||
// Helper Lambda to handle storing initialization and body statements for
|
||||
// both ForStmt and CXXForRangeStmt.
|
||||
auto StoreLoopStatements = [](LoopAnalysis &Analysis, Stmt *LoopStmt) {
|
||||
if (auto *For = dyn_cast<ForStmt>(LoopStmt)) {
|
||||
Analysis.OriginalInits.push_back(For->getInit());
|
||||
Analysis.TheForStmt = For;
|
||||
} else {
|
||||
auto *CXXFor = cast<CXXForRangeStmt>(LoopStmt);
|
||||
Analysis.OriginalInits.push_back(CXXFor->getBeginStmt());
|
||||
Analysis.TheForStmt = CXXFor;
|
||||
}
|
||||
};
|
||||
|
||||
// Helper lambda functions to encapsulate the processing of different
|
||||
// derivations of the canonical loop sequence grammar
|
||||
// Modularized code for handling loop generation and transformations.
|
||||
auto AnalyzeLoopGeneration = [&](Stmt *Child) {
|
||||
auto *LoopTransform = cast<OMPLoopTransformationDirective>(Child);
|
||||
Stmt *TransformedStmt = LoopTransform->getTransformedStmt();
|
||||
unsigned NumGeneratedTopLevelLoops =
|
||||
LoopTransform->getNumGeneratedTopLevelLoops();
|
||||
// Handle the case where transformed statement is not available due to
|
||||
// dependent contexts
|
||||
if (!TransformedStmt) {
|
||||
if (NumGeneratedTopLevelLoops > 0) {
|
||||
SeqAnalysis.LoopSeqSize += NumGeneratedTopLevelLoops;
|
||||
return true;
|
||||
}
|
||||
// Unroll full (0 loops produced)
|
||||
Diag(Child->getBeginLoc(), diag::err_omp_not_for)
|
||||
<< 0 << getOpenMPDirectiveName(Kind);
|
||||
return false;
|
||||
}
|
||||
// Handle loop transformations with multiple loop nests
|
||||
// Unroll full
|
||||
if (!NumGeneratedTopLevelLoops) {
|
||||
Diag(Child->getBeginLoc(), diag::err_omp_not_for)
|
||||
<< 0 << getOpenMPDirectiveName(Kind);
|
||||
return false;
|
||||
}
|
||||
// Loop transformatons such as split or loopranged fuse
|
||||
if (NumGeneratedTopLevelLoops > 1) {
|
||||
// Get the preinits related to this loop sequence generating
|
||||
// loop transformation (i.e loopranged fuse, split...)
|
||||
// These preinits differ slightly from regular inits/pre-inits related
|
||||
// to single loop generating loop transformations (interchange, unroll)
|
||||
// given that they are not bounded to a particular loop nest
|
||||
// so they need to be treated independently
|
||||
updatePreInits(LoopTransform, SeqAnalysis.LoopSequencePreInits);
|
||||
return analyzeLoopSequence(TransformedStmt, SeqAnalysis, Context, Kind);
|
||||
}
|
||||
// Vast majority: (Tile, Unroll, Stripe, Reverse, Interchange, Fuse all)
|
||||
// Process the transformed loop statement
|
||||
LoopAnalysis &NewTransformedSingleLoop =
|
||||
SeqAnalysis.Loops.emplace_back(Child);
|
||||
unsigned IsCanonical = checkOpenMPLoop(
|
||||
Kind, nullptr, nullptr, TransformedStmt, SemaRef, *DSAStack, TmpDSA,
|
||||
NewTransformedSingleLoop.HelperExprs);
|
||||
|
||||
if (!IsCanonical)
|
||||
return false;
|
||||
|
||||
StoreLoopStatements(NewTransformedSingleLoop, TransformedStmt);
|
||||
updatePreInits(LoopTransform, NewTransformedSingleLoop.TransformsPreInits);
|
||||
|
||||
SeqAnalysis.LoopSeqSize++;
|
||||
return true;
|
||||
};
|
||||
|
||||
// Modularized code for handling regular canonical loops.
|
||||
auto AnalyzeRegularLoop = [&](Stmt *Child) {
|
||||
LoopAnalysis &NewRegularLoop = SeqAnalysis.Loops.emplace_back(Child);
|
||||
unsigned IsCanonical =
|
||||
checkOpenMPLoop(Kind, nullptr, nullptr, Child, SemaRef, *DSAStack,
|
||||
TmpDSA, NewRegularLoop.HelperExprs);
|
||||
|
||||
if (!IsCanonical)
|
||||
return false;
|
||||
|
||||
StoreLoopStatements(NewRegularLoop, Child);
|
||||
NestedLoopCounterVisitor NLCV;
|
||||
NLCV.TraverseStmt(Child);
|
||||
return true;
|
||||
};
|
||||
|
||||
// High level grammar validation.
|
||||
for (Stmt *Child : LoopSeqStmt->children()) {
|
||||
if (!Child)
|
||||
continue;
|
||||
// Skip over non-loop-sequence statements.
|
||||
if (!LoopSequenceAnalysis::isLoopSequenceDerivation(Child)) {
|
||||
Child = Child->IgnoreContainers();
|
||||
// Ignore empty compound statement.
|
||||
if (!Child)
|
||||
continue;
|
||||
// In the case of a nested loop sequence ignoring containers would not
|
||||
// be enough, a recurisve transversal of the loop sequence is required.
|
||||
if (isa<CompoundStmt>(Child)) {
|
||||
if (!analyzeLoopSequence(Child, SeqAnalysis, Context, Kind))
|
||||
return false;
|
||||
// Already been treated, skip this children
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// Regular loop sequence handling.
|
||||
if (LoopSequenceAnalysis::isLoopSequenceDerivation(Child)) {
|
||||
if (LoopAnalysis::isLoopTransformation(Child)) {
|
||||
if (!AnalyzeLoopGeneration(Child))
|
||||
return false;
|
||||
// AnalyzeLoopGeneration updates SeqAnalysis.LoopSeqSize accordingly.
|
||||
} else {
|
||||
if (!AnalyzeRegularLoop(Child))
|
||||
return false;
|
||||
SeqAnalysis.LoopSeqSize++;
|
||||
}
|
||||
} else {
|
||||
// Report error for invalid statement inside canonical loop sequence.
|
||||
Diag(Child->getBeginLoc(), diag::err_omp_not_for)
|
||||
<< 0 << getOpenMPDirectiveName(Kind);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SemaOpenMP::checkTransformableLoopSequence(
|
||||
OpenMPDirectiveKind Kind, Stmt *AStmt, LoopSequenceAnalysis &SeqAnalysis,
|
||||
ASTContext &Context) {
|
||||
// Following OpenMP 6.0 API Specification, a Canonical Loop Sequence follows
|
||||
// the grammar:
|
||||
//
|
||||
// canonical-loop-sequence:
|
||||
// {
|
||||
// loop-sequence+
|
||||
// }
|
||||
// where loop-sequence can be any of the following:
|
||||
// 1. canonical-loop-sequence
|
||||
// 2. loop-nest
|
||||
// 3. loop-sequence-generating-construct (i.e OMPLoopTransformationDirective)
|
||||
//
|
||||
// To recognise and traverse this structure the helper function
|
||||
// analyzeLoopSequence serves as the recurisve entry point
|
||||
// and tries to match the input AST to the canonical loop sequence grammar
|
||||
// structure. This function will perform both a semantic and syntactical
|
||||
// analysis of the given statement according to OpenMP 6.0 definition of
|
||||
// the aforementioned canonical loop sequence.
|
||||
|
||||
// We expect an outer compound statement.
|
||||
if (!isa<CompoundStmt>(AStmt)) {
|
||||
Diag(AStmt->getBeginLoc(), diag::err_omp_not_a_loop_sequence)
|
||||
<< getOpenMPDirectiveName(Kind);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Recursive entry point to process the main loop sequence
|
||||
if (!analyzeLoopSequence(AStmt, SeqAnalysis, Context, Kind))
|
||||
return false;
|
||||
|
||||
// Diagnose an empty loop sequence.
|
||||
if (!SeqAnalysis.LoopSeqSize) {
|
||||
Diag(AStmt->getBeginLoc(), diag::err_omp_empty_loop_sequence)
|
||||
<< getOpenMPDirectiveName(Kind);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Add preinit statements that need to be propagated from the selected loop.
|
||||
static void addLoopPreInits(ASTContext &Context,
|
||||
OMPLoopBasedDirective::HelperExprs &LoopHelper,
|
||||
Stmt *LoopStmt, ArrayRef<Stmt *> OriginalInit,
|
||||
@ -14540,7 +14811,7 @@ StmtResult SemaOpenMP::ActOnOpenMPTileDirective(ArrayRef<OMPClause *> Clauses,
|
||||
// Verify and diagnose loop nest.
|
||||
SmallVector<OMPLoopBasedDirective::HelperExprs, 4> LoopHelpers(NumLoops);
|
||||
Stmt *Body = nullptr;
|
||||
SmallVector<SmallVector<Stmt *, 0>, 4> OriginalInits;
|
||||
SmallVector<SmallVector<Stmt *>, 4> OriginalInits;
|
||||
if (!checkTransformableLoopNest(OMPD_tile, AStmt, NumLoops, LoopHelpers, Body,
|
||||
OriginalInits))
|
||||
return StmtError();
|
||||
@ -14817,7 +15088,7 @@ StmtResult SemaOpenMP::ActOnOpenMPStripeDirective(ArrayRef<OMPClause *> Clauses,
|
||||
// Verify and diagnose loop nest.
|
||||
SmallVector<OMPLoopBasedDirective::HelperExprs, 4> LoopHelpers(NumLoops);
|
||||
Stmt *Body = nullptr;
|
||||
SmallVector<SmallVector<Stmt *, 0>, 4> OriginalInits;
|
||||
SmallVector<SmallVector<Stmt *>, 4> OriginalInits;
|
||||
if (!checkTransformableLoopNest(OMPD_stripe, AStmt, NumLoops, LoopHelpers,
|
||||
Body, OriginalInits))
|
||||
return StmtError();
|
||||
@ -15078,7 +15349,7 @@ StmtResult SemaOpenMP::ActOnOpenMPUnrollDirective(ArrayRef<OMPClause *> Clauses,
|
||||
Stmt *Body = nullptr;
|
||||
SmallVector<OMPLoopBasedDirective::HelperExprs, NumLoops> LoopHelpers(
|
||||
NumLoops);
|
||||
SmallVector<SmallVector<Stmt *, 0>, NumLoops + 1> OriginalInits;
|
||||
SmallVector<SmallVector<Stmt *>, NumLoops + 1> OriginalInits;
|
||||
if (!checkTransformableLoopNest(OMPD_unroll, AStmt, NumLoops, LoopHelpers,
|
||||
Body, OriginalInits))
|
||||
return StmtError();
|
||||
@ -15348,7 +15619,7 @@ StmtResult SemaOpenMP::ActOnOpenMPReverseDirective(Stmt *AStmt,
|
||||
Stmt *Body = nullptr;
|
||||
SmallVector<OMPLoopBasedDirective::HelperExprs, NumLoops> LoopHelpers(
|
||||
NumLoops);
|
||||
SmallVector<SmallVector<Stmt *, 0>, NumLoops + 1> OriginalInits;
|
||||
SmallVector<SmallVector<Stmt *>, NumLoops + 1> OriginalInits;
|
||||
if (!checkTransformableLoopNest(OMPD_reverse, AStmt, NumLoops, LoopHelpers,
|
||||
Body, OriginalInits))
|
||||
return StmtError();
|
||||
@ -15540,7 +15811,7 @@ StmtResult SemaOpenMP::ActOnOpenMPInterchangeDirective(
|
||||
// Verify and diagnose loop nest.
|
||||
SmallVector<OMPLoopBasedDirective::HelperExprs, 4> LoopHelpers(NumLoops);
|
||||
Stmt *Body = nullptr;
|
||||
SmallVector<SmallVector<Stmt *, 0>, 2> OriginalInits;
|
||||
SmallVector<SmallVector<Stmt *>, 2> OriginalInits;
|
||||
if (!checkTransformableLoopNest(OMPD_interchange, AStmt, NumLoops,
|
||||
LoopHelpers, Body, OriginalInits))
|
||||
return StmtError();
|
||||
@ -15716,6 +15987,484 @@ StmtResult SemaOpenMP::ActOnOpenMPInterchangeDirective(
|
||||
buildPreInits(Context, PreInits));
|
||||
}
|
||||
|
||||
StmtResult SemaOpenMP::ActOnOpenMPFuseDirective(ArrayRef<OMPClause *> Clauses,
|
||||
Stmt *AStmt,
|
||||
SourceLocation StartLoc,
|
||||
SourceLocation EndLoc) {
|
||||
|
||||
ASTContext &Context = getASTContext();
|
||||
DeclContext *CurrContext = SemaRef.CurContext;
|
||||
Scope *CurScope = SemaRef.getCurScope();
|
||||
CaptureVars CopyTransformer(SemaRef);
|
||||
|
||||
// Ensure the structured block is not empty
|
||||
if (!AStmt)
|
||||
return StmtError();
|
||||
|
||||
// Defer transformation in dependent contexts
|
||||
// The NumLoopNests argument is set to a placeholder 1 (even though
|
||||
// using looprange fuse could yield up to 3 top level loop nests)
|
||||
// because a dependent context could prevent determining its true value
|
||||
if (CurrContext->isDependentContext())
|
||||
return OMPFuseDirective::Create(Context, StartLoc, EndLoc, Clauses,
|
||||
/* NumLoops */ 1, AStmt, nullptr, nullptr);
|
||||
|
||||
// Validate that the potential loop sequence is transformable for fusion
|
||||
// Also collect the HelperExprs, Loop Stmts, Inits, and Number of loops
|
||||
LoopSequenceAnalysis SeqAnalysis;
|
||||
if (!checkTransformableLoopSequence(OMPD_fuse, AStmt, SeqAnalysis, Context))
|
||||
return StmtError();
|
||||
|
||||
// SeqAnalysis.LoopSeqSize exists mostly to handle dependent contexts,
|
||||
// otherwise it must be the same as SeqAnalysis.Loops.size().
|
||||
assert(SeqAnalysis.LoopSeqSize == SeqAnalysis.Loops.size() &&
|
||||
"Inconsistent size of the loop sequence and the number of loops "
|
||||
"found in the sequence");
|
||||
|
||||
// Handle clauses, which can be any of the following: [looprange, apply]
|
||||
const auto *LRC =
|
||||
OMPExecutableDirective::getSingleClause<OMPLoopRangeClause>(Clauses);
|
||||
|
||||
// The clause arguments are invalidated if any error arises
|
||||
// such as non-constant or non-positive arguments
|
||||
if (LRC && (!LRC->getFirst() || !LRC->getCount()))
|
||||
return StmtError();
|
||||
|
||||
// Delayed semantic check of LoopRange constraint
|
||||
// Evaluates the loop range arguments and returns the first and count values
|
||||
auto EvaluateLoopRangeArguments = [&Context](Expr *First, Expr *Count,
|
||||
uint64_t &FirstVal,
|
||||
uint64_t &CountVal) {
|
||||
llvm::APSInt FirstInt = First->EvaluateKnownConstInt(Context);
|
||||
llvm::APSInt CountInt = Count->EvaluateKnownConstInt(Context);
|
||||
FirstVal = FirstInt.getZExtValue();
|
||||
CountVal = CountInt.getZExtValue();
|
||||
};
|
||||
|
||||
// OpenMP [6.0, Restrictions]
|
||||
// first + count - 1 must not evaluate to a value greater than the
|
||||
// loop sequence length of the associated canonical loop sequence.
|
||||
auto ValidLoopRange = [](uint64_t FirstVal, uint64_t CountVal,
|
||||
unsigned NumLoops) -> bool {
|
||||
return FirstVal + CountVal - 1 <= NumLoops;
|
||||
};
|
||||
uint64_t FirstVal = 1, CountVal = 0, LastVal = SeqAnalysis.LoopSeqSize;
|
||||
|
||||
// Validates the loop range after evaluating the semantic information
|
||||
// and ensures that the range is valid for the given loop sequence size.
|
||||
// Expressions are evaluated at compile time to obtain constant values.
|
||||
if (LRC) {
|
||||
EvaluateLoopRangeArguments(LRC->getFirst(), LRC->getCount(), FirstVal,
|
||||
CountVal);
|
||||
if (CountVal == 1)
|
||||
SemaRef.Diag(LRC->getCountLoc(), diag::warn_omp_redundant_fusion)
|
||||
<< getOpenMPDirectiveName(OMPD_fuse);
|
||||
|
||||
if (!ValidLoopRange(FirstVal, CountVal, SeqAnalysis.LoopSeqSize)) {
|
||||
SemaRef.Diag(LRC->getFirstLoc(), diag::err_omp_invalid_looprange)
|
||||
<< getOpenMPDirectiveName(OMPD_fuse) << FirstVal
|
||||
<< (FirstVal + CountVal - 1) << SeqAnalysis.LoopSeqSize;
|
||||
return StmtError();
|
||||
}
|
||||
|
||||
LastVal = FirstVal + CountVal - 1;
|
||||
}
|
||||
|
||||
// Complete fusion generates a single canonical loop nest
|
||||
// However looprange clause may generate several loop nests
|
||||
unsigned NumGeneratedTopLevelLoops =
|
||||
LRC ? SeqAnalysis.LoopSeqSize - CountVal + 1 : 1;
|
||||
|
||||
// Emit a warning for redundant loop fusion when the sequence contains only
|
||||
// one loop.
|
||||
if (SeqAnalysis.LoopSeqSize == 1)
|
||||
SemaRef.Diag(AStmt->getBeginLoc(), diag::warn_omp_redundant_fusion)
|
||||
<< getOpenMPDirectiveName(OMPD_fuse);
|
||||
|
||||
// Select the type with the largest bit width among all induction variables
|
||||
QualType IVType =
|
||||
SeqAnalysis.Loops[FirstVal - 1].HelperExprs.IterationVarRef->getType();
|
||||
for (unsigned I : llvm::seq<unsigned>(FirstVal, LastVal)) {
|
||||
QualType CurrentIVType =
|
||||
SeqAnalysis.Loops[I].HelperExprs.IterationVarRef->getType();
|
||||
if (Context.getTypeSize(CurrentIVType) > Context.getTypeSize(IVType)) {
|
||||
IVType = CurrentIVType;
|
||||
}
|
||||
}
|
||||
uint64_t IVBitWidth = Context.getIntWidth(IVType);
|
||||
|
||||
// Create pre-init declarations for all loops lower bounds, upper bounds,
|
||||
// strides and num-iterations for every top level loop in the fusion
|
||||
SmallVector<VarDecl *, 4> LBVarDecls;
|
||||
SmallVector<VarDecl *, 4> STVarDecls;
|
||||
SmallVector<VarDecl *, 4> NIVarDecls;
|
||||
SmallVector<VarDecl *, 4> UBVarDecls;
|
||||
SmallVector<VarDecl *, 4> IVVarDecls;
|
||||
|
||||
// Helper lambda to create variables for bounds, strides, and other
|
||||
// expressions. Generates both the variable declaration and the corresponding
|
||||
// initialization statement.
|
||||
auto CreateHelperVarAndStmt =
|
||||
[&, &SemaRef = SemaRef](Expr *ExprToCopy, const std::string &BaseName,
|
||||
unsigned I, bool NeedsNewVD = false) {
|
||||
Expr *TransformedExpr =
|
||||
AssertSuccess(CopyTransformer.TransformExpr(ExprToCopy));
|
||||
if (!TransformedExpr)
|
||||
return std::pair<VarDecl *, StmtResult>(nullptr, StmtError());
|
||||
|
||||
auto Name = (Twine(".omp.") + BaseName + std::to_string(I)).str();
|
||||
|
||||
VarDecl *VD;
|
||||
if (NeedsNewVD) {
|
||||
VD = buildVarDecl(SemaRef, SourceLocation(), IVType, Name);
|
||||
SemaRef.AddInitializerToDecl(VD, TransformedExpr, false);
|
||||
} else {
|
||||
// Create a unique variable name
|
||||
DeclRefExpr *DRE = cast<DeclRefExpr>(TransformedExpr);
|
||||
VD = cast<VarDecl>(DRE->getDecl());
|
||||
VD->setDeclName(&SemaRef.PP.getIdentifierTable().get(Name));
|
||||
}
|
||||
// Create the corresponding declaration statement
|
||||
StmtResult DeclStmt = new (Context) class DeclStmt(
|
||||
DeclGroupRef(VD), SourceLocation(), SourceLocation());
|
||||
return std::make_pair(VD, DeclStmt);
|
||||
};
|
||||
|
||||
// PreInits hold a sequence of variable declarations that must be executed
|
||||
// before the fused loop begins. These include bounds, strides, and other
|
||||
// helper variables required for the transformation. Other loop transforms
|
||||
// also contain their own preinits
|
||||
SmallVector<Stmt *> PreInits;
|
||||
|
||||
// Update the general preinits using the preinits generated by loop sequence
|
||||
// generating loop transformations. These preinits differ slightly from
|
||||
// single-loop transformation preinits, as they can be detached from a
|
||||
// specific loop inside multiple generated loop nests. This happens
|
||||
// because certain helper variables, like '.omp.fuse.max', are introduced to
|
||||
// handle fused iteration spaces and may not be directly tied to a single
|
||||
// original loop. The preinit structure must ensure that hidden variables
|
||||
// like '.omp.fuse.max' are still properly handled.
|
||||
// Transformations that apply this concept: Loopranged Fuse, Split
|
||||
llvm::append_range(PreInits, SeqAnalysis.LoopSequencePreInits);
|
||||
|
||||
// Process each single loop to generate and collect declarations
|
||||
// and statements for all helper expressions related to
|
||||
// particular single loop nests
|
||||
|
||||
// Also In the case of the fused loops, we keep track of their original
|
||||
// inits by appending them to their preinits statement, and in the case of
|
||||
// transformations, also append their preinits (which contain the original
|
||||
// loop initialization statement or other statements)
|
||||
|
||||
// Firstly we need to set TransformIndex to match the begining of the
|
||||
// looprange section
|
||||
unsigned int TransformIndex = 0;
|
||||
for (unsigned I : llvm::seq<unsigned>(FirstVal - 1)) {
|
||||
if (SeqAnalysis.Loops[I].isLoopTransformation())
|
||||
++TransformIndex;
|
||||
}
|
||||
|
||||
for (unsigned int I = FirstVal - 1, J = 0; I < LastVal; ++I, ++J) {
|
||||
if (SeqAnalysis.Loops[I].isRegularLoop()) {
|
||||
addLoopPreInits(Context, SeqAnalysis.Loops[I].HelperExprs,
|
||||
SeqAnalysis.Loops[I].TheForStmt,
|
||||
SeqAnalysis.Loops[I].OriginalInits, PreInits);
|
||||
} else if (SeqAnalysis.Loops[I].isLoopTransformation()) {
|
||||
// For transformed loops, insert both pre-inits and original inits.
|
||||
// Order matters: pre-inits may define variables used in the original
|
||||
// inits such as upper bounds...
|
||||
SmallVector<Stmt *> &TransformPreInit =
|
||||
SeqAnalysis.Loops[TransformIndex++].TransformsPreInits;
|
||||
llvm::append_range(PreInits, TransformPreInit);
|
||||
|
||||
addLoopPreInits(Context, SeqAnalysis.Loops[I].HelperExprs,
|
||||
SeqAnalysis.Loops[I].TheForStmt,
|
||||
SeqAnalysis.Loops[I].OriginalInits, PreInits);
|
||||
}
|
||||
auto [UBVD, UBDStmt] =
|
||||
CreateHelperVarAndStmt(SeqAnalysis.Loops[I].HelperExprs.UB, "ub", J);
|
||||
auto [LBVD, LBDStmt] =
|
||||
CreateHelperVarAndStmt(SeqAnalysis.Loops[I].HelperExprs.LB, "lb", J);
|
||||
auto [STVD, STDStmt] =
|
||||
CreateHelperVarAndStmt(SeqAnalysis.Loops[I].HelperExprs.ST, "st", J);
|
||||
auto [NIVD, NIDStmt] = CreateHelperVarAndStmt(
|
||||
SeqAnalysis.Loops[I].HelperExprs.NumIterations, "ni", J, true);
|
||||
auto [IVVD, IVDStmt] = CreateHelperVarAndStmt(
|
||||
SeqAnalysis.Loops[I].HelperExprs.IterationVarRef, "iv", J);
|
||||
|
||||
assert(LBVD && STVD && NIVD && IVVD &&
|
||||
"OpenMP Fuse Helper variables creation failed");
|
||||
|
||||
UBVarDecls.push_back(UBVD);
|
||||
LBVarDecls.push_back(LBVD);
|
||||
STVarDecls.push_back(STVD);
|
||||
NIVarDecls.push_back(NIVD);
|
||||
IVVarDecls.push_back(IVVD);
|
||||
|
||||
PreInits.push_back(LBDStmt.get());
|
||||
PreInits.push_back(STDStmt.get());
|
||||
PreInits.push_back(NIDStmt.get());
|
||||
PreInits.push_back(IVDStmt.get());
|
||||
}
|
||||
|
||||
auto MakeVarDeclRef = [&SemaRef = this->SemaRef](VarDecl *VD) {
|
||||
return buildDeclRefExpr(SemaRef, VD, VD->getType(), VD->getLocation(),
|
||||
false);
|
||||
};
|
||||
|
||||
// Following up the creation of the final fused loop will be performed
|
||||
// which has the following shape (considering the selected loops):
|
||||
//
|
||||
// for (fuse.index = 0; fuse.index < max(ni0, ni1..., nik); ++fuse.index) {
|
||||
// if (fuse.index < ni0){
|
||||
// iv0 = lb0 + st0 * fuse.index;
|
||||
// original.index0 = iv0
|
||||
// body(0);
|
||||
// }
|
||||
// if (fuse.index < ni1){
|
||||
// iv1 = lb1 + st1 * fuse.index;
|
||||
// original.index1 = iv1
|
||||
// body(1);
|
||||
// }
|
||||
//
|
||||
// ...
|
||||
//
|
||||
// if (fuse.index < nik){
|
||||
// ivk = lbk + stk * fuse.index;
|
||||
// original.indexk = ivk
|
||||
// body(k); Expr *InitVal = IntegerLiteral::Create(Context,
|
||||
// llvm::APInt(IVWidth, 0),
|
||||
// }
|
||||
|
||||
// 1. Create the initialized fuse index
|
||||
StringRef IndexName = ".omp.fuse.index";
|
||||
Expr *InitVal = IntegerLiteral::Create(Context, llvm::APInt(IVBitWidth, 0),
|
||||
IVType, SourceLocation());
|
||||
VarDecl *IndexDecl =
|
||||
buildVarDecl(SemaRef, {}, IVType, IndexName, nullptr, nullptr);
|
||||
SemaRef.AddInitializerToDecl(IndexDecl, InitVal, false);
|
||||
StmtResult InitStmt = new (Context)
|
||||
DeclStmt(DeclGroupRef(IndexDecl), SourceLocation(), SourceLocation());
|
||||
|
||||
if (!InitStmt.isUsable())
|
||||
return StmtError();
|
||||
|
||||
auto MakeIVRef = [&SemaRef = this->SemaRef, IndexDecl, IVType,
|
||||
Loc = InitVal->getExprLoc()]() {
|
||||
return buildDeclRefExpr(SemaRef, IndexDecl, IVType, Loc, false);
|
||||
};
|
||||
|
||||
// 2. Iteratively compute the max number of logical iterations Max(NI_1, NI_2,
|
||||
// ..., NI_k)
|
||||
//
|
||||
// This loop accumulates the maximum value across multiple expressions,
|
||||
// ensuring each step constructs a unique AST node for correctness. By using
|
||||
// intermediate temporary variables and conditional operators, we maintain
|
||||
// distinct nodes and avoid duplicating subtrees, For instance, max(a,b,c):
|
||||
// omp.temp0 = max(a, b)
|
||||
// omp.temp1 = max(omp.temp0, c)
|
||||
// omp.fuse.max = max(omp.temp1, omp.temp0)
|
||||
|
||||
ExprResult MaxExpr;
|
||||
// I is the range of loops in the sequence that we fuse.
|
||||
for (unsigned I = FirstVal - 1, J = 0; I < LastVal; ++I, ++J) {
|
||||
DeclRefExpr *NIRef = MakeVarDeclRef(NIVarDecls[J]);
|
||||
QualType NITy = NIRef->getType();
|
||||
|
||||
if (MaxExpr.isUnset()) {
|
||||
// Initialize MaxExpr with the first NI expression
|
||||
MaxExpr = NIRef;
|
||||
} else {
|
||||
// Create a new acummulator variable t_i = MaxExpr
|
||||
std::string TempName = (Twine(".omp.temp.") + Twine(J)).str();
|
||||
VarDecl *TempDecl =
|
||||
buildVarDecl(SemaRef, {}, NITy, TempName, nullptr, nullptr);
|
||||
TempDecl->setInit(MaxExpr.get());
|
||||
DeclRefExpr *TempRef =
|
||||
buildDeclRefExpr(SemaRef, TempDecl, NITy, SourceLocation(), false);
|
||||
DeclRefExpr *TempRef2 =
|
||||
buildDeclRefExpr(SemaRef, TempDecl, NITy, SourceLocation(), false);
|
||||
// Add a DeclStmt to PreInits to ensure the variable is declared.
|
||||
StmtResult TempStmt = new (Context)
|
||||
DeclStmt(DeclGroupRef(TempDecl), SourceLocation(), SourceLocation());
|
||||
|
||||
if (!TempStmt.isUsable())
|
||||
return StmtError();
|
||||
PreInits.push_back(TempStmt.get());
|
||||
|
||||
// Build MaxExpr <-(MaxExpr > NIRef ? MaxExpr : NIRef)
|
||||
ExprResult Comparison =
|
||||
SemaRef.BuildBinOp(nullptr, SourceLocation(), BO_GT, TempRef, NIRef);
|
||||
// Handle any errors in Comparison creation
|
||||
if (!Comparison.isUsable())
|
||||
return StmtError();
|
||||
|
||||
DeclRefExpr *NIRef2 = MakeVarDeclRef(NIVarDecls[J]);
|
||||
// Update MaxExpr using a conditional expression to hold the max value
|
||||
MaxExpr = new (Context) ConditionalOperator(
|
||||
Comparison.get(), SourceLocation(), TempRef2, SourceLocation(),
|
||||
NIRef2->getExprStmt(), NITy, VK_LValue, OK_Ordinary);
|
||||
|
||||
if (!MaxExpr.isUsable())
|
||||
return StmtError();
|
||||
}
|
||||
}
|
||||
if (!MaxExpr.isUsable())
|
||||
return StmtError();
|
||||
|
||||
// 3. Declare the max variable
|
||||
const std::string MaxName = Twine(".omp.fuse.max").str();
|
||||
VarDecl *MaxDecl =
|
||||
buildVarDecl(SemaRef, {}, IVType, MaxName, nullptr, nullptr);
|
||||
MaxDecl->setInit(MaxExpr.get());
|
||||
DeclRefExpr *MaxRef = buildDeclRefExpr(SemaRef, MaxDecl, IVType, {}, false);
|
||||
StmtResult MaxStmt = new (Context)
|
||||
DeclStmt(DeclGroupRef(MaxDecl), SourceLocation(), SourceLocation());
|
||||
|
||||
if (MaxStmt.isInvalid())
|
||||
return StmtError();
|
||||
PreInits.push_back(MaxStmt.get());
|
||||
|
||||
// 4. Create condition Expr: index < n_max
|
||||
ExprResult CondExpr = SemaRef.BuildBinOp(CurScope, SourceLocation(), BO_LT,
|
||||
MakeIVRef(), MaxRef);
|
||||
if (!CondExpr.isUsable())
|
||||
return StmtError();
|
||||
|
||||
// 5. Increment Expr: ++index
|
||||
ExprResult IncrExpr =
|
||||
SemaRef.BuildUnaryOp(CurScope, SourceLocation(), UO_PreInc, MakeIVRef());
|
||||
if (!IncrExpr.isUsable())
|
||||
return StmtError();
|
||||
|
||||
// 6. Build the Fused Loop Body
|
||||
// The final fused loop iterates over the maximum logical range. Inside the
|
||||
// loop, each original loop's index is calculated dynamically, and its body
|
||||
// is executed conditionally.
|
||||
//
|
||||
// Each sub-loop's body is guarded by a conditional statement to ensure
|
||||
// it executes only within its logical iteration range:
|
||||
//
|
||||
// if (fuse.index < ni_k){
|
||||
// iv_k = lb_k + st_k * fuse.index;
|
||||
// original.index = iv_k
|
||||
// body(k);
|
||||
// }
|
||||
|
||||
CompoundStmt *FusedBody = nullptr;
|
||||
SmallVector<Stmt *, 4> FusedBodyStmts;
|
||||
for (unsigned I = FirstVal - 1, J = 0; I < LastVal; ++I, ++J) {
|
||||
// Assingment of the original sub-loop index to compute the logical index
|
||||
// IV_k = LB_k + omp.fuse.index * ST_k
|
||||
ExprResult IdxExpr =
|
||||
SemaRef.BuildBinOp(CurScope, SourceLocation(), BO_Mul,
|
||||
MakeVarDeclRef(STVarDecls[J]), MakeIVRef());
|
||||
if (!IdxExpr.isUsable())
|
||||
return StmtError();
|
||||
IdxExpr = SemaRef.BuildBinOp(CurScope, SourceLocation(), BO_Add,
|
||||
MakeVarDeclRef(LBVarDecls[J]), IdxExpr.get());
|
||||
|
||||
if (!IdxExpr.isUsable())
|
||||
return StmtError();
|
||||
IdxExpr = SemaRef.BuildBinOp(CurScope, SourceLocation(), BO_Assign,
|
||||
MakeVarDeclRef(IVVarDecls[J]), IdxExpr.get());
|
||||
if (!IdxExpr.isUsable())
|
||||
return StmtError();
|
||||
|
||||
// Update the original i_k = IV_k
|
||||
SmallVector<Stmt *, 4> BodyStmts;
|
||||
BodyStmts.push_back(IdxExpr.get());
|
||||
llvm::append_range(BodyStmts, SeqAnalysis.Loops[I].HelperExprs.Updates);
|
||||
|
||||
// If the loop is a CXXForRangeStmt then the iterator variable is needed
|
||||
if (auto *SourceCXXFor =
|
||||
dyn_cast<CXXForRangeStmt>(SeqAnalysis.Loops[I].TheForStmt))
|
||||
BodyStmts.push_back(SourceCXXFor->getLoopVarStmt());
|
||||
|
||||
Stmt *Body =
|
||||
(isa<ForStmt>(SeqAnalysis.Loops[I].TheForStmt))
|
||||
? cast<ForStmt>(SeqAnalysis.Loops[I].TheForStmt)->getBody()
|
||||
: cast<CXXForRangeStmt>(SeqAnalysis.Loops[I].TheForStmt)->getBody();
|
||||
BodyStmts.push_back(Body);
|
||||
|
||||
CompoundStmt *CombinedBody =
|
||||
CompoundStmt::Create(Context, BodyStmts, FPOptionsOverride(),
|
||||
SourceLocation(), SourceLocation());
|
||||
ExprResult Condition =
|
||||
SemaRef.BuildBinOp(CurScope, SourceLocation(), BO_LT, MakeIVRef(),
|
||||
MakeVarDeclRef(NIVarDecls[J]));
|
||||
|
||||
if (!Condition.isUsable())
|
||||
return StmtError();
|
||||
|
||||
IfStmt *IfStatement = IfStmt::Create(
|
||||
Context, SourceLocation(), IfStatementKind::Ordinary, nullptr, nullptr,
|
||||
Condition.get(), SourceLocation(), SourceLocation(), CombinedBody,
|
||||
SourceLocation(), nullptr);
|
||||
|
||||
FusedBodyStmts.push_back(IfStatement);
|
||||
}
|
||||
FusedBody = CompoundStmt::Create(Context, FusedBodyStmts, FPOptionsOverride(),
|
||||
SourceLocation(), SourceLocation());
|
||||
|
||||
// 7. Construct the final fused loop
|
||||
ForStmt *FusedForStmt = new (Context)
|
||||
ForStmt(Context, InitStmt.get(), CondExpr.get(), nullptr, IncrExpr.get(),
|
||||
FusedBody, InitStmt.get()->getBeginLoc(), SourceLocation(),
|
||||
IncrExpr.get()->getEndLoc());
|
||||
|
||||
// In the case of looprange, the result of fuse won't simply
|
||||
// be a single loop (ForStmt), but rather a loop sequence
|
||||
// (CompoundStmt) of 3 parts: the pre-fusion loops, the fused loop
|
||||
// and the post-fusion loops, preserving its original order.
|
||||
//
|
||||
// Note: If looprange clause produces a single fused loop nest then
|
||||
// this compound statement wrapper is unnecessary (Therefore this
|
||||
// treatment is skipped)
|
||||
|
||||
Stmt *FusionStmt = FusedForStmt;
|
||||
if (LRC && CountVal != SeqAnalysis.LoopSeqSize) {
|
||||
SmallVector<Stmt *, 4> FinalLoops;
|
||||
|
||||
// Reset the transform index
|
||||
TransformIndex = 0;
|
||||
|
||||
// Collect all non-fused loops before and after the fused region.
|
||||
// Pre-fusion and post-fusion loops are inserted in order exploiting their
|
||||
// symmetry, along with their corresponding transformation pre-inits if
|
||||
// needed. The fused loop is added between the two regions.
|
||||
for (unsigned I : llvm::seq<unsigned>(SeqAnalysis.LoopSeqSize)) {
|
||||
if (I >= FirstVal - 1 && I < FirstVal + CountVal - 1) {
|
||||
// Update the Transformation counter to skip already treated
|
||||
// loop transformations
|
||||
if (!SeqAnalysis.Loops[I].isLoopTransformation())
|
||||
++TransformIndex;
|
||||
continue;
|
||||
}
|
||||
|
||||
// No need to handle:
|
||||
// Regular loops: they are kept intact as-is.
|
||||
// Loop-sequence-generating transformations: already handled earlier.
|
||||
// Only TransformSingleLoop requires inserting pre-inits here
|
||||
if (SeqAnalysis.Loops[I].isRegularLoop()) {
|
||||
const auto &TransformPreInit =
|
||||
SeqAnalysis.Loops[TransformIndex++].TransformsPreInits;
|
||||
if (!TransformPreInit.empty())
|
||||
llvm::append_range(PreInits, TransformPreInit);
|
||||
}
|
||||
|
||||
FinalLoops.push_back(SeqAnalysis.Loops[I].TheForStmt);
|
||||
}
|
||||
|
||||
FinalLoops.insert(FinalLoops.begin() + (FirstVal - 1), FusedForStmt);
|
||||
FusionStmt = CompoundStmt::Create(Context, FinalLoops, FPOptionsOverride(),
|
||||
SourceLocation(), SourceLocation());
|
||||
}
|
||||
return OMPFuseDirective::Create(Context, StartLoc, EndLoc, Clauses,
|
||||
NumGeneratedTopLevelLoops, AStmt, FusionStmt,
|
||||
buildPreInits(Context, PreInits));
|
||||
}
|
||||
|
||||
OMPClause *SemaOpenMP::ActOnOpenMPSingleExprClause(OpenMPClauseKind Kind,
|
||||
Expr *Expr,
|
||||
SourceLocation StartLoc,
|
||||
@ -16887,6 +17636,31 @@ OMPClause *SemaOpenMP::ActOnOpenMPPartialClause(Expr *FactorExpr,
|
||||
FactorExpr);
|
||||
}
|
||||
|
||||
OMPClause *SemaOpenMP::ActOnOpenMPLoopRangeClause(
|
||||
Expr *First, Expr *Count, SourceLocation StartLoc, SourceLocation LParenLoc,
|
||||
SourceLocation FirstLoc, SourceLocation CountLoc, SourceLocation EndLoc) {
|
||||
|
||||
// OpenMP [6.0, Restrictions]
|
||||
// First and Count must be integer expressions with positive value
|
||||
ExprResult FirstVal =
|
||||
VerifyPositiveIntegerConstantInClause(First, OMPC_looprange);
|
||||
if (FirstVal.isInvalid())
|
||||
First = nullptr;
|
||||
|
||||
ExprResult CountVal =
|
||||
VerifyPositiveIntegerConstantInClause(Count, OMPC_looprange);
|
||||
if (CountVal.isInvalid())
|
||||
Count = nullptr;
|
||||
|
||||
// OpenMP [6.0, Restrictions]
|
||||
// first + count - 1 must not evaluate to a value greater than the
|
||||
// loop sequence length of the associated canonical loop sequence.
|
||||
// This check must be performed afterwards due to the delayed
|
||||
// parsing and computation of the associated loop sequence
|
||||
return OMPLoopRangeClause::Create(getASTContext(), StartLoc, LParenLoc,
|
||||
FirstLoc, CountLoc, EndLoc, First, Count);
|
||||
}
|
||||
|
||||
OMPClause *SemaOpenMP::ActOnOpenMPAlignClause(Expr *A, SourceLocation StartLoc,
|
||||
SourceLocation LParenLoc,
|
||||
SourceLocation EndLoc) {
|
||||
|
||||
@ -1783,6 +1783,14 @@ public:
|
||||
LParenLoc, EndLoc);
|
||||
}
|
||||
|
||||
OMPClause *
|
||||
RebuildOMPLoopRangeClause(Expr *First, Expr *Count, SourceLocation StartLoc,
|
||||
SourceLocation LParenLoc, SourceLocation FirstLoc,
|
||||
SourceLocation CountLoc, SourceLocation EndLoc) {
|
||||
return getSema().OpenMP().ActOnOpenMPLoopRangeClause(
|
||||
First, Count, StartLoc, LParenLoc, FirstLoc, CountLoc, EndLoc);
|
||||
}
|
||||
|
||||
/// Build a new OpenMP 'allocator' clause.
|
||||
///
|
||||
/// By default, performs semantic analysis to build the new OpenMP clause.
|
||||
@ -9607,6 +9615,17 @@ StmtResult TreeTransform<Derived>::TransformOMPInterchangeDirective(
|
||||
return Res;
|
||||
}
|
||||
|
||||
template <typename Derived>
|
||||
StmtResult
|
||||
TreeTransform<Derived>::TransformOMPFuseDirective(OMPFuseDirective *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) {
|
||||
@ -10500,6 +10519,31 @@ TreeTransform<Derived>::TransformOMPPartialClause(OMPPartialClause *C) {
|
||||
C->getEndLoc());
|
||||
}
|
||||
|
||||
template <typename Derived>
|
||||
OMPClause *
|
||||
TreeTransform<Derived>::TransformOMPLoopRangeClause(OMPLoopRangeClause *C) {
|
||||
ExprResult F = getDerived().TransformExpr(C->getFirst());
|
||||
if (F.isInvalid())
|
||||
return nullptr;
|
||||
|
||||
ExprResult Cn = getDerived().TransformExpr(C->getCount());
|
||||
if (Cn.isInvalid())
|
||||
return nullptr;
|
||||
|
||||
Expr *First = F.get();
|
||||
Expr *Count = Cn.get();
|
||||
|
||||
bool Changed = (First != C->getFirst()) || (Count != C->getCount());
|
||||
|
||||
// If no changes and AlwaysRebuild() is false, return the original clause
|
||||
if (!Changed && !getDerived().AlwaysRebuild())
|
||||
return C;
|
||||
|
||||
return RebuildOMPLoopRangeClause(First, Count, C->getBeginLoc(),
|
||||
C->getLParenLoc(), C->getFirstLoc(),
|
||||
C->getCountLoc(), C->getEndLoc());
|
||||
}
|
||||
|
||||
template <typename Derived>
|
||||
OMPClause *
|
||||
TreeTransform<Derived>::TransformOMPCollapseClause(OMPCollapseClause *C) {
|
||||
|
||||
@ -11215,6 +11215,9 @@ OMPClause *OMPClauseReader::readClause() {
|
||||
case llvm::omp::OMPC_partial:
|
||||
C = OMPPartialClause::CreateEmpty(Context);
|
||||
break;
|
||||
case llvm::omp::OMPC_looprange:
|
||||
C = OMPLoopRangeClause::CreateEmpty(Context);
|
||||
break;
|
||||
case llvm::omp::OMPC_allocator:
|
||||
C = new (Context) OMPAllocatorClause();
|
||||
break;
|
||||
@ -11618,6 +11621,14 @@ void OMPClauseReader::VisitOMPPartialClause(OMPPartialClause *C) {
|
||||
C->setLParenLoc(Record.readSourceLocation());
|
||||
}
|
||||
|
||||
void OMPClauseReader::VisitOMPLoopRangeClause(OMPLoopRangeClause *C) {
|
||||
C->setFirst(Record.readSubExpr());
|
||||
C->setCount(Record.readSubExpr());
|
||||
C->setLParenLoc(Record.readSourceLocation());
|
||||
C->setFirstLoc(Record.readSourceLocation());
|
||||
C->setCountLoc(Record.readSourceLocation());
|
||||
}
|
||||
|
||||
void OMPClauseReader::VisitOMPAllocatorClause(OMPAllocatorClause *C) {
|
||||
C->setAllocator(Record.readExpr());
|
||||
C->setLParenLoc(Record.readSourceLocation());
|
||||
|
||||
@ -2469,10 +2469,21 @@ void ASTStmtReader::VisitOMPReverseDirective(OMPReverseDirective *D) {
|
||||
VisitOMPCanonicalLoopNestTransformationDirective(D);
|
||||
}
|
||||
|
||||
void ASTStmtReader::VisitOMPCanonicalLoopSequenceTransformationDirective(
|
||||
OMPCanonicalLoopSequenceTransformationDirective *D) {
|
||||
VisitStmt(D);
|
||||
VisitOMPExecutableDirective(D);
|
||||
D->setNumGeneratedTopLevelLoops(Record.readUInt32());
|
||||
}
|
||||
|
||||
void ASTStmtReader::VisitOMPInterchangeDirective(OMPInterchangeDirective *D) {
|
||||
VisitOMPCanonicalLoopNestTransformationDirective(D);
|
||||
}
|
||||
|
||||
void ASTStmtReader::VisitOMPFuseDirective(OMPFuseDirective *D) {
|
||||
VisitOMPCanonicalLoopSequenceTransformationDirective(D);
|
||||
}
|
||||
|
||||
void ASTStmtReader::VisitOMPForDirective(OMPForDirective *D) {
|
||||
VisitOMPLoopDirective(D);
|
||||
D->setHasCancel(Record.readBool());
|
||||
@ -3615,6 +3626,12 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) {
|
||||
break;
|
||||
}
|
||||
|
||||
case STMT_OMP_FUSE_DIRECTIVE: {
|
||||
unsigned NumClauses = Record[ASTStmtReader::NumStmtFields];
|
||||
S = OMPFuseDirective::CreateEmpty(Context, NumClauses);
|
||||
break;
|
||||
}
|
||||
|
||||
case STMT_OMP_INTERCHANGE_DIRECTIVE: {
|
||||
unsigned NumLoops = Record[ASTStmtReader::NumStmtFields];
|
||||
unsigned NumClauses = Record[ASTStmtReader::NumStmtFields + 1];
|
||||
|
||||
@ -7882,6 +7882,14 @@ void OMPClauseWriter::VisitOMPPartialClause(OMPPartialClause *C) {
|
||||
Record.AddSourceLocation(C->getLParenLoc());
|
||||
}
|
||||
|
||||
void OMPClauseWriter::VisitOMPLoopRangeClause(OMPLoopRangeClause *C) {
|
||||
Record.AddStmt(C->getFirst());
|
||||
Record.AddStmt(C->getCount());
|
||||
Record.AddSourceLocation(C->getLParenLoc());
|
||||
Record.AddSourceLocation(C->getFirstLoc());
|
||||
Record.AddSourceLocation(C->getCountLoc());
|
||||
}
|
||||
|
||||
void OMPClauseWriter::VisitOMPAllocatorClause(OMPAllocatorClause *C) {
|
||||
Record.AddStmt(C->getAllocator());
|
||||
Record.AddSourceLocation(C->getLParenLoc());
|
||||
|
||||
@ -2487,6 +2487,18 @@ void ASTStmtWriter::VisitOMPInterchangeDirective(OMPInterchangeDirective *D) {
|
||||
Code = serialization::STMT_OMP_INTERCHANGE_DIRECTIVE;
|
||||
}
|
||||
|
||||
void ASTStmtWriter::VisitOMPCanonicalLoopSequenceTransformationDirective(
|
||||
OMPCanonicalLoopSequenceTransformationDirective *D) {
|
||||
VisitStmt(D);
|
||||
VisitOMPExecutableDirective(D);
|
||||
Record.writeUInt32(D->getNumGeneratedTopLevelLoops());
|
||||
}
|
||||
|
||||
void ASTStmtWriter::VisitOMPFuseDirective(OMPFuseDirective *D) {
|
||||
VisitOMPCanonicalLoopSequenceTransformationDirective(D);
|
||||
Code = serialization::STMT_OMP_FUSE_DIRECTIVE;
|
||||
}
|
||||
|
||||
void ASTStmtWriter::VisitOMPForDirective(OMPForDirective *D) {
|
||||
VisitOMPLoopDirective(D);
|
||||
Record.writeBool(D->hasCancel());
|
||||
|
||||
@ -1814,6 +1814,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
|
||||
case Stmt::OMPStripeDirectiveClass:
|
||||
case Stmt::OMPTileDirectiveClass:
|
||||
case Stmt::OMPInterchangeDirectiveClass:
|
||||
case Stmt::OMPFuseDirectiveClass:
|
||||
case Stmt::OMPInteropDirectiveClass:
|
||||
case Stmt::OMPDispatchDirectiveClass:
|
||||
case Stmt::OMPMaskedDirectiveClass:
|
||||
|
||||
397
clang/test/OpenMP/fuse_ast_print.cpp
Normal file
397
clang/test/OpenMP/fuse_ast_print.cpp
Normal file
@ -0,0 +1,397 @@
|
||||
// Check no warnings/errors
|
||||
// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -std=c++20 -fopenmp-version=60 -fsyntax-only -verify %s
|
||||
// expected-no-diagnostics
|
||||
|
||||
// Check AST and unparsing
|
||||
// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -std=c++20 -fopenmp-version=60 -ast-dump %s | FileCheck %s --check-prefix=DUMP
|
||||
// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -std=c++20 -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 -std=c++20 -fopenmp-version=60 -emit-pch -o %t %s
|
||||
// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fopenmp -std=c++20 -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 -std=c++20 -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 fuse
|
||||
// DUMP: OMPFuseDirective
|
||||
#pragma omp fuse
|
||||
// PRINT: {
|
||||
// DUMP: CompoundStmt
|
||||
{
|
||||
// PRINT: for (int i = 0; i < 10; i += 2)
|
||||
// DUMP: ForStmt
|
||||
for (int i = 0; i < 10; i += 2)
|
||||
// PRINT: body(i)
|
||||
// DUMP: CallExpr
|
||||
body(i);
|
||||
// PRINT: for (int j = 10; j > 0; --j)
|
||||
// DUMP: ForStmt
|
||||
for (int j = 10; j > 0; --j)
|
||||
// PRINT: body(j)
|
||||
// DUMP: CallExpr
|
||||
body(j);
|
||||
// PRINT: for (int k = 0; k <= 10; ++k)
|
||||
// DUMP: ForStmt
|
||||
for (int k = 0; k <= 10; ++k)
|
||||
// PRINT: body(k)
|
||||
// DUMP: CallExpr
|
||||
body(k);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// PRINT-LABEL: void foo2(
|
||||
// DUMP-LABEL: FunctionDecl {{.*}} foo2
|
||||
void foo2() {
|
||||
// PRINT: #pragma omp unroll partial(4)
|
||||
// DUMP: OMPUnrollDirective
|
||||
// DUMP-NEXT: OMPPartialClause
|
||||
// DUMP-NEXT: ConstantExpr
|
||||
// DUMP-NEXT: value: Int 4
|
||||
// DUMP-NEXT: IntegerLiteral {{.*}} 4
|
||||
#pragma omp unroll partial(4)
|
||||
// PRINT: #pragma omp fuse
|
||||
// DUMP-NEXT: OMPFuseDirective
|
||||
#pragma omp fuse
|
||||
// PRINT: {
|
||||
// DUMP: CompoundStmt
|
||||
{
|
||||
// PRINT: for (int i = 0; i < 10; i += 2)
|
||||
// DUMP: ForStmt
|
||||
for (int i = 0; i < 10; i += 2)
|
||||
// PRINT: body(i)
|
||||
// DUMP: CallExpr
|
||||
body(i);
|
||||
// PRINT: for (int j = 10; j > 0; --j)
|
||||
// DUMP: ForStmt
|
||||
for (int j = 10; j > 0; --j)
|
||||
// PRINT: body(j)
|
||||
// DUMP: CallExpr
|
||||
body(j);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//PRINT-LABEL: void foo3(
|
||||
//DUMP-LABEL: FunctionTemplateDecl {{.*}} foo3
|
||||
template<int Factor1, int Factor2>
|
||||
void foo3() {
|
||||
// PRINT: #pragma omp fuse
|
||||
// DUMP: OMPFuseDirective
|
||||
#pragma omp fuse
|
||||
// PRINT: {
|
||||
// DUMP: CompoundStmt
|
||||
{
|
||||
// PRINT: #pragma omp unroll partial(Factor1)
|
||||
// DUMP: OMPUnrollDirective
|
||||
#pragma omp unroll partial(Factor1)
|
||||
// PRINT: for (int i = 0; i < 12; i += 1)
|
||||
// DUMP: ForStmt
|
||||
for (int i = 0; i < 12; i += 1)
|
||||
// PRINT: body(i)
|
||||
// DUMP: CallExpr
|
||||
body(i);
|
||||
// PRINT: #pragma omp unroll partial(Factor2)
|
||||
// DUMP: OMPUnrollDirective
|
||||
#pragma omp unroll partial(Factor2)
|
||||
// PRINT: for (int k = 0; k <= 10; ++k)
|
||||
// DUMP: ForStmt
|
||||
for (int k = 0; k <= 10; ++k)
|
||||
// PRINT: body(k)
|
||||
// DUMP: CallExpr
|
||||
body(k);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Also test instantiating the template.
|
||||
void tfoo3() {
|
||||
foo3<4,2>();
|
||||
}
|
||||
|
||||
//PRINT-LABEL: void foo4(
|
||||
//DUMP-LABEL: FunctionTemplateDecl {{.*}} foo4
|
||||
template<typename T, T Step>
|
||||
void foo4(int start, int end) {
|
||||
// PRINT: #pragma omp fuse
|
||||
// DUMP: OMPFuseDirective
|
||||
#pragma omp fuse
|
||||
// PRINT: {
|
||||
// DUMP: CompoundStmt
|
||||
{
|
||||
// PRINT: for (T i = start; i < end; i += Step)
|
||||
// DUMP: ForStmt
|
||||
for (T i = start; i < end; i += Step)
|
||||
// PRINT: body(i)
|
||||
// DUMP: CallExpr
|
||||
body(i);
|
||||
|
||||
// PRINT: for (T j = end; j > start; j -= Step)
|
||||
// DUMP: ForStmt
|
||||
for (T j = end; j > start; j -= Step) {
|
||||
// PRINT: body(j)
|
||||
// DUMP: CallExpr
|
||||
body(j);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Also test instantiating the template.
|
||||
void tfoo4() {
|
||||
foo4<int, 4>(0, 64);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// PRINT-LABEL: void foo5(
|
||||
// DUMP-LABEL: FunctionDecl {{.*}} foo5
|
||||
void foo5() {
|
||||
double arr[128], arr2[128];
|
||||
// PRINT: #pragma omp fuse
|
||||
// DUMP: OMPFuseDirective
|
||||
#pragma omp fuse
|
||||
// PRINT: {
|
||||
// DUMP: CompoundStmt
|
||||
{
|
||||
// PRINT-NEXT: for (auto &&a : arr)
|
||||
// DUMP-NEXT: CXXForRangeStmt
|
||||
for (auto &&a: arr)
|
||||
// PRINT: body(a)
|
||||
// DUMP: CallExpr
|
||||
body(a);
|
||||
// PRINT: for (double v = 42; auto &&b : arr)
|
||||
// DUMP: CXXForRangeStmt
|
||||
for (double v = 42; auto &&b: arr)
|
||||
// PRINT: body(b, v);
|
||||
// DUMP: CallExpr
|
||||
body(b, v);
|
||||
// PRINT: for (auto &&c : arr2)
|
||||
// DUMP: CXXForRangeStmt
|
||||
for (auto &&c: arr2)
|
||||
// PRINT: body(c)
|
||||
// DUMP: CallExpr
|
||||
body(c);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// PRINT-LABEL: void foo6(
|
||||
// DUMP-LABEL: FunctionDecl {{.*}} foo6
|
||||
void foo6() {
|
||||
// PRINT: #pragma omp fuse
|
||||
// DUMP: OMPFuseDirective
|
||||
#pragma omp fuse
|
||||
// PRINT: {
|
||||
// DUMP: CompoundStmt
|
||||
{
|
||||
// PRINT: #pragma omp fuse
|
||||
// DUMP: OMPFuseDirective
|
||||
#pragma omp fuse
|
||||
// PRINT: {
|
||||
// DUMP: CompoundStmt
|
||||
{
|
||||
// PRINT: for (int i = 0; i <= 10; ++i)
|
||||
// DUMP: ForStmt
|
||||
for (int i = 0; i <= 10; ++i)
|
||||
body(i);
|
||||
// PRINT: for (int j = 0; j < 100; ++j)
|
||||
// DUMP: ForStmt
|
||||
for(int j = 0; j < 100; ++j)
|
||||
body(j);
|
||||
}
|
||||
// PRINT: #pragma omp unroll partial(4)
|
||||
// DUMP: OMPUnrollDirective
|
||||
#pragma omp unroll partial(4)
|
||||
// PRINT: for (int k = 0; k < 250; ++k)
|
||||
// DUMP: ForStmt
|
||||
for (int k = 0; k < 250; ++k)
|
||||
body(k);
|
||||
}
|
||||
}
|
||||
|
||||
// PRINT-LABEL: void foo7(
|
||||
// DUMP-LABEL: FunctionDecl {{.*}} foo7
|
||||
void foo7() {
|
||||
// PRINT: #pragma omp fuse
|
||||
// DUMP: OMPFuseDirective
|
||||
#pragma omp fuse
|
||||
// PRINT: {
|
||||
// DUMP: CompoundStmt
|
||||
{
|
||||
// PRINT: {
|
||||
// DUMP: CompoundStmt
|
||||
{
|
||||
// PRINT: {
|
||||
// DUMP: CompoundStmt
|
||||
{
|
||||
// PRINT: for (int i = 0; i < 10; i += 2)
|
||||
// DUMP: ForStmt
|
||||
for (int i = 0; i < 10; i += 2)
|
||||
// PRINT: body(i)
|
||||
// DUMP: CallExpr
|
||||
body(i);
|
||||
// PRINT: for (int j = 10; j > 0; --j)
|
||||
// DUMP: ForStmt
|
||||
for (int j = 10; j > 0; --j)
|
||||
// PRINT: body(j)
|
||||
// DUMP: CallExpr
|
||||
body(j);
|
||||
}
|
||||
}
|
||||
// PRINT: {
|
||||
// DUMP: CompoundStmt
|
||||
{
|
||||
// PRINT: {
|
||||
// DUMP: CompoundStmt
|
||||
{
|
||||
// PRINT: {
|
||||
// DUMP: CompoundStmt
|
||||
{
|
||||
// PRINT: for (int k = 0; k <= 10; ++k)
|
||||
// DUMP: ForStmt
|
||||
for (int k = 0; k <= 10; ++k)
|
||||
// PRINT: body(k)
|
||||
// DUMP: CallExpr
|
||||
body(k);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// PRINT-LABEL: void foo8(
|
||||
// DUMP-LABEL: FunctionDecl {{.*}} foo8
|
||||
void foo8() {
|
||||
// PRINT: #pragma omp fuse looprange(2,2)
|
||||
// DUMP: OMPFuseDirective
|
||||
// DUMP: OMPLooprangeClause
|
||||
#pragma omp fuse looprange(2,2)
|
||||
// PRINT: {
|
||||
// DUMP: CompoundStmt
|
||||
{
|
||||
// PRINT: for (int i = 0; i < 10; i += 2)
|
||||
// DUMP: ForStmt
|
||||
for (int i = 0; i < 10; i += 2)
|
||||
// PRINT: body(i)
|
||||
// DUMP: CallExpr
|
||||
body(i);
|
||||
// PRINT: for (int j = 10; j > 0; --j)
|
||||
// DUMP: ForStmt
|
||||
for (int j = 10; j > 0; --j)
|
||||
// PRINT: body(j)
|
||||
// DUMP: CallExpr
|
||||
body(j);
|
||||
// PRINT: for (int k = 0; k <= 10; ++k)
|
||||
// DUMP: ForStmt
|
||||
for (int k = 0; k <= 10; ++k)
|
||||
// PRINT: body(k)
|
||||
// DUMP: CallExpr
|
||||
body(k);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//PRINT-LABEL: void foo9(
|
||||
//DUMP-LABEL: FunctionTemplateDecl {{.*}} foo9
|
||||
//DUMP-LABEL: NonTypeTemplateParmDecl {{.*}} F
|
||||
//DUMP-LABEL: NonTypeTemplateParmDecl {{.*}} C
|
||||
template<int F, int C>
|
||||
void foo9() {
|
||||
// PRINT: #pragma omp fuse looprange(F,C)
|
||||
// DUMP: OMPFuseDirective
|
||||
// DUMP: OMPLooprangeClause
|
||||
#pragma omp fuse looprange(F,C)
|
||||
// PRINT: {
|
||||
// DUMP: CompoundStmt
|
||||
{
|
||||
// PRINT: for (int i = 0; i < 10; i += 2)
|
||||
// DUMP: ForStmt
|
||||
for (int i = 0; i < 10; i += 2)
|
||||
// PRINT: body(i)
|
||||
// DUMP: CallExpr
|
||||
body(i);
|
||||
// PRINT: for (int j = 10; j > 0; --j)
|
||||
// DUMP: ForStmt
|
||||
for (int j = 10; j > 0; --j)
|
||||
// PRINT: body(j)
|
||||
// DUMP: CallExpr
|
||||
body(j);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Also test instantiating the template.
|
||||
void tfoo9() {
|
||||
foo9<1, 2>();
|
||||
}
|
||||
|
||||
// PRINT-LABEL: void foo10(
|
||||
// DUMP-LABEL: FunctionDecl {{.*}} foo10
|
||||
void foo10() {
|
||||
// PRINT: #pragma omp fuse looprange(2,2)
|
||||
// DUMP: OMPFuseDirective
|
||||
// DUMP: OMPLooprangeClause
|
||||
#pragma omp fuse looprange(2,2)
|
||||
// PRINT: {
|
||||
// DUMP: CompoundStmt
|
||||
{
|
||||
// PRINT: for (int i = 0; i < 10; i += 2)
|
||||
// DUMP: ForStmt
|
||||
for (int i = 0; i < 10; i += 2)
|
||||
// PRINT: body(i)
|
||||
// DUMP: CallExpr
|
||||
body(i);
|
||||
// PRINT: for (int ii = 0; ii < 10; ii += 2)
|
||||
// DUMP: ForStmt
|
||||
for (int ii = 0; ii < 10; ii += 2)
|
||||
// PRINT: body(ii)
|
||||
// DUMP: CallExpr
|
||||
body(ii);
|
||||
// PRINT: #pragma omp fuse looprange(2,2)
|
||||
// DUMP: OMPFuseDirective
|
||||
// DUMP: OMPLooprangeClause
|
||||
#pragma omp fuse looprange(2,2)
|
||||
{
|
||||
// PRINT: for (int j = 10; j > 0; --j)
|
||||
// DUMP: ForStmt
|
||||
for (int j = 10; j > 0; --j)
|
||||
// PRINT: body(j)
|
||||
// DUMP: CallExpr
|
||||
body(j);
|
||||
// PRINT: for (int jj = 10; jj > 0; --jj)
|
||||
// DUMP: ForStmt
|
||||
for (int jj = 10; jj > 0; --jj)
|
||||
// PRINT: body(jj)
|
||||
// DUMP: CallExpr
|
||||
body(jj);
|
||||
// PRINT: for (int k = 0; k <= 10; ++k)
|
||||
// DUMP: ForStmt
|
||||
for (int k = 0; k <= 10; ++k)
|
||||
// PRINT: body(k)
|
||||
// DUMP: CallExpr
|
||||
body(k);
|
||||
// PRINT: for (int kk = 0; kk <= 10; ++kk)
|
||||
// DUMP: ForStmt
|
||||
for (int kk = 0; kk <= 10; ++kk)
|
||||
// PRINT: body(kk)
|
||||
// DUMP: CallExpr
|
||||
body(kk);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
2328
clang/test/OpenMP/fuse_codegen.cpp
Normal file
2328
clang/test/OpenMP/fuse_codegen.cpp
Normal file
File diff suppressed because it is too large
Load Diff
209
clang/test/OpenMP/fuse_messages.cpp
Normal file
209
clang/test/OpenMP/fuse_messages.cpp
Normal file
@ -0,0 +1,209 @@
|
||||
// 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 fuse' must be a loop sequence containing canonical loops or loop-generating constructs}}
|
||||
#pragma omp fuse
|
||||
;
|
||||
|
||||
// expected-error@+2 {{statement after '#pragma omp fuse' must be a for loop}}
|
||||
#pragma omp fuse
|
||||
{int bar = 0;}
|
||||
|
||||
// expected-error@+4 {{statement after '#pragma omp fuse' must be a for loop}}
|
||||
#pragma omp fuse
|
||||
{
|
||||
for(int i = 0; i < 10; ++i);
|
||||
int x = 2;
|
||||
}
|
||||
|
||||
// expected-error@+2 {{statement after '#pragma omp fuse' must be a loop sequence containing canonical loops or loop-generating constructs}}
|
||||
#pragma omp fuse
|
||||
#pragma omp for
|
||||
for (int i = 0; i < 7; ++i)
|
||||
;
|
||||
|
||||
{
|
||||
// expected-error@+2 {{expected statement}}
|
||||
#pragma omp fuse
|
||||
}
|
||||
|
||||
// expected-warning@+1 {{extra tokens at the end of '#pragma omp fuse' are ignored}}
|
||||
#pragma omp fuse foo
|
||||
{
|
||||
for (int i = 0; i < 7; ++i)
|
||||
;
|
||||
for(int j = 0; j < 100; ++j);
|
||||
|
||||
}
|
||||
|
||||
|
||||
// expected-error@+1 {{unexpected OpenMP clause 'final' in directive '#pragma omp fuse'}}
|
||||
#pragma omp fuse final(0)
|
||||
{
|
||||
for (int i = 0; i < 7; ++i)
|
||||
;
|
||||
for(int j = 0; j < 100; ++j);
|
||||
|
||||
}
|
||||
|
||||
//expected-error@+3 {{increment clause of OpenMP for loop must perform simple addition or subtraction on loop variable 'i'}}
|
||||
#pragma omp fuse
|
||||
{
|
||||
for(int i = 0; i < 10; i*=2) {
|
||||
;
|
||||
}
|
||||
for(int j = 0; j < 100; ++j);
|
||||
}
|
||||
|
||||
//expected-error@+2 {{loop sequence after '#pragma omp fuse' must contain at least 1 canonical loop or loop-generating construct}}
|
||||
#pragma omp fuse
|
||||
{}
|
||||
|
||||
//expected-error@+3 {{statement after '#pragma omp fuse' must be a for loop}}
|
||||
#pragma omp fuse
|
||||
{
|
||||
#pragma omp unroll full
|
||||
for(int i = 0; i < 10; ++i);
|
||||
|
||||
for(int j = 0; j < 10; ++j);
|
||||
}
|
||||
|
||||
//expected-warning@+2 {{looprange clause selects a single loop, resulting in redundant fusion}}
|
||||
#pragma omp fuse
|
||||
{
|
||||
for(int i = 0; i < 10; ++i);
|
||||
}
|
||||
|
||||
//expected-warning@+1 {{looprange clause selects a single loop, resulting in redundant fusion}}
|
||||
#pragma omp fuse looprange(1, 1)
|
||||
{
|
||||
for(int i = 0; i < 10; ++i);
|
||||
for(int j = 0; j < 100; ++j);
|
||||
}
|
||||
|
||||
//expected-error@+1 {{argument to 'looprange' clause must be a strictly positive integer value}}
|
||||
#pragma omp fuse looprange(1, -1)
|
||||
{
|
||||
for(int i = 0; i < 10; ++i);
|
||||
for(int j = 0; j < 100; ++j);
|
||||
}
|
||||
|
||||
//expected-error@+1 {{argument to 'looprange' clause must be a strictly positive integer value}}
|
||||
#pragma omp fuse looprange(1, 0)
|
||||
{
|
||||
for(int i = 0; i < 10; ++i);
|
||||
for(int j = 0; j < 100; ++j);
|
||||
}
|
||||
|
||||
const int x = 1;
|
||||
constexpr int y = 4;
|
||||
//expected-error@+1 {{looprange clause selects loops from 1 to 4 but this exceeds the number of loops (3) in the loop sequence}}
|
||||
#pragma omp fuse looprange(x,y)
|
||||
{
|
||||
for(int i = 0; i < 10; ++i);
|
||||
for(int j = 0; j < 100; ++j);
|
||||
for(int k = 0; k < 50; ++k);
|
||||
}
|
||||
|
||||
//expected-error@+1 {{looprange clause selects loops from 1 to 420 but this exceeds the number of loops (3) in the loop sequence}}
|
||||
#pragma omp fuse looprange(1,420)
|
||||
{
|
||||
for(int i = 0; i < 10; ++i);
|
||||
for(int j = 0; j < 100; ++j);
|
||||
for(int k = 0; k < 50; ++k);
|
||||
}
|
||||
|
||||
//expected-error@+1 {{looprange clause selects loops from 1 to 6 but this exceeds the number of loops (5) in the loop sequence}}
|
||||
#pragma omp fuse looprange(1,6)
|
||||
{
|
||||
for(int i = 0; i < 10; ++i);
|
||||
for(int j = 0; j < 100; ++j);
|
||||
for(int k = 0; k < 50; ++k);
|
||||
// This fusion results in 2 loops
|
||||
#pragma omp fuse looprange(1,2)
|
||||
{
|
||||
for(int i = 0; i < 10; ++i);
|
||||
for(int j = 0; j < 100; ++j);
|
||||
for(int k = 0; k < 50; ++k);
|
||||
}
|
||||
}
|
||||
|
||||
//expected-error@+1 {{looprange clause selects loops from 2 to 4 but this exceeds the number of loops (3) in the loop sequence}}
|
||||
#pragma omp fuse looprange(2,3)
|
||||
{
|
||||
#pragma omp unroll partial(2)
|
||||
for(int i = 0; i < 10; ++i);
|
||||
|
||||
#pragma omp reverse
|
||||
for(int j = 0; j < 10; ++j);
|
||||
|
||||
#pragma omp fuse
|
||||
{
|
||||
{
|
||||
#pragma omp reverse
|
||||
for(int j = 0; j < 10; ++j);
|
||||
}
|
||||
for(int k = 0; k < 50; ++k);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// In a template context, but expression itself not instantiation-dependent
|
||||
template <typename T>
|
||||
static void templated_func() {
|
||||
|
||||
//expected-warning@+1 {{looprange clause selects a single loop, resulting in redundant fusion}}
|
||||
#pragma omp fuse looprange(2,1)
|
||||
{
|
||||
for(int i = 0; i < 10; ++i);
|
||||
for(int j = 0; j < 100; ++j);
|
||||
for(int k = 0; k < 50; ++k);
|
||||
}
|
||||
|
||||
//expected-error@+1 {{looprange clause selects loops from 3 to 5 but this exceeds the number of loops (3) in the loop sequence}}
|
||||
#pragma omp fuse looprange(3,3)
|
||||
{
|
||||
for(int i = 0; i < 10; ++i);
|
||||
for(int j = 0; j < 100; ++j);
|
||||
for(int k = 0; k < 50; ++k);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template <int V>
|
||||
static void templated_func_value_dependent() {
|
||||
|
||||
//expected-warning@+1 {{looprange clause selects a single loop, resulting in redundant fusion}}
|
||||
#pragma omp fuse looprange(V,1)
|
||||
{
|
||||
for(int i = 0; i < 10; ++i);
|
||||
for(int j = 0; j < 100; ++j);
|
||||
for(int k = 0; k < 50; ++k);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void templated_func_type_dependent() {
|
||||
constexpr T s = 1;
|
||||
|
||||
//expected-error@+1 {{argument to 'looprange' clause must be a strictly positive integer value}}
|
||||
#pragma omp fuse looprange(s,s-1)
|
||||
{
|
||||
for(int i = 0; i < 10; ++i);
|
||||
for(int j = 0; j < 100; ++j);
|
||||
for(int k = 0; k < 50; ++k);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void template_inst() {
|
||||
// expected-note@+1 {{in instantiation of function template specialization 'templated_func<int>' requested here}}
|
||||
templated_func<int>();
|
||||
// expected-note@+1 {{in instantiation of function template specialization 'templated_func_value_dependent<1>' requested here}}
|
||||
templated_func_value_dependent<1>();
|
||||
// expected-note@+1 {{in instantiation of function template specialization 'templated_func_type_dependent<int>' requested here}}
|
||||
templated_func_type_dependent<int>();
|
||||
}
|
||||
|
||||
|
||||
@ -2148,6 +2148,9 @@ public:
|
||||
void VisitOMPUnrollDirective(const OMPUnrollDirective *D);
|
||||
void VisitOMPReverseDirective(const OMPReverseDirective *D);
|
||||
void VisitOMPInterchangeDirective(const OMPInterchangeDirective *D);
|
||||
void VisitOMPCanonicalLoopSequenceTransformationDirective(
|
||||
const OMPCanonicalLoopSequenceTransformationDirective *D);
|
||||
void VisitOMPFuseDirective(const OMPFuseDirective *D);
|
||||
void VisitOMPForDirective(const OMPForDirective *D);
|
||||
void VisitOMPForSimdDirective(const OMPForSimdDirective *D);
|
||||
void VisitOMPSectionsDirective(const OMPSectionsDirective *D);
|
||||
@ -2353,6 +2356,11 @@ void OMPClauseEnqueue::VisitOMPPartialClause(const OMPPartialClause *C) {
|
||||
Visitor->AddStmt(C->getFactor());
|
||||
}
|
||||
|
||||
void OMPClauseEnqueue::VisitOMPLoopRangeClause(const OMPLoopRangeClause *C) {
|
||||
Visitor->AddStmt(C->getFirst());
|
||||
Visitor->AddStmt(C->getCount());
|
||||
}
|
||||
|
||||
void OMPClauseEnqueue::VisitOMPAllocatorClause(const OMPAllocatorClause *C) {
|
||||
Visitor->AddStmt(C->getAllocator());
|
||||
}
|
||||
@ -3317,6 +3325,15 @@ void EnqueueVisitor::VisitOMPInterchangeDirective(
|
||||
VisitOMPCanonicalLoopNestTransformationDirective(D);
|
||||
}
|
||||
|
||||
void EnqueueVisitor::VisitOMPCanonicalLoopSequenceTransformationDirective(
|
||||
const OMPCanonicalLoopSequenceTransformationDirective *D) {
|
||||
VisitOMPExecutableDirective(D);
|
||||
}
|
||||
|
||||
void EnqueueVisitor::VisitOMPFuseDirective(const OMPFuseDirective *D) {
|
||||
VisitOMPCanonicalLoopSequenceTransformationDirective(D);
|
||||
}
|
||||
|
||||
void EnqueueVisitor::VisitOMPForDirective(const OMPForDirective *D) {
|
||||
VisitOMPLoopDirective(D);
|
||||
}
|
||||
@ -6275,6 +6292,8 @@ CXString clang_getCursorKindSpelling(enum CXCursorKind Kind) {
|
||||
return cxstring::createRef("OMPReverseDirective");
|
||||
case CXCursor_OMPInterchangeDirective:
|
||||
return cxstring::createRef("OMPInterchangeDirective");
|
||||
case CXCursor_OMPFuseDirective:
|
||||
return cxstring::createRef("OMPFuseDirective");
|
||||
case CXCursor_OMPForDirective:
|
||||
return cxstring::createRef("OMPForDirective");
|
||||
case CXCursor_OMPForSimdDirective:
|
||||
|
||||
@ -687,6 +687,9 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent,
|
||||
case Stmt::OMPInterchangeDirectiveClass:
|
||||
K = CXCursor_OMPInterchangeDirective;
|
||||
break;
|
||||
case Stmt::OMPFuseDirectiveClass:
|
||||
K = CXCursor_OMPFuseDirective;
|
||||
break;
|
||||
case Stmt::OMPForDirectiveClass:
|
||||
K = CXCursor_OMPForDirective;
|
||||
break;
|
||||
|
||||
@ -243,6 +243,7 @@ using Initializer = tomp::clause::InitializerT<TypeTy, IdTy, ExprTy>;
|
||||
using InReduction = tomp::clause::InReductionT<TypeTy, IdTy, ExprTy>;
|
||||
using IsDevicePtr = tomp::clause::IsDevicePtrT<TypeTy, IdTy, ExprTy>;
|
||||
using Lastprivate = tomp::clause::LastprivateT<TypeTy, IdTy, ExprTy>;
|
||||
using LoopRange = tomp::clause::LoopRangeT<TypeTy, IdTy, ExprTy>;
|
||||
using Linear = tomp::clause::LinearT<TypeTy, IdTy, ExprTy>;
|
||||
using Link = tomp::clause::LinkT<TypeTy, IdTy, ExprTy>;
|
||||
using Map = tomp::clause::MapT<TypeTy, IdTy, ExprTy>;
|
||||
|
||||
@ -613,6 +613,7 @@ public:
|
||||
NODE_ENUM(OmpLinearModifier, Value)
|
||||
NODE(parser, OmpLocator)
|
||||
NODE(parser, OmpLocatorList)
|
||||
NODE(parser, OmpLoopRangeClause)
|
||||
NODE(parser, OmpMapClause)
|
||||
NODE(OmpMapClause, Modifier)
|
||||
NODE(parser, OmpMapper)
|
||||
|
||||
@ -4546,6 +4546,15 @@ struct OmpLinearClause {
|
||||
std::tuple<OmpObjectList, MODIFIERS(), /*PostModified=*/bool> t;
|
||||
};
|
||||
|
||||
// Ref: [6.0:207-208]
|
||||
//
|
||||
// loop-range-clause ->
|
||||
// LOOPRANGE(first, count) // since 6.0
|
||||
struct OmpLoopRangeClause {
|
||||
TUPLE_CLASS_BOILERPLATE(OmpLoopRangeClause);
|
||||
std::tuple<ScalarIntConstantExpr, ScalarIntConstantExpr> t;
|
||||
};
|
||||
|
||||
// Ref: [4.5:216-219], [5.0:315-324], [5.1:347-355], [5.2:150-158]
|
||||
//
|
||||
// map-clause ->
|
||||
|
||||
@ -1036,6 +1036,11 @@ Link make(const parser::OmpClause::Link &inp,
|
||||
return Link{/*List=*/makeObjects(inp.v, semaCtx)};
|
||||
}
|
||||
|
||||
LoopRange make(const parser::OmpClause::Looprange &inp,
|
||||
semantics::SemanticsContext &semaCtx) {
|
||||
llvm_unreachable("Unimplemented: looprange");
|
||||
}
|
||||
|
||||
Map make(const parser::OmpClause::Map &inp,
|
||||
semantics::SemanticsContext &semaCtx) {
|
||||
// inp.v -> parser::OmpMapClause
|
||||
|
||||
@ -1023,6 +1023,9 @@ TYPE_PARSER(
|
||||
maybe(":"_tok >> nonemptyList(Parser<OmpLinearClause::Modifier>{})),
|
||||
/*PostModified=*/pure(true)))
|
||||
|
||||
TYPE_PARSER(construct<OmpLoopRangeClause>(
|
||||
scalarIntConstantExpr, "," >> scalarIntConstantExpr))
|
||||
|
||||
// OpenMPv5.2 12.5.2 detach-clause -> DETACH (event-handle)
|
||||
TYPE_PARSER(construct<OmpDetachClause>(Parser<OmpObject>{}))
|
||||
|
||||
@ -1207,6 +1210,8 @@ TYPE_PARSER( //
|
||||
parenthesized(Parser<OmpLinearClause>{}))) ||
|
||||
"LINK" >> construct<OmpClause>(construct<OmpClause::Link>(
|
||||
parenthesized(Parser<OmpObjectList>{}))) ||
|
||||
"LOOPRANGE" >> construct<OmpClause>(construct<OmpClause::Looprange>(
|
||||
parenthesized(Parser<OmpLoopRangeClause>{}))) ||
|
||||
"MAP" >> construct<OmpClause>(construct<OmpClause::Map>(
|
||||
parenthesized(Parser<OmpMapClause>{}))) ||
|
||||
"MATCH" >> construct<OmpClause>(construct<OmpClause::Match>(
|
||||
|
||||
@ -2345,6 +2345,13 @@ public:
|
||||
}
|
||||
}
|
||||
}
|
||||
void Unparse(const OmpLoopRangeClause &x) {
|
||||
Word("LOOPRANGE(");
|
||||
Walk(std::get<0>(x.t));
|
||||
Put(", ");
|
||||
Walk(std::get<1>(x.t));
|
||||
Put(")");
|
||||
}
|
||||
void Unparse(const OmpReductionClause &x) {
|
||||
using Modifier = OmpReductionClause::Modifier;
|
||||
Walk(std::get<std::optional<std::list<Modifier>>>(x.t), ": ");
|
||||
|
||||
@ -3106,6 +3106,12 @@ CHECK_REQ_CONSTANT_SCALAR_INT_CLAUSE(Collapse, OMPC_collapse)
|
||||
CHECK_REQ_CONSTANT_SCALAR_INT_CLAUSE(Safelen, OMPC_safelen)
|
||||
CHECK_REQ_CONSTANT_SCALAR_INT_CLAUSE(Simdlen, OMPC_simdlen)
|
||||
|
||||
void OmpStructureChecker::Enter(const parser::OmpClause::Looprange &x) {
|
||||
context_.Say(GetContext().clauseSource,
|
||||
"LOOPRANGE clause is not implemented yet"_err_en_US,
|
||||
ContextDirectiveAsFortran());
|
||||
}
|
||||
|
||||
// Restrictions specific to each clause are implemented apart from the
|
||||
// generalized restrictions.
|
||||
|
||||
|
||||
@ -1268,6 +1268,15 @@ struct WriteT {
|
||||
using EmptyTrait = std::true_type;
|
||||
};
|
||||
|
||||
// V6: [6.4.7] Looprange clause
|
||||
template <typename T, typename I, typename E> struct LoopRangeT {
|
||||
using Begin = E;
|
||||
using End = E;
|
||||
|
||||
using TupleTrait = std::true_type;
|
||||
std::tuple<Begin, End> t;
|
||||
};
|
||||
|
||||
// ---
|
||||
|
||||
template <typename T, typename I, typename E>
|
||||
@ -1300,8 +1309,8 @@ using TupleClausesT =
|
||||
DoacrossT<T, I, E>, DynGroupprivateT<T, I, E>, FromT<T, I, E>,
|
||||
GrainsizeT<T, I, E>, IfT<T, I, E>, InitT<T, I, E>,
|
||||
InReductionT<T, I, E>, LastprivateT<T, I, E>, LinearT<T, I, E>,
|
||||
MapT<T, I, E>, NumTasksT<T, I, E>, OrderT<T, I, E>,
|
||||
ReductionT<T, I, E>, ScheduleT<T, I, E>,
|
||||
LoopRangeT<T, I, E>, MapT<T, I, E>, NumTasksT<T, I, E>,
|
||||
OrderT<T, I, E>, ReductionT<T, I, E>, ScheduleT<T, I, E>,
|
||||
TaskReductionT<T, I, E>, ToT<T, I, E>>;
|
||||
|
||||
template <typename T, typename I, typename E>
|
||||
|
||||
@ -284,6 +284,10 @@ def OMPC_Linear : Clause<[Spelling<"linear">]> {
|
||||
def OMPC_Link : Clause<[Spelling<"link">]> {
|
||||
let flangClass = "OmpObjectList";
|
||||
}
|
||||
def OMPC_LoopRange : Clause<[Spelling<"looprange">]> {
|
||||
let clangClass = "OMPLoopRangeClause";
|
||||
let flangClass = "OmpLoopRangeClause";
|
||||
}
|
||||
def OMPC_Map : Clause<[Spelling<"map">]> {
|
||||
let clangClass = "OMPMapClause";
|
||||
let flangClass = "OmpMapClause";
|
||||
@ -902,6 +906,11 @@ def OMP_Groupprivate : Directive<[Spelling<"groupprivate">]> {
|
||||
let category = CA_Declarative;
|
||||
let languages = [L_C, L_Fortran];
|
||||
}
|
||||
def OMP_Fuse : Directive<[Spelling<"fuse">]> {
|
||||
let allowedOnceClauses = [VersionedClause<OMPC_LoopRange, 60>];
|
||||
let association = AS_Block;
|
||||
let category = CA_Executable;
|
||||
}
|
||||
def OMP_Interchange : Directive<[Spelling<"interchange">]> {
|
||||
let allowedOnceClauses = [
|
||||
VersionedClause<OMPC_Permutation>,
|
||||
|
||||
191
openmp/runtime/test/transform/fuse/foreach.cpp
Normal file
191
openmp/runtime/test/transform/fuse/foreach.cpp
Normal file
@ -0,0 +1,191 @@
|
||||
// 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 fuse
|
||||
{
|
||||
for (Reporter a{"C"}; auto &&v : Reporter("A"))
|
||||
printf("v=%d\n", v);
|
||||
for (Reporter aa{"D"}; auto &&vv : Reporter("B"))
|
||||
printf("vv=%d\n", vv);
|
||||
}
|
||||
printf("done\n");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
// CHECK: [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: [A] iterator advance: 0 += 0
|
||||
// CHECK-NEXT: [A] iterator move assign
|
||||
// CHECK-NEXT: [A] iterator deref: 0
|
||||
// CHECK-NEXT: v=0
|
||||
// CHECK-NEXT: [A] iterator dtor
|
||||
// CHECK-NEXT: [B] iterator advance: 0 += 0
|
||||
// CHECK-NEXT: [B] iterator move assign
|
||||
// CHECK-NEXT: [B] iterator deref: 0
|
||||
// CHECK-NEXT: vv=0
|
||||
// CHECK-NEXT: [B] 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
|
||||
// CHECK-NEXT: [A] iterator dtor
|
||||
// CHECK-NEXT: [B] iterator advance: 0 += 1
|
||||
// CHECK-NEXT: [B] iterator move assign
|
||||
// CHECK-NEXT: [B] iterator deref: 1
|
||||
// CHECK-NEXT: vv=1
|
||||
// CHECK-NEXT: [B] 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
|
||||
// CHECK-NEXT: [A] iterator dtor
|
||||
// CHECK-NEXT: [B] iterator advance: 0 += 2
|
||||
// CHECK-NEXT: [B] iterator move assign
|
||||
// CHECK-NEXT: [B] iterator deref: 2
|
||||
// CHECK-NEXT: vv=2
|
||||
// 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
|
||||
|
||||
#endif
|
||||
50
openmp/runtime/test/transform/fuse/intfor.c
Normal file
50
openmp/runtime/test/transform/fuse/intfor.c
Normal file
@ -0,0 +1,50 @@
|
||||
// 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 fuse
|
||||
{
|
||||
for (int i = 5; i <= 25; i += 5)
|
||||
printf("i=%d\n", i);
|
||||
for (int j = 10; j < 100; j += 10)
|
||||
printf("j=%d\n", j);
|
||||
for (int k = 10; k > 0; --k)
|
||||
printf("k=%d\n", k);
|
||||
}
|
||||
printf("done\n");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
#endif /* HEADER */
|
||||
|
||||
// CHECK: do
|
||||
// CHECK-NEXT: i=5
|
||||
// CHECK-NEXT: j=10
|
||||
// CHECK-NEXT: k=10
|
||||
// CHECK-NEXT: i=10
|
||||
// CHECK-NEXT: j=20
|
||||
// CHECK-NEXT: k=9
|
||||
// CHECK-NEXT: i=15
|
||||
// CHECK-NEXT: j=30
|
||||
// CHECK-NEXT: k=8
|
||||
// CHECK-NEXT: i=20
|
||||
// CHECK-NEXT: j=40
|
||||
// CHECK-NEXT: k=7
|
||||
// CHECK-NEXT: i=25
|
||||
// CHECK-NEXT: j=50
|
||||
// CHECK-NEXT: k=6
|
||||
// CHECK-NEXT: j=60
|
||||
// CHECK-NEXT: k=5
|
||||
// CHECK-NEXT: j=70
|
||||
// CHECK-NEXT: k=4
|
||||
// CHECK-NEXT: j=80
|
||||
// CHECK-NEXT: k=3
|
||||
// CHECK-NEXT: j=90
|
||||
// CHECK-NEXT: k=2
|
||||
// CHECK-NEXT: k=1
|
||||
// CHECK-NEXT: done
|
||||
194
openmp/runtime/test/transform/fuse/iterfor.cpp
Normal file
194
openmp/runtime/test/transform/fuse/iterfor.cpp
Normal file
@ -0,0 +1,194 @@
|
||||
// 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 C("C");
|
||||
Reporter D("D");
|
||||
#pragma omp fuse
|
||||
{
|
||||
for (auto it = C.begin(); it != C.end(); ++it)
|
||||
printf("v=%d\n", *it);
|
||||
|
||||
for (auto it = D.begin(); it != D.end(); ++it)
|
||||
printf("vv=%d\n", *it);
|
||||
}
|
||||
printf("done\n");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
#endif /* HEADER */
|
||||
|
||||
// CHECK: do
|
||||
// CHECK: [C] ctor
|
||||
// CHECK-NEXT: [D] ctor
|
||||
// CHECK-NEXT: [C] begin()
|
||||
// CHECK-NEXT: [C] begin()
|
||||
// CHECK-NEXT: [C] end()
|
||||
// CHECK-NEXT: [C] iterator distance: 3
|
||||
// CHECK-NEXT: [D] begin()
|
||||
// CHECK-NEXT: [D] begin()
|
||||
// CHECK-NEXT: [D] end()
|
||||
// CHECK-NEXT: [D] iterator distance: 3
|
||||
// CHECK-NEXT: [C] iterator advance: 0 += 0
|
||||
// CHECK-NEXT: [C] iterator move assign
|
||||
// CHECK-NEXT: [C] iterator deref: 0
|
||||
// CHECK-NEXT: v=0
|
||||
// CHECK-NEXT: [C] iterator dtor
|
||||
// CHECK-NEXT: [D] iterator advance: 0 += 0
|
||||
// CHECK-NEXT: [D] iterator move assign
|
||||
// CHECK-NEXT: [D] iterator deref: 0
|
||||
// CHECK-NEXT: vv=0
|
||||
// CHECK-NEXT: [D] iterator dtor
|
||||
// CHECK-NEXT: [C] iterator advance: 0 += 1
|
||||
// CHECK-NEXT: [C] iterator move assign
|
||||
// CHECK-NEXT: [C] iterator deref: 1
|
||||
// CHECK-NEXT: v=1
|
||||
// CHECK-NEXT: [C] iterator dtor
|
||||
// CHECK-NEXT: [D] iterator advance: 0 += 1
|
||||
// CHECK-NEXT: [D] iterator move assign
|
||||
// CHECK-NEXT: [D] iterator deref: 1
|
||||
// CHECK-NEXT: vv=1
|
||||
// CHECK-NEXT: [D] iterator dtor
|
||||
// CHECK-NEXT: [C] iterator advance: 0 += 2
|
||||
// CHECK-NEXT: [C] iterator move assign
|
||||
// CHECK-NEXT: [C] iterator deref: 2
|
||||
// CHECK-NEXT: v=2
|
||||
// CHECK-NEXT: [C] iterator dtor
|
||||
// CHECK-NEXT: [D] iterator advance: 0 += 2
|
||||
// CHECK-NEXT: [D] iterator move assign
|
||||
// CHECK-NEXT: [D] iterator deref: 2
|
||||
// CHECK-NEXT: vv=2
|
||||
// CHECK-NEXT: [D] iterator dtor
|
||||
// CHECK-NEXT: [D] iterator dtor
|
||||
// CHECK-NEXT: [D] iterator dtor
|
||||
// CHECK-NEXT: [C] iterator dtor
|
||||
// CHECK-NEXT: [C] iterator dtor
|
||||
// CHECK-NEXT: done
|
||||
// CHECK-NEXT: [D] iterator dtor
|
||||
// CHECK-NEXT: [C] iterator dtor
|
||||
// CHECK-NEXT: [D] dtor
|
||||
// CHECK-NEXT: [C] dtor
|
||||
@ -0,0 +1,207 @@
|
||||
// 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(2) num_threads(1)
|
||||
for (int i = 0; i < 3; ++i)
|
||||
#pragma omp fuse
|
||||
{
|
||||
for (Reporter c{"init-stmt"}; auto &&v : Reporter("range"))
|
||||
printf("i=%d v=%d\n", i, v);
|
||||
for (int vv = 0; vv < 3; ++vv)
|
||||
printf("i=%d vv=%d\n", i, vv);
|
||||
}
|
||||
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 += 0
|
||||
// CHECK-NEXT: [range] iterator move assign
|
||||
// CHECK-NEXT: [range] iterator deref: 0
|
||||
// CHECK-NEXT: i=0 v=0
|
||||
// CHECK-NEXT: [range] iterator dtor
|
||||
// CHECK-NEXT: i=0 vv=0
|
||||
// CHECK-NEXT: [range] iterator advance: 0 += 1
|
||||
// CHECK-NEXT: [range] iterator move assign
|
||||
// CHECK-NEXT: [range] iterator deref: 1
|
||||
// CHECK-NEXT: i=0 v=1
|
||||
// CHECK-NEXT: [range] iterator dtor
|
||||
// CHECK-NEXT: i=0 vv=1
|
||||
// CHECK-NEXT: [range] iterator advance: 0 += 2
|
||||
// CHECK-NEXT: [range] iterator move assign
|
||||
// CHECK-NEXT: [range] iterator deref: 2
|
||||
// CHECK-NEXT: i=0 v=2
|
||||
// CHECK-NEXT: [range] iterator dtor
|
||||
// CHECK-NEXT: i=0 vv=2
|
||||
// CHECK-NEXT: [range] iterator advance: 0 += 0
|
||||
// CHECK-NEXT: [range] iterator move assign
|
||||
// CHECK-NEXT: [range] iterator deref: 0
|
||||
// CHECK-NEXT: i=1 v=0
|
||||
// CHECK-NEXT: [range] iterator dtor
|
||||
// CHECK-NEXT: i=1 vv=0
|
||||
// CHECK-NEXT: [range] iterator advance: 0 += 1
|
||||
// CHECK-NEXT: [range] iterator move assign
|
||||
// CHECK-NEXT: [range] iterator deref: 1
|
||||
// CHECK-NEXT: i=1 v=1
|
||||
// CHECK-NEXT: [range] iterator dtor
|
||||
// CHECK-NEXT: i=1 vv=1
|
||||
// CHECK-NEXT: [range] iterator advance: 0 += 2
|
||||
// CHECK-NEXT: [range] iterator move assign
|
||||
// CHECK-NEXT: [range] iterator deref: 2
|
||||
// CHECK-NEXT: i=1 v=2
|
||||
// CHECK-NEXT: [range] iterator dtor
|
||||
// CHECK-NEXT: i=1 vv=2
|
||||
// CHECK-NEXT: [range] iterator advance: 0 += 0
|
||||
// CHECK-NEXT: [range] iterator move assign
|
||||
// CHECK-NEXT: [range] iterator deref: 0
|
||||
// CHECK-NEXT: i=2 v=0
|
||||
// CHECK-NEXT: [range] iterator dtor
|
||||
// CHECK-NEXT: i=2 vv=0
|
||||
// CHECK-NEXT: [range] iterator advance: 0 += 1
|
||||
// CHECK-NEXT: [range] iterator move assign
|
||||
// CHECK-NEXT: [range] iterator deref: 1
|
||||
// CHECK-NEXT: i=2 v=1
|
||||
// CHECK-NEXT: [range] iterator dtor
|
||||
// CHECK-NEXT: i=2 vv=1
|
||||
// CHECK-NEXT: [range] iterator advance: 0 += 2
|
||||
// CHECK-NEXT: [range] iterator move assign
|
||||
// CHECK-NEXT: [range] iterator deref: 2
|
||||
// CHECK-NEXT: i=2 v=2
|
||||
// CHECK-NEXT: [range] iterator dtor
|
||||
// CHECK-NEXT: i=2 vv=2
|
||||
// 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
|
||||
@ -0,0 +1,45 @@
|
||||
// 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(2) num_threads(1)
|
||||
for (int i = 0; i < 3; ++i)
|
||||
#pragma omp fuse
|
||||
{
|
||||
for (int j = 0; j < 3; ++j)
|
||||
printf("i=%d j=%d\n", i, j);
|
||||
for (int k = 0; k < 3; ++k)
|
||||
printf("i=%d k=%d\n", i, k);
|
||||
}
|
||||
printf("done\n");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
#endif /* HEADER */
|
||||
|
||||
// CHECK: do
|
||||
// CHECK-NEXT: i=0 j=0
|
||||
// CHECK-NEXT: i=0 k=0
|
||||
// CHECK-NEXT: i=0 j=1
|
||||
// CHECK-NEXT: i=0 k=1
|
||||
// CHECK-NEXT: i=0 j=2
|
||||
// CHECK-NEXT: i=0 k=2
|
||||
// CHECK-NEXT: i=1 j=0
|
||||
// CHECK-NEXT: i=1 k=0
|
||||
// CHECK-NEXT: i=1 j=1
|
||||
// CHECK-NEXT: i=1 k=1
|
||||
// CHECK-NEXT: i=1 j=2
|
||||
// CHECK-NEXT: i=1 k=2
|
||||
// CHECK-NEXT: i=2 j=0
|
||||
// CHECK-NEXT: i=2 k=0
|
||||
// CHECK-NEXT: i=2 j=1
|
||||
// CHECK-NEXT: i=2 k=1
|
||||
// CHECK-NEXT: i=2 j=2
|
||||
// CHECK-NEXT: i=2 k=2
|
||||
// CHECK-NEXT: done
|
||||
Loading…
x
Reference in New Issue
Block a user