[llvm] [DirectX] Denote `dx.resource.getpointer` with `IntrInaccessibleMemOnly` and `IntrReadMem` (PR #193593)

Finn Plummer via llvm-commits llvm-commits at lists.llvm.org
Wed Apr 22 13:57:00 PDT 2026


https://github.com/inbelic created https://github.com/llvm/llvm-project/pull/193593

`IntrConvergent` was originally added to `dx.resource.getpointer` to prevent optimization passes (SimplifyCFG, GVN) from sinking the intrinsic out of control flow branches, which would create phi nodes on the returned pointer.

Using `IntrInaccessibleMemOnly` and `IntrReadMem` semantics still prevent passes from merging or sinking identical calls across branches. However, this allows the call to be moved within a single control flow path and allows for further legal optimizations.

Updates relevant tests and adds a new test to demonstrate a now legal potential optimization.

Assisted by: Claude Opus 4.6

>From 977e3e4430810e7f7ca73a70900846a8579208a9 Mon Sep 17 00:00:00 2001
From: Finn Plummer <mail at inbelic.dev>
Date: Wed, 22 Apr 2026 17:58:25 +0000
Subject: [PATCH] [DirectX] Denote `dx.resource.getpointer` with
 `IntrInaccessibleMemOnly` and `IntrReadMem`

`IntrConvergent` was originally added to `dx.resource.getpointer` to
prevent optimization passes (SimplifyCFG, GVN) from sinking the
intrinsic out of control flow branches, which would create phi nodes on
the returned pointer.

Using `IntrInaccessibleMemOnly` and `IntrReadMem` semantics still
prevent passes from merging or sinking identical calls across branches.
However, this allows the call to be moved within a single control flow
path and allows for further legal optimizations.

Updates relevant tests and adds a new test to demonstrate a potential
optimization
---
 llvm/include/llvm/IR/IntrinsicsDirectX.td     |  2 +-
 .../DirectX/getpointer-sink-behavior.ll       | 31 +++++++++++++++++++
 .../Transforms/GVN/no-sink-dxgetpointer.ll    |  5 +--
 .../DirectX/no-sink-dxgetpointer.ll           |  6 ++--
 4 files changed, 38 insertions(+), 6 deletions(-)
 create mode 100644 llvm/test/Transforms/DirectX/getpointer-sink-behavior.ll

diff --git a/llvm/include/llvm/IR/IntrinsicsDirectX.td b/llvm/include/llvm/IR/IntrinsicsDirectX.td
index 13fd0e2dc6255..54e783b0ee337 100644
--- a/llvm/include/llvm/IR/IntrinsicsDirectX.td
+++ b/llvm/include/llvm/IR/IntrinsicsDirectX.td
@@ -38,7 +38,7 @@ def int_dx_resource_handlefromimplicitbinding
 
 def int_dx_resource_getpointer
     : DefaultAttrsIntrinsic<[llvm_anyptr_ty], [llvm_any_ty, llvm_any_ty],
-                            [IntrConvergent, IntrNoMem]>;
+                            [IntrReadMem, IntrInaccessibleMemOnly]>;
 
 def int_dx_resource_nonuniformindex
     : DefaultAttrsIntrinsic<[llvm_i32_ty], [llvm_i32_ty], [IntrNoMem]>;
diff --git a/llvm/test/Transforms/DirectX/getpointer-sink-behavior.ll b/llvm/test/Transforms/DirectX/getpointer-sink-behavior.ll
new file mode 100644
index 0000000000000..d560a2af4e3c5
--- /dev/null
+++ b/llvm/test/Transforms/DirectX/getpointer-sink-behavior.ll
@@ -0,0 +1,31 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -passes=sink -S %s | FileCheck %s
+
+; Verify that dx.resource.getpointer can be sunk into a branch where it is
+; only used, now that it is no longer marked convergent.
+
+define void @can_sink_into_branch(i1 %cond) {
+; CHECK-LABEL: define void @can_sink_into_branch(
+; CHECK-SAME: i1 [[COND:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    br i1 [[COND]], label %[[IF_THEN:.*]], label %[[IF_END:.*]]
+; CHECK:       [[IF_THEN]]:
+; CHECK-NEXT:    [[BUF:%.*]] = call target("dx.RawBuffer", i32, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_i32_1_0t(i32 0, i32 0, i32 1, i32 0, ptr null)
+; CHECK-NEXT:    [[PTR:%.*]] = call noundef nonnull align 4 dereferenceable(4) ptr @llvm.dx.resource.getpointer.p0.tdx.RawBuffer_i32_1_0t.i32(target("dx.RawBuffer", i32, 1, 0) [[BUF]], i32 0)
+; CHECK-NEXT:    store i32 42, ptr [[PTR]], align 4
+; CHECK-NEXT:    br label %[[IF_END]]
+; CHECK:       [[IF_END]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %buf = call target("dx.RawBuffer", i32, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_i32_1_0t(i32 0, i32 0, i32 1, i32 0, ptr null)
+  %ptr = call noundef nonnull align 4 dereferenceable(4) ptr @llvm.dx.resource.getpointer.p0.tdx.RawBuffer_i32_1_0t.i32(target("dx.RawBuffer", i32, 1, 0) %buf, i32 0)
+  br i1 %cond, label %if.then, label %if.end
+
+if.then:
+  store i32 42, ptr %ptr, align 4
+  br label %if.end
+
+if.end:
+  ret void
+}
diff --git a/llvm/test/Transforms/GVN/no-sink-dxgetpointer.ll b/llvm/test/Transforms/GVN/no-sink-dxgetpointer.ll
index 2d5a07562f4a4..eeaead53f893a 100644
--- a/llvm/test/Transforms/GVN/no-sink-dxgetpointer.ll
+++ b/llvm/test/Transforms/GVN/no-sink-dxgetpointer.ll
@@ -1,8 +1,9 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
 ; RUN: opt -passes=gvn -passes=instnamer -S %s | FileCheck %s
 
-; This test ensures that given dx.resource.getpointer is marked convergent, the
-; GVN pass is prevented from sinking these intrinsics.
+; This test ensures that given dx.resource.getpointer reads inaccessible memory,
+; the GVN pass is prevented from sinking these intrinsics out of branches which
+; would create phi nodes on the returned ptr.
 ;
 ; NOTE: The following ir represents case F and G from:
 ; https://godbolt.org/z/cK4xh1P49.
diff --git a/llvm/test/Transforms/SimplifyCFG/DirectX/no-sink-dxgetpointer.ll b/llvm/test/Transforms/SimplifyCFG/DirectX/no-sink-dxgetpointer.ll
index 038b25d765d6f..576acce2b8dd2 100644
--- a/llvm/test/Transforms/SimplifyCFG/DirectX/no-sink-dxgetpointer.ll
+++ b/llvm/test/Transforms/SimplifyCFG/DirectX/no-sink-dxgetpointer.ll
@@ -1,9 +1,9 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
 ; RUN: opt -passes=simplifycfg -passes=instnamer -S %s | FileCheck %s
 
-; This test ensures that given dx.resource.getpointer is marked convergent, the
-; SimplifyCFG pass will be prevented from moving these intrinsics into the
-; branches required for sinking handle retrieve before resource access.
+; This test ensures that given dx.resource.getpointer reads inaccessible memory,
+; the SimplifyCFG pass will be prevented from sinking these intrinsics out of
+; branches which would create phi nodes on the returned ptr.
 ;
 ; NOTE: The following test ir is generated from:
 ; https://godbolt.org/z/1EdGTbscE.



More information about the llvm-commits mailing list