diff --git a/llvm/docs/SPIRVUsage.rst b/llvm/docs/SPIRVUsage.rst index 3e19ff881dffc..ec842db586f77 100644 --- a/llvm/docs/SPIRVUsage.rst +++ b/llvm/docs/SPIRVUsage.rst @@ -405,6 +405,10 @@ SPIR-V backend, along with their descriptions and argument details. - 32-bit Integer - `[32-bit Integer]` - Retrieves the thread ID within a workgroup. Essential for identifying execution context in parallel compute operations. + * - `int_spv_flattened_thread_id_in_group` + - 32-bit Integer + - `[32-bit Integer]` + - Provides a flattened index for a given thread within a given group (SV_GroupIndex) * - `int_spv_create_handle` - Pointer - `[8-bit Integer]` diff --git a/llvm/include/llvm/IR/IntrinsicsSPIRV.td b/llvm/include/llvm/IR/IntrinsicsSPIRV.td index df3e137c80980..4a0e10db2f1e4 100644 --- a/llvm/include/llvm/IR/IntrinsicsSPIRV.td +++ b/llvm/include/llvm/IR/IntrinsicsSPIRV.td @@ -61,6 +61,7 @@ let TargetPrefix = "spv" in { def int_spv_thread_id : Intrinsic<[llvm_i32_ty], [llvm_i32_ty], [IntrNoMem, IntrWillReturn]>; def int_spv_group_id : Intrinsic<[llvm_i32_ty], [llvm_i32_ty], [IntrNoMem, IntrWillReturn]>; def int_spv_thread_id_in_group : Intrinsic<[llvm_i32_ty], [llvm_i32_ty], [IntrNoMem, IntrWillReturn]>; + def int_spv_flattened_thread_id_in_group : Intrinsic<[llvm_i32_ty], [], [IntrNoMem, IntrWillReturn]>; def int_spv_all : DefaultAttrsIntrinsic<[llvm_i1_ty], [llvm_any_ty], [IntrNoMem]>; def int_spv_any : DefaultAttrsIntrinsic<[llvm_i1_ty], [llvm_any_ty], [IntrNoMem]>; def int_spv_cross : DefaultAttrsIntrinsic<[llvm_anyfloat_ty], [LLVMMatchType<0>, LLVMMatchType<0>], [IntrNoMem]>; diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp index b188f36ca9a9e..d82675d4b8ef5 100644 --- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp @@ -341,6 +341,9 @@ class SPIRVInstructionSelector : public InstructionSelector { bool loadVec3BuiltinInputID(SPIRV::BuiltIn::BuiltIn BuiltInValue, Register ResVReg, const SPIRVType *ResType, MachineInstr &I) const; + bool loadBuiltinInputID(SPIRV::BuiltIn::BuiltIn BuiltInValue, + Register ResVReg, const SPIRVType *ResType, + MachineInstr &I) const; bool loadHandleBeforePosition(Register &HandleReg, const SPIRVType *ResType, GIntrinsic &HandleDef, MachineInstr &Pos) const; }; @@ -3059,6 +3062,15 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg, // builtin variable return loadVec3BuiltinInputID(SPIRV::BuiltIn::WorkgroupId, ResVReg, ResType, I); + case Intrinsic::spv_flattened_thread_id_in_group: + // The HLSL SV_GroupIndex semantic is lowered to + // llvm.spv.flattened.thread.id.in.group() intrinsic in LLVM IR for SPIR-V + // backend. + // + // In SPIR-V backend, llvm.spv.flattened.thread.id.in.group is translated to + // a `LocalInvocationIndex` builtin variable + return loadBuiltinInputID(SPIRV::BuiltIn::LocalInvocationIndex, ResVReg, + ResType, I); case Intrinsic::spv_fdot: return selectFloatDot(ResVReg, ResType, I); case Intrinsic::spv_udot: @@ -4005,6 +4017,40 @@ bool SPIRVInstructionSelector::loadVec3BuiltinInputID( return Result && MIB.constrainAllUses(TII, TRI, RBI); } +// Generate the instructions to load 32-bit integer builtin input IDs/Indices. +// Like LocalInvocationIndex +bool SPIRVInstructionSelector::loadBuiltinInputID( + SPIRV::BuiltIn::BuiltIn BuiltInValue, Register ResVReg, + const SPIRVType *ResType, MachineInstr &I) const { + MachineIRBuilder MIRBuilder(I); + const SPIRVType *PtrType = GR.getOrCreateSPIRVPointerType( + ResType, MIRBuilder, SPIRV::StorageClass::Input); + + // Create new register for the input ID builtin variable. + Register NewRegister = + MIRBuilder.getMRI()->createVirtualRegister(GR.getRegClass(PtrType)); + MIRBuilder.getMRI()->setType( + NewRegister, + LLT::pointer(storageClassToAddressSpace(SPIRV::StorageClass::Input), + GR.getPointerSize())); + GR.assignSPIRVTypeToVReg(PtrType, NewRegister, MIRBuilder.getMF()); + + // Build global variable with the necessary decorations for the input ID + // builtin variable. + Register Variable = GR.buildGlobalVariable( + NewRegister, PtrType, getLinkStringForBuiltIn(BuiltInValue), nullptr, + SPIRV::StorageClass::Input, nullptr, true, true, + SPIRV::LinkageType::Import, MIRBuilder, false); + + // Load uint value from the global variable. + auto MIB = BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(SPIRV::OpLoad)) + .addDef(ResVReg) + .addUse(GR.getSPIRVTypeID(ResType)) + .addUse(Variable); + + return MIB.constrainAllUses(TII, TRI, RBI); +} + SPIRVType *SPIRVInstructionSelector::widenTypeToVec4(const SPIRVType *Type, MachineInstr &I) const { MachineIRBuilder MIRBuilder(I); diff --git a/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/SV_GroupIndex.ll b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/SV_GroupIndex.ll new file mode 100644 index 0000000000000..83caa62150cda --- /dev/null +++ b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/SV_GroupIndex.ll @@ -0,0 +1,31 @@ +; RUN: llc -O0 -verify-machineinstrs -mtriple=spirv-vulkan-unknown %s -o - | FileCheck %s +; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-vulkan-unknown %s -o - -filetype=obj | spirv-val --target-env vulkan1.3 %} + +; CHECK-DAG: %[[#int:]] = OpTypeInt 32 0 +; CHECK-DAG: %[[#ptr_Input_int:]] = OpTypePointer Input %[[#int]] +; CHECK-DAG: %[[#LocalInvocationIndex:]] = OpVariable %[[#ptr_Input_int]] Input + +; CHECK-DAG: OpEntryPoint GLCompute {{.*}} %[[#LocalInvocationIndex]] +; CHECK-DAG: OpName %[[#LocalInvocationIndex]] "__spirv_BuiltInLocalInvocationIndex" +; CHECK-DAG: OpDecorate %[[#LocalInvocationIndex]] BuiltIn LocalInvocationIndex + +target triple = "spirv-unknown-vulkan-library" + +declare void @local_index_user(i32) + +; Function Attrs: convergent noinline norecurse +define void @main() #1 { +entry: + +; CHECK: %[[#load:]] = OpLoad %[[#int]] %[[#LocalInvocationIndex]] + %1 = call i32 @llvm.spv.flattened.thread.id.in.group() + + call spir_func void @local_index_user(i32 %1) + ret void +} + +; Function Attrs: nounwind willreturn memory(none) +declare i32 @llvm.spv.flattened.thread.id.in.group() #3 + +attributes #1 = { convergent noinline norecurse "hlsl.numthreads"="1,1,1" "hlsl.shader"="compute" "no-trapping-math"="true" "stack-protector-buffer-size"="8" } +attributes #3 = { nounwind willreturn memory(none) }