[llvm] 1fbf812 - [HLSL] Add `InterlockedCompareStoreFloatBitwise` function and resource methods (#222166)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Sep 29 13:47:25 PDT 2026
Author: Joshua Batista
Date: 2026-09-29T13:47:01-07:00
New Revision: 1fbf8123cd0224cbcc7a877269f4e998ba97adf0
URL: https://github.com/llvm/llvm-project/commit/1fbf8123cd0224cbcc7a877269f4e998ba97adf0
DIFF: https://github.com/llvm/llvm-project/commit/1fbf8123cd0224cbcc7a877269f4e998ba97adf0.diff
LOG: [HLSL] Add `InterlockedCompareStoreFloatBitwise` function and resource methods (#222166)
This PR adds the `InterlockedCompareStoreFloatBitwise` standalone
function and
resource methods.
`cmpxchg` rejects a float operand, clang therefore
bitcasts both float arguments to `i32` before it emits the `cmpxchg`,
and the
backend only ever sees an integer compare exchange. No DXIL legalization
is
needed for this operation.
DXC declares this function for 32-bit float alone, with no integer
overload,
so the overload set here is float only. `half` and `double` are
rejected.
The operation reuses the 32-bit integer DXIL operation, so it needs no
capability bits and works from shader model 6.0. A test checks both
halves of
that: the float-bitwise form is accepted at SM 6.0, and the 64-bit
compare-store is still rejected there.
Fixes: https://github.com/llvm/llvm-project/issues/99202
Assisted by: Github Copilot
Added:
clang/test/CodeGenHLSL/builtins/InterlockedCompareStoreFloatBitwise.hlsl
clang/test/CodeGenHLSL/builtins/RWByteAddressBuffer-InterlockedCompareStoreFloatBitwise.hlsl
clang/test/CodeGenHLSL/builtins/RasterizerOrderedByteAddressBuffer-InterlockedCompareStoreFloatBitwise.hlsl
clang/test/SemaHLSL/BuiltIns/ByteAddressBuffer-InterlockedCompareStoreFloatBitwise-sm60.hlsl
clang/test/SemaHLSL/BuiltIns/InterlockedCompareStoreFloatBitwise-errors.hlsl
llvm/test/CodeGen/DirectX/ResourceAtomicCompareStoreFloat.ll
Modified:
clang/include/clang/Basic/Builtins.td
clang/lib/CodeGen/CGHLSLBuiltins.cpp
clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
clang/lib/Sema/HLSLExternalSemaSource.cpp
clang/lib/Sema/SemaHLSL.cpp
clang/test/CodeGenHLSL/builtins/RWBuffer-Interlocked.hlsl
clang/test/CodeGenHLSL/builtins/RWTexture-Interlocked.hlsl
Removed:
################################################################################
diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index 1ed6a14cc0d8c..29a22604e1f99 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -5619,6 +5619,12 @@ def HLSLInterlockedCompareStore : LangBuiltin<"HLSL_LANG"> {
let Prototype = "void (...)";
}
+def HLSLInterlockedCompareStoreFloatBitwise : LangBuiltin<"HLSL_LANG"> {
+ let Spellings = ["__builtin_hlsl_interlocked_compare_store_float_bitwise"];
+ let Attributes = [NoThrow, CustomTypeChecking];
+ let Prototype = "void (...)";
+}
+
def HLSLInterlockedExchange : LangBuiltin<"HLSL_LANG"> {
let Spellings = ["__builtin_hlsl_interlocked_exchange"];
let Attributes = [NoThrow, CustomTypeChecking];
diff --git a/clang/lib/CodeGen/CGHLSLBuiltins.cpp b/clang/lib/CodeGen/CGHLSLBuiltins.cpp
index 3fab006326504..96597746f5fea 100644
--- a/clang/lib/CodeGen/CGHLSLBuiltins.cpp
+++ b/clang/lib/CodeGen/CGHLSLBuiltins.cpp
@@ -385,6 +385,17 @@ static Value *handleInterlockedCompareOp(CodeGenFunction &CGF,
Value *Compare = CGF.EmitScalarExpr(E->getArg(1));
Value *Val = CGF.EmitScalarExpr(E->getArg(2));
+ // `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.
+ if (Compare->getType()->isFloatingPointTy()) {
+ llvm::Type *IntTy =
+ CGF.Builder.getIntNTy(Compare->getType()->getPrimitiveSizeInBits());
+ Compare = CGF.Builder.CreateBitCast(Compare, IntTy);
+ Val = CGF.Builder.CreateBitCast(Val, IntTy);
+ DestAddr = DestAddr.withElementType(IntTy);
+ }
+
Value *Pair = CGF.Builder.CreateAtomicCmpXchg(
DestAddr, Compare, Val, llvm::AtomicOrdering::Monotonic,
llvm::AtomicOrdering::Monotonic, getHLSLAtomicScope(CGF, DestLV));
@@ -1519,7 +1530,8 @@ 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_store: {
+ case Builtin::BI__builtin_hlsl_interlocked_compare_store:
+ case Builtin::BI__builtin_hlsl_interlocked_compare_store_float_bitwise: {
return handleInterlockedCompareOp(*this, E);
}
case Builtin::BI__builtin_hlsl_interlocked_exchange: {
diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
index 53c4925e94c38..2e4bc4778037d 100644
--- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
+++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
@@ -1850,6 +1850,11 @@ BuiltinTypeDeclBuilder::addByteAddressBufferInterlockedMethods() {
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");
addByteAddressBufferInterlockedMethod(
"InterlockedExchange", AST.UnsignedIntTy,
"__builtin_hlsl_interlocked_exchange", /*RequiresOriginalValue=*/true);
diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp
index 4c4508313383e..bb49eb02aefe1 100644
--- a/clang/lib/Sema/HLSLExternalSemaSource.cpp
+++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp
@@ -996,6 +996,19 @@ static void defineHLSLInterlockedCompareFunc(Sema &S, NamespaceDecl *NS,
buildAtomicOverload(S, NS, FuncName, BuiltinName, ElemTy, AS, Shape);
}
+// The float-bitwise compare-and-swap functions have their own names and take
+// float only.
+static void defineHLSLInterlockedCompareFuncFloat(Sema &S, NamespaceDecl *NS,
+ StringRef FuncName,
+ StringRef BuiltinName,
+ AtomicOverloadShape Shape) {
+ ASTContext &AST = S.getASTContext();
+ buildAtomicOverload(S, NS, FuncName, BuiltinName, AST.FloatTy,
+ LangAS::hlsl_groupshared, Shape);
+ buildAtomicOverload(S, NS, FuncName, BuiltinName, AST.FloatTy,
+ LangAS::hlsl_device, Shape);
+}
+
void HLSLExternalSemaSource::defineHLSLAtomicIntrinsics() {
defineHLSLInterlockedFunc(*SemaPtr, HLSLNamespace, "InterlockedAdd",
"__builtin_hlsl_interlocked_add");
@@ -1009,6 +1022,10 @@ void HLSLExternalSemaSource::defineHLSLAtomicIntrinsics() {
"InterlockedCompareStore",
"__builtin_hlsl_interlocked_compare_store",
AtomicOverloadShape::CompareStore);
+ defineHLSLInterlockedCompareFuncFloat(
+ *SemaPtr, HLSLNamespace, "InterlockedCompareStoreFloatBitwise",
+ "__builtin_hlsl_interlocked_compare_store_float_bitwise",
+ AtomicOverloadShape::CompareStore);
defineHLSLInterlockedFunc(*SemaPtr, HLSLNamespace, "InterlockedExchange",
"__builtin_hlsl_interlocked_exchange",
/*RequiresOriginalValue=*/true,
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 23e38cea17ad3..a2459efd62db7 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -4121,7 +4121,7 @@ static bool CheckSamplingBuiltin(Sema &S, CallExpr *TheCall, SampleKind Kind) {
}
/// The `dest` types an interlocked operation accepts. Float is 32-bit only.
-enum class InterlockedDest { Int, IntOrFloat };
+enum class InterlockedDest { Int, IntOrFloat, Float };
/// Check a call to an HLSL interlocked builtin. The builtins are variadic, so
/// this is the only check a direct call gets. Overload resolution checks the
@@ -4144,13 +4144,17 @@ static bool CheckInterlockedBuiltin(Sema &S, CallExpr *TheCall,
}
QualType DestTy = TheCall->getArg(0)->getType().getUnqualifiedType();
- const bool AllowsFloat = Dest == InterlockedDest::IntOrFloat;
- if (!DestTy->isIntegerType() &&
- !(AllowsFloat && DestTy->isSpecificBuiltinType(BuiltinType::Float))) {
+ const bool DestIsOK =
+ DestTy->isSpecificBuiltinType(BuiltinType::Float)
+ ? Dest != InterlockedDest::Int
+ : Dest != InterlockedDest::Float && DestTy->isIntegerType();
+ if (!DestIsOK) {
S.Diag(TheCall->getArg(0)->getBeginLoc(),
diag::err_builtin_invalid_arg_type)
- << /*ordinal=*/1 << /*scalar*/ 1 << /*integer*/ 1
- << /*32 bit floating-point*/ (AllowsFloat ? 3 : 0) << DestTy;
+ << /*ordinal=*/1 << /*scalar*/ 1
+ << /*integer*/ (Dest == InterlockedDest::Float ? 0 : 1)
+ << /*32 bit floating-point*/ (Dest == InterlockedDest::Int ? 0 : 3)
+ << DestTy;
return true;
}
@@ -4728,6 +4732,12 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) {
/*ReportsOriginalValue=*/false))
return true;
break;
+ case Builtin::BI__builtin_hlsl_interlocked_compare_store_float_bitwise:
+ if (CheckInterlockedBuiltin(SemaRef, TheCall, /*MinArgs=*/3, /*MaxArgs=*/3,
+ InterlockedDest::Float,
+ /*ReportsOriginalValue=*/false))
+ return true;
+ break;
case Builtin::BI__builtin_hlsl_interlocked_compare_exchange:
if (CheckInterlockedBuiltin(SemaRef, TheCall, /*MinArgs=*/4, /*MaxArgs=*/4,
InterlockedDest::Int,
diff --git a/clang/test/CodeGenHLSL/builtins/InterlockedCompareStoreFloatBitwise.hlsl b/clang/test/CodeGenHLSL/builtins/InterlockedCompareStoreFloatBitwise.hlsl
new file mode 100644
index 0000000000000..e73a72d8072bc
--- /dev/null
+++ b/clang/test/CodeGenHLSL/builtins/InterlockedCompareStoreFloatBitwise.hlsl
@@ -0,0 +1,34 @@
+// 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 InterlockedCompareStoreFloatBitwise to `cmpxchg
+// monotonic`. `cmpxchg` takes an integer, so the float arguments become their
+// bit patterns first. The operation reports nothing, so the `cmpxchg` result
+// stays unused.
+
+groupshared float gs_f32;
+
+// CHECK-LABEL: define {{.*}}void @{{.*}}test_float
+// CHECK: [[CMP:%.*]] = bitcast float %{{.*}} to i32
+// CHECK-NEXT: [[VAL:%.*]] = bitcast float %{{.*}} to i32
+// CHECK-NEXT: cmpxchg ptr addrspace(3) {{.*}}@gs_f32{{.*}}, i32 [[CMP]], i32 [[VAL]] syncscope("workgroup") monotonic monotonic
+export void test_float(float cmp, float v) {
+ InterlockedCompareStoreFloatBitwise(gs_f32, cmp, v);
+}
+
+// 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: cmpxchg ptr %{{.*}}, i32 [[CMP]], i32 [[VAL]] syncscope("device") monotonic monotonic
+// SPVCHECK-NEXT: cmpxchg ptr addrspace(11) %{{.*}}, i32 [[CMP]], i32 [[VAL]] syncscope("device") monotonic monotonic
+export void test_device(float cmp, float v) {
+ InterlockedCompareStoreFloatBitwise(Buf[0], cmp, v);
+}
diff --git a/clang/test/CodeGenHLSL/builtins/RWBuffer-Interlocked.hlsl b/clang/test/CodeGenHLSL/builtins/RWBuffer-Interlocked.hlsl
index 47cff714a7df7..b85e40a9b1093 100644
--- a/clang/test/CodeGenHLSL/builtins/RWBuffer-Interlocked.hlsl
+++ b/clang/test/CodeGenHLSL/builtins/RWBuffer-Interlocked.hlsl
@@ -19,6 +19,7 @@
RWBuffer<int> Out : register(u0);
RWBuffer<uint> UOut : register(u1);
+RWBuffer<float> FOut : register(u2);
// CHECK-LABEL: define void @main
// DXCHECK: %[[PTR1:.*]] = call {{.*}} @llvm.dx.resource.getpointer.p0.tdx.TypedBuffer_i32_1_0_1t.i32(target("dx.TypedBuffer", i32, 1, 0, 1) %{{.*}}, i32 %{{.*}})
@@ -43,6 +44,8 @@ RWBuffer<uint> UOut : register(u1);
// DXCHECK: cmpxchg ptr %[[PTR10]], i32 1, i32 2 syncscope("device") monotonic monotonic
// DXCHECK: %[[PTR11:.*]] = call {{.*}} @llvm.dx.resource.getpointer.p0.tdx.TypedBuffer_i32_1_0_1t.i32(target("dx.TypedBuffer", i32, 1, 0, 1) %{{.*}}, i32 %{{.*}})
// 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
// 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 %{{.*}})
@@ -65,6 +68,8 @@ RWBuffer<uint> UOut : register(u1);
// SPVCHECK: cmpxchg ptr addrspace(11) %[[PTR10]], i32 1, i32 2 syncscope("device") monotonic monotonic
// SPVCHECK: %[[PTR11:.*]] = call {{.*}} @llvm.spv.resource.getpointer.{{.*}}(target("spirv.SignedImage", i32, {{.*}}) %{{.*}}, i32 %{{.*}})
// 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
[shader("compute")]
[numthreads(1,1,1)]
void main(uint3 id : SV_DispatchThreadID) {
@@ -80,4 +85,5 @@ void main(uint3 id : SV_DispatchThreadID) {
InterlockedExchange(Out[id.x], 1, orig);
InterlockedCompareStore(Out[id.x], 1, 2);
InterlockedCompareExchange(Out[id.x], 1, 2, orig);
+ InterlockedCompareStoreFloatBitwise(FOut[id.x], 1.0f, 2.0f);
}
diff --git a/clang/test/CodeGenHLSL/builtins/RWByteAddressBuffer-InterlockedCompareStoreFloatBitwise.hlsl b/clang/test/CodeGenHLSL/builtins/RWByteAddressBuffer-InterlockedCompareStoreFloatBitwise.hlsl
new file mode 100644
index 0000000000000..ca2f2b02cdaa4
--- /dev/null
+++ b/clang/test/CodeGenHLSL/builtins/RWByteAddressBuffer-InterlockedCompareStoreFloatBitwise.hlsl
@@ -0,0 +1,27 @@
+// 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::InterlockedCompareStoreFloatBitwise
+// 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.
+
+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: cmpxchg ptr %[[PTR]], i32 %[[CMP]], i32 %[[VAL]] syncscope("device") monotonic monotonic
+// SPVCHECK-NEXT: cmpxchg ptr addrspace(11) %[[PTR]], i32 %[[CMP]], i32 %[[VAL]] syncscope("device") monotonic monotonic
+export void test_bab_float(uint off, float cmp, float v) {
+ BAB.InterlockedCompareStoreFloatBitwise(off, cmp, v);
+}
diff --git a/clang/test/CodeGenHLSL/builtins/RWTexture-Interlocked.hlsl b/clang/test/CodeGenHLSL/builtins/RWTexture-Interlocked.hlsl
index 6fc44101d4879..69972462f4b9c 100644
--- a/clang/test/CodeGenHLSL/builtins/RWTexture-Interlocked.hlsl
+++ b/clang/test/CodeGenHLSL/builtins/RWTexture-Interlocked.hlsl
@@ -44,6 +44,8 @@ RWTexture2D<float> FOut : register(u2);
// DXCHECK: cmpxchg ptr %[[PTR10]], i32 1, i32 2 syncscope("device") monotonic monotonic
// DXCHECK: %[[PTR11:.*]] = call {{.*}} @llvm.dx.resource.getpointer.{{.*}}(target("dx.Texture", i32, 1, 0, 1, 2) %{{.*}}, <2 x i32> %{{.*}})
// DXCHECK: cmpxchg ptr %[[PTR11]], i32 1, i32 2 syncscope("device") monotonic monotonic
+// DXCHECK: %[[PTR12:.*]] = call {{.*}} @llvm.dx.resource.getpointer.{{.*}}(target("dx.Texture", float, 1, 0, 0, 2) %{{.*}}, <2 x i32> %{{.*}})
+// DXCHECK: cmpxchg ptr %[[PTR12]], i32 1065353216, i32 1073741824 syncscope("device") monotonic monotonic
// SPVCHECK: %[[PTR1:.*]] = call {{.*}} @llvm.spv.resource.getpointer.{{.*}}(target("spirv.SignedImage", i32, {{.*}}) %{{.*}}, <2 x i32> %{{.*}})
// SPVCHECK: atomicrmw add ptr addrspace(11) %[[PTR1]], i32 1 syncscope("device") monotonic
// SPVCHECK: %[[PTR2:.*]] = call {{.*}} @llvm.spv.resource.getpointer.{{.*}}(target("spirv.SignedImage", i32, {{.*}}) %{{.*}}, <2 x i32> %{{.*}})
@@ -66,6 +68,8 @@ RWTexture2D<float> FOut : register(u2);
// SPVCHECK: cmpxchg ptr addrspace(11) %[[PTR10]], i32 1, i32 2 syncscope("device") monotonic monotonic
// SPVCHECK: %[[PTR11:.*]] = call {{.*}} @llvm.spv.resource.getpointer.{{.*}}(target("spirv.SignedImage", i32, {{.*}}) %{{.*}}, <2 x i32> %{{.*}})
// 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, {{.*}}) %{{.*}}, <2 x i32> %{{.*}})
+// SPVCHECK: cmpxchg ptr addrspace(11) %[[PTR12]], i32 1065353216, i32 1073741824 syncscope("device") monotonic monotonic
[shader("compute")]
[numthreads(1,1,1)]
void main(uint3 id : SV_DispatchThreadID) {
@@ -82,4 +86,7 @@ void main(uint3 id : SV_DispatchThreadID) {
InterlockedExchange(FOut[id.xy], 1.0f, FOrig);
InterlockedCompareStore(Out[id.xy], 1, 2);
InterlockedCompareExchange(Out[id.xy], 1, 2, Orig);
+ // The float bitwise forms compare the bit pattern, so the operands show up
+ // as the i32 encodings of 1.0f and 2.0f.
+ InterlockedCompareStoreFloatBitwise(FOut[id.xy], 1.0f, 2.0f);
}
diff --git a/clang/test/CodeGenHLSL/builtins/RasterizerOrderedByteAddressBuffer-InterlockedCompareStoreFloatBitwise.hlsl b/clang/test/CodeGenHLSL/builtins/RasterizerOrderedByteAddressBuffer-InterlockedCompareStoreFloatBitwise.hlsl
new file mode 100644
index 0000000000000..89162c57f416f
--- /dev/null
+++ b/clang/test/CodeGenHLSL/builtins/RasterizerOrderedByteAddressBuffer-InterlockedCompareStoreFloatBitwise.hlsl
@@ -0,0 +1,19 @@
+// 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. Add a `spirv-pc-vulkan1.3-library` RUN line here when SPIR-V ROV
+// support lands. See https://github.com/llvm/llvm-project/issues/124585.
+
+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: cmpxchg ptr %[[PTR]], i32 %[[CMP]], i32 %[[VAL]] syncscope("device") monotonic monotonic
+export void test_rovb_float(uint off, float cmp, float v) {
+ ROVB.InterlockedCompareStoreFloatBitwise(off, cmp, v);
+}
diff --git a/clang/test/SemaHLSL/BuiltIns/ByteAddressBuffer-InterlockedCompareStoreFloatBitwise-sm60.hlsl b/clang/test/SemaHLSL/BuiltIns/ByteAddressBuffer-InterlockedCompareStoreFloatBitwise-sm60.hlsl
new file mode 100644
index 0000000000000..5954884163670
--- /dev/null
+++ b/clang/test/SemaHLSL/BuiltIns/ByteAddressBuffer-InterlockedCompareStoreFloatBitwise-sm60.hlsl
@@ -0,0 +1,39 @@
+// 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-store
+// needs SM 6.6. This file checks both halves, so it proves the two are gated
+//
diff erently.
+
+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) {
+ BAB.InterlockedCompareStoreFloatBitwise(off, cmp, v);
+}
+
+void sm60_rovb_float_bitwise_ok(uint off, float cmp, float v) {
+ ROVB.InterlockedCompareStoreFloatBitwise(off, cmp, v);
+}
+
+void sm60_free_function_ok(float cmp, float v) {
+ InterlockedCompareStoreFloatBitwise(gs_f32, cmp, v);
+}
+
+void sm60_direct_builtin_ok(float cmp, float v) {
+ __builtin_hlsl_interlocked_compare_store_float_bitwise(gs_f32, cmp, v);
+}
+
+void sm60_no_bab_compare_store64(uint off, uint64_t cmp, uint64_t v) {
+ BAB.InterlockedCompareStore64(off, cmp, v);
+ // expected-error at -1 {{no member named 'InterlockedCompareStore64' in 'hlsl::RWByteAddressBuffer'}}
+}
+
+void sm60_no_direct_builtin_u64(uint64_t cmp, uint64_t v) {
+ __builtin_hlsl_interlocked_compare_store(gs_u64, cmp, v);
+ // expected-error at -1 {{'__builtin_hlsl_interlocked_compare_store' requires shader model 6.6 or newer}}
+}
diff --git a/clang/test/SemaHLSL/BuiltIns/InterlockedCompareStoreFloatBitwise-errors.hlsl b/clang/test/SemaHLSL/BuiltIns/InterlockedCompareStoreFloatBitwise-errors.hlsl
new file mode 100644
index 0000000000000..f291c6264f099
--- /dev/null
+++ b/clang/test/SemaHLSL/BuiltIns/InterlockedCompareStoreFloatBitwise-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
+
+// InterlockedCompareStoreFloatBitwise compares the bit pattern of a 32-bit
+// float, so it is provided as a float-only overload set (groupshared/device).
+// It reports nothing, so it has a single 3-argument form and no out parameter.
+
+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) {
+ InterlockedCompareStoreFloatBitwise(gs_f32, cmp); // expected-error{{no matching function for call to 'InterlockedCompareStoreFloatBitwise'}}
+ // expected-note@*:* 2 {{candidate function}}
+}
+
+void too_many(float cmp, float v, float extra) {
+ InterlockedCompareStoreFloatBitwise(gs_f32, cmp, v, extra); // expected-error{{no matching function for call to 'InterlockedCompareStoreFloatBitwise'}}
+ // expected-note@*:* 2 {{candidate function}}
+}
+
+void local_dest(float cmp, float v) {
+ float dest;
+ InterlockedCompareStoreFloatBitwise(dest, cmp, v); // expected-error{{no matching function for call to 'InterlockedCompareStoreFloatBitwise'}}
+ // 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) {
+ InterlockedCompareStoreFloatBitwise(gs_i32, cmp, v); // expected-error{{no matching function for call to 'InterlockedCompareStoreFloatBitwise'}}
+ // expected-note@*:* 2 {{candidate function}}
+}
+
+void double_dest(double cmp, double v) {
+ InterlockedCompareStoreFloatBitwise(gs_f64, cmp, v); // expected-error{{no matching function for call to 'InterlockedCompareStoreFloatBitwise'}}
+ // expected-note@*:* 2 {{candidate function}}
+}
+
+void half_dest(half cmp, half v) {
+ InterlockedCompareStoreFloatBitwise(gs_f16, cmp, v); // expected-error{{no matching function for call to 'InterlockedCompareStoreFloatBitwise'}}
+ // expected-note@*:* 2 {{candidate function}}
+}
+
+void struct_dest(float cmp, float v) {
+ InterlockedCompareStoreFloatBitwise(gs_s, cmp, v); // expected-error{{no matching function for call to 'InterlockedCompareStoreFloatBitwise'}}
+ // expected-note@*:* 2 {{candidate function}}
+}
+
+void direct_too_few(float cmp) {
+ __builtin_hlsl_interlocked_compare_store_float_bitwise(gs_f32, cmp);
+ // expected-error at -1 {{too few arguments to function call, expected 3, have 2}}
+}
+
+void direct_too_many(float cmp, float v, float extra) {
+ __builtin_hlsl_interlocked_compare_store_float_bitwise(gs_f32, cmp, v, extra);
+ // expected-error at -1 {{too many arguments to function call, expected 3, have 4}}
+}
+
+void direct_integer_dest(int cmp, int v) {
+ __builtin_hlsl_interlocked_compare_store_float_bitwise(gs_i32, cmp, v);
+ // 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) {
+ __builtin_hlsl_interlocked_compare_store_float_bitwise(gs_f64, cmp, v);
+ // 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) {
+ __builtin_hlsl_interlocked_compare_store_float_bitwise(gs_f16, cmp, v);
+ // 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;
+ __builtin_hlsl_interlocked_compare_store_float_bitwise(local_s, 1.0f, 2.0f);
+ // 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) {
+ __builtin_hlsl_interlocked_compare_store_float_bitwise(1.0f, cmp, v);
+ // expected-error at -1 {{cannot bind non-lvalue argument '1.F' to out parameter}}
+}
+
+void direct_default_as_dest(float cmp, float v) {
+ float local;
+ __builtin_hlsl_interlocked_compare_store_float_bitwise(local, cmp, v);
+ // expected-error at -1 {{1st argument to atomic builtin must reference groupshared or device memory (was 'float')}}
+}
+
+// The last argument is the new value rather than an out parameter, so an
+// rvalue is accepted here.
+void direct_rvalue_value_ok() {
+ __builtin_hlsl_interlocked_compare_store_float_bitwise(gs_f32, 1.0f, 2.0f);
+}
diff --git a/llvm/test/CodeGen/DirectX/ResourceAtomicCompareStoreFloat.ll b/llvm/test/CodeGen/DirectX/ResourceAtomicCompareStoreFloat.ll
new file mode 100644
index 0000000000000..03eff2ade22ea
--- /dev/null
+++ b/llvm/test/CodeGen/DirectX/ResourceAtomicCompareStoreFloat.ll
@@ -0,0 +1,30 @@
+; RUN: opt -S -dxil-resource-access -dxil-op-lower -mtriple=dxil-pc-shadermodel6.6-compute %s | FileCheck %s
+
+; InterlockedCompareStoreFloatBitwise compares the bit pattern of a float, so
+; clang emits an i32 `cmpxchg` on a float resource. Lowering must key the DXIL
+; op off the operand type rather than the resource element type.
+
+target triple = "dxil-pc-shadermodel6.6-compute"
+
+; CHECK-LABEL: define void @typed_buffer_float_compare_store
+define void @typed_buffer_float_compare_store(i32 %index, i32 %cmp, i32 %val) {
+ %buffer = call target("dx.TypedBuffer", float, 1, 0, 0)
+ @llvm.dx.resource.handlefrombinding(i32 0, i32 0, i32 1, i32 0, ptr null)
+ %ptr = call ptr @llvm.dx.resource.getpointer(
+ target("dx.TypedBuffer", float, 1, 0, 0) %buffer, i32 %index)
+ ; CHECK: call i32 @dx.op.atomicCompareExchange.i32(i32 79, %dx.types.Handle %{{.*}}, i32 %index, i32 poison, i32 poison, i32 %cmp, i32 %val)
+ %old = cmpxchg ptr %ptr, i32 %cmp, i32 %val monotonic monotonic
+ ret void
+}
+
+; The raw buffer path carries no element type, so the same i32 op applies.
+; CHECK-LABEL: define void @raw_buffer_float_compare_store
+define void @raw_buffer_float_compare_store(i32 %offset, i32 %cmp, i32 %val) {
+ %buffer = call target("dx.RawBuffer", i8, 1, 0, 0)
+ @llvm.dx.resource.handlefrombinding(i32 0, i32 0, i32 1, i32 0, ptr null)
+ %ptr = call ptr @llvm.dx.resource.getpointer(
+ target("dx.RawBuffer", i8, 1, 0, 0) %buffer, i32 %offset)
+ ; CHECK: call i32 @dx.op.atomicCompareExchange.i32(i32 79, %dx.types.Handle %{{.*}}, i32 %offset, i32 poison, i32 poison, i32 %cmp, i32 %val)
+ %old = cmpxchg ptr %ptr, i32 %cmp, i32 %val monotonic monotonic
+ ret void
+}
More information about the llvm-commits
mailing list