diff options
| author | eunhhu <[email protected]> | 2026-10-05 07:32:17 +0100 |
|---|---|---|
| committer | eunhhu <[email protected]> | 2026-10-05 07:38:00 +0100 |
| commit | 75b9c1c1e936e2b7c55275139897e1c78d7939c1 (patch) | |
| tree | 63f4a2c15a9ca651b9ce7f961683692626e551e0 /core/module-run.h | |
| parent | 71f1de8416a01ce6305ec9c5ed29895f4344886b (diff) | |
| download | pure-75b9c1c1e936e2b7c55275139897e1c78d7939c1.tar.gz pure-75b9c1c1e936e2b7c55275139897e1c78d7939c1.zip | |
Run a block through a frame and step, and compile blocks to AArch64 that call the same steps (021)
- run() keeps a block's operand stack and open loops in a Frame; step()
runs one instruction (branches and loops too, one dispatch), and
enter_each, enter_loop, leave_loop and iterate are their own functions.
The executor is the same machine code, 7% fewer instructions.
- The verifier records each instruction's stack height (from its block's
start), so compiled code knows each slot's place.
- module-jit.c (AArch64 Linux; elsewhere, or PURE_JIT=0, none): a
module's handlers', views' and folds' blocks compiled when it loads.
Branches and loops are branches; every other instruction calls the
executor's own step, so both run the same meaning. A loop that is not
opened lands where its iteration would have ended; an empty loop
iterates in place.
- test-module recompiles a module whose code a test changes after load.
- A search (100,000 nodes, 1,000,000 edges, core only) is 150 ms
compiled, against 122 ms executed: a call of step for every instruction
costs more than the executor's switch; the hot instructions are written
as code next.
Diffstat (limited to 'core/module-run.h')
| -rw-r--r-- | core/module-run.h | 94 |
1 files changed, 94 insertions, 0 deletions
diff --git a/core/module-run.h b/core/module-run.h new file mode 100644 index 0000000..49bdf35 --- /dev/null +++ b/core/module-run.h @@ -0,0 +1,94 @@ +#ifndef PURE_MODULE_RUN_H +#define PURE_MODULE_RUN_H +/* How a block runs, shared by the executor (module.c) and the compiled code + * (module-jit.c): their frame, and the steps the compiled code calls. Not a + * public interface. */ +#include "module.h" + +/* An item's or a key's op->plan: its cell a text; a FIND joined to the ITEM_GET after it. */ +enum { PLAN_TEXT = 1, PLAN_JOINED = 2 }; + +typedef struct { union { double num; int64_t i64; }; PureBytes bytes; } Slot; + +/* A walk (EACH): the list, the item it runs for, where it ends (items the + * walk removes move it), and whether that item left. */ +typedef struct { uint32_t list; size_t at, end; bool removed; } Walk; +/* A loop that runs in its block's run (LOOP, EACH): its block and where + * the code around it ends, the arena as it began, a walk's walk and the + * event's walk and row around it, a count's values (low + k of count) and + * slot. */ +typedef struct { + uint32_t start, end, outer; + size_t mark; + bool walking, integer; + Walk walk, *around; + size_t row; + uint64_t count, k; + PureValue low, *slot; +} Loop; +typedef struct { + PureApp *app; + const PureInputShape *shape; /* message, or NULL */ + const char *key; /* a keyed message's key */ + int64_t base; /* the revision a message's edit was made against, or -1 */ + PureArena *arena; + double dt; + size_t row; /* the row a column is evaluated for (in a fold, the fold's item) */ + size_t outer; /* in a fold, the row of the column it runs in */ + Walk *walk; /* the walk the code runs in, or NULL */ + PureValue *locals; /* the event's locals (a handler's, and what runs in it), or NULL */ + bool arguments; /* FIELD reads the arguments given for a view, not the message */ + bool modified, exists; + uint32_t (*spans)[2]; /* a message's variable fields: start and size in the payload */ +} Event; + +/* ---- Running a block (docs/design/021). A frame holds what a block reads + * and changes as it runs: its operand stack and the loops open in it. step + * runs one instruction but a branch or a loop's; run takes those itself, as + * the compiled code does (module-jit.c), so both run the same steps. */ +typedef struct { + const PureModule *m; + PureValue *values; + Event *ev; + PureApp *app; + PureArena *arena; + Slot *stack; + size_t sp; + uint32_t end; /* where the innermost loop's block ends, or the block's */ + unsigned depth; /* the loops open */ + PureFault kind; /* a fault's */ + PureResult status; /* what refused or stopped it */ + uint32_t fault_pc; /* the instruction that faulted or refused */ + Loop *loops; /* PURE_MODULE_NESTING of them, apart: a walk points into its loop */ +} Frame; + +/* How a step went: on to the next instruction; a fault (kind at fault_pc); + * refused (status: past a limit or nothing found is a fault there, else it + * is returned); stopped (status returned as it is). */ +enum { STEP_NEXT, STEP_FAULT, STEP_REFUSED, STEP_RETURN }; + +/* The instruction after op at pc: past a fold's block, or the ITEM_GET a FIND was joined to. */ +static inline uint32_t after(const PureOp *op, uint32_t pc) { + return op->op == PURE_OP_FOLD ? pc + 1 + (op->arg >> 16) : pc + 1 + (op->op == PURE_OP_LIST_FIND && op->plan & PLAN_JOINED); +} + +/* The steps compiled code takes from the executor (module.c): an + * instruction but a branch or a loop's (how; the next is after()); a walk or + * a loop opened at pc (STEP_NEXT: its block next, STEP_SKIP: none, past its + * block); BREAK; a loop's block ran once (STEP_NEXT: again, STEP_DONE: the + * loop ended). */ +enum { STEP_SKIP = 4, STEP_DONE = 5 }; +int pure_run_step(Frame *, uint32_t pc); +int pure_run_each(Frame *, uint32_t pc); +int pure_run_loop(Frame *, uint32_t pc); +void pure_run_break(Frame *); +int pure_run_iterate(Frame *); + +/* A module's code compiled (AArch64 Linux; elsewhere, or with PURE_JIT=0 in + * the environment, none): a block's code runs a frame as run does, giving how + * it went (a STEP_). */ +typedef int (*PureCompiled)(Frame *); +struct PureJit *pure_jit_make(const PureModule *); +void pure_jit_free(struct PureJit *); +PureCompiled pure_jit_code(const struct PureJit *, PureBlock); +#endif |
