Coverage Report

Created: 2022-08-24 06:38

/src/solidity/libevmasm/PeepholeOptimiser.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 PeepholeOptimiser.cpp
20
 * Performs local optimising code changes to assembly.
21
 */
22
23
#include <libevmasm/PeepholeOptimiser.h>
24
25
#include <libevmasm/AssemblyItem.h>
26
#include <libevmasm/SemanticInformation.h>
27
28
using namespace std;
29
using namespace solidity;
30
using namespace solidity::evmasm;
31
32
// TODO: Extend this to use the tools from ExpressionClasses.cpp
33
34
namespace
35
{
36
37
struct OptimiserState
38
{
39
  AssemblyItems const& items;
40
  size_t i;
41
  std::back_insert_iterator<AssemblyItems> out;
42
};
43
44
template<typename FunctionType>
45
struct FunctionParameterCount;
46
template<typename R, typename... Args>
47
struct FunctionParameterCount<R(Args...)>
48
{
49
  static constexpr auto value = sizeof...(Args);
50
};
51
52
template <class Method>
53
struct SimplePeepholeOptimizerMethod
54
{
55
  template <size_t... Indices>
56
  static bool applyRule(AssemblyItems::const_iterator _in, back_insert_iterator<AssemblyItems> _out, index_sequence<Indices...>)
57
0
  {
58
0
    return Method::applySimple(_in[Indices]..., _out);
59
0
  }
Unexecuted instantiation: PeepholeOptimiser.cpp:bool (anonymous namespace)::SimplePeepholeOptimizerMethod<(anonymous namespace)::PushPop>::applyRule<0ul, 1ul>(std::__1::__wrap_iter<solidity::evmasm::AssemblyItem const*>, std::__1::back_insert_iterator<std::__1::vector<solidity::evmasm::AssemblyItem, std::__1::allocator<solidity::evmasm::AssemblyItem> > >, std::__1::integer_sequence<unsigned long, 0ul, 1ul>)
Unexecuted instantiation: PeepholeOptimiser.cpp:bool (anonymous namespace)::SimplePeepholeOptimizerMethod<(anonymous namespace)::OpPop>::applyRule<0ul, 1ul>(std::__1::__wrap_iter<solidity::evmasm::AssemblyItem const*>, std::__1::back_insert_iterator<std::__1::vector<solidity::evmasm::AssemblyItem, std::__1::allocator<solidity::evmasm::AssemblyItem> > >, std::__1::integer_sequence<unsigned long, 0ul, 1ul>)
Unexecuted instantiation: PeepholeOptimiser.cpp:bool (anonymous namespace)::SimplePeepholeOptimizerMethod<(anonymous namespace)::OpStop>::applyRule<0ul, 1ul>(std::__1::__wrap_iter<solidity::evmasm::AssemblyItem const*>, std::__1::back_insert_iterator<std::__1::vector<solidity::evmasm::AssemblyItem, std::__1::allocator<solidity::evmasm::AssemblyItem> > >, std::__1::integer_sequence<unsigned long, 0ul, 1ul>)
Unexecuted instantiation: PeepholeOptimiser.cpp:bool (anonymous namespace)::SimplePeepholeOptimizerMethod<(anonymous namespace)::OpReturnRevert>::applyRule<0ul, 1ul, 2ul, 3ul>(std::__1::__wrap_iter<solidity::evmasm::AssemblyItem const*>, std::__1::back_insert_iterator<std::__1::vector<solidity::evmasm::AssemblyItem, std::__1::allocator<solidity::evmasm::AssemblyItem> > >, std::__1::integer_sequence<unsigned long, 0ul, 1ul, 2ul, 3ul>)
Unexecuted instantiation: PeepholeOptimiser.cpp:bool (anonymous namespace)::SimplePeepholeOptimizerMethod<(anonymous namespace)::DoublePush>::applyRule<0ul, 1ul>(std::__1::__wrap_iter<solidity::evmasm::AssemblyItem const*>, std::__1::back_insert_iterator<std::__1::vector<solidity::evmasm::AssemblyItem, std::__1::allocator<solidity::evmasm::AssemblyItem> > >, std::__1::integer_sequence<unsigned long, 0ul, 1ul>)
Unexecuted instantiation: PeepholeOptimiser.cpp:bool (anonymous namespace)::SimplePeepholeOptimizerMethod<(anonymous namespace)::DoubleSwap>::applyRule<0ul, 1ul>(std::__1::__wrap_iter<solidity::evmasm::AssemblyItem const*>, std::__1::back_insert_iterator<std::__1::vector<solidity::evmasm::AssemblyItem, std::__1::allocator<solidity::evmasm::AssemblyItem> > >, std::__1::integer_sequence<unsigned long, 0ul, 1ul>)
Unexecuted instantiation: PeepholeOptimiser.cpp:bool (anonymous namespace)::SimplePeepholeOptimizerMethod<(anonymous namespace)::CommutativeSwap>::applyRule<0ul, 1ul>(std::__1::__wrap_iter<solidity::evmasm::AssemblyItem const*>, std::__1::back_insert_iterator<std::__1::vector<solidity::evmasm::AssemblyItem, std::__1::allocator<solidity::evmasm::AssemblyItem> > >, std::__1::integer_sequence<unsigned long, 0ul, 1ul>)
Unexecuted instantiation: PeepholeOptimiser.cpp:bool (anonymous namespace)::SimplePeepholeOptimizerMethod<(anonymous namespace)::SwapComparison>::applyRule<0ul, 1ul>(std::__1::__wrap_iter<solidity::evmasm::AssemblyItem const*>, std::__1::back_insert_iterator<std::__1::vector<solidity::evmasm::AssemblyItem, std::__1::allocator<solidity::evmasm::AssemblyItem> > >, std::__1::integer_sequence<unsigned long, 0ul, 1ul>)
Unexecuted instantiation: PeepholeOptimiser.cpp:bool (anonymous namespace)::SimplePeepholeOptimizerMethod<(anonymous namespace)::DupSwap>::applyRule<0ul, 1ul>(std::__1::__wrap_iter<solidity::evmasm::AssemblyItem const*>, std::__1::back_insert_iterator<std::__1::vector<solidity::evmasm::AssemblyItem, std::__1::allocator<solidity::evmasm::AssemblyItem> > >, std::__1::integer_sequence<unsigned long, 0ul, 1ul>)
Unexecuted instantiation: PeepholeOptimiser.cpp:bool (anonymous namespace)::SimplePeepholeOptimizerMethod<(anonymous namespace)::IsZeroIsZeroJumpI>::applyRule<0ul, 1ul, 2ul, 3ul>(std::__1::__wrap_iter<solidity::evmasm::AssemblyItem const*>, std::__1::back_insert_iterator<std::__1::vector<solidity::evmasm::AssemblyItem, std::__1::allocator<solidity::evmasm::AssemblyItem> > >, std::__1::integer_sequence<unsigned long, 0ul, 1ul, 2ul, 3ul>)
Unexecuted instantiation: PeepholeOptimiser.cpp:bool (anonymous namespace)::SimplePeepholeOptimizerMethod<(anonymous namespace)::EqIsZeroJumpI>::applyRule<0ul, 1ul, 2ul, 3ul>(std::__1::__wrap_iter<solidity::evmasm::AssemblyItem const*>, std::__1::back_insert_iterator<std::__1::vector<solidity::evmasm::AssemblyItem, std::__1::allocator<solidity::evmasm::AssemblyItem> > >, std::__1::integer_sequence<unsigned long, 0ul, 1ul, 2ul, 3ul>)
Unexecuted instantiation: PeepholeOptimiser.cpp:bool (anonymous namespace)::SimplePeepholeOptimizerMethod<(anonymous namespace)::DoubleJump>::applyRule<0ul, 1ul, 2ul, 3ul, 4ul>(std::__1::__wrap_iter<solidity::evmasm::AssemblyItem const*>, std::__1::back_insert_iterator<std::__1::vector<solidity::evmasm::AssemblyItem, std::__1::allocator<solidity::evmasm::AssemblyItem> > >, std::__1::integer_sequence<unsigned long, 0ul, 1ul, 2ul, 3ul, 4ul>)
Unexecuted instantiation: PeepholeOptimiser.cpp:bool (anonymous namespace)::SimplePeepholeOptimizerMethod<(anonymous namespace)::JumpToNext>::applyRule<0ul, 1ul, 2ul>(std::__1::__wrap_iter<solidity::evmasm::AssemblyItem const*>, std::__1::back_insert_iterator<std::__1::vector<solidity::evmasm::AssemblyItem, std::__1::allocator<solidity::evmasm::AssemblyItem> > >, std::__1::integer_sequence<unsigned long, 0ul, 1ul, 2ul>)
Unexecuted instantiation: PeepholeOptimiser.cpp:bool (anonymous namespace)::SimplePeepholeOptimizerMethod<(anonymous namespace)::TagConjunctions>::applyRule<0ul, 1ul, 2ul>(std::__1::__wrap_iter<solidity::evmasm::AssemblyItem const*>, std::__1::back_insert_iterator<std::__1::vector<solidity::evmasm::AssemblyItem, std::__1::allocator<solidity::evmasm::AssemblyItem> > >, std::__1::integer_sequence<unsigned long, 0ul, 1ul, 2ul>)
Unexecuted instantiation: PeepholeOptimiser.cpp:bool (anonymous namespace)::SimplePeepholeOptimizerMethod<(anonymous namespace)::TruthyAnd>::applyRule<0ul, 1ul, 2ul>(std::__1::__wrap_iter<solidity::evmasm::AssemblyItem const*>, std::__1::back_insert_iterator<std::__1::vector<solidity::evmasm::AssemblyItem, std::__1::allocator<solidity::evmasm::AssemblyItem> > >, std::__1::integer_sequence<unsigned long, 0ul, 1ul, 2ul>)
Unexecuted instantiation: PeepholeOptimiser.cpp:bool (anonymous namespace)::SimplePeepholeOptimizerMethod<(anonymous namespace)::Identity>::applyRule<0ul>(std::__1::__wrap_iter<solidity::evmasm::AssemblyItem const*>, std::__1::back_insert_iterator<std::__1::vector<solidity::evmasm::AssemblyItem, std::__1::allocator<solidity::evmasm::AssemblyItem> > >, std::__1::integer_sequence<unsigned long, 0ul>)
60
  static bool apply(OptimiserState& _state)
61
0
  {
62
0
    static constexpr size_t WindowSize = FunctionParameterCount<decltype(Method::applySimple)>::value - 1;
63
0
    if (
64
0
      _state.i + WindowSize <= _state.items.size() &&
65
0
      applyRule(_state.items.begin() + static_cast<ptrdiff_t>(_state.i), _state.out, make_index_sequence<WindowSize>{})
66
0
    )
67
0
    {
68
0
      _state.i += WindowSize;
69
0
      return true;
70
0
    }
71
0
    else
72
0
      return false;
73
0
  }
Unexecuted instantiation: PeepholeOptimiser.cpp:(anonymous namespace)::SimplePeepholeOptimizerMethod<(anonymous namespace)::PushPop>::apply((anonymous namespace)::OptimiserState&)
Unexecuted instantiation: PeepholeOptimiser.cpp:(anonymous namespace)::SimplePeepholeOptimizerMethod<(anonymous namespace)::OpPop>::apply((anonymous namespace)::OptimiserState&)
Unexecuted instantiation: PeepholeOptimiser.cpp:(anonymous namespace)::SimplePeepholeOptimizerMethod<(anonymous namespace)::OpStop>::apply((anonymous namespace)::OptimiserState&)
Unexecuted instantiation: PeepholeOptimiser.cpp:(anonymous namespace)::SimplePeepholeOptimizerMethod<(anonymous namespace)::OpReturnRevert>::apply((anonymous namespace)::OptimiserState&)
Unexecuted instantiation: PeepholeOptimiser.cpp:(anonymous namespace)::SimplePeepholeOptimizerMethod<(anonymous namespace)::DoublePush>::apply((anonymous namespace)::OptimiserState&)
Unexecuted instantiation: PeepholeOptimiser.cpp:(anonymous namespace)::SimplePeepholeOptimizerMethod<(anonymous namespace)::DoubleSwap>::apply((anonymous namespace)::OptimiserState&)
Unexecuted instantiation: PeepholeOptimiser.cpp:(anonymous namespace)::SimplePeepholeOptimizerMethod<(anonymous namespace)::CommutativeSwap>::apply((anonymous namespace)::OptimiserState&)
Unexecuted instantiation: PeepholeOptimiser.cpp:(anonymous namespace)::SimplePeepholeOptimizerMethod<(anonymous namespace)::SwapComparison>::apply((anonymous namespace)::OptimiserState&)
Unexecuted instantiation: PeepholeOptimiser.cpp:(anonymous namespace)::SimplePeepholeOptimizerMethod<(anonymous namespace)::DupSwap>::apply((anonymous namespace)::OptimiserState&)
Unexecuted instantiation: PeepholeOptimiser.cpp:(anonymous namespace)::SimplePeepholeOptimizerMethod<(anonymous namespace)::IsZeroIsZeroJumpI>::apply((anonymous namespace)::OptimiserState&)
Unexecuted instantiation: PeepholeOptimiser.cpp:(anonymous namespace)::SimplePeepholeOptimizerMethod<(anonymous namespace)::EqIsZeroJumpI>::apply((anonymous namespace)::OptimiserState&)
Unexecuted instantiation: PeepholeOptimiser.cpp:(anonymous namespace)::SimplePeepholeOptimizerMethod<(anonymous namespace)::DoubleJump>::apply((anonymous namespace)::OptimiserState&)
Unexecuted instantiation: PeepholeOptimiser.cpp:(anonymous namespace)::SimplePeepholeOptimizerMethod<(anonymous namespace)::JumpToNext>::apply((anonymous namespace)::OptimiserState&)
Unexecuted instantiation: PeepholeOptimiser.cpp:(anonymous namespace)::SimplePeepholeOptimizerMethod<(anonymous namespace)::TagConjunctions>::apply((anonymous namespace)::OptimiserState&)
Unexecuted instantiation: PeepholeOptimiser.cpp:(anonymous namespace)::SimplePeepholeOptimizerMethod<(anonymous namespace)::TruthyAnd>::apply((anonymous namespace)::OptimiserState&)
Unexecuted instantiation: PeepholeOptimiser.cpp:(anonymous namespace)::SimplePeepholeOptimizerMethod<(anonymous namespace)::Identity>::apply((anonymous namespace)::OptimiserState&)
74
};
75
76
struct Identity: SimplePeepholeOptimizerMethod<Identity>
77
{
78
  static bool applySimple(AssemblyItem const& _item, std::back_insert_iterator<AssemblyItems> _out)
79
0
  {
80
0
    *_out = _item;
81
0
    return true;
82
0
  }
83
};
84
85
struct PushPop: SimplePeepholeOptimizerMethod<PushPop>
86
{
87
  static bool applySimple(AssemblyItem const& _push, AssemblyItem const& _pop, std::back_insert_iterator<AssemblyItems>)
88
0
  {
89
0
    auto t = _push.type();
90
0
    return _pop == Instruction::POP && (
91
0
      SemanticInformation::isDupInstruction(_push) ||
92
0
      t == Push || t == PushTag || t == PushSub ||
93
0
      t == PushSubSize || t == PushProgramSize || t == PushData || t == PushLibraryAddress
94
0
    );
95
0
  }
96
};
97
98
struct OpPop: SimplePeepholeOptimizerMethod<OpPop>
99
{
100
  static bool applySimple(
101
    AssemblyItem const& _op,
102
    AssemblyItem const& _pop,
103
    std::back_insert_iterator<AssemblyItems> _out
104
  )
105
0
  {
106
0
    if (_pop == Instruction::POP && _op.type() == Operation)
107
0
    {
108
0
      Instruction instr = _op.instruction();
109
0
      if (instructionInfo(instr).ret == 1 && !instructionInfo(instr).sideEffects)
110
0
      {
111
0
        for (int j = 0; j < instructionInfo(instr).args; j++)
112
0
          *_out = {Instruction::POP, _op.location()};
113
0
        return true;
114
0
      }
115
0
    }
116
0
    return false;
117
0
  }
118
};
119
120
struct OpStop: SimplePeepholeOptimizerMethod<OpStop>
121
{
122
  static bool applySimple(
123
    AssemblyItem const& _op,
124
    AssemblyItem const& _stop,
125
    std::back_insert_iterator<AssemblyItems> _out
126
  )
127
0
  {
128
0
    if (_stop == Instruction::STOP)
129
0
    {
130
0
      if (_op.type() == Operation)
131
0
      {
132
0
        Instruction instr = _op.instruction();
133
0
        if (!instructionInfo(instr).sideEffects)
134
0
        {
135
0
          *_out = {Instruction::STOP, _op.location()};
136
0
          return true;
137
0
        }
138
0
      }
139
0
      else if (_op.type() == Push)
140
0
      {
141
0
        *_out = {Instruction::STOP, _op.location()};
142
0
        return true;
143
0
      }
144
0
    }
145
0
    return false;
146
0
  }
147
};
148
149
struct OpReturnRevert: SimplePeepholeOptimizerMethod<OpReturnRevert>
150
{
151
  static bool applySimple(
152
    AssemblyItem const& _op,
153
    AssemblyItem const& _push,
154
    AssemblyItem const& _pushOrDup,
155
    AssemblyItem const& _returnRevert,
156
    std::back_insert_iterator<AssemblyItems> _out
157
  )
158
0
  {
159
0
    if (
160
0
      (_returnRevert == Instruction::RETURN || _returnRevert == Instruction::REVERT) &&
161
0
      _push.type() == Push &&
162
0
      (_pushOrDup.type() == Push || _pushOrDup == dupInstruction(1))
163
0
    )
164
0
      if (
165
0
        (_op.type() == Operation && !instructionInfo(_op.instruction()).sideEffects) ||
166
0
        _op.type() == Push
167
0
      )
168
0
      {
169
0
          *_out = _push;
170
0
          *_out = _pushOrDup;
171
0
          *_out = _returnRevert;
172
0
          return true;
173
0
      }
174
0
    return false;
175
0
  }
176
};
177
178
struct DoubleSwap: SimplePeepholeOptimizerMethod<DoubleSwap>
179
{
180
  static size_t applySimple(AssemblyItem const& _s1, AssemblyItem const& _s2, std::back_insert_iterator<AssemblyItems>)
181
0
  {
182
0
    return _s1 == _s2 && SemanticInformation::isSwapInstruction(_s1);
183
0
  }
184
};
185
186
struct DoublePush: SimplePeepholeOptimizerMethod<DoublePush>
187
{
188
  static bool applySimple(AssemblyItem const& _push1, AssemblyItem const& _push2, std::back_insert_iterator<AssemblyItems> _out)
189
0
  {
190
0
    if (_push1.type() == Push && _push2.type() == Push && _push1.data() == _push2.data())
191
0
    {
192
0
      *_out = _push1;
193
0
      *_out = {Instruction::DUP1, _push2.location()};
194
0
      return true;
195
0
    }
196
0
    else
197
0
      return false;
198
0
  }
199
};
200
201
struct CommutativeSwap: SimplePeepholeOptimizerMethod<CommutativeSwap>
202
{
203
  static bool applySimple(AssemblyItem const& _swap, AssemblyItem const& _op, std::back_insert_iterator<AssemblyItems> _out)
204
0
  {
205
    // Remove SWAP1 if following instruction is commutative
206
0
    if (
207
0
      _swap == Instruction::SWAP1 &&
208
0
      SemanticInformation::isCommutativeOperation(_op)
209
0
    )
210
0
    {
211
0
      *_out = _op;
212
0
      return true;
213
0
    }
214
0
    else
215
0
      return false;
216
0
  }
217
};
218
219
struct SwapComparison: SimplePeepholeOptimizerMethod<SwapComparison>
220
{
221
  static bool applySimple(AssemblyItem const& _swap, AssemblyItem const& _op, std::back_insert_iterator<AssemblyItems> _out)
222
0
  {
223
0
    static map<Instruction, Instruction> const swappableOps{
224
0
      { Instruction::LT, Instruction::GT },
225
0
      { Instruction::GT, Instruction::LT },
226
0
      { Instruction::SLT, Instruction::SGT },
227
0
      { Instruction::SGT, Instruction::SLT }
228
0
    };
229
230
0
    if (
231
0
      _swap == Instruction::SWAP1 &&
232
0
      _op.type() == Operation &&
233
0
      swappableOps.count(_op.instruction())
234
0
    )
235
0
    {
236
0
      *_out = swappableOps.at(_op.instruction());
237
0
      return true;
238
0
    }
239
0
    else
240
0
      return false;
241
0
  }
242
};
243
244
/// Remove swapN after dupN
245
struct DupSwap: SimplePeepholeOptimizerMethod<DupSwap>
246
{
247
  static size_t applySimple(
248
    AssemblyItem const& _dupN,
249
    AssemblyItem const& _swapN,
250
    std::back_insert_iterator<AssemblyItems> _out
251
  )
252
0
  {
253
0
    if (
254
0
      SemanticInformation::isDupInstruction(_dupN) &&
255
0
      SemanticInformation::isSwapInstruction(_swapN) &&
256
0
      getDupNumber(_dupN.instruction()) == getSwapNumber(_swapN.instruction())
257
0
    )
258
0
    {
259
0
      *_out = _dupN;
260
0
      return true;
261
0
    }
262
0
    else
263
0
      return false;
264
0
  }
265
};
266
267
268
struct IsZeroIsZeroJumpI: SimplePeepholeOptimizerMethod<IsZeroIsZeroJumpI>
269
{
270
  static size_t applySimple(
271
    AssemblyItem const& _iszero1,
272
    AssemblyItem const& _iszero2,
273
    AssemblyItem const& _pushTag,
274
    AssemblyItem const& _jumpi,
275
    std::back_insert_iterator<AssemblyItems> _out
276
  )
277
0
  {
278
0
    if (
279
0
      _iszero1 == Instruction::ISZERO &&
280
0
      _iszero2 == Instruction::ISZERO &&
281
0
      _pushTag.type() == PushTag &&
282
0
      _jumpi == Instruction::JUMPI
283
0
    )
284
0
    {
285
0
      *_out = _pushTag;
286
0
      *_out = _jumpi;
287
0
      return true;
288
0
    }
289
0
    else
290
0
      return false;
291
0
  }
292
};
293
294
struct EqIsZeroJumpI: SimplePeepholeOptimizerMethod<EqIsZeroJumpI>
295
{
296
  static size_t applySimple(
297
    AssemblyItem const& _eq,
298
    AssemblyItem const& _iszero,
299
    AssemblyItem const& _pushTag,
300
    AssemblyItem const& _jumpi,
301
    std::back_insert_iterator<AssemblyItems> _out
302
  )
303
0
  {
304
0
    if (
305
0
      _eq == Instruction::EQ &&
306
0
      _iszero == Instruction::ISZERO &&
307
0
      _pushTag.type() == PushTag &&
308
0
      _jumpi == Instruction::JUMPI
309
0
    )
310
0
    {
311
0
      *_out = AssemblyItem(Instruction::SUB, _eq.location());
312
0
      *_out = _pushTag;
313
0
      *_out = _jumpi;
314
0
      return true;
315
0
    }
316
0
    else
317
0
      return false;
318
0
  }
319
};
320
321
// push_tag_1 jumpi push_tag_2 jump tag_1: -> iszero push_tag_2 jumpi tag_1:
322
struct DoubleJump: SimplePeepholeOptimizerMethod<DoubleJump>
323
{
324
  static size_t applySimple(
325
    AssemblyItem const& _pushTag1,
326
    AssemblyItem const& _jumpi,
327
    AssemblyItem const& _pushTag2,
328
    AssemblyItem const& _jump,
329
    AssemblyItem const& _tag1,
330
    std::back_insert_iterator<AssemblyItems> _out
331
  )
332
0
  {
333
0
    if (
334
0
      _pushTag1.type() == PushTag &&
335
0
      _jumpi == Instruction::JUMPI &&
336
0
      _pushTag2.type() == PushTag &&
337
0
      _jump == Instruction::JUMP &&
338
0
      _tag1.type() == Tag &&
339
0
      _pushTag1.data() == _tag1.data()
340
0
    )
341
0
    {
342
0
      *_out = AssemblyItem(Instruction::ISZERO, _jumpi.location());
343
0
      *_out = _pushTag2;
344
0
      *_out = _jumpi;
345
0
      *_out = _tag1;
346
0
      return true;
347
0
    }
348
0
    else
349
0
      return false;
350
0
  }
351
};
352
353
struct JumpToNext: SimplePeepholeOptimizerMethod<JumpToNext>
354
{
355
  static size_t applySimple(
356
    AssemblyItem const& _pushTag,
357
    AssemblyItem const& _jump,
358
    AssemblyItem const& _tag,
359
    std::back_insert_iterator<AssemblyItems> _out
360
  )
361
0
  {
362
0
    if (
363
0
      _pushTag.type() == PushTag &&
364
0
      (_jump == Instruction::JUMP || _jump == Instruction::JUMPI) &&
365
0
      _tag.type() == Tag &&
366
0
      _pushTag.data() == _tag.data()
367
0
    )
368
0
    {
369
0
      if (_jump == Instruction::JUMPI)
370
0
        *_out = AssemblyItem(Instruction::POP, _jump.location());
371
0
      *_out = _tag;
372
0
      return true;
373
0
    }
374
0
    else
375
0
      return false;
376
0
  }
377
};
378
379
struct TagConjunctions: SimplePeepholeOptimizerMethod<TagConjunctions>
380
{
381
  static bool applySimple(
382
    AssemblyItem const& _pushTag,
383
    AssemblyItem const& _pushConstant,
384
    AssemblyItem const& _and,
385
    std::back_insert_iterator<AssemblyItems> _out
386
  )
387
0
  {
388
0
    if (_and != Instruction::AND)
389
0
      return false;
390
0
    if (
391
0
      _pushTag.type() == PushTag &&
392
0
      _pushConstant.type() == Push &&
393
0
      (_pushConstant.data() & u256(0xFFFFFFFF)) == u256(0xFFFFFFFF)
394
0
    )
395
0
    {
396
0
      *_out = _pushTag;
397
0
      return true;
398
0
    }
399
0
    else if (
400
      // tag and constant are swapped
401
0
      _pushConstant.type() == PushTag &&
402
0
      _pushTag.type() == Push &&
403
0
      (_pushTag.data() & u256(0xFFFFFFFF)) == u256(0xFFFFFFFF)
404
0
    )
405
0
    {
406
0
      *_out = _pushConstant;
407
0
      return true;
408
0
    }
409
0
    else
410
0
      return false;
411
0
  }
412
};
413
414
struct TruthyAnd: SimplePeepholeOptimizerMethod<TruthyAnd>
415
{
416
  static bool applySimple(
417
    AssemblyItem const& _push,
418
    AssemblyItem const& _not,
419
    AssemblyItem const& _and,
420
    std::back_insert_iterator<AssemblyItems>
421
  )
422
0
  {
423
0
    return (
424
0
      _push.type() == Push && _push.data() == 0 &&
425
0
      _not == Instruction::NOT &&
426
0
      _and == Instruction::AND
427
0
    );
428
0
  }
429
};
430
431
/// Removes everything after a JUMP (or similar) until the next JUMPDEST.
432
struct UnreachableCode
433
{
434
  static bool apply(OptimiserState& _state)
435
0
  {
436
0
    auto it = _state.items.begin() + static_cast<ptrdiff_t>(_state.i);
437
0
    auto end = _state.items.end();
438
0
    if (it == end)
439
0
      return false;
440
0
    if (
441
0
      it[0] != Instruction::JUMP &&
442
0
      it[0] != Instruction::RETURN &&
443
0
      it[0] != Instruction::STOP &&
444
0
      it[0] != Instruction::INVALID &&
445
0
      it[0] != Instruction::SELFDESTRUCT &&
446
0
      it[0] != Instruction::REVERT
447
0
    )
448
0
      return false;
449
450
0
    ptrdiff_t i = 1;
451
0
    while (it + i != end && it[i].type() != Tag)
452
0
      i++;
453
0
    if (i > 1)
454
0
    {
455
0
      *_state.out = it[0];
456
0
      _state.i += static_cast<size_t>(i);
457
0
      return true;
458
0
    }
459
0
    else
460
0
      return false;
461
0
  }
462
};
463
464
void applyMethods(OptimiserState&)
465
0
{
466
0
  assertThrow(false, OptimizerException, "Peephole optimizer failed to apply identity.");
467
0
}
468
469
template <typename Method, typename... OtherMethods>
470
void applyMethods(OptimiserState& _state, Method, OtherMethods... _other)
471
0
{
472
0
  if (!Method::apply(_state))
473
0
    applyMethods(_state, _other...);
474
0
}
Unexecuted instantiation: PeepholeOptimiser.cpp:void (anonymous namespace)::applyMethods<(anonymous namespace)::PushPop, (anonymous namespace)::OpPop, (anonymous namespace)::OpStop, (anonymous namespace)::OpReturnRevert, (anonymous namespace)::DoublePush, (anonymous namespace)::DoubleSwap, (anonymous namespace)::CommutativeSwap, (anonymous namespace)::SwapComparison, (anonymous namespace)::DupSwap, (anonymous namespace)::IsZeroIsZeroJumpI, (anonymous namespace)::EqIsZeroJumpI, (anonymous namespace)::DoubleJump, (anonymous namespace)::JumpToNext, (anonymous namespace)::UnreachableCode, (anonymous namespace)::TagConjunctions, (anonymous namespace)::TruthyAnd, (anonymous namespace)::Identity>((anonymous namespace)::OptimiserState&, (anonymous namespace)::PushPop, (anonymous namespace)::OpPop, (anonymous namespace)::OpStop, (anonymous namespace)::OpReturnRevert, (anonymous namespace)::DoublePush, (anonymous namespace)::DoubleSwap, (anonymous namespace)::CommutativeSwap, (anonymous namespace)::SwapComparison, (anonymous namespace)::DupSwap, (anonymous namespace)::IsZeroIsZeroJumpI, (anonymous namespace)::EqIsZeroJumpI, (anonymous namespace)::DoubleJump, (anonymous namespace)::JumpToNext, (anonymous namespace)::UnreachableCode, (anonymous namespace)::TagConjunctions, (anonymous namespace)::TruthyAnd, (anonymous namespace)::Identity)
Unexecuted instantiation: PeepholeOptimiser.cpp:void (anonymous namespace)::applyMethods<(anonymous namespace)::OpPop, (anonymous namespace)::OpStop, (anonymous namespace)::OpReturnRevert, (anonymous namespace)::DoublePush, (anonymous namespace)::DoubleSwap, (anonymous namespace)::CommutativeSwap, (anonymous namespace)::SwapComparison, (anonymous namespace)::DupSwap, (anonymous namespace)::IsZeroIsZeroJumpI, (anonymous namespace)::EqIsZeroJumpI, (anonymous namespace)::DoubleJump, (anonymous namespace)::JumpToNext, (anonymous namespace)::UnreachableCode, (anonymous namespace)::TagConjunctions, (anonymous namespace)::TruthyAnd, (anonymous namespace)::Identity>((anonymous namespace)::OptimiserState&, (anonymous namespace)::OpPop, (anonymous namespace)::OpStop, (anonymous namespace)::OpReturnRevert, (anonymous namespace)::DoublePush, (anonymous namespace)::DoubleSwap, (anonymous namespace)::CommutativeSwap, (anonymous namespace)::SwapComparison, (anonymous namespace)::DupSwap, (anonymous namespace)::IsZeroIsZeroJumpI, (anonymous namespace)::EqIsZeroJumpI, (anonymous namespace)::DoubleJump, (anonymous namespace)::JumpToNext, (anonymous namespace)::UnreachableCode, (anonymous namespace)::TagConjunctions, (anonymous namespace)::TruthyAnd, (anonymous namespace)::Identity)
Unexecuted instantiation: PeepholeOptimiser.cpp:void (anonymous namespace)::applyMethods<(anonymous namespace)::OpStop, (anonymous namespace)::OpReturnRevert, (anonymous namespace)::DoublePush, (anonymous namespace)::DoubleSwap, (anonymous namespace)::CommutativeSwap, (anonymous namespace)::SwapComparison, (anonymous namespace)::DupSwap, (anonymous namespace)::IsZeroIsZeroJumpI, (anonymous namespace)::EqIsZeroJumpI, (anonymous namespace)::DoubleJump, (anonymous namespace)::JumpToNext, (anonymous namespace)::UnreachableCode, (anonymous namespace)::TagConjunctions, (anonymous namespace)::TruthyAnd, (anonymous namespace)::Identity>((anonymous namespace)::OptimiserState&, (anonymous namespace)::OpStop, (anonymous namespace)::OpReturnRevert, (anonymous namespace)::DoublePush, (anonymous namespace)::DoubleSwap, (anonymous namespace)::CommutativeSwap, (anonymous namespace)::SwapComparison, (anonymous namespace)::DupSwap, (anonymous namespace)::IsZeroIsZeroJumpI, (anonymous namespace)::EqIsZeroJumpI, (anonymous namespace)::DoubleJump, (anonymous namespace)::JumpToNext, (anonymous namespace)::UnreachableCode, (anonymous namespace)::TagConjunctions, (anonymous namespace)::TruthyAnd, (anonymous namespace)::Identity)
Unexecuted instantiation: PeepholeOptimiser.cpp:void (anonymous namespace)::applyMethods<(anonymous namespace)::OpReturnRevert, (anonymous namespace)::DoublePush, (anonymous namespace)::DoubleSwap, (anonymous namespace)::CommutativeSwap, (anonymous namespace)::SwapComparison, (anonymous namespace)::DupSwap, (anonymous namespace)::IsZeroIsZeroJumpI, (anonymous namespace)::EqIsZeroJumpI, (anonymous namespace)::DoubleJump, (anonymous namespace)::JumpToNext, (anonymous namespace)::UnreachableCode, (anonymous namespace)::TagConjunctions, (anonymous namespace)::TruthyAnd, (anonymous namespace)::Identity>((anonymous namespace)::OptimiserState&, (anonymous namespace)::OpReturnRevert, (anonymous namespace)::DoublePush, (anonymous namespace)::DoubleSwap, (anonymous namespace)::CommutativeSwap, (anonymous namespace)::SwapComparison, (anonymous namespace)::DupSwap, (anonymous namespace)::IsZeroIsZeroJumpI, (anonymous namespace)::EqIsZeroJumpI, (anonymous namespace)::DoubleJump, (anonymous namespace)::JumpToNext, (anonymous namespace)::UnreachableCode, (anonymous namespace)::TagConjunctions, (anonymous namespace)::TruthyAnd, (anonymous namespace)::Identity)
Unexecuted instantiation: PeepholeOptimiser.cpp:void (anonymous namespace)::applyMethods<(anonymous namespace)::DoublePush, (anonymous namespace)::DoubleSwap, (anonymous namespace)::CommutativeSwap, (anonymous namespace)::SwapComparison, (anonymous namespace)::DupSwap, (anonymous namespace)::IsZeroIsZeroJumpI, (anonymous namespace)::EqIsZeroJumpI, (anonymous namespace)::DoubleJump, (anonymous namespace)::JumpToNext, (anonymous namespace)::UnreachableCode, (anonymous namespace)::TagConjunctions, (anonymous namespace)::TruthyAnd, (anonymous namespace)::Identity>((anonymous namespace)::OptimiserState&, (anonymous namespace)::DoublePush, (anonymous namespace)::DoubleSwap, (anonymous namespace)::CommutativeSwap, (anonymous namespace)::SwapComparison, (anonymous namespace)::DupSwap, (anonymous namespace)::IsZeroIsZeroJumpI, (anonymous namespace)::EqIsZeroJumpI, (anonymous namespace)::DoubleJump, (anonymous namespace)::JumpToNext, (anonymous namespace)::UnreachableCode, (anonymous namespace)::TagConjunctions, (anonymous namespace)::TruthyAnd, (anonymous namespace)::Identity)
Unexecuted instantiation: PeepholeOptimiser.cpp:void (anonymous namespace)::applyMethods<(anonymous namespace)::DoubleSwap, (anonymous namespace)::CommutativeSwap, (anonymous namespace)::SwapComparison, (anonymous namespace)::DupSwap, (anonymous namespace)::IsZeroIsZeroJumpI, (anonymous namespace)::EqIsZeroJumpI, (anonymous namespace)::DoubleJump, (anonymous namespace)::JumpToNext, (anonymous namespace)::UnreachableCode, (anonymous namespace)::TagConjunctions, (anonymous namespace)::TruthyAnd, (anonymous namespace)::Identity>((anonymous namespace)::OptimiserState&, (anonymous namespace)::DoubleSwap, (anonymous namespace)::CommutativeSwap, (anonymous namespace)::SwapComparison, (anonymous namespace)::DupSwap, (anonymous namespace)::IsZeroIsZeroJumpI, (anonymous namespace)::EqIsZeroJumpI, (anonymous namespace)::DoubleJump, (anonymous namespace)::JumpToNext, (anonymous namespace)::UnreachableCode, (anonymous namespace)::TagConjunctions, (anonymous namespace)::TruthyAnd, (anonymous namespace)::Identity)
Unexecuted instantiation: PeepholeOptimiser.cpp:void (anonymous namespace)::applyMethods<(anonymous namespace)::CommutativeSwap, (anonymous namespace)::SwapComparison, (anonymous namespace)::DupSwap, (anonymous namespace)::IsZeroIsZeroJumpI, (anonymous namespace)::EqIsZeroJumpI, (anonymous namespace)::DoubleJump, (anonymous namespace)::JumpToNext, (anonymous namespace)::UnreachableCode, (anonymous namespace)::TagConjunctions, (anonymous namespace)::TruthyAnd, (anonymous namespace)::Identity>((anonymous namespace)::OptimiserState&, (anonymous namespace)::CommutativeSwap, (anonymous namespace)::SwapComparison, (anonymous namespace)::DupSwap, (anonymous namespace)::IsZeroIsZeroJumpI, (anonymous namespace)::EqIsZeroJumpI, (anonymous namespace)::DoubleJump, (anonymous namespace)::JumpToNext, (anonymous namespace)::UnreachableCode, (anonymous namespace)::TagConjunctions, (anonymous namespace)::TruthyAnd, (anonymous namespace)::Identity)
Unexecuted instantiation: PeepholeOptimiser.cpp:void (anonymous namespace)::applyMethods<(anonymous namespace)::SwapComparison, (anonymous namespace)::DupSwap, (anonymous namespace)::IsZeroIsZeroJumpI, (anonymous namespace)::EqIsZeroJumpI, (anonymous namespace)::DoubleJump, (anonymous namespace)::JumpToNext, (anonymous namespace)::UnreachableCode, (anonymous namespace)::TagConjunctions, (anonymous namespace)::TruthyAnd, (anonymous namespace)::Identity>((anonymous namespace)::OptimiserState&, (anonymous namespace)::SwapComparison, (anonymous namespace)::DupSwap, (anonymous namespace)::IsZeroIsZeroJumpI, (anonymous namespace)::EqIsZeroJumpI, (anonymous namespace)::DoubleJump, (anonymous namespace)::JumpToNext, (anonymous namespace)::UnreachableCode, (anonymous namespace)::TagConjunctions, (anonymous namespace)::TruthyAnd, (anonymous namespace)::Identity)
Unexecuted instantiation: PeepholeOptimiser.cpp:void (anonymous namespace)::applyMethods<(anonymous namespace)::DupSwap, (anonymous namespace)::IsZeroIsZeroJumpI, (anonymous namespace)::EqIsZeroJumpI, (anonymous namespace)::DoubleJump, (anonymous namespace)::JumpToNext, (anonymous namespace)::UnreachableCode, (anonymous namespace)::TagConjunctions, (anonymous namespace)::TruthyAnd, (anonymous namespace)::Identity>((anonymous namespace)::OptimiserState&, (anonymous namespace)::DupSwap, (anonymous namespace)::IsZeroIsZeroJumpI, (anonymous namespace)::EqIsZeroJumpI, (anonymous namespace)::DoubleJump, (anonymous namespace)::JumpToNext, (anonymous namespace)::UnreachableCode, (anonymous namespace)::TagConjunctions, (anonymous namespace)::TruthyAnd, (anonymous namespace)::Identity)
Unexecuted instantiation: PeepholeOptimiser.cpp:void (anonymous namespace)::applyMethods<(anonymous namespace)::IsZeroIsZeroJumpI, (anonymous namespace)::EqIsZeroJumpI, (anonymous namespace)::DoubleJump, (anonymous namespace)::JumpToNext, (anonymous namespace)::UnreachableCode, (anonymous namespace)::TagConjunctions, (anonymous namespace)::TruthyAnd, (anonymous namespace)::Identity>((anonymous namespace)::OptimiserState&, (anonymous namespace)::IsZeroIsZeroJumpI, (anonymous namespace)::EqIsZeroJumpI, (anonymous namespace)::DoubleJump, (anonymous namespace)::JumpToNext, (anonymous namespace)::UnreachableCode, (anonymous namespace)::TagConjunctions, (anonymous namespace)::TruthyAnd, (anonymous namespace)::Identity)
Unexecuted instantiation: PeepholeOptimiser.cpp:void (anonymous namespace)::applyMethods<(anonymous namespace)::EqIsZeroJumpI, (anonymous namespace)::DoubleJump, (anonymous namespace)::JumpToNext, (anonymous namespace)::UnreachableCode, (anonymous namespace)::TagConjunctions, (anonymous namespace)::TruthyAnd, (anonymous namespace)::Identity>((anonymous namespace)::OptimiserState&, (anonymous namespace)::EqIsZeroJumpI, (anonymous namespace)::DoubleJump, (anonymous namespace)::JumpToNext, (anonymous namespace)::UnreachableCode, (anonymous namespace)::TagConjunctions, (anonymous namespace)::TruthyAnd, (anonymous namespace)::Identity)
Unexecuted instantiation: PeepholeOptimiser.cpp:void (anonymous namespace)::applyMethods<(anonymous namespace)::DoubleJump, (anonymous namespace)::JumpToNext, (anonymous namespace)::UnreachableCode, (anonymous namespace)::TagConjunctions, (anonymous namespace)::TruthyAnd, (anonymous namespace)::Identity>((anonymous namespace)::OptimiserState&, (anonymous namespace)::DoubleJump, (anonymous namespace)::JumpToNext, (anonymous namespace)::UnreachableCode, (anonymous namespace)::TagConjunctions, (anonymous namespace)::TruthyAnd, (anonymous namespace)::Identity)
Unexecuted instantiation: PeepholeOptimiser.cpp:void (anonymous namespace)::applyMethods<(anonymous namespace)::JumpToNext, (anonymous namespace)::UnreachableCode, (anonymous namespace)::TagConjunctions, (anonymous namespace)::TruthyAnd, (anonymous namespace)::Identity>((anonymous namespace)::OptimiserState&, (anonymous namespace)::JumpToNext, (anonymous namespace)::UnreachableCode, (anonymous namespace)::TagConjunctions, (anonymous namespace)::TruthyAnd, (anonymous namespace)::Identity)
Unexecuted instantiation: PeepholeOptimiser.cpp:void (anonymous namespace)::applyMethods<(anonymous namespace)::UnreachableCode, (anonymous namespace)::TagConjunctions, (anonymous namespace)::TruthyAnd, (anonymous namespace)::Identity>((anonymous namespace)::OptimiserState&, (anonymous namespace)::UnreachableCode, (anonymous namespace)::TagConjunctions, (anonymous namespace)::TruthyAnd, (anonymous namespace)::Identity)
Unexecuted instantiation: PeepholeOptimiser.cpp:void (anonymous namespace)::applyMethods<(anonymous namespace)::TagConjunctions, (anonymous namespace)::TruthyAnd, (anonymous namespace)::Identity>((anonymous namespace)::OptimiserState&, (anonymous namespace)::TagConjunctions, (anonymous namespace)::TruthyAnd, (anonymous namespace)::Identity)
Unexecuted instantiation: PeepholeOptimiser.cpp:void (anonymous namespace)::applyMethods<(anonymous namespace)::TruthyAnd, (anonymous namespace)::Identity>((anonymous namespace)::OptimiserState&, (anonymous namespace)::TruthyAnd, (anonymous namespace)::Identity)
Unexecuted instantiation: PeepholeOptimiser.cpp:void (anonymous namespace)::applyMethods<(anonymous namespace)::Identity>((anonymous namespace)::OptimiserState&, (anonymous namespace)::Identity)
475
476
size_t numberOfPops(AssemblyItems const& _items)
477
0
{
478
0
  return static_cast<size_t>(std::count(_items.begin(), _items.end(), Instruction::POP));
479
0
}
480
481
}
482
483
bool PeepholeOptimiser::optimise()
484
0
{
485
  // Avoid referencing immutables too early by using approx. counting in bytesRequired()
486
0
  auto const approx = evmasm::Precision::Approximate;
487
0
  OptimiserState state {m_items, 0, std::back_inserter(m_optimisedItems)};
488
0
  while (state.i < m_items.size())
489
0
    applyMethods(
490
0
      state,
491
0
      PushPop(), OpPop(), OpStop(), OpReturnRevert(), DoublePush(), DoubleSwap(), CommutativeSwap(), SwapComparison(),
492
0
      DupSwap(), IsZeroIsZeroJumpI(), EqIsZeroJumpI(), DoubleJump(), JumpToNext(), UnreachableCode(),
493
0
      TagConjunctions(), TruthyAnd(), Identity()
494
0
    );
495
0
  if (m_optimisedItems.size() < m_items.size() || (
496
0
    m_optimisedItems.size() == m_items.size() && (
497
0
      evmasm::bytesRequired(m_optimisedItems, 3, approx) < evmasm::bytesRequired(m_items, 3, approx) ||
498
0
      numberOfPops(m_optimisedItems) > numberOfPops(m_items)
499
0
    )
500
0
  ))
501
0
  {
502
0
    m_items = std::move(m_optimisedItems);
503
0
    return true;
504
0
  }
505
0
  else
506
0
    return false;
507
0
}