[flang-commits] [flang] [flang][OpenMP] Support ALLOCATABLE list items in LINEAR (PR #228827)

via flang-commits flang-commits at lists.llvm.org
Sat Oct 3 22:26:11 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-flang-fir-hlfir

Author: Aditya Trivedi (adit4443ya)

<details>
<summary>Changes</summary>

Pass the allocatable's data address as the linear variable so it matches linear_var_types, and rebind the symbol in the loop body to a descriptor over that address so the body uses the private copy.

Fixes #<!-- -->182401

---
Full diff: https://github.com/llvm/llvm-project/pull/228827.diff


3 Files Affected:

- (modified) flang/lib/Lower/OpenMP/ClauseProcessor.cpp (+8-1) 
- (modified) flang/lib/Lower/OpenMP/OpenMP.cpp (+55) 
- (added) flang/test/Lower/OpenMP/linear-allocatable.f90 (+90) 


``````````diff
diff --git a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
index e41223b5b16c0..0b5b3e73010b3 100644
--- a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
+++ b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp
@@ -1804,7 +1804,14 @@ bool ClauseProcessor::processLinear(mlir::omp::LinearClauseOps &result,
 
     for (const omp::Object &object : objects) {
       semantics::Symbol *sym = object.sym();
-      const mlir::Value variable = converter.getSymbolAddress(*sym);
+      mlir::Value variable = converter.getSymbolAddress(*sym);
+      // Pass an allocatable's data address so it matches linear_var_types.
+      if (!isDeclareSimd && semantics::IsAllocatable(sym->GetUltimate())) {
+        fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
+        mlir::Location loc = converter.getCurrentLocation();
+        mlir::Value box = fir::LoadOp::create(firOpBuilder, loc, variable);
+        variable = fir::BoxAddrOp::create(firOpBuilder, loc, box);
+      }
       result.linearVars.push_back(variable);
       mlir::Type ty = converter.genType(*sym);
       typeAttrs.push_back(mlir::TypeAttr::get(ty));
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index 99a7882a658c8..23f0605f29318 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -3030,6 +3030,60 @@ genFlushOp(lower::AbstractConverter &converter, lower::SymMap &symTable,
                                     operandRange);
 }
 
+/// Rebind linear allocatables to an in-loop descriptor over the linear address.
+static void bindLinearAllocatables(
+    lower::AbstractConverter &converter, mlir::Location loc,
+    const ConstructQueue &queue,
+    llvm::ArrayRef<std::pair<mlir::omp::BlockArgOpenMPOpInterface,
+                             const ObjectEntryBlockArgs &>>
+        wrapperArgs) {
+  llvm::SmallVector<mlir::Value> linearVars;
+  for (auto [argGeneratingOp, blockArgs] : wrapperArgs) {
+    mlir::Operation *wrapperOp = argGeneratingOp.getOperation();
+    if (auto simdOp = mlir::dyn_cast<mlir::omp::SimdOp>(wrapperOp))
+      llvm::append_range(linearVars, simdOp.getLinearVars());
+    else if (auto wsloopOp = mlir::dyn_cast<mlir::omp::WsloopOp>(wrapperOp))
+      llvm::append_range(linearVars, wsloopOp.getLinearVars());
+  }
+  if (linearVars.empty())
+    return;
+
+  fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
+  llvm::SmallPtrSet<const semantics::Symbol *, 4> boundSyms;
+  for (const UnitConstruct &unit : queue) {
+    for (const Clause &clause : unit.clauses) {
+      const auto *linear = std::get_if<clause::Linear>(&clause.u);
+      if (!linear)
+        continue;
+      for (const Object &object : std::get<ObjectList>(linear->t)) {
+        const semantics::Symbol *sym = object.sym();
+        if (!semantics::IsAllocatable(sym->GetUltimate()) ||
+            !boundSyms.insert(sym).second)
+          continue;
+
+        mlir::Value descAddr = converter.getSymbolAddress(*sym);
+        auto *dataAddr = llvm::find_if(linearVars, [&](mlir::Value var) {
+          auto boxAddr = var.getDefiningOp<fir::BoxAddrOp>();
+          auto load =
+              boxAddr ? boxAddr.getVal().getDefiningOp<fir::LoadOp>() : nullptr;
+          return load && load.getMemref() == descAddr;
+        });
+        if (dataAddr == linearVars.end())
+          continue;
+
+        mlir::Type boxType = fir::unwrapRefType(descAddr.getType());
+        mlir::Value box =
+            fir::EmboxOp::create(firOpBuilder, loc, boxType, *dataAddr);
+        mlir::Value newDescAddr = firOpBuilder.createTemporary(loc, boxType);
+        fir::StoreOp::create(firOpBuilder, loc, box, newDescAddr);
+        converter.bindSymbol(
+            *sym, fir::MutableBoxValue(newDescAddr, /*lenParameters=*/{},
+                                       /*mutableProperties=*/{}));
+      }
+    }
+  }
+}
+
 static mlir::omp::LoopNestOp genLoopNestOp(
     lower::AbstractConverter &converter, lower::SymMap &symTable,
     semantics::SemanticsContext &semaCtx, lower::pft::Evaluation &eval,
@@ -3043,6 +3097,7 @@ static mlir::omp::LoopNestOp genLoopNestOp(
     llvm::function_ref<void(mlir::Operation *)> loopPostIvCb = nullptr) {
   auto ivCallback = [&](mlir::Operation *op) {
     genLoopVars(op, converter, loc, iv, wrapperArgs);
+    bindLinearAllocatables(converter, loc, queue, wrapperArgs);
     if (loopPostIvCb)
       loopPostIvCb(op);
     return llvm::SmallVector<const semantics::Symbol *>(iv);
diff --git a/flang/test/Lower/OpenMP/linear-allocatable.f90 b/flang/test/Lower/OpenMP/linear-allocatable.f90
new file mode 100644
index 0000000000000..c4ee243fce90c
--- /dev/null
+++ b/flang/test/Lower/OpenMP/linear-allocatable.f90
@@ -0,0 +1,90 @@
+! LINEAR on an allocatable: data address operand, rebuilt descriptor in body.
+
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -o - %s 2>&1 | FileCheck %s
+! RUN: bbc -emit-hlfir -fopenmp -o - %s 2>&1 | FileCheck %s
+
+! CHECK-LABEL: func.func @_QPsimd_linear_allocatable
+! CHECK:         %[[A:.*]]:2 = hlfir.declare %{{.*}} {fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QFsimd_linear_allocatableEa"}
+! CHECK:         %[[BOX:.*]] = fir.load %[[A]]#0 : !fir.ref<!fir.box<!fir.heap<i32>>>
+! CHECK:         %[[ADDR:.*]] = fir.box_addr %[[BOX]] : (!fir.box<!fir.heap<i32>>) -> !fir.heap<i32>
+! CHECK:         omp.simd linear(%[[ADDR]] : !fir.heap<i32> = %{{.*}} : i32
+! CHECK-SAME:    linear_var_types([i32
+! CHECK:           omp.loop_nest
+! CHECK:             %[[NEW_BOX:.*]] = fir.embox %[[ADDR]] : (!fir.heap<i32>) -> !fir.box<!fir.heap<i32>>
+! CHECK:             fir.store %[[NEW_BOX]] to %[[NEW_DESC:.*]] : !fir.ref<!fir.box<!fir.heap<i32>>>
+! CHECK:             %[[PRIV_A:.*]]:2 = hlfir.declare %[[NEW_DESC]] {fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QFsimd_linear_allocatableEa"}
+! CHECK:             hlfir.assign %{{.*}} to %[[PRIV_A]]#0 realloc : i32, !fir.ref<!fir.box<!fir.heap<i32>>>
+! CHECK:             omp.yield
+subroutine simd_linear_allocatable()
+  integer, allocatable :: a
+  integer :: i
+  allocate(a)
+  a = 0
+  !$omp simd linear(a)
+  do i = 1, 2
+    a = 2
+  end do
+  !$omp end simd
+end subroutine
+
+! CHECK-LABEL: func.func @_QPdo_linear_allocatable
+! CHECK:         %[[A:.*]]:2 = hlfir.declare %{{.*}} {fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QFdo_linear_allocatableEa"}
+! CHECK:         %[[BOX:.*]] = fir.load %[[A]]#0 : !fir.ref<!fir.box<!fir.heap<i32>>>
+! CHECK:         %[[ADDR:.*]] = fir.box_addr %[[BOX]] : (!fir.box<!fir.heap<i32>>) -> !fir.heap<i32>
+! CHECK:         omp.wsloop linear(%[[ADDR]] : !fir.heap<i32> = %{{.*}} : i32
+! CHECK:           omp.loop_nest
+! CHECK:             %[[NEW_BOX:.*]] = fir.embox %[[ADDR]] : (!fir.heap<i32>) -> !fir.box<!fir.heap<i32>>
+! CHECK:             fir.store %[[NEW_BOX]] to %[[NEW_DESC:.*]] : !fir.ref<!fir.box<!fir.heap<i32>>>
+! CHECK:             %[[PRIV_A:.*]]:2 = hlfir.declare %[[NEW_DESC]] {fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QFdo_linear_allocatableEa"}
+! CHECK:             hlfir.assign %{{.*}} to %[[PRIV_A]]#0 realloc : i32, !fir.ref<!fir.box<!fir.heap<i32>>>
+subroutine do_linear_allocatable()
+  integer, allocatable :: a
+  integer :: i
+  allocate(a)
+  a = 0
+  !$omp parallel do linear(a:2)
+  do i = 1, 10
+    a = a + 1
+  end do
+  !$omp end parallel do
+end subroutine
+
+! Composite: the linear operand moves to omp.wsloop.
+! CHECK-LABEL: func.func @_QPdo_simd_linear_allocatable
+! CHECK:         %[[ADDR:.*]] = fir.box_addr %{{.*}} : (!fir.box<!fir.heap<i64>>) -> !fir.heap<i64>
+! CHECK:         omp.wsloop linear(%[[ADDR]] : !fir.heap<i64> = %{{.*}} : i64
+! CHECK:           omp.simd
+! CHECK:             omp.loop_nest
+! CHECK:               %[[NEW_BOX:.*]] = fir.embox %[[ADDR]] : (!fir.heap<i64>) -> !fir.box<!fir.heap<i64>>
+! CHECK:               fir.store %[[NEW_BOX]] to %[[NEW_DESC:.*]] : !fir.ref<!fir.box<!fir.heap<i64>>>
+! CHECK:               hlfir.declare %[[NEW_DESC]] {fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QFdo_simd_linear_allocatableEa"}
+subroutine do_simd_linear_allocatable()
+  integer(8), allocatable :: a
+  integer :: i
+  allocate(a)
+  a = 0
+  !$omp do simd linear(a)
+  do i = 1, 10
+    a = a + 1
+  end do
+  !$omp end do simd
+end subroutine
+
+! The original descriptor is used after the loop.
+! CHECK-LABEL: func.func @_QPoriginal_used_after_loop
+! CHECK:         %[[A:.*]]:2 = hlfir.declare %{{.*}} {fortran_attrs = #fir.var_attrs<allocatable>, uniq_name = "_QForiginal_used_after_loopEa"}
+! CHECK:         omp.simd linear
+! CHECK:         }
+! CHECK:         hlfir.assign %{{.*}} to %[[A]]#0 realloc : i32, !fir.ref<!fir.box<!fir.heap<i32>>>
+subroutine original_used_after_loop()
+  integer, allocatable :: a
+  integer :: i
+  allocate(a)
+  a = 0
+  !$omp simd linear(a)
+  do i = 1, 2
+    a = 2
+  end do
+  !$omp end simd
+  a = 5
+end subroutine

``````````

</details>


https://github.com/llvm/llvm-project/pull/228827


More information about the flang-commits mailing list