[clang] [llvm] [HLSL] Implement HLSL InterlockedXor (PR #209254)

Alexander Johnston via cfe-commits cfe-commits at lists.llvm.org
Fri Jul 24 08:00:30 PDT 2026


https://github.com/Alexander-Johnston updated https://github.com/llvm/llvm-project/pull/209254

>From e0c11f9ef1e2db3a48d3154afaf4aafb80c177f0 Mon Sep 17 00:00:00 2001
From: Alexander Johnston <alexander.johnston at amd.com>
Date: Mon, 13 Jul 2026 18:36:28 +0100
Subject: [PATCH 1/3] [HLSL] Implement HLSL InterlockedXor

Follows existing HLSL InterlockedOp implementations to provide
InterlockedXor in HLSL, with DirectX and SPIRV support.
Does not include RWByteAddressBuffer interlocked methods.
---
 clang/include/clang/Basic/Builtins.td         |   6 ++
 clang/lib/CodeGen/CGHLSLBuiltins.cpp          |   5 +
 clang/lib/CodeGen/CGHLSLRuntime.h             |   1 +
 clang/lib/Sema/HLSLExternalSemaSource.cpp     |   2 +
 clang/lib/Sema/SemaHLSL.cpp                   |   7 +-
 .../CodeGenHLSL/builtins/InterlockedXor.hlsl  |  59 +++++++++++
 .../BuiltIns/InterlockedXor-errors.hlsl       | 100 ++++++++++++++++++
 llvm/include/llvm/IR/IntrinsicsDirectX.td     |   4 +
 llvm/include/llvm/IR/IntrinsicsSPIRV.td       |   4 +
 .../Target/DirectX/DXILIntrinsicExpansion.cpp |   4 +
 .../Target/SPIRV/SPIRVInstructionSelector.cpp |   2 +
 llvm/test/CodeGen/DirectX/InterlockedXor.ll   |  52 +++++++++
 .../SPIRV/hlsl-intrinsics/InterlockedXor.ll   |  36 +++++++
 .../hlsl-intrinsics/InterlockedXor_spv_i64.ll |  37 +++++++
 14 files changed, 316 insertions(+), 3 deletions(-)
 create mode 100644 clang/test/CodeGenHLSL/builtins/InterlockedXor.hlsl
 create mode 100644 clang/test/SemaHLSL/BuiltIns/InterlockedXor-errors.hlsl
 create mode 100644 llvm/test/CodeGen/DirectX/InterlockedXor.ll
 create mode 100644 llvm/test/CodeGen/SPIRV/hlsl-intrinsics/InterlockedXor.ll
 create mode 100644 llvm/test/CodeGen/SPIRV/hlsl-intrinsics/InterlockedXor_spv_i64.ll

diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index 344a712ddc585..06d559a5c3ce2 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -5545,6 +5545,12 @@ def HLSLInterlockedOr : LangBuiltin<"HLSL_LANG"> {
   let Prototype = "void (...)";
 }
 
+def HLSLInterlockedXor : LangBuiltin<"HLSL_LANG"> {
+  let Spellings = ["__builtin_hlsl_interlocked_xor"];
+  let Attributes = [NoThrow];
+  let Prototype = "void (...)";
+}
+
 def HLSLWaveActiveBallot : LangBuiltin<"HLSL_LANG"> {
   let Spellings = ["__builtin_hlsl_wave_active_ballot"];
   let Attributes = [NoThrow, Const];
diff --git a/clang/lib/CodeGen/CGHLSLBuiltins.cpp b/clang/lib/CodeGen/CGHLSLBuiltins.cpp
index 1bda113143e07..d5d0d9b09bd46 100644
--- a/clang/lib/CodeGen/CGHLSLBuiltins.cpp
+++ b/clang/lib/CodeGen/CGHLSLBuiltins.cpp
@@ -1466,6 +1466,11 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID,
                                CGM.getHLSLRuntime().getInterlockedOrIntrinsic(),
                                "hlsl.interlocked.or");
   }
+  case Builtin::BI__builtin_hlsl_interlocked_xor: {
+    return handleInterlockedOp(*this, E,
+                               CGM.getHLSLRuntime().getInterlockedXorIntrinsic(),
+                               "hlsl.interlocked.xor");
+  }
   case Builtin::BI__builtin_hlsl_wave_active_ballot: {
     [[maybe_unused]] Value *Op = EmitScalarExpr(E->getArg(0));
     assert(Op->getType()->isIntegerTy(1) &&
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h b/clang/lib/CodeGen/CGHLSLRuntime.h
index cf47b1633fd3c..0fe6a59cc5927 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.h
+++ b/clang/lib/CodeGen/CGHLSLRuntime.h
@@ -153,6 +153,7 @@ class CGHLSLRuntime {
   GENERATE_HLSL_INTRINSIC_FUNCTION(WaveActiveBitAnd, wave_reduce_and)
   GENERATE_HLSL_INTRINSIC_FUNCTION(InterlockedAdd, interlocked_add)
   GENERATE_HLSL_INTRINSIC_FUNCTION(InterlockedOr, interlocked_or)
+  GENERATE_HLSL_INTRINSIC_FUNCTION(InterlockedXor, interlocked_xor)
   GENERATE_HLSL_INTRINSIC_FUNCTION(WaveActiveMax, wave_reduce_max)
   GENERATE_HLSL_INTRINSIC_FUNCTION(WaveActiveUMax, wave_reduce_umax)
   GENERATE_HLSL_INTRINSIC_FUNCTION(WaveActiveMin, wave_reduce_min)
diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp
index 115795ca74bae..77b8550ba4c6c 100644
--- a/clang/lib/Sema/HLSLExternalSemaSource.cpp
+++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp
@@ -821,6 +821,8 @@ void HLSLExternalSemaSource::defineHLSLAtomicIntrinsics() {
                             "__builtin_hlsl_interlocked_add");
   defineHLSLInterlockedFunc(*SemaPtr, HLSLNamespace, "InterlockedOr",
                             "__builtin_hlsl_interlocked_or");
+  defineHLSLInterlockedFunc(*SemaPtr, HLSLNamespace, "InterlockedXor",
+                            "__builtin_hlsl_interlocked_xor");
 }
 
 void HLSLExternalSemaSource::onCompletion(CXXRecordDecl *Record,
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index ae6cbce2f890c..fa8921b4b451e 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -4541,10 +4541,11 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
     break;
   }
   case Builtin::BI__builtin_hlsl_interlocked_add:
-  case Builtin::BI__builtin_hlsl_interlocked_or: {
+  case Builtin::BI__builtin_hlsl_interlocked_or:
+  case Builtin::BI__builtin_hlsl_interlocked_xor: {
     // The builtin's prototype in Builtins.td is `void (...)`, so direct calls
-    // to `__builtin_hlsl_interlocked_add` bypass argument checking entirely.
-    // When reached via the synthesized `InterlockedAdd` overload set in
+    // to `__builtin_hlsl_interlocked_op` bypass argument checking entirely.
+    // When reached via the synthesized `InterlockedOp` overload set in
     // HLSLExternalSemaSource, overload resolution has already enforced the
     // argument count, integer-type matching, and the address-space requirement
     // on `dest`. The checks below are a safety net for callers that invoke the
diff --git a/clang/test/CodeGenHLSL/builtins/InterlockedXor.hlsl b/clang/test/CodeGenHLSL/builtins/InterlockedXor.hlsl
new file mode 100644
index 0000000000000..3935ddff52702
--- /dev/null
+++ b/clang/test/CodeGenHLSL/builtins/InterlockedXor.hlsl
@@ -0,0 +1,59 @@
+// RUN: %clang_cc1 -std=hlsl2021 -finclude-default-header -triple \
+// RUN:   dxil-pc-shadermodel6.6-compute %s -emit-llvm -disable-llvm-passes -o - | \
+// RUN:   FileCheck %s --check-prefixes=CHECK,DXCHECK
+
+// RUN: %clang_cc1 -std=hlsl2021 -finclude-default-header -triple \
+// RUN:   spirv-pc-vulkan-compute %s -emit-llvm -disable-llvm-passes -o - | \
+// RUN:   FileCheck %s --check-prefixes=CHECK,SPVCHECK
+
+// Test basic lowering of HLSL InterlockedXor to the target intrinsic.
+
+groupshared int  gs_i32;
+groupshared uint gs_u32;
+groupshared int64_t  gs_i64;
+groupshared uint64_t gs_u64;
+
+// CHECK-LABEL: define {{(dso_local |hidden |internal |protected |spir_func )*}}void @{{.*}}test_int_2arg
+// DXCHECK:  call i32 @llvm.dx.interlocked.xor.i32.p3(ptr addrspace(3) {{.*}}@gs_i32{{.*}}, i32 %{{.*}})
+// SPVCHECK: call spir_func i32 @llvm.spv.interlocked.xor.i32.p3(ptr addrspace(3) {{.*}}@gs_i32{{.*}}, i32 %{{.*}})
+export void test_int_2arg(int v) {
+  InterlockedXor(gs_i32, v);
+}
+
+// CHECK-LABEL: define {{(dso_local |hidden |internal |protected |spir_func )*}}void @{{.*}}test_uint_2arg
+// DXCHECK:  call i32 @llvm.dx.interlocked.xor.i32.p3(ptr addrspace(3) {{.*}}@gs_u32{{.*}}, i32 %{{.*}})
+// SPVCHECK: call spir_func i32 @llvm.spv.interlocked.xor.i32.p3(ptr addrspace(3) {{.*}}@gs_u32{{.*}}, i32 %{{.*}})
+export void test_uint_2arg(uint v) {
+  InterlockedXor(gs_u32, v);
+}
+
+// CHECK-LABEL: define {{(dso_local |hidden |internal |protected |spir_func )*}}void @{{.*}}test_int_3arg
+// DXCHECK:  %[[R:.*]] = call i32 @llvm.dx.interlocked.xor.i32.p3(ptr addrspace(3) {{.*}}@gs_i32{{.*}}, i32 %{{.*}})
+// SPVCHECK: %[[R:.*]] = call spir_func i32 @llvm.spv.interlocked.xor.i32.p3(ptr addrspace(3) {{.*}}@gs_i32{{.*}}, i32 %{{.*}})
+// CHECK:    store i32 %[[R]], ptr {{.*}}
+export void test_int_3arg(int v, out int orig) {
+  InterlockedXor(gs_i32, v, orig);
+}
+
+// CHECK-LABEL: define {{(dso_local |hidden |internal |protected |spir_func )*}}void @{{.*}}test_uint_3arg
+// DXCHECK:  %[[R:.*]] = call i32 @llvm.dx.interlocked.xor.i32.p3(ptr addrspace(3) {{.*}}@gs_u32{{.*}}, i32 %{{.*}})
+// SPVCHECK: %[[R:.*]] = call spir_func i32 @llvm.spv.interlocked.xor.i32.p3(ptr addrspace(3) {{.*}}@gs_u32{{.*}}, i32 %{{.*}})
+// CHECK:    store i32 %[[R]], ptr {{.*}}
+export void test_uint_3arg(uint v, out uint orig) {
+  InterlockedXor(gs_u32, v, orig);
+}
+
+// CHECK-LABEL: define {{(dso_local |hidden |internal |protected |spir_func )*}}void @{{.*}}test_int64_2arg
+// DXCHECK:  call i64 @llvm.dx.interlocked.xor.i64.p3(ptr addrspace(3) {{.*}}@gs_i64{{.*}}, i64 %{{.*}})
+// SPVCHECK: call spir_func i64 @llvm.spv.interlocked.xor.i64.p3(ptr addrspace(3) {{.*}}@gs_i64{{.*}}, i64 %{{.*}})
+export void test_int64_2arg(int64_t v) {
+  InterlockedXor(gs_i64, v);
+}
+
+// CHECK-LABEL: define {{(dso_local |hidden |internal |protected |spir_func )*}}void @{{.*}}test_uint64_3arg
+// DXCHECK:  %[[R:.*]] = call i64 @llvm.dx.interlocked.xor.i64.p3(ptr addrspace(3) {{.*}}@gs_u64{{.*}}, i64 %{{.*}})
+// SPVCHECK: %[[R:.*]] = call spir_func i64 @llvm.spv.interlocked.xor.i64.p3(ptr addrspace(3) {{.*}}@gs_u64{{.*}}, i64 %{{.*}})
+// CHECK:    store i64 %[[R]], ptr {{.*}}
+export void test_uint64_3arg(uint64_t v, out uint64_t orig) {
+  InterlockedXor(gs_u64, v, orig);
+}
diff --git a/clang/test/SemaHLSL/BuiltIns/InterlockedXor-errors.hlsl b/clang/test/SemaHLSL/BuiltIns/InterlockedXor-errors.hlsl
new file mode 100644
index 0000000000000..ce9fb07bdf569
--- /dev/null
+++ b/clang/test/SemaHLSL/BuiltIns/InterlockedXor-errors.hlsl
@@ -0,0 +1,100 @@
+// RUN: %clang_cc1 -std=hlsl202x -finclude-default-header \
+// RUN:   -triple dxil-pc-shadermodel6.6-library %s -emit-llvm-only \
+// RUN:   -disable-llvm-passes -verify
+
+// InterlockedXor is provided as a set of address-space-qualified overloads
+// (groupshared/device, {int,uint,int64_t,uint64_t}, 2-arg/3-arg). All arg
+// mismatches surface as "no matching function" with 16 candidates. The
+// candidate notes come from synthesized FunctionDecls with no source
+// location, so they are matched with `@*:*`.
+
+groupshared int   gs_i32;
+groupshared float gs_f32;
+struct S { int x; };
+groupshared S     gs_s;
+
+void too_few(int v) {
+  InterlockedXor(gs_i32); // expected-error{{no matching function for call to 'InterlockedXor'}}
+  // expected-note@*:* 16 {{candidate function}}
+}
+
+void too_many(int v, int extra) {
+  int o;
+  InterlockedXor(gs_i32, v, o, extra); // expected-error{{no matching function for call to 'InterlockedXor'}}
+  // expected-note@*:* 16 {{candidate function}}
+}
+
+// Atomics must operate on actual addresses in groupshared or device memory;
+// passing a plain local (no address space) must not bind to any overload.
+void local_dest(int v) {
+  int dest;
+  InterlockedXor(dest, v); // expected-error{{no matching function for call to 'InterlockedXor'}}
+  // expected-note@*:* 16 {{candidate function}}
+}
+
+void float_dest(float v) {
+  InterlockedXor(gs_f32, v); // expected-error{{no matching function for call to 'InterlockedXor'}}
+  // expected-note@*:* 16 {{candidate function}}
+}
+
+void struct_dest(int v) {
+  InterlockedXor(gs_s, v); // expected-error{{no matching function for call to 'InterlockedXor'}}
+  // expected-note@*:* 16 {{candidate function}}
+}
+
+void mismatched_orig_type(int v) {
+  uint orig;
+  InterlockedXor(gs_i32, v, orig); // expected-error{{no matching function for call to 'InterlockedXor'}}
+  // expected-note@*:* 16 {{candidate function}}
+}
+
+// The tests below exercise direct invocations of the underlying clang builtin
+// `__builtin_hlsl_interlocked_xor`. These bypass overload resolution against
+// the synthesized `InterlockedXor` overload set (the builtin's prototype in
+// Builtins.td is `void (...)`), so each error is produced by the explicit
+// checks in SemaHLSL.cpp rather than by candidate-set rejection.
+
+void direct_too_few() {
+  __builtin_hlsl_interlocked_xor(gs_i32);
+  // expected-error at -1 {{too few arguments to function call, expected at least 2, have 1}}
+}
+
+void direct_too_many(int v, int extra) {
+  int o;
+  __builtin_hlsl_interlocked_xor(gs_i32, v, o, extra);
+  // expected-error at -1 {{too many arguments to function call, expected at most 3, have 4}}
+}
+
+void direct_non_integer_dest() {
+  S local_s;
+  __builtin_hlsl_interlocked_xor(local_s, 1);
+  // expected-error at -1 {{1st argument must be a scalar integer type (was 'S')}}
+}
+
+void direct_nonlvalue_dest(int v) {
+  __builtin_hlsl_interlocked_xor(1, v);
+  // expected-error at -1 {{cannot bind non-lvalue argument '1' to out parameter}}
+}
+
+void direct_mismatched_value() {
+  uint uv = 1u;
+  __builtin_hlsl_interlocked_xor(gs_i32, uv);
+  // expected-error at -1 {{passing 'uint' (aka 'unsigned int') to parameter of incompatible type 'int'}}
+}
+
+void direct_mismatched_orig(int v) {
+  uint orig;
+  __builtin_hlsl_interlocked_xor(gs_i32, v, orig);
+  // expected-error at -1 {{passing 'uint' (aka 'unsigned int') to parameter of incompatible type 'int'}}
+}
+
+void direct_nonlvalue_orig(int v) {
+  __builtin_hlsl_interlocked_xor(gs_i32, v, 1);
+  // expected-error at -1 {{cannot bind non-lvalue argument '1' to out parameter}}
+}
+
+void direct_default_as_dest(int v) {
+  int local;
+  __builtin_hlsl_interlocked_xor(local, v);
+  // expected-error at -1 {{1st argument to atomic builtin must reference groupshared or device memory (was 'int')}}
+}
diff --git a/llvm/include/llvm/IR/IntrinsicsDirectX.td b/llvm/include/llvm/IR/IntrinsicsDirectX.td
index 4dd86270f0d01..c2f960104a263 100644
--- a/llvm/include/llvm/IR/IntrinsicsDirectX.td
+++ b/llvm/include/llvm/IR/IntrinsicsDirectX.td
@@ -265,6 +265,10 @@ def int_dx_interlocked_or :
     DefaultAttrsIntrinsic<[llvm_anyint_ty],
                           [llvm_anyptr_ty, LLVMMatchType<0>],
                           [IntrArgMemOnly]>;
+def int_dx_interlocked_xor :
+    DefaultAttrsIntrinsic<[llvm_anyint_ty],
+                          [llvm_anyptr_ty, LLVMMatchType<0>],
+                          [IntrArgMemOnly]>;
 def int_dx_wave_reduce_max : DefaultAttrsIntrinsic<[llvm_any_ty], [LLVMMatchType<0>], [IntrConvergent, IntrNoMem, IntrTriviallyScalarizable]>;
 def int_dx_wave_reduce_umax : DefaultAttrsIntrinsic<[llvm_anyint_ty], [LLVMMatchType<0>], [IntrConvergent, IntrNoMem, IntrTriviallyScalarizable]>;
 def int_dx_wave_reduce_min : DefaultAttrsIntrinsic<[llvm_any_ty], [LLVMMatchType<0>], [IntrConvergent, IntrNoMem, IntrTriviallyScalarizable]>;
diff --git a/llvm/include/llvm/IR/IntrinsicsSPIRV.td b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
index d948ef78b9584..b58474bc73beb 100644
--- a/llvm/include/llvm/IR/IntrinsicsSPIRV.td
+++ b/llvm/include/llvm/IR/IntrinsicsSPIRV.td
@@ -156,6 +156,10 @@ def int_spv_rsqrt : DefaultAttrsIntrinsic<[LLVMMatchType<0>], [llvm_anyfloat_ty]
       DefaultAttrsIntrinsic<[llvm_anyint_ty],
                             [llvm_anyptr_ty, LLVMMatchType<0>],
                             [IntrArgMemOnly]>;
+  def int_spv_interlocked_xor :
+      DefaultAttrsIntrinsic<[llvm_anyint_ty],
+                            [llvm_anyptr_ty, LLVMMatchType<0>],
+                            [IntrArgMemOnly]>;
   def int_spv_subgroup_ballot : ClangBuiltin<"__builtin_spirv_subgroup_ballot">,
     DefaultAttrsIntrinsic<[llvm_v4i32_ty], [llvm_i1_ty], [IntrConvergent, IntrNoMem]>;
   def int_spv_wave_reduce_umax : DefaultAttrsIntrinsic<[llvm_any_ty], [LLVMMatchType<0>], [IntrConvergent, IntrNoMem]>;
diff --git a/llvm/lib/Target/DirectX/DXILIntrinsicExpansion.cpp b/llvm/lib/Target/DirectX/DXILIntrinsicExpansion.cpp
index 1025015c09a1f..adcadf9cb1e3a 100644
--- a/llvm/lib/Target/DirectX/DXILIntrinsicExpansion.cpp
+++ b/llvm/lib/Target/DirectX/DXILIntrinsicExpansion.cpp
@@ -230,6 +230,7 @@ static bool isIntrinsicExpansion(Function &F) {
   case Intrinsic::dx_radians:
   case Intrinsic::dx_interlocked_add:
   case Intrinsic::dx_interlocked_or:
+  case Intrinsic::dx_interlocked_xor:
   case Intrinsic::usub_sat:
   case Intrinsic::vector_reduce_add:
   case Intrinsic::vector_reduce_fadd:
@@ -1347,6 +1348,9 @@ static bool expandIntrinsic(Function &F, CallInst *Orig) {
   case Intrinsic::dx_interlocked_or:
     Result = expandInterlockedIntrinsic(Orig, AtomicRMWInst::Or);
     break;
+  case Intrinsic::dx_interlocked_xor:
+    Result = expandInterlockedIntrinsic(Orig, AtomicRMWInst::Xor);
+    break;
   case Intrinsic::dx_resource_load_rawbuffer:
     if (expandBufferLoadIntrinsic(Orig, /*IsRaw*/ true))
       return true;
diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
index 11128eadef95f..aa1dac0873291 100644
--- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
@@ -5444,6 +5444,8 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg,
     return selectInterlockedOp(ResVReg, ResType, I, SPIRV::OpAtomicIAdd);
   case Intrinsic::spv_interlocked_or:
     return selectInterlockedOp(ResVReg, ResType, I, SPIRV::OpAtomicOr);
+  case Intrinsic::spv_interlocked_xor:
+    return selectInterlockedOp(ResVReg, ResType, I, SPIRV::OpAtomicXor);
   case Intrinsic::spv_wave_reduce_umax:
     return selectWaveReduceMax(ResVReg, ResType, I, /*IsUnsigned*/ true);
   case Intrinsic::spv_wave_reduce_max:
diff --git a/llvm/test/CodeGen/DirectX/InterlockedXor.ll b/llvm/test/CodeGen/DirectX/InterlockedXor.ll
new file mode 100644
index 0000000000000..d724ec6b79ba0
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/InterlockedXor.ll
@@ -0,0 +1,52 @@
+; RUN: opt -S -dxil-intrinsic-expansion -mtriple=dxil-pc-shadermodel6.6-compute %s | FileCheck %s
+
+; Verify llvm.dx.interlocked.xor expands to atomicrmw xor monotonic.
+
+; Groupshared (addrspace 3) memory tests.
+ at gs_i32 = internal addrspace(3) global i32 zeroinitializer
+ at gs_i64 = internal addrspace(3) global i64 zeroinitializer
+
+define i32 @test_i32(i32 %v) {
+entry:
+; CHECK-LABEL: @test_i32
+; CHECK: %[[R:.*]] = atomicrmw xor ptr addrspace(3) @gs_i32, i32 %v monotonic
+; CHECK: ret i32 %[[R]]
+  %r = call i32 @llvm.dx.interlocked.xor.i32.p3(ptr addrspace(3) @gs_i32, i32 %v)
+  ret i32 %r
+}
+
+define i64 @test_i64(i64 %v) {
+entry:
+; CHECK-LABEL: @test_i64
+; CHECK: %[[R:.*]] = atomicrmw xor ptr addrspace(3) @gs_i64, i64 %v monotonic
+; CHECK: ret i64 %[[R]]
+  %r = call i64 @llvm.dx.interlocked.xor.i64.p3(ptr addrspace(3) @gs_i64, i64 %v)
+  ret i64 %r
+}
+
+; Device (addrspace 1) memory tests.
+ at dev_i32 = external addrspace(1) global i32
+ at dev_i64 = external addrspace(1) global i64
+
+define i32 @test_device_i32(i32 %v) {
+entry:
+; CHECK-LABEL: @test_device_i32
+; CHECK: %[[R:.*]] = atomicrmw xor ptr addrspace(1) @dev_i32, i32 %v monotonic
+; CHECK: ret i32 %[[R]]
+  %r = call i32 @llvm.dx.interlocked.xor.i32.p1(ptr addrspace(1) @dev_i32, i32 %v)
+  ret i32 %r
+}
+
+define i64 @test_device_i64(i64 %v) {
+entry:
+; CHECK-LABEL: @test_device_i64
+; CHECK: %[[R:.*]] = atomicrmw xor ptr addrspace(1) @dev_i64, i64 %v monotonic
+; CHECK: ret i64 %[[R]]
+  %r = call i64 @llvm.dx.interlocked.xor.i64.p1(ptr addrspace(1) @dev_i64, i64 %v)
+  ret i64 %r
+}
+
+declare i32 @llvm.dx.interlocked.xor.i32.p3(ptr addrspace(3), i32)
+declare i64 @llvm.dx.interlocked.xor.i64.p3(ptr addrspace(3), i64)
+declare i32 @llvm.dx.interlocked.xor.i32.p1(ptr addrspace(1), i32)
+declare i64 @llvm.dx.interlocked.xor.i64.p1(ptr addrspace(1), i64)
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/InterlockedXor.ll b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/InterlockedXor.ll
new file mode 100644
index 0000000000000..a91a92a559f73
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/InterlockedXor.ll
@@ -0,0 +1,36 @@
+; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv1.6-vulkan1.3-compute %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv1.6-vulkan1.3-compute %s -o - -filetype=obj | spirv-val %}
+
+; Test lowering of llvm.spv.interlocked.xor to OpAtomicIOr.
+
+; CHECK-DAG: %[[#uint:]] = OpTypeInt 32 0
+; CHECK-DAG: %[[#scope_wg:]] = OpConstant %[[#uint]] 2
+; CHECK-DAG: %[[#scope_dev:]] = OpConstant %[[#uint]] 1
+; CHECK-DAG: %[[#mem_wg:]] = OpConstant %[[#uint]] 256
+; CHECK-DAG: %[[#mem_uniform:]] = OpConstant %[[#uint]] 64
+
+ at gs_i32 = internal addrspace(3) global i32 zeroinitializer
+ at dev_i32 = external addrspace(11) global i32
+
+; Workgroup (addrspace 3) memory tests.
+
+; CHECK-LABEL: Begin function test_i32
+define i32 @test_i32(i32 %v) {
+entry:
+; CHECK: %[[#R:]] = OpAtomicXor %[[#uint]] %[[#]] %[[#scope_wg]] %[[#mem_wg]] %[[#]]
+  %r = call i32 @llvm.spv.interlocked.xor.i32.p3(ptr addrspace(3) @gs_i32, i32 %v)
+  ret i32 %r
+}
+
+; Device / StorageBuffer (addrspace 11) memory tests.
+
+; CHECK-LABEL: Begin function test_device_i32
+define i32 @test_device_i32(i32 %v) {
+entry:
+; CHECK: %[[#R:]] = OpAtomicXor %[[#uint]] %[[#]] %[[#scope_dev]] %[[#mem_uniform]] %[[#]]
+  %r = call i32 @llvm.spv.interlocked.xor.i32.p11(ptr addrspace(11) @dev_i32, i32 %v)
+  ret i32 %r
+}
+
+declare i32 @llvm.spv.interlocked.xor.i32.p3(ptr addrspace(3), i32)
+declare i32 @llvm.spv.interlocked.xor.i32.p11(ptr addrspace(11), i32)
diff --git a/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/InterlockedXor_spv_i64.ll b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/InterlockedXor_spv_i64.ll
new file mode 100644
index 0000000000000..482a1a641b53d
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/hlsl-intrinsics/InterlockedXor_spv_i64.ll
@@ -0,0 +1,37 @@
+; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv1.6-vulkan1.3-compute %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv1.6-vulkan1.3-compute %s -o - -filetype=obj | spirv-val %}
+
+; Test lowering of llvm.spv.interlocked.xor with i64 to OpAtomicIOr.
+
+; CHECK-DAG: %[[#ulong:]] = OpTypeInt 64 0
+; CHECK-DAG: %[[#uint:]] = OpTypeInt 32 0
+; CHECK-DAG: %[[#scope_wg:]] = OpConstant %[[#uint]] 2
+; CHECK-DAG: %[[#scope_dev:]] = OpConstant %[[#uint]] 1
+; CHECK-DAG: %[[#mem_wg:]] = OpConstant %[[#uint]] 256
+; CHECK-DAG: %[[#mem_uniform:]] = OpConstant %[[#uint]] 64
+
+ at gs_i64 = internal addrspace(3) global i64 zeroinitializer
+ at dev_i64 = external addrspace(11) global i64
+
+; Workgroup (addrspace 3) memory test.
+
+; CHECK-LABEL: Begin function test_i64
+define i64 @test_i64(i64 %v) {
+entry:
+; CHECK: %[[#R:]] = OpAtomicXor %[[#ulong]] %[[#]] %[[#scope_wg]] %[[#mem_wg]] %[[#]]
+  %r = call i64 @llvm.spv.interlocked.xor.i64.p3(ptr addrspace(3) @gs_i64, i64 %v)
+  ret i64 %r
+}
+
+; Device / StorageBuffer (addrspace 11) memory test.
+
+; CHECK-LABEL: Begin function test_device_i64
+define i64 @test_device_i64(i64 %v) {
+entry:
+; CHECK: %[[#R:]] = OpAtomicXor %[[#ulong]] %[[#]] %[[#scope_dev]] %[[#mem_uniform]] %[[#]]
+  %r = call i64 @llvm.spv.interlocked.xor.i64.p11(ptr addrspace(11) @dev_i64, i64 %v)
+  ret i64 %r
+}
+
+declare i64 @llvm.spv.interlocked.xor.i64.p3(ptr addrspace(3), i64)
+declare i64 @llvm.spv.interlocked.xor.i64.p11(ptr addrspace(11), i64)

>From c9629d029f3cd3269fe1dd85825d82ca2b90604b Mon Sep 17 00:00:00 2001
From: Alexander Johnston <alexander.johnston at amd.com>
Date: Mon, 13 Jul 2026 18:52:02 +0100
Subject: [PATCH 2/3] Fix format error

---
 clang/lib/CodeGen/CGHLSLBuiltins.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/clang/lib/CodeGen/CGHLSLBuiltins.cpp b/clang/lib/CodeGen/CGHLSLBuiltins.cpp
index d5d0d9b09bd46..9d33e7c5c868a 100644
--- a/clang/lib/CodeGen/CGHLSLBuiltins.cpp
+++ b/clang/lib/CodeGen/CGHLSLBuiltins.cpp
@@ -1467,9 +1467,9 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID,
                                "hlsl.interlocked.or");
   }
   case Builtin::BI__builtin_hlsl_interlocked_xor: {
-    return handleInterlockedOp(*this, E,
-                               CGM.getHLSLRuntime().getInterlockedXorIntrinsic(),
-                               "hlsl.interlocked.xor");
+    return handleInterlockedOp(
+        *this, E, CGM.getHLSLRuntime().getInterlockedXorIntrinsic(),
+        "hlsl.interlocked.xor");
   }
   case Builtin::BI__builtin_hlsl_wave_active_ballot: {
     [[maybe_unused]] Value *Op = EmitScalarExpr(E->getArg(0));

>From 5b695925c296d9ecc249ef330932e03c9ce2dd18 Mon Sep 17 00:00:00 2001
From: Alexander Johnston <alexander.johnston at amd.com>
Date: Fri, 24 Jul 2026 16:00:13 +0100
Subject: [PATCH 3/3] Add resource functions and tests for InterlockedXor

---
 clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp |  4 ++
 .../CodeGenHLSL/builtins/InterlockedXor.hlsl  |  2 +-
 .../builtins/RWBuffer-Interlocked.hlsl        |  5 ++
 .../RWByteAddressBuffer-InterlockedXor.hlsl   | 62 +++++++++++++++++++
 ...deredByteAddressBuffer-InterlockedXor.hlsl | 45 ++++++++++++++
 5 files changed, 117 insertions(+), 1 deletion(-)
 create mode 100644 clang/test/CodeGenHLSL/builtins/RWByteAddressBuffer-InterlockedXor.hlsl
 create mode 100644 clang/test/CodeGenHLSL/builtins/RasterizerOrderedByteAddressBuffer-InterlockedXor.hlsl

diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
index 65b2da923d461..587e669e5cb5e 100644
--- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
+++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
@@ -1646,6 +1646,8 @@ BuiltinTypeDeclBuilder::addByteAddressBufferInterlockedMethods() {
                                         "__builtin_hlsl_interlocked_add");
   addByteAddressBufferInterlockedMethod("InterlockedOr", AST.UnsignedIntTy,
                                         "__builtin_hlsl_interlocked_or");
+  addByteAddressBufferInterlockedMethod("InterlockedXor", AST.UnsignedIntTy,
+                                        "__builtin_hlsl_interlocked_xor");
 
   // Skip synthesizing the 64 bit methods on DXIL targets older than SM 6.6.
   const llvm::Triple &TT = AST.getTargetInfo().getTriple();
@@ -1659,6 +1661,8 @@ BuiltinTypeDeclBuilder::addByteAddressBufferInterlockedMethods() {
                                           "__builtin_hlsl_interlocked_add");
     addByteAddressBufferInterlockedMethod("InterlockedOr64", AST.UnsignedLongTy,
                                           "__builtin_hlsl_interlocked_or");
+    addByteAddressBufferInterlockedMethod("InterlockedXor64", AST.UnsignedLongTy,
+                                          "__builtin_hlsl_interlocked_xor");
   }
 
   return *this;
diff --git a/clang/test/CodeGenHLSL/builtins/InterlockedXor.hlsl b/clang/test/CodeGenHLSL/builtins/InterlockedXor.hlsl
index 8987d2eed770c..8cd799c09eb01 100644
--- a/clang/test/CodeGenHLSL/builtins/InterlockedXor.hlsl
+++ b/clang/test/CodeGenHLSL/builtins/InterlockedXor.hlsl
@@ -6,7 +6,7 @@
 // RUN:   spirv-pc-vulkan-library %s -emit-llvm -disable-llvm-passes -o - | \
 // RUN:   FileCheck %s --check-prefixes=CHECK,SPVCHECK
 
-// Test basic lowering of HLSL InterlockedXor to the target intrinsic.
+// Test basic lowering of HLSL InterlockedXor to `atomicrmw or monotonic`.
 
 groupshared int  gs_i32;
 groupshared uint gs_u32;
diff --git a/clang/test/CodeGenHLSL/builtins/RWBuffer-Interlocked.hlsl b/clang/test/CodeGenHLSL/builtins/RWBuffer-Interlocked.hlsl
index 3eb8fbdee0a11..4d7050ba3a43c 100644
--- a/clang/test/CodeGenHLSL/builtins/RWBuffer-Interlocked.hlsl
+++ b/clang/test/CodeGenHLSL/builtins/RWBuffer-Interlocked.hlsl
@@ -21,13 +21,18 @@ RWBuffer<int> Out : register(u0);
 // DXCHECK:  atomicrmw add ptr %[[PTR1]], i32 1 monotonic
 // DXCHECK:  %[[PTR2:.*]] = call {{.*}} @llvm.dx.resource.getpointer.p0.tdx.TypedBuffer_i32_1_0_1t.i32(target("dx.TypedBuffer", i32, 1, 0, 1) %{{.*}}, i32 %{{.*}})
 // DXCHECK:  atomicrmw or ptr %[[PTR2]], i32 1 monotonic
+// DXCHECK:  %[[PTR3:.*]] = call {{.*}} @llvm.dx.resource.getpointer.p0.tdx.TypedBuffer_i32_1_0_1t.i32(target("dx.TypedBuffer", i32, 1, 0, 1) %{{.*}}, i32 %{{.*}})
+// DXCHECK:  atomicrmw xor ptr %[[PTR3]], i32 1 monotonic
 // SPVCHECK: %[[PTR1:.*]] = call {{.*}} @llvm.spv.resource.getpointer.{{.*}}(target("spirv.{{Image|SignedImage}}", i32, {{.*}}) %{{.*}}, i32 %{{.*}})
 // SPVCHECK: atomicrmw add ptr addrspace(11) %[[PTR1]], i32 1 monotonic
 // SPVCHECK: %[[PTR2:.*]] = call {{.*}} @llvm.spv.resource.getpointer.{{.*}}(target("spirv.{{Image|SignedImage}}", i32, {{.*}}) %{{.*}}, i32 %{{.*}})
 // SPVCHECK: atomicrmw or ptr addrspace(11) %[[PTR2]], i32 1 monotonic
+// SPVCHECK: %[[PTR3:.*]] = call {{.*}} @llvm.spv.resource.getpointer.{{.*}}(target("spirv.{{Image|SignedImage}}", i32, {{.*}}) %{{.*}}, i32 %{{.*}})
+// SPVCHECK: atomicrmw xor ptr addrspace(11) %[[PTR3]], i32 1 monotonic
 [shader("compute")]
 [numthreads(1,1,1)]
 void main(uint3 id : SV_DispatchThreadID) {
   InterlockedAdd(Out[id.x], 1);
   InterlockedOr(Out[id.x], 1);
+  InterlockedXor(Out[id.x], 1);
 }
diff --git a/clang/test/CodeGenHLSL/builtins/RWByteAddressBuffer-InterlockedXor.hlsl b/clang/test/CodeGenHLSL/builtins/RWByteAddressBuffer-InterlockedXor.hlsl
new file mode 100644
index 0000000000000..f4f788b7968a5
--- /dev/null
+++ b/clang/test/CodeGenHLSL/builtins/RWByteAddressBuffer-InterlockedXor.hlsl
@@ -0,0 +1,62 @@
+// RUN: %clang_cc1 -std=hlsl202x -finclude-default-header -triple \
+// RUN:   dxil-pc-shadermodel6.6-library %s -emit-llvm -disable-llvm-passes -o - | \
+// RUN:   FileCheck %s --check-prefixes=CHECK,DXCHECK
+
+// RUN: %clang_cc1 -std=hlsl202x -finclude-default-header -triple \
+// RUN:   spirv-pc-vulkan1.3-library %s -emit-llvm -disable-llvm-passes -o - | \
+// RUN:   FileCheck %s --check-prefixes=CHECK,SPVCHECK
+
+// Test that the RWByteAddressBuffer::InterlockedXor and InterlockedXor64
+// member methods lower to `resource_getpointer -> atomicrmw or`, and that
+// the 3-argument overload stores the returned original value through the
+// out parameter, for both DXIL and SPIR-V targets.
+
+RWByteAddressBuffer BAB : register(u0);
+
+// 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:  atomicrmw xor ptr %[[PTR]], i32 %{{.*}} monotonic
+// SPVCHECK: %[[HANDLE:.*]] = load target("spirv.VulkanBuffer", [0 x i8], 12, 1), ptr {{.*}}
+// SPVCHECK: %[[PTR:.*]] = call ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.VulkanBuffer_a0i8_12_1t.i32(target("spirv.VulkanBuffer", [0 x i8], 12, 1) %[[HANDLE]], i32 %{{.*}})
+// SPVCHECK: atomicrmw xor ptr addrspace(11) %[[PTR]], i32 %{{.*}} monotonic
+export void test_bab_int_2arg(uint off, int v) {
+  BAB.InterlockedXor(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:.*]] = atomicrmw xor ptr %[[PTR]], i32 %{{.*}} monotonic
+// DXCHECK:  store i32 %[[R]], ptr {{.*}}
+// SPVCHECK: %[[HANDLE:.*]] = load target("spirv.VulkanBuffer", [0 x i8], 12, 1), ptr {{.*}}
+// SPVCHECK: %[[PTR:.*]] = call ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.VulkanBuffer_a0i8_12_1t.i32(target("spirv.VulkanBuffer", [0 x i8], 12, 1) %[[HANDLE]], i32 %{{.*}})
+// SPVCHECK: %[[R:.*]] = atomicrmw xor ptr addrspace(11) %[[PTR]], i32 %{{.*}} monotonic
+// SPVCHECK: store i32 %[[R]], ptr {{.*}}
+export void test_bab_uint_3arg(uint off, uint v, out uint orig) {
+  BAB.InterlockedXor(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:  atomicrmw xor ptr %[[PTR]], i64 %{{.*}} monotonic
+// SPVCHECK: %[[HANDLE:.*]] = load target("spirv.VulkanBuffer", [0 x i8], 12, 1), ptr {{.*}}
+// SPVCHECK: %[[PTR:.*]] = call ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.VulkanBuffer_a0i8_12_1t.i32(target("spirv.VulkanBuffer", [0 x i8], 12, 1) %[[HANDLE]], i32 %{{.*}})
+// SPVCHECK: atomicrmw xor ptr addrspace(11) %[[PTR]], i64 %{{.*}} monotonic
+export void test_bab_int64_2arg(uint off, int64_t v) {
+  BAB.InterlockedXor64(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:.*]] = atomicrmw xor ptr %[[PTR]], i64 %{{.*}} monotonic
+// DXCHECK:  store i64 %[[R]], ptr {{.*}}
+// SPVCHECK: %[[HANDLE:.*]] = load target("spirv.VulkanBuffer", [0 x i8], 12, 1), ptr {{.*}}
+// SPVCHECK: %[[PTR:.*]] = call ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.VulkanBuffer_a0i8_12_1t.i32(target("spirv.VulkanBuffer", [0 x i8], 12, 1) %[[HANDLE]], i32 %{{.*}})
+// SPVCHECK: %[[R:.*]] = atomicrmw xor ptr addrspace(11) %[[PTR]], i64 %{{.*}} monotonic
+// SPVCHECK: store i64 %[[R]], ptr {{.*}}
+export void test_bab_uint64_3arg(uint off, uint64_t v, out uint64_t orig) {
+  BAB.InterlockedXor64(off, v, orig);
+}
diff --git a/clang/test/CodeGenHLSL/builtins/RasterizerOrderedByteAddressBuffer-InterlockedXor.hlsl b/clang/test/CodeGenHLSL/builtins/RasterizerOrderedByteAddressBuffer-InterlockedXor.hlsl
new file mode 100644
index 0000000000000..b8dfa4f81ee73
--- /dev/null
+++ b/clang/test/CodeGenHLSL/builtins/RasterizerOrderedByteAddressBuffer-InterlockedXor.hlsl
@@ -0,0 +1,45 @@
+// RUN: %clang_cc1 -std=hlsl202x -finclude-default-header -triple \
+// RUN:   dxil-pc-shadermodel6.6-library %s -emit-llvm -disable-llvm-passes -o - | \
+// RUN:   FileCheck %s --check-prefixes=CHECK,DXCHECK
+
+// SPIR-V codegen for RasterizerOrderedByteAddressBuffer is not implemented
+// yet (asserts in clang/lib/CodeGen/Targets/SPIR.cpp on
+// `!ResAttrs.IsROV && "Rasterizer order views not implemented for SPIR-V yet"`).
+// Add a `spirv-pc-vulkan1.3-library` RUN line here when SPIR-V ROV support
+// lands.
+
+RasterizerOrderedByteAddressBuffer ROVB : register(u1);
+
+// CHECK-LABEL: define 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: atomicrmw xor ptr %[[PTR]], i32 %{{.*}} monotonic
+export void test_rovb_int_2arg(uint off, int v) {
+  ROVB.InterlockedXor(off, v);
+}
+
+// CHECK-LABEL: define 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:.*]] = atomicrmw xor ptr %[[PTR]], i32 %{{.*}} monotonic
+// DXCHECK: store i32 %[[R]], ptr {{.*}}
+export void test_rovb_uint_3arg(uint off, uint v, out uint orig) {
+  ROVB.InterlockedXor(off, v, orig);
+}
+
+// CHECK-LABEL: define 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: atomicrmw xor ptr %[[PTR]], i64 %{{.*}} monotonic
+export void test_rovb_int64_2arg(uint off, int64_t v) {
+  ROVB.InterlockedXor64(off, v);
+}
+
+// CHECK-LABEL: define 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:.*]] = atomicrmw xor ptr %[[PTR]], i64 %{{.*}} monotonic
+// DXCHECK: store i64 %[[R]], ptr {{.*}}
+export void test_rovb_uint64_3arg(uint off, uint64_t v, out uint64_t orig) {
+  ROVB.InterlockedXor64(off, v, orig);
+}



More information about the cfe-commits mailing list