* SOFTWARE.
*/
-/*
- * XXX: phi function live intervals start at first ordinary instruction,
- * add_range should be taking care of that already ...
- *
- * XXX: TEX must choose TEX's def as representative
- *
- * XXX: Aieee! Must materialize MOVs if source is in other basic block!
- * -- absolutely, or we cannot execute the MOV conditionally at all
- * XXX: Aieee! Must include PHIs in LVA so we pull through liveness if
- * PHI source is e.g. in dominator block.
- * -- seems we lose liveness somehow, track that
- */
-
#include "nv50_context.h"
#include "nv50_pc.h"
bgn = val->insn->serial;
if (bgn < b->entry->serial || bgn > b->exit->serial)
bgn = b->entry->serial;
- // debug_printf("add_range(value %i): [%i, %i)\n", val->n, bgn, end);
if (bgn > end) {
debug_printf("Aieee! BLOCK [%i, %i], RANGE [%i, %i)\n",
do_join_values(ctx, a, b);
}
-/* For each operand of each phi in b, generate a new value by inserting a MOV
- * at the end of the block it is coming from and replace the operand with it.
- * This eliminates liveness conflicts.
+/* For phi functions with sources from blocks that are not direct predecessors,
+ * if such a source is to be used in an earlier predecessor, we need to add an
+ * additional phi function. Used when inserting the MOVs below.
+ */
+static struct nv_value *
+propagate_phi(struct nv_pc *pc, struct nv_instruction *phi, int s)
+{
+ struct nv_basic_block *b = pc->current_block;
+ struct nv_value *val = phi->src[s]->value;
+ struct nv_instruction *nvi = new_instruction(pc, NV_OP_PHI);
+ int i, k;
+
+ (nvi->def[0] = new_value(pc, val->reg.file, val->reg.type))->insn = nvi;
+
+ for (k = 0, i = 0; i < 4 && phi->src[i]; ++i) {
+ if (bb_reachable_by(b, phi->src[i]->value->insn->bb, b))
+ nvi->src[k++] = new_ref(pc, phi->src[i]->value);
+ }
+ return nvi->def[0];
+}
+
+/* For IF blocks without ELSE blocks, insert an empty block for the MOVs.
+ * Insert additional PHIs for cases where a direct MOV wouldn't be valid.
*/
static int
-pass_generate_phi_movs(struct nv_pc_pass *ctx, struct nv_basic_block *b)
+pass_generate_phi_movs_1(struct nv_pc_pass *ctx, struct nv_basic_block *b)
{
- struct nv_instruction *i, *i2;
- struct nv_basic_block *p, *pn;
+ struct nv_instruction *i, *ni;
struct nv_value *val;
+ struct nv_basic_block *p, *pn;
int n, j;
b->pass_seq = ctx->pc->pass_seq;
for (n = 0; n < b->num_in; ++n) {
- p = b->in[n];
+ p = pn = b->in[n];
assert(p);
- if (b->num_in > 1 && p->out[0] && p->out[1]) { /* if without else */
+ if (b->num_in > 1 && p->out[0] && p->out[1]) {
pn = new_basic_block(ctx->pc);
if (p->out[0] == b)
break;
}
}
-
pn->out[0] = b;
pn->in[0] = p;
pn->num_in = 1;
- } else
- pn = p;
+ }
ctx->pc->current_block = pn;
- /* every block with PHIs will also have other operations */
for (i = b->phi; i && i->opcode == NV_OP_PHI; i = i->next) {
- for (j = 0; j < 4; ++j) {
- if (!i->src[j])
- j = 3;
- else
+ for (j = 0; j < 4 && i->src[j]; ++j) {
if (bb_reachable_by(pn, i->src[j]->value->insn->bb, b))
break;
}
- if (j >= 4)
+ if (j >= 4 || !i->src[j])
continue;
- assert(i->src[j]);
val = i->src[j]->value;
- /* XXX: should probably not insert this after terminator */
- i2 = new_instruction(ctx->pc, NV_OP_MOV);
-
- i2->def[0] = new_value(ctx->pc, val->reg.file, val->reg.type);
- i2->src[0] = new_ref (ctx->pc, val);
- i2->def[0]->insn = i2;
-
- nv_reference(ctx->pc, &i->src[j], i2->def[0]);
+ if (!nvbb_dominated_by(pn, val->insn->bb))
+ nv_reference(ctx->pc, &i->src[j], propagate_phi(ctx->pc, i, j));
}
if (pn != p && pn->exit) {
- /* XXX: this branch should probably be eliminated */
ctx->pc->current_block = b->in[n ? 0 : 1];
- i2 = new_instruction(ctx->pc, NV_OP_BRA);
- i2->target = b;
- i2->is_terminator = 1;
+ ni = new_instruction(ctx->pc, NV_OP_BRA);
+ ni->target = b;
+ ni->is_terminator = 1;
}
}
- if (b->out[0] && b->out[0]->pass_seq < ctx->pc->pass_seq) {
- pass_generate_phi_movs(ctx, b->out[0]);
- }
+ for (j = 0; j < 2; ++j)
+ if (b->out[j] && b->out[j]->pass_seq < ctx->pc->pass_seq)
+ pass_generate_phi_movs_1(ctx, b->out[j]);
+
+ return 0;
+}
+
+/* Now everything should be in order and we can insert the MOVs. */
+static int
+pass_generate_phi_movs_2(struct nv_pc_pass *ctx, struct nv_basic_block *b)
+{
+ struct nv_instruction *i, *mov;
+ struct nv_value *val;
+ struct nv_basic_block *p;
+ int n, j;
+
+ b->pass_seq = ctx->pc->pass_seq;
+
+ for (n = 0; n < b->num_in; ++n) {
+ ctx->pc->current_block = p = b->in[n];
+
+ for (i = b->phi; i && i->opcode == NV_OP_PHI; i = i->next) {
+ for (j = 0; j < 4 && i->src[j]; ++j) {
+ if (bb_reachable_by(p, i->src[j]->value->insn->bb, b))
+ break;
+ }
+ if (j >= 4 || !i->src[j])
+ continue;
+ val = i->src[j]->value;
+
+ mov = new_instruction(ctx->pc, NV_OP_MOV);
+
+ /* TODO: insert instruction at correct position in the first place */
+ if (mov->prev && mov->prev->target)
+ nv_nvi_permute(mov->prev, mov);
+
+ mov->def[0] = new_value(ctx->pc, val->reg.file, val->reg.type);
+ mov->def[0]->insn = mov;
+ mov->src[0] = new_ref(ctx->pc, val);
- if (b->out[1] && b->out[1]->pass_seq < ctx->pc->pass_seq) {
- pass_generate_phi_movs(ctx, b->out[1]);
+ nv_reference(ctx->pc, &i->src[j], mov->def[0]);
+ }
}
+ for (j = 1; j >= 0; --j) /* different order for the sake of diversity */
+ if (b->out[j] && b->out[j]->pass_seq < ctx->pc->pass_seq)
+ pass_generate_phi_movs_2(ctx, b->out[j]);
+
return 0;
}
+/* For each operand of each PHI in b, generate a new value by inserting a MOV
+ * at the end of the block it is coming from and replace the operand with its
+ * result. This eliminates liveness conflicts and enables us to let values be
+ * copied to the right register if such a conflict exists nonetheless.
+ */
+static INLINE int
+pass_generate_phi_movs(struct nv_pc_pass *ctx, struct nv_basic_block *b)
+{
+ if (pass_generate_phi_movs_1(ctx, b))
+ return 1;
+
+ ++ctx->pc->pass_seq;
+ return pass_generate_phi_movs_2(ctx, b);
+}
+
static int
pass_join_values(struct nv_pc_pass *ctx, int iter)
{
return 0;
}
+/* Order the instructions so that live intervals can be expressed in numbers. */
static int
pass_order_instructions(struct nv_pc_pass *ctx, struct nv_basic_block *b)
{
int j;
struct nv_value *val;
- debug_printf("live_set of %p: ", b);
+ debug_printf("LIVE-INs of BB:%i: ", b->id);
for (j = 0; j < pc->num_values; ++j) {
if (!(b->live_set[j / 32] & (1 << (j % 32))))
{
if (!val->insn) /* don't add non-def values */
return;
- /* debug_printf("live[%p] <- %i\n", b, val->n); */
-
b->live_set[val->n / 32] |= 1 << (val->n % 32);
}
static INLINE void
live_set_rem(struct nv_basic_block *b, struct nv_value *val)
{
- /* if (val->insn)
- debug_printf("live[%p] -> %i\n", b, val->n); */
b->live_set[val->n / 32] &= ~(1 << (val->n % 32));
}
}
/* The live set of a block contains those values that are live immediately
- * before the beginning of the block.
+ * before the beginning of the block, so do a backwards scan.
*/
static int
pass_build_live_sets(struct nv_pc_pass *ctx, struct nv_basic_block *b)
struct nv_instruction *i;
int j, n, ret = 0;
+ debug_printf("pass_build_live_sets BB:%i\n", b->id);
+
+ if (b->pass_seq >= ctx->pc->pass_seq) {
+ debug_printf("already visited\n");
+ return 0;
+ }
+ b->pass_seq = ctx->pc->pass_seq;
+
/* slight hack for undecidedness: set phi = entry if it's undefined */
if (!b->phi)
b->phi = b->entry;
if (bb_reachable_by(b, i->src[j]->value->insn->bb, b->out[n])) {
live_set_add(b, i->src[j]->value);
- debug_printf("%p: live set + %i\n", b, i->src[j]->value->n);
+ debug_printf("BB:%i liveset + %i\n", b->id, i->src[j]->value->n);
} else {
live_set_rem(b, i->src[j]->value);
- debug_printf("%p: live set - %i\n", b, i->src[j]->value->n);
+ debug_printf("BB:%i liveset - %i\n", b->id, i->src[j]->value->n);
}
}
}
}
- if (b->pass_seq >= ctx->pc->pass_seq)
- return 0;
- b->pass_seq = ctx->pc->pass_seq;
-
- debug_printf("%s: visiting block %p\n", __FUNCTION__, b);
-
if (!b->entry)
return 0;
+
bb_live_set_print(ctx->pc, b);
for (i = b->exit; i; i = i->prev) {
if (b->out[1] && b->out[1]->pass_seq < ctx->pc->pass_seq)
pass_build_intervals(ctx, b->out[1]);
- debug_printf("built intervals for block %p\n", b);
-
return 0;
}