10#include "IRAdaptor.hpp"
11#include "tpde/ValLocalIdx.hpp"
12#include "tpde/base.hpp"
13#include "util/SmallBitSet.hpp"
14#include "util/SmallVector.hpp"
18template <IRAdaptor Adaptor>
21 using IRValueRef =
typename Adaptor::IRValueRef;
22 using IRInstRef =
typename Adaptor::IRInstRef;
23 using IRBlockRef =
typename Adaptor::IRBlockRef;
24 using IRFuncRef =
typename Adaptor::IRFuncRef;
26 static constexpr IRBlockRef INVALID_BLOCK_REF = Adaptor::INVALID_BLOCK_REF;
29 static constexpr size_t SMALL_BLOCK_NUM = 64;
30 static constexpr size_t SMALL_VALUE_NUM = 128;
36 enum class BlockIndex : u32 {
38 static constexpr BlockIndex INVALID_BLOCK_IDX =
static_cast<BlockIndex
>(~0u);
41 util::SmallVector<IRBlockRef, SMALL_BLOCK_NUM> block_layout = {};
45 util::SmallVector<u32, SMALL_BLOCK_NUM> block_loop_map = {};
51 BlockIndex begin = INVALID_BLOCK_IDX, end = INVALID_BLOCK_IDX;
57 u32 definitions = 0, definitions_in_childs = 0;
60 util::SmallVector<Loop, 16> loops = {};
65 BlockIndex first, last;
67 u32 lowest_common_loop;
79 util::SmallVector<LivenessInfo, SMALL_VALUE_NUM> liveness = {};
84 u16 liveness_epoch = 0;
85 u32 liveness_max_value;
89 explicit Analyzer(Adaptor *adaptor) : adaptor(adaptor) {}
93 void switch_func(IRFuncRef func);
95 IRBlockRef block_ref(
const BlockIndex idx)
const noexcept {
96 assert(
static_cast<u32
>(idx) <= block_layout.size());
97 if (
static_cast<u32
>(idx) == block_layout.size()) {
100 return INVALID_BLOCK_REF;
102 return block_layout[
static_cast<u32
>(idx)];
105 BlockIndex block_idx(IRBlockRef block_ref)
const noexcept {
106 return static_cast<BlockIndex
>(adaptor->block_info(block_ref));
109 const LivenessInfo &liveness_info(
const ValLocalIdx val_idx)
const noexcept {
110 assert(
static_cast<u32
>(val_idx) < liveness.size());
111 assert(liveness[
static_cast<u32
>(val_idx)].epoch == liveness_epoch &&
112 "access to liveness of ignored value");
113 return liveness[
static_cast<u32
>(val_idx)];
116 u32 block_loop_idx(
const BlockIndex idx)
const noexcept {
117 return block_loop_map[
static_cast<u32
>(idx)];
120 const Loop &loop_from_idx(
const u32 idx)
const noexcept {
return loops[idx]; }
122 bool block_has_multiple_incoming(
const BlockIndex idx)
const noexcept {
123 return block_has_multiple_incoming(block_ref(idx));
126 bool block_has_multiple_incoming(IRBlockRef block_ref)
const noexcept {
127 return (adaptor->block_info2(block_ref) & 0b11) == 2;
130 bool block_has_phis(BlockIndex idx)
const noexcept {
131 return block_has_phis(block_ref(idx));
134 bool block_has_phis(IRBlockRef block_ref)
const noexcept {
135 return (adaptor->block_info2(block_ref) & 0b1'0000) != 0;
138 void print_rpo(std::ostream &os)
const;
139 void print_block_layout(std::ostream &os)
const;
140 void print_loops(std::ostream &os)
const;
141 void print_liveness(std::ostream &os)
const;
145 LivenessInfo &liveness_maybe(
const IRValueRef val)
noexcept;
147 void build_block_layout();
149 void build_loop_tree_and_block_layout(
150 const util::SmallVector<IRBlockRef, SMALL_BLOCK_NUM> &block_rpo,
151 const util::SmallVector<u32, SMALL_BLOCK_NUM> &loop_parent,
152 const util::SmallBitSet<256> &loop_heads);
155 void build_rpo_block_order(
156 util::SmallVector<IRBlockRef, SMALL_BLOCK_NUM> &out)
const noexcept;
161 const util::SmallVector<IRBlockRef, SMALL_BLOCK_NUM> &block_rpo,
162 util::SmallVector<u32, SMALL_BLOCK_NUM> &loop_parent,
163 util::SmallBitSet<256> &loop_heads)
const noexcept;
165 void compute_liveness() noexcept;
168template <IRAdaptor Adaptor>
169void Analyzer<Adaptor>::switch_func([[maybe_unused]] IRFuncRef func) {
170 build_block_layout();
174template <IRAdaptor Adaptor>
175void Analyzer<Adaptor>::print_rpo(std::ostream &os)
const {
177 util::SmallVector<std::tuple<IRBlockRef, u32, u32>, SMALL_BLOCK_NUM> data;
178 for (IRBlockRef cur : adaptor->cur_blocks()) {
179 data.emplace_back(cur, adaptor->block_info(cur), adaptor->block_info2(cur));
182 util::SmallVector<IRBlockRef, SMALL_BLOCK_NUM> rpo;
183 build_rpo_block_order(rpo);
184 for (u32 i = 0; i < rpo.size(); ++i) {
185 os << std::format(
" {}: {}\n", i, adaptor->block_fmt_ref(rpo[i]));
188 for (
const auto &[cur, val1, val2] : data) {
189 adaptor->block_set_info(cur, val1);
190 adaptor->block_set_info2(cur, val2);
194template <IRAdaptor Adaptor>
195void Analyzer<Adaptor>::print_block_layout(std::ostream &os)
const {
196 for (u32 i = 0; i < block_layout.size(); ++i) {
197 os << std::format(
" {}: {}\n", i, adaptor->block_fmt_ref(block_layout[i]));
201template <IRAdaptor Adaptor>
202void Analyzer<Adaptor>::print_loops(std::ostream &os)
const {
203 for (u32 i = 0; i < loops.size(); ++i) {
204 const auto &loop = loops[i];
205 os << std::format(
" {}: level {}, parent {}, {}->{}\n",
209 static_cast<u32
>(loop.begin),
210 static_cast<u32
>(loop.end));
214template <IRAdaptor Adaptor>
215void Analyzer<Adaptor>::print_liveness(std::ostream &os)
const {
216 for (u32 i = 0; i <= liveness_max_value; ++i) {
217 if (liveness[i].epoch != liveness_epoch) {
218 os << std::format(
" {}: ignored\n", i);
222 const auto &info = liveness[i];
223 os << std::format(
" {}: {} refs, {}->{} ({}->{}), lf: {}\n",
226 static_cast<u32
>(info.first),
227 static_cast<u32
>(info.last),
228 adaptor->block_fmt_ref(block_ref(info.first)),
229 adaptor->block_fmt_ref(block_ref(info.last)),
234template <IRAdaptor Adaptor>
235typename Analyzer<Adaptor>::LivenessInfo &
236 Analyzer<Adaptor>::liveness_maybe(
const IRValueRef val)
noexcept {
237 const ValLocalIdx val_idx = adaptor->val_local_idx(val);
238 if constexpr (Adaptor::TPDE_PROVIDES_HIGHEST_VAL_IDX) {
239 assert(liveness.size() >
static_cast<u32
>(val_idx));
240 return liveness[
static_cast<u32
>(val_idx)];
242 if (liveness_max_value <=
static_cast<u32
>(val_idx)) {
243 liveness_max_value =
static_cast<u32
>(val_idx);
244 if (liveness.size() <= liveness_max_value) {
246 liveness.resize(liveness_max_value + 0x100);
249 return liveness[
static_cast<u32
>(val_idx)];
253template <IRAdaptor Adaptor>
254void Analyzer<Adaptor>::build_block_layout() {
255 util::SmallVector<IRBlockRef, SMALL_BLOCK_NUM> block_rpo{};
256 build_rpo_block_order(block_rpo);
258 util::SmallVector<u32, SMALL_BLOCK_NUM> loop_parent{};
259 util::SmallBitSet<256> loop_heads{};
262 identify_loops(block_rpo, loop_parent, loop_heads);
263 assert(loop_parent.size() == block_rpo.size());
265 loop_heads.mark_set(0);
267 build_loop_tree_and_block_layout(block_rpo, loop_parent, loop_heads);
270template <IRAdaptor Adaptor>
271void Analyzer<Adaptor>::build_loop_tree_and_block_layout(
272 const util::SmallVector<IRBlockRef, SMALL_BLOCK_NUM> &block_rpo,
273 const util::SmallVector<u32, SMALL_BLOCK_NUM> &loop_parent,
274 const util::SmallBitSet<256> &loop_heads) {
276 struct BlockLoopInfo {
281 util::SmallVector<BlockLoopInfo, SMALL_BLOCK_NUM> loop_blocks;
282 loop_blocks.resize_uninitialized(block_rpo.size());
283 for (u32 i = 0; i < block_rpo.size(); ++i) {
284 loop_blocks[i] = BlockLoopInfo{~0u, i};
290 const auto build_or_get_parent_loop = [&](
const u32 i,
291 const auto &self) -> u32 {
292 const auto parent = loop_parent[i];
293 if (loop_blocks[parent].loop_idx != ~0u) {
295 return loop_blocks[parent].loop_idx;
298 const auto parent_loop_idx = self(parent, self);
299 const auto loop_idx = loops.size();
300 loops.push_back(Loop{.level = loops[parent_loop_idx].level + 1,
301 .parent = parent_loop_idx});
302 loop_blocks[parent].loop_idx = loop_idx;
309 loops.push_back(Loop{.level = 0, .parent = 0, .num_blocks = 1});
310 loop_blocks[0].loop_idx = 0;
312 for (u32 i = 1; i < loop_parent.size(); ++i) {
313 const u32 parent_loop =
314 build_or_get_parent_loop(i, build_or_get_parent_loop);
316 if (loop_heads.is_set(i)) {
323 auto loop_idx = loop_blocks[i].loop_idx;
324 if (loop_idx == ~0u) [[likely]] {
325 loop_idx = loops.size();
327 Loop{.level = loops[parent_loop].level + 1, .parent = parent_loop});
328 loop_blocks[i].loop_idx = loop_idx;
330 ++loops[loop_idx].num_blocks;
332 loop_blocks[i].loop_idx = parent_loop;
333 ++loops[parent_loop].num_blocks;
340 for (u32 i = loops.size() - 1; i > 0; --i) {
341 const auto &loop = loops[i];
342 loops[loop.parent].num_blocks += loop.num_blocks;
345 assert(loops[0].num_blocks == block_rpo.size());
358 block_layout.resize_uninitialized(block_rpo.size());
362 block_loop_map.resize_uninitialized(block_rpo.size());
364 loops[0].begin = loops[0].end =
static_cast<BlockIndex
>(0);
366 const auto layout_loop = [&](
const u32 loop_idx,
const auto &self) ->
void {
367 assert(loops[loop_idx].begin == INVALID_BLOCK_IDX);
368 const auto parent = loops[loop_idx].parent;
369 if (loops[parent].begin == INVALID_BLOCK_IDX) {
374 const auto loop_begin = loops[parent].end;
375 loops[parent].end =
static_cast<BlockIndex
>(
static_cast<u32
>(loop_begin) +
376 loops[loop_idx].num_blocks);
377 assert(
static_cast<u32
>(loops[parent].end) -
378 static_cast<u32
>(loops[parent].begin) <=
379 loops[parent].num_blocks);
381 loops[loop_idx].begin = loops[loop_idx].end = loop_begin;
384 for (u32 i = 0u; i < block_rpo.size(); ++i) {
385 const auto loop_idx = loop_blocks[i].loop_idx;
386 if (loops[loop_idx].begin == INVALID_BLOCK_IDX) {
387 layout_loop(loop_idx, layout_loop);
390 const auto block_ref = block_rpo[loop_blocks[i].rpo_idx];
391 const auto block_idx =
static_cast<u32
>(loops[loop_idx].end);
392 loops[loop_idx].end =
static_cast<BlockIndex
>(block_idx + 1);
394 block_layout[block_idx] = block_ref;
395 block_loop_map[block_idx] = loop_idx;
396 adaptor->block_set_info(block_ref, block_idx);
399 assert(
static_cast<u32
>(loops[0].end) == block_rpo.size());
402template <IRAdaptor Adaptor>
403void Analyzer<Adaptor>::build_rpo_block_order(
404 util::SmallVector<IRBlockRef, SMALL_BLOCK_NUM> &out)
const noexcept {
411 for (IRBlockRef cur : adaptor->cur_blocks()) {
412 adaptor->block_set_info(cur, idx);
413 adaptor->block_set_info2(cur, 0);
418 out.resize_uninitialized(num_blocks);
436 util::SmallVector<IRBlockRef, SMALL_BLOCK_NUM / 2> stack;
437 stack.push_back(adaptor->cur_entry_block());
442 auto rpo_idx = num_blocks - 1;
443 while (!stack.empty()) {
444 const auto cur_node = stack.back();
448 if (adaptor->block_info2(cur_node) & 0b1000) {
456 if (adaptor->block_info2(cur_node) & 0b100) {
459 adaptor->block_set_info(cur_node, rpo_idx);
461 adaptor->block_set_info2(cur_node,
462 adaptor->block_info2(cur_node) | 0b1000);
463 out[rpo_idx] = cur_node;
469 adaptor->block_set_info2(cur_node, adaptor->block_info2(cur_node) | 0b100);
471 const auto start_idx = stack.size();
473 for (
const auto succ : adaptor->block_succs(cur_node)) {
474 assert(succ != adaptor->cur_entry_block());
475 const u32 info = adaptor->block_info2(succ);
476 if ((info & 0b11) != 0) {
477 if ((info & 0b11) == 1) {
479 adaptor->block_set_info2(succ, (info & ~0b11) | 2);
485 adaptor->block_set_info2(succ, info | 1);
490 if (adaptor->block_info2(succ) & 0b1100) {
494 stack.push_back(succ);
500 const auto len = stack.size() - start_idx;
506 if (adaptor->block_info(stack[start_idx]) >
507 adaptor->block_info(stack[start_idx + 1])) {
508 std::swap(stack[start_idx], stack[start_idx + 1]);
513 std::sort(stack.begin() + start_idx,
515 [
this](
const IRBlockRef lhs,
const IRBlockRef rhs) {
519 return adaptor->block_info(lhs) < adaptor->block_info(rhs);
523 if (rpo_idx != 0xFFFF'FFFF) {
528 out.erase(out.begin(), out.begin() + 1 + rpo_idx);
531 for (
auto i = 0u; i < out.size(); ++i) {
532 adaptor->block_set_info(out[i], i);
537 for (IRBlockRef cur : adaptor->cur_blocks()) {
538 if (adaptor->block_info2(cur) == 0) {
539 adaptor->block_set_info(cur, 0xFFFF'FFFF);
546 TPDE_LOG_TRACE(
"Finished building RPO for blocks:");
547 for (u32 i = 0; i < out.size(); ++i) {
548 TPDE_LOG_TRACE(
"Index {}: {}", i, adaptor->block_fmt_ref(out[i]));
553template <IRAdaptor Adaptor>
554void Analyzer<Adaptor>::identify_loops(
555 const util::SmallVector<IRBlockRef, SMALL_BLOCK_NUM> &block_rpo,
556 util::SmallVector<u32, SMALL_BLOCK_NUM> &loop_parent,
557 util::SmallBitSet<256> &loop_heads)
const noexcept {
559 loop_parent.resize(block_rpo.size());
561 loop_heads.resize(block_rpo.size());
576 util::SmallVector<BlockInfo, SMALL_BLOCK_NUM> block_infos;
577 block_infos.resize(block_rpo.size());
584 decltype(adaptor->block_succs(adaptor->cur_entry_block()).begin()) succ_it;
585 decltype(adaptor->block_succs(adaptor->cur_entry_block()).end()) end_it;
593 util::SmallVector<TravState, SMALL_BLOCK_NUM> trav_state;
595 trav_state.push_back(
596 TravState{.block_idx = 0,
598 .succ_it = adaptor->block_succs(block_rpo[0]).begin(),
599 .end_it = adaptor->block_succs(block_rpo[0]).end()});
601 const auto tag_lhead = [&block_infos](u32 b, u32 h) {
602 if (b == h || h == 0) {
606 auto cur1 = b, cur2 = h;
607 while (block_infos[cur1].iloop_header != 0) {
608 const auto ih = block_infos[cur1].iloop_header;
612 if (block_infos[ih].dfsp_pos < block_infos[cur2].dfsp_pos) {
613 block_infos[cur1].iloop_header = cur2;
620 block_infos[cur1].iloop_header = cur2;
625 while (!trav_state.empty()) {
626 auto &state = trav_state.back();
627 const auto block_idx = state.block_idx;
628 switch (state.state) {
631 block_infos[block_idx].traversed =
true;
632 block_infos[block_idx].dfsp_pos = state.dfsp_pos;
645 while (state.succ_it != state.end_it) {
646 const auto succ_idx = adaptor->block_info(*state.succ_it);
647 if (succ_idx == block_idx) {
648 block_infos[block_idx].self_loop =
true;
651 if (block_infos[succ_idx].traversed) {
652 if (block_infos[succ_idx].dfsp_pos > 0) {
653 tag_lhead(block_idx, succ_idx);
654 }
else if (block_infos[succ_idx].iloop_header != 0) {
655 auto h_idx = block_infos[succ_idx].iloop_header;
656 if (block_infos[h_idx].dfsp_pos > 0) {
657 tag_lhead(block_idx, h_idx);
659 while (block_infos[h_idx].iloop_header != 0) {
660 h_idx = block_infos[h_idx].iloop_header;
661 if (block_infos[h_idx].dfsp_pos > 0) {
662 tag_lhead(block_idx, h_idx);
677 auto succs = adaptor->block_succs(block_rpo[succ_idx]);
678 trav_state.push_back(TravState{.block_idx = succ_idx,
679 .dfsp_pos = state.dfsp_pos + 1,
680 .succ_it = succs.begin(),
681 .end_it = succs.end()});
689 block_infos[block_idx].dfsp_pos = 0;
690 trav_state.pop_back();
691 if (trav_state.empty()) {
694 trav_state.back().nh = block_infos[block_idx].iloop_header;
698 const auto nh = state.nh;
699 tag_lhead(block_idx, nh);
708 for (u32 i = 0; i < block_rpo.size(); ++i) {
709 auto &info = block_infos[i];
710 if (info.iloop_header != 0) {
711 loop_parent[i] = info.iloop_header;
712 loop_heads.mark_set(info.iloop_header);
714 if (info.self_loop) {
715 loop_heads.mark_set(i);
720template <IRAdaptor Adaptor>
721void Analyzer<Adaptor>::compute_liveness() noexcept {
727 TPDE_LOG_TRACE(
"Starting Liveness Analysis");
730 if (++liveness_epoch == 0) {
735 if constexpr (Adaptor::TPDE_PROVIDES_HIGHEST_VAL_IDX) {
736 liveness_max_value = adaptor->cur_highest_val_idx();
737 if (liveness_max_value >= liveness.size()) {
738 liveness.resize(liveness_max_value + 1);
741 liveness_max_value = 0;
746 const auto visit = [
this](
const IRValueRef value,
const u32 block_idx) {
747 TPDE_LOG_TRACE(
" Visiting value {} in block {}",
748 adaptor->value_fmt_ref(value),
750 if (adaptor->val_ignore_in_liveness_analysis(value)) {
751 TPDE_LOG_TRACE(
" value is ignored");
755 auto &liveness = liveness_maybe(value);
756 if (liveness.epoch != liveness_epoch) {
757 TPDE_LOG_TRACE(
" initializing liveness info, lcl is {}",
758 block_loop_map[block_idx]);
759 liveness = LivenessInfo{
760 .first =
static_cast<BlockIndex
>(block_idx),
761 .last =
static_cast<BlockIndex
>(block_idx),
763 .lowest_common_loop = block_loop_map[block_idx],
765 .epoch = liveness_epoch,
770 assert(liveness.ref_count != ~0u &&
"used value without definition");
772 ++liveness.ref_count;
773 TPDE_LOG_TRACE(
" increasing ref_count to {}", liveness.ref_count);
776 const auto update_for_block_only = [&liveness, block_idx]() {
777 const auto old_first =
static_cast<u32
>(liveness.first);
778 const auto old_last =
static_cast<u32
>(liveness.last);
780 const auto new_first = std::min(old_first, block_idx);
781 const auto new_last = std::max(old_last, block_idx);
782 liveness.first =
static_cast<BlockIndex
>(new_first);
783 liveness.last =
static_cast<BlockIndex
>(new_last);
787 liveness.last_full = (old_last == new_last) ? liveness.last_full : false;
790 const auto update_for_loop = [&liveness](
const Loop &loop) {
791 const auto old_first =
static_cast<u32
>(liveness.first);
792 const auto old_last =
static_cast<u32
>(liveness.last);
794 const auto new_first = std::min(old_first,
static_cast<u32
>(loop.begin));
795 const auto new_last = std::max(old_last,
static_cast<u32
>(loop.end) - 1);
796 liveness.first =
static_cast<BlockIndex
>(new_first);
797 liveness.last =
static_cast<BlockIndex
>(new_last);
801 liveness.last_full = (old_last == new_last) ? liveness.last_full : true;
804 const auto block_loop_idx = block_loop_map[block_idx];
805 if (liveness.lowest_common_loop == block_loop_idx) {
807 TPDE_LOG_TRACE(
" lcl is same as block loop");
808 update_for_block_only();
810 TPDE_LOG_TRACE(
" new interval {}->{}, lf: {}",
811 static_cast<u32
>(liveness.first),
812 static_cast<u32
>(liveness.last),
817 const Loop &liveness_loop = loops[liveness.lowest_common_loop];
818 const Loop &block_loop = loops[block_loop_idx];
820 if (liveness_loop.level < block_loop.level &&
821 static_cast<u32
>(block_loop.begin) <
822 static_cast<u32
>(liveness_loop.end)) {
823 assert(
static_cast<u32
>(block_loop.end) <=
824 static_cast<u32
>(liveness_loop.end));
826 TPDE_LOG_TRACE(
" block_loop {} is nested inside lcl", block_loop_idx);
831 const auto target_level = liveness_loop.level + 1;
832 auto cur_loop_idx = block_loop_idx;
833 auto cur_level = block_loop.level;
840 while (cur_level != target_level) {
841 cur_loop_idx = loops[cur_loop_idx].parent;
844 assert(loops[cur_loop_idx].level == target_level);
845 TPDE_LOG_TRACE(
" target_loop is {}", cur_loop_idx);
846 update_for_loop(loops[cur_loop_idx]);
848 TPDE_LOG_TRACE(
" new interval {}->{}, lf: {}",
849 static_cast<u32
>(liveness.first),
850 static_cast<u32
>(liveness.last),
855 TPDE_LOG_TRACE(
" block_loop {} is nested higher or in a different loop",
870 auto lhs_idx = liveness.lowest_common_loop;
871 auto rhs_idx = block_loop_idx;
872 auto prev_rhs = rhs_idx;
873 auto prev_lhs = lhs_idx;
874 while (lhs_idx != rhs_idx) {
875 const auto lhs_level = loops[lhs_idx].level;
876 const auto rhs_level = loops[rhs_idx].level;
877 if (lhs_level > rhs_level) {
879 lhs_idx = loops[lhs_idx].parent;
880 }
else if (lhs_level < rhs_level) {
882 rhs_idx = loops[rhs_idx].parent;
886 lhs_idx = loops[lhs_idx].parent;
887 rhs_idx = loops[rhs_idx].parent;
891 assert(
static_cast<u32
>(loops[lhs_idx].begin) <=
892 static_cast<u32
>(liveness_loop.begin));
893 assert(
static_cast<u32
>(loops[lhs_idx].end) >=
894 static_cast<u32
>(liveness_loop.end));
895 TPDE_LOG_TRACE(
" new lcl is {}", lhs_idx);
897 liveness.lowest_common_loop = lhs_idx;
901 assert(loops[prev_lhs].parent == lhs_idx);
902 assert(
static_cast<u32
>(loops[prev_lhs].begin) <=
903 static_cast<u32
>(liveness_loop.begin));
904 assert(
static_cast<u32
>(loops[prev_lhs].end) >=
905 static_cast<u32
>(liveness_loop.end));
906 update_for_loop(loops[prev_lhs]);
911 if (lhs_idx == block_loop_idx) {
912 update_for_block_only();
914 assert(loops[prev_rhs].parent == lhs_idx);
915 assert(loops[prev_rhs].level == loops[lhs_idx].level + 1);
916 update_for_loop(loops[prev_rhs]);
919 TPDE_LOG_TRACE(
" new interval {}->{}, lf: {}",
920 static_cast<u32
>(liveness.first),
921 static_cast<u32
>(liveness.last),
925 assert(block_layout[0] == adaptor->cur_entry_block());
926 if constexpr (Adaptor::TPDE_LIVENESS_VISIT_ARGS) {
927 for (
const IRValueRef arg : adaptor->cur_args()) {
932 for (u32 block_idx = 0; block_idx < block_layout.size(); ++block_idx) {
933 IRBlockRef block = block_layout[block_idx];
935 "Analyzing block {} ('{}')", block_idx, adaptor->block_fmt_ref(block));
936 const auto block_loop_idx = block_loop_map[block_idx];
938 bool has_phis =
false;
939 for (
const IRValueRef phi : adaptor->block_phis(block)) {
940 TPDE_LOG_TRACE(
"Analyzing phi {}", adaptor->value_fmt_ref(phi));
943 const auto phi_ref = adaptor->val_as_phi(phi);
944 const u32 slot_count = phi_ref.incoming_count();
945 for (u32 i = 0; i < slot_count; ++i) {
946 const IRBlockRef incoming_block = phi_ref.incoming_block_for_slot(i);
947 const IRValueRef incoming_value = phi_ref.incoming_val_for_slot(i);
948 if (adaptor->block_info2(incoming_block) == 0) {
949 TPDE_LOG_TRACE(
"ignoring phi input from unreachable pred ({})",
950 adaptor->block_fmt_ref(incoming_block));
953 const auto incoming_block_idx = adaptor->block_info(incoming_block);
956 visit(incoming_value, incoming_block_idx);
958 visit(phi, incoming_block_idx);
963 adaptor->block_set_info2(block, adaptor->block_info2(block) | 0b1'0000);
966 for (
const IRInstRef inst : adaptor->block_insts(block)) {
967 TPDE_LOG_TRACE(
"Analyzing instruction {}", adaptor->inst_fmt_ref(inst));
968 for (
const IRValueRef res : adaptor->inst_results(inst)) {
970 visit(res, block_idx);
971 ++loops[block_loop_idx].definitions;
974 for (
const IRValueRef operand : adaptor->inst_operands(inst)) {
975 visit(operand, block_idx);
984 for (u32 idx = loops.size() - 1; idx != 0; --idx) {
985 auto &loop = loops[idx];
986 loops[loop.parent].definitions_in_childs +=
987 loop.definitions_in_childs + loop.definitions;
992 for (
auto &entry : liveness) {
993 if (entry.ref_count == ~0u) {
999 TPDE_LOG_TRACE(
"Finished Liveness Analysis");