Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 136 additions & 6 deletions flang/lib/Semantics/resolve-directives.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,9 @@ template <typename T> class DirectiveAttributeVisitor {
dataSharingAttributeObjects_.clear();
}
bool HasDataSharingAttributeObject(const Symbol &);
std::tuple<const parser::Name *, const parser::ScalarExpr *,
const parser::ScalarExpr *, const parser::ScalarExpr *>
GetLoopBounds(const parser::DoConstruct &);
const parser::Name *GetLoopIndex(const parser::DoConstruct &);
const parser::DoConstruct *GetDoConstructIf(
const parser::ExecutionPartConstruct &);
Expand Down Expand Up @@ -933,6 +936,13 @@ class OmpAttributeVisitor : DirectiveAttributeVisitor<llvm::omp::Directive> {
privateDataSharingAttributeObjects_.clear();
}

/// Check that loops in the loop nest are perfectly nested, as well that lower
/// bound, upper bound, and step expressions do not use the iv
/// of a surrounding loop of the associated loops nest.
/// We do not support non-perfectly nested loops not non-rectangular loops yet
/// (both introduced in OpenMP 5.0)
void CheckPerfectNestAndRectangularLoop(const parser::OpenMPLoopConstruct &x);

// Predetermined DSA rules
void PrivatizeAssociatedLoopIndexAndCheckLoopLevel(
const parser::OpenMPLoopConstruct &);
Expand Down Expand Up @@ -1009,23 +1019,31 @@ bool DirectiveAttributeVisitor<T>::HasDataSharingAttributeObject(
}

template <typename T>
const parser::Name *DirectiveAttributeVisitor<T>::GetLoopIndex(
const parser::DoConstruct &x) {
std::tuple<const parser::Name *, const parser::ScalarExpr *,
const parser::ScalarExpr *, const parser::ScalarExpr *>
DirectiveAttributeVisitor<T>::GetLoopBounds(const parser::DoConstruct &x) {
using Bounds = parser::LoopControl::Bounds;
if (x.GetLoopControl()) {
if (const Bounds * b{std::get_if<Bounds>(&x.GetLoopControl()->u)}) {
return &b->name.thing;
} else {
return nullptr;
auto &&step = b->step;
return {&b->name.thing, &b->lower, &b->upper,
step.has_value() ? &step.value() : nullptr};
}
} else {
context_
.Say(std::get<parser::Statement<parser::NonLabelDoStmt>>(x.t).source,
"Loop control is not present in the DO LOOP"_err_en_US)
.Attach(GetContext().directiveSource,
"associated with the enclosing LOOP construct"_en_US);
return nullptr;
}
return {nullptr, nullptr, nullptr, nullptr};
}

template <typename T>
const parser::Name *DirectiveAttributeVisitor<T>::GetLoopIndex(
const parser::DoConstruct &x) {
auto &&[iv, lb, ub, step] = GetLoopBounds(x);
return iv;
}

template <typename T>
Expand Down Expand Up @@ -1957,6 +1975,10 @@ bool OmpAttributeVisitor::Pre(const parser::OpenMPLoopConstruct &x) {
}
}
}

// Must be done before iv privatization
CheckPerfectNestAndRectangularLoop(x);

PrivatizeAssociatedLoopIndexAndCheckLoopLevel(x);
ordCollapseLevel = GetNumAffectedLoopsFromLoopConstruct(x) + 1;
return true;
Expand Down Expand Up @@ -2152,6 +2174,114 @@ void OmpAttributeVisitor::CollectNumAffectedLoopsFromClauses(
}
}

void OmpAttributeVisitor::CheckPerfectNestAndRectangularLoop(
const parser::OpenMPLoopConstruct &x) {
auto &dirContext = GetContext();
std::int64_t dirDepth{dirContext.associatedLoopLevel};
if (dirDepth <= 0)
return;

auto checkExprHasSymbols = [&](llvm::SmallVector<Symbol *> &ivs,
const parser::ScalarExpr *bound) {
if (ivs.empty())
return;
auto boundExpr{semantics::AnalyzeExpr(context_, *bound)};
if (!boundExpr)
return;
semantics::UnorderedSymbolSet boundSyms =
evaluate::CollectSymbols(*boundExpr);
if (boundSyms.empty())
return;
for (Symbol *iv : ivs) {
if (boundSyms.count(*iv) != 0) {
// TODO: Point to occurence of iv in boundExpr, directiveSource as a
// note
context_.Say(dirContext.directiveSource,
"Trip count must be computable and invariant"_err_en_US);
}
}
};

// Skip over loop transformation directives
const parser::OpenMPLoopConstruct *innerMostLoop = &x;
const parser::NestedConstruct *innerMostNest = nullptr;
while (auto &optLoopCons{
std::get<std::optional<parser::NestedConstruct>>(innerMostLoop->t)}) {
innerMostNest = &(optLoopCons.value());
if (const auto *innerLoop{
std::get_if<common::Indirection<parser::OpenMPLoopConstruct>>(
innerMostNest)}) {
innerMostLoop = &(innerLoop->value());
} else {
break;
}
}

if (!innerMostNest)
return;
const auto &outer{std::get_if<parser::DoConstruct>(innerMostNest)};
if (!outer)
return;

llvm::SmallVector<Symbol *> ivs;
int curLevel{0};
const parser::DoConstruct *loop{outer};
while (true) {
auto [iv, lb, ub, step] = GetLoopBounds(*loop);

if (lb)
checkExprHasSymbols(ivs, lb);
if (ub)
checkExprHasSymbols(ivs, ub);
if (step)
checkExprHasSymbols(ivs, step);
if (iv) {
if (auto *symbol{currScope().FindSymbol(iv->source)})
ivs.push_back(symbol);
}

// Stop after processing all affected loops
if (curLevel + 1 >= dirDepth)
break;

// Recurse into nested loop
const auto &block{std::get<parser::Block>(loop->t)};
if (block.empty()) {
// Insufficient number of nested loops already reported by
// CheckAssocLoopLevel()
break;
}

loop = GetDoConstructIf(block.front());
if (!loop) {
// Insufficient number of nested loops already reported by
// CheckAssocLoopLevel()
break;
}

auto checkPerfectNest = [&, this]() {
auto blockSize = block.size();
if (blockSize <= 1)
return;

if (parser::Unwrap<parser::ContinueStmt>(x))
blockSize -= 1;

if (blockSize <= 1)
return;

// Non-perfectly nested loop
// TODO: Point to non-DO statement, directiveSource as a note
context_.Say(dirContext.directiveSource,
"Canonical loop nest must be perfectly nested."_err_en_US);
};

checkPerfectNest();

++curLevel;
}
}

// 2.15.1.1 Data-sharing Attribute Rules - Predetermined
// - The loop iteration variable(s) in the associated do-loop(s) of a do,
// parallel do, taskloop, or distribute construct is (are) private.
Expand Down
1 change: 1 addition & 0 deletions flang/test/Semantics/OpenMP/do08.f90
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ program omp
!$omp end do


!ERROR: Canonical loop nest must be perfectly nested.
!ERROR: The value of the parameter in the COLLAPSE or ORDERED clause must not be larger than the number of nested loops following the construct.
!$omp do collapse(3)
do 60 i=2,200,2
Expand Down
1 change: 1 addition & 0 deletions flang/test/Semantics/OpenMP/do13.f90
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ program omp
!$omp end do


!ERROR: Canonical loop nest must be perfectly nested.
!ERROR: The value of the parameter in the COLLAPSE or ORDERED clause must not be larger than the number of nested loops following the construct.
!$omp do collapse(3)
do 60 i=1,10
Expand Down
73 changes: 73 additions & 0 deletions flang/test/Semantics/OpenMP/do22.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
! RUN: %python %S/../test_errors.py %s %flang -fopenmp
! Check for existence of loop following a DO directive

subroutine do_imperfectly_nested_before
integer i, j

!ERROR: The value of the parameter in the COLLAPSE or ORDERED clause must not be larger than the number of nested loops following the construct.
!$omp do collapse(2)
do i = 1, 10
print *, i
do j = 1, 10
print *, i, j
end do
end do
!$omp end do
end subroutine


subroutine do_imperfectly_nested_behind
integer i, j

!ERROR: Canonical loop nest must be perfectly nested.
!$omp do collapse(2)
do i = 1, 10
do j = 1, 10
print *, i, j
end do
print *, i
end do
!$omp end do
end subroutine


subroutine do_nonrectangular_lb
integer i, j

!ERROR: Trip count must be computable and invariant
!$omp do collapse(2)
do i = 1, 10
do j = i, 10
print *, i, j
end do
end do
!$omp end do
end subroutine


subroutine do_nonrectangular_ub
integer i, j

!ERROR: Trip count must be computable and invariant
!$omp do collapse(2)
do i = 1, 10
do j = 0, i
print *, i, j
end do
end do
!$omp end do
end subroutine


subroutine do_nonrectangular_step
integer i, j

!ERROR: Trip count must be computable and invariant
!$omp do collapse(2)
do i = 1, 10
do j = 1, 10, i
print *, i, j
end do
end do
!$omp end do
end subroutine
Loading
Loading