Coverage Report

Created: 2025-09-08 08:10

/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
75.1k
{
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
75.1k
  AssemblyItem pushSelf{PushTag, u256(-4)};
45
75.1k
  if (
46
75.1k
    std::count(m_items.cbegin(), m_items.cend(), pushSelf.tag()) ||
47
75.1k
    std::count(m_items.cbegin(), m_items.cend(), pushSelf.pushTag())
48
75.1k
  )
49
0
    return false;
50
51
75.1k
  std::function<bool(size_t, size_t)> comparator = [&](size_t _i, size_t _j)
52
17.4M
  {
53
17.4M
    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
17.4M
    AssemblyItem pushFirstTag{pushSelf};
59
17.4M
    AssemblyItem pushSecondTag{pushSelf};
60
61
17.4M
    if (_i < m_items.size() && m_items.at(_i).type() == Tag)
62
17.4M
      pushFirstTag = m_items.at(_i).pushTag();
63
17.4M
    if (_j < m_items.size() && m_items.at(_j).type() == Tag)
64
17.4M
      pushSecondTag = m_items.at(_j).pushTag();
65
66
17.4M
    using diff_type = BlockIterator::difference_type;
67
17.4M
    BlockIterator first{m_items.begin() + diff_type(_i), m_items.end(), &pushFirstTag, &pushSelf};
68
17.4M
    BlockIterator second{m_items.begin() + diff_type(_j), m_items.end(), &pushSecondTag, &pushSelf};
69
17.4M
    BlockIterator end{m_items.end(), m_items.end()};
70
71
17.4M
    if (first != end && (*first).type() == Tag)
72
17.4M
      ++first;
73
17.4M
    if (second != end && (*second).type() == Tag)
74
17.4M
      ++second;
75
76
17.4M
    return std::lexicographical_compare(first, end, second, end);
77
17.4M
  };
78
79
75.1k
  size_t iterations = 0;
80
75.1k
  for (; ; ++iterations)
81
83.7k
  {
82
    //@todo this should probably be optimized.
83
83.7k
    std::set<size_t, std::function<bool(size_t, size_t)>> blocksSeen(comparator);
84
21.4M
    for (size_t i = 0; i < m_items.size(); ++i)
85
21.3M
    {
86
21.3M
      if (m_items.at(i).type() != Tag)
87
19.8M
        continue;
88
1.53M
      auto it = blocksSeen.find(i);
89
1.53M
      if (it == blocksSeen.end())
90
1.33M
        blocksSeen.insert(i);
91
203k
      else
92
203k
        m_replacedTags[m_items.at(i).data()] = m_items.at(*it).data();
93
1.53M
    }
94
95
83.7k
    if (!applyTagReplacement(m_items, m_replacedTags))
96
75.1k
      break;
97
83.7k
  }
98
75.1k
  return iterations > 0;
99
75.1k
}
100
101
bool BlockDeduplicator::applyTagReplacement(
102
  AssemblyItems& _items,
103
  std::map<u256, u256> const& _replacements,
104
  SubAssemblyID _subId
105
)
106
105k
{
107
105k
  bool changed = false;
108
105k
  for (AssemblyItem& item: _items)
109
22.6M
    if (item.type() == PushTag || item.type() == RelativeJump || item.type() == ConditionalRelativeJump)
110
2.17M
    {
111
2.17M
      SubAssemblyID subId;
112
2.17M
      size_t tagId;
113
2.17M
      std::tie(subId, tagId) = item.splitForeignPushTag();
114
2.17M
      if (subId != _subId)
115
143k
        continue;
116
2.03M
      auto it = _replacements.find(tagId);
117
      // Recursively look for the element replaced by tagId
118
2.13M
      for (auto _it = it; _it != _replacements.end(); _it = _replacements.find(_it->second))
119
104k
        it = _it;
120
121
2.03M
      if (it != _replacements.end())
122
104k
      {
123
104k
        changed = true;
124
104k
        item.setPushTagSubIdAndTag(subId, static_cast<size_t>(it->second));
125
104k
      }
126
2.03M
    }
127
105k
  return changed;
128
105k
}
129
130
BlockDeduplicator::BlockIterator& BlockDeduplicator::BlockIterator::operator++()
131
89.7M
{
132
89.7M
  if (it == end)
133
0
    return *this;
134
89.7M
  if (SemanticInformation::altersControlFlow(*it) && *it != AssemblyItem{Instruction::JUMPI} && it->type() != ConditionalRelativeJump)
135
811k
    it = end;
136
88.8M
  else
137
88.8M
  {
138
88.8M
    ++it;
139
90.5M
    while (it != end && it->type() == Tag)
140
1.65M
      ++it;
141
88.8M
  }
142
89.7M
  return *this;
143
89.7M
}
144
145
AssemblyItem const& BlockDeduplicator::BlockIterator::operator*() const
146
191M
{
147
191M
  if (replaceItem && replaceWith && *it == *replaceItem)
148
12.2k
    return *replaceWith;
149
191M
  else
150
191M
    return *it;
151
191M
}