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{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(); + 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 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 Min, return __detail::smoothstep_vec_impl(Min, Max, X); } +inline bool CheckAccessFullyMapped(uint Status) { + return static_cast(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 BuiltinTypeMethodBuilder &callBuiltin(StringRef BuiltinName, - QualType ReturnType, Ts... ArgSpecs); + QualType ReturnType, Ts &&...ArgSpecs); template BuiltinTypeMethodBuilder &assign(TLHS LHS, TRHS RHS); template BuiltinTypeMethodBuilder &dereference(T Ptr); @@ -572,7 +572,7 @@ BuiltinTypeMethodBuilder &BuiltinTypeMethodBuilder::returnThis() { template BuiltinTypeMethodBuilder & BuiltinTypeMethodBuilder::callBuiltin(StringRef BuiltinName, - QualType ReturnType, Ts... ArgSpecs) { + QualType ReturnType, Ts &&...ArgSpecs) { ensureCompleteDecl(); std::array 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(); + 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 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 {{.*}} +// CHECK-LOAD-NEXT: DeclRefExpr {{.*}} '' 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]]' 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 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' +// CHECK-NEXT: DeclRefExpr {{.*}} '' 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]]' 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::Load(unsigned int, unsigned int&)(ptr {{.*}} @RWSB1, i32 noundef 1, ptr {{.*}} %tmp) +// CHECK: call {{.*}} float @hlsl::StructuredBuffer::Load(unsigned int, unsigned int&)(ptr {{.*}} @SB1, i32 noundef 2, ptr {{.*}} %tmp1) +// CHECK: add +// CHECK: ret float + +// CHECK: define {{.*}} float @hlsl::RWStructuredBuffer::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::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::Load(unsigned int, unsigned int&)(ptr {{.*}} @ROSB1, i32 noundef 10, ptr {{.*}} %tmp) +// CHECK: call {{.*}} <2 x i32> @hlsl::RasterizerOrderedStructuredBuffer::Load(unsigned int, unsigned int&)(ptr {{.*}} @ROSB2, i32 noundef 20, ptr {{.*}} %tmp2) +// CHECK: ret + +// CHECK: define {{.*}} float @hlsl::RasterizerOrderedStructuredBuffer::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::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::Load(unsigned int, unsigned int&)(ptr {{.*}} @Buf, i32 noundef 1, ptr {{.*}} %tmp) +// CHECK: call {{.*}} <4 x i32> @hlsl::RWBuffer::Load(unsigned int, unsigned int&)(ptr {{.*}} @RWBuf, i32 noundef 2, ptr {{.*}} %tmp1) +// CHECK: add +// CHECK: ret float + +// CHECK: define {{.*}} float @hlsl::Buffer::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::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(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(&I)) { + if (const auto *II = dyn_cast(&I)) { switch (II->getIntrinsicID()) { default: break; @@ -192,6 +212,13 @@ void ModuleShaderFlags::updateFunctionFlags(ComputedShaderFlags &CSF, DRTM[cast(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; } }