[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