[llvm-branch-commits] [llvm] [DirectX] Handle dx.RawBuffer in DXILResourceAccess (PR #121725)
via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Sun Jan 5 18:49:15 PST 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-backend-directx
Author: Justin Bogner (bogner)
<details>
<summary>Changes</summary>
This adds handling for raw and structured buffers when lowering resource access via `llvm.dx.resource.getpointer`.
Fixes #<!-- -->121714
---
Patch is 25.43 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/121725.diff
3 Files Affected:
- (modified) llvm/lib/Target/DirectX/DXILResourceAccess.cpp (+178-77)
- (added) llvm/test/CodeGen/DirectX/ResourceAccess/load_rawbuffer.ll (+167)
- (added) llvm/test/CodeGen/DirectX/ResourceAccess/store_rawbuffer.ll (+124)
``````````diff
diff --git a/llvm/lib/Target/DirectX/DXILResourceAccess.cpp b/llvm/lib/Target/DirectX/DXILResourceAccess.cpp
index 837624935c5fae..b0074b58e403c4 100644
--- a/llvm/lib/Target/DirectX/DXILResourceAccess.cpp
+++ b/llvm/lib/Target/DirectX/DXILResourceAccess.cpp
@@ -21,99 +21,207 @@
using namespace llvm;
-static void replaceTypedBufferAccess(IntrinsicInst *II,
- dxil::ResourceTypeInfo &RTI) {
- const DataLayout &DL = II->getDataLayout();
+static Value *calculateGEPOffset(GetElementPtrInst *GEP, Value *PrevOffset,
+ dxil::ResourceTypeInfo &RTI) {
+ assert(!PrevOffset && "Non-constant GEP chains not handled yet");
+
+ const DataLayout &DL = GEP->getDataLayout();
+
+ uint64_t ScalarSize = 1;
+ if (RTI.isTyped()) {
+ Type *ContainedType = RTI.getHandleTy()->getTypeParameter(0);
+ // We need the size of an element in bytes so that we can calculate the
+ // offset in elements given a total offset in bytes.
+ Type *ScalarType = ContainedType->getScalarType();
+ ScalarSize = DL.getTypeSizeInBits(ScalarType) / 8;
+ }
+
+ APInt ConstantOffset(DL.getIndexTypeSizeInBits(GEP->getType()), 0);
+ if (GEP->accumulateConstantOffset(DL, ConstantOffset)) {
+ APInt Scaled = ConstantOffset.udiv(ScalarSize);
+ return ConstantInt::get(Type::getInt32Ty(GEP->getContext()), Scaled);
+ }
+
+ auto IndexIt = GEP->idx_begin();
+ assert(cast<ConstantInt>(IndexIt)->getZExtValue() == 0 &&
+ "GEP is not indexing through pointer");
+ ++IndexIt;
+ Value *Offset = *IndexIt;
+ assert(++IndexIt == GEP->idx_end() && "Too many indices in GEP");
+ return Offset;
+}
+
+static void createTypedBufferStore(IntrinsicInst *II, StoreInst *SI,
+ Value *Offset, dxil::ResourceTypeInfo &RTI) {
+ IRBuilder<> Builder(SI);
+ Type *ContainedType = RTI.getHandleTy()->getTypeParameter(0);
+ Type *LoadType = StructType::get(ContainedType, Builder.getInt1Ty());
+
+ Value *V = SI->getValueOperand();
+ if (V->getType() == ContainedType) {
+ // V is already the right type.
+ assert(!Offset && "store of whole element has offset?");
+ } else if (V->getType() == ContainedType->getScalarType()) {
+ // We're storing a scalar, so we need to load the current value and only
+ // replace the relevant part.
+ auto *Load = Builder.CreateIntrinsic(
+ LoadType, Intrinsic::dx_resource_load_typedbuffer,
+ {II->getOperand(0), II->getOperand(1)});
+ auto *Struct = Builder.CreateExtractValue(Load, {0});
+
+ // If we have an offset from seeing a GEP earlier, use that. Otherwise, 0.
+ if (!Offset)
+ Offset = ConstantInt::get(Builder.getInt32Ty(), 0);
+ V = Builder.CreateInsertElement(Struct, V, Offset);
+ } else {
+ llvm_unreachable("Store to typed resource has invalid type");
+ }
+
+ auto *Inst = Builder.CreateIntrinsic(
+ Builder.getVoidTy(), Intrinsic::dx_resource_store_typedbuffer,
+ {II->getOperand(0), II->getOperand(1), V});
+ SI->replaceAllUsesWith(Inst);
+}
+
+static void createRawStore(IntrinsicInst *II, StoreInst *SI, Value *Offset) {
+ IRBuilder<> Builder(SI);
+
+ if (!Offset)
+ Offset = ConstantInt::get(Builder.getInt32Ty(), 0);
+ Value *V = SI->getValueOperand();
+ // TODO: break up larger types
+ auto *Inst = Builder.CreateIntrinsic(
+ Builder.getVoidTy(), Intrinsic::dx_resource_store_rawbuffer,
+ {II->getOperand(0), II->getOperand(1), Offset, V});
+ SI->replaceAllUsesWith(Inst);
+}
+
+static void createStoreIntrinsic(IntrinsicInst *II, StoreInst *SI,
+ Value *Offset, dxil::ResourceTypeInfo &RTI) {
+ switch (RTI.getResourceKind()) {
+ case dxil::ResourceKind::TypedBuffer:
+ return createTypedBufferStore(II, SI, Offset, RTI);
+ case dxil::ResourceKind::RawBuffer:
+ case dxil::ResourceKind::StructuredBuffer:
+ return createRawStore(II, SI, Offset);
+ case dxil::ResourceKind::Texture1D:
+ case dxil::ResourceKind::Texture2D:
+ case dxil::ResourceKind::Texture2DMS:
+ case dxil::ResourceKind::Texture3D:
+ case dxil::ResourceKind::TextureCube:
+ case dxil::ResourceKind::Texture1DArray:
+ case dxil::ResourceKind::Texture2DArray:
+ case dxil::ResourceKind::Texture2DMSArray:
+ case dxil::ResourceKind::TextureCubeArray:
+ case dxil::ResourceKind::FeedbackTexture2D:
+ case dxil::ResourceKind::FeedbackTexture2DArray:
+ // TODO: handle these
+ return;
+ case dxil::ResourceKind::CBuffer:
+ case dxil::ResourceKind::Sampler:
+ case dxil::ResourceKind::TBuffer:
+ case dxil::ResourceKind::RTAccelerationStructure:
+ case dxil::ResourceKind::Invalid:
+ case dxil::ResourceKind::NumEntries:
+ llvm_unreachable("Invalid resource kind for store");
+ }
+ llvm_unreachable("Unhandled case in switch");
+}
+
+static void createTypedBufferLoad(IntrinsicInst *II, LoadInst *LI,
+ Value *Offset, dxil::ResourceTypeInfo &RTI) {
+ IRBuilder<> Builder(LI);
+ Type *ContainedType = RTI.getHandleTy()->getTypeParameter(0);
+ Type *LoadType = StructType::get(ContainedType, Builder.getInt1Ty());
- auto *HandleType = cast<TargetExtType>(II->getOperand(0)->getType());
- assert(HandleType->getName() == "dx.TypedBuffer" &&
- "Unexpected typed buffer type");
- Type *ContainedType = HandleType->getTypeParameter(0);
+ Value *V =
+ Builder.CreateIntrinsic(LoadType, Intrinsic::dx_resource_load_typedbuffer,
+ {II->getOperand(0), II->getOperand(1)});
+ V = Builder.CreateExtractValue(V, {0});
- Type *LoadType =
- StructType::get(ContainedType, Type::getInt1Ty(II->getContext()));
+ if (Offset)
+ V = Builder.CreateExtractElement(V, Offset);
- // We need the size of an element in bytes so that we can calculate the offset
- // in elements given a total offset in bytes later.
- Type *ScalarType = ContainedType->getScalarType();
- uint64_t ScalarSize = DL.getTypeSizeInBits(ScalarType) / 8;
+ LI->replaceAllUsesWith(V);
+}
+static void createRawLoad(IntrinsicInst *II, LoadInst *LI, Value *Offset) {
+ IRBuilder<> Builder(LI);
+ // TODO: break up larger types
+ Type *LoadType = StructType::get(LI->getType(), Builder.getInt1Ty());
+ if (!Offset)
+ Offset = ConstantInt::get(Builder.getInt32Ty(), 0);
+ Value *V =
+ Builder.CreateIntrinsic(LoadType, Intrinsic::dx_resource_load_rawbuffer,
+ {II->getOperand(0), II->getOperand(1), Offset});
+ V = Builder.CreateExtractValue(V, {0});
+
+ LI->replaceAllUsesWith(V);
+}
+
+static void createLoadIntrinsic(IntrinsicInst *II, LoadInst *LI, Value *Offset,
+ dxil::ResourceTypeInfo &RTI) {
+ switch (RTI.getResourceKind()) {
+ case dxil::ResourceKind::TypedBuffer:
+ return createTypedBufferLoad(II, LI, Offset, RTI);
+ case dxil::ResourceKind::RawBuffer:
+ case dxil::ResourceKind::StructuredBuffer:
+ return createRawLoad(II, LI, Offset);
+ case dxil::ResourceKind::Texture1D:
+ case dxil::ResourceKind::Texture2D:
+ case dxil::ResourceKind::Texture2DMS:
+ case dxil::ResourceKind::Texture3D:
+ case dxil::ResourceKind::TextureCube:
+ case dxil::ResourceKind::Texture1DArray:
+ case dxil::ResourceKind::Texture2DArray:
+ case dxil::ResourceKind::Texture2DMSArray:
+ case dxil::ResourceKind::TextureCubeArray:
+ case dxil::ResourceKind::FeedbackTexture2D:
+ case dxil::ResourceKind::FeedbackTexture2DArray:
+ case dxil::ResourceKind::CBuffer:
+ case dxil::ResourceKind::TBuffer:
+ // TODO: handle these
+ return;
+ case dxil::ResourceKind::Sampler:
+ case dxil::ResourceKind::RTAccelerationStructure:
+ case dxil::ResourceKind::Invalid:
+ case dxil::ResourceKind::NumEntries:
+ llvm_unreachable("Invalid resource kind for load");
+ }
+ llvm_unreachable("Unhandled case in switch");
+}
+
+static void
+replaceAccess(IntrinsicInst *II, dxil::ResourceTypeInfo &RTI) {
// Process users keeping track of indexing accumulated from GEPs.
- struct AccessAndIndex {
+ struct AccessAndOffset {
User *Access;
- Value *Index;
+ Value *Offset;
};
- SmallVector<AccessAndIndex> Worklist;
+ SmallVector<AccessAndOffset> Worklist;
for (User *U : II->users())
Worklist.push_back({U, nullptr});
SmallVector<Instruction *> DeadInsts;
while (!Worklist.empty()) {
- AccessAndIndex Current = Worklist.back();
+ AccessAndOffset Current = Worklist.back();
Worklist.pop_back();
if (auto *GEP = dyn_cast<GetElementPtrInst>(Current.Access)) {
IRBuilder<> Builder(GEP);
- Value *Index;
- APInt ConstantOffset(DL.getIndexTypeSizeInBits(GEP->getType()), 0);
- if (GEP->accumulateConstantOffset(DL, ConstantOffset)) {
- APInt Scaled = ConstantOffset.udiv(ScalarSize);
- Index = ConstantInt::get(Builder.getInt32Ty(), Scaled);
- } else {
- auto IndexIt = GEP->idx_begin();
- assert(cast<ConstantInt>(IndexIt)->getZExtValue() == 0 &&
- "GEP is not indexing through pointer");
- ++IndexIt;
- Index = *IndexIt;
- assert(++IndexIt == GEP->idx_end() && "Too many indices in GEP");
- }
-
+ Value *Offset = calculateGEPOffset(GEP, Current.Offset, RTI);
for (User *U : GEP->users())
- Worklist.push_back({U, Index});
+ Worklist.push_back({U, Offset});
DeadInsts.push_back(GEP);
} else if (auto *SI = dyn_cast<StoreInst>(Current.Access)) {
assert(SI->getValueOperand() != II && "Pointer escaped!");
- IRBuilder<> Builder(SI);
-
- Value *V = SI->getValueOperand();
- if (V->getType() == ContainedType) {
- // V is already the right type.
- } else if (V->getType() == ScalarType) {
- // We're storing a scalar, so we need to load the current value and only
- // replace the relevant part.
- auto *Load = Builder.CreateIntrinsic(
- LoadType, Intrinsic::dx_resource_load_typedbuffer,
- {II->getOperand(0), II->getOperand(1)});
- auto *Struct = Builder.CreateExtractValue(Load, {0});
-
- // If we have an offset from seeing a GEP earlier, use it.
- Value *IndexOp = Current.Index
- ? Current.Index
- : ConstantInt::get(Builder.getInt32Ty(), 0);
- V = Builder.CreateInsertElement(Struct, V, IndexOp);
- } else {
- llvm_unreachable("Store to typed resource has invalid type");
- }
-
- auto *Inst = Builder.CreateIntrinsic(
- Builder.getVoidTy(), Intrinsic::dx_resource_store_typedbuffer,
- {II->getOperand(0), II->getOperand(1), V});
- SI->replaceAllUsesWith(Inst);
+ createStoreIntrinsic(II, SI, Current.Offset, RTI);
DeadInsts.push_back(SI);
} else if (auto *LI = dyn_cast<LoadInst>(Current.Access)) {
- IRBuilder<> Builder(LI);
- Value *V = Builder.CreateIntrinsic(
- LoadType, Intrinsic::dx_resource_load_typedbuffer,
- {II->getOperand(0), II->getOperand(1)});
- V = Builder.CreateExtractValue(V, {0});
-
- if (Current.Index)
- V = Builder.CreateExtractElement(V, Current.Index);
-
- LI->replaceAllUsesWith(V);
+ createLoadIntrinsic(II, LI, Current.Offset, RTI);
DeadInsts.push_back(LI);
} else
@@ -137,15 +245,8 @@ static bool transformResourcePointers(Function &F, DXILResourceTypeMap &DRTM) {
Resources.emplace_back(II, DRTM[HandleTy]);
}
- for (auto &[II, RI] : Resources) {
- if (RI.isTyped()) {
- Changed = true;
- replaceTypedBufferAccess(II, RI);
- }
-
- // TODO: handle other resource types. We should probably have an
- // `unreachable` here once we've added support for all of them.
- }
+ for (auto &[II, RI] : Resources)
+ replaceAccess(II, RI);
return Changed;
}
diff --git a/llvm/test/CodeGen/DirectX/ResourceAccess/load_rawbuffer.ll b/llvm/test/CodeGen/DirectX/ResourceAccess/load_rawbuffer.ll
new file mode 100644
index 00000000000000..5d2e4041c3ea02
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/ResourceAccess/load_rawbuffer.ll
@@ -0,0 +1,167 @@
+; RUN: opt -S -dxil-resource-access %s | FileCheck %s
+
+target triple = "dxil-pc-shadermodel6.6-compute"
+
+declare void @f32_user(float)
+declare void @v4f32_user(<4 x float>)
+declare void @i32_user(i32)
+declare void @v4i32_user(<4 x i32>)
+declare void @v3f16_user(<3 x half>)
+declare void @v4f64_user(<4 x double>)
+
+; CHECK-LABEL: define void @loadf32_struct
+define void @loadf32_struct(i32 %index) {
+ %buffer = call target("dx.RawBuffer", float, 0, 0, 0)
+ @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_0_0_0(
+ i32 0, i32 0, i32 1, i32 0, i1 false)
+
+ ; CHECK-NOT: @llvm.dx.resource.getpointer
+ %ptr = call ptr @llvm.dx.resource.getpointer(
+ target("dx.RawBuffer", float, 0, 0, 0) %buffer, i32 %index)
+
+ ; CHECK: %[[LOAD:.*]] = call { float, i1 } @llvm.dx.resource.load.rawbuffer.f32.tdx.RawBuffer_f32_0_0_0t(target("dx.RawBuffer", float, 0, 0, 0) %buffer, i32 %index, i32 0)
+ ; CHECK: %[[VAL:.*]] = extractvalue { float, i1 } %[[LOAD]], 0
+ ; CHECK: call void @f32_user(float %[[VAL]])
+ %data = load float, ptr %ptr
+ call void @f32_user(float %data)
+
+ ret void
+}
+
+; CHECK-LABEL: define void @loadf32_byte
+define void @loadf32_byte(i32 %offset) {
+ %buffer = call target("dx.RawBuffer", i8, 0, 0, 0)
+ @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_i8_0_0_0(
+ i32 0, i32 0, i32 1, i32 0, i1 false)
+
+ ; CHECK-NOT: @llvm.dx.resource.getpointer
+ %ptr = call ptr @llvm.dx.resource.getpointer(
+ target("dx.RawBuffer", i8, 0, 0, 0) %buffer, i32 %offset)
+
+ ; CHECK: %[[LOAD:.*]] = call { float, i1 } @llvm.dx.resource.load.rawbuffer.f32.tdx.RawBuffer_i8_0_0_0t(target("dx.RawBuffer", i8, 0, 0, 0) %buffer, i32 %offset, i32 0)
+ ; CHECK: %[[VAL:.*]] = extractvalue { float, i1 } %[[LOAD]], 0
+ ; CHECK: call void @f32_user(float %[[VAL]])
+ %data = load float, ptr %ptr
+ call void @f32_user(float %data)
+
+ ret void
+}
+
+; CHECK-LABEL: define void @loadv4f32_struct
+define void @loadv4f32_struct(i32 %index) {
+ %buffer = call target("dx.RawBuffer", <4 x float>, 0, 0, 0)
+ @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_v4f32_0_0_0(
+ i32 0, i32 0, i32 1, i32 0, i1 false)
+
+ ; CHECK-NOT: @llvm.dx.resource.getpointer
+ %ptr = call ptr @llvm.dx.resource.getpointer(
+ target("dx.RawBuffer", <4 x float>, 0, 0, 0) %buffer, i32 %index)
+
+ ; CHECK: %[[LOAD:.*]] = call { <4 x float>, i1 } @llvm.dx.resource.load.rawbuffer.v4f32.tdx.RawBuffer_v4f32_0_0_0t(target("dx.RawBuffer", <4 x float>, 0, 0, 0) %buffer, i32 %index, i32 0)
+ ; CHECK: %[[VAL:.*]] = extractvalue { <4 x float>, i1 } %[[LOAD]], 0
+ ; CHECK: call void @v4f32_user(<4 x float> %[[VAL]])
+ %data = load <4 x float>, ptr %ptr
+ call void @v4f32_user(<4 x float> %data)
+
+ ret void
+}
+
+; CHECK-LABEL: define void @loadv4f32_byte
+define void @loadv4f32_byte(i32 %offset) {
+ %buffer = call target("dx.RawBuffer", i8, 0, 0, 0)
+ @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_i8_0_0_0(
+ i32 0, i32 0, i32 1, i32 0, i1 false)
+
+ ; CHECK-NOT: @llvm.dx.resource.getpointer
+ %ptr = call ptr @llvm.dx.resource.getpointer(
+ target("dx.RawBuffer", i8, 0, 0, 0) %buffer, i32 %offset)
+
+ ; CHECK: %[[LOAD:.*]] = call { <4 x float>, i1 } @llvm.dx.resource.load.rawbuffer.v4f32.tdx.RawBuffer_i8_0_0_0t(target("dx.RawBuffer", i8, 0, 0, 0) %buffer, i32 %offset, i32 0)
+ ; CHECK: %[[VAL:.*]] = extractvalue { <4 x float>, i1 } %[[LOAD]], 0
+ ; CHECK: call void @v4f32_user(<4 x float> %[[VAL]]
+ %data = load <4 x float>, ptr %ptr
+ call void @v4f32_user(<4 x float> %data)
+
+ ret void
+}
+
+; CHECK-LABEL: define void @loadelements
+define void @loadelements(i32 %index) {
+ %buffer = call target("dx.RawBuffer", {<4 x float>, <4 x i32>}, 0, 0, 0)
+ @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_sl_v4f32v4i32s_0_0_0(
+ i32 0, i32 0, i32 1, i32 0, i1 false)
+
+ ; CHECK-NOT: @llvm.dx.resource.getpointer
+ %ptr = call ptr @llvm.dx.resource.getpointer(
+ target("dx.RawBuffer", {<4 x float>, <4 x i32>}, 0, 0, 0) %buffer,
+ i32 %index)
+
+ ; CHECK: %[[LOADF32:.*]] = call { <4 x float>, i1 } @llvm.dx.resource.load.rawbuffer.v4f32.tdx.RawBuffer_sl_v4f32v4i32s_0_0_0t(target("dx.RawBuffer", { <4 x float>, <4 x i32> }, 0, 0, 0) %buffer, i32 %index, i32 0)
+ ; CHECK: %[[VALF32:.*]] = extractvalue { <4 x float>, i1 } %[[LOADF32]], 0
+ ; CHECK: call void @v4f32_user(<4 x float> %[[VALF32]]
+ %dataf32 = load <4 x float>, ptr %ptr
+ call void @v4f32_user(<4 x float> %dataf32)
+
+ ; CHECK: %[[LOADI32:.*]] = call { <4 x i32>, i1 } @llvm.dx.resource.load.rawbuffer.v4i32.tdx.RawBuffer_sl_v4f32v4i32s_0_0_0t(target("dx.RawBuffer", { <4 x float>, <4 x i32> }, 0, 0, 0) %buffer, i32 %index, i32 16)
+ ; CHECK: %[[VALI32:.*]] = extractvalue { <4 x i32>, i1 } %[[LOADI32]], 0
+ ; CHECK: call void @v4i32_user(<4 x i32> %[[VALI32]]
+ %addri32 = getelementptr inbounds nuw i8, ptr %ptr, i32 16
+ %datai32 = load <4 x i32>, ptr %addri32
+ call void @v4i32_user(<4 x i32> %datai32)
+
+ ret void
+}
+
+; CHECK-LABEL: define void @loadnested
+define void @loadnested(i32 %index) {
+ %buffer = call
+ target("dx.RawBuffer", {i32, {<4 x float>, <3 x half>}}, 0, 0, 0)
+ @llvm.dx.resource.handlefrombinding(i32 0, i32 0, i32 1, i32 0, i1 false)
+
+ ; CHECK-NOT: @llvm.dx.resource.getpointer
+ %ptr = call ptr @llvm.dx.resource.getpointer(
+ target("dx.RawBuffer", {i32, {<4 x float>, <3 x half>}}, 0, 0, 0) %buffer,
+ i32 %index)
+
+ ; CHECK: %[[LOADI32:.*]] = call { i32, i1 } @llvm.dx.resource.load.rawbuffer.i32.tdx.RawBuffer_sl_i32sl_v4f32v3f16ss_0_0_0t(target("dx.RawBuffer", { i32, { <4 x float>, <3 x half> } }, 0, 0, 0) %buffer, i32 %index, i32 0)
+ ; CHECK: %[[VALI32:.*]] = extractvalue { i32, i1 } %[[LOADI32]], 0
+ ; CHECK: call void @i32_user(i32 %[[VALI32]])
+ %datai32 = load i32, ptr %ptr
+ call void @i32_user(i32 %datai32)
+
+ ; CHECK: %[[LOADF32:.*]] = call { <4 x float>, i1 } @llvm.dx.resource.load.rawbuffer.v4f32.tdx.RawBuffer_sl_i32sl_v4f32v3f16ss_0_0_0t(target("dx.RawBuffer", { i32, { <4 x float>, <3 x half> } }, 0, 0, 0) %buffer, i32 %index, i32 4)
+ ; CHECK: %[[VALF32:.*]] = extractvalue { <4 x float>, i1 } %[[LOADF32]], 0
+ ; CHECK: call void @v4f32_user(<4 x float> %[[VALF32]])
+ %addrf32 = getelementptr inbounds nuw i8, ptr %ptr, i32 4
+ %dataf32 = load <4 x float>, ptr %addrf32
+ call void @v4f32_user(<4 x float> %dataf32)
+
+ ; CHECK: %[[LOADF16:.*]] = call { <3 x half>, i1 } @llvm.dx.resource.load.rawbuffer.v3f16.tdx.RawBuffer_sl_i32sl_v4f32v3f16ss_0_0_0t(target("dx.RawBuffer", { i32, { <4 x float>, <3 x half> } }, 0, 0, 0) %buffer, i32 %index, i32 20)
+ ; CHECK: %[[VALF16:.*]] = extractvalue { <3 x half>, i1 } %[[LOADF16]], 0
+ ; CHECK: call void @v3f16_user(<3 x half> %[[VALF16]])
+ %addrf16 = getelementptr inbounds nuw i8, ptr %ptr, i32 20
+ %dataf16 = load <3 x half>, ptr %addrf16
+ call void @v3f16_user(<3 x half> %dataf16)
+
+ ret void
+}
+
+; byteaddressbuf.Load<int64_t4>
+; CHECK-LABEL: define void @loadv4f64_byte
+define void @loadv4f64_byte(i32 %offset) {
+ %buffer = call target("dx.RawBuffer", i8, 0, 0, 0)
+ @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_i8_0_0_0(
+ i32 0, i32 0, i32 1, i32 0, i1 false)
+
+ ; CHECK-NOT: @llvm.dx.resource.getpointer
+ %ptr = call ptr @llvm.dx.resource.getpointer(
+ target("dx.RawBuffer", i8, 0, 0, 0) %buffer, i32 %offset)
+
+ ; CHECK: %[[LOAD:.*]] = call { <4 x double>, i1 } @llvm.dx.resource.load.rawbuffer.v4f64.tdx.RawBuffer_i8_0_0_0t(target("dx.RawBuffer", i8, 0, 0, 0) %buffer, i32 %offset, i32 0)
+ ; CHECK: %[[VAL:.*]] = extractvalue { <4 x double>, i1 } %[[LOAD]], 0
+ ; CHECK: call void @v4f64_user(<4 x double> %[[VAL]])
+ %data = load <4 x double>, ptr %ptr
+ call void @v4f64_user(<4 x double> %data)
+
+ ret void
+}
diff --git a/llvm/test/CodeGen/DirectX/ResourceAccess/store_rawbuffer.ll b/llvm/test/CodeGen/DirectX/ResourceAccess/store_rawbuffer.ll
new file mode 100644
index 00000000000000..b19f9d04a2dffa
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/ResourceAccess/store_rawbuffer.ll
@@ -0,0 +1,124 @@
+; RUN: opt -S -dxil-resource-access %s | FileCheck %s
+
+target triple = "dxil-pc-shadermodel6.6-compute"
+
+; CHECK-LABEL: define void @storef32_struct
+define void @storef32_struct(i32 %index, float %data) {
+ %buffer = call target("dx....
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/121725
More information about the llvm-branch-commits
mailing list