[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