[flang-commits] [flang] [flang] Add an alignment attribute to fir.alloca (PR #234897)
via flang-commits
flang-commits at lists.llvm.org
Fri Oct 23 03:55:40 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-flang-codegen
Author: Eugene Epshteyn (eugeneepshteyn)
<details>
<summary>Changes</summary>
Add an optional `alignment` attribute to `fir.alloca`, mirroring the one on `fir.allocmem`, so that FIR can express an alignment requirement on stack storage. The verifier requires a positive power of two.
Codegen passes the maximum of the requested alignment and the data layout's ABI alignment for the type to `llvm.alloca`, so an attribute below the natural alignment can never under-align the object. `fir.global` lowering gets the same clamp.
This is the first of a series that adds a per-variable alignment directive (`!DIR$ ALIGN(n)`) to flang. This patch is independent of the directive's syntax and only adds the IR carrier. The following patches preserve the attribute across the stack/heap conversions, carry it on the declare ops and let temporaries inherit it; the directive's syntax will be proposed in a Discourse RFC before the parser, semantics and lowering patches go up.
Assisted-by: AI
---
Full diff: https://github.com/llvm/llvm-project/pull/234897.diff
6 Files Affected:
- (modified) flang/include/flang/Optimizer/Dialect/FIROps.td (+7-1)
- (modified) flang/lib/Optimizer/CodeGen/CodeGen.cpp (+13-2)
- (modified) flang/lib/Optimizer/Dialect/FIROps.cpp (+10-6)
- (modified) flang/test/Fir/convert-to-llvm.fir (+32)
- (modified) flang/test/Fir/fir-ops.fir (+2)
- (modified) flang/test/Fir/invalid.fir (+8)
``````````diff
diff --git a/flang/include/flang/Optimizer/Dialect/FIROps.td b/flang/include/flang/Optimizer/Dialect/FIROps.td
index fc00daac1695b1..0b727b46dc4ce4 100644
--- a/flang/include/flang/Optimizer/Dialect/FIROps.td
+++ b/flang/include/flang/Optimizer/Dialect/FIROps.td
@@ -205,6 +205,11 @@ def fir_AllocaOp : fir_Op<"alloca", [
The pinned attribute is used to flag fir.alloca operation in a specific
region and avoid them being hoisted in an alloca hoisting pass.
+
+ The optional alignment attribute requests a minimum byte alignment for
+ the allocation, as `fir.allocmem` and `fir.global` do. It must be a
+ positive power of two. Code generation never lowers the alignment below
+ the ABI alignment of the allocated type.
}];
let arguments = (ins
@@ -213,7 +218,8 @@ def fir_AllocaOp : fir_Op<"alloca", [
OptionalAttr<StrAttr>:$bindc_name,
UnitAttr:$pinned,
Variadic<AnyIntegerType>:$typeparams,
- Variadic<AnyIntegerType>:$shape
+ Variadic<AnyIntegerType>:$shape,
+ OptionalAttr<I64Attr>:$alignment
);
let results =
diff --git a/flang/lib/Optimizer/CodeGen/CodeGen.cpp b/flang/lib/Optimizer/CodeGen/CodeGen.cpp
index d5097aa6f76035..63cc61193d1f65 100644
--- a/flang/lib/Optimizer/CodeGen/CodeGen.cpp
+++ b/flang/lib/Optimizer/CodeGen/CodeGen.cpp
@@ -480,13 +480,21 @@ struct AllocaOpConversion : public fir::FIROpConversion<fir::AllocaOp> {
rewriter.setInsertionPointAfter(size.getDefiningOp());
}
+ // A requested alignment is a minimum: never go below the ABI alignment of
+ // the allocated type, which is what an alloca without an explicit
+ // alignment gets.
+ unsigned alignment = 0;
+ if (std::optional<std::int64_t> requested = alloc.getAlignment())
+ alignment = static_cast<unsigned>(std::max<std::uint64_t>(
+ *requested, getDataLayout().getTypeABIAlignment(llvmObjectType)));
+
// NOTE: we used to pass alloc->getAttrs() in the builder for non opaque
// pointers! Only propagate pinned and bindc_name to help debugging, but
// this should have no functional purpose (and passing the operand segment
// attribute like before is certainly bad).
auto llvmAlloc = mlir::LLVM::AllocaOp::create(
rewriter, loc, ::getLlvmPtrType(alloc.getContext(), allocaAs),
- llvmObjectType, size);
+ llvmObjectType, size, alignment);
if (alloc.getPinned())
llvmAlloc->setDiscardableAttr(alloc.getPinnedAttrName(),
alloc.getPinnedAttr());
@@ -3848,8 +3856,11 @@ struct GlobalOpConversion : public fir::FIROpConversion<fir::GlobalOp> {
0, getGlobalAddressSpace(rewriter), false,
mlir::LLVM::ThreadLocalMode::NotThreadLocal, comdat, attrs, dbgExprs);
+ // A requested alignment is a minimum: never go below the ABI alignment of
+ // the global's type.
if (global.getAlignment() && *global.getAlignment() > 0)
- g.setAlignment(*global.getAlignment());
+ g.setAlignment(std::max<std::uint64_t>(
+ *global.getAlignment(), getDataLayout().getTypeABIAlignment(tyAttr)));
if (auto section = global.getSection())
g.setSection(*section);
diff --git a/flang/lib/Optimizer/Dialect/FIROps.cpp b/flang/lib/Optimizer/Dialect/FIROps.cpp
index 3d079afa0d1a51..97bca969e96b7b 100644
--- a/flang/lib/Optimizer/Dialect/FIROps.cpp
+++ b/flang/lib/Optimizer/Dialect/FIROps.cpp
@@ -408,7 +408,7 @@ void fir::AllocaOp::build(mlir::OpBuilder &builder,
llvm::ArrayRef<mlir::NamedAttribute> attributes) {
auto nameAttr = builder.getStringAttr(uniqName);
build(builder, result, wrapAllocaResultType(inType), inType, nameAttr, {},
- /*pinned=*/false, typeparams, shape);
+ /*pinned=*/false, typeparams, shape, /*alignment=*/{});
result.addAttributes(attributes);
}
@@ -419,7 +419,7 @@ void fir::AllocaOp::build(mlir::OpBuilder &builder,
llvm::ArrayRef<mlir::NamedAttribute> attributes) {
auto nameAttr = builder.getStringAttr(uniqName);
build(builder, result, wrapAllocaResultType(inType), inType, nameAttr, {},
- pinned, typeparams, shape);
+ pinned, typeparams, shape, /*alignment=*/{});
result.addAttributes(attributes);
}
@@ -433,7 +433,7 @@ void fir::AllocaOp::build(mlir::OpBuilder &builder,
auto bindcAttr =
bindcName.empty() ? mlir::StringAttr{} : builder.getStringAttr(bindcName);
build(builder, result, wrapAllocaResultType(inType), inType, nameAttr,
- bindcAttr, /*pinned=*/false, typeparams, shape);
+ bindcAttr, /*pinned=*/false, typeparams, shape, /*alignment=*/{});
result.addAttributes(attributes);
}
@@ -448,7 +448,7 @@ void fir::AllocaOp::build(mlir::OpBuilder &builder,
auto bindcAttr =
bindcName.empty() ? mlir::StringAttr{} : builder.getStringAttr(bindcName);
build(builder, result, wrapAllocaResultType(inType), inType, nameAttr,
- bindcAttr, pinned, typeparams, shape);
+ bindcAttr, pinned, typeparams, shape, /*alignment=*/{});
result.addAttributes(attributes);
}
@@ -457,7 +457,7 @@ void fir::AllocaOp::build(mlir::OpBuilder &builder,
mlir::ValueRange typeparams, mlir::ValueRange shape,
llvm::ArrayRef<mlir::NamedAttribute> attributes) {
build(builder, result, wrapAllocaResultType(inType), inType, {}, {},
- /*pinned=*/false, typeparams, shape);
+ /*pinned=*/false, typeparams, shape, /*alignment=*/{});
result.addAttributes(attributes);
}
@@ -467,7 +467,7 @@ void fir::AllocaOp::build(mlir::OpBuilder &builder,
mlir::ValueRange shape,
llvm::ArrayRef<mlir::NamedAttribute> attributes) {
build(builder, result, wrapAllocaResultType(inType), inType, {}, {}, pinned,
- typeparams, shape);
+ typeparams, shape, /*alignment=*/{});
result.addAttributes(attributes);
}
@@ -492,6 +492,10 @@ llvm::LogicalResult fir::AllocaOp::verify() {
mlir::Type outType = getType();
if (!mlir::isa<fir::ReferenceType>(outType))
return emitOpError("must be a !fir.ref type");
+ if (std::optional<std::int64_t> alignment = getAlignment()) {
+ if (*alignment <= 0 || (*alignment & (*alignment - 1)) != 0)
+ return emitOpError("alignment must be a positive power of two");
+ }
return mlir::success();
}
diff --git a/flang/test/Fir/convert-to-llvm.fir b/flang/test/Fir/convert-to-llvm.fir
index 73b930b3803285..a0c4d0f6db0f46 100644
--- a/flang/test/Fir/convert-to-llvm.fir
+++ b/flang/test/Fir/convert-to-llvm.fir
@@ -1115,6 +1115,38 @@ func.func @alloca_several() -> !fir.ref<i32> {
// -----
+// Test fir.alloca with a requested alignment
+
+func.func @alloca_aligned() -> !fir.ref<!fir.array<10xf32>> {
+ %1 = fir.alloca !fir.array<10xf32> {alignment = 64 : i64}
+ return %1 : !fir.ref<!fir.array<10xf32>>
+}
+
+// CHECK-LABEL: llvm.func @alloca_aligned() -> !llvm.ptr
+// CHECK: [[N:%.*]] = llvm.mlir.constant(1 : i64) : i64
+// GENERIC: [[A:%.*]] = llvm.alloca [[N]] x !llvm.array<10 x f32> {alignment = 64 : i64}
+// AMDGPU: [[AA:%.*]] = llvm.alloca [[N]] x !llvm.array<10 x f32> {alignment = 64 : i64} : (i64) -> !llvm.ptr<5>
+// AMDGPU: [[A:%.*]] = llvm.addrspacecast [[AA]] : !llvm.ptr<5> to !llvm.ptr
+// CHECK: llvm.return [[A]] : !llvm.ptr
+
+// -----
+
+// A requested alignment below the ABI alignment of the type is raised to it.
+
+func.func @alloca_underaligned() -> !fir.ref<i32> {
+ %1 = fir.alloca i32 {alignment = 2 : i64}
+ return %1 : !fir.ref<i32>
+}
+
+// CHECK-LABEL: llvm.func @alloca_underaligned() -> !llvm.ptr
+// CHECK: [[N:%.*]] = llvm.mlir.constant(1 : i64) : i64
+// GENERIC: [[A:%.*]] = llvm.alloca [[N]] x i32 {alignment = 4 : i64}
+// AMDGPU: [[AA:%.*]] = llvm.alloca [[N]] x i32 {alignment = 4 : i64} : (i64) -> !llvm.ptr<5>
+// AMDGPU: [[A:%.*]] = llvm.addrspacecast [[AA]] : !llvm.ptr<5> to !llvm.ptr
+// CHECK: llvm.return [[A]] : !llvm.ptr
+
+// -----
+
// Test fir.alloca of pointer to array
func.func @alloca_ptr_to_array() -> !fir.ref<!fir.ptr<!fir.array<?xi32>>> {
diff --git a/flang/test/Fir/fir-ops.fir b/flang/test/Fir/fir-ops.fir
index 704cd2ab217fa6..bf42a9c1ae79e1 100644
--- a/flang/test/Fir/fir-ops.fir
+++ b/flang/test/Fir/fir-ops.fir
@@ -37,10 +37,12 @@ func.func @instructions() {
// CHECK: [[VAL_0:%.*]] = fir.alloca !fir.array<10xi32>
// CHECK: [[VAL_1:%.*]] = fir.load [[VAL_0]] : !fir.ref<!fir.array<10xi32>>
// CHECK: [[VAL_2:%.*]] = fir.alloca i32
+// CHECK: [[VAL_ALIGNED:%.*]] = fir.alloca !fir.array<10xf32> <{alignment = 64 : i64}>
// CHECK: [[VAL_3:%.*]] = arith.constant 22 : i32
%0 = fir.alloca !fir.array<10xi32>
%1 = fir.load %0 : !fir.ref<!fir.array<10xi32>>
%2 = fir.alloca i32
+ %aligned = fir.alloca !fir.array<10xf32> {alignment = 64 : i64}
%3 = arith.constant 22 : i32
// CHECK: fir.store [[VAL_3]] to [[VAL_2]] : !fir.ref<i32>
diff --git a/flang/test/Fir/invalid.fir b/flang/test/Fir/invalid.fir
index 8964df1d2e93e6..8bf414f86173f9 100644
--- a/flang/test/Fir/invalid.fir
+++ b/flang/test/Fir/invalid.fir
@@ -1400,3 +1400,11 @@ func.func @allocmem_bad_alignment() -> !fir.heap<!fir.array<10xf32>> {
%0 = fir.allocmem !fir.array<10xf32> {alignment = 3 : i64}
return %0 : !fir.heap<!fir.array<10xf32>>
}
+
+// -----
+
+func.func @alloca_bad_alignment() -> !fir.ref<!fir.array<10xf32>> {
+ // expected-error at +1 {{'fir.alloca' op alignment must be a positive power of two}}
+ %0 = fir.alloca !fir.array<10xf32> {alignment = 24 : i64}
+ return %0 : !fir.ref<!fir.array<10xf32>>
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/234897
More information about the flang-commits
mailing list