Author: Joshua Batista
Date: 2026-09-21T17:54:06-07:00
New Revision: 33fcd70991316e238e63684cd4018f618d690668

URL: 
https://github.com/llvm/llvm-project/commit/33fcd70991316e238e63684cd4018f618d690668
DIFF: 
https://github.com/llvm/llvm-project/commit/33fcd70991316e238e63684cd4018f618d690668.diff

LOG: [HLSL] Add `InterlockedMax` function and resource methods (#222160)

This PR adds the `InterlockedMax` standalone function and resource
methods.
It mimics the implementation of the `InterlockedMin` function and
resource
methods.

Like `min`, `max` is signedness aware. A signed operand lowers to
`atomicrmw max` and an unsigned operand lowers to `atomicrmw umax`. This
matches DXC, which selects between the `IMax` and `UMax` atomic binary
operations.

The PR also adds the 64-bit `InterlockedMax64` methods, which DXIL gates
on
shader model 6.6.

Fixes: https://github.com/llvm/llvm-project/issues/99124
Assisted by: Github Copilot

Added: 
    clang/test/CodeGenHLSL/builtins/InterlockedMax.hlsl
    clang/test/CodeGenHLSL/builtins/RWByteAddressBuffer-InterlockedMax.hlsl
    
clang/test/CodeGenHLSL/builtins/RasterizerOrderedByteAddressBuffer-InterlockedMax.hlsl
    clang/test/SemaHLSL/BuiltIns/ByteAddressBuffer-InterlockedMax-errors.hlsl
    
clang/test/SemaHLSL/BuiltIns/ByteAddressBuffer-InterlockedMax-sm65-errors.hlsl
    clang/test/SemaHLSL/BuiltIns/InterlockedMax-errors.hlsl

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 90340ad7f812c..80d09c089ae95 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -5559,6 +5559,12 @@ def HLSLInterlockedAnd : LangBuiltin<"HLSL_LANG"> {
   let Prototype = "void (...)";
 }
 
+def HLSLInterlockedMax : LangBuiltin<"HLSL_LANG"> {
+  let Spellings = ["__builtin_hlsl_interlocked_max"];
+  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 b1ec72ae46242..bb0fe135ff8b0 100644
--- a/clang/lib/CodeGen/CGHLSLBuiltins.cpp
+++ b/clang/lib/CodeGen/CGHLSLBuiltins.cpp
@@ -1461,6 +1461,13 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned 
BuiltinID,
   case Builtin::BI__builtin_hlsl_interlocked_and: {
     return handleInterlockedOp(*this, E, llvm::AtomicRMWInst::And);
   }
+  case Builtin::BI__builtin_hlsl_interlocked_max: {
+    llvm::AtomicRMWInst::BinOp Op =
+        E->getArg(0)->getType()->hasSignedIntegerRepresentation()
+            ? llvm::AtomicRMWInst::Max
+            : llvm::AtomicRMWInst::UMax;
+    return handleInterlockedOp(*this, E, Op);
+  }
   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 e49b1cba7b1dd..812f3d82c47a4 100644
--- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
+++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
@@ -1761,6 +1761,10 @@ 
BuiltinTypeDeclBuilder::addByteAddressBufferInterlockedMethods() {
                                         "__builtin_hlsl_interlocked_add");
   addByteAddressBufferInterlockedMethod("InterlockedAnd", AST.UnsignedIntTy,
                                         "__builtin_hlsl_interlocked_and");
+  addByteAddressBufferInterlockedMethod("InterlockedMax", AST.IntTy,
+                                        "__builtin_hlsl_interlocked_max");
+  addByteAddressBufferInterlockedMethod("InterlockedMax", AST.UnsignedIntTy,
+                                        "__builtin_hlsl_interlocked_max");
   addByteAddressBufferInterlockedMethod("InterlockedMin", AST.IntTy,
                                         "__builtin_hlsl_interlocked_min");
   addByteAddressBufferInterlockedMethod("InterlockedMin", AST.UnsignedIntTy,
@@ -1783,6 +1787,11 @@ 
BuiltinTypeDeclBuilder::addByteAddressBufferInterlockedMethods() {
     addByteAddressBufferInterlockedMethod("InterlockedAnd64",
                                           AST.UnsignedLongTy,
                                           "__builtin_hlsl_interlocked_and");
+    addByteAddressBufferInterlockedMethod("InterlockedMax64", AST.LongTy,
+                                          "__builtin_hlsl_interlocked_max");
+    addByteAddressBufferInterlockedMethod("InterlockedMax64",
+                                          AST.UnsignedLongTy,
+                                          "__builtin_hlsl_interlocked_max");
     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 3f56792b5ff66..ad6cedc90bc25 100644
--- a/clang/lib/Sema/HLSLExternalSemaSource.cpp
+++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp
@@ -907,6 +907,8 @@ void HLSLExternalSemaSource::defineHLSLAtomicIntrinsics() {
                             "__builtin_hlsl_interlocked_add");
   defineHLSLInterlockedFunc(*SemaPtr, HLSLNamespace, "InterlockedAnd",
                             "__builtin_hlsl_interlocked_and");
+  defineHLSLInterlockedFunc(*SemaPtr, HLSLNamespace, "InterlockedMax",
+                            "__builtin_hlsl_interlocked_max");
   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 461bb8fb96678..a5429a90f962b 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -4602,6 +4602,7 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned 
BuiltinID, CallExpr *TheCall) {
   }
   case Builtin::BI__builtin_hlsl_interlocked_add:
   case Builtin::BI__builtin_hlsl_interlocked_and:
+  case Builtin::BI__builtin_hlsl_interlocked_max:
   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/InterlockedMax.hlsl 
b/clang/test/CodeGenHLSL/builtins/InterlockedMax.hlsl
new file mode 100644
index 0000000000000..028b890be22cc
--- /dev/null
+++ b/clang/test/CodeGenHLSL/builtins/InterlockedMax.hlsl
@@ -0,0 +1,70 @@
+// 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
+
+// RUN: %clang_cc1 -std=hlsl2021 -finclude-default-header -triple \
+// RUN:   spirv-pc-vulkan-library %s -emit-llvm -disable-llvm-passes -o - | \
+// RUN:   FileCheck %s
+
+// Test signed and unsigned lowering of HLSL InterlockedMax.
+
+groupshared int gs_i32;
+groupshared uint gs_u32;
+groupshared int64_t gs_i64;
+groupshared uint64_t gs_u64;
+
+// CHECK-LABEL: define {{.*}}void @{{.*}}test_int_2arg
+// CHECK:     %[[R:.*]] = atomicrmw max ptr addrspace(3) {{.*}}@gs_i32{{.*}}, 
i32 %{{.*}} syncscope("workgroup") monotonic
+// CHECK-NOT: store i32 %[[R]]
+export void test_int_2arg(int v) {
+  InterlockedMax(gs_i32, v);
+}
+
+// CHECK-LABEL: define {{.*}}void @{{.*}}test_uint_2arg
+// CHECK:     %[[R:.*]] = atomicrmw umax ptr addrspace(3) {{.*}}@gs_u32{{.*}}, 
i32 %{{.*}} syncscope("workgroup") monotonic
+// CHECK-NOT: store i32 %[[R]]
+export void test_uint_2arg(uint v) {
+  InterlockedMax(gs_u32, v);
+}
+
+// CHECK-LABEL: define {{.*}}void @{{.*}}test_int_3arg
+// CHECK: %[[R:.*]] = atomicrmw max ptr addrspace(3) {{.*}}@gs_i32{{.*}}, i32 
%{{.*}} syncscope("workgroup") monotonic
+// CHECK: store i32 %[[R]], ptr {{.*}}
+export void test_int_3arg(int v, out int orig) {
+  InterlockedMax(gs_i32, v, orig);
+}
+
+// CHECK-LABEL: define {{.*}}void @{{.*}}test_uint_3arg
+// CHECK: %[[R:.*]] = atomicrmw umax ptr addrspace(3) {{.*}}@gs_u32{{.*}}, i32 
%{{.*}} syncscope("workgroup") monotonic
+// CHECK: store i32 %[[R]], ptr {{.*}}
+export void test_uint_3arg(uint v, out uint orig) {
+  InterlockedMax(gs_u32, v, orig);
+}
+
+// CHECK-LABEL: define {{.*}}void @{{.*}}test_int64_2arg
+// CHECK:     %[[R:.*]] = atomicrmw max ptr addrspace(3) {{.*}}@gs_i64{{.*}}, 
i64 %{{.*}} syncscope("workgroup") monotonic
+// CHECK-NOT: store i64 %[[R]]
+export void test_int64_2arg(int64_t v) {
+  InterlockedMax(gs_i64, v);
+}
+
+// CHECK-LABEL: define {{.*}}void @{{.*}}test_uint64_2arg
+// CHECK:     %[[R:.*]] = atomicrmw umax ptr addrspace(3) {{.*}}@gs_u64{{.*}}, 
i64 %{{.*}} syncscope("workgroup") monotonic
+// CHECK-NOT: store i64 %[[R]]
+export void test_uint64_2arg(uint64_t v) {
+  InterlockedMax(gs_u64, v);
+}
+
+// CHECK-LABEL: define {{.*}}void @{{.*}}test_int64_3arg
+// CHECK: %[[R:.*]] = atomicrmw max ptr addrspace(3) {{.*}}@gs_i64{{.*}}, i64 
%{{.*}} syncscope("workgroup") monotonic
+// CHECK: store i64 %[[R]], ptr {{.*}}
+export void test_int64_3arg(int64_t v, out int64_t orig) {
+  InterlockedMax(gs_i64, v, orig);
+}
+
+// CHECK-LABEL: define {{.*}}void @{{.*}}test_uint64_3arg
+// CHECK: %[[R:.*]] = atomicrmw umax 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) {
+  InterlockedMax(gs_u64, v, orig);
+}

diff  --git a/clang/test/CodeGenHLSL/builtins/RWBuffer-Interlocked.hlsl 
b/clang/test/CodeGenHLSL/builtins/RWBuffer-Interlocked.hlsl
index d79e69a52f9c4..81c53ee4a5baa 100644
--- a/clang/test/CodeGenHLSL/builtins/RWBuffer-Interlocked.hlsl
+++ b/clang/test/CodeGenHLSL/builtins/RWBuffer-Interlocked.hlsl
@@ -11,11 +11,11 @@
 // LangAS::hlsl_device branch of the dest-argument address-space check in
 // SemaHLSL and ensures the atomicrmw is emitted on the pointer returned by
 // resource.getpointer for a TypedBuffer (as opposed to the RawBuffer path
-// covered by the ByteAddressBuffer tests). InterlockedMin is called once on
-// a signed buffer and once on an unsigned buffer so that the signed/unsigned
-// atomicrmw selection is pinned to an exactly-named resource handle type
-// (spirv.SignedImage vs spirv.Image). Add new intrinsics here as more
-// InterlockedX operations gain resource support.
+// covered by the ByteAddressBuffer tests). InterlockedMin and InterlockedMax
+// are each called once on a signed buffer and once on an unsigned buffer so
+// that the signed/unsigned atomicrmw selection is pinned to an exactly-named
+// resource handle type (spirv.SignedImage vs spirv.Image). Add new intrinsics
+// here as more InterlockedX operations gain resource support.
 
 RWBuffer<int> Out : register(u0);
 RWBuffer<uint> UOut : register(u1);
@@ -33,6 +33,10 @@ RWBuffer<uint> UOut : register(u1);
 // 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
+// DXCHECK:  %[[PTR7:.*]] = call {{.*}} 
@llvm.dx.resource.getpointer.p0.tdx.TypedBuffer_i32_1_0_1t.i32(target("dx.TypedBuffer",
 i32, 1, 0, 1) %{{.*}}, i32 %{{.*}})
+// DXCHECK:  atomicrmw max ptr %[[PTR7]], i32 1 syncscope("device") monotonic
+// DXCHECK:  %[[PTR8:.*]] = call {{.*}} 
@llvm.dx.resource.getpointer.p0.tdx.TypedBuffer_i32_1_0_0t.i32(target("dx.TypedBuffer",
 i32, 1, 0, 0) %{{.*}}, i32 %{{.*}})
+// DXCHECK:  atomicrmw umax ptr %[[PTR8]], 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 %{{.*}})
@@ -45,6 +49,10 @@ RWBuffer<uint> UOut : register(u1);
 // 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
+// SPVCHECK: %[[PTR7:.*]] = call {{.*}} 
@llvm.spv.resource.getpointer.{{.*}}(target("spirv.SignedImage", i32, {{.*}}) 
%{{.*}}, i32 %{{.*}})
+// SPVCHECK: atomicrmw max ptr addrspace(11) %[[PTR7]], i32 1 
syncscope("device") monotonic
+// SPVCHECK: %[[PTR8:.*]] = call {{.*}} 
@llvm.spv.resource.getpointer.{{.*}}(target("spirv.Image", i32, {{.*}}) 
%{{.*}}, i32 %{{.*}})
+// SPVCHECK: atomicrmw umax ptr addrspace(11) %[[PTR8]], i32 1 
syncscope("device") monotonic
 [shader("compute")]
 [numthreads(1,1,1)]
 void main(uint3 id : SV_DispatchThreadID) {
@@ -54,4 +62,6 @@ void main(uint3 id : SV_DispatchThreadID) {
   InterlockedMin(Out[id.x], 1);
   InterlockedMin(UOut[id.x], 1u);
   InterlockedAnd(Out[id.x], 1);
+  InterlockedMax(Out[id.x], 1);
+  InterlockedMax(UOut[id.x], 1u);
 }

diff  --git 
a/clang/test/CodeGenHLSL/builtins/RWByteAddressBuffer-InterlockedMax.hlsl 
b/clang/test/CodeGenHLSL/builtins/RWByteAddressBuffer-InterlockedMax.hlsl
new file mode 100644
index 0000000000000..80d38171313b3
--- /dev/null
+++ b/clang/test/CodeGenHLSL/builtins/RWByteAddressBuffer-InterlockedMax.hlsl
@@ -0,0 +1,92 @@
+// 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 signed and unsigned RWByteAddressBuffer::InterlockedMax and
+// InterlockedMax64 methods lower through a resource pointer.
+
+RWByteAddressBuffer BAB : register(u0);
+
+// CHECK-LABEL: define {{.*}}void @{{.*}}test_bab_int
+// DXCHECK:  %[[PTR:.*]] = call ptr 
@llvm.dx.resource.getpointer.p0.tdx.RawBuffer_i8_1_0t.i32({{.*}})
+// DXCHECK:  atomicrmw max ptr %[[PTR]], i32 %{{.*}} syncscope("device") 
monotonic
+// SPVCHECK: %[[PTR:.*]] = call ptr addrspace(11) 
@llvm.spv.resource.getpointer.p11.tspirv.VulkanBuffer_a0i8_12_1t.i32({{.*}})
+// SPVCHECK: atomicrmw max ptr addrspace(11) %[[PTR]], i32 %{{.*}} 
syncscope("device") monotonic
+export void test_bab_int(uint off, int v) {
+  BAB.InterlockedMax(off, v);
+}
+
+// CHECK-LABEL: define {{.*}}void @{{.*}}test_bab_uint
+// DXCHECK:  %[[PTR:.*]] = call ptr 
@llvm.dx.resource.getpointer.p0.tdx.RawBuffer_i8_1_0t.i32({{.*}})
+// DXCHECK:  atomicrmw umax ptr %[[PTR]], i32 %{{.*}} syncscope("device") 
monotonic
+// SPVCHECK: %[[PTR:.*]] = call ptr addrspace(11) 
@llvm.spv.resource.getpointer.p11.tspirv.VulkanBuffer_a0i8_12_1t.i32({{.*}})
+// SPVCHECK: atomicrmw umax ptr addrspace(11) %[[PTR]], i32 %{{.*}} 
syncscope("device") monotonic
+export void test_bab_uint(uint off, uint v) {
+  BAB.InterlockedMax(off, v);
+}
+
+// CHECK-LABEL: define {{.*}}void @{{.*}}test_bab_int_orig
+// DXCHECK:  %[[PTR:.*]] = call ptr 
@llvm.dx.resource.getpointer.p0.tdx.RawBuffer_i8_1_0t.i32({{.*}})
+// DXCHECK:  %[[R:.*]] = atomicrmw max ptr %[[PTR]], i32 %{{.*}} 
syncscope("device") monotonic
+// DXCHECK:  store i32 %[[R]], ptr {{.*}}
+// SPVCHECK: %[[PTR:.*]] = call ptr addrspace(11) 
@llvm.spv.resource.getpointer.p11.tspirv.VulkanBuffer_a0i8_12_1t.i32({{.*}})
+// SPVCHECK: %[[R:.*]] = atomicrmw max ptr addrspace(11) %[[PTR]], i32 %{{.*}} 
syncscope("device") monotonic
+// SPVCHECK: store i32 %[[R]], ptr {{.*}}
+export void test_bab_int_orig(uint off, int v, out int orig) {
+  BAB.InterlockedMax(off, v, orig);
+}
+
+// CHECK-LABEL: define {{.*}}void @{{.*}}test_bab_uint_orig
+// DXCHECK:  %[[PTR:.*]] = call ptr 
@llvm.dx.resource.getpointer.p0.tdx.RawBuffer_i8_1_0t.i32({{.*}})
+// DXCHECK:  %[[R:.*]] = atomicrmw umax ptr %[[PTR]], i32 %{{.*}} 
syncscope("device") monotonic
+// DXCHECK:  store i32 %[[R]], ptr {{.*}}
+// SPVCHECK: %[[PTR:.*]] = call ptr addrspace(11) 
@llvm.spv.resource.getpointer.p11.tspirv.VulkanBuffer_a0i8_12_1t.i32({{.*}})
+// SPVCHECK: %[[R:.*]] = atomicrmw umax ptr addrspace(11) %[[PTR]], i32 
%{{.*}} syncscope("device") monotonic
+// SPVCHECK: store i32 %[[R]], ptr {{.*}}
+export void test_bab_uint_orig(uint off, uint v, out uint orig) {
+  BAB.InterlockedMax(off, v, orig);
+}
+
+// CHECK-LABEL: define {{.*}}void @{{.*}}test_bab_int64
+// DXCHECK:  %[[PTR:.*]] = call ptr 
@llvm.dx.resource.getpointer.p0.tdx.RawBuffer_i8_1_0t.i32({{.*}})
+// DXCHECK:  atomicrmw max ptr %[[PTR]], i64 %{{.*}} syncscope("device") 
monotonic
+// SPVCHECK: %[[PTR:.*]] = call ptr addrspace(11) 
@llvm.spv.resource.getpointer.p11.tspirv.VulkanBuffer_a0i8_12_1t.i32({{.*}})
+// SPVCHECK: atomicrmw max ptr addrspace(11) %[[PTR]], i64 %{{.*}} 
syncscope("device") monotonic
+export void test_bab_int64(uint off, int64_t v) {
+  BAB.InterlockedMax64(off, v);
+}
+
+// CHECK-LABEL: define {{.*}}void @{{.*}}test_bab_uint64
+// DXCHECK:  %[[PTR:.*]] = call ptr 
@llvm.dx.resource.getpointer.p0.tdx.RawBuffer_i8_1_0t.i32({{.*}})
+// DXCHECK:  atomicrmw umax ptr %[[PTR]], i64 %{{.*}} syncscope("device") 
monotonic
+// SPVCHECK: %[[PTR:.*]] = call ptr addrspace(11) 
@llvm.spv.resource.getpointer.p11.tspirv.VulkanBuffer_a0i8_12_1t.i32({{.*}})
+// SPVCHECK: atomicrmw umax ptr addrspace(11) %[[PTR]], i64 %{{.*}} 
syncscope("device") monotonic
+export void test_bab_uint64(uint off, uint64_t v) {
+  BAB.InterlockedMax64(off, v);
+}
+
+// CHECK-LABEL: define {{.*}}void @{{.*}}test_bab_int64_orig
+// DXCHECK:  %[[PTR:.*]] = call ptr 
@llvm.dx.resource.getpointer.p0.tdx.RawBuffer_i8_1_0t.i32({{.*}})
+// DXCHECK:  %[[R:.*]] = atomicrmw max ptr %[[PTR]], i64 %{{.*}} 
syncscope("device") monotonic
+// DXCHECK:  store i64 %[[R]], ptr {{.*}}
+// SPVCHECK: %[[PTR:.*]] = call ptr addrspace(11) 
@llvm.spv.resource.getpointer.p11.tspirv.VulkanBuffer_a0i8_12_1t.i32({{.*}})
+// SPVCHECK: %[[R:.*]] = atomicrmw max ptr addrspace(11) %[[PTR]], i64 %{{.*}} 
syncscope("device") monotonic
+// SPVCHECK: store i64 %[[R]], ptr {{.*}}
+export void test_bab_int64_orig(uint off, int64_t v, out int64_t orig) {
+  BAB.InterlockedMax64(off, v, orig);
+}
+
+// CHECK-LABEL: define {{.*}}void @{{.*}}test_bab_uint64_orig
+// DXCHECK:  %[[PTR:.*]] = call ptr 
@llvm.dx.resource.getpointer.p0.tdx.RawBuffer_i8_1_0t.i32({{.*}})
+// DXCHECK:  %[[R:.*]] = atomicrmw umax ptr %[[PTR]], i64 %{{.*}} 
syncscope("device") monotonic
+// DXCHECK:  store i64 %[[R]], ptr {{.*}}
+// SPVCHECK: %[[PTR:.*]] = call ptr addrspace(11) 
@llvm.spv.resource.getpointer.p11.tspirv.VulkanBuffer_a0i8_12_1t.i32({{.*}})
+// SPVCHECK: %[[R:.*]] = atomicrmw umax ptr addrspace(11) %[[PTR]], i64 
%{{.*}} syncscope("device") monotonic
+// SPVCHECK: store i64 %[[R]], ptr {{.*}}
+export void test_bab_uint64_orig(uint off, uint64_t v, out uint64_t orig) {
+  BAB.InterlockedMax64(off, v, orig);
+}

diff  --git a/clang/test/CodeGenHLSL/builtins/RWTexture-Interlocked.hlsl 
b/clang/test/CodeGenHLSL/builtins/RWTexture-Interlocked.hlsl
index 8e2e7c9e57165..90c8c7e33836a 100644
--- a/clang/test/CodeGenHLSL/builtins/RWTexture-Interlocked.hlsl
+++ b/clang/test/CodeGenHLSL/builtins/RWTexture-Interlocked.hlsl
@@ -31,6 +31,10 @@ RWTexture2DArray<uint> UOut : register(u1);
 // DXCHECK:  atomicrmw xor ptr %[[PTR4]], i32 1 syncscope("device") monotonic
 // DXCHECK:  %[[PTR5:.*]] = call {{.*}} 
@llvm.dx.resource.getpointer.{{.*}}(target("dx.Texture", i32, 1, 0, 0, 7) 
%{{.*}}, <3 x i32> %{{.*}})
 // DXCHECK:  atomicrmw umin ptr %[[PTR5]], i32 1 syncscope("device") monotonic
+// DXCHECK:  %[[PTR6:.*]] = call {{.*}} 
@llvm.dx.resource.getpointer.{{.*}}(target("dx.Texture", i32, 1, 0, 1, 2) 
%{{.*}}, <2 x i32> %{{.*}})
+// DXCHECK:  atomicrmw max ptr %[[PTR6]], i32 1 syncscope("device") monotonic
+// DXCHECK:  %[[PTR7:.*]] = call {{.*}} 
@llvm.dx.resource.getpointer.{{.*}}(target("dx.Texture", i32, 1, 0, 0, 7) 
%{{.*}}, <3 x i32> %{{.*}})
+// DXCHECK:  atomicrmw umax ptr %[[PTR7]], i32 1 syncscope("device") 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> %{{.*}})
@@ -41,6 +45,10 @@ RWTexture2DArray<uint> UOut : register(u1);
 // SPVCHECK: atomicrmw xor ptr addrspace(11) %[[PTR4]], i32 1 
syncscope("device") monotonic
 // SPVCHECK: %[[PTR5:.*]] = call {{.*}} 
@llvm.spv.resource.getpointer.{{.*}}(target("spirv.Image", i32, {{.*}}) 
%{{.*}}, <3 x i32> %{{.*}})
 // SPVCHECK: atomicrmw umin ptr addrspace(11) %[[PTR5]], i32 1 
syncscope("device") monotonic
+// SPVCHECK: %[[PTR6:.*]] = call {{.*}} 
@llvm.spv.resource.getpointer.{{.*}}(target("spirv.SignedImage", i32, {{.*}}) 
%{{.*}}, <2 x i32> %{{.*}})
+// SPVCHECK: atomicrmw max ptr addrspace(11) %[[PTR6]], i32 1 
syncscope("device") monotonic
+// SPVCHECK: %[[PTR7:.*]] = call {{.*}} 
@llvm.spv.resource.getpointer.{{.*}}(target("spirv.Image", i32, {{.*}}) 
%{{.*}}, <3 x i32> %{{.*}})
+// SPVCHECK: atomicrmw umax ptr addrspace(11) %[[PTR7]], i32 1 
syncscope("device") monotonic
 [shader("compute")]
 [numthreads(1,1,1)]
 void main(uint3 id : SV_DispatchThreadID) {
@@ -49,4 +57,6 @@ void main(uint3 id : SV_DispatchThreadID) {
   InterlockedOr(UOut[id], 1u);
   InterlockedXor(UOut[id], 1u);
   InterlockedMin(UOut[id], 1u);
+  InterlockedMax(Out[id.xy], 1);
+  InterlockedMax(UOut[id], 1u);
 }

diff  --git 
a/clang/test/CodeGenHLSL/builtins/RasterizerOrderedByteAddressBuffer-InterlockedMax.hlsl
 
b/clang/test/CodeGenHLSL/builtins/RasterizerOrderedByteAddressBuffer-InterlockedMax.hlsl
new file mode 100644
index 0000000000000..bdee1a77ee477
--- /dev/null
+++ 
b/clang/test/CodeGenHLSL/builtins/RasterizerOrderedByteAddressBuffer-InterlockedMax.hlsl
@@ -0,0 +1,39 @@
+// 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
+
+// SPIR-V codegen for RasterizerOrderedByteAddressBuffer is not implemented.
+
+RasterizerOrderedByteAddressBuffer ROVB : register(u1);
+
+// CHECK-LABEL: define void @{{.*}}test_rovb_int
+// CHECK:     %[[PTR:.*]] = call ptr 
@llvm.dx.resource.getpointer.p0.tdx.RawBuffer_i8_1_1t.i32({{.*}})
+// CHECK:     %[[R:.*]] = atomicrmw max ptr %[[PTR]], i32 %{{.*}} 
syncscope("device") monotonic
+// CHECK-NOT: store i32 %[[R]]
+export void test_rovb_int(uint off, int v) {
+  ROVB.InterlockedMax(off, v);
+}
+
+// CHECK-LABEL: define void @{{.*}}test_rovb_uint_orig
+// CHECK: %[[PTR:.*]] = call ptr 
@llvm.dx.resource.getpointer.p0.tdx.RawBuffer_i8_1_1t.i32({{.*}})
+// CHECK: %[[R:.*]] = atomicrmw umax ptr %[[PTR]], i32 %{{.*}} 
syncscope("device") monotonic
+// CHECK: store i32 %[[R]], ptr {{.*}}
+export void test_rovb_uint_orig(uint off, uint v, out uint orig) {
+  ROVB.InterlockedMax(off, v, orig);
+}
+
+// CHECK-LABEL: define void @{{.*}}test_rovb_int64
+// CHECK:     %[[PTR:.*]] = call ptr 
@llvm.dx.resource.getpointer.p0.tdx.RawBuffer_i8_1_1t.i32({{.*}})
+// CHECK:     %[[R:.*]] = atomicrmw max ptr %[[PTR]], i64 %{{.*}} 
syncscope("device") monotonic
+// CHECK-NOT: store i64 %[[R]]
+export void test_rovb_int64(uint off, int64_t v) {
+  ROVB.InterlockedMax64(off, v);
+}
+
+// CHECK-LABEL: define void @{{.*}}test_rovb_uint64_orig
+// CHECK: %[[PTR:.*]] = call ptr 
@llvm.dx.resource.getpointer.p0.tdx.RawBuffer_i8_1_1t.i32({{.*}})
+// CHECK: %[[R:.*]] = atomicrmw umax ptr %[[PTR]], i64 %{{.*}} 
syncscope("device") monotonic
+// CHECK: store i64 %[[R]], ptr {{.*}}
+export void test_rovb_uint64_orig(uint off, uint64_t v, out uint64_t orig) {
+  ROVB.InterlockedMax64(off, v, orig);
+}

diff  --git 
a/clang/test/SemaHLSL/BuiltIns/ByteAddressBuffer-InterlockedMax-errors.hlsl 
b/clang/test/SemaHLSL/BuiltIns/ByteAddressBuffer-InterlockedMax-errors.hlsl
new file mode 100644
index 0000000000000..2ec7cc6e69ba4
--- /dev/null
+++ b/clang/test/SemaHLSL/BuiltIns/ByteAddressBuffer-InterlockedMax-errors.hlsl
@@ -0,0 +1,34 @@
+// RUN: %clang_cc1 -std=hlsl202x -finclude-default-header \
+// RUN:   -triple dxil-pc-shadermodel6.6-library %s -fsyntax-only -verify \
+// RUN:   -verify-ignore-unexpected=note,warning
+
+RWByteAddressBuffer BAB : register(u0);
+RasterizerOrderedByteAddressBuffer ROVB : register(u1);
+
+struct S { int x; };
+
+void too_few(uint off) {
+  BAB.InterlockedMax(off);
+  // expected-error@-1 {{no matching member function for call to 
'InterlockedMax'}}
+}
+
+void too_many(uint off, int v, int extra) {
+  int orig;
+  BAB.InterlockedMax(off, v, orig, extra);
+  // expected-error@-1 {{no matching member function for call to 
'InterlockedMax'}}
+}
+
+void struct_value(uint off, S v) {
+  BAB.InterlockedMax(off, v);
+  // expected-error@-1 {{no matching member function for call to 
'InterlockedMax'}}
+}
+
+void rovb_too_few(uint off) {
+  ROVB.InterlockedMax(off);
+  // expected-error@-1 {{no matching member function for call to 
'InterlockedMax'}}
+}
+
+void rovb_struct_value(uint off, S v) {
+  ROVB.InterlockedMax(off, v);
+  // expected-error@-1 {{no matching member function for call to 
'InterlockedMax'}}
+}

diff  --git 
a/clang/test/SemaHLSL/BuiltIns/ByteAddressBuffer-InterlockedMax-sm65-errors.hlsl
 
b/clang/test/SemaHLSL/BuiltIns/ByteAddressBuffer-InterlockedMax-sm65-errors.hlsl
new file mode 100644
index 0000000000000..4694d81e5b24b
--- /dev/null
+++ 
b/clang/test/SemaHLSL/BuiltIns/ByteAddressBuffer-InterlockedMax-sm65-errors.hlsl
@@ -0,0 +1,26 @@
+// RUN: %clang_cc1 -std=hlsl202x -finclude-default-header \
+// RUN:   -triple dxil-pc-shadermodel6.5-library %s -fsyntax-only -verify \
+// RUN:   -verify-ignore-unexpected=warning
+
+RWByteAddressBuffer BAB : register(u0);
+RasterizerOrderedByteAddressBuffer ROVB : register(u1);
+
+void sm65_no_bab_max64(uint off, int64_t v) {
+  BAB.InterlockedMax64(off, v);
+  // expected-error@-1 {{no member named 'InterlockedMax64' in 
'hlsl::RWByteAddressBuffer'}}
+}
+
+void sm65_no_rovb_max64(uint off, int64_t v) {
+  ROVB.InterlockedMax64(off, v);
+  // expected-error@-1 {{no member named 'InterlockedMax64' in 
'hlsl::RasterizerOrderedByteAddressBuffer'}}
+}
+
+void sm65_bab_max32_ok(uint off, int v) {
+  BAB.InterlockedMax(off, v);
+}
+
+groupshared int64_t gs_i64;
+void sm65_direct_builtin(int64_t v) {
+  __builtin_hlsl_interlocked_max(gs_i64, v);
+  // expected-error@-1 {{'__builtin_hlsl_interlocked_max' requires shader 
model 6.6 or newer}}
+}

diff  --git a/clang/test/SemaHLSL/BuiltIns/InterlockedMax-errors.hlsl 
b/clang/test/SemaHLSL/BuiltIns/InterlockedMax-errors.hlsl
new file mode 100644
index 0000000000000..9f563d5e2ac38
--- /dev/null
+++ b/clang/test/SemaHLSL/BuiltIns/InterlockedMax-errors.hlsl
@@ -0,0 +1,89 @@
+// RUN: %clang_cc1 -std=hlsl202x -finclude-default-header \
+// RUN:   -triple dxil-pc-shadermodel6.6-library %s -emit-llvm-only \
+// RUN:   -disable-llvm-passes -verify
+
+// InterlockedMax is provided as a set of address-space-qualified overloads
+// (groupshared/device, {int,uint,int64_t,uint64_t}, 2-arg/3-arg).
+
+groupshared int gs_i32;
+groupshared float gs_f32;
+struct S { int x; };
+groupshared S gs_s;
+
+void too_few() {
+  InterlockedMax(gs_i32); // expected-error{{no matching function for call to 
'InterlockedMax'}}
+  // expected-note@*:* 16 {{candidate function}}
+}
+
+void too_many(int v, int extra) {
+  int orig;
+  InterlockedMax(gs_i32, v, orig, extra); // expected-error{{no matching 
function for call to 'InterlockedMax'}}
+  // expected-note@*:* 16 {{candidate function}}
+}
+
+void local_dest(int v) {
+  int dest;
+  InterlockedMax(dest, v); // expected-error{{no matching function for call to 
'InterlockedMax'}}
+  // expected-note@*:* 16 {{candidate function}}
+}
+
+void float_dest(float v) {
+  InterlockedMax(gs_f32, v); // expected-error{{no matching function for call 
to 'InterlockedMax'}}
+  // expected-note@*:* 16 {{candidate function}}
+}
+
+void struct_dest(int v) {
+  InterlockedMax(gs_s, v); // expected-error{{no matching function for call to 
'InterlockedMax'}}
+  // expected-note@*:* 16 {{candidate function}}
+}
+
+void mismatched_orig_type(int v) {
+  uint orig;
+  InterlockedMax(gs_i32, v, orig); // expected-error{{no matching function for 
call to 'InterlockedMax'}}
+  // expected-note@*:* 16 {{candidate function}}
+}
+
+void direct_too_few() {
+  __builtin_hlsl_interlocked_max(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 orig;
+  __builtin_hlsl_interlocked_max(gs_i32, v, orig, 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_max(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_max(1, v);
+  // expected-error@-1 {{cannot bind non-lvalue argument '1' to out parameter}}
+}
+
+void direct_mismatched_value() {
+  uint value = 1;
+  __builtin_hlsl_interlocked_max(gs_i32, value);
+  // 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_max(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_max(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_max(local, v);
+  // expected-error@-1 {{1st argument to atomic builtin must reference 
groupshared or device memory (was 'int')}}
+}


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to