[llvm] [DirectX] Handle llvm.dx.resource.getbasepointer intrinsic in DXILResourceAccess pass (PR #204732)

via llvm-commits llvm-commits at lists.llvm.org
Thu Jun 18 22:59:49 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-backend-directx

Author: Helena Kotas (hekota)

<details>
<summary>Changes</summary>

The `llvm.dx.resource.getbasepointer` intrinsic is emitted for `Constantbuffer<T>` element access and needs to be translated to `llvm.dx.resource.load.cbufferrow` calls in the `DXILResourceAccess` pass.

Fixes #<!-- -->204234

---
Full diff: https://github.com/llvm/llvm-project/pull/204732.diff


3 Files Affected:

- (modified) llvm/lib/Target/DirectX/DXILOpLowering.cpp (+1) 
- (modified) llvm/lib/Target/DirectX/DXILResourceAccess.cpp (+12-3) 
- (added) llvm/test/CodeGen/DirectX/ResourceAccess/load-constant-buffer-t.ll (+189) 


``````````diff
diff --git a/llvm/lib/Target/DirectX/DXILOpLowering.cpp b/llvm/lib/Target/DirectX/DXILOpLowering.cpp
index efac3dd7900c1..abd94023a8092 100644
--- a/llvm/lib/Target/DirectX/DXILOpLowering.cpp
+++ b/llvm/lib/Target/DirectX/DXILOpLowering.cpp
@@ -1157,6 +1157,7 @@ class OpLowerer {
       case Intrinsic::dx_resource_handlefrombinding:
         HasErrors |= lowerHandleFromBinding(F);
         break;
+      case Intrinsic::dx_resource_getbasepointer:
       case Intrinsic::dx_resource_getpointer:
         HasErrors |= lowerGetPointer(F);
         break;
diff --git a/llvm/lib/Target/DirectX/DXILResourceAccess.cpp b/llvm/lib/Target/DirectX/DXILResourceAccess.cpp
index 132f1aa8d0aa4..25d860e615c17 100644
--- a/llvm/lib/Target/DirectX/DXILResourceAccess.cpp
+++ b/llvm/lib/Target/DirectX/DXILResourceAccess.cpp
@@ -57,7 +57,8 @@ static Value *traverseGEPOffsets(const DataLayout &DL, IRBuilder<> &Builder,
 
   while (Ptr) {
     if (auto *II = dyn_cast<IntrinsicInst>(Ptr)) {
-      assert(II->getIntrinsicID() == Intrinsic::dx_resource_getpointer &&
+      assert((II->getIntrinsicID() == Intrinsic::dx_resource_getpointer ||
+              II->getIntrinsicID() == Intrinsic::dx_resource_getbasepointer) &&
              "Resource access through unexpected intrinsic");
       return Offset ? Offset : ConstantInt::get(Builder.getInt32Ty(), 0);
     }
@@ -415,7 +416,10 @@ static void createCBufferLoad(IntrinsicInst *II, LoadInst *LI,
 
   IRBuilder<> Builder(LI);
 
-  ConstantInt *GlobalOffset = dyn_cast<ConstantInt>(II->getOperand(1));
+  ConstantInt *GlobalOffset =
+      II->getIntrinsicID() == Intrinsic::dx_resource_getbasepointer
+          ? ConstantInt::get(Builder.getInt32Ty(), 0)
+          : dyn_cast<ConstantInt>(II->getOperand(1));
   assert(GlobalOffset && "CBuffer getpointer index must be constant");
 
   uint64_t GlobalOffsetVal = GlobalOffset->getZExtValue();
@@ -797,8 +801,13 @@ static bool transformResourcePointers(Function &F, DXILResourceTypeMap &DRTM) {
   for (BasicBlock &BB : make_early_inc_range(F))
     for (Instruction &I : BB)
       if (auto *II = dyn_cast<IntrinsicInst>(&I))
-        if (II->getIntrinsicID() == Intrinsic::dx_resource_getpointer) {
+        if (II->getIntrinsicID() == Intrinsic::dx_resource_getpointer ||
+            II->getIntrinsicID() == Intrinsic::dx_resource_getbasepointer) {
           auto *HandleTy = cast<TargetExtType>(II->getArgOperand(0)->getType());
+          assert(
+              (DRTM[HandleTy].isCBuffer() ||
+               II->getIntrinsicID() != Intrinsic::dx_resource_getbasepointer) &&
+              "dx_resource_getbasepointer should only be used by cbuffers");
           Resources.emplace_back(II, DRTM[HandleTy]);
         }
 
diff --git a/llvm/test/CodeGen/DirectX/ResourceAccess/load-constant-buffer-t.ll b/llvm/test/CodeGen/DirectX/ResourceAccess/load-constant-buffer-t.ll
new file mode 100644
index 0000000000000..b2c6c01298d05
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/ResourceAccess/load-constant-buffer-t.ll
@@ -0,0 +1,189 @@
+; RUN: opt -S -dxil-resource-access %s | FileCheck %s
+
+target triple = "dxil-pc-shadermodel6.6-library"
+
+; struct S {
+;     float3 f3;
+;     int a;
+; };
+
+; struct MyConstants {
+;     float f;
+;     int2 i2;
+;     half3 h3;
+;     double d;
+;     int array[2];
+;     float2x2 m;
+;     S s;
+; };
+
+; ConstantBuffer<MyConstants> CB;
+
+%MyConstants = type <{ float, <2 x i32>, target("dx.Padding", 4), <3 x half>,
+  target("dx.Padding", 2), double, <{ [1 x <{ i32, target("dx.Padding", 12) }>], i32 }>,
+  target("dx.Padding", 12), <{ [1 x <{ <2 x float>, target("dx.Padding", 8) }>], <2 x float> }>,
+  target("dx.Padding", 8), %S }>
+
+%S = type <{ <3 x float>, i32 }>
+
+; CHECK: define void @f
+define void @f(ptr %dst) {
+entry:
+  %CB_handle = call target("dx.CBuffer", %MyConstants) 
+        @llvm.dx.resource.handlefromimplicitbinding.tdx.CBuffer_s_MyConstantsst(i32 0, i32 5, i32 1, i32 0, ptr null)
+  
+; CB.f
+;
+; CHECK: [[CBLOAD0:%.*]] = call { float, float, float, float } 
+; CHECK-SAME: @llvm.dx.resource.load.cbufferrow.4.f32.f32.f32.f32.tdx.CBuffer_s_MyConstantsst(
+; CHECK-SAME: target("dx.CBuffer", %MyConstants) %CB_handle, i32 0)
+; CHECK-NEXT: [[CB_F:%.*]] = extractvalue { float, float, float, float } [[CBLOAD0]], 0
+; CHECK-NEXT: store float [[CB_F]], ptr %dst, align 4
+  %CB_ptr0 = call noundef align 1 dereferenceable(72) ptr addrspace(2)
+      @llvm.dx.resource.getbasepointer.p2.tdx.CBuffer_s_MyConstantsst(target("dx.CBuffer", %MyConstants) %CB_handle)
+  %f_ptr = getelementptr inbounds nuw %MyConstants, ptr addrspace(2) %CB_ptr0, i32 0, i32 0
+  %f = load float, ptr addrspace(2) %f_ptr, align 4
+  store float %f, ptr %dst, align 4
+
+; CB.i2
+;
+; CHECK: [[CBLOAD1:%.*]] = call { i32, i32, i32, i32 }
+; CHECK-SAME: @llvm.dx.resource.load.cbufferrow.4.i32.i32.i32.i32.tdx.CBuffer_s_MyConstantsst(
+; CHECK-SAME: target("dx.CBuffer", %MyConstants) %CB_handle, i32 0)
+; CHECK-NEXT: [[CB_I2_0:%.*]] = extractvalue { i32, i32, i32, i32 } [[CBLOAD1]], 1
+; CHECK-NEXT: [[CB_I2_1:%.*]] = extractvalue { i32, i32, i32, i32 } [[CBLOAD1]], 2
+; CHECK-NEXT: [[VEC0:%.*]] = insertelement <2 x i32> poison, i32 [[CB_I2_0]], i32 0
+; CHECK-NEXT: [[VEC1:%.*]] = insertelement <2 x i32> [[VEC0]], i32 [[CB_I2_1]], i32 1
+; CHECK-NEXT: [[DST_PTR:%.*]] = getelementptr inbounds nuw i8, ptr %dst, i32 8
+; CHECK-NEXT: store <2 x i32> [[VEC1]], ptr [[DST_PTR]], align 4
+  %CB_ptr1 = call noundef align 1 dereferenceable(72) ptr addrspace(2)
+    @llvm.dx.resource.getbasepointer.p2.tdx.CBuffer_s_MyConstantsst(target("dx.CBuffer", %MyConstants) %CB_handle)
+  %i2_ptr = getelementptr inbounds nuw %MyConstants, ptr addrspace(2) %CB_ptr1, i32 0, i32 1
+  %i2 = load <2 x i32>, ptr addrspace(2) %i2_ptr, align 4
+  %dst1 = getelementptr inbounds nuw i8, ptr %dst, i32 8
+  store <2 x i32> %i2, ptr %dst1, align 4
+
+; CB.h3
+;
+; CHECK: [[CBLOAD2:%.*]] = call { half, half, half, half, half, half, half, half }
+; CHECK-SAME: @llvm.dx.resource.load.cbufferrow.8.f16.f16.f16.f16.f16.f16.f16.f16.tdx.CBuffer_s_MyConstantsst(
+; CHECK-SAME: target("dx.CBuffer", %MyConstants) %CB_handle, i32 1)
+; CHECK-NEXT: [[CB_H3_0:%.*]] = extractvalue { half, half, half, half, half, half, half, half } [[CBLOAD2]], 0
+; CHECK-NEXT: [[CB_H3_1:%.*]] = extractvalue { half, half, half, half, half, half, half, half } [[CBLOAD2]], 1
+; CHECK-NEXT: [[CB_H3_2:%.*]] = extractvalue { half, half, half, half, half, half, half, half } [[CBLOAD2]], 2
+; CHECK-NEXT: [[VEC0:%.*]] = insertelement <3 x half> poison, half [[CB_H3_0]], i32 0
+; CHECK-NEXT: [[VEC1:%.*]] = insertelement <3 x half> [[VEC0]], half [[CB_H3_1]], i32 1
+; CHECK-NEXT: [[VEC2:%.*]] = insertelement <3 x half> [[VEC1]], half [[CB_H3_2]], i32 2
+; CHECK-NEXT: [[DST_PTR:%.*]] = getelementptr inbounds nuw i8, ptr %dst, i32 16
+; CHECK-NEXT: store <3 x half> [[VEC2]], ptr [[DST_PTR]], align 2
+  %CB_ptr2 = call noundef align 1 dereferenceable(72) ptr addrspace(2)
+    @llvm.dx.resource.getbasepointer.p2.tdx.CBuffer_s_MyConstantsst(target("dx.CBuffer", %MyConstants) %CB_handle)
+  %h3_ptr = getelementptr inbounds nuw %MyConstants, ptr addrspace(2) %CB_ptr2, i32 0, i32 3
+  %h3 = load <3 x half>, ptr addrspace(2) %h3_ptr, align 2
+  %dst2 = getelementptr inbounds nuw i8, ptr %dst, i32 16
+  store <3 x half> %h3, ptr %dst2, align 2
+
+; CB.d
+;
+; CHECK: [[CBLOAD3:%.*]] = call { double, double }
+; CHECK-SAME: @llvm.dx.resource.load.cbufferrow.2.f64.f64.tdx.CBuffer_s_MyConstantsst(
+; CHECK-SAME: target("dx.CBuffer", %MyConstants) %CB_handle, i32 1)
+; CHECK-NEXT: [[CB_D3:%.*]] = extractvalue { double, double } [[CBLOAD3]], 1
+; CHECK-NEXT: [[DST_PTR:%.*]] = getelementptr inbounds nuw i8, ptr %dst, i32 32
+; CHECK-NEXT: store double [[CB_D3]], ptr [[DST_PTR]], align 8
+  %CB_ptr3 = call noundef align 1 dereferenceable(72) ptr addrspace(2)
+    @llvm.dx.resource.getbasepointer.p2.tdx.CBuffer_s_MyConstantsst(target("dx.CBuffer", %MyConstants) %CB_handle)
+  %d_ptr = getelementptr inbounds nuw %MyConstants, ptr addrspace(2) %CB_ptr3, i32 0, i32 5
+  %d = load double, ptr addrspace(2) %d_ptr, align 8
+  %dst3 = getelementptr inbounds nuw i8, ptr %dst, i32 32
+  store double %d, ptr %dst3, align 8
+
+; CB.array[1]
+; - reusing %CB_ptr3 from previous case
+;
+; CHECK: [[CBLOAD4:%.*]] = call { i32, i32, i32, i32 }
+; CHECK-SAME: @llvm.dx.resource.load.cbufferrow.4.i32.i32.i32.i32.tdx.CBuffer_s_MyConstantsst(
+; CHECK-SAME: target("dx.CBuffer", %MyConstants) %CB_handle, i32 3)
+; CHECK-NEXT: [[ARRAY_1:%.*]] = extractvalue { i32, i32, i32, i32 } [[CBLOAD4]], 0
+; CHECK-NEXT: [[DST_PTR:%.*]] = getelementptr inbounds nuw i8, ptr %dst, i32 40
+; CHECK-NEXT: store i32 [[ARRAY_1]], ptr [[DST_PTR]], align 4
+   %array = getelementptr inbounds nuw %MyConstants, ptr addrspace(2) %CB_ptr3, i32 0, i32 6
+   %arraydecay = getelementptr inbounds [2 x i32], ptr addrspace(2) %array, i32 0, i32 0
+   %array_1_ptr = getelementptr <{ i32, target("dx.Padding", 12) }>, ptr addrspace(2) %arraydecay, i32 1, i32 0
+   %array_1 = load i32, ptr addrspace(2) %array_1_ptr, align 16
+   %dst4 = getelementptr inbounds nuw i8, ptr %dst, i32 40
+   store i32 %array_1, ptr %dst4, align 4
+
+; CB.m
+; - reusing %CB_ptr3 from previous case
+;
+; CHECK: [[CBLOAD5:%.*]] = call { float, float, float, float }
+; CHECK-SAME: @llvm.dx.resource.load.cbufferrow.4.f32.f32.f32.f32.tdx.CBuffer_s_MyConstantsst(
+; CHECK-SAME: target("dx.CBuffer", %MyConstants) %CB_handle, i32 4)
+; CHECK-NEXT: [[M00:%.*]] = extractvalue { float, float, float, float } %m.load, 0
+; CHECK-NEXT: [[M10:%.*]] = extractvalue { float, float, float, float } %m.load, 1
+; CHECK-NEXT: [[M01:%.*]] = extractvalue { float, float, float, float } %m.load, 2
+; CHECK-NEXT: [[M11:%.*]] = extractvalue { float, float, float, float } %m.load, 3
+; CHECK-NEXT: [[MAT0:%.*]] = insertelement <4 x float> poison, float [[M00]], i32 0
+; CHECK-NEXT: [[MAT1:%.*]] = insertelement <4 x float> [[MAT0]], float [[M10]], i32 1
+; CHECK-NEXT: [[MAT2:%.*]] = insertelement <4 x float> [[MAT1]], float [[M01]], i32 2
+; CHECK-NEXT: [[MAT3:%.*]] = insertelement <4 x float> [[MAT2]], float [[M11]], i32 3
+; CHECK-NEXT: [[DST_PTR:%.*]] = getelementptr inbounds nuw i8, ptr %dst, i32 56
+; CHECK-NEXT: store <4 x float> [[MAT3]], ptr [[DST_PTR]], align 4
+   %m_ptr = getelementptr inbounds nuw %MyConstants, ptr addrspace(2) %CB_ptr3, i32 0, i32 8
+   %m = load <4 x float>, ptr addrspace(2) %m_ptr, align 4
+   %dst5 = getelementptr inbounds nuw i8, ptr %dst, i32 56
+   store <4 x float> %m, ptr %dst5, align 4
+
+; CB.s
+; - reusing %CB_ptr3 from previous case
+
+; CHECK-NEXT: [[DST_PTR1:%.*]] = getelementptr inbounds nuw i8, ptr %dst, i32 72
+; CHECK-NEXT: [[CBLOAD6:%.*]] = call { float, float, float, float }
+; CHECK-SAME: @llvm.dx.resource.load.cbufferrow.4.f32.f32.f32.f32.tdx.CBuffer_s_MyConstantsst
+; CHECK-SAME: target("dx.CBuffer", %MyConstants) %CB_handle, i32 5)
+; CHECK-NEXT: [[F3_0:%.*]] = extractvalue { float, float, float, float } [[CBLOAD6]], 0
+; CHECK-NEXT: [[F3_1:%.*]] = extractvalue { float, float, float, float } [[CBLOAD6]], 1
+; CHECK-NEXT: [[F3_2:%.*]] = extractvalue { float, float, float, float } [[CBLOAD6]], 2
+; CHECK-NEXT: [[VEC0:%.*]] = insertelement <3 x float> poison, float [[F3_0]], i32 0
+; CHECK-NEXT: [[VEC1:%.*]] = insertelement <3 x float> [[VEC0]], float [[F3_1]], i32 1
+; CHECK-NEXT: [[VEC2:%.*]] = insertelement <3 x float> [[VEC1]], float [[F3_2]], i32 2
+; CHECK-NEXT: store <3 x float> [[VEC2]], ptr [[DST_PTR1]], align 4
+; CHECK-NEXT: [[DST_PTR2:%.*]] = getelementptr inbounds nuw i8, ptr %dst, i32 84
+; CHECK-NEXT: [[CBLOAD7:%.*]] = call { i32, i32, i32, i32 }
+; CHECK-SAME: @llvm.dx.resource.load.cbufferrow.4.i32.i32.i32.i32.tdx.CBuffer_s_MyConstantsst
+; CHECK-SAME: target("dx.CBuffer", %MyConstants) %CB_handle, i32 5)
+; CHECK-NEXT: [[A:%.*]] = extractvalue { i32, i32, i32, i32 } [[CBLOAD7]], 3
+; CHECK-NEXT: store i32 [[A]], ptr [[DST_PTR2]], align 4
+   %CB_ptr4 = call noundef align 1 dereferenceable(72) ptr addrspace(2)
+      @llvm.dx.resource.getbasepointer.p2.tdx.CBuffer_s_MyConstantsst(target("dx.CBuffer", %MyConstants) %CB_handle)
+   %s_ptr = getelementptr inbounds nuw %MyConstants, ptr addrspace(2) %CB_ptr4, i32 0, i32 9
+   %s_f3_ptr = getelementptr inbounds %S, ptr addrspace(2) %s_ptr, i32 0, i32 0
+   %dst6 = getelementptr inbounds nuw i8, ptr %dst, i32 72
+   %s_f3  = load <3 x float>, ptr addrspace(2) %s_f3_ptr, align 4
+   store <3 x float> %s_f3, ptr %dst6, align 4
+   %s_a_ptr = getelementptr inbounds %S, ptr addrspace(2) %s_ptr, i32 0, i32 1
+   %dst7 = getelementptr inbounds nuw i8, ptr %dst, i32 84
+   %s_a = load i32, ptr addrspace(2) %s_a_ptr, align 4
+   store i32 %s_a, ptr %dst7, align 4
+
+; - CB.s.a
+;
+; CHECK: [[CBLOAD8:%.*]] = call { i32, i32, i32, i32 }
+; CHECK-SAME: @llvm.dx.resource.load.cbufferrow.4.i32.i32.i32.i32.tdx.CBuffer_s_MyConstantsst(
+; CHECK-SAME: target("dx.CBuffer", %MyConstants) %CB_handle, i32 5)
+; CHECK-NEXT: [[S_A:%.*]] = extractvalue { i32, i32, i32, i32 } [[CBLOAD8]], 3
+; CHECK-NEXT: [[DST_PTR:%.*]] = getelementptr inbounds nuw i8, ptr %dst, i32 88
+; CHECK-NEXT: store i32 [[S_A]], ptr [[DST_PTR]], align 4
+  %CB_ptr5 = call noundef align 1 dereferenceable(72) ptr addrspace(2)
+      @llvm.dx.resource.getbasepointer.p2.tdx.CBuffer_s_MyConstantsst(target("dx.CBuffer", %MyConstants) %CB_handle)
+  %s1_ptr = getelementptr inbounds nuw %MyConstants, ptr addrspace(2) %CB_ptr5, i32 0, i32 9
+  %s1_a_ptr = getelementptr inbounds nuw %S, ptr addrspace(2) %s_ptr, i32 0, i32 1
+  %s1_a = load i32, ptr addrspace(2) %s1_a_ptr, align 4
+  %dst8 = getelementptr inbounds nuw i8, ptr %dst, i32 88
+  store i32 %s1_a, ptr %dst8, align 4
+
+  ret void
+}
+
+; CHECK-NOT: call {{.*}} @llvm.dx.resource.getbasepointer

``````````

</details>


https://github.com/llvm/llvm-project/pull/204732


More information about the llvm-commits mailing list