Skip to content
Open
6 changes: 6 additions & 0 deletions clang/include/clang/Basic/Builtins.td
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down
43 changes: 43 additions & 0 deletions clang/lib/CodeGen/CGHLSLBuiltins.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/Headers/hlsl/hlsl_intrinsics.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
//===----------------------------------------------------------------------===//
Expand Down
17 changes: 17 additions & 0 deletions clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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) {
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down
18 changes: 18 additions & 0 deletions clang/lib/Sema/SemaHLSL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
20 changes: 20 additions & 0 deletions clang/test/AST/HLSL/StructuredBuffers-AST.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -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 ()'
Expand Down
19 changes: 19 additions & 0 deletions clang/test/AST/HLSL/TypedBuffers-AST.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -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)'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
37 changes: 37 additions & 0 deletions clang/test/CodeGenHLSL/resources/StructuredBuffers-methods-ps.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
38 changes: 38 additions & 0 deletions clang/test/CodeGenHLSL/resources/TypedBuffers-methods.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
29 changes: 28 additions & 1 deletion llvm/lib/Target/DirectX/DXILShaderFlags.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
}
Expand Down