Skip to content

[HLSL][SPIRV] Use resource names #143412

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 8 additions & 9 deletions clang/lib/CodeGen/CGHLSLBuiltins.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -295,17 +295,16 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID,
Value *SpaceOp = EmitScalarExpr(E->getArg(2));
Value *RangeOp = EmitScalarExpr(E->getArg(3));
Value *IndexOp = EmitScalarExpr(E->getArg(4));
Value *Name = EmitScalarExpr(E->getArg(5));
// FIXME: NonUniformResourceIndex bit is not yet implemented
// (llvm/llvm-project#135452)
Value *NonUniform =
llvm::ConstantInt::get(llvm::Type::getInt1Ty(getLLVMContext()), false);

auto [IntrinsicID, HasNameArg] =
llvm::Intrinsic::ID IntrinsicID =
CGM.getHLSLRuntime().getCreateHandleFromBindingIntrinsic();
SmallVector<Value *> Args{SpaceOp, RegisterOp, RangeOp, IndexOp,
NonUniform};
if (HasNameArg)
Args.push_back(EmitScalarExpr(E->getArg(5)));
SmallVector<Value *> Args{SpaceOp, RegisterOp, RangeOp,
IndexOp, NonUniform, Name};
return Builder.CreateIntrinsic(HandleTy, IntrinsicID, Args);
}
case Builtin::BI__builtin_hlsl_resource_handlefromimplicitbinding: {
Expand All @@ -314,16 +313,16 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID,
Value *RangeOp = EmitScalarExpr(E->getArg(2));
Value *IndexOp = EmitScalarExpr(E->getArg(3));
Value *OrderID = EmitScalarExpr(E->getArg(4));
Value *Name = EmitScalarExpr(E->getArg(5));
// FIXME: NonUniformResourceIndex bit is not yet implemented
// (llvm/llvm-project#135452)
Value *NonUniform =
llvm::ConstantInt::get(llvm::Type::getInt1Ty(getLLVMContext()), false);

auto [IntrinsicID, HasNameArg] =
llvm::Intrinsic::ID IntrinsicID =
CGM.getHLSLRuntime().getCreateHandleFromImplicitBindingIntrinsic();
SmallVector<Value *> Args{OrderID, SpaceOp, RangeOp, IndexOp, NonUniform};
if (HasNameArg)
Args.push_back(EmitScalarExpr(E->getArg(5)));
SmallVector<Value *> Args{OrderID, SpaceOp, RangeOp,
IndexOp, NonUniform, Name};
return Builder.CreateIntrinsic(HandleTy, IntrinsicID, Args);
}
case Builtin::BI__builtin_hlsl_all: {
Expand Down
49 changes: 8 additions & 41 deletions clang/lib/CodeGen/CGHLSLRuntime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -237,35 +237,6 @@ static void fillPackoffsetLayout(const HLSLBufferDecl *BufDecl,
}
}

std::pair<llvm::Intrinsic::ID, bool>
CGHLSLRuntime::getCreateHandleFromBindingIntrinsic() {
switch (getArch()) {
case llvm::Triple::dxil:
return std::pair(llvm::Intrinsic::dx_resource_handlefrombinding, true);
case llvm::Triple::spirv:
return std::pair(llvm::Intrinsic::spv_resource_handlefrombinding, false);
default:
llvm_unreachable("Intrinsic resource_handlefrombinding not supported by "
"target architecture");
}
}

std::pair<llvm::Intrinsic::ID, bool>
CGHLSLRuntime::getCreateHandleFromImplicitBindingIntrinsic() {
switch (getArch()) {
case llvm::Triple::dxil:
return std::pair(llvm::Intrinsic::dx_resource_handlefromimplicitbinding,
true);
case llvm::Triple::spirv:
return std::pair(llvm::Intrinsic::spv_resource_handlefromimplicitbinding,
false);
default:
llvm_unreachable(
"Intrinsic resource_handlefromimplicitbinding not supported by "
"target architecture");
}
}

// Codegen for HLSLBufferDecl
void CGHLSLRuntime::addBuffer(const HLSLBufferDecl *BufDecl) {

Expand Down Expand Up @@ -595,31 +566,27 @@ void CGHLSLRuntime::initializeBufferFromBinding(const HLSLBufferDecl *BufDecl,
llvm::ConstantInt::get(CGM.IntTy, RBA ? RBA->getSpaceNumber() : 0);
Value *Name = nullptr;

auto [IntrinsicID, HasNameArg] =
llvm::Intrinsic::ID IntrinsicID =
RBA->hasRegisterSlot()
? CGM.getHLSLRuntime().getCreateHandleFromBindingIntrinsic()
: CGM.getHLSLRuntime().getCreateHandleFromImplicitBindingIntrinsic();

if (HasNameArg) {
std::string Str(BufDecl->getName());
std::string GlobalName(Str + ".str");
Name = CGM.GetAddrOfConstantCString(Str, GlobalName.c_str()).getPointer();
}
std::string Str(BufDecl->getName());
std::string GlobalName(Str + ".str");
Name = CGM.GetAddrOfConstantCString(Str, GlobalName.c_str()).getPointer();

// buffer with explicit binding
if (RBA->hasRegisterSlot()) {
auto *RegSlot = llvm::ConstantInt::get(CGM.IntTy, RBA->getSlotNumber());
SmallVector<Value *> Args{Space, RegSlot, RangeSize, Index, NonUniform};
if (Name)
Args.push_back(Name);
SmallVector<Value *> Args{Space, RegSlot, RangeSize,
Index, NonUniform, Name};
initializeBuffer(CGM, GV, IntrinsicID, Args);
} else {
// buffer with implicit binding
auto *OrderID =
llvm::ConstantInt::get(CGM.IntTy, RBA->getImplicitBindingOrderID());
SmallVector<Value *> Args{OrderID, Space, RangeSize, Index, NonUniform};
if (Name)
Args.push_back(Name);
SmallVector<Value *> Args{OrderID, Space, RangeSize,
Index, NonUniform, Name};
initializeBuffer(CGM, GV, IntrinsicID, Args);
}
}
Expand Down
13 changes: 4 additions & 9 deletions clang/lib/CodeGen/CGHLSLRuntime.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,10 @@ class CGHLSLRuntime {

GENERATE_HLSL_INTRINSIC_FUNCTION(CreateResourceGetPointer,
resource_getpointer)
GENERATE_HLSL_INTRINSIC_FUNCTION(CreateHandleFromBinding,
resource_handlefrombinding)
GENERATE_HLSL_INTRINSIC_FUNCTION(CreateHandleFromImplicitBinding,
resource_handlefromimplicitbinding)
GENERATE_HLSL_INTRINSIC_FUNCTION(BufferUpdateCounter, resource_updatecounter)
GENERATE_HLSL_INTRINSIC_FUNCTION(GroupMemoryBarrierWithGroupSync,
group_memory_barrier_with_group_sync)
Expand All @@ -125,15 +129,6 @@ class CGHLSLRuntime {
// End of reserved area for HLSL intrinsic getters.
//===----------------------------------------------------------------------===//

// Returns ID of the intrinsic that initializes resource handle from binding
// and a bool value indicating whether the last argument of the intrinsic is
// the resource name (not all targets need that).
std::pair<llvm::Intrinsic::ID, bool> getCreateHandleFromBindingIntrinsic();

// Same as above but for implicit binding.
std::pair<llvm::Intrinsic::ID, bool>
getCreateHandleFromImplicitBindingIntrinsic();

protected:
CodeGenModule &CGM;

Expand Down
16 changes: 8 additions & 8 deletions llvm/include/llvm/IR/IntrinsicsSPIRV.td
Original file line number Diff line number Diff line change
Expand Up @@ -115,15 +115,15 @@ let TargetPrefix = "spv" in {
// array size of the binding, as well as an index and an indicator
// whether that index may be non-uniform.
def int_spv_resource_handlefrombinding
: DefaultAttrsIntrinsic<
[llvm_any_ty],
[llvm_i32_ty, llvm_i32_ty, llvm_i32_ty, llvm_i32_ty, llvm_i1_ty],
[IntrNoMem]>;
: DefaultAttrsIntrinsic<[llvm_any_ty],
[llvm_i32_ty, llvm_i32_ty, llvm_i32_ty,
llvm_i32_ty, llvm_i1_ty, llvm_ptr_ty],
[IntrNoMem]>;
def int_spv_resource_handlefromimplicitbinding
: DefaultAttrsIntrinsic<
[llvm_any_ty],
[llvm_i32_ty, llvm_i32_ty, llvm_i32_ty, llvm_i32_ty, llvm_i1_ty],
[IntrNoMem]>;
: DefaultAttrsIntrinsic<[llvm_any_ty],
[llvm_i32_ty, llvm_i32_ty, llvm_i32_ty,
llvm_i32_ty, llvm_i1_ty, llvm_ptr_ty],
[IntrNoMem]>;

def int_spv_firstbituhigh : DefaultAttrsIntrinsic<[LLVMScalarOrSameVectorWidth<0, llvm_i32_ty>], [llvm_anyint_ty], [IntrNoMem]>;
def int_spv_firstbitshigh : DefaultAttrsIntrinsic<[LLVMScalarOrSameVectorWidth<0, llvm_i32_ty>], [llvm_anyint_ty], [IntrNoMem]>;
Expand Down
94 changes: 1 addition & 93 deletions llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -799,107 +799,15 @@ Register SPIRVGlobalRegistry::buildGlobalVariable(
return Reg;
}

static std::string GetSpirvImageTypeName(const SPIRVType *Type,
MachineIRBuilder &MIRBuilder,
const std::string &Prefix,
SPIRVGlobalRegistry &GR);

// Returns a name based on the Type. Notes that this does not look at
// decorations, and will return the same string for two types that are the same
// except for decorations.
static std::string buildSpirvTypeName(const SPIRVType *Type,
MachineIRBuilder &MIRBuilder,
SPIRVGlobalRegistry &GR) {
switch (Type->getOpcode()) {
case SPIRV::OpTypeSampledImage: {
return GetSpirvImageTypeName(Type, MIRBuilder, "sampled_image_", GR);
}
case SPIRV::OpTypeImage: {
return GetSpirvImageTypeName(Type, MIRBuilder, "image_", GR);
}
case SPIRV::OpTypeArray: {
MachineRegisterInfo *MRI = MIRBuilder.getMRI();
Register ElementTypeReg = Type->getOperand(1).getReg();
auto *ElementType = MRI->getUniqueVRegDef(ElementTypeReg);
uint32_t ArraySize = getArrayComponentCount(MRI, Type);
return (buildSpirvTypeName(ElementType, MIRBuilder, GR) + Twine("[") +
Twine(ArraySize) + Twine("]"))
.str();
}
case SPIRV::OpTypeFloat:
return ("f" + Twine(Type->getOperand(1).getImm())).str();
case SPIRV::OpTypeSampler:
return ("sampler");
case SPIRV::OpTypeInt:
if (Type->getOperand(2).getImm())
return ("i" + Twine(Type->getOperand(1).getImm())).str();
return ("u" + Twine(Type->getOperand(1).getImm())).str();
case SPIRV::OpTypePointer: {
uint32_t StorageClass = GR.getPointerStorageClass(Type);
SPIRVType *PointeeType = GR.getPointeeType(Type);
return ("p_" + Twine(StorageClass) + Twine("_") +
buildSpirvTypeName(PointeeType, MIRBuilder, GR))
.str();
}
case SPIRV::OpTypeStruct: {
std::string TypeName = "{";
for (uint32_t I = 1; I < Type->getNumOperands(); ++I) {
SPIRVType *MemberType =
GR.getSPIRVTypeForVReg(Type->getOperand(I).getReg());
TypeName += '_' + buildSpirvTypeName(MemberType, MIRBuilder, GR);
}
return TypeName + "}";
}
case SPIRV::OpTypeVector: {
MachineRegisterInfo *MRI = MIRBuilder.getMRI();
Register ElementTypeReg = Type->getOperand(1).getReg();
auto *ElementType = MRI->getUniqueVRegDef(ElementTypeReg);
uint32_t VectorSize = GR.getScalarOrVectorComponentCount(Type);
return (buildSpirvTypeName(ElementType, MIRBuilder, GR) + Twine("[") +
Twine(VectorSize) + Twine("]"))
.str();
}
case SPIRV::OpTypeRuntimeArray: {
MachineRegisterInfo *MRI = MIRBuilder.getMRI();
Register ElementTypeReg = Type->getOperand(1).getReg();
auto *ElementType = MRI->getUniqueVRegDef(ElementTypeReg);
uint32_t ArraySize = 0;
return (buildSpirvTypeName(ElementType, MIRBuilder, GR) + Twine("[") +
Twine(ArraySize) + Twine("]"))
.str();
}
default:
llvm_unreachable("Trying to the the name of an unknown type.");
}
}

static std::string GetSpirvImageTypeName(const SPIRVType *Type,
MachineIRBuilder &MIRBuilder,
const std::string &Prefix,
SPIRVGlobalRegistry &GR) {
Register SampledTypeReg = Type->getOperand(1).getReg();
auto *SampledType = MIRBuilder.getMRI()->getUniqueVRegDef(SampledTypeReg);
std::string TypeName =
Prefix + buildSpirvTypeName(SampledType, MIRBuilder, GR);
for (uint32_t I = 2; I < Type->getNumOperands(); ++I) {
TypeName = (TypeName + '_' + Twine(Type->getOperand(I).getImm())).str();
}
return TypeName;
}

Register SPIRVGlobalRegistry::getOrCreateGlobalVariableWithBinding(
const SPIRVType *VarType, uint32_t Set, uint32_t Binding,
const SPIRVType *VarType, uint32_t Set, uint32_t Binding, StringRef Name,
MachineIRBuilder &MIRBuilder) {
Register VarReg =
MIRBuilder.getMRI()->createVirtualRegister(&SPIRV::iIDRegClass);

// TODO(138533): The name should come from the llvm-ir, but how that name will
// be passed from the HLSL to the backend has not been decided. Using this
// place holder for now.
std::string Name =
("__resource_" + buildSpirvTypeName(VarType, MIRBuilder, *this) + "_" +
Twine(Set) + "_" + Twine(Binding))
.str();
buildGlobalVariable(VarReg, VarType, Name, nullptr,
getPointerStorageClass(VarType), nullptr, false, false,
SPIRV::LinkageType::Import, MIRBuilder, false);
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h
Original file line number Diff line number Diff line change
Expand Up @@ -548,6 +548,7 @@ class SPIRVGlobalRegistry : public SPIRVIRMapping {
bool IsInstSelector);
Register getOrCreateGlobalVariableWithBinding(const SPIRVType *VarType,
uint32_t Set, uint32_t Binding,
StringRef Name,
MachineIRBuilder &MIRBuilder);

// Convenient helpers for getting types with check for duplicates.
Expand Down
16 changes: 10 additions & 6 deletions llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ class SPIRVInstructionSelector : public InstructionSelector {
SPIRV::StorageClass::StorageClass SC,
uint32_t Set, uint32_t Binding,
uint32_t ArraySize, Register IndexReg,
bool IsNonUniform,
bool IsNonUniform, StringRef Name,
MachineIRBuilder MIRBuilder) const;
SPIRVType *widenTypeToVec4(const SPIRVType *Type, MachineInstr &I) const;
bool extractSubvector(Register &ResVReg, const SPIRVType *ResType,
Expand Down Expand Up @@ -3380,22 +3380,22 @@ bool SPIRVInstructionSelector::selectImageWriteIntrinsic(
Register SPIRVInstructionSelector::buildPointerToResource(
const SPIRVType *SpirvResType, SPIRV::StorageClass::StorageClass SC,
uint32_t Set, uint32_t Binding, uint32_t ArraySize, Register IndexReg,
bool IsNonUniform, MachineIRBuilder MIRBuilder) const {
bool IsNonUniform, StringRef Name, MachineIRBuilder MIRBuilder) const {
const Type *ResType = GR.getTypeForSPIRVType(SpirvResType);
if (ArraySize == 1) {
SPIRVType *PtrType =
GR.getOrCreateSPIRVPointerType(ResType, MIRBuilder, SC);
assert(GR.getPointeeType(PtrType) == SpirvResType &&
"SpirvResType did not have an explicit layout.");
return GR.getOrCreateGlobalVariableWithBinding(PtrType, Set, Binding,
return GR.getOrCreateGlobalVariableWithBinding(PtrType, Set, Binding, Name,
MIRBuilder);
}

const Type *VarType = ArrayType::get(const_cast<Type *>(ResType), ArraySize);
SPIRVType *VarPointerType =
GR.getOrCreateSPIRVPointerType(VarType, MIRBuilder, SC);
Register VarReg = GR.getOrCreateGlobalVariableWithBinding(
VarPointerType, Set, Binding, MIRBuilder);
VarPointerType, Set, Binding, Name, MIRBuilder);

SPIRVType *ResPointerType =
GR.getOrCreateSPIRVPointerType(ResType, MIRBuilder, SC);
Expand Down Expand Up @@ -4081,6 +4081,9 @@ bool SPIRVInstructionSelector::loadHandleBeforePosition(
uint32_t ArraySize = foldImm(HandleDef.getOperand(4), MRI);
Register IndexReg = HandleDef.getOperand(5).getReg();
bool IsNonUniform = ArraySize > 1 && foldImm(HandleDef.getOperand(6), MRI);
std::string Name =
getStringValueFromReg(HandleDef.getOperand(7).getReg(), *MRI);

bool IsStructuredBuffer = ResType->getOpcode() == SPIRV::OpTypePointer;
MachineIRBuilder MIRBuilder(HandleDef);
SPIRVType *VarType = ResType;
Expand All @@ -4091,8 +4094,9 @@ bool SPIRVInstructionSelector::loadHandleBeforePosition(
SC = GR.getPointerStorageClass(ResType);
}

Register VarReg = buildPointerToResource(VarType, SC, Set, Binding, ArraySize,
IndexReg, IsNonUniform, MIRBuilder);
Register VarReg =
buildPointerToResource(VarType, SC, Set, Binding, ArraySize, IndexReg,
IsNonUniform, Name, MIRBuilder);

if (IsNonUniform)
buildOpDecorate(HandleReg, HandleDef, TII, SPIRV::Decoration::NonUniformEXT,
Expand Down
10 changes: 10 additions & 0 deletions llvm/lib/Target/SPIRV/SPIRVUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,16 @@ std::string getStringImm(const MachineInstr &MI, unsigned StartIndex) {
return getSPIRVStringOperand(MI, StartIndex);
}

std::string getStringValueFromReg(Register Reg, MachineRegisterInfo &MRI) {
MachineInstr *Def = getVRegDef(MRI, Reg);
assert(Def && Def->getOpcode() == TargetOpcode::G_GLOBAL_VALUE &&
"Expected G_GLOBAL_VALUE");
const GlobalValue *GV = Def->getOperand(1).getGlobal();
Value *V = GV->getOperand(0);
const ConstantDataArray *CDA = cast<ConstantDataArray>(V);
return CDA->getAsCString().str();
}

void addNumImm(const APInt &Imm, MachineInstrBuilder &MIB) {
const auto Bitwidth = Imm.getBitWidth();
if (Bitwidth == 1)
Expand Down
4 changes: 4 additions & 0 deletions llvm/lib/Target/SPIRV/SPIRVUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,10 @@ void addStringImm(const StringRef &Str, IRBuilder<> &B,
// the reverse of the logic in addStringImm.
std::string getStringImm(const MachineInstr &MI, unsigned StartIndex);

// Returns the string constant that the register refers to. It is assumed that
// Reg is a global value that contains a string.
std::string getStringValueFromReg(Register Reg, MachineRegisterInfo &MRI);

// Add the given numerical immediate to MIB.
void addNumImm(const APInt &Imm, MachineInstrBuilder &MIB);

Expand Down
Loading
Loading