[Mlir-commits] [mlir] 6248bfb - [mlir][SPIR-V] Derive Aligned memory operand for PhysicalStorageBuffer in vector.load/store (#214200)
llvmlistbot at llvm.org
llvmlistbot at llvm.org
Wed Oct 14 02:11:48 PDT 2026
Author: Arseniy Obolenskiy
Date: 2026-10-14T11:11:37+02:00
New Revision: 6248bfbe99eb1409f2dfdbcb791df3a2c64b97e6
URL: https://github.com/llvm/llvm-project/commit/6248bfbe99eb1409f2dfdbcb791df3a2c64b97e6
DIFF: https://github.com/llvm/llvm-project/commit/6248bfbe99eb1409f2dfdbcb791df3a2c64b97e6.diff
LOG: [mlir][SPIR-V] Derive Aligned memory operand for PhysicalStorageBuffer in vector.load/store (#214200)
Mirror calculateMemoryRequirements from MemRefToSPIRV.cpp, since
PhysicalStorageBuffer pointers carry no implicit alignment guarantee
Added:
Modified:
mlir/lib/Conversion/VectorToSPIRV/VectorToSPIRV.cpp
mlir/test/Conversion/VectorToSPIRV/vector-to-spirv.mlir
Removed:
################################################################################
diff --git a/mlir/lib/Conversion/VectorToSPIRV/VectorToSPIRV.cpp b/mlir/lib/Conversion/VectorToSPIRV/VectorToSPIRV.cpp
index 7038f25119159..4b93f7b24e0a8 100644
--- a/mlir/lib/Conversion/VectorToSPIRV/VectorToSPIRV.cpp
+++ b/mlir/lib/Conversion/VectorToSPIRV/VectorToSPIRV.cpp
@@ -53,6 +53,49 @@ 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
+/// element 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. The vector pointer is a
+ // bitcast of a scalar element pointer, so only element alignment is known.
+ std::optional<int64_t> sizeInBytes;
+ Type pointeeType = ptrType.getPointeeType();
+ if (auto vecType = dyn_cast<VectorType>(pointeeType))
+ pointeeType = vecType.getElementType();
+ if (auto scalarType = dyn_cast<spirv::ScalarType>(pointeeType))
+ sizeInBytes = scalarType.getSizeInBytes();
+
+ 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> {
@@ -764,22 +807,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.
@@ -789,9 +816,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();
}
@@ -820,12 +853,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
@@ -845,19 +872,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 6319e30500034..b3b27fde9f861 100644
--- a/mlir/test/Conversion/VectorToSPIRV/vector-to-spirv.mlir
+++ b/mlir/test/Conversion/VectorToSPIRV/vector-to-spirv.mlir
@@ -1312,3 +1312,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", 4] : 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", 4] : 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