[llvm] [SPIRV] Lower `spirv.Layout` type (PR #140059)
Steven Perron via llvm-commits
llvm-commits at lists.llvm.org
Tue May 27 08:03:15 PDT 2025
https://github.com/s-perron updated https://github.com/llvm/llvm-project/pull/140059
>From d007a3509d9b6c1b4ac3bea760a17d3955978877 Mon Sep 17 00:00:00 2001
From: Steven Perron <stevenperron at google.com>
Date: Thu, 1 May 2025 13:35:40 -0400
Subject: [PATCH 1/2] [SPIRV] Lower `spirv.Layout` type
Add this type in accordance with the [design doc](https://github.com/llvm/wg-hlsl/pull/171).
Fixes https://github.com/llvm/llvm-project/issues/138276
---
llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp | 8 ++++
llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp | 14 ++++--
llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp | 46 ++++++++++++++++---
llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h | 6 ++-
llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp | 17 +++----
llvm/lib/Target/SPIRV/SPIRVUtils.cpp | 2 +
llvm/lib/Target/SPIRV/SPIRVUtils.h | 2 +
.../SPIRV/hlsl-resources/spirv.layout.type.ll | 44 ++++++++++++++++++
8 files changed, 119 insertions(+), 20 deletions(-)
create mode 100644 llvm/test/CodeGen/SPIRV/hlsl-resources/spirv.layout.type.ll
diff --git a/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp b/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
index c516be0297e66..e7b257777e5e8 100644
--- a/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp
@@ -3163,6 +3163,12 @@ static SPIRVType *getVulkanBufferType(const TargetExtType *ExtensionType,
return GR->getOrCreateVulkanBufferType(MIRBuilder, T, SC, IsWritable);
}
+static SPIRVType *getLayoutType(const TargetExtType *ExtensionType,
+ MachineIRBuilder &MIRBuilder,
+ SPIRVGlobalRegistry *GR) {
+ return GR->getOrCreateLayoutType(MIRBuilder, ExtensionType);
+}
+
namespace SPIRV {
TargetExtType *parseBuiltinTypeNameToTargetExtType(std::string TypeName,
LLVMContext &Context) {
@@ -3240,6 +3246,8 @@ SPIRVType *lowerBuiltinType(const Type *OpaqueType,
TargetType = getInlineSpirvType(BuiltinType, MIRBuilder, GR);
} else if (Name == "spirv.VulkanBuffer") {
TargetType = getVulkanBufferType(BuiltinType, MIRBuilder, GR);
+ } else if (Name == "spirv.Layout") {
+ TargetType = getLayoutType(BuiltinType, MIRBuilder, GR);
} else {
// Lookup the demangled builtin type in the TableGen records.
const SPIRV::BuiltinType *TypeRecord = SPIRV::lookupBuiltinType(Name);
diff --git a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
index b336732ec4b64..06fa1ba0f481d 100644
--- a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
@@ -673,10 +673,16 @@ Type *SPIRVEmitIntrinsics::deduceElementTypeHelper(
} else if (HandleType->getTargetExtName() == "spirv.VulkanBuffer") {
// This call is supposed to index into an array
Ty = HandleType->getTypeParameter(0);
- assert(Ty->isArrayTy() &&
- "spv_resource_getpointer indexes into an array, so the type of "
- "the buffer should be an array.");
- Ty = Ty->getArrayElementType();
+ if (Ty->isArrayTy())
+ Ty = Ty->getArrayElementType();
+ else {
+ TargetExtType *BufferTy = cast<TargetExtType>(Ty);
+ assert(BufferTy->getTargetExtName() == "spirv.Layout");
+ Ty = BufferTy->getTypeParameter(0);
+ assert(Ty && Ty->isStructTy());
+ uint32_t Index = cast<ConstantInt>(II->getOperand(1))->getZExtValue();
+ Ty = cast<StructType>(Ty)->getElementType(Index);
+ }
} else {
llvm_unreachable("Unknown handle type for spv_resource_getpointer.");
}
diff --git a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
index ac397fc486e19..a66c9dc6a3c30 100644
--- a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
@@ -957,7 +957,7 @@ SPIRVType *SPIRVGlobalRegistry::getOpTypeOpaque(const StructType *Ty,
SPIRVType *SPIRVGlobalRegistry::getOpTypeStruct(
const StructType *Ty, MachineIRBuilder &MIRBuilder,
SPIRV::AccessQualifier::AccessQualifier AccQual,
- bool ExplicitLayoutRequired, bool EmitIR) {
+ StructOffsetDecorator Decorator, bool EmitIR) {
const SPIRVSubtarget &ST =
cast<SPIRVSubtarget>(MIRBuilder.getMF().getSubtarget());
SmallVector<Register, 4> FieldTypes;
@@ -974,7 +974,7 @@ SPIRVType *SPIRVGlobalRegistry::getOpTypeStruct(
for (const auto &Elem : Ty->elements()) {
SPIRVType *ElemTy = findSPIRVType(toTypedPointer(Elem), MIRBuilder, AccQual,
- ExplicitLayoutRequired, EmitIR);
+ Decorator != nullptr, EmitIR);
assert(ElemTy && ElemTy->getOpcode() != SPIRV::OpTypeVoid &&
"Invalid struct element type");
FieldTypes.push_back(getSPIRVTypeID(ElemTy));
@@ -1001,9 +1001,8 @@ SPIRVType *SPIRVGlobalRegistry::getOpTypeStruct(
return MIBStruct;
});
- if (ExplicitLayoutRequired)
- addStructOffsetDecorations(SPVType->defs().begin()->getReg(),
- const_cast<StructType *>(Ty), MIRBuilder);
+ if (Decorator)
+ Decorator(SPVType->defs().begin()->getReg());
return SPVType;
}
@@ -1142,8 +1141,15 @@ SPIRVType *SPIRVGlobalRegistry::createSPIRVType(
if (auto SType = dyn_cast<StructType>(Ty)) {
if (SType->isOpaque())
return getOpTypeOpaque(SType, MIRBuilder);
- return getOpTypeStruct(SType, MIRBuilder, AccQual, ExplicitLayoutRequired,
- EmitIR);
+
+ StructOffsetDecorator Decorator = nullptr;
+ if (ExplicitLayoutRequired) {
+ Decorator = [&MIRBuilder, SType, this](Register Reg) {
+ addStructOffsetDecorations(Reg, const_cast<StructType *>(SType),
+ MIRBuilder);
+ };
+ }
+ return getOpTypeStruct(SType, MIRBuilder, AccQual, Decorator, EmitIR);
}
if (auto FType = dyn_cast<FunctionType>(Ty)) {
SPIRVType *RetTy = findSPIRVType(FType->getReturnType(), MIRBuilder,
@@ -1460,6 +1466,32 @@ SPIRVType *SPIRVGlobalRegistry::getOrCreateVulkanBufferType(
return R;
}
+SPIRVType *SPIRVGlobalRegistry::getOrCreateLayoutType(
+ MachineIRBuilder &MIRBuilder, const TargetExtType *T, bool EmitIr) {
+ auto Key = SPIRV::handle(T);
+ if (const MachineInstr *MI = findMI(Key, &MIRBuilder.getMF()))
+ return MI;
+
+ StructType *ST = cast<StructType>(T->getTypeParameter(0));
+ ArrayRef<uint32_t> Offsets = T->int_params().slice(1);
+ assert(ST->getNumElements() == Offsets.size());
+
+ StructOffsetDecorator Decorator = [&MIRBuilder, &Offsets](Register Reg) {
+ for (uint32_t I = 0; I < Offsets.size(); ++I) {
+ buildOpMemberDecorate(Reg, MIRBuilder, SPIRV::Decoration::Offset, I,
+ {Offsets[I]});
+ }
+ };
+
+ // We need a new OpTypeStruct instruction because decorations will be
+ // different from a struct with an explicit layout created from a different
+ // entry point.
+ SPIRVType *SPIRVStructType = getOpTypeStruct(
+ ST, MIRBuilder, SPIRV::AccessQualifier::None, Decorator, EmitIr);
+ add(Key, SPIRVStructType);
+ return SPIRVStructType;
+}
+
SPIRVType *SPIRVGlobalRegistry::getOrCreateOpTypeImage(
MachineIRBuilder &MIRBuilder, SPIRVType *SampledType, SPIRV::Dim::Dim Dim,
uint32_t Depth, uint32_t Arrayed, uint32_t Multisampled, uint32_t Sampled,
diff --git a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h
index 7338e805956d6..3b481b3aba0c1 100644
--- a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h
+++ b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h
@@ -26,6 +26,7 @@
namespace llvm {
class SPIRVSubtarget;
using SPIRVType = const MachineInstr;
+using StructOffsetDecorator = std::function<void(Register)>;
class SPIRVGlobalRegistry : public SPIRVIRMapping {
// Registers holding values which have types associated with them.
@@ -451,7 +452,7 @@ class SPIRVGlobalRegistry : public SPIRVIRMapping {
SPIRVType *getOpTypeStruct(const StructType *Ty, MachineIRBuilder &MIRBuilder,
SPIRV::AccessQualifier::AccessQualifier AccQual,
- bool ExplicitLayoutRequired, bool EmitIR);
+ StructOffsetDecorator Decorator, bool EmitIR);
SPIRVType *getOpTypePointer(SPIRV::StorageClass::StorageClass SC,
SPIRVType *ElemType, MachineIRBuilder &MIRBuilder,
@@ -601,6 +602,9 @@ class SPIRVGlobalRegistry : public SPIRVIRMapping {
SPIRV::StorageClass::StorageClass SC,
bool IsWritable, bool EmitIr = false);
+ SPIRVType *getOrCreateLayoutType(MachineIRBuilder &MIRBuilder,
+ const TargetExtType *T, bool EmitIr = false);
+
SPIRVType *
getOrCreateOpTypeImage(MachineIRBuilder &MIRBuilder, SPIRVType *SampledType,
SPIRV::Dim::Dim Dim, uint32_t Depth, uint32_t Arrayed,
diff --git a/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp b/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
index 578e82881f6e8..42f4e935d70d0 100644
--- a/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
@@ -86,14 +86,15 @@ SPIRVLegalizerInfo::SPIRVLegalizerInfo(const SPIRVSubtarget &ST) {
const LLT p8 = LLT::pointer(8, PSize); // Output
const LLT p10 = LLT::pointer(10, PSize); // Private
const LLT p11 = LLT::pointer(11, PSize); // StorageBuffer
+ const LLT p12 = LLT::pointer(12, PSize); // Uniform
// TODO: remove copy-pasting here by using concatenation in some way.
auto allPtrsScalarsAndVectors = {
- p0, p1, p2, p3, p4, p5, p6, p7, p8,
- p10, p11, s1, s8, s16, s32, s64, v2s1, v2s8,
- v2s16, v2s32, v2s64, v3s1, v3s8, v3s16, v3s32, v3s64, v4s1,
- v4s8, v4s16, v4s32, v4s64, v8s1, v8s8, v8s16, v8s32, v8s64,
- v16s1, v16s8, v16s16, v16s32, v16s64};
+ p0, p1, p2, p3, p4, p5, p6, p7, p8,
+ p10, p11, p12, s1, s8, s16, s32, s64, v2s1,
+ v2s8, v2s16, v2s32, v2s64, v3s1, v3s8, v3s16, v3s32, v3s64,
+ v4s1, v4s8, v4s16, v4s32, v4s64, v8s1, v8s8, v8s16, v8s32,
+ v8s64, v16s1, v16s8, v16s16, v16s32, v16s64};
auto allVectors = {v2s1, v2s8, v2s16, v2s32, v2s64, v3s1, v3s8,
v3s16, v3s32, v3s64, v4s1, v4s8, v4s16, v4s32,
@@ -120,10 +121,10 @@ SPIRVLegalizerInfo::SPIRVLegalizerInfo(const SPIRVSubtarget &ST) {
s16, s32, s64, v2s16, v2s32, v2s64, v3s16, v3s32, v3s64,
v4s16, v4s32, v4s64, v8s16, v8s32, v8s64, v16s16, v16s32, v16s64};
- auto allFloatAndIntScalarsAndPtrs = {s8, s16, s32, s64, p0, p1, p2, p3,
- p4, p5, p6, p7, p8, p10, p11};
+ auto allFloatAndIntScalarsAndPtrs = {s8, s16, s32, s64, p0, p1, p2, p3,
+ p4, p5, p6, p7, p8, p10, p11, p12};
- auto allPtrs = {p0, p1, p2, p3, p4, p5, p6, p7, p8, p10, p11};
+ auto allPtrs = {p0, p1, p2, p3, p4, p5, p6, p7, p8, p10, p11, p12};
bool IsExtendedInts =
ST.canUseExtension(
diff --git a/llvm/lib/Target/SPIRV/SPIRVUtils.cpp b/llvm/lib/Target/SPIRV/SPIRVUtils.cpp
index f38794afab436..725a7979d3e5b 100644
--- a/llvm/lib/Target/SPIRV/SPIRVUtils.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVUtils.cpp
@@ -262,6 +262,8 @@ addressSpaceToStorageClass(unsigned AddrSpace, const SPIRVSubtarget &STI) {
return SPIRV::StorageClass::Private;
case 11:
return SPIRV::StorageClass::StorageBuffer;
+ case 12:
+ return SPIRV::StorageClass::Uniform;
default:
report_fatal_error("Unknown address space");
}
diff --git a/llvm/lib/Target/SPIRV/SPIRVUtils.h b/llvm/lib/Target/SPIRV/SPIRVUtils.h
index ccf394de45c89..f14a7d356ea58 100644
--- a/llvm/lib/Target/SPIRV/SPIRVUtils.h
+++ b/llvm/lib/Target/SPIRV/SPIRVUtils.h
@@ -210,6 +210,8 @@ storageClassToAddressSpace(SPIRV::StorageClass::StorageClass SC) {
return 10;
case SPIRV::StorageClass::StorageBuffer:
return 11;
+ case SPIRV::StorageClass::Uniform:
+ return 12;
default:
report_fatal_error("Unable to get address space id");
}
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-resources/spirv.layout.type.ll b/llvm/test/CodeGen/SPIRV/hlsl-resources/spirv.layout.type.ll
new file mode 100644
index 0000000000000..daed54cc4049b
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/hlsl-resources/spirv.layout.type.ll
@@ -0,0 +1,44 @@
+; RUN: llc -O0 -verify-machineinstrs -mtriple=spirv1.6-unknown-vulkan1.3-library %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv1.6-unknown-vulkan1.3-library %s -o - -filetype=obj | spirv-val %}
+
+target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-n8:16:32:64-G10"
+
+; CHECK-DAG: OpName [[standard_layout:%[0-9]+]] "standard_layout"
+; CHECK-DAG: OpMemberDecorate [[standard_layout]] 0 Offset 0
+; CHECK-DAG: OpMemberDecorate [[standard_layout]] 1 Offset 4
+
+; CHECK-DAG: OpName [[standard_layout_with_different_offset:%[0-9]+]] "standard_layout"
+; CHECK-DAG: OpMemberDecorate [[standard_layout_with_different_offset]] 0 Offset 0
+; CHECK-DAG: OpMemberDecorate [[standard_layout_with_different_offset]] 1 Offset 8
+%standard_layout = type { i32, i32 }
+
+; CHECK-DAG: OpName [[backwards_layout:%[0-9]+]] "backwards_layout"
+; CHECK-DAG: OpMemberDecorate [[backwards_layout]] 0 Offset 4
+; CHECK-DAG: OpMemberDecorate [[backwards_layout]] 1 Offset 0
+%backwards_layout = type { i32, i32 }
+
+; CHECK-DAG: OpName [[large_gap:%[0-9]+]] "large_gap"
+; CHECK-DAG: OpMemberDecorate [[large_gap]] 0 Offset 0
+; CHECK-DAG: OpMemberDecorate [[large_gap]] 1 Offset 64
+; CHECK-DAG: OpMemberDecorate [[large_gap]] 2 Offset 1020
+; CHECK-DAG: OpMemberDecorate [[large_gap]] 3 Offset 4
+%large_gap = type { i32, i32, i32, i32 }
+
+; CHECK-DAG: OpName [[mixed_layout:%[0-9]+]] "mixed_layout"
+; CHECK-DAG: OpMemberDecorate [[mixed_layout]] 0 Offset 0
+; CHECK-DAG: OpMemberDecorate [[mixed_layout]] 1 Offset 8
+; CHECK-DAG: OpMemberDecorate [[mixed_layout]] 2 Offset 4
+; CHECK-DAG: OpMemberDecorate [[mixed_layout]] 3 Offset 12
+%mixed_layout = type { i32, i32, i32, i32 }
+
+define void @main() local_unnamed_addr #1 {
+entry:
+ %standard_handle = tail call target("spirv.VulkanBuffer", target("spirv.Layout", %standard_layout, 8, 0, 4), 2, 0) @llvm.spv.resource.handlefrombinding.tspirv.VulkanBuffer_tspirv.Layout_s___cblayout_Bs_8_0_4t_2_0t(i32 0, i32 1, i32 1, i32 0, i1 false)
+ %standard_handle_with_different_offset = tail call target("spirv.VulkanBuffer", target("spirv.Layout", %standard_layout, 12, 0, 8), 2, 0) @llvm.spv.resource.handlefrombinding.tspirv.VulkanBuffer_tspirv.Layout_s___cblayout_Bs_8_0_4t_2_0t(i32 0, i32 1, i32 1, i32 0, i1 false)
+ %backwards_handle = tail call target("spirv.VulkanBuffer", target("spirv.Layout", %backwards_layout, 8, 4, 0), 2, 0) @llvm.spv.resource.handlefrombinding.tspirv.VulkanBuffer_tspirv.Layout_s___cblayout_Bs_8_0_4t_2_0t(i32 0, i32 1, i32 1, i32 0, i1 false)
+ %large_gap_handle = tail call target("spirv.VulkanBuffer", target("spirv.Layout", %large_gap, 1024, 0, 64, 1020, 4), 2, 0) @llvm.spv.resource.handlefrombinding.tspirv.VulkanBuffer_tspirv.Layout_s___cblayout_Bs_8_0_4t_2_0t(i32 0, i32 1, i32 1, i32 0, i1 false)
+ %mixed_handle = tail call target("spirv.VulkanBuffer", target("spirv.Layout", %mixed_layout, 16, 0, 8, 4, 12), 2, 0) @llvm.spv.resource.handlefrombinding.tspirv.VulkanBuffer_tspirv.Layout_s___cblayout_Bs_8_0_4t_2_0t(i32 0, i32 1, i32 1, i32 0, i1 false)
+ ret void
+}
+
+attributes #1 = { mustprogress nofree noinline norecurse nosync nounwind willreturn memory(readwrite, argmem: write, inaccessiblemem: none) "approx-func-fp-math"="false" "frame-pointer"="all" "hlsl.numthreads"="1,1,1" "hlsl.shader"="compute" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
\ No newline at end of file
>From c881b85fb1fff686f8c2af5424f9ac2a666225c0 Mon Sep 17 00:00:00 2001
From: Steven Perron <stevenperron at google.com>
Date: Tue, 27 May 2025 11:02:59 -0400
Subject: [PATCH 2/2] Add EOL to last line in file.
---
llvm/test/CodeGen/SPIRV/hlsl-resources/spirv.layout.type.ll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-resources/spirv.layout.type.ll b/llvm/test/CodeGen/SPIRV/hlsl-resources/spirv.layout.type.ll
index daed54cc4049b..14c98b2fd55a5 100644
--- a/llvm/test/CodeGen/SPIRV/hlsl-resources/spirv.layout.type.ll
+++ b/llvm/test/CodeGen/SPIRV/hlsl-resources/spirv.layout.type.ll
@@ -41,4 +41,4 @@ entry:
ret void
}
-attributes #1 = { mustprogress nofree noinline norecurse nosync nounwind willreturn memory(readwrite, argmem: write, inaccessiblemem: none) "approx-func-fp-math"="false" "frame-pointer"="all" "hlsl.numthreads"="1,1,1" "hlsl.shader"="compute" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
\ No newline at end of file
+attributes #1 = { mustprogress nofree noinline norecurse nosync nounwind willreturn memory(readwrite, argmem: write, inaccessiblemem: none) "approx-func-fp-math"="false" "frame-pointer"="all" "hlsl.numthreads"="1,1,1" "hlsl.shader"="compute" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
More information about the llvm-commits
mailing list