aboutsummaryrefslogtreecommitdiffstats
path: root/core/module-batch.c
diff options
context:
space:
mode:
Diffstat (limited to 'core/module-batch.c')
-rw-r--r--core/module-batch.c164
1 files changed, 159 insertions, 5 deletions
diff --git a/core/module-batch.c b/core/module-batch.c
index a05577c..04719be 100644
--- a/core/module-batch.c
+++ b/core/module-batch.c
@@ -25,7 +25,8 @@
#include "module-run.h"
#include <stdlib.h>
-enum { BATCH = 1024, BUFFERS = 24, MASKS = 32, SPLITS = 8, READS = 16, STORES = 8, APPENDS = 8, REDUCES = 8, LOCALS = 256, LEAST = 16, FAILURES = 3 };
+enum { BATCH = 1024, BUFFERS = 24, MASKS = 32, SPLITS = 8, READS = 16, STORES = 8, APPENDS = 8, REDUCES = 8, RECURS = 4, UPDATES = 8,
+ RECORDS = 8, STEPS = 96, LOCALS = 256, LEAST = 16, FAILURES = 3 };
enum { UNIFORM, AFFINE, LANES, REDUCE }; /* REDUCE: x in x = x op e (value: its reduction) */
typedef struct {
@@ -44,6 +45,14 @@ typedef struct Mask { const uint32_t *lanes; const struct Mask *parent; struct M
/* An item's field read at rows (known: else any row), and one set: what it sets and in which lanes (NULL: all). */
typedef struct { uint32_t list, field; bool known; Val rows; } Read;
typedef struct { uint32_t list, field; Val rows, value; Mask *mask; } Store;
+/* A recurrence's x = e ahead of the batch, round by round: e's instructions
+ * as steps (a number, x's value, the loop's value, or an operation on the
+ * numbers before it), the statement from to its set, the recurrence's index;
+ * and where the block reads a recurrence, its value there in each lane. */
+enum { S_NUMBER = 250, S_X, S_SLOT };
+typedef struct { uint8_t op, x; double k; } Step;
+typedef struct { uint32_t from, set; unsigned x, first, count; } Update;
+typedef struct { uint32_t pc; unsigned x; double *lanes; } Record;
/* An item a LIST_APPEND at pc appends in the lanes of mask (NULL: all): its fields. */
typedef struct { uint32_t pc; Mask *mask; unsigned count; Val cells[PURE_RECORD_FIELDS]; } Append;
/* Lanes a branch sent ahead (NULL: every lane): they run again at pc. */
@@ -93,9 +102,18 @@ 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 */
+ Update updates[UPDATES];
+ Record records[RECORDS];
+ Step steps[STEPS];
} Batch;
static _Thread_local Batch batch;
+
+/* A lane's round in the batch (and a round's lane). */
+static inline unsigned round_of(const Batch *b, unsigned l) { return b->reverse ? b->n - 1 - l : l; }
static _Thread_local double buffer[BUFFERS][BATCH + 24]; /* not 2^k apart: lanes of different buffers in different sets of the cache */
static const uint32_t no_lanes[BATCH]; /* a mask's when there were no lanes left to make it (the batch failed) */
@@ -875,6 +893,124 @@ static void reduce_step(Batch *b, Reduce *r, const Val *e) {
}
}
+/* ---- Recurrences */
+
+bool pure_batch_recurs(const PureModule *m, uint32_t s, uint32_t start, const uint64_t *set, unsigned slot, uint32_t *from) {
+ unsigned x = m->code[s].arg & 0xFF;
+ uint32_t p = s;
+ while (p > start && m->height[p]) --p; /* its statement's start: nothing on the stack */
+ if (m->height[p]) return false;
+ for (uint32_t at = p; at < s; ++at) {
+ const PureOp *op = &m->code[at];
+ unsigned local = op->arg & 0xFF;
+ switch (op->op) {
+ case PURE_OP_CONST: if (op->arg == 2) return false; break; /* an i64 */
+ case PURE_OP_FIELD: case PURE_OP_LOAD: break;
+ case PURE_OP_LOCAL_LOAD:
+ if (local != x && local != slot && set[local / 64] >> (local % 64) & 1) return false;
+ break;
+ case PURE_OP_ADD: case PURE_OP_SUB: case PURE_OP_MUL: case PURE_OP_DIV: case PURE_OP_MOD: case PURE_OP_MIN: case PURE_OP_MAX:
+ case PURE_OP_NEG: case PURE_OP_NOT: case PURE_OP_FLOOR: case PURE_OP_LT: case PURE_OP_LE: case PURE_OP_GT: case PURE_OP_GE:
+ case PURE_OP_EQ: case PURE_OP_NE: case PURE_OP_AND: case PURE_OP_OR: break;
+ default: return false;
+ }
+ }
+ *from = p;
+ return true;
+}
+
+/* A field the message (or the arguments) gives, as FIELD reads it. */
+static double field_value(const Batch *b, uint32_t arg) {
+ const PureField *field = &b->m->field[b->ev->shape->field_start + arg];
+ const unsigned char *given = (b->ev->arguments ? b->app->arguments.data : b->app->payload.data) + field->offset;
+ return field->type == PURE_FIELD_U32 ? read32(given) : field->type == PURE_FIELD_BOOL ? given[0] : read_double(given);
+}
+
+/* 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;
+ for (uint32_t pc = start; pc < end; ++pc) {
+ const PureOp *op = &m->code[pc];
+ if (op->op == PURE_OP_LOOP) 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 < b->recur_count && b->recur_local[x] != local) ++x;
+ if (x == b->recur_count) {
+ if (x == RECURS) return false;
+ b->recur_local[x] = local, b->recur_value[x] = b->ev->locals[local].num, ++b->recur_count;
+ }
+ 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};
+ for (uint32_t at = from; at < pc; ++at) {
+ const PureOp *e = &m->code[at];
+ unsigned l = e->arg & 0xFF, r = 0;
+ if (b->step_count == STEPS) return false;
+ Step *step = &b->steps[b->step_count++];
+ *step = (Step){e->op, 0, 0};
+ if (e->op == PURE_OP_CONST) step->op = S_NUMBER, step->k = e->value;
+ else if (e->op == PURE_OP_FIELD) step->op = S_NUMBER, step->k = field_value(b, e->arg);
+ else if (e->op == PURE_OP_LOAD) step->op = S_NUMBER, step->k = b->frame->values[e->arg].num;
+ else if (e->op == PURE_OP_LOCAL_LOAD && l == b->slot) step->op = S_SLOT;
+ else if (e->op == PURE_OP_LOCAL_LOAD) {
+ while (r < b->recur_count && b->recur_local[r] != l) ++r;
+ if (r < b->recur_count) step->op = S_X, step->x = (uint8_t)r;
+ else step->op = S_NUMBER, step->k = b->ev->locals[l].num; /* the block does not set it */
+ }
+ }
+ u->count = b->step_count - u->first;
+ }
+ 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) 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 || !(b->records[b->record_count].lanes = fresh(b))) return false;
+ b->records[b->record_count].pc = pc, b->records[b->record_count++].x = x;
+ }
+ return true;
+}
+
+/* The recurrences round by round, in the rounds' order (as the executor computes them): each record's lane. false: a round faults. */
+static bool recur(Batch *b) {
+ double stack[PURE_MODULE_STACK];
+ if (!b->update_count) return true;
+ for (unsigned r = 0; r < b->n; ++r) {
+ unsigned lane = round_of(b, r), u = 0, k = 0;
+ double slot = r ? b->first + (double)r : b->first;
+ while (u < b->update_count || k < b->record_count) { /* in the block's order */
+ if (k < b->record_count && b->records[k].pc < (u < b->update_count ? b->updates[u].from : UINT32_MAX)) {
+ b->records[k].lanes[lane] = b->recur_value[b->records[k].x];
+ ++k;
+ continue;
+ }
+ const Update *up = &b->updates[u++];
+ unsigned sp = 0;
+ for (unsigned i = up->first; i < up->first + up->count; ++i) {
+ const Step *step = &b->steps[i];
+ switch (step->op) {
+ case S_NUMBER: stack[sp++] = step->k; break;
+ case S_X: stack[sp++] = b->recur_value[step->x]; break;
+ case S_SLOT: stack[sp++] = slot; break;
+ case PURE_OP_NEG: stack[sp - 1] = -stack[sp - 1]; break;
+ case PURE_OP_NOT: stack[sp - 1] = stack[sp - 1] == 0; break;
+ case PURE_OP_FLOOR: stack[sp - 1] = floor(stack[sp - 1]); break;
+ default:
+ --sp;
+ if (!number_op(step->op, stack[sp - 1], stack[sp], &stack[sp - 1])) return false; /* its fault: the executor's */
+ }
+ }
+ b->recur_value[up->x] = stack[0];
+ }
+ }
+ return true;
+}
+
/* ---- The pass */
static void nested(Batch *b, const PureOp *op, uint32_t pc);
@@ -893,6 +1029,12 @@ static bool pass(Batch *b, uint32_t start, uint32_t end) {
pc = next;
continue;
}
+ unsigned u = 0;
+ 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;
+ }
const PureOp *op = &m->code[pc];
uint32_t arg = op->arg;
++pc;
@@ -926,7 +1068,17 @@ static bool pass(Batch *b, uint32_t start, uint32_t end) {
for (unsigned d = b->sp - 1 - arg; d + 1 < b->sp; ++d) release(b, &b->stack[d]);
b->stack[b->sp - 1 - arg] = b->stack[b->sp - 1], b->sp -= arg;
break;
- case PURE_OP_LOCAL_LOAD: local_load(b, arg, pc - 1); 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);
+ else { /* a recurrence's value here, round by round */
+ Val v = lanes_val(b->records[k].lanes, false);
+ v.temp = false, v.owner = (uint16_t)(arg >> 16);
+ b->stack[b->sp++] = v;
+ }
+ break;
+ }
case PURE_OP_LOCAL_STORE: local_store(b, arg); break;
case PURE_OP_NEG: case PURE_OP_NOT: case PURE_OP_FLOOR: unary(b, op->op); break;
case PURE_OP_CLAMP: clamp(b); break;
@@ -1015,6 +1167,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 a = 0; a < b->append_count; ++a) {
mark_mask(b, kept, kept_mask, b->appends[a].mask);
for (unsigned f = 0; f < b->appends[a].count; ++f) mark_val(kept, &b->appends[a].cells[f]);
@@ -1108,8 +1261,6 @@ static void nested(Batch *b, const PureOp *op, uint32_t pc) {
/* ---- The end: the sets, if no read of the batch would have seen one */
-/* A lane's round in the batch. */
-static inline unsigned round_of(const Batch *b, unsigned l) { return b->reverse ? b->n - 1 - l : l; }
/* Whether a store at rows (in the lanes of mask: NULL all) sets in a round the row a later round's read reads. */
static bool reads_set(const Batch *b, const Read *r, const Store *s, const uint32_t *mask) {
@@ -1296,6 +1447,7 @@ int pure_batch_run(Frame *frame, Loop *loop, unsigned *done) {
b->failed = b->none = b->items = false, b->active = NULL, b->nest = (Nest){0}, b->work = 0, b->var = NULL;
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 = 0;
+ b->recur_count = b->update_count = b->record_count = b->step_count = 0;
memset(b->sets, 0, sizeof(b->sets));
for (uint32_t pc = loop->start; pc < loop->end; ++pc) { /* the locals it sets (a nested loop's slot too), and whether it sets items */
const PureOp *op = &m->code[pc];
@@ -1306,7 +1458,8 @@ int pure_batch_run(Frame *frame, Loop *loop, unsigned *done) {
b->state[at] = 0;
}
}
- if (pass(b, loop->start, loop->end)) {
+ if (!recurrences(b, loop->start, loop->end) || !recur(b)) b->failed = true; /* the recurrences ahead, round by round */
+ if (!b->failed && pass(b, loop->start, loop->end)) {
arrive(b, loop->end);
for (unsigned w = 0; w < LOCALS / 64; ++w) /* the lanes each local was set in, made before anything changes */
for (uint64_t bits = b->sets[w]; bits; bits &= bits - 1) {
@@ -1337,6 +1490,7 @@ int pure_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 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;