Coverage Report

Created: 2022-08-24 06:43

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