[flang-commits] [flang] [flang][OpenMp] Fix LINEAR steps and ALLOCATABLE loop iteration variables (PR #234112)
Aditya Trivedi via flang-commits
flang-commits at lists.llvm.org
Wed Oct 21 01:47:15 PDT 2026
https://github.com/adit4443ya created https://github.com/llvm/llvm-project/pull/234112
fixes https://github.com/llvm/llvm-project/issues/233992 and
fixes https://github.com/llvm/llvm-project/issues/233990
>From 30dbb8d2a09a7b31c2f675b379f079eb8f1f1428 Mon Sep 17 00:00:00 2001
From: Aditya Trivedi <adit4443ya at gmail.com>
Date: Wed, 21 Oct 2026 14:08:52 +0530
Subject: [PATCH 1/2] [flang][OpenMP] Use one step per list item in LINEAR
processLinear added the step objects.size() times for every list item, so
items after a multi-item LINEAR clause were paired with the wrong step.
---
flang/lib/Lower/OpenMP/ClauseProcessor.cpp | 4 ++--
flang/test/Lower/OpenMP/simd-linear.f90 | 27 ++++++++++++++++++++++
2 files changed, 29 insertions(+), 2 deletions(-)
diff --git a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
index 34961d3d44ba2f..43fa51dfd98325 100644
--- a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
+++ b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
@@ -1787,7 +1787,7 @@ bool ClauseProcessor::processLinear(
clause.t)) {
mlir::Value operand =
fir::getBase(converter.genExprValue(toEvExpr(*mod), stmtCtx));
- result.linearStepVars.append(objects.size(), operand);
+ result.linearStepVars.push_back(operand);
} else {
// If nothing is present, add the default step of 1.
fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
@@ -1795,7 +1795,7 @@ bool ClauseProcessor::processLinear(
mlir::Type integerTy = ty.isInteger() ? ty : firOpBuilder.getI32Type();
mlir::Value operand =
firOpBuilder.createIntegerConstant(currentLocation, integerTy, 1);
- result.linearStepVars.append(objects.size(), operand);
+ result.linearStepVars.push_back(operand);
}
// Determine the linear modifier:
diff --git a/flang/test/Lower/OpenMP/simd-linear.f90 b/flang/test/Lower/OpenMP/simd-linear.f90
index ef2fcafff39bf3..8f30e404d76c35 100644
--- a/flang/test/Lower/OpenMP/simd-linear.f90
+++ b/flang/test/Lower/OpenMP/simd-linear.f90
@@ -105,3 +105,30 @@ subroutine non_i32_type
!CHECK: }
!IMPLICIT: }
end subroutine
+
+!CHECK-LABEL: func.func @_QPmultiple_items
+!CHECK: %[[A:.*]]:2 = hlfir.declare {{.*}} uniq_name("_QFmultiple_itemsEa")
+!CHECK: %[[B:.*]]:2 = hlfir.declare {{.*}} uniq_name("_QFmultiple_itemsEb")
+!CHECK: %[[C:.*]]:2 = hlfir.declare {{.*}} uniq_name("_QFmultiple_itemsEc")
+!CHECK: %[[TWO_A:.*]] = arith.constant 2 : i32
+!CHECK: %[[TWO_B:.*]] = arith.constant 2 : i32
+!CHECK: %[[THREE:.*]] = arith.constant 3 : i32
+!CHECK: omp.simd linear(val(@{{[^ ]+}} %[[A]]#0 -> %{{[^ ]+}} : !fir.ref<i32> = %[[TWO_A]] : i32), val(@{{[^ ]+}} %[[B]]#0 -> %{{[^ ]+}} : !fir.ref<i32> = %[[TWO_B]] : i32), val(@{{[^ ]+}} %[[C]]#0 -> %{{[^ ]+}} : !fir.ref<i32> = %[[THREE]] : i32)) linear_var_types([i32, i32, i32])
+
+!IMPLICIT-LABEL: func.func @_QPmultiple_items
+!IMPLICIT: %[[I:.*]]:2 = hlfir.declare {{.*}} uniq_name("_QFmultiple_itemsEi")
+!IMPLICIT: %[[TWO_A:.*]] = arith.constant 2 : i32
+!IMPLICIT: %[[TWO_B:.*]] = arith.constant 2 : i32
+!IMPLICIT: %[[THREE:.*]] = arith.constant 3 : i32
+!IMPLICIT: %{{.*}} = arith.constant 1 : i32
+!IMPLICIT: %{{.*}} = arith.constant 4 : i32
+!IMPLICIT: %[[STEP:.*]] = arith.constant 1 : i32
+!IMPLICIT: omp.simd linear({{.*}} = %[[TWO_A]] : i32, {{.*}} = %[[TWO_B]] : i32, {{.*}} = %[[THREE]] : i32, @{{[^ ]+}} %[[I]]#0 -> %{{[^ ]+}} : !fir.ref<i32> = %[[STEP]] : i32)
+subroutine multiple_items
+ implicit none
+ integer :: a, b, c, i
+ !$omp simd linear(a,b:2) linear(c:3)
+ do i = 1, 4
+ end do
+ !$omp end simd
+end subroutine
>From 267073a5db87ec1e86048c67b019e953fa156adc Mon Sep 17 00:00:00 2001
From: Aditya Trivedi <adit4443ya at gmail.com>
Date: Wed, 21 Oct 2026 14:08:52 +0530
Subject: [PATCH 2/2] [flang][OpenMP] Fix ALLOCATABLE loop iteration variables
Make an ALLOCATABLE SIMD loop variable implicitly linear, now that linear
privatizers support ALLOCATABLE, so it has the right value after the loop.
Size loop variables from the declared kind for ALLOCATABLE and POINTER
instead of the descriptor, which gave a spurious "narrowed into 64 bits"
warning and an i64 loop index.
---
flang/lib/Lower/OpenMP/OpenMP.cpp | 13 ++++--------
flang/lib/Lower/OpenMP/Utils.cpp | 16 +++++++++++++--
flang/lib/Lower/OpenMP/Utils.h | 2 ++
flang/test/Lower/OpenMP/lastprivate-iv.f90 | 13 ++++++------
.../test/Lower/OpenMP/linear-allocatable.f90 | 20 +++++++++++++++++++
.../Lower/OpenMP/loop-pointer-variable.f90 | 18 ++++++++---------
6 files changed, 54 insertions(+), 28 deletions(-)
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index abe1df79f6aef2..abb4c53b3b1229 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -1518,7 +1518,7 @@ genLoopVars(mlir::Operation *op, lower::AbstractConverter &converter,
std::size_t loopVarTypeSize = 0;
for (const semantics::Symbol *arg : args)
- loopVarTypeSize = std::max(loopVarTypeSize, arg->GetUltimate().size());
+ loopVarTypeSize = std::max(loopVarTypeSize, getLoopVarSize(*arg));
mlir::Type loopVarType = getLoopVarType(converter, loopVarTypeSize);
llvm::SmallVector<mlir::Type> tiv(args.size(), loopVarType);
llvm::SmallVector<mlir::Location> locs(args.size(), loc);
@@ -2464,14 +2464,9 @@ static void genSimdImplicitLinear(
variable) != clauseOps.linearVars.end())
continue;
- // TODO: Implicit linearization is skipped if iv is a pointer
- // or an allocatable, due to potential mismatch between the linear
- // variable type (example !fir.ref<!fir.box<!fir.heap<i32>>>)
- // and the linear step size (example: i64). Handle this type mismatch
- // gracefully.
+ // TODO: Implicit linearization is skipped if iv is a pointer.
if (loopVar->test(Fortran::semantics::Symbol::Flag::OmpLinear) &&
- !(Fortran::semantics::IsAllocatableOrPointer(*loopVar) ||
- Fortran::semantics::IsAllocatableOrPointer(loopVar->GetUltimate()))) {
+ !Fortran::semantics::IsPointer(loopVar->GetUltimate())) {
mlir::Type ty = converter.genType(*loopVar);
typeAttrs.push_back(mlir::TypeAttr::get(ty));
if (semaCtx.langOptions().getOpenMPVersion() >= 52)
@@ -3075,7 +3070,7 @@ static void genCanonicalLoopNest(
converter.genExprValue(*semantics::GetExpr(bounds->Upper()), stmtCtx));
// Get the integer kind for the loop variable and cast the loop bounds.
- size_t loopVarTypeSize = bounds->Name().thing.symbol->GetUltimate().size();
+ size_t loopVarTypeSize = getLoopVarSize(*bounds->Name().thing.symbol);
mlir::Type loopVarType = getLoopVarType(converter, loopVarTypeSize);
loopVarTypes.push_back(loopVarType);
mlir::Value loopStepVar = [&]() {
diff --git a/flang/lib/Lower/OpenMP/Utils.cpp b/flang/lib/Lower/OpenMP/Utils.cpp
index 9e37c02eac3f1f..87016d2022e536 100644
--- a/flang/lib/Lower/OpenMP/Utils.cpp
+++ b/flang/lib/Lower/OpenMP/Utils.cpp
@@ -145,6 +145,18 @@ mlir::Type getLoopVarType(lower::AbstractConverter &converter,
return converter.getFirOpBuilder().getIntegerType(loopVarTypeSize);
}
+std::size_t getLoopVarSize(const semantics::Symbol &sym) {
+ const semantics::Symbol &ultimate = sym.GetUltimate();
+ // The storage of an ALLOCATABLE or POINTER is its descriptor; use the kind.
+ if (semantics::IsAllocatableOrPointer(ultimate))
+ if (const semantics::DeclTypeSpec *type = ultimate.GetType())
+ if (const semantics::IntrinsicTypeSpec *intrinsic = type->AsIntrinsic())
+ if (std::optional<std::int64_t> kind =
+ evaluate::ToInt64(intrinsic->kind()))
+ return *kind;
+ return ultimate.size();
+}
+
semantics::Symbol *
getIterationVariableSymbol(const lower::pft::Evaluation &eval) {
return eval.visit(common::visitors{
@@ -897,8 +909,8 @@ void collectLoopRelatedInfo(
currentLocation, firOpBuilder.getIntegerType(32), 1));
}
iv.push_back(bounds->Name().thing.symbol);
- loopVarTypeSize = std::max(
- loopVarTypeSize, bounds->Name().thing.symbol->GetUltimate().size());
+ loopVarTypeSize =
+ std::max(loopVarTypeSize, getLoopVarSize(*bounds->Name().thing.symbol));
if (--collapseValue) {
doConstructEval = tryGetNestedDoConstruct(*doConstructEval);
// No DO construct left to descend into: COLLAPSE (or a tile SIZES list)
diff --git a/flang/lib/Lower/OpenMP/Utils.h b/flang/lib/Lower/OpenMP/Utils.h
index c769380aabbb01..16775f8facbc53 100644
--- a/flang/lib/Lower/OpenMP/Utils.h
+++ b/flang/lib/Lower/OpenMP/Utils.h
@@ -154,6 +154,8 @@ omp::ObjectList gatherObjectsOf(omp::Object derivedTypeMember,
mlir::Type getLoopVarType(lower::AbstractConverter &converter,
std::size_t loopVarTypeSize);
+std::size_t getLoopVarSize(const semantics::Symbol &sym);
+
semantics::Symbol *
getIterationVariableSymbol(const lower::pft::Evaluation &eval);
diff --git a/flang/test/Lower/OpenMP/lastprivate-iv.f90 b/flang/test/Lower/OpenMP/lastprivate-iv.f90
index f40cfbc587fb01..bc0b2a0ead4c2c 100644
--- a/flang/test/Lower/OpenMP/lastprivate-iv.f90
+++ b/flang/test/Lower/OpenMP/lastprivate-iv.f90
@@ -96,27 +96,26 @@ subroutine lastprivate_iv_i1
end subroutine
!CHECK: omp.wsloop private(@_QFlastprivate_iv_pointerEi_private_box_ptr_i32 %{{.*}}#0 -> %[[PRIVATE_IV:.*]] : !fir.ref<!fir.box<!fir.ptr<i32>>>) {
-!CHECK: omp.loop_nest (%[[LOOP_INDEX:.*]]) : i64
+!CHECK: omp.loop_nest (%[[LOOP_INDEX:.*]]) : i32
!CHECK: %[[PRIVATE_IV_DECL:.*]]:2 = hlfir.declare %[[PRIVATE_IV]] uniq_name("_QFlastprivate_iv_pointerEi") fortran_attrs<pointer> : (!fir.ref<!fir.box<!fir.ptr<i32>>>) -> (!fir.ref<!fir.box<!fir.ptr<i32>>>, !fir.ref<!fir.box<!fir.ptr<i32>>>)
-!CHECK: %[[LOOP_INDEX_INCR:.*]] = arith.addi %[[LOOP_INDEX]], %{{.*}} : i64
+!CHECK: %[[LOOP_INDEX_INCR:.*]] = arith.addi %[[LOOP_INDEX]], %{{.*}} : i32
!CHECK: fir.if %{{.*}} {
!CHECK: %[[PRIVATE_IV_BOX:.*]] = fir.load %[[PRIVATE_IV_DECL]]#0 : !fir.ref<!fir.box<!fir.ptr<i32>>>
!CHECK: %[[PRIVATE_IV_ADDR:.*]] = fir.box_addr %[[PRIVATE_IV_BOX]] : (!fir.box<!fir.ptr<i32>>) -> !fir.ptr<i32>
-!CHECK: hlfir.assign %[[LOOP_INDEX_INCR]] to %[[PRIVATE_IV_ADDR]] : i64, !fir.ptr<i32>
+!CHECK: hlfir.assign %[[LOOP_INDEX_INCR]] to %[[PRIVATE_IV_ADDR]] : i32, !fir.ptr<i32>
!CHECK: }
!CHECK: omp.yield
!CHECK: }
!CHECK: }
!CHECK-FIR: omp.wsloop private(@_QFlastprivate_iv_pointerEi_private_box_ptr_i32 %{{.*}} -> %[[PRIVATE_IV:.*]] : !fir.ref<!fir.box<!fir.ptr<i32>>>) {
-!CHECK-FIR: omp.loop_nest (%[[LOOP_INDEX:.*]]) : i64
+!CHECK-FIR: omp.loop_nest (%[[LOOP_INDEX:.*]]) : i32
!CHECK-FIR: %[[PRIVATE_IV_DECL:.*]] = fir.declare %[[PRIVATE_IV]] uniq_name("_QFlastprivate_iv_pointerEi") fortran_attrs<pointer> : (!fir.ref<!fir.box<!fir.ptr<i32>>>) -> !fir.ref<!fir.box<!fir.ptr<i32>>>
-!CHECK-FIR: %[[LOOP_INDEX_INCR:.*]] = arith.addi %[[LOOP_INDEX]], %{{.*}} : i64
+!CHECK-FIR: %[[LOOP_INDEX_INCR:.*]] = arith.addi %[[LOOP_INDEX]], %{{.*}} : i32
!CHECK-FIR: fir.if %{{.*}} {
!CHECK-FIR: %[[PRIVATE_IV_BOX:.*]] = fir.load %[[PRIVATE_IV_DECL]] : !fir.ref<!fir.box<!fir.ptr<i32>>>
!CHECK-FIR: %[[PRIVATE_IV_ADDR:.*]] = fir.box_addr %[[PRIVATE_IV_BOX]] : (!fir.box<!fir.ptr<i32>>) -> !fir.ptr<i32>
-!CHECK-FIR: %[[LOOP_INDEX_CVT:.*]] = fir.convert %[[LOOP_INDEX_INCR]] : (i64) -> i32
-!CHECK-FIR: fir.store %[[LOOP_INDEX_CVT]] to %[[PRIVATE_IV_ADDR]] : !fir.ptr<i32>
+!CHECK-FIR: fir.store %[[LOOP_INDEX_INCR]] to %[[PRIVATE_IV_ADDR]] : !fir.ptr<i32>
!CHECK-FIR: }
!CHECK-FIR: omp.yield
!CHECK-FIR: }
diff --git a/flang/test/Lower/OpenMP/linear-allocatable.f90 b/flang/test/Lower/OpenMP/linear-allocatable.f90
index b6b62f5cf6872e..8a9df5b6ab9002 100644
--- a/flang/test/Lower/OpenMP/linear-allocatable.f90
+++ b/flang/test/Lower/OpenMP/linear-allocatable.f90
@@ -2,6 +2,9 @@
! RUN: %flang_fc1 -emit-hlfir -fopenmp -o - %s 2>&1 | FileCheck %s
! RUN: bbc -emit-hlfir -fopenmp -o - %s 2>&1 | FileCheck %s
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -o /dev/null %s 2>&1 | FileCheck %s --check-prefix=WARN --allow-empty
+
+! WARN-NOT: warning
! CHECK-LABEL: omp.private {type = linear} @_QFsimd_linear_allocatableEa_linear_box_heap_i32 : !fir.box<!fir.heap<i32>> init {
! CHECK: fir.allocmem i32
@@ -111,3 +114,20 @@ subroutine original_used_after_loop()
!$omp end simd
a = 5
end subroutine
+
+! An allocatable loop iteration variable is implicitly linear.
+! CHECK-LABEL: func.func @_QPimplicit_allocatable_iv
+! CHECK: %[[I:.*]]:2 = hlfir.declare %{{.*}} uniq_name("_QFimplicit_allocatable_ivEi") fortran_attrs<allocatable>
+! CHECK: omp.simd linear(@_QFimplicit_allocatable_ivEi_linear_box_heap_i32 %[[I]]#0 -> %{{.*}} : !fir.ref<!fir.box<!fir.heap<i32>>> = %{{.*}} : i32) linear_var_types([i32])
+! CHECK: omp.loop_nest (%{{.*}}) : i32 =
+subroutine implicit_allocatable_iv()
+ integer, allocatable :: i
+ integer :: k
+ allocate(i)
+ k = 0
+ !$omp simd
+ do i = 1, 4
+ k = k + 1
+ end do
+ !$omp end simd
+end subroutine
diff --git a/flang/test/Lower/OpenMP/loop-pointer-variable.f90 b/flang/test/Lower/OpenMP/loop-pointer-variable.f90
index 0738e849cc7a7f..75c26fe6107999 100644
--- a/flang/test/Lower/OpenMP/loop-pointer-variable.f90
+++ b/flang/test/Lower/OpenMP/loop-pointer-variable.f90
@@ -10,17 +10,15 @@ program loop_var
!CHECK: %[[IA1:.*]]:2 = hlfir.declare %{{.*}} uniq_name("_QFEia1") fortran_attrs<allocatable>
!CHECK: omp.wsloop private(@_QFEip1_private_box_ptr_i32 %{{.*}}#0 -> %[[IP1_PVT:.*]], @_QFEip2_private_box_ptr_i32 %{{.*}}#0 -> %[[IP2_PVT:.*]] : !fir.ref<!fir.box<!fir.ptr<i32>>>, !fir.ref<!fir.box<!fir.ptr<i32>>>)
-!CHECK: omp.loop_nest (%[[IP1_INDX:.*]], %[[IP2_INDX:.*]]) : i64 = ({{.*}}) to ({{.*}}) inclusive step ({{.*}})
+!CHECK: omp.loop_nest (%[[IP1_INDX:.*]], %[[IP2_INDX:.*]]) : i32 = ({{.*}}) to ({{.*}}) inclusive step ({{.*}})
!CHECK: %[[IP1_PVT_DECL:.*]]:2 = hlfir.declare %[[IP1_PVT]] uniq_name("_QFEip1") fortran_attrs<pointer> : (!fir.ref<!fir.box<!fir.ptr<i32>>>) -> (!fir.ref<!fir.box<!fir.ptr<i32>>>, !fir.ref<!fir.box<!fir.ptr<i32>>>)
!CHECK: %[[IP2_PVT_DECL:.*]]:2 = hlfir.declare %[[IP2_PVT]] uniq_name("_QFEip2") fortran_attrs<pointer> : (!fir.ref<!fir.box<!fir.ptr<i32>>>) -> (!fir.ref<!fir.box<!fir.ptr<i32>>>, !fir.ref<!fir.box<!fir.ptr<i32>>>)
-!CHECK: %[[IP1:.*]] = fir.convert %[[IP1_INDX]] : (i64) -> i32
!CHECK: %[[IP1_BOX:.*]] = fir.load %[[IP1_PVT_DECL]]#0 : !fir.ref<!fir.box<!fir.ptr<i32>>>
!CHECK: %[[IP1_ADDR:.*]] = fir.box_addr %[[IP1_BOX]] : (!fir.box<!fir.ptr<i32>>) -> !fir.ptr<i32>
-!CHECK: hlfir.assign %[[IP1]] to %[[IP1_ADDR]] : i32, !fir.ptr<i32>
-!CHECK: %[[IP2:.*]] = fir.convert %[[IP2_INDX]] : (i64) -> i32
+!CHECK: hlfir.assign %[[IP1_INDX]] to %[[IP1_ADDR]] : i32, !fir.ptr<i32>
!CHECK: %[[IP2_BOX:.*]] = fir.load %[[IP2_PVT_DECL]]#0 : !fir.ref<!fir.box<!fir.ptr<i32>>>
!CHECK: %[[IP2_ADDR:.*]] = fir.box_addr %[[IP2_BOX]] : (!fir.box<!fir.ptr<i32>>) -> !fir.ptr<i32>
-!CHECK: hlfir.assign %[[IP2]] to %[[IP2_ADDR]] : i32, !fir.ptr<i32>
+!CHECK: hlfir.assign %[[IP2_INDX]] to %[[IP2_ADDR]] : i32, !fir.ptr<i32>
!CHECK: omp.yield
!$omp do collapse(2)
do ip1 = 1, 10
@@ -29,12 +27,12 @@ program loop_var
end do
!$omp end do
-!CHECK: omp.simd
-!CHECK: omp.loop_nest (%[[IA1_INDX:.*]]) : i64 = ({{.*}}) to ({{.*}}) inclusive step ({{.*}})
-!CHECK: %[[IA1_INDX_I32:.*]] = fir.convert %[[IA1_INDX]] : (i64) -> i32
-!CHECK: %[[IA1_BOX:.*]] = fir.load %[[IA1]]#0 : !fir.ref<!fir.box<!fir.heap<i32>>>
+!CHECK: omp.simd linear(@_QFEia1_linear_box_heap_i32 %[[IA1]]#0 -> %[[IA1_PVT:.*]] : !fir.ref<!fir.box<!fir.heap<i32>>> = %{{.*}} : i32) linear_var_types([i32])
+!CHECK: omp.loop_nest (%[[IA1_INDX:.*]]) : i32 = ({{.*}}) to ({{.*}}) inclusive step ({{.*}})
+!CHECK: %[[IA1_PVT_DECL:.*]]:2 = hlfir.declare %[[IA1_PVT]] uniq_name("_QFEia1") fortran_attrs<allocatable>
+!CHECK: %[[IA1_BOX:.*]] = fir.load %[[IA1_PVT_DECL]]#0 : !fir.ref<!fir.box<!fir.heap<i32>>>
!CHECK: %[[IA1_ADDR:.*]] = fir.box_addr %[[IA1_BOX]] : (!fir.box<!fir.heap<i32>>) -> !fir.heap<i32>
-!CHECK: hlfir.assign %[[IA1_INDX_I32]] to %[[IA1_ADDR]] : i32, !fir.heap<i32>
+!CHECK: hlfir.assign %[[IA1_INDX]] to %[[IA1_ADDR]] : i32, !fir.heap<i32>
!CHECK: omp.yield
!$omp simd
do ia1 = 1, 10
More information about the flang-commits
mailing list