Coverage Report

Created: 2022-08-24 06:52

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