Repository: shader-slang/slang
Pull request #6845: Implement 64bit countbits intrinsic for select targets (#6433)
Author: login: sricker-nvidia
Labels: name: pr: non-breaking
description: PRs without breaking changes
Created: 2025-04-17T00:42:16Z | Merged: 2025-04-19T11:33:28Z

Change modifies the countbits intrinsic to use generics in order to support 64bit countbits on select platforms where this is supported.

This should align with the implementation of other uint64_t intrinsics such as abs, min, max and clamp.

Added new countbits64 test to verify changes.

Updated documentation for 64bit-type-support.html


Diff:
diff --git a/docs/64bit-type-support.md b/docs/64bit-type-support.md
index 15faccd864d..5f54f6601b6 100644
--- a/docs/64bit-type-support.md
+++ b/docs/64bit-type-support.md
@@ -147,7 +147,7 @@ D3D12    | FXC/DXBC         |      No        |          No        |   2
 2) uint64_t support requires https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/hlsl-shader-model-6-0-features-for-direct3d-12, so DXBC is not a target.
 
 The intrinsics available on `uint64_t` type are `abs`, `min`, `max`, `clamp` and `countbits`.
-The intrinsics available on `uint64_t` type are `abs`, `min`, `max` and `clamp`.
+The intrinsics available on `int64_t` type are `abs`, `min`, `max`, `clamp` and `countbits`.
 
 GLSL
 ====
diff --git a/prelude/slang-cpp-scalar-intrinsics.h b/prelude/slang-cpp-scalar-intrinsics.h
index 22b5e12e450..0a19eb3277e 100644
--- a/prelude/slang-cpp-scalar-intrinsics.h
+++ b/prelude/slang-cpp-scalar-intrinsics.h
@@ -661,6 +661,23 @@ SLANG_FORCE_INLINE double I32_asdouble(int32_t low, int32_t hi)
     return u.d;
 }
 
+SLANG_FORCE_INLINE uint32_t I32_countbits(int32_t v)
+{
+#if SLANG_GCC_FAMILY && !defined(SLANG_LLVM)
+    return __builtin_popcount(uint32_t(v));
+#elif SLANG_PROCESSOR_X86_64 && SLANG_VC
+    return __popcnt(uint32_t(v));
+#else
+    uint32_t c = 0;
+    while (v)
+    {
+        c++;
+        v &= v - 1;
+    }
+    return c;
+#endif
+}
+
 // ----------------------------- U32 -----------------------------------------
 
 SLANG_FORCE_INLINE uint32_t U32_abs(uint32_t f)
@@ -729,9 +746,6 @@ SLANG_FORCE_INLINE uint64_t U64_max(uint64_t a, uint64_t b)
     return a > b ? a : b;
 }
 
-// TODO(JS): We don't define countbits for 64bit in the core module currently.
-// It's not clear from documentation if it should return 32 or 64 bits, if it exists.
-// 32 bits can always hold the result, and will be implicitly promoted.
 SLANG_FORCE_INLINE uint32_t U64_countbits(uint64_t v)
 {
 #if SLANG_GCC_FAMILY && !defined(SLANG_LLVM)
@@ -765,6 +779,11 @@ SLANG_FORCE_INLINE int64_t I64_max(int64_t a, int64_t b)
     return a > b ? a : b;
 }
 
+SLANG_FORCE_INLINE uint32_t I64_countbits(int64_t v)
+{
+    return U64_countbits(uint64_t(v));
+}
+
 // ----------------------------- UPTR -----------------------------------------
 
 SLANG_FORCE_INLINE uintptr_t UPTR_abs(uintptr_t f)
diff --git a/prelude/slang-cuda-prelude.h b/prelude/slang-cuda-prelude.h
index 5585ad6e00b..38e018e3e3c 100644
--- a/prelude/slang-cuda-prelude.h
+++ b/prelude/slang-cuda-prelude.h
@@ -1823,6 +1823,11 @@ SLANG_FORCE_INLINE SLANG_CUDA_CALL double I32_asdouble(int32_t low, int32_t hi)
     return u.d;
 }
 
+SLANG_FORCE_INLINE SLANG_CUDA_CALL uint32_t I32_countbits(int32_t v)
+{
+    return __popc(uint32_t(v));
+}
+
 // ----------------------------- U32 -----------------------------------------
 
 // Unary
@@ -1882,6 +1887,11 @@ SLANG_FORCE_INLINE SLANG_CUDA_CALL int64_t I64_max(int64_t a, int64_t b)
     return a > b ? a : b;
 }
 
+SLANG_FORCE_INLINE SLANG_CUDA_CALL uint32_t I64_countbits(int64_t v)
+{
+    return __popcll(uint64_t(v));
+}
+
 // ----------------------------- U64 -----------------------------------------
 
 SLANG_FORCE_INLINE SLANG_CUDA_CALL int64_t U64_abs(uint64_t f)
diff --git a/source/slang/hlsl.meta.slang b/source/slang/hlsl.meta.slang
index e71997c6ce6..6b1a4579fc2 100644
--- a/source/slang/hlsl.meta.slang
+++ b/source/slang/hlsl.meta.slang
@@ -8028,7 +8028,8 @@ vector<T,N> cospi(vector<T,N> x)
 [__readNone]
 [ForceInline]
 [require(cpp_cuda_glsl_hlsl_metal_spirv_wgsl, shader5_sm_5_0)]
-uint countbits(uint value)
+__generic<T : __BuiltinIntegerType>
+uint countbits(T value)
 {
     __target_switch
     {
@@ -8037,22 +8038,42 @@ uint countbits(uint value)
     case glsl:
         __intrinsic_asm "bitCount";
     case metal:
-        __intrinsic_asm "popcount";
+        if(T is int64_t || T  is uint64_t)
+        {
+            // emulate 64-bit
+            uint2 value_uint2 = bit_cast<uint2>(value);
+            uint2 counted_bits_uint2 = countbits(value_uint2);
+            return counted_bits_uint2.x + counted_bits_uint2.y;
+        }
+        else
+        {
+            __intrinsic_asm "popcount";
+        }
     case cuda:
     case cpp:
         __intrinsic_asm "$P_countbits($0)";
     case spirv:
-        return spirv_asm {OpBitCount $$uint result $value};
+        if(T is int64_t || T  is uint64_t)
+        {
+            // emulate 64-bit
+            uint2 value_uint2 = bit_cast<uint2>(value);
+            uint2 counted_bits_uint2 = countbits(value_uint2);
+            return counted_bits_uint2.x + counted_bits_uint2.y;
+        }
+        else
+        {
+            return spirv_asm {OpBitCount $$uint result $value};
+        }
     case wgsl:
         __intrinsic_asm "countOneBits";
     }
 }
 
-__generic <let N : int>
+__generic<T : __BuiltinIntegerType, let N : int>
 [__readNone]
 [ForceInline]
 [require(cpp_cuda_glsl_hlsl_metal_spirv_wgsl, shader5_sm_5_0)]
-vector<uint, N> countbits(vector<uint, N> value)
+vector<uint, N> countbits(vector<T, N> value)
 {
     __target_switch
     {
@@ -8061,9 +8082,25 @@ vector<uint, N> countbits(vector<uint, N> value)
     case glsl:
         __intrinsic_asm "bitCount";
     case metal:
-        __intrinsic_asm "popcount";
+        if(T is int64_t || T  is uint64_t)
+        {
+            // emulate 64-bit
+            VECTOR_MAP_UNARY(uint, N, countbits, value);
+        }
+        else
+        {
+            __intrinsic_asm "popcount";
+        }
     case spirv:
-        return spirv_asm {OpBitCount $$vector<uint, N> result $value};
+        if(T is int64_t || T  is uint64_t)
+        {
+            // emulate 64-bit
+            VECTOR_MAP_UNARY(uint, N, countbits, value);
+        }
+        else
+        {
+            return spirv_asm {OpBitCount $$vector<uint, N> result $value};
+        }
     case wgsl:
         __intrinsic_asm "countOneBits";
     default:
diff --git a/tests/hlsl-intrinsic/countbits64.slang b/tests/hlsl-intrinsic/countbits64.slang
new file mode 100644
index 00000000000..a24b31477f9
--- /dev/null
+++ b/tests/hlsl-intrinsic/countbits64.slang
@@ -0,0 +1,44 @@
+//TEST(compute):COMPARE_COMPUTE_EX(filecheck-buffer=CHK):-slang -compute -cpu
+//TEST(compute):COMPARE_COMPUTE_EX(filecheck-buffer=CHK):-slang -vk -compute -render-feature int64
+//TEST(compute):COMPARE_COMPUTE_EX(filecheck-buffer=CHK):-slang -cuda -compute
+//TEST(compute):COMPARE_COMPUTE_EX(filecheck-buffer=CHK):-slang -mtl -compute
+// No support for uint64_t on fxc - we need SM6.0 and dxil to use uint64_t with d3d12
+// https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/hlsl-shader-model-6-0-features-for-direct3d-12
+//TEST(compute):COMPARE_COMPUTE_EX(filecheck-buffer=CHK):-slang -compute -dx12 -profile cs_6_0 -use-dxil -shaderobj -render-feature hardware-device
+
+//CHK:1
+
+//TEST_INPUT:ubuffer(data=[0], stride=4):out,name=outputBuffer
+RWStructuredBuffer<uint> outputBuffer;
+
+[numthreads(1, 1, 1)]
+void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID)
+{
+    uint r1 = countbits(0b1ULL << 32);
+    uint2 r2 = countbits(uint64_t2(0b0ULL << 32, 0b1ULL << 32));
+    uint3 r3 = countbits(uint64_t3(0b0ULL << 32, 0b1ULL << 32, 0b11ULL << 32));
+    uint4 r4 = countbits(uint64_t4(0b0ULL << 32, 0b1ULL << 32, 0b11ULL << 32, 0b111ULL << 32));
+
+    uint r5 = countbits(0b1LL << 32);
+    uint2 r6 = countbits(int64_t2(0b0LL << 32, 0b1LL << 32));
+    uint3 r7 = countbits(int64_t3(0b0LL << 32, 0b1LL << 32, 0b11LL << 32));
+    uint4 r8 = countbits(int64_t4(0b0LL << 32, 0b1LL << 32, 0b11LL << 32, 0b111LL << 32));
+
+    uint bigShiftU32 = 0b111U << 32;
+    int bigShiftI32 = 0b1111 << 32;
+
+    uint bitCountBigShiftU32 = countbits(bigShiftU32);
+    uint bitCountBigShiftI32 = countbits(bigShiftI32);
+
+    outputBuffer[0] = true
+        && (r1 == 1)
+        && (r2.x == 0 && r2.y == 1)
+        && (r3.x == 0 && r3.y == 1 && r3.z == 2)
+        && (r4.x == 0 && r4.y == 1 && r4.z == 2 && r4.w == 3)
+        && (r5 == 1)
+        && (r6.x == 0 && r6.y == 1)
+        && (r7.x == 0 && r7.y == 1 && r7.z == 2)
+        && (r8.x == 0 && r8.y == 1 && r8.z == 2 && r8.w == 3)
+        && (bitCountBigShiftU32 == 0 && bitCountBigShiftI32 == 0)
+        ;
+}


Reviews:
user: login: csyonghe
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-17T01:16:05Z
user: login: sricker-nvidia
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-17T01:46:21Z
user: login: csyonghe
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-17T01:48:14Z
user: login: jkwak-work
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-17T17:31:03Z
user: login: sricker-nvidia
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-17T18:16:24Z
user: login: sricker-nvidia
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-17T18:17:58Z
user: login: sricker-nvidia
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-17T18:19:18Z
user: login: sricker-nvidia
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-17T18:21:02Z
user: login: sricker-nvidia
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-17T21:11:29Z
user: login: jkwak-work
body: Looks good to me.
state: DISMISSED
author_association: COLLABORATOR
submitted_at: 2025-04-18T18:01:16Z
user: login: sricker-nvidia
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-18T18:01:58Z
user: login: jkwak-work
body: Looks good to me
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-04-19T10:33:52Z

Review comments:
diff_hunk: @@ -0,0 +1,46 @@
+//TEST(compute):COMPARE_COMPUTE_EX(filecheck-buffer=CHK):-slang -compute -cpu
+// Spirv lacks support for 64bit countbits, see https://docs.vulkan.org/spec/latest/appendices/spirvenv.html#VUID-StandaloneSpirv-Base-04781
path: tests/hlsl-intrinsic/countbits64.slang
user: login: csyonghe
body: I think we should just emulate this by bit cast it to uint2, and  then sum up two 32 bitcounts.
created_at: 2025-04-17T01:16:05Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,46 @@
+//TEST(compute):COMPARE_COMPUTE_EX(filecheck-buffer=CHK):-slang -compute -cpu
+// Spirv lacks support for 64bit countbits, see https://docs.vulkan.org/spec/latest/appendices/spirvenv.html#VUID-StandaloneSpirv-Base-04781
path: tests/hlsl-intrinsic/countbits64.slang
user: login: sricker-nvidia
body: Would this be something like:

%u32 = OpTypeInt 32 0;
%uint2 = OpTypeVector %u32 2;
%u64asuint2 = OpBitcast %uint2 $value
%bitcounts = OpBitCount  %u64asuint2
result : $$uint = OpIAdd ...
created_at: 2025-04-17T01:46:21Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,46 @@
+//TEST(compute):COMPARE_COMPUTE_EX(filecheck-buffer=CHK):-slang -compute -cpu
+// Spirv lacks support for 64bit countbits, see https://docs.vulkan.org/spec/latest/appendices/spirvenv.html#VUID-StandaloneSpirv-Base-04781
path: tests/hlsl-intrinsic/countbits64.slang
user: login: csyonghe
body: You can write the bit cast as slang code before the spirv_asm.
created_at: 2025-04-17T01:48:14Z
author_association: COLLABORATOR
diff_hunk: @@ -765,6 +779,23 @@ SLANG_FORCE_INLINE int64_t I64_max(int64_t a, int64_t b)
     return a > b ? a : b;
 }
 
+SLANG_FORCE_INLINE uint32_t I64_countbits(int64_t v)
path: prelude/slang-cpp-scalar-intrinsics.h
user: login: jkwak-work
body: Can this be just like this without copy&paste?
```
SLANG_FORCE_INLINE uint32_t I64_countbits(int64_t v)
{
     return U64_countbits(uint64_t(v));
}
```
created_at: 2025-04-17T17:25:43Z
author_association: COLLABORATOR
diff_hunk: @@ -661,6 +661,23 @@ SLANG_FORCE_INLINE double I32_asdouble(int32_t low, int32_t hi)
     return u.d;
 }
 
+SLANG_FORCE_INLINE uint32_t I32_countbits(int32_t v)
path: prelude/slang-cpp-scalar-intrinsics.h
user: login: jkwak-work
body: Don't we need uint32_t variant as well?
created_at: 2025-04-17T17:26:34Z
author_association: COLLABORATOR
diff_hunk: @@ -146,8 +146,10 @@ D3D12    | FXC/DXBC         |      No        |          No        |   2
 
 2) uint64_t support requires https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/hlsl-shader-model-6-0-features-for-direct3d-12, so DXBC is not a target.
 
+3) There are additional SPIR-V requirements applying to Vulkan shaders. uint64_t support for countbits is not available as the base operand of OpBitCount must be a 32-bit integer scalar or a vector of 32-bit integers (https://docs.vulkan.org/spec/latest/appendices/spirvenv.html#VUID-StandaloneSpirv-Base-04781)
path: docs/64bit-type-support.md
user: login: jkwak-work
body: I don't think this statement is necessary for emulating things that SPIRV natively doesn't support.
We have so many places doing emulations like that.
created_at: 2025-04-17T17:29:40Z
author_association: COLLABORATOR
diff_hunk: @@ -7960,7 +7960,8 @@ vector<T,N> cospi(vector<T,N> x)
 [__readNone]
 [ForceInline]
 [require(cpp_cuda_glsl_hlsl_metal_spirv_wgsl, shader5_sm_5_0)]
-uint countbits(uint value)
+__generic<T : __BuiltinIntegerType>
+uint countbits(T value)
 {
     __target_switch
path: source/slang/hlsl.meta.slang
user: login: jkwak-work
body: As Yong pointed out you should use `if ( T is XX)` thing that doesn't have a performance impact at the runtime.
You can also use `static_assert(T is XX)` to explicitly prevent the users from using a certain types by making them an error, although I don't think you need it for this issue.
created_at: 2025-04-17T17:30:58Z
author_association: COLLABORATOR
diff_hunk: @@ -765,6 +779,23 @@ SLANG_FORCE_INLINE int64_t I64_max(int64_t a, int64_t b)
     return a > b ? a : b;
 }
 
+SLANG_FORCE_INLINE uint32_t I64_countbits(int64_t v)
path: prelude/slang-cpp-scalar-intrinsics.h
user: login: sricker-nvidia
body: Sure, I'll update.
created_at: 2025-04-17T18:16:23Z
author_association: COLLABORATOR
diff_hunk: @@ -661,6 +661,23 @@ SLANG_FORCE_INLINE double I32_asdouble(int32_t low, int32_t hi)
     return u.d;
 }
 
+SLANG_FORCE_INLINE uint32_t I32_countbits(int32_t v)
path: prelude/slang-cpp-scalar-intrinsics.h
user: login: sricker-nvidia
body: Not sure I follow, could you clarify? We have a uint32_t variant at L716:

SLANG_FORCE_INLINE uint32_t U32_countbits(uint32_t v)
{
...
created_at: 2025-04-17T18:17:58Z
author_association: COLLABORATOR
diff_hunk: @@ -146,8 +146,10 @@ D3D12    | FXC/DXBC         |      No        |          No        |   2
 
 2) uint64_t support requires https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/hlsl-shader-model-6-0-features-for-direct3d-12, so DXBC is not a target.
 
+3) There are additional SPIR-V requirements applying to Vulkan shaders. uint64_t support for countbits is not available as the base operand of OpBitCount must be a 32-bit integer scalar or a vector of 32-bit integers (https://docs.vulkan.org/spec/latest/appendices/spirvenv.html#VUID-StandaloneSpirv-Base-04781)
path: docs/64bit-type-support.md
user: login: sricker-nvidia
body: I figured it would be good to have since the current version doesn't have spir-v emulation working. I'll remove this line if I'm able to get it functional based on some of the above suggestions.
created_at: 2025-04-17T18:19:18Z
author_association: COLLABORATOR
diff_hunk: @@ -7960,7 +7960,8 @@ vector<T,N> cospi(vector<T,N> x)
 [__readNone]
 [ForceInline]
 [require(cpp_cuda_glsl_hlsl_metal_spirv_wgsl, shader5_sm_5_0)]
-uint countbits(uint value)
+__generic<T : __BuiltinIntegerType>
+uint countbits(T value)
 {
     __target_switch
path: source/slang/hlsl.meta.slang
user: login: sricker-nvidia
body: Thanks, that's good to know. My limited understanding of intrinsics made me think that I needed to avoid most of the normal slang semantics when implementing functions like countbits. 
created_at: 2025-04-17T18:21:02Z
author_association: COLLABORATOR
diff_hunk: @@ -765,6 +779,23 @@ SLANG_FORCE_INLINE int64_t I64_max(int64_t a, int64_t b)
     return a > b ? a : b;
 }
 
+SLANG_FORCE_INLINE uint32_t I64_countbits(int64_t v)
path: prelude/slang-cpp-scalar-intrinsics.h
user: login: sricker-nvidia
body: I made this update for I64_countbits, but not for I32_countbits due to the layout of the header file. In the header file U64_countbits is defined/declared above I64_countbits, but I32_countbits is not defined above/before U32_countbits. The header lumps all of these integer functions together in sections in this format:

--- I32 ---
...
--- U32 ---
...
--- U64 ---
...
--- I64 ---

If it makes sense to move the U32 section above the I32 section then I can fix I32_countbits as well.
created_at: 2025-04-17T21:11:29Z
author_association: COLLABORATOR
diff_hunk: @@ -147,7 +147,7 @@ D3D12    | FXC/DXBC         |      No        |          No        |   2
 2) uint64_t support requires https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/hlsl-shader-model-6-0-features-for-direct3d-12, so DXBC is not a target.
 
 The intrinsics available on `uint64_t` type are `abs`, `min`, `max`, `clamp` and `countbits`.
-The intrinsics available on `uint64_t` type are `abs`, `min`, `max` and `clamp`.
+The intrinsics available on `int64_t` type are `abs`, `min`, `max, clamp` and `countbits`.
path: docs/64bit-type-support.md
user: login: jkwak-work
body: It looks like single quotes are missing between max and clamp.
created_at: 2025-04-18T17:58:11Z
author_association: COLLABORATOR
diff_hunk: @@ -661,6 +661,23 @@ SLANG_FORCE_INLINE double I32_asdouble(int32_t low, int32_t hi)
     return u.d;
 }
 
+SLANG_FORCE_INLINE uint32_t I32_countbits(int32_t v)
path: prelude/slang-cpp-scalar-intrinsics.h
user: login: jkwak-work
body: Oh. I didn't see that.
Thanks.
created_at: 2025-04-18T17:58:40Z
author_association: COLLABORATOR
diff_hunk: @@ -147,7 +147,7 @@ D3D12    | FXC/DXBC         |      No        |          No        |   2
 2) uint64_t support requires https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/hlsl-shader-model-6-0-features-for-direct3d-12, so DXBC is not a target.
 
 The intrinsics available on `uint64_t` type are `abs`, `min`, `max`, `clamp` and `countbits`.
-The intrinsics available on `uint64_t` type are `abs`, `min`, `max` and `clamp`.
+The intrinsics available on `int64_t` type are `abs`, `min`, `max, clamp` and `countbits`.
path: docs/64bit-type-support.md
user: login: sricker-nvidia
body: ah, good catch, I'll fix
created_at: 2025-04-18T18:01:58Z
author_association: COLLABORATOR

PR comments:
user: login: sricker-nvidia
created_at: 2025-04-17T01:06:43Z
body: Noting that I attempted to emulate 64-bit countbits for spir-v using something like:
    case spirv:
\-        return spirv_asm {OpBitCount $$uint result $value};
\+        return spirv_asm {
\+            %u64 = OpTypeInt 64 0;
\+            %u32 = OpTypeInt 32 0;
\+            %u64_32 = OpConstant %u64 32;
\+            %high = OpShiftRightLogical $value %u64_32;
\+            %low32 = OpUConvert %u32 $value;
\+            %high32 = OpUConvert %u32 %high;
\+            %lowCount = OpBitCount %low32;
\+            %highCount = OpBitCount %high32;
\+            result : $$uint = OpIAdd %lowCount %highCount
\+        };
But got complaints that OpConstant was being provided 5 words while expecting 4, presumably because it was using a 64-bit type. However, I don't understand the failure as this should be legal so long as "OpCapability Int64" is also given. I also see spirv-tools tests using similar spir-v assembly, so I'm not sure why it doesn't work.  The constant itself is needed as OpShiftRightLogical expects scalars of the same width (and value is 64-bit).

That aside, I'm also wondering if emulation like the above would have a negative impact on perf for the 32-bit case as now more instructions will be used to accomplish the same.
author_association: COLLABORATOR
user: login: csyonghe
created_at: 2025-04-17T01:18:05Z
body: you can use 
```
...
case spirv:
    if(T is int64_t || T  is uint64_t) return spirv_asm { // emulate};
    else{ uint32_t i32 = x;  return spirv_asm { // direct } }
```
   
author_association: COLLABORATOR
user: login: sricker-nvidia
created_at: 2025-04-17T01:32:57Z
body: Thanks Yong, I wasn't sure if I could mix conditionals with intrinsics. I can give that a try if I figure out the issue with OpConstant.
author_association: COLLABORATOR
user: login: juliusikkala
created_at: 2025-04-17T07:26:15Z
body: I wonder if the mac test failure here is the same kind as in #6786. There, the test involving a `vector<uintptr_t, 2>` fails only on Mac. Maybe there's something about 64-bit vectors that breaks on Mac OS builds?
author_association: CONTRIBUTOR
user: login: sricker-nvidia
created_at: 2025-04-17T17:31:28Z
body: Interesting, thanks for sharing Julius. I'm taking a look now.

I've also just noticed that some similar 64-bit intrinsic testing in scalar-uint64.slang is devoid of a metal subtest, so perhaps there's some history we're not aware of?

The metal spec that I'm looking at (https://developer.apple.com/metal/Metal-Shading-Language-Specification.pdf) doesn't explicitly state 64-bit support for things like min, abs, countbits, etc, but it feels implied as there is a row in Table 6.2. _Integer functions in the Metal standard library_:

`T popcount(T x) Returns the number of nonzero bits in x.`

in section 6.3 _Integer Functions_ and this section also has a paragraph stating:

`T is one of the scalar or vector integer types. Tu is the corresponding unsigned scalar or vector integer type. T32 is one of the scalar or vector 32-bit int or uint types.`

The fact that the table did not list T32 popcount(T32 x) ... to me implies 64-bit support, but that doesn't seem to be the case in practice. I see another line in the spec that mentions unsigned long uint64_t support being available in Metal 2.2 and later. Maybe a higher version needs to be requested?
author_association: COLLABORATOR