aboutsummaryrefslogtreecommitdiffstats
path: root/core/module-jit.c
diff options
context:
space:
mode:
Diffstat (limited to 'core/module-jit.c')
-rw-r--r--core/module-jit.c75
1 files changed, 64 insertions, 11 deletions
diff --git a/core/module-jit.c b/core/module-jit.c
index 2732b6a..e8858fb 100644
--- a/core/module-jit.c
+++ b/core/module-jit.c
@@ -1354,6 +1354,8 @@ static void store_tagged(Block *k, unsigned h, unsigned col, unsigned at, int sl
* its room and its bits' reach), the count or the items added counted in
* their register, the event's headroom less the item's bytes; nothing else
* written until a call or the nest's end (books_back). */
+static void append(Block *k, const PureOp *op, uint32_t pc, unsigned height);
+
static void append_books(Block *k, const PureOp *op, uint32_t pc, unsigned height) {
Emit *e = k->e;
const PureModule *m = k->m;
@@ -1374,7 +1376,8 @@ static void append_books(Block *k, const PureOp *op, uint32_t pc, unsigned heigh
}
unsigned some = key == 1 ? columns[1] : columns[0]; /* a column kept: 0 when the list is not in that form */
size_t bytes = row_bytes(m, list);
- cbz_x(e, some, slow);
+ int other = new_label(e), done = new_label(e); /* a keyed list not DENSE (HASHED): as append does it, the headroom kept */
+ cbz_x(e, some, key ? other : slow);
if (!key) {
load64(e, X16, X24, list * sizeof(PureList *));
load64(e, X17, X16, pure_list_layout[LIST_ROOM_AT]);
@@ -1408,6 +1411,12 @@ static void append_books(Block *k, const PureOp *op, uint32_t pc, unsigned heigh
add_imm(e, pending, pending, 1);
}
slow_back(k, s, V_MEM, 0);
+ if (key) {
+ b(e, done);
+ bind(e, other);
+ append(k, op, pc, height);
+ }
+ bind(e, done);
}
/* A key's bits in a HASHED list's table (module-list.c's key_bits: -0 as +0) into x17. */
@@ -2972,6 +2981,8 @@ typedef struct {
bool bounded; /* an integer its loop's opening bounds (its ends checked there: no check of its own) */
bool proven; /* a list access whose key or position its loop's opening found in range */
bool fused; /* a comparison only the branch right after it reads: its flags, no register */
+ bool deferred; /* a number's arithmetic whose finiteness its one reader, right after it, checks (tier_defer) */
+ int retry; /* where its finiteness check gives up, the first of such a run's snapshot; or -1: its own */
bool aliased; /* a full list's position found: its key's register (they are equal) */
uint16_t list, field;
int a, b, c, d; /* operands: values, or -1 */
@@ -3080,7 +3091,7 @@ static int tval(Tier *t, unsigned op, unsigned rep, int a, int b, int c) {
TVal *v = &t->vals[t->val_count];
memset(v, 0, sizeof(*v));
v->op = (uint8_t)op, v->rep = (uint8_t)rep, v->a = a, v->b = b, v->c = c;
- v->snap = -1, v->loop = t->cur_loop, v->var = -1, v->label = -1, v->reg = -1, v->d = -1;
+ v->snap = -1, v->loop = t->cur_loop, v->var = -1, v->label = -1, v->reg = -1, v->d = -1, v->retry = -1;
v->lo = -INFINITY, v->hi = INFINITY;
int made = (int)t->val_count++;
if (op == T_PHI) return made; /* bound at its merge */
@@ -4008,6 +4019,29 @@ static void tier_facts(Tier *t) {
if (t->vals[i].lo > t->vals[i].hi && t->vals[i].rep != R_BOOL) t->vals[i].lo = -INFINITY, t->vals[i].hi = INFINITY, t->vals[i].whole = false, t->vals[i].zero = true;
}
+/* A run of numbers' arithmetic each read by the next alone, right after it
+ * (no other code between but constants): only the last checks it is finite. A number not
+ * finite stays not finite through an add, a sub, a mul, or as what a
+ * division divides; where it is not, the code gives up at the run's first
+ * snapshot, so the executor makes the run again and fails where the
+ * executor does. */
+static void tier_defer(Tier *t) {
+ for (size_t i = 0; i + 1 < t->item_count; ++i) {
+ size_t next = i + 1; /* the next value but constants (made as they are: no check, nothing changed) */
+ while (next < t->item_count && t->items[next].kind == I_VALUE && t->vals[t->items[next].value].op == T_CONST) ++next;
+ if (t->items[i].kind != I_VALUE || next == t->item_count || t->items[next].kind != I_VALUE) continue;
+ TVal *v = &t->vals[t->items[i].value], *w = &t->vals[t->items[next].value];
+ int vi = t->items[i].value;
+ bool arith = v->rep == R_NUM && v->op >= T_ADD && v->op <= T_DIV && v->snap >= 0 && v->uses == 1;
+ bool reads = w->rep == R_NUM && w->snap >= 0 && w->loop == v->loop &&
+ (((w->op == T_ADD || w->op == T_SUB || w->op == T_MUL) && (w->a == vi || w->b == vi)) ||
+ (w->op == T_DIV && w->a == vi && w->b != vi && (t->vals[w->b].lo > 0 || t->vals[w->b].hi < 0)));
+ if (!arith || !reads || tfinite(v) || tfinite(w)) continue;
+ v->deferred = true;
+ w->retry = v->retry >= 0 ? v->retry : v->snap;
+ }
+}
+
/* Each value's register: a whole number an integer, else a number (a bool's as made). Uses counted. */
static void tier_reps(Tier *t) {
for (size_t i = 0; i < t->val_count; ++i) {
@@ -4169,8 +4203,9 @@ static void tuse_affine(Tier *t, int x, int l, int at) {
static void tuse_snap(Tier *t, int s, int at) {
if (s < 0) return;
const TSnap *snap = &t->snaps[s];
- for (int var = 0; var < VARS; ++var)
- if (var < V_STACK ? tchanged(t, var, snap->vars[var]) : snap->vars[var] >= 0) tuse(t, snap->vars[var], at);
+ for (int var = 0; var < VARS; ++var) /* (a constant written as the constant it is) */
+ if ((var < V_STACK ? tchanged(t, var, snap->vars[var]) : snap->vars[var] >= 0) && t->vals[snap->vars[var]].op != T_CONST)
+ tuse(t, snap->vars[var], at);
for (int l = snap->loop; l >= 0; l = t->loops[l].parent) { /* the open loops: their rounds, counts and lows */
tuse(t, t->loops[l].k, at), tuse(t, t->loops[l].count, at);
if (t->loops[l].low >= 0 && t->vals[t->loops[l].low].op != T_CONST) tuse(t, t->loops[l].low, at); /* a constant written as one */
@@ -4228,7 +4263,8 @@ static void tier_needed(Tier *t, bool *need) {
}
#define NEED(v) do { int v_ = (v); if (v_ >= 0 && !need[v_]) need[v_] = true, work[top++] = v_; } while (0)
#define NEED_SNAP(s) do { if ((s) >= 0) { const TSnap *sn = &t->snaps[s]; \
- for (int var = 0; var < VARS; ++var) if (var < V_STACK ? tchanged(t, var, sn->vars[var]) : sn->vars[var] >= 0) NEED(sn->vars[var]); \
+ for (int var = 0; var < VARS; ++var) if ((var < V_STACK ? tchanged(t, var, sn->vars[var]) : sn->vars[var] >= 0) && \
+ t->vals[sn->vars[var]].op != T_CONST) NEED(sn->vars[var]); \
for (int l = sn->loop; l >= 0; l = t->loops[l].parent) { NEED(t->loops[l].k); NEED(t->loops[l].count); \
if (t->loops[l].low >= 0 && t->vals[t->loops[l].low].op != T_CONST) NEED(t->loops[l].low); } } } while (0)
for (size_t i = 0; i < t->item_count; ++i) {
@@ -4272,6 +4308,7 @@ static void tier_needed(Tier *t, bool *need) {
NEED(v->c);
NEED(v->d);
NEED_SNAP(v->snap);
+ NEED_SNAP(v->retry);
for (unsigned w = 0; w < v->ins; ++w) NEED(v->in[w]);
}
#undef NEED
@@ -4304,6 +4341,7 @@ static bool tier_registers(Tier *t) {
TVal *v = &t->vals[it->value];
tuse(t, v->a, p), tuse(t, v->b, p), tuse(t, v->c, p), tuse(t, v->d, p);
tuse_snap(t, v->snap, p);
+ tuse_snap(t, v->retry, p); /* the run's first snapshot: given up to from here */
break;
}
case I_UNLESS: tuse(t, it->value, p); /* fall through */
@@ -4605,6 +4643,19 @@ static bool tier_has_moves(const Tier *t, int g, int way) {
return false;
}
+/* A value a snapshot holds as a number in d0 (or its own d register): a
+ * constant made there, as the constant it is (its register, if it has one,
+ * not kept for the snapshot: another value's by then). */
+static unsigned tsnap_d(Tier *t, int v) {
+ const TVal *x = &t->vals[v];
+ if (x->op != T_CONST) return td(t, v, D0);
+ uint64_t bits;
+ memcpy(&bits, &x->num, sizeof(bits));
+ mov_imm(t->k->e, X9, bits);
+ fp2(t->k->e, FMOV_DX, D0, X9);
+ return D0;
+}
+
/* What the tier changed, back where the executor keeps it: the variables
* whose values differ from memory's (in vars: a snapshot's or the end's),
* the work, the lists it set (their versions). */
@@ -4615,8 +4666,8 @@ static void tier_write(Tier *t, const int *vars) {
tbase(t, B_VALUES, X16);
for (int var = 0; var < V_STACK; ++var) {
int v = vars[var];
- if (!tchanged(t, var, v) || t->vals[v].reg < 0) continue;
- unsigned d = td(t, v, D0);
+ if (!tchanged(t, var, v) || (t->vals[v].reg < 0 && t->vals[v].op != T_CONST)) continue;
+ unsigned d = tsnap_d(t, v);
if (var < V_STATE) store_d(e, d, X17, (size_t)var * sizeof(PureValue));
else store_d(e, d, X16, (size_t)(var - V_STATE) * sizeof(PureValue));
}
@@ -4738,8 +4789,8 @@ static void tier_stub(Tier *t, int s) {
tbase(t, B_STACK, X17);
for (unsigned h = l0->base; h < snap->height; ++h) {
int v = snap->vars[V_STACK + h];
- if (v < 0 || t->vals[v].reg < 0) continue;
- store_d(e, td(t, v, D0), X17, AT(h));
+ if (v < 0 || (t->vals[v].reg < 0 && t->vals[v].op != T_CONST)) continue;
+ store_d(e, tsnap_d(t, v), X17, AT(h));
if (t->vals[v].kind == K_INDEX) {
mov_w(e, X9, (uint32_t)t->vals[v].list & 0xFFFF);
store_h(e, X9, X17, AT(h) + OWNER);
@@ -5000,7 +5051,8 @@ static void tier_value(Tier *t, int vi) {
unsigned x = td(t, v->a, D0), y = td(t, v->b, D1);
uint32_t fop = v->op == T_ADD ? FADD : v->op == T_SUB ? FSUB : v->op == T_MUL ? FMUL : v->op == T_MIN ? FMIN : FMAX;
fp3(e, fop, r, x, y);
- if (fail >= 0 && (v->op == T_ADD || v->op == T_SUB || v->op == T_MUL) && !tfinite(v)) tfinite_check(t, r, fail);
+ if (fail >= 0 && (v->op == T_ADD || v->op == T_SUB || v->op == T_MUL) && !tfinite(v) && !v->deferred)
+ tfinite_check(t, r, v->retry >= 0 ? tfail(t, v->retry) : fail);
return;
}
case T_DIV:
@@ -5015,7 +5067,7 @@ static void tier_value(Tier *t, int vi) {
b_cond(e, EQ, fail);
}
fp3(e, FDIV, r, x, y);
- if (!tfinite(v)) tfinite_check(t, r, fail);
+ if (!tfinite(v) && !v->deferred) tfinite_check(t, r, v->retry >= 0 ? tfail(t, v->retry) : fail);
return;
}
case T_NEG:
@@ -5403,6 +5455,7 @@ static Tier *tier_plan(Block *k, unsigned li) {
const char *hoist = getenv("PURE_TIER_HOIST");
if (!t->failed && !(hoist && !strcmp(hoist, "0"))) tier_hoist(t);
if (!t->failed) tier_reps(t);
+ if (!t->failed) tier_defer(t);
const char *bounds = getenv("PURE_TIER_BOUNDS");
if (!t->failed && !(bounds && !strcmp(bounds, "0"))) tier_bounds(t);
if (t->failed || !tier_registers(t)) {