aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoreunhhu <[email protected]>2026-10-05 08:41:45 +0100
committereunhhu <[email protected]>2026-10-05 08:41:45 +0100
commit1ef9dd24773058f766ad80ffc7a59295fc44c92d (patch)
tree1fd00811ed87ff2976b1698d0dbed4873b44adc3
parentc1d1fdde86832f8c8528135975b660b00f1a3678 (diff)
downloadpure-1ef9dd24773058f766ad80ffc7a59295fc44c92d.tar.gz
pure-1ef9dd24773058f766ad80ffc7a59295fc44c92d.zip
Compile only blocks with a loop, keep no code for a module without one, set a frame's fields one by one, and inline group_of (021)
- Compiled code paid only where blocks repeat; a block run once (a view's cell, a fold's item, most handlers) ran slower through the code's entry and its calls of step: the bank's table queries took 16-24% more instructions. Now only a block with an EACH or a LOOP is compiled, and a module with none keeps no compiled code. - run() set its whole frame each time (the compiled code's run among it); now its fields one by one. - group_of is inlined into its pass: after the executor's refactor it was a call per row (per_branch +4-5% instructions; now +1.1-1.3%).
-rw-r--r--core/module-jit.c26
-rw-r--r--core/module.c21
2 files changed, 36 insertions, 11 deletions
diff --git a/core/module-jit.c b/core/module-jit.c
index 8e50eb0..49c763f 100644
--- a/core/module-jit.c
+++ b/core/module-jit.c
@@ -730,8 +730,18 @@ static bool compile_block(const PureModule *m, PureBlock block, unsigned end_hei
typedef struct { PureBlock block; unsigned leaves; size_t entry; } Made;
-/* A block to make, leaving leaves values on the stack. */
-static bool want(PureBlock block, unsigned leaves, Made **made, size_t *count, size_t *room) {
+/* Whether a block has a loop: compiled code pays where blocks repeat; a
+ * short block runs once (a view's cell, a fold's item, most handlers) as
+ * fast in the executor, without the code's entry and its calls of step. */
+static bool loops_in(const PureModule *m, PureBlock block) {
+ for (uint32_t pc = block.start; pc < block.start + block.length; ++pc)
+ if (m->code[pc].op == PURE_OP_EACH || m->code[pc].op == PURE_OP_LOOP) return true;
+ return false;
+}
+
+/* A block to make (one with a loop), leaving leaves values on the stack. */
+static bool want(const PureModule *m, PureBlock block, unsigned leaves, Made **made, size_t *count, size_t *room) {
+ if (!loops_in(m, block)) return true;
for (size_t i = 0; i < *count; ++i)
if ((*made)[i].block.start == block.start && (*made)[i].block.length == block.length) return true;
if (!grow_array((void **)made, room, *count + 1, sizeof(**made))) return false;
@@ -746,17 +756,21 @@ struct PureJit *pure_jit_make(const PureModule *m) {
size_t count = 0, room = 0;
bool ok = true;
/* The handlers' and the views' code, then the folds' blocks in what is made (they run as blocks of their own). */
- for (uint32_t h = 0; ok && h < m->handlers; ++h) ok = want(m->handler[h].code, 0, &made, &count, &room);
+ for (uint32_t h = 0; ok && h < m->handlers; ++h) ok = want(m, m->handler[h].code, 0, &made, &count, &room);
for (uint32_t v = 0; ok && v < m->views; ++v)
- if (m->view_code[v].length) ok = want(m->view_code[v], 1, &made, &count, &room);
+ if (m->view_code[v].length) ok = want(m, m->view_code[v], 1, &made, &count, &room);
for (size_t i = 0; ok && i < count; ++i)
for (uint32_t pc = made[i].block.start; ok && pc < made[i].block.start + made[i].block.length; ++pc) {
const PureOp *op = &m->code[pc];
unsigned how = op->arg & PURE_FOLD_KIND; /* count and first leave a bool; the others a value and a bool */
if (op->op == PURE_OP_FOLD)
- ok = want((PureBlock){pc + 1, op->arg >> 16}, how == PURE_FOLD_COUNT || how == PURE_FOLD_FIRST ? 1 : 2, &made, &count,
- &room);
+ ok = want(m, (PureBlock){pc + 1, op->arg >> 16}, how == PURE_FOLD_COUNT || how == PURE_FOLD_FIRST ? 1 : 2, &made,
+ &count, &room);
}
+ if (ok && !count) { /* nothing worth it: run() then looks for no code at all */
+ free(made);
+ return NULL;
+ }
Emit e = {0};
for (size_t i = 0; ok && i < count; ++i) ok = compile_block(m, made[i].block, made[i].leaves, &e, &made[i].entry);
ok = ok && resolve(&e);
diff --git a/core/module.c b/core/module.c
index c486d45..8771cab 100644
--- a/core/module.c
+++ b/core/module.c
@@ -2294,7 +2294,7 @@ static size_t group_slot(uint64_t bits, size_t capacity) {
}
/* key's group, added when absent (with add); NULL when absent, or without room. */
-static Group *group_of(struct PurePass *pass, double key, unsigned how, bool add) {
+static inline __attribute__((always_inline)) Group *group_of(struct PurePass *pass, double key, unsigned how, bool add) {
key = key == 0 ? 0 : key;
if (pass->capacity) {
for (size_t at = group_slot(order_bits(key), pass->capacity);; at = (at + 1) & (pass->capacity - 1)) {
@@ -3071,13 +3071,24 @@ int pure_run_iterate(Frame *frame) {
return how != STEP_NEXT ? how : next != UINT32_MAX ? STEP_NEXT : STEP_DONE;
}
+/* A frame for block, its fields set one by one: a block runs often (a
+ * fold's item, a view's cell), and filling the whole frame each time (the
+ * compiled code's run among it) costs more than the block. */
+static inline __attribute__((always_inline)) void open_frame(Frame *frame, const PureModule *m, PureValue *values, PureBlock block,
+ Event *ev, Slot *stack, Loop *loops) {
+ frame->m = m, frame->values = values, frame->ev = ev, frame->app = ev->app, frame->arena = ev->arena;
+ frame->stack = stack, frame->sp = 0, frame->loops = loops, frame->depth = 0;
+ frame->end = block.start + block.length;
+}
+
/* A block run by its compiled code. */
static __attribute__((noinline)) PureResult run_compiled(PureCompiled code, const PureModule *m, PureValue *values,
PureBlock block, Event *ev, Slot *result) {
Slot stack[PURE_MODULE_STACK];
Loop loops[PURE_MODULE_NESTING];
- Frame frame = {.m = m, .values = values, .ev = ev, .app = ev->app, .arena = ev->arena, .stack = stack,
- .end = block.start + block.length, .loops = loops};
+ Frame frame;
+ open_frame(&frame, m, values, block, ev, stack, loops);
+ frame.run.list = NULL; /* no run kept yet */
return finish(&frame, code(&frame), result);
}
@@ -3086,8 +3097,8 @@ static PureResult run(const PureModule *m, PureValue *values, PureBlock block, E
if (code) return run_compiled(code, m, values, block, ev, result);
Slot stack[PURE_MODULE_STACK];
Loop loops[PURE_MODULE_NESTING];
- Frame frame = {.m = m, .values = values, .ev = ev, .app = ev->app, .arena = ev->arena, .stack = stack,
- .end = block.start + block.length, .loops = loops};
+ Frame frame;
+ open_frame(&frame, m, values, block, ev, stack, loops);
int how = STEP_NEXT;
for (uint32_t pc = block.start;;) {
if (pc >= frame.end) { /* the block ran; or a loop's ran once: again, or the loop ends (its end may be its outer's) */