Coverage Report

Created: 2026-06-30 07:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/solidity/libyul/backends/evm/ssa/SSACFGLoopNestingForest.cpp
Line
Count
Source
1
/*
2
  This file is part of solidity.
3
4
  solidity is free software: you can redistribute it and/or modify
5
  it under the terms of the GNU General Public License as published by
6
  the Free Software Foundation, either version 3 of the License, or
7
  (at your option) any later version.
8
9
  solidity is distributed in the hope that it will be useful,
10
  but WITHOUT ANY WARRANTY; without even the implied warranty of
11
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
  GNU General Public License for more details.
13
14
  You should have received a copy of the GNU General Public License
15
  along with solidity.  If not, see <http://www.gnu.org/licenses/>.
16
*/
17
// SPDX-License-Identifier: GPL-3.0
18
19
#include <libyul/backends/evm/ssa/SSACFGLoopNestingForest.h>
20
21
#include <range/v3/algorithm/reverse.hpp>
22
23
using namespace solidity::yul::ssa;
24
25
SSACFGLoopNestingForest::SSACFGLoopNestingForest(traversal::ForwardTopologicalSort const& _sort):
26
0
  m_sort(_sort),
27
0
  m_cfg(_sort.cfg()),
28
0
  m_vertexPartition(m_cfg.numBlocks()),
29
0
  m_loopParents(m_cfg.numBlocks(), std::numeric_limits<BlockIdValue>::max())
30
0
{
31
0
  auto dfsOrder = m_sort.preOrder();
32
  // we go from innermost to outermost
33
0
  ranges::reverse(dfsOrder);
34
35
0
  for (auto const& blockId: dfsOrder)
36
0
    findLoop(blockId);
37
38
  // get the root nodes
39
0
  for (auto loopHeader: m_loopNodes)
40
0
  {
41
0
    while (m_loopParents[loopHeader] != std::numeric_limits<BlockIdValue>::max())
42
0
      loopHeader = m_loopParents[loopHeader];
43
0
    m_loopRootNodes.insert(loopHeader);
44
0
  }
45
0
}
46
47
void SSACFGLoopNestingForest::findLoop(BlockIdValue const _potentialHeader)
48
0
{
49
0
  if (m_sort.backEdgeTargets().contains(_potentialHeader))
50
0
  {
51
0
    std::set<BlockIdValue> loopBody;
52
0
    std::set<BlockIdValue> workList;
53
0
    for (auto const pred: m_cfg.block(SSACFG::BlockId{_potentialHeader}).entries)
54
0
    {
55
0
      auto const representative = m_vertexPartition.find(pred.value);
56
0
      if (
57
0
        representative != _potentialHeader &&
58
0
        m_sort.backEdge(SSACFG::BlockId{pred}, SSACFG::BlockId{_potentialHeader})
59
0
      )
60
0
        workList.insert(representative);
61
0
    }
62
63
0
    while (!workList.empty())
64
0
    {
65
0
      auto const y = workList.extract(workList.begin()).value();
66
0
      loopBody.insert(y);
67
68
0
      for (auto const& predecessor: m_cfg.block(SSACFG::BlockId{y}).entries)
69
0
      {
70
0
        if (!m_sort.backEdge(SSACFG::BlockId{predecessor}, SSACFG::BlockId{y}))
71
0
        {
72
0
          auto const predecessorHeader = m_vertexPartition.find(predecessor.value);
73
0
          if (predecessorHeader != _potentialHeader && loopBody.count(predecessorHeader) == 0)
74
0
            workList.insert(predecessorHeader);
75
0
        }
76
0
      }
77
0
    }
78
79
0
    if (!loopBody.empty())
80
0
      collapse(loopBody, _potentialHeader);
81
0
  }
82
0
}
83
void SSACFGLoopNestingForest::collapse(std::set<BlockIdValue> const& _loopBody, BlockIdValue _loopHeader)
84
0
{
85
0
  for (auto const z: _loopBody)
86
0
  {
87
0
    m_loopParents[z] = _loopHeader;
88
0
    m_vertexPartition.merge(_loopHeader, z, false);  // don't merge by size, loop header should be representative
89
0
  }
90
  yulAssert(m_vertexPartition.find(_loopHeader) == _loopHeader);  // representative was preserved
91
0
  m_loopNodes.insert(_loopHeader);
92
0
}