/src/solidity/libevmasm/CommonSubexpressionEliminator.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 CommonSubexpressionEliminator.cpp |
20 | | * @author Christian <c@ethdev.com> |
21 | | * @date 2015 |
22 | | * Optimizer step for common subexpression elimination and stack reorganisation. |
23 | | */ |
24 | | |
25 | | #include <functional> |
26 | | #include <libsolutil/Keccak256.h> |
27 | | #include <libevmasm/CommonSubexpressionEliminator.h> |
28 | | #include <libevmasm/AssemblyItem.h> |
29 | | #include <libsolutil/StackTooDeepString.h> |
30 | | |
31 | | #include <range/v3/view/reverse.hpp> |
32 | | |
33 | | using namespace std; |
34 | | using namespace solidity; |
35 | | using namespace solidity::evmasm; |
36 | | using namespace solidity::langutil; |
37 | | |
38 | | vector<AssemblyItem> CommonSubexpressionEliminator::getOptimizedItems() |
39 | 602k | { |
40 | 602k | optimizeBreakingItem(); |
41 | | |
42 | 602k | KnownState nextInitialState = m_state; |
43 | 602k | if (m_breakingItem) |
44 | 601k | nextInitialState.feedItem(*m_breakingItem); |
45 | 602k | KnownState nextState = nextInitialState; |
46 | | |
47 | 602k | ScopeGuard reset([&]() |
48 | 602k | { |
49 | 602k | m_breakingItem = nullptr; |
50 | 602k | m_storeOperations.clear(); |
51 | 602k | m_initialState = move(nextInitialState); |
52 | 602k | m_state = move(nextState); |
53 | 602k | }); |
54 | | |
55 | 602k | map<int, Id> initialStackContents; |
56 | 602k | map<int, Id> targetStackContents; |
57 | 602k | int minHeight = m_state.stackHeight() + 1; |
58 | 602k | if (!m_state.stackElements().empty()) |
59 | 364k | minHeight = min(minHeight, m_state.stackElements().begin()->first); |
60 | 1.34M | for (int height = minHeight; height <= m_initialState.stackHeight(); ++height) |
61 | 742k | initialStackContents[height] = m_initialState.stackElement(height, SourceLocation()); |
62 | 1.98M | for (int height = minHeight; height <= m_state.stackHeight(); ++height) |
63 | 1.37M | targetStackContents[height] = m_state.stackElement(height, SourceLocation()); |
64 | | |
65 | 602k | AssemblyItems items = CSECodeGenerator(m_state.expressionClasses(), m_storeOperations).generateCode( |
66 | 602k | m_initialState.sequenceNumber(), |
67 | 602k | m_initialState.stackHeight(), |
68 | 602k | initialStackContents, |
69 | 602k | targetStackContents |
70 | 602k | ); |
71 | 602k | if (m_breakingItem) |
72 | 601k | items.push_back(*m_breakingItem); |
73 | | |
74 | 602k | return items; |
75 | 602k | } |
76 | | |
77 | | void CommonSubexpressionEliminator::feedItem(AssemblyItem const& _item, bool _copyItem) |
78 | 2.60M | { |
79 | 2.60M | StoreOperation op = m_state.feedItem(_item, _copyItem); |
80 | 2.60M | if (op.isValid()) |
81 | 96.7k | m_storeOperations.push_back(op); |
82 | 2.60M | } |
83 | | |
84 | | void CommonSubexpressionEliminator::optimizeBreakingItem() |
85 | 602k | { |
86 | 602k | if (!m_breakingItem) |
87 | 0 | return; |
88 | | |
89 | 602k | ExpressionClasses& classes = m_state.expressionClasses(); |
90 | 602k | SourceLocation const& itemLocation = m_breakingItem->location(); |
91 | 602k | if (*m_breakingItem == AssemblyItem(Instruction::JUMPI)) |
92 | 123k | { |
93 | 123k | AssemblyItem::JumpType jumpType = m_breakingItem->getJumpType(); |
94 | | |
95 | 123k | Id condition = m_state.stackElement(m_state.stackHeight() - 1, itemLocation); |
96 | 123k | if (classes.knownNonZero(condition)) |
97 | 2.37k | { |
98 | 2.37k | feedItem(AssemblyItem(Instruction::SWAP1, itemLocation), true); |
99 | 2.37k | feedItem(AssemblyItem(Instruction::POP, itemLocation), true); |
100 | | |
101 | 2.37k | AssemblyItem item(Instruction::JUMP, itemLocation); |
102 | 2.37k | item.setJumpType(jumpType); |
103 | 2.37k | m_breakingItem = classes.storeItem(item); |
104 | 2.37k | } |
105 | 120k | else if (classes.knownZero(condition)) |
106 | 905 | { |
107 | 905 | AssemblyItem it(Instruction::POP, itemLocation); |
108 | 905 | feedItem(it, true); |
109 | 905 | feedItem(it, true); |
110 | 905 | m_breakingItem = nullptr; |
111 | 905 | } |
112 | 123k | } |
113 | 479k | else if (*m_breakingItem == AssemblyItem(Instruction::RETURN)) |
114 | 8.57k | { |
115 | 8.57k | Id size = m_state.stackElement(m_state.stackHeight() - 1, itemLocation); |
116 | 8.57k | if (classes.knownZero(size)) |
117 | 0 | { |
118 | 0 | feedItem(AssemblyItem(Instruction::POP, itemLocation), true); |
119 | 0 | feedItem(AssemblyItem(Instruction::POP, itemLocation), true); |
120 | 0 | AssemblyItem item(Instruction::STOP, itemLocation); |
121 | 0 | m_breakingItem = classes.storeItem(item); |
122 | 0 | } |
123 | 8.57k | } |
124 | 602k | } |
125 | | |
126 | | CSECodeGenerator::CSECodeGenerator( |
127 | | ExpressionClasses& _expressionClasses, |
128 | | vector<CSECodeGenerator::StoreOperation> const& _storeOperations |
129 | | ): |
130 | | m_expressionClasses(_expressionClasses) |
131 | 602k | { |
132 | 602k | for (auto const& store: _storeOperations) |
133 | 96.7k | m_storeOperations[make_pair(store.target, store.slot)].push_back(store); |
134 | 602k | } |
135 | | |
136 | | AssemblyItems CSECodeGenerator::generateCode( |
137 | | unsigned _initialSequenceNumber, |
138 | | int _initialStackHeight, |
139 | | map<int, Id> const& _initialStack, |
140 | | map<int, Id> const& _targetStackContents |
141 | | ) |
142 | 602k | { |
143 | 602k | m_stackHeight = _initialStackHeight; |
144 | 602k | m_stack = _initialStack; |
145 | 602k | m_targetStack = _targetStackContents; |
146 | 602k | for (auto const& item: m_stack) |
147 | 742k | m_classPositions[item.second].insert(item.first); |
148 | | |
149 | | // generate the dependency graph starting from final storage and memory writes and target stack contents |
150 | 602k | for (auto const& p: m_storeOperations) |
151 | 96.5k | addDependencies(p.second.back().expression); |
152 | 602k | for (auto const& targetItem: m_targetStack) |
153 | 1.37M | { |
154 | 1.37M | m_finalClasses.insert(targetItem.second); |
155 | 1.37M | addDependencies(targetItem.second); |
156 | 1.37M | } |
157 | | |
158 | | // store all needed sequenced expressions |
159 | 602k | set<pair<unsigned, Id>> sequencedExpressions; |
160 | 602k | for (auto const& p: m_neededBy) |
161 | 1.21M | for (auto id: {p.first, p.second}) |
162 | 2.42M | if (unsigned seqNr = m_expressionClasses.representative(id).sequenceNumber) |
163 | 470k | { |
164 | | // Invalid sequenced operation. |
165 | | // @todo quick fix for now. Proper fix needs to choose representative with higher |
166 | | // sequence number during dependency analysis. |
167 | 470k | assertThrow(seqNr >= _initialSequenceNumber, StackTooDeepException, util::stackTooDeepString); |
168 | 470k | sequencedExpressions.insert(make_pair(seqNr, id)); |
169 | 470k | } |
170 | | |
171 | | // Perform all operations on storage and memory in order, if they are needed. |
172 | 602k | for (auto const& seqAndId: sequencedExpressions) |
173 | 194k | if (!m_classPositions.count(seqAndId.second)) |
174 | 194k | generateClassElement(seqAndId.second, true); |
175 | | |
176 | | // generate the target stack elements |
177 | 602k | for (auto const& targetItem: m_targetStack) |
178 | 1.37M | { |
179 | 1.37M | if (m_stack.count(targetItem.first) && m_stack.at(targetItem.first) == targetItem.second) |
180 | 433k | continue; // already there |
181 | 945k | generateClassElement(targetItem.second); |
182 | 945k | assertThrow(!m_classPositions[targetItem.second].empty(), OptimizerException, ""); |
183 | 945k | if (m_classPositions[targetItem.second].count(targetItem.first)) |
184 | 606k | continue; |
185 | 339k | SourceLocation sourceLocation; |
186 | 339k | if (m_expressionClasses.representative(targetItem.second).item) |
187 | 339k | sourceLocation = m_expressionClasses.representative(targetItem.second).item->location(); |
188 | 339k | int position = classElementPosition(targetItem.second); |
189 | 339k | if (position < targetItem.first) |
190 | | // it is already at its target, we need another copy |
191 | 121k | appendDup(position, sourceLocation); |
192 | 217k | else |
193 | 217k | appendOrRemoveSwap(position, sourceLocation); |
194 | 339k | appendOrRemoveSwap(targetItem.first, sourceLocation); |
195 | 339k | } |
196 | | |
197 | | // remove surplus elements |
198 | 691k | while (removeStackTopIfPossible()) |
199 | 88.9k | { |
200 | | // no-op |
201 | 88.9k | } |
202 | | |
203 | | // check validity |
204 | 602k | int finalHeight = 0; |
205 | 602k | if (!m_targetStack.empty()) |
206 | | // have target stack, so its height should be the final height |
207 | 364k | finalHeight = (--m_targetStack.end())->first; |
208 | 238k | else if (!_initialStack.empty()) |
209 | | // no target stack, only erase the initial stack |
210 | 8.07k | finalHeight = _initialStack.begin()->first - 1; |
211 | 230k | else |
212 | | // neither initial no target stack, no change in height |
213 | 230k | finalHeight = _initialStackHeight; |
214 | 602k | assertThrow(finalHeight == m_stackHeight, OptimizerException, "Incorrect final stack height."); |
215 | | |
216 | 602k | return m_generatedItems; |
217 | 602k | } |
218 | | |
219 | | void CSECodeGenerator::addDependencies(Id _c) |
220 | 2.68M | { |
221 | 2.68M | if (m_classPositions.count(_c)) |
222 | 901k | return; // it is already on the stack |
223 | 1.78M | if (m_neededBy.find(_c) != m_neededBy.end()) |
224 | 200k | return; // we already computed the dependencies for _c |
225 | 1.58M | ExpressionClasses::Expression expr = m_expressionClasses.representative(_c); |
226 | 1.58M | assertThrow(expr.item, OptimizerException, ""); |
227 | | // If this exception happens, we need to find a different way to generate the |
228 | | // compound expression. |
229 | 1.58M | assertThrow(expr.item->type() != UndefinedItem, ItemNotAvailableException, "Undefined item requested but not available."); |
230 | 1.58M | for (Id argument: expr.arguments) |
231 | 1.19M | { |
232 | 1.19M | addDependencies(argument); |
233 | 1.19M | m_neededBy.insert(make_pair(argument, _c)); |
234 | 1.19M | } |
235 | 1.58M | if (expr.item && expr.item->type() == Operation && ( |
236 | 744k | expr.item->instruction() == Instruction::SLOAD || |
237 | 744k | expr.item->instruction() == Instruction::MLOAD || |
238 | 744k | expr.item->instruction() == Instruction::KECCAK256 |
239 | 744k | )) |
240 | 108k | { |
241 | | // this loads an unknown value from storage or memory and thus, in addition to its |
242 | | // arguments, depends on all store operations to addresses where we do not know that |
243 | | // they are different that occur before this load |
244 | 108k | StoreOperation::Target target = expr.item->instruction() == Instruction::SLOAD ? |
245 | 104k | StoreOperation::Storage : StoreOperation::Memory; |
246 | 108k | Id slotToLoadFrom = expr.arguments.at(0); |
247 | 108k | for (auto const& p: m_storeOperations) |
248 | 67.2k | { |
249 | 67.2k | if (p.first.first != target) |
250 | 4.54k | continue; |
251 | 62.6k | Id slot = p.first.second; |
252 | 62.6k | StoreOperations const& storeOps = p.second; |
253 | 62.6k | if (storeOps.front().sequenceNumber > expr.sequenceNumber) |
254 | 40.7k | continue; |
255 | 21.8k | bool knownToBeIndependent = false; |
256 | 21.8k | switch (expr.item->instruction()) |
257 | 21.8k | { |
258 | 904 | case Instruction::SLOAD: |
259 | 904 | knownToBeIndependent = m_expressionClasses.knownToBeDifferent(slot, slotToLoadFrom); |
260 | 904 | break; |
261 | 19.6k | case Instruction::MLOAD: |
262 | 19.6k | knownToBeIndependent = m_expressionClasses.knownToBeDifferentBy32(slot, slotToLoadFrom); |
263 | 19.6k | break; |
264 | 1.32k | case Instruction::KECCAK256: |
265 | 1.32k | { |
266 | 1.32k | Id length = expr.arguments.at(1); |
267 | 1.32k | AssemblyItem offsetInstr(Instruction::SUB, expr.item->location()); |
268 | 1.32k | Id offsetToStart = m_expressionClasses.find(offsetInstr, {slot, slotToLoadFrom}); |
269 | 1.32k | u256 const* o = m_expressionClasses.knownConstant(offsetToStart); |
270 | 1.32k | u256 const* l = m_expressionClasses.knownConstant(length); |
271 | 1.32k | if (l && *l == 0) |
272 | 0 | knownToBeIndependent = true; |
273 | 1.32k | else if (o) |
274 | 1.32k | { |
275 | | // We could get problems here if both *o and *l are larger than 2**254 |
276 | | // but it is probably ok for the optimizer to produce wrong code for such cases |
277 | | // which cannot be executed anyway because of the non-payable price. |
278 | 1.32k | if (u2s(*o) <= -32) |
279 | 6 | knownToBeIndependent = true; |
280 | 1.32k | else if (l && u2s(*o) >= 0 && *o >= *l) |
281 | 48 | knownToBeIndependent = true; |
282 | 1.32k | } |
283 | 1.32k | break; |
284 | 0 | } |
285 | 0 | default: |
286 | 0 | break; |
287 | 21.8k | } |
288 | 21.8k | if (knownToBeIndependent) |
289 | 1.02k | continue; |
290 | | |
291 | | // note that store and load never have the same sequence number |
292 | 20.8k | Id latestStore = storeOps.front().expression; |
293 | 21.1k | for (auto it = ++storeOps.begin(); it != storeOps.end(); ++it) |
294 | 249 | if (it->sequenceNumber < expr.sequenceNumber) |
295 | 82 | latestStore = it->expression; |
296 | 20.8k | addDependencies(latestStore); |
297 | 20.8k | m_neededBy.insert(make_pair(latestStore, _c)); |
298 | 20.8k | } |
299 | 108k | } |
300 | 1.58M | } |
301 | | |
302 | | void CSECodeGenerator::generateClassElement(Id _c, bool _allowSequenced) |
303 | 2.24M | { |
304 | 2.24M | for (auto const& it: m_classPositions) |
305 | 11.6M | for (int p: it.second) |
306 | 7.83M | if (p > m_stackHeight) |
307 | 0 | { |
308 | 0 | assertThrow(false, OptimizerException, ""); |
309 | 0 | } |
310 | | // do some cleanup |
311 | 2.24M | removeStackTopIfPossible(); |
312 | | |
313 | 2.24M | if (m_classPositions.count(_c)) |
314 | 794k | { |
315 | 794k | assertThrow( |
316 | 794k | !m_classPositions[_c].empty(), |
317 | 794k | OptimizerException, |
318 | 794k | "Element already removed but still needed." |
319 | 794k | ); |
320 | 794k | return; |
321 | 794k | } |
322 | 1.45M | ExpressionClasses::Expression const& expr = m_expressionClasses.representative(_c); |
323 | 1.45M | assertThrow( |
324 | 1.45M | _allowSequenced || expr.sequenceNumber == 0, |
325 | 1.45M | OptimizerException, |
326 | 1.45M | "Sequence constrained operation requested out of sequence." |
327 | 1.45M | ); |
328 | 1.45M | assertThrow(expr.item, OptimizerException, "Non-generated expression without item."); |
329 | 1.45M | assertThrow( |
330 | 1.45M | expr.item->type() != UndefinedItem, |
331 | 1.45M | OptimizerException, |
332 | 1.45M | "Undefined item requested but not available." |
333 | 1.45M | ); |
334 | 1.45M | vector<Id> const& arguments = expr.arguments; |
335 | 1.45M | for (Id arg: arguments | ranges::views::reverse) |
336 | 1.10M | generateClassElement(arg); |
337 | | |
338 | 1.45M | SourceLocation const& itemLocation = expr.item->location(); |
339 | | // The arguments are somewhere on the stack now, so it remains to move them at the correct place. |
340 | | // This is quite difficult as sometimes, the values also have to removed in this process |
341 | | // (if canBeRemoved() returns true) and the two arguments can be equal. For now, this is |
342 | | // implemented for every single case for combinations of up to two arguments manually. |
343 | 1.45M | if (arguments.size() == 1) |
344 | 190k | { |
345 | 190k | if (canBeRemoved(arguments[0], _c)) |
346 | 110k | appendOrRemoveSwap(classElementPosition(arguments[0]), itemLocation); |
347 | 80.0k | else |
348 | 80.0k | appendDup(classElementPosition(arguments[0]), itemLocation); |
349 | 190k | } |
350 | 1.26M | else if (arguments.size() == 2) |
351 | 458k | { |
352 | 458k | if (canBeRemoved(arguments[1], _c)) |
353 | 327k | { |
354 | 327k | appendOrRemoveSwap(classElementPosition(arguments[1]), itemLocation); |
355 | 327k | if (arguments[0] == arguments[1]) |
356 | 2.29k | appendDup(m_stackHeight, itemLocation); |
357 | 325k | else if (canBeRemoved(arguments[0], _c)) |
358 | 180k | { |
359 | 180k | appendOrRemoveSwap(m_stackHeight - 1, itemLocation); |
360 | 180k | appendOrRemoveSwap(classElementPosition(arguments[0]), itemLocation); |
361 | 180k | } |
362 | 145k | else |
363 | 145k | appendDup(classElementPosition(arguments[0]), itemLocation); |
364 | 327k | } |
365 | 130k | else |
366 | 130k | { |
367 | 130k | if (arguments[0] == arguments[1]) |
368 | 43 | { |
369 | 43 | appendDup(classElementPosition(arguments[0]), itemLocation); |
370 | 43 | appendDup(m_stackHeight, itemLocation); |
371 | 43 | } |
372 | 130k | else if (canBeRemoved(arguments[0], _c)) |
373 | 47.5k | { |
374 | 47.5k | appendOrRemoveSwap(classElementPosition(arguments[0]), itemLocation); |
375 | 47.5k | appendDup(classElementPosition(arguments[1]), itemLocation); |
376 | 47.5k | appendOrRemoveSwap(m_stackHeight - 1, itemLocation); |
377 | 47.5k | } |
378 | 83.3k | else |
379 | 83.3k | { |
380 | 83.3k | appendDup(classElementPosition(arguments[1]), itemLocation); |
381 | 83.3k | appendDup(classElementPosition(arguments[0]), itemLocation); |
382 | 83.3k | } |
383 | 130k | } |
384 | 458k | } |
385 | 804k | else |
386 | 804k | assertThrow( |
387 | 1.45M | arguments.size() <= 2, |
388 | 1.45M | OptimizerException, |
389 | 1.45M | "Opcodes with more than two arguments not implemented yet." |
390 | 1.45M | ); |
391 | 2.56M | for (size_t i = 0; i < arguments.size(); ++i) |
392 | 1.45M | assertThrow( |
393 | 1.45M | m_stack[m_stackHeight - static_cast<int>(i)] == arguments[i], |
394 | 1.45M | OptimizerException, |
395 | 1.45M | "Expected arguments not present." |
396 | 1.45M | ); |
397 | | |
398 | 1.52M | while (SemanticInformation::isCommutativeOperation(*expr.item) && |
399 | 1.52M | !m_generatedItems.empty() && |
400 | 1.52M | m_generatedItems.back() == AssemblyItem(Instruction::SWAP1)) |
401 | | // this will not append a swap but remove the one that is already there |
402 | 69.7k | appendOrRemoveSwap(m_stackHeight - 1, itemLocation); |
403 | 2.56M | for (size_t i = 0; i < arguments.size(); ++i) |
404 | 1.10M | { |
405 | 1.10M | m_classPositions[m_stack[m_stackHeight - static_cast<int>(i)]].erase(m_stackHeight - static_cast<int>(i)); |
406 | 1.10M | m_stack.erase(m_stackHeight - static_cast<int>(i)); |
407 | 1.10M | } |
408 | 1.45M | appendItem(*expr.item); |
409 | 1.45M | if (expr.item->type() != Operation || instructionInfo(expr.item->instruction()).ret == 1) |
410 | 1.35M | { |
411 | 1.35M | m_stack[m_stackHeight] = _c; |
412 | 1.35M | m_classPositions[_c].insert(m_stackHeight); |
413 | 1.35M | } |
414 | 96.6k | else |
415 | 96.6k | { |
416 | 96.6k | assertThrow( |
417 | 96.6k | instructionInfo(expr.item->instruction()).ret == 0, |
418 | 96.6k | OptimizerException, |
419 | 96.6k | "Invalid number of return values." |
420 | 96.6k | ); |
421 | 96.6k | m_classPositions[_c]; // ensure it is created to mark the expression as generated |
422 | 96.6k | } |
423 | 1.45M | } |
424 | | |
425 | | int CSECodeGenerator::classElementPosition(Id _id) const |
426 | 2.54M | { |
427 | 2.54M | assertThrow( |
428 | 2.54M | m_classPositions.count(_id) && !m_classPositions.at(_id).empty(), |
429 | 2.54M | OptimizerException, |
430 | 2.54M | "Element requested but is not present." |
431 | 2.54M | ); |
432 | 2.54M | return *max_element(m_classPositions.at(_id).begin(), m_classPositions.at(_id).end()); |
433 | 2.54M | } |
434 | | |
435 | | bool CSECodeGenerator::canBeRemoved(Id _element, Id _result, int _fromPosition) |
436 | 3.59M | { |
437 | | // Default for _fromPosition is the canonical position of the element. |
438 | 3.59M | if (_fromPosition == c_invalidPosition) |
439 | 1.10M | _fromPosition = classElementPosition(_element); |
440 | | |
441 | 3.59M | bool haveCopy = m_classPositions.at(_element).size() > 1; |
442 | 3.59M | if (m_finalClasses.count(_element)) |
443 | | // It is part of the target stack. It can be removed if it is a copy that is not in the target position. |
444 | 1.87M | return haveCopy && (!m_targetStack.count(_fromPosition) || m_targetStack[_fromPosition] != _element); |
445 | 1.72M | else if (!haveCopy) |
446 | 1.72M | { |
447 | | // Can be removed unless it is needed by a class that has not been computed yet. |
448 | | // Note that m_classPositions also includes classes that were deleted in the meantime. |
449 | 1.72M | auto range = m_neededBy.equal_range(_element); |
450 | 2.61M | for (auto it = range.first; it != range.second; ++it) |
451 | 1.77M | if (it->second != _result && !m_classPositions.count(it->second)) |
452 | 882k | return false; |
453 | 1.72M | } |
454 | 841k | return true; |
455 | 3.59M | } |
456 | | |
457 | | bool CSECodeGenerator::removeStackTopIfPossible() |
458 | 2.93M | { |
459 | 2.93M | if (m_stack.empty()) |
460 | 445k | return false; |
461 | 2.49M | assertThrow(m_stack.count(m_stackHeight) > 0, OptimizerException, ""); |
462 | 2.49M | Id top = m_stack[m_stackHeight]; |
463 | 2.49M | if (!canBeRemoved(top, Id(-1), m_stackHeight)) |
464 | 2.31M | return false; |
465 | 176k | m_classPositions[m_stack[m_stackHeight]].erase(m_stackHeight); |
466 | 176k | m_stack.erase(m_stackHeight); |
467 | 176k | appendItem(AssemblyItem(Instruction::POP)); |
468 | 176k | return true; |
469 | 2.49M | } |
470 | | |
471 | | void CSECodeGenerator::appendDup(int _fromPosition, SourceLocation const& _location) |
472 | 563k | { |
473 | 563k | assertThrow(_fromPosition != c_invalidPosition, OptimizerException, ""); |
474 | 563k | int instructionNum = 1 + m_stackHeight - _fromPosition; |
475 | 563k | assertThrow(instructionNum <= 16, StackTooDeepException, util::stackTooDeepString); |
476 | 563k | assertThrow(1 <= instructionNum, OptimizerException, "Invalid stack access."); |
477 | 563k | appendItem(AssemblyItem(dupInstruction(static_cast<unsigned>(instructionNum)), _location)); |
478 | 563k | m_stack[m_stackHeight] = m_stack[_fromPosition]; |
479 | 563k | m_classPositions[m_stack[m_stackHeight]].insert(m_stackHeight); |
480 | 563k | } |
481 | | |
482 | | void CSECodeGenerator::appendOrRemoveSwap(int _fromPosition, SourceLocation const& _location) |
483 | 1.51M | { |
484 | 1.51M | assertThrow(_fromPosition != c_invalidPosition, OptimizerException, ""); |
485 | 1.51M | if (_fromPosition == m_stackHeight) |
486 | 710k | return; |
487 | 809k | int instructionNum = m_stackHeight - _fromPosition; |
488 | 809k | assertThrow(instructionNum <= 16, StackTooDeepException, util::stackTooDeepString); |
489 | 809k | assertThrow(1 <= instructionNum, OptimizerException, "Invalid stack access."); |
490 | 809k | appendItem(AssemblyItem(swapInstruction(static_cast<unsigned>(instructionNum)), _location)); |
491 | | |
492 | 809k | if (m_stack[m_stackHeight] != m_stack[_fromPosition]) |
493 | 809k | { |
494 | 809k | m_classPositions[m_stack[m_stackHeight]].erase(m_stackHeight); |
495 | 809k | m_classPositions[m_stack[m_stackHeight]].insert(_fromPosition); |
496 | 809k | m_classPositions[m_stack[_fromPosition]].erase(_fromPosition); |
497 | 809k | m_classPositions[m_stack[_fromPosition]].insert(m_stackHeight); |
498 | 809k | swap(m_stack[m_stackHeight], m_stack[_fromPosition]); |
499 | 809k | } |
500 | 809k | if (m_generatedItems.size() >= 2 && |
501 | 809k | SemanticInformation::isSwapInstruction(m_generatedItems.back()) && |
502 | 809k | *(m_generatedItems.end() - 2) == m_generatedItems.back()) |
503 | 154k | { |
504 | 154k | m_generatedItems.pop_back(); |
505 | 154k | m_generatedItems.pop_back(); |
506 | 154k | } |
507 | 809k | } |
508 | | |
509 | | void CSECodeGenerator::appendItem(AssemblyItem const& _item) |
510 | 3.00M | { |
511 | 3.00M | m_generatedItems.push_back(_item); |
512 | 3.00M | m_stackHeight += static_cast<int>(_item.deposit()); |
513 | 3.00M | } |