IR3_REG_SSA = 0x4000, /* 'instr' is ptr to assigning instr */
IR3_REG_ARRAY = 0x8000,
+ IR3_REG_DEST = 0x10000,
} flags;
/* used for cat5 instructions, but also for internal/IR level
} array;
};
+
+ /* For IR3_REG_DEST, pointer back to the instruction containing this
+ * register.
+ */
+ struct ir3_instruction *instr;
+
/* For IR3_REG_SSA, src registers contain ptr back to assigning
* instruction.
*
* array access (although the net effect is the same, it points
* back to a previous instruction that we depend on).
*/
- struct ir3_instruction *instr;
+ struct ir3_register *def;
};
/*
* last read. But all the writes that happen before that have
* something depending on them
*/
- struct ir3_instruction *last_write;
+ struct ir3_register *last_write;
/* extra stuff used in RA pass: */
unsigned base; /* base vreg name */
/* TODO better name */
static inline struct ir3_instruction *ssa(struct ir3_register *reg)
{
- if (reg->flags & (IR3_REG_SSA | IR3_REG_ARRAY)) {
- return reg->instr;
- }
+ if ((reg->flags & (IR3_REG_SSA | IR3_REG_ARRAY)) && reg->def)
+ return reg->def->instr;
return NULL;
}
if ((__instr)->regs_count) \
for (struct ir3_register *__srcreg = (void *)~0; __srcreg; __srcreg = NULL) \
for (unsigned __cnt = (__instr)->regs_count - 1, __n = 0; __n < __cnt; __n++) \
- if ((__srcreg = (__instr)->regs[__n + 1]))
+ if ((__srcreg = (__instr)->regs[__n + 1]) && !(__srcreg->flags & IR3_REG_DEST))
/* iterator for an instructions's sources (reg): */
#define foreach_src(__srcreg, __instr) \
if (n >= instr->regs_count)
return &instr->deps[n - instr->regs_count];
if (ssa(instr->regs[n]))
- return &instr->regs[n]->instr;
+ return &instr->regs[n]->def->instr;
return NULL;
}
struct ir3_register *reg;
if (src->regs[0]->flags & IR3_REG_HALF)
flags |= IR3_REG_HALF;
- reg = ir3_reg_create(instr, 0, IR3_REG_SSA | flags);
- reg->instr = src;
+ reg = ir3_reg_create(instr, INVALID_REG, IR3_REG_SSA | flags);
+ reg->def = src->regs[0];
reg->wrmask = src->regs[0]->wrmask;
return reg;
}
static inline struct ir3_register * __ssa_dst(struct ir3_instruction *instr)
{
- struct ir3_register *reg = ir3_reg_create(instr, 0, 0);
- reg->flags |= IR3_REG_SSA;
+ struct ir3_register *reg = ir3_reg_create(instr, INVALID_REG, 0);
+ reg->flags |= IR3_REG_SSA | IR3_REG_DEST;
+ reg->instr = instr;
return reg;
}
if (cond->opc == OPC_ABSNEG_S &&
cond->flags == 0 &&
(cond->regs[1]->flags & (IR3_REG_SNEG | IR3_REG_SABS)) == IR3_REG_SNEG) {
- cond = cond->regs[1]->instr;
+ cond = cond->regs[1]->def->instr;
}
compile_assert(ctx, bs[1] == bs[2]);
if (get_block(ctx, nsrc->pred) == pred) {
if (nsrc->src.ssa->parent_instr->type == nir_instr_type_ssa_undef) {
/* Create an ir3 undef */
- ir3_reg_create(phi, INVALID_REG, phi->regs[0]->flags);
+ ir3_reg_create(phi, INVALID_REG, phi->regs[0]->flags & ~IR3_REG_DEST);
} else {
struct ir3_instruction *src = ir3_get_src(ctx, &nsrc->src)[0];
__ssa_src(phi, src, 0);
__ssa_dst(mov)->flags |= flags;
src = ir3_reg_create(mov, 0, IR3_REG_ARRAY |
COND(address, IR3_REG_RELATIV) | flags);
- src->instr = arr->last_write;
+ src->def = arr->last_write;
src->size = arr->length;
src->array.id = arr->id;
src->array.offset = n;
src->barrier_conflict |= IR3_BARRIER_ARRAY_R | IR3_BARRIER_ARRAY_W;
dst->flags |= IR3_REG_ARRAY;
- dst->instr = arr->last_write;
+ dst->def = arr->last_write;
dst->size = arr->length;
dst->array.id = arr->id;
dst->array.offset = n;
- arr->last_write = src;
+ arr->last_write = dst;
array_insert(block, block->keeps, src);
}
mov->barrier_class = IR3_BARRIER_ARRAY_W;
mov->barrier_conflict = IR3_BARRIER_ARRAY_R | IR3_BARRIER_ARRAY_W;
- dst = ir3_reg_create(mov, 0, IR3_REG_ARRAY |
+ dst = ir3_reg_create(mov, 0, IR3_REG_DEST | IR3_REG_SSA | IR3_REG_ARRAY |
flags |
COND(address, IR3_REG_RELATIV));
- dst->instr = arr->last_write;
+ dst->def = arr->last_write;
+ dst->instr = mov;
dst->size = arr->length;
dst->array.id = arr->id;
dst->array.offset = n;
- ir3_reg_create(mov, 0, IR3_REG_SSA | flags)->instr = src;
+ ir3_reg_create(mov, 0, IR3_REG_SSA | flags)->def = src->regs[0];
if (address)
ir3_instr_set_address(mov, address);
- arr->last_write = mov;
+ arr->last_write = dst;
/* the array store may only matter to something in an earlier
* block (ie. loops), but since arrays are not in SSA, depth
reg->array = src_reg->array;
}
reg->flags = new_flags;
- reg->instr = ssa(src_reg);
+ reg->def = src_reg->def;
instr->barrier_class |= src->barrier_class;
instr->barrier_conflict |= src->barrier_conflict;
unuse(src);
- reg->instr->use_count++;
+ reg->def->instr->use_count++;
return true;
}
* address registers:
*/
if ((src_reg->flags & IR3_REG_RELATIV) &&
- conflicts(instr->address, reg->instr->address))
+ conflicts(instr->address, reg->def->instr->address))
return false;
/* This seems to be a hw bug, or something where the timings
instr->regs[n+1] = src_reg;
if (src_reg->flags & IR3_REG_RELATIV)
- ir3_instr_set_address(instr, reg->instr->address);
+ ir3_instr_set_address(instr, reg->def->instr->address);
return true;
}
static bool
has_conflicting_write(struct ir3_instruction *src,
struct ir3_instruction *use,
- struct ir3_instruction **def,
+ struct ir3_register **def,
unsigned id, int offset)
{
assert(src->block == use->block);
return true;
if (last_write)
- *def = instr;
+ *def = dst;
last_write = false;
}
if (is_meta(use))
continue;
- struct ir3_instruction *def = src->instr;
+ struct ir3_register *def = src->def;
if (has_conflicting_write(mov, use, &def, src->array.id, offset))
continue;
/* If we're sinking the array read past any writes, make
* sure to update it to point to the new previous write:
*/
- use->regs[n + 1]->instr = def;
+ use->regs[n + 1]->def = def;
removed = true;
}
foreach_src_n (src, n, assigner) {
unsigned d;
- if (!src->instr)
+ if (!src->def)
continue;
- d = delay_calc_srcn(block, src->instr, consumer, srcn, soft, pred);
+ d = delay_calc_srcn(block, src->def->instr, consumer, srcn, soft, pred);
/* A (rptN) instruction executes in consecutive cycles so
* it's outputs are written in successive cycles. And
* for src registers. There is exactly one case, bary.f,
* which has a vecN (collect) src that is not (r)'d.
*/
- if ((assigner->opc == OPC_META_SPLIT) && src->instr->repeat) {
+ if ((assigner->opc == OPC_META_SPLIT) && src->def->instr->repeat) {
/* (rptN) assigner case: */
- d -= MIN2(d, src->instr->repeat - assigner->split.off);
+ d -= MIN2(d, src->def->instr->repeat - assigner->split.off);
} else if ((assigner->opc == OPC_META_COLLECT) && consumer->repeat &&
(consumer->regs[srcn]->flags & IR3_REG_R)) {
d -= MIN2(d, n);
if ((src->flags & IR3_REG_RELATIV) && !(src->flags & IR3_REG_CONST)) {
d = delay_calc_array(block, src->array.id, instr, i+1, soft, pred, 6);
- } else if (src->instr) {
- d = delay_calc_srcn(block, src->instr, instr, i+1, soft, pred);
+ } else if (src->def) {
+ d = delay_calc_srcn(block, src->def->instr, instr, i+1, soft, pred);
}
delay = MAX2(delay, d);
struct ir3_instruction *mov = ir3_MOV(src->block, src,
(collect->regs[idx+1]->flags & IR3_REG_HALF) ? TYPE_U16 : TYPE_U32);
- collect->regs[idx+1]->instr = mov;
+ collect->regs[idx+1]->def = mov->regs[0];
/* if collect and src are in the same block, move the inserted mov
* to just before the collect to avoid a use-before-def. Otherwise
foreach_instr_safe (instr, &block->instr_list) {
foreach_src (reg, instr) {
- if (!reg->instr)
+ if (!reg->def)
continue;
- if (is_self_mov(reg->instr)) {
- list_delinit(®->instr->node);
- reg->instr = reg->instr->regs[1]->instr;
+ if (is_self_mov(reg->def->instr)) {
+ list_delinit(®->def->instr->node);
+ reg->def = reg->def->instr->regs[1]->def;
}
}
for (unsigned i = 0; i < instr->deps_count; i++) {
if (instr->deps[i] && is_self_mov(instr->deps[i])) {
list_delinit(&instr->deps[i]->node);
- instr->deps[i] = instr->deps[i]->regs[1]->instr;
+ instr->deps[i] = instr->deps[i]->regs[1]->def->instr;
}
}
}
* Note for array writes from another block, we aren't really
* sure who wrote it so skip trying to show this
*/
- if (reg->instr && (reg->instr->block == instr->block)) {
+ if (reg->def && (reg->def->instr->block == instr->block)) {
printf(SYN_ARRAY(", "));
- printf(SYN_SSA("ssa_%u"), reg->instr->serialno);
+ printf(SYN_SSA("ssa_%u"), reg->def->instr->serialno);
}
printf(SYN_ARRAY("]"));
} else if (reg->flags & IR3_REG_SSA) {
- /* For dst regs, reg->instr will be NULL: */
- printf(SYN_SSA("ssa_%u"), reg->instr ? reg->instr->serialno : instr->serialno);
+ /* For dst regs, reg->def will be NULL: */
+ printf(SYN_SSA("ssa_%u"), (reg->flags & IR3_REG_DEST) ? instr->serialno : reg->def->instr->serialno);
} else if (reg->flags & IR3_REG_RELATIV) {
if (reg->flags & IR3_REG_CONST)
printf(SYN_CONST("c<a0.x + %d>"), reg->array.offset);
printf(" %sp0.%c ("SYN_SSA("ssa_%u")"),",
instr->cat0.inv1 ? "!" : "",
"xyzw"[instr->cat0.comp1 & 0x3],
- instr->regs[1]->instr->serialno);
+ instr->regs[1]->def->instr->serialno);
}
if (brinfo[instr->cat0.brtype].nsrc >= 2) {
printf(" %sp0.%c ("SYN_SSA("ssa_%u")"),",
instr->cat0.inv2 ? "!" : "",
"xyzw"[instr->cat0.comp2 & 0x3],
- instr->regs[2]->instr->serialno);
+ instr->regs[2]->def->instr->serialno);
}
}
printf(" target=block%u", block_id(instr->cat0.target));
*/
foreach_src_n (src, n, instr) {
struct ir3_instruction *dd;
- if (!src->instr)
+ if (!src->def)
continue;
- dd = get_definer(ctx, src->instr, &dsz, &doff);
+ dd = get_definer(ctx, src->def->instr, &dsz, &doff);
if ((!d) || instr_before(dd, d)) {
d = dd;
struct ir3_instruction *dd;
int dsz, doff;
- dd = get_definer(ctx, d->regs[1]->instr, &dsz, &doff);
+ dd = get_definer(ctx, d->regs[1]->def->instr, &dsz, &doff);
/* by definition, should come before: */
ra_assert(ctx, instr_before(dd, d));
*/
if (ctx->scalar_pass) {
if (instr->opc == OPC_META_SPLIT) {
- instr->name = instr->regs[1]->instr->name + instr->split.off;
+ instr->name = instr->regs[1]->def->instr->name + instr->split.off;
continue;
}
if (instr->opc == OPC_META_COLLECT) {
- instr->name = instr->regs[1]->instr->name;
+ instr->name = instr->regs[1]->def->instr->name;
continue;
}
}
struct ir3_array *arr = ir3_lookup_array(ctx->ir, src->array.id);
src_n = arr->base + src->array.offset;
} else {
- src_n = scalar_name(ctx, src->instr, 0);
+ src_n = scalar_name(ctx, src->def->instr, 0);
}
unsigned reg = ra_get_node_reg(ctx->g, src_n);
}
foreach_src_n (reg, n, instr) {
- struct ir3_instruction *src = reg->instr;
+ struct ir3_instruction *src = reg->def ? reg->def->instr : NULL;
if (src && should_assign(ctx, instr))
reg_assign(ctx, src->regs[0], src);
- /* Note: reg->instr could be null for IR3_REG_ARRAY */
+ /* Note: reg->def could be null for IR3_REG_ARRAY */
if (((reg->flags & IR3_REG_ARRAY) && ctx->scalar_pass) ||
(src && should_assign(ctx, src))) {
reg_assign(ctx, instr->regs[n+1], src);
precolor(ctx, instr);
foreach_src (src, instr) {
- if (!src->instr)
+ if (!src->def)
continue;
- precolor(ctx, src->instr);
+ precolor(ctx, src->def->instr);
}
}
}
if (ctx->scalar_pass) {
if (instr->opc == OPC_META_SPLIT) {
debug_assert(n == 0); /* split results in a scalar */
- struct ir3_instruction *src = instr->regs[1]->instr;
+ struct ir3_instruction *src = instr->regs[1]->def->instr;
return scalar_name(ctx, src, instr->split.off);
} else if (instr->opc == OPC_META_COLLECT) {
debug_assert(n < (instr->regs_count + 1));
- struct ir3_instruction *src = instr->regs[n + 1]->instr;
+ struct ir3_instruction *src = instr->regs[n + 1]->def->instr;
return scalar_name(ctx, src, 0);
}
} else {
__ra_itr_push(ctx, arr->base + reg->array.offset);
debug_assert(reg->array.offset < arr->length);
}
- } else {
- foreach_name_n (name, i, ctx, reg->instr) {
+ } else if (reg->def) {
+ foreach_name_n (name, i, ctx, reg->def->instr) {
/* split takes a src w/ wrmask potentially greater
* than 0x1, but it really only cares about a single
* component. This shows up in splits coming out of
src = collect;
}
- reg->instr = src;
+ reg->def = src->regs[0];
reg->flags |= IR3_REG_SSA;
}
regfile[regn(instr->regs[0]) + i] = split;
}
} else {
+ instr->regs[0]->instr = instr;
regfile[regn(instr->regs[0])] = instr;
}
}