llvmorg-github-actions[bot] wrote:

<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-ir

@llvm/pr-subscribers-backend-directx

Author: Joshua Batista (bob80905)

<details>
<summary>Changes</summary>

This PR completes the implementation of the InterlockedAdd functions in HLSL, 
by adding these methods to all existing resource classes.
Fixes: https://github.com/llvm/llvm-project/issues/99122
Assisted by: Github Copilot

---

Patch is 30.24 KiB, truncated to 20.00 KiB below, full version: 
https://github.com/llvm/llvm-project/pull/208128.diff


12 Files Affected:

- (modified) clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp (+68) 
- (modified) clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h (+4) 
- (modified) clang/lib/Sema/HLSLExternalSemaSource.cpp (+2) 
- (modified) clang/lib/Sema/SemaHLSL.cpp (+23) 
- (added) clang/test/CodeGenHLSL/builtins/ByteAddressBuffer-InterlockedAdd.hlsl 
(+80) 
- (added) 
clang/test/SemaHLSL/BuiltIns/ByteAddressBuffer-InterlockedAdd-errors.hlsl (+63) 
- (modified) llvm/include/llvm/IR/IntrinsicsDirectX.td (+8) 
- (modified) llvm/lib/Target/DirectX/DXIL.td (+11) 
- (modified) llvm/lib/Target/DirectX/DXILOpLowering.cpp (+30) 
- (modified) llvm/lib/Target/DirectX/DXILResourceAccess.cpp (+141) 
- (added) llvm/test/CodeGen/DirectX/ResourceAtomicBinOp-i64-sm65.ll (+16) 
- (added) llvm/test/CodeGen/DirectX/ResourceAtomicBinOp.ll (+59) 


``````````diff
diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp 
b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
index f8018729b4644..245487b71875f 100644
--- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
+++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
@@ -1635,6 +1635,38 @@ 
BuiltinTypeDeclBuilder::addByteAddressBufferStoreMethods() {
   return *this;
 }
 
+BuiltinTypeDeclBuilder &
+BuiltinTypeDeclBuilder::addByteAddressBufferInterlockedMethods() {
+  assert(!Record->isCompleteDefinition() && "record is already complete");
+  ASTContext &AST = SemaRef.getASTContext();
+
+  // Each entry declares two overloads (with and without an out original-value
+  // parameter). Adding a new atomic here only requires a new line — the shared
+  // helper takes care of the composition.
+  addByteAddressBufferInterlockedMethod("InterlockedAdd", AST.UnsignedIntTy,
+                                        "__builtin_hlsl_interlocked_add");
+
+  // 64-bit typed atomics on UAVs require SM 6.6 (DXIL 1.6 introduces the
+  // int64 overload of the atomicBinOp op). Skip synthesizing the *64 methods
+  // on older DXIL targets so callers get "no matching member function" from
+  // overload resolution — this matches DXC and mirrors how other HLSL SM-gated
+  // features are handled (see hlsl_intrinsics.h `_HLSL_AVAILABILITY`
+  // annotations). Non-DXIL targets (e.g., SPIR-V) always get the method: their
+  // 64-bit atomic support is gated by device extensions, not shader model.
+  const llvm::Triple &TT = AST.getTargetInfo().getTriple();
+  bool DXILNeedsSM66 =
+      TT.getArch() == llvm::Triple::dxil &&
+      AST.getTargetInfo().getPlatformMinVersion() < VersionTuple(6, 6);
+  if (!DXILNeedsSM66) {
+    // HLSL's uint64_t is `unsigned long`.
+    addByteAddressBufferInterlockedMethod("InterlockedAdd64",
+                                          AST.UnsignedLongTy,
+                                          "__builtin_hlsl_interlocked_add");
+  }
+
+  return *this;
+}
+
 BuiltinTypeDeclBuilder &
 BuiltinTypeDeclBuilder::addSampleMethods(ResourceDimension Dim, bool IsArray) {
   assert(!Record->isCompleteDefinition() && "record is already complete");
@@ -2356,6 +2388,42 @@ BuiltinTypeDeclBuilder::addStoreFunction(DeclarationName 
&Name, bool IsConst,
       .finalize();
 }
 
+BuiltinTypeDeclBuilder &
+BuiltinTypeDeclBuilder::addByteAddressBufferInterlockedMethod(
+    StringRef MethodName, QualType ValueTy, StringRef BuiltinName) {
+  assert(!Record->isCompleteDefinition() && "record is already complete");
+  ASTContext &AST = SemaRef.getASTContext();
+  using PH = BuiltinTypeMethodBuilder::PlaceHolder;
+
+  // Interlocked atomics operate on a typed slot in the buffer. Compose
+  // `resource_getpointer_typed` with the scalar `__builtin_hlsl_interlocked_*`
+  // builtin so backend lowering (DXIL and SPIR-V) can pattern-match a
+  // resource-pointer atomicrmw.
+  QualType AddrSpaceElemTy =
+      AST.getAddrSpaceQualType(ValueTy, LangAS::hlsl_device);
+  QualType ElemPtrTy = AST.getPointerType(AddrSpaceElemTy);
+
+  auto BuildOverload = [&](bool WithOriginalValue) {
+    BuiltinTypeMethodBuilder MMB(*this, MethodName, AST.VoidTy);
+    MMB.addParam("Offset", AST.UnsignedIntTy).addParam("Value", ValueTy);
+    if (WithOriginalValue)
+      MMB.addParam("OriginalValue", ValueTy,
+                   HLSLParamModifierAttr::Keyword_out);
+    MMB.callBuiltin("__builtin_hlsl_resource_getpointer_typed", ElemPtrTy,
+                    PH::Handle, PH::_0, ValueTy)
+        .dereference(PH::LastStmt);
+    if (WithOriginalValue)
+      MMB.callBuiltin(BuiltinName, AST.VoidTy, PH::LastStmt, PH::_1, PH::_2);
+    else
+      MMB.callBuiltin(BuiltinName, AST.VoidTy, PH::LastStmt, PH::_1);
+    MMB.finalize();
+  };
+
+  BuildOverload(/*WithOriginalValue=*/false);
+  BuildOverload(/*WithOriginalValue=*/true);
+  return *this;
+}
+
 BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addAppendMethod() {
   using PH = BuiltinTypeMethodBuilder::PlaceHolder;
   ASTContext &AST = SemaRef.getASTContext();
diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h 
b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h
index 09cf1fceca116..e809ef264198c 100644
--- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h
+++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h
@@ -106,6 +106,7 @@ class BuiltinTypeDeclBuilder {
                                                 bool IsArray = false);
   BuiltinTypeDeclBuilder &addByteAddressBufferLoadMethods();
   BuiltinTypeDeclBuilder &addByteAddressBufferStoreMethods();
+  BuiltinTypeDeclBuilder &addByteAddressBufferInterlockedMethods();
   BuiltinTypeDeclBuilder &addSampleMethods(ResourceDimension Dim,
                                            bool IsArray = false);
   BuiltinTypeDeclBuilder &addSampleBiasMethods(ResourceDimension Dim,
@@ -134,6 +135,9 @@ class BuiltinTypeDeclBuilder {
                             QualType ReturnTy = QualType());
   BuiltinTypeDeclBuilder &addStoreFunction(DeclarationName &Name, bool IsConst,
                                            QualType ValueType);
+  BuiltinTypeDeclBuilder &
+  addByteAddressBufferInterlockedMethod(StringRef MethodName, QualType ValueTy,
+                                        StringRef BuiltinName);
   BuiltinTypeDeclBuilder &addAppendMethod();
   BuiltinTypeDeclBuilder &addConsumeMethod();
 
diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp 
b/clang/lib/Sema/HLSLExternalSemaSource.cpp
index 1d2a3983a639d..4c1894d6761d2 100644
--- a/clang/lib/Sema/HLSLExternalSemaSource.cpp
+++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp
@@ -645,6 +645,7 @@ void 
HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() {
                     /*RawBuffer=*/true, /*HasCounter=*/false)
         .addByteAddressBufferLoadMethods()
         .addByteAddressBufferStoreMethods()
+        .addByteAddressBufferInterlockedMethods()
         .addGetDimensionsMethodForBuffer()
         .completeDefinition();
   });
@@ -654,6 +655,7 @@ void 
HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() {
   onCompletion(Decl, [this](CXXRecordDecl *Decl) {
     setupBufferType(Decl, *SemaPtr, ResourceClass::UAV, /*IsROV=*/true,
                     /*RawBuffer=*/true, /*HasCounter=*/false)
+        .addByteAddressBufferInterlockedMethods()
         .addGetDimensionsMethodForBuffer()
         .completeDefinition();
   });
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index ae6cbce2f890c..1ffddeac080fa 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -4568,6 +4568,29 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned 
BuiltinID, CallExpr *TheCall) {
       return true;
     }
 
+    // 64-bit interlocked ops require SM 6.6 on DXIL — the DXIL 1.6 int64
+    // overloads of atomicBinOp/cmpXchg are what enable them. The synthesized
+    // wrapper methods (e.g. RWByteAddressBuffer::InterlockedAdd64) that call
+    // this builtin are themselves only declared when the target supports it
+    // (see HLSLBuiltinTypeDeclBuilder), so pre-SM6.6 usage is caught by
+    // overload resolution. This defensive check catches direct
+    // `__builtin_hlsl_interlocked_add` calls from HLSL code with a 64-bit
+    // dest on pre-SM6.6 DXIL targets. Skip synthetic invocations (invalid
+    // source location) built while composing wrapper method bodies.
+    const TargetInfo &TI = SemaRef.Context.getTargetInfo();
+    if (TheCall->getBeginLoc().isValid() &&
+        TI.getTriple().getArch() == llvm::Triple::dxil &&
+        SemaRef.Context.getTypeSize(DestTy) == 64 &&
+        TI.getPlatformMinVersion() < VersionTuple(6, 6)) {
+      llvm::StringRef PlatformName(
+          AvailabilityAttr::getPrettyPlatformName(TI.getPlatformName()));
+      SemaRef.Diag(TheCall->getBeginLoc(), diag::warn_hlsl_availability)
+          << TheCall->getDirectCallee() << PlatformName
+          << VersionTuple(6, 6).getAsString() << /*UseEnvironment=*/false
+          << /*EnvName=*/"";
+      return true;
+    }
+
     if (CheckModifiableLValue(&SemaRef, TheCall, 0))
       return true;
 
diff --git 
a/clang/test/CodeGenHLSL/builtins/ByteAddressBuffer-InterlockedAdd.hlsl 
b/clang/test/CodeGenHLSL/builtins/ByteAddressBuffer-InterlockedAdd.hlsl
new file mode 100644
index 0000000000000..ae6179dc4bc99
--- /dev/null
+++ b/clang/test/CodeGenHLSL/builtins/ByteAddressBuffer-InterlockedAdd.hlsl
@@ -0,0 +1,80 @@
+// RUN: %clang_cc1 -std=hlsl202x -finclude-default-header -triple \
+// RUN:   dxil-pc-shadermodel6.6-compute %s -emit-llvm -disable-llvm-passes -o 
- | \
+// RUN:   FileCheck %s --check-prefixes=CHECK,DXCHECK
+
+// Test that the RWByteAddressBuffer::InterlockedAdd and
+// RasterizerOrderedByteAddressBuffer::InterlockedAdd member methods lower to
+// `dx.resource.getpointer.typed -> dx.interlocked.add`, and that the
+// 3-argument overload stores the returned original value through the out
+// parameter.
+
+RWByteAddressBuffer BAB : register(u0);
+RasterizerOrderedByteAddressBuffer ROVB : register(u1);
+
+// CHECK-LABEL: define {{(dso_local |hidden |internal |protected |spir_func 
)*}}void @{{.*}}test_bab_int_2arg
+// DXCHECK: %[[HANDLE:.*]] = load target("dx.RawBuffer", i8, 1, 0), ptr {{.*}}
+// DXCHECK: %[[PTR:.*]] = call ptr 
@llvm.dx.resource.getpointer.p0.tdx.RawBuffer_i8_1_0t.i32(target("dx.RawBuffer",
 i8, 1, 0) %[[HANDLE]], i32 %{{.*}})
+// DXCHECK: call i32 @llvm.dx.interlocked.add.i32.p0(ptr %[[PTR]], i32 %{{.*}})
+export void test_bab_int_2arg(uint off, int v) {
+  BAB.InterlockedAdd(off, v);
+}
+
+// CHECK-LABEL: define {{(dso_local |hidden |internal |protected |spir_func 
)*}}void @{{.*}}test_bab_uint_3arg
+// DXCHECK: %[[HANDLE:.*]] = load target("dx.RawBuffer", i8, 1, 0), ptr {{.*}}
+// DXCHECK: %[[PTR:.*]] = call ptr 
@llvm.dx.resource.getpointer.p0.tdx.RawBuffer_i8_1_0t.i32(target("dx.RawBuffer",
 i8, 1, 0) %[[HANDLE]], i32 %{{.*}})
+// DXCHECK: %[[R:.*]] = call i32 @llvm.dx.interlocked.add.i32.p0(ptr %[[PTR]], 
i32 %{{.*}})
+// DXCHECK: store i32 %[[R]], ptr {{.*}}
+export void test_bab_uint_3arg(uint off, uint v, out uint orig) {
+  BAB.InterlockedAdd(off, v, orig);
+}
+
+// CHECK-LABEL: define {{(dso_local |hidden |internal |protected |spir_func 
)*}}void @{{.*}}test_rovb_int_2arg
+// DXCHECK: %[[HANDLE:.*]] = load target("dx.RawBuffer", i8, 1, 1), ptr {{.*}}
+// DXCHECK: %[[PTR:.*]] = call ptr 
@llvm.dx.resource.getpointer.p0.tdx.RawBuffer_i8_1_1t.i32(target("dx.RawBuffer",
 i8, 1, 1) %[[HANDLE]], i32 %{{.*}})
+// DXCHECK: call i32 @llvm.dx.interlocked.add.i32.p0(ptr %[[PTR]], i32 %{{.*}})
+export void test_rovb_int_2arg(uint off, int v) {
+  ROVB.InterlockedAdd(off, v);
+}
+
+// CHECK-LABEL: define {{(dso_local |hidden |internal |protected |spir_func 
)*}}void @{{.*}}test_rovb_uint_3arg
+// DXCHECK: %[[HANDLE:.*]] = load target("dx.RawBuffer", i8, 1, 1), ptr {{.*}}
+// DXCHECK: %[[PTR:.*]] = call ptr 
@llvm.dx.resource.getpointer.p0.tdx.RawBuffer_i8_1_1t.i32(target("dx.RawBuffer",
 i8, 1, 1) %[[HANDLE]], i32 %{{.*}})
+// DXCHECK: %[[R:.*]] = call i32 @llvm.dx.interlocked.add.i32.p0(ptr %[[PTR]], 
i32 %{{.*}})
+// DXCHECK: store i32 %[[R]], ptr {{.*}}
+export void test_rovb_uint_3arg(uint off, uint v, out uint orig) {
+  ROVB.InterlockedAdd(off, v, orig);
+}
+
+// CHECK-LABEL: define {{(dso_local |hidden |internal |protected |spir_func 
)*}}void @{{.*}}test_bab_int64_2arg
+// DXCHECK: %[[HANDLE:.*]] = load target("dx.RawBuffer", i8, 1, 0), ptr {{.*}}
+// DXCHECK: %[[PTR:.*]] = call ptr 
@llvm.dx.resource.getpointer.p0.tdx.RawBuffer_i8_1_0t.i32(target("dx.RawBuffer",
 i8, 1, 0) %[[HANDLE]], i32 %{{.*}})
+// DXCHECK: call i64 @llvm.dx.interlocked.add.i64.p0(ptr %[[PTR]], i64 %{{.*}})
+export void test_bab_int64_2arg(uint off, int64_t v) {
+  BAB.InterlockedAdd64(off, v);
+}
+
+// CHECK-LABEL: define {{(dso_local |hidden |internal |protected |spir_func 
)*}}void @{{.*}}test_bab_uint64_3arg
+// DXCHECK: %[[HANDLE:.*]] = load target("dx.RawBuffer", i8, 1, 0), ptr {{.*}}
+// DXCHECK: %[[PTR:.*]] = call ptr 
@llvm.dx.resource.getpointer.p0.tdx.RawBuffer_i8_1_0t.i32(target("dx.RawBuffer",
 i8, 1, 0) %[[HANDLE]], i32 %{{.*}})
+// DXCHECK: %[[R:.*]] = call i64 @llvm.dx.interlocked.add.i64.p0(ptr %[[PTR]], 
i64 %{{.*}})
+// DXCHECK: store i64 %[[R]], ptr {{.*}}
+export void test_bab_uint64_3arg(uint off, uint64_t v, out uint64_t orig) {
+  BAB.InterlockedAdd64(off, v, orig);
+}
+
+// CHECK-LABEL: define {{(dso_local |hidden |internal |protected |spir_func 
)*}}void @{{.*}}test_rovb_int64_2arg
+// DXCHECK: %[[HANDLE:.*]] = load target("dx.RawBuffer", i8, 1, 1), ptr {{.*}}
+// DXCHECK: %[[PTR:.*]] = call ptr 
@llvm.dx.resource.getpointer.p0.tdx.RawBuffer_i8_1_1t.i32(target("dx.RawBuffer",
 i8, 1, 1) %[[HANDLE]], i32 %{{.*}})
+// DXCHECK: call i64 @llvm.dx.interlocked.add.i64.p0(ptr %[[PTR]], i64 %{{.*}})
+export void test_rovb_int64_2arg(uint off, int64_t v) {
+  ROVB.InterlockedAdd64(off, v);
+}
+
+// CHECK-LABEL: define {{(dso_local |hidden |internal |protected |spir_func 
)*}}void @{{.*}}test_rovb_uint64_3arg
+// DXCHECK: %[[HANDLE:.*]] = load target("dx.RawBuffer", i8, 1, 1), ptr {{.*}}
+// DXCHECK: %[[PTR:.*]] = call ptr 
@llvm.dx.resource.getpointer.p0.tdx.RawBuffer_i8_1_1t.i32(target("dx.RawBuffer",
 i8, 1, 1) %[[HANDLE]], i32 %{{.*}})
+// DXCHECK: %[[R:.*]] = call i64 @llvm.dx.interlocked.add.i64.p0(ptr %[[PTR]], 
i64 %{{.*}})
+// DXCHECK: store i64 %[[R]], ptr {{.*}}
+export void test_rovb_uint64_3arg(uint off, uint64_t v, out uint64_t orig) {
+  ROVB.InterlockedAdd64(off, v, orig);
+}
diff --git 
a/clang/test/SemaHLSL/BuiltIns/ByteAddressBuffer-InterlockedAdd-errors.hlsl 
b/clang/test/SemaHLSL/BuiltIns/ByteAddressBuffer-InterlockedAdd-errors.hlsl
new file mode 100644
index 0000000000000..13fc70d700421
--- /dev/null
+++ b/clang/test/SemaHLSL/BuiltIns/ByteAddressBuffer-InterlockedAdd-errors.hlsl
@@ -0,0 +1,63 @@
+// RUN: %clang_cc1 -std=hlsl202x -finclude-default-header \
+// RUN:   -triple dxil-pc-shadermodel6.6-compute %s -fsyntax-only -verify \
+// RUN:   -verify-ignore-unexpected=note,warning
+
+// RUN: %clang_cc1 -std=hlsl202x -finclude-default-header \
+// RUN:   -triple dxil-pc-shadermodel6.5-compute -DTEST_SM65 %s -fsyntax-only \
+// RUN:   -verify -verify-ignore-unexpected=note,warning
+
+RWByteAddressBuffer BAB : register(u0);
+RasterizerOrderedByteAddressBuffer ROVB : register(u1);
+
+struct S { int x; };
+
+#ifdef TEST_SM65
+
+// InterlockedAdd64 is only synthesized on DXIL when the shader model is at
+// least 6.6 (matches DXC). On SM 6.5 the member is not declared at all, so
+// the reference must fail with "no member named".
+void sm65_no_bab_add64(uint off, int64_t v) {
+  BAB.InterlockedAdd64(off, v);
+  // expected-error@-1 {{no member named 'InterlockedAdd64' in 
'hlsl::RWByteAddressBuffer'}}
+}
+
+void sm65_no_rovb_add64(uint off, int64_t v) {
+  ROVB.InterlockedAdd64(off, v);
+  // expected-error@-1 {{no member named 'InterlockedAdd64' in 
'hlsl::RasterizerOrderedByteAddressBuffer'}}
+}
+
+// 32-bit InterlockedAdd is always available.
+void sm65_bab_add32_ok(uint off, int v) {
+  BAB.InterlockedAdd(off, v);
+}
+
+#else
+
+void too_few(uint off) {
+  BAB.InterlockedAdd(off);
+  // expected-error@-1 {{no matching member function for call to 
'InterlockedAdd'}}
+}
+
+void too_many(uint off, int v, int extra) {
+  int orig;
+  BAB.InterlockedAdd(off, v, orig, extra);
+  // expected-error@-1 {{no matching member function for call to 
'InterlockedAdd'}}
+}
+
+void struct_value(uint off, S v) {
+  BAB.InterlockedAdd(off, v);
+  // expected-error@-1 {{no matching member function for call to 
'InterlockedAdd'}}
+}
+
+// Same shape of errors on RasterizerOrderedByteAddressBuffer.
+void rovb_too_few(uint off) {
+  ROVB.InterlockedAdd(off);
+  // expected-error@-1 {{no matching member function for call to 
'InterlockedAdd'}}
+}
+
+void rovb_struct_value(uint off, S v) {
+  ROVB.InterlockedAdd(off, v);
+  // expected-error@-1 {{no matching member function for call to 
'InterlockedAdd'}}
+}
+
+#endif
diff --git a/llvm/include/llvm/IR/IntrinsicsDirectX.td 
b/llvm/include/llvm/IR/IntrinsicsDirectX.td
index 63186ab4d7191..6b88d5c633621 100644
--- a/llvm/include/llvm/IR/IntrinsicsDirectX.td
+++ b/llvm/include/llvm/IR/IntrinsicsDirectX.td
@@ -61,6 +61,14 @@ def int_dx_resource_store_rawbuffer
     : DefaultAttrsIntrinsic<
           [], [llvm_any_ty, llvm_i32_ty, llvm_i32_ty, llvm_any_ty],
           [IntrWriteMem]>;
+// Resource atomic binary op: performs an atomic read-modify-write on a UAV
+// resource element and returns the original value. The i32 operation code
+// matches DXIL's AtomicBinOpCode enum.
+def int_dx_resource_atomicbinop
+    : DefaultAttrsIntrinsic<[llvm_anyint_ty],
+                            [llvm_any_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
 // function name. The total size of the return should always be 128 bits.
diff --git a/llvm/lib/Target/DirectX/DXIL.td b/llvm/lib/Target/DirectX/DXIL.td
index 299d2d113b6bf..637fc05b3b2da 100644
--- a/llvm/lib/Target/DirectX/DXIL.td
+++ b/llvm/lib/Target/DirectX/DXIL.td
@@ -983,6 +983,17 @@ def BufferStore : DXILOp<69, bufferStore> {
   let stages = [Stages<DXIL1_0, [all_stages]>];
 }
 
+def AtomicBinOp : DXILOp<78, atomicBinOp> {
+  let Doc = "performs an atomic read-modify-write on a UAV resource "
+            "element, returning the original value";
+  // Handle, AtomicBinOpCode, Coord0, Coord1, Coord2, NewValue
+  let arguments = [HandleTy, Int32Ty, Int32Ty, Int32Ty, Int32Ty, OverloadTy];
+  let result = OverloadTy;
+  let overloads = [Overloads<DXIL1_0, [Int32Ty]>,
+                   Overloads<DXIL1_6, [Int32Ty, Int64Ty]>];
+  let stages = [Stages<DXIL1_0, [all_stages]>];
+}
+
 def UpdateCounter : DXILOp<70, bufferUpdateCounter> {
   let Doc = "increments/decrements a buffer counter";
   let arguments = [HandleTy, Int8Ty];
diff --git a/llvm/lib/Target/DirectX/DXILOpLowering.cpp 
b/llvm/lib/Target/DirectX/DXILOpLowering.cpp
index 93d5a08a6e0a2..64e0de8e55bdd 100644
--- a/llvm/lib/Target/DirectX/DXILOpLowering.cpp
+++ b/llvm/lib/Target/DirectX/DXILOpLowering.cpp
@@ -849,6 +849,33 @@ class OpLowerer {
     });
   }
 
+  [[nodiscard]] bool lowerResourceAtomicBinOp(Function &F) {
+    IRBuilder<> &IRB = OpBuilder.getIRB();
+
+    return replaceFunction(F, [&](CallInst *CI) -> Error {
+      IRB.SetInsertPoint(CI);
+      Value *Handle =
+          createTmpHandleCast(CI->getArgOperand(0), OpBuilder.getHandleType());
+      Value *Index = CI->getArgOperand(1);
+      Value *Offset = CI->getArgOperand(2);
+      Value *BinOp = CI->getArgOperand(3);
+      Value *NewValue = CI->getArgOperand(4);
+
+      std::array<Value *, 6> Args{Handle, BinOp,           Index,
+                                  Offset, IRB.getInt32(0), NewValue};
+
+      Expected<CallInst *> OpCall = OpBuilder.tryCreateOp(
+          OpCode::AtomicBinOp, Args, CI->getName(), CI->getType());
+
+      if (Error E = OpCall.takeError())
+        return E;
+
+      CI->replaceAllUsesWith(*OpCall);
+      CI->eraseFromParent();
+      return Error::success();
+    });
+  }
+
   [[nodiscard]] bool lowerGetDimensionsX(Function &F) {
     IRBuilder<> &IRB = OpBuilder.getIRB();
     Type *Int32Ty = IRB.getInt32Ty();
@@ -1210,6 +1237,9 @@ class OpLowerer {
       case Intrinsic::dx_resource_updatecounter:
         HasErrors |= lowerUpdateCounter(F);
         break;
+      case Intrinsic::dx_resource_atomicbinop:
+        HasErrors |= lowerResourceAtomicBinOp(F);
+        break;
       case Intrinsic::dx_resource_getdimensions_x:
         HasErrors |= lowerGetDimensionsX(F);
         break;
diff --git a/llvm/lib/Target/DirectX/DXILResourceAccess.cpp 
b/llvm/lib/Target/DirectX/DXILResourceAccess.cpp
index 25d860e615c17..5f5c4660e744b 100644
--- a/llvm/lib/Target/DirectX/DXILResourceAccess.cpp
+++ b/llvm/lib/Target/DirectX/DXILResourceAccess.cpp
@@ -26,6 +26,7 @@
 #include "llvm/InitializePasses.h"
 #include "llvm/Support/FormatVariadic.h"
 #include "llvm/Transforms/Utils/ValueMapper.h"
+#include <optional>
 
 #define DEBUG_TYPE "dxil-resource-access"
 
@@ -230,6 +231,114 @@ static void createStoreIntrinsic(IntrinsicInst *II, 
StoreInst *SI,
   llvm_unreachable("Unhandled case in switch");
 }
 
+static std::optional<unsigned> getAtomicBinOpCode(AtomicRMWInst::BinOp BinOp) {
+  switch (BinOp) {
+  case AtomicRMWInst::Add:
+    return 0;
+  case AtomicRMWInst::And:
+    return 1;
+  ...
[truncated]

``````````

</details>


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

Reply via email to