[llvm-branch-commits] [clang] [HLSL] Add `InterlockedCompareExchangeFloatBitwise` function and resource methods (PR #222167)

Joshua Batista via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Thu Sep 10 11:07:46 PDT 2026


https://github.com/bob80905 updated https://github.com/llvm/llvm-project/pull/222167

>From 6b84ad3cf678bc9317a222a5bd962e4497a5c88d Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Tue, 8 Sep 2026 14:02:39 -0700
Subject: [PATCH] First attempt implementing
 InterlockedCompareExchangeFloatBitwise

---
 clang/include/clang/Basic/Builtins.td         |   7 ++
 clang/lib/CodeGen/CGHLSLBuiltins.cpp          |   7 +-
 clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp |   6 +-
 clang/lib/Sema/HLSLExternalSemaSource.cpp     |   4 +
 clang/lib/Sema/SemaHLSL.cpp                   |   7 ++
 ...nterlockedCompareExchangeFloatBitwise.hlsl |  43 +++++++
 .../builtins/RWBuffer-Interlocked.hlsl        |  10 ++
 ...nterlockedCompareExchangeFloatBitwise.hlsl |  32 +++++
 ...nterlockedCompareExchangeFloatBitwise.hlsl |  25 ++++
 ...ockedCompareExchangeFloatBitwise-sm60.hlsl |  42 +++++++
 ...kedCompareExchangeFloatBitwise-errors.hlsl | 119 ++++++++++++++++++
 11 files changed, 299 insertions(+), 3 deletions(-)
 create mode 100644 clang/test/CodeGenHLSL/builtins/InterlockedCompareExchangeFloatBitwise.hlsl
 create mode 100644 clang/test/CodeGenHLSL/builtins/RWByteAddressBuffer-InterlockedCompareExchangeFloatBitwise.hlsl
 create mode 100644 clang/test/CodeGenHLSL/builtins/RasterizerOrderedByteAddressBuffer-InterlockedCompareExchangeFloatBitwise.hlsl
 create mode 100644 clang/test/SemaHLSL/BuiltIns/ByteAddressBuffer-InterlockedCompareExchangeFloatBitwise-sm60.hlsl
 create mode 100644 clang/test/SemaHLSL/BuiltIns/InterlockedCompareExchangeFloatBitwise-errors.hlsl

diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index af9a743716e6e..5b2c8bb06a594 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -5565,6 +5565,13 @@ def HLSLInterlockedCompareExchange : LangBuiltin<"HLSL_LANG"> {
   let Prototype = "void (...)";
 }
 
+def HLSLInterlockedCompareExchangeFloatBitwise : LangBuiltin<"HLSL_LANG"> {
+  let Spellings = ["__builtin_hlsl_interlocked_compare_exchange_float_bitwise"];
+  // Prevent inadvertent float -> double arg promotion.
+  let Attributes = [NoThrow, CustomTypeChecking];
+  let Prototype = "void (...)";
+}
+
 def HLSLInterlockedCompareStore : LangBuiltin<"HLSL_LANG"> {
   let Spellings = ["__builtin_hlsl_interlocked_compare_store"];
   // Prevent inadvertent float -> double arg promotion.
diff --git a/clang/lib/CodeGen/CGHLSLBuiltins.cpp b/clang/lib/CodeGen/CGHLSLBuiltins.cpp
index 3af0b523df31a..799694e8637cb 100644
--- a/clang/lib/CodeGen/CGHLSLBuiltins.cpp
+++ b/clang/lib/CodeGen/CGHLSLBuiltins.cpp
@@ -363,9 +363,11 @@ static Value *handleInterlockedCompareOp(CodeGenFunction &CGF,
   // `cmpxchg` takes an integer or a pointer, so the float-bitwise operations
   // work on the bit pattern of the float. This is what those operations mean,
   // and DXIL and SPIR-V both need the integer form.
+  llvm::Type *FloatTy = nullptr;
   if (Compare->getType()->isFloatingPointTy()) {
+    FloatTy = Compare->getType();
     llvm::Type *IntTy =
-        CGF.Builder.getIntNTy(Compare->getType()->getPrimitiveSizeInBits());
+        CGF.Builder.getIntNTy(FloatTy->getPrimitiveSizeInBits());
     Compare = CGF.Builder.CreateBitCast(Compare, IntTy);
     Val = CGF.Builder.CreateBitCast(Val, IntTy);
     DestAddr = DestAddr.withElementType(IntTy);
@@ -382,6 +384,8 @@ static Value *handleInterlockedCompareOp(CodeGenFunction &CGF,
   // `cmpxchg` yields a { previous value, success } pair. HLSL reports only the
   // previous value, through the `original_value` reference parameter.
   Value *Original = CGF.Builder.CreateExtractValue(Pair, 0);
+  if (FloatTy)
+    Original = CGF.Builder.CreateBitCast(Original, FloatTy);
   LValue OrigLV = CGF.EmitLValue(E->getArg(3));
   CGF.EmitStoreThroughLValue(RValue::get(Original), OrigLV);
   return Original;
@@ -1529,6 +1533,7 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID,
     return handleInterlockedOp(*this, E, llvm::AtomicRMWInst::And);
   }
   case Builtin::BI__builtin_hlsl_interlocked_compare_exchange:
+  case Builtin::BI__builtin_hlsl_interlocked_compare_exchange_float_bitwise:
   case Builtin::BI__builtin_hlsl_interlocked_compare_store:
   case Builtin::BI__builtin_hlsl_interlocked_compare_store_float_bitwise: {
     return handleInterlockedCompareOp(*this, E);
diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
index 9918a09338482..d59d609866c6d 100644
--- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
+++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
@@ -1757,11 +1757,13 @@ BuiltinTypeDeclBuilder::addByteAddressBufferInterlockedMethods() {
       "InterlockedCompareExchange", AST.UnsignedIntTy,
       "__builtin_hlsl_interlocked_compare_exchange",
       /*WithOriginalValue=*/true);
+  addByteAddressBufferInterlockedCompareMethod(
+      "InterlockedCompareExchangeFloatBitwise", AST.FloatTy,
+      "__builtin_hlsl_interlocked_compare_exchange_float_bitwise",
+      /*WithOriginalValue=*/true);
   addByteAddressBufferInterlockedCompareMethod(
       "InterlockedCompareStore", AST.UnsignedIntTy,
       "__builtin_hlsl_interlocked_compare_store");
-  // The float-bitwise compare reuses the 32-bit integer DXIL operation, so it
-  // needs no capability bits and works from SM 6.0.
   addByteAddressBufferInterlockedCompareMethod(
       "InterlockedCompareStoreFloatBitwise", AST.FloatTy,
       "__builtin_hlsl_interlocked_compare_store_float_bitwise");
diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp
index 20efbf4cde861..df18c0e298eaa 100644
--- a/clang/lib/Sema/HLSLExternalSemaSource.cpp
+++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp
@@ -953,6 +953,10 @@ void HLSLExternalSemaSource::defineHLSLAtomicIntrinsics() {
       *SemaPtr, HLSLNamespace, "InterlockedCompareExchange",
       "__builtin_hlsl_interlocked_compare_exchange",
       AtomicOverloadShape::CompareExchange);
+  defineHLSLInterlockedCompareFunc(
+      *SemaPtr, HLSLNamespace, "InterlockedCompareExchangeFloatBitwise",
+      "__builtin_hlsl_interlocked_compare_exchange_float_bitwise",
+      AtomicOverloadShape::CompareExchange, /*FloatOnly=*/true);
   defineHLSLInterlockedCompareFunc(*SemaPtr, HLSLNamespace,
                                    "InterlockedCompareStore",
                                    "__builtin_hlsl_interlocked_compare_store",
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 244f0442ccc11..461b8c9670cd0 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -4837,6 +4837,13 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
                                 /*ReportsOriginalValue=*/true))
       return true;
     break;
+  case Builtin::BI__builtin_hlsl_interlocked_compare_exchange_float_bitwise:
+    // The same shape, but it compares bit patterns, so float alone.
+    if (CheckInterlockedBuiltin(SemaRef, TheCall, /*MinArgs=*/4, /*MaxArgs=*/4,
+                                InterlockedDest::Float,
+                                /*ReportsOriginalValue=*/true))
+      return true;
+    break;
   // Note these are llvm builtins that we want to catch invalid intrinsic
   // generation. Normal handling of these builtins will occur elsewhere.
   case Builtin::BI__builtin_elementwise_bitreverse: {
diff --git a/clang/test/CodeGenHLSL/builtins/InterlockedCompareExchangeFloatBitwise.hlsl b/clang/test/CodeGenHLSL/builtins/InterlockedCompareExchangeFloatBitwise.hlsl
new file mode 100644
index 0000000000000..42be35d40b967
--- /dev/null
+++ b/clang/test/CodeGenHLSL/builtins/InterlockedCompareExchangeFloatBitwise.hlsl
@@ -0,0 +1,43 @@
+// RUN: %clang_cc1 -std=hlsl2021 -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=hlsl2021 -finclude-default-header -triple \
+// RUN:   spirv-pc-vulkan-library %s -emit-llvm -disable-llvm-passes -o - | \
+// RUN:   FileCheck %s --check-prefixes=CHECK,SPVCHECK
+
+// Test basic lowering of HLSL InterlockedCompareExchangeFloatBitwise to
+// `cmpxchg monotonic`. `cmpxchg` takes an integer, so the float arguments
+// become their bit patterns first, and the reported value becomes a float
+// again before the store.
+
+groupshared float gs_f32;
+
+// CHECK-LABEL: define {{.*}}void @{{.*}}test_float
+// CHECK: [[CMP:%.*]] = bitcast float %{{.*}} to i32
+// CHECK-NEXT: [[VAL:%.*]] = bitcast float %{{.*}} to i32
+// DXCHECK-NEXT:  [[PAIR:%.*]] = cmpxchg ptr addrspace(3) {{.*}}@gs_f32{{.*}}, i32 [[CMP]], i32 [[VAL]] syncscope("workgroup") monotonic monotonic
+// SPVCHECK-NEXT: [[PAIR:%.*]] = cmpxchg ptr addrspace(3) {{.*}}@gs_f32{{.*}}, i32 [[CMP]], i32 [[VAL]] syncscope("workgroup") monotonic monotonic
+// CHECK-NEXT: [[RES:%.*]] = extractvalue { i32, i1 } [[PAIR]], 0
+// CHECK-NEXT: [[ORIG:%.*]] = bitcast i32 [[RES]] to float
+// CHECK-NEXT: store float [[ORIG]], ptr %orig
+export void test_float(float cmp, float v) {
+  float orig;
+  InterlockedCompareExchangeFloatBitwise(gs_f32, cmp, v, orig);
+}
+
+// A device-address-space destination uses the "device" scope instead.
+RWBuffer<float> Buf : register(u0);
+
+// CHECK-LABEL: define {{.*}}void @{{.*}}test_device
+// CHECK: [[CMP:%.*]] = bitcast float %{{.*}} to i32
+// CHECK-NEXT: [[VAL:%.*]] = bitcast float %{{.*}} to i32
+// DXCHECK-NEXT:  [[PAIR:%.*]] = cmpxchg ptr %{{.*}}, i32 [[CMP]], i32 [[VAL]] syncscope("device") monotonic monotonic
+// SPVCHECK-NEXT: [[PAIR:%.*]] = cmpxchg ptr addrspace(11) %{{.*}}, i32 [[CMP]], i32 [[VAL]] syncscope("device") monotonic monotonic
+// CHECK-NEXT: [[RES:%.*]] = extractvalue { i32, i1 } [[PAIR]], 0
+// CHECK-NEXT: [[ORIG:%.*]] = bitcast i32 [[RES]] to float
+// CHECK-NEXT: store float [[ORIG]], ptr %orig
+export void test_device(float cmp, float v) {
+  float orig;
+  InterlockedCompareExchangeFloatBitwise(Buf[0], cmp, v, orig);
+}
diff --git a/clang/test/CodeGenHLSL/builtins/RWBuffer-Interlocked.hlsl b/clang/test/CodeGenHLSL/builtins/RWBuffer-Interlocked.hlsl
index b85e40a9b1093..603e081df841e 100644
--- a/clang/test/CodeGenHLSL/builtins/RWBuffer-Interlocked.hlsl
+++ b/clang/test/CodeGenHLSL/builtins/RWBuffer-Interlocked.hlsl
@@ -46,6 +46,10 @@ RWBuffer<float> FOut : register(u2);
 // DXCHECK:  cmpxchg ptr %[[PTR11]], i32 1, i32 2 syncscope("device") monotonic monotonic
 // DXCHECK:  %[[PTR12:.*]] = call {{.*}} @llvm.dx.resource.getpointer.p0.tdx.TypedBuffer_f32_1_0_0t.i32(target("dx.TypedBuffer", float, 1, 0, 0) %{{.*}}, i32 %{{.*}})
 // DXCHECK:  cmpxchg ptr %[[PTR12]], i32 1065353216, i32 1073741824 syncscope("device") monotonic monotonic
+// DXCHECK:  %[[PTR13:.*]] = call {{.*}} @llvm.dx.resource.getpointer.p0.tdx.TypedBuffer_f32_1_0_0t.i32(target("dx.TypedBuffer", float, 1, 0, 0) %{{.*}}, i32 %{{.*}})
+// DXCHECK:  %[[PAIR13:.*]] = cmpxchg ptr %[[PTR13]], i32 1065353216, i32 1073741824 syncscope("device") monotonic monotonic
+// DXCHECK:  %[[RES13:.*]] = extractvalue { i32, i1 } %[[PAIR13]], 0
+// DXCHECK:  bitcast i32 %[[RES13]] to float
 // SPVCHECK: %[[PTR1:.*]] = call {{.*}} @llvm.spv.resource.getpointer.{{.*}}(target("spirv.SignedImage", i32, {{.*}}) %{{.*}}, i32 %{{.*}})
 // SPVCHECK: atomicrmw add ptr addrspace(11) %[[PTR1]], i32 1 syncscope("device") monotonic
 // SPVCHECK: %[[PTR2:.*]] = call {{.*}} @llvm.spv.resource.getpointer.{{.*}}(target("spirv.SignedImage", i32, {{.*}}) %{{.*}}, i32 %{{.*}})
@@ -70,6 +74,10 @@ RWBuffer<float> FOut : register(u2);
 // SPVCHECK: cmpxchg ptr addrspace(11) %[[PTR11]], i32 1, i32 2 syncscope("device") monotonic monotonic
 // SPVCHECK: %[[PTR12:.*]] = call {{.*}} @llvm.spv.resource.getpointer.{{.*}}(target("spirv.Image", float, {{.*}}) %{{.*}}, i32 %{{.*}})
 // SPVCHECK: cmpxchg ptr addrspace(11) %[[PTR12]], i32 1065353216, i32 1073741824 syncscope("device") monotonic monotonic
+// SPVCHECK: %[[PTR13:.*]] = call {{.*}} @llvm.spv.resource.getpointer.{{.*}}(target("spirv.Image", float, {{.*}}) %{{.*}}, i32 %{{.*}})
+// SPVCHECK: %[[PAIR13:.*]] = cmpxchg ptr addrspace(11) %[[PTR13]], i32 1065353216, i32 1073741824 syncscope("device") monotonic monotonic
+// SPVCHECK: %[[RES13:.*]] = extractvalue { i32, i1 } %[[PAIR13]], 0
+// SPVCHECK: bitcast i32 %[[RES13]] to float
 [shader("compute")]
 [numthreads(1,1,1)]
 void main(uint3 id : SV_DispatchThreadID) {
@@ -86,4 +94,6 @@ void main(uint3 id : SV_DispatchThreadID) {
   InterlockedCompareStore(Out[id.x], 1, 2);
   InterlockedCompareExchange(Out[id.x], 1, 2, orig);
   InterlockedCompareStoreFloatBitwise(FOut[id.x], 1.0f, 2.0f);
+  float forig;
+  InterlockedCompareExchangeFloatBitwise(FOut[id.x], 1.0f, 2.0f, forig);
 }
diff --git a/clang/test/CodeGenHLSL/builtins/RWByteAddressBuffer-InterlockedCompareExchangeFloatBitwise.hlsl b/clang/test/CodeGenHLSL/builtins/RWByteAddressBuffer-InterlockedCompareExchangeFloatBitwise.hlsl
new file mode 100644
index 0000000000000..912565da3400e
--- /dev/null
+++ b/clang/test/CodeGenHLSL/builtins/RWByteAddressBuffer-InterlockedCompareExchangeFloatBitwise.hlsl
@@ -0,0 +1,32 @@
+// 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::InterlockedCompareExchangeFloatBitwise
+// member method lowers to `resource_getpointer -> cmpxchg`, for both DXIL and
+// SPIR-V targets. The float arguments become their bit patterns first, because
+// `cmpxchg` takes an integer. The method takes the reported value by
+// reference, so the pointer is loaded before the store.
+
+RWByteAddressBuffer BAB : register(u0);
+
+// CHECK-LABEL: define {{.*}}void @{{.*}}test_bab_float
+// 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 %{{.*}})
+// 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 %{{.*}})
+// CHECK: %[[CMP:.*]] = bitcast float %{{.*}} to i32
+// CHECK-NEXT: %[[VAL:.*]] = bitcast float %{{.*}} to i32
+// DXCHECK-NEXT:  %[[PAIR:.*]] = cmpxchg ptr %[[PTR]], i32 %[[CMP]], i32 %[[VAL]] syncscope("device") monotonic monotonic
+// SPVCHECK-NEXT: %[[PAIR:.*]] = cmpxchg ptr addrspace(11) %[[PTR]], i32 %[[CMP]], i32 %[[VAL]] syncscope("device") monotonic monotonic
+// CHECK-NEXT: %[[RES:.*]] = extractvalue { i32, i1 } %[[PAIR]], 0
+// CHECK-NEXT: %[[ORIG:.*]] = bitcast i32 %[[RES]] to float
+// CHECK-NEXT: %[[DEST:.*]] = load ptr, ptr %OriginalValue.addr
+// CHECK-NEXT: store float %[[ORIG]], ptr %[[DEST]]
+export void test_bab_float(uint off, float cmp, float v, out float orig) {
+  BAB.InterlockedCompareExchangeFloatBitwise(off, cmp, v, orig);
+}
diff --git a/clang/test/CodeGenHLSL/builtins/RasterizerOrderedByteAddressBuffer-InterlockedCompareExchangeFloatBitwise.hlsl b/clang/test/CodeGenHLSL/builtins/RasterizerOrderedByteAddressBuffer-InterlockedCompareExchangeFloatBitwise.hlsl
new file mode 100644
index 0000000000000..67df534c00ae2
--- /dev/null
+++ b/clang/test/CodeGenHLSL/builtins/RasterizerOrderedByteAddressBuffer-InterlockedCompareExchangeFloatBitwise.hlsl
@@ -0,0 +1,25 @@
+// 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_float
+// 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: %[[CMP:.*]] = bitcast float %{{.*}} to i32
+// DXCHECK-NEXT: %[[VAL:.*]] = bitcast float %{{.*}} to i32
+// DXCHECK-NEXT: %[[PAIR:.*]] = cmpxchg ptr %[[PTR]], i32 %[[CMP]], i32 %[[VAL]] syncscope("device") monotonic monotonic
+// DXCHECK-NEXT: %[[RES:.*]] = extractvalue { i32, i1 } %[[PAIR]], 0
+// DXCHECK-NEXT: %[[ORIG:.*]] = bitcast i32 %[[RES]] to float
+// DXCHECK-NEXT: %[[DEST:.*]] = load ptr, ptr %OriginalValue.addr
+// DXCHECK-NEXT: store float %[[ORIG]], ptr %[[DEST]]
+export void test_rovb_float(uint off, float cmp, float v, out float orig) {
+  ROVB.InterlockedCompareExchangeFloatBitwise(off, cmp, v, orig);
+}
diff --git a/clang/test/SemaHLSL/BuiltIns/ByteAddressBuffer-InterlockedCompareExchangeFloatBitwise-sm60.hlsl b/clang/test/SemaHLSL/BuiltIns/ByteAddressBuffer-InterlockedCompareExchangeFloatBitwise-sm60.hlsl
new file mode 100644
index 0000000000000..2adc6d1e1e552
--- /dev/null
+++ b/clang/test/SemaHLSL/BuiltIns/ByteAddressBuffer-InterlockedCompareExchangeFloatBitwise-sm60.hlsl
@@ -0,0 +1,42 @@
+// RUN: %clang_cc1 -std=hlsl202x -finclude-default-header \
+// RUN:   -triple dxil-pc-shadermodel6.0-library %s -fsyntax-only -verify \
+// RUN:   -verify-ignore-unexpected=warning
+
+// The float-bitwise compare reuses the 32-bit integer DXIL operation, so it
+// needs no capability bits and works from SM 6.0. The 64-bit compare-exchange
+// needs SM 6.6. This file checks both halves, so it proves the two are gated
+// differently.
+
+RWByteAddressBuffer BAB : register(u0);
+RasterizerOrderedByteAddressBuffer ROVB : register(u1);
+groupshared float gs_f32;
+groupshared uint64_t gs_u64;
+
+void sm60_bab_float_bitwise_ok(uint off, float cmp, float v, out float orig) {
+  BAB.InterlockedCompareExchangeFloatBitwise(off, cmp, v, orig);
+}
+
+void sm60_rovb_float_bitwise_ok(uint off, float cmp, float v, out float orig) {
+  ROVB.InterlockedCompareExchangeFloatBitwise(off, cmp, v, orig);
+}
+
+void sm60_free_function_ok(float cmp, float v) {
+  float orig;
+  InterlockedCompareExchangeFloatBitwise(gs_f32, cmp, v, orig);
+}
+
+void sm60_direct_builtin_ok(float cmp, float v) {
+  float orig;
+  __builtin_hlsl_interlocked_compare_exchange_float_bitwise(gs_f32, cmp, v, orig);
+}
+
+void sm60_no_bab_compare_exchange64(uint off, uint64_t cmp, uint64_t v,
+                                    out uint64_t orig) {
+  BAB.InterlockedCompareExchange64(off, cmp, v, orig);
+  // expected-error at -1 {{no member named 'InterlockedCompareExchange64' in 'hlsl::RWByteAddressBuffer'}}
+}
+
+void sm60_no_direct_builtin_u64(uint64_t cmp, uint64_t v, out uint64_t orig) {
+  __builtin_hlsl_interlocked_compare_exchange(gs_u64, cmp, v, orig);
+  // expected-error at -1 {{'__builtin_hlsl_interlocked_compare_exchange' requires shader model 6.6 or newer}}
+}
diff --git a/clang/test/SemaHLSL/BuiltIns/InterlockedCompareExchangeFloatBitwise-errors.hlsl b/clang/test/SemaHLSL/BuiltIns/InterlockedCompareExchangeFloatBitwise-errors.hlsl
new file mode 100644
index 0000000000000..934654c5f366d
--- /dev/null
+++ b/clang/test/SemaHLSL/BuiltIns/InterlockedCompareExchangeFloatBitwise-errors.hlsl
@@ -0,0 +1,119 @@
+// RUN: %clang_cc1 -std=hlsl202x -finclude-default-header \
+// RUN:   -triple dxil-pc-shadermodel6.6-library %s -emit-llvm-only \
+// RUN:   -disable-llvm-passes -verify
+
+// InterlockedCompareExchangeFloatBitwise compares the bit pattern of a 32-bit
+// float, so it is provided as a float-only overload set (groupshared/device).
+// It reports the previous value, so it has a single 4-argument form.
+
+groupshared float gs_f32;
+groupshared int gs_i32;
+groupshared double gs_f64;
+groupshared half gs_f16;
+struct S { float x; };
+groupshared S gs_s;
+
+void too_few(float cmp, float v) {
+  InterlockedCompareExchangeFloatBitwise(gs_f32, cmp, v); // expected-error{{no matching function for call to 'InterlockedCompareExchangeFloatBitwise'}}
+  // expected-note@*:* 2 {{candidate function}}
+}
+
+void too_many(float cmp, float v, float extra) {
+  float orig;
+  InterlockedCompareExchangeFloatBitwise(gs_f32, cmp, v, orig, extra); // expected-error{{no matching function for call to 'InterlockedCompareExchangeFloatBitwise'}}
+  // expected-note@*:* 2 {{candidate function}}
+}
+
+void local_dest(float cmp, float v) {
+  float dest;
+  float orig;
+  InterlockedCompareExchangeFloatBitwise(dest, cmp, v, orig); // expected-error{{no matching function for call to 'InterlockedCompareExchangeFloatBitwise'}}
+  // expected-note@*:* 2 {{candidate function}}
+}
+
+// The bitwise compare is defined for 32-bit float alone, so there is no
+// integer, half or double overload.
+void int_dest(int cmp, int v) {
+  int orig;
+  InterlockedCompareExchangeFloatBitwise(gs_i32, cmp, v, orig); // expected-error{{no matching function for call to 'InterlockedCompareExchangeFloatBitwise'}}
+  // expected-note@*:* 2 {{candidate function}}
+}
+
+void double_dest(double cmp, double v) {
+  double orig;
+  InterlockedCompareExchangeFloatBitwise(gs_f64, cmp, v, orig); // expected-error{{no matching function for call to 'InterlockedCompareExchangeFloatBitwise'}}
+  // expected-note@*:* 2 {{candidate function}}
+}
+
+void half_dest(half cmp, half v) {
+  half orig;
+  InterlockedCompareExchangeFloatBitwise(gs_f16, cmp, v, orig); // expected-error{{no matching function for call to 'InterlockedCompareExchangeFloatBitwise'}}
+  // expected-note@*:* 2 {{candidate function}}
+}
+
+void struct_dest(float cmp, float v) {
+  float orig;
+  InterlockedCompareExchangeFloatBitwise(gs_s, cmp, v, orig); // expected-error{{no matching function for call to 'InterlockedCompareExchangeFloatBitwise'}}
+  // expected-note@*:* 2 {{candidate function}}
+}
+
+void direct_too_few(float cmp, float v) {
+  __builtin_hlsl_interlocked_compare_exchange_float_bitwise(gs_f32, cmp, v);
+  // expected-error at -1 {{too few arguments to function call, expected 4, have 3}}
+}
+
+void direct_too_many(float cmp, float v, float extra) {
+  float orig;
+  __builtin_hlsl_interlocked_compare_exchange_float_bitwise(gs_f32, cmp, v, orig, extra);
+  // expected-error at -1 {{too many arguments to function call, expected 4, have 5}}
+}
+
+void direct_integer_dest(int cmp, int v) {
+  int orig;
+  __builtin_hlsl_interlocked_compare_exchange_float_bitwise(gs_i32, cmp, v, orig);
+  // expected-error at -1 {{1st argument must be a scalar 32 bit floating-point type (was 'int')}}
+}
+
+void direct_double_dest(double cmp, double v) {
+  double orig;
+  __builtin_hlsl_interlocked_compare_exchange_float_bitwise(gs_f64, cmp, v, orig);
+  // expected-error at -1 {{1st argument must be a scalar 32 bit floating-point type (was 'double')}}
+}
+
+void direct_half_dest(half cmp, half v) {
+  half orig;
+  __builtin_hlsl_interlocked_compare_exchange_float_bitwise(gs_f16, cmp, v, orig);
+  // expected-error at -1 {{1st argument must be a scalar 32 bit floating-point type (was 'half')}}
+}
+
+void direct_non_scalar_dest() {
+  S local_s;
+  float orig;
+  __builtin_hlsl_interlocked_compare_exchange_float_bitwise(local_s, 1.0f, 2.0f, orig);
+  // expected-error at -1 {{1st argument must be a scalar 32 bit floating-point type (was 'S')}}
+}
+
+void direct_nonlvalue_dest(float cmp, float v) {
+  float orig;
+  __builtin_hlsl_interlocked_compare_exchange_float_bitwise(1.0f, cmp, v, orig);
+  // expected-error at -1 {{cannot bind non-lvalue argument '1.F' to out parameter}}
+}
+
+// The last argument is an out parameter, so an rvalue is rejected there.
+void direct_nonlvalue_original_value(float cmp, float v) {
+  __builtin_hlsl_interlocked_compare_exchange_float_bitwise(gs_f32, cmp, v, 0.0f);
+  // expected-error at -1 {{cannot bind non-lvalue argument '0.F' to out parameter}}
+}
+
+void direct_mismatched_original_value(float cmp, float v) {
+  int orig;
+  __builtin_hlsl_interlocked_compare_exchange_float_bitwise(gs_f32, cmp, v, orig);
+  // expected-error at -1 {{passing 'int' to parameter of incompatible type 'float'}}
+}
+
+void direct_default_as_dest(float cmp, float v) {
+  float local;
+  float orig;
+  __builtin_hlsl_interlocked_compare_exchange_float_bitwise(local, cmp, v, orig);
+  // expected-error at -1 {{1st argument to atomic builtin must reference groupshared or device memory (was 'float')}}
+}



More information about the llvm-branch-commits mailing list