diff options
| author | eunhhu <[email protected]> | 2026-10-08 14:56:33 +0100 |
|---|---|---|
| committer | eunhhu <[email protected]> | 2026-10-08 14:56:33 +0100 |
| commit | a6fabf00b80fc226318a659bb73cbc4f5790fac7 (patch) | |
| tree | 29d8ba10cba4a3909a1b07a211abfeef80f7f710 /core/module-batch.c | |
| parent | 042dbfd98d664d67dcb006f1870984fc97bfebe1 (diff) | |
| download | pure-a6fabf00b80fc226318a659bb73cbc4f5790fac7.tar.gz pure-a6fabf00b80fc226318a659bb73cbc4f5790fac7.zip | |
Batch plans: the recurrences read once at load too (design 025, stage 1): their locals, updates and steps, records and a round's actions in the plan, a number that stays planned by its source (a constant, field, LOAD or local the block does not set: the same source one number, two sources of the same value two) and filled in as a batch opens, the pass finding an update or a record by its instruction's role; the check compares updates, records and whether the batch holds them with the block read again each batch: seeds 1 2 3 5 23 (default and PURE_SELECT=all), the fixed sources and the unit tests pass
Diffstat (limited to 'core/module-batch.c')
| -rw-r--r-- | core/module-batch.c | 325 |
1 files changed, 227 insertions, 98 deletions
diff --git a/core/module-batch.c b/core/module-batch.c index 19b239f..5bcab67 100644 --- a/core/module-batch.c +++ b/core/module-batch.c @@ -57,21 +57,44 @@ typedef struct { uint32_t list, field; Val rows, value; Mask *mask; } Store; enum { V_X = STEPS, V_SLOT = V_X + RECURS, V_NUMBER, VALUES = 256 }; typedef struct { uint8_t op, a, c; } Step; typedef struct { uint32_t from, set; unsigned x, first, count; uint8_t value; } Update; -typedef struct { uint32_t pc; unsigned x; double *lanes; bool shared; } Record; /* shared: an earlier record's lanes, x not set between */ +typedef struct { uint32_t pc; unsigned x, of; bool shared; } Record; /* shared: record of's lanes, x not set between */ /* A sum into a cell, L[k].f = L[k].f + e: its statement's second k from * again to resume (past its find and read, skipped), its ADD and its set; * and as the pass leaves it, its rows and e in the lanes of mask, added at * the end round by round. */ typedef struct { uint32_t again, resume, add, set; } Sum; typedef struct { uint32_t list, field, add, set; Val rows, e; Mask *mask; } Scatter; +/* A round of the recurrences, in the block's order: a step (op, its two + * numbers, where its result goes), a record of x (a: x, c: the record) or x + * set (a: its value, to: x). */ +enum { A_RECORD = 254, A_SET }; +typedef struct { uint8_t op, a, c, to; } Action; +/* A batch loop's recurrences as the load reads them (docs/design/025): the + * locals, the updates and their steps, the records, the numbers that stay + * (each from where a batch reads it: a constant, a field, a LOAD, a local + * the block does not set; the same source one number) and a round's + * actions. A batch fills the numbers and the recurrences' values in. */ +enum { S_CONST, S_FIELD, S_LOAD, S_LOCAL }; +typedef struct { uint8_t kind; uint32_t arg; double value; } Source; +typedef struct { + unsigned recur_count, update_count, record_count, step_count, source_count, action_count; + unsigned recur_local[RECURS]; + Update updates[UPDATES]; + Record records[RECORDS]; + Step steps[STEPS]; + Source sources[VALUES - V_NUMBER]; + Action actions[STEPS + UPDATES + RECORDS]; +} Recurring; /* A batch loop's block as the load reads it, once (docs/design/025): the * locals it sets (a nested loop's slot too), whether it sets items, its sums * into cells, and at each of its instructions what a pass does there besides - * the instruction: at a sum's again, add or set, that (role, the sum's - * index), and at a LOCAL_LOAD beginning x = x op e how far on op is (by; 0: - * none). A nested loop's instructions have no role (a sum is the loop's own - * block's) but may begin x = x op e. failed: more sums than a batch holds. */ -enum { AT_PLAIN, AT_AGAIN, AT_ADD, AT_SET }; + * the instruction: at a sum's again, add or set, at a recurrence's update + * (its from) or record, that (role, the sum's, update's or record's index), + * and at a LOCAL_LOAD beginning x = x op e how far on op is (by; 0: none). + * A nested loop's instructions have no role (sums and recurrences are the + * loop's own block's) but may begin x = x op e. Its recurrences (NULL: + * none). failed: more sums or recurrences than a batch holds. */ +enum { AT_PLAIN, AT_AGAIN, AT_ADD, AT_SET, AT_UPDATE, AT_RECORD }; typedef struct { uint8_t role, index; uint16_t by; } PlanAt; typedef struct PureBatchPlan { uint32_t loop, start, end; @@ -79,6 +102,7 @@ typedef struct PureBatchPlan { bool items, failed; unsigned sum_count; Sum sums[SUMS]; + Recurring *recur; PlanAt at[]; } PureBatchPlan; /* An item a LIST_APPEND at pc appends in the lanes of mask (NULL: all): its fields. */ @@ -174,14 +198,9 @@ typedef struct { Store stores[STORES]; Append appends[APPENDS]; unsigned append_count; - unsigned recur_count, update_count, record_count, step_count; - unsigned recur_local[RECURS]; double recur_value[RECURS]; /* each recurrence's after the rounds so far */ double values[VALUES]; /* what a round of the recurrences keeps (V_): numbers from V_NUMBER on */ - unsigned number_count; - Update updates[UPDATES]; - Record records[RECORDS]; - Step steps[STEPS]; + double *record_lanes[RECORDS]; /* each record's value in each lane */ unsigned scatter_count; Scatter scatters[SUMS]; Filter filters[FILTERS]; @@ -1035,10 +1054,12 @@ static void check_failed(const Batch *b, const char *what, uint32_t pc) { abort(); } static bool plan_sums(const PureModule *m, PureBatchPlan *p); +static double field_value(const Batch *b, uint32_t arg); static void check_plan(Batch *b, const Loop *loop) { const PureBatchPlan *p = b->plan; const PureModule *m = b->m; if (p->start != loop->start || p->end != loop->end || b->end != p->end) check_failed(b, "range", loop->start); + if ((m->code[p->loop].op == PURE_OP_EACH ? LOCALS : (m->code[p->loop].arg & 0xFF)) != b->slot) check_failed(b, "slot", loop->start); uint64_t sets[LOCALS / 64] = {0}; bool items = false; for (uint32_t pc = loop->start; pc < loop->end; ++pc) { @@ -1049,8 +1070,95 @@ static void check_plan(Batch *b, const Loop *loop) { if (memcmp(sets, p->sets, sizeof(sets)) || items != p->items) check_failed(b, "sets", loop->start); PureBatchPlan again = {.start = p->start, .end = p->end}; bool fits = !items || plan_sums(m, &again); - if (!fits != p->failed || again.sum_count != p->sum_count || memcmp(again.sums, p->sums, sizeof(again.sums))) - check_failed(b, "sums", loop->start); + if (again.sum_count != p->sum_count || memcmp(again.sums, p->sums, sizeof(again.sums))) check_failed(b, "sums", loop->start); + /* the recurrences as each batch found them (numbers by their values) */ + static struct { unsigned recur_count, update_count, record_count, step_count, number_count; unsigned recur_local[RECURS]; + Update updates[UPDATES]; Record records[RECORDS]; Step steps[STEPS]; double values[VALUES]; } o; + memset(&o, 0, sizeof(o)); + bool recurs = true; + for (uint32_t pc = loop->start; pc < loop->end && recurs; ++pc) { + const PureOp *op = &m->code[pc]; + if (op->op == PURE_OP_LOOP || op->op == PURE_OP_EACH) pc += op->arg >> 16; + if (op->op != PURE_OP_LOCAL_STORE || !(op->plan & PLAN_RECUR)) continue; + unsigned local = op->arg & 0xFF, x = 0; + while (x < o.recur_count && o.recur_local[x] != local) ++x; + if (x == o.recur_count) { + if (x == RECURS) { recurs = false; break; } + o.recur_local[o.recur_count++] = local; + } + uint32_t from; + if (o.update_count == UPDATES || !pure_batch_recurs(m, pc, loop->start, b->sets, b->slot, &from)) { recurs = false; break; } + Update *u = &o.updates[o.update_count++]; + *u = (Update){from, pc, x, o.step_count, 0, 0}; + uint8_t operands[PURE_MODULE_STACK]; + unsigned depth = 0; + for (uint32_t at = from; at < pc && recurs; ++at) { + const PureOp *e = &m->code[at]; + unsigned l = e->arg & 0xFF, r = 0; + int v = -1; + double k = 0; + bool numbered = false; + switch (e->op) { + case PURE_OP_CONST: k = e->value, numbered = true; break; + case PURE_OP_FIELD: k = field_value(b, e->arg), numbered = true; break; + case PURE_OP_LOAD: k = b->frame->values[e->arg].num, numbered = true; break; + case PURE_OP_LOCAL_LOAD: + while (r < o.recur_count && o.recur_local[r] != l) ++r; + if (l == b->slot) v = V_SLOT; + else if (r < o.recur_count) v = (int)(V_X + r); + else k = b->ev->locals[l].num, numbered = true; + break; + default: { + bool one = e->op == PURE_OP_NEG || e->op == PURE_OP_NOT || e->op == PURE_OP_FLOOR; + uint8_t c = operands[--depth], a = one ? c : operands[--depth]; + for (unsigned i = u->first; i < o.step_count && v < 0; ++i) + if (o.steps[i].op == e->op && o.steps[i].a == a && o.steps[i].c == c) v = (int)i; + if (v < 0 && o.step_count < STEPS) o.steps[o.step_count] = (Step){e->op, a, c}, v = (int)o.step_count++; + } + } + if (numbered) { + for (unsigned i = V_NUMBER; i < V_NUMBER + o.number_count && v < 0; ++i) + if (!memcmp(&o.values[i], &k, sizeof(k))) v = (int)i; + if (v < 0 && V_NUMBER + o.number_count < VALUES) o.values[V_NUMBER + o.number_count] = k, v = (int)(V_NUMBER + o.number_count++); + } + if (v < 0) recurs = false; + else operands[depth++] = (uint8_t)v; + } + if (recurs) u->count = o.step_count - u->first; + } + for (uint32_t pc = loop->start; pc < loop->end && recurs && o.recur_count; ++pc) { + const PureOp *op = &m->code[pc]; + if (op->op == PURE_OP_LOOP || op->op == PURE_OP_EACH) pc += op->arg >> 16; + if (op->op != PURE_OP_LOCAL_LOAD) continue; + unsigned x = 0, u = 0; + while (x < o.recur_count && o.recur_local[x] != (op->arg & 0xFF)) ++x; + while (u < o.update_count && !(pc >= o.updates[u].from && pc < o.updates[u].set)) ++u; + if (x == o.recur_count || u < o.update_count) continue; + if (o.record_count == RECORDS) { recurs = false; break; } + Record *record = &o.records[o.record_count++]; + *record = (Record){pc, x, 0, false}; + for (unsigned j = 0; j + 1 < o.record_count && !record->shared; ++j) { + bool set = o.records[j].x != x; + for (unsigned v = 0; v < o.update_count && !set; ++v) + set = o.updates[v].x == x && o.updates[v].set > o.records[j].pc && o.updates[v].set < pc; + if (!set) record->shared = true; + } + } + if (!(recurs && fits) != p->failed) check_failed(b, "failed", loop->start); + if (p->failed) return; + const Recurring *rc = p->recur; + unsigned recur_count = rc ? rc->recur_count : 0, update_count = rc ? rc->update_count : 0, record_count = rc ? rc->record_count : 0; + if (recur_count != o.recur_count || update_count != o.update_count || record_count != o.record_count) + check_failed(b, "recurrences' counts", loop->start); + for (unsigned x = 0; x < recur_count; ++x) + if (rc->recur_local[x] != o.recur_local[x]) check_failed(b, "recurrence", loop->start); + for (unsigned u = 0; u < update_count; ++u) + if (rc->updates[u].from != o.updates[u].from || rc->updates[u].set != o.updates[u].set || rc->updates[u].x != o.updates[u].x || + rc->updates[u].count != o.updates[u].count) + check_failed(b, "update", rc->updates[u].from); + for (unsigned k = 0; k < record_count; ++k) + if (rc->records[k].pc != o.records[k].pc || rc->records[k].x != o.records[k].x || rc->records[k].shared != o.records[k].shared) + check_failed(b, "record", rc->records[k].pc); } #endif @@ -1470,35 +1578,40 @@ static double field_value(const Batch *b, uint32_t arg) { return field->type == PURE_FIELD_U32 ? read32(given) : field->type == PURE_FIELD_BOOL ? given[0] : read_double(given); } -/* Where the recurrences' rounds keep a number that stays (the one there - * already when it has its bits), or -1 when they hold no more. */ -static int number(Batch *b, double k) { - for (unsigned i = V_NUMBER; i < V_NUMBER + b->number_count; ++i) - if (!memcmp(&b->values[i], &k, sizeof(k))) return (int)i; - if (V_NUMBER + b->number_count == VALUES) return -1; - b->values[V_NUMBER + b->number_count] = k; - return (int)(V_NUMBER + b->number_count++); +/* Where the recurrences' rounds keep a number that stays (one source's + * there already, the same constant's or the same field's, LOAD's or local's), + * or -1 when they hold no more. */ +static int number(Recurring *rc, uint8_t kind, uint32_t arg, double value) { + for (unsigned i = 0; i < rc->source_count; ++i) + if (rc->sources[i].kind == kind && (kind == S_CONST ? !memcmp(&rc->sources[i].value, &value, sizeof(value)) : rc->sources[i].arg == arg)) + return (int)(V_NUMBER + i); + if (V_NUMBER + rc->source_count == VALUES) return -1; + rc->sources[rc->source_count] = (Source){kind, arg, value}; + return (int)(V_NUMBER + rc->source_count++); } -/* The recurrences of the batch's block (their sets planned PLAN_RECUR): each - * x = e as steps, and each other read of x a record. false: more than it holds. */ -static bool recurrences(Batch *b, uint32_t start, uint32_t end) { - const PureModule *m = b->m; - b->number_count = 0; +/* The recurrences of the plan's block (their sets planned PLAN_RECUR): each + * x = e as steps, each other read of x a record, and a round's actions (a + * slot: the loop's local, LOCALS a walk's). false: more than a batch holds + * (counted by the numbers' sources: two of the same value are two). */ +static bool plan_recurrences(const PureModule *m, PureBatchPlan *p, unsigned slot) { + uint32_t start = p->start, end = p->end; + Recurring *rc = NULL; for (uint32_t pc = start; pc < end; ++pc) { const PureOp *op = &m->code[pc]; if (op->op == PURE_OP_LOOP || op->op == PURE_OP_EACH) pc += op->arg >> 16; if (op->op != PURE_OP_LOCAL_STORE || !(op->plan & PLAN_RECUR)) continue; + if (!rc && !(rc = p->recur = calloc(1, sizeof(*rc)))) return false; unsigned local = op->arg & 0xFF, x = 0; - while (x < b->recur_count && b->recur_local[x] != local) ++x; - if (x == b->recur_count) { + while (x < rc->recur_count && rc->recur_local[x] != local) ++x; + if (x == rc->recur_count) { if (x == RECURS) return false; - b->recur_local[x] = local, b->recur_value[x] = b->ev->locals[local].num, ++b->recur_count; + rc->recur_local[rc->recur_count++] = local; } uint32_t from; - if (b->update_count == UPDATES || !pure_batch_recurs(m, pc, start, b->sets, b->slot, &from)) return false; - Update *u = &b->updates[b->update_count++]; - *u = (Update){from, pc, x, b->step_count, 0, 0}; + if (rc->update_count == UPDATES || !pure_batch_recurs(m, pc, start, p->sets, slot, &from)) return false; + Update *u = &rc->updates[rc->update_count++]; + *u = (Update){from, pc, x, rc->step_count, 0, 0}; uint8_t operands[PURE_MODULE_STACK]; unsigned depth = 0; for (uint32_t at = from; at < pc; ++at) { @@ -1506,77 +1619,96 @@ static bool recurrences(Batch *b, uint32_t start, uint32_t end) { unsigned l = e->arg & 0xFF, r = 0; int v = -1; switch (e->op) { - case PURE_OP_CONST: v = number(b, e->value); break; - case PURE_OP_FIELD: v = number(b, field_value(b, e->arg)); break; - case PURE_OP_LOAD: v = number(b, b->frame->values[e->arg].num); break; + case PURE_OP_CONST: v = number(rc, S_CONST, 0, e->value); break; + case PURE_OP_FIELD: v = number(rc, S_FIELD, e->arg, 0); break; + case PURE_OP_LOAD: v = number(rc, S_LOAD, e->arg, 0); break; case PURE_OP_LOCAL_LOAD: - while (r < b->recur_count && b->recur_local[r] != l) ++r; - v = l == b->slot ? V_SLOT : r < b->recur_count ? (int)(V_X + r) : number(b, b->ev->locals[l].num); /* the block does not set it */ + while (r < rc->recur_count && rc->recur_local[r] != l) ++r; + v = l == slot ? V_SLOT : r < rc->recur_count ? (int)(V_X + r) : number(rc, S_LOCAL, l, 0); /* the block does not set it */ break; default: { /* an operation of one number (NEG, NOT, FLOOR) or two */ bool one = e->op == PURE_OP_NEG || e->op == PURE_OP_NOT || e->op == PURE_OP_FLOOR; uint8_t c = operands[--depth], a = one ? c : operands[--depth]; - for (unsigned i = u->first; i < b->step_count && v < 0; ++i) /* computed before in e (x the same until e is done) */ - if (b->steps[i].op == e->op && b->steps[i].a == a && b->steps[i].c == c) v = (int)i; - if (v < 0 && b->step_count < STEPS) b->steps[b->step_count] = (Step){e->op, a, c}, v = (int)b->step_count++; + for (unsigned i = u->first; i < rc->step_count && v < 0; ++i) /* computed before in e (x the same until e is done) */ + if (rc->steps[i].op == e->op && rc->steps[i].a == a && rc->steps[i].c == c) v = (int)i; + if (v < 0 && rc->step_count < STEPS) rc->steps[rc->step_count] = (Step){e->op, a, c}, v = (int)rc->step_count++; } } if (v < 0) return false; /* more steps or numbers than it holds */ operands[depth++] = (uint8_t)v; } - u->count = b->step_count - u->first, u->value = operands[0]; + u->count = rc->step_count - u->first, u->value = operands[0]; + p->at[from - start].role = AT_UPDATE, p->at[from - start].index = (uint8_t)(rc->update_count - 1); } + if (!rc) return true; for (uint32_t pc = start; pc < end; ++pc) { /* the block's other reads of a recurrence */ const PureOp *op = &m->code[pc]; if (op->op == PURE_OP_LOOP || op->op == PURE_OP_EACH) pc += op->arg >> 16; if (op->op != PURE_OP_LOCAL_LOAD) continue; unsigned x = 0, u = 0; - while (x < b->recur_count && b->recur_local[x] != (op->arg & 0xFF)) ++x; - while (u < b->update_count && !(pc >= b->updates[u].from && pc < b->updates[u].set)) ++u; - if (x == b->recur_count || u < b->update_count) continue; - if (b->record_count == RECORDS) return false; - Record *record = &b->records[b->record_count++]; - *record = (Record){pc, x, NULL, false}; - for (unsigned j = 0; j + 1 < b->record_count && !record->shared; ++j) { /* x read before where it was not set since: those lanes */ - bool set = b->records[j].x != x; - for (unsigned v = 0; v < b->update_count && !set; ++v) - set = b->updates[v].x == x && b->updates[v].set > b->records[j].pc && b->updates[v].set < pc; - if (!set) record->lanes = b->records[j].lanes, record->shared = true; - } - if (!record->lanes && !(record->lanes = fresh(b))) return false; - } - return true; -} - -/* The recurrences round by round, in the rounds' order (as the executor - * computes them): each record's lane. Their steps, sets of x and records - * laid out once in the block's order, each a number from two others (an - * update's last step setting x itself), so a round is one loop. false: a - * round faults. */ -enum { A_RECORD = 254, A_SET }; -typedef struct { uint8_t op, a, c, to; } Action; -static bool recur(Batch *b) { - if (!b->update_count) return true; - Action actions[STEPS + UPDATES + RECORDS]; - double *lanes[RECORDS]; - unsigned count = 0, u = 0, k = 0; - while (u < b->update_count || k < b->record_count) { - if (k < b->record_count && b->records[k].pc < (u < b->update_count ? b->updates[u].from : UINT32_MAX)) { - lanes[k] = b->records[k].lanes; - if (!b->records[k].shared) actions[count++] = (Action){A_RECORD, (uint8_t)(V_X + b->records[k].x), (uint8_t)k, 0}; + while (x < rc->recur_count && rc->recur_local[x] != (op->arg & 0xFF)) ++x; + while (u < rc->update_count && !(pc >= rc->updates[u].from && pc < rc->updates[u].set)) ++u; + if (x == rc->recur_count || u < rc->update_count) continue; + if (rc->record_count == RECORDS) return false; + Record *record = &rc->records[rc->record_count++]; + *record = (Record){pc, x, 0, false}; + for (unsigned j = 0; j + 1 < rc->record_count && !record->shared; ++j) { /* x read before where it was not set since: those lanes */ + bool set = rc->records[j].x != x; + for (unsigned v = 0; v < rc->update_count && !set; ++v) + set = rc->updates[v].x == x && rc->updates[v].set > rc->records[j].pc && rc->updates[v].set < pc; + if (!set) record->of = rc->records[j].shared ? rc->records[j].of : j, record->shared = true; + } + p->at[pc - start].role = AT_RECORD, p->at[pc - start].index = (uint8_t)(rc->record_count - 1); + } + unsigned u = 0, k = 0; /* a round's actions, in the block's order */ + while (u < rc->update_count || k < rc->record_count) { + if (k < rc->record_count && rc->records[k].pc < (u < rc->update_count ? rc->updates[u].from : UINT32_MAX)) { + if (!rc->records[k].shared) rc->actions[rc->action_count++] = (Action){A_RECORD, (uint8_t)(V_X + rc->records[k].x), (uint8_t)k, 0}; ++k; continue; } - const Update *up = &b->updates[u++]; + const Update *up = &rc->updates[u++]; for (unsigned i = up->first; i < up->first + up->count; ++i) - actions[count++] = (Action){b->steps[i].op, b->steps[i].a, b->steps[i].c, (uint8_t)i}; + rc->actions[rc->action_count++] = (Action){rc->steps[i].op, rc->steps[i].a, rc->steps[i].c, (uint8_t)i}; if (up->count && up->value == up->first + up->count - 1) /* its last step's: x itself (nothing after it reads that step) */ - actions[count - 1].to = (uint8_t)(V_X + up->x); + rc->actions[rc->action_count - 1].to = (uint8_t)(V_X + up->x); else - actions[count++] = (Action){A_SET, up->value, up->value, (uint8_t)(V_X + up->x)}; + rc->actions[rc->action_count++] = (Action){A_SET, up->value, up->value, (uint8_t)(V_X + up->x)}; } + return true; +} + +/* The batch's recurrences: their values as the rounds before left them, the + * numbers that stay, and lanes for the records. false: no lanes left. */ +static bool recurrences(Batch *b) { + const Recurring *rc = b->plan->recur; + if (!rc) return true; + for (unsigned x = 0; x < rc->recur_count; ++x) b->recur_value[x] = b->ev->locals[rc->recur_local[x]].num; + for (unsigned i = 0; i < rc->source_count; ++i) { + const Source *source = &rc->sources[i]; + b->values[V_NUMBER + i] = source->kind == S_CONST ? source->value + : source->kind == S_FIELD ? field_value(b, source->arg) + : source->kind == S_LOAD ? b->frame->values[source->arg].num + : b->ev->locals[source->arg].num; + } + for (unsigned k = 0; k < rc->record_count; ++k) + if (!(b->record_lanes[k] = rc->records[k].shared ? b->record_lanes[rc->records[k].of] : fresh(b))) return false; + return true; +} + +/* The recurrences round by round, in the rounds' order (as the executor + * computes them): each record's lane. Their steps, sets of x and records + * laid out once in the block's order (the plan's actions), each a number + * from two others (an update's last step setting x itself), so a round is + * one loop. false: a round faults. */ +static bool recur(Batch *b) { + const Recurring *rc = b->plan->recur; + if (!rc || !rc->update_count) return true; + const Action *actions = rc->actions; + double *const *lanes = b->record_lanes; + unsigned count = rc->action_count; double *restrict values = b->values; - for (unsigned x = 0; x < b->recur_count; ++x) values[V_X + x] = b->recur_value[x]; + for (unsigned x = 0; x < rc->recur_count; ++x) values[V_X + x] = b->recur_value[x]; for (unsigned r = 0, n = b->n; r < n; ++r) { unsigned lane = round_of(b, r); values[V_SLOT] = r ? b->first + (double)r : b->first; @@ -1603,7 +1735,7 @@ static bool recur(Batch *b) { values[t->to] = v; } } - for (unsigned x = 0; x < b->recur_count; ++x) b->recur_value[x] = values[V_X + x]; + for (unsigned x = 0; x < rc->recur_count; ++x) b->recur_value[x] = values[V_X + x]; return true; } @@ -1672,8 +1804,9 @@ static PureBatchPlan *plan_loop(const PureModule *m, uint32_t pc) { else p->at[at - start].by = (uint16_t)(by - at); } } + if (!plan_recurrences(m, p, m->code[pc].op == PURE_OP_EACH ? LOCALS : m->code[pc].arg & 0xFF)) p->failed = true; if (p->items && !plan_sums(m, p)) p->failed = true; - for (unsigned k = 0; k < p->sum_count; ++k) { + for (unsigned k = 0; k < p->sum_count; ++k) { /* (over a record's: the pass skips the sum's key again) */ const Sum *sum = &p->sums[k]; p->at[sum->again - start].role = AT_AGAIN, p->at[sum->add - start].role = AT_ADD, p->at[sum->set - start].role = AT_SET; p->at[sum->again - start].index = p->at[sum->add - start].index = p->at[sum->set - start].index = (uint8_t)k; @@ -1697,7 +1830,7 @@ bool pure_batch_plans(PureModule *m) { } void pure_batch_plans_free(PureModule *m) { - for (uint32_t i = 0; i < m->batch_plan_count; ++i) free(m->batch_plans[i]); + for (uint32_t i = 0; i < m->batch_plan_count; ++i) free(m->batch_plans[i]->recur), free(m->batch_plans[i]); free(m->batch_plans); m->batch_plans = NULL, m->batch_plan_count = 0; } @@ -1828,9 +1961,12 @@ static bool pass(Batch *b, uint32_t start, uint32_t end) { pc = next; continue; } - unsigned u = 0; const PlanAt *at_pc = &b->plan->at[pc - b->plan->start]; - if (at_pc->role) { /* a sum into a cell: its row and e, kept for the end */ + if (at_pc->role == AT_UPDATE) { /* a recurrence's x = e: made ahead, round by round */ + pc = b->plan->recur->updates[at_pc->index].set + 1; + continue; + } + if (at_pc->role && at_pc->role != AT_RECORD) { /* a sum into a cell: its row and e, kept for the end */ const Sum *sum = &b->plan->sums[at_pc->index]; if (at_pc->role == AT_AGAIN) pc = sum->resume; /* the key again, its find and the cell's read: not needed */ else if (at_pc->role == AT_ADD) pc = sum->set; @@ -1848,11 +1984,6 @@ static bool pass(Batch *b, uint32_t start, uint32_t end) { } continue; } - while (u < b->update_count && b->updates[u].from != pc) ++u; - if (u < b->update_count) { /* a recurrence's x = e: made ahead, round by round */ - pc = b->updates[u].set + 1; - continue; - } if (m->reuses && m->reuse_length[pc]) { /* the same code as an earlier value's, nothing changed between: that value, the code skipped */ uint32_t root = m->reuse_from[pc] - 1; unsigned k = 0; @@ -1939,11 +2070,9 @@ static bool pass(Batch *b, uint32_t start, uint32_t end) { b->stack[b->sp - 1 - arg] = b->stack[b->sp - 1], b->sp -= arg; break; case PURE_OP_LOCAL_LOAD: { - unsigned k = 0; - while (k < b->record_count && b->records[k].pc != pc - 1) ++k; - if (k == b->record_count) local_load(b, arg, pc - 1); + if (at_pc->role != AT_RECORD) local_load(b, arg, pc - 1); else { /* a recurrence's value here, round by round */ - Val v = lanes_val(b->records[k].lanes, false); + Val v = lanes_val(b->record_lanes[at_pc->index], false); v.temp = false; if (b->sel) v = gather(b, &v); v.owner = (uint16_t)(arg >> 16); @@ -2070,7 +2199,7 @@ static void sweep(Batch *b) { for (unsigned s = 0; s < b->store_count; ++s) mark_val(kept, &b->stores[s].rows), mark_val(kept, &b->stores[s].value), mark_mask(b, kept, kept_mask, b->stores[s].mask); for (unsigned d = 0; d < b->sp; ++d) mark_val(kept, &b->stack[d]); - for (unsigned r = 0; r < b->record_count; ++r) mark_lanes(kept, b->records[r].lanes); + for (unsigned r = 0; b->plan->recur && r < b->plan->recur->record_count; ++r) mark_lanes(kept, b->record_lanes[r]); for (unsigned i = 0; i < b->scatter_count; ++i) mark_val(kept, &b->scatters[i].rows), mark_val(kept, &b->scatters[i].e), mark_mask(b, kept, kept_mask, b->scatters[i].mask); for (unsigned a = 0; a < b->append_count; ++a) { @@ -2890,13 +3019,13 @@ static int batch_run(Frame *frame, Loop *loop, unsigned *done) { b->sp = b->split_count = b->buffers = b->masks = b->read_count = b->store_count = b->spare_count = 0; b->mask_free_count = b->reduce_count = b->append_count = b->filter_count = b->known_count = 0; b->sel = NULL; - b->recur_count = b->update_count = b->record_count = b->step_count = b->scatter_count = 0; + b->scatter_count = 0; for (unsigned w = 0; w < LOCALS / 64; ++w) /* the locals it sets: not set yet */ for (uint64_t bits = plan->sets[w]; bits; bits &= bits - 1) b->state[w * 64 + (unsigned)__builtin_ctzll(bits)] = 0; #ifdef PURE_BATCH_CHECK check_plan(b, loop); #endif - if (plan->failed || !recurrences(b, loop->start, loop->end) || !recur(b)) + if (plan->failed || !recurrences(b) || !recur(b)) b->failed = true; /* more sums than a batch holds; the recurrences ahead, round by round */ if (!b->failed && pass(b, guard ? loop->start + 5 : loop->start, loop->end)) { arrive(b, loop->end); @@ -2974,7 +3103,7 @@ static int batch_run(Frame *frame, Loop *loop, unsigned *done) { } app->work += work; for (unsigned r = 0; r < b->reduce_count; ++r) frame->ev->locals[b->reduce[r].local].num = b->reduce[r].result; - for (unsigned x = 0; x < b->recur_count; ++x) frame->ev->locals[b->recur_local[x]].num = b->recur_value[x]; + for (unsigned x = 0; plan->recur && x < plan->recur->recur_count; ++x) frame->ev->locals[plan->recur->recur_local[x]].num = b->recur_value[x]; for (unsigned w = 0; w < LOCALS / 64; ++w) /* each local the block set: as the last round to set it left it */ for (uint64_t bits = b->sets[w]; bits; bits &= bits - 1) { unsigned at = w * 64 + (unsigned)__builtin_ctzll(bits), last = n - 1; |
