https://github.com/bob80905 updated https://github.com/llvm/llvm-project/pull/219317
>From aaab76de2b84dbdbce9eb2e242d5fc1ea69c8356 Mon Sep 17 00:00:00 2001 From: Joshua Batista <[email protected]> Date: Thu, 27 Aug 2026 15:24:52 -0700 Subject: [PATCH 1/2] first attempt --- clang/include/clang/Basic/Builtins.td | 6 ++ clang/lib/CodeGen/CGHLSLBuiltins.cpp | 3 + clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp | 5 + clang/lib/Sema/HLSLExternalSemaSource.cpp | 2 + clang/lib/Sema/SemaHLSL.cpp | 1 + .../CodeGenHLSL/builtins/InterlockedAnd.hlsl | 59 +++++++++++ .../builtins/RWBuffer-Interlocked.hlsl | 5 + .../RWByteAddressBuffer-InterlockedAnd.hlsl | 62 +++++++++++ ...deredByteAddressBuffer-InterlockedAnd.hlsl | 45 ++++++++ .../BuiltIns/InterlockedAnd-errors.hlsl | 100 ++++++++++++++++++ 10 files changed, 288 insertions(+) create mode 100644 clang/test/CodeGenHLSL/builtins/InterlockedAnd.hlsl create mode 100644 clang/test/CodeGenHLSL/builtins/RWByteAddressBuffer-InterlockedAnd.hlsl create mode 100644 clang/test/CodeGenHLSL/builtins/RasterizerOrderedByteAddressBuffer-InterlockedAnd.hlsl create mode 100644 clang/test/SemaHLSL/BuiltIns/InterlockedAnd-errors.hlsl diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td index 0aec57f201301..c919f762ee34a 100644 --- a/clang/include/clang/Basic/Builtins.td +++ b/clang/include/clang/Basic/Builtins.td @@ -5551,6 +5551,12 @@ def HLSLInterlockedAdd : LangBuiltin<"HLSL_LANG"> { let Prototype = "void (...)"; } +def HLSLInterlockedAnd : LangBuiltin<"HLSL_LANG"> { + let Spellings = ["__builtin_hlsl_interlocked_and"]; + let Attributes = [NoThrow]; + let Prototype = "void (...)"; +} + def HLSLInterlockedMin : LangBuiltin<"HLSL_LANG"> { let Spellings = ["__builtin_hlsl_interlocked_min"]; let Attributes = [NoThrow]; diff --git a/clang/lib/CodeGen/CGHLSLBuiltins.cpp b/clang/lib/CodeGen/CGHLSLBuiltins.cpp index 8d74e7652ff49..14b8fd13104b6 100644 --- a/clang/lib/CodeGen/CGHLSLBuiltins.cpp +++ b/clang/lib/CodeGen/CGHLSLBuiltins.cpp @@ -1458,6 +1458,9 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID, // selectAtomicRMW). No intermediate intrinsic. return handleInterlockedOp(*this, E, llvm::AtomicRMWInst::Add); } + case Builtin::BI__builtin_hlsl_interlocked_and: { + return handleInterlockedOp(*this, E, llvm::AtomicRMWInst::And); + } case Builtin::BI__builtin_hlsl_interlocked_min: { llvm::AtomicRMWInst::BinOp Op = E->getArg(0)->getType()->hasSignedIntegerRepresentation() diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp index 35caa0a740235..2606b7a692d2e 100644 --- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp +++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp @@ -1727,6 +1727,8 @@ BuiltinTypeDeclBuilder::addByteAddressBufferInterlockedMethods() { // original-value parameter for each entry. addByteAddressBufferInterlockedMethod("InterlockedAdd", AST.UnsignedIntTy, "__builtin_hlsl_interlocked_add"); + addByteAddressBufferInterlockedMethod("InterlockedAnd", AST.UnsignedIntTy, + "__builtin_hlsl_interlocked_and"); addByteAddressBufferInterlockedMethod("InterlockedMin", AST.IntTy, "__builtin_hlsl_interlocked_min"); addByteAddressBufferInterlockedMethod("InterlockedMin", AST.UnsignedIntTy, @@ -1746,6 +1748,9 @@ BuiltinTypeDeclBuilder::addByteAddressBufferInterlockedMethods() { addByteAddressBufferInterlockedMethod("InterlockedAdd64", AST.UnsignedLongTy, "__builtin_hlsl_interlocked_add"); + addByteAddressBufferInterlockedMethod("InterlockedAnd64", + AST.UnsignedLongTy, + "__builtin_hlsl_interlocked_and"); addByteAddressBufferInterlockedMethod("InterlockedMin64", AST.LongTy, "__builtin_hlsl_interlocked_min"); addByteAddressBufferInterlockedMethod("InterlockedMin64", diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp index f8fe990210fbb..939e4c9222e2d 100644 --- a/clang/lib/Sema/HLSLExternalSemaSource.cpp +++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp @@ -881,6 +881,8 @@ static void defineHLSLInterlockedFunc(Sema &S, NamespaceDecl *NS, void HLSLExternalSemaSource::defineHLSLAtomicIntrinsics() { defineHLSLInterlockedFunc(*SemaPtr, HLSLNamespace, "InterlockedAdd", "__builtin_hlsl_interlocked_add"); + defineHLSLInterlockedFunc(*SemaPtr, HLSLNamespace, "InterlockedAnd", + "__builtin_hlsl_interlocked_and"); defineHLSLInterlockedFunc(*SemaPtr, HLSLNamespace, "InterlockedMin", "__builtin_hlsl_interlocked_min"); defineHLSLInterlockedFunc(*SemaPtr, HLSLNamespace, "InterlockedOr", diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index 99c2191ec3787..1665fb65901f8 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -4711,6 +4711,7 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) { break; } case Builtin::BI__builtin_hlsl_interlocked_add: + case Builtin::BI__builtin_hlsl_interlocked_and: case Builtin::BI__builtin_hlsl_interlocked_min: case Builtin::BI__builtin_hlsl_interlocked_or: case Builtin::BI__builtin_hlsl_interlocked_xor: { diff --git a/clang/test/CodeGenHLSL/builtins/InterlockedAnd.hlsl b/clang/test/CodeGenHLSL/builtins/InterlockedAnd.hlsl new file mode 100644 index 0000000000000..ac712a1489c8f --- /dev/null +++ b/clang/test/CodeGenHLSL/builtins/InterlockedAnd.hlsl @@ -0,0 +1,59 @@ +// 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 InterlockedAnd to `atomicrmw and monotonic`. + +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: atomicrmw and ptr addrspace(3) {{.*}}@gs_i32{{.*}}, i32 %{{.*}} syncscope("workgroup") monotonic +// SPVCHECK: atomicrmw and ptr addrspace(3) {{.*}}@gs_i32{{.*}}, i32 %{{.*}} syncscope("workgroup") monotonic +export void test_int_2arg(int v) { + InterlockedAnd(gs_i32, v); +} + +// CHECK-LABEL: define {{(dso_local |hidden |internal |protected |spir_func )*}}void @{{.*}}test_uint_2arg +// DXCHECK: atomicrmw and ptr addrspace(3) {{.*}}@gs_u32{{.*}}, i32 %{{.*}} syncscope("workgroup") monotonic +// SPVCHECK: atomicrmw and ptr addrspace(3) {{.*}}@gs_u32{{.*}}, i32 %{{.*}} syncscope("workgroup") monotonic +export void test_uint_2arg(uint v) { + InterlockedAnd(gs_u32, v); +} + +// CHECK-LABEL: define {{(dso_local |hidden |internal |protected |spir_func )*}}void @{{.*}}test_int_3arg +// DXCHECK: %[[R:.*]] = atomicrmw and ptr addrspace(3) {{.*}}@gs_i32{{.*}}, i32 %{{.*}} syncscope("workgroup") monotonic +// SPVCHECK: %[[R:.*]] = atomicrmw and ptr addrspace(3) {{.*}}@gs_i32{{.*}}, i32 %{{.*}} syncscope("workgroup") monotonic +// CHECK: store i32 %[[R]], ptr {{.*}} +export void test_int_3arg(int v, out int orig) { + InterlockedAnd(gs_i32, v, orig); +} + +// CHECK-LABEL: define {{(dso_local |hidden |internal |protected |spir_func )*}}void @{{.*}}test_uint_3arg +// DXCHECK: %[[R:.*]] = atomicrmw and ptr addrspace(3) {{.*}}@gs_u32{{.*}}, i32 %{{.*}} syncscope("workgroup") monotonic +// SPVCHECK: %[[R:.*]] = atomicrmw and ptr addrspace(3) {{.*}}@gs_u32{{.*}}, i32 %{{.*}} syncscope("workgroup") monotonic +// CHECK: store i32 %[[R]], ptr {{.*}} +export void test_uint_3arg(uint v, out uint orig) { + InterlockedAnd(gs_u32, v, orig); +} + +// CHECK-LABEL: define {{(dso_local |hidden |internal |protected |spir_func )*}}void @{{.*}}test_int64_2arg +// DXCHECK: atomicrmw and ptr addrspace(3) {{.*}}@gs_i64{{.*}}, i64 %{{.*}} syncscope("workgroup") monotonic +// SPVCHECK: atomicrmw and ptr addrspace(3) {{.*}}@gs_i64{{.*}}, i64 %{{.*}} syncscope("workgroup") monotonic +export void test_int64_2arg(int64_t v) { + InterlockedAnd(gs_i64, v); +} + +// CHECK-LABEL: define {{(dso_local |hidden |internal |protected |spir_func )*}}void @{{.*}}test_uint64_3arg +// DXCHECK: %[[R:.*]] = atomicrmw and ptr addrspace(3) {{.*}}@gs_u64{{.*}}, i64 %{{.*}} syncscope("workgroup") monotonic +// SPVCHECK: %[[R:.*]] = atomicrmw and ptr addrspace(3) {{.*}}@gs_u64{{.*}}, i64 %{{.*}} syncscope("workgroup") monotonic +// CHECK: store i64 %[[R]], ptr {{.*}} +export void test_uint64_3arg(uint64_t v, out uint64_t orig) { + InterlockedAnd(gs_u64, v, orig); +} diff --git a/clang/test/CodeGenHLSL/builtins/RWBuffer-Interlocked.hlsl b/clang/test/CodeGenHLSL/builtins/RWBuffer-Interlocked.hlsl index 980bee046aefa..d79e69a52f9c4 100644 --- a/clang/test/CodeGenHLSL/builtins/RWBuffer-Interlocked.hlsl +++ b/clang/test/CodeGenHLSL/builtins/RWBuffer-Interlocked.hlsl @@ -31,6 +31,8 @@ RWBuffer<uint> UOut : register(u1); // DXCHECK: atomicrmw min ptr %[[PTR4]], i32 1 syncscope("device") monotonic // DXCHECK: %[[PTR5:.*]] = call {{.*}} @llvm.dx.resource.getpointer.p0.tdx.TypedBuffer_i32_1_0_0t.i32(target("dx.TypedBuffer", i32, 1, 0, 0) %{{.*}}, i32 %{{.*}}) // DXCHECK: atomicrmw umin ptr %[[PTR5]], i32 1 syncscope("device") monotonic +// DXCHECK: %[[PTR6:.*]] = call {{.*}} @llvm.dx.resource.getpointer.p0.tdx.TypedBuffer_i32_1_0_1t.i32(target("dx.TypedBuffer", i32, 1, 0, 1) %{{.*}}, i32 %{{.*}}) +// DXCHECK: atomicrmw and ptr %[[PTR6]], i32 1 syncscope("device") 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 %{{.*}}) @@ -41,6 +43,8 @@ RWBuffer<uint> UOut : register(u1); // SPVCHECK: atomicrmw min ptr addrspace(11) %[[PTR4]], i32 1 syncscope("device") monotonic // SPVCHECK: %[[PTR5:.*]] = call {{.*}} @llvm.spv.resource.getpointer.{{.*}}(target("spirv.Image", i32, {{.*}}) %{{.*}}, i32 %{{.*}}) // SPVCHECK: atomicrmw umin ptr addrspace(11) %[[PTR5]], i32 1 syncscope("device") monotonic +// SPVCHECK: %[[PTR6:.*]] = call {{.*}} @llvm.spv.resource.getpointer.{{.*}}(target("spirv.SignedImage", i32, {{.*}}) %{{.*}}, i32 %{{.*}}) +// SPVCHECK: atomicrmw and ptr addrspace(11) %[[PTR6]], i32 1 syncscope("device") monotonic [shader("compute")] [numthreads(1,1,1)] void main(uint3 id : SV_DispatchThreadID) { @@ -49,4 +53,5 @@ void main(uint3 id : SV_DispatchThreadID) { InterlockedXor(Out[id.x], 1); InterlockedMin(Out[id.x], 1); InterlockedMin(UOut[id.x], 1u); + InterlockedAnd(Out[id.x], 1); } diff --git a/clang/test/CodeGenHLSL/builtins/RWByteAddressBuffer-InterlockedAnd.hlsl b/clang/test/CodeGenHLSL/builtins/RWByteAddressBuffer-InterlockedAnd.hlsl new file mode 100644 index 0000000000000..f6f19f0580523 --- /dev/null +++ b/clang/test/CodeGenHLSL/builtins/RWByteAddressBuffer-InterlockedAnd.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::InterlockedAnd and InterlockedAnd64 +// member methods lower to `resource_getpointer -> atomicrmw and`, 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 and ptr %[[PTR]], i32 %{{.*}} syncscope("device") 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 and ptr addrspace(11) %[[PTR]], i32 %{{.*}} syncscope("device") monotonic +export void test_bab_int_2arg(uint off, int v) { + BAB.InterlockedAnd(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 and ptr %[[PTR]], i32 %{{.*}} syncscope("device") 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 and ptr addrspace(11) %[[PTR]], i32 %{{.*}} syncscope("device") monotonic +// SPVCHECK: store i32 %[[R]], ptr {{.*}} +export void test_bab_uint_3arg(uint off, uint v, out uint orig) { + BAB.InterlockedAnd(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 and ptr %[[PTR]], i64 %{{.*}} syncscope("device") 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 and ptr addrspace(11) %[[PTR]], i64 %{{.*}} syncscope("device") monotonic +export void test_bab_int64_2arg(uint off, int64_t v) { + BAB.InterlockedAnd64(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 and ptr %[[PTR]], i64 %{{.*}} syncscope("device") 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 and ptr addrspace(11) %[[PTR]], i64 %{{.*}} syncscope("device") monotonic +// SPVCHECK: store i64 %[[R]], ptr {{.*}} +export void test_bab_uint64_3arg(uint off, uint64_t v, out uint64_t orig) { + BAB.InterlockedAnd64(off, v, orig); +} diff --git a/clang/test/CodeGenHLSL/builtins/RasterizerOrderedByteAddressBuffer-InterlockedAnd.hlsl b/clang/test/CodeGenHLSL/builtins/RasterizerOrderedByteAddressBuffer-InterlockedAnd.hlsl new file mode 100644 index 0000000000000..8820c2004c5c6 --- /dev/null +++ b/clang/test/CodeGenHLSL/builtins/RasterizerOrderedByteAddressBuffer-InterlockedAnd.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 and ptr %[[PTR]], i32 %{{.*}} syncscope("device") monotonic +export void test_rovb_int_2arg(uint off, int v) { + ROVB.InterlockedAnd(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 and ptr %[[PTR]], i32 %{{.*}} syncscope("device") monotonic +// DXCHECK: store i32 %[[R]], ptr {{.*}} +export void test_rovb_uint_3arg(uint off, uint v, out uint orig) { + ROVB.InterlockedAnd(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 and ptr %[[PTR]], i64 %{{.*}} syncscope("device") monotonic +export void test_rovb_int64_2arg(uint off, int64_t v) { + ROVB.InterlockedAnd64(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 and ptr %[[PTR]], i64 %{{.*}} syncscope("device") monotonic +// DXCHECK: store i64 %[[R]], ptr {{.*}} +export void test_rovb_uint64_3arg(uint off, uint64_t v, out uint64_t orig) { + ROVB.InterlockedAnd64(off, v, orig); +} diff --git a/clang/test/SemaHLSL/BuiltIns/InterlockedAnd-errors.hlsl b/clang/test/SemaHLSL/BuiltIns/InterlockedAnd-errors.hlsl new file mode 100644 index 0000000000000..6674d7140a44f --- /dev/null +++ b/clang/test/SemaHLSL/BuiltIns/InterlockedAnd-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 + +// InterlockedAnd 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) { + InterlockedAnd(gs_i32); // expected-error{{no matching function for call to 'InterlockedAnd'}} + // expected-note@*:* 16 {{candidate function}} +} + +void too_many(int v, int extra) { + int o; + InterlockedAnd(gs_i32, v, o, extra); // expected-error{{no matching function for call to 'InterlockedAnd'}} + // 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; + InterlockedAnd(dest, v); // expected-error{{no matching function for call to 'InterlockedAnd'}} + // expected-note@*:* 16 {{candidate function}} +} + +void float_dest(float v) { + InterlockedAnd(gs_f32, v); // expected-error{{no matching function for call to 'InterlockedAnd'}} + // expected-note@*:* 16 {{candidate function}} +} + +void struct_dest(int v) { + InterlockedAnd(gs_s, v); // expected-error{{no matching function for call to 'InterlockedAnd'}} + // expected-note@*:* 16 {{candidate function}} +} + +void mismatched_orig_type(int v) { + uint orig; + InterlockedAnd(gs_i32, v, orig); // expected-error{{no matching function for call to 'InterlockedAnd'}} + // expected-note@*:* 16 {{candidate function}} +} + +// The tests below exercise direct invocations of the underlying clang builtin +// `__builtin_hlsl_interlocked_and`. These bypass overload resolution against +// the synthesized `InterlockedAnd` 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_and(gs_i32); + // expected-error@-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_and(gs_i32, v, o, extra); + // expected-error@-1 {{too many arguments to function call, expected at most 3, have 4}} +} + +void direct_non_integer_dest() { + S local_s; + __builtin_hlsl_interlocked_and(local_s, 1); + // expected-error@-1 {{1st argument must be a scalar integer type (was 'S')}} +} + +void direct_nonlvalue_dest(int v) { + __builtin_hlsl_interlocked_and(1, v); + // expected-error@-1 {{cannot bind non-lvalue argument '1' to out parameter}} +} + +void direct_mismatched_value() { + uint uv = 1u; + __builtin_hlsl_interlocked_and(gs_i32, uv); + // expected-error@-1 {{passing 'uint' (aka 'unsigned int') to parameter of incompatible type 'int'}} +} + +void direct_mismatched_orig(int v) { + uint orig; + __builtin_hlsl_interlocked_and(gs_i32, v, orig); + // expected-error@-1 {{passing 'uint' (aka 'unsigned int') to parameter of incompatible type 'int'}} +} + +void direct_nonlvalue_orig(int v) { + __builtin_hlsl_interlocked_and(gs_i32, v, 1); + // expected-error@-1 {{cannot bind non-lvalue argument '1' to out parameter}} +} + +void direct_default_as_dest(int v) { + int local; + __builtin_hlsl_interlocked_and(local, v); + // expected-error@-1 {{1st argument to atomic builtin must reference groupshared or device memory (was 'int')}} +} >From 3bbad477dc6b1c246af1f8ecb786cc7c85e96221 Mon Sep 17 00:00:00 2001 From: Joshua Batista <[email protected]> Date: Thu, 27 Aug 2026 15:47:56 -0700 Subject: [PATCH 2/2] test quality improvement --- .../BuiltIns/InterlockedAnd-errors.hlsl | 31 ++++++------------- 1 file changed, 10 insertions(+), 21 deletions(-) diff --git a/clang/test/SemaHLSL/BuiltIns/InterlockedAnd-errors.hlsl b/clang/test/SemaHLSL/BuiltIns/InterlockedAnd-errors.hlsl index 6674d7140a44f..3ddd14bd15e01 100644 --- a/clang/test/SemaHLSL/BuiltIns/InterlockedAnd-errors.hlsl +++ b/clang/test/SemaHLSL/BuiltIns/InterlockedAnd-errors.hlsl @@ -3,29 +3,24 @@ // RUN: -disable-llvm-passes -verify // InterlockedAnd 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/device, {int,uint,int64_t,uint64_t}, 2-arg/3-arg). -groupshared int gs_i32; +groupshared int gs_i32; groupshared float gs_f32; struct S { int x; }; -groupshared S gs_s; +groupshared S gs_s; -void too_few(int v) { +void too_few() { InterlockedAnd(gs_i32); // expected-error{{no matching function for call to 'InterlockedAnd'}} // expected-note@*:* 16 {{candidate function}} } void too_many(int v, int extra) { - int o; - InterlockedAnd(gs_i32, v, o, extra); // expected-error{{no matching function for call to 'InterlockedAnd'}} + int orig; + InterlockedAnd(gs_i32, v, orig, extra); // expected-error{{no matching function for call to 'InterlockedAnd'}} // 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; InterlockedAnd(dest, v); // expected-error{{no matching function for call to 'InterlockedAnd'}} @@ -48,20 +43,14 @@ void mismatched_orig_type(int v) { // expected-note@*:* 16 {{candidate function}} } -// The tests below exercise direct invocations of the underlying clang builtin -// `__builtin_hlsl_interlocked_and`. These bypass overload resolution against -// the synthesized `InterlockedAnd` 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_and(gs_i32); // expected-error@-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_and(gs_i32, v, o, extra); + int orig; + __builtin_hlsl_interlocked_and(gs_i32, v, orig, extra); // expected-error@-1 {{too many arguments to function call, expected at most 3, have 4}} } @@ -77,8 +66,8 @@ void direct_nonlvalue_dest(int v) { } void direct_mismatched_value() { - uint uv = 1u; - __builtin_hlsl_interlocked_and(gs_i32, uv); + uint value = 1; + __builtin_hlsl_interlocked_and(gs_i32, value); // expected-error@-1 {{passing 'uint' (aka 'unsigned int') to parameter of incompatible type 'int'}} } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
