igneum/proto-cuda/packs-readwidth/scr4k128/program.metal

126 lines
7.9 KiB
Metal

#include <metal_stdlib>
using namespace metal;
#define MASK 0x0fffffffu
constant uint SEEDW[8] = { 0x67a9a7beu, 0x1a155b25u, 0xfddfb732u, 0x4b5af2e8u, 0xc55caf33u, 0xa27c13b7u, 0x06628a48u, 0x03852469u };
inline uint splitmix32(uint x) {
x ^= x >> 16; x *= 0x7feb352du;
x ^= x >> 15; x *= 0x846ca68bu;
x ^= x >> 16;
return x;
}
inline uint rotl_imm(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31
inline uint rotr_var(uint x, uint n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); }
inline uint ds_elem(uint i, uint d0, uint d1) {
uint x = i ^ d0;
x *= 0x9E3779B1u; x ^= x >> 15;
x += d1;
x *= 0x85EBCA77u; x ^= x >> 13;
x *= 0xC2B2AE3Du; x ^= x >> 16;
return x;
}
// Variant 5 (read-width experiment, 5 October 2026, NOT the lottery hash): a 128 KiB scratch per warp, 256 slots of
// 16 bytes per lane, lane-major. A slot starts the unit as the fill words below (tagged lazily: a slot whose tag is not
// this unit's reads as its fill) and holds what the unit wrote afterwards. scr_fill mirrors verify::scratch_fill.
inline uint scr_fill(uint gbase, uint lane, uint slot, uint j) { uint sw = (j == 0u) ? 0x67a9a7beu : ((j == 1u) ? 0x1a155b25u : 0xfddfb732u); return splitmix32(((gbase + lane) ^ sw) + slot * 0x9e3779b1u + (j + 1u) * 0x85ebca77u); }
kernel void igneum_hash(device const uint* dataset [[buffer(0)]],
device ulong* out [[buffer(1)]],
constant uint& baseNonce [[buffer(2)]],
device uint* scratch [[buffer(3)]],
constant uint& groups [[buffer(4)]],
constant uint& salt [[buffer(5)]],
uint tid [[thread_position_in_grid]],
uint nthreads [[threads_per_grid]]) {
uint lane = tid & 31u;
uint warp_ = tid >> 5;
uint nwarps_ = nthreads >> 5;
device uint* arena = scratch + ((size_t)warp_ * 32u + lane) * 1024u;
for (uint g_ = warp_; g_ < groups; g_ += nwarps_) {
uint gid = g_ * 32u + lane;
uint gbase = baseNonce + g_ * 32u;
uint tag = salt + g_;
uint nonce = baseNonce + gid;
uint r0, r1, r2, r3, r4, r5, r6, r7;
{ uint x = nonce ^ SEEDW[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ SEEDW[1]; }
{ uint x = nonce ^ SEEDW[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ SEEDW[2]; }
{ uint x = nonce ^ SEEDW[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ SEEDW[3]; }
{ uint x = nonce ^ SEEDW[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ SEEDW[4]; }
{ uint x = nonce ^ SEEDW[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ SEEDW[5]; }
{ uint x = nonce ^ SEEDW[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ SEEDW[6]; }
{ uint x = nonce ^ SEEDW[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ SEEDW[7]; }
{ uint x = nonce ^ SEEDW[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ SEEDW[0]; }
for (uint it = 0u; it < 8u; ++it) {
uint sel = r0;
r2 = r3 * r4 + r2; // 0
r1 = r1 + r7 + select(0x42da7657u, 0xc3bd2355u, ((sel >> 4u) & 1u) != 0u); // 1
r2 = r2 + r3 + select(0x61f0b51cu, 0x2735a174u, ((sel >> 26u) & 1u) != 0u); // 2
r4 = r0 * r6 + r4; // 3
r7 = r7 ^ dataset[r2 & MASK]; // 4
r4 = r4 ^ dataset[r1 & MASK]; // 5
r6 = r6 ^ simd_shuffle_xor(r3, (ushort)4); // 6
r1 = r1 ^ simd_shuffle_xor(r5, (ushort)8); // 7
r7 = r7 ^ r5; // 8
r3 = r3 | r4; // 9
r1 = r1 | r2; // 10
r4 = r4 ^ dataset[r3 & MASK]; // 11
r6 = r6 | r2; // 12
r2 = r2 * r5; // 13
r1 = r1 ^ dataset[r2 & MASK]; // 14
r7 = rotl_imm(r7, 1u); // 15
{ uint s_ = r6 & 255u; uint4 v_ = *(device const uint4*)(arena + s_ * 4u); uint m_ = (v_.x == tag) ? 0xffffffffu : 0u; uint w0_ = (v_.y & m_) | (scr_fill(gbase, lane, s_, 0u) & ~m_); uint w1_ = (v_.z & m_) | (scr_fill(gbase, lane, s_, 1u) & ~m_); uint w2_ = (v_.w & m_) | (scr_fill(gbase, lane, s_, 2u) & ~m_); uint x_ = r3 ^ w0_; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ w1_; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ w2_; r3 = x_; *(device uint4*)(arena + s_ * 4u) = uint4(tag, x_ ^ w1_, rotl_imm(x_, 7u) ^ w2_, x_ + w0_); } // 16
r7 = r7 ^ dataset[r4 & MASK]; // 17
r3 = r3 ^ simd_shuffle_xor(r4, (ushort)2); // 18
r4 = r0 * r2 + r4; // 19
r0 = r0 ^ simd_shuffle_xor(r6, (ushort)8); // 20
r5 = r5 ^ r7; // 21
r2 = mulhi(r2, r5); // 22
r3 = r3 ^ dataset[r7 & MASK]; // 23
r7 = mulhi(r7, r3); // 24
r5 = r5 | r4; // 25
r4 = r5 * r2 + r4; // 26
r5 = r5 * r1; // 27
r6 = mulhi(r6, r7); // 28
r6 = r6 + r1 + select(0x3b2d2124u, 0x187a9128u, ((sel >> 9u) & 1u) != 0u); // 29
r6 = rotr_var(r6, r7); // 30
r3 = r3 ^ dataset[r1 & MASK]; // 31
{ uint s_ = r0 & 255u; uint4 v_ = *(device const uint4*)(arena + s_ * 4u); uint m_ = (v_.x == tag) ? 0xffffffffu : 0u; uint w0_ = (v_.y & m_) | (scr_fill(gbase, lane, s_, 0u) & ~m_); uint w1_ = (v_.z & m_) | (scr_fill(gbase, lane, s_, 1u) & ~m_); uint w2_ = (v_.w & m_) | (scr_fill(gbase, lane, s_, 2u) & ~m_); uint x_ = r1 ^ w0_; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ w1_; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ w2_; r1 = x_; *(device uint4*)(arena + s_ * 4u) = uint4(tag, x_ ^ w1_, rotl_imm(x_, 7u) ^ w2_, x_ + w0_); } // 32
r0 = r0 + r4 + select(0x2c35699fu, 0x351dde38u, ((sel >> 18u) & 1u) != 0u); // 33
{ uint s_ = r2 & 255u; uint4 v_ = *(device const uint4*)(arena + s_ * 4u); uint m_ = (v_.x == tag) ? 0xffffffffu : 0u; uint w0_ = (v_.y & m_) | (scr_fill(gbase, lane, s_, 0u) & ~m_); uint w1_ = (v_.z & m_) | (scr_fill(gbase, lane, s_, 1u) & ~m_); uint w2_ = (v_.w & m_) | (scr_fill(gbase, lane, s_, 2u) & ~m_); uint x_ = r0 ^ w0_; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ w1_; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ w2_; r0 = x_; *(device uint4*)(arena + s_ * 4u) = uint4(tag, x_ ^ w1_, rotl_imm(x_, 7u) ^ w2_, x_ + w0_); } // 34
r0 = r0 * r3; // 35
r2 = r2 ^ r5; // 36
r4 = r4 ^ dataset[r0 & MASK]; // 37
r1 = r3 * r5 + r1; // 38
r0 = r0 + r3 + select(0xa907b90bu, 0x1b053acfu, ((sel >> 25u) & 1u) != 0u); // 39
r2 = rotr_var(r2, r5); // 40
r3 = r3 * r2; // 41
r1 = r1 + r5 + select(0xa32e000cu, 0x6058c2e3u, ((sel >> 20u) & 1u) != 0u); // 42
r3 = r3 ^ r4; // 43
r3 = r3 ^ dataset[r5 & MASK]; // 44
r1 = r1 + r5 + select(0x81b8bc2cu, 0x1907970cu, ((sel >> 7u) & 1u) != 0u); // 45
r7 = r7 ^ r1; // 46
r0 = r0 + r3 + select(0x838b5065u, 0x36360066u, ((sel >> 31u) & 1u) != 0u); // 47
r7 = mulhi(r7, r5); // 48
r0 = r0 ^ dataset[r2 & MASK]; // 49
r2 = r2 - r6; // 50
r7 = r7 - r5; // 51
r2 = r2 ^ r3; // 52
r7 = r7 - r0; // 53
r3 = r5 * r0 + r3; // 54
r7 = r7 ^ r5; // 55
r2 = r2 ^ dataset[r7 & MASK]; // 56
r5 = r5 - r6; // 57
r1 = r1 ^ dataset[r3 & MASK]; // 58
{ uint s_ = r4 & 255u; uint4 v_ = *(device const uint4*)(arena + s_ * 4u); uint m_ = (v_.x == tag) ? 0xffffffffu : 0u; uint w0_ = (v_.y & m_) | (scr_fill(gbase, lane, s_, 0u) & ~m_); uint w1_ = (v_.z & m_) | (scr_fill(gbase, lane, s_, 1u) & ~m_); uint w2_ = (v_.w & m_) | (scr_fill(gbase, lane, s_, 2u) & ~m_); uint x_ = r1 ^ w0_; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ w1_; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ w2_; r1 = x_; *(device uint4*)(arena + s_ * 4u) = uint4(tag, x_ ^ w1_, rotl_imm(x_, 7u) ^ w2_, x_ + w0_); } // 59
r4 = r4 - r6; // 60
r1 = r1 * r2; // 61
r3 = r6 * r0 + r3; // 62
r0 = r0 + r1 + select(0x2fe0e98bu, 0xc88e2942u, ((sel >> 16u) & 1u) != 0u); // 63
}
uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u);
uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u);
out[gid] = ((ulong)hi << 32) | (ulong)lo;
}
}