#include "util/register_allocate.h"
#include "v3d_compiler.h"
+/* Keeps track of conditional / partial writes in a block */
struct partial_update_state {
- struct qinst *insts[4];
- uint8_t channels;
+ /* Instruction doing a conditional or partial write */
+ struct qinst *inst;
+ /* Instruction that set the flags for the conditional write */
+ struct qinst *flags_inst;
};
static int
static void
vir_setup_use(struct v3d_compile *c, struct qblock *block, int ip,
- struct qreg src)
+ struct partial_update_state *partial_update_ht, struct qinst *inst,
+ struct qreg src, struct qinst *flags_inst)
{
int var = vir_reg_to_var(src);
if (var == -1)
* use of a variable without having completely
* defined that variable within the block.
*/
- if (!BITSET_TEST(block->def, var))
- BITSET_SET(block->use, var);
-}
-
-static struct partial_update_state *
-get_partial_update_state(struct hash_table *partial_update_ht,
- struct qinst *inst)
-{
- struct hash_entry *entry =
- _mesa_hash_table_search(partial_update_ht,
- &inst->dst.index);
- if (entry)
- return entry->data;
-
- struct partial_update_state *state =
- rzalloc(partial_update_ht, struct partial_update_state);
-
- _mesa_hash_table_insert(partial_update_ht, &inst->dst.index, state);
+ if (!BITSET_TEST(block->def, var)) {
+ /* If this use of var is conditional and the condition
+ * and flags match those of a previous instruction
+ * in the same block partially defining var then we
+ * consider var completely defined within the block.
+ */
+ if (BITSET_TEST(block->defout, var)) {
+ struct partial_update_state *state =
+ &partial_update_ht[var];
+ if (state->inst) {
+ if (vir_get_cond(inst) == vir_get_cond(state->inst) &&
+ flags_inst == state->flags_inst) {
+ return;
+ }
+ }
+ }
- return state;
+ BITSET_SET(block->use, var);
+ }
}
+/* The def[] bitset marks when an initialization in a
+ * block completely screens off previous updates of
+ * that variable.
+ */
static void
vir_setup_def(struct v3d_compile *c, struct qblock *block, int ip,
- struct hash_table *partial_update_ht, struct qinst *inst)
+ struct partial_update_state *partial_update, struct qinst *inst,
+ struct qinst *flags_inst)
{
if (inst->qpu.type != V3D_QPU_INSTR_TYPE_ALU)
return;
- /* The def[] bitset marks when an initialization in a
- * block completely screens off previous updates of
- * that variable.
- */
int var = vir_reg_to_var(inst->dst);
if (var == -1)
return;
return;
}
- /* Finally, look at the condition code and packing and mark it as a
- * def. We need to make sure that we understand sequences
- * instructions like:
- *
- * mov.zs t0, t1
- * mov.zc t0, t2
+ /* Keep track of conditional writes.
*
- * or:
+ * Notice that the dst's live range for a conditional or partial writes
+ * will get extended up the control flow to the top of the program until
+ * we find a full write, making register allocation more difficult, so
+ * we should try our best to keep track of these and figure out if a
+ * combination of them actually writes the entire register so we can
+ * stop that process early and reduce liveness.
*
- * mmov t0.8a, t1
- * mmov t0.8b, t2
- * mmov t0.8c, t3
- * mmov t0.8d, t4
- *
- * as defining the temp within the block, because otherwise dst's live
- * range will get extended up the control flow to the top of the
- * program.
+ * FIXME: Track partial updates via pack/unpack.
*/
- struct partial_update_state *state =
- get_partial_update_state(partial_update_ht, inst);
- uint8_t mask = 0xf; /* XXX vir_channels_written(inst); */
-
- if (inst->qpu.flags.ac == V3D_QPU_COND_NONE &&
- inst->qpu.flags.mc == V3D_QPU_COND_NONE) {
- state->channels |= mask;
- } else {
- for (int i = 0; i < 4; i++) {
- if (!(mask & (1 << i)))
- continue;
-
- /* XXXif (state->insts[i] &&
- state->insts[i]->cond ==
- qpu_cond_complement(inst->cond))
- state->channels |= 1 << i;
- else
- */
- state->insts[i] = inst;
- }
- }
-
- if (state->channels == 0xf)
- BITSET_SET(block->def, var);
-}
-
-static void
-sf_state_clear(struct hash_table *partial_update_ht)
-{
- hash_table_foreach(partial_update_ht, entry) {
- struct partial_update_state *state = entry->data;
-
- for (int i = 0; i < 4; i++) {
- if (state->insts[i] &&
- (state->insts[i]->qpu.flags.ac != V3D_QPU_COND_NONE ||
- state->insts[i]->qpu.flags.mc != V3D_QPU_COND_NONE))
- state->insts[i] = NULL;
- }
+ struct partial_update_state *state = &partial_update[var];
+ if (inst->qpu.flags.ac != V3D_QPU_COND_NONE ||
+ inst->qpu.flags.mc != V3D_QPU_COND_NONE) {
+ state->inst = inst;
+ state->flags_inst = flags_inst;
}
}
static void
vir_setup_def_use(struct v3d_compile *c)
{
- struct hash_table *partial_update_ht =
- _mesa_hash_table_create(c, _mesa_hash_int, _mesa_key_int_equal);
+ struct partial_update_state *partial_update =
+ rzalloc_array(c, struct partial_update_state, c->num_temps);
int ip = 0;
vir_for_each_block(block, c) {
block->start_ip = ip;
- _mesa_hash_table_clear(partial_update_ht, NULL);
+ memset(partial_update, 0,
+ sizeof(struct partial_update_state) * c->num_temps);
+
+ struct qinst *flags_inst = NULL;
vir_for_each_inst(inst, block) {
- for (int i = 0; i < vir_get_nsrc(inst); i++)
- vir_setup_use(c, block, ip, inst->src[i]);
+ for (int i = 0; i < vir_get_nsrc(inst); i++) {
+ vir_setup_use(c, block, ip, partial_update,
+ inst, inst->src[i], flags_inst);
+ }
- vir_setup_def(c, block, ip, partial_update_ht, inst);
+ vir_setup_def(c, block, ip, partial_update,
+ inst, flags_inst);
- if (false /* XXX inst->uf */)
- sf_state_clear(partial_update_ht);
+ if (inst->qpu.flags.apf != V3D_QPU_PF_NONE ||
+ inst->qpu.flags.mpf != V3D_QPU_PF_NONE) {
+ flags_inst = inst;
+ }
+
+ if (inst->qpu.flags.auf != V3D_QPU_UF_NONE ||
+ inst->qpu.flags.auf != V3D_QPU_UF_NONE) {
+ flags_inst = NULL;
+ }
/* Payload registers: r0/1/2 contain W, centroid W,
* and Z at program start. Register allocation will
block->end_ip = ip;
}
- _mesa_hash_table_destroy(partial_update_ht, NULL);
+ ralloc_free(partial_update);
}
static bool