17ec681f3Smrg/*
27ec681f3Smrg * Copyright (C) 2021 Alyssa Rosenzweig
37ec681f3Smrg * Copyright (C) 2019-2020 Collabora, Ltd.
47ec681f3Smrg *
57ec681f3Smrg * Permission is hereby granted, free of charge, to any person obtaining a
67ec681f3Smrg * copy of this software and associated documentation files (the "Software"),
77ec681f3Smrg * to deal in the Software without restriction, including without limitation
87ec681f3Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
97ec681f3Smrg * and/or sell copies of the Software, and to permit persons to whom the
107ec681f3Smrg * Software is furnished to do so, subject to the following conditions:
117ec681f3Smrg *
127ec681f3Smrg * The above copyright notice and this permission notice (including the next
137ec681f3Smrg * paragraph) shall be included in all copies or substantial portions of the
147ec681f3Smrg * Software.
157ec681f3Smrg *
167ec681f3Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
177ec681f3Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
187ec681f3Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
197ec681f3Smrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
207ec681f3Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
217ec681f3Smrg * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
227ec681f3Smrg * SOFTWARE.
237ec681f3Smrg */
247ec681f3Smrg
257ec681f3Smrg#include "agx_compiler.h"
267ec681f3Smrg#include "util/u_memory.h"
277ec681f3Smrg#include "util/list.h"
287ec681f3Smrg#include "util/set.h"
297ec681f3Smrg
307ec681f3Smrg/* Liveness analysis is a backwards-may dataflow analysis pass. Within a block,
317ec681f3Smrg * we compute live_out from live_in. The intrablock pass is linear-time. It
327ec681f3Smrg * returns whether progress was made. */
337ec681f3Smrg
347ec681f3Smrg/* live_in[s] = GEN[s] + (live_out[s] - KILL[s]) */
357ec681f3Smrg
367ec681f3Smrgvoid
377ec681f3Smrgagx_liveness_ins_update(BITSET_WORD *live, agx_instr *I)
387ec681f3Smrg{
397ec681f3Smrg   agx_foreach_dest(I, d) {
407ec681f3Smrg      if (I->dest[d].type == AGX_INDEX_NORMAL)
417ec681f3Smrg         BITSET_CLEAR(live, I->dest[d].value);
427ec681f3Smrg   }
437ec681f3Smrg
447ec681f3Smrg   agx_foreach_src(I, s) {
457ec681f3Smrg      if (I->src[s].type == AGX_INDEX_NORMAL) {
467ec681f3Smrg         /* If the source is not live after this instruction, but becomes live
477ec681f3Smrg          * at this instruction, this is the use that kills the source */
487ec681f3Smrg         I->src[s].kill = !BITSET_TEST(live, I->src[s].value);
497ec681f3Smrg         BITSET_SET(live, I->src[s].value);
507ec681f3Smrg      }
517ec681f3Smrg   }
527ec681f3Smrg}
537ec681f3Smrg
547ec681f3Smrgstatic bool
557ec681f3Smrgliveness_block_update(agx_block *blk, unsigned words)
567ec681f3Smrg{
577ec681f3Smrg   bool progress = false;
587ec681f3Smrg
597ec681f3Smrg   /* live_out[s] = sum { p in succ[s] } ( live_in[p] ) */
607ec681f3Smrg   agx_foreach_successor(blk, succ) {
617ec681f3Smrg      for (unsigned i = 0; i < words; ++i)
627ec681f3Smrg         blk->live_out[i] |= succ->live_in[i];
637ec681f3Smrg   }
647ec681f3Smrg
657ec681f3Smrg   /* live_in is live_out after iteration */
667ec681f3Smrg   BITSET_WORD *live = ralloc_array(blk, BITSET_WORD, words);
677ec681f3Smrg   memcpy(live, blk->live_out, words * sizeof(BITSET_WORD));
687ec681f3Smrg
697ec681f3Smrg   agx_foreach_instr_in_block_rev(blk, I)
707ec681f3Smrg      agx_liveness_ins_update(live, I);
717ec681f3Smrg
727ec681f3Smrg   /* To figure out progress, diff live_in */
737ec681f3Smrg   for (unsigned i = 0; i < words; ++i)
747ec681f3Smrg      progress |= (blk->live_in[i] != live[i]);
757ec681f3Smrg
767ec681f3Smrg   ralloc_free(blk->live_in);
777ec681f3Smrg   blk->live_in = live;
787ec681f3Smrg
797ec681f3Smrg   return progress;
807ec681f3Smrg}
817ec681f3Smrg
827ec681f3Smrg/* Globally, liveness analysis uses a fixed-point algorithm based on a
837ec681f3Smrg * worklist. We initialize a work list with the exit block. We iterate the work
847ec681f3Smrg * list to compute live_in from live_out for each block on the work list,
857ec681f3Smrg * adding the predecessors of the block to the work list if we made progress.
867ec681f3Smrg */
877ec681f3Smrg
887ec681f3Smrgvoid
897ec681f3Smrgagx_compute_liveness(agx_context *ctx)
907ec681f3Smrg{
917ec681f3Smrg   if (ctx->has_liveness)
927ec681f3Smrg      return;
937ec681f3Smrg
947ec681f3Smrg   /* Set of agx_block */
957ec681f3Smrg   struct set *work_list = _mesa_set_create(NULL, _mesa_hash_pointer,
967ec681f3Smrg                                                  _mesa_key_pointer_equal);
977ec681f3Smrg
987ec681f3Smrg   /* Free any previous liveness, and allocate */
997ec681f3Smrg   unsigned words = BITSET_WORDS(ctx->alloc);
1007ec681f3Smrg
1017ec681f3Smrg   agx_foreach_block(ctx, block) {
1027ec681f3Smrg      if (block->live_in)
1037ec681f3Smrg         ralloc_free(block->live_in);
1047ec681f3Smrg
1057ec681f3Smrg      if (block->live_out)
1067ec681f3Smrg         ralloc_free(block->live_out);
1077ec681f3Smrg
1087ec681f3Smrg      block->pass_flags = false;
1097ec681f3Smrg      block->live_in = rzalloc_array(block, BITSET_WORD, words);
1107ec681f3Smrg      block->live_out = rzalloc_array(block, BITSET_WORD, words);
1117ec681f3Smrg   }
1127ec681f3Smrg
1137ec681f3Smrg   /* Initialize the work list with the exit block */
1147ec681f3Smrg   struct set_entry *cur = _mesa_set_add(work_list, agx_exit_block(ctx));
1157ec681f3Smrg
1167ec681f3Smrg   /* Iterate the work list */
1177ec681f3Smrg   do {
1187ec681f3Smrg      /* Pop off a block */
1197ec681f3Smrg      agx_block *blk = (struct agx_block *) cur->key;
1207ec681f3Smrg      _mesa_set_remove(work_list, cur);
1217ec681f3Smrg
1227ec681f3Smrg      /* Update its liveness information */
1237ec681f3Smrg      bool progress = liveness_block_update(blk, words);
1247ec681f3Smrg
1257ec681f3Smrg      /* If we made progress, we need to process the predecessors */
1267ec681f3Smrg
1277ec681f3Smrg      if (progress || !blk->pass_flags) {
1287ec681f3Smrg         agx_foreach_predecessor(blk, pred)
1297ec681f3Smrg            _mesa_set_add(work_list, pred);
1307ec681f3Smrg      }
1317ec681f3Smrg
1327ec681f3Smrg      /* Use pass flags to communicate that we've visited this block */
1337ec681f3Smrg      blk->pass_flags = true;
1347ec681f3Smrg   } while((cur = _mesa_set_next_entry(work_list, NULL)) != NULL);
1357ec681f3Smrg
1367ec681f3Smrg   _mesa_set_destroy(work_list, NULL);
1377ec681f3Smrg
1387ec681f3Smrg   ctx->has_liveness = true;
1397ec681f3Smrg}
140