/src/solidity/libevmasm/ExpressionClasses.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 ExpressionClasses.cpp |
20 | | * @author Christian <c@ethdev.com> |
21 | | * @date 2015 |
22 | | * Container for equivalence classes of expressions for use in common subexpression elimination. |
23 | | */ |
24 | | |
25 | | #include <libevmasm/ExpressionClasses.h> |
26 | | |
27 | | #include <libevmasm/Assembly.h> |
28 | | #include <libevmasm/CommonSubexpressionEliminator.h> |
29 | | #include <libevmasm/SimplificationRules.h> |
30 | | |
31 | | #include <boost/container_hash/hash.hpp> |
32 | | |
33 | | #include <functional> |
34 | | #include <limits> |
35 | | #include <tuple> |
36 | | |
37 | | using namespace std; |
38 | | using namespace solidity; |
39 | | using namespace solidity::evmasm; |
40 | | using namespace solidity::langutil; |
41 | | |
42 | | bool ExpressionClasses::Expression::operator==(ExpressionClasses::Expression const& _other) const |
43 | 0 | { |
44 | 0 | assertThrow(!!item && !!_other.item, OptimizerException, ""); |
45 | 0 | auto type = item->type(); |
46 | 0 | auto otherType = _other.item->type(); |
47 | 0 | if (type != otherType) |
48 | 0 | return false; |
49 | 0 | else if (type == Operation) |
50 | 0 | { |
51 | 0 | auto instr = item->instruction(); |
52 | 0 | auto otherInstr = _other.item->instruction(); |
53 | 0 | return std::tie(instr, arguments, sequenceNumber) == |
54 | 0 | std::tie(otherInstr, _other.arguments, _other.sequenceNumber); |
55 | 0 | } |
56 | 0 | else |
57 | 0 | return std::tie(item->data(), arguments, sequenceNumber) == |
58 | 0 | std::tie(_other.item->data(), _other.arguments, _other.sequenceNumber); |
59 | 0 | } |
60 | | |
61 | | std::size_t ExpressionClasses::Expression::ExpressionHash::operator()(Expression const& _expression) const |
62 | 0 | { |
63 | 0 | assertThrow(!!_expression.item, OptimizerException, ""); |
64 | 0 | std::size_t seed = 0; |
65 | 0 | auto type = _expression.item->type(); |
66 | 0 | boost::hash_combine(seed, type); |
67 | |
|
68 | 0 | if (type == Operation) |
69 | 0 | boost::hash_combine(seed, _expression.item->instruction()); |
70 | 0 | else |
71 | 0 | boost::hash_combine(seed, _expression.item->data()); |
72 | |
|
73 | 0 | boost::hash_range(seed, _expression.arguments.begin(), _expression.arguments.end()); |
74 | 0 | boost::hash_combine(seed, _expression.sequenceNumber); |
75 | |
|
76 | 0 | return seed; |
77 | 0 | } |
78 | | |
79 | | ExpressionClasses::Id ExpressionClasses::find( |
80 | | AssemblyItem const& _item, |
81 | | Ids const& _arguments, |
82 | | bool _copyItem, |
83 | | unsigned _sequenceNumber |
84 | | ) |
85 | 0 | { |
86 | 0 | Expression exp; |
87 | 0 | exp.id = Id(-1); |
88 | 0 | exp.item = &_item; |
89 | 0 | exp.arguments = _arguments; |
90 | 0 | exp.sequenceNumber = _sequenceNumber; |
91 | |
|
92 | 0 | if (SemanticInformation::isCommutativeOperation(_item)) |
93 | 0 | sort(exp.arguments.begin(), exp.arguments.end()); |
94 | |
|
95 | 0 | if (SemanticInformation::isDeterministic(_item)) |
96 | 0 | { |
97 | 0 | auto it = m_expressions.find(exp); |
98 | 0 | if (it != m_expressions.end()) |
99 | 0 | return it->id; |
100 | 0 | } |
101 | | |
102 | 0 | if (_copyItem) |
103 | 0 | exp.item = storeItem(_item); |
104 | |
|
105 | 0 | ExpressionClasses::Id id = tryToSimplify(exp); |
106 | 0 | if (id < m_representatives.size()) |
107 | 0 | exp.id = id; |
108 | 0 | else |
109 | 0 | { |
110 | 0 | exp.id = static_cast<Id>(m_representatives.size()); |
111 | 0 | m_representatives.push_back(exp); |
112 | 0 | } |
113 | 0 | m_expressions.insert(exp); |
114 | 0 | return exp.id; |
115 | 0 | } |
116 | | |
117 | | void ExpressionClasses::forceEqual( |
118 | | ExpressionClasses::Id _id, |
119 | | AssemblyItem const& _item, |
120 | | ExpressionClasses::Ids const& _arguments, |
121 | | bool _copyItem |
122 | | ) |
123 | 0 | { |
124 | 0 | Expression exp; |
125 | 0 | exp.id = _id; |
126 | 0 | exp.item = &_item; |
127 | 0 | exp.arguments = _arguments; |
128 | |
|
129 | 0 | if (SemanticInformation::isCommutativeOperation(_item)) |
130 | 0 | sort(exp.arguments.begin(), exp.arguments.end()); |
131 | |
|
132 | 0 | if (_copyItem) |
133 | 0 | exp.item = storeItem(_item); |
134 | |
|
135 | 0 | m_expressions.insert(exp); |
136 | 0 | } |
137 | | |
138 | | ExpressionClasses::Id ExpressionClasses::newClass(SourceLocation const& _location) |
139 | 0 | { |
140 | 0 | Expression exp; |
141 | 0 | exp.id = static_cast<Id>(m_representatives.size()); |
142 | 0 | exp.item = storeItem(AssemblyItem(UndefinedItem, (u256(1) << 255) + exp.id, _location)); |
143 | 0 | m_representatives.push_back(exp); |
144 | 0 | m_expressions.insert(exp); |
145 | 0 | return exp.id; |
146 | 0 | } |
147 | | |
148 | | bool ExpressionClasses::knownToBeDifferent(ExpressionClasses::Id _a, ExpressionClasses::Id _b) |
149 | 0 | { |
150 | | // Try to simplify "_a - _b" and return true iff the value is a non-zero constant. |
151 | 0 | return knownNonZero(find(Instruction::SUB, {_a, _b})); |
152 | 0 | } |
153 | | |
154 | | bool ExpressionClasses::knownToBeDifferentBy32(ExpressionClasses::Id _a, ExpressionClasses::Id _b) |
155 | 0 | { |
156 | | // Try to simplify "_a - _b" and return true iff the value is at least 32 away from zero. |
157 | 0 | u256 const* v = knownConstant(find(Instruction::SUB, {_a, _b})); |
158 | | // forbidden interval is ["-31", 31] |
159 | 0 | return v && *v + 31 > u256(62); |
160 | 0 | } |
161 | | |
162 | | bool ExpressionClasses::knownZero(Id _c) |
163 | 0 | { |
164 | 0 | return Pattern(u256(0)).matches(representative(_c), *this); |
165 | 0 | } |
166 | | |
167 | | bool ExpressionClasses::knownNonZero(Id _c) |
168 | 0 | { |
169 | 0 | return Pattern(u256(0)).matches(representative(find(Instruction::ISZERO, {_c})), *this); |
170 | 0 | } |
171 | | |
172 | | u256 const* ExpressionClasses::knownConstant(Id _c) |
173 | 0 | { |
174 | 0 | map<unsigned, Expression const*> matchGroups; |
175 | 0 | Pattern constant(Push); |
176 | 0 | constant.setMatchGroup(1, matchGroups); |
177 | 0 | if (!constant.matches(representative(_c), *this)) |
178 | 0 | return nullptr; |
179 | 0 | return &constant.d(); |
180 | 0 | } |
181 | | |
182 | | AssemblyItem const* ExpressionClasses::storeItem(AssemblyItem const& _item) |
183 | 0 | { |
184 | 0 | m_spareAssemblyItems.push_back(make_shared<AssemblyItem>(_item)); |
185 | 0 | return m_spareAssemblyItems.back().get(); |
186 | 0 | } |
187 | | |
188 | | string ExpressionClasses::fullDAGToString(ExpressionClasses::Id _id) const |
189 | 0 | { |
190 | 0 | Expression const& expr = representative(_id); |
191 | 0 | stringstream str; |
192 | 0 | str << dec << expr.id << ":"; |
193 | 0 | if (expr.item) |
194 | 0 | { |
195 | 0 | str << *expr.item << "("; |
196 | 0 | for (Id arg: expr.arguments) |
197 | 0 | str << fullDAGToString(arg) << ","; |
198 | 0 | str << ")"; |
199 | 0 | } |
200 | 0 | else |
201 | 0 | str << " UNIQUE"; |
202 | 0 | return str.str(); |
203 | 0 | } |
204 | | |
205 | | ExpressionClasses::Id ExpressionClasses::tryToSimplify(Expression const& _expr) |
206 | 0 | { |
207 | 0 | static Rules rules; |
208 | 0 | assertThrow(rules.isInitialized(), OptimizerException, "Rule list not properly initialized."); |
209 | | |
210 | 0 | if ( |
211 | 0 | !_expr.item || |
212 | 0 | _expr.item->type() != Operation || |
213 | 0 | !SemanticInformation::isDeterministic(*_expr.item) |
214 | 0 | ) |
215 | 0 | return numeric_limits<unsigned>::max(); |
216 | | |
217 | 0 | if (auto match = rules.findFirstMatch(_expr, *this)) |
218 | 0 | { |
219 | | // Debug info |
220 | 0 | if (false) |
221 | 0 | { |
222 | 0 | cout << "Simplifying " << *_expr.item << "("; |
223 | 0 | for (Id arg: _expr.arguments) |
224 | 0 | cout << fullDAGToString(arg) << ", "; |
225 | 0 | cout << ")" << endl; |
226 | 0 | cout << "with rule " << match->pattern.toString() << endl; |
227 | 0 | cout << "to " << match->action().toString() << endl; |
228 | 0 | } |
229 | |
|
230 | 0 | return rebuildExpression(ExpressionTemplate(match->action(), _expr.item->location())); |
231 | 0 | } |
232 | | |
233 | 0 | return numeric_limits<unsigned>::max(); |
234 | 0 | } |
235 | | |
236 | | ExpressionClasses::Id ExpressionClasses::rebuildExpression(ExpressionTemplate const& _template) |
237 | 0 | { |
238 | 0 | if (_template.hasId) |
239 | 0 | return _template.id; |
240 | | |
241 | 0 | Ids arguments; |
242 | 0 | for (ExpressionTemplate const& t: _template.arguments) |
243 | 0 | arguments.push_back(rebuildExpression(t)); |
244 | 0 | return find(_template.item, arguments); |
245 | 0 | } |