[flang-commits] [flang] [llvm] [Flang][OpenMP] Add semantic support for OpenMP Loop Interchange and permutation clause in Flang (PR #183435)
Ferran Toda via flang-commits
flang-commits at lists.llvm.org
Mon Mar 16 07:22:38 PDT 2026
https://github.com/NouTimbaler updated https://github.com/llvm/llvm-project/pull/183435
>From d86b48e490f3c23eabc0199118e5ae9dbbb127f8 Mon Sep 17 00:00:00 2001
From: Ferran Toda <ferran.todacasaban at bsc.es>
Date: Thu, 12 Mar 2026 02:14:58 +0000
Subject: [PATCH 1/2] keep only parse and semantics
---
.../flang/Semantics/openmp-directive-sets.h | 2 +
flang/lib/Lower/OpenMP/OpenMP.cpp | 14 +++
flang/lib/Parser/openmp-parsers.cpp | 3 +-
flang/lib/Semantics/check-omp-loop.cpp | 36 ++++++
flang/lib/Semantics/check-omp-structure.cpp | 1 -
flang/lib/Semantics/resolve-directives.cpp | 60 +++++++---
flang/test/Parser/OpenMP/do-interchange.f90 | 34 ++++++
flang/test/Parser/OpenMP/interchange-fail.f90 | 31 +++++
.../Parser/OpenMP/interchange-permutation.f90 | 35 ++++++
flang/test/Parser/OpenMP/interchange.f90 | 30 +++++
.../OpenMP/interchange-permutation.f90 | 108 ++++++++++++++++++
flang/test/Semantics/OpenMP/interchange01.f90 | 43 +++++++
llvm/include/llvm/Frontend/OpenMP/OMP.td | 2 +-
13 files changed, 382 insertions(+), 17 deletions(-)
create mode 100644 flang/test/Parser/OpenMP/do-interchange.f90
create mode 100644 flang/test/Parser/OpenMP/interchange-fail.f90
create mode 100644 flang/test/Parser/OpenMP/interchange-permutation.f90
create mode 100644 flang/test/Parser/OpenMP/interchange.f90
create mode 100644 flang/test/Semantics/OpenMP/interchange-permutation.f90
create mode 100644 flang/test/Semantics/OpenMP/interchange01.f90
diff --git a/flang/include/flang/Semantics/openmp-directive-sets.h b/flang/include/flang/Semantics/openmp-directive-sets.h
index 609a7be700c28..5e9979d032028 100644
--- a/flang/include/flang/Semantics/openmp-directive-sets.h
+++ b/flang/include/flang/Semantics/openmp-directive-sets.h
@@ -278,12 +278,14 @@ static const OmpDirectiveSet loopConstructSet{
Directive::OMPD_fuse,
Directive::OMPD_tile,
Directive::OMPD_unroll,
+ Directive::OMPD_interchange,
};
static const OmpDirectiveSet loopTransformationSet{
Directive::OMPD_tile,
Directive::OMPD_unroll,
Directive::OMPD_fuse,
+ Directive::OMPD_interchange,
};
static const OmpDirectiveSet nonPartialVarSet{
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index e2328b39c180f..ae5f6f50bda09 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -2245,6 +2245,16 @@ static void genCanonicalLoopNest(
firOpBuilder.setInsertionPointAfter(loops.front());
}
+static void genInterchangeOp(Fortran::lower::AbstractConverter &converter,
+ Fortran::lower::SymMap &symTable,
+ lower::StatementContext &stmtCtx,
+ Fortran::semantics::SemanticsContext &semaCtx,
+ Fortran::lower::pft::Evaluation &eval,
+ mlir::Location loc, const ConstructQueue &queue,
+ ConstructQueue::const_iterator item) {
+ TODO(converter.getCurrentLocation(), "OpenMP Interchange");
+}
+
static void genTileOp(Fortran::lower::AbstractConverter &converter,
Fortran::lower::SymMap &symTable,
lower::StatementContext &stmtCtx,
@@ -3740,6 +3750,10 @@ static void genOMPDispatch(lower::AbstractConverter &converter,
newOp = genTeamsOp(converter, symTable, stmtCtx, semaCtx, eval, loc, queue,
item);
break;
+ case llvm::omp::Directive::OMPD_interchange:
+ genInterchangeOp(converter, symTable, stmtCtx, semaCtx, eval, loc, queue,
+ item);
+ break;
case llvm::omp::Directive::OMPD_tile:
genTileOp(converter, symTable, stmtCtx, semaCtx, eval, loc, queue, item);
break;
diff --git a/flang/lib/Parser/openmp-parsers.cpp b/flang/lib/Parser/openmp-parsers.cpp
index 7838173d791a1..72b95397059bc 100644
--- a/flang/lib/Parser/openmp-parsers.cpp
+++ b/flang/lib/Parser/openmp-parsers.cpp
@@ -1608,7 +1608,7 @@ TYPE_PARSER( //
"SIZES" >> construct<OmpClause>(construct<OmpClause::Sizes>(
parenthesized(nonemptyList(scalarIntExpr)))) ||
"PERMUTATION" >> construct<OmpClause>(construct<OmpClause::Permutation>(
- parenthesized(nonemptyList(scalarIntExpr)))) ||
+ parenthesized(nonemptyList(scalarIntConstantExpr)))) ||
"THREADS"_id >> construct<OmpClause>(construct<OmpClause::Threads>()) ||
"THREADSET" >> construct<OmpClause>(construct<OmpClause::Threadset>(
parenthesized(Parser<OmpThreadsetClause>{}))) ||
@@ -2487,6 +2487,7 @@ static constexpr DirectiveSet GetLoopDirectives() {
unsigned(Directive::OMPD_fuse),
unsigned(Directive::OMPD_tile),
unsigned(Directive::OMPD_unroll),
+ unsigned(Directive::OMPD_interchange),
};
return loopDirectives;
}
diff --git a/flang/lib/Semantics/check-omp-loop.cpp b/flang/lib/Semantics/check-omp-loop.cpp
index 818768d9af61e..5a07d459736a6 100644
--- a/flang/lib/Semantics/check-omp-loop.cpp
+++ b/flang/lib/Semantics/check-omp-loop.cpp
@@ -805,6 +805,42 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Sizes &c) {
/*paramName=*/"parameter", /*allowZero=*/false);
}
+void OmpStructureChecker::Enter(const parser::OmpClause::Permutation &c) {
+ llvm::omp::Clause clause = llvm::omp::Clause::OMPC_permutation;
+ CheckAllowedClause(clause);
+ if (c.v.size() < 2)
+ context_.Say(GetContext().clauseSource,
+ "The %s clause must have a length of at least two"_err_en_US,
+ parser::ToUpperCaseLetters(getClauseName(clause).str()));
+
+ llvm::SmallVector<bool> found(c.v.size(), false);
+ bool cont = true;
+ for (const auto &val : c.v) {
+ if (const auto v{GetIntValue(val)}) {
+ if (*v <= 0) {
+ cont = false;
+ context_.Say(GetContext().clauseSource,
+ "The parameter of the %s clause must be "
+ "a constant positive integer expression"_err_en_US,
+ parser::ToUpperCaseLetters(getClauseName(clause).str()));
+ } else if ((unsigned)*v - 1 < c.v.size()) {
+ found[*v - 1] = true;
+ }
+ } else
+ cont = false;
+ }
+
+ if (!cont)
+ return;
+ for (auto i : found) {
+ if (!i) {
+ context_.Say(GetContext().clauseSource,
+ "Every integer from 1 must appear in the %s clause"_err_en_US,
+ parser::ToUpperCaseLetters(getClauseName(clause).str()));
+ }
+ }
+}
+
void OmpStructureChecker::Enter(const parser::OmpClause::Looprange &x) {
CheckAllowedClause(llvm::omp::Clause::OMPC_looprange);
auto &[first, count]{x.v.t};
diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index 431c41f443f7a..179581469e5c9 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -5785,7 +5785,6 @@ CHECK_SIMPLE_CLAUSE(OmpxAttribute, OMPC_ompx_attribute)
CHECK_SIMPLE_CLAUSE(Order, OMPC_order)
CHECK_SIMPLE_CLAUSE(Otherwise, OMPC_otherwise)
CHECK_SIMPLE_CLAUSE(Partial, OMPC_partial)
-CHECK_SIMPLE_CLAUSE(Permutation, OMPC_permutation)
CHECK_SIMPLE_CLAUSE(ProcBind, OMPC_proc_bind)
CHECK_SIMPLE_CLAUSE(Read, OMPC_read)
CHECK_SIMPLE_CLAUSE(Relaxed, OMPC_relaxed)
diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp
index c8ffa22d6bb5f..50c47bb682092 100644
--- a/flang/lib/Semantics/resolve-directives.cpp
+++ b/flang/lib/Semantics/resolve-directives.cpp
@@ -2130,6 +2130,10 @@ static bool isSizesClause(const parser::OmpClause *clause) {
return std::holds_alternative<parser::OmpClause::Sizes>(clause->u);
}
+static bool isCollapseClause(const parser::OmpClause *clause) {
+ return std::holds_alternative<parser::OmpClause::Collapse>(clause->u);
+}
+
std::int64_t OmpAttributeVisitor::SetAssociatedMaxClause(
llvm::SmallVector<std::int64_t> &levels,
llvm::SmallVector<const parser::OmpClause *> &clauses) {
@@ -2138,13 +2142,14 @@ std::int64_t OmpAttributeVisitor::SetAssociatedMaxClause(
// does not exeed the number of tiled loops.
std::int64_t tileLevel = 0;
for (auto [level, clause] : llvm::zip_equal(levels, clauses))
- if (isSizesClause(clause))
+ if (clause && isSizesClause(clause))
tileLevel = level;
std::int64_t maxLevel = 1;
const parser::OmpClause *maxClause = nullptr;
for (auto [level, clause] : llvm::zip_equal(levels, clauses)) {
- if (tileLevel > 0 && tileLevel < level) {
+ if (clause && isCollapseClause(clause) && tileLevel > 0 &&
+ tileLevel < level) {
context_.Say(clause->source,
"The value of the parameter in the COLLAPSE clause must"
" not be larger than the number of the number of tiled loops"
@@ -2180,6 +2185,17 @@ void OmpAttributeVisitor::CollectNumAffectedLoopsFromLoopConstruct(
CollectNumAffectedLoopsFromClauses(clauseList, levels, clauses);
CollectNumAffectedLoopsFromInnerLoopContruct(x, levels, clauses);
+
+ bool has_permutation =
+ llvm::any_of(clauseList.v, [](const parser::OmpClause &c) {
+ return c.Id() == llvm::omp::Clause::OMPC_permutation;
+ });
+ if (x.BeginDir().DirName().v == llvm::omp::Directive::OMPD_interchange &&
+ !has_permutation) {
+ // OMPD_interchange with no permutation clause needs a level 2 nest
+ levels.push_back(2);
+ clauses.push_back(nullptr);
+ }
}
void OmpAttributeVisitor::CollectNumAffectedLoopsFromInnerLoopContruct(
@@ -2222,6 +2238,11 @@ void OmpAttributeVisitor::CollectNumAffectedLoopsFromClauses(
levels.push_back(tclause->v.size());
clauses.push_back(&clause);
}
+ if (const auto iclause{
+ std::get_if<parser::OmpClause::Permutation>(&clause.u)}) {
+ levels.push_back(iclause->v.size());
+ clauses.push_back(&clause);
+ }
}
}
@@ -2400,18 +2421,29 @@ void OmpAttributeVisitor::PrivatizeAssociatedLoopIndexAndCheckLoopLevel(
void OmpAttributeVisitor::CheckAssocLoopLevel(
std::int64_t level, const parser::OmpClause *clause) {
- if (clause && level != 0) {
- 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;
+ if (level != 0) {
+ if (clause) {
+ 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;
+ case llvm::omp::OMPC_permutation:
+ context_.Say(clause->source,
+ "The PERMUTATION 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;
+ }
+ } else if (GetContext().directive ==
+ llvm::omp::Directive::OMPD_interchange) {
+ // OMPD_interchange with no permutation clause needs a level 2 nest
+ context_.Say(GetContext().directiveSource,
+ "The INTERCHANGE construct must be followed by a canonical loop nest of at least 2 levels"_err_en_US);
}
}
}
diff --git a/flang/test/Parser/OpenMP/do-interchange.f90 b/flang/test/Parser/OpenMP/do-interchange.f90
new file mode 100644
index 0000000000000..e5fbc288cef39
--- /dev/null
+++ b/flang/test/Parser/OpenMP/do-interchange.f90
@@ -0,0 +1,34 @@
+! RUN: %flang_fc1 -fdebug-unparse -fopenmp -fopenmp-version=60 %s | FileCheck --ignore-case %s
+! RUN: %flang_fc1 -fdebug-dump-parse-tree -fopenmp -fopenmp-version=60 %s | FileCheck --check-prefix="PARSE-TREE" %s
+
+subroutine openmp_do_interchange(x)
+
+ integer :: x, y
+
+!CHECK: !$omp do
+!CHECK: !$omp interchange permutation
+!$omp do
+!$omp interchange permutation(2,1)
+!CHECK: do
+ do x = 1, 100
+ !CHECK: do
+ do y = 1, 100
+ call F1()
+ !CHECK: end do
+ end do
+!CHECK: end do
+ end do
+!CHECK: !$omp end interchange
+!$omp end interchange
+!$omp end do
+
+!PARSE-TREE:| | ExecutionPartConstruct -> ExecutableConstruct -> OpenMPConstruct -> OpenMPLoopConstruct
+!PARSE-TREE:| | | OmpBeginLoopDirective
+!PARSE-TREE:| | | Block
+!PARSE-TREE:| | | | ExecutionPartConstruct -> ExecutableConstruct -> OpenMPConstruct -> OpenMPLoopConstruct
+!PARSE-TREE:| | | | | OmpBeginLoopDirective
+!PARSE-TREE:| | | | | | OmpDirectiveName -> llvm::omp::Directive = interchange
+!PARSE-TREE:| | | | | Block
+!PARSE-TREE:| | | | | | ExecutionPartConstruct -> ExecutableConstruct -> DoConstruct
+
+END subroutine openmp_do_interchange
diff --git a/flang/test/Parser/OpenMP/interchange-fail.f90 b/flang/test/Parser/OpenMP/interchange-fail.f90
new file mode 100644
index 0000000000000..d83ef1746f30f
--- /dev/null
+++ b/flang/test/Parser/OpenMP/interchange-fail.f90
@@ -0,0 +1,31 @@
+! RUN: split-file %s %t
+! RUN: not %flang_fc1 -fsyntax-only -fopenmp -fopenmp-version=60 %t/stray_end1.f90 2>&1 | FileCheck %t/stray_end1.f90
+! RUN: not %flang_fc1 -fsyntax-only -fopenmp -fopenmp-version=60 %t/stray_end2.f90 2>&1 | FileCheck %t/stray_end2.f90
+! RUN: not %flang_fc1 -fsyntax-only -fopenmp -fopenmp-version=60 %t/stray_begin.f90 2>&1 | FileCheck %t/stray_begin.f90
+
+
+!--- stray_end1.f90
+! Parser error
+
+subroutine stray_end1
+ !CHECK: error: Misplaced OpenMP end-directive
+ !$omp end interchange
+end subroutine
+
+
+!--- stray_end2.f90
+
+subroutine stray_end2
+ print *
+ !CHECK: error: Misplaced OpenMP end-directive
+ !$omp end interchange
+end subroutine
+
+
+!--- stray_begin.f90
+
+subroutine stray_begin
+ !CHECK: error: This construct should contain a DO-loop or a loop-nest-generating OpenMP construct
+ !$omp interchange permutation(2,1)
+end subroutine
+
diff --git a/flang/test/Parser/OpenMP/interchange-permutation.f90 b/flang/test/Parser/OpenMP/interchange-permutation.f90
new file mode 100644
index 0000000000000..53392e050a662
--- /dev/null
+++ b/flang/test/Parser/OpenMP/interchange-permutation.f90
@@ -0,0 +1,35 @@
+! RUN: %flang_fc1 -fdebug-unparse -fopenmp -fopenmp-version=60 %s | FileCheck --ignore-case %s
+! RUN: %flang_fc1 -fdebug-dump-parse-tree -fopenmp -fopenmp-version=60 %s | FileCheck --check-prefix="PARSE-TREE" %s
+
+subroutine openmp_interchange(x)
+
+ integer :: x, y
+
+!CHECK: !$omp interchange permutation(2_4,1_4)
+!$omp interchange permutation(2,1)
+!CHECK: do
+ do x = 1, 100
+ !CHECK: do
+ do y = 1, 100
+ call F1()
+ !CHECK: end do
+ end do
+!CHECK: end do
+ end do
+!CHECK: !$omp end interchange
+!$omp end interchange
+
+!PARSE-TREE: OpenMPConstruct -> OpenMPLoopConstruct
+!PARSE-TREE: OmpBeginLoopDirective
+!PARSE-TREE: OmpDirectiveName -> llvm::omp::Directive = interchange
+!PARSE-TREE: OmpClauseList -> OmpClause -> Permutation -> Scalar -> Integer -> Constant -> Expr = '2_4'
+!PARSE-TREE: LiteralConstant -> IntLiteralConstant = '2'
+!PARSE-TREE: Scalar -> Integer -> Constant -> Expr = '1_4'
+!PARSE-TREE: LiteralConstant -> IntLiteralConstant = '1'
+!PARSE-TREE: Flags = {}
+!PARSE-TREE: DoConstruct
+!PARSE-TREE: EndDoStmt
+!PARSE-TREE: OmpEndLoopDirective
+!PARSE-TREE: OmpDirectiveName -> llvm::omp::Directive = interchange
+
+END subroutine openmp_interchange
diff --git a/flang/test/Parser/OpenMP/interchange.f90 b/flang/test/Parser/OpenMP/interchange.f90
new file mode 100644
index 0000000000000..8aba562724428
--- /dev/null
+++ b/flang/test/Parser/OpenMP/interchange.f90
@@ -0,0 +1,30 @@
+! RUN: %flang_fc1 -fdebug-unparse -fopenmp -fopenmp-version=60 %s | FileCheck --ignore-case %s
+! RUN: %flang_fc1 -fdebug-dump-parse-tree -fopenmp -fopenmp-version=60 %s | FileCheck --check-prefix="PARSE-TREE" %s
+
+subroutine openmp_interchange(x)
+
+ integer :: x, y
+
+!CHECK: !$omp interchange
+!$omp interchange
+!CHECK: do
+ do x = 1, 100
+ !CHECK: do
+ do y = 1, 100
+ call F1()
+ !CHECK: end do
+ end do
+!CHECK: end do
+ end do
+!CHECK: !$omp end interchange
+!$omp end interchange
+
+!PARSE-TREE: OpenMPConstruct -> OpenMPLoopConstruct
+!PARSE-TREE: OmpBeginLoopDirective
+!PARSE-TREE: OmpDirectiveName -> llvm::omp::Directive = interchange
+!PARSE-TREE: DoConstruct
+!PARSE-TREE: EndDoStmt
+!PARSE-TREE: OmpEndLoopDirective
+!PARSE-TREE: OmpDirectiveName -> llvm::omp::Directive = interchange
+
+END subroutine openmp_interchange
diff --git a/flang/test/Semantics/OpenMP/interchange-permutation.f90 b/flang/test/Semantics/OpenMP/interchange-permutation.f90
new file mode 100644
index 0000000000000..f0309b159157d
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/interchange-permutation.f90
@@ -0,0 +1,108 @@
+! Testing the Semantics of interchange
+!RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=60
+
+
+subroutine double_permutation
+ implicit none
+ integer i, j
+
+ !ERROR: At most one PERMUTATION clause can appear on the INTERCHANGE directive
+ !$omp interchange permutation(2,1) permutation(2,1)
+ do i = 1, 5
+ do j = 1, 5
+ print *, i
+ end do
+ end do
+end subroutine
+
+subroutine zero_parameter
+ implicit none
+ integer i, j
+
+ !ERROR: The parameter of the PERMUTATION clause must be a constant positive integer expression
+ !$omp interchange permutation(0,1)
+ do i = 1, 5
+ do j = 1, 5
+ print *, i
+ end do
+ end do
+end subroutine
+
+
+subroutine negative_parameter
+ implicit none
+ integer i, j
+
+ !ERROR: The parameter of the PERMUTATION clause must be a constant positive integer expression
+ !$omp interchange permutation(2,-1)
+ do i = 1, 5
+ do j = 1, 5
+ print *, i
+ end do
+ end do
+end subroutine
+
+
+subroutine constant_parameter
+ implicit none
+ integer i, j, a
+
+ !ERROR: Must be a constant value
+ !$omp interchange permutation(2,a)
+ do i = 1, 5
+ do j = 1, 5
+ print *, i
+ end do
+ end do
+end subroutine
+
+subroutine insufficient_loops
+ implicit none
+ integer i
+
+ !ERROR: The PERMUTATION clause has more entries than there are nested canonical loops.
+ !$omp interchange permutation(2, 1)
+ do i = 1, 5
+ print *, i
+ end do
+end subroutine
+
+subroutine minimum_parameters
+ implicit none
+ integer i, j
+
+ !ERROR: The PERMUTATION clause must have a length of at least two
+ !$omp interchange permutation(1)
+ do i = 1, 5
+ do j = 1, 5
+ print *, i
+ end do
+ end do
+end subroutine
+
+subroutine parameter_number
+ implicit none
+ integer i, j
+
+ !ERROR: Every integer from 1 must appear in the PERMUTATION clause
+ !$omp interchange permutation(1,1)
+ do i = 1, 5
+ do j = 1, 5
+ print *, i
+ end do
+ end do
+end subroutine
+
+subroutine parameter_number2
+ implicit none
+ integer i, j
+
+ !ERROR: Every integer from 1 must appear in the PERMUTATION clause
+ !$omp interchange permutation(1,3)
+ do i = 1, 5
+ do j = 1, 5
+ print *, i
+ end do
+ end do
+end subroutine
+
diff --git a/flang/test/Semantics/OpenMP/interchange01.f90 b/flang/test/Semantics/OpenMP/interchange01.f90
new file mode 100644
index 0000000000000..1bcdcea17a1da
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/interchange01.f90
@@ -0,0 +1,43 @@
+! Testing the Semantics of interchange
+!RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=51
+
+
+subroutine on_unroll
+ implicit none
+ integer i, j
+
+ !ERROR: OpenMP loop construct cannot apply to a fully unrolled loop
+ !$omp interchange
+ !$omp unroll
+ do i = 1, 5
+ do j = 1, 5
+ print *, i
+ end do
+ end do
+end subroutine
+
+subroutine loop_assoc
+ implicit none
+ integer :: i, j
+
+ !$omp interchange
+ !ERROR: The associated loop of a loop-associated directive cannot be a DO WHILE.
+ do while (i <= 10)
+ do j = 1, 5
+ i = i + 1
+ print *, i
+ end do
+ end do
+end subroutine
+
+subroutine insufficient_loops
+ implicit none
+ integer i
+
+ !ERROR: The INTERCHANGE construct must be followed by a canonical loop nest of at least 2 levels
+ !$omp interchange
+ do i = 1, 5
+ print *, i
+ end do
+end subroutine
+
diff --git a/llvm/include/llvm/Frontend/OpenMP/OMP.td b/llvm/include/llvm/Frontend/OpenMP/OMP.td
index cb688959f7519..773804b262a24 100644
--- a/llvm/include/llvm/Frontend/OpenMP/OMP.td
+++ b/llvm/include/llvm/Frontend/OpenMP/OMP.td
@@ -453,7 +453,7 @@ def OMPC_Partial: Clause<[Spelling<"partial">]> {
}
def OMPC_Permutation: Clause<[Spelling<"permutation">]> {
let clangClass = "OMPPermutationClause";
- let flangClass = "ScalarIntExpr";
+ let flangClass = "ScalarIntConstantExpr";
let isValueList = true;
}
def OMPC_Priority : Clause<[Spelling<"priority">]> {
>From 8e576b28257c940eb9dc8c362c867185df49124b Mon Sep 17 00:00:00 2001
From: Ferran Toda <ferran.todacasaban at bsc.es>
Date: Mon, 16 Mar 2026 14:22:18 +0000
Subject: [PATCH 2/2] add Todo test
---
flang/test/Lower/OpenMP/Todo/interchange.f90 | 15 +++++++++++++++
1 file changed, 15 insertions(+)
create mode 100644 flang/test/Lower/OpenMP/Todo/interchange.f90
diff --git a/flang/test/Lower/OpenMP/Todo/interchange.f90 b/flang/test/Lower/OpenMP/Todo/interchange.f90
new file mode 100644
index 0000000000000..123b8fc657ed9
--- /dev/null
+++ b/flang/test/Lower/OpenMP/Todo/interchange.f90
@@ -0,0 +1,15 @@
+! Tests reduction processor behavior when a reduction symbol is not supported.
+
+! RUN: %not_todo_cmd %flang_fc1 -emit-hlfir -fopenmp -o - %s 2>&1 | FileCheck %s
+
+subroutine foo
+ implicit none
+ integer :: j, i
+
+ !CHECK: not yet implemented: OpenMP Interchange
+ !$omp interchange
+ do i=1,10
+ do j=1,10
+ end do
+ end do
+end subroutine
More information about the flang-commits
mailing list