/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 | } |