リポジトリ / emssr / independent_common_compiler
r2
| r2 emssr | 1 | # -*- coding: utf-8 -*- |
| r2 emssr | 2 | """lane64 の実行台。 |
| r2 emssr | 3 | |
| r2 emssr | 4 | 仕様は docs/common-compiler/arch/20。標準機能だけで書く。 |
| r2 emssr | 5 | |
| r2 emssr | 6 | 道は二本ある。 |
| r2 emssr | 7 | |
| r2 emssr | 8 | 解釈器 式 (2.1) の mask 付き意味をそのまま持つ。これが定義になる |
| r2 emssr | 9 | 翻訳器 block を Python の code object へ落とす。速い道になる |
| r2 emssr | 10 | |
| r2 emssr | 11 | 命題 6.1 が二本の一致を保証し、test_vcpu.py がそれを実際に確かめる。 |
| r2 emssr | 12 | lane の作業領域は式 (1.2) で交わらないので、翻訳器の道は lane を process へ配れる。 |
| r2 emssr | 13 | |
| r2 emssr | 14 | python vcpu.py image.bin --lanes 8 --words 65536 --procs 6 --in corpus.txt |
| r2 emssr | 15 | """ |
| r2 emssr | 16 | |
| r2 emssr | 17 | import os |
| r2 emssr | 18 | import sys |
| r2 emssr | 19 | import time |
| r2 emssr | 20 | |
| r2 emssr | 21 | INSN = 16 |
| r2 emssr | 22 | |
| r2 emssr | 23 | HALT, MAT, ADD, MUL, NEG, EQ, LT, JMP, LD, ST, OK, KO, PUT = range(13) |
| r2 emssr | 24 | |
| r2 emssr | 25 | NAME = ('HALT', 'MAT', 'ADD', 'MUL', 'NEG', 'EQ', 'LT', 'JMP', |
| r2 emssr | 26 | 'LD', 'ST', 'OK', 'KO', 'PUT') |
| r2 emssr | 27 | |
| r2 emssr | 28 | # block を閉じる箱(20 章 式 7.4) |
| r2 emssr | 29 | TERM = frozenset([EQ, LT, JMP, OK, KO, HALT]) |
| r2 emssr | 30 | |
| r2 emssr | 31 | # 報告の返り値。block の番号と混ざらないよう負で持つ |
| r2 emssr | 32 | R_OK, R_KO, R_END = -1, -2, -3 |
| r2 emssr | 33 | |
| r2 emssr | 34 | LANDMARKS = 256 |
| r2 emssr | 35 | |
| r2 emssr | 36 | |
| r2 emssr | 37 | # ===== 復号(20 章 §2)===== |
| r2 emssr | 38 | |
| r2 emssr | 39 | def decode(image): |
| r2 emssr | 40 | """image を欄の組の並びへ開く。 |
| r2 emssr | 41 | |
| r2 emssr | 42 | 固定 16 byte なので、長さを数える走査が要らない。欄の並びは |
| r2 emssr | 43 | op / a / b / c / s_b / s_c / imm で、全部 little endian になる。 |
| r2 emssr | 44 | """ |
| r2 emssr | 45 | if len(image) % INSN: |
| r2 emssr | 46 | raise ValueError('image の byte 数が %d の倍数でない: %d' % (INSN, len(image))) |
| r2 emssr | 47 | prog = [] |
| r2 emssr | 48 | for k in range(0, len(image), INSN): |
| r2 emssr | 49 | w = image[k:k + INSN] |
| r2 emssr | 50 | prog.append((w[0], w[1], w[2], w[3], |
| r2 emssr | 51 | w[4] | (w[5] << 8), w[6] | (w[7] << 8), |
| r2 emssr | 52 | int.from_bytes(w[8:16], 'little', signed=True))) |
| r2 emssr | 53 | return prog |
| r2 emssr | 54 | |
| r2 emssr | 55 | |
| r2 emssr | 56 | def encode(prog): |
| r2 emssr | 57 | """復号の逆(式 40.1、30.4)。二つを往復させると image が戻る。""" |
| r2 emssr | 58 | out = bytearray() |
| r2 emssr | 59 | for (op, a, b, c, sb, sc, im) in prog: |
| r2 emssr | 60 | out += bytes([op, a, b, c, sb & 255, sb >> 8, sc & 255, sc >> 8]) |
| r2 emssr | 61 | out += int(im).to_bytes(8, 'little', signed=True) |
| r2 emssr | 62 | return bytes(out) |
| r2 emssr | 63 | |
| r2 emssr | 64 | |
| r2 emssr | 65 | def disasm(prog): |
| r2 emssr | 66 | out = [] |
| r2 emssr | 67 | for i, (op, a, b, c, sb, sc, im) in enumerate(prog): |
| r2 emssr | 68 | out.append('%5d %-4s a=%-3d b=%-3d c=%-3d sb=%-2d sc=%-2d imm=%d' |
| r2 emssr | 69 | % (i, NAME[op] if op < len(NAME) else '?', a, b, c, sb, sc, im)) |
| r2 emssr | 70 | return out |
| r2 emssr | 71 | |
| r2 emssr | 72 | |
| r2 emssr | 73 | # ===== block(20 章 §7.3)===== |
| r2 emssr | 74 | |
| r2 emssr | 75 | def entries(prog): |
| r2 emssr | 76 | """block の入口(式 7.4)。分岐の行き先と、分岐の次が入口になる。""" |
| r2 emssr | 77 | n = len(prog) |
| r2 emssr | 78 | e = set([0]) |
| r2 emssr | 79 | for i, (op, _a, _b, _c, _sb, _sc, im) in enumerate(prog): |
| r2 emssr | 80 | if op in TERM: |
| r2 emssr | 81 | e.add(i + 1) |
| r2 emssr | 82 | if op in (EQ, LT, JMP): |
| r2 emssr | 83 | e.add(i + im) |
| r2 emssr | 84 | return sorted(x for x in e if 0 <= x < n) |
| r2 emssr | 85 | |
| r2 emssr | 86 | |
| r2 emssr | 87 | def blocks(prog): |
| r2 emssr | 88 | """(入口, 直線部分の箱, 閉じる箱 or None, 次の入口) の並び。 |
| r2 emssr | 89 | |
| r2 emssr | 90 | 閉じる箱が None のときは、次の入口へそのまま落ちる。 |
| r2 emssr | 91 | """ |
| r2 emssr | 92 | ent = entries(prog) |
| r2 emssr | 93 | n = len(prog) |
| r2 emssr | 94 | out = [] |
| r2 emssr | 95 | for k, pc in enumerate(ent): |
| r2 emssr | 96 | stop = ent[k + 1] if k + 1 < len(ent) else n |
| r2 emssr | 97 | j = pc |
| r2 emssr | 98 | body = [] |
| r2 emssr | 99 | while j < stop and prog[j][0] not in TERM: |
| r2 emssr | 100 | body.append(j) |
| r2 emssr | 101 | j += 1 |
| r2 emssr | 102 | out.append((pc, body, j if j < stop else None, stop)) |
| r2 emssr | 103 | return ent, dict((pc, k) for k, pc in enumerate(ent)), out |
| r2 emssr | 104 | |
| r2 emssr | 105 | |
| r2 emssr | 106 | # ===== 層(20 章 §7.1)===== |
| r2 emssr | 107 | |
| r2 emssr | 108 | def layers(prog, body): |
| r2 emssr | 109 | """直線部分の因果深度(式 7.1)と層(式 7.2)。 |
| r2 emssr | 110 | |
| r2 emssr | 111 | 実行の速さには効かない。効くのは「並べ替えてよい」という保証のほうで、 |
| r2 emssr | 112 | それが block を直線に書き下せる根拠になる(定理 21.1)。層の幅は |
| r2 emssr | 113 | その保証がどれだけ広いかの目安として出す。 |
| r2 emssr | 114 | """ |
| r2 emssr | 115 | depth, last, lay = {}, {}, [] |
| r2 emssr | 116 | for i in body: |
| r2 emssr | 117 | op, a, b, c = prog[i][0], prog[i][1], prog[i][2], prog[i][3] |
| r2 emssr | 118 | pred = () |
| r2 emssr | 119 | if op in (ADD, MUL): |
| r2 emssr | 120 | pred = (b, c) |
| r2 emssr | 121 | elif op in (NEG, LD): |
| r2 emssr | 122 | pred = (b,) |
| r2 emssr | 123 | elif op in (ST, PUT): |
| r2 emssr | 124 | pred = (b, c) if op == ST else (b,) |
| r2 emssr | 125 | d = 0 |
| r2 emssr | 126 | for r in pred: |
| r2 emssr | 127 | if r in last: |
| r2 emssr | 128 | d = max(d, last[r] + 1) |
| r2 emssr | 129 | depth[i] = d |
| r2 emssr | 130 | if op in (MAT, ADD, MUL, NEG, LD): |
| r2 emssr | 131 | last[a] = d |
| r2 emssr | 132 | while len(lay) <= d: |
| r2 emssr | 133 | lay.append([]) |
| r2 emssr | 134 | lay[d].append(i) |
| r2 emssr | 135 | return lay |
| r2 emssr | 136 | |
| r2 emssr | 137 | |
| r2 emssr | 138 | # ===== 層を同時に走らせる(定理 21.1)===== |
| r2 emssr | 139 | # |
| r2 emssr | 140 | # 層の中の箱は互いに独立なので、どの順で走らせても、同時に走らせても観測が変わらない。 |
| r2 emssr | 141 | # |
| r2 emssr | 142 | # (C1 ⊗ id);(id ⊗ C2) = C1 ⊗ C2 定理 21.1 |
| r2 emssr | 143 | # |
| r2 emssr | 144 | # 配るのは値の箱だけになる。作業領域を触る箱は番地がワイヤの値なので別名を排除できず |
| r2 emssr | 145 | # (§19)、本文の順のまま置く。だから値の箱の連なりを先に切り出し、その中でだけ |
| r2 emssr | 146 | # 層を作る。 |
| r2 emssr | 147 | # |
| r2 emssr | 148 | # 割に合うかは箱一つの重さで決まる。分子が Z なので、深い作図では一つの ⋆ が |
| r2 emssr | 149 | # 多倍長の乗算になり、配る費用を上回る。 |
| r2 emssr | 150 | |
| r2 emssr | 151 | _ARITH = frozenset([MAT, ADD, MUL, NEG]) |
| r2 emssr | 152 | |
| r2 emssr | 153 | |
| r2 emssr | 154 | def _box(job): |
| r2 emssr | 155 | """値の箱を一つ。ワイヤの値だけを受けるので、worker は state を持たない。""" |
| r2 emssr | 156 | op, a, u, v, sb, sc, im = job |
| r2 emssr | 157 | if op == MUL: |
| r2 emssr | 158 | return a, u * v |
| r2 emssr | 159 | if op == ADD: |
| r2 emssr | 160 | return a, (u << sb) + (v << sc) |
| r2 emssr | 161 | if op == MAT: |
| r2 emssr | 162 | return a, im |
| r2 emssr | 163 | if op == NEG: |
| r2 emssr | 164 | return a, -u |
| r2 emssr | 165 | raise ValueError('値の箱ではない: %s' % NAME[op]) |
| r2 emssr | 166 | |
| r2 emssr | 167 | |
| r2 emssr | 168 | def _runs(prog, body): |
| r2 emssr | 169 | """値の箱の連なりと、作業領域を触る箱を、本文の順に切り分ける。""" |
| r2 emssr | 170 | out, cur = [], [] |
| r2 emssr | 171 | for i in body: |
| r2 emssr | 172 | if prog[i][0] in _ARITH: |
| r2 emssr | 173 | cur.append(i) |
| r2 emssr | 174 | else: |
| r2 emssr | 175 | if cur: |
| r2 emssr | 176 | out.append((True, cur)) |
| r2 emssr | 177 | cur = [] |
| r2 emssr | 178 | out.append((False, i)) |
| r2 emssr | 179 | if cur: |
| r2 emssr | 180 | out.append((True, cur)) |
| r2 emssr | 181 | return out |
| r2 emssr | 182 | |
| r2 emssr | 183 | |
| r2 emssr | 184 | def _layers_hz(prog, seg): |
| r2 emssr | 185 | """並べ替えてよい層。式 (7.1) の Pred に WAR と WAW を足す。 |
| r2 emssr | 186 | |
| r2 emssr | 187 | landmark は allocator が使い回す(13 §13 の wire-to-landmark embedding)ので、 |
| r2 emssr | 188 | RAW だけでは並べ替えの根拠にならない。定理 21.1 の独立性はワイヤの上の話であり、 |
| r2 emssr | 189 | 割り当ての後まで自動では届かない。 |
| r2 emssr | 190 | """ |
| r2 emssr | 191 | lastw, lastr, lay = {}, {}, [] |
| r2 emssr | 192 | for i in seg: |
| r2 emssr | 193 | rs, w = _regs(prog, i) |
| r2 emssr | 194 | d = 0 |
| r2 emssr | 195 | for r in rs: |
| r2 emssr | 196 | if r in lastw: |
| r2 emssr | 197 | d = max(d, lastw[r] + 1) |
| r2 emssr | 198 | if w is not None: |
| r2 emssr | 199 | if w in lastw: |
| r2 emssr | 200 | d = max(d, lastw[w] + 1) |
| r2 emssr | 201 | if w in lastr: |
| r2 emssr | 202 | d = max(d, lastr[w] + 1) |
| r2 emssr | 203 | for r in rs: |
| r2 emssr | 204 | lastr[r] = max(lastr.get(r, -1), d) |
| r2 emssr | 205 | if w is not None: |
| r2 emssr | 206 | lastw[w] = d |
| r2 emssr | 207 | while len(lay) <= d: |
| r2 emssr | 208 | lay.append([]) |
| r2 emssr | 209 | lay[d].append(i) |
| r2 emssr | 210 | return lay |
| r2 emssr | 211 | |
| r2 emssr | 212 | |
| r2 emssr | 213 | def _job(prog, R, i): |
| r2 emssr | 214 | op, a, b, c, sb, sc, im = prog[i] |
| r2 emssr | 215 | return (op, a, R[b], R[c], sb, sc, im) |
| r2 emssr | 216 | |
| r2 emssr | 217 | |
| r2 emssr | 218 | def run_lane_par(prog, lane, pool=None, minw=2, budget=1 << 34): |
| r2 emssr | 219 | """block を層の順に走らせ、幅のある層を pool へ配る。""" |
| r2 emssr | 220 | R, M, OUT = lane.R, lane.M, lane.OUT |
| r2 emssr | 221 | _ent, idx, bl = blocks(prog) |
| r2 emssr | 222 | n, b = len(prog), 0 |
| r2 emssr | 223 | for step in range(budget): |
| r2 emssr | 224 | _pc, body, term, stop = bl[b] |
| r2 emssr | 225 | for arith, seg in _runs(prog, body): |
| r2 emssr | 226 | if not arith: |
| r2 emssr | 227 | op, a, u, v = prog[seg][0], prog[seg][1], prog[seg][2], prog[seg][3] |
| r2 emssr | 228 | if op == LD: |
| r2 emssr | 229 | R[a] = M[R[u]] |
| r2 emssr | 230 | elif op == ST: |
| r2 emssr | 231 | M[R[u]] = R[v] |
| r2 emssr | 232 | elif op == PUT: |
| r2 emssr | 233 | OUT.append(R[u] & 255) |
| r2 emssr | 234 | else: |
| r2 emssr | 235 | raise ValueError('直線部分に置けない箱: %s' % NAME[op]) |
| r2 emssr | 236 | continue |
| r2 emssr | 237 | for L in _layers_hz(prog, seg): |
| r2 emssr | 238 | outs = [prog[i][1] for i in L] |
| r2 emssr | 239 | wide = (pool is not None and len(L) >= minw |
| r2 emssr | 240 | and len(set(outs)) == len(outs)) |
| r2 emssr | 241 | if wide: |
| r2 emssr | 242 | for a, val in pool.map(_box, [_job(prog, R, i) for i in L]): |
| r2 emssr | 243 | R[a] = val |
| r2 emssr | 244 | else: |
| r2 emssr | 245 | for i in L: |
| r2 emssr | 246 | a, val = _box(_job(prog, R, i)) |
| r2 emssr | 247 | R[a] = val |
| r2 emssr | 248 | lane.blocks = step + 1 |
| r2 emssr | 249 | if term is None: |
| r2 emssr | 250 | if stop < n: |
| r2 emssr | 251 | b = idx[stop] |
| r2 emssr | 252 | continue |
| r2 emssr | 253 | lane.report = None |
| r2 emssr | 254 | return None |
| r2 emssr | 255 | op, _a, bb, cc, sb, sc, im = prog[term] |
| r2 emssr | 256 | if op == OK: |
| r2 emssr | 257 | lane.report = 0 |
| r2 emssr | 258 | return 0 |
| r2 emssr | 259 | if op == KO: |
| r2 emssr | 260 | lane.report = 42 |
| r2 emssr | 261 | return 42 |
| r2 emssr | 262 | if op == HALT: |
| r2 emssr | 263 | lane.report = None |
| r2 emssr | 264 | return None |
| r2 emssr | 265 | if op == JMP: |
| r2 emssr | 266 | t = term + im |
| r2 emssr | 267 | else: |
| r2 emssr | 268 | u, v = R[bb] << sb, R[cc] << sc |
| r2 emssr | 269 | t = term + im if ((u == v) if op == EQ else (u < v)) else term + 1 |
| r2 emssr | 270 | if 0 <= t < n and t in idx: |
| r2 emssr | 271 | b = idx[t] |
| r2 emssr | 272 | else: |
| r2 emssr | 273 | lane.report = None |
| r2 emssr | 274 | return None |
| r2 emssr | 275 | raise RuntimeError('block の歩数上限 %d を越えた' % budget) |
| r2 emssr | 276 | |
| r2 emssr | 277 | |
| r2 emssr | 278 | # ===== 翻訳(20 章 §7.2)===== |
| r2 emssr | 279 | |
| r2 emssr | 280 | # ===== 定理 21.1 の回収 ===== |
| r2 emssr | 281 | # |
| r2 emssr | 282 | # 層分解(式 7.1、7.2)は block を「state を共有しない独立 chamber の積」だと言う。 |
| r2 emssr | 283 | # |
| r2 emssr | 284 | # (C1 ⊗ id);(id ⊗ C2) = C1 ⊗ C2 定理 21.1 |
| r2 emssr | 285 | # |
| r2 emssr | 286 | # だから block の中で landmark file を辿る必要が無い。読む前に書かない landmark を |
| r2 emssr | 287 | # 入口で local へ写し、書いた landmark を出口で戻せば、間は local variable で足りる。 |
| r2 emssr | 288 | # CPython では R[k] が LOAD_FAST + LOAD_CONST + BINARY_SUBSCR、local は LOAD_FAST 一つ。 |
| r2 emssr | 289 | # |
| r2 emssr | 290 | # 作業領域 M は別。番地がワイヤの値なので別名を排除できない(§19)。 |
| r2 emssr | 291 | |
| r2 emssr | 292 | def _regs(prog, i): |
| r2 emssr | 293 | """(読む landmark, 書く landmark)。M と OUT はここに現れない。""" |
| r2 emssr | 294 | op, a, b, c = prog[i][0], prog[i][1], prog[i][2], prog[i][3] |
| r2 emssr | 295 | if op == MAT: |
| r2 emssr | 296 | return (), a |
| r2 emssr | 297 | if op in (ADD, MUL): |
| r2 emssr | 298 | return (b, c), a |
| r2 emssr | 299 | if op in (NEG, LD): |
| r2 emssr | 300 | return (b,), a |
| r2 emssr | 301 | if op == ST: |
| r2 emssr | 302 | return (b, c), None |
| r2 emssr | 303 | if op in (PUT,): |
| r2 emssr | 304 | return (b,), None |
| r2 emssr | 305 | if op in (EQ, LT): |
| r2 emssr | 306 | return (b, c), None |
| r2 emssr | 307 | return (), None |
| r2 emssr | 308 | |
| r2 emssr | 309 | |
| r2 emssr | 310 | def _shift(name, r, s): |
| r2 emssr | 311 | return name(r) if s == 0 else '(%s<<%d)' % (name(r), s) |
| r2 emssr | 312 | |
| r2 emssr | 313 | |
| r2 emssr | 314 | def _stmt(prog, i, name=None): |
| r2 emssr | 315 | if name is None: |
| r2 emssr | 316 | name = lambda r: 'R[%d]' % r |
| r2 emssr | 317 | op, a, b, c, sb, sc, im = prog[i] |
| r2 emssr | 318 | if op == MAT: |
| r2 emssr | 319 | return '%s=%d' % (name(a), im) |
| r2 emssr | 320 | if op == ADD: |
| r2 emssr | 321 | return '%s=%s+%s' % (name(a), _shift(name, b, sb), _shift(name, c, sc)) |
| r2 emssr | 322 | if op == MUL: |
| r2 emssr | 323 | return '%s=%s*%s' % (name(a), name(b), name(c)) |
| r2 emssr | 324 | if op == NEG: |
| r2 emssr | 325 | return '%s=-%s' % (name(a), name(b)) |
| r2 emssr | 326 | if op == LD: |
| r2 emssr | 327 | return '%s=M[%s]' % (name(a), name(b)) |
| r2 emssr | 328 | if op == ST: |
| r2 emssr | 329 | return 'M[%s]=%s' % (name(b), name(c)) |
| r2 emssr | 330 | if op == PUT: |
| r2 emssr | 331 | return 'OUT.append(%s&255)' % name(b) |
| r2 emssr | 332 | raise ValueError('直線部分に置けない箱: %s' % NAME[op]) |
| r2 emssr | 333 | |
| r2 emssr | 334 | |
| r2 emssr | 335 | def _tail(prog, i, stop, idx, n, name=None): |
| r2 emssr | 336 | """block の閉じ。行の並びを返す。""" |
| r2 emssr | 337 | if name is None: |
| r2 emssr | 338 | name = lambda r: 'R[%d]' % r |
| r2 emssr | 339 | if i is None: |
| r2 emssr | 340 | return ['return %d' % idx[stop]] if stop < n else ['return %d' % R_END] |
| r2 emssr | 341 | |
| r2 emssr | 342 | def to(t): |
| r2 emssr | 343 | return idx[t] if 0 <= t < n and t in idx else R_END |
| r2 emssr | 344 | |
| r2 emssr | 345 | op, _a, b, c, sb, sc, im = prog[i] |
| r2 emssr | 346 | if op == OK: |
| r2 emssr | 347 | return ['return %d' % R_OK] |
| r2 emssr | 348 | if op == KO: |
| r2 emssr | 349 | return ['return %d' % R_KO] |
| r2 emssr | 350 | if op == HALT: |
| r2 emssr | 351 | return ['return %d' % R_END] |
| r2 emssr | 352 | if op == JMP: |
| r2 emssr | 353 | return ['return %d' % to(i + im)] |
| r2 emssr | 354 | rel = '==' if op == EQ else '<' |
| r2 emssr | 355 | return ['if %s%s%s: return %d' |
| r2 emssr | 356 | % (_shift(name, b, sb), rel, _shift(name, c, sc), to(i + im)), |
| r2 emssr | 357 | 'return %d' % to(i + 1)] |
| r2 emssr | 358 | |
| r2 emssr | 359 | |
| r2 emssr | 360 | def _succ(prog, term, stop, idx, n): |
| r2 emssr | 361 | """block の後続。_tail の行き先と同じ。""" |
| r2 emssr | 362 | if term is None: |
| r2 emssr | 363 | return [idx[stop]] if stop < n else [] |
| r2 emssr | 364 | |
| r2 emssr | 365 | def to(t): |
| r2 emssr | 366 | return idx[t] if 0 <= t < n and t in idx else None |
| r2 emssr | 367 | |
| r2 emssr | 368 | op, _a, _b, _c, _sb, _sc, im = prog[term] |
| r2 emssr | 369 | if op in (OK, KO, HALT): |
| r2 emssr | 370 | return [] |
| r2 emssr | 371 | if op == JMP: |
| r2 emssr | 372 | return [x for x in [to(term + im)] if x is not None] |
| r2 emssr | 373 | return [x for x in [to(term + im), to(term + 1)] if x is not None] |
| r2 emssr | 374 | |
| r2 emssr | 375 | |
| r2 emssr | 376 | def _liveness(prog, bl, idx, n): |
| r2 emssr | 377 | """block ごとの live-out(後続が読む landmark の合併)。 |
| r2 emssr | 378 | |
| r2 emssr | 379 | block を出た landmark file を読むのは後続 block だけであり、実行台は |
| r2 emssr | 380 | 作業領域と OUT しか見ない。だから最後の block の live-out は空になる。 |
| r2 emssr | 381 | """ |
| r2 emssr | 382 | use, dfs, succ = [], [], [] |
| r2 emssr | 383 | for pc, body, term, stop in bl: |
| r2 emssr | 384 | w, u = set(), set() |
| r2 emssr | 385 | for i in list(body) + ([] if term is None else [term]): |
| r2 emssr | 386 | rs, d = _regs(prog, i) |
| r2 emssr | 387 | u |= set(r for r in rs if r not in w) |
| r2 emssr | 388 | if d is not None: |
| r2 emssr | 389 | w.add(d) |
| r2 emssr | 390 | use.append(u) |
| r2 emssr | 391 | dfs.append(w) |
| r2 emssr | 392 | succ.append(_succ(prog, term, stop, idx, n)) |
| r2 emssr | 393 | live_in = [set() for _ in bl] |
| r2 emssr | 394 | live_out = [set() for _ in bl] |
| r2 emssr | 395 | moved = True |
| r2 emssr | 396 | while moved: |
| r2 emssr | 397 | moved = False |
| r2 emssr | 398 | for k in range(len(bl) - 1, -1, -1): |
| r2 emssr | 399 | o = set() |
| r2 emssr | 400 | for t in succ[k]: |
| r2 emssr | 401 | o |= live_in[t] |
| r2 emssr | 402 | i = use[k] | (o - dfs[k]) |
| r2 emssr | 403 | if o != live_out[k] or i != live_in[k]: |
| r2 emssr | 404 | live_out[k], live_in[k] = o, i |
| r2 emssr | 405 | moved = True |
| r2 emssr | 406 | return live_out |
| r2 emssr | 407 | |
| r2 emssr | 408 | |
| r2 emssr | 409 | def _block(prog, k, body, term, stop, idx, n, out_live=None, dead=None, scan=None): |
| r2 emssr | 410 | """block ひとつを関数の行へ落とす。 |
| r2 emssr | 411 | |
| r2 emssr | 412 | block の中では値を記号で追う(局所の値番号付け)。doc: casm.lane64.vn |
| r2 emssr | 413 | |
| r2 emssr | 414 | landmark reg[r] は入口の値 R[r]、即値、または局所変数の名前 |
| r2 emssr | 415 | 作業領域 mem[c] は定数番地 c へこの block が書いた値。次の Load はその名前を使う |
| r2 emssr | 416 | |
| r2 emssr | 417 | 即値どうしの算術は畳む。commonc の lane64 版はワイヤを語 64 + w に置き、 |
| r2 emssr | 418 | 一箱が MAT・LD・ST の往復に開くので(back_lane64.cam の覆い)、この追跡で |
| r2 emssr | 419 | 往復が消え、綴った数は一つの即値に畳まれる。Store は他の block が読むので残す。 |
| r2 emssr | 420 | ただし dead に入った番地への Store は落とす(他のどの block も読まないワイヤ)。 |
| r2 emssr | 421 | scan を渡すと文を出さず、この block が読む定数番地のうち、自分の Store より前に |
| r2 emssr | 422 | 読むものを scan へ集める(translate の一巡目)。 |
| r2 emssr | 423 | 意味は式 (2.1) の解釈器が定義であり、test_vcpu.py が両者の一致を確かめる。 |
| r2 emssr | 424 | """ |
| r2 emssr | 425 | steps = list(body) + ([] if term is None else [term]) |
| r2 emssr | 426 | reg = {} |
| r2 emssr | 427 | mem = {} |
| r2 emssr | 428 | tmp = [0] |
| r2 emssr | 429 | out = ['def _b%d(R, M, OUT):' % k] |
| r2 emssr | 430 | |
| r2 emssr | 431 | def get(r): |
| r2 emssr | 432 | if r not in reg: |
| r2 emssr | 433 | reg[r] = 'R[%d]' % r |
| r2 emssr | 434 | return reg[r] |
| r2 emssr | 435 | |
| r2 emssr | 436 | def lit(e): |
| r2 emssr | 437 | try: |
| r2 emssr | 438 | return int(e) |
| r2 emssr | 439 | except ValueError: |
| r2 emssr | 440 | return None |
| r2 emssr | 441 | |
| r2 emssr | 442 | def shifted(r, sh): |
| r2 emssr | 443 | e = get(r) |
| r2 emssr | 444 | v = lit(e) |
| r2 emssr | 445 | if sh == 0: |
| r2 emssr | 446 | return e |
| r2 emssr | 447 | if v is not None: |
| r2 emssr | 448 | return str(v << sh) |
| r2 emssr | 449 | return '(%s<<%d)' % (e, sh) |
| r2 emssr | 450 | |
| r2 emssr | 451 | def fresh(expr): |
| r2 emssr | 452 | tmp[0] += 1 |
| r2 emssr | 453 | name = 't%d' % tmp[0] |
| r2 emssr | 454 | out.append(' %s=%s' % (name, expr)) |
| r2 emssr | 455 | return name |
| r2 emssr | 456 | |
| r2 emssr | 457 | def fold2(x, y, f, sym): |
| r2 emssr | 458 | vx, vy = lit(x), lit(y) |
| r2 emssr | 459 | if vx is not None and vy is not None: |
| r2 emssr | 460 | return str(f(vx, vy)) |
| r2 emssr | 461 | return fresh('%s%s%s' % (x, sym, y)) |
| r2 emssr | 462 | |
| r2 emssr | 463 | for i in body: |
| r2 emssr | 464 | op, a, b, c, sb, sc, im = prog[i] |
| r2 emssr | 465 | if op == MAT: |
| r2 emssr | 466 | reg[a] = str(im) |
| r2 emssr | 467 | elif op == ADD: |
| r2 emssr | 468 | reg[a] = fold2(shifted(b, sb), shifted(c, sc), lambda x, y: x + y, '+') |
| r2 emssr | 469 | elif op == MUL: |
| r2 emssr | 470 | reg[a] = fold2(get(b), get(c), lambda x, y: x * y, '*') |
| r2 emssr | 471 | elif op == NEG: |
| r2 emssr | 472 | e = get(b) |
| r2 emssr | 473 | v = lit(e) |
| r2 emssr | 474 | reg[a] = str(-v) if v is not None else fresh('-%s' % e) |
| r2 emssr | 475 | elif op == LD: |
| r2 emssr | 476 | addr = get(b) |
| r2 emssr | 477 | v = lit(addr) |
| r2 emssr | 478 | if v is not None and v in mem: |
| r2 emssr | 479 | reg[a] = mem[v] |
| r2 emssr | 480 | else: |
| r2 emssr | 481 | if scan is not None: |
| r2 emssr | 482 | scan.add(v) |
| r2 emssr | 483 | reg[a] = fresh('M[%s]' % addr) |
| r2 emssr | 484 | if v is not None: |
| r2 emssr | 485 | mem[v] = reg[a] |
| r2 emssr | 486 | elif op == ST: |
| r2 emssr | 487 | addr, val = get(b), get(c) |
| r2 emssr | 488 | v = lit(addr) |
| r2 emssr | 489 | if not (dead is not None and v in dead): |
| r2 emssr | 490 | out.append(' M[%s]=%s' % (addr, val)) |
| r2 emssr | 491 | if v is not None: |
| r2 emssr | 492 | mem[v] = val |
| r2 emssr | 493 | else: |
| r2 emssr | 494 | mem.clear() |
| r2 emssr | 495 | elif op == PUT: |
| r2 emssr | 496 | out.append(' OUT.append(%s&255)' % get(b)) |
| r2 emssr | 497 | else: |
| r2 emssr | 498 | raise ValueError('直線部分に置けない箱: %s' % NAME[op]) |
| r2 emssr | 499 | keep = set(reg) if out_live is None else (set(reg) & out_live) |
| r2 emssr | 500 | for r in sorted(keep): |
| r2 emssr | 501 | if reg[r] != 'R[%d]' % r: |
| r2 emssr | 502 | out.append(' R[%d]=%s' % (r, reg[r])) |
| r2 emssr | 503 | if term is None: |
| r2 emssr | 504 | tail = _tail(prog, None, stop, idx, n) |
| r2 emssr | 505 | else: |
| r2 emssr | 506 | op, _a, b, c, sb, sc, im = prog[term] |
| r2 emssr | 507 | if op in (EQ, LT): |
| r2 emssr | 508 | rel = '==' if op == EQ else '<' |
| r2 emssr | 509 | x, y = shifted(b, sb), shifted(c, sc) |
| r2 emssr | 510 | vx, vy = lit(x), lit(y) |
| r2 emssr | 511 | to = lambda t: idx[t] if 0 <= t < n and t in idx else R_END |
| r2 emssr | 512 | if vx is not None and vy is not None: |
| r2 emssr | 513 | taken = (vx == vy) if op == EQ else (vx < vy) |
| r2 emssr | 514 | tail = ['return %d' % (to(term + im) if taken else to(term + 1))] |
| r2 emssr | 515 | else: |
| r2 emssr | 516 | tail = ['if %s%s%s: return %d' % (x, rel, y, to(term + im)), |
| r2 emssr | 517 | 'return %d' % to(term + 1)] |
| r2 emssr | 518 | else: |
| r2 emssr | 519 | tail = _tail(prog, term, stop, idx, n) |
| r2 emssr | 520 | out += [' ' + t for t in tail] |
| r2 emssr | 521 | return out + [''] |
| r2 emssr | 522 | |
| r2 emssr | 523 | |
| r2 emssr | 524 | def translate(prog, wires=None): |
| r2 emssr | 525 | """image を Python の source と code object へ落とす。 |
| r2 emssr | 526 | |
| r2 emssr | 527 | block ひとつが関数ひとつになる。式 (0.1) の B は block を実行した回数であり、 |
| r2 emssr | 528 | 箱の数ではない。landmark file は block の入口と出口だけで触る(定理 21.1)。 |
| r2 emssr | 529 | |
| r2 emssr | 530 | wires = (lo, hi) を渡すと、その区間の語は commonc のワイヤ(語 64 + w、19 §3.3)とみなし、 |
| r2 emssr | 531 | 他のどの block も読まない Store を落とす。ワイヤは箱の番号で名指しされるので、 |
| r2 emssr | 532 | 平文の側から計算した番地で読まれることは無い(toolchain が作業領域をワイヤの上に置く)。 |
| r2 emssr | 533 | 区間の外の語(出口 0..63、作業領域、退避語)は残す。 |
| r2 emssr | 534 | """ |
| r2 emssr | 535 | ent, idx, bl = blocks(prog) |
| r2 emssr | 536 | n = len(prog) |
| r2 emssr | 537 | live = _liveness(prog, bl, idx, n) |
| r2 emssr | 538 | dead = None |
| r2 emssr | 539 | if wires is not None: |
| r2 emssr | 540 | lo, hi = wires |
| r2 emssr | 541 | read = set() |
| r2 emssr | 542 | for k, (pc, body, term, stop) in enumerate(bl): |
| r2 emssr | 543 | _block(prog, k, body, term, stop, idx, n, live[k], scan=read) |
| r2 emssr | 544 | dead = set(c for c in range(lo, hi) if c not in read) |
| r2 emssr | 545 | lines = [] |
| r2 emssr | 546 | for k, (pc, body, term, stop) in enumerate(bl): |
| r2 emssr | 547 | lines += _block(prog, k, body, term, stop, idx, n, live[k], dead=dead) |
| r2 emssr | 548 | lines.append('TAB = (' + ''.join('_b%d,' % k for k in range(len(bl))) + ')') |
| r2 emssr | 549 | src = chr(10).join(lines) |
| r2 emssr | 550 | ns = {} |
| r2 emssr | 551 | exec(compile(src, '<lane64>', 'exec'), ns) |
| r2 emssr | 552 | return src, ns['TAB'] |
| r2 emssr | 553 | |
| r2 emssr | 554 | |
| r2 emssr | 555 | # ===== lane 一枚(翻訳器の道)===== |
| r2 emssr | 556 | |
| r2 emssr | 557 | class Lane(object): |
| r2 emssr | 558 | """Σ_lane64 の一枚(20 章 式 1.1)。語は Z なので巻き戻さない(§0.3)。""" |
| r2 emssr | 559 | |
| r2 emssr | 560 | def __init__(self, words): |
| r2 emssr | 561 | self.R = [0] * LANDMARKS |
| r2 emssr | 562 | self.M = [0] * words |
| r2 emssr | 563 | self.OUT = [] |
| r2 emssr | 564 | self.report = None |
| r2 emssr | 565 | self.blocks = 0 |
| r2 emssr | 566 | |
| r2 emssr | 567 | def put_input(self, data, base): |
| r2 emssr | 568 | """18 章 §7.1 と同じ置き方(式 8.2)。""" |
| r2 emssr | 569 | n = min(len(data), len(self.M) - base) |
| r2 emssr | 570 | self.M[0] = n |
| r2 emssr | 571 | self.M[1] = base |
| r2 emssr | 572 | self.M[base:base + n] = data[:n] |
| r2 emssr | 573 | return n |
| r2 emssr | 574 | |
| r2 emssr | 575 | |
| r2 emssr | 576 | def run_lane(tab, lane, budget=1 << 34): |
| r2 emssr | 577 | """block を辿る。式 (0.1) の T_trans がこの loop になる。""" |
| r2 emssr | 578 | R, M, OUT = lane.R, lane.M, lane.OUT |
| r2 emssr | 579 | b = 0 |
| r2 emssr | 580 | try: |
| r2 emssr | 581 | for k in range(budget): |
| r2 emssr | 582 | b = tab[b](R, M, OUT) |
| r2 emssr | 583 | if b < 0: |
| r2 emssr | 584 | lane.blocks = k + 1 |
| r2 emssr | 585 | lane.report = 0 if b == R_OK else (42 if b == R_KO else None) |
| r2 emssr | 586 | return lane.report |
| r2 emssr | 587 | except IndexError: |
| r2 emssr | 588 | # 語番号が作業領域を越えた。式 (50.4) の image 実現不能にあたる。 |
| r2 emssr | 589 | # try は例外が飛ばない限り費用ゼロなので、速い道の邪魔にならない |
| r2 emssr | 590 | raise IndexError('語番号が作業領域を越える(語数 %d)。--words を増やす' |
| r2 emssr | 591 | % len(M)) |
| r2 emssr | 592 | raise RuntimeError('block の歩数上限 %d を越えた' % budget) |
| r2 emssr | 593 | |
| r2 emssr | 594 | |
| r2 emssr | 595 | # ===== 解釈器(式 2.1、3.1、3.2、3.3)===== |
| r2 emssr | 596 | # |
| r2 emssr | 597 | # active relation は W bit の整数一個で持つ。m1 = m & c、m0 = m & ~c が |
| r2 emssr | 598 | # それぞれ一演算になる。速さのためではなく、意味の定義を一箇所に置くために書く。 |
| r2 emssr | 599 | |
| r2 emssr | 600 | class Interp(object): |
| r2 emssr | 601 | |
| r2 emssr | 602 | def __init__(self, prog, lanes=1, words=4096): |
| r2 emssr | 603 | self.prog = prog |
| r2 emssr | 604 | self.W = lanes |
| r2 emssr | 605 | self.R = [[0] * LANDMARKS for _ in range(lanes)] |
| r2 emssr | 606 | self.M = [[0] * words for _ in range(lanes)] |
| r2 emssr | 607 | self.OUT = [[] for _ in range(lanes)] |
| r2 emssr | 608 | self.report = [None] * lanes |
| r2 emssr | 609 | self.steps = 0 |
| r2 emssr | 610 | |
| r2 emssr | 611 | def put_input(self, lane, data, base): |
| r2 emssr | 612 | m = self.M[lane] |
| r2 emssr | 613 | n = min(len(data), len(m) - base) |
| r2 emssr | 614 | m[0] = n |
| r2 emssr | 615 | m[1] = base |
| r2 emssr | 616 | m[base:base + n] = data[:n] |
| r2 emssr | 617 | return n |
| r2 emssr | 618 | |
| r2 emssr | 619 | _one = (0,) |
| r2 emssr | 620 | |
| r2 emssr | 621 | def _active(self, m): |
| r2 emssr | 622 | return [l for l in range(self.W) if (m >> l) & 1] |
| r2 emssr | 623 | |
| r2 emssr | 624 | def run(self, budget=1 << 30): |
| r2 emssr | 625 | prog, n = self.prog, len(self.prog) |
| r2 emssr | 626 | groups = [(((1 << self.W) - 1), 0)] |
| r2 emssr | 627 | while groups: |
| r2 emssr | 628 | self.steps += 1 |
| r2 emssr | 629 | if self.steps > budget: |
| r2 emssr | 630 | raise RuntimeError('歩数上限 %d を越えた' % budget) |
| r2 emssr | 631 | if len(groups) == 1: |
| r2 emssr | 632 | msk, pc = groups.pop() # 判定が割れるまでは組が一つ |
| r2 emssr | 633 | else: |
| r2 emssr | 634 | k = min(range(len(groups)), key=lambda i: groups[i][1]) |
| r2 emssr | 635 | msk, pc = groups.pop(k) |
| r2 emssr | 636 | if msk == 0 or pc < 0 or pc >= n: |
| r2 emssr | 637 | continue |
| r2 emssr | 638 | op, a, b, c, sb, sc, im = prog[pc] |
| r2 emssr | 639 | act = (self._one if msk == 1 else self._active(msk)) |
| r2 emssr | 640 | if op == HALT: |
| r2 emssr | 641 | continue |
| r2 emssr | 642 | if op == OK or op == KO: |
| r2 emssr | 643 | for l in act: |
| r2 emssr | 644 | self.report[l] = 0 if op == OK else 42 |
| r2 emssr | 645 | continue |
| r2 emssr | 646 | if op == JMP: |
| r2 emssr | 647 | groups = _merge(groups, [(msk, pc + im)]) |
| r2 emssr | 648 | continue |
| r2 emssr | 649 | if op == EQ or op == LT: |
| r2 emssr | 650 | cm = 0 |
| r2 emssr | 651 | for l in act: |
| r2 emssr | 652 | R = self.R[l] |
| r2 emssr | 653 | u, v = R[b] << sb, R[c] << sc |
| r2 emssr | 654 | if (u == v) if op == EQ else (u < v): |
| r2 emssr | 655 | cm |= 1 << l |
| r2 emssr | 656 | m1, m0 = msk & cm, msk & ~cm # 式 (3.2) |
| r2 emssr | 657 | groups = _merge(groups, [(m1, pc + im), (m0, pc + 1)]) |
| r2 emssr | 658 | continue |
| r2 emssr | 659 | # 値の箱。mask を外れた lane は動かない(式 2.1) |
| r2 emssr | 660 | for l in act: |
| r2 emssr | 661 | R = self.R[l] |
| r2 emssr | 662 | if op == MAT: |
| r2 emssr | 663 | R[a] = im |
| r2 emssr | 664 | elif op == ADD: |
| r2 emssr | 665 | R[a] = (R[b] << sb) + (R[c] << sc) |
| r2 emssr | 666 | elif op == MUL: |
| r2 emssr | 667 | R[a] = R[b] * R[c] |
| r2 emssr | 668 | elif op == NEG: |
| r2 emssr | 669 | R[a] = -R[b] |
| r2 emssr | 670 | elif op == LD: |
| r2 emssr | 671 | R[a] = self.M[l][R[b]] |
| r2 emssr | 672 | elif op == ST: |
| r2 emssr | 673 | self.M[l][R[b]] = R[c] |
| r2 emssr | 674 | elif op == PUT: |
| r2 emssr | 675 | self.OUT[l].append(R[b] & 255) |
| r2 emssr | 676 | groups = _merge(groups, [(msk, pc + 1)]) |
| r2 emssr | 677 | return self.report |
| r2 emssr | 678 | |
| r2 emssr | 679 | |
| r2 emssr | 680 | def _merge(groups, fresh): |
| r2 emssr | 681 | """reconvergence(式 3.3)。pc が同じ組は mask を合わせて一つにする。""" |
| r2 emssr | 682 | if not groups and len(fresh) == 1 and fresh[0][0]: |
| r2 emssr | 683 | return list(fresh) # 割れていない間はそのまま渡す |
| r2 emssr | 684 | out = list(groups) |
| r2 emssr | 685 | for msk, pc in fresh: |
| r2 emssr | 686 | if msk == 0: |
| r2 emssr | 687 | continue |
| r2 emssr | 688 | for k, (m2, p2) in enumerate(out): |
| r2 emssr | 689 | if p2 == pc: |
| r2 emssr | 690 | out[k] = (m2 | msk, pc) |
| r2 emssr | 691 | break |
| r2 emssr | 692 | else: |
| r2 emssr | 693 | out.append((msk, pc)) |
| r2 emssr | 694 | return out |
| r2 emssr | 695 | |
| r2 emssr | 696 | |
| r2 emssr | 697 | # ===== lane を process へ配る(20 章 §0.5)===== |
| r2 emssr | 698 | # |
| r2 emssr | 699 | # 立ち上げに 600 ms かかるので、pool は host の寿命だけ持ち続ける。 |
| r2 emssr | 700 | # lane の作業領域が式 (1.2) で交わらないから、持ち回しても state が混ざらない。 |
| r2 emssr | 701 | |
| r2 emssr | 702 | _W = {} # worker の中の束縛。image ごとに一度だけ翻訳する |
| r2 emssr | 703 | |
| r2 emssr | 704 | |
| r2 emssr | 705 | def _worker_init(): |
| r2 emssr | 706 | _W.clear() |
| r2 emssr | 707 | |
| r2 emssr | 708 | |
| r2 emssr | 709 | def _worker_run(job): |
| r2 emssr | 710 | image, words, data, in_base, head, emit, count = job |
| r2 emssr | 711 | # 束縛の鍵は image そのものにする。長さや id で引くと、同じ大きさの別の |
| r2 emssr | 712 | # image が前の翻訳を拾う |
| r2 emssr | 713 | tab = _W.get(image) |
| r2 emssr | 714 | if tab is None: |
| r2 emssr | 715 | tab = translate(decode(image))[1] |
| r2 emssr | 716 | _W[image] = tab |
| r2 emssr | 717 | res = [] |
| r2 emssr | 718 | for _ in range(count): |
| r2 emssr | 719 | lane = Lane(words) |
| r2 emssr | 720 | if data is not None: |
| r2 emssr | 721 | lane.put_input(data, in_base) |
| r2 emssr | 722 | rep = run_lane(tab, lane) |
| r2 emssr | 723 | emitted = None |
| r2 emssr | 724 | if emit: |
| r2 emssr | 725 | base, ln = lane.M[1], lane.M[2] |
| r2 emssr | 726 | if base and ln: |
| r2 emssr | 727 | emitted = bytes(v & 255 for v in lane.M[base:base + ln]) |
| r2 emssr | 728 | res.append((rep, bytes(lane.OUT), list(lane.M[:head]), emitted, lane.blocks)) |
| r2 emssr | 729 | return res |
| r2 emssr | 730 | |
| r2 emssr | 731 | |
| r2 emssr | 732 | def _run_share(tab, count, words, data, in_base, head, emit): |
| r2 emssr | 733 | """同じ image を lane 何本か分だけ回す。lane の作業領域は式 (1.2) で |
| r2 emssr | 734 | 交わらないので、この呼びは互いに独立になる(定理 21.1)。""" |
| r2 emssr | 735 | out = [] |
| r2 emssr | 736 | for _ in range(count): |
| r2 emssr | 737 | lane = Lane(words) |
| r2 emssr | 738 | if data is not None: |
| r2 emssr | 739 | lane.put_input(data, in_base) |
| r2 emssr | 740 | rep = run_lane(tab, lane) |
| r2 emssr | 741 | emitted = None |
| r2 emssr | 742 | if emit: |
| r2 emssr | 743 | base, ln = lane.M[1], lane.M[2] |
| r2 emssr | 744 | if base and ln: |
| r2 emssr | 745 | emitted = bytes(v & 255 for v in lane.M[base:base + ln]) |
| r2 emssr | 746 | out.append((rep, bytes(lane.OUT), list(lane.M[:head]), emitted, |
| r2 emssr | 747 | lane.blocks)) |
| r2 emssr | 748 | return out |
| r2 emssr | 749 | |
| r2 emssr | 750 | |
| r2 emssr | 751 | class VCPU(object): |
| r2 emssr | 752 | """常駐の実行台。pool を作り直さない(式 0.4)。 |
| r2 emssr | 753 | |
| r2 emssr | 754 | lane を配る先は二つある。process は写す量が要るかわりに本当に同時に走る。 |
| r2 emssr | 755 | thread は image の翻訳を共有できるので配る費用がほぼ無い。どちらを取るかは |
| r2 emssr | 756 | lane 一本の仕事量で決まるので、両方を持って測れるようにする。 |
| r2 emssr | 757 | """ |
| r2 emssr | 758 | |
| r2 emssr | 759 | def __init__(self, procs=1, threads=1): |
| r2 emssr | 760 | self.P = max(1, int(procs)) |
| r2 emssr | 761 | self.T = max(1, int(threads)) |
| r2 emssr | 762 | self.pool = None |
| r2 emssr | 763 | self.tpool = None |
| r2 emssr | 764 | self._tab = {} |
| r2 emssr | 765 | |
| r2 emssr | 766 | def close(self): |
| r2 emssr | 767 | if self.pool is not None: |
| r2 emssr | 768 | self.pool.close() |
| r2 emssr | 769 | self.pool.join() |
| r2 emssr | 770 | self.pool = None |
| r2 emssr | 771 | if self.tpool is not None: |
| r2 emssr | 772 | self.tpool.shutdown() |
| r2 emssr | 773 | self.tpool = None |
| r2 emssr | 774 | |
| r2 emssr | 775 | def _ensure(self): |
| r2 emssr | 776 | if self.pool is None and self.P > 1: |
| r2 emssr | 777 | import multiprocessing as mp |
| r2 emssr | 778 | self.pool = mp.Pool(self.P, initializer=_worker_init) |
| r2 emssr | 779 | return self.pool |
| r2 emssr | 780 | |
| r2 emssr | 781 | def _threads(self): |
| r2 emssr | 782 | """立ち上げたら畳まない。次の run は待機中の worker がそのまま拾う。""" |
| r2 emssr | 783 | if self.tpool is None and self.T > 1: |
| r2 emssr | 784 | from concurrent.futures import ThreadPoolExecutor |
| r2 emssr | 785 | self.tpool = ThreadPoolExecutor(self.T) |
| r2 emssr | 786 | list(self.tpool.map(lambda _: None, range(self.T))) |
| r2 emssr | 787 | return self.tpool |
| r2 emssr | 788 | |
| r2 emssr | 789 | def _shares(self, lanes, n): |
| r2 emssr | 790 | return [c for c in (lanes // n + (1 if k < lanes % n else 0) |
| r2 emssr | 791 | for k in range(n)) if c] |
| r2 emssr | 792 | |
| r2 emssr | 793 | def _table(self, image): |
| r2 emssr | 794 | tab = self._tab.get(image) |
| r2 emssr | 795 | if tab is None: |
| r2 emssr | 796 | tab = translate(decode(image))[1] |
| r2 emssr | 797 | self._tab[image] = tab |
| r2 emssr | 798 | return tab |
| r2 emssr | 799 | |
| r2 emssr | 800 | def run(self, image, lanes=1, words=4096, data=None, in_base=64, |
| r2 emssr | 801 | head=64, emit=False): |
| r2 emssr | 802 | """lane を P 本の process へ均等に配る。返りは lane ごとの組になる。 |
| r2 emssr | 803 | |
| r2 emssr | 804 | 組は (報告, Put の byte 列, 先頭 head 語, 取り出した image, block 数)。 |
| r2 emssr | 805 | 作業領域まるごとを持ち帰らないのは、process 間で写す量を抑えるためになる。 |
| r2 emssr | 806 | """ |
| r2 emssr | 807 | if lanes > 1 and self.T > 1: |
| r2 emssr | 808 | tab = self._table(image) |
| r2 emssr | 809 | futs = [self._threads().submit(_run_share, tab, c, words, data, |
| r2 emssr | 810 | in_base, head, emit) |
| r2 emssr | 811 | for c in self._shares(lanes, self.T)] |
| r2 emssr | 812 | res = [] |
| r2 emssr | 813 | for f in futs: |
| r2 emssr | 814 | res.extend(f.result()) |
| r2 emssr | 815 | return res |
| r2 emssr | 816 | if self.P <= 1 or lanes == 1: |
| r2 emssr | 817 | return _run_share(self._table(image), lanes, words, data, in_base, |
| r2 emssr | 818 | head, emit) |
| r2 emssr | 819 | jobs = [(image, words, data, in_base, head, emit, c) |
| r2 emssr | 820 | for c in self._shares(lanes, self.P)] |
| r2 emssr | 821 | res = [] |
| r2 emssr | 822 | for part in self._ensure().map(_worker_run, jobs): |
| r2 emssr | 823 | res.extend(part) |
| r2 emssr | 824 | return res |
| r2 emssr | 825 | |
| r2 emssr | 826 | |
| r2 emssr | 827 | # ===== CLI ===== |
| r2 emssr | 828 | |
| r2 emssr | 829 | def _usage(): |
| r2 emssr | 830 | sys.stderr.write( |
| r2 emssr | 831 | 'vcpu <image.bin> [option]\n' |
| r2 emssr | 832 | ' --lanes W lane 数。既定 1(20 章 式 1.1)\n' |
| r2 emssr | 833 | ' --words S lane あたりの語数。既定 4096\n' |
| r2 emssr | 834 | ' --procs P lane を配る process の本数。既定 1\n' |
| r2 emssr | 835 | ' --in F 全 lane の作業領域へ F を写す。語 0 が byte 数、語 1 が基底\n' |
| r2 emssr | 836 | ' --in-base B 本文の基底語番号。既定 64\n' |
| r2 emssr | 837 | ' --dump N 復帰後に語 0..N-1 を出す\n' |
| r2 emssr | 838 | ' --emit F lane 0 が組んだ image を取り出す(19 章 §3.1)\n' |
| r2 emssr | 839 | ' --interp 式 (2.1) の mask 付き解釈器で走らせる\n' |
| r2 emssr | 840 | ' --cupy 語も述語も bit のまま GPU で走らせる(cuvcpu)\n' |
| r2 emssr | 841 | ' --bits NB --cupy の語の幅。既定 32\n' |
| r2 emssr | 842 | ' --list image を逆アセンブルして並べる\n' |
| r2 emssr | 843 | ' --source 翻訳した Python を出す\n') |
| r2 emssr | 844 | |
| r2 emssr | 845 | |
| r2 emssr | 846 | def main(argv): |
| r2 emssr | 847 | if len(argv) < 2: |
| r2 emssr | 848 | _usage() |
| r2 emssr | 849 | return 2 |
| r2 emssr | 850 | img_path = argv[1] |
| r2 emssr | 851 | lanes, words, procs, dump, in_base, threads = 1, 4096, 1, 0, 64, 1 |
| r2 emssr | 852 | par = 0 |
| r2 emssr | 853 | bits = 32 |
| r2 emssr | 854 | in_path = emit_path = None |
| r2 emssr | 855 | listing = interp = source = cupy = False |
| r2 emssr | 856 | i = 2 |
| r2 emssr | 857 | while i < len(argv): |
| r2 emssr | 858 | a = argv[i] |
| r2 emssr | 859 | if a in ('--lanes', '--words', '--procs', '--dump', '--in-base', |
| r2 emssr | 860 | '--threads', '--par', '--bits'): |
| r2 emssr | 861 | i += 1 |
| r2 emssr | 862 | v = int(argv[i]) |
| r2 emssr | 863 | if a == '--lanes': |
| r2 emssr | 864 | lanes = v |
| r2 emssr | 865 | elif a == '--words': |
| r2 emssr | 866 | words = v |
| r2 emssr | 867 | elif a == '--procs': |
| r2 emssr | 868 | procs = v |
| r2 emssr | 869 | elif a == '--threads': |
| r2 emssr | 870 | threads = v |
| r2 emssr | 871 | elif a == '--par': |
| r2 emssr | 872 | par = v |
| r2 emssr | 873 | elif a == '--bits': |
| r2 emssr | 874 | bits = v |
| r2 emssr | 875 | elif a == '--dump': |
| r2 emssr | 876 | dump = v |
| r2 emssr | 877 | else: |
| r2 emssr | 878 | in_base = v |
| r2 emssr | 879 | elif a == '--in': |
| r2 emssr | 880 | i += 1 |
| r2 emssr | 881 | in_path = argv[i] |
| r2 emssr | 882 | elif a == '--emit': |
| r2 emssr | 883 | i += 1 |
| r2 emssr | 884 | emit_path = argv[i] |
| r2 emssr | 885 | elif a == '--list': |
| r2 emssr | 886 | listing = True |
| r2 emssr | 887 | elif a == '--interp': |
| r2 emssr | 888 | interp = True |
| r2 emssr | 889 | elif a == '--cupy': |
| r2 emssr | 890 | cupy = True |
| r2 emssr | 891 | elif a == '--source': |
| r2 emssr | 892 | source = True |
| r2 emssr | 893 | else: |
| r2 emssr | 894 | _usage() |
| r2 emssr | 895 | return 2 |
| r2 emssr | 896 | i += 1 |
| r2 emssr | 897 | |
| r2 emssr | 898 | with open(img_path, 'rb') as f: |
| r2 emssr | 899 | image = f.read() |
| r2 emssr | 900 | prog = decode(image) |
| r2 emssr | 901 | |
| r2 emssr | 902 | if listing: |
| r2 emssr | 903 | for ln in disasm(prog): |
| r2 emssr | 904 | print(ln) |
| r2 emssr | 905 | return 0 |
| r2 emssr | 906 | if source: |
| r2 emssr | 907 | print(translate(prog)[0]) |
| r2 emssr | 908 | return 0 |
| r2 emssr | 909 | |
| r2 emssr | 910 | data = None |
| r2 emssr | 911 | if in_path is not None: |
| r2 emssr | 912 | with open(in_path, 'rb') as f: |
| r2 emssr | 913 | data = f.read() |
| r2 emssr | 914 | |
| r2 emssr | 915 | t0 = time.perf_counter() |
| r2 emssr | 916 | if interp: |
| r2 emssr | 917 | m = Interp(prog, lanes=lanes, words=words) |
| r2 emssr | 918 | if data is not None: |
| r2 emssr | 919 | for l in range(lanes): |
| r2 emssr | 920 | m.put_input(l, data, in_base) |
| r2 emssr | 921 | rep = m.run() |
| r2 emssr | 922 | res = [(rep[l], bytes(m.OUT[l]), m.M[l][:max(dump, 3)], None, 0) |
| r2 emssr | 923 | for l in range(lanes)] |
| r2 emssr | 924 | note = '解釈器 %d 歩' % m.steps |
| r2 emssr | 925 | elif cupy: |
| r2 emssr | 926 | import cuvcpu |
| r2 emssr | 927 | res = cuvcpu.run(prog, lanes=lanes, words=words, bits=bits, data=data, |
| r2 emssr | 928 | in_base=in_base, head=max(dump, 4), |
| r2 emssr | 929 | emit=emit_path is not None) |
| r2 emssr | 930 | note = 'GPU %d bit %d 歩' % (bits, res[0][4]) |
| r2 emssr | 931 | elif par: |
| r2 emssr | 932 | import multiprocessing as mp |
| r2 emssr | 933 | tab = None |
| r2 emssr | 934 | pool = mp.Pool(par) |
| r2 emssr | 935 | lane = Lane(words) |
| r2 emssr | 936 | if data is not None: |
| r2 emssr | 937 | lane.put_input(data, in_base) |
| r2 emssr | 938 | rep = run_lane_par(prog, lane, pool) |
| r2 emssr | 939 | pool.close() |
| r2 emssr | 940 | pool.join() |
| r2 emssr | 941 | res = [(rep, bytes(lane.OUT), list(lane.M[:max(dump, 4)]), None, |
| r2 emssr | 942 | lane.blocks)] |
| r2 emssr | 943 | note = '層並列 block %d 回' % lane.blocks |
| r2 emssr | 944 | del tab |
| r2 emssr | 945 | else: |
| r2 emssr | 946 | vm = VCPU(procs, threads) |
| r2 emssr | 947 | res = vm.run(image, lanes=lanes, words=words, data=data, |
| r2 emssr | 948 | in_base=in_base, head=max(dump, 4), emit=emit_path is not None) |
| r2 emssr | 949 | vm.close() |
| r2 emssr | 950 | note = '翻訳器 block %d 回' % sum(r[4] for r in res) |
| r2 emssr | 951 | el = time.perf_counter() - t0 |
| r2 emssr | 952 | |
| r2 emssr | 953 | bad = 0 |
| r2 emssr | 954 | for l, (rep, out, head, _em, _b) in enumerate(res): |
| r2 emssr | 955 | tag = 'tu' if rep == 0 else ('te' if rep == 42 else '報告なし') |
| r2 emssr | 956 | if rep != 0: |
| r2 emssr | 957 | bad += 1 |
| r2 emssr | 958 | if lanes <= 8 or l < 2 or l == lanes - 1: |
| r2 emssr | 959 | print('lane %-4d %s' % (l, tag)) |
| r2 emssr | 960 | print('%s %.3f 秒 lane %d process %d thread %d' |
| r2 emssr | 961 | % (note, el, lanes, procs, threads)) |
| r2 emssr | 962 | |
| r2 emssr | 963 | rep0, out0, head0, em0, _ = res[0] |
| r2 emssr | 964 | if out0: |
| r2 emssr | 965 | print('out : %d byte %s' % (len(out0), out0.decode('latin-1'))) |
| r2 emssr | 966 | for k in range(dump): |
| r2 emssr | 967 | v = head0[k] |
| r2 emssr | 968 | # 分子は Z なので、深い作図では十進へ落とすほうが計算より重くなる |
| r2 emssr | 969 | if v.bit_length() > 4096: |
| r2 emssr | 970 | print(' w[%d] = %d bit の整数' % (k, v.bit_length())) |
| r2 emssr | 971 | else: |
| r2 emssr | 972 | print(' w[%d] = %d' % (k, v)) |
| r2 emssr | 973 | if emit_path is not None: |
| r2 emssr | 974 | if em0 is None: |
| r2 emssr | 975 | sys.stderr.write('image が空(語1=%d 語2=%d)\n' % (head0[1], head0[2])) |
| r2 emssr | 976 | return 1 |
| r2 emssr | 977 | with open(emit_path, 'wb') as f: |
| r2 emssr | 978 | f.write(em0) |
| r2 emssr | 979 | print('image : %s %d byte' % (emit_path, len(em0))) |
| r2 emssr | 980 | return 1 if bad else 0 |
| r2 emssr | 981 | |
| r2 emssr | 982 | |
| r2 emssr | 983 | if __name__ == '__main__': |
| r2 emssr | 984 | sys.exit(main(sys.argv)) |