38using Bitset = analysis::dynamic_bitset::DynamicBitset;
40std::vector<int> compute_reachable(
const std::vector<CFGBlock>& cfg) {
41 std::vector<int> reachable(cfg.size(), 0);
51 for (
int v : cfg[u].successors) {
52 if (v >= 0 &&
static_cast<size_t>(v) < cfg.size() &&
62struct SCCDecomposition {
63 std::vector<int> comp;
64 std::vector<std::vector<int>> nodes;
67SCCDecomposition compute_scc_kosaraju(
const std::vector<CFGBlock>& cfg,
68 const std::vector<int>& reachable) {
69 int n = (int)cfg.size();
70 std::vector<std::vector<int>> g(n);
71 std::vector<std::vector<int>> gr(n);
72 for (
int u = 0; u < n; ++u) {
73 if (reachable[u] == 0) {
76 for (
int v : cfg[u].successors) {
77 if (v >= 0 && v < n && reachable[v] != 0) {
84 std::vector<int> order;
85 order.reserve((
size_t)n);
86 std::vector<int> vis(n, 0);
87 for (
int i = 0; i < n; ++i) {
88 if (reachable[i] == 0 || vis[i] != 0) {
91 std::stack<std::pair<int, size_t>> st;
95 auto& [u, idx] = st.top();
96 if (idx < g[u].size()) {
109 std::vector<int> comp(n, -1);
111 for (
int k = (
int)order.size() - 1; k >= 0; --k) {
112 int v0 = order[(size_t)k];
113 if (comp[v0] != -1) {
118 comp[v0] = comp_count;
122 for (
int p : gr[v]) {
124 comp[p] = comp_count;
132 std::vector<std::vector<int>> nodes((
size_t)comp_count);
133 for (
int i = 0; i < n; ++i) {
134 if (reachable[i] == 0 || comp[i] < 0) {
137 nodes[(size_t)comp[i]].push_back(i);
140 return {.comp = std::move(comp), .nodes = std::move(nodes)};
143bool scc_is_cyclic(
const std::vector<CFGBlock>& cfg,
144 const std::vector<int>& reachable,
145 const std::vector<int>& scc_nodes) {
146 if (scc_nodes.empty()) {
149 if (scc_nodes.size() > 1) {
152 int u = scc_nodes[0];
153 if (reachable[u] == 0) {
156 return std::ranges::any_of(cfg[u].successors,
157 [u](
int v) {
return v == u; });
160std::vector<int> scc_entry_nodes(
const std::vector<CFGBlock>& cfg,
161 const std::vector<int>& reachable,
162 const SCCDecomposition& scc,
int cid) {
163 std::vector<int> entry;
164 for (
int v : scc.nodes[(
size_t)cid]) {
165 if (reachable[v] == 0) {
168 for (
int p : cfg[v].predecessors) {
169 if (p < 0 ||
static_cast<size_t>(p) >= cfg.size()) {
172 if (reachable[p] == 0) {
175 if (scc.comp[p] != cid) {
181 std::ranges::sort(entry);
182 auto ret = std::ranges::unique(entry);
183 entry.erase(ret.begin(), ret.end());
187bool check_reducible(
const std::vector<CFGBlock>& cfg,
188 const std::vector<int>& reachable) {
189 int n = (int)cfg.size();
194 auto scc = compute_scc_kosaraju(cfg, reachable);
195 for (
size_t cid = 0; cid < scc.nodes.size(); ++cid) {
196 const auto& nodes = scc.nodes[cid];
197 if (!scc_is_cyclic(cfg, reachable, nodes)) {
201 auto entry_nodes = scc_entry_nodes(cfg, reachable, scc, (
int)cid);
202 if (entry_nodes.size() > 1) {
209void rebuild_predecessors(std::vector<CFGBlock>& cfg) {
210 for (
auto& b : cfg) {
211 b.predecessors.clear();
213 for (
size_t u = 0; u < cfg.size(); ++u) {
214 for (
int v : cfg[u].successors) {
215 if (v >= 0 &&
static_cast<size_t>(v) < cfg.size()) {
216 cfg[(size_t)v].predecessors.push_back((
int)u);
222bool node_split_make_reducible(std::vector<CFGBlock>& cfg,
223 std::vector<int>& origin_map,
229 bool changed_any =
false;
233 for (
int iter = 0; iter < 128; ++iter) {
234 rebuild_predecessors(cfg);
235 auto reachable = compute_reachable(cfg);
236 auto scc = compute_scc_kosaraju(cfg, reachable);
238 bool changed_this_iter =
false;
239 for (
size_t cid = 0; cid < scc.nodes.size(); ++cid) {
240 const auto& comp_nodes = scc.nodes[cid];
241 if (!scc_is_cyclic(cfg, reachable, comp_nodes)) {
244 auto entry_nodes = scc_entry_nodes(cfg, reachable, scc, (
int)cid);
245 if (entry_nodes.size() <= 1) {
251 int primary_entry = entry_nodes.front();
254 std::vector<uint8_t> in_component(cfg.size(), 0);
255 for (
int v : comp_nodes) {
256 if (v >= 0 &&
static_cast<size_t>(v) < in_component.size()) {
257 in_component[(size_t)v] = 1;
261 int original_node_count = (int)cfg.size();
263 for (
size_t ei = 1; ei < entry_nodes.size(); ++ei) {
264 int entry = entry_nodes[ei];
266 if (cfg.size() + comp_nodes.size() > max_blocks) {
270 std::vector<int> clone_of((
size_t)original_node_count, -1);
273 for (
int v : comp_nodes) {
276 nb.predecessors.clear();
278 int new_idx = (int)cfg.size();
279 cfg.push_back(std::move(nb));
280 origin_map.push_back(origin_map[(
size_t)v]);
281 if (v >= 0 && v < original_node_count) {
282 clone_of[(size_t)v] = new_idx;
288 for (
int v : comp_nodes) {
289 int v_clone = clone_of[(size_t)v];
293 for (
int s : cfg[(
size_t)v].successors) {
294 if (s >= 0 && s < original_node_count &&
295 in_component[(
size_t)s] != 0) {
296 cfg[(size_t)v_clone].successors.push_back(
297 clone_of[(
size_t)s]);
299 cfg[(size_t)v_clone].successors.push_back(s);
305 int entry_clone = clone_of[(size_t)entry];
306 for (
int p = 0; p < original_node_count; ++p) {
307 if (reachable[(
size_t)p] == 0) {
310 if (in_component[(
size_t)p] != 0) {
313 std::ranges::replace(cfg[(
size_t)p].successors, entry,
318 changed_this_iter =
true;
323 if (!changed_this_iter) {
330bool tail_duplicate_trivial_joins(std::vector<CFGBlock>& cfg,
331 std::vector<int>& origin_map,
337 rebuild_predecessors(cfg);
338 auto reachable = compute_reachable(cfg);
339 auto scc = compute_scc_kosaraju(cfg, reachable);
341 std::vector<uint8_t> cyclic(cfg.size(), 0);
342 for (
const auto& comp_nodes : scc.nodes) {
343 if (!scc_is_cyclic(cfg, reachable, comp_nodes)) {
346 for (
int v : comp_nodes) {
347 if (v >= 0 &&
static_cast<size_t>(v) < cyclic.size()) {
348 cyclic[(size_t)v] = 1;
353 bool changed =
false;
354 int original_n = (int)cfg.size();
357 constexpr int K_MAX_TRIVIAL_TOKENS = 6;
359 for (
int c = 1; c < original_n; ++c) {
360 if (reachable[(
size_t)c] == 0) {
363 if (cyclic[(
size_t)c] != 0) {
367 if (cfg[(
size_t)c].successors.empty()) {
370 if (cfg[(
size_t)c].successors.size() > 1) {
373 if (cfg[(
size_t)c].predecessors.size() < 2) {
377 cfg[(size_t)c].end_token_idx - cfg[(
size_t)c].start_token_idx;
378 if (tok_count > K_MAX_TRIVIAL_TOKENS) {
382 auto preds = cfg[(size_t)c].predecessors;
383 std::ranges::sort(preds);
384 auto ret = std::ranges::unique(preds);
385 preds.erase(ret.begin(), ret.end());
387 for (
int p : preds) {
388 if (cfg.size() + 1 > max_blocks) {
389 rebuild_predecessors(cfg);
396 int clone_idx = (int)cfg.size();
397 cfg.push_back(std::move(clone));
398 origin_map.push_back(origin_map[(
size_t)c]);
400 std::ranges::replace(cfg[(
size_t)p].successors, c, clone_idx);
406 rebuild_predecessors(cfg);
410std::vector<Bitset> compute_dominators(
const std::vector<CFGBlock>& cfg,
411 const std::vector<int>& reachable) {
412 int n = (int)cfg.size();
413 std::vector<Bitset> dom;
414 dom.reserve((
size_t)n);
415 for (
int i = 0; i < n; ++i) {
423 for (
int i = 0; i < n; ++i) {
435 auto compute_for_node = [&](
int i) -> Bitset {
438 if (cfg[i].predecessors.empty()) {
441 for (
int p : cfg[i].predecessors) {
445 new_dom.intersectWith(dom[p]);
457 for (
int i = 1; i < n; ++i) {
458 if (reachable[i] == 0) {
461 Bitset new_dom = compute_for_node(i);
462 if (!new_dom.equals(dom[i])) {
463 dom[i] = std::move(new_dom);
472std::vector<Bitset> compute_postdominators(
const std::vector<CFGBlock>& cfg,
473 const std::vector<int>& reachable) {
474 int n = (int)cfg.size();
475 int node_count = n + 1;
477 std::vector<std::vector<int>> succ((
size_t)node_count);
478 std::vector<std::vector<int>> pred((
size_t)node_count);
480 auto build_reverse_graph = [&]() {
481 for (
int i = 0; i < n; ++i) {
485 for (
int s : cfg[i].successors) {
486 if (s >= 0 && s < n && reachable[s]) {
487 succ[i].push_back(s);
488 pred[s].push_back(i);
491 if (succ[i].empty()) {
492 succ[i].push_back(n);
493 pred[n].push_back(i);
499 std::vector<Bitset> pdom;
500 pdom.reserve((
size_t)node_count);
501 for (
int i = 0; i < node_count; ++i) {
502 pdom.emplace_back(node_count);
504 for (
int i = 0; i < node_count; ++i) {
512 build_reverse_graph();
514 std::vector<Bitset> pdom = init();
516 auto compute_for_node = [&](
int i) -> Bitset {
517 Bitset new_pdom(node_count);
519 for (
int s : succ[i]) {
520 new_pdom.intersectWith(pdom[s]);
529 for (
int i = 0; i < n; ++i) {
530 if (reachable[i] == 0) {
533 Bitset new_pdom = compute_for_node(i);
534 if (!new_pdom.equals(pdom[i])) {
535 pdom[i] = std::move(new_pdom);
544int compute_ipdom_for_node(
int node,
const std::vector<Bitset>& pdom) {
547 int node_count =
static_cast<int>(pdom.size());
549 std::vector<int> strict;
550 strict.reserve((
size_t)node_count);
551 for (
int i = 0; i < node_count; ++i) {
552 if (i != node && pdom[node].test(i)) {
556 if (strict.empty()) {
565 for (
int c : strict) {
566 bool postdominates_other =
false;
567 for (
int other : strict) {
573 if (pdom[other].test(c)) {
574 postdominates_other =
true;
578 if (!postdominates_other) {
586std::vector<int> compute_ipdom_vector(
const std::vector<Bitset>& pdom,
588 const std::vector<int>& reachable) {
589 std::vector<int> ipdom(real_node_count, -1);
590 int exit_node = real_node_count;
591 for (
int i = 0; i < real_node_count; ++i) {
592 if (reachable[i] == 0) {
595 int ip = compute_ipdom_for_node(i, pdom);
596 ipdom[i] = (ip == exit_node) ? -1 : ip;
602 const std::vector<CFGBlock>& cfg,
603 const std::vector<int>& reachable,
604 const std::vector<Bitset>& dom) {
605 int n = (int)cfg.size();
606 for (
int u = 0; u < n; ++u) {
607 if (reachable[u] == 0) {
610 for (
int h : cfg[u].successors) {
611 if (h < 0 || h >= n || reachable[h] == 0) {
614 if (!dom[u].test(h)) {
617 auto& body = result.loop_body[h];
618 std::deque<int> work;
620 if (!body.contains(u)) {
624 while (!work.empty()) {
625 int x = work.front();
627 for (
int p : cfg[x].predecessors) {
628 if (reachable[p] == 0) {
631 if (!body.contains(p)) {
642 for (
auto& [header, body] : result.loop_body) {
644 (header >= 0 &&
static_cast<size_t>(header) < result.ipdom.size())
645 ? result.ipdom[header]
647 while (f != -1 && body.contains(f)) {
650 result.loop_follow[header] = f;
655 const std::vector<int>& reachable) {
656 int n = (int)result.innermost_loop_header.size();
657 for (
int b = 0; b < n; ++b) {
658 if (reachable[b] == 0) {
661 int best_header = -1;
662 size_t best_size = std::numeric_limits<size_t>::max();
663 for (
const auto& [header, body] : result.loop_body) {
664 if (body.contains(b) && body.size() < best_size) {
665 best_size = body.size();
666 best_header = header;
669 result.innermost_loop_header[b] = best_header;
679 const auto& cfg = block_result.cfg_blocks;
688 const auto reachable0 = compute_reachable(cfg);
689 const bool reducible0 = check_reducible(cfg, reachable0);
691 const std::vector<CFGBlock>* analysis_cfg = &cfg;
693 std::vector<CFGBlock> work_cfg = cfg;
694 std::vector<int> origin_map(cfg.size());
695 for (
size_t i = 0; i < cfg.size(); ++i) {
696 origin_map[i] =
static_cast<int>(i);
699 constexpr size_t MAX_BLOCKS = 256;
700 constexpr size_t MAX_BLOCKS_RATIO = 8;
702 std::max<size_t>(cfg.size() * MAX_BLOCKS_RATIO, MAX_BLOCKS);
705 tail_duplicate_trivial_joins(work_cfg, origin_map, max_blocks);
707 changed |= node_split_make_reducible(work_cfg, origin_map, max_blocks);
711 result.structured_cfg_blocks = std::move(work_cfg);
712 result.structured_block_origin = std::move(origin_map);
713 analysis_cfg = &result.structured_cfg_blocks;
715 result.structured_stack_depth_in.resize(
716 result.structured_cfg_blocks.size(), -1);
717 for (
size_t i = 0; i < result.structured_cfg_blocks.size(); ++i) {
718 int orig = result.structured_block_origin[i];
719 if (orig >= 0 &&
static_cast<size_t>(orig) <
720 stack_safety.stack_depth_in.size()) {
721 result.structured_stack_depth_in[i] =
722 stack_safety.stack_depth_in[(size_t)orig];
727 const auto reachable = compute_reachable(*analysis_cfg);
728 result.reducible = check_reducible(*analysis_cfg, reachable);
729 result.success = result.reducible;
731 result.ipdom.assign(analysis_cfg->size(), -1);
732 result.innermost_loop_header.assign(analysis_cfg->size(), -1);
734 const auto dom = compute_dominators(*analysis_cfg, reachable);
735 const auto pdom = compute_postdominators(*analysis_cfg, reachable);
737 int n = (int)analysis_cfg->size();
738 result.ipdom = compute_ipdom_vector(pdom, n, reachable);
740 compute_natural_loops(result, *analysis_cfg, reachable, dom);
741 compute_loop_follow(result);
742 compute_innermost_loop_header(result, reachable);
PassT::Result & getResult()
Result run(const std::vector< Token > &tokens, AnalysisManager &am) override
StructurizeCFGResult Result
std::vector< int > predecessors
std::vector< int > successors