[llvm-branch-commits] [flang] [flang] Enable allocation-placement pass by default - memory passes unification [4/5] (PR #210766)
via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Mon Jul 27 07:28:28 PDT 2026
https://github.com/jeanPerier updated https://github.com/llvm/llvm-project/pull/210766
>From d3058ca3af59e6a665f9a7bf7c7b1a87da2616c7 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 dc874937fa45b..72064498244ca 100644
--- a/flang/include/flang/Optimizer/Passes/CommandLineOpts.h
+++ b/flang/include/flang/Optimizer/Passes/CommandLineOpts.h
@@ -58,10 +58,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 b9b34c9bb3e87..5cebe1ffcccc4 100644
--- a/flang/lib/Optimizer/Passes/CommandLineOpts.cpp
+++ b/flang/lib/Optimizer/Passes/CommandLineOpts.cpp
@@ -62,9 +62,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 60682b8c9f603..fa5cdc23b228d 100644
--- a/flang/lib/Optimizer/Passes/Pipelines.cpp
+++ b/flang/lib/Optimizer/Passes/Pipelines.cpp
@@ -81,11 +81,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 21697485a2a89..4467aa36605ed 100644
--- a/flang/test/Driver/bbc-mlir-pass-pipeline.f90
+++ b/flang/test/Driver/bbc-mlir-pass-pipeline.f90
@@ -39,7 +39,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 d5126012b6957..e5583ebb2f8f5 100644
--- a/flang/test/Driver/mlir-debug-pass-pipeline.f90
+++ b/flang/test/Driver/mlir-debug-pass-pipeline.f90
@@ -76,7 +76,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 b679564adff10..7db5cb7c87333 100644
--- a/flang/test/Driver/mlir-pass-pipeline.f90
+++ b/flang/test/Driver/mlir-pass-pipeline.f90
@@ -128,7 +128,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 14dea7818f230..58b02269969fc 100644
--- a/flang/test/Fir/basic-program.fir
+++ b/flang/test/Fir/basic-program.fir
@@ -111,7 +111,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 llvm-branch-commits
mailing list