aboutsummaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/module-jit.c348
-rw-r--r--core/module-list.c2
-rw-r--r--core/module-run.h4
3 files changed, 347 insertions, 7 deletions
diff --git a/core/module-jit.c b/core/module-jit.c
index 93d7b9b..7ea27c1 100644
--- a/core/module-jit.c
+++ b/core/module-jit.c
@@ -230,6 +230,8 @@ static void store32_index(Emit *e, unsigned t, unsigned base, unsigned index) {
static void load8_index(Emit *e, unsigned t, unsigned base, unsigned index) { put(e, 0x38606800 | index << 16 | base << 5 | t); } /* [base, index] */
static void store8_index(Emit *e, unsigned t, unsigned base, unsigned index) { put(e, 0x38206800 | index << 16 | base << 5 | t); }
static void orr_lsl(Emit *e, unsigned d, unsigned n, unsigned m, unsigned shift) { put(e, 0xAA000000 | m << 16 | shift << 10 | n << 5 | d); }
+static void add_lsl(Emit *e, unsigned d, unsigned n, unsigned m, unsigned shift) { put(e, 0x8B000000 | m << 16 | shift << 10 | n << 5 | d); }
+static void prefetch(Emit *e, unsigned base) { put(e, 0xF9800000 | base << 5); } /* PRFM PLDL1KEEP, [base] */
/* ---- What the compiler knows */
@@ -282,8 +284,17 @@ typedef struct {
int at_kept, end_kept, k_kept, n_kept; /* its state's kept values */
int nest; /* the outermost loop around it (itself when none is) */
uint64_t region; /* what its nest keeps: each in a register of its own, all read at each label in it */
+ int ahead; /* a slice it opens whose key it reads ahead (an Ahead), or -1 */
} LoopAt;
+/* A key as a loop computes it, followed from its block's start (reading
+ * ahead): a constant, the loop's round (a count's value, a walk's place),
+ * an item's field at a position, or arithmetic of two. */
+enum { S_CONST, S_ROUND, S_ITEM, S_ADD, S_SUB, S_MUL };
+typedef struct { uint8_t kind; uint16_t list, field; int a, b; double value; } Sym;
+/* A slice read ahead: the loop opening it (an EACH in its block), its list and the column its block reads, its low key. */
+typedef struct { unsigned inner; uint32_t list, field; int key; Sym nodes[32]; unsigned count; } Ahead;
+
typedef struct { int label; PureFault kind; uint32_t pc; } FaultAt;
/* An instruction's step, called out of the fast way's line: its operands
@@ -302,6 +313,7 @@ typedef struct {
int loop; /* the innermost loop around it */
int tree_list; /* ITEM_GET whose kept column was none: the run a tree's code keeps tried first (else -1) */
unsigned tree_field;
+ bool two; /* the step at pc, then the one at pc + 1 (an ITEM_GET and its HAS, run as one) */
} Slow;
/* A cell of eight bytes or one, read out of the line a four-byte one is read in. */
@@ -327,7 +339,10 @@ typedef struct {
size_t wide_count, wide_room;
Kept kept[KEPT_MOST];
unsigned kept_count;
+ Ahead *aheads;
+ size_t ahead_count, ahead_room;
uint64_t fresh; /* what is kept and holds its value now */
+ bool joined; /* the instruction just compiled took the next one too */
Value vs[PURE_MODULE_STACK + 1];
} Block;
@@ -688,6 +703,12 @@ static void stub_call(Block *k, const Slow *s, uintptr_t function) {
mov_w(e, X1, s->pc);
call(e, function);
cbnz_w(e, X0, k->exit);
+ if (s->two) {
+ mov_reg(e, X0, X19);
+ mov_w(e, X1, s->pc + 1);
+ call(e, (uintptr_t)pure_run_step);
+ cbnz_w(e, X0, k->exit);
+ }
if (s->result >= 0) {
if (s->result_kind == V_D) load_d(e, s->result_reg, X20, AT(s->result));
else { /* a bool or a whole number: its number back to an integer */
@@ -1008,6 +1029,75 @@ static void append(Block *k, const PureOp *op, uint32_t pc, unsigned height) {
slow_back(k, s, V_MEM, 0);
}
+/* ITEM_GET of a number at pc and the HAS at pc + 1 that takes it, as one:
+ * the cell read as a key (a four- or one-byte cell is one already; an
+ * eight-byte one when whole) and found in the key bits, kept for the loop
+ * around. False when there is nothing kept to do it with (then each alone). */
+static bool item_has(Block *k, uint32_t pc, unsigned height) {
+ Emit *e = k->e;
+ const PureModule *m = k->m;
+ const PureOp *get = &m->code[pc], *has = &m->code[pc + 1];
+ int loop = k->loop_of[pc - k->block.start];
+ if (get->plan & PLAN_TEXT || has->op != PURE_OP_LIST_HAS || has->plan & PLAN_TEXT || k->target[pc + 1 - k->block.start] ||
+ k->loop_of[pc + 1 - k->block.start] != loop)
+ return false;
+ for (unsigned l = 0; l < k->loop_count; ++l)
+ if (k->loops[l].end == pc + 1 || k->loops[l].start == pc + 1) return false;
+ uint32_t list = list_of(get->arg), field = item_field(get->arg);
+ int col_c = kept_find(k, loop, C_COLUMN, list, field), count_c = kept_find(k, loop, C_COUNT, list, 0),
+ bits_c = kept_find(k, loop, C_BITS, has->arg, 0), limit_c = kept_find(k, loop, C_LIMIT, has->arg, 0);
+ if (col_c < 0 || bits_c < 0 || limit_c < 0) return false;
+ unflag_below(k, height);
+ spill_all(k, height - 1);
+ size_t s = (size_t)(slow_at(k, pc, (uintptr_t)pure_run_step, height, height - 1, (int)height - 1) - k->slows);
+ if (e->failed) return true;
+ k->slows[s].two = true;
+ int slow = k->slows[s].label, other = new_label(e), key = new_label(e), absent = new_label(e), done = new_label(e),
+ one = new_label(e);
+ unsigned r = alloc_x(k, height), column = kept_reg(k, col_c), bits = kept_reg(k, bits_c), limit = kept_reg(k, limit_c);
+ cbz_x(e, column, slow);
+ cbz_x(e, bits, slow);
+ unsigned at = value_index(k, height - 1, X1);
+ int walk = k->vs[height - 1].walk;
+ if (walk < 0 || k->loops[walk].list != list) {
+ unsigned count;
+ if (count_c >= 0) count = kept_reg(k, count_c);
+ else {
+ load64(e, X16, X24, list * sizeof(PureList *));
+ load64(e, X17, X16, pure_list_layout[LIST_COUNT_AT]);
+ count = X17;
+ }
+ cmp_reg(e, at, count);
+ b_cond(e, HS, fault_at(k, PURE_FAULT_NOT_FOUND, pc));
+ }
+ tbz(e, column, 58, other);
+ load32_index(e, X9, column, at);
+ bind(e, key);
+ cmp_reg(e, X9, limit);
+ b_cond(e, HS, absent);
+ lsr_imm(e, X10, X9, 6);
+ load64_index(e, X10, bits, X10);
+ lsr_reg(e, r, X10, X9);
+ b(e, done);
+ bind(e, other); /* eight bytes: a key when whole; one byte: one */
+ tbz(e, column, 59, one);
+ load_d_index(e, D0, column, at);
+ fp2(e, FCVTZU_X, X9, D0);
+ fp2(e, UCVTF_X, D1, X9);
+ fcmp(e, D0, D1);
+ b_cond(e, EQ, key);
+ b(e, absent);
+ bind(e, one);
+ load8_index(e, X9, column, at);
+ b(e, key);
+ bind(e, absent);
+ mov_w(e, r, 0);
+ bind(e, done);
+ k->vs[height - 1] = (Value){V_BIT, (uint8_t)r, 0, false, false, -1, 0, -1};
+ slow_back(k, s, V_BIT, r);
+ return true;
+}
+
static void loop_open(Block *k, uint32_t pc, unsigned height);
static void loop_ends(Block *k, uint32_t pc);
@@ -1198,6 +1288,10 @@ static void compile_op(Block *k, uint32_t pc) {
}
case PURE_OP_ITEM_GET: { /* a FLAT list's cell by its column, a tree's from the run kept */
if (op->plan & PLAN_TEXT) break;
+ if (item_has(k, pc, height)) {
+ k->joined = true;
+ return;
+ }
uint32_t list = list_of(arg), field = item_field(arg);
int col_c = kept_find(k, loop, C_COLUMN, list, field), count_c = kept_find(k, loop, C_COUNT, list, 0);
unflag_below(k, height);
@@ -1520,10 +1614,8 @@ static void iteration(Block *k, LoopAt *l, unsigned li) {
unsigned place = kept_reg(k, l->at_kept), end = kept_reg(k, l->end_kept);
add_imm(e, place, place, 1);
cmp_reg(e, place, end);
- b_cond(e, HS, done);
- b(e, again);
+ b_cond(e, LO, again);
bind(e, done);
- k->fresh = kept_at(k, (int)(l - k->loops)); /* the done way: as it was */
load64(e, X10, X27, at + offsetof(Loop, around));
store64(e, X10, X22, offsetof(Event, walk));
load64(e, X10, X27, at + offsetof(Loop, row));
@@ -1726,6 +1818,247 @@ static void plan_kept(Block *k) {
free(used);
}
+/* ---- Reading ahead (docs/design/022)
+ * A loop that opens, in its block, a slice of a packed list from a key it
+ * computes of what it reads at its round (an item of a list at a position
+ * that moves with the round, then arithmetic) reads that slice's bucket
+ * AHEAD_FAR rounds ahead and the first cell of the column the slice's block
+ * reads AHEAD_NEAR rounds ahead: their misses overlap the rounds between
+ * (software prefetch of indirect accesses, as compilers do it). A read
+ * ahead changes nothing and never faults: what is out of range is not read. */
+
+enum { AHEAD_FAR = 16, AHEAD_NEAR = 8 };
+
+static unsigned sym_depth(const Ahead *a, int n);
+
+static int sym(Ahead *a, Sym node) {
+ if (a->count == sizeof(a->nodes) / sizeof(*a->nodes)) return -1;
+ a->nodes[a->count] = node;
+ return (int)a->count++;
+}
+
+static bool has_round(const Ahead *a, int n) {
+ if (n < 0) return false;
+ const Sym *s = &a->nodes[n];
+ return s->kind == S_ROUND || (s->kind == S_ITEM && has_round(a, s->a)) ||
+ (s->kind >= S_ADD && (has_round(a, s->a) || has_round(a, s->b)));
+}
+
+/* Loop li's block followed from its start to the first loop it opens: when
+ * that is a slice of a stored list whose low key comes of the round, the
+ * plan to read it ahead. */
+static void plan_ahead(Block *k, unsigned li) {
+ const PureModule *m = k->m;
+ LoopAt *l = &k->loops[li];
+ if (!l->walk && m->code[l->at].arg & PURE_LOOP_INTEGER) return;
+ Ahead a = {0};
+ int stack[PURE_MODULE_STACK + 2], local[PURE_MODULE_LOCALS];
+ for (unsigned i = 0; i < PURE_MODULE_STACK + 2; ++i) stack[i] = -1;
+ for (unsigned i = 0; i < PURE_MODULE_LOCALS; ++i) local[i] = -1;
+ int round = sym(&a, (Sym){S_ROUND, 0, 0, -1, -1, 0});
+ if (!l->walk) local[m->code[l->at].arg & 0xFF] = round;
+ for (uint32_t pc = l->start; pc < l->end;) {
+ const PureOp *op = &m->code[pc];
+ unsigned h = k->height[pc - k->block.start];
+ if (h + 1 > PURE_MODULE_STACK) return;
+ switch (op->op) {
+ case PURE_OP_CONST:
+ if (op->arg) stack[h] = -1;
+ else {
+ double v;
+ memcpy(&v, &op->integer, sizeof(v));
+ stack[h] = sym(&a, (Sym){S_CONST, 0, 0, -1, -1, v});
+ }
+ break;
+ case PURE_OP_LOCAL_LOAD: stack[h] = local[op->arg & 0xFF]; break;
+ case PURE_OP_LOCAL_STORE: local[op->arg & 0xFF] = stack[h - 1]; break;
+ case PURE_OP_ROW: stack[h] = l->walk && l->list == op->arg ? round : -1; break;
+ case PURE_OP_INDEX_FIRST: stack[h] = sym(&a, (Sym){S_CONST, 0, 0, -1, -1, 0}); break;
+ case PURE_OP_INDEX_MOVE: /* a position: clamped as it runs, out of range not read ahead */
+ stack[h - 2] = stack[h - 2] >= 0 && stack[h - 1] >= 0 ? sym(&a, (Sym){S_ADD, 0, 0, stack[h - 2], stack[h - 1], 0}) : -1;
+ break;
+ case PURE_OP_ITEM_GET:
+ stack[h - 1] = op->plan & PLAN_TEXT || stack[h - 1] < 0 ? -1
+ : sym(&a, (Sym){S_ITEM, (uint16_t)list_of(op->arg), (uint16_t)item_field(op->arg), stack[h - 1], -1, 0});
+ break;
+ case PURE_OP_ADD: case PURE_OP_SUB: case PURE_OP_MUL:
+ stack[h - 2] = stack[h - 2] >= 0 && stack[h - 1] >= 0
+ ? sym(&a, (Sym){op->op == PURE_OP_ADD ? S_ADD : op->op == PURE_OP_SUB ? S_SUB : S_MUL, 0, 0, stack[h - 2],
+ stack[h - 1], 0})
+ : -1;
+ break;
+ case PURE_OP_LT: case PURE_OP_LE: case PURE_OP_GT: case PURE_OP_GE: case PURE_OP_EQ: case PURE_OP_NE:
+ stack[h - 2] = -1;
+ break;
+ case PURE_OP_LIST_COUNT: case PURE_OP_LOAD: stack[h] = -1; break;
+ case PURE_OP_NOT: stack[h - 1] = -1; break;
+ case PURE_OP_JZ: case PURE_OP_BREAK: break; /* the way on keeps what it had */
+ case PURE_OP_EACH: {
+ unsigned inner = (unsigned)k->opens[pc - k->block.start];
+ const LoopAt *in = &k->loops[inner];
+ if (!(op->arg & PURE_EACH_SLICED) || event_list(m, in->list) || !has_round(&a, stack[h - 2])) return;
+ for (uint32_t at = in->start; at < in->end; ++at) { /* the column its block reads */
+ const PureOp *get = &m->code[at];
+ if (k->loop_of[at - k->block.start] != (int)inner || get->op != PURE_OP_ITEM_GET || get->plan & PLAN_TEXT ||
+ list_of(get->arg) != in->list)
+ continue;
+ a.inner = inner, a.list = in->list, a.field = item_field(get->arg), a.key = stack[h - 2];
+ if (sym_depth(&a, a.key) > 4) return; /* d4-d7 */
+ if (!grow_array((void **)&k->aheads, &k->ahead_room, k->ahead_count + 1, sizeof(*k->aheads))) return;
+ k->aheads[k->ahead_count] = a;
+ l->ahead = (int)k->ahead_count++;
+ return;
+ }
+ return;
+ }
+ default: return;
+ }
+ pc = after(op, pc);
+ }
+}
+
+/* The round of loop li moved on by rounds, as a number in d (its place's or its count's value). */
+static void round_ahead(Block *k, unsigned li, unsigned d, unsigned by) {
+ Emit *e = k->e;
+ const LoopAt *l = &k->loops[li];
+ if (l->walk) fp2(e, UCVTF_X, d, k->kept[l->at_kept].reg);
+ else load_d(e, d, X28, (k->m->code[l->at].arg & 0xFF) * sizeof(PureValue));
+ double ahead = (double)by;
+ uint64_t bits;
+ memcpy(&bits, &ahead, sizeof(bits));
+ mov_imm(e, X9, bits);
+ fp2(e, FMOV_DX, D3, X9);
+ fp3(e, FADD, d, d, D3);
+}
+
+/* The registers node n takes (d4 up). */
+static unsigned sym_depth(const Ahead *a, int n) {
+ const Sym *s = &a->nodes[n];
+ if (s->kind == S_ITEM) return sym_depth(a, s->a);
+ if (s->kind < S_ADD) return 1;
+ unsigned x = sym_depth(a, s->a), y = sym_depth(a, s->b) + 1;
+ return x > y ? x : y;
+}
+
+/* The key's node n, the round moved on by rounds, into d4 + depth; to skip
+ * when it reads past a list's items or a list that is not FLAT. */
+static void emit_key(Block *k, unsigned li, const Ahead *a, int n, unsigned depth, unsigned by, int skip) {
+ Emit *e = k->e;
+ const Sym *s = &a->nodes[n];
+ unsigned r = D4 + depth;
+ switch (s->kind) {
+ case S_CONST: {
+ uint64_t bits;
+ memcpy(&bits, &s->value, sizeof(bits));
+ mov_imm(e, X9, bits);
+ fp2(e, FMOV_DX, r, X9);
+ return;
+ }
+ case S_ROUND: round_ahead(k, li, r, by); return;
+ case S_ITEM: {
+ emit_key(k, li, a, s->a, depth, by, skip); /* its position */
+ int column_c = kept_find(k, (int)li, C_COLUMN, s->list, s->field), count_c = kept_find(k, (int)li, C_COUNT, s->list, 0);
+ if (column_c >= 0) { /* the column the loop keeps: read through its tagged address */
+ unsigned column = kept_reg(k, column_c);
+ fp2(e, FCVTZU_X, X0, r);
+ cbz_x(e, column, skip);
+ if (count_c >= 0) cmp_reg(e, X0, kept_reg(k, count_c));
+ else {
+ load64(e, X16, X24, (size_t)s->list * sizeof(PureList *));
+ load64(e, X10, X16, pure_list_layout[LIST_COUNT_AT]);
+ cmp_reg(e, X0, X10);
+ }
+ b_cond(e, HS, skip);
+ load_cell(k, r, column, X0);
+ return;
+ }
+ int four = new_label(e), one = new_label(e), done = new_label(e);
+ fp2(e, FCVTZU_X, X0, r);
+ load64(e, X16, X24, (size_t)s->list * sizeof(PureList *));
+ load8(e, X17, X16, pure_list_layout[LIST_FORM_AT]);
+ cmp_w_imm(e, X17, FLAT_FORM);
+ b_cond(e, NE, skip);
+ load64(e, X10, X16, pure_list_layout[LIST_COUNT_AT]);
+ cmp_reg(e, X0, X10);
+ b_cond(e, HS, skip);
+ load64(e, X17, X16, pure_list_layout[LIST_COLUMNS_AT] + s->field * sizeof(void *));
+ load8(e, X10, X16, pure_list_layout[LIST_WIDTH_AT] + s->field);
+ cmp_w_imm(e, X10, 4);
+ b_cond(e, EQ, four);
+ cmp_w_imm(e, X10, 1);
+ b_cond(e, EQ, one);
+ load_d_index(e, r, X17, X0);
+ b(e, done);
+ bind(e, four);
+ load32_index(e, X10, X17, X0);
+ fp2(e, UCVTF_W, r, X10);
+ b(e, done);
+ bind(e, one);
+ load8_index(e, X10, X17, X0);
+ fp2(e, UCVTF_W, r, X10);
+ bind(e, done);
+ return;
+ }
+ default:
+ emit_key(k, li, a, s->a, depth, by, skip);
+ emit_key(k, li, a, s->b, depth + 1, by, skip);
+ fp3(e, s->kind == S_ADD ? FADD : s->kind == S_SUB ? FSUB : FMUL, r, r, D4 + depth + 1);
+ return;
+ }
+}
+
+/* The key in d4 as the address of its bucket's first position in a's
+ * packed list (x16 the list, x17 its pack, x10 the bucket, x9 the address),
+ * or to skip when the list is not packed. Not checked against the index's
+ * range: a read ahead may go anywhere (PRFM never faults). */
+static void key_bucket(Block *k, const Ahead *a, int skip) {
+ Emit *e = k->e;
+ fp2(e, FCVTZS_X, X0, D4);
+ load64(e, X16, X24, (size_t)a->list * sizeof(PureList *));
+ load64(e, X17, X16, pure_list_layout[LIST_PACK_AT]);
+ cbz_x(e, X17, skip);
+ load64(e, X9, X17, pure_pack_layout[PACK_ORIGIN_AT]);
+ sub_reg(e, X10, X0, X9);
+ load32(e, X9, X17, pure_pack_layout[PACK_SHIFT_AT]);
+ lsr_reg(e, X10, X10, X9);
+ load64(e, X9, X17, pure_pack_layout[PACK_FIRST_AT]);
+ add_lsl(e, X9, X9, X10, 2);
+}
+
+/* At the start of loop li's block: its slice's bucket AHEAD_FAR rounds on;
+ * AHEAD_NEAR rounds on, the bucket read (ahead before) for the slice's first
+ * cell of the column its block reads, and that cell read ahead. */
+static void read_ahead(Block *k, unsigned li) {
+ Emit *e = k->e;
+ const LoopAt *l = &k->loops[li];
+ const Ahead *a = &k->aheads[l->ahead];
+ int near = new_label(e), done = new_label(e), column_c = kept_find(k, (int)li, C_COLUMN, a->list, a->field);
+ emit_key(k, li, a, a->key, 0, AHEAD_FAR, near);
+ key_bucket(k, a, near);
+ prefetch(e, X9);
+ bind(e, near);
+ emit_key(k, li, a, a->key, 0, AHEAD_NEAR, done);
+ key_bucket(k, a, done);
+ load64(e, X0, X17, pure_pack_layout[PACK_BUCKETS_AT]); /* a bucket of the index: its first position is read */
+ cmp_reg(e, X10, X0);
+ b_cond(e, HS, done);
+ load32(e, X10, X9, 0);
+ if (column_c >= 0) { /* the tagged address the inner loop keeps: its top byte the cells' width */
+ unsigned column = kept_reg(k, column_c);
+ cbz_x(e, column, done);
+ lsr_imm(e, X9, column, 56);
+ mul_reg(e, X10, X10, X9);
+ add_reg(e, X17, column, X10);
+ } else {
+ load64(e, X17, X16, pure_list_layout[LIST_COLUMNS_AT] + a->field * sizeof(void *));
+ load8(e, X9, X16, pure_list_layout[LIST_WIDTH_AT] + a->field);
+ mul_reg(e, X10, X10, X9);
+ add_reg(e, X17, X17, X10);
+ }
+ prefetch(e, X17);
+ bind(e, done);
+}
+
/* The block's heights (absolute: a loop's block is verified from its own start), its loops, and each instruction's. */
static bool shape_block(Block *k) {
const PureModule *m = k->m;
@@ -1758,6 +2091,7 @@ static bool shape_block(Block *k) {
l->again = new_label(k->e), l->after = new_label(k->e), l->depth = depth + 1, l->at = pc;
l->walk = op->op == PURE_OP_EACH, l->list = op->arg >> 8 & 0xFF, l->base = at - pops;
l->nest = depth ? k->loops[open[0]].nest : (int)k->loop_count;
+ l->ahead = -1;
l->at_kept = l->end_kept = l->k_kept = l->n_kept = -1;
if (l->start < l->end) k->target[l->start - k->block.start] = true; /* its block begins: what it keeps read */
k->opens[pc - k->block.start] = (int)k->loop_count;
@@ -1782,6 +2116,8 @@ static bool shape_block(Block *k) {
for (uint32_t i = 0; i <= n; ++i) k->op_label[i] = new_label(k->e);
k->exit = new_label(k->e);
plan_kept(k);
+ for (unsigned li = 0; li < k->loop_count; ++li)
+ if (k->loops[li].start < k->loops[li].end && (k->loops[li].walk ? k->loops[li].fast : true)) plan_ahead(k, li);
return !k->e->failed;
}
@@ -1817,9 +2153,12 @@ static bool compile_block(const PureModule *m, PureBlock block, unsigned end_hei
loop_ends(k, pc);
if (k->target[pc - block.start]) join(k, k->height[pc - block.start], k->loop_of[pc - block.start]);
bind(e, k->op_label[pc - block.start]);
+ int l = k->loop_of[pc - block.start];
+ if (l >= 0 && k->loops[l].start == pc && k->loops[l].ahead >= 0) read_ahead(k, (unsigned)l);
compile_op(k, pc);
const PureOp *op = &m->code[pc];
- pc = op->op == PURE_OP_EACH || op->op == PURE_OP_LOOP ? pc + 1 : after(op, pc);
+ pc = op->op == PURE_OP_EACH || op->op == PURE_OP_LOOP ? pc + 1 : after(op, pc) + k->joined;
+ k->joined = false;
}
uint32_t end = block.start + block.length;
loop_ends(k, end);
@@ -1850,6 +2189,7 @@ static bool compile_block(const PureModule *m, PureBlock block, unsigned end_hei
free(k->faults);
free(k->slows);
free(k->wides);
+ free(k->aheads);
free(k);
return ok;
}
diff --git a/core/module-list.c b/core/module-list.c
index 7a1e08b..fb6fff6 100644
--- a/core/module-list.c
+++ b/core/module-list.c
@@ -1492,7 +1492,7 @@ const size_t pure_list_layout[] = {
const size_t pure_pack_layout[] = {
[PACK_FIRST_AT] = offsetof(Pack, first), [PACK_WHOLE_AT] = offsetof(Pack, whole), [PACK_SHIFT_AT] = offsetof(Pack, shift),
[PACK_ORIGIN_AT] = offsetof(Pack, origin_whole), [PACK_LOW_AT] = offsetof(Pack, low_whole),
- [PACK_HIGH_AT] = offsetof(Pack, high_whole)};
+ [PACK_HIGH_AT] = offsetof(Pack, high_whole), [PACK_BUCKETS_AT] = offsetof(Pack, buckets)};
bool pure_list_lookup(const PureList *list, unsigned key_field, PureCell key, unsigned field, PureCell *cell) {
bool found;
diff --git a/core/module-run.h b/core/module-run.h
index c0d6c41..c56e7b1 100644
--- a/core/module-run.h
+++ b/core/module-run.h
@@ -108,8 +108,8 @@ extern const size_t pure_list_layout[];
/* A packed list's index (docs/design/022), where its keys are whole: the
* first position of each bucket (u32), whether the keys are whole, the
* buckets' width as a shift, the first bucket's start, the first key and
- * the last (i64). */
-enum { PACK_FIRST_AT, PACK_WHOLE_AT, PACK_SHIFT_AT, PACK_ORIGIN_AT, PACK_LOW_AT, PACK_HIGH_AT };
+ * the last (i64), and how many buckets (size_t). */
+enum { PACK_FIRST_AT, PACK_WHOLE_AT, PACK_SHIFT_AT, PACK_ORIGIN_AT, PACK_LOW_AT, PACK_HIGH_AT, PACK_BUCKETS_AT };
extern const size_t pure_pack_layout[];
/* A module's code compiled (AArch64 Linux; elsewhere, or with PURE_JIT=0 in