https://github.com/hekota created https://github.com/llvm/llvm-project/pull/132453
None >From 47b41c88a60a7f376070b9ff779ec955eebf523a Mon Sep 17 00:00:00 2001 From: Helena Kotas <heko...@microsoft.com> Date: Wed, 12 Mar 2025 17:20:51 -0700 Subject: [PATCH 1/3] [HLSL] Create default resource constructor with BuiltinTypeMethodBuilder --- clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp | 126 ++++++++++-------- 1 file changed, 72 insertions(+), 54 deletions(-) diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp index db0ed3434d837..a52c6a49264c8 100644 --- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp +++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp @@ -89,21 +89,24 @@ struct TemplateParameterListBuilder { // statement (unless the last statement is already a ReturnStmt). struct BuiltinTypeMethodBuilder { private: - struct MethodParam { + struct Param { const IdentifierInfo &NameII; QualType Ty; HLSLParamModifierAttr::Spelling Modifier; - MethodParam(const IdentifierInfo &NameII, QualType Ty, - HLSLParamModifierAttr::Spelling Modifier) + Param(const IdentifierInfo &NameII, QualType Ty, + HLSLParamModifierAttr::Spelling Modifier) : NameII(NameII), Ty(Ty), Modifier(Modifier) {} }; BuiltinTypeDeclBuilder &DeclBuilder; - DeclarationNameInfo NameInfo; + DeclarationName Name; QualType ReturnTy; + // method or constructor declaration (CXXConstructorDecl derives from + // CXXMethodDecl) CXXMethodDecl *Method; bool IsConst; - llvm::SmallVector<MethodParam> Params; + bool IsConstructor; + llvm::SmallVector<Param> Params; llvm::SmallVector<Stmt *> StmtsList; // Argument placeholders, inspired by std::placeholder. These are the indices @@ -122,12 +125,14 @@ struct BuiltinTypeMethodBuilder { friend BuiltinTypeDeclBuilder; BuiltinTypeMethodBuilder(BuiltinTypeDeclBuilder &DB, DeclarationName &Name, - QualType ReturnTy, bool IsConst = false) - : DeclBuilder(DB), NameInfo(DeclarationNameInfo(Name, SourceLocation())), - ReturnTy(ReturnTy), Method(nullptr), IsConst(IsConst) {} - - BuiltinTypeMethodBuilder(BuiltinTypeDeclBuilder &DB, StringRef Name, - QualType ReturnTy, bool IsConst = false); + QualType ReturnTy, bool IsConst = false, + bool IsConstructor = false) + : DeclBuilder(DB), Name(Name), ReturnTy(ReturnTy), Method(nullptr), + IsConst(IsConst), IsConstructor(IsConstructor) {} + + BuiltinTypeMethodBuilder(BuiltinTypeDeclBuilder &DB, StringRef NameStr, + QualType ReturnTy, bool IsConst = false, + bool IsConstructor = false); BuiltinTypeMethodBuilder(const BuiltinTypeMethodBuilder &Other) = delete; ~BuiltinTypeMethodBuilder() { finalizeMethod(); } @@ -148,7 +153,14 @@ struct BuiltinTypeMethodBuilder { Expr *getResourceHandleExpr(); private: - void createMethodDecl(); + void createDecl(); + + // Makes sure the declaration is created; should be called before any + // statement added or when access to 'this' is needed. + void ensureCompleteDecl() { + if (!Method) + createDecl(); + } }; TemplateParameterListBuilder::~TemplateParameterListBuilder() { @@ -323,13 +335,26 @@ Expr *BuiltinTypeMethodBuilder::convertPlaceholder(PlaceHolder PH) { } BuiltinTypeMethodBuilder::BuiltinTypeMethodBuilder(BuiltinTypeDeclBuilder &DB, - StringRef Name, + StringRef NameStr, QualType ReturnTy, - bool IsConst) - : DeclBuilder(DB), ReturnTy(ReturnTy), Method(nullptr), IsConst(IsConst) { - const IdentifierInfo &II = - DB.SemaRef.getASTContext().Idents.get(Name, tok::TokenKind::identifier); - NameInfo = DeclarationNameInfo(DeclarationName(&II), SourceLocation()); + bool IsConst, + bool IsConstructor) + : DeclBuilder(DB), ReturnTy(ReturnTy), Method(nullptr), IsConst(IsConst), + IsConstructor(IsConstructor) { + + assert((!NameStr.empty() || IsConstructor) && "method needs a name"); + assert(((IsConstructor && !IsConst) || !IsConstructor) && + "constructor cannot be const"); + + ASTContext &AST = DB.SemaRef.getASTContext(); + if (IsConstructor) { + Name = AST.DeclarationNames.getCXXConstructorName( + DB.Record->getTypeForDecl()->getCanonicalTypeUnqualified()); + } else { + const IdentifierInfo &II = + AST.Idents.get(NameStr, tok::TokenKind::identifier); + Name = DeclarationName(&II); + } } BuiltinTypeMethodBuilder & @@ -342,13 +367,13 @@ BuiltinTypeMethodBuilder::addParam(StringRef Name, QualType Ty, return *this; } -void BuiltinTypeMethodBuilder::createMethodDecl() { - assert(Method == nullptr && "Method already created"); +void BuiltinTypeMethodBuilder::createDecl() { + assert(Method == nullptr && "Method or constructor is already created"); - // create method type + // create method or constructor type ASTContext &AST = DeclBuilder.SemaRef.getASTContext(); SmallVector<QualType> ParamTypes; - for (MethodParam &MP : Params) + for (Param &MP : Params) ParamTypes.emplace_back(MP.Ty); FunctionProtoType::ExtProtoInfo ExtInfo; @@ -357,18 +382,26 @@ void BuiltinTypeMethodBuilder::createMethodDecl() { QualType MethodTy = AST.getFunctionType(ReturnTy, ParamTypes, ExtInfo); - // create method decl + // create method or constructor decl auto *TSInfo = AST.getTrivialTypeSourceInfo(MethodTy, SourceLocation()); - Method = CXXMethodDecl::Create( - AST, DeclBuilder.Record, SourceLocation(), NameInfo, MethodTy, TSInfo, - SC_None, false, false, ConstexprSpecKind::Unspecified, SourceLocation()); + DeclarationNameInfo NameInfo = DeclarationNameInfo(Name, SourceLocation()); + if (IsConstructor) + Method = CXXConstructorDecl::Create( + AST, DeclBuilder.Record, SourceLocation(), NameInfo, MethodTy, TSInfo, + ExplicitSpecifier(), false, true, false, + ConstexprSpecKind::Unspecified); + else + Method = + CXXMethodDecl::Create(AST, DeclBuilder.Record, SourceLocation(), + NameInfo, MethodTy, TSInfo, SC_None, false, false, + ConstexprSpecKind::Unspecified, SourceLocation()); // create params & set them to the function prototype SmallVector<ParmVarDecl *> ParmDecls; auto FnProtoLoc = Method->getTypeSourceInfo()->getTypeLoc().getAs<FunctionProtoTypeLoc>(); for (int I = 0, E = Params.size(); I != E; I++) { - MethodParam &MP = Params[I]; + Param &MP = Params[I]; ParmVarDecl *Parm = ParmVarDecl::Create( AST, Method->getDeclContext(), SourceLocation(), SourceLocation(), &MP.NameII, MP.Ty, @@ -386,10 +419,7 @@ void BuiltinTypeMethodBuilder::createMethodDecl() { } Expr *BuiltinTypeMethodBuilder::getResourceHandleExpr() { - // The first statement added to a method or access to 'this' creates the - // declaration. - if (!Method) - createMethodDecl(); + ensureCompleteDecl(); ASTContext &AST = DeclBuilder.SemaRef.getASTContext(); CXXThisExpr *This = CXXThisExpr::Create( @@ -407,10 +437,7 @@ BuiltinTypeMethodBuilder::callBuiltin(StringRef BuiltinName, std::array<Expr *, sizeof...(ArgSpecs)> Args{ convertPlaceholder(std::forward<Ts>(ArgSpecs))...}; - // The first statement added to a method or access to 'this` creates the - // declaration. - if (!Method) - createMethodDecl(); + ensureCompleteDecl(); ASTContext &AST = DeclBuilder.SemaRef.getASTContext(); FunctionDecl *FD = lookupBuiltinFunction(DeclBuilder.SemaRef, BuiltinName); @@ -454,8 +481,8 @@ BuiltinTypeMethodBuilder &BuiltinTypeMethodBuilder::dereference(T Ptr) { BuiltinTypeDeclBuilder &BuiltinTypeMethodBuilder::finalizeMethod() { assert(!DeclBuilder.Record->isCompleteDefinition() && "record is already complete"); - assert(Method != nullptr && - "method decl not created; are you missing a call to build the body?"); + + ensureCompleteDecl(); if (!Method->hasBody()) { ASTContext &AST = DeclBuilder.SemaRef.getASTContext(); @@ -600,27 +627,18 @@ BuiltinTypeDeclBuilder::addHandleMember(ResourceClass RC, ResourceKind RK, return *this; } +// Adds default constructor to the resource class: +// Resource::Resource() BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addDefaultHandleConstructor() { if (Record->isCompleteDefinition()) return *this; - ASTContext &AST = Record->getASTContext(); - QualType ConstructorType = - AST.getFunctionType(AST.VoidTy, {}, FunctionProtoType::ExtProtoInfo()); - - CanQualType CanTy = Record->getTypeForDecl()->getCanonicalTypeUnqualified(); - DeclarationName Name = AST.DeclarationNames.getCXXConstructorName(CanTy); - CXXConstructorDecl *Constructor = CXXConstructorDecl::Create( - AST, Record, SourceLocation(), - DeclarationNameInfo(Name, SourceLocation()), ConstructorType, - AST.getTrivialTypeSourceInfo(ConstructorType, SourceLocation()), - ExplicitSpecifier(), false, true, false, ConstexprSpecKind::Unspecified); - - Constructor->setBody(CompoundStmt::Create( - AST, {}, FPOptionsOverride(), SourceLocation(), SourceLocation())); - Constructor->setAccess(AccessSpecifier::AS_public); - Record->addDecl(Constructor); - 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. + return BuiltinTypeMethodBuilder(*this, "", SemaRef.getASTContext().VoidTy, + false, true) + .finalizeMethod(); } BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addArraySubscriptOperators() { >From 4301a06b4dc2f96de3ea76e23685fa8b4b99e534 Mon Sep 17 00:00:00 2001 From: Helena Kotas <heko...@microsoft.com> Date: Wed, 12 Mar 2025 17:51:18 -0700 Subject: [PATCH 2/3] rename finalizeMethod to finalize --- clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp index a52c6a49264c8..058525d77d99e 100644 --- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp +++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp @@ -71,22 +71,23 @@ struct TemplateParameterListBuilder { // BuiltinTypeMethodBuilder(RecordBuilder, "MethodName", ReturnType) // .addParam("param_name", Type, InOutModifier) // .callBuiltin("builtin_name", BuiltinParams...) -// .finalizeMethod(); +// .finalize(); // // The builder needs to have all of the method parameters before it can create // a CXXMethodDecl. It collects them in addParam calls and when a first // method that builds the body is called or when access to 'this` is needed it // creates the CXXMethodDecl and ParmVarDecls instances. These can then be // referenced from the body building methods. Destructor or an explicit call to -// finalizeMethod() will complete the method definition. +// finalize() will complete the method definition. // // The callBuiltin helper method accepts constants via `Expr *` or placeholder // value arguments to indicate which function arguments to forward to the // builtin. // // If the method that is being built has a non-void return type the -// finalizeMethod will create a return statent with the value of the last -// statement (unless the last statement is already a ReturnStmt). +// finalize() will create a return statement with the value of the last +// statement (unless the last statement is already a ReturnStmt or the return +// value is void). struct BuiltinTypeMethodBuilder { private: struct Param { @@ -135,7 +136,7 @@ struct BuiltinTypeMethodBuilder { bool IsConstructor = false); BuiltinTypeMethodBuilder(const BuiltinTypeMethodBuilder &Other) = delete; - ~BuiltinTypeMethodBuilder() { finalizeMethod(); } + ~BuiltinTypeMethodBuilder() { finalize(); } BuiltinTypeMethodBuilder & operator=(const BuiltinTypeMethodBuilder &Other) = delete; @@ -149,7 +150,7 @@ struct BuiltinTypeMethodBuilder { template <typename TLHS, typename TRHS> BuiltinTypeMethodBuilder &assign(TLHS LHS, TRHS RHS); template <typename T> BuiltinTypeMethodBuilder &dereference(T Ptr); - BuiltinTypeDeclBuilder &finalizeMethod(); + BuiltinTypeDeclBuilder &finalize(); Expr *getResourceHandleExpr(); private: @@ -478,7 +479,7 @@ BuiltinTypeMethodBuilder &BuiltinTypeMethodBuilder::dereference(T Ptr) { return *this; } -BuiltinTypeDeclBuilder &BuiltinTypeMethodBuilder::finalizeMethod() { +BuiltinTypeDeclBuilder &BuiltinTypeMethodBuilder::finalize() { assert(!DeclBuilder.Record->isCompleteDefinition() && "record is already complete"); @@ -638,7 +639,7 @@ BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addDefaultHandleConstructor() { // value will get overwritten. return BuiltinTypeMethodBuilder(*this, "", SemaRef.getASTContext().VoidTy, false, true) - .finalizeMethod(); + .finalize(); } BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addArraySubscriptOperators() { @@ -732,7 +733,7 @@ BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addIncrementCounterMethod() { SemaRef.getASTContext().UnsignedIntTy) .callBuiltin("__builtin_hlsl_buffer_update_counter", QualType(), PH::Handle, getConstantIntExpr(1)) - .finalizeMethod(); + .finalize(); } BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addDecrementCounterMethod() { @@ -741,7 +742,7 @@ BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addDecrementCounterMethod() { SemaRef.getASTContext().UnsignedIntTy) .callBuiltin("__builtin_hlsl_buffer_update_counter", QualType(), PH::Handle, getConstantIntExpr(-1)) - .finalizeMethod(); + .finalize(); } BuiltinTypeDeclBuilder & @@ -765,7 +766,7 @@ BuiltinTypeDeclBuilder::addHandleAccessFunction(DeclarationName &Name, .callBuiltin("__builtin_hlsl_resource_getpointer", ElemPtrTy, PH::Handle, PH::_0) .dereference(PH::LastStmt) - .finalizeMethod(); + .finalize(); } BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addAppendMethod() { @@ -780,7 +781,7 @@ BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addAppendMethod() { AST.getPointerType(ElemTy), PH::Handle, PH::LastStmt) .dereference(PH::LastStmt) .assign(PH::LastStmt, PH::_0) - .finalizeMethod(); + .finalize(); } BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addConsumeMethod() { @@ -793,7 +794,7 @@ BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addConsumeMethod() { .callBuiltin("__builtin_hlsl_resource_getpointer", AST.getPointerType(ElemTy), PH::Handle, PH::LastStmt) .dereference(PH::LastStmt) - .finalizeMethod(); + .finalize(); } } // namespace hlsl >From e9a80485e8f146506469aab74bd555d79dcf4e40 Mon Sep 17 00:00:00 2001 From: Helena Kotas <heko...@microsoft.com> Date: Fri, 14 Mar 2025 12:52:51 -0700 Subject: [PATCH 3/3] Implement resource constructors Adds resource constructor that initializes resource handle based on resource binding Updated default constructor to set the handle value to poison WIP --- clang/include/clang/Basic/Builtins.td | 12 ++ clang/include/clang/Sema/SemaHLSL.h | 4 + clang/lib/CodeGen/CGBuiltin.cpp | 18 +++ clang/lib/CodeGen/CGHLSLRuntime.cpp | 2 +- clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp | 35 +++++- clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h | 1 + clang/lib/Sema/HLSLExternalSemaSource.cpp | 3 +- clang/lib/Sema/SemaDecl.cpp | 6 +- clang/lib/Sema/SemaHLSL.cpp | 87 +++++++++++++++ .../ByteAddressBuffers-constructors.hlsl | 97 ++++++++++++---- .../builtins/RWBuffer-constructor-opt.hlsl | 2 +- .../builtins/RWBuffer-constructor.hlsl | 78 +++++++++++-- .../StructuredBuffers-constructors.hlsl | 105 ++++++++++++------ clang/test/CodeGenHLSL/resource-bindings.hlsl | 50 +++++---- 14 files changed, 404 insertions(+), 96 deletions(-) diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td index 2fbdfaea57ccd..8e3828e4a4771 100644 --- a/clang/include/clang/Basic/Builtins.td +++ b/clang/include/clang/Basic/Builtins.td @@ -4783,6 +4783,18 @@ def HLSLResourceGetPointer : LangBuiltin<"HLSL_LANG"> { let Prototype = "void(...)"; } +def HLSLResourceCreatePoisonHandle : LangBuiltin<"HLSL_LANG"> { + let Spellings = ["__builtin_hlsl_resource_createpoisonhandle"]; + let Attributes = [NoThrow]; + let Prototype = "void(...)"; +} + +def HLSLResourceCreateHandleFromBinding : LangBuiltin<"HLSL_LANG"> { + let Spellings = ["__builtin_hlsl_resource_createhandlefrombinding"]; + let Attributes = [NoThrow]; + let Prototype = "void(...)"; +} + def HLSLAll : LangBuiltin<"HLSL_LANG"> { let Spellings = ["__builtin_hlsl_all"]; let Attributes = [NoThrow, Const]; diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h index f333fe30e8da0..4ad00b210cccf 100644 --- a/clang/include/clang/Sema/SemaHLSL.h +++ b/clang/include/clang/Sema/SemaHLSL.h @@ -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, @@ -179,6 +180,9 @@ class SemaHLSL : public SemaBase { void processExplicitBindingsOnDecl(VarDecl *D); void diagnoseAvailabilityViolations(TranslationUnitDecl *TU); + bool initResourceVarFromBinding(VarDecl *VD, unsigned SpaceNo, + unsigned RegisterNo, int32_t Range, + unsigned Index); }; } // namespace clang diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index a5ed2595bad4d..5e364b5913a13 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -19579,6 +19579,24 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID, RetTy, CGM.getHLSLRuntime().getCreateResourceGetPointerIntrinsic(), ArrayRef<Value *>{HandleOp, IndexOp}); } + case Builtin::BI__builtin_hlsl_resource_createpoisonhandle: { + llvm::Type *HandleTy = CGM.getTypes().ConvertType(E->getType()); + return llvm::PoisonValue::get(HandleTy); + } + case Builtin::BI__builtin_hlsl_resource_createhandlefrombinding: { + llvm::Type *HandleTy = CGM.getTypes().ConvertType(E->getType()); + Value *SpaceNoOp = EmitScalarExpr(E->getArg(1)); + Value *RegisterNoOp = EmitScalarExpr(E->getArg(2)); + Value *RangeOp = EmitScalarExpr(E->getArg(3)); + Value *IndexOp = EmitScalarExpr(E->getArg(4)); + // FIXME: NonUniformResourceIndex bit is not yet implemented + Value *NonUniform = + llvm::ConstantInt::get(llvm::Type::getInt1Ty(getLLVMContext()), false); + return Builder.CreateIntrinsic( + HandleTy, CGM.getHLSLRuntime().getCreateHandleFromBindingIntrinsic(), + ArrayRef<Value *>{SpaceNoOp, RegisterNoOp, RangeOp, IndexOp, + NonUniform}); + } case Builtin::BI__builtin_hlsl_all: { Value *Op0 = EmitScalarExpr(E->getArg(0)); return Builder.CreateIntrinsic( diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp index dc34653e8f497..b4e693dbe85f2 100644 --- a/clang/lib/CodeGen/CGHLSLRuntime.cpp +++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp @@ -659,7 +659,7 @@ void CGHLSLRuntime::handleGlobalVarDefinition(const VarDecl *VD, // not implemented yet. return; - createResourceInitFn(CGM, GV, RBA->getSlotNumber(), RBA->getSpaceNumber()); + // createResourceInitFn(CGM, GV, RBA->getSlotNumber(), RBA->getSpaceNumber()); } llvm::Instruction *CGHLSLRuntime::getConvergenceToken(BasicBlock &BB) { diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp index 058525d77d99e..602688d575252 100644 --- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp +++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp @@ -399,6 +399,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++) { @@ -413,6 +414,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); } @@ -446,10 +448,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; @@ -634,11 +640,32 @@ 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; return BuiltinTypeMethodBuilder(*this, "", SemaRef.getASTContext().VoidTy, false, true) + .callBuiltin("__builtin_hlsl_resource_createpoisonhandle", QualType(), + 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("spaceNo", AST.UnsignedIntTy) + .addParam("registerNo", AST.UnsignedIntTy) + .addParam("range", AST.IntTy) + .addParam("index", AST.UnsignedIntTy) + .callBuiltin("__builtin_hlsl_resource_createhandlefrombinding", + HandleType, PH::Handle, PH::_0, PH::_1, PH::_2, PH::_3) + .assign(PH::Handle, PH::LastStmt) .finalize(); } diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h index 2c944c4a60038..2d3abb6055669 100644 --- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h +++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h @@ -74,6 +74,7 @@ class BuiltinTypeDeclBuilder { // Builtin types methods BuiltinTypeDeclBuilder &addDefaultHandleConstructor(); + BuiltinTypeDeclBuilder &addHandleConstructorFromBinding(); // Builtin types methods BuiltinTypeDeclBuilder &addLoadMethods(); diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp index 9224f12f2d025..7b477249de0a7 100644 --- a/clang/lib/Sema/HLSLExternalSemaSource.cpp +++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp @@ -131,7 +131,8 @@ static BuiltinTypeDeclBuilder setupBufferType(CXXRecordDecl *Decl, Sema &S, bool IsROV, bool RawBuffer) { return BuiltinTypeDeclBuilder(S, Decl) .addHandleMember(RC, RK, IsROV, RawBuffer) - .addDefaultHandleConstructor(); + .addDefaultHandleConstructor() + .addHandleConstructorFromBinding(); } // This function is responsible for constructing the constraint expression for diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 9c67fbd40ac71..b56f0b36b390e 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -14340,10 +14340,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: diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index d10acd1b7807c..6971c3780766b 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -19,6 +19,7 @@ #include "clang/AST/DeclarationName.h" #include "clang/AST/DynamicRecursiveASTVisitor.h" #include "clang/AST/Expr.h" +#include "clang/AST/ExprCXX.h" #include "clang/AST/Type.h" #include "clang/AST/TypeLoc.h" #include "clang/Basic/Builtins.h" @@ -308,6 +309,10 @@ static bool isResourceRecordTypeOrArrayOf(const Type *Ty) { return HLSLAttributedResourceType::findHandleTypeOnResource(Ty) != nullptr; } +static bool isResourceRecordTypeOrArrayOf(VarDecl *VD) { + return isResourceRecordTypeOrArrayOf(VD->getType().getTypePtr()); +} + // Returns true if the type is a leaf element type that is not valid to be // included in HLSL Buffer, such as a resource class, empty struct, zero-sized // array, or a builtin intangible type. Returns false it is a valid leaf element @@ -2359,6 +2364,29 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) { break; } + case Builtin::BI__builtin_hlsl_resource_createpoisonhandle: { + 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_createhandlefrombinding: { + 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)) @@ -3147,6 +3175,65 @@ void SemaHLSL::ActOnVariableDeclarator(VarDecl *VD) { } } +bool SemaHLSL::initResourceVarFromBinding(VarDecl *VD, unsigned SpaceNo, + unsigned RegisterNo, int32_t Range, + unsigned Index) { + InitializedEntity Entity = InitializedEntity::InitializeVariable(VD); + InitializationKind Kind = InitializationKind::CreateDirect( + VD->getLocation(), SourceLocation(), SourceLocation()); + + ASTContext &AST = SemaRef.getASTContext(); + uint64_t UIntTySize = AST.getTypeSize(AST.UnsignedIntTy); + uint64_t IntTySize = AST.getTypeSize(AST.IntTy); + Expr *Args[] = {IntegerLiteral::Create(AST, llvm::APInt(UIntTySize, SpaceNo), + AST.UnsignedIntTy, SourceLocation()), + IntegerLiteral::Create(AST, + llvm::APInt(UIntTySize, RegisterNo), + AST.UnsignedIntTy, SourceLocation()), + IntegerLiteral::Create(AST, llvm::APInt(IntTySize, Range), + AST.IntTy, SourceLocation()), + IntegerLiteral::Create(AST, llvm::APInt(UIntTySize, Index), + AST.UnsignedIntTy, SourceLocation())}; + + InitializationSequence InitSeq(SemaRef, Entity, Kind, Args); + ExprResult Init = InitSeq.Perform(SemaRef, Entity, Kind, Args); + + if (!Init.get()) + return false; + + VD->setInit(SemaRef.MaybeCreateExprWithCleanups(Init.get())); + VD->setInitStyle(VarDecl::CallInit); + SemaRef.CheckCompleteVariableDeclaration(VD); + return true; +} + +// Returns true in the initialization has been handled; +// Return false to let Clang handle the default initializaton. +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 (explicit binding) + // NOTE: it would probably be best to do this for all resources at the end + // of the translation unit after implicit bindings are assigned. + if (!isResourceRecordTypeOrArrayOf(VD)) + return false; + + // FIXME: We currectly support only simple resources - no arrays of resources + // or resources in user defined structs). + if (!VD->getType()->isHLSLResourceRecord()) + return false; + + HLSLResourceBindingAttr *RBA = VD->getAttr<HLSLResourceBindingAttr>(); + if (!RBA) + return false; + + return initResourceVarFromBinding(VD, RBA->getSpaceNumber(), + RBA->getSlotNumber(), 1, 0); +} + // Walks though the global variable declaration, collects all resource binding // requirements and adds them to Bindings void SemaHLSL::collectResourceBindingsOnVarDecl(VarDecl *VD) { diff --git a/clang/test/CodeGenHLSL/builtins/ByteAddressBuffers-constructors.hlsl b/clang/test/CodeGenHLSL/builtins/ByteAddressBuffers-constructors.hlsl index 926a37c689517..73ddc12e78ef9 100644 --- a/clang/test/CodeGenHLSL/builtins/ByteAddressBuffers-constructors.hlsl +++ b/clang/test/CodeGenHLSL/builtins/ByteAddressBuffers-constructors.hlsl @@ -3,32 +3,85 @@ // NOTE: SPIRV codegen for resource types is not yet implemented -ByteAddressBuffer Buffer0: register(t0); -RWByteAddressBuffer Buffer1: register(u1, space2); -RasterizerOrderedByteAddressBuffer Buffer2: register(u3, space4); +ByteAddressBuffer Buf1: register(t1, space2); +RWByteAddressBuffer Buf2; -// CHECK: "class.hlsl::ByteAddressBuffer" = type { target("dx.RawBuffer", i8, 0, 0) } -// CHECK: "class.hlsl::RWByteAddressBuffer" = type { target("dx.RawBuffer", i8, 1, 0) } -// CHECK: "class.hlsl::RasterizerOrderedByteAddressBuffer" = type { target("dx.RawBuffer", i8, 1, 1) } +export void foo() { + RasterizerOrderedByteAddressBuffer Buf3; +} -// CHECK: @_ZL7Buffer0 = internal global %"class.hlsl::ByteAddressBuffer" poison, align 4 -// CHECK: @_ZL7Buffer1 = internal global %"class.hlsl::RWByteAddressBuffer" poison, align 4 -// CHECK: @_ZL7Buffer2 = internal global %"class.hlsl::RasterizerOrderedByteAddressBuffer" poison, align 4 +// CHECK: %"class.hlsl::ByteAddressBuffer" = type { target("dx.RawBuffer", i8, 0, 0) } +// CHECK: %"class.hlsl::RWByteAddressBuffer" = type { target("dx.RawBuffer", i8, 1, 0) } +// CHECK: %"class.hlsl::RasterizerOrderedByteAddressBuffer" = type { target("dx.RawBuffer", i8, 1, 1) } -// CHECK; define internal void @_init_resource_Buffer0() -// CHECK-DXIL: [[H:%.*]] = call target("dx.RawBuffer", i8, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_i8_0_0t(i32 0, i32 0, i32 1, i32 0, i1 false) -// CHECK-DXIL: store target("dx.RawBuffer", i8, 0, 0) [[H]], ptr @_ZL7Buffer0, align 4 +// CHECK: @_ZL4Buf1 = internal global %"class.hlsl::ByteAddressBuffer" poison, align 4 +// CHECK: @_ZL4Buf2 = internal global %"class.hlsl::RWByteAddressBuffer" poison, align 4 -// CHECK; define internal void @_init_resource_Buffer1() -// CHECK-DXIL: [[H:%.*]] = call target("dx.RawBuffer", i8, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_i8_1_0t(i32 2, i32 1, i32 1, i32 0, i1 false) -// CHECK-DXIL: store target("dx.RawBuffer", i8, 1, 0) [[H]], ptr @_ZL7Buffer1, align 4 +// +// Buf1 initialization part 1 - constructor that creates handle from binding +// +// CHECK: define internal void @__cxx_global_var_init() +// CHECK-NEXT: entry: +// CHECK-NEXT: call void @_ZN4hlsl17ByteAddressBufferC1Ejjij(ptr noundef nonnull align 4 dereferenceable(4) @_ZL4Buf1, +// CHECK-SAME: i32 noundef 2, i32 noundef 1, i32 noundef 1, i32 noundef 0) -// CHECK; define internal void @_init_resource_Buffer2() -// CHECK-DXIL: [[H:%.*]] = call target("dx.RawBuffer", i8, 1, 1) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_i8_1_1t(i32 4, i32 3, i32 1, i32 0, i1 false) -// CHECK-DXIL: store target("dx.RawBuffer", i8, 1, 1) [[H]], ptr @_ZL7Buffer2, align 4 +// CHECK: define linkonce_odr void @_ZN4hlsl17ByteAddressBufferC1Ejjij(ptr noundef nonnull align 4 dereferenceable(4) %this, +// CHECK-SAME: i32 noundef %spaceNo, i32 noundef %registerNo, i32 noundef %range, i32 noundef %index) +// CHECK: call void @_ZN4hlsl17ByteAddressBufferC2Ejjij(ptr noundef nonnull align 4 dereferenceable(4) %this1, +// CHECK-SAME: i32 noundef %0, i32 noundef %1, i32 noundef %2, i32 noundef %3) + +// +// Buf2 initialization part 1 - default constructor that initializes handle to poison +// + +// CHECK: define internal void @__cxx_global_var_init.1() +// CHECK-NEXT: entry: +// CHECK-NEXT: call void @_ZN4hlsl19RWByteAddressBufferC1Ev(ptr noundef nonnull align 4 dereferenceable(4) @_ZL4Buf2) + +// CHECK: define linkonce_odr void @_ZN4hlsl19RWByteAddressBufferC1Ev(ptr noundef nonnull align 4 dereferenceable(4) %this) +// CHECK: call void @_ZN4hlsl19RWByteAddressBufferC2Ev(ptr noundef nonnull align 4 dereferenceable(4) %this1) + +// +// Buf3 initialization part 1 - local variable with default constructor that initializes handle to poison +// + +// CHECK: define void @_Z3foov() #2 { +// CHECK-NEXT: entry: +// CHECK-NEXT: %Buf3 = alloca %"class.hlsl::RasterizerOrderedByteAddressBuffer", align 4 +// CHECK-NEXT: call void @_ZN4hlsl34RasterizerOrderedByteAddressBufferC1Ev(ptr noundef nonnull align 4 dereferenceable(4) %Buf3) + +// CHECK: define linkonce_odr void @_ZN4hlsl34RasterizerOrderedByteAddressBufferC1Ev(ptr noundef nonnull align 4 dereferenceable(4) %this) +// CHECK: call void @_ZN4hlsl34RasterizerOrderedByteAddressBufferC2Ev(ptr noundef nonnull align 4 dereferenceable(4) %this1) + +// +// Buf1 initialization part 2 - body of constructor that creates handle from binding +// +// CHECK: define linkonce_odr void @_ZN4hlsl17ByteAddressBufferC2Ejjij(ptr noundef nonnull align 4 dereferenceable(4) %this, +// CHECK-SAME: i32 noundef %spaceNo, i32 noundef %registerNo, i32 noundef %range, i32 noundef %index) +// CHECK-DXIL: %[[HANDLE:.*]] = call target("dx.RawBuffer", i8, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_i8_0_0t( +// CHECK-SAME: i32 %0, i32 %1, i32 %2, i32 %3, i1 false) +// CHECK: %[[HANDLE_PTR:.*]] = getelementptr inbounds nuw %"class.hlsl::ByteAddressBuffer", ptr %this1, i32 0, i32 0 +// CHECK: store target("dx.RawBuffer", i8, 0, 0) %[[HANDLE]], ptr %[[HANDLE_PTR]], align 4 + +// +// Buf2 initialization part 2 - body of default constructor that initializes handle to poison +// +// CHECK: define linkonce_odr void @_ZN4hlsl19RWByteAddressBufferC2Ev(ptr noundef nonnull align 4 dereferenceable(4) %this) +// CHECK: %[[HANDLE_PTR:.*]] = getelementptr inbounds nuw %"class.hlsl::RWByteAddressBuffer", ptr %this1, i32 0, i32 0 +// CHECK: store i8 poison, ptr %[[HANDLE_PTR]], align 4 + +// +// Buf3 initialization part 2 - body of default constructor that initializes handle to poison +// +// CHECK: define linkonce_odr void @_ZN4hlsl34RasterizerOrderedByteAddressBufferC2Ev(ptr noundef nonnull align 4 dereferenceable(4) %this) +// CHECK: %[[HANDLE_PTR:.*]] = getelementptr inbounds nuw %"class.hlsl::RasterizerOrderedByteAddressBuffer", ptr %this1, i32 0, i32 0 +// CHECK: store i8 poison, ptr %[[HANDLE_PTR]], align 4 + +// +// Module initialization +// // CHECK: define internal void @_GLOBAL__sub_I_ByteAddressBuffers_constructors.hlsl() -// CHECK: entry: -// CHECK: call void @_init_resource__ZL7Buffer0() -// CHECK: call void @_init_resource__ZL7Buffer1() -// CHECK: call void @_init_resource__ZL7Buffer2() +// CHECK-NEXT: entry: +// CHECK-NEXT: call void @__cxx_global_var_init() +// CHECK-NEXT: call void @__cxx_global_var_init.1() diff --git a/clang/test/CodeGenHLSL/builtins/RWBuffer-constructor-opt.hlsl b/clang/test/CodeGenHLSL/builtins/RWBuffer-constructor-opt.hlsl index 56c523f6bc8cf..c83a53897c045 100644 --- a/clang/test/CodeGenHLSL/builtins/RWBuffer-constructor-opt.hlsl +++ b/clang/test/CodeGenHLSL/builtins/RWBuffer-constructor-opt.hlsl @@ -1,7 +1,7 @@ // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -emit-llvm -O3 -o - %s | FileCheck %s // RUN: %clang_cc1 -triple spirv-vulkan-compute -x hlsl -emit-llvm -O3 -o - %s | FileCheck %s -// All referenced to an unused resource should be removed by optimizations. +// All references to an unused resource should be removed by optimizations. RWBuffer<float> Buf : register(u5, space3); [shader("compute")] diff --git a/clang/test/CodeGenHLSL/builtins/RWBuffer-constructor.hlsl b/clang/test/CodeGenHLSL/builtins/RWBuffer-constructor.hlsl index 5324176a7b9bb..9bb5121bcf7e8 100644 --- a/clang/test/CodeGenHLSL/builtins/RWBuffer-constructor.hlsl +++ b/clang/test/CodeGenHLSL/builtins/RWBuffer-constructor.hlsl @@ -4,17 +4,81 @@ // NOTE: SPIRV codegen for resource types is not yet implemented -RWBuffer<float> Buf : register(u5, space3); +RWBuffer<float> Buf1 : register(u5, space3); +RWBuffer<double> Buf2; + +export void foo() { + RWBuffer<int> Buf3; +} // CHECK: %"class.hlsl::RWBuffer" = type { target("dx.TypedBuffer", float, 1, 0, 0) } -// CHECK: @_ZL3Buf = internal global %"class.hlsl::RWBuffer" poison, align 4 +// CHECK: %"class.hlsl::RWBuffer.0" = type { target("dx.TypedBuffer", double, 1, 0, 0) } +// CHECK: %"class.hlsl::RWBuffer.1" = type { target("dx.TypedBuffer", i32, 1, 0, 1) } -// CHECK: define internal void @_init_resource__ZL3Buf() -// CHECK-DXIL: [[H:%.*]] = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0_0t(i32 3, i32 5, i32 1, i32 0, i1 false) -// CHECK-DXIL: store target("dx.TypedBuffer", float, 1, 0, 0) [[H]], ptr @_ZL3Buf, align 4 +// CHECK: @_ZL4Buf1 = internal global %"class.hlsl::RWBuffer" poison, align 4 +// CHECK: @_ZL4Buf2 = internal global %"class.hlsl::RWBuffer.0" poison, align 4 -// CHECK: define linkonce_odr void @_ZN4hlsl8RWBufferIfEC2Ev(ptr noundef nonnull align 4 dereferenceable(4) %this) +// +// Buf1 initialization part 1 - constructor that creates handle from binding +// +// CHECK: define internal void @__cxx_global_var_init() // CHECK-NEXT: entry: +// CHECK-NEXT: call void @_ZN4hlsl8RWBufferIfEC1Ejjij(ptr noundef nonnull align 4 dereferenceable(4) @_ZL4Buf1, +// CHECK-SAME: i32 noundef 3, i32 noundef 5, i32 noundef 1, i32 noundef 0) + +// CHECK: define linkonce_odr void @_ZN4hlsl8RWBufferIfEC1Ejjij(ptr noundef nonnull align 4 dereferenceable(4) %this, +// CHECK-SAME: i32 noundef %spaceNo, i32 noundef %registerNo, i32 noundef %range, i32 noundef %index) +// CHECK: call void @_ZN4hlsl8RWBufferIfEC2Ejjij( +// +// Buf2 initialization part 1 - default constructor that initializes handle to poison +// + +// CHECK: define internal void @__cxx_global_var_init.1() #0 { +// CHECK-NEXT: entry: +// CHECK-NEXT: call void @_ZN4hlsl8RWBufferIdEC1Ev(ptr noundef nonnull align 4 dereferenceable(4) @_ZL4Buf2) + +// CHECK: define linkonce_odr void @_ZN4hlsl8RWBufferIdEC1Ev(ptr noundef nonnull align 4 dereferenceable(4) %this) +// CHECK: call void @_ZN4hlsl8RWBufferIdEC2Ev(ptr noundef nonnull align 4 dereferenceable(4) %this1) + +// +// Buf3 initialization part 1 - local variable with default constructor that initializes handle to poison +// +// CHECK: define void @_Z3foov() +// CHECK-NEXT: entry: +// CHECK-NEXT: %Buf3 = alloca %"class.hlsl::RWBuffer.1", align 4 +// CHECK-NEXT: call void @_ZN4hlsl8RWBufferIiEC1Ev(ptr noundef nonnull align 4 dereferenceable(4) %Buf3) + +// CHECK: define linkonce_odr void @_ZN4hlsl8RWBufferIiEC1Ev(ptr noundef nonnull align 4 dereferenceable(4) %this) +// CHECK: call void @_ZN4hlsl8RWBufferIiEC2Ev(ptr noundef nonnull align 4 dereferenceable(4) %this1) + +// +// Buf1 initialization part 2 - body of constructor that creates handle from binding +// +// CHECK: define linkonce_odr void @_ZN4hlsl8RWBufferIfEC2Ejjij(ptr noundef nonnull align 4 dereferenceable(4) %this, +// CHECK-SAME: i32 noundef %spaceNo, i32 noundef %registerNo, i32 noundef %range, i32 noundef %index) +// CHECK-DXIL: %[[HANDLE:.*]] = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0_0t( +// CHECK: %[[HANDLE_PTR:.*]] = getelementptr inbounds nuw %"class.hlsl::RWBuffer", ptr %this1, i32 0, i32 0 +// CHECK: store target("dx.TypedBuffer", float, 1, 0, 0) %[[HANDLE]], ptr %[[HANDLE_PTR]], align 4 + +// +// Buf2 initialization part 2 - body of default constructor that initializes handle to poison +// +// CHECK: define linkonce_odr void @_ZN4hlsl8RWBufferIdEC2Ev(ptr noundef nonnull align 4 dereferenceable(4) %this) +// CHECK: %[[HANDLE_PTR:.*]] = getelementptr inbounds nuw %"class.hlsl::RWBuffer.0", ptr %this1, i32 0, i32 0 +// CHECK: store target("dx.TypedBuffer", double, 1, 0, 0) poison, ptr %[[HANDLE_PTR]], align 4 + +// +// Buf3 initialization part 2 - body of default constructor that initializes handle to poison +// +// CHECK: define linkonce_odr void @_ZN4hlsl8RWBufferIiEC2Ev(ptr noundef nonnull align 4 dereferenceable(4) %this) +// CHECK: %[[HANDLE_PTR:.*]] = getelementptr inbounds nuw %"class.hlsl::RWBuffer.1", ptr %this1, i32 0, i32 0 +// CHECK: store target("dx.TypedBuffer", i32, 1, 0, 1) poison, ptr %[[HANDLE_PTR]], align 4 + +// +// Module initialization +// // CHECK: define internal void @_GLOBAL__sub_I_RWBuffer_constructor.hlsl() -// CHECK: call void @_init_resource__ZL3Buf() +// CHECK-NEXT: entry: +// CHECK-NEXT: call void @__cxx_global_var_init() +// CHECK-NEXT: call void @__cxx_global_var_init.1() diff --git a/clang/test/CodeGenHLSL/builtins/StructuredBuffers-constructors.hlsl b/clang/test/CodeGenHLSL/builtins/StructuredBuffers-constructors.hlsl index 04534c5550252..da205816dd762 100644 --- a/clang/test/CodeGenHLSL/builtins/StructuredBuffers-constructors.hlsl +++ b/clang/test/CodeGenHLSL/builtins/StructuredBuffers-constructors.hlsl @@ -3,57 +3,88 @@ // NOTE: SPIRV codegen for resource types is not yet implemented -StructuredBuffer<float> Buf : register(t10); -RWStructuredBuffer<float> Buf2 : register(u5, space1); -AppendStructuredBuffer<float> Buf3 : register(u3); -ConsumeStructuredBuffer<float> Buf4 : register(u4); -RasterizerOrderedStructuredBuffer<float> Buf5 : register(u1, space2); +StructuredBuffer<float> Buf1 : register(t10, space2); +RWStructuredBuffer<float> Buf2; + +export void foo() { + AppendStructuredBuffer<float> Buf3; +} // CHECK: %"class.hlsl::StructuredBuffer" = type { target("dx.RawBuffer", float, 0, 0) } // CHECK: %"class.hlsl::RWStructuredBuffer" = type { target("dx.RawBuffer", float, 1, 0) } // CHECK: %"class.hlsl::AppendStructuredBuffer" = type { target("dx.RawBuffer", float, 1, 0) } -// CHECK: %"class.hlsl::ConsumeStructuredBuffer" = type { target("dx.RawBuffer", float, 1, 0) } -// CHECK: %"class.hlsl::RasterizerOrderedStructuredBuffer" = type { target("dx.RawBuffer", float, 1, 1) } -// CHECK: @_ZL3Buf = internal global %"class.hlsl::StructuredBuffer" poison, align 4 +// CHECK: @_ZL4Buf1 = internal global %"class.hlsl::StructuredBuffer" poison, align 4 // CHECK: @_ZL4Buf2 = internal global %"class.hlsl::RWStructuredBuffer" poison, align 4 -// CHECK: @_ZL4Buf3 = internal global %"class.hlsl::AppendStructuredBuffer" poison, align 4 -// CHECK: @_ZL4Buf4 = internal global %"class.hlsl::ConsumeStructuredBuffer" poison, align 4 -// CHECK: @_ZL4Buf5 = internal global %"class.hlsl::RasterizerOrderedStructuredBuffer" poison, align 4 -// CHECK: define internal void @_init_resource__ZL3Buf() -// CHECK-DXIL: [[H:%.*]] = call target("dx.RawBuffer", float, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_0_0t(i32 0, i32 10, i32 1, i32 0, i1 false) -// CHECK-DXIL: store target("dx.RawBuffer", float, 0, 0) [[H]], ptr @_ZL3Buf, align 4 +// +// Buf1 initialization part 1 - constructor that creates handle from binding +// + +// CHECK: define internal void @__cxx_global_var_init() +// CHECK-NEXT: entry: +// CHECK-NEXT: call void @_ZN4hlsl16StructuredBufferIfEC1Ejjij(ptr noundef nonnull align 4 dereferenceable(4) @_ZL4Buf1, i32 noundef 2, i32 noundef 10, i32 noundef 1, i32 noundef 0) -// CHECK: define internal void @_init_resource__ZL4Buf2() -// CHECK-DXIL: [[H:%.*]] = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_1_0t(i32 1, i32 5, i32 1, i32 0, i1 false) -// CHECK-DXIL: store target("dx.RawBuffer", float, 1, 0) [[H]], ptr @_ZL4Buf2, align 4 +// CHECK: define linkonce_odr void @_ZN4hlsl16StructuredBufferIfEC1Ejjij(ptr noundef nonnull align 4 dereferenceable(4) %this, +// CHECK-SAME: i32 noundef %spaceNo, i32 noundef %registerNo, i32 noundef %range, i32 noundef %index) +// CHECK: call void @_ZN4hlsl16StructuredBufferIfEC2Ejjij(ptr noundef nonnull align 4 dereferenceable(4) %this1, +// CHECK-SAME: i32 noundef %0, i32 noundef %1, i32 noundef %2, i32 noundef %3) -// CHECK: define internal void @_init_resource__ZL4Buf3() -// CHECK-DXIL: [[H:%.*]] = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_1_0t(i32 0, i32 3, i32 1, i32 0, i1 false) -// CHECK-DXIL: store target("dx.RawBuffer", float, 1, 0) [[H]], ptr @_ZL4Buf3, align 4 +// +// Buf2 initialization part 1 - default constructor that initializes handle to poison +// + +// CHECK: define internal void @__cxx_global_var_init.1() +// CHECK-NEXT: entry: +// CHECK-NEXT: call void @_ZN4hlsl18RWStructuredBufferIfEC1Ev(ptr noundef nonnull align 4 dereferenceable(4) @_ZL4Buf2) -// CHECK: define internal void @_init_resource__ZL4Buf4() -// CHECK-DXIL: [[H:%.*]] = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_1_0t(i32 0, i32 4, i32 1, i32 0, i1 false) -// CHECK-DXIL: store target("dx.RawBuffer", float, 1, 0) [[H]], ptr @_ZL4Buf4, align 4 +// CHECK: define linkonce_odr void @_ZN4hlsl18RWStructuredBufferIfEC1Ev(ptr noundef nonnull align 4 dereferenceable(4) %this) +// CHECK: call void @_ZN4hlsl18RWStructuredBufferIfEC2Ev(ptr noundef nonnull align 4 dereferenceable(4) %this1) -// CHECK: define internal void @_init_resource__ZL4Buf5() -// CHECK-DXIL: [[H:%.*]] = call target("dx.RawBuffer", float, 1, 1) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_1_1t(i32 2, i32 1, i32 1, i32 0, i1 false) -// CHECK-DXIL: store target("dx.RawBuffer", float, 1, 1) [[H]], ptr @_ZL4Buf5, align 4 +// +// Buf3 initialization part 1 - local variable with default constructor that initializes handle to poison +// -// CHECK: define linkonce_odr void @_ZN4hlsl16StructuredBufferIfEC2Ev(ptr noundef nonnull align 4 dereferenceable(4) %this) +// CHECK: define void @_Z3foov() // CHECK-NEXT: entry: +// CHECK-NEXT: %Buf3 = alloca %"class.hlsl::AppendStructuredBuffer", align 4 +// CHECK-NEXT: call void @_ZN4hlsl22AppendStructuredBufferIfEC1Ev(ptr noundef nonnull align 4 dereferenceable(4) %Buf3) + +// CHECK: define linkonce_odr void @_ZN4hlsl22AppendStructuredBufferIfEC1Ev(ptr noundef nonnull align 4 dereferenceable(4) %this) +// CHECK: call void @_ZN4hlsl22AppendStructuredBufferIfEC2Ev(ptr noundef nonnull align 4 dereferenceable(4) %this1) + +// +// Buf1 initialization part 2 - body of constructor that creates handle from binding +// + +// CHECK: define linkonce_odr void @_ZN4hlsl16StructuredBufferIfEC2Ejjij(ptr noundef nonnull align 4 dereferenceable(4) %this, +// CHECK-SAME: i32 noundef %spaceNo, i32 noundef %registerNo, i32 noundef %range, i32 noundef %index) +// CHECK-DXIL: %[[HANDLE:.*]] = call target("dx.RawBuffer", float, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_0_0t( +// CHECK-SAME: i32 %0, i32 %1, i32 %2, i32 %3, i1 false) +// CHECK: %[[HANDLE_PTR:.*]] = getelementptr inbounds nuw %"class.hlsl::StructuredBuffer", ptr %this1, i32 0, i32 0 +// CHECK: store target("dx.RawBuffer", float, 0, 0) %4, ptr %[[HANDLE_PTR]], align 4 + +// +// Buf2 initialization part 2 - body of default constructor that initializes handle to poison +// + // CHECK: define linkonce_odr void @_ZN4hlsl18RWStructuredBufferIfEC2Ev(ptr noundef nonnull align 4 dereferenceable(4) %this) -// CHECK-NEXT: entry: +// CHECK: %[[HANDLE_PTR:.*]] = getelementptr inbounds nuw %"class.hlsl::RWStructuredBuffer", ptr %this1, i32 0, i32 0 +// CHECK: store target("dx.RawBuffer", float, 1, 0) poison, ptr %[[HANDLE_PTR]], align 4 + +// +// Buf3 initialization part 2 - body of default constructor that initializes handle to poison +// + // CHECK: define linkonce_odr void @_ZN4hlsl22AppendStructuredBufferIfEC2Ev(ptr noundef nonnull align 4 dereferenceable(4) %this) -// CHECK-NEXT: entry: -// CHECK: define linkonce_odr void @_ZN4hlsl23ConsumeStructuredBufferIfEC2Ev(ptr noundef nonnull align 4 dereferenceable(4) %this) -// CHECK: define linkonce_odr void @_ZN4hlsl33RasterizerOrderedStructuredBufferIfEC2Ev(ptr noundef nonnull align 4 dereferenceable(4) %this) -// CHECK-NEXT: entry: +// CHECK: %[[HANDLE_PTR:.*]] = getelementptr inbounds nuw %"class.hlsl::AppendStructuredBuffer", ptr %this1, i32 0, i32 0 +// CHECK: store target("dx.RawBuffer", float, 1, 0) poison, ptr %[[HANDLE_PTR]], align 4 + +// +// Module initialization +// // CHECK: define internal void @_GLOBAL__sub_I_StructuredBuffers_constructors.hlsl() -// CHECK: call void @_init_resource__ZL3Buf() -// CHECK: call void @_init_resource__ZL4Buf2() -// CHECK: call void @_init_resource__ZL4Buf3() -// CHECK: call void @_init_resource__ZL4Buf4() -// CHECK: call void @_init_resource__ZL4Buf5() +// CHECK-NEXT: entry: +// CHECK-NEXT: call void @__cxx_global_var_init() +// CHECK-NEXT: call void @__cxx_global_var_init.1() diff --git a/clang/test/CodeGenHLSL/resource-bindings.hlsl b/clang/test/CodeGenHLSL/resource-bindings.hlsl index 3342fb55a59a4..3a7b9fc3e4952 100644 --- a/clang/test/CodeGenHLSL/resource-bindings.hlsl +++ b/clang/test/CodeGenHLSL/resource-bindings.hlsl @@ -1,34 +1,46 @@ // RUN: %clang_cc1 -triple dxil--shadermodel6.6-compute -x hlsl -finclude-default-header -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s -// CHECK: define internal void @_init_resource__ZL4U0S0() -// CHECK: [[H:%.*]] = call target("dx.TypedBuffer", <4 x float>, 1, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_v4f32_1_0_0t(i32 0, i32 0, i32 1, i32 0, i1 false) -// CHECK: store target("dx.TypedBuffer", <4 x float>, 1, 0, 0) [[H]], ptr @_ZL4U0S0, align 4 +// CHECK: %"class.hlsl::RWBuffer" = type { target("dx.TypedBuffer", <4 x float>, 1, 0, 0) } +// CHECK: %"class.hlsl::RWBuffer.0" = type { target("dx.TypedBuffer", float, 1, 0, 0) } +// CHECK: %"class.hlsl::StructuredBuffer" = type { target("dx.RawBuffer", i32, 0, 0) } +// CHECK: %"class.hlsl::RWStructuredBuffer" = type { target("dx.RawBuffer", %struct.S, 1, 0) } + +// CHECK: @_ZL4U0S0 = internal global %"class.hlsl::RWBuffer" poison, align 4 +// CHECK: @_ZL4U5S3 = internal global %"class.hlsl::RWBuffer.0" poison, align 4 +// CHECK: @_ZL4T2S2 = internal global %"class.hlsl::StructuredBuffer" poison, align 4 +// CHECK: @_ZL4T3S0 = internal global %"class.hlsl::RWStructuredBuffer" poison, align 4 + +// CHECK: %[[HANDLE:.*]] = call target("dx.TypedBuffer", <4 x float>, 1, 0, 0) +// CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_v4f32_1_0_0t( +// CHECK-SAME: i32 %{{[0-9]+}}, i32 %{{[0-9]+}}, i32 %{{[0-9]+}}, i32 %{{[0-9]+}}, i1 false) +// CHECK: %[[HANDLE_PTR:.*]] = getelementptr inbounds nuw %"class.hlsl::RWBuffer", ptr %this{{[0-9]*}}, i32 0, i32 0 +// CHECK: store target("dx.TypedBuffer", <4 x float>, 1, 0, 0) %[[HANDLE]], ptr %[[HANDLE_PTR]], align 4 RWBuffer<float4> U0S0 : register(u0); -// CHECK: define internal void @_init_resource__ZL4U5S3() -// CHECK: [[H:%.*]] = call target("dx.TypedBuffer", float, 1, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0_0t(i32 3, i32 5, i32 1, i32 0, i1 false) -// CHECK: store target("dx.TypedBuffer", float, 1, 0, 0) [[H]], ptr @_ZL4U5S3, align 4 +// CHECK: %[[HANDLE:.*]] = call target("dx.TypedBuffer", float, 1, 0, 0) +// CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.TypedBuffer_f32_1_0_0t( +// CHECK-SAME: i32 %{{[0-9]+}}, i32 %{{[0-9]+}}, i32 %{{[0-9]+}}, i32 %{{[0-9]+}}, i1 false) +// CHECK: %[[HANDLE_PTR:.*]] = getelementptr inbounds nuw %"class.hlsl::RWBuffer.0", ptr %this{{[0-9]*}}, i32 0, i32 0 +// CHECK: store target("dx.TypedBuffer", float, 1, 0, 0) %[[HANDLE]], ptr %[[HANDLE_PTR]], align 4 RWBuffer<float> U5S3 : register(u5, space3); -// CHECK: define internal void @_init_resource__ZL4T2S2() -// CHECK: [[H:%.*]] = call target("dx.RawBuffer", i32, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_i32_0_0t(i32 2, i32 2, i32 1, i32 0, i1 false) -// CHECK: store target("dx.RawBuffer", i32, 0, 0) [[H]], ptr @_ZL4T2S2, align 4 +// CHECK: %[[HANDLE:.*]] = call target("dx.RawBuffer", i32, 0, 0) +// CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_i32_0_0t( +// CHECK-SAME: i32 %{{[0-9]+}}, i32 %{{[0-9]+}}, i32 %{{[0-9]+}}, i32 %{{[0-9]+}}, i1 false) +// CHECK: %[[HANDLE_PTR:.*]] = getelementptr inbounds nuw %"class.hlsl::StructuredBuffer", ptr %this{{[0-9]*}}, i32 0, i32 0 +// CHECK: store target("dx.RawBuffer", i32, 0, 0) %[[HANDLE]], ptr %[[HANDLE_PTR]], align 4 StructuredBuffer<int> T2S2 : register(t2, space2); struct S { float4 f; int i; }; -// CHECK: define internal void @_init_resource__ZL4T3S0() -// CHECK: [[H:%.*]] = call target("dx.RawBuffer", %struct.S, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_s_struct.Ss_0_0t(i32 0, i32 3, i32 1, i32 0, i1 false) -// CHECK: store target("dx.RawBuffer", %struct.S, 0, 0) [[H]], ptr @_ZL4T3S0, align 4 -StructuredBuffer<S> T3S0 : register(t3); - -// CHECK: define void @main() -// CHECK: call void @_init_resource__ZL4U0S0() -// CHECK: call void @_init_resource__ZL4U5S3() -// CHECK: call void @_init_resource__ZL4T2S2() -// CHECK: call void @_init_resource__ZL4T3S0() +// CHECK: %[[HANDLE:.*]] = call target("dx.RawBuffer", %struct.S, 1, 0) +// CHECK-SAME: @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_s_struct.Ss_1_0t( +// CHECK-SAME: i32 %{{[0-9]+}}, i32 %{{[0-9]+}}, i32 %{{[0-9]+}}, i32 %{{[0-9]+}}, i1 false) +// CHECK: %[[HANDLE_PTR:.*]] = getelementptr inbounds nuw %"class.hlsl::RWStructuredBuffer", ptr %this{{[0-9]*}}, i32 0, i32 0 +// CHECK: store target("dx.RawBuffer", %struct.S, 1, 0) %[[HANDLE]], ptr %[[HANDLE_PTR]], align 4 +RWStructuredBuffer<S> T3S0 : register(u3); [numthreads(4,1,1)] void main() {} _______________________________________________ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits