[llvm-branch-commits] [flang] [flang] Add policy-driven allocation-placement pass - memory passes unification [2/5] (PR #210742)

Tom Eccles via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Tue Jul 21 07:53:23 PDT 2026


================
@@ -0,0 +1,324 @@
+//===- AllocationPlacement.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
+//
+//===----------------------------------------------------------------------===//
+//
+// This pass decides, for each array allocation in a function, whether it should
+// live on the stack (fir.alloca) or on the heap (fir.allocmem), and rewrites it
+// accordingly. The decision is delegated to the policy in
+// AllocationPlacementPolicy.h. Two rewrite engines are reused:
+//   - stack-to-heap uses fir::replaceAllocas (MemoryUtils);
+//   - heap-to-stack reuses the StackArrays analysis and rewrite, which only
+//     stackifies fir.allocmem that are provably freed on all paths.
+//
+//===----------------------------------------------------------------------===//
+
+#include "StackArrays.h"
+#include "flang/Optimizer/Dialect/FIRDialect.h"
+#include "flang/Optimizer/Dialect/FIROps.h"
+#include "flang/Optimizer/Dialect/FIROpsSupport.h"
+#include "flang/Optimizer/Dialect/FIRType.h"
+#include "flang/Optimizer/Dialect/Support/FIRContext.h"
+#include "flang/Optimizer/Support/DataLayout.h"
+#include "flang/Optimizer/Transforms/AllocationPlacementPolicy.h"
+#include "flang/Optimizer/Transforms/MemoryUtils.h"
+#include "flang/Optimizer/Transforms/Passes.h"
+#include "mlir/Dialect/Arith/IR/Arith.h"
+#include "mlir/Dialect/DLTI/DLTI.h"
+#include "mlir/Dialect/Func/IR/FuncOps.h"
+#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
+#include "mlir/IR/Diagnostics.h"
+#include "mlir/Pass/Pass.h"
+#include "mlir/Transforms/GreedyPatternRewriteDriver.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+#include <optional>
+
+namespace fir {
+#define GEN_PASS_DEF_ALLOCATIONPLACEMENT
+#include "flang/Optimizer/Transforms/Passes.h.inc"
+} // namespace fir
+
+#define DEBUG_TYPE "allocation-placement"
+
+//===----------------------------------------------------------------------===//
+// Default placement policy
+//===----------------------------------------------------------------------===//
+
+fir::AllocationPlacement
+fir::decideAllocationPlacement(const AllocationInfo &info,
+                               const AllocationPlacementThresholds &thresholds,
+                               std::size_t stackBytesUsed) {
+  using P = fir::AllocationPlacement;
+
+  // Translate a "should this be on the stack" decision into a placement,
+  // accounting for where the allocation currently lives.
+  auto place = [&](bool wantStack) -> P {
+    if (wantStack)
+      return info.isCurrentlyOnStack ? P::Leave : P::Stack;
+    return info.isCurrentlyOnStack ? P::Heap : P::Leave;
+  };
+
+  // -fstack-arrays: put everything on the stack (best effort). The
+  // heap-to-stack conversion still only happens where it is provably safe.
+  if (thresholds.stackArrays)
+    return place(/*wantStack=*/true);
+
+  // Runtime-sized arrays (automatic arrays, dynamic temporaries) go on the
+  // heap.
+  if (info.isDynamic)
+    return place(/*wantStack=*/false);
+
+  // Without a known constant size we cannot reason about thresholds.
+  if (!info.byteSize)
+    return P::Leave;
+
+  // Constant-size user variables always go on the stack.
+  if (!info.isTemporary)
+    return place(/*wantStack=*/true);
+
+  auto size = static_cast<std::size_t>(*info.byteSize);
+  if (size <= thresholds.smallArrayThresholdBytes)
+    // Small arrays go on the stack while the per-function budget allows it.
+    return place(/*wantStack=*/stackBytesUsed + size <=
+                 thresholds.totalStackLimitBytes);
+
+  // Big array temporaries go on the heap.
+  return place(/*wantStack=*/false);
+}
+
+namespace {
+
+/// Return true if the allocation is a compiler temporary, i.e. it has no
+/// uniqued name (user variables always carry one).
+static bool isTemporaryAllocation(mlir::Operation *op) {
----------------
tblah wrote:

what about checking for `fir.must_be_heap` on allocmem operations?

Or if we don't need this at all perhaps we should delete that attribute.

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


More information about the llvm-branch-commits mailing list