Skip to content
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
b391971
Improve canonloop/iv naming
Meinersbur Sep 19, 2025
a6e3426
Merge branch 'main' into users/meinersbur/mlir_loop-varnaming
Meinersbur Sep 23, 2025
ce66eec
Avoid compiler warning
Meinersbur Sep 23, 2025
3a141d6
Add perfect-nest and rectangular loop nest tests
Meinersbur Sep 23, 2025
ff4eccb
Add omp.tile operation
Meinersbur Sep 23, 2025
1cdb2f3
Merge branch 'users/meinersbur/flang_loopnest-checks' into HEAD
Meinersbur Sep 23, 2025
30f036d
Merge branch 'users/meinersbur/mlir_loop-varnaming' into HEAD
Meinersbur Sep 23, 2025
bfe9c6b
[flang] Add standalone tile support
Meinersbur Sep 23, 2025
375e948
Merge branch 'users/meinersbur/flang_loopnest-checks' into HEAD
Meinersbur Sep 23, 2025
4675062
Merge branch 'users/meinersbur/mlir_loop-varnaming' into HEAD
Meinersbur Sep 23, 2025
e57b52f
Merge branch 'users/meinersbur/mlir_tile' into HEAD
Meinersbur Sep 23, 2025
e7c0c5a
dos2unix
Meinersbur Sep 23, 2025
4934424
Fix symbol resolution
Meinersbur Sep 23, 2025
dd3f66d
Merge branch 'users/meinersbur/flang_loopnest-checks' into HEAD
Meinersbur Sep 24, 2025
2436b5e
Merge branch 'users/meinersbur/mlir_loop-varnaming' into HEAD
Meinersbur Sep 24, 2025
d3683d5
Merge branch 'users/meinersbur/mlir_tile' into HEAD
Meinersbur Sep 24, 2025
01a056f
avoid compiler warning
Meinersbur Sep 24, 2025
7655a11
Avoid structured binding capture to appease compiler
Meinersbur Sep 24, 2025
4eb6526
Merge branch 'users/meinersbur/mlir_tile' into HEAD
Meinersbur Sep 24, 2025
f078a8e
regexify varnames
Meinersbur Oct 1, 2025
f7d1c21
Merge branch 'main' (early part) into users/meinersbur/mlir_tile
Meinersbur Oct 2, 2025
1842342
Merge branch 'main' (early part) into users/meinersbur/mlir_tile
Meinersbur Oct 2, 2025
5a8776c
Merge branch 'main' into users/meinersbur/mlir_tile
Meinersbur Oct 2, 2025
44ea460
remove merge conflict leftovers
Meinersbur Oct 2, 2025
bcc5cd5
Add - token to format test
Meinersbur Oct 2, 2025
7789e64
Merge branch 'main' into users/meinersbur/mlir_tile
Meinersbur Oct 2, 2025
f350bb8
Merge branch 'main' (early part) into users/meinersbur/mlir_tile
Meinersbur Oct 2, 2025
d7c9c27
Merge branch 'main' into users/meinersbur/mlir_tile
Meinersbur Oct 2, 2025
7d130c9
Merge branch 'users/meinersbur/mlir_tile' into users/meinersbur/flang…
Meinersbur Oct 2, 2025
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
13 changes: 13 additions & 0 deletions flang/lib/Lower/OpenMP/ClauseProcessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,19 @@ bool ClauseProcessor::processNumTasks(
return false;
}

bool ClauseProcessor::processSizes(StatementContext &stmtCtx,
mlir::omp::SizesClauseOps &result) const {
if (auto *clause = findUniqueClause<omp::clause::Sizes>()) {
result.sizes.reserve(clause->v.size());
for (const ExprTy &vv : clause->v)
result.sizes.push_back(fir::getBase(converter.genExprValue(vv, stmtCtx)));

return true;
}

return false;
}

bool ClauseProcessor::processNumTeams(
lower::StatementContext &stmtCtx,
mlir::omp::NumTeamsClauseOps &result) const {
Expand Down
2 changes: 2 additions & 0 deletions flang/lib/Lower/OpenMP/ClauseProcessor.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ class ClauseProcessor {
mlir::omp::LoopRelatedClauseOps &loopResult,
mlir::omp::CollapseClauseOps &collapseResult,
llvm::SmallVectorImpl<const semantics::Symbol *> &iv) const;
bool processSizes(StatementContext &stmtCtx,
mlir::omp::SizesClauseOps &result) const;
bool processDevice(lower::StatementContext &stmtCtx,
mlir::omp::DeviceClauseOps &result) const;
bool processDeviceType(mlir::omp::DeviceTypeClauseOps &result) const;
Expand Down
360 changes: 236 additions & 124 deletions flang/lib/Lower/OpenMP/OpenMP.cpp

Large diffs are not rendered by default.

24 changes: 20 additions & 4 deletions flang/lib/Lower/OpenMP/Utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -652,7 +652,6 @@ int64_t collectLoopRelatedInfo(
mlir::omp::LoopRelatedClauseOps &result,
llvm::SmallVectorImpl<const semantics::Symbol *> &iv) {
int64_t numCollapse = 1;
fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();

// Collect the loops to collapse.
lower::pft::Evaluation *doConstructEval = &eval.getFirstNestedEvaluation();
Expand All @@ -667,6 +666,25 @@ int64_t collectLoopRelatedInfo(
numCollapse = collapseValue;
}

collectLoopRelatedInfo(converter, currentLocation, eval, numCollapse, result,
iv);
return numCollapse;
}

void collectLoopRelatedInfo(
lower::AbstractConverter &converter, mlir::Location currentLocation,
lower::pft::Evaluation &eval, int64_t numCollapse,
mlir::omp::LoopRelatedClauseOps &result,
llvm::SmallVectorImpl<const semantics::Symbol *> &iv) {

fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();

// Collect the loops to collapse.
lower::pft::Evaluation *doConstructEval = &eval.getFirstNestedEvaluation();
if (doConstructEval->getIf<parser::DoConstruct>()->IsDoConcurrent()) {
TODO(currentLocation, "Do Concurrent in Worksharing loop construct");
}

// Collect sizes from tile directive if present.
std::int64_t sizesLengthValue = 0l;
if (auto *ompCons{eval.getIf<parser::OpenMPConstruct>()}) {
Expand All @@ -676,7 +694,7 @@ int64_t collectLoopRelatedInfo(
});
}

collapseValue = std::max(collapseValue, sizesLengthValue);
std::int64_t collapseValue = std::max(numCollapse, sizesLengthValue);
std::size_t loopVarTypeSize = 0;
do {
lower::pft::Evaluation *doLoop =
Expand Down Expand Up @@ -709,8 +727,6 @@ int64_t collectLoopRelatedInfo(
} while (collapseValue > 0);

convertLoopBounds(converter, currentLocation, result, loopVarTypeSize);

return numCollapse;
}

} // namespace omp
Expand Down
7 changes: 7 additions & 0 deletions flang/lib/Lower/OpenMP/Utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,13 @@ int64_t collectLoopRelatedInfo(
mlir::omp::LoopRelatedClauseOps &result,
llvm::SmallVectorImpl<const semantics::Symbol *> &iv);

void collectLoopRelatedInfo(
lower::AbstractConverter &converter, mlir::Location currentLocation,
lower::pft::Evaluation &eval, std::int64_t collapseValue,
// const omp::List<omp::Clause> &clauses,
mlir::omp::LoopRelatedClauseOps &result,
llvm::SmallVectorImpl<const semantics::Symbol *> &iv);

void collectTileSizesFromOpenMPConstruct(
const parser::OpenMPConstruct *ompCons,
llvm::SmallVectorImpl<int64_t> &tileSizes,
Expand Down
7 changes: 4 additions & 3 deletions flang/lib/Semantics/check-directive-structure.h
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,8 @@ class DirectiveStructureChecker : public virtual BaseChecker {
const C &clause, const parser::ScalarIntConstantExpr &i);

void RequiresPositiveParameter(const C &clause,
const parser::ScalarIntExpr &i, llvm::StringRef paramName = "parameter");
const parser::ScalarIntExpr &i, llvm::StringRef paramName = "parameter",
bool allowZero = true);

void OptionalConstantPositiveParameter(
const C &clause, const std::optional<parser::ScalarIntConstantExpr> &o);
Expand Down Expand Up @@ -657,9 +658,9 @@ void DirectiveStructureChecker<D, C, PC, ClauseEnumSize>::SayNotMatching(
template <typename D, typename C, typename PC, std::size_t ClauseEnumSize>
void DirectiveStructureChecker<D, C, PC,
ClauseEnumSize>::RequiresPositiveParameter(const C &clause,
const parser::ScalarIntExpr &i, llvm::StringRef paramName) {
const parser::ScalarIntExpr &i, llvm::StringRef paramName, bool allowZero) {
if (const auto v{GetIntValue(i)}) {
if (*v < 0) {
if (*v < (allowZero ? 0 : 1)) {
context_.Say(GetContext().clauseSource,
"The %s of the %s clause must be "
"a positive integer expression"_err_en_US,
Expand Down
8 changes: 7 additions & 1 deletion flang/lib/Semantics/check-omp-structure.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2749,6 +2749,13 @@ void OmpStructureChecker::Enter(const parser::OmpClause &x) {
}
}

void OmpStructureChecker::Enter(const parser::OmpClause::Sizes &c) {
CheckAllowedClause(llvm::omp::Clause::OMPC_sizes);
for (const parser::Cosubscript &v : c.v)
RequiresPositiveParameter(llvm::omp::Clause::OMPC_sizes, v,
/*paramName=*/"parameter", /*allowZero=*/false);
}

// Following clauses do not have a separate node in parse-tree.h.
CHECK_SIMPLE_CLAUSE(Absent, OMPC_absent)
CHECK_SIMPLE_CLAUSE(Affinity, OMPC_affinity)
Expand Down Expand Up @@ -2790,7 +2797,6 @@ CHECK_SIMPLE_CLAUSE(Notinbranch, OMPC_notinbranch)
CHECK_SIMPLE_CLAUSE(Partial, OMPC_partial)
CHECK_SIMPLE_CLAUSE(ProcBind, OMPC_proc_bind)
CHECK_SIMPLE_CLAUSE(Simd, OMPC_simd)
CHECK_SIMPLE_CLAUSE(Sizes, OMPC_sizes)
CHECK_SIMPLE_CLAUSE(Permutation, OMPC_permutation)
CHECK_SIMPLE_CLAUSE(Uniform, OMPC_uniform)
CHECK_SIMPLE_CLAUSE(Unknown, OMPC_unknown)
Expand Down
158 changes: 148 additions & 10 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 Expand Up @@ -2258,10 +2388,18 @@ void OmpAttributeVisitor::PrivatizeAssociatedLoopIndexAndCheckLoopLevel(
void OmpAttributeVisitor::CheckAssocLoopLevel(
std::int64_t level, const parser::OmpClause *clause) {
if (clause && level != 0) {
context_.Say(clause->source,
"The value of the parameter in the COLLAPSE or ORDERED clause must"
" not be larger than the number of nested loops"
" following the construct."_err_en_US);
switch (clause->Id()) {
case llvm::omp::OMPC_sizes:
context_.Say(clause->source,
"The SIZES clause has more entries than there are nested canonical loops."_err_en_US);
break;
default:
context_.Say(clause->source,
"The value of the parameter in the COLLAPSE or ORDERED clause must"
" not be larger than the number of nested loops"
" following the construct."_err_en_US);
break;
}
}
}

Expand Down
Loading