agx: Adapt liveness analysis for SSA
Lifted from nir_liveness. Signed-off-by: Alyssa Rosenzweig <alyssa@collabora.com> Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/16268>
This commit is contained in:
parent
590df764d6
commit
606d9340f3
|
@ -51,34 +51,6 @@ agx_liveness_ins_update(BITSET_WORD *live, agx_instr *I)
|
|||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
liveness_block_update(agx_block *blk, unsigned words)
|
||||
{
|
||||
bool progress = false;
|
||||
|
||||
/* live_out[s] = sum { p in succ[s] } ( live_in[p] ) */
|
||||
agx_foreach_successor(blk, succ) {
|
||||
for (unsigned i = 0; i < words; ++i)
|
||||
blk->live_out[i] |= succ->live_in[i];
|
||||
}
|
||||
|
||||
/* live_in is live_out after iteration */
|
||||
BITSET_WORD *live = ralloc_array(blk, BITSET_WORD, words);
|
||||
memcpy(live, blk->live_out, words * sizeof(BITSET_WORD));
|
||||
|
||||
agx_foreach_instr_in_block_rev(blk, I)
|
||||
agx_liveness_ins_update(live, I);
|
||||
|
||||
/* To figure out progress, diff live_in */
|
||||
for (unsigned i = 0; i < words; ++i)
|
||||
progress |= (blk->live_in[i] != live[i]);
|
||||
|
||||
ralloc_free(blk->live_in);
|
||||
blk->live_in = live;
|
||||
|
||||
return progress;
|
||||
}
|
||||
|
||||
/* Globally, liveness analysis uses a fixed-point algorithm based on a
|
||||
* worklist. We initialize a work list with the exit block. We iterate the work
|
||||
* list to compute live_in from live_out for each block on the work list,
|
||||
|
@ -91,9 +63,8 @@ agx_compute_liveness(agx_context *ctx)
|
|||
if (ctx->has_liveness)
|
||||
return;
|
||||
|
||||
/* Set of agx_block */
|
||||
struct set *work_list = _mesa_set_create(NULL, _mesa_hash_pointer,
|
||||
_mesa_key_pointer_equal);
|
||||
u_worklist worklist;
|
||||
u_worklist_init(&worklist, ctx->num_blocks, NULL);
|
||||
|
||||
/* Free any previous liveness, and allocate */
|
||||
unsigned words = BITSET_WORDS(ctx->alloc);
|
||||
|
@ -105,34 +76,72 @@ agx_compute_liveness(agx_context *ctx)
|
|||
if (block->live_out)
|
||||
ralloc_free(block->live_out);
|
||||
|
||||
block->pass_flags = false;
|
||||
block->live_in = rzalloc_array(block, BITSET_WORD, words);
|
||||
block->live_out = rzalloc_array(block, BITSET_WORD, words);
|
||||
|
||||
agx_worklist_push_head(&worklist, block);
|
||||
}
|
||||
|
||||
/* Initialize the work list with the exit block */
|
||||
struct set_entry *cur = _mesa_set_add(work_list, agx_exit_block(ctx));
|
||||
|
||||
/* Iterate the work list */
|
||||
do {
|
||||
/* Pop off a block */
|
||||
agx_block *blk = (struct agx_block *) cur->key;
|
||||
_mesa_set_remove(work_list, cur);
|
||||
while(!u_worklist_is_empty(&worklist)) {
|
||||
/* Pop in reverse order since liveness is a backwards pass */
|
||||
agx_block *blk = agx_worklist_pop_head(&worklist);
|
||||
|
||||
/* Update its liveness information */
|
||||
bool progress = liveness_block_update(blk, words);
|
||||
memcpy(blk->live_in, blk->live_out, words * sizeof(BITSET_WORD));
|
||||
|
||||
/* If we made progress, we need to process the predecessors */
|
||||
if (progress) {
|
||||
agx_foreach_predecessor(blk, pred)
|
||||
_mesa_set_add(work_list, *pred);
|
||||
agx_foreach_instr_in_block_rev(blk, I) {
|
||||
/* Phi nodes are handled separately, so we skip them. As phi nodes are at
|
||||
* the beginning and we're iterating backwards, we stop as soon as we hit
|
||||
* a phi node.
|
||||
*/
|
||||
if (I->op == AGX_OPCODE_PHI)
|
||||
break;
|
||||
|
||||
agx_liveness_ins_update(blk->live_in, I);
|
||||
}
|
||||
|
||||
/* Use pass flags to communicate that we've visited this block */
|
||||
blk->pass_flags = true;
|
||||
} while((cur = _mesa_set_next_entry(work_list, NULL)) != NULL);
|
||||
/* Propagate the live in of the successor (blk) to the live out of
|
||||
* predecessors.
|
||||
*
|
||||
* Phi nodes are logically on the control flow edge and act in parallel. To
|
||||
* handle when propagating, we kill writes from phis and make live the
|
||||
* corresponding sources.
|
||||
*/
|
||||
agx_foreach_predecessor(blk, pred) {
|
||||
BITSET_WORD *live = ralloc_array(blk, BITSET_WORD, words);
|
||||
memcpy(live, blk->live_in, words * sizeof(BITSET_WORD));
|
||||
|
||||
_mesa_set_destroy(work_list, NULL);
|
||||
/* Kill write */
|
||||
agx_foreach_instr_in_block(blk, I) {
|
||||
if (I->op != AGX_OPCODE_PHI) break;
|
||||
|
||||
assert(I->dest[0].type == AGX_INDEX_NORMAL);
|
||||
BITSET_CLEAR(live, I->dest[0].value);
|
||||
}
|
||||
|
||||
/* Make live the corresponding source */
|
||||
agx_foreach_instr_in_block(blk, I) {
|
||||
if (I->op != AGX_OPCODE_PHI) break;
|
||||
|
||||
agx_index operand = I->src[agx_predecessor_index(blk, *pred)];
|
||||
assert(operand.type == AGX_INDEX_NORMAL);
|
||||
BITSET_SET(live, operand.value);
|
||||
}
|
||||
|
||||
bool progress = false;
|
||||
|
||||
for (unsigned i = 0; i < words; ++i) {
|
||||
progress |= live[i] & ~((*pred)->live_out[i]);
|
||||
(*pred)->live_out[i] |= live[i];
|
||||
}
|
||||
|
||||
if (progress)
|
||||
agx_worklist_push_tail(&worklist, *pred);
|
||||
}
|
||||
}
|
||||
|
||||
u_worklist_fini(&worklist);
|
||||
|
||||
ctx->has_liveness = true;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue