https://github.com/bogner created https://github.com/llvm/llvm-project/pull/121725
This adds handling for raw and structured buffers when lowering resource access via `llvm.dx.resource.getpointer`. Fixes #121714 >From 192d20b16e418b89a549e93406f782ac0a0fa093 Mon Sep 17 00:00:00 2001 From: Justin Bogner <m...@justinbogner.com> Date: Fri, 20 Dec 2024 17:43:37 -0700 Subject: [PATCH] [DirectX] Handle dx.RawBuffer in DXILResourceAccess This adds handling for raw and structured buffers when lowering resource access via `llvm.dx.resource.getpointer`. Fixes #121714 --- .../lib/Target/DirectX/DXILResourceAccess.cpp | 255 ++++++++++++------ .../DirectX/ResourceAccess/load_rawbuffer.ll | 167 ++++++++++++ .../DirectX/ResourceAccess/store_rawbuffer.ll | 124 +++++++++ 3 files changed, 469 insertions(+), 77 deletions(-) create mode 100644 llvm/test/CodeGen/DirectX/ResourceAccess/load_rawbuffer.ll create mode 100644 llvm/test/CodeGen/DirectX/ResourceAccess/store_rawbuffer.ll 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.RawBuffer", float, 1, 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", float, 1, 0, 0) %buffer, i32 %index) + + ; CHECK: call void @llvm.dx.resource.store.rawbuffer.tdx.RawBuffer_f32_1_0_0t.f32(target("dx.RawBuffer", float, 1, 0, 0) %buffer, i32 %index, i32 0, float %data) + store float %data, ptr %ptr + + ret void +} + +; CHECK-LABEL: define void @storef32_byte +define void @storef32_byte(i32 %offset, float %data) { + %buffer = call target("dx.RawBuffer", i8, 1, 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", i8, 1, 0, 0) %buffer, i32 %offset) + + ; CHECK: call void @llvm.dx.resource.store.rawbuffer.tdx.RawBuffer_i8_1_0_0t.f32(target("dx.RawBuffer", i8, 1, 0, 0) %buffer, i32 %offset, i32 0, float %data) + store float %data, ptr %ptr + + ret void +} + +; CHECK-LABEL: define void @storev4f32_struct +define void @storev4f32_struct(i32 %index, <4 x float> %data) { + %buffer = call target("dx.RawBuffer", <4 x float>, 1, 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", <4 x float>, 1, 0, 0) %buffer, i32 %index) + + ; CHECK: call void @llvm.dx.resource.store.rawbuffer.tdx.RawBuffer_v4f32_1_0_0t.v4f32(target("dx.RawBuffer", <4 x float>, 1, 0, 0) %buffer, i32 %index, i32 0, <4 x float> %data) + store <4 x float> %data, ptr %ptr + + ret void +} + +; CHECK-LABEL: define void @storev4f32_byte +define void @storev4f32_byte(i32 %offset, <4 x float> %data) { + %buffer = call target("dx.RawBuffer", i8, 1, 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", i8, 1, 0, 0) %buffer, i32 %offset) + + ; CHECK: call void @llvm.dx.resource.store.rawbuffer.tdx.RawBuffer_i8_1_0_0t.v4f32(target("dx.RawBuffer", i8, 1, 0, 0) %buffer, i32 %offset, i32 0, <4 x float> %data) + store <4 x float> %data, ptr %ptr + + ret void +} + +; CHECK-LABEL: define void @storeelements +define void @storeelements(i32 %index, <4 x float> %dataf32, <4 x i32> %datai32) { + %buffer = call target("dx.RawBuffer", {<4 x float>, <4 x i32>}, 1, 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", {<4 x float>, <4 x i32>}, 1, 0, 0) %buffer, + i32 %index) + + ; CHECK: call void @llvm.dx.resource.store.rawbuffer.tdx.RawBuffer_sl_v4f32v4i32s_1_0_0t.v4f32(target("dx.RawBuffer", { <4 x float>, <4 x i32> }, 1, 0, 0) %buffer, i32 %index, i32 0, <4 x float> %dataf32) + store <4 x float> %dataf32, ptr %ptr + + ; CHECK: call void @llvm.dx.resource.store.rawbuffer.tdx.RawBuffer_sl_v4f32v4i32s_1_0_0t.v4i32(target("dx.RawBuffer", { <4 x float>, <4 x i32> }, 1, 0, 0) %buffer, i32 %index, i32 16, <4 x i32> %datai32) + %addri32 = getelementptr inbounds nuw i8, ptr %ptr, i32 16 + store <4 x i32> %datai32, ptr %addri32 + + ret void +} + +; CHECK-LABEL: define void @storenested +define void @storenested(i32 %index, i32 %datai32, <4 x float> %dataf32, <3 x half> %dataf16) { + %buffer = call + target("dx.RawBuffer", {i32, {<4 x float>, <3 x half>}}, 1, 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>}}, 1, 0, 0) %buffer, + i32 %index) + + ; CHECK: call void @llvm.dx.resource.store.rawbuffer.tdx.RawBuffer_sl_i32sl_v4f32v3f16ss_1_0_0t.i32(target("dx.RawBuffer", { i32, { <4 x float>, <3 x half> } }, 1, 0, 0) %buffer, i32 %index, i32 0, i32 %datai32) + store i32 %datai32, ptr %ptr + + ; CHECK: call void @llvm.dx.resource.store.rawbuffer.tdx.RawBuffer_sl_i32sl_v4f32v3f16ss_1_0_0t.v4f32(target("dx.RawBuffer", { i32, { <4 x float>, <3 x half> } }, 1, 0, 0) %buffer, i32 %index, i32 4, <4 x float> %dataf32) + %addrf32 = getelementptr inbounds nuw i8, ptr %ptr, i32 4 + store <4 x float> %dataf32, ptr %addrf32 + + ; CHECK: call void @llvm.dx.resource.store.rawbuffer.tdx.RawBuffer_sl_i32sl_v4f32v3f16ss_1_0_0t.v3f16(target("dx.RawBuffer", { i32, { <4 x float>, <3 x half> } }, 1, 0, 0) %buffer, i32 %index, i32 20, <3 x half> %dataf16) + %addrf16 = getelementptr inbounds nuw i8, ptr %ptr, i32 20 + store <3 x half> %dataf16, ptr %addrf16 + + ret void +} + +; byteaddressbuf.Store<int64_t4> +; CHECK-LABEL: define void @storev4f64_byte +define void @storev4f64_byte(i32 %offset, <4 x double> %data) { + %buffer = call target("dx.RawBuffer", i8, 1, 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", i8, 1, 0, 0) %buffer, i32 %offset) + + ; CHECK: call void @llvm.dx.resource.store.rawbuffer.tdx.RawBuffer_i8_1_0_0t.v4f64(target("dx.RawBuffer", i8, 1, 0, 0) %buffer, i32 %offset, i32 0, <4 x double> %data) + store <4 x double> %data, ptr %ptr + + ret void +} _______________________________________________ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits