リポジトリ / emssr / independent_common_compiler
r2
| r2 emssr | 1 | # -*- coding: utf-8 -*- |
| r2 emssr | 2 | """lane64 の GPU 実行台(limb 版)。thread 一本が lane 一本になる。 |
| r2 emssr | 3 | |
| r2 emssr | 4 | 仕様は docs/common-compiler/arch/20。式 (2.1) の解釈器と同じ意味を、CUDA の kernel の中で |
| r2 emssr | 5 | lane ごとに走らせる。cuvcpu.py は語を bit 平面に持ち、命令一つが平面の演算の列になるので、 |
| r2 emssr | 6 | 語が広いと launch の数で足が止まる。ここでは語を 64 bit の limb の並びで持ち、lane ごとの |
| r2 emssr | 7 | thread が block を跨いで命令を辿る。分岐は thread ごとに好きに飛ぶ(mask は要らない)。 |
| r2 emssr | 8 | |
| r2 emssr | 9 | 並び 語 主。語 w の limb i、lane l は M[(w·NLIMB + i)·lanes + l] に置く。 |
| r2 emssr | 10 | 同じ語を読む lane の番地が連続するので、warp の memory access が一度で済む。 |
| r2 emssr | 11 | lane 主に置くと lane ごとに words·NLIMB 語ずつ離れ、一命令が 32 回の転送になる |
| r2 emssr | 12 | 語 NLIMB 本の limb。二の補数、little endian。既定 16 本 = 1024 bit |
| r2 emssr | 13 | landmark R[0..7]。commonc の lane64 版は R1 R2 R3 だけを使う(back_lane64.cam の覆い) |
| r2 emssr | 14 | 作業領域 lane ごとに words 語。読む語(watch)だけを host へ戻す |
| r2 emssr | 15 | |
| r2 emssr | 16 | 語の幅を越えた乗算は報告 43 で止まる。lane64 の語は ℤ なので(20 §0.3)、この道は |
| r2 emssr | 17 | NLIMB·64 bit を越えた時点で core 意味論と離れる。 |
| r2 emssr | 18 | |
| r2 emssr | 19 | run(prog, seeds, words, watch, need=None) → lane ごとの (報告, {語: 値}) |
| r2 emssr | 20 | """ |
| r2 emssr | 21 | |
| r2 emssr | 22 | import numpy as np |
| r2 emssr | 23 | |
| r2 emssr | 24 | INSN_FIELDS = 7 |
| r2 emssr | 25 | NREG_BYTES = 8 * 8 # shared memory へ置く register。NREG 本 × 8 byte |
| r2 emssr | 26 | R_OK, R_KO, R_WIDE, R_BUDGET = 0, 42, 43, 44 |
| r2 emssr | 27 | |
| r2 emssr | 28 | SRC = r""" |
| r2 emssr | 29 | #define NLIMB %(nlimb)d |
| r2 emssr | 30 | #define NREG 8 |
| r2 emssr | 31 | typedef unsigned long long u64; |
| r2 emssr | 32 | typedef long long i64; |
| r2 emssr | 33 | |
| r2 emssr | 34 | struct Big { u64 v[NLIMB]; }; |
| r2 emssr | 35 | |
| r2 emssr | 36 | __device__ __forceinline__ void big_zero(Big &r) { for (int i = 0; i < NLIMB; i++) r.v[i] = 0; } |
| r2 emssr | 37 | __device__ __forceinline__ void big_imm(Big &r, i64 x) { |
| r2 emssr | 38 | r.v[0] = (u64)x; u64 f = (x < 0) ? ~0ULL : 0ULL; |
| r2 emssr | 39 | for (int i = 1; i < NLIMB; i++) r.v[i] = f; |
| r2 emssr | 40 | } |
| r2 emssr | 41 | __device__ __forceinline__ bool big_neg_p(const Big &a) { return (a.v[NLIMB - 1] >> 63) != 0; } |
| r2 emssr | 42 | __device__ __forceinline__ void big_add(Big &r, const Big &a, const Big &b) { |
| r2 emssr | 43 | u64 c = 0; |
| r2 emssr | 44 | for (int i = 0; i < NLIMB; i++) { |
| r2 emssr | 45 | u64 s = a.v[i] + b.v[i]; u64 c1 = (s < a.v[i]) ? 1 : 0; |
| r2 emssr | 46 | u64 t = s + c; u64 c2 = (t < s) ? 1 : 0; |
| r2 emssr | 47 | r.v[i] = t; c = c1 | c2; |
| r2 emssr | 48 | } |
| r2 emssr | 49 | } |
| r2 emssr | 50 | __device__ __forceinline__ void big_not(Big &r, const Big &a) { for (int i = 0; i < NLIMB; i++) r.v[i] = ~a.v[i]; } |
| r2 emssr | 51 | __device__ __forceinline__ void big_negate(Big &r, const Big &a) { |
| r2 emssr | 52 | Big n; big_not(n, a); Big one; big_imm(one, 1); big_add(r, n, one); |
| r2 emssr | 53 | } |
| r2 emssr | 54 | __device__ __forceinline__ void big_sub(Big &r, const Big &a, const Big &b) { Big nb; big_negate(nb, b); big_add(r, a, nb); } |
| r2 emssr | 55 | __device__ __forceinline__ bool big_eq(const Big &a, const Big &b) { |
| r2 emssr | 56 | for (int i = 0; i < NLIMB; i++) if (a.v[i] != b.v[i]) return false; return true; |
| r2 emssr | 57 | } |
| r2 emssr | 58 | __device__ __forceinline__ bool big_lt(const Big &a, const Big &b) { |
| r2 emssr | 59 | bool na = big_neg_p(a), nb = big_neg_p(b); |
| r2 emssr | 60 | if (na != nb) return na; |
| r2 emssr | 61 | for (int i = NLIMB - 1; i >= 0; i--) { if (a.v[i] != b.v[i]) return a.v[i] < b.v[i]; } |
| r2 emssr | 62 | return false; |
| r2 emssr | 63 | } |
| r2 emssr | 64 | __device__ __forceinline__ void big_shl(Big &r, const Big &a, int s) { |
| r2 emssr | 65 | if (s <= 0) { r = a; return; } |
| r2 emssr | 66 | int w = s / 64, bts = s %% 64; |
| r2 emssr | 67 | Big t; big_zero(t); |
| r2 emssr | 68 | for (int i = NLIMB - 1; i >= w; i--) { |
| r2 emssr | 69 | u64 hi = a.v[i - w] << bts; |
| r2 emssr | 70 | u64 lo = (bts && i - w - 1 >= 0) ? (a.v[i - w - 1] >> (64 - bts)) : 0; |
| r2 emssr | 71 | t.v[i] = hi | lo; |
| r2 emssr | 72 | } |
| r2 emssr | 73 | r = t; |
| r2 emssr | 74 | } |
| r2 emssr | 75 | // |a|·|b| を 2·NLIMB limb で作り、上半分が零でなければ幅を越えたとみなす |
| r2 emssr | 76 | __device__ bool big_mul(Big &r, const Big &a0, const Big &b0) { |
| r2 emssr | 77 | Big a, b; bool na = big_neg_p(a0), nb = big_neg_p(b0); |
| r2 emssr | 78 | if (na) big_negate(a, a0); else a = a0; |
| r2 emssr | 79 | if (nb) big_negate(b, b0); else b = b0; |
| r2 emssr | 80 | // 上位の零の limb は掛けない。小さい数どうしなら 1 回の乗算で済む |
| r2 emssr | 81 | int la = 0, lb = 0; |
| r2 emssr | 82 | for (int i = 0; i < NLIMB; i++) { if (a.v[i]) la = i + 1; if (b.v[i]) lb = i + 1; } |
| r2 emssr | 83 | u64 acc[2 * NLIMB]; |
| r2 emssr | 84 | for (int i = 0; i < 2 * NLIMB; i++) acc[i] = 0; |
| r2 emssr | 85 | for (int i = 0; i < la; i++) { |
| r2 emssr | 86 | u64 carry = 0; |
| r2 emssr | 87 | for (int j = 0; j < lb; j++) { |
| r2 emssr | 88 | u64 lo = a.v[i] * b.v[j]; |
| r2 emssr | 89 | u64 hi = __umul64hi(a.v[i], b.v[j]); |
| r2 emssr | 90 | u64 s = acc[i + j] + lo; u64 c1 = (s < lo) ? 1 : 0; |
| r2 emssr | 91 | u64 t = s + carry; u64 c2 = (t < s) ? 1 : 0; |
| r2 emssr | 92 | acc[i + j] = t; |
| r2 emssr | 93 | carry = hi + c1 + c2; |
| r2 emssr | 94 | } |
| r2 emssr | 95 | int k = i + lb; |
| r2 emssr | 96 | while (carry && k < 2 * NLIMB) { u64 s = acc[k] + carry; carry = (s < carry) ? 1 : 0; acc[k] = s; k++; } |
| r2 emssr | 97 | } |
| r2 emssr | 98 | bool wide = false; |
| r2 emssr | 99 | for (int i = NLIMB; i < 2 * NLIMB; i++) if (acc[i]) wide = true; |
| r2 emssr | 100 | if (acc[NLIMB - 1] >> 63) wide = true; |
| r2 emssr | 101 | for (int i = 0; i < NLIMB; i++) r.v[i] = acc[i]; |
| r2 emssr | 102 | if (na != nb) { Big t = r; big_negate(r, t); } |
| r2 emssr | 103 | return wide; |
| r2 emssr | 104 | } |
| r2 emssr | 105 | |
| r2 emssr | 106 | // 語の番地 → 詰めた並びの位置。work が 0 なら詰めない |
| r2 emssr | 107 | __device__ __forceinline__ i64 slot(i64 a, int need, int work, int words) { |
| r2 emssr | 108 | if (work == 0) return (a >= 0 && a < words) ? a : -1; |
| r2 emssr | 109 | if (a >= 0 && a < need) return a; |
| r2 emssr | 110 | if (a >= work && a - work + need < words) return a - work + need; |
| r2 emssr | 111 | return -1; |
| r2 emssr | 112 | } |
| r2 emssr | 113 | |
| r2 emssr | 114 | extern "C" __global__ |
| r2 emssr | 115 | void lane64_run(const i64 *prog, int ninsn, u64 *M, int words, int lanes, i64 budget, |
| r2 emssr | 116 | int need, int work, int *report, i64 *steps, |
| r2 emssr | 117 | const u64 *ROM, i64 romlo, i64 romn) |
| r2 emssr | 118 | { |
| r2 emssr | 119 | int lane = blockIdx.x * blockDim.x + threadIdx.x; |
| r2 emssr | 120 | if (lane >= lanes) return; |
| r2 emssr | 121 | Big R[NREG]; |
| r2 emssr | 122 | int pc = 0; i64 n = 0; int rep = %(r_budget)d; |
| r2 emssr | 123 | while (n < budget) { |
| r2 emssr | 124 | n++; |
| r2 emssr | 125 | if (pc < 0 || pc >= ninsn) { rep = %(r_end)d; break; } |
| r2 emssr | 126 | const i64 *ins = prog + (size_t)pc * %(fields)d; |
| r2 emssr | 127 | int op = (int)ins[0], a = (int)ins[1], b = (int)ins[2], c = (int)ins[3]; |
| r2 emssr | 128 | int sb = (int)ins[4], sc = (int)ins[5]; i64 im = ins[6]; |
| r2 emssr | 129 | if (op == 0) { rep = %(r_end)d; break; } // HALT |
| r2 emssr | 130 | else if (op == 1) { big_imm(R[a], im); pc++; } // MAT |
| r2 emssr | 131 | else if (op == 2) { Big x, y; big_shl(x, R[b], sb); big_shl(y, R[c], sc); big_add(R[a], x, y); pc++; } // ADD |
| r2 emssr | 132 | else if (op == 3) { Big t; if (big_mul(t, R[b], R[c])) { rep = %(r_wide)d; break; } R[a] = t; pc++; } // MUL |
| r2 emssr | 133 | else if (op == 4) { Big t; big_negate(t, R[b]); R[a] = t; pc++; } // NEG |
| r2 emssr | 134 | else if (op == 5) { Big x, y; big_shl(x, R[b], sb); big_shl(y, R[c], sc); pc += big_eq(x, y) ? (int)im : 1; } // EQ |
| r2 emssr | 135 | else if (op == 6) { Big x, y; big_shl(x, R[b], sb); big_shl(y, R[c], sc); pc += big_lt(x, y) ? (int)im : 1; } // LT |
| r2 emssr | 136 | else if (op == 7) { pc += (int)im; } // JMP |
| r2 emssr | 137 | else if (op == 8) { // LD |
| r2 emssr | 138 | i64 raw = (i64)R[b].v[0]; |
| r2 emssr | 139 | if (raw >= romlo) { // 共有帯。lane で割らない |
| r2 emssr | 140 | i64 o = raw - romlo; if (o >= romn) { rep = %(r_end)d; break; } |
| r2 emssr | 141 | const u64 *w = ROM + (size_t)o * NLIMB; |
| r2 emssr | 142 | for (int i = 0; i < NLIMB; i++) R[a].v[i] = w[i]; pc++; |
| r2 emssr | 143 | } else { |
| r2 emssr | 144 | i64 addr = slot(raw, need, work, words); if (addr < 0) { rep = %(r_end)d; break; } |
| r2 emssr | 145 | const u64 *w = M + ((size_t)addr * NLIMB) * (size_t)lanes + lane; |
| r2 emssr | 146 | for (int i = 0; i < NLIMB; i++) R[a].v[i] = w[(size_t)i * lanes]; pc++; |
| r2 emssr | 147 | } |
| r2 emssr | 148 | } |
| r2 emssr | 149 | else if (op == 9) { // ST |
| r2 emssr | 150 | i64 raw = (i64)R[b].v[0]; |
| r2 emssr | 151 | if (raw >= romlo) { rep = %(r_end)d; break; } // 共有帯へは書けない |
| r2 emssr | 152 | i64 addr = slot(raw, need, work, words); if (addr < 0) { rep = %(r_end)d; break; } |
| r2 emssr | 153 | u64 *w = M + ((size_t)addr * NLIMB) * (size_t)lanes + lane; |
| r2 emssr | 154 | for (int i = 0; i < NLIMB; i++) w[(size_t)i * lanes] = R[c].v[i]; pc++; |
| r2 emssr | 155 | } |
| r2 emssr | 156 | else if (op == 10) { rep = %(r_ok)d; break; } // OK |
| r2 emssr | 157 | else if (op == 11) { rep = %(r_ko)d; break; } // KO |
| r2 emssr | 158 | else if (op == 12) { pc++; } // PUT(GPU では捨てる) |
| r2 emssr | 159 | else { rep = %(r_end)d; break; } |
| r2 emssr | 160 | } |
| r2 emssr | 161 | report[lane] = rep; steps[lane] = n; |
| r2 emssr | 162 | } |
| r2 emssr | 163 | |
| r2 emssr | 164 | |
| r2 emssr | 165 | // ===== 語が 64 bit で足りるときの道 ===== |
| r2 emssr | 166 | // |
| r2 emssr | 167 | // Big R[NREG] は添字が命令から来るので、compiler が register に置けず local memory へ落ちる。 |
| r2 emssr | 168 | // 一命令ごとに local への往復が入り、thread 一本の歩みが桁で遅くなる。 |
| r2 emssr | 169 | // 語を 64 bit に決め打てる場合は、register を shared memory の |
| r2 emssr | 170 | // |
| r2 emssr | 171 | // R[k] = sreg[k * blockDim.x + threadIdx.x] |
| r2 emssr | 172 | // |
| r2 emssr | 173 | // へ置く。stride が blockDim.x なので bank が衝突せず、添字が動いても速い。 |
| r2 emssr | 174 | // 積が 64 bit を越えたら幅の報告で止める(意味は Big の道と同じ)。 |
| r2 emssr | 175 | |
| r2 emssr | 176 | extern "C" __global__ |
| r2 emssr | 177 | void lane64_run1(const i64 *prog, int ninsn, u64 *M, int words, int lanes, i64 budget, |
| r2 emssr | 178 | int need, int work, int *report, i64 *steps, |
| r2 emssr | 179 | const u64 *ROM, i64 romlo, i64 romn) |
| r2 emssr | 180 | { |
| r2 emssr | 181 | extern __shared__ i64 sreg[]; |
| r2 emssr | 182 | int lane = blockIdx.x * blockDim.x + threadIdx.x; |
| r2 emssr | 183 | if (lane >= lanes) return; |
| r2 emssr | 184 | i64 *R = sreg + threadIdx.x; |
| r2 emssr | 185 | const int rs = blockDim.x; |
| r2 emssr | 186 | int pc = 0; i64 n = 0; int rep = %(r_budget)d; |
| r2 emssr | 187 | while (n < budget) { |
| r2 emssr | 188 | n++; |
| r2 emssr | 189 | if (pc < 0 || pc >= ninsn) { rep = %(r_end)d; break; } |
| r2 emssr | 190 | const i64 *ins = prog + (size_t)pc * %(fields)d; |
| r2 emssr | 191 | int op = (int)ins[0], a = (int)ins[1], b = (int)ins[2], c = (int)ins[3]; |
| r2 emssr | 192 | int sb = (int)ins[4], sc = (int)ins[5]; i64 im = ins[6]; |
| r2 emssr | 193 | if (op == 0) { rep = %(r_end)d; break; } |
| r2 emssr | 194 | else if (op == 1) { R[a * rs] = im; pc++; } |
| r2 emssr | 195 | else if (op == 2) { R[a * rs] = (R[b * rs] << sb) + (R[c * rs] << sc); pc++; } |
| r2 emssr | 196 | else if (op == 3) { |
| r2 emssr | 197 | i64 x = R[b * rs], y = R[c * rs]; |
| r2 emssr | 198 | u64 ux = (u64)(x < 0 ? -x : x), uy = (u64)(y < 0 ? -y : y); |
| r2 emssr | 199 | if (__umul64hi(ux, uy) || ((ux * uy) >> 63)) { rep = %(r_wide)d; break; } |
| r2 emssr | 200 | R[a * rs] = x * y; pc++; |
| r2 emssr | 201 | } |
| r2 emssr | 202 | else if (op == 4) { R[a * rs] = -R[b * rs]; pc++; } |
| r2 emssr | 203 | else if (op == 5) { pc += ((R[b * rs] << sb) == (R[c * rs] << sc)) ? (int)im : 1; } |
| r2 emssr | 204 | else if (op == 6) { pc += ((R[b * rs] << sb) < (R[c * rs] << sc)) ? (int)im : 1; } |
| r2 emssr | 205 | else if (op == 7) { pc += (int)im; } |
| r2 emssr | 206 | else if (op == 8) { |
| r2 emssr | 207 | i64 raw = R[b * rs]; |
| r2 emssr | 208 | if (raw >= romlo) { // 共有帯。lane で割らない |
| r2 emssr | 209 | i64 o = raw - romlo; if (o >= romn) { rep = %(r_end)d; break; } |
| r2 emssr | 210 | R[a * rs] = (i64)ROM[(size_t)o]; pc++; |
| r2 emssr | 211 | } else { |
| r2 emssr | 212 | i64 addr = slot(raw, need, work, words); if (addr < 0) { rep = %(r_end)d; break; } |
| r2 emssr | 213 | R[a * rs] = (i64)M[(size_t)addr * (size_t)lanes + lane]; pc++; |
| r2 emssr | 214 | } |
| r2 emssr | 215 | } |
| r2 emssr | 216 | else if (op == 9) { |
| r2 emssr | 217 | i64 raw = R[b * rs]; |
| r2 emssr | 218 | if (raw >= romlo) { rep = %(r_end)d; break; } // 共有帯へは書けない |
| r2 emssr | 219 | i64 addr = slot(raw, need, work, words); if (addr < 0) { rep = %(r_end)d; break; } |
| r2 emssr | 220 | M[(size_t)addr * (size_t)lanes + lane] = (u64)R[c * rs]; pc++; |
| r2 emssr | 221 | } |
| r2 emssr | 222 | else if (op == 10) { rep = %(r_ok)d; break; } |
| r2 emssr | 223 | else if (op == 11) { rep = %(r_ko)d; break; } |
| r2 emssr | 224 | else if (op == 12) { pc++; } |
| r2 emssr | 225 | else { rep = %(r_end)d; break; } |
| r2 emssr | 226 | } |
| r2 emssr | 227 | report[lane] = rep; steps[lane] = n; |
| r2 emssr | 228 | } |
| r2 emssr | 229 | |
| r2 emssr | 230 | // ===== 多数の lane が一つの仕事を分ける道 ===== |
| r2 emssr | 231 | // |
| r2 emssr | 232 | // 段と段の間では、lane i が書いた値を lane j が読む。そのために |
| r2 emssr | 233 | // |
| r2 emssr | 234 | // 1. 共有帯へ書ける(既存の道は報告 45 で止める) |
| r2 emssr | 235 | // 2. 書いた後に grid 全体で足並みを揃える |
| r2 emssr | 236 | // |
| r2 emssr | 237 | // の二つが要る。合図は「共有帯への書き込み」そのものにする。境界へ書く所が |
| r2 emssr | 238 | // チェンバーの接合点なので、pc を外から指さなくて済む。全 lane が段ごとに |
| r2 emssr | 239 | // ちょうど一度ずつ書く形の平文でだけ使える。 |
| r2 emssr | 240 | // |
| r2 emssr | 241 | // 足並み揃えは世代を反転させる素朴な形。block が全部同時に載っていないと |
| r2 emssr | 242 | // 止まるので、grid は device に載る数までに抑える(host 側で決める)。 |
| r2 emssr | 243 | |
| r2 emssr | 244 | __device__ __forceinline__ void gbar(int *cnt, int *gen, int nblocks) |
| r2 emssr | 245 | { |
| r2 emssr | 246 | __syncthreads(); |
| r2 emssr | 247 | if (threadIdx.x == 0) { |
| r2 emssr | 248 | int g = *((volatile int *)gen); |
| r2 emssr | 249 | __threadfence(); |
| r2 emssr | 250 | if (atomicAdd(cnt, 1) == nblocks - 1) { |
| r2 emssr | 251 | *cnt = 0; |
| r2 emssr | 252 | __threadfence(); |
| r2 emssr | 253 | atomicExch(gen, g + 1); |
| r2 emssr | 254 | } else { |
| r2 emssr | 255 | while (*((volatile int *)gen) == g) __threadfence(); |
| r2 emssr | 256 | } |
| r2 emssr | 257 | } |
| r2 emssr | 258 | __syncthreads(); |
| r2 emssr | 259 | } |
| r2 emssr | 260 | |
| r2 emssr | 261 | extern "C" __global__ |
| r2 emssr | 262 | void lane64_runb(const i64 *prog, int ninsn, u64 *M, int words, int lanes, i64 budget, |
| r2 emssr | 263 | int need, int work, int *report, i64 *steps, |
| r2 emssr | 264 | u64 *ROM, i64 romlo, i64 romn, int *cnt, int *gen, int nblocks) |
| r2 emssr | 265 | { |
| r2 emssr | 266 | extern __shared__ i64 sregb[]; |
| r2 emssr | 267 | int lane = blockIdx.x * blockDim.x + threadIdx.x; |
| r2 emssr | 268 | i64 *R = sregb + threadIdx.x; |
| r2 emssr | 269 | const int rs = blockDim.x; |
| r2 emssr | 270 | // 足並みを揃える以上、休んでいる thread を作れない。lanes は grid x block に |
| r2 emssr | 271 | // ちょうど合わせる(余りの lane は自分の席で同じ文を走らせる)。 |
| r2 emssr | 272 | int pc = 0; i64 n = 0; int rep = %(r_budget)d; |
| r2 emssr | 273 | while (n < budget) { |
| r2 emssr | 274 | n++; |
| r2 emssr | 275 | if (pc < 0 || pc >= ninsn) { rep = %(r_end)d; break; } |
| r2 emssr | 276 | const i64 *ins = prog + (size_t)pc * %(fields)d; |
| r2 emssr | 277 | int op = (int)ins[0], a = (int)ins[1], b = (int)ins[2], c = (int)ins[3]; |
| r2 emssr | 278 | int sb = (int)ins[4], sc = (int)ins[5]; i64 im = ins[6]; |
| r2 emssr | 279 | if (op == 0) { rep = %(r_end)d; break; } |
| r2 emssr | 280 | else if (op == 1) { R[a * rs] = im; pc++; } |
| r2 emssr | 281 | else if (op == 2) { R[a * rs] = (R[b * rs] << sb) + (R[c * rs] << sc); pc++; } |
| r2 emssr | 282 | else if (op == 3) { |
| r2 emssr | 283 | i64 x = R[b * rs], y = R[c * rs]; |
| r2 emssr | 284 | u64 ux = (u64)(x < 0 ? -x : x), uy = (u64)(y < 0 ? -y : y); |
| r2 emssr | 285 | if (__umul64hi(ux, uy) || ((ux * uy) >> 63)) { rep = %(r_wide)d; break; } |
| r2 emssr | 286 | R[a * rs] = x * y; pc++; |
| r2 emssr | 287 | } |
| r2 emssr | 288 | else if (op == 4) { R[a * rs] = -R[b * rs]; pc++; } |
| r2 emssr | 289 | else if (op == 5) { pc += ((R[b * rs] << sb) == (R[c * rs] << sc)) ? (int)im : 1; } |
| r2 emssr | 290 | else if (op == 6) { pc += ((R[b * rs] << sb) < (R[c * rs] << sc)) ? (int)im : 1; } |
| r2 emssr | 291 | else if (op == 7) { pc += (int)im; } |
| r2 emssr | 292 | else if (op == 8) { |
| r2 emssr | 293 | i64 raw = R[b * rs]; |
| r2 emssr | 294 | if (raw >= romlo) { |
| r2 emssr | 295 | i64 o = raw - romlo; if (o >= romn) { rep = %(r_end)d; break; } |
| r2 emssr | 296 | R[a * rs] = (i64)ROM[(size_t)o]; pc++; |
| r2 emssr | 297 | } else { |
| r2 emssr | 298 | i64 addr = slot(raw, need, work, words); if (addr < 0) { rep = %(r_end)d; break; } |
| r2 emssr | 299 | R[a * rs] = (i64)M[(size_t)addr * (size_t)lanes + lane]; pc++; |
| r2 emssr | 300 | } |
| r2 emssr | 301 | } |
| r2 emssr | 302 | else if (op == 9) { |
| r2 emssr | 303 | i64 raw = R[b * rs]; |
| r2 emssr | 304 | if (raw >= romlo) { // 共有帯へ書く。ここが接合点 |
| r2 emssr | 305 | i64 o = raw - romlo; if (o >= romn) { rep = %(r_end)d; break; } |
| r2 emssr | 306 | ROM[(size_t)o] = (u64)R[c * rs]; |
| r2 emssr | 307 | pc++; |
| r2 emssr | 308 | __threadfence(); |
| r2 emssr | 309 | gbar(cnt, gen, nblocks); |
| r2 emssr | 310 | continue; |
| r2 emssr | 311 | } |
| r2 emssr | 312 | i64 addr = slot(raw, need, work, words); if (addr < 0) { rep = %(r_end)d; break; } |
| r2 emssr | 313 | M[(size_t)addr * (size_t)lanes + lane] = (u64)R[c * rs]; pc++; |
| r2 emssr | 314 | } |
| r2 emssr | 315 | else if (op == 10) { rep = %(r_ok)d; break; } |
| r2 emssr | 316 | else if (op == 11) { rep = %(r_ko)d; break; } |
| r2 emssr | 317 | else if (op == 12) { pc++; } |
| r2 emssr | 318 | else { rep = %(r_end)d; break; } |
| r2 emssr | 319 | } |
| r2 emssr | 320 | report[lane] = rep; steps[lane] = n; |
| r2 emssr | 321 | } |
| r2 emssr | 322 | |
| r2 emssr | 323 | // ===== warp を pc の小さい順にそろえて歩く道 ===== |
| r2 emssr | 324 | // |
| r2 emssr | 325 | // thread ごとに好きに飛ぶと、warp の中で pc が割れた瞬間から命令が直列になり、 |
| r2 emssr | 326 | // 語の読みも散って memory に縛られる(電力 50 W で頭打ち)。 |
| r2 emssr | 327 | // 20 §6 命題 6.1: 合流は最も小さい pc から取れば足りる。warp の中で最小の pc を |
| r2 emssr | 328 | // 一つ選び、そこに居る lane だけが一命令進む。分岐で割れても、後ろの lane が |
| r2 emssr | 329 | // 先に追いつくので、loop の出口で自然に合流する。 |
| r2 emssr | 330 | |
| r2 emssr | 331 | extern "C" __global__ |
| r2 emssr | 332 | void lane64_run1_sync(const i64 *prog, int ninsn, u64 *M, int words, int lanes, i64 budget, |
| r2 emssr | 333 | int need, int work, int *report, i64 *steps, |
| r2 emssr | 334 | const u64 *ROM, i64 romlo, i64 romn, int stride) |
| r2 emssr | 335 | { |
| r2 emssr | 336 | // stride は語の帯の中の lane の数(M2 の列数)。lanes はいま走らせる lane の数で、 |
| r2 emssr | 337 | // 先頭から lanes 本だけが歩く。段を長さの順に並べておけば、短い段が終わった後の |
| r2 emssr | 338 | // 歩は先頭の生きている lane だけで済む |
| r2 emssr | 339 | extern __shared__ i64 sreg[]; |
| r2 emssr | 340 | int lane = blockIdx.x * blockDim.x + threadIdx.x; |
| r2 emssr | 341 | bool alive = lane < lanes; |
| r2 emssr | 342 | i64 *R = sreg + threadIdx.x; |
| r2 emssr | 343 | const int rs = blockDim.x; |
| r2 emssr | 344 | int pc = 0; i64 n = 0; int rep = %(r_budget)d; |
| r2 emssr | 345 | bool done = !alive; |
| r2 emssr | 346 | while (true) { |
| r2 emssr | 347 | unsigned live = __ballot_sync(0xffffffffu, !done); |
| r2 emssr | 348 | if (live == 0) break; |
| r2 emssr | 349 | int mypc = done ? 0x7fffffff : pc; |
| r2 emssr | 350 | int pcmin = __reduce_min_sync(0xffffffffu, mypc); |
| r2 emssr | 351 | if (done || pc != pcmin) continue; |
| r2 emssr | 352 | n++; |
| r2 emssr | 353 | if (n > budget) { done = true; continue; } |
| r2 emssr | 354 | if (pc < 0 || pc >= ninsn) { rep = %(r_end)d; done = true; continue; } |
| r2 emssr | 355 | const i64 *ins = prog + (size_t)pc * %(fields)d; |
| r2 emssr | 356 | int op = (int)ins[0], a = (int)ins[1], b = (int)ins[2], c = (int)ins[3]; |
| r2 emssr | 357 | int sb = (int)ins[4], sc = (int)ins[5]; i64 im = ins[6]; |
| r2 emssr | 358 | if (op == 0) { rep = %(r_end)d; done = true; } |
| r2 emssr | 359 | else if (op == 1) { R[a * rs] = im; pc++; } |
| r2 emssr | 360 | else if (op == 2) { R[a * rs] = (R[b * rs] << sb) + (R[c * rs] << sc); pc++; } |
| r2 emssr | 361 | else if (op == 3) { |
| r2 emssr | 362 | i64 x = R[b * rs], y = R[c * rs]; |
| r2 emssr | 363 | u64 ux = (u64)(x < 0 ? -x : x), uy = (u64)(y < 0 ? -y : y); |
| r2 emssr | 364 | if (__umul64hi(ux, uy) || ((ux * uy) >> 63)) { rep = %(r_wide)d; done = true; } |
| r2 emssr | 365 | else { R[a * rs] = x * y; pc++; } |
| r2 emssr | 366 | } |
| r2 emssr | 367 | else if (op == 4) { R[a * rs] = -R[b * rs]; pc++; } |
| r2 emssr | 368 | else if (op == 5) { pc += ((R[b * rs] << sb) == (R[c * rs] << sc)) ? (int)im : 1; } |
| r2 emssr | 369 | else if (op == 6) { pc += ((R[b * rs] << sb) < (R[c * rs] << sc)) ? (int)im : 1; } |
| r2 emssr | 370 | else if (op == 7) { pc += (int)im; } |
| r2 emssr | 371 | else if (op == 8) { |
| r2 emssr | 372 | i64 raw = R[b * rs]; |
| r2 emssr | 373 | if (raw >= romlo) { |
| r2 emssr | 374 | i64 o = raw - romlo; if (o >= romn) { rep = %(r_end)d; done = true; } |
| r2 emssr | 375 | else { R[a * rs] = (i64)ROM[(size_t)o]; pc++; } |
| r2 emssr | 376 | } else { |
| r2 emssr | 377 | i64 addr = slot(raw, need, work, words); if (addr < 0) { rep = %(r_end)d; done = true; } |
| r2 emssr | 378 | else { R[a * rs] = (i64)M[(size_t)addr * (size_t)stride + lane]; pc++; } |
| r2 emssr | 379 | } |
| r2 emssr | 380 | } |
| r2 emssr | 381 | else if (op == 9) { |
| r2 emssr | 382 | i64 raw = R[b * rs]; |
| r2 emssr | 383 | if (raw >= romlo) { rep = %(r_end)d; done = true; } |
| r2 emssr | 384 | else { |
| r2 emssr | 385 | i64 addr = slot(raw, need, work, words); if (addr < 0) { rep = %(r_end)d; done = true; } |
| r2 emssr | 386 | else { M[(size_t)addr * (size_t)stride + lane] = (u64)R[c * rs]; pc++; } |
| r2 emssr | 387 | } |
| r2 emssr | 388 | } |
| r2 emssr | 389 | else if (op == 10) { rep = %(r_ok)d; done = true; } |
| r2 emssr | 390 | else if (op == 11) { rep = %(r_ko)d; done = true; } |
| r2 emssr | 391 | else if (op == 12) { pc++; } |
| r2 emssr | 392 | else { rep = %(r_end)d; done = true; } |
| r2 emssr | 393 | } |
| r2 emssr | 394 | if (alive) { report[lane] = rep; steps[lane] = n; } |
| r2 emssr | 395 | } |
| r2 emssr | 396 | |
| r2 emssr | 397 | extern "C" __global__ |
| r2 emssr | 398 | void gather(const u64 *M, int words, int lanes, const int *watch, int nwatch, u64 *out) |
| r2 emssr | 399 | { |
| r2 emssr | 400 | int lane = blockIdx.x * blockDim.x + threadIdx.x; |
| r2 emssr | 401 | if (lane >= lanes) return; |
| r2 emssr | 402 | u64 *o = out + (size_t)lane * nwatch * NLIMB; |
| r2 emssr | 403 | for (int k = 0; k < nwatch; k++) { |
| r2 emssr | 404 | const u64 *w = M + ((size_t)watch[k] * NLIMB) * (size_t)lanes + lane; |
| r2 emssr | 405 | for (int i = 0; i < NLIMB; i++) o[k * NLIMB + i] = w[(size_t)i * lanes]; |
| r2 emssr | 406 | } |
| r2 emssr | 407 | } |
| r2 emssr | 408 | |
| r2 emssr | 409 | // 先頭 lanes 本だけを集める。stride は帯の中の lane の数 |
| r2 emssr | 410 | extern "C" __global__ |
| r2 emssr | 411 | void gather_s(const u64 *M, int stride, int lanes, const int *watch, int nwatch, u64 *out) |
| r2 emssr | 412 | { |
| r2 emssr | 413 | int lane = blockIdx.x * blockDim.x + threadIdx.x; |
| r2 emssr | 414 | if (lane >= lanes) return; |
| r2 emssr | 415 | u64 *o = out + (size_t)lane * nwatch; |
| r2 emssr | 416 | for (int k = 0; k < nwatch; k++) o[k] = M[(size_t)watch[k] * stride + lane]; |
| r2 emssr | 417 | } |
| r2 emssr | 418 | """ |
| r2 emssr | 419 | |
| r2 emssr | 420 | R_END = 45 |
| r2 emssr | 421 | _MOD = {} |
| r2 emssr | 422 | |
| r2 emssr | 423 | |
| r2 emssr | 424 | # ===== block 内の即値を畳む ===== |
| r2 emssr | 425 | # |
| r2 emssr | 426 | # commonc が出す lane64 の命令列は、番地を one の倍化と加算で組み立てる。 |
| r2 emssr | 427 | # CPU 側の vcpu.translate は block ごとに値を記号で追い、即値どうしの算術を |
| r2 emssr | 428 | # 一つの即値へ畳む(casm.lane64.vn)。GPU の解釈器は命令を一つずつ歩くので、 |
| r2 emssr | 429 | # 同じ畳み込みを先に済ませておかないと、番地を綴る鎖がそのまま実行費用になる。 |
| r2 emssr | 430 | # |
| r2 emssr | 431 | # R1 = 1 ; R1 = R1+R1 ; ... ; R2 = R1+R3 → R1 = 65554 の一命令 |
| r2 emssr | 432 | # |
| r2 emssr | 433 | # block の入口で値が分かっている register は無い、として始める。分岐は畳まない。 |
| r2 emssr | 434 | |
| r2 emssr | 435 | MAT, ADD, MUL, NEG, EQ, LT, JMP, LD, ST = 1, 2, 3, 4, 5, 6, 7, 8, 9 |
| r2 emssr | 436 | _BR = (EQ, LT, JMP) |
| r2 emssr | 437 | _TERM = (0, 10, 11) |
| r2 emssr | 438 | |
| r2 emssr | 439 | |
| r2 emssr | 440 | def leaders(prog): |
| r2 emssr | 441 | """block の先頭になる命令の番号""" |
| r2 emssr | 442 | out = set([0]) |
| r2 emssr | 443 | for i, ins in enumerate(prog): |
| r2 emssr | 444 | op, im = ins[0], ins[6] |
| r2 emssr | 445 | if op in _BR: |
| r2 emssr | 446 | out.add(i + int(im)) |
| r2 emssr | 447 | out.add(i + 1) |
| r2 emssr | 448 | elif op in _TERM: |
| r2 emssr | 449 | out.add(i + 1) |
| r2 emssr | 450 | return sorted(a for a in out if 0 <= a < len(prog)) |
| r2 emssr | 451 | |
| r2 emssr | 452 | |
| r2 emssr | 453 | def _reads(ins): |
| r2 emssr | 454 | op = ins[0] |
| r2 emssr | 455 | if op in (ADD, EQ, LT, MUL): |
| r2 emssr | 456 | return (ins[2], ins[3]) |
| r2 emssr | 457 | if op in (NEG, LD): |
| r2 emssr | 458 | return (ins[2],) |
| r2 emssr | 459 | if op == ST: |
| r2 emssr | 460 | return (ins[2], ins[3]) |
| r2 emssr | 461 | if op == 12: |
| r2 emssr | 462 | return (ins[2],) |
| r2 emssr | 463 | return () |
| r2 emssr | 464 | |
| r2 emssr | 465 | |
| r2 emssr | 466 | def _writes(ins): |
| r2 emssr | 467 | op = ins[0] |
| r2 emssr | 468 | return ins[1] if op in (MAT, ADD, MUL, NEG, LD) else None |
| r2 emssr | 469 | |
| r2 emssr | 470 | |
| r2 emssr | 471 | def _walk(prog, lo, hi, live, dead, rewrite): |
| r2 emssr | 472 | """block をひと通り追う。live には「自分の Store より前に読んだ定数番地」を集め、 |
| r2 emssr | 473 | rewrite が真なら即値の算術と語への往復を畳み、dead の番地への Store を落とす。""" |
| r2 emssr | 474 | val, mem = {}, {} |
| r2 emssr | 475 | for i in range(lo, hi): |
| r2 emssr | 476 | ins = prog[i] |
| r2 emssr | 477 | if ins is None: |
| r2 emssr | 478 | continue |
| r2 emssr | 479 | op = ins[0] |
| r2 emssr | 480 | if op == MAT: |
| r2 emssr | 481 | val[ins[1]] = int(ins[6]) |
| r2 emssr | 482 | elif op in (ADD, MUL, NEG): |
| r2 emssr | 483 | b, c, sb, sc = ins[2], ins[3], ins[4], ins[5] |
| r2 emssr | 484 | vb = val.get(b) |
| r2 emssr | 485 | vc = val.get(c) if op != NEG else 0 |
| r2 emssr | 486 | if vb is not None and vc is not None: |
| r2 emssr | 487 | v = (vb << sb) + (vc << sc) if op == ADD else (vb * vc if op == MUL else -vb) |
| r2 emssr | 488 | if rewrite: |
| r2 emssr | 489 | prog[i] = [MAT, ins[1], 0, 0, 0, 0, v] |
| r2 emssr | 490 | val[ins[1]] = v |
| r2 emssr | 491 | else: |
| r2 emssr | 492 | val.pop(ins[1], None) |
| r2 emssr | 493 | elif op == LD: |
| r2 emssr | 494 | a = val.get(ins[2]) |
| r2 emssr | 495 | if a is not None and a in mem: |
| r2 emssr | 496 | if rewrite: |
| r2 emssr | 497 | prog[i] = [MAT, ins[1], 0, 0, 0, 0, mem[a]] |
| r2 emssr | 498 | val[ins[1]] = mem[a] |
| r2 emssr | 499 | else: |
| r2 emssr | 500 | if a is not None: |
| r2 emssr | 501 | live.add(a) |
| r2 emssr | 502 | val.pop(ins[1], None) |
| r2 emssr | 503 | elif op == ST: |
| r2 emssr | 504 | a, v = val.get(ins[2]), val.get(ins[3]) |
| r2 emssr | 505 | if a is None: |
| r2 emssr | 506 | mem.clear() |
| r2 emssr | 507 | elif v is None: |
| r2 emssr | 508 | mem.pop(a, None) |
| r2 emssr | 509 | else: |
| r2 emssr | 510 | mem[a] = v |
| r2 emssr | 511 | # 同じ block の Load は先に繋いでから落とす。落とすのは block を跨ぐ分だけ |
| r2 emssr | 512 | if rewrite and a is not None and a in dead: |
| r2 emssr | 513 | prog[i] = None |
| r2 emssr | 514 | else: |
| r2 emssr | 515 | w = _writes(ins) |
| r2 emssr | 516 | if w is not None: |
| r2 emssr | 517 | val.pop(w, None) |
| r2 emssr | 518 | |
| r2 emssr | 519 | |
| r2 emssr | 520 | def fold(prog, wires=None): |
| r2 emssr | 521 | """block の中で値を記号で追い、即値どうしの算術と語への往復を畳む。意味は変わらない。 |
| r2 emssr | 522 | |
| r2 emssr | 523 | lane64 の一箱は MAT・ST・LD の往復に開くので、register だけを追っても畳めない。 |
| r2 emssr | 524 | 定数番地への Store と、その番地からの Load を繋いで初めて鎖が一つの即値になる。 |
| r2 emssr | 525 | """ |
| r2 emssr | 526 | prog = [list(x) for x in prog] |
| r2 emssr | 527 | heads = leaders(prog) + [len(prog)] |
| r2 emssr | 528 | |
| r2 emssr | 529 | # 一巡目。block を跨いで読まれる定数番地を集める |
| r2 emssr | 530 | live = set() |
| r2 emssr | 531 | for k in range(len(heads) - 1): |
| r2 emssr | 532 | _walk(prog, heads[k], heads[k + 1], live, (), False) |
| r2 emssr | 533 | |
| r2 emssr | 534 | # commonc が出すワイヤの番地は定数だけで、本文の番地は WORK 以上にある。 |
| r2 emssr | 535 | # だから [64, need) のうち誰も跨いで読まない番地への Store は落としてよい |
| r2 emssr | 536 | dead = set() |
| r2 emssr | 537 | if wires: |
| r2 emssr | 538 | lo, hi = wires |
| r2 emssr | 539 | dead = set(a for a in range(lo, hi) if a not in live) |
| r2 emssr | 540 | |
| r2 emssr | 541 | # 二巡目。畳んで、死んだ Store を落とす |
| r2 emssr | 542 | for k in range(len(heads) - 1): |
| r2 emssr | 543 | lo, hi = heads[k], heads[k + 1] |
| r2 emssr | 544 | _walk(prog, lo, hi, set(), dead, True) |
| r2 emssr | 545 | # block の中だけの死んだ書き込みを落とす |
| r2 emssr | 546 | for k in range(len(heads) - 1): |
| r2 emssr | 547 | lo, hi = heads[k], heads[k + 1] |
| r2 emssr | 548 | last, drop = {}, set() |
| r2 emssr | 549 | for i in range(lo, hi): |
| r2 emssr | 550 | ins = prog[i] |
| r2 emssr | 551 | if ins is None: |
| r2 emssr | 552 | continue |
| r2 emssr | 553 | for r in _reads(ins): |
| r2 emssr | 554 | last.pop(r, None) |
| r2 emssr | 555 | w = _writes(ins) |
| r2 emssr | 556 | if w is not None: |
| r2 emssr | 557 | if ins[0] == MAT and w in last: |
| r2 emssr | 558 | drop.add(last[w]) |
| r2 emssr | 559 | last[w] = i if ins[0] == MAT else None |
| r2 emssr | 560 | if last[w] is None: |
| r2 emssr | 561 | last.pop(w) |
| r2 emssr | 562 | for i in drop: |
| r2 emssr | 563 | prog[i] = None |
| r2 emssr | 564 | # 落とした分だけ番号が詰まるので、分岐の変位を貼り直す |
| r2 emssr | 565 | keep = [i for i, x in enumerate(prog) if x is not None] |
| r2 emssr | 566 | newpos, n = {}, 0 |
| r2 emssr | 567 | for i in range(len(prog)): |
| r2 emssr | 568 | newpos[i] = n |
| r2 emssr | 569 | if prog[i] is not None: |
| r2 emssr | 570 | n += 1 |
| r2 emssr | 571 | newpos[len(prog)] = n |
| r2 emssr | 572 | out = [] |
| r2 emssr | 573 | for i in keep: |
| r2 emssr | 574 | ins = list(prog[i]) |
| r2 emssr | 575 | if ins[0] in _BR: |
| r2 emssr | 576 | ins[6] = newpos[min(i + int(ins[6]), len(prog))] - newpos[i] |
| r2 emssr | 577 | out.append(tuple(ins)) |
| r2 emssr | 578 | return out |
| r2 emssr | 579 | |
| r2 emssr | 580 | |
| r2 emssr | 581 | def compact(prog, wires, work=0): |
| r2 emssr | 582 | """ワイヤの番地を詰める。返りは (prog, 新しい need)。 |
| r2 emssr | 583 | |
| r2 emssr | 584 | commonc の一箱はワイヤ一本を語 64 + w に取るので、image が大きいと lane ごとの |
| r2 emssr | 585 | ワイヤの帯が数万語になる。畳んだ後に生き残る番地はその一部なので、番地として |
| r2 emssr | 586 | 使われている即値だけを 64 から順に付け直す。 |
| r2 emssr | 587 | |
| r2 emssr | 588 | 即値が番地か値かは block の中の使われ方で決める。MAT で置いた register が、 |
| r2 emssr | 589 | 上書きされるまでに LD / ST の番地としてしか使われなければ番地。値として |
| r2 emssr | 590 | 使われた即値は触らない。触らない即値がワイヤの帯に残れば、need は |
| r2 emssr | 591 | その最大までしか下がらない(返りの need がそれを示す)。 |
| r2 emssr | 592 | """ |
| r2 emssr | 593 | lo, hi = wires |
| r2 emssr | 594 | # 二値の八箱の退避語は定数番地の最大の上に置かれ、ワイヤの帯と本文の間の穴に落ちる |
| r2 emssr | 595 | # ことがある(16 §5.3)。穴は lane に無いので、そこも一緒に詰める |
| r2 emssr | 596 | hi = max(hi, work) if work else hi |
| r2 emssr | 597 | prog = [list(x) for x in prog] |
| r2 emssr | 598 | heads = leaders(prog) + [len(prog)] |
| r2 emssr | 599 | addr_mats, keep_hi = [], lo |
| r2 emssr | 600 | for k in range(len(heads) - 1): |
| r2 emssr | 601 | a, b = heads[k], heads[k + 1] |
| r2 emssr | 602 | for i in range(a, b): |
| r2 emssr | 603 | ins = prog[i] |
| r2 emssr | 604 | if ins[0] != MAT or not (lo <= ins[6] < hi): |
| r2 emssr | 605 | continue |
| r2 emssr | 606 | reg, ok, closed = ins[1], True, False |
| r2 emssr | 607 | for j in range(i + 1, b): |
| r2 emssr | 608 | jn = prog[j] |
| r2 emssr | 609 | op = jn[0] |
| r2 emssr | 610 | if op in (LD, ST) and jn[2] == reg and not (op == ST and jn[3] == reg): |
| r2 emssr | 611 | pass |
| r2 emssr | 612 | elif reg in _reads(jn): |
| r2 emssr | 613 | ok = False |
| r2 emssr | 614 | break |
| r2 emssr | 615 | if _writes(jn) == reg: |
| r2 emssr | 616 | closed = True |
| r2 emssr | 617 | break |
| r2 emssr | 618 | # block の外へ出る register は次の block でも番地として使われる(commonc は |
| r2 emssr | 619 | # ワイヤの番地を定数でしか作らない)。値として読まれた即値だけを残す |
| r2 emssr | 620 | if ok: |
| r2 emssr | 621 | addr_mats.append(i) |
| r2 emssr | 622 | else: |
| r2 emssr | 623 | keep_hi = max(keep_hi, ins[6] + 1) |
| r2 emssr | 624 | used = sorted(set(prog[i][6] for i in addr_mats)) |
| r2 emssr | 625 | base = keep_hi |
| r2 emssr | 626 | remap = {a: base + k for k, a in enumerate(used)} |
| r2 emssr | 627 | for i in addr_mats: |
| r2 emssr | 628 | prog[i][6] = remap[prog[i][6]] |
| r2 emssr | 629 | return [tuple(x) for x in prog], base + len(used) |
| r2 emssr | 630 | |
| r2 emssr | 631 | |
| r2 emssr | 632 | def _module(cp, nlimb): |
| r2 emssr | 633 | if nlimb not in _MOD: |
| r2 emssr | 634 | src = SRC % {"nlimb": nlimb, "fields": INSN_FIELDS, "r_ok": R_OK, "r_ko": R_KO, |
| r2 emssr | 635 | "r_wide": R_WIDE, "r_budget": R_BUDGET, "r_end": R_END} |
| r2 emssr | 636 | _MOD[nlimb] = cp.RawModule(code=src, options=("-std=c++11",)) |
| r2 emssr | 637 | return _MOD[nlimb] |
| r2 emssr | 638 | |
| r2 emssr | 639 | |
| r2 emssr | 640 | def to_limbs(v, nlimb): |
| r2 emssr | 641 | """整数を limb の並びへ(二の補数)""" |
| r2 emssr | 642 | mask = (1 << (64 * nlimb)) - 1 |
| r2 emssr | 643 | x = v & mask |
| r2 emssr | 644 | return [(x >> (64 * i)) & 0xFFFFFFFFFFFFFFFF for i in range(nlimb)] |
| r2 emssr | 645 | |
| r2 emssr | 646 | |
| r2 emssr | 647 | def from_limbs(limbs, nlimb): |
| r2 emssr | 648 | x = 0 |
| r2 emssr | 649 | for i, l in enumerate(limbs): |
| r2 emssr | 650 | x |= int(l) << (64 * i) |
| r2 emssr | 651 | if x >> (64 * nlimb - 1): |
| r2 emssr | 652 | x -= 1 << (64 * nlimb) |
| r2 emssr | 653 | return x |
| r2 emssr | 654 | |
| r2 emssr | 655 | |
| r2 emssr | 656 | _NOROM_BUF = {} |
| r2 emssr | 657 | |
| r2 emssr | 658 | |
| r2 emssr | 659 | def _NOROM(cp): |
| r2 emssr | 660 | """共有帯を使わないときに渡す一語の置き場。kernel は romlo = 2^62 で触らない""" |
| r2 emssr | 661 | if "b" not in _NOROM_BUF: |
| r2 emssr | 662 | _NOROM_BUF["b"] = cp.zeros((1,), dtype=cp.uint64) |
| r2 emssr | 663 | return _NOROM_BUF["b"] |
| r2 emssr | 664 | |
| r2 emssr | 665 | |
| r2 emssr | 666 | def pack(addr, need, work): |
| r2 emssr | 667 | """語番号 → 詰めた並びの位置。kernel の slot() と同じ式(式は device 側 §slot)。""" |
| r2 emssr | 668 | if work == 0: |
| r2 emssr | 669 | return addr |
| r2 emssr | 670 | if 0 <= addr < need: |
| r2 emssr | 671 | return addr |
| r2 emssr | 672 | return addr - work + need |
| r2 emssr | 673 | |
| r2 emssr | 674 | |
| r2 emssr | 675 | |
| r2 emssr | 676 | def run(prog, seeds, words, watch, nlimb=16, budget=1 << 26, chunk=64, need=0, work=0, |
| r2 emssr | 677 | base=None): |
| r2 emssr | 678 | """image(decode 済み)を seeds の lane 分だけ走らせる。 |
| r2 emssr | 679 | |
| r2 emssr | 680 | seeds は lane ごとの {語: 値}、watch は host へ戻す語の並び。 |
| r2 emssr | 681 | 返りは lane ごとの (報告, {語: 値})。報告は 0 = tu、42 = te、43 = 幅を越えた、44 = 歩数の上限。 |
| r2 emssr | 682 | |
| r2 emssr | 683 | base は全 lane へ同じ値を置く語({語: 値})。模型の表のように lane ごとに同じ物を |
| r2 emssr | 684 | 渡すとき、seeds へ毎回入れると host の側だけで lane × 語 の手間になる。ここでは |
| r2 emssr | 685 | 語ごとに一度だけ device の帯へ書くので、手間は語の数で済む。 |
| r2 emssr | 686 | |
| r2 emssr | 687 | need と work を渡すと、ワイヤ(0 .. need)と本文の作業領域(work 以上)の間の穴を詰める。 |
| r2 emssr | 688 | commonc の lane64 版は本文を WORK = 2^16 から置くので、詰めないと lane ごとに |
| r2 emssr | 689 | 65536 語ぶんの空きを確保することになる(20 §1.2 の lane の作業領域は交わらない)。 |
| r2 emssr | 690 | |
| r2 emssr | 691 | words = need + (本文が使う語の数) |
| r2 emssr | 692 | |
| r2 emssr | 693 | 穴を詰めると lane あたりの領域が減り、同じ memory でより多くの lane を並べられる。 |
| r2 emssr | 694 | """ |
| r2 emssr | 695 | import cupy as cp |
| r2 emssr | 696 | mod = _module(cp, nlimb) |
| r2 emssr | 697 | k_run = mod.get_function("lane64_run1" if nlimb == 1 else "lane64_run") |
| r2 emssr | 698 | k_gather = mod.get_function("gather") |
| r2 emssr | 699 | prog = fold(prog) |
| r2 emssr | 700 | table = np.array([list(p) for p in prog], dtype=np.int64).reshape(-1) |
| r2 emssr | 701 | d_prog = cp.asarray(table) |
| r2 emssr | 702 | d_watch = cp.asarray(np.array([pack(w, need, work) for w in watch], dtype=np.int32)) |
| r2 emssr | 703 | out = [] |
| r2 emssr | 704 | for start in range(0, len(seeds), chunk): |
| r2 emssr | 705 | part = seeds[start:start + chunk] |
| r2 emssr | 706 | lanes = len(part) |
| r2 emssr | 707 | M = cp.zeros((lanes * words * nlimb,), dtype=cp.uint64) |
| r2 emssr | 708 | if base: |
| r2 emssr | 709 | for addr, v in base.items(): |
| r2 emssr | 710 | a = pack(addr, need, work) |
| r2 emssr | 711 | v = int(v) |
| r2 emssr | 712 | if 0 <= v < (1 << 63): |
| r2 emssr | 713 | M[(a * nlimb) * lanes:(a * nlimb) * lanes + lanes] = np.uint64(v) |
| r2 emssr | 714 | else: |
| r2 emssr | 715 | for i, limb in enumerate(to_limbs(v, nlimb)): |
| r2 emssr | 716 | if limb: |
| r2 emssr | 717 | o = (a * nlimb + i) * lanes |
| r2 emssr | 718 | M[o:o + lanes] = np.uint64(limb) |
| r2 emssr | 719 | idx, vals = [], [] |
| r2 emssr | 720 | for l, seed in enumerate(part): |
| r2 emssr | 721 | for addr, v in seed.items(): |
| r2 emssr | 722 | a = pack(addr, need, work) |
| r2 emssr | 723 | v = int(v) |
| r2 emssr | 724 | if 0 <= v < (1 << 63): |
| r2 emssr | 725 | idx.append((a * nlimb) * lanes + l) |
| r2 emssr | 726 | vals.append(v) |
| r2 emssr | 727 | else: |
| r2 emssr | 728 | for i, limb in enumerate(to_limbs(v, nlimb)): |
| r2 emssr | 729 | if limb: |
| r2 emssr | 730 | idx.append((a * nlimb + i) * lanes + l) |
| r2 emssr | 731 | vals.append(limb) |
| r2 emssr | 732 | if idx: |
| r2 emssr | 733 | M[cp.asarray(np.array(idx, dtype=np.int64))] = cp.asarray(np.array(vals, dtype=np.uint64)) |
| r2 emssr | 734 | report = cp.zeros((lanes,), dtype=cp.int32) |
| r2 emssr | 735 | steps = cp.zeros((lanes,), dtype=cp.int64) |
| r2 emssr | 736 | threads = 128 |
| r2 emssr | 737 | grid = (lanes + threads - 1) // threads |
| r2 emssr | 738 | sh = threads * NREG_BYTES if nlimb == 1 else 0 |
| r2 emssr | 739 | k_run((grid,), (threads,), (d_prog, np.int32(len(prog)), M, np.int32(words), np.int32(lanes), |
| r2 emssr | 740 | np.int64(budget), np.int32(need), np.int32(work), report, steps, |
| r2 emssr | 741 | _NOROM(cp), np.int64(1 << 62), np.int64(0)), |
| r2 emssr | 742 | shared_mem=sh) |
| r2 emssr | 743 | got = cp.zeros((lanes * len(watch) * nlimb,), dtype=cp.uint64) |
| r2 emssr | 744 | if watch: |
| r2 emssr | 745 | k_gather((grid,), (threads,), (M, np.int32(words), np.int32(lanes), d_watch, np.int32(len(watch)), got)) |
| r2 emssr | 746 | rep = report.get() |
| r2 emssr | 747 | raw = got.get() |
| r2 emssr | 748 | for l in range(lanes): |
| r2 emssr | 749 | vals_l = {} |
| r2 emssr | 750 | for k, w in enumerate(watch): |
| r2 emssr | 751 | o = (l * len(watch) + k) * nlimb |
| r2 emssr | 752 | vals_l[w] = from_limbs(raw[o:o + nlimb], nlimb) |
| r2 emssr | 753 | out.append((int(rep[l]), vals_l)) |
| r2 emssr | 754 | del M, got |
| r2 emssr | 755 | return out |
| r2 emssr | 756 | |
| r2 emssr | 757 | |
| r2 emssr | 758 | def run_shared(prog, base, lanes, words, watch, need, work, nlimb=4, budget=1 << 26, |
| r2 emssr | 759 | chunk=2048, romlo=None, romn=0): |
| r2 emssr | 760 | """lane が共有する種 base に、lane ごとの少しの語 lanes[k] を重ねて走らせる。 |
| r2 emssr | 761 | |
| r2 emssr | 762 | 語の並びは詰める: ワイヤ [0, need) をそのまま、作業領域 [work, ...) を need の後ろに置く。 |
| r2 emssr | 763 | words は詰めた後の語数(need + 作業領域の幅)。平文の側の語は全部 work 以上にあるので、 |
| r2 emssr | 764 | 間の番地は使われない。返りは lane ごとの (報告, {語: 値})。 |
| r2 emssr | 765 | |
| r2 emssr | 766 | romlo を渡すと、番地 [romlo, romlo + romn) は**全 lane で一枚だけ**持つ。 |
| r2 emssr | 767 | 読むだけの表(値段の表など)をここへ置くと、lane あたりの重さが桁で落ちる。 |
| r2 emssr | 768 | |
| r2 emssr | 769 | lane あたりの語数 = need + (romlo - work) |
| r2 emssr | 770 | 共有帯の語数 = romn |
| r2 emssr | 771 | |
| r2 emssr | 772 | この帯へ Store すると報告 45 で止まる。書く語を入れてはいけない。 |
| r2 emssr | 773 | """ |
| r2 emssr | 774 | import cupy as cp |
| r2 emssr | 775 | mod = _module(cp, nlimb) |
| r2 emssr | 776 | k_run = mod.get_function("lane64_run1" if nlimb == 1 else "lane64_run") |
| r2 emssr | 777 | k_gather = mod.get_function("gather") |
| r2 emssr | 778 | prog = fold(prog, wires=(64, need)) |
| r2 emssr | 779 | table = np.array([list(p) for p in prog], dtype=np.int64).reshape(-1) |
| r2 emssr | 780 | d_prog = cp.asarray(table) |
| r2 emssr | 781 | hi = romlo if romlo is not None else (1 << 62) |
| r2 emssr | 782 | |
| r2 emssr | 783 | def pos(a): |
| r2 emssr | 784 | return a if a < need else a - work + need |
| r2 emssr | 785 | |
| r2 emssr | 786 | d_watch = cp.asarray(np.array([pos(w) for w in watch], dtype=np.int32)) |
| r2 emssr | 787 | # 共有の種を一枚の並びにする。共有帯の分は別の並びへ分ける |
| r2 emssr | 788 | row = np.zeros((words * nlimb,), dtype=np.uint64) |
| r2 emssr | 789 | rom = np.zeros((max(1, romn) * nlimb,), dtype=np.uint64) |
| r2 emssr | 790 | for addr, v in base.items(): |
| r2 emssr | 791 | v = int(v) |
| r2 emssr | 792 | p0 = (addr - hi) * nlimb if addr >= hi else pos(addr) * nlimb |
| r2 emssr | 793 | dst = rom if addr >= hi else row |
| r2 emssr | 794 | if 0 <= v < (1 << 63): |
| r2 emssr | 795 | dst[p0] = v |
| r2 emssr | 796 | else: |
| r2 emssr | 797 | for i, limb in enumerate(to_limbs(v, nlimb)): |
| r2 emssr | 798 | dst[p0 + i] = limb |
| r2 emssr | 799 | d_row = cp.asarray(row) |
| r2 emssr | 800 | d_rom = cp.asarray(rom) |
| r2 emssr | 801 | out = [] |
| r2 emssr | 802 | for start in range(0, len(lanes), chunk): |
| r2 emssr | 803 | part = lanes[start:start + chunk] |
| r2 emssr | 804 | n = len(part) |
| r2 emssr | 805 | M = cp.repeat(d_row, n) |
| r2 emssr | 806 | idx, vals = [], [] |
| r2 emssr | 807 | for l, seed in enumerate(part): |
| r2 emssr | 808 | for addr, v in seed.items(): |
| r2 emssr | 809 | idx.append((pos(addr) * nlimb) * n + l) |
| r2 emssr | 810 | vals.append(int(v)) |
| r2 emssr | 811 | if idx: |
| r2 emssr | 812 | M[cp.asarray(np.array(idx, dtype=np.int64))] = cp.asarray(np.array(vals, dtype=np.uint64)) |
| r2 emssr | 813 | report = cp.zeros((n,), dtype=cp.int32) |
| r2 emssr | 814 | steps = cp.zeros((n,), dtype=cp.int64) |
| r2 emssr | 815 | threads = 128 |
| r2 emssr | 816 | grid = (n + threads - 1) // threads |
| r2 emssr | 817 | sh = threads * NREG_BYTES if nlimb == 1 else 0 |
| r2 emssr | 818 | k_run((grid,), (threads,), (d_prog, np.int32(len(prog)), M, np.int32(words), np.int32(n), |
| r2 emssr | 819 | np.int64(budget), np.int32(need), np.int32(work), report, steps, |
| r2 emssr | 820 | d_rom, np.int64(hi), np.int64(romn)), |
| r2 emssr | 821 | shared_mem=sh) |
| r2 emssr | 822 | got = cp.zeros((n * len(watch) * nlimb,), dtype=cp.uint64) |
| r2 emssr | 823 | k_gather((grid,), (threads,), (M, np.int32(words), np.int32(n), d_watch, np.int32(len(watch)), got)) |
| r2 emssr | 824 | rep = report.get() |
| r2 emssr | 825 | raw = got.get().reshape(n, len(watch), nlimb) |
| r2 emssr | 826 | for l in range(n): |
| r2 emssr | 827 | out.append((int(rep[l]), dict((w, from_limbs(raw[l, k], nlimb)) for k, w in enumerate(watch)))) |
| r2 emssr | 828 | del M, got |
| r2 emssr | 829 | return out |
| r2 emssr | 830 | |
| r2 emssr | 831 | |
| r2 emssr | 832 | |
| r2 emssr | 833 | class Lanes(object): |
| r2 emssr | 834 | """lane の作業領域と共有帯を device に置いたまま、image を何度も流す。 |
| r2 emssr | 835 | |
| r2 emssr | 836 | host は語を一つずつ触らない。種は (lane, 語, 値) の並びを一度に散らし、読みは |
| r2 emssr | 837 | (lane × 語) の並びを一度に集める。どちらも cupy の一手で済むので、lane の数にも |
| r2 emssr | 838 | 語の数にも Python の反復が付かない。 |
| r2 emssr | 839 | |
| r2 emssr | 840 | L = Lanes(prog, need, work, romlo, romn, nlanes, base={語: 値}) |
| r2 emssr | 841 | L.put(lanes, addrs, vals) 種を散らす(三つとも同じ長さの並び) |
| r2 emssr | 842 | L.rom_put(offset, values) 共有帯の一部を書く |
| r2 emssr | 843 | rep, W = L.run(watch) rep[lane] が報告、W[lane, k] が watch[k] の値(device) |
| r2 emssr | 844 | L.M2 語 × lane の二次元の眺め(device) |
| r2 emssr | 845 | L.reset() 作業領域を base の並びへ戻す |
| r2 emssr | 846 | |
| r2 emssr | 847 | 語の並びは run_shared と同じ: ワイヤ [0, need) をそのまま、作業領域 [work, romlo) を |
| r2 emssr | 848 | need の後ろに詰める。M は語主で M2[pos(語), lane]。 |
| r2 emssr | 849 | |
| r2 emssr | 850 | 共有帯 rom は cupy の並び一枚。別の Lanes の M をそのまま rom に渡せる(rom=)ので、 |
| r2 emssr | 851 | lane の畳みのように「全 lane の語を読む」kernel は、その M を共有帯として読めば書ける。 |
| r2 emssr | 852 | """ |
| r2 emssr | 853 | |
| r2 emssr | 854 | def __init__(self, prog, need, work, romlo, romn, nlanes, base=None, nlimb=1, rom=None, sync=True): |
| r2 emssr | 855 | """sync が真なら warp を pc の小さい順にそろえて歩く(lane64_run1_sync)。""" |
| r2 emssr | 856 | import cupy as cp |
| r2 emssr | 857 | self.cp = cp |
| r2 emssr | 858 | if nlimb != 1: |
| r2 emssr | 859 | raise ValueError("Lanes は 64 bit の語だけを扱う(nlimb=1)") |
| r2 emssr | 860 | self.need, self.work, self.romlo = need, work, romlo |
| r2 emssr | 861 | self.words = need + (romlo - work) |
| r2 emssr | 862 | self.n = nlanes |
| r2 emssr | 863 | mod = _module(cp, 1) |
| r2 emssr | 864 | self.sync = sync |
| r2 emssr | 865 | self.k_run = mod.get_function("lane64_run1_sync" if sync else "lane64_run1") |
| r2 emssr | 866 | self.k_gather = mod.get_function("gather_s") |
| r2 emssr | 867 | prog = fold(prog, wires=(64, need)) |
| r2 emssr | 868 | prog, need = compact(prog, (64, need), work=work) |
| r2 emssr | 869 | self.need = need |
| r2 emssr | 870 | self.words = need + (romlo - work) |
| r2 emssr | 871 | self.nprog = len(prog) |
| r2 emssr | 872 | self.d_prog = cp.asarray(np.array([list(p) for p in prog], dtype=np.int64).reshape(-1)) |
| r2 emssr | 873 | if rom is not None: |
| r2 emssr | 874 | self.d_rom = rom |
| r2 emssr | 875 | self.romn = int(rom.size) |
| r2 emssr | 876 | else: |
| r2 emssr | 877 | self.romn = romn |
| r2 emssr | 878 | self.d_rom = cp.zeros((max(1, romn),), dtype=cp.uint64) |
| r2 emssr | 879 | row = np.zeros((self.words,), dtype=np.uint64) |
| r2 emssr | 880 | for addr, v in (base or {}).items(): |
| r2 emssr | 881 | row[self.pos(addr)] = int(v) |
| r2 emssr | 882 | self.d_row = cp.asarray(row) |
| r2 emssr | 883 | self.M = cp.zeros((self.words * nlanes,), dtype=cp.uint64) |
| r2 emssr | 884 | self.reset() |
| r2 emssr | 885 | |
| r2 emssr | 886 | def pos(self, a): |
| r2 emssr | 887 | return a if a < self.need else a - self.work + self.need |
| r2 emssr | 888 | |
| r2 emssr | 889 | @property |
| r2 emssr | 890 | def M2(self): |
| r2 emssr | 891 | return self.M.reshape(self.words, self.n) |
| r2 emssr | 892 | |
| r2 emssr | 893 | def reset(self): |
| r2 emssr | 894 | self.M2[:, :] = self.d_row.reshape(-1, 1) |
| r2 emssr | 895 | |
| r2 emssr | 896 | def put(self, lanes, addrs, vals): |
| r2 emssr | 897 | """(lane, 語, 値) の並びを一度に散らす。addrs は語番号(pos は中で当てる)。""" |
| r2 emssr | 898 | cp = self.cp |
| r2 emssr | 899 | lanes = cp.asarray(np.asarray(lanes, dtype=np.int64)) |
| r2 emssr | 900 | addrs = np.asarray(addrs, dtype=np.int64) |
| r2 emssr | 901 | addrs = np.where(addrs < self.need, addrs, addrs - self.work + self.need) |
| r2 emssr | 902 | idx = cp.asarray(addrs) * self.n + lanes |
| r2 emssr | 903 | self.M[idx] = cp.asarray(np.asarray(vals, dtype=np.uint64)) |
| r2 emssr | 904 | |
| r2 emssr | 905 | def put_block(self, addr, block): |
| r2 emssr | 906 | """語 addr から続く語の帯へ、(語数 × lane) の並びを一度に置く。block は device でも host でもよい。""" |
| r2 emssr | 907 | cp = self.cp |
| r2 emssr | 908 | b = cp.asarray(block, dtype=cp.uint64) |
| r2 emssr | 909 | self.M2[self.pos(addr):self.pos(addr) + b.shape[0], :] = b |
| r2 emssr | 910 | |
| r2 emssr | 911 | def get_block(self, addr, n): |
| r2 emssr | 912 | """語 addr から n 語の帯を (n × lane) で返す(device)。""" |
| r2 emssr | 913 | return self.M2[self.pos(addr):self.pos(addr) + n, :] |
| r2 emssr | 914 | |
| r2 emssr | 915 | def rom_put(self, offset, values): |
| r2 emssr | 916 | self.d_rom[offset:offset + len(values)] = self.cp.asarray(np.asarray(values, dtype=np.uint64)) |
| r2 emssr | 917 | |
| r2 emssr | 918 | def run(self, watch=(), budget=1 << 36, active=None): |
| r2 emssr | 919 | """先頭 active 本の lane を走らせる(省くと全部)。返りは (報告, watch の値) で、どちらも active 本ぶん。""" |
| r2 emssr | 920 | cp = self.cp |
| r2 emssr | 921 | n = self.n if active is None else min(active, self.n) |
| r2 emssr | 922 | report = cp.zeros((n,), dtype=cp.int32) |
| r2 emssr | 923 | steps = cp.zeros((n,), dtype=cp.int64) |
| r2 emssr | 924 | threads = 128 |
| r2 emssr | 925 | grid = (n + threads - 1) // threads |
| r2 emssr | 926 | if self.sync: |
| r2 emssr | 927 | args = (self.d_prog, np.int32(self.nprog), self.M, np.int32(self.words), np.int32(n), |
| r2 emssr | 928 | np.int64(budget), np.int32(self.need), np.int32(self.work), report, steps, |
| r2 emssr | 929 | self.d_rom, np.int64(self.romlo), np.int64(self.romn), np.int32(self.n)) |
| r2 emssr | 930 | else: |
| r2 emssr | 931 | if n != self.n: |
| r2 emssr | 932 | raise ValueError("先頭だけを走らせるには sync の道が要る") |
| r2 emssr | 933 | args = (self.d_prog, np.int32(self.nprog), self.M, np.int32(self.words), np.int32(n), |
| r2 emssr | 934 | np.int64(budget), np.int32(self.need), np.int32(self.work), report, steps, |
| r2 emssr | 935 | self.d_rom, np.int64(self.romlo), np.int64(self.romn)) |
| r2 emssr | 936 | self.k_run((grid,), (threads,), args, shared_mem=threads * NREG_BYTES) |
| r2 emssr | 937 | W = None |
| r2 emssr | 938 | if watch: |
| r2 emssr | 939 | d_watch = cp.asarray(np.array([self.pos(w) for w in watch], dtype=np.int32)) |
| r2 emssr | 940 | got = cp.zeros((n * len(watch),), dtype=cp.uint64) |
| r2 emssr | 941 | self.k_gather((grid,), (threads,), (self.M, np.int32(self.n), np.int32(n), d_watch, |
| r2 emssr | 942 | np.int32(len(watch)), got)) |
| r2 emssr | 943 | W = got.reshape(n, len(watch)).astype(cp.int64) |
| r2 emssr | 944 | return report.get(), W |