86 lines
4.8 KiB
Python
86 lines
4.8 KiB
Python
#!/usr/bin/env python3
|
|
"""Attack-pass F9: the grinding and forced-locality variants of a pack's kernel_bound.cu, as a second translation unit.
|
|
|
|
Reads the pack's kernel_bound.cu (never edited in place) and writes f9-variants.cu with three kernels copied from
|
|
igneum_hash_bound and changed only where stated:
|
|
|
|
f9_hash_perwarp the init words come from a per-warp table (tbl[gid >> 5]) instead of the kernel argument:
|
|
the grinding miner's shape (one nonce_hi per warp); also run with an unsearched table as the control
|
|
f9_hash_forced4 the loads named by --force (the iteration-0 sites whose address depends on the init words and the
|
|
nonce only) read lane 0's address in every lane (the index broadcast by __shfl_sync): the ceiling
|
|
of any locality a grind could reach on those sites; its hashes are wrong on purpose
|
|
f9_hash_forcedall every load reads lane 0's address: the harness-firing case (DRAM traffic divided by 32)
|
|
f9_hash_forced1 only the first forced site reads lane 0's address in every lane (31 lines saved per warp)
|
|
f9_hash_pair at the first forced site lane 1 reads lane 0's address (1 line saved per warp of 4,096: the
|
|
grind's own order of magnitude, placed deterministically)
|
|
|
|
Usage: make-variants.py <pack>/kernel_bound.cu f9-variants.cu --force 7,8,9,10,31
|
|
"""
|
|
import re
|
|
import sys
|
|
|
|
src_path, out_path = sys.argv[1], sys.argv[2]
|
|
force = set()
|
|
if "--force" in sys.argv:
|
|
force = {int(x) for x in sys.argv[sys.argv.index("--force") + 1].split(",")}
|
|
|
|
text = open(src_path).read()
|
|
head, rest = text.split("__global__ void igneum_hash_bound(", 1)
|
|
sig, body_and_tail = rest.split(") {", 1)
|
|
body, tail = body_and_tail.split("\ncudaError_t igneum_launch_hash_bound(", 1)
|
|
assert body.rstrip().endswith("}"), "kernel body end not found"
|
|
load_re = re.compile(r"^(\s*r\d = r\d \^ )ds\[(.*) & mask\];(\s*// (\d+) load)$")
|
|
|
|
def forced(body, which, pair=False):
|
|
out = []
|
|
n = 0
|
|
for line in body.split("\n"):
|
|
m = load_re.match(line)
|
|
if m and (which is None or int(m.group(4)) in which):
|
|
if pair:
|
|
line = (f"{m.group(1).rstrip()} 0; {{ uint32_t a_ = ({m.group(2)}) & mask; uint32_t b_ = __shfl_sync(0xffffffffu, a_, 0); "
|
|
f"{m.group(1).strip()}ds[((threadIdx.x & 31u) == 1u) ? b_ : a_]; }}{m.group(3)} PAIR")
|
|
else:
|
|
line = f"{m.group(1)}ds[__shfl_sync(0xffffffffu, ({m.group(2)}) & mask, 0)];{m.group(3)} FORCED"
|
|
n += 1
|
|
out.append(line)
|
|
return "\n".join(out), n
|
|
|
|
perwarp_body = body.replace("uint32_t gid = blockIdx.x * blockDim.x + threadIdx.x;",
|
|
"uint32_t gid = blockIdx.x * blockDim.x + threadIdx.x;\n IgneumInitWords iw = tbl[gid >> 5];", 1)
|
|
assert "tbl[gid >> 5]" in perwarp_body, "gid line not found"
|
|
sig_tbl = sig.replace("IgneumInitWords iw", "const IgneumInitWords* __restrict__ tbl")
|
|
assert sig_tbl != sig
|
|
f4_body, n4 = forced(body, force)
|
|
fa_body, na = forced(body, None)
|
|
first = {min(force)}
|
|
f1_body, n1 = forced(body, first)
|
|
pr_body, npr = forced(body, first, pair=True)
|
|
assert n1 == 1 and npr == 1
|
|
assert n4 == len(force), f"forced {n4} of {len(force)} sites"
|
|
assert na == 16, f"forced {na} of 16 load slots"
|
|
|
|
launcher = """
|
|
cudaError_t f9_launch_{name}(const uint32_t* ds, uint64_t* out, uint32_t baseNonce, uint32_t mask,
|
|
{argtype} 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;
|
|
f9_hash_{name}<<<nonces / block, block>>>(ds, out, baseNonce, mask, iw);
|
|
return cudaGetLastError();
|
|
}}
|
|
"""
|
|
with open(out_path, "w") as f:
|
|
f.write("// Generated by tools/attack/f9-grind/pod/make-variants.py from " + src_path + ". Attack-pass F9 variants; not a miner.\n")
|
|
f.write(head)
|
|
f.write("__global__ void f9_hash_perwarp(" + sig_tbl + ") {" + perwarp_body + "\n")
|
|
f.write("__global__ void f9_hash_forced4(" + sig + ") {" + f4_body + "\n")
|
|
f.write("__global__ void f9_hash_forcedall(" + sig + ") {" + fa_body + "\n")
|
|
f.write("__global__ void f9_hash_forced1(" + sig + ") {" + f1_body + "\n")
|
|
f.write("__global__ void f9_hash_pair(" + sig + ") {" + pr_body + "\n")
|
|
f.write(launcher.format(name="perwarp", argtype="const IgneumInitWords*"))
|
|
f.write(launcher.format(name="forced4", argtype="IgneumInitWords"))
|
|
f.write(launcher.format(name="forcedall", argtype="IgneumInitWords"))
|
|
f.write(launcher.format(name="forced1", argtype="IgneumInitWords"))
|
|
f.write(launcher.format(name="pair", argtype="IgneumInitWords"))
|
|
print(f"wrote {out_path}: perwarp (table init), forced4 ({n4} sites {sorted(force)}), forcedall ({na} load slots), forced1 and pair (site {min(force)})")
|