[flang-commits] [flang] [flang][openacc] Add ACCHoistAllocatableRealloc pass (PR #206190)
via flang-commits
flang-commits at lists.llvm.org
Wed Jul 1 06:45:10 PDT 2026
================
@@ -0,0 +1,208 @@
+//===- ACCHoistAllocatableRealloc.cpp ------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Hoist the allocatable reallocation that SeparateAllocatableAssign leaves
+// inside an OpenACC kernels construct out to the host, so only the assignment
+// runs on the device. A device-side allocate/deallocate is undefined behavior
+// under the OpenACC specification.
+//
+//===----------------------------------------------------------------------===//
+
+#include "flang/Optimizer/Dialect/FIROps.h"
+#include "flang/Optimizer/Dialect/FIRType.h"
+#include "flang/Optimizer/OpenACC/Passes.h"
+#include "mlir/Dialect/OpenACC/OpenACC.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/ADT/SmallVector.h"
+
+namespace fir::acc {
+#define GEN_PASS_DEF_ACCHOISTALLOCATABLEREALLOC
+#include "flang/Optimizer/OpenACC/Passes.h.inc"
+} // namespace fir::acc
+
+using namespace mlir;
+
+namespace {
+
+/// True for an allocatable descriptor reference:
+/// !fir.ref<!fir.box<!fir.heap<>>>.
+static bool isAllocatableDescRef(Type t) {
+ auto ref = dyn_cast<fir::ReferenceType>(t);
+ if (!ref)
+ return false;
+ auto box = dyn_cast<fir::BaseBoxType>(ref.getEleTy());
+ if (!box)
+ return false;
+ return isa<fir::HeapType>(box.getEleTy());
+}
+
+/// True if `v` is defined outside `region` (and therefore dominates the op that
+/// owns the region).
+static bool definedOutside(Value v, Region ®ion) {
+ return !region.isAncestor(v.getParentRegion());
+}
+
+/// Return the ancestor of `op` that is a direct child of `region`'s entry
+/// block, or null if `op` is not nested within that entry block.
+static Operation *topLevelInEntry(Operation *op, Region ®ion) {
+ Block *entry = ®ion.front();
+ for (Operation *cur = op; cur; cur = cur->getParentOp()) {
+ if (cur->getBlock() == entry)
+ return cur;
+ }
+ return nullptr;
+}
+
+/// Collect into `slice` the entry-block ops of `region` that `root` depends on
+/// (its backward SSA slice, including values used by nested ops). Returns false
+/// if a dependency is a region-local block argument, i.e. it cannot be hoisted.
+static bool collectReallocSlice(Operation *root, Region ®ion,
+ llvm::SmallPtrSetImpl<Operation *> &slice) {
+ llvm::SmallVector<Operation *> worklist{root};
+ bool ok = true;
+ auto use = [&](Value v) {
+ if (definedOutside(v, region))
+ return;
+ if (Operation *def = v.getDefiningOp()) {
+ if (Operation *top = topLevelInEntry(def, region))
+ worklist.push_back(top);
+ else
+ ok = false;
+ } else {
+ ok = false; // region block argument
+ }
+ };
+ while (!worklist.empty()) {
+ Operation *op = worklist.pop_back_val();
+ if (!slice.insert(op).second)
+ continue;
+ for (Value v : op->getOperands())
+ use(v);
+ op->walk([&](Operation *nested) {
+ for (Value v : nested->getOperands())
+ use(v);
+ });
+ }
+ return ok;
+}
----------------
jeanPerier wrote:
Is this also collecting the if op?
If it is not, it should because the reallocation should remain conditional on the host because reallocation may not be needed and may break the semantic of the program (for example, if an allocatable is TARGET and has been captured in a pointer, reallocating it would make it dangling).
In general, that also means in my opinion that we should only do it when the allocatable assignment is known to be reached (it could be inside its control flow unrelated to the reallocation). What is happening in this case?
https://github.com/llvm/llvm-project/pull/206190
More information about the flang-commits
mailing list