[flang-commits] [flang] [flang] Enable allocation-placement pass by default - memory passes unification [4/5] (PR #210766)
via flang-commits
flang-commits at lists.llvm.org
Wed Jul 29 04:31:16 PDT 2026
https://github.com/jeanPerier updated https://github.com/llvm/llvm-project/pull/210766
>From 26e3b7c257dae5582cc7732d1521b44510a7cf1f Mon Sep 17 00:00:00 2001
From: Jean Perier <jperier at nvidia.com>
Date: Mon, 20 Jul 2026 10:22:45 -0700
Subject: [PATCH] [flang] Enable allocation-placement pass by default
Make the unified allocation-placement pass the default in the FIR optimizer
pipeline in place of the legacy stack-arrays and memory-allocation-opt passes,
by defaulting -enable-allocation-placement to true. Passing
-enable-allocation-placement=false restores the legacy passes for comparison.
Add a -disable-allocation-placement switch that skips the pass entirely (wired
through addAllocationPlacement like the other optimizer passes), so codegen
tests can opt out of placement policy independently of the legacy fallback.
Update the pipeline-dump tests to expect AllocationPlacement, and disable the
pass in the alloca/allocmem codegen tests (alloc.fir, coordinateof.fir) so they
keep testing lowering rather than placement policy. Document the unified pass,
its policy, thresholds, and options in fstack-arrays.md.
---
flang/docs/fstack-arrays.md | 64 +++++++++++++++++++
.../flang/Optimizer/Passes/CommandLineOpts.h | 8 ++-
.../lib/Optimizer/Passes/CommandLineOpts.cpp | 13 +++-
flang/lib/Optimizer/Passes/Pipelines.cpp | 13 ++--
flang/test/Driver/bbc-mlir-pass-pipeline.f90 | 2 +-
.../test/Driver/mlir-debug-pass-pipeline.f90 | 2 +-
flang/test/Driver/mlir-pass-pipeline.f90 | 2 +-
flang/test/Fir/alloc.fir | 6 +-
.../Fir/allocation-placement-pipeline.fir | 12 ++--
flang/test/Fir/basic-program.fir | 2 +-
flang/test/Fir/coordinateof.fir | 3 +-
11 files changed, 104 insertions(+), 23 deletions(-)
diff --git a/flang/docs/fstack-arrays.md b/flang/docs/fstack-arrays.md
index 8eaa5d3f70e67..038d361c2a465 100644
--- a/flang/docs/fstack-arrays.md
+++ b/flang/docs/fstack-arrays.md
@@ -1,4 +1,13 @@
# Stack arrays pass
+
+> **Note**
+> The heap-to-stack (`stack-arrays`) and stack-to-heap (`memory-allocation-opt`)
+> transformations have been unified into a single policy-driven pass,
+> `allocation-placement`, which is now the default in the FIR optimizer
+> pipeline. The [Unified allocation-placement pass](#unified-allocation-placement-pass)
+> section below describes the current behavior and options; the rest of this
+> document describes the heap-to-stack analysis that the unified pass reuses.
+
## Problem Description
In gfortran, `-fstack-arrays` will cause all local arrays, including those of
unknown size, to be allocated from stack memory. Gfortran enables this flag by
@@ -161,3 +170,58 @@ heap allocated array temporaries are detected and converted by the new pass.
Another test will check that `allocate` statements in source code will not be
moved to the stack.
+
+## Unified allocation-placement pass
+The heap-to-stack transformation described above and the stack-to-heap
+transformation previously performed by the `memory-allocation-opt` pass have been
+merged into a single pass, `allocation-placement`. Instead of running one of two
+opposing passes depending on the value of `-fstack-arrays`, the pipeline always
+runs `allocation-placement`, which decides, per array allocation, whether it
+should live on the stack (`fir.alloca`) or on the heap (`fir.allocmem`) and
+rewrites it accordingly. Heap-to-stack rewrites still reuse the analysis and
+rewrite pattern described above, so they are only performed where the allocation
+is provably freed on all paths through the function.
+
+### Placement policy
+The decision is made by a policy function taking a few facts about each
+allocation (whether it currently lives on the stack, whether it is a compiler
+temporary or a user variable, whether it has a runtime-determined size, and its
+constant byte size when known) together with a set of tunable thresholds:
+
+- `small-array-threshold` (default 64 bytes): constant-size arrays up to this
+ size are considered "small".
+- `total-stack-limit` (default 4 MiB): per-function budget for small arrays
+ placed on the stack.
+
+With `-fno-stack-arrays` (the default), the policy is:
+
+- Runtime-sized arrays (automatic arrays, dynamic temporaries) are placed on the
+ heap.
+- Small constant-size arrays (user variables and temporaries) are placed on the
+ stack while the per-function stack budget allows it.
+- Large constant-size arrays are placed on the stack for user variables and on
+ the heap for compiler temporaries.
+
+With `-fstack-arrays`, the policy places every array on the stack on a
+best-effort basis (heap-to-stack rewrites still only happen where they are
+provably safe).
+
+A compiler temporary is distinguished from a user variable by the absence of a
+uniqued name (`uniqName`) on the allocation: user variables always carry one.
+
+### Customizing the policy
+The default policy can be fully overridden through a hook with the same signature
+as the default decision function. A hook may, for example, apply different
+thresholds inside device routines or parallel regions, and then delegate back to
+the default decision function. This keeps the pass generic while allowing
+target- or region-specific placement strategies to be layered on top.
+
+### Options
+- `-fstack-arrays` / `-fno-stack-arrays`: select the stack-arrays strategy or the
+ default size/kind-based policy.
+- `-allocation-placement-small-array-size` / `-allocation-placement-stack-limit`:
+ tune the "small array" byte-size threshold and the per-function stack budget.
+- `-disable-allocation-placement`: skip the pass entirely (no stack/heap
+ rewriting).
+- `-enable-allocation-placement=false`: fall back to the legacy `stack-arrays`
+ and `memory-allocation-opt` passes for A/B comparison.
diff --git a/flang/include/flang/Optimizer/Passes/CommandLineOpts.h b/flang/include/flang/Optimizer/Passes/CommandLineOpts.h
index d42ce8b762e63..30397b77d1c4b 100644
--- a/flang/include/flang/Optimizer/Passes/CommandLineOpts.h
+++ b/flang/include/flang/Optimizer/Passes/CommandLineOpts.h
@@ -57,10 +57,14 @@ extern llvm::cl::opt<bool> disableFirMao;
extern llvm::cl::opt<bool> enableFirLICM;
extern llvm::cl::opt<bool> useOldAliasTags;
-/// Experimental option to replace the stack-arrays and memory-allocation-opt
-/// passes with the unified allocation-placement pass.
+/// Use the unified allocation-placement pass instead of the legacy stack-arrays
+/// and memory-allocation-opt passes. Enabled by default; set to false to fall
+/// back to the legacy passes.
extern llvm::cl::opt<bool> enableAllocationPlacement;
+/// Skip the allocation-placement pass entirely (no stack/heap rewriting).
+extern llvm::cl::opt<bool> disableAllocationPlacement;
+
/// Constant-size arrays up to this many bytes are considered "small" and placed
/// on the stack by the allocation-placement pass.
extern llvm::cl::opt<std::size_t> allocationPlacementSmallArraySize;
diff --git a/flang/lib/Optimizer/Passes/CommandLineOpts.cpp b/flang/lib/Optimizer/Passes/CommandLineOpts.cpp
index d2af7dd5ecd64..2a78391a1780f 100644
--- a/flang/lib/Optimizer/Passes/CommandLineOpts.cpp
+++ b/flang/lib/Optimizer/Passes/CommandLineOpts.cpp
@@ -61,9 +61,16 @@ cl::opt<bool> useOldAliasTags(
"the FIR alias tags pass"),
cl::init(false), cl::Hidden);
EnableOption(FirLICM, "fir-licm", "FIR loop invariant code motion");
-EnableOption(AllocationPlacement, "allocation-placement",
- "unified array allocation placement (experimental; replaces "
- "stack-arrays and memory-allocation-opt)");
+// Enabled by default: the unified allocation-placement pass supersedes the
+// legacy stack-arrays and memory-allocation-opt passes. Pass
+// -enable-allocation-placement=false to fall back to the legacy passes.
+cl::opt<bool> enableAllocationPlacement(
+ "enable-allocation-placement",
+ cl::desc("use the unified array allocation-placement pass instead of the "
+ "legacy stack-arrays and memory-allocation-opt passes"),
+ cl::init(true), cl::Hidden);
+DisableOption(AllocationPlacement, "allocation-placement",
+ "unified array allocation placement");
cl::opt<std::size_t> allocationPlacementSmallArraySize(
"allocation-placement-small-array-size",
diff --git a/flang/lib/Optimizer/Passes/Pipelines.cpp b/flang/lib/Optimizer/Passes/Pipelines.cpp
index f40d99aa5a66c..d0b78040a77ab 100644
--- a/flang/lib/Optimizer/Passes/Pipelines.cpp
+++ b/flang/lib/Optimizer/Passes/Pipelines.cpp
@@ -74,11 +74,14 @@ void addMemoryAllocationOpt(mlir::PassManager &pm) {
}
void addAllocationPlacement(mlir::PassManager &pm, bool stackArrays) {
- fir::AllocationPlacementOptions options;
- options.stackArrays = stackArrays;
- options.smallArrayThresholdBytes = allocationPlacementSmallArraySize;
- options.totalStackLimitBytes = allocationPlacementStackLimit;
- pm.addPass(fir::createAllocationPlacement(options));
+ addNestedPassConditionally<mlir::func::FuncOp>(
+ pm, disableAllocationPlacement, [&]() {
+ fir::AllocationPlacementOptions options;
+ options.stackArrays = stackArrays;
+ options.smallArrayThresholdBytes = allocationPlacementSmallArraySize;
+ options.totalStackLimitBytes = allocationPlacementStackLimit;
+ return fir::createAllocationPlacement(options);
+ });
}
void addCodeGenRewritePass(mlir::PassManager &pm, bool preserveDeclare) {
diff --git a/flang/test/Driver/bbc-mlir-pass-pipeline.f90 b/flang/test/Driver/bbc-mlir-pass-pipeline.f90
index 4c98c22b90e33..808fa98c69e8c 100644
--- a/flang/test/Driver/bbc-mlir-pass-pipeline.f90
+++ b/flang/test/Driver/bbc-mlir-pass-pipeline.f90
@@ -38,7 +38,7 @@
! CHECK-NEXT: (S) 0 num-dce'd - Number of operations DCE'd
! CHECK-NEXT: 'func.func' Pipeline
-! CHECK-NEXT: MemoryAllocationOpt
+! CHECK-NEXT: AllocationPlacement
! CHECK-NEXT: Inliner
! CHECK-NEXT: SimplifyRegionLite
diff --git a/flang/test/Driver/mlir-debug-pass-pipeline.f90 b/flang/test/Driver/mlir-debug-pass-pipeline.f90
index 93c227ebfde74..99ad3436c52d5 100644
--- a/flang/test/Driver/mlir-debug-pass-pipeline.f90
+++ b/flang/test/Driver/mlir-debug-pass-pipeline.f90
@@ -75,7 +75,7 @@
! ALL-NEXT: (S) 0 num-dce'd - Number of operations DCE'd
! ALL-NEXT: 'func.func' Pipeline
-! ALL-NEXT: MemoryAllocationOpt
+! ALL-NEXT: AllocationPlacement
! ALL-NEXT: Inliner
! ALL-NEXT: SimplifyRegionLite
diff --git a/flang/test/Driver/mlir-pass-pipeline.f90 b/flang/test/Driver/mlir-pass-pipeline.f90
index dadf3ce28c66f..c98aa99e9527d 100644
--- a/flang/test/Driver/mlir-pass-pipeline.f90
+++ b/flang/test/Driver/mlir-pass-pipeline.f90
@@ -127,7 +127,7 @@
! ALL-NEXT: (S) 0 num-dce'd - Number of operations DCE'd
! ALL-NEXT: 'func.func' Pipeline
-! ALL-NEXT: MemoryAllocationOpt
+! ALL-NEXT: AllocationPlacement
! ALL-NEXT: Inliner
! ALL-NEXT: SimplifyRegionLite
diff --git a/flang/test/Fir/alloc.fir b/flang/test/Fir/alloc.fir
index 613c8e274baad..900c7435a2f8a 100644
--- a/flang/test/Fir/alloc.fir
+++ b/flang/test/Fir/alloc.fir
@@ -1,5 +1,7 @@
-// RUN: tco %s | FileCheck %s
-// RUN: %flang_fc1 -emit-llvm %s -o - | FileCheck %s
+// This test checks FIR alloca/allocmem lowering to LLVM; disable the
+// allocation-placement pass so it does not move allocations by policy.
+// RUN: tco -disable-allocation-placement %s | FileCheck %s
+// RUN: %flang_fc1 -mllvm -disable-allocation-placement -emit-llvm %s -o - | FileCheck %s
// UNSUPPORTED: system-windows
// Disabled on 32-bit targets due to the additional `trunc` opcodes required
diff --git a/flang/test/Fir/allocation-placement-pipeline.fir b/flang/test/Fir/allocation-placement-pipeline.fir
index e8aa904b2d40d..30fbd7a8d9dd2 100644
--- a/flang/test/Fir/allocation-placement-pipeline.fir
+++ b/flang/test/Fir/allocation-placement-pipeline.fir
@@ -1,10 +1,10 @@
-// Test that the experimental -enable-allocation-placement flag replaces the
-// legacy stack-arrays / memory-allocation-opt passes with the unified
-// allocation-placement pass in the default optimizer pipeline. By default the
-// legacy passes are still used.
+// Test that the default optimizer pipeline uses the unified
+// allocation-placement pass in place of the legacy stack-arrays /
+// memory-allocation-opt passes, and that -enable-allocation-placement=false
+// restores the legacy passes.
-// RUN: tco -enable-allocation-placement %s --mlir-pass-statistics --mlir-pass-statistics-display=pipeline 2>&1 | FileCheck %s --check-prefix=NEW
-// RUN: tco %s --mlir-pass-statistics --mlir-pass-statistics-display=pipeline 2>&1 | FileCheck %s --check-prefix=OLD
+// RUN: tco %s --mlir-pass-statistics --mlir-pass-statistics-display=pipeline 2>&1 | FileCheck %s --check-prefix=NEW
+// RUN: tco -enable-allocation-placement=false %s --mlir-pass-statistics --mlir-pass-statistics-display=pipeline 2>&1 | FileCheck %s --check-prefix=OLD
// REQUIRES: asserts
diff --git a/flang/test/Fir/basic-program.fir b/flang/test/Fir/basic-program.fir
index d1e7cdb44d0c7..9784136b9485e 100644
--- a/flang/test/Fir/basic-program.fir
+++ b/flang/test/Fir/basic-program.fir
@@ -110,7 +110,7 @@ func.func @_QQmain() {
// PASSES-NEXT: (S) 0 num-dce'd - Number of operations DCE'd
// PASSES-NEXT: 'func.func' Pipeline
-// PASSES-NEXT: MemoryAllocationOpt
+// PASSES-NEXT: AllocationPlacement
// PASSES-NEXT: Inliner
// PASSES-NEXT: SimplifyRegionLite
diff --git a/flang/test/Fir/coordinateof.fir b/flang/test/Fir/coordinateof.fir
index a01e9e9d1fc40..7f3635803cb0a 100644
--- a/flang/test/Fir/coordinateof.fir
+++ b/flang/test/Fir/coordinateof.fir
@@ -1,4 +1,5 @@
-// RUN: fir-opt %s | tco | FileCheck %s
+// Disable allocation-placement so the tested allocas are not moved by policy.
+// RUN: fir-opt %s | tco -disable-allocation-placement | FileCheck %s
// tests on coordinate_of op
More information about the flang-commits
mailing list