Skip to content

[HLSL] Resource initialization by constructors #135120

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 10 commits into from
Apr 30, 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
12 changes: 12 additions & 0 deletions clang/include/clang/Basic/Builtins.td
Original file line number Diff line number Diff line change
Expand Up @@ -4801,6 +4801,18 @@ def HLSLResourceGetPointer : LangBuiltin<"HLSL_LANG"> {
let Prototype = "void(...)";
}

def HLSLResourceUninitializedHandle : LangBuiltin<"HLSL_LANG"> {
let Spellings = ["__builtin_hlsl_resource_uninitializedhandle"];
let Attributes = [NoThrow];
let Prototype = "void(...)";
}

def HLSLResourceHandleFromBinding : LangBuiltin<"HLSL_LANG"> {
let Spellings = ["__builtin_hlsl_resource_handlefrombinding"];
let Attributes = [NoThrow];
let Prototype = "void(...)";
}

def HLSLAll : LangBuiltin<"HLSL_LANG"> {
let Spellings = ["__builtin_hlsl_all"];
let Attributes = [NoThrow, Const];
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Sema/SemaHLSL.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ class SemaHLSL : public SemaBase {
HLSLParamModifierAttr::Spelling Spelling);
void ActOnTopLevelFunction(FunctionDecl *FD);
void ActOnVariableDeclarator(VarDecl *VD);
bool ActOnUninitializedVarDecl(VarDecl *D);
void ActOnEndOfTranslationUnit(TranslationUnitDecl *TU);
void CheckEntryPoint(FunctionDecl *FD);
void CheckSemanticAnnotation(FunctionDecl *EntryPoint, const Decl *Param,
Expand Down
18 changes: 18 additions & 0 deletions clang/lib/CodeGen/CGHLSLBuiltins.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,24 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID,
RetTy, CGM.getHLSLRuntime().getCreateResourceGetPointerIntrinsic(),
ArrayRef<Value *>{HandleOp, IndexOp});
}
case Builtin::BI__builtin_hlsl_resource_uninitializedhandle: {
llvm::Type *HandleTy = CGM.getTypes().ConvertType(E->getType());
return llvm::PoisonValue::get(HandleTy);
}
case Builtin::BI__builtin_hlsl_resource_handlefrombinding: {
llvm::Type *HandleTy = CGM.getTypes().ConvertType(E->getType());
Value *RegisterOp = EmitScalarExpr(E->getArg(1));
Value *SpaceOp = EmitScalarExpr(E->getArg(2));
Value *RangeOp = EmitScalarExpr(E->getArg(3));
Value *IndexOp = EmitScalarExpr(E->getArg(4));
// FIXME: NonUniformResourceIndex bit is not yet implemented
// (llvm/llvm-project#135452)
Value *NonUniform =
llvm::ConstantInt::get(llvm::Type::getInt1Ty(getLLVMContext()), false);
return Builder.CreateIntrinsic(
HandleTy, CGM.getHLSLRuntime().getCreateHandleFromBindingIntrinsic(),
ArrayRef<Value *>{SpaceOp, RegisterOp, RangeOp, IndexOp, NonUniform});
}
case Builtin::BI__builtin_hlsl_all: {
Value *Op0 = EmitScalarExpr(E->getArg(0));
return Builder.CreateIntrinsic(
Expand Down
75 changes: 28 additions & 47 deletions clang/lib/CodeGen/CGHLSLRuntime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,9 @@ using namespace llvm;

using llvm::hlsl::CBufferRowSizeInBytes;

static void createResourceInitFn(CodeGenModule &CGM, llvm::GlobalVariable *GV,
unsigned Slot, unsigned Space);
static void initializeBufferFromBinding(CodeGenModule &CGM,
llvm::GlobalVariable *GV, unsigned Slot,
unsigned Space);

namespace {

Expand Down Expand Up @@ -255,14 +256,14 @@ void CGHLSLRuntime::addBuffer(const HLSLBufferDecl *BufDecl) {
// Add globals for constant buffer elements and create metadata nodes
emitBufferGlobalsAndMetadata(BufDecl, BufGV);

// Resource initialization
// Initialize cbuffer from binding (implicit or explicit)
const HLSLResourceBindingAttr *RBA =
BufDecl->getAttr<HLSLResourceBindingAttr>();
// FIXME: handle implicit binding if no binding attribute is found
// (llvm/llvm-project#110722)
if (RBA)
createResourceInitFn(CGM, BufGV, RBA->getSlotNumber(),
RBA->getSpaceNumber());
initializeBufferFromBinding(CGM, BufGV, RBA->getSlotNumber(),
RBA->getSpaceNumber());
}

llvm::TargetExtType *
Expand Down Expand Up @@ -505,15 +506,15 @@ void CGHLSLRuntime::generateGlobalCtorDtorCalls() {
}
}

static void createResourceInitFn(CodeGenModule &CGM, llvm::GlobalVariable *GV,
unsigned Slot, unsigned Space) {
LLVMContext &Ctx = CGM.getLLVMContext();
llvm::Type *Int1Ty = llvm::Type::getInt1Ty(Ctx);
static void initializeBuffer(CodeGenModule &CGM, llvm::GlobalVariable *GV,
Intrinsic::ID IntrID,
ArrayRef<llvm::Value *> Args) {

LLVMContext &Ctx = CGM.getLLVMContext();
llvm::Function *InitResFunc = llvm::Function::Create(
llvm::FunctionType::get(CGM.VoidTy, false),
llvm::GlobalValue::InternalLinkage,
("_init_resource_" + GV->getName()).str(), CGM.getModule());
("_init_buffer_" + GV->getName()).str(), CGM.getModule());
InitResFunc->addFnAttr(llvm::Attribute::AlwaysInline);

llvm::BasicBlock *EntryBB =
Expand All @@ -522,28 +523,12 @@ static void createResourceInitFn(CodeGenModule &CGM, llvm::GlobalVariable *GV,
const DataLayout &DL = CGM.getModule().getDataLayout();
Builder.SetInsertPoint(EntryBB);

// Make sure the global variable is resource handle (cbuffer) or
// resource class (=class where the first element is a resource handle).
// Make sure the global variable is buffer resource handle
llvm::Type *HandleTy = GV->getValueType();
assert((HandleTy->isTargetExtTy() ||
(HandleTy->isStructTy() &&
HandleTy->getStructElementType(0)->isTargetExtTy())) &&
"unexpected type of the global");
if (!HandleTy->isTargetExtTy())
HandleTy = HandleTy->getStructElementType(0);
assert(HandleTy->isTargetExtTy() && "unexpected type of the buffer global");

llvm::Value *Args[] = {
llvm::ConstantInt::get(CGM.IntTy, Space), /* reg_space */
llvm::ConstantInt::get(CGM.IntTy, Slot), /* lower_bound */
// FIXME: resource arrays are not yet implemented
llvm::ConstantInt::get(CGM.IntTy, 1), /* range_size */
llvm::ConstantInt::get(CGM.IntTy, 0), /* index */
// FIXME: NonUniformResourceIndex bit is not yet implemented
llvm::ConstantInt::get(Int1Ty, false) /* non-uniform */
};
llvm::Value *CreateHandle = Builder.CreateIntrinsic(
/*ReturnType=*/HandleTy,
CGM.getHLSLRuntime().getCreateHandleFromBindingIntrinsic(), Args, nullptr,
/*ReturnType=*/HandleTy, IntrID, Args, nullptr,
Twine(GV->getName()).concat("_h"));

llvm::Value *HandleRef = Builder.CreateStructGEP(GV->getValueType(), GV, 0);
Expand All @@ -554,24 +539,20 @@ static void createResourceInitFn(CodeGenModule &CGM, llvm::GlobalVariable *GV,
CGM.AddCXXGlobalInit(InitResFunc);
}

void CGHLSLRuntime::handleGlobalVarDefinition(const VarDecl *VD,
llvm::GlobalVariable *GV) {

// If the global variable has resource binding, create an init function
// for the resource
const HLSLResourceBindingAttr *RBA = VD->getAttr<HLSLResourceBindingAttr>();
if (!RBA)
// FIXME: collect unbound resources for implicit binding resolution later
// on?
return;

if (!VD->getType().getTypePtr()->isHLSLResourceRecord())
// FIXME: Only simple declarations of resources are supported for now.
// Arrays of resources or resources in user defined classes are
// not implemented yet.
return;

createResourceInitFn(CGM, GV, RBA->getSlotNumber(), RBA->getSpaceNumber());
static void initializeBufferFromBinding(CodeGenModule &CGM,
llvm::GlobalVariable *GV, unsigned Slot,
unsigned Space) {
llvm::Type *Int1Ty = llvm::Type::getInt1Ty(CGM.getLLVMContext());
llvm::Value *Args[] = {
llvm::ConstantInt::get(CGM.IntTy, Space), /* reg_space */
llvm::ConstantInt::get(CGM.IntTy, Slot), /* lower_bound */
llvm::ConstantInt::get(CGM.IntTy, 1), /* range_size */
llvm::ConstantInt::get(CGM.IntTy, 0), /* index */
llvm::ConstantInt::get(Int1Ty, false) /* non-uniform */
};
initializeBuffer(CGM, GV,
CGM.getHLSLRuntime().getCreateHandleFromBindingIntrinsic(),
Args);
}

llvm::Instruction *CGHLSLRuntime::getConvergenceToken(BasicBlock &BB) {
Expand Down
1 change: 0 additions & 1 deletion clang/lib/CodeGen/CGHLSLRuntime.h
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,6 @@ class CGHLSLRuntime {

void emitEntryFunction(const FunctionDecl *FD, llvm::Function *Fn);
void setHLSLFunctionAttributes(const FunctionDecl *FD, llvm::Function *Fn);
void handleGlobalVarDefinition(const VarDecl *VD, llvm::GlobalVariable *Var);

llvm::Instruction *getConvergenceToken(llvm::BasicBlock &BB);

Expand Down
3 changes: 0 additions & 3 deletions clang/lib/CodeGen/CodeGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5760,9 +5760,6 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D,
getCUDARuntime().handleVarRegistration(D, *GV);
}

if (LangOpts.HLSL)
getHLSLRuntime().handleGlobalVarDefinition(D, GV);

GV->setInitializer(Init);
if (emitter)
emitter->finalize(GV);
Expand Down
36 changes: 32 additions & 4 deletions clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,7 @@ void BuiltinTypeMethodBuilder::createDecl() {

// create params & set them to the function prototype
SmallVector<ParmVarDecl *> ParmDecls;
unsigned CurScopeDepth = DeclBuilder.SemaRef.getCurScope()->getDepth();
auto FnProtoLoc =
Method->getTypeSourceInfo()->getTypeLoc().getAs<FunctionProtoTypeLoc>();
for (int I = 0, E = Params.size(); I != E; I++) {
Expand All @@ -414,6 +415,7 @@ void BuiltinTypeMethodBuilder::createDecl() {
HLSLParamModifierAttr::Create(AST, SourceRange(), MP.Modifier);
Parm->addAttr(Mod);
}
Parm->setScopeInfo(CurScopeDepth, I);
ParmDecls.push_back(Parm);
FnProtoLoc.setParam(I, Parm);
}
Expand Down Expand Up @@ -447,10 +449,14 @@ BuiltinTypeMethodBuilder::callBuiltin(StringRef BuiltinName,
AST, NestedNameSpecifierLoc(), SourceLocation(), FD, false,
FD->getNameInfo(), AST.BuiltinFnTy, VK_PRValue);

auto *ImpCast = ImplicitCastExpr::Create(
AST, AST.getPointerType(FD->getType()), CK_BuiltinFnToFnPtr, DRE, nullptr,
VK_PRValue, FPOptionsOverride());

if (ReturnType.isNull())
ReturnType = FD->getReturnType();

Expr *Call = CallExpr::Create(AST, DRE, Args, ReturnType, VK_PRValue,
Expr *Call = CallExpr::Create(AST, ImpCast, Args, ReturnType, VK_PRValue,
SourceLocation(), FPOptionsOverride());
StmtsList.push_back(Call);
return *this;
Expand Down Expand Up @@ -632,11 +638,33 @@ BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addDefaultHandleConstructor() {
if (Record->isCompleteDefinition())
return *this;

// FIXME: initialize handle to poison value; this can be added after
// resource constructor from binding is implemented, otherwise the handle
// value will get overwritten.
using PH = BuiltinTypeMethodBuilder::PlaceHolder;
QualType HandleType = getResourceHandleField()->getType();
return BuiltinTypeMethodBuilder(*this, "", SemaRef.getASTContext().VoidTy,
false, true)
.callBuiltin("__builtin_hlsl_resource_uninitializedhandle", HandleType,
PH::Handle)
.assign(PH::Handle, PH::LastStmt)
.finalize();
}

BuiltinTypeDeclBuilder &
BuiltinTypeDeclBuilder::addHandleConstructorFromBinding() {
if (Record->isCompleteDefinition())
return *this;

using PH = BuiltinTypeMethodBuilder::PlaceHolder;
ASTContext &AST = SemaRef.getASTContext();
QualType HandleType = getResourceHandleField()->getType();

return BuiltinTypeMethodBuilder(*this, "", AST.VoidTy, false, true)
.addParam("registerNo", AST.UnsignedIntTy)
.addParam("spaceNo", AST.UnsignedIntTy)
.addParam("range", AST.IntTy)
.addParam("index", AST.UnsignedIntTy)
.callBuiltin("__builtin_hlsl_resource_handlefrombinding", HandleType,
PH::Handle, PH::_0, PH::_1, PH::_2, PH::_3)
.assign(PH::Handle, PH::LastStmt)
.finalize();
}

Expand Down
1 change: 1 addition & 0 deletions clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ class BuiltinTypeDeclBuilder {

// Builtin types methods
BuiltinTypeDeclBuilder &addDefaultHandleConstructor();
BuiltinTypeDeclBuilder &addHandleConstructorFromBinding();

// Builtin types methods
BuiltinTypeDeclBuilder &addLoadMethods();
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/Sema/HLSLExternalSemaSource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,8 @@ static BuiltinTypeDeclBuilder setupBufferType(CXXRecordDecl *Decl, Sema &S,
bool RawBuffer) {
return BuiltinTypeDeclBuilder(S, Decl)
.addHandleMember(RC, IsROV, RawBuffer)
.addDefaultHandleConstructor();
.addDefaultHandleConstructor()
.addHandleConstructorFromBinding();
}

// This function is responsible for constructing the constraint expression for
Expand Down
6 changes: 2 additions & 4 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14378,10 +14378,8 @@ void Sema::ActOnUninitializedDecl(Decl *RealDecl) {
Var->getType().getAddressSpace() == LangAS::opencl_local)
return;

// In HLSL, objects in the hlsl_constant address space are initialized
// externally, so don't synthesize an implicit initializer.
if (getLangOpts().HLSL &&
Var->getType().getAddressSpace() == LangAS::hlsl_constant)
// Handle HLSL uninitialized decls
if (getLangOpts().HLSL && HLSL().ActOnUninitializedVarDecl(Var))
return;

// C++03 [dcl.init]p9:
Expand Down
86 changes: 86 additions & 0 deletions clang/lib/Sema/SemaHLSL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include "clang/Sema/ParsedAttr.h"
#include "clang/Sema/Sema.h"
#include "clang/Sema/Template.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
Expand Down Expand Up @@ -2395,6 +2396,29 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {

break;
}
case Builtin::BI__builtin_hlsl_resource_uninitializedhandle: {
if (SemaRef.checkArgCount(TheCall, 1) ||
CheckResourceHandle(&SemaRef, TheCall, 0))
return true;
// use the type of the handle (arg0) as a return type
QualType ResourceTy = TheCall->getArg(0)->getType();
TheCall->setType(ResourceTy);
break;
}
case Builtin::BI__builtin_hlsl_resource_handlefrombinding: {
ASTContext &AST = SemaRef.getASTContext();
if (SemaRef.checkArgCount(TheCall, 5) ||
CheckResourceHandle(&SemaRef, TheCall, 0) ||
CheckArgTypeMatches(&SemaRef, TheCall->getArg(1), AST.UnsignedIntTy) ||
CheckArgTypeMatches(&SemaRef, TheCall->getArg(2), AST.UnsignedIntTy) ||
CheckArgTypeMatches(&SemaRef, TheCall->getArg(3), AST.IntTy) ||
CheckArgTypeMatches(&SemaRef, TheCall->getArg(4), AST.UnsignedIntTy))
return true;
// use the type of the handle (arg0) as a return type
QualType ResourceTy = TheCall->getArg(0)->getType();
TheCall->setType(ResourceTy);
break;
}
case Builtin::BI__builtin_hlsl_and:
case Builtin::BI__builtin_hlsl_or: {
if (SemaRef.checkArgCount(TheCall, 2))
Expand Down Expand Up @@ -3218,6 +3242,68 @@ void SemaHLSL::ActOnVariableDeclarator(VarDecl *VD) {
deduceAddressSpace(VD);
}

static bool initVarDeclWithCtor(Sema &S, VarDecl *VD,
MutableArrayRef<Expr *> Args) {
InitializedEntity Entity = InitializedEntity::InitializeVariable(VD);
InitializationKind Kind = InitializationKind::CreateDirect(
VD->getLocation(), SourceLocation(), SourceLocation());

InitializationSequence InitSeq(S, Entity, Kind, Args);
ExprResult Init = InitSeq.Perform(S, Entity, Kind, Args);

if (!Init.get())
return false;

VD->setInit(S.MaybeCreateExprWithCleanups(Init.get()));
VD->setInitStyle(VarDecl::CallInit);
S.CheckCompleteVariableDeclaration(VD);
return true;
}

static bool initGlobalResourceDecl(Sema &S, VarDecl *VD) {
HLSLResourceBindingAttr *RBA = VD->getAttr<HLSLResourceBindingAttr>();
if (!RBA)
// FIXME: add support for implicit binding (llvm/llvm-project#110722)
return false;

ASTContext &AST = S.getASTContext();
uint64_t UIntTySize = AST.getTypeSize(AST.UnsignedIntTy);
uint64_t IntTySize = AST.getTypeSize(AST.IntTy);
Expr *Args[] = {
IntegerLiteral::Create(AST, llvm::APInt(UIntTySize, RBA->getSlotNumber()),
AST.UnsignedIntTy, SourceLocation()),
IntegerLiteral::Create(AST,
llvm::APInt(UIntTySize, RBA->getSpaceNumber()),
AST.UnsignedIntTy, SourceLocation()),
IntegerLiteral::Create(AST, llvm::APInt(IntTySize, 1), AST.IntTy,
SourceLocation()),
IntegerLiteral::Create(AST, llvm::APInt(UIntTySize, 0), AST.UnsignedIntTy,
SourceLocation())};

return initVarDeclWithCtor(S, VD, Args);
}

// Returns true if the initialization has been handled.
// Returns false to use default initialization.
bool SemaHLSL::ActOnUninitializedVarDecl(VarDecl *VD) {
// Objects in the hlsl_constant address space are initialized
// externally, so don't synthesize an implicit initializer.
if (VD->getType().getAddressSpace() == LangAS::hlsl_constant)
return true;

// Initialize resources
if (!isResourceRecordTypeOrArrayOf(VD))
return false;

// FIXME: We currectly support only simple resources - no arrays of resources
// or resources in user defined structs.
// (llvm/llvm-project#133835, llvm/llvm-project#133837)
if (VD->getType()->isHLSLResourceRecord())
return initGlobalResourceDecl(SemaRef, VD);

return false;
}

// Walks though the global variable declaration, collects all resource binding
// requirements and adds them to Bindings
void SemaHLSL::collectResourceBindingsOnVarDecl(VarDecl *VD) {
Expand Down
Loading