pan/bi: Add SSA-based liveness pass

Adapted from NIR's liveness analysis. This is different from our non-SSA
liveness pass for a few reasons:

1. It must handle phi nodes. This implies significant changes to the worklist
   algorithm.
2. It only handles SSA. It doesn't need funny labelling schemes for
   handling nir_registers in parallel with SSA defs.
3. It is scalar-only. The vector liveness information isn't interesting when
   vectors are handled via COLLECT and SPLIT. This means it uses a bitset (uses
   8x less memory to store livenss information, should be easier on the caches
   too).

Eventually, this will become our only pre-RA liveness pass. For now, both passes
are maintained in parallel: the SSA pass used before out-of-SSA, the non-SSA
pass used after out-of-SSA and before RA, and the post-RA pass used after RA.

Signed-off-by: Alyssa Rosenzweig <alyssa@collabora.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/17794>
This commit is contained in:
Alyssa Rosenzweig 2022-07-25 11:25:24 -04:00 committed by Marge Bot
parent 0a83748c54
commit 6c5ab777ee
2 changed files with 106 additions and 0 deletions

View file

@ -1,6 +1,7 @@
/*
* Copyright (C) 2020 Collabora, Ltd.
* Copyright (C) 2018-2019 Alyssa Rosenzweig <alyssa@rosenzweig.io>
* Copyright © 2014 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
@ -124,3 +125,101 @@ bi_compute_liveness(bi_context *ctx)
u_worklist_fini(&worklist);
}
void
bi_liveness_ins_update_ssa(BITSET_WORD *live, const bi_instr *I)
{
bi_foreach_dest(I, d) {
assert(I->dest[d].type == BI_INDEX_NORMAL);
BITSET_CLEAR(live, I->dest[d].value);
}
bi_foreach_src(I, s) {
if (I->src[s].type == BI_INDEX_NORMAL)
BITSET_SET(live, I->src[s].value);
}
}
void
bi_compute_liveness_ssa(bi_context *ctx)
{
u_worklist worklist;
u_worklist_init(&worklist, ctx->num_blocks, NULL);
/* Free any previous liveness, and allocate */
unsigned words = BITSET_WORDS(ctx->ssa_alloc);
bi_foreach_block(ctx, block) {
if (block->ssa_live_in)
ralloc_free(block->ssa_live_in);
if (block->ssa_live_out)
ralloc_free(block->ssa_live_out);
block->ssa_live_in = rzalloc_array(block, BITSET_WORD, words);
block->ssa_live_out = rzalloc_array(block, BITSET_WORD, words);
bi_worklist_push_head(&worklist, block);
}
/* Iterate the work list */
while(!u_worklist_is_empty(&worklist)) {
/* Pop in reverse order since liveness is a backwards pass */
bi_block *blk = bi_worklist_pop_head(&worklist);
/* Update its liveness information */
memcpy(blk->ssa_live_in, blk->ssa_live_out, words * sizeof(BITSET_WORD));
bi_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 == BI_OPCODE_PHI)
break;
bi_liveness_ins_update_ssa(blk->ssa_live_in, I);
}
/* 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.
*/
bi_foreach_predecessor(blk, pred) {
BITSET_WORD *live = ralloc_array(blk, BITSET_WORD, words);
memcpy(live, blk->ssa_live_in, words * sizeof(BITSET_WORD));
/* Kill write */
bi_foreach_instr_in_block(blk, I) {
if (I->op != BI_OPCODE_PHI) break;
assert(I->dest[0].type == BI_INDEX_NORMAL);
BITSET_CLEAR(live, I->dest[0].value);
}
/* Make live the corresponding source */
bi_foreach_instr_in_block(blk, I) {
if (I->op != BI_OPCODE_PHI) break;
bi_index operand = I->src[bi_predecessor_index(blk, *pred)];
if (operand.type == BI_INDEX_NORMAL)
BITSET_SET(live, operand.value);
}
BITSET_WORD progress = 0;
for (unsigned i = 0; i < words; ++i) {
progress |= live[i] & ~((*pred)->ssa_live_out[i]);
(*pred)->ssa_live_out[i] |= live[i];
}
if (progress != 0)
bi_worklist_push_tail(&worklist, *pred);
}
}
u_worklist_fini(&worklist);
}

View file

@ -706,6 +706,10 @@ typedef struct bi_block {
uint8_t *live_in;
uint8_t *live_out;
/* Scalar liveness indexed by SSA index */
BITSET_WORD *ssa_live_in;
BITSET_WORD *ssa_live_out;
/* If true, uses clauses; if false, uses instructions */
bool scheduled;
struct list_head clauses; /* list of bi_clause */
@ -1180,6 +1184,9 @@ bool bi_opt_constant_fold(bi_context *ctx);
void bi_compute_liveness(bi_context *ctx);
void bi_liveness_ins_update(uint8_t *live, bi_instr *ins, unsigned max);
void bi_compute_liveness_ssa(bi_context *ctx);
void bi_liveness_ins_update_ssa(BITSET_WORD *live, const bi_instr *ins);
void bi_postra_liveness(bi_context *ctx);
uint64_t MUST_CHECK bi_postra_liveness_ins(uint64_t live, bi_instr *ins);