📦 Out of Context リポジトリ

リポジトリ / emssr / independent_common_compiler

r2
r2 emssr1
# -*- coding: utf-8 -*-
r2 emssr2
"""lane64 の実行台。
r2 emssr3
r2 emssr4
仕様は docs/common-compiler/arch/20。標準機能だけで書く。
r2 emssr5
r2 emssr6
道は二本ある。
r2 emssr7
r2 emssr8
  解釈器  式 (2.1) の mask 付き意味をそのまま持つ。これが定義になる
r2 emssr9
  翻訳器  block を Python の code object へ落とす。速い道になる
r2 emssr10
r2 emssr11
命題 6.1 が二本の一致を保証し、test_vcpu.py がそれを実際に確かめる。
r2 emssr12
lane の作業領域は式 (1.2) で交わらないので、翻訳器の道は lane を process へ配れる。
r2 emssr13
r2 emssr14
  python vcpu.py image.bin --lanes 8 --words 65536 --procs 6 --in corpus.txt
r2 emssr15
"""
r2 emssr16
r2 emssr17
import os
r2 emssr18
import sys
r2 emssr19
import time
r2 emssr20
r2 emssr21
INSN = 16
r2 emssr22
r2 emssr23
HALT, MAT, ADD, MUL, NEG, EQ, LT, JMP, LD, ST, OK, KO, PUT = range(13)
r2 emssr24
r2 emssr25
NAME = ('HALT', 'MAT', 'ADD', 'MUL', 'NEG', 'EQ', 'LT', 'JMP',
r2 emssr26
        'LD', 'ST', 'OK', 'KO', 'PUT')
r2 emssr27
r2 emssr28
# block を閉じる箱(20 章 式 7.4)
r2 emssr29
TERM = frozenset([EQ, LT, JMP, OK, KO, HALT])
r2 emssr30
r2 emssr31
# 報告の返り値。block の番号と混ざらないよう負で持つ
r2 emssr32
R_OK, R_KO, R_END = -1, -2, -3
r2 emssr33
r2 emssr34
LANDMARKS = 256
r2 emssr35
r2 emssr36
r2 emssr37
# ===== 復号(20 章 §2)=====
r2 emssr38
r2 emssr39
def decode(image):
r2 emssr40
    """image を欄の組の並びへ開く。
r2 emssr41
r2 emssr42
    固定 16 byte なので、長さを数える走査が要らない。欄の並びは
r2 emssr43
    op / a / b / c / s_b / s_c / imm で、全部 little endian になる。
r2 emssr44
    """
r2 emssr45
    if len(image) % INSN:
r2 emssr46
        raise ValueError('image の byte 数が %d の倍数でない: %d' % (INSN, len(image)))
r2 emssr47
    prog = []
r2 emssr48
    for k in range(0, len(image), INSN):
r2 emssr49
        w = image[k:k + INSN]
r2 emssr50
        prog.append((w[0], w[1], w[2], w[3],
r2 emssr51
                     w[4] | (w[5] << 8), w[6] | (w[7] << 8),
r2 emssr52
                     int.from_bytes(w[8:16], 'little', signed=True)))
r2 emssr53
    return prog
r2 emssr54
r2 emssr55
r2 emssr56
def encode(prog):
r2 emssr57
    """復号の逆(式 40.1、30.4)。二つを往復させると image が戻る。"""
r2 emssr58
    out = bytearray()
r2 emssr59
    for (op, a, b, c, sb, sc, im) in prog:
r2 emssr60
        out += bytes([op, a, b, c, sb & 255, sb >> 8, sc & 255, sc >> 8])
r2 emssr61
        out += int(im).to_bytes(8, 'little', signed=True)
r2 emssr62
    return bytes(out)
r2 emssr63
r2 emssr64
r2 emssr65
def disasm(prog):
r2 emssr66
    out = []
r2 emssr67
    for i, (op, a, b, c, sb, sc, im) in enumerate(prog):
r2 emssr68
        out.append('%5d  %-4s a=%-3d b=%-3d c=%-3d sb=%-2d sc=%-2d imm=%d'
r2 emssr69
                   % (i, NAME[op] if op < len(NAME) else '?', a, b, c, sb, sc, im))
r2 emssr70
    return out
r2 emssr71
r2 emssr72
r2 emssr73
# ===== block(20 章 §7.3)=====
r2 emssr74
r2 emssr75
def entries(prog):
r2 emssr76
    """block の入口(式 7.4)。分岐の行き先と、分岐の次が入口になる。"""
r2 emssr77
    n = len(prog)
r2 emssr78
    e = set([0])
r2 emssr79
    for i, (op, _a, _b, _c, _sb, _sc, im) in enumerate(prog):
r2 emssr80
        if op in TERM:
r2 emssr81
            e.add(i + 1)
r2 emssr82
            if op in (EQ, LT, JMP):
r2 emssr83
                e.add(i + im)
r2 emssr84
    return sorted(x for x in e if 0 <= x < n)
r2 emssr85
r2 emssr86
r2 emssr87
def blocks(prog):
r2 emssr88
    """(入口, 直線部分の箱, 閉じる箱 or None, 次の入口) の並び。
r2 emssr89
r2 emssr90
    閉じる箱が None のときは、次の入口へそのまま落ちる。
r2 emssr91
    """
r2 emssr92
    ent = entries(prog)
r2 emssr93
    n = len(prog)
r2 emssr94
    out = []
r2 emssr95
    for k, pc in enumerate(ent):
r2 emssr96
        stop = ent[k + 1] if k + 1 < len(ent) else n
r2 emssr97
        j = pc
r2 emssr98
        body = []
r2 emssr99
        while j < stop and prog[j][0] not in TERM:
r2 emssr100
            body.append(j)
r2 emssr101
            j += 1
r2 emssr102
        out.append((pc, body, j if j < stop else None, stop))
r2 emssr103
    return ent, dict((pc, k) for k, pc in enumerate(ent)), out
r2 emssr104
r2 emssr105
r2 emssr106
# ===== 層(20 章 §7.1)=====
r2 emssr107
r2 emssr108
def layers(prog, body):
r2 emssr109
    """直線部分の因果深度(式 7.1)と層(式 7.2)。
r2 emssr110
r2 emssr111
    実行の速さには効かない。効くのは「並べ替えてよい」という保証のほうで、
r2 emssr112
    それが block を直線に書き下せる根拠になる(定理 21.1)。層の幅は
r2 emssr113
    その保証がどれだけ広いかの目安として出す。
r2 emssr114
    """
r2 emssr115
    depth, last, lay = {}, {}, []
r2 emssr116
    for i in body:
r2 emssr117
        op, a, b, c = prog[i][0], prog[i][1], prog[i][2], prog[i][3]
r2 emssr118
        pred = ()
r2 emssr119
        if op in (ADD, MUL):
r2 emssr120
            pred = (b, c)
r2 emssr121
        elif op in (NEG, LD):
r2 emssr122
            pred = (b,)
r2 emssr123
        elif op in (ST, PUT):
r2 emssr124
            pred = (b, c) if op == ST else (b,)
r2 emssr125
        d = 0
r2 emssr126
        for r in pred:
r2 emssr127
            if r in last:
r2 emssr128
                d = max(d, last[r] + 1)
r2 emssr129
        depth[i] = d
r2 emssr130
        if op in (MAT, ADD, MUL, NEG, LD):
r2 emssr131
            last[a] = d
r2 emssr132
        while len(lay) <= d:
r2 emssr133
            lay.append([])
r2 emssr134
        lay[d].append(i)
r2 emssr135
    return lay
r2 emssr136
r2 emssr137
r2 emssr138
# ===== 層を同時に走らせる(定理 21.1)=====
r2 emssr139
#
r2 emssr140
# 層の中の箱は互いに独立なので、どの順で走らせても、同時に走らせても観測が変わらない。
r2 emssr141
#
r2 emssr142
#   (C1 ⊗ id);(id ⊗ C2) = C1 ⊗ C2                          定理 21.1
r2 emssr143
#
r2 emssr144
# 配るのは値の箱だけになる。作業領域を触る箱は番地がワイヤの値なので別名を排除できず
r2 emssr145
# (§19)、本文の順のまま置く。だから値の箱の連なりを先に切り出し、その中でだけ
r2 emssr146
# 層を作る。
r2 emssr147
#
r2 emssr148
# 割に合うかは箱一つの重さで決まる。分子が Z なので、深い作図では一つの ⋆ が
r2 emssr149
# 多倍長の乗算になり、配る費用を上回る。
r2 emssr150
r2 emssr151
_ARITH = frozenset([MAT, ADD, MUL, NEG])
r2 emssr152
r2 emssr153
r2 emssr154
def _box(job):
r2 emssr155
    """値の箱を一つ。ワイヤの値だけを受けるので、worker は state を持たない。"""
r2 emssr156
    op, a, u, v, sb, sc, im = job
r2 emssr157
    if op == MUL:
r2 emssr158
        return a, u * v
r2 emssr159
    if op == ADD:
r2 emssr160
        return a, (u << sb) + (v << sc)
r2 emssr161
    if op == MAT:
r2 emssr162
        return a, im
r2 emssr163
    if op == NEG:
r2 emssr164
        return a, -u
r2 emssr165
    raise ValueError('値の箱ではない: %s' % NAME[op])
r2 emssr166
r2 emssr167
r2 emssr168
def _runs(prog, body):
r2 emssr169
    """値の箱の連なりと、作業領域を触る箱を、本文の順に切り分ける。"""
r2 emssr170
    out, cur = [], []
r2 emssr171
    for i in body:
r2 emssr172
        if prog[i][0] in _ARITH:
r2 emssr173
            cur.append(i)
r2 emssr174
        else:
r2 emssr175
            if cur:
r2 emssr176
                out.append((True, cur))
r2 emssr177
                cur = []
r2 emssr178
            out.append((False, i))
r2 emssr179
    if cur:
r2 emssr180
        out.append((True, cur))
r2 emssr181
    return out
r2 emssr182
r2 emssr183
r2 emssr184
def _layers_hz(prog, seg):
r2 emssr185
    """並べ替えてよい層。式 (7.1) の Pred に WAR と WAW を足す。
r2 emssr186
r2 emssr187
    landmark は allocator が使い回す(13 §13 の wire-to-landmark embedding)ので、
r2 emssr188
    RAW だけでは並べ替えの根拠にならない。定理 21.1 の独立性はワイヤの上の話であり、
r2 emssr189
    割り当ての後まで自動では届かない。
r2 emssr190
    """
r2 emssr191
    lastw, lastr, lay = {}, {}, []
r2 emssr192
    for i in seg:
r2 emssr193
        rs, w = _regs(prog, i)
r2 emssr194
        d = 0
r2 emssr195
        for r in rs:
r2 emssr196
            if r in lastw:
r2 emssr197
                d = max(d, lastw[r] + 1)
r2 emssr198
        if w is not None:
r2 emssr199
            if w in lastw:
r2 emssr200
                d = max(d, lastw[w] + 1)
r2 emssr201
            if w in lastr:
r2 emssr202
                d = max(d, lastr[w] + 1)
r2 emssr203
        for r in rs:
r2 emssr204
            lastr[r] = max(lastr.get(r, -1), d)
r2 emssr205
        if w is not None:
r2 emssr206
            lastw[w] = d
r2 emssr207
        while len(lay) <= d:
r2 emssr208
            lay.append([])
r2 emssr209
        lay[d].append(i)
r2 emssr210
    return lay
r2 emssr211
r2 emssr212
r2 emssr213
def _job(prog, R, i):
r2 emssr214
    op, a, b, c, sb, sc, im = prog[i]
r2 emssr215
    return (op, a, R[b], R[c], sb, sc, im)
r2 emssr216
r2 emssr217
r2 emssr218
def run_lane_par(prog, lane, pool=None, minw=2, budget=1 << 34):
r2 emssr219
    """block を層の順に走らせ、幅のある層を pool へ配る。"""
r2 emssr220
    R, M, OUT = lane.R, lane.M, lane.OUT
r2 emssr221
    _ent, idx, bl = blocks(prog)
r2 emssr222
    n, b = len(prog), 0
r2 emssr223
    for step in range(budget):
r2 emssr224
        _pc, body, term, stop = bl[b]
r2 emssr225
        for arith, seg in _runs(prog, body):
r2 emssr226
            if not arith:
r2 emssr227
                op, a, u, v = prog[seg][0], prog[seg][1], prog[seg][2], prog[seg][3]
r2 emssr228
                if op == LD:
r2 emssr229
                    R[a] = M[R[u]]
r2 emssr230
                elif op == ST:
r2 emssr231
                    M[R[u]] = R[v]
r2 emssr232
                elif op == PUT:
r2 emssr233
                    OUT.append(R[u] & 255)
r2 emssr234
                else:
r2 emssr235
                    raise ValueError('直線部分に置けない箱: %s' % NAME[op])
r2 emssr236
                continue
r2 emssr237
            for L in _layers_hz(prog, seg):
r2 emssr238
                outs = [prog[i][1] for i in L]
r2 emssr239
                wide = (pool is not None and len(L) >= minw
r2 emssr240
                        and len(set(outs)) == len(outs))
r2 emssr241
                if wide:
r2 emssr242
                    for a, val in pool.map(_box, [_job(prog, R, i) for i in L]):
r2 emssr243
                        R[a] = val
r2 emssr244
                else:
r2 emssr245
                    for i in L:
r2 emssr246
                        a, val = _box(_job(prog, R, i))
r2 emssr247
                        R[a] = val
r2 emssr248
        lane.blocks = step + 1
r2 emssr249
        if term is None:
r2 emssr250
            if stop < n:
r2 emssr251
                b = idx[stop]
r2 emssr252
                continue
r2 emssr253
            lane.report = None
r2 emssr254
            return None
r2 emssr255
        op, _a, bb, cc, sb, sc, im = prog[term]
r2 emssr256
        if op == OK:
r2 emssr257
            lane.report = 0
r2 emssr258
            return 0
r2 emssr259
        if op == KO:
r2 emssr260
            lane.report = 42
r2 emssr261
            return 42
r2 emssr262
        if op == HALT:
r2 emssr263
            lane.report = None
r2 emssr264
            return None
r2 emssr265
        if op == JMP:
r2 emssr266
            t = term + im
r2 emssr267
        else:
r2 emssr268
            u, v = R[bb] << sb, R[cc] << sc
r2 emssr269
            t = term + im if ((u == v) if op == EQ else (u < v)) else term + 1
r2 emssr270
        if 0 <= t < n and t in idx:
r2 emssr271
            b = idx[t]
r2 emssr272
        else:
r2 emssr273
            lane.report = None
r2 emssr274
            return None
r2 emssr275
    raise RuntimeError('block の歩数上限 %d を越えた' % budget)
r2 emssr276
r2 emssr277
r2 emssr278
# ===== 翻訳(20 章 §7.2)=====
r2 emssr279
r2 emssr280
# ===== 定理 21.1 の回収 =====
r2 emssr281
#
r2 emssr282
# 層分解(式 7.1、7.2)は block を「state を共有しない独立 chamber の積」だと言う。
r2 emssr283
#
r2 emssr284
#   (C1 ⊗ id);(id ⊗ C2) = C1 ⊗ C2                          定理 21.1
r2 emssr285
#
r2 emssr286
# だから block の中で landmark file を辿る必要が無い。読む前に書かない landmark を
r2 emssr287
# 入口で local へ写し、書いた landmark を出口で戻せば、間は local variable で足りる。
r2 emssr288
# CPython では R[k] が LOAD_FAST + LOAD_CONST + BINARY_SUBSCR、local は LOAD_FAST 一つ。
r2 emssr289
#
r2 emssr290
# 作業領域 M は別。番地がワイヤの値なので別名を排除できない(§19)。
r2 emssr291
r2 emssr292
def _regs(prog, i):
r2 emssr293
    """(読む landmark, 書く landmark)。M と OUT はここに現れない。"""
r2 emssr294
    op, a, b, c = prog[i][0], prog[i][1], prog[i][2], prog[i][3]
r2 emssr295
    if op == MAT:
r2 emssr296
        return (), a
r2 emssr297
    if op in (ADD, MUL):
r2 emssr298
        return (b, c), a
r2 emssr299
    if op in (NEG, LD):
r2 emssr300
        return (b,), a
r2 emssr301
    if op == ST:
r2 emssr302
        return (b, c), None
r2 emssr303
    if op in (PUT,):
r2 emssr304
        return (b,), None
r2 emssr305
    if op in (EQ, LT):
r2 emssr306
        return (b, c), None
r2 emssr307
    return (), None
r2 emssr308
r2 emssr309
r2 emssr310
def _shift(name, r, s):
r2 emssr311
    return name(r) if s == 0 else '(%s<<%d)' % (name(r), s)
r2 emssr312
r2 emssr313
r2 emssr314
def _stmt(prog, i, name=None):
r2 emssr315
    if name is None:
r2 emssr316
        name = lambda r: 'R[%d]' % r
r2 emssr317
    op, a, b, c, sb, sc, im = prog[i]
r2 emssr318
    if op == MAT:
r2 emssr319
        return '%s=%d' % (name(a), im)
r2 emssr320
    if op == ADD:
r2 emssr321
        return '%s=%s+%s' % (name(a), _shift(name, b, sb), _shift(name, c, sc))
r2 emssr322
    if op == MUL:
r2 emssr323
        return '%s=%s*%s' % (name(a), name(b), name(c))
r2 emssr324
    if op == NEG:
r2 emssr325
        return '%s=-%s' % (name(a), name(b))
r2 emssr326
    if op == LD:
r2 emssr327
        return '%s=M[%s]' % (name(a), name(b))
r2 emssr328
    if op == ST:
r2 emssr329
        return 'M[%s]=%s' % (name(b), name(c))
r2 emssr330
    if op == PUT:
r2 emssr331
        return 'OUT.append(%s&255)' % name(b)
r2 emssr332
    raise ValueError('直線部分に置けない箱: %s' % NAME[op])
r2 emssr333
r2 emssr334
r2 emssr335
def _tail(prog, i, stop, idx, n, name=None):
r2 emssr336
    """block の閉じ。行の並びを返す。"""
r2 emssr337
    if name is None:
r2 emssr338
        name = lambda r: 'R[%d]' % r
r2 emssr339
    if i is None:
r2 emssr340
        return ['return %d' % idx[stop]] if stop < n else ['return %d' % R_END]
r2 emssr341
r2 emssr342
    def to(t):
r2 emssr343
        return idx[t] if 0 <= t < n and t in idx else R_END
r2 emssr344
r2 emssr345
    op, _a, b, c, sb, sc, im = prog[i]
r2 emssr346
    if op == OK:
r2 emssr347
        return ['return %d' % R_OK]
r2 emssr348
    if op == KO:
r2 emssr349
        return ['return %d' % R_KO]
r2 emssr350
    if op == HALT:
r2 emssr351
        return ['return %d' % R_END]
r2 emssr352
    if op == JMP:
r2 emssr353
        return ['return %d' % to(i + im)]
r2 emssr354
    rel = '==' if op == EQ else '<'
r2 emssr355
    return ['if %s%s%s: return %d'
r2 emssr356
            % (_shift(name, b, sb), rel, _shift(name, c, sc), to(i + im)),
r2 emssr357
            'return %d' % to(i + 1)]
r2 emssr358
r2 emssr359
r2 emssr360
def _succ(prog, term, stop, idx, n):
r2 emssr361
    """block の後続。_tail の行き先と同じ。"""
r2 emssr362
    if term is None:
r2 emssr363
        return [idx[stop]] if stop < n else []
r2 emssr364
r2 emssr365
    def to(t):
r2 emssr366
        return idx[t] if 0 <= t < n and t in idx else None
r2 emssr367
r2 emssr368
    op, _a, _b, _c, _sb, _sc, im = prog[term]
r2 emssr369
    if op in (OK, KO, HALT):
r2 emssr370
        return []
r2 emssr371
    if op == JMP:
r2 emssr372
        return [x for x in [to(term + im)] if x is not None]
r2 emssr373
    return [x for x in [to(term + im), to(term + 1)] if x is not None]
r2 emssr374
r2 emssr375
r2 emssr376
def _liveness(prog, bl, idx, n):
r2 emssr377
    """block ごとの live-out(後続が読む landmark の合併)。
r2 emssr378
r2 emssr379
    block を出た landmark file を読むのは後続 block だけであり、実行台は
r2 emssr380
    作業領域と OUT しか見ない。だから最後の block の live-out は空になる。
r2 emssr381
    """
r2 emssr382
    use, dfs, succ = [], [], []
r2 emssr383
    for pc, body, term, stop in bl:
r2 emssr384
        w, u = set(), set()
r2 emssr385
        for i in list(body) + ([] if term is None else [term]):
r2 emssr386
            rs, d = _regs(prog, i)
r2 emssr387
            u |= set(r for r in rs if r not in w)
r2 emssr388
            if d is not None:
r2 emssr389
                w.add(d)
r2 emssr390
        use.append(u)
r2 emssr391
        dfs.append(w)
r2 emssr392
        succ.append(_succ(prog, term, stop, idx, n))
r2 emssr393
    live_in = [set() for _ in bl]
r2 emssr394
    live_out = [set() for _ in bl]
r2 emssr395
    moved = True
r2 emssr396
    while moved:
r2 emssr397
        moved = False
r2 emssr398
        for k in range(len(bl) - 1, -1, -1):
r2 emssr399
            o = set()
r2 emssr400
            for t in succ[k]:
r2 emssr401
                o |= live_in[t]
r2 emssr402
            i = use[k] | (o - dfs[k])
r2 emssr403
            if o != live_out[k] or i != live_in[k]:
r2 emssr404
                live_out[k], live_in[k] = o, i
r2 emssr405
                moved = True
r2 emssr406
    return live_out
r2 emssr407
r2 emssr408
r2 emssr409
def _block(prog, k, body, term, stop, idx, n, out_live=None, dead=None, scan=None):
r2 emssr410
    """block ひとつを関数の行へ落とす。
r2 emssr411
r2 emssr412
    block の中では値を記号で追う(局所の値番号付け)。doc: casm.lane64.vn
r2 emssr413
r2 emssr414
      landmark   reg[r] は入口の値 R[r]、即値、または局所変数の名前
r2 emssr415
      作業領域   mem[c] は定数番地 c へこの block が書いた値。次の Load はその名前を使う
r2 emssr416
r2 emssr417
    即値どうしの算術は畳む。commonc の lane64 版はワイヤを語 64 + w に置き、
r2 emssr418
    一箱が MAT・LD・ST の往復に開くので(back_lane64.cam の覆い)、この追跡で
r2 emssr419
    往復が消え、綴った数は一つの即値に畳まれる。Store は他の block が読むので残す。
r2 emssr420
    ただし dead に入った番地への Store は落とす(他のどの block も読まないワイヤ)。
r2 emssr421
    scan を渡すと文を出さず、この block が読む定数番地のうち、自分の Store より前に
r2 emssr422
    読むものを scan へ集める(translate の一巡目)。
r2 emssr423
    意味は式 (2.1) の解釈器が定義であり、test_vcpu.py が両者の一致を確かめる。
r2 emssr424
    """
r2 emssr425
    steps = list(body) + ([] if term is None else [term])
r2 emssr426
    reg = {}
r2 emssr427
    mem = {}
r2 emssr428
    tmp = [0]
r2 emssr429
    out = ['def _b%d(R, M, OUT):' % k]
r2 emssr430
r2 emssr431
    def get(r):
r2 emssr432
        if r not in reg:
r2 emssr433
            reg[r] = 'R[%d]' % r
r2 emssr434
        return reg[r]
r2 emssr435
r2 emssr436
    def lit(e):
r2 emssr437
        try:
r2 emssr438
            return int(e)
r2 emssr439
        except ValueError:
r2 emssr440
            return None
r2 emssr441
r2 emssr442
    def shifted(r, sh):
r2 emssr443
        e = get(r)
r2 emssr444
        v = lit(e)
r2 emssr445
        if sh == 0:
r2 emssr446
            return e
r2 emssr447
        if v is not None:
r2 emssr448
            return str(v << sh)
r2 emssr449
        return '(%s<<%d)' % (e, sh)
r2 emssr450
r2 emssr451
    def fresh(expr):
r2 emssr452
        tmp[0] += 1
r2 emssr453
        name = 't%d' % tmp[0]
r2 emssr454
        out.append('    %s=%s' % (name, expr))
r2 emssr455
        return name
r2 emssr456
r2 emssr457
    def fold2(x, y, f, sym):
r2 emssr458
        vx, vy = lit(x), lit(y)
r2 emssr459
        if vx is not None and vy is not None:
r2 emssr460
            return str(f(vx, vy))
r2 emssr461
        return fresh('%s%s%s' % (x, sym, y))
r2 emssr462
r2 emssr463
    for i in body:
r2 emssr464
        op, a, b, c, sb, sc, im = prog[i]
r2 emssr465
        if op == MAT:
r2 emssr466
            reg[a] = str(im)
r2 emssr467
        elif op == ADD:
r2 emssr468
            reg[a] = fold2(shifted(b, sb), shifted(c, sc), lambda x, y: x + y, '+')
r2 emssr469
        elif op == MUL:
r2 emssr470
            reg[a] = fold2(get(b), get(c), lambda x, y: x * y, '*')
r2 emssr471
        elif op == NEG:
r2 emssr472
            e = get(b)
r2 emssr473
            v = lit(e)
r2 emssr474
            reg[a] = str(-v) if v is not None else fresh('-%s' % e)
r2 emssr475
        elif op == LD:
r2 emssr476
            addr = get(b)
r2 emssr477
            v = lit(addr)
r2 emssr478
            if v is not None and v in mem:
r2 emssr479
                reg[a] = mem[v]
r2 emssr480
            else:
r2 emssr481
                if scan is not None:
r2 emssr482
                    scan.add(v)
r2 emssr483
                reg[a] = fresh('M[%s]' % addr)
r2 emssr484
                if v is not None:
r2 emssr485
                    mem[v] = reg[a]
r2 emssr486
        elif op == ST:
r2 emssr487
            addr, val = get(b), get(c)
r2 emssr488
            v = lit(addr)
r2 emssr489
            if not (dead is not None and v in dead):
r2 emssr490
                out.append('    M[%s]=%s' % (addr, val))
r2 emssr491
            if v is not None:
r2 emssr492
                mem[v] = val
r2 emssr493
            else:
r2 emssr494
                mem.clear()
r2 emssr495
        elif op == PUT:
r2 emssr496
            out.append('    OUT.append(%s&255)' % get(b))
r2 emssr497
        else:
r2 emssr498
            raise ValueError('直線部分に置けない箱: %s' % NAME[op])
r2 emssr499
    keep = set(reg) if out_live is None else (set(reg) & out_live)
r2 emssr500
    for r in sorted(keep):
r2 emssr501
        if reg[r] != 'R[%d]' % r:
r2 emssr502
            out.append('    R[%d]=%s' % (r, reg[r]))
r2 emssr503
    if term is None:
r2 emssr504
        tail = _tail(prog, None, stop, idx, n)
r2 emssr505
    else:
r2 emssr506
        op, _a, b, c, sb, sc, im = prog[term]
r2 emssr507
        if op in (EQ, LT):
r2 emssr508
            rel = '==' if op == EQ else '<'
r2 emssr509
            x, y = shifted(b, sb), shifted(c, sc)
r2 emssr510
            vx, vy = lit(x), lit(y)
r2 emssr511
            to = lambda t: idx[t] if 0 <= t < n and t in idx else R_END
r2 emssr512
            if vx is not None and vy is not None:
r2 emssr513
                taken = (vx == vy) if op == EQ else (vx < vy)
r2 emssr514
                tail = ['return %d' % (to(term + im) if taken else to(term + 1))]
r2 emssr515
            else:
r2 emssr516
                tail = ['if %s%s%s: return %d' % (x, rel, y, to(term + im)),
r2 emssr517
                        'return %d' % to(term + 1)]
r2 emssr518
        else:
r2 emssr519
            tail = _tail(prog, term, stop, idx, n)
r2 emssr520
    out += ['    ' + t for t in tail]
r2 emssr521
    return out + ['']
r2 emssr522
r2 emssr523
r2 emssr524
def translate(prog, wires=None):
r2 emssr525
    """image を Python の source と code object へ落とす。
r2 emssr526
r2 emssr527
    block ひとつが関数ひとつになる。式 (0.1) の B は block を実行した回数であり、
r2 emssr528
    箱の数ではない。landmark file は block の入口と出口だけで触る(定理 21.1)。
r2 emssr529
r2 emssr530
    wires = (lo, hi) を渡すと、その区間の語は commonc のワイヤ(語 64 + w、19 §3.3)とみなし、
r2 emssr531
    他のどの block も読まない Store を落とす。ワイヤは箱の番号で名指しされるので、
r2 emssr532
    平文の側から計算した番地で読まれることは無い(toolchain が作業領域をワイヤの上に置く)。
r2 emssr533
    区間の外の語(出口 0..63、作業領域、退避語)は残す。
r2 emssr534
    """
r2 emssr535
    ent, idx, bl = blocks(prog)
r2 emssr536
    n = len(prog)
r2 emssr537
    live = _liveness(prog, bl, idx, n)
r2 emssr538
    dead = None
r2 emssr539
    if wires is not None:
r2 emssr540
        lo, hi = wires
r2 emssr541
        read = set()
r2 emssr542
        for k, (pc, body, term, stop) in enumerate(bl):
r2 emssr543
            _block(prog, k, body, term, stop, idx, n, live[k], scan=read)
r2 emssr544
        dead = set(c for c in range(lo, hi) if c not in read)
r2 emssr545
    lines = []
r2 emssr546
    for k, (pc, body, term, stop) in enumerate(bl):
r2 emssr547
        lines += _block(prog, k, body, term, stop, idx, n, live[k], dead=dead)
r2 emssr548
    lines.append('TAB = (' + ''.join('_b%d,' % k for k in range(len(bl))) + ')')
r2 emssr549
    src = chr(10).join(lines)
r2 emssr550
    ns = {}
r2 emssr551
    exec(compile(src, '<lane64>', 'exec'), ns)
r2 emssr552
    return src, ns['TAB']
r2 emssr553
r2 emssr554
r2 emssr555
# ===== lane 一枚(翻訳器の道)=====
r2 emssr556
r2 emssr557
class Lane(object):
r2 emssr558
    """Σ_lane64 の一枚(20 章 式 1.1)。語は Z なので巻き戻さない(§0.3)。"""
r2 emssr559
r2 emssr560
    def __init__(self, words):
r2 emssr561
        self.R = [0] * LANDMARKS
r2 emssr562
        self.M = [0] * words
r2 emssr563
        self.OUT = []
r2 emssr564
        self.report = None
r2 emssr565
        self.blocks = 0
r2 emssr566
r2 emssr567
    def put_input(self, data, base):
r2 emssr568
        """18 章 §7.1 と同じ置き方(式 8.2)。"""
r2 emssr569
        n = min(len(data), len(self.M) - base)
r2 emssr570
        self.M[0] = n
r2 emssr571
        self.M[1] = base
r2 emssr572
        self.M[base:base + n] = data[:n]
r2 emssr573
        return n
r2 emssr574
r2 emssr575
r2 emssr576
def run_lane(tab, lane, budget=1 << 34):
r2 emssr577
    """block を辿る。式 (0.1) の T_trans がこの loop になる。"""
r2 emssr578
    R, M, OUT = lane.R, lane.M, lane.OUT
r2 emssr579
    b = 0
r2 emssr580
    try:
r2 emssr581
        for k in range(budget):
r2 emssr582
            b = tab[b](R, M, OUT)
r2 emssr583
            if b < 0:
r2 emssr584
                lane.blocks = k + 1
r2 emssr585
                lane.report = 0 if b == R_OK else (42 if b == R_KO else None)
r2 emssr586
                return lane.report
r2 emssr587
    except IndexError:
r2 emssr588
        # 語番号が作業領域を越えた。式 (50.4) の image 実現不能にあたる。
r2 emssr589
        # try は例外が飛ばない限り費用ゼロなので、速い道の邪魔にならない
r2 emssr590
        raise IndexError('語番号が作業領域を越える(語数 %d)。--words を増やす'
r2 emssr591
                         % len(M))
r2 emssr592
    raise RuntimeError('block の歩数上限 %d を越えた' % budget)
r2 emssr593
r2 emssr594
r2 emssr595
# ===== 解釈器(式 2.1、3.1、3.2、3.3)=====
r2 emssr596
#
r2 emssr597
# active relation は W bit の整数一個で持つ。m1 = m & c、m0 = m & ~c が
r2 emssr598
# それぞれ一演算になる。速さのためではなく、意味の定義を一箇所に置くために書く。
r2 emssr599
r2 emssr600
class Interp(object):
r2 emssr601
r2 emssr602
    def __init__(self, prog, lanes=1, words=4096):
r2 emssr603
        self.prog = prog
r2 emssr604
        self.W = lanes
r2 emssr605
        self.R = [[0] * LANDMARKS for _ in range(lanes)]
r2 emssr606
        self.M = [[0] * words for _ in range(lanes)]
r2 emssr607
        self.OUT = [[] for _ in range(lanes)]
r2 emssr608
        self.report = [None] * lanes
r2 emssr609
        self.steps = 0
r2 emssr610
r2 emssr611
    def put_input(self, lane, data, base):
r2 emssr612
        m = self.M[lane]
r2 emssr613
        n = min(len(data), len(m) - base)
r2 emssr614
        m[0] = n
r2 emssr615
        m[1] = base
r2 emssr616
        m[base:base + n] = data[:n]
r2 emssr617
        return n
r2 emssr618
r2 emssr619
    _one = (0,)
r2 emssr620
r2 emssr621
    def _active(self, m):
r2 emssr622
        return [l for l in range(self.W) if (m >> l) & 1]
r2 emssr623
r2 emssr624
    def run(self, budget=1 << 30):
r2 emssr625
        prog, n = self.prog, len(self.prog)
r2 emssr626
        groups = [(((1 << self.W) - 1), 0)]
r2 emssr627
        while groups:
r2 emssr628
            self.steps += 1
r2 emssr629
            if self.steps > budget:
r2 emssr630
                raise RuntimeError('歩数上限 %d を越えた' % budget)
r2 emssr631
            if len(groups) == 1:
r2 emssr632
                msk, pc = groups.pop()          # 判定が割れるまでは組が一つ
r2 emssr633
            else:
r2 emssr634
                k = min(range(len(groups)), key=lambda i: groups[i][1])
r2 emssr635
                msk, pc = groups.pop(k)
r2 emssr636
            if msk == 0 or pc < 0 or pc >= n:
r2 emssr637
                continue
r2 emssr638
            op, a, b, c, sb, sc, im = prog[pc]
r2 emssr639
            act = (self._one if msk == 1 else self._active(msk))
r2 emssr640
            if op == HALT:
r2 emssr641
                continue
r2 emssr642
            if op == OK or op == KO:
r2 emssr643
                for l in act:
r2 emssr644
                    self.report[l] = 0 if op == OK else 42
r2 emssr645
                continue
r2 emssr646
            if op == JMP:
r2 emssr647
                groups = _merge(groups, [(msk, pc + im)])
r2 emssr648
                continue
r2 emssr649
            if op == EQ or op == LT:
r2 emssr650
                cm = 0
r2 emssr651
                for l in act:
r2 emssr652
                    R = self.R[l]
r2 emssr653
                    u, v = R[b] << sb, R[c] << sc
r2 emssr654
                    if (u == v) if op == EQ else (u < v):
r2 emssr655
                        cm |= 1 << l
r2 emssr656
                m1, m0 = msk & cm, msk & ~cm     # 式 (3.2)
r2 emssr657
                groups = _merge(groups, [(m1, pc + im), (m0, pc + 1)])
r2 emssr658
                continue
r2 emssr659
            # 値の箱。mask を外れた lane は動かない(式 2.1)
r2 emssr660
            for l in act:
r2 emssr661
                R = self.R[l]
r2 emssr662
                if op == MAT:
r2 emssr663
                    R[a] = im
r2 emssr664
                elif op == ADD:
r2 emssr665
                    R[a] = (R[b] << sb) + (R[c] << sc)
r2 emssr666
                elif op == MUL:
r2 emssr667
                    R[a] = R[b] * R[c]
r2 emssr668
                elif op == NEG:
r2 emssr669
                    R[a] = -R[b]
r2 emssr670
                elif op == LD:
r2 emssr671
                    R[a] = self.M[l][R[b]]
r2 emssr672
                elif op == ST:
r2 emssr673
                    self.M[l][R[b]] = R[c]
r2 emssr674
                elif op == PUT:
r2 emssr675
                    self.OUT[l].append(R[b] & 255)
r2 emssr676
            groups = _merge(groups, [(msk, pc + 1)])
r2 emssr677
        return self.report
r2 emssr678
r2 emssr679
r2 emssr680
def _merge(groups, fresh):
r2 emssr681
    """reconvergence(式 3.3)。pc が同じ組は mask を合わせて一つにする。"""
r2 emssr682
    if not groups and len(fresh) == 1 and fresh[0][0]:
r2 emssr683
        return list(fresh)                      # 割れていない間はそのまま渡す
r2 emssr684
    out = list(groups)
r2 emssr685
    for msk, pc in fresh:
r2 emssr686
        if msk == 0:
r2 emssr687
            continue
r2 emssr688
        for k, (m2, p2) in enumerate(out):
r2 emssr689
            if p2 == pc:
r2 emssr690
                out[k] = (m2 | msk, pc)
r2 emssr691
                break
r2 emssr692
        else:
r2 emssr693
            out.append((msk, pc))
r2 emssr694
    return out
r2 emssr695
r2 emssr696
r2 emssr697
# ===== lane を process へ配る(20 章 §0.5)=====
r2 emssr698
#
r2 emssr699
# 立ち上げに 600 ms かかるので、pool は host の寿命だけ持ち続ける。
r2 emssr700
# lane の作業領域が式 (1.2) で交わらないから、持ち回しても state が混ざらない。
r2 emssr701
r2 emssr702
_W = {}          # worker の中の束縛。image ごとに一度だけ翻訳する
r2 emssr703
r2 emssr704
r2 emssr705
def _worker_init():
r2 emssr706
    _W.clear()
r2 emssr707
r2 emssr708
r2 emssr709
def _worker_run(job):
r2 emssr710
    image, words, data, in_base, head, emit, count = job
r2 emssr711
    # 束縛の鍵は image そのものにする。長さや id で引くと、同じ大きさの別の
r2 emssr712
    # image が前の翻訳を拾う
r2 emssr713
    tab = _W.get(image)
r2 emssr714
    if tab is None:
r2 emssr715
        tab = translate(decode(image))[1]
r2 emssr716
        _W[image] = tab
r2 emssr717
    res = []
r2 emssr718
    for _ in range(count):
r2 emssr719
        lane = Lane(words)
r2 emssr720
        if data is not None:
r2 emssr721
            lane.put_input(data, in_base)
r2 emssr722
        rep = run_lane(tab, lane)
r2 emssr723
        emitted = None
r2 emssr724
        if emit:
r2 emssr725
            base, ln = lane.M[1], lane.M[2]
r2 emssr726
            if base and ln:
r2 emssr727
                emitted = bytes(v & 255 for v in lane.M[base:base + ln])
r2 emssr728
        res.append((rep, bytes(lane.OUT), list(lane.M[:head]), emitted, lane.blocks))
r2 emssr729
    return res
r2 emssr730
r2 emssr731
r2 emssr732
def _run_share(tab, count, words, data, in_base, head, emit):
r2 emssr733
    """同じ image を lane 何本か分だけ回す。lane の作業領域は式 (1.2) で
r2 emssr734
    交わらないので、この呼びは互いに独立になる(定理 21.1)。"""
r2 emssr735
    out = []
r2 emssr736
    for _ in range(count):
r2 emssr737
        lane = Lane(words)
r2 emssr738
        if data is not None:
r2 emssr739
            lane.put_input(data, in_base)
r2 emssr740
        rep = run_lane(tab, lane)
r2 emssr741
        emitted = None
r2 emssr742
        if emit:
r2 emssr743
            base, ln = lane.M[1], lane.M[2]
r2 emssr744
            if base and ln:
r2 emssr745
                emitted = bytes(v & 255 for v in lane.M[base:base + ln])
r2 emssr746
        out.append((rep, bytes(lane.OUT), list(lane.M[:head]), emitted,
r2 emssr747
                    lane.blocks))
r2 emssr748
    return out
r2 emssr749
r2 emssr750
r2 emssr751
class VCPU(object):
r2 emssr752
    """常駐の実行台。pool を作り直さない(式 0.4)。
r2 emssr753
r2 emssr754
    lane を配る先は二つある。process は写す量が要るかわりに本当に同時に走る。
r2 emssr755
    thread は image の翻訳を共有できるので配る費用がほぼ無い。どちらを取るかは
r2 emssr756
    lane 一本の仕事量で決まるので、両方を持って測れるようにする。
r2 emssr757
    """
r2 emssr758
r2 emssr759
    def __init__(self, procs=1, threads=1):
r2 emssr760
        self.P = max(1, int(procs))
r2 emssr761
        self.T = max(1, int(threads))
r2 emssr762
        self.pool = None
r2 emssr763
        self.tpool = None
r2 emssr764
        self._tab = {}
r2 emssr765
r2 emssr766
    def close(self):
r2 emssr767
        if self.pool is not None:
r2 emssr768
            self.pool.close()
r2 emssr769
            self.pool.join()
r2 emssr770
            self.pool = None
r2 emssr771
        if self.tpool is not None:
r2 emssr772
            self.tpool.shutdown()
r2 emssr773
            self.tpool = None
r2 emssr774
r2 emssr775
    def _ensure(self):
r2 emssr776
        if self.pool is None and self.P > 1:
r2 emssr777
            import multiprocessing as mp
r2 emssr778
            self.pool = mp.Pool(self.P, initializer=_worker_init)
r2 emssr779
        return self.pool
r2 emssr780
r2 emssr781
    def _threads(self):
r2 emssr782
        """立ち上げたら畳まない。次の run は待機中の worker がそのまま拾う。"""
r2 emssr783
        if self.tpool is None and self.T > 1:
r2 emssr784
            from concurrent.futures import ThreadPoolExecutor
r2 emssr785
            self.tpool = ThreadPoolExecutor(self.T)
r2 emssr786
            list(self.tpool.map(lambda _: None, range(self.T)))
r2 emssr787
        return self.tpool
r2 emssr788
r2 emssr789
    def _shares(self, lanes, n):
r2 emssr790
        return [c for c in (lanes // n + (1 if k < lanes % n else 0)
r2 emssr791
                            for k in range(n)) if c]
r2 emssr792
r2 emssr793
    def _table(self, image):
r2 emssr794
        tab = self._tab.get(image)
r2 emssr795
        if tab is None:
r2 emssr796
            tab = translate(decode(image))[1]
r2 emssr797
            self._tab[image] = tab
r2 emssr798
        return tab
r2 emssr799
r2 emssr800
    def run(self, image, lanes=1, words=4096, data=None, in_base=64,
r2 emssr801
            head=64, emit=False):
r2 emssr802
        """lane を P 本の process へ均等に配る。返りは lane ごとの組になる。
r2 emssr803
r2 emssr804
        組は (報告, Put の byte 列, 先頭 head 語, 取り出した image, block 数)。
r2 emssr805
        作業領域まるごとを持ち帰らないのは、process 間で写す量を抑えるためになる。
r2 emssr806
        """
r2 emssr807
        if lanes > 1 and self.T > 1:
r2 emssr808
            tab = self._table(image)
r2 emssr809
            futs = [self._threads().submit(_run_share, tab, c, words, data,
r2 emssr810
                                           in_base, head, emit)
r2 emssr811
                    for c in self._shares(lanes, self.T)]
r2 emssr812
            res = []
r2 emssr813
            for f in futs:
r2 emssr814
                res.extend(f.result())
r2 emssr815
            return res
r2 emssr816
        if self.P <= 1 or lanes == 1:
r2 emssr817
            return _run_share(self._table(image), lanes, words, data, in_base,
r2 emssr818
                              head, emit)
r2 emssr819
        jobs = [(image, words, data, in_base, head, emit, c)
r2 emssr820
                for c in self._shares(lanes, self.P)]
r2 emssr821
        res = []
r2 emssr822
        for part in self._ensure().map(_worker_run, jobs):
r2 emssr823
            res.extend(part)
r2 emssr824
        return res
r2 emssr825
r2 emssr826
r2 emssr827
# ===== CLI =====
r2 emssr828
r2 emssr829
def _usage():
r2 emssr830
    sys.stderr.write(
r2 emssr831
        'vcpu <image.bin> [option]\n'
r2 emssr832
        '  --lanes W    lane 数。既定 1(20 章 式 1.1)\n'
r2 emssr833
        '  --words S    lane あたりの語数。既定 4096\n'
r2 emssr834
        '  --procs P    lane を配る process の本数。既定 1\n'
r2 emssr835
        '  --in F       全 lane の作業領域へ F を写す。語 0 が byte 数、語 1 が基底\n'
r2 emssr836
        '  --in-base B  本文の基底語番号。既定 64\n'
r2 emssr837
        '  --dump N     復帰後に語 0..N-1 を出す\n'
r2 emssr838
        '  --emit F     lane 0 が組んだ image を取り出す(19 章 §3.1)\n'
r2 emssr839
        '  --interp     式 (2.1) の mask 付き解釈器で走らせる\n'
r2 emssr840
        '  --cupy       語も述語も bit のまま GPU で走らせる(cuvcpu)\n'
r2 emssr841
        '  --bits NB    --cupy の語の幅。既定 32\n'
r2 emssr842
        '  --list       image を逆アセンブルして並べる\n'
r2 emssr843
        '  --source     翻訳した Python を出す\n')
r2 emssr844
r2 emssr845
r2 emssr846
def main(argv):
r2 emssr847
    if len(argv) < 2:
r2 emssr848
        _usage()
r2 emssr849
        return 2
r2 emssr850
    img_path = argv[1]
r2 emssr851
    lanes, words, procs, dump, in_base, threads = 1, 4096, 1, 0, 64, 1
r2 emssr852
    par = 0
r2 emssr853
    bits = 32
r2 emssr854
    in_path = emit_path = None
r2 emssr855
    listing = interp = source = cupy = False
r2 emssr856
    i = 2
r2 emssr857
    while i < len(argv):
r2 emssr858
        a = argv[i]
r2 emssr859
        if a in ('--lanes', '--words', '--procs', '--dump', '--in-base',
r2 emssr860
                 '--threads', '--par', '--bits'):
r2 emssr861
            i += 1
r2 emssr862
            v = int(argv[i])
r2 emssr863
            if a == '--lanes':
r2 emssr864
                lanes = v
r2 emssr865
            elif a == '--words':
r2 emssr866
                words = v
r2 emssr867
            elif a == '--procs':
r2 emssr868
                procs = v
r2 emssr869
            elif a == '--threads':
r2 emssr870
                threads = v
r2 emssr871
            elif a == '--par':
r2 emssr872
                par = v
r2 emssr873
            elif a == '--bits':
r2 emssr874
                bits = v
r2 emssr875
            elif a == '--dump':
r2 emssr876
                dump = v
r2 emssr877
            else:
r2 emssr878
                in_base = v
r2 emssr879
        elif a == '--in':
r2 emssr880
            i += 1
r2 emssr881
            in_path = argv[i]
r2 emssr882
        elif a == '--emit':
r2 emssr883
            i += 1
r2 emssr884
            emit_path = argv[i]
r2 emssr885
        elif a == '--list':
r2 emssr886
            listing = True
r2 emssr887
        elif a == '--interp':
r2 emssr888
            interp = True
r2 emssr889
        elif a == '--cupy':
r2 emssr890
            cupy = True
r2 emssr891
        elif a == '--source':
r2 emssr892
            source = True
r2 emssr893
        else:
r2 emssr894
            _usage()
r2 emssr895
            return 2
r2 emssr896
        i += 1
r2 emssr897
r2 emssr898
    with open(img_path, 'rb') as f:
r2 emssr899
        image = f.read()
r2 emssr900
    prog = decode(image)
r2 emssr901
r2 emssr902
    if listing:
r2 emssr903
        for ln in disasm(prog):
r2 emssr904
            print(ln)
r2 emssr905
        return 0
r2 emssr906
    if source:
r2 emssr907
        print(translate(prog)[0])
r2 emssr908
        return 0
r2 emssr909
r2 emssr910
    data = None
r2 emssr911
    if in_path is not None:
r2 emssr912
        with open(in_path, 'rb') as f:
r2 emssr913
            data = f.read()
r2 emssr914
r2 emssr915
    t0 = time.perf_counter()
r2 emssr916
    if interp:
r2 emssr917
        m = Interp(prog, lanes=lanes, words=words)
r2 emssr918
        if data is not None:
r2 emssr919
            for l in range(lanes):
r2 emssr920
                m.put_input(l, data, in_base)
r2 emssr921
        rep = m.run()
r2 emssr922
        res = [(rep[l], bytes(m.OUT[l]), m.M[l][:max(dump, 3)], None, 0)
r2 emssr923
               for l in range(lanes)]
r2 emssr924
        note = '解釈器 %d 歩' % m.steps
r2 emssr925
    elif cupy:
r2 emssr926
        import cuvcpu
r2 emssr927
        res = cuvcpu.run(prog, lanes=lanes, words=words, bits=bits, data=data,
r2 emssr928
                         in_base=in_base, head=max(dump, 4),
r2 emssr929
                         emit=emit_path is not None)
r2 emssr930
        note = 'GPU %d bit  %d 歩' % (bits, res[0][4])
r2 emssr931
    elif par:
r2 emssr932
        import multiprocessing as mp
r2 emssr933
        tab = None
r2 emssr934
        pool = mp.Pool(par)
r2 emssr935
        lane = Lane(words)
r2 emssr936
        if data is not None:
r2 emssr937
            lane.put_input(data, in_base)
r2 emssr938
        rep = run_lane_par(prog, lane, pool)
r2 emssr939
        pool.close()
r2 emssr940
        pool.join()
r2 emssr941
        res = [(rep, bytes(lane.OUT), list(lane.M[:max(dump, 4)]), None,
r2 emssr942
                lane.blocks)]
r2 emssr943
        note = '層並列 block %d 回' % lane.blocks
r2 emssr944
        del tab
r2 emssr945
    else:
r2 emssr946
        vm = VCPU(procs, threads)
r2 emssr947
        res = vm.run(image, lanes=lanes, words=words, data=data,
r2 emssr948
                     in_base=in_base, head=max(dump, 4), emit=emit_path is not None)
r2 emssr949
        vm.close()
r2 emssr950
        note = '翻訳器 block %d 回' % sum(r[4] for r in res)
r2 emssr951
    el = time.perf_counter() - t0
r2 emssr952
r2 emssr953
    bad = 0
r2 emssr954
    for l, (rep, out, head, _em, _b) in enumerate(res):
r2 emssr955
        tag = 'tu' if rep == 0 else ('te' if rep == 42 else '報告なし')
r2 emssr956
        if rep != 0:
r2 emssr957
            bad += 1
r2 emssr958
        if lanes <= 8 or l < 2 or l == lanes - 1:
r2 emssr959
            print('lane %-4d %s' % (l, tag))
r2 emssr960
    print('%s   %.3f 秒   lane %d   process %d   thread %d'
r2 emssr961
          % (note, el, lanes, procs, threads))
r2 emssr962
r2 emssr963
    rep0, out0, head0, em0, _ = res[0]
r2 emssr964
    if out0:
r2 emssr965
        print('out    : %d byte  %s' % (len(out0), out0.decode('latin-1')))
r2 emssr966
    for k in range(dump):
r2 emssr967
        v = head0[k]
r2 emssr968
        # 分子は Z なので、深い作図では十進へ落とすほうが計算より重くなる
r2 emssr969
        if v.bit_length() > 4096:
r2 emssr970
            print('  w[%d] = %d bit の整数' % (k, v.bit_length()))
r2 emssr971
        else:
r2 emssr972
            print('  w[%d] = %d' % (k, v))
r2 emssr973
    if emit_path is not None:
r2 emssr974
        if em0 is None:
r2 emssr975
            sys.stderr.write('image が空(語1=%d 語2=%d)\n' % (head0[1], head0[2]))
r2 emssr976
            return 1
r2 emssr977
        with open(emit_path, 'wb') as f:
r2 emssr978
            f.write(em0)
r2 emssr979
        print('image  : %s  %d byte' % (emit_path, len(em0)))
r2 emssr980
    return 1 if bad else 0
r2 emssr981
r2 emssr982
r2 emssr983
if __name__ == '__main__':
r2 emssr984
    sys.exit(main(sys.argv))