pan/bi: Add SSA-based liveness pass
authorAlyssa Rosenzweig <alyssa@collabora.com>
Mon, 25 Jul 2022 15:25:24 +0000 (11:25 -0400)
committerMarge Bot <emma+marge@anholt.net>
Fri, 2 Sep 2022 16:03:23 +0000 (16:03 +0000)
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>

src/panfrost/bifrost/bi_liveness.c
src/panfrost/bifrost/compiler.h

index 4d6c97a..5d237a7 100644 (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);
+}
index 9ca858e..e4b5759 100644 (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);