[Mlir-commits] [mlir] [mlir][SPIR-V] Derive Aligned memory operand for PhysicalStorageBuffer in vector.load/store (PR #214200)
Arseniy Obolenskiy
llvmlistbot at llvm.org
Wed Aug 5 04:21:47 PDT 2026
https://github.com/aobolensk created https://github.com/llvm/llvm-project/pull/214200
Mirror calculateMemoryRequirements from MemRefToSPIRV.cpp, since PhysicalStorageBuffer pointers carry no implicit alignment guarantee
>From b68c7545e6a610e333c72992a8905d333609db43 Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Wed, 5 Aug 2026 13:19:34 +0200
Subject: [PATCH] [mlir][SPIR-V] Derive Aligned memory operand for
PhysicalStorageBuffer in vector.load/store
Mirror calculateMemoryRequirements from MemRefToSPIRV.cpp, since PhysicalStorageBuffer pointers carry no implicit alignment guarantee
---
.../VectorToSPIRV/VectorToSPIRV.cpp | 99 ++++++++++++-------
.../VectorToSPIRV/vector-to-spirv.mlir | 30 ++++++
2 files changed, 92 insertions(+), 37 deletions(-)
diff --git a/mlir/lib/Conversion/VectorToSPIRV/VectorToSPIRV.cpp b/mlir/lib/Conversion/VectorToSPIRV/VectorToSPIRV.cpp
index 78693e924c4d9..22b3619b27e8c 100644
--- a/mlir/lib/Conversion/VectorToSPIRV/VectorToSPIRV.cpp
+++ b/mlir/lib/Conversion/VectorToSPIRV/VectorToSPIRV.cpp
@@ -53,6 +53,50 @@ static int getNumBits(Type type) {
return type.getIntOrFloatBitWidth();
}
+namespace {
+struct MemoryRequirements {
+ spirv::MemoryAccessAttr memoryAccess;
+ IntegerAttr alignment;
+};
+} // namespace
+
+/// PhysicalStorageBuffer pointers always need Aligned, derived from pointee
+/// size unless preferredAlignment is set; other storage classes may omit it.
+static FailureOr<MemoryRequirements>
+calculateMemoryRequirements(Value accessedPtr, uint64_t preferredAlignment) {
+ if (preferredAlignment >= std::numeric_limits<uint32_t>::max())
+ return failure();
+
+ MLIRContext *ctx = accessedPtr.getContext();
+ auto ptrType = cast<spirv::PointerType>(accessedPtr.getType());
+ bool mayOmitAlignment =
+ !preferredAlignment &&
+ ptrType.getStorageClass() != spirv::StorageClass::PhysicalStorageBuffer;
+ if (mayOmitAlignment)
+ return MemoryRequirements{spirv::MemoryAccessAttr{}, IntegerAttr{}};
+
+ // PhysicalStorageBuffer pointers require Aligned.
+ std::optional<int64_t> sizeInBytes;
+ Type pointeeType = ptrType.getPointeeType();
+ if (auto scalarType = dyn_cast<spirv::ScalarType>(pointeeType)) {
+ sizeInBytes = scalarType.getSizeInBytes();
+ } else if (auto vecType = dyn_cast<VectorType>(pointeeType)) {
+ if (auto scalarElem = dyn_cast<spirv::ScalarType>(vecType.getElementType()))
+ if (auto elemSize = scalarElem.getSizeInBytes())
+ sizeInBytes = *elemSize * vecType.getNumElements();
+ }
+
+ if (!sizeInBytes)
+ return failure();
+
+ auto memoryAccess =
+ spirv::MemoryAccessAttr::get(ctx, spirv::MemoryAccess::Aligned);
+ uint64_t alignmentValue =
+ preferredAlignment ? preferredAlignment : *sizeInBytes;
+ auto alignment = IntegerAttr::get(IntegerType::get(ctx, 32), alignmentValue);
+ return MemoryRequirements{memoryAccess, alignment};
+}
+
namespace {
struct VectorShapeCast final : public OpConversionPattern<vector::ShapeCastOp> {
@@ -760,22 +804,6 @@ struct VectorLoadOpConverter final
auto vectorPtrType = spirv::PointerType::get(spirvVectorType, storageClass);
- std::optional<uint64_t> alignment = loadOp.getAlignment();
- if (alignment > std::numeric_limits<uint32_t>::max()) {
- return rewriter.notifyMatchFailure(loadOp,
- "invalid alignment requirement");
- }
-
- auto memoryAccess = spirv::MemoryAccess::None;
- spirv::MemoryAccessAttr memoryAccessAttr;
- IntegerAttr alignmentAttr;
- if (alignment.has_value()) {
- memoryAccess |= spirv::MemoryAccess::Aligned;
- memoryAccessAttr =
- spirv::MemoryAccessAttr::get(rewriter.getContext(), memoryAccess);
- alignmentAttr = rewriter.getI32IntegerAttr(alignment.value());
- }
-
// For single element vectors, we don't need to bitcast the access chain to
// the original vector type. Both is going to be the same, a pointer
// to a scalar.
@@ -785,9 +813,15 @@ struct VectorLoadOpConverter final
: spirv::BitcastOp::create(rewriter, loc, vectorPtrType,
accessChain);
- rewriter.replaceOpWithNewOp<spirv::LoadOp>(loadOp, spirvVectorType,
- castedAccessChain,
- memoryAccessAttr, alignmentAttr);
+ auto memoryRequirements = calculateMemoryRequirements(
+ castedAccessChain, loadOp.getAlignment().value_or(0));
+ if (failed(memoryRequirements))
+ return rewriter.notifyMatchFailure(
+ loadOp, "failed to determine memory requirements");
+
+ auto [memoryAccess, alignment] = *memoryRequirements;
+ rewriter.replaceOpWithNewOp<spirv::LoadOp>(
+ loadOp, spirvVectorType, castedAccessChain, memoryAccess, alignment);
return success();
}
@@ -816,12 +850,6 @@ struct VectorStoreOpConverter final
return rewriter.notifyMatchFailure(
storeOp, "failed to get memref element pointer");
- std::optional<uint64_t> alignment = storeOp.getAlignment();
- if (alignment > std::numeric_limits<uint32_t>::max()) {
- return rewriter.notifyMatchFailure(storeOp,
- "invalid alignment requirement");
- }
-
spirv::StorageClass storageClass = attr.getValue();
auto vectorType = storeOp.getVectorType();
// Use the converted vector type instead of original (single element vector
@@ -841,19 +869,16 @@ struct VectorStoreOpConverter final
: spirv::BitcastOp::create(rewriter, loc, vectorPtrType,
accessChain);
- auto memoryAccess = spirv::MemoryAccess::None;
- spirv::MemoryAccessAttr memoryAccessAttr;
- IntegerAttr alignmentAttr;
- if (alignment.has_value()) {
- memoryAccess |= spirv::MemoryAccess::Aligned;
- memoryAccessAttr =
- spirv::MemoryAccessAttr::get(rewriter.getContext(), memoryAccess);
- alignmentAttr = rewriter.getI32IntegerAttr(alignment.value());
- }
+ auto memoryRequirements = calculateMemoryRequirements(
+ castedAccessChain, storeOp.getAlignment().value_or(0));
+ if (failed(memoryRequirements))
+ return rewriter.notifyMatchFailure(
+ storeOp, "failed to determine memory requirements");
- rewriter.replaceOpWithNewOp<spirv::StoreOp>(
- storeOp, castedAccessChain, adaptor.getValueToStore(), memoryAccessAttr,
- alignmentAttr);
+ auto [memoryAccess, alignment] = *memoryRequirements;
+ rewriter.replaceOpWithNewOp<spirv::StoreOp>(storeOp, castedAccessChain,
+ adaptor.getValueToStore(),
+ memoryAccess, alignment);
return success();
}
diff --git a/mlir/test/Conversion/VectorToSPIRV/vector-to-spirv.mlir b/mlir/test/Conversion/VectorToSPIRV/vector-to-spirv.mlir
index f904dd9d35c37..2beb59d16b631 100644
--- a/mlir/test/Conversion/VectorToSPIRV/vector-to-spirv.mlir
+++ b/mlir/test/Conversion/VectorToSPIRV/vector-to-spirv.mlir
@@ -1276,3 +1276,33 @@ func.func @vector_load(%arg0 : memref<4xf32, #spirv.storage_class<StorageBuffer>
%0 = vector.load %arg0[%idx] : memref<4xf32, #spirv.storage_class<StorageBuffer>>, vector<4xf32>
return %0: vector<4xf32>
}
+
+// -----
+
+// PhysicalStorageBuffer pointers get an Aligned memory operand, unlike StorageBuffer above.
+
+module attributes {
+ spirv.target_env = #spirv.target_env<
+ #spirv.vce<v1.6, [Shader, PhysicalStorageBufferAddresses],
+ [SPV_KHR_physical_storage_buffer]>, #spirv.resource_limits<>>
+ } {
+
+// CHECK-LABEL: @vector_load_physical
+// CHECK: spirv.Load "PhysicalStorageBuffer" {{%.*}} ["Aligned", 16] : vector<4xf32>
+func.func @vector_load_physical(%arg0 : memref<4xf32, #spirv.storage_class<PhysicalStorageBuffer>>
+ {spirv.decoration = #spirv.decoration<Aliased>}) -> vector<4xf32> {
+ %idx = arith.constant 0 : index
+ %0 = vector.load %arg0[%idx] : memref<4xf32, #spirv.storage_class<PhysicalStorageBuffer>>, vector<4xf32>
+ return %0: vector<4xf32>
+}
+
+// CHECK-LABEL: @vector_store_physical
+// CHECK: spirv.Store "PhysicalStorageBuffer" {{%.*}}, {{%.*}} ["Aligned", 16] : vector<4xf32>
+func.func @vector_store_physical(%arg0 : memref<4xf32, #spirv.storage_class<PhysicalStorageBuffer>>
+ {spirv.decoration = #spirv.decoration<Aliased>}, %arg1 : vector<4xf32>) {
+ %idx = arith.constant 0 : index
+ vector.store %arg1, %arg0[%idx] : memref<4xf32, #spirv.storage_class<PhysicalStorageBuffer>>, vector<4xf32>
+ return
+}
+
+}
More information about the Mlir-commits
mailing list