igneum-pow 0.2.0: generator v2 draws exactly 16 load slots from instructions 1..63, a load's source from the registers written earlier and not read by a load since, the other 48 ops from the ten non-load weights; accept.rs is spec 01 section 1.4.6 (static: no stale load source, every register injected; dynamic: 64 units on the seed-keyed closed-form dataset, no constant bit, no lane-constant site, under 164 saturated, bias within 136 of 1024, distinct addresses above 245,760); a rejected candidate is replaced by the next attempt of the seed (seed || k_le32), 32 a consensus fault. Packs carry the generator version, attempt and program id. Version 1 kept as generate_v1 for the census. Packs: igneum-genesis, igneum-hourly, igneum-genesis-mh regenerated by igneum-pow export; new igneum-devnet-v4-epoch0 (devnet genesis hash, day bytes 20730). Checks: Rust 39 of 39 tests; Metal natively via the Swift port (export cross-check 3 of 3 warps, identical programs and vectors on five seeds incl. three with attempt 1, fuzz 2,000 of 2,000); CUDA emu 4 of 4 packs; OpenCL emu 2 packs x 2 configurations; Apple OpenCL 4 of 4 packs at 27.9 Mhash/s. Census 20,000: 5.225 percent rejected, accepted distinct mean 127.887. Spec 01 0.2 (1.4.2, 1.4.3, 1.4.6, 1.11, 1.15, 1.16, 1.17), igneum-pow README, the CUDA, OpenCL and Metal test notes, bench-log entry, ledger M5 and M6 Fixed. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
145 lines
7.4 KiB
Text
145 lines
7.4 KiB
Text
// Generated by igneum-pow export (generator v2) for seed "igneum-hourly". Do not edit by hand.
|
|
// Bit-exact twin of the Metal kernel for the same seed (see proto-cuda/CHECKLIST.md and program.metal).
|
|
// Compiled ahead of time by nvcc together with proto-cuda/host.cu. No NVRTC.
|
|
#include <cuda_runtime.h>
|
|
#include <cstdint>
|
|
#include "program.h"
|
|
|
|
__device__ __forceinline__ uint32_t splitmix32(uint32_t x) {
|
|
x ^= x >> 16; x *= 0x7feb352du;
|
|
x ^= x >> 15; x *= 0x846ca68bu;
|
|
x ^= x >> 16;
|
|
return x;
|
|
}
|
|
// n is a literal in 1..31 at every call site, so both shift amounts are in 1..31.
|
|
__device__ __forceinline__ uint32_t rotl_imm(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); }
|
|
// n is masked to 0..31; the second shift amount is masked too, so n == 0 gives x.
|
|
__device__ __forceinline__ uint32_t rotr_var(uint32_t x, uint32_t n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); }
|
|
__device__ __forceinline__ uint32_t ds_elem(uint32_t i, uint32_t d0, uint32_t d1) {
|
|
uint32_t x = i ^ d0;
|
|
x *= 0x9E3779B1u; x ^= x >> 15;
|
|
x += d1;
|
|
x *= 0x85EBCA77u; x ^= x >> 13;
|
|
x *= 0xC2B2AE3Du; x ^= x >> 16;
|
|
return x;
|
|
}
|
|
|
|
// dataset[i] = ds_elem(i, d0, d1) for i < n. Same closed form as the Metal igneum_fill kernel.
|
|
__global__ void igneum_fill(uint32_t* ds, uint32_t n, uint32_t d0, uint32_t d1) {
|
|
uint32_t i = blockIdx.x * blockDim.x + threadIdx.x;
|
|
if (i < n) ds[i] = ds_elem(i, d0, d1);
|
|
}
|
|
|
|
// One hash per thread. blockDim.x is a multiple of 32; lane = threadIdx.x & 31 and every
|
|
// __shfl_xor_sync stays inside the lane's own warp, exactly like simd_shuffle_xor inside a
|
|
// 32-wide Metal SIMD group. Control flow is uniform, so the full 0xffffffff member mask is valid.
|
|
__global__ void igneum_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask) {
|
|
uint32_t gid = blockIdx.x * blockDim.x + threadIdx.x;
|
|
uint32_t nonce = baseNonce + gid;
|
|
uint32_t r0, r1, r2, r3, r4, r5, r6, r7;
|
|
{ uint32_t x = nonce ^ 0x6bdee811u; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x8f488bbeu; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1]
|
|
{ uint32_t x = nonce ^ 0x8f488bbeu; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0xc5cdece7u; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2]
|
|
{ uint32_t x = nonce ^ 0xc5cdece7u; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x210af22du; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3]
|
|
{ uint32_t x = nonce ^ 0x210af22du; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0x2f687b65u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4]
|
|
{ uint32_t x = nonce ^ 0x2f687b65u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0x17471eeeu; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5]
|
|
{ uint32_t x = nonce ^ 0x17471eeeu; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0xee16e284u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6]
|
|
{ uint32_t x = nonce ^ 0xee16e284u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0xfc9eb8f9u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7]
|
|
{ uint32_t x = nonce ^ 0xfc9eb8f9u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x6bdee811u; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0]
|
|
|
|
for (uint32_t it = 0u; it < 8u; ++it) {
|
|
uint32_t sel = r0;
|
|
r5 = __umulhi(r5, r0); // 0 mulhi
|
|
r6 = r6 ^ ds[r5 & mask]; // 1 load
|
|
r0 = r0 ^ ds[r6 & mask]; // 2 load
|
|
r5 = r5 ^ ds[r0 & mask]; // 3 load
|
|
r3 = r3 - r0; // 4 sub
|
|
r6 = r5 * r4 + r6; // 5 mad
|
|
r7 = r7 * r2; // 6 mul
|
|
r5 = r5 + r1 + ((((sel >> 1u) & 1u) != 0u) ? 0xc4382c99u : 0x99d561fcu); // 7 add
|
|
r1 = r1 ^ ds[r7 & mask]; // 8 load
|
|
r5 = r5 ^ ds[r1 & mask]; // 9 load
|
|
r0 = r6 * r2 + r0; // 10 mad
|
|
r6 = __umulhi(r6, r0); // 11 mulhi
|
|
r2 = rotl_imm(r2, 25u); // 12 rotl
|
|
r5 = r5 ^ ds[r0 & mask]; // 13 load
|
|
r2 = __umulhi(r2, r1); // 14 mulhi
|
|
r7 = r7 + r4 + ((((sel >> 17u) & 1u) != 0u) ? 0x4ab35569u : 0xa105b846u); // 15 add
|
|
r1 = r1 ^ ds[r6 & mask]; // 16 load
|
|
r7 = r7 ^ ds[r1 & mask]; // 17 load
|
|
r2 = rotr_var(r2, r0); // 18 rotr
|
|
r7 = r7 ^ r3; // 19 xor
|
|
r1 = __umulhi(r1, r6); // 20 mulhi
|
|
r3 = r3 ^ ds[r5 & mask]; // 21 load
|
|
r6 = r6 ^ r3; // 22 xor
|
|
r0 = r0 * r3; // 23 mul
|
|
r4 = r4 + r0 + ((((sel >> 2u) & 1u) != 0u) ? 0x344a9ec0u : 0x3706948au); // 24 add
|
|
r7 = rotl_imm(r7, 24u); // 25 rotl
|
|
r3 = r3 ^ r4; // 26 xor
|
|
r2 = r2 ^ r0; // 27 xor
|
|
r0 = r0 ^ r7; // 28 xor
|
|
r3 = r3 - r1; // 29 sub
|
|
r5 = r5 ^ ds[r7 & mask]; // 30 load
|
|
r0 = r0 ^ ds[r2 & mask]; // 31 load
|
|
r0 = r1 * r7 + r0; // 32 mad
|
|
r5 = r5 ^ ds[r4 & mask]; // 33 load
|
|
r6 = r6 ^ r0; // 34 xor
|
|
r1 = r1 ^ ds[r0 & mask]; // 35 load
|
|
r1 = r0 * r2 + r1; // 36 mad
|
|
r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r4, 8); // 37 shfl
|
|
r6 = rotl_imm(r6, 21u); // 38 rotl
|
|
r5 = r5 ^ r4; // 39 xor
|
|
r5 = r1 * r1 + r5; // 40 mad
|
|
r3 = r3 + r4 + ((((sel >> 17u) & 1u) != 0u) ? 0x7116ab79u : 0x2f1426d3u); // 41 add
|
|
r7 = r7 - r1; // 42 sub
|
|
r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r4, 2); // 43 shfl
|
|
r6 = rotr_var(r6, r3); // 44 rotr
|
|
r1 = rotl_imm(r1, 18u); // 45 rotl
|
|
r1 = r1 ^ ds[r3 & mask]; // 46 load
|
|
r5 = r5 ^ __shfl_xor_sync(0xffffffffu, r3, 8); // 47 shfl
|
|
r0 = r0 ^ ds[r7 & mask]; // 48 load
|
|
r2 = rotl_imm(r2, 27u); // 49 rotl
|
|
r1 = r1 ^ r7; // 50 xor
|
|
r6 = r6 + r2 + ((((sel >> 16u) & 1u) != 0u) ? 0xbc8977b1u : 0xaa5a26d7u); // 51 add
|
|
r0 = r0 ^ r5; // 52 xor
|
|
r1 = rotr_var(r1, r5); // 53 rotr
|
|
r6 = rotl_imm(r6, 24u); // 54 rotl
|
|
r1 = r1 ^ r5; // 55 xor
|
|
r6 = r6 ^ r7; // 56 xor
|
|
r5 = r5 ^ r7; // 57 xor
|
|
r2 = r2 ^ r5; // 58 xor
|
|
r0 = r4 * r0 + r0; // 59 mad
|
|
r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r6, 8); // 60 shfl
|
|
r6 = r6 ^ ds[r5 & mask]; // 61 load
|
|
r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r3, 2); // 62 shfl
|
|
r5 = r5 | r2; // 63 or
|
|
}
|
|
uint32_t lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u);
|
|
uint32_t hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u);
|
|
out[gid] = ((uint64_t)hi << 32) | (uint64_t)lo;
|
|
}
|
|
|
|
// Host-side launch wrappers. Declared in program.h, called from host.cu.
|
|
cudaError_t igneum_launch_fill(uint32_t* ds, uint32_t nWords, uint32_t d0, uint32_t d1) {
|
|
if (nWords == 0u) return cudaErrorInvalidValue;
|
|
uint32_t block = 256u;
|
|
uint32_t grid = (nWords + block - 1u) / block;
|
|
igneum_fill<<<grid, block>>>(ds, nWords, d0, d1);
|
|
return cudaGetLastError();
|
|
}
|
|
|
|
cudaError_t igneum_launch_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask,
|
|
uint32_t nonces, uint32_t blockWarps) {
|
|
if (blockWarps == 0u || blockWarps > 32u) return cudaErrorInvalidValue;
|
|
uint32_t block = 32u * blockWarps;
|
|
if (nonces == 0u || (nonces % block) != 0u) return cudaErrorInvalidValue;
|
|
igneum_hash<<<nonces / block, block>>>(ds, out, baseNonce, mask);
|
|
return cudaGetLastError();
|
|
}
|
|
|
|
cudaError_t igneum_hash_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps) {
|
|
cudaFuncAttributes attr;
|
|
cudaError_t e = cudaFuncGetAttributes(&attr, igneum_hash);
|
|
if (e != cudaSuccess) return e;
|
|
*numRegs = attr.numRegs;
|
|
return cudaOccupancyMaxActiveBlocksPerMultiprocessor(blocksPerSM, igneum_hash, (int)(32u * blockWarps), 0);
|
|
}
|