read-width experiment (gate 1): load classes W=4/16/64, per-load width mix, scratch RMW variant behind a generator flag; 20 packs; CPU verifier and acceptance mirror; emulator shims

Nothing changes for the default class: the pinned packs are byte-identical (tests/packs.rs), the v2 draw stream is untouched.
LoadClass {mix, load_slots, scratch}: fixed widths w16, w64, w64x4 (4 loads of 64 B), era mixes 50/35/15 and 25/50/25 drawn per load with one extra below(100) roll, and the scratch variant scr0/2/4/8 (persistent warps, 1 MiB per warp, tagged lazy fill, measurement only). A wide load reads the W-aligned address and folds every word: x = dst ^ w0; x = (rotl(x, 11) * 0x9e3779b1) ^ w[j]. Program ids carry the class. proto-opencl/host.c taken from opencl-rdna4 23810df (--memprobe, select read-back).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
igneum-labs 2026-10-05 19:46:54 +00:00
parent 5aa75166d8
commit dc84789e34
253 changed files with 35043 additions and 95 deletions

View file

@ -14,9 +14,9 @@
//! costs about a millisecond on one core. The census (section 7.3) checked on 100,000 programs that the
//! closed-form verdict agrees with the memory-hard one on all but 39 threshold-edge cases.
use crate::generator::{Instr, Op, Program, INSTR_COUNT, ITERATIONS, LANES};
use crate::generator::{Instr, Op, Program, INSTR_COUNT, ITERATIONS, LANES, SCRATCH_SLOT_MASK};
use crate::seed::{fnv1a64, SplitMix64};
use crate::verify::{dataset_elem, splitmix32};
use crate::verify::{dataset_elem, fold_words, splitmix32, ScratchModel};
/// Units (32-lane warps) the dynamic test interprets.
pub const ACCEPT_UNITS: usize = 64;
@ -33,6 +33,12 @@ pub const BIAS_TOLERANCE: u32 = 136;
/// Distinct addresses per lane per evaluation, summed over 2,048 evaluations, must exceed this (mean above 120).
pub const MIN_DISTINCT_SUM: u64 = 245_760;
/// The distinct-address bound for a program with `loads` loads per hash: the same 120 of 128 ratio, so
/// [`MIN_DISTINCT_SUM`] for the lottery hash and `loads x 1,920` for the read-width classes with other counts.
pub fn min_distinct_sum(loads: usize) -> u64 {
loads as u64 * ACCEPT_HASHES as u64 * 120 / 128
}
/// Why a candidate was rejected. The verdict (accept or reject) is what consensus depends on; the reason is the
/// first failing test in the order of the module table.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
@ -67,7 +73,7 @@ impl std::fmt::Display for Reject {
Reject::Saturated { count } => write!(f, "(c) {count} of 16384 final register values saturated (limit 163)"),
Reject::OutputBias { bit, ones } => write!(f, "(c) output bit {bit} set in {ones} of 2048 hashes"),
Reject::DistinctAddresses { sum } => {
write!(f, "(c) distinct addresses {sum} over 2048 hashes (mean {:.2}, needs above 120)", *sum as f64 / 2048.0)
write!(f, "(c) distinct addresses {sum} over 2048 hashes (mean {:.2}, needs above 120 of 128 loads)", *sum as f64 / 2048.0)
}
}
}
@ -183,12 +189,28 @@ fn run_unit(p: &Program, unit: usize, base: u32, acc: &mut Acc, lane_addrs: &mut
}
let mut idx = [0u32; LANES];
let mut nload = 0usize;
let mut scratch = if p.has_scratch() { Some(ScratchModel::new()) } else { None };
for it in 0..ITERATIONS {
let sel = r[0];
for (k, ins) in p.instrs.iter().enumerate() {
let d = ins.dst as usize;
let a = ins.src as usize;
match ins.op {
Op::Scratch => {
// Variant 5: the slot stands in for the address (bit 31 set so it never aliases a dataset word).
let m = scratch.as_mut().expect("a scratch op needs a scratch class");
for lane in 0..LANES {
idx[lane] = r[a][lane] & SCRATCH_SLOT_MASK;
}
if idx.iter().all(|&x| x == idx[0]) {
return Err(Reject::LaneConstantSite { iteration: it as u8, instr: k as u8, unit: unit as u8 });
}
for lane in 0..LANES {
r[d][lane] = m.rmw(&p.seed, base, lane, idx[lane], r[d][lane]);
lane_addrs[lane * loads + nload] = 0x8000_0000 | idx[lane];
}
nload += 1;
}
Op::Add => {
let (imm, imm2, bit) = (ins.imm, ins.imm2, ins.bit as u32);
let src = r[a];
@ -255,14 +277,26 @@ fn run_unit(p: &Program, unit: usize, base: u32, acc: &mut Acc, lane_addrs: &mut
}
}
Op::Load => {
// Read-width experiment: a load of `width` words reads from the aligned address and folds every
// word (verify::fold_words); width 1 is the lottery hash's xor of one word.
let width = ins.width as usize;
let align = !(ins.width as u32 - 1);
for lane in 0..LANES {
idx[lane] = r[a][lane] & mask;
idx[lane] = (r[a][lane] & mask) & align;
}
if idx.iter().all(|&x| x == idx[0]) {
return Err(Reject::LaneConstantSite { iteration: it as u8, instr: k as u8, unit: unit as u8 });
}
for lane in 0..LANES {
r[d][lane] ^= dataset_elem(idx[lane], d0, d1);
if width == 1 {
r[d][lane] ^= dataset_elem(idx[lane], d0, d1);
} else {
let mut w = [0u32; 16];
for j in 0..width {
w[j] = dataset_elem(idx[lane] + j as u32, d0, d1);
}
r[d][lane] = fold_words(r[d][lane], &w[..width]);
}
lane_addrs[lane * loads + nload] = idx[lane];
}
nload += 1;
@ -333,7 +367,7 @@ pub fn check_dynamic(p: &Program) -> Result<AcceptReport, Reject> {
}
bias_max = bias_max.max(d);
}
if acc.distinct_sum <= MIN_DISTINCT_SUM {
if acc.distinct_sum <= min_distinct_sum(loads) {
return Err(Reject::DistinctAddresses { sum: acc.distinct_sum });
}
Ok(AcceptReport { distinct_sum: acc.distinct_sum, saturated: acc.saturated, bias_max })
@ -348,9 +382,50 @@ pub fn check(p: &Program) -> Result<AcceptReport, Reject> {
#[cfg(test)]
mod tests {
use super::*;
use crate::generator::{candidate, generate, GeneratorConfig, generate_v1};
use crate::generator::{candidate, candidate_class, generate, generate_class, GeneratorConfig, generate_v1, LoadClass};
use crate::verify::{DatasetMode, DatasetSource};
#[test]
fn distinct_bound_scales_with_the_load_count() {
assert_eq!(min_distinct_sum(128), MIN_DISTINCT_SUM);
assert_eq!(min_distinct_sum(32), 61_440);
}
/// The read-width classes pass the rule at about the version 2 rate, and the instrumented interpreter agrees
/// with `verify.rs` on every class (the fold is shared, the addresses are aligned the same way).
#[test]
fn classes_pass_and_match_verify() {
for name in ["w16", "w64", "w64x4", "50,35,15", "25,50,25", "scr2", "scr8"] {
let c = LoadClass::parse(name).unwrap();
let p = generate_class("igneum-genesis", c);
assert!(check(&p).is_ok(), "{name}");
let mut rejected = 0;
for i in 0..60u32 {
let s = format!("igneum-rw-accept/{i}");
let q = candidate_class(&s, s.as_bytes(), 0, c);
if check(&q).is_err() {
rejected += 1;
}
}
assert!(rejected < 15, "{name}: {rejected} of 60 rejected");
let ds = DatasetSource::from_key(p.seed, DatasetMode::ClosedForm, ACCEPT_DATASET_LOG2);
let bases = accept_base_nonces(&p.seed);
let loads = p.loads_per_hash();
let mut acc = Acc { and_acc: [u32::MAX; 8], or_acc: [0; 8], saturated: 0, bit_ones: [0; 64], distinct_sum: 0 };
let mut la = vec![0u32; LANES * loads];
let mut ones = [0u32; 64];
for (u, &b) in bases.iter().enumerate() {
run_unit(&p, u, b, &mut acc, &mut la).unwrap();
for h in crate::verify::hash_warp(&p, b, &ds) {
for j in 0..64 {
ones[j] += ((h >> j) & 1) as u32;
}
}
}
assert_eq!(acc.bit_ones, ones, "{name}: bit counts match the reference interpreter");
}
}
/// The instrumented interpreter agrees with `verify.rs` on the closed-form dataset keyed by the seed words.
#[test]
fn instrumented_interpreter_matches_verify() {

View file

@ -9,13 +9,207 @@
//! One deliberate difference from the Swift: `program_json` writes the cache line mask inside the `"item"` string
//! as a bare `0x003fffff`. The Swift writes it quoted (`jhex`), which is not valid JSON.
use crate::generator::{Op, Program, GENERATOR_VERSION, INSTR_COUNT, ITERATIONS, LOAD_SLOTS};
use crate::generator::{Instr, Op, Program, GENERATOR_VERSION, INSTR_COUNT, ITERATIONS, LOAD_SLOTS};
use crate::memhard::{
MixParams, CACHE_LINES_PER_SEGMENT, CACHE_LINE_MASK, CACHE_LOG2_WORDS, CACHE_SEGMENTS, CACHE_SEGMENT_LOG2_LINES,
CACHE_TAG, CACHE_WORDS, CHACHA_ROUNDS, CHACHA_SIGMA, ITEM_ROUNDS,
};
use crate::seed::SplitMix64;
use crate::verify::{DatasetMode, DatasetSource, Epoch};
use crate::generator::{SCRATCH_BYTES_PER_WARP, SCRATCH_SLOTS, SCRATCH_SLOT_MASK, SCRATCH_WORDS_PER_LANE};
use crate::verify::{DatasetMode, DatasetSource, Epoch, FOLD_MUL, FOLD_ROT};
/// Where the words of a wide load come from (read-width experiment).
#[derive(Clone, Copy, PartialEq, Eq)]
enum WideSource {
/// `dataset`/`ds`: vector loads from the stored dataset.
Stored,
/// the closed form per word (Metal inline shortcut kernel).
InlineClosed,
/// one `mh_item` derivation per load, words taken from it (Metal inline memory-hard kernel).
InlineMemhard,
}
/// One wide `load` as a single statement block (read-width experiment, 5 October 2026): `width` words from the
/// address aligned down to `width` words, folded into `dst` as `verify::fold_words`. The emitted text is the
/// same shape in the three dialects: the vector loads differ (`uint4` pointer on Metal and CUDA, `vload4` on
/// OpenCL C 1.2). For `width == 1` the caller emits the lottery hash's one-word form instead.
fn wide_load_stmt(dialect: CoreDialect, d: &str, a: &str, width: u8, src: WideSource, closed: Option<(u32, u32)>) -> String {
debug_assert!(width == 4 || width == 16);
let (u, mask, base_ptr) = match dialect {
CoreDialect::Metal => ("uint", "MASK", "dataset"),
CoreDialect::Cuda => ("uint32_t", "mask", "ds"),
CoreDialect::OpenCl => ("uint", "mask", "ds"),
};
let vectors = width as usize / 4;
let mut s = String::with_capacity(400);
s.push_str(&format!("{{ {u} b_ = ({a} & {mask}) & ~{}u; ", width as u32 - 1));
match src {
WideSource::Stored => match dialect {
CoreDialect::Metal => s.push_str(&format!("device const uint4* l_ = (device const uint4*)({base_ptr} + b_); ")),
CoreDialect::Cuda => s.push_str(&format!("const uint4* l_ = (const uint4*)({base_ptr} + b_); ")),
CoreDialect::OpenCl => {}
},
WideSource::InlineClosed => {}
WideSource::InlineMemhard => s.push_str("uint s_[16]; mh_item(cache, b_ >> 4u, s_); "),
}
let word = |j: usize| -> String {
match src {
WideSource::Stored => format!("v{}_.{}", j / 4, ["x", "y", "z", "w"][j % 4]),
WideSource::InlineClosed => {
let (d0, d1) = closed.expect("closed-form words need d0, d1");
format!("ds_elem(b_ + {j}u, {}, {})", hex(d0), hex(d1))
}
WideSource::InlineMemhard => format!("s_[(b_ & 15u) + {j}u]"),
}
};
if src == WideSource::Stored {
for v in 0..vectors {
match dialect {
CoreDialect::OpenCl => s.push_str(&format!("uint4 v{v}_ = vload4({v}u, {base_ptr} + b_); ")),
_ => s.push_str(&format!("uint4 v{v}_ = l_[{v}]; ")),
}
}
}
s.push_str(&format!("{u} x_ = {d} ^ {}; ", word(0)));
for j in 1..width as usize {
s.push_str(&format!("x_ = (rotl_imm(x_, {FOLD_ROT}u) * {}) ^ {}; ", hex(FOLD_MUL), word(j)));
}
s.push_str(&format!("{d} = x_; }}"));
s
}
/// The load class lines of program.h (empty for the lottery hash, so the pinned packs do not change).
fn class_header_lines(p: &Program) -> String {
if p.class.is_v2() {
return String::new();
}
let mut s = String::new();
s.push_str("// Read-width experiment (5 October 2026, docs/plans/read-width.md): NOT the lottery hash. A load of W words reads
");
s.push_str("// the W-word-aligned address and folds every word into dst: x = dst ^ w[0]; x = (rotl(x, 11) * 0x9e3779b1) ^ w[j]; dst = x.
");
s.push_str(&format!("#define IGNEUM_LOAD_CLASS {}
", jstr(&p.class.name())));
s.push_str(&format!("#define IGNEUM_LOAD_SLOTS {}
", p.class.load_slots));
s.push_str(&format!("#define IGNEUM_LOAD_MIX {{ {}, {}, {} }}
", p.class.mix[0], p.class.mix[1], p.class.mix[2]));
let c = p.width_counts();
s.push_str(&format!("#define IGNEUM_LOAD_WIDTH_COUNTS {{ {}, {}, {} }} // loads of 4, 16, 64 bytes per program
", c[0], c[1], c[2]));
s.push_str(&format!("#define IGNEUM_BYTES_PER_HASH {}
", p.bytes_per_hash()));
s.push_str(&format!("#define IGNEUM_FOLD_ROT {FOLD_ROT}
"));
s.push_str(&format!("#define IGNEUM_FOLD_MUL {}
", hex(FOLD_MUL)));
s
}
/// The width of a load instruction's statement, for the emitters (1 for every non-load op).
fn load_width(ins: &Instr) -> u8 {
if ins.op == Op::Load {
ins.width
} else {
1
}
}
/// Variant 5 prelude: `scr_fill(gbase, lane, slot, j)`, the fill word of a scratch slot (`verify::scratch_fill`),
/// with the program's seed words as literals.
fn scratch_prelude(p: &Program, dialect: CoreDialect) -> String {
if !p.has_scratch() {
return String::new();
}
let (u, fn_) = match dialect {
CoreDialect::Metal => ("uint", "inline"),
CoreDialect::Cuda => ("uint32_t", "__device__ __forceinline__"),
CoreDialect::OpenCl => ("uint", "static inline"),
};
let mut s = String::new();
s.push_str(&format!("// Variant 5 (read-width experiment, 5 October 2026, NOT the lottery hash): a 1 MiB scratch per warp, {} slots of
", SCRATCH_SLOTS));
s.push_str("// 16 bytes per lane, lane-major. A slot starts the unit as the fill words below (tagged lazily: a slot whose tag is not
");
s.push_str("// this unit's reads as its fill) and holds what the unit wrote afterwards. scr_fill mirrors verify::scratch_fill.
");
s.push_str(&format!(
"{fn_} {u} scr_fill({u} gbase, {u} lane, {u} slot, {u} j) {{ {u} sw = (j == 0u) ? {} : ((j == 1u) ? {} : {}); return splitmix32(((gbase + lane) ^ sw) + slot * 0x9e3779b1u + (j + 1u) * 0x85ebca77u); }}
",
hex(p.seed[0]),
hex(p.seed[1]),
hex(p.seed[2])
));
s
}
/// Variant 5: one scratch read-modify-write as a statement block. `arena`, `tag`, `gbase` and `lane` are in scope
/// (the persistent prologue). Reads 16 bytes, folds the three data words into dst, rewrites the slot behind the tag.
fn scratch_stmt(dialect: CoreDialect, d: &str, a: &str) -> String {
let (u, load, store) = match dialect {
CoreDialect::Metal => ("uint", "uint4 v_ = *(device const uint4*)(arena + s_ * 4u);", "*(device uint4*)(arena + s_ * 4u) = uint4(tag, x_ ^ w1_, rotl_imm(x_, 7u) ^ w2_, x_ + w0_);"),
CoreDialect::Cuda => ("uint32_t", "uint4 v_ = *(const uint4*)(arena + s_ * 4u);", "*(uint4*)(arena + s_ * 4u) = make_uint4(tag, x_ ^ w1_, rotl_imm(x_, 7u) ^ w2_, x_ + w0_);"),
CoreDialect::OpenCl => ("uint", "uint4 v_ = vload4(s_, arena);", "vstore4(IGNEUM_U4(tag, x_ ^ w1_, rotl_imm(x_, 7u) ^ w2_, x_ + w0_), s_, arena);"),
};
format!(
"{{ {u} s_ = {a} & {}u; {load} {u} m_ = (v_.x == tag) ? 0xffffffffu : 0u; {u} w0_ = (v_.y & m_) | (scr_fill(gbase, lane, s_, 0u) & ~m_); {u} w1_ = (v_.z & m_) | (scr_fill(gbase, lane, s_, 1u) & ~m_); {u} w2_ = (v_.w & m_) | (scr_fill(gbase, lane, s_, 2u) & ~m_); {u} x_ = {d} ^ w0_; x_ = (rotl_imm(x_, {FOLD_ROT}u) * {k}) ^ w1_; x_ = (rotl_imm(x_, {FOLD_ROT}u) * {k}) ^ w2_; {d} = x_; {store} }}",
SCRATCH_SLOT_MASK,
k = hex(FOLD_MUL)
)
}
/// Variant 5: the persistent-warp prologue. The kernel is launched with N warps (the resident count, the host's
/// choice); warp `w` owns arena `w` and runs the units `w, w + N, w + 2N, ...` of the launch. Inside the loop the
/// lottery hash's text is unchanged: `gid` is the unit's first output index plus the lane. The host MUST launch
/// `groups` as a multiple of N (a uniform trip count: the OpenCL local-memory exchange carries a barrier).
fn persistent_prologue(dialect: CoreDialect) -> String {
let (u, tid, nthreads, ptr) = match dialect {
CoreDialect::Metal => ("uint", "tid", "nthreads", "device uint*"),
CoreDialect::Cuda => ("uint32_t", "(blockIdx.x * blockDim.x + threadIdx.x)", "(gridDim.x * blockDim.x)", "uint32_t*"),
CoreDialect::OpenCl => ("uint", "(uint)get_global_id(0)", "(uint)get_global_size(0)", "__global uint*"),
};
let mut s = String::new();
s.push_str(&format!(" {u} lane = {tid} & 31u;
"));
s.push_str(&format!(" {u} warp_ = {tid} >> 5;
"));
s.push_str(&format!(" {u} nwarps_ = {nthreads} >> 5;
"));
s.push_str(&format!(" {ptr} arena = scratch + ((size_t)warp_ * 32u + lane) * {}u;
", SCRATCH_WORDS_PER_LANE));
s.push_str(&format!(" for ({u} g_ = warp_; g_ < groups; g_ += nwarps_) {{
"));
s.push_str(&format!(" {u} gid = g_ * 32u + lane;
"));
s.push_str(&format!(" {u} gbase = baseNonce + g_ * 32u;
"));
s.push_str(&format!(" {u} tag = salt + g_;
"));
s
}
/// The scratch lines of program.h (variant 5).
fn scratch_header_lines(p: &Program) -> String {
if !p.has_scratch() {
return String::new();
}
let mut s = String::new();
s.push_str("// Variant 5: persistent warps, a 1 MiB scratch per launched warp (the host launches N warps and passes scratch,
");
s.push_str("// groups and salt as the last three kernel arguments; groups must be a multiple of N; the tag of a unit is salt + unit).
");
s.push_str("#define IGNEUM_PERSISTENT_WARPS 1
");
s.push_str(&format!("#define IGNEUM_SCRATCH_OPS {} // scratch read-modify-writes per program ({} per hash)
", p.class.scratch_slots(), p.scratch_ops_per_hash()));
s.push_str(&format!("#define IGNEUM_SCRATCH_SLOTS {SCRATCH_SLOTS}u
"));
s.push_str(&format!("#define IGNEUM_SCRATCH_WORDS_PER_LANE {SCRATCH_WORDS_PER_LANE}u
"));
s.push_str(&format!("#define IGNEUM_SCRATCH_BYTES_PER_WARP {SCRATCH_BYTES_PER_WARP}u
"));
s
}
pub fn hex(v: u32) -> String {
format!("0x{v:08x}u")
@ -259,6 +453,7 @@ fn metal_program_impl(p: &Program, dataset_log2: u32, source: LoadSource, bound:
s.push('\n');
buffer0 = "device const uint* cache [[buffer(0)]]";
}
s.push_str(&scratch_prelude(p, CoreDialect::Metal));
if bound {
s.push_str("// Header-bound variant: the init words come from buffer 3 (bind.rs), not from SEEDW.\n");
s.push_str(&format!("kernel void igneum_hash_bound({buffer0},\n"));
@ -270,7 +465,17 @@ fn metal_program_impl(p: &Program, dataset_log2: u32, source: LoadSource, bound:
if bound {
s.push_str(" constant uint* initw [[buffer(3)]],\n");
}
s.push_str(" uint gid [[thread_position_in_grid]]) {\n");
if p.has_scratch() {
let b = if bound { 4 } else { 3 };
s.push_str(&format!(" device uint* scratch [[buffer({b})]],\n"));
s.push_str(&format!(" constant uint& groups [[buffer({})]],\n", b + 1));
s.push_str(&format!(" constant uint& salt [[buffer({})]],\n", b + 2));
s.push_str(" uint tid [[thread_position_in_grid]],\n");
s.push_str(" uint nthreads [[threads_per_grid]]) {\n");
s.push_str(&persistent_prologue(CoreDialect::Metal));
} else {
s.push_str(" uint gid [[thread_position_in_grid]]) {\n");
}
s.push_str(" uint nonce = baseNonce + gid;\n");
s.push_str(" uint r0, r1, r2, r3, r4, r5, r6, r7;\n");
if p.has_wide() {
@ -319,8 +524,17 @@ fn metal_program_impl(p: &Program, dataset_log2: u32, source: LoadSource, bound:
Op::Rotr => format!("{d} = rotr_var({d}, {a});"),
Op::Mad => format!("{d} = {a} * {b} + {d};"),
Op::Shfl => format!("{d} = {d} ^ simd_shuffle_xor({a}, (ushort){});", ins.mask),
Op::Load if load_width(ins) > 1 => {
let (src, closed) = match &source {
LoadSource::Stored => (WideSource::Stored, None),
LoadSource::InlineClosed(d0, d1) => (WideSource::InlineClosed, Some((*d0, *d1))),
LoadSource::InlineMemhard(_) => (WideSource::InlineMemhard, None),
};
wide_load_stmt(CoreDialect::Metal, &d, &a, ins.width, src, closed)
}
Op::Load => format!("{d} = {d} ^ {};", fetch(word_index(&a, false))),
Op::WLoad => format!("{d} = {d} ^ {};", fetch(word_index(&a, true))),
Op::Scratch => scratch_stmt(CoreDialect::Metal, &d, &a),
};
s.push_str(&format!(" {line} // {k}\n"));
}
@ -328,6 +542,9 @@ fn metal_program_impl(p: &Program, dataset_log2: u32, source: LoadSource, bound:
s.push_str(" uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u);\n");
s.push_str(" uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u);\n");
s.push_str(" out[gid] = ((ulong)hi << 32) | (ulong)lo;\n");
if p.has_scratch() {
s.push_str(" }\n");
}
s.push_str("}\n");
s
}
@ -379,8 +596,10 @@ fn cuda_instr_lines(p: &Program) -> String {
Op::Rotr => format!("{d} = rotr_var({d}, {a});"),
Op::Mad => format!("{d} = {a} * {b} + {d};"),
Op::Shfl => format!("{d} = {d} ^ __shfl_xor_sync(0xffffffffu, {a}, {});", ins.mask),
Op::Load if load_width(ins) > 1 => wide_load_stmt(CoreDialect::Cuda, &d, &a, ins.width, WideSource::Stored, None),
Op::Load => format!("{d} = {d} ^ ds[{a} & mask];"),
Op::WLoad => format!("{d} = {d} ^ ds[(__shfl_sync(0xffffffffu, {a}, 0) & wmask) + lane];"),
Op::Scratch => scratch_stmt(CoreDialect::Cuda, &d, &a),
};
s.push_str(&format!(" {line} // {k} {}\n", ins.op.name()));
}
@ -448,8 +667,14 @@ pub fn cuda_kernel(p: &Program, memhard: Option<&MixParams>) -> String {
s.push_str("// One hash per thread. blockDim.x is a multiple of 32; lane = threadIdx.x & 31 and every\n");
s.push_str("// __shfl_xor_sync stays inside the lane's own warp, exactly like simd_shuffle_xor inside a\n");
s.push_str("// 32-wide Metal SIMD group. Control flow is uniform, so the full 0xffffffff member mask is valid.\n");
s.push_str("__global__ void igneum_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask) {\n");
s.push_str(" uint32_t gid = blockIdx.x * blockDim.x + threadIdx.x;\n");
s.push_str(&scratch_prelude(p, CoreDialect::Cuda));
let scratch_args = if p.has_scratch() { ", uint32_t* scratch, uint32_t groups, uint32_t salt" } else { "" };
s.push_str(&format!("__global__ void igneum_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask{scratch_args}) {{\n"));
if p.has_scratch() {
s.push_str(&persistent_prologue(CoreDialect::Cuda));
} else {
s.push_str(" uint32_t gid = blockIdx.x * blockDim.x + threadIdx.x;\n");
}
s.push_str(" uint32_t nonce = baseNonce + gid;\n");
s.push_str(" uint32_t r0, r1, r2, r3, r4, r5, r6, r7;\n");
if p.has_wide() {
@ -464,6 +689,9 @@ pub fn cuda_kernel(p: &Program, memhard: Option<&MixParams>) -> String {
s.push_str(" uint32_t lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u);\n");
s.push_str(" uint32_t hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u);\n");
s.push_str(" out[gid] = ((uint64_t)hi << 32) | (uint64_t)lo;\n");
if p.has_scratch() {
s.push_str(" }\n");
}
s.push_str("}\n");
s.push('\n');
s.push_str("// Host-side launch wrappers. Declared in program.h, called from host.cu.\n");
@ -494,16 +722,28 @@ pub fn cuda_kernel(p: &Program, memhard: Option<&MixParams>) -> String {
s.push_str("}\n");
s.push('\n');
}
s.push_str(
"cudaError_t igneum_launch_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask,\n",
);
s.push_str(" uint32_t nonces, uint32_t blockWarps) {\n");
s.push_str(" if (blockWarps == 0u || blockWarps > 32u) return cudaErrorInvalidValue;\n");
s.push_str(" uint32_t block = 32u * blockWarps;\n");
s.push_str(" if (nonces == 0u || (nonces % block) != 0u) return cudaErrorInvalidValue;\n");
s.push_str(" igneum_hash<<<nonces / block, block>>>(ds, out, baseNonce, mask);\n");
s.push_str(" return cudaGetLastError();\n");
s.push_str("}\n");
if p.has_scratch() {
s.push_str("// Variant 5: the wrapper launches `warps` persistent warps over `nonces / 32` units (host.cu does not use it).\n");
s.push_str("cudaError_t igneum_launch_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask,\n");
s.push_str(" uint32_t nonces, uint32_t blockWarps, uint32_t* scratch, uint32_t warps, uint32_t salt) {\n");
s.push_str(" if (blockWarps == 0u || blockWarps > 32u || warps == 0u || (warps % blockWarps) != 0u) return cudaErrorInvalidValue;\n");
s.push_str(" uint32_t block = 32u * blockWarps;\n");
s.push_str(" if (nonces == 0u || (nonces % (32u * warps)) != 0u) return cudaErrorInvalidValue;\n");
s.push_str(" igneum_hash<<<warps / blockWarps, block>>>(ds, out, baseNonce, mask, scratch, nonces / 32u, salt);\n");
s.push_str(" return cudaGetLastError();\n");
s.push_str("}\n");
} else {
s.push_str(
"cudaError_t igneum_launch_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask,\n",
);
s.push_str(" uint32_t nonces, uint32_t blockWarps) {\n");
s.push_str(" if (blockWarps == 0u || blockWarps > 32u) return cudaErrorInvalidValue;\n");
s.push_str(" uint32_t block = 32u * blockWarps;\n");
s.push_str(" if (nonces == 0u || (nonces % block) != 0u) return cudaErrorInvalidValue;\n");
s.push_str(" igneum_hash<<<nonces / block, block>>>(ds, out, baseNonce, mask);\n");
s.push_str(" return cudaGetLastError();\n");
s.push_str("}\n");
}
s.push('\n');
s.push_str("cudaError_t igneum_hash_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps) {\n");
s.push_str(" cudaFuncAttributes attr;\n");
@ -548,8 +788,14 @@ pub fn cuda_kernel_bound(p: &Program, memhard: Option<&MixParams>) -> String {
s.push_str("__device__ __forceinline__ uint32_t rotr_var(uint32_t x, uint32_t n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); }\n");
s.push('\n');
let _ = memhard; // the bound kernel reads the stored dataset in both constructions
s.push_str("__global__ void igneum_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, IgneumInitWords iw) {\n");
s.push_str(" uint32_t gid = blockIdx.x * blockDim.x + threadIdx.x;\n");
s.push_str(&scratch_prelude(p, CoreDialect::Cuda));
let scratch_args = if p.has_scratch() { ", uint32_t* scratch, uint32_t groups, uint32_t salt" } else { "" };
s.push_str(&format!("__global__ void igneum_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, IgneumInitWords iw{scratch_args}) {{\n"));
if p.has_scratch() {
s.push_str(&persistent_prologue(CoreDialect::Cuda));
} else {
s.push_str(" uint32_t gid = blockIdx.x * blockDim.x + threadIdx.x;\n");
}
s.push_str(" uint32_t nonce = baseNonce + gid;\n");
s.push_str(" uint32_t r0, r1, r2, r3, r4, r5, r6, r7;\n");
if p.has_wide() {
@ -568,18 +814,33 @@ pub fn cuda_kernel_bound(p: &Program, memhard: Option<&MixParams>) -> String {
s.push_str(" uint32_t lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u);\n");
s.push_str(" uint32_t hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u);\n");
s.push_str(" out[gid] = ((uint64_t)hi << 32) | (uint64_t)lo;\n");
if p.has_scratch() {
s.push_str(" }\n");
}
s.push_str("}\n");
s.push('\n');
s.push_str(
"cudaError_t igneum_launch_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask,\n",
);
s.push_str(" IgneumInitWords iw, uint32_t nonces, uint32_t blockWarps) {\n");
s.push_str(" if (blockWarps == 0u || blockWarps > 32u) return cudaErrorInvalidValue;\n");
s.push_str(" uint32_t block = 32u * blockWarps;\n");
s.push_str(" if (nonces == 0u || (nonces % block) != 0u) return cudaErrorInvalidValue;\n");
s.push_str(" igneum_hash_bound<<<nonces / block, block>>>(ds, out, baseNonce, mask, iw);\n");
s.push_str(" return cudaGetLastError();\n");
s.push_str("}\n");
if p.has_scratch() {
s.push_str("// Variant 5: the wrapper launches `warps` persistent warps over `nonces / 32` units (host.cu does not use it).\n");
s.push_str("cudaError_t igneum_launch_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask,\n");
s.push_str(" IgneumInitWords iw, uint32_t nonces, uint32_t blockWarps, uint32_t* scratch, uint32_t warps, uint32_t salt) {\n");
s.push_str(" if (blockWarps == 0u || blockWarps > 32u || warps == 0u || (warps % blockWarps) != 0u) return cudaErrorInvalidValue;\n");
s.push_str(" uint32_t block = 32u * blockWarps;\n");
s.push_str(" if (nonces == 0u || (nonces % (32u * warps)) != 0u) return cudaErrorInvalidValue;\n");
s.push_str(" igneum_hash_bound<<<warps / blockWarps, block>>>(ds, out, baseNonce, mask, iw, scratch, nonces / 32u, salt);\n");
s.push_str(" return cudaGetLastError();\n");
s.push_str("}\n");
} else {
s.push_str(
"cudaError_t igneum_launch_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask,\n",
);
s.push_str(" IgneumInitWords iw, uint32_t nonces, uint32_t blockWarps) {\n");
s.push_str(" if (blockWarps == 0u || blockWarps > 32u) return cudaErrorInvalidValue;\n");
s.push_str(" uint32_t block = 32u * blockWarps;\n");
s.push_str(" if (nonces == 0u || (nonces % block) != 0u) return cudaErrorInvalidValue;\n");
s.push_str(" igneum_hash_bound<<<nonces / block, block>>>(ds, out, baseNonce, mask, iw);\n");
s.push_str(" return cudaGetLastError();\n");
s.push_str("}\n");
}
s.push('\n');
s.push_str("cudaError_t igneum_hash_bound_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps) {\n");
s.push_str(" cudaFuncAttributes attr;\n");
@ -614,8 +875,10 @@ fn opencl_instr_lines(p: &Program) -> String {
Op::Rotr => format!("{d} = rotr_var({d}, {a});"),
Op::Mad => format!("{d} = {a} * {b} + {d};"),
Op::Shfl => format!("{{ uint t_; IGNEUM_SHFL_XOR(t_, {a}, {}u); {d} = {d} ^ t_; }}", ins.mask),
Op::Load if load_width(ins) > 1 => wide_load_stmt(CoreDialect::OpenCl, &d, &a, ins.width, WideSource::Stored, None),
Op::Load => format!("{d} = {d} ^ ds[{a} & mask];"),
Op::WLoad => format!("{{ uint t_; IGNEUM_BCAST0(t_, {a}); {d} = {d} ^ ds[(t_ & wmask) + lane]; }}"),
Op::Scratch => scratch_stmt(CoreDialect::OpenCl, &d, &a),
};
s.push_str(&format!(" {line} // {k} {}\n", ins.op.name()));
}
@ -631,8 +894,13 @@ pub fn opencl_kernel_bound(p: &Program, memhard: Option<&MixParams>) -> String {
s.push_str(
"// Header-bound variant (bind.rs): the init words come from initw, not SEEDW. Same body as igneum_hash.\n",
);
s.push_str("IGNEUM_KERNEL_HASH void igneum_hash_bound(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask, __global const uint* initw) {\n");
s.push_str(" uint gid = (uint)get_global_id(0);\n");
let scratch_args = if p.has_scratch() { ", __global uint* scratch, uint groups, uint salt" } else { "" };
s.push_str(&format!("IGNEUM_KERNEL_HASH void igneum_hash_bound(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask, __global const uint* initw{scratch_args}) {{\n"));
if p.has_scratch() {
s.push_str(&persistent_prologue(CoreDialect::OpenCl));
} else {
s.push_str(" uint gid = (uint)get_global_id(0);\n");
}
s.push_str(" uint lid = (uint)get_local_id(0);\n");
s.push_str(" uint nonce = baseNonce + gid;\n");
s.push_str(" uint r0, r1, r2, r3, r4, r5, r6, r7;\n");
@ -659,6 +927,9 @@ pub fn opencl_kernel_bound(p: &Program, memhard: Option<&MixParams>) -> String {
s.push_str(" uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u);\n");
s.push_str(" uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u);\n");
s.push_str(" out[gid] = ((ulong)hi << 32) | (ulong)lo;\n");
if p.has_scratch() {
s.push_str(" }\n");
}
s.push_str("}\n");
s
}
@ -686,6 +957,9 @@ pub fn opencl_kernel(p: &Program, memhard: Option<&MixParams>) -> String {
s.push_str("#ifdef cl_khr_subgroups\n#pragma OPENCL EXTENSION cl_khr_subgroups : enable\n#endif\n");
s.push_str("#ifdef cl_khr_subgroup_shuffle\n#pragma OPENCL EXTENSION cl_khr_subgroup_shuffle : enable\n#endif\n");
s.push_str("#elif IGNEUM_EXCHANGE == 2\n#pragma OPENCL EXTENSION cl_intel_subgroups : enable\n#endif\n");
if p.has_scratch() {
s.push_str("#define IGNEUM_U4(a, b, c, d) ((uint4)((a), (b), (c), (d)))\n");
}
s.push_str("#else\n");
s.push_str("// Not an OpenCL compiler: proto-opencl/emu compiles this file as C++ and supplies the built-ins and these two macros.\n");
s.push_str("#include \"emu_opencl.h\"\n#endif\n");
@ -751,8 +1025,14 @@ pub fn opencl_kernel(p: &Program, memhard: Option<&MixParams>) -> String {
s.push_str("// One hash per work-item. IGNEUM_GROUP is a multiple of 32; lane = lid & 31 and every exchange stays inside the\n");
s.push_str("// lane's own aligned run of 32 work-items, exactly like simd_shuffle_xor inside a 32-wide Metal SIMD group and\n");
s.push_str("// __shfl_xor_sync inside a CUDA warp. Control flow is uniform (no branches at all).\n");
s.push_str("IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask) {\n");
s.push_str(" uint gid = (uint)get_global_id(0);\n");
s.push_str(&scratch_prelude(p, CoreDialect::OpenCl));
let scratch_args = if p.has_scratch() { ", __global uint* scratch, uint groups, uint salt" } else { "" };
s.push_str(&format!("IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask{scratch_args}) {{\n"));
if p.has_scratch() {
s.push_str(&persistent_prologue(CoreDialect::OpenCl));
} else {
s.push_str(" uint gid = (uint)get_global_id(0);\n");
}
s.push_str(" uint lid = (uint)get_local_id(0);\n");
s.push_str(" uint nonce = baseNonce + gid;\n");
s.push_str(" uint r0, r1, r2, r3, r4, r5, r6, r7;\n");
@ -774,6 +1054,9 @@ pub fn opencl_kernel(p: &Program, memhard: Option<&MixParams>) -> String {
s.push_str(" uint lo = r0 ^ rotl_imm(r1, 7u) ^ rotl_imm(r2, 14u) ^ rotl_imm(r3, 21u);\n");
s.push_str(" uint hi = r4 ^ rotl_imm(r5, 9u) ^ rotl_imm(r6, 18u) ^ rotl_imm(r7, 27u);\n");
s.push_str(" out[gid] = ((ulong)hi << 32) | (ulong)lo;\n");
if p.has_scratch() {
s.push_str(" }\n");
}
s.push_str("}\n");
s.push('\n');
s.push_str("#if IGNEUM_EXCHANGE != 0\n");
@ -828,6 +1111,8 @@ pub fn program_header(p: &Program, day: &str, ds: &DatasetSource) -> String {
s.push_str(&format!("#define IGNEUM_LOADS_PER_HASH {}\n", p.loads_per_hash()));
s.push_str(&format!("#define IGNEUM_WIDE_LOADS_PER_HASH {}\n", p.wide_loads_per_hash()));
s.push_str(&format!("#define IGNEUM_OP_MIX {}\n", jstr(&p.op_mix())));
s.push_str(&class_header_lines(p));
s.push_str(&scratch_header_lines(p));
s.push_str("// 0 = closed-form dataset (ds_elem), 1 = memory-hard cache construction (MEMHARD.md, memhard.h)\n");
s.push_str(&format!("#define IGNEUM_DATASET_MODE {}\n", if memhard.is_some() { 1 } else { 0 }));
s.push('\n');
@ -854,10 +1139,15 @@ pub fn program_header(p: &Program, day: &str, ds: &DatasetSource) -> String {
s.push_str("// Defined in kernel.cu. Both launch on the default stream and return cudaGetLastError().\n");
s.push_str("cudaError_t igneum_launch_fill(uint32_t* ds, uint32_t nWords, uint32_t d0, uint32_t d1);\n");
}
s.push_str(
"cudaError_t igneum_launch_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask,\n",
);
s.push_str(" uint32_t nonces, uint32_t blockWarps);\n");
if p.has_scratch() {
s.push_str("cudaError_t igneum_launch_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask,\n");
s.push_str(" uint32_t nonces, uint32_t blockWarps, uint32_t* scratch, uint32_t warps, uint32_t salt);\n");
} else {
s.push_str(
"cudaError_t igneum_launch_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask,\n",
);
s.push_str(" uint32_t nonces, uint32_t blockWarps);\n");
}
s.push_str("cudaError_t igneum_hash_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps);\n");
s.push_str("#endif\n");
s
@ -1004,6 +1294,19 @@ pub fn program_json(p: &Program, day: &str, ds: &DatasetSource) -> String {
s.push_str(&format!(" \"iterations\": {ITERATIONS},\n"));
s.push_str(&format!(" \"instruction_count\": {INSTR_COUNT},\n"));
s.push_str(&format!(" \"loads_per_hash\": {},\n", p.loads_per_hash()));
if !p.class.is_v2() {
let c = p.width_counts();
s.push_str(&format!(" \"load_class\": {},\n", jstr(&p.class.name())));
s.push_str(&format!(" \"load_slots\": {},\n", p.class.load_slots));
s.push_str(&format!(" \"load_mix_percent_4_16_64\": [{}, {}, {}],\n", p.class.mix[0], p.class.mix[1], p.class.mix[2]));
s.push_str(&format!(" \"load_width_counts_4_16_64\": [{}, {}, {}],\n", c[0], c[1], c[2]));
s.push_str(&format!(" \"bytes_per_hash\": {},\n", p.bytes_per_hash()));
if p.has_scratch() {
s.push_str(&format!(" \"scratch_ops_per_hash\": {},\n", p.scratch_ops_per_hash()));
s.push_str(&format!(" \"scratch\": \"variant 5 (measurement only): persistent warps; a 1 MiB scratch per warp of {SCRATCH_SLOTS} 16-byte slots per lane (lane-major); slot = src & 0x{SCRATCH_SLOT_MASK:x}; a slot reads as its fill (scratch_fill(seed words, unit base nonce, lane, slot, j) = splitmix32(((base + lane) ^ seed[j]) + slot * 0x9e3779b1 + (j + 1) * 0x85ebca77), j in 0..2) until the unit writes it; read w0 w1 w2 (behind a per-unit tag on the GPU), x = fold(dst, w0, w1, w2), dst = x, rewrite (x ^ w1, rotl(x, 7) ^ w2, x + w0)\",\n"));
}
s.push_str(&format!(" \"wide_load\": \"read-width experiment (5 October 2026, docs/plans/read-width.md), NOT the lottery hash: a load of W words (width field, 4 or 16) reads dataset[b .. b + W) with b = (src & mask) & ~(W - 1) and folds every word into dst: x = dst ^ w[0]; for j in 1..W: x = (rotl(x, {FOLD_ROT}) * 0x{FOLD_MUL:08x}) ^ w[j]; dst = x; width 1 is the plain load; the width is drawn per instruction from the class mix with one extra below(100) draw after the nine of version 2, and the program id is FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed words || attempt_le32 || mix[3] || load_slots\",\n"));
}
s.push_str(&format!(
" \"op_mix\": {{{}}},\n",
p.histogram().iter().map(|(n, c)| format!("{}: {c}", jstr(n))).collect::<Vec<_>>().join(", ")
@ -1071,6 +1374,23 @@ pub fn program_json(p: &Program, day: &str, ds: &DatasetSource) -> String {
s.push_str(" \"instructions\": [\n");
let n = p.instrs.len();
for (k, ins) in p.instrs.iter().enumerate() {
if !p.class.is_v2() {
s.push_str(&format!(
" {{\"i\": {k}, \"op\": {}, \"dst\": {}, \"src\": {}, \"src2\": {}, \"imm\": {}, \"imm2\": {}, \"rot\": {}, \"bit\": {}, \"mask\": {}, \"width\": {}}}",
jstr(ins.op.name()),
ins.dst,
ins.src,
ins.src2,
jhex(ins.imm),
jhex(ins.imm2),
ins.rot,
ins.bit,
ins.mask,
ins.width
));
s.push_str(if k + 1 < n { ",\n" } else { "\n" });
continue;
}
s.push_str(&format!(
" {{\"i\": {k}, \"op\": {}, \"dst\": {}, \"src\": {}, \"src2\": {}, \"imm\": {}, \"imm2\": {}, \"rot\": {}, \"bit\": {}, \"mask\": {}}}",
jstr(ins.op.name()),

View file

@ -18,6 +18,13 @@
//! The retired version 1 generator (op rolled per instruction with a 25 percent load weight, no acceptance) is
//! kept as [`generate_v1`] for the census tool and the lever measurements of `proto-metal/MEMHARD.md`. Its
//! programs are not the lottery hash and no pack or vector of version 1 is current.
//!
//! Read-width experiment (5 October 2026, gate 1, `docs/plans/read-width.md`; NOT the lottery hash, behind
//! [`LoadClass`]): a program class whose `load` reads `W` bytes (4, 16 or 64: 1, 4 or 16 words, aligned to `W`)
//! and folds every word into `dst` (`verify::fold_words`), with the width fixed per class or drawn per load from
//! an era-fixed mix. The default class [`LoadClass::V2`] is the generator above, draw for draw and byte for byte;
//! every other class takes one extra draw per instruction (the width roll), so its program stream differs from
//! version 2 and its program id carries the class.
use crate::accept::{check, Reject};
use crate::seed::{fnv1a64, program_rng, seed_words_from_bytes};
@ -53,6 +60,9 @@ pub enum Op {
Load,
/// Warp-coalesced load (lever b of the version 1 generator). Never emitted by version 2.
WLoad,
/// Scratch read-modify-write (read-width experiment, variant 5, 5 October 2026): a 16-byte slot of the lane's
/// own 32 KiB of the warp's 1 MiB scratch, read, folded into dst, rewritten. Never emitted by version 2.
Scratch,
}
impl Op {
@ -71,6 +81,7 @@ impl Op {
Op::Shfl => "shfl",
Op::Load => "load",
Op::WLoad => "wload",
Op::Scratch => "scratch",
}
}
@ -88,6 +99,7 @@ impl Op {
"shfl" => Op::Shfl,
"load" => Op::Load,
"wload" => Op::WLoad,
"scratch" => Op::Scratch,
_ => return None,
})
}
@ -95,11 +107,13 @@ impl Op {
/// An injecting op: bijective in `dst` and bringing another register (or the dataset) in. The acceptance
/// rule's part (b) requires one such write per register.
pub fn injects(self) -> bool {
matches!(self, Op::Add | Op::Sub | Op::Xor | Op::Mad | Op::Shfl | Op::Load | Op::WLoad)
matches!(self, Op::Add | Op::Sub | Op::Xor | Op::Mad | Op::Shfl | Op::Load | Op::WLoad | Op::Scratch)
}
/// A memory operation: the fresh-source rule, the acceptance tests and the load count treat the scratch
/// read-modify-write as a load (it is one of the program's 128 memory operations).
pub fn is_load(self) -> bool {
matches!(self, Op::Load | Op::WLoad)
matches!(self, Op::Load | Op::WLoad | Op::Scratch)
}
}
@ -124,6 +138,9 @@ pub struct Instr {
pub bit: u8,
/// Shuffle xor mask: 1, 2, 4, 8 or 16.
pub mask: u8,
/// Words read by a `load`: 1 (the lottery hash, 4 bytes), 4 or 16 (the read-width experiment). 1 on every
/// other op.
pub width: u8,
}
#[derive(Clone, Debug, PartialEq, Eq)]
@ -139,9 +156,144 @@ pub struct Program {
pub generator: u32,
/// Attempt index: 0 for the bare seed, `k` for the k-th re-derivation after rejections.
pub attempt: u32,
/// The load class: [`LoadClass::V2`] for the lottery hash, another for the read-width experiment.
pub class: LoadClass,
pub instrs: Vec<Instr>,
}
/// The widths a `load` may read, in words: 4, 16 and 64 bytes.
pub const WIDTH_WORDS: [u8; 3] = [1, 4, 16];
/// The load class of a program (read-width experiment, 5 October 2026). `mix` holds the percent weights of the
/// three widths of [`WIDTH_WORDS`] (sum 100); `load_slots` the number of `load` instructions per program.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct LoadClass {
pub mix: [u8; 3],
pub load_slots: u8,
/// Variant 5: `Some(k)` gives the program a 1 MiB per-warp scratch (the kernels run persistent warps) and
/// turns `k` of the load slots into scratch read-modify-writes. `None` for every other class.
pub scratch: Option<u8>,
}
/// Scratch geometry (variant 5): 2^11 slots of 16 bytes per lane (32 KiB), 32 lanes per warp (1 MiB), lane-major.
pub const SCRATCH_SLOT_BITS: u32 = 11;
pub const SCRATCH_SLOTS: usize = 1 << SCRATCH_SLOT_BITS;
pub const SCRATCH_SLOT_MASK: u32 = SCRATCH_SLOTS as u32 - 1;
pub const SCRATCH_WORDS_PER_LANE: usize = SCRATCH_SLOTS * 4;
pub const SCRATCH_BYTES_PER_WARP: usize = SCRATCH_WORDS_PER_LANE * 4 * LANES;
impl LoadClass {
/// Generator version 2 as adopted on 4 October 2026: 16 loads of one word. The lottery hash.
pub const V2: LoadClass = LoadClass { mix: [100, 0, 0], load_slots: LOAD_SLOTS as u8, scratch: None };
/// A fixed width (1, 4 or 16 words) with `load_slots` loads per program.
pub fn fixed(width_words: u8, load_slots: u8) -> LoadClass {
let mut mix = [0u8; 3];
let i = WIDTH_WORDS.iter().position(|&w| w == width_words).expect("width must be 1, 4 or 16 words");
mix[i] = 100;
LoadClass { mix, load_slots, scratch: None }
}
/// Per-load width drawn from `mix` (percent for 4, 16, 64 bytes), 16 loads per program.
pub fn mixed(mix: [u8; 3]) -> LoadClass {
assert_eq!(mix.iter().map(|&m| m as u32).sum::<u32>(), 100, "the mix must sum to 100");
LoadClass { mix, load_slots: LOAD_SLOTS as u8, scratch: None }
}
/// Variant 5: version 2 widths, 16 memory operations of which `k` are scratch read-modify-writes.
pub fn scratch(k: u8) -> LoadClass {
assert!(k as usize <= LOAD_SLOTS);
LoadClass { mix: [100, 0, 0], load_slots: LOAD_SLOTS as u8, scratch: Some(k) }
}
/// Parse "p4,p16,p64" or one of the names of [`LoadClass::name`].
pub fn parse(s: &str) -> Option<LoadClass> {
if let Some(k) = s.strip_prefix("scr") {
let k: u8 = k.parse().ok()?;
if k as usize > LOAD_SLOTS {
return None;
}
return Some(LoadClass::scratch(k));
}
let (mix_s, slots) = match s.split_once("x") {
Some((m, n)) if !m.contains(',') => (m, n.parse::<u8>().ok()?),
_ => (s, LOAD_SLOTS as u8),
};
let mix: [u8; 3] = match mix_s {
"v2" => return Some(LoadClass::V2),
"w4" => [100, 0, 0],
"w16" => [0, 100, 0],
"w64" => [0, 0, 100],
m => {
let v: Vec<u8> = m.split(',').map(|x| x.trim().parse::<u8>().ok()).collect::<Option<Vec<_>>>()?;
if v.len() != 3 || v.iter().map(|&x| x as u32).sum::<u32>() != 100 {
return None;
}
[v[0], v[1], v[2]]
}
};
if slots == 0 || slots as usize >= INSTR_COUNT {
return None;
}
Some(LoadClass { mix, load_slots: slots, scratch: None })
}
/// Scratch read-modify-writes per program (0 without a scratch).
pub fn scratch_slots(&self) -> usize {
self.scratch.unwrap_or(0) as usize
}
pub fn is_v2(&self) -> bool {
*self == LoadClass::V2
}
/// "v2", "w4", "w16", "w64", "w64x4", "mix50-35-15", "mix25-50-25x8", "scr4".
pub fn name(&self) -> String {
if self.is_v2() {
return "v2".to_string();
}
if let Some(k) = self.scratch {
return format!("scr{k}");
}
let base = match self.mix {
[100, 0, 0] => "w4".to_string(),
[0, 100, 0] => "w16".to_string(),
[0, 0, 100] => "w64".to_string(),
[a, b, c] => format!("mix{a}-{b}-{c}"),
};
if self.load_slots as usize == LOAD_SLOTS {
base
} else {
format!("{base}x{}", self.load_slots)
}
}
/// The width in words of a load whose width roll (0..99) is `roll`: the first entry of the mix whose cumulative
/// weight exceeds the roll.
pub fn width_for_roll(&self, roll: u64) -> u8 {
let mut acc = 0u64;
for (i, &m) in self.mix.iter().enumerate() {
acc += m as u64;
if roll < acc {
return WIDTH_WORDS[i];
}
}
WIDTH_WORDS[2]
}
/// Expected dataset bytes read per hash: dataset loads per hash times the mean width (scratch traffic apart).
pub fn expected_bytes_per_hash(&self) -> f64 {
let mean = self.mix.iter().zip(WIDTH_WORDS.iter()).map(|(&m, &w)| m as f64 / 100.0 * w as f64 * 4.0).sum::<f64>();
(self.load_slots as usize - self.scratch_slots()) as f64 * ITERATIONS as f64 * mean
}
}
impl Default for LoadClass {
fn default() -> Self {
LoadClass::V2
}
}
impl Program {
pub fn loads_per_hash(&self) -> usize {
self.instrs.iter().filter(|i| i.op.is_load()).count() * ITERATIONS
@ -152,6 +304,27 @@ impl Program {
pub fn has_wide(&self) -> bool {
self.instrs.iter().any(|i| i.op == Op::WLoad)
}
/// Dataset bytes read per hash: 4 per one-word load, 16 and 64 for the wider loads of the experiment.
pub fn bytes_per_hash(&self) -> usize {
self.instrs.iter().filter(|i| i.op == Op::Load).map(|i| i.width as usize * 4).sum::<usize>() * ITERATIONS
}
/// Scratch read-modify-writes per hash (variant 5): each reads 16 bytes and writes 16 bytes.
pub fn scratch_ops_per_hash(&self) -> usize {
self.instrs.iter().filter(|i| i.op == Op::Scratch).count() * ITERATIONS
}
pub fn has_scratch(&self) -> bool {
self.class.scratch.is_some()
}
/// Width histogram of the loads, in words: (1, 4, 16) counts.
pub fn width_counts(&self) -> [usize; 3] {
let mut c = [0usize; 3];
for i in self.instrs.iter().filter(|i| i.op == Op::Load) {
if let Some(k) = WIDTH_WORDS.iter().position(|&w| w == i.width) {
c[k] += 1;
}
}
c
}
/// Distinct dataset items a 32-lane warp touches per hash: 32 per plain load, 2 per wide load.
pub fn items_per_warp(&self) -> usize {
(self.loads_per_hash() - self.wide_loads_per_hash()) * 32 + self.wide_loads_per_hash() * 2
@ -177,7 +350,11 @@ impl Program {
/// || attempt_le32`. Written into every pack so a version 1 program, or another attempt of the same seed,
/// can never be mistaken for this one.
pub fn program_id(&self) -> u64 {
program_id(self.generator, &self.seed, self.attempt)
if self.class.is_v2() {
program_id(self.generator, &self.seed, self.attempt)
} else {
program_id_class(self.generator, &self.seed, self.attempt, &self.class)
}
}
}
@ -192,6 +369,28 @@ pub fn program_id(generator: u32, seed: &[u32; 8], attempt: u32) -> u64 {
fnv1a64(&b)
}
/// Domain tag of the program id of a read-width class (never collides with [`PROGRAM_ID_TAG`]).
pub const PROGRAM_ID_TAG_RW: &[u8] = b"igneum-program-rw/";
/// The program id of a non-default class: the tag, then the same fields as [`program_id`], then the three mix
/// percentages and the slot count as bytes.
pub fn program_id_class(generator: u32, seed: &[u32; 8], attempt: u32, class: &LoadClass) -> u64 {
let mut b = Vec::with_capacity(PROGRAM_ID_TAG_RW.len() + 4 + 32 + 4 + 4);
b.extend_from_slice(PROGRAM_ID_TAG_RW);
b.extend_from_slice(&generator.to_le_bytes());
for w in seed {
b.extend_from_slice(&w.to_le_bytes());
}
b.extend_from_slice(&attempt.to_le_bytes());
b.extend_from_slice(&class.mix);
b.push(class.load_slots);
if let Some(k) = class.scratch {
b.extend_from_slice(b"scratch/");
b.push(k);
}
fnv1a64(&b)
}
/// Weights of the ten non-load families under version 2, in draw order. Sum 75. The load family has no
/// weight: its count is fixed by [`LOAD_SLOTS`].
pub const NONLOAD_WEIGHTS: [(Op, u64); 10] = [
@ -237,20 +436,39 @@ pub fn attempt_words(seed_bytes: &[u8], attempt: u32) -> [u32; 8] {
/// One version 2 candidate from its seed words, before the acceptance rule. Spec 01 section 1.4.3: 16 slot draws,
/// then nine draws per instruction, 592 per program.
pub fn candidate_from_words(seed_string: &str, seed_bytes: &[u8], seed: [u32; 8], attempt: u32) -> Program {
candidate_from_words_class(seed_string, seed_bytes, seed, attempt, LoadClass::V2)
}
/// [`candidate_from_words`] for a load class. For [`LoadClass::V2`] this is the version 2 draw stream exactly;
/// for any other class the slot count is the class's and every instruction takes a tenth draw, `below(100)`,
/// the width roll (used only on a load slot, drawn on every slot so the stream stays uniform).
pub fn candidate_from_words_class(
seed_string: &str,
seed_bytes: &[u8],
seed: [u32; 8],
attempt: u32,
class: LoadClass,
) -> Program {
let mut rng = program_rng(&seed);
// (1) Load slots: a uniform 16-subset of 1..63 by partial Fisher-Yates. Instruction 0 is never a load.
let slots = class.load_slots as usize;
// (1) Load slots: a uniform subset of 1..63 by partial Fisher-Yates. Instruction 0 is never a load.
let mut p: [u8; INSTR_COUNT - 1] = [0; INSTR_COUNT - 1];
for (i, slot) in p.iter_mut().enumerate() {
*slot = (i + 1) as u8;
}
for i in 0..LOAD_SLOTS {
for i in 0..slots {
let j = i + rng.below((INSTR_COUNT - 1 - i) as u64) as usize;
p.swap(i, j);
}
let mut is_load = [false; INSTR_COUNT];
for &slot in &p[..LOAD_SLOTS] {
for &slot in &p[..slots] {
is_load[slot as usize] = true;
}
// Variant 5: the first k drawn load slots (a uniform k-subset, the draw order is random) are scratch ops.
let mut is_scratch = [false; INSTR_COUNT];
for &slot in &p[..class.scratch_slots()] {
is_scratch[slot as usize] = true;
}
// (2) The instructions. `fresh[r]`: r was written by an earlier instruction and no load has read it since.
let mut fresh = [false; 8];
let mut instrs = Vec::with_capacity(INSTR_COUNT);
@ -265,10 +483,10 @@ pub fn candidate_from_words(seed_string: &str, seed_bytes: &[u8], seed: [u32; 8]
roll -= w;
}
if is_load[k] {
op = Op::Load;
op = if is_scratch[k] { Op::Scratch } else { Op::Load };
}
let dst = rng.below(8);
let src = if op == Op::Load {
let src = if op.is_load() {
let mut eligible = [0u64; 8];
let mut n = 0usize;
for r in 0..8u64 {
@ -301,11 +519,13 @@ pub fn candidate_from_words(seed_string: &str, seed_bytes: &[u8], seed: [u32; 8]
let rot = 1 + rng.below(31) as u32;
let bit = rng.below(32);
let mask = 1u8 << rng.below(5);
if op == Op::Load {
let width = if class.is_v2() { 1 } else { class.width_for_roll(rng.below(100)) };
let width = if op == Op::Load { width } else { 1 };
if op.is_load() {
fresh[src as usize] = false;
}
fresh[dst as usize] = true;
instrs.push(Instr { op, dst: dst as u8, src: src as u8, src2: b as u8, imm, imm2, rot, bit: bit as u8, mask });
instrs.push(Instr { op, dst: dst as u8, src: src as u8, src2: b as u8, imm, imm2, rot, bit: bit as u8, mask, width });
}
Program {
seed_string: seed_string.to_string(),
@ -313,6 +533,7 @@ pub fn candidate_from_words(seed_string: &str, seed_bytes: &[u8], seed: [u32; 8]
seed,
generator: GENERATOR_VERSION,
attempt,
class,
instrs,
}
}
@ -322,6 +543,11 @@ pub fn candidate(seed_string: &str, seed_bytes: &[u8], attempt: u32) -> Program
candidate_from_words(seed_string, seed_bytes, attempt_words(seed_bytes, attempt), attempt)
}
/// [`candidate`] for a load class.
pub fn candidate_class(seed_string: &str, seed_bytes: &[u8], attempt: u32, class: LoadClass) -> Program {
candidate_from_words_class(seed_string, seed_bytes, attempt_words(seed_bytes, attempt), attempt, class)
}
/// Why no program could be derived from a seed.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Exhausted {
@ -342,9 +568,14 @@ impl std::error::Error for Exhausted {}
/// This is what the chain calls (`Epoch::from_seed_bytes`) with the 32-byte epoch seed, and what the packs call
/// with the UTF-8 of a seed string.
pub fn try_generate_from_seed_bytes(seed_string: &str, seed_bytes: &[u8]) -> Result<Program, Exhausted> {
try_generate_class(seed_string, seed_bytes, LoadClass::V2)
}
/// [`try_generate_from_seed_bytes`] for a load class.
pub fn try_generate_class(seed_string: &str, seed_bytes: &[u8], class: LoadClass) -> Result<Program, Exhausted> {
let mut last = None;
for attempt in 0..MAX_ATTEMPTS {
let p = candidate(seed_string, seed_bytes, attempt);
let p = candidate_class(seed_string, seed_bytes, attempt, class);
match check(&p) {
Ok(_) => return Ok(p),
Err(r) => last = Some(r),
@ -358,16 +589,31 @@ pub fn generate_from_seed_bytes(seed_string: &str, seed_bytes: &[u8]) -> Program
try_generate_from_seed_bytes(seed_string, seed_bytes).unwrap_or_else(|e| panic!("{e}"))
}
/// [`generate_from_seed_bytes`] for a load class.
pub fn generate_from_seed_bytes_class(seed_string: &str, seed_bytes: &[u8], class: LoadClass) -> Program {
try_generate_class(seed_string, seed_bytes, class).unwrap_or_else(|e| panic!("{e}"))
}
/// The program of a seed string (its UTF-8 bytes are the program seed).
pub fn generate(seed_string: &str) -> Program {
generate_from_seed_bytes(seed_string, seed_string.as_bytes())
}
/// [`generate`] for a load class.
pub fn generate_class(seed_string: &str, class: LoadClass) -> Program {
generate_from_seed_bytes_class(seed_string, seed_string.as_bytes(), class)
}
/// Every candidate of a seed up to and including the accepted one, with each rejection. For reports and tests.
pub fn attempts(seed_string: &str, seed_bytes: &[u8]) -> Vec<(Program, Result<(), Reject>)> {
attempts_class(seed_string, seed_bytes, LoadClass::V2)
}
/// [`attempts`] for a load class.
pub fn attempts_class(seed_string: &str, seed_bytes: &[u8], class: LoadClass) -> Vec<(Program, Result<(), Reject>)> {
let mut out = Vec::new();
for attempt in 0..MAX_ATTEMPTS {
let p = candidate(seed_string, seed_bytes, attempt);
let p = candidate_class(seed_string, seed_bytes, attempt, class);
let verdict = check(&p).map(|_| ());
let accepted = verdict.is_ok();
out.push((p, verdict));
@ -455,7 +701,7 @@ pub fn generate_v1_from_words(seed_string: &str, seed: [u32; 8], cfg: &Generator
if op == Op::Load && bit * 100 < cfg.wide_frac * 32 {
op = Op::WLoad;
}
instrs.push(Instr { op, dst: dst as u8, src: a as u8, src2: b as u8, imm, imm2, rot, bit: bit as u8, mask });
instrs.push(Instr { op, dst: dst as u8, src: a as u8, src2: b as u8, imm, imm2, rot, bit: bit as u8, mask, width: 1 });
}
Program {
seed_string: seed_string.to_string(),
@ -463,6 +709,7 @@ pub fn generate_v1_from_words(seed_string: &str, seed: [u32; 8], cfg: &Generator
seed,
generator: 1,
attempt: 0,
class: LoadClass::V2,
instrs,
}
}
@ -565,6 +812,81 @@ mod tests {
assert_ne!(program_id(2, &p.seed, 0), program_id(2, &p.seed, 1));
}
/// The read-width classes (5 October 2026): the default class is the version 2 stream exactly; a class
/// program has its slot count, widths from its mix only, and an id that separates it from version 2 and from
/// the other classes.
#[test]
fn load_classes() {
let v2 = candidate("igneum-genesis", b"igneum-genesis", 0);
let same = candidate_class("igneum-genesis", b"igneum-genesis", 0, LoadClass::V2);
assert_eq!(v2, same);
assert!(v2.instrs.iter().all(|i| i.width == 1));
assert_eq!(v2.bytes_per_hash(), 512);
assert_eq!(LoadClass::parse("w16"), Some(LoadClass::fixed(4, 16)));
assert_eq!(LoadClass::parse("w64x4"), Some(LoadClass::fixed(16, 4)));
assert_eq!(LoadClass::parse("50,35,15"), Some(LoadClass::mixed([50, 35, 15])));
assert_eq!(LoadClass::parse("v2"), Some(LoadClass::V2));
assert_eq!(LoadClass::parse("50,35,10"), None);
assert_eq!(LoadClass::fixed(16, 4).name(), "w64x4");
assert_eq!(LoadClass::mixed([25, 50, 25]).name(), "mix25-50-25");
// W = 4 with 16 slots IS the lottery hash: the w4 name parses to the default class
assert_eq!(LoadClass::fixed(1, 16), LoadClass::V2);
assert_eq!(LoadClass::parse("w4"), Some(LoadClass::V2));
assert_eq!(LoadClass::fixed(1, 16).name(), "v2");
assert_eq!(LoadClass::fixed(1, 8).name(), "w4x8");
assert!((LoadClass::mixed([50, 35, 15]).expected_bytes_per_hash() - 2201.6).abs() < 1e-6);
assert!((LoadClass::fixed(16, 4).expected_bytes_per_hash() - 2048.0).abs() < 1e-9);
let mut ids = std::collections::HashSet::new();
ids.insert(v2.program_id());
for (name, slots, widths) in [("w4x8", 8, vec![1u8]), ("w16", 16, vec![4]), ("w64", 16, vec![16]), ("w64x4", 4, vec![16]), ("50,35,15", 16, vec![1, 4, 16]), ("25,50,25", 16, vec![1, 4, 16])] {
let c = LoadClass::parse(name).unwrap();
let p = candidate_class("igneum-genesis", b"igneum-genesis", 0, c);
assert_eq!(p.class, c);
assert_eq!(p.instrs.len(), INSTR_COUNT);
assert_eq!(p.loads_per_hash(), 8 * slots);
assert_ne!(p.instrs[0].op, Op::Load);
for ins in &p.instrs {
if ins.op == Op::Load {
assert!(widths.contains(&ins.width), "{name}: width {}", ins.width);
} else {
assert_eq!(ins.width, 1);
}
}
assert!(ids.insert(p.program_id()), "{name}: program id collides");
}
// variant 5: k scratch ops among the 16 memory operations, the rest one-word loads
for k in [0u8, 2, 4, 8] {
let c = LoadClass::parse(&format!("scr{k}")).unwrap();
assert_eq!(c, LoadClass::scratch(k));
assert_eq!(c.name(), format!("scr{k}"));
let p = candidate_class("igneum-genesis", b"igneum-genesis", 0, c);
assert_eq!(p.loads_per_hash(), 128);
assert_eq!(p.scratch_ops_per_hash(), 8 * k as usize);
assert_eq!(p.bytes_per_hash(), (16 - k as usize) * 8 * 4);
assert!(p.instrs.iter().all(|i| i.width == 1));
assert!(ids.insert(p.program_id()), "scr{k}: program id collides");
}
assert!(!LoadClass::scratch(0).is_v2());
// a class with the version 2 widths but another slot count takes the extra roll: a different stream
let w4x8 = candidate_class("igneum-genesis", b"igneum-genesis", 0, LoadClass::fixed(1, 8));
assert_ne!(w4x8.instrs, v2.instrs);
// the mix draws every width over a population
let mut counts = [0usize; 3];
for i in 0..200u32 {
let s = format!("igneum-rw-mix/{i}");
let p = candidate_class(&s, s.as_bytes(), 0, LoadClass::mixed([50, 35, 15]));
let c = p.width_counts();
for k in 0..3 {
counts[k] += c[k];
}
}
let total = (counts[0] + counts[1] + counts[2]) as f64;
assert_eq!(total as usize, 200 * 16);
assert!((counts[0] as f64 / total - 0.50).abs() < 0.05, "{counts:?}");
assert!((counts[1] as f64 / total - 0.35).abs() < 0.05, "{counts:?}");
assert!((counts[2] as f64 / total - 0.15).abs() < 0.05, "{counts:?}");
}
#[test]
fn generate_returns_an_accepted_program() {
let p = generate("igneum-genesis");

View file

@ -7,8 +7,12 @@
//! [--epoch-hex <64 hex> --day-hex <hex>] byte seeds instead of strings (Epoch::from_seed_bytes)
//! igneum-pow accept --seed <s> [--epoch-hex <64 hex>] every candidate of the seed with its verdict (spec 01 section 1.4.6)
//! igneum-pow show --seed <s> [--epoch-hex <64 hex>] the accepted program, one instruction per line
//!
//! Read-width experiment (5 October 2026, docs/plans/read-width.md): `--class v2|w4|w16|w64|w64x4|p4,p16,p64[xN]`
//! on every command selects the load class (default v2, the lottery hash). Nothing in a v2 run changes.
use igneum_pow::emit::export_pack;
use igneum_pow::generator::LoadClass;
use igneum_pow::memhard::Cache;
use igneum_pow::seed::day_key;
use igneum_pow::verify::{DatasetMode, Epoch, DEFAULT_DATASET_LOG2};
@ -26,6 +30,7 @@ struct Args {
prehash: String,
epoch_hex: Option<String>,
day_hex: Option<String>,
class: LoadClass,
}
fn usage() -> ! {
@ -36,7 +41,8 @@ fn usage() -> ! {
\x20 hash --nonce <n> print the 64-bit hash of one nonce (pack form, init words = seed words)\n\
\x20 hash-bound --prehash <64 hex> --nonce <u64> print the header-bound hash (bind.rs) of one 64-bit nonce\n\
\x20 accept every candidate of the seed (or --epoch-hex) with its acceptance verdict\n\
\x20 show the accepted program, one instruction per line"
\x20 show the accepted program, one instruction per line\n\
\x20 --class C load class (read-width experiment): v2 (default), w4, w16, w64, w64x4, or p4,p16,p64[xN]"
);
std::process::exit(2)
}
@ -54,6 +60,7 @@ fn parse() -> Args {
prehash: "00".repeat(32),
epoch_hex: None,
day_hex: None,
class: LoadClass::V2,
};
let mut it = std::env::args().skip(1);
a.cmd = it.next().unwrap_or_else(|| usage());
@ -70,6 +77,7 @@ fn parse() -> Args {
"--prehash" => a.prehash = val(),
"--epoch-hex" => a.epoch_hex = Some(val()),
"--day-hex" => a.day_hex = Some(val()),
"--class" => a.class = LoadClass::parse(&val()).unwrap_or_else(|| usage()),
_ => usage(),
}
}
@ -85,7 +93,7 @@ fn main() {
"accept" => accept(&a),
"show" => show(&a),
"hash" => {
let e = Epoch::new(&a.seed, &a.day, mode, a.dataset_log2);
let e = Epoch::new_class(&a.seed, &a.day, mode, a.dataset_log2, a.class);
println!("{:016x}", e.hash(a.nonce as u32));
}
"hash-bound" => {
@ -96,9 +104,9 @@ fn main() {
(Some(eh), Some(dh)) => {
let eb = igneum_pow::bind::unhex(eh).unwrap_or_else(|| usage());
let db = igneum_pow::bind::unhex(dh).unwrap_or_else(|| usage());
Epoch::from_seed_bytes(&eb, &db, "cli")
Epoch::from_seed_bytes_class(&eb, &db, "cli", a.class)
}
_ => Epoch::new(&a.seed, &a.day, mode, a.dataset_log2),
_ => Epoch::new_class(&a.seed, &a.day, mode, a.dataset_log2, a.class),
};
let init = igneum_pow::bind::block_init_words(&prehash, a.nonce);
println!("init words {}", init.iter().map(|w| format!("{w:08x}")).collect::<Vec<_>>().join(" "));
@ -125,11 +133,14 @@ fn bench(a: &Args, mode: DatasetMode) {
drop(c);
}
let t0 = Instant::now();
let e = Epoch::new(&a.seed, &a.day, mode, a.dataset_log2);
let e = Epoch::new_class(&a.seed, &a.day, mode, a.dataset_log2, a.class);
let build_ms = t0.elapsed().as_secs_f64() * 1e3;
println!(
"program: {} loads/hash, {} items/warp, op mix {}; epoch built in {build_ms:.1} ms",
"program: class {}, {} loads/hash, {} bytes/hash, widths (1,4,16 words) {:?}, {} items/warp, op mix {}; epoch built in {build_ms:.1} ms",
e.program.class.name(),
e.program.loads_per_hash(),
e.program.bytes_per_hash(),
e.program.width_counts(),
e.program.items_per_warp(),
e.program.op_mix()
);
@ -162,9 +173,9 @@ fn export(a: &Args, mode: DatasetMode) {
(Some(eh), Some(dh)) => {
let eb = igneum_pow::bind::unhex(eh).unwrap_or_else(|| usage());
let db = igneum_pow::bind::unhex(dh).unwrap_or_else(|| usage());
(Epoch::from_seed_bytes(&eb, &db, &format!("igneum-epoch/{eh}/day/{dh}")), format!("bytes:{dh}"))
(Epoch::from_seed_bytes_class(&eb, &db, &format!("igneum-epoch/{eh}/day/{dh}"), a.class), format!("bytes:{dh}"))
}
_ => (Epoch::new(&a.seed, &a.day, mode, a.dataset_log2), a.day.clone()),
_ => (Epoch::new_class(&a.seed, &a.day, mode, a.dataset_log2, a.class), a.day.clone()),
};
let build_ms = t0.elapsed().as_secs_f64() * 1e3;
println!("igneum-pow export {out}");
@ -179,7 +190,7 @@ fn export(a: &Args, mode: DatasetMode) {
e.program.program_id(),
e.program.loads_per_hash()
);
println!("op mix: {}", e.program.op_mix());
println!("op mix: {}; class {}, {} bytes/hash, widths (1,4,16 words) {:?}", e.program.op_mix(), e.program.class.name(), e.program.bytes_per_hash(), e.program.width_counts());
let source = format!("igneum-pow (Rust) CPU interpreter, generator v{}, {} dataset", e.program.generator, e.dataset.mode().name());
let pack = export_pack(&e, &day_label, &source);
let dir = std::path::Path::new(&out);
@ -209,7 +220,7 @@ fn seed_bytes_of(a: &Args) -> (String, Vec<u8>) {
fn accept(a: &Args) {
let (label, bytes) = seed_bytes_of(a);
let t0 = Instant::now();
let tries = igneum_pow::generator::attempts(&label, &bytes);
let tries = igneum_pow::generator::attempts_class(&label, &bytes, a.class);
let ms = t0.elapsed().as_secs_f64() * 1e3;
for (p, verdict) in &tries {
match verdict {
@ -233,19 +244,20 @@ fn accept(a: &Args) {
fn show(a: &Args) {
let (label, bytes) = seed_bytes_of(a);
let p = igneum_pow::generator::generate_from_seed_bytes(&label, &bytes);
let p = igneum_pow::generator::generate_from_seed_bytes_class(&label, &bytes, a.class);
println!(
"seed \"{}\" generator v{} attempt {} program id {:016x} seed words {}",
"seed \"{}\" generator v{} class {} attempt {} program id {:016x} seed words {}",
p.seed_string,
p.generator,
p.class.name(),
p.attempt,
p.program_id(),
p.seed.iter().map(|w| format!("{w:08x}")).collect::<Vec<_>>().join(" ")
);
println!("op mix {} loads/hash {}", p.op_mix(), p.loads_per_hash());
println!("op mix {} loads/hash {} bytes/hash {}", p.op_mix(), p.loads_per_hash(), p.bytes_per_hash());
for (k, i) in p.instrs.iter().enumerate() {
println!(
"{k:2}: {:5} dst={} src={} src2={} imm={:#010x} imm2={:#010x} rot={} bit={} mask={}",
"{k:2}: {:5} dst={} src={} src2={} imm={:#010x} imm2={:#010x} rot={} bit={} mask={}{}",
i.op.name(),
i.dst,
i.src,
@ -254,7 +266,8 @@ fn show(a: &Args) {
i.imm2,
i.rot,
i.bit,
i.mask
i.mask,
if i.op == igneum_pow::generator::Op::Load && i.width > 1 { format!(" width={}B", i.width as u32 * 4) } else { String::new() }
);
}
}

View file

@ -269,6 +269,34 @@ impl MemhardCpu {
}
u
}
/// `out[k][j] = dataset[base[k] + j]` for `j < width` (read-width experiment): `base[k]` is aligned to `width`
/// words, so every lane's words lie in one item, derived once per distinct item. Returns the distinct items.
pub fn fetch_wide(&self, base: &[u32], width: usize, out: &mut [[u32; 16]]) -> usize {
let n = base.len();
assert!(n <= FETCH_MAX && out.len() >= n && width <= 16);
let mut uniq = [0u32; FETCH_MAX];
let mut slot = [0u8; FETCH_MAX];
let mut u = 0usize;
for k in 0..n {
let t = base[k] >> 4;
let j = match uniq[..u].iter().position(|&x| x == t) {
Some(j) => j,
None => {
uniq[u] = t;
u += 1;
u - 1
}
};
slot[k] = j as u8;
}
let mut items = [[0u32; 16]; FETCH_MAX];
derive_items(&uniq[..u], &self.params, &self.cache, &mut items);
for k in 0..n {
let o = (base[k] & 15) as usize;
out[k][..width].copy_from_slice(&items[slot[k] as usize][o..o + width]);
}
u
}
}
#[cfg(test)]

View file

@ -1,10 +1,91 @@
//! The CPU reference interpreter for one 32-lane warp (`cpuWarpTraced` in the Swift) and the API the node
//! calls. Dataset words come from the memory-hard cache (default) or from the closed form (old packs).
use crate::generator::{generate, Instr, Op, Program, ITERATIONS, LANES};
use crate::generator::{
generate, generate_class, Instr, LoadClass, Op, Program, ITERATIONS, LANES, SCRATCH_SLOTS, SCRATCH_SLOT_MASK,
};
use crate::memhard::MemhardCpu;
use crate::seed::day_key;
/// Read-width experiment (5 October 2026): a `load` of `W` words folds every word into `dst`:
/// `x = dst XOR w[0]; for j in 1..W: x = (rotl(x, FOLD_ROT) * FOLD_MUL) XOR w[j]; dst = x`. For `W = 1` this is the
/// lottery hash's `dst XOR dataset[...]`. The fold is state-dependent (the rotate-multiply sits between the words),
/// so no function of the line alone replaces it: two different lines give two different maps of `dst`, and a
/// dataset of folded lines cannot be stored in place of the dataset (see `docs/plans/read-width.md`).
pub const FOLD_ROT: u32 = 11;
pub const FOLD_MUL: u32 = 0x9E3779B1;
/// The fold of `words` into `dst` (at least one word).
#[inline(always)]
pub fn fold_words(dst: u32, words: &[u32]) -> u32 {
let mut x = dst ^ words[0];
for &w in &words[1..] {
x = x.rotate_left(FOLD_ROT).wrapping_mul(FOLD_MUL) ^ w;
}
x
}
/// Variant 5 (scratch): the fill value of word `j` (0..2) of slot `slot` of lane `lane` of the unit at base nonce
/// `base`, under program seed words `seed`. The scratch of a unit starts as these values; a slot written during
/// the unit's hash holds what was written. Mirrored as `scr_fill` in every emitted kernel.
#[inline(always)]
pub fn scratch_fill(seed: &[u32; 8], base: u32, lane: u32, slot: u32, j: u32) -> u32 {
splitmix32(
(base.wrapping_add(lane) ^ seed[j as usize])
.wrapping_add(slot.wrapping_mul(0x9E3779B1))
.wrapping_add((j + 1).wrapping_mul(0x85EBCA77)),
)
}
/// Variant 5: the 16-byte slot after a read-modify-write that read `w` and folded to `x`: `(x ^ w1, rotl(x, 7) ^ w2,
/// x + w0)` behind the slot's tag.
#[inline(always)]
pub fn scratch_rewrite(x: u32, w: &[u32; 3]) -> [u32; 3] {
[x ^ w[1], x.rotate_left(7) ^ w[2], x.wrapping_add(w[0])]
}
/// The CPU model of one unit's scratch (variant 5): per lane, the written slots and their words. Unwritten slots
/// read as [`scratch_fill`]. A unit touches at most `scratch ops x 32` slots, so the model is small whatever the
/// nominal 1 MiB; a GPU keeps the real 1 MiB per resident warp with a per-unit tag per slot.
pub struct ScratchModel {
written: Vec<bool>,
data: Vec<[u32; 3]>,
pub reads: usize,
pub writes: usize,
}
impl ScratchModel {
pub fn new() -> Self {
Self { written: vec![false; LANES * SCRATCH_SLOTS], data: vec![[0; 3]; LANES * SCRATCH_SLOTS], reads: 0, writes: 0 }
}
/// Read slot `slot` of `lane`, then rewrite it from the fold result `x`. Returns the three words read.
#[inline]
pub fn rmw(&mut self, seed: &[u32; 8], base: u32, lane: usize, slot: u32, dst: u32) -> u32 {
let i = lane * SCRATCH_SLOTS + slot as usize;
let w = if self.written[i] {
self.data[i]
} else {
[
scratch_fill(seed, base, lane as u32, slot, 0),
scratch_fill(seed, base, lane as u32, slot, 1),
scratch_fill(seed, base, lane as u32, slot, 2),
]
};
let x = fold_words(dst, &w);
self.data[i] = scratch_rewrite(x, &w);
self.written[i] = true;
self.reads += 1;
self.writes += 1;
x
}
}
impl Default for ScratchModel {
fn default() -> Self {
Self::new()
}
}
/// Dataset element, closed form of (day words, index). The original prototype's six-operation element.
#[inline(always)]
pub fn dataset_elem(i: u32, d0: u32, d1: u32) -> u32 {
@ -121,6 +202,23 @@ impl DatasetSource {
Dataset::MemoryHard(m) => m.fetch(idx, out),
}
}
/// `out[k][j] = dataset[base[k] + j]` for `j < width`; bases are masked and aligned to `width` words
/// (`width` 4 or 16, so a lane's words lie in one item). Returns items derived (0 for the closed form).
#[inline]
fn fetch_wide(&self, base: &[u32; LANES], width: usize, out: &mut [[u32; 16]; LANES]) -> usize {
match &self.dataset {
Dataset::ClosedForm { d0, d1 } => {
for k in 0..LANES {
for j in 0..width {
out[k][j] = dataset_elem(base[k] + j as u32, *d0, *d1);
}
}
0
}
Dataset::MemoryHard(m) => m.fetch_wide(base, width, out),
}
}
}
/// The result of interpreting one warp.
@ -160,10 +258,19 @@ pub fn interpret_warp_init(program: &Program, seed: &[u32; 8], base_nonce: u32,
let mut items_derived = 0usize;
let mut idx = [0u32; LANES];
let mut val = [0u32; LANES];
let mut scratch = if program.has_scratch() { Some(ScratchModel::new()) } else { None };
for _ in 0..ITERATIONS {
let sel = r[0];
for ins in &program.instrs {
step(ins, &mut r, &sel, mask, ds, &mut idx, &mut val, &mut items_derived);
if ins.op == Op::Scratch {
let m = scratch.as_mut().expect("a scratch op needs a scratch class");
let (d, a) = (ins.dst as usize, ins.src as usize);
for lane in 0..LANES {
let slot = r[a][lane] & SCRATCH_SLOT_MASK;
r[d][lane] = m.rmw(&program.seed, base_nonce, lane, slot, r[d][lane]);
}
}
}
}
let mut hashes = [0u64; LANES];
@ -255,7 +362,7 @@ fn step(
r[d][lane] ^= src[lane ^ m];
}
}
Op::Load => {
Op::Load if ins.width == 1 => {
for lane in 0..LANES {
idx[lane] = r[a][lane] & mask;
}
@ -264,6 +371,22 @@ fn step(
r[d][lane] ^= val[lane];
}
}
Op::Load => {
// Read-width experiment: `width` words from the aligned address, every word folded into dst.
let width = ins.width as usize;
let align = !(ins.width as u32 - 1);
for lane in 0..LANES {
idx[lane] = (r[a][lane] & mask) & align;
}
let mut vals = [[0u32; 16]; LANES];
*items_derived += ds.fetch_wide(idx, width, &mut vals);
for lane in 0..LANES {
r[d][lane] = fold_words(r[d][lane], &vals[lane][..width]);
}
}
Op::Scratch => {
// handled by the caller (interpret_warp_init), which owns the unit's scratch model
}
Op::WLoad => {
// Lane 0's register, masked, aligned down to 32 words; lane l reads word base + l.
let base = (r[a][0] & mask) & !31;
@ -299,6 +422,11 @@ impl Epoch {
Self { program: generate(seed), dataset: DatasetSource::new(day, mode, dataset_log2) }
}
/// [`Epoch::new`] with a load class (read-width experiment).
pub fn new_class(seed: &str, day: &str, mode: DatasetMode, dataset_log2: u32, class: LoadClass) -> Self {
Self { program: generate_class(seed, class), dataset: DatasetSource::new(day, mode, dataset_log2) }
}
/// The production shape: memory-hard, 1 GiB dataset.
pub fn memory_hard(seed: &str, day: &str) -> Self {
Self::new(seed, day, DatasetMode::MemoryHard, DEFAULT_DATASET_LOG2)
@ -309,7 +437,12 @@ impl Epoch {
/// `seed_words_from_bytes(day_bytes)` (`bind::day_bytes`). Memory-hard, 1 GiB dataset. `label` is only
/// recorded in emitted packs.
pub fn from_seed_bytes(epoch_seed: &[u8], day_bytes: &[u8], label: &str) -> Self {
let program = crate::generator::generate_from_seed_bytes(label, epoch_seed);
Self::from_seed_bytes_class(epoch_seed, day_bytes, label, LoadClass::V2)
}
/// [`Epoch::from_seed_bytes`] with a load class (read-width experiment).
pub fn from_seed_bytes_class(epoch_seed: &[u8], day_bytes: &[u8], label: &str, class: LoadClass) -> Self {
let program = crate::generator::generate_from_seed_bytes_class(label, epoch_seed, class);
let key = crate::seed::seed_words_from_bytes(day_bytes);
let mut dataset = DatasetSource::from_key(key, DatasetMode::MemoryHard, DEFAULT_DATASET_LOG2);
dataset.key_bytes = day_bytes.to_vec();
@ -356,6 +489,80 @@ mod tests {
assert_eq!(ds.word(0x0fffffff), 0xf78c84a4);
}
/// Read-width experiment: the fold for one word is a plain xor; a wide fetch hands each lane the words the
/// scalar path would; two distinct lines give two distinct maps of dst (one point suffices as a smoke check).
#[test]
fn fold_and_wide_fetch() {
assert_eq!(fold_words(0x1234_5678, &[0xdead_beef]), 0x1234_5678 ^ 0xdead_beef);
let w = [1u32, 2, 3, 4];
let x = fold_words(7, &w);
let mut y: u32 = 7 ^ 1;
for &v in &w[1..] {
y = y.rotate_left(FOLD_ROT).wrapping_mul(FOLD_MUL) ^ v;
}
assert_eq!(x, y);
assert_ne!(fold_words(7, &[1, 2, 3, 4]), fold_words(7, &[1, 2, 3, 5]));
let ds = DatasetSource::new("2026-10-03", DatasetMode::MemoryHard, 20);
let mut base = [0u32; LANES];
for (k, b) in base.iter_mut().enumerate() {
*b = ((k as u32 * 0x9E37_79B1) & ds.mask) & !15;
}
let mut out = [[0u32; 16]; LANES];
let items = ds.fetch_wide(&base, 16, &mut out);
assert!(items >= 1 && items <= LANES);
for k in 0..LANES {
for j in 0..16 {
assert_eq!(out[k][j], ds.word(base[k] + j as u32), "lane {k} word {j}");
}
}
let mut base4 = base;
for b in base4.iter_mut() {
*b += 8;
}
let items4 = ds.fetch_wide(&base4, 4, &mut out);
assert_eq!(items4, items);
for k in 0..LANES {
for j in 0..4 {
assert_eq!(out[k][j], ds.word(base4[k] + j as u32));
}
}
}
/// A wide-load program interprets identically on the closed form and through the memory-hard path's fold
/// (the same fold code), and a mixed-class epoch builds and hashes.
/// Variant 5: a fill word is deterministic, a rewrite changes the slot, and a second read of a written slot
/// returns the rewrite, not the fill.
#[test]
fn scratch_model() {
let seed = [1u32, 2, 3, 4, 5, 6, 7, 8];
assert_eq!(scratch_fill(&seed, 32, 3, 100, 1), scratch_fill(&seed, 32, 3, 100, 1));
assert_ne!(scratch_fill(&seed, 32, 3, 100, 1), scratch_fill(&seed, 32, 3, 100, 2));
assert_ne!(scratch_fill(&seed, 32, 3, 100, 1), scratch_fill(&seed, 64, 3, 100, 1));
let mut m = ScratchModel::new();
let w = [scratch_fill(&seed, 32, 3, 100, 0), scratch_fill(&seed, 32, 3, 100, 1), scratch_fill(&seed, 32, 3, 100, 2)];
let x = m.rmw(&seed, 32, 3, 100, 0xabcd);
assert_eq!(x, fold_words(0xabcd, &w));
let x2 = m.rmw(&seed, 32, 3, 100, 0xabcd);
assert_eq!(x2, fold_words(0xabcd, &scratch_rewrite(x, &w)));
assert_eq!(m.reads, 2);
let e = Epoch::new_class("igneum-genesis", "2026-10-03", DatasetMode::ClosedForm, 20, LoadClass::scratch(4));
assert_eq!(e.program.scratch_ops_per_hash(), 32);
assert_eq!(e.hash_warp(0), e.hash_warp(0));
}
#[test]
fn wide_class_epochs_hash() {
for name in ["w16", "w64x4", "50,35,15"] {
let c = LoadClass::parse(name).unwrap();
let e = Epoch::new_class("igneum-genesis", "2026-10-03", DatasetMode::ClosedForm, 20, c);
assert_eq!(e.program.class, c);
let a = e.hash_warp(0);
let b = e.hash_warp(0);
assert_eq!(a, b);
assert_ne!(a[0], a[1]);
}
}
#[test]
fn closed_form_genesis_vector_lane0() {
// Generator v2 vectors (4 October 2026), proto-cuda/packs/igneum-genesis/vectors.json.

View file

@ -20,6 +20,9 @@
#define __forceinline__ inline
struct uint3 { unsigned x, y, z; };
// uint4 and make_uint4 (read-width experiment, 5 October 2026): the wide loads and the scratch slots are 16-byte vectors.
struct uint4 { unsigned x, y, z, w; };
static inline uint4 make_uint4(unsigned x, unsigned y, unsigned z, unsigned w) { uint4 v; v.x = x; v.y = y; v.z = z; v.w = w; return v; }
struct dim3 { unsigned x, y, z; dim3(unsigned x_ = 1, unsigned y_ = 1, unsigned z_ = 1) : x(x_), y(y_), z(z_) {} };
extern thread_local uint3 threadIdx;
extern thread_local uint3 blockIdx;

View file

@ -0,0 +1,278 @@
// Generated by igneum-pow export (generator v2) for seed "igneum-readwidth/A/0". Do not edit by hand.
// OpenCL C twin of the Metal kernel for the same seed (see proto-opencl/README.md, WAVEFRONT.md and program.metal).
// Built from source at runtime by proto-opencl/host.c, which passes these defines:
// IGNEUM_GROUP work-group size of igneum_hash, a multiple of 32 (default 32: one work-group = one 32-lane unit)
// IGNEUM_EXCHANGE 0 = local-memory exchange with a barrier (any device, any wave width; the default)
// 1 = sub_group_shuffle_xor (cl_khr_subgroup_shuffle), only with IGNEUM_GROUP 32 and a sub-group size of exactly 32
// 2 = intel_sub_group_shuffle_xor (cl_intel_subgroups), same condition
// The verification unit is always 32 lanes. A 64-wide hardware wave (AMD GCN/CDNA, RDNA in wave64) runs two units;
// the exchange masks are 1, 2, 4, 8, 16, so every partner lane lies inside the lane's own aligned run of 32.
#ifndef IGNEUM_GROUP
#define IGNEUM_GROUP 32
#endif
#ifndef IGNEUM_EXCHANGE
#define IGNEUM_EXCHANGE 0
#endif
#ifdef __OPENCL_VERSION__
#define IGNEUM_KERNEL_HASH __kernel __attribute__((reqd_work_group_size(IGNEUM_GROUP, 1, 1)))
#define IGNEUM_LOCAL_WORDS(name, n) __local uint name[n]
#if IGNEUM_EXCHANGE == 1
#ifdef cl_khr_subgroups
#pragma OPENCL EXTENSION cl_khr_subgroups : enable
#endif
#ifdef cl_khr_subgroup_shuffle
#pragma OPENCL EXTENSION cl_khr_subgroup_shuffle : enable
#endif
#elif IGNEUM_EXCHANGE == 2
#pragma OPENCL EXTENSION cl_intel_subgroups : enable
#endif
#else
// Not an OpenCL compiler: proto-opencl/emu compiles this file as C++ and supplies the built-ins and these two macros.
#include "emu_opencl.h"
#endif
#if IGNEUM_EXCHANGE == 1
#define IGNEUM_SHFL_XOR(dst, a, m) dst = sub_group_shuffle_xor((a), (uint)(m))
#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u)
#elif IGNEUM_EXCHANGE == 2
#define IGNEUM_SHFL_XOR(dst, a, m) dst = intel_sub_group_shuffle_xor((a), (uint)(m))
#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u)
#else
// Local-memory exchange. Two buffers of IGNEUM_GROUP words alternate (xk counts exchanges), so one barrier per
// exchange is enough: a lane can only overwrite buffer b at exchange k+2 after passing barrier k+1, and every lane
// reaches barrier k+1 only after its read of buffer b at exchange k. The partner lid ^ m stays inside the lane's
// aligned run of 32 because m < 32. Control flow is uniform, so every work-item reaches every barrier.
#define IGNEUM_SHFL_XOR(dst, a, m) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid ^ (uint)(m))]; xk += 1u; }
#define IGNEUM_BCAST0(dst, a) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid & ~31u)]; xk += 1u; }
#endif
static inline uint splitmix32(uint 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. OpenCL rotate() rotates left by n modulo 32.
static inline uint rotl_imm(uint x, uint n) { return rotate(x, n); }
// Right rotation by n modulo 32 as a left rotation by (32 - n) modulo 32; n == 0 gives x.
static inline uint rotr_var(uint x, uint n) { return rotate(x, (0u - n) & 31u); }
static 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;
}
// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines.
// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals.
#define MH_CACHE_LINE_MASK 0x003fffffu
#define MH_SEGMENT_LINES 64u
#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); }
static inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site
// y = ChaCha12 core(x) + x
static inline void mh_chacha_block(const uint* x, uint* y) {
for (uint i = 0u; i < 16u; ++i) y[i] = x[i];
for (uint r = 0u; r < 6u; ++r) {
MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u)
MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u)
}
for (uint i = 0u; i < 16u; ++i) y[i] += x[i];
}
// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0.
static inline void mh_cache_segment(__global uint* cache, uint seg) {
uint prev[16]; uint x[16]; uint y[16];
for (uint i = 0u; i < 16u; ++i) prev[i] = 0u;
for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) {
x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3];
x[4] = 0x3067619fu ^ prev[4];
x[5] = 0x3c269176u ^ prev[5];
x[6] = 0x84a03b03u ^ prev[6];
x[7] = 0xf8c63294u ^ prev[7];
x[8] = 0xff977c5bu ^ prev[8];
x[9] = 0xe60def3eu ^ prev[9];
x[10] = 0x63630141u ^ prev[10];
x[11] = 0xb8fbcb58u ^ prev[11];
x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15];
mh_chacha_block(x, y);
__global uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u);
for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; }
}
}
// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations.
static inline void mh_mixer(uint* s, uint rk) {
s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u;
s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu;
s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u;
s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu;
s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u;
s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u;
s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u;
s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u;
s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du;
s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u;
s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du;
s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu;
s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du;
s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu;
s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u;
s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u;
MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u)
MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u)
MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u)
MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u)
}
// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer.
static inline void mh_item(__global const uint* cache, uint t, uint* s) {
s[0] = 0x3067619fu;
s[1] = 0x3c269176u;
s[2] = 0x84a03b03u;
s[3] = 0xf8c63294u;
s[4] = 0xff977c5bu;
s[5] = 0xe60def3eu;
s[6] = 0x63630141u;
s[7] = 0xb8fbcb58u;
s[8] = t * 0x42146205u + 0xbab68293u;
s[9] = t * 0x52cbe0fbu + 0xcc162340u;
s[10] = t * 0x7ecf4a03u + 0x6ce151ccu;
s[11] = t * 0x6728907fu + 0xe62b8997u;
s[12] = t * 0xd81d9751u + 0xc9c80297u;
s[13] = t * 0x132952c3u + 0xf74a1654u;
s[14] = t * 0xf60de277u + 0x3d704af5u;
s[15] = t * 0x05358035u + 0x3cf522b7u;
for (uint r = 0u; r < 8u; ++r) {
mh_mixer(s, 0x9E3779B9u * (r + 1u));
__global const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u);
for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i];
}
mh_mixer(s, 0x9E3779B9u * 9u);
}
// dataset[w] without the dataset: derive item w >> 4 and take word w & 15.
static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; }
// Memory-hard dataset (MEMHARD.md). One work-item per cache segment; one work-item per 64-byte dataset item.
// The same constants as memhard.h in this pack (one emitter, three dialects).
__kernel void igneum_cache_fill(__global uint* cache, uint nSegments) {
uint seg = (uint)get_global_id(0);
if (seg < nSegments) mh_cache_segment(cache, seg);
}
__kernel void igneum_build(__global uint* ds, __global const uint* cache, uint nItems) {
uint t = (uint)get_global_id(0);
if (t < nItems) {
uint s[16];
mh_item(cache, t, s);
__global uint* d = ds + ((ulong)t * 16u);
for (uint i = 0u; i < 16u; ++i) d[i] = s[i];
}
}
// One hash per work-item. IGNEUM_GROUP is a multiple of 32; lane = lid & 31 and every exchange stays inside the
// lane's own aligned run of 32 work-items, exactly like simd_shuffle_xor inside a 32-wide Metal SIMD group and
// __shfl_xor_sync inside a CUDA warp. Control flow is uniform (no branches at all).
IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask) {
uint gid = (uint)get_global_id(0);
uint lid = (uint)get_local_id(0);
uint nonce = baseNonce + gid;
uint r0, r1, r2, r3, r4, r5, r6, r7;
#if IGNEUM_EXCHANGE == 0
IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP);
uint xk = 0u;
#else
(void)lid;
#endif
{ uint x = nonce ^ 0xaa5a3f6eu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x5c0410c3u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1]
{ uint x = nonce ^ 0x5c0410c3u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0x9d994375u; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2]
{ uint x = nonce ^ 0x9d994375u; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0xd8d53386u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3]
{ uint x = nonce ^ 0xd8d53386u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0x2c956ee2u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4]
{ uint x = nonce ^ 0x2c956ee2u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xe313c2f9u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5]
{ uint x = nonce ^ 0xe313c2f9u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0x495ace1bu; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6]
{ uint x = nonce ^ 0x495ace1bu; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0x8238bb25u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7]
{ uint x = nonce ^ 0x8238bb25u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0xaa5a3f6eu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0]
for (uint it = 0u; it < 8u; ++it) {
uint sel = r0;
r2 = r2 + r7 + ((((sel >> 30u) & 1u) != 0u) ? 0xb788d5f8u : 0x92d00116u); // 0 add
r3 = mul_hi(r3, r2); // 1 mulhi
r6 = r6 ^ ds[r3 & mask]; // 2 load
r0 = r0 ^ ds[r6 & mask]; // 3 load
r5 = r5 * r2; // 4 mul
r7 = r7 + r2 + ((((sel >> 20u) & 1u) != 0u) ? 0x9a773a55u : 0xd5f6f389u); // 5 add
{ uint b_ = (r5 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r6 = x_; } // 6 load
r5 = r5 ^ ds[r6 & mask]; // 7 load
{ uint b_ = (r2 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r1 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r1 = x_; } // 8 load
r4 = r4 * r5; // 9 mul
{ uint b_ = (r0 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r2 = x_; } // 10 load
r3 = r3 ^ ds[r2 & mask]; // 11 load
r4 = r4 ^ r0; // 12 xor
r7 = r7 ^ ds[r3 & mask]; // 13 load
r7 = r7 ^ r1; // 14 xor
r4 = r1 * r4 + r4; // 15 mad
r3 = r0 * r4 + r3; // 16 mad
r4 = r6 * r2 + r4; // 17 mad
r0 = rotr_var(r0, r2); // 18 rotr
r5 = rotr_var(r5, r6); // 19 rotr
r4 = r4 ^ ds[r1 & mask]; // 20 load
r2 = r2 ^ ds[r4 & mask]; // 21 load
r1 = rotl_imm(r1, 14u); // 22 rotl
r7 = r7 - r1; // 23 sub
{ uint t_; IGNEUM_SHFL_XOR(t_, r6, 2u); r2 = r2 ^ t_; } // 24 shfl
{ uint t_; IGNEUM_SHFL_XOR(t_, r4, 16u); r1 = r1 ^ t_; } // 25 shfl
r6 = r6 ^ ds[r0 & mask]; // 26 load
{ uint t_; IGNEUM_SHFL_XOR(t_, r6, 1u); r0 = r0 ^ t_; } // 27 shfl
r0 = r0 + r2 + ((((sel >> 25u) & 1u) != 0u) ? 0xc6311db1u : 0x1a3cecfbu); // 28 add
r5 = mul_hi(r5, r7); // 29 mulhi
r0 = r1 * r7 + r0; // 30 mad
r5 = rotl_imm(r5, 23u); // 31 rotl
r0 = r0 + r2 + ((((sel >> 28u) & 1u) != 0u) ? 0xf1282553u : 0x7aa05e39u); // 32 add
r1 = r1 ^ r6; // 33 xor
r0 = r0 - r1; // 34 sub
r1 = r1 ^ ds[r6 & mask]; // 35 load
r1 = rotl_imm(r1, 7u); // 36 rotl
{ uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r0 = r0 ^ t_; } // 37 shfl
r3 = r3 + r5 + ((((sel >> 13u) & 1u) != 0u) ? 0x70173cfdu : 0x4a9bf1a8u); // 38 add
r3 = mul_hi(r3, r4); // 39 mulhi
r3 = r3 + r6 + ((((sel >> 8u) & 1u) != 0u) ? 0x2596fd35u : 0x26b3e1a7u); // 40 add
r3 = r3 - r7; // 41 sub
r0 = r0 - r4; // 42 sub
r5 = rotr_var(r5, r7); // 43 rotr
r3 = r4 * r1 + r3; // 44 mad
r4 = r4 ^ ds[r2 & mask]; // 45 load
r2 = mul_hi(r2, r5); // 46 mulhi
r3 = r3 ^ ds[r5 & mask]; // 47 load
r5 = mul_hi(r5, r6); // 48 mulhi
r5 = r5 + r7 + ((((sel >> 22u) & 1u) != 0u) ? 0x724f77b9u : 0x9f31d23cu); // 49 add
{ uint t_; IGNEUM_SHFL_XOR(t_, r7, 1u); r2 = r2 ^ t_; } // 50 shfl
r1 = r1 * r5; // 51 mul
r1 = r1 * r6; // 52 mul
{ uint b_ = (r1 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r6 = x_; } // 53 load
r7 = r7 ^ r2; // 54 xor
r2 = r2 - r1; // 55 sub
r7 = r7 ^ r3; // 56 xor
r3 = rotl_imm(r3, 2u); // 57 rotl
r6 = mul_hi(r6, r4); // 58 mulhi
r4 = rotr_var(r4, r5); // 59 rotr
r3 = mul_hi(r3, r1); // 60 mulhi
r3 = r3 ^ r6; // 61 xor
{ uint t_; IGNEUM_SHFL_XOR(t_, r1, 16u); r5 = r5 ^ t_; } // 62 shfl
r1 = r1 ^ ds[r6 & mask]; // 63 load
}
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;
}
#if IGNEUM_EXCHANGE != 0
// Reports the sub-group size this device uses for a work-group of IGNEUM_GROUP items. host.c runs it only when the
// per-kernel query (clGetKernelSubGroupInfoKHR on igneum_hash) is unavailable; that query is preferred because a
// compiler may pick a different wave width per kernel (RDNA: wave32 or wave64). See WAVEFRONT.md.
IGNEUM_KERNEL_HASH void igneum_probe_subgroup(__global uint* out) {
if (get_local_id(0) == 0u) { out[0] = get_sub_group_size(); out[1] = get_num_sub_groups(); }
}
#endif

View file

@ -0,0 +1,164 @@
// Generated by igneum-pow export (generator v2) for seed "igneum-readwidth/A/0". 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"
#include "memhard.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;
}
// Memory-hard dataset (MEMHARD.md). One thread per cache segment; one thread per 64-byte dataset item.
// The core functions (mh_cache_segment, mh_item) are in memhard.h and are also compiled for the host.
__global__ void igneum_cache_fill(uint32_t* cache, uint32_t nSegments) {
uint32_t seg = blockIdx.x * blockDim.x + threadIdx.x;
if (seg < nSegments) mh_cache_segment(cache, seg);
}
__global__ void igneum_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems) {
uint32_t t = blockIdx.x * blockDim.x + threadIdx.x;
if (t < nItems) {
uint32_t s[16];
mh_item(cache, t, s);
uint32_t* d = ds + (size_t)t * 16u;
for (uint32_t i = 0u; i < 16u; ++i) d[i] = s[i];
}
}
// 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 ^ 0xaa5a3f6eu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x5c0410c3u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1]
{ uint32_t x = nonce ^ 0x5c0410c3u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0x9d994375u; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2]
{ uint32_t x = nonce ^ 0x9d994375u; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0xd8d53386u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3]
{ uint32_t x = nonce ^ 0xd8d53386u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0x2c956ee2u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4]
{ uint32_t x = nonce ^ 0x2c956ee2u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xe313c2f9u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5]
{ uint32_t x = nonce ^ 0xe313c2f9u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0x495ace1bu; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6]
{ uint32_t x = nonce ^ 0x495ace1bu; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0x8238bb25u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7]
{ uint32_t x = nonce ^ 0x8238bb25u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0xaa5a3f6eu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0]
for (uint32_t it = 0u; it < 8u; ++it) {
uint32_t sel = r0;
r2 = r2 + r7 + ((((sel >> 30u) & 1u) != 0u) ? 0xb788d5f8u : 0x92d00116u); // 0 add
r3 = __umulhi(r3, r2); // 1 mulhi
r6 = r6 ^ ds[r3 & mask]; // 2 load
r0 = r0 ^ ds[r6 & mask]; // 3 load
r5 = r5 * r2; // 4 mul
r7 = r7 + r2 + ((((sel >> 20u) & 1u) != 0u) ? 0x9a773a55u : 0xd5f6f389u); // 5 add
{ uint32_t b_ = (r5 & mask) & ~15u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint32_t x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r6 = x_; } // 6 load
r5 = r5 ^ ds[r6 & mask]; // 7 load
{ uint32_t b_ = (r2 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r1 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r1 = x_; } // 8 load
r4 = r4 * r5; // 9 mul
{ uint32_t b_ = (r0 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r2 = x_; } // 10 load
r3 = r3 ^ ds[r2 & mask]; // 11 load
r4 = r4 ^ r0; // 12 xor
r7 = r7 ^ ds[r3 & mask]; // 13 load
r7 = r7 ^ r1; // 14 xor
r4 = r1 * r4 + r4; // 15 mad
r3 = r0 * r4 + r3; // 16 mad
r4 = r6 * r2 + r4; // 17 mad
r0 = rotr_var(r0, r2); // 18 rotr
r5 = rotr_var(r5, r6); // 19 rotr
r4 = r4 ^ ds[r1 & mask]; // 20 load
r2 = r2 ^ ds[r4 & mask]; // 21 load
r1 = rotl_imm(r1, 14u); // 22 rotl
r7 = r7 - r1; // 23 sub
r2 = r2 ^ __shfl_xor_sync(0xffffffffu, r6, 2); // 24 shfl
r1 = r1 ^ __shfl_xor_sync(0xffffffffu, r4, 16); // 25 shfl
r6 = r6 ^ ds[r0 & mask]; // 26 load
r0 = r0 ^ __shfl_xor_sync(0xffffffffu, r6, 1); // 27 shfl
r0 = r0 + r2 + ((((sel >> 25u) & 1u) != 0u) ? 0xc6311db1u : 0x1a3cecfbu); // 28 add
r5 = __umulhi(r5, r7); // 29 mulhi
r0 = r1 * r7 + r0; // 30 mad
r5 = rotl_imm(r5, 23u); // 31 rotl
r0 = r0 + r2 + ((((sel >> 28u) & 1u) != 0u) ? 0xf1282553u : 0x7aa05e39u); // 32 add
r1 = r1 ^ r6; // 33 xor
r0 = r0 - r1; // 34 sub
r1 = r1 ^ ds[r6 & mask]; // 35 load
r1 = rotl_imm(r1, 7u); // 36 rotl
r0 = r0 ^ __shfl_xor_sync(0xffffffffu, r4, 8); // 37 shfl
r3 = r3 + r5 + ((((sel >> 13u) & 1u) != 0u) ? 0x70173cfdu : 0x4a9bf1a8u); // 38 add
r3 = __umulhi(r3, r4); // 39 mulhi
r3 = r3 + r6 + ((((sel >> 8u) & 1u) != 0u) ? 0x2596fd35u : 0x26b3e1a7u); // 40 add
r3 = r3 - r7; // 41 sub
r0 = r0 - r4; // 42 sub
r5 = rotr_var(r5, r7); // 43 rotr
r3 = r4 * r1 + r3; // 44 mad
r4 = r4 ^ ds[r2 & mask]; // 45 load
r2 = __umulhi(r2, r5); // 46 mulhi
r3 = r3 ^ ds[r5 & mask]; // 47 load
r5 = __umulhi(r5, r6); // 48 mulhi
r5 = r5 + r7 + ((((sel >> 22u) & 1u) != 0u) ? 0x724f77b9u : 0x9f31d23cu); // 49 add
r2 = r2 ^ __shfl_xor_sync(0xffffffffu, r7, 1); // 50 shfl
r1 = r1 * r5; // 51 mul
r1 = r1 * r6; // 52 mul
{ uint32_t b_ = (r1 & mask) & ~15u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint32_t x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r6 = x_; } // 53 load
r7 = r7 ^ r2; // 54 xor
r2 = r2 - r1; // 55 sub
r7 = r7 ^ r3; // 56 xor
r3 = rotl_imm(r3, 2u); // 57 rotl
r6 = __umulhi(r6, r4); // 58 mulhi
r4 = rotr_var(r4, r5); // 59 rotr
r3 = __umulhi(r3, r1); // 60 mulhi
r3 = r3 ^ r6; // 61 xor
r5 = r5 ^ __shfl_xor_sync(0xffffffffu, r1, 16); // 62 shfl
r1 = r1 ^ ds[r6 & mask]; // 63 load
}
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_cache_fill(uint32_t* cache, uint32_t nSegments) {
if (nSegments == 0u) return cudaErrorInvalidValue;
uint32_t block = 256u;
uint32_t grid = (nSegments + block - 1u) / block;
igneum_cache_fill<<<grid, block>>>(cache, nSegments);
return cudaGetLastError();
}
cudaError_t igneum_launch_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems) {
if (nItems == 0u) return cudaErrorInvalidValue;
uint32_t block = 256u;
uint32_t grid = (nItems + block - 1u) / block;
igneum_build<<<grid, block>>>(ds, cache, nItems);
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);
}

View file

@ -0,0 +1,372 @@
// Generated by igneum-pow export (generator v2) for seed "igneum-readwidth/A/0". Do not edit by hand.
// OpenCL C twin of the Metal kernel for the same seed (see proto-opencl/README.md, WAVEFRONT.md and program.metal).
// Built from source at runtime by proto-opencl/host.c, which passes these defines:
// IGNEUM_GROUP work-group size of igneum_hash, a multiple of 32 (default 32: one work-group = one 32-lane unit)
// IGNEUM_EXCHANGE 0 = local-memory exchange with a barrier (any device, any wave width; the default)
// 1 = sub_group_shuffle_xor (cl_khr_subgroup_shuffle), only with IGNEUM_GROUP 32 and a sub-group size of exactly 32
// 2 = intel_sub_group_shuffle_xor (cl_intel_subgroups), same condition
// The verification unit is always 32 lanes. A 64-wide hardware wave (AMD GCN/CDNA, RDNA in wave64) runs two units;
// the exchange masks are 1, 2, 4, 8, 16, so every partner lane lies inside the lane's own aligned run of 32.
#ifndef IGNEUM_GROUP
#define IGNEUM_GROUP 32
#endif
#ifndef IGNEUM_EXCHANGE
#define IGNEUM_EXCHANGE 0
#endif
#ifdef __OPENCL_VERSION__
#define IGNEUM_KERNEL_HASH __kernel __attribute__((reqd_work_group_size(IGNEUM_GROUP, 1, 1)))
#define IGNEUM_LOCAL_WORDS(name, n) __local uint name[n]
#if IGNEUM_EXCHANGE == 1
#ifdef cl_khr_subgroups
#pragma OPENCL EXTENSION cl_khr_subgroups : enable
#endif
#ifdef cl_khr_subgroup_shuffle
#pragma OPENCL EXTENSION cl_khr_subgroup_shuffle : enable
#endif
#elif IGNEUM_EXCHANGE == 2
#pragma OPENCL EXTENSION cl_intel_subgroups : enable
#endif
#else
// Not an OpenCL compiler: proto-opencl/emu compiles this file as C++ and supplies the built-ins and these two macros.
#include "emu_opencl.h"
#endif
#if IGNEUM_EXCHANGE == 1
#define IGNEUM_SHFL_XOR(dst, a, m) dst = sub_group_shuffle_xor((a), (uint)(m))
#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u)
#elif IGNEUM_EXCHANGE == 2
#define IGNEUM_SHFL_XOR(dst, a, m) dst = intel_sub_group_shuffle_xor((a), (uint)(m))
#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u)
#else
// Local-memory exchange. Two buffers of IGNEUM_GROUP words alternate (xk counts exchanges), so one barrier per
// exchange is enough: a lane can only overwrite buffer b at exchange k+2 after passing barrier k+1, and every lane
// reaches barrier k+1 only after its read of buffer b at exchange k. The partner lid ^ m stays inside the lane's
// aligned run of 32 because m < 32. Control flow is uniform, so every work-item reaches every barrier.
#define IGNEUM_SHFL_XOR(dst, a, m) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid ^ (uint)(m))]; xk += 1u; }
#define IGNEUM_BCAST0(dst, a) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid & ~31u)]; xk += 1u; }
#endif
static inline uint splitmix32(uint 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. OpenCL rotate() rotates left by n modulo 32.
static inline uint rotl_imm(uint x, uint n) { return rotate(x, n); }
// Right rotation by n modulo 32 as a left rotation by (32 - n) modulo 32; n == 0 gives x.
static inline uint rotr_var(uint x, uint n) { return rotate(x, (0u - n) & 31u); }
static 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;
}
// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines.
// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals.
#define MH_CACHE_LINE_MASK 0x003fffffu
#define MH_SEGMENT_LINES 64u
#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); }
static inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site
// y = ChaCha12 core(x) + x
static inline void mh_chacha_block(const uint* x, uint* y) {
for (uint i = 0u; i < 16u; ++i) y[i] = x[i];
for (uint r = 0u; r < 6u; ++r) {
MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u)
MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u)
}
for (uint i = 0u; i < 16u; ++i) y[i] += x[i];
}
// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0.
static inline void mh_cache_segment(__global uint* cache, uint seg) {
uint prev[16]; uint x[16]; uint y[16];
for (uint i = 0u; i < 16u; ++i) prev[i] = 0u;
for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) {
x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3];
x[4] = 0x3067619fu ^ prev[4];
x[5] = 0x3c269176u ^ prev[5];
x[6] = 0x84a03b03u ^ prev[6];
x[7] = 0xf8c63294u ^ prev[7];
x[8] = 0xff977c5bu ^ prev[8];
x[9] = 0xe60def3eu ^ prev[9];
x[10] = 0x63630141u ^ prev[10];
x[11] = 0xb8fbcb58u ^ prev[11];
x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15];
mh_chacha_block(x, y);
__global uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u);
for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; }
}
}
// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations.
static inline void mh_mixer(uint* s, uint rk) {
s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u;
s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu;
s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u;
s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu;
s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u;
s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u;
s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u;
s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u;
s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du;
s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u;
s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du;
s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu;
s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du;
s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu;
s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u;
s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u;
MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u)
MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u)
MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u)
MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u)
}
// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer.
static inline void mh_item(__global const uint* cache, uint t, uint* s) {
s[0] = 0x3067619fu;
s[1] = 0x3c269176u;
s[2] = 0x84a03b03u;
s[3] = 0xf8c63294u;
s[4] = 0xff977c5bu;
s[5] = 0xe60def3eu;
s[6] = 0x63630141u;
s[7] = 0xb8fbcb58u;
s[8] = t * 0x42146205u + 0xbab68293u;
s[9] = t * 0x52cbe0fbu + 0xcc162340u;
s[10] = t * 0x7ecf4a03u + 0x6ce151ccu;
s[11] = t * 0x6728907fu + 0xe62b8997u;
s[12] = t * 0xd81d9751u + 0xc9c80297u;
s[13] = t * 0x132952c3u + 0xf74a1654u;
s[14] = t * 0xf60de277u + 0x3d704af5u;
s[15] = t * 0x05358035u + 0x3cf522b7u;
for (uint r = 0u; r < 8u; ++r) {
mh_mixer(s, 0x9E3779B9u * (r + 1u));
__global const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u);
for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i];
}
mh_mixer(s, 0x9E3779B9u * 9u);
}
// dataset[w] without the dataset: derive item w >> 4 and take word w & 15.
static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; }
// Memory-hard dataset (MEMHARD.md). One work-item per cache segment; one work-item per 64-byte dataset item.
// The same constants as memhard.h in this pack (one emitter, three dialects).
__kernel void igneum_cache_fill(__global uint* cache, uint nSegments) {
uint seg = (uint)get_global_id(0);
if (seg < nSegments) mh_cache_segment(cache, seg);
}
__kernel void igneum_build(__global uint* ds, __global const uint* cache, uint nItems) {
uint t = (uint)get_global_id(0);
if (t < nItems) {
uint s[16];
mh_item(cache, t, s);
__global uint* d = ds + ((ulong)t * 16u);
for (uint i = 0u; i < 16u; ++i) d[i] = s[i];
}
}
// One hash per work-item. IGNEUM_GROUP is a multiple of 32; lane = lid & 31 and every exchange stays inside the
// lane's own aligned run of 32 work-items, exactly like simd_shuffle_xor inside a 32-wide Metal SIMD group and
// __shfl_xor_sync inside a CUDA warp. Control flow is uniform (no branches at all).
IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask) {
uint gid = (uint)get_global_id(0);
uint lid = (uint)get_local_id(0);
uint nonce = baseNonce + gid;
uint r0, r1, r2, r3, r4, r5, r6, r7;
#if IGNEUM_EXCHANGE == 0
IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP);
uint xk = 0u;
#else
(void)lid;
#endif
{ uint x = nonce ^ 0xaa5a3f6eu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x5c0410c3u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1]
{ uint x = nonce ^ 0x5c0410c3u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0x9d994375u; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2]
{ uint x = nonce ^ 0x9d994375u; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0xd8d53386u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3]
{ uint x = nonce ^ 0xd8d53386u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0x2c956ee2u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4]
{ uint x = nonce ^ 0x2c956ee2u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xe313c2f9u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5]
{ uint x = nonce ^ 0xe313c2f9u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0x495ace1bu; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6]
{ uint x = nonce ^ 0x495ace1bu; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0x8238bb25u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7]
{ uint x = nonce ^ 0x8238bb25u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0xaa5a3f6eu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0]
for (uint it = 0u; it < 8u; ++it) {
uint sel = r0;
r2 = r2 + r7 + ((((sel >> 30u) & 1u) != 0u) ? 0xb788d5f8u : 0x92d00116u); // 0 add
r3 = mul_hi(r3, r2); // 1 mulhi
r6 = r6 ^ ds[r3 & mask]; // 2 load
r0 = r0 ^ ds[r6 & mask]; // 3 load
r5 = r5 * r2; // 4 mul
r7 = r7 + r2 + ((((sel >> 20u) & 1u) != 0u) ? 0x9a773a55u : 0xd5f6f389u); // 5 add
{ uint b_ = (r5 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r6 = x_; } // 6 load
r5 = r5 ^ ds[r6 & mask]; // 7 load
{ uint b_ = (r2 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r1 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r1 = x_; } // 8 load
r4 = r4 * r5; // 9 mul
{ uint b_ = (r0 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r2 = x_; } // 10 load
r3 = r3 ^ ds[r2 & mask]; // 11 load
r4 = r4 ^ r0; // 12 xor
r7 = r7 ^ ds[r3 & mask]; // 13 load
r7 = r7 ^ r1; // 14 xor
r4 = r1 * r4 + r4; // 15 mad
r3 = r0 * r4 + r3; // 16 mad
r4 = r6 * r2 + r4; // 17 mad
r0 = rotr_var(r0, r2); // 18 rotr
r5 = rotr_var(r5, r6); // 19 rotr
r4 = r4 ^ ds[r1 & mask]; // 20 load
r2 = r2 ^ ds[r4 & mask]; // 21 load
r1 = rotl_imm(r1, 14u); // 22 rotl
r7 = r7 - r1; // 23 sub
{ uint t_; IGNEUM_SHFL_XOR(t_, r6, 2u); r2 = r2 ^ t_; } // 24 shfl
{ uint t_; IGNEUM_SHFL_XOR(t_, r4, 16u); r1 = r1 ^ t_; } // 25 shfl
r6 = r6 ^ ds[r0 & mask]; // 26 load
{ uint t_; IGNEUM_SHFL_XOR(t_, r6, 1u); r0 = r0 ^ t_; } // 27 shfl
r0 = r0 + r2 + ((((sel >> 25u) & 1u) != 0u) ? 0xc6311db1u : 0x1a3cecfbu); // 28 add
r5 = mul_hi(r5, r7); // 29 mulhi
r0 = r1 * r7 + r0; // 30 mad
r5 = rotl_imm(r5, 23u); // 31 rotl
r0 = r0 + r2 + ((((sel >> 28u) & 1u) != 0u) ? 0xf1282553u : 0x7aa05e39u); // 32 add
r1 = r1 ^ r6; // 33 xor
r0 = r0 - r1; // 34 sub
r1 = r1 ^ ds[r6 & mask]; // 35 load
r1 = rotl_imm(r1, 7u); // 36 rotl
{ uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r0 = r0 ^ t_; } // 37 shfl
r3 = r3 + r5 + ((((sel >> 13u) & 1u) != 0u) ? 0x70173cfdu : 0x4a9bf1a8u); // 38 add
r3 = mul_hi(r3, r4); // 39 mulhi
r3 = r3 + r6 + ((((sel >> 8u) & 1u) != 0u) ? 0x2596fd35u : 0x26b3e1a7u); // 40 add
r3 = r3 - r7; // 41 sub
r0 = r0 - r4; // 42 sub
r5 = rotr_var(r5, r7); // 43 rotr
r3 = r4 * r1 + r3; // 44 mad
r4 = r4 ^ ds[r2 & mask]; // 45 load
r2 = mul_hi(r2, r5); // 46 mulhi
r3 = r3 ^ ds[r5 & mask]; // 47 load
r5 = mul_hi(r5, r6); // 48 mulhi
r5 = r5 + r7 + ((((sel >> 22u) & 1u) != 0u) ? 0x724f77b9u : 0x9f31d23cu); // 49 add
{ uint t_; IGNEUM_SHFL_XOR(t_, r7, 1u); r2 = r2 ^ t_; } // 50 shfl
r1 = r1 * r5; // 51 mul
r1 = r1 * r6; // 52 mul
{ uint b_ = (r1 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r6 = x_; } // 53 load
r7 = r7 ^ r2; // 54 xor
r2 = r2 - r1; // 55 sub
r7 = r7 ^ r3; // 56 xor
r3 = rotl_imm(r3, 2u); // 57 rotl
r6 = mul_hi(r6, r4); // 58 mulhi
r4 = rotr_var(r4, r5); // 59 rotr
r3 = mul_hi(r3, r1); // 60 mulhi
r3 = r3 ^ r6; // 61 xor
{ uint t_; IGNEUM_SHFL_XOR(t_, r1, 16u); r5 = r5 ^ t_; } // 62 shfl
r1 = r1 ^ ds[r6 & mask]; // 63 load
}
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;
}
#if IGNEUM_EXCHANGE != 0
// Reports the sub-group size this device uses for a work-group of IGNEUM_GROUP items. host.c runs it only when the
// per-kernel query (clGetKernelSubGroupInfoKHR on igneum_hash) is unavailable; that query is preferred because a
// compiler may pick a different wave width per kernel (RDNA: wave32 or wave64). See WAVEFRONT.md.
IGNEUM_KERNEL_HASH void igneum_probe_subgroup(__global uint* out) {
if (get_local_id(0) == 0u) { out[0] = get_sub_group_size(); out[1] = get_num_sub_groups(); }
}
#endif
// Header-bound variant (bind.rs): the init words come from initw, not SEEDW. Same body as igneum_hash.
IGNEUM_KERNEL_HASH void igneum_hash_bound(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask, __global const uint* initw) {
uint gid = (uint)get_global_id(0);
uint lid = (uint)get_local_id(0);
uint nonce = baseNonce + gid;
uint r0, r1, r2, r3, r4, r5, r6, r7;
uint iw0 = initw[0], iw1 = initw[1], iw2 = initw[2], iw3 = initw[3], iw4 = initw[4], iw5 = initw[5], iw6 = initw[6], iw7 = initw[7];
#if IGNEUM_EXCHANGE == 0
IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP);
uint xk = 0u;
#else
(void)lid;
#endif
{ uint x = nonce ^ iw0; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ iw1; }
{ uint x = nonce ^ iw1; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ iw2; }
{ uint x = nonce ^ iw2; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ iw3; }
{ uint x = nonce ^ iw3; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ iw4; }
{ uint x = nonce ^ iw4; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ iw5; }
{ uint x = nonce ^ iw5; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ iw6; }
{ uint x = nonce ^ iw6; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ iw7; }
{ uint x = nonce ^ iw7; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ iw0; }
for (uint it = 0u; it < 8u; ++it) {
uint sel = r0;
r2 = r2 + r7 + ((((sel >> 30u) & 1u) != 0u) ? 0xb788d5f8u : 0x92d00116u); // 0 add
r3 = mul_hi(r3, r2); // 1 mulhi
r6 = r6 ^ ds[r3 & mask]; // 2 load
r0 = r0 ^ ds[r6 & mask]; // 3 load
r5 = r5 * r2; // 4 mul
r7 = r7 + r2 + ((((sel >> 20u) & 1u) != 0u) ? 0x9a773a55u : 0xd5f6f389u); // 5 add
{ uint b_ = (r5 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r6 = x_; } // 6 load
r5 = r5 ^ ds[r6 & mask]; // 7 load
{ uint b_ = (r2 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r1 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r1 = x_; } // 8 load
r4 = r4 * r5; // 9 mul
{ uint b_ = (r0 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r2 = x_; } // 10 load
r3 = r3 ^ ds[r2 & mask]; // 11 load
r4 = r4 ^ r0; // 12 xor
r7 = r7 ^ ds[r3 & mask]; // 13 load
r7 = r7 ^ r1; // 14 xor
r4 = r1 * r4 + r4; // 15 mad
r3 = r0 * r4 + r3; // 16 mad
r4 = r6 * r2 + r4; // 17 mad
r0 = rotr_var(r0, r2); // 18 rotr
r5 = rotr_var(r5, r6); // 19 rotr
r4 = r4 ^ ds[r1 & mask]; // 20 load
r2 = r2 ^ ds[r4 & mask]; // 21 load
r1 = rotl_imm(r1, 14u); // 22 rotl
r7 = r7 - r1; // 23 sub
{ uint t_; IGNEUM_SHFL_XOR(t_, r6, 2u); r2 = r2 ^ t_; } // 24 shfl
{ uint t_; IGNEUM_SHFL_XOR(t_, r4, 16u); r1 = r1 ^ t_; } // 25 shfl
r6 = r6 ^ ds[r0 & mask]; // 26 load
{ uint t_; IGNEUM_SHFL_XOR(t_, r6, 1u); r0 = r0 ^ t_; } // 27 shfl
r0 = r0 + r2 + ((((sel >> 25u) & 1u) != 0u) ? 0xc6311db1u : 0x1a3cecfbu); // 28 add
r5 = mul_hi(r5, r7); // 29 mulhi
r0 = r1 * r7 + r0; // 30 mad
r5 = rotl_imm(r5, 23u); // 31 rotl
r0 = r0 + r2 + ((((sel >> 28u) & 1u) != 0u) ? 0xf1282553u : 0x7aa05e39u); // 32 add
r1 = r1 ^ r6; // 33 xor
r0 = r0 - r1; // 34 sub
r1 = r1 ^ ds[r6 & mask]; // 35 load
r1 = rotl_imm(r1, 7u); // 36 rotl
{ uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r0 = r0 ^ t_; } // 37 shfl
r3 = r3 + r5 + ((((sel >> 13u) & 1u) != 0u) ? 0x70173cfdu : 0x4a9bf1a8u); // 38 add
r3 = mul_hi(r3, r4); // 39 mulhi
r3 = r3 + r6 + ((((sel >> 8u) & 1u) != 0u) ? 0x2596fd35u : 0x26b3e1a7u); // 40 add
r3 = r3 - r7; // 41 sub
r0 = r0 - r4; // 42 sub
r5 = rotr_var(r5, r7); // 43 rotr
r3 = r4 * r1 + r3; // 44 mad
r4 = r4 ^ ds[r2 & mask]; // 45 load
r2 = mul_hi(r2, r5); // 46 mulhi
r3 = r3 ^ ds[r5 & mask]; // 47 load
r5 = mul_hi(r5, r6); // 48 mulhi
r5 = r5 + r7 + ((((sel >> 22u) & 1u) != 0u) ? 0x724f77b9u : 0x9f31d23cu); // 49 add
{ uint t_; IGNEUM_SHFL_XOR(t_, r7, 1u); r2 = r2 ^ t_; } // 50 shfl
r1 = r1 * r5; // 51 mul
r1 = r1 * r6; // 52 mul
{ uint b_ = (r1 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r6 = x_; } // 53 load
r7 = r7 ^ r2; // 54 xor
r2 = r2 - r1; // 55 sub
r7 = r7 ^ r3; // 56 xor
r3 = rotl_imm(r3, 2u); // 57 rotl
r6 = mul_hi(r6, r4); // 58 mulhi
r4 = rotr_var(r4, r5); // 59 rotr
r3 = mul_hi(r3, r1); // 60 mulhi
r3 = r3 ^ r6; // 61 xor
{ uint t_; IGNEUM_SHFL_XOR(t_, r1, 16u); r5 = r5 ^ t_; } // 62 shfl
r1 = r1 ^ ds[r6 & mask]; // 63 load
}
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;
}

View file

@ -0,0 +1,123 @@
// Generated by igneum-pow export (generator v2) for seed "igneum-readwidth/A/0". Do not edit by hand.
// Header-bound twin of igneum_hash in kernel.cu: the init words come from a kernel argument, not SEEDW.
// Host declarations (also in program_bound.h if present):
// struct IgneumInitWords { uint32_t w[8]; };
// cudaError_t igneum_launch_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask,
// IgneumInitWords iw, uint32_t nonces, uint32_t blockWarps);
// cudaError_t igneum_hash_bound_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps);
#include <cuda_runtime.h>
#include <cstdint>
#include "program.h"
struct IgneumInitWords { uint32_t w[8]; };
__device__ __forceinline__ uint32_t splitmix32(uint32_t x) {
x ^= x >> 16; x *= 0x7feb352du;
x ^= x >> 15; x *= 0x846ca68bu;
x ^= x >> 16;
return x;
}
__device__ __forceinline__ uint32_t rotl_imm(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); }
__device__ __forceinline__ uint32_t rotr_var(uint32_t x, uint32_t n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); }
__global__ void igneum_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, IgneumInitWords iw) {
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 ^ iw.w[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ iw.w[1]; }
{ uint32_t x = nonce ^ iw.w[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ iw.w[2]; }
{ uint32_t x = nonce ^ iw.w[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ iw.w[3]; }
{ uint32_t x = nonce ^ iw.w[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ iw.w[4]; }
{ uint32_t x = nonce ^ iw.w[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ iw.w[5]; }
{ uint32_t x = nonce ^ iw.w[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ iw.w[6]; }
{ uint32_t x = nonce ^ iw.w[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ iw.w[7]; }
{ uint32_t x = nonce ^ iw.w[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ iw.w[0]; }
for (uint32_t it = 0u; it < 8u; ++it) {
uint32_t sel = r0;
r2 = r2 + r7 + ((((sel >> 30u) & 1u) != 0u) ? 0xb788d5f8u : 0x92d00116u); // 0 add
r3 = __umulhi(r3, r2); // 1 mulhi
r6 = r6 ^ ds[r3 & mask]; // 2 load
r0 = r0 ^ ds[r6 & mask]; // 3 load
r5 = r5 * r2; // 4 mul
r7 = r7 + r2 + ((((sel >> 20u) & 1u) != 0u) ? 0x9a773a55u : 0xd5f6f389u); // 5 add
{ uint32_t b_ = (r5 & mask) & ~15u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint32_t x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r6 = x_; } // 6 load
r5 = r5 ^ ds[r6 & mask]; // 7 load
{ uint32_t b_ = (r2 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r1 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r1 = x_; } // 8 load
r4 = r4 * r5; // 9 mul
{ uint32_t b_ = (r0 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r2 = x_; } // 10 load
r3 = r3 ^ ds[r2 & mask]; // 11 load
r4 = r4 ^ r0; // 12 xor
r7 = r7 ^ ds[r3 & mask]; // 13 load
r7 = r7 ^ r1; // 14 xor
r4 = r1 * r4 + r4; // 15 mad
r3 = r0 * r4 + r3; // 16 mad
r4 = r6 * r2 + r4; // 17 mad
r0 = rotr_var(r0, r2); // 18 rotr
r5 = rotr_var(r5, r6); // 19 rotr
r4 = r4 ^ ds[r1 & mask]; // 20 load
r2 = r2 ^ ds[r4 & mask]; // 21 load
r1 = rotl_imm(r1, 14u); // 22 rotl
r7 = r7 - r1; // 23 sub
r2 = r2 ^ __shfl_xor_sync(0xffffffffu, r6, 2); // 24 shfl
r1 = r1 ^ __shfl_xor_sync(0xffffffffu, r4, 16); // 25 shfl
r6 = r6 ^ ds[r0 & mask]; // 26 load
r0 = r0 ^ __shfl_xor_sync(0xffffffffu, r6, 1); // 27 shfl
r0 = r0 + r2 + ((((sel >> 25u) & 1u) != 0u) ? 0xc6311db1u : 0x1a3cecfbu); // 28 add
r5 = __umulhi(r5, r7); // 29 mulhi
r0 = r1 * r7 + r0; // 30 mad
r5 = rotl_imm(r5, 23u); // 31 rotl
r0 = r0 + r2 + ((((sel >> 28u) & 1u) != 0u) ? 0xf1282553u : 0x7aa05e39u); // 32 add
r1 = r1 ^ r6; // 33 xor
r0 = r0 - r1; // 34 sub
r1 = r1 ^ ds[r6 & mask]; // 35 load
r1 = rotl_imm(r1, 7u); // 36 rotl
r0 = r0 ^ __shfl_xor_sync(0xffffffffu, r4, 8); // 37 shfl
r3 = r3 + r5 + ((((sel >> 13u) & 1u) != 0u) ? 0x70173cfdu : 0x4a9bf1a8u); // 38 add
r3 = __umulhi(r3, r4); // 39 mulhi
r3 = r3 + r6 + ((((sel >> 8u) & 1u) != 0u) ? 0x2596fd35u : 0x26b3e1a7u); // 40 add
r3 = r3 - r7; // 41 sub
r0 = r0 - r4; // 42 sub
r5 = rotr_var(r5, r7); // 43 rotr
r3 = r4 * r1 + r3; // 44 mad
r4 = r4 ^ ds[r2 & mask]; // 45 load
r2 = __umulhi(r2, r5); // 46 mulhi
r3 = r3 ^ ds[r5 & mask]; // 47 load
r5 = __umulhi(r5, r6); // 48 mulhi
r5 = r5 + r7 + ((((sel >> 22u) & 1u) != 0u) ? 0x724f77b9u : 0x9f31d23cu); // 49 add
r2 = r2 ^ __shfl_xor_sync(0xffffffffu, r7, 1); // 50 shfl
r1 = r1 * r5; // 51 mul
r1 = r1 * r6; // 52 mul
{ uint32_t b_ = (r1 & mask) & ~15u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint32_t x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r6 = x_; } // 53 load
r7 = r7 ^ r2; // 54 xor
r2 = r2 - r1; // 55 sub
r7 = r7 ^ r3; // 56 xor
r3 = rotl_imm(r3, 2u); // 57 rotl
r6 = __umulhi(r6, r4); // 58 mulhi
r4 = rotr_var(r4, r5); // 59 rotr
r3 = __umulhi(r3, r1); // 60 mulhi
r3 = r3 ^ r6; // 61 xor
r5 = r5 ^ __shfl_xor_sync(0xffffffffu, r1, 16); // 62 shfl
r1 = r1 ^ ds[r6 & mask]; // 63 load
}
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;
}
cudaError_t igneum_launch_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask,
IgneumInitWords iw, 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_bound<<<nonces / block, block>>>(ds, out, baseNonce, mask, iw);
return cudaGetLastError();
}
cudaError_t igneum_hash_bound_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps) {
cudaFuncAttributes attr;
cudaError_t e = cudaFuncGetAttributes(&attr, igneum_hash_bound);
if (e != cudaSuccess) return e;
*numRegs = attr.numRegs;
return cudaOccupancyMaxActiveBlocksPerMultiprocessor(blocksPerSM, igneum_hash_bound, (int)(32u * blockWarps), 0);
}

View file

@ -0,0 +1,108 @@
// Generated by igneum-pow export (generator v2) for seed "igneum-readwidth/A/0". Do not edit by hand.
// Memory-hard dataset core, the same text that the Mac's Metal kernels and CPU verifier were checked against.
// Included by kernel.cu (device), host.cu (host reference) and proto-opencl/host.c (C99 host reference).
// See proto-metal/MEMHARD.md for the construction. kernel.cl carries the same text in OpenCL C.
#pragma once
#ifdef __cplusplus
#include <cstdint>
#else
#include <stdint.h>
#endif
#if defined(__CUDACC__)
#define IGNEUM_HD __host__ __device__ __forceinline__
#elif defined(_MSC_VER) && !defined(__cplusplus)
#define IGNEUM_HD static __inline
#else
#define IGNEUM_HD static inline
#endif
// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines.
// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals.
#define MH_CACHE_LINE_MASK 0x003fffffu
#define MH_SEGMENT_LINES 64u
#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); }
IGNEUM_HD uint32_t mh_rotl(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site
// y = ChaCha12 core(x) + x
IGNEUM_HD void mh_chacha_block(const uint32_t* x, uint32_t* y) {
for (uint32_t i = 0u; i < 16u; ++i) y[i] = x[i];
for (uint32_t r = 0u; r < 6u; ++r) {
MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u)
MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u)
}
for (uint32_t i = 0u; i < 16u; ++i) y[i] += x[i];
}
// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0.
IGNEUM_HD void mh_cache_segment(uint32_t* cache, uint32_t seg) {
uint32_t prev[16]; uint32_t x[16]; uint32_t y[16];
for (uint32_t i = 0u; i < 16u; ++i) prev[i] = 0u;
for (uint32_t j = 0u; j < MH_SEGMENT_LINES; ++j) {
x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3];
x[4] = 0x3067619fu ^ prev[4];
x[5] = 0x3c269176u ^ prev[5];
x[6] = 0x84a03b03u ^ prev[6];
x[7] = 0xf8c63294u ^ prev[7];
x[8] = 0xff977c5bu ^ prev[8];
x[9] = 0xe60def3eu ^ prev[9];
x[10] = 0x63630141u ^ prev[10];
x[11] = 0xb8fbcb58u ^ prev[11];
x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15];
mh_chacha_block(x, y);
uint32_t* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u);
for (uint32_t i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; }
}
}
// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations.
IGNEUM_HD void mh_mixer(uint32_t* s, uint32_t rk) {
s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u;
s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu;
s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u;
s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu;
s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u;
s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u;
s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u;
s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u;
s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du;
s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u;
s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du;
s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu;
s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du;
s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu;
s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u;
s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u;
MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u)
MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u)
MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u)
MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u)
}
// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer.
IGNEUM_HD void mh_item(const uint32_t* cache, uint32_t t, uint32_t* s) {
s[0] = 0x3067619fu;
s[1] = 0x3c269176u;
s[2] = 0x84a03b03u;
s[3] = 0xf8c63294u;
s[4] = 0xff977c5bu;
s[5] = 0xe60def3eu;
s[6] = 0x63630141u;
s[7] = 0xb8fbcb58u;
s[8] = t * 0x42146205u + 0xbab68293u;
s[9] = t * 0x52cbe0fbu + 0xcc162340u;
s[10] = t * 0x7ecf4a03u + 0x6ce151ccu;
s[11] = t * 0x6728907fu + 0xe62b8997u;
s[12] = t * 0xd81d9751u + 0xc9c80297u;
s[13] = t * 0x132952c3u + 0xf74a1654u;
s[14] = t * 0xf60de277u + 0x3d704af5u;
s[15] = t * 0x05358035u + 0x3cf522b7u;
for (uint32_t r = 0u; r < 8u; ++r) {
mh_mixer(s, 0x9E3779B9u * (r + 1u));
const uint32_t* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u);
for (uint32_t i = 0u; i < 16u; ++i) s[i] ^= line[i];
}
mh_mixer(s, 0x9E3779B9u * 9u);
}
// dataset[w] without the dataset: derive item w >> 4 and take word w & 15.
IGNEUM_HD uint32_t mh_word(const uint32_t* cache, uint32_t w) { uint32_t s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; }

View file

@ -0,0 +1,106 @@
#include <metal_stdlib>
using namespace metal;
// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines.
// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals.
#define MH_CACHE_LINE_MASK 0x003fffffu
#define MH_SEGMENT_LINES 64u
#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); }
inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site
// y = ChaCha12 core(x) + x
inline void mh_chacha_block(const thread uint* x, thread uint* y) {
for (uint i = 0u; i < 16u; ++i) y[i] = x[i];
for (uint r = 0u; r < 6u; ++r) {
MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u)
MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u)
}
for (uint i = 0u; i < 16u; ++i) y[i] += x[i];
}
// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0.
inline void mh_cache_segment(device uint* cache, uint seg) {
uint prev[16]; uint x[16]; uint y[16];
for (uint i = 0u; i < 16u; ++i) prev[i] = 0u;
for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) {
x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3];
x[4] = 0x3067619fu ^ prev[4];
x[5] = 0x3c269176u ^ prev[5];
x[6] = 0x84a03b03u ^ prev[6];
x[7] = 0xf8c63294u ^ prev[7];
x[8] = 0xff977c5bu ^ prev[8];
x[9] = 0xe60def3eu ^ prev[9];
x[10] = 0x63630141u ^ prev[10];
x[11] = 0xb8fbcb58u ^ prev[11];
x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15];
mh_chacha_block(x, y);
device uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u);
for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; }
}
}
// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations.
inline void mh_mixer(thread uint* s, uint rk) {
s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u;
s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu;
s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u;
s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu;
s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u;
s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u;
s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u;
s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u;
s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du;
s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u;
s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du;
s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu;
s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du;
s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu;
s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u;
s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u;
MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u)
MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u)
MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u)
MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u)
}
// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer.
inline void mh_item(device const uint* cache, uint t, thread uint* s) {
s[0] = 0x3067619fu;
s[1] = 0x3c269176u;
s[2] = 0x84a03b03u;
s[3] = 0xf8c63294u;
s[4] = 0xff977c5bu;
s[5] = 0xe60def3eu;
s[6] = 0x63630141u;
s[7] = 0xb8fbcb58u;
s[8] = t * 0x42146205u + 0xbab68293u;
s[9] = t * 0x52cbe0fbu + 0xcc162340u;
s[10] = t * 0x7ecf4a03u + 0x6ce151ccu;
s[11] = t * 0x6728907fu + 0xe62b8997u;
s[12] = t * 0xd81d9751u + 0xc9c80297u;
s[13] = t * 0x132952c3u + 0xf74a1654u;
s[14] = t * 0xf60de277u + 0x3d704af5u;
s[15] = t * 0x05358035u + 0x3cf522b7u;
for (uint r = 0u; r < 8u; ++r) {
mh_mixer(s, 0x9E3779B9u * (r + 1u));
device const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u);
for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i];
}
mh_mixer(s, 0x9E3779B9u * 9u);
}
// dataset[w] without the dataset: derive item w >> 4 and take word w & 15.
inline uint mh_word(device const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; }
// One thread per segment (2^16 threads).
kernel void igneum_cache_fill(device uint* cache [[buffer(0)]], uint gid [[thread_position_in_grid]]) {
mh_cache_segment(cache, gid);
}
// One thread per 64-byte item (dataset words / 16 threads).
kernel void igneum_build(device const uint* cache [[buffer(0)]], device uint* dataset [[buffer(1)]],
uint gid [[thread_position_in_grid]]) {
uint s[16];
mh_item(cache, gid, s);
device uint* d = dataset + gid * 16u;
for (uint i = 0u; i < 16u; ++i) d[i] = s[i];
}

View file

@ -0,0 +1,60 @@
// Generated by igneum-pow export (generator v2) for seed "igneum-readwidth/A/0". Do not edit by hand.
// Program metadata for host.cu plus the launch wrappers defined in kernel.cu.
// Also included by proto-opencl/host.c (C99), which defines IGNEUM_NO_CUDA first and reads only the macros.
#pragma once
#ifdef __cplusplus
#include <cstdint>
#else
#include <stdint.h>
#endif
#ifndef IGNEUM_NO_CUDA
#include <cuda_runtime.h>
#endif
#define IGNEUM_SEED_STRING "igneum-readwidth/A/0"
#define IGNEUM_SEED_BYTES_HEX "69676e65756d2d7265616477696474682f412f30"
#define IGNEUM_GENERATOR 2
#define IGNEUM_PROGRAM_ATTEMPT 0
#define IGNEUM_PROGRAM_ID 0xd30baa94fa82322eull
#define IGNEUM_DAY_STRING "2026-10-03"
#define IGNEUM_DAY_BYTES_HEX "6461792f323032362d31302d3033"
#define IGNEUM_DAY0 0x3067619fu
#define IGNEUM_DAY1 0x3c269176u
#define IGNEUM_DATASET_LOG2 28
#define IGNEUM_MASK 0x0fffffffu
#define IGNEUM_LANES 32
#define IGNEUM_ITERATIONS 8
#define IGNEUM_INSTR_COUNT 64
#define IGNEUM_LOADS_PER_HASH 128
#define IGNEUM_WIDE_LOADS_PER_HASH 0
#define IGNEUM_OP_MIX "load=16 add=7 mulhi=7 shfl=6 xor=6 mad=5 sub=5 mul=4 rotl=4 rotr=4"
// Read-width experiment (5 October 2026, docs/plans/read-width.md): NOT the lottery hash. A load of W words reads
// the W-word-aligned address and folds every word into dst: x = dst ^ w[0]; x = (rotl(x, 11) * 0x9e3779b1) ^ w[j]; dst = x.
#define IGNEUM_LOAD_CLASS "mix50-35-15"
#define IGNEUM_LOAD_SLOTS 16
#define IGNEUM_LOAD_MIX { 50, 35, 15 }
#define IGNEUM_LOAD_WIDTH_COUNTS { 12, 2, 2 } // loads of 4, 16, 64 bytes per program
#define IGNEUM_BYTES_PER_HASH 1664
#define IGNEUM_FOLD_ROT 11
#define IGNEUM_FOLD_MUL 0x9e3779b1u
// 0 = closed-form dataset (ds_elem), 1 = memory-hard cache construction (MEMHARD.md, memhard.h)
#define IGNEUM_DATASET_MODE 1
#define IGNEUM_SEEDW_INIT { 0xaa5a3f6eu, 0x5c0410c3u, 0x9d994375u, 0xd8d53386u, 0x2c956ee2u, 0xe313c2f9u, 0x495ace1bu, 0x8238bb25u }
#define IGNEUM_KEY_INIT { 0x3067619fu, 0x3c269176u, 0x84a03b03u, 0xf8c63294u, 0xff977c5bu, 0xe60def3eu, 0x63630141u, 0xb8fbcb58u }
#define IGNEUM_CACHE_LOG2_WORDS 26
#define IGNEUM_CACHE_SEGMENT_LOG2_LINES 6
#define IGNEUM_CACHE_SEGMENTS 65536u
#define IGNEUM_ITEM_ROUNDS 8
#define IGNEUM_MIX_ROT_INIT { 20u, 20u, 19u, 4u, 26u, 3u, 3u, 27u }
#define IGNEUM_MIX_MUL_INIT { 0x42146205u, 0x52cbe0fbu, 0x7ecf4a03u, 0x6728907fu, 0xd81d9751u, 0x132952c3u, 0xf60de277u, 0x05358035u, 0xbaf6499du, 0xe4db9667u, 0x3e98f45du, 0xd0004eddu, 0x2691630du, 0x9beb3bcfu, 0xab310379u, 0x99cfb423u }
#define IGNEUM_MIX_RC_INIT { 0xbab68293u, 0xcc162340u, 0x6ce151ccu, 0xe62b8997u, 0xc9c80297u, 0xf74a1654u, 0x3d704af5u, 0x3cf522b7u, 0x2b9cac04u, 0xa880ac10u, 0x13e5dd1du, 0x6fc3e233u, 0x2d83eeacu, 0x9006e8bfu, 0x2c4b5362u, 0x31b49ee2u }
#ifndef IGNEUM_NO_CUDA
// Defined in kernel.cu. All launch on the default stream and return cudaGetLastError().
cudaError_t igneum_launch_cache_fill(uint32_t* cache, uint32_t nSegments);
cudaError_t igneum_launch_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems);
cudaError_t igneum_launch_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask,
uint32_t nonces, uint32_t blockWarps);
cudaError_t igneum_hash_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps);
#endif

View file

@ -0,0 +1,127 @@
{
"format": "igneum-program-pack-3",
"generator": 2,
"attempt": 0,
"program_id": "0xd30baa94fa82322e",
"program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32",
"dataset_mode": "memory-hard",
"seed": "igneum-readwidth/A/0",
"seed_bytes": "69676e65756d2d7265616477696474682f412f30",
"seed_words": ["0xaa5a3f6e", "0x5c0410c3", "0x9d994375", "0xd8d53386", "0x2c956ee2", "0xe313c2f9", "0x495ace1b", "0x8238bb25"],
"seed_derivation": "seed_words = FNV-1a 64 over seed_bytes (attempt 0) or seed_bytes || attempt_le32 (attempt k >= 1), basis ^ (salt * 0x9E3779B97F4A7C15) for salt 0..3, then h ^= h>>33; h *= 0xff51afd7ed558ccd; h ^= h>>33; words[2*salt] = low 32, words[2*salt+1] = high 32",
"generator_rule": "version 2: exactly 16 load slots drawn first from instructions 1..63 (partial Fisher-Yates), the other 48 ops from the ten non-load weights (sum 75); a load's source is drawn from the registers other than dst written by an earlier instruction and not read by a load since; the candidate must pass the acceptance rule of spec 01 section 1.4.6 (static: no cyclically stale load source, every register has an injecting write; dynamic: 64 units on the seed-keyed closed-form dataset with no constant register bit, no lane-constant load site, under 164 saturated final values, every output bit within 136 of 1024, distinct addresses above 245760), else the next attempt of the seed is tried",
"lanes": 32,
"registers": 8,
"iterations": 8,
"instruction_count": 64,
"loads_per_hash": 128,
"load_class": "mix50-35-15",
"load_slots": 16,
"load_mix_percent_4_16_64": [50, 35, 15],
"load_width_counts_4_16_64": [12, 2, 2],
"bytes_per_hash": 1664,
"wide_load": "read-width experiment (5 October 2026, docs/plans/read-width.md), NOT the lottery hash: a load of W words (width field, 4 or 16) reads dataset[b .. b + W) with b = (src & mask) & ~(W - 1) and folds every word into dst: x = dst ^ w[0]; for j in 1..W: x = (rotl(x, 11) * 0x9e3779b1) ^ w[j]; dst = x; width 1 is the plain load; the width is drawn per instruction from the class mix with one extra below(100) draw after the nine of version 2, and the program id is FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed words || attempt_le32 || mix[3] || load_slots",
"op_mix": {"load": 16, "add": 7, "mulhi": 7, "shfl": 6, "xor": 6, "mad": 5, "sub": 5, "mul": 4, "rotl": 4, "rotr": 4},
"register_init": "for i in 0..7: x = nonce ^ seed_words[i]; x += 0x9e3779b9 * (i+1) (mod 2^32); x = splitmix32(x); r[i] = x ^ seed_words[(i+1) & 7]",
"splitmix32": "x ^= x>>16; x *= 0x7feb352d; x ^= x>>15; x *= 0x846ca68b; x ^= x>>16",
"iteration": "sel = r0 sampled once at the top of each iteration, then all instructions in order",
"output": "lo = r0 ^ rotl(r1,7) ^ rotl(r2,14) ^ rotl(r3,21); hi = r4 ^ rotl(r5,9) ^ rotl(r6,18) ^ rotl(r7,27); out = (hi << 32) | lo",
"op_semantics": {
"add": "dst = dst + src + (bit `bit` of sel ? imm2 : imm)",
"sub": "dst = dst - src",
"mul": "dst = dst * src (low 32)",
"mulhi": "dst = high 32 bits of dst * src",
"xor": "dst = dst ^ src",
"or": "dst = dst | src",
"rotl": "dst = rotl(dst, rot), rot in 1..31",
"rotr": "dst = rotr(dst, src & 31)",
"mad": "dst = src * src2 + dst",
"shfl": "dst = dst ^ (src of lane (lane ^ mask)), mask in {1,2,4,8,16}, within the 32-lane warp",
"load": "dst = dst ^ dataset[src & dataset.mask]",
"wload": "base = (src of lane 0 & dataset.mask) & ~31; dst = dst ^ dataset[base + lane] (warp-coalesced 128-byte load, lever b, only when --wide-frac > 0)"
},
"dataset": {
"log2_words": 28,
"bytes": 1073741824,
"mask": "0x0fffffff",
"day": "2026-10-03",
"day_bytes": "6461792f323032362d31302d3033",
"day_words_from": "seed_words_from_bytes(day_bytes)",
"d0": "0x3067619f",
"d1": "0x3c269176",
"mode": "memory-hard",
"spec": "proto-metal/MEMHARD.md",
"key": ["0x3067619f", "0x3c269176", "0x84a03b03", "0xf8c63294", "0xff977c5b", "0xe60def3e", "0x63630141", "0xb8fbcb58"],
"key_derivation": "the 8 words of seed_words_from_bytes(day_bytes); d0, d1 are key[0], key[1]",
"cache": {"log2_words": 26, "bytes": 268435456, "line_words": 16, "segment_lines": 64, "segments": 65536, "block": "ChaCha12 core + feed-forward, rotations 16 12 8 7", "sigma": ["0x61707865", "0x3320646e", "0x79622d32", "0x6b206574"], "tag": ["0x49676e65", "0x756d4d48"], "chain": "in_j = prev_line ^ (sigma[0..3] || key[0..7] || seg || j || tag[0..1]); line_j = block(in_j); prev_0 = 0"},
"mixer": {"draw": "SplitMix64 seeded with key[0] | key[1] << 32: rot[0..7] = 1 + next() % 31, mul[0..15] = low32(next()) | 1, rc[0..15] = low32(next())", "rot": [20, 20, 19, 4, 26, 3, 3, 27], "mul": ["0x42146205", "0x52cbe0fb", "0x7ecf4a03", "0x6728907f", "0xd81d9751", "0x132952c3", "0xf60de277", "0x05358035", "0xbaf6499d", "0xe4db9667", "0x3e98f45d", "0xd0004edd", "0x2691630d", "0x9beb3bcf", "0xab310379", "0x99cfb423"], "rc": ["0xbab68293", "0xcc162340", "0x6ce151cc", "0xe62b8997", "0xc9c80297", "0xf74a1654", "0x3d704af5", "0x3cf522b7", "0x2b9cac04", "0xa880ac10", "0x13e5dd1d", "0x6fc3e233", "0x2d83eeac", "0x9006e8bf", "0x2c4b5362", "0x31b49ee2"], "round": "for i in 0..15: s[i] = (s[i] ^ (rc[i] + (r+1) * 0x9E3779B9)) * mul[i]; then quarter rounds on columns (0,4,8,12) (1,5,9,13) (2,6,10,14) (3,7,11,15) with rot[0..3] and diagonals (0,5,10,15) (1,6,11,12) (2,7,8,13) (3,4,9,14) with rot[4..7]", "quarter_round": "a += b; d ^= a; d = rotl(d, r1); c += d; b ^= c; b = rotl(b, r2); a += b; d ^= a; d = rotl(d, r3); c += d; b ^= c; b = rotl(b, r4)"},
"item": "s[0..7] = key; s[8+i] = t * mul[i] + rc[i] for i in 0..7; for r in 0..7: s = M_r(s); line = s[0] & 0x003fffff; s[i] ^= cache[line * 16 + i]; then s = M_8(s); item(t) = s",
"word": "dataset[w] = item(w >> 4)[w & 15]"
},
"instructions": [
{"i": 0, "op": "add", "dst": 2, "src": 7, "src2": 2, "imm": "0x92d00116", "imm2": "0xb788d5f8", "rot": 18, "bit": 30, "mask": 16, "width": 1},
{"i": 1, "op": "mulhi", "dst": 3, "src": 2, "src2": 2, "imm": "0xda7cdbf6", "imm2": "0xb722360d", "rot": 28, "bit": 18, "mask": 2, "width": 1},
{"i": 2, "op": "load", "dst": 6, "src": 3, "src2": 3, "imm": "0x4c76905d", "imm2": "0x8700b920", "rot": 20, "bit": 19, "mask": 16, "width": 1},
{"i": 3, "op": "load", "dst": 0, "src": 6, "src2": 5, "imm": "0xc88bd196", "imm2": "0x0bec95ee", "rot": 27, "bit": 2, "mask": 1, "width": 1},
{"i": 4, "op": "mul", "dst": 5, "src": 2, "src2": 0, "imm": "0x7ed20556", "imm2": "0x3cb2440a", "rot": 19, "bit": 8, "mask": 8, "width": 1},
{"i": 5, "op": "add", "dst": 7, "src": 2, "src2": 5, "imm": "0xd5f6f389", "imm2": "0x9a773a55", "rot": 29, "bit": 20, "mask": 2, "width": 1},
{"i": 6, "op": "load", "dst": 6, "src": 5, "src2": 7, "imm": "0xd9894991", "imm2": "0xf41ba497", "rot": 14, "bit": 9, "mask": 4, "width": 16},
{"i": 7, "op": "load", "dst": 5, "src": 6, "src2": 0, "imm": "0xab148320", "imm2": "0x83097251", "rot": 31, "bit": 6, "mask": 16, "width": 1},
{"i": 8, "op": "load", "dst": 1, "src": 2, "src2": 0, "imm": "0x9f83b9b8", "imm2": "0x5b9a1ccc", "rot": 17, "bit": 28, "mask": 1, "width": 4},
{"i": 9, "op": "mul", "dst": 4, "src": 5, "src2": 1, "imm": "0x70d7287a", "imm2": "0x803900fa", "rot": 26, "bit": 24, "mask": 4, "width": 1},
{"i": 10, "op": "load", "dst": 2, "src": 0, "src2": 1, "imm": "0xf15303dd", "imm2": "0x4725246d", "rot": 4, "bit": 10, "mask": 2, "width": 4},
{"i": 11, "op": "load", "dst": 3, "src": 2, "src2": 6, "imm": "0xd6d526e4", "imm2": "0x0af56645", "rot": 19, "bit": 18, "mask": 4, "width": 1},
{"i": 12, "op": "xor", "dst": 4, "src": 0, "src2": 5, "imm": "0xa08eca24", "imm2": "0x21fe64a3", "rot": 29, "bit": 29, "mask": 8, "width": 1},
{"i": 13, "op": "load", "dst": 7, "src": 3, "src2": 2, "imm": "0x0f32a107", "imm2": "0x92e66063", "rot": 24, "bit": 22, "mask": 8, "width": 1},
{"i": 14, "op": "xor", "dst": 7, "src": 1, "src2": 2, "imm": "0x9eec6af9", "imm2": "0x21dc3f6f", "rot": 28, "bit": 18, "mask": 4, "width": 1},
{"i": 15, "op": "mad", "dst": 4, "src": 1, "src2": 4, "imm": "0xfa6381a2", "imm2": "0x2409fa4f", "rot": 18, "bit": 31, "mask": 4, "width": 1},
{"i": 16, "op": "mad", "dst": 3, "src": 0, "src2": 4, "imm": "0x52a02e74", "imm2": "0xcdd43c2d", "rot": 9, "bit": 12, "mask": 2, "width": 1},
{"i": 17, "op": "mad", "dst": 4, "src": 6, "src2": 2, "imm": "0x60e472f6", "imm2": "0xe8623e17", "rot": 5, "bit": 30, "mask": 4, "width": 1},
{"i": 18, "op": "rotr", "dst": 0, "src": 2, "src2": 1, "imm": "0x08cbdd3e", "imm2": "0x3ab8f3a3", "rot": 27, "bit": 2, "mask": 8, "width": 1},
{"i": 19, "op": "rotr", "dst": 5, "src": 6, "src2": 6, "imm": "0x09d41964", "imm2": "0x98709424", "rot": 28, "bit": 2, "mask": 2, "width": 1},
{"i": 20, "op": "load", "dst": 4, "src": 1, "src2": 3, "imm": "0x3831d4c0", "imm2": "0xb095fd4b", "rot": 28, "bit": 18, "mask": 2, "width": 1},
{"i": 21, "op": "load", "dst": 2, "src": 4, "src2": 0, "imm": "0x61f00f86", "imm2": "0x2bd27add", "rot": 1, "bit": 5, "mask": 2, "width": 1},
{"i": 22, "op": "rotl", "dst": 1, "src": 5, "src2": 6, "imm": "0xe8785677", "imm2": "0xafeefe07", "rot": 14, "bit": 20, "mask": 2, "width": 1},
{"i": 23, "op": "sub", "dst": 7, "src": 1, "src2": 3, "imm": "0x06c22ebe", "imm2": "0x486a522e", "rot": 19, "bit": 17, "mask": 2, "width": 1},
{"i": 24, "op": "shfl", "dst": 2, "src": 6, "src2": 5, "imm": "0xf684e748", "imm2": "0xd67fa827", "rot": 31, "bit": 24, "mask": 2, "width": 1},
{"i": 25, "op": "shfl", "dst": 1, "src": 4, "src2": 1, "imm": "0x712a551f", "imm2": "0xf6b8ff0b", "rot": 7, "bit": 7, "mask": 16, "width": 1},
{"i": 26, "op": "load", "dst": 6, "src": 0, "src2": 6, "imm": "0xcc4fcce5", "imm2": "0xd121fe63", "rot": 31, "bit": 20, "mask": 1, "width": 1},
{"i": 27, "op": "shfl", "dst": 0, "src": 6, "src2": 6, "imm": "0x1a31bcef", "imm2": "0x0152fc58", "rot": 19, "bit": 31, "mask": 1, "width": 1},
{"i": 28, "op": "add", "dst": 0, "src": 2, "src2": 6, "imm": "0x1a3cecfb", "imm2": "0xc6311db1", "rot": 25, "bit": 25, "mask": 16, "width": 1},
{"i": 29, "op": "mulhi", "dst": 5, "src": 7, "src2": 7, "imm": "0x7eda9d10", "imm2": "0x74eacbd7", "rot": 8, "bit": 20, "mask": 1, "width": 1},
{"i": 30, "op": "mad", "dst": 0, "src": 1, "src2": 7, "imm": "0xf1992097", "imm2": "0xcc9bf87e", "rot": 14, "bit": 0, "mask": 16, "width": 1},
{"i": 31, "op": "rotl", "dst": 5, "src": 1, "src2": 6, "imm": "0xf7adfcf5", "imm2": "0x16693ac6", "rot": 23, "bit": 10, "mask": 4, "width": 1},
{"i": 32, "op": "add", "dst": 0, "src": 2, "src2": 7, "imm": "0x7aa05e39", "imm2": "0xf1282553", "rot": 21, "bit": 28, "mask": 4, "width": 1},
{"i": 33, "op": "xor", "dst": 1, "src": 6, "src2": 4, "imm": "0xa773d935", "imm2": "0x38d34547", "rot": 8, "bit": 16, "mask": 8, "width": 1},
{"i": 34, "op": "sub", "dst": 0, "src": 1, "src2": 4, "imm": "0x20af644a", "imm2": "0x28e75a4d", "rot": 7, "bit": 28, "mask": 8, "width": 1},
{"i": 35, "op": "load", "dst": 1, "src": 6, "src2": 2, "imm": "0x6fb47122", "imm2": "0x562f2b23", "rot": 9, "bit": 23, "mask": 8, "width": 1},
{"i": 36, "op": "rotl", "dst": 1, "src": 6, "src2": 3, "imm": "0xafd08d8b", "imm2": "0xa7f3e93d", "rot": 7, "bit": 30, "mask": 16, "width": 1},
{"i": 37, "op": "shfl", "dst": 0, "src": 4, "src2": 0, "imm": "0x84fda071", "imm2": "0x3ec80cba", "rot": 25, "bit": 11, "mask": 8, "width": 1},
{"i": 38, "op": "add", "dst": 3, "src": 5, "src2": 1, "imm": "0x4a9bf1a8", "imm2": "0x70173cfd", "rot": 15, "bit": 13, "mask": 2, "width": 1},
{"i": 39, "op": "mulhi", "dst": 3, "src": 4, "src2": 3, "imm": "0x2ea4e0bd", "imm2": "0x1431b05f", "rot": 6, "bit": 22, "mask": 1, "width": 1},
{"i": 40, "op": "add", "dst": 3, "src": 6, "src2": 3, "imm": "0x26b3e1a7", "imm2": "0x2596fd35", "rot": 4, "bit": 8, "mask": 1, "width": 1},
{"i": 41, "op": "sub", "dst": 3, "src": 7, "src2": 5, "imm": "0x50df0069", "imm2": "0xba092804", "rot": 28, "bit": 31, "mask": 8, "width": 1},
{"i": 42, "op": "sub", "dst": 0, "src": 4, "src2": 1, "imm": "0x22b4b65e", "imm2": "0x567989de", "rot": 3, "bit": 2, "mask": 16, "width": 1},
{"i": 43, "op": "rotr", "dst": 5, "src": 7, "src2": 6, "imm": "0xeebb6e92", "imm2": "0xdb303272", "rot": 7, "bit": 26, "mask": 16, "width": 1},
{"i": 44, "op": "mad", "dst": 3, "src": 4, "src2": 1, "imm": "0x17907041", "imm2": "0x2665fc2a", "rot": 24, "bit": 10, "mask": 8, "width": 1},
{"i": 45, "op": "load", "dst": 4, "src": 2, "src2": 0, "imm": "0xf85e047a", "imm2": "0x514751b2", "rot": 21, "bit": 3, "mask": 16, "width": 1},
{"i": 46, "op": "mulhi", "dst": 2, "src": 5, "src2": 6, "imm": "0x79ee74b0", "imm2": "0x71d32fd1", "rot": 26, "bit": 5, "mask": 2, "width": 1},
{"i": 47, "op": "load", "dst": 3, "src": 5, "src2": 6, "imm": "0x73fa2964", "imm2": "0x25d08887", "rot": 22, "bit": 22, "mask": 4, "width": 1},
{"i": 48, "op": "mulhi", "dst": 5, "src": 6, "src2": 3, "imm": "0xb686c1c8", "imm2": "0x31b980fa", "rot": 26, "bit": 20, "mask": 8, "width": 1},
{"i": 49, "op": "add", "dst": 5, "src": 7, "src2": 0, "imm": "0x9f31d23c", "imm2": "0x724f77b9", "rot": 9, "bit": 22, "mask": 4, "width": 1},
{"i": 50, "op": "shfl", "dst": 2, "src": 7, "src2": 6, "imm": "0x0793dd9f", "imm2": "0xbed3bbbb", "rot": 31, "bit": 23, "mask": 1, "width": 1},
{"i": 51, "op": "mul", "dst": 1, "src": 5, "src2": 7, "imm": "0x713cb7a1", "imm2": "0x9e06cef8", "rot": 20, "bit": 2, "mask": 1, "width": 1},
{"i": 52, "op": "mul", "dst": 1, "src": 6, "src2": 5, "imm": "0x57dcbb02", "imm2": "0xf262ce05", "rot": 11, "bit": 17, "mask": 4, "width": 1},
{"i": 53, "op": "load", "dst": 6, "src": 1, "src2": 2, "imm": "0x6a4caa97", "imm2": "0x26d85624", "rot": 22, "bit": 28, "mask": 2, "width": 16},
{"i": 54, "op": "xor", "dst": 7, "src": 2, "src2": 5, "imm": "0x2cb21c3f", "imm2": "0x53fa6b79", "rot": 2, "bit": 17, "mask": 4, "width": 1},
{"i": 55, "op": "sub", "dst": 2, "src": 1, "src2": 5, "imm": "0x19c7d010", "imm2": "0xd7f64a4b", "rot": 28, "bit": 30, "mask": 4, "width": 1},
{"i": 56, "op": "xor", "dst": 7, "src": 3, "src2": 2, "imm": "0xecabf384", "imm2": "0x0587918d", "rot": 25, "bit": 5, "mask": 8, "width": 1},
{"i": 57, "op": "rotl", "dst": 3, "src": 6, "src2": 0, "imm": "0x5dba32f7", "imm2": "0x3228574b", "rot": 2, "bit": 20, "mask": 4, "width": 1},
{"i": 58, "op": "mulhi", "dst": 6, "src": 4, "src2": 5, "imm": "0x558cb969", "imm2": "0xb0531a17", "rot": 2, "bit": 17, "mask": 2, "width": 1},
{"i": 59, "op": "rotr", "dst": 4, "src": 5, "src2": 2, "imm": "0x8384814a", "imm2": "0xad7ee308", "rot": 7, "bit": 2, "mask": 16, "width": 1},
{"i": 60, "op": "mulhi", "dst": 3, "src": 1, "src2": 7, "imm": "0x86193717", "imm2": "0x3daece51", "rot": 10, "bit": 12, "mask": 4, "width": 1},
{"i": 61, "op": "xor", "dst": 3, "src": 6, "src2": 7, "imm": "0x83dada1b", "imm2": "0xc9f04198", "rot": 24, "bit": 22, "mask": 2, "width": 1},
{"i": 62, "op": "shfl", "dst": 5, "src": 1, "src2": 0, "imm": "0x21dc46bb", "imm2": "0xe2eb5f1e", "rot": 19, "bit": 19, "mask": 16, "width": 1},
{"i": 63, "op": "load", "dst": 1, "src": 6, "src2": 5, "imm": "0x5a37a588", "imm2": "0xcb1e1122", "rot": 13, "bit": 1, "mask": 1, "width": 1}
]
}

View file

@ -0,0 +1,109 @@
#include <metal_stdlib>
using namespace metal;
#define MASK 0x0fffffffu
constant uint SEEDW[8] = { 0xaa5a3f6eu, 0x5c0410c3u, 0x9d994375u, 0xd8d53386u, 0x2c956ee2u, 0xe313c2f9u, 0x495ace1bu, 0x8238bb25u };
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;
}
kernel void igneum_hash(device const uint* dataset [[buffer(0)]],
device ulong* out [[buffer(1)]],
constant uint& baseNonce [[buffer(2)]],
uint gid [[thread_position_in_grid]]) {
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 = r2 + r7 + select(0x92d00116u, 0xb788d5f8u, ((sel >> 30u) & 1u) != 0u); // 0
r3 = mulhi(r3, r2); // 1
r6 = r6 ^ dataset[r3 & MASK]; // 2
r0 = r0 ^ dataset[r6 & MASK]; // 3
r5 = r5 * r2; // 4
r7 = r7 + r2 + select(0xd5f6f389u, 0x9a773a55u, ((sel >> 20u) & 1u) != 0u); // 5
{ uint b_ = (r5 & MASK) & ~15u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r6 = x_; } // 6
r5 = r5 ^ dataset[r6 & MASK]; // 7
{ uint b_ = (r2 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r1 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r1 = x_; } // 8
r4 = r4 * r5; // 9
{ uint b_ = (r0 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r2 = x_; } // 10
r3 = r3 ^ dataset[r2 & MASK]; // 11
r4 = r4 ^ r0; // 12
r7 = r7 ^ dataset[r3 & MASK]; // 13
r7 = r7 ^ r1; // 14
r4 = r1 * r4 + r4; // 15
r3 = r0 * r4 + r3; // 16
r4 = r6 * r2 + r4; // 17
r0 = rotr_var(r0, r2); // 18
r5 = rotr_var(r5, r6); // 19
r4 = r4 ^ dataset[r1 & MASK]; // 20
r2 = r2 ^ dataset[r4 & MASK]; // 21
r1 = rotl_imm(r1, 14u); // 22
r7 = r7 - r1; // 23
r2 = r2 ^ simd_shuffle_xor(r6, (ushort)2); // 24
r1 = r1 ^ simd_shuffle_xor(r4, (ushort)16); // 25
r6 = r6 ^ dataset[r0 & MASK]; // 26
r0 = r0 ^ simd_shuffle_xor(r6, (ushort)1); // 27
r0 = r0 + r2 + select(0x1a3cecfbu, 0xc6311db1u, ((sel >> 25u) & 1u) != 0u); // 28
r5 = mulhi(r5, r7); // 29
r0 = r1 * r7 + r0; // 30
r5 = rotl_imm(r5, 23u); // 31
r0 = r0 + r2 + select(0x7aa05e39u, 0xf1282553u, ((sel >> 28u) & 1u) != 0u); // 32
r1 = r1 ^ r6; // 33
r0 = r0 - r1; // 34
r1 = r1 ^ dataset[r6 & MASK]; // 35
r1 = rotl_imm(r1, 7u); // 36
r0 = r0 ^ simd_shuffle_xor(r4, (ushort)8); // 37
r3 = r3 + r5 + select(0x4a9bf1a8u, 0x70173cfdu, ((sel >> 13u) & 1u) != 0u); // 38
r3 = mulhi(r3, r4); // 39
r3 = r3 + r6 + select(0x26b3e1a7u, 0x2596fd35u, ((sel >> 8u) & 1u) != 0u); // 40
r3 = r3 - r7; // 41
r0 = r0 - r4; // 42
r5 = rotr_var(r5, r7); // 43
r3 = r4 * r1 + r3; // 44
r4 = r4 ^ dataset[r2 & MASK]; // 45
r2 = mulhi(r2, r5); // 46
r3 = r3 ^ dataset[r5 & MASK]; // 47
r5 = mulhi(r5, r6); // 48
r5 = r5 + r7 + select(0x9f31d23cu, 0x724f77b9u, ((sel >> 22u) & 1u) != 0u); // 49
r2 = r2 ^ simd_shuffle_xor(r7, (ushort)1); // 50
r1 = r1 * r5; // 51
r1 = r1 * r6; // 52
{ uint b_ = (r1 & MASK) & ~15u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r6 = x_; } // 53
r7 = r7 ^ r2; // 54
r2 = r2 - r1; // 55
r7 = r7 ^ r3; // 56
r3 = rotl_imm(r3, 2u); // 57
r6 = mulhi(r6, r4); // 58
r4 = rotr_var(r4, r5); // 59
r3 = mulhi(r3, r1); // 60
r3 = r3 ^ r6; // 61
r5 = r5 ^ simd_shuffle_xor(r1, (ushort)16); // 62
r1 = r1 ^ dataset[r6 & MASK]; // 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;
}

View file

@ -0,0 +1,111 @@
#include <metal_stdlib>
using namespace metal;
#define MASK 0x0fffffffu
constant uint SEEDW[8] = { 0xaa5a3f6eu, 0x5c0410c3u, 0x9d994375u, 0xd8d53386u, 0x2c956ee2u, 0xe313c2f9u, 0x495ace1bu, 0x8238bb25u };
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;
}
// Header-bound variant: the init words come from buffer 3 (bind.rs), not from SEEDW.
kernel void igneum_hash_bound(device const uint* dataset [[buffer(0)]],
device ulong* out [[buffer(1)]],
constant uint& baseNonce [[buffer(2)]],
constant uint* initw [[buffer(3)]],
uint gid [[thread_position_in_grid]]) {
uint nonce = baseNonce + gid;
uint r0, r1, r2, r3, r4, r5, r6, r7;
{ uint x = nonce ^ initw[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ initw[1]; }
{ uint x = nonce ^ initw[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ initw[2]; }
{ uint x = nonce ^ initw[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ initw[3]; }
{ uint x = nonce ^ initw[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ initw[4]; }
{ uint x = nonce ^ initw[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ initw[5]; }
{ uint x = nonce ^ initw[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ initw[6]; }
{ uint x = nonce ^ initw[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ initw[7]; }
{ uint x = nonce ^ initw[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ initw[0]; }
for (uint it = 0u; it < 8u; ++it) {
uint sel = r0;
r2 = r2 + r7 + select(0x92d00116u, 0xb788d5f8u, ((sel >> 30u) & 1u) != 0u); // 0
r3 = mulhi(r3, r2); // 1
r6 = r6 ^ dataset[r3 & MASK]; // 2
r0 = r0 ^ dataset[r6 & MASK]; // 3
r5 = r5 * r2; // 4
r7 = r7 + r2 + select(0xd5f6f389u, 0x9a773a55u, ((sel >> 20u) & 1u) != 0u); // 5
{ uint b_ = (r5 & MASK) & ~15u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r6 = x_; } // 6
r5 = r5 ^ dataset[r6 & MASK]; // 7
{ uint b_ = (r2 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r1 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r1 = x_; } // 8
r4 = r4 * r5; // 9
{ uint b_ = (r0 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r2 = x_; } // 10
r3 = r3 ^ dataset[r2 & MASK]; // 11
r4 = r4 ^ r0; // 12
r7 = r7 ^ dataset[r3 & MASK]; // 13
r7 = r7 ^ r1; // 14
r4 = r1 * r4 + r4; // 15
r3 = r0 * r4 + r3; // 16
r4 = r6 * r2 + r4; // 17
r0 = rotr_var(r0, r2); // 18
r5 = rotr_var(r5, r6); // 19
r4 = r4 ^ dataset[r1 & MASK]; // 20
r2 = r2 ^ dataset[r4 & MASK]; // 21
r1 = rotl_imm(r1, 14u); // 22
r7 = r7 - r1; // 23
r2 = r2 ^ simd_shuffle_xor(r6, (ushort)2); // 24
r1 = r1 ^ simd_shuffle_xor(r4, (ushort)16); // 25
r6 = r6 ^ dataset[r0 & MASK]; // 26
r0 = r0 ^ simd_shuffle_xor(r6, (ushort)1); // 27
r0 = r0 + r2 + select(0x1a3cecfbu, 0xc6311db1u, ((sel >> 25u) & 1u) != 0u); // 28
r5 = mulhi(r5, r7); // 29
r0 = r1 * r7 + r0; // 30
r5 = rotl_imm(r5, 23u); // 31
r0 = r0 + r2 + select(0x7aa05e39u, 0xf1282553u, ((sel >> 28u) & 1u) != 0u); // 32
r1 = r1 ^ r6; // 33
r0 = r0 - r1; // 34
r1 = r1 ^ dataset[r6 & MASK]; // 35
r1 = rotl_imm(r1, 7u); // 36
r0 = r0 ^ simd_shuffle_xor(r4, (ushort)8); // 37
r3 = r3 + r5 + select(0x4a9bf1a8u, 0x70173cfdu, ((sel >> 13u) & 1u) != 0u); // 38
r3 = mulhi(r3, r4); // 39
r3 = r3 + r6 + select(0x26b3e1a7u, 0x2596fd35u, ((sel >> 8u) & 1u) != 0u); // 40
r3 = r3 - r7; // 41
r0 = r0 - r4; // 42
r5 = rotr_var(r5, r7); // 43
r3 = r4 * r1 + r3; // 44
r4 = r4 ^ dataset[r2 & MASK]; // 45
r2 = mulhi(r2, r5); // 46
r3 = r3 ^ dataset[r5 & MASK]; // 47
r5 = mulhi(r5, r6); // 48
r5 = r5 + r7 + select(0x9f31d23cu, 0x724f77b9u, ((sel >> 22u) & 1u) != 0u); // 49
r2 = r2 ^ simd_shuffle_xor(r7, (ushort)1); // 50
r1 = r1 * r5; // 51
r1 = r1 * r6; // 52
{ uint b_ = (r1 & MASK) & ~15u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r6 = x_; } // 53
r7 = r7 ^ r2; // 54
r2 = r2 - r1; // 55
r7 = r7 ^ r3; // 56
r3 = rotl_imm(r3, 2u); // 57
r6 = mulhi(r6, r4); // 58
r4 = rotr_var(r4, r5); // 59
r3 = mulhi(r3, r1); // 60
r3 = r3 ^ r6; // 61
r5 = r5 ^ simd_shuffle_xor(r1, (ushort)16); // 62
r1 = r1 ^ dataset[r6 & MASK]; // 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;
}

View file

@ -0,0 +1,57 @@
// Generated by igneum-pow export (generator v2) for seed "igneum-readwidth/A/0". Do not edit by hand.
// Expected outputs: igneum-pow (Rust) CPU interpreter, generator v2, memory-hard dataset
#pragma once
#ifdef __cplusplus
#include <cstdint>
#else
#include <stdint.h>
#endif
#define IGNEUM_VEC_WARPS 3
static const uint32_t IGNEUM_VEC_BASE[IGNEUM_VEC_WARPS] = { 0u, 4096u, 1000000u };
static const uint64_t IGNEUM_VEC_OUT[IGNEUM_VEC_WARPS][32] = {
{ // base nonce 0
0xc02af7d21c32b6e2ull, 0xf9676c7b81e42590ull, 0x3b5b2f433e291756ull, 0x2307064009985ab7ull, 0xf1f2d84aa86f1f5aull, 0xa7ed219296c204beull, 0xf8105c9692ac08e7ull, 0x04377ca8373b8e1full,
0x2f76672aa0a6104dull, 0xac85110c18a8edc8ull, 0x3b4c20d7d3c22e52ull, 0x365c221488fa37e4ull, 0x989dab8f749cd976ull, 0x37f6690cc11dd5a7ull, 0x6159da4241e04ae2ull, 0x3c19bfdcd7821055ull,
0x5b48a58c99800f4full, 0x991018ff7b067a45ull, 0xf281167cb9c28ef2ull, 0xe9497f5c0207892dull, 0xac1ccf9060f7c78bull, 0xf6f4282e68ceff58ull, 0xfa2694f87cf04ef6ull, 0x62d01632e98c6a11ull,
0x63a875eb903ee9afull, 0x680ad026ac9e5e1full, 0x63d80718c2e2912bull, 0x377a3c901dadb9f5ull, 0xb2c39a01420d0806ull, 0x5ce44109e5c38357ull, 0x8532912b848b1f5bull, 0x76141c17aa59c5f6ull
},
{ // base nonce 4096
0x30301278872d1766ull, 0xfd009372bb888e00ull, 0xb93e45944b9cb923ull, 0xe74cb4f099ac3ea9ull, 0x3cd9b2381d1cde3cull, 0xb69bddb5df734350ull, 0xbccc2d57615b8465ull, 0xa30bbff25bf3d723ull,
0x66ca1a4c82d500d0ull, 0xbb60ed823f1bee84ull, 0x6312dd68c839352full, 0xf15a1a4e9b902225ull, 0xcf315f5496b73b41ull, 0x5e6aa39724dbe6caull, 0x3a4be831dca92fd7ull, 0xbd5f1ae9b2892322ull,
0x69582160f1bdf99cull, 0xcc41242a8f4776a2ull, 0x377a337ca9a5f44aull, 0x12e453c8c831a462ull, 0xfe278d9da6dcaf29ull, 0xfddd6db8998a7f00ull, 0x6f9d4db889ef7e98ull, 0x278294842317978dull,
0xe4bab477a44177b0ull, 0x52c1ea0d495c960dull, 0xf1504ce7e9c0d818ull, 0x15673b7d791269d7ull, 0x18828b1a6fd35583ull, 0xa6d769abd44e3827ull, 0x82b838f1172afc66ull, 0x5b18a4842a119defull
},
{ // base nonce 1000000
0xfd55a3c4be548e4eull, 0x00f110e15155ab33ull, 0x374d6799a6b45a35ull, 0x27f9962d27ae0515ull, 0xd0ffb5d4c0f1dd83ull, 0x718d962e2ec9d7e7ull, 0xe07c572ca8705952ull, 0x99db60352bae63b7ull,
0x065808eaebc7f7c8ull, 0x0072533c9ddc9f30ull, 0x7b11d0becd7297edull, 0xcf4b94c52549288aull, 0x0e8d98977c965c26ull, 0x890c6664b01fb638ull, 0x4d0a4b30a326dca6ull, 0x948a02536dca9925ull,
0x1d9b3442fd4c3e0aull, 0x8b555804a68adabbull, 0x4942826f941b2089ull, 0x967b94a90c51196eull, 0xea8cf4b416d1878cull, 0xbefa6e39bfc1860aull, 0x53cbf72fe366ef94ull, 0xbfe204292b3f8b03ull,
0xe269bfe9953bcf03ull, 0x24a758d6174e0da3ull, 0x25f57ab508d87895ull, 0xe45ec6208eb56f7dull, 0x47c891dda3f102c6ull, 0x088698d191e8dc33ull, 0xc1c4cd437ae11388ull, 0xa17c529d1a26c64dull
}
};
// Dataset self-test: dataset[0..15] and dataset[IGNEUM_MASK] (268435455).
static const uint32_t IGNEUM_DS_HEAD[16] = {
0xffc3cd94u, 0x5920ccd8u, 0x392f44bbu, 0x5e57f67au, 0x2f2bc2a9u, 0x620b0e36u, 0xbdc09014u, 0x436654bfu,
0x311e0b48u, 0x1abd93adu, 0x59cc7ce8u, 0xee5247b2u, 0x86171fe8u, 0x6d874751u, 0xc9f7728fu, 0x7c2a435du
};
static const uint32_t IGNEUM_DS_LAST_INDEX = 268435455u;
static const uint32_t IGNEUM_DS_LAST = 0xa33ada72u;
// 64 sampled dataset words (index, value) computed on the Mac.
#define IGNEUM_DS_SAMPLES 64
static const uint32_t IGNEUM_DS_SAMPLE_INDEX[IGNEUM_DS_SAMPLES] = {
59471966u, 217795994u, 208353206u, 42483309u, 172547758u, 148076330u, 183853158u, 214389424u, 267488061u, 169781097u, 184093494u, 153880993u, 84977930u, 46426879u, 3093825u, 225364072u, 44593546u, 260713159u, 168250303u, 52384140u, 223401610u, 45554030u, 95410555u, 175039924u, 79171087u, 267580473u, 24168642u, 37981670u, 171551130u, 195559979u, 204611762u, 140997658u, 138925853u, 86637313u, 20736778u, 219665210u, 160430336u, 264654675u, 8013395u, 228945585u, 213884386u, 104419827u, 44185464u, 142737231u, 99284897u, 132475900u, 61861762u, 132056166u, 262388043u, 91878046u, 117353561u, 124768597u, 71352993u, 190698941u, 46055428u, 55281366u, 165145231u, 106810753u, 171985651u, 232085256u, 159510492u, 40072060u, 209107596u, 39023794u
};
static const uint32_t IGNEUM_DS_SAMPLE_VALUE[IGNEUM_DS_SAMPLES] = {
0xe8b73d94u, 0x337028b5u, 0xafe148c9u, 0xab99f7aeu, 0x434ea619u, 0xd85cb880u, 0x54764c7fu, 0x82c7e420u, 0xedf4cb9eu, 0x9884c959u, 0x223ee793u, 0x3a9ccf69u, 0x81da4fd2u, 0xd6ce8cb9u, 0xe3922dcau, 0x3e7e6bdeu, 0x382a3acau, 0x567e7f7fu, 0x25a0f084u, 0xbfeef128u, 0xe338abfbu, 0x7c3b5280u, 0x909bc5f1u, 0xd8b74b9cu, 0x8e31a22eu, 0x26b5f1d8u, 0x79122c00u, 0xcafc3340u, 0xd5e02ea3u, 0x1aee1afdu, 0xdb090d9au, 0xb049f435u, 0x4954d8bau, 0x03797ba0u, 0x196eefbdu, 0xd153412au, 0xbe5d2c4bu, 0xdaa14f0eu, 0x8e61ed07u, 0x9e9a64c6u, 0x2e29ff36u, 0x392a8589u, 0xb56a5912u, 0xfa6e8b57u, 0xd1a737cbu, 0xb0fa841au, 0xbe1c341fu, 0xe25be0f1u, 0xe937f543u, 0xebab2248u, 0x8e1b607au, 0x202a2fedu, 0x95e2819cu, 0x9c9652d4u, 0x32fedef0u, 0xdecfff82u, 0xcb5d43e5u, 0xb735806au, 0x8905939cu, 0xfbf8472du, 0xada74e5du, 0x7ebdeeeau, 0x0119f2b3u, 0xa9a376b8u
};
// Cache self-test (memory-hard mode): cache[0..15], the last 16 words, and FNV-1a 64 over all 2^26 words.
static const uint32_t IGNEUM_CACHE_HEAD[16] = {
0x355a86d2u, 0x7957db1cu, 0xd21772afu, 0x6fc1e09bu, 0xd55ce61du, 0x6e6a278bu, 0xd3f543ceu, 0x223d8e82u,
0x143ab337u, 0x2e9f05bdu, 0x2eb389bfu, 0x0c6e449eu, 0x5cfa4222u, 0xba6560feu, 0x8e3e1aa4u, 0xdbcc1d53u
};
static const uint32_t IGNEUM_CACHE_LAST[16] = {
0x41190d91u, 0xbd277957u, 0x22ddbb49u, 0x6986f207u, 0xdf69a4d6u, 0x26401a3au, 0x818230fbu, 0xc417122du,
0x3597b211u, 0xb553ce55u, 0xcf39cc0du, 0x3b7fc43au, 0x3fd43b00u, 0x67e1c80eu, 0xffa7ea7du, 0xca2960abu
};
static const uint64_t IGNEUM_CACHE_FNV64 = 0x48c4f5bf24166b2eull;

View file

@ -0,0 +1,36 @@
{
"seed": "igneum-readwidth/A/0",
"day": "2026-10-03",
"dataset_mode": "memory-hard",
"dataset_log2_words": 28,
"mask": "0x0fffffff",
"lanes": 32,
"source": "igneum-pow (Rust) CPU interpreter, generator v2, memory-hard dataset",
"warps": [
{"base_nonce": 0, "expected": [
"0xc02af7d21c32b6e2", "0xf9676c7b81e42590", "0x3b5b2f433e291756", "0x2307064009985ab7", "0xf1f2d84aa86f1f5a", "0xa7ed219296c204be", "0xf8105c9692ac08e7", "0x04377ca8373b8e1f",
"0x2f76672aa0a6104d", "0xac85110c18a8edc8", "0x3b4c20d7d3c22e52", "0x365c221488fa37e4", "0x989dab8f749cd976", "0x37f6690cc11dd5a7", "0x6159da4241e04ae2", "0x3c19bfdcd7821055",
"0x5b48a58c99800f4f", "0x991018ff7b067a45", "0xf281167cb9c28ef2", "0xe9497f5c0207892d", "0xac1ccf9060f7c78b", "0xf6f4282e68ceff58", "0xfa2694f87cf04ef6", "0x62d01632e98c6a11",
"0x63a875eb903ee9af", "0x680ad026ac9e5e1f", "0x63d80718c2e2912b", "0x377a3c901dadb9f5", "0xb2c39a01420d0806", "0x5ce44109e5c38357", "0x8532912b848b1f5b", "0x76141c17aa59c5f6"
]},
{"base_nonce": 4096, "expected": [
"0x30301278872d1766", "0xfd009372bb888e00", "0xb93e45944b9cb923", "0xe74cb4f099ac3ea9", "0x3cd9b2381d1cde3c", "0xb69bddb5df734350", "0xbccc2d57615b8465", "0xa30bbff25bf3d723",
"0x66ca1a4c82d500d0", "0xbb60ed823f1bee84", "0x6312dd68c839352f", "0xf15a1a4e9b902225", "0xcf315f5496b73b41", "0x5e6aa39724dbe6ca", "0x3a4be831dca92fd7", "0xbd5f1ae9b2892322",
"0x69582160f1bdf99c", "0xcc41242a8f4776a2", "0x377a337ca9a5f44a", "0x12e453c8c831a462", "0xfe278d9da6dcaf29", "0xfddd6db8998a7f00", "0x6f9d4db889ef7e98", "0x278294842317978d",
"0xe4bab477a44177b0", "0x52c1ea0d495c960d", "0xf1504ce7e9c0d818", "0x15673b7d791269d7", "0x18828b1a6fd35583", "0xa6d769abd44e3827", "0x82b838f1172afc66", "0x5b18a4842a119def"
]},
{"base_nonce": 1000000, "expected": [
"0xfd55a3c4be548e4e", "0x00f110e15155ab33", "0x374d6799a6b45a35", "0x27f9962d27ae0515", "0xd0ffb5d4c0f1dd83", "0x718d962e2ec9d7e7", "0xe07c572ca8705952", "0x99db60352bae63b7",
"0x065808eaebc7f7c8", "0x0072533c9ddc9f30", "0x7b11d0becd7297ed", "0xcf4b94c52549288a", "0x0e8d98977c965c26", "0x890c6664b01fb638", "0x4d0a4b30a326dca6", "0x948a02536dca9925",
"0x1d9b3442fd4c3e0a", "0x8b555804a68adabb", "0x4942826f941b2089", "0x967b94a90c51196e", "0xea8cf4b416d1878c", "0xbefa6e39bfc1860a", "0x53cbf72fe366ef94", "0xbfe204292b3f8b03",
"0xe269bfe9953bcf03", "0x24a758d6174e0da3", "0x25f57ab508d87895", "0xe45ec6208eb56f7d", "0x47c891dda3f102c6", "0x088698d191e8dc33", "0xc1c4cd437ae11388", "0xa17c529d1a26c64d"
]}
],
"dataset_head": ["0xffc3cd94", "0x5920ccd8", "0x392f44bb", "0x5e57f67a", "0x2f2bc2a9", "0x620b0e36", "0xbdc09014", "0x436654bf", "0x311e0b48", "0x1abd93ad", "0x59cc7ce8", "0xee5247b2", "0x86171fe8", "0x6d874751", "0xc9f7728f", "0x7c2a435d"],
"dataset_last_index": 268435455,
"dataset_last": "0xa33ada72",
"dataset_samples": [{"index": 59471966, "value": "0xe8b73d94"}, {"index": 217795994, "value": "0x337028b5"}, {"index": 208353206, "value": "0xafe148c9"}, {"index": 42483309, "value": "0xab99f7ae"}, {"index": 172547758, "value": "0x434ea619"}, {"index": 148076330, "value": "0xd85cb880"}, {"index": 183853158, "value": "0x54764c7f"}, {"index": 214389424, "value": "0x82c7e420"}, {"index": 267488061, "value": "0xedf4cb9e"}, {"index": 169781097, "value": "0x9884c959"}, {"index": 184093494, "value": "0x223ee793"}, {"index": 153880993, "value": "0x3a9ccf69"}, {"index": 84977930, "value": "0x81da4fd2"}, {"index": 46426879, "value": "0xd6ce8cb9"}, {"index": 3093825, "value": "0xe3922dca"}, {"index": 225364072, "value": "0x3e7e6bde"}, {"index": 44593546, "value": "0x382a3aca"}, {"index": 260713159, "value": "0x567e7f7f"}, {"index": 168250303, "value": "0x25a0f084"}, {"index": 52384140, "value": "0xbfeef128"}, {"index": 223401610, "value": "0xe338abfb"}, {"index": 45554030, "value": "0x7c3b5280"}, {"index": 95410555, "value": "0x909bc5f1"}, {"index": 175039924, "value": "0xd8b74b9c"}, {"index": 79171087, "value": "0x8e31a22e"}, {"index": 267580473, "value": "0x26b5f1d8"}, {"index": 24168642, "value": "0x79122c00"}, {"index": 37981670, "value": "0xcafc3340"}, {"index": 171551130, "value": "0xd5e02ea3"}, {"index": 195559979, "value": "0x1aee1afd"}, {"index": 204611762, "value": "0xdb090d9a"}, {"index": 140997658, "value": "0xb049f435"}, {"index": 138925853, "value": "0x4954d8ba"}, {"index": 86637313, "value": "0x03797ba0"}, {"index": 20736778, "value": "0x196eefbd"}, {"index": 219665210, "value": "0xd153412a"}, {"index": 160430336, "value": "0xbe5d2c4b"}, {"index": 264654675, "value": "0xdaa14f0e"}, {"index": 8013395, "value": "0x8e61ed07"}, {"index": 228945585, "value": "0x9e9a64c6"}, {"index": 213884386, "value": "0x2e29ff36"}, {"index": 104419827, "value": "0x392a8589"}, {"index": 44185464, "value": "0xb56a5912"}, {"index": 142737231, "value": "0xfa6e8b57"}, {"index": 99284897, "value": "0xd1a737cb"}, {"index": 132475900, "value": "0xb0fa841a"}, {"index": 61861762, "value": "0xbe1c341f"}, {"index": 132056166, "value": "0xe25be0f1"}, {"index": 262388043, "value": "0xe937f543"}, {"index": 91878046, "value": "0xebab2248"}, {"index": 117353561, "value": "0x8e1b607a"}, {"index": 124768597, "value": "0x202a2fed"}, {"index": 71352993, "value": "0x95e2819c"}, {"index": 190698941, "value": "0x9c9652d4"}, {"index": 46055428, "value": "0x32fedef0"}, {"index": 55281366, "value": "0xdecfff82"}, {"index": 165145231, "value": "0xcb5d43e5"}, {"index": 106810753, "value": "0xb735806a"}, {"index": 171985651, "value": "0x8905939c"}, {"index": 232085256, "value": "0xfbf8472d"}, {"index": 159510492, "value": "0xada74e5d"}, {"index": 40072060, "value": "0x7ebdeeea"}, {"index": 209107596, "value": "0x0119f2b3"}, {"index": 39023794, "value": "0xa9a376b8"}],
"cache_head": ["0x355a86d2", "0x7957db1c", "0xd21772af", "0x6fc1e09b", "0xd55ce61d", "0x6e6a278b", "0xd3f543ce", "0x223d8e82", "0x143ab337", "0x2e9f05bd", "0x2eb389bf", "0x0c6e449e", "0x5cfa4222", "0xba6560fe", "0x8e3e1aa4", "0xdbcc1d53"],
"cache_last_line": ["0x41190d91", "0xbd277957", "0x22ddbb49", "0x6986f207", "0xdf69a4d6", "0x26401a3a", "0x818230fb", "0xc417122d", "0x3597b211", "0xb553ce55", "0xcf39cc0d", "0x3b7fc43a", "0x3fd43b00", "0x67e1c80e", "0xffa7ea7d", "0xca2960ab"],
"cache_fnv1a64": "0x48c4f5bf24166b2e"
}

View file

@ -0,0 +1,278 @@
// Generated by igneum-pow export (generator v2) for seed "igneum-readwidth/A/1". Do not edit by hand.
// OpenCL C twin of the Metal kernel for the same seed (see proto-opencl/README.md, WAVEFRONT.md and program.metal).
// Built from source at runtime by proto-opencl/host.c, which passes these defines:
// IGNEUM_GROUP work-group size of igneum_hash, a multiple of 32 (default 32: one work-group = one 32-lane unit)
// IGNEUM_EXCHANGE 0 = local-memory exchange with a barrier (any device, any wave width; the default)
// 1 = sub_group_shuffle_xor (cl_khr_subgroup_shuffle), only with IGNEUM_GROUP 32 and a sub-group size of exactly 32
// 2 = intel_sub_group_shuffle_xor (cl_intel_subgroups), same condition
// The verification unit is always 32 lanes. A 64-wide hardware wave (AMD GCN/CDNA, RDNA in wave64) runs two units;
// the exchange masks are 1, 2, 4, 8, 16, so every partner lane lies inside the lane's own aligned run of 32.
#ifndef IGNEUM_GROUP
#define IGNEUM_GROUP 32
#endif
#ifndef IGNEUM_EXCHANGE
#define IGNEUM_EXCHANGE 0
#endif
#ifdef __OPENCL_VERSION__
#define IGNEUM_KERNEL_HASH __kernel __attribute__((reqd_work_group_size(IGNEUM_GROUP, 1, 1)))
#define IGNEUM_LOCAL_WORDS(name, n) __local uint name[n]
#if IGNEUM_EXCHANGE == 1
#ifdef cl_khr_subgroups
#pragma OPENCL EXTENSION cl_khr_subgroups : enable
#endif
#ifdef cl_khr_subgroup_shuffle
#pragma OPENCL EXTENSION cl_khr_subgroup_shuffle : enable
#endif
#elif IGNEUM_EXCHANGE == 2
#pragma OPENCL EXTENSION cl_intel_subgroups : enable
#endif
#else
// Not an OpenCL compiler: proto-opencl/emu compiles this file as C++ and supplies the built-ins and these two macros.
#include "emu_opencl.h"
#endif
#if IGNEUM_EXCHANGE == 1
#define IGNEUM_SHFL_XOR(dst, a, m) dst = sub_group_shuffle_xor((a), (uint)(m))
#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u)
#elif IGNEUM_EXCHANGE == 2
#define IGNEUM_SHFL_XOR(dst, a, m) dst = intel_sub_group_shuffle_xor((a), (uint)(m))
#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u)
#else
// Local-memory exchange. Two buffers of IGNEUM_GROUP words alternate (xk counts exchanges), so one barrier per
// exchange is enough: a lane can only overwrite buffer b at exchange k+2 after passing barrier k+1, and every lane
// reaches barrier k+1 only after its read of buffer b at exchange k. The partner lid ^ m stays inside the lane's
// aligned run of 32 because m < 32. Control flow is uniform, so every work-item reaches every barrier.
#define IGNEUM_SHFL_XOR(dst, a, m) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid ^ (uint)(m))]; xk += 1u; }
#define IGNEUM_BCAST0(dst, a) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid & ~31u)]; xk += 1u; }
#endif
static inline uint splitmix32(uint 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. OpenCL rotate() rotates left by n modulo 32.
static inline uint rotl_imm(uint x, uint n) { return rotate(x, n); }
// Right rotation by n modulo 32 as a left rotation by (32 - n) modulo 32; n == 0 gives x.
static inline uint rotr_var(uint x, uint n) { return rotate(x, (0u - n) & 31u); }
static 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;
}
// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines.
// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals.
#define MH_CACHE_LINE_MASK 0x003fffffu
#define MH_SEGMENT_LINES 64u
#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); }
static inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site
// y = ChaCha12 core(x) + x
static inline void mh_chacha_block(const uint* x, uint* y) {
for (uint i = 0u; i < 16u; ++i) y[i] = x[i];
for (uint r = 0u; r < 6u; ++r) {
MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u)
MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u)
}
for (uint i = 0u; i < 16u; ++i) y[i] += x[i];
}
// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0.
static inline void mh_cache_segment(__global uint* cache, uint seg) {
uint prev[16]; uint x[16]; uint y[16];
for (uint i = 0u; i < 16u; ++i) prev[i] = 0u;
for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) {
x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3];
x[4] = 0x3067619fu ^ prev[4];
x[5] = 0x3c269176u ^ prev[5];
x[6] = 0x84a03b03u ^ prev[6];
x[7] = 0xf8c63294u ^ prev[7];
x[8] = 0xff977c5bu ^ prev[8];
x[9] = 0xe60def3eu ^ prev[9];
x[10] = 0x63630141u ^ prev[10];
x[11] = 0xb8fbcb58u ^ prev[11];
x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15];
mh_chacha_block(x, y);
__global uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u);
for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; }
}
}
// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations.
static inline void mh_mixer(uint* s, uint rk) {
s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u;
s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu;
s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u;
s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu;
s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u;
s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u;
s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u;
s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u;
s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du;
s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u;
s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du;
s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu;
s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du;
s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu;
s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u;
s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u;
MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u)
MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u)
MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u)
MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u)
}
// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer.
static inline void mh_item(__global const uint* cache, uint t, uint* s) {
s[0] = 0x3067619fu;
s[1] = 0x3c269176u;
s[2] = 0x84a03b03u;
s[3] = 0xf8c63294u;
s[4] = 0xff977c5bu;
s[5] = 0xe60def3eu;
s[6] = 0x63630141u;
s[7] = 0xb8fbcb58u;
s[8] = t * 0x42146205u + 0xbab68293u;
s[9] = t * 0x52cbe0fbu + 0xcc162340u;
s[10] = t * 0x7ecf4a03u + 0x6ce151ccu;
s[11] = t * 0x6728907fu + 0xe62b8997u;
s[12] = t * 0xd81d9751u + 0xc9c80297u;
s[13] = t * 0x132952c3u + 0xf74a1654u;
s[14] = t * 0xf60de277u + 0x3d704af5u;
s[15] = t * 0x05358035u + 0x3cf522b7u;
for (uint r = 0u; r < 8u; ++r) {
mh_mixer(s, 0x9E3779B9u * (r + 1u));
__global const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u);
for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i];
}
mh_mixer(s, 0x9E3779B9u * 9u);
}
// dataset[w] without the dataset: derive item w >> 4 and take word w & 15.
static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; }
// Memory-hard dataset (MEMHARD.md). One work-item per cache segment; one work-item per 64-byte dataset item.
// The same constants as memhard.h in this pack (one emitter, three dialects).
__kernel void igneum_cache_fill(__global uint* cache, uint nSegments) {
uint seg = (uint)get_global_id(0);
if (seg < nSegments) mh_cache_segment(cache, seg);
}
__kernel void igneum_build(__global uint* ds, __global const uint* cache, uint nItems) {
uint t = (uint)get_global_id(0);
if (t < nItems) {
uint s[16];
mh_item(cache, t, s);
__global uint* d = ds + ((ulong)t * 16u);
for (uint i = 0u; i < 16u; ++i) d[i] = s[i];
}
}
// One hash per work-item. IGNEUM_GROUP is a multiple of 32; lane = lid & 31 and every exchange stays inside the
// lane's own aligned run of 32 work-items, exactly like simd_shuffle_xor inside a 32-wide Metal SIMD group and
// __shfl_xor_sync inside a CUDA warp. Control flow is uniform (no branches at all).
IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask) {
uint gid = (uint)get_global_id(0);
uint lid = (uint)get_local_id(0);
uint nonce = baseNonce + gid;
uint r0, r1, r2, r3, r4, r5, r6, r7;
#if IGNEUM_EXCHANGE == 0
IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP);
uint xk = 0u;
#else
(void)lid;
#endif
{ uint x = nonce ^ 0xa7198abfu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x6cd0f8cfu; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1]
{ uint x = nonce ^ 0x6cd0f8cfu; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0xe4ef8ebfu; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2]
{ uint x = nonce ^ 0xe4ef8ebfu; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x03ee1e65u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3]
{ uint x = nonce ^ 0x03ee1e65u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xcfcfc5c0u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4]
{ uint x = nonce ^ 0xcfcfc5c0u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0x82e9e19bu; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5]
{ uint x = nonce ^ 0x82e9e19bu; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0x5d7a8a2fu; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6]
{ uint x = nonce ^ 0x5d7a8a2fu; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0xfafccd93u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7]
{ uint x = nonce ^ 0xfafccd93u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0xa7198abfu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0]
for (uint it = 0u; it < 8u; ++it) {
uint sel = r0;
r4 = r4 + r3 + ((((sel >> 8u) & 1u) != 0u) ? 0x5b2d5fe3u : 0x4a4c6caau); // 0 add
r7 = r7 | r6; // 1 or
r2 = r2 * r7; // 2 mul
{ uint t_; IGNEUM_SHFL_XOR(t_, r1, 2u); r5 = r5 ^ t_; } // 3 shfl
r4 = r4 ^ r6; // 4 xor
r0 = r0 ^ ds[r4 & mask]; // 5 load
r3 = rotl_imm(r3, 17u); // 6 rotl
r1 = r1 + r4 + ((((sel >> 27u) & 1u) != 0u) ? 0x83aa2c52u : 0xaeb38cc5u); // 7 add
r1 = rotl_imm(r1, 31u); // 8 rotl
r4 = r4 ^ r7; // 9 xor
{ uint b_ = (r0 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r6 = x_; } // 10 load
{ uint b_ = (r6 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r0 = x_; } // 11 load
{ uint t_; IGNEUM_SHFL_XOR(t_, r2, 1u); r5 = r5 ^ t_; } // 12 shfl
{ uint t_; IGNEUM_SHFL_XOR(t_, r4, 2u); r1 = r1 ^ t_; } // 13 shfl
r5 = r5 * r7; // 14 mul
r3 = r3 ^ r0; // 15 xor
r5 = r5 * r4; // 16 mul
{ uint t_; IGNEUM_SHFL_XOR(t_, r0, 4u); r2 = r2 ^ t_; } // 17 shfl
r4 = r4 + r1 + ((((sel >> 31u) & 1u) != 0u) ? 0xfb36bddau : 0x87d3a998u); // 18 add
r0 = r0 + r1 + ((((sel >> 22u) & 1u) != 0u) ? 0xf2ef7076u : 0x88921092u); // 19 add
r6 = r6 + r1 + ((((sel >> 3u) & 1u) != 0u) ? 0xe42e69c6u : 0x35e06b74u); // 20 add
{ uint t_; IGNEUM_SHFL_XOR(t_, r5, 8u); r2 = r2 ^ t_; } // 21 shfl
r3 = r3 + r6 + ((((sel >> 3u) & 1u) != 0u) ? 0xb45d9671u : 0x74df5734u); // 22 add
{ uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r5 = r5 ^ t_; } // 23 shfl
r4 = r4 ^ ds[r2 & mask]; // 24 load
r2 = r2 ^ r1; // 25 xor
r7 = r7 * r6; // 26 mul
{ uint b_ = (r3 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r2 = x_; } // 27 load
r3 = rotl_imm(r3, 29u); // 28 rotl
{ uint t_; IGNEUM_SHFL_XOR(t_, r0, 8u); r3 = r3 ^ t_; } // 29 shfl
r1 = r1 ^ ds[r4 & mask]; // 30 load
r4 = r4 - r6; // 31 sub
r2 = r2 * r4; // 32 mul
r4 = r3 * r3 + r4; // 33 mad
r0 = r0 + r6 + ((((sel >> 15u) & 1u) != 0u) ? 0x97d2762du : 0x19318d72u); // 34 add
r7 = r7 + r1 + ((((sel >> 19u) & 1u) != 0u) ? 0x79830eadu : 0x26b63296u); // 35 add
r7 = r7 ^ ds[r2 & mask]; // 36 load
r0 = rotr_var(r0, r7); // 37 rotr
r2 = r2 + r7 + ((((sel >> 28u) & 1u) != 0u) ? 0x23c8dec4u : 0xb228dc81u); // 38 add
r0 = r0 ^ ds[r5 & mask]; // 39 load
{ uint t_; IGNEUM_SHFL_XOR(t_, r5, 2u); r7 = r7 ^ t_; } // 40 shfl
r5 = r0 * r1 + r5; // 41 mad
r2 = r2 ^ r3; // 42 xor
r7 = r7 ^ ds[r0 & mask]; // 43 load
{ uint b_ = (r5 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r0 = x_; } // 44 load
r2 = r2 + r7 + ((((sel >> 22u) & 1u) != 0u) ? 0x5a3a7fe1u : 0xcc64df8eu); // 45 add
r0 = rotr_var(r0, r5); // 46 rotr
r3 = rotr_var(r3, r7); // 47 rotr
r2 = r7 * r6 + r2; // 48 mad
r6 = r3 * r5 + r6; // 49 mad
r1 = r1 ^ ds[r6 & mask]; // 50 load
r7 = r7 ^ ds[r1 & mask]; // 51 load
r3 = r3 + r4 + ((((sel >> 6u) & 1u) != 0u) ? 0xcba22643u : 0x7a646d78u); // 52 add
r4 = r4 ^ r0; // 53 xor
{ uint t_; IGNEUM_SHFL_XOR(t_, r7, 4u); r3 = r3 ^ t_; } // 54 shfl
r0 = rotr_var(r0, r1); // 55 rotr
r5 = r5 ^ r3; // 56 xor
r1 = r1 + r0 + ((((sel >> 8u) & 1u) != 0u) ? 0x1834af00u : 0x6f26b909u); // 57 add
r6 = r6 ^ ds[r4 & mask]; // 58 load
{ uint b_ = (r7 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r1 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r1 = x_; } // 59 load
r1 = r1 ^ ds[r0 & mask]; // 60 load
r4 = r4 ^ r7; // 61 xor
r6 = r2 * r1 + r6; // 62 mad
{ uint b_ = (r2 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r3 = x_; } // 63 load
}
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;
}
#if IGNEUM_EXCHANGE != 0
// Reports the sub-group size this device uses for a work-group of IGNEUM_GROUP items. host.c runs it only when the
// per-kernel query (clGetKernelSubGroupInfoKHR on igneum_hash) is unavailable; that query is preferred because a
// compiler may pick a different wave width per kernel (RDNA: wave32 or wave64). See WAVEFRONT.md.
IGNEUM_KERNEL_HASH void igneum_probe_subgroup(__global uint* out) {
if (get_local_id(0) == 0u) { out[0] = get_sub_group_size(); out[1] = get_num_sub_groups(); }
}
#endif

View file

@ -0,0 +1,164 @@
// Generated by igneum-pow export (generator v2) for seed "igneum-readwidth/A/1". 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"
#include "memhard.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;
}
// Memory-hard dataset (MEMHARD.md). One thread per cache segment; one thread per 64-byte dataset item.
// The core functions (mh_cache_segment, mh_item) are in memhard.h and are also compiled for the host.
__global__ void igneum_cache_fill(uint32_t* cache, uint32_t nSegments) {
uint32_t seg = blockIdx.x * blockDim.x + threadIdx.x;
if (seg < nSegments) mh_cache_segment(cache, seg);
}
__global__ void igneum_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems) {
uint32_t t = blockIdx.x * blockDim.x + threadIdx.x;
if (t < nItems) {
uint32_t s[16];
mh_item(cache, t, s);
uint32_t* d = ds + (size_t)t * 16u;
for (uint32_t i = 0u; i < 16u; ++i) d[i] = s[i];
}
}
// 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 ^ 0xa7198abfu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x6cd0f8cfu; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1]
{ uint32_t x = nonce ^ 0x6cd0f8cfu; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0xe4ef8ebfu; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2]
{ uint32_t x = nonce ^ 0xe4ef8ebfu; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x03ee1e65u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3]
{ uint32_t x = nonce ^ 0x03ee1e65u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xcfcfc5c0u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4]
{ uint32_t x = nonce ^ 0xcfcfc5c0u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0x82e9e19bu; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5]
{ uint32_t x = nonce ^ 0x82e9e19bu; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0x5d7a8a2fu; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6]
{ uint32_t x = nonce ^ 0x5d7a8a2fu; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0xfafccd93u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7]
{ uint32_t x = nonce ^ 0xfafccd93u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0xa7198abfu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0]
for (uint32_t it = 0u; it < 8u; ++it) {
uint32_t sel = r0;
r4 = r4 + r3 + ((((sel >> 8u) & 1u) != 0u) ? 0x5b2d5fe3u : 0x4a4c6caau); // 0 add
r7 = r7 | r6; // 1 or
r2 = r2 * r7; // 2 mul
r5 = r5 ^ __shfl_xor_sync(0xffffffffu, r1, 2); // 3 shfl
r4 = r4 ^ r6; // 4 xor
r0 = r0 ^ ds[r4 & mask]; // 5 load
r3 = rotl_imm(r3, 17u); // 6 rotl
r1 = r1 + r4 + ((((sel >> 27u) & 1u) != 0u) ? 0x83aa2c52u : 0xaeb38cc5u); // 7 add
r1 = rotl_imm(r1, 31u); // 8 rotl
r4 = r4 ^ r7; // 9 xor
{ uint32_t b_ = (r0 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r6 = x_; } // 10 load
{ uint32_t b_ = (r6 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r0 = x_; } // 11 load
r5 = r5 ^ __shfl_xor_sync(0xffffffffu, r2, 1); // 12 shfl
r1 = r1 ^ __shfl_xor_sync(0xffffffffu, r4, 2); // 13 shfl
r5 = r5 * r7; // 14 mul
r3 = r3 ^ r0; // 15 xor
r5 = r5 * r4; // 16 mul
r2 = r2 ^ __shfl_xor_sync(0xffffffffu, r0, 4); // 17 shfl
r4 = r4 + r1 + ((((sel >> 31u) & 1u) != 0u) ? 0xfb36bddau : 0x87d3a998u); // 18 add
r0 = r0 + r1 + ((((sel >> 22u) & 1u) != 0u) ? 0xf2ef7076u : 0x88921092u); // 19 add
r6 = r6 + r1 + ((((sel >> 3u) & 1u) != 0u) ? 0xe42e69c6u : 0x35e06b74u); // 20 add
r2 = r2 ^ __shfl_xor_sync(0xffffffffu, r5, 8); // 21 shfl
r3 = r3 + r6 + ((((sel >> 3u) & 1u) != 0u) ? 0xb45d9671u : 0x74df5734u); // 22 add
r5 = r5 ^ __shfl_xor_sync(0xffffffffu, r4, 8); // 23 shfl
r4 = r4 ^ ds[r2 & mask]; // 24 load
r2 = r2 ^ r1; // 25 xor
r7 = r7 * r6; // 26 mul
{ uint32_t b_ = (r3 & mask) & ~15u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint32_t x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r2 = x_; } // 27 load
r3 = rotl_imm(r3, 29u); // 28 rotl
r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r0, 8); // 29 shfl
r1 = r1 ^ ds[r4 & mask]; // 30 load
r4 = r4 - r6; // 31 sub
r2 = r2 * r4; // 32 mul
r4 = r3 * r3 + r4; // 33 mad
r0 = r0 + r6 + ((((sel >> 15u) & 1u) != 0u) ? 0x97d2762du : 0x19318d72u); // 34 add
r7 = r7 + r1 + ((((sel >> 19u) & 1u) != 0u) ? 0x79830eadu : 0x26b63296u); // 35 add
r7 = r7 ^ ds[r2 & mask]; // 36 load
r0 = rotr_var(r0, r7); // 37 rotr
r2 = r2 + r7 + ((((sel >> 28u) & 1u) != 0u) ? 0x23c8dec4u : 0xb228dc81u); // 38 add
r0 = r0 ^ ds[r5 & mask]; // 39 load
r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r5, 2); // 40 shfl
r5 = r0 * r1 + r5; // 41 mad
r2 = r2 ^ r3; // 42 xor
r7 = r7 ^ ds[r0 & mask]; // 43 load
{ uint32_t b_ = (r5 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r0 = x_; } // 44 load
r2 = r2 + r7 + ((((sel >> 22u) & 1u) != 0u) ? 0x5a3a7fe1u : 0xcc64df8eu); // 45 add
r0 = rotr_var(r0, r5); // 46 rotr
r3 = rotr_var(r3, r7); // 47 rotr
r2 = r7 * r6 + r2; // 48 mad
r6 = r3 * r5 + r6; // 49 mad
r1 = r1 ^ ds[r6 & mask]; // 50 load
r7 = r7 ^ ds[r1 & mask]; // 51 load
r3 = r3 + r4 + ((((sel >> 6u) & 1u) != 0u) ? 0xcba22643u : 0x7a646d78u); // 52 add
r4 = r4 ^ r0; // 53 xor
r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r7, 4); // 54 shfl
r0 = rotr_var(r0, r1); // 55 rotr
r5 = r5 ^ r3; // 56 xor
r1 = r1 + r0 + ((((sel >> 8u) & 1u) != 0u) ? 0x1834af00u : 0x6f26b909u); // 57 add
r6 = r6 ^ ds[r4 & mask]; // 58 load
{ uint32_t b_ = (r7 & mask) & ~15u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint32_t x_ = r1 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r1 = x_; } // 59 load
r1 = r1 ^ ds[r0 & mask]; // 60 load
r4 = r4 ^ r7; // 61 xor
r6 = r2 * r1 + r6; // 62 mad
{ uint32_t b_ = (r2 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r3 = x_; } // 63 load
}
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_cache_fill(uint32_t* cache, uint32_t nSegments) {
if (nSegments == 0u) return cudaErrorInvalidValue;
uint32_t block = 256u;
uint32_t grid = (nSegments + block - 1u) / block;
igneum_cache_fill<<<grid, block>>>(cache, nSegments);
return cudaGetLastError();
}
cudaError_t igneum_launch_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems) {
if (nItems == 0u) return cudaErrorInvalidValue;
uint32_t block = 256u;
uint32_t grid = (nItems + block - 1u) / block;
igneum_build<<<grid, block>>>(ds, cache, nItems);
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);
}

View file

@ -0,0 +1,372 @@
// Generated by igneum-pow export (generator v2) for seed "igneum-readwidth/A/1". Do not edit by hand.
// OpenCL C twin of the Metal kernel for the same seed (see proto-opencl/README.md, WAVEFRONT.md and program.metal).
// Built from source at runtime by proto-opencl/host.c, which passes these defines:
// IGNEUM_GROUP work-group size of igneum_hash, a multiple of 32 (default 32: one work-group = one 32-lane unit)
// IGNEUM_EXCHANGE 0 = local-memory exchange with a barrier (any device, any wave width; the default)
// 1 = sub_group_shuffle_xor (cl_khr_subgroup_shuffle), only with IGNEUM_GROUP 32 and a sub-group size of exactly 32
// 2 = intel_sub_group_shuffle_xor (cl_intel_subgroups), same condition
// The verification unit is always 32 lanes. A 64-wide hardware wave (AMD GCN/CDNA, RDNA in wave64) runs two units;
// the exchange masks are 1, 2, 4, 8, 16, so every partner lane lies inside the lane's own aligned run of 32.
#ifndef IGNEUM_GROUP
#define IGNEUM_GROUP 32
#endif
#ifndef IGNEUM_EXCHANGE
#define IGNEUM_EXCHANGE 0
#endif
#ifdef __OPENCL_VERSION__
#define IGNEUM_KERNEL_HASH __kernel __attribute__((reqd_work_group_size(IGNEUM_GROUP, 1, 1)))
#define IGNEUM_LOCAL_WORDS(name, n) __local uint name[n]
#if IGNEUM_EXCHANGE == 1
#ifdef cl_khr_subgroups
#pragma OPENCL EXTENSION cl_khr_subgroups : enable
#endif
#ifdef cl_khr_subgroup_shuffle
#pragma OPENCL EXTENSION cl_khr_subgroup_shuffle : enable
#endif
#elif IGNEUM_EXCHANGE == 2
#pragma OPENCL EXTENSION cl_intel_subgroups : enable
#endif
#else
// Not an OpenCL compiler: proto-opencl/emu compiles this file as C++ and supplies the built-ins and these two macros.
#include "emu_opencl.h"
#endif
#if IGNEUM_EXCHANGE == 1
#define IGNEUM_SHFL_XOR(dst, a, m) dst = sub_group_shuffle_xor((a), (uint)(m))
#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u)
#elif IGNEUM_EXCHANGE == 2
#define IGNEUM_SHFL_XOR(dst, a, m) dst = intel_sub_group_shuffle_xor((a), (uint)(m))
#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u)
#else
// Local-memory exchange. Two buffers of IGNEUM_GROUP words alternate (xk counts exchanges), so one barrier per
// exchange is enough: a lane can only overwrite buffer b at exchange k+2 after passing barrier k+1, and every lane
// reaches barrier k+1 only after its read of buffer b at exchange k. The partner lid ^ m stays inside the lane's
// aligned run of 32 because m < 32. Control flow is uniform, so every work-item reaches every barrier.
#define IGNEUM_SHFL_XOR(dst, a, m) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid ^ (uint)(m))]; xk += 1u; }
#define IGNEUM_BCAST0(dst, a) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid & ~31u)]; xk += 1u; }
#endif
static inline uint splitmix32(uint 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. OpenCL rotate() rotates left by n modulo 32.
static inline uint rotl_imm(uint x, uint n) { return rotate(x, n); }
// Right rotation by n modulo 32 as a left rotation by (32 - n) modulo 32; n == 0 gives x.
static inline uint rotr_var(uint x, uint n) { return rotate(x, (0u - n) & 31u); }
static 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;
}
// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines.
// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals.
#define MH_CACHE_LINE_MASK 0x003fffffu
#define MH_SEGMENT_LINES 64u
#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); }
static inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site
// y = ChaCha12 core(x) + x
static inline void mh_chacha_block(const uint* x, uint* y) {
for (uint i = 0u; i < 16u; ++i) y[i] = x[i];
for (uint r = 0u; r < 6u; ++r) {
MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u)
MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u)
}
for (uint i = 0u; i < 16u; ++i) y[i] += x[i];
}
// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0.
static inline void mh_cache_segment(__global uint* cache, uint seg) {
uint prev[16]; uint x[16]; uint y[16];
for (uint i = 0u; i < 16u; ++i) prev[i] = 0u;
for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) {
x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3];
x[4] = 0x3067619fu ^ prev[4];
x[5] = 0x3c269176u ^ prev[5];
x[6] = 0x84a03b03u ^ prev[6];
x[7] = 0xf8c63294u ^ prev[7];
x[8] = 0xff977c5bu ^ prev[8];
x[9] = 0xe60def3eu ^ prev[9];
x[10] = 0x63630141u ^ prev[10];
x[11] = 0xb8fbcb58u ^ prev[11];
x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15];
mh_chacha_block(x, y);
__global uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u);
for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; }
}
}
// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations.
static inline void mh_mixer(uint* s, uint rk) {
s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u;
s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu;
s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u;
s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu;
s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u;
s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u;
s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u;
s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u;
s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du;
s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u;
s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du;
s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu;
s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du;
s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu;
s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u;
s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u;
MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u)
MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u)
MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u)
MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u)
}
// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer.
static inline void mh_item(__global const uint* cache, uint t, uint* s) {
s[0] = 0x3067619fu;
s[1] = 0x3c269176u;
s[2] = 0x84a03b03u;
s[3] = 0xf8c63294u;
s[4] = 0xff977c5bu;
s[5] = 0xe60def3eu;
s[6] = 0x63630141u;
s[7] = 0xb8fbcb58u;
s[8] = t * 0x42146205u + 0xbab68293u;
s[9] = t * 0x52cbe0fbu + 0xcc162340u;
s[10] = t * 0x7ecf4a03u + 0x6ce151ccu;
s[11] = t * 0x6728907fu + 0xe62b8997u;
s[12] = t * 0xd81d9751u + 0xc9c80297u;
s[13] = t * 0x132952c3u + 0xf74a1654u;
s[14] = t * 0xf60de277u + 0x3d704af5u;
s[15] = t * 0x05358035u + 0x3cf522b7u;
for (uint r = 0u; r < 8u; ++r) {
mh_mixer(s, 0x9E3779B9u * (r + 1u));
__global const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u);
for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i];
}
mh_mixer(s, 0x9E3779B9u * 9u);
}
// dataset[w] without the dataset: derive item w >> 4 and take word w & 15.
static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; }
// Memory-hard dataset (MEMHARD.md). One work-item per cache segment; one work-item per 64-byte dataset item.
// The same constants as memhard.h in this pack (one emitter, three dialects).
__kernel void igneum_cache_fill(__global uint* cache, uint nSegments) {
uint seg = (uint)get_global_id(0);
if (seg < nSegments) mh_cache_segment(cache, seg);
}
__kernel void igneum_build(__global uint* ds, __global const uint* cache, uint nItems) {
uint t = (uint)get_global_id(0);
if (t < nItems) {
uint s[16];
mh_item(cache, t, s);
__global uint* d = ds + ((ulong)t * 16u);
for (uint i = 0u; i < 16u; ++i) d[i] = s[i];
}
}
// One hash per work-item. IGNEUM_GROUP is a multiple of 32; lane = lid & 31 and every exchange stays inside the
// lane's own aligned run of 32 work-items, exactly like simd_shuffle_xor inside a 32-wide Metal SIMD group and
// __shfl_xor_sync inside a CUDA warp. Control flow is uniform (no branches at all).
IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask) {
uint gid = (uint)get_global_id(0);
uint lid = (uint)get_local_id(0);
uint nonce = baseNonce + gid;
uint r0, r1, r2, r3, r4, r5, r6, r7;
#if IGNEUM_EXCHANGE == 0
IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP);
uint xk = 0u;
#else
(void)lid;
#endif
{ uint x = nonce ^ 0xa7198abfu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x6cd0f8cfu; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1]
{ uint x = nonce ^ 0x6cd0f8cfu; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0xe4ef8ebfu; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2]
{ uint x = nonce ^ 0xe4ef8ebfu; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x03ee1e65u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3]
{ uint x = nonce ^ 0x03ee1e65u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xcfcfc5c0u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4]
{ uint x = nonce ^ 0xcfcfc5c0u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0x82e9e19bu; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5]
{ uint x = nonce ^ 0x82e9e19bu; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0x5d7a8a2fu; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6]
{ uint x = nonce ^ 0x5d7a8a2fu; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0xfafccd93u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7]
{ uint x = nonce ^ 0xfafccd93u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0xa7198abfu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0]
for (uint it = 0u; it < 8u; ++it) {
uint sel = r0;
r4 = r4 + r3 + ((((sel >> 8u) & 1u) != 0u) ? 0x5b2d5fe3u : 0x4a4c6caau); // 0 add
r7 = r7 | r6; // 1 or
r2 = r2 * r7; // 2 mul
{ uint t_; IGNEUM_SHFL_XOR(t_, r1, 2u); r5 = r5 ^ t_; } // 3 shfl
r4 = r4 ^ r6; // 4 xor
r0 = r0 ^ ds[r4 & mask]; // 5 load
r3 = rotl_imm(r3, 17u); // 6 rotl
r1 = r1 + r4 + ((((sel >> 27u) & 1u) != 0u) ? 0x83aa2c52u : 0xaeb38cc5u); // 7 add
r1 = rotl_imm(r1, 31u); // 8 rotl
r4 = r4 ^ r7; // 9 xor
{ uint b_ = (r0 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r6 = x_; } // 10 load
{ uint b_ = (r6 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r0 = x_; } // 11 load
{ uint t_; IGNEUM_SHFL_XOR(t_, r2, 1u); r5 = r5 ^ t_; } // 12 shfl
{ uint t_; IGNEUM_SHFL_XOR(t_, r4, 2u); r1 = r1 ^ t_; } // 13 shfl
r5 = r5 * r7; // 14 mul
r3 = r3 ^ r0; // 15 xor
r5 = r5 * r4; // 16 mul
{ uint t_; IGNEUM_SHFL_XOR(t_, r0, 4u); r2 = r2 ^ t_; } // 17 shfl
r4 = r4 + r1 + ((((sel >> 31u) & 1u) != 0u) ? 0xfb36bddau : 0x87d3a998u); // 18 add
r0 = r0 + r1 + ((((sel >> 22u) & 1u) != 0u) ? 0xf2ef7076u : 0x88921092u); // 19 add
r6 = r6 + r1 + ((((sel >> 3u) & 1u) != 0u) ? 0xe42e69c6u : 0x35e06b74u); // 20 add
{ uint t_; IGNEUM_SHFL_XOR(t_, r5, 8u); r2 = r2 ^ t_; } // 21 shfl
r3 = r3 + r6 + ((((sel >> 3u) & 1u) != 0u) ? 0xb45d9671u : 0x74df5734u); // 22 add
{ uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r5 = r5 ^ t_; } // 23 shfl
r4 = r4 ^ ds[r2 & mask]; // 24 load
r2 = r2 ^ r1; // 25 xor
r7 = r7 * r6; // 26 mul
{ uint b_ = (r3 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r2 = x_; } // 27 load
r3 = rotl_imm(r3, 29u); // 28 rotl
{ uint t_; IGNEUM_SHFL_XOR(t_, r0, 8u); r3 = r3 ^ t_; } // 29 shfl
r1 = r1 ^ ds[r4 & mask]; // 30 load
r4 = r4 - r6; // 31 sub
r2 = r2 * r4; // 32 mul
r4 = r3 * r3 + r4; // 33 mad
r0 = r0 + r6 + ((((sel >> 15u) & 1u) != 0u) ? 0x97d2762du : 0x19318d72u); // 34 add
r7 = r7 + r1 + ((((sel >> 19u) & 1u) != 0u) ? 0x79830eadu : 0x26b63296u); // 35 add
r7 = r7 ^ ds[r2 & mask]; // 36 load
r0 = rotr_var(r0, r7); // 37 rotr
r2 = r2 + r7 + ((((sel >> 28u) & 1u) != 0u) ? 0x23c8dec4u : 0xb228dc81u); // 38 add
r0 = r0 ^ ds[r5 & mask]; // 39 load
{ uint t_; IGNEUM_SHFL_XOR(t_, r5, 2u); r7 = r7 ^ t_; } // 40 shfl
r5 = r0 * r1 + r5; // 41 mad
r2 = r2 ^ r3; // 42 xor
r7 = r7 ^ ds[r0 & mask]; // 43 load
{ uint b_ = (r5 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r0 = x_; } // 44 load
r2 = r2 + r7 + ((((sel >> 22u) & 1u) != 0u) ? 0x5a3a7fe1u : 0xcc64df8eu); // 45 add
r0 = rotr_var(r0, r5); // 46 rotr
r3 = rotr_var(r3, r7); // 47 rotr
r2 = r7 * r6 + r2; // 48 mad
r6 = r3 * r5 + r6; // 49 mad
r1 = r1 ^ ds[r6 & mask]; // 50 load
r7 = r7 ^ ds[r1 & mask]; // 51 load
r3 = r3 + r4 + ((((sel >> 6u) & 1u) != 0u) ? 0xcba22643u : 0x7a646d78u); // 52 add
r4 = r4 ^ r0; // 53 xor
{ uint t_; IGNEUM_SHFL_XOR(t_, r7, 4u); r3 = r3 ^ t_; } // 54 shfl
r0 = rotr_var(r0, r1); // 55 rotr
r5 = r5 ^ r3; // 56 xor
r1 = r1 + r0 + ((((sel >> 8u) & 1u) != 0u) ? 0x1834af00u : 0x6f26b909u); // 57 add
r6 = r6 ^ ds[r4 & mask]; // 58 load
{ uint b_ = (r7 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r1 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r1 = x_; } // 59 load
r1 = r1 ^ ds[r0 & mask]; // 60 load
r4 = r4 ^ r7; // 61 xor
r6 = r2 * r1 + r6; // 62 mad
{ uint b_ = (r2 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r3 = x_; } // 63 load
}
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;
}
#if IGNEUM_EXCHANGE != 0
// Reports the sub-group size this device uses for a work-group of IGNEUM_GROUP items. host.c runs it only when the
// per-kernel query (clGetKernelSubGroupInfoKHR on igneum_hash) is unavailable; that query is preferred because a
// compiler may pick a different wave width per kernel (RDNA: wave32 or wave64). See WAVEFRONT.md.
IGNEUM_KERNEL_HASH void igneum_probe_subgroup(__global uint* out) {
if (get_local_id(0) == 0u) { out[0] = get_sub_group_size(); out[1] = get_num_sub_groups(); }
}
#endif
// Header-bound variant (bind.rs): the init words come from initw, not SEEDW. Same body as igneum_hash.
IGNEUM_KERNEL_HASH void igneum_hash_bound(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask, __global const uint* initw) {
uint gid = (uint)get_global_id(0);
uint lid = (uint)get_local_id(0);
uint nonce = baseNonce + gid;
uint r0, r1, r2, r3, r4, r5, r6, r7;
uint iw0 = initw[0], iw1 = initw[1], iw2 = initw[2], iw3 = initw[3], iw4 = initw[4], iw5 = initw[5], iw6 = initw[6], iw7 = initw[7];
#if IGNEUM_EXCHANGE == 0
IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP);
uint xk = 0u;
#else
(void)lid;
#endif
{ uint x = nonce ^ iw0; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ iw1; }
{ uint x = nonce ^ iw1; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ iw2; }
{ uint x = nonce ^ iw2; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ iw3; }
{ uint x = nonce ^ iw3; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ iw4; }
{ uint x = nonce ^ iw4; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ iw5; }
{ uint x = nonce ^ iw5; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ iw6; }
{ uint x = nonce ^ iw6; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ iw7; }
{ uint x = nonce ^ iw7; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ iw0; }
for (uint it = 0u; it < 8u; ++it) {
uint sel = r0;
r4 = r4 + r3 + ((((sel >> 8u) & 1u) != 0u) ? 0x5b2d5fe3u : 0x4a4c6caau); // 0 add
r7 = r7 | r6; // 1 or
r2 = r2 * r7; // 2 mul
{ uint t_; IGNEUM_SHFL_XOR(t_, r1, 2u); r5 = r5 ^ t_; } // 3 shfl
r4 = r4 ^ r6; // 4 xor
r0 = r0 ^ ds[r4 & mask]; // 5 load
r3 = rotl_imm(r3, 17u); // 6 rotl
r1 = r1 + r4 + ((((sel >> 27u) & 1u) != 0u) ? 0x83aa2c52u : 0xaeb38cc5u); // 7 add
r1 = rotl_imm(r1, 31u); // 8 rotl
r4 = r4 ^ r7; // 9 xor
{ uint b_ = (r0 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r6 = x_; } // 10 load
{ uint b_ = (r6 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r0 = x_; } // 11 load
{ uint t_; IGNEUM_SHFL_XOR(t_, r2, 1u); r5 = r5 ^ t_; } // 12 shfl
{ uint t_; IGNEUM_SHFL_XOR(t_, r4, 2u); r1 = r1 ^ t_; } // 13 shfl
r5 = r5 * r7; // 14 mul
r3 = r3 ^ r0; // 15 xor
r5 = r5 * r4; // 16 mul
{ uint t_; IGNEUM_SHFL_XOR(t_, r0, 4u); r2 = r2 ^ t_; } // 17 shfl
r4 = r4 + r1 + ((((sel >> 31u) & 1u) != 0u) ? 0xfb36bddau : 0x87d3a998u); // 18 add
r0 = r0 + r1 + ((((sel >> 22u) & 1u) != 0u) ? 0xf2ef7076u : 0x88921092u); // 19 add
r6 = r6 + r1 + ((((sel >> 3u) & 1u) != 0u) ? 0xe42e69c6u : 0x35e06b74u); // 20 add
{ uint t_; IGNEUM_SHFL_XOR(t_, r5, 8u); r2 = r2 ^ t_; } // 21 shfl
r3 = r3 + r6 + ((((sel >> 3u) & 1u) != 0u) ? 0xb45d9671u : 0x74df5734u); // 22 add
{ uint t_; IGNEUM_SHFL_XOR(t_, r4, 8u); r5 = r5 ^ t_; } // 23 shfl
r4 = r4 ^ ds[r2 & mask]; // 24 load
r2 = r2 ^ r1; // 25 xor
r7 = r7 * r6; // 26 mul
{ uint b_ = (r3 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r2 = x_; } // 27 load
r3 = rotl_imm(r3, 29u); // 28 rotl
{ uint t_; IGNEUM_SHFL_XOR(t_, r0, 8u); r3 = r3 ^ t_; } // 29 shfl
r1 = r1 ^ ds[r4 & mask]; // 30 load
r4 = r4 - r6; // 31 sub
r2 = r2 * r4; // 32 mul
r4 = r3 * r3 + r4; // 33 mad
r0 = r0 + r6 + ((((sel >> 15u) & 1u) != 0u) ? 0x97d2762du : 0x19318d72u); // 34 add
r7 = r7 + r1 + ((((sel >> 19u) & 1u) != 0u) ? 0x79830eadu : 0x26b63296u); // 35 add
r7 = r7 ^ ds[r2 & mask]; // 36 load
r0 = rotr_var(r0, r7); // 37 rotr
r2 = r2 + r7 + ((((sel >> 28u) & 1u) != 0u) ? 0x23c8dec4u : 0xb228dc81u); // 38 add
r0 = r0 ^ ds[r5 & mask]; // 39 load
{ uint t_; IGNEUM_SHFL_XOR(t_, r5, 2u); r7 = r7 ^ t_; } // 40 shfl
r5 = r0 * r1 + r5; // 41 mad
r2 = r2 ^ r3; // 42 xor
r7 = r7 ^ ds[r0 & mask]; // 43 load
{ uint b_ = (r5 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r0 = x_; } // 44 load
r2 = r2 + r7 + ((((sel >> 22u) & 1u) != 0u) ? 0x5a3a7fe1u : 0xcc64df8eu); // 45 add
r0 = rotr_var(r0, r5); // 46 rotr
r3 = rotr_var(r3, r7); // 47 rotr
r2 = r7 * r6 + r2; // 48 mad
r6 = r3 * r5 + r6; // 49 mad
r1 = r1 ^ ds[r6 & mask]; // 50 load
r7 = r7 ^ ds[r1 & mask]; // 51 load
r3 = r3 + r4 + ((((sel >> 6u) & 1u) != 0u) ? 0xcba22643u : 0x7a646d78u); // 52 add
r4 = r4 ^ r0; // 53 xor
{ uint t_; IGNEUM_SHFL_XOR(t_, r7, 4u); r3 = r3 ^ t_; } // 54 shfl
r0 = rotr_var(r0, r1); // 55 rotr
r5 = r5 ^ r3; // 56 xor
r1 = r1 + r0 + ((((sel >> 8u) & 1u) != 0u) ? 0x1834af00u : 0x6f26b909u); // 57 add
r6 = r6 ^ ds[r4 & mask]; // 58 load
{ uint b_ = (r7 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r1 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r1 = x_; } // 59 load
r1 = r1 ^ ds[r0 & mask]; // 60 load
r4 = r4 ^ r7; // 61 xor
r6 = r2 * r1 + r6; // 62 mad
{ uint b_ = (r2 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r3 = x_; } // 63 load
}
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;
}

View file

@ -0,0 +1,123 @@
// Generated by igneum-pow export (generator v2) for seed "igneum-readwidth/A/1". Do not edit by hand.
// Header-bound twin of igneum_hash in kernel.cu: the init words come from a kernel argument, not SEEDW.
// Host declarations (also in program_bound.h if present):
// struct IgneumInitWords { uint32_t w[8]; };
// cudaError_t igneum_launch_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask,
// IgneumInitWords iw, uint32_t nonces, uint32_t blockWarps);
// cudaError_t igneum_hash_bound_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps);
#include <cuda_runtime.h>
#include <cstdint>
#include "program.h"
struct IgneumInitWords { uint32_t w[8]; };
__device__ __forceinline__ uint32_t splitmix32(uint32_t x) {
x ^= x >> 16; x *= 0x7feb352du;
x ^= x >> 15; x *= 0x846ca68bu;
x ^= x >> 16;
return x;
}
__device__ __forceinline__ uint32_t rotl_imm(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); }
__device__ __forceinline__ uint32_t rotr_var(uint32_t x, uint32_t n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); }
__global__ void igneum_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, IgneumInitWords iw) {
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 ^ iw.w[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ iw.w[1]; }
{ uint32_t x = nonce ^ iw.w[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ iw.w[2]; }
{ uint32_t x = nonce ^ iw.w[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ iw.w[3]; }
{ uint32_t x = nonce ^ iw.w[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ iw.w[4]; }
{ uint32_t x = nonce ^ iw.w[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ iw.w[5]; }
{ uint32_t x = nonce ^ iw.w[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ iw.w[6]; }
{ uint32_t x = nonce ^ iw.w[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ iw.w[7]; }
{ uint32_t x = nonce ^ iw.w[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ iw.w[0]; }
for (uint32_t it = 0u; it < 8u; ++it) {
uint32_t sel = r0;
r4 = r4 + r3 + ((((sel >> 8u) & 1u) != 0u) ? 0x5b2d5fe3u : 0x4a4c6caau); // 0 add
r7 = r7 | r6; // 1 or
r2 = r2 * r7; // 2 mul
r5 = r5 ^ __shfl_xor_sync(0xffffffffu, r1, 2); // 3 shfl
r4 = r4 ^ r6; // 4 xor
r0 = r0 ^ ds[r4 & mask]; // 5 load
r3 = rotl_imm(r3, 17u); // 6 rotl
r1 = r1 + r4 + ((((sel >> 27u) & 1u) != 0u) ? 0x83aa2c52u : 0xaeb38cc5u); // 7 add
r1 = rotl_imm(r1, 31u); // 8 rotl
r4 = r4 ^ r7; // 9 xor
{ uint32_t b_ = (r0 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r6 = x_; } // 10 load
{ uint32_t b_ = (r6 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r0 = x_; } // 11 load
r5 = r5 ^ __shfl_xor_sync(0xffffffffu, r2, 1); // 12 shfl
r1 = r1 ^ __shfl_xor_sync(0xffffffffu, r4, 2); // 13 shfl
r5 = r5 * r7; // 14 mul
r3 = r3 ^ r0; // 15 xor
r5 = r5 * r4; // 16 mul
r2 = r2 ^ __shfl_xor_sync(0xffffffffu, r0, 4); // 17 shfl
r4 = r4 + r1 + ((((sel >> 31u) & 1u) != 0u) ? 0xfb36bddau : 0x87d3a998u); // 18 add
r0 = r0 + r1 + ((((sel >> 22u) & 1u) != 0u) ? 0xf2ef7076u : 0x88921092u); // 19 add
r6 = r6 + r1 + ((((sel >> 3u) & 1u) != 0u) ? 0xe42e69c6u : 0x35e06b74u); // 20 add
r2 = r2 ^ __shfl_xor_sync(0xffffffffu, r5, 8); // 21 shfl
r3 = r3 + r6 + ((((sel >> 3u) & 1u) != 0u) ? 0xb45d9671u : 0x74df5734u); // 22 add
r5 = r5 ^ __shfl_xor_sync(0xffffffffu, r4, 8); // 23 shfl
r4 = r4 ^ ds[r2 & mask]; // 24 load
r2 = r2 ^ r1; // 25 xor
r7 = r7 * r6; // 26 mul
{ uint32_t b_ = (r3 & mask) & ~15u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint32_t x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r2 = x_; } // 27 load
r3 = rotl_imm(r3, 29u); // 28 rotl
r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r0, 8); // 29 shfl
r1 = r1 ^ ds[r4 & mask]; // 30 load
r4 = r4 - r6; // 31 sub
r2 = r2 * r4; // 32 mul
r4 = r3 * r3 + r4; // 33 mad
r0 = r0 + r6 + ((((sel >> 15u) & 1u) != 0u) ? 0x97d2762du : 0x19318d72u); // 34 add
r7 = r7 + r1 + ((((sel >> 19u) & 1u) != 0u) ? 0x79830eadu : 0x26b63296u); // 35 add
r7 = r7 ^ ds[r2 & mask]; // 36 load
r0 = rotr_var(r0, r7); // 37 rotr
r2 = r2 + r7 + ((((sel >> 28u) & 1u) != 0u) ? 0x23c8dec4u : 0xb228dc81u); // 38 add
r0 = r0 ^ ds[r5 & mask]; // 39 load
r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r5, 2); // 40 shfl
r5 = r0 * r1 + r5; // 41 mad
r2 = r2 ^ r3; // 42 xor
r7 = r7 ^ ds[r0 & mask]; // 43 load
{ uint32_t b_ = (r5 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r0 = x_; } // 44 load
r2 = r2 + r7 + ((((sel >> 22u) & 1u) != 0u) ? 0x5a3a7fe1u : 0xcc64df8eu); // 45 add
r0 = rotr_var(r0, r5); // 46 rotr
r3 = rotr_var(r3, r7); // 47 rotr
r2 = r7 * r6 + r2; // 48 mad
r6 = r3 * r5 + r6; // 49 mad
r1 = r1 ^ ds[r6 & mask]; // 50 load
r7 = r7 ^ ds[r1 & mask]; // 51 load
r3 = r3 + r4 + ((((sel >> 6u) & 1u) != 0u) ? 0xcba22643u : 0x7a646d78u); // 52 add
r4 = r4 ^ r0; // 53 xor
r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r7, 4); // 54 shfl
r0 = rotr_var(r0, r1); // 55 rotr
r5 = r5 ^ r3; // 56 xor
r1 = r1 + r0 + ((((sel >> 8u) & 1u) != 0u) ? 0x1834af00u : 0x6f26b909u); // 57 add
r6 = r6 ^ ds[r4 & mask]; // 58 load
{ uint32_t b_ = (r7 & mask) & ~15u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint32_t x_ = r1 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r1 = x_; } // 59 load
r1 = r1 ^ ds[r0 & mask]; // 60 load
r4 = r4 ^ r7; // 61 xor
r6 = r2 * r1 + r6; // 62 mad
{ uint32_t b_ = (r2 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r3 = x_; } // 63 load
}
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;
}
cudaError_t igneum_launch_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask,
IgneumInitWords iw, 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_bound<<<nonces / block, block>>>(ds, out, baseNonce, mask, iw);
return cudaGetLastError();
}
cudaError_t igneum_hash_bound_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps) {
cudaFuncAttributes attr;
cudaError_t e = cudaFuncGetAttributes(&attr, igneum_hash_bound);
if (e != cudaSuccess) return e;
*numRegs = attr.numRegs;
return cudaOccupancyMaxActiveBlocksPerMultiprocessor(blocksPerSM, igneum_hash_bound, (int)(32u * blockWarps), 0);
}

View file

@ -0,0 +1,108 @@
// Generated by igneum-pow export (generator v2) for seed "igneum-readwidth/A/1". Do not edit by hand.
// Memory-hard dataset core, the same text that the Mac's Metal kernels and CPU verifier were checked against.
// Included by kernel.cu (device), host.cu (host reference) and proto-opencl/host.c (C99 host reference).
// See proto-metal/MEMHARD.md for the construction. kernel.cl carries the same text in OpenCL C.
#pragma once
#ifdef __cplusplus
#include <cstdint>
#else
#include <stdint.h>
#endif
#if defined(__CUDACC__)
#define IGNEUM_HD __host__ __device__ __forceinline__
#elif defined(_MSC_VER) && !defined(__cplusplus)
#define IGNEUM_HD static __inline
#else
#define IGNEUM_HD static inline
#endif
// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines.
// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals.
#define MH_CACHE_LINE_MASK 0x003fffffu
#define MH_SEGMENT_LINES 64u
#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); }
IGNEUM_HD uint32_t mh_rotl(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site
// y = ChaCha12 core(x) + x
IGNEUM_HD void mh_chacha_block(const uint32_t* x, uint32_t* y) {
for (uint32_t i = 0u; i < 16u; ++i) y[i] = x[i];
for (uint32_t r = 0u; r < 6u; ++r) {
MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u)
MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u)
}
for (uint32_t i = 0u; i < 16u; ++i) y[i] += x[i];
}
// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0.
IGNEUM_HD void mh_cache_segment(uint32_t* cache, uint32_t seg) {
uint32_t prev[16]; uint32_t x[16]; uint32_t y[16];
for (uint32_t i = 0u; i < 16u; ++i) prev[i] = 0u;
for (uint32_t j = 0u; j < MH_SEGMENT_LINES; ++j) {
x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3];
x[4] = 0x3067619fu ^ prev[4];
x[5] = 0x3c269176u ^ prev[5];
x[6] = 0x84a03b03u ^ prev[6];
x[7] = 0xf8c63294u ^ prev[7];
x[8] = 0xff977c5bu ^ prev[8];
x[9] = 0xe60def3eu ^ prev[9];
x[10] = 0x63630141u ^ prev[10];
x[11] = 0xb8fbcb58u ^ prev[11];
x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15];
mh_chacha_block(x, y);
uint32_t* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u);
for (uint32_t i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; }
}
}
// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations.
IGNEUM_HD void mh_mixer(uint32_t* s, uint32_t rk) {
s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u;
s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu;
s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u;
s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu;
s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u;
s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u;
s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u;
s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u;
s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du;
s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u;
s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du;
s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu;
s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du;
s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu;
s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u;
s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u;
MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u)
MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u)
MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u)
MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u)
}
// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer.
IGNEUM_HD void mh_item(const uint32_t* cache, uint32_t t, uint32_t* s) {
s[0] = 0x3067619fu;
s[1] = 0x3c269176u;
s[2] = 0x84a03b03u;
s[3] = 0xf8c63294u;
s[4] = 0xff977c5bu;
s[5] = 0xe60def3eu;
s[6] = 0x63630141u;
s[7] = 0xb8fbcb58u;
s[8] = t * 0x42146205u + 0xbab68293u;
s[9] = t * 0x52cbe0fbu + 0xcc162340u;
s[10] = t * 0x7ecf4a03u + 0x6ce151ccu;
s[11] = t * 0x6728907fu + 0xe62b8997u;
s[12] = t * 0xd81d9751u + 0xc9c80297u;
s[13] = t * 0x132952c3u + 0xf74a1654u;
s[14] = t * 0xf60de277u + 0x3d704af5u;
s[15] = t * 0x05358035u + 0x3cf522b7u;
for (uint32_t r = 0u; r < 8u; ++r) {
mh_mixer(s, 0x9E3779B9u * (r + 1u));
const uint32_t* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u);
for (uint32_t i = 0u; i < 16u; ++i) s[i] ^= line[i];
}
mh_mixer(s, 0x9E3779B9u * 9u);
}
// dataset[w] without the dataset: derive item w >> 4 and take word w & 15.
IGNEUM_HD uint32_t mh_word(const uint32_t* cache, uint32_t w) { uint32_t s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; }

View file

@ -0,0 +1,106 @@
#include <metal_stdlib>
using namespace metal;
// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines.
// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals.
#define MH_CACHE_LINE_MASK 0x003fffffu
#define MH_SEGMENT_LINES 64u
#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); }
inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site
// y = ChaCha12 core(x) + x
inline void mh_chacha_block(const thread uint* x, thread uint* y) {
for (uint i = 0u; i < 16u; ++i) y[i] = x[i];
for (uint r = 0u; r < 6u; ++r) {
MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u)
MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u)
}
for (uint i = 0u; i < 16u; ++i) y[i] += x[i];
}
// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0.
inline void mh_cache_segment(device uint* cache, uint seg) {
uint prev[16]; uint x[16]; uint y[16];
for (uint i = 0u; i < 16u; ++i) prev[i] = 0u;
for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) {
x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3];
x[4] = 0x3067619fu ^ prev[4];
x[5] = 0x3c269176u ^ prev[5];
x[6] = 0x84a03b03u ^ prev[6];
x[7] = 0xf8c63294u ^ prev[7];
x[8] = 0xff977c5bu ^ prev[8];
x[9] = 0xe60def3eu ^ prev[9];
x[10] = 0x63630141u ^ prev[10];
x[11] = 0xb8fbcb58u ^ prev[11];
x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15];
mh_chacha_block(x, y);
device uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u);
for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; }
}
}
// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations.
inline void mh_mixer(thread uint* s, uint rk) {
s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u;
s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu;
s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u;
s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu;
s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u;
s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u;
s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u;
s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u;
s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du;
s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u;
s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du;
s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu;
s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du;
s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu;
s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u;
s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u;
MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u)
MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u)
MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u)
MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u)
}
// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer.
inline void mh_item(device const uint* cache, uint t, thread uint* s) {
s[0] = 0x3067619fu;
s[1] = 0x3c269176u;
s[2] = 0x84a03b03u;
s[3] = 0xf8c63294u;
s[4] = 0xff977c5bu;
s[5] = 0xe60def3eu;
s[6] = 0x63630141u;
s[7] = 0xb8fbcb58u;
s[8] = t * 0x42146205u + 0xbab68293u;
s[9] = t * 0x52cbe0fbu + 0xcc162340u;
s[10] = t * 0x7ecf4a03u + 0x6ce151ccu;
s[11] = t * 0x6728907fu + 0xe62b8997u;
s[12] = t * 0xd81d9751u + 0xc9c80297u;
s[13] = t * 0x132952c3u + 0xf74a1654u;
s[14] = t * 0xf60de277u + 0x3d704af5u;
s[15] = t * 0x05358035u + 0x3cf522b7u;
for (uint r = 0u; r < 8u; ++r) {
mh_mixer(s, 0x9E3779B9u * (r + 1u));
device const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u);
for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i];
}
mh_mixer(s, 0x9E3779B9u * 9u);
}
// dataset[w] without the dataset: derive item w >> 4 and take word w & 15.
inline uint mh_word(device const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; }
// One thread per segment (2^16 threads).
kernel void igneum_cache_fill(device uint* cache [[buffer(0)]], uint gid [[thread_position_in_grid]]) {
mh_cache_segment(cache, gid);
}
// One thread per 64-byte item (dataset words / 16 threads).
kernel void igneum_build(device const uint* cache [[buffer(0)]], device uint* dataset [[buffer(1)]],
uint gid [[thread_position_in_grid]]) {
uint s[16];
mh_item(cache, gid, s);
device uint* d = dataset + gid * 16u;
for (uint i = 0u; i < 16u; ++i) d[i] = s[i];
}

View file

@ -0,0 +1,60 @@
// Generated by igneum-pow export (generator v2) for seed "igneum-readwidth/A/1". Do not edit by hand.
// Program metadata for host.cu plus the launch wrappers defined in kernel.cu.
// Also included by proto-opencl/host.c (C99), which defines IGNEUM_NO_CUDA first and reads only the macros.
#pragma once
#ifdef __cplusplus
#include <cstdint>
#else
#include <stdint.h>
#endif
#ifndef IGNEUM_NO_CUDA
#include <cuda_runtime.h>
#endif
#define IGNEUM_SEED_STRING "igneum-readwidth/A/1"
#define IGNEUM_SEED_BYTES_HEX "69676e65756d2d7265616477696474682f412f31"
#define IGNEUM_GENERATOR 2
#define IGNEUM_PROGRAM_ATTEMPT 0
#define IGNEUM_PROGRAM_ID 0x6408f2e28fdd344cull
#define IGNEUM_DAY_STRING "2026-10-03"
#define IGNEUM_DAY_BYTES_HEX "6461792f323032362d31302d3033"
#define IGNEUM_DAY0 0x3067619fu
#define IGNEUM_DAY1 0x3c269176u
#define IGNEUM_DATASET_LOG2 28
#define IGNEUM_MASK 0x0fffffffu
#define IGNEUM_LANES 32
#define IGNEUM_ITERATIONS 8
#define IGNEUM_INSTR_COUNT 64
#define IGNEUM_LOADS_PER_HASH 128
#define IGNEUM_WIDE_LOADS_PER_HASH 0
#define IGNEUM_OP_MIX "load=16 add=12 shfl=9 xor=8 mad=5 mul=5 rotr=4 rotl=3 or=1 sub=1"
// Read-width experiment (5 October 2026, docs/plans/read-width.md): NOT the lottery hash. A load of W words reads
// the W-word-aligned address and folds every word into dst: x = dst ^ w[0]; x = (rotl(x, 11) * 0x9e3779b1) ^ w[j]; dst = x.
#define IGNEUM_LOAD_CLASS "mix50-35-15"
#define IGNEUM_LOAD_SLOTS 16
#define IGNEUM_LOAD_MIX { 50, 35, 15 }
#define IGNEUM_LOAD_WIDTH_COUNTS { 10, 4, 2 } // loads of 4, 16, 64 bytes per program
#define IGNEUM_BYTES_PER_HASH 1856
#define IGNEUM_FOLD_ROT 11
#define IGNEUM_FOLD_MUL 0x9e3779b1u
// 0 = closed-form dataset (ds_elem), 1 = memory-hard cache construction (MEMHARD.md, memhard.h)
#define IGNEUM_DATASET_MODE 1
#define IGNEUM_SEEDW_INIT { 0xa7198abfu, 0x6cd0f8cfu, 0xe4ef8ebfu, 0x03ee1e65u, 0xcfcfc5c0u, 0x82e9e19bu, 0x5d7a8a2fu, 0xfafccd93u }
#define IGNEUM_KEY_INIT { 0x3067619fu, 0x3c269176u, 0x84a03b03u, 0xf8c63294u, 0xff977c5bu, 0xe60def3eu, 0x63630141u, 0xb8fbcb58u }
#define IGNEUM_CACHE_LOG2_WORDS 26
#define IGNEUM_CACHE_SEGMENT_LOG2_LINES 6
#define IGNEUM_CACHE_SEGMENTS 65536u
#define IGNEUM_ITEM_ROUNDS 8
#define IGNEUM_MIX_ROT_INIT { 20u, 20u, 19u, 4u, 26u, 3u, 3u, 27u }
#define IGNEUM_MIX_MUL_INIT { 0x42146205u, 0x52cbe0fbu, 0x7ecf4a03u, 0x6728907fu, 0xd81d9751u, 0x132952c3u, 0xf60de277u, 0x05358035u, 0xbaf6499du, 0xe4db9667u, 0x3e98f45du, 0xd0004eddu, 0x2691630du, 0x9beb3bcfu, 0xab310379u, 0x99cfb423u }
#define IGNEUM_MIX_RC_INIT { 0xbab68293u, 0xcc162340u, 0x6ce151ccu, 0xe62b8997u, 0xc9c80297u, 0xf74a1654u, 0x3d704af5u, 0x3cf522b7u, 0x2b9cac04u, 0xa880ac10u, 0x13e5dd1du, 0x6fc3e233u, 0x2d83eeacu, 0x9006e8bfu, 0x2c4b5362u, 0x31b49ee2u }
#ifndef IGNEUM_NO_CUDA
// Defined in kernel.cu. All launch on the default stream and return cudaGetLastError().
cudaError_t igneum_launch_cache_fill(uint32_t* cache, uint32_t nSegments);
cudaError_t igneum_launch_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems);
cudaError_t igneum_launch_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask,
uint32_t nonces, uint32_t blockWarps);
cudaError_t igneum_hash_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps);
#endif

View file

@ -0,0 +1,127 @@
{
"format": "igneum-program-pack-3",
"generator": 2,
"attempt": 0,
"program_id": "0x6408f2e28fdd344c",
"program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32",
"dataset_mode": "memory-hard",
"seed": "igneum-readwidth/A/1",
"seed_bytes": "69676e65756d2d7265616477696474682f412f31",
"seed_words": ["0xa7198abf", "0x6cd0f8cf", "0xe4ef8ebf", "0x03ee1e65", "0xcfcfc5c0", "0x82e9e19b", "0x5d7a8a2f", "0xfafccd93"],
"seed_derivation": "seed_words = FNV-1a 64 over seed_bytes (attempt 0) or seed_bytes || attempt_le32 (attempt k >= 1), basis ^ (salt * 0x9E3779B97F4A7C15) for salt 0..3, then h ^= h>>33; h *= 0xff51afd7ed558ccd; h ^= h>>33; words[2*salt] = low 32, words[2*salt+1] = high 32",
"generator_rule": "version 2: exactly 16 load slots drawn first from instructions 1..63 (partial Fisher-Yates), the other 48 ops from the ten non-load weights (sum 75); a load's source is drawn from the registers other than dst written by an earlier instruction and not read by a load since; the candidate must pass the acceptance rule of spec 01 section 1.4.6 (static: no cyclically stale load source, every register has an injecting write; dynamic: 64 units on the seed-keyed closed-form dataset with no constant register bit, no lane-constant load site, under 164 saturated final values, every output bit within 136 of 1024, distinct addresses above 245760), else the next attempt of the seed is tried",
"lanes": 32,
"registers": 8,
"iterations": 8,
"instruction_count": 64,
"loads_per_hash": 128,
"load_class": "mix50-35-15",
"load_slots": 16,
"load_mix_percent_4_16_64": [50, 35, 15],
"load_width_counts_4_16_64": [10, 4, 2],
"bytes_per_hash": 1856,
"wide_load": "read-width experiment (5 October 2026, docs/plans/read-width.md), NOT the lottery hash: a load of W words (width field, 4 or 16) reads dataset[b .. b + W) with b = (src & mask) & ~(W - 1) and folds every word into dst: x = dst ^ w[0]; for j in 1..W: x = (rotl(x, 11) * 0x9e3779b1) ^ w[j]; dst = x; width 1 is the plain load; the width is drawn per instruction from the class mix with one extra below(100) draw after the nine of version 2, and the program id is FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed words || attempt_le32 || mix[3] || load_slots",
"op_mix": {"load": 16, "add": 12, "shfl": 9, "xor": 8, "mad": 5, "mul": 5, "rotr": 4, "rotl": 3, "or": 1, "sub": 1},
"register_init": "for i in 0..7: x = nonce ^ seed_words[i]; x += 0x9e3779b9 * (i+1) (mod 2^32); x = splitmix32(x); r[i] = x ^ seed_words[(i+1) & 7]",
"splitmix32": "x ^= x>>16; x *= 0x7feb352d; x ^= x>>15; x *= 0x846ca68b; x ^= x>>16",
"iteration": "sel = r0 sampled once at the top of each iteration, then all instructions in order",
"output": "lo = r0 ^ rotl(r1,7) ^ rotl(r2,14) ^ rotl(r3,21); hi = r4 ^ rotl(r5,9) ^ rotl(r6,18) ^ rotl(r7,27); out = (hi << 32) | lo",
"op_semantics": {
"add": "dst = dst + src + (bit `bit` of sel ? imm2 : imm)",
"sub": "dst = dst - src",
"mul": "dst = dst * src (low 32)",
"mulhi": "dst = high 32 bits of dst * src",
"xor": "dst = dst ^ src",
"or": "dst = dst | src",
"rotl": "dst = rotl(dst, rot), rot in 1..31",
"rotr": "dst = rotr(dst, src & 31)",
"mad": "dst = src * src2 + dst",
"shfl": "dst = dst ^ (src of lane (lane ^ mask)), mask in {1,2,4,8,16}, within the 32-lane warp",
"load": "dst = dst ^ dataset[src & dataset.mask]",
"wload": "base = (src of lane 0 & dataset.mask) & ~31; dst = dst ^ dataset[base + lane] (warp-coalesced 128-byte load, lever b, only when --wide-frac > 0)"
},
"dataset": {
"log2_words": 28,
"bytes": 1073741824,
"mask": "0x0fffffff",
"day": "2026-10-03",
"day_bytes": "6461792f323032362d31302d3033",
"day_words_from": "seed_words_from_bytes(day_bytes)",
"d0": "0x3067619f",
"d1": "0x3c269176",
"mode": "memory-hard",
"spec": "proto-metal/MEMHARD.md",
"key": ["0x3067619f", "0x3c269176", "0x84a03b03", "0xf8c63294", "0xff977c5b", "0xe60def3e", "0x63630141", "0xb8fbcb58"],
"key_derivation": "the 8 words of seed_words_from_bytes(day_bytes); d0, d1 are key[0], key[1]",
"cache": {"log2_words": 26, "bytes": 268435456, "line_words": 16, "segment_lines": 64, "segments": 65536, "block": "ChaCha12 core + feed-forward, rotations 16 12 8 7", "sigma": ["0x61707865", "0x3320646e", "0x79622d32", "0x6b206574"], "tag": ["0x49676e65", "0x756d4d48"], "chain": "in_j = prev_line ^ (sigma[0..3] || key[0..7] || seg || j || tag[0..1]); line_j = block(in_j); prev_0 = 0"},
"mixer": {"draw": "SplitMix64 seeded with key[0] | key[1] << 32: rot[0..7] = 1 + next() % 31, mul[0..15] = low32(next()) | 1, rc[0..15] = low32(next())", "rot": [20, 20, 19, 4, 26, 3, 3, 27], "mul": ["0x42146205", "0x52cbe0fb", "0x7ecf4a03", "0x6728907f", "0xd81d9751", "0x132952c3", "0xf60de277", "0x05358035", "0xbaf6499d", "0xe4db9667", "0x3e98f45d", "0xd0004edd", "0x2691630d", "0x9beb3bcf", "0xab310379", "0x99cfb423"], "rc": ["0xbab68293", "0xcc162340", "0x6ce151cc", "0xe62b8997", "0xc9c80297", "0xf74a1654", "0x3d704af5", "0x3cf522b7", "0x2b9cac04", "0xa880ac10", "0x13e5dd1d", "0x6fc3e233", "0x2d83eeac", "0x9006e8bf", "0x2c4b5362", "0x31b49ee2"], "round": "for i in 0..15: s[i] = (s[i] ^ (rc[i] + (r+1) * 0x9E3779B9)) * mul[i]; then quarter rounds on columns (0,4,8,12) (1,5,9,13) (2,6,10,14) (3,7,11,15) with rot[0..3] and diagonals (0,5,10,15) (1,6,11,12) (2,7,8,13) (3,4,9,14) with rot[4..7]", "quarter_round": "a += b; d ^= a; d = rotl(d, r1); c += d; b ^= c; b = rotl(b, r2); a += b; d ^= a; d = rotl(d, r3); c += d; b ^= c; b = rotl(b, r4)"},
"item": "s[0..7] = key; s[8+i] = t * mul[i] + rc[i] for i in 0..7; for r in 0..7: s = M_r(s); line = s[0] & 0x003fffff; s[i] ^= cache[line * 16 + i]; then s = M_8(s); item(t) = s",
"word": "dataset[w] = item(w >> 4)[w & 15]"
},
"instructions": [
{"i": 0, "op": "add", "dst": 4, "src": 3, "src2": 4, "imm": "0x4a4c6caa", "imm2": "0x5b2d5fe3", "rot": 28, "bit": 8, "mask": 1, "width": 1},
{"i": 1, "op": "or", "dst": 7, "src": 6, "src2": 0, "imm": "0x8c4c9f7c", "imm2": "0x3b47c42a", "rot": 3, "bit": 13, "mask": 8, "width": 1},
{"i": 2, "op": "mul", "dst": 2, "src": 7, "src2": 1, "imm": "0xe7cf827b", "imm2": "0x786c6890", "rot": 24, "bit": 25, "mask": 8, "width": 1},
{"i": 3, "op": "shfl", "dst": 5, "src": 1, "src2": 6, "imm": "0xa686f6b7", "imm2": "0x48920bc0", "rot": 1, "bit": 5, "mask": 2, "width": 1},
{"i": 4, "op": "xor", "dst": 4, "src": 6, "src2": 1, "imm": "0x58489525", "imm2": "0x1c56b23f", "rot": 25, "bit": 21, "mask": 1, "width": 1},
{"i": 5, "op": "load", "dst": 0, "src": 4, "src2": 6, "imm": "0xacf5203a", "imm2": "0x76e3d8a1", "rot": 22, "bit": 20, "mask": 1, "width": 1},
{"i": 6, "op": "rotl", "dst": 3, "src": 1, "src2": 6, "imm": "0x2507e906", "imm2": "0xf9c5c1f1", "rot": 17, "bit": 11, "mask": 4, "width": 1},
{"i": 7, "op": "add", "dst": 1, "src": 4, "src2": 3, "imm": "0xaeb38cc5", "imm2": "0x83aa2c52", "rot": 10, "bit": 27, "mask": 4, "width": 1},
{"i": 8, "op": "rotl", "dst": 1, "src": 2, "src2": 4, "imm": "0x261b9557", "imm2": "0xe0df5769", "rot": 31, "bit": 30, "mask": 8, "width": 1},
{"i": 9, "op": "xor", "dst": 4, "src": 7, "src2": 1, "imm": "0x8f733ec9", "imm2": "0x468afd76", "rot": 3, "bit": 1, "mask": 2, "width": 1},
{"i": 10, "op": "load", "dst": 6, "src": 0, "src2": 1, "imm": "0x53eef18d", "imm2": "0x763b1f0a", "rot": 20, "bit": 20, "mask": 4, "width": 4},
{"i": 11, "op": "load", "dst": 0, "src": 6, "src2": 4, "imm": "0xa2f22f71", "imm2": "0xe0a8243e", "rot": 20, "bit": 30, "mask": 1, "width": 4},
{"i": 12, "op": "shfl", "dst": 5, "src": 2, "src2": 6, "imm": "0x88827407", "imm2": "0xe9f1befa", "rot": 10, "bit": 11, "mask": 1, "width": 1},
{"i": 13, "op": "shfl", "dst": 1, "src": 4, "src2": 7, "imm": "0x88867e60", "imm2": "0x2726b465", "rot": 9, "bit": 6, "mask": 2, "width": 1},
{"i": 14, "op": "mul", "dst": 5, "src": 7, "src2": 2, "imm": "0x41530f45", "imm2": "0xf0badd76", "rot": 1, "bit": 13, "mask": 1, "width": 1},
{"i": 15, "op": "xor", "dst": 3, "src": 0, "src2": 6, "imm": "0x29e41472", "imm2": "0xd2c1de0c", "rot": 22, "bit": 5, "mask": 16, "width": 1},
{"i": 16, "op": "mul", "dst": 5, "src": 4, "src2": 4, "imm": "0xafa3d0bd", "imm2": "0x1568e384", "rot": 19, "bit": 20, "mask": 2, "width": 1},
{"i": 17, "op": "shfl", "dst": 2, "src": 0, "src2": 6, "imm": "0xffccd357", "imm2": "0x4b21f8cf", "rot": 3, "bit": 6, "mask": 4, "width": 1},
{"i": 18, "op": "add", "dst": 4, "src": 1, "src2": 3, "imm": "0x87d3a998", "imm2": "0xfb36bdda", "rot": 29, "bit": 31, "mask": 4, "width": 1},
{"i": 19, "op": "add", "dst": 0, "src": 1, "src2": 0, "imm": "0x88921092", "imm2": "0xf2ef7076", "rot": 20, "bit": 22, "mask": 2, "width": 1},
{"i": 20, "op": "add", "dst": 6, "src": 1, "src2": 4, "imm": "0x35e06b74", "imm2": "0xe42e69c6", "rot": 3, "bit": 3, "mask": 2, "width": 1},
{"i": 21, "op": "shfl", "dst": 2, "src": 5, "src2": 0, "imm": "0xa7323c7a", "imm2": "0x81d43278", "rot": 16, "bit": 10, "mask": 8, "width": 1},
{"i": 22, "op": "add", "dst": 3, "src": 6, "src2": 2, "imm": "0x74df5734", "imm2": "0xb45d9671", "rot": 26, "bit": 3, "mask": 2, "width": 1},
{"i": 23, "op": "shfl", "dst": 5, "src": 4, "src2": 2, "imm": "0x1e0660fe", "imm2": "0x13c7e83f", "rot": 13, "bit": 15, "mask": 8, "width": 1},
{"i": 24, "op": "load", "dst": 4, "src": 2, "src2": 2, "imm": "0x68bfe9f0", "imm2": "0x2067c339", "rot": 5, "bit": 11, "mask": 1, "width": 1},
{"i": 25, "op": "xor", "dst": 2, "src": 1, "src2": 3, "imm": "0x383e819c", "imm2": "0xef4f43f5", "rot": 2, "bit": 25, "mask": 2, "width": 1},
{"i": 26, "op": "mul", "dst": 7, "src": 6, "src2": 4, "imm": "0x45892e5e", "imm2": "0x5c358e4b", "rot": 13, "bit": 9, "mask": 1, "width": 1},
{"i": 27, "op": "load", "dst": 2, "src": 3, "src2": 2, "imm": "0x8fe1d831", "imm2": "0xe2f0a7df", "rot": 18, "bit": 5, "mask": 16, "width": 16},
{"i": 28, "op": "rotl", "dst": 3, "src": 4, "src2": 4, "imm": "0x78e2bf43", "imm2": "0x723070c4", "rot": 29, "bit": 6, "mask": 2, "width": 1},
{"i": 29, "op": "shfl", "dst": 3, "src": 0, "src2": 3, "imm": "0x6a40f0ce", "imm2": "0x8f751dd0", "rot": 26, "bit": 12, "mask": 8, "width": 1},
{"i": 30, "op": "load", "dst": 1, "src": 4, "src2": 6, "imm": "0x3bcdf182", "imm2": "0x5ec397eb", "rot": 5, "bit": 24, "mask": 16, "width": 1},
{"i": 31, "op": "sub", "dst": 4, "src": 6, "src2": 5, "imm": "0x28631d4e", "imm2": "0x1c8b6e91", "rot": 2, "bit": 13, "mask": 4, "width": 1},
{"i": 32, "op": "mul", "dst": 2, "src": 4, "src2": 4, "imm": "0xf8b83d3f", "imm2": "0x6bbab3d3", "rot": 3, "bit": 19, "mask": 1, "width": 1},
{"i": 33, "op": "mad", "dst": 4, "src": 3, "src2": 3, "imm": "0xdf39e98a", "imm2": "0x1c4dbde8", "rot": 8, "bit": 17, "mask": 2, "width": 1},
{"i": 34, "op": "add", "dst": 0, "src": 6, "src2": 4, "imm": "0x19318d72", "imm2": "0x97d2762d", "rot": 14, "bit": 15, "mask": 16, "width": 1},
{"i": 35, "op": "add", "dst": 7, "src": 1, "src2": 1, "imm": "0x26b63296", "imm2": "0x79830ead", "rot": 26, "bit": 19, "mask": 16, "width": 1},
{"i": 36, "op": "load", "dst": 7, "src": 2, "src2": 2, "imm": "0xf2fe58f7", "imm2": "0x3813073a", "rot": 19, "bit": 7, "mask": 2, "width": 1},
{"i": 37, "op": "rotr", "dst": 0, "src": 7, "src2": 7, "imm": "0x268c32f9", "imm2": "0x1f02f30a", "rot": 20, "bit": 10, "mask": 1, "width": 1},
{"i": 38, "op": "add", "dst": 2, "src": 7, "src2": 1, "imm": "0xb228dc81", "imm2": "0x23c8dec4", "rot": 14, "bit": 28, "mask": 4, "width": 1},
{"i": 39, "op": "load", "dst": 0, "src": 5, "src2": 6, "imm": "0x08c2fe36", "imm2": "0xa232fa5b", "rot": 22, "bit": 13, "mask": 16, "width": 1},
{"i": 40, "op": "shfl", "dst": 7, "src": 5, "src2": 4, "imm": "0x8601d298", "imm2": "0x04a37e40", "rot": 27, "bit": 21, "mask": 2, "width": 1},
{"i": 41, "op": "mad", "dst": 5, "src": 0, "src2": 1, "imm": "0xc0112c85", "imm2": "0xcae01ef3", "rot": 16, "bit": 5, "mask": 4, "width": 1},
{"i": 42, "op": "xor", "dst": 2, "src": 3, "src2": 1, "imm": "0x19f77b37", "imm2": "0x0952e878", "rot": 28, "bit": 23, "mask": 4, "width": 1},
{"i": 43, "op": "load", "dst": 7, "src": 0, "src2": 6, "imm": "0x020804cf", "imm2": "0x9eb73dda", "rot": 30, "bit": 2, "mask": 1, "width": 1},
{"i": 44, "op": "load", "dst": 0, "src": 5, "src2": 6, "imm": "0x485a8a15", "imm2": "0xdeac555a", "rot": 13, "bit": 21, "mask": 8, "width": 4},
{"i": 45, "op": "add", "dst": 2, "src": 7, "src2": 0, "imm": "0xcc64df8e", "imm2": "0x5a3a7fe1", "rot": 18, "bit": 22, "mask": 8, "width": 1},
{"i": 46, "op": "rotr", "dst": 0, "src": 5, "src2": 5, "imm": "0xe6e78bd9", "imm2": "0x5c932006", "rot": 31, "bit": 20, "mask": 2, "width": 1},
{"i": 47, "op": "rotr", "dst": 3, "src": 7, "src2": 4, "imm": "0xa77d4cb5", "imm2": "0x32f2b544", "rot": 15, "bit": 16, "mask": 8, "width": 1},
{"i": 48, "op": "mad", "dst": 2, "src": 7, "src2": 6, "imm": "0x197fc3bf", "imm2": "0x02536a02", "rot": 3, "bit": 5, "mask": 16, "width": 1},
{"i": 49, "op": "mad", "dst": 6, "src": 3, "src2": 5, "imm": "0x6aa6ad47", "imm2": "0x0f1c2695", "rot": 6, "bit": 31, "mask": 8, "width": 1},
{"i": 50, "op": "load", "dst": 1, "src": 6, "src2": 1, "imm": "0x106ee155", "imm2": "0x6e409245", "rot": 29, "bit": 22, "mask": 2, "width": 1},
{"i": 51, "op": "load", "dst": 7, "src": 1, "src2": 0, "imm": "0xc604b17f", "imm2": "0x7ba442ad", "rot": 8, "bit": 27, "mask": 1, "width": 1},
{"i": 52, "op": "add", "dst": 3, "src": 4, "src2": 3, "imm": "0x7a646d78", "imm2": "0xcba22643", "rot": 1, "bit": 6, "mask": 2, "width": 1},
{"i": 53, "op": "xor", "dst": 4, "src": 0, "src2": 1, "imm": "0x0c951e55", "imm2": "0xe4242078", "rot": 10, "bit": 14, "mask": 4, "width": 1},
{"i": 54, "op": "shfl", "dst": 3, "src": 7, "src2": 5, "imm": "0x1e8e8986", "imm2": "0xcc7c23e9", "rot": 26, "bit": 11, "mask": 4, "width": 1},
{"i": 55, "op": "rotr", "dst": 0, "src": 1, "src2": 1, "imm": "0x384aa1de", "imm2": "0x03bd71f3", "rot": 7, "bit": 27, "mask": 16, "width": 1},
{"i": 56, "op": "xor", "dst": 5, "src": 3, "src2": 6, "imm": "0x0f3bbfb3", "imm2": "0x829b8b8d", "rot": 15, "bit": 3, "mask": 4, "width": 1},
{"i": 57, "op": "add", "dst": 1, "src": 0, "src2": 0, "imm": "0x6f26b909", "imm2": "0x1834af00", "rot": 28, "bit": 8, "mask": 2, "width": 1},
{"i": 58, "op": "load", "dst": 6, "src": 4, "src2": 2, "imm": "0x6bcbd615", "imm2": "0xadbaf0ef", "rot": 3, "bit": 4, "mask": 8, "width": 1},
{"i": 59, "op": "load", "dst": 1, "src": 7, "src2": 1, "imm": "0x79dc0a3f", "imm2": "0x436e360d", "rot": 30, "bit": 13, "mask": 8, "width": 16},
{"i": 60, "op": "load", "dst": 1, "src": 0, "src2": 2, "imm": "0x30cd5237", "imm2": "0x8bd46148", "rot": 22, "bit": 29, "mask": 1, "width": 1},
{"i": 61, "op": "xor", "dst": 4, "src": 7, "src2": 4, "imm": "0x74419ddb", "imm2": "0x70d9f3d5", "rot": 3, "bit": 26, "mask": 16, "width": 1},
{"i": 62, "op": "mad", "dst": 6, "src": 2, "src2": 1, "imm": "0x938645fc", "imm2": "0xa9e49728", "rot": 20, "bit": 27, "mask": 8, "width": 1},
{"i": 63, "op": "load", "dst": 3, "src": 2, "src2": 2, "imm": "0x2089409e", "imm2": "0x0437ba4e", "rot": 3, "bit": 31, "mask": 16, "width": 4}
]
}

View file

@ -0,0 +1,109 @@
#include <metal_stdlib>
using namespace metal;
#define MASK 0x0fffffffu
constant uint SEEDW[8] = { 0xa7198abfu, 0x6cd0f8cfu, 0xe4ef8ebfu, 0x03ee1e65u, 0xcfcfc5c0u, 0x82e9e19bu, 0x5d7a8a2fu, 0xfafccd93u };
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;
}
kernel void igneum_hash(device const uint* dataset [[buffer(0)]],
device ulong* out [[buffer(1)]],
constant uint& baseNonce [[buffer(2)]],
uint gid [[thread_position_in_grid]]) {
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;
r4 = r4 + r3 + select(0x4a4c6caau, 0x5b2d5fe3u, ((sel >> 8u) & 1u) != 0u); // 0
r7 = r7 | r6; // 1
r2 = r2 * r7; // 2
r5 = r5 ^ simd_shuffle_xor(r1, (ushort)2); // 3
r4 = r4 ^ r6; // 4
r0 = r0 ^ dataset[r4 & MASK]; // 5
r3 = rotl_imm(r3, 17u); // 6
r1 = r1 + r4 + select(0xaeb38cc5u, 0x83aa2c52u, ((sel >> 27u) & 1u) != 0u); // 7
r1 = rotl_imm(r1, 31u); // 8
r4 = r4 ^ r7; // 9
{ uint b_ = (r0 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r6 = x_; } // 10
{ uint b_ = (r6 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r0 = x_; } // 11
r5 = r5 ^ simd_shuffle_xor(r2, (ushort)1); // 12
r1 = r1 ^ simd_shuffle_xor(r4, (ushort)2); // 13
r5 = r5 * r7; // 14
r3 = r3 ^ r0; // 15
r5 = r5 * r4; // 16
r2 = r2 ^ simd_shuffle_xor(r0, (ushort)4); // 17
r4 = r4 + r1 + select(0x87d3a998u, 0xfb36bddau, ((sel >> 31u) & 1u) != 0u); // 18
r0 = r0 + r1 + select(0x88921092u, 0xf2ef7076u, ((sel >> 22u) & 1u) != 0u); // 19
r6 = r6 + r1 + select(0x35e06b74u, 0xe42e69c6u, ((sel >> 3u) & 1u) != 0u); // 20
r2 = r2 ^ simd_shuffle_xor(r5, (ushort)8); // 21
r3 = r3 + r6 + select(0x74df5734u, 0xb45d9671u, ((sel >> 3u) & 1u) != 0u); // 22
r5 = r5 ^ simd_shuffle_xor(r4, (ushort)8); // 23
r4 = r4 ^ dataset[r2 & MASK]; // 24
r2 = r2 ^ r1; // 25
r7 = r7 * r6; // 26
{ uint b_ = (r3 & MASK) & ~15u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r2 = x_; } // 27
r3 = rotl_imm(r3, 29u); // 28
r3 = r3 ^ simd_shuffle_xor(r0, (ushort)8); // 29
r1 = r1 ^ dataset[r4 & MASK]; // 30
r4 = r4 - r6; // 31
r2 = r2 * r4; // 32
r4 = r3 * r3 + r4; // 33
r0 = r0 + r6 + select(0x19318d72u, 0x97d2762du, ((sel >> 15u) & 1u) != 0u); // 34
r7 = r7 + r1 + select(0x26b63296u, 0x79830eadu, ((sel >> 19u) & 1u) != 0u); // 35
r7 = r7 ^ dataset[r2 & MASK]; // 36
r0 = rotr_var(r0, r7); // 37
r2 = r2 + r7 + select(0xb228dc81u, 0x23c8dec4u, ((sel >> 28u) & 1u) != 0u); // 38
r0 = r0 ^ dataset[r5 & MASK]; // 39
r7 = r7 ^ simd_shuffle_xor(r5, (ushort)2); // 40
r5 = r0 * r1 + r5; // 41
r2 = r2 ^ r3; // 42
r7 = r7 ^ dataset[r0 & MASK]; // 43
{ uint b_ = (r5 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r0 = x_; } // 44
r2 = r2 + r7 + select(0xcc64df8eu, 0x5a3a7fe1u, ((sel >> 22u) & 1u) != 0u); // 45
r0 = rotr_var(r0, r5); // 46
r3 = rotr_var(r3, r7); // 47
r2 = r7 * r6 + r2; // 48
r6 = r3 * r5 + r6; // 49
r1 = r1 ^ dataset[r6 & MASK]; // 50
r7 = r7 ^ dataset[r1 & MASK]; // 51
r3 = r3 + r4 + select(0x7a646d78u, 0xcba22643u, ((sel >> 6u) & 1u) != 0u); // 52
r4 = r4 ^ r0; // 53
r3 = r3 ^ simd_shuffle_xor(r7, (ushort)4); // 54
r0 = rotr_var(r0, r1); // 55
r5 = r5 ^ r3; // 56
r1 = r1 + r0 + select(0x6f26b909u, 0x1834af00u, ((sel >> 8u) & 1u) != 0u); // 57
r6 = r6 ^ dataset[r4 & MASK]; // 58
{ uint b_ = (r7 & MASK) & ~15u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint x_ = r1 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r1 = x_; } // 59
r1 = r1 ^ dataset[r0 & MASK]; // 60
r4 = r4 ^ r7; // 61
r6 = r2 * r1 + r6; // 62
{ uint b_ = (r2 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r3 = x_; } // 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;
}

View file

@ -0,0 +1,111 @@
#include <metal_stdlib>
using namespace metal;
#define MASK 0x0fffffffu
constant uint SEEDW[8] = { 0xa7198abfu, 0x6cd0f8cfu, 0xe4ef8ebfu, 0x03ee1e65u, 0xcfcfc5c0u, 0x82e9e19bu, 0x5d7a8a2fu, 0xfafccd93u };
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;
}
// Header-bound variant: the init words come from buffer 3 (bind.rs), not from SEEDW.
kernel void igneum_hash_bound(device const uint* dataset [[buffer(0)]],
device ulong* out [[buffer(1)]],
constant uint& baseNonce [[buffer(2)]],
constant uint* initw [[buffer(3)]],
uint gid [[thread_position_in_grid]]) {
uint nonce = baseNonce + gid;
uint r0, r1, r2, r3, r4, r5, r6, r7;
{ uint x = nonce ^ initw[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ initw[1]; }
{ uint x = nonce ^ initw[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ initw[2]; }
{ uint x = nonce ^ initw[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ initw[3]; }
{ uint x = nonce ^ initw[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ initw[4]; }
{ uint x = nonce ^ initw[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ initw[5]; }
{ uint x = nonce ^ initw[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ initw[6]; }
{ uint x = nonce ^ initw[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ initw[7]; }
{ uint x = nonce ^ initw[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ initw[0]; }
for (uint it = 0u; it < 8u; ++it) {
uint sel = r0;
r4 = r4 + r3 + select(0x4a4c6caau, 0x5b2d5fe3u, ((sel >> 8u) & 1u) != 0u); // 0
r7 = r7 | r6; // 1
r2 = r2 * r7; // 2
r5 = r5 ^ simd_shuffle_xor(r1, (ushort)2); // 3
r4 = r4 ^ r6; // 4
r0 = r0 ^ dataset[r4 & MASK]; // 5
r3 = rotl_imm(r3, 17u); // 6
r1 = r1 + r4 + select(0xaeb38cc5u, 0x83aa2c52u, ((sel >> 27u) & 1u) != 0u); // 7
r1 = rotl_imm(r1, 31u); // 8
r4 = r4 ^ r7; // 9
{ uint b_ = (r0 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r6 = x_; } // 10
{ uint b_ = (r6 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r0 = x_; } // 11
r5 = r5 ^ simd_shuffle_xor(r2, (ushort)1); // 12
r1 = r1 ^ simd_shuffle_xor(r4, (ushort)2); // 13
r5 = r5 * r7; // 14
r3 = r3 ^ r0; // 15
r5 = r5 * r4; // 16
r2 = r2 ^ simd_shuffle_xor(r0, (ushort)4); // 17
r4 = r4 + r1 + select(0x87d3a998u, 0xfb36bddau, ((sel >> 31u) & 1u) != 0u); // 18
r0 = r0 + r1 + select(0x88921092u, 0xf2ef7076u, ((sel >> 22u) & 1u) != 0u); // 19
r6 = r6 + r1 + select(0x35e06b74u, 0xe42e69c6u, ((sel >> 3u) & 1u) != 0u); // 20
r2 = r2 ^ simd_shuffle_xor(r5, (ushort)8); // 21
r3 = r3 + r6 + select(0x74df5734u, 0xb45d9671u, ((sel >> 3u) & 1u) != 0u); // 22
r5 = r5 ^ simd_shuffle_xor(r4, (ushort)8); // 23
r4 = r4 ^ dataset[r2 & MASK]; // 24
r2 = r2 ^ r1; // 25
r7 = r7 * r6; // 26
{ uint b_ = (r3 & MASK) & ~15u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r2 = x_; } // 27
r3 = rotl_imm(r3, 29u); // 28
r3 = r3 ^ simd_shuffle_xor(r0, (ushort)8); // 29
r1 = r1 ^ dataset[r4 & MASK]; // 30
r4 = r4 - r6; // 31
r2 = r2 * r4; // 32
r4 = r3 * r3 + r4; // 33
r0 = r0 + r6 + select(0x19318d72u, 0x97d2762du, ((sel >> 15u) & 1u) != 0u); // 34
r7 = r7 + r1 + select(0x26b63296u, 0x79830eadu, ((sel >> 19u) & 1u) != 0u); // 35
r7 = r7 ^ dataset[r2 & MASK]; // 36
r0 = rotr_var(r0, r7); // 37
r2 = r2 + r7 + select(0xb228dc81u, 0x23c8dec4u, ((sel >> 28u) & 1u) != 0u); // 38
r0 = r0 ^ dataset[r5 & MASK]; // 39
r7 = r7 ^ simd_shuffle_xor(r5, (ushort)2); // 40
r5 = r0 * r1 + r5; // 41
r2 = r2 ^ r3; // 42
r7 = r7 ^ dataset[r0 & MASK]; // 43
{ uint b_ = (r5 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r0 = x_; } // 44
r2 = r2 + r7 + select(0xcc64df8eu, 0x5a3a7fe1u, ((sel >> 22u) & 1u) != 0u); // 45
r0 = rotr_var(r0, r5); // 46
r3 = rotr_var(r3, r7); // 47
r2 = r7 * r6 + r2; // 48
r6 = r3 * r5 + r6; // 49
r1 = r1 ^ dataset[r6 & MASK]; // 50
r7 = r7 ^ dataset[r1 & MASK]; // 51
r3 = r3 + r4 + select(0x7a646d78u, 0xcba22643u, ((sel >> 6u) & 1u) != 0u); // 52
r4 = r4 ^ r0; // 53
r3 = r3 ^ simd_shuffle_xor(r7, (ushort)4); // 54
r0 = rotr_var(r0, r1); // 55
r5 = r5 ^ r3; // 56
r1 = r1 + r0 + select(0x6f26b909u, 0x1834af00u, ((sel >> 8u) & 1u) != 0u); // 57
r6 = r6 ^ dataset[r4 & MASK]; // 58
{ uint b_ = (r7 & MASK) & ~15u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint x_ = r1 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r1 = x_; } // 59
r1 = r1 ^ dataset[r0 & MASK]; // 60
r4 = r4 ^ r7; // 61
r6 = r2 * r1 + r6; // 62
{ uint b_ = (r2 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r3 = x_; } // 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;
}

View file

@ -0,0 +1,57 @@
// Generated by igneum-pow export (generator v2) for seed "igneum-readwidth/A/1". Do not edit by hand.
// Expected outputs: igneum-pow (Rust) CPU interpreter, generator v2, memory-hard dataset
#pragma once
#ifdef __cplusplus
#include <cstdint>
#else
#include <stdint.h>
#endif
#define IGNEUM_VEC_WARPS 3
static const uint32_t IGNEUM_VEC_BASE[IGNEUM_VEC_WARPS] = { 0u, 4096u, 1000000u };
static const uint64_t IGNEUM_VEC_OUT[IGNEUM_VEC_WARPS][32] = {
{ // base nonce 0
0x9a39e4c3b724e139ull, 0x0b2f9aefe9b5a7d9ull, 0x489f25154d36b837ull, 0x2bacca9851bedf63ull, 0x23b9e9d6f6f024a2ull, 0xa297adc388367ac7ull, 0x5f77305cf55428e2ull, 0x13f19dd950363e8cull,
0xd3cc9c7923f85ae7ull, 0x5fdfc76e2559aaebull, 0x24bbc07c2b73b227ull, 0x5c128b8728383521ull, 0x95536c4dd0071840ull, 0xc2337e88f9eba324ull, 0x8a0d0d5db0b8c0c0ull, 0x4aa918c941e517f8ull,
0x00b15821dc1a563dull, 0xa13888b391d5c4ffull, 0x2545a17fbecd2233ull, 0xf340d8b211921866ull, 0x827e215990856eccull, 0xa524b3dff5acb2f2ull, 0x495b540800df7c9eull, 0x9e274c9e9ed7edc5ull,
0x5b8c358f08d10146ull, 0x7745916245fd6bd6ull, 0x20ab3090ecbf86e7ull, 0x3c2429a761881d58ull, 0xb14ad7a2b75a3e5bull, 0xd040113da7d8929aull, 0x0aac7bad8ef1785full, 0x69ef296bba30963dull
},
{ // base nonce 4096
0x380980b425973372ull, 0xb039f8c8f2085f2eull, 0xdb9093fb95d8bffbull, 0x87fb86cda3374fcfull, 0x4138cea25d6a4d5full, 0xbe0a9b404a88c69full, 0xc696db4aa95e6473ull, 0x6d7fd33fb2be2a34ull,
0x46acefa0c5e54e1full, 0xe898e7d08e946d6cull, 0xea998c0f7965842aull, 0xf6b60065df70f28dull, 0x24622e14557fd974ull, 0x5730af77da1186ffull, 0xf776630a6ff0a022ull, 0xa69c82d11c376933ull,
0x93fb4e2c9c12456full, 0xa7a30a3c776b32cfull, 0x1669db65ca0f2724ull, 0x491df6828bfc7ad0ull, 0xeb5bf506947713c2ull, 0xe83fd62f415cfe9eull, 0xbc4415e97b359dd0ull, 0x2ab09721b97d003full,
0xaf04c23a63a7bb7full, 0x6c4986d838379e4aull, 0x0dcecd1729525d44ull, 0x92ba240b16950bbaull, 0xed6df8cc108a7e50ull, 0x60252d3be009c7eaull, 0x300d103ffc4e8d35ull, 0x73522acf3d2f56d4ull
},
{ // base nonce 1000000
0x254a52e3824ffcb7ull, 0x35f6dfb86ffa9f8eull, 0x27838da0f63ce460ull, 0xd25ec8a499e26256ull, 0xfe3b4f012071b8b2ull, 0xde760f5a3e5378cdull, 0x0810bd3a5f232ac3ull, 0x7ee980b9098f2df2ull,
0xbae54f58f6daab8eull, 0x6f89d253a6b184f1ull, 0xb9c87bb5f8914f6full, 0xd264d719aa5d7bd5ull, 0x23e89f82b32a5a44ull, 0x080a72f8a360f627ull, 0xe28b507b1cba42beull, 0x25b77fdc149f8eb7ull,
0x1a34f036f09596f5ull, 0x3e67e706d4719f7eull, 0x33764e53c58097a6ull, 0xbcf62943f155349aull, 0x8eb89841fbcb8ee5ull, 0x386b0433a1567ee7ull, 0x0c5775cb8fce2a91ull, 0x64518cc52f393a59ull,
0x8f3cdb40e81a1cddull, 0x6af5a66a0a2436c1ull, 0xb76a5386454bef44ull, 0xfdd12f4ff2f53256ull, 0xf9ff7b79b577274full, 0xe7ed16e8ad89f703ull, 0x5a64c33fdfec335eull, 0xda50f54c2c547ac1ull
}
};
// Dataset self-test: dataset[0..15] and dataset[IGNEUM_MASK] (268435455).
static const uint32_t IGNEUM_DS_HEAD[16] = {
0xffc3cd94u, 0x5920ccd8u, 0x392f44bbu, 0x5e57f67au, 0x2f2bc2a9u, 0x620b0e36u, 0xbdc09014u, 0x436654bfu,
0x311e0b48u, 0x1abd93adu, 0x59cc7ce8u, 0xee5247b2u, 0x86171fe8u, 0x6d874751u, 0xc9f7728fu, 0x7c2a435du
};
static const uint32_t IGNEUM_DS_LAST_INDEX = 268435455u;
static const uint32_t IGNEUM_DS_LAST = 0xa33ada72u;
// 64 sampled dataset words (index, value) computed on the Mac.
#define IGNEUM_DS_SAMPLES 64
static const uint32_t IGNEUM_DS_SAMPLE_INDEX[IGNEUM_DS_SAMPLES] = {
59471966u, 217795994u, 208353206u, 42483309u, 172547758u, 148076330u, 183853158u, 214389424u, 267488061u, 169781097u, 184093494u, 153880993u, 84977930u, 46426879u, 3093825u, 225364072u, 44593546u, 260713159u, 168250303u, 52384140u, 223401610u, 45554030u, 95410555u, 175039924u, 79171087u, 267580473u, 24168642u, 37981670u, 171551130u, 195559979u, 204611762u, 140997658u, 138925853u, 86637313u, 20736778u, 219665210u, 160430336u, 264654675u, 8013395u, 228945585u, 213884386u, 104419827u, 44185464u, 142737231u, 99284897u, 132475900u, 61861762u, 132056166u, 262388043u, 91878046u, 117353561u, 124768597u, 71352993u, 190698941u, 46055428u, 55281366u, 165145231u, 106810753u, 171985651u, 232085256u, 159510492u, 40072060u, 209107596u, 39023794u
};
static const uint32_t IGNEUM_DS_SAMPLE_VALUE[IGNEUM_DS_SAMPLES] = {
0xe8b73d94u, 0x337028b5u, 0xafe148c9u, 0xab99f7aeu, 0x434ea619u, 0xd85cb880u, 0x54764c7fu, 0x82c7e420u, 0xedf4cb9eu, 0x9884c959u, 0x223ee793u, 0x3a9ccf69u, 0x81da4fd2u, 0xd6ce8cb9u, 0xe3922dcau, 0x3e7e6bdeu, 0x382a3acau, 0x567e7f7fu, 0x25a0f084u, 0xbfeef128u, 0xe338abfbu, 0x7c3b5280u, 0x909bc5f1u, 0xd8b74b9cu, 0x8e31a22eu, 0x26b5f1d8u, 0x79122c00u, 0xcafc3340u, 0xd5e02ea3u, 0x1aee1afdu, 0xdb090d9au, 0xb049f435u, 0x4954d8bau, 0x03797ba0u, 0x196eefbdu, 0xd153412au, 0xbe5d2c4bu, 0xdaa14f0eu, 0x8e61ed07u, 0x9e9a64c6u, 0x2e29ff36u, 0x392a8589u, 0xb56a5912u, 0xfa6e8b57u, 0xd1a737cbu, 0xb0fa841au, 0xbe1c341fu, 0xe25be0f1u, 0xe937f543u, 0xebab2248u, 0x8e1b607au, 0x202a2fedu, 0x95e2819cu, 0x9c9652d4u, 0x32fedef0u, 0xdecfff82u, 0xcb5d43e5u, 0xb735806au, 0x8905939cu, 0xfbf8472du, 0xada74e5du, 0x7ebdeeeau, 0x0119f2b3u, 0xa9a376b8u
};
// Cache self-test (memory-hard mode): cache[0..15], the last 16 words, and FNV-1a 64 over all 2^26 words.
static const uint32_t IGNEUM_CACHE_HEAD[16] = {
0x355a86d2u, 0x7957db1cu, 0xd21772afu, 0x6fc1e09bu, 0xd55ce61du, 0x6e6a278bu, 0xd3f543ceu, 0x223d8e82u,
0x143ab337u, 0x2e9f05bdu, 0x2eb389bfu, 0x0c6e449eu, 0x5cfa4222u, 0xba6560feu, 0x8e3e1aa4u, 0xdbcc1d53u
};
static const uint32_t IGNEUM_CACHE_LAST[16] = {
0x41190d91u, 0xbd277957u, 0x22ddbb49u, 0x6986f207u, 0xdf69a4d6u, 0x26401a3au, 0x818230fbu, 0xc417122du,
0x3597b211u, 0xb553ce55u, 0xcf39cc0du, 0x3b7fc43au, 0x3fd43b00u, 0x67e1c80eu, 0xffa7ea7du, 0xca2960abu
};
static const uint64_t IGNEUM_CACHE_FNV64 = 0x48c4f5bf24166b2eull;

View file

@ -0,0 +1,36 @@
{
"seed": "igneum-readwidth/A/1",
"day": "2026-10-03",
"dataset_mode": "memory-hard",
"dataset_log2_words": 28,
"mask": "0x0fffffff",
"lanes": 32,
"source": "igneum-pow (Rust) CPU interpreter, generator v2, memory-hard dataset",
"warps": [
{"base_nonce": 0, "expected": [
"0x9a39e4c3b724e139", "0x0b2f9aefe9b5a7d9", "0x489f25154d36b837", "0x2bacca9851bedf63", "0x23b9e9d6f6f024a2", "0xa297adc388367ac7", "0x5f77305cf55428e2", "0x13f19dd950363e8c",
"0xd3cc9c7923f85ae7", "0x5fdfc76e2559aaeb", "0x24bbc07c2b73b227", "0x5c128b8728383521", "0x95536c4dd0071840", "0xc2337e88f9eba324", "0x8a0d0d5db0b8c0c0", "0x4aa918c941e517f8",
"0x00b15821dc1a563d", "0xa13888b391d5c4ff", "0x2545a17fbecd2233", "0xf340d8b211921866", "0x827e215990856ecc", "0xa524b3dff5acb2f2", "0x495b540800df7c9e", "0x9e274c9e9ed7edc5",
"0x5b8c358f08d10146", "0x7745916245fd6bd6", "0x20ab3090ecbf86e7", "0x3c2429a761881d58", "0xb14ad7a2b75a3e5b", "0xd040113da7d8929a", "0x0aac7bad8ef1785f", "0x69ef296bba30963d"
]},
{"base_nonce": 4096, "expected": [
"0x380980b425973372", "0xb039f8c8f2085f2e", "0xdb9093fb95d8bffb", "0x87fb86cda3374fcf", "0x4138cea25d6a4d5f", "0xbe0a9b404a88c69f", "0xc696db4aa95e6473", "0x6d7fd33fb2be2a34",
"0x46acefa0c5e54e1f", "0xe898e7d08e946d6c", "0xea998c0f7965842a", "0xf6b60065df70f28d", "0x24622e14557fd974", "0x5730af77da1186ff", "0xf776630a6ff0a022", "0xa69c82d11c376933",
"0x93fb4e2c9c12456f", "0xa7a30a3c776b32cf", "0x1669db65ca0f2724", "0x491df6828bfc7ad0", "0xeb5bf506947713c2", "0xe83fd62f415cfe9e", "0xbc4415e97b359dd0", "0x2ab09721b97d003f",
"0xaf04c23a63a7bb7f", "0x6c4986d838379e4a", "0x0dcecd1729525d44", "0x92ba240b16950bba", "0xed6df8cc108a7e50", "0x60252d3be009c7ea", "0x300d103ffc4e8d35", "0x73522acf3d2f56d4"
]},
{"base_nonce": 1000000, "expected": [
"0x254a52e3824ffcb7", "0x35f6dfb86ffa9f8e", "0x27838da0f63ce460", "0xd25ec8a499e26256", "0xfe3b4f012071b8b2", "0xde760f5a3e5378cd", "0x0810bd3a5f232ac3", "0x7ee980b9098f2df2",
"0xbae54f58f6daab8e", "0x6f89d253a6b184f1", "0xb9c87bb5f8914f6f", "0xd264d719aa5d7bd5", "0x23e89f82b32a5a44", "0x080a72f8a360f627", "0xe28b507b1cba42be", "0x25b77fdc149f8eb7",
"0x1a34f036f09596f5", "0x3e67e706d4719f7e", "0x33764e53c58097a6", "0xbcf62943f155349a", "0x8eb89841fbcb8ee5", "0x386b0433a1567ee7", "0x0c5775cb8fce2a91", "0x64518cc52f393a59",
"0x8f3cdb40e81a1cdd", "0x6af5a66a0a2436c1", "0xb76a5386454bef44", "0xfdd12f4ff2f53256", "0xf9ff7b79b577274f", "0xe7ed16e8ad89f703", "0x5a64c33fdfec335e", "0xda50f54c2c547ac1"
]}
],
"dataset_head": ["0xffc3cd94", "0x5920ccd8", "0x392f44bb", "0x5e57f67a", "0x2f2bc2a9", "0x620b0e36", "0xbdc09014", "0x436654bf", "0x311e0b48", "0x1abd93ad", "0x59cc7ce8", "0xee5247b2", "0x86171fe8", "0x6d874751", "0xc9f7728f", "0x7c2a435d"],
"dataset_last_index": 268435455,
"dataset_last": "0xa33ada72",
"dataset_samples": [{"index": 59471966, "value": "0xe8b73d94"}, {"index": 217795994, "value": "0x337028b5"}, {"index": 208353206, "value": "0xafe148c9"}, {"index": 42483309, "value": "0xab99f7ae"}, {"index": 172547758, "value": "0x434ea619"}, {"index": 148076330, "value": "0xd85cb880"}, {"index": 183853158, "value": "0x54764c7f"}, {"index": 214389424, "value": "0x82c7e420"}, {"index": 267488061, "value": "0xedf4cb9e"}, {"index": 169781097, "value": "0x9884c959"}, {"index": 184093494, "value": "0x223ee793"}, {"index": 153880993, "value": "0x3a9ccf69"}, {"index": 84977930, "value": "0x81da4fd2"}, {"index": 46426879, "value": "0xd6ce8cb9"}, {"index": 3093825, "value": "0xe3922dca"}, {"index": 225364072, "value": "0x3e7e6bde"}, {"index": 44593546, "value": "0x382a3aca"}, {"index": 260713159, "value": "0x567e7f7f"}, {"index": 168250303, "value": "0x25a0f084"}, {"index": 52384140, "value": "0xbfeef128"}, {"index": 223401610, "value": "0xe338abfb"}, {"index": 45554030, "value": "0x7c3b5280"}, {"index": 95410555, "value": "0x909bc5f1"}, {"index": 175039924, "value": "0xd8b74b9c"}, {"index": 79171087, "value": "0x8e31a22e"}, {"index": 267580473, "value": "0x26b5f1d8"}, {"index": 24168642, "value": "0x79122c00"}, {"index": 37981670, "value": "0xcafc3340"}, {"index": 171551130, "value": "0xd5e02ea3"}, {"index": 195559979, "value": "0x1aee1afd"}, {"index": 204611762, "value": "0xdb090d9a"}, {"index": 140997658, "value": "0xb049f435"}, {"index": 138925853, "value": "0x4954d8ba"}, {"index": 86637313, "value": "0x03797ba0"}, {"index": 20736778, "value": "0x196eefbd"}, {"index": 219665210, "value": "0xd153412a"}, {"index": 160430336, "value": "0xbe5d2c4b"}, {"index": 264654675, "value": "0xdaa14f0e"}, {"index": 8013395, "value": "0x8e61ed07"}, {"index": 228945585, "value": "0x9e9a64c6"}, {"index": 213884386, "value": "0x2e29ff36"}, {"index": 104419827, "value": "0x392a8589"}, {"index": 44185464, "value": "0xb56a5912"}, {"index": 142737231, "value": "0xfa6e8b57"}, {"index": 99284897, "value": "0xd1a737cb"}, {"index": 132475900, "value": "0xb0fa841a"}, {"index": 61861762, "value": "0xbe1c341f"}, {"index": 132056166, "value": "0xe25be0f1"}, {"index": 262388043, "value": "0xe937f543"}, {"index": 91878046, "value": "0xebab2248"}, {"index": 117353561, "value": "0x8e1b607a"}, {"index": 124768597, "value": "0x202a2fed"}, {"index": 71352993, "value": "0x95e2819c"}, {"index": 190698941, "value": "0x9c9652d4"}, {"index": 46055428, "value": "0x32fedef0"}, {"index": 55281366, "value": "0xdecfff82"}, {"index": 165145231, "value": "0xcb5d43e5"}, {"index": 106810753, "value": "0xb735806a"}, {"index": 171985651, "value": "0x8905939c"}, {"index": 232085256, "value": "0xfbf8472d"}, {"index": 159510492, "value": "0xada74e5d"}, {"index": 40072060, "value": "0x7ebdeeea"}, {"index": 209107596, "value": "0x0119f2b3"}, {"index": 39023794, "value": "0xa9a376b8"}],
"cache_head": ["0x355a86d2", "0x7957db1c", "0xd21772af", "0x6fc1e09b", "0xd55ce61d", "0x6e6a278b", "0xd3f543ce", "0x223d8e82", "0x143ab337", "0x2e9f05bd", "0x2eb389bf", "0x0c6e449e", "0x5cfa4222", "0xba6560fe", "0x8e3e1aa4", "0xdbcc1d53"],
"cache_last_line": ["0x41190d91", "0xbd277957", "0x22ddbb49", "0x6986f207", "0xdf69a4d6", "0x26401a3a", "0x818230fb", "0xc417122d", "0x3597b211", "0xb553ce55", "0xcf39cc0d", "0x3b7fc43a", "0x3fd43b00", "0x67e1c80e", "0xffa7ea7d", "0xca2960ab"],
"cache_fnv1a64": "0x48c4f5bf24166b2e"
}

View file

@ -0,0 +1,278 @@
// Generated by igneum-pow export (generator v2) for seed "igneum-readwidth/A/2". Do not edit by hand.
// OpenCL C twin of the Metal kernel for the same seed (see proto-opencl/README.md, WAVEFRONT.md and program.metal).
// Built from source at runtime by proto-opencl/host.c, which passes these defines:
// IGNEUM_GROUP work-group size of igneum_hash, a multiple of 32 (default 32: one work-group = one 32-lane unit)
// IGNEUM_EXCHANGE 0 = local-memory exchange with a barrier (any device, any wave width; the default)
// 1 = sub_group_shuffle_xor (cl_khr_subgroup_shuffle), only with IGNEUM_GROUP 32 and a sub-group size of exactly 32
// 2 = intel_sub_group_shuffle_xor (cl_intel_subgroups), same condition
// The verification unit is always 32 lanes. A 64-wide hardware wave (AMD GCN/CDNA, RDNA in wave64) runs two units;
// the exchange masks are 1, 2, 4, 8, 16, so every partner lane lies inside the lane's own aligned run of 32.
#ifndef IGNEUM_GROUP
#define IGNEUM_GROUP 32
#endif
#ifndef IGNEUM_EXCHANGE
#define IGNEUM_EXCHANGE 0
#endif
#ifdef __OPENCL_VERSION__
#define IGNEUM_KERNEL_HASH __kernel __attribute__((reqd_work_group_size(IGNEUM_GROUP, 1, 1)))
#define IGNEUM_LOCAL_WORDS(name, n) __local uint name[n]
#if IGNEUM_EXCHANGE == 1
#ifdef cl_khr_subgroups
#pragma OPENCL EXTENSION cl_khr_subgroups : enable
#endif
#ifdef cl_khr_subgroup_shuffle
#pragma OPENCL EXTENSION cl_khr_subgroup_shuffle : enable
#endif
#elif IGNEUM_EXCHANGE == 2
#pragma OPENCL EXTENSION cl_intel_subgroups : enable
#endif
#else
// Not an OpenCL compiler: proto-opencl/emu compiles this file as C++ and supplies the built-ins and these two macros.
#include "emu_opencl.h"
#endif
#if IGNEUM_EXCHANGE == 1
#define IGNEUM_SHFL_XOR(dst, a, m) dst = sub_group_shuffle_xor((a), (uint)(m))
#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u)
#elif IGNEUM_EXCHANGE == 2
#define IGNEUM_SHFL_XOR(dst, a, m) dst = intel_sub_group_shuffle_xor((a), (uint)(m))
#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u)
#else
// Local-memory exchange. Two buffers of IGNEUM_GROUP words alternate (xk counts exchanges), so one barrier per
// exchange is enough: a lane can only overwrite buffer b at exchange k+2 after passing barrier k+1, and every lane
// reaches barrier k+1 only after its read of buffer b at exchange k. The partner lid ^ m stays inside the lane's
// aligned run of 32 because m < 32. Control flow is uniform, so every work-item reaches every barrier.
#define IGNEUM_SHFL_XOR(dst, a, m) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid ^ (uint)(m))]; xk += 1u; }
#define IGNEUM_BCAST0(dst, a) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid & ~31u)]; xk += 1u; }
#endif
static inline uint splitmix32(uint 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. OpenCL rotate() rotates left by n modulo 32.
static inline uint rotl_imm(uint x, uint n) { return rotate(x, n); }
// Right rotation by n modulo 32 as a left rotation by (32 - n) modulo 32; n == 0 gives x.
static inline uint rotr_var(uint x, uint n) { return rotate(x, (0u - n) & 31u); }
static 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;
}
// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines.
// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals.
#define MH_CACHE_LINE_MASK 0x003fffffu
#define MH_SEGMENT_LINES 64u
#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); }
static inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site
// y = ChaCha12 core(x) + x
static inline void mh_chacha_block(const uint* x, uint* y) {
for (uint i = 0u; i < 16u; ++i) y[i] = x[i];
for (uint r = 0u; r < 6u; ++r) {
MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u)
MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u)
}
for (uint i = 0u; i < 16u; ++i) y[i] += x[i];
}
// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0.
static inline void mh_cache_segment(__global uint* cache, uint seg) {
uint prev[16]; uint x[16]; uint y[16];
for (uint i = 0u; i < 16u; ++i) prev[i] = 0u;
for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) {
x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3];
x[4] = 0x3067619fu ^ prev[4];
x[5] = 0x3c269176u ^ prev[5];
x[6] = 0x84a03b03u ^ prev[6];
x[7] = 0xf8c63294u ^ prev[7];
x[8] = 0xff977c5bu ^ prev[8];
x[9] = 0xe60def3eu ^ prev[9];
x[10] = 0x63630141u ^ prev[10];
x[11] = 0xb8fbcb58u ^ prev[11];
x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15];
mh_chacha_block(x, y);
__global uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u);
for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; }
}
}
// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations.
static inline void mh_mixer(uint* s, uint rk) {
s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u;
s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu;
s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u;
s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu;
s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u;
s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u;
s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u;
s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u;
s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du;
s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u;
s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du;
s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu;
s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du;
s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu;
s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u;
s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u;
MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u)
MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u)
MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u)
MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u)
}
// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer.
static inline void mh_item(__global const uint* cache, uint t, uint* s) {
s[0] = 0x3067619fu;
s[1] = 0x3c269176u;
s[2] = 0x84a03b03u;
s[3] = 0xf8c63294u;
s[4] = 0xff977c5bu;
s[5] = 0xe60def3eu;
s[6] = 0x63630141u;
s[7] = 0xb8fbcb58u;
s[8] = t * 0x42146205u + 0xbab68293u;
s[9] = t * 0x52cbe0fbu + 0xcc162340u;
s[10] = t * 0x7ecf4a03u + 0x6ce151ccu;
s[11] = t * 0x6728907fu + 0xe62b8997u;
s[12] = t * 0xd81d9751u + 0xc9c80297u;
s[13] = t * 0x132952c3u + 0xf74a1654u;
s[14] = t * 0xf60de277u + 0x3d704af5u;
s[15] = t * 0x05358035u + 0x3cf522b7u;
for (uint r = 0u; r < 8u; ++r) {
mh_mixer(s, 0x9E3779B9u * (r + 1u));
__global const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u);
for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i];
}
mh_mixer(s, 0x9E3779B9u * 9u);
}
// dataset[w] without the dataset: derive item w >> 4 and take word w & 15.
static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; }
// Memory-hard dataset (MEMHARD.md). One work-item per cache segment; one work-item per 64-byte dataset item.
// The same constants as memhard.h in this pack (one emitter, three dialects).
__kernel void igneum_cache_fill(__global uint* cache, uint nSegments) {
uint seg = (uint)get_global_id(0);
if (seg < nSegments) mh_cache_segment(cache, seg);
}
__kernel void igneum_build(__global uint* ds, __global const uint* cache, uint nItems) {
uint t = (uint)get_global_id(0);
if (t < nItems) {
uint s[16];
mh_item(cache, t, s);
__global uint* d = ds + ((ulong)t * 16u);
for (uint i = 0u; i < 16u; ++i) d[i] = s[i];
}
}
// One hash per work-item. IGNEUM_GROUP is a multiple of 32; lane = lid & 31 and every exchange stays inside the
// lane's own aligned run of 32 work-items, exactly like simd_shuffle_xor inside a 32-wide Metal SIMD group and
// __shfl_xor_sync inside a CUDA warp. Control flow is uniform (no branches at all).
IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask) {
uint gid = (uint)get_global_id(0);
uint lid = (uint)get_local_id(0);
uint nonce = baseNonce + gid;
uint r0, r1, r2, r3, r4, r5, r6, r7;
#if IGNEUM_EXCHANGE == 0
IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP);
uint xk = 0u;
#else
(void)lid;
#endif
{ uint x = nonce ^ 0x774fd410u; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x520f91b2u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1]
{ uint x = nonce ^ 0x520f91b2u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0x9357786fu; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2]
{ uint x = nonce ^ 0x9357786fu; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x8bbe44d7u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3]
{ uint x = nonce ^ 0x8bbe44d7u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0x23db18feu; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4]
{ uint x = nonce ^ 0x23db18feu; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0x52022aecu; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5]
{ uint x = nonce ^ 0x52022aecu; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0x653ea608u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6]
{ uint x = nonce ^ 0x653ea608u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0x57788e47u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7]
{ uint x = nonce ^ 0x57788e47u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x774fd410u; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0]
for (uint it = 0u; it < 8u; ++it) {
uint sel = r0;
r2 = r2 + r7 + ((((sel >> 7u) & 1u) != 0u) ? 0xd8952471u : 0x17c8e8eeu); // 0 add
r0 = rotr_var(r0, r5); // 1 rotr
r5 = r5 + r0 + ((((sel >> 21u) & 1u) != 0u) ? 0x4325cc6au : 0x37d9560au); // 2 add
r7 = r7 ^ r5; // 3 xor
{ uint b_ = (r0 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r3 = x_; } // 4 load
{ uint b_ = (r7 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r5 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r5 = x_; } // 5 load
r0 = mul_hi(r0, r5); // 6 mulhi
r4 = r4 * r7; // 7 mul
r0 = r5 * r6 + r0; // 8 mad
{ uint b_ = (r2 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r6 = x_; } // 9 load
r0 = r0 ^ r6; // 10 xor
r5 = r5 + r3 + ((((sel >> 22u) & 1u) != 0u) ? 0x81619a4cu : 0xbc4eca12u); // 11 add
r4 = mul_hi(r4, r2); // 12 mulhi
r2 = r2 ^ ds[r0 & mask]; // 13 load
r0 = r0 + r2 + ((((sel >> 3u) & 1u) != 0u) ? 0xa557fd2bu : 0xcf9919eeu); // 14 add
r4 = r4 + r5 + ((((sel >> 7u) & 1u) != 0u) ? 0x33857f70u : 0x36ec1d20u); // 15 add
r7 = r7 + r5 + ((((sel >> 6u) & 1u) != 0u) ? 0x40495714u : 0x42f5db15u); // 16 add
r7 = r7 ^ ds[r5 & mask]; // 17 load
r3 = r3 ^ r6; // 18 xor
{ uint b_ = (r2 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r7 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r7 = x_; } // 19 load
{ uint b_ = (r3 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r5 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r5 = x_; } // 20 load
r1 = r1 ^ ds[r0 & mask]; // 21 load
r1 = mul_hi(r1, r3); // 22 mulhi
r1 = rotr_var(r1, r5); // 23 rotr
r2 = mul_hi(r2, r0); // 24 mulhi
{ uint b_ = (r4 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r0 = x_; } // 25 load
{ uint b_ = (r1 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r0 = x_; } // 26 load
{ uint t_; IGNEUM_SHFL_XOR(t_, r2, 2u); r5 = r5 ^ t_; } // 27 shfl
r5 = r5 + r4 + ((((sel >> 12u) & 1u) != 0u) ? 0x58a2eb85u : 0x22029cb9u); // 28 add
{ uint b_ = (r5 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r2 = x_; } // 29 load
r6 = r6 * r5; // 30 mul
r6 = r6 ^ ds[r2 & mask]; // 31 load
r7 = mul_hi(r7, r5); // 32 mulhi
r0 = r0 ^ ds[r7 & mask]; // 33 load
{ uint t_; IGNEUM_SHFL_XOR(t_, r2, 1u); r7 = r7 ^ t_; } // 34 shfl
r7 = r7 * r0; // 35 mul
r5 = r5 ^ ds[r7 & mask]; // 36 load
r3 = rotr_var(r3, r2); // 37 rotr
r6 = rotl_imm(r6, 1u); // 38 rotl
r3 = r3 * r0; // 39 mul
r3 = mul_hi(r3, r7); // 40 mulhi
r5 = r5 ^ r2; // 41 xor
r4 = r4 * r0; // 42 mul
r3 = r3 + r0 + ((((sel >> 4u) & 1u) != 0u) ? 0xfaaf2d1eu : 0x831bfab3u); // 43 add
r0 = r1 * r6 + r0; // 44 mad
r6 = rotl_imm(r6, 26u); // 45 rotl
r2 = r2 ^ r1; // 46 xor
{ uint t_; IGNEUM_SHFL_XOR(t_, r7, 2u); r0 = r0 ^ t_; } // 47 shfl
r1 = r1 ^ r2; // 48 xor
r3 = r3 + r5 + ((((sel >> 7u) & 1u) != 0u) ? 0xc5759120u : 0x31df1a86u); // 49 add
r1 = r4 * r1 + r1; // 50 mad
{ uint t_; IGNEUM_SHFL_XOR(t_, r4, 2u); r1 = r1 ^ t_; } // 51 shfl
r5 = r5 + r4 + ((((sel >> 16u) & 1u) != 0u) ? 0x4e4a2759u : 0x553b85d1u); // 52 add
r1 = r1 ^ ds[r3 & mask]; // 53 load
r6 = rotl_imm(r6, 21u); // 54 rotl
r1 = mul_hi(r1, r4); // 55 mulhi
{ uint t_; IGNEUM_SHFL_XOR(t_, r3, 1u); r0 = r0 ^ t_; } // 56 shfl
r1 = r1 ^ r2; // 57 xor
{ uint t_; IGNEUM_SHFL_XOR(t_, r6, 16u); r3 = r3 ^ t_; } // 58 shfl
r5 = r5 * r0; // 59 mul
r4 = r4 ^ ds[r2 & mask]; // 60 load
r0 = rotr_var(r0, r4); // 61 rotr
r6 = r6 * r7; // 62 mul
r6 = rotl_imm(r6, 2u); // 63 rotl
}
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;
}
#if IGNEUM_EXCHANGE != 0
// Reports the sub-group size this device uses for a work-group of IGNEUM_GROUP items. host.c runs it only when the
// per-kernel query (clGetKernelSubGroupInfoKHR on igneum_hash) is unavailable; that query is preferred because a
// compiler may pick a different wave width per kernel (RDNA: wave32 or wave64). See WAVEFRONT.md.
IGNEUM_KERNEL_HASH void igneum_probe_subgroup(__global uint* out) {
if (get_local_id(0) == 0u) { out[0] = get_sub_group_size(); out[1] = get_num_sub_groups(); }
}
#endif

View file

@ -0,0 +1,164 @@
// Generated by igneum-pow export (generator v2) for seed "igneum-readwidth/A/2". 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"
#include "memhard.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;
}
// Memory-hard dataset (MEMHARD.md). One thread per cache segment; one thread per 64-byte dataset item.
// The core functions (mh_cache_segment, mh_item) are in memhard.h and are also compiled for the host.
__global__ void igneum_cache_fill(uint32_t* cache, uint32_t nSegments) {
uint32_t seg = blockIdx.x * blockDim.x + threadIdx.x;
if (seg < nSegments) mh_cache_segment(cache, seg);
}
__global__ void igneum_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems) {
uint32_t t = blockIdx.x * blockDim.x + threadIdx.x;
if (t < nItems) {
uint32_t s[16];
mh_item(cache, t, s);
uint32_t* d = ds + (size_t)t * 16u;
for (uint32_t i = 0u; i < 16u; ++i) d[i] = s[i];
}
}
// 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 ^ 0x774fd410u; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x520f91b2u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1]
{ uint32_t x = nonce ^ 0x520f91b2u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0x9357786fu; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2]
{ uint32_t x = nonce ^ 0x9357786fu; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x8bbe44d7u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3]
{ uint32_t x = nonce ^ 0x8bbe44d7u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0x23db18feu; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4]
{ uint32_t x = nonce ^ 0x23db18feu; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0x52022aecu; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5]
{ uint32_t x = nonce ^ 0x52022aecu; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0x653ea608u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6]
{ uint32_t x = nonce ^ 0x653ea608u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0x57788e47u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7]
{ uint32_t x = nonce ^ 0x57788e47u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x774fd410u; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0]
for (uint32_t it = 0u; it < 8u; ++it) {
uint32_t sel = r0;
r2 = r2 + r7 + ((((sel >> 7u) & 1u) != 0u) ? 0xd8952471u : 0x17c8e8eeu); // 0 add
r0 = rotr_var(r0, r5); // 1 rotr
r5 = r5 + r0 + ((((sel >> 21u) & 1u) != 0u) ? 0x4325cc6au : 0x37d9560au); // 2 add
r7 = r7 ^ r5; // 3 xor
{ uint32_t b_ = (r0 & mask) & ~15u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint32_t x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r3 = x_; } // 4 load
{ uint32_t b_ = (r7 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r5 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r5 = x_; } // 5 load
r0 = __umulhi(r0, r5); // 6 mulhi
r4 = r4 * r7; // 7 mul
r0 = r5 * r6 + r0; // 8 mad
{ uint32_t b_ = (r2 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r6 = x_; } // 9 load
r0 = r0 ^ r6; // 10 xor
r5 = r5 + r3 + ((((sel >> 22u) & 1u) != 0u) ? 0x81619a4cu : 0xbc4eca12u); // 11 add
r4 = __umulhi(r4, r2); // 12 mulhi
r2 = r2 ^ ds[r0 & mask]; // 13 load
r0 = r0 + r2 + ((((sel >> 3u) & 1u) != 0u) ? 0xa557fd2bu : 0xcf9919eeu); // 14 add
r4 = r4 + r5 + ((((sel >> 7u) & 1u) != 0u) ? 0x33857f70u : 0x36ec1d20u); // 15 add
r7 = r7 + r5 + ((((sel >> 6u) & 1u) != 0u) ? 0x40495714u : 0x42f5db15u); // 16 add
r7 = r7 ^ ds[r5 & mask]; // 17 load
r3 = r3 ^ r6; // 18 xor
{ uint32_t b_ = (r2 & mask) & ~15u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint32_t x_ = r7 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r7 = x_; } // 19 load
{ uint32_t b_ = (r3 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r5 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r5 = x_; } // 20 load
r1 = r1 ^ ds[r0 & mask]; // 21 load
r1 = __umulhi(r1, r3); // 22 mulhi
r1 = rotr_var(r1, r5); // 23 rotr
r2 = __umulhi(r2, r0); // 24 mulhi
{ uint32_t b_ = (r4 & mask) & ~15u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint32_t x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r0 = x_; } // 25 load
{ uint32_t b_ = (r1 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r0 = x_; } // 26 load
r5 = r5 ^ __shfl_xor_sync(0xffffffffu, r2, 2); // 27 shfl
r5 = r5 + r4 + ((((sel >> 12u) & 1u) != 0u) ? 0x58a2eb85u : 0x22029cb9u); // 28 add
{ uint32_t b_ = (r5 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r2 = x_; } // 29 load
r6 = r6 * r5; // 30 mul
r6 = r6 ^ ds[r2 & mask]; // 31 load
r7 = __umulhi(r7, r5); // 32 mulhi
r0 = r0 ^ ds[r7 & mask]; // 33 load
r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r2, 1); // 34 shfl
r7 = r7 * r0; // 35 mul
r5 = r5 ^ ds[r7 & mask]; // 36 load
r3 = rotr_var(r3, r2); // 37 rotr
r6 = rotl_imm(r6, 1u); // 38 rotl
r3 = r3 * r0; // 39 mul
r3 = __umulhi(r3, r7); // 40 mulhi
r5 = r5 ^ r2; // 41 xor
r4 = r4 * r0; // 42 mul
r3 = r3 + r0 + ((((sel >> 4u) & 1u) != 0u) ? 0xfaaf2d1eu : 0x831bfab3u); // 43 add
r0 = r1 * r6 + r0; // 44 mad
r6 = rotl_imm(r6, 26u); // 45 rotl
r2 = r2 ^ r1; // 46 xor
r0 = r0 ^ __shfl_xor_sync(0xffffffffu, r7, 2); // 47 shfl
r1 = r1 ^ r2; // 48 xor
r3 = r3 + r5 + ((((sel >> 7u) & 1u) != 0u) ? 0xc5759120u : 0x31df1a86u); // 49 add
r1 = r4 * r1 + r1; // 50 mad
r1 = r1 ^ __shfl_xor_sync(0xffffffffu, r4, 2); // 51 shfl
r5 = r5 + r4 + ((((sel >> 16u) & 1u) != 0u) ? 0x4e4a2759u : 0x553b85d1u); // 52 add
r1 = r1 ^ ds[r3 & mask]; // 53 load
r6 = rotl_imm(r6, 21u); // 54 rotl
r1 = __umulhi(r1, r4); // 55 mulhi
r0 = r0 ^ __shfl_xor_sync(0xffffffffu, r3, 1); // 56 shfl
r1 = r1 ^ r2; // 57 xor
r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r6, 16); // 58 shfl
r5 = r5 * r0; // 59 mul
r4 = r4 ^ ds[r2 & mask]; // 60 load
r0 = rotr_var(r0, r4); // 61 rotr
r6 = r6 * r7; // 62 mul
r6 = rotl_imm(r6, 2u); // 63 rotl
}
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_cache_fill(uint32_t* cache, uint32_t nSegments) {
if (nSegments == 0u) return cudaErrorInvalidValue;
uint32_t block = 256u;
uint32_t grid = (nSegments + block - 1u) / block;
igneum_cache_fill<<<grid, block>>>(cache, nSegments);
return cudaGetLastError();
}
cudaError_t igneum_launch_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems) {
if (nItems == 0u) return cudaErrorInvalidValue;
uint32_t block = 256u;
uint32_t grid = (nItems + block - 1u) / block;
igneum_build<<<grid, block>>>(ds, cache, nItems);
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);
}

View file

@ -0,0 +1,372 @@
// Generated by igneum-pow export (generator v2) for seed "igneum-readwidth/A/2". Do not edit by hand.
// OpenCL C twin of the Metal kernel for the same seed (see proto-opencl/README.md, WAVEFRONT.md and program.metal).
// Built from source at runtime by proto-opencl/host.c, which passes these defines:
// IGNEUM_GROUP work-group size of igneum_hash, a multiple of 32 (default 32: one work-group = one 32-lane unit)
// IGNEUM_EXCHANGE 0 = local-memory exchange with a barrier (any device, any wave width; the default)
// 1 = sub_group_shuffle_xor (cl_khr_subgroup_shuffle), only with IGNEUM_GROUP 32 and a sub-group size of exactly 32
// 2 = intel_sub_group_shuffle_xor (cl_intel_subgroups), same condition
// The verification unit is always 32 lanes. A 64-wide hardware wave (AMD GCN/CDNA, RDNA in wave64) runs two units;
// the exchange masks are 1, 2, 4, 8, 16, so every partner lane lies inside the lane's own aligned run of 32.
#ifndef IGNEUM_GROUP
#define IGNEUM_GROUP 32
#endif
#ifndef IGNEUM_EXCHANGE
#define IGNEUM_EXCHANGE 0
#endif
#ifdef __OPENCL_VERSION__
#define IGNEUM_KERNEL_HASH __kernel __attribute__((reqd_work_group_size(IGNEUM_GROUP, 1, 1)))
#define IGNEUM_LOCAL_WORDS(name, n) __local uint name[n]
#if IGNEUM_EXCHANGE == 1
#ifdef cl_khr_subgroups
#pragma OPENCL EXTENSION cl_khr_subgroups : enable
#endif
#ifdef cl_khr_subgroup_shuffle
#pragma OPENCL EXTENSION cl_khr_subgroup_shuffle : enable
#endif
#elif IGNEUM_EXCHANGE == 2
#pragma OPENCL EXTENSION cl_intel_subgroups : enable
#endif
#else
// Not an OpenCL compiler: proto-opencl/emu compiles this file as C++ and supplies the built-ins and these two macros.
#include "emu_opencl.h"
#endif
#if IGNEUM_EXCHANGE == 1
#define IGNEUM_SHFL_XOR(dst, a, m) dst = sub_group_shuffle_xor((a), (uint)(m))
#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u)
#elif IGNEUM_EXCHANGE == 2
#define IGNEUM_SHFL_XOR(dst, a, m) dst = intel_sub_group_shuffle_xor((a), (uint)(m))
#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u)
#else
// Local-memory exchange. Two buffers of IGNEUM_GROUP words alternate (xk counts exchanges), so one barrier per
// exchange is enough: a lane can only overwrite buffer b at exchange k+2 after passing barrier k+1, and every lane
// reaches barrier k+1 only after its read of buffer b at exchange k. The partner lid ^ m stays inside the lane's
// aligned run of 32 because m < 32. Control flow is uniform, so every work-item reaches every barrier.
#define IGNEUM_SHFL_XOR(dst, a, m) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid ^ (uint)(m))]; xk += 1u; }
#define IGNEUM_BCAST0(dst, a) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid & ~31u)]; xk += 1u; }
#endif
static inline uint splitmix32(uint 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. OpenCL rotate() rotates left by n modulo 32.
static inline uint rotl_imm(uint x, uint n) { return rotate(x, n); }
// Right rotation by n modulo 32 as a left rotation by (32 - n) modulo 32; n == 0 gives x.
static inline uint rotr_var(uint x, uint n) { return rotate(x, (0u - n) & 31u); }
static 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;
}
// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines.
// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals.
#define MH_CACHE_LINE_MASK 0x003fffffu
#define MH_SEGMENT_LINES 64u
#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); }
static inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site
// y = ChaCha12 core(x) + x
static inline void mh_chacha_block(const uint* x, uint* y) {
for (uint i = 0u; i < 16u; ++i) y[i] = x[i];
for (uint r = 0u; r < 6u; ++r) {
MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u)
MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u)
}
for (uint i = 0u; i < 16u; ++i) y[i] += x[i];
}
// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0.
static inline void mh_cache_segment(__global uint* cache, uint seg) {
uint prev[16]; uint x[16]; uint y[16];
for (uint i = 0u; i < 16u; ++i) prev[i] = 0u;
for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) {
x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3];
x[4] = 0x3067619fu ^ prev[4];
x[5] = 0x3c269176u ^ prev[5];
x[6] = 0x84a03b03u ^ prev[6];
x[7] = 0xf8c63294u ^ prev[7];
x[8] = 0xff977c5bu ^ prev[8];
x[9] = 0xe60def3eu ^ prev[9];
x[10] = 0x63630141u ^ prev[10];
x[11] = 0xb8fbcb58u ^ prev[11];
x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15];
mh_chacha_block(x, y);
__global uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u);
for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; }
}
}
// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations.
static inline void mh_mixer(uint* s, uint rk) {
s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u;
s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu;
s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u;
s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu;
s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u;
s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u;
s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u;
s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u;
s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du;
s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u;
s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du;
s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu;
s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du;
s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu;
s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u;
s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u;
MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u)
MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u)
MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u)
MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u)
}
// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer.
static inline void mh_item(__global const uint* cache, uint t, uint* s) {
s[0] = 0x3067619fu;
s[1] = 0x3c269176u;
s[2] = 0x84a03b03u;
s[3] = 0xf8c63294u;
s[4] = 0xff977c5bu;
s[5] = 0xe60def3eu;
s[6] = 0x63630141u;
s[7] = 0xb8fbcb58u;
s[8] = t * 0x42146205u + 0xbab68293u;
s[9] = t * 0x52cbe0fbu + 0xcc162340u;
s[10] = t * 0x7ecf4a03u + 0x6ce151ccu;
s[11] = t * 0x6728907fu + 0xe62b8997u;
s[12] = t * 0xd81d9751u + 0xc9c80297u;
s[13] = t * 0x132952c3u + 0xf74a1654u;
s[14] = t * 0xf60de277u + 0x3d704af5u;
s[15] = t * 0x05358035u + 0x3cf522b7u;
for (uint r = 0u; r < 8u; ++r) {
mh_mixer(s, 0x9E3779B9u * (r + 1u));
__global const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u);
for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i];
}
mh_mixer(s, 0x9E3779B9u * 9u);
}
// dataset[w] without the dataset: derive item w >> 4 and take word w & 15.
static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; }
// Memory-hard dataset (MEMHARD.md). One work-item per cache segment; one work-item per 64-byte dataset item.
// The same constants as memhard.h in this pack (one emitter, three dialects).
__kernel void igneum_cache_fill(__global uint* cache, uint nSegments) {
uint seg = (uint)get_global_id(0);
if (seg < nSegments) mh_cache_segment(cache, seg);
}
__kernel void igneum_build(__global uint* ds, __global const uint* cache, uint nItems) {
uint t = (uint)get_global_id(0);
if (t < nItems) {
uint s[16];
mh_item(cache, t, s);
__global uint* d = ds + ((ulong)t * 16u);
for (uint i = 0u; i < 16u; ++i) d[i] = s[i];
}
}
// One hash per work-item. IGNEUM_GROUP is a multiple of 32; lane = lid & 31 and every exchange stays inside the
// lane's own aligned run of 32 work-items, exactly like simd_shuffle_xor inside a 32-wide Metal SIMD group and
// __shfl_xor_sync inside a CUDA warp. Control flow is uniform (no branches at all).
IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask) {
uint gid = (uint)get_global_id(0);
uint lid = (uint)get_local_id(0);
uint nonce = baseNonce + gid;
uint r0, r1, r2, r3, r4, r5, r6, r7;
#if IGNEUM_EXCHANGE == 0
IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP);
uint xk = 0u;
#else
(void)lid;
#endif
{ uint x = nonce ^ 0x774fd410u; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x520f91b2u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1]
{ uint x = nonce ^ 0x520f91b2u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0x9357786fu; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2]
{ uint x = nonce ^ 0x9357786fu; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x8bbe44d7u; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3]
{ uint x = nonce ^ 0x8bbe44d7u; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0x23db18feu; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4]
{ uint x = nonce ^ 0x23db18feu; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0x52022aecu; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5]
{ uint x = nonce ^ 0x52022aecu; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0x653ea608u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6]
{ uint x = nonce ^ 0x653ea608u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0x57788e47u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7]
{ uint x = nonce ^ 0x57788e47u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x774fd410u; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0]
for (uint it = 0u; it < 8u; ++it) {
uint sel = r0;
r2 = r2 + r7 + ((((sel >> 7u) & 1u) != 0u) ? 0xd8952471u : 0x17c8e8eeu); // 0 add
r0 = rotr_var(r0, r5); // 1 rotr
r5 = r5 + r0 + ((((sel >> 21u) & 1u) != 0u) ? 0x4325cc6au : 0x37d9560au); // 2 add
r7 = r7 ^ r5; // 3 xor
{ uint b_ = (r0 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r3 = x_; } // 4 load
{ uint b_ = (r7 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r5 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r5 = x_; } // 5 load
r0 = mul_hi(r0, r5); // 6 mulhi
r4 = r4 * r7; // 7 mul
r0 = r5 * r6 + r0; // 8 mad
{ uint b_ = (r2 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r6 = x_; } // 9 load
r0 = r0 ^ r6; // 10 xor
r5 = r5 + r3 + ((((sel >> 22u) & 1u) != 0u) ? 0x81619a4cu : 0xbc4eca12u); // 11 add
r4 = mul_hi(r4, r2); // 12 mulhi
r2 = r2 ^ ds[r0 & mask]; // 13 load
r0 = r0 + r2 + ((((sel >> 3u) & 1u) != 0u) ? 0xa557fd2bu : 0xcf9919eeu); // 14 add
r4 = r4 + r5 + ((((sel >> 7u) & 1u) != 0u) ? 0x33857f70u : 0x36ec1d20u); // 15 add
r7 = r7 + r5 + ((((sel >> 6u) & 1u) != 0u) ? 0x40495714u : 0x42f5db15u); // 16 add
r7 = r7 ^ ds[r5 & mask]; // 17 load
r3 = r3 ^ r6; // 18 xor
{ uint b_ = (r2 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r7 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r7 = x_; } // 19 load
{ uint b_ = (r3 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r5 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r5 = x_; } // 20 load
r1 = r1 ^ ds[r0 & mask]; // 21 load
r1 = mul_hi(r1, r3); // 22 mulhi
r1 = rotr_var(r1, r5); // 23 rotr
r2 = mul_hi(r2, r0); // 24 mulhi
{ uint b_ = (r4 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r0 = x_; } // 25 load
{ uint b_ = (r1 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r0 = x_; } // 26 load
{ uint t_; IGNEUM_SHFL_XOR(t_, r2, 2u); r5 = r5 ^ t_; } // 27 shfl
r5 = r5 + r4 + ((((sel >> 12u) & 1u) != 0u) ? 0x58a2eb85u : 0x22029cb9u); // 28 add
{ uint b_ = (r5 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r2 = x_; } // 29 load
r6 = r6 * r5; // 30 mul
r6 = r6 ^ ds[r2 & mask]; // 31 load
r7 = mul_hi(r7, r5); // 32 mulhi
r0 = r0 ^ ds[r7 & mask]; // 33 load
{ uint t_; IGNEUM_SHFL_XOR(t_, r2, 1u); r7 = r7 ^ t_; } // 34 shfl
r7 = r7 * r0; // 35 mul
r5 = r5 ^ ds[r7 & mask]; // 36 load
r3 = rotr_var(r3, r2); // 37 rotr
r6 = rotl_imm(r6, 1u); // 38 rotl
r3 = r3 * r0; // 39 mul
r3 = mul_hi(r3, r7); // 40 mulhi
r5 = r5 ^ r2; // 41 xor
r4 = r4 * r0; // 42 mul
r3 = r3 + r0 + ((((sel >> 4u) & 1u) != 0u) ? 0xfaaf2d1eu : 0x831bfab3u); // 43 add
r0 = r1 * r6 + r0; // 44 mad
r6 = rotl_imm(r6, 26u); // 45 rotl
r2 = r2 ^ r1; // 46 xor
{ uint t_; IGNEUM_SHFL_XOR(t_, r7, 2u); r0 = r0 ^ t_; } // 47 shfl
r1 = r1 ^ r2; // 48 xor
r3 = r3 + r5 + ((((sel >> 7u) & 1u) != 0u) ? 0xc5759120u : 0x31df1a86u); // 49 add
r1 = r4 * r1 + r1; // 50 mad
{ uint t_; IGNEUM_SHFL_XOR(t_, r4, 2u); r1 = r1 ^ t_; } // 51 shfl
r5 = r5 + r4 + ((((sel >> 16u) & 1u) != 0u) ? 0x4e4a2759u : 0x553b85d1u); // 52 add
r1 = r1 ^ ds[r3 & mask]; // 53 load
r6 = rotl_imm(r6, 21u); // 54 rotl
r1 = mul_hi(r1, r4); // 55 mulhi
{ uint t_; IGNEUM_SHFL_XOR(t_, r3, 1u); r0 = r0 ^ t_; } // 56 shfl
r1 = r1 ^ r2; // 57 xor
{ uint t_; IGNEUM_SHFL_XOR(t_, r6, 16u); r3 = r3 ^ t_; } // 58 shfl
r5 = r5 * r0; // 59 mul
r4 = r4 ^ ds[r2 & mask]; // 60 load
r0 = rotr_var(r0, r4); // 61 rotr
r6 = r6 * r7; // 62 mul
r6 = rotl_imm(r6, 2u); // 63 rotl
}
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;
}
#if IGNEUM_EXCHANGE != 0
// Reports the sub-group size this device uses for a work-group of IGNEUM_GROUP items. host.c runs it only when the
// per-kernel query (clGetKernelSubGroupInfoKHR on igneum_hash) is unavailable; that query is preferred because a
// compiler may pick a different wave width per kernel (RDNA: wave32 or wave64). See WAVEFRONT.md.
IGNEUM_KERNEL_HASH void igneum_probe_subgroup(__global uint* out) {
if (get_local_id(0) == 0u) { out[0] = get_sub_group_size(); out[1] = get_num_sub_groups(); }
}
#endif
// Header-bound variant (bind.rs): the init words come from initw, not SEEDW. Same body as igneum_hash.
IGNEUM_KERNEL_HASH void igneum_hash_bound(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask, __global const uint* initw) {
uint gid = (uint)get_global_id(0);
uint lid = (uint)get_local_id(0);
uint nonce = baseNonce + gid;
uint r0, r1, r2, r3, r4, r5, r6, r7;
uint iw0 = initw[0], iw1 = initw[1], iw2 = initw[2], iw3 = initw[3], iw4 = initw[4], iw5 = initw[5], iw6 = initw[6], iw7 = initw[7];
#if IGNEUM_EXCHANGE == 0
IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP);
uint xk = 0u;
#else
(void)lid;
#endif
{ uint x = nonce ^ iw0; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ iw1; }
{ uint x = nonce ^ iw1; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ iw2; }
{ uint x = nonce ^ iw2; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ iw3; }
{ uint x = nonce ^ iw3; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ iw4; }
{ uint x = nonce ^ iw4; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ iw5; }
{ uint x = nonce ^ iw5; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ iw6; }
{ uint x = nonce ^ iw6; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ iw7; }
{ uint x = nonce ^ iw7; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ iw0; }
for (uint it = 0u; it < 8u; ++it) {
uint sel = r0;
r2 = r2 + r7 + ((((sel >> 7u) & 1u) != 0u) ? 0xd8952471u : 0x17c8e8eeu); // 0 add
r0 = rotr_var(r0, r5); // 1 rotr
r5 = r5 + r0 + ((((sel >> 21u) & 1u) != 0u) ? 0x4325cc6au : 0x37d9560au); // 2 add
r7 = r7 ^ r5; // 3 xor
{ uint b_ = (r0 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r3 = x_; } // 4 load
{ uint b_ = (r7 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r5 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r5 = x_; } // 5 load
r0 = mul_hi(r0, r5); // 6 mulhi
r4 = r4 * r7; // 7 mul
r0 = r5 * r6 + r0; // 8 mad
{ uint b_ = (r2 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r6 = x_; } // 9 load
r0 = r0 ^ r6; // 10 xor
r5 = r5 + r3 + ((((sel >> 22u) & 1u) != 0u) ? 0x81619a4cu : 0xbc4eca12u); // 11 add
r4 = mul_hi(r4, r2); // 12 mulhi
r2 = r2 ^ ds[r0 & mask]; // 13 load
r0 = r0 + r2 + ((((sel >> 3u) & 1u) != 0u) ? 0xa557fd2bu : 0xcf9919eeu); // 14 add
r4 = r4 + r5 + ((((sel >> 7u) & 1u) != 0u) ? 0x33857f70u : 0x36ec1d20u); // 15 add
r7 = r7 + r5 + ((((sel >> 6u) & 1u) != 0u) ? 0x40495714u : 0x42f5db15u); // 16 add
r7 = r7 ^ ds[r5 & mask]; // 17 load
r3 = r3 ^ r6; // 18 xor
{ uint b_ = (r2 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r7 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r7 = x_; } // 19 load
{ uint b_ = (r3 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r5 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r5 = x_; } // 20 load
r1 = r1 ^ ds[r0 & mask]; // 21 load
r1 = mul_hi(r1, r3); // 22 mulhi
r1 = rotr_var(r1, r5); // 23 rotr
r2 = mul_hi(r2, r0); // 24 mulhi
{ uint b_ = (r4 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r0 = x_; } // 25 load
{ uint b_ = (r1 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r0 = x_; } // 26 load
{ uint t_; IGNEUM_SHFL_XOR(t_, r2, 2u); r5 = r5 ^ t_; } // 27 shfl
r5 = r5 + r4 + ((((sel >> 12u) & 1u) != 0u) ? 0x58a2eb85u : 0x22029cb9u); // 28 add
{ uint b_ = (r5 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r2 = x_; } // 29 load
r6 = r6 * r5; // 30 mul
r6 = r6 ^ ds[r2 & mask]; // 31 load
r7 = mul_hi(r7, r5); // 32 mulhi
r0 = r0 ^ ds[r7 & mask]; // 33 load
{ uint t_; IGNEUM_SHFL_XOR(t_, r2, 1u); r7 = r7 ^ t_; } // 34 shfl
r7 = r7 * r0; // 35 mul
r5 = r5 ^ ds[r7 & mask]; // 36 load
r3 = rotr_var(r3, r2); // 37 rotr
r6 = rotl_imm(r6, 1u); // 38 rotl
r3 = r3 * r0; // 39 mul
r3 = mul_hi(r3, r7); // 40 mulhi
r5 = r5 ^ r2; // 41 xor
r4 = r4 * r0; // 42 mul
r3 = r3 + r0 + ((((sel >> 4u) & 1u) != 0u) ? 0xfaaf2d1eu : 0x831bfab3u); // 43 add
r0 = r1 * r6 + r0; // 44 mad
r6 = rotl_imm(r6, 26u); // 45 rotl
r2 = r2 ^ r1; // 46 xor
{ uint t_; IGNEUM_SHFL_XOR(t_, r7, 2u); r0 = r0 ^ t_; } // 47 shfl
r1 = r1 ^ r2; // 48 xor
r3 = r3 + r5 + ((((sel >> 7u) & 1u) != 0u) ? 0xc5759120u : 0x31df1a86u); // 49 add
r1 = r4 * r1 + r1; // 50 mad
{ uint t_; IGNEUM_SHFL_XOR(t_, r4, 2u); r1 = r1 ^ t_; } // 51 shfl
r5 = r5 + r4 + ((((sel >> 16u) & 1u) != 0u) ? 0x4e4a2759u : 0x553b85d1u); // 52 add
r1 = r1 ^ ds[r3 & mask]; // 53 load
r6 = rotl_imm(r6, 21u); // 54 rotl
r1 = mul_hi(r1, r4); // 55 mulhi
{ uint t_; IGNEUM_SHFL_XOR(t_, r3, 1u); r0 = r0 ^ t_; } // 56 shfl
r1 = r1 ^ r2; // 57 xor
{ uint t_; IGNEUM_SHFL_XOR(t_, r6, 16u); r3 = r3 ^ t_; } // 58 shfl
r5 = r5 * r0; // 59 mul
r4 = r4 ^ ds[r2 & mask]; // 60 load
r0 = rotr_var(r0, r4); // 61 rotr
r6 = r6 * r7; // 62 mul
r6 = rotl_imm(r6, 2u); // 63 rotl
}
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;
}

View file

@ -0,0 +1,123 @@
// Generated by igneum-pow export (generator v2) for seed "igneum-readwidth/A/2". Do not edit by hand.
// Header-bound twin of igneum_hash in kernel.cu: the init words come from a kernel argument, not SEEDW.
// Host declarations (also in program_bound.h if present):
// struct IgneumInitWords { uint32_t w[8]; };
// cudaError_t igneum_launch_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask,
// IgneumInitWords iw, uint32_t nonces, uint32_t blockWarps);
// cudaError_t igneum_hash_bound_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps);
#include <cuda_runtime.h>
#include <cstdint>
#include "program.h"
struct IgneumInitWords { uint32_t w[8]; };
__device__ __forceinline__ uint32_t splitmix32(uint32_t x) {
x ^= x >> 16; x *= 0x7feb352du;
x ^= x >> 15; x *= 0x846ca68bu;
x ^= x >> 16;
return x;
}
__device__ __forceinline__ uint32_t rotl_imm(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); }
__device__ __forceinline__ uint32_t rotr_var(uint32_t x, uint32_t n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); }
__global__ void igneum_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, IgneumInitWords iw) {
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 ^ iw.w[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ iw.w[1]; }
{ uint32_t x = nonce ^ iw.w[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ iw.w[2]; }
{ uint32_t x = nonce ^ iw.w[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ iw.w[3]; }
{ uint32_t x = nonce ^ iw.w[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ iw.w[4]; }
{ uint32_t x = nonce ^ iw.w[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ iw.w[5]; }
{ uint32_t x = nonce ^ iw.w[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ iw.w[6]; }
{ uint32_t x = nonce ^ iw.w[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ iw.w[7]; }
{ uint32_t x = nonce ^ iw.w[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ iw.w[0]; }
for (uint32_t it = 0u; it < 8u; ++it) {
uint32_t sel = r0;
r2 = r2 + r7 + ((((sel >> 7u) & 1u) != 0u) ? 0xd8952471u : 0x17c8e8eeu); // 0 add
r0 = rotr_var(r0, r5); // 1 rotr
r5 = r5 + r0 + ((((sel >> 21u) & 1u) != 0u) ? 0x4325cc6au : 0x37d9560au); // 2 add
r7 = r7 ^ r5; // 3 xor
{ uint32_t b_ = (r0 & mask) & ~15u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint32_t x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r3 = x_; } // 4 load
{ uint32_t b_ = (r7 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r5 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r5 = x_; } // 5 load
r0 = __umulhi(r0, r5); // 6 mulhi
r4 = r4 * r7; // 7 mul
r0 = r5 * r6 + r0; // 8 mad
{ uint32_t b_ = (r2 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r6 = x_; } // 9 load
r0 = r0 ^ r6; // 10 xor
r5 = r5 + r3 + ((((sel >> 22u) & 1u) != 0u) ? 0x81619a4cu : 0xbc4eca12u); // 11 add
r4 = __umulhi(r4, r2); // 12 mulhi
r2 = r2 ^ ds[r0 & mask]; // 13 load
r0 = r0 + r2 + ((((sel >> 3u) & 1u) != 0u) ? 0xa557fd2bu : 0xcf9919eeu); // 14 add
r4 = r4 + r5 + ((((sel >> 7u) & 1u) != 0u) ? 0x33857f70u : 0x36ec1d20u); // 15 add
r7 = r7 + r5 + ((((sel >> 6u) & 1u) != 0u) ? 0x40495714u : 0x42f5db15u); // 16 add
r7 = r7 ^ ds[r5 & mask]; // 17 load
r3 = r3 ^ r6; // 18 xor
{ uint32_t b_ = (r2 & mask) & ~15u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint32_t x_ = r7 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r7 = x_; } // 19 load
{ uint32_t b_ = (r3 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r5 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r5 = x_; } // 20 load
r1 = r1 ^ ds[r0 & mask]; // 21 load
r1 = __umulhi(r1, r3); // 22 mulhi
r1 = rotr_var(r1, r5); // 23 rotr
r2 = __umulhi(r2, r0); // 24 mulhi
{ uint32_t b_ = (r4 & mask) & ~15u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint32_t x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r0 = x_; } // 25 load
{ uint32_t b_ = (r1 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r0 = x_; } // 26 load
r5 = r5 ^ __shfl_xor_sync(0xffffffffu, r2, 2); // 27 shfl
r5 = r5 + r4 + ((((sel >> 12u) & 1u) != 0u) ? 0x58a2eb85u : 0x22029cb9u); // 28 add
{ uint32_t b_ = (r5 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r2 = x_; } // 29 load
r6 = r6 * r5; // 30 mul
r6 = r6 ^ ds[r2 & mask]; // 31 load
r7 = __umulhi(r7, r5); // 32 mulhi
r0 = r0 ^ ds[r7 & mask]; // 33 load
r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r2, 1); // 34 shfl
r7 = r7 * r0; // 35 mul
r5 = r5 ^ ds[r7 & mask]; // 36 load
r3 = rotr_var(r3, r2); // 37 rotr
r6 = rotl_imm(r6, 1u); // 38 rotl
r3 = r3 * r0; // 39 mul
r3 = __umulhi(r3, r7); // 40 mulhi
r5 = r5 ^ r2; // 41 xor
r4 = r4 * r0; // 42 mul
r3 = r3 + r0 + ((((sel >> 4u) & 1u) != 0u) ? 0xfaaf2d1eu : 0x831bfab3u); // 43 add
r0 = r1 * r6 + r0; // 44 mad
r6 = rotl_imm(r6, 26u); // 45 rotl
r2 = r2 ^ r1; // 46 xor
r0 = r0 ^ __shfl_xor_sync(0xffffffffu, r7, 2); // 47 shfl
r1 = r1 ^ r2; // 48 xor
r3 = r3 + r5 + ((((sel >> 7u) & 1u) != 0u) ? 0xc5759120u : 0x31df1a86u); // 49 add
r1 = r4 * r1 + r1; // 50 mad
r1 = r1 ^ __shfl_xor_sync(0xffffffffu, r4, 2); // 51 shfl
r5 = r5 + r4 + ((((sel >> 16u) & 1u) != 0u) ? 0x4e4a2759u : 0x553b85d1u); // 52 add
r1 = r1 ^ ds[r3 & mask]; // 53 load
r6 = rotl_imm(r6, 21u); // 54 rotl
r1 = __umulhi(r1, r4); // 55 mulhi
r0 = r0 ^ __shfl_xor_sync(0xffffffffu, r3, 1); // 56 shfl
r1 = r1 ^ r2; // 57 xor
r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r6, 16); // 58 shfl
r5 = r5 * r0; // 59 mul
r4 = r4 ^ ds[r2 & mask]; // 60 load
r0 = rotr_var(r0, r4); // 61 rotr
r6 = r6 * r7; // 62 mul
r6 = rotl_imm(r6, 2u); // 63 rotl
}
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;
}
cudaError_t igneum_launch_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask,
IgneumInitWords iw, 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_bound<<<nonces / block, block>>>(ds, out, baseNonce, mask, iw);
return cudaGetLastError();
}
cudaError_t igneum_hash_bound_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps) {
cudaFuncAttributes attr;
cudaError_t e = cudaFuncGetAttributes(&attr, igneum_hash_bound);
if (e != cudaSuccess) return e;
*numRegs = attr.numRegs;
return cudaOccupancyMaxActiveBlocksPerMultiprocessor(blocksPerSM, igneum_hash_bound, (int)(32u * blockWarps), 0);
}

View file

@ -0,0 +1,108 @@
// Generated by igneum-pow export (generator v2) for seed "igneum-readwidth/A/2". Do not edit by hand.
// Memory-hard dataset core, the same text that the Mac's Metal kernels and CPU verifier were checked against.
// Included by kernel.cu (device), host.cu (host reference) and proto-opencl/host.c (C99 host reference).
// See proto-metal/MEMHARD.md for the construction. kernel.cl carries the same text in OpenCL C.
#pragma once
#ifdef __cplusplus
#include <cstdint>
#else
#include <stdint.h>
#endif
#if defined(__CUDACC__)
#define IGNEUM_HD __host__ __device__ __forceinline__
#elif defined(_MSC_VER) && !defined(__cplusplus)
#define IGNEUM_HD static __inline
#else
#define IGNEUM_HD static inline
#endif
// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines.
// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals.
#define MH_CACHE_LINE_MASK 0x003fffffu
#define MH_SEGMENT_LINES 64u
#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); }
IGNEUM_HD uint32_t mh_rotl(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site
// y = ChaCha12 core(x) + x
IGNEUM_HD void mh_chacha_block(const uint32_t* x, uint32_t* y) {
for (uint32_t i = 0u; i < 16u; ++i) y[i] = x[i];
for (uint32_t r = 0u; r < 6u; ++r) {
MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u)
MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u)
}
for (uint32_t i = 0u; i < 16u; ++i) y[i] += x[i];
}
// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0.
IGNEUM_HD void mh_cache_segment(uint32_t* cache, uint32_t seg) {
uint32_t prev[16]; uint32_t x[16]; uint32_t y[16];
for (uint32_t i = 0u; i < 16u; ++i) prev[i] = 0u;
for (uint32_t j = 0u; j < MH_SEGMENT_LINES; ++j) {
x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3];
x[4] = 0x3067619fu ^ prev[4];
x[5] = 0x3c269176u ^ prev[5];
x[6] = 0x84a03b03u ^ prev[6];
x[7] = 0xf8c63294u ^ prev[7];
x[8] = 0xff977c5bu ^ prev[8];
x[9] = 0xe60def3eu ^ prev[9];
x[10] = 0x63630141u ^ prev[10];
x[11] = 0xb8fbcb58u ^ prev[11];
x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15];
mh_chacha_block(x, y);
uint32_t* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u);
for (uint32_t i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; }
}
}
// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations.
IGNEUM_HD void mh_mixer(uint32_t* s, uint32_t rk) {
s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u;
s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu;
s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u;
s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu;
s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u;
s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u;
s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u;
s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u;
s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du;
s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u;
s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du;
s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu;
s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du;
s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu;
s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u;
s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u;
MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u)
MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u)
MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u)
MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u)
}
// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer.
IGNEUM_HD void mh_item(const uint32_t* cache, uint32_t t, uint32_t* s) {
s[0] = 0x3067619fu;
s[1] = 0x3c269176u;
s[2] = 0x84a03b03u;
s[3] = 0xf8c63294u;
s[4] = 0xff977c5bu;
s[5] = 0xe60def3eu;
s[6] = 0x63630141u;
s[7] = 0xb8fbcb58u;
s[8] = t * 0x42146205u + 0xbab68293u;
s[9] = t * 0x52cbe0fbu + 0xcc162340u;
s[10] = t * 0x7ecf4a03u + 0x6ce151ccu;
s[11] = t * 0x6728907fu + 0xe62b8997u;
s[12] = t * 0xd81d9751u + 0xc9c80297u;
s[13] = t * 0x132952c3u + 0xf74a1654u;
s[14] = t * 0xf60de277u + 0x3d704af5u;
s[15] = t * 0x05358035u + 0x3cf522b7u;
for (uint32_t r = 0u; r < 8u; ++r) {
mh_mixer(s, 0x9E3779B9u * (r + 1u));
const uint32_t* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u);
for (uint32_t i = 0u; i < 16u; ++i) s[i] ^= line[i];
}
mh_mixer(s, 0x9E3779B9u * 9u);
}
// dataset[w] without the dataset: derive item w >> 4 and take word w & 15.
IGNEUM_HD uint32_t mh_word(const uint32_t* cache, uint32_t w) { uint32_t s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; }

View file

@ -0,0 +1,106 @@
#include <metal_stdlib>
using namespace metal;
// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines.
// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals.
#define MH_CACHE_LINE_MASK 0x003fffffu
#define MH_SEGMENT_LINES 64u
#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); }
inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site
// y = ChaCha12 core(x) + x
inline void mh_chacha_block(const thread uint* x, thread uint* y) {
for (uint i = 0u; i < 16u; ++i) y[i] = x[i];
for (uint r = 0u; r < 6u; ++r) {
MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u)
MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u)
}
for (uint i = 0u; i < 16u; ++i) y[i] += x[i];
}
// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0.
inline void mh_cache_segment(device uint* cache, uint seg) {
uint prev[16]; uint x[16]; uint y[16];
for (uint i = 0u; i < 16u; ++i) prev[i] = 0u;
for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) {
x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3];
x[4] = 0x3067619fu ^ prev[4];
x[5] = 0x3c269176u ^ prev[5];
x[6] = 0x84a03b03u ^ prev[6];
x[7] = 0xf8c63294u ^ prev[7];
x[8] = 0xff977c5bu ^ prev[8];
x[9] = 0xe60def3eu ^ prev[9];
x[10] = 0x63630141u ^ prev[10];
x[11] = 0xb8fbcb58u ^ prev[11];
x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15];
mh_chacha_block(x, y);
device uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u);
for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; }
}
}
// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations.
inline void mh_mixer(thread uint* s, uint rk) {
s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u;
s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu;
s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u;
s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu;
s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u;
s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u;
s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u;
s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u;
s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du;
s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u;
s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du;
s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu;
s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du;
s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu;
s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u;
s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u;
MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u)
MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u)
MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u)
MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u)
}
// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer.
inline void mh_item(device const uint* cache, uint t, thread uint* s) {
s[0] = 0x3067619fu;
s[1] = 0x3c269176u;
s[2] = 0x84a03b03u;
s[3] = 0xf8c63294u;
s[4] = 0xff977c5bu;
s[5] = 0xe60def3eu;
s[6] = 0x63630141u;
s[7] = 0xb8fbcb58u;
s[8] = t * 0x42146205u + 0xbab68293u;
s[9] = t * 0x52cbe0fbu + 0xcc162340u;
s[10] = t * 0x7ecf4a03u + 0x6ce151ccu;
s[11] = t * 0x6728907fu + 0xe62b8997u;
s[12] = t * 0xd81d9751u + 0xc9c80297u;
s[13] = t * 0x132952c3u + 0xf74a1654u;
s[14] = t * 0xf60de277u + 0x3d704af5u;
s[15] = t * 0x05358035u + 0x3cf522b7u;
for (uint r = 0u; r < 8u; ++r) {
mh_mixer(s, 0x9E3779B9u * (r + 1u));
device const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u);
for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i];
}
mh_mixer(s, 0x9E3779B9u * 9u);
}
// dataset[w] without the dataset: derive item w >> 4 and take word w & 15.
inline uint mh_word(device const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; }
// One thread per segment (2^16 threads).
kernel void igneum_cache_fill(device uint* cache [[buffer(0)]], uint gid [[thread_position_in_grid]]) {
mh_cache_segment(cache, gid);
}
// One thread per 64-byte item (dataset words / 16 threads).
kernel void igneum_build(device const uint* cache [[buffer(0)]], device uint* dataset [[buffer(1)]],
uint gid [[thread_position_in_grid]]) {
uint s[16];
mh_item(cache, gid, s);
device uint* d = dataset + gid * 16u;
for (uint i = 0u; i < 16u; ++i) d[i] = s[i];
}

View file

@ -0,0 +1,60 @@
// Generated by igneum-pow export (generator v2) for seed "igneum-readwidth/A/2". Do not edit by hand.
// Program metadata for host.cu plus the launch wrappers defined in kernel.cu.
// Also included by proto-opencl/host.c (C99), which defines IGNEUM_NO_CUDA first and reads only the macros.
#pragma once
#ifdef __cplusplus
#include <cstdint>
#else
#include <stdint.h>
#endif
#ifndef IGNEUM_NO_CUDA
#include <cuda_runtime.h>
#endif
#define IGNEUM_SEED_STRING "igneum-readwidth/A/2"
#define IGNEUM_SEED_BYTES_HEX "69676e65756d2d7265616477696474682f412f32"
#define IGNEUM_GENERATOR 2
#define IGNEUM_PROGRAM_ATTEMPT 0
#define IGNEUM_PROGRAM_ID 0x11d47bbfc868120eull
#define IGNEUM_DAY_STRING "2026-10-03"
#define IGNEUM_DAY_BYTES_HEX "6461792f323032362d31302d3033"
#define IGNEUM_DAY0 0x3067619fu
#define IGNEUM_DAY1 0x3c269176u
#define IGNEUM_DATASET_LOG2 28
#define IGNEUM_MASK 0x0fffffffu
#define IGNEUM_LANES 32
#define IGNEUM_ITERATIONS 8
#define IGNEUM_INSTR_COUNT 64
#define IGNEUM_LOADS_PER_HASH 128
#define IGNEUM_WIDE_LOADS_PER_HASH 0
#define IGNEUM_OP_MIX "load=16 add=10 mul=7 mulhi=7 xor=7 shfl=6 rotl=4 rotr=4 mad=3"
// Read-width experiment (5 October 2026, docs/plans/read-width.md): NOT the lottery hash. A load of W words reads
// the W-word-aligned address and folds every word into dst: x = dst ^ w[0]; x = (rotl(x, 11) * 0x9e3779b1) ^ w[j]; dst = x.
#define IGNEUM_LOAD_CLASS "mix50-35-15"
#define IGNEUM_LOAD_SLOTS 16
#define IGNEUM_LOAD_MIX { 50, 35, 15 }
#define IGNEUM_LOAD_WIDTH_COUNTS { 8, 5, 3 } // loads of 4, 16, 64 bytes per program
#define IGNEUM_BYTES_PER_HASH 2432
#define IGNEUM_FOLD_ROT 11
#define IGNEUM_FOLD_MUL 0x9e3779b1u
// 0 = closed-form dataset (ds_elem), 1 = memory-hard cache construction (MEMHARD.md, memhard.h)
#define IGNEUM_DATASET_MODE 1
#define IGNEUM_SEEDW_INIT { 0x774fd410u, 0x520f91b2u, 0x9357786fu, 0x8bbe44d7u, 0x23db18feu, 0x52022aecu, 0x653ea608u, 0x57788e47u }
#define IGNEUM_KEY_INIT { 0x3067619fu, 0x3c269176u, 0x84a03b03u, 0xf8c63294u, 0xff977c5bu, 0xe60def3eu, 0x63630141u, 0xb8fbcb58u }
#define IGNEUM_CACHE_LOG2_WORDS 26
#define IGNEUM_CACHE_SEGMENT_LOG2_LINES 6
#define IGNEUM_CACHE_SEGMENTS 65536u
#define IGNEUM_ITEM_ROUNDS 8
#define IGNEUM_MIX_ROT_INIT { 20u, 20u, 19u, 4u, 26u, 3u, 3u, 27u }
#define IGNEUM_MIX_MUL_INIT { 0x42146205u, 0x52cbe0fbu, 0x7ecf4a03u, 0x6728907fu, 0xd81d9751u, 0x132952c3u, 0xf60de277u, 0x05358035u, 0xbaf6499du, 0xe4db9667u, 0x3e98f45du, 0xd0004eddu, 0x2691630du, 0x9beb3bcfu, 0xab310379u, 0x99cfb423u }
#define IGNEUM_MIX_RC_INIT { 0xbab68293u, 0xcc162340u, 0x6ce151ccu, 0xe62b8997u, 0xc9c80297u, 0xf74a1654u, 0x3d704af5u, 0x3cf522b7u, 0x2b9cac04u, 0xa880ac10u, 0x13e5dd1du, 0x6fc3e233u, 0x2d83eeacu, 0x9006e8bfu, 0x2c4b5362u, 0x31b49ee2u }
#ifndef IGNEUM_NO_CUDA
// Defined in kernel.cu. All launch on the default stream and return cudaGetLastError().
cudaError_t igneum_launch_cache_fill(uint32_t* cache, uint32_t nSegments);
cudaError_t igneum_launch_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems);
cudaError_t igneum_launch_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask,
uint32_t nonces, uint32_t blockWarps);
cudaError_t igneum_hash_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps);
#endif

View file

@ -0,0 +1,127 @@
{
"format": "igneum-program-pack-3",
"generator": 2,
"attempt": 0,
"program_id": "0x11d47bbfc868120e",
"program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32",
"dataset_mode": "memory-hard",
"seed": "igneum-readwidth/A/2",
"seed_bytes": "69676e65756d2d7265616477696474682f412f32",
"seed_words": ["0x774fd410", "0x520f91b2", "0x9357786f", "0x8bbe44d7", "0x23db18fe", "0x52022aec", "0x653ea608", "0x57788e47"],
"seed_derivation": "seed_words = FNV-1a 64 over seed_bytes (attempt 0) or seed_bytes || attempt_le32 (attempt k >= 1), basis ^ (salt * 0x9E3779B97F4A7C15) for salt 0..3, then h ^= h>>33; h *= 0xff51afd7ed558ccd; h ^= h>>33; words[2*salt] = low 32, words[2*salt+1] = high 32",
"generator_rule": "version 2: exactly 16 load slots drawn first from instructions 1..63 (partial Fisher-Yates), the other 48 ops from the ten non-load weights (sum 75); a load's source is drawn from the registers other than dst written by an earlier instruction and not read by a load since; the candidate must pass the acceptance rule of spec 01 section 1.4.6 (static: no cyclically stale load source, every register has an injecting write; dynamic: 64 units on the seed-keyed closed-form dataset with no constant register bit, no lane-constant load site, under 164 saturated final values, every output bit within 136 of 1024, distinct addresses above 245760), else the next attempt of the seed is tried",
"lanes": 32,
"registers": 8,
"iterations": 8,
"instruction_count": 64,
"loads_per_hash": 128,
"load_class": "mix50-35-15",
"load_slots": 16,
"load_mix_percent_4_16_64": [50, 35, 15],
"load_width_counts_4_16_64": [8, 5, 3],
"bytes_per_hash": 2432,
"wide_load": "read-width experiment (5 October 2026, docs/plans/read-width.md), NOT the lottery hash: a load of W words (width field, 4 or 16) reads dataset[b .. b + W) with b = (src & mask) & ~(W - 1) and folds every word into dst: x = dst ^ w[0]; for j in 1..W: x = (rotl(x, 11) * 0x9e3779b1) ^ w[j]; dst = x; width 1 is the plain load; the width is drawn per instruction from the class mix with one extra below(100) draw after the nine of version 2, and the program id is FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed words || attempt_le32 || mix[3] || load_slots",
"op_mix": {"load": 16, "add": 10, "mul": 7, "mulhi": 7, "xor": 7, "shfl": 6, "rotl": 4, "rotr": 4, "mad": 3},
"register_init": "for i in 0..7: x = nonce ^ seed_words[i]; x += 0x9e3779b9 * (i+1) (mod 2^32); x = splitmix32(x); r[i] = x ^ seed_words[(i+1) & 7]",
"splitmix32": "x ^= x>>16; x *= 0x7feb352d; x ^= x>>15; x *= 0x846ca68b; x ^= x>>16",
"iteration": "sel = r0 sampled once at the top of each iteration, then all instructions in order",
"output": "lo = r0 ^ rotl(r1,7) ^ rotl(r2,14) ^ rotl(r3,21); hi = r4 ^ rotl(r5,9) ^ rotl(r6,18) ^ rotl(r7,27); out = (hi << 32) | lo",
"op_semantics": {
"add": "dst = dst + src + (bit `bit` of sel ? imm2 : imm)",
"sub": "dst = dst - src",
"mul": "dst = dst * src (low 32)",
"mulhi": "dst = high 32 bits of dst * src",
"xor": "dst = dst ^ src",
"or": "dst = dst | src",
"rotl": "dst = rotl(dst, rot), rot in 1..31",
"rotr": "dst = rotr(dst, src & 31)",
"mad": "dst = src * src2 + dst",
"shfl": "dst = dst ^ (src of lane (lane ^ mask)), mask in {1,2,4,8,16}, within the 32-lane warp",
"load": "dst = dst ^ dataset[src & dataset.mask]",
"wload": "base = (src of lane 0 & dataset.mask) & ~31; dst = dst ^ dataset[base + lane] (warp-coalesced 128-byte load, lever b, only when --wide-frac > 0)"
},
"dataset": {
"log2_words": 28,
"bytes": 1073741824,
"mask": "0x0fffffff",
"day": "2026-10-03",
"day_bytes": "6461792f323032362d31302d3033",
"day_words_from": "seed_words_from_bytes(day_bytes)",
"d0": "0x3067619f",
"d1": "0x3c269176",
"mode": "memory-hard",
"spec": "proto-metal/MEMHARD.md",
"key": ["0x3067619f", "0x3c269176", "0x84a03b03", "0xf8c63294", "0xff977c5b", "0xe60def3e", "0x63630141", "0xb8fbcb58"],
"key_derivation": "the 8 words of seed_words_from_bytes(day_bytes); d0, d1 are key[0], key[1]",
"cache": {"log2_words": 26, "bytes": 268435456, "line_words": 16, "segment_lines": 64, "segments": 65536, "block": "ChaCha12 core + feed-forward, rotations 16 12 8 7", "sigma": ["0x61707865", "0x3320646e", "0x79622d32", "0x6b206574"], "tag": ["0x49676e65", "0x756d4d48"], "chain": "in_j = prev_line ^ (sigma[0..3] || key[0..7] || seg || j || tag[0..1]); line_j = block(in_j); prev_0 = 0"},
"mixer": {"draw": "SplitMix64 seeded with key[0] | key[1] << 32: rot[0..7] = 1 + next() % 31, mul[0..15] = low32(next()) | 1, rc[0..15] = low32(next())", "rot": [20, 20, 19, 4, 26, 3, 3, 27], "mul": ["0x42146205", "0x52cbe0fb", "0x7ecf4a03", "0x6728907f", "0xd81d9751", "0x132952c3", "0xf60de277", "0x05358035", "0xbaf6499d", "0xe4db9667", "0x3e98f45d", "0xd0004edd", "0x2691630d", "0x9beb3bcf", "0xab310379", "0x99cfb423"], "rc": ["0xbab68293", "0xcc162340", "0x6ce151cc", "0xe62b8997", "0xc9c80297", "0xf74a1654", "0x3d704af5", "0x3cf522b7", "0x2b9cac04", "0xa880ac10", "0x13e5dd1d", "0x6fc3e233", "0x2d83eeac", "0x9006e8bf", "0x2c4b5362", "0x31b49ee2"], "round": "for i in 0..15: s[i] = (s[i] ^ (rc[i] + (r+1) * 0x9E3779B9)) * mul[i]; then quarter rounds on columns (0,4,8,12) (1,5,9,13) (2,6,10,14) (3,7,11,15) with rot[0..3] and diagonals (0,5,10,15) (1,6,11,12) (2,7,8,13) (3,4,9,14) with rot[4..7]", "quarter_round": "a += b; d ^= a; d = rotl(d, r1); c += d; b ^= c; b = rotl(b, r2); a += b; d ^= a; d = rotl(d, r3); c += d; b ^= c; b = rotl(b, r4)"},
"item": "s[0..7] = key; s[8+i] = t * mul[i] + rc[i] for i in 0..7; for r in 0..7: s = M_r(s); line = s[0] & 0x003fffff; s[i] ^= cache[line * 16 + i]; then s = M_8(s); item(t) = s",
"word": "dataset[w] = item(w >> 4)[w & 15]"
},
"instructions": [
{"i": 0, "op": "add", "dst": 2, "src": 7, "src2": 4, "imm": "0x17c8e8ee", "imm2": "0xd8952471", "rot": 31, "bit": 7, "mask": 4, "width": 1},
{"i": 1, "op": "rotr", "dst": 0, "src": 5, "src2": 2, "imm": "0xcd7dcc5c", "imm2": "0xa1188ae7", "rot": 15, "bit": 23, "mask": 16, "width": 1},
{"i": 2, "op": "add", "dst": 5, "src": 0, "src2": 1, "imm": "0x37d9560a", "imm2": "0x4325cc6a", "rot": 4, "bit": 21, "mask": 2, "width": 1},
{"i": 3, "op": "xor", "dst": 7, "src": 5, "src2": 3, "imm": "0x6e598036", "imm2": "0x3370fa87", "rot": 22, "bit": 10, "mask": 4, "width": 1},
{"i": 4, "op": "load", "dst": 3, "src": 0, "src2": 4, "imm": "0x072213d2", "imm2": "0xa024aa08", "rot": 9, "bit": 1, "mask": 16, "width": 16},
{"i": 5, "op": "load", "dst": 5, "src": 7, "src2": 4, "imm": "0x3f74f8d4", "imm2": "0x29345708", "rot": 6, "bit": 9, "mask": 8, "width": 4},
{"i": 6, "op": "mulhi", "dst": 0, "src": 5, "src2": 2, "imm": "0x0b8090fa", "imm2": "0x6677fdf7", "rot": 15, "bit": 24, "mask": 8, "width": 1},
{"i": 7, "op": "mul", "dst": 4, "src": 7, "src2": 4, "imm": "0xf40f7deb", "imm2": "0x93c66180", "rot": 23, "bit": 1, "mask": 1, "width": 1},
{"i": 8, "op": "mad", "dst": 0, "src": 5, "src2": 6, "imm": "0xfdce2834", "imm2": "0x61107ebf", "rot": 1, "bit": 10, "mask": 8, "width": 1},
{"i": 9, "op": "load", "dst": 6, "src": 2, "src2": 6, "imm": "0x99c257d5", "imm2": "0x7b8a8224", "rot": 10, "bit": 29, "mask": 2, "width": 4},
{"i": 10, "op": "xor", "dst": 0, "src": 6, "src2": 6, "imm": "0x1d598c2f", "imm2": "0x989541c3", "rot": 5, "bit": 7, "mask": 4, "width": 1},
{"i": 11, "op": "add", "dst": 5, "src": 3, "src2": 3, "imm": "0xbc4eca12", "imm2": "0x81619a4c", "rot": 9, "bit": 22, "mask": 16, "width": 1},
{"i": 12, "op": "mulhi", "dst": 4, "src": 2, "src2": 0, "imm": "0xdd65e362", "imm2": "0x3928839a", "rot": 14, "bit": 28, "mask": 4, "width": 1},
{"i": 13, "op": "load", "dst": 2, "src": 0, "src2": 6, "imm": "0x059fcec1", "imm2": "0x1e541b07", "rot": 4, "bit": 21, "mask": 1, "width": 1},
{"i": 14, "op": "add", "dst": 0, "src": 2, "src2": 6, "imm": "0xcf9919ee", "imm2": "0xa557fd2b", "rot": 6, "bit": 3, "mask": 1, "width": 1},
{"i": 15, "op": "add", "dst": 4, "src": 5, "src2": 0, "imm": "0x36ec1d20", "imm2": "0x33857f70", "rot": 21, "bit": 7, "mask": 8, "width": 1},
{"i": 16, "op": "add", "dst": 7, "src": 5, "src2": 1, "imm": "0x42f5db15", "imm2": "0x40495714", "rot": 18, "bit": 6, "mask": 8, "width": 1},
{"i": 17, "op": "load", "dst": 7, "src": 5, "src2": 1, "imm": "0xf649fe2d", "imm2": "0x4477242b", "rot": 27, "bit": 3, "mask": 8, "width": 1},
{"i": 18, "op": "xor", "dst": 3, "src": 6, "src2": 7, "imm": "0xae781ff8", "imm2": "0xbe6ecc6d", "rot": 30, "bit": 18, "mask": 1, "width": 1},
{"i": 19, "op": "load", "dst": 7, "src": 2, "src2": 4, "imm": "0x68e68ea0", "imm2": "0x4dc840da", "rot": 19, "bit": 5, "mask": 1, "width": 16},
{"i": 20, "op": "load", "dst": 5, "src": 3, "src2": 3, "imm": "0x59d3176d", "imm2": "0xdb77debd", "rot": 28, "bit": 5, "mask": 16, "width": 4},
{"i": 21, "op": "load", "dst": 1, "src": 0, "src2": 5, "imm": "0xbdc0a385", "imm2": "0x464f9bc3", "rot": 17, "bit": 12, "mask": 8, "width": 1},
{"i": 22, "op": "mulhi", "dst": 1, "src": 3, "src2": 2, "imm": "0xa654255b", "imm2": "0xea4103de", "rot": 27, "bit": 31, "mask": 2, "width": 1},
{"i": 23, "op": "rotr", "dst": 1, "src": 5, "src2": 7, "imm": "0xa3991a21", "imm2": "0x4a407fc9", "rot": 22, "bit": 9, "mask": 16, "width": 1},
{"i": 24, "op": "mulhi", "dst": 2, "src": 0, "src2": 7, "imm": "0x06fa7a88", "imm2": "0x06f12fe9", "rot": 3, "bit": 30, "mask": 16, "width": 1},
{"i": 25, "op": "load", "dst": 0, "src": 4, "src2": 5, "imm": "0xf9075dec", "imm2": "0xe902c837", "rot": 16, "bit": 22, "mask": 2, "width": 16},
{"i": 26, "op": "load", "dst": 0, "src": 1, "src2": 2, "imm": "0xd363edf4", "imm2": "0x8445583d", "rot": 10, "bit": 21, "mask": 1, "width": 4},
{"i": 27, "op": "shfl", "dst": 5, "src": 2, "src2": 7, "imm": "0x8327fa2f", "imm2": "0x3a6092b0", "rot": 1, "bit": 23, "mask": 2, "width": 1},
{"i": 28, "op": "add", "dst": 5, "src": 4, "src2": 3, "imm": "0x22029cb9", "imm2": "0x58a2eb85", "rot": 20, "bit": 12, "mask": 8, "width": 1},
{"i": 29, "op": "load", "dst": 2, "src": 5, "src2": 0, "imm": "0x508d6c6e", "imm2": "0x51e70669", "rot": 23, "bit": 5, "mask": 4, "width": 4},
{"i": 30, "op": "mul", "dst": 6, "src": 5, "src2": 0, "imm": "0xbbd10d76", "imm2": "0xcc8e1885", "rot": 29, "bit": 31, "mask": 1, "width": 1},
{"i": 31, "op": "load", "dst": 6, "src": 2, "src2": 7, "imm": "0x032a9acc", "imm2": "0x0e202d4e", "rot": 9, "bit": 31, "mask": 16, "width": 1},
{"i": 32, "op": "mulhi", "dst": 7, "src": 5, "src2": 7, "imm": "0x050c9aca", "imm2": "0x68f23dcc", "rot": 8, "bit": 8, "mask": 8, "width": 1},
{"i": 33, "op": "load", "dst": 0, "src": 7, "src2": 6, "imm": "0x170bc6d7", "imm2": "0x8a08c2e4", "rot": 2, "bit": 15, "mask": 1, "width": 1},
{"i": 34, "op": "shfl", "dst": 7, "src": 2, "src2": 6, "imm": "0x691b40d0", "imm2": "0x44b77bc4", "rot": 11, "bit": 16, "mask": 1, "width": 1},
{"i": 35, "op": "mul", "dst": 7, "src": 0, "src2": 0, "imm": "0x708adb53", "imm2": "0x8101af24", "rot": 12, "bit": 19, "mask": 1, "width": 1},
{"i": 36, "op": "load", "dst": 5, "src": 7, "src2": 3, "imm": "0x0c731099", "imm2": "0x8f29d683", "rot": 3, "bit": 9, "mask": 4, "width": 1},
{"i": 37, "op": "rotr", "dst": 3, "src": 2, "src2": 0, "imm": "0x08ea65a2", "imm2": "0xc22ef0f2", "rot": 26, "bit": 7, "mask": 8, "width": 1},
{"i": 38, "op": "rotl", "dst": 6, "src": 7, "src2": 6, "imm": "0xa5e2e082", "imm2": "0x3e9f9d96", "rot": 1, "bit": 22, "mask": 16, "width": 1},
{"i": 39, "op": "mul", "dst": 3, "src": 0, "src2": 0, "imm": "0xb08ca537", "imm2": "0x98bd2f28", "rot": 15, "bit": 8, "mask": 2, "width": 1},
{"i": 40, "op": "mulhi", "dst": 3, "src": 7, "src2": 0, "imm": "0x6b4f575a", "imm2": "0x0de8bd13", "rot": 14, "bit": 27, "mask": 2, "width": 1},
{"i": 41, "op": "xor", "dst": 5, "src": 2, "src2": 1, "imm": "0xb651de8e", "imm2": "0x74c00b0c", "rot": 24, "bit": 21, "mask": 16, "width": 1},
{"i": 42, "op": "mul", "dst": 4, "src": 0, "src2": 6, "imm": "0xd604a95a", "imm2": "0x93ad566c", "rot": 20, "bit": 16, "mask": 8, "width": 1},
{"i": 43, "op": "add", "dst": 3, "src": 0, "src2": 1, "imm": "0x831bfab3", "imm2": "0xfaaf2d1e", "rot": 2, "bit": 4, "mask": 16, "width": 1},
{"i": 44, "op": "mad", "dst": 0, "src": 1, "src2": 6, "imm": "0x40201b32", "imm2": "0xde9bd02f", "rot": 8, "bit": 6, "mask": 4, "width": 1},
{"i": 45, "op": "rotl", "dst": 6, "src": 1, "src2": 5, "imm": "0x4bfbff1c", "imm2": "0x02d87bfc", "rot": 26, "bit": 8, "mask": 4, "width": 1},
{"i": 46, "op": "xor", "dst": 2, "src": 1, "src2": 2, "imm": "0xc069583c", "imm2": "0x88265351", "rot": 26, "bit": 15, "mask": 2, "width": 1},
{"i": 47, "op": "shfl", "dst": 0, "src": 7, "src2": 7, "imm": "0xffe42b00", "imm2": "0x8c0acfed", "rot": 21, "bit": 7, "mask": 2, "width": 1},
{"i": 48, "op": "xor", "dst": 1, "src": 2, "src2": 1, "imm": "0x2f594ed1", "imm2": "0x7d51b0fb", "rot": 12, "bit": 19, "mask": 4, "width": 1},
{"i": 49, "op": "add", "dst": 3, "src": 5, "src2": 4, "imm": "0x31df1a86", "imm2": "0xc5759120", "rot": 23, "bit": 7, "mask": 1, "width": 1},
{"i": 50, "op": "mad", "dst": 1, "src": 4, "src2": 1, "imm": "0x1a81eb4c", "imm2": "0xb0108e0b", "rot": 27, "bit": 7, "mask": 1, "width": 1},
{"i": 51, "op": "shfl", "dst": 1, "src": 4, "src2": 7, "imm": "0x1a207d4c", "imm2": "0x44993b80", "rot": 27, "bit": 27, "mask": 2, "width": 1},
{"i": 52, "op": "add", "dst": 5, "src": 4, "src2": 4, "imm": "0x553b85d1", "imm2": "0x4e4a2759", "rot": 8, "bit": 16, "mask": 16, "width": 1},
{"i": 53, "op": "load", "dst": 1, "src": 3, "src2": 4, "imm": "0x0c0b1b74", "imm2": "0x39083c8c", "rot": 4, "bit": 11, "mask": 1, "width": 1},
{"i": 54, "op": "rotl", "dst": 6, "src": 4, "src2": 0, "imm": "0x1da26beb", "imm2": "0x0ddad11c", "rot": 21, "bit": 27, "mask": 2, "width": 1},
{"i": 55, "op": "mulhi", "dst": 1, "src": 4, "src2": 7, "imm": "0x1fe9ea90", "imm2": "0x9457576e", "rot": 2, "bit": 2, "mask": 1, "width": 1},
{"i": 56, "op": "shfl", "dst": 0, "src": 3, "src2": 4, "imm": "0x15776f9f", "imm2": "0xe087579f", "rot": 14, "bit": 11, "mask": 1, "width": 1},
{"i": 57, "op": "xor", "dst": 1, "src": 2, "src2": 5, "imm": "0x1c5bb8e2", "imm2": "0x977e10b3", "rot": 18, "bit": 27, "mask": 16, "width": 1},
{"i": 58, "op": "shfl", "dst": 3, "src": 6, "src2": 3, "imm": "0x37ecda35", "imm2": "0x21944e21", "rot": 3, "bit": 12, "mask": 16, "width": 1},
{"i": 59, "op": "mul", "dst": 5, "src": 0, "src2": 6, "imm": "0xf3685385", "imm2": "0x86144005", "rot": 21, "bit": 30, "mask": 4, "width": 1},
{"i": 60, "op": "load", "dst": 4, "src": 2, "src2": 0, "imm": "0xfdcc24f2", "imm2": "0x9bc020dc", "rot": 7, "bit": 18, "mask": 2, "width": 1},
{"i": 61, "op": "rotr", "dst": 0, "src": 4, "src2": 2, "imm": "0x9a85b0f3", "imm2": "0xc04760bd", "rot": 26, "bit": 4, "mask": 16, "width": 1},
{"i": 62, "op": "mul", "dst": 6, "src": 7, "src2": 7, "imm": "0x261783fc", "imm2": "0x7e75e2f9", "rot": 9, "bit": 0, "mask": 16, "width": 1},
{"i": 63, "op": "rotl", "dst": 6, "src": 0, "src2": 4, "imm": "0x3569c3f0", "imm2": "0xbe2e19c5", "rot": 2, "bit": 18, "mask": 8, "width": 1}
]
}

View file

@ -0,0 +1,109 @@
#include <metal_stdlib>
using namespace metal;
#define MASK 0x0fffffffu
constant uint SEEDW[8] = { 0x774fd410u, 0x520f91b2u, 0x9357786fu, 0x8bbe44d7u, 0x23db18feu, 0x52022aecu, 0x653ea608u, 0x57788e47u };
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;
}
kernel void igneum_hash(device const uint* dataset [[buffer(0)]],
device ulong* out [[buffer(1)]],
constant uint& baseNonce [[buffer(2)]],
uint gid [[thread_position_in_grid]]) {
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 = r2 + r7 + select(0x17c8e8eeu, 0xd8952471u, ((sel >> 7u) & 1u) != 0u); // 0
r0 = rotr_var(r0, r5); // 1
r5 = r5 + r0 + select(0x37d9560au, 0x4325cc6au, ((sel >> 21u) & 1u) != 0u); // 2
r7 = r7 ^ r5; // 3
{ uint b_ = (r0 & MASK) & ~15u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r3 = x_; } // 4
{ uint b_ = (r7 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r5 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r5 = x_; } // 5
r0 = mulhi(r0, r5); // 6
r4 = r4 * r7; // 7
r0 = r5 * r6 + r0; // 8
{ uint b_ = (r2 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r6 = x_; } // 9
r0 = r0 ^ r6; // 10
r5 = r5 + r3 + select(0xbc4eca12u, 0x81619a4cu, ((sel >> 22u) & 1u) != 0u); // 11
r4 = mulhi(r4, r2); // 12
r2 = r2 ^ dataset[r0 & MASK]; // 13
r0 = r0 + r2 + select(0xcf9919eeu, 0xa557fd2bu, ((sel >> 3u) & 1u) != 0u); // 14
r4 = r4 + r5 + select(0x36ec1d20u, 0x33857f70u, ((sel >> 7u) & 1u) != 0u); // 15
r7 = r7 + r5 + select(0x42f5db15u, 0x40495714u, ((sel >> 6u) & 1u) != 0u); // 16
r7 = r7 ^ dataset[r5 & MASK]; // 17
r3 = r3 ^ r6; // 18
{ uint b_ = (r2 & MASK) & ~15u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint x_ = r7 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r7 = x_; } // 19
{ uint b_ = (r3 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r5 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r5 = x_; } // 20
r1 = r1 ^ dataset[r0 & MASK]; // 21
r1 = mulhi(r1, r3); // 22
r1 = rotr_var(r1, r5); // 23
r2 = mulhi(r2, r0); // 24
{ uint b_ = (r4 & MASK) & ~15u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r0 = x_; } // 25
{ uint b_ = (r1 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r0 = x_; } // 26
r5 = r5 ^ simd_shuffle_xor(r2, (ushort)2); // 27
r5 = r5 + r4 + select(0x22029cb9u, 0x58a2eb85u, ((sel >> 12u) & 1u) != 0u); // 28
{ uint b_ = (r5 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r2 = x_; } // 29
r6 = r6 * r5; // 30
r6 = r6 ^ dataset[r2 & MASK]; // 31
r7 = mulhi(r7, r5); // 32
r0 = r0 ^ dataset[r7 & MASK]; // 33
r7 = r7 ^ simd_shuffle_xor(r2, (ushort)1); // 34
r7 = r7 * r0; // 35
r5 = r5 ^ dataset[r7 & MASK]; // 36
r3 = rotr_var(r3, r2); // 37
r6 = rotl_imm(r6, 1u); // 38
r3 = r3 * r0; // 39
r3 = mulhi(r3, r7); // 40
r5 = r5 ^ r2; // 41
r4 = r4 * r0; // 42
r3 = r3 + r0 + select(0x831bfab3u, 0xfaaf2d1eu, ((sel >> 4u) & 1u) != 0u); // 43
r0 = r1 * r6 + r0; // 44
r6 = rotl_imm(r6, 26u); // 45
r2 = r2 ^ r1; // 46
r0 = r0 ^ simd_shuffle_xor(r7, (ushort)2); // 47
r1 = r1 ^ r2; // 48
r3 = r3 + r5 + select(0x31df1a86u, 0xc5759120u, ((sel >> 7u) & 1u) != 0u); // 49
r1 = r4 * r1 + r1; // 50
r1 = r1 ^ simd_shuffle_xor(r4, (ushort)2); // 51
r5 = r5 + r4 + select(0x553b85d1u, 0x4e4a2759u, ((sel >> 16u) & 1u) != 0u); // 52
r1 = r1 ^ dataset[r3 & MASK]; // 53
r6 = rotl_imm(r6, 21u); // 54
r1 = mulhi(r1, r4); // 55
r0 = r0 ^ simd_shuffle_xor(r3, (ushort)1); // 56
r1 = r1 ^ r2; // 57
r3 = r3 ^ simd_shuffle_xor(r6, (ushort)16); // 58
r5 = r5 * r0; // 59
r4 = r4 ^ dataset[r2 & MASK]; // 60
r0 = rotr_var(r0, r4); // 61
r6 = r6 * r7; // 62
r6 = rotl_imm(r6, 2u); // 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;
}

View file

@ -0,0 +1,111 @@
#include <metal_stdlib>
using namespace metal;
#define MASK 0x0fffffffu
constant uint SEEDW[8] = { 0x774fd410u, 0x520f91b2u, 0x9357786fu, 0x8bbe44d7u, 0x23db18feu, 0x52022aecu, 0x653ea608u, 0x57788e47u };
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;
}
// Header-bound variant: the init words come from buffer 3 (bind.rs), not from SEEDW.
kernel void igneum_hash_bound(device const uint* dataset [[buffer(0)]],
device ulong* out [[buffer(1)]],
constant uint& baseNonce [[buffer(2)]],
constant uint* initw [[buffer(3)]],
uint gid [[thread_position_in_grid]]) {
uint nonce = baseNonce + gid;
uint r0, r1, r2, r3, r4, r5, r6, r7;
{ uint x = nonce ^ initw[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ initw[1]; }
{ uint x = nonce ^ initw[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ initw[2]; }
{ uint x = nonce ^ initw[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ initw[3]; }
{ uint x = nonce ^ initw[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ initw[4]; }
{ uint x = nonce ^ initw[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ initw[5]; }
{ uint x = nonce ^ initw[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ initw[6]; }
{ uint x = nonce ^ initw[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ initw[7]; }
{ uint x = nonce ^ initw[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ initw[0]; }
for (uint it = 0u; it < 8u; ++it) {
uint sel = r0;
r2 = r2 + r7 + select(0x17c8e8eeu, 0xd8952471u, ((sel >> 7u) & 1u) != 0u); // 0
r0 = rotr_var(r0, r5); // 1
r5 = r5 + r0 + select(0x37d9560au, 0x4325cc6au, ((sel >> 21u) & 1u) != 0u); // 2
r7 = r7 ^ r5; // 3
{ uint b_ = (r0 & MASK) & ~15u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r3 = x_; } // 4
{ uint b_ = (r7 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r5 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r5 = x_; } // 5
r0 = mulhi(r0, r5); // 6
r4 = r4 * r7; // 7
r0 = r5 * r6 + r0; // 8
{ uint b_ = (r2 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r6 = x_; } // 9
r0 = r0 ^ r6; // 10
r5 = r5 + r3 + select(0xbc4eca12u, 0x81619a4cu, ((sel >> 22u) & 1u) != 0u); // 11
r4 = mulhi(r4, r2); // 12
r2 = r2 ^ dataset[r0 & MASK]; // 13
r0 = r0 + r2 + select(0xcf9919eeu, 0xa557fd2bu, ((sel >> 3u) & 1u) != 0u); // 14
r4 = r4 + r5 + select(0x36ec1d20u, 0x33857f70u, ((sel >> 7u) & 1u) != 0u); // 15
r7 = r7 + r5 + select(0x42f5db15u, 0x40495714u, ((sel >> 6u) & 1u) != 0u); // 16
r7 = r7 ^ dataset[r5 & MASK]; // 17
r3 = r3 ^ r6; // 18
{ uint b_ = (r2 & MASK) & ~15u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint x_ = r7 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r7 = x_; } // 19
{ uint b_ = (r3 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r5 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r5 = x_; } // 20
r1 = r1 ^ dataset[r0 & MASK]; // 21
r1 = mulhi(r1, r3); // 22
r1 = rotr_var(r1, r5); // 23
r2 = mulhi(r2, r0); // 24
{ uint b_ = (r4 & MASK) & ~15u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r0 = x_; } // 25
{ uint b_ = (r1 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r0 = x_; } // 26
r5 = r5 ^ simd_shuffle_xor(r2, (ushort)2); // 27
r5 = r5 + r4 + select(0x22029cb9u, 0x58a2eb85u, ((sel >> 12u) & 1u) != 0u); // 28
{ uint b_ = (r5 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r2 = x_; } // 29
r6 = r6 * r5; // 30
r6 = r6 ^ dataset[r2 & MASK]; // 31
r7 = mulhi(r7, r5); // 32
r0 = r0 ^ dataset[r7 & MASK]; // 33
r7 = r7 ^ simd_shuffle_xor(r2, (ushort)1); // 34
r7 = r7 * r0; // 35
r5 = r5 ^ dataset[r7 & MASK]; // 36
r3 = rotr_var(r3, r2); // 37
r6 = rotl_imm(r6, 1u); // 38
r3 = r3 * r0; // 39
r3 = mulhi(r3, r7); // 40
r5 = r5 ^ r2; // 41
r4 = r4 * r0; // 42
r3 = r3 + r0 + select(0x831bfab3u, 0xfaaf2d1eu, ((sel >> 4u) & 1u) != 0u); // 43
r0 = r1 * r6 + r0; // 44
r6 = rotl_imm(r6, 26u); // 45
r2 = r2 ^ r1; // 46
r0 = r0 ^ simd_shuffle_xor(r7, (ushort)2); // 47
r1 = r1 ^ r2; // 48
r3 = r3 + r5 + select(0x31df1a86u, 0xc5759120u, ((sel >> 7u) & 1u) != 0u); // 49
r1 = r4 * r1 + r1; // 50
r1 = r1 ^ simd_shuffle_xor(r4, (ushort)2); // 51
r5 = r5 + r4 + select(0x553b85d1u, 0x4e4a2759u, ((sel >> 16u) & 1u) != 0u); // 52
r1 = r1 ^ dataset[r3 & MASK]; // 53
r6 = rotl_imm(r6, 21u); // 54
r1 = mulhi(r1, r4); // 55
r0 = r0 ^ simd_shuffle_xor(r3, (ushort)1); // 56
r1 = r1 ^ r2; // 57
r3 = r3 ^ simd_shuffle_xor(r6, (ushort)16); // 58
r5 = r5 * r0; // 59
r4 = r4 ^ dataset[r2 & MASK]; // 60
r0 = rotr_var(r0, r4); // 61
r6 = r6 * r7; // 62
r6 = rotl_imm(r6, 2u); // 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;
}

View file

@ -0,0 +1,57 @@
// Generated by igneum-pow export (generator v2) for seed "igneum-readwidth/A/2". Do not edit by hand.
// Expected outputs: igneum-pow (Rust) CPU interpreter, generator v2, memory-hard dataset
#pragma once
#ifdef __cplusplus
#include <cstdint>
#else
#include <stdint.h>
#endif
#define IGNEUM_VEC_WARPS 3
static const uint32_t IGNEUM_VEC_BASE[IGNEUM_VEC_WARPS] = { 0u, 4096u, 1000000u };
static const uint64_t IGNEUM_VEC_OUT[IGNEUM_VEC_WARPS][32] = {
{ // base nonce 0
0x1e5a818edb31f50eull, 0x39ab5ff405f05a3aull, 0x8ba99b65319aea26ull, 0x00e66c9a5d9edc85ull, 0x673a7e5b68954010ull, 0x7182b60aa5acefacull, 0x14242454aa3bdad1ull, 0x68c88b2b4ee2d9a4ull,
0xd5a63f69306d1985ull, 0x7f1ed7474726b715ull, 0x10b2f2b157e45181ull, 0x267f18d74afdb191ull, 0xe6d713eba1ee4ec4ull, 0xd1112f5d2ca5a7e6ull, 0xd4407d80e22e732dull, 0xf0ddeb92365545aeull,
0xc877c1ad94896995ull, 0x9ecf17f1b4b9be25ull, 0x1f529607bcdc4065ull, 0xe7cc5c40d4e4d57bull, 0xb29bf3beadf6f5d7ull, 0xbc674c35c773fcacull, 0x35ffa42b4c965803ull, 0xaf0c5c09e68ba314ull,
0x6a313419bd31d9a6ull, 0xae5dd12365e96057ull, 0xd36b68108f13bf22ull, 0xb24bce1326403870ull, 0x4e79d454a075b862ull, 0x6301a9ec32ab72a7ull, 0x2be3716f8db33d01ull, 0x0259670d3010002eull
},
{ // base nonce 4096
0x8caaf6166eb165bbull, 0xa589ad41c360235full, 0x221d65887c34c329ull, 0xb167f8e67bddb1c8ull, 0x3368dcad623b40adull, 0x0143881c07493949ull, 0x0c0d44b06195e623ull, 0xa23ac0e6640ff95full,
0x60e159dc557915a2ull, 0xea2b4ca8b06d5887ull, 0x5ff87ec91d1267e9ull, 0x49999f98b08dc991ull, 0x4a27b1ae7e9d2044ull, 0xad16f2167074cbceull, 0x2fe4de42eac4c102ull, 0x02b2e4945eb0f25full,
0x00ec90afcc4c7be1ull, 0xedd7998f09f6202bull, 0xe871ff53c81ba442ull, 0xea2cbe8c86632596ull, 0x8c4ef1bbc35954f2ull, 0x2fe50dcc0129f200ull, 0x45089308785c4e5aull, 0xecdb829e3482a126ull,
0x6092081b5ac666a3ull, 0xf20050f6cf7c36d0ull, 0x577b199c944cff9aull, 0xe567ab065574693cull, 0xd38fa9a00813f5bbull, 0x3f3b602229a4e685ull, 0x4bc63c32b3eee422ull, 0x0267ae18d258e96bull
},
{ // base nonce 1000000
0xbc346fa7af395ccaull, 0xf6ab289812882ea5ull, 0x1af5ae5da7af7f73ull, 0x3f1c7f8f7c1a0dd2ull, 0x01f938d5c871fedcull, 0x375a820f2322f11aull, 0x33be6d6c9d5e59e1ull, 0xc401cdcff783f93bull,
0x5e41cc8a873b6cdeull, 0x17b7410058197847ull, 0x9ea7c3ca06afb2d1ull, 0x601ddfbee56db4c5ull, 0x69e68ca477d75cb8ull, 0x7c226802f7f403a9ull, 0xcdb9bceade734442ull, 0x0a0a9e8e2524ceebull,
0x52a88bf50c886dbfull, 0x7a395acb2fc42cdfull, 0x060fe78ab0c1382bull, 0xeb448b87bdfd6af3ull, 0x8f9832144201dba5ull, 0x65a8bd919a4a5230ull, 0xea3ec06d53c77fa3ull, 0xab2ea3b4dcb53ae0ull,
0xbacf785210da5784ull, 0x01ccffb87836c39bull, 0x95eff222af8774d5ull, 0xeb12cd87ca802c98ull, 0x500473c6eb87f917ull, 0xf63aebcada9c2a14ull, 0xc3416375ce92b971ull, 0xe23abae6636093f9ull
}
};
// Dataset self-test: dataset[0..15] and dataset[IGNEUM_MASK] (268435455).
static const uint32_t IGNEUM_DS_HEAD[16] = {
0xffc3cd94u, 0x5920ccd8u, 0x392f44bbu, 0x5e57f67au, 0x2f2bc2a9u, 0x620b0e36u, 0xbdc09014u, 0x436654bfu,
0x311e0b48u, 0x1abd93adu, 0x59cc7ce8u, 0xee5247b2u, 0x86171fe8u, 0x6d874751u, 0xc9f7728fu, 0x7c2a435du
};
static const uint32_t IGNEUM_DS_LAST_INDEX = 268435455u;
static const uint32_t IGNEUM_DS_LAST = 0xa33ada72u;
// 64 sampled dataset words (index, value) computed on the Mac.
#define IGNEUM_DS_SAMPLES 64
static const uint32_t IGNEUM_DS_SAMPLE_INDEX[IGNEUM_DS_SAMPLES] = {
59471966u, 217795994u, 208353206u, 42483309u, 172547758u, 148076330u, 183853158u, 214389424u, 267488061u, 169781097u, 184093494u, 153880993u, 84977930u, 46426879u, 3093825u, 225364072u, 44593546u, 260713159u, 168250303u, 52384140u, 223401610u, 45554030u, 95410555u, 175039924u, 79171087u, 267580473u, 24168642u, 37981670u, 171551130u, 195559979u, 204611762u, 140997658u, 138925853u, 86637313u, 20736778u, 219665210u, 160430336u, 264654675u, 8013395u, 228945585u, 213884386u, 104419827u, 44185464u, 142737231u, 99284897u, 132475900u, 61861762u, 132056166u, 262388043u, 91878046u, 117353561u, 124768597u, 71352993u, 190698941u, 46055428u, 55281366u, 165145231u, 106810753u, 171985651u, 232085256u, 159510492u, 40072060u, 209107596u, 39023794u
};
static const uint32_t IGNEUM_DS_SAMPLE_VALUE[IGNEUM_DS_SAMPLES] = {
0xe8b73d94u, 0x337028b5u, 0xafe148c9u, 0xab99f7aeu, 0x434ea619u, 0xd85cb880u, 0x54764c7fu, 0x82c7e420u, 0xedf4cb9eu, 0x9884c959u, 0x223ee793u, 0x3a9ccf69u, 0x81da4fd2u, 0xd6ce8cb9u, 0xe3922dcau, 0x3e7e6bdeu, 0x382a3acau, 0x567e7f7fu, 0x25a0f084u, 0xbfeef128u, 0xe338abfbu, 0x7c3b5280u, 0x909bc5f1u, 0xd8b74b9cu, 0x8e31a22eu, 0x26b5f1d8u, 0x79122c00u, 0xcafc3340u, 0xd5e02ea3u, 0x1aee1afdu, 0xdb090d9au, 0xb049f435u, 0x4954d8bau, 0x03797ba0u, 0x196eefbdu, 0xd153412au, 0xbe5d2c4bu, 0xdaa14f0eu, 0x8e61ed07u, 0x9e9a64c6u, 0x2e29ff36u, 0x392a8589u, 0xb56a5912u, 0xfa6e8b57u, 0xd1a737cbu, 0xb0fa841au, 0xbe1c341fu, 0xe25be0f1u, 0xe937f543u, 0xebab2248u, 0x8e1b607au, 0x202a2fedu, 0x95e2819cu, 0x9c9652d4u, 0x32fedef0u, 0xdecfff82u, 0xcb5d43e5u, 0xb735806au, 0x8905939cu, 0xfbf8472du, 0xada74e5du, 0x7ebdeeeau, 0x0119f2b3u, 0xa9a376b8u
};
// Cache self-test (memory-hard mode): cache[0..15], the last 16 words, and FNV-1a 64 over all 2^26 words.
static const uint32_t IGNEUM_CACHE_HEAD[16] = {
0x355a86d2u, 0x7957db1cu, 0xd21772afu, 0x6fc1e09bu, 0xd55ce61du, 0x6e6a278bu, 0xd3f543ceu, 0x223d8e82u,
0x143ab337u, 0x2e9f05bdu, 0x2eb389bfu, 0x0c6e449eu, 0x5cfa4222u, 0xba6560feu, 0x8e3e1aa4u, 0xdbcc1d53u
};
static const uint32_t IGNEUM_CACHE_LAST[16] = {
0x41190d91u, 0xbd277957u, 0x22ddbb49u, 0x6986f207u, 0xdf69a4d6u, 0x26401a3au, 0x818230fbu, 0xc417122du,
0x3597b211u, 0xb553ce55u, 0xcf39cc0du, 0x3b7fc43au, 0x3fd43b00u, 0x67e1c80eu, 0xffa7ea7du, 0xca2960abu
};
static const uint64_t IGNEUM_CACHE_FNV64 = 0x48c4f5bf24166b2eull;

View file

@ -0,0 +1,36 @@
{
"seed": "igneum-readwidth/A/2",
"day": "2026-10-03",
"dataset_mode": "memory-hard",
"dataset_log2_words": 28,
"mask": "0x0fffffff",
"lanes": 32,
"source": "igneum-pow (Rust) CPU interpreter, generator v2, memory-hard dataset",
"warps": [
{"base_nonce": 0, "expected": [
"0x1e5a818edb31f50e", "0x39ab5ff405f05a3a", "0x8ba99b65319aea26", "0x00e66c9a5d9edc85", "0x673a7e5b68954010", "0x7182b60aa5acefac", "0x14242454aa3bdad1", "0x68c88b2b4ee2d9a4",
"0xd5a63f69306d1985", "0x7f1ed7474726b715", "0x10b2f2b157e45181", "0x267f18d74afdb191", "0xe6d713eba1ee4ec4", "0xd1112f5d2ca5a7e6", "0xd4407d80e22e732d", "0xf0ddeb92365545ae",
"0xc877c1ad94896995", "0x9ecf17f1b4b9be25", "0x1f529607bcdc4065", "0xe7cc5c40d4e4d57b", "0xb29bf3beadf6f5d7", "0xbc674c35c773fcac", "0x35ffa42b4c965803", "0xaf0c5c09e68ba314",
"0x6a313419bd31d9a6", "0xae5dd12365e96057", "0xd36b68108f13bf22", "0xb24bce1326403870", "0x4e79d454a075b862", "0x6301a9ec32ab72a7", "0x2be3716f8db33d01", "0x0259670d3010002e"
]},
{"base_nonce": 4096, "expected": [
"0x8caaf6166eb165bb", "0xa589ad41c360235f", "0x221d65887c34c329", "0xb167f8e67bddb1c8", "0x3368dcad623b40ad", "0x0143881c07493949", "0x0c0d44b06195e623", "0xa23ac0e6640ff95f",
"0x60e159dc557915a2", "0xea2b4ca8b06d5887", "0x5ff87ec91d1267e9", "0x49999f98b08dc991", "0x4a27b1ae7e9d2044", "0xad16f2167074cbce", "0x2fe4de42eac4c102", "0x02b2e4945eb0f25f",
"0x00ec90afcc4c7be1", "0xedd7998f09f6202b", "0xe871ff53c81ba442", "0xea2cbe8c86632596", "0x8c4ef1bbc35954f2", "0x2fe50dcc0129f200", "0x45089308785c4e5a", "0xecdb829e3482a126",
"0x6092081b5ac666a3", "0xf20050f6cf7c36d0", "0x577b199c944cff9a", "0xe567ab065574693c", "0xd38fa9a00813f5bb", "0x3f3b602229a4e685", "0x4bc63c32b3eee422", "0x0267ae18d258e96b"
]},
{"base_nonce": 1000000, "expected": [
"0xbc346fa7af395cca", "0xf6ab289812882ea5", "0x1af5ae5da7af7f73", "0x3f1c7f8f7c1a0dd2", "0x01f938d5c871fedc", "0x375a820f2322f11a", "0x33be6d6c9d5e59e1", "0xc401cdcff783f93b",
"0x5e41cc8a873b6cde", "0x17b7410058197847", "0x9ea7c3ca06afb2d1", "0x601ddfbee56db4c5", "0x69e68ca477d75cb8", "0x7c226802f7f403a9", "0xcdb9bceade734442", "0x0a0a9e8e2524ceeb",
"0x52a88bf50c886dbf", "0x7a395acb2fc42cdf", "0x060fe78ab0c1382b", "0xeb448b87bdfd6af3", "0x8f9832144201dba5", "0x65a8bd919a4a5230", "0xea3ec06d53c77fa3", "0xab2ea3b4dcb53ae0",
"0xbacf785210da5784", "0x01ccffb87836c39b", "0x95eff222af8774d5", "0xeb12cd87ca802c98", "0x500473c6eb87f917", "0xf63aebcada9c2a14", "0xc3416375ce92b971", "0xe23abae6636093f9"
]}
],
"dataset_head": ["0xffc3cd94", "0x5920ccd8", "0x392f44bb", "0x5e57f67a", "0x2f2bc2a9", "0x620b0e36", "0xbdc09014", "0x436654bf", "0x311e0b48", "0x1abd93ad", "0x59cc7ce8", "0xee5247b2", "0x86171fe8", "0x6d874751", "0xc9f7728f", "0x7c2a435d"],
"dataset_last_index": 268435455,
"dataset_last": "0xa33ada72",
"dataset_samples": [{"index": 59471966, "value": "0xe8b73d94"}, {"index": 217795994, "value": "0x337028b5"}, {"index": 208353206, "value": "0xafe148c9"}, {"index": 42483309, "value": "0xab99f7ae"}, {"index": 172547758, "value": "0x434ea619"}, {"index": 148076330, "value": "0xd85cb880"}, {"index": 183853158, "value": "0x54764c7f"}, {"index": 214389424, "value": "0x82c7e420"}, {"index": 267488061, "value": "0xedf4cb9e"}, {"index": 169781097, "value": "0x9884c959"}, {"index": 184093494, "value": "0x223ee793"}, {"index": 153880993, "value": "0x3a9ccf69"}, {"index": 84977930, "value": "0x81da4fd2"}, {"index": 46426879, "value": "0xd6ce8cb9"}, {"index": 3093825, "value": "0xe3922dca"}, {"index": 225364072, "value": "0x3e7e6bde"}, {"index": 44593546, "value": "0x382a3aca"}, {"index": 260713159, "value": "0x567e7f7f"}, {"index": 168250303, "value": "0x25a0f084"}, {"index": 52384140, "value": "0xbfeef128"}, {"index": 223401610, "value": "0xe338abfb"}, {"index": 45554030, "value": "0x7c3b5280"}, {"index": 95410555, "value": "0x909bc5f1"}, {"index": 175039924, "value": "0xd8b74b9c"}, {"index": 79171087, "value": "0x8e31a22e"}, {"index": 267580473, "value": "0x26b5f1d8"}, {"index": 24168642, "value": "0x79122c00"}, {"index": 37981670, "value": "0xcafc3340"}, {"index": 171551130, "value": "0xd5e02ea3"}, {"index": 195559979, "value": "0x1aee1afd"}, {"index": 204611762, "value": "0xdb090d9a"}, {"index": 140997658, "value": "0xb049f435"}, {"index": 138925853, "value": "0x4954d8ba"}, {"index": 86637313, "value": "0x03797ba0"}, {"index": 20736778, "value": "0x196eefbd"}, {"index": 219665210, "value": "0xd153412a"}, {"index": 160430336, "value": "0xbe5d2c4b"}, {"index": 264654675, "value": "0xdaa14f0e"}, {"index": 8013395, "value": "0x8e61ed07"}, {"index": 228945585, "value": "0x9e9a64c6"}, {"index": 213884386, "value": "0x2e29ff36"}, {"index": 104419827, "value": "0x392a8589"}, {"index": 44185464, "value": "0xb56a5912"}, {"index": 142737231, "value": "0xfa6e8b57"}, {"index": 99284897, "value": "0xd1a737cb"}, {"index": 132475900, "value": "0xb0fa841a"}, {"index": 61861762, "value": "0xbe1c341f"}, {"index": 132056166, "value": "0xe25be0f1"}, {"index": 262388043, "value": "0xe937f543"}, {"index": 91878046, "value": "0xebab2248"}, {"index": 117353561, "value": "0x8e1b607a"}, {"index": 124768597, "value": "0x202a2fed"}, {"index": 71352993, "value": "0x95e2819c"}, {"index": 190698941, "value": "0x9c9652d4"}, {"index": 46055428, "value": "0x32fedef0"}, {"index": 55281366, "value": "0xdecfff82"}, {"index": 165145231, "value": "0xcb5d43e5"}, {"index": 106810753, "value": "0xb735806a"}, {"index": 171985651, "value": "0x8905939c"}, {"index": 232085256, "value": "0xfbf8472d"}, {"index": 159510492, "value": "0xada74e5d"}, {"index": 40072060, "value": "0x7ebdeeea"}, {"index": 209107596, "value": "0x0119f2b3"}, {"index": 39023794, "value": "0xa9a376b8"}],
"cache_head": ["0x355a86d2", "0x7957db1c", "0xd21772af", "0x6fc1e09b", "0xd55ce61d", "0x6e6a278b", "0xd3f543ce", "0x223d8e82", "0x143ab337", "0x2e9f05bd", "0x2eb389bf", "0x0c6e449e", "0x5cfa4222", "0xba6560fe", "0x8e3e1aa4", "0xdbcc1d53"],
"cache_last_line": ["0x41190d91", "0xbd277957", "0x22ddbb49", "0x6986f207", "0xdf69a4d6", "0x26401a3a", "0x818230fb", "0xc417122d", "0x3597b211", "0xb553ce55", "0xcf39cc0d", "0x3b7fc43a", "0x3fd43b00", "0x67e1c80e", "0xffa7ea7d", "0xca2960ab"],
"cache_fnv1a64": "0x48c4f5bf24166b2e"
}

View file

@ -0,0 +1,278 @@
// Generated by igneum-pow export (generator v2) for seed "igneum-readwidth/A/3". Do not edit by hand.
// OpenCL C twin of the Metal kernel for the same seed (see proto-opencl/README.md, WAVEFRONT.md and program.metal).
// Built from source at runtime by proto-opencl/host.c, which passes these defines:
// IGNEUM_GROUP work-group size of igneum_hash, a multiple of 32 (default 32: one work-group = one 32-lane unit)
// IGNEUM_EXCHANGE 0 = local-memory exchange with a barrier (any device, any wave width; the default)
// 1 = sub_group_shuffle_xor (cl_khr_subgroup_shuffle), only with IGNEUM_GROUP 32 and a sub-group size of exactly 32
// 2 = intel_sub_group_shuffle_xor (cl_intel_subgroups), same condition
// The verification unit is always 32 lanes. A 64-wide hardware wave (AMD GCN/CDNA, RDNA in wave64) runs two units;
// the exchange masks are 1, 2, 4, 8, 16, so every partner lane lies inside the lane's own aligned run of 32.
#ifndef IGNEUM_GROUP
#define IGNEUM_GROUP 32
#endif
#ifndef IGNEUM_EXCHANGE
#define IGNEUM_EXCHANGE 0
#endif
#ifdef __OPENCL_VERSION__
#define IGNEUM_KERNEL_HASH __kernel __attribute__((reqd_work_group_size(IGNEUM_GROUP, 1, 1)))
#define IGNEUM_LOCAL_WORDS(name, n) __local uint name[n]
#if IGNEUM_EXCHANGE == 1
#ifdef cl_khr_subgroups
#pragma OPENCL EXTENSION cl_khr_subgroups : enable
#endif
#ifdef cl_khr_subgroup_shuffle
#pragma OPENCL EXTENSION cl_khr_subgroup_shuffle : enable
#endif
#elif IGNEUM_EXCHANGE == 2
#pragma OPENCL EXTENSION cl_intel_subgroups : enable
#endif
#else
// Not an OpenCL compiler: proto-opencl/emu compiles this file as C++ and supplies the built-ins and these two macros.
#include "emu_opencl.h"
#endif
#if IGNEUM_EXCHANGE == 1
#define IGNEUM_SHFL_XOR(dst, a, m) dst = sub_group_shuffle_xor((a), (uint)(m))
#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u)
#elif IGNEUM_EXCHANGE == 2
#define IGNEUM_SHFL_XOR(dst, a, m) dst = intel_sub_group_shuffle_xor((a), (uint)(m))
#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u)
#else
// Local-memory exchange. Two buffers of IGNEUM_GROUP words alternate (xk counts exchanges), so one barrier per
// exchange is enough: a lane can only overwrite buffer b at exchange k+2 after passing barrier k+1, and every lane
// reaches barrier k+1 only after its read of buffer b at exchange k. The partner lid ^ m stays inside the lane's
// aligned run of 32 because m < 32. Control flow is uniform, so every work-item reaches every barrier.
#define IGNEUM_SHFL_XOR(dst, a, m) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid ^ (uint)(m))]; xk += 1u; }
#define IGNEUM_BCAST0(dst, a) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid & ~31u)]; xk += 1u; }
#endif
static inline uint splitmix32(uint 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. OpenCL rotate() rotates left by n modulo 32.
static inline uint rotl_imm(uint x, uint n) { return rotate(x, n); }
// Right rotation by n modulo 32 as a left rotation by (32 - n) modulo 32; n == 0 gives x.
static inline uint rotr_var(uint x, uint n) { return rotate(x, (0u - n) & 31u); }
static 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;
}
// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines.
// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals.
#define MH_CACHE_LINE_MASK 0x003fffffu
#define MH_SEGMENT_LINES 64u
#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); }
static inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site
// y = ChaCha12 core(x) + x
static inline void mh_chacha_block(const uint* x, uint* y) {
for (uint i = 0u; i < 16u; ++i) y[i] = x[i];
for (uint r = 0u; r < 6u; ++r) {
MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u)
MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u)
}
for (uint i = 0u; i < 16u; ++i) y[i] += x[i];
}
// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0.
static inline void mh_cache_segment(__global uint* cache, uint seg) {
uint prev[16]; uint x[16]; uint y[16];
for (uint i = 0u; i < 16u; ++i) prev[i] = 0u;
for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) {
x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3];
x[4] = 0x3067619fu ^ prev[4];
x[5] = 0x3c269176u ^ prev[5];
x[6] = 0x84a03b03u ^ prev[6];
x[7] = 0xf8c63294u ^ prev[7];
x[8] = 0xff977c5bu ^ prev[8];
x[9] = 0xe60def3eu ^ prev[9];
x[10] = 0x63630141u ^ prev[10];
x[11] = 0xb8fbcb58u ^ prev[11];
x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15];
mh_chacha_block(x, y);
__global uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u);
for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; }
}
}
// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations.
static inline void mh_mixer(uint* s, uint rk) {
s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u;
s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu;
s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u;
s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu;
s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u;
s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u;
s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u;
s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u;
s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du;
s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u;
s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du;
s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu;
s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du;
s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu;
s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u;
s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u;
MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u)
MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u)
MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u)
MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u)
}
// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer.
static inline void mh_item(__global const uint* cache, uint t, uint* s) {
s[0] = 0x3067619fu;
s[1] = 0x3c269176u;
s[2] = 0x84a03b03u;
s[3] = 0xf8c63294u;
s[4] = 0xff977c5bu;
s[5] = 0xe60def3eu;
s[6] = 0x63630141u;
s[7] = 0xb8fbcb58u;
s[8] = t * 0x42146205u + 0xbab68293u;
s[9] = t * 0x52cbe0fbu + 0xcc162340u;
s[10] = t * 0x7ecf4a03u + 0x6ce151ccu;
s[11] = t * 0x6728907fu + 0xe62b8997u;
s[12] = t * 0xd81d9751u + 0xc9c80297u;
s[13] = t * 0x132952c3u + 0xf74a1654u;
s[14] = t * 0xf60de277u + 0x3d704af5u;
s[15] = t * 0x05358035u + 0x3cf522b7u;
for (uint r = 0u; r < 8u; ++r) {
mh_mixer(s, 0x9E3779B9u * (r + 1u));
__global const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u);
for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i];
}
mh_mixer(s, 0x9E3779B9u * 9u);
}
// dataset[w] without the dataset: derive item w >> 4 and take word w & 15.
static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; }
// Memory-hard dataset (MEMHARD.md). One work-item per cache segment; one work-item per 64-byte dataset item.
// The same constants as memhard.h in this pack (one emitter, three dialects).
__kernel void igneum_cache_fill(__global uint* cache, uint nSegments) {
uint seg = (uint)get_global_id(0);
if (seg < nSegments) mh_cache_segment(cache, seg);
}
__kernel void igneum_build(__global uint* ds, __global const uint* cache, uint nItems) {
uint t = (uint)get_global_id(0);
if (t < nItems) {
uint s[16];
mh_item(cache, t, s);
__global uint* d = ds + ((ulong)t * 16u);
for (uint i = 0u; i < 16u; ++i) d[i] = s[i];
}
}
// One hash per work-item. IGNEUM_GROUP is a multiple of 32; lane = lid & 31 and every exchange stays inside the
// lane's own aligned run of 32 work-items, exactly like simd_shuffle_xor inside a 32-wide Metal SIMD group and
// __shfl_xor_sync inside a CUDA warp. Control flow is uniform (no branches at all).
IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask) {
uint gid = (uint)get_global_id(0);
uint lid = (uint)get_local_id(0);
uint nonce = baseNonce + gid;
uint r0, r1, r2, r3, r4, r5, r6, r7;
#if IGNEUM_EXCHANGE == 0
IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP);
uint xk = 0u;
#else
(void)lid;
#endif
{ uint x = nonce ^ 0x30b957f0u; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x374e2a95u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1]
{ uint x = nonce ^ 0x374e2a95u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0xf416345eu; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2]
{ uint x = nonce ^ 0xf416345eu; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x7af15ccbu; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3]
{ uint x = nonce ^ 0x7af15ccbu; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xf0bcabc5u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4]
{ uint x = nonce ^ 0xf0bcabc5u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xb5b36f35u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5]
{ uint x = nonce ^ 0xb5b36f35u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0xf99641c7u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6]
{ uint x = nonce ^ 0xf99641c7u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0xd0312afeu; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7]
{ uint x = nonce ^ 0xd0312afeu; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x30b957f0u; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0]
for (uint it = 0u; it < 8u; ++it) {
uint sel = r0;
r3 = rotr_var(r3, r4); // 0 rotr
r0 = r0 ^ r7; // 1 xor
{ uint b_ = (r0 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r1 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r1 = x_; } // 2 load
r5 = rotl_imm(r5, 15u); // 3 rotl
r2 = r2 + r3 + ((((sel >> 6u) & 1u) != 0u) ? 0xd35e575cu : 0xd7a264d9u); // 4 add
{ uint t_; IGNEUM_SHFL_XOR(t_, r2, 1u); r5 = r5 ^ t_; } // 5 shfl
r1 = r2 * r5 + r1; // 6 mad
r2 = r2 | r3; // 7 or
r3 = r3 ^ r5; // 8 xor
r0 = r0 + r3 + ((((sel >> 29u) & 1u) != 0u) ? 0xd98be6edu : 0x4e543e70u); // 9 add
r5 = rotr_var(r5, r1); // 10 rotr
r3 = r3 * r5; // 11 mul
r2 = r2 ^ r7; // 12 xor
r1 = r1 + r6 + ((((sel >> 30u) & 1u) != 0u) ? 0x97a9219cu : 0xe36f3c9fu); // 13 add
r2 = r2 | r1; // 14 or
r1 = r1 | r3; // 15 or
r7 = r3 * r7 + r7; // 16 mad
r3 = r3 + r2 + ((((sel >> 13u) & 1u) != 0u) ? 0xf799b153u : 0x0c0a7b49u); // 17 add
r7 = r7 ^ ds[r5 & mask]; // 18 load
r7 = r7 ^ ds[r2 & mask]; // 19 load
{ uint t_; IGNEUM_SHFL_XOR(t_, r0, 8u); r5 = r5 ^ t_; } // 20 shfl
r4 = r4 ^ ds[r1 & mask]; // 21 load
r5 = r5 ^ ds[r7 & mask]; // 22 load
r4 = r4 ^ r2; // 23 xor
r5 = r5 + r3 + ((((sel >> 14u) & 1u) != 0u) ? 0x26eb8325u : 0x011f9670u); // 24 add
r0 = r0 * r1; // 25 mul
r4 = r4 ^ r7; // 26 xor
r7 = r7 + r5 + ((((sel >> 26u) & 1u) != 0u) ? 0xf12a4057u : 0x42177757u); // 27 add
r5 = r0 * r7 + r5; // 28 mad
r3 = r3 * r6; // 29 mul
r6 = rotl_imm(r6, 24u); // 30 rotl
r4 = rotr_var(r4, r0); // 31 rotr
r6 = r6 ^ ds[r4 & mask]; // 32 load
{ uint t_; IGNEUM_SHFL_XOR(t_, r1, 1u); r0 = r0 ^ t_; } // 33 shfl
r4 = r4 * r5; // 34 mul
r2 = rotl_imm(r2, 15u); // 35 rotl
r7 = r7 + r3 + ((((sel >> 5u) & 1u) != 0u) ? 0xc1b9573fu : 0xe0ebc725u); // 36 add
{ uint b_ = (r2 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r0 = x_; } // 37 load
r7 = r0 * r2 + r7; // 38 mad
r0 = r0 + r7 + ((((sel >> 16u) & 1u) != 0u) ? 0xde4cd365u : 0xd0b844deu); // 39 add
{ uint b_ = (r6 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r7 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r7 = x_; } // 40 load
r0 = r0 ^ r1; // 41 xor
r1 = rotl_imm(r1, 19u); // 42 rotl
r7 = r7 ^ r1; // 43 xor
r2 = r2 * r4; // 44 mul
{ uint b_ = (r4 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r6 = x_; } // 45 load
r7 = r7 + r3 + ((((sel >> 28u) & 1u) != 0u) ? 0x5d12f1a2u : 0x7633c48cu); // 46 add
r3 = r3 ^ ds[r2 & mask]; // 47 load
{ uint b_ = (r5 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r2 = x_; } // 48 load
r7 = r7 * r5; // 49 mul
r3 = r7 * r0 + r3; // 50 mad
{ uint b_ = (r2 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r3 = x_; } // 51 load
{ uint b_ = (r7 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r4 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r4 = x_; } // 52 load
r4 = mul_hi(r4, r1); // 53 mulhi
r7 = r7 + r4 + ((((sel >> 24u) & 1u) != 0u) ? 0xd8ed09bau : 0xf64a6e41u); // 54 add
r5 = mul_hi(r5, r3); // 55 mulhi
r5 = r5 * r7; // 56 mul
{ uint b_ = (r0 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r3 = x_; } // 57 load
r5 = mul_hi(r5, r2); // 58 mulhi
r7 = r7 * r5; // 59 mul
{ uint b_ = (r3 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r6 = x_; } // 60 load
r7 = r1 * r1 + r7; // 61 mad
r0 = r0 ^ ds[r7 & mask]; // 62 load
r5 = r5 * r3; // 63 mul
}
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;
}
#if IGNEUM_EXCHANGE != 0
// Reports the sub-group size this device uses for a work-group of IGNEUM_GROUP items. host.c runs it only when the
// per-kernel query (clGetKernelSubGroupInfoKHR on igneum_hash) is unavailable; that query is preferred because a
// compiler may pick a different wave width per kernel (RDNA: wave32 or wave64). See WAVEFRONT.md.
IGNEUM_KERNEL_HASH void igneum_probe_subgroup(__global uint* out) {
if (get_local_id(0) == 0u) { out[0] = get_sub_group_size(); out[1] = get_num_sub_groups(); }
}
#endif

View file

@ -0,0 +1,164 @@
// Generated by igneum-pow export (generator v2) for seed "igneum-readwidth/A/3". 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"
#include "memhard.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;
}
// Memory-hard dataset (MEMHARD.md). One thread per cache segment; one thread per 64-byte dataset item.
// The core functions (mh_cache_segment, mh_item) are in memhard.h and are also compiled for the host.
__global__ void igneum_cache_fill(uint32_t* cache, uint32_t nSegments) {
uint32_t seg = blockIdx.x * blockDim.x + threadIdx.x;
if (seg < nSegments) mh_cache_segment(cache, seg);
}
__global__ void igneum_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems) {
uint32_t t = blockIdx.x * blockDim.x + threadIdx.x;
if (t < nItems) {
uint32_t s[16];
mh_item(cache, t, s);
uint32_t* d = ds + (size_t)t * 16u;
for (uint32_t i = 0u; i < 16u; ++i) d[i] = s[i];
}
}
// 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 ^ 0x30b957f0u; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x374e2a95u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1]
{ uint32_t x = nonce ^ 0x374e2a95u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0xf416345eu; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2]
{ uint32_t x = nonce ^ 0xf416345eu; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x7af15ccbu; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3]
{ uint32_t x = nonce ^ 0x7af15ccbu; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xf0bcabc5u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4]
{ uint32_t x = nonce ^ 0xf0bcabc5u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xb5b36f35u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5]
{ uint32_t x = nonce ^ 0xb5b36f35u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0xf99641c7u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6]
{ uint32_t x = nonce ^ 0xf99641c7u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0xd0312afeu; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7]
{ uint32_t x = nonce ^ 0xd0312afeu; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x30b957f0u; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0]
for (uint32_t it = 0u; it < 8u; ++it) {
uint32_t sel = r0;
r3 = rotr_var(r3, r4); // 0 rotr
r0 = r0 ^ r7; // 1 xor
{ uint32_t b_ = (r0 & mask) & ~15u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint32_t x_ = r1 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r1 = x_; } // 2 load
r5 = rotl_imm(r5, 15u); // 3 rotl
r2 = r2 + r3 + ((((sel >> 6u) & 1u) != 0u) ? 0xd35e575cu : 0xd7a264d9u); // 4 add
r5 = r5 ^ __shfl_xor_sync(0xffffffffu, r2, 1); // 5 shfl
r1 = r2 * r5 + r1; // 6 mad
r2 = r2 | r3; // 7 or
r3 = r3 ^ r5; // 8 xor
r0 = r0 + r3 + ((((sel >> 29u) & 1u) != 0u) ? 0xd98be6edu : 0x4e543e70u); // 9 add
r5 = rotr_var(r5, r1); // 10 rotr
r3 = r3 * r5; // 11 mul
r2 = r2 ^ r7; // 12 xor
r1 = r1 + r6 + ((((sel >> 30u) & 1u) != 0u) ? 0x97a9219cu : 0xe36f3c9fu); // 13 add
r2 = r2 | r1; // 14 or
r1 = r1 | r3; // 15 or
r7 = r3 * r7 + r7; // 16 mad
r3 = r3 + r2 + ((((sel >> 13u) & 1u) != 0u) ? 0xf799b153u : 0x0c0a7b49u); // 17 add
r7 = r7 ^ ds[r5 & mask]; // 18 load
r7 = r7 ^ ds[r2 & mask]; // 19 load
r5 = r5 ^ __shfl_xor_sync(0xffffffffu, r0, 8); // 20 shfl
r4 = r4 ^ ds[r1 & mask]; // 21 load
r5 = r5 ^ ds[r7 & mask]; // 22 load
r4 = r4 ^ r2; // 23 xor
r5 = r5 + r3 + ((((sel >> 14u) & 1u) != 0u) ? 0x26eb8325u : 0x011f9670u); // 24 add
r0 = r0 * r1; // 25 mul
r4 = r4 ^ r7; // 26 xor
r7 = r7 + r5 + ((((sel >> 26u) & 1u) != 0u) ? 0xf12a4057u : 0x42177757u); // 27 add
r5 = r0 * r7 + r5; // 28 mad
r3 = r3 * r6; // 29 mul
r6 = rotl_imm(r6, 24u); // 30 rotl
r4 = rotr_var(r4, r0); // 31 rotr
r6 = r6 ^ ds[r4 & mask]; // 32 load
r0 = r0 ^ __shfl_xor_sync(0xffffffffu, r1, 1); // 33 shfl
r4 = r4 * r5; // 34 mul
r2 = rotl_imm(r2, 15u); // 35 rotl
r7 = r7 + r3 + ((((sel >> 5u) & 1u) != 0u) ? 0xc1b9573fu : 0xe0ebc725u); // 36 add
{ uint32_t b_ = (r2 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r0 = x_; } // 37 load
r7 = r0 * r2 + r7; // 38 mad
r0 = r0 + r7 + ((((sel >> 16u) & 1u) != 0u) ? 0xde4cd365u : 0xd0b844deu); // 39 add
{ uint32_t b_ = (r6 & mask) & ~15u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint32_t x_ = r7 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r7 = x_; } // 40 load
r0 = r0 ^ r1; // 41 xor
r1 = rotl_imm(r1, 19u); // 42 rotl
r7 = r7 ^ r1; // 43 xor
r2 = r2 * r4; // 44 mul
{ uint32_t b_ = (r4 & mask) & ~15u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint32_t x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r6 = x_; } // 45 load
r7 = r7 + r3 + ((((sel >> 28u) & 1u) != 0u) ? 0x5d12f1a2u : 0x7633c48cu); // 46 add
r3 = r3 ^ ds[r2 & mask]; // 47 load
{ uint32_t b_ = (r5 & mask) & ~15u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint32_t x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r2 = x_; } // 48 load
r7 = r7 * r5; // 49 mul
r3 = r7 * r0 + r3; // 50 mad
{ uint32_t b_ = (r2 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r3 = x_; } // 51 load
{ uint32_t b_ = (r7 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r4 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r4 = x_; } // 52 load
r4 = __umulhi(r4, r1); // 53 mulhi
r7 = r7 + r4 + ((((sel >> 24u) & 1u) != 0u) ? 0xd8ed09bau : 0xf64a6e41u); // 54 add
r5 = __umulhi(r5, r3); // 55 mulhi
r5 = r5 * r7; // 56 mul
{ uint32_t b_ = (r0 & mask) & ~15u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint32_t x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r3 = x_; } // 57 load
r5 = __umulhi(r5, r2); // 58 mulhi
r7 = r7 * r5; // 59 mul
{ uint32_t b_ = (r3 & mask) & ~15u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint32_t x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r6 = x_; } // 60 load
r7 = r1 * r1 + r7; // 61 mad
r0 = r0 ^ ds[r7 & mask]; // 62 load
r5 = r5 * r3; // 63 mul
}
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_cache_fill(uint32_t* cache, uint32_t nSegments) {
if (nSegments == 0u) return cudaErrorInvalidValue;
uint32_t block = 256u;
uint32_t grid = (nSegments + block - 1u) / block;
igneum_cache_fill<<<grid, block>>>(cache, nSegments);
return cudaGetLastError();
}
cudaError_t igneum_launch_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems) {
if (nItems == 0u) return cudaErrorInvalidValue;
uint32_t block = 256u;
uint32_t grid = (nItems + block - 1u) / block;
igneum_build<<<grid, block>>>(ds, cache, nItems);
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);
}

View file

@ -0,0 +1,372 @@
// Generated by igneum-pow export (generator v2) for seed "igneum-readwidth/A/3". Do not edit by hand.
// OpenCL C twin of the Metal kernel for the same seed (see proto-opencl/README.md, WAVEFRONT.md and program.metal).
// Built from source at runtime by proto-opencl/host.c, which passes these defines:
// IGNEUM_GROUP work-group size of igneum_hash, a multiple of 32 (default 32: one work-group = one 32-lane unit)
// IGNEUM_EXCHANGE 0 = local-memory exchange with a barrier (any device, any wave width; the default)
// 1 = sub_group_shuffle_xor (cl_khr_subgroup_shuffle), only with IGNEUM_GROUP 32 and a sub-group size of exactly 32
// 2 = intel_sub_group_shuffle_xor (cl_intel_subgroups), same condition
// The verification unit is always 32 lanes. A 64-wide hardware wave (AMD GCN/CDNA, RDNA in wave64) runs two units;
// the exchange masks are 1, 2, 4, 8, 16, so every partner lane lies inside the lane's own aligned run of 32.
#ifndef IGNEUM_GROUP
#define IGNEUM_GROUP 32
#endif
#ifndef IGNEUM_EXCHANGE
#define IGNEUM_EXCHANGE 0
#endif
#ifdef __OPENCL_VERSION__
#define IGNEUM_KERNEL_HASH __kernel __attribute__((reqd_work_group_size(IGNEUM_GROUP, 1, 1)))
#define IGNEUM_LOCAL_WORDS(name, n) __local uint name[n]
#if IGNEUM_EXCHANGE == 1
#ifdef cl_khr_subgroups
#pragma OPENCL EXTENSION cl_khr_subgroups : enable
#endif
#ifdef cl_khr_subgroup_shuffle
#pragma OPENCL EXTENSION cl_khr_subgroup_shuffle : enable
#endif
#elif IGNEUM_EXCHANGE == 2
#pragma OPENCL EXTENSION cl_intel_subgroups : enable
#endif
#else
// Not an OpenCL compiler: proto-opencl/emu compiles this file as C++ and supplies the built-ins and these two macros.
#include "emu_opencl.h"
#endif
#if IGNEUM_EXCHANGE == 1
#define IGNEUM_SHFL_XOR(dst, a, m) dst = sub_group_shuffle_xor((a), (uint)(m))
#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u)
#elif IGNEUM_EXCHANGE == 2
#define IGNEUM_SHFL_XOR(dst, a, m) dst = intel_sub_group_shuffle_xor((a), (uint)(m))
#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u)
#else
// Local-memory exchange. Two buffers of IGNEUM_GROUP words alternate (xk counts exchanges), so one barrier per
// exchange is enough: a lane can only overwrite buffer b at exchange k+2 after passing barrier k+1, and every lane
// reaches barrier k+1 only after its read of buffer b at exchange k. The partner lid ^ m stays inside the lane's
// aligned run of 32 because m < 32. Control flow is uniform, so every work-item reaches every barrier.
#define IGNEUM_SHFL_XOR(dst, a, m) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid ^ (uint)(m))]; xk += 1u; }
#define IGNEUM_BCAST0(dst, a) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid & ~31u)]; xk += 1u; }
#endif
static inline uint splitmix32(uint 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. OpenCL rotate() rotates left by n modulo 32.
static inline uint rotl_imm(uint x, uint n) { return rotate(x, n); }
// Right rotation by n modulo 32 as a left rotation by (32 - n) modulo 32; n == 0 gives x.
static inline uint rotr_var(uint x, uint n) { return rotate(x, (0u - n) & 31u); }
static 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;
}
// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines.
// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals.
#define MH_CACHE_LINE_MASK 0x003fffffu
#define MH_SEGMENT_LINES 64u
#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); }
static inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site
// y = ChaCha12 core(x) + x
static inline void mh_chacha_block(const uint* x, uint* y) {
for (uint i = 0u; i < 16u; ++i) y[i] = x[i];
for (uint r = 0u; r < 6u; ++r) {
MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u)
MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u)
}
for (uint i = 0u; i < 16u; ++i) y[i] += x[i];
}
// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0.
static inline void mh_cache_segment(__global uint* cache, uint seg) {
uint prev[16]; uint x[16]; uint y[16];
for (uint i = 0u; i < 16u; ++i) prev[i] = 0u;
for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) {
x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3];
x[4] = 0x3067619fu ^ prev[4];
x[5] = 0x3c269176u ^ prev[5];
x[6] = 0x84a03b03u ^ prev[6];
x[7] = 0xf8c63294u ^ prev[7];
x[8] = 0xff977c5bu ^ prev[8];
x[9] = 0xe60def3eu ^ prev[9];
x[10] = 0x63630141u ^ prev[10];
x[11] = 0xb8fbcb58u ^ prev[11];
x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15];
mh_chacha_block(x, y);
__global uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u);
for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; }
}
}
// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations.
static inline void mh_mixer(uint* s, uint rk) {
s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u;
s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu;
s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u;
s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu;
s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u;
s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u;
s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u;
s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u;
s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du;
s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u;
s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du;
s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu;
s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du;
s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu;
s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u;
s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u;
MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u)
MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u)
MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u)
MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u)
}
// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer.
static inline void mh_item(__global const uint* cache, uint t, uint* s) {
s[0] = 0x3067619fu;
s[1] = 0x3c269176u;
s[2] = 0x84a03b03u;
s[3] = 0xf8c63294u;
s[4] = 0xff977c5bu;
s[5] = 0xe60def3eu;
s[6] = 0x63630141u;
s[7] = 0xb8fbcb58u;
s[8] = t * 0x42146205u + 0xbab68293u;
s[9] = t * 0x52cbe0fbu + 0xcc162340u;
s[10] = t * 0x7ecf4a03u + 0x6ce151ccu;
s[11] = t * 0x6728907fu + 0xe62b8997u;
s[12] = t * 0xd81d9751u + 0xc9c80297u;
s[13] = t * 0x132952c3u + 0xf74a1654u;
s[14] = t * 0xf60de277u + 0x3d704af5u;
s[15] = t * 0x05358035u + 0x3cf522b7u;
for (uint r = 0u; r < 8u; ++r) {
mh_mixer(s, 0x9E3779B9u * (r + 1u));
__global const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u);
for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i];
}
mh_mixer(s, 0x9E3779B9u * 9u);
}
// dataset[w] without the dataset: derive item w >> 4 and take word w & 15.
static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; }
// Memory-hard dataset (MEMHARD.md). One work-item per cache segment; one work-item per 64-byte dataset item.
// The same constants as memhard.h in this pack (one emitter, three dialects).
__kernel void igneum_cache_fill(__global uint* cache, uint nSegments) {
uint seg = (uint)get_global_id(0);
if (seg < nSegments) mh_cache_segment(cache, seg);
}
__kernel void igneum_build(__global uint* ds, __global const uint* cache, uint nItems) {
uint t = (uint)get_global_id(0);
if (t < nItems) {
uint s[16];
mh_item(cache, t, s);
__global uint* d = ds + ((ulong)t * 16u);
for (uint i = 0u; i < 16u; ++i) d[i] = s[i];
}
}
// One hash per work-item. IGNEUM_GROUP is a multiple of 32; lane = lid & 31 and every exchange stays inside the
// lane's own aligned run of 32 work-items, exactly like simd_shuffle_xor inside a 32-wide Metal SIMD group and
// __shfl_xor_sync inside a CUDA warp. Control flow is uniform (no branches at all).
IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask) {
uint gid = (uint)get_global_id(0);
uint lid = (uint)get_local_id(0);
uint nonce = baseNonce + gid;
uint r0, r1, r2, r3, r4, r5, r6, r7;
#if IGNEUM_EXCHANGE == 0
IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP);
uint xk = 0u;
#else
(void)lid;
#endif
{ uint x = nonce ^ 0x30b957f0u; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x374e2a95u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1]
{ uint x = nonce ^ 0x374e2a95u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0xf416345eu; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2]
{ uint x = nonce ^ 0xf416345eu; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x7af15ccbu; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3]
{ uint x = nonce ^ 0x7af15ccbu; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xf0bcabc5u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4]
{ uint x = nonce ^ 0xf0bcabc5u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xb5b36f35u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5]
{ uint x = nonce ^ 0xb5b36f35u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0xf99641c7u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6]
{ uint x = nonce ^ 0xf99641c7u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0xd0312afeu; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7]
{ uint x = nonce ^ 0xd0312afeu; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x30b957f0u; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0]
for (uint it = 0u; it < 8u; ++it) {
uint sel = r0;
r3 = rotr_var(r3, r4); // 0 rotr
r0 = r0 ^ r7; // 1 xor
{ uint b_ = (r0 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r1 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r1 = x_; } // 2 load
r5 = rotl_imm(r5, 15u); // 3 rotl
r2 = r2 + r3 + ((((sel >> 6u) & 1u) != 0u) ? 0xd35e575cu : 0xd7a264d9u); // 4 add
{ uint t_; IGNEUM_SHFL_XOR(t_, r2, 1u); r5 = r5 ^ t_; } // 5 shfl
r1 = r2 * r5 + r1; // 6 mad
r2 = r2 | r3; // 7 or
r3 = r3 ^ r5; // 8 xor
r0 = r0 + r3 + ((((sel >> 29u) & 1u) != 0u) ? 0xd98be6edu : 0x4e543e70u); // 9 add
r5 = rotr_var(r5, r1); // 10 rotr
r3 = r3 * r5; // 11 mul
r2 = r2 ^ r7; // 12 xor
r1 = r1 + r6 + ((((sel >> 30u) & 1u) != 0u) ? 0x97a9219cu : 0xe36f3c9fu); // 13 add
r2 = r2 | r1; // 14 or
r1 = r1 | r3; // 15 or
r7 = r3 * r7 + r7; // 16 mad
r3 = r3 + r2 + ((((sel >> 13u) & 1u) != 0u) ? 0xf799b153u : 0x0c0a7b49u); // 17 add
r7 = r7 ^ ds[r5 & mask]; // 18 load
r7 = r7 ^ ds[r2 & mask]; // 19 load
{ uint t_; IGNEUM_SHFL_XOR(t_, r0, 8u); r5 = r5 ^ t_; } // 20 shfl
r4 = r4 ^ ds[r1 & mask]; // 21 load
r5 = r5 ^ ds[r7 & mask]; // 22 load
r4 = r4 ^ r2; // 23 xor
r5 = r5 + r3 + ((((sel >> 14u) & 1u) != 0u) ? 0x26eb8325u : 0x011f9670u); // 24 add
r0 = r0 * r1; // 25 mul
r4 = r4 ^ r7; // 26 xor
r7 = r7 + r5 + ((((sel >> 26u) & 1u) != 0u) ? 0xf12a4057u : 0x42177757u); // 27 add
r5 = r0 * r7 + r5; // 28 mad
r3 = r3 * r6; // 29 mul
r6 = rotl_imm(r6, 24u); // 30 rotl
r4 = rotr_var(r4, r0); // 31 rotr
r6 = r6 ^ ds[r4 & mask]; // 32 load
{ uint t_; IGNEUM_SHFL_XOR(t_, r1, 1u); r0 = r0 ^ t_; } // 33 shfl
r4 = r4 * r5; // 34 mul
r2 = rotl_imm(r2, 15u); // 35 rotl
r7 = r7 + r3 + ((((sel >> 5u) & 1u) != 0u) ? 0xc1b9573fu : 0xe0ebc725u); // 36 add
{ uint b_ = (r2 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r0 = x_; } // 37 load
r7 = r0 * r2 + r7; // 38 mad
r0 = r0 + r7 + ((((sel >> 16u) & 1u) != 0u) ? 0xde4cd365u : 0xd0b844deu); // 39 add
{ uint b_ = (r6 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r7 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r7 = x_; } // 40 load
r0 = r0 ^ r1; // 41 xor
r1 = rotl_imm(r1, 19u); // 42 rotl
r7 = r7 ^ r1; // 43 xor
r2 = r2 * r4; // 44 mul
{ uint b_ = (r4 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r6 = x_; } // 45 load
r7 = r7 + r3 + ((((sel >> 28u) & 1u) != 0u) ? 0x5d12f1a2u : 0x7633c48cu); // 46 add
r3 = r3 ^ ds[r2 & mask]; // 47 load
{ uint b_ = (r5 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r2 = x_; } // 48 load
r7 = r7 * r5; // 49 mul
r3 = r7 * r0 + r3; // 50 mad
{ uint b_ = (r2 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r3 = x_; } // 51 load
{ uint b_ = (r7 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r4 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r4 = x_; } // 52 load
r4 = mul_hi(r4, r1); // 53 mulhi
r7 = r7 + r4 + ((((sel >> 24u) & 1u) != 0u) ? 0xd8ed09bau : 0xf64a6e41u); // 54 add
r5 = mul_hi(r5, r3); // 55 mulhi
r5 = r5 * r7; // 56 mul
{ uint b_ = (r0 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r3 = x_; } // 57 load
r5 = mul_hi(r5, r2); // 58 mulhi
r7 = r7 * r5; // 59 mul
{ uint b_ = (r3 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r6 = x_; } // 60 load
r7 = r1 * r1 + r7; // 61 mad
r0 = r0 ^ ds[r7 & mask]; // 62 load
r5 = r5 * r3; // 63 mul
}
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;
}
#if IGNEUM_EXCHANGE != 0
// Reports the sub-group size this device uses for a work-group of IGNEUM_GROUP items. host.c runs it only when the
// per-kernel query (clGetKernelSubGroupInfoKHR on igneum_hash) is unavailable; that query is preferred because a
// compiler may pick a different wave width per kernel (RDNA: wave32 or wave64). See WAVEFRONT.md.
IGNEUM_KERNEL_HASH void igneum_probe_subgroup(__global uint* out) {
if (get_local_id(0) == 0u) { out[0] = get_sub_group_size(); out[1] = get_num_sub_groups(); }
}
#endif
// Header-bound variant (bind.rs): the init words come from initw, not SEEDW. Same body as igneum_hash.
IGNEUM_KERNEL_HASH void igneum_hash_bound(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask, __global const uint* initw) {
uint gid = (uint)get_global_id(0);
uint lid = (uint)get_local_id(0);
uint nonce = baseNonce + gid;
uint r0, r1, r2, r3, r4, r5, r6, r7;
uint iw0 = initw[0], iw1 = initw[1], iw2 = initw[2], iw3 = initw[3], iw4 = initw[4], iw5 = initw[5], iw6 = initw[6], iw7 = initw[7];
#if IGNEUM_EXCHANGE == 0
IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP);
uint xk = 0u;
#else
(void)lid;
#endif
{ uint x = nonce ^ iw0; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ iw1; }
{ uint x = nonce ^ iw1; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ iw2; }
{ uint x = nonce ^ iw2; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ iw3; }
{ uint x = nonce ^ iw3; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ iw4; }
{ uint x = nonce ^ iw4; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ iw5; }
{ uint x = nonce ^ iw5; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ iw6; }
{ uint x = nonce ^ iw6; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ iw7; }
{ uint x = nonce ^ iw7; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ iw0; }
for (uint it = 0u; it < 8u; ++it) {
uint sel = r0;
r3 = rotr_var(r3, r4); // 0 rotr
r0 = r0 ^ r7; // 1 xor
{ uint b_ = (r0 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r1 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r1 = x_; } // 2 load
r5 = rotl_imm(r5, 15u); // 3 rotl
r2 = r2 + r3 + ((((sel >> 6u) & 1u) != 0u) ? 0xd35e575cu : 0xd7a264d9u); // 4 add
{ uint t_; IGNEUM_SHFL_XOR(t_, r2, 1u); r5 = r5 ^ t_; } // 5 shfl
r1 = r2 * r5 + r1; // 6 mad
r2 = r2 | r3; // 7 or
r3 = r3 ^ r5; // 8 xor
r0 = r0 + r3 + ((((sel >> 29u) & 1u) != 0u) ? 0xd98be6edu : 0x4e543e70u); // 9 add
r5 = rotr_var(r5, r1); // 10 rotr
r3 = r3 * r5; // 11 mul
r2 = r2 ^ r7; // 12 xor
r1 = r1 + r6 + ((((sel >> 30u) & 1u) != 0u) ? 0x97a9219cu : 0xe36f3c9fu); // 13 add
r2 = r2 | r1; // 14 or
r1 = r1 | r3; // 15 or
r7 = r3 * r7 + r7; // 16 mad
r3 = r3 + r2 + ((((sel >> 13u) & 1u) != 0u) ? 0xf799b153u : 0x0c0a7b49u); // 17 add
r7 = r7 ^ ds[r5 & mask]; // 18 load
r7 = r7 ^ ds[r2 & mask]; // 19 load
{ uint t_; IGNEUM_SHFL_XOR(t_, r0, 8u); r5 = r5 ^ t_; } // 20 shfl
r4 = r4 ^ ds[r1 & mask]; // 21 load
r5 = r5 ^ ds[r7 & mask]; // 22 load
r4 = r4 ^ r2; // 23 xor
r5 = r5 + r3 + ((((sel >> 14u) & 1u) != 0u) ? 0x26eb8325u : 0x011f9670u); // 24 add
r0 = r0 * r1; // 25 mul
r4 = r4 ^ r7; // 26 xor
r7 = r7 + r5 + ((((sel >> 26u) & 1u) != 0u) ? 0xf12a4057u : 0x42177757u); // 27 add
r5 = r0 * r7 + r5; // 28 mad
r3 = r3 * r6; // 29 mul
r6 = rotl_imm(r6, 24u); // 30 rotl
r4 = rotr_var(r4, r0); // 31 rotr
r6 = r6 ^ ds[r4 & mask]; // 32 load
{ uint t_; IGNEUM_SHFL_XOR(t_, r1, 1u); r0 = r0 ^ t_; } // 33 shfl
r4 = r4 * r5; // 34 mul
r2 = rotl_imm(r2, 15u); // 35 rotl
r7 = r7 + r3 + ((((sel >> 5u) & 1u) != 0u) ? 0xc1b9573fu : 0xe0ebc725u); // 36 add
{ uint b_ = (r2 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r0 = x_; } // 37 load
r7 = r0 * r2 + r7; // 38 mad
r0 = r0 + r7 + ((((sel >> 16u) & 1u) != 0u) ? 0xde4cd365u : 0xd0b844deu); // 39 add
{ uint b_ = (r6 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r7 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r7 = x_; } // 40 load
r0 = r0 ^ r1; // 41 xor
r1 = rotl_imm(r1, 19u); // 42 rotl
r7 = r7 ^ r1; // 43 xor
r2 = r2 * r4; // 44 mul
{ uint b_ = (r4 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r6 = x_; } // 45 load
r7 = r7 + r3 + ((((sel >> 28u) & 1u) != 0u) ? 0x5d12f1a2u : 0x7633c48cu); // 46 add
r3 = r3 ^ ds[r2 & mask]; // 47 load
{ uint b_ = (r5 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r2 = x_; } // 48 load
r7 = r7 * r5; // 49 mul
r3 = r7 * r0 + r3; // 50 mad
{ uint b_ = (r2 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r3 = x_; } // 51 load
{ uint b_ = (r7 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r4 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r4 = x_; } // 52 load
r4 = mul_hi(r4, r1); // 53 mulhi
r7 = r7 + r4 + ((((sel >> 24u) & 1u) != 0u) ? 0xd8ed09bau : 0xf64a6e41u); // 54 add
r5 = mul_hi(r5, r3); // 55 mulhi
r5 = r5 * r7; // 56 mul
{ uint b_ = (r0 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r3 = x_; } // 57 load
r5 = mul_hi(r5, r2); // 58 mulhi
r7 = r7 * r5; // 59 mul
{ uint b_ = (r3 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r6 = x_; } // 60 load
r7 = r1 * r1 + r7; // 61 mad
r0 = r0 ^ ds[r7 & mask]; // 62 load
r5 = r5 * r3; // 63 mul
}
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;
}

View file

@ -0,0 +1,123 @@
// Generated by igneum-pow export (generator v2) for seed "igneum-readwidth/A/3". Do not edit by hand.
// Header-bound twin of igneum_hash in kernel.cu: the init words come from a kernel argument, not SEEDW.
// Host declarations (also in program_bound.h if present):
// struct IgneumInitWords { uint32_t w[8]; };
// cudaError_t igneum_launch_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask,
// IgneumInitWords iw, uint32_t nonces, uint32_t blockWarps);
// cudaError_t igneum_hash_bound_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps);
#include <cuda_runtime.h>
#include <cstdint>
#include "program.h"
struct IgneumInitWords { uint32_t w[8]; };
__device__ __forceinline__ uint32_t splitmix32(uint32_t x) {
x ^= x >> 16; x *= 0x7feb352du;
x ^= x >> 15; x *= 0x846ca68bu;
x ^= x >> 16;
return x;
}
__device__ __forceinline__ uint32_t rotl_imm(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); }
__device__ __forceinline__ uint32_t rotr_var(uint32_t x, uint32_t n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); }
__global__ void igneum_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, IgneumInitWords iw) {
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 ^ iw.w[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ iw.w[1]; }
{ uint32_t x = nonce ^ iw.w[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ iw.w[2]; }
{ uint32_t x = nonce ^ iw.w[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ iw.w[3]; }
{ uint32_t x = nonce ^ iw.w[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ iw.w[4]; }
{ uint32_t x = nonce ^ iw.w[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ iw.w[5]; }
{ uint32_t x = nonce ^ iw.w[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ iw.w[6]; }
{ uint32_t x = nonce ^ iw.w[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ iw.w[7]; }
{ uint32_t x = nonce ^ iw.w[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ iw.w[0]; }
for (uint32_t it = 0u; it < 8u; ++it) {
uint32_t sel = r0;
r3 = rotr_var(r3, r4); // 0 rotr
r0 = r0 ^ r7; // 1 xor
{ uint32_t b_ = (r0 & mask) & ~15u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint32_t x_ = r1 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r1 = x_; } // 2 load
r5 = rotl_imm(r5, 15u); // 3 rotl
r2 = r2 + r3 + ((((sel >> 6u) & 1u) != 0u) ? 0xd35e575cu : 0xd7a264d9u); // 4 add
r5 = r5 ^ __shfl_xor_sync(0xffffffffu, r2, 1); // 5 shfl
r1 = r2 * r5 + r1; // 6 mad
r2 = r2 | r3; // 7 or
r3 = r3 ^ r5; // 8 xor
r0 = r0 + r3 + ((((sel >> 29u) & 1u) != 0u) ? 0xd98be6edu : 0x4e543e70u); // 9 add
r5 = rotr_var(r5, r1); // 10 rotr
r3 = r3 * r5; // 11 mul
r2 = r2 ^ r7; // 12 xor
r1 = r1 + r6 + ((((sel >> 30u) & 1u) != 0u) ? 0x97a9219cu : 0xe36f3c9fu); // 13 add
r2 = r2 | r1; // 14 or
r1 = r1 | r3; // 15 or
r7 = r3 * r7 + r7; // 16 mad
r3 = r3 + r2 + ((((sel >> 13u) & 1u) != 0u) ? 0xf799b153u : 0x0c0a7b49u); // 17 add
r7 = r7 ^ ds[r5 & mask]; // 18 load
r7 = r7 ^ ds[r2 & mask]; // 19 load
r5 = r5 ^ __shfl_xor_sync(0xffffffffu, r0, 8); // 20 shfl
r4 = r4 ^ ds[r1 & mask]; // 21 load
r5 = r5 ^ ds[r7 & mask]; // 22 load
r4 = r4 ^ r2; // 23 xor
r5 = r5 + r3 + ((((sel >> 14u) & 1u) != 0u) ? 0x26eb8325u : 0x011f9670u); // 24 add
r0 = r0 * r1; // 25 mul
r4 = r4 ^ r7; // 26 xor
r7 = r7 + r5 + ((((sel >> 26u) & 1u) != 0u) ? 0xf12a4057u : 0x42177757u); // 27 add
r5 = r0 * r7 + r5; // 28 mad
r3 = r3 * r6; // 29 mul
r6 = rotl_imm(r6, 24u); // 30 rotl
r4 = rotr_var(r4, r0); // 31 rotr
r6 = r6 ^ ds[r4 & mask]; // 32 load
r0 = r0 ^ __shfl_xor_sync(0xffffffffu, r1, 1); // 33 shfl
r4 = r4 * r5; // 34 mul
r2 = rotl_imm(r2, 15u); // 35 rotl
r7 = r7 + r3 + ((((sel >> 5u) & 1u) != 0u) ? 0xc1b9573fu : 0xe0ebc725u); // 36 add
{ uint32_t b_ = (r2 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r0 = x_; } // 37 load
r7 = r0 * r2 + r7; // 38 mad
r0 = r0 + r7 + ((((sel >> 16u) & 1u) != 0u) ? 0xde4cd365u : 0xd0b844deu); // 39 add
{ uint32_t b_ = (r6 & mask) & ~15u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint32_t x_ = r7 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r7 = x_; } // 40 load
r0 = r0 ^ r1; // 41 xor
r1 = rotl_imm(r1, 19u); // 42 rotl
r7 = r7 ^ r1; // 43 xor
r2 = r2 * r4; // 44 mul
{ uint32_t b_ = (r4 & mask) & ~15u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint32_t x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r6 = x_; } // 45 load
r7 = r7 + r3 + ((((sel >> 28u) & 1u) != 0u) ? 0x5d12f1a2u : 0x7633c48cu); // 46 add
r3 = r3 ^ ds[r2 & mask]; // 47 load
{ uint32_t b_ = (r5 & mask) & ~15u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint32_t x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r2 = x_; } // 48 load
r7 = r7 * r5; // 49 mul
r3 = r7 * r0 + r3; // 50 mad
{ uint32_t b_ = (r2 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r3 = x_; } // 51 load
{ uint32_t b_ = (r7 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r4 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r4 = x_; } // 52 load
r4 = __umulhi(r4, r1); // 53 mulhi
r7 = r7 + r4 + ((((sel >> 24u) & 1u) != 0u) ? 0xd8ed09bau : 0xf64a6e41u); // 54 add
r5 = __umulhi(r5, r3); // 55 mulhi
r5 = r5 * r7; // 56 mul
{ uint32_t b_ = (r0 & mask) & ~15u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint32_t x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r3 = x_; } // 57 load
r5 = __umulhi(r5, r2); // 58 mulhi
r7 = r7 * r5; // 59 mul
{ uint32_t b_ = (r3 & mask) & ~15u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint32_t x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r6 = x_; } // 60 load
r7 = r1 * r1 + r7; // 61 mad
r0 = r0 ^ ds[r7 & mask]; // 62 load
r5 = r5 * r3; // 63 mul
}
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;
}
cudaError_t igneum_launch_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask,
IgneumInitWords iw, 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_bound<<<nonces / block, block>>>(ds, out, baseNonce, mask, iw);
return cudaGetLastError();
}
cudaError_t igneum_hash_bound_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps) {
cudaFuncAttributes attr;
cudaError_t e = cudaFuncGetAttributes(&attr, igneum_hash_bound);
if (e != cudaSuccess) return e;
*numRegs = attr.numRegs;
return cudaOccupancyMaxActiveBlocksPerMultiprocessor(blocksPerSM, igneum_hash_bound, (int)(32u * blockWarps), 0);
}

View file

@ -0,0 +1,108 @@
// Generated by igneum-pow export (generator v2) for seed "igneum-readwidth/A/3". Do not edit by hand.
// Memory-hard dataset core, the same text that the Mac's Metal kernels and CPU verifier were checked against.
// Included by kernel.cu (device), host.cu (host reference) and proto-opencl/host.c (C99 host reference).
// See proto-metal/MEMHARD.md for the construction. kernel.cl carries the same text in OpenCL C.
#pragma once
#ifdef __cplusplus
#include <cstdint>
#else
#include <stdint.h>
#endif
#if defined(__CUDACC__)
#define IGNEUM_HD __host__ __device__ __forceinline__
#elif defined(_MSC_VER) && !defined(__cplusplus)
#define IGNEUM_HD static __inline
#else
#define IGNEUM_HD static inline
#endif
// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines.
// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals.
#define MH_CACHE_LINE_MASK 0x003fffffu
#define MH_SEGMENT_LINES 64u
#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); }
IGNEUM_HD uint32_t mh_rotl(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site
// y = ChaCha12 core(x) + x
IGNEUM_HD void mh_chacha_block(const uint32_t* x, uint32_t* y) {
for (uint32_t i = 0u; i < 16u; ++i) y[i] = x[i];
for (uint32_t r = 0u; r < 6u; ++r) {
MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u)
MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u)
}
for (uint32_t i = 0u; i < 16u; ++i) y[i] += x[i];
}
// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0.
IGNEUM_HD void mh_cache_segment(uint32_t* cache, uint32_t seg) {
uint32_t prev[16]; uint32_t x[16]; uint32_t y[16];
for (uint32_t i = 0u; i < 16u; ++i) prev[i] = 0u;
for (uint32_t j = 0u; j < MH_SEGMENT_LINES; ++j) {
x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3];
x[4] = 0x3067619fu ^ prev[4];
x[5] = 0x3c269176u ^ prev[5];
x[6] = 0x84a03b03u ^ prev[6];
x[7] = 0xf8c63294u ^ prev[7];
x[8] = 0xff977c5bu ^ prev[8];
x[9] = 0xe60def3eu ^ prev[9];
x[10] = 0x63630141u ^ prev[10];
x[11] = 0xb8fbcb58u ^ prev[11];
x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15];
mh_chacha_block(x, y);
uint32_t* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u);
for (uint32_t i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; }
}
}
// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations.
IGNEUM_HD void mh_mixer(uint32_t* s, uint32_t rk) {
s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u;
s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu;
s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u;
s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu;
s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u;
s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u;
s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u;
s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u;
s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du;
s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u;
s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du;
s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu;
s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du;
s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu;
s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u;
s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u;
MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u)
MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u)
MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u)
MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u)
}
// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer.
IGNEUM_HD void mh_item(const uint32_t* cache, uint32_t t, uint32_t* s) {
s[0] = 0x3067619fu;
s[1] = 0x3c269176u;
s[2] = 0x84a03b03u;
s[3] = 0xf8c63294u;
s[4] = 0xff977c5bu;
s[5] = 0xe60def3eu;
s[6] = 0x63630141u;
s[7] = 0xb8fbcb58u;
s[8] = t * 0x42146205u + 0xbab68293u;
s[9] = t * 0x52cbe0fbu + 0xcc162340u;
s[10] = t * 0x7ecf4a03u + 0x6ce151ccu;
s[11] = t * 0x6728907fu + 0xe62b8997u;
s[12] = t * 0xd81d9751u + 0xc9c80297u;
s[13] = t * 0x132952c3u + 0xf74a1654u;
s[14] = t * 0xf60de277u + 0x3d704af5u;
s[15] = t * 0x05358035u + 0x3cf522b7u;
for (uint32_t r = 0u; r < 8u; ++r) {
mh_mixer(s, 0x9E3779B9u * (r + 1u));
const uint32_t* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u);
for (uint32_t i = 0u; i < 16u; ++i) s[i] ^= line[i];
}
mh_mixer(s, 0x9E3779B9u * 9u);
}
// dataset[w] without the dataset: derive item w >> 4 and take word w & 15.
IGNEUM_HD uint32_t mh_word(const uint32_t* cache, uint32_t w) { uint32_t s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; }

View file

@ -0,0 +1,106 @@
#include <metal_stdlib>
using namespace metal;
// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines.
// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals.
#define MH_CACHE_LINE_MASK 0x003fffffu
#define MH_SEGMENT_LINES 64u
#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); }
inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site
// y = ChaCha12 core(x) + x
inline void mh_chacha_block(const thread uint* x, thread uint* y) {
for (uint i = 0u; i < 16u; ++i) y[i] = x[i];
for (uint r = 0u; r < 6u; ++r) {
MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u)
MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u)
}
for (uint i = 0u; i < 16u; ++i) y[i] += x[i];
}
// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0.
inline void mh_cache_segment(device uint* cache, uint seg) {
uint prev[16]; uint x[16]; uint y[16];
for (uint i = 0u; i < 16u; ++i) prev[i] = 0u;
for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) {
x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3];
x[4] = 0x3067619fu ^ prev[4];
x[5] = 0x3c269176u ^ prev[5];
x[6] = 0x84a03b03u ^ prev[6];
x[7] = 0xf8c63294u ^ prev[7];
x[8] = 0xff977c5bu ^ prev[8];
x[9] = 0xe60def3eu ^ prev[9];
x[10] = 0x63630141u ^ prev[10];
x[11] = 0xb8fbcb58u ^ prev[11];
x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15];
mh_chacha_block(x, y);
device uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u);
for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; }
}
}
// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations.
inline void mh_mixer(thread uint* s, uint rk) {
s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u;
s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu;
s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u;
s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu;
s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u;
s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u;
s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u;
s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u;
s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du;
s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u;
s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du;
s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu;
s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du;
s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu;
s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u;
s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u;
MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u)
MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u)
MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u)
MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u)
}
// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer.
inline void mh_item(device const uint* cache, uint t, thread uint* s) {
s[0] = 0x3067619fu;
s[1] = 0x3c269176u;
s[2] = 0x84a03b03u;
s[3] = 0xf8c63294u;
s[4] = 0xff977c5bu;
s[5] = 0xe60def3eu;
s[6] = 0x63630141u;
s[7] = 0xb8fbcb58u;
s[8] = t * 0x42146205u + 0xbab68293u;
s[9] = t * 0x52cbe0fbu + 0xcc162340u;
s[10] = t * 0x7ecf4a03u + 0x6ce151ccu;
s[11] = t * 0x6728907fu + 0xe62b8997u;
s[12] = t * 0xd81d9751u + 0xc9c80297u;
s[13] = t * 0x132952c3u + 0xf74a1654u;
s[14] = t * 0xf60de277u + 0x3d704af5u;
s[15] = t * 0x05358035u + 0x3cf522b7u;
for (uint r = 0u; r < 8u; ++r) {
mh_mixer(s, 0x9E3779B9u * (r + 1u));
device const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u);
for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i];
}
mh_mixer(s, 0x9E3779B9u * 9u);
}
// dataset[w] without the dataset: derive item w >> 4 and take word w & 15.
inline uint mh_word(device const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; }
// One thread per segment (2^16 threads).
kernel void igneum_cache_fill(device uint* cache [[buffer(0)]], uint gid [[thread_position_in_grid]]) {
mh_cache_segment(cache, gid);
}
// One thread per 64-byte item (dataset words / 16 threads).
kernel void igneum_build(device const uint* cache [[buffer(0)]], device uint* dataset [[buffer(1)]],
uint gid [[thread_position_in_grid]]) {
uint s[16];
mh_item(cache, gid, s);
device uint* d = dataset + gid * 16u;
for (uint i = 0u; i < 16u; ++i) d[i] = s[i];
}

View file

@ -0,0 +1,60 @@
// Generated by igneum-pow export (generator v2) for seed "igneum-readwidth/A/3". Do not edit by hand.
// Program metadata for host.cu plus the launch wrappers defined in kernel.cu.
// Also included by proto-opencl/host.c (C99), which defines IGNEUM_NO_CUDA first and reads only the macros.
#pragma once
#ifdef __cplusplus
#include <cstdint>
#else
#include <stdint.h>
#endif
#ifndef IGNEUM_NO_CUDA
#include <cuda_runtime.h>
#endif
#define IGNEUM_SEED_STRING "igneum-readwidth/A/3"
#define IGNEUM_SEED_BYTES_HEX "69676e65756d2d7265616477696474682f412f33"
#define IGNEUM_GENERATOR 2
#define IGNEUM_PROGRAM_ATTEMPT 0
#define IGNEUM_PROGRAM_ID 0x67648c3193a33f6cull
#define IGNEUM_DAY_STRING "2026-10-03"
#define IGNEUM_DAY_BYTES_HEX "6461792f323032362d31302d3033"
#define IGNEUM_DAY0 0x3067619fu
#define IGNEUM_DAY1 0x3c269176u
#define IGNEUM_DATASET_LOG2 28
#define IGNEUM_MASK 0x0fffffffu
#define IGNEUM_LANES 32
#define IGNEUM_ITERATIONS 8
#define IGNEUM_INSTR_COUNT 64
#define IGNEUM_LOADS_PER_HASH 128
#define IGNEUM_WIDE_LOADS_PER_HASH 0
#define IGNEUM_OP_MIX "load=16 add=10 mul=9 xor=7 mad=6 rotl=4 mulhi=3 or=3 rotr=3 shfl=3"
// Read-width experiment (5 October 2026, docs/plans/read-width.md): NOT the lottery hash. A load of W words reads
// the W-word-aligned address and folds every word into dst: x = dst ^ w[0]; x = (rotl(x, 11) * 0x9e3779b1) ^ w[j]; dst = x.
#define IGNEUM_LOAD_CLASS "mix50-35-15"
#define IGNEUM_LOAD_SLOTS 16
#define IGNEUM_LOAD_MIX { 50, 35, 15 }
#define IGNEUM_LOAD_WIDTH_COUNTS { 7, 3, 6 } // loads of 4, 16, 64 bytes per program
#define IGNEUM_BYTES_PER_HASH 3680
#define IGNEUM_FOLD_ROT 11
#define IGNEUM_FOLD_MUL 0x9e3779b1u
// 0 = closed-form dataset (ds_elem), 1 = memory-hard cache construction (MEMHARD.md, memhard.h)
#define IGNEUM_DATASET_MODE 1
#define IGNEUM_SEEDW_INIT { 0x30b957f0u, 0x374e2a95u, 0xf416345eu, 0x7af15ccbu, 0xf0bcabc5u, 0xb5b36f35u, 0xf99641c7u, 0xd0312afeu }
#define IGNEUM_KEY_INIT { 0x3067619fu, 0x3c269176u, 0x84a03b03u, 0xf8c63294u, 0xff977c5bu, 0xe60def3eu, 0x63630141u, 0xb8fbcb58u }
#define IGNEUM_CACHE_LOG2_WORDS 26
#define IGNEUM_CACHE_SEGMENT_LOG2_LINES 6
#define IGNEUM_CACHE_SEGMENTS 65536u
#define IGNEUM_ITEM_ROUNDS 8
#define IGNEUM_MIX_ROT_INIT { 20u, 20u, 19u, 4u, 26u, 3u, 3u, 27u }
#define IGNEUM_MIX_MUL_INIT { 0x42146205u, 0x52cbe0fbu, 0x7ecf4a03u, 0x6728907fu, 0xd81d9751u, 0x132952c3u, 0xf60de277u, 0x05358035u, 0xbaf6499du, 0xe4db9667u, 0x3e98f45du, 0xd0004eddu, 0x2691630du, 0x9beb3bcfu, 0xab310379u, 0x99cfb423u }
#define IGNEUM_MIX_RC_INIT { 0xbab68293u, 0xcc162340u, 0x6ce151ccu, 0xe62b8997u, 0xc9c80297u, 0xf74a1654u, 0x3d704af5u, 0x3cf522b7u, 0x2b9cac04u, 0xa880ac10u, 0x13e5dd1du, 0x6fc3e233u, 0x2d83eeacu, 0x9006e8bfu, 0x2c4b5362u, 0x31b49ee2u }
#ifndef IGNEUM_NO_CUDA
// Defined in kernel.cu. All launch on the default stream and return cudaGetLastError().
cudaError_t igneum_launch_cache_fill(uint32_t* cache, uint32_t nSegments);
cudaError_t igneum_launch_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems);
cudaError_t igneum_launch_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask,
uint32_t nonces, uint32_t blockWarps);
cudaError_t igneum_hash_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps);
#endif

View file

@ -0,0 +1,127 @@
{
"format": "igneum-program-pack-3",
"generator": 2,
"attempt": 0,
"program_id": "0x67648c3193a33f6c",
"program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32",
"dataset_mode": "memory-hard",
"seed": "igneum-readwidth/A/3",
"seed_bytes": "69676e65756d2d7265616477696474682f412f33",
"seed_words": ["0x30b957f0", "0x374e2a95", "0xf416345e", "0x7af15ccb", "0xf0bcabc5", "0xb5b36f35", "0xf99641c7", "0xd0312afe"],
"seed_derivation": "seed_words = FNV-1a 64 over seed_bytes (attempt 0) or seed_bytes || attempt_le32 (attempt k >= 1), basis ^ (salt * 0x9E3779B97F4A7C15) for salt 0..3, then h ^= h>>33; h *= 0xff51afd7ed558ccd; h ^= h>>33; words[2*salt] = low 32, words[2*salt+1] = high 32",
"generator_rule": "version 2: exactly 16 load slots drawn first from instructions 1..63 (partial Fisher-Yates), the other 48 ops from the ten non-load weights (sum 75); a load's source is drawn from the registers other than dst written by an earlier instruction and not read by a load since; the candidate must pass the acceptance rule of spec 01 section 1.4.6 (static: no cyclically stale load source, every register has an injecting write; dynamic: 64 units on the seed-keyed closed-form dataset with no constant register bit, no lane-constant load site, under 164 saturated final values, every output bit within 136 of 1024, distinct addresses above 245760), else the next attempt of the seed is tried",
"lanes": 32,
"registers": 8,
"iterations": 8,
"instruction_count": 64,
"loads_per_hash": 128,
"load_class": "mix50-35-15",
"load_slots": 16,
"load_mix_percent_4_16_64": [50, 35, 15],
"load_width_counts_4_16_64": [7, 3, 6],
"bytes_per_hash": 3680,
"wide_load": "read-width experiment (5 October 2026, docs/plans/read-width.md), NOT the lottery hash: a load of W words (width field, 4 or 16) reads dataset[b .. b + W) with b = (src & mask) & ~(W - 1) and folds every word into dst: x = dst ^ w[0]; for j in 1..W: x = (rotl(x, 11) * 0x9e3779b1) ^ w[j]; dst = x; width 1 is the plain load; the width is drawn per instruction from the class mix with one extra below(100) draw after the nine of version 2, and the program id is FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed words || attempt_le32 || mix[3] || load_slots",
"op_mix": {"load": 16, "add": 10, "mul": 9, "xor": 7, "mad": 6, "rotl": 4, "mulhi": 3, "or": 3, "rotr": 3, "shfl": 3},
"register_init": "for i in 0..7: x = nonce ^ seed_words[i]; x += 0x9e3779b9 * (i+1) (mod 2^32); x = splitmix32(x); r[i] = x ^ seed_words[(i+1) & 7]",
"splitmix32": "x ^= x>>16; x *= 0x7feb352d; x ^= x>>15; x *= 0x846ca68b; x ^= x>>16",
"iteration": "sel = r0 sampled once at the top of each iteration, then all instructions in order",
"output": "lo = r0 ^ rotl(r1,7) ^ rotl(r2,14) ^ rotl(r3,21); hi = r4 ^ rotl(r5,9) ^ rotl(r6,18) ^ rotl(r7,27); out = (hi << 32) | lo",
"op_semantics": {
"add": "dst = dst + src + (bit `bit` of sel ? imm2 : imm)",
"sub": "dst = dst - src",
"mul": "dst = dst * src (low 32)",
"mulhi": "dst = high 32 bits of dst * src",
"xor": "dst = dst ^ src",
"or": "dst = dst | src",
"rotl": "dst = rotl(dst, rot), rot in 1..31",
"rotr": "dst = rotr(dst, src & 31)",
"mad": "dst = src * src2 + dst",
"shfl": "dst = dst ^ (src of lane (lane ^ mask)), mask in {1,2,4,8,16}, within the 32-lane warp",
"load": "dst = dst ^ dataset[src & dataset.mask]",
"wload": "base = (src of lane 0 & dataset.mask) & ~31; dst = dst ^ dataset[base + lane] (warp-coalesced 128-byte load, lever b, only when --wide-frac > 0)"
},
"dataset": {
"log2_words": 28,
"bytes": 1073741824,
"mask": "0x0fffffff",
"day": "2026-10-03",
"day_bytes": "6461792f323032362d31302d3033",
"day_words_from": "seed_words_from_bytes(day_bytes)",
"d0": "0x3067619f",
"d1": "0x3c269176",
"mode": "memory-hard",
"spec": "proto-metal/MEMHARD.md",
"key": ["0x3067619f", "0x3c269176", "0x84a03b03", "0xf8c63294", "0xff977c5b", "0xe60def3e", "0x63630141", "0xb8fbcb58"],
"key_derivation": "the 8 words of seed_words_from_bytes(day_bytes); d0, d1 are key[0], key[1]",
"cache": {"log2_words": 26, "bytes": 268435456, "line_words": 16, "segment_lines": 64, "segments": 65536, "block": "ChaCha12 core + feed-forward, rotations 16 12 8 7", "sigma": ["0x61707865", "0x3320646e", "0x79622d32", "0x6b206574"], "tag": ["0x49676e65", "0x756d4d48"], "chain": "in_j = prev_line ^ (sigma[0..3] || key[0..7] || seg || j || tag[0..1]); line_j = block(in_j); prev_0 = 0"},
"mixer": {"draw": "SplitMix64 seeded with key[0] | key[1] << 32: rot[0..7] = 1 + next() % 31, mul[0..15] = low32(next()) | 1, rc[0..15] = low32(next())", "rot": [20, 20, 19, 4, 26, 3, 3, 27], "mul": ["0x42146205", "0x52cbe0fb", "0x7ecf4a03", "0x6728907f", "0xd81d9751", "0x132952c3", "0xf60de277", "0x05358035", "0xbaf6499d", "0xe4db9667", "0x3e98f45d", "0xd0004edd", "0x2691630d", "0x9beb3bcf", "0xab310379", "0x99cfb423"], "rc": ["0xbab68293", "0xcc162340", "0x6ce151cc", "0xe62b8997", "0xc9c80297", "0xf74a1654", "0x3d704af5", "0x3cf522b7", "0x2b9cac04", "0xa880ac10", "0x13e5dd1d", "0x6fc3e233", "0x2d83eeac", "0x9006e8bf", "0x2c4b5362", "0x31b49ee2"], "round": "for i in 0..15: s[i] = (s[i] ^ (rc[i] + (r+1) * 0x9E3779B9)) * mul[i]; then quarter rounds on columns (0,4,8,12) (1,5,9,13) (2,6,10,14) (3,7,11,15) with rot[0..3] and diagonals (0,5,10,15) (1,6,11,12) (2,7,8,13) (3,4,9,14) with rot[4..7]", "quarter_round": "a += b; d ^= a; d = rotl(d, r1); c += d; b ^= c; b = rotl(b, r2); a += b; d ^= a; d = rotl(d, r3); c += d; b ^= c; b = rotl(b, r4)"},
"item": "s[0..7] = key; s[8+i] = t * mul[i] + rc[i] for i in 0..7; for r in 0..7: s = M_r(s); line = s[0] & 0x003fffff; s[i] ^= cache[line * 16 + i]; then s = M_8(s); item(t) = s",
"word": "dataset[w] = item(w >> 4)[w & 15]"
},
"instructions": [
{"i": 0, "op": "rotr", "dst": 3, "src": 4, "src2": 3, "imm": "0x33f6fe7b", "imm2": "0xd46e1ac2", "rot": 21, "bit": 9, "mask": 8, "width": 1},
{"i": 1, "op": "xor", "dst": 0, "src": 7, "src2": 4, "imm": "0x12e86486", "imm2": "0x1e848be1", "rot": 3, "bit": 7, "mask": 4, "width": 1},
{"i": 2, "op": "load", "dst": 1, "src": 0, "src2": 7, "imm": "0x25bf1dfe", "imm2": "0x85cff09c", "rot": 27, "bit": 24, "mask": 4, "width": 16},
{"i": 3, "op": "rotl", "dst": 5, "src": 3, "src2": 3, "imm": "0xbfde1d4f", "imm2": "0x098e8aab", "rot": 15, "bit": 30, "mask": 4, "width": 1},
{"i": 4, "op": "add", "dst": 2, "src": 3, "src2": 6, "imm": "0xd7a264d9", "imm2": "0xd35e575c", "rot": 7, "bit": 6, "mask": 4, "width": 1},
{"i": 5, "op": "shfl", "dst": 5, "src": 2, "src2": 4, "imm": "0x5d5210ad", "imm2": "0xd984eb95", "rot": 27, "bit": 13, "mask": 1, "width": 1},
{"i": 6, "op": "mad", "dst": 1, "src": 2, "src2": 5, "imm": "0xb969487c", "imm2": "0x61ea012e", "rot": 26, "bit": 25, "mask": 1, "width": 1},
{"i": 7, "op": "or", "dst": 2, "src": 3, "src2": 4, "imm": "0xc1c5d61e", "imm2": "0x50d4e85c", "rot": 19, "bit": 25, "mask": 4, "width": 1},
{"i": 8, "op": "xor", "dst": 3, "src": 5, "src2": 2, "imm": "0x33a2e407", "imm2": "0x4c9951f2", "rot": 2, "bit": 24, "mask": 16, "width": 1},
{"i": 9, "op": "add", "dst": 0, "src": 3, "src2": 0, "imm": "0x4e543e70", "imm2": "0xd98be6ed", "rot": 4, "bit": 29, "mask": 1, "width": 1},
{"i": 10, "op": "rotr", "dst": 5, "src": 1, "src2": 0, "imm": "0x76afe302", "imm2": "0x9eca97e5", "rot": 3, "bit": 20, "mask": 8, "width": 1},
{"i": 11, "op": "mul", "dst": 3, "src": 5, "src2": 3, "imm": "0xbfb4e238", "imm2": "0x2746818f", "rot": 9, "bit": 25, "mask": 8, "width": 1},
{"i": 12, "op": "xor", "dst": 2, "src": 7, "src2": 3, "imm": "0x29d5f28f", "imm2": "0xab4d8139", "rot": 10, "bit": 4, "mask": 1, "width": 1},
{"i": 13, "op": "add", "dst": 1, "src": 6, "src2": 1, "imm": "0xe36f3c9f", "imm2": "0x97a9219c", "rot": 30, "bit": 30, "mask": 2, "width": 1},
{"i": 14, "op": "or", "dst": 2, "src": 1, "src2": 0, "imm": "0x3c03e7d3", "imm2": "0xd16859dd", "rot": 15, "bit": 28, "mask": 2, "width": 1},
{"i": 15, "op": "or", "dst": 1, "src": 3, "src2": 0, "imm": "0x38a5677d", "imm2": "0xa67fd283", "rot": 29, "bit": 15, "mask": 2, "width": 1},
{"i": 16, "op": "mad", "dst": 7, "src": 3, "src2": 7, "imm": "0x78f5e68e", "imm2": "0x9d9644ec", "rot": 25, "bit": 1, "mask": 2, "width": 1},
{"i": 17, "op": "add", "dst": 3, "src": 2, "src2": 0, "imm": "0x0c0a7b49", "imm2": "0xf799b153", "rot": 22, "bit": 13, "mask": 8, "width": 1},
{"i": 18, "op": "load", "dst": 7, "src": 5, "src2": 6, "imm": "0xf2026981", "imm2": "0x15d47d37", "rot": 28, "bit": 27, "mask": 2, "width": 1},
{"i": 19, "op": "load", "dst": 7, "src": 2, "src2": 4, "imm": "0x9d237b38", "imm2": "0x527aa464", "rot": 5, "bit": 22, "mask": 2, "width": 1},
{"i": 20, "op": "shfl", "dst": 5, "src": 0, "src2": 0, "imm": "0x102562b7", "imm2": "0xe66dad9b", "rot": 9, "bit": 22, "mask": 8, "width": 1},
{"i": 21, "op": "load", "dst": 4, "src": 1, "src2": 5, "imm": "0xe8365586", "imm2": "0x6f13fc63", "rot": 26, "bit": 6, "mask": 8, "width": 1},
{"i": 22, "op": "load", "dst": 5, "src": 7, "src2": 2, "imm": "0xacd9154f", "imm2": "0x785e81e4", "rot": 22, "bit": 29, "mask": 1, "width": 1},
{"i": 23, "op": "xor", "dst": 4, "src": 2, "src2": 6, "imm": "0x03eb8f2d", "imm2": "0x2f686222", "rot": 30, "bit": 30, "mask": 2, "width": 1},
{"i": 24, "op": "add", "dst": 5, "src": 3, "src2": 3, "imm": "0x011f9670", "imm2": "0x26eb8325", "rot": 15, "bit": 14, "mask": 8, "width": 1},
{"i": 25, "op": "mul", "dst": 0, "src": 1, "src2": 0, "imm": "0x0cacc6a3", "imm2": "0x28bca959", "rot": 8, "bit": 19, "mask": 2, "width": 1},
{"i": 26, "op": "xor", "dst": 4, "src": 7, "src2": 7, "imm": "0x60fc8d5f", "imm2": "0xb8453fd3", "rot": 22, "bit": 3, "mask": 8, "width": 1},
{"i": 27, "op": "add", "dst": 7, "src": 5, "src2": 6, "imm": "0x42177757", "imm2": "0xf12a4057", "rot": 15, "bit": 26, "mask": 16, "width": 1},
{"i": 28, "op": "mad", "dst": 5, "src": 0, "src2": 7, "imm": "0x0f21b242", "imm2": "0x2fa77c71", "rot": 30, "bit": 20, "mask": 1, "width": 1},
{"i": 29, "op": "mul", "dst": 3, "src": 6, "src2": 0, "imm": "0x69e62f8d", "imm2": "0x0037ce73", "rot": 18, "bit": 12, "mask": 2, "width": 1},
{"i": 30, "op": "rotl", "dst": 6, "src": 7, "src2": 6, "imm": "0xb259e02f", "imm2": "0x0a33cfdf", "rot": 24, "bit": 12, "mask": 4, "width": 1},
{"i": 31, "op": "rotr", "dst": 4, "src": 0, "src2": 6, "imm": "0xa8839588", "imm2": "0xd5af17af", "rot": 18, "bit": 19, "mask": 1, "width": 1},
{"i": 32, "op": "load", "dst": 6, "src": 4, "src2": 4, "imm": "0xd7e19199", "imm2": "0xf45eb79a", "rot": 24, "bit": 11, "mask": 2, "width": 1},
{"i": 33, "op": "shfl", "dst": 0, "src": 1, "src2": 5, "imm": "0x3e9f25b5", "imm2": "0x405b0189", "rot": 9, "bit": 30, "mask": 1, "width": 1},
{"i": 34, "op": "mul", "dst": 4, "src": 5, "src2": 2, "imm": "0x42ed1682", "imm2": "0xdd577015", "rot": 27, "bit": 2, "mask": 8, "width": 1},
{"i": 35, "op": "rotl", "dst": 2, "src": 4, "src2": 0, "imm": "0x39954523", "imm2": "0x9d5b1079", "rot": 15, "bit": 13, "mask": 16, "width": 1},
{"i": 36, "op": "add", "dst": 7, "src": 3, "src2": 0, "imm": "0xe0ebc725", "imm2": "0xc1b9573f", "rot": 14, "bit": 5, "mask": 2, "width": 1},
{"i": 37, "op": "load", "dst": 0, "src": 2, "src2": 5, "imm": "0x921d347e", "imm2": "0x18b83d03", "rot": 27, "bit": 7, "mask": 4, "width": 4},
{"i": 38, "op": "mad", "dst": 7, "src": 0, "src2": 2, "imm": "0x211c8e0d", "imm2": "0x963c2eec", "rot": 6, "bit": 23, "mask": 16, "width": 1},
{"i": 39, "op": "add", "dst": 0, "src": 7, "src2": 2, "imm": "0xd0b844de", "imm2": "0xde4cd365", "rot": 27, "bit": 16, "mask": 4, "width": 1},
{"i": 40, "op": "load", "dst": 7, "src": 6, "src2": 6, "imm": "0x3b8d9718", "imm2": "0x8ab1654a", "rot": 7, "bit": 16, "mask": 16, "width": 16},
{"i": 41, "op": "xor", "dst": 0, "src": 1, "src2": 2, "imm": "0x014f5a59", "imm2": "0xc8398216", "rot": 2, "bit": 17, "mask": 2, "width": 1},
{"i": 42, "op": "rotl", "dst": 1, "src": 5, "src2": 1, "imm": "0x3bee3d81", "imm2": "0x2ce34bcd", "rot": 19, "bit": 6, "mask": 1, "width": 1},
{"i": 43, "op": "xor", "dst": 7, "src": 1, "src2": 1, "imm": "0x27aaf2ee", "imm2": "0xec39fef7", "rot": 9, "bit": 18, "mask": 8, "width": 1},
{"i": 44, "op": "mul", "dst": 2, "src": 4, "src2": 3, "imm": "0x1b770975", "imm2": "0xa556f55d", "rot": 4, "bit": 23, "mask": 1, "width": 1},
{"i": 45, "op": "load", "dst": 6, "src": 4, "src2": 5, "imm": "0xe6ebfbe4", "imm2": "0x0a9cc201", "rot": 2, "bit": 5, "mask": 4, "width": 16},
{"i": 46, "op": "add", "dst": 7, "src": 3, "src2": 7, "imm": "0x7633c48c", "imm2": "0x5d12f1a2", "rot": 10, "bit": 28, "mask": 1, "width": 1},
{"i": 47, "op": "load", "dst": 3, "src": 2, "src2": 5, "imm": "0xe92f250d", "imm2": "0x626fb82e", "rot": 27, "bit": 8, "mask": 1, "width": 1},
{"i": 48, "op": "load", "dst": 2, "src": 5, "src2": 5, "imm": "0x32ec784c", "imm2": "0x5c40c45a", "rot": 23, "bit": 0, "mask": 4, "width": 16},
{"i": 49, "op": "mul", "dst": 7, "src": 5, "src2": 0, "imm": "0x68a298b5", "imm2": "0xded99974", "rot": 31, "bit": 22, "mask": 8, "width": 1},
{"i": 50, "op": "mad", "dst": 3, "src": 7, "src2": 0, "imm": "0xf89a028b", "imm2": "0x8ece86f6", "rot": 24, "bit": 20, "mask": 2, "width": 1},
{"i": 51, "op": "load", "dst": 3, "src": 2, "src2": 7, "imm": "0xe1a0d923", "imm2": "0xdb8faa0b", "rot": 15, "bit": 23, "mask": 8, "width": 4},
{"i": 52, "op": "load", "dst": 4, "src": 7, "src2": 6, "imm": "0x28e50e93", "imm2": "0x965bcc9c", "rot": 25, "bit": 28, "mask": 4, "width": 4},
{"i": 53, "op": "mulhi", "dst": 4, "src": 1, "src2": 2, "imm": "0x0065ecaf", "imm2": "0xe870289e", "rot": 8, "bit": 18, "mask": 4, "width": 1},
{"i": 54, "op": "add", "dst": 7, "src": 4, "src2": 3, "imm": "0xf64a6e41", "imm2": "0xd8ed09ba", "rot": 14, "bit": 24, "mask": 16, "width": 1},
{"i": 55, "op": "mulhi", "dst": 5, "src": 3, "src2": 1, "imm": "0xd12951e6", "imm2": "0xe3ae79fe", "rot": 2, "bit": 0, "mask": 2, "width": 1},
{"i": 56, "op": "mul", "dst": 5, "src": 7, "src2": 4, "imm": "0xac8f65d4", "imm2": "0x11401bd1", "rot": 22, "bit": 6, "mask": 2, "width": 1},
{"i": 57, "op": "load", "dst": 3, "src": 0, "src2": 1, "imm": "0x2815f83f", "imm2": "0x34b3e25b", "rot": 7, "bit": 27, "mask": 2, "width": 16},
{"i": 58, "op": "mulhi", "dst": 5, "src": 2, "src2": 7, "imm": "0xbc696afd", "imm2": "0x488bfc7d", "rot": 24, "bit": 31, "mask": 4, "width": 1},
{"i": 59, "op": "mul", "dst": 7, "src": 5, "src2": 7, "imm": "0x0f1bd7bd", "imm2": "0xf3a4c1c8", "rot": 25, "bit": 10, "mask": 4, "width": 1},
{"i": 60, "op": "load", "dst": 6, "src": 3, "src2": 1, "imm": "0x2ebad44e", "imm2": "0x6b6fcf95", "rot": 4, "bit": 16, "mask": 8, "width": 16},
{"i": 61, "op": "mad", "dst": 7, "src": 1, "src2": 1, "imm": "0xd9791f15", "imm2": "0xdd0cbcb2", "rot": 8, "bit": 14, "mask": 16, "width": 1},
{"i": 62, "op": "load", "dst": 0, "src": 7, "src2": 1, "imm": "0x057ced89", "imm2": "0x08fdfbc1", "rot": 20, "bit": 15, "mask": 16, "width": 1},
{"i": 63, "op": "mul", "dst": 5, "src": 3, "src2": 4, "imm": "0xbd9f5fdd", "imm2": "0xac802ec9", "rot": 27, "bit": 11, "mask": 2, "width": 1}
]
}

View file

@ -0,0 +1,109 @@
#include <metal_stdlib>
using namespace metal;
#define MASK 0x0fffffffu
constant uint SEEDW[8] = { 0x30b957f0u, 0x374e2a95u, 0xf416345eu, 0x7af15ccbu, 0xf0bcabc5u, 0xb5b36f35u, 0xf99641c7u, 0xd0312afeu };
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;
}
kernel void igneum_hash(device const uint* dataset [[buffer(0)]],
device ulong* out [[buffer(1)]],
constant uint& baseNonce [[buffer(2)]],
uint gid [[thread_position_in_grid]]) {
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;
r3 = rotr_var(r3, r4); // 0
r0 = r0 ^ r7; // 1
{ uint b_ = (r0 & MASK) & ~15u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint x_ = r1 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r1 = x_; } // 2
r5 = rotl_imm(r5, 15u); // 3
r2 = r2 + r3 + select(0xd7a264d9u, 0xd35e575cu, ((sel >> 6u) & 1u) != 0u); // 4
r5 = r5 ^ simd_shuffle_xor(r2, (ushort)1); // 5
r1 = r2 * r5 + r1; // 6
r2 = r2 | r3; // 7
r3 = r3 ^ r5; // 8
r0 = r0 + r3 + select(0x4e543e70u, 0xd98be6edu, ((sel >> 29u) & 1u) != 0u); // 9
r5 = rotr_var(r5, r1); // 10
r3 = r3 * r5; // 11
r2 = r2 ^ r7; // 12
r1 = r1 + r6 + select(0xe36f3c9fu, 0x97a9219cu, ((sel >> 30u) & 1u) != 0u); // 13
r2 = r2 | r1; // 14
r1 = r1 | r3; // 15
r7 = r3 * r7 + r7; // 16
r3 = r3 + r2 + select(0x0c0a7b49u, 0xf799b153u, ((sel >> 13u) & 1u) != 0u); // 17
r7 = r7 ^ dataset[r5 & MASK]; // 18
r7 = r7 ^ dataset[r2 & MASK]; // 19
r5 = r5 ^ simd_shuffle_xor(r0, (ushort)8); // 20
r4 = r4 ^ dataset[r1 & MASK]; // 21
r5 = r5 ^ dataset[r7 & MASK]; // 22
r4 = r4 ^ r2; // 23
r5 = r5 + r3 + select(0x011f9670u, 0x26eb8325u, ((sel >> 14u) & 1u) != 0u); // 24
r0 = r0 * r1; // 25
r4 = r4 ^ r7; // 26
r7 = r7 + r5 + select(0x42177757u, 0xf12a4057u, ((sel >> 26u) & 1u) != 0u); // 27
r5 = r0 * r7 + r5; // 28
r3 = r3 * r6; // 29
r6 = rotl_imm(r6, 24u); // 30
r4 = rotr_var(r4, r0); // 31
r6 = r6 ^ dataset[r4 & MASK]; // 32
r0 = r0 ^ simd_shuffle_xor(r1, (ushort)1); // 33
r4 = r4 * r5; // 34
r2 = rotl_imm(r2, 15u); // 35
r7 = r7 + r3 + select(0xe0ebc725u, 0xc1b9573fu, ((sel >> 5u) & 1u) != 0u); // 36
{ uint b_ = (r2 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r0 = x_; } // 37
r7 = r0 * r2 + r7; // 38
r0 = r0 + r7 + select(0xd0b844deu, 0xde4cd365u, ((sel >> 16u) & 1u) != 0u); // 39
{ uint b_ = (r6 & MASK) & ~15u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint x_ = r7 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r7 = x_; } // 40
r0 = r0 ^ r1; // 41
r1 = rotl_imm(r1, 19u); // 42
r7 = r7 ^ r1; // 43
r2 = r2 * r4; // 44
{ uint b_ = (r4 & MASK) & ~15u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r6 = x_; } // 45
r7 = r7 + r3 + select(0x7633c48cu, 0x5d12f1a2u, ((sel >> 28u) & 1u) != 0u); // 46
r3 = r3 ^ dataset[r2 & MASK]; // 47
{ uint b_ = (r5 & MASK) & ~15u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r2 = x_; } // 48
r7 = r7 * r5; // 49
r3 = r7 * r0 + r3; // 50
{ uint b_ = (r2 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r3 = x_; } // 51
{ uint b_ = (r7 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r4 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r4 = x_; } // 52
r4 = mulhi(r4, r1); // 53
r7 = r7 + r4 + select(0xf64a6e41u, 0xd8ed09bau, ((sel >> 24u) & 1u) != 0u); // 54
r5 = mulhi(r5, r3); // 55
r5 = r5 * r7; // 56
{ uint b_ = (r0 & MASK) & ~15u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r3 = x_; } // 57
r5 = mulhi(r5, r2); // 58
r7 = r7 * r5; // 59
{ uint b_ = (r3 & MASK) & ~15u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r6 = x_; } // 60
r7 = r1 * r1 + r7; // 61
r0 = r0 ^ dataset[r7 & MASK]; // 62
r5 = r5 * r3; // 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;
}

View file

@ -0,0 +1,111 @@
#include <metal_stdlib>
using namespace metal;
#define MASK 0x0fffffffu
constant uint SEEDW[8] = { 0x30b957f0u, 0x374e2a95u, 0xf416345eu, 0x7af15ccbu, 0xf0bcabc5u, 0xb5b36f35u, 0xf99641c7u, 0xd0312afeu };
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;
}
// Header-bound variant: the init words come from buffer 3 (bind.rs), not from SEEDW.
kernel void igneum_hash_bound(device const uint* dataset [[buffer(0)]],
device ulong* out [[buffer(1)]],
constant uint& baseNonce [[buffer(2)]],
constant uint* initw [[buffer(3)]],
uint gid [[thread_position_in_grid]]) {
uint nonce = baseNonce + gid;
uint r0, r1, r2, r3, r4, r5, r6, r7;
{ uint x = nonce ^ initw[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ initw[1]; }
{ uint x = nonce ^ initw[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ initw[2]; }
{ uint x = nonce ^ initw[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ initw[3]; }
{ uint x = nonce ^ initw[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ initw[4]; }
{ uint x = nonce ^ initw[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ initw[5]; }
{ uint x = nonce ^ initw[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ initw[6]; }
{ uint x = nonce ^ initw[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ initw[7]; }
{ uint x = nonce ^ initw[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ initw[0]; }
for (uint it = 0u; it < 8u; ++it) {
uint sel = r0;
r3 = rotr_var(r3, r4); // 0
r0 = r0 ^ r7; // 1
{ uint b_ = (r0 & MASK) & ~15u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint x_ = r1 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r1 = x_; } // 2
r5 = rotl_imm(r5, 15u); // 3
r2 = r2 + r3 + select(0xd7a264d9u, 0xd35e575cu, ((sel >> 6u) & 1u) != 0u); // 4
r5 = r5 ^ simd_shuffle_xor(r2, (ushort)1); // 5
r1 = r2 * r5 + r1; // 6
r2 = r2 | r3; // 7
r3 = r3 ^ r5; // 8
r0 = r0 + r3 + select(0x4e543e70u, 0xd98be6edu, ((sel >> 29u) & 1u) != 0u); // 9
r5 = rotr_var(r5, r1); // 10
r3 = r3 * r5; // 11
r2 = r2 ^ r7; // 12
r1 = r1 + r6 + select(0xe36f3c9fu, 0x97a9219cu, ((sel >> 30u) & 1u) != 0u); // 13
r2 = r2 | r1; // 14
r1 = r1 | r3; // 15
r7 = r3 * r7 + r7; // 16
r3 = r3 + r2 + select(0x0c0a7b49u, 0xf799b153u, ((sel >> 13u) & 1u) != 0u); // 17
r7 = r7 ^ dataset[r5 & MASK]; // 18
r7 = r7 ^ dataset[r2 & MASK]; // 19
r5 = r5 ^ simd_shuffle_xor(r0, (ushort)8); // 20
r4 = r4 ^ dataset[r1 & MASK]; // 21
r5 = r5 ^ dataset[r7 & MASK]; // 22
r4 = r4 ^ r2; // 23
r5 = r5 + r3 + select(0x011f9670u, 0x26eb8325u, ((sel >> 14u) & 1u) != 0u); // 24
r0 = r0 * r1; // 25
r4 = r4 ^ r7; // 26
r7 = r7 + r5 + select(0x42177757u, 0xf12a4057u, ((sel >> 26u) & 1u) != 0u); // 27
r5 = r0 * r7 + r5; // 28
r3 = r3 * r6; // 29
r6 = rotl_imm(r6, 24u); // 30
r4 = rotr_var(r4, r0); // 31
r6 = r6 ^ dataset[r4 & MASK]; // 32
r0 = r0 ^ simd_shuffle_xor(r1, (ushort)1); // 33
r4 = r4 * r5; // 34
r2 = rotl_imm(r2, 15u); // 35
r7 = r7 + r3 + select(0xe0ebc725u, 0xc1b9573fu, ((sel >> 5u) & 1u) != 0u); // 36
{ uint b_ = (r2 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r0 = x_; } // 37
r7 = r0 * r2 + r7; // 38
r0 = r0 + r7 + select(0xd0b844deu, 0xde4cd365u, ((sel >> 16u) & 1u) != 0u); // 39
{ uint b_ = (r6 & MASK) & ~15u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint x_ = r7 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r7 = x_; } // 40
r0 = r0 ^ r1; // 41
r1 = rotl_imm(r1, 19u); // 42
r7 = r7 ^ r1; // 43
r2 = r2 * r4; // 44
{ uint b_ = (r4 & MASK) & ~15u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r6 = x_; } // 45
r7 = r7 + r3 + select(0x7633c48cu, 0x5d12f1a2u, ((sel >> 28u) & 1u) != 0u); // 46
r3 = r3 ^ dataset[r2 & MASK]; // 47
{ uint b_ = (r5 & MASK) & ~15u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r2 = x_; } // 48
r7 = r7 * r5; // 49
r3 = r7 * r0 + r3; // 50
{ uint b_ = (r2 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r3 = x_; } // 51
{ uint b_ = (r7 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r4 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r4 = x_; } // 52
r4 = mulhi(r4, r1); // 53
r7 = r7 + r4 + select(0xf64a6e41u, 0xd8ed09bau, ((sel >> 24u) & 1u) != 0u); // 54
r5 = mulhi(r5, r3); // 55
r5 = r5 * r7; // 56
{ uint b_ = (r0 & MASK) & ~15u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r3 = x_; } // 57
r5 = mulhi(r5, r2); // 58
r7 = r7 * r5; // 59
{ uint b_ = (r3 & MASK) & ~15u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r6 = x_; } // 60
r7 = r1 * r1 + r7; // 61
r0 = r0 ^ dataset[r7 & MASK]; // 62
r5 = r5 * r3; // 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;
}

View file

@ -0,0 +1,57 @@
// Generated by igneum-pow export (generator v2) for seed "igneum-readwidth/A/3". Do not edit by hand.
// Expected outputs: igneum-pow (Rust) CPU interpreter, generator v2, memory-hard dataset
#pragma once
#ifdef __cplusplus
#include <cstdint>
#else
#include <stdint.h>
#endif
#define IGNEUM_VEC_WARPS 3
static const uint32_t IGNEUM_VEC_BASE[IGNEUM_VEC_WARPS] = { 0u, 4096u, 1000000u };
static const uint64_t IGNEUM_VEC_OUT[IGNEUM_VEC_WARPS][32] = {
{ // base nonce 0
0x828587efbc9cfc52ull, 0xd16fc546d5ec261cull, 0xb8af925838b9e1acull, 0x1fc15054e4659c04ull, 0x93ebf4629d0269bbull, 0x52d88b5a306a4737ull, 0x0831861028fa46deull, 0xa4d101fceb303faaull,
0x96e9ddfb6a3af702ull, 0x2684793f4309a5baull, 0x12af0be41555dc8cull, 0xa5a7e5ff81a320abull, 0xa3cdfe85fb3696bfull, 0x2771918e4de403a4ull, 0xd46c364e94b850faull, 0x41f55fd8b9e13547ull,
0x31922377a1f64cf8ull, 0x13b1dedfb55abdb7ull, 0xd4d88bf9b5ad332dull, 0xda146417bd74c676ull, 0x360aed886b80ce04ull, 0x82340715236f4d67ull, 0x830d5072d63ffc31ull, 0xd08271eaa0cab2dcull,
0x9abe271aec0bd6cfull, 0xc12cb8813051e021ull, 0xc0e2a5765296703aull, 0x40afd7e8bcd7d766ull, 0x8d086fb3a523d08full, 0x2693f50209f3451aull, 0x7efb655d14f1ebeeull, 0x99ee7b1b6c7d4bc1ull
},
{ // base nonce 4096
0xbb4af8028dd1238full, 0xb1d96223fb465f3aull, 0xf6177d3717c5902dull, 0x7eb185cfde7ef8cdull, 0xed4d5f3af5e2455cull, 0x0b27846cbbb73cd6ull, 0x237cfab99f0d5ac0ull, 0xbb1e762688a5fa1full,
0xbd0462583cfd0143ull, 0xaa071a547999c287ull, 0x42f373a7cc6b30d0ull, 0xab2f623965cd83c7ull, 0x0ffe1417daac3f15ull, 0x3517ce71179553f4ull, 0xd6a3417bf3cd74e1ull, 0xcb3d43858bc48ff3ull,
0xaee5c7ff929e9c42ull, 0x1306f6d212e43830ull, 0x14c859a79eb2dad0ull, 0x8cfab5f11caec5e2ull, 0x2de1093748836118ull, 0xc1fa999644710490ull, 0x7c1afa846083ca13ull, 0x14a3ace55059a23dull,
0x0a5624ae684434deull, 0xbad4e3a866d28c00ull, 0x0708afb27b44085aull, 0xea27cfd72bcf1862ull, 0x5c9538461c82d517ull, 0x95e0033bf5d07d57ull, 0x49b334ff88786acfull, 0xade49578b94c594bull
},
{ // base nonce 1000000
0x448bdf6ce862af09ull, 0xb137b3a92e26de3bull, 0xf57d56bf9cdb1ba1ull, 0x60a48fcbd0a34564ull, 0xd4578282342e482aull, 0x09d7d7bd7e7166e1ull, 0xa13ea5faa65a969bull, 0x1c8f45874270eb1bull,
0x6c6b1c5799bceaf4ull, 0x11505938be6c01c3ull, 0x46d70f2787f3deaaull, 0xe7546fa4fdf67d02ull, 0x27780d83c95eb515ull, 0x5206a7adfba9f15dull, 0x485504ae37c128e0ull, 0xd442920b668e97dbull,
0x56fdc1e95fb2d170ull, 0x09e38ff904a5b56aull, 0xb2ae26db1222f910ull, 0xa19864d4801daf4dull, 0x06a933927c9d1911ull, 0x3dab78408de69705ull, 0x17f568a8f4bf8d07ull, 0x3bc2d52c807a0290ull,
0x58626fdb2dac1a6eull, 0x5bf21bfea7756f58ull, 0xd40dc38290489e78ull, 0xa0851f74c4783040ull, 0x9f521fa799d21d1eull, 0x363b3f454f9feef6ull, 0xb383f1a11e51190full, 0x4c83f6eb054d58a8ull
}
};
// Dataset self-test: dataset[0..15] and dataset[IGNEUM_MASK] (268435455).
static const uint32_t IGNEUM_DS_HEAD[16] = {
0xffc3cd94u, 0x5920ccd8u, 0x392f44bbu, 0x5e57f67au, 0x2f2bc2a9u, 0x620b0e36u, 0xbdc09014u, 0x436654bfu,
0x311e0b48u, 0x1abd93adu, 0x59cc7ce8u, 0xee5247b2u, 0x86171fe8u, 0x6d874751u, 0xc9f7728fu, 0x7c2a435du
};
static const uint32_t IGNEUM_DS_LAST_INDEX = 268435455u;
static const uint32_t IGNEUM_DS_LAST = 0xa33ada72u;
// 64 sampled dataset words (index, value) computed on the Mac.
#define IGNEUM_DS_SAMPLES 64
static const uint32_t IGNEUM_DS_SAMPLE_INDEX[IGNEUM_DS_SAMPLES] = {
59471966u, 217795994u, 208353206u, 42483309u, 172547758u, 148076330u, 183853158u, 214389424u, 267488061u, 169781097u, 184093494u, 153880993u, 84977930u, 46426879u, 3093825u, 225364072u, 44593546u, 260713159u, 168250303u, 52384140u, 223401610u, 45554030u, 95410555u, 175039924u, 79171087u, 267580473u, 24168642u, 37981670u, 171551130u, 195559979u, 204611762u, 140997658u, 138925853u, 86637313u, 20736778u, 219665210u, 160430336u, 264654675u, 8013395u, 228945585u, 213884386u, 104419827u, 44185464u, 142737231u, 99284897u, 132475900u, 61861762u, 132056166u, 262388043u, 91878046u, 117353561u, 124768597u, 71352993u, 190698941u, 46055428u, 55281366u, 165145231u, 106810753u, 171985651u, 232085256u, 159510492u, 40072060u, 209107596u, 39023794u
};
static const uint32_t IGNEUM_DS_SAMPLE_VALUE[IGNEUM_DS_SAMPLES] = {
0xe8b73d94u, 0x337028b5u, 0xafe148c9u, 0xab99f7aeu, 0x434ea619u, 0xd85cb880u, 0x54764c7fu, 0x82c7e420u, 0xedf4cb9eu, 0x9884c959u, 0x223ee793u, 0x3a9ccf69u, 0x81da4fd2u, 0xd6ce8cb9u, 0xe3922dcau, 0x3e7e6bdeu, 0x382a3acau, 0x567e7f7fu, 0x25a0f084u, 0xbfeef128u, 0xe338abfbu, 0x7c3b5280u, 0x909bc5f1u, 0xd8b74b9cu, 0x8e31a22eu, 0x26b5f1d8u, 0x79122c00u, 0xcafc3340u, 0xd5e02ea3u, 0x1aee1afdu, 0xdb090d9au, 0xb049f435u, 0x4954d8bau, 0x03797ba0u, 0x196eefbdu, 0xd153412au, 0xbe5d2c4bu, 0xdaa14f0eu, 0x8e61ed07u, 0x9e9a64c6u, 0x2e29ff36u, 0x392a8589u, 0xb56a5912u, 0xfa6e8b57u, 0xd1a737cbu, 0xb0fa841au, 0xbe1c341fu, 0xe25be0f1u, 0xe937f543u, 0xebab2248u, 0x8e1b607au, 0x202a2fedu, 0x95e2819cu, 0x9c9652d4u, 0x32fedef0u, 0xdecfff82u, 0xcb5d43e5u, 0xb735806au, 0x8905939cu, 0xfbf8472du, 0xada74e5du, 0x7ebdeeeau, 0x0119f2b3u, 0xa9a376b8u
};
// Cache self-test (memory-hard mode): cache[0..15], the last 16 words, and FNV-1a 64 over all 2^26 words.
static const uint32_t IGNEUM_CACHE_HEAD[16] = {
0x355a86d2u, 0x7957db1cu, 0xd21772afu, 0x6fc1e09bu, 0xd55ce61du, 0x6e6a278bu, 0xd3f543ceu, 0x223d8e82u,
0x143ab337u, 0x2e9f05bdu, 0x2eb389bfu, 0x0c6e449eu, 0x5cfa4222u, 0xba6560feu, 0x8e3e1aa4u, 0xdbcc1d53u
};
static const uint32_t IGNEUM_CACHE_LAST[16] = {
0x41190d91u, 0xbd277957u, 0x22ddbb49u, 0x6986f207u, 0xdf69a4d6u, 0x26401a3au, 0x818230fbu, 0xc417122du,
0x3597b211u, 0xb553ce55u, 0xcf39cc0du, 0x3b7fc43au, 0x3fd43b00u, 0x67e1c80eu, 0xffa7ea7du, 0xca2960abu
};
static const uint64_t IGNEUM_CACHE_FNV64 = 0x48c4f5bf24166b2eull;

View file

@ -0,0 +1,36 @@
{
"seed": "igneum-readwidth/A/3",
"day": "2026-10-03",
"dataset_mode": "memory-hard",
"dataset_log2_words": 28,
"mask": "0x0fffffff",
"lanes": 32,
"source": "igneum-pow (Rust) CPU interpreter, generator v2, memory-hard dataset",
"warps": [
{"base_nonce": 0, "expected": [
"0x828587efbc9cfc52", "0xd16fc546d5ec261c", "0xb8af925838b9e1ac", "0x1fc15054e4659c04", "0x93ebf4629d0269bb", "0x52d88b5a306a4737", "0x0831861028fa46de", "0xa4d101fceb303faa",
"0x96e9ddfb6a3af702", "0x2684793f4309a5ba", "0x12af0be41555dc8c", "0xa5a7e5ff81a320ab", "0xa3cdfe85fb3696bf", "0x2771918e4de403a4", "0xd46c364e94b850fa", "0x41f55fd8b9e13547",
"0x31922377a1f64cf8", "0x13b1dedfb55abdb7", "0xd4d88bf9b5ad332d", "0xda146417bd74c676", "0x360aed886b80ce04", "0x82340715236f4d67", "0x830d5072d63ffc31", "0xd08271eaa0cab2dc",
"0x9abe271aec0bd6cf", "0xc12cb8813051e021", "0xc0e2a5765296703a", "0x40afd7e8bcd7d766", "0x8d086fb3a523d08f", "0x2693f50209f3451a", "0x7efb655d14f1ebee", "0x99ee7b1b6c7d4bc1"
]},
{"base_nonce": 4096, "expected": [
"0xbb4af8028dd1238f", "0xb1d96223fb465f3a", "0xf6177d3717c5902d", "0x7eb185cfde7ef8cd", "0xed4d5f3af5e2455c", "0x0b27846cbbb73cd6", "0x237cfab99f0d5ac0", "0xbb1e762688a5fa1f",
"0xbd0462583cfd0143", "0xaa071a547999c287", "0x42f373a7cc6b30d0", "0xab2f623965cd83c7", "0x0ffe1417daac3f15", "0x3517ce71179553f4", "0xd6a3417bf3cd74e1", "0xcb3d43858bc48ff3",
"0xaee5c7ff929e9c42", "0x1306f6d212e43830", "0x14c859a79eb2dad0", "0x8cfab5f11caec5e2", "0x2de1093748836118", "0xc1fa999644710490", "0x7c1afa846083ca13", "0x14a3ace55059a23d",
"0x0a5624ae684434de", "0xbad4e3a866d28c00", "0x0708afb27b44085a", "0xea27cfd72bcf1862", "0x5c9538461c82d517", "0x95e0033bf5d07d57", "0x49b334ff88786acf", "0xade49578b94c594b"
]},
{"base_nonce": 1000000, "expected": [
"0x448bdf6ce862af09", "0xb137b3a92e26de3b", "0xf57d56bf9cdb1ba1", "0x60a48fcbd0a34564", "0xd4578282342e482a", "0x09d7d7bd7e7166e1", "0xa13ea5faa65a969b", "0x1c8f45874270eb1b",
"0x6c6b1c5799bceaf4", "0x11505938be6c01c3", "0x46d70f2787f3deaa", "0xe7546fa4fdf67d02", "0x27780d83c95eb515", "0x5206a7adfba9f15d", "0x485504ae37c128e0", "0xd442920b668e97db",
"0x56fdc1e95fb2d170", "0x09e38ff904a5b56a", "0xb2ae26db1222f910", "0xa19864d4801daf4d", "0x06a933927c9d1911", "0x3dab78408de69705", "0x17f568a8f4bf8d07", "0x3bc2d52c807a0290",
"0x58626fdb2dac1a6e", "0x5bf21bfea7756f58", "0xd40dc38290489e78", "0xa0851f74c4783040", "0x9f521fa799d21d1e", "0x363b3f454f9feef6", "0xb383f1a11e51190f", "0x4c83f6eb054d58a8"
]}
],
"dataset_head": ["0xffc3cd94", "0x5920ccd8", "0x392f44bb", "0x5e57f67a", "0x2f2bc2a9", "0x620b0e36", "0xbdc09014", "0x436654bf", "0x311e0b48", "0x1abd93ad", "0x59cc7ce8", "0xee5247b2", "0x86171fe8", "0x6d874751", "0xc9f7728f", "0x7c2a435d"],
"dataset_last_index": 268435455,
"dataset_last": "0xa33ada72",
"dataset_samples": [{"index": 59471966, "value": "0xe8b73d94"}, {"index": 217795994, "value": "0x337028b5"}, {"index": 208353206, "value": "0xafe148c9"}, {"index": 42483309, "value": "0xab99f7ae"}, {"index": 172547758, "value": "0x434ea619"}, {"index": 148076330, "value": "0xd85cb880"}, {"index": 183853158, "value": "0x54764c7f"}, {"index": 214389424, "value": "0x82c7e420"}, {"index": 267488061, "value": "0xedf4cb9e"}, {"index": 169781097, "value": "0x9884c959"}, {"index": 184093494, "value": "0x223ee793"}, {"index": 153880993, "value": "0x3a9ccf69"}, {"index": 84977930, "value": "0x81da4fd2"}, {"index": 46426879, "value": "0xd6ce8cb9"}, {"index": 3093825, "value": "0xe3922dca"}, {"index": 225364072, "value": "0x3e7e6bde"}, {"index": 44593546, "value": "0x382a3aca"}, {"index": 260713159, "value": "0x567e7f7f"}, {"index": 168250303, "value": "0x25a0f084"}, {"index": 52384140, "value": "0xbfeef128"}, {"index": 223401610, "value": "0xe338abfb"}, {"index": 45554030, "value": "0x7c3b5280"}, {"index": 95410555, "value": "0x909bc5f1"}, {"index": 175039924, "value": "0xd8b74b9c"}, {"index": 79171087, "value": "0x8e31a22e"}, {"index": 267580473, "value": "0x26b5f1d8"}, {"index": 24168642, "value": "0x79122c00"}, {"index": 37981670, "value": "0xcafc3340"}, {"index": 171551130, "value": "0xd5e02ea3"}, {"index": 195559979, "value": "0x1aee1afd"}, {"index": 204611762, "value": "0xdb090d9a"}, {"index": 140997658, "value": "0xb049f435"}, {"index": 138925853, "value": "0x4954d8ba"}, {"index": 86637313, "value": "0x03797ba0"}, {"index": 20736778, "value": "0x196eefbd"}, {"index": 219665210, "value": "0xd153412a"}, {"index": 160430336, "value": "0xbe5d2c4b"}, {"index": 264654675, "value": "0xdaa14f0e"}, {"index": 8013395, "value": "0x8e61ed07"}, {"index": 228945585, "value": "0x9e9a64c6"}, {"index": 213884386, "value": "0x2e29ff36"}, {"index": 104419827, "value": "0x392a8589"}, {"index": 44185464, "value": "0xb56a5912"}, {"index": 142737231, "value": "0xfa6e8b57"}, {"index": 99284897, "value": "0xd1a737cb"}, {"index": 132475900, "value": "0xb0fa841a"}, {"index": 61861762, "value": "0xbe1c341f"}, {"index": 132056166, "value": "0xe25be0f1"}, {"index": 262388043, "value": "0xe937f543"}, {"index": 91878046, "value": "0xebab2248"}, {"index": 117353561, "value": "0x8e1b607a"}, {"index": 124768597, "value": "0x202a2fed"}, {"index": 71352993, "value": "0x95e2819c"}, {"index": 190698941, "value": "0x9c9652d4"}, {"index": 46055428, "value": "0x32fedef0"}, {"index": 55281366, "value": "0xdecfff82"}, {"index": 165145231, "value": "0xcb5d43e5"}, {"index": 106810753, "value": "0xb735806a"}, {"index": 171985651, "value": "0x8905939c"}, {"index": 232085256, "value": "0xfbf8472d"}, {"index": 159510492, "value": "0xada74e5d"}, {"index": 40072060, "value": "0x7ebdeeea"}, {"index": 209107596, "value": "0x0119f2b3"}, {"index": 39023794, "value": "0xa9a376b8"}],
"cache_head": ["0x355a86d2", "0x7957db1c", "0xd21772af", "0x6fc1e09b", "0xd55ce61d", "0x6e6a278b", "0xd3f543ce", "0x223d8e82", "0x143ab337", "0x2e9f05bd", "0x2eb389bf", "0x0c6e449e", "0x5cfa4222", "0xba6560fe", "0x8e3e1aa4", "0xdbcc1d53"],
"cache_last_line": ["0x41190d91", "0xbd277957", "0x22ddbb49", "0x6986f207", "0xdf69a4d6", "0x26401a3a", "0x818230fb", "0xc417122d", "0x3597b211", "0xb553ce55", "0xcf39cc0d", "0x3b7fc43a", "0x3fd43b00", "0x67e1c80e", "0xffa7ea7d", "0xca2960ab"],
"cache_fnv1a64": "0x48c4f5bf24166b2e"
}

View file

@ -0,0 +1,278 @@
// Generated by igneum-pow export (generator v2) for seed "igneum-readwidth/A/5". Do not edit by hand.
// OpenCL C twin of the Metal kernel for the same seed (see proto-opencl/README.md, WAVEFRONT.md and program.metal).
// Built from source at runtime by proto-opencl/host.c, which passes these defines:
// IGNEUM_GROUP work-group size of igneum_hash, a multiple of 32 (default 32: one work-group = one 32-lane unit)
// IGNEUM_EXCHANGE 0 = local-memory exchange with a barrier (any device, any wave width; the default)
// 1 = sub_group_shuffle_xor (cl_khr_subgroup_shuffle), only with IGNEUM_GROUP 32 and a sub-group size of exactly 32
// 2 = intel_sub_group_shuffle_xor (cl_intel_subgroups), same condition
// The verification unit is always 32 lanes. A 64-wide hardware wave (AMD GCN/CDNA, RDNA in wave64) runs two units;
// the exchange masks are 1, 2, 4, 8, 16, so every partner lane lies inside the lane's own aligned run of 32.
#ifndef IGNEUM_GROUP
#define IGNEUM_GROUP 32
#endif
#ifndef IGNEUM_EXCHANGE
#define IGNEUM_EXCHANGE 0
#endif
#ifdef __OPENCL_VERSION__
#define IGNEUM_KERNEL_HASH __kernel __attribute__((reqd_work_group_size(IGNEUM_GROUP, 1, 1)))
#define IGNEUM_LOCAL_WORDS(name, n) __local uint name[n]
#if IGNEUM_EXCHANGE == 1
#ifdef cl_khr_subgroups
#pragma OPENCL EXTENSION cl_khr_subgroups : enable
#endif
#ifdef cl_khr_subgroup_shuffle
#pragma OPENCL EXTENSION cl_khr_subgroup_shuffle : enable
#endif
#elif IGNEUM_EXCHANGE == 2
#pragma OPENCL EXTENSION cl_intel_subgroups : enable
#endif
#else
// Not an OpenCL compiler: proto-opencl/emu compiles this file as C++ and supplies the built-ins and these two macros.
#include "emu_opencl.h"
#endif
#if IGNEUM_EXCHANGE == 1
#define IGNEUM_SHFL_XOR(dst, a, m) dst = sub_group_shuffle_xor((a), (uint)(m))
#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u)
#elif IGNEUM_EXCHANGE == 2
#define IGNEUM_SHFL_XOR(dst, a, m) dst = intel_sub_group_shuffle_xor((a), (uint)(m))
#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u)
#else
// Local-memory exchange. Two buffers of IGNEUM_GROUP words alternate (xk counts exchanges), so one barrier per
// exchange is enough: a lane can only overwrite buffer b at exchange k+2 after passing barrier k+1, and every lane
// reaches barrier k+1 only after its read of buffer b at exchange k. The partner lid ^ m stays inside the lane's
// aligned run of 32 because m < 32. Control flow is uniform, so every work-item reaches every barrier.
#define IGNEUM_SHFL_XOR(dst, a, m) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid ^ (uint)(m))]; xk += 1u; }
#define IGNEUM_BCAST0(dst, a) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid & ~31u)]; xk += 1u; }
#endif
static inline uint splitmix32(uint 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. OpenCL rotate() rotates left by n modulo 32.
static inline uint rotl_imm(uint x, uint n) { return rotate(x, n); }
// Right rotation by n modulo 32 as a left rotation by (32 - n) modulo 32; n == 0 gives x.
static inline uint rotr_var(uint x, uint n) { return rotate(x, (0u - n) & 31u); }
static 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;
}
// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines.
// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals.
#define MH_CACHE_LINE_MASK 0x003fffffu
#define MH_SEGMENT_LINES 64u
#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); }
static inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site
// y = ChaCha12 core(x) + x
static inline void mh_chacha_block(const uint* x, uint* y) {
for (uint i = 0u; i < 16u; ++i) y[i] = x[i];
for (uint r = 0u; r < 6u; ++r) {
MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u)
MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u)
}
for (uint i = 0u; i < 16u; ++i) y[i] += x[i];
}
// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0.
static inline void mh_cache_segment(__global uint* cache, uint seg) {
uint prev[16]; uint x[16]; uint y[16];
for (uint i = 0u; i < 16u; ++i) prev[i] = 0u;
for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) {
x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3];
x[4] = 0x3067619fu ^ prev[4];
x[5] = 0x3c269176u ^ prev[5];
x[6] = 0x84a03b03u ^ prev[6];
x[7] = 0xf8c63294u ^ prev[7];
x[8] = 0xff977c5bu ^ prev[8];
x[9] = 0xe60def3eu ^ prev[9];
x[10] = 0x63630141u ^ prev[10];
x[11] = 0xb8fbcb58u ^ prev[11];
x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15];
mh_chacha_block(x, y);
__global uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u);
for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; }
}
}
// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations.
static inline void mh_mixer(uint* s, uint rk) {
s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u;
s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu;
s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u;
s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu;
s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u;
s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u;
s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u;
s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u;
s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du;
s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u;
s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du;
s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu;
s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du;
s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu;
s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u;
s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u;
MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u)
MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u)
MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u)
MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u)
}
// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer.
static inline void mh_item(__global const uint* cache, uint t, uint* s) {
s[0] = 0x3067619fu;
s[1] = 0x3c269176u;
s[2] = 0x84a03b03u;
s[3] = 0xf8c63294u;
s[4] = 0xff977c5bu;
s[5] = 0xe60def3eu;
s[6] = 0x63630141u;
s[7] = 0xb8fbcb58u;
s[8] = t * 0x42146205u + 0xbab68293u;
s[9] = t * 0x52cbe0fbu + 0xcc162340u;
s[10] = t * 0x7ecf4a03u + 0x6ce151ccu;
s[11] = t * 0x6728907fu + 0xe62b8997u;
s[12] = t * 0xd81d9751u + 0xc9c80297u;
s[13] = t * 0x132952c3u + 0xf74a1654u;
s[14] = t * 0xf60de277u + 0x3d704af5u;
s[15] = t * 0x05358035u + 0x3cf522b7u;
for (uint r = 0u; r < 8u; ++r) {
mh_mixer(s, 0x9E3779B9u * (r + 1u));
__global const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u);
for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i];
}
mh_mixer(s, 0x9E3779B9u * 9u);
}
// dataset[w] without the dataset: derive item w >> 4 and take word w & 15.
static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; }
// Memory-hard dataset (MEMHARD.md). One work-item per cache segment; one work-item per 64-byte dataset item.
// The same constants as memhard.h in this pack (one emitter, three dialects).
__kernel void igneum_cache_fill(__global uint* cache, uint nSegments) {
uint seg = (uint)get_global_id(0);
if (seg < nSegments) mh_cache_segment(cache, seg);
}
__kernel void igneum_build(__global uint* ds, __global const uint* cache, uint nItems) {
uint t = (uint)get_global_id(0);
if (t < nItems) {
uint s[16];
mh_item(cache, t, s);
__global uint* d = ds + ((ulong)t * 16u);
for (uint i = 0u; i < 16u; ++i) d[i] = s[i];
}
}
// One hash per work-item. IGNEUM_GROUP is a multiple of 32; lane = lid & 31 and every exchange stays inside the
// lane's own aligned run of 32 work-items, exactly like simd_shuffle_xor inside a 32-wide Metal SIMD group and
// __shfl_xor_sync inside a CUDA warp. Control flow is uniform (no branches at all).
IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask) {
uint gid = (uint)get_global_id(0);
uint lid = (uint)get_local_id(0);
uint nonce = baseNonce + gid;
uint r0, r1, r2, r3, r4, r5, r6, r7;
#if IGNEUM_EXCHANGE == 0
IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP);
uint xk = 0u;
#else
(void)lid;
#endif
{ uint x = nonce ^ 0xc255b2bfu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0xdba9d396u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1]
{ uint x = nonce ^ 0xdba9d396u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0x6ea527e4u; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2]
{ uint x = nonce ^ 0x6ea527e4u; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x05f1866au; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3]
{ uint x = nonce ^ 0x05f1866au; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0x2947e02eu; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4]
{ uint x = nonce ^ 0x2947e02eu; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0x0ecc5c6fu; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5]
{ uint x = nonce ^ 0x0ecc5c6fu; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0xc3b7068du; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6]
{ uint x = nonce ^ 0xc3b7068du; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0x282d1c2eu; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7]
{ uint x = nonce ^ 0x282d1c2eu; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0xc255b2bfu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0]
for (uint it = 0u; it < 8u; ++it) {
uint sel = r0;
r3 = rotl_imm(r3, 26u); // 0 rotl
r2 = rotr_var(r2, r0); // 1 rotr
r0 = r0 ^ r7; // 2 xor
r4 = r4 ^ r7; // 3 xor
r2 = r2 ^ ds[r4 & mask]; // 4 load
{ uint b_ = (r2 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r3 = x_; } // 5 load
{ uint t_; IGNEUM_SHFL_XOR(t_, r7, 4u); r0 = r0 ^ t_; } // 6 shfl
r6 = rotl_imm(r6, 31u); // 7 rotl
r1 = r4 * r2 + r1; // 8 mad
r1 = r7 * r0 + r1; // 9 mad
r4 = r4 ^ ds[r1 & mask]; // 10 load
r6 = r7 * r0 + r6; // 11 mad
r2 = r2 * r6; // 12 mul
{ uint b_ = (r2 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r0 = x_; } // 13 load
r2 = r2 + r7 + ((((sel >> 22u) & 1u) != 0u) ? 0x4a3db5a5u : 0x5df3957du); // 14 add
{ uint t_; IGNEUM_SHFL_XOR(t_, r7, 1u); r5 = r5 ^ t_; } // 15 shfl
r3 = r5 * r6 + r3; // 16 mad
r2 = r2 * r5; // 17 mul
{ uint t_; IGNEUM_SHFL_XOR(t_, r5, 1u); r1 = r1 ^ t_; } // 18 shfl
r0 = r0 - r1; // 19 sub
r4 = r6 * r4 + r4; // 20 mad
{ uint t_; IGNEUM_SHFL_XOR(t_, r0, 16u); r3 = r3 ^ t_; } // 21 shfl
r1 = rotl_imm(r1, 24u); // 22 rotl
r1 = r1 + r0 + ((((sel >> 22u) & 1u) != 0u) ? 0xfc07c54cu : 0x5c141117u); // 23 add
r2 = r2 ^ ds[r0 & mask]; // 24 load
r0 = r0 ^ r1; // 25 xor
r3 = r3 ^ r7; // 26 xor
{ uint b_ = (r3 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r2 = x_; } // 27 load
r0 = r0 ^ r6; // 28 xor
r4 = r4 ^ r1; // 29 xor
r6 = r6 * r3; // 30 mul
r3 = rotl_imm(r3, 23u); // 31 rotl
r7 = rotr_var(r7, r1); // 32 rotr
{ uint t_; IGNEUM_SHFL_XOR(t_, r2, 2u); r6 = r6 ^ t_; } // 33 shfl
{ uint b_ = (r0 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r5 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r5 = x_; } // 34 load
r6 = r6 + r3 + ((((sel >> 29u) & 1u) != 0u) ? 0x0b74657bu : 0xfb55c58du); // 35 add
{ uint b_ = (r5 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r6 = x_; } // 36 load
r1 = r1 ^ ds[r4 & mask]; // 37 load
{ uint b_ = (r3 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r6 = x_; } // 38 load
r4 = r4 ^ ds[r7 & mask]; // 39 load
r7 = mul_hi(r7, r3); // 40 mulhi
r0 = r0 | r6; // 41 or
r0 = r3 * r0 + r0; // 42 mad
{ uint b_ = (r6 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r4 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r4 = x_; } // 43 load
r2 = r4 * r0 + r2; // 44 mad
r2 = r4 * r2 + r2; // 45 mad
{ uint t_; IGNEUM_SHFL_XOR(t_, r5, 16u); r7 = r7 ^ t_; } // 46 shfl
r6 = r1 * r4 + r6; // 47 mad
r7 = r7 ^ ds[r1 & mask]; // 48 load
r6 = mul_hi(r6, r5); // 49 mulhi
r5 = mul_hi(r5, r0); // 50 mulhi
{ uint b_ = (r6 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r1 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r1 = x_; } // 51 load
{ uint t_; IGNEUM_SHFL_XOR(t_, r3, 1u); r7 = r7 ^ t_; } // 52 shfl
r2 = r2 ^ r5; // 53 xor
{ uint t_; IGNEUM_SHFL_XOR(t_, r6, 4u); r0 = r0 ^ t_; } // 54 shfl
r2 = r2 * r5; // 55 mul
r5 = r5 * r6; // 56 mul
{ uint b_ = (r4 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r6 = x_; } // 57 load
r4 = r7 * r7 + r4; // 58 mad
r2 = r2 ^ ds[r1 & mask]; // 59 load
r1 = r1 * r4; // 60 mul
r3 = r3 * r4; // 61 mul
r6 = r6 ^ r4; // 62 xor
r3 = rotr_var(r3, r1); // 63 rotr
}
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;
}
#if IGNEUM_EXCHANGE != 0
// Reports the sub-group size this device uses for a work-group of IGNEUM_GROUP items. host.c runs it only when the
// per-kernel query (clGetKernelSubGroupInfoKHR on igneum_hash) is unavailable; that query is preferred because a
// compiler may pick a different wave width per kernel (RDNA: wave32 or wave64). See WAVEFRONT.md.
IGNEUM_KERNEL_HASH void igneum_probe_subgroup(__global uint* out) {
if (get_local_id(0) == 0u) { out[0] = get_sub_group_size(); out[1] = get_num_sub_groups(); }
}
#endif

View file

@ -0,0 +1,164 @@
// Generated by igneum-pow export (generator v2) for seed "igneum-readwidth/A/5". 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"
#include "memhard.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;
}
// Memory-hard dataset (MEMHARD.md). One thread per cache segment; one thread per 64-byte dataset item.
// The core functions (mh_cache_segment, mh_item) are in memhard.h and are also compiled for the host.
__global__ void igneum_cache_fill(uint32_t* cache, uint32_t nSegments) {
uint32_t seg = blockIdx.x * blockDim.x + threadIdx.x;
if (seg < nSegments) mh_cache_segment(cache, seg);
}
__global__ void igneum_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems) {
uint32_t t = blockIdx.x * blockDim.x + threadIdx.x;
if (t < nItems) {
uint32_t s[16];
mh_item(cache, t, s);
uint32_t* d = ds + (size_t)t * 16u;
for (uint32_t i = 0u; i < 16u; ++i) d[i] = s[i];
}
}
// 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 ^ 0xc255b2bfu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0xdba9d396u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1]
{ uint32_t x = nonce ^ 0xdba9d396u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0x6ea527e4u; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2]
{ uint32_t x = nonce ^ 0x6ea527e4u; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x05f1866au; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3]
{ uint32_t x = nonce ^ 0x05f1866au; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0x2947e02eu; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4]
{ uint32_t x = nonce ^ 0x2947e02eu; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0x0ecc5c6fu; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5]
{ uint32_t x = nonce ^ 0x0ecc5c6fu; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0xc3b7068du; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6]
{ uint32_t x = nonce ^ 0xc3b7068du; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0x282d1c2eu; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7]
{ uint32_t x = nonce ^ 0x282d1c2eu; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0xc255b2bfu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0]
for (uint32_t it = 0u; it < 8u; ++it) {
uint32_t sel = r0;
r3 = rotl_imm(r3, 26u); // 0 rotl
r2 = rotr_var(r2, r0); // 1 rotr
r0 = r0 ^ r7; // 2 xor
r4 = r4 ^ r7; // 3 xor
r2 = r2 ^ ds[r4 & mask]; // 4 load
{ uint32_t b_ = (r2 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r3 = x_; } // 5 load
r0 = r0 ^ __shfl_xor_sync(0xffffffffu, r7, 4); // 6 shfl
r6 = rotl_imm(r6, 31u); // 7 rotl
r1 = r4 * r2 + r1; // 8 mad
r1 = r7 * r0 + r1; // 9 mad
r4 = r4 ^ ds[r1 & mask]; // 10 load
r6 = r7 * r0 + r6; // 11 mad
r2 = r2 * r6; // 12 mul
{ uint32_t b_ = (r2 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r0 = x_; } // 13 load
r2 = r2 + r7 + ((((sel >> 22u) & 1u) != 0u) ? 0x4a3db5a5u : 0x5df3957du); // 14 add
r5 = r5 ^ __shfl_xor_sync(0xffffffffu, r7, 1); // 15 shfl
r3 = r5 * r6 + r3; // 16 mad
r2 = r2 * r5; // 17 mul
r1 = r1 ^ __shfl_xor_sync(0xffffffffu, r5, 1); // 18 shfl
r0 = r0 - r1; // 19 sub
r4 = r6 * r4 + r4; // 20 mad
r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r0, 16); // 21 shfl
r1 = rotl_imm(r1, 24u); // 22 rotl
r1 = r1 + r0 + ((((sel >> 22u) & 1u) != 0u) ? 0xfc07c54cu : 0x5c141117u); // 23 add
r2 = r2 ^ ds[r0 & mask]; // 24 load
r0 = r0 ^ r1; // 25 xor
r3 = r3 ^ r7; // 26 xor
{ uint32_t b_ = (r3 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r2 = x_; } // 27 load
r0 = r0 ^ r6; // 28 xor
r4 = r4 ^ r1; // 29 xor
r6 = r6 * r3; // 30 mul
r3 = rotl_imm(r3, 23u); // 31 rotl
r7 = rotr_var(r7, r1); // 32 rotr
r6 = r6 ^ __shfl_xor_sync(0xffffffffu, r2, 2); // 33 shfl
{ uint32_t b_ = (r0 & mask) & ~15u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint32_t x_ = r5 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r5 = x_; } // 34 load
r6 = r6 + r3 + ((((sel >> 29u) & 1u) != 0u) ? 0x0b74657bu : 0xfb55c58du); // 35 add
{ uint32_t b_ = (r5 & mask) & ~15u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint32_t x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r6 = x_; } // 36 load
r1 = r1 ^ ds[r4 & mask]; // 37 load
{ uint32_t b_ = (r3 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r6 = x_; } // 38 load
r4 = r4 ^ ds[r7 & mask]; // 39 load
r7 = __umulhi(r7, r3); // 40 mulhi
r0 = r0 | r6; // 41 or
r0 = r3 * r0 + r0; // 42 mad
{ uint32_t b_ = (r6 & mask) & ~15u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint32_t x_ = r4 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r4 = x_; } // 43 load
r2 = r4 * r0 + r2; // 44 mad
r2 = r4 * r2 + r2; // 45 mad
r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r5, 16); // 46 shfl
r6 = r1 * r4 + r6; // 47 mad
r7 = r7 ^ ds[r1 & mask]; // 48 load
r6 = __umulhi(r6, r5); // 49 mulhi
r5 = __umulhi(r5, r0); // 50 mulhi
{ uint32_t b_ = (r6 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r1 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r1 = x_; } // 51 load
r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r3, 1); // 52 shfl
r2 = r2 ^ r5; // 53 xor
r0 = r0 ^ __shfl_xor_sync(0xffffffffu, r6, 4); // 54 shfl
r2 = r2 * r5; // 55 mul
r5 = r5 * r6; // 56 mul
{ uint32_t b_ = (r4 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r6 = x_; } // 57 load
r4 = r7 * r7 + r4; // 58 mad
r2 = r2 ^ ds[r1 & mask]; // 59 load
r1 = r1 * r4; // 60 mul
r3 = r3 * r4; // 61 mul
r6 = r6 ^ r4; // 62 xor
r3 = rotr_var(r3, r1); // 63 rotr
}
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_cache_fill(uint32_t* cache, uint32_t nSegments) {
if (nSegments == 0u) return cudaErrorInvalidValue;
uint32_t block = 256u;
uint32_t grid = (nSegments + block - 1u) / block;
igneum_cache_fill<<<grid, block>>>(cache, nSegments);
return cudaGetLastError();
}
cudaError_t igneum_launch_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems) {
if (nItems == 0u) return cudaErrorInvalidValue;
uint32_t block = 256u;
uint32_t grid = (nItems + block - 1u) / block;
igneum_build<<<grid, block>>>(ds, cache, nItems);
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);
}

View file

@ -0,0 +1,372 @@
// Generated by igneum-pow export (generator v2) for seed "igneum-readwidth/A/5". Do not edit by hand.
// OpenCL C twin of the Metal kernel for the same seed (see proto-opencl/README.md, WAVEFRONT.md and program.metal).
// Built from source at runtime by proto-opencl/host.c, which passes these defines:
// IGNEUM_GROUP work-group size of igneum_hash, a multiple of 32 (default 32: one work-group = one 32-lane unit)
// IGNEUM_EXCHANGE 0 = local-memory exchange with a barrier (any device, any wave width; the default)
// 1 = sub_group_shuffle_xor (cl_khr_subgroup_shuffle), only with IGNEUM_GROUP 32 and a sub-group size of exactly 32
// 2 = intel_sub_group_shuffle_xor (cl_intel_subgroups), same condition
// The verification unit is always 32 lanes. A 64-wide hardware wave (AMD GCN/CDNA, RDNA in wave64) runs two units;
// the exchange masks are 1, 2, 4, 8, 16, so every partner lane lies inside the lane's own aligned run of 32.
#ifndef IGNEUM_GROUP
#define IGNEUM_GROUP 32
#endif
#ifndef IGNEUM_EXCHANGE
#define IGNEUM_EXCHANGE 0
#endif
#ifdef __OPENCL_VERSION__
#define IGNEUM_KERNEL_HASH __kernel __attribute__((reqd_work_group_size(IGNEUM_GROUP, 1, 1)))
#define IGNEUM_LOCAL_WORDS(name, n) __local uint name[n]
#if IGNEUM_EXCHANGE == 1
#ifdef cl_khr_subgroups
#pragma OPENCL EXTENSION cl_khr_subgroups : enable
#endif
#ifdef cl_khr_subgroup_shuffle
#pragma OPENCL EXTENSION cl_khr_subgroup_shuffle : enable
#endif
#elif IGNEUM_EXCHANGE == 2
#pragma OPENCL EXTENSION cl_intel_subgroups : enable
#endif
#else
// Not an OpenCL compiler: proto-opencl/emu compiles this file as C++ and supplies the built-ins and these two macros.
#include "emu_opencl.h"
#endif
#if IGNEUM_EXCHANGE == 1
#define IGNEUM_SHFL_XOR(dst, a, m) dst = sub_group_shuffle_xor((a), (uint)(m))
#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u)
#elif IGNEUM_EXCHANGE == 2
#define IGNEUM_SHFL_XOR(dst, a, m) dst = intel_sub_group_shuffle_xor((a), (uint)(m))
#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u)
#else
// Local-memory exchange. Two buffers of IGNEUM_GROUP words alternate (xk counts exchanges), so one barrier per
// exchange is enough: a lane can only overwrite buffer b at exchange k+2 after passing barrier k+1, and every lane
// reaches barrier k+1 only after its read of buffer b at exchange k. The partner lid ^ m stays inside the lane's
// aligned run of 32 because m < 32. Control flow is uniform, so every work-item reaches every barrier.
#define IGNEUM_SHFL_XOR(dst, a, m) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid ^ (uint)(m))]; xk += 1u; }
#define IGNEUM_BCAST0(dst, a) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid & ~31u)]; xk += 1u; }
#endif
static inline uint splitmix32(uint 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. OpenCL rotate() rotates left by n modulo 32.
static inline uint rotl_imm(uint x, uint n) { return rotate(x, n); }
// Right rotation by n modulo 32 as a left rotation by (32 - n) modulo 32; n == 0 gives x.
static inline uint rotr_var(uint x, uint n) { return rotate(x, (0u - n) & 31u); }
static 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;
}
// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines.
// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals.
#define MH_CACHE_LINE_MASK 0x003fffffu
#define MH_SEGMENT_LINES 64u
#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); }
static inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site
// y = ChaCha12 core(x) + x
static inline void mh_chacha_block(const uint* x, uint* y) {
for (uint i = 0u; i < 16u; ++i) y[i] = x[i];
for (uint r = 0u; r < 6u; ++r) {
MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u)
MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u)
}
for (uint i = 0u; i < 16u; ++i) y[i] += x[i];
}
// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0.
static inline void mh_cache_segment(__global uint* cache, uint seg) {
uint prev[16]; uint x[16]; uint y[16];
for (uint i = 0u; i < 16u; ++i) prev[i] = 0u;
for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) {
x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3];
x[4] = 0x3067619fu ^ prev[4];
x[5] = 0x3c269176u ^ prev[5];
x[6] = 0x84a03b03u ^ prev[6];
x[7] = 0xf8c63294u ^ prev[7];
x[8] = 0xff977c5bu ^ prev[8];
x[9] = 0xe60def3eu ^ prev[9];
x[10] = 0x63630141u ^ prev[10];
x[11] = 0xb8fbcb58u ^ prev[11];
x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15];
mh_chacha_block(x, y);
__global uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u);
for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; }
}
}
// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations.
static inline void mh_mixer(uint* s, uint rk) {
s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u;
s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu;
s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u;
s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu;
s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u;
s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u;
s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u;
s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u;
s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du;
s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u;
s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du;
s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu;
s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du;
s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu;
s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u;
s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u;
MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u)
MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u)
MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u)
MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u)
}
// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer.
static inline void mh_item(__global const uint* cache, uint t, uint* s) {
s[0] = 0x3067619fu;
s[1] = 0x3c269176u;
s[2] = 0x84a03b03u;
s[3] = 0xf8c63294u;
s[4] = 0xff977c5bu;
s[5] = 0xe60def3eu;
s[6] = 0x63630141u;
s[7] = 0xb8fbcb58u;
s[8] = t * 0x42146205u + 0xbab68293u;
s[9] = t * 0x52cbe0fbu + 0xcc162340u;
s[10] = t * 0x7ecf4a03u + 0x6ce151ccu;
s[11] = t * 0x6728907fu + 0xe62b8997u;
s[12] = t * 0xd81d9751u + 0xc9c80297u;
s[13] = t * 0x132952c3u + 0xf74a1654u;
s[14] = t * 0xf60de277u + 0x3d704af5u;
s[15] = t * 0x05358035u + 0x3cf522b7u;
for (uint r = 0u; r < 8u; ++r) {
mh_mixer(s, 0x9E3779B9u * (r + 1u));
__global const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u);
for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i];
}
mh_mixer(s, 0x9E3779B9u * 9u);
}
// dataset[w] without the dataset: derive item w >> 4 and take word w & 15.
static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; }
// Memory-hard dataset (MEMHARD.md). One work-item per cache segment; one work-item per 64-byte dataset item.
// The same constants as memhard.h in this pack (one emitter, three dialects).
__kernel void igneum_cache_fill(__global uint* cache, uint nSegments) {
uint seg = (uint)get_global_id(0);
if (seg < nSegments) mh_cache_segment(cache, seg);
}
__kernel void igneum_build(__global uint* ds, __global const uint* cache, uint nItems) {
uint t = (uint)get_global_id(0);
if (t < nItems) {
uint s[16];
mh_item(cache, t, s);
__global uint* d = ds + ((ulong)t * 16u);
for (uint i = 0u; i < 16u; ++i) d[i] = s[i];
}
}
// One hash per work-item. IGNEUM_GROUP is a multiple of 32; lane = lid & 31 and every exchange stays inside the
// lane's own aligned run of 32 work-items, exactly like simd_shuffle_xor inside a 32-wide Metal SIMD group and
// __shfl_xor_sync inside a CUDA warp. Control flow is uniform (no branches at all).
IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask) {
uint gid = (uint)get_global_id(0);
uint lid = (uint)get_local_id(0);
uint nonce = baseNonce + gid;
uint r0, r1, r2, r3, r4, r5, r6, r7;
#if IGNEUM_EXCHANGE == 0
IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP);
uint xk = 0u;
#else
(void)lid;
#endif
{ uint x = nonce ^ 0xc255b2bfu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0xdba9d396u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1]
{ uint x = nonce ^ 0xdba9d396u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0x6ea527e4u; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2]
{ uint x = nonce ^ 0x6ea527e4u; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x05f1866au; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3]
{ uint x = nonce ^ 0x05f1866au; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0x2947e02eu; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4]
{ uint x = nonce ^ 0x2947e02eu; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0x0ecc5c6fu; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5]
{ uint x = nonce ^ 0x0ecc5c6fu; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0xc3b7068du; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6]
{ uint x = nonce ^ 0xc3b7068du; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0x282d1c2eu; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7]
{ uint x = nonce ^ 0x282d1c2eu; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0xc255b2bfu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0]
for (uint it = 0u; it < 8u; ++it) {
uint sel = r0;
r3 = rotl_imm(r3, 26u); // 0 rotl
r2 = rotr_var(r2, r0); // 1 rotr
r0 = r0 ^ r7; // 2 xor
r4 = r4 ^ r7; // 3 xor
r2 = r2 ^ ds[r4 & mask]; // 4 load
{ uint b_ = (r2 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r3 = x_; } // 5 load
{ uint t_; IGNEUM_SHFL_XOR(t_, r7, 4u); r0 = r0 ^ t_; } // 6 shfl
r6 = rotl_imm(r6, 31u); // 7 rotl
r1 = r4 * r2 + r1; // 8 mad
r1 = r7 * r0 + r1; // 9 mad
r4 = r4 ^ ds[r1 & mask]; // 10 load
r6 = r7 * r0 + r6; // 11 mad
r2 = r2 * r6; // 12 mul
{ uint b_ = (r2 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r0 = x_; } // 13 load
r2 = r2 + r7 + ((((sel >> 22u) & 1u) != 0u) ? 0x4a3db5a5u : 0x5df3957du); // 14 add
{ uint t_; IGNEUM_SHFL_XOR(t_, r7, 1u); r5 = r5 ^ t_; } // 15 shfl
r3 = r5 * r6 + r3; // 16 mad
r2 = r2 * r5; // 17 mul
{ uint t_; IGNEUM_SHFL_XOR(t_, r5, 1u); r1 = r1 ^ t_; } // 18 shfl
r0 = r0 - r1; // 19 sub
r4 = r6 * r4 + r4; // 20 mad
{ uint t_; IGNEUM_SHFL_XOR(t_, r0, 16u); r3 = r3 ^ t_; } // 21 shfl
r1 = rotl_imm(r1, 24u); // 22 rotl
r1 = r1 + r0 + ((((sel >> 22u) & 1u) != 0u) ? 0xfc07c54cu : 0x5c141117u); // 23 add
r2 = r2 ^ ds[r0 & mask]; // 24 load
r0 = r0 ^ r1; // 25 xor
r3 = r3 ^ r7; // 26 xor
{ uint b_ = (r3 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r2 = x_; } // 27 load
r0 = r0 ^ r6; // 28 xor
r4 = r4 ^ r1; // 29 xor
r6 = r6 * r3; // 30 mul
r3 = rotl_imm(r3, 23u); // 31 rotl
r7 = rotr_var(r7, r1); // 32 rotr
{ uint t_; IGNEUM_SHFL_XOR(t_, r2, 2u); r6 = r6 ^ t_; } // 33 shfl
{ uint b_ = (r0 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r5 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r5 = x_; } // 34 load
r6 = r6 + r3 + ((((sel >> 29u) & 1u) != 0u) ? 0x0b74657bu : 0xfb55c58du); // 35 add
{ uint b_ = (r5 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r6 = x_; } // 36 load
r1 = r1 ^ ds[r4 & mask]; // 37 load
{ uint b_ = (r3 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r6 = x_; } // 38 load
r4 = r4 ^ ds[r7 & mask]; // 39 load
r7 = mul_hi(r7, r3); // 40 mulhi
r0 = r0 | r6; // 41 or
r0 = r3 * r0 + r0; // 42 mad
{ uint b_ = (r6 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r4 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r4 = x_; } // 43 load
r2 = r4 * r0 + r2; // 44 mad
r2 = r4 * r2 + r2; // 45 mad
{ uint t_; IGNEUM_SHFL_XOR(t_, r5, 16u); r7 = r7 ^ t_; } // 46 shfl
r6 = r1 * r4 + r6; // 47 mad
r7 = r7 ^ ds[r1 & mask]; // 48 load
r6 = mul_hi(r6, r5); // 49 mulhi
r5 = mul_hi(r5, r0); // 50 mulhi
{ uint b_ = (r6 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r1 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r1 = x_; } // 51 load
{ uint t_; IGNEUM_SHFL_XOR(t_, r3, 1u); r7 = r7 ^ t_; } // 52 shfl
r2 = r2 ^ r5; // 53 xor
{ uint t_; IGNEUM_SHFL_XOR(t_, r6, 4u); r0 = r0 ^ t_; } // 54 shfl
r2 = r2 * r5; // 55 mul
r5 = r5 * r6; // 56 mul
{ uint b_ = (r4 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r6 = x_; } // 57 load
r4 = r7 * r7 + r4; // 58 mad
r2 = r2 ^ ds[r1 & mask]; // 59 load
r1 = r1 * r4; // 60 mul
r3 = r3 * r4; // 61 mul
r6 = r6 ^ r4; // 62 xor
r3 = rotr_var(r3, r1); // 63 rotr
}
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;
}
#if IGNEUM_EXCHANGE != 0
// Reports the sub-group size this device uses for a work-group of IGNEUM_GROUP items. host.c runs it only when the
// per-kernel query (clGetKernelSubGroupInfoKHR on igneum_hash) is unavailable; that query is preferred because a
// compiler may pick a different wave width per kernel (RDNA: wave32 or wave64). See WAVEFRONT.md.
IGNEUM_KERNEL_HASH void igneum_probe_subgroup(__global uint* out) {
if (get_local_id(0) == 0u) { out[0] = get_sub_group_size(); out[1] = get_num_sub_groups(); }
}
#endif
// Header-bound variant (bind.rs): the init words come from initw, not SEEDW. Same body as igneum_hash.
IGNEUM_KERNEL_HASH void igneum_hash_bound(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask, __global const uint* initw) {
uint gid = (uint)get_global_id(0);
uint lid = (uint)get_local_id(0);
uint nonce = baseNonce + gid;
uint r0, r1, r2, r3, r4, r5, r6, r7;
uint iw0 = initw[0], iw1 = initw[1], iw2 = initw[2], iw3 = initw[3], iw4 = initw[4], iw5 = initw[5], iw6 = initw[6], iw7 = initw[7];
#if IGNEUM_EXCHANGE == 0
IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP);
uint xk = 0u;
#else
(void)lid;
#endif
{ uint x = nonce ^ iw0; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ iw1; }
{ uint x = nonce ^ iw1; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ iw2; }
{ uint x = nonce ^ iw2; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ iw3; }
{ uint x = nonce ^ iw3; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ iw4; }
{ uint x = nonce ^ iw4; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ iw5; }
{ uint x = nonce ^ iw5; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ iw6; }
{ uint x = nonce ^ iw6; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ iw7; }
{ uint x = nonce ^ iw7; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ iw0; }
for (uint it = 0u; it < 8u; ++it) {
uint sel = r0;
r3 = rotl_imm(r3, 26u); // 0 rotl
r2 = rotr_var(r2, r0); // 1 rotr
r0 = r0 ^ r7; // 2 xor
r4 = r4 ^ r7; // 3 xor
r2 = r2 ^ ds[r4 & mask]; // 4 load
{ uint b_ = (r2 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r3 = x_; } // 5 load
{ uint t_; IGNEUM_SHFL_XOR(t_, r7, 4u); r0 = r0 ^ t_; } // 6 shfl
r6 = rotl_imm(r6, 31u); // 7 rotl
r1 = r4 * r2 + r1; // 8 mad
r1 = r7 * r0 + r1; // 9 mad
r4 = r4 ^ ds[r1 & mask]; // 10 load
r6 = r7 * r0 + r6; // 11 mad
r2 = r2 * r6; // 12 mul
{ uint b_ = (r2 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r0 = x_; } // 13 load
r2 = r2 + r7 + ((((sel >> 22u) & 1u) != 0u) ? 0x4a3db5a5u : 0x5df3957du); // 14 add
{ uint t_; IGNEUM_SHFL_XOR(t_, r7, 1u); r5 = r5 ^ t_; } // 15 shfl
r3 = r5 * r6 + r3; // 16 mad
r2 = r2 * r5; // 17 mul
{ uint t_; IGNEUM_SHFL_XOR(t_, r5, 1u); r1 = r1 ^ t_; } // 18 shfl
r0 = r0 - r1; // 19 sub
r4 = r6 * r4 + r4; // 20 mad
{ uint t_; IGNEUM_SHFL_XOR(t_, r0, 16u); r3 = r3 ^ t_; } // 21 shfl
r1 = rotl_imm(r1, 24u); // 22 rotl
r1 = r1 + r0 + ((((sel >> 22u) & 1u) != 0u) ? 0xfc07c54cu : 0x5c141117u); // 23 add
r2 = r2 ^ ds[r0 & mask]; // 24 load
r0 = r0 ^ r1; // 25 xor
r3 = r3 ^ r7; // 26 xor
{ uint b_ = (r3 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r2 = x_; } // 27 load
r0 = r0 ^ r6; // 28 xor
r4 = r4 ^ r1; // 29 xor
r6 = r6 * r3; // 30 mul
r3 = rotl_imm(r3, 23u); // 31 rotl
r7 = rotr_var(r7, r1); // 32 rotr
{ uint t_; IGNEUM_SHFL_XOR(t_, r2, 2u); r6 = r6 ^ t_; } // 33 shfl
{ uint b_ = (r0 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r5 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r5 = x_; } // 34 load
r6 = r6 + r3 + ((((sel >> 29u) & 1u) != 0u) ? 0x0b74657bu : 0xfb55c58du); // 35 add
{ uint b_ = (r5 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r6 = x_; } // 36 load
r1 = r1 ^ ds[r4 & mask]; // 37 load
{ uint b_ = (r3 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r6 = x_; } // 38 load
r4 = r4 ^ ds[r7 & mask]; // 39 load
r7 = mul_hi(r7, r3); // 40 mulhi
r0 = r0 | r6; // 41 or
r0 = r3 * r0 + r0; // 42 mad
{ uint b_ = (r6 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r4 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r4 = x_; } // 43 load
r2 = r4 * r0 + r2; // 44 mad
r2 = r4 * r2 + r2; // 45 mad
{ uint t_; IGNEUM_SHFL_XOR(t_, r5, 16u); r7 = r7 ^ t_; } // 46 shfl
r6 = r1 * r4 + r6; // 47 mad
r7 = r7 ^ ds[r1 & mask]; // 48 load
r6 = mul_hi(r6, r5); // 49 mulhi
r5 = mul_hi(r5, r0); // 50 mulhi
{ uint b_ = (r6 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r1 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r1 = x_; } // 51 load
{ uint t_; IGNEUM_SHFL_XOR(t_, r3, 1u); r7 = r7 ^ t_; } // 52 shfl
r2 = r2 ^ r5; // 53 xor
{ uint t_; IGNEUM_SHFL_XOR(t_, r6, 4u); r0 = r0 ^ t_; } // 54 shfl
r2 = r2 * r5; // 55 mul
r5 = r5 * r6; // 56 mul
{ uint b_ = (r4 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r6 = x_; } // 57 load
r4 = r7 * r7 + r4; // 58 mad
r2 = r2 ^ ds[r1 & mask]; // 59 load
r1 = r1 * r4; // 60 mul
r3 = r3 * r4; // 61 mul
r6 = r6 ^ r4; // 62 xor
r3 = rotr_var(r3, r1); // 63 rotr
}
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;
}

View file

@ -0,0 +1,123 @@
// Generated by igneum-pow export (generator v2) for seed "igneum-readwidth/A/5". Do not edit by hand.
// Header-bound twin of igneum_hash in kernel.cu: the init words come from a kernel argument, not SEEDW.
// Host declarations (also in program_bound.h if present):
// struct IgneumInitWords { uint32_t w[8]; };
// cudaError_t igneum_launch_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask,
// IgneumInitWords iw, uint32_t nonces, uint32_t blockWarps);
// cudaError_t igneum_hash_bound_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps);
#include <cuda_runtime.h>
#include <cstdint>
#include "program.h"
struct IgneumInitWords { uint32_t w[8]; };
__device__ __forceinline__ uint32_t splitmix32(uint32_t x) {
x ^= x >> 16; x *= 0x7feb352du;
x ^= x >> 15; x *= 0x846ca68bu;
x ^= x >> 16;
return x;
}
__device__ __forceinline__ uint32_t rotl_imm(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); }
__device__ __forceinline__ uint32_t rotr_var(uint32_t x, uint32_t n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); }
__global__ void igneum_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, IgneumInitWords iw) {
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 ^ iw.w[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ iw.w[1]; }
{ uint32_t x = nonce ^ iw.w[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ iw.w[2]; }
{ uint32_t x = nonce ^ iw.w[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ iw.w[3]; }
{ uint32_t x = nonce ^ iw.w[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ iw.w[4]; }
{ uint32_t x = nonce ^ iw.w[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ iw.w[5]; }
{ uint32_t x = nonce ^ iw.w[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ iw.w[6]; }
{ uint32_t x = nonce ^ iw.w[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ iw.w[7]; }
{ uint32_t x = nonce ^ iw.w[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ iw.w[0]; }
for (uint32_t it = 0u; it < 8u; ++it) {
uint32_t sel = r0;
r3 = rotl_imm(r3, 26u); // 0 rotl
r2 = rotr_var(r2, r0); // 1 rotr
r0 = r0 ^ r7; // 2 xor
r4 = r4 ^ r7; // 3 xor
r2 = r2 ^ ds[r4 & mask]; // 4 load
{ uint32_t b_ = (r2 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r3 = x_; } // 5 load
r0 = r0 ^ __shfl_xor_sync(0xffffffffu, r7, 4); // 6 shfl
r6 = rotl_imm(r6, 31u); // 7 rotl
r1 = r4 * r2 + r1; // 8 mad
r1 = r7 * r0 + r1; // 9 mad
r4 = r4 ^ ds[r1 & mask]; // 10 load
r6 = r7 * r0 + r6; // 11 mad
r2 = r2 * r6; // 12 mul
{ uint32_t b_ = (r2 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r0 = x_; } // 13 load
r2 = r2 + r7 + ((((sel >> 22u) & 1u) != 0u) ? 0x4a3db5a5u : 0x5df3957du); // 14 add
r5 = r5 ^ __shfl_xor_sync(0xffffffffu, r7, 1); // 15 shfl
r3 = r5 * r6 + r3; // 16 mad
r2 = r2 * r5; // 17 mul
r1 = r1 ^ __shfl_xor_sync(0xffffffffu, r5, 1); // 18 shfl
r0 = r0 - r1; // 19 sub
r4 = r6 * r4 + r4; // 20 mad
r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r0, 16); // 21 shfl
r1 = rotl_imm(r1, 24u); // 22 rotl
r1 = r1 + r0 + ((((sel >> 22u) & 1u) != 0u) ? 0xfc07c54cu : 0x5c141117u); // 23 add
r2 = r2 ^ ds[r0 & mask]; // 24 load
r0 = r0 ^ r1; // 25 xor
r3 = r3 ^ r7; // 26 xor
{ uint32_t b_ = (r3 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r2 = x_; } // 27 load
r0 = r0 ^ r6; // 28 xor
r4 = r4 ^ r1; // 29 xor
r6 = r6 * r3; // 30 mul
r3 = rotl_imm(r3, 23u); // 31 rotl
r7 = rotr_var(r7, r1); // 32 rotr
r6 = r6 ^ __shfl_xor_sync(0xffffffffu, r2, 2); // 33 shfl
{ uint32_t b_ = (r0 & mask) & ~15u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint32_t x_ = r5 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r5 = x_; } // 34 load
r6 = r6 + r3 + ((((sel >> 29u) & 1u) != 0u) ? 0x0b74657bu : 0xfb55c58du); // 35 add
{ uint32_t b_ = (r5 & mask) & ~15u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint32_t x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r6 = x_; } // 36 load
r1 = r1 ^ ds[r4 & mask]; // 37 load
{ uint32_t b_ = (r3 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r6 = x_; } // 38 load
r4 = r4 ^ ds[r7 & mask]; // 39 load
r7 = __umulhi(r7, r3); // 40 mulhi
r0 = r0 | r6; // 41 or
r0 = r3 * r0 + r0; // 42 mad
{ uint32_t b_ = (r6 & mask) & ~15u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint32_t x_ = r4 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r4 = x_; } // 43 load
r2 = r4 * r0 + r2; // 44 mad
r2 = r4 * r2 + r2; // 45 mad
r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r5, 16); // 46 shfl
r6 = r1 * r4 + r6; // 47 mad
r7 = r7 ^ ds[r1 & mask]; // 48 load
r6 = __umulhi(r6, r5); // 49 mulhi
r5 = __umulhi(r5, r0); // 50 mulhi
{ uint32_t b_ = (r6 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r1 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r1 = x_; } // 51 load
r7 = r7 ^ __shfl_xor_sync(0xffffffffu, r3, 1); // 52 shfl
r2 = r2 ^ r5; // 53 xor
r0 = r0 ^ __shfl_xor_sync(0xffffffffu, r6, 4); // 54 shfl
r2 = r2 * r5; // 55 mul
r5 = r5 * r6; // 56 mul
{ uint32_t b_ = (r4 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r6 = x_; } // 57 load
r4 = r7 * r7 + r4; // 58 mad
r2 = r2 ^ ds[r1 & mask]; // 59 load
r1 = r1 * r4; // 60 mul
r3 = r3 * r4; // 61 mul
r6 = r6 ^ r4; // 62 xor
r3 = rotr_var(r3, r1); // 63 rotr
}
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;
}
cudaError_t igneum_launch_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask,
IgneumInitWords iw, 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_bound<<<nonces / block, block>>>(ds, out, baseNonce, mask, iw);
return cudaGetLastError();
}
cudaError_t igneum_hash_bound_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps) {
cudaFuncAttributes attr;
cudaError_t e = cudaFuncGetAttributes(&attr, igneum_hash_bound);
if (e != cudaSuccess) return e;
*numRegs = attr.numRegs;
return cudaOccupancyMaxActiveBlocksPerMultiprocessor(blocksPerSM, igneum_hash_bound, (int)(32u * blockWarps), 0);
}

View file

@ -0,0 +1,108 @@
// Generated by igneum-pow export (generator v2) for seed "igneum-readwidth/A/5". Do not edit by hand.
// Memory-hard dataset core, the same text that the Mac's Metal kernels and CPU verifier were checked against.
// Included by kernel.cu (device), host.cu (host reference) and proto-opencl/host.c (C99 host reference).
// See proto-metal/MEMHARD.md for the construction. kernel.cl carries the same text in OpenCL C.
#pragma once
#ifdef __cplusplus
#include <cstdint>
#else
#include <stdint.h>
#endif
#if defined(__CUDACC__)
#define IGNEUM_HD __host__ __device__ __forceinline__
#elif defined(_MSC_VER) && !defined(__cplusplus)
#define IGNEUM_HD static __inline
#else
#define IGNEUM_HD static inline
#endif
// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines.
// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals.
#define MH_CACHE_LINE_MASK 0x003fffffu
#define MH_SEGMENT_LINES 64u
#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); }
IGNEUM_HD uint32_t mh_rotl(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site
// y = ChaCha12 core(x) + x
IGNEUM_HD void mh_chacha_block(const uint32_t* x, uint32_t* y) {
for (uint32_t i = 0u; i < 16u; ++i) y[i] = x[i];
for (uint32_t r = 0u; r < 6u; ++r) {
MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u)
MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u)
}
for (uint32_t i = 0u; i < 16u; ++i) y[i] += x[i];
}
// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0.
IGNEUM_HD void mh_cache_segment(uint32_t* cache, uint32_t seg) {
uint32_t prev[16]; uint32_t x[16]; uint32_t y[16];
for (uint32_t i = 0u; i < 16u; ++i) prev[i] = 0u;
for (uint32_t j = 0u; j < MH_SEGMENT_LINES; ++j) {
x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3];
x[4] = 0x3067619fu ^ prev[4];
x[5] = 0x3c269176u ^ prev[5];
x[6] = 0x84a03b03u ^ prev[6];
x[7] = 0xf8c63294u ^ prev[7];
x[8] = 0xff977c5bu ^ prev[8];
x[9] = 0xe60def3eu ^ prev[9];
x[10] = 0x63630141u ^ prev[10];
x[11] = 0xb8fbcb58u ^ prev[11];
x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15];
mh_chacha_block(x, y);
uint32_t* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u);
for (uint32_t i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; }
}
}
// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations.
IGNEUM_HD void mh_mixer(uint32_t* s, uint32_t rk) {
s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u;
s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu;
s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u;
s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu;
s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u;
s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u;
s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u;
s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u;
s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du;
s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u;
s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du;
s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu;
s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du;
s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu;
s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u;
s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u;
MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u)
MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u)
MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u)
MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u)
}
// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer.
IGNEUM_HD void mh_item(const uint32_t* cache, uint32_t t, uint32_t* s) {
s[0] = 0x3067619fu;
s[1] = 0x3c269176u;
s[2] = 0x84a03b03u;
s[3] = 0xf8c63294u;
s[4] = 0xff977c5bu;
s[5] = 0xe60def3eu;
s[6] = 0x63630141u;
s[7] = 0xb8fbcb58u;
s[8] = t * 0x42146205u + 0xbab68293u;
s[9] = t * 0x52cbe0fbu + 0xcc162340u;
s[10] = t * 0x7ecf4a03u + 0x6ce151ccu;
s[11] = t * 0x6728907fu + 0xe62b8997u;
s[12] = t * 0xd81d9751u + 0xc9c80297u;
s[13] = t * 0x132952c3u + 0xf74a1654u;
s[14] = t * 0xf60de277u + 0x3d704af5u;
s[15] = t * 0x05358035u + 0x3cf522b7u;
for (uint32_t r = 0u; r < 8u; ++r) {
mh_mixer(s, 0x9E3779B9u * (r + 1u));
const uint32_t* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u);
for (uint32_t i = 0u; i < 16u; ++i) s[i] ^= line[i];
}
mh_mixer(s, 0x9E3779B9u * 9u);
}
// dataset[w] without the dataset: derive item w >> 4 and take word w & 15.
IGNEUM_HD uint32_t mh_word(const uint32_t* cache, uint32_t w) { uint32_t s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; }

View file

@ -0,0 +1,106 @@
#include <metal_stdlib>
using namespace metal;
// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines.
// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals.
#define MH_CACHE_LINE_MASK 0x003fffffu
#define MH_SEGMENT_LINES 64u
#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); }
inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site
// y = ChaCha12 core(x) + x
inline void mh_chacha_block(const thread uint* x, thread uint* y) {
for (uint i = 0u; i < 16u; ++i) y[i] = x[i];
for (uint r = 0u; r < 6u; ++r) {
MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u)
MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u)
}
for (uint i = 0u; i < 16u; ++i) y[i] += x[i];
}
// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0.
inline void mh_cache_segment(device uint* cache, uint seg) {
uint prev[16]; uint x[16]; uint y[16];
for (uint i = 0u; i < 16u; ++i) prev[i] = 0u;
for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) {
x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3];
x[4] = 0x3067619fu ^ prev[4];
x[5] = 0x3c269176u ^ prev[5];
x[6] = 0x84a03b03u ^ prev[6];
x[7] = 0xf8c63294u ^ prev[7];
x[8] = 0xff977c5bu ^ prev[8];
x[9] = 0xe60def3eu ^ prev[9];
x[10] = 0x63630141u ^ prev[10];
x[11] = 0xb8fbcb58u ^ prev[11];
x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15];
mh_chacha_block(x, y);
device uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u);
for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; }
}
}
// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations.
inline void mh_mixer(thread uint* s, uint rk) {
s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u;
s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu;
s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u;
s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu;
s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u;
s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u;
s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u;
s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u;
s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du;
s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u;
s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du;
s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu;
s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du;
s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu;
s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u;
s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u;
MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u)
MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u)
MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u)
MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u)
}
// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer.
inline void mh_item(device const uint* cache, uint t, thread uint* s) {
s[0] = 0x3067619fu;
s[1] = 0x3c269176u;
s[2] = 0x84a03b03u;
s[3] = 0xf8c63294u;
s[4] = 0xff977c5bu;
s[5] = 0xe60def3eu;
s[6] = 0x63630141u;
s[7] = 0xb8fbcb58u;
s[8] = t * 0x42146205u + 0xbab68293u;
s[9] = t * 0x52cbe0fbu + 0xcc162340u;
s[10] = t * 0x7ecf4a03u + 0x6ce151ccu;
s[11] = t * 0x6728907fu + 0xe62b8997u;
s[12] = t * 0xd81d9751u + 0xc9c80297u;
s[13] = t * 0x132952c3u + 0xf74a1654u;
s[14] = t * 0xf60de277u + 0x3d704af5u;
s[15] = t * 0x05358035u + 0x3cf522b7u;
for (uint r = 0u; r < 8u; ++r) {
mh_mixer(s, 0x9E3779B9u * (r + 1u));
device const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u);
for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i];
}
mh_mixer(s, 0x9E3779B9u * 9u);
}
// dataset[w] without the dataset: derive item w >> 4 and take word w & 15.
inline uint mh_word(device const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; }
// One thread per segment (2^16 threads).
kernel void igneum_cache_fill(device uint* cache [[buffer(0)]], uint gid [[thread_position_in_grid]]) {
mh_cache_segment(cache, gid);
}
// One thread per 64-byte item (dataset words / 16 threads).
kernel void igneum_build(device const uint* cache [[buffer(0)]], device uint* dataset [[buffer(1)]],
uint gid [[thread_position_in_grid]]) {
uint s[16];
mh_item(cache, gid, s);
device uint* d = dataset + gid * 16u;
for (uint i = 0u; i < 16u; ++i) d[i] = s[i];
}

View file

@ -0,0 +1,60 @@
// Generated by igneum-pow export (generator v2) for seed "igneum-readwidth/A/5". Do not edit by hand.
// Program metadata for host.cu plus the launch wrappers defined in kernel.cu.
// Also included by proto-opencl/host.c (C99), which defines IGNEUM_NO_CUDA first and reads only the macros.
#pragma once
#ifdef __cplusplus
#include <cstdint>
#else
#include <stdint.h>
#endif
#ifndef IGNEUM_NO_CUDA
#include <cuda_runtime.h>
#endif
#define IGNEUM_SEED_STRING "igneum-readwidth/A/5"
#define IGNEUM_SEED_BYTES_HEX "69676e65756d2d7265616477696474682f412f35"
#define IGNEUM_GENERATOR 2
#define IGNEUM_PROGRAM_ATTEMPT 0
#define IGNEUM_PROGRAM_ID 0x8c7f626ae00b8a7cull
#define IGNEUM_DAY_STRING "2026-10-03"
#define IGNEUM_DAY_BYTES_HEX "6461792f323032362d31302d3033"
#define IGNEUM_DAY0 0x3067619fu
#define IGNEUM_DAY1 0x3c269176u
#define IGNEUM_DATASET_LOG2 28
#define IGNEUM_MASK 0x0fffffffu
#define IGNEUM_LANES 32
#define IGNEUM_ITERATIONS 8
#define IGNEUM_INSTR_COUNT 64
#define IGNEUM_LOADS_PER_HASH 128
#define IGNEUM_WIDE_LOADS_PER_HASH 0
#define IGNEUM_OP_MIX "load=16 mad=10 shfl=8 xor=8 mul=7 rotl=4 add=3 mulhi=3 rotr=3 or=1 sub=1"
// Read-width experiment (5 October 2026, docs/plans/read-width.md): NOT the lottery hash. A load of W words reads
// the W-word-aligned address and folds every word into dst: x = dst ^ w[0]; x = (rotl(x, 11) * 0x9e3779b1) ^ w[j]; dst = x.
#define IGNEUM_LOAD_CLASS "mix50-35-15"
#define IGNEUM_LOAD_SLOTS 16
#define IGNEUM_LOAD_MIX { 50, 35, 15 }
#define IGNEUM_LOAD_WIDTH_COUNTS { 7, 6, 3 } // loads of 4, 16, 64 bytes per program
#define IGNEUM_BYTES_PER_HASH 2528
#define IGNEUM_FOLD_ROT 11
#define IGNEUM_FOLD_MUL 0x9e3779b1u
// 0 = closed-form dataset (ds_elem), 1 = memory-hard cache construction (MEMHARD.md, memhard.h)
#define IGNEUM_DATASET_MODE 1
#define IGNEUM_SEEDW_INIT { 0xc255b2bfu, 0xdba9d396u, 0x6ea527e4u, 0x05f1866au, 0x2947e02eu, 0x0ecc5c6fu, 0xc3b7068du, 0x282d1c2eu }
#define IGNEUM_KEY_INIT { 0x3067619fu, 0x3c269176u, 0x84a03b03u, 0xf8c63294u, 0xff977c5bu, 0xe60def3eu, 0x63630141u, 0xb8fbcb58u }
#define IGNEUM_CACHE_LOG2_WORDS 26
#define IGNEUM_CACHE_SEGMENT_LOG2_LINES 6
#define IGNEUM_CACHE_SEGMENTS 65536u
#define IGNEUM_ITEM_ROUNDS 8
#define IGNEUM_MIX_ROT_INIT { 20u, 20u, 19u, 4u, 26u, 3u, 3u, 27u }
#define IGNEUM_MIX_MUL_INIT { 0x42146205u, 0x52cbe0fbu, 0x7ecf4a03u, 0x6728907fu, 0xd81d9751u, 0x132952c3u, 0xf60de277u, 0x05358035u, 0xbaf6499du, 0xe4db9667u, 0x3e98f45du, 0xd0004eddu, 0x2691630du, 0x9beb3bcfu, 0xab310379u, 0x99cfb423u }
#define IGNEUM_MIX_RC_INIT { 0xbab68293u, 0xcc162340u, 0x6ce151ccu, 0xe62b8997u, 0xc9c80297u, 0xf74a1654u, 0x3d704af5u, 0x3cf522b7u, 0x2b9cac04u, 0xa880ac10u, 0x13e5dd1du, 0x6fc3e233u, 0x2d83eeacu, 0x9006e8bfu, 0x2c4b5362u, 0x31b49ee2u }
#ifndef IGNEUM_NO_CUDA
// Defined in kernel.cu. All launch on the default stream and return cudaGetLastError().
cudaError_t igneum_launch_cache_fill(uint32_t* cache, uint32_t nSegments);
cudaError_t igneum_launch_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems);
cudaError_t igneum_launch_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask,
uint32_t nonces, uint32_t blockWarps);
cudaError_t igneum_hash_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps);
#endif

View file

@ -0,0 +1,127 @@
{
"format": "igneum-program-pack-3",
"generator": 2,
"attempt": 0,
"program_id": "0x8c7f626ae00b8a7c",
"program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32",
"dataset_mode": "memory-hard",
"seed": "igneum-readwidth/A/5",
"seed_bytes": "69676e65756d2d7265616477696474682f412f35",
"seed_words": ["0xc255b2bf", "0xdba9d396", "0x6ea527e4", "0x05f1866a", "0x2947e02e", "0x0ecc5c6f", "0xc3b7068d", "0x282d1c2e"],
"seed_derivation": "seed_words = FNV-1a 64 over seed_bytes (attempt 0) or seed_bytes || attempt_le32 (attempt k >= 1), basis ^ (salt * 0x9E3779B97F4A7C15) for salt 0..3, then h ^= h>>33; h *= 0xff51afd7ed558ccd; h ^= h>>33; words[2*salt] = low 32, words[2*salt+1] = high 32",
"generator_rule": "version 2: exactly 16 load slots drawn first from instructions 1..63 (partial Fisher-Yates), the other 48 ops from the ten non-load weights (sum 75); a load's source is drawn from the registers other than dst written by an earlier instruction and not read by a load since; the candidate must pass the acceptance rule of spec 01 section 1.4.6 (static: no cyclically stale load source, every register has an injecting write; dynamic: 64 units on the seed-keyed closed-form dataset with no constant register bit, no lane-constant load site, under 164 saturated final values, every output bit within 136 of 1024, distinct addresses above 245760), else the next attempt of the seed is tried",
"lanes": 32,
"registers": 8,
"iterations": 8,
"instruction_count": 64,
"loads_per_hash": 128,
"load_class": "mix50-35-15",
"load_slots": 16,
"load_mix_percent_4_16_64": [50, 35, 15],
"load_width_counts_4_16_64": [7, 6, 3],
"bytes_per_hash": 2528,
"wide_load": "read-width experiment (5 October 2026, docs/plans/read-width.md), NOT the lottery hash: a load of W words (width field, 4 or 16) reads dataset[b .. b + W) with b = (src & mask) & ~(W - 1) and folds every word into dst: x = dst ^ w[0]; for j in 1..W: x = (rotl(x, 11) * 0x9e3779b1) ^ w[j]; dst = x; width 1 is the plain load; the width is drawn per instruction from the class mix with one extra below(100) draw after the nine of version 2, and the program id is FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed words || attempt_le32 || mix[3] || load_slots",
"op_mix": {"load": 16, "mad": 10, "shfl": 8, "xor": 8, "mul": 7, "rotl": 4, "add": 3, "mulhi": 3, "rotr": 3, "or": 1, "sub": 1},
"register_init": "for i in 0..7: x = nonce ^ seed_words[i]; x += 0x9e3779b9 * (i+1) (mod 2^32); x = splitmix32(x); r[i] = x ^ seed_words[(i+1) & 7]",
"splitmix32": "x ^= x>>16; x *= 0x7feb352d; x ^= x>>15; x *= 0x846ca68b; x ^= x>>16",
"iteration": "sel = r0 sampled once at the top of each iteration, then all instructions in order",
"output": "lo = r0 ^ rotl(r1,7) ^ rotl(r2,14) ^ rotl(r3,21); hi = r4 ^ rotl(r5,9) ^ rotl(r6,18) ^ rotl(r7,27); out = (hi << 32) | lo",
"op_semantics": {
"add": "dst = dst + src + (bit `bit` of sel ? imm2 : imm)",
"sub": "dst = dst - src",
"mul": "dst = dst * src (low 32)",
"mulhi": "dst = high 32 bits of dst * src",
"xor": "dst = dst ^ src",
"or": "dst = dst | src",
"rotl": "dst = rotl(dst, rot), rot in 1..31",
"rotr": "dst = rotr(dst, src & 31)",
"mad": "dst = src * src2 + dst",
"shfl": "dst = dst ^ (src of lane (lane ^ mask)), mask in {1,2,4,8,16}, within the 32-lane warp",
"load": "dst = dst ^ dataset[src & dataset.mask]",
"wload": "base = (src of lane 0 & dataset.mask) & ~31; dst = dst ^ dataset[base + lane] (warp-coalesced 128-byte load, lever b, only when --wide-frac > 0)"
},
"dataset": {
"log2_words": 28,
"bytes": 1073741824,
"mask": "0x0fffffff",
"day": "2026-10-03",
"day_bytes": "6461792f323032362d31302d3033",
"day_words_from": "seed_words_from_bytes(day_bytes)",
"d0": "0x3067619f",
"d1": "0x3c269176",
"mode": "memory-hard",
"spec": "proto-metal/MEMHARD.md",
"key": ["0x3067619f", "0x3c269176", "0x84a03b03", "0xf8c63294", "0xff977c5b", "0xe60def3e", "0x63630141", "0xb8fbcb58"],
"key_derivation": "the 8 words of seed_words_from_bytes(day_bytes); d0, d1 are key[0], key[1]",
"cache": {"log2_words": 26, "bytes": 268435456, "line_words": 16, "segment_lines": 64, "segments": 65536, "block": "ChaCha12 core + feed-forward, rotations 16 12 8 7", "sigma": ["0x61707865", "0x3320646e", "0x79622d32", "0x6b206574"], "tag": ["0x49676e65", "0x756d4d48"], "chain": "in_j = prev_line ^ (sigma[0..3] || key[0..7] || seg || j || tag[0..1]); line_j = block(in_j); prev_0 = 0"},
"mixer": {"draw": "SplitMix64 seeded with key[0] | key[1] << 32: rot[0..7] = 1 + next() % 31, mul[0..15] = low32(next()) | 1, rc[0..15] = low32(next())", "rot": [20, 20, 19, 4, 26, 3, 3, 27], "mul": ["0x42146205", "0x52cbe0fb", "0x7ecf4a03", "0x6728907f", "0xd81d9751", "0x132952c3", "0xf60de277", "0x05358035", "0xbaf6499d", "0xe4db9667", "0x3e98f45d", "0xd0004edd", "0x2691630d", "0x9beb3bcf", "0xab310379", "0x99cfb423"], "rc": ["0xbab68293", "0xcc162340", "0x6ce151cc", "0xe62b8997", "0xc9c80297", "0xf74a1654", "0x3d704af5", "0x3cf522b7", "0x2b9cac04", "0xa880ac10", "0x13e5dd1d", "0x6fc3e233", "0x2d83eeac", "0x9006e8bf", "0x2c4b5362", "0x31b49ee2"], "round": "for i in 0..15: s[i] = (s[i] ^ (rc[i] + (r+1) * 0x9E3779B9)) * mul[i]; then quarter rounds on columns (0,4,8,12) (1,5,9,13) (2,6,10,14) (3,7,11,15) with rot[0..3] and diagonals (0,5,10,15) (1,6,11,12) (2,7,8,13) (3,4,9,14) with rot[4..7]", "quarter_round": "a += b; d ^= a; d = rotl(d, r1); c += d; b ^= c; b = rotl(b, r2); a += b; d ^= a; d = rotl(d, r3); c += d; b ^= c; b = rotl(b, r4)"},
"item": "s[0..7] = key; s[8+i] = t * mul[i] + rc[i] for i in 0..7; for r in 0..7: s = M_r(s); line = s[0] & 0x003fffff; s[i] ^= cache[line * 16 + i]; then s = M_8(s); item(t) = s",
"word": "dataset[w] = item(w >> 4)[w & 15]"
},
"instructions": [
{"i": 0, "op": "rotl", "dst": 3, "src": 5, "src2": 6, "imm": "0x7e513467", "imm2": "0x0fed3e98", "rot": 26, "bit": 29, "mask": 8, "width": 1},
{"i": 1, "op": "rotr", "dst": 2, "src": 0, "src2": 1, "imm": "0x944f466f", "imm2": "0xbbb1d985", "rot": 17, "bit": 20, "mask": 1, "width": 1},
{"i": 2, "op": "xor", "dst": 0, "src": 7, "src2": 4, "imm": "0x42d344ab", "imm2": "0xc4fa2084", "rot": 29, "bit": 10, "mask": 4, "width": 1},
{"i": 3, "op": "xor", "dst": 4, "src": 7, "src2": 2, "imm": "0x6a558d6d", "imm2": "0x21feb6e9", "rot": 31, "bit": 0, "mask": 8, "width": 1},
{"i": 4, "op": "load", "dst": 2, "src": 4, "src2": 4, "imm": "0xe6c6b0d6", "imm2": "0xf7ff31ee", "rot": 25, "bit": 21, "mask": 4, "width": 1},
{"i": 5, "op": "load", "dst": 3, "src": 2, "src2": 0, "imm": "0x8c1b23b2", "imm2": "0x7db0b535", "rot": 16, "bit": 1, "mask": 1, "width": 4},
{"i": 6, "op": "shfl", "dst": 0, "src": 7, "src2": 4, "imm": "0x9d7e34c0", "imm2": "0xf15d4662", "rot": 4, "bit": 16, "mask": 4, "width": 1},
{"i": 7, "op": "rotl", "dst": 6, "src": 5, "src2": 1, "imm": "0x8981adb9", "imm2": "0x4bb830b4", "rot": 31, "bit": 24, "mask": 4, "width": 1},
{"i": 8, "op": "mad", "dst": 1, "src": 4, "src2": 2, "imm": "0xcb805ebd", "imm2": "0xde933f7d", "rot": 29, "bit": 27, "mask": 8, "width": 1},
{"i": 9, "op": "mad", "dst": 1, "src": 7, "src2": 0, "imm": "0x66835a74", "imm2": "0xde14a90d", "rot": 30, "bit": 29, "mask": 4, "width": 1},
{"i": 10, "op": "load", "dst": 4, "src": 1, "src2": 3, "imm": "0x0e0ff63d", "imm2": "0x848393fd", "rot": 20, "bit": 13, "mask": 4, "width": 1},
{"i": 11, "op": "mad", "dst": 6, "src": 7, "src2": 0, "imm": "0x54b20c82", "imm2": "0x6be278b9", "rot": 27, "bit": 25, "mask": 4, "width": 1},
{"i": 12, "op": "mul", "dst": 2, "src": 6, "src2": 4, "imm": "0x152bfc86", "imm2": "0x403f65d5", "rot": 31, "bit": 12, "mask": 8, "width": 1},
{"i": 13, "op": "load", "dst": 0, "src": 2, "src2": 5, "imm": "0x0c3b44ce", "imm2": "0x03bbe6be", "rot": 13, "bit": 27, "mask": 4, "width": 4},
{"i": 14, "op": "add", "dst": 2, "src": 7, "src2": 2, "imm": "0x5df3957d", "imm2": "0x4a3db5a5", "rot": 19, "bit": 22, "mask": 16, "width": 1},
{"i": 15, "op": "shfl", "dst": 5, "src": 7, "src2": 2, "imm": "0x62602275", "imm2": "0x8dd64d3c", "rot": 14, "bit": 1, "mask": 1, "width": 1},
{"i": 16, "op": "mad", "dst": 3, "src": 5, "src2": 6, "imm": "0xdcbd85e9", "imm2": "0x6aa7c473", "rot": 3, "bit": 30, "mask": 8, "width": 1},
{"i": 17, "op": "mul", "dst": 2, "src": 5, "src2": 0, "imm": "0xbc049a58", "imm2": "0x65f78542", "rot": 22, "bit": 28, "mask": 8, "width": 1},
{"i": 18, "op": "shfl", "dst": 1, "src": 5, "src2": 3, "imm": "0x1381d2fa", "imm2": "0xb37a6855", "rot": 22, "bit": 29, "mask": 1, "width": 1},
{"i": 19, "op": "sub", "dst": 0, "src": 1, "src2": 3, "imm": "0x0975c87d", "imm2": "0xcf931617", "rot": 10, "bit": 22, "mask": 8, "width": 1},
{"i": 20, "op": "mad", "dst": 4, "src": 6, "src2": 4, "imm": "0x10759307", "imm2": "0x3eae5571", "rot": 15, "bit": 7, "mask": 8, "width": 1},
{"i": 21, "op": "shfl", "dst": 3, "src": 0, "src2": 0, "imm": "0x047b824d", "imm2": "0xf79c23a1", "rot": 28, "bit": 5, "mask": 16, "width": 1},
{"i": 22, "op": "rotl", "dst": 1, "src": 3, "src2": 3, "imm": "0x6ebf5dd2", "imm2": "0xcc6928a6", "rot": 24, "bit": 0, "mask": 1, "width": 1},
{"i": 23, "op": "add", "dst": 1, "src": 0, "src2": 2, "imm": "0x5c141117", "imm2": "0xfc07c54c", "rot": 6, "bit": 22, "mask": 4, "width": 1},
{"i": 24, "op": "load", "dst": 2, "src": 0, "src2": 0, "imm": "0x4ca5dd93", "imm2": "0x50f7ea7a", "rot": 15, "bit": 21, "mask": 16, "width": 1},
{"i": 25, "op": "xor", "dst": 0, "src": 1, "src2": 0, "imm": "0xb02c8523", "imm2": "0xc20cbd93", "rot": 8, "bit": 14, "mask": 4, "width": 1},
{"i": 26, "op": "xor", "dst": 3, "src": 7, "src2": 6, "imm": "0x4ac5aa82", "imm2": "0xd0a3b8ca", "rot": 1, "bit": 22, "mask": 16, "width": 1},
{"i": 27, "op": "load", "dst": 2, "src": 3, "src2": 0, "imm": "0xc7cb167e", "imm2": "0xe604db1b", "rot": 3, "bit": 1, "mask": 16, "width": 4},
{"i": 28, "op": "xor", "dst": 0, "src": 6, "src2": 5, "imm": "0x68319ac4", "imm2": "0x1d0fc6ef", "rot": 31, "bit": 13, "mask": 8, "width": 1},
{"i": 29, "op": "xor", "dst": 4, "src": 1, "src2": 4, "imm": "0x46e6b295", "imm2": "0x279187a0", "rot": 22, "bit": 6, "mask": 4, "width": 1},
{"i": 30, "op": "mul", "dst": 6, "src": 3, "src2": 6, "imm": "0x087009f4", "imm2": "0x69bd5f06", "rot": 17, "bit": 13, "mask": 1, "width": 1},
{"i": 31, "op": "rotl", "dst": 3, "src": 0, "src2": 3, "imm": "0x96261bd7", "imm2": "0x2304582f", "rot": 23, "bit": 31, "mask": 4, "width": 1},
{"i": 32, "op": "rotr", "dst": 7, "src": 1, "src2": 1, "imm": "0x975e5e73", "imm2": "0x7382921b", "rot": 11, "bit": 21, "mask": 2, "width": 1},
{"i": 33, "op": "shfl", "dst": 6, "src": 2, "src2": 2, "imm": "0xfc64a106", "imm2": "0xfa7eb90c", "rot": 1, "bit": 16, "mask": 2, "width": 1},
{"i": 34, "op": "load", "dst": 5, "src": 0, "src2": 7, "imm": "0xe536f810", "imm2": "0xe44d2045", "rot": 4, "bit": 9, "mask": 8, "width": 16},
{"i": 35, "op": "add", "dst": 6, "src": 3, "src2": 6, "imm": "0xfb55c58d", "imm2": "0x0b74657b", "rot": 25, "bit": 29, "mask": 8, "width": 1},
{"i": 36, "op": "load", "dst": 6, "src": 5, "src2": 1, "imm": "0x85c5e518", "imm2": "0xdf4f27ed", "rot": 10, "bit": 6, "mask": 2, "width": 16},
{"i": 37, "op": "load", "dst": 1, "src": 4, "src2": 6, "imm": "0xd47ed295", "imm2": "0x062102da", "rot": 1, "bit": 15, "mask": 16, "width": 1},
{"i": 38, "op": "load", "dst": 6, "src": 3, "src2": 7, "imm": "0x07c3080a", "imm2": "0x2f4b3801", "rot": 8, "bit": 10, "mask": 4, "width": 4},
{"i": 39, "op": "load", "dst": 4, "src": 7, "src2": 5, "imm": "0x1beb83e7", "imm2": "0x90d4963d", "rot": 16, "bit": 31, "mask": 4, "width": 1},
{"i": 40, "op": "mulhi", "dst": 7, "src": 3, "src2": 7, "imm": "0x42b300c8", "imm2": "0x05b01fcb", "rot": 5, "bit": 17, "mask": 1, "width": 1},
{"i": 41, "op": "or", "dst": 0, "src": 6, "src2": 5, "imm": "0xd6d93d15", "imm2": "0xcef78983", "rot": 31, "bit": 6, "mask": 1, "width": 1},
{"i": 42, "op": "mad", "dst": 0, "src": 3, "src2": 0, "imm": "0xd14e02da", "imm2": "0x72d866a4", "rot": 9, "bit": 29, "mask": 4, "width": 1},
{"i": 43, "op": "load", "dst": 4, "src": 6, "src2": 6, "imm": "0x84684f5c", "imm2": "0x396d612e", "rot": 2, "bit": 7, "mask": 2, "width": 16},
{"i": 44, "op": "mad", "dst": 2, "src": 4, "src2": 0, "imm": "0x0a129ff8", "imm2": "0x8dbb8864", "rot": 20, "bit": 29, "mask": 8, "width": 1},
{"i": 45, "op": "mad", "dst": 2, "src": 4, "src2": 2, "imm": "0x8e8de36b", "imm2": "0x5f1ad032", "rot": 11, "bit": 7, "mask": 2, "width": 1},
{"i": 46, "op": "shfl", "dst": 7, "src": 5, "src2": 0, "imm": "0x395fb2f0", "imm2": "0x1f8120ba", "rot": 13, "bit": 25, "mask": 16, "width": 1},
{"i": 47, "op": "mad", "dst": 6, "src": 1, "src2": 4, "imm": "0x46dd77ef", "imm2": "0x3344daf3", "rot": 24, "bit": 26, "mask": 4, "width": 1},
{"i": 48, "op": "load", "dst": 7, "src": 1, "src2": 5, "imm": "0x72e62a51", "imm2": "0x6887cd34", "rot": 28, "bit": 6, "mask": 4, "width": 1},
{"i": 49, "op": "mulhi", "dst": 6, "src": 5, "src2": 6, "imm": "0x15785a34", "imm2": "0x4cd4815d", "rot": 18, "bit": 22, "mask": 1, "width": 1},
{"i": 50, "op": "mulhi", "dst": 5, "src": 0, "src2": 3, "imm": "0xf4adde36", "imm2": "0x3a596180", "rot": 28, "bit": 11, "mask": 2, "width": 1},
{"i": 51, "op": "load", "dst": 1, "src": 6, "src2": 0, "imm": "0xbe0a1ce3", "imm2": "0xa67b98b2", "rot": 19, "bit": 12, "mask": 8, "width": 4},
{"i": 52, "op": "shfl", "dst": 7, "src": 3, "src2": 2, "imm": "0xde71a6c7", "imm2": "0x089678f0", "rot": 11, "bit": 22, "mask": 1, "width": 1},
{"i": 53, "op": "xor", "dst": 2, "src": 5, "src2": 6, "imm": "0x25e0b714", "imm2": "0xc498694f", "rot": 31, "bit": 20, "mask": 2, "width": 1},
{"i": 54, "op": "shfl", "dst": 0, "src": 6, "src2": 5, "imm": "0x4f08890f", "imm2": "0x9214703e", "rot": 1, "bit": 23, "mask": 4, "width": 1},
{"i": 55, "op": "mul", "dst": 2, "src": 5, "src2": 3, "imm": "0x3c2cb502", "imm2": "0xd2b3be81", "rot": 31, "bit": 18, "mask": 1, "width": 1},
{"i": 56, "op": "mul", "dst": 5, "src": 6, "src2": 3, "imm": "0x856a28d5", "imm2": "0xd0a711af", "rot": 24, "bit": 0, "mask": 1, "width": 1},
{"i": 57, "op": "load", "dst": 6, "src": 4, "src2": 7, "imm": "0xf0c439cf", "imm2": "0x36507133", "rot": 14, "bit": 2, "mask": 4, "width": 4},
{"i": 58, "op": "mad", "dst": 4, "src": 7, "src2": 7, "imm": "0x1df01e69", "imm2": "0xae8c4b8e", "rot": 28, "bit": 17, "mask": 2, "width": 1},
{"i": 59, "op": "load", "dst": 2, "src": 1, "src2": 6, "imm": "0x3796e56b", "imm2": "0x4b519b72", "rot": 30, "bit": 31, "mask": 2, "width": 1},
{"i": 60, "op": "mul", "dst": 1, "src": 4, "src2": 2, "imm": "0x4c165ca5", "imm2": "0xd4c97f2a", "rot": 18, "bit": 20, "mask": 1, "width": 1},
{"i": 61, "op": "mul", "dst": 3, "src": 4, "src2": 1, "imm": "0x5d55a322", "imm2": "0xdde0b168", "rot": 1, "bit": 27, "mask": 4, "width": 1},
{"i": 62, "op": "xor", "dst": 6, "src": 4, "src2": 5, "imm": "0x82cf3c97", "imm2": "0x7c368d65", "rot": 26, "bit": 17, "mask": 4, "width": 1},
{"i": 63, "op": "rotr", "dst": 3, "src": 1, "src2": 2, "imm": "0x53bbae08", "imm2": "0x223ccdc5", "rot": 28, "bit": 16, "mask": 4, "width": 1}
]
}

View file

@ -0,0 +1,109 @@
#include <metal_stdlib>
using namespace metal;
#define MASK 0x0fffffffu
constant uint SEEDW[8] = { 0xc255b2bfu, 0xdba9d396u, 0x6ea527e4u, 0x05f1866au, 0x2947e02eu, 0x0ecc5c6fu, 0xc3b7068du, 0x282d1c2eu };
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;
}
kernel void igneum_hash(device const uint* dataset [[buffer(0)]],
device ulong* out [[buffer(1)]],
constant uint& baseNonce [[buffer(2)]],
uint gid [[thread_position_in_grid]]) {
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;
r3 = rotl_imm(r3, 26u); // 0
r2 = rotr_var(r2, r0); // 1
r0 = r0 ^ r7; // 2
r4 = r4 ^ r7; // 3
r2 = r2 ^ dataset[r4 & MASK]; // 4
{ uint b_ = (r2 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r3 = x_; } // 5
r0 = r0 ^ simd_shuffle_xor(r7, (ushort)4); // 6
r6 = rotl_imm(r6, 31u); // 7
r1 = r4 * r2 + r1; // 8
r1 = r7 * r0 + r1; // 9
r4 = r4 ^ dataset[r1 & MASK]; // 10
r6 = r7 * r0 + r6; // 11
r2 = r2 * r6; // 12
{ uint b_ = (r2 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r0 = x_; } // 13
r2 = r2 + r7 + select(0x5df3957du, 0x4a3db5a5u, ((sel >> 22u) & 1u) != 0u); // 14
r5 = r5 ^ simd_shuffle_xor(r7, (ushort)1); // 15
r3 = r5 * r6 + r3; // 16
r2 = r2 * r5; // 17
r1 = r1 ^ simd_shuffle_xor(r5, (ushort)1); // 18
r0 = r0 - r1; // 19
r4 = r6 * r4 + r4; // 20
r3 = r3 ^ simd_shuffle_xor(r0, (ushort)16); // 21
r1 = rotl_imm(r1, 24u); // 22
r1 = r1 + r0 + select(0x5c141117u, 0xfc07c54cu, ((sel >> 22u) & 1u) != 0u); // 23
r2 = r2 ^ dataset[r0 & MASK]; // 24
r0 = r0 ^ r1; // 25
r3 = r3 ^ r7; // 26
{ uint b_ = (r3 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r2 = x_; } // 27
r0 = r0 ^ r6; // 28
r4 = r4 ^ r1; // 29
r6 = r6 * r3; // 30
r3 = rotl_imm(r3, 23u); // 31
r7 = rotr_var(r7, r1); // 32
r6 = r6 ^ simd_shuffle_xor(r2, (ushort)2); // 33
{ uint b_ = (r0 & MASK) & ~15u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint x_ = r5 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r5 = x_; } // 34
r6 = r6 + r3 + select(0xfb55c58du, 0x0b74657bu, ((sel >> 29u) & 1u) != 0u); // 35
{ uint b_ = (r5 & MASK) & ~15u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r6 = x_; } // 36
r1 = r1 ^ dataset[r4 & MASK]; // 37
{ uint b_ = (r3 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r6 = x_; } // 38
r4 = r4 ^ dataset[r7 & MASK]; // 39
r7 = mulhi(r7, r3); // 40
r0 = r0 | r6; // 41
r0 = r3 * r0 + r0; // 42
{ uint b_ = (r6 & MASK) & ~15u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint x_ = r4 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r4 = x_; } // 43
r2 = r4 * r0 + r2; // 44
r2 = r4 * r2 + r2; // 45
r7 = r7 ^ simd_shuffle_xor(r5, (ushort)16); // 46
r6 = r1 * r4 + r6; // 47
r7 = r7 ^ dataset[r1 & MASK]; // 48
r6 = mulhi(r6, r5); // 49
r5 = mulhi(r5, r0); // 50
{ uint b_ = (r6 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r1 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r1 = x_; } // 51
r7 = r7 ^ simd_shuffle_xor(r3, (ushort)1); // 52
r2 = r2 ^ r5; // 53
r0 = r0 ^ simd_shuffle_xor(r6, (ushort)4); // 54
r2 = r2 * r5; // 55
r5 = r5 * r6; // 56
{ uint b_ = (r4 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r6 = x_; } // 57
r4 = r7 * r7 + r4; // 58
r2 = r2 ^ dataset[r1 & MASK]; // 59
r1 = r1 * r4; // 60
r3 = r3 * r4; // 61
r6 = r6 ^ r4; // 62
r3 = rotr_var(r3, r1); // 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;
}

View file

@ -0,0 +1,111 @@
#include <metal_stdlib>
using namespace metal;
#define MASK 0x0fffffffu
constant uint SEEDW[8] = { 0xc255b2bfu, 0xdba9d396u, 0x6ea527e4u, 0x05f1866au, 0x2947e02eu, 0x0ecc5c6fu, 0xc3b7068du, 0x282d1c2eu };
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;
}
// Header-bound variant: the init words come from buffer 3 (bind.rs), not from SEEDW.
kernel void igneum_hash_bound(device const uint* dataset [[buffer(0)]],
device ulong* out [[buffer(1)]],
constant uint& baseNonce [[buffer(2)]],
constant uint* initw [[buffer(3)]],
uint gid [[thread_position_in_grid]]) {
uint nonce = baseNonce + gid;
uint r0, r1, r2, r3, r4, r5, r6, r7;
{ uint x = nonce ^ initw[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ initw[1]; }
{ uint x = nonce ^ initw[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ initw[2]; }
{ uint x = nonce ^ initw[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ initw[3]; }
{ uint x = nonce ^ initw[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ initw[4]; }
{ uint x = nonce ^ initw[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ initw[5]; }
{ uint x = nonce ^ initw[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ initw[6]; }
{ uint x = nonce ^ initw[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ initw[7]; }
{ uint x = nonce ^ initw[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ initw[0]; }
for (uint it = 0u; it < 8u; ++it) {
uint sel = r0;
r3 = rotl_imm(r3, 26u); // 0
r2 = rotr_var(r2, r0); // 1
r0 = r0 ^ r7; // 2
r4 = r4 ^ r7; // 3
r2 = r2 ^ dataset[r4 & MASK]; // 4
{ uint b_ = (r2 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r3 = x_; } // 5
r0 = r0 ^ simd_shuffle_xor(r7, (ushort)4); // 6
r6 = rotl_imm(r6, 31u); // 7
r1 = r4 * r2 + r1; // 8
r1 = r7 * r0 + r1; // 9
r4 = r4 ^ dataset[r1 & MASK]; // 10
r6 = r7 * r0 + r6; // 11
r2 = r2 * r6; // 12
{ uint b_ = (r2 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r0 = x_; } // 13
r2 = r2 + r7 + select(0x5df3957du, 0x4a3db5a5u, ((sel >> 22u) & 1u) != 0u); // 14
r5 = r5 ^ simd_shuffle_xor(r7, (ushort)1); // 15
r3 = r5 * r6 + r3; // 16
r2 = r2 * r5; // 17
r1 = r1 ^ simd_shuffle_xor(r5, (ushort)1); // 18
r0 = r0 - r1; // 19
r4 = r6 * r4 + r4; // 20
r3 = r3 ^ simd_shuffle_xor(r0, (ushort)16); // 21
r1 = rotl_imm(r1, 24u); // 22
r1 = r1 + r0 + select(0x5c141117u, 0xfc07c54cu, ((sel >> 22u) & 1u) != 0u); // 23
r2 = r2 ^ dataset[r0 & MASK]; // 24
r0 = r0 ^ r1; // 25
r3 = r3 ^ r7; // 26
{ uint b_ = (r3 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r2 = x_; } // 27
r0 = r0 ^ r6; // 28
r4 = r4 ^ r1; // 29
r6 = r6 * r3; // 30
r3 = rotl_imm(r3, 23u); // 31
r7 = rotr_var(r7, r1); // 32
r6 = r6 ^ simd_shuffle_xor(r2, (ushort)2); // 33
{ uint b_ = (r0 & MASK) & ~15u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint x_ = r5 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r5 = x_; } // 34
r6 = r6 + r3 + select(0xfb55c58du, 0x0b74657bu, ((sel >> 29u) & 1u) != 0u); // 35
{ uint b_ = (r5 & MASK) & ~15u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r6 = x_; } // 36
r1 = r1 ^ dataset[r4 & MASK]; // 37
{ uint b_ = (r3 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r6 = x_; } // 38
r4 = r4 ^ dataset[r7 & MASK]; // 39
r7 = mulhi(r7, r3); // 40
r0 = r0 | r6; // 41
r0 = r3 * r0 + r0; // 42
{ uint b_ = (r6 & MASK) & ~15u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint x_ = r4 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r4 = x_; } // 43
r2 = r4 * r0 + r2; // 44
r2 = r4 * r2 + r2; // 45
r7 = r7 ^ simd_shuffle_xor(r5, (ushort)16); // 46
r6 = r1 * r4 + r6; // 47
r7 = r7 ^ dataset[r1 & MASK]; // 48
r6 = mulhi(r6, r5); // 49
r5 = mulhi(r5, r0); // 50
{ uint b_ = (r6 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r1 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r1 = x_; } // 51
r7 = r7 ^ simd_shuffle_xor(r3, (ushort)1); // 52
r2 = r2 ^ r5; // 53
r0 = r0 ^ simd_shuffle_xor(r6, (ushort)4); // 54
r2 = r2 * r5; // 55
r5 = r5 * r6; // 56
{ uint b_ = (r4 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r6 = x_; } // 57
r4 = r7 * r7 + r4; // 58
r2 = r2 ^ dataset[r1 & MASK]; // 59
r1 = r1 * r4; // 60
r3 = r3 * r4; // 61
r6 = r6 ^ r4; // 62
r3 = rotr_var(r3, r1); // 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;
}

View file

@ -0,0 +1,57 @@
// Generated by igneum-pow export (generator v2) for seed "igneum-readwidth/A/5". Do not edit by hand.
// Expected outputs: igneum-pow (Rust) CPU interpreter, generator v2, memory-hard dataset
#pragma once
#ifdef __cplusplus
#include <cstdint>
#else
#include <stdint.h>
#endif
#define IGNEUM_VEC_WARPS 3
static const uint32_t IGNEUM_VEC_BASE[IGNEUM_VEC_WARPS] = { 0u, 4096u, 1000000u };
static const uint64_t IGNEUM_VEC_OUT[IGNEUM_VEC_WARPS][32] = {
{ // base nonce 0
0xeaee437d9c4bba42ull, 0xa89fc64fb516d23bull, 0xf50c45c711918300ull, 0x7f02b33440a1ee24ull, 0x2010bc3f907d5040ull, 0x74d0198a4fbd6269ull, 0xe6248af74a39174eull, 0x1eb569508ef6c62bull,
0xa33d857ed5fe79beull, 0x373c29561fbc6793ull, 0xf085a877af1bb6e8ull, 0x642c45f3214b4d6dull, 0xe1ad911edc598629ull, 0xff3e0523b7834ed3ull, 0x7c8752e2db521931ull, 0xe14314ca7f3637bcull,
0x325b4f284dcc8f53ull, 0x4d2d6b6e3dd0acc0ull, 0x435670a68d505a3dull, 0x719f3f336a843deeull, 0x101662b22c52db66ull, 0x4467ce7e0020e5deull, 0xef2031f7841e1b86ull, 0xccac3386fc48adbdull,
0x0634a7aa079c7fcfull, 0x5139e023bceb13d3ull, 0xc114f6be02fd1befull, 0x3401895eb20ff261ull, 0xf1e3dcf94fa94b59ull, 0xa4af930f43a9a6e7ull, 0xcf2a8f7bc60fd349ull, 0x3cf48986c94c816cull
},
{ // base nonce 4096
0x81fc83f13ff4be40ull, 0x7c42d114d8e674fbull, 0x816154766b564cccull, 0xb7bf2a4a3d544d2eull, 0x7de35c931f18d8c9ull, 0x1d3829de6e647586ull, 0x76a8d92d75a87fc4ull, 0x2308825663bba9b7ull,
0xe6d226a37785ffa1ull, 0xdeef0eed55374e0full, 0xde5cfde59a1a53ecull, 0xc4799ac83c5a0602ull, 0xaab7dada14aa7c47ull, 0x7e5eacebfb6ec92cull, 0x2c99a69019e48570ull, 0xae065fa5a54b586aull,
0x23cb704f5a33e2caull, 0xf0bdea48e2c03edbull, 0xe150785d233211ceull, 0xb45388e11497f6bbull, 0xf862683cde4568bcull, 0x2e4e81f9fafef5c2ull, 0x8e6aef8b82861379ull, 0x7883eea61e6b6cbdull,
0xcc89c8ea28a7a715ull, 0x4adb824fc1302fc7ull, 0x0b99a18825cf092cull, 0xac0f0a2a901bc59aull, 0xef5e0f0bf1c9b8aeull, 0x2220f11e1b69bc0aull, 0x3e9695b77c69892cull, 0xbfe252bff26b14fcull
},
{ // base nonce 1000000
0xb2cc4dc245d1522dull, 0x3f89483adcc726bcull, 0x3df0f900a0027a6cull, 0xa47b69fe239ee7dcull, 0xff539ee62e5ffa98ull, 0x622a019529ba3cc4ull, 0xf92f75b66deb9f43ull, 0x5bdb07bf6d151978ull,
0xc6b6ee7fb2350d3aull, 0x0d47d76e37088cc7ull, 0xc662723c083ae26dull, 0x68ddb8f6849ffa3cull, 0x5439d7ce29a28ac0ull, 0x59ffe6543a643d07ull, 0xec4286570ff0b04cull, 0xef042881c63fdda4ull,
0xf8185da0349a2894ull, 0xdc4e87d0650fe7a1ull, 0x7df286ee2777a93full, 0x4c2b743817f1a427ull, 0x1b7aef4be8cf3bb3ull, 0xfb64afb73c0fbab8ull, 0x365b83541877755dull, 0x59872f36106c4827ull,
0x452e18f12aa47c59ull, 0xed37e59b230c5ad7ull, 0x383768d545312f62ull, 0x30c548bee141f5f5ull, 0x073494619104a4c4ull, 0x0ba09ddfc99ee224ull, 0x0a371f34ea9b52a4ull, 0xe3c763c14c3e2f38ull
}
};
// Dataset self-test: dataset[0..15] and dataset[IGNEUM_MASK] (268435455).
static const uint32_t IGNEUM_DS_HEAD[16] = {
0xffc3cd94u, 0x5920ccd8u, 0x392f44bbu, 0x5e57f67au, 0x2f2bc2a9u, 0x620b0e36u, 0xbdc09014u, 0x436654bfu,
0x311e0b48u, 0x1abd93adu, 0x59cc7ce8u, 0xee5247b2u, 0x86171fe8u, 0x6d874751u, 0xc9f7728fu, 0x7c2a435du
};
static const uint32_t IGNEUM_DS_LAST_INDEX = 268435455u;
static const uint32_t IGNEUM_DS_LAST = 0xa33ada72u;
// 64 sampled dataset words (index, value) computed on the Mac.
#define IGNEUM_DS_SAMPLES 64
static const uint32_t IGNEUM_DS_SAMPLE_INDEX[IGNEUM_DS_SAMPLES] = {
59471966u, 217795994u, 208353206u, 42483309u, 172547758u, 148076330u, 183853158u, 214389424u, 267488061u, 169781097u, 184093494u, 153880993u, 84977930u, 46426879u, 3093825u, 225364072u, 44593546u, 260713159u, 168250303u, 52384140u, 223401610u, 45554030u, 95410555u, 175039924u, 79171087u, 267580473u, 24168642u, 37981670u, 171551130u, 195559979u, 204611762u, 140997658u, 138925853u, 86637313u, 20736778u, 219665210u, 160430336u, 264654675u, 8013395u, 228945585u, 213884386u, 104419827u, 44185464u, 142737231u, 99284897u, 132475900u, 61861762u, 132056166u, 262388043u, 91878046u, 117353561u, 124768597u, 71352993u, 190698941u, 46055428u, 55281366u, 165145231u, 106810753u, 171985651u, 232085256u, 159510492u, 40072060u, 209107596u, 39023794u
};
static const uint32_t IGNEUM_DS_SAMPLE_VALUE[IGNEUM_DS_SAMPLES] = {
0xe8b73d94u, 0x337028b5u, 0xafe148c9u, 0xab99f7aeu, 0x434ea619u, 0xd85cb880u, 0x54764c7fu, 0x82c7e420u, 0xedf4cb9eu, 0x9884c959u, 0x223ee793u, 0x3a9ccf69u, 0x81da4fd2u, 0xd6ce8cb9u, 0xe3922dcau, 0x3e7e6bdeu, 0x382a3acau, 0x567e7f7fu, 0x25a0f084u, 0xbfeef128u, 0xe338abfbu, 0x7c3b5280u, 0x909bc5f1u, 0xd8b74b9cu, 0x8e31a22eu, 0x26b5f1d8u, 0x79122c00u, 0xcafc3340u, 0xd5e02ea3u, 0x1aee1afdu, 0xdb090d9au, 0xb049f435u, 0x4954d8bau, 0x03797ba0u, 0x196eefbdu, 0xd153412au, 0xbe5d2c4bu, 0xdaa14f0eu, 0x8e61ed07u, 0x9e9a64c6u, 0x2e29ff36u, 0x392a8589u, 0xb56a5912u, 0xfa6e8b57u, 0xd1a737cbu, 0xb0fa841au, 0xbe1c341fu, 0xe25be0f1u, 0xe937f543u, 0xebab2248u, 0x8e1b607au, 0x202a2fedu, 0x95e2819cu, 0x9c9652d4u, 0x32fedef0u, 0xdecfff82u, 0xcb5d43e5u, 0xb735806au, 0x8905939cu, 0xfbf8472du, 0xada74e5du, 0x7ebdeeeau, 0x0119f2b3u, 0xa9a376b8u
};
// Cache self-test (memory-hard mode): cache[0..15], the last 16 words, and FNV-1a 64 over all 2^26 words.
static const uint32_t IGNEUM_CACHE_HEAD[16] = {
0x355a86d2u, 0x7957db1cu, 0xd21772afu, 0x6fc1e09bu, 0xd55ce61du, 0x6e6a278bu, 0xd3f543ceu, 0x223d8e82u,
0x143ab337u, 0x2e9f05bdu, 0x2eb389bfu, 0x0c6e449eu, 0x5cfa4222u, 0xba6560feu, 0x8e3e1aa4u, 0xdbcc1d53u
};
static const uint32_t IGNEUM_CACHE_LAST[16] = {
0x41190d91u, 0xbd277957u, 0x22ddbb49u, 0x6986f207u, 0xdf69a4d6u, 0x26401a3au, 0x818230fbu, 0xc417122du,
0x3597b211u, 0xb553ce55u, 0xcf39cc0du, 0x3b7fc43au, 0x3fd43b00u, 0x67e1c80eu, 0xffa7ea7du, 0xca2960abu
};
static const uint64_t IGNEUM_CACHE_FNV64 = 0x48c4f5bf24166b2eull;

View file

@ -0,0 +1,36 @@
{
"seed": "igneum-readwidth/A/5",
"day": "2026-10-03",
"dataset_mode": "memory-hard",
"dataset_log2_words": 28,
"mask": "0x0fffffff",
"lanes": 32,
"source": "igneum-pow (Rust) CPU interpreter, generator v2, memory-hard dataset",
"warps": [
{"base_nonce": 0, "expected": [
"0xeaee437d9c4bba42", "0xa89fc64fb516d23b", "0xf50c45c711918300", "0x7f02b33440a1ee24", "0x2010bc3f907d5040", "0x74d0198a4fbd6269", "0xe6248af74a39174e", "0x1eb569508ef6c62b",
"0xa33d857ed5fe79be", "0x373c29561fbc6793", "0xf085a877af1bb6e8", "0x642c45f3214b4d6d", "0xe1ad911edc598629", "0xff3e0523b7834ed3", "0x7c8752e2db521931", "0xe14314ca7f3637bc",
"0x325b4f284dcc8f53", "0x4d2d6b6e3dd0acc0", "0x435670a68d505a3d", "0x719f3f336a843dee", "0x101662b22c52db66", "0x4467ce7e0020e5de", "0xef2031f7841e1b86", "0xccac3386fc48adbd",
"0x0634a7aa079c7fcf", "0x5139e023bceb13d3", "0xc114f6be02fd1bef", "0x3401895eb20ff261", "0xf1e3dcf94fa94b59", "0xa4af930f43a9a6e7", "0xcf2a8f7bc60fd349", "0x3cf48986c94c816c"
]},
{"base_nonce": 4096, "expected": [
"0x81fc83f13ff4be40", "0x7c42d114d8e674fb", "0x816154766b564ccc", "0xb7bf2a4a3d544d2e", "0x7de35c931f18d8c9", "0x1d3829de6e647586", "0x76a8d92d75a87fc4", "0x2308825663bba9b7",
"0xe6d226a37785ffa1", "0xdeef0eed55374e0f", "0xde5cfde59a1a53ec", "0xc4799ac83c5a0602", "0xaab7dada14aa7c47", "0x7e5eacebfb6ec92c", "0x2c99a69019e48570", "0xae065fa5a54b586a",
"0x23cb704f5a33e2ca", "0xf0bdea48e2c03edb", "0xe150785d233211ce", "0xb45388e11497f6bb", "0xf862683cde4568bc", "0x2e4e81f9fafef5c2", "0x8e6aef8b82861379", "0x7883eea61e6b6cbd",
"0xcc89c8ea28a7a715", "0x4adb824fc1302fc7", "0x0b99a18825cf092c", "0xac0f0a2a901bc59a", "0xef5e0f0bf1c9b8ae", "0x2220f11e1b69bc0a", "0x3e9695b77c69892c", "0xbfe252bff26b14fc"
]},
{"base_nonce": 1000000, "expected": [
"0xb2cc4dc245d1522d", "0x3f89483adcc726bc", "0x3df0f900a0027a6c", "0xa47b69fe239ee7dc", "0xff539ee62e5ffa98", "0x622a019529ba3cc4", "0xf92f75b66deb9f43", "0x5bdb07bf6d151978",
"0xc6b6ee7fb2350d3a", "0x0d47d76e37088cc7", "0xc662723c083ae26d", "0x68ddb8f6849ffa3c", "0x5439d7ce29a28ac0", "0x59ffe6543a643d07", "0xec4286570ff0b04c", "0xef042881c63fdda4",
"0xf8185da0349a2894", "0xdc4e87d0650fe7a1", "0x7df286ee2777a93f", "0x4c2b743817f1a427", "0x1b7aef4be8cf3bb3", "0xfb64afb73c0fbab8", "0x365b83541877755d", "0x59872f36106c4827",
"0x452e18f12aa47c59", "0xed37e59b230c5ad7", "0x383768d545312f62", "0x30c548bee141f5f5", "0x073494619104a4c4", "0x0ba09ddfc99ee224", "0x0a371f34ea9b52a4", "0xe3c763c14c3e2f38"
]}
],
"dataset_head": ["0xffc3cd94", "0x5920ccd8", "0x392f44bb", "0x5e57f67a", "0x2f2bc2a9", "0x620b0e36", "0xbdc09014", "0x436654bf", "0x311e0b48", "0x1abd93ad", "0x59cc7ce8", "0xee5247b2", "0x86171fe8", "0x6d874751", "0xc9f7728f", "0x7c2a435d"],
"dataset_last_index": 268435455,
"dataset_last": "0xa33ada72",
"dataset_samples": [{"index": 59471966, "value": "0xe8b73d94"}, {"index": 217795994, "value": "0x337028b5"}, {"index": 208353206, "value": "0xafe148c9"}, {"index": 42483309, "value": "0xab99f7ae"}, {"index": 172547758, "value": "0x434ea619"}, {"index": 148076330, "value": "0xd85cb880"}, {"index": 183853158, "value": "0x54764c7f"}, {"index": 214389424, "value": "0x82c7e420"}, {"index": 267488061, "value": "0xedf4cb9e"}, {"index": 169781097, "value": "0x9884c959"}, {"index": 184093494, "value": "0x223ee793"}, {"index": 153880993, "value": "0x3a9ccf69"}, {"index": 84977930, "value": "0x81da4fd2"}, {"index": 46426879, "value": "0xd6ce8cb9"}, {"index": 3093825, "value": "0xe3922dca"}, {"index": 225364072, "value": "0x3e7e6bde"}, {"index": 44593546, "value": "0x382a3aca"}, {"index": 260713159, "value": "0x567e7f7f"}, {"index": 168250303, "value": "0x25a0f084"}, {"index": 52384140, "value": "0xbfeef128"}, {"index": 223401610, "value": "0xe338abfb"}, {"index": 45554030, "value": "0x7c3b5280"}, {"index": 95410555, "value": "0x909bc5f1"}, {"index": 175039924, "value": "0xd8b74b9c"}, {"index": 79171087, "value": "0x8e31a22e"}, {"index": 267580473, "value": "0x26b5f1d8"}, {"index": 24168642, "value": "0x79122c00"}, {"index": 37981670, "value": "0xcafc3340"}, {"index": 171551130, "value": "0xd5e02ea3"}, {"index": 195559979, "value": "0x1aee1afd"}, {"index": 204611762, "value": "0xdb090d9a"}, {"index": 140997658, "value": "0xb049f435"}, {"index": 138925853, "value": "0x4954d8ba"}, {"index": 86637313, "value": "0x03797ba0"}, {"index": 20736778, "value": "0x196eefbd"}, {"index": 219665210, "value": "0xd153412a"}, {"index": 160430336, "value": "0xbe5d2c4b"}, {"index": 264654675, "value": "0xdaa14f0e"}, {"index": 8013395, "value": "0x8e61ed07"}, {"index": 228945585, "value": "0x9e9a64c6"}, {"index": 213884386, "value": "0x2e29ff36"}, {"index": 104419827, "value": "0x392a8589"}, {"index": 44185464, "value": "0xb56a5912"}, {"index": 142737231, "value": "0xfa6e8b57"}, {"index": 99284897, "value": "0xd1a737cb"}, {"index": 132475900, "value": "0xb0fa841a"}, {"index": 61861762, "value": "0xbe1c341f"}, {"index": 132056166, "value": "0xe25be0f1"}, {"index": 262388043, "value": "0xe937f543"}, {"index": 91878046, "value": "0xebab2248"}, {"index": 117353561, "value": "0x8e1b607a"}, {"index": 124768597, "value": "0x202a2fed"}, {"index": 71352993, "value": "0x95e2819c"}, {"index": 190698941, "value": "0x9c9652d4"}, {"index": 46055428, "value": "0x32fedef0"}, {"index": 55281366, "value": "0xdecfff82"}, {"index": 165145231, "value": "0xcb5d43e5"}, {"index": 106810753, "value": "0xb735806a"}, {"index": 171985651, "value": "0x8905939c"}, {"index": 232085256, "value": "0xfbf8472d"}, {"index": 159510492, "value": "0xada74e5d"}, {"index": 40072060, "value": "0x7ebdeeea"}, {"index": 209107596, "value": "0x0119f2b3"}, {"index": 39023794, "value": "0xa9a376b8"}],
"cache_head": ["0x355a86d2", "0x7957db1c", "0xd21772af", "0x6fc1e09b", "0xd55ce61d", "0x6e6a278b", "0xd3f543ce", "0x223d8e82", "0x143ab337", "0x2e9f05bd", "0x2eb389bf", "0x0c6e449e", "0x5cfa4222", "0xba6560fe", "0x8e3e1aa4", "0xdbcc1d53"],
"cache_last_line": ["0x41190d91", "0xbd277957", "0x22ddbb49", "0x6986f207", "0xdf69a4d6", "0x26401a3a", "0x818230fb", "0xc417122d", "0x3597b211", "0xb553ce55", "0xcf39cc0d", "0x3b7fc43a", "0x3fd43b00", "0x67e1c80e", "0xffa7ea7d", "0xca2960ab"],
"cache_fnv1a64": "0x48c4f5bf24166b2e"
}

View file

@ -0,0 +1,278 @@
// Generated by igneum-pow export (generator v2) for seed "igneum-readwidth/A/6". Do not edit by hand.
// OpenCL C twin of the Metal kernel for the same seed (see proto-opencl/README.md, WAVEFRONT.md and program.metal).
// Built from source at runtime by proto-opencl/host.c, which passes these defines:
// IGNEUM_GROUP work-group size of igneum_hash, a multiple of 32 (default 32: one work-group = one 32-lane unit)
// IGNEUM_EXCHANGE 0 = local-memory exchange with a barrier (any device, any wave width; the default)
// 1 = sub_group_shuffle_xor (cl_khr_subgroup_shuffle), only with IGNEUM_GROUP 32 and a sub-group size of exactly 32
// 2 = intel_sub_group_shuffle_xor (cl_intel_subgroups), same condition
// The verification unit is always 32 lanes. A 64-wide hardware wave (AMD GCN/CDNA, RDNA in wave64) runs two units;
// the exchange masks are 1, 2, 4, 8, 16, so every partner lane lies inside the lane's own aligned run of 32.
#ifndef IGNEUM_GROUP
#define IGNEUM_GROUP 32
#endif
#ifndef IGNEUM_EXCHANGE
#define IGNEUM_EXCHANGE 0
#endif
#ifdef __OPENCL_VERSION__
#define IGNEUM_KERNEL_HASH __kernel __attribute__((reqd_work_group_size(IGNEUM_GROUP, 1, 1)))
#define IGNEUM_LOCAL_WORDS(name, n) __local uint name[n]
#if IGNEUM_EXCHANGE == 1
#ifdef cl_khr_subgroups
#pragma OPENCL EXTENSION cl_khr_subgroups : enable
#endif
#ifdef cl_khr_subgroup_shuffle
#pragma OPENCL EXTENSION cl_khr_subgroup_shuffle : enable
#endif
#elif IGNEUM_EXCHANGE == 2
#pragma OPENCL EXTENSION cl_intel_subgroups : enable
#endif
#else
// Not an OpenCL compiler: proto-opencl/emu compiles this file as C++ and supplies the built-ins and these two macros.
#include "emu_opencl.h"
#endif
#if IGNEUM_EXCHANGE == 1
#define IGNEUM_SHFL_XOR(dst, a, m) dst = sub_group_shuffle_xor((a), (uint)(m))
#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u)
#elif IGNEUM_EXCHANGE == 2
#define IGNEUM_SHFL_XOR(dst, a, m) dst = intel_sub_group_shuffle_xor((a), (uint)(m))
#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u)
#else
// Local-memory exchange. Two buffers of IGNEUM_GROUP words alternate (xk counts exchanges), so one barrier per
// exchange is enough: a lane can only overwrite buffer b at exchange k+2 after passing barrier k+1, and every lane
// reaches barrier k+1 only after its read of buffer b at exchange k. The partner lid ^ m stays inside the lane's
// aligned run of 32 because m < 32. Control flow is uniform, so every work-item reaches every barrier.
#define IGNEUM_SHFL_XOR(dst, a, m) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid ^ (uint)(m))]; xk += 1u; }
#define IGNEUM_BCAST0(dst, a) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid & ~31u)]; xk += 1u; }
#endif
static inline uint splitmix32(uint 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. OpenCL rotate() rotates left by n modulo 32.
static inline uint rotl_imm(uint x, uint n) { return rotate(x, n); }
// Right rotation by n modulo 32 as a left rotation by (32 - n) modulo 32; n == 0 gives x.
static inline uint rotr_var(uint x, uint n) { return rotate(x, (0u - n) & 31u); }
static 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;
}
// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines.
// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals.
#define MH_CACHE_LINE_MASK 0x003fffffu
#define MH_SEGMENT_LINES 64u
#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); }
static inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site
// y = ChaCha12 core(x) + x
static inline void mh_chacha_block(const uint* x, uint* y) {
for (uint i = 0u; i < 16u; ++i) y[i] = x[i];
for (uint r = 0u; r < 6u; ++r) {
MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u)
MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u)
}
for (uint i = 0u; i < 16u; ++i) y[i] += x[i];
}
// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0.
static inline void mh_cache_segment(__global uint* cache, uint seg) {
uint prev[16]; uint x[16]; uint y[16];
for (uint i = 0u; i < 16u; ++i) prev[i] = 0u;
for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) {
x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3];
x[4] = 0x3067619fu ^ prev[4];
x[5] = 0x3c269176u ^ prev[5];
x[6] = 0x84a03b03u ^ prev[6];
x[7] = 0xf8c63294u ^ prev[7];
x[8] = 0xff977c5bu ^ prev[8];
x[9] = 0xe60def3eu ^ prev[9];
x[10] = 0x63630141u ^ prev[10];
x[11] = 0xb8fbcb58u ^ prev[11];
x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15];
mh_chacha_block(x, y);
__global uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u);
for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; }
}
}
// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations.
static inline void mh_mixer(uint* s, uint rk) {
s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u;
s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu;
s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u;
s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu;
s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u;
s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u;
s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u;
s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u;
s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du;
s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u;
s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du;
s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu;
s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du;
s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu;
s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u;
s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u;
MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u)
MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u)
MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u)
MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u)
}
// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer.
static inline void mh_item(__global const uint* cache, uint t, uint* s) {
s[0] = 0x3067619fu;
s[1] = 0x3c269176u;
s[2] = 0x84a03b03u;
s[3] = 0xf8c63294u;
s[4] = 0xff977c5bu;
s[5] = 0xe60def3eu;
s[6] = 0x63630141u;
s[7] = 0xb8fbcb58u;
s[8] = t * 0x42146205u + 0xbab68293u;
s[9] = t * 0x52cbe0fbu + 0xcc162340u;
s[10] = t * 0x7ecf4a03u + 0x6ce151ccu;
s[11] = t * 0x6728907fu + 0xe62b8997u;
s[12] = t * 0xd81d9751u + 0xc9c80297u;
s[13] = t * 0x132952c3u + 0xf74a1654u;
s[14] = t * 0xf60de277u + 0x3d704af5u;
s[15] = t * 0x05358035u + 0x3cf522b7u;
for (uint r = 0u; r < 8u; ++r) {
mh_mixer(s, 0x9E3779B9u * (r + 1u));
__global const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u);
for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i];
}
mh_mixer(s, 0x9E3779B9u * 9u);
}
// dataset[w] without the dataset: derive item w >> 4 and take word w & 15.
static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; }
// Memory-hard dataset (MEMHARD.md). One work-item per cache segment; one work-item per 64-byte dataset item.
// The same constants as memhard.h in this pack (one emitter, three dialects).
__kernel void igneum_cache_fill(__global uint* cache, uint nSegments) {
uint seg = (uint)get_global_id(0);
if (seg < nSegments) mh_cache_segment(cache, seg);
}
__kernel void igneum_build(__global uint* ds, __global const uint* cache, uint nItems) {
uint t = (uint)get_global_id(0);
if (t < nItems) {
uint s[16];
mh_item(cache, t, s);
__global uint* d = ds + ((ulong)t * 16u);
for (uint i = 0u; i < 16u; ++i) d[i] = s[i];
}
}
// One hash per work-item. IGNEUM_GROUP is a multiple of 32; lane = lid & 31 and every exchange stays inside the
// lane's own aligned run of 32 work-items, exactly like simd_shuffle_xor inside a 32-wide Metal SIMD group and
// __shfl_xor_sync inside a CUDA warp. Control flow is uniform (no branches at all).
IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask) {
uint gid = (uint)get_global_id(0);
uint lid = (uint)get_local_id(0);
uint nonce = baseNonce + gid;
uint r0, r1, r2, r3, r4, r5, r6, r7;
#if IGNEUM_EXCHANGE == 0
IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP);
uint xk = 0u;
#else
(void)lid;
#endif
{ uint x = nonce ^ 0x2a53aad4u; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x37fcb6e2u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1]
{ uint x = nonce ^ 0x37fcb6e2u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0x25d66a27u; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2]
{ uint x = nonce ^ 0x25d66a27u; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0xf5249e5eu; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3]
{ uint x = nonce ^ 0xf5249e5eu; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0x15f86a59u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4]
{ uint x = nonce ^ 0x15f86a59u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0x8144559au; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5]
{ uint x = nonce ^ 0x8144559au; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0xcad741a6u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6]
{ uint x = nonce ^ 0xcad741a6u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0xcb961373u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7]
{ uint x = nonce ^ 0xcb961373u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x2a53aad4u; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0]
for (uint it = 0u; it < 8u; ++it) {
uint sel = r0;
r0 = rotl_imm(r0, 10u); // 0 rotl
r5 = r3 * r1 + r5; // 1 mad
{ uint b_ = (r0 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r5 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r5 = x_; } // 2 load
{ uint b_ = (r5 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r6 = x_; } // 3 load
r5 = r2 * r2 + r5; // 4 mad
r4 = r4 + r3 + ((((sel >> 10u) & 1u) != 0u) ? 0x4edf355au : 0xbd587d10u); // 5 add
r3 = r3 ^ r2; // 6 xor
r5 = r5 ^ r3; // 7 xor
r7 = r6 * r2 + r7; // 8 mad
r7 = r6 * r3 + r7; // 9 mad
{ uint b_ = (r5 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r3 = x_; } // 10 load
r1 = mul_hi(r1, r4); // 11 mulhi
r1 = r1 ^ r6; // 12 xor
r2 = mul_hi(r2, r1); // 13 mulhi
{ uint t_; IGNEUM_SHFL_XOR(t_, r7, 4u); r6 = r6 ^ t_; } // 14 shfl
r4 = r6 * r4 + r4; // 15 mad
{ uint t_; IGNEUM_SHFL_XOR(t_, r0, 8u); r1 = r1 ^ t_; } // 16 shfl
r5 = r5 ^ r4; // 17 xor
r0 = r0 * r2; // 18 mul
r6 = r6 ^ ds[r3 & mask]; // 19 load
r6 = mul_hi(r6, r2); // 20 mulhi
r7 = r2 * r6 + r7; // 21 mad
r4 = r4 * r5; // 22 mul
r3 = r3 * r5; // 23 mul
r6 = r6 * r4; // 24 mul
r3 = r3 ^ ds[r7 & mask]; // 25 load
r2 = rotr_var(r2, r5); // 26 rotr
r5 = r5 | r6; // 27 or
{ uint b_ = (r2 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r4 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r4 = x_; } // 28 load
r6 = r6 | r0; // 29 or
r0 = r0 + r3 + ((((sel >> 2u) & 1u) != 0u) ? 0xdca976acu : 0x5faa0547u); // 30 add
r6 = mul_hi(r6, r0); // 31 mulhi
r3 = rotr_var(r3, r7); // 32 rotr
{ uint t_; IGNEUM_SHFL_XOR(t_, r4, 4u); r0 = r0 ^ t_; } // 33 shfl
r2 = rotl_imm(r2, 17u); // 34 rotl
r7 = mul_hi(r7, r6); // 35 mulhi
{ uint t_; IGNEUM_SHFL_XOR(t_, r2, 2u); r3 = r3 ^ t_; } // 36 shfl
r6 = r6 ^ ds[r0 & mask]; // 37 load
{ uint b_ = (r6 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r0 = x_; } // 38 load
r1 = r1 ^ r0; // 39 xor
{ uint t_; IGNEUM_SHFL_XOR(t_, r1, 2u); r3 = r3 ^ t_; } // 40 shfl
r1 = r1 ^ ds[r5 & mask]; // 41 load
r3 = r3 * r2; // 42 mul
r2 = r2 ^ ds[r7 & mask]; // 43 load
r7 = r7 + r3 + ((((sel >> 7u) & 1u) != 0u) ? 0x610bd3c2u : 0xe28c457cu); // 44 add
r7 = mul_hi(r7, r6); // 45 mulhi
r3 = r3 ^ r4; // 46 xor
r3 = r3 ^ ds[r7 & mask]; // 47 load
r7 = mul_hi(r7, r2); // 48 mulhi
r6 = mul_hi(r6, r3); // 49 mulhi
{ uint b_ = (r1 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r2 = x_; } // 50 load
{ uint b_ = (r3 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r6 = x_; } // 51 load
r5 = r5 ^ r3; // 52 xor
r5 = r5 + r7 + ((((sel >> 31u) & 1u) != 0u) ? 0xcca8fa7cu : 0xd6457f6eu); // 53 add
r7 = rotr_var(r7, r1); // 54 rotr
r6 = r6 + r7 + ((((sel >> 12u) & 1u) != 0u) ? 0x826cb755u : 0xf3e1301cu); // 55 add
{ uint b_ = (r0 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r3 = x_; } // 56 load
r1 = rotr_var(r1, r5); // 57 rotr
{ uint b_ = (r6 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r2 = x_; } // 58 load
r3 = r3 - r7; // 59 sub
r1 = r4 * r6 + r1; // 60 mad
r2 = r1 * r2 + r2; // 61 mad
r4 = r4 ^ r3; // 62 xor
r2 = r2 ^ ds[r4 & mask]; // 63 load
}
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;
}
#if IGNEUM_EXCHANGE != 0
// Reports the sub-group size this device uses for a work-group of IGNEUM_GROUP items. host.c runs it only when the
// per-kernel query (clGetKernelSubGroupInfoKHR on igneum_hash) is unavailable; that query is preferred because a
// compiler may pick a different wave width per kernel (RDNA: wave32 or wave64). See WAVEFRONT.md.
IGNEUM_KERNEL_HASH void igneum_probe_subgroup(__global uint* out) {
if (get_local_id(0) == 0u) { out[0] = get_sub_group_size(); out[1] = get_num_sub_groups(); }
}
#endif

View file

@ -0,0 +1,164 @@
// Generated by igneum-pow export (generator v2) for seed "igneum-readwidth/A/6". 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"
#include "memhard.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;
}
// Memory-hard dataset (MEMHARD.md). One thread per cache segment; one thread per 64-byte dataset item.
// The core functions (mh_cache_segment, mh_item) are in memhard.h and are also compiled for the host.
__global__ void igneum_cache_fill(uint32_t* cache, uint32_t nSegments) {
uint32_t seg = blockIdx.x * blockDim.x + threadIdx.x;
if (seg < nSegments) mh_cache_segment(cache, seg);
}
__global__ void igneum_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems) {
uint32_t t = blockIdx.x * blockDim.x + threadIdx.x;
if (t < nItems) {
uint32_t s[16];
mh_item(cache, t, s);
uint32_t* d = ds + (size_t)t * 16u;
for (uint32_t i = 0u; i < 16u; ++i) d[i] = s[i];
}
}
// 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 ^ 0x2a53aad4u; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x37fcb6e2u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1]
{ uint32_t x = nonce ^ 0x37fcb6e2u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0x25d66a27u; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2]
{ uint32_t x = nonce ^ 0x25d66a27u; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0xf5249e5eu; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3]
{ uint32_t x = nonce ^ 0xf5249e5eu; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0x15f86a59u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4]
{ uint32_t x = nonce ^ 0x15f86a59u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0x8144559au; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5]
{ uint32_t x = nonce ^ 0x8144559au; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0xcad741a6u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6]
{ uint32_t x = nonce ^ 0xcad741a6u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0xcb961373u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7]
{ uint32_t x = nonce ^ 0xcb961373u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x2a53aad4u; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0]
for (uint32_t it = 0u; it < 8u; ++it) {
uint32_t sel = r0;
r0 = rotl_imm(r0, 10u); // 0 rotl
r5 = r3 * r1 + r5; // 1 mad
{ uint32_t b_ = (r0 & mask) & ~15u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint32_t x_ = r5 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r5 = x_; } // 2 load
{ uint32_t b_ = (r5 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r6 = x_; } // 3 load
r5 = r2 * r2 + r5; // 4 mad
r4 = r4 + r3 + ((((sel >> 10u) & 1u) != 0u) ? 0x4edf355au : 0xbd587d10u); // 5 add
r3 = r3 ^ r2; // 6 xor
r5 = r5 ^ r3; // 7 xor
r7 = r6 * r2 + r7; // 8 mad
r7 = r6 * r3 + r7; // 9 mad
{ uint32_t b_ = (r5 & mask) & ~15u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint32_t x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r3 = x_; } // 10 load
r1 = __umulhi(r1, r4); // 11 mulhi
r1 = r1 ^ r6; // 12 xor
r2 = __umulhi(r2, r1); // 13 mulhi
r6 = r6 ^ __shfl_xor_sync(0xffffffffu, r7, 4); // 14 shfl
r4 = r6 * r4 + r4; // 15 mad
r1 = r1 ^ __shfl_xor_sync(0xffffffffu, r0, 8); // 16 shfl
r5 = r5 ^ r4; // 17 xor
r0 = r0 * r2; // 18 mul
r6 = r6 ^ ds[r3 & mask]; // 19 load
r6 = __umulhi(r6, r2); // 20 mulhi
r7 = r2 * r6 + r7; // 21 mad
r4 = r4 * r5; // 22 mul
r3 = r3 * r5; // 23 mul
r6 = r6 * r4; // 24 mul
r3 = r3 ^ ds[r7 & mask]; // 25 load
r2 = rotr_var(r2, r5); // 26 rotr
r5 = r5 | r6; // 27 or
{ uint32_t b_ = (r2 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r4 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r4 = x_; } // 28 load
r6 = r6 | r0; // 29 or
r0 = r0 + r3 + ((((sel >> 2u) & 1u) != 0u) ? 0xdca976acu : 0x5faa0547u); // 30 add
r6 = __umulhi(r6, r0); // 31 mulhi
r3 = rotr_var(r3, r7); // 32 rotr
r0 = r0 ^ __shfl_xor_sync(0xffffffffu, r4, 4); // 33 shfl
r2 = rotl_imm(r2, 17u); // 34 rotl
r7 = __umulhi(r7, r6); // 35 mulhi
r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r2, 2); // 36 shfl
r6 = r6 ^ ds[r0 & mask]; // 37 load
{ uint32_t b_ = (r6 & mask) & ~15u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint32_t x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r0 = x_; } // 38 load
r1 = r1 ^ r0; // 39 xor
r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r1, 2); // 40 shfl
r1 = r1 ^ ds[r5 & mask]; // 41 load
r3 = r3 * r2; // 42 mul
r2 = r2 ^ ds[r7 & mask]; // 43 load
r7 = r7 + r3 + ((((sel >> 7u) & 1u) != 0u) ? 0x610bd3c2u : 0xe28c457cu); // 44 add
r7 = __umulhi(r7, r6); // 45 mulhi
r3 = r3 ^ r4; // 46 xor
r3 = r3 ^ ds[r7 & mask]; // 47 load
r7 = __umulhi(r7, r2); // 48 mulhi
r6 = __umulhi(r6, r3); // 49 mulhi
{ uint32_t b_ = (r1 & mask) & ~15u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint32_t x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r2 = x_; } // 50 load
{ uint32_t b_ = (r3 & mask) & ~15u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint32_t x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r6 = x_; } // 51 load
r5 = r5 ^ r3; // 52 xor
r5 = r5 + r7 + ((((sel >> 31u) & 1u) != 0u) ? 0xcca8fa7cu : 0xd6457f6eu); // 53 add
r7 = rotr_var(r7, r1); // 54 rotr
r6 = r6 + r7 + ((((sel >> 12u) & 1u) != 0u) ? 0x826cb755u : 0xf3e1301cu); // 55 add
{ uint32_t b_ = (r0 & mask) & ~15u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint32_t x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r3 = x_; } // 56 load
r1 = rotr_var(r1, r5); // 57 rotr
{ uint32_t b_ = (r6 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r2 = x_; } // 58 load
r3 = r3 - r7; // 59 sub
r1 = r4 * r6 + r1; // 60 mad
r2 = r1 * r2 + r2; // 61 mad
r4 = r4 ^ r3; // 62 xor
r2 = r2 ^ ds[r4 & mask]; // 63 load
}
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_cache_fill(uint32_t* cache, uint32_t nSegments) {
if (nSegments == 0u) return cudaErrorInvalidValue;
uint32_t block = 256u;
uint32_t grid = (nSegments + block - 1u) / block;
igneum_cache_fill<<<grid, block>>>(cache, nSegments);
return cudaGetLastError();
}
cudaError_t igneum_launch_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems) {
if (nItems == 0u) return cudaErrorInvalidValue;
uint32_t block = 256u;
uint32_t grid = (nItems + block - 1u) / block;
igneum_build<<<grid, block>>>(ds, cache, nItems);
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);
}

View file

@ -0,0 +1,372 @@
// Generated by igneum-pow export (generator v2) for seed "igneum-readwidth/A/6". Do not edit by hand.
// OpenCL C twin of the Metal kernel for the same seed (see proto-opencl/README.md, WAVEFRONT.md and program.metal).
// Built from source at runtime by proto-opencl/host.c, which passes these defines:
// IGNEUM_GROUP work-group size of igneum_hash, a multiple of 32 (default 32: one work-group = one 32-lane unit)
// IGNEUM_EXCHANGE 0 = local-memory exchange with a barrier (any device, any wave width; the default)
// 1 = sub_group_shuffle_xor (cl_khr_subgroup_shuffle), only with IGNEUM_GROUP 32 and a sub-group size of exactly 32
// 2 = intel_sub_group_shuffle_xor (cl_intel_subgroups), same condition
// The verification unit is always 32 lanes. A 64-wide hardware wave (AMD GCN/CDNA, RDNA in wave64) runs two units;
// the exchange masks are 1, 2, 4, 8, 16, so every partner lane lies inside the lane's own aligned run of 32.
#ifndef IGNEUM_GROUP
#define IGNEUM_GROUP 32
#endif
#ifndef IGNEUM_EXCHANGE
#define IGNEUM_EXCHANGE 0
#endif
#ifdef __OPENCL_VERSION__
#define IGNEUM_KERNEL_HASH __kernel __attribute__((reqd_work_group_size(IGNEUM_GROUP, 1, 1)))
#define IGNEUM_LOCAL_WORDS(name, n) __local uint name[n]
#if IGNEUM_EXCHANGE == 1
#ifdef cl_khr_subgroups
#pragma OPENCL EXTENSION cl_khr_subgroups : enable
#endif
#ifdef cl_khr_subgroup_shuffle
#pragma OPENCL EXTENSION cl_khr_subgroup_shuffle : enable
#endif
#elif IGNEUM_EXCHANGE == 2
#pragma OPENCL EXTENSION cl_intel_subgroups : enable
#endif
#else
// Not an OpenCL compiler: proto-opencl/emu compiles this file as C++ and supplies the built-ins and these two macros.
#include "emu_opencl.h"
#endif
#if IGNEUM_EXCHANGE == 1
#define IGNEUM_SHFL_XOR(dst, a, m) dst = sub_group_shuffle_xor((a), (uint)(m))
#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u)
#elif IGNEUM_EXCHANGE == 2
#define IGNEUM_SHFL_XOR(dst, a, m) dst = intel_sub_group_shuffle_xor((a), (uint)(m))
#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u)
#else
// Local-memory exchange. Two buffers of IGNEUM_GROUP words alternate (xk counts exchanges), so one barrier per
// exchange is enough: a lane can only overwrite buffer b at exchange k+2 after passing barrier k+1, and every lane
// reaches barrier k+1 only after its read of buffer b at exchange k. The partner lid ^ m stays inside the lane's
// aligned run of 32 because m < 32. Control flow is uniform, so every work-item reaches every barrier.
#define IGNEUM_SHFL_XOR(dst, a, m) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid ^ (uint)(m))]; xk += 1u; }
#define IGNEUM_BCAST0(dst, a) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid & ~31u)]; xk += 1u; }
#endif
static inline uint splitmix32(uint 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. OpenCL rotate() rotates left by n modulo 32.
static inline uint rotl_imm(uint x, uint n) { return rotate(x, n); }
// Right rotation by n modulo 32 as a left rotation by (32 - n) modulo 32; n == 0 gives x.
static inline uint rotr_var(uint x, uint n) { return rotate(x, (0u - n) & 31u); }
static 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;
}
// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines.
// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals.
#define MH_CACHE_LINE_MASK 0x003fffffu
#define MH_SEGMENT_LINES 64u
#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); }
static inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site
// y = ChaCha12 core(x) + x
static inline void mh_chacha_block(const uint* x, uint* y) {
for (uint i = 0u; i < 16u; ++i) y[i] = x[i];
for (uint r = 0u; r < 6u; ++r) {
MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u)
MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u)
}
for (uint i = 0u; i < 16u; ++i) y[i] += x[i];
}
// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0.
static inline void mh_cache_segment(__global uint* cache, uint seg) {
uint prev[16]; uint x[16]; uint y[16];
for (uint i = 0u; i < 16u; ++i) prev[i] = 0u;
for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) {
x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3];
x[4] = 0x3067619fu ^ prev[4];
x[5] = 0x3c269176u ^ prev[5];
x[6] = 0x84a03b03u ^ prev[6];
x[7] = 0xf8c63294u ^ prev[7];
x[8] = 0xff977c5bu ^ prev[8];
x[9] = 0xe60def3eu ^ prev[9];
x[10] = 0x63630141u ^ prev[10];
x[11] = 0xb8fbcb58u ^ prev[11];
x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15];
mh_chacha_block(x, y);
__global uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u);
for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; }
}
}
// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations.
static inline void mh_mixer(uint* s, uint rk) {
s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u;
s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu;
s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u;
s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu;
s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u;
s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u;
s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u;
s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u;
s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du;
s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u;
s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du;
s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu;
s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du;
s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu;
s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u;
s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u;
MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u)
MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u)
MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u)
MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u)
}
// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer.
static inline void mh_item(__global const uint* cache, uint t, uint* s) {
s[0] = 0x3067619fu;
s[1] = 0x3c269176u;
s[2] = 0x84a03b03u;
s[3] = 0xf8c63294u;
s[4] = 0xff977c5bu;
s[5] = 0xe60def3eu;
s[6] = 0x63630141u;
s[7] = 0xb8fbcb58u;
s[8] = t * 0x42146205u + 0xbab68293u;
s[9] = t * 0x52cbe0fbu + 0xcc162340u;
s[10] = t * 0x7ecf4a03u + 0x6ce151ccu;
s[11] = t * 0x6728907fu + 0xe62b8997u;
s[12] = t * 0xd81d9751u + 0xc9c80297u;
s[13] = t * 0x132952c3u + 0xf74a1654u;
s[14] = t * 0xf60de277u + 0x3d704af5u;
s[15] = t * 0x05358035u + 0x3cf522b7u;
for (uint r = 0u; r < 8u; ++r) {
mh_mixer(s, 0x9E3779B9u * (r + 1u));
__global const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u);
for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i];
}
mh_mixer(s, 0x9E3779B9u * 9u);
}
// dataset[w] without the dataset: derive item w >> 4 and take word w & 15.
static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; }
// Memory-hard dataset (MEMHARD.md). One work-item per cache segment; one work-item per 64-byte dataset item.
// The same constants as memhard.h in this pack (one emitter, three dialects).
__kernel void igneum_cache_fill(__global uint* cache, uint nSegments) {
uint seg = (uint)get_global_id(0);
if (seg < nSegments) mh_cache_segment(cache, seg);
}
__kernel void igneum_build(__global uint* ds, __global const uint* cache, uint nItems) {
uint t = (uint)get_global_id(0);
if (t < nItems) {
uint s[16];
mh_item(cache, t, s);
__global uint* d = ds + ((ulong)t * 16u);
for (uint i = 0u; i < 16u; ++i) d[i] = s[i];
}
}
// One hash per work-item. IGNEUM_GROUP is a multiple of 32; lane = lid & 31 and every exchange stays inside the
// lane's own aligned run of 32 work-items, exactly like simd_shuffle_xor inside a 32-wide Metal SIMD group and
// __shfl_xor_sync inside a CUDA warp. Control flow is uniform (no branches at all).
IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask) {
uint gid = (uint)get_global_id(0);
uint lid = (uint)get_local_id(0);
uint nonce = baseNonce + gid;
uint r0, r1, r2, r3, r4, r5, r6, r7;
#if IGNEUM_EXCHANGE == 0
IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP);
uint xk = 0u;
#else
(void)lid;
#endif
{ uint x = nonce ^ 0x2a53aad4u; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0x37fcb6e2u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1]
{ uint x = nonce ^ 0x37fcb6e2u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0x25d66a27u; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2]
{ uint x = nonce ^ 0x25d66a27u; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0xf5249e5eu; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3]
{ uint x = nonce ^ 0xf5249e5eu; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0x15f86a59u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4]
{ uint x = nonce ^ 0x15f86a59u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0x8144559au; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5]
{ uint x = nonce ^ 0x8144559au; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0xcad741a6u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6]
{ uint x = nonce ^ 0xcad741a6u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0xcb961373u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7]
{ uint x = nonce ^ 0xcb961373u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x2a53aad4u; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0]
for (uint it = 0u; it < 8u; ++it) {
uint sel = r0;
r0 = rotl_imm(r0, 10u); // 0 rotl
r5 = r3 * r1 + r5; // 1 mad
{ uint b_ = (r0 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r5 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r5 = x_; } // 2 load
{ uint b_ = (r5 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r6 = x_; } // 3 load
r5 = r2 * r2 + r5; // 4 mad
r4 = r4 + r3 + ((((sel >> 10u) & 1u) != 0u) ? 0x4edf355au : 0xbd587d10u); // 5 add
r3 = r3 ^ r2; // 6 xor
r5 = r5 ^ r3; // 7 xor
r7 = r6 * r2 + r7; // 8 mad
r7 = r6 * r3 + r7; // 9 mad
{ uint b_ = (r5 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r3 = x_; } // 10 load
r1 = mul_hi(r1, r4); // 11 mulhi
r1 = r1 ^ r6; // 12 xor
r2 = mul_hi(r2, r1); // 13 mulhi
{ uint t_; IGNEUM_SHFL_XOR(t_, r7, 4u); r6 = r6 ^ t_; } // 14 shfl
r4 = r6 * r4 + r4; // 15 mad
{ uint t_; IGNEUM_SHFL_XOR(t_, r0, 8u); r1 = r1 ^ t_; } // 16 shfl
r5 = r5 ^ r4; // 17 xor
r0 = r0 * r2; // 18 mul
r6 = r6 ^ ds[r3 & mask]; // 19 load
r6 = mul_hi(r6, r2); // 20 mulhi
r7 = r2 * r6 + r7; // 21 mad
r4 = r4 * r5; // 22 mul
r3 = r3 * r5; // 23 mul
r6 = r6 * r4; // 24 mul
r3 = r3 ^ ds[r7 & mask]; // 25 load
r2 = rotr_var(r2, r5); // 26 rotr
r5 = r5 | r6; // 27 or
{ uint b_ = (r2 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r4 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r4 = x_; } // 28 load
r6 = r6 | r0; // 29 or
r0 = r0 + r3 + ((((sel >> 2u) & 1u) != 0u) ? 0xdca976acu : 0x5faa0547u); // 30 add
r6 = mul_hi(r6, r0); // 31 mulhi
r3 = rotr_var(r3, r7); // 32 rotr
{ uint t_; IGNEUM_SHFL_XOR(t_, r4, 4u); r0 = r0 ^ t_; } // 33 shfl
r2 = rotl_imm(r2, 17u); // 34 rotl
r7 = mul_hi(r7, r6); // 35 mulhi
{ uint t_; IGNEUM_SHFL_XOR(t_, r2, 2u); r3 = r3 ^ t_; } // 36 shfl
r6 = r6 ^ ds[r0 & mask]; // 37 load
{ uint b_ = (r6 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r0 = x_; } // 38 load
r1 = r1 ^ r0; // 39 xor
{ uint t_; IGNEUM_SHFL_XOR(t_, r1, 2u); r3 = r3 ^ t_; } // 40 shfl
r1 = r1 ^ ds[r5 & mask]; // 41 load
r3 = r3 * r2; // 42 mul
r2 = r2 ^ ds[r7 & mask]; // 43 load
r7 = r7 + r3 + ((((sel >> 7u) & 1u) != 0u) ? 0x610bd3c2u : 0xe28c457cu); // 44 add
r7 = mul_hi(r7, r6); // 45 mulhi
r3 = r3 ^ r4; // 46 xor
r3 = r3 ^ ds[r7 & mask]; // 47 load
r7 = mul_hi(r7, r2); // 48 mulhi
r6 = mul_hi(r6, r3); // 49 mulhi
{ uint b_ = (r1 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r2 = x_; } // 50 load
{ uint b_ = (r3 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r6 = x_; } // 51 load
r5 = r5 ^ r3; // 52 xor
r5 = r5 + r7 + ((((sel >> 31u) & 1u) != 0u) ? 0xcca8fa7cu : 0xd6457f6eu); // 53 add
r7 = rotr_var(r7, r1); // 54 rotr
r6 = r6 + r7 + ((((sel >> 12u) & 1u) != 0u) ? 0x826cb755u : 0xf3e1301cu); // 55 add
{ uint b_ = (r0 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r3 = x_; } // 56 load
r1 = rotr_var(r1, r5); // 57 rotr
{ uint b_ = (r6 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r2 = x_; } // 58 load
r3 = r3 - r7; // 59 sub
r1 = r4 * r6 + r1; // 60 mad
r2 = r1 * r2 + r2; // 61 mad
r4 = r4 ^ r3; // 62 xor
r2 = r2 ^ ds[r4 & mask]; // 63 load
}
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;
}
#if IGNEUM_EXCHANGE != 0
// Reports the sub-group size this device uses for a work-group of IGNEUM_GROUP items. host.c runs it only when the
// per-kernel query (clGetKernelSubGroupInfoKHR on igneum_hash) is unavailable; that query is preferred because a
// compiler may pick a different wave width per kernel (RDNA: wave32 or wave64). See WAVEFRONT.md.
IGNEUM_KERNEL_HASH void igneum_probe_subgroup(__global uint* out) {
if (get_local_id(0) == 0u) { out[0] = get_sub_group_size(); out[1] = get_num_sub_groups(); }
}
#endif
// Header-bound variant (bind.rs): the init words come from initw, not SEEDW. Same body as igneum_hash.
IGNEUM_KERNEL_HASH void igneum_hash_bound(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask, __global const uint* initw) {
uint gid = (uint)get_global_id(0);
uint lid = (uint)get_local_id(0);
uint nonce = baseNonce + gid;
uint r0, r1, r2, r3, r4, r5, r6, r7;
uint iw0 = initw[0], iw1 = initw[1], iw2 = initw[2], iw3 = initw[3], iw4 = initw[4], iw5 = initw[5], iw6 = initw[6], iw7 = initw[7];
#if IGNEUM_EXCHANGE == 0
IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP);
uint xk = 0u;
#else
(void)lid;
#endif
{ uint x = nonce ^ iw0; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ iw1; }
{ uint x = nonce ^ iw1; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ iw2; }
{ uint x = nonce ^ iw2; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ iw3; }
{ uint x = nonce ^ iw3; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ iw4; }
{ uint x = nonce ^ iw4; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ iw5; }
{ uint x = nonce ^ iw5; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ iw6; }
{ uint x = nonce ^ iw6; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ iw7; }
{ uint x = nonce ^ iw7; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ iw0; }
for (uint it = 0u; it < 8u; ++it) {
uint sel = r0;
r0 = rotl_imm(r0, 10u); // 0 rotl
r5 = r3 * r1 + r5; // 1 mad
{ uint b_ = (r0 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r5 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r5 = x_; } // 2 load
{ uint b_ = (r5 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r6 = x_; } // 3 load
r5 = r2 * r2 + r5; // 4 mad
r4 = r4 + r3 + ((((sel >> 10u) & 1u) != 0u) ? 0x4edf355au : 0xbd587d10u); // 5 add
r3 = r3 ^ r2; // 6 xor
r5 = r5 ^ r3; // 7 xor
r7 = r6 * r2 + r7; // 8 mad
r7 = r6 * r3 + r7; // 9 mad
{ uint b_ = (r5 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r3 = x_; } // 10 load
r1 = mul_hi(r1, r4); // 11 mulhi
r1 = r1 ^ r6; // 12 xor
r2 = mul_hi(r2, r1); // 13 mulhi
{ uint t_; IGNEUM_SHFL_XOR(t_, r7, 4u); r6 = r6 ^ t_; } // 14 shfl
r4 = r6 * r4 + r4; // 15 mad
{ uint t_; IGNEUM_SHFL_XOR(t_, r0, 8u); r1 = r1 ^ t_; } // 16 shfl
r5 = r5 ^ r4; // 17 xor
r0 = r0 * r2; // 18 mul
r6 = r6 ^ ds[r3 & mask]; // 19 load
r6 = mul_hi(r6, r2); // 20 mulhi
r7 = r2 * r6 + r7; // 21 mad
r4 = r4 * r5; // 22 mul
r3 = r3 * r5; // 23 mul
r6 = r6 * r4; // 24 mul
r3 = r3 ^ ds[r7 & mask]; // 25 load
r2 = rotr_var(r2, r5); // 26 rotr
r5 = r5 | r6; // 27 or
{ uint b_ = (r2 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r4 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r4 = x_; } // 28 load
r6 = r6 | r0; // 29 or
r0 = r0 + r3 + ((((sel >> 2u) & 1u) != 0u) ? 0xdca976acu : 0x5faa0547u); // 30 add
r6 = mul_hi(r6, r0); // 31 mulhi
r3 = rotr_var(r3, r7); // 32 rotr
{ uint t_; IGNEUM_SHFL_XOR(t_, r4, 4u); r0 = r0 ^ t_; } // 33 shfl
r2 = rotl_imm(r2, 17u); // 34 rotl
r7 = mul_hi(r7, r6); // 35 mulhi
{ uint t_; IGNEUM_SHFL_XOR(t_, r2, 2u); r3 = r3 ^ t_; } // 36 shfl
r6 = r6 ^ ds[r0 & mask]; // 37 load
{ uint b_ = (r6 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r0 = x_; } // 38 load
r1 = r1 ^ r0; // 39 xor
{ uint t_; IGNEUM_SHFL_XOR(t_, r1, 2u); r3 = r3 ^ t_; } // 40 shfl
r1 = r1 ^ ds[r5 & mask]; // 41 load
r3 = r3 * r2; // 42 mul
r2 = r2 ^ ds[r7 & mask]; // 43 load
r7 = r7 + r3 + ((((sel >> 7u) & 1u) != 0u) ? 0x610bd3c2u : 0xe28c457cu); // 44 add
r7 = mul_hi(r7, r6); // 45 mulhi
r3 = r3 ^ r4; // 46 xor
r3 = r3 ^ ds[r7 & mask]; // 47 load
r7 = mul_hi(r7, r2); // 48 mulhi
r6 = mul_hi(r6, r3); // 49 mulhi
{ uint b_ = (r1 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r2 = x_; } // 50 load
{ uint b_ = (r3 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r6 = x_; } // 51 load
r5 = r5 ^ r3; // 52 xor
r5 = r5 + r7 + ((((sel >> 31u) & 1u) != 0u) ? 0xcca8fa7cu : 0xd6457f6eu); // 53 add
r7 = rotr_var(r7, r1); // 54 rotr
r6 = r6 + r7 + ((((sel >> 12u) & 1u) != 0u) ? 0x826cb755u : 0xf3e1301cu); // 55 add
{ uint b_ = (r0 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r3 = x_; } // 56 load
r1 = rotr_var(r1, r5); // 57 rotr
{ uint b_ = (r6 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r2 = x_; } // 58 load
r3 = r3 - r7; // 59 sub
r1 = r4 * r6 + r1; // 60 mad
r2 = r1 * r2 + r2; // 61 mad
r4 = r4 ^ r3; // 62 xor
r2 = r2 ^ ds[r4 & mask]; // 63 load
}
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;
}

View file

@ -0,0 +1,123 @@
// Generated by igneum-pow export (generator v2) for seed "igneum-readwidth/A/6". Do not edit by hand.
// Header-bound twin of igneum_hash in kernel.cu: the init words come from a kernel argument, not SEEDW.
// Host declarations (also in program_bound.h if present):
// struct IgneumInitWords { uint32_t w[8]; };
// cudaError_t igneum_launch_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask,
// IgneumInitWords iw, uint32_t nonces, uint32_t blockWarps);
// cudaError_t igneum_hash_bound_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps);
#include <cuda_runtime.h>
#include <cstdint>
#include "program.h"
struct IgneumInitWords { uint32_t w[8]; };
__device__ __forceinline__ uint32_t splitmix32(uint32_t x) {
x ^= x >> 16; x *= 0x7feb352du;
x ^= x >> 15; x *= 0x846ca68bu;
x ^= x >> 16;
return x;
}
__device__ __forceinline__ uint32_t rotl_imm(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); }
__device__ __forceinline__ uint32_t rotr_var(uint32_t x, uint32_t n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); }
__global__ void igneum_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, IgneumInitWords iw) {
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 ^ iw.w[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ iw.w[1]; }
{ uint32_t x = nonce ^ iw.w[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ iw.w[2]; }
{ uint32_t x = nonce ^ iw.w[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ iw.w[3]; }
{ uint32_t x = nonce ^ iw.w[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ iw.w[4]; }
{ uint32_t x = nonce ^ iw.w[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ iw.w[5]; }
{ uint32_t x = nonce ^ iw.w[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ iw.w[6]; }
{ uint32_t x = nonce ^ iw.w[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ iw.w[7]; }
{ uint32_t x = nonce ^ iw.w[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ iw.w[0]; }
for (uint32_t it = 0u; it < 8u; ++it) {
uint32_t sel = r0;
r0 = rotl_imm(r0, 10u); // 0 rotl
r5 = r3 * r1 + r5; // 1 mad
{ uint32_t b_ = (r0 & mask) & ~15u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint32_t x_ = r5 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r5 = x_; } // 2 load
{ uint32_t b_ = (r5 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r6 = x_; } // 3 load
r5 = r2 * r2 + r5; // 4 mad
r4 = r4 + r3 + ((((sel >> 10u) & 1u) != 0u) ? 0x4edf355au : 0xbd587d10u); // 5 add
r3 = r3 ^ r2; // 6 xor
r5 = r5 ^ r3; // 7 xor
r7 = r6 * r2 + r7; // 8 mad
r7 = r6 * r3 + r7; // 9 mad
{ uint32_t b_ = (r5 & mask) & ~15u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint32_t x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r3 = x_; } // 10 load
r1 = __umulhi(r1, r4); // 11 mulhi
r1 = r1 ^ r6; // 12 xor
r2 = __umulhi(r2, r1); // 13 mulhi
r6 = r6 ^ __shfl_xor_sync(0xffffffffu, r7, 4); // 14 shfl
r4 = r6 * r4 + r4; // 15 mad
r1 = r1 ^ __shfl_xor_sync(0xffffffffu, r0, 8); // 16 shfl
r5 = r5 ^ r4; // 17 xor
r0 = r0 * r2; // 18 mul
r6 = r6 ^ ds[r3 & mask]; // 19 load
r6 = __umulhi(r6, r2); // 20 mulhi
r7 = r2 * r6 + r7; // 21 mad
r4 = r4 * r5; // 22 mul
r3 = r3 * r5; // 23 mul
r6 = r6 * r4; // 24 mul
r3 = r3 ^ ds[r7 & mask]; // 25 load
r2 = rotr_var(r2, r5); // 26 rotr
r5 = r5 | r6; // 27 or
{ uint32_t b_ = (r2 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r4 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r4 = x_; } // 28 load
r6 = r6 | r0; // 29 or
r0 = r0 + r3 + ((((sel >> 2u) & 1u) != 0u) ? 0xdca976acu : 0x5faa0547u); // 30 add
r6 = __umulhi(r6, r0); // 31 mulhi
r3 = rotr_var(r3, r7); // 32 rotr
r0 = r0 ^ __shfl_xor_sync(0xffffffffu, r4, 4); // 33 shfl
r2 = rotl_imm(r2, 17u); // 34 rotl
r7 = __umulhi(r7, r6); // 35 mulhi
r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r2, 2); // 36 shfl
r6 = r6 ^ ds[r0 & mask]; // 37 load
{ uint32_t b_ = (r6 & mask) & ~15u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint32_t x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r0 = x_; } // 38 load
r1 = r1 ^ r0; // 39 xor
r3 = r3 ^ __shfl_xor_sync(0xffffffffu, r1, 2); // 40 shfl
r1 = r1 ^ ds[r5 & mask]; // 41 load
r3 = r3 * r2; // 42 mul
r2 = r2 ^ ds[r7 & mask]; // 43 load
r7 = r7 + r3 + ((((sel >> 7u) & 1u) != 0u) ? 0x610bd3c2u : 0xe28c457cu); // 44 add
r7 = __umulhi(r7, r6); // 45 mulhi
r3 = r3 ^ r4; // 46 xor
r3 = r3 ^ ds[r7 & mask]; // 47 load
r7 = __umulhi(r7, r2); // 48 mulhi
r6 = __umulhi(r6, r3); // 49 mulhi
{ uint32_t b_ = (r1 & mask) & ~15u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint32_t x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r2 = x_; } // 50 load
{ uint32_t b_ = (r3 & mask) & ~15u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint32_t x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r6 = x_; } // 51 load
r5 = r5 ^ r3; // 52 xor
r5 = r5 + r7 + ((((sel >> 31u) & 1u) != 0u) ? 0xcca8fa7cu : 0xd6457f6eu); // 53 add
r7 = rotr_var(r7, r1); // 54 rotr
r6 = r6 + r7 + ((((sel >> 12u) & 1u) != 0u) ? 0x826cb755u : 0xf3e1301cu); // 55 add
{ uint32_t b_ = (r0 & mask) & ~15u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint32_t x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r3 = x_; } // 56 load
r1 = rotr_var(r1, r5); // 57 rotr
{ uint32_t b_ = (r6 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r2 = x_; } // 58 load
r3 = r3 - r7; // 59 sub
r1 = r4 * r6 + r1; // 60 mad
r2 = r1 * r2 + r2; // 61 mad
r4 = r4 ^ r3; // 62 xor
r2 = r2 ^ ds[r4 & mask]; // 63 load
}
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;
}
cudaError_t igneum_launch_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask,
IgneumInitWords iw, 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_bound<<<nonces / block, block>>>(ds, out, baseNonce, mask, iw);
return cudaGetLastError();
}
cudaError_t igneum_hash_bound_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps) {
cudaFuncAttributes attr;
cudaError_t e = cudaFuncGetAttributes(&attr, igneum_hash_bound);
if (e != cudaSuccess) return e;
*numRegs = attr.numRegs;
return cudaOccupancyMaxActiveBlocksPerMultiprocessor(blocksPerSM, igneum_hash_bound, (int)(32u * blockWarps), 0);
}

View file

@ -0,0 +1,108 @@
// Generated by igneum-pow export (generator v2) for seed "igneum-readwidth/A/6". Do not edit by hand.
// Memory-hard dataset core, the same text that the Mac's Metal kernels and CPU verifier were checked against.
// Included by kernel.cu (device), host.cu (host reference) and proto-opencl/host.c (C99 host reference).
// See proto-metal/MEMHARD.md for the construction. kernel.cl carries the same text in OpenCL C.
#pragma once
#ifdef __cplusplus
#include <cstdint>
#else
#include <stdint.h>
#endif
#if defined(__CUDACC__)
#define IGNEUM_HD __host__ __device__ __forceinline__
#elif defined(_MSC_VER) && !defined(__cplusplus)
#define IGNEUM_HD static __inline
#else
#define IGNEUM_HD static inline
#endif
// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines.
// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals.
#define MH_CACHE_LINE_MASK 0x003fffffu
#define MH_SEGMENT_LINES 64u
#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); }
IGNEUM_HD uint32_t mh_rotl(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site
// y = ChaCha12 core(x) + x
IGNEUM_HD void mh_chacha_block(const uint32_t* x, uint32_t* y) {
for (uint32_t i = 0u; i < 16u; ++i) y[i] = x[i];
for (uint32_t r = 0u; r < 6u; ++r) {
MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u)
MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u)
}
for (uint32_t i = 0u; i < 16u; ++i) y[i] += x[i];
}
// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0.
IGNEUM_HD void mh_cache_segment(uint32_t* cache, uint32_t seg) {
uint32_t prev[16]; uint32_t x[16]; uint32_t y[16];
for (uint32_t i = 0u; i < 16u; ++i) prev[i] = 0u;
for (uint32_t j = 0u; j < MH_SEGMENT_LINES; ++j) {
x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3];
x[4] = 0x3067619fu ^ prev[4];
x[5] = 0x3c269176u ^ prev[5];
x[6] = 0x84a03b03u ^ prev[6];
x[7] = 0xf8c63294u ^ prev[7];
x[8] = 0xff977c5bu ^ prev[8];
x[9] = 0xe60def3eu ^ prev[9];
x[10] = 0x63630141u ^ prev[10];
x[11] = 0xb8fbcb58u ^ prev[11];
x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15];
mh_chacha_block(x, y);
uint32_t* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u);
for (uint32_t i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; }
}
}
// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations.
IGNEUM_HD void mh_mixer(uint32_t* s, uint32_t rk) {
s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u;
s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu;
s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u;
s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu;
s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u;
s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u;
s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u;
s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u;
s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du;
s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u;
s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du;
s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu;
s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du;
s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu;
s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u;
s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u;
MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u)
MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u)
MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u)
MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u)
}
// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer.
IGNEUM_HD void mh_item(const uint32_t* cache, uint32_t t, uint32_t* s) {
s[0] = 0x3067619fu;
s[1] = 0x3c269176u;
s[2] = 0x84a03b03u;
s[3] = 0xf8c63294u;
s[4] = 0xff977c5bu;
s[5] = 0xe60def3eu;
s[6] = 0x63630141u;
s[7] = 0xb8fbcb58u;
s[8] = t * 0x42146205u + 0xbab68293u;
s[9] = t * 0x52cbe0fbu + 0xcc162340u;
s[10] = t * 0x7ecf4a03u + 0x6ce151ccu;
s[11] = t * 0x6728907fu + 0xe62b8997u;
s[12] = t * 0xd81d9751u + 0xc9c80297u;
s[13] = t * 0x132952c3u + 0xf74a1654u;
s[14] = t * 0xf60de277u + 0x3d704af5u;
s[15] = t * 0x05358035u + 0x3cf522b7u;
for (uint32_t r = 0u; r < 8u; ++r) {
mh_mixer(s, 0x9E3779B9u * (r + 1u));
const uint32_t* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u);
for (uint32_t i = 0u; i < 16u; ++i) s[i] ^= line[i];
}
mh_mixer(s, 0x9E3779B9u * 9u);
}
// dataset[w] without the dataset: derive item w >> 4 and take word w & 15.
IGNEUM_HD uint32_t mh_word(const uint32_t* cache, uint32_t w) { uint32_t s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; }

View file

@ -0,0 +1,106 @@
#include <metal_stdlib>
using namespace metal;
// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines.
// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals.
#define MH_CACHE_LINE_MASK 0x003fffffu
#define MH_SEGMENT_LINES 64u
#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); }
inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site
// y = ChaCha12 core(x) + x
inline void mh_chacha_block(const thread uint* x, thread uint* y) {
for (uint i = 0u; i < 16u; ++i) y[i] = x[i];
for (uint r = 0u; r < 6u; ++r) {
MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u)
MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u)
}
for (uint i = 0u; i < 16u; ++i) y[i] += x[i];
}
// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0.
inline void mh_cache_segment(device uint* cache, uint seg) {
uint prev[16]; uint x[16]; uint y[16];
for (uint i = 0u; i < 16u; ++i) prev[i] = 0u;
for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) {
x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3];
x[4] = 0x3067619fu ^ prev[4];
x[5] = 0x3c269176u ^ prev[5];
x[6] = 0x84a03b03u ^ prev[6];
x[7] = 0xf8c63294u ^ prev[7];
x[8] = 0xff977c5bu ^ prev[8];
x[9] = 0xe60def3eu ^ prev[9];
x[10] = 0x63630141u ^ prev[10];
x[11] = 0xb8fbcb58u ^ prev[11];
x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15];
mh_chacha_block(x, y);
device uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u);
for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; }
}
}
// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations.
inline void mh_mixer(thread uint* s, uint rk) {
s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u;
s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu;
s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u;
s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu;
s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u;
s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u;
s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u;
s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u;
s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du;
s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u;
s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du;
s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu;
s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du;
s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu;
s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u;
s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u;
MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u)
MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u)
MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u)
MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u)
}
// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer.
inline void mh_item(device const uint* cache, uint t, thread uint* s) {
s[0] = 0x3067619fu;
s[1] = 0x3c269176u;
s[2] = 0x84a03b03u;
s[3] = 0xf8c63294u;
s[4] = 0xff977c5bu;
s[5] = 0xe60def3eu;
s[6] = 0x63630141u;
s[7] = 0xb8fbcb58u;
s[8] = t * 0x42146205u + 0xbab68293u;
s[9] = t * 0x52cbe0fbu + 0xcc162340u;
s[10] = t * 0x7ecf4a03u + 0x6ce151ccu;
s[11] = t * 0x6728907fu + 0xe62b8997u;
s[12] = t * 0xd81d9751u + 0xc9c80297u;
s[13] = t * 0x132952c3u + 0xf74a1654u;
s[14] = t * 0xf60de277u + 0x3d704af5u;
s[15] = t * 0x05358035u + 0x3cf522b7u;
for (uint r = 0u; r < 8u; ++r) {
mh_mixer(s, 0x9E3779B9u * (r + 1u));
device const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u);
for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i];
}
mh_mixer(s, 0x9E3779B9u * 9u);
}
// dataset[w] without the dataset: derive item w >> 4 and take word w & 15.
inline uint mh_word(device const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; }
// One thread per segment (2^16 threads).
kernel void igneum_cache_fill(device uint* cache [[buffer(0)]], uint gid [[thread_position_in_grid]]) {
mh_cache_segment(cache, gid);
}
// One thread per 64-byte item (dataset words / 16 threads).
kernel void igneum_build(device const uint* cache [[buffer(0)]], device uint* dataset [[buffer(1)]],
uint gid [[thread_position_in_grid]]) {
uint s[16];
mh_item(cache, gid, s);
device uint* d = dataset + gid * 16u;
for (uint i = 0u; i < 16u; ++i) d[i] = s[i];
}

View file

@ -0,0 +1,60 @@
// Generated by igneum-pow export (generator v2) for seed "igneum-readwidth/A/6". Do not edit by hand.
// Program metadata for host.cu plus the launch wrappers defined in kernel.cu.
// Also included by proto-opencl/host.c (C99), which defines IGNEUM_NO_CUDA first and reads only the macros.
#pragma once
#ifdef __cplusplus
#include <cstdint>
#else
#include <stdint.h>
#endif
#ifndef IGNEUM_NO_CUDA
#include <cuda_runtime.h>
#endif
#define IGNEUM_SEED_STRING "igneum-readwidth/A/6"
#define IGNEUM_SEED_BYTES_HEX "69676e65756d2d7265616477696474682f412f36"
#define IGNEUM_GENERATOR 2
#define IGNEUM_PROGRAM_ATTEMPT 0
#define IGNEUM_PROGRAM_ID 0xd9bea03c18e5b8d6ull
#define IGNEUM_DAY_STRING "2026-10-03"
#define IGNEUM_DAY_BYTES_HEX "6461792f323032362d31302d3033"
#define IGNEUM_DAY0 0x3067619fu
#define IGNEUM_DAY1 0x3c269176u
#define IGNEUM_DATASET_LOG2 28
#define IGNEUM_MASK 0x0fffffffu
#define IGNEUM_LANES 32
#define IGNEUM_ITERATIONS 8
#define IGNEUM_INSTR_COUNT 64
#define IGNEUM_LOADS_PER_HASH 128
#define IGNEUM_WIDE_LOADS_PER_HASH 0
#define IGNEUM_OP_MIX "load=16 mad=8 mulhi=8 xor=8 add=5 mul=5 shfl=5 rotr=4 or=2 rotl=2 sub=1"
// Read-width experiment (5 October 2026, docs/plans/read-width.md): NOT the lottery hash. A load of W words reads
// the W-word-aligned address and folds every word into dst: x = dst ^ w[0]; x = (rotl(x, 11) * 0x9e3779b1) ^ w[j]; dst = x.
#define IGNEUM_LOAD_CLASS "mix50-35-15"
#define IGNEUM_LOAD_SLOTS 16
#define IGNEUM_LOAD_MIX { 50, 35, 15 }
#define IGNEUM_LOAD_WIDTH_COUNTS { 7, 3, 6 } // loads of 4, 16, 64 bytes per program
#define IGNEUM_BYTES_PER_HASH 3680
#define IGNEUM_FOLD_ROT 11
#define IGNEUM_FOLD_MUL 0x9e3779b1u
// 0 = closed-form dataset (ds_elem), 1 = memory-hard cache construction (MEMHARD.md, memhard.h)
#define IGNEUM_DATASET_MODE 1
#define IGNEUM_SEEDW_INIT { 0x2a53aad4u, 0x37fcb6e2u, 0x25d66a27u, 0xf5249e5eu, 0x15f86a59u, 0x8144559au, 0xcad741a6u, 0xcb961373u }
#define IGNEUM_KEY_INIT { 0x3067619fu, 0x3c269176u, 0x84a03b03u, 0xf8c63294u, 0xff977c5bu, 0xe60def3eu, 0x63630141u, 0xb8fbcb58u }
#define IGNEUM_CACHE_LOG2_WORDS 26
#define IGNEUM_CACHE_SEGMENT_LOG2_LINES 6
#define IGNEUM_CACHE_SEGMENTS 65536u
#define IGNEUM_ITEM_ROUNDS 8
#define IGNEUM_MIX_ROT_INIT { 20u, 20u, 19u, 4u, 26u, 3u, 3u, 27u }
#define IGNEUM_MIX_MUL_INIT { 0x42146205u, 0x52cbe0fbu, 0x7ecf4a03u, 0x6728907fu, 0xd81d9751u, 0x132952c3u, 0xf60de277u, 0x05358035u, 0xbaf6499du, 0xe4db9667u, 0x3e98f45du, 0xd0004eddu, 0x2691630du, 0x9beb3bcfu, 0xab310379u, 0x99cfb423u }
#define IGNEUM_MIX_RC_INIT { 0xbab68293u, 0xcc162340u, 0x6ce151ccu, 0xe62b8997u, 0xc9c80297u, 0xf74a1654u, 0x3d704af5u, 0x3cf522b7u, 0x2b9cac04u, 0xa880ac10u, 0x13e5dd1du, 0x6fc3e233u, 0x2d83eeacu, 0x9006e8bfu, 0x2c4b5362u, 0x31b49ee2u }
#ifndef IGNEUM_NO_CUDA
// Defined in kernel.cu. All launch on the default stream and return cudaGetLastError().
cudaError_t igneum_launch_cache_fill(uint32_t* cache, uint32_t nSegments);
cudaError_t igneum_launch_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems);
cudaError_t igneum_launch_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask,
uint32_t nonces, uint32_t blockWarps);
cudaError_t igneum_hash_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps);
#endif

View file

@ -0,0 +1,127 @@
{
"format": "igneum-program-pack-3",
"generator": 2,
"attempt": 0,
"program_id": "0xd9bea03c18e5b8d6",
"program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32",
"dataset_mode": "memory-hard",
"seed": "igneum-readwidth/A/6",
"seed_bytes": "69676e65756d2d7265616477696474682f412f36",
"seed_words": ["0x2a53aad4", "0x37fcb6e2", "0x25d66a27", "0xf5249e5e", "0x15f86a59", "0x8144559a", "0xcad741a6", "0xcb961373"],
"seed_derivation": "seed_words = FNV-1a 64 over seed_bytes (attempt 0) or seed_bytes || attempt_le32 (attempt k >= 1), basis ^ (salt * 0x9E3779B97F4A7C15) for salt 0..3, then h ^= h>>33; h *= 0xff51afd7ed558ccd; h ^= h>>33; words[2*salt] = low 32, words[2*salt+1] = high 32",
"generator_rule": "version 2: exactly 16 load slots drawn first from instructions 1..63 (partial Fisher-Yates), the other 48 ops from the ten non-load weights (sum 75); a load's source is drawn from the registers other than dst written by an earlier instruction and not read by a load since; the candidate must pass the acceptance rule of spec 01 section 1.4.6 (static: no cyclically stale load source, every register has an injecting write; dynamic: 64 units on the seed-keyed closed-form dataset with no constant register bit, no lane-constant load site, under 164 saturated final values, every output bit within 136 of 1024, distinct addresses above 245760), else the next attempt of the seed is tried",
"lanes": 32,
"registers": 8,
"iterations": 8,
"instruction_count": 64,
"loads_per_hash": 128,
"load_class": "mix50-35-15",
"load_slots": 16,
"load_mix_percent_4_16_64": [50, 35, 15],
"load_width_counts_4_16_64": [7, 3, 6],
"bytes_per_hash": 3680,
"wide_load": "read-width experiment (5 October 2026, docs/plans/read-width.md), NOT the lottery hash: a load of W words (width field, 4 or 16) reads dataset[b .. b + W) with b = (src & mask) & ~(W - 1) and folds every word into dst: x = dst ^ w[0]; for j in 1..W: x = (rotl(x, 11) * 0x9e3779b1) ^ w[j]; dst = x; width 1 is the plain load; the width is drawn per instruction from the class mix with one extra below(100) draw after the nine of version 2, and the program id is FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed words || attempt_le32 || mix[3] || load_slots",
"op_mix": {"load": 16, "mad": 8, "mulhi": 8, "xor": 8, "add": 5, "mul": 5, "shfl": 5, "rotr": 4, "or": 2, "rotl": 2, "sub": 1},
"register_init": "for i in 0..7: x = nonce ^ seed_words[i]; x += 0x9e3779b9 * (i+1) (mod 2^32); x = splitmix32(x); r[i] = x ^ seed_words[(i+1) & 7]",
"splitmix32": "x ^= x>>16; x *= 0x7feb352d; x ^= x>>15; x *= 0x846ca68b; x ^= x>>16",
"iteration": "sel = r0 sampled once at the top of each iteration, then all instructions in order",
"output": "lo = r0 ^ rotl(r1,7) ^ rotl(r2,14) ^ rotl(r3,21); hi = r4 ^ rotl(r5,9) ^ rotl(r6,18) ^ rotl(r7,27); out = (hi << 32) | lo",
"op_semantics": {
"add": "dst = dst + src + (bit `bit` of sel ? imm2 : imm)",
"sub": "dst = dst - src",
"mul": "dst = dst * src (low 32)",
"mulhi": "dst = high 32 bits of dst * src",
"xor": "dst = dst ^ src",
"or": "dst = dst | src",
"rotl": "dst = rotl(dst, rot), rot in 1..31",
"rotr": "dst = rotr(dst, src & 31)",
"mad": "dst = src * src2 + dst",
"shfl": "dst = dst ^ (src of lane (lane ^ mask)), mask in {1,2,4,8,16}, within the 32-lane warp",
"load": "dst = dst ^ dataset[src & dataset.mask]",
"wload": "base = (src of lane 0 & dataset.mask) & ~31; dst = dst ^ dataset[base + lane] (warp-coalesced 128-byte load, lever b, only when --wide-frac > 0)"
},
"dataset": {
"log2_words": 28,
"bytes": 1073741824,
"mask": "0x0fffffff",
"day": "2026-10-03",
"day_bytes": "6461792f323032362d31302d3033",
"day_words_from": "seed_words_from_bytes(day_bytes)",
"d0": "0x3067619f",
"d1": "0x3c269176",
"mode": "memory-hard",
"spec": "proto-metal/MEMHARD.md",
"key": ["0x3067619f", "0x3c269176", "0x84a03b03", "0xf8c63294", "0xff977c5b", "0xe60def3e", "0x63630141", "0xb8fbcb58"],
"key_derivation": "the 8 words of seed_words_from_bytes(day_bytes); d0, d1 are key[0], key[1]",
"cache": {"log2_words": 26, "bytes": 268435456, "line_words": 16, "segment_lines": 64, "segments": 65536, "block": "ChaCha12 core + feed-forward, rotations 16 12 8 7", "sigma": ["0x61707865", "0x3320646e", "0x79622d32", "0x6b206574"], "tag": ["0x49676e65", "0x756d4d48"], "chain": "in_j = prev_line ^ (sigma[0..3] || key[0..7] || seg || j || tag[0..1]); line_j = block(in_j); prev_0 = 0"},
"mixer": {"draw": "SplitMix64 seeded with key[0] | key[1] << 32: rot[0..7] = 1 + next() % 31, mul[0..15] = low32(next()) | 1, rc[0..15] = low32(next())", "rot": [20, 20, 19, 4, 26, 3, 3, 27], "mul": ["0x42146205", "0x52cbe0fb", "0x7ecf4a03", "0x6728907f", "0xd81d9751", "0x132952c3", "0xf60de277", "0x05358035", "0xbaf6499d", "0xe4db9667", "0x3e98f45d", "0xd0004edd", "0x2691630d", "0x9beb3bcf", "0xab310379", "0x99cfb423"], "rc": ["0xbab68293", "0xcc162340", "0x6ce151cc", "0xe62b8997", "0xc9c80297", "0xf74a1654", "0x3d704af5", "0x3cf522b7", "0x2b9cac04", "0xa880ac10", "0x13e5dd1d", "0x6fc3e233", "0x2d83eeac", "0x9006e8bf", "0x2c4b5362", "0x31b49ee2"], "round": "for i in 0..15: s[i] = (s[i] ^ (rc[i] + (r+1) * 0x9E3779B9)) * mul[i]; then quarter rounds on columns (0,4,8,12) (1,5,9,13) (2,6,10,14) (3,7,11,15) with rot[0..3] and diagonals (0,5,10,15) (1,6,11,12) (2,7,8,13) (3,4,9,14) with rot[4..7]", "quarter_round": "a += b; d ^= a; d = rotl(d, r1); c += d; b ^= c; b = rotl(b, r2); a += b; d ^= a; d = rotl(d, r3); c += d; b ^= c; b = rotl(b, r4)"},
"item": "s[0..7] = key; s[8+i] = t * mul[i] + rc[i] for i in 0..7; for r in 0..7: s = M_r(s); line = s[0] & 0x003fffff; s[i] ^= cache[line * 16 + i]; then s = M_8(s); item(t) = s",
"word": "dataset[w] = item(w >> 4)[w & 15]"
},
"instructions": [
{"i": 0, "op": "rotl", "dst": 0, "src": 7, "src2": 6, "imm": "0x2237bb81", "imm2": "0xb069b9f1", "rot": 10, "bit": 18, "mask": 2, "width": 1},
{"i": 1, "op": "mad", "dst": 5, "src": 3, "src2": 1, "imm": "0x4fd258fa", "imm2": "0x918a4f37", "rot": 1, "bit": 23, "mask": 16, "width": 1},
{"i": 2, "op": "load", "dst": 5, "src": 0, "src2": 2, "imm": "0xbf959e37", "imm2": "0x365cef9c", "rot": 27, "bit": 21, "mask": 16, "width": 16},
{"i": 3, "op": "load", "dst": 6, "src": 5, "src2": 5, "imm": "0x48715ea5", "imm2": "0x70bc4e9a", "rot": 21, "bit": 7, "mask": 8, "width": 4},
{"i": 4, "op": "mad", "dst": 5, "src": 2, "src2": 2, "imm": "0x84131e0c", "imm2": "0xd3744459", "rot": 24, "bit": 28, "mask": 2, "width": 1},
{"i": 5, "op": "add", "dst": 4, "src": 3, "src2": 4, "imm": "0xbd587d10", "imm2": "0x4edf355a", "rot": 18, "bit": 10, "mask": 8, "width": 1},
{"i": 6, "op": "xor", "dst": 3, "src": 2, "src2": 3, "imm": "0x41f40748", "imm2": "0x6cfe9987", "rot": 14, "bit": 6, "mask": 16, "width": 1},
{"i": 7, "op": "xor", "dst": 5, "src": 3, "src2": 4, "imm": "0x2c14f9c9", "imm2": "0x4f37fd66", "rot": 3, "bit": 1, "mask": 1, "width": 1},
{"i": 8, "op": "mad", "dst": 7, "src": 6, "src2": 2, "imm": "0xcb0b8c69", "imm2": "0x451450b3", "rot": 21, "bit": 7, "mask": 16, "width": 1},
{"i": 9, "op": "mad", "dst": 7, "src": 6, "src2": 3, "imm": "0x5d258713", "imm2": "0x9822328d", "rot": 30, "bit": 31, "mask": 16, "width": 1},
{"i": 10, "op": "load", "dst": 3, "src": 5, "src2": 4, "imm": "0x61b87fba", "imm2": "0x51702831", "rot": 20, "bit": 30, "mask": 8, "width": 16},
{"i": 11, "op": "mulhi", "dst": 1, "src": 4, "src2": 7, "imm": "0xc9e85411", "imm2": "0xe89df827", "rot": 26, "bit": 23, "mask": 8, "width": 1},
{"i": 12, "op": "xor", "dst": 1, "src": 6, "src2": 6, "imm": "0x1ceb672f", "imm2": "0xf15040b7", "rot": 27, "bit": 19, "mask": 16, "width": 1},
{"i": 13, "op": "mulhi", "dst": 2, "src": 1, "src2": 3, "imm": "0xe2dcec46", "imm2": "0x6f243314", "rot": 1, "bit": 3, "mask": 4, "width": 1},
{"i": 14, "op": "shfl", "dst": 6, "src": 7, "src2": 5, "imm": "0x8bac3c39", "imm2": "0x55595cd3", "rot": 1, "bit": 28, "mask": 4, "width": 1},
{"i": 15, "op": "mad", "dst": 4, "src": 6, "src2": 4, "imm": "0x6d3cc858", "imm2": "0x3c5576ea", "rot": 2, "bit": 21, "mask": 1, "width": 1},
{"i": 16, "op": "shfl", "dst": 1, "src": 0, "src2": 1, "imm": "0xdd571938", "imm2": "0x044350f3", "rot": 5, "bit": 5, "mask": 8, "width": 1},
{"i": 17, "op": "xor", "dst": 5, "src": 4, "src2": 4, "imm": "0x2c2d15da", "imm2": "0xb15a5c88", "rot": 31, "bit": 19, "mask": 16, "width": 1},
{"i": 18, "op": "mul", "dst": 0, "src": 2, "src2": 3, "imm": "0x033348a5", "imm2": "0xd44c3d67", "rot": 1, "bit": 10, "mask": 16, "width": 1},
{"i": 19, "op": "load", "dst": 6, "src": 3, "src2": 1, "imm": "0x0dda22a1", "imm2": "0x1ae5993b", "rot": 8, "bit": 4, "mask": 2, "width": 1},
{"i": 20, "op": "mulhi", "dst": 6, "src": 2, "src2": 7, "imm": "0xd48e8694", "imm2": "0x11ed9b7b", "rot": 24, "bit": 19, "mask": 16, "width": 1},
{"i": 21, "op": "mad", "dst": 7, "src": 2, "src2": 6, "imm": "0x44beb2fe", "imm2": "0x044aa951", "rot": 28, "bit": 22, "mask": 1, "width": 1},
{"i": 22, "op": "mul", "dst": 4, "src": 5, "src2": 0, "imm": "0xa0256d05", "imm2": "0xd61f8a1a", "rot": 1, "bit": 0, "mask": 16, "width": 1},
{"i": 23, "op": "mul", "dst": 3, "src": 5, "src2": 3, "imm": "0x0b79eb26", "imm2": "0x7f479e48", "rot": 3, "bit": 0, "mask": 8, "width": 1},
{"i": 24, "op": "mul", "dst": 6, "src": 4, "src2": 4, "imm": "0x91742ca1", "imm2": "0x9f369d2a", "rot": 19, "bit": 2, "mask": 1, "width": 1},
{"i": 25, "op": "load", "dst": 3, "src": 7, "src2": 6, "imm": "0x820853df", "imm2": "0xb288d5cd", "rot": 3, "bit": 23, "mask": 8, "width": 1},
{"i": 26, "op": "rotr", "dst": 2, "src": 5, "src2": 7, "imm": "0x65a24fb5", "imm2": "0xfaaf9fca", "rot": 19, "bit": 12, "mask": 8, "width": 1},
{"i": 27, "op": "or", "dst": 5, "src": 6, "src2": 4, "imm": "0xb8dc6cbb", "imm2": "0xcac9d5a4", "rot": 8, "bit": 2, "mask": 8, "width": 1},
{"i": 28, "op": "load", "dst": 4, "src": 2, "src2": 1, "imm": "0x98a93309", "imm2": "0x235dc454", "rot": 31, "bit": 16, "mask": 4, "width": 4},
{"i": 29, "op": "or", "dst": 6, "src": 0, "src2": 0, "imm": "0xc9dc1885", "imm2": "0xade1c24e", "rot": 9, "bit": 20, "mask": 16, "width": 1},
{"i": 30, "op": "add", "dst": 0, "src": 3, "src2": 4, "imm": "0x5faa0547", "imm2": "0xdca976ac", "rot": 2, "bit": 2, "mask": 8, "width": 1},
{"i": 31, "op": "mulhi", "dst": 6, "src": 0, "src2": 7, "imm": "0xec8a588f", "imm2": "0x7e280eb3", "rot": 12, "bit": 19, "mask": 2, "width": 1},
{"i": 32, "op": "rotr", "dst": 3, "src": 7, "src2": 2, "imm": "0x6deb6b1d", "imm2": "0x79c4c05a", "rot": 25, "bit": 22, "mask": 2, "width": 1},
{"i": 33, "op": "shfl", "dst": 0, "src": 4, "src2": 4, "imm": "0x3e652de8", "imm2": "0x3a967830", "rot": 31, "bit": 5, "mask": 4, "width": 1},
{"i": 34, "op": "rotl", "dst": 2, "src": 0, "src2": 7, "imm": "0xd524262b", "imm2": "0xb65f15f3", "rot": 17, "bit": 2, "mask": 4, "width": 1},
{"i": 35, "op": "mulhi", "dst": 7, "src": 6, "src2": 5, "imm": "0x9c455c39", "imm2": "0x7241afc1", "rot": 9, "bit": 20, "mask": 8, "width": 1},
{"i": 36, "op": "shfl", "dst": 3, "src": 2, "src2": 4, "imm": "0x78d91acc", "imm2": "0x4efaf581", "rot": 17, "bit": 6, "mask": 2, "width": 1},
{"i": 37, "op": "load", "dst": 6, "src": 0, "src2": 1, "imm": "0x45f26b60", "imm2": "0xad134516", "rot": 19, "bit": 4, "mask": 4, "width": 1},
{"i": 38, "op": "load", "dst": 0, "src": 6, "src2": 3, "imm": "0x1798a61a", "imm2": "0xc40c58c6", "rot": 21, "bit": 28, "mask": 4, "width": 16},
{"i": 39, "op": "xor", "dst": 1, "src": 0, "src2": 4, "imm": "0x862680d2", "imm2": "0x92df77ec", "rot": 12, "bit": 9, "mask": 1, "width": 1},
{"i": 40, "op": "shfl", "dst": 3, "src": 1, "src2": 4, "imm": "0x44316a4c", "imm2": "0x843d6213", "rot": 6, "bit": 24, "mask": 2, "width": 1},
{"i": 41, "op": "load", "dst": 1, "src": 5, "src2": 1, "imm": "0xeaeb2a04", "imm2": "0xc4cba010", "rot": 7, "bit": 23, "mask": 16, "width": 1},
{"i": 42, "op": "mul", "dst": 3, "src": 2, "src2": 5, "imm": "0xe108d4b1", "imm2": "0xf65c52e5", "rot": 3, "bit": 30, "mask": 16, "width": 1},
{"i": 43, "op": "load", "dst": 2, "src": 7, "src2": 5, "imm": "0x940d6ee3", "imm2": "0x9d6e3a8f", "rot": 27, "bit": 28, "mask": 1, "width": 1},
{"i": 44, "op": "add", "dst": 7, "src": 3, "src2": 0, "imm": "0xe28c457c", "imm2": "0x610bd3c2", "rot": 9, "bit": 7, "mask": 4, "width": 1},
{"i": 45, "op": "mulhi", "dst": 7, "src": 6, "src2": 3, "imm": "0xec2679c5", "imm2": "0x522fb2f8", "rot": 4, "bit": 17, "mask": 16, "width": 1},
{"i": 46, "op": "xor", "dst": 3, "src": 4, "src2": 5, "imm": "0xce5dce0b", "imm2": "0x5447f7da", "rot": 5, "bit": 28, "mask": 2, "width": 1},
{"i": 47, "op": "load", "dst": 3, "src": 7, "src2": 4, "imm": "0x1be86e58", "imm2": "0xe9e1afb9", "rot": 4, "bit": 17, "mask": 2, "width": 1},
{"i": 48, "op": "mulhi", "dst": 7, "src": 2, "src2": 1, "imm": "0x26aea9a4", "imm2": "0xa5fa94ce", "rot": 22, "bit": 22, "mask": 16, "width": 1},
{"i": 49, "op": "mulhi", "dst": 6, "src": 3, "src2": 2, "imm": "0xc25a3528", "imm2": "0x7a974c09", "rot": 31, "bit": 31, "mask": 8, "width": 1},
{"i": 50, "op": "load", "dst": 2, "src": 1, "src2": 0, "imm": "0x3abaf4ef", "imm2": "0x38d75b93", "rot": 26, "bit": 22, "mask": 16, "width": 16},
{"i": 51, "op": "load", "dst": 6, "src": 3, "src2": 0, "imm": "0x8b41ee85", "imm2": "0x9171a667", "rot": 19, "bit": 18, "mask": 2, "width": 16},
{"i": 52, "op": "xor", "dst": 5, "src": 3, "src2": 7, "imm": "0x9c7de4ed", "imm2": "0x72e1df1d", "rot": 18, "bit": 5, "mask": 2, "width": 1},
{"i": 53, "op": "add", "dst": 5, "src": 7, "src2": 6, "imm": "0xd6457f6e", "imm2": "0xcca8fa7c", "rot": 11, "bit": 31, "mask": 4, "width": 1},
{"i": 54, "op": "rotr", "dst": 7, "src": 1, "src2": 2, "imm": "0x5ad54c6e", "imm2": "0x0bf13a74", "rot": 2, "bit": 19, "mask": 4, "width": 1},
{"i": 55, "op": "add", "dst": 6, "src": 7, "src2": 5, "imm": "0xf3e1301c", "imm2": "0x826cb755", "rot": 18, "bit": 12, "mask": 1, "width": 1},
{"i": 56, "op": "load", "dst": 3, "src": 0, "src2": 6, "imm": "0x68c04498", "imm2": "0x09d73293", "rot": 31, "bit": 9, "mask": 16, "width": 16},
{"i": 57, "op": "rotr", "dst": 1, "src": 5, "src2": 0, "imm": "0x6ae5f1c4", "imm2": "0x4acb3d16", "rot": 2, "bit": 25, "mask": 2, "width": 1},
{"i": 58, "op": "load", "dst": 2, "src": 6, "src2": 3, "imm": "0x908dad61", "imm2": "0x3caa6b5c", "rot": 26, "bit": 23, "mask": 8, "width": 4},
{"i": 59, "op": "sub", "dst": 3, "src": 7, "src2": 4, "imm": "0xf497693d", "imm2": "0x12a59627", "rot": 6, "bit": 15, "mask": 16, "width": 1},
{"i": 60, "op": "mad", "dst": 1, "src": 4, "src2": 6, "imm": "0x87197b1e", "imm2": "0x9fe5c5a6", "rot": 5, "bit": 18, "mask": 1, "width": 1},
{"i": 61, "op": "mad", "dst": 2, "src": 1, "src2": 2, "imm": "0x7ffc05da", "imm2": "0xdd4b88aa", "rot": 15, "bit": 8, "mask": 2, "width": 1},
{"i": 62, "op": "xor", "dst": 4, "src": 3, "src2": 0, "imm": "0x660f3bb9", "imm2": "0x48d985a9", "rot": 2, "bit": 27, "mask": 1, "width": 1},
{"i": 63, "op": "load", "dst": 2, "src": 4, "src2": 4, "imm": "0x45650a4c", "imm2": "0x4d838ea4", "rot": 15, "bit": 13, "mask": 1, "width": 1}
]
}

View file

@ -0,0 +1,109 @@
#include <metal_stdlib>
using namespace metal;
#define MASK 0x0fffffffu
constant uint SEEDW[8] = { 0x2a53aad4u, 0x37fcb6e2u, 0x25d66a27u, 0xf5249e5eu, 0x15f86a59u, 0x8144559au, 0xcad741a6u, 0xcb961373u };
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;
}
kernel void igneum_hash(device const uint* dataset [[buffer(0)]],
device ulong* out [[buffer(1)]],
constant uint& baseNonce [[buffer(2)]],
uint gid [[thread_position_in_grid]]) {
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;
r0 = rotl_imm(r0, 10u); // 0
r5 = r3 * r1 + r5; // 1
{ uint b_ = (r0 & MASK) & ~15u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint x_ = r5 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r5 = x_; } // 2
{ uint b_ = (r5 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r6 = x_; } // 3
r5 = r2 * r2 + r5; // 4
r4 = r4 + r3 + select(0xbd587d10u, 0x4edf355au, ((sel >> 10u) & 1u) != 0u); // 5
r3 = r3 ^ r2; // 6
r5 = r5 ^ r3; // 7
r7 = r6 * r2 + r7; // 8
r7 = r6 * r3 + r7; // 9
{ uint b_ = (r5 & MASK) & ~15u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r3 = x_; } // 10
r1 = mulhi(r1, r4); // 11
r1 = r1 ^ r6; // 12
r2 = mulhi(r2, r1); // 13
r6 = r6 ^ simd_shuffle_xor(r7, (ushort)4); // 14
r4 = r6 * r4 + r4; // 15
r1 = r1 ^ simd_shuffle_xor(r0, (ushort)8); // 16
r5 = r5 ^ r4; // 17
r0 = r0 * r2; // 18
r6 = r6 ^ dataset[r3 & MASK]; // 19
r6 = mulhi(r6, r2); // 20
r7 = r2 * r6 + r7; // 21
r4 = r4 * r5; // 22
r3 = r3 * r5; // 23
r6 = r6 * r4; // 24
r3 = r3 ^ dataset[r7 & MASK]; // 25
r2 = rotr_var(r2, r5); // 26
r5 = r5 | r6; // 27
{ uint b_ = (r2 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r4 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r4 = x_; } // 28
r6 = r6 | r0; // 29
r0 = r0 + r3 + select(0x5faa0547u, 0xdca976acu, ((sel >> 2u) & 1u) != 0u); // 30
r6 = mulhi(r6, r0); // 31
r3 = rotr_var(r3, r7); // 32
r0 = r0 ^ simd_shuffle_xor(r4, (ushort)4); // 33
r2 = rotl_imm(r2, 17u); // 34
r7 = mulhi(r7, r6); // 35
r3 = r3 ^ simd_shuffle_xor(r2, (ushort)2); // 36
r6 = r6 ^ dataset[r0 & MASK]; // 37
{ uint b_ = (r6 & MASK) & ~15u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r0 = x_; } // 38
r1 = r1 ^ r0; // 39
r3 = r3 ^ simd_shuffle_xor(r1, (ushort)2); // 40
r1 = r1 ^ dataset[r5 & MASK]; // 41
r3 = r3 * r2; // 42
r2 = r2 ^ dataset[r7 & MASK]; // 43
r7 = r7 + r3 + select(0xe28c457cu, 0x610bd3c2u, ((sel >> 7u) & 1u) != 0u); // 44
r7 = mulhi(r7, r6); // 45
r3 = r3 ^ r4; // 46
r3 = r3 ^ dataset[r7 & MASK]; // 47
r7 = mulhi(r7, r2); // 48
r6 = mulhi(r6, r3); // 49
{ uint b_ = (r1 & MASK) & ~15u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r2 = x_; } // 50
{ uint b_ = (r3 & MASK) & ~15u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r6 = x_; } // 51
r5 = r5 ^ r3; // 52
r5 = r5 + r7 + select(0xd6457f6eu, 0xcca8fa7cu, ((sel >> 31u) & 1u) != 0u); // 53
r7 = rotr_var(r7, r1); // 54
r6 = r6 + r7 + select(0xf3e1301cu, 0x826cb755u, ((sel >> 12u) & 1u) != 0u); // 55
{ uint b_ = (r0 & MASK) & ~15u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r3 = x_; } // 56
r1 = rotr_var(r1, r5); // 57
{ uint b_ = (r6 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r2 = x_; } // 58
r3 = r3 - r7; // 59
r1 = r4 * r6 + r1; // 60
r2 = r1 * r2 + r2; // 61
r4 = r4 ^ r3; // 62
r2 = r2 ^ dataset[r4 & MASK]; // 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;
}

View file

@ -0,0 +1,111 @@
#include <metal_stdlib>
using namespace metal;
#define MASK 0x0fffffffu
constant uint SEEDW[8] = { 0x2a53aad4u, 0x37fcb6e2u, 0x25d66a27u, 0xf5249e5eu, 0x15f86a59u, 0x8144559au, 0xcad741a6u, 0xcb961373u };
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;
}
// Header-bound variant: the init words come from buffer 3 (bind.rs), not from SEEDW.
kernel void igneum_hash_bound(device const uint* dataset [[buffer(0)]],
device ulong* out [[buffer(1)]],
constant uint& baseNonce [[buffer(2)]],
constant uint* initw [[buffer(3)]],
uint gid [[thread_position_in_grid]]) {
uint nonce = baseNonce + gid;
uint r0, r1, r2, r3, r4, r5, r6, r7;
{ uint x = nonce ^ initw[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ initw[1]; }
{ uint x = nonce ^ initw[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ initw[2]; }
{ uint x = nonce ^ initw[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ initw[3]; }
{ uint x = nonce ^ initw[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ initw[4]; }
{ uint x = nonce ^ initw[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ initw[5]; }
{ uint x = nonce ^ initw[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ initw[6]; }
{ uint x = nonce ^ initw[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ initw[7]; }
{ uint x = nonce ^ initw[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ initw[0]; }
for (uint it = 0u; it < 8u; ++it) {
uint sel = r0;
r0 = rotl_imm(r0, 10u); // 0
r5 = r3 * r1 + r5; // 1
{ uint b_ = (r0 & MASK) & ~15u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint x_ = r5 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r5 = x_; } // 2
{ uint b_ = (r5 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r6 = x_; } // 3
r5 = r2 * r2 + r5; // 4
r4 = r4 + r3 + select(0xbd587d10u, 0x4edf355au, ((sel >> 10u) & 1u) != 0u); // 5
r3 = r3 ^ r2; // 6
r5 = r5 ^ r3; // 7
r7 = r6 * r2 + r7; // 8
r7 = r6 * r3 + r7; // 9
{ uint b_ = (r5 & MASK) & ~15u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r3 = x_; } // 10
r1 = mulhi(r1, r4); // 11
r1 = r1 ^ r6; // 12
r2 = mulhi(r2, r1); // 13
r6 = r6 ^ simd_shuffle_xor(r7, (ushort)4); // 14
r4 = r6 * r4 + r4; // 15
r1 = r1 ^ simd_shuffle_xor(r0, (ushort)8); // 16
r5 = r5 ^ r4; // 17
r0 = r0 * r2; // 18
r6 = r6 ^ dataset[r3 & MASK]; // 19
r6 = mulhi(r6, r2); // 20
r7 = r2 * r6 + r7; // 21
r4 = r4 * r5; // 22
r3 = r3 * r5; // 23
r6 = r6 * r4; // 24
r3 = r3 ^ dataset[r7 & MASK]; // 25
r2 = rotr_var(r2, r5); // 26
r5 = r5 | r6; // 27
{ uint b_ = (r2 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r4 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r4 = x_; } // 28
r6 = r6 | r0; // 29
r0 = r0 + r3 + select(0x5faa0547u, 0xdca976acu, ((sel >> 2u) & 1u) != 0u); // 30
r6 = mulhi(r6, r0); // 31
r3 = rotr_var(r3, r7); // 32
r0 = r0 ^ simd_shuffle_xor(r4, (ushort)4); // 33
r2 = rotl_imm(r2, 17u); // 34
r7 = mulhi(r7, r6); // 35
r3 = r3 ^ simd_shuffle_xor(r2, (ushort)2); // 36
r6 = r6 ^ dataset[r0 & MASK]; // 37
{ uint b_ = (r6 & MASK) & ~15u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r0 = x_; } // 38
r1 = r1 ^ r0; // 39
r3 = r3 ^ simd_shuffle_xor(r1, (ushort)2); // 40
r1 = r1 ^ dataset[r5 & MASK]; // 41
r3 = r3 * r2; // 42
r2 = r2 ^ dataset[r7 & MASK]; // 43
r7 = r7 + r3 + select(0xe28c457cu, 0x610bd3c2u, ((sel >> 7u) & 1u) != 0u); // 44
r7 = mulhi(r7, r6); // 45
r3 = r3 ^ r4; // 46
r3 = r3 ^ dataset[r7 & MASK]; // 47
r7 = mulhi(r7, r2); // 48
r6 = mulhi(r6, r3); // 49
{ uint b_ = (r1 & MASK) & ~15u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r2 = x_; } // 50
{ uint b_ = (r3 & MASK) & ~15u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r6 = x_; } // 51
r5 = r5 ^ r3; // 52
r5 = r5 + r7 + select(0xd6457f6eu, 0xcca8fa7cu, ((sel >> 31u) & 1u) != 0u); // 53
r7 = rotr_var(r7, r1); // 54
r6 = r6 + r7 + select(0xf3e1301cu, 0x826cb755u, ((sel >> 12u) & 1u) != 0u); // 55
{ uint b_ = (r0 & MASK) & ~15u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r3 = x_; } // 56
r1 = rotr_var(r1, r5); // 57
{ uint b_ = (r6 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r2 = x_; } // 58
r3 = r3 - r7; // 59
r1 = r4 * r6 + r1; // 60
r2 = r1 * r2 + r2; // 61
r4 = r4 ^ r3; // 62
r2 = r2 ^ dataset[r4 & MASK]; // 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;
}

View file

@ -0,0 +1,57 @@
// Generated by igneum-pow export (generator v2) for seed "igneum-readwidth/A/6". Do not edit by hand.
// Expected outputs: igneum-pow (Rust) CPU interpreter, generator v2, memory-hard dataset
#pragma once
#ifdef __cplusplus
#include <cstdint>
#else
#include <stdint.h>
#endif
#define IGNEUM_VEC_WARPS 3
static const uint32_t IGNEUM_VEC_BASE[IGNEUM_VEC_WARPS] = { 0u, 4096u, 1000000u };
static const uint64_t IGNEUM_VEC_OUT[IGNEUM_VEC_WARPS][32] = {
{ // base nonce 0
0x93e8388cac40c811ull, 0xa7fc8484877d2d42ull, 0x0e4fade6c54c3828ull, 0xd51024c9edc222c6ull, 0xf69126345bc6a789ull, 0xe4bf38ace928f612ull, 0xa3915889cbe42924ull, 0x338fb75c6a087e3dull,
0xf27481a4ff979f3bull, 0xefe50d7bc95581abull, 0x7d2fa9d720b02ecaull, 0xfca2c211f788acf2ull, 0x2e66cc8095f3cb16ull, 0xae75758d3dc34127ull, 0x9553b782a362e16eull, 0xcb22382b49172a28ull,
0x5fa9a18442efb5d8ull, 0x8bdd7aceb04694f1ull, 0xd7b19cf5b3dd0d2eull, 0x2f704fb0b2342d83ull, 0x8c3747e33ccefd96ull, 0x67c00a117dfc2e3cull, 0x3d548ce336a6c72full, 0xfca49cfec0f68f8full,
0x1c253507e5d1a81cull, 0xa17ebb784aa17028ull, 0x511b1f8191bbf57full, 0xabfd11f3a4eac2c8ull, 0x729a42cfe7386d86ull, 0xfa1d04c46ce4f2c2ull, 0xf108ab5b9481c6b0ull, 0xe9fe2f9e682f40d8ull
},
{ // base nonce 4096
0xc5d7b8097741686bull, 0x7fae5d95c0939293ull, 0x9b115b4967fd0af2ull, 0xf347d09f0b570b6bull, 0xb611632e5e2cc724ull, 0x92a6046c22ce9826ull, 0x6a40216015aeabe7ull, 0x8c5b7bbf98ee8ad1ull,
0x160da4c87f1ff350ull, 0xf898efb8f2ecbf70ull, 0x84ec67d175b97164ull, 0x5813809116708d53ull, 0xe5318deef160edcfull, 0x1d3a703990fe850eull, 0xf6a0b78ee2dc77adull, 0xfddb5e0da4c705d5ull,
0x98af1bd6cbba216full, 0xd339b38e62f2f4a5ull, 0xa6ac444d1edabc78ull, 0xe37432779a991a79ull, 0x2e10f0ac4e7a00d6ull, 0xf948234a7b8a4b92ull, 0x44c7abbb3897dd06ull, 0x14c7287af3dcb661ull,
0x9b6b3d1c5b2a53f8ull, 0x36f4b115baec85acull, 0x41192bfbaba0592full, 0x0a6a7e78a97065d0ull, 0x54a2f48437f43f94ull, 0x5c7b3a5411458527ull, 0x359ac92b685d331dull, 0x2746c6f53c998ac1ull
},
{ // base nonce 1000000
0x299e126aefa2a456ull, 0xd7cb35bb207a4744ull, 0x28755e512456e322ull, 0x7e12f9a852ed67b7ull, 0x54ff55e7b8fdf7f7ull, 0xb36a09f8c0fefd19ull, 0xb466f445db516c51ull, 0xd129d222de67ce0dull,
0xc8a4d0e8591982a8ull, 0xb7566ad957bb1e1dull, 0xa56457ebd28d83efull, 0xd981708266e400c0ull, 0x73c629bbcedab2d9ull, 0x0d5fb3aa79870efbull, 0xf225b95724a24e46ull, 0xdf8d23b7472004bfull,
0x4ca04ddf25cd4aceull, 0x80864b3b0740f888ull, 0xc114ed0d7042660aull, 0x8a3c7c3bcb5575c3ull, 0xa46c25ba69798388ull, 0x33a8aeab66d63875ull, 0x5bea09d563955e97ull, 0x81e81e3c8ea6d163ull,
0x09f4dca8200b8220ull, 0x0227171d66f5197dull, 0x9d5ae8c2fe24c1abull, 0x572b88825c78884dull, 0x33be787f0b6d52fcull, 0x88689ec5df299a7full, 0xfad078a36872646eull, 0x8d7439bc62c19870ull
}
};
// Dataset self-test: dataset[0..15] and dataset[IGNEUM_MASK] (268435455).
static const uint32_t IGNEUM_DS_HEAD[16] = {
0xffc3cd94u, 0x5920ccd8u, 0x392f44bbu, 0x5e57f67au, 0x2f2bc2a9u, 0x620b0e36u, 0xbdc09014u, 0x436654bfu,
0x311e0b48u, 0x1abd93adu, 0x59cc7ce8u, 0xee5247b2u, 0x86171fe8u, 0x6d874751u, 0xc9f7728fu, 0x7c2a435du
};
static const uint32_t IGNEUM_DS_LAST_INDEX = 268435455u;
static const uint32_t IGNEUM_DS_LAST = 0xa33ada72u;
// 64 sampled dataset words (index, value) computed on the Mac.
#define IGNEUM_DS_SAMPLES 64
static const uint32_t IGNEUM_DS_SAMPLE_INDEX[IGNEUM_DS_SAMPLES] = {
59471966u, 217795994u, 208353206u, 42483309u, 172547758u, 148076330u, 183853158u, 214389424u, 267488061u, 169781097u, 184093494u, 153880993u, 84977930u, 46426879u, 3093825u, 225364072u, 44593546u, 260713159u, 168250303u, 52384140u, 223401610u, 45554030u, 95410555u, 175039924u, 79171087u, 267580473u, 24168642u, 37981670u, 171551130u, 195559979u, 204611762u, 140997658u, 138925853u, 86637313u, 20736778u, 219665210u, 160430336u, 264654675u, 8013395u, 228945585u, 213884386u, 104419827u, 44185464u, 142737231u, 99284897u, 132475900u, 61861762u, 132056166u, 262388043u, 91878046u, 117353561u, 124768597u, 71352993u, 190698941u, 46055428u, 55281366u, 165145231u, 106810753u, 171985651u, 232085256u, 159510492u, 40072060u, 209107596u, 39023794u
};
static const uint32_t IGNEUM_DS_SAMPLE_VALUE[IGNEUM_DS_SAMPLES] = {
0xe8b73d94u, 0x337028b5u, 0xafe148c9u, 0xab99f7aeu, 0x434ea619u, 0xd85cb880u, 0x54764c7fu, 0x82c7e420u, 0xedf4cb9eu, 0x9884c959u, 0x223ee793u, 0x3a9ccf69u, 0x81da4fd2u, 0xd6ce8cb9u, 0xe3922dcau, 0x3e7e6bdeu, 0x382a3acau, 0x567e7f7fu, 0x25a0f084u, 0xbfeef128u, 0xe338abfbu, 0x7c3b5280u, 0x909bc5f1u, 0xd8b74b9cu, 0x8e31a22eu, 0x26b5f1d8u, 0x79122c00u, 0xcafc3340u, 0xd5e02ea3u, 0x1aee1afdu, 0xdb090d9au, 0xb049f435u, 0x4954d8bau, 0x03797ba0u, 0x196eefbdu, 0xd153412au, 0xbe5d2c4bu, 0xdaa14f0eu, 0x8e61ed07u, 0x9e9a64c6u, 0x2e29ff36u, 0x392a8589u, 0xb56a5912u, 0xfa6e8b57u, 0xd1a737cbu, 0xb0fa841au, 0xbe1c341fu, 0xe25be0f1u, 0xe937f543u, 0xebab2248u, 0x8e1b607au, 0x202a2fedu, 0x95e2819cu, 0x9c9652d4u, 0x32fedef0u, 0xdecfff82u, 0xcb5d43e5u, 0xb735806au, 0x8905939cu, 0xfbf8472du, 0xada74e5du, 0x7ebdeeeau, 0x0119f2b3u, 0xa9a376b8u
};
// Cache self-test (memory-hard mode): cache[0..15], the last 16 words, and FNV-1a 64 over all 2^26 words.
static const uint32_t IGNEUM_CACHE_HEAD[16] = {
0x355a86d2u, 0x7957db1cu, 0xd21772afu, 0x6fc1e09bu, 0xd55ce61du, 0x6e6a278bu, 0xd3f543ceu, 0x223d8e82u,
0x143ab337u, 0x2e9f05bdu, 0x2eb389bfu, 0x0c6e449eu, 0x5cfa4222u, 0xba6560feu, 0x8e3e1aa4u, 0xdbcc1d53u
};
static const uint32_t IGNEUM_CACHE_LAST[16] = {
0x41190d91u, 0xbd277957u, 0x22ddbb49u, 0x6986f207u, 0xdf69a4d6u, 0x26401a3au, 0x818230fbu, 0xc417122du,
0x3597b211u, 0xb553ce55u, 0xcf39cc0du, 0x3b7fc43au, 0x3fd43b00u, 0x67e1c80eu, 0xffa7ea7du, 0xca2960abu
};
static const uint64_t IGNEUM_CACHE_FNV64 = 0x48c4f5bf24166b2eull;

View file

@ -0,0 +1,36 @@
{
"seed": "igneum-readwidth/A/6",
"day": "2026-10-03",
"dataset_mode": "memory-hard",
"dataset_log2_words": 28,
"mask": "0x0fffffff",
"lanes": 32,
"source": "igneum-pow (Rust) CPU interpreter, generator v2, memory-hard dataset",
"warps": [
{"base_nonce": 0, "expected": [
"0x93e8388cac40c811", "0xa7fc8484877d2d42", "0x0e4fade6c54c3828", "0xd51024c9edc222c6", "0xf69126345bc6a789", "0xe4bf38ace928f612", "0xa3915889cbe42924", "0x338fb75c6a087e3d",
"0xf27481a4ff979f3b", "0xefe50d7bc95581ab", "0x7d2fa9d720b02eca", "0xfca2c211f788acf2", "0x2e66cc8095f3cb16", "0xae75758d3dc34127", "0x9553b782a362e16e", "0xcb22382b49172a28",
"0x5fa9a18442efb5d8", "0x8bdd7aceb04694f1", "0xd7b19cf5b3dd0d2e", "0x2f704fb0b2342d83", "0x8c3747e33ccefd96", "0x67c00a117dfc2e3c", "0x3d548ce336a6c72f", "0xfca49cfec0f68f8f",
"0x1c253507e5d1a81c", "0xa17ebb784aa17028", "0x511b1f8191bbf57f", "0xabfd11f3a4eac2c8", "0x729a42cfe7386d86", "0xfa1d04c46ce4f2c2", "0xf108ab5b9481c6b0", "0xe9fe2f9e682f40d8"
]},
{"base_nonce": 4096, "expected": [
"0xc5d7b8097741686b", "0x7fae5d95c0939293", "0x9b115b4967fd0af2", "0xf347d09f0b570b6b", "0xb611632e5e2cc724", "0x92a6046c22ce9826", "0x6a40216015aeabe7", "0x8c5b7bbf98ee8ad1",
"0x160da4c87f1ff350", "0xf898efb8f2ecbf70", "0x84ec67d175b97164", "0x5813809116708d53", "0xe5318deef160edcf", "0x1d3a703990fe850e", "0xf6a0b78ee2dc77ad", "0xfddb5e0da4c705d5",
"0x98af1bd6cbba216f", "0xd339b38e62f2f4a5", "0xa6ac444d1edabc78", "0xe37432779a991a79", "0x2e10f0ac4e7a00d6", "0xf948234a7b8a4b92", "0x44c7abbb3897dd06", "0x14c7287af3dcb661",
"0x9b6b3d1c5b2a53f8", "0x36f4b115baec85ac", "0x41192bfbaba0592f", "0x0a6a7e78a97065d0", "0x54a2f48437f43f94", "0x5c7b3a5411458527", "0x359ac92b685d331d", "0x2746c6f53c998ac1"
]},
{"base_nonce": 1000000, "expected": [
"0x299e126aefa2a456", "0xd7cb35bb207a4744", "0x28755e512456e322", "0x7e12f9a852ed67b7", "0x54ff55e7b8fdf7f7", "0xb36a09f8c0fefd19", "0xb466f445db516c51", "0xd129d222de67ce0d",
"0xc8a4d0e8591982a8", "0xb7566ad957bb1e1d", "0xa56457ebd28d83ef", "0xd981708266e400c0", "0x73c629bbcedab2d9", "0x0d5fb3aa79870efb", "0xf225b95724a24e46", "0xdf8d23b7472004bf",
"0x4ca04ddf25cd4ace", "0x80864b3b0740f888", "0xc114ed0d7042660a", "0x8a3c7c3bcb5575c3", "0xa46c25ba69798388", "0x33a8aeab66d63875", "0x5bea09d563955e97", "0x81e81e3c8ea6d163",
"0x09f4dca8200b8220", "0x0227171d66f5197d", "0x9d5ae8c2fe24c1ab", "0x572b88825c78884d", "0x33be787f0b6d52fc", "0x88689ec5df299a7f", "0xfad078a36872646e", "0x8d7439bc62c19870"
]}
],
"dataset_head": ["0xffc3cd94", "0x5920ccd8", "0x392f44bb", "0x5e57f67a", "0x2f2bc2a9", "0x620b0e36", "0xbdc09014", "0x436654bf", "0x311e0b48", "0x1abd93ad", "0x59cc7ce8", "0xee5247b2", "0x86171fe8", "0x6d874751", "0xc9f7728f", "0x7c2a435d"],
"dataset_last_index": 268435455,
"dataset_last": "0xa33ada72",
"dataset_samples": [{"index": 59471966, "value": "0xe8b73d94"}, {"index": 217795994, "value": "0x337028b5"}, {"index": 208353206, "value": "0xafe148c9"}, {"index": 42483309, "value": "0xab99f7ae"}, {"index": 172547758, "value": "0x434ea619"}, {"index": 148076330, "value": "0xd85cb880"}, {"index": 183853158, "value": "0x54764c7f"}, {"index": 214389424, "value": "0x82c7e420"}, {"index": 267488061, "value": "0xedf4cb9e"}, {"index": 169781097, "value": "0x9884c959"}, {"index": 184093494, "value": "0x223ee793"}, {"index": 153880993, "value": "0x3a9ccf69"}, {"index": 84977930, "value": "0x81da4fd2"}, {"index": 46426879, "value": "0xd6ce8cb9"}, {"index": 3093825, "value": "0xe3922dca"}, {"index": 225364072, "value": "0x3e7e6bde"}, {"index": 44593546, "value": "0x382a3aca"}, {"index": 260713159, "value": "0x567e7f7f"}, {"index": 168250303, "value": "0x25a0f084"}, {"index": 52384140, "value": "0xbfeef128"}, {"index": 223401610, "value": "0xe338abfb"}, {"index": 45554030, "value": "0x7c3b5280"}, {"index": 95410555, "value": "0x909bc5f1"}, {"index": 175039924, "value": "0xd8b74b9c"}, {"index": 79171087, "value": "0x8e31a22e"}, {"index": 267580473, "value": "0x26b5f1d8"}, {"index": 24168642, "value": "0x79122c00"}, {"index": 37981670, "value": "0xcafc3340"}, {"index": 171551130, "value": "0xd5e02ea3"}, {"index": 195559979, "value": "0x1aee1afd"}, {"index": 204611762, "value": "0xdb090d9a"}, {"index": 140997658, "value": "0xb049f435"}, {"index": 138925853, "value": "0x4954d8ba"}, {"index": 86637313, "value": "0x03797ba0"}, {"index": 20736778, "value": "0x196eefbd"}, {"index": 219665210, "value": "0xd153412a"}, {"index": 160430336, "value": "0xbe5d2c4b"}, {"index": 264654675, "value": "0xdaa14f0e"}, {"index": 8013395, "value": "0x8e61ed07"}, {"index": 228945585, "value": "0x9e9a64c6"}, {"index": 213884386, "value": "0x2e29ff36"}, {"index": 104419827, "value": "0x392a8589"}, {"index": 44185464, "value": "0xb56a5912"}, {"index": 142737231, "value": "0xfa6e8b57"}, {"index": 99284897, "value": "0xd1a737cb"}, {"index": 132475900, "value": "0xb0fa841a"}, {"index": 61861762, "value": "0xbe1c341f"}, {"index": 132056166, "value": "0xe25be0f1"}, {"index": 262388043, "value": "0xe937f543"}, {"index": 91878046, "value": "0xebab2248"}, {"index": 117353561, "value": "0x8e1b607a"}, {"index": 124768597, "value": "0x202a2fed"}, {"index": 71352993, "value": "0x95e2819c"}, {"index": 190698941, "value": "0x9c9652d4"}, {"index": 46055428, "value": "0x32fedef0"}, {"index": 55281366, "value": "0xdecfff82"}, {"index": 165145231, "value": "0xcb5d43e5"}, {"index": 106810753, "value": "0xb735806a"}, {"index": 171985651, "value": "0x8905939c"}, {"index": 232085256, "value": "0xfbf8472d"}, {"index": 159510492, "value": "0xada74e5d"}, {"index": 40072060, "value": "0x7ebdeeea"}, {"index": 209107596, "value": "0x0119f2b3"}, {"index": 39023794, "value": "0xa9a376b8"}],
"cache_head": ["0x355a86d2", "0x7957db1c", "0xd21772af", "0x6fc1e09b", "0xd55ce61d", "0x6e6a278b", "0xd3f543ce", "0x223d8e82", "0x143ab337", "0x2e9f05bd", "0x2eb389bf", "0x0c6e449e", "0x5cfa4222", "0xba6560fe", "0x8e3e1aa4", "0xdbcc1d53"],
"cache_last_line": ["0x41190d91", "0xbd277957", "0x22ddbb49", "0x6986f207", "0xdf69a4d6", "0x26401a3a", "0x818230fb", "0xc417122d", "0x3597b211", "0xb553ce55", "0xcf39cc0d", "0x3b7fc43a", "0x3fd43b00", "0x67e1c80e", "0xffa7ea7d", "0xca2960ab"],
"cache_fnv1a64": "0x48c4f5bf24166b2e"
}

View file

@ -0,0 +1,278 @@
// Generated by igneum-pow export (generator v2) for seed "igneum-readwidth/B/0". Do not edit by hand.
// OpenCL C twin of the Metal kernel for the same seed (see proto-opencl/README.md, WAVEFRONT.md and program.metal).
// Built from source at runtime by proto-opencl/host.c, which passes these defines:
// IGNEUM_GROUP work-group size of igneum_hash, a multiple of 32 (default 32: one work-group = one 32-lane unit)
// IGNEUM_EXCHANGE 0 = local-memory exchange with a barrier (any device, any wave width; the default)
// 1 = sub_group_shuffle_xor (cl_khr_subgroup_shuffle), only with IGNEUM_GROUP 32 and a sub-group size of exactly 32
// 2 = intel_sub_group_shuffle_xor (cl_intel_subgroups), same condition
// The verification unit is always 32 lanes. A 64-wide hardware wave (AMD GCN/CDNA, RDNA in wave64) runs two units;
// the exchange masks are 1, 2, 4, 8, 16, so every partner lane lies inside the lane's own aligned run of 32.
#ifndef IGNEUM_GROUP
#define IGNEUM_GROUP 32
#endif
#ifndef IGNEUM_EXCHANGE
#define IGNEUM_EXCHANGE 0
#endif
#ifdef __OPENCL_VERSION__
#define IGNEUM_KERNEL_HASH __kernel __attribute__((reqd_work_group_size(IGNEUM_GROUP, 1, 1)))
#define IGNEUM_LOCAL_WORDS(name, n) __local uint name[n]
#if IGNEUM_EXCHANGE == 1
#ifdef cl_khr_subgroups
#pragma OPENCL EXTENSION cl_khr_subgroups : enable
#endif
#ifdef cl_khr_subgroup_shuffle
#pragma OPENCL EXTENSION cl_khr_subgroup_shuffle : enable
#endif
#elif IGNEUM_EXCHANGE == 2
#pragma OPENCL EXTENSION cl_intel_subgroups : enable
#endif
#else
// Not an OpenCL compiler: proto-opencl/emu compiles this file as C++ and supplies the built-ins and these two macros.
#include "emu_opencl.h"
#endif
#if IGNEUM_EXCHANGE == 1
#define IGNEUM_SHFL_XOR(dst, a, m) dst = sub_group_shuffle_xor((a), (uint)(m))
#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u)
#elif IGNEUM_EXCHANGE == 2
#define IGNEUM_SHFL_XOR(dst, a, m) dst = intel_sub_group_shuffle_xor((a), (uint)(m))
#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u)
#else
// Local-memory exchange. Two buffers of IGNEUM_GROUP words alternate (xk counts exchanges), so one barrier per
// exchange is enough: a lane can only overwrite buffer b at exchange k+2 after passing barrier k+1, and every lane
// reaches barrier k+1 only after its read of buffer b at exchange k. The partner lid ^ m stays inside the lane's
// aligned run of 32 because m < 32. Control flow is uniform, so every work-item reaches every barrier.
#define IGNEUM_SHFL_XOR(dst, a, m) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid ^ (uint)(m))]; xk += 1u; }
#define IGNEUM_BCAST0(dst, a) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid & ~31u)]; xk += 1u; }
#endif
static inline uint splitmix32(uint 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. OpenCL rotate() rotates left by n modulo 32.
static inline uint rotl_imm(uint x, uint n) { return rotate(x, n); }
// Right rotation by n modulo 32 as a left rotation by (32 - n) modulo 32; n == 0 gives x.
static inline uint rotr_var(uint x, uint n) { return rotate(x, (0u - n) & 31u); }
static 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;
}
// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines.
// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals.
#define MH_CACHE_LINE_MASK 0x003fffffu
#define MH_SEGMENT_LINES 64u
#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); }
static inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site
// y = ChaCha12 core(x) + x
static inline void mh_chacha_block(const uint* x, uint* y) {
for (uint i = 0u; i < 16u; ++i) y[i] = x[i];
for (uint r = 0u; r < 6u; ++r) {
MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u)
MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u)
}
for (uint i = 0u; i < 16u; ++i) y[i] += x[i];
}
// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0.
static inline void mh_cache_segment(__global uint* cache, uint seg) {
uint prev[16]; uint x[16]; uint y[16];
for (uint i = 0u; i < 16u; ++i) prev[i] = 0u;
for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) {
x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3];
x[4] = 0x3067619fu ^ prev[4];
x[5] = 0x3c269176u ^ prev[5];
x[6] = 0x84a03b03u ^ prev[6];
x[7] = 0xf8c63294u ^ prev[7];
x[8] = 0xff977c5bu ^ prev[8];
x[9] = 0xe60def3eu ^ prev[9];
x[10] = 0x63630141u ^ prev[10];
x[11] = 0xb8fbcb58u ^ prev[11];
x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15];
mh_chacha_block(x, y);
__global uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u);
for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; }
}
}
// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations.
static inline void mh_mixer(uint* s, uint rk) {
s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u;
s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu;
s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u;
s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu;
s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u;
s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u;
s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u;
s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u;
s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du;
s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u;
s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du;
s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu;
s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du;
s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu;
s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u;
s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u;
MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u)
MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u)
MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u)
MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u)
}
// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer.
static inline void mh_item(__global const uint* cache, uint t, uint* s) {
s[0] = 0x3067619fu;
s[1] = 0x3c269176u;
s[2] = 0x84a03b03u;
s[3] = 0xf8c63294u;
s[4] = 0xff977c5bu;
s[5] = 0xe60def3eu;
s[6] = 0x63630141u;
s[7] = 0xb8fbcb58u;
s[8] = t * 0x42146205u + 0xbab68293u;
s[9] = t * 0x52cbe0fbu + 0xcc162340u;
s[10] = t * 0x7ecf4a03u + 0x6ce151ccu;
s[11] = t * 0x6728907fu + 0xe62b8997u;
s[12] = t * 0xd81d9751u + 0xc9c80297u;
s[13] = t * 0x132952c3u + 0xf74a1654u;
s[14] = t * 0xf60de277u + 0x3d704af5u;
s[15] = t * 0x05358035u + 0x3cf522b7u;
for (uint r = 0u; r < 8u; ++r) {
mh_mixer(s, 0x9E3779B9u * (r + 1u));
__global const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u);
for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i];
}
mh_mixer(s, 0x9E3779B9u * 9u);
}
// dataset[w] without the dataset: derive item w >> 4 and take word w & 15.
static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; }
// Memory-hard dataset (MEMHARD.md). One work-item per cache segment; one work-item per 64-byte dataset item.
// The same constants as memhard.h in this pack (one emitter, three dialects).
__kernel void igneum_cache_fill(__global uint* cache, uint nSegments) {
uint seg = (uint)get_global_id(0);
if (seg < nSegments) mh_cache_segment(cache, seg);
}
__kernel void igneum_build(__global uint* ds, __global const uint* cache, uint nItems) {
uint t = (uint)get_global_id(0);
if (t < nItems) {
uint s[16];
mh_item(cache, t, s);
__global uint* d = ds + ((ulong)t * 16u);
for (uint i = 0u; i < 16u; ++i) d[i] = s[i];
}
}
// One hash per work-item. IGNEUM_GROUP is a multiple of 32; lane = lid & 31 and every exchange stays inside the
// lane's own aligned run of 32 work-items, exactly like simd_shuffle_xor inside a 32-wide Metal SIMD group and
// __shfl_xor_sync inside a CUDA warp. Control flow is uniform (no branches at all).
IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask) {
uint gid = (uint)get_global_id(0);
uint lid = (uint)get_local_id(0);
uint nonce = baseNonce + gid;
uint r0, r1, r2, r3, r4, r5, r6, r7;
#if IGNEUM_EXCHANGE == 0
IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP);
uint xk = 0u;
#else
(void)lid;
#endif
{ uint x = nonce ^ 0x3673211cu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0xaae550b4u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1]
{ uint x = nonce ^ 0xaae550b4u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0x5a0ce2e0u; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2]
{ uint x = nonce ^ 0x5a0ce2e0u; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x1d2471cfu; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3]
{ uint x = nonce ^ 0x1d2471cfu; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xba944366u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4]
{ uint x = nonce ^ 0xba944366u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xbdd4d1dbu; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5]
{ uint x = nonce ^ 0xbdd4d1dbu; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0xcb1984a9u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6]
{ uint x = nonce ^ 0xcb1984a9u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0x081ce12au; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7]
{ uint x = nonce ^ 0x081ce12au; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x3673211cu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0]
for (uint it = 0u; it < 8u; ++it) {
uint sel = r0;
{ uint t_; IGNEUM_SHFL_XOR(t_, r2, 1u); r0 = r0 ^ t_; } // 0 shfl
r2 = r2 * r0; // 1 mul
r0 = r0 + r6 + ((((sel >> 20u) & 1u) != 0u) ? 0x03fa29f3u : 0x7fbf4ae6u); // 2 add
r2 = r2 * r7; // 3 mul
r6 = r6 ^ ds[r2 & mask]; // 4 load
{ uint b_ = (r0 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r7 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r7 = x_; } // 5 load
{ uint t_; IGNEUM_SHFL_XOR(t_, r4, 16u); r6 = r6 ^ t_; } // 6 shfl
r3 = r3 ^ r5; // 7 xor
r6 = r6 ^ ds[r7 & mask]; // 8 load
r0 = r0 + r7 + ((((sel >> 25u) & 1u) != 0u) ? 0x308c81bfu : 0xd59b21b0u); // 9 add
r5 = rotr_var(r5, r7); // 10 rotr
{ uint b_ = (r6 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r3 = x_; } // 11 load
r2 = r2 * r6; // 12 mul
{ uint b_ = (r0 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r7 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r7 = x_; } // 13 load
r2 = r5 * r2 + r2; // 14 mad
r4 = r4 + r0 + ((((sel >> 24u) & 1u) != 0u) ? 0x67081e7eu : 0x902dc661u); // 15 add
{ uint b_ = (r4 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r3 = x_; } // 16 load
r3 = mul_hi(r3, r4); // 17 mulhi
{ uint b_ = (r7 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r4 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r4 = x_; } // 18 load
r2 = r2 + r1 + ((((sel >> 9u) & 1u) != 0u) ? 0xd1e74db0u : 0x3ee3182cu); // 19 add
r7 = r7 ^ r6; // 20 xor
r5 = r5 ^ r3; // 21 xor
{ uint t_; IGNEUM_SHFL_XOR(t_, r2, 16u); r6 = r6 ^ t_; } // 22 shfl
{ uint b_ = (r2 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r0 = x_; } // 23 load
r2 = r2 + r5 + ((((sel >> 22u) & 1u) != 0u) ? 0x62ac9e52u : 0xd2d451c6u); // 24 add
{ uint b_ = (r7 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r3 = x_; } // 25 load
{ uint t_; IGNEUM_SHFL_XOR(t_, r1, 16u); r4 = r4 ^ t_; } // 26 shfl
{ uint b_ = (r4 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r1 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r1 = x_; } // 27 load
r1 = r1 + r5 + ((((sel >> 2u) & 1u) != 0u) ? 0x072cfabeu : 0x111ff813u); // 28 add
r4 = r1 * r2 + r4; // 29 mad
r2 = r2 * r0; // 30 mul
r0 = r0 ^ r5; // 31 xor
r1 = r1 ^ ds[r0 & mask]; // 32 load
r2 = r2 - r3; // 33 sub
{ uint t_; IGNEUM_SHFL_XOR(t_, r0, 2u); r2 = r2 ^ t_; } // 34 shfl
r0 = r0 - r6; // 35 sub
r4 = r4 * r7; // 36 mul
r5 = r5 ^ r6; // 37 xor
r0 = mul_hi(r0, r6); // 38 mulhi
{ uint t_; IGNEUM_SHFL_XOR(t_, r7, 4u); r5 = r5 ^ t_; } // 39 shfl
{ uint b_ = (r3 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r6 = x_; } // 40 load
r2 = r2 ^ ds[r4 & mask]; // 41 load
r5 = r5 ^ ds[r6 & mask]; // 42 load
r1 = rotr_var(r1, r4); // 43 rotr
r0 = r0 | r5; // 44 or
{ uint t_; IGNEUM_SHFL_XOR(t_, r2, 2u); r0 = r0 ^ t_; } // 45 shfl
r7 = r7 * r4; // 46 mul
r3 = r3 ^ r4; // 47 xor
r2 = mul_hi(r2, r6); // 48 mulhi
r5 = r5 ^ r4; // 49 xor
r1 = rotr_var(r1, r6); // 50 rotr
r7 = r7 + r0 + ((((sel >> 22u) & 1u) != 0u) ? 0x0fe79cecu : 0x68d3a5c0u); // 51 add
r5 = r5 + r4 + ((((sel >> 8u) & 1u) != 0u) ? 0x04309e6fu : 0xeb764d91u); // 52 add
r7 = r7 ^ r0; // 53 xor
r4 = r4 + r0 + ((((sel >> 6u) & 1u) != 0u) ? 0xba0b81c6u : 0x1390b188u); // 54 add
r0 = r0 + r5 + ((((sel >> 15u) & 1u) != 0u) ? 0x5ec96dd8u : 0x4c21a6cdu); // 55 add
r7 = r5 * r3 + r7; // 56 mad
{ uint b_ = (r0 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r6 = x_; } // 57 load
r5 = r5 + r1 + ((((sel >> 1u) & 1u) != 0u) ? 0x6c0ac4ddu : 0x68297b06u); // 58 add
r6 = rotr_var(r6, r7); // 59 rotr
{ uint b_ = (r4 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r7 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r7 = x_; } // 60 load
{ uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r6 = r6 ^ t_; } // 61 shfl
r4 = rotl_imm(r4, 6u); // 62 rotl
r1 = r2 * r1 + r1; // 63 mad
}
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;
}
#if IGNEUM_EXCHANGE != 0
// Reports the sub-group size this device uses for a work-group of IGNEUM_GROUP items. host.c runs it only when the
// per-kernel query (clGetKernelSubGroupInfoKHR on igneum_hash) is unavailable; that query is preferred because a
// compiler may pick a different wave width per kernel (RDNA: wave32 or wave64). See WAVEFRONT.md.
IGNEUM_KERNEL_HASH void igneum_probe_subgroup(__global uint* out) {
if (get_local_id(0) == 0u) { out[0] = get_sub_group_size(); out[1] = get_num_sub_groups(); }
}
#endif

View file

@ -0,0 +1,164 @@
// Generated by igneum-pow export (generator v2) for seed "igneum-readwidth/B/0". 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"
#include "memhard.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;
}
// Memory-hard dataset (MEMHARD.md). One thread per cache segment; one thread per 64-byte dataset item.
// The core functions (mh_cache_segment, mh_item) are in memhard.h and are also compiled for the host.
__global__ void igneum_cache_fill(uint32_t* cache, uint32_t nSegments) {
uint32_t seg = blockIdx.x * blockDim.x + threadIdx.x;
if (seg < nSegments) mh_cache_segment(cache, seg);
}
__global__ void igneum_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems) {
uint32_t t = blockIdx.x * blockDim.x + threadIdx.x;
if (t < nItems) {
uint32_t s[16];
mh_item(cache, t, s);
uint32_t* d = ds + (size_t)t * 16u;
for (uint32_t i = 0u; i < 16u; ++i) d[i] = s[i];
}
}
// 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 ^ 0x3673211cu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0xaae550b4u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1]
{ uint32_t x = nonce ^ 0xaae550b4u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0x5a0ce2e0u; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2]
{ uint32_t x = nonce ^ 0x5a0ce2e0u; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x1d2471cfu; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3]
{ uint32_t x = nonce ^ 0x1d2471cfu; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xba944366u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4]
{ uint32_t x = nonce ^ 0xba944366u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xbdd4d1dbu; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5]
{ uint32_t x = nonce ^ 0xbdd4d1dbu; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0xcb1984a9u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6]
{ uint32_t x = nonce ^ 0xcb1984a9u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0x081ce12au; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7]
{ uint32_t x = nonce ^ 0x081ce12au; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x3673211cu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0]
for (uint32_t it = 0u; it < 8u; ++it) {
uint32_t sel = r0;
r0 = r0 ^ __shfl_xor_sync(0xffffffffu, r2, 1); // 0 shfl
r2 = r2 * r0; // 1 mul
r0 = r0 + r6 + ((((sel >> 20u) & 1u) != 0u) ? 0x03fa29f3u : 0x7fbf4ae6u); // 2 add
r2 = r2 * r7; // 3 mul
r6 = r6 ^ ds[r2 & mask]; // 4 load
{ uint32_t b_ = (r0 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r7 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r7 = x_; } // 5 load
r6 = r6 ^ __shfl_xor_sync(0xffffffffu, r4, 16); // 6 shfl
r3 = r3 ^ r5; // 7 xor
r6 = r6 ^ ds[r7 & mask]; // 8 load
r0 = r0 + r7 + ((((sel >> 25u) & 1u) != 0u) ? 0x308c81bfu : 0xd59b21b0u); // 9 add
r5 = rotr_var(r5, r7); // 10 rotr
{ uint32_t b_ = (r6 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r3 = x_; } // 11 load
r2 = r2 * r6; // 12 mul
{ uint32_t b_ = (r0 & mask) & ~15u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint32_t x_ = r7 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r7 = x_; } // 13 load
r2 = r5 * r2 + r2; // 14 mad
r4 = r4 + r0 + ((((sel >> 24u) & 1u) != 0u) ? 0x67081e7eu : 0x902dc661u); // 15 add
{ uint32_t b_ = (r4 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r3 = x_; } // 16 load
r3 = __umulhi(r3, r4); // 17 mulhi
{ uint32_t b_ = (r7 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r4 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r4 = x_; } // 18 load
r2 = r2 + r1 + ((((sel >> 9u) & 1u) != 0u) ? 0xd1e74db0u : 0x3ee3182cu); // 19 add
r7 = r7 ^ r6; // 20 xor
r5 = r5 ^ r3; // 21 xor
r6 = r6 ^ __shfl_xor_sync(0xffffffffu, r2, 16); // 22 shfl
{ uint32_t b_ = (r2 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r0 = x_; } // 23 load
r2 = r2 + r5 + ((((sel >> 22u) & 1u) != 0u) ? 0x62ac9e52u : 0xd2d451c6u); // 24 add
{ uint32_t b_ = (r7 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r3 = x_; } // 25 load
r4 = r4 ^ __shfl_xor_sync(0xffffffffu, r1, 16); // 26 shfl
{ uint32_t b_ = (r4 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r1 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r1 = x_; } // 27 load
r1 = r1 + r5 + ((((sel >> 2u) & 1u) != 0u) ? 0x072cfabeu : 0x111ff813u); // 28 add
r4 = r1 * r2 + r4; // 29 mad
r2 = r2 * r0; // 30 mul
r0 = r0 ^ r5; // 31 xor
r1 = r1 ^ ds[r0 & mask]; // 32 load
r2 = r2 - r3; // 33 sub
r2 = r2 ^ __shfl_xor_sync(0xffffffffu, r0, 2); // 34 shfl
r0 = r0 - r6; // 35 sub
r4 = r4 * r7; // 36 mul
r5 = r5 ^ r6; // 37 xor
r0 = __umulhi(r0, r6); // 38 mulhi
r5 = r5 ^ __shfl_xor_sync(0xffffffffu, r7, 4); // 39 shfl
{ uint32_t b_ = (r3 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r6 = x_; } // 40 load
r2 = r2 ^ ds[r4 & mask]; // 41 load
r5 = r5 ^ ds[r6 & mask]; // 42 load
r1 = rotr_var(r1, r4); // 43 rotr
r0 = r0 | r5; // 44 or
r0 = r0 ^ __shfl_xor_sync(0xffffffffu, r2, 2); // 45 shfl
r7 = r7 * r4; // 46 mul
r3 = r3 ^ r4; // 47 xor
r2 = __umulhi(r2, r6); // 48 mulhi
r5 = r5 ^ r4; // 49 xor
r1 = rotr_var(r1, r6); // 50 rotr
r7 = r7 + r0 + ((((sel >> 22u) & 1u) != 0u) ? 0x0fe79cecu : 0x68d3a5c0u); // 51 add
r5 = r5 + r4 + ((((sel >> 8u) & 1u) != 0u) ? 0x04309e6fu : 0xeb764d91u); // 52 add
r7 = r7 ^ r0; // 53 xor
r4 = r4 + r0 + ((((sel >> 6u) & 1u) != 0u) ? 0xba0b81c6u : 0x1390b188u); // 54 add
r0 = r0 + r5 + ((((sel >> 15u) & 1u) != 0u) ? 0x5ec96dd8u : 0x4c21a6cdu); // 55 add
r7 = r5 * r3 + r7; // 56 mad
{ uint32_t b_ = (r0 & mask) & ~15u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint32_t x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r6 = x_; } // 57 load
r5 = r5 + r1 + ((((sel >> 1u) & 1u) != 0u) ? 0x6c0ac4ddu : 0x68297b06u); // 58 add
r6 = rotr_var(r6, r7); // 59 rotr
{ uint32_t b_ = (r4 & mask) & ~15u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint32_t x_ = r7 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r7 = x_; } // 60 load
r6 = r6 ^ __shfl_xor_sync(0xffffffffu, r3, 4); // 61 shfl
r4 = rotl_imm(r4, 6u); // 62 rotl
r1 = r2 * r1 + r1; // 63 mad
}
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_cache_fill(uint32_t* cache, uint32_t nSegments) {
if (nSegments == 0u) return cudaErrorInvalidValue;
uint32_t block = 256u;
uint32_t grid = (nSegments + block - 1u) / block;
igneum_cache_fill<<<grid, block>>>(cache, nSegments);
return cudaGetLastError();
}
cudaError_t igneum_launch_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems) {
if (nItems == 0u) return cudaErrorInvalidValue;
uint32_t block = 256u;
uint32_t grid = (nItems + block - 1u) / block;
igneum_build<<<grid, block>>>(ds, cache, nItems);
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);
}

View file

@ -0,0 +1,372 @@
// Generated by igneum-pow export (generator v2) for seed "igneum-readwidth/B/0". Do not edit by hand.
// OpenCL C twin of the Metal kernel for the same seed (see proto-opencl/README.md, WAVEFRONT.md and program.metal).
// Built from source at runtime by proto-opencl/host.c, which passes these defines:
// IGNEUM_GROUP work-group size of igneum_hash, a multiple of 32 (default 32: one work-group = one 32-lane unit)
// IGNEUM_EXCHANGE 0 = local-memory exchange with a barrier (any device, any wave width; the default)
// 1 = sub_group_shuffle_xor (cl_khr_subgroup_shuffle), only with IGNEUM_GROUP 32 and a sub-group size of exactly 32
// 2 = intel_sub_group_shuffle_xor (cl_intel_subgroups), same condition
// The verification unit is always 32 lanes. A 64-wide hardware wave (AMD GCN/CDNA, RDNA in wave64) runs two units;
// the exchange masks are 1, 2, 4, 8, 16, so every partner lane lies inside the lane's own aligned run of 32.
#ifndef IGNEUM_GROUP
#define IGNEUM_GROUP 32
#endif
#ifndef IGNEUM_EXCHANGE
#define IGNEUM_EXCHANGE 0
#endif
#ifdef __OPENCL_VERSION__
#define IGNEUM_KERNEL_HASH __kernel __attribute__((reqd_work_group_size(IGNEUM_GROUP, 1, 1)))
#define IGNEUM_LOCAL_WORDS(name, n) __local uint name[n]
#if IGNEUM_EXCHANGE == 1
#ifdef cl_khr_subgroups
#pragma OPENCL EXTENSION cl_khr_subgroups : enable
#endif
#ifdef cl_khr_subgroup_shuffle
#pragma OPENCL EXTENSION cl_khr_subgroup_shuffle : enable
#endif
#elif IGNEUM_EXCHANGE == 2
#pragma OPENCL EXTENSION cl_intel_subgroups : enable
#endif
#else
// Not an OpenCL compiler: proto-opencl/emu compiles this file as C++ and supplies the built-ins and these two macros.
#include "emu_opencl.h"
#endif
#if IGNEUM_EXCHANGE == 1
#define IGNEUM_SHFL_XOR(dst, a, m) dst = sub_group_shuffle_xor((a), (uint)(m))
#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u)
#elif IGNEUM_EXCHANGE == 2
#define IGNEUM_SHFL_XOR(dst, a, m) dst = intel_sub_group_shuffle_xor((a), (uint)(m))
#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u)
#else
// Local-memory exchange. Two buffers of IGNEUM_GROUP words alternate (xk counts exchanges), so one barrier per
// exchange is enough: a lane can only overwrite buffer b at exchange k+2 after passing barrier k+1, and every lane
// reaches barrier k+1 only after its read of buffer b at exchange k. The partner lid ^ m stays inside the lane's
// aligned run of 32 because m < 32. Control flow is uniform, so every work-item reaches every barrier.
#define IGNEUM_SHFL_XOR(dst, a, m) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid ^ (uint)(m))]; xk += 1u; }
#define IGNEUM_BCAST0(dst, a) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid & ~31u)]; xk += 1u; }
#endif
static inline uint splitmix32(uint 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. OpenCL rotate() rotates left by n modulo 32.
static inline uint rotl_imm(uint x, uint n) { return rotate(x, n); }
// Right rotation by n modulo 32 as a left rotation by (32 - n) modulo 32; n == 0 gives x.
static inline uint rotr_var(uint x, uint n) { return rotate(x, (0u - n) & 31u); }
static 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;
}
// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines.
// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals.
#define MH_CACHE_LINE_MASK 0x003fffffu
#define MH_SEGMENT_LINES 64u
#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); }
static inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site
// y = ChaCha12 core(x) + x
static inline void mh_chacha_block(const uint* x, uint* y) {
for (uint i = 0u; i < 16u; ++i) y[i] = x[i];
for (uint r = 0u; r < 6u; ++r) {
MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u)
MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u)
}
for (uint i = 0u; i < 16u; ++i) y[i] += x[i];
}
// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0.
static inline void mh_cache_segment(__global uint* cache, uint seg) {
uint prev[16]; uint x[16]; uint y[16];
for (uint i = 0u; i < 16u; ++i) prev[i] = 0u;
for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) {
x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3];
x[4] = 0x3067619fu ^ prev[4];
x[5] = 0x3c269176u ^ prev[5];
x[6] = 0x84a03b03u ^ prev[6];
x[7] = 0xf8c63294u ^ prev[7];
x[8] = 0xff977c5bu ^ prev[8];
x[9] = 0xe60def3eu ^ prev[9];
x[10] = 0x63630141u ^ prev[10];
x[11] = 0xb8fbcb58u ^ prev[11];
x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15];
mh_chacha_block(x, y);
__global uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u);
for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; }
}
}
// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations.
static inline void mh_mixer(uint* s, uint rk) {
s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u;
s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu;
s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u;
s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu;
s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u;
s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u;
s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u;
s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u;
s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du;
s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u;
s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du;
s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu;
s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du;
s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu;
s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u;
s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u;
MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u)
MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u)
MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u)
MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u)
}
// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer.
static inline void mh_item(__global const uint* cache, uint t, uint* s) {
s[0] = 0x3067619fu;
s[1] = 0x3c269176u;
s[2] = 0x84a03b03u;
s[3] = 0xf8c63294u;
s[4] = 0xff977c5bu;
s[5] = 0xe60def3eu;
s[6] = 0x63630141u;
s[7] = 0xb8fbcb58u;
s[8] = t * 0x42146205u + 0xbab68293u;
s[9] = t * 0x52cbe0fbu + 0xcc162340u;
s[10] = t * 0x7ecf4a03u + 0x6ce151ccu;
s[11] = t * 0x6728907fu + 0xe62b8997u;
s[12] = t * 0xd81d9751u + 0xc9c80297u;
s[13] = t * 0x132952c3u + 0xf74a1654u;
s[14] = t * 0xf60de277u + 0x3d704af5u;
s[15] = t * 0x05358035u + 0x3cf522b7u;
for (uint r = 0u; r < 8u; ++r) {
mh_mixer(s, 0x9E3779B9u * (r + 1u));
__global const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u);
for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i];
}
mh_mixer(s, 0x9E3779B9u * 9u);
}
// dataset[w] without the dataset: derive item w >> 4 and take word w & 15.
static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; }
// Memory-hard dataset (MEMHARD.md). One work-item per cache segment; one work-item per 64-byte dataset item.
// The same constants as memhard.h in this pack (one emitter, three dialects).
__kernel void igneum_cache_fill(__global uint* cache, uint nSegments) {
uint seg = (uint)get_global_id(0);
if (seg < nSegments) mh_cache_segment(cache, seg);
}
__kernel void igneum_build(__global uint* ds, __global const uint* cache, uint nItems) {
uint t = (uint)get_global_id(0);
if (t < nItems) {
uint s[16];
mh_item(cache, t, s);
__global uint* d = ds + ((ulong)t * 16u);
for (uint i = 0u; i < 16u; ++i) d[i] = s[i];
}
}
// One hash per work-item. IGNEUM_GROUP is a multiple of 32; lane = lid & 31 and every exchange stays inside the
// lane's own aligned run of 32 work-items, exactly like simd_shuffle_xor inside a 32-wide Metal SIMD group and
// __shfl_xor_sync inside a CUDA warp. Control flow is uniform (no branches at all).
IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask) {
uint gid = (uint)get_global_id(0);
uint lid = (uint)get_local_id(0);
uint nonce = baseNonce + gid;
uint r0, r1, r2, r3, r4, r5, r6, r7;
#if IGNEUM_EXCHANGE == 0
IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP);
uint xk = 0u;
#else
(void)lid;
#endif
{ uint x = nonce ^ 0x3673211cu; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0xaae550b4u; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1]
{ uint x = nonce ^ 0xaae550b4u; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0x5a0ce2e0u; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2]
{ uint x = nonce ^ 0x5a0ce2e0u; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x1d2471cfu; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3]
{ uint x = nonce ^ 0x1d2471cfu; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0xba944366u; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4]
{ uint x = nonce ^ 0xba944366u; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0xbdd4d1dbu; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5]
{ uint x = nonce ^ 0xbdd4d1dbu; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0xcb1984a9u; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6]
{ uint x = nonce ^ 0xcb1984a9u; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0x081ce12au; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7]
{ uint x = nonce ^ 0x081ce12au; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x3673211cu; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0]
for (uint it = 0u; it < 8u; ++it) {
uint sel = r0;
{ uint t_; IGNEUM_SHFL_XOR(t_, r2, 1u); r0 = r0 ^ t_; } // 0 shfl
r2 = r2 * r0; // 1 mul
r0 = r0 + r6 + ((((sel >> 20u) & 1u) != 0u) ? 0x03fa29f3u : 0x7fbf4ae6u); // 2 add
r2 = r2 * r7; // 3 mul
r6 = r6 ^ ds[r2 & mask]; // 4 load
{ uint b_ = (r0 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r7 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r7 = x_; } // 5 load
{ uint t_; IGNEUM_SHFL_XOR(t_, r4, 16u); r6 = r6 ^ t_; } // 6 shfl
r3 = r3 ^ r5; // 7 xor
r6 = r6 ^ ds[r7 & mask]; // 8 load
r0 = r0 + r7 + ((((sel >> 25u) & 1u) != 0u) ? 0x308c81bfu : 0xd59b21b0u); // 9 add
r5 = rotr_var(r5, r7); // 10 rotr
{ uint b_ = (r6 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r3 = x_; } // 11 load
r2 = r2 * r6; // 12 mul
{ uint b_ = (r0 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r7 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r7 = x_; } // 13 load
r2 = r5 * r2 + r2; // 14 mad
r4 = r4 + r0 + ((((sel >> 24u) & 1u) != 0u) ? 0x67081e7eu : 0x902dc661u); // 15 add
{ uint b_ = (r4 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r3 = x_; } // 16 load
r3 = mul_hi(r3, r4); // 17 mulhi
{ uint b_ = (r7 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r4 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r4 = x_; } // 18 load
r2 = r2 + r1 + ((((sel >> 9u) & 1u) != 0u) ? 0xd1e74db0u : 0x3ee3182cu); // 19 add
r7 = r7 ^ r6; // 20 xor
r5 = r5 ^ r3; // 21 xor
{ uint t_; IGNEUM_SHFL_XOR(t_, r2, 16u); r6 = r6 ^ t_; } // 22 shfl
{ uint b_ = (r2 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r0 = x_; } // 23 load
r2 = r2 + r5 + ((((sel >> 22u) & 1u) != 0u) ? 0x62ac9e52u : 0xd2d451c6u); // 24 add
{ uint b_ = (r7 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r3 = x_; } // 25 load
{ uint t_; IGNEUM_SHFL_XOR(t_, r1, 16u); r4 = r4 ^ t_; } // 26 shfl
{ uint b_ = (r4 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r1 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r1 = x_; } // 27 load
r1 = r1 + r5 + ((((sel >> 2u) & 1u) != 0u) ? 0x072cfabeu : 0x111ff813u); // 28 add
r4 = r1 * r2 + r4; // 29 mad
r2 = r2 * r0; // 30 mul
r0 = r0 ^ r5; // 31 xor
r1 = r1 ^ ds[r0 & mask]; // 32 load
r2 = r2 - r3; // 33 sub
{ uint t_; IGNEUM_SHFL_XOR(t_, r0, 2u); r2 = r2 ^ t_; } // 34 shfl
r0 = r0 - r6; // 35 sub
r4 = r4 * r7; // 36 mul
r5 = r5 ^ r6; // 37 xor
r0 = mul_hi(r0, r6); // 38 mulhi
{ uint t_; IGNEUM_SHFL_XOR(t_, r7, 4u); r5 = r5 ^ t_; } // 39 shfl
{ uint b_ = (r3 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r6 = x_; } // 40 load
r2 = r2 ^ ds[r4 & mask]; // 41 load
r5 = r5 ^ ds[r6 & mask]; // 42 load
r1 = rotr_var(r1, r4); // 43 rotr
r0 = r0 | r5; // 44 or
{ uint t_; IGNEUM_SHFL_XOR(t_, r2, 2u); r0 = r0 ^ t_; } // 45 shfl
r7 = r7 * r4; // 46 mul
r3 = r3 ^ r4; // 47 xor
r2 = mul_hi(r2, r6); // 48 mulhi
r5 = r5 ^ r4; // 49 xor
r1 = rotr_var(r1, r6); // 50 rotr
r7 = r7 + r0 + ((((sel >> 22u) & 1u) != 0u) ? 0x0fe79cecu : 0x68d3a5c0u); // 51 add
r5 = r5 + r4 + ((((sel >> 8u) & 1u) != 0u) ? 0x04309e6fu : 0xeb764d91u); // 52 add
r7 = r7 ^ r0; // 53 xor
r4 = r4 + r0 + ((((sel >> 6u) & 1u) != 0u) ? 0xba0b81c6u : 0x1390b188u); // 54 add
r0 = r0 + r5 + ((((sel >> 15u) & 1u) != 0u) ? 0x5ec96dd8u : 0x4c21a6cdu); // 55 add
r7 = r5 * r3 + r7; // 56 mad
{ uint b_ = (r0 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r6 = x_; } // 57 load
r5 = r5 + r1 + ((((sel >> 1u) & 1u) != 0u) ? 0x6c0ac4ddu : 0x68297b06u); // 58 add
r6 = rotr_var(r6, r7); // 59 rotr
{ uint b_ = (r4 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r7 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r7 = x_; } // 60 load
{ uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r6 = r6 ^ t_; } // 61 shfl
r4 = rotl_imm(r4, 6u); // 62 rotl
r1 = r2 * r1 + r1; // 63 mad
}
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;
}
#if IGNEUM_EXCHANGE != 0
// Reports the sub-group size this device uses for a work-group of IGNEUM_GROUP items. host.c runs it only when the
// per-kernel query (clGetKernelSubGroupInfoKHR on igneum_hash) is unavailable; that query is preferred because a
// compiler may pick a different wave width per kernel (RDNA: wave32 or wave64). See WAVEFRONT.md.
IGNEUM_KERNEL_HASH void igneum_probe_subgroup(__global uint* out) {
if (get_local_id(0) == 0u) { out[0] = get_sub_group_size(); out[1] = get_num_sub_groups(); }
}
#endif
// Header-bound variant (bind.rs): the init words come from initw, not SEEDW. Same body as igneum_hash.
IGNEUM_KERNEL_HASH void igneum_hash_bound(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask, __global const uint* initw) {
uint gid = (uint)get_global_id(0);
uint lid = (uint)get_local_id(0);
uint nonce = baseNonce + gid;
uint r0, r1, r2, r3, r4, r5, r6, r7;
uint iw0 = initw[0], iw1 = initw[1], iw2 = initw[2], iw3 = initw[3], iw4 = initw[4], iw5 = initw[5], iw6 = initw[6], iw7 = initw[7];
#if IGNEUM_EXCHANGE == 0
IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP);
uint xk = 0u;
#else
(void)lid;
#endif
{ uint x = nonce ^ iw0; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ iw1; }
{ uint x = nonce ^ iw1; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ iw2; }
{ uint x = nonce ^ iw2; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ iw3; }
{ uint x = nonce ^ iw3; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ iw4; }
{ uint x = nonce ^ iw4; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ iw5; }
{ uint x = nonce ^ iw5; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ iw6; }
{ uint x = nonce ^ iw6; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ iw7; }
{ uint x = nonce ^ iw7; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ iw0; }
for (uint it = 0u; it < 8u; ++it) {
uint sel = r0;
{ uint t_; IGNEUM_SHFL_XOR(t_, r2, 1u); r0 = r0 ^ t_; } // 0 shfl
r2 = r2 * r0; // 1 mul
r0 = r0 + r6 + ((((sel >> 20u) & 1u) != 0u) ? 0x03fa29f3u : 0x7fbf4ae6u); // 2 add
r2 = r2 * r7; // 3 mul
r6 = r6 ^ ds[r2 & mask]; // 4 load
{ uint b_ = (r0 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r7 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r7 = x_; } // 5 load
{ uint t_; IGNEUM_SHFL_XOR(t_, r4, 16u); r6 = r6 ^ t_; } // 6 shfl
r3 = r3 ^ r5; // 7 xor
r6 = r6 ^ ds[r7 & mask]; // 8 load
r0 = r0 + r7 + ((((sel >> 25u) & 1u) != 0u) ? 0x308c81bfu : 0xd59b21b0u); // 9 add
r5 = rotr_var(r5, r7); // 10 rotr
{ uint b_ = (r6 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r3 = x_; } // 11 load
r2 = r2 * r6; // 12 mul
{ uint b_ = (r0 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r7 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r7 = x_; } // 13 load
r2 = r5 * r2 + r2; // 14 mad
r4 = r4 + r0 + ((((sel >> 24u) & 1u) != 0u) ? 0x67081e7eu : 0x902dc661u); // 15 add
{ uint b_ = (r4 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r3 = x_; } // 16 load
r3 = mul_hi(r3, r4); // 17 mulhi
{ uint b_ = (r7 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r4 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r4 = x_; } // 18 load
r2 = r2 + r1 + ((((sel >> 9u) & 1u) != 0u) ? 0xd1e74db0u : 0x3ee3182cu); // 19 add
r7 = r7 ^ r6; // 20 xor
r5 = r5 ^ r3; // 21 xor
{ uint t_; IGNEUM_SHFL_XOR(t_, r2, 16u); r6 = r6 ^ t_; } // 22 shfl
{ uint b_ = (r2 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r0 = x_; } // 23 load
r2 = r2 + r5 + ((((sel >> 22u) & 1u) != 0u) ? 0x62ac9e52u : 0xd2d451c6u); // 24 add
{ uint b_ = (r7 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r3 = x_; } // 25 load
{ uint t_; IGNEUM_SHFL_XOR(t_, r1, 16u); r4 = r4 ^ t_; } // 26 shfl
{ uint b_ = (r4 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r1 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r1 = x_; } // 27 load
r1 = r1 + r5 + ((((sel >> 2u) & 1u) != 0u) ? 0x072cfabeu : 0x111ff813u); // 28 add
r4 = r1 * r2 + r4; // 29 mad
r2 = r2 * r0; // 30 mul
r0 = r0 ^ r5; // 31 xor
r1 = r1 ^ ds[r0 & mask]; // 32 load
r2 = r2 - r3; // 33 sub
{ uint t_; IGNEUM_SHFL_XOR(t_, r0, 2u); r2 = r2 ^ t_; } // 34 shfl
r0 = r0 - r6; // 35 sub
r4 = r4 * r7; // 36 mul
r5 = r5 ^ r6; // 37 xor
r0 = mul_hi(r0, r6); // 38 mulhi
{ uint t_; IGNEUM_SHFL_XOR(t_, r7, 4u); r5 = r5 ^ t_; } // 39 shfl
{ uint b_ = (r3 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r6 = x_; } // 40 load
r2 = r2 ^ ds[r4 & mask]; // 41 load
r5 = r5 ^ ds[r6 & mask]; // 42 load
r1 = rotr_var(r1, r4); // 43 rotr
r0 = r0 | r5; // 44 or
{ uint t_; IGNEUM_SHFL_XOR(t_, r2, 2u); r0 = r0 ^ t_; } // 45 shfl
r7 = r7 * r4; // 46 mul
r3 = r3 ^ r4; // 47 xor
r2 = mul_hi(r2, r6); // 48 mulhi
r5 = r5 ^ r4; // 49 xor
r1 = rotr_var(r1, r6); // 50 rotr
r7 = r7 + r0 + ((((sel >> 22u) & 1u) != 0u) ? 0x0fe79cecu : 0x68d3a5c0u); // 51 add
r5 = r5 + r4 + ((((sel >> 8u) & 1u) != 0u) ? 0x04309e6fu : 0xeb764d91u); // 52 add
r7 = r7 ^ r0; // 53 xor
r4 = r4 + r0 + ((((sel >> 6u) & 1u) != 0u) ? 0xba0b81c6u : 0x1390b188u); // 54 add
r0 = r0 + r5 + ((((sel >> 15u) & 1u) != 0u) ? 0x5ec96dd8u : 0x4c21a6cdu); // 55 add
r7 = r5 * r3 + r7; // 56 mad
{ uint b_ = (r0 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r6 = x_; } // 57 load
r5 = r5 + r1 + ((((sel >> 1u) & 1u) != 0u) ? 0x6c0ac4ddu : 0x68297b06u); // 58 add
r6 = rotr_var(r6, r7); // 59 rotr
{ uint b_ = (r4 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r7 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r7 = x_; } // 60 load
{ uint t_; IGNEUM_SHFL_XOR(t_, r3, 4u); r6 = r6 ^ t_; } // 61 shfl
r4 = rotl_imm(r4, 6u); // 62 rotl
r1 = r2 * r1 + r1; // 63 mad
}
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;
}

View file

@ -0,0 +1,123 @@
// Generated by igneum-pow export (generator v2) for seed "igneum-readwidth/B/0". Do not edit by hand.
// Header-bound twin of igneum_hash in kernel.cu: the init words come from a kernel argument, not SEEDW.
// Host declarations (also in program_bound.h if present):
// struct IgneumInitWords { uint32_t w[8]; };
// cudaError_t igneum_launch_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask,
// IgneumInitWords iw, uint32_t nonces, uint32_t blockWarps);
// cudaError_t igneum_hash_bound_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps);
#include <cuda_runtime.h>
#include <cstdint>
#include "program.h"
struct IgneumInitWords { uint32_t w[8]; };
__device__ __forceinline__ uint32_t splitmix32(uint32_t x) {
x ^= x >> 16; x *= 0x7feb352du;
x ^= x >> 15; x *= 0x846ca68bu;
x ^= x >> 16;
return x;
}
__device__ __forceinline__ uint32_t rotl_imm(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); }
__device__ __forceinline__ uint32_t rotr_var(uint32_t x, uint32_t n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); }
__global__ void igneum_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, IgneumInitWords iw) {
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 ^ iw.w[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ iw.w[1]; }
{ uint32_t x = nonce ^ iw.w[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ iw.w[2]; }
{ uint32_t x = nonce ^ iw.w[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ iw.w[3]; }
{ uint32_t x = nonce ^ iw.w[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ iw.w[4]; }
{ uint32_t x = nonce ^ iw.w[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ iw.w[5]; }
{ uint32_t x = nonce ^ iw.w[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ iw.w[6]; }
{ uint32_t x = nonce ^ iw.w[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ iw.w[7]; }
{ uint32_t x = nonce ^ iw.w[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ iw.w[0]; }
for (uint32_t it = 0u; it < 8u; ++it) {
uint32_t sel = r0;
r0 = r0 ^ __shfl_xor_sync(0xffffffffu, r2, 1); // 0 shfl
r2 = r2 * r0; // 1 mul
r0 = r0 + r6 + ((((sel >> 20u) & 1u) != 0u) ? 0x03fa29f3u : 0x7fbf4ae6u); // 2 add
r2 = r2 * r7; // 3 mul
r6 = r6 ^ ds[r2 & mask]; // 4 load
{ uint32_t b_ = (r0 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r7 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r7 = x_; } // 5 load
r6 = r6 ^ __shfl_xor_sync(0xffffffffu, r4, 16); // 6 shfl
r3 = r3 ^ r5; // 7 xor
r6 = r6 ^ ds[r7 & mask]; // 8 load
r0 = r0 + r7 + ((((sel >> 25u) & 1u) != 0u) ? 0x308c81bfu : 0xd59b21b0u); // 9 add
r5 = rotr_var(r5, r7); // 10 rotr
{ uint32_t b_ = (r6 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r3 = x_; } // 11 load
r2 = r2 * r6; // 12 mul
{ uint32_t b_ = (r0 & mask) & ~15u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint32_t x_ = r7 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r7 = x_; } // 13 load
r2 = r5 * r2 + r2; // 14 mad
r4 = r4 + r0 + ((((sel >> 24u) & 1u) != 0u) ? 0x67081e7eu : 0x902dc661u); // 15 add
{ uint32_t b_ = (r4 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r3 = x_; } // 16 load
r3 = __umulhi(r3, r4); // 17 mulhi
{ uint32_t b_ = (r7 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r4 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r4 = x_; } // 18 load
r2 = r2 + r1 + ((((sel >> 9u) & 1u) != 0u) ? 0xd1e74db0u : 0x3ee3182cu); // 19 add
r7 = r7 ^ r6; // 20 xor
r5 = r5 ^ r3; // 21 xor
r6 = r6 ^ __shfl_xor_sync(0xffffffffu, r2, 16); // 22 shfl
{ uint32_t b_ = (r2 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r0 = x_; } // 23 load
r2 = r2 + r5 + ((((sel >> 22u) & 1u) != 0u) ? 0x62ac9e52u : 0xd2d451c6u); // 24 add
{ uint32_t b_ = (r7 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r3 = x_; } // 25 load
r4 = r4 ^ __shfl_xor_sync(0xffffffffu, r1, 16); // 26 shfl
{ uint32_t b_ = (r4 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r1 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r1 = x_; } // 27 load
r1 = r1 + r5 + ((((sel >> 2u) & 1u) != 0u) ? 0x072cfabeu : 0x111ff813u); // 28 add
r4 = r1 * r2 + r4; // 29 mad
r2 = r2 * r0; // 30 mul
r0 = r0 ^ r5; // 31 xor
r1 = r1 ^ ds[r0 & mask]; // 32 load
r2 = r2 - r3; // 33 sub
r2 = r2 ^ __shfl_xor_sync(0xffffffffu, r0, 2); // 34 shfl
r0 = r0 - r6; // 35 sub
r4 = r4 * r7; // 36 mul
r5 = r5 ^ r6; // 37 xor
r0 = __umulhi(r0, r6); // 38 mulhi
r5 = r5 ^ __shfl_xor_sync(0xffffffffu, r7, 4); // 39 shfl
{ uint32_t b_ = (r3 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r6 = x_; } // 40 load
r2 = r2 ^ ds[r4 & mask]; // 41 load
r5 = r5 ^ ds[r6 & mask]; // 42 load
r1 = rotr_var(r1, r4); // 43 rotr
r0 = r0 | r5; // 44 or
r0 = r0 ^ __shfl_xor_sync(0xffffffffu, r2, 2); // 45 shfl
r7 = r7 * r4; // 46 mul
r3 = r3 ^ r4; // 47 xor
r2 = __umulhi(r2, r6); // 48 mulhi
r5 = r5 ^ r4; // 49 xor
r1 = rotr_var(r1, r6); // 50 rotr
r7 = r7 + r0 + ((((sel >> 22u) & 1u) != 0u) ? 0x0fe79cecu : 0x68d3a5c0u); // 51 add
r5 = r5 + r4 + ((((sel >> 8u) & 1u) != 0u) ? 0x04309e6fu : 0xeb764d91u); // 52 add
r7 = r7 ^ r0; // 53 xor
r4 = r4 + r0 + ((((sel >> 6u) & 1u) != 0u) ? 0xba0b81c6u : 0x1390b188u); // 54 add
r0 = r0 + r5 + ((((sel >> 15u) & 1u) != 0u) ? 0x5ec96dd8u : 0x4c21a6cdu); // 55 add
r7 = r5 * r3 + r7; // 56 mad
{ uint32_t b_ = (r0 & mask) & ~15u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint32_t x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r6 = x_; } // 57 load
r5 = r5 + r1 + ((((sel >> 1u) & 1u) != 0u) ? 0x6c0ac4ddu : 0x68297b06u); // 58 add
r6 = rotr_var(r6, r7); // 59 rotr
{ uint32_t b_ = (r4 & mask) & ~15u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint32_t x_ = r7 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r7 = x_; } // 60 load
r6 = r6 ^ __shfl_xor_sync(0xffffffffu, r3, 4); // 61 shfl
r4 = rotl_imm(r4, 6u); // 62 rotl
r1 = r2 * r1 + r1; // 63 mad
}
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;
}
cudaError_t igneum_launch_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask,
IgneumInitWords iw, 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_bound<<<nonces / block, block>>>(ds, out, baseNonce, mask, iw);
return cudaGetLastError();
}
cudaError_t igneum_hash_bound_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps) {
cudaFuncAttributes attr;
cudaError_t e = cudaFuncGetAttributes(&attr, igneum_hash_bound);
if (e != cudaSuccess) return e;
*numRegs = attr.numRegs;
return cudaOccupancyMaxActiveBlocksPerMultiprocessor(blocksPerSM, igneum_hash_bound, (int)(32u * blockWarps), 0);
}

View file

@ -0,0 +1,108 @@
// Generated by igneum-pow export (generator v2) for seed "igneum-readwidth/B/0". Do not edit by hand.
// Memory-hard dataset core, the same text that the Mac's Metal kernels and CPU verifier were checked against.
// Included by kernel.cu (device), host.cu (host reference) and proto-opencl/host.c (C99 host reference).
// See proto-metal/MEMHARD.md for the construction. kernel.cl carries the same text in OpenCL C.
#pragma once
#ifdef __cplusplus
#include <cstdint>
#else
#include <stdint.h>
#endif
#if defined(__CUDACC__)
#define IGNEUM_HD __host__ __device__ __forceinline__
#elif defined(_MSC_VER) && !defined(__cplusplus)
#define IGNEUM_HD static __inline
#else
#define IGNEUM_HD static inline
#endif
// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines.
// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals.
#define MH_CACHE_LINE_MASK 0x003fffffu
#define MH_SEGMENT_LINES 64u
#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); }
IGNEUM_HD uint32_t mh_rotl(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site
// y = ChaCha12 core(x) + x
IGNEUM_HD void mh_chacha_block(const uint32_t* x, uint32_t* y) {
for (uint32_t i = 0u; i < 16u; ++i) y[i] = x[i];
for (uint32_t r = 0u; r < 6u; ++r) {
MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u)
MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u)
}
for (uint32_t i = 0u; i < 16u; ++i) y[i] += x[i];
}
// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0.
IGNEUM_HD void mh_cache_segment(uint32_t* cache, uint32_t seg) {
uint32_t prev[16]; uint32_t x[16]; uint32_t y[16];
for (uint32_t i = 0u; i < 16u; ++i) prev[i] = 0u;
for (uint32_t j = 0u; j < MH_SEGMENT_LINES; ++j) {
x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3];
x[4] = 0x3067619fu ^ prev[4];
x[5] = 0x3c269176u ^ prev[5];
x[6] = 0x84a03b03u ^ prev[6];
x[7] = 0xf8c63294u ^ prev[7];
x[8] = 0xff977c5bu ^ prev[8];
x[9] = 0xe60def3eu ^ prev[9];
x[10] = 0x63630141u ^ prev[10];
x[11] = 0xb8fbcb58u ^ prev[11];
x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15];
mh_chacha_block(x, y);
uint32_t* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u);
for (uint32_t i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; }
}
}
// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations.
IGNEUM_HD void mh_mixer(uint32_t* s, uint32_t rk) {
s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u;
s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu;
s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u;
s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu;
s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u;
s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u;
s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u;
s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u;
s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du;
s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u;
s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du;
s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu;
s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du;
s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu;
s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u;
s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u;
MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u)
MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u)
MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u)
MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u)
}
// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer.
IGNEUM_HD void mh_item(const uint32_t* cache, uint32_t t, uint32_t* s) {
s[0] = 0x3067619fu;
s[1] = 0x3c269176u;
s[2] = 0x84a03b03u;
s[3] = 0xf8c63294u;
s[4] = 0xff977c5bu;
s[5] = 0xe60def3eu;
s[6] = 0x63630141u;
s[7] = 0xb8fbcb58u;
s[8] = t * 0x42146205u + 0xbab68293u;
s[9] = t * 0x52cbe0fbu + 0xcc162340u;
s[10] = t * 0x7ecf4a03u + 0x6ce151ccu;
s[11] = t * 0x6728907fu + 0xe62b8997u;
s[12] = t * 0xd81d9751u + 0xc9c80297u;
s[13] = t * 0x132952c3u + 0xf74a1654u;
s[14] = t * 0xf60de277u + 0x3d704af5u;
s[15] = t * 0x05358035u + 0x3cf522b7u;
for (uint32_t r = 0u; r < 8u; ++r) {
mh_mixer(s, 0x9E3779B9u * (r + 1u));
const uint32_t* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u);
for (uint32_t i = 0u; i < 16u; ++i) s[i] ^= line[i];
}
mh_mixer(s, 0x9E3779B9u * 9u);
}
// dataset[w] without the dataset: derive item w >> 4 and take word w & 15.
IGNEUM_HD uint32_t mh_word(const uint32_t* cache, uint32_t w) { uint32_t s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; }

View file

@ -0,0 +1,106 @@
#include <metal_stdlib>
using namespace metal;
// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines.
// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals.
#define MH_CACHE_LINE_MASK 0x003fffffu
#define MH_SEGMENT_LINES 64u
#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); }
inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site
// y = ChaCha12 core(x) + x
inline void mh_chacha_block(const thread uint* x, thread uint* y) {
for (uint i = 0u; i < 16u; ++i) y[i] = x[i];
for (uint r = 0u; r < 6u; ++r) {
MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u)
MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u)
}
for (uint i = 0u; i < 16u; ++i) y[i] += x[i];
}
// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0.
inline void mh_cache_segment(device uint* cache, uint seg) {
uint prev[16]; uint x[16]; uint y[16];
for (uint i = 0u; i < 16u; ++i) prev[i] = 0u;
for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) {
x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3];
x[4] = 0x3067619fu ^ prev[4];
x[5] = 0x3c269176u ^ prev[5];
x[6] = 0x84a03b03u ^ prev[6];
x[7] = 0xf8c63294u ^ prev[7];
x[8] = 0xff977c5bu ^ prev[8];
x[9] = 0xe60def3eu ^ prev[9];
x[10] = 0x63630141u ^ prev[10];
x[11] = 0xb8fbcb58u ^ prev[11];
x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15];
mh_chacha_block(x, y);
device uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u);
for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; }
}
}
// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations.
inline void mh_mixer(thread uint* s, uint rk) {
s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u;
s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu;
s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u;
s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu;
s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u;
s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u;
s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u;
s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u;
s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du;
s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u;
s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du;
s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu;
s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du;
s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu;
s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u;
s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u;
MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u)
MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u)
MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u)
MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u)
}
// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer.
inline void mh_item(device const uint* cache, uint t, thread uint* s) {
s[0] = 0x3067619fu;
s[1] = 0x3c269176u;
s[2] = 0x84a03b03u;
s[3] = 0xf8c63294u;
s[4] = 0xff977c5bu;
s[5] = 0xe60def3eu;
s[6] = 0x63630141u;
s[7] = 0xb8fbcb58u;
s[8] = t * 0x42146205u + 0xbab68293u;
s[9] = t * 0x52cbe0fbu + 0xcc162340u;
s[10] = t * 0x7ecf4a03u + 0x6ce151ccu;
s[11] = t * 0x6728907fu + 0xe62b8997u;
s[12] = t * 0xd81d9751u + 0xc9c80297u;
s[13] = t * 0x132952c3u + 0xf74a1654u;
s[14] = t * 0xf60de277u + 0x3d704af5u;
s[15] = t * 0x05358035u + 0x3cf522b7u;
for (uint r = 0u; r < 8u; ++r) {
mh_mixer(s, 0x9E3779B9u * (r + 1u));
device const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u);
for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i];
}
mh_mixer(s, 0x9E3779B9u * 9u);
}
// dataset[w] without the dataset: derive item w >> 4 and take word w & 15.
inline uint mh_word(device const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; }
// One thread per segment (2^16 threads).
kernel void igneum_cache_fill(device uint* cache [[buffer(0)]], uint gid [[thread_position_in_grid]]) {
mh_cache_segment(cache, gid);
}
// One thread per 64-byte item (dataset words / 16 threads).
kernel void igneum_build(device const uint* cache [[buffer(0)]], device uint* dataset [[buffer(1)]],
uint gid [[thread_position_in_grid]]) {
uint s[16];
mh_item(cache, gid, s);
device uint* d = dataset + gid * 16u;
for (uint i = 0u; i < 16u; ++i) d[i] = s[i];
}

View file

@ -0,0 +1,60 @@
// Generated by igneum-pow export (generator v2) for seed "igneum-readwidth/B/0". Do not edit by hand.
// Program metadata for host.cu plus the launch wrappers defined in kernel.cu.
// Also included by proto-opencl/host.c (C99), which defines IGNEUM_NO_CUDA first and reads only the macros.
#pragma once
#ifdef __cplusplus
#include <cstdint>
#else
#include <stdint.h>
#endif
#ifndef IGNEUM_NO_CUDA
#include <cuda_runtime.h>
#endif
#define IGNEUM_SEED_STRING "igneum-readwidth/B/0"
#define IGNEUM_SEED_BYTES_HEX "69676e65756d2d7265616477696474682f422f30"
#define IGNEUM_GENERATOR 2
#define IGNEUM_PROGRAM_ATTEMPT 0
#define IGNEUM_PROGRAM_ID 0x5404700e8a2db8feull
#define IGNEUM_DAY_STRING "2026-10-03"
#define IGNEUM_DAY_BYTES_HEX "6461792f323032362d31302d3033"
#define IGNEUM_DAY0 0x3067619fu
#define IGNEUM_DAY1 0x3c269176u
#define IGNEUM_DATASET_LOG2 28
#define IGNEUM_MASK 0x0fffffffu
#define IGNEUM_LANES 32
#define IGNEUM_ITERATIONS 8
#define IGNEUM_INSTR_COUNT 64
#define IGNEUM_LOADS_PER_HASH 128
#define IGNEUM_WIDE_LOADS_PER_HASH 0
#define IGNEUM_OP_MIX "load=16 add=11 shfl=8 xor=8 mul=6 mad=4 rotr=4 mulhi=3 sub=2 or=1 rotl=1"
// Read-width experiment (5 October 2026, docs/plans/read-width.md): NOT the lottery hash. A load of W words reads
// the W-word-aligned address and folds every word into dst: x = dst ^ w[0]; x = (rotl(x, 11) * 0x9e3779b1) ^ w[j]; dst = x.
#define IGNEUM_LOAD_CLASS "mix25-50-25"
#define IGNEUM_LOAD_SLOTS 16
#define IGNEUM_LOAD_MIX { 25, 50, 25 }
#define IGNEUM_LOAD_WIDTH_COUNTS { 5, 8, 3 } // loads of 4, 16, 64 bytes per program
#define IGNEUM_BYTES_PER_HASH 2720
#define IGNEUM_FOLD_ROT 11
#define IGNEUM_FOLD_MUL 0x9e3779b1u
// 0 = closed-form dataset (ds_elem), 1 = memory-hard cache construction (MEMHARD.md, memhard.h)
#define IGNEUM_DATASET_MODE 1
#define IGNEUM_SEEDW_INIT { 0x3673211cu, 0xaae550b4u, 0x5a0ce2e0u, 0x1d2471cfu, 0xba944366u, 0xbdd4d1dbu, 0xcb1984a9u, 0x081ce12au }
#define IGNEUM_KEY_INIT { 0x3067619fu, 0x3c269176u, 0x84a03b03u, 0xf8c63294u, 0xff977c5bu, 0xe60def3eu, 0x63630141u, 0xb8fbcb58u }
#define IGNEUM_CACHE_LOG2_WORDS 26
#define IGNEUM_CACHE_SEGMENT_LOG2_LINES 6
#define IGNEUM_CACHE_SEGMENTS 65536u
#define IGNEUM_ITEM_ROUNDS 8
#define IGNEUM_MIX_ROT_INIT { 20u, 20u, 19u, 4u, 26u, 3u, 3u, 27u }
#define IGNEUM_MIX_MUL_INIT { 0x42146205u, 0x52cbe0fbu, 0x7ecf4a03u, 0x6728907fu, 0xd81d9751u, 0x132952c3u, 0xf60de277u, 0x05358035u, 0xbaf6499du, 0xe4db9667u, 0x3e98f45du, 0xd0004eddu, 0x2691630du, 0x9beb3bcfu, 0xab310379u, 0x99cfb423u }
#define IGNEUM_MIX_RC_INIT { 0xbab68293u, 0xcc162340u, 0x6ce151ccu, 0xe62b8997u, 0xc9c80297u, 0xf74a1654u, 0x3d704af5u, 0x3cf522b7u, 0x2b9cac04u, 0xa880ac10u, 0x13e5dd1du, 0x6fc3e233u, 0x2d83eeacu, 0x9006e8bfu, 0x2c4b5362u, 0x31b49ee2u }
#ifndef IGNEUM_NO_CUDA
// Defined in kernel.cu. All launch on the default stream and return cudaGetLastError().
cudaError_t igneum_launch_cache_fill(uint32_t* cache, uint32_t nSegments);
cudaError_t igneum_launch_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems);
cudaError_t igneum_launch_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask,
uint32_t nonces, uint32_t blockWarps);
cudaError_t igneum_hash_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps);
#endif

View file

@ -0,0 +1,127 @@
{
"format": "igneum-program-pack-3",
"generator": 2,
"attempt": 0,
"program_id": "0x5404700e8a2db8fe",
"program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32",
"dataset_mode": "memory-hard",
"seed": "igneum-readwidth/B/0",
"seed_bytes": "69676e65756d2d7265616477696474682f422f30",
"seed_words": ["0x3673211c", "0xaae550b4", "0x5a0ce2e0", "0x1d2471cf", "0xba944366", "0xbdd4d1db", "0xcb1984a9", "0x081ce12a"],
"seed_derivation": "seed_words = FNV-1a 64 over seed_bytes (attempt 0) or seed_bytes || attempt_le32 (attempt k >= 1), basis ^ (salt * 0x9E3779B97F4A7C15) for salt 0..3, then h ^= h>>33; h *= 0xff51afd7ed558ccd; h ^= h>>33; words[2*salt] = low 32, words[2*salt+1] = high 32",
"generator_rule": "version 2: exactly 16 load slots drawn first from instructions 1..63 (partial Fisher-Yates), the other 48 ops from the ten non-load weights (sum 75); a load's source is drawn from the registers other than dst written by an earlier instruction and not read by a load since; the candidate must pass the acceptance rule of spec 01 section 1.4.6 (static: no cyclically stale load source, every register has an injecting write; dynamic: 64 units on the seed-keyed closed-form dataset with no constant register bit, no lane-constant load site, under 164 saturated final values, every output bit within 136 of 1024, distinct addresses above 245760), else the next attempt of the seed is tried",
"lanes": 32,
"registers": 8,
"iterations": 8,
"instruction_count": 64,
"loads_per_hash": 128,
"load_class": "mix25-50-25",
"load_slots": 16,
"load_mix_percent_4_16_64": [25, 50, 25],
"load_width_counts_4_16_64": [5, 8, 3],
"bytes_per_hash": 2720,
"wide_load": "read-width experiment (5 October 2026, docs/plans/read-width.md), NOT the lottery hash: a load of W words (width field, 4 or 16) reads dataset[b .. b + W) with b = (src & mask) & ~(W - 1) and folds every word into dst: x = dst ^ w[0]; for j in 1..W: x = (rotl(x, 11) * 0x9e3779b1) ^ w[j]; dst = x; width 1 is the plain load; the width is drawn per instruction from the class mix with one extra below(100) draw after the nine of version 2, and the program id is FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed words || attempt_le32 || mix[3] || load_slots",
"op_mix": {"load": 16, "add": 11, "shfl": 8, "xor": 8, "mul": 6, "mad": 4, "rotr": 4, "mulhi": 3, "sub": 2, "or": 1, "rotl": 1},
"register_init": "for i in 0..7: x = nonce ^ seed_words[i]; x += 0x9e3779b9 * (i+1) (mod 2^32); x = splitmix32(x); r[i] = x ^ seed_words[(i+1) & 7]",
"splitmix32": "x ^= x>>16; x *= 0x7feb352d; x ^= x>>15; x *= 0x846ca68b; x ^= x>>16",
"iteration": "sel = r0 sampled once at the top of each iteration, then all instructions in order",
"output": "lo = r0 ^ rotl(r1,7) ^ rotl(r2,14) ^ rotl(r3,21); hi = r4 ^ rotl(r5,9) ^ rotl(r6,18) ^ rotl(r7,27); out = (hi << 32) | lo",
"op_semantics": {
"add": "dst = dst + src + (bit `bit` of sel ? imm2 : imm)",
"sub": "dst = dst - src",
"mul": "dst = dst * src (low 32)",
"mulhi": "dst = high 32 bits of dst * src",
"xor": "dst = dst ^ src",
"or": "dst = dst | src",
"rotl": "dst = rotl(dst, rot), rot in 1..31",
"rotr": "dst = rotr(dst, src & 31)",
"mad": "dst = src * src2 + dst",
"shfl": "dst = dst ^ (src of lane (lane ^ mask)), mask in {1,2,4,8,16}, within the 32-lane warp",
"load": "dst = dst ^ dataset[src & dataset.mask]",
"wload": "base = (src of lane 0 & dataset.mask) & ~31; dst = dst ^ dataset[base + lane] (warp-coalesced 128-byte load, lever b, only when --wide-frac > 0)"
},
"dataset": {
"log2_words": 28,
"bytes": 1073741824,
"mask": "0x0fffffff",
"day": "2026-10-03",
"day_bytes": "6461792f323032362d31302d3033",
"day_words_from": "seed_words_from_bytes(day_bytes)",
"d0": "0x3067619f",
"d1": "0x3c269176",
"mode": "memory-hard",
"spec": "proto-metal/MEMHARD.md",
"key": ["0x3067619f", "0x3c269176", "0x84a03b03", "0xf8c63294", "0xff977c5b", "0xe60def3e", "0x63630141", "0xb8fbcb58"],
"key_derivation": "the 8 words of seed_words_from_bytes(day_bytes); d0, d1 are key[0], key[1]",
"cache": {"log2_words": 26, "bytes": 268435456, "line_words": 16, "segment_lines": 64, "segments": 65536, "block": "ChaCha12 core + feed-forward, rotations 16 12 8 7", "sigma": ["0x61707865", "0x3320646e", "0x79622d32", "0x6b206574"], "tag": ["0x49676e65", "0x756d4d48"], "chain": "in_j = prev_line ^ (sigma[0..3] || key[0..7] || seg || j || tag[0..1]); line_j = block(in_j); prev_0 = 0"},
"mixer": {"draw": "SplitMix64 seeded with key[0] | key[1] << 32: rot[0..7] = 1 + next() % 31, mul[0..15] = low32(next()) | 1, rc[0..15] = low32(next())", "rot": [20, 20, 19, 4, 26, 3, 3, 27], "mul": ["0x42146205", "0x52cbe0fb", "0x7ecf4a03", "0x6728907f", "0xd81d9751", "0x132952c3", "0xf60de277", "0x05358035", "0xbaf6499d", "0xe4db9667", "0x3e98f45d", "0xd0004edd", "0x2691630d", "0x9beb3bcf", "0xab310379", "0x99cfb423"], "rc": ["0xbab68293", "0xcc162340", "0x6ce151cc", "0xe62b8997", "0xc9c80297", "0xf74a1654", "0x3d704af5", "0x3cf522b7", "0x2b9cac04", "0xa880ac10", "0x13e5dd1d", "0x6fc3e233", "0x2d83eeac", "0x9006e8bf", "0x2c4b5362", "0x31b49ee2"], "round": "for i in 0..15: s[i] = (s[i] ^ (rc[i] + (r+1) * 0x9E3779B9)) * mul[i]; then quarter rounds on columns (0,4,8,12) (1,5,9,13) (2,6,10,14) (3,7,11,15) with rot[0..3] and diagonals (0,5,10,15) (1,6,11,12) (2,7,8,13) (3,4,9,14) with rot[4..7]", "quarter_round": "a += b; d ^= a; d = rotl(d, r1); c += d; b ^= c; b = rotl(b, r2); a += b; d ^= a; d = rotl(d, r3); c += d; b ^= c; b = rotl(b, r4)"},
"item": "s[0..7] = key; s[8+i] = t * mul[i] + rc[i] for i in 0..7; for r in 0..7: s = M_r(s); line = s[0] & 0x003fffff; s[i] ^= cache[line * 16 + i]; then s = M_8(s); item(t) = s",
"word": "dataset[w] = item(w >> 4)[w & 15]"
},
"instructions": [
{"i": 0, "op": "shfl", "dst": 0, "src": 2, "src2": 1, "imm": "0x4e24f8dc", "imm2": "0x287cd532", "rot": 25, "bit": 23, "mask": 1, "width": 1},
{"i": 1, "op": "mul", "dst": 2, "src": 0, "src2": 2, "imm": "0x6268115c", "imm2": "0xfe52413e", "rot": 30, "bit": 14, "mask": 4, "width": 1},
{"i": 2, "op": "add", "dst": 0, "src": 6, "src2": 3, "imm": "0x7fbf4ae6", "imm2": "0x03fa29f3", "rot": 30, "bit": 20, "mask": 4, "width": 1},
{"i": 3, "op": "mul", "dst": 2, "src": 7, "src2": 5, "imm": "0xcd8625e1", "imm2": "0xd6540f7e", "rot": 15, "bit": 15, "mask": 4, "width": 1},
{"i": 4, "op": "load", "dst": 6, "src": 2, "src2": 5, "imm": "0xfeb8e6c3", "imm2": "0x3a797ec1", "rot": 31, "bit": 17, "mask": 2, "width": 1},
{"i": 5, "op": "load", "dst": 7, "src": 0, "src2": 4, "imm": "0x8f4f518d", "imm2": "0xa6ed0a9e", "rot": 2, "bit": 14, "mask": 1, "width": 4},
{"i": 6, "op": "shfl", "dst": 6, "src": 4, "src2": 1, "imm": "0xddd60fee", "imm2": "0x9529e2c3", "rot": 21, "bit": 9, "mask": 16, "width": 1},
{"i": 7, "op": "xor", "dst": 3, "src": 5, "src2": 3, "imm": "0x015e2388", "imm2": "0x1a59ccd7", "rot": 2, "bit": 12, "mask": 2, "width": 1},
{"i": 8, "op": "load", "dst": 6, "src": 7, "src2": 5, "imm": "0x90060b55", "imm2": "0x2315bf19", "rot": 20, "bit": 1, "mask": 16, "width": 1},
{"i": 9, "op": "add", "dst": 0, "src": 7, "src2": 7, "imm": "0xd59b21b0", "imm2": "0x308c81bf", "rot": 5, "bit": 25, "mask": 16, "width": 1},
{"i": 10, "op": "rotr", "dst": 5, "src": 7, "src2": 0, "imm": "0x4d9af117", "imm2": "0x49369d76", "rot": 31, "bit": 21, "mask": 4, "width": 1},
{"i": 11, "op": "load", "dst": 3, "src": 6, "src2": 2, "imm": "0x2ddeb7f6", "imm2": "0x855ff344", "rot": 25, "bit": 26, "mask": 1, "width": 4},
{"i": 12, "op": "mul", "dst": 2, "src": 6, "src2": 6, "imm": "0x8b16ab7b", "imm2": "0x970dc008", "rot": 27, "bit": 10, "mask": 16, "width": 1},
{"i": 13, "op": "load", "dst": 7, "src": 0, "src2": 7, "imm": "0xcfd7f002", "imm2": "0x1e1d817b", "rot": 1, "bit": 0, "mask": 2, "width": 16},
{"i": 14, "op": "mad", "dst": 2, "src": 5, "src2": 2, "imm": "0xea959ce8", "imm2": "0x156549ed", "rot": 11, "bit": 2, "mask": 8, "width": 1},
{"i": 15, "op": "add", "dst": 4, "src": 0, "src2": 7, "imm": "0x902dc661", "imm2": "0x67081e7e", "rot": 12, "bit": 24, "mask": 16, "width": 1},
{"i": 16, "op": "load", "dst": 3, "src": 4, "src2": 4, "imm": "0x86963c37", "imm2": "0xcdea85b2", "rot": 8, "bit": 19, "mask": 8, "width": 4},
{"i": 17, "op": "mulhi", "dst": 3, "src": 4, "src2": 5, "imm": "0x5c5720f0", "imm2": "0x551bd9d5", "rot": 20, "bit": 12, "mask": 1, "width": 1},
{"i": 18, "op": "load", "dst": 4, "src": 7, "src2": 7, "imm": "0xa9fcc38a", "imm2": "0xca2468f8", "rot": 25, "bit": 31, "mask": 1, "width": 4},
{"i": 19, "op": "add", "dst": 2, "src": 1, "src2": 7, "imm": "0x3ee3182c", "imm2": "0xd1e74db0", "rot": 6, "bit": 9, "mask": 16, "width": 1},
{"i": 20, "op": "xor", "dst": 7, "src": 6, "src2": 0, "imm": "0x7e5446be", "imm2": "0x9e29589c", "rot": 30, "bit": 14, "mask": 4, "width": 1},
{"i": 21, "op": "xor", "dst": 5, "src": 3, "src2": 1, "imm": "0x161aa453", "imm2": "0xf277d1f5", "rot": 22, "bit": 18, "mask": 4, "width": 1},
{"i": 22, "op": "shfl", "dst": 6, "src": 2, "src2": 0, "imm": "0xde7d5591", "imm2": "0x6dce8eb2", "rot": 11, "bit": 15, "mask": 16, "width": 1},
{"i": 23, "op": "load", "dst": 0, "src": 2, "src2": 1, "imm": "0xf1ab876f", "imm2": "0x976deeca", "rot": 15, "bit": 26, "mask": 8, "width": 4},
{"i": 24, "op": "add", "dst": 2, "src": 5, "src2": 5, "imm": "0xd2d451c6", "imm2": "0x62ac9e52", "rot": 19, "bit": 22, "mask": 1, "width": 1},
{"i": 25, "op": "load", "dst": 3, "src": 7, "src2": 0, "imm": "0xfed1b2fd", "imm2": "0x56ac9cfa", "rot": 1, "bit": 16, "mask": 4, "width": 4},
{"i": 26, "op": "shfl", "dst": 4, "src": 1, "src2": 5, "imm": "0x41708a21", "imm2": "0x9c760382", "rot": 25, "bit": 30, "mask": 16, "width": 1},
{"i": 27, "op": "load", "dst": 1, "src": 4, "src2": 6, "imm": "0xe3c92c63", "imm2": "0x6d61f62c", "rot": 13, "bit": 19, "mask": 1, "width": 4},
{"i": 28, "op": "add", "dst": 1, "src": 5, "src2": 0, "imm": "0x111ff813", "imm2": "0x072cfabe", "rot": 16, "bit": 2, "mask": 1, "width": 1},
{"i": 29, "op": "mad", "dst": 4, "src": 1, "src2": 2, "imm": "0xeae5ec79", "imm2": "0x537fa17e", "rot": 20, "bit": 10, "mask": 16, "width": 1},
{"i": 30, "op": "mul", "dst": 2, "src": 0, "src2": 2, "imm": "0xf468725d", "imm2": "0xeb01a28a", "rot": 30, "bit": 11, "mask": 2, "width": 1},
{"i": 31, "op": "xor", "dst": 0, "src": 5, "src2": 1, "imm": "0x4b6a048b", "imm2": "0xd15a1f0a", "rot": 26, "bit": 4, "mask": 4, "width": 1},
{"i": 32, "op": "load", "dst": 1, "src": 0, "src2": 4, "imm": "0x80ec21f1", "imm2": "0x694ca047", "rot": 19, "bit": 26, "mask": 8, "width": 1},
{"i": 33, "op": "sub", "dst": 2, "src": 3, "src2": 6, "imm": "0x11d13dc6", "imm2": "0x6c8931e8", "rot": 10, "bit": 13, "mask": 2, "width": 1},
{"i": 34, "op": "shfl", "dst": 2, "src": 0, "src2": 2, "imm": "0x056d2ef8", "imm2": "0xa833af40", "rot": 22, "bit": 26, "mask": 2, "width": 1},
{"i": 35, "op": "sub", "dst": 0, "src": 6, "src2": 6, "imm": "0x9e95fe74", "imm2": "0xbf36d23c", "rot": 1, "bit": 12, "mask": 2, "width": 1},
{"i": 36, "op": "mul", "dst": 4, "src": 7, "src2": 5, "imm": "0x3d50d394", "imm2": "0x637cc722", "rot": 12, "bit": 4, "mask": 16, "width": 1},
{"i": 37, "op": "xor", "dst": 5, "src": 6, "src2": 7, "imm": "0x3272395d", "imm2": "0x36dd11fb", "rot": 31, "bit": 4, "mask": 16, "width": 1},
{"i": 38, "op": "mulhi", "dst": 0, "src": 6, "src2": 6, "imm": "0x67324b08", "imm2": "0xd825e2fa", "rot": 11, "bit": 21, "mask": 1, "width": 1},
{"i": 39, "op": "shfl", "dst": 5, "src": 7, "src2": 6, "imm": "0x6aff9133", "imm2": "0x22d6b873", "rot": 17, "bit": 4, "mask": 4, "width": 1},
{"i": 40, "op": "load", "dst": 6, "src": 3, "src2": 7, "imm": "0xac1e076f", "imm2": "0x0e0ff9a2", "rot": 21, "bit": 8, "mask": 2, "width": 4},
{"i": 41, "op": "load", "dst": 2, "src": 4, "src2": 0, "imm": "0xd94d7e29", "imm2": "0xa18f1af4", "rot": 14, "bit": 4, "mask": 4, "width": 1},
{"i": 42, "op": "load", "dst": 5, "src": 6, "src2": 4, "imm": "0xe02caafc", "imm2": "0xe37b52cf", "rot": 28, "bit": 24, "mask": 8, "width": 1},
{"i": 43, "op": "rotr", "dst": 1, "src": 4, "src2": 5, "imm": "0xde64b2e0", "imm2": "0xeb2327ba", "rot": 19, "bit": 5, "mask": 1, "width": 1},
{"i": 44, "op": "or", "dst": 0, "src": 5, "src2": 1, "imm": "0x23797ba8", "imm2": "0xdaa53dbe", "rot": 17, "bit": 0, "mask": 8, "width": 1},
{"i": 45, "op": "shfl", "dst": 0, "src": 2, "src2": 5, "imm": "0x9bb8bc40", "imm2": "0x9a1d5dc9", "rot": 22, "bit": 4, "mask": 2, "width": 1},
{"i": 46, "op": "mul", "dst": 7, "src": 4, "src2": 5, "imm": "0x238172b8", "imm2": "0x1a3008f9", "rot": 25, "bit": 20, "mask": 16, "width": 1},
{"i": 47, "op": "xor", "dst": 3, "src": 4, "src2": 5, "imm": "0x934047d2", "imm2": "0xe984b304", "rot": 27, "bit": 2, "mask": 1, "width": 1},
{"i": 48, "op": "mulhi", "dst": 2, "src": 6, "src2": 5, "imm": "0xf1c06c0c", "imm2": "0x0f679cae", "rot": 5, "bit": 31, "mask": 1, "width": 1},
{"i": 49, "op": "xor", "dst": 5, "src": 4, "src2": 3, "imm": "0x4b9454cd", "imm2": "0x816b6e2d", "rot": 5, "bit": 22, "mask": 4, "width": 1},
{"i": 50, "op": "rotr", "dst": 1, "src": 6, "src2": 3, "imm": "0xe234dec2", "imm2": "0xeddc8839", "rot": 6, "bit": 7, "mask": 1, "width": 1},
{"i": 51, "op": "add", "dst": 7, "src": 0, "src2": 0, "imm": "0x68d3a5c0", "imm2": "0x0fe79cec", "rot": 5, "bit": 22, "mask": 2, "width": 1},
{"i": 52, "op": "add", "dst": 5, "src": 4, "src2": 1, "imm": "0xeb764d91", "imm2": "0x04309e6f", "rot": 23, "bit": 8, "mask": 1, "width": 1},
{"i": 53, "op": "xor", "dst": 7, "src": 0, "src2": 3, "imm": "0x6d420b6b", "imm2": "0xa4eb51ff", "rot": 27, "bit": 9, "mask": 1, "width": 1},
{"i": 54, "op": "add", "dst": 4, "src": 0, "src2": 0, "imm": "0x1390b188", "imm2": "0xba0b81c6", "rot": 4, "bit": 6, "mask": 16, "width": 1},
{"i": 55, "op": "add", "dst": 0, "src": 5, "src2": 0, "imm": "0x4c21a6cd", "imm2": "0x5ec96dd8", "rot": 18, "bit": 15, "mask": 2, "width": 1},
{"i": 56, "op": "mad", "dst": 7, "src": 5, "src2": 3, "imm": "0xd0655c74", "imm2": "0x8dc4bd13", "rot": 24, "bit": 11, "mask": 1, "width": 1},
{"i": 57, "op": "load", "dst": 6, "src": 0, "src2": 7, "imm": "0xe738815c", "imm2": "0xc546dbe7", "rot": 24, "bit": 29, "mask": 2, "width": 16},
{"i": 58, "op": "add", "dst": 5, "src": 1, "src2": 6, "imm": "0x68297b06", "imm2": "0x6c0ac4dd", "rot": 19, "bit": 1, "mask": 8, "width": 1},
{"i": 59, "op": "rotr", "dst": 6, "src": 7, "src2": 3, "imm": "0x83f974a4", "imm2": "0x13fcd20f", "rot": 3, "bit": 18, "mask": 2, "width": 1},
{"i": 60, "op": "load", "dst": 7, "src": 4, "src2": 5, "imm": "0xbca249c5", "imm2": "0x2094b70b", "rot": 15, "bit": 24, "mask": 2, "width": 16},
{"i": 61, "op": "shfl", "dst": 6, "src": 3, "src2": 0, "imm": "0xcd1113c9", "imm2": "0x475dbb4b", "rot": 11, "bit": 11, "mask": 4, "width": 1},
{"i": 62, "op": "rotl", "dst": 4, "src": 5, "src2": 7, "imm": "0xdbc5d842", "imm2": "0xf8b7004c", "rot": 6, "bit": 25, "mask": 8, "width": 1},
{"i": 63, "op": "mad", "dst": 1, "src": 2, "src2": 1, "imm": "0xd2538bab", "imm2": "0x6b862569", "rot": 9, "bit": 5, "mask": 1, "width": 1}
]
}

View file

@ -0,0 +1,109 @@
#include <metal_stdlib>
using namespace metal;
#define MASK 0x0fffffffu
constant uint SEEDW[8] = { 0x3673211cu, 0xaae550b4u, 0x5a0ce2e0u, 0x1d2471cfu, 0xba944366u, 0xbdd4d1dbu, 0xcb1984a9u, 0x081ce12au };
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;
}
kernel void igneum_hash(device const uint* dataset [[buffer(0)]],
device ulong* out [[buffer(1)]],
constant uint& baseNonce [[buffer(2)]],
uint gid [[thread_position_in_grid]]) {
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;
r0 = r0 ^ simd_shuffle_xor(r2, (ushort)1); // 0
r2 = r2 * r0; // 1
r0 = r0 + r6 + select(0x7fbf4ae6u, 0x03fa29f3u, ((sel >> 20u) & 1u) != 0u); // 2
r2 = r2 * r7; // 3
r6 = r6 ^ dataset[r2 & MASK]; // 4
{ uint b_ = (r0 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r7 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r7 = x_; } // 5
r6 = r6 ^ simd_shuffle_xor(r4, (ushort)16); // 6
r3 = r3 ^ r5; // 7
r6 = r6 ^ dataset[r7 & MASK]; // 8
r0 = r0 + r7 + select(0xd59b21b0u, 0x308c81bfu, ((sel >> 25u) & 1u) != 0u); // 9
r5 = rotr_var(r5, r7); // 10
{ uint b_ = (r6 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r3 = x_; } // 11
r2 = r2 * r6; // 12
{ uint b_ = (r0 & MASK) & ~15u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint x_ = r7 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r7 = x_; } // 13
r2 = r5 * r2 + r2; // 14
r4 = r4 + r0 + select(0x902dc661u, 0x67081e7eu, ((sel >> 24u) & 1u) != 0u); // 15
{ uint b_ = (r4 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r3 = x_; } // 16
r3 = mulhi(r3, r4); // 17
{ uint b_ = (r7 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r4 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r4 = x_; } // 18
r2 = r2 + r1 + select(0x3ee3182cu, 0xd1e74db0u, ((sel >> 9u) & 1u) != 0u); // 19
r7 = r7 ^ r6; // 20
r5 = r5 ^ r3; // 21
r6 = r6 ^ simd_shuffle_xor(r2, (ushort)16); // 22
{ uint b_ = (r2 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r0 = x_; } // 23
r2 = r2 + r5 + select(0xd2d451c6u, 0x62ac9e52u, ((sel >> 22u) & 1u) != 0u); // 24
{ uint b_ = (r7 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r3 = x_; } // 25
r4 = r4 ^ simd_shuffle_xor(r1, (ushort)16); // 26
{ uint b_ = (r4 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r1 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r1 = x_; } // 27
r1 = r1 + r5 + select(0x111ff813u, 0x072cfabeu, ((sel >> 2u) & 1u) != 0u); // 28
r4 = r1 * r2 + r4; // 29
r2 = r2 * r0; // 30
r0 = r0 ^ r5; // 31
r1 = r1 ^ dataset[r0 & MASK]; // 32
r2 = r2 - r3; // 33
r2 = r2 ^ simd_shuffle_xor(r0, (ushort)2); // 34
r0 = r0 - r6; // 35
r4 = r4 * r7; // 36
r5 = r5 ^ r6; // 37
r0 = mulhi(r0, r6); // 38
r5 = r5 ^ simd_shuffle_xor(r7, (ushort)4); // 39
{ uint b_ = (r3 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r6 = x_; } // 40
r2 = r2 ^ dataset[r4 & MASK]; // 41
r5 = r5 ^ dataset[r6 & MASK]; // 42
r1 = rotr_var(r1, r4); // 43
r0 = r0 | r5; // 44
r0 = r0 ^ simd_shuffle_xor(r2, (ushort)2); // 45
r7 = r7 * r4; // 46
r3 = r3 ^ r4; // 47
r2 = mulhi(r2, r6); // 48
r5 = r5 ^ r4; // 49
r1 = rotr_var(r1, r6); // 50
r7 = r7 + r0 + select(0x68d3a5c0u, 0x0fe79cecu, ((sel >> 22u) & 1u) != 0u); // 51
r5 = r5 + r4 + select(0xeb764d91u, 0x04309e6fu, ((sel >> 8u) & 1u) != 0u); // 52
r7 = r7 ^ r0; // 53
r4 = r4 + r0 + select(0x1390b188u, 0xba0b81c6u, ((sel >> 6u) & 1u) != 0u); // 54
r0 = r0 + r5 + select(0x4c21a6cdu, 0x5ec96dd8u, ((sel >> 15u) & 1u) != 0u); // 55
r7 = r5 * r3 + r7; // 56
{ uint b_ = (r0 & MASK) & ~15u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r6 = x_; } // 57
r5 = r5 + r1 + select(0x68297b06u, 0x6c0ac4ddu, ((sel >> 1u) & 1u) != 0u); // 58
r6 = rotr_var(r6, r7); // 59
{ uint b_ = (r4 & MASK) & ~15u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint x_ = r7 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r7 = x_; } // 60
r6 = r6 ^ simd_shuffle_xor(r3, (ushort)4); // 61
r4 = rotl_imm(r4, 6u); // 62
r1 = r2 * r1 + r1; // 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;
}

View file

@ -0,0 +1,111 @@
#include <metal_stdlib>
using namespace metal;
#define MASK 0x0fffffffu
constant uint SEEDW[8] = { 0x3673211cu, 0xaae550b4u, 0x5a0ce2e0u, 0x1d2471cfu, 0xba944366u, 0xbdd4d1dbu, 0xcb1984a9u, 0x081ce12au };
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;
}
// Header-bound variant: the init words come from buffer 3 (bind.rs), not from SEEDW.
kernel void igneum_hash_bound(device const uint* dataset [[buffer(0)]],
device ulong* out [[buffer(1)]],
constant uint& baseNonce [[buffer(2)]],
constant uint* initw [[buffer(3)]],
uint gid [[thread_position_in_grid]]) {
uint nonce = baseNonce + gid;
uint r0, r1, r2, r3, r4, r5, r6, r7;
{ uint x = nonce ^ initw[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ initw[1]; }
{ uint x = nonce ^ initw[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ initw[2]; }
{ uint x = nonce ^ initw[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ initw[3]; }
{ uint x = nonce ^ initw[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ initw[4]; }
{ uint x = nonce ^ initw[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ initw[5]; }
{ uint x = nonce ^ initw[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ initw[6]; }
{ uint x = nonce ^ initw[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ initw[7]; }
{ uint x = nonce ^ initw[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ initw[0]; }
for (uint it = 0u; it < 8u; ++it) {
uint sel = r0;
r0 = r0 ^ simd_shuffle_xor(r2, (ushort)1); // 0
r2 = r2 * r0; // 1
r0 = r0 + r6 + select(0x7fbf4ae6u, 0x03fa29f3u, ((sel >> 20u) & 1u) != 0u); // 2
r2 = r2 * r7; // 3
r6 = r6 ^ dataset[r2 & MASK]; // 4
{ uint b_ = (r0 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r7 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r7 = x_; } // 5
r6 = r6 ^ simd_shuffle_xor(r4, (ushort)16); // 6
r3 = r3 ^ r5; // 7
r6 = r6 ^ dataset[r7 & MASK]; // 8
r0 = r0 + r7 + select(0xd59b21b0u, 0x308c81bfu, ((sel >> 25u) & 1u) != 0u); // 9
r5 = rotr_var(r5, r7); // 10
{ uint b_ = (r6 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r3 = x_; } // 11
r2 = r2 * r6; // 12
{ uint b_ = (r0 & MASK) & ~15u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint x_ = r7 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r7 = x_; } // 13
r2 = r5 * r2 + r2; // 14
r4 = r4 + r0 + select(0x902dc661u, 0x67081e7eu, ((sel >> 24u) & 1u) != 0u); // 15
{ uint b_ = (r4 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r3 = x_; } // 16
r3 = mulhi(r3, r4); // 17
{ uint b_ = (r7 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r4 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r4 = x_; } // 18
r2 = r2 + r1 + select(0x3ee3182cu, 0xd1e74db0u, ((sel >> 9u) & 1u) != 0u); // 19
r7 = r7 ^ r6; // 20
r5 = r5 ^ r3; // 21
r6 = r6 ^ simd_shuffle_xor(r2, (ushort)16); // 22
{ uint b_ = (r2 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r0 = x_; } // 23
r2 = r2 + r5 + select(0xd2d451c6u, 0x62ac9e52u, ((sel >> 22u) & 1u) != 0u); // 24
{ uint b_ = (r7 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r3 = x_; } // 25
r4 = r4 ^ simd_shuffle_xor(r1, (ushort)16); // 26
{ uint b_ = (r4 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r1 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r1 = x_; } // 27
r1 = r1 + r5 + select(0x111ff813u, 0x072cfabeu, ((sel >> 2u) & 1u) != 0u); // 28
r4 = r1 * r2 + r4; // 29
r2 = r2 * r0; // 30
r0 = r0 ^ r5; // 31
r1 = r1 ^ dataset[r0 & MASK]; // 32
r2 = r2 - r3; // 33
r2 = r2 ^ simd_shuffle_xor(r0, (ushort)2); // 34
r0 = r0 - r6; // 35
r4 = r4 * r7; // 36
r5 = r5 ^ r6; // 37
r0 = mulhi(r0, r6); // 38
r5 = r5 ^ simd_shuffle_xor(r7, (ushort)4); // 39
{ uint b_ = (r3 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r6 = x_; } // 40
r2 = r2 ^ dataset[r4 & MASK]; // 41
r5 = r5 ^ dataset[r6 & MASK]; // 42
r1 = rotr_var(r1, r4); // 43
r0 = r0 | r5; // 44
r0 = r0 ^ simd_shuffle_xor(r2, (ushort)2); // 45
r7 = r7 * r4; // 46
r3 = r3 ^ r4; // 47
r2 = mulhi(r2, r6); // 48
r5 = r5 ^ r4; // 49
r1 = rotr_var(r1, r6); // 50
r7 = r7 + r0 + select(0x68d3a5c0u, 0x0fe79cecu, ((sel >> 22u) & 1u) != 0u); // 51
r5 = r5 + r4 + select(0xeb764d91u, 0x04309e6fu, ((sel >> 8u) & 1u) != 0u); // 52
r7 = r7 ^ r0; // 53
r4 = r4 + r0 + select(0x1390b188u, 0xba0b81c6u, ((sel >> 6u) & 1u) != 0u); // 54
r0 = r0 + r5 + select(0x4c21a6cdu, 0x5ec96dd8u, ((sel >> 15u) & 1u) != 0u); // 55
r7 = r5 * r3 + r7; // 56
{ uint b_ = (r0 & MASK) & ~15u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint x_ = r6 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r6 = x_; } // 57
r5 = r5 + r1 + select(0x68297b06u, 0x6c0ac4ddu, ((sel >> 1u) & 1u) != 0u); // 58
r6 = rotr_var(r6, r7); // 59
{ uint b_ = (r4 & MASK) & ~15u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint x_ = r7 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r7 = x_; } // 60
r6 = r6 ^ simd_shuffle_xor(r3, (ushort)4); // 61
r4 = rotl_imm(r4, 6u); // 62
r1 = r2 * r1 + r1; // 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;
}

View file

@ -0,0 +1,57 @@
// Generated by igneum-pow export (generator v2) for seed "igneum-readwidth/B/0". Do not edit by hand.
// Expected outputs: igneum-pow (Rust) CPU interpreter, generator v2, memory-hard dataset
#pragma once
#ifdef __cplusplus
#include <cstdint>
#else
#include <stdint.h>
#endif
#define IGNEUM_VEC_WARPS 3
static const uint32_t IGNEUM_VEC_BASE[IGNEUM_VEC_WARPS] = { 0u, 4096u, 1000000u };
static const uint64_t IGNEUM_VEC_OUT[IGNEUM_VEC_WARPS][32] = {
{ // base nonce 0
0x2b6c8e6b238224dbull, 0x94dca5778f870901ull, 0xb0c977094b3c1a7aull, 0xdc0f62ab6e8db552ull, 0x5581af7ea67bfd20ull, 0xaa10426d0e2aa72full, 0x60fd1bf45ef55751ull, 0x9ab74415a3de8b66ull,
0x2cb6ee8ac9c0efc8ull, 0x2ae892465dff9094ull, 0x99c9c94b9d2874c7ull, 0xb12be63d01872ea3ull, 0x84024fac8312e908ull, 0xbb67e4878f174f0cull, 0x3b027e93ca233521ull, 0x8ba6cfeb9bfbbc0full,
0x44c59d77e3247d78ull, 0xd4a765b48c283d2aull, 0xe53083a844015f24ull, 0xb0f7b8dad47d924bull, 0x185b4a0f6150375eull, 0x537ed78b8976846cull, 0xc9d4264a0fc2da7dull, 0x6c0ef3c92dd3e063ull,
0x396461e1a90913e9ull, 0xc2ea0411a102f780ull, 0x0d8b3f485c471e7eull, 0xd8a5c0b7995246c7ull, 0x4507e1e49dbd3c35ull, 0x80145ecfc13419c0ull, 0x9c2f1f09b3ef4ed1ull, 0xd776b774670d63f1ull
},
{ // base nonce 4096
0xe094ec92f00bd1fdull, 0x14d3ad7d06eba7bfull, 0xd4b4ee3a4e95e930ull, 0x5656fc12f488f952ull, 0x4db64fa3172356b8ull, 0x7669809a83c52addull, 0x240c4920d64dd3ebull, 0xfcc0c08f3cebe318ull,
0xd48c95d54226a468ull, 0x383acc22e4367a07ull, 0x1b16e6976aa7160eull, 0xc1d0216840450b35ull, 0xcfc19bac8283d5e0ull, 0xaecef9011360b2c8ull, 0x6a1dbf38ce8f3476ull, 0x813ecd9804528e50ull,
0x95273d12cbfda3e0ull, 0xb6abb5bc816f81d2ull, 0x3d734251c7eb5488ull, 0x162710fc525e1a35ull, 0xa8485c7fa11f1d69ull, 0xf81a8cdc4b928aa5ull, 0x1ad63b6197331e30ull, 0x04ce2b194f3fe8adull,
0xc76ba96148e01225ull, 0x26e24635c3556e18ull, 0xeb8c92b751bc5c70ull, 0xb8886d57d441fe5dull, 0x3f8d68c1f1f1fe2dull, 0x7c441c21089fb44eull, 0x27e2950b292fec5aull, 0x581d48ac2f0dc82eull
},
{ // base nonce 1000000
0xea40156c5e27f6a3ull, 0x1db25b47fd85ab8eull, 0xdf6bd10d80c1b46dull, 0x2e1da26fcbea64b0ull, 0xdd5e41310fcabbfbull, 0xdf9780cb1f87d4afull, 0x6028258e6529007full, 0x886fecbfe753c3d9ull,
0x3c20a9a6f5bcb77eull, 0x605194089256f91cull, 0xd7546f5b5c79f188ull, 0xefc8ae5b45bd346bull, 0xd067b744f6659df8ull, 0x9c8fbb301da7e947ull, 0x238f22c8b511e7cbull, 0xa67a31f99be23902ull,
0x5d9224b25e12fcffull, 0x0166de07227aaebbull, 0xfb50fea4c86552a7ull, 0xf7535d8fe65fe0d0ull, 0x161ab76c522de840ull, 0xaa3d2ddcd8e80d1aull, 0x9bdb9180dd7d39faull, 0xa4111c74b1d438c9ull,
0x75ec298c4f067224ull, 0x9c923c3a46403618ull, 0x61ec429ff3c0283eull, 0x155cf3df9f82ff51ull, 0xd6f0279de6eec131ull, 0xc3ea627086d07a96ull, 0x1cb80fef0710e142ull, 0xfd4772b1cd071be4ull
}
};
// Dataset self-test: dataset[0..15] and dataset[IGNEUM_MASK] (268435455).
static const uint32_t IGNEUM_DS_HEAD[16] = {
0xffc3cd94u, 0x5920ccd8u, 0x392f44bbu, 0x5e57f67au, 0x2f2bc2a9u, 0x620b0e36u, 0xbdc09014u, 0x436654bfu,
0x311e0b48u, 0x1abd93adu, 0x59cc7ce8u, 0xee5247b2u, 0x86171fe8u, 0x6d874751u, 0xc9f7728fu, 0x7c2a435du
};
static const uint32_t IGNEUM_DS_LAST_INDEX = 268435455u;
static const uint32_t IGNEUM_DS_LAST = 0xa33ada72u;
// 64 sampled dataset words (index, value) computed on the Mac.
#define IGNEUM_DS_SAMPLES 64
static const uint32_t IGNEUM_DS_SAMPLE_INDEX[IGNEUM_DS_SAMPLES] = {
59471966u, 217795994u, 208353206u, 42483309u, 172547758u, 148076330u, 183853158u, 214389424u, 267488061u, 169781097u, 184093494u, 153880993u, 84977930u, 46426879u, 3093825u, 225364072u, 44593546u, 260713159u, 168250303u, 52384140u, 223401610u, 45554030u, 95410555u, 175039924u, 79171087u, 267580473u, 24168642u, 37981670u, 171551130u, 195559979u, 204611762u, 140997658u, 138925853u, 86637313u, 20736778u, 219665210u, 160430336u, 264654675u, 8013395u, 228945585u, 213884386u, 104419827u, 44185464u, 142737231u, 99284897u, 132475900u, 61861762u, 132056166u, 262388043u, 91878046u, 117353561u, 124768597u, 71352993u, 190698941u, 46055428u, 55281366u, 165145231u, 106810753u, 171985651u, 232085256u, 159510492u, 40072060u, 209107596u, 39023794u
};
static const uint32_t IGNEUM_DS_SAMPLE_VALUE[IGNEUM_DS_SAMPLES] = {
0xe8b73d94u, 0x337028b5u, 0xafe148c9u, 0xab99f7aeu, 0x434ea619u, 0xd85cb880u, 0x54764c7fu, 0x82c7e420u, 0xedf4cb9eu, 0x9884c959u, 0x223ee793u, 0x3a9ccf69u, 0x81da4fd2u, 0xd6ce8cb9u, 0xe3922dcau, 0x3e7e6bdeu, 0x382a3acau, 0x567e7f7fu, 0x25a0f084u, 0xbfeef128u, 0xe338abfbu, 0x7c3b5280u, 0x909bc5f1u, 0xd8b74b9cu, 0x8e31a22eu, 0x26b5f1d8u, 0x79122c00u, 0xcafc3340u, 0xd5e02ea3u, 0x1aee1afdu, 0xdb090d9au, 0xb049f435u, 0x4954d8bau, 0x03797ba0u, 0x196eefbdu, 0xd153412au, 0xbe5d2c4bu, 0xdaa14f0eu, 0x8e61ed07u, 0x9e9a64c6u, 0x2e29ff36u, 0x392a8589u, 0xb56a5912u, 0xfa6e8b57u, 0xd1a737cbu, 0xb0fa841au, 0xbe1c341fu, 0xe25be0f1u, 0xe937f543u, 0xebab2248u, 0x8e1b607au, 0x202a2fedu, 0x95e2819cu, 0x9c9652d4u, 0x32fedef0u, 0xdecfff82u, 0xcb5d43e5u, 0xb735806au, 0x8905939cu, 0xfbf8472du, 0xada74e5du, 0x7ebdeeeau, 0x0119f2b3u, 0xa9a376b8u
};
// Cache self-test (memory-hard mode): cache[0..15], the last 16 words, and FNV-1a 64 over all 2^26 words.
static const uint32_t IGNEUM_CACHE_HEAD[16] = {
0x355a86d2u, 0x7957db1cu, 0xd21772afu, 0x6fc1e09bu, 0xd55ce61du, 0x6e6a278bu, 0xd3f543ceu, 0x223d8e82u,
0x143ab337u, 0x2e9f05bdu, 0x2eb389bfu, 0x0c6e449eu, 0x5cfa4222u, 0xba6560feu, 0x8e3e1aa4u, 0xdbcc1d53u
};
static const uint32_t IGNEUM_CACHE_LAST[16] = {
0x41190d91u, 0xbd277957u, 0x22ddbb49u, 0x6986f207u, 0xdf69a4d6u, 0x26401a3au, 0x818230fbu, 0xc417122du,
0x3597b211u, 0xb553ce55u, 0xcf39cc0du, 0x3b7fc43au, 0x3fd43b00u, 0x67e1c80eu, 0xffa7ea7du, 0xca2960abu
};
static const uint64_t IGNEUM_CACHE_FNV64 = 0x48c4f5bf24166b2eull;

View file

@ -0,0 +1,36 @@
{
"seed": "igneum-readwidth/B/0",
"day": "2026-10-03",
"dataset_mode": "memory-hard",
"dataset_log2_words": 28,
"mask": "0x0fffffff",
"lanes": 32,
"source": "igneum-pow (Rust) CPU interpreter, generator v2, memory-hard dataset",
"warps": [
{"base_nonce": 0, "expected": [
"0x2b6c8e6b238224db", "0x94dca5778f870901", "0xb0c977094b3c1a7a", "0xdc0f62ab6e8db552", "0x5581af7ea67bfd20", "0xaa10426d0e2aa72f", "0x60fd1bf45ef55751", "0x9ab74415a3de8b66",
"0x2cb6ee8ac9c0efc8", "0x2ae892465dff9094", "0x99c9c94b9d2874c7", "0xb12be63d01872ea3", "0x84024fac8312e908", "0xbb67e4878f174f0c", "0x3b027e93ca233521", "0x8ba6cfeb9bfbbc0f",
"0x44c59d77e3247d78", "0xd4a765b48c283d2a", "0xe53083a844015f24", "0xb0f7b8dad47d924b", "0x185b4a0f6150375e", "0x537ed78b8976846c", "0xc9d4264a0fc2da7d", "0x6c0ef3c92dd3e063",
"0x396461e1a90913e9", "0xc2ea0411a102f780", "0x0d8b3f485c471e7e", "0xd8a5c0b7995246c7", "0x4507e1e49dbd3c35", "0x80145ecfc13419c0", "0x9c2f1f09b3ef4ed1", "0xd776b774670d63f1"
]},
{"base_nonce": 4096, "expected": [
"0xe094ec92f00bd1fd", "0x14d3ad7d06eba7bf", "0xd4b4ee3a4e95e930", "0x5656fc12f488f952", "0x4db64fa3172356b8", "0x7669809a83c52add", "0x240c4920d64dd3eb", "0xfcc0c08f3cebe318",
"0xd48c95d54226a468", "0x383acc22e4367a07", "0x1b16e6976aa7160e", "0xc1d0216840450b35", "0xcfc19bac8283d5e0", "0xaecef9011360b2c8", "0x6a1dbf38ce8f3476", "0x813ecd9804528e50",
"0x95273d12cbfda3e0", "0xb6abb5bc816f81d2", "0x3d734251c7eb5488", "0x162710fc525e1a35", "0xa8485c7fa11f1d69", "0xf81a8cdc4b928aa5", "0x1ad63b6197331e30", "0x04ce2b194f3fe8ad",
"0xc76ba96148e01225", "0x26e24635c3556e18", "0xeb8c92b751bc5c70", "0xb8886d57d441fe5d", "0x3f8d68c1f1f1fe2d", "0x7c441c21089fb44e", "0x27e2950b292fec5a", "0x581d48ac2f0dc82e"
]},
{"base_nonce": 1000000, "expected": [
"0xea40156c5e27f6a3", "0x1db25b47fd85ab8e", "0xdf6bd10d80c1b46d", "0x2e1da26fcbea64b0", "0xdd5e41310fcabbfb", "0xdf9780cb1f87d4af", "0x6028258e6529007f", "0x886fecbfe753c3d9",
"0x3c20a9a6f5bcb77e", "0x605194089256f91c", "0xd7546f5b5c79f188", "0xefc8ae5b45bd346b", "0xd067b744f6659df8", "0x9c8fbb301da7e947", "0x238f22c8b511e7cb", "0xa67a31f99be23902",
"0x5d9224b25e12fcff", "0x0166de07227aaebb", "0xfb50fea4c86552a7", "0xf7535d8fe65fe0d0", "0x161ab76c522de840", "0xaa3d2ddcd8e80d1a", "0x9bdb9180dd7d39fa", "0xa4111c74b1d438c9",
"0x75ec298c4f067224", "0x9c923c3a46403618", "0x61ec429ff3c0283e", "0x155cf3df9f82ff51", "0xd6f0279de6eec131", "0xc3ea627086d07a96", "0x1cb80fef0710e142", "0xfd4772b1cd071be4"
]}
],
"dataset_head": ["0xffc3cd94", "0x5920ccd8", "0x392f44bb", "0x5e57f67a", "0x2f2bc2a9", "0x620b0e36", "0xbdc09014", "0x436654bf", "0x311e0b48", "0x1abd93ad", "0x59cc7ce8", "0xee5247b2", "0x86171fe8", "0x6d874751", "0xc9f7728f", "0x7c2a435d"],
"dataset_last_index": 268435455,
"dataset_last": "0xa33ada72",
"dataset_samples": [{"index": 59471966, "value": "0xe8b73d94"}, {"index": 217795994, "value": "0x337028b5"}, {"index": 208353206, "value": "0xafe148c9"}, {"index": 42483309, "value": "0xab99f7ae"}, {"index": 172547758, "value": "0x434ea619"}, {"index": 148076330, "value": "0xd85cb880"}, {"index": 183853158, "value": "0x54764c7f"}, {"index": 214389424, "value": "0x82c7e420"}, {"index": 267488061, "value": "0xedf4cb9e"}, {"index": 169781097, "value": "0x9884c959"}, {"index": 184093494, "value": "0x223ee793"}, {"index": 153880993, "value": "0x3a9ccf69"}, {"index": 84977930, "value": "0x81da4fd2"}, {"index": 46426879, "value": "0xd6ce8cb9"}, {"index": 3093825, "value": "0xe3922dca"}, {"index": 225364072, "value": "0x3e7e6bde"}, {"index": 44593546, "value": "0x382a3aca"}, {"index": 260713159, "value": "0x567e7f7f"}, {"index": 168250303, "value": "0x25a0f084"}, {"index": 52384140, "value": "0xbfeef128"}, {"index": 223401610, "value": "0xe338abfb"}, {"index": 45554030, "value": "0x7c3b5280"}, {"index": 95410555, "value": "0x909bc5f1"}, {"index": 175039924, "value": "0xd8b74b9c"}, {"index": 79171087, "value": "0x8e31a22e"}, {"index": 267580473, "value": "0x26b5f1d8"}, {"index": 24168642, "value": "0x79122c00"}, {"index": 37981670, "value": "0xcafc3340"}, {"index": 171551130, "value": "0xd5e02ea3"}, {"index": 195559979, "value": "0x1aee1afd"}, {"index": 204611762, "value": "0xdb090d9a"}, {"index": 140997658, "value": "0xb049f435"}, {"index": 138925853, "value": "0x4954d8ba"}, {"index": 86637313, "value": "0x03797ba0"}, {"index": 20736778, "value": "0x196eefbd"}, {"index": 219665210, "value": "0xd153412a"}, {"index": 160430336, "value": "0xbe5d2c4b"}, {"index": 264654675, "value": "0xdaa14f0e"}, {"index": 8013395, "value": "0x8e61ed07"}, {"index": 228945585, "value": "0x9e9a64c6"}, {"index": 213884386, "value": "0x2e29ff36"}, {"index": 104419827, "value": "0x392a8589"}, {"index": 44185464, "value": "0xb56a5912"}, {"index": 142737231, "value": "0xfa6e8b57"}, {"index": 99284897, "value": "0xd1a737cb"}, {"index": 132475900, "value": "0xb0fa841a"}, {"index": 61861762, "value": "0xbe1c341f"}, {"index": 132056166, "value": "0xe25be0f1"}, {"index": 262388043, "value": "0xe937f543"}, {"index": 91878046, "value": "0xebab2248"}, {"index": 117353561, "value": "0x8e1b607a"}, {"index": 124768597, "value": "0x202a2fed"}, {"index": 71352993, "value": "0x95e2819c"}, {"index": 190698941, "value": "0x9c9652d4"}, {"index": 46055428, "value": "0x32fedef0"}, {"index": 55281366, "value": "0xdecfff82"}, {"index": 165145231, "value": "0xcb5d43e5"}, {"index": 106810753, "value": "0xb735806a"}, {"index": 171985651, "value": "0x8905939c"}, {"index": 232085256, "value": "0xfbf8472d"}, {"index": 159510492, "value": "0xada74e5d"}, {"index": 40072060, "value": "0x7ebdeeea"}, {"index": 209107596, "value": "0x0119f2b3"}, {"index": 39023794, "value": "0xa9a376b8"}],
"cache_head": ["0x355a86d2", "0x7957db1c", "0xd21772af", "0x6fc1e09b", "0xd55ce61d", "0x6e6a278b", "0xd3f543ce", "0x223d8e82", "0x143ab337", "0x2e9f05bd", "0x2eb389bf", "0x0c6e449e", "0x5cfa4222", "0xba6560fe", "0x8e3e1aa4", "0xdbcc1d53"],
"cache_last_line": ["0x41190d91", "0xbd277957", "0x22ddbb49", "0x6986f207", "0xdf69a4d6", "0x26401a3a", "0x818230fb", "0xc417122d", "0x3597b211", "0xb553ce55", "0xcf39cc0d", "0x3b7fc43a", "0x3fd43b00", "0x67e1c80e", "0xffa7ea7d", "0xca2960ab"],
"cache_fnv1a64": "0x48c4f5bf24166b2e"
}

View file

@ -0,0 +1,278 @@
// Generated by igneum-pow export (generator v2) for seed "igneum-readwidth/B/1". Do not edit by hand.
// OpenCL C twin of the Metal kernel for the same seed (see proto-opencl/README.md, WAVEFRONT.md and program.metal).
// Built from source at runtime by proto-opencl/host.c, which passes these defines:
// IGNEUM_GROUP work-group size of igneum_hash, a multiple of 32 (default 32: one work-group = one 32-lane unit)
// IGNEUM_EXCHANGE 0 = local-memory exchange with a barrier (any device, any wave width; the default)
// 1 = sub_group_shuffle_xor (cl_khr_subgroup_shuffle), only with IGNEUM_GROUP 32 and a sub-group size of exactly 32
// 2 = intel_sub_group_shuffle_xor (cl_intel_subgroups), same condition
// The verification unit is always 32 lanes. A 64-wide hardware wave (AMD GCN/CDNA, RDNA in wave64) runs two units;
// the exchange masks are 1, 2, 4, 8, 16, so every partner lane lies inside the lane's own aligned run of 32.
#ifndef IGNEUM_GROUP
#define IGNEUM_GROUP 32
#endif
#ifndef IGNEUM_EXCHANGE
#define IGNEUM_EXCHANGE 0
#endif
#ifdef __OPENCL_VERSION__
#define IGNEUM_KERNEL_HASH __kernel __attribute__((reqd_work_group_size(IGNEUM_GROUP, 1, 1)))
#define IGNEUM_LOCAL_WORDS(name, n) __local uint name[n]
#if IGNEUM_EXCHANGE == 1
#ifdef cl_khr_subgroups
#pragma OPENCL EXTENSION cl_khr_subgroups : enable
#endif
#ifdef cl_khr_subgroup_shuffle
#pragma OPENCL EXTENSION cl_khr_subgroup_shuffle : enable
#endif
#elif IGNEUM_EXCHANGE == 2
#pragma OPENCL EXTENSION cl_intel_subgroups : enable
#endif
#else
// Not an OpenCL compiler: proto-opencl/emu compiles this file as C++ and supplies the built-ins and these two macros.
#include "emu_opencl.h"
#endif
#if IGNEUM_EXCHANGE == 1
#define IGNEUM_SHFL_XOR(dst, a, m) dst = sub_group_shuffle_xor((a), (uint)(m))
#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u)
#elif IGNEUM_EXCHANGE == 2
#define IGNEUM_SHFL_XOR(dst, a, m) dst = intel_sub_group_shuffle_xor((a), (uint)(m))
#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u)
#else
// Local-memory exchange. Two buffers of IGNEUM_GROUP words alternate (xk counts exchanges), so one barrier per
// exchange is enough: a lane can only overwrite buffer b at exchange k+2 after passing barrier k+1, and every lane
// reaches barrier k+1 only after its read of buffer b at exchange k. The partner lid ^ m stays inside the lane's
// aligned run of 32 because m < 32. Control flow is uniform, so every work-item reaches every barrier.
#define IGNEUM_SHFL_XOR(dst, a, m) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid ^ (uint)(m))]; xk += 1u; }
#define IGNEUM_BCAST0(dst, a) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid & ~31u)]; xk += 1u; }
#endif
static inline uint splitmix32(uint 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. OpenCL rotate() rotates left by n modulo 32.
static inline uint rotl_imm(uint x, uint n) { return rotate(x, n); }
// Right rotation by n modulo 32 as a left rotation by (32 - n) modulo 32; n == 0 gives x.
static inline uint rotr_var(uint x, uint n) { return rotate(x, (0u - n) & 31u); }
static 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;
}
// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines.
// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals.
#define MH_CACHE_LINE_MASK 0x003fffffu
#define MH_SEGMENT_LINES 64u
#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); }
static inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site
// y = ChaCha12 core(x) + x
static inline void mh_chacha_block(const uint* x, uint* y) {
for (uint i = 0u; i < 16u; ++i) y[i] = x[i];
for (uint r = 0u; r < 6u; ++r) {
MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u)
MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u)
}
for (uint i = 0u; i < 16u; ++i) y[i] += x[i];
}
// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0.
static inline void mh_cache_segment(__global uint* cache, uint seg) {
uint prev[16]; uint x[16]; uint y[16];
for (uint i = 0u; i < 16u; ++i) prev[i] = 0u;
for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) {
x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3];
x[4] = 0x3067619fu ^ prev[4];
x[5] = 0x3c269176u ^ prev[5];
x[6] = 0x84a03b03u ^ prev[6];
x[7] = 0xf8c63294u ^ prev[7];
x[8] = 0xff977c5bu ^ prev[8];
x[9] = 0xe60def3eu ^ prev[9];
x[10] = 0x63630141u ^ prev[10];
x[11] = 0xb8fbcb58u ^ prev[11];
x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15];
mh_chacha_block(x, y);
__global uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u);
for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; }
}
}
// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations.
static inline void mh_mixer(uint* s, uint rk) {
s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u;
s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu;
s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u;
s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu;
s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u;
s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u;
s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u;
s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u;
s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du;
s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u;
s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du;
s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu;
s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du;
s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu;
s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u;
s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u;
MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u)
MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u)
MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u)
MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u)
}
// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer.
static inline void mh_item(__global const uint* cache, uint t, uint* s) {
s[0] = 0x3067619fu;
s[1] = 0x3c269176u;
s[2] = 0x84a03b03u;
s[3] = 0xf8c63294u;
s[4] = 0xff977c5bu;
s[5] = 0xe60def3eu;
s[6] = 0x63630141u;
s[7] = 0xb8fbcb58u;
s[8] = t * 0x42146205u + 0xbab68293u;
s[9] = t * 0x52cbe0fbu + 0xcc162340u;
s[10] = t * 0x7ecf4a03u + 0x6ce151ccu;
s[11] = t * 0x6728907fu + 0xe62b8997u;
s[12] = t * 0xd81d9751u + 0xc9c80297u;
s[13] = t * 0x132952c3u + 0xf74a1654u;
s[14] = t * 0xf60de277u + 0x3d704af5u;
s[15] = t * 0x05358035u + 0x3cf522b7u;
for (uint r = 0u; r < 8u; ++r) {
mh_mixer(s, 0x9E3779B9u * (r + 1u));
__global const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u);
for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i];
}
mh_mixer(s, 0x9E3779B9u * 9u);
}
// dataset[w] without the dataset: derive item w >> 4 and take word w & 15.
static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; }
// Memory-hard dataset (MEMHARD.md). One work-item per cache segment; one work-item per 64-byte dataset item.
// The same constants as memhard.h in this pack (one emitter, three dialects).
__kernel void igneum_cache_fill(__global uint* cache, uint nSegments) {
uint seg = (uint)get_global_id(0);
if (seg < nSegments) mh_cache_segment(cache, seg);
}
__kernel void igneum_build(__global uint* ds, __global const uint* cache, uint nItems) {
uint t = (uint)get_global_id(0);
if (t < nItems) {
uint s[16];
mh_item(cache, t, s);
__global uint* d = ds + ((ulong)t * 16u);
for (uint i = 0u; i < 16u; ++i) d[i] = s[i];
}
}
// One hash per work-item. IGNEUM_GROUP is a multiple of 32; lane = lid & 31 and every exchange stays inside the
// lane's own aligned run of 32 work-items, exactly like simd_shuffle_xor inside a 32-wide Metal SIMD group and
// __shfl_xor_sync inside a CUDA warp. Control flow is uniform (no branches at all).
IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask) {
uint gid = (uint)get_global_id(0);
uint lid = (uint)get_local_id(0);
uint nonce = baseNonce + gid;
uint r0, r1, r2, r3, r4, r5, r6, r7;
#if IGNEUM_EXCHANGE == 0
IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP);
uint xk = 0u;
#else
(void)lid;
#endif
{ uint x = nonce ^ 0x3e345412u; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0xfc2b3a0eu; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1]
{ uint x = nonce ^ 0xfc2b3a0eu; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0x7740353du; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2]
{ uint x = nonce ^ 0x7740353du; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x2df159dbu; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3]
{ uint x = nonce ^ 0x2df159dbu; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0x50bc6eedu; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4]
{ uint x = nonce ^ 0x50bc6eedu; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0x2e61acb2u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5]
{ uint x = nonce ^ 0x2e61acb2u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0x3c800bffu; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6]
{ uint x = nonce ^ 0x3c800bffu; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0xbaecd8c1u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7]
{ uint x = nonce ^ 0xbaecd8c1u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x3e345412u; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0]
for (uint it = 0u; it < 8u; ++it) {
uint sel = r0;
r5 = r4 * r0 + r5; // 0 mad
r1 = rotl_imm(r1, 23u); // 1 rotl
{ uint b_ = (r5 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r0 = x_; } // 2 load
r5 = r4 * r2 + r5; // 3 mad
r1 = r1 * r6; // 4 mul
r1 = r1 + r0 + ((((sel >> 6u) & 1u) != 0u) ? 0x9dd9fb05u : 0x9db46598u); // 5 add
{ uint t_; IGNEUM_SHFL_XOR(t_, r7, 16u); r1 = r1 ^ t_; } // 6 shfl
r6 = r6 * r1; // 7 mul
r1 = mul_hi(r1, r7); // 8 mulhi
r2 = mul_hi(r2, r1); // 9 mulhi
r0 = r0 + r4 + ((((sel >> 13u) & 1u) != 0u) ? 0xc15822b6u : 0xe45fccebu); // 10 add
r5 = r1 * r2 + r5; // 11 mad
r4 = r4 * r7; // 12 mul
{ uint b_ = (r5 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r0 = x_; } // 13 load
r3 = r3 ^ r4; // 14 xor
r1 = r1 ^ r6; // 15 xor
r5 = rotr_var(r5, r6); // 16 rotr
r3 = rotr_var(r3, r7); // 17 rotr
{ uint t_; IGNEUM_SHFL_XOR(t_, r4, 16u); r1 = r1 ^ t_; } // 18 shfl
r5 = r5 ^ ds[r6 & mask]; // 19 load
r6 = rotl_imm(r6, 30u); // 20 rotl
{ uint b_ = (r2 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r5 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r5 = x_; } // 21 load
r3 = r3 ^ r5; // 22 xor
r5 = r5 + r7 + ((((sel >> 6u) & 1u) != 0u) ? 0x950603c6u : 0x1d4f8db7u); // 23 add
{ uint b_ = (r4 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r3 = x_; } // 24 load
{ uint b_ = (r1 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r0 = x_; } // 25 load
r2 = r2 + r3 + ((((sel >> 31u) & 1u) != 0u) ? 0x0ae476a4u : 0x55db0d92u); // 26 add
r5 = r5 ^ ds[r6 & mask]; // 27 load
{ uint b_ = (r5 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r2 = x_; } // 28 load
r1 = r1 ^ r6; // 29 xor
r7 = mul_hi(r7, r4); // 30 mulhi
r6 = rotr_var(r6, r3); // 31 rotr
r0 = r0 ^ ds[r3 & mask]; // 32 load
r5 = rotr_var(r5, r0); // 33 rotr
r5 = rotl_imm(r5, 20u); // 34 rotl
r0 = mul_hi(r0, r4); // 35 mulhi
r6 = r6 + r7 + ((((sel >> 18u) & 1u) != 0u) ? 0xfe7a0454u : 0x5d6e3dc5u); // 36 add
{ uint b_ = (r1 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r2 = x_; } // 37 load
r2 = rotl_imm(r2, 19u); // 38 rotl
r3 = r3 + r6 + ((((sel >> 20u) & 1u) != 0u) ? 0xc1d4ae24u : 0xe7e241bau); // 39 add
{ uint b_ = (r7 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r4 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r4 = x_; } // 40 load
r3 = r3 + r1 + ((((sel >> 21u) & 1u) != 0u) ? 0x8f9f8556u : 0xb45cdd60u); // 41 add
{ uint t_; IGNEUM_SHFL_XOR(t_, r2, 4u); r5 = r5 ^ t_; } // 42 shfl
r0 = r0 * r1; // 43 mul
r0 = rotr_var(r0, r7); // 44 rotr
r5 = r5 - r3; // 45 sub
r2 = r7 * r7 + r2; // 46 mad
r6 = r3 * r1 + r6; // 47 mad
r0 = r0 * r7; // 48 mul
r0 = r0 + r4 + ((((sel >> 1u) & 1u) != 0u) ? 0xb9083b6cu : 0x3fa0c5cdu); // 49 add
r4 = r4 + r6 + ((((sel >> 13u) & 1u) != 0u) ? 0x9eecc778u : 0x2d6873e8u); // 50 add
r2 = r2 - r7; // 51 sub
r6 = rotl_imm(r6, 6u); // 52 rotl
r3 = rotr_var(r3, r5); // 53 rotr
{ uint b_ = (r2 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r3 = x_; } // 54 load
{ uint b_ = (r5 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r7 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r7 = x_; } // 55 load
r2 = r2 | r6; // 56 or
r4 = r4 ^ ds[r0 & mask]; // 57 load
r3 = r3 ^ r6; // 58 xor
r0 = r0 ^ ds[r6 & mask]; // 59 load
r2 = r2 + r4 + ((((sel >> 3u) & 1u) != 0u) ? 0x19c76fb3u : 0x5052a1c3u); // 60 add
r7 = r7 ^ ds[r4 & mask]; // 61 load
r6 = r6 | r1; // 62 or
r5 = r5 + r1 + ((((sel >> 4u) & 1u) != 0u) ? 0x535fe3fau : 0x08c757c0u); // 63 add
}
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;
}
#if IGNEUM_EXCHANGE != 0
// Reports the sub-group size this device uses for a work-group of IGNEUM_GROUP items. host.c runs it only when the
// per-kernel query (clGetKernelSubGroupInfoKHR on igneum_hash) is unavailable; that query is preferred because a
// compiler may pick a different wave width per kernel (RDNA: wave32 or wave64). See WAVEFRONT.md.
IGNEUM_KERNEL_HASH void igneum_probe_subgroup(__global uint* out) {
if (get_local_id(0) == 0u) { out[0] = get_sub_group_size(); out[1] = get_num_sub_groups(); }
}
#endif

View file

@ -0,0 +1,164 @@
// Generated by igneum-pow export (generator v2) for seed "igneum-readwidth/B/1". 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"
#include "memhard.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;
}
// Memory-hard dataset (MEMHARD.md). One thread per cache segment; one thread per 64-byte dataset item.
// The core functions (mh_cache_segment, mh_item) are in memhard.h and are also compiled for the host.
__global__ void igneum_cache_fill(uint32_t* cache, uint32_t nSegments) {
uint32_t seg = blockIdx.x * blockDim.x + threadIdx.x;
if (seg < nSegments) mh_cache_segment(cache, seg);
}
__global__ void igneum_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems) {
uint32_t t = blockIdx.x * blockDim.x + threadIdx.x;
if (t < nItems) {
uint32_t s[16];
mh_item(cache, t, s);
uint32_t* d = ds + (size_t)t * 16u;
for (uint32_t i = 0u; i < 16u; ++i) d[i] = s[i];
}
}
// 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 ^ 0x3e345412u; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0xfc2b3a0eu; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1]
{ uint32_t x = nonce ^ 0xfc2b3a0eu; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0x7740353du; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2]
{ uint32_t x = nonce ^ 0x7740353du; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x2df159dbu; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3]
{ uint32_t x = nonce ^ 0x2df159dbu; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0x50bc6eedu; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4]
{ uint32_t x = nonce ^ 0x50bc6eedu; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0x2e61acb2u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5]
{ uint32_t x = nonce ^ 0x2e61acb2u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0x3c800bffu; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6]
{ uint32_t x = nonce ^ 0x3c800bffu; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0xbaecd8c1u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7]
{ uint32_t x = nonce ^ 0xbaecd8c1u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x3e345412u; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0]
for (uint32_t it = 0u; it < 8u; ++it) {
uint32_t sel = r0;
r5 = r4 * r0 + r5; // 0 mad
r1 = rotl_imm(r1, 23u); // 1 rotl
{ uint32_t b_ = (r5 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r0 = x_; } // 2 load
r5 = r4 * r2 + r5; // 3 mad
r1 = r1 * r6; // 4 mul
r1 = r1 + r0 + ((((sel >> 6u) & 1u) != 0u) ? 0x9dd9fb05u : 0x9db46598u); // 5 add
r1 = r1 ^ __shfl_xor_sync(0xffffffffu, r7, 16); // 6 shfl
r6 = r6 * r1; // 7 mul
r1 = __umulhi(r1, r7); // 8 mulhi
r2 = __umulhi(r2, r1); // 9 mulhi
r0 = r0 + r4 + ((((sel >> 13u) & 1u) != 0u) ? 0xc15822b6u : 0xe45fccebu); // 10 add
r5 = r1 * r2 + r5; // 11 mad
r4 = r4 * r7; // 12 mul
{ uint32_t b_ = (r5 & mask) & ~15u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint32_t x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r0 = x_; } // 13 load
r3 = r3 ^ r4; // 14 xor
r1 = r1 ^ r6; // 15 xor
r5 = rotr_var(r5, r6); // 16 rotr
r3 = rotr_var(r3, r7); // 17 rotr
r1 = r1 ^ __shfl_xor_sync(0xffffffffu, r4, 16); // 18 shfl
r5 = r5 ^ ds[r6 & mask]; // 19 load
r6 = rotl_imm(r6, 30u); // 20 rotl
{ uint32_t b_ = (r2 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r5 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r5 = x_; } // 21 load
r3 = r3 ^ r5; // 22 xor
r5 = r5 + r7 + ((((sel >> 6u) & 1u) != 0u) ? 0x950603c6u : 0x1d4f8db7u); // 23 add
{ uint32_t b_ = (r4 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r3 = x_; } // 24 load
{ uint32_t b_ = (r1 & mask) & ~15u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint32_t x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r0 = x_; } // 25 load
r2 = r2 + r3 + ((((sel >> 31u) & 1u) != 0u) ? 0x0ae476a4u : 0x55db0d92u); // 26 add
r5 = r5 ^ ds[r6 & mask]; // 27 load
{ uint32_t b_ = (r5 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r2 = x_; } // 28 load
r1 = r1 ^ r6; // 29 xor
r7 = __umulhi(r7, r4); // 30 mulhi
r6 = rotr_var(r6, r3); // 31 rotr
r0 = r0 ^ ds[r3 & mask]; // 32 load
r5 = rotr_var(r5, r0); // 33 rotr
r5 = rotl_imm(r5, 20u); // 34 rotl
r0 = __umulhi(r0, r4); // 35 mulhi
r6 = r6 + r7 + ((((sel >> 18u) & 1u) != 0u) ? 0xfe7a0454u : 0x5d6e3dc5u); // 36 add
{ uint32_t b_ = (r1 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r2 = x_; } // 37 load
r2 = rotl_imm(r2, 19u); // 38 rotl
r3 = r3 + r6 + ((((sel >> 20u) & 1u) != 0u) ? 0xc1d4ae24u : 0xe7e241bau); // 39 add
{ uint32_t b_ = (r7 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r4 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r4 = x_; } // 40 load
r3 = r3 + r1 + ((((sel >> 21u) & 1u) != 0u) ? 0x8f9f8556u : 0xb45cdd60u); // 41 add
r5 = r5 ^ __shfl_xor_sync(0xffffffffu, r2, 4); // 42 shfl
r0 = r0 * r1; // 43 mul
r0 = rotr_var(r0, r7); // 44 rotr
r5 = r5 - r3; // 45 sub
r2 = r7 * r7 + r2; // 46 mad
r6 = r3 * r1 + r6; // 47 mad
r0 = r0 * r7; // 48 mul
r0 = r0 + r4 + ((((sel >> 1u) & 1u) != 0u) ? 0xb9083b6cu : 0x3fa0c5cdu); // 49 add
r4 = r4 + r6 + ((((sel >> 13u) & 1u) != 0u) ? 0x9eecc778u : 0x2d6873e8u); // 50 add
r2 = r2 - r7; // 51 sub
r6 = rotl_imm(r6, 6u); // 52 rotl
r3 = rotr_var(r3, r5); // 53 rotr
{ uint32_t b_ = (r2 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r3 = x_; } // 54 load
{ uint32_t b_ = (r5 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r7 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r7 = x_; } // 55 load
r2 = r2 | r6; // 56 or
r4 = r4 ^ ds[r0 & mask]; // 57 load
r3 = r3 ^ r6; // 58 xor
r0 = r0 ^ ds[r6 & mask]; // 59 load
r2 = r2 + r4 + ((((sel >> 3u) & 1u) != 0u) ? 0x19c76fb3u : 0x5052a1c3u); // 60 add
r7 = r7 ^ ds[r4 & mask]; // 61 load
r6 = r6 | r1; // 62 or
r5 = r5 + r1 + ((((sel >> 4u) & 1u) != 0u) ? 0x535fe3fau : 0x08c757c0u); // 63 add
}
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_cache_fill(uint32_t* cache, uint32_t nSegments) {
if (nSegments == 0u) return cudaErrorInvalidValue;
uint32_t block = 256u;
uint32_t grid = (nSegments + block - 1u) / block;
igneum_cache_fill<<<grid, block>>>(cache, nSegments);
return cudaGetLastError();
}
cudaError_t igneum_launch_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems) {
if (nItems == 0u) return cudaErrorInvalidValue;
uint32_t block = 256u;
uint32_t grid = (nItems + block - 1u) / block;
igneum_build<<<grid, block>>>(ds, cache, nItems);
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);
}

View file

@ -0,0 +1,372 @@
// Generated by igneum-pow export (generator v2) for seed "igneum-readwidth/B/1". Do not edit by hand.
// OpenCL C twin of the Metal kernel for the same seed (see proto-opencl/README.md, WAVEFRONT.md and program.metal).
// Built from source at runtime by proto-opencl/host.c, which passes these defines:
// IGNEUM_GROUP work-group size of igneum_hash, a multiple of 32 (default 32: one work-group = one 32-lane unit)
// IGNEUM_EXCHANGE 0 = local-memory exchange with a barrier (any device, any wave width; the default)
// 1 = sub_group_shuffle_xor (cl_khr_subgroup_shuffle), only with IGNEUM_GROUP 32 and a sub-group size of exactly 32
// 2 = intel_sub_group_shuffle_xor (cl_intel_subgroups), same condition
// The verification unit is always 32 lanes. A 64-wide hardware wave (AMD GCN/CDNA, RDNA in wave64) runs two units;
// the exchange masks are 1, 2, 4, 8, 16, so every partner lane lies inside the lane's own aligned run of 32.
#ifndef IGNEUM_GROUP
#define IGNEUM_GROUP 32
#endif
#ifndef IGNEUM_EXCHANGE
#define IGNEUM_EXCHANGE 0
#endif
#ifdef __OPENCL_VERSION__
#define IGNEUM_KERNEL_HASH __kernel __attribute__((reqd_work_group_size(IGNEUM_GROUP, 1, 1)))
#define IGNEUM_LOCAL_WORDS(name, n) __local uint name[n]
#if IGNEUM_EXCHANGE == 1
#ifdef cl_khr_subgroups
#pragma OPENCL EXTENSION cl_khr_subgroups : enable
#endif
#ifdef cl_khr_subgroup_shuffle
#pragma OPENCL EXTENSION cl_khr_subgroup_shuffle : enable
#endif
#elif IGNEUM_EXCHANGE == 2
#pragma OPENCL EXTENSION cl_intel_subgroups : enable
#endif
#else
// Not an OpenCL compiler: proto-opencl/emu compiles this file as C++ and supplies the built-ins and these two macros.
#include "emu_opencl.h"
#endif
#if IGNEUM_EXCHANGE == 1
#define IGNEUM_SHFL_XOR(dst, a, m) dst = sub_group_shuffle_xor((a), (uint)(m))
#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u)
#elif IGNEUM_EXCHANGE == 2
#define IGNEUM_SHFL_XOR(dst, a, m) dst = intel_sub_group_shuffle_xor((a), (uint)(m))
#define IGNEUM_BCAST0(dst, a) dst = sub_group_broadcast((a), 0u)
#else
// Local-memory exchange. Two buffers of IGNEUM_GROUP words alternate (xk counts exchanges), so one barrier per
// exchange is enough: a lane can only overwrite buffer b at exchange k+2 after passing barrier k+1, and every lane
// reaches barrier k+1 only after its read of buffer b at exchange k. The partner lid ^ m stays inside the lane's
// aligned run of 32 because m < 32. Control flow is uniform, so every work-item reaches every barrier.
#define IGNEUM_SHFL_XOR(dst, a, m) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid ^ (uint)(m))]; xk += 1u; }
#define IGNEUM_BCAST0(dst, a) { xch[(xk & 1u) * IGNEUM_GROUP + lid] = (a); barrier(CLK_LOCAL_MEM_FENCE); dst = xch[(xk & 1u) * IGNEUM_GROUP + (lid & ~31u)]; xk += 1u; }
#endif
static inline uint splitmix32(uint 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. OpenCL rotate() rotates left by n modulo 32.
static inline uint rotl_imm(uint x, uint n) { return rotate(x, n); }
// Right rotation by n modulo 32 as a left rotation by (32 - n) modulo 32; n == 0 gives x.
static inline uint rotr_var(uint x, uint n) { return rotate(x, (0u - n) & 31u); }
static 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;
}
// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines.
// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals.
#define MH_CACHE_LINE_MASK 0x003fffffu
#define MH_SEGMENT_LINES 64u
#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); }
static inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site
// y = ChaCha12 core(x) + x
static inline void mh_chacha_block(const uint* x, uint* y) {
for (uint i = 0u; i < 16u; ++i) y[i] = x[i];
for (uint r = 0u; r < 6u; ++r) {
MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u)
MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u)
}
for (uint i = 0u; i < 16u; ++i) y[i] += x[i];
}
// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0.
static inline void mh_cache_segment(__global uint* cache, uint seg) {
uint prev[16]; uint x[16]; uint y[16];
for (uint i = 0u; i < 16u; ++i) prev[i] = 0u;
for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) {
x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3];
x[4] = 0x3067619fu ^ prev[4];
x[5] = 0x3c269176u ^ prev[5];
x[6] = 0x84a03b03u ^ prev[6];
x[7] = 0xf8c63294u ^ prev[7];
x[8] = 0xff977c5bu ^ prev[8];
x[9] = 0xe60def3eu ^ prev[9];
x[10] = 0x63630141u ^ prev[10];
x[11] = 0xb8fbcb58u ^ prev[11];
x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15];
mh_chacha_block(x, y);
__global uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u);
for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; }
}
}
// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations.
static inline void mh_mixer(uint* s, uint rk) {
s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u;
s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu;
s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u;
s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu;
s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u;
s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u;
s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u;
s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u;
s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du;
s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u;
s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du;
s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu;
s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du;
s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu;
s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u;
s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u;
MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u)
MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u)
MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u)
MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u)
}
// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer.
static inline void mh_item(__global const uint* cache, uint t, uint* s) {
s[0] = 0x3067619fu;
s[1] = 0x3c269176u;
s[2] = 0x84a03b03u;
s[3] = 0xf8c63294u;
s[4] = 0xff977c5bu;
s[5] = 0xe60def3eu;
s[6] = 0x63630141u;
s[7] = 0xb8fbcb58u;
s[8] = t * 0x42146205u + 0xbab68293u;
s[9] = t * 0x52cbe0fbu + 0xcc162340u;
s[10] = t * 0x7ecf4a03u + 0x6ce151ccu;
s[11] = t * 0x6728907fu + 0xe62b8997u;
s[12] = t * 0xd81d9751u + 0xc9c80297u;
s[13] = t * 0x132952c3u + 0xf74a1654u;
s[14] = t * 0xf60de277u + 0x3d704af5u;
s[15] = t * 0x05358035u + 0x3cf522b7u;
for (uint r = 0u; r < 8u; ++r) {
mh_mixer(s, 0x9E3779B9u * (r + 1u));
__global const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u);
for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i];
}
mh_mixer(s, 0x9E3779B9u * 9u);
}
// dataset[w] without the dataset: derive item w >> 4 and take word w & 15.
static inline uint mh_word(__global const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; }
// Memory-hard dataset (MEMHARD.md). One work-item per cache segment; one work-item per 64-byte dataset item.
// The same constants as memhard.h in this pack (one emitter, three dialects).
__kernel void igneum_cache_fill(__global uint* cache, uint nSegments) {
uint seg = (uint)get_global_id(0);
if (seg < nSegments) mh_cache_segment(cache, seg);
}
__kernel void igneum_build(__global uint* ds, __global const uint* cache, uint nItems) {
uint t = (uint)get_global_id(0);
if (t < nItems) {
uint s[16];
mh_item(cache, t, s);
__global uint* d = ds + ((ulong)t * 16u);
for (uint i = 0u; i < 16u; ++i) d[i] = s[i];
}
}
// One hash per work-item. IGNEUM_GROUP is a multiple of 32; lane = lid & 31 and every exchange stays inside the
// lane's own aligned run of 32 work-items, exactly like simd_shuffle_xor inside a 32-wide Metal SIMD group and
// __shfl_xor_sync inside a CUDA warp. Control flow is uniform (no branches at all).
IGNEUM_KERNEL_HASH void igneum_hash(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask) {
uint gid = (uint)get_global_id(0);
uint lid = (uint)get_local_id(0);
uint nonce = baseNonce + gid;
uint r0, r1, r2, r3, r4, r5, r6, r7;
#if IGNEUM_EXCHANGE == 0
IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP);
uint xk = 0u;
#else
(void)lid;
#endif
{ uint x = nonce ^ 0x3e345412u; x += 0x9e3779b9u; x = splitmix32(x); r0 = x ^ 0xfc2b3a0eu; } // SEEDW[0], 0x9e3779b9u * 1u, SEEDW[1]
{ uint x = nonce ^ 0xfc2b3a0eu; x += 0x3c6ef372u; x = splitmix32(x); r1 = x ^ 0x7740353du; } // SEEDW[1], 0x9e3779b9u * 2u, SEEDW[2]
{ uint x = nonce ^ 0x7740353du; x += 0xdaa66d2bu; x = splitmix32(x); r2 = x ^ 0x2df159dbu; } // SEEDW[2], 0x9e3779b9u * 3u, SEEDW[3]
{ uint x = nonce ^ 0x2df159dbu; x += 0x78dde6e4u; x = splitmix32(x); r3 = x ^ 0x50bc6eedu; } // SEEDW[3], 0x9e3779b9u * 4u, SEEDW[4]
{ uint x = nonce ^ 0x50bc6eedu; x += 0x1715609du; x = splitmix32(x); r4 = x ^ 0x2e61acb2u; } // SEEDW[4], 0x9e3779b9u * 5u, SEEDW[5]
{ uint x = nonce ^ 0x2e61acb2u; x += 0xb54cda56u; x = splitmix32(x); r5 = x ^ 0x3c800bffu; } // SEEDW[5], 0x9e3779b9u * 6u, SEEDW[6]
{ uint x = nonce ^ 0x3c800bffu; x += 0x5384540fu; x = splitmix32(x); r6 = x ^ 0xbaecd8c1u; } // SEEDW[6], 0x9e3779b9u * 7u, SEEDW[7]
{ uint x = nonce ^ 0xbaecd8c1u; x += 0xf1bbcdc8u; x = splitmix32(x); r7 = x ^ 0x3e345412u; } // SEEDW[7], 0x9e3779b9u * 8u, SEEDW[0]
for (uint it = 0u; it < 8u; ++it) {
uint sel = r0;
r5 = r4 * r0 + r5; // 0 mad
r1 = rotl_imm(r1, 23u); // 1 rotl
{ uint b_ = (r5 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r0 = x_; } // 2 load
r5 = r4 * r2 + r5; // 3 mad
r1 = r1 * r6; // 4 mul
r1 = r1 + r0 + ((((sel >> 6u) & 1u) != 0u) ? 0x9dd9fb05u : 0x9db46598u); // 5 add
{ uint t_; IGNEUM_SHFL_XOR(t_, r7, 16u); r1 = r1 ^ t_; } // 6 shfl
r6 = r6 * r1; // 7 mul
r1 = mul_hi(r1, r7); // 8 mulhi
r2 = mul_hi(r2, r1); // 9 mulhi
r0 = r0 + r4 + ((((sel >> 13u) & 1u) != 0u) ? 0xc15822b6u : 0xe45fccebu); // 10 add
r5 = r1 * r2 + r5; // 11 mad
r4 = r4 * r7; // 12 mul
{ uint b_ = (r5 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r0 = x_; } // 13 load
r3 = r3 ^ r4; // 14 xor
r1 = r1 ^ r6; // 15 xor
r5 = rotr_var(r5, r6); // 16 rotr
r3 = rotr_var(r3, r7); // 17 rotr
{ uint t_; IGNEUM_SHFL_XOR(t_, r4, 16u); r1 = r1 ^ t_; } // 18 shfl
r5 = r5 ^ ds[r6 & mask]; // 19 load
r6 = rotl_imm(r6, 30u); // 20 rotl
{ uint b_ = (r2 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r5 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r5 = x_; } // 21 load
r3 = r3 ^ r5; // 22 xor
r5 = r5 + r7 + ((((sel >> 6u) & 1u) != 0u) ? 0x950603c6u : 0x1d4f8db7u); // 23 add
{ uint b_ = (r4 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r3 = x_; } // 24 load
{ uint b_ = (r1 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r0 = x_; } // 25 load
r2 = r2 + r3 + ((((sel >> 31u) & 1u) != 0u) ? 0x0ae476a4u : 0x55db0d92u); // 26 add
r5 = r5 ^ ds[r6 & mask]; // 27 load
{ uint b_ = (r5 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r2 = x_; } // 28 load
r1 = r1 ^ r6; // 29 xor
r7 = mul_hi(r7, r4); // 30 mulhi
r6 = rotr_var(r6, r3); // 31 rotr
r0 = r0 ^ ds[r3 & mask]; // 32 load
r5 = rotr_var(r5, r0); // 33 rotr
r5 = rotl_imm(r5, 20u); // 34 rotl
r0 = mul_hi(r0, r4); // 35 mulhi
r6 = r6 + r7 + ((((sel >> 18u) & 1u) != 0u) ? 0xfe7a0454u : 0x5d6e3dc5u); // 36 add
{ uint b_ = (r1 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r2 = x_; } // 37 load
r2 = rotl_imm(r2, 19u); // 38 rotl
r3 = r3 + r6 + ((((sel >> 20u) & 1u) != 0u) ? 0xc1d4ae24u : 0xe7e241bau); // 39 add
{ uint b_ = (r7 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r4 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r4 = x_; } // 40 load
r3 = r3 + r1 + ((((sel >> 21u) & 1u) != 0u) ? 0x8f9f8556u : 0xb45cdd60u); // 41 add
{ uint t_; IGNEUM_SHFL_XOR(t_, r2, 4u); r5 = r5 ^ t_; } // 42 shfl
r0 = r0 * r1; // 43 mul
r0 = rotr_var(r0, r7); // 44 rotr
r5 = r5 - r3; // 45 sub
r2 = r7 * r7 + r2; // 46 mad
r6 = r3 * r1 + r6; // 47 mad
r0 = r0 * r7; // 48 mul
r0 = r0 + r4 + ((((sel >> 1u) & 1u) != 0u) ? 0xb9083b6cu : 0x3fa0c5cdu); // 49 add
r4 = r4 + r6 + ((((sel >> 13u) & 1u) != 0u) ? 0x9eecc778u : 0x2d6873e8u); // 50 add
r2 = r2 - r7; // 51 sub
r6 = rotl_imm(r6, 6u); // 52 rotl
r3 = rotr_var(r3, r5); // 53 rotr
{ uint b_ = (r2 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r3 = x_; } // 54 load
{ uint b_ = (r5 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r7 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r7 = x_; } // 55 load
r2 = r2 | r6; // 56 or
r4 = r4 ^ ds[r0 & mask]; // 57 load
r3 = r3 ^ r6; // 58 xor
r0 = r0 ^ ds[r6 & mask]; // 59 load
r2 = r2 + r4 + ((((sel >> 3u) & 1u) != 0u) ? 0x19c76fb3u : 0x5052a1c3u); // 60 add
r7 = r7 ^ ds[r4 & mask]; // 61 load
r6 = r6 | r1; // 62 or
r5 = r5 + r1 + ((((sel >> 4u) & 1u) != 0u) ? 0x535fe3fau : 0x08c757c0u); // 63 add
}
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;
}
#if IGNEUM_EXCHANGE != 0
// Reports the sub-group size this device uses for a work-group of IGNEUM_GROUP items. host.c runs it only when the
// per-kernel query (clGetKernelSubGroupInfoKHR on igneum_hash) is unavailable; that query is preferred because a
// compiler may pick a different wave width per kernel (RDNA: wave32 or wave64). See WAVEFRONT.md.
IGNEUM_KERNEL_HASH void igneum_probe_subgroup(__global uint* out) {
if (get_local_id(0) == 0u) { out[0] = get_sub_group_size(); out[1] = get_num_sub_groups(); }
}
#endif
// Header-bound variant (bind.rs): the init words come from initw, not SEEDW. Same body as igneum_hash.
IGNEUM_KERNEL_HASH void igneum_hash_bound(__global const uint* ds, __global ulong* out, uint baseNonce, uint mask, __global const uint* initw) {
uint gid = (uint)get_global_id(0);
uint lid = (uint)get_local_id(0);
uint nonce = baseNonce + gid;
uint r0, r1, r2, r3, r4, r5, r6, r7;
uint iw0 = initw[0], iw1 = initw[1], iw2 = initw[2], iw3 = initw[3], iw4 = initw[4], iw5 = initw[5], iw6 = initw[6], iw7 = initw[7];
#if IGNEUM_EXCHANGE == 0
IGNEUM_LOCAL_WORDS(xch, 2 * IGNEUM_GROUP);
uint xk = 0u;
#else
(void)lid;
#endif
{ uint x = nonce ^ iw0; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ iw1; }
{ uint x = nonce ^ iw1; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ iw2; }
{ uint x = nonce ^ iw2; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ iw3; }
{ uint x = nonce ^ iw3; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ iw4; }
{ uint x = nonce ^ iw4; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ iw5; }
{ uint x = nonce ^ iw5; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ iw6; }
{ uint x = nonce ^ iw6; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ iw7; }
{ uint x = nonce ^ iw7; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ iw0; }
for (uint it = 0u; it < 8u; ++it) {
uint sel = r0;
r5 = r4 * r0 + r5; // 0 mad
r1 = rotl_imm(r1, 23u); // 1 rotl
{ uint b_ = (r5 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r0 = x_; } // 2 load
r5 = r4 * r2 + r5; // 3 mad
r1 = r1 * r6; // 4 mul
r1 = r1 + r0 + ((((sel >> 6u) & 1u) != 0u) ? 0x9dd9fb05u : 0x9db46598u); // 5 add
{ uint t_; IGNEUM_SHFL_XOR(t_, r7, 16u); r1 = r1 ^ t_; } // 6 shfl
r6 = r6 * r1; // 7 mul
r1 = mul_hi(r1, r7); // 8 mulhi
r2 = mul_hi(r2, r1); // 9 mulhi
r0 = r0 + r4 + ((((sel >> 13u) & 1u) != 0u) ? 0xc15822b6u : 0xe45fccebu); // 10 add
r5 = r1 * r2 + r5; // 11 mad
r4 = r4 * r7; // 12 mul
{ uint b_ = (r5 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r0 = x_; } // 13 load
r3 = r3 ^ r4; // 14 xor
r1 = r1 ^ r6; // 15 xor
r5 = rotr_var(r5, r6); // 16 rotr
r3 = rotr_var(r3, r7); // 17 rotr
{ uint t_; IGNEUM_SHFL_XOR(t_, r4, 16u); r1 = r1 ^ t_; } // 18 shfl
r5 = r5 ^ ds[r6 & mask]; // 19 load
r6 = rotl_imm(r6, 30u); // 20 rotl
{ uint b_ = (r2 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r5 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r5 = x_; } // 21 load
r3 = r3 ^ r5; // 22 xor
r5 = r5 + r7 + ((((sel >> 6u) & 1u) != 0u) ? 0x950603c6u : 0x1d4f8db7u); // 23 add
{ uint b_ = (r4 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r3 = x_; } // 24 load
{ uint b_ = (r1 & mask) & ~15u; uint4 v0_ = vload4(0u, ds + b_); uint4 v1_ = vload4(1u, ds + b_); uint4 v2_ = vload4(2u, ds + b_); uint4 v3_ = vload4(3u, ds + b_); uint x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r0 = x_; } // 25 load
r2 = r2 + r3 + ((((sel >> 31u) & 1u) != 0u) ? 0x0ae476a4u : 0x55db0d92u); // 26 add
r5 = r5 ^ ds[r6 & mask]; // 27 load
{ uint b_ = (r5 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r2 = x_; } // 28 load
r1 = r1 ^ r6; // 29 xor
r7 = mul_hi(r7, r4); // 30 mulhi
r6 = rotr_var(r6, r3); // 31 rotr
r0 = r0 ^ ds[r3 & mask]; // 32 load
r5 = rotr_var(r5, r0); // 33 rotr
r5 = rotl_imm(r5, 20u); // 34 rotl
r0 = mul_hi(r0, r4); // 35 mulhi
r6 = r6 + r7 + ((((sel >> 18u) & 1u) != 0u) ? 0xfe7a0454u : 0x5d6e3dc5u); // 36 add
{ uint b_ = (r1 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r2 = x_; } // 37 load
r2 = rotl_imm(r2, 19u); // 38 rotl
r3 = r3 + r6 + ((((sel >> 20u) & 1u) != 0u) ? 0xc1d4ae24u : 0xe7e241bau); // 39 add
{ uint b_ = (r7 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r4 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r4 = x_; } // 40 load
r3 = r3 + r1 + ((((sel >> 21u) & 1u) != 0u) ? 0x8f9f8556u : 0xb45cdd60u); // 41 add
{ uint t_; IGNEUM_SHFL_XOR(t_, r2, 4u); r5 = r5 ^ t_; } // 42 shfl
r0 = r0 * r1; // 43 mul
r0 = rotr_var(r0, r7); // 44 rotr
r5 = r5 - r3; // 45 sub
r2 = r7 * r7 + r2; // 46 mad
r6 = r3 * r1 + r6; // 47 mad
r0 = r0 * r7; // 48 mul
r0 = r0 + r4 + ((((sel >> 1u) & 1u) != 0u) ? 0xb9083b6cu : 0x3fa0c5cdu); // 49 add
r4 = r4 + r6 + ((((sel >> 13u) & 1u) != 0u) ? 0x9eecc778u : 0x2d6873e8u); // 50 add
r2 = r2 - r7; // 51 sub
r6 = rotl_imm(r6, 6u); // 52 rotl
r3 = rotr_var(r3, r5); // 53 rotr
{ uint b_ = (r2 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r3 = x_; } // 54 load
{ uint b_ = (r5 & mask) & ~3u; uint4 v0_ = vload4(0u, ds + b_); uint x_ = r7 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r7 = x_; } // 55 load
r2 = r2 | r6; // 56 or
r4 = r4 ^ ds[r0 & mask]; // 57 load
r3 = r3 ^ r6; // 58 xor
r0 = r0 ^ ds[r6 & mask]; // 59 load
r2 = r2 + r4 + ((((sel >> 3u) & 1u) != 0u) ? 0x19c76fb3u : 0x5052a1c3u); // 60 add
r7 = r7 ^ ds[r4 & mask]; // 61 load
r6 = r6 | r1; // 62 or
r5 = r5 + r1 + ((((sel >> 4u) & 1u) != 0u) ? 0x535fe3fau : 0x08c757c0u); // 63 add
}
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;
}

View file

@ -0,0 +1,123 @@
// Generated by igneum-pow export (generator v2) for seed "igneum-readwidth/B/1". Do not edit by hand.
// Header-bound twin of igneum_hash in kernel.cu: the init words come from a kernel argument, not SEEDW.
// Host declarations (also in program_bound.h if present):
// struct IgneumInitWords { uint32_t w[8]; };
// cudaError_t igneum_launch_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask,
// IgneumInitWords iw, uint32_t nonces, uint32_t blockWarps);
// cudaError_t igneum_hash_bound_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps);
#include <cuda_runtime.h>
#include <cstdint>
#include "program.h"
struct IgneumInitWords { uint32_t w[8]; };
__device__ __forceinline__ uint32_t splitmix32(uint32_t x) {
x ^= x >> 16; x *= 0x7feb352du;
x ^= x >> 15; x *= 0x846ca68bu;
x ^= x >> 16;
return x;
}
__device__ __forceinline__ uint32_t rotl_imm(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); }
__device__ __forceinline__ uint32_t rotr_var(uint32_t x, uint32_t n) { n &= 31u; return (x >> n) | (x << ((32u - n) & 31u)); }
__global__ void igneum_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask, IgneumInitWords iw) {
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 ^ iw.w[0]; x += 0x9e3779b9u * 1u; x = splitmix32(x); r0 = x ^ iw.w[1]; }
{ uint32_t x = nonce ^ iw.w[1]; x += 0x9e3779b9u * 2u; x = splitmix32(x); r1 = x ^ iw.w[2]; }
{ uint32_t x = nonce ^ iw.w[2]; x += 0x9e3779b9u * 3u; x = splitmix32(x); r2 = x ^ iw.w[3]; }
{ uint32_t x = nonce ^ iw.w[3]; x += 0x9e3779b9u * 4u; x = splitmix32(x); r3 = x ^ iw.w[4]; }
{ uint32_t x = nonce ^ iw.w[4]; x += 0x9e3779b9u * 5u; x = splitmix32(x); r4 = x ^ iw.w[5]; }
{ uint32_t x = nonce ^ iw.w[5]; x += 0x9e3779b9u * 6u; x = splitmix32(x); r5 = x ^ iw.w[6]; }
{ uint32_t x = nonce ^ iw.w[6]; x += 0x9e3779b9u * 7u; x = splitmix32(x); r6 = x ^ iw.w[7]; }
{ uint32_t x = nonce ^ iw.w[7]; x += 0x9e3779b9u * 8u; x = splitmix32(x); r7 = x ^ iw.w[0]; }
for (uint32_t it = 0u; it < 8u; ++it) {
uint32_t sel = r0;
r5 = r4 * r0 + r5; // 0 mad
r1 = rotl_imm(r1, 23u); // 1 rotl
{ uint32_t b_ = (r5 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r0 = x_; } // 2 load
r5 = r4 * r2 + r5; // 3 mad
r1 = r1 * r6; // 4 mul
r1 = r1 + r0 + ((((sel >> 6u) & 1u) != 0u) ? 0x9dd9fb05u : 0x9db46598u); // 5 add
r1 = r1 ^ __shfl_xor_sync(0xffffffffu, r7, 16); // 6 shfl
r6 = r6 * r1; // 7 mul
r1 = __umulhi(r1, r7); // 8 mulhi
r2 = __umulhi(r2, r1); // 9 mulhi
r0 = r0 + r4 + ((((sel >> 13u) & 1u) != 0u) ? 0xc15822b6u : 0xe45fccebu); // 10 add
r5 = r1 * r2 + r5; // 11 mad
r4 = r4 * r7; // 12 mul
{ uint32_t b_ = (r5 & mask) & ~15u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint32_t x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r0 = x_; } // 13 load
r3 = r3 ^ r4; // 14 xor
r1 = r1 ^ r6; // 15 xor
r5 = rotr_var(r5, r6); // 16 rotr
r3 = rotr_var(r3, r7); // 17 rotr
r1 = r1 ^ __shfl_xor_sync(0xffffffffu, r4, 16); // 18 shfl
r5 = r5 ^ ds[r6 & mask]; // 19 load
r6 = rotl_imm(r6, 30u); // 20 rotl
{ uint32_t b_ = (r2 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r5 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r5 = x_; } // 21 load
r3 = r3 ^ r5; // 22 xor
r5 = r5 + r7 + ((((sel >> 6u) & 1u) != 0u) ? 0x950603c6u : 0x1d4f8db7u); // 23 add
{ uint32_t b_ = (r4 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r3 = x_; } // 24 load
{ uint32_t b_ = (r1 & mask) & ~15u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint32_t x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r0 = x_; } // 25 load
r2 = r2 + r3 + ((((sel >> 31u) & 1u) != 0u) ? 0x0ae476a4u : 0x55db0d92u); // 26 add
r5 = r5 ^ ds[r6 & mask]; // 27 load
{ uint32_t b_ = (r5 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r2 = x_; } // 28 load
r1 = r1 ^ r6; // 29 xor
r7 = __umulhi(r7, r4); // 30 mulhi
r6 = rotr_var(r6, r3); // 31 rotr
r0 = r0 ^ ds[r3 & mask]; // 32 load
r5 = rotr_var(r5, r0); // 33 rotr
r5 = rotl_imm(r5, 20u); // 34 rotl
r0 = __umulhi(r0, r4); // 35 mulhi
r6 = r6 + r7 + ((((sel >> 18u) & 1u) != 0u) ? 0xfe7a0454u : 0x5d6e3dc5u); // 36 add
{ uint32_t b_ = (r1 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r2 = x_; } // 37 load
r2 = rotl_imm(r2, 19u); // 38 rotl
r3 = r3 + r6 + ((((sel >> 20u) & 1u) != 0u) ? 0xc1d4ae24u : 0xe7e241bau); // 39 add
{ uint32_t b_ = (r7 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r4 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r4 = x_; } // 40 load
r3 = r3 + r1 + ((((sel >> 21u) & 1u) != 0u) ? 0x8f9f8556u : 0xb45cdd60u); // 41 add
r5 = r5 ^ __shfl_xor_sync(0xffffffffu, r2, 4); // 42 shfl
r0 = r0 * r1; // 43 mul
r0 = rotr_var(r0, r7); // 44 rotr
r5 = r5 - r3; // 45 sub
r2 = r7 * r7 + r2; // 46 mad
r6 = r3 * r1 + r6; // 47 mad
r0 = r0 * r7; // 48 mul
r0 = r0 + r4 + ((((sel >> 1u) & 1u) != 0u) ? 0xb9083b6cu : 0x3fa0c5cdu); // 49 add
r4 = r4 + r6 + ((((sel >> 13u) & 1u) != 0u) ? 0x9eecc778u : 0x2d6873e8u); // 50 add
r2 = r2 - r7; // 51 sub
r6 = rotl_imm(r6, 6u); // 52 rotl
r3 = rotr_var(r3, r5); // 53 rotr
{ uint32_t b_ = (r2 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r3 = x_; } // 54 load
{ uint32_t b_ = (r5 & mask) & ~3u; const uint4* l_ = (const uint4*)(ds + b_); uint4 v0_ = l_[0]; uint32_t x_ = r7 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r7 = x_; } // 55 load
r2 = r2 | r6; // 56 or
r4 = r4 ^ ds[r0 & mask]; // 57 load
r3 = r3 ^ r6; // 58 xor
r0 = r0 ^ ds[r6 & mask]; // 59 load
r2 = r2 + r4 + ((((sel >> 3u) & 1u) != 0u) ? 0x19c76fb3u : 0x5052a1c3u); // 60 add
r7 = r7 ^ ds[r4 & mask]; // 61 load
r6 = r6 | r1; // 62 or
r5 = r5 + r1 + ((((sel >> 4u) & 1u) != 0u) ? 0x535fe3fau : 0x08c757c0u); // 63 add
}
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;
}
cudaError_t igneum_launch_hash_bound(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask,
IgneumInitWords iw, 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_bound<<<nonces / block, block>>>(ds, out, baseNonce, mask, iw);
return cudaGetLastError();
}
cudaError_t igneum_hash_bound_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps) {
cudaFuncAttributes attr;
cudaError_t e = cudaFuncGetAttributes(&attr, igneum_hash_bound);
if (e != cudaSuccess) return e;
*numRegs = attr.numRegs;
return cudaOccupancyMaxActiveBlocksPerMultiprocessor(blocksPerSM, igneum_hash_bound, (int)(32u * blockWarps), 0);
}

View file

@ -0,0 +1,108 @@
// Generated by igneum-pow export (generator v2) for seed "igneum-readwidth/B/1". Do not edit by hand.
// Memory-hard dataset core, the same text that the Mac's Metal kernels and CPU verifier were checked against.
// Included by kernel.cu (device), host.cu (host reference) and proto-opencl/host.c (C99 host reference).
// See proto-metal/MEMHARD.md for the construction. kernel.cl carries the same text in OpenCL C.
#pragma once
#ifdef __cplusplus
#include <cstdint>
#else
#include <stdint.h>
#endif
#if defined(__CUDACC__)
#define IGNEUM_HD __host__ __device__ __forceinline__
#elif defined(_MSC_VER) && !defined(__cplusplus)
#define IGNEUM_HD static __inline
#else
#define IGNEUM_HD static inline
#endif
// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines.
// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals.
#define MH_CACHE_LINE_MASK 0x003fffffu
#define MH_SEGMENT_LINES 64u
#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); }
IGNEUM_HD uint32_t mh_rotl(uint32_t x, uint32_t n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site
// y = ChaCha12 core(x) + x
IGNEUM_HD void mh_chacha_block(const uint32_t* x, uint32_t* y) {
for (uint32_t i = 0u; i < 16u; ++i) y[i] = x[i];
for (uint32_t r = 0u; r < 6u; ++r) {
MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u)
MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u)
}
for (uint32_t i = 0u; i < 16u; ++i) y[i] += x[i];
}
// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0.
IGNEUM_HD void mh_cache_segment(uint32_t* cache, uint32_t seg) {
uint32_t prev[16]; uint32_t x[16]; uint32_t y[16];
for (uint32_t i = 0u; i < 16u; ++i) prev[i] = 0u;
for (uint32_t j = 0u; j < MH_SEGMENT_LINES; ++j) {
x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3];
x[4] = 0x3067619fu ^ prev[4];
x[5] = 0x3c269176u ^ prev[5];
x[6] = 0x84a03b03u ^ prev[6];
x[7] = 0xf8c63294u ^ prev[7];
x[8] = 0xff977c5bu ^ prev[8];
x[9] = 0xe60def3eu ^ prev[9];
x[10] = 0x63630141u ^ prev[10];
x[11] = 0xb8fbcb58u ^ prev[11];
x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15];
mh_chacha_block(x, y);
uint32_t* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u);
for (uint32_t i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; }
}
}
// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations.
IGNEUM_HD void mh_mixer(uint32_t* s, uint32_t rk) {
s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u;
s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu;
s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u;
s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu;
s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u;
s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u;
s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u;
s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u;
s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du;
s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u;
s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du;
s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu;
s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du;
s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu;
s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u;
s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u;
MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u)
MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u)
MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u)
MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u)
}
// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer.
IGNEUM_HD void mh_item(const uint32_t* cache, uint32_t t, uint32_t* s) {
s[0] = 0x3067619fu;
s[1] = 0x3c269176u;
s[2] = 0x84a03b03u;
s[3] = 0xf8c63294u;
s[4] = 0xff977c5bu;
s[5] = 0xe60def3eu;
s[6] = 0x63630141u;
s[7] = 0xb8fbcb58u;
s[8] = t * 0x42146205u + 0xbab68293u;
s[9] = t * 0x52cbe0fbu + 0xcc162340u;
s[10] = t * 0x7ecf4a03u + 0x6ce151ccu;
s[11] = t * 0x6728907fu + 0xe62b8997u;
s[12] = t * 0xd81d9751u + 0xc9c80297u;
s[13] = t * 0x132952c3u + 0xf74a1654u;
s[14] = t * 0xf60de277u + 0x3d704af5u;
s[15] = t * 0x05358035u + 0x3cf522b7u;
for (uint32_t r = 0u; r < 8u; ++r) {
mh_mixer(s, 0x9E3779B9u * (r + 1u));
const uint32_t* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u);
for (uint32_t i = 0u; i < 16u; ++i) s[i] ^= line[i];
}
mh_mixer(s, 0x9E3779B9u * 9u);
}
// dataset[w] without the dataset: derive item w >> 4 and take word w & 15.
IGNEUM_HD uint32_t mh_word(const uint32_t* cache, uint32_t w) { uint32_t s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; }

View file

@ -0,0 +1,106 @@
#include <metal_stdlib>
using namespace metal;
// Memory-hard dataset core (MEMHARD.md). Cache: 2^26 words in 2^16 segments of 64 chained ChaCha12 lines.
// Item: 8 rounds of seed-parameterised mixer + one 64-byte cache read, then a final mixer. All parameters are literals.
#define MH_CACHE_LINE_MASK 0x003fffffu
#define MH_SEGMENT_LINES 64u
#define MH_QR(a, b, c, d, r1, r2, r3, r4) { a += b; d ^= a; d = mh_rotl(d, r1); c += d; b ^= c; b = mh_rotl(b, r2); a += b; d ^= a; d = mh_rotl(d, r3); c += d; b ^= c; b = mh_rotl(b, r4); }
inline uint mh_rotl(uint x, uint n) { return (x << n) | (x >> (32u - n)); } // n in 1..31 at every call site
// y = ChaCha12 core(x) + x
inline void mh_chacha_block(const thread uint* x, thread uint* y) {
for (uint i = 0u; i < 16u; ++i) y[i] = x[i];
for (uint r = 0u; r < 6u; ++r) {
MH_QR(y[0], y[4], y[8], y[12], 16u, 12u, 8u, 7u) MH_QR(y[1], y[5], y[9], y[13], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[6], y[10], y[14], 16u, 12u, 8u, 7u) MH_QR(y[3], y[7], y[11], y[15], 16u, 12u, 8u, 7u)
MH_QR(y[0], y[5], y[10], y[15], 16u, 12u, 8u, 7u) MH_QR(y[1], y[6], y[11], y[12], 16u, 12u, 8u, 7u)
MH_QR(y[2], y[7], y[8], y[13], 16u, 12u, 8u, 7u) MH_QR(y[3], y[4], y[9], y[14], 16u, 12u, 8u, 7u)
}
for (uint i = 0u; i < 16u; ++i) y[i] += x[i];
}
// One cache segment: 64 chained lines written at cache[seg * 1024]. in_j = prev ^ (sigma || K || seg || j || tag), prev_0 = 0.
inline void mh_cache_segment(device uint* cache, uint seg) {
uint prev[16]; uint x[16]; uint y[16];
for (uint i = 0u; i < 16u; ++i) prev[i] = 0u;
for (uint j = 0u; j < MH_SEGMENT_LINES; ++j) {
x[0] = 0x61707865u ^ prev[0]; x[1] = 0x3320646eu ^ prev[1]; x[2] = 0x79622d32u ^ prev[2]; x[3] = 0x6b206574u ^ prev[3];
x[4] = 0x3067619fu ^ prev[4];
x[5] = 0x3c269176u ^ prev[5];
x[6] = 0x84a03b03u ^ prev[6];
x[7] = 0xf8c63294u ^ prev[7];
x[8] = 0xff977c5bu ^ prev[8];
x[9] = 0xe60def3eu ^ prev[9];
x[10] = 0x63630141u ^ prev[10];
x[11] = 0xb8fbcb58u ^ prev[11];
x[12] = seg ^ prev[12]; x[13] = j ^ prev[13]; x[14] = 0x49676e65u ^ prev[14]; x[15] = 0x756d4d48u ^ prev[15];
mh_chacha_block(x, y);
device uint* line = cache + ((seg * MH_SEGMENT_LINES + j) * 16u);
for (uint i = 0u; i < 16u; ++i) { line[i] = y[i]; prev[i] = y[i]; }
}
}
// M_r: per word (s ^ (RC + rk)) * MUL, then a column round and a diagonal round with the seed-drawn rotations.
inline void mh_mixer(thread uint* s, uint rk) {
s[0] = (s[0] ^ (0xbab68293u + rk)) * 0x42146205u;
s[1] = (s[1] ^ (0xcc162340u + rk)) * 0x52cbe0fbu;
s[2] = (s[2] ^ (0x6ce151ccu + rk)) * 0x7ecf4a03u;
s[3] = (s[3] ^ (0xe62b8997u + rk)) * 0x6728907fu;
s[4] = (s[4] ^ (0xc9c80297u + rk)) * 0xd81d9751u;
s[5] = (s[5] ^ (0xf74a1654u + rk)) * 0x132952c3u;
s[6] = (s[6] ^ (0x3d704af5u + rk)) * 0xf60de277u;
s[7] = (s[7] ^ (0x3cf522b7u + rk)) * 0x05358035u;
s[8] = (s[8] ^ (0x2b9cac04u + rk)) * 0xbaf6499du;
s[9] = (s[9] ^ (0xa880ac10u + rk)) * 0xe4db9667u;
s[10] = (s[10] ^ (0x13e5dd1du + rk)) * 0x3e98f45du;
s[11] = (s[11] ^ (0x6fc3e233u + rk)) * 0xd0004eddu;
s[12] = (s[12] ^ (0x2d83eeacu + rk)) * 0x2691630du;
s[13] = (s[13] ^ (0x9006e8bfu + rk)) * 0x9beb3bcfu;
s[14] = (s[14] ^ (0x2c4b5362u + rk)) * 0xab310379u;
s[15] = (s[15] ^ (0x31b49ee2u + rk)) * 0x99cfb423u;
MH_QR(s[0], s[4], s[8], s[12], 20u, 20u, 19u, 4u) MH_QR(s[1], s[5], s[9], s[13], 20u, 20u, 19u, 4u)
MH_QR(s[2], s[6], s[10], s[14], 20u, 20u, 19u, 4u) MH_QR(s[3], s[7], s[11], s[15], 20u, 20u, 19u, 4u)
MH_QR(s[0], s[5], s[10], s[15], 26u, 3u, 3u, 27u) MH_QR(s[1], s[6], s[11], s[12], 26u, 3u, 3u, 27u)
MH_QR(s[2], s[7], s[8], s[13], 26u, 3u, 3u, 27u) MH_QR(s[3], s[4], s[9], s[14], 26u, 3u, 3u, 27u)
}
// Item t: 16 words. s = (K, t * MUL[i] + RC[i]); 8 rounds of mixer + cache line s[0] & mask; final mixer.
inline void mh_item(device const uint* cache, uint t, thread uint* s) {
s[0] = 0x3067619fu;
s[1] = 0x3c269176u;
s[2] = 0x84a03b03u;
s[3] = 0xf8c63294u;
s[4] = 0xff977c5bu;
s[5] = 0xe60def3eu;
s[6] = 0x63630141u;
s[7] = 0xb8fbcb58u;
s[8] = t * 0x42146205u + 0xbab68293u;
s[9] = t * 0x52cbe0fbu + 0xcc162340u;
s[10] = t * 0x7ecf4a03u + 0x6ce151ccu;
s[11] = t * 0x6728907fu + 0xe62b8997u;
s[12] = t * 0xd81d9751u + 0xc9c80297u;
s[13] = t * 0x132952c3u + 0xf74a1654u;
s[14] = t * 0xf60de277u + 0x3d704af5u;
s[15] = t * 0x05358035u + 0x3cf522b7u;
for (uint r = 0u; r < 8u; ++r) {
mh_mixer(s, 0x9E3779B9u * (r + 1u));
device const uint* line = cache + ((s[0] & MH_CACHE_LINE_MASK) * 16u);
for (uint i = 0u; i < 16u; ++i) s[i] ^= line[i];
}
mh_mixer(s, 0x9E3779B9u * 9u);
}
// dataset[w] without the dataset: derive item w >> 4 and take word w & 15.
inline uint mh_word(device const uint* cache, uint w) { uint s[16]; mh_item(cache, w >> 4u, s); return s[w & 15u]; }
// One thread per segment (2^16 threads).
kernel void igneum_cache_fill(device uint* cache [[buffer(0)]], uint gid [[thread_position_in_grid]]) {
mh_cache_segment(cache, gid);
}
// One thread per 64-byte item (dataset words / 16 threads).
kernel void igneum_build(device const uint* cache [[buffer(0)]], device uint* dataset [[buffer(1)]],
uint gid [[thread_position_in_grid]]) {
uint s[16];
mh_item(cache, gid, s);
device uint* d = dataset + gid * 16u;
for (uint i = 0u; i < 16u; ++i) d[i] = s[i];
}

View file

@ -0,0 +1,60 @@
// Generated by igneum-pow export (generator v2) for seed "igneum-readwidth/B/1". Do not edit by hand.
// Program metadata for host.cu plus the launch wrappers defined in kernel.cu.
// Also included by proto-opencl/host.c (C99), which defines IGNEUM_NO_CUDA first and reads only the macros.
#pragma once
#ifdef __cplusplus
#include <cstdint>
#else
#include <stdint.h>
#endif
#ifndef IGNEUM_NO_CUDA
#include <cuda_runtime.h>
#endif
#define IGNEUM_SEED_STRING "igneum-readwidth/B/1"
#define IGNEUM_SEED_BYTES_HEX "69676e65756d2d7265616477696474682f422f31"
#define IGNEUM_GENERATOR 2
#define IGNEUM_PROGRAM_ATTEMPT 0
#define IGNEUM_PROGRAM_ID 0x695b119dcb76931bull
#define IGNEUM_DAY_STRING "2026-10-03"
#define IGNEUM_DAY_BYTES_HEX "6461792f323032362d31302d3033"
#define IGNEUM_DAY0 0x3067619fu
#define IGNEUM_DAY1 0x3c269176u
#define IGNEUM_DATASET_LOG2 28
#define IGNEUM_MASK 0x0fffffffu
#define IGNEUM_LANES 32
#define IGNEUM_ITERATIONS 8
#define IGNEUM_INSTR_COUNT 64
#define IGNEUM_LOADS_PER_HASH 128
#define IGNEUM_WIDE_LOADS_PER_HASH 0
#define IGNEUM_OP_MIX "load=16 add=11 rotr=6 mad=5 mul=5 rotl=5 xor=5 mulhi=4 shfl=3 or=2 sub=2"
// Read-width experiment (5 October 2026, docs/plans/read-width.md): NOT the lottery hash. A load of W words reads
// the W-word-aligned address and folds every word into dst: x = dst ^ w[0]; x = (rotl(x, 11) * 0x9e3779b1) ^ w[j]; dst = x.
#define IGNEUM_LOAD_CLASS "mix25-50-25"
#define IGNEUM_LOAD_SLOTS 16
#define IGNEUM_LOAD_MIX { 25, 50, 25 }
#define IGNEUM_LOAD_WIDTH_COUNTS { 6, 8, 2 } // loads of 4, 16, 64 bytes per program
#define IGNEUM_BYTES_PER_HASH 2240
#define IGNEUM_FOLD_ROT 11
#define IGNEUM_FOLD_MUL 0x9e3779b1u
// 0 = closed-form dataset (ds_elem), 1 = memory-hard cache construction (MEMHARD.md, memhard.h)
#define IGNEUM_DATASET_MODE 1
#define IGNEUM_SEEDW_INIT { 0x3e345412u, 0xfc2b3a0eu, 0x7740353du, 0x2df159dbu, 0x50bc6eedu, 0x2e61acb2u, 0x3c800bffu, 0xbaecd8c1u }
#define IGNEUM_KEY_INIT { 0x3067619fu, 0x3c269176u, 0x84a03b03u, 0xf8c63294u, 0xff977c5bu, 0xe60def3eu, 0x63630141u, 0xb8fbcb58u }
#define IGNEUM_CACHE_LOG2_WORDS 26
#define IGNEUM_CACHE_SEGMENT_LOG2_LINES 6
#define IGNEUM_CACHE_SEGMENTS 65536u
#define IGNEUM_ITEM_ROUNDS 8
#define IGNEUM_MIX_ROT_INIT { 20u, 20u, 19u, 4u, 26u, 3u, 3u, 27u }
#define IGNEUM_MIX_MUL_INIT { 0x42146205u, 0x52cbe0fbu, 0x7ecf4a03u, 0x6728907fu, 0xd81d9751u, 0x132952c3u, 0xf60de277u, 0x05358035u, 0xbaf6499du, 0xe4db9667u, 0x3e98f45du, 0xd0004eddu, 0x2691630du, 0x9beb3bcfu, 0xab310379u, 0x99cfb423u }
#define IGNEUM_MIX_RC_INIT { 0xbab68293u, 0xcc162340u, 0x6ce151ccu, 0xe62b8997u, 0xc9c80297u, 0xf74a1654u, 0x3d704af5u, 0x3cf522b7u, 0x2b9cac04u, 0xa880ac10u, 0x13e5dd1du, 0x6fc3e233u, 0x2d83eeacu, 0x9006e8bfu, 0x2c4b5362u, 0x31b49ee2u }
#ifndef IGNEUM_NO_CUDA
// Defined in kernel.cu. All launch on the default stream and return cudaGetLastError().
cudaError_t igneum_launch_cache_fill(uint32_t* cache, uint32_t nSegments);
cudaError_t igneum_launch_build(uint32_t* ds, const uint32_t* cache, uint32_t nItems);
cudaError_t igneum_launch_hash(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask,
uint32_t nonces, uint32_t blockWarps);
cudaError_t igneum_hash_info(int* numRegs, int* blocksPerSM, uint32_t blockWarps);
#endif

View file

@ -0,0 +1,127 @@
{
"format": "igneum-program-pack-3",
"generator": 2,
"attempt": 0,
"program_id": "0x695b119dcb76931b",
"program_id_derivation": "FNV-1a 64 over 'igneum-program/' || generator_le32 || seed_words as little-endian bytes || attempt_le32",
"dataset_mode": "memory-hard",
"seed": "igneum-readwidth/B/1",
"seed_bytes": "69676e65756d2d7265616477696474682f422f31",
"seed_words": ["0x3e345412", "0xfc2b3a0e", "0x7740353d", "0x2df159db", "0x50bc6eed", "0x2e61acb2", "0x3c800bff", "0xbaecd8c1"],
"seed_derivation": "seed_words = FNV-1a 64 over seed_bytes (attempt 0) or seed_bytes || attempt_le32 (attempt k >= 1), basis ^ (salt * 0x9E3779B97F4A7C15) for salt 0..3, then h ^= h>>33; h *= 0xff51afd7ed558ccd; h ^= h>>33; words[2*salt] = low 32, words[2*salt+1] = high 32",
"generator_rule": "version 2: exactly 16 load slots drawn first from instructions 1..63 (partial Fisher-Yates), the other 48 ops from the ten non-load weights (sum 75); a load's source is drawn from the registers other than dst written by an earlier instruction and not read by a load since; the candidate must pass the acceptance rule of spec 01 section 1.4.6 (static: no cyclically stale load source, every register has an injecting write; dynamic: 64 units on the seed-keyed closed-form dataset with no constant register bit, no lane-constant load site, under 164 saturated final values, every output bit within 136 of 1024, distinct addresses above 245760), else the next attempt of the seed is tried",
"lanes": 32,
"registers": 8,
"iterations": 8,
"instruction_count": 64,
"loads_per_hash": 128,
"load_class": "mix25-50-25",
"load_slots": 16,
"load_mix_percent_4_16_64": [25, 50, 25],
"load_width_counts_4_16_64": [6, 8, 2],
"bytes_per_hash": 2240,
"wide_load": "read-width experiment (5 October 2026, docs/plans/read-width.md), NOT the lottery hash: a load of W words (width field, 4 or 16) reads dataset[b .. b + W) with b = (src & mask) & ~(W - 1) and folds every word into dst: x = dst ^ w[0]; for j in 1..W: x = (rotl(x, 11) * 0x9e3779b1) ^ w[j]; dst = x; width 1 is the plain load; the width is drawn per instruction from the class mix with one extra below(100) draw after the nine of version 2, and the program id is FNV-1a 64 over 'igneum-program-rw/' || generator_le32 || seed words || attempt_le32 || mix[3] || load_slots",
"op_mix": {"load": 16, "add": 11, "rotr": 6, "mad": 5, "mul": 5, "rotl": 5, "xor": 5, "mulhi": 4, "shfl": 3, "or": 2, "sub": 2},
"register_init": "for i in 0..7: x = nonce ^ seed_words[i]; x += 0x9e3779b9 * (i+1) (mod 2^32); x = splitmix32(x); r[i] = x ^ seed_words[(i+1) & 7]",
"splitmix32": "x ^= x>>16; x *= 0x7feb352d; x ^= x>>15; x *= 0x846ca68b; x ^= x>>16",
"iteration": "sel = r0 sampled once at the top of each iteration, then all instructions in order",
"output": "lo = r0 ^ rotl(r1,7) ^ rotl(r2,14) ^ rotl(r3,21); hi = r4 ^ rotl(r5,9) ^ rotl(r6,18) ^ rotl(r7,27); out = (hi << 32) | lo",
"op_semantics": {
"add": "dst = dst + src + (bit `bit` of sel ? imm2 : imm)",
"sub": "dst = dst - src",
"mul": "dst = dst * src (low 32)",
"mulhi": "dst = high 32 bits of dst * src",
"xor": "dst = dst ^ src",
"or": "dst = dst | src",
"rotl": "dst = rotl(dst, rot), rot in 1..31",
"rotr": "dst = rotr(dst, src & 31)",
"mad": "dst = src * src2 + dst",
"shfl": "dst = dst ^ (src of lane (lane ^ mask)), mask in {1,2,4,8,16}, within the 32-lane warp",
"load": "dst = dst ^ dataset[src & dataset.mask]",
"wload": "base = (src of lane 0 & dataset.mask) & ~31; dst = dst ^ dataset[base + lane] (warp-coalesced 128-byte load, lever b, only when --wide-frac > 0)"
},
"dataset": {
"log2_words": 28,
"bytes": 1073741824,
"mask": "0x0fffffff",
"day": "2026-10-03",
"day_bytes": "6461792f323032362d31302d3033",
"day_words_from": "seed_words_from_bytes(day_bytes)",
"d0": "0x3067619f",
"d1": "0x3c269176",
"mode": "memory-hard",
"spec": "proto-metal/MEMHARD.md",
"key": ["0x3067619f", "0x3c269176", "0x84a03b03", "0xf8c63294", "0xff977c5b", "0xe60def3e", "0x63630141", "0xb8fbcb58"],
"key_derivation": "the 8 words of seed_words_from_bytes(day_bytes); d0, d1 are key[0], key[1]",
"cache": {"log2_words": 26, "bytes": 268435456, "line_words": 16, "segment_lines": 64, "segments": 65536, "block": "ChaCha12 core + feed-forward, rotations 16 12 8 7", "sigma": ["0x61707865", "0x3320646e", "0x79622d32", "0x6b206574"], "tag": ["0x49676e65", "0x756d4d48"], "chain": "in_j = prev_line ^ (sigma[0..3] || key[0..7] || seg || j || tag[0..1]); line_j = block(in_j); prev_0 = 0"},
"mixer": {"draw": "SplitMix64 seeded with key[0] | key[1] << 32: rot[0..7] = 1 + next() % 31, mul[0..15] = low32(next()) | 1, rc[0..15] = low32(next())", "rot": [20, 20, 19, 4, 26, 3, 3, 27], "mul": ["0x42146205", "0x52cbe0fb", "0x7ecf4a03", "0x6728907f", "0xd81d9751", "0x132952c3", "0xf60de277", "0x05358035", "0xbaf6499d", "0xe4db9667", "0x3e98f45d", "0xd0004edd", "0x2691630d", "0x9beb3bcf", "0xab310379", "0x99cfb423"], "rc": ["0xbab68293", "0xcc162340", "0x6ce151cc", "0xe62b8997", "0xc9c80297", "0xf74a1654", "0x3d704af5", "0x3cf522b7", "0x2b9cac04", "0xa880ac10", "0x13e5dd1d", "0x6fc3e233", "0x2d83eeac", "0x9006e8bf", "0x2c4b5362", "0x31b49ee2"], "round": "for i in 0..15: s[i] = (s[i] ^ (rc[i] + (r+1) * 0x9E3779B9)) * mul[i]; then quarter rounds on columns (0,4,8,12) (1,5,9,13) (2,6,10,14) (3,7,11,15) with rot[0..3] and diagonals (0,5,10,15) (1,6,11,12) (2,7,8,13) (3,4,9,14) with rot[4..7]", "quarter_round": "a += b; d ^= a; d = rotl(d, r1); c += d; b ^= c; b = rotl(b, r2); a += b; d ^= a; d = rotl(d, r3); c += d; b ^= c; b = rotl(b, r4)"},
"item": "s[0..7] = key; s[8+i] = t * mul[i] + rc[i] for i in 0..7; for r in 0..7: s = M_r(s); line = s[0] & 0x003fffff; s[i] ^= cache[line * 16 + i]; then s = M_8(s); item(t) = s",
"word": "dataset[w] = item(w >> 4)[w & 15]"
},
"instructions": [
{"i": 0, "op": "mad", "dst": 5, "src": 4, "src2": 0, "imm": "0x392cc69d", "imm2": "0xdff38f87", "rot": 18, "bit": 19, "mask": 2, "width": 1},
{"i": 1, "op": "rotl", "dst": 1, "src": 7, "src2": 0, "imm": "0xa731596a", "imm2": "0x4ce406f5", "rot": 23, "bit": 27, "mask": 4, "width": 1},
{"i": 2, "op": "load", "dst": 0, "src": 5, "src2": 5, "imm": "0x4d78cee5", "imm2": "0x7a624dd7", "rot": 16, "bit": 24, "mask": 16, "width": 4},
{"i": 3, "op": "mad", "dst": 5, "src": 4, "src2": 2, "imm": "0x80a8d418", "imm2": "0x3f405868", "rot": 21, "bit": 4, "mask": 8, "width": 1},
{"i": 4, "op": "mul", "dst": 1, "src": 6, "src2": 4, "imm": "0xc4e8867c", "imm2": "0xd2ad8c44", "rot": 26, "bit": 21, "mask": 16, "width": 1},
{"i": 5, "op": "add", "dst": 1, "src": 0, "src2": 6, "imm": "0x9db46598", "imm2": "0x9dd9fb05", "rot": 14, "bit": 6, "mask": 16, "width": 1},
{"i": 6, "op": "shfl", "dst": 1, "src": 7, "src2": 5, "imm": "0xc9049c0f", "imm2": "0x007f6621", "rot": 24, "bit": 19, "mask": 16, "width": 1},
{"i": 7, "op": "mul", "dst": 6, "src": 1, "src2": 7, "imm": "0x073fea86", "imm2": "0x1ed9dff1", "rot": 26, "bit": 24, "mask": 2, "width": 1},
{"i": 8, "op": "mulhi", "dst": 1, "src": 7, "src2": 5, "imm": "0x3faa8f96", "imm2": "0x01445abc", "rot": 26, "bit": 3, "mask": 4, "width": 1},
{"i": 9, "op": "mulhi", "dst": 2, "src": 1, "src2": 1, "imm": "0x97d622e9", "imm2": "0xfd732e57", "rot": 28, "bit": 29, "mask": 1, "width": 1},
{"i": 10, "op": "add", "dst": 0, "src": 4, "src2": 4, "imm": "0xe45fcceb", "imm2": "0xc15822b6", "rot": 15, "bit": 13, "mask": 2, "width": 1},
{"i": 11, "op": "mad", "dst": 5, "src": 1, "src2": 2, "imm": "0x6148c1cc", "imm2": "0xdaa7b1d3", "rot": 3, "bit": 25, "mask": 16, "width": 1},
{"i": 12, "op": "mul", "dst": 4, "src": 7, "src2": 1, "imm": "0x7b0965e5", "imm2": "0x5086e5aa", "rot": 30, "bit": 28, "mask": 16, "width": 1},
{"i": 13, "op": "load", "dst": 0, "src": 5, "src2": 4, "imm": "0x4c87a2ab", "imm2": "0x5db5ba1c", "rot": 22, "bit": 19, "mask": 16, "width": 16},
{"i": 14, "op": "xor", "dst": 3, "src": 4, "src2": 6, "imm": "0xdc246d40", "imm2": "0xa5cfff4a", "rot": 6, "bit": 19, "mask": 16, "width": 1},
{"i": 15, "op": "xor", "dst": 1, "src": 6, "src2": 6, "imm": "0x6d3e113d", "imm2": "0x69e25584", "rot": 12, "bit": 27, "mask": 2, "width": 1},
{"i": 16, "op": "rotr", "dst": 5, "src": 6, "src2": 2, "imm": "0x45569439", "imm2": "0x14d0f916", "rot": 10, "bit": 5, "mask": 16, "width": 1},
{"i": 17, "op": "rotr", "dst": 3, "src": 7, "src2": 1, "imm": "0x412cb31a", "imm2": "0xe0713d9c", "rot": 17, "bit": 0, "mask": 2, "width": 1},
{"i": 18, "op": "shfl", "dst": 1, "src": 4, "src2": 3, "imm": "0x2419b99c", "imm2": "0x9ab3a71e", "rot": 17, "bit": 15, "mask": 16, "width": 1},
{"i": 19, "op": "load", "dst": 5, "src": 6, "src2": 7, "imm": "0xa1f88336", "imm2": "0xca05a80c", "rot": 17, "bit": 22, "mask": 8, "width": 1},
{"i": 20, "op": "rotl", "dst": 6, "src": 7, "src2": 4, "imm": "0x51de29c5", "imm2": "0xfe2d2af0", "rot": 30, "bit": 20, "mask": 1, "width": 1},
{"i": 21, "op": "load", "dst": 5, "src": 2, "src2": 6, "imm": "0x5db413b6", "imm2": "0x3dad8864", "rot": 20, "bit": 23, "mask": 4, "width": 4},
{"i": 22, "op": "xor", "dst": 3, "src": 5, "src2": 0, "imm": "0xdeadb225", "imm2": "0x7397a1f2", "rot": 5, "bit": 3, "mask": 2, "width": 1},
{"i": 23, "op": "add", "dst": 5, "src": 7, "src2": 7, "imm": "0x1d4f8db7", "imm2": "0x950603c6", "rot": 19, "bit": 6, "mask": 1, "width": 1},
{"i": 24, "op": "load", "dst": 3, "src": 4, "src2": 5, "imm": "0x148ee6e2", "imm2": "0xfc0cbfef", "rot": 1, "bit": 23, "mask": 16, "width": 4},
{"i": 25, "op": "load", "dst": 0, "src": 1, "src2": 1, "imm": "0x114e77c3", "imm2": "0x8b1c13f1", "rot": 4, "bit": 0, "mask": 8, "width": 16},
{"i": 26, "op": "add", "dst": 2, "src": 3, "src2": 3, "imm": "0x55db0d92", "imm2": "0x0ae476a4", "rot": 24, "bit": 31, "mask": 4, "width": 1},
{"i": 27, "op": "load", "dst": 5, "src": 6, "src2": 5, "imm": "0xee300382", "imm2": "0x1609cf99", "rot": 8, "bit": 11, "mask": 16, "width": 1},
{"i": 28, "op": "load", "dst": 2, "src": 5, "src2": 2, "imm": "0x96783a44", "imm2": "0x0f090418", "rot": 11, "bit": 24, "mask": 1, "width": 4},
{"i": 29, "op": "xor", "dst": 1, "src": 6, "src2": 7, "imm": "0x6c8d15c2", "imm2": "0x2cde0f35", "rot": 18, "bit": 14, "mask": 4, "width": 1},
{"i": 30, "op": "mulhi", "dst": 7, "src": 4, "src2": 1, "imm": "0xc68ea9de", "imm2": "0xc45d9dfa", "rot": 29, "bit": 14, "mask": 2, "width": 1},
{"i": 31, "op": "rotr", "dst": 6, "src": 3, "src2": 5, "imm": "0x2b816dd1", "imm2": "0xdf13f697", "rot": 17, "bit": 12, "mask": 2, "width": 1},
{"i": 32, "op": "load", "dst": 0, "src": 3, "src2": 3, "imm": "0x6711453d", "imm2": "0x78ae13e9", "rot": 2, "bit": 6, "mask": 4, "width": 1},
{"i": 33, "op": "rotr", "dst": 5, "src": 0, "src2": 3, "imm": "0xe0068514", "imm2": "0xba437440", "rot": 14, "bit": 2, "mask": 2, "width": 1},
{"i": 34, "op": "rotl", "dst": 5, "src": 3, "src2": 7, "imm": "0xdde8ab9a", "imm2": "0x9b3f4073", "rot": 20, "bit": 26, "mask": 1, "width": 1},
{"i": 35, "op": "mulhi", "dst": 0, "src": 4, "src2": 2, "imm": "0x2b47ae8c", "imm2": "0x2617710c", "rot": 30, "bit": 29, "mask": 1, "width": 1},
{"i": 36, "op": "add", "dst": 6, "src": 7, "src2": 0, "imm": "0x5d6e3dc5", "imm2": "0xfe7a0454", "rot": 22, "bit": 18, "mask": 8, "width": 1},
{"i": 37, "op": "load", "dst": 2, "src": 1, "src2": 7, "imm": "0x21116475", "imm2": "0xbe53b7b8", "rot": 21, "bit": 2, "mask": 8, "width": 4},
{"i": 38, "op": "rotl", "dst": 2, "src": 7, "src2": 2, "imm": "0xd76a18c4", "imm2": "0xb7ed0d92", "rot": 19, "bit": 10, "mask": 16, "width": 1},
{"i": 39, "op": "add", "dst": 3, "src": 6, "src2": 3, "imm": "0xe7e241ba", "imm2": "0xc1d4ae24", "rot": 10, "bit": 20, "mask": 16, "width": 1},
{"i": 40, "op": "load", "dst": 4, "src": 7, "src2": 0, "imm": "0xb1006316", "imm2": "0x617eb11a", "rot": 1, "bit": 27, "mask": 1, "width": 4},
{"i": 41, "op": "add", "dst": 3, "src": 1, "src2": 3, "imm": "0xb45cdd60", "imm2": "0x8f9f8556", "rot": 5, "bit": 21, "mask": 16, "width": 1},
{"i": 42, "op": "shfl", "dst": 5, "src": 2, "src2": 1, "imm": "0x8fc745c7", "imm2": "0x98256336", "rot": 16, "bit": 7, "mask": 4, "width": 1},
{"i": 43, "op": "mul", "dst": 0, "src": 1, "src2": 3, "imm": "0xe8aea431", "imm2": "0xfeed34f6", "rot": 30, "bit": 2, "mask": 8, "width": 1},
{"i": 44, "op": "rotr", "dst": 0, "src": 7, "src2": 4, "imm": "0x8f1da3ee", "imm2": "0xa9c20edf", "rot": 19, "bit": 17, "mask": 2, "width": 1},
{"i": 45, "op": "sub", "dst": 5, "src": 3, "src2": 7, "imm": "0x9da4c5e7", "imm2": "0xbcd50b0c", "rot": 7, "bit": 18, "mask": 1, "width": 1},
{"i": 46, "op": "mad", "dst": 2, "src": 7, "src2": 7, "imm": "0x837be930", "imm2": "0x9b62650b", "rot": 5, "bit": 23, "mask": 1, "width": 1},
{"i": 47, "op": "mad", "dst": 6, "src": 3, "src2": 1, "imm": "0x825afaa4", "imm2": "0x0cf9f1f0", "rot": 17, "bit": 9, "mask": 2, "width": 1},
{"i": 48, "op": "mul", "dst": 0, "src": 7, "src2": 2, "imm": "0x4424f910", "imm2": "0xbaeba671", "rot": 23, "bit": 6, "mask": 4, "width": 1},
{"i": 49, "op": "add", "dst": 0, "src": 4, "src2": 7, "imm": "0x3fa0c5cd", "imm2": "0xb9083b6c", "rot": 28, "bit": 1, "mask": 2, "width": 1},
{"i": 50, "op": "add", "dst": 4, "src": 6, "src2": 4, "imm": "0x2d6873e8", "imm2": "0x9eecc778", "rot": 29, "bit": 13, "mask": 16, "width": 1},
{"i": 51, "op": "sub", "dst": 2, "src": 7, "src2": 2, "imm": "0xace9966d", "imm2": "0xe4213c89", "rot": 30, "bit": 18, "mask": 8, "width": 1},
{"i": 52, "op": "rotl", "dst": 6, "src": 7, "src2": 0, "imm": "0x03bc6330", "imm2": "0x32d9aee6", "rot": 6, "bit": 19, "mask": 1, "width": 1},
{"i": 53, "op": "rotr", "dst": 3, "src": 5, "src2": 1, "imm": "0xef7e5b3e", "imm2": "0xb2aea388", "rot": 27, "bit": 31, "mask": 2, "width": 1},
{"i": 54, "op": "load", "dst": 3, "src": 2, "src2": 2, "imm": "0x711b4f5d", "imm2": "0xac8e6837", "rot": 1, "bit": 5, "mask": 1, "width": 4},
{"i": 55, "op": "load", "dst": 7, "src": 5, "src2": 7, "imm": "0xb0f4faf4", "imm2": "0xb13fe736", "rot": 16, "bit": 2, "mask": 4, "width": 4},
{"i": 56, "op": "or", "dst": 2, "src": 6, "src2": 5, "imm": "0x4f416150", "imm2": "0x2a9f59e3", "rot": 31, "bit": 6, "mask": 4, "width": 1},
{"i": 57, "op": "load", "dst": 4, "src": 0, "src2": 0, "imm": "0x54ce0fba", "imm2": "0x48d98570", "rot": 11, "bit": 2, "mask": 4, "width": 1},
{"i": 58, "op": "xor", "dst": 3, "src": 6, "src2": 6, "imm": "0xf34a7cca", "imm2": "0xf345c73d", "rot": 18, "bit": 18, "mask": 16, "width": 1},
{"i": 59, "op": "load", "dst": 0, "src": 6, "src2": 6, "imm": "0x730474e9", "imm2": "0xb90ac73f", "rot": 25, "bit": 31, "mask": 4, "width": 1},
{"i": 60, "op": "add", "dst": 2, "src": 4, "src2": 1, "imm": "0x5052a1c3", "imm2": "0x19c76fb3", "rot": 12, "bit": 3, "mask": 1, "width": 1},
{"i": 61, "op": "load", "dst": 7, "src": 4, "src2": 0, "imm": "0xbecfc16b", "imm2": "0xd2837365", "rot": 21, "bit": 22, "mask": 8, "width": 1},
{"i": 62, "op": "or", "dst": 6, "src": 1, "src2": 3, "imm": "0x7353a546", "imm2": "0xcc33abd9", "rot": 24, "bit": 5, "mask": 4, "width": 1},
{"i": 63, "op": "add", "dst": 5, "src": 1, "src2": 1, "imm": "0x08c757c0", "imm2": "0x535fe3fa", "rot": 21, "bit": 4, "mask": 4, "width": 1}
]
}

View file

@ -0,0 +1,109 @@
#include <metal_stdlib>
using namespace metal;
#define MASK 0x0fffffffu
constant uint SEEDW[8] = { 0x3e345412u, 0xfc2b3a0eu, 0x7740353du, 0x2df159dbu, 0x50bc6eedu, 0x2e61acb2u, 0x3c800bffu, 0xbaecd8c1u };
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;
}
kernel void igneum_hash(device const uint* dataset [[buffer(0)]],
device ulong* out [[buffer(1)]],
constant uint& baseNonce [[buffer(2)]],
uint gid [[thread_position_in_grid]]) {
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;
r5 = r4 * r0 + r5; // 0
r1 = rotl_imm(r1, 23u); // 1
{ uint b_ = (r5 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r0 = x_; } // 2
r5 = r4 * r2 + r5; // 3
r1 = r1 * r6; // 4
r1 = r1 + r0 + select(0x9db46598u, 0x9dd9fb05u, ((sel >> 6u) & 1u) != 0u); // 5
r1 = r1 ^ simd_shuffle_xor(r7, (ushort)16); // 6
r6 = r6 * r1; // 7
r1 = mulhi(r1, r7); // 8
r2 = mulhi(r2, r1); // 9
r0 = r0 + r4 + select(0xe45fccebu, 0xc15822b6u, ((sel >> 13u) & 1u) != 0u); // 10
r5 = r1 * r2 + r5; // 11
r4 = r4 * r7; // 12
{ uint b_ = (r5 & MASK) & ~15u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r0 = x_; } // 13
r3 = r3 ^ r4; // 14
r1 = r1 ^ r6; // 15
r5 = rotr_var(r5, r6); // 16
r3 = rotr_var(r3, r7); // 17
r1 = r1 ^ simd_shuffle_xor(r4, (ushort)16); // 18
r5 = r5 ^ dataset[r6 & MASK]; // 19
r6 = rotl_imm(r6, 30u); // 20
{ uint b_ = (r2 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r5 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r5 = x_; } // 21
r3 = r3 ^ r5; // 22
r5 = r5 + r7 + select(0x1d4f8db7u, 0x950603c6u, ((sel >> 6u) & 1u) != 0u); // 23
{ uint b_ = (r4 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r3 = x_; } // 24
{ uint b_ = (r1 & MASK) & ~15u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint4 v1_ = l_[1]; uint4 v2_ = l_[2]; uint4 v3_ = l_[3]; uint x_ = r0 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v1_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v2_.w; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v3_.w; r0 = x_; } // 25
r2 = r2 + r3 + select(0x55db0d92u, 0x0ae476a4u, ((sel >> 31u) & 1u) != 0u); // 26
r5 = r5 ^ dataset[r6 & MASK]; // 27
{ uint b_ = (r5 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r2 = x_; } // 28
r1 = r1 ^ r6; // 29
r7 = mulhi(r7, r4); // 30
r6 = rotr_var(r6, r3); // 31
r0 = r0 ^ dataset[r3 & MASK]; // 32
r5 = rotr_var(r5, r0); // 33
r5 = rotl_imm(r5, 20u); // 34
r0 = mulhi(r0, r4); // 35
r6 = r6 + r7 + select(0x5d6e3dc5u, 0xfe7a0454u, ((sel >> 18u) & 1u) != 0u); // 36
{ uint b_ = (r1 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r2 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r2 = x_; } // 37
r2 = rotl_imm(r2, 19u); // 38
r3 = r3 + r6 + select(0xe7e241bau, 0xc1d4ae24u, ((sel >> 20u) & 1u) != 0u); // 39
{ uint b_ = (r7 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r4 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r4 = x_; } // 40
r3 = r3 + r1 + select(0xb45cdd60u, 0x8f9f8556u, ((sel >> 21u) & 1u) != 0u); // 41
r5 = r5 ^ simd_shuffle_xor(r2, (ushort)4); // 42
r0 = r0 * r1; // 43
r0 = rotr_var(r0, r7); // 44
r5 = r5 - r3; // 45
r2 = r7 * r7 + r2; // 46
r6 = r3 * r1 + r6; // 47
r0 = r0 * r7; // 48
r0 = r0 + r4 + select(0x3fa0c5cdu, 0xb9083b6cu, ((sel >> 1u) & 1u) != 0u); // 49
r4 = r4 + r6 + select(0x2d6873e8u, 0x9eecc778u, ((sel >> 13u) & 1u) != 0u); // 50
r2 = r2 - r7; // 51
r6 = rotl_imm(r6, 6u); // 52
r3 = rotr_var(r3, r5); // 53
{ uint b_ = (r2 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r3 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r3 = x_; } // 54
{ uint b_ = (r5 & MASK) & ~3u; device const uint4* l_ = (device const uint4*)(dataset + b_); uint4 v0_ = l_[0]; uint x_ = r7 ^ v0_.x; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.y; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.z; x_ = (rotl_imm(x_, 11u) * 0x9e3779b1u) ^ v0_.w; r7 = x_; } // 55
r2 = r2 | r6; // 56
r4 = r4 ^ dataset[r0 & MASK]; // 57
r3 = r3 ^ r6; // 58
r0 = r0 ^ dataset[r6 & MASK]; // 59
r2 = r2 + r4 + select(0x5052a1c3u, 0x19c76fb3u, ((sel >> 3u) & 1u) != 0u); // 60
r7 = r7 ^ dataset[r4 & MASK]; // 61
r6 = r6 | r1; // 62
r5 = r5 + r1 + select(0x08c757c0u, 0x535fe3fau, ((sel >> 4u) & 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;
}

Some files were not shown because too many files have changed in this diff Show more