Author: Deric C.
Date: 2026-09-11T13:45:50-07:00
New Revision: 5c27600b852b9a86b7811142482a09defd963ad3

URL: 
https://github.com/llvm/llvm-project/commit/5c27600b852b9a86b7811142482a09defd963ad3
DIFF: 
https://github.com/llvm/llvm-project/commit/5c27600b852b9a86b7811142482a09defd963ad3.diff

LOG: [DirectX] Add texture support to atomicrmw lowering (#221369)

Fixes https://github.com/llvm/llvm-project/issues/218034
Fixes https://github.com/llvm/llvm-project/issues/186154

atomicrmw lowering didn't support textures. This PR adds that missing
support.

Assisted by: Claude Opus 5

Added: 
    clang/test/CodeGenHLSL/builtins/RWTexture-Interlocked.hlsl
    llvm/test/CodeGen/DirectX/ResourceAtomicBinOp-texture-nonscalar.ll
    llvm/test/CodeGen/DirectX/ResourceAtomicBinOp-texture-unsupported-kind.ll
    llvm/test/CodeGen/DirectX/ResourceAtomicBinOp-texture.ll

Modified: 
    llvm/include/llvm/IR/IntrinsicsDirectX.td
    llvm/lib/Target/DirectX/DXILOpLowering.cpp
    llvm/lib/Target/DirectX/DXILResourceAccess.cpp
    llvm/test/CodeGen/DirectX/ResourceAtomicBinOp.ll
    llvm/test/CodeGen/DirectX/ShaderFlags/atomic64-typed-resource.ll

Removed: 
    


################################################################################
diff  --git a/clang/test/CodeGenHLSL/builtins/RWTexture-Interlocked.hlsl 
b/clang/test/CodeGenHLSL/builtins/RWTexture-Interlocked.hlsl
new file mode 100644
index 0000000000000..8e2e7c9e57165
--- /dev/null
+++ b/clang/test/CodeGenHLSL/builtins/RWTexture-Interlocked.hlsl
@@ -0,0 +1,52 @@
+// RUN: %clang_cc1 -std=hlsl202x -finclude-default-header -triple \
+// RUN:   dxil-pc-shadermodel6.6-compute %s -emit-llvm -o - | \
+// RUN:   FileCheck %s --check-prefixes=CHECK,DXCHECK
+
+// RUN: %clang_cc1 -std=hlsl202x -finclude-default-header -triple \
+// RUN:   spirv-pc-vulkan1.3-compute %s -emit-llvm -o - | \
+// RUN:   FileCheck %s --check-prefixes=CHECK,SPVCHECK
+
+// Regression coverage for free-function interlocked operations on a texture
+// subscript (RWTexture2D<T>[i], RWTexture2DArray<T>[i]). This is the texture
+// counterpart of RWBuffer-Interlocked.hlsl: the atomicrmw has to be emitted on
+// the pointer returned by resource.getpointer with the whole coordinate 
vector,
+// since that is what DXILResourceAccess splits into the coordinate operands of
+// the DXIL AtomicBinOp op. InterlockedMin is called once on a signed texture
+// and once on an unsigned one so that the signed/unsigned atomicrmw selection
+// is pinned to an exactly-named resource handle type (spirv.SignedImage vs
+// spirv.Image). Add new intrinsics here as more InterlockedX operations gain
+// resource support.
+
+RWTexture2D<int> Out : register(u0);
+RWTexture2DArray<uint> UOut : register(u1);
+
+// CHECK-LABEL: define void @main
+// DXCHECK:  %[[PTR1:.*]] = call {{.*}} 
@llvm.dx.resource.getpointer.{{.*}}(target("dx.Texture", i32, 1, 0, 1, 2) 
%{{.*}}, <2 x i32> %{{.*}})
+// DXCHECK:  atomicrmw add ptr %[[PTR1]], i32 1 syncscope("device") monotonic
+// DXCHECK:  %[[PTR2:.*]] = call {{.*}} 
@llvm.dx.resource.getpointer.{{.*}}(target("dx.Texture", i32, 1, 0, 1, 2) 
%{{.*}}, <2 x i32> %{{.*}})
+// DXCHECK:  atomicrmw min ptr %[[PTR2]], i32 1 syncscope("device") monotonic
+// DXCHECK:  %[[PTR3:.*]] = call {{.*}} 
@llvm.dx.resource.getpointer.{{.*}}(target("dx.Texture", i32, 1, 0, 0, 7) 
%{{.*}}, <3 x i32> %{{.*}})
+// DXCHECK:  atomicrmw or ptr %[[PTR3]], i32 1 syncscope("device") monotonic
+// DXCHECK:  %[[PTR4:.*]] = call {{.*}} 
@llvm.dx.resource.getpointer.{{.*}}(target("dx.Texture", i32, 1, 0, 0, 7) 
%{{.*}}, <3 x i32> %{{.*}})
+// DXCHECK:  atomicrmw xor ptr %[[PTR4]], i32 1 syncscope("device") monotonic
+// DXCHECK:  %[[PTR5:.*]] = call {{.*}} 
@llvm.dx.resource.getpointer.{{.*}}(target("dx.Texture", i32, 1, 0, 0, 7) 
%{{.*}}, <3 x i32> %{{.*}})
+// DXCHECK:  atomicrmw umin ptr %[[PTR5]], i32 1 syncscope("device") monotonic
+// SPVCHECK: %[[PTR1:.*]] = call {{.*}} 
@llvm.spv.resource.getpointer.{{.*}}(target("spirv.SignedImage", i32, {{.*}}) 
%{{.*}}, <2 x i32> %{{.*}})
+// SPVCHECK: atomicrmw add ptr addrspace(11) %[[PTR1]], i32 1 
syncscope("device") monotonic
+// SPVCHECK: %[[PTR2:.*]] = call {{.*}} 
@llvm.spv.resource.getpointer.{{.*}}(target("spirv.SignedImage", i32, {{.*}}) 
%{{.*}}, <2 x i32> %{{.*}})
+// SPVCHECK: atomicrmw min ptr addrspace(11) %[[PTR2]], i32 1 
syncscope("device") monotonic
+// SPVCHECK: %[[PTR3:.*]] = call {{.*}} 
@llvm.spv.resource.getpointer.{{.*}}(target("spirv.Image", i32, {{.*}}) 
%{{.*}}, <3 x i32> %{{.*}})
+// SPVCHECK: atomicrmw or ptr addrspace(11) %[[PTR3]], i32 1 
syncscope("device") monotonic
+// SPVCHECK: %[[PTR4:.*]] = call {{.*}} 
@llvm.spv.resource.getpointer.{{.*}}(target("spirv.Image", i32, {{.*}}) 
%{{.*}}, <3 x i32> %{{.*}})
+// SPVCHECK: atomicrmw xor ptr addrspace(11) %[[PTR4]], i32 1 
syncscope("device") monotonic
+// SPVCHECK: %[[PTR5:.*]] = call {{.*}} 
@llvm.spv.resource.getpointer.{{.*}}(target("spirv.Image", i32, {{.*}}) 
%{{.*}}, <3 x i32> %{{.*}})
+// SPVCHECK: atomicrmw umin ptr addrspace(11) %[[PTR5]], i32 1 
syncscope("device") monotonic
+[shader("compute")]
+[numthreads(1,1,1)]
+void main(uint3 id : SV_DispatchThreadID) {
+  InterlockedAdd(Out[id.xy], 1);
+  InterlockedMin(Out[id.xy], 1);
+  InterlockedOr(UOut[id], 1u);
+  InterlockedXor(UOut[id], 1u);
+  InterlockedMin(UOut[id], 1u);
+}

diff  --git a/llvm/include/llvm/IR/IntrinsicsDirectX.td 
b/llvm/include/llvm/IR/IntrinsicsDirectX.td
index a8927f83ee2f8..f90340c379386 100644
--- a/llvm/include/llvm/IR/IntrinsicsDirectX.td
+++ b/llvm/include/llvm/IR/IntrinsicsDirectX.td
@@ -71,12 +71,14 @@ def int_dx_resource_store_rawbuffer
 // Atomic read-modify-write on a UAV resource element. Emitted by
 // DXILResourceAccess from an `atomicrmw` on a `dx.resource.getpointer` result,
 // and lowered by DXILOpLowering to the DXIL `AtomicBinOp` op (78).
-// Args: handle, AtomicBinOpCode, coord0 (index), coord1 (offset or poison),
-//       new value. Returns the original value.
+// Args: handle, AtomicBinOpCode, coord0, coord1, coord2, new value. Returns
+//       the original value. Buffer accesses use coord0 as the element index 
and
+//       coord1 as the byte offset into a struct, while texture accesses use 
one
+//       coordinate per texture dimension. Unused coordinates are poison.
 def int_dx_resource_atomic_binop
     : DefaultAttrsIntrinsic<
           [llvm_any_ty],
-          [llvm_any_ty, llvm_i32_ty, llvm_i32_ty, llvm_i32_ty,
+          [llvm_any_ty, llvm_i32_ty, llvm_i32_ty, llvm_i32_ty, llvm_i32_ty,
            LLVMMatchType<0>],
           [IntrArgMemOnly]>;
 // dx.resource.load.cbufferrow encodes the number of elements returned in the

diff  --git a/llvm/lib/Target/DirectX/DXILOpLowering.cpp 
b/llvm/lib/Target/DirectX/DXILOpLowering.cpp
index 20c2f93a17c10..a98c115bcd62e 100644
--- a/llvm/lib/Target/DirectX/DXILOpLowering.cpp
+++ b/llvm/lib/Target/DirectX/DXILOpLowering.cpp
@@ -1120,11 +1120,11 @@ class OpLowerer {
       Value *BinOp = CI->getArgOperand(1);
       Value *Coord0 = CI->getArgOperand(2);
       Value *Coord1 = CI->getArgOperand(3);
-      Value *NewValue = CI->getArgOperand(4);
+      Value *Coord2 = CI->getArgOperand(4);
+      Value *NewValue = CI->getArgOperand(5);
 
-      std::array<Value *, 6> Args{
-          Handle,  BinOp, Coord0, Coord1, ConstantInt::get(IRB.getInt32Ty(), 
0),
-          NewValue};
+      std::array<Value *, 6> Args{Handle, BinOp,  Coord0,
+                                  Coord1, Coord2, NewValue};
       Expected<CallInst *> OpCall = OpBuilder.tryCreateOp(
           dxil::OpCode::AtomicBinOp, Args, CI->getName(), CI->getType());
       if (Error E = OpCall.takeError()) {

diff  --git a/llvm/lib/Target/DirectX/DXILResourceAccess.cpp 
b/llvm/lib/Target/DirectX/DXILResourceAccess.cpp
index 8c4d2b6c14042..5b5b86400726a 100644
--- a/llvm/lib/Target/DirectX/DXILResourceAccess.cpp
+++ b/llvm/lib/Target/DirectX/DXILResourceAccess.cpp
@@ -330,8 +330,11 @@ getAtomicBinOpCode(AtomicRMWInst::BinOp BinOp) {
   llvm_unreachable("Unhandled atomicrmw operation");
 }
 
-static void createAtomicBinOp(IntrinsicInst *II, AtomicRMWInst *AI,
-                              dxil::ResourceTypeInfo &RTI) {
+static void emitAtomicBinOp(IRBuilder<> &Builder, AtomicRMWInst *AI,
+                            Value *Handle, ArrayRef<Value *> Coords) {
+  assert(!Coords.empty() && Coords.size() <= 3 &&
+         "Atomic operations take between one and three coordinates");
+
   std::optional<dxil::AtomicBinOpCode> BinOpCode =
       getAtomicBinOpCode(AI->getOperation());
   if (!BinOpCode) {
@@ -339,6 +342,23 @@ static void createAtomicBinOp(IntrinsicInst *II, 
AtomicRMWInst *AI,
     return;
   }
 
+  SmallVector<Value *, 6> Args{
+      Handle, Builder.getInt32(static_cast<uint32_t>(*BinOpCode))};
+  append_range(Args, Coords);
+  Args.append(3 - Coords.size(), PoisonValue::get(Builder.getInt32Ty()));
+  Args.push_back(AI->getValOperand());
+
+  // Emit the target-independent intrinsic; DXILOpLowering lowers it to the
+  // DXIL `AtomicBinOp` op and handles the target-ext-typed handle cast via
+  // its `createTmpHandleCast` bookkeeping.
+  Value *Result = Builder.CreateIntrinsic(
+      AI->getType(), Intrinsic::dx_resource_atomic_binop, Args);
+
+  AI->replaceAllUsesWith(Result);
+}
+
+static void createBufferAtomicBinOp(IntrinsicInst *II, AtomicRMWInst *AI,
+                                    dxil::ResourceTypeInfo &RTI) {
   const DataLayout &DL = AI->getDataLayout();
   IRBuilder<> Builder(AI);
   Value *Index = II->getOperand(1);
@@ -349,25 +369,45 @@ static void createAtomicBinOp(IntrinsicInst *II, 
AtomicRMWInst *AI,
       traverseGEPOffsets(DL, Builder, AI->getPointerOperand(), AccessSize);
 
   // For non-struct buffers (RawBuffer or TypedBuffer), fold the byte offset
-  // into the index and mark the coord1 arg as poison — only StructuredBuffer
+  // into the index and only pass a single coordinate — only StructuredBuffer
   // atomics use both a struct index and a byte offset.
   if (!RTI.isStruct()) {
     auto *ConstantOffset = dyn_cast<ConstantInt>(Offset);
     if (!ConstantOffset || !ConstantOffset->isZero())
       Index = Builder.CreateAdd(Index, Offset);
-    Offset = llvm::PoisonValue::get(Builder.getInt32Ty());
+
+    emitAtomicBinOp(Builder, AI, II->getOperand(0), {Index});
+    return;
   }
 
-  Value *BinOp = Builder.getInt32(static_cast<uint32_t>(*BinOpCode));
+  emitAtomicBinOp(Builder, AI, II->getOperand(0), {Index, Offset});
+}
 
-  // Emit the target-independent intrinsic; DXILOpLowering lowers it to the
-  // DXIL `AtomicBinOp` op and handles the target-ext-typed handle cast via
-  // its `createTmpHandleCast` bookkeeping.
-  Value *Result = Builder.CreateIntrinsic(
-      AI->getType(), Intrinsic::dx_resource_atomic_binop,
-      {II->getOperand(0), BinOp, Index, Offset, AI->getValOperand()});
+static void createTextureAtomicBinOp(IntrinsicInst *II, AtomicRMWInst *AI,
+                                     dxil::ResourceTypeInfo &RTI) {
+  Type *ContainedType = RTI.getHandleTy()->getTypeParameter(0);
+  if (!ContainedType->isIntegerTy()) {
+    reportFatalUsageError("DXIL atomicrmw requires a texture resource with a "
+                          "scalar integer element type");
+    return;
+  }
 
-  AI->replaceAllUsesWith(Result);
+  IRBuilder<> Builder(AI);
+
+  // The coordinates of a texture access are a scalar or a vector with one
+  // element per texture dimension, including the array slice if there is one.
+  // These map directly onto the coordinate operands of the atomic op.
+  Value *Coords = II->getOperand(1);
+  SmallVector<Value *, 3> CoordArgs;
+  if (auto *VecTy = dyn_cast<FixedVectorType>(Coords->getType())) {
+    assert(VecTy->getNumElements() <= 3 && "Too many texture coordinates");
+    for (unsigned I = 0, E = VecTy->getNumElements(); I != E; ++I)
+      CoordArgs.push_back(Builder.CreateExtractElement(Coords, I));
+  } else {
+    CoordArgs.push_back(Coords);
+  }
+
+  emitAtomicBinOp(Builder, AI, II->getOperand(0), CoordArgs);
 }
 
 static void createAtomicBinOpIntrinsic(IntrinsicInst *II, AtomicRMWInst *AI,
@@ -376,20 +416,21 @@ static void createAtomicBinOpIntrinsic(IntrinsicInst *II, 
AtomicRMWInst *AI,
   case dxil::ResourceKind::TypedBuffer:
   case dxil::ResourceKind::RawBuffer:
   case dxil::ResourceKind::StructuredBuffer:
-    return createAtomicBinOp(II, AI, RTI);
+    return createBufferAtomicBinOp(II, AI, RTI);
   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:
+    return createTextureAtomicBinOp(II, AI, RTI);
+  case dxil::ResourceKind::Texture2DMS:
   case dxil::ResourceKind::Texture2DMSArray:
+  case dxil::ResourceKind::TextureCube:
   case dxil::ResourceKind::TextureCubeArray:
   case dxil::ResourceKind::FeedbackTexture2D:
   case dxil::ResourceKind::FeedbackTexture2DArray:
     reportFatalUsageError(
-        "DXIL atomicrmw not implemented for texture resources");
+        "DXIL atomicrmw not implemented for this texture resource kind");
     return;
   case dxil::ResourceKind::CBuffer:
   case dxil::ResourceKind::Sampler:

diff  --git 
a/llvm/test/CodeGen/DirectX/ResourceAtomicBinOp-texture-nonscalar.ll 
b/llvm/test/CodeGen/DirectX/ResourceAtomicBinOp-texture-nonscalar.ll
new file mode 100644
index 0000000000000..2abc27da6ffbd
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/ResourceAtomicBinOp-texture-nonscalar.ll
@@ -0,0 +1,64 @@
+; RUN: split-file %s %t
+; RUN: not opt -S -dxil-resource-access -dxil-op-lower %t/texture1d-int2.ll 
2>&1 | FileCheck %t/texture1d-int2.ll
+; RUN: not opt -S -dxil-resource-access -dxil-op-lower %t/texture2d-int4.ll 
2>&1 | FileCheck %t/texture2d-int4.ll
+; RUN: not opt -S -dxil-resource-access -dxil-op-lower 
%t/texture2darray-i64x2.ll 2>&1 | FileCheck %t/texture2darray-i64x2.ll
+; RUN: not opt -S -dxil-resource-access -dxil-op-lower %t/texture3d-float4.ll 
2>&1 | FileCheck %t/texture3d-float4.ll
+
+; A texture atomic operates on a whole texel, so there is no way to address a
+; single component of a multi-component texel.
+
+;--- texture1d-int2.ll
+
+target triple = "dxil-pc-shadermodel6.6-compute"
+
+; CHECK: DXIL atomicrmw requires a texture resource with a scalar integer 
element type
+define i32 @atomic_texture1d_int2(i32 %coord, i32 %value) {
+  %texture = call target("dx.Texture", <2 x i32>, 1, 0, 0, 1)
+      @llvm.dx.resource.handlefrombinding(i32 0, i32 0, i32 1, i32 0, ptr null)
+  %ptr = call ptr @llvm.dx.resource.getpointer(
+      target("dx.Texture", <2 x i32>, 1, 0, 0, 1) %texture, i32 %coord)
+  %old = atomicrmw add ptr %ptr, i32 %value monotonic
+  ret i32 %old
+}
+
+;--- texture2d-int4.ll
+
+target triple = "dxil-pc-shadermodel6.6-compute"
+
+; CHECK: DXIL atomicrmw requires a texture resource with a scalar integer 
element type
+define i32 @atomic_texture2d_int4(<2 x i32> %coords, i32 %value) {
+  %texture = call target("dx.Texture", <4 x i32>, 1, 0, 0, 2)
+      @llvm.dx.resource.handlefrombinding(i32 0, i32 0, i32 1, i32 0, ptr null)
+  %ptr = call ptr @llvm.dx.resource.getpointer(
+      target("dx.Texture", <4 x i32>, 1, 0, 0, 2) %texture, <2 x i32> %coords)
+  %old = atomicrmw add ptr %ptr, i32 %value monotonic
+  ret i32 %old
+}
+
+;--- texture2darray-i64x2.ll
+
+target triple = "dxil-pc-shadermodel6.6-compute"
+
+; CHECK: DXIL atomicrmw requires a texture resource with a scalar integer 
element type
+define i64 @atomic_texture2darray_i64x2(<3 x i32> %coords, i64 %value) {
+  %texture = call target("dx.Texture", <2 x i64>, 1, 0, 0, 7)
+      @llvm.dx.resource.handlefrombinding(i32 0, i32 0, i32 1, i32 0, ptr null)
+  %ptr = call ptr @llvm.dx.resource.getpointer(
+      target("dx.Texture", <2 x i64>, 1, 0, 0, 7) %texture, <3 x i32> %coords)
+  %old = atomicrmw max ptr %ptr, i64 %value monotonic
+  ret i64 %old
+}
+
+;--- texture3d-float4.ll
+
+target triple = "dxil-pc-shadermodel6.6-compute"
+
+; CHECK: DXIL atomicrmw requires a texture resource with a scalar integer 
element type
+define i32 @atomic_texture3d_float4(<3 x i32> %coords, i32 %value) {
+  %texture = call target("dx.Texture", <4 x float>, 1, 0, 0, 4)
+      @llvm.dx.resource.handlefrombinding(i32 0, i32 0, i32 1, i32 0, ptr null)
+  %ptr = call ptr @llvm.dx.resource.getpointer(
+      target("dx.Texture", <4 x float>, 1, 0, 0, 4) %texture, <3 x i32> 
%coords)
+  %old = atomicrmw xchg ptr %ptr, i32 %value monotonic
+  ret i32 %old
+}

diff  --git 
a/llvm/test/CodeGen/DirectX/ResourceAtomicBinOp-texture-unsupported-kind.ll 
b/llvm/test/CodeGen/DirectX/ResourceAtomicBinOp-texture-unsupported-kind.ll
new file mode 100644
index 0000000000000..63761098d4207
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/ResourceAtomicBinOp-texture-unsupported-kind.ll
@@ -0,0 +1,64 @@
+; RUN: split-file %s %t
+; RUN: not opt -S -dxil-resource-access -dxil-op-lower %t/texture2dms.ll 2>&1 
| FileCheck %t/texture2dms.ll
+; RUN: not opt -S -dxil-resource-access -dxil-op-lower %t/texture2dmsarray.ll 
2>&1 | FileCheck %t/texture2dmsarray.ll
+; RUN: not opt -S -dxil-resource-access -dxil-op-lower %t/texturecube.ll 2>&1 
| FileCheck %t/texturecube.ll
+; RUN: not opt -S -dxil-resource-access -dxil-op-lower %t/texturecubearray.ll 
2>&1 | FileCheck %t/texturecubearray.ll
+
+; The DXIL AtomicBinOp op only supports 1D, 2D, 3D and array textures, so
+; multisampled and cube textures must be rejected.
+
+;--- texture2dms.ll
+
+target triple = "dxil-pc-shadermodel6.6-compute"
+
+; CHECK: DXIL atomicrmw not implemented for this texture resource kind
+define i32 @atomic_texture2dms(<2 x i32> %coords, i32 %value) {
+  %texture = call target("dx.MSTexture", i32, 1, 4, 0, 3)
+      @llvm.dx.resource.handlefrombinding(i32 0, i32 0, i32 1, i32 0, ptr null)
+  %ptr = call ptr @llvm.dx.resource.getpointer(
+      target("dx.MSTexture", i32, 1, 4, 0, 3) %texture, <2 x i32> %coords)
+  %old = atomicrmw add ptr %ptr, i32 %value monotonic
+  ret i32 %old
+}
+
+;--- texture2dmsarray.ll
+
+target triple = "dxil-pc-shadermodel6.6-compute"
+
+; CHECK: DXIL atomicrmw not implemented for this texture resource kind
+define i32 @atomic_texture2dmsarray(<3 x i32> %coords, i32 %value) {
+  %texture = call target("dx.MSTexture", i32, 1, 4, 0, 8)
+      @llvm.dx.resource.handlefrombinding(i32 0, i32 0, i32 1, i32 0, ptr null)
+  %ptr = call ptr @llvm.dx.resource.getpointer(
+      target("dx.MSTexture", i32, 1, 4, 0, 8) %texture, <3 x i32> %coords)
+  %old = atomicrmw add ptr %ptr, i32 %value monotonic
+  ret i32 %old
+}
+
+;--- texturecube.ll
+
+target triple = "dxil-pc-shadermodel6.6-compute"
+
+; CHECK: DXIL atomicrmw not implemented for this texture resource kind
+define i32 @atomic_texturecube(<3 x i32> %coords, i32 %value) {
+  %texture = call target("dx.Texture", i32, 1, 0, 0, 5)
+      @llvm.dx.resource.handlefrombinding(i32 0, i32 0, i32 1, i32 0, ptr null)
+  %ptr = call ptr @llvm.dx.resource.getpointer(
+      target("dx.Texture", i32, 1, 0, 0, 5) %texture, <3 x i32> %coords)
+  %old = atomicrmw umax ptr %ptr, i32 %value monotonic
+  ret i32 %old
+}
+
+;--- texturecubearray.ll
+
+target triple = "dxil-pc-shadermodel6.6-compute"
+
+; CHECK: DXIL atomicrmw not implemented for this texture resource kind
+define i32 @atomic_texturecubearray(<4 x i32> %coords, i32 %value) {
+  %texture = call target("dx.Texture", i32, 1, 0, 0, 9)
+      @llvm.dx.resource.handlefrombinding(i32 0, i32 0, i32 1, i32 0, ptr null)
+  %ptr = call ptr @llvm.dx.resource.getpointer(
+      target("dx.Texture", i32, 1, 0, 0, 9) %texture, <4 x i32> %coords)
+  %old = atomicrmw xchg ptr %ptr, i32 %value monotonic
+  ret i32 %old
+}

diff  --git a/llvm/test/CodeGen/DirectX/ResourceAtomicBinOp-texture.ll 
b/llvm/test/CodeGen/DirectX/ResourceAtomicBinOp-texture.ll
new file mode 100644
index 0000000000000..f42b3de490243
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/ResourceAtomicBinOp-texture.ll
@@ -0,0 +1,62 @@
+; RUN: opt -S -dxil-resource-access -dxil-op-lower %s | FileCheck %s
+
+; Verify atomicrmw through a dx.resource.getpointer of a texture is lowered to
+; dx.op.atomicBinOp, with one coordinate operand per texture dimension.
+
+target triple = "dxil-pc-shadermodel6.6-compute"
+
+; CHECK-LABEL: define i32 @atomic_texture1d(
+define i32 @atomic_texture1d(i32 %coord, i32 %value) {
+  %texture = call target("dx.Texture", i32, 1, 0, 0, 1)
+      @llvm.dx.resource.handlefrombinding(i32 0, i32 0, i32 1, i32 0, ptr null)
+  %ptr = call ptr @llvm.dx.resource.getpointer(
+      target("dx.Texture", i32, 1, 0, 0, 1) %texture, i32 %coord)
+
+  ; CHECK: call i32 @dx.op.atomicBinOp.i32(i32 78, %dx.types.Handle %{{.*}}, 
i32 0, i32 %coord, i32 poison, i32 poison, i32 %value)
+  %add = atomicrmw add ptr %ptr, i32 %value monotonic
+  ret i32 %add
+}
+
+; CHECK-LABEL: define i32 @atomic_texture2d(
+define i32 @atomic_texture2d(<2 x i32> %coords, i32 %value) {
+  %texture = call target("dx.Texture", i32, 1, 0, 0, 2)
+      @llvm.dx.resource.handlefrombinding(i32 0, i32 1, i32 1, i32 0, ptr null)
+  %ptr = call ptr @llvm.dx.resource.getpointer(
+      target("dx.Texture", i32, 1, 0, 0, 2) %texture, <2 x i32> %coords)
+
+  ; CHECK: %[[X:.*]] = extractelement <2 x i32> %coords, i64 0
+  ; CHECK: %[[Y:.*]] = extractelement <2 x i32> %coords, i64 1
+  ; CHECK: call i32 @dx.op.atomicBinOp.i32(i32 78, %dx.types.Handle %{{.*}}, 
i32 6, i32 %[[X]], i32 %[[Y]], i32 poison, i32 %value)
+  %umin = atomicrmw umin ptr %ptr, i32 %value monotonic
+  ret i32 %umin
+}
+
+; CHECK-LABEL: define i32 @atomic_texture2darray(
+define i32 @atomic_texture2darray(<3 x i32> %coords, i32 %value) {
+  %texture = call target("dx.Texture", i32, 1, 0, 0, 7)
+      @llvm.dx.resource.handlefrombinding(i32 0, i32 2, i32 1, i32 0, ptr null)
+  %ptr = call ptr @llvm.dx.resource.getpointer(
+      target("dx.Texture", i32, 1, 0, 0, 7) %texture, <3 x i32> %coords)
+
+  ; CHECK: %[[X:.*]] = extractelement <3 x i32> %coords, i64 0
+  ; CHECK: %[[Y:.*]] = extractelement <3 x i32> %coords, i64 1
+  ; CHECK: %[[Z:.*]] = extractelement <3 x i32> %coords, i64 2
+  ; CHECK: call i32 @dx.op.atomicBinOp.i32(i32 78, %dx.types.Handle %{{.*}}, 
i32 8, i32 %[[X]], i32 %[[Y]], i32 %[[Z]], i32 %value)
+  %xchg = atomicrmw xchg ptr %ptr, i32 %value monotonic
+  ret i32 %xchg
+}
+
+; CHECK-LABEL: define i64 @atomic_texture3d_i64(
+define i64 @atomic_texture3d_i64(<3 x i32> %coords, i64 %value) {
+  %texture = call target("dx.Texture", i64, 1, 0, 0, 4)
+      @llvm.dx.resource.handlefrombinding(i32 0, i32 3, i32 1, i32 0, ptr null)
+  %ptr = call ptr @llvm.dx.resource.getpointer(
+      target("dx.Texture", i64, 1, 0, 0, 4) %texture, <3 x i32> %coords)
+
+  ; CHECK: %[[X:.*]] = extractelement <3 x i32> %coords, i64 0
+  ; CHECK: %[[Y:.*]] = extractelement <3 x i32> %coords, i64 1
+  ; CHECK: %[[Z:.*]] = extractelement <3 x i32> %coords, i64 2
+  ; CHECK: call i64 @dx.op.atomicBinOp.i64(i32 78, %dx.types.Handle %{{.*}}, 
i32 5, i32 %[[X]], i32 %[[Y]], i32 %[[Z]], i64 %value)
+  %max = atomicrmw max ptr %ptr, i64 %value monotonic
+  ret i64 %max
+}

diff  --git a/llvm/test/CodeGen/DirectX/ResourceAtomicBinOp.ll 
b/llvm/test/CodeGen/DirectX/ResourceAtomicBinOp.ll
index bc852c8a2c81f..69f023c609bab 100644
--- a/llvm/test/CodeGen/DirectX/ResourceAtomicBinOp.ll
+++ b/llvm/test/CodeGen/DirectX/ResourceAtomicBinOp.ll
@@ -13,23 +13,23 @@ define i32 @atomic_i32(i32 %index, i32 %value) {
   %ptr = call ptr @llvm.dx.resource.getpointer(
       target("dx.RawBuffer", i32, 1, 0, 0) %buffer, i32 %index)
 
-  ; I32: call i32 @dx.op.atomicBinOp.i32(i32 78, %dx.types.Handle %{{.*}}, i32 
0, i32 %index, i32 0, i32 0, i32 %value)
+  ; I32: call i32 @dx.op.atomicBinOp.i32(i32 78, %dx.types.Handle %{{.*}}, i32 
0, i32 %index, i32 0, i32 poison, i32 %value)
   %add = atomicrmw add ptr %ptr, i32 %value monotonic
-  ; I32: call i32 @dx.op.atomicBinOp.i32(i32 78, %dx.types.Handle %{{.*}}, i32 
1, i32 %index, i32 0, i32 0, i32 %value)
+  ; I32: call i32 @dx.op.atomicBinOp.i32(i32 78, %dx.types.Handle %{{.*}}, i32 
1, i32 %index, i32 0, i32 poison, i32 %value)
   %and = atomicrmw and ptr %ptr, i32 %value monotonic
-  ; I32: call i32 @dx.op.atomicBinOp.i32(i32 78, %dx.types.Handle %{{.*}}, i32 
2, i32 %index, i32 0, i32 0, i32 %value)
+  ; I32: call i32 @dx.op.atomicBinOp.i32(i32 78, %dx.types.Handle %{{.*}}, i32 
2, i32 %index, i32 0, i32 poison, i32 %value)
   %or = atomicrmw or ptr %ptr, i32 %value monotonic
-  ; I32: call i32 @dx.op.atomicBinOp.i32(i32 78, %dx.types.Handle %{{.*}}, i32 
3, i32 %index, i32 0, i32 0, i32 %value)
+  ; I32: call i32 @dx.op.atomicBinOp.i32(i32 78, %dx.types.Handle %{{.*}}, i32 
3, i32 %index, i32 0, i32 poison, i32 %value)
   %xor = atomicrmw xor ptr %ptr, i32 %value monotonic
-  ; I32: call i32 @dx.op.atomicBinOp.i32(i32 78, %dx.types.Handle %{{.*}}, i32 
4, i32 %index, i32 0, i32 0, i32 %value)
+  ; I32: call i32 @dx.op.atomicBinOp.i32(i32 78, %dx.types.Handle %{{.*}}, i32 
4, i32 %index, i32 0, i32 poison, i32 %value)
   %min = atomicrmw min ptr %ptr, i32 %value monotonic
-  ; I32: call i32 @dx.op.atomicBinOp.i32(i32 78, %dx.types.Handle %{{.*}}, i32 
5, i32 %index, i32 0, i32 0, i32 %value)
+  ; I32: call i32 @dx.op.atomicBinOp.i32(i32 78, %dx.types.Handle %{{.*}}, i32 
5, i32 %index, i32 0, i32 poison, i32 %value)
   %max = atomicrmw max ptr %ptr, i32 %value monotonic
-  ; I32: call i32 @dx.op.atomicBinOp.i32(i32 78, %dx.types.Handle %{{.*}}, i32 
6, i32 %index, i32 0, i32 0, i32 %value)
+  ; I32: call i32 @dx.op.atomicBinOp.i32(i32 78, %dx.types.Handle %{{.*}}, i32 
6, i32 %index, i32 0, i32 poison, i32 %value)
   %umin = atomicrmw umin ptr %ptr, i32 %value monotonic
-  ; I32: call i32 @dx.op.atomicBinOp.i32(i32 78, %dx.types.Handle %{{.*}}, i32 
7, i32 %index, i32 0, i32 0, i32 %value)
+  ; I32: call i32 @dx.op.atomicBinOp.i32(i32 78, %dx.types.Handle %{{.*}}, i32 
7, i32 %index, i32 0, i32 poison, i32 %value)
   %umax = atomicrmw umax ptr %ptr, i32 %value monotonic
-  ; I32: call i32 @dx.op.atomicBinOp.i32(i32 78, %dx.types.Handle %{{.*}}, i32 
8, i32 %index, i32 0, i32 0, i32 %value)
+  ; I32: call i32 @dx.op.atomicBinOp.i32(i32 78, %dx.types.Handle %{{.*}}, i32 
8, i32 %index, i32 0, i32 poison, i32 %value)
   %xchg = atomicrmw xchg ptr %ptr, i32 %value monotonic
   ret i32 %xchg
 }
@@ -41,7 +41,7 @@ define i32 @atomic_i32_byteaddress(i32 %offset, i32 %value) {
   %ptr = call ptr @llvm.dx.resource.getpointer(
       target("dx.RawBuffer", i8, 1, 0, 0) %buffer, i32 %offset)
 
-  ; I32: call i32 @dx.op.atomicBinOp.i32(i32 78, %dx.types.Handle %{{.*}}, i32 
0, i32 %offset, i32 poison, i32 0, i32 %value)
+  ; I32: call i32 @dx.op.atomicBinOp.i32(i32 78, %dx.types.Handle %{{.*}}, i32 
0, i32 %offset, i32 poison, i32 poison, i32 %value)
   %old = atomicrmw add ptr %ptr, i32 %value monotonic
   ret i32 %old
 }
@@ -53,7 +53,7 @@ define i64 @atomic_i64(i32 %index, i64 %value) {
   %ptr = call ptr @llvm.dx.resource.getpointer(
       target("dx.RawBuffer", i64, 1, 0, 0) %buffer, i32 %index)
 
-  ; I64: call i64 @dx.op.atomicBinOp.i64(i32 78, %dx.types.Handle %{{.*}}, i32 
0, i32 %index, i32 0, i32 0, i64 %value)
+  ; I64: call i64 @dx.op.atomicBinOp.i64(i32 78, %dx.types.Handle %{{.*}}, i32 
0, i32 %index, i32 0, i32 poison, i64 %value)
   %old = atomicrmw add ptr %ptr, i64 %value monotonic
   ret i64 %old
 }

diff  --git a/llvm/test/CodeGen/DirectX/ShaderFlags/atomic64-typed-resource.ll 
b/llvm/test/CodeGen/DirectX/ShaderFlags/atomic64-typed-resource.ll
index 25706c25ce7e9..8f4ffdd577af7 100644
--- a/llvm/test/CodeGen/DirectX/ShaderFlags/atomic64-typed-resource.ll
+++ b/llvm/test/CodeGen/DirectX/ShaderFlags/atomic64-typed-resource.ll
@@ -23,7 +23,7 @@ define void @main() #0 {
           i32 0, i32 0, i32 1, i32 0, ptr null)
   %old = call i64 @llvm.dx.resource.atomic.binop.i64(
       target("dx.TypedBuffer", i64, 1, 0, 0) %handle,
-      i32 0, i32 0, i32 poison, i64 1)
+      i32 0, i32 0, i32 poison, i32 poison, i64 1)
   ret void
 }
 


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to