1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
|
/* Research 045 (029's principle 1, experiment (b)): the executor's own instruction bodies stitched without dispatch.
* Reads a module as Pure loads it and writes C for Pure's core to include (native.inc): the module's code as a
* constant array; a function for each basic block calling the executor's step() on each of its constant instructions,
* so the C compiler specializes each body to its instruction alone (no fetch, no decode, no dispatch within a block);
* and run_native, run_calls's loop dispatching once a block. Calls, returns and the ends of loops go through
* run_calls's own code. The semantics are the executor's source, unchanged.
*
* cc -O2 -std=c17 -I../../../core stitch.c ../../../build/libpure.a -lm -pthread -o stitch
* ./stitch MODULE > native.inc (then core/module.c built with -DPURE_NATIVE_INC='"native.inc"') */
#include "module.h"
#include <inttypes.h>
#include <stdio.h>
int main(int argc, char **argv) {
if (argc != 2) { fprintf(stderr, "usage: %s MODULE\n", argv[0]); return 2; }
PureModule m = {0};
char error[160];
if (pure_module_read(argv[1], &m, error, sizeof(error)) != PURE_OK) { fprintf(stderr, "%s\n", error); return 1; }
uint32_t n = m.code_count;
printf("/* Stitched from %s: %u instructions (research 045). */\n", argv[1], n);
printf("enum { NATIVE_COUNT = %u, STEP_ENDED = 9 }; /* STEP_ENDED: a block reached its end (frame.end) */\n", n);
printf("static const PureOp native_code[NATIVE_COUNT] = {\n");
for (uint32_t pc = 0; pc < n; ++pc) {
const PureOp *o = &m.code[pc];
printf(" {.integer = INT64_C(%" PRId64 "), .arg = %" PRIu32 "u, .op = %u, .plan = %u, .shape = %u, .range = %u},\n",
o->integer, o->arg, o->op, o->plan, o->shape, o->range);
}
printf("};\n\n");
printf("static bool native_matches(const PureModule *m) {\n"
" if (m->code_count != NATIVE_COUNT) return false;\n"
" for (uint32_t pc = 0; pc < NATIVE_COUNT; ++pc)\n"
" if (m->code[pc].op != native_code[pc].op || m->code[pc].arg != native_code[pc].arg ||\n"
" m->code[pc].integer != native_code[pc].integer || m->code[pc].plan != native_code[pc].plan)\n"
" return false;\n"
" return true;\n"
"}\n\n");
/* Basic blocks: a leader at each block's and function's start, each jump's target, past each instruction that may
* go elsewhere (a branch, a loop's, a call, a return), and every 16 instructions. Each is a function of its own (one
* enormous function took the compiler past 3 GiB): stitched within, dispatched once a block. */
static bool leader[1 << 16];
for (uint32_t h = 0; h < m.handlers; ++h) leader[m.handler[h].code.start] = true;
for (uint32_t f = 0; f < m.functions; ++f) leader[m.function[f].code.start] = true;
for (uint32_t pc = 0; pc < n; ++pc) {
uint8_t op = m.code[pc].op;
bool control = op == PURE_OP_JZ || op == PURE_OP_JMP || op == PURE_OP_LOOP || op == PURE_OP_EACH || op == PURE_OP_BREAK ||
op == PURE_OP_CALL || op == PURE_OP_RETURN || op == PURE_OP_FOLD;
if ((op == PURE_OP_JZ || op == PURE_OP_JMP) && m.code[pc].arg < n) leader[m.code[pc].arg] = true;
if (control && pc + 1 < n) leader[pc + 1] = true;
}
leader[0] = true;
for (uint32_t pc = 0, run = 0; pc < n; ++pc, ++run) {
if (leader[pc]) run = 0;
else if (run == 16) leader[pc] = true, run = 0;
}
for (uint32_t pc = 0; pc < n;) {
uint32_t end = pc + 1;
while (end < n && !leader[end]) ++end;
printf("static __attribute__((noinline)) int native_%u(Frame *frame, uint32_t *pc) {\n int how;\n", pc);
for (uint32_t k = pc; k < end; ++k) {
printf(" if (%uu >= frame->end) { *pc = %uu; return STEP_ENDED; }\n", k, k);
printf(" if ((how = step(frame, &native_code[%u], %uu, pc, true)) != STEP_NEXT || *pc != %uu) return how;\n", k, k, k + 1);
}
printf(" return STEP_NEXT;\n}\n");
pc = end;
}
printf("typedef int (*NativeBlock)(Frame *, uint32_t *);\n");
printf("static const NativeBlock native_blocks[NATIVE_COUNT] = {\n");
for (uint32_t pc = 0; pc < n; ++pc) if (leader[pc]) printf(" [%u] = native_%u,\n", pc, pc);
printf("};\n\n");
printf("static __attribute__((noinline)) PureResult run_native(const PureModule *m, PureValue *values, PureBlock block, Event *ev,\n"
" Slot *result) {\n"
" Slot stack[PURE_MODULE_STACK];\n"
" Loop few[PURE_MODULE_NESTING], *loops = m->nesting > PURE_MODULE_NESTING ? deep_loops(ev->app, m->nesting) : few;\n"
" if (!loops) return PURE_ALLOCATION;\n"
" Frame frame;\n"
" open_frame(&frame, m, values, block, ev, stack, loops);\n"
" Call *top = NULL;\n"
" int how = STEP_NEXT;\n"
" for (uint32_t pc = block.start;;) {\n"
" if (pc >= frame.end) {\n"
" if (!frame.depth || (how = iterate(&frame, &pc)) != STEP_NEXT) break;\n"
" continue;\n"
" }\n"
" NativeBlock b = pc < NATIVE_COUNT ? native_blocks[pc] : NULL;\n"
" how = b ? b(&frame, &pc) : step(&frame, &m->code[pc], pc, &pc, true);\n"
" if (how == STEP_NEXT || how == STEP_ENDED) continue;\n"
" if (how != STEP_CALL && how != STEP_BACK && how != STEP_BATCH) break;\n"
" Frame apart = frame;\n"
" if (how == STEP_CALL) how = enter_call(&apart, &top, &pc);\n"
" else if (how == STEP_BACK) how = leave_call(&apart, &top, &pc);\n"
" else {\n"
" uint32_t past = 0;\n"
" how = open_batches(&apart, pc - 1, &past);\n"
" pc = past;\n"
" }\n"
" frame = apart;\n"
" if (how != STEP_NEXT) break;\n"
" }\n"
" if (top) {\n"
" Frame apart = frame;\n"
" unwind_calls(&apart, top);\n"
" frame = apart;\n"
" }\n"
" return finish(&frame, how, result);\n"
"}\n");
pure_module_release(&m);
return 0;
}
|