32 const auto& cfg_blocks = block_result.cfg_blocks;
39 std::vector<int> worklist;
41 if (!cfg_blocks.empty()) {
43 worklist.push_back(0);
46 size_t processed_count = 0;
47 while (!worklist.empty()) {
50 cfg_blocks.size() * cfg_blocks.size()) {
52 "Failed to prove stack safety; check loops. "
53 "processed_count = {}, cfg_blocks = {}, heuristic_limit = {}, "
54 "worklist_size = {}, next_block_candidate = {}",
55 processed_count, cfg_blocks.size(),
56 cfg_blocks.size() * cfg_blocks.size(), worklist.size(),
60 int block_idx = worklist.back();
63 const auto& block = cfg_blocks[block_idx];
66 if (depth_in < block.min_stack_needed) {
68 std::format(
"Stack underflow before executing block {}: "
69 "depth_in = {}, min_needed = {}, start token '{}'",
70 block_idx, depth_in, block.min_stack_needed,
71 tokens[block.start_token_idx].text),
72 block.start_token_idx);
75 int depth_out = depth_in + block.stack_effect;
77 for (
int succ_idx : block.successors) {
80 worklist.push_back(succ_idx);
84 "Stack depth mismatch on converging paths: "
85 "block {} -> successor {}. "
86 "incoming depth = {}, recorded successor depth = {}, "
87 "current start token '{}', successor start token '{}'",
88 block_idx, succ_idx, depth_out,
90 tokens[block.start_token_idx].text,
91 tokens[cfg_blocks[succ_idx].start_token_idx].text),
92 cfg_blocks[succ_idx].start_token_idx);
97 for (
size_t i = 0; i < cfg_blocks.size(); ++i) {
99 cfg_blocks[i].successors.empty()) {
102 if (final_depth != expected_final_depth) {
105 "Expression stack not balanced on "
106 "reachable terminal block {}: "
107 "final depth = {}, expected = {}, start token '{}'",
108 i, final_depth, expected_final_depth,
109 tokens[cfg_blocks[i].start_token_idx].text),
110 cfg_blocks[i].start_token_idx);