[llvm] [HLSL][DirectX] Handles GEPs in dxil resource access pass (PR #224754)

Sarah Spall via llvm-commits llvm-commits at lists.llvm.org
Fri Sep 18 14:53:57 PDT 2026


https://github.com/spall created https://github.com/llvm/llvm-project/pull/224754

Add support to traverse through GEPs in dxil resource access pass, as well as support getbasepointer which is used by cbuffers.

Closes #224102

Assisted by GPT-5.6 Sol and @inbelic 

>From c5ad74db2f5eec8907559c97e905fd8d86248ce7 Mon Sep 17 00:00:00 2001
From: Sarah Spall <sarahspall at microsoft.com>
Date: Thu, 17 Sep 2026 15:39:09 -0700
Subject: [PATCH 1/6] start with @inbelic original pr

---
 .../lib/Target/DirectX/DXILResourceAccess.cpp | 89 +++++++++++++++++--
 1 file changed, 84 insertions(+), 5 deletions(-)

diff --git a/llvm/lib/Target/DirectX/DXILResourceAccess.cpp b/llvm/lib/Target/DirectX/DXILResourceAccess.cpp
index 40f5a36284ae3..5d87efd030f5f 100644
--- a/llvm/lib/Target/DirectX/DXILResourceAccess.cpp
+++ b/llvm/lib/Target/DirectX/DXILResourceAccess.cpp
@@ -9,6 +9,7 @@
 #include "DXILResourceAccess.h"
 #include "DirectX.h"
 #include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/MapVector.h"
 #include "llvm/ADT/SetVector.h"
 #include "llvm/ADT/SmallSet.h"
 #include "llvm/Analysis/DXILResource.h"
@@ -650,6 +651,7 @@ static void createCBufferLoad(IntrinsicInst *II, LoadInst *LI,
         DL, Builder, LastGEP->getPointerOperand(), hlsl::CBufferRowSizeInBytes);
     CurrentRow = Builder.CreateAdd(GEPOffset, CurrentRow);
 
+    // todo look here
     APInt ConstantOffset(DL.getIndexTypeSizeInBits(LastGEP->getType()), 0);
     if (LastGEP->accumulateConstantOffset(DL, ConstantOffset)) {
       APInt Remainder(DL.getIndexTypeSizeInBits(LastGEP->getType()),
@@ -793,6 +795,8 @@ static SmallVector<IntrinsicInst *> collectUsedHandles(Value *Ptr) {
     } else if (auto *Select = dyn_cast<SelectInst>(X))
       for (Value *V : {Select->getTrueValue(), Select->getFalseValue()})
         Worklist.push_back(V);
+    else if (auto *GEP = dyn_cast<GetElementPtrInst>(X))
+      Worklist.push_back(GEP->getPointerOperand());
     else if (auto *II = dyn_cast<IntrinsicInst>(X)) {
       Intrinsic::ID IID = II->getIntrinsicID();
 
@@ -828,24 +832,52 @@ namespace {
 struct AccessIndices {
   Value *GetPtrIdx;
   Value *HandleIdx;
+  Value *OffsetIdx;
 
   bool hasGetPtrIdx() { return GetPtrIdx != nullptr; }
   bool hasHandleIdx() { return HandleIdx != nullptr; }
+  bool hasOffsetIdx() { return OffsetIdx != nullptr; }
 };
 } // namespace
 
+// Compute the total byte offset described by a GEP as a single i32 value,
+// handling both constant and variable indices.
+static Value *accumulateGEPOffset(GetElementPtrInst *GEP,
+                                  IRBuilder<> &Builder) {
+  const DataLayout &DL = GEP->getDataLayout();
+  unsigned BitWidth = DL.getIndexTypeSizeInBits(GEP->getType());
+  SmallMapVector<Value *, APInt, 4> VariableOffsets;
+  APInt ConstantOffset(BitWidth, 0);
+  bool Success =
+      GEP->collectOffset(DL, BitWidth, VariableOffsets, ConstantOffset);
+  assert(Success && "Unhandled GEP structure for resource access");
+  (void)Success;
+
+  Type *I32 = Builder.getInt32Ty();
+  Value *Offset = ConstantInt::get(I32, ConstantOffset.getSExtValue());
+  for (auto &[V, Scale] : VariableOffsets) {
+    Value *Index = Builder.CreateZExtOrTrunc(V, I32);
+    if (!Scale.isOne())
+      Index =
+          Builder.CreateMul(Index, ConstantInt::get(I32, Scale.getSExtValue()));
+    Offset = Builder.CreateAdd(Offset, Index);
+  }
+  return Offset;
+}
+
 // getAccessIndices traverses up the control flow that a ptr came from and
 // propagates back the indicies used to access the resource (AccessIndices):
 //
 //  - GetPtrIdx is the index of dx.resource.getpointer
 //  - HandleIdx is the index of dx.resource.handlefrom.*
+//  - OffsetIdx is the accumulated byte offset of any GEPs in the ptr chain
 static AccessIndices
 getAccessIndices(Instruction *I, SmallSetVector<Instruction *, 16> &DeadInsts,
                  SmallDenseMap<PHINode *, PHINode *> &VisitedPhis) {
   if (auto *II = dyn_cast<IntrinsicInst>(I)) {
     if (llvm::is_contained(HandleIntrins, II->getIntrinsicID())) {
       DeadInsts.insert(II);
-      return {nullptr, II->getArgOperand(/*Index=*/3)};
+      return {nullptr, II->getArgOperand(/*Index=*/3), nullptr};
     }
 
     if (II->getIntrinsicID() == Intrinsic::dx_resource_getpointer) {
@@ -860,10 +892,25 @@ getAccessIndices(Instruction *I, SmallSetVector<Instruction *, 16> &DeadInsts,
     }
   }
 
+  if (auto *GEP = dyn_cast<GetElementPtrInst>(I)) {
+    auto *V = dyn_cast<Instruction>(GEP->getPointerOperand());
+    auto AccessIdx = getAccessIndices(V, DeadInsts, VisitedPhis);
+
+    IRBuilder<> Builder(GEP);
+    Value *GEPOffset = accumulateGEPOffset(GEP, Builder);
+    AccessIdx.OffsetIdx =
+        AccessIdx.hasOffsetIdx()
+            ? Builder.CreateAdd(AccessIdx.OffsetIdx, GEPOffset)
+            : GEPOffset;
+
+    DeadInsts.insert(GEP);
+    return AccessIdx;
+  }
+
   if (auto *Phi = dyn_cast<PHINode>(I)) {
     // If we're already building indices for this phi, return a ref to the phi
     if (auto It = VisitedPhis.find(Phi); It != VisitedPhis.end())
-      return {nullptr, It->second};
+      return {nullptr, It->second, nullptr};
 
     unsigned NumEdges = Phi->getNumIncomingValues();
     assert(NumEdges != 0 && "Malformed Phi Node");
@@ -873,6 +920,8 @@ getAccessIndices(Instruction *I, SmallSetVector<Instruction *, 16> &DeadInsts,
         PHINode::Create(Builder.getInt32Ty(), NumEdges));
     std::unique_ptr<PHINode> HandlePhi(
         PHINode::Create(Builder.getInt32Ty(), NumEdges));
+    std::unique_ptr<PHINode> OffsetPhi(
+        PHINode::Create(Builder.getInt32Ty(), NumEdges));
 
     // Register a ref to this phi for a recursive phi. This is safe to add to
     // the map even if we end up deleting newly created phi below since we can't
@@ -887,6 +936,8 @@ getAccessIndices(Instruction *I, SmallSetVector<Instruction *, 16> &DeadInsts,
       if (AccessIdx.hasGetPtrIdx())
         GetPtrPhi->addIncoming(AccessIdx.GetPtrIdx, BB);
       HandlePhi->addIncoming(AccessIdx.HandleIdx, BB);
+      if (AccessIdx.hasOffsetIdx())
+        OffsetPhi->addIncoming(AccessIdx.OffsetIdx, BB);
     }
 
     Value *GetPtrIdx;
@@ -907,8 +958,16 @@ getAccessIndices(Instruction *I, SmallSetVector<Instruction *, 16> &DeadInsts,
       Builder.Insert(HandleIdx);
     }
 
+    Value *OffsetIdx;
+    if (Value *ConstantIdx = OffsetPhi->hasConstantValue())
+      OffsetIdx = ConstantIdx;
+    else {
+      OffsetIdx = OffsetPhi.release();
+      Builder.Insert(OffsetIdx);
+    }
+
     DeadInsts.insert(Phi);
-    return {GetPtrIdx, HandleIdx};
+    return {GetPtrIdx, HandleIdx, OffsetIdx};
   }
 
   if (auto *Select = dyn_cast<SelectInst>(I)) {
@@ -920,6 +979,7 @@ getAccessIndices(Instruction *I, SmallSetVector<Instruction *, 16> &DeadInsts,
 
     IRBuilder<> Builder(Select);
     Value *GetPtrSelect = nullptr;
+    Value *OffsetSelect = nullptr;
 
     if (TrueAccessIdx.hasGetPtrIdx() && FalseAccessIdx.hasGetPtrIdx())
       GetPtrSelect =
@@ -929,8 +989,19 @@ getAccessIndices(Instruction *I, SmallSetVector<Instruction *, 16> &DeadInsts,
     auto *HandleSelect =
         Builder.CreateSelect(Select->getCondition(), TrueAccessIdx.HandleIdx,
                              FalseAccessIdx.HandleIdx);
+
+    if (TrueAccessIdx.hasOffsetIdx() || FalseAccessIdx.hasOffsetIdx()) {
+      Value *Zero = ConstantInt::get(Builder.getInt32Ty(), 0);
+      Value *TrueOffset =
+          TrueAccessIdx.hasOffsetIdx() ? TrueAccessIdx.OffsetIdx : Zero;
+      Value *FalseOffset =
+          FalseAccessIdx.hasOffsetIdx() ? FalseAccessIdx.OffsetIdx : Zero;
+      OffsetSelect =
+          Builder.CreateSelect(Select->getCondition(), TrueOffset, FalseOffset);
+    }
+
     DeadInsts.insert(Select);
-    return {GetPtrSelect, HandleSelect};
+    return {GetPtrSelect, HandleSelect, OffsetSelect};
   }
 
   llvm_unreachable("collectUsedHandles should assure this does not occur");
@@ -959,7 +1030,15 @@ replaceHandleWithIndices(Instruction *Ptr, IntrinsicInst *OldHandle,
     auto *GetPtr = Builder.CreateIntrinsic(Ptr->getType(),
                                            Intrinsic::dx_resource_getpointer,
                                            {Handle, AccessIdx.GetPtrIdx});
-    Ptr->replaceAllUsesWith(GetPtr);
+
+    // Reapply any byte offset that came from GEPs in the original ptr chain so
+    // that later access lowering still sees it.
+    Value *Result = GetPtr;
+    if (AccessIdx.hasOffsetIdx())
+      Result =
+          Builder.CreateGEP(Builder.getInt8Ty(), GetPtr, AccessIdx.OffsetIdx);
+    // todo is this gep right?
+    Ptr->replaceAllUsesWith(Result);
   } else {
     assert(Ptr->getType()->isTargetExtTy() && !AccessIdx.hasGetPtrIdx() &&
            "Unexpected resource access operand type");

>From a82c03958974490ebf67cc05abb72ba607b3dfed Mon Sep 17 00:00:00 2001
From: Sarah Spall <sarahspall at microsoft.com>
Date: Thu, 17 Sep 2026 19:00:23 -0700
Subject: [PATCH 2/6] handle case where only one branch has gep

---
 llvm/lib/Target/DirectX/DXILResourceAccess.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/llvm/lib/Target/DirectX/DXILResourceAccess.cpp b/llvm/lib/Target/DirectX/DXILResourceAccess.cpp
index 5d87efd030f5f..ad20dc9fc4c97 100644
--- a/llvm/lib/Target/DirectX/DXILResourceAccess.cpp
+++ b/llvm/lib/Target/DirectX/DXILResourceAccess.cpp
@@ -938,6 +938,8 @@ getAccessIndices(Instruction *I, SmallSetVector<Instruction *, 16> &DeadInsts,
       HandlePhi->addIncoming(AccessIdx.HandleIdx, BB);
       if (AccessIdx.hasOffsetIdx())
         OffsetPhi->addIncoming(AccessIdx.OffsetIdx, BB);
+      else
+        OffsetPhi->addIncoming(ConstantInt::get(Builder.getInt32Ty(), 0), BB);
     }
 
     Value *GetPtrIdx;

>From 056f862b5af4351da5f9c5a1bf592067e25b5a2b Mon Sep 17 00:00:00 2001
From: Sarah Spall <sarahspall at microsoft.com>
Date: Thu, 17 Sep 2026 19:44:11 -0700
Subject: [PATCH 3/6] deal with basepointers

---
 llvm/lib/Target/DirectX/DXILResourceAccess.cpp | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/llvm/lib/Target/DirectX/DXILResourceAccess.cpp b/llvm/lib/Target/DirectX/DXILResourceAccess.cpp
index ad20dc9fc4c97..7595d435fb6b5 100644
--- a/llvm/lib/Target/DirectX/DXILResourceAccess.cpp
+++ b/llvm/lib/Target/DirectX/DXILResourceAccess.cpp
@@ -800,7 +800,8 @@ static SmallVector<IntrinsicInst *> collectUsedHandles(Value *Ptr) {
     else if (auto *II = dyn_cast<IntrinsicInst>(X)) {
       Intrinsic::ID IID = II->getIntrinsicID();
 
-      if (IID == Intrinsic::dx_resource_getpointer)
+      if (IID == Intrinsic::dx_resource_getpointer ||
+          IID == Intrinsic::dx_resource_getbasepointer)
         Worklist.push_back(II->getArgOperand(/*Handle=*/0));
 
       if (llvm::is_contained(HandleIntrins, IID))
@@ -880,12 +881,17 @@ getAccessIndices(Instruction *I, SmallSetVector<Instruction *, 16> &DeadInsts,
       return {nullptr, II->getArgOperand(/*Index=*/3), nullptr};
     }
 
-    if (II->getIntrinsicID() == Intrinsic::dx_resource_getpointer) {
+    Intrinsic::ID IID = II->getIntrinsicID();
+    if (IID == Intrinsic::dx_resource_getpointer ||
+        IID == Intrinsic::dx_resource_getbasepointer) {
       auto *V = dyn_cast<Instruction>(II->getArgOperand(/*Handle=*/0));
       auto AccessIdx = getAccessIndices(V, DeadInsts, VisitedPhis);
       assert(!AccessIdx.hasGetPtrIdx() &&
              "Encountered multiple dx.resource.getpointers in ptr chain?");
-      AccessIdx.GetPtrIdx = II->getArgOperand(1);
+      IRBuilder<> Builder(II);
+      AccessIdx.GetPtrIdx = ConstantInt::get(Builder.getInt32Ty(), 0);
+      if (IID == Intrinsic::dx_resource_getpointer)
+        AccessIdx.GetPtrIdx = II->getArgOperand(1);
 
       DeadInsts.insert(II);
       return AccessIdx;

>From 6d98f7b2646250fdfe7b184b76795f9992640faf Mon Sep 17 00:00:00 2001
From: Sarah Spall <sarahspall at microsoft.com>
Date: Fri, 18 Sep 2026 11:54:58 -0700
Subject: [PATCH 4/6] ai updates

---
 .../lib/Target/DirectX/DXILResourceAccess.cpp | 35 +++++++++++++------
 1 file changed, 25 insertions(+), 10 deletions(-)

diff --git a/llvm/lib/Target/DirectX/DXILResourceAccess.cpp b/llvm/lib/Target/DirectX/DXILResourceAccess.cpp
index 7595d435fb6b5..fd24238d44980 100644
--- a/llvm/lib/Target/DirectX/DXILResourceAccess.cpp
+++ b/llvm/lib/Target/DirectX/DXILResourceAccess.cpp
@@ -613,6 +613,9 @@ struct CBufferRowIntrin {
 };
 } // namespace
 
+static Value *accumulateGEPOffset(GetElementPtrInst *GEP,
+                                  IRBuilder<> &Builder);
+
 static void createCBufferLoad(IntrinsicInst *II, LoadInst *LI,
                               dxil::ResourceTypeInfo &RTI) {
   const DataLayout &DL = LI->getDataLayout();
@@ -667,9 +670,17 @@ static void createCBufferLoad(IntrinsicInst *II, LoadInst *LI,
       // because arrays and structs are always row aligned, and accesses to
       // vector elements will show up as a load of the vector followed by an
       // extractelement.
+      Value *RowOffset = *LastGEP->idx_begin();
+      if (LastGEP->getSourceElementType()->isIntegerTy(8)) {
+        Value *ByteOffset =
+            accumulateGEPOffset(cast<GetElementPtrInst>(LastGEP), Builder);
+        RowOffset = Builder.CreateExactUDiv(
+            ByteOffset, ConstantInt::get(Builder.getInt32Ty(),
+                                         hlsl::CBufferRowSizeInBytes));
+      }
       CurrentRow = cast<ConstantInt>(CurrentRow)->isZero()
-                       ? *LastGEP->idx_begin()
-                       : Builder.CreateAdd(CurrentRow, *LastGEP->idx_begin());
+                       ? RowOffset
+                       : Builder.CreateAdd(CurrentRow, RowOffset);
       CurrentIndex = 0;
     }
   }
@@ -928,6 +939,7 @@ getAccessIndices(Instruction *I, SmallSetVector<Instruction *, 16> &DeadInsts,
         PHINode::Create(Builder.getInt32Ty(), NumEdges));
     std::unique_ptr<PHINode> OffsetPhi(
         PHINode::Create(Builder.getInt32Ty(), NumEdges));
+    bool HasOffset = false;
 
     // Register a ref to this phi for a recursive phi. This is safe to add to
     // the map even if we end up deleting newly created phi below since we can't
@@ -942,9 +954,10 @@ getAccessIndices(Instruction *I, SmallSetVector<Instruction *, 16> &DeadInsts,
       if (AccessIdx.hasGetPtrIdx())
         GetPtrPhi->addIncoming(AccessIdx.GetPtrIdx, BB);
       HandlePhi->addIncoming(AccessIdx.HandleIdx, BB);
-      if (AccessIdx.hasOffsetIdx())
+      if (AccessIdx.hasOffsetIdx()) {
         OffsetPhi->addIncoming(AccessIdx.OffsetIdx, BB);
-      else
+        HasOffset = true;
+      } else
         OffsetPhi->addIncoming(ConstantInt::get(Builder.getInt32Ty(), 0), BB);
     }
 
@@ -966,12 +979,14 @@ getAccessIndices(Instruction *I, SmallSetVector<Instruction *, 16> &DeadInsts,
       Builder.Insert(HandleIdx);
     }
 
-    Value *OffsetIdx;
-    if (Value *ConstantIdx = OffsetPhi->hasConstantValue())
-      OffsetIdx = ConstantIdx;
-    else {
-      OffsetIdx = OffsetPhi.release();
-      Builder.Insert(OffsetIdx);
+    Value *OffsetIdx = nullptr;
+    if (HasOffset) {
+      if (Value *ConstantIdx = OffsetPhi->hasConstantValue())
+        OffsetIdx = ConstantIdx;
+      else {
+        OffsetIdx = OffsetPhi.release();
+        Builder.Insert(OffsetIdx);
+      }
     }
 
     DeadInsts.insert(Phi);

>From 3e2088d055d13d6fef33f5428f4f7d8d5c6bbbb2 Mon Sep 17 00:00:00 2001
From: Sarah Spall <sarahspall at microsoft.com>
Date: Fri, 18 Sep 2026 13:04:20 -0700
Subject: [PATCH 5/6] avoid unncessary gep index multiplication and division
 for cbuffers

---
 .../lib/Target/DirectX/DXILResourceAccess.cpp | 30 +++++++++----------
 1 file changed, 14 insertions(+), 16 deletions(-)

diff --git a/llvm/lib/Target/DirectX/DXILResourceAccess.cpp b/llvm/lib/Target/DirectX/DXILResourceAccess.cpp
index fd24238d44980..7351bc80c19b5 100644
--- a/llvm/lib/Target/DirectX/DXILResourceAccess.cpp
+++ b/llvm/lib/Target/DirectX/DXILResourceAccess.cpp
@@ -613,9 +613,6 @@ struct CBufferRowIntrin {
 };
 } // namespace
 
-static Value *accumulateGEPOffset(GetElementPtrInst *GEP,
-                                  IRBuilder<> &Builder);
-
 static void createCBufferLoad(IntrinsicInst *II, LoadInst *LI,
                               dxil::ResourceTypeInfo &RTI) {
   const DataLayout &DL = LI->getDataLayout();
@@ -654,7 +651,6 @@ static void createCBufferLoad(IntrinsicInst *II, LoadInst *LI,
         DL, Builder, LastGEP->getPointerOperand(), hlsl::CBufferRowSizeInBytes);
     CurrentRow = Builder.CreateAdd(GEPOffset, CurrentRow);
 
-    // todo look here
     APInt ConstantOffset(DL.getIndexTypeSizeInBits(LastGEP->getType()), 0);
     if (LastGEP->accumulateConstantOffset(DL, ConstantOffset)) {
       APInt Remainder(DL.getIndexTypeSizeInBits(LastGEP->getType()),
@@ -670,17 +666,9 @@ static void createCBufferLoad(IntrinsicInst *II, LoadInst *LI,
       // because arrays and structs are always row aligned, and accesses to
       // vector elements will show up as a load of the vector followed by an
       // extractelement.
-      Value *RowOffset = *LastGEP->idx_begin();
-      if (LastGEP->getSourceElementType()->isIntegerTy(8)) {
-        Value *ByteOffset =
-            accumulateGEPOffset(cast<GetElementPtrInst>(LastGEP), Builder);
-        RowOffset = Builder.CreateExactUDiv(
-            ByteOffset, ConstantInt::get(Builder.getInt32Ty(),
-                                         hlsl::CBufferRowSizeInBytes));
-      }
       CurrentRow = cast<ConstantInt>(CurrentRow)->isZero()
-                       ? RowOffset
-                       : Builder.CreateAdd(CurrentRow, RowOffset);
+                       ? *LastGEP->idx_begin()
+                       : Builder.CreateAdd(CurrentRow, *LastGEP->idx_begin());
       CurrentIndex = 0;
     }
   }
@@ -781,6 +769,16 @@ static Instruction *getHandleOperand(Instruction *AI) {
   return nullptr;
 }
 
+static Instruction *getHandleRoot(Instruction *I) {
+  while (auto *GEP = dyn_cast<GetElementPtrInst>(I)) {
+    auto *PointerI = dyn_cast<Instruction>(GEP->getPointerOperand());
+    if (!PointerI)
+      break;
+    I = PointerI;
+  }
+  return I;
+}
+
 static const std::array<Intrinsic::ID, 2> HandleIntrins = {
     Intrinsic::dx_resource_handlefrombinding,
     Intrinsic::dx_resource_handlefromimplicitbinding,
@@ -1060,7 +1058,6 @@ replaceHandleWithIndices(Instruction *Ptr, IntrinsicInst *OldHandle,
     if (AccessIdx.hasOffsetIdx())
       Result =
           Builder.CreateGEP(Builder.getInt8Ty(), GetPtr, AccessIdx.OffsetIdx);
-    // todo is this gep right?
     Ptr->replaceAllUsesWith(Result);
   } else {
     assert(Ptr->getType()->isTargetExtTy() && !AccessIdx.hasGetPtrIdx() &&
@@ -1104,7 +1101,8 @@ static bool legalizeResourceHandles(Function &F, DXILResourceTypeMap &DRTM) {
           continue;
         }
 
-        replaceHandleWithIndices(HandleOp, Handles[0], DeadInsts, VisitedPhis);
+        replaceHandleWithIndices(getHandleRoot(HandleOp), Handles[0], DeadInsts,
+                                 VisitedPhis);
       }
     }
   }

>From dd726a18ccc96d15cf794a3794c17d6a4fdaa606 Mon Sep 17 00:00:00 2001
From: Sarah Spall <sarahspall at microsoft.com>
Date: Fri, 18 Sep 2026 14:45:19 -0700
Subject: [PATCH 6/6] test

---
 .../ResourceAccess/handle-to-index-gep.ll     | 184 ++++++++++++++++++
 1 file changed, 184 insertions(+)
 create mode 100644 llvm/test/CodeGen/DirectX/ResourceAccess/handle-to-index-gep.ll

diff --git a/llvm/test/CodeGen/DirectX/ResourceAccess/handle-to-index-gep.ll b/llvm/test/CodeGen/DirectX/ResourceAccess/handle-to-index-gep.ll
new file mode 100644
index 0000000000000..52a0c875ae3e7
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/ResourceAccess/handle-to-index-gep.ll
@@ -0,0 +1,184 @@
+; RUN: opt -S -dxil-resource-type -dxil-resource-access -mtriple=dxil-pc-shadermodel6.3-library %s | FileCheck %s
+
+%struct.S = type { i32, i32 }
+%__cblayout_CB = type <{ <3 x i32>, target("dx.Padding", 4) }>
+%__cblayout_CB2 = type <{ <{ [9 x <{ <3 x i32>, target("dx.Padding", 4) }>], <3 x i32> }> }>
+
+
+ at OutArr.str = internal unnamed_addr constant [7 x i8] c"OutArr\00"
+ at CBArr.str = internal unnamed_addr constant [6 x i8] c"CBArr\00"
+ at CB.str = private unnamed_addr constant [3 x i8] c"CB\00"
+
+; CHECK-LABEL: define i32 @gep_phi(
+; CHECK-SAME: i1 %[[COND:.*]], i32 %[[A:.*]], i32 %[[B:.*]], i32 %[[X:.*]], i32 %[[Y:.*]])
+define i32 @gep_phi(i1 %cond, i32 %a, i32 %b, i32 %x, i32 %y) {
+entry:
+; CHECK: entry:
+; CHECK-NEXT: %[[X_OFFSET:.*]] = mul i32 %[[X]], 8
+; CHECK-NEXT: %[[X_FIELD:.*]] = add i32 4, %[[X_OFFSET]]
+; CHECK-NEXT: br i1 %[[COND]], label %then, label %merge
+  %handle0 = call target("dx.RawBuffer", %struct.S, 1, 0) @llvm.dx.resource.handlefromimplicitbinding(i32 2, i32 0, i32 -1, i32 0, ptr @OutArr.str)
+  %ptr0 = call ptr @llvm.dx.resource.getpointer(target("dx.RawBuffer", %struct.S, 1, 0) %handle0, i32 %a)
+  %field0 = getelementptr %struct.S, ptr %ptr0, i32 %x, i32 1
+  br i1 %cond, label %then, label %merge
+
+then:
+; CHECK: then:
+; CHECK-NEXT: %[[Y_OFFSET:.*]] = mul i32 %[[Y]], 8
+; CHECK-NEXT: %[[Y_FIELD:.*]] = add i32 4, %[[Y_OFFSET]]
+; CHECK-NEXT: br label %merge
+  %handle1 = call target("dx.RawBuffer", %struct.S, 1, 0) @llvm.dx.resource.handlefromimplicitbinding(i32 2, i32 0, i32 -1, i32 1, ptr @OutArr.str)
+  %ptr1 = call ptr @llvm.dx.resource.getpointer(target("dx.RawBuffer", %struct.S, 1, 0) %handle1, i32 %b)
+  %field1 = getelementptr %struct.S, ptr %ptr1, i32 %y, i32 1
+  br label %merge
+
+merge:
+; CHECK: merge:
+; CHECK-NEXT: %[[ELEMENT:.*]] = phi i32 [ %[[A]], %entry ], [ %[[B]], %then ]
+; CHECK-NEXT: %[[HANDLE_IDX:.*]] = phi i32 [ 0, %entry ], [ 1, %then ]
+; CHECK-NEXT: %[[OFFSET:.*]] = phi i32 [ %[[X_FIELD]], %entry ], [ %[[Y_FIELD]], %then ]
+; CHECK-NEXT: %[[HANDLE:.*]] = call target("dx.RawBuffer", %struct.S, 1, 0) @llvm.dx.resource.handlefromimplicitbinding.tdx.RawBuffer_s_struct.Ss_1_0t(i32 2, i32 0, i32 -1, i32 %[[HANDLE_IDX]], ptr @OutArr.str)
+; CHECK-NEXT: %[[LOAD:.*]] = call { i32, i1 } @llvm.dx.resource.load.rawbuffer.i32.tdx.RawBuffer_s_struct.Ss_1_0t(target("dx.RawBuffer", %struct.S, 1, 0) %[[HANDLE]], i32 %[[ELEMENT]], i32 %[[OFFSET]])
+; CHECK-NEXT: %[[VALUE:.*]] = extractvalue { i32, i1 } %[[LOAD]], 0
+; CHECK-NEXT: ret i32 %[[VALUE]]
+  %ptr = phi ptr [ %field0, %entry ], [ %field1, %then ]
+  %value = load i32, ptr %ptr
+  ret i32 %value
+}
+
+; CHECK-LABEL: define i32 @gep_phi_one_branch(
+; CHECK-SAME: i1 %[[COND:.*]], i32 %[[A:.*]], i32 %[[B:.*]])
+define i32 @gep_phi_one_branch(i1 %cond, i32 %a, i32 %b) {
+entry:
+  %handle0 = call target("dx.RawBuffer", %struct.S, 1, 0) @llvm.dx.resource.handlefromimplicitbinding(i32 2, i32 0, i32 -1, i32 0, ptr @OutArr.str)
+  %ptr0 = call ptr @llvm.dx.resource.getpointer(target("dx.RawBuffer", %struct.S, 1, 0) %handle0, i32 %a)
+; CHECK: br i1 %[[COND]], label %then, label %merge
+  br i1 %cond, label %then, label %merge
+
+then:
+  %handle1 = call target("dx.RawBuffer", %struct.S, 1, 0) @llvm.dx.resource.handlefromimplicitbinding(i32 2, i32 0, i32 -1, i32 1, ptr @OutArr.str)
+  %ptr1 = call ptr @llvm.dx.resource.getpointer(target("dx.RawBuffer", %struct.S, 1, 0) %handle1, i32 %b)
+  %field1 = getelementptr %struct.S, ptr %ptr1, i32 0, i32 1
+  ; CHECK: br label %merge
+  br label %merge
+
+merge:
+; CHECK: merge:
+; CHECK-NEXT: %[[ELEMENT:.*]] = phi i32 [ %[[A]], %entry ], [ %[[B]], %then ]
+; CHECK-NEXT: %[[HANDLE_IDX:.*]] = phi i32 [ 0, %entry ], [ 1, %then ]
+; CHECK-NEXT: %[[OFFSET:.*]] = phi i32 [ 0, %entry ], [ 4, %then ]
+; CHECK-NEXT: %[[HANDLE:.*]] = call target("dx.RawBuffer", %struct.S, 1, 0) @llvm.dx.resource.handlefromimplicitbinding.tdx.RawBuffer_s_struct.Ss_1_0t(i32 2, i32 0, i32 -1, i32 %[[HANDLE_IDX]], ptr @OutArr.str)
+; CHECK-NEXT: %[[LOAD:.*]] = call { i32, i1 } @llvm.dx.resource.load.rawbuffer.i32.tdx.RawBuffer_s_struct.Ss_1_0t(target("dx.RawBuffer", %struct.S, 1, 0) %[[HANDLE]], i32 %[[ELEMENT]], i32 %[[OFFSET]])
+; CHECK-NEXT: %[[VALUE:.*]] = extractvalue { i32, i1 } %[[LOAD]], 0
+; CHECK-NEXT: ret i32 %[[VALUE]]
+  %ptr = phi ptr [ %ptr0, %entry ], [ %field1, %then ]
+  %value = load i32, ptr %ptr
+  ret i32 %value
+}
+
+; CHECK-LABEL: define i32 @gep_select_one_branch(
+; CHECK-SAME: i1 %[[COND:.*]], i32 %[[A:.*]], i32 %[[B:.*]])
+define i32 @gep_select_one_branch(i1 %cond, i32 %a, i32 %b) {
+entry:
+; CHECK: entry:
+; CHECK-NEXT: %[[ELEMENT:.*]] = select i1 %[[COND]], i32 %[[A]], i32 %[[B]]
+; CHECK-NEXT: %[[HANDLE_IDX:.*]] = select i1 %[[COND]], i32 0, i32 1
+; CHECK-NEXT: %[[OFFSET:.*]] = select i1 %[[COND]], i32 0, i32 4
+; CHECK-NEXT: %[[HANDLE:.*]] = call target("dx.RawBuffer", %struct.S, 1, 0) @llvm.dx.resource.handlefromimplicitbinding.tdx.RawBuffer_s_struct.Ss_1_0t(i32 2, i32 0, i32 -1, i32 %[[HANDLE_IDX]], ptr @OutArr.str)
+; CHECK-NEXT: %[[LOAD:.*]] = call { i32, i1 } @llvm.dx.resource.load.rawbuffer.i32.tdx.RawBuffer_s_struct.Ss_1_0t(target("dx.RawBuffer", %struct.S, 1, 0) %[[HANDLE]], i32 %[[ELEMENT]], i32 %[[OFFSET]])
+; CHECK-NEXT: %[[VALUE:.*]] = extractvalue { i32, i1 } %[[LOAD]], 0
+; CHECK-NEXT: ret i32 %[[VALUE]]
+  %handle0 = call target("dx.RawBuffer", %struct.S, 1, 0) @llvm.dx.resource.handlefromimplicitbinding(i32 2, i32 0, i32 -1, i32 0, ptr @OutArr.str)
+  %ptr0 = call ptr @llvm.dx.resource.getpointer(target("dx.RawBuffer", %struct.S, 1, 0) %handle0, i32 %a)
+  %handle1 = call target("dx.RawBuffer", %struct.S, 1, 0) @llvm.dx.resource.handlefromimplicitbinding(i32 2, i32 0, i32 -1, i32 1, ptr @OutArr.str)
+  %ptr1 = call ptr @llvm.dx.resource.getpointer(target("dx.RawBuffer", %struct.S, 1, 0) %handle1, i32 %b)
+  %field1 = getelementptr %struct.S, ptr %ptr1, i32 0, i32 1
+  %ptr = select i1 %cond, ptr %ptr0, ptr %field1
+  %value = load i32, ptr %ptr
+  ret i32 %value
+}
+
+; CHECK-LABEL: define <3 x i32> @basepointer_phi(
+; CHECK-SAME: i1 %[[COND:.*]])
+define <3 x i32> @basepointer_phi(i1 %cond) {
+entry:
+  %handle0 = call target("dx.CBuffer", %__cblayout_CB) @llvm.dx.resource.handlefromimplicitbinding(i32 1, i32 0, i32 2, i32 0, ptr @CBArr.str)
+  %ptr0 = call ptr addrspace(2) @llvm.dx.resource.getbasepointer(target("dx.CBuffer", %__cblayout_CB) %handle0)
+; CHECK: br i1 %[[COND]], label %then, label %merge
+  br i1 %cond, label %then, label %merge
+
+then:
+  %handle1 = call target("dx.CBuffer", %__cblayout_CB) @llvm.dx.resource.handlefromimplicitbinding(i32 1, i32 0, i32 2, i32 1, ptr @CBArr.str)
+  %ptr1 = call ptr addrspace(2) @llvm.dx.resource.getbasepointer(target("dx.CBuffer", %__cblayout_CB) %handle1)
+; CHECK: br label %merge
+  br label %merge
+
+merge:
+; CHECK: merge:
+; CHECK-NEXT: %[[HANDLE_IDX:.*]] = phi i32 [ 0, %entry ], [ 1, %then ]
+; CHECK-NEXT: %[[HANDLE:.*]] = call target("dx.CBuffer", %__cblayout_CB) @llvm.dx.resource.handlefromimplicitbinding.tdx.CBuffer_s___cblayout_CBst(i32 1, i32 0, i32 2, i32 %[[HANDLE_IDX]], ptr @CBArr.str)
+; CHECK-NEXT: %[[LOAD:.*]] = call { i32, i32, i32, i32 } @llvm.dx.resource.load.cbufferrow.4.i32.i32.i32.i32.tdx.CBuffer_s___cblayout_CBst(target("dx.CBuffer", %__cblayout_CB) %[[HANDLE]], i32 1)
+; CHECK-NEXT: %[[X:.*]] = extractvalue { i32, i32, i32, i32 } %[[LOAD]], 0
+; CHECK-NEXT: %[[Y:.*]] = extractvalue { i32, i32, i32, i32 } %[[LOAD]], 1
+; CHECK-NEXT: %[[Z:.*]] = extractvalue { i32, i32, i32, i32 } %[[LOAD]], 2
+; CHECK-NEXT: %[[V0:.*]] = insertelement <3 x i32> poison, i32 %[[X]], i32 0
+; CHECK-NEXT: %[[V1:.*]] = insertelement <3 x i32> %[[V0]], i32 %[[Y]], i32 1
+; CHECK-NEXT: %[[V2:.*]] = insertelement <3 x i32> %[[V1]], i32 %[[Z]], i32 2
+; CHECK-NEXT: ret <3 x i32> %[[V2]]
+  %ptr = phi ptr addrspace(2) [ %ptr0, %entry ], [ %ptr1, %then ]
+  %row1 = getelementptr i8, ptr addrspace(2) %ptr, i32 16
+  %value = load <3 x i32>, ptr addrspace(2) %row1, align 16
+  ret <3 x i32> %value
+}
+
+; CHECK-LABEL: define <3 x i32> @basepointer_phi_variable_row(
+; CHECK-SAME: i1 %[[COND:.*]], i32 %[[ROW:.*]])
+define <3 x i32> @basepointer_phi_variable_row(i1 %cond, i32 %row) {
+entry:
+  %handle0 = call target("dx.CBuffer", %__cblayout_CB) @llvm.dx.resource.handlefromimplicitbinding(i32 1, i32 0, i32 2, i32 0, ptr @CBArr.str)
+  %ptr0 = call ptr addrspace(2) @llvm.dx.resource.getbasepointer(target("dx.CBuffer", %__cblayout_CB) %handle0)
+; CHECK: br i1 %[[COND]], label %then, label %merge
+  br i1 %cond, label %then, label %merge
+
+then:
+  %handle1 = call target("dx.CBuffer", %__cblayout_CB) @llvm.dx.resource.handlefromimplicitbinding(i32 1, i32 0, i32 2, i32 1, ptr @CBArr.str)
+  %ptr1 = call ptr addrspace(2) @llvm.dx.resource.getbasepointer(target("dx.CBuffer", %__cblayout_CB) %handle1)
+; CHECK: br label %merge
+  br label %merge
+
+merge:
+; CHECK: merge:
+; CHECK-NEXT: %[[HANDLE_IDX:.*]] = phi i32 [ 0, %entry ], [ 1, %then ]
+; CHECK-NEXT: %[[HANDLE:.*]] = call target("dx.CBuffer", %__cblayout_CB) @llvm.dx.resource.handlefromimplicitbinding.tdx.CBuffer_s___cblayout_CBst(i32 1, i32 0, i32 2, i32 %[[HANDLE_IDX]], ptr @CBArr.str)
+; CHECK-NEXT: %[[LOAD:.*]] = call { i32, i32, i32, i32 } @llvm.dx.resource.load.cbufferrow.4.i32.i32.i32.i32.tdx.CBuffer_s___cblayout_CBst(target("dx.CBuffer", %__cblayout_CB) %[[HANDLE]], i32 %[[ROW]])
+  %ptr = phi ptr addrspace(2) [ %ptr0, %entry ], [ %ptr1, %then ]
+  %rowptr = getelementptr %__cblayout_CB, ptr addrspace(2) %ptr, i32 %row
+  %value = load <3 x i32>, ptr addrspace(2) %rowptr, align 16
+  ret <3 x i32> %value
+}
+
+; CHECK-LABEL: define void @cb_phi_handle(
+; CHECK-SAME: ptr %[[DST:.*]], i1 %[[COND:.*]], i32 %[[IDX:.*]])
+define void @cb_phi_handle(ptr %dst, i1 %cond, i32 %idx) {
+entry:
+  %h0 = call target("dx.CBuffer", %__cblayout_CB2) @llvm.dx.resource.handlefromimplicitbinding(i32 1, i32 0, i32 2, i32 0, ptr @CB.str)
+  %p0 = call ptr addrspace(2) @llvm.dx.resource.getpointer(target("dx.CBuffer", %__cblayout_CB2) %h0, i32 0)
+  br i1 %cond, label %if.then, label %main
+
+if.then:
+  %h1 = call target("dx.CBuffer", %__cblayout_CB2) @llvm.dx.resource.handlefromimplicitbinding(i32 1, i32 0, i32 2, i32 1, ptr @CB.str)
+  %p1 = call ptr addrspace(2) @llvm.dx.resource.getpointer(target("dx.CBuffer", %__cblayout_CB2) %h1, i32 0)
+  br label %main
+
+main:
+; CHECK: main:
+; CHECK-NEXT: %[[HANDLE_IDX:.*]] = phi i32 [ 0, %entry ], [ 1, %if.then ]
+; CHECK-NEXT: %[[HANDLE:.*]] = call target("dx.CBuffer", %__cblayout_CB2) @llvm.dx.resource.handlefromimplicitbinding.tdx.CBuffer_s___cblayout_CB2st(i32 1, i32 0, i32 2, i32 %[[HANDLE_IDX]], ptr @CB.str)
+; CHECK-NEXT: %[[LOAD:.*]] = call { i32, i32, i32, i32 } @llvm.dx.resource.load.cbufferrow.4.i32.i32.i32.i32.tdx.CBuffer_s___cblayout_CB2st(target("dx.CBuffer", %__cblayout_CB2) %[[HANDLE]], i32 %[[IDX]])
+  %pp = phi ptr addrspace(2) [ %p0, %entry ], [ %p1, %if.then ]
+  %gep = getelementptr <{ <3 x i32>, target("dx.Padding", 4) }>, ptr addrspace(2) %pp, i32 %idx
+  %ld = load <3 x i32>, ptr addrspace(2) %gep, align 16
+  %e = extractelement <3 x i32> %ld, i32 0
+  store i32 %e, ptr %dst, align 4
+  ret void
+}



More information about the llvm-commits mailing list