[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:
Jack Styles
2025-07-01 08:39:15 +01:00
committed by GitHub
parent 372e332a57
commit 65cb0eae58
11 changed files with 520 additions and 96 deletions

View File

@@ -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;
};

View File

@@ -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 =

View File

@@ -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) {

View File

@@ -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) {

View File

@@ -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;
}
}

View File

@@ -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) {

View File

@@ -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)

View File

@@ -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)

View 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

View 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

View 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