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());
405#if defined(TPDE_ASSERTS) && 0
412 util::SmallVector<Constraint, 16> constraint_stack{};
413 constraint_stack.push_back(
414 Constraint{0,
static_cast<u32
>(loops[0].end), 0, 0});
415 for (u32 i = 1; i < loops.size(); ++i) {
416 const auto &loop = loops[i];
417 assert(!constraint_stack.empty());
418 while (
static_cast<u32
>(loop.begin) >= constraint_stack.back().end) {
419 constraint_stack.pop_back();
422 const auto &con = constraint_stack.back();
423 assert(
static_cast<u32
>(loop.end) -
static_cast<u32
>(loop.begin) ==
425 assert(
static_cast<u32
>(loop.begin) >= con.begin);
426 assert(
static_cast<u32
>(loop.end) <= con.end);
427 assert(loop.parent == con.index);
428 assert(loop.level == con.level + 1);
430 constraint_stack.push_back(Constraint{
static_cast<u32
>(loop.begin),
431 static_cast<u32
>(loop.end),
439template <IRAdaptor Adaptor>
440void Analyzer<Adaptor>::build_rpo_block_order(
441 util::SmallVector<IRBlockRef, SMALL_BLOCK_NUM> &out)
const noexcept {
448 for (IRBlockRef cur : adaptor->cur_blocks()) {
449 adaptor->block_set_info(cur, idx);
450 adaptor->block_set_info2(cur, 0);
455 out.resize_uninitialized(num_blocks);
473 util::SmallVector<IRBlockRef, SMALL_BLOCK_NUM / 2> stack;
474 stack.push_back(adaptor->cur_entry_block());
479 auto rpo_idx = num_blocks - 1;
480 while (!stack.empty()) {
481 const auto cur_node = stack.back();
485 if (adaptor->block_info2(cur_node) & 0b1000) {
493 if (adaptor->block_info2(cur_node) & 0b100) {
496 adaptor->block_set_info(cur_node, rpo_idx);
498 adaptor->block_set_info2(cur_node,
499 adaptor->block_info2(cur_node) | 0b1000);
500 out[rpo_idx] = cur_node;
506 adaptor->block_set_info2(cur_node, adaptor->block_info2(cur_node) | 0b100);
508 const auto start_idx = stack.size();
510 for (
const auto succ : adaptor->block_succs(cur_node)) {
511 assert(succ != adaptor->cur_entry_block());
512 const u32 info = adaptor->block_info2(succ);
513 if ((info & 0b11) != 0) {
514 if ((info & 0b11) == 1) {
516 adaptor->block_set_info2(succ, (info & ~0b11) | 2);
522 adaptor->block_set_info2(succ, info | 1);
527 if (adaptor->block_info2(succ) & 0b1100) {
531 stack.push_back(succ);
537 const auto len = stack.size() - start_idx;
543 if (adaptor->block_info(stack[start_idx]) >
544 adaptor->block_info(stack[start_idx + 1])) {
545 std::swap(stack[start_idx], stack[start_idx + 1]);
550 std::sort(stack.begin() + start_idx,
552 [
this](
const IRBlockRef lhs,
const IRBlockRef rhs) {
556 return adaptor->block_info(lhs) < adaptor->block_info(rhs);
560 if (rpo_idx != 0xFFFF'FFFF) {
565 out.erase(out.begin(), out.begin() + 1 + rpo_idx);
568 for (
auto i = 0u; i < out.size(); ++i) {
569 adaptor->block_set_info(out[i], i);
574 for (IRBlockRef cur : adaptor->cur_blocks()) {
575 if (adaptor->block_info2(cur) == 0) {
576 adaptor->block_set_info(cur, 0xFFFF'FFFF);
583 TPDE_LOG_TRACE(
"Finished building RPO for blocks:");
584 for (u32 i = 0; i < out.size(); ++i) {
585 TPDE_LOG_TRACE(
"Index {}: {}", i, adaptor->block_fmt_ref(out[i]));
590template <IRAdaptor Adaptor>
591void Analyzer<Adaptor>::identify_loops(
592 const util::SmallVector<IRBlockRef, SMALL_BLOCK_NUM> &block_rpo,
593 util::SmallVector<u32, SMALL_BLOCK_NUM> &loop_parent,
594 util::SmallBitSet<256> &loop_heads)
const noexcept {
596 loop_parent.resize(block_rpo.size());
598 loop_heads.resize(block_rpo.size());
613 util::SmallVector<BlockInfo, SMALL_BLOCK_NUM> block_infos;
614 block_infos.resize(block_rpo.size());
621 decltype(adaptor->block_succs(adaptor->cur_entry_block()).begin()) succ_it;
622 decltype(adaptor->block_succs(adaptor->cur_entry_block()).end()) end_it;
630 util::SmallVector<TravState, SMALL_BLOCK_NUM> trav_state;
632 trav_state.push_back(
633 TravState{.block_idx = 0,
635 .succ_it = adaptor->block_succs(block_rpo[0]).begin(),
636 .end_it = adaptor->block_succs(block_rpo[0]).end()});
638 const auto tag_lhead = [&block_infos](u32 b, u32 h) {
639 if (b == h || h == 0) {
643 auto cur1 = b, cur2 = h;
644 while (block_infos[cur1].iloop_header != 0) {
645 const auto ih = block_infos[cur1].iloop_header;
649 if (block_infos[ih].dfsp_pos < block_infos[cur2].dfsp_pos) {
650 block_infos[cur1].iloop_header = cur2;
657 block_infos[cur1].iloop_header = cur2;
662 while (!trav_state.empty()) {
663 auto &state = trav_state.back();
664 const auto block_idx = state.block_idx;
665 switch (state.state) {
668 block_infos[block_idx].traversed =
true;
669 block_infos[block_idx].dfsp_pos = state.dfsp_pos;
682 while (state.succ_it != state.end_it) {
683 const auto succ_idx = adaptor->block_info(*state.succ_it);
684 if (succ_idx == block_idx) {
685 block_infos[block_idx].self_loop =
true;
688 if (block_infos[succ_idx].traversed) {
689 if (block_infos[succ_idx].dfsp_pos > 0) {
690 tag_lhead(block_idx, succ_idx);
691 }
else if (block_infos[succ_idx].iloop_header != 0) {
692 auto h_idx = block_infos[succ_idx].iloop_header;
693 if (block_infos[h_idx].dfsp_pos > 0) {
694 tag_lhead(block_idx, h_idx);
696 while (block_infos[h_idx].iloop_header != 0) {
697 h_idx = block_infos[h_idx].iloop_header;
698 if (block_infos[h_idx].dfsp_pos > 0) {
699 tag_lhead(block_idx, h_idx);
714 auto succs = adaptor->block_succs(block_rpo[succ_idx]);
715 trav_state.push_back(TravState{.block_idx = succ_idx,
716 .dfsp_pos = state.dfsp_pos + 1,
717 .succ_it = succs.begin(),
718 .end_it = succs.end()});
726 block_infos[block_idx].dfsp_pos = 0;
727 trav_state.pop_back();
728 if (trav_state.empty()) {
731 trav_state.back().nh = block_infos[block_idx].iloop_header;
735 const auto nh = state.nh;
736 tag_lhead(block_idx, nh);
745 for (u32 i = 0; i < block_rpo.size(); ++i) {
746 auto &info = block_infos[i];
747 if (info.iloop_header != 0) {
748 loop_parent[i] = info.iloop_header;
749 loop_heads.mark_set(info.iloop_header);
751 if (info.self_loop) {
752 loop_heads.mark_set(i);
757template <IRAdaptor Adaptor>
758void Analyzer<Adaptor>::compute_liveness() noexcept {
764 TPDE_LOG_TRACE(
"Starting Liveness Analysis");
767 if (++liveness_epoch == 0) {
772 if constexpr (Adaptor::TPDE_PROVIDES_HIGHEST_VAL_IDX) {
773 liveness_max_value = adaptor->cur_highest_val_idx();
774 if (liveness_max_value >= liveness.size()) {
775 liveness.resize(liveness_max_value + 1);
778 liveness_max_value = 0;
783 const auto visit = [
this](
const IRValueRef value,
const u32 block_idx) {
784 TPDE_LOG_TRACE(
" Visiting value {} in block {}",
785 adaptor->value_fmt_ref(value),
787 if (adaptor->val_ignore_in_liveness_analysis(value)) {
788 TPDE_LOG_TRACE(
" value is ignored");
792 auto &liveness = liveness_maybe(value);
793 if (liveness.epoch != liveness_epoch) {
794 TPDE_LOG_TRACE(
" initializing liveness info, lcl is {}",
795 block_loop_map[block_idx]);
796 liveness = LivenessInfo{
797 .first =
static_cast<BlockIndex
>(block_idx),
798 .last =
static_cast<BlockIndex
>(block_idx),
800 .lowest_common_loop = block_loop_map[block_idx],
802 .epoch = liveness_epoch,
807 assert(liveness.ref_count != ~0u &&
"used value without definition");
809 ++liveness.ref_count;
810 TPDE_LOG_TRACE(
" increasing ref_count to {}", liveness.ref_count);
813 const auto update_for_block_only = [&liveness, block_idx]() {
814 const auto old_first =
static_cast<u32
>(liveness.first);
815 const auto old_last =
static_cast<u32
>(liveness.last);
817 const auto new_first = std::min(old_first, block_idx);
818 const auto new_last = std::max(old_last, block_idx);
819 liveness.first =
static_cast<BlockIndex
>(new_first);
820 liveness.last =
static_cast<BlockIndex
>(new_last);
824 liveness.last_full = (old_last == new_last) ? liveness.last_full : false;
827 const auto update_for_loop = [&liveness](
const Loop &loop) {
828 const auto old_first =
static_cast<u32
>(liveness.first);
829 const auto old_last =
static_cast<u32
>(liveness.last);
831 const auto new_first = std::min(old_first,
static_cast<u32
>(loop.begin));
832 const auto new_last = std::max(old_last,
static_cast<u32
>(loop.end) - 1);
833 liveness.first =
static_cast<BlockIndex
>(new_first);
834 liveness.last =
static_cast<BlockIndex
>(new_last);
838 liveness.last_full = (old_last == new_last) ? liveness.last_full : true;
841 const auto block_loop_idx = block_loop_map[block_idx];
842 if (liveness.lowest_common_loop == block_loop_idx) {
844 TPDE_LOG_TRACE(
" lcl is same as block loop");
845 update_for_block_only();
847 TPDE_LOG_TRACE(
" new interval {}->{}, lf: {}",
848 static_cast<u32
>(liveness.first),
849 static_cast<u32
>(liveness.last),
854 const Loop &liveness_loop = loops[liveness.lowest_common_loop];
855 const Loop &block_loop = loops[block_loop_idx];
857 if (liveness_loop.level < block_loop.level &&
858 static_cast<u32
>(block_loop.begin) <
859 static_cast<u32
>(liveness_loop.end)) {
860 assert(
static_cast<u32
>(block_loop.end) <=
861 static_cast<u32
>(liveness_loop.end));
863 TPDE_LOG_TRACE(
" block_loop {} is nested inside lcl", block_loop_idx);
868 const auto target_level = liveness_loop.level + 1;
869 auto cur_loop_idx = block_loop_idx;
870 auto cur_level = block_loop.level;
877 while (cur_level != target_level) {
878 cur_loop_idx = loops[cur_loop_idx].parent;
881 assert(loops[cur_loop_idx].level == target_level);
882 TPDE_LOG_TRACE(
" target_loop is {}", cur_loop_idx);
883 update_for_loop(loops[cur_loop_idx]);
885 TPDE_LOG_TRACE(
" new interval {}->{}, lf: {}",
886 static_cast<u32
>(liveness.first),
887 static_cast<u32
>(liveness.last),
892 TPDE_LOG_TRACE(
" block_loop {} is nested higher or in a different loop",
907 auto lhs_idx = liveness.lowest_common_loop;
908 auto rhs_idx = block_loop_idx;
909 auto prev_rhs = rhs_idx;
910 auto prev_lhs = lhs_idx;
911 while (lhs_idx != rhs_idx) {
912 const auto lhs_level = loops[lhs_idx].level;
913 const auto rhs_level = loops[rhs_idx].level;
914 if (lhs_level > rhs_level) {
916 lhs_idx = loops[lhs_idx].parent;
917 }
else if (lhs_level < rhs_level) {
919 rhs_idx = loops[rhs_idx].parent;
923 lhs_idx = loops[lhs_idx].parent;
924 rhs_idx = loops[rhs_idx].parent;
928 assert(
static_cast<u32
>(loops[lhs_idx].begin) <=
929 static_cast<u32
>(liveness_loop.begin));
930 assert(
static_cast<u32
>(loops[lhs_idx].end) >=
931 static_cast<u32
>(liveness_loop.end));
932 TPDE_LOG_TRACE(
" new lcl is {}", lhs_idx);
934 liveness.lowest_common_loop = lhs_idx;
938 assert(loops[prev_lhs].parent == lhs_idx);
939 assert(
static_cast<u32
>(loops[prev_lhs].begin) <=
940 static_cast<u32
>(liveness_loop.begin));
941 assert(
static_cast<u32
>(loops[prev_lhs].end) >=
942 static_cast<u32
>(liveness_loop.end));
943 update_for_loop(loops[prev_lhs]);
948 if (lhs_idx == block_loop_idx) {
949 update_for_block_only();
951 assert(loops[prev_rhs].parent == lhs_idx);
952 assert(loops[prev_rhs].level == loops[lhs_idx].level + 1);
953 update_for_loop(loops[prev_rhs]);
956 TPDE_LOG_TRACE(
" new interval {}->{}, lf: {}",
957 static_cast<u32
>(liveness.first),
958 static_cast<u32
>(liveness.last),
962 assert(block_layout[0] == adaptor->cur_entry_block());
963 if constexpr (Adaptor::TPDE_LIVENESS_VISIT_ARGS) {
964 for (
const IRValueRef arg : adaptor->cur_args()) {
969 for (u32 block_idx = 0; block_idx < block_layout.size(); ++block_idx) {
970 IRBlockRef block = block_layout[block_idx];
972 "Analyzing block {} ('{}')", block_idx, adaptor->block_fmt_ref(block));
973 const auto block_loop_idx = block_loop_map[block_idx];
975 bool has_phis =
false;
976 for (
const IRValueRef phi : adaptor->block_phis(block)) {
977 TPDE_LOG_TRACE(
"Analyzing phi {}", adaptor->value_fmt_ref(phi));
980 const auto phi_ref = adaptor->val_as_phi(phi);
981 const u32 slot_count = phi_ref.incoming_count();
982 for (u32 i = 0; i < slot_count; ++i) {
983 const IRBlockRef incoming_block = phi_ref.incoming_block_for_slot(i);
984 const IRValueRef incoming_value = phi_ref.incoming_val_for_slot(i);
985 if (adaptor->block_info2(incoming_block) == 0) {
986 TPDE_LOG_TRACE(
"ignoring phi input from unreachable pred ({})",
987 adaptor->block_fmt_ref(incoming_block));
990 const auto incoming_block_idx = adaptor->block_info(incoming_block);
993 visit(incoming_value, incoming_block_idx);
995 visit(phi, incoming_block_idx);
1000 adaptor->block_set_info2(block, adaptor->block_info2(block) | 0b1'0000);
1003 for (
const IRInstRef inst : adaptor->block_insts(block)) {
1004 TPDE_LOG_TRACE(
"Analyzing instruction {}", adaptor->inst_fmt_ref(inst));
1005 for (
const IRValueRef res : adaptor->inst_results(inst)) {
1007 visit(res, block_idx);
1008 ++loops[block_loop_idx].definitions;
1011 for (
const IRValueRef operand : adaptor->inst_operands(inst)) {
1012 visit(operand, block_idx);
1021 for (u32 idx = loops.size() - 1; idx != 0; --idx) {
1022 auto &loop = loops[idx];
1023 loops[loop.parent].definitions_in_childs +=
1024 loop.definitions_in_childs + loop.definitions;
1029 for (
auto &entry : liveness) {
1030 if (entry.ref_count == ~0u) {
1031 entry.ref_count = 0;
1036 TPDE_LOG_TRACE(
"Finished Liveness Analysis");