Coverage Report

Created: 2025-09-04 07:34

/src/solidity/libevmasm/BlockDeduplicator.cpp
Line
Count
Source (jump to first uncovered line)
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
 * @file BlockDeduplicator.cpp
20
 * @author Christian <c@ethdev.com>
21
 * @date 2015
22
 * Unifies basic blocks that share content.
23
 */
24
25
#include <libevmasm/BlockDeduplicator.h>
26
27
#include <libevmasm/AssemblyItem.h>
28
#include <libevmasm/SemanticInformation.h>
29
30
#include <functional>
31
#include <set>
32
33
using namespace solidity;
34
using namespace solidity::evmasm;
35
36
37
bool BlockDeduplicator::deduplicate()
38
72.6k
{
39
  // Compares indices based on the suffix that starts there, ignoring tags and stopping at
40
  // opcodes that stop the control flow.
41
42
  // Virtual tag that signifies "the current block" and which is used to optimise loops.
43
  // We abort if this virtual tag actually exists.
44
72.6k
  AssemblyItem pushSelf{PushTag, u256(-4)};
45
72.6k
  if (
46
72.6k
    std::count(m_items.cbegin(), m_items.cend(), pushSelf.tag()) ||
47
72.6k
    std::count(m_items.cbegin(), m_items.cend(), pushSelf.pushTag())
48
72.6k
  )
49
0
    return false;
50
51
72.6k
  std::function<bool(size_t, size_t)> comparator = [&](size_t _i, size_t _j)
52
16.2M
  {
53
16.2M
    if (_i == _j)
54
0
      return false;
55
56
    // To compare recursive loops, we have to already unify PushTag opcodes of the
57
    // block's own tag.
58
16.2M
    AssemblyItem pushFirstTag{pushSelf};
59
16.2M
    AssemblyItem pushSecondTag{pushSelf};
60
61
16.2M
    if (_i < m_items.size() && m_items.at(_i).type() == Tag)
62
16.2M
      pushFirstTag = m_items.at(_i).pushTag();
63
16.2M
    if (_j < m_items.size() && m_items.at(_j).type() == Tag)
64
16.2M
      pushSecondTag = m_items.at(_j).pushTag();
65
66
16.2M
    using diff_type = BlockIterator::difference_type;
67
16.2M
    BlockIterator first{m_items.begin() + diff_type(_i), m_items.end(), &pushFirstTag, &pushSelf};
68
16.2M
    BlockIterator second{m_items.begin() + diff_type(_j), m_items.end(), &pushSecondTag, &pushSelf};
69
16.2M
    BlockIterator end{m_items.end(), m_items.end()};
70
71
16.2M
    if (first != end && (*first).type() == Tag)
72
16.2M
      ++first;
73
16.2M
    if (second != end && (*second).type() == Tag)
74
16.2M
      ++second;
75
76
16.2M
    return std::lexicographical_compare(first, end, second, end);
77
16.2M
  };
78
79
72.6k
  size_t iterations = 0;
80
72.6k
  for (; ; ++iterations)
81
80.9k
  {
82
    //@todo this should probably be optimized.
83
80.9k
    std::set<size_t, std::function<bool(size_t, size_t)>> blocksSeen(comparator);
84
20.3M
    for (size_t i = 0; i < m_items.size(); ++i)
85
20.2M
    {
86
20.2M
      if (m_items.at(i).type() != Tag)
87
18.8M
        continue;
88
1.45M
      auto it = blocksSeen.find(i);
89
1.45M
      if (it == blocksSeen.end())
90
1.25M
        blocksSeen.insert(i);
91
197k
      else
92
197k
        m_replacedTags[m_items.at(i).data()] = m_items.at(*it).data();
93
1.45M
    }
94
95
80.9k
    if (!applyTagReplacement(m_items, m_replacedTags))
96
72.6k
      break;
97
80.9k
  }
98
72.6k
  return iterations > 0;
99
72.6k
}
100
101
bool BlockDeduplicator::applyTagReplacement(
102
  AssemblyItems& _items,
103
  std::map<u256, u256> const& _replacements,
104
  SubAssemblyID _subId
105
)
106
102k
{
107
102k
  bool changed = false;
108
102k
  for (AssemblyItem& item: _items)
109
21.4M
    if (item.type() == PushTag || item.type() == RelativeJump || item.type() == ConditionalRelativeJump)
110
2.06M
    {
111
2.06M
      SubAssemblyID subId;
112
2.06M
      size_t tagId;
113
2.06M
      std::tie(subId, tagId) = item.splitForeignPushTag();
114
2.06M
      if (subId != _subId)
115
136k
        continue;
116
1.93M
      auto it = _replacements.find(tagId);
117
      // Recursively look for the element replaced by tagId
118
2.03M
      for (auto _it = it; _it != _replacements.end(); _it = _replacements.find(_it->second))
119
101k
        it = _it;
120
121
1.93M
      if (it != _replacements.end())
122
101k
      {
123
101k
        changed = true;
124
101k
        item.setPushTagSubIdAndTag(subId, static_cast<size_t>(it->second));
125
101k
      }
126
1.93M
    }
127
102k
  return changed;
128
102k
}
129
130
BlockDeduplicator::BlockIterator& BlockDeduplicator::BlockIterator::operator++()
131
83.1M
{
132
83.1M
  if (it == end)
133
0
    return *this;
134
83.1M
  if (SemanticInformation::altersControlFlow(*it) && *it != AssemblyItem{Instruction::JUMPI} && it->type() != ConditionalRelativeJump)
135
786k
    it = end;
136
82.4M
  else
137
82.4M
  {
138
82.4M
    ++it;
139
83.9M
    while (it != end && it->type() == Tag)
140
1.57M
      ++it;
141
82.4M
  }
142
83.1M
  return *this;
143
83.1M
}
144
145
AssemblyItem const& BlockDeduplicator::BlockIterator::operator*() const
146
177M
{
147
177M
  if (replaceItem && replaceWith && *it == *replaceItem)
148
11.3k
    return *replaceWith;
149
177M
  else
150
177M
    return *it;
151
177M
}