Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
157 changes: 151 additions & 6 deletions flang/lib/Semantics/resolve-directives.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,24 @@ template <typename T> class DirectiveAttributeVisitor {
dataSharingAttributeObjects_.clear();
}
bool HasDataSharingAttributeObject(const Symbol &);

/// Extract the iv and bounds of a DO loop:
/// 1. The loop index/induction variable
/// 2. The lower bound
/// 3. The upper bound
/// 4. The step/increment (or nullptr if not present)
///
/// Each returned tuple value can be nullptr if not present. Diagnoses an
/// error if the the DO loop is a DO WHILE or DO CONCURRENT loop.
std::tuple<const parser::Name *, const parser::ScalarExpr *,
const parser::ScalarExpr *, const parser::ScalarExpr *>
GetLoopBounds(const parser::DoConstruct &);

/// Extract the loop index/induction variable from a DO loop. Diagnoses an
/// error if the the DO loop is a DO WHILE or DO CONCURRENT loop and returns
/// nullptr.
const parser::Name *GetLoopIndex(const parser::DoConstruct &);

const parser::DoConstruct *GetDoConstructIf(
const parser::ExecutionPartConstruct &);
Symbol *DeclareNewAccessEntity(const Symbol &, Symbol::Flag, Scope &);
Expand Down Expand Up @@ -933,6 +950,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 +1033,30 @@ 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) {
return std::get<const parser::Name *>(GetLoopBounds(x));
}

template <typename T>
Expand Down Expand Up @@ -1957,6 +1988,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 +2187,116 @@ 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);
}
}
};

// Find the associated region by skipping nested loop-associated constructs
// such as loop transformations
const parser::NestedConstruct *innermostAssocRegion{nullptr};
const parser::OpenMPLoopConstruct *innermostConstruct{&x};
while (const auto &innerAssocStmt{
std::get<std::optional<parser::NestedConstruct>>(
innermostConstruct->t)}) {
innermostAssocRegion = &(innerAssocStmt.value());
if (const auto *innerConstruct{
std::get_if<common::Indirection<parser::OpenMPLoopConstruct>>(
innermostAssocRegion)}) {
innermostConstruct = &innerConstruct->value();
} else {
break;
}
}

if (!innermostAssocRegion)
return;
const auto &outer{std::get_if<parser::DoConstruct>(innermostAssocRegion)};
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