📦 Out of Context リポジトリ

リポジトリ / emssr / independent_common_compiler

r2
r2 emssr1
# -*- coding: utf-8 -*-
r2 emssr2
"""lane64 の GPU 実行台(limb 版)。thread 一本が lane 一本になる。
r2 emssr3
r2 emssr4
仕様は docs/common-compiler/arch/20。式 (2.1) の解釈器と同じ意味を、CUDA の kernel の中で
r2 emssr5
lane ごとに走らせる。cuvcpu.py は語を bit 平面に持ち、命令一つが平面の演算の列になるので、
r2 emssr6
語が広いと launch の数で足が止まる。ここでは語を 64 bit の limb の並びで持ち、lane ごとの
r2 emssr7
thread が block を跨いで命令を辿る。分岐は thread ごとに好きに飛ぶ(mask は要らない)。
r2 emssr8
r2 emssr9
  並び   語 主。語 w の limb i、lane l は M[(w·NLIMB + i)·lanes + l] に置く。
r2 emssr10
         同じ語を読む lane の番地が連続するので、warp の memory access が一度で済む。
r2 emssr11
         lane 主に置くと lane ごとに words·NLIMB 語ずつ離れ、一命令が 32 回の転送になる
r2 emssr12
  語     NLIMB 本の limb。二の補数、little endian。既定 16 本 = 1024 bit
r2 emssr13
  landmark  R[0..7]。commonc の lane64 版は R1 R2 R3 だけを使う(back_lane64.cam の覆い)
r2 emssr14
  作業領域  lane ごとに words 語。読む語(watch)だけを host へ戻す
r2 emssr15
r2 emssr16
語の幅を越えた乗算は報告 43 で止まる。lane64 の語は ℤ なので(20 §0.3)、この道は
r2 emssr17
NLIMB·64 bit を越えた時点で core 意味論と離れる。
r2 emssr18
r2 emssr19
  run(prog, seeds, words, watch, need=None) → lane ごとの (報告, {語: 値})
r2 emssr20
"""
r2 emssr21
r2 emssr22
import numpy as np
r2 emssr23
r2 emssr24
INSN_FIELDS = 7
r2 emssr25
NREG_BYTES = 8 * 8          # shared memory へ置く register。NREG 本 × 8 byte
r2 emssr26
R_OK, R_KO, R_WIDE, R_BUDGET = 0, 42, 43, 44
r2 emssr27
r2 emssr28
SRC = r"""
r2 emssr29
#define NLIMB %(nlimb)d
r2 emssr30
#define NREG 8
r2 emssr31
typedef unsigned long long u64;
r2 emssr32
typedef long long i64;
r2 emssr33
r2 emssr34
struct Big { u64 v[NLIMB]; };
r2 emssr35
r2 emssr36
__device__ __forceinline__ void big_zero(Big &r) { for (int i = 0; i < NLIMB; i++) r.v[i] = 0; }
r2 emssr37
__device__ __forceinline__ void big_imm(Big &r, i64 x) {
r2 emssr38
    r.v[0] = (u64)x; u64 f = (x < 0) ? ~0ULL : 0ULL;
r2 emssr39
    for (int i = 1; i < NLIMB; i++) r.v[i] = f;
r2 emssr40
}
r2 emssr41
__device__ __forceinline__ bool big_neg_p(const Big &a) { return (a.v[NLIMB - 1] >> 63) != 0; }
r2 emssr42
__device__ __forceinline__ void big_add(Big &r, const Big &a, const Big &b) {
r2 emssr43
    u64 c = 0;
r2 emssr44
    for (int i = 0; i < NLIMB; i++) {
r2 emssr45
        u64 s = a.v[i] + b.v[i]; u64 c1 = (s < a.v[i]) ? 1 : 0;
r2 emssr46
        u64 t = s + c; u64 c2 = (t < s) ? 1 : 0;
r2 emssr47
        r.v[i] = t; c = c1 | c2;
r2 emssr48
    }
r2 emssr49
}
r2 emssr50
__device__ __forceinline__ void big_not(Big &r, const Big &a) { for (int i = 0; i < NLIMB; i++) r.v[i] = ~a.v[i]; }
r2 emssr51
__device__ __forceinline__ void big_negate(Big &r, const Big &a) {
r2 emssr52
    Big n; big_not(n, a); Big one; big_imm(one, 1); big_add(r, n, one);
r2 emssr53
}
r2 emssr54
__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 emssr55
__device__ __forceinline__ bool big_eq(const Big &a, const Big &b) {
r2 emssr56
    for (int i = 0; i < NLIMB; i++) if (a.v[i] != b.v[i]) return false; return true;
r2 emssr57
}
r2 emssr58
__device__ __forceinline__ bool big_lt(const Big &a, const Big &b) {
r2 emssr59
    bool na = big_neg_p(a), nb = big_neg_p(b);
r2 emssr60
    if (na != nb) return na;
r2 emssr61
    for (int i = NLIMB - 1; i >= 0; i--) { if (a.v[i] != b.v[i]) return a.v[i] < b.v[i]; }
r2 emssr62
    return false;
r2 emssr63
}
r2 emssr64
__device__ __forceinline__ void big_shl(Big &r, const Big &a, int s) {
r2 emssr65
    if (s <= 0) { r = a; return; }
r2 emssr66
    int w = s / 64, bts = s %% 64;
r2 emssr67
    Big t; big_zero(t);
r2 emssr68
    for (int i = NLIMB - 1; i >= w; i--) {
r2 emssr69
        u64 hi = a.v[i - w] << bts;
r2 emssr70
        u64 lo = (bts && i - w - 1 >= 0) ? (a.v[i - w - 1] >> (64 - bts)) : 0;
r2 emssr71
        t.v[i] = hi | lo;
r2 emssr72
    }
r2 emssr73
    r = t;
r2 emssr74
}
r2 emssr75
// |a|·|b| を 2·NLIMB limb で作り、上半分が零でなければ幅を越えたとみなす
r2 emssr76
__device__ bool big_mul(Big &r, const Big &a0, const Big &b0) {
r2 emssr77
    Big a, b; bool na = big_neg_p(a0), nb = big_neg_p(b0);
r2 emssr78
    if (na) big_negate(a, a0); else a = a0;
r2 emssr79
    if (nb) big_negate(b, b0); else b = b0;
r2 emssr80
    // 上位の零の limb は掛けない。小さい数どうしなら 1 回の乗算で済む
r2 emssr81
    int la = 0, lb = 0;
r2 emssr82
    for (int i = 0; i < NLIMB; i++) { if (a.v[i]) la = i + 1; if (b.v[i]) lb = i + 1; }
r2 emssr83
    u64 acc[2 * NLIMB];
r2 emssr84
    for (int i = 0; i < 2 * NLIMB; i++) acc[i] = 0;
r2 emssr85
    for (int i = 0; i < la; i++) {
r2 emssr86
        u64 carry = 0;
r2 emssr87
        for (int j = 0; j < lb; j++) {
r2 emssr88
            u64 lo = a.v[i] * b.v[j];
r2 emssr89
            u64 hi = __umul64hi(a.v[i], b.v[j]);
r2 emssr90
            u64 s = acc[i + j] + lo; u64 c1 = (s < lo) ? 1 : 0;
r2 emssr91
            u64 t = s + carry; u64 c2 = (t < s) ? 1 : 0;
r2 emssr92
            acc[i + j] = t;
r2 emssr93
            carry = hi + c1 + c2;
r2 emssr94
        }
r2 emssr95
        int k = i + lb;
r2 emssr96
        while (carry && k < 2 * NLIMB) { u64 s = acc[k] + carry; carry = (s < carry) ? 1 : 0; acc[k] = s; k++; }
r2 emssr97
    }
r2 emssr98
    bool wide = false;
r2 emssr99
    for (int i = NLIMB; i < 2 * NLIMB; i++) if (acc[i]) wide = true;
r2 emssr100
    if (acc[NLIMB - 1] >> 63) wide = true;
r2 emssr101
    for (int i = 0; i < NLIMB; i++) r.v[i] = acc[i];
r2 emssr102
    if (na != nb) { Big t = r; big_negate(r, t); }
r2 emssr103
    return wide;
r2 emssr104
}
r2 emssr105
r2 emssr106
// 語の番地 → 詰めた並びの位置。work が 0 なら詰めない
r2 emssr107
__device__ __forceinline__ i64 slot(i64 a, int need, int work, int words) {
r2 emssr108
    if (work == 0) return (a >= 0 && a < words) ? a : -1;
r2 emssr109
    if (a >= 0 && a < need) return a;
r2 emssr110
    if (a >= work && a - work + need < words) return a - work + need;
r2 emssr111
    return -1;
r2 emssr112
}
r2 emssr113
r2 emssr114
extern "C" __global__
r2 emssr115
void lane64_run(const i64 *prog, int ninsn, u64 *M, int words, int lanes, i64 budget,
r2 emssr116
                int need, int work, int *report, i64 *steps,
r2 emssr117
                const u64 *ROM, i64 romlo, i64 romn)
r2 emssr118
{
r2 emssr119
    int lane = blockIdx.x * blockDim.x + threadIdx.x;
r2 emssr120
    if (lane >= lanes) return;
r2 emssr121
    Big R[NREG];
r2 emssr122
    int pc = 0; i64 n = 0; int rep = %(r_budget)d;
r2 emssr123
    while (n < budget) {
r2 emssr124
        n++;
r2 emssr125
        if (pc < 0 || pc >= ninsn) { rep = %(r_end)d; break; }
r2 emssr126
        const i64 *ins = prog + (size_t)pc * %(fields)d;
r2 emssr127
        int op = (int)ins[0], a = (int)ins[1], b = (int)ins[2], c = (int)ins[3];
r2 emssr128
        int sb = (int)ins[4], sc = (int)ins[5]; i64 im = ins[6];
r2 emssr129
        if (op == 0) { rep = %(r_end)d; break; }                         // HALT
r2 emssr130
        else if (op == 1) { big_imm(R[a], im); pc++; }                  // MAT
r2 emssr131
        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 emssr132
        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 emssr133
        else if (op == 4) { Big t; big_negate(t, R[b]); R[a] = t; pc++; }   // NEG
r2 emssr134
        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 emssr135
        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 emssr136
        else if (op == 7) { pc += (int)im; }                            // JMP
r2 emssr137
        else if (op == 8) {                                             // LD
r2 emssr138
            i64 raw = (i64)R[b].v[0];
r2 emssr139
            if (raw >= romlo) {                                         // 共有帯。lane で割らない
r2 emssr140
                i64 o = raw - romlo; if (o >= romn) { rep = %(r_end)d; break; }
r2 emssr141
                const u64 *w = ROM + (size_t)o * NLIMB;
r2 emssr142
                for (int i = 0; i < NLIMB; i++) R[a].v[i] = w[i]; pc++;
r2 emssr143
            } else {
r2 emssr144
                i64 addr = slot(raw, need, work, words); if (addr < 0) { rep = %(r_end)d; break; }
r2 emssr145
                const u64 *w = M + ((size_t)addr * NLIMB) * (size_t)lanes + lane;
r2 emssr146
                for (int i = 0; i < NLIMB; i++) R[a].v[i] = w[(size_t)i * lanes]; pc++;
r2 emssr147
            }
r2 emssr148
        }
r2 emssr149
        else if (op == 9) {                                             // ST
r2 emssr150
            i64 raw = (i64)R[b].v[0];
r2 emssr151
            if (raw >= romlo) { rep = %(r_end)d; break; }               // 共有帯へは書けない
r2 emssr152
            i64 addr = slot(raw, need, work, words); if (addr < 0) { rep = %(r_end)d; break; }
r2 emssr153
            u64 *w = M + ((size_t)addr * NLIMB) * (size_t)lanes + lane;
r2 emssr154
            for (int i = 0; i < NLIMB; i++) w[(size_t)i * lanes] = R[c].v[i]; pc++;
r2 emssr155
        }
r2 emssr156
        else if (op == 10) { rep = %(r_ok)d; break; }                   // OK
r2 emssr157
        else if (op == 11) { rep = %(r_ko)d; break; }                   // KO
r2 emssr158
        else if (op == 12) { pc++; }                                    // PUT(GPU では捨てる)
r2 emssr159
        else { rep = %(r_end)d; break; }
r2 emssr160
    }
r2 emssr161
    report[lane] = rep; steps[lane] = n;
r2 emssr162
}
r2 emssr163
r2 emssr164
r2 emssr165
// ===== 語が 64 bit で足りるときの道 =====
r2 emssr166
//
r2 emssr167
// Big R[NREG] は添字が命令から来るので、compiler が register に置けず local memory へ落ちる。
r2 emssr168
// 一命令ごとに local への往復が入り、thread 一本の歩みが桁で遅くなる。
r2 emssr169
// 語を 64 bit に決め打てる場合は、register を shared memory の
r2 emssr170
//
r2 emssr171
//     R[k]  =  sreg[k * blockDim.x + threadIdx.x]
r2 emssr172
//
r2 emssr173
// へ置く。stride が blockDim.x なので bank が衝突せず、添字が動いても速い。
r2 emssr174
// 積が 64 bit を越えたら幅の報告で止める(意味は Big の道と同じ)。
r2 emssr175
r2 emssr176
extern "C" __global__
r2 emssr177
void lane64_run1(const i64 *prog, int ninsn, u64 *M, int words, int lanes, i64 budget,
r2 emssr178
                 int need, int work, int *report, i64 *steps,
r2 emssr179
                 const u64 *ROM, i64 romlo, i64 romn)
r2 emssr180
{
r2 emssr181
    extern __shared__ i64 sreg[];
r2 emssr182
    int lane = blockIdx.x * blockDim.x + threadIdx.x;
r2 emssr183
    if (lane >= lanes) return;
r2 emssr184
    i64 *R = sreg + threadIdx.x;
r2 emssr185
    const int rs = blockDim.x;
r2 emssr186
    int pc = 0; i64 n = 0; int rep = %(r_budget)d;
r2 emssr187
    while (n < budget) {
r2 emssr188
        n++;
r2 emssr189
        if (pc < 0 || pc >= ninsn) { rep = %(r_end)d; break; }
r2 emssr190
        const i64 *ins = prog + (size_t)pc * %(fields)d;
r2 emssr191
        int op = (int)ins[0], a = (int)ins[1], b = (int)ins[2], c = (int)ins[3];
r2 emssr192
        int sb = (int)ins[4], sc = (int)ins[5]; i64 im = ins[6];
r2 emssr193
        if (op == 0) { rep = %(r_end)d; break; }
r2 emssr194
        else if (op == 1) { R[a * rs] = im; pc++; }
r2 emssr195
        else if (op == 2) { R[a * rs] = (R[b * rs] << sb) + (R[c * rs] << sc); pc++; }
r2 emssr196
        else if (op == 3) {
r2 emssr197
            i64 x = R[b * rs], y = R[c * rs];
r2 emssr198
            u64 ux = (u64)(x < 0 ? -x : x), uy = (u64)(y < 0 ? -y : y);
r2 emssr199
            if (__umul64hi(ux, uy) || ((ux * uy) >> 63)) { rep = %(r_wide)d; break; }
r2 emssr200
            R[a * rs] = x * y; pc++;
r2 emssr201
        }
r2 emssr202
        else if (op == 4) { R[a * rs] = -R[b * rs]; pc++; }
r2 emssr203
        else if (op == 5) { pc += ((R[b * rs] << sb) == (R[c * rs] << sc)) ? (int)im : 1; }
r2 emssr204
        else if (op == 6) { pc += ((R[b * rs] << sb) < (R[c * rs] << sc)) ? (int)im : 1; }
r2 emssr205
        else if (op == 7) { pc += (int)im; }
r2 emssr206
        else if (op == 8) {
r2 emssr207
            i64 raw = R[b * rs];
r2 emssr208
            if (raw >= romlo) {                                         // 共有帯。lane で割らない
r2 emssr209
                i64 o = raw - romlo; if (o >= romn) { rep = %(r_end)d; break; }
r2 emssr210
                R[a * rs] = (i64)ROM[(size_t)o]; pc++;
r2 emssr211
            } else {
r2 emssr212
                i64 addr = slot(raw, need, work, words); if (addr < 0) { rep = %(r_end)d; break; }
r2 emssr213
                R[a * rs] = (i64)M[(size_t)addr * (size_t)lanes + lane]; pc++;
r2 emssr214
            }
r2 emssr215
        }
r2 emssr216
        else if (op == 9) {
r2 emssr217
            i64 raw = R[b * rs];
r2 emssr218
            if (raw >= romlo) { rep = %(r_end)d; break; }               // 共有帯へは書けない
r2 emssr219
            i64 addr = slot(raw, need, work, words); if (addr < 0) { rep = %(r_end)d; break; }
r2 emssr220
            M[(size_t)addr * (size_t)lanes + lane] = (u64)R[c * rs]; pc++;
r2 emssr221
        }
r2 emssr222
        else if (op == 10) { rep = %(r_ok)d; break; }
r2 emssr223
        else if (op == 11) { rep = %(r_ko)d; break; }
r2 emssr224
        else if (op == 12) { pc++; }
r2 emssr225
        else { rep = %(r_end)d; break; }
r2 emssr226
    }
r2 emssr227
    report[lane] = rep; steps[lane] = n;
r2 emssr228
}
r2 emssr229
r2 emssr230
// ===== 多数の lane が一つの仕事を分ける道 =====
r2 emssr231
//
r2 emssr232
// 段と段の間では、lane i が書いた値を lane j が読む。そのために
r2 emssr233
//
r2 emssr234
//   1. 共有帯へ書ける(既存の道は報告 45 で止める)
r2 emssr235
//   2. 書いた後に grid 全体で足並みを揃える
r2 emssr236
//
r2 emssr237
// の二つが要る。合図は「共有帯への書き込み」そのものにする。境界へ書く所が
r2 emssr238
// チェンバーの接合点なので、pc を外から指さなくて済む。全 lane が段ごとに
r2 emssr239
// ちょうど一度ずつ書く形の平文でだけ使える。
r2 emssr240
//
r2 emssr241
// 足並み揃えは世代を反転させる素朴な形。block が全部同時に載っていないと
r2 emssr242
// 止まるので、grid は device に載る数までに抑える(host 側で決める)。
r2 emssr243
r2 emssr244
__device__ __forceinline__ void gbar(int *cnt, int *gen, int nblocks)
r2 emssr245
{
r2 emssr246
    __syncthreads();
r2 emssr247
    if (threadIdx.x == 0) {
r2 emssr248
        int g = *((volatile int *)gen);
r2 emssr249
        __threadfence();
r2 emssr250
        if (atomicAdd(cnt, 1) == nblocks - 1) {
r2 emssr251
            *cnt = 0;
r2 emssr252
            __threadfence();
r2 emssr253
            atomicExch(gen, g + 1);
r2 emssr254
        } else {
r2 emssr255
            while (*((volatile int *)gen) == g) __threadfence();
r2 emssr256
        }
r2 emssr257
    }
r2 emssr258
    __syncthreads();
r2 emssr259
}
r2 emssr260
r2 emssr261
extern "C" __global__
r2 emssr262
void lane64_runb(const i64 *prog, int ninsn, u64 *M, int words, int lanes, i64 budget,
r2 emssr263
                 int need, int work, int *report, i64 *steps,
r2 emssr264
                 u64 *ROM, i64 romlo, i64 romn, int *cnt, int *gen, int nblocks)
r2 emssr265
{
r2 emssr266
    extern __shared__ i64 sregb[];
r2 emssr267
    int lane = blockIdx.x * blockDim.x + threadIdx.x;
r2 emssr268
    i64 *R = sregb + threadIdx.x;
r2 emssr269
    const int rs = blockDim.x;
r2 emssr270
    // 足並みを揃える以上、休んでいる thread を作れない。lanes は grid x block に
r2 emssr271
    // ちょうど合わせる(余りの lane は自分の席で同じ文を走らせる)。
r2 emssr272
    int pc = 0; i64 n = 0; int rep = %(r_budget)d;
r2 emssr273
    while (n < budget) {
r2 emssr274
        n++;
r2 emssr275
        if (pc < 0 || pc >= ninsn) { rep = %(r_end)d; break; }
r2 emssr276
        const i64 *ins = prog + (size_t)pc * %(fields)d;
r2 emssr277
        int op = (int)ins[0], a = (int)ins[1], b = (int)ins[2], c = (int)ins[3];
r2 emssr278
        int sb = (int)ins[4], sc = (int)ins[5]; i64 im = ins[6];
r2 emssr279
        if (op == 0) { rep = %(r_end)d; break; }
r2 emssr280
        else if (op == 1) { R[a * rs] = im; pc++; }
r2 emssr281
        else if (op == 2) { R[a * rs] = (R[b * rs] << sb) + (R[c * rs] << sc); pc++; }
r2 emssr282
        else if (op == 3) {
r2 emssr283
            i64 x = R[b * rs], y = R[c * rs];
r2 emssr284
            u64 ux = (u64)(x < 0 ? -x : x), uy = (u64)(y < 0 ? -y : y);
r2 emssr285
            if (__umul64hi(ux, uy) || ((ux * uy) >> 63)) { rep = %(r_wide)d; break; }
r2 emssr286
            R[a * rs] = x * y; pc++;
r2 emssr287
        }
r2 emssr288
        else if (op == 4) { R[a * rs] = -R[b * rs]; pc++; }
r2 emssr289
        else if (op == 5) { pc += ((R[b * rs] << sb) == (R[c * rs] << sc)) ? (int)im : 1; }
r2 emssr290
        else if (op == 6) { pc += ((R[b * rs] << sb) < (R[c * rs] << sc)) ? (int)im : 1; }
r2 emssr291
        else if (op == 7) { pc += (int)im; }
r2 emssr292
        else if (op == 8) {
r2 emssr293
            i64 raw = R[b * rs];
r2 emssr294
            if (raw >= romlo) {
r2 emssr295
                i64 o = raw - romlo; if (o >= romn) { rep = %(r_end)d; break; }
r2 emssr296
                R[a * rs] = (i64)ROM[(size_t)o]; pc++;
r2 emssr297
            } else {
r2 emssr298
                i64 addr = slot(raw, need, work, words); if (addr < 0) { rep = %(r_end)d; break; }
r2 emssr299
                R[a * rs] = (i64)M[(size_t)addr * (size_t)lanes + lane]; pc++;
r2 emssr300
            }
r2 emssr301
        }
r2 emssr302
        else if (op == 9) {
r2 emssr303
            i64 raw = R[b * rs];
r2 emssr304
            if (raw >= romlo) {                    // 共有帯へ書く。ここが接合点
r2 emssr305
                i64 o = raw - romlo; if (o >= romn) { rep = %(r_end)d; break; }
r2 emssr306
                ROM[(size_t)o] = (u64)R[c * rs];
r2 emssr307
                pc++;
r2 emssr308
                __threadfence();
r2 emssr309
                gbar(cnt, gen, nblocks);
r2 emssr310
                continue;
r2 emssr311
            }
r2 emssr312
            i64 addr = slot(raw, need, work, words); if (addr < 0) { rep = %(r_end)d; break; }
r2 emssr313
            M[(size_t)addr * (size_t)lanes + lane] = (u64)R[c * rs]; pc++;
r2 emssr314
        }
r2 emssr315
        else if (op == 10) { rep = %(r_ok)d; break; }
r2 emssr316
        else if (op == 11) { rep = %(r_ko)d; break; }
r2 emssr317
        else if (op == 12) { pc++; }
r2 emssr318
        else { rep = %(r_end)d; break; }
r2 emssr319
    }
r2 emssr320
    report[lane] = rep; steps[lane] = n;
r2 emssr321
}
r2 emssr322
r2 emssr323
// ===== warp を pc の小さい順にそろえて歩く道 =====
r2 emssr324
//
r2 emssr325
// thread ごとに好きに飛ぶと、warp の中で pc が割れた瞬間から命令が直列になり、
r2 emssr326
// 語の読みも散って memory に縛られる(電力 50 W で頭打ち)。
r2 emssr327
// 20 §6 命題 6.1: 合流は最も小さい pc から取れば足りる。warp の中で最小の pc を
r2 emssr328
// 一つ選び、そこに居る lane だけが一命令進む。分岐で割れても、後ろの lane が
r2 emssr329
// 先に追いつくので、loop の出口で自然に合流する。
r2 emssr330
r2 emssr331
extern "C" __global__
r2 emssr332
void lane64_run1_sync(const i64 *prog, int ninsn, u64 *M, int words, int lanes, i64 budget,
r2 emssr333
                      int need, int work, int *report, i64 *steps,
r2 emssr334
                      const u64 *ROM, i64 romlo, i64 romn, int stride)
r2 emssr335
{
r2 emssr336
    // stride は語の帯の中の lane の数(M2 の列数)。lanes はいま走らせる lane の数で、
r2 emssr337
    // 先頭から lanes 本だけが歩く。段を長さの順に並べておけば、短い段が終わった後の
r2 emssr338
    // 歩は先頭の生きている lane だけで済む
r2 emssr339
    extern __shared__ i64 sreg[];
r2 emssr340
    int lane = blockIdx.x * blockDim.x + threadIdx.x;
r2 emssr341
    bool alive = lane < lanes;
r2 emssr342
    i64 *R = sreg + threadIdx.x;
r2 emssr343
    const int rs = blockDim.x;
r2 emssr344
    int pc = 0; i64 n = 0; int rep = %(r_budget)d;
r2 emssr345
    bool done = !alive;
r2 emssr346
    while (true) {
r2 emssr347
        unsigned live = __ballot_sync(0xffffffffu, !done);
r2 emssr348
        if (live == 0) break;
r2 emssr349
        int mypc = done ? 0x7fffffff : pc;
r2 emssr350
        int pcmin = __reduce_min_sync(0xffffffffu, mypc);
r2 emssr351
        if (done || pc != pcmin) continue;
r2 emssr352
        n++;
r2 emssr353
        if (n > budget) { done = true; continue; }
r2 emssr354
        if (pc < 0 || pc >= ninsn) { rep = %(r_end)d; done = true; continue; }
r2 emssr355
        const i64 *ins = prog + (size_t)pc * %(fields)d;
r2 emssr356
        int op = (int)ins[0], a = (int)ins[1], b = (int)ins[2], c = (int)ins[3];
r2 emssr357
        int sb = (int)ins[4], sc = (int)ins[5]; i64 im = ins[6];
r2 emssr358
        if (op == 0) { rep = %(r_end)d; done = true; }
r2 emssr359
        else if (op == 1) { R[a * rs] = im; pc++; }
r2 emssr360
        else if (op == 2) { R[a * rs] = (R[b * rs] << sb) + (R[c * rs] << sc); pc++; }
r2 emssr361
        else if (op == 3) {
r2 emssr362
            i64 x = R[b * rs], y = R[c * rs];
r2 emssr363
            u64 ux = (u64)(x < 0 ? -x : x), uy = (u64)(y < 0 ? -y : y);
r2 emssr364
            if (__umul64hi(ux, uy) || ((ux * uy) >> 63)) { rep = %(r_wide)d; done = true; }
r2 emssr365
            else { R[a * rs] = x * y; pc++; }
r2 emssr366
        }
r2 emssr367
        else if (op == 4) { R[a * rs] = -R[b * rs]; pc++; }
r2 emssr368
        else if (op == 5) { pc += ((R[b * rs] << sb) == (R[c * rs] << sc)) ? (int)im : 1; }
r2 emssr369
        else if (op == 6) { pc += ((R[b * rs] << sb) < (R[c * rs] << sc)) ? (int)im : 1; }
r2 emssr370
        else if (op == 7) { pc += (int)im; }
r2 emssr371
        else if (op == 8) {
r2 emssr372
            i64 raw = R[b * rs];
r2 emssr373
            if (raw >= romlo) {
r2 emssr374
                i64 o = raw - romlo; if (o >= romn) { rep = %(r_end)d; done = true; }
r2 emssr375
                else { R[a * rs] = (i64)ROM[(size_t)o]; pc++; }
r2 emssr376
            } else {
r2 emssr377
                i64 addr = slot(raw, need, work, words); if (addr < 0) { rep = %(r_end)d; done = true; }
r2 emssr378
                else { R[a * rs] = (i64)M[(size_t)addr * (size_t)stride + lane]; pc++; }
r2 emssr379
            }
r2 emssr380
        }
r2 emssr381
        else if (op == 9) {
r2 emssr382
            i64 raw = R[b * rs];
r2 emssr383
            if (raw >= romlo) { rep = %(r_end)d; done = true; }
r2 emssr384
            else {
r2 emssr385
                i64 addr = slot(raw, need, work, words); if (addr < 0) { rep = %(r_end)d; done = true; }
r2 emssr386
                else { M[(size_t)addr * (size_t)stride + lane] = (u64)R[c * rs]; pc++; }
r2 emssr387
            }
r2 emssr388
        }
r2 emssr389
        else if (op == 10) { rep = %(r_ok)d; done = true; }
r2 emssr390
        else if (op == 11) { rep = %(r_ko)d; done = true; }
r2 emssr391
        else if (op == 12) { pc++; }
r2 emssr392
        else { rep = %(r_end)d; done = true; }
r2 emssr393
    }
r2 emssr394
    if (alive) { report[lane] = rep; steps[lane] = n; }
r2 emssr395
}
r2 emssr396
r2 emssr397
extern "C" __global__
r2 emssr398
void gather(const u64 *M, int words, int lanes, const int *watch, int nwatch, u64 *out)
r2 emssr399
{
r2 emssr400
    int lane = blockIdx.x * blockDim.x + threadIdx.x;
r2 emssr401
    if (lane >= lanes) return;
r2 emssr402
    u64 *o = out + (size_t)lane * nwatch * NLIMB;
r2 emssr403
    for (int k = 0; k < nwatch; k++) {
r2 emssr404
        const u64 *w = M + ((size_t)watch[k] * NLIMB) * (size_t)lanes + lane;
r2 emssr405
        for (int i = 0; i < NLIMB; i++) o[k * NLIMB + i] = w[(size_t)i * lanes];
r2 emssr406
    }
r2 emssr407
}
r2 emssr408
r2 emssr409
// 先頭 lanes 本だけを集める。stride は帯の中の lane の数
r2 emssr410
extern "C" __global__
r2 emssr411
void gather_s(const u64 *M, int stride, int lanes, const int *watch, int nwatch, u64 *out)
r2 emssr412
{
r2 emssr413
    int lane = blockIdx.x * blockDim.x + threadIdx.x;
r2 emssr414
    if (lane >= lanes) return;
r2 emssr415
    u64 *o = out + (size_t)lane * nwatch;
r2 emssr416
    for (int k = 0; k < nwatch; k++) o[k] = M[(size_t)watch[k] * stride + lane];
r2 emssr417
}
r2 emssr418
"""
r2 emssr419
r2 emssr420
R_END = 45
r2 emssr421
_MOD = {}
r2 emssr422
r2 emssr423
r2 emssr424
# ===== block 内の即値を畳む =====
r2 emssr425
#
r2 emssr426
# commonc が出す lane64 の命令列は、番地を one の倍化と加算で組み立てる。
r2 emssr427
# CPU 側の vcpu.translate は block ごとに値を記号で追い、即値どうしの算術を
r2 emssr428
# 一つの即値へ畳む(casm.lane64.vn)。GPU の解釈器は命令を一つずつ歩くので、
r2 emssr429
# 同じ畳み込みを先に済ませておかないと、番地を綴る鎖がそのまま実行費用になる。
r2 emssr430
#
r2 emssr431
#   R1 = 1 ; R1 = R1+R1 ; ... ; R2 = R1+R3   →   R1 = 65554 の一命令
r2 emssr432
#
r2 emssr433
# block の入口で値が分かっている register は無い、として始める。分岐は畳まない。
r2 emssr434
r2 emssr435
MAT, ADD, MUL, NEG, EQ, LT, JMP, LD, ST = 1, 2, 3, 4, 5, 6, 7, 8, 9
r2 emssr436
_BR = (EQ, LT, JMP)
r2 emssr437
_TERM = (0, 10, 11)
r2 emssr438
r2 emssr439
r2 emssr440
def leaders(prog):
r2 emssr441
    """block の先頭になる命令の番号"""
r2 emssr442
    out = set([0])
r2 emssr443
    for i, ins in enumerate(prog):
r2 emssr444
        op, im = ins[0], ins[6]
r2 emssr445
        if op in _BR:
r2 emssr446
            out.add(i + int(im))
r2 emssr447
            out.add(i + 1)
r2 emssr448
        elif op in _TERM:
r2 emssr449
            out.add(i + 1)
r2 emssr450
    return sorted(a for a in out if 0 <= a < len(prog))
r2 emssr451
r2 emssr452
r2 emssr453
def _reads(ins):
r2 emssr454
    op = ins[0]
r2 emssr455
    if op in (ADD, EQ, LT, MUL):
r2 emssr456
        return (ins[2], ins[3])
r2 emssr457
    if op in (NEG, LD):
r2 emssr458
        return (ins[2],)
r2 emssr459
    if op == ST:
r2 emssr460
        return (ins[2], ins[3])
r2 emssr461
    if op == 12:
r2 emssr462
        return (ins[2],)
r2 emssr463
    return ()
r2 emssr464
r2 emssr465
r2 emssr466
def _writes(ins):
r2 emssr467
    op = ins[0]
r2 emssr468
    return ins[1] if op in (MAT, ADD, MUL, NEG, LD) else None
r2 emssr469
r2 emssr470
r2 emssr471
def _walk(prog, lo, hi, live, dead, rewrite):
r2 emssr472
    """block をひと通り追う。live には「自分の Store より前に読んだ定数番地」を集め、
r2 emssr473
    rewrite が真なら即値の算術と語への往復を畳み、dead の番地への Store を落とす。"""
r2 emssr474
    val, mem = {}, {}
r2 emssr475
    for i in range(lo, hi):
r2 emssr476
        ins = prog[i]
r2 emssr477
        if ins is None:
r2 emssr478
            continue
r2 emssr479
        op = ins[0]
r2 emssr480
        if op == MAT:
r2 emssr481
            val[ins[1]] = int(ins[6])
r2 emssr482
        elif op in (ADD, MUL, NEG):
r2 emssr483
            b, c, sb, sc = ins[2], ins[3], ins[4], ins[5]
r2 emssr484
            vb = val.get(b)
r2 emssr485
            vc = val.get(c) if op != NEG else 0
r2 emssr486
            if vb is not None and vc is not None:
r2 emssr487
                v = (vb << sb) + (vc << sc) if op == ADD else (vb * vc if op == MUL else -vb)
r2 emssr488
                if rewrite:
r2 emssr489
                    prog[i] = [MAT, ins[1], 0, 0, 0, 0, v]
r2 emssr490
                val[ins[1]] = v
r2 emssr491
            else:
r2 emssr492
                val.pop(ins[1], None)
r2 emssr493
        elif op == LD:
r2 emssr494
            a = val.get(ins[2])
r2 emssr495
            if a is not None and a in mem:
r2 emssr496
                if rewrite:
r2 emssr497
                    prog[i] = [MAT, ins[1], 0, 0, 0, 0, mem[a]]
r2 emssr498
                val[ins[1]] = mem[a]
r2 emssr499
            else:
r2 emssr500
                if a is not None:
r2 emssr501
                    live.add(a)
r2 emssr502
                val.pop(ins[1], None)
r2 emssr503
        elif op == ST:
r2 emssr504
            a, v = val.get(ins[2]), val.get(ins[3])
r2 emssr505
            if a is None:
r2 emssr506
                mem.clear()
r2 emssr507
            elif v is None:
r2 emssr508
                mem.pop(a, None)
r2 emssr509
            else:
r2 emssr510
                mem[a] = v
r2 emssr511
            # 同じ block の Load は先に繋いでから落とす。落とすのは block を跨ぐ分だけ
r2 emssr512
            if rewrite and a is not None and a in dead:
r2 emssr513
                prog[i] = None
r2 emssr514
        else:
r2 emssr515
            w = _writes(ins)
r2 emssr516
            if w is not None:
r2 emssr517
                val.pop(w, None)
r2 emssr518
r2 emssr519
r2 emssr520
def fold(prog, wires=None):
r2 emssr521
    """block の中で値を記号で追い、即値どうしの算術と語への往復を畳む。意味は変わらない。
r2 emssr522
r2 emssr523
    lane64 の一箱は MAT・ST・LD の往復に開くので、register だけを追っても畳めない。
r2 emssr524
    定数番地への Store と、その番地からの Load を繋いで初めて鎖が一つの即値になる。
r2 emssr525
    """
r2 emssr526
    prog = [list(x) for x in prog]
r2 emssr527
    heads = leaders(prog) + [len(prog)]
r2 emssr528
r2 emssr529
    # 一巡目。block を跨いで読まれる定数番地を集める
r2 emssr530
    live = set()
r2 emssr531
    for k in range(len(heads) - 1):
r2 emssr532
        _walk(prog, heads[k], heads[k + 1], live, (), False)
r2 emssr533
r2 emssr534
    # commonc が出すワイヤの番地は定数だけで、本文の番地は WORK 以上にある。
r2 emssr535
    # だから [64, need) のうち誰も跨いで読まない番地への Store は落としてよい
r2 emssr536
    dead = set()
r2 emssr537
    if wires:
r2 emssr538
        lo, hi = wires
r2 emssr539
        dead = set(a for a in range(lo, hi) if a not in live)
r2 emssr540
r2 emssr541
    # 二巡目。畳んで、死んだ Store を落とす
r2 emssr542
    for k in range(len(heads) - 1):
r2 emssr543
        lo, hi = heads[k], heads[k + 1]
r2 emssr544
        _walk(prog, lo, hi, set(), dead, True)
r2 emssr545
    # block の中だけの死んだ書き込みを落とす
r2 emssr546
    for k in range(len(heads) - 1):
r2 emssr547
        lo, hi = heads[k], heads[k + 1]
r2 emssr548
        last, drop = {}, set()
r2 emssr549
        for i in range(lo, hi):
r2 emssr550
            ins = prog[i]
r2 emssr551
            if ins is None:
r2 emssr552
                continue
r2 emssr553
            for r in _reads(ins):
r2 emssr554
                last.pop(r, None)
r2 emssr555
            w = _writes(ins)
r2 emssr556
            if w is not None:
r2 emssr557
                if ins[0] == MAT and w in last:
r2 emssr558
                    drop.add(last[w])
r2 emssr559
                last[w] = i if ins[0] == MAT else None
r2 emssr560
                if last[w] is None:
r2 emssr561
                    last.pop(w)
r2 emssr562
        for i in drop:
r2 emssr563
            prog[i] = None
r2 emssr564
    # 落とした分だけ番号が詰まるので、分岐の変位を貼り直す
r2 emssr565
    keep = [i for i, x in enumerate(prog) if x is not None]
r2 emssr566
    newpos, n = {}, 0
r2 emssr567
    for i in range(len(prog)):
r2 emssr568
        newpos[i] = n
r2 emssr569
        if prog[i] is not None:
r2 emssr570
            n += 1
r2 emssr571
    newpos[len(prog)] = n
r2 emssr572
    out = []
r2 emssr573
    for i in keep:
r2 emssr574
        ins = list(prog[i])
r2 emssr575
        if ins[0] in _BR:
r2 emssr576
            ins[6] = newpos[min(i + int(ins[6]), len(prog))] - newpos[i]
r2 emssr577
        out.append(tuple(ins))
r2 emssr578
    return out
r2 emssr579
r2 emssr580
r2 emssr581
def compact(prog, wires, work=0):
r2 emssr582
    """ワイヤの番地を詰める。返りは (prog, 新しい need)。
r2 emssr583
r2 emssr584
    commonc の一箱はワイヤ一本を語 64 + w に取るので、image が大きいと lane ごとの
r2 emssr585
    ワイヤの帯が数万語になる。畳んだ後に生き残る番地はその一部なので、番地として
r2 emssr586
    使われている即値だけを 64 から順に付け直す。
r2 emssr587
r2 emssr588
    即値が番地か値かは block の中の使われ方で決める。MAT で置いた register が、
r2 emssr589
    上書きされるまでに LD / ST の番地としてしか使われなければ番地。値として
r2 emssr590
    使われた即値は触らない。触らない即値がワイヤの帯に残れば、need は
r2 emssr591
    その最大までしか下がらない(返りの need がそれを示す)。
r2 emssr592
    """
r2 emssr593
    lo, hi = wires
r2 emssr594
    # 二値の八箱の退避語は定数番地の最大の上に置かれ、ワイヤの帯と本文の間の穴に落ちる
r2 emssr595
    # ことがある(16 §5.3)。穴は lane に無いので、そこも一緒に詰める
r2 emssr596
    hi = max(hi, work) if work else hi
r2 emssr597
    prog = [list(x) for x in prog]
r2 emssr598
    heads = leaders(prog) + [len(prog)]
r2 emssr599
    addr_mats, keep_hi = [], lo
r2 emssr600
    for k in range(len(heads) - 1):
r2 emssr601
        a, b = heads[k], heads[k + 1]
r2 emssr602
        for i in range(a, b):
r2 emssr603
            ins = prog[i]
r2 emssr604
            if ins[0] != MAT or not (lo <= ins[6] < hi):
r2 emssr605
                continue
r2 emssr606
            reg, ok, closed = ins[1], True, False
r2 emssr607
            for j in range(i + 1, b):
r2 emssr608
                jn = prog[j]
r2 emssr609
                op = jn[0]
r2 emssr610
                if op in (LD, ST) and jn[2] == reg and not (op == ST and jn[3] == reg):
r2 emssr611
                    pass
r2 emssr612
                elif reg in _reads(jn):
r2 emssr613
                    ok = False
r2 emssr614
                    break
r2 emssr615
                if _writes(jn) == reg:
r2 emssr616
                    closed = True
r2 emssr617
                    break
r2 emssr618
            # block の外へ出る register は次の block でも番地として使われる(commonc は
r2 emssr619
            # ワイヤの番地を定数でしか作らない)。値として読まれた即値だけを残す
r2 emssr620
            if ok:
r2 emssr621
                addr_mats.append(i)
r2 emssr622
            else:
r2 emssr623
                keep_hi = max(keep_hi, ins[6] + 1)
r2 emssr624
    used = sorted(set(prog[i][6] for i in addr_mats))
r2 emssr625
    base = keep_hi
r2 emssr626
    remap = {a: base + k for k, a in enumerate(used)}
r2 emssr627
    for i in addr_mats:
r2 emssr628
        prog[i][6] = remap[prog[i][6]]
r2 emssr629
    return [tuple(x) for x in prog], base + len(used)
r2 emssr630
r2 emssr631
r2 emssr632
def _module(cp, nlimb):
r2 emssr633
    if nlimb not in _MOD:
r2 emssr634
        src = SRC % {"nlimb": nlimb, "fields": INSN_FIELDS, "r_ok": R_OK, "r_ko": R_KO,
r2 emssr635
                     "r_wide": R_WIDE, "r_budget": R_BUDGET, "r_end": R_END}
r2 emssr636
        _MOD[nlimb] = cp.RawModule(code=src, options=("-std=c++11",))
r2 emssr637
    return _MOD[nlimb]
r2 emssr638
r2 emssr639
r2 emssr640
def to_limbs(v, nlimb):
r2 emssr641
    """整数を limb の並びへ(二の補数)"""
r2 emssr642
    mask = (1 << (64 * nlimb)) - 1
r2 emssr643
    x = v & mask
r2 emssr644
    return [(x >> (64 * i)) & 0xFFFFFFFFFFFFFFFF for i in range(nlimb)]
r2 emssr645
r2 emssr646
r2 emssr647
def from_limbs(limbs, nlimb):
r2 emssr648
    x = 0
r2 emssr649
    for i, l in enumerate(limbs):
r2 emssr650
        x |= int(l) << (64 * i)
r2 emssr651
    if x >> (64 * nlimb - 1):
r2 emssr652
        x -= 1 << (64 * nlimb)
r2 emssr653
    return x
r2 emssr654
r2 emssr655
r2 emssr656
_NOROM_BUF = {}
r2 emssr657
r2 emssr658
r2 emssr659
def _NOROM(cp):
r2 emssr660
    """共有帯を使わないときに渡す一語の置き場。kernel は romlo = 2^62 で触らない"""
r2 emssr661
    if "b" not in _NOROM_BUF:
r2 emssr662
        _NOROM_BUF["b"] = cp.zeros((1,), dtype=cp.uint64)
r2 emssr663
    return _NOROM_BUF["b"]
r2 emssr664
r2 emssr665
r2 emssr666
def pack(addr, need, work):
r2 emssr667
    """語番号 → 詰めた並びの位置。kernel の slot() と同じ式(式は device 側 §slot)。"""
r2 emssr668
    if work == 0:
r2 emssr669
        return addr
r2 emssr670
    if 0 <= addr < need:
r2 emssr671
        return addr
r2 emssr672
    return addr - work + need
r2 emssr673
r2 emssr674
r2 emssr675
r2 emssr676
def run(prog, seeds, words, watch, nlimb=16, budget=1 << 26, chunk=64, need=0, work=0,
r2 emssr677
        base=None):
r2 emssr678
    """image(decode 済み)を seeds の lane 分だけ走らせる。
r2 emssr679
r2 emssr680
    seeds は lane ごとの {語: 値}、watch は host へ戻す語の並び。
r2 emssr681
    返りは lane ごとの (報告, {語: 値})。報告は 0 = tu、42 = te、43 = 幅を越えた、44 = 歩数の上限。
r2 emssr682
r2 emssr683
    base は全 lane へ同じ値を置く語({語: 値})。模型の表のように lane ごとに同じ物を
r2 emssr684
    渡すとき、seeds へ毎回入れると host の側だけで lane × 語 の手間になる。ここでは
r2 emssr685
    語ごとに一度だけ device の帯へ書くので、手間は語の数で済む。
r2 emssr686
r2 emssr687
    need と work を渡すと、ワイヤ(0 .. need)と本文の作業領域(work 以上)の間の穴を詰める。
r2 emssr688
    commonc の lane64 版は本文を WORK = 2^16 から置くので、詰めないと lane ごとに
r2 emssr689
    65536 語ぶんの空きを確保することになる(20 §1.2 の lane の作業領域は交わらない)。
r2 emssr690
r2 emssr691
        words = need + (本文が使う語の数)
r2 emssr692
r2 emssr693
    穴を詰めると lane あたりの領域が減り、同じ memory でより多くの lane を並べられる。
r2 emssr694
    """
r2 emssr695
    import cupy as cp
r2 emssr696
    mod = _module(cp, nlimb)
r2 emssr697
    k_run = mod.get_function("lane64_run1" if nlimb == 1 else "lane64_run")
r2 emssr698
    k_gather = mod.get_function("gather")
r2 emssr699
    prog = fold(prog)
r2 emssr700
    table = np.array([list(p) for p in prog], dtype=np.int64).reshape(-1)
r2 emssr701
    d_prog = cp.asarray(table)
r2 emssr702
    d_watch = cp.asarray(np.array([pack(w, need, work) for w in watch], dtype=np.int32))
r2 emssr703
    out = []
r2 emssr704
    for start in range(0, len(seeds), chunk):
r2 emssr705
        part = seeds[start:start + chunk]
r2 emssr706
        lanes = len(part)
r2 emssr707
        M = cp.zeros((lanes * words * nlimb,), dtype=cp.uint64)
r2 emssr708
        if base:
r2 emssr709
            for addr, v in base.items():
r2 emssr710
                a = pack(addr, need, work)
r2 emssr711
                v = int(v)
r2 emssr712
                if 0 <= v < (1 << 63):
r2 emssr713
                    M[(a * nlimb) * lanes:(a * nlimb) * lanes + lanes] = np.uint64(v)
r2 emssr714
                else:
r2 emssr715
                    for i, limb in enumerate(to_limbs(v, nlimb)):
r2 emssr716
                        if limb:
r2 emssr717
                            o = (a * nlimb + i) * lanes
r2 emssr718
                            M[o:o + lanes] = np.uint64(limb)
r2 emssr719
        idx, vals = [], []
r2 emssr720
        for l, seed in enumerate(part):
r2 emssr721
            for addr, v in seed.items():
r2 emssr722
                a = pack(addr, need, work)
r2 emssr723
                v = int(v)
r2 emssr724
                if 0 <= v < (1 << 63):
r2 emssr725
                    idx.append((a * nlimb) * lanes + l)
r2 emssr726
                    vals.append(v)
r2 emssr727
                else:
r2 emssr728
                    for i, limb in enumerate(to_limbs(v, nlimb)):
r2 emssr729
                        if limb:
r2 emssr730
                            idx.append((a * nlimb + i) * lanes + l)
r2 emssr731
                            vals.append(limb)
r2 emssr732
        if idx:
r2 emssr733
            M[cp.asarray(np.array(idx, dtype=np.int64))] = cp.asarray(np.array(vals, dtype=np.uint64))
r2 emssr734
        report = cp.zeros((lanes,), dtype=cp.int32)
r2 emssr735
        steps = cp.zeros((lanes,), dtype=cp.int64)
r2 emssr736
        threads = 128
r2 emssr737
        grid = (lanes + threads - 1) // threads
r2 emssr738
        sh = threads * NREG_BYTES if nlimb == 1 else 0
r2 emssr739
        k_run((grid,), (threads,), (d_prog, np.int32(len(prog)), M, np.int32(words), np.int32(lanes),
r2 emssr740
                                    np.int64(budget), np.int32(need), np.int32(work), report, steps,
r2 emssr741
                                    _NOROM(cp), np.int64(1 << 62), np.int64(0)),
r2 emssr742
              shared_mem=sh)
r2 emssr743
        got = cp.zeros((lanes * len(watch) * nlimb,), dtype=cp.uint64)
r2 emssr744
        if watch:
r2 emssr745
            k_gather((grid,), (threads,), (M, np.int32(words), np.int32(lanes), d_watch, np.int32(len(watch)), got))
r2 emssr746
        rep = report.get()
r2 emssr747
        raw = got.get()
r2 emssr748
        for l in range(lanes):
r2 emssr749
            vals_l = {}
r2 emssr750
            for k, w in enumerate(watch):
r2 emssr751
                o = (l * len(watch) + k) * nlimb
r2 emssr752
                vals_l[w] = from_limbs(raw[o:o + nlimb], nlimb)
r2 emssr753
            out.append((int(rep[l]), vals_l))
r2 emssr754
        del M, got
r2 emssr755
    return out
r2 emssr756
r2 emssr757
r2 emssr758
def run_shared(prog, base, lanes, words, watch, need, work, nlimb=4, budget=1 << 26,
r2 emssr759
               chunk=2048, romlo=None, romn=0):
r2 emssr760
    """lane が共有する種 base に、lane ごとの少しの語 lanes[k] を重ねて走らせる。
r2 emssr761
r2 emssr762
    語の並びは詰める: ワイヤ [0, need) をそのまま、作業領域 [work, ...) を need の後ろに置く。
r2 emssr763
    words は詰めた後の語数(need + 作業領域の幅)。平文の側の語は全部 work 以上にあるので、
r2 emssr764
    間の番地は使われない。返りは lane ごとの (報告, {語: 値})。
r2 emssr765
r2 emssr766
    romlo を渡すと、番地 [romlo, romlo + romn) は**全 lane で一枚だけ**持つ。
r2 emssr767
    読むだけの表(値段の表など)をここへ置くと、lane あたりの重さが桁で落ちる。
r2 emssr768
r2 emssr769
        lane あたりの語数 = need + (romlo - work)
r2 emssr770
        共有帯の語数     = romn
r2 emssr771
r2 emssr772
    この帯へ Store すると報告 45 で止まる。書く語を入れてはいけない。
r2 emssr773
    """
r2 emssr774
    import cupy as cp
r2 emssr775
    mod = _module(cp, nlimb)
r2 emssr776
    k_run = mod.get_function("lane64_run1" if nlimb == 1 else "lane64_run")
r2 emssr777
    k_gather = mod.get_function("gather")
r2 emssr778
    prog = fold(prog, wires=(64, need))
r2 emssr779
    table = np.array([list(p) for p in prog], dtype=np.int64).reshape(-1)
r2 emssr780
    d_prog = cp.asarray(table)
r2 emssr781
    hi = romlo if romlo is not None else (1 << 62)
r2 emssr782
r2 emssr783
    def pos(a):
r2 emssr784
        return a if a < need else a - work + need
r2 emssr785
r2 emssr786
    d_watch = cp.asarray(np.array([pos(w) for w in watch], dtype=np.int32))
r2 emssr787
    # 共有の種を一枚の並びにする。共有帯の分は別の並びへ分ける
r2 emssr788
    row = np.zeros((words * nlimb,), dtype=np.uint64)
r2 emssr789
    rom = np.zeros((max(1, romn) * nlimb,), dtype=np.uint64)
r2 emssr790
    for addr, v in base.items():
r2 emssr791
        v = int(v)
r2 emssr792
        p0 = (addr - hi) * nlimb if addr >= hi else pos(addr) * nlimb
r2 emssr793
        dst = rom if addr >= hi else row
r2 emssr794
        if 0 <= v < (1 << 63):
r2 emssr795
            dst[p0] = v
r2 emssr796
        else:
r2 emssr797
            for i, limb in enumerate(to_limbs(v, nlimb)):
r2 emssr798
                dst[p0 + i] = limb
r2 emssr799
    d_row = cp.asarray(row)
r2 emssr800
    d_rom = cp.asarray(rom)
r2 emssr801
    out = []
r2 emssr802
    for start in range(0, len(lanes), chunk):
r2 emssr803
        part = lanes[start:start + chunk]
r2 emssr804
        n = len(part)
r2 emssr805
        M = cp.repeat(d_row, n)
r2 emssr806
        idx, vals = [], []
r2 emssr807
        for l, seed in enumerate(part):
r2 emssr808
            for addr, v in seed.items():
r2 emssr809
                idx.append((pos(addr) * nlimb) * n + l)
r2 emssr810
                vals.append(int(v))
r2 emssr811
        if idx:
r2 emssr812
            M[cp.asarray(np.array(idx, dtype=np.int64))] = cp.asarray(np.array(vals, dtype=np.uint64))
r2 emssr813
        report = cp.zeros((n,), dtype=cp.int32)
r2 emssr814
        steps = cp.zeros((n,), dtype=cp.int64)
r2 emssr815
        threads = 128
r2 emssr816
        grid = (n + threads - 1) // threads
r2 emssr817
        sh = threads * NREG_BYTES if nlimb == 1 else 0
r2 emssr818
        k_run((grid,), (threads,), (d_prog, np.int32(len(prog)), M, np.int32(words), np.int32(n),
r2 emssr819
                                    np.int64(budget), np.int32(need), np.int32(work), report, steps,
r2 emssr820
                                    d_rom, np.int64(hi), np.int64(romn)),
r2 emssr821
              shared_mem=sh)
r2 emssr822
        got = cp.zeros((n * len(watch) * nlimb,), dtype=cp.uint64)
r2 emssr823
        k_gather((grid,), (threads,), (M, np.int32(words), np.int32(n), d_watch, np.int32(len(watch)), got))
r2 emssr824
        rep = report.get()
r2 emssr825
        raw = got.get().reshape(n, len(watch), nlimb)
r2 emssr826
        for l in range(n):
r2 emssr827
            out.append((int(rep[l]), dict((w, from_limbs(raw[l, k], nlimb)) for k, w in enumerate(watch))))
r2 emssr828
        del M, got
r2 emssr829
    return out
r2 emssr830
r2 emssr831
r2 emssr832
r2 emssr833
class Lanes(object):
r2 emssr834
    """lane の作業領域と共有帯を device に置いたまま、image を何度も流す。
r2 emssr835
r2 emssr836
    host は語を一つずつ触らない。種は (lane, 語, 値) の並びを一度に散らし、読みは
r2 emssr837
    (lane × 語) の並びを一度に集める。どちらも cupy の一手で済むので、lane の数にも
r2 emssr838
    語の数にも Python の反復が付かない。
r2 emssr839
r2 emssr840
        L = Lanes(prog, need, work, romlo, romn, nlanes, base={語: 値})
r2 emssr841
        L.put(lanes, addrs, vals)          種を散らす(三つとも同じ長さの並び)
r2 emssr842
        L.rom_put(offset, values)          共有帯の一部を書く
r2 emssr843
        rep, W = L.run(watch)              rep[lane] が報告、W[lane, k] が watch[k] の値(device)
r2 emssr844
        L.M2                               語 × lane の二次元の眺め(device)
r2 emssr845
        L.reset()                          作業領域を base の並びへ戻す
r2 emssr846
r2 emssr847
    語の並びは run_shared と同じ: ワイヤ [0, need) をそのまま、作業領域 [work, romlo) を
r2 emssr848
    need の後ろに詰める。M は語主で M2[pos(語), lane]。
r2 emssr849
r2 emssr850
    共有帯 rom は cupy の並び一枚。別の Lanes の M をそのまま rom に渡せる(rom=)ので、
r2 emssr851
    lane の畳みのように「全 lane の語を読む」kernel は、その M を共有帯として読めば書ける。
r2 emssr852
    """
r2 emssr853
r2 emssr854
    def __init__(self, prog, need, work, romlo, romn, nlanes, base=None, nlimb=1, rom=None, sync=True):
r2 emssr855
        """sync が真なら warp を pc の小さい順にそろえて歩く(lane64_run1_sync)。"""
r2 emssr856
        import cupy as cp
r2 emssr857
        self.cp = cp
r2 emssr858
        if nlimb != 1:
r2 emssr859
            raise ValueError("Lanes は 64 bit の語だけを扱う(nlimb=1)")
r2 emssr860
        self.need, self.work, self.romlo = need, work, romlo
r2 emssr861
        self.words = need + (romlo - work)
r2 emssr862
        self.n = nlanes
r2 emssr863
        mod = _module(cp, 1)
r2 emssr864
        self.sync = sync
r2 emssr865
        self.k_run = mod.get_function("lane64_run1_sync" if sync else "lane64_run1")
r2 emssr866
        self.k_gather = mod.get_function("gather_s")
r2 emssr867
        prog = fold(prog, wires=(64, need))
r2 emssr868
        prog, need = compact(prog, (64, need), work=work)
r2 emssr869
        self.need = need
r2 emssr870
        self.words = need + (romlo - work)
r2 emssr871
        self.nprog = len(prog)
r2 emssr872
        self.d_prog = cp.asarray(np.array([list(p) for p in prog], dtype=np.int64).reshape(-1))
r2 emssr873
        if rom is not None:
r2 emssr874
            self.d_rom = rom
r2 emssr875
            self.romn = int(rom.size)
r2 emssr876
        else:
r2 emssr877
            self.romn = romn
r2 emssr878
            self.d_rom = cp.zeros((max(1, romn),), dtype=cp.uint64)
r2 emssr879
        row = np.zeros((self.words,), dtype=np.uint64)
r2 emssr880
        for addr, v in (base or {}).items():
r2 emssr881
            row[self.pos(addr)] = int(v)
r2 emssr882
        self.d_row = cp.asarray(row)
r2 emssr883
        self.M = cp.zeros((self.words * nlanes,), dtype=cp.uint64)
r2 emssr884
        self.reset()
r2 emssr885
r2 emssr886
    def pos(self, a):
r2 emssr887
        return a if a < self.need else a - self.work + self.need
r2 emssr888
r2 emssr889
    @property
r2 emssr890
    def M2(self):
r2 emssr891
        return self.M.reshape(self.words, self.n)
r2 emssr892
r2 emssr893
    def reset(self):
r2 emssr894
        self.M2[:, :] = self.d_row.reshape(-1, 1)
r2 emssr895
r2 emssr896
    def put(self, lanes, addrs, vals):
r2 emssr897
        """(lane, 語, 値) の並びを一度に散らす。addrs は語番号(pos は中で当てる)。"""
r2 emssr898
        cp = self.cp
r2 emssr899
        lanes = cp.asarray(np.asarray(lanes, dtype=np.int64))
r2 emssr900
        addrs = np.asarray(addrs, dtype=np.int64)
r2 emssr901
        addrs = np.where(addrs < self.need, addrs, addrs - self.work + self.need)
r2 emssr902
        idx = cp.asarray(addrs) * self.n + lanes
r2 emssr903
        self.M[idx] = cp.asarray(np.asarray(vals, dtype=np.uint64))
r2 emssr904
r2 emssr905
    def put_block(self, addr, block):
r2 emssr906
        """語 addr から続く語の帯へ、(語数 × lane) の並びを一度に置く。block は device でも host でもよい。"""
r2 emssr907
        cp = self.cp
r2 emssr908
        b = cp.asarray(block, dtype=cp.uint64)
r2 emssr909
        self.M2[self.pos(addr):self.pos(addr) + b.shape[0], :] = b
r2 emssr910
r2 emssr911
    def get_block(self, addr, n):
r2 emssr912
        """語 addr から n 語の帯を (n × lane) で返す(device)。"""
r2 emssr913
        return self.M2[self.pos(addr):self.pos(addr) + n, :]
r2 emssr914
r2 emssr915
    def rom_put(self, offset, values):
r2 emssr916
        self.d_rom[offset:offset + len(values)] = self.cp.asarray(np.asarray(values, dtype=np.uint64))
r2 emssr917
r2 emssr918
    def run(self, watch=(), budget=1 << 36, active=None):
r2 emssr919
        """先頭 active 本の lane を走らせる(省くと全部)。返りは (報告, watch の値) で、どちらも active 本ぶん。"""
r2 emssr920
        cp = self.cp
r2 emssr921
        n = self.n if active is None else min(active, self.n)
r2 emssr922
        report = cp.zeros((n,), dtype=cp.int32)
r2 emssr923
        steps = cp.zeros((n,), dtype=cp.int64)
r2 emssr924
        threads = 128
r2 emssr925
        grid = (n + threads - 1) // threads
r2 emssr926
        if self.sync:
r2 emssr927
            args = (self.d_prog, np.int32(self.nprog), self.M, np.int32(self.words), np.int32(n),
r2 emssr928
                    np.int64(budget), np.int32(self.need), np.int32(self.work), report, steps,
r2 emssr929
                    self.d_rom, np.int64(self.romlo), np.int64(self.romn), np.int32(self.n))
r2 emssr930
        else:
r2 emssr931
            if n != self.n:
r2 emssr932
                raise ValueError("先頭だけを走らせるには sync の道が要る")
r2 emssr933
            args = (self.d_prog, np.int32(self.nprog), self.M, np.int32(self.words), np.int32(n),
r2 emssr934
                    np.int64(budget), np.int32(self.need), np.int32(self.work), report, steps,
r2 emssr935
                    self.d_rom, np.int64(self.romlo), np.int64(self.romn))
r2 emssr936
        self.k_run((grid,), (threads,), args, shared_mem=threads * NREG_BYTES)
r2 emssr937
        W = None
r2 emssr938
        if watch:
r2 emssr939
            d_watch = cp.asarray(np.array([self.pos(w) for w in watch], dtype=np.int32))
r2 emssr940
            got = cp.zeros((n * len(watch),), dtype=cp.uint64)
r2 emssr941
            self.k_gather((grid,), (threads,), (self.M, np.int32(self.n), np.int32(n), d_watch,
r2 emssr942
                                                np.int32(len(watch)), got))
r2 emssr943
            W = got.reshape(n, len(watch)).astype(cp.int64)
r2 emssr944
        return report.get(), W