Skip to content

Commit f175030

Browse files
authored
[HLSL] Resource initialization by constructors (llvm#135120)
- Adds resource constructor that takes explicit binding for all resource classes. - Updates implementation of default resource constructor to initialize resource handle to `poison`. - Removes initialization of resource classes from Codegen. - Initialization of `cbuffer` still needs to happen in `CGHLSLRuntime` because it does not have a corresponding resource class type. - Adds `ImplicitCastExpr` for builtin function calls. Sema adds these automatically when a method of a template class is instantiated, but some resource classes like `ByteAddressBuffer` are not templates so they need to have this added explicitly. Design proposal: llvm/wg-hlsl#197 Closes llvm#134154
1 parent 6bb4ce0 commit f175030

22 files changed

+684
-181
lines changed

clang/include/clang/Basic/Builtins.td

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4801,6 +4801,18 @@ def HLSLResourceGetPointer : LangBuiltin<"HLSL_LANG"> {
48014801
let Prototype = "void(...)";
48024802
}
48034803

4804+
def HLSLResourceUninitializedHandle : LangBuiltin<"HLSL_LANG"> {
4805+
let Spellings = ["__builtin_hlsl_resource_uninitializedhandle"];
4806+
let Attributes = [NoThrow];
4807+
let Prototype = "void(...)";
4808+
}
4809+
4810+
def HLSLResourceHandleFromBinding : LangBuiltin<"HLSL_LANG"> {
4811+
let Spellings = ["__builtin_hlsl_resource_handlefrombinding"];
4812+
let Attributes = [NoThrow];
4813+
let Prototype = "void(...)";
4814+
}
4815+
48044816
def HLSLAll : LangBuiltin<"HLSL_LANG"> {
48054817
let Spellings = ["__builtin_hlsl_all"];
48064818
let Attributes = [NoThrow, Const];

clang/include/clang/Sema/SemaHLSL.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ class SemaHLSL : public SemaBase {
105105
HLSLParamModifierAttr::Spelling Spelling);
106106
void ActOnTopLevelFunction(FunctionDecl *FD);
107107
void ActOnVariableDeclarator(VarDecl *VD);
108+
bool ActOnUninitializedVarDecl(VarDecl *D);
108109
void ActOnEndOfTranslationUnit(TranslationUnitDecl *TU);
109110
void CheckEntryPoint(FunctionDecl *FD);
110111
void CheckSemanticAnnotation(FunctionDecl *EntryPoint, const Decl *Param,

clang/lib/CodeGen/CGHLSLBuiltins.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,24 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID,
285285
RetTy, CGM.getHLSLRuntime().getCreateResourceGetPointerIntrinsic(),
286286
ArrayRef<Value *>{HandleOp, IndexOp});
287287
}
288+
case Builtin::BI__builtin_hlsl_resource_uninitializedhandle: {
289+
llvm::Type *HandleTy = CGM.getTypes().ConvertType(E->getType());
290+
return llvm::PoisonValue::get(HandleTy);
291+
}
292+
case Builtin::BI__builtin_hlsl_resource_handlefrombinding: {
293+
llvm::Type *HandleTy = CGM.getTypes().ConvertType(E->getType());
294+
Value *RegisterOp = EmitScalarExpr(E->getArg(1));
295+
Value *SpaceOp = EmitScalarExpr(E->getArg(2));
296+
Value *RangeOp = EmitScalarExpr(E->getArg(3));
297+
Value *IndexOp = EmitScalarExpr(E->getArg(4));
298+
// FIXME: NonUniformResourceIndex bit is not yet implemented
299+
// (llvm/llvm-project#135452)
300+
Value *NonUniform =
301+
llvm::ConstantInt::get(llvm::Type::getInt1Ty(getLLVMContext()), false);
302+
return Builder.CreateIntrinsic(
303+
HandleTy, CGM.getHLSLRuntime().getCreateHandleFromBindingIntrinsic(),
304+
ArrayRef<Value *>{SpaceOp, RegisterOp, RangeOp, IndexOp, NonUniform});
305+
}
288306
case Builtin::BI__builtin_hlsl_all: {
289307
Value *Op0 = EmitScalarExpr(E->getArg(0));
290308
return Builder.CreateIntrinsic(

clang/lib/CodeGen/CGHLSLRuntime.cpp

Lines changed: 28 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,9 @@ using namespace llvm;
4141

4242
using llvm::hlsl::CBufferRowSizeInBytes;
4343

44-
static void createResourceInitFn(CodeGenModule &CGM, llvm::GlobalVariable *GV,
45-
unsigned Slot, unsigned Space);
44+
static void initializeBufferFromBinding(CodeGenModule &CGM,
45+
llvm::GlobalVariable *GV, unsigned Slot,
46+
unsigned Space);
4647

4748
namespace {
4849

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

258-
// Resource initialization
259+
// Initialize cbuffer from binding (implicit or explicit)
259260
const HLSLResourceBindingAttr *RBA =
260261
BufDecl->getAttr<HLSLResourceBindingAttr>();
261262
// FIXME: handle implicit binding if no binding attribute is found
262263
// (llvm/llvm-project#110722)
263264
if (RBA)
264-
createResourceInitFn(CGM, BufGV, RBA->getSlotNumber(),
265-
RBA->getSpaceNumber());
265+
initializeBufferFromBinding(CGM, BufGV, RBA->getSlotNumber(),
266+
RBA->getSpaceNumber());
266267
}
267268

268269
llvm::TargetExtType *
@@ -505,15 +506,15 @@ void CGHLSLRuntime::generateGlobalCtorDtorCalls() {
505506
}
506507
}
507508

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

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

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

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

535-
llvm::Value *Args[] = {
536-
llvm::ConstantInt::get(CGM.IntTy, Space), /* reg_space */
537-
llvm::ConstantInt::get(CGM.IntTy, Slot), /* lower_bound */
538-
// FIXME: resource arrays are not yet implemented
539-
llvm::ConstantInt::get(CGM.IntTy, 1), /* range_size */
540-
llvm::ConstantInt::get(CGM.IntTy, 0), /* index */
541-
// FIXME: NonUniformResourceIndex bit is not yet implemented
542-
llvm::ConstantInt::get(Int1Ty, false) /* non-uniform */
543-
};
544530
llvm::Value *CreateHandle = Builder.CreateIntrinsic(
545-
/*ReturnType=*/HandleTy,
546-
CGM.getHLSLRuntime().getCreateHandleFromBindingIntrinsic(), Args, nullptr,
531+
/*ReturnType=*/HandleTy, IntrID, Args, nullptr,
547532
Twine(GV->getName()).concat("_h"));
548533

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

557-
void CGHLSLRuntime::handleGlobalVarDefinition(const VarDecl *VD,
558-
llvm::GlobalVariable *GV) {
559-
560-
// If the global variable has resource binding, create an init function
561-
// for the resource
562-
const HLSLResourceBindingAttr *RBA = VD->getAttr<HLSLResourceBindingAttr>();
563-
if (!RBA)
564-
// FIXME: collect unbound resources for implicit binding resolution later
565-
// on?
566-
return;
567-
568-
if (!VD->getType().getTypePtr()->isHLSLResourceRecord())
569-
// FIXME: Only simple declarations of resources are supported for now.
570-
// Arrays of resources or resources in user defined classes are
571-
// not implemented yet.
572-
return;
573-
574-
createResourceInitFn(CGM, GV, RBA->getSlotNumber(), RBA->getSpaceNumber());
542+
static void initializeBufferFromBinding(CodeGenModule &CGM,
543+
llvm::GlobalVariable *GV, unsigned Slot,
544+
unsigned Space) {
545+
llvm::Type *Int1Ty = llvm::Type::getInt1Ty(CGM.getLLVMContext());
546+
llvm::Value *Args[] = {
547+
llvm::ConstantInt::get(CGM.IntTy, Space), /* reg_space */
548+
llvm::ConstantInt::get(CGM.IntTy, Slot), /* lower_bound */
549+
llvm::ConstantInt::get(CGM.IntTy, 1), /* range_size */
550+
llvm::ConstantInt::get(CGM.IntTy, 0), /* index */
551+
llvm::ConstantInt::get(Int1Ty, false) /* non-uniform */
552+
};
553+
initializeBuffer(CGM, GV,
554+
CGM.getHLSLRuntime().getCreateHandleFromBindingIntrinsic(),
555+
Args);
575556
}
576557

577558
llvm::Instruction *CGHLSLRuntime::getConvergenceToken(BasicBlock &BB) {

clang/lib/CodeGen/CGHLSLRuntime.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,6 @@ class CGHLSLRuntime {
150150

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

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

clang/lib/CodeGen/CodeGenModule.cpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5760,9 +5760,6 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D,
57605760
getCUDARuntime().handleVarRegistration(D, *GV);
57615761
}
57625762

5763-
if (LangOpts.HLSL)
5764-
getHLSLRuntime().handleGlobalVarDefinition(D, GV);
5765-
57665763
GV->setInitializer(Init);
57675764
if (emitter)
57685765
emitter->finalize(GV);

clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,7 @@ void BuiltinTypeMethodBuilder::createDecl() {
400400

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

452+
auto *ImpCast = ImplicitCastExpr::Create(
453+
AST, AST.getPointerType(FD->getType()), CK_BuiltinFnToFnPtr, DRE, nullptr,
454+
VK_PRValue, FPOptionsOverride());
455+
450456
if (ReturnType.isNull())
451457
ReturnType = FD->getReturnType();
452458

453-
Expr *Call = CallExpr::Create(AST, DRE, Args, ReturnType, VK_PRValue,
459+
Expr *Call = CallExpr::Create(AST, ImpCast, Args, ReturnType, VK_PRValue,
454460
SourceLocation(), FPOptionsOverride());
455461
StmtsList.push_back(Call);
456462
return *this;
@@ -632,11 +638,33 @@ BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addDefaultHandleConstructor() {
632638
if (Record->isCompleteDefinition())
633639
return *this;
634640

635-
// FIXME: initialize handle to poison value; this can be added after
636-
// resource constructor from binding is implemented, otherwise the handle
637-
// value will get overwritten.
641+
using PH = BuiltinTypeMethodBuilder::PlaceHolder;
642+
QualType HandleType = getResourceHandleField()->getType();
638643
return BuiltinTypeMethodBuilder(*this, "", SemaRef.getASTContext().VoidTy,
639644
false, true)
645+
.callBuiltin("__builtin_hlsl_resource_uninitializedhandle", HandleType,
646+
PH::Handle)
647+
.assign(PH::Handle, PH::LastStmt)
648+
.finalize();
649+
}
650+
651+
BuiltinTypeDeclBuilder &
652+
BuiltinTypeDeclBuilder::addHandleConstructorFromBinding() {
653+
if (Record->isCompleteDefinition())
654+
return *this;
655+
656+
using PH = BuiltinTypeMethodBuilder::PlaceHolder;
657+
ASTContext &AST = SemaRef.getASTContext();
658+
QualType HandleType = getResourceHandleField()->getType();
659+
660+
return BuiltinTypeMethodBuilder(*this, "", AST.VoidTy, false, true)
661+
.addParam("registerNo", AST.UnsignedIntTy)
662+
.addParam("spaceNo", AST.UnsignedIntTy)
663+
.addParam("range", AST.IntTy)
664+
.addParam("index", AST.UnsignedIntTy)
665+
.callBuiltin("__builtin_hlsl_resource_handlefrombinding", HandleType,
666+
PH::Handle, PH::_0, PH::_1, PH::_2, PH::_3)
667+
.assign(PH::Handle, PH::LastStmt)
640668
.finalize();
641669
}
642670

clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ class BuiltinTypeDeclBuilder {
7878

7979
// Builtin types methods
8080
BuiltinTypeDeclBuilder &addDefaultHandleConstructor();
81+
BuiltinTypeDeclBuilder &addHandleConstructorFromBinding();
8182

8283
// Builtin types methods
8384
BuiltinTypeDeclBuilder &addLoadMethods();

clang/lib/Sema/HLSLExternalSemaSource.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,8 @@ static BuiltinTypeDeclBuilder setupBufferType(CXXRecordDecl *Decl, Sema &S,
131131
bool RawBuffer) {
132132
return BuiltinTypeDeclBuilder(S, Decl)
133133
.addHandleMember(RC, IsROV, RawBuffer)
134-
.addDefaultHandleConstructor();
134+
.addDefaultHandleConstructor()
135+
.addHandleConstructorFromBinding();
135136
}
136137

137138
// This function is responsible for constructing the constraint expression for

clang/lib/Sema/SemaDecl.cpp

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14378,10 +14378,8 @@ void Sema::ActOnUninitializedDecl(Decl *RealDecl) {
1437814378
Var->getType().getAddressSpace() == LangAS::opencl_local)
1437914379
return;
1438014380

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

1438714385
// C++03 [dcl.init]p9:

clang/lib/Sema/SemaHLSL.cpp

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include "clang/Sema/ParsedAttr.h"
3333
#include "clang/Sema/Sema.h"
3434
#include "clang/Sema/Template.h"
35+
#include "llvm/ADT/ArrayRef.h"
3536
#include "llvm/ADT/STLExtras.h"
3637
#include "llvm/ADT/SmallVector.h"
3738
#include "llvm/ADT/StringExtras.h"
@@ -2395,6 +2396,29 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
23952396

23962397
break;
23972398
}
2399+
case Builtin::BI__builtin_hlsl_resource_uninitializedhandle: {
2400+
if (SemaRef.checkArgCount(TheCall, 1) ||
2401+
CheckResourceHandle(&SemaRef, TheCall, 0))
2402+
return true;
2403+
// use the type of the handle (arg0) as a return type
2404+
QualType ResourceTy = TheCall->getArg(0)->getType();
2405+
TheCall->setType(ResourceTy);
2406+
break;
2407+
}
2408+
case Builtin::BI__builtin_hlsl_resource_handlefrombinding: {
2409+
ASTContext &AST = SemaRef.getASTContext();
2410+
if (SemaRef.checkArgCount(TheCall, 5) ||
2411+
CheckResourceHandle(&SemaRef, TheCall, 0) ||
2412+
CheckArgTypeMatches(&SemaRef, TheCall->getArg(1), AST.UnsignedIntTy) ||
2413+
CheckArgTypeMatches(&SemaRef, TheCall->getArg(2), AST.UnsignedIntTy) ||
2414+
CheckArgTypeMatches(&SemaRef, TheCall->getArg(3), AST.IntTy) ||
2415+
CheckArgTypeMatches(&SemaRef, TheCall->getArg(4), AST.UnsignedIntTy))
2416+
return true;
2417+
// use the type of the handle (arg0) as a return type
2418+
QualType ResourceTy = TheCall->getArg(0)->getType();
2419+
TheCall->setType(ResourceTy);
2420+
break;
2421+
}
23982422
case Builtin::BI__builtin_hlsl_and:
23992423
case Builtin::BI__builtin_hlsl_or: {
24002424
if (SemaRef.checkArgCount(TheCall, 2))
@@ -3218,6 +3242,68 @@ void SemaHLSL::ActOnVariableDeclarator(VarDecl *VD) {
32183242
deduceAddressSpace(VD);
32193243
}
32203244

3245+
static bool initVarDeclWithCtor(Sema &S, VarDecl *VD,
3246+
MutableArrayRef<Expr *> Args) {
3247+
InitializedEntity Entity = InitializedEntity::InitializeVariable(VD);
3248+
InitializationKind Kind = InitializationKind::CreateDirect(
3249+
VD->getLocation(), SourceLocation(), SourceLocation());
3250+
3251+
InitializationSequence InitSeq(S, Entity, Kind, Args);
3252+
ExprResult Init = InitSeq.Perform(S, Entity, Kind, Args);
3253+
3254+
if (!Init.get())
3255+
return false;
3256+
3257+
VD->setInit(S.MaybeCreateExprWithCleanups(Init.get()));
3258+
VD->setInitStyle(VarDecl::CallInit);
3259+
S.CheckCompleteVariableDeclaration(VD);
3260+
return true;
3261+
}
3262+
3263+
static bool initGlobalResourceDecl(Sema &S, VarDecl *VD) {
3264+
HLSLResourceBindingAttr *RBA = VD->getAttr<HLSLResourceBindingAttr>();
3265+
if (!RBA)
3266+
// FIXME: add support for implicit binding (llvm/llvm-project#110722)
3267+
return false;
3268+
3269+
ASTContext &AST = S.getASTContext();
3270+
uint64_t UIntTySize = AST.getTypeSize(AST.UnsignedIntTy);
3271+
uint64_t IntTySize = AST.getTypeSize(AST.IntTy);
3272+
Expr *Args[] = {
3273+
IntegerLiteral::Create(AST, llvm::APInt(UIntTySize, RBA->getSlotNumber()),
3274+
AST.UnsignedIntTy, SourceLocation()),
3275+
IntegerLiteral::Create(AST,
3276+
llvm::APInt(UIntTySize, RBA->getSpaceNumber()),
3277+
AST.UnsignedIntTy, SourceLocation()),
3278+
IntegerLiteral::Create(AST, llvm::APInt(IntTySize, 1), AST.IntTy,
3279+
SourceLocation()),
3280+
IntegerLiteral::Create(AST, llvm::APInt(UIntTySize, 0), AST.UnsignedIntTy,
3281+
SourceLocation())};
3282+
3283+
return initVarDeclWithCtor(S, VD, Args);
3284+
}
3285+
3286+
// Returns true if the initialization has been handled.
3287+
// Returns false to use default initialization.
3288+
bool SemaHLSL::ActOnUninitializedVarDecl(VarDecl *VD) {
3289+
// Objects in the hlsl_constant address space are initialized
3290+
// externally, so don't synthesize an implicit initializer.
3291+
if (VD->getType().getAddressSpace() == LangAS::hlsl_constant)
3292+
return true;
3293+
3294+
// Initialize resources
3295+
if (!isResourceRecordTypeOrArrayOf(VD))
3296+
return false;
3297+
3298+
// FIXME: We currectly support only simple resources - no arrays of resources
3299+
// or resources in user defined structs.
3300+
// (llvm/llvm-project#133835, llvm/llvm-project#133837)
3301+
if (VD->getType()->isHLSLResourceRecord())
3302+
return initGlobalResourceDecl(SemaRef, VD);
3303+
3304+
return false;
3305+
}
3306+
32213307
// Walks though the global variable declaration, collects all resource binding
32223308
// requirements and adds them to Bindings
32233309
void SemaHLSL::collectResourceBindingsOnVarDecl(VarDecl *VD) {

0 commit comments

Comments
 (0)