[Flang][OpenMP] Add Semantics support for Nested OpenMPLoopConstructs (#145917)
In OpenMP Version 5.1, the tile and unroll directives were added. When using these directives, it is possible to nest them within other OpenMP Loop Constructs. This patch enables the semantics to allow for this behaviour on these specific directives. Any nested loops will be stored within the initial Loop Construct until reaching the DoConstruct itself. Relevant tests have been added, and previous behaviour has been retained with no changes. See also, #110008
This commit is contained in:
@@ -267,6 +267,7 @@ struct AccEndCombinedDirective;
|
||||
struct OpenACCDeclarativeConstruct;
|
||||
struct OpenACCRoutineConstruct;
|
||||
struct OpenMPConstruct;
|
||||
struct OpenMPLoopConstruct;
|
||||
struct OpenMPDeclarativeConstruct;
|
||||
struct OmpEndLoopDirective;
|
||||
struct OmpMemoryOrderClause;
|
||||
@@ -5021,11 +5022,13 @@ struct OpenMPBlockConstruct {
|
||||
};
|
||||
|
||||
// OpenMP directives enclosing do loop
|
||||
using NestedConstruct =
|
||||
std::variant<DoConstruct, common::Indirection<OpenMPLoopConstruct>>;
|
||||
struct OpenMPLoopConstruct {
|
||||
TUPLE_CLASS_BOILERPLATE(OpenMPLoopConstruct);
|
||||
OpenMPLoopConstruct(OmpBeginLoopDirective &&a)
|
||||
: t({std::move(a), std::nullopt, std::nullopt}) {}
|
||||
std::tuple<OmpBeginLoopDirective, std::optional<DoConstruct>,
|
||||
std::tuple<OmpBeginLoopDirective, std::optional<NestedConstruct>,
|
||||
std::optional<OmpEndLoopDirective>>
|
||||
t;
|
||||
};
|
||||
|
||||
@@ -3769,6 +3769,16 @@ static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
|
||||
mlir::Location currentLocation =
|
||||
converter.genLocation(beginLoopDirective.source);
|
||||
|
||||
auto &optLoopCons =
|
||||
std::get<std::optional<parser::NestedConstruct>>(loopConstruct.t);
|
||||
if (optLoopCons.has_value()) {
|
||||
if (auto *ompNestedLoopCons{
|
||||
std::get_if<common::Indirection<parser::OpenMPLoopConstruct>>(
|
||||
&*optLoopCons)}) {
|
||||
genOMP(converter, symTable, semaCtx, eval, ompNestedLoopCons->value());
|
||||
}
|
||||
}
|
||||
|
||||
llvm::omp::Directive directive =
|
||||
std::get<parser::OmpLoopDirective>(beginLoopDirective.t).v;
|
||||
const parser::CharBlock &source =
|
||||
|
||||
@@ -2926,7 +2926,8 @@ public:
|
||||
Walk(std::get<OmpBeginLoopDirective>(x.t));
|
||||
Put("\n");
|
||||
EndOpenMP();
|
||||
Walk(std::get<std::optional<DoConstruct>>(x.t));
|
||||
Walk(std::get<std::optional<std::variant<DoConstruct,
|
||||
common::Indirection<parser::OpenMPLoopConstruct>>>>(x.t));
|
||||
Walk(std::get<std::optional<OmpEndLoopDirective>>(x.t));
|
||||
}
|
||||
void Unparse(const BasedPointer &x) {
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "canonicalize-omp.h"
|
||||
#include "flang/Parser/parse-tree-visitor.h"
|
||||
#include "flang/Parser/parse-tree.h"
|
||||
|
||||
// After Loop Canonicalization, rewrite OpenMP parse tree to make OpenMP
|
||||
// Constructs more structured which provide explicit scopes for later
|
||||
@@ -125,6 +126,16 @@ private:
|
||||
parser::Block::iterator nextIt;
|
||||
auto &beginDir{std::get<parser::OmpBeginLoopDirective>(x.t)};
|
||||
auto &dir{std::get<parser::OmpLoopDirective>(beginDir.t)};
|
||||
auto missingDoConstruct = [](auto &dir, auto &messages) {
|
||||
messages.Say(dir.source,
|
||||
"A DO loop must follow the %s directive"_err_en_US,
|
||||
parser::ToUpperCaseLetters(dir.source.ToString()));
|
||||
};
|
||||
auto tileUnrollError = [](auto &dir, auto &messages) {
|
||||
messages.Say(dir.source,
|
||||
"If a loop construct has been fully unrolled, it cannot then be tiled"_err_en_US,
|
||||
parser::ToUpperCaseLetters(dir.source.ToString()));
|
||||
};
|
||||
|
||||
nextIt = it;
|
||||
while (++nextIt != block.end()) {
|
||||
@@ -135,31 +146,95 @@ private:
|
||||
if (auto *doCons{GetConstructIf<parser::DoConstruct>(*nextIt)}) {
|
||||
if (doCons->GetLoopControl()) {
|
||||
// move DoConstruct
|
||||
std::get<std::optional<parser::DoConstruct>>(x.t) =
|
||||
std::get<std::optional<std::variant<parser::DoConstruct,
|
||||
common::Indirection<parser::OpenMPLoopConstruct>>>>(x.t) =
|
||||
std::move(*doCons);
|
||||
nextIt = block.erase(nextIt);
|
||||
// try to match OmpEndLoopDirective
|
||||
if (nextIt != block.end()) {
|
||||
if (auto *endDir{
|
||||
GetConstructIf<parser::OmpEndLoopDirective>(*nextIt)}) {
|
||||
std::get<std::optional<parser::OmpEndLoopDirective>>(x.t) =
|
||||
std::move(*endDir);
|
||||
block.erase(nextIt);
|
||||
}
|
||||
if (auto *endDir{
|
||||
GetConstructIf<parser::OmpEndLoopDirective>(*nextIt)}) {
|
||||
std::get<std::optional<parser::OmpEndLoopDirective>>(x.t) =
|
||||
std::move(*endDir);
|
||||
nextIt = block.erase(nextIt);
|
||||
}
|
||||
} else {
|
||||
messages_.Say(dir.source,
|
||||
"DO loop after the %s directive must have loop control"_err_en_US,
|
||||
parser::ToUpperCaseLetters(dir.source.ToString()));
|
||||
}
|
||||
} else if (auto *ompLoopCons{
|
||||
GetOmpIf<parser::OpenMPLoopConstruct>(*nextIt)}) {
|
||||
// We should allow UNROLL and TILE constructs to be inserted between an
|
||||
// OpenMP Loop Construct and the DO loop itself
|
||||
auto &nestedBeginDirective =
|
||||
std::get<parser::OmpBeginLoopDirective>(ompLoopCons->t);
|
||||
auto &nestedBeginLoopDirective =
|
||||
std::get<parser::OmpLoopDirective>(nestedBeginDirective.t);
|
||||
if ((nestedBeginLoopDirective.v == llvm::omp::Directive::OMPD_unroll ||
|
||||
nestedBeginLoopDirective.v ==
|
||||
llvm::omp::Directive::OMPD_tile) &&
|
||||
!(nestedBeginLoopDirective.v == llvm::omp::Directive::OMPD_unroll &&
|
||||
dir.v == llvm::omp::Directive::OMPD_tile)) {
|
||||
// iterate through the remaining block items to find the end directive
|
||||
// for the unroll/tile directive.
|
||||
parser::Block::iterator endIt;
|
||||
endIt = nextIt;
|
||||
while (endIt != block.end()) {
|
||||
if (auto *endDir{
|
||||
GetConstructIf<parser::OmpEndLoopDirective>(*endIt)}) {
|
||||
auto &endLoopDirective =
|
||||
std::get<parser::OmpLoopDirective>(endDir->t);
|
||||
if (endLoopDirective.v == dir.v) {
|
||||
std::get<std::optional<parser::OmpEndLoopDirective>>(x.t) =
|
||||
std::move(*endDir);
|
||||
endIt = block.erase(endIt);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
++endIt;
|
||||
}
|
||||
RewriteOpenMPLoopConstruct(*ompLoopCons, block, nextIt);
|
||||
auto &ompLoop = std::get<std::optional<parser::NestedConstruct>>(x.t);
|
||||
ompLoop =
|
||||
std::optional<parser::NestedConstruct>{parser::NestedConstruct{
|
||||
common::Indirection{std::move(*ompLoopCons)}}};
|
||||
nextIt = block.erase(nextIt);
|
||||
} else if (nestedBeginLoopDirective.v ==
|
||||
llvm::omp::Directive::OMPD_unroll &&
|
||||
dir.v == llvm::omp::Directive::OMPD_tile) {
|
||||
// if a loop has been unrolled, the user can not then tile that loop
|
||||
// as it has been unrolled
|
||||
parser::OmpClauseList &unrollClauseList{
|
||||
std::get<parser::OmpClauseList>(nestedBeginDirective.t)};
|
||||
if (unrollClauseList.v.empty()) {
|
||||
// if the clause list is empty for an unroll construct, we assume
|
||||
// the loop is being fully unrolled
|
||||
tileUnrollError(dir, messages_);
|
||||
} else {
|
||||
// parse the clauses for the unroll directive to find the full
|
||||
// clause
|
||||
for (auto clause{unrollClauseList.v.begin()};
|
||||
clause != unrollClauseList.v.end(); ++clause) {
|
||||
if (clause->Id() == llvm::omp::OMPC_full) {
|
||||
tileUnrollError(dir, messages_);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
messages_.Say(nestedBeginLoopDirective.source,
|
||||
"Only Loop Transformation Constructs or Loop Nests can be nested within Loop Constructs"_err_en_US,
|
||||
parser::ToUpperCaseLetters(
|
||||
nestedBeginLoopDirective.source.ToString()));
|
||||
}
|
||||
} else {
|
||||
messages_.Say(dir.source,
|
||||
"A DO loop must follow the %s directive"_err_en_US,
|
||||
parser::ToUpperCaseLetters(dir.source.ToString()));
|
||||
missingDoConstruct(dir, messages_);
|
||||
}
|
||||
// If we get here, we either found a loop, or issued an error message.
|
||||
return;
|
||||
}
|
||||
if (nextIt == block.end()) {
|
||||
missingDoConstruct(dir, messages_);
|
||||
}
|
||||
}
|
||||
|
||||
void RewriteOmpAllocations(parser::ExecutionPart &body) {
|
||||
|
||||
@@ -762,10 +762,13 @@ void OmpStructureChecker::Enter(const parser::OpenMPLoopConstruct &x) {
|
||||
}
|
||||
SetLoopInfo(x);
|
||||
|
||||
if (const auto &doConstruct{
|
||||
std::get<std::optional<parser::DoConstruct>>(x.t)}) {
|
||||
const auto &doBlock{std::get<parser::Block>(doConstruct->t)};
|
||||
CheckNoBranching(doBlock, beginDir.v, beginDir.source);
|
||||
auto &optLoopCons = std::get<std::optional<parser::NestedConstruct>>(x.t);
|
||||
if (optLoopCons.has_value()) {
|
||||
if (const auto &doConstruct{
|
||||
std::get_if<parser::DoConstruct>(&*optLoopCons)}) {
|
||||
const auto &doBlock{std::get<parser::Block>(doConstruct->t)};
|
||||
CheckNoBranching(doBlock, beginDir.v, beginDir.source);
|
||||
}
|
||||
}
|
||||
CheckLoopItrVariableIsInt(x);
|
||||
CheckAssociatedLoopConstraints(x);
|
||||
@@ -786,12 +789,15 @@ const parser::Name OmpStructureChecker::GetLoopIndex(
|
||||
return std::get<Bounds>(x->GetLoopControl()->u).name.thing;
|
||||
}
|
||||
void OmpStructureChecker::SetLoopInfo(const parser::OpenMPLoopConstruct &x) {
|
||||
if (const auto &loopConstruct{
|
||||
std::get<std::optional<parser::DoConstruct>>(x.t)}) {
|
||||
const parser::DoConstruct *loop{&*loopConstruct};
|
||||
if (loop && loop->IsDoNormal()) {
|
||||
const parser::Name &itrVal{GetLoopIndex(loop)};
|
||||
SetLoopIv(itrVal.symbol);
|
||||
auto &optLoopCons = std::get<std::optional<parser::NestedConstruct>>(x.t);
|
||||
if (optLoopCons.has_value()) {
|
||||
if (const auto &loopConstruct{
|
||||
std::get_if<parser::DoConstruct>(&*optLoopCons)}) {
|
||||
const parser::DoConstruct *loop{&*loopConstruct};
|
||||
if (loop && loop->IsDoNormal()) {
|
||||
const parser::Name &itrVal{GetLoopIndex(loop)};
|
||||
SetLoopIv(itrVal.symbol);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -857,27 +863,30 @@ void OmpStructureChecker::CheckIteratorModifier(const parser::OmpIterator &x) {
|
||||
|
||||
void OmpStructureChecker::CheckLoopItrVariableIsInt(
|
||||
const parser::OpenMPLoopConstruct &x) {
|
||||
if (const auto &loopConstruct{
|
||||
std::get<std::optional<parser::DoConstruct>>(x.t)}) {
|
||||
auto &optLoopCons = std::get<std::optional<parser::NestedConstruct>>(x.t);
|
||||
if (optLoopCons.has_value()) {
|
||||
if (const auto &loopConstruct{
|
||||
std::get_if<parser::DoConstruct>(&*optLoopCons)}) {
|
||||
|
||||
for (const parser::DoConstruct *loop{&*loopConstruct}; loop;) {
|
||||
if (loop->IsDoNormal()) {
|
||||
const parser::Name &itrVal{GetLoopIndex(loop)};
|
||||
if (itrVal.symbol) {
|
||||
const auto *type{itrVal.symbol->GetType()};
|
||||
if (!type->IsNumeric(TypeCategory::Integer)) {
|
||||
context_.Say(itrVal.source,
|
||||
"The DO loop iteration"
|
||||
" variable must be of the type integer."_err_en_US,
|
||||
itrVal.ToString());
|
||||
for (const parser::DoConstruct *loop{&*loopConstruct}; loop;) {
|
||||
if (loop->IsDoNormal()) {
|
||||
const parser::Name &itrVal{GetLoopIndex(loop)};
|
||||
if (itrVal.symbol) {
|
||||
const auto *type{itrVal.symbol->GetType()};
|
||||
if (!type->IsNumeric(TypeCategory::Integer)) {
|
||||
context_.Say(itrVal.source,
|
||||
"The DO loop iteration"
|
||||
" variable must be of the type integer."_err_en_US,
|
||||
itrVal.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
// Get the next DoConstruct if block is not empty.
|
||||
const auto &block{std::get<parser::Block>(loop->t)};
|
||||
const auto it{block.begin()};
|
||||
loop = it != block.end() ? parser::Unwrap<parser::DoConstruct>(*it)
|
||||
: nullptr;
|
||||
}
|
||||
// Get the next DoConstruct if block is not empty.
|
||||
const auto &block{std::get<parser::Block>(loop->t)};
|
||||
const auto it{block.begin()};
|
||||
loop = it != block.end() ? parser::Unwrap<parser::DoConstruct>(*it)
|
||||
: nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1077,25 +1086,28 @@ void OmpStructureChecker::CheckDistLinear(
|
||||
|
||||
// Match the loop index variables with the collected symbols from linear
|
||||
// clauses.
|
||||
if (const auto &loopConstruct{
|
||||
std::get<std::optional<parser::DoConstruct>>(x.t)}) {
|
||||
for (const parser::DoConstruct *loop{&*loopConstruct}; loop;) {
|
||||
if (loop->IsDoNormal()) {
|
||||
const parser::Name &itrVal{GetLoopIndex(loop)};
|
||||
if (itrVal.symbol) {
|
||||
// Remove the symbol from the collected set
|
||||
indexVars.erase(&itrVal.symbol->GetUltimate());
|
||||
}
|
||||
collapseVal--;
|
||||
if (collapseVal == 0) {
|
||||
break;
|
||||
auto &optLoopCons = std::get<std::optional<parser::NestedConstruct>>(x.t);
|
||||
if (optLoopCons.has_value()) {
|
||||
if (const auto &loopConstruct{
|
||||
std::get_if<parser::DoConstruct>(&*optLoopCons)}) {
|
||||
for (const parser::DoConstruct *loop{&*loopConstruct}; loop;) {
|
||||
if (loop->IsDoNormal()) {
|
||||
const parser::Name &itrVal{GetLoopIndex(loop)};
|
||||
if (itrVal.symbol) {
|
||||
// Remove the symbol from the collected set
|
||||
indexVars.erase(&itrVal.symbol->GetUltimate());
|
||||
}
|
||||
collapseVal--;
|
||||
if (collapseVal == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Get the next DoConstruct if block is not empty.
|
||||
const auto &block{std::get<parser::Block>(loop->t)};
|
||||
const auto it{block.begin()};
|
||||
loop = it != block.end() ? parser::Unwrap<parser::DoConstruct>(*it)
|
||||
: nullptr;
|
||||
}
|
||||
// Get the next DoConstruct if block is not empty.
|
||||
const auto &block{std::get<parser::Block>(loop->t)};
|
||||
const auto it{block.begin()};
|
||||
loop = it != block.end() ? parser::Unwrap<parser::DoConstruct>(*it)
|
||||
: nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1796,10 +1796,13 @@ bool OmpAttributeVisitor::Pre(const parser::OpenMPLoopConstruct &x) {
|
||||
SetContextAssociatedLoopLevel(GetAssociatedLoopLevelFromClauses(clauseList));
|
||||
|
||||
if (beginDir.v == llvm::omp::Directive::OMPD_do) {
|
||||
if (const auto &doConstruct{
|
||||
std::get<std::optional<parser::DoConstruct>>(x.t)}) {
|
||||
if (doConstruct.value().IsDoWhile()) {
|
||||
return true;
|
||||
auto &optLoopCons = std::get<std::optional<parser::NestedConstruct>>(x.t);
|
||||
if (optLoopCons.has_value()) {
|
||||
if (const auto &doConstruct{
|
||||
std::get_if<parser::DoConstruct>(&*optLoopCons)}) {
|
||||
if (doConstruct->IsDoWhile()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1962,48 +1965,69 @@ void OmpAttributeVisitor::PrivatizeAssociatedLoopIndexAndCheckLoopLevel(
|
||||
bool hasCollapseClause{
|
||||
clause ? (clause->Id() == llvm::omp::OMPC_collapse) : false};
|
||||
|
||||
const auto &outer{std::get<std::optional<parser::DoConstruct>>(x.t)};
|
||||
if (outer.has_value()) {
|
||||
for (const parser::DoConstruct *loop{&*outer}; loop && level > 0; --level) {
|
||||
if (loop->IsDoConcurrent()) {
|
||||
// DO CONCURRENT is explicitly allowed for the LOOP construct so long as
|
||||
// there isn't a COLLAPSE clause
|
||||
if (isLoopConstruct) {
|
||||
if (hasCollapseClause) {
|
||||
// hasCollapseClause implies clause != nullptr
|
||||
context_.Say(clause->source,
|
||||
"DO CONCURRENT loops cannot be used with the COLLAPSE clause."_err_en_US);
|
||||
auto &optLoopCons = std::get<std::optional<parser::NestedConstruct>>(x.t);
|
||||
if (optLoopCons.has_value()) {
|
||||
if (const auto &outer{std::get_if<parser::DoConstruct>(&*optLoopCons)}) {
|
||||
for (const parser::DoConstruct *loop{&*outer}; loop && level > 0;
|
||||
--level) {
|
||||
if (loop->IsDoConcurrent()) {
|
||||
// DO CONCURRENT is explicitly allowed for the LOOP construct so long
|
||||
// as there isn't a COLLAPSE clause
|
||||
if (isLoopConstruct) {
|
||||
if (hasCollapseClause) {
|
||||
// hasCollapseClause implies clause != nullptr
|
||||
context_.Say(clause->source,
|
||||
"DO CONCURRENT loops cannot be used with the COLLAPSE clause."_err_en_US);
|
||||
}
|
||||
} else {
|
||||
auto &stmt =
|
||||
std::get<parser::Statement<parser::NonLabelDoStmt>>(loop->t);
|
||||
context_.Say(stmt.source,
|
||||
"DO CONCURRENT loops cannot form part of a loop nest."_err_en_US);
|
||||
}
|
||||
} else {
|
||||
auto &stmt =
|
||||
std::get<parser::Statement<parser::NonLabelDoStmt>>(loop->t);
|
||||
context_.Say(stmt.source,
|
||||
"DO CONCURRENT loops cannot form part of a loop nest."_err_en_US);
|
||||
}
|
||||
}
|
||||
// go through all the nested do-loops and resolve index variables
|
||||
const parser::Name *iv{GetLoopIndex(*loop)};
|
||||
if (iv) {
|
||||
if (auto *symbol{ResolveOmp(*iv, ivDSA, currScope())}) {
|
||||
SetSymbolDSA(*symbol, {Symbol::Flag::OmpPreDetermined, ivDSA});
|
||||
iv->symbol = symbol; // adjust the symbol within region
|
||||
AddToContextObjectWithDSA(*symbol, ivDSA);
|
||||
}
|
||||
// go through all the nested do-loops and resolve index variables
|
||||
const parser::Name *iv{GetLoopIndex(*loop)};
|
||||
if (iv) {
|
||||
if (auto *symbol{ResolveOmp(*iv, ivDSA, currScope())}) {
|
||||
SetSymbolDSA(*symbol, {Symbol::Flag::OmpPreDetermined, ivDSA});
|
||||
iv->symbol = symbol; // adjust the symbol within region
|
||||
AddToContextObjectWithDSA(*symbol, ivDSA);
|
||||
}
|
||||
|
||||
const auto &block{std::get<parser::Block>(loop->t)};
|
||||
const auto it{block.begin()};
|
||||
loop = it != block.end() ? GetDoConstructIf(*it) : nullptr;
|
||||
const auto &block{std::get<parser::Block>(loop->t)};
|
||||
const auto it{block.begin()};
|
||||
loop = it != block.end() ? GetDoConstructIf(*it) : nullptr;
|
||||
}
|
||||
}
|
||||
CheckAssocLoopLevel(level, GetAssociatedClause());
|
||||
} else if (const auto &loop{std::get_if<
|
||||
common::Indirection<parser::OpenMPLoopConstruct>>(
|
||||
&*optLoopCons)}) {
|
||||
auto &beginDirective =
|
||||
std::get<parser::OmpBeginLoopDirective>(loop->value().t);
|
||||
auto &beginLoopDirective =
|
||||
std::get<parser::OmpLoopDirective>(beginDirective.t);
|
||||
if (beginLoopDirective.v != llvm::omp::Directive::OMPD_unroll &&
|
||||
beginLoopDirective.v != llvm::omp::Directive::OMPD_tile) {
|
||||
context_.Say(GetContext().directiveSource,
|
||||
"Only UNROLL or TILE constructs are allowed between an OpenMP Loop Construct and a DO construct"_err_en_US,
|
||||
parser::ToUpperCaseLetters(llvm::omp::getOpenMPDirectiveName(
|
||||
GetContext().directive, version)
|
||||
.str()));
|
||||
} else {
|
||||
PrivatizeAssociatedLoopIndexAndCheckLoopLevel(loop->value());
|
||||
}
|
||||
} else {
|
||||
context_.Say(GetContext().directiveSource,
|
||||
"A DO loop must follow the %s directive"_err_en_US,
|
||||
parser::ToUpperCaseLetters(
|
||||
llvm::omp::getOpenMPDirectiveName(GetContext().directive, version)
|
||||
.str()));
|
||||
}
|
||||
CheckAssocLoopLevel(level, GetAssociatedClause());
|
||||
} else {
|
||||
context_.Say(GetContext().directiveSource,
|
||||
"A DO loop must follow the %s directive"_err_en_US,
|
||||
parser::ToUpperCaseLetters(
|
||||
llvm::omp::getOpenMPDirectiveName(GetContext().directive, version)
|
||||
.str()));
|
||||
}
|
||||
}
|
||||
|
||||
void OmpAttributeVisitor::CheckAssocLoopLevel(
|
||||
std::int64_t level, const parser::OmpClause *clause) {
|
||||
if (clause && level != 0) {
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
! Test to ensure TODO message is emitted for tile OpenMP 5.1 Directives when they are nested.
|
||||
|
||||
!RUN: not %flang -fopenmp -fopenmp-version=51 %s 2>&1 | FileCheck %s
|
||||
|
||||
subroutine loop_transformation_construct
|
||||
implicit none
|
||||
integer :: I = 10
|
||||
integer :: x
|
||||
integer :: y(I)
|
||||
|
||||
!$omp do
|
||||
!$omp tile
|
||||
do i = 1, I
|
||||
y(i) = y(i) * 5
|
||||
end do
|
||||
!$omp end tile
|
||||
!$omp end do
|
||||
end subroutine
|
||||
|
||||
!CHECK: not yet implemented: Unhandled loop directive (tile)
|
||||
@@ -0,0 +1,20 @@
|
||||
! Test to ensure TODO message is emitted for unroll OpenMP 5.1 Directives when they are nested.
|
||||
|
||||
!RUN: not %flang -fopenmp -fopenmp-version=51 %s 2>&1 | FileCheck %s
|
||||
|
||||
program loop_transformation_construct
|
||||
implicit none
|
||||
integer, parameter :: I = 10
|
||||
integer :: x
|
||||
integer :: y(I)
|
||||
|
||||
!$omp do
|
||||
!$omp unroll
|
||||
do x = 1, I
|
||||
y(x) = y(x) * 5
|
||||
end do
|
||||
!$omp end unroll
|
||||
!$omp end do
|
||||
end program loop_transformation_construct
|
||||
|
||||
!CHECK: not yet implemented: Unhandled loop directive (unroll)
|
||||
74
flang/test/Parser/OpenMP/loop-transformation-construct01.f90
Normal file
74
flang/test/Parser/OpenMP/loop-transformation-construct01.f90
Normal file
@@ -0,0 +1,74 @@
|
||||
! Test the Parse Tree to ensure the OpenMP Loop Transformation Constructs nest correctly with 1 nested loop.
|
||||
|
||||
! RUN: %flang_fc1 -fdebug-dump-parse-tree -fopenmp -fopenmp-version=51 %s | FileCheck %s --check-prefix=CHECK-PARSE
|
||||
! RUN: %flang_fc1 -fdebug-unparse -fopenmp -fopenmp-version=51 %s | FileCheck %s --check-prefix=CHECK-UNPARSE
|
||||
|
||||
subroutine loop_transformation_construct
|
||||
implicit none
|
||||
integer :: I = 10
|
||||
integer :: x
|
||||
integer :: y(I)
|
||||
|
||||
!$omp do
|
||||
!$omp unroll
|
||||
do i = 1, I
|
||||
y(i) = y(i) * 5
|
||||
end do
|
||||
!$omp end unroll
|
||||
!$omp end do
|
||||
end subroutine
|
||||
|
||||
!CHECK-PARSE: | ExecutionPart -> Block
|
||||
!CHECK-PARSE-NEXT: | | ExecutionPartConstruct -> ExecutableConstruct -> OpenMPConstruct -> OpenMPLoopConstruct
|
||||
!CHECK-PARSE-NEXT: | | | OmpBeginLoopDirective
|
||||
!CHECK-PARSE-NEXT: | | | | OmpLoopDirective -> llvm::omp::Directive = do
|
||||
!CHECK-PARSE-NEXT: | | | | OmpClauseList ->
|
||||
!CHECK-PARSE-NEXT: | | | OpenMPLoopConstruct
|
||||
!CHECK-PARSE-NEXT: | | | | OmpBeginLoopDirective
|
||||
!CHECK-PARSE-NEXT: | | | | | OmpLoopDirective -> llvm::omp::Directive = unroll
|
||||
!CHECK-PARSE-NEXT: | | | | | OmpClauseList ->
|
||||
!CHECK-PARSE-NEXT: | | | | DoConstruct
|
||||
!CHECK-PARSE-NEXT: | | | | | NonLabelDoStmt
|
||||
!CHECK-PARSE-NEXT: | | | | | | LoopControl -> LoopBounds
|
||||
!CHECK-PARSE-NEXT: | | | | | | | Scalar -> Name = 'i'
|
||||
!CHECK-PARSE-NEXT: | | | | | | | Scalar -> Expr = '1_4'
|
||||
!CHECK-PARSE-NEXT: | | | | | | | | LiteralConstant -> IntLiteralConstant = '1'
|
||||
!CHECK-PARSE-NEXT: | | | | | | | Scalar -> Expr = 'i'
|
||||
!CHECK-PARSE-NEXT: | | | | | | | | Designator -> DataRef -> Name = 'i'
|
||||
!CHECK-PARSE-NEXT: | | | | | Block
|
||||
!CHECK-PARSE-NEXT: | | | | | | ExecutionPartConstruct -> ExecutableConstruct -> ActionStmt -> AssignmentStmt = 'y(int(i,kind=8))=5_4*y(int(i,kind=8))'
|
||||
!CHECK-PARSE-NEXT: | | | | | | | Variable = 'y(int(i,kind=8))'
|
||||
!CHECK-PARSE-NEXT: | | | | | | | | Designator -> DataRef -> ArrayElement
|
||||
!CHECK-PARSE-NEXT: | | | | | | | | | DataRef -> Name = 'y'
|
||||
!CHECK-PARSE-NEXT: | | | | | | | | | SectionSubscript -> Integer -> Expr = 'i'
|
||||
!CHECK-PARSE-NEXT: | | | | | | | | | | Designator -> DataRef -> Name = 'i'
|
||||
!CHECK-PARSE-NEXT: | | | | | | | Expr = '5_4*y(int(i,kind=8))'
|
||||
!CHECK-PARSE-NEXT: | | | | | | | | Multiply
|
||||
!CHECK-PARSE-NEXT: | | | | | | | | | Expr = 'y(int(i,kind=8))'
|
||||
!CHECK-PARSE-NEXT: | | | | | | | | | | Designator -> DataRef -> ArrayElement
|
||||
!CHECK-PARSE-NEXT: | | | | | | | | | | | DataRef -> Name = 'y'
|
||||
!CHECK-PARSE-NEXT: | | | | | | | | | | | SectionSubscript -> Integer -> Expr = 'i'
|
||||
!CHECK-PARSE-NEXT: | | | | | | | | | | | | Designator -> DataRef -> Name = 'i'
|
||||
!CHECK-PARSE-NEXT: | | | | | | | | | Expr = '5_4'
|
||||
!CHECK-PARSE-NEXT: | | | | | | | | | | LiteralConstant -> IntLiteralConstant = '5'
|
||||
!CHECK-PARSE-NEXT: | | | | | EndDoStmt ->
|
||||
!CHECK-PARSE-NEXT: | | | | OmpEndLoopDirective
|
||||
!CHECK-PARSE-NEXT: | | | | | OmpLoopDirective -> llvm::omp::Directive = unroll
|
||||
!CHECK-PARSE-NEXT: | | | | | OmpClauseList ->
|
||||
!CHECK-PARSE-NEXT: | | | OmpEndLoopDirective
|
||||
!CHECK-PARSE-NEXT: | | | | OmpLoopDirective -> llvm::omp::Directive = do
|
||||
!CHECK-PARSE-NEXT: | | | | OmpClauseList ->
|
||||
|
||||
!CHECK-UNPARSE: SUBROUTINE loop_transformation_construct
|
||||
!CHECK-UNPARSE-NEXT: IMPLICIT NONE
|
||||
!CHECK-UNPARSE-NEXT: INTEGER :: i = 10_4
|
||||
!CHECK-UNPARSE-NEXT: INTEGER x
|
||||
!CHECK-UNPARSE-NEXT: INTEGER y(i)
|
||||
!CHECK-UNPARSE-NEXT: !$OMP DO
|
||||
!CHECK-UNPARSE-NEXT: !$OMP UNROLL
|
||||
!CHECK-UNPARSE-NEXT: DO i=1_4,i
|
||||
!CHECK-UNPARSE-NEXT: y(int(i,kind=8))=5_4*y(int(i,kind=8))
|
||||
!CHECK-UNPARSE-NEXT: END DO
|
||||
!CHECK-UNPARSE-NEXT: !$OMP END UNROLL
|
||||
!CHECK-UNPARSE-NEXT: !$OMP END DO
|
||||
!CHECK-UNPARSE-NEXT: END SUBROUTINE
|
||||
85
flang/test/Parser/OpenMP/loop-transformation-construct02.f90
Normal file
85
flang/test/Parser/OpenMP/loop-transformation-construct02.f90
Normal file
@@ -0,0 +1,85 @@
|
||||
! Test the Parse Tree to ensure the OpenMP Loop Transformation Constructs nest correctly with multiple nested loops.
|
||||
|
||||
! RUN: %flang_fc1 -fdebug-dump-parse-tree -fopenmp -fopenmp-version=51 %s | FileCheck %s --check-prefix=CHECK-PARSE
|
||||
! RUN: %flang_fc1 -fdebug-unparse -fopenmp -fopenmp-version=51 %s | FileCheck %s --check-prefix=CHECK-UNPARSE
|
||||
|
||||
subroutine loop_transformation_construct
|
||||
implicit none
|
||||
integer :: I = 10
|
||||
integer :: x
|
||||
integer :: y(I)
|
||||
|
||||
!$omp do
|
||||
!$omp unroll
|
||||
!$omp tile
|
||||
do i = 1, I
|
||||
y(i) = y(i) * 5
|
||||
end do
|
||||
!$omp end tile
|
||||
!$omp end unroll
|
||||
!$omp end do
|
||||
end subroutine
|
||||
|
||||
!CHECK-PARSE: | ExecutionPart -> Block
|
||||
!CHECK-PARSE-NEXT: | | ExecutionPartConstruct -> ExecutableConstruct -> OpenMPConstruct -> OpenMPLoopConstruct
|
||||
!CHECK-PARSE-NEXT: | | | OmpBeginLoopDirective
|
||||
!CHECK-PARSE-NEXT: | | | | OmpLoopDirective -> llvm::omp::Directive = do
|
||||
!CHECK-PARSE-NEXT: | | | | OmpClauseList ->
|
||||
!CHECK-PARSE-NEXT: | | | OpenMPLoopConstruct
|
||||
!CHECK-PARSE-NEXT: | | | | OmpBeginLoopDirective
|
||||
!CHECK-PARSE-NEXT: | | | | | OmpLoopDirective -> llvm::omp::Directive = unroll
|
||||
!CHECK-PARSE-NEXT: | | | | | OmpClauseList ->
|
||||
!CHECK-PARSE-NEXT: | | | | OpenMPLoopConstruct
|
||||
!CHECK-PARSE-NEXT: | | | | | OmpBeginLoopDirective
|
||||
!CHECK-PARSE-NEXT: | | | | | | OmpLoopDirective -> llvm::omp::Directive = tile
|
||||
!CHECK-PARSE-NEXT: | | | | | | OmpClauseList ->
|
||||
!CHECK-PARSE-NEXT: | | | | | DoConstruct
|
||||
!CHECK-PARSE-NEXT: | | | | | | NonLabelDoStmt
|
||||
!CHECK-PARSE-NEXT: | | | | | | | LoopControl -> LoopBounds
|
||||
!CHECK-PARSE-NEXT: | | | | | | | | Scalar -> Name = 'i'
|
||||
!CHECK-PARSE-NEXT: | | | | | | | | Scalar -> Expr = '1_4'
|
||||
!CHECK-PARSE-NEXT: | | | | | | | | | LiteralConstant -> IntLiteralConstant = '1'
|
||||
!CHECK-PARSE-NEXT: | | | | | | | | Scalar -> Expr = 'i'
|
||||
!CHECK-PARSE-NEXT: | | | | | | | | | Designator -> DataRef -> Name = 'i'
|
||||
!CHECK-PARSE-NEXT: | | | | | | Block
|
||||
!CHECK-PARSE-NEXT: | | | | | | | ExecutionPartConstruct -> ExecutableConstruct -> ActionStmt -> AssignmentStmt = 'y(int(i,kind=8))=5_4*y(int(i,kind=8))'
|
||||
!CHECK-PARSE-NEXT: | | | | | | | | Variable = 'y(int(i,kind=8))'
|
||||
!CHECK-PARSE-NEXT: | | | | | | | | | Designator -> DataRef -> ArrayElement
|
||||
!CHECK-PARSE-NEXT: | | | | | | | | | | DataRef -> Name = 'y'
|
||||
!CHECK-PARSE-NEXT: | | | | | | | | | | SectionSubscript -> Integer -> Expr = 'i'
|
||||
!CHECK-PARSE-NEXT: | | | | | | | | | | | Designator -> DataRef -> Name = 'i'
|
||||
!CHECK-PARSE-NEXT: | | | | | | | | Expr = '5_4*y(int(i,kind=8))'
|
||||
!CHECK-PARSE-NEXT: | | | | | | | | | Multiply
|
||||
!CHECK-PARSE-NEXT: | | | | | | | | | | Expr = 'y(int(i,kind=8))'
|
||||
!CHECK-PARSE-NEXT: | | | | | | | | | | | Designator -> DataRef -> ArrayElement
|
||||
!CHECK-PARSE-NEXT: | | | | | | | | | | | | DataRef -> Name = 'y'
|
||||
!CHECK-PARSE-NEXT: | | | | | | | | | | | | SectionSubscript -> Integer -> Expr = 'i'
|
||||
!CHECK-PARSE-NEXT: | | | | | | | | | | | | | Designator -> DataRef -> Name = 'i'
|
||||
!CHECK-PARSE-NEXT: | | | | | | | | | | Expr = '5_4'
|
||||
!CHECK-PARSE-NEXT: | | | | | | | | | | | LiteralConstant -> IntLiteralConstant = '5'
|
||||
!CHECK-PARSE-NEXT: | | | | | | EndDoStmt ->
|
||||
!CHECK-PARSE-NEXT: | | | | | OmpEndLoopDirective
|
||||
!CHECK-PARSE-NEXT: | | | | | | OmpLoopDirective -> llvm::omp::Directive = tile
|
||||
!CHECK-PARSE-NEXT: | | | | | | OmpClauseList ->
|
||||
!CHECK-PARSE-NEXT: | | | | OmpEndLoopDirective
|
||||
!CHECK-PARSE-NEXT: | | | | | OmpLoopDirective -> llvm::omp::Directive = unroll
|
||||
!CHECK-PARSE-NEXT: | | | | | OmpClauseList ->
|
||||
!CHECK-PARSE-NEXT: | | | OmpEndLoopDirective
|
||||
!CHECK-PARSE-NEXT: | | | | OmpLoopDirective -> llvm::omp::Directive = do
|
||||
!CHECK-PARSE-NEXT: | | | | OmpClauseList ->
|
||||
|
||||
!CHECK-UNPARSE: SUBROUTINE loop_transformation_construct
|
||||
!CHECK-UNPARSE-NEXT: IMPLICIT NONE
|
||||
!CHECK-UNPARSE-NEXT: INTEGER :: i = 10_4
|
||||
!CHECK-UNPARSE-NEXT: INTEGER x
|
||||
!CHECK-UNPARSE-NEXT: INTEGER y(i)
|
||||
!CHECK-UNPARSE-NEXT: !$OMP DO
|
||||
!CHECK-UNPARSE-NEXT: !$OMP UNROLL
|
||||
!CHECK-UNPARSE-NEXT: !$OMP TILE
|
||||
!CHECK-UNPARSE-NEXT: DO i=1_4,i
|
||||
!CHECK-UNPARSE-NEXT: y(int(i,kind=8))=5_4*y(int(i,kind=8))
|
||||
!CHECK-UNPARSE-NEXT: END DO
|
||||
!CHECK-UNPARSE-NEXT: !$OMP END TILE
|
||||
!CHECK-UNPARSE-NEXT: !$OMP END UNROLL
|
||||
!CHECK-UNPARSE-NEXT: !$OMP END DO
|
||||
!CHECK-UNPARSE-NEXT: END SUBROUTINE
|
||||
100
flang/test/Semantics/OpenMP/loop-transformation-construct01.f90
Normal file
100
flang/test/Semantics/OpenMP/loop-transformation-construct01.f90
Normal file
@@ -0,0 +1,100 @@
|
||||
! Testing the Semantics of nested Loop Transformation Constructs
|
||||
|
||||
!RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=51
|
||||
|
||||
subroutine loop_transformation_construct1
|
||||
implicit none
|
||||
|
||||
!$omp do
|
||||
!ERROR: A DO loop must follow the UNROLL directive
|
||||
!$omp unroll
|
||||
end subroutine
|
||||
|
||||
subroutine loop_transformation_construct2
|
||||
implicit none
|
||||
integer :: i = 5
|
||||
integer :: y
|
||||
integer :: v(i)
|
||||
|
||||
!$omp do
|
||||
!$omp tile
|
||||
do x = 1, i
|
||||
v(x) = x(x) * 2
|
||||
end do
|
||||
!$omp end tile
|
||||
!$omp end do
|
||||
!ERROR: The END TILE directive must follow the DO loop associated with the loop construct
|
||||
!$omp end tile
|
||||
end subroutine
|
||||
|
||||
subroutine loop_transformation_construct2
|
||||
implicit none
|
||||
integer :: i = 5
|
||||
integer :: y
|
||||
integer :: v(i)
|
||||
|
||||
!$omp do
|
||||
!ERROR: Only Loop Transformation Constructs or Loop Nests can be nested within Loop Constructs
|
||||
!$omp parallel do
|
||||
do x = 1, i
|
||||
v(x) = x(x) * 2
|
||||
end do
|
||||
end subroutine
|
||||
|
||||
subroutine loop_transformation_construct3
|
||||
implicit none
|
||||
integer :: i = 5
|
||||
integer :: y
|
||||
integer :: v(i)
|
||||
|
||||
!$omp do
|
||||
do x = 1, i
|
||||
v(x) = x(x) * 2
|
||||
end do
|
||||
!ERROR: A DO loop must follow the TILE directive
|
||||
!$omp tile
|
||||
end subroutine
|
||||
|
||||
subroutine loop_transformation_construct4
|
||||
implicit none
|
||||
integer :: i = 5
|
||||
integer :: y
|
||||
integer :: v(i)
|
||||
|
||||
!$omp do
|
||||
!ERROR: If a loop construct has been fully unrolled, it cannot then be tiled
|
||||
!$omp tile
|
||||
!$omp unroll full
|
||||
do x = 1, i
|
||||
v(x) = x(x) * 2
|
||||
end do
|
||||
end subroutine
|
||||
|
||||
subroutine loop_transformation_construct5
|
||||
implicit none
|
||||
integer :: i = 5
|
||||
integer :: y
|
||||
integer :: v(i)
|
||||
|
||||
!$omp do
|
||||
!ERROR: If a loop construct has been fully unrolled, it cannot then be tiled
|
||||
!$omp tile
|
||||
!$omp unroll
|
||||
do x = 1, i
|
||||
v(x) = x(x) * 2
|
||||
end do
|
||||
end subroutine
|
||||
|
||||
subroutine loop_transformation_construct6
|
||||
implicit none
|
||||
integer :: i = 5
|
||||
integer :: y
|
||||
integer :: v(i)
|
||||
|
||||
!$omp do
|
||||
!$omp tile
|
||||
!$omp unroll partial(2)
|
||||
do x = 1, i
|
||||
v(x) = x(x) * 2
|
||||
end do
|
||||
end subroutine
|
||||
Reference in New Issue
Block a user