Author: Joshua Batista Date: 2025-11-21T10:11:38-08:00 New Revision: fea070b610e0dc08447be60db7f13c150b2892d5
URL: https://github.com/llvm/llvm-project/commit/fea070b610e0dc08447be60db7f13c150b2892d5 DIFF: https://github.com/llvm/llvm-project/commit/fea070b610e0dc08447be60db7f13c150b2892d5.diff LOG: [HLSL] Add Load overload with status (#166449) This PR adds a Load method for resources, which takes an additional parameter by reference, status. It fills the status parameter with a 1 or 0, depending on whether or not the resource access was mapped. CheckAccessFullyMapped is also added as an intrinsic, and called in the production of this status bit. Only addresses DXIL for the below issue: https://github.com/llvm/llvm-project/issues/138910 Also only addresses the DXIL variant for the below issue: https://github.com/llvm/llvm-project/issues/99204 Added: Modified: clang/include/clang/Basic/Builtins.td clang/lib/CodeGen/CGHLSLBuiltins.cpp clang/lib/Headers/hlsl/hlsl_intrinsics.h clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h clang/lib/Sema/SemaHLSL.cpp clang/test/AST/HLSL/StructuredBuffers-AST.hlsl clang/test/AST/HLSL/TypedBuffers-AST.hlsl clang/test/CodeGenHLSL/resources/StructuredBuffers-methods-lib.hlsl clang/test/CodeGenHLSL/resources/StructuredBuffers-methods-ps.hlsl clang/test/CodeGenHLSL/resources/TypedBuffers-methods.hlsl llvm/lib/Target/DirectX/DXILShaderFlags.cpp Removed: ################################################################################ diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td index 502382a069856..7530aebdcb581 100644 --- a/clang/include/clang/Basic/Builtins.td +++ b/clang/include/clang/Basic/Builtins.td @@ -4946,6 +4946,12 @@ def HLSLResourceGetPointer : LangBuiltin<"HLSL_LANG"> { let Prototype = "void(...)"; } +def HLSLResourceLoadWithStatus : LangBuiltin<"HLSL_LANG"> { + let Spellings = ["__builtin_hlsl_resource_load_with_status"]; + let Attributes = [NoThrow]; + let Prototype = "void(...)"; +} + def HLSLResourceUninitializedHandle : LangBuiltin<"HLSL_LANG"> { let Spellings = ["__builtin_hlsl_resource_uninitializedhandle"]; let Attributes = [NoThrow]; diff --git a/clang/lib/CodeGen/CGHLSLBuiltins.cpp b/clang/lib/CodeGen/CGHLSLBuiltins.cpp index 12d9a98915ce3..5ae3fed099cd4 100644 --- a/clang/lib/CodeGen/CGHLSLBuiltins.cpp +++ b/clang/lib/CodeGen/CGHLSLBuiltins.cpp @@ -404,6 +404,49 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID, RetTy, CGM.getHLSLRuntime().getCreateResourceGetPointerIntrinsic(), ArrayRef<Value *>{HandleOp, IndexOp}); } + case Builtin::BI__builtin_hlsl_resource_load_with_status: { + Value *HandleOp = EmitScalarExpr(E->getArg(0)); + Value *IndexOp = EmitScalarExpr(E->getArg(1)); + + // Get the *address* of the status argument to write to it by reference + LValue StatusLVal = EmitLValue(E->getArg(2)); + Address StatusAddr = StatusLVal.getAddress(); + + QualType HandleTy = E->getArg(0)->getType(); + const HLSLAttributedResourceType *RT = + HandleTy->getAs<HLSLAttributedResourceType>(); + assert(CGM.getTarget().getTriple().getArch() == llvm::Triple::dxil && + "Only DXIL currently implements load with status"); + + Intrinsic::ID IntrID = RT->getAttrs().RawBuffer + ? llvm::Intrinsic::dx_resource_load_rawbuffer + : llvm::Intrinsic::dx_resource_load_typedbuffer; + + llvm::Type *DataTy = ConvertType(E->getType()); + llvm::Type *RetTy = llvm::StructType::get(Builder.getContext(), + {DataTy, Builder.getInt1Ty()}); + + SmallVector<Value *, 3> Args; + Args.push_back(HandleOp); + Args.push_back(IndexOp); + + if (RT->getAttrs().RawBuffer) { + Value *Offset = Builder.getInt32(0); + Args.push_back(Offset); + } + + // The load intrinsics give us a (T value, i1 status) pair - + // shepherd these into the return value and out reference respectively. + Value *ResRet = + Builder.CreateIntrinsic(RetTy, IntrID, Args, {}, "ld.struct"); + Value *LoadedValue = Builder.CreateExtractValue(ResRet, {0}, "ld.value"); + Value *StatusBit = Builder.CreateExtractValue(ResRet, {1}, "ld.status"); + Value *ExtendedStatus = + Builder.CreateZExt(StatusBit, Builder.getInt32Ty(), "ld.status.ext"); + Builder.CreateStore(ExtendedStatus, StatusAddr); + + return LoadedValue; + } case Builtin::BI__builtin_hlsl_resource_uninitializedhandle: { llvm::Type *HandleTy = CGM.getTypes().ConvertType(E->getType()); return llvm::PoisonValue::get(HandleTy); diff --git a/clang/lib/Headers/hlsl/hlsl_intrinsics.h b/clang/lib/Headers/hlsl/hlsl_intrinsics.h index c26c8bb5261d4..a538be5ebd099 100644 --- a/clang/lib/Headers/hlsl/hlsl_intrinsics.h +++ b/clang/lib/Headers/hlsl/hlsl_intrinsics.h @@ -666,6 +666,10 @@ smoothstep(__detail::HLSL_FIXED_VECTOR<float, N> Min, return __detail::smoothstep_vec_impl(Min, Max, X); } +inline bool CheckAccessFullyMapped(uint Status) { + return static_cast<bool>(Status); +} + //===----------------------------------------------------------------------===// // fwidth builtin //===----------------------------------------------------------------------===// diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp index 066acf6f01a90..868f894a03c49 100644 --- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp +++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp @@ -202,7 +202,7 @@ struct BuiltinTypeMethodBuilder { BuiltinTypeMethodBuilder &declareLocalVar(LocalVar &Var); template <typename... Ts> BuiltinTypeMethodBuilder &callBuiltin(StringRef BuiltinName, - QualType ReturnType, Ts... ArgSpecs); + QualType ReturnType, Ts &&...ArgSpecs); template <typename TLHS, typename TRHS> BuiltinTypeMethodBuilder &assign(TLHS LHS, TRHS RHS); template <typename T> BuiltinTypeMethodBuilder &dereference(T Ptr); @@ -572,7 +572,7 @@ BuiltinTypeMethodBuilder &BuiltinTypeMethodBuilder::returnThis() { template <typename... Ts> BuiltinTypeMethodBuilder & BuiltinTypeMethodBuilder::callBuiltin(StringRef BuiltinName, - QualType ReturnType, Ts... ArgSpecs) { + QualType ReturnType, Ts &&...ArgSpecs) { ensureCompleteDecl(); std::array<Expr *, sizeof...(ArgSpecs)> Args{ @@ -1140,6 +1140,7 @@ BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addLoadMethods() { DeclarationName Load(&II); // TODO: We also need versions with status for CheckAccessFullyMapped. addHandleAccessFunction(Load, /*IsConst=*/false, /*IsRef=*/false); + addLoadWithStatusFunction(Load, /*IsConst=*/false); return *this; } @@ -1232,6 +1233,22 @@ BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addDecrementCounterMethod() { .finalize(); } +BuiltinTypeDeclBuilder & +BuiltinTypeDeclBuilder::addLoadWithStatusFunction(DeclarationName &Name, + bool IsConst) { + assert(!Record->isCompleteDefinition() && "record is already complete"); + ASTContext &AST = SemaRef.getASTContext(); + using PH = BuiltinTypeMethodBuilder::PlaceHolder; + + QualType ReturnTy = getHandleElementType(); + return BuiltinTypeMethodBuilder(*this, Name, ReturnTy, IsConst) + .addParam("Index", AST.UnsignedIntTy) + .addParam("Status", AST.UnsignedIntTy, HLSLParamModifierAttr::Keyword_out) + .callBuiltin("__builtin_hlsl_resource_load_with_status", ReturnTy, + PH::Handle, PH::_0, PH::_1) + .finalize(); +} + BuiltinTypeDeclBuilder & BuiltinTypeDeclBuilder::addHandleAccessFunction(DeclarationName &Name, bool IsConst, bool IsRef) { diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h index 95e3a6c4fb2f1..47c8b0e225612 100644 --- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h +++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h @@ -91,6 +91,8 @@ class BuiltinTypeDeclBuilder { BuiltinTypeDeclBuilder &addDecrementCounterMethod(); BuiltinTypeDeclBuilder &addHandleAccessFunction(DeclarationName &Name, bool IsConst, bool IsRef); + BuiltinTypeDeclBuilder &addLoadWithStatusFunction(DeclarationName &Name, + bool IsConst); BuiltinTypeDeclBuilder &addAppendMethod(); BuiltinTypeDeclBuilder &addConsumeMethod(); diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index c5666941fd36a..0a164a7b5bbbd 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -3057,6 +3057,24 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) { break; } + case Builtin::BI__builtin_hlsl_resource_load_with_status: { + if (SemaRef.checkArgCount(TheCall, 3) || + CheckResourceHandle(&SemaRef, TheCall, 0) || + CheckArgTypeMatches(&SemaRef, TheCall->getArg(1), + SemaRef.getASTContext().UnsignedIntTy) || + CheckArgTypeMatches(&SemaRef, TheCall->getArg(2), + SemaRef.getASTContext().UnsignedIntTy) || + CheckModifiableLValue(&SemaRef, TheCall, 2)) + return true; + + auto *ResourceTy = + TheCall->getArg(0)->getType()->castAs<HLSLAttributedResourceType>(); + QualType ReturnType = ResourceTy->getContainedType(); + TheCall->setType(ReturnType); + + break; + } + case Builtin::BI__builtin_hlsl_resource_uninitializedhandle: { assert(TheCall->getNumArgs() == 1 && "expected 1 arg"); // Update return type to be the attributed resource type from arg0. diff --git a/clang/test/AST/HLSL/StructuredBuffers-AST.hlsl b/clang/test/AST/HLSL/StructuredBuffers-AST.hlsl index 538eb5256f8d5..eb65a28dba6ff 100644 --- a/clang/test/AST/HLSL/StructuredBuffers-AST.hlsl +++ b/clang/test/AST/HLSL/StructuredBuffers-AST.hlsl @@ -326,6 +326,26 @@ RESOURCE<float> Buffer; // CHECK-LOAD-NEXT: DeclRefExpr {{.*}} 'unsigned int' ParmVar {{.*}} 'Index' 'unsigned int' // CHECK-LOAD-NEXT: AlwaysInlineAttr {{.*}} Implicit always_inline +// Load with status method + +// CHECK-LOAD: CXXMethodDecl {{.*}} Load 'element_type (unsigned int, out unsigned int)' +// CHECK-LOAD-NEXT: ParmVarDecl {{.*}} Index 'unsigned int' +// CHECK-LOAD-NEXT: ParmVarDecl {{.*}} Status 'unsigned int &__restrict' +// CHECK-LOAD-NEXT: HLSLParamModifierAttr {{.*}} out +// CHECK-LOAD-NEXT: CompoundStmt +// CHECK-LOAD-NEXT: ReturnStmt +// CHECK-LOAD-NEXT: CallExpr {{.*}} 'element_type' +// CHECK-LOAD-NEXT: ImplicitCastExpr {{.*}} <BuiltinFnToFnPtr> +// CHECK-LOAD-NEXT: DeclRefExpr {{.*}} '<builtin fn type>' Function {{.*}} '__builtin_hlsl_resource_load_with_status' 'void (...) noexcept' +// CHECK-LOAD-NEXT: MemberExpr {{.*}} '__hlsl_resource_t +// CHECK-LOAD-UAV-SAME{LITERAL}: [[hlsl::resource_class(UAV)]] +// CHECK-LOAD-SRV-SAME{LITERAL}: [[hlsl::resource_class(SRV)]] +// CHECK-LOAD-SAME{LITERAL}: [[hlsl::contained_type(element_type)]] +// CHECK-LOAD-NEXT: CXXThisExpr {{.*}} 'hlsl::[[RESOURCE]]<element_type>' lvalue implicit this +// CHECK-LOAD-NEXT: DeclRefExpr {{.*}} 'unsigned int' ParmVar {{.*}} 'Index' 'unsigned int' +// CHECK-LOAD-NEXT: DeclRefExpr {{.*}} 'unsigned int' ParmVar {{.*}} 'Status' 'unsigned int &__restrict' +// CHECK-LOAD-NEXT: AlwaysInlineAttr {{.*}} Implicit always_inline + // IncrementCounter method // CHECK-COUNTER: CXXMethodDecl {{.*}} IncrementCounter 'unsigned int ()' diff --git a/clang/test/AST/HLSL/TypedBuffers-AST.hlsl b/clang/test/AST/HLSL/TypedBuffers-AST.hlsl index 4b66faa79662a..762386c622dad 100644 --- a/clang/test/AST/HLSL/TypedBuffers-AST.hlsl +++ b/clang/test/AST/HLSL/TypedBuffers-AST.hlsl @@ -214,6 +214,25 @@ RESOURCE<float> Buffer; // CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' ParmVar {{.*}} 'Index' 'unsigned int' // CHECK-NEXT: AlwaysInlineAttr {{.*}} Implicit always_inline +// Load with status method +// CHECK: CXXMethodDecl {{.*}} Load 'element_type (unsigned int, out unsigned int)' +// CHECK-NEXT: ParmVarDecl {{.*}} Index 'unsigned int' +// CHECK-NEXT: ParmVarDecl {{.*}} Status 'unsigned int &__restrict' +// CHECK-NEXT: HLSLParamModifierAttr {{.*}} out +// CHECK-NEXT: CompoundStmt +// CHECK-NEXT: ReturnStmt +// CHECK-NEXT: CallExpr {{.*}} 'element_type' +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(...) noexcept' <BuiltinFnToFnPtr> +// CHECK-NEXT: DeclRefExpr {{.*}} '<builtin fn type>' Function {{.*}} '__builtin_hlsl_resource_load_with_status' 'void (...) noexcept' +// CHECK-NEXT: MemberExpr {{.*}} '__hlsl_resource_t +// CHECK-UAV-SAME{LITERAL}: [[hlsl::resource_class(UAV)]] +// CHECK-SRV-SAME{LITERAL}: [[hlsl::resource_class(SRV)]] +// CHECK-SAME{LITERAL}: [[hlsl::contained_type(element_type)]] +// CHECK-NEXT: CXXThisExpr {{.*}} 'hlsl::[[RESOURCE]]<element_type>' lvalue implicit this +// CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' ParmVar {{.*}} 'Index' 'unsigned int' +// CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' ParmVar {{.*}} 'Status' 'unsigned int &__restrict' +// CHECK-NEXT: AlwaysInlineAttr {{.*}} Implicit always_inline + // GetDimensions method // CHECK-NEXT: CXXMethodDecl {{.*}} GetDimensions 'void (out unsigned int)' diff --git a/clang/test/CodeGenHLSL/resources/StructuredBuffers-methods-lib.hlsl b/clang/test/CodeGenHLSL/resources/StructuredBuffers-methods-lib.hlsl index 1f248d0560006..54c386cab537b 100644 --- a/clang/test/CodeGenHLSL/resources/StructuredBuffers-methods-lib.hlsl +++ b/clang/test/CodeGenHLSL/resources/StructuredBuffers-methods-lib.hlsl @@ -104,6 +104,44 @@ export float TestLoad() { // CHECK-NEXT: %[[VAL:.*]] = load float, ptr %[[PTR]] // CHECK-NEXT: ret float %[[VAL]] +export float TestLoadWithStatus() { + uint s1; + uint s2; + float ret = RWSB1.Load(1, s1) + SB1.Load(2, s2); + ret += float(s1 + s2); + return ret; +} + +// CHECK: define noundef nofpclass(nan inf) float @TestLoadWithStatus()() +// CHECK: call {{.*}} float @hlsl::RWStructuredBuffer<float>::Load(unsigned int, unsigned int&)(ptr {{.*}} @RWSB1, i32 noundef 1, ptr {{.*}} %tmp) +// CHECK: call {{.*}} float @hlsl::StructuredBuffer<float>::Load(unsigned int, unsigned int&)(ptr {{.*}} @SB1, i32 noundef 2, ptr {{.*}} %tmp1) +// CHECK: add +// CHECK: ret float + +// CHECK: define {{.*}} float @hlsl::RWStructuredBuffer<float>::Load(unsigned int, unsigned int&)(ptr {{.*}} %this, i32 noundef %Index, ptr {{.*}} %Status) +// CHECK: %__handle = getelementptr inbounds nuw %"class.hlsl::RWStructuredBuffer", ptr %{{.*}}, i32 0, i32 0 +// DXIL-NEXT: %[[HANDLE:.*]] = load target("dx.RawBuffer", float, 1, 0), ptr %__handle +// CHECK-NEXT: %[[INDEX:.*]] = load i32, ptr %Index.addr +// CHECK-NEXT: %[[LOADED_STATUS_ADDR:.*]] = load ptr, ptr %Status.addr +// DXIL-NEXT: %[[STRUCT:.*]] = call { float, i1 } @llvm.dx.resource.load.rawbuffer.f32.tdx.RawBuffer_f32_1_0t(target("dx.RawBuffer", float, 1, 0) %[[HANDLE]], i32 %[[INDEX]], i32 0) +// CHECK-NEXT: %[[VALUE:.*]] = extractvalue { float, i1 } %[[STRUCT]], 0 +// CHECK-NEXT: %[[STATUS_TEMP:.*]] = extractvalue { float, i1 } %[[STRUCT]], 1 +// CHECK-NEXT: %[[STATUS_EXT:.*]] = zext i1 %[[STATUS_TEMP]] to i32 +// CHECK-NEXT: store i32 %[[STATUS_EXT]], ptr %[[LOADED_STATUS_ADDR]], align 4 +// CHECK-NEXT: ret float %[[VALUE]] + +// CHECK: define {{.*}} float @hlsl::StructuredBuffer<float>::Load(unsigned int, unsigned int&)(ptr {{.*}} %this, i32 noundef %Index, ptr {{.*}} %Status) +// CHECK: %__handle = getelementptr inbounds nuw %"class.hlsl::StructuredBuffer", ptr %{{.*}}, i32 0, i32 0 +// DXIL-NEXT: %[[HANDLE:.*]] = load target("dx.RawBuffer", float, 0, 0), ptr %__handle +// CHECK-NEXT: %[[INDEX:.*]] = load i32, ptr %Index.addr +// CHECK-NEXT: %[[LOADED_STATUS_ADDR:.*]] = load ptr, ptr %Status.addr, align 4 +// DXIL-NEXT: %[[STRUCT:.*]] = call { float, i1 } @llvm.dx.resource.load.rawbuffer.f32.tdx.RawBuffer_f32_0_0t(target("dx.RawBuffer", float, 0, 0) %[[HANDLE]], i32 %[[INDEX]], i32 0) +// CHECK-NEXT: %[[VALUE:.*]] = extractvalue { float, i1 } %[[STRUCT]], 0 +// CHECK-NEXT: %[[STATUS_TEMP:.*]] = extractvalue { float, i1 } %[[STRUCT]], 1 +// CHECK-NEXT: %[[STATUS_EXT:.*]] = zext i1 %[[STATUS_TEMP]] to i32 +// CHECK-NEXT: store i32 %[[STATUS_EXT]], ptr %[[LOADED_STATUS_ADDR]], align 4 +// CHECK-NEXT: ret float %[[VALUE]] + export uint TestGetDimensions() { uint dim1, dim2, dim3, stride1, stride2, stride3; SB1.GetDimensions(dim1, stride1); diff --git a/clang/test/CodeGenHLSL/resources/StructuredBuffers-methods-ps.hlsl b/clang/test/CodeGenHLSL/resources/StructuredBuffers-methods-ps.hlsl index 25fa75965d686..157bae2e08a78 100644 --- a/clang/test/CodeGenHLSL/resources/StructuredBuffers-methods-ps.hlsl +++ b/clang/test/CodeGenHLSL/resources/StructuredBuffers-methods-ps.hlsl @@ -65,6 +65,43 @@ export float TestLoad() { // CHECK-NEXT: %[[VAL:.*]] = load <2 x i32>, ptr %[[BUFPTR]] // CHECK-NEXT: ret <2 x i32> %[[VAL]] +export float TestLoadWithStatus() { + uint status; + uint status2; + float val = ROSB1.Load(10, status).x + ROSB2.Load(20, status2).x; + return val + float(status + status2); +} + +// CHECK: define {{.*}} float @TestLoadWithStatus()() +// CHECK: call {{.*}} float @hlsl::RasterizerOrderedStructuredBuffer<float>::Load(unsigned int, unsigned int&)(ptr {{.*}} @ROSB1, i32 noundef 10, ptr {{.*}} %tmp) +// CHECK: call {{.*}} <2 x i32> @hlsl::RasterizerOrderedStructuredBuffer<int vector[2]>::Load(unsigned int, unsigned int&)(ptr {{.*}} @ROSB2, i32 noundef 20, ptr {{.*}} %tmp2) +// CHECK: ret + +// CHECK: define {{.*}} float @hlsl::RasterizerOrderedStructuredBuffer<float>::Load(unsigned int, unsigned int&)(ptr {{.*}} %this, i32 noundef %Index, ptr {{.*}} %Status) +// CHECK: %__handle = getelementptr inbounds nuw %"class.hlsl::RasterizerOrderedStructuredBuffer", ptr {{.*}}, i32 0, i32 0 +// CHECK-NEXT: %[[HANDLE:.*]] = load target("dx.RawBuffer", float, 1, 1), ptr %__handle +// CHECK-NEXT: %[[INDEX:.*]] = load i32, ptr %Index.addr +// CHECK-NEXT: %[[LOADED_STATUS_ADDR:.*]] = load ptr, ptr %Status.addr, align 4 +// DXIL-NEXT: %[[STRUCT:.*]] = call { float, i1 } @llvm.dx.resource.load.rawbuffer.f32.tdx.RawBuffer_f32_1_1t(target("dx.RawBuffer", float, 1, 1) %[[HANDLE]], i32 %[[INDEX]], i32 0) +// CHECK-NEXT: %[[VALUE:.*]] = extractvalue { float, i1 } %[[STRUCT]], 0 +// CHECK-NEXT: %[[STATUS_TEMP:.*]] = extractvalue { float, i1 } %[[STRUCT]], 1 +// CHECK-NEXT: %[[STATUS_EXT:.*]] = zext i1 %[[STATUS_TEMP]] to i32 +// CHECK-NEXT: store i32 %[[STATUS_EXT]], ptr %[[LOADED_STATUS_ADDR]], align 4 +// CHECK-NEXT: ret float %[[VALUE]] + +// CHECK: define {{.*}} <2 x i32> @hlsl::RasterizerOrderedStructuredBuffer<int vector[2]>::Load(unsigned int, unsigned int&)(ptr {{.*}} %this, i32 noundef %Index, ptr {{.*}} %Status) +// CHECK: %__handle = getelementptr inbounds nuw %"class.hlsl::RasterizerOrderedStructuredBuffer.0", ptr {{.*}}, i32 0, i32 0 +// CHECK-NEXT: %[[HANDLE:.*]] = load target("dx.RawBuffer", <2 x i32>, 1, 1), ptr %__handle +// CHECK-NEXT: %[[INDEX:.*]] = load i32, ptr %Index.addr +// CHECK-NEXT: %[[LOADED_STATUS_ADDR:.*]] = load ptr, ptr %Status.addr, align 4 +// DXIL-NEXT: %[[STRUCT:.*]] = call { <2 x i32>, i1 } @llvm.dx.resource.load.rawbuffer.v2i32.tdx.RawBuffer_v2i32_1_1t(target("dx.RawBuffer", <2 x i32>, 1, 1) %[[HANDLE]], i32 %[[INDEX]], i32 0) +// CHECK-NEXT: %[[VALUE:.*]] = extractvalue { <2 x i32>, i1 } %[[STRUCT]], 0 +// CHECK-NEXT: %[[STATUS_TEMP:.*]] = extractvalue { <2 x i32>, i1 } %[[STRUCT]], 1 +// CHECK-NEXT: %[[STATUS_EXT:.*]] = zext i1 %[[STATUS_TEMP]] to i32 +// CHECK-NEXT: store i32 %[[STATUS_EXT]], ptr %[[LOADED_STATUS_ADDR]], align 4 +// CHECK-NEXT: ret <2 x i32> %[[VALUE]] + + export uint TestGetDimensions() { uint dim1, dim2, stride1, stride2; ROSB1.GetDimensions(dim1, stride1); diff --git a/clang/test/CodeGenHLSL/resources/TypedBuffers-methods.hlsl b/clang/test/CodeGenHLSL/resources/TypedBuffers-methods.hlsl index fdc1ef08b7c2c..499f5b1ca54ef 100644 --- a/clang/test/CodeGenHLSL/resources/TypedBuffers-methods.hlsl +++ b/clang/test/CodeGenHLSL/resources/TypedBuffers-methods.hlsl @@ -38,6 +38,44 @@ export float TestLoad() { // CHECK-NEXT: %[[VEC:.*]] = load <4 x i32>, ptr %[[PTR]] // CHECK-NEXT: ret <4 x i32> %[[VEC]] +export float TestLoadWithStatus() { + uint s1; + uint s2; + float ret = Buf.Load(1, s1) + float(RWBuf.Load(2, s2).y); + ret += float(s1 + s2); + return ret; +} + +// CHECK: define noundef nofpclass(nan inf) float @TestLoadWithStatus()() +// CHECK: call {{.*}} float @hlsl::Buffer<float>::Load(unsigned int, unsigned int&)(ptr {{.*}} @Buf, i32 noundef 1, ptr {{.*}} %tmp) +// CHECK: call {{.*}} <4 x i32> @hlsl::RWBuffer<unsigned int vector[4]>::Load(unsigned int, unsigned int&)(ptr {{.*}} @RWBuf, i32 noundef 2, ptr {{.*}} %tmp1) +// CHECK: add +// CHECK: ret float + +// CHECK: define {{.*}} float @hlsl::Buffer<float>::Load(unsigned int, unsigned int&)(ptr {{.*}} %this, i32 noundef %Index, ptr {{.*}} %Status) +// CHECK: %__handle = getelementptr inbounds nuw %"class.hlsl::Buffer", ptr %{{.*}}, i32 0, i32 0 +// DXIL-NEXT: %[[HANDLE:.*]] = load target("dx.TypedBuffer", float, 0, 0, 0), ptr %__handle +// CHECK-NEXT: %[[INDEX:.*]] = load i32, ptr %Index.addr +// CHECK-NEXT: %[[LOADED_STATUS_ADDR:.*]] = load ptr, ptr %Status.addr +// DXIL-NEXT: %[[STRUCT:.*]] = call { float, i1 } @llvm.dx.resource.load.typedbuffer.f32.tdx.TypedBuffer_f32_0_0_0t(target("dx.TypedBuffer", float, 0, 0, 0) %[[HANDLE]], i32 %[[INDEX]]) +// CHECK-NEXT: %[[VALUE:.*]] = extractvalue { float, i1 } %[[STRUCT]], 0 +// CHECK-NEXT: %[[STATUS_TEMP:.*]] = extractvalue { float, i1 } %[[STRUCT]], 1 +// CHECK-NEXT: %[[STATUS_EXT:.*]] = zext i1 %[[STATUS_TEMP]] to i32 +// CHECK-NEXT: store i32 %[[STATUS_EXT]], ptr %[[LOADED_STATUS_ADDR]], align 4 +// CHECK-NEXT: ret float %[[VALUE]] + +// CHECK: define {{.*}} <4 x i32> @hlsl::RWBuffer<unsigned int vector[4]>::Load(unsigned int, unsigned int&)(ptr {{.*}} %this, i32 noundef %Index, ptr {{.*}} %Status) +// CHECK: %__handle = getelementptr inbounds nuw %"class.hlsl::RWBuffer", ptr %{{.*}}, i32 0, i32 0 +// DXIL-NEXT: %[[HANDLE:.*]] = load target("dx.TypedBuffer", <4 x i32>, 1, 0, 0), ptr %__handle +// CHECK-NEXT: %[[INDEX:.*]] = load i32, ptr %Index.addr +// CHECK-NEXT: %[[LOADED_STATUS_ADDR:.*]] = load ptr, ptr %Status.addr +// DXIL-NEXT: %[[STRUCT:.*]] = call { <4 x i32>, i1 } @llvm.dx.resource.load.typedbuffer.v4i32.tdx.TypedBuffer_v4i32_1_0_0t(target("dx.TypedBuffer", <4 x i32>, 1, 0, 0) %[[HANDLE]], i32 %[[INDEX]]) +// CHECK-NEXT: %[[VALUE:.*]] = extractvalue { <4 x i32>, i1 } %[[STRUCT]], 0 +// CHECK-NEXT: %[[STATUS_TEMP:.*]] = extractvalue { <4 x i32>, i1 } %[[STRUCT]], 1 +// CHECK-NEXT: %[[STATUS_EXT:.*]] = zext i1 %[[STATUS_TEMP]] to i32 +// CHECK-NEXT: store i32 %[[STATUS_EXT]], ptr %[[LOADED_STATUS_ADDR]], align 4 +// CHECK-NEXT: ret <4 x i32> %[[VALUE]] + export uint TestGetDimensions() { uint dim1, dim2; Buf.GetDimensions(dim1); diff --git a/llvm/lib/Target/DirectX/DXILShaderFlags.cpp b/llvm/lib/Target/DirectX/DXILShaderFlags.cpp index ce6e8121b9d94..e0049dc75c0db 100644 --- a/llvm/lib/Target/DirectX/DXILShaderFlags.cpp +++ b/llvm/lib/Target/DirectX/DXILShaderFlags.cpp @@ -100,6 +100,26 @@ static bool checkWaveOps(Intrinsic::ID IID) { } } +// Checks to see if the status bit from a load with status +// instruction is ever extracted. If it is, the module needs +// to have the TiledResources shader flag set. +bool checkIfStatusIsExtracted(const IntrinsicInst &II) { + [[maybe_unused]] Intrinsic::ID IID = II.getIntrinsicID(); + assert(IID == Intrinsic::dx_resource_load_typedbuffer || + IID == Intrinsic::dx_resource_load_rawbuffer && + "unexpected intrinsic ID"); + for (const User *U : II.users()) { + if (const ExtractValueInst *EVI = dyn_cast<ExtractValueInst>(U)) { + // Resource load operations return a {result, status} pair. + // Check if we extract the status + if (EVI->getNumIndices() == 1 && EVI->getIndices()[0] == 1) + return true; + } + } + + return false; +} + /// Update the shader flags mask based on the given instruction. /// \param CSF Shader flags mask to update. /// \param I Instruction to check. @@ -164,7 +184,7 @@ void ModuleShaderFlags::updateFunctionFlags(ComputedShaderFlags &CSF, } } - if (auto *II = dyn_cast<IntrinsicInst>(&I)) { + if (const auto *II = dyn_cast<IntrinsicInst>(&I)) { switch (II->getIntrinsicID()) { default: break; @@ -192,6 +212,13 @@ void ModuleShaderFlags::updateFunctionFlags(ComputedShaderFlags &CSF, DRTM[cast<TargetExtType>(II->getArgOperand(0)->getType())]; if (RTI.isTyped()) CSF.TypedUAVLoadAdditionalFormats |= RTI.getTyped().ElementCount > 1; + if (!CSF.TiledResources && checkIfStatusIsExtracted(*II)) + CSF.TiledResources = true; + break; + } + case Intrinsic::dx_resource_load_rawbuffer: { + if (!CSF.TiledResources && checkIfStatusIsExtracted(*II)) + CSF.TiledResources = true; break; } } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
