https://github.com/hekota updated https://github.com/llvm/llvm-project/pull/138976
>From dfce6ce8edc520251c0561cfd50cc6c11af46ecd Mon Sep 17 00:00:00 2001 From: Helena Kotas <heko...@microsoft.com> Date: Wed, 7 May 2025 14:43:27 -0700 Subject: [PATCH] [HLSL] Add resource constructor with implicit binding for global resources Adds constructor for resources with implicit binding and applies it to all resources without binding at the global scope. Adds Clang buildin function __builtin_hlsl_resource_handlefromimplicitbinding that it translated to llvm.dx|spv.resource.handlefromimplicitbinding calls. Specific bindings are assigned in DXILResourceImplicitBinding pass. Depends on #138043 Closes #136784 --- clang/include/clang/Basic/Builtins.td | 6 ++ clang/include/clang/Sema/SemaHLSL.h | 7 ++ clang/lib/CodeGen/CGHLSLBuiltins.cpp | 15 ++++ clang/lib/CodeGen/CGHLSLRuntime.h | 2 + clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp | 20 ++++++ clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h | 3 +- clang/lib/Sema/HLSLExternalSemaSource.cpp | 3 +- clang/lib/Sema/SemaHLSL.cpp | 72 +++++++++++++------ .../test/AST/HLSL/ByteAddressBuffers-AST.hlsl | 22 ++++++ .../test/AST/HLSL/StructuredBuffers-AST.hlsl | 22 ++++++ clang/test/AST/HLSL/TypedBuffers-AST.hlsl | 22 ++++++ .../CodeGenHLSL/GlobalConstructorLib.hlsl | 2 +- .../ByteAddressBuffers-constructors.hlsl | 55 ++++++++++++-- .../builtins/RWBuffer-constructor.hlsl | 55 ++++++++++++-- .../StructuredBuffers-constructors.hlsl | 54 +++++++++++++- .../StructuredBuffers-methods-ps.hlsl | 6 +- clang/test/CodeGenHLSL/static-local-ctor.hlsl | 2 +- llvm/include/llvm/IR/IntrinsicsSPIRV.td | 5 ++ 18 files changed, 335 insertions(+), 38 deletions(-) diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td index 11b1e247237a7..187d3b5ed24a7 100644 --- a/clang/include/clang/Basic/Builtins.td +++ b/clang/include/clang/Basic/Builtins.td @@ -4819,6 +4819,12 @@ def HLSLResourceHandleFromBinding : LangBuiltin<"HLSL_LANG"> { let Prototype = "void(...)"; } +def HLSLResourceHandleFromImplicitBinding : LangBuiltin<"HLSL_LANG"> { + let Spellings = ["__builtin_hlsl_resource_handlefromimplicitbinding"]; + 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 5d260acf92abb..bedf541439dbf 100644 --- a/clang/include/clang/Sema/SemaHLSL.h +++ b/clang/include/clang/Sema/SemaHLSL.h @@ -174,6 +174,8 @@ class SemaHLSL : public SemaBase { // buffer which will be created at the end of the translation unit. llvm::SmallVector<Decl *> DefaultCBufferDecls; + uint32_t ImplicitBindingNextOrderID = 0; + private: void collectResourceBindingsOnVarDecl(VarDecl *D); void collectResourceBindingsOnUserRecordDecl(const VarDecl *VD, @@ -181,6 +183,11 @@ class SemaHLSL : public SemaBase { void processExplicitBindingsOnDecl(VarDecl *D); void diagnoseAvailabilityViolations(TranslationUnitDecl *TU); + + bool initGlobalResourceDecl(VarDecl *VD); + uint32_t getNextImplicitBindingOrderID() { + return ImplicitBindingNextOrderID++; + } }; } // namespace clang diff --git a/clang/lib/CodeGen/CGHLSLBuiltins.cpp b/clang/lib/CodeGen/CGHLSLBuiltins.cpp index 5d93df34c66b2..d4a0714da07b3 100644 --- a/clang/lib/CodeGen/CGHLSLBuiltins.cpp +++ b/clang/lib/CodeGen/CGHLSLBuiltins.cpp @@ -303,6 +303,21 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID, HandleTy, CGM.getHLSLRuntime().getCreateHandleFromBindingIntrinsic(), ArrayRef<Value *>{SpaceOp, RegisterOp, RangeOp, IndexOp, NonUniform}); } + case Builtin::BI__builtin_hlsl_resource_handlefromimplicitbinding: { + llvm::Type *HandleTy = CGM.getTypes().ConvertType(E->getType()); + Value *SpaceOp = EmitScalarExpr(E->getArg(1)); + Value *RangeOp = EmitScalarExpr(E->getArg(2)); + Value *IndexOp = EmitScalarExpr(E->getArg(3)); + Value *OrderID = 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().getCreateHandleFromImplicitBindingIntrinsic(), + ArrayRef<Value *>{OrderID, SpaceOp, 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.h b/clang/lib/CodeGen/CGHLSLRuntime.h index 4d6db3f5d9f3e..e40864d8ed854 100644 --- a/clang/lib/CodeGen/CGHLSLRuntime.h +++ b/clang/lib/CodeGen/CGHLSLRuntime.h @@ -119,6 +119,8 @@ class CGHLSLRuntime { 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) diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp index 35364a4d6f2ac..d5fbd5f6ecc9f 100644 --- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp +++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp @@ -668,6 +668,26 @@ BuiltinTypeDeclBuilder::addHandleConstructorFromBinding() { .finalize(); } +BuiltinTypeDeclBuilder & +BuiltinTypeDeclBuilder::addHandleConstructorFromImplicitBinding() { + 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("range", AST.IntTy) + .addParam("index", AST.UnsignedIntTy) + .addParam("order_id", AST.UnsignedIntTy) + .callBuiltin("__builtin_hlsl_resource_handlefromimplicitbinding", + HandleType, PH::Handle, PH::_0, PH::_1, PH::_2, PH::_3) + .assign(PH::Handle, PH::LastStmt) + .finalize(); +} + BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addArraySubscriptOperators() { ASTContext &AST = Record->getASTContext(); DeclarationName Subscript = diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h index db617dc53c899..a52e2938104c7 100644 --- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h +++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h @@ -76,9 +76,10 @@ class BuiltinTypeDeclBuilder { AccessSpecifier Access = AccessSpecifier::AS_private); BuiltinTypeDeclBuilder &addArraySubscriptOperators(); - // Builtin types methods + // Builtin types constructors BuiltinTypeDeclBuilder &addDefaultHandleConstructor(); BuiltinTypeDeclBuilder &addHandleConstructorFromBinding(); + BuiltinTypeDeclBuilder &addHandleConstructorFromImplicitBinding(); // Builtin types methods BuiltinTypeDeclBuilder &addLoadMethods(); diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp index f09232a9db4da..38bde7c28e946 100644 --- a/clang/lib/Sema/HLSLExternalSemaSource.cpp +++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp @@ -132,7 +132,8 @@ static BuiltinTypeDeclBuilder setupBufferType(CXXRecordDecl *Decl, Sema &S, return BuiltinTypeDeclBuilder(S, Decl) .addHandleMember(RC, IsROV, RawBuffer) .addDefaultHandleConstructor() - .addHandleConstructorFromBinding(); + .addHandleConstructorFromBinding() + .addHandleConstructorFromImplicitBinding(); } // This function is responsible for constructing the constraint expression for diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index 70aacaa2aadbe..c0669a8d60470 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -2427,6 +2427,20 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) { TheCall->setType(ResourceTy); break; } + case Builtin::BI__builtin_hlsl_resource_handlefromimplicitbinding: { + 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.IntTy) || + CheckArgTypeMatches(&SemaRef, TheCall->getArg(3), AST.UnsignedIntTy) || + 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)) @@ -3258,8 +3272,10 @@ static bool initVarDeclWithCtor(Sema &S, VarDecl *VD, VD->getLocation(), SourceLocation(), SourceLocation()); InitializationSequence InitSeq(S, Entity, Kind, Args); - ExprResult Init = InitSeq.Perform(S, Entity, Kind, Args); + if (InitSeq.Failed()) + return false; + ExprResult Init = InitSeq.Perform(S, Entity, Kind, Args); if (!Init.get()) return false; @@ -3269,27 +3285,42 @@ static bool initVarDeclWithCtor(Sema &S, VarDecl *VD, return true; } -static bool initGlobalResourceDecl(Sema &S, VarDecl *VD) { +bool SemaHLSL::initGlobalResourceDecl(VarDecl *VD) { + std::optional<uint32_t> RegisterSlot; + uint32_t SpaceNo = 0; HLSLResourceBindingAttr *RBA = VD->getAttr<HLSLResourceBindingAttr>(); - if (!RBA || RBA->isImplicit()) - // FIXME: add support for implicit binding (llvm/llvm-project#110722) - return false; + if (RBA) { + if (!RBA->isImplicit()) + RegisterSlot = RBA->getSlotNumber(); + SpaceNo = RBA->getSpaceNumber(); + } - ASTContext &AST = S.getASTContext(); + 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, 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); + IntegerLiteral *One = IntegerLiteral::Create(AST, llvm::APInt(IntTySize, 1), + AST.IntTy, SourceLocation()); + IntegerLiteral *Zero = IntegerLiteral::Create( + AST, llvm::APInt(UIntTySize, 0), AST.UnsignedIntTy, SourceLocation()); + IntegerLiteral *Space = + IntegerLiteral::Create(AST, llvm::APInt(UIntTySize, SpaceNo), + AST.UnsignedIntTy, SourceLocation()); + + // resource with explicit binding + if (RegisterSlot.has_value()) { + IntegerLiteral *RegSlot = IntegerLiteral::Create( + AST, llvm::APInt(UIntTySize, RegisterSlot.value()), AST.UnsignedIntTy, + SourceLocation()); + Expr *Args[] = {RegSlot, Space, One, Zero}; + return initVarDeclWithCtor(SemaRef, VD, Args); + } + + // resource with explicit binding + IntegerLiteral *OrderId = IntegerLiteral::Create( + AST, llvm::APInt(UIntTySize, getNextImplicitBindingOrderID()), + AST.UnsignedIntTy, SourceLocation()); + Expr *Args[] = {Space, One, Zero, OrderId}; + return initVarDeclWithCtor(SemaRef, VD, Args); } // Returns true if the initialization has been handled. @@ -3307,8 +3338,9 @@ bool SemaHLSL::ActOnUninitializedVarDecl(VarDecl *VD) { // 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); + // Initialize resources at the global scope + if (VD->hasGlobalStorage() && VD->getType()->isHLSLResourceRecord()) + return initGlobalResourceDecl(VD); return false; } diff --git a/clang/test/AST/HLSL/ByteAddressBuffers-AST.hlsl b/clang/test/AST/HLSL/ByteAddressBuffers-AST.hlsl index 5fba939d29cfe..99f26473dbb02 100644 --- a/clang/test/AST/HLSL/ByteAddressBuffers-AST.hlsl +++ b/clang/test/AST/HLSL/ByteAddressBuffers-AST.hlsl @@ -78,5 +78,27 @@ RESOURCE Buffer; // CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' ParmVar {{.*}} 'index' 'unsigned int' // CHECK-NEXT: AlwaysInlineAttr +// Constructor from implicit binding + +// CHECK: CXXConstructorDecl {{.*}} [[RESOURCE]] 'void (unsigned int, int, unsigned int, unsigned int)' inline +// CHECK-NEXT: ParmVarDecl {{.*}} spaceNo 'unsigned int' +// CHECK-NEXT: ParmVarDecl {{.*}} range 'int' +// CHECK-NEXT: ParmVarDecl {{.*}} index 'unsigned int' +// CHECK-NEXT: ParmVarDecl {{.*}} order_id 'unsigned int' +// CHECK-NEXT: CompoundStmt {{.*}} +// CHECK-NEXT: BinaryOperator {{.*}} '=' +// CHECK-NEXT: MemberExpr {{.*}} lvalue .__handle +// CHECK-NEXT: CXXThisExpr {{.*}} 'hlsl::[[RESOURCE]]' lvalue implicit this +// CHECK-NEXT: CallExpr {{.*}} '__hlsl_resource_t +// CHECK-NEXT: ImplicitCastExpr {{.*}} <BuiltinFnToFnPtr> +// CHECK-NEXT: DeclRefExpr {{.*}} '<builtin fn type>' Function {{.*}} '__builtin_hlsl_resource_handlefromimplicitbinding' +// CHECK-NEXT: MemberExpr {{.*}} lvalue .__handle +// CHECK-NEXT: CXXThisExpr {{.*}} 'hlsl::[[RESOURCE]]' lvalue implicit this +// CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' ParmVar {{.*}} 'spaceNo' 'unsigned int' +// CHECK-NEXT: DeclRefExpr {{.*}} 'int' ParmVar {{.*}} 'range' 'int' +// CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' ParmVar {{.*}} 'index' 'unsigned int' +// CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' ParmVar {{.*}} 'order_id' 'unsigned int' +// CHECK-NEXT: AlwaysInlineAttr + // CHECK-NOSUBSCRIPT-NOT: CXXMethodDecl {{.*}} operator[] 'const element_type &(unsigned int) const' // CHECK-NOSUBSCRIPT-NOT: CXXMethodDecl {{.*}} operator[] 'element_type &(unsigned int)' diff --git a/clang/test/AST/HLSL/StructuredBuffers-AST.hlsl b/clang/test/AST/HLSL/StructuredBuffers-AST.hlsl index 63265a0003582..03bfa6cb26003 100644 --- a/clang/test/AST/HLSL/StructuredBuffers-AST.hlsl +++ b/clang/test/AST/HLSL/StructuredBuffers-AST.hlsl @@ -125,6 +125,28 @@ RESOURCE<float> Buffer; // CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' ParmVar {{.*}} 'index' 'unsigned int' // CHECK-NEXT: AlwaysInlineAttr +// Constructor from implicit binding + +// CHECK: CXXConstructorDecl {{.*}} [[RESOURCE]]<element_type> 'void (unsigned int, int, unsigned int, unsigned int)' inline +// CHECK-NEXT: ParmVarDecl {{.*}} spaceNo 'unsigned int' +// CHECK-NEXT: ParmVarDecl {{.*}} range 'int' +// CHECK-NEXT: ParmVarDecl {{.*}} index 'unsigned int' +// CHECK-NEXT: ParmVarDecl {{.*}} order_id 'unsigned int' +// CHECK-NEXT: CompoundStmt {{.*}} +// CHECK-NEXT: BinaryOperator {{.*}} '=' +// CHECK-NEXT: MemberExpr {{.*}} lvalue .__handle +// CHECK-NEXT: CXXThisExpr {{.*}} '[[RESOURCE]]<element_type>' lvalue implicit this +// CHECK-NEXT: CallExpr {{.*}} '__hlsl_resource_t +// CHECK-NEXT: ImplicitCastExpr {{.*}} <BuiltinFnToFnPtr> +// CHECK-NEXT: DeclRefExpr {{.*}} '<builtin fn type>' Function {{.*}} '__builtin_hlsl_resource_handlefromimplicitbinding' +// CHECK-NEXT: MemberExpr {{.*}} lvalue .__handle +// CHECK-NEXT: CXXThisExpr {{.*}} '[[RESOURCE]]<element_type>' lvalue implicit this +// CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' ParmVar {{.*}} 'spaceNo' 'unsigned int' +// CHECK-NEXT: DeclRefExpr {{.*}} 'int' ParmVar {{.*}} 'range' 'int' +// CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' ParmVar {{.*}} 'index' 'unsigned int' +// CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' ParmVar {{.*}} 'order_id' 'unsigned int' +// CHECK-NEXT: AlwaysInlineAttr + // Subscript operators // CHECK-SUBSCRIPT: CXXMethodDecl {{.*}} operator[] 'const hlsl_device element_type &(unsigned int) const' diff --git a/clang/test/AST/HLSL/TypedBuffers-AST.hlsl b/clang/test/AST/HLSL/TypedBuffers-AST.hlsl index 6074c1e8bcdd2..f7b720090d436 100644 --- a/clang/test/AST/HLSL/TypedBuffers-AST.hlsl +++ b/clang/test/AST/HLSL/TypedBuffers-AST.hlsl @@ -92,6 +92,28 @@ RESOURCE<float> Buffer; // CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' ParmVar {{.*}} 'index' 'unsigned int' // CHECK-NEXT: AlwaysInlineAttr +// Constructor from implicit binding + +// CHECK: CXXConstructorDecl {{.*}} [[RESOURCE]]<element_type> 'void (unsigned int, int, unsigned int, unsigned int)' inline +// CHECK-NEXT: ParmVarDecl {{.*}} spaceNo 'unsigned int' +// CHECK-NEXT: ParmVarDecl {{.*}} range 'int' +// CHECK-NEXT: ParmVarDecl {{.*}} index 'unsigned int' +// CHECK-NEXT: ParmVarDecl {{.*}} order_id 'unsigned int' +// CHECK-NEXT: CompoundStmt {{.*}} +// CHECK-NEXT: BinaryOperator {{.*}} '=' +// CHECK-NEXT: MemberExpr {{.*}} lvalue .__handle +// CHECK-NEXT: CXXThisExpr {{.*}} '[[RESOURCE]]<element_type>' lvalue implicit this +// CHECK-NEXT: CallExpr {{.*}} '__hlsl_resource_t +// CHECK-NEXT: ImplicitCastExpr {{.*}} <BuiltinFnToFnPtr> +// CHECK-NEXT: DeclRefExpr {{.*}} '<builtin fn type>' Function {{.*}} '__builtin_hlsl_resource_handlefromimplicitbinding' +// CHECK-NEXT: MemberExpr {{.*}} lvalue .__handle +// CHECK-NEXT: CXXThisExpr {{.*}} '[[RESOURCE]]<element_type>' lvalue implicit this +// CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' ParmVar {{.*}} 'spaceNo' 'unsigned int' +// CHECK-NEXT: DeclRefExpr {{.*}} 'int' ParmVar {{.*}} 'range' 'int' +// CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' ParmVar {{.*}} 'index' 'unsigned int' +// CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' ParmVar {{.*}} 'order_id' 'unsigned int' +// CHECK-NEXT: AlwaysInlineAttr + // Subsctript operators // CHECK: CXXMethodDecl {{.*}} operator[] 'const hlsl_device element_type &(unsigned int) const' diff --git a/clang/test/CodeGenHLSL/GlobalConstructorLib.hlsl b/clang/test/CodeGenHLSL/GlobalConstructorLib.hlsl index b798c2a6d6c4b..1d451acfc6214 100644 --- a/clang/test/CodeGenHLSL/GlobalConstructorLib.hlsl +++ b/clang/test/CodeGenHLSL/GlobalConstructorLib.hlsl @@ -33,7 +33,7 @@ void SecondEntry() {} // Verify the constructor is alwaysinline // NOINLINE: ; Function Attrs: {{.*}}alwaysinline -// NOINLINE-NEXT: define linkonce_odr void @_ZN4hlsl8RWBufferIfEC2Ev({{.*}} [[CtorAttr:\#[0-9]+]] +// NOINLINE-NEXT: define linkonce_odr void @_ZN4hlsl8RWBufferIfEC2Ejijj({{.*}} [[CtorAttr:\#[0-9]+]] // NOINLINE: ; Function Attrs: {{.*}}alwaysinline // NOINLINE-NEXT: define internal void @_GLOBAL__sub_I_GlobalConstructorLib.hlsl() [[InitAttr:\#[0-9]+]] diff --git a/clang/test/CodeGenHLSL/builtins/ByteAddressBuffers-constructors.hlsl b/clang/test/CodeGenHLSL/builtins/ByteAddressBuffers-constructors.hlsl index d7c4b03552cdc..7bc9b624ba9b9 100644 --- a/clang/test/CodeGenHLSL/builtins/ByteAddressBuffers-constructors.hlsl +++ b/clang/test/CodeGenHLSL/builtins/ByteAddressBuffers-constructors.hlsl @@ -55,11 +55,33 @@ export void foo() { // CHECK-SAME: i32 noundef %0, i32 noundef %1, i32 noundef %2, i32 noundef %3) // CHECK-NEXT: ret void -// Buf2 initialization part 1 - FIXME: constructor with implicit binding does not exist yet; -// the global init function currently calls the default RWByteAddressBuffer C1 constructor -// CHECK: define internal void @__cxx_global_var_init.1() +// Buf2 initialization part 1 - global init function that calls RWByteAddressBuffer C1 constructor with implicit binding +// CHECK: define internal void @__cxx_global_var_init.1() #0 { // CHECK-NEXT: entry: -// CHECK-NEXT: call void @_ZN4hlsl19RWByteAddressBufferC1Ev(ptr noundef nonnull align 4 dereferenceable(4) @_ZL4Buf2) +// CHECK-NEXT: call void @_ZN4hlsl19RWByteAddressBufferC1Ejijj(ptr noundef nonnull align 4 dereferenceable(4) @_ZL4Buf2, +// CHECK-SAME: i32 noundef 0, i32 noundef 1, i32 noundef 0, i32 noundef 0) + +// Buf2 initialization part 2 - body of RWByteAddressBuffer C1 constructor with implicit binding that calls the C2 constructor +// CHECK: define linkonce_odr void @_ZN4hlsl19RWByteAddressBufferC1Ejijj(ptr noundef nonnull align 4 dereferenceable(4) %this, +// CHECK-SAME: i32 noundef %spaceNo, i32 noundef %range, i32 noundef %index, i32 noundef %order_id) +// CHECK-NEXT: entry: +// CHECK-NEXT: %this.addr = alloca ptr, align 4 +// CHECK-NEXT: %spaceNo.addr = alloca i32, align 4 +// CHECK-NEXT: %range.addr = alloca i32, align 4 +// CHECK-NEXT: %index.addr = alloca i32, align 4 +// CHECK-NEXT: %order_id.addr = alloca i32, align 4 +// CHECK-NEXT: store ptr %this, ptr %this.addr, align 4 +// CHECK-NEXT: store i32 %spaceNo, ptr %spaceNo.addr, align 4 +// CHECK-NEXT: store i32 %range, ptr %range.addr, align 4 +// CHECK-NEXT: store i32 %index, ptr %index.addr, align 4 +// CHECK-NEXT: store i32 %order_id, ptr %order_id.addr, align 4 +// CHECK-NEXT: %this1 = load ptr, ptr %this.addr, align 4 +// CHECK-NEXT: %0 = load i32, ptr %spaceNo.addr, align 4 +// CHECK-NEXT: %1 = load i32, ptr %range.addr, align 4 +// CHECK-NEXT: %2 = load i32, ptr %index.addr, align 4 +// CHECK-NEXT: %3 = load i32, ptr %order_id.addr, align 4 +// CHECK-NEXT: call void @_ZN4hlsl19RWByteAddressBufferC2Ejijj(ptr noundef nonnull align 4 dereferenceable(4) %this1, i32 noundef %0, i32 noundef %1, i32 noundef %2, i32 noundef %3) #4 +// CHECK-NEXT: ret void // Buf3 initialization part 1 - local variable declared in function foo() is initialized by // RasterizerOrderedByteAddressBuffer C1 default constructor @@ -104,6 +126,31 @@ export void foo() { // CHECK-DXIL-NEXT: store target("dx.RawBuffer", i8, 0, 0) %4, ptr %__handle, align 4 // CHECK-NEXT: ret void +// Buf2 initialization part 3 - body of RWByteAddressBuffer C2 constructor with implicit binding that initializes +// handle with @llvm.dx.resource.handlefromimplicitbinding +// CHECK: define linkonce_odr void @_ZN4hlsl19RWByteAddressBufferC2Ejijj(ptr noundef nonnull align 4 dereferenceable(4) %this, +// CHECK-SAME: i32 noundef %spaceNo, i32 noundef %range, i32 noundef %index, i32 noundef %order_id) unnamed_addr #1 align 2 { +// CHECK-NEXT: entry: +// CHECK-NEXT: %this.addr = alloca ptr, align 4 +// CHECK-NEXT: %spaceNo.addr = alloca i32, align 4 +// CHECK-NEXT: %range.addr = alloca i32, align 4 +// CHECK-NEXT: %index.addr = alloca i32, align 4 +// CHECK-NEXT: %order_id.addr = alloca i32, align 4 +// CHECK-NEXT: store ptr %this, ptr %this.addr, align 4 +// CHECK-NEXT: store i32 %spaceNo, ptr %spaceNo.addr, align 4 +// CHECK-NEXT: store i32 %range, ptr %range.addr, align 4 +// CHECK-NEXT: store i32 %index, ptr %index.addr, align 4 +// CHECK-NEXT: store i32 %order_id, ptr %order_id.addr, align 4 +// CHECK-NEXT: %this1 = load ptr, ptr %this.addr, align 4 +// CHECK-NEXT: %0 = load i32, ptr %spaceNo.addr, align 4 +// CHECK-NEXT: %1 = load i32, ptr %range.addr, align 4 +// CHECK-NEXT: %2 = load i32, ptr %index.addr, align 4 +// CHECK-NEXT: %3 = load i32, ptr %order_id.addr, align 4 +// CHECK-NEXT: %4 = call target("dx.RawBuffer", i8, 1, 0) @llvm.dx.resource.handlefromimplicitbinding.tdx.RawBuffer_i8_1_0t(i32 %3, i32 %0, i32 %1, i32 %2, i1 false) +// CHECK-NEXT: %__handle = getelementptr inbounds nuw %"class.hlsl::RWByteAddressBuffer", ptr %this1, i32 0, i32 0 +// CHECK-NEXT: store target("dx.RawBuffer", i8, 1, 0) %4, ptr %__handle, align 4 +// CHECK-NEXT: ret void + // Buf3 initialization part 3 - body of RasterizerOrderedByteAddressBuffer default C2 constructor that // initializes handle to poison // CHECK: define linkonce_odr void @_ZN4hlsl34RasterizerOrderedByteAddressBufferC2Ev(ptr noundef nonnull align 4 dereferenceable(4) %this) diff --git a/clang/test/CodeGenHLSL/builtins/RWBuffer-constructor.hlsl b/clang/test/CodeGenHLSL/builtins/RWBuffer-constructor.hlsl index adf231dedf4cb..7335a3938f476 100644 --- a/clang/test/CodeGenHLSL/builtins/RWBuffer-constructor.hlsl +++ b/clang/test/CodeGenHLSL/builtins/RWBuffer-constructor.hlsl @@ -55,11 +55,33 @@ export void foo() { // CHECK-SAME: i32 noundef %0, i32 noundef %1, i32 noundef %2, i32 noundef %3) // CHECK-NEXT: ret void -// Buf2 initialization part 1 - FIXME: constructor with implicit binding does not exist yet; -// the global init function currently calls the default RWBufer<double> C1 constructor -// CHECK: define internal void @__cxx_global_var_init.1() #0 { +// Buf2 initialization part 1 - global init function that calls RWBuffer<float> C1 constructor with implicit binding +// CHECK: define internal void @__cxx_global_var_init.1() // CHECK-NEXT: entry: -// CHECK-NEXT: call void @_ZN4hlsl8RWBufferIdEC1Ev(ptr noundef nonnull align 4 dereferenceable(4) @_ZL4Buf2) +// CHECK-NEXT: call void @_ZN4hlsl8RWBufferIdEC1Ejijj(ptr noundef nonnull align 4 dereferenceable(4) @_ZL4Buf2, +// CHECK-SAME: i32 noundef 0, i32 noundef 1, i32 noundef 0, i32 noundef 0) + +// Buf2 initialization part 2 - body of RWBuffer<float> C1 constructor with implicit binding that calls the C2 constructor +// CHECK: define linkonce_odr void @_ZN4hlsl8RWBufferIdEC1Ejijj(ptr noundef nonnull align 4 dereferenceable(4) %this, +// CHECK-SAME: i32 noundef %spaceNo, i32 noundef %range, i32 noundef %index, i32 noundef %order_id) +// CHECK-NEXT: entry: +// CHECK-NEXT: %this.addr = alloca ptr, align 4 +// CHECK-NEXT: %spaceNo.addr = alloca i32, align 4 +// CHECK-NEXT: %range.addr = alloca i32, align 4 +// CHECK-NEXT: %index.addr = alloca i32, align 4 +// CHECK-NEXT: %order_id.addr = alloca i32, align 4 +// CHECK-NEXT: store ptr %this, ptr %this.addr, align 4 +// CHECK-NEXT: store i32 %spaceNo, ptr %spaceNo.addr, align 4 +// CHECK-NEXT: store i32 %range, ptr %range.addr, align 4 +// CHECK-NEXT: store i32 %index, ptr %index.addr, align 4 +// CHECK-NEXT: store i32 %order_id, ptr %order_id.addr, align 4 +// CHECK-NEXT: %this1 = load ptr, ptr %this.addr, align 4 +// CHECK-NEXT: %0 = load i32, ptr %spaceNo.addr, align 4 +// CHECK-NEXT: %1 = load i32, ptr %range.addr, align 4 +// CHECK-NEXT: %2 = load i32, ptr %index.addr, align 4 +// CHECK-NEXT: %3 = load i32, ptr %order_id.addr, align 4 +// CHECK-NEXT: call void @_ZN4hlsl8RWBufferIdEC2Ejijj(ptr noundef nonnull align 4 dereferenceable(4) %this1, i32 noundef %0, i32 noundef %1, i32 noundef %2, i32 noundef %3) #4 +// CHECK-NEXT: ret void // Buf3 initialization part 1 - local variable declared in function foo() is initialized by RWBuffer<int> C1 default constructor // CHECK: define void @_Z3foov() @@ -102,6 +124,31 @@ export void foo() { // CHECK-DXIL-NEXT: store target("dx.TypedBuffer", float, 1, 0, 0) %4, ptr %__handle, align 4 // CHECK-NEXT: ret void +// Buf2 initialization part 3 - body of RWBuffer<float> C2 constructor with implicit binding that initializes +// handle with @llvm.dx.resource.handlefromimplicitbinding +// CHECK: define linkonce_odr void @_ZN4hlsl8RWBufferIdEC2Ejijj(ptr noundef nonnull align 4 dereferenceable(4) %this, +// CHECK-SAME: i32 noundef %spaceNo, i32 noundef %range, i32 noundef %index, i32 noundef %order_id) unnamed_addr #1 align 2 { +// CHECK-NEXT: entry: +// CHECK-NEXT: %this.addr = alloca ptr, align 4 +// CHECK-NEXT: %spaceNo.addr = alloca i32, align 4 +// CHECK-NEXT: %range.addr = alloca i32, align 4 +// CHECK-NEXT: %index.addr = alloca i32, align 4 +// CHECK-NEXT: %order_id.addr = alloca i32, align 4 +// CHECK-NEXT: store ptr %this, ptr %this.addr, align 4 +// CHECK-NEXT: store i32 %spaceNo, ptr %spaceNo.addr, align 4 +// CHECK-NEXT: store i32 %range, ptr %range.addr, align 4 +// CHECK-NEXT: store i32 %index, ptr %index.addr, align 4 +// CHECK-NEXT: store i32 %order_id, ptr %order_id.addr, align 4 +// CHECK-NEXT: %this1 = load ptr, ptr %this.addr, align 4 +// CHECK-NEXT: %0 = load i32, ptr %spaceNo.addr, align 4 +// CHECK-NEXT: %1 = load i32, ptr %range.addr, align 4 +// CHECK-NEXT: %2 = load i32, ptr %index.addr, align 4 +// CHECK-NEXT: %3 = load i32, ptr %order_id.addr, align 4 +// CHECK-NEXT: %4 = call target("dx.TypedBuffer", double, 1, 0, 0) @llvm.dx.resource.handlefromimplicitbinding.tdx.TypedBuffer_f64_1_0_0t(i32 %3, i32 %0, i32 %1, i32 %2, i1 false) +// CHECK-NEXT: %__handle = getelementptr inbounds nuw %"class.hlsl::RWBuffer.0", ptr %this1, i32 0, i32 0 +// CHECK-NEXT: store target("dx.TypedBuffer", double, 1, 0, 0) %4, ptr %__handle, align 4 +// CHECK-NEXT: ret void + // Buf3 initialization part 3 - body of RWBuffer<int> default C2 constructor that initializes handle to poison // CHECK: define linkonce_odr void @_ZN4hlsl8RWBufferIiEC2Ev(ptr noundef nonnull align 4 dereferenceable(4) %this) // CHECK-NEXT: entry: diff --git a/clang/test/CodeGenHLSL/builtins/StructuredBuffers-constructors.hlsl b/clang/test/CodeGenHLSL/builtins/StructuredBuffers-constructors.hlsl index ea818a737cf74..f77712a1148af 100644 --- a/clang/test/CodeGenHLSL/builtins/StructuredBuffers-constructors.hlsl +++ b/clang/test/CodeGenHLSL/builtins/StructuredBuffers-constructors.hlsl @@ -56,11 +56,34 @@ export void foo() { // CHECK-SAME: i32 noundef %0, i32 noundef %1, i32 noundef %2, i32 noundef %3) // CHECK-NEXT: ret void -// Buf2 initialization part 1 - FIXME: constructor with implicit binding does not exist yet; -// the global init function currently calls the default RWStructuredBufer<double> C1 constructor +// Buf2 initialization part 1 - global init function that calls RWStructuredBuffer<float> C1 constructor with +// implicit binding // 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-NEXT: call void @_ZN4hlsl18RWStructuredBufferIfEC1Ejijj(ptr noundef nonnull align 4 dereferenceable(4) @_ZL4Buf2, +// CHECK-SAME: i32 noundef 0, i32 noundef 1, i32 noundef 0, i32 noundef 0) + +// Buf2 initialization part 2 - body of RWStructuredBuffer<float> C1 constructor with implicit binding that calls the C2 constructor +// CHECK: define linkonce_odr void @_ZN4hlsl18RWStructuredBufferIfEC1Ejijj(ptr noundef nonnull align 4 dereferenceable(4) %this, +// CHECK-SAME: i32 noundef %spaceNo, i32 noundef %range, i32 noundef %index, i32 noundef %order_id) +// CHECK-NEXT: entry: +// CHECK-NEXT: %this.addr = alloca ptr, align 4 +// CHECK-NEXT: %spaceNo.addr = alloca i32, align 4 +// CHECK-NEXT: %range.addr = alloca i32, align 4 +// CHECK-NEXT: %index.addr = alloca i32, align 4 +// CHECK-NEXT: %order_id.addr = alloca i32, align 4 +// CHECK-NEXT: store ptr %this, ptr %this.addr, align 4 +// CHECK-NEXT: store i32 %spaceNo, ptr %spaceNo.addr, align 4 +// CHECK-NEXT: store i32 %range, ptr %range.addr, align 4 +// CHECK-NEXT: store i32 %index, ptr %index.addr, align 4 +// CHECK-NEXT: store i32 %order_id, ptr %order_id.addr, align 4 +// CHECK-NEXT: %this1 = load ptr, ptr %this.addr, align 4 +// CHECK-NEXT: %0 = load i32, ptr %spaceNo.addr, align 4 +// CHECK-NEXT: %1 = load i32, ptr %range.addr, align 4 +// CHECK-NEXT: %2 = load i32, ptr %index.addr, align 4 +// CHECK-NEXT: %3 = load i32, ptr %order_id.addr, align 4 +// CHECK-NEXT: call void @_ZN4hlsl18RWStructuredBufferIfEC2Ejijj(ptr noundef nonnull align 4 dereferenceable(4) %this1, i32 noundef %0, i32 noundef %1, i32 noundef %2, i32 noundef %3) #4 +// CHECK-NEXT: ret void // Buf3 initialization part 1 - local variable declared in function foo() is initialized by // AppendStructuredBuffer<float> C1 default constructor @@ -105,6 +128,31 @@ export void foo() { // CHECK-DXIL-NEXT: store target("dx.RawBuffer", float, 0, 0) %4, ptr %__handle, align 4 // CHECK-NEXT: ret void +// Buf2 initialization part 3 - body of RWStructuredBuffer<float> C2 constructor with implicit binding that initializes +// handle with @llvm.dx.resource.handlefromimplicitbinding +// CHECK: define linkonce_odr void @_ZN4hlsl18RWStructuredBufferIfEC2Ejijj(ptr noundef nonnull align 4 dereferenceable(4) %this, +// CHECK-SAME: i32 noundef %spaceNo, i32 noundef %range, i32 noundef %index, i32 noundef %order_id) unnamed_addr #1 align 2 { +// CHECK-NEXT: entry: +// CHECK-NEXT: %this.addr = alloca ptr, align 4 +// CHECK-NEXT: %spaceNo.addr = alloca i32, align 4 +// CHECK-NEXT: %range.addr = alloca i32, align 4 +// CHECK-NEXT: %index.addr = alloca i32, align 4 +// CHECK-NEXT: %order_id.addr = alloca i32, align 4 +// CHECK-NEXT: store ptr %this, ptr %this.addr, align 4 +// CHECK-NEXT: store i32 %spaceNo, ptr %spaceNo.addr, align 4 +// CHECK-NEXT: store i32 %range, ptr %range.addr, align 4 +// CHECK-NEXT: store i32 %index, ptr %index.addr, align 4 +// CHECK-NEXT: store i32 %order_id, ptr %order_id.addr, align 4 +// CHECK-NEXT: %this1 = load ptr, ptr %this.addr, align 4 +// CHECK-NEXT: %0 = load i32, ptr %spaceNo.addr, align 4 +// CHECK-NEXT: %1 = load i32, ptr %range.addr, align 4 +// CHECK-NEXT: %2 = load i32, ptr %index.addr, align 4 +// CHECK-NEXT: %3 = load i32, ptr %order_id.addr, align 4 +// CHECK-NEXT: %4 = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefromimplicitbinding.tdx.RawBuffer_f32_1_0t(i32 %3, i32 %0, i32 %1, i32 %2, i1 false) +// CHECK-NEXT: %__handle = getelementptr inbounds nuw %"class.hlsl::RWStructuredBuffer", ptr %this1, i32 0, i32 0 +// CHECK-NEXT: store target("dx.RawBuffer", float, 1, 0) %4, ptr %__handle, align 4 +// CHECK-NEXT: ret void + // Buf3 initialization part 3 - body of AppendStructuredBuffer<float> default C2 constructor that // initializes handle to poison // CHECK: define linkonce_odr void @_ZN4hlsl22AppendStructuredBufferIfEC2Ev(ptr noundef nonnull align 4 dereferenceable(4) %this) diff --git a/clang/test/CodeGenHLSL/builtins/StructuredBuffers-methods-ps.hlsl b/clang/test/CodeGenHLSL/builtins/StructuredBuffers-methods-ps.hlsl index f2aea4e376b03..47afba27b6ebd 100644 --- a/clang/test/CodeGenHLSL/builtins/StructuredBuffers-methods-ps.hlsl +++ b/clang/test/CodeGenHLSL/builtins/StructuredBuffers-methods-ps.hlsl @@ -32,6 +32,6 @@ export float TestLoad() { // CHECK: %[[PTR1:.*]] = call ptr @llvm.dx.resource.getpointer.p0.tdx.RawBuffer_f32_1_1t(target("dx.RawBuffer", float, 1, 1) %{{[0-9]+}}, i32 %{{[0-9]+}}) // CHECK: %[[VALUE1:.*]] = load float, ptr %[[PTR1]] -// CHECK: declare i32 @llvm.dx.resource.updatecounter.tdx.RawBuffer_f32_1_0t(target("dx.RawBuffer", float, 1, 0), i8) #3 -// CHECK: declare i32 @llvm.dx.resource.updatecounter.tdx.RawBuffer_f32_1_1t(target("dx.RawBuffer", float, 1, 1), i8) #3 -// CHECK: declare ptr @llvm.dx.resource.getpointer.p0.tdx.RawBuffer_f32_1_1t(target("dx.RawBuffer", float, 1, 1), i32) #4 +// CHECK: declare i32 @llvm.dx.resource.updatecounter.tdx.RawBuffer_f32_1_0t(target("dx.RawBuffer", float, 1, 0), i8) +// CHECK: declare i32 @llvm.dx.resource.updatecounter.tdx.RawBuffer_f32_1_1t(target("dx.RawBuffer", float, 1, 1), i8) +// CHECK: declare ptr @llvm.dx.resource.getpointer.p0.tdx.RawBuffer_f32_1_1t(target("dx.RawBuffer", float, 1, 1), i32) diff --git a/clang/test/CodeGenHLSL/static-local-ctor.hlsl b/clang/test/CodeGenHLSL/static-local-ctor.hlsl index 7aeb5e987d6b2..474bcf1aff6ac 100644 --- a/clang/test/CodeGenHLSL/static-local-ctor.hlsl +++ b/clang/test/CodeGenHLSL/static-local-ctor.hlsl @@ -21,7 +21,7 @@ void InitBuf(RWBuffer<int> buf) { // CHECK-NEXT: br i1 [[Tmp3]] // CHECK-NOT: _Init_thread_header // CHECK: init.check: -// CHECK-NEXT: call void @_ZN4hlsl8RWBufferIiEC1Ev +// CHECK-NEXT: call void @_ZN4hlsl8RWBufferIiEC1Ejijj // CHECK-NEXT: store i8 1, ptr @_ZGVZ4mainvE5mybuf // CHECK-NOT: _Init_thread_footer diff --git a/llvm/include/llvm/IR/IntrinsicsSPIRV.td b/llvm/include/llvm/IR/IntrinsicsSPIRV.td index 404467781b4d0..8d984d6ce58df 100644 --- a/llvm/include/llvm/IR/IntrinsicsSPIRV.td +++ b/llvm/include/llvm/IR/IntrinsicsSPIRV.td @@ -119,6 +119,11 @@ let TargetPrefix = "spv" in { [llvm_any_ty], [llvm_i32_ty, llvm_i32_ty, llvm_i32_ty, llvm_i32_ty, llvm_i1_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]>; 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]>; _______________________________________________ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits