Coverage Report

Created: 2025-09-08 08:10

/src/solidity/libsolidity/codegen/YulUtilFunctions.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
 * Component that can generate various useful Yul functions.
20
 */
21
22
#include <libsolidity/codegen/YulUtilFunctions.h>
23
24
#include <libsolidity/codegen/MultiUseYulFunctionCollector.h>
25
#include <libsolidity/ast/AST.h>
26
#include <libsolidity/codegen/CompilerUtils.h>
27
#include <libsolidity/codegen/ir/IRVariable.h>
28
29
#include <libsolutil/CommonData.h>
30
#include <libsolutil/FunctionSelector.h>
31
#include <libsolutil/Whiskers.h>
32
#include <libsolutil/StringUtils.h>
33
#include <libsolidity/ast/TypeProvider.h>
34
35
#include <range/v3/algorithm/all_of.hpp>
36
37
using namespace solidity;
38
using namespace solidity::util;
39
using namespace solidity::frontend;
40
using namespace std::string_literals;
41
42
namespace
43
{
44
45
std::optional<size_t> staticEncodingSize(std::vector<Type const*> const& _parameterTypes)
46
29
{
47
29
  size_t encodedSize = 0;
48
29
  for (auto* type: _parameterTypes)
49
29
  {
50
29
    if (type->isDynamicallyEncoded())
51
29
      return std::nullopt;
52
0
    encodedSize += type->calldataHeadSize();
53
0
  }
54
0
  return encodedSize;
55
29
}
56
57
}
58
59
std::string YulUtilFunctions::identityFunction()
60
16.5k
{
61
16.5k
  std::string functionName = "identity";
62
16.5k
  return m_functionCollector.createFunction("identity", [&](std::vector<std::string>& _args, std::vector<std::string>& _rets) {
63
4.69k
    _args.push_back("value");
64
4.69k
    _rets.push_back("ret");
65
4.69k
    return "ret := value";
66
4.69k
  });
67
16.5k
}
68
69
std::string YulUtilFunctions::combineExternalFunctionIdFunction()
70
98
{
71
98
  std::string functionName = "combine_external_function_id";
72
98
  return m_functionCollector.createFunction(functionName, [&]() {
73
96
    return Whiskers(R"(
74
96
      function <functionName>(addr, selector) -> combined {
75
96
        combined := <shl64>(or(<shl32>(addr), and(selector, 0xffffffff)))
76
96
      }
77
96
    )")
78
96
    ("functionName", functionName)
79
96
    ("shl32", shiftLeftFunction(32))
80
96
    ("shl64", shiftLeftFunction(64))
81
96
    .render();
82
96
  });
83
98
}
84
85
std::string YulUtilFunctions::splitExternalFunctionIdFunction()
86
75
{
87
75
  std::string functionName = "split_external_function_id";
88
75
  return m_functionCollector.createFunction(functionName, [&]() {
89
73
    return Whiskers(R"(
90
73
      function <functionName>(combined) -> addr, selector {
91
73
        combined := <shr64>(combined)
92
73
        selector := and(combined, 0xffffffff)
93
73
        addr := <shr32>(combined)
94
73
      }
95
73
    )")
96
73
    ("functionName", functionName)
97
73
    ("shr32", shiftRightFunction(32))
98
73
    ("shr64", shiftRightFunction(64))
99
73
    .render();
100
73
  });
101
75
}
102
103
std::string YulUtilFunctions::copyToMemoryFunction(bool _fromCalldata, bool _cleanup)
104
3.41k
{
105
3.41k
  std::string functionName =
106
3.41k
    "copy_"s +
107
3.41k
    (_fromCalldata ? "calldata"s : "memory"s) +
108
3.41k
    "_to_memory"s +
109
3.41k
    (_cleanup ? "_with_cleanup"s : ""s);
110
111
3.41k
  return m_functionCollector.createFunction(functionName, [&](std::vector<std::string>& _args, std::vector<std::string>&) {
112
2.80k
    _args = {"src", "dst", "length"};
113
114
2.80k
    if (_fromCalldata)
115
742
      return Whiskers(R"(
116
742
        calldatacopy(dst, src, length)
117
742
        <?cleanup>mstore(add(dst, length), 0)</cleanup>
118
742
      )")
119
742
      ("cleanup", _cleanup)
120
742
      .render();
121
2.06k
    else
122
2.06k
    {
123
2.06k
      if (m_evmVersion.hasMcopy())
124
694
        return Whiskers(R"(
125
694
          mcopy(dst, src, length)
126
694
          <?cleanup>mstore(add(dst, length), 0)</cleanup>
127
694
        )")
128
694
        ("cleanup", _cleanup)
129
694
        .render();
130
1.36k
      else
131
1.36k
        return Whiskers(R"(
132
1.36k
          let i := 0
133
1.36k
          for { } lt(i, length) { i := add(i, 32) }
134
1.36k
          {
135
1.36k
            mstore(add(dst, i), mload(add(src, i)))
136
1.36k
          }
137
1.36k
          <?cleanup>mstore(add(dst, length), 0)</cleanup>
138
1.36k
        )")
139
1.36k
        ("cleanup", _cleanup)
140
1.36k
        .render();
141
2.06k
    }
142
2.80k
  });
143
3.41k
}
144
145
std::string YulUtilFunctions::copyLiteralToMemoryFunction(std::string const& _literal)
146
619
{
147
619
  std::string functionName = "copy_literal_to_memory_" + util::toHex(util::keccak256(_literal).asBytes());
148
149
619
  return m_functionCollector.createFunction(functionName, [&]() {
150
619
    return Whiskers(R"(
151
619
      function <functionName>() -> memPtr {
152
619
        memPtr := <arrayAllocationFunction>(<size>)
153
619
        <storeLiteralInMem>(add(memPtr, 32))
154
619
      }
155
619
      )")
156
619
      ("functionName", functionName)
157
619
      ("arrayAllocationFunction", allocateMemoryArrayFunction(*TypeProvider::array(DataLocation::Memory, true)))
158
619
      ("size", std::to_string(_literal.size()))
159
619
      ("storeLiteralInMem", storeLiteralInMemoryFunction(_literal))
160
619
      .render();
161
619
  });
162
619
}
163
164
std::string YulUtilFunctions::storeLiteralInMemoryFunction(std::string const& _literal)
165
1.23k
{
166
1.23k
  std::string functionName = "store_literal_in_memory_" + util::toHex(util::keccak256(_literal).asBytes());
167
168
1.23k
  return m_functionCollector.createFunction(functionName, [&]() {
169
1.23k
    size_t words = (_literal.length() + 31) / 32;
170
1.23k
    std::vector<std::map<std::string, std::string>> wordParams(words);
171
4.62k
    for (size_t i = 0; i < words; ++i)
172
3.39k
    {
173
3.39k
      wordParams[i]["offset"] = std::to_string(i * 32);
174
3.39k
      wordParams[i]["wordValue"] = formatAsStringOrNumber(_literal.substr(32 * i, 32));
175
3.39k
    }
176
177
1.23k
    return Whiskers(R"(
178
1.23k
      function <functionName>(memPtr) {
179
1.23k
        <#word>
180
1.23k
          mstore(add(memPtr, <offset>), <wordValue>)
181
1.23k
        </word>
182
1.23k
      }
183
1.23k
      )")
184
1.23k
      ("functionName", functionName)
185
1.23k
      ("word", wordParams)
186
1.23k
      .render();
187
1.23k
  });
188
1.23k
}
189
190
std::string YulUtilFunctions::copyLiteralToStorageFunction(std::string const& _literal)
191
212
{
192
212
  std::string functionName = "copy_literal_to_storage_" + util::toHex(util::keccak256(_literal).asBytes());
193
194
212
  return m_functionCollector.createFunction(functionName, [&](std::vector<std::string>& _args, std::vector<std::string>&) {
195
212
    _args = {"slot"};
196
197
212
    if (_literal.size() >= 32)
198
43
    {
199
43
      size_t words = (_literal.length() + 31) / 32;
200
43
      std::vector<std::map<std::string, std::string>> wordParams(words);
201
141
      for (size_t i = 0; i < words; ++i)
202
98
      {
203
98
        wordParams[i]["offset"] = std::to_string(i);
204
98
        wordParams[i]["wordValue"] = formatAsStringOrNumber(_literal.substr(32 * i, 32));
205
98
      }
206
43
      return Whiskers(R"(
207
43
        let oldLen := <byteArrayLength>(sload(slot))
208
43
        <cleanUpArrayEnd>(slot, oldLen, <length>)
209
43
        sstore(slot, <encodedLen>)
210
43
        let dstPtr := <dataArea>(slot)
211
43
        <#word>
212
43
          sstore(add(dstPtr, <offset>), <wordValue>)
213
43
        </word>
214
43
      )")
215
43
      ("byteArrayLength", extractByteArrayLengthFunction())
216
43
      ("cleanUpArrayEnd", cleanUpDynamicByteArrayEndSlotsFunction(*TypeProvider::bytesStorage()))
217
43
      ("dataArea", arrayDataAreaFunction(*TypeProvider::bytesStorage()))
218
43
      ("word", wordParams)
219
43
      ("length", std::to_string(_literal.size()))
220
43
      ("encodedLen", std::to_string(2 * _literal.size() + 1))
221
43
      .render();
222
43
    }
223
169
    else
224
169
      return Whiskers(R"(
225
169
        let oldLen := <byteArrayLength>(sload(slot))
226
169
        <cleanUpArrayEnd>(slot, oldLen, <length>)
227
169
        sstore(slot, add(<wordValue>, <encodedLen>))
228
169
      )")
229
169
      ("byteArrayLength", extractByteArrayLengthFunction())
230
169
      ("cleanUpArrayEnd", cleanUpDynamicByteArrayEndSlotsFunction(*TypeProvider::bytesStorage()))
231
169
      ("wordValue", formatAsStringOrNumber(_literal))
232
169
      ("length", std::to_string(_literal.size()))
233
169
      ("encodedLen", std::to_string(2 * _literal.size()))
234
169
      .render();
235
212
  });
236
212
}
237
238
std::string YulUtilFunctions::revertWithError(
239
  std::string const& _signature,
240
  std::vector<Type const*> const& _parameterTypes,
241
  std::vector<ASTPointer<Expression const>> const& _errorArguments,
242
  std::string const& _posVar,
243
  std::string const& _endVar
244
)
245
29
{
246
29
  solAssert((!_posVar.empty() && !_endVar.empty()) || (_posVar.empty() && _endVar.empty()));
247
29
  bool const needsNewVariable = !_posVar.empty() && !_endVar.empty();
248
29
  bool needsAllocation = true;
249
250
29
  if (std::optional<size_t> size = staticEncodingSize(_parameterTypes))
251
0
    if (
252
0
      ranges::all_of(_parameterTypes, [](auto const* type) {
253
0
        solAssert(!dynamic_cast<InaccessibleDynamicType const*>(type));
254
0
        return type && type->isValueType();
255
0
      })
256
0
    )
257
0
    {
258
0
      constexpr size_t errorSelectorSize = 4;
259
0
      needsAllocation = *size + errorSelectorSize > CompilerUtils::generalPurposeMemoryStart;
260
0
    }
261
262
29
  Whiskers templ(R"({
263
29
    <?needsAllocation>
264
29
    let <pos> := <allocateUnbounded>()
265
29
    <!needsAllocation>
266
29
    let <pos> := 0
267
29
    </needsAllocation>
268
29
    mstore(<pos>, <hash>)
269
29
    let <end> := <encode>(add(<pos>, 4) <argumentVars>)
270
29
    revert(<pos>, sub(<end>, <pos>))
271
29
  })");
272
29
  templ("pos", needsNewVariable ? _posVar : "memPtr");
273
29
  templ("end", needsNewVariable ? _endVar : "end");
274
29
  templ("hash", formatNumber(util::selectorFromSignatureU256(_signature)));
275
29
  templ("needsAllocation", needsAllocation);
276
29
  if (needsAllocation)
277
29
    templ("allocateUnbounded", allocateUnboundedFunction());
278
279
29
  std::vector<std::string> errorArgumentVars;
280
29
  std::vector<Type const*> errorArgumentTypes;
281
29
  for (ASTPointer<Expression const> const& arg: _errorArguments)
282
37
  {
283
37
    errorArgumentVars += IRVariable(*arg).stackSlots();
284
37
    solAssert(arg->annotation().type);
285
37
    errorArgumentTypes.push_back(arg->annotation().type);
286
37
  }
287
29
  templ("argumentVars", joinHumanReadablePrefixed(errorArgumentVars));
288
29
  templ("encode", ABIFunctions(m_evmVersion, m_eofVersion, m_revertStrings, m_functionCollector).tupleEncoder(errorArgumentTypes, _parameterTypes));
289
290
29
  return templ.render();
291
29
}
292
293
std::string YulUtilFunctions::requireOrAssertFunction(bool _assert, Type const* _messageType, ASTPointer<Expression const> _stringArgumentExpression)
294
101
{
295
101
  std::string functionName =
296
101
    std::string(_assert ? "assert_helper" : "require_helper") +
297
101
    (_messageType ? ("_" + _messageType->identifier()) : "");
298
299
101
  solAssert(!_assert || !_messageType, "Asserts can't have messages!");
300
301
101
  return m_functionCollector.createFunction(functionName, [&]() {
302
66
    if (!_messageType)
303
60
      return Whiskers(R"(
304
60
        function <functionName>(condition) {
305
60
          if iszero(condition) { <error> }
306
60
        }
307
60
      )")
308
60
      ("error", _assert ? panicFunction(PanicCode::Assert) + "()" : "revert(0, 0)")
309
60
      ("functionName", functionName)
310
60
      .render();
311
312
6
    solAssert(_stringArgumentExpression, "Require with string must have a string argument");
313
6
    solAssert(_stringArgumentExpression->annotation().type);
314
6
    std::vector<std::string> functionParameterNames = IRVariable(*_stringArgumentExpression).stackSlots();
315
316
6
    return Whiskers(R"(
317
6
      function <functionName>(condition <functionParameterNames>) {
318
6
        if iszero(condition)
319
6
          <revertWithError>
320
6
      }
321
6
    )")
322
6
    ("functionName", functionName)
323
6
    ("revertWithError", revertWithError("Error(string)", {TypeProvider::stringMemory()}, {_stringArgumentExpression}))
324
6
    ("functionParameterNames", joinHumanReadablePrefixed(functionParameterNames))
325
6
    .render();
326
6
  });
327
101
}
328
329
std::string YulUtilFunctions::requireWithErrorFunction(FunctionCall const& errorConstructorCall)
330
0
{
331
0
  ErrorDefinition const* errorDefinition = dynamic_cast<ErrorDefinition const*>(ASTNode::referencedDeclaration(errorConstructorCall.expression()));
332
0
  solAssert(errorDefinition);
333
334
0
  std::string const errorSignature = errorDefinition->functionType(true)->externalSignature();
335
  // Note that in most cases we'll always generate one function per error definition,
336
  // because types in the constructor call will match the ones in the definition. The only
337
  // exception are calls with types, where each instance has its own type (e.g. literals).
338
0
  std::string functionName = "require_helper_t_error_" + std::to_string(errorDefinition->id()) + "_" + errorDefinition->name();
339
0
  for (ASTPointer<Expression const> const& argument: errorConstructorCall.sortedArguments())
340
0
  {
341
0
    solAssert(argument->annotation().type);
342
0
    functionName += ("_" + argument->annotation().type->identifier());
343
0
  }
344
345
0
  std::vector<std::string> functionParameterNames;
346
0
  for (ASTPointer<Expression const> const& arg: errorConstructorCall.sortedArguments())
347
0
  {
348
0
    solAssert(arg->annotation().type);
349
0
    if (arg->annotation().type->sizeOnStack() > 0)
350
0
      functionParameterNames += IRVariable(*arg).stackSlots();
351
0
  }
352
353
0
  return m_functionCollector.createFunction(functionName, [&]() {
354
0
    return Whiskers(R"(
355
0
      function <functionName>(condition <functionParameterNames>) {
356
0
        if iszero(condition)
357
0
          <revertWithError>
358
0
      }
359
0
    )")
360
0
    ("functionName", functionName)
361
0
    ("functionParameterNames", joinHumanReadablePrefixed(functionParameterNames))
362
    // We're creating parameter names from the expressions passed into the constructor call,
363
    // which will result in odd names like `expr_29` that would normally be used for locals.
364
    // Note that this is the naming expected by `revertWithError()`.
365
0
    ("revertWithError", revertWithError(errorSignature, errorDefinition->functionType(true)->parameterTypes(), errorConstructorCall.sortedArguments()))
366
0
    .render();
367
0
  });
368
0
}
369
370
std::string YulUtilFunctions::leftAlignFunction(Type const& _type)
371
192
{
372
192
  std::string functionName = std::string("leftAlign_") + _type.identifier();
373
192
  return m_functionCollector.createFunction(functionName, [&]() {
374
192
    Whiskers templ(R"(
375
192
      function <functionName>(value) -> aligned {
376
192
        <body>
377
192
      }
378
192
    )");
379
192
    templ("functionName", functionName);
380
192
    switch (_type.category())
381
192
    {
382
2
    case Type::Category::Address:
383
2
      templ("body", "aligned := " + leftAlignFunction(IntegerType(160)) + "(value)");
384
2
      break;
385
169
    case Type::Category::Integer:
386
169
    {
387
169
      IntegerType const& type = dynamic_cast<IntegerType const&>(_type);
388
169
      if (type.numBits() == 256)
389
150
        templ("body", "aligned := value");
390
19
      else
391
19
        templ("body", "aligned := " + shiftLeftFunction(256 - type.numBits()) + "(value)");
392
169
      break;
393
0
    }
394
0
    case Type::Category::RationalNumber:
395
0
      solAssert(false, "Left align requested for rational number.");
396
0
      break;
397
0
    case Type::Category::Bool:
398
0
      templ("body", "aligned := " + leftAlignFunction(IntegerType(8)) + "(value)");
399
0
      break;
400
0
    case Type::Category::FixedPoint:
401
0
      solUnimplemented("Fixed point types not implemented.");
402
0
      break;
403
0
    case Type::Category::Array:
404
0
    case Type::Category::Struct:
405
0
      solAssert(false, "Left align requested for non-value type.");
406
0
      break;
407
21
    case Type::Category::FixedBytes:
408
21
      templ("body", "aligned := value");
409
21
      break;
410
0
    case Type::Category::Contract:
411
0
      templ("body", "aligned := " + leftAlignFunction(*TypeProvider::address()) + "(value)");
412
0
      break;
413
0
    case Type::Category::Enum:
414
0
    {
415
0
      solAssert(dynamic_cast<EnumType const&>(_type).storageBytes() == 1, "");
416
0
      templ("body", "aligned := " + leftAlignFunction(IntegerType(8)) + "(value)");
417
0
      break;
418
0
    }
419
0
    case Type::Category::InaccessibleDynamic:
420
0
      solAssert(false, "Left align requested for inaccessible dynamic type.");
421
0
      break;
422
0
    default:
423
0
      solAssert(false, "Left align of type " + _type.identifier() + " requested.");
424
192
    }
425
426
192
    return templ.render();
427
192
  });
428
192
}
429
430
std::string YulUtilFunctions::shiftLeftFunction(size_t _numBits)
431
1.79k
{
432
1.79k
  solAssert(_numBits < 256, "");
433
434
1.79k
  std::string functionName = "shift_left_" + std::to_string(_numBits);
435
1.79k
  return m_functionCollector.createFunction(functionName, [&]() {
436
1.64k
    return
437
1.64k
      Whiskers(R"(
438
1.64k
      function <functionName>(value) -> newValue {
439
1.64k
        newValue :=
440
1.64k
        <?hasShifts>
441
1.64k
          shl(<numBits>, value)
442
1.64k
        <!hasShifts>
443
1.64k
          mul(value, <multiplier>)
444
1.64k
        </hasShifts>
445
1.64k
      }
446
1.64k
      )")
447
1.64k
      ("functionName", functionName)
448
1.64k
      ("numBits", std::to_string(_numBits))
449
1.64k
      ("hasShifts", m_evmVersion.hasBitwiseShifting())
450
1.64k
      ("multiplier", toCompactHexWithPrefix(u256(1) << _numBits))
451
1.64k
      .render();
452
1.64k
  });
453
1.79k
}
454
455
std::string YulUtilFunctions::shiftLeftFunctionDynamic()
456
1.79k
{
457
1.79k
  std::string functionName = "shift_left_dynamic";
458
1.79k
  return m_functionCollector.createFunction(functionName, [&]() {
459
1.48k
    return
460
1.48k
      Whiskers(R"(
461
1.48k
      function <functionName>(bits, value) -> newValue {
462
1.48k
        newValue :=
463
1.48k
        <?hasShifts>
464
1.48k
          shl(bits, value)
465
1.48k
        <!hasShifts>
466
1.48k
          mul(value, exp(2, bits))
467
1.48k
        </hasShifts>
468
1.48k
      }
469
1.48k
      )")
470
1.48k
      ("functionName", functionName)
471
1.48k
      ("hasShifts", m_evmVersion.hasBitwiseShifting())
472
1.48k
      .render();
473
1.48k
  });
474
1.79k
}
475
476
std::string YulUtilFunctions::shiftRightFunction(size_t _numBits)
477
10.6k
{
478
10.6k
  solAssert(_numBits < 256, "");
479
480
  // Note that if this is extended with signed shifts,
481
  // the opcodes SAR and SDIV behave differently with regards to rounding!
482
483
10.6k
  std::string functionName = "shift_right_" + std::to_string(_numBits) + "_unsigned";
484
10.6k
  return m_functionCollector.createFunction(functionName, [&]() {
485
9.48k
    return
486
9.48k
      Whiskers(R"(
487
9.48k
      function <functionName>(value) -> newValue {
488
9.48k
        newValue :=
489
9.48k
        <?hasShifts>
490
9.48k
          shr(<numBits>, value)
491
9.48k
        <!hasShifts>
492
9.48k
          div(value, <multiplier>)
493
9.48k
        </hasShifts>
494
9.48k
      }
495
9.48k
      )")
496
9.48k
      ("functionName", functionName)
497
9.48k
      ("hasShifts", m_evmVersion.hasBitwiseShifting())
498
9.48k
      ("numBits", std::to_string(_numBits))
499
9.48k
      ("multiplier", toCompactHexWithPrefix(u256(1) << _numBits))
500
9.48k
      .render();
501
9.48k
  });
502
10.6k
}
503
504
std::string YulUtilFunctions::shiftRightFunctionDynamic()
505
2.18k
{
506
2.18k
  std::string const functionName = "shift_right_unsigned_dynamic";
507
2.18k
  return m_functionCollector.createFunction(functionName, [&]() {
508
1.92k
    return
509
1.92k
      Whiskers(R"(
510
1.92k
      function <functionName>(bits, value) -> newValue {
511
1.92k
        newValue :=
512
1.92k
        <?hasShifts>
513
1.92k
          shr(bits, value)
514
1.92k
        <!hasShifts>
515
1.92k
          div(value, exp(2, bits))
516
1.92k
        </hasShifts>
517
1.92k
      }
518
1.92k
      )")
519
1.92k
      ("functionName", functionName)
520
1.92k
      ("hasShifts", m_evmVersion.hasBitwiseShifting())
521
1.92k
      .render();
522
1.92k
  });
523
2.18k
}
524
525
std::string YulUtilFunctions::shiftRightSignedFunctionDynamic()
526
0
{
527
0
  std::string const functionName = "shift_right_signed_dynamic";
528
0
  return m_functionCollector.createFunction(functionName, [&]() {
529
0
    return
530
0
      Whiskers(R"(
531
0
      function <functionName>(bits, value) -> result {
532
0
        <?hasShifts>
533
0
          result := sar(bits, value)
534
0
        <!hasShifts>
535
0
          let divisor := exp(2, bits)
536
0
          let xor_mask := sub(0, slt(value, 0))
537
0
          result := xor(div(xor(value, xor_mask), divisor), xor_mask)
538
0
          // combined version of
539
0
          //   switch slt(value, 0)
540
0
          //   case 0 { result := div(value, divisor) }
541
0
          //   default { result := not(div(not(value), divisor)) }
542
0
        </hasShifts>
543
0
      }
544
0
      )")
545
0
      ("functionName", functionName)
546
0
      ("hasShifts", m_evmVersion.hasBitwiseShifting())
547
0
      .render();
548
0
  });
549
0
}
550
551
552
std::string YulUtilFunctions::typedShiftLeftFunction(Type const& _type, Type const& _amountType)
553
2
{
554
2
  solUnimplementedAssert(_type.category() != Type::Category::FixedPoint, "Not yet implemented - FixedPointType.");
555
2
  solAssert(_type.category() == Type::Category::FixedBytes || _type.category() == Type::Category::Integer, "");
556
2
  solAssert(_amountType.category() == Type::Category::Integer, "");
557
2
  solAssert(!dynamic_cast<IntegerType const&>(_amountType).isSigned(), "");
558
2
  std::string const functionName = "shift_left_" + _type.identifier() + "_" + _amountType.identifier();
559
2
  return m_functionCollector.createFunction(functionName, [&]() {
560
2
    return
561
2
      Whiskers(R"(
562
2
      function <functionName>(value, bits) -> result {
563
2
        bits := <cleanAmount>(bits)
564
2
        result := <cleanup>(<shift>(bits, <cleanup>(value)))
565
2
      }
566
2
      )")
567
2
      ("functionName", functionName)
568
2
      ("cleanAmount", cleanupFunction(_amountType))
569
2
      ("shift", shiftLeftFunctionDynamic())
570
2
      ("cleanup", cleanupFunction(_type))
571
2
      .render();
572
2
  });
573
2
}
574
575
std::string YulUtilFunctions::typedShiftRightFunction(Type const& _type, Type const& _amountType)
576
56
{
577
56
  solUnimplementedAssert(_type.category() != Type::Category::FixedPoint, "Not yet implemented - FixedPointType.");
578
56
  solAssert(_type.category() == Type::Category::FixedBytes || _type.category() == Type::Category::Integer, "");
579
56
  solAssert(_amountType.category() == Type::Category::Integer, "");
580
56
  solAssert(!dynamic_cast<IntegerType const&>(_amountType).isSigned(), "");
581
56
  IntegerType const* integerType = dynamic_cast<IntegerType const*>(&_type);
582
56
  bool valueSigned = integerType && integerType->isSigned();
583
584
56
  std::string const functionName = "shift_right_" + _type.identifier() + "_" + _amountType.identifier();
585
56
  return m_functionCollector.createFunction(functionName, [&]() {
586
52
    return
587
52
      Whiskers(R"(
588
52
      function <functionName>(value, bits) -> result {
589
52
        bits := <cleanAmount>(bits)
590
52
        result := <cleanup>(<shift>(bits, <cleanup>(value)))
591
52
      }
592
52
      )")
593
52
      ("functionName", functionName)
594
52
      ("cleanAmount", cleanupFunction(_amountType))
595
52
      ("shift", valueSigned ? shiftRightSignedFunctionDynamic() : shiftRightFunctionDynamic())
596
52
      ("cleanup", cleanupFunction(_type))
597
52
      .render();
598
52
  });
599
56
}
600
601
std::string YulUtilFunctions::updateByteSliceFunction(size_t _numBytes, size_t _shiftBytes)
602
1.05k
{
603
1.05k
  solAssert(_numBytes <= 32, "");
604
1.05k
  solAssert(_shiftBytes <= 32, "");
605
1.05k
  size_t numBits = _numBytes * 8;
606
1.05k
  size_t shiftBits = _shiftBytes * 8;
607
1.05k
  std::string functionName = "update_byte_slice_" + std::to_string(_numBytes) + "_shift_" + std::to_string(_shiftBytes);
608
1.05k
  return m_functionCollector.createFunction(functionName, [&]() {
609
985
    return
610
985
      Whiskers(R"(
611
985
      function <functionName>(value, toInsert) -> result {
612
985
        let mask := <mask>
613
985
        toInsert := <shl>(toInsert)
614
985
        value := and(value, not(mask))
615
985
        result := or(value, and(toInsert, mask))
616
985
      }
617
985
      )")
618
985
      ("functionName", functionName)
619
985
      ("mask", formatNumber(((bigint(1) << numBits) - 1) << shiftBits))
620
985
      ("shl", shiftLeftFunction(shiftBits))
621
985
      .render();
622
985
  });
623
1.05k
}
624
625
std::string YulUtilFunctions::updateByteSliceFunctionDynamic(size_t _numBytes)
626
1.57k
{
627
1.57k
  solAssert(_numBytes <= 32, "");
628
1.57k
  size_t numBits = _numBytes * 8;
629
1.57k
  std::string functionName = "update_byte_slice_dynamic" + std::to_string(_numBytes);
630
1.57k
  return m_functionCollector.createFunction(functionName, [&]() {
631
1.52k
    return
632
1.52k
      Whiskers(R"(
633
1.52k
      function <functionName>(value, shiftBytes, toInsert) -> result {
634
1.52k
        let shiftBits := mul(shiftBytes, 8)
635
1.52k
        let mask := <shl>(shiftBits, <mask>)
636
1.52k
        toInsert := <shl>(shiftBits, toInsert)
637
1.52k
        value := and(value, not(mask))
638
1.52k
        result := or(value, and(toInsert, mask))
639
1.52k
      }
640
1.52k
      )")
641
1.52k
      ("functionName", functionName)
642
1.52k
      ("mask", formatNumber((bigint(1) << numBits) - 1))
643
1.52k
      ("shl", shiftLeftFunctionDynamic())
644
1.52k
      .render();
645
1.52k
  });
646
1.57k
}
647
648
std::string YulUtilFunctions::maskBytesFunctionDynamic()
649
2.36k
{
650
2.36k
  std::string functionName = "mask_bytes_dynamic";
651
2.36k
  return m_functionCollector.createFunction(functionName, [&]() {
652
1.13k
    return Whiskers(R"(
653
1.13k
      function <functionName>(data, bytes) -> result {
654
1.13k
        let mask := not(<shr>(mul(8, bytes), not(0)))
655
1.13k
        result := and(data, mask)
656
1.13k
      })")
657
1.13k
      ("functionName", functionName)
658
1.13k
      ("shr", shiftRightFunctionDynamic())
659
1.13k
      .render();
660
1.13k
  });
661
2.36k
}
662
663
std::string YulUtilFunctions::maskLowerOrderBytesFunction(size_t _bytes)
664
49
{
665
49
  std::string functionName = "mask_lower_order_bytes_" + std::to_string(_bytes);
666
49
  solAssert(_bytes <= 32, "");
667
49
  return m_functionCollector.createFunction(functionName, [&]() {
668
48
    return Whiskers(R"(
669
48
      function <functionName>(data) -> result {
670
48
        result := and(data, <mask>)
671
48
      })")
672
48
      ("functionName", functionName)
673
48
      ("mask", formatNumber((~u256(0)) >> (256 - 8 * _bytes)))
674
48
      .render();
675
48
  });
676
49
}
677
678
std::string YulUtilFunctions::maskLowerOrderBytesFunctionDynamic()
679
49
{
680
49
  std::string functionName = "mask_lower_order_bytes_dynamic";
681
49
  return m_functionCollector.createFunction(functionName, [&]() {
682
48
    return Whiskers(R"(
683
48
      function <functionName>(data, bytes) -> result {
684
48
        let mask := not(<shl>(mul(8, bytes), not(0)))
685
48
        result := and(data, mask)
686
48
      })")
687
48
      ("functionName", functionName)
688
48
      ("shl", shiftLeftFunctionDynamic())
689
48
      .render();
690
48
  });
691
49
}
692
693
std::string YulUtilFunctions::roundUpFunction()
694
9.70k
{
695
9.70k
  std::string functionName = "round_up_to_mul_of_32";
696
9.70k
  return m_functionCollector.createFunction(functionName, [&]() {
697
3.99k
    return
698
3.99k
      Whiskers(R"(
699
3.99k
      function <functionName>(value) -> result {
700
3.99k
        result := and(add(value, 31), not(31))
701
3.99k
      }
702
3.99k
      )")
703
3.99k
      ("functionName", functionName)
704
3.99k
      .render();
705
3.99k
  });
706
9.70k
}
707
708
std::string YulUtilFunctions::divide32CeilFunction()
709
1.24k
{
710
1.24k
  return m_functionCollector.createFunction(
711
1.24k
    "divide_by_32_ceil",
712
1.24k
    [&](std::vector<std::string>& _args, std::vector<std::string>& _ret) {
713
1.23k
      _args = {"value"};
714
1.23k
      _ret = {"result"};
715
1.23k
      return "result := div(add(value, 31), 32)";
716
1.23k
    }
717
1.24k
  );
718
1.24k
}
719
720
std::string YulUtilFunctions::overflowCheckedIntAddFunction(IntegerType const& _type)
721
3.62k
{
722
3.62k
  std::string functionName = "checked_add_" + _type.identifier();
723
3.62k
  return m_functionCollector.createFunction(functionName, [&]() {
724
1.50k
    return
725
1.50k
      Whiskers(R"(
726
1.50k
      function <functionName>(x, y) -> sum {
727
1.50k
        x := <cleanupFunction>(x)
728
1.50k
        y := <cleanupFunction>(y)
729
1.50k
        sum := add(x, y)
730
1.50k
        <?signed>
731
1.50k
          <?256bit>
732
1.50k
            // overflow, if x >= 0 and sum < y
733
1.50k
            // underflow, if x < 0 and sum >= y
734
1.50k
            if or(
735
1.50k
              and(iszero(slt(x, 0)), slt(sum, y)),
736
1.50k
              and(slt(x, 0), iszero(slt(sum, y)))
737
1.50k
            ) { <panic>() }
738
1.50k
          <!256bit>
739
1.50k
            if or(
740
1.50k
              sgt(sum, <maxValue>),
741
1.50k
              slt(sum, <minValue>)
742
1.50k
            ) { <panic>() }
743
1.50k
          </256bit>
744
1.50k
        <!signed>
745
1.50k
          <?256bit>
746
1.50k
            if gt(x, sum) { <panic>() }
747
1.50k
          <!256bit>
748
1.50k
            if gt(sum, <maxValue>) { <panic>() }
749
1.50k
          </256bit>
750
1.50k
        </signed>
751
1.50k
      }
752
1.50k
      )")
753
1.50k
      ("functionName", functionName)
754
1.50k
      ("signed", _type.isSigned())
755
1.50k
      ("maxValue", toCompactHexWithPrefix(u256(_type.maxValue())))
756
1.50k
      ("minValue", toCompactHexWithPrefix(u256(_type.minValue())))
757
1.50k
      ("cleanupFunction", cleanupFunction(_type))
758
1.50k
      ("panic", panicFunction(PanicCode::UnderOverflow))
759
1.50k
      ("256bit", _type.numBits() == 256)
760
1.50k
      .render();
761
1.50k
  });
762
3.62k
}
763
764
std::string YulUtilFunctions::wrappingIntAddFunction(IntegerType const& _type)
765
0
{
766
0
  std::string functionName = "wrapping_add_" + _type.identifier();
767
0
  return m_functionCollector.createFunction(functionName, [&]() {
768
0
    return
769
0
      Whiskers(R"(
770
0
      function <functionName>(x, y) -> sum {
771
0
        sum := <cleanupFunction>(add(x, y))
772
0
      }
773
0
      )")
774
0
      ("functionName", functionName)
775
0
      ("cleanupFunction", cleanupFunction(_type))
776
0
      .render();
777
0
  });
778
0
}
779
780
std::string YulUtilFunctions::overflowCheckedIntMulFunction(IntegerType const& _type)
781
4.66k
{
782
4.66k
  std::string functionName = "checked_mul_" + _type.identifier();
783
4.66k
  return m_functionCollector.createFunction(functionName, [&]() {
784
1.52k
    return
785
      // Multiplication by zero could be treated separately and directly return zero.
786
1.52k
      Whiskers(R"(
787
1.52k
      function <functionName>(x, y) -> product {
788
1.52k
        x := <cleanupFunction>(x)
789
1.52k
        y := <cleanupFunction>(y)
790
1.52k
        let product_raw := mul(x, y)
791
1.52k
        product := <cleanupFunction>(product_raw)
792
1.52k
        <?signed>
793
1.52k
          <?gt128bit>
794
1.52k
            <?256bit>
795
1.52k
              // special case
796
1.52k
              if and(slt(x, 0), eq(y, <minValue>)) { <panic>() }
797
1.52k
            </256bit>
798
1.52k
            // overflow, if x != 0 and y != product/x
799
1.52k
            if iszero(
800
1.52k
              or(
801
1.52k
                iszero(x),
802
1.52k
                eq(y, sdiv(product, x))
803
1.52k
              )
804
1.52k
            ) { <panic>() }
805
1.52k
          <!gt128bit>
806
1.52k
            if iszero(eq(product, product_raw)) { <panic>() }
807
1.52k
          </gt128bit>
808
1.52k
        <!signed>
809
1.52k
          <?gt128bit>
810
1.52k
            // overflow, if x != 0 and y != product/x
811
1.52k
            if iszero(
812
1.52k
              or(
813
1.52k
                iszero(x),
814
1.52k
                eq(y, div(product, x))
815
1.52k
              )
816
1.52k
            ) { <panic>() }
817
1.52k
          <!gt128bit>
818
1.52k
            if iszero(eq(product, product_raw)) { <panic>() }
819
1.52k
          </gt128bit>
820
1.52k
        </signed>
821
1.52k
      }
822
1.52k
      )")
823
1.52k
      ("functionName", functionName)
824
1.52k
      ("signed", _type.isSigned())
825
1.52k
      ("cleanupFunction", cleanupFunction(_type))
826
1.52k
      ("panic", panicFunction(PanicCode::UnderOverflow))
827
1.52k
      ("minValue", toCompactHexWithPrefix(u256(_type.minValue())))
828
1.52k
      ("256bit", _type.numBits() == 256)
829
1.52k
      ("gt128bit", _type.numBits() > 128)
830
1.52k
      .render();
831
1.52k
  });
832
4.66k
}
833
834
std::string YulUtilFunctions::wrappingIntMulFunction(IntegerType const& _type)
835
0
{
836
0
  std::string functionName = "wrapping_mul_" + _type.identifier();
837
0
  return m_functionCollector.createFunction(functionName, [&]() {
838
0
    return
839
0
      Whiskers(R"(
840
0
      function <functionName>(x, y) -> product {
841
0
        product := <cleanupFunction>(mul(x, y))
842
0
      }
843
0
      )")
844
0
      ("functionName", functionName)
845
0
      ("cleanupFunction", cleanupFunction(_type))
846
0
      .render();
847
0
  });
848
0
}
849
850
std::string YulUtilFunctions::overflowCheckedIntDivFunction(IntegerType const& _type)
851
693
{
852
693
  std::string functionName = "checked_div_" + _type.identifier();
853
693
  return m_functionCollector.createFunction(functionName, [&]() {
854
528
    return
855
528
      Whiskers(R"(
856
528
      function <functionName>(x, y) -> r {
857
528
        x := <cleanupFunction>(x)
858
528
        y := <cleanupFunction>(y)
859
528
        if iszero(y) { <panicDivZero>() }
860
528
        <?signed>
861
528
        // overflow for minVal / -1
862
528
        if and(
863
528
          eq(x, <minVal>),
864
528
          eq(y, sub(0, 1))
865
528
        ) { <panicOverflow>() }
866
528
        </signed>
867
528
        r := <?signed>s</signed>div(x, y)
868
528
      }
869
528
      )")
870
528
      ("functionName", functionName)
871
528
      ("signed", _type.isSigned())
872
528
      ("minVal", toCompactHexWithPrefix(u256(_type.minValue())))
873
528
      ("cleanupFunction", cleanupFunction(_type))
874
528
      ("panicDivZero", panicFunction(PanicCode::DivisionByZero))
875
528
      ("panicOverflow", panicFunction(PanicCode::UnderOverflow))
876
528
      .render();
877
528
  });
878
693
}
879
880
std::string YulUtilFunctions::wrappingIntDivFunction(IntegerType const& _type)
881
0
{
882
0
  std::string functionName = "wrapping_div_" + _type.identifier();
883
0
  return m_functionCollector.createFunction(functionName, [&]() {
884
0
    return
885
0
      Whiskers(R"(
886
0
      function <functionName>(x, y) -> r {
887
0
        x := <cleanupFunction>(x)
888
0
        y := <cleanupFunction>(y)
889
0
        if iszero(y) { <error>() }
890
0
        r := <?signed>s</signed>div(x, y)
891
0
      }
892
0
      )")
893
0
      ("functionName", functionName)
894
0
      ("cleanupFunction", cleanupFunction(_type))
895
0
      ("signed", _type.isSigned())
896
0
      ("error", panicFunction(PanicCode::DivisionByZero))
897
0
      .render();
898
0
  });
899
0
}
900
901
std::string YulUtilFunctions::intModFunction(IntegerType const& _type)
902
1.02k
{
903
1.02k
  std::string functionName = "mod_" + _type.identifier();
904
1.02k
  return m_functionCollector.createFunction(functionName, [&]() {
905
622
    return
906
622
      Whiskers(R"(
907
622
      function <functionName>(x, y) -> r {
908
622
        x := <cleanupFunction>(x)
909
622
        y := <cleanupFunction>(y)
910
622
        if iszero(y) { <panic>() }
911
622
        r := <?signed>s</signed>mod(x, y)
912
622
      }
913
622
      )")
914
622
      ("functionName", functionName)
915
622
      ("signed", _type.isSigned())
916
622
      ("cleanupFunction", cleanupFunction(_type))
917
622
      ("panic", panicFunction(PanicCode::DivisionByZero))
918
622
      .render();
919
622
  });
920
1.02k
}
921
922
std::string YulUtilFunctions::overflowCheckedIntSubFunction(IntegerType const& _type)
923
5.65k
{
924
5.65k
  std::string functionName = "checked_sub_" + _type.identifier();
925
5.65k
  return m_functionCollector.createFunction(functionName, [&] {
926
1.64k
    return
927
1.64k
      Whiskers(R"(
928
1.64k
      function <functionName>(x, y) -> diff {
929
1.64k
        x := <cleanupFunction>(x)
930
1.64k
        y := <cleanupFunction>(y)
931
1.64k
        diff := sub(x, y)
932
1.64k
        <?signed>
933
1.64k
          <?256bit>
934
1.64k
            // underflow, if y >= 0 and diff > x
935
1.64k
            // overflow, if y < 0 and diff < x
936
1.64k
            if or(
937
1.64k
              and(iszero(slt(y, 0)), sgt(diff, x)),
938
1.64k
              and(slt(y, 0), slt(diff, x))
939
1.64k
            ) { <panic>() }
940
1.64k
          <!256bit>
941
1.64k
            if or(
942
1.64k
              slt(diff, <minValue>),
943
1.64k
              sgt(diff, <maxValue>)
944
1.64k
            ) { <panic>() }
945
1.64k
          </256bit>
946
1.64k
        <!signed>
947
1.64k
          <?256bit>
948
1.64k
            if gt(diff, x) { <panic>() }
949
1.64k
          <!256bit>
950
1.64k
            if gt(diff, <maxValue>) { <panic>() }
951
1.64k
          </256bit>
952
1.64k
        </signed>
953
1.64k
      }
954
1.64k
      )")
955
1.64k
      ("functionName", functionName)
956
1.64k
      ("signed", _type.isSigned())
957
1.64k
      ("maxValue", toCompactHexWithPrefix(u256(_type.maxValue())))
958
1.64k
      ("minValue", toCompactHexWithPrefix(u256(_type.minValue())))
959
1.64k
      ("cleanupFunction", cleanupFunction(_type))
960
1.64k
      ("panic", panicFunction(PanicCode::UnderOverflow))
961
1.64k
      ("256bit", _type.numBits() == 256)
962
1.64k
      .render();
963
1.64k
  });
964
5.65k
}
965
966
std::string YulUtilFunctions::wrappingIntSubFunction(IntegerType const& _type)
967
0
{
968
0
  std::string functionName = "wrapping_sub_" + _type.identifier();
969
0
  return m_functionCollector.createFunction(functionName, [&] {
970
0
    return
971
0
      Whiskers(R"(
972
0
      function <functionName>(x, y) -> diff {
973
0
        diff := <cleanupFunction>(sub(x, y))
974
0
      }
975
0
      )")
976
0
      ("functionName", functionName)
977
0
      ("cleanupFunction", cleanupFunction(_type))
978
0
      .render();
979
0
  });
980
0
}
981
982
std::string YulUtilFunctions::overflowCheckedIntExpFunction(
983
  IntegerType const& _type,
984
  IntegerType const& _exponentType
985
)
986
3.40k
{
987
3.40k
  solAssert(!_exponentType.isSigned(), "");
988
989
3.40k
  std::string functionName = "checked_exp_" + _type.identifier() + "_" + _exponentType.identifier();
990
3.40k
  return m_functionCollector.createFunction(functionName, [&]() {
991
2.22k
    return
992
2.22k
      Whiskers(R"(
993
2.22k
      function <functionName>(base, exponent) -> power {
994
2.22k
        base := <baseCleanupFunction>(base)
995
2.22k
        exponent := <exponentCleanupFunction>(exponent)
996
2.22k
        <?signed>
997
2.22k
          power := <exp>(base, exponent, <minValue>, <maxValue>)
998
2.22k
        <!signed>
999
2.22k
          power := <exp>(base, exponent, <maxValue>)
1000
2.22k
        </signed>
1001
2.22k
1002
2.22k
      }
1003
2.22k
      )")
1004
2.22k
      ("functionName", functionName)
1005
2.22k
      ("signed", _type.isSigned())
1006
2.22k
      ("exp", _type.isSigned() ? overflowCheckedSignedExpFunction() : overflowCheckedUnsignedExpFunction())
1007
2.22k
      ("maxValue", toCompactHexWithPrefix(_type.max()))
1008
2.22k
      ("minValue", toCompactHexWithPrefix(_type.min()))
1009
2.22k
      ("baseCleanupFunction", cleanupFunction(_type))
1010
2.22k
      ("exponentCleanupFunction", cleanupFunction(_exponentType))
1011
2.22k
      .render();
1012
2.22k
  });
1013
3.40k
}
1014
1015
std::string YulUtilFunctions::overflowCheckedIntLiteralExpFunction(
1016
  RationalNumberType const& _baseType,
1017
  IntegerType const& _exponentType,
1018
  IntegerType const& _commonType
1019
)
1020
178
{
1021
178
  solAssert(!_exponentType.isSigned(), "");
1022
178
  solAssert(_baseType.isNegative() == _commonType.isSigned(), "");
1023
178
  solAssert(_commonType.numBits() == 256, "");
1024
1025
178
  std::string functionName = "checked_exp_" + _baseType.richIdentifier() + "_" + _exponentType.identifier();
1026
1027
178
  return m_functionCollector.createFunction(functionName, [&]()
1028
178
  {
1029
    // Converts a bigint number into u256 (negative numbers represented in two's complement form.)
1030
    // We assume that `_v` fits in 256 bits.
1031
175
    auto bigint2u = [&](bigint const& _v) -> u256
1032
175
    {
1033
175
      if (_v < 0)
1034
0
        return s2u(s256(_v));
1035
175
      return u256(_v);
1036
175
    };
1037
1038
    // Calculates the upperbound for exponentiation, that is, calculate `b`, such that
1039
    // _base**b <= _maxValue and _base**(b + 1) > _maxValue
1040
175
    auto findExponentUpperbound = [](bigint const _base, bigint const _maxValue) -> unsigned
1041
175
    {
1042
      // There is no overflow for these cases
1043
175
      if (_base == 0 || _base == -1 || _base == 1)
1044
52
        return 0;
1045
1046
123
      unsigned first = 0;
1047
123
      unsigned last = 255;
1048
123
      unsigned middle;
1049
1050
849
      while (first < last)
1051
836
      {
1052
836
        middle = (first + last) / 2;
1053
1054
836
        if (
1055
          // The condition on msb is a shortcut that avoids computing large powers in
1056
          // arbitrary precision.
1057
836
          boost::multiprecision::msb(_base) * middle <= boost::multiprecision::msb(_maxValue) &&
1058
836
          boost::multiprecision::pow(_base, middle) <= _maxValue
1059
836
        )
1060
361
        {
1061
361
          if (boost::multiprecision::pow(_base, middle + 1) > _maxValue)
1062
110
            return middle;
1063
251
          else
1064
251
            first = middle + 1;
1065
361
        }
1066
475
        else
1067
475
          last = middle;
1068
836
      }
1069
1070
13
      return last;
1071
123
    };
1072
1073
175
    bigint baseValue = _baseType.isNegative() ?
1074
0
      u2s(_baseType.literalValue(nullptr)) :
1075
175
      _baseType.literalValue(nullptr);
1076
175
    bool needsOverflowCheck = !((baseValue == 0) || (baseValue == -1) || (baseValue == 1));
1077
175
    unsigned exponentUpperbound;
1078
1079
175
    if (_baseType.isNegative())
1080
0
    {
1081
      // Only checks for underflow. The only case where this can be a problem is when, for a
1082
      // negative base, say `b`, and an even exponent, say `e`, `b**e = 2**255` (which is an
1083
      // overflow.) But this never happens because, `255 = 3*5*17`, and therefore there is no even
1084
      // number `e` such that `b**e = 2**255`.
1085
0
      exponentUpperbound = findExponentUpperbound(abs(baseValue), abs(_commonType.minValue()));
1086
1087
0
      bigint power = boost::multiprecision::pow(baseValue, exponentUpperbound);
1088
0
      bigint overflowedPower = boost::multiprecision::pow(baseValue, exponentUpperbound + 1);
1089
1090
0
      if (needsOverflowCheck)
1091
0
        solAssert(
1092
0
          (power <= _commonType.maxValue()) && (power >= _commonType.minValue()) &&
1093
0
          !((overflowedPower <= _commonType.maxValue()) && (overflowedPower >= _commonType.minValue())),
1094
0
          "Incorrect exponent upper bound calculated."
1095
0
        );
1096
0
    }
1097
175
    else
1098
175
    {
1099
175
      exponentUpperbound = findExponentUpperbound(baseValue, _commonType.maxValue());
1100
1101
175
      if (needsOverflowCheck)
1102
175
        solAssert(
1103
175
          boost::multiprecision::pow(baseValue, exponentUpperbound) <= _commonType.maxValue() &&
1104
175
          boost::multiprecision::pow(baseValue, exponentUpperbound + 1) > _commonType.maxValue(),
1105
175
          "Incorrect exponent upper bound calculated."
1106
175
        );
1107
175
    }
1108
1109
175
    return Whiskers(R"(
1110
175
      function <functionName>(exponent) -> power {
1111
175
        exponent := <exponentCleanupFunction>(exponent)
1112
175
        <?needsOverflowCheck>
1113
175
        if gt(exponent, <exponentUpperbound>) { <panic>() }
1114
175
        </needsOverflowCheck>
1115
175
        power := exp(<base>, exponent)
1116
175
      }
1117
175
      )")
1118
175
      ("functionName", functionName)
1119
175
      ("exponentCleanupFunction", cleanupFunction(_exponentType))
1120
175
      ("needsOverflowCheck", needsOverflowCheck)
1121
175
      ("exponentUpperbound", std::to_string(exponentUpperbound))
1122
175
      ("panic", panicFunction(PanicCode::UnderOverflow))
1123
175
      ("base", bigint2u(baseValue).str())
1124
175
      .render();
1125
175
  });
1126
178
}
1127
1128
std::string YulUtilFunctions::overflowCheckedUnsignedExpFunction()
1129
2.20k
{
1130
  // Checks for the "small number specialization" below.
1131
2.20k
  using namespace boost::multiprecision;
1132
2.20k
  solAssert(pow(bigint(10), 77) < pow(bigint(2), 256), "");
1133
2.20k
  solAssert(pow(bigint(11), 77) >= pow(bigint(2), 256), "");
1134
2.20k
  solAssert(pow(bigint(10), 78) >= pow(bigint(2), 256), "");
1135
1136
2.20k
  solAssert(pow(bigint(306), 31) < pow(bigint(2), 256), "");
1137
2.20k
  solAssert(pow(bigint(307), 31) >= pow(bigint(2), 256), "");
1138
2.20k
  solAssert(pow(bigint(306), 32) >= pow(bigint(2), 256), "");
1139
1140
2.20k
  std::string functionName = "checked_exp_unsigned";
1141
2.20k
  return m_functionCollector.createFunction(functionName, [&]() {
1142
1.17k
    return
1143
1.17k
      Whiskers(R"(
1144
1.17k
      function <functionName>(base, exponent, max) -> power {
1145
1.17k
        // This function currently cannot be inlined because of the
1146
1.17k
        // "leave" statements. We have to improve the optimizer.
1147
1.17k
1148
1.17k
        // Note that 0**0 == 1
1149
1.17k
        if iszero(exponent) { power := 1 leave }
1150
1.17k
        if iszero(base) { power := 0 leave }
1151
1.17k
1152
1.17k
        // Specializations for small bases
1153
1.17k
        switch base
1154
1.17k
        // 0 is handled above
1155
1.17k
        case 1 { power := 1 leave }
1156
1.17k
        case 2
1157
1.17k
        {
1158
1.17k
          if gt(exponent, 255) { <panic>() }
1159
1.17k
          power := exp(2, exponent)
1160
1.17k
          if gt(power, max) { <panic>() }
1161
1.17k
          leave
1162
1.17k
        }
1163
1.17k
        if or(
1164
1.17k
          and(lt(base, 11), lt(exponent, 78)),
1165
1.17k
          and(lt(base, 307), lt(exponent, 32))
1166
1.17k
        )
1167
1.17k
        {
1168
1.17k
          power := exp(base, exponent)
1169
1.17k
          if gt(power, max) { <panic>() }
1170
1.17k
          leave
1171
1.17k
        }
1172
1.17k
1173
1.17k
        power, base := <expLoop>(1, base, exponent, max)
1174
1.17k
1175
1.17k
        if gt(power, div(max, base)) { <panic>() }
1176
1.17k
        power := mul(power, base)
1177
1.17k
      }
1178
1.17k
      )")
1179
1.17k
      ("functionName", functionName)
1180
1.17k
      ("panic", panicFunction(PanicCode::UnderOverflow))
1181
1.17k
      ("expLoop", overflowCheckedExpLoopFunction())
1182
1.17k
      .render();
1183
1.17k
  });
1184
2.20k
}
1185
1186
std::string YulUtilFunctions::overflowCheckedSignedExpFunction()
1187
16
{
1188
16
  std::string functionName = "checked_exp_signed";
1189
16
  return m_functionCollector.createFunction(functionName, [&]() {
1190
10
    return
1191
10
      Whiskers(R"(
1192
10
      function <functionName>(base, exponent, min, max) -> power {
1193
10
        // Currently, `leave` avoids this function being inlined.
1194
10
        // We have to improve the optimizer.
1195
10
1196
10
        // Note that 0**0 == 1
1197
10
        switch exponent
1198
10
        case 0 { power := 1 leave }
1199
10
        case 1 { power := base leave }
1200
10
        if iszero(base) { power := 0 leave }
1201
10
1202
10
        power := 1
1203
10
1204
10
        // We pull out the first iteration because it is the only one in which
1205
10
        // base can be negative.
1206
10
        // Exponent is at least 2 here.
1207
10
1208
10
        // overflow check for base * base
1209
10
        switch sgt(base, 0)
1210
10
        case 1 { if gt(base, div(max, base)) { <panic>() } }
1211
10
        case 0 { if slt(base, sdiv(max, base)) { <panic>() } }
1212
10
        if and(exponent, 1)
1213
10
        {
1214
10
          power := base
1215
10
        }
1216
10
        base := mul(base, base)
1217
10
        exponent := <shr_1>(exponent)
1218
10
1219
10
        // Below this point, base is always positive.
1220
10
1221
10
        power, base := <expLoop>(power, base, exponent, max)
1222
10
1223
10
        if and(sgt(power, 0), gt(power, div(max, base))) { <panic>() }
1224
10
        if and(slt(power, 0), slt(power, sdiv(min, base))) { <panic>() }
1225
10
        power := mul(power, base)
1226
10
      }
1227
10
      )")
1228
10
      ("functionName", functionName)
1229
10
      ("panic", panicFunction(PanicCode::UnderOverflow))
1230
10
      ("expLoop", overflowCheckedExpLoopFunction())
1231
10
      ("shr_1", shiftRightFunction(1))
1232
10
      .render();
1233
10
  });
1234
16
}
1235
1236
std::string YulUtilFunctions::overflowCheckedExpLoopFunction()
1237
1.18k
{
1238
  // We use this loop for both signed and unsigned exponentiation
1239
  // because we pull out the first iteration in the signed case which
1240
  // results in the base always being positive.
1241
1242
  // This function does not include the final multiplication.
1243
1244
1.18k
  std::string functionName = "checked_exp_helper";
1245
1.18k
  return m_functionCollector.createFunction(functionName, [&]() {
1246
1.18k
    return
1247
1.18k
      Whiskers(R"(
1248
1.18k
      function <functionName>(_power, _base, exponent, max) -> power, base {
1249
1.18k
        power := _power
1250
1.18k
        base  := _base
1251
1.18k
        for { } gt(exponent, 1) {}
1252
1.18k
        {
1253
1.18k
          // overflow check for base * base
1254
1.18k
          if gt(base, div(max, base)) { <panic>() }
1255
1.18k
          if and(exponent, 1)
1256
1.18k
          {
1257
1.18k
            // No checks for power := mul(power, base) needed, because the check
1258
1.18k
            // for base * base above is sufficient, since:
1259
1.18k
            // |power| <= base (proof by induction) and thus:
1260
1.18k
            // |power * base| <= base * base <= max <= |min| (for signed)
1261
1.18k
            // (this is equally true for signed and unsigned exp)
1262
1.18k
            power := mul(power, base)
1263
1.18k
          }
1264
1.18k
          base := mul(base, base)
1265
1.18k
          exponent := <shr_1>(exponent)
1266
1.18k
        }
1267
1.18k
      }
1268
1.18k
      )")
1269
1.18k
      ("functionName", functionName)
1270
1.18k
      ("panic", panicFunction(PanicCode::UnderOverflow))
1271
1.18k
      ("shr_1", shiftRightFunction(1))
1272
1.18k
      .render();
1273
1.18k
  });
1274
1.18k
}
1275
1276
std::string YulUtilFunctions::wrappingIntExpFunction(
1277
  IntegerType const& _type,
1278
  IntegerType const& _exponentType
1279
)
1280
0
{
1281
0
  solAssert(!_exponentType.isSigned(), "");
1282
1283
0
  std::string functionName = "wrapping_exp_" + _type.identifier() + "_" + _exponentType.identifier();
1284
0
  return m_functionCollector.createFunction(functionName, [&]() {
1285
0
    return
1286
0
      Whiskers(R"(
1287
0
      function <functionName>(base, exponent) -> power {
1288
0
        base := <baseCleanupFunction>(base)
1289
0
        exponent := <exponentCleanupFunction>(exponent)
1290
0
        power := <baseCleanupFunction>(exp(base, exponent))
1291
0
      }
1292
0
      )")
1293
0
      ("functionName", functionName)
1294
0
      ("baseCleanupFunction", cleanupFunction(_type))
1295
0
      ("exponentCleanupFunction", cleanupFunction(_exponentType))
1296
0
      .render();
1297
0
  });
1298
0
}
1299
1300
std::string YulUtilFunctions::arrayLengthFunction(ArrayType const& _type)
1301
12.9k
{
1302
12.9k
  std::string functionName = "array_length_" + _type.identifier();
1303
12.9k
  return m_functionCollector.createFunction(functionName, [&]() {
1304
10.0k
    Whiskers w(R"(
1305
10.0k
      function <functionName>(value<?dynamic><?calldata>, len</calldata></dynamic>) -> length {
1306
10.0k
        <?dynamic>
1307
10.0k
          <?memory>
1308
10.0k
            length := mload(value)
1309
10.0k
          </memory>
1310
10.0k
          <?storage>
1311
10.0k
            length := sload(value)
1312
10.0k
            <?byteArray>
1313
10.0k
              length := <extractByteArrayLength>(length)
1314
10.0k
            </byteArray>
1315
10.0k
          </storage>
1316
10.0k
          <?calldata>
1317
10.0k
            length := len
1318
10.0k
          </calldata>
1319
10.0k
        <!dynamic>
1320
10.0k
          length := <length>
1321
10.0k
        </dynamic>
1322
10.0k
      }
1323
10.0k
    )");
1324
10.0k
    w("functionName", functionName);
1325
10.0k
    w("dynamic", _type.isDynamicallySized());
1326
10.0k
    if (!_type.isDynamicallySized())
1327
1.47k
      w("length", toCompactHexWithPrefix(_type.length()));
1328
10.0k
    w("memory", _type.location() == DataLocation::Memory);
1329
10.0k
    w("storage", _type.location() == DataLocation::Storage);
1330
10.0k
    w("calldata", _type.location() == DataLocation::CallData);
1331
10.0k
    if (_type.location() == DataLocation::Storage)
1332
1.38k
    {
1333
1.38k
      w("byteArray", _type.isByteArrayOrString());
1334
1.38k
      if (_type.isByteArrayOrString())
1335
145
        w("extractByteArrayLength", extractByteArrayLengthFunction());
1336
1.38k
    }
1337
1338
10.0k
    return w.render();
1339
10.0k
  });
1340
12.9k
}
1341
1342
std::string YulUtilFunctions::extractByteArrayLengthFunction()
1343
4.70k
{
1344
4.70k
  std::string functionName = "extract_byte_array_length";
1345
4.70k
  return m_functionCollector.createFunction(functionName, [&]() {
1346
1.66k
    Whiskers w(R"(
1347
1.66k
      function <functionName>(data) -> length {
1348
1.66k
        length := div(data, 2)
1349
1.66k
        let outOfPlaceEncoding := and(data, 1)
1350
1.66k
        if iszero(outOfPlaceEncoding) {
1351
1.66k
          length := and(length, 0x7f)
1352
1.66k
        }
1353
1.66k
1354
1.66k
        if eq(outOfPlaceEncoding, lt(length, 32)) {
1355
1.66k
          <panic>()
1356
1.66k
        }
1357
1.66k
      }
1358
1.66k
    )");
1359
1.66k
    w("functionName", functionName);
1360
1.66k
    w("panic", panicFunction(PanicCode::StorageEncodingError));
1361
1.66k
    return w.render();
1362
1.66k
  });
1363
4.70k
}
1364
1365
std::string YulUtilFunctions::resizeArrayFunction(ArrayType const& _type)
1366
196
{
1367
196
  solAssert(_type.location() == DataLocation::Storage, "");
1368
196
  solUnimplementedAssert(_type.baseType()->storageBytes() <= 32);
1369
1370
196
  if (_type.isByteArrayOrString())
1371
0
    return resizeDynamicByteArrayFunction(_type);
1372
1373
196
  std::string functionName = "resize_array_" + _type.identifier();
1374
196
  return m_functionCollector.createFunction(functionName, [&]() {
1375
175
    Whiskers templ(R"(
1376
175
      function <functionName>(array, newLen) {
1377
175
        if gt(newLen, <maxArrayLength>) {
1378
175
          <panic>()
1379
175
        }
1380
175
1381
175
        let oldLen := <fetchLength>(array)
1382
175
1383
175
        <?isDynamic>
1384
175
          // Store new length
1385
175
          sstore(array, newLen)
1386
175
        </isDynamic>
1387
175
1388
175
        <?needsClearing>
1389
175
          <cleanUpArrayEnd>(array, oldLen, newLen)
1390
175
        </needsClearing>
1391
175
      })");
1392
175
      templ("functionName", functionName);
1393
175
      templ("maxArrayLength", (u256(1) << 64).str());
1394
175
      templ("panic", panicFunction(util::PanicCode::ResourceError));
1395
175
      templ("fetchLength", arrayLengthFunction(_type));
1396
175
      templ("isDynamic", _type.isDynamicallySized());
1397
175
      bool isMappingBase = _type.baseType()->category() == Type::Category::Mapping;
1398
175
      templ("needsClearing", !isMappingBase);
1399
175
      if (!isMappingBase)
1400
175
        templ("cleanUpArrayEnd", cleanUpStorageArrayEndFunction(_type));
1401
175
      return templ.render();
1402
175
  });
1403
196
}
1404
1405
std::string YulUtilFunctions::cleanUpStorageArrayEndFunction(ArrayType const& _type)
1406
175
{
1407
175
  solAssert(_type.location() == DataLocation::Storage, "");
1408
175
  solAssert(_type.baseType()->category() != Type::Category::Mapping, "");
1409
175
  solAssert(!_type.isByteArrayOrString(), "");
1410
175
  solUnimplementedAssert(_type.baseType()->storageBytes() <= 32);
1411
1412
175
  std::string functionName = "cleanup_storage_array_end_" + _type.identifier();
1413
175
  return m_functionCollector.createFunction(functionName, [&](std::vector<std::string>& _args, std::vector<std::string>&) {
1414
175
    _args = {"array", "len", "startIndex"};
1415
175
    return Whiskers(R"(
1416
175
      if lt(startIndex, len) {
1417
175
        // Size was reduced, clear end of array
1418
175
        let oldSlotCount := <convertToSize>(len)
1419
175
        let newSlotCount := <convertToSize>(startIndex)
1420
175
        let arrayDataStart := <dataPosition>(array)
1421
175
        let deleteStart := add(arrayDataStart, newSlotCount)
1422
175
        let deleteEnd := add(arrayDataStart, oldSlotCount)
1423
175
        <?packed>
1424
175
          // if we are dealing with packed array and offset is greater than zero
1425
175
          // we have  to partially clear last slot that is still used, so decreasing start by one
1426
175
          let offset := mul(mod(startIndex, <itemsPerSlot>), <storageBytes>)
1427
175
          if gt(offset, 0) { <partialClearStorageSlot>(sub(deleteStart, 1), offset) }
1428
175
        </packed>
1429
175
        <clearStorageRange>(deleteStart, deleteEnd)
1430
175
      }
1431
175
    )")
1432
175
    ("convertToSize", arrayConvertLengthToSize(_type))
1433
175
    ("dataPosition", arrayDataAreaFunction(_type))
1434
175
    ("clearStorageRange", clearStorageRangeFunction(*_type.baseType()))
1435
175
    ("packed", _type.baseType()->storageBytes() <= 16)
1436
175
    ("itemsPerSlot", std::to_string(32 / _type.baseType()->storageBytes()))
1437
175
    ("storageBytes", std::to_string(_type.baseType()->storageBytes()))
1438
175
    ("partialClearStorageSlot", partialClearStorageSlotFunction())
1439
175
    .render();
1440
175
  });
1441
175
}
1442
1443
std::string YulUtilFunctions::resizeDynamicByteArrayFunction(ArrayType const& _type)
1444
0
{
1445
0
  std::string functionName = "resize_array_" + _type.identifier();
1446
0
  return m_functionCollector.createFunction(functionName, [&](std::vector<std::string>& _args, std::vector<std::string>&) {
1447
0
    _args = {"array", "newLen"};
1448
0
    return Whiskers(R"(
1449
0
      let data := sload(array)
1450
0
      let oldLen := <extractLength>(data)
1451
0
1452
0
      if gt(newLen, oldLen) {
1453
0
        <increaseSize>(array, data, oldLen, newLen)
1454
0
      }
1455
0
1456
0
      if lt(newLen, oldLen) {
1457
0
        <decreaseSize>(array, data, oldLen, newLen)
1458
0
      }
1459
0
    )")
1460
0
    ("extractLength", extractByteArrayLengthFunction())
1461
0
    ("decreaseSize", decreaseByteArraySizeFunction(_type))
1462
0
    ("increaseSize", increaseByteArraySizeFunction(_type))
1463
0
    .render();
1464
0
  });
1465
0
}
1466
1467
std::string YulUtilFunctions::cleanUpDynamicByteArrayEndSlotsFunction(ArrayType const& _type)
1468
1.44k
{
1469
1.44k
  solAssert(_type.isByteArrayOrString(), "");
1470
1.44k
  solAssert(_type.isDynamicallySized(), "");
1471
1472
1.44k
  std::string functionName = "clean_up_bytearray_end_slots_" + _type.identifier();
1473
1.44k
  return m_functionCollector.createFunction(functionName, [&](std::vector<std::string>& _args, std::vector<std::string>&) {
1474
1.24k
    _args = {"array", "len", "startIndex"};
1475
1.24k
    return Whiskers(R"(
1476
1.24k
      if gt(len, 31) {
1477
1.24k
        let dataArea := <dataLocation>(array)
1478
1.24k
        let deleteStart := add(dataArea, <div32Ceil>(startIndex))
1479
1.24k
        // If we are clearing array to be short byte array, we want to clear only data starting from array data area.
1480
1.24k
        if lt(startIndex, 32) { deleteStart := dataArea }
1481
1.24k
        <clearStorageRange>(deleteStart, add(dataArea, <div32Ceil>(len)))
1482
1.24k
      }
1483
1.24k
    )")
1484
1.24k
    ("dataLocation", arrayDataAreaFunction(_type))
1485
1.24k
    ("div32Ceil", divide32CeilFunction())
1486
1.24k
    ("clearStorageRange", clearStorageRangeFunction(*_type.baseType()))
1487
1.24k
    .render();
1488
1.24k
  });
1489
1.44k
}
1490
1491
std::string YulUtilFunctions::decreaseByteArraySizeFunction(ArrayType const& _type)
1492
0
{
1493
0
  std::string functionName = "byte_array_decrease_size_" + _type.identifier();
1494
0
  return m_functionCollector.createFunction(functionName, [&]() {
1495
0
    return Whiskers(R"(
1496
0
      function <functionName>(array, data, oldLen, newLen) {
1497
0
        switch lt(newLen, 32)
1498
0
        case  0 {
1499
0
          let arrayDataStart := <dataPosition>(array)
1500
0
          let deleteStart := add(arrayDataStart, <div32Ceil>(newLen))
1501
0
1502
0
          // we have to partially clear last slot that is still used
1503
0
          let offset := and(newLen, 0x1f)
1504
0
          if offset { <partialClearStorageSlot>(sub(deleteStart, 1), offset) }
1505
0
1506
0
          <clearStorageRange>(deleteStart, add(arrayDataStart, <div32Ceil>(oldLen)))
1507
0
1508
0
          sstore(array, or(mul(2, newLen), 1))
1509
0
        }
1510
0
        default {
1511
0
          switch gt(oldLen, 31)
1512
0
          case 1 {
1513
0
            let arrayDataStart := <dataPosition>(array)
1514
0
            // clear whole old array, as we are transforming to short bytes array
1515
0
            <clearStorageRange>(add(arrayDataStart, 1), add(arrayDataStart, <div32Ceil>(oldLen)))
1516
0
            <transitLongToShort>(array, newLen)
1517
0
          }
1518
0
          default {
1519
0
            sstore(array, <encodeUsedSetLen>(data, newLen))
1520
0
          }
1521
0
        }
1522
0
      })")
1523
0
      ("functionName", functionName)
1524
0
      ("dataPosition", arrayDataAreaFunction(_type))
1525
0
      ("partialClearStorageSlot", partialClearStorageSlotFunction())
1526
0
      ("clearStorageRange", clearStorageRangeFunction(*_type.baseType()))
1527
0
      ("transitLongToShort", byteArrayTransitLongToShortFunction(_type))
1528
0
      ("div32Ceil", divide32CeilFunction())
1529
0
      ("encodeUsedSetLen", shortByteArrayEncodeUsedAreaSetLengthFunction())
1530
0
      .render();
1531
0
  });
1532
0
}
1533
1534
std::string YulUtilFunctions::increaseByteArraySizeFunction(ArrayType const& _type)
1535
0
{
1536
0
  std::string functionName = "byte_array_increase_size_" + _type.identifier();
1537
0
  return m_functionCollector.createFunction(functionName, [&](std::vector<std::string>& _args, std::vector<std::string>&) {
1538
0
    _args = {"array", "data", "oldLen", "newLen"};
1539
0
    return Whiskers(R"(
1540
0
      if gt(newLen, <maxArrayLength>) { <panic>() }
1541
0
1542
0
      switch lt(oldLen, 32)
1543
0
      case 0 {
1544
0
        // in this case array stays unpacked, so we just set new length
1545
0
        sstore(array, add(mul(2, newLen), 1))
1546
0
      }
1547
0
      default {
1548
0
        switch lt(newLen, 32)
1549
0
        case 0 {
1550
0
          // we need to copy elements to data area as we changed array from packed to unpacked
1551
0
          data := and(not(0xff), data)
1552
0
          sstore(<dataPosition>(array), data)
1553
0
          sstore(array, add(mul(2, newLen), 1))
1554
0
        }
1555
0
        default {
1556
0
          // here array stays packed, we just need to increase length
1557
0
          sstore(array, <encodeUsedSetLen>(data, newLen))
1558
0
        }
1559
0
      }
1560
0
    )")
1561
0
    ("panic", panicFunction(PanicCode::ResourceError))
1562
0
    ("maxArrayLength", (u256(1) << 64).str())
1563
0
    ("dataPosition", arrayDataAreaFunction(_type))
1564
0
    ("encodeUsedSetLen", shortByteArrayEncodeUsedAreaSetLengthFunction())
1565
0
    .render();
1566
0
  });
1567
0
}
1568
1569
std::string YulUtilFunctions::byteArrayTransitLongToShortFunction(ArrayType const& _type)
1570
18
{
1571
18
  std::string functionName = "transit_byte_array_long_to_short_" + _type.identifier();
1572
18
  return m_functionCollector.createFunction(functionName, [&]() {
1573
18
    return Whiskers(R"(
1574
18
      function <functionName>(array, len) {
1575
18
        // we need to copy elements from old array to new
1576
18
        // we want to copy only elements that are part of the array after resizing
1577
18
        let dataPos := <dataPosition>(array)
1578
18
        let data := <extractUsedApplyLen>(sload(dataPos), len)
1579
18
        sstore(array, data)
1580
18
        sstore(dataPos, 0)
1581
18
      })")
1582
18
      ("functionName", functionName)
1583
18
      ("dataPosition", arrayDataAreaFunction(_type))
1584
18
      ("extractUsedApplyLen", shortByteArrayEncodeUsedAreaSetLengthFunction())
1585
18
      .render();
1586
18
  });
1587
18
}
1588
1589
std::string YulUtilFunctions::shortByteArrayEncodeUsedAreaSetLengthFunction()
1590
1.26k
{
1591
1.26k
  std::string functionName = "extract_used_part_and_set_length_of_short_byte_array";
1592
1.26k
  return m_functionCollector.createFunction(functionName, [&]() {
1593
1.13k
    return Whiskers(R"(
1594
1.13k
      function <functionName>(data, len) -> used {
1595
1.13k
        // we want to save only elements that are part of the array after resizing
1596
1.13k
        // others should be set to zero
1597
1.13k
        data := <maskBytes>(data, len)
1598
1.13k
        used := or(data, mul(2, len))
1599
1.13k
      })")
1600
1.13k
      ("functionName", functionName)
1601
1.13k
      ("maskBytes", maskBytesFunctionDynamic())
1602
1.13k
      .render();
1603
1.13k
  });
1604
1.26k
}
1605
1606
std::string YulUtilFunctions::longByteArrayStorageIndexAccessNoCheckFunction()
1607
707
{
1608
707
  return m_functionCollector.createFunction(
1609
707
    "long_byte_array_index_access_no_checks",
1610
707
    [&](std::vector<std::string>& _args, std::vector<std::string>& _returnParams) {
1611
470
      _args = {"array", "index"};
1612
470
      _returnParams = {"slot", "offset"};
1613
470
      return Whiskers(R"(
1614
470
        offset := sub(31, mod(index, 0x20))
1615
470
        let dataArea := <dataAreaFunc>(array)
1616
470
        slot := add(dataArea, div(index, 0x20))
1617
470
      )")
1618
470
      ("dataAreaFunc", arrayDataAreaFunction(*TypeProvider::bytesStorage()))
1619
470
      .render();
1620
470
    }
1621
707
  );
1622
707
}
1623
1624
std::string YulUtilFunctions::storageArrayPopFunction(ArrayType const& _type)
1625
37
{
1626
37
  solAssert(_type.location() == DataLocation::Storage, "");
1627
37
  solAssert(_type.isDynamicallySized(), "");
1628
37
  solUnimplementedAssert(_type.baseType()->storageBytes() <= 32, "Base type is not yet implemented.");
1629
37
  if (_type.isByteArrayOrString())
1630
30
    return storageByteArrayPopFunction(_type);
1631
1632
7
  std::string functionName = "array_pop_" + _type.identifier();
1633
7
  return m_functionCollector.createFunction(functionName, [&]() {
1634
4
    return Whiskers(R"(
1635
4
      function <functionName>(array) {
1636
4
        let oldLen := <fetchLength>(array)
1637
4
        if iszero(oldLen) { <panic>() }
1638
4
        let newLen := sub(oldLen, 1)
1639
4
        let slot, offset := <indexAccess>(array, newLen)
1640
4
        <?+setToZero><setToZero>(slot, offset)</+setToZero>
1641
4
        sstore(array, newLen)
1642
4
      })")
1643
4
      ("functionName", functionName)
1644
4
      ("panic", panicFunction(PanicCode::EmptyArrayPop))
1645
4
      ("fetchLength", arrayLengthFunction(_type))
1646
4
      ("indexAccess", storageArrayIndexAccessFunction(_type))
1647
4
      (
1648
4
        "setToZero",
1649
4
        _type.baseType()->category() != Type::Category::Mapping ? storageSetToZeroFunction(*_type.baseType(), VariableDeclaration::Location::Unspecified) : ""
1650
4
      )
1651
4
      .render();
1652
4
  });
1653
37
}
1654
1655
std::string YulUtilFunctions::storageByteArrayPopFunction(ArrayType const& _type)
1656
30
{
1657
30
  solAssert(_type.location() == DataLocation::Storage, "");
1658
30
  solAssert(_type.isDynamicallySized(), "");
1659
30
  solAssert(_type.isByteArrayOrString(), "");
1660
1661
30
  std::string functionName = "byte_array_pop_" + _type.identifier();
1662
30
  return m_functionCollector.createFunction(functionName, [&]() {
1663
18
    return Whiskers(R"(
1664
18
      function <functionName>(array) {
1665
18
        let data := sload(array)
1666
18
        let oldLen := <extractByteArrayLength>(data)
1667
18
        if iszero(oldLen) { <panic>() }
1668
18
1669
18
        switch oldLen
1670
18
        case 32 {
1671
18
          // Here we have a special case where array transitions to shorter than 32
1672
18
          // So we need to copy data
1673
18
          <transitLongToShort>(array, 31)
1674
18
        }
1675
18
        default {
1676
18
          let newLen := sub(oldLen, 1)
1677
18
          switch lt(oldLen, 32)
1678
18
          case 1 {
1679
18
            sstore(array, <encodeUsedSetLen>(data, newLen))
1680
18
          }
1681
18
          default {
1682
18
            let slot, offset := <indexAccessNoChecks>(array, newLen)
1683
18
            <setToZero>(slot, offset)
1684
18
            sstore(array, sub(data, 2))
1685
18
          }
1686
18
        }
1687
18
      })")
1688
18
      ("functionName", functionName)
1689
18
      ("panic", panicFunction(PanicCode::EmptyArrayPop))
1690
18
      ("extractByteArrayLength", extractByteArrayLengthFunction())
1691
18
      ("transitLongToShort", byteArrayTransitLongToShortFunction(_type))
1692
18
      ("encodeUsedSetLen", shortByteArrayEncodeUsedAreaSetLengthFunction())
1693
18
      ("indexAccessNoChecks", longByteArrayStorageIndexAccessNoCheckFunction())
1694
18
      ("setToZero", storageSetToZeroFunction(*_type.baseType(), VariableDeclaration::Location::Unspecified))
1695
18
      .render();
1696
18
  });
1697
30
}
1698
1699
std::string YulUtilFunctions::storageArrayPushFunction(ArrayType const& _type, Type const* _fromType)
1700
326
{
1701
326
  solAssert(_type.location() == DataLocation::Storage, "");
1702
326
  solAssert(_type.isDynamicallySized(), "");
1703
326
  if (!_fromType)
1704
0
    _fromType = _type.baseType();
1705
326
  else if (_fromType->isValueType())
1706
326
    solUnimplementedAssert(*_fromType == *_type.baseType());
1707
1708
326
  std::string functionName =
1709
326
    std::string{"array_push_from_"} +
1710
326
    _fromType->identifier() +
1711
326
    "_to_" +
1712
326
    _type.identifier();
1713
326
  return m_functionCollector.createFunction(functionName, [&]() {
1714
204
    return Whiskers(R"(
1715
204
      function <functionName>(array <values>) {
1716
204
        <?isByteArrayOrString>
1717
204
          let data := sload(array)
1718
204
          let oldLen := <extractByteArrayLength>(data)
1719
204
          if iszero(lt(oldLen, <maxArrayLength>)) { <panic>() }
1720
204
1721
204
          switch gt(oldLen, 31)
1722
204
          case 0 {
1723
204
            let value := byte(0 <values>)
1724
204
            switch oldLen
1725
204
            case 31 {
1726
204
              // Here we have special case when array switches from short array to long array
1727
204
              // We need to copy data
1728
204
              let dataArea := <dataAreaFunction>(array)
1729
204
              data := and(data, not(0xff))
1730
204
              sstore(dataArea, or(and(0xff, value), data))
1731
204
              // New length is 32, encoded as (32 * 2 + 1)
1732
204
              sstore(array, 65)
1733
204
            }
1734
204
            default {
1735
204
              data := add(data, 2)
1736
204
              let shiftBits := mul(8, sub(31, oldLen))
1737
204
              let valueShifted := <shl>(shiftBits, and(0xff, value))
1738
204
              let mask := <shl>(shiftBits, 0xff)
1739
204
              data := or(and(data, not(mask)), valueShifted)
1740
204
              sstore(array, data)
1741
204
            }
1742
204
          }
1743
204
          default {
1744
204
            sstore(array, add(data, 2))
1745
204
            let slot, offset := <indexAccess>(array, oldLen)
1746
204
            <storeValue>(slot, offset <values>)
1747
204
          }
1748
204
        <!isByteArrayOrString>
1749
204
          let oldLen := sload(array)
1750
204
          if iszero(lt(oldLen, <maxArrayLength>)) { <panic>() }
1751
204
          sstore(array, add(oldLen, 1))
1752
204
          let slot, offset := <indexAccess>(array, oldLen)
1753
204
          <storeValue>(slot, offset <values>)
1754
204
        </isByteArrayOrString>
1755
204
      })")
1756
204
      ("functionName", functionName)
1757
204
      ("values", _fromType->sizeOnStack() == 0 ? "" : ", " + suffixedVariableNameList("value", 0, _fromType->sizeOnStack()))
1758
204
      ("panic", panicFunction(PanicCode::ResourceError))
1759
204
      ("extractByteArrayLength", _type.isByteArrayOrString() ? extractByteArrayLengthFunction() : "")
1760
204
      ("dataAreaFunction", arrayDataAreaFunction(_type))
1761
204
      ("isByteArrayOrString", _type.isByteArrayOrString())
1762
204
      ("indexAccess", storageArrayIndexAccessFunction(_type))
1763
204
      ("storeValue", updateStorageValueFunction(*_fromType, *_type.baseType(), VariableDeclaration::Location::Unspecified))
1764
204
      ("maxArrayLength", (u256(1) << 64).str())
1765
204
      ("shl", shiftLeftFunctionDynamic())
1766
204
      .render();
1767
204
  });
1768
326
}
1769
1770
std::string YulUtilFunctions::storageArrayPushZeroFunction(ArrayType const& _type)
1771
3
{
1772
3
  solAssert(_type.location() == DataLocation::Storage, "");
1773
3
  solAssert(_type.isDynamicallySized(), "");
1774
3
  solUnimplementedAssert(_type.baseType()->storageBytes() <= 32, "Base type is not yet implemented.");
1775
1776
3
  std::string functionName = "array_push_zero_" + _type.identifier();
1777
3
  return m_functionCollector.createFunction(functionName, [&]() {
1778
3
    return Whiskers(R"(
1779
3
      function <functionName>(array) -> slot, offset {
1780
3
        <?isBytes>
1781
3
          let data := sload(array)
1782
3
          let oldLen := <extractLength>(data)
1783
3
          <increaseBytesSize>(array, data, oldLen, add(oldLen, 1))
1784
3
        <!isBytes>
1785
3
          let oldLen := <fetchLength>(array)
1786
3
          if iszero(lt(oldLen, <maxArrayLength>)) { <panic>() }
1787
3
          sstore(array, add(oldLen, 1))
1788
3
        </isBytes>
1789
3
        slot, offset := <indexAccess>(array, oldLen)
1790
3
      })")
1791
3
      ("functionName", functionName)
1792
3
      ("isBytes", _type.isByteArrayOrString())
1793
3
      ("increaseBytesSize", _type.isByteArrayOrString() ? increaseByteArraySizeFunction(_type) : "")
1794
3
      ("extractLength", _type.isByteArrayOrString() ? extractByteArrayLengthFunction() : "")
1795
3
      ("panic", panicFunction(PanicCode::ResourceError))
1796
3
      ("fetchLength", arrayLengthFunction(_type))
1797
3
      ("indexAccess", storageArrayIndexAccessFunction(_type))
1798
3
      ("maxArrayLength", (u256(1) << 64).str())
1799
3
      .render();
1800
3
  });
1801
3
}
1802
1803
std::string YulUtilFunctions::partialClearStorageSlotFunction()
1804
175
{
1805
175
  std::string functionName = "partial_clear_storage_slot";
1806
175
  return m_functionCollector.createFunction(functionName, [&]() {
1807
135
    return Whiskers(R"(
1808
135
    function <functionName>(slot, offset) {
1809
135
      let mask := <shr>(mul(8, sub(32, offset)), <ones>)
1810
135
      sstore(slot, and(mask, sload(slot)))
1811
135
    }
1812
135
    )")
1813
135
    ("functionName", functionName)
1814
135
    ("ones", formatNumber((bigint(1) << 256) - 1))
1815
135
    ("shr", shiftRightFunctionDynamic())
1816
135
    .render();
1817
135
  });
1818
175
}
1819
1820
std::string YulUtilFunctions::clearStorageRangeFunction(Type const& _type)
1821
1.45k
{
1822
1.45k
  if (_type.storageBytes() < 32)
1823
1.45k
    solAssert(_type.isValueType(), "");
1824
1825
1.45k
  std::string functionName = "clear_storage_range_" + _type.identifier();
1826
1827
1.45k
  return m_functionCollector.createFunction(functionName, [&]() {
1828
1.43k
    return Whiskers(R"(
1829
1.43k
      function <functionName>(start, end) {
1830
1.43k
        for {} lt(start, end) { start := add(start, <increment>) }
1831
1.43k
        {
1832
1.43k
          <setToZero>(start, 0)
1833
1.43k
        }
1834
1.43k
      }
1835
1.43k
    )")
1836
1.43k
    ("functionName", functionName)
1837
1.43k
    ("setToZero", storageSetToZeroFunction(_type.storageBytes() < 32 ? *TypeProvider::uint256() : _type, VariableDeclaration::Location::Unspecified))
1838
1.43k
    ("increment", _type.storageSize().str())
1839
1.43k
    .render();
1840
1.43k
  });
1841
1.45k
}
1842
1843
std::string YulUtilFunctions::clearStorageArrayFunction(ArrayType const& _type)
1844
36
{
1845
36
  solAssert(_type.location() == DataLocation::Storage, "");
1846
1847
36
  if (_type.baseType()->storageBytes() < 32)
1848
18
  {
1849
18
    solAssert(_type.baseType()->isValueType(), "Invalid storage size for non-value type.");
1850
18
    solAssert(_type.baseType()->storageSize() <= 1, "Invalid storage size for type.");
1851
18
  }
1852
1853
36
  if (_type.baseType()->isValueType())
1854
36
    solAssert(_type.baseType()->storageSize() <= 1, "Invalid size for value type.");
1855
1856
36
  std::string functionName = "clear_storage_array_" + _type.identifier();
1857
1858
36
  return m_functionCollector.createFunction(functionName, [&]() {
1859
36
    return Whiskers(R"(
1860
36
      function <functionName>(slot) {
1861
36
        <?dynamic>
1862
36
          <resizeArray>(slot, 0)
1863
36
        <!dynamic>
1864
36
          <?+clearRange><clearRange>(slot, add(slot, <lenToSize>(<len>)))</+clearRange>
1865
36
        </dynamic>
1866
36
      }
1867
36
    )")
1868
36
    ("functionName", functionName)
1869
36
    ("dynamic", _type.isDynamicallySized())
1870
36
    ("resizeArray", _type.isDynamicallySized() ? resizeArrayFunction(_type) : "")
1871
36
    (
1872
36
      "clearRange",
1873
36
      _type.baseType()->category() != Type::Category::Mapping ?
1874
36
      clearStorageRangeFunction((_type.baseType()->storageBytes() < 32) ? *TypeProvider::uint256() : *_type.baseType()) :
1875
36
      ""
1876
36
    )
1877
36
    ("lenToSize", arrayConvertLengthToSize(_type))
1878
36
    ("len", _type.length().str())
1879
36
    .render();
1880
36
  });
1881
36
}
1882
1883
std::string YulUtilFunctions::clearStorageStructFunction(StructType const& _type)
1884
31
{
1885
31
  solAssert(_type.location() == DataLocation::Storage, "");
1886
1887
31
  std::string functionName = "clear_struct_storage_" + _type.identifier();
1888
1889
31
  return m_functionCollector.createFunction(functionName, [&] {
1890
31
    MemberList::MemberMap structMembers = _type.nativeMembers(nullptr);
1891
31
    std::vector<std::map<std::string, std::string>> memberSetValues;
1892
1893
31
    std::set<u256> slotsCleared;
1894
31
    for (auto const& member: structMembers)
1895
84
    {
1896
84
      if (member.type->category() == Type::Category::Mapping)
1897
0
        continue;
1898
84
      if (member.type->storageBytes() < 32)
1899
74
      {
1900
74
        auto const& slotDiff = _type.storageOffsetsOfMember(member.name).first;
1901
74
        if (!slotsCleared.count(slotDiff))
1902
47
        {
1903
47
          memberSetValues.emplace_back().emplace("clearMember", "sstore(add(slot, " + slotDiff.str() + "), 0)");
1904
47
          slotsCleared.emplace(slotDiff);
1905
47
        }
1906
74
      }
1907
10
      else
1908
10
      {
1909
10
        auto const& [memberSlotDiff, memberStorageOffset] = _type.storageOffsetsOfMember(member.name);
1910
10
        solAssert(memberStorageOffset == 0, "");
1911
1912
10
        memberSetValues.emplace_back().emplace("clearMember", Whiskers(R"(
1913
10
            <setZero>(add(slot, <memberSlotDiff>), <memberStorageOffset>)
1914
10
          )")
1915
10
          ("setZero", storageSetToZeroFunction(*member.type, VariableDeclaration::Location::Unspecified))
1916
10
          ("memberSlotDiff",  memberSlotDiff.str())
1917
10
          ("memberStorageOffset", std::to_string(memberStorageOffset))
1918
10
          .render()
1919
10
        );
1920
10
      }
1921
84
    }
1922
1923
31
    return Whiskers(R"(
1924
31
      function <functionName>(slot) {
1925
31
        <#member>
1926
31
          <clearMember>
1927
31
        </member>
1928
31
      }
1929
31
    )")
1930
31
    ("functionName", functionName)
1931
31
    ("member", memberSetValues)
1932
31
    .render();
1933
31
  });
1934
31
}
1935
1936
std::string YulUtilFunctions::copyArrayToStorageFunction(ArrayType const& _fromType, ArrayType const& _toType)
1937
199
{
1938
199
  solAssert(
1939
199
    (*_fromType.copyForLocation(_toType.location(), _toType.isPointer())).equals(dynamic_cast<ReferenceType const&>(_toType)),
1940
199
    ""
1941
199
  );
1942
199
  if (!_toType.isDynamicallySized())
1943
199
    solAssert(!_fromType.isDynamicallySized() && _fromType.length() <= _toType.length(), "");
1944
1945
199
  if (_fromType.isByteArrayOrString())
1946
27
    return copyByteArrayToStorageFunction(_fromType, _toType);
1947
172
  if (_toType.baseType()->isValueType())
1948
123
    return copyValueArrayToStorageFunction(_fromType, _toType);
1949
1950
49
  solAssert(_toType.storageStride() == 32);
1951
49
  solAssert(!_fromType.baseType()->isValueType());
1952
1953
49
  std::string functionName = "copy_array_to_storage_from_" + _fromType.identifier() + "_to_" + _toType.identifier();
1954
49
  return m_functionCollector.createFunction(functionName, [&](){
1955
49
    Whiskers templ(R"(
1956
49
      function <functionName>(slot, value<?isFromDynamicCalldata>, len</isFromDynamicCalldata>) {
1957
49
        <?fromStorage> if eq(slot, value) { leave } </fromStorage>
1958
49
        let length := <arrayLength>(value<?isFromDynamicCalldata>, len</isFromDynamicCalldata>)
1959
49
1960
49
        <resizeArray>(slot, length)
1961
49
1962
49
        let srcPtr := <srcDataLocation>(value)
1963
49
1964
49
        let elementSlot := <dstDataLocation>(slot)
1965
49
1966
49
        for { let i := 0 } lt(i, length) {i := add(i, 1)} {
1967
49
          <?fromCalldata>
1968
49
            let <stackItems> :=
1969
49
            <?dynamicallyEncodedBase>
1970
49
              <accessCalldataTail>(value, srcPtr)
1971
49
            <!dynamicallyEncodedBase>
1972
49
              srcPtr
1973
49
            </dynamicallyEncodedBase>
1974
49
          </fromCalldata>
1975
49
1976
49
          <?fromMemory>
1977
49
            let <stackItems> := <readFromMemoryOrCalldata>(srcPtr)
1978
49
          </fromMemory>
1979
49
1980
49
          <?fromStorage>
1981
49
            let <stackItems> := srcPtr
1982
49
          </fromStorage>
1983
49
1984
49
          <updateStorageValue>(elementSlot, <stackItems>)
1985
49
1986
49
          srcPtr := add(srcPtr, <srcStride>)
1987
49
1988
49
          elementSlot := add(elementSlot, <storageSize>)
1989
49
        }
1990
49
      }
1991
49
    )");
1992
49
    if (_fromType.dataStoredIn(DataLocation::Storage))
1993
49
      solAssert(!_fromType.isValueType(), "");
1994
49
    templ("functionName", functionName);
1995
49
    bool fromCalldata = _fromType.dataStoredIn(DataLocation::CallData);
1996
49
    templ("isFromDynamicCalldata", _fromType.isDynamicallySized() && fromCalldata);
1997
49
    templ("fromStorage", _fromType.dataStoredIn(DataLocation::Storage));
1998
49
    bool fromMemory = _fromType.dataStoredIn(DataLocation::Memory);
1999
49
    templ("fromMemory", fromMemory);
2000
49
    templ("fromCalldata", fromCalldata);
2001
49
    templ("srcDataLocation", arrayDataAreaFunction(_fromType));
2002
49
    if (fromCalldata)
2003
32
    {
2004
32
      templ("dynamicallyEncodedBase", _fromType.baseType()->isDynamicallyEncoded());
2005
32
      if (_fromType.baseType()->isDynamicallyEncoded())
2006
18
        templ("accessCalldataTail", accessCalldataTailFunction(*_fromType.baseType()));
2007
32
    }
2008
49
    templ("resizeArray", resizeArrayFunction(_toType));
2009
49
    templ("arrayLength",arrayLengthFunction(_fromType));
2010
49
    templ("dstDataLocation", arrayDataAreaFunction(_toType));
2011
49
    if (fromMemory || (fromCalldata && _fromType.baseType()->isValueType()))
2012
8
      templ("readFromMemoryOrCalldata", readFromMemoryOrCalldata(*_fromType.baseType(), fromCalldata));
2013
49
    templ("stackItems", suffixedVariableNameList(
2014
49
      "stackItem_",
2015
49
      0,
2016
49
      _fromType.baseType()->stackItems().size()
2017
49
    ));
2018
49
    templ("updateStorageValue", updateStorageValueFunction(*_fromType.baseType(), *_toType.baseType(), VariableDeclaration::Location::Unspecified, 0));
2019
49
    templ("srcStride",
2020
49
      fromCalldata ?
2021
32
      std::to_string(_fromType.calldataStride()) :
2022
49
        fromMemory ?
2023
8
        std::to_string(_fromType.memoryStride()) :
2024
17
        formatNumber(_fromType.baseType()->storageSize())
2025
49
    );
2026
49
    templ("storageSize", _toType.baseType()->storageSize().str());
2027
2028
49
    return templ.render();
2029
49
  });
2030
49
}
2031
2032
2033
std::string YulUtilFunctions::copyByteArrayToStorageFunction(ArrayType const& _fromType, ArrayType const& _toType)
2034
6.89k
{
2035
6.89k
  solAssert(
2036
6.89k
    (*_fromType.copyForLocation(_toType.location(), _toType.isPointer())).equals(dynamic_cast<ReferenceType const&>(_toType)),
2037
6.89k
    ""
2038
6.89k
  );
2039
6.89k
  solAssert(_fromType.isByteArrayOrString(), "");
2040
6.89k
  solAssert(_toType.isByteArrayOrString(), "");
2041
2042
6.89k
  std::string functionName = "copy_byte_array_to_storage_from_" + _fromType.identifier() + "_to_" + _toType.identifier();
2043
6.89k
  return m_functionCollector.createFunction(functionName, [&](){
2044
1.22k
    Whiskers templ(R"(
2045
1.22k
      function <functionName>(slot, src<?fromCalldata>, len</fromCalldata>) {
2046
1.22k
        <?fromStorage> if eq(slot, src) { leave } </fromStorage>
2047
1.22k
2048
1.22k
        let newLen := <arrayLength>(src<?fromCalldata>, len</fromCalldata>)
2049
1.22k
        // Make sure array length is sane
2050
1.22k
        if gt(newLen, 0xffffffffffffffff) { <panic>() }
2051
1.22k
2052
1.22k
        let oldLen := <byteArrayLength>(sload(slot))
2053
1.22k
2054
1.22k
        // potentially truncate data
2055
1.22k
        <cleanUpEndArray>(slot, oldLen, newLen)
2056
1.22k
2057
1.22k
        let srcOffset := 0
2058
1.22k
        <?fromMemory>
2059
1.22k
          srcOffset := 0x20
2060
1.22k
        </fromMemory>
2061
1.22k
2062
1.22k
        switch gt(newLen, 31)
2063
1.22k
        case 1 {
2064
1.22k
          let loopEnd := and(newLen, not(0x1f))
2065
1.22k
          <?fromStorage> src := <srcDataLocation>(src) </fromStorage>
2066
1.22k
          let dstPtr := <dstDataLocation>(slot)
2067
1.22k
          let i := 0
2068
1.22k
          for { } lt(i, loopEnd) { i := add(i, 0x20) } {
2069
1.22k
            sstore(dstPtr, <read>(add(src, srcOffset)))
2070
1.22k
            dstPtr := add(dstPtr, 1)
2071
1.22k
            srcOffset := add(srcOffset, <srcIncrement>)
2072
1.22k
          }
2073
1.22k
          if lt(loopEnd, newLen) {
2074
1.22k
            let lastValue := <read>(add(src, srcOffset))
2075
1.22k
            sstore(dstPtr, <maskBytes>(lastValue, and(newLen, 0x1f)))
2076
1.22k
          }
2077
1.22k
          sstore(slot, add(mul(newLen, 2), 1))
2078
1.22k
        }
2079
1.22k
        default {
2080
1.22k
          let value := 0
2081
1.22k
          if newLen {
2082
1.22k
            value := <read>(add(src, srcOffset))
2083
1.22k
          }
2084
1.22k
          sstore(slot, <byteArrayCombineShort>(value, newLen))
2085
1.22k
        }
2086
1.22k
      }
2087
1.22k
    )");
2088
1.22k
    templ("functionName", functionName);
2089
1.22k
    bool fromStorage = _fromType.dataStoredIn(DataLocation::Storage);
2090
1.22k
    templ("fromStorage", fromStorage);
2091
1.22k
    bool fromCalldata = _fromType.dataStoredIn(DataLocation::CallData);
2092
1.22k
    templ("fromMemory", _fromType.dataStoredIn(DataLocation::Memory));
2093
1.22k
    templ("fromCalldata", fromCalldata);
2094
1.22k
    templ("arrayLength", arrayLengthFunction(_fromType));
2095
1.22k
    templ("panic", panicFunction(PanicCode::ResourceError));
2096
1.22k
    templ("byteArrayLength", extractByteArrayLengthFunction());
2097
1.22k
    templ("dstDataLocation", arrayDataAreaFunction(_toType));
2098
1.22k
    if (fromStorage)
2099
106
      templ("srcDataLocation", arrayDataAreaFunction(_fromType));
2100
1.22k
    templ("cleanUpEndArray", cleanUpDynamicByteArrayEndSlotsFunction(_toType));
2101
1.22k
    templ("srcIncrement", std::to_string(fromStorage ? 1 : 0x20));
2102
1.22k
    templ("read", fromStorage ? "sload" : fromCalldata ? "calldataload" : "mload");
2103
1.22k
    templ("maskBytes", maskBytesFunctionDynamic());
2104
1.22k
    templ("byteArrayCombineShort", shortByteArrayEncodeUsedAreaSetLengthFunction());
2105
2106
1.22k
    return templ.render();
2107
1.22k
  });
2108
6.89k
}
2109
2110
std::string YulUtilFunctions::copyValueArrayToStorageFunction(ArrayType const& _fromType, ArrayType const& _toType)
2111
123
{
2112
123
  solAssert(_fromType.baseType()->isValueType(), "");
2113
123
  solAssert(_toType.baseType()->isValueType(), "");
2114
123
  solAssert(_fromType.baseType()->isImplicitlyConvertibleTo(*_toType.baseType()), "");
2115
2116
123
  solAssert(!_fromType.isByteArrayOrString(), "");
2117
123
  solAssert(!_toType.isByteArrayOrString(), "");
2118
123
  solAssert(_toType.dataStoredIn(DataLocation::Storage), "");
2119
2120
123
  solAssert(_fromType.storageStride() <= _toType.storageStride(), "");
2121
123
  solAssert(_toType.storageStride() <= 32, "");
2122
2123
123
  std::string functionName = "copy_array_to_storage_from_" + _fromType.identifier() + "_to_" + _toType.identifier();
2124
123
  return m_functionCollector.createFunction(functionName, [&](){
2125
123
    Whiskers templ(R"(
2126
123
      function <functionName>(dst, src<?isFromDynamicCalldata>, len</isFromDynamicCalldata>) {
2127
123
        <?isFromStorage>
2128
123
        if eq(dst, src) { leave }
2129
123
        </isFromStorage>
2130
123
        let length := <arrayLength>(src<?isFromDynamicCalldata>, len</isFromDynamicCalldata>)
2131
123
        // Make sure array length is sane
2132
123
        if gt(length, 0xffffffffffffffff) { <panic>() }
2133
123
        <resizeArray>(dst, length)
2134
123
2135
123
        let srcPtr := <srcDataLocation>(src)
2136
123
        let dstSlot := <dstDataLocation>(dst)
2137
123
2138
123
        let fullSlots := div(length, <itemsPerSlot>)
2139
123
2140
123
        <?isFromStorage>
2141
123
        let srcSlotValue := sload(srcPtr)
2142
123
        let srcItemIndexInSlot := 0
2143
123
        </isFromStorage>
2144
123
2145
123
        for { let i := 0 } lt(i, fullSlots) { i := add(i, 1) } {
2146
123
          let dstSlotValue := 0
2147
123
          <?sameTypeFromStorage>
2148
123
            dstSlotValue := <maskFull>(srcSlotValue)
2149
123
            <updateSrcPtr>
2150
123
          <!sameTypeFromStorage>
2151
123
            <?multipleItemsPerSlotDst>for { let j := 0 } lt(j, <itemsPerSlot>) { j := add(j, 1) } </multipleItemsPerSlotDst>
2152
123
            {
2153
123
              <?isFromStorage>
2154
123
              let <stackItems> := <convert>(
2155
123
                <extractFromSlot>(srcSlotValue, mul(<srcStride>, srcItemIndexInSlot))
2156
123
              )
2157
123
              <!isFromStorage>
2158
123
              let <stackItems> := <readFromMemoryOrCalldata>(srcPtr)
2159
123
              </isFromStorage>
2160
123
              let itemValue := <prepareStore>(<stackItems>)
2161
123
              dstSlotValue :=
2162
123
              <?multipleItemsPerSlotDst>
2163
123
                <updateByteSlice>(dstSlotValue, mul(<dstStride>, j), itemValue)
2164
123
              <!multipleItemsPerSlotDst>
2165
123
                itemValue
2166
123
              </multipleItemsPerSlotDst>
2167
123
2168
123
              <updateSrcPtr>
2169
123
            }
2170
123
          </sameTypeFromStorage>
2171
123
2172
123
          sstore(add(dstSlot, i), dstSlotValue)
2173
123
        }
2174
123
2175
123
        <?multipleItemsPerSlotDst>
2176
123
          let spill := sub(length, mul(fullSlots, <itemsPerSlot>))
2177
123
          if gt(spill, 0) {
2178
123
            let dstSlotValue := 0
2179
123
            <?sameTypeFromStorage>
2180
123
              dstSlotValue := <maskBytes>(srcSlotValue, mul(spill, <srcStride>))
2181
123
              <updateSrcPtr>
2182
123
            <!sameTypeFromStorage>
2183
123
              for { let j := 0 } lt(j, spill) { j := add(j, 1) } {
2184
123
                <?isFromStorage>
2185
123
                let <stackItems> := <convert>(
2186
123
                  <extractFromSlot>(srcSlotValue, mul(<srcStride>, srcItemIndexInSlot))
2187
123
                )
2188
123
                <!isFromStorage>
2189
123
                let <stackItems> := <readFromMemoryOrCalldata>(srcPtr)
2190
123
                </isFromStorage>
2191
123
                let itemValue := <prepareStore>(<stackItems>)
2192
123
                dstSlotValue := <updateByteSlice>(dstSlotValue, mul(<dstStride>, j), itemValue)
2193
123
2194
123
                <updateSrcPtr>
2195
123
              }
2196
123
            </sameTypeFromStorage>
2197
123
            sstore(add(dstSlot, fullSlots), dstSlotValue)
2198
123
          }
2199
123
        </multipleItemsPerSlotDst>
2200
123
      }
2201
123
    )");
2202
123
    if (_fromType.dataStoredIn(DataLocation::Storage))
2203
123
      solAssert(!_fromType.isValueType(), "");
2204
2205
123
    bool fromCalldata = _fromType.dataStoredIn(DataLocation::CallData);
2206
123
    bool fromStorage = _fromType.dataStoredIn(DataLocation::Storage);
2207
123
    templ("functionName", functionName);
2208
123
    templ("resizeArray", resizeArrayFunction(_toType));
2209
123
    templ("arrayLength", arrayLengthFunction(_fromType));
2210
123
    templ("panic", panicFunction(PanicCode::ResourceError));
2211
123
    templ("isFromDynamicCalldata", _fromType.isDynamicallySized() && fromCalldata);
2212
123
    templ("isFromStorage", fromStorage);
2213
123
    templ("readFromMemoryOrCalldata", readFromMemoryOrCalldata(*_fromType.baseType(), fromCalldata));
2214
123
    templ("srcDataLocation", arrayDataAreaFunction(_fromType));
2215
123
    templ("dstDataLocation", arrayDataAreaFunction(_toType));
2216
123
    templ("srcStride", std::to_string(_fromType.storageStride()));
2217
123
    templ("stackItems", suffixedVariableNameList(
2218
123
      "stackItem_",
2219
123
      0,
2220
123
      _fromType.baseType()->stackItems().size()
2221
123
    ));
2222
123
    unsigned itemsPerSlot = 32 / _toType.storageStride();
2223
123
    templ("itemsPerSlot", std::to_string(itemsPerSlot));
2224
123
    templ("multipleItemsPerSlotDst", itemsPerSlot > 1);
2225
123
    bool sameTypeFromStorage = fromStorage && (*_fromType.baseType() == *_toType.baseType());
2226
123
    if (auto functionType = dynamic_cast<FunctionType const*>(_fromType.baseType()))
2227
0
    {
2228
0
      solAssert(functionType->equalExcludingStateMutability(
2229
0
        dynamic_cast<FunctionType const&>(*_toType.baseType())
2230
0
      ));
2231
0
      sameTypeFromStorage = fromStorage;
2232
0
    }
2233
123
    templ("sameTypeFromStorage", sameTypeFromStorage);
2234
123
    if (sameTypeFromStorage)
2235
49
    {
2236
49
      templ("maskFull", maskLowerOrderBytesFunction(itemsPerSlot * _toType.storageStride()));
2237
49
      templ("maskBytes", maskLowerOrderBytesFunctionDynamic());
2238
49
    }
2239
74
    else
2240
74
    {
2241
74
      templ("dstStride", std::to_string(_toType.storageStride()));
2242
74
      templ("extractFromSlot", extractFromStorageValueDynamic(*_fromType.baseType()));
2243
74
      templ("updateByteSlice", updateByteSliceFunctionDynamic(_toType.storageStride()));
2244
74
      templ("convert", conversionFunction(*_fromType.baseType(), *_toType.baseType()));
2245
74
      templ("prepareStore", prepareStoreFunction(*_toType.baseType()));
2246
74
    }
2247
123
    if (fromStorage)
2248
63
      templ("updateSrcPtr", Whiskers(R"(
2249
63
        <?srcReadMultiPerSlot>
2250
63
          srcItemIndexInSlot := add(srcItemIndexInSlot, 1)
2251
63
          if eq(srcItemIndexInSlot, <srcItemsPerSlot>) {
2252
63
            // here we are done with this slot, we need to read next one
2253
63
            srcPtr := add(srcPtr, 1)
2254
63
            srcSlotValue := sload(srcPtr)
2255
63
            srcItemIndexInSlot := 0
2256
63
          }
2257
63
        <!srcReadMultiPerSlot>
2258
63
          srcPtr := add(srcPtr, 1)
2259
63
          srcSlotValue := sload(srcPtr)
2260
63
        </srcReadMultiPerSlot>
2261
63
        )")
2262
63
        ("srcReadMultiPerSlot", !sameTypeFromStorage && _fromType.storageStride() <= 16)
2263
63
        ("srcItemsPerSlot", std::to_string(32 / _fromType.storageStride()))
2264
63
        .render()
2265
63
      );
2266
60
    else
2267
60
      templ("updateSrcPtr", Whiskers(R"(
2268
60
          srcPtr := add(srcPtr, <srcStride>)
2269
60
        )")
2270
60
        ("srcStride", fromCalldata ? std::to_string(_fromType.calldataStride()) : std::to_string(_fromType.memoryStride()))
2271
60
        .render()
2272
60
      );
2273
2274
123
    return templ.render();
2275
123
  });
2276
123
}
2277
2278
2279
std::string YulUtilFunctions::arrayConvertLengthToSize(ArrayType const& _type)
2280
211
{
2281
211
  std::string functionName = "array_convert_length_to_size_" + _type.identifier();
2282
211
  return m_functionCollector.createFunction(functionName, [&]() {
2283
185
    Type const& baseType = *_type.baseType();
2284
2285
185
    switch (_type.location())
2286
185
    {
2287
185
      case DataLocation::Storage:
2288
185
      {
2289
185
        unsigned const baseStorageBytes = baseType.storageBytes();
2290
185
        solAssert(baseStorageBytes > 0, "");
2291
185
        solAssert(32 / baseStorageBytes > 0, "");
2292
2293
185
        return Whiskers(R"(
2294
185
          function <functionName>(length) -> size {
2295
185
            size := length
2296
185
            <?multiSlot>
2297
185
              size := <mul>(<storageSize>, length)
2298
185
            <!multiSlot>
2299
185
              // Number of slots rounded up
2300
185
              size := div(add(length, sub(<itemsPerSlot>, 1)), <itemsPerSlot>)
2301
185
            </multiSlot>
2302
185
          })")
2303
185
          ("functionName", functionName)
2304
185
          ("multiSlot", baseType.storageSize() > 1)
2305
185
          ("itemsPerSlot", std::to_string(32 / baseStorageBytes))
2306
185
          ("storageSize", baseType.storageSize().str())
2307
185
          ("mul", overflowCheckedIntMulFunction(*TypeProvider::uint256()))
2308
185
          .render();
2309
185
      }
2310
0
      case DataLocation::CallData: // fallthrough
2311
0
      case DataLocation::Memory:
2312
0
        return Whiskers(R"(
2313
0
          function <functionName>(length) -> size {
2314
0
            <?byteArray>
2315
0
              size := length
2316
0
            <!byteArray>
2317
0
              size := <mul>(length, <stride>)
2318
0
            </byteArray>
2319
0
          })")
2320
0
          ("functionName", functionName)
2321
0
          ("stride", std::to_string(_type.location() == DataLocation::Memory ? _type.memoryStride() : _type.calldataStride()))
2322
0
          ("byteArray", _type.isByteArrayOrString())
2323
0
          ("mul", overflowCheckedIntMulFunction(*TypeProvider::uint256()))
2324
0
          .render();
2325
0
      default:
2326
0
        solAssert(false, "");
2327
185
    }
2328
2329
185
  });
2330
211
}
2331
2332
std::string YulUtilFunctions::arrayAllocationSizeFunction(ArrayType const& _type)
2333
5.39k
{
2334
5.39k
  solAssert(_type.dataStoredIn(DataLocation::Memory), "");
2335
5.39k
  std::string functionName = "array_allocation_size_" + _type.identifier();
2336
5.39k
  return m_functionCollector.createFunction(functionName, [&]() {
2337
4.35k
    Whiskers w(R"(
2338
4.35k
      function <functionName>(length) -> size {
2339
4.35k
        // Make sure we can allocate memory without overflow
2340
4.35k
        if gt(length, 0xffffffffffffffff) { <panic>() }
2341
4.35k
        <?byteArray>
2342
4.35k
          size := <roundUp>(length)
2343
4.35k
        <!byteArray>
2344
4.35k
          size := mul(length, 0x20)
2345
4.35k
        </byteArray>
2346
4.35k
        <?dynamic>
2347
4.35k
          // add length slot
2348
4.35k
          size := add(size, 0x20)
2349
4.35k
        </dynamic>
2350
4.35k
      }
2351
4.35k
    )");
2352
4.35k
    w("functionName", functionName);
2353
4.35k
    w("panic", panicFunction(PanicCode::ResourceError));
2354
4.35k
    w("byteArray", _type.isByteArrayOrString());
2355
4.35k
    w("roundUp", roundUpFunction());
2356
4.35k
    w("dynamic", _type.isDynamicallySized());
2357
4.35k
    return w.render();
2358
4.35k
  });
2359
5.39k
}
2360
2361
std::string YulUtilFunctions::arrayDataAreaFunction(ArrayType const& _type)
2362
10.0k
{
2363
10.0k
  std::string functionName = "array_dataslot_" + _type.identifier();
2364
10.0k
  return m_functionCollector.createFunction(functionName, [&]() {
2365
    // No special processing for calldata arrays, because they are stored as
2366
    // offset of the data area and length on the stack, so the offset already
2367
    // points to the data area.
2368
    // This might change, if calldata arrays are stored in a single
2369
    // stack slot at some point.
2370
7.89k
    return Whiskers(R"(
2371
7.89k
      function <functionName>(ptr) -> data {
2372
7.89k
        data := ptr
2373
7.89k
        <?dynamic>
2374
7.89k
          <?memory>
2375
7.89k
            data := add(ptr, 0x20)
2376
7.89k
          </memory>
2377
7.89k
          <?storage>
2378
7.89k
            mstore(0, ptr)
2379
7.89k
            data := keccak256(0, 0x20)
2380
7.89k
          </storage>
2381
7.89k
        </dynamic>
2382
7.89k
      }
2383
7.89k
    )")
2384
7.89k
    ("functionName", functionName)
2385
7.89k
    ("dynamic", _type.isDynamicallySized())
2386
7.89k
    ("memory", _type.location() == DataLocation::Memory)
2387
7.89k
    ("storage", _type.location() == DataLocation::Storage)
2388
7.89k
    .render();
2389
7.89k
  });
2390
10.0k
}
2391
2392
std::string YulUtilFunctions::storageArrayIndexAccessFunction(ArrayType const& _type)
2393
1.21k
{
2394
1.21k
  std::string functionName = "storage_array_index_access_" + _type.identifier();
2395
1.21k
  return m_functionCollector.createFunction(functionName, [&]() {
2396
689
    return Whiskers(R"(
2397
689
      function <functionName>(array, index) -> slot, offset {
2398
689
        let arrayLength := <arrayLen>(array)
2399
689
        if iszero(lt(index, arrayLength)) { <panic>() }
2400
689
2401
689
        <?multipleItemsPerSlot>
2402
689
          <?isBytesArray>
2403
689
            switch lt(arrayLength, 0x20)
2404
689
            case 0 {
2405
689
              slot, offset := <indexAccessNoChecks>(array, index)
2406
689
            }
2407
689
            default {
2408
689
              offset := sub(31, mod(index, 0x20))
2409
689
              slot := array
2410
689
            }
2411
689
          <!isBytesArray>
2412
689
            let dataArea := <dataAreaFunc>(array)
2413
689
            slot := add(dataArea, div(index, <itemsPerSlot>))
2414
689
            offset := mul(mod(index, <itemsPerSlot>), <storageBytes>)
2415
689
          </isBytesArray>
2416
689
        <!multipleItemsPerSlot>
2417
689
          let dataArea := <dataAreaFunc>(array)
2418
689
          slot := add(dataArea, mul(index, <storageSize>))
2419
689
          offset := 0
2420
689
        </multipleItemsPerSlot>
2421
689
      }
2422
689
    )")
2423
689
    ("functionName", functionName)
2424
689
    ("panic", panicFunction(PanicCode::ArrayOutOfBounds))
2425
689
    ("arrayLen", arrayLengthFunction(_type))
2426
689
    ("dataAreaFunc", arrayDataAreaFunction(_type))
2427
689
    ("indexAccessNoChecks", longByteArrayStorageIndexAccessNoCheckFunction())
2428
689
    ("multipleItemsPerSlot", _type.baseType()->storageBytes() <= 16)
2429
689
    ("isBytesArray", _type.isByteArrayOrString())
2430
689
    ("storageSize", _type.baseType()->storageSize().str())
2431
689
    ("storageBytes", toString(_type.baseType()->storageBytes()))
2432
689
    ("itemsPerSlot", std::to_string(32 / _type.baseType()->storageBytes()))
2433
689
    .render();
2434
689
  });
2435
1.21k
}
2436
2437
std::string YulUtilFunctions::memoryArrayIndexAccessFunction(ArrayType const& _type)
2438
12.3k
{
2439
12.3k
  std::string functionName = "memory_array_index_access_" + _type.identifier();
2440
12.3k
  return m_functionCollector.createFunction(functionName, [&]() {
2441
3.14k
    return Whiskers(R"(
2442
3.14k
      function <functionName>(baseRef, index) -> addr {
2443
3.14k
        if iszero(lt(index, <arrayLen>(baseRef))) {
2444
3.14k
          <panic>()
2445
3.14k
        }
2446
3.14k
2447
3.14k
        let offset := mul(index, <stride>)
2448
3.14k
        <?dynamicallySized>
2449
3.14k
          offset := add(offset, 32)
2450
3.14k
        </dynamicallySized>
2451
3.14k
        addr := add(baseRef, offset)
2452
3.14k
      }
2453
3.14k
    )")
2454
3.14k
    ("functionName", functionName)
2455
3.14k
    ("panic", panicFunction(PanicCode::ArrayOutOfBounds))
2456
3.14k
    ("arrayLen", arrayLengthFunction(_type))
2457
3.14k
    ("stride", std::to_string(_type.memoryStride()))
2458
3.14k
    ("dynamicallySized", _type.isDynamicallySized())
2459
3.14k
    .render();
2460
3.14k
  });
2461
12.3k
}
2462
2463
std::string YulUtilFunctions::calldataArrayIndexAccessFunction(ArrayType const& _type)
2464
5
{
2465
5
  solAssert(_type.dataStoredIn(DataLocation::CallData), "");
2466
5
  std::string functionName = "calldata_array_index_access_" + _type.identifier();
2467
5
  return m_functionCollector.createFunction(functionName, [&]() {
2468
5
    return Whiskers(R"(
2469
5
      function <functionName>(base_ref<?dynamicallySized>, length</dynamicallySized>, index) -> addr<?dynamicallySizedBase>, len</dynamicallySizedBase> {
2470
5
        if iszero(lt(index, <?dynamicallySized>length<!dynamicallySized><arrayLen></dynamicallySized>)) { <panic>() }
2471
5
        addr := add(base_ref, mul(index, <stride>))
2472
5
        <?dynamicallyEncodedBase>
2473
5
          addr<?dynamicallySizedBase>, len</dynamicallySizedBase> := <accessCalldataTail>(base_ref, addr)
2474
5
        </dynamicallyEncodedBase>
2475
5
      }
2476
5
    )")
2477
5
    ("functionName", functionName)
2478
5
    ("panic", panicFunction(PanicCode::ArrayOutOfBounds))
2479
5
    ("stride", std::to_string(_type.calldataStride()))
2480
5
    ("dynamicallySized", _type.isDynamicallySized())
2481
5
    ("dynamicallyEncodedBase", _type.baseType()->isDynamicallyEncoded())
2482
5
    ("dynamicallySizedBase", _type.baseType()->isDynamicallySized())
2483
5
    ("arrayLen",  toCompactHexWithPrefix(_type.length()))
2484
5
    ("accessCalldataTail", _type.baseType()->isDynamicallyEncoded() ? accessCalldataTailFunction(*_type.baseType()): "")
2485
5
    .render();
2486
5
  });
2487
5
}
2488
2489
std::string YulUtilFunctions::calldataArrayIndexRangeAccess(ArrayType const& _type)
2490
370
{
2491
370
  solAssert(_type.dataStoredIn(DataLocation::CallData), "");
2492
370
  solAssert(_type.isDynamicallySized(), "");
2493
370
  std::string functionName = "calldata_array_index_range_access_" + _type.identifier();
2494
370
  return m_functionCollector.createFunction(functionName, [&]() {
2495
96
    return Whiskers(R"(
2496
96
      function <functionName>(offset, length, startIndex, endIndex) -> offsetOut, lengthOut {
2497
96
        if gt(startIndex, endIndex) { <revertSliceStartAfterEnd>() }
2498
96
        if gt(endIndex, length) { <revertSliceGreaterThanLength>() }
2499
96
        offsetOut := add(offset, mul(startIndex, <stride>))
2500
96
        lengthOut := sub(endIndex, startIndex)
2501
96
      }
2502
96
    )")
2503
96
    ("functionName", functionName)
2504
96
    ("stride", std::to_string(_type.calldataStride()))
2505
96
    ("revertSliceStartAfterEnd", revertReasonIfDebugFunction("Slice starts after end"))
2506
96
    ("revertSliceGreaterThanLength", revertReasonIfDebugFunction("Slice is greater than length"))
2507
96
    .render();
2508
96
  });
2509
370
}
2510
2511
std::string YulUtilFunctions::accessCalldataTailFunction(Type const& _type)
2512
14.5k
{
2513
14.5k
  solAssert(_type.isDynamicallyEncoded(), "");
2514
14.5k
  solAssert(_type.dataStoredIn(DataLocation::CallData), "");
2515
14.5k
  std::string functionName = "access_calldata_tail_" + _type.identifier();
2516
14.5k
  return m_functionCollector.createFunction(functionName, [&]() {
2517
803
    return Whiskers(R"(
2518
803
      function <functionName>(base_ref, ptr_to_tail) -> addr<?dynamicallySized>, length</dynamicallySized> {
2519
803
        let rel_offset_of_tail := calldataload(ptr_to_tail)
2520
803
        if iszero(slt(rel_offset_of_tail, sub(sub(calldatasize(), base_ref), sub(<neededLength>, 1)))) { <invalidCalldataTailOffset>() }
2521
803
        addr := add(base_ref, rel_offset_of_tail)
2522
803
        <?dynamicallySized>
2523
803
          length := calldataload(addr)
2524
803
          if gt(length, 0xffffffffffffffff) { <invalidCalldataTailLength>() }
2525
803
          addr := add(addr, 32)
2526
803
          if sgt(addr, sub(calldatasize(), mul(length, <calldataStride>))) { <shortCalldataTail>() }
2527
803
        </dynamicallySized>
2528
803
      }
2529
803
    )")
2530
803
    ("functionName", functionName)
2531
803
    ("dynamicallySized", _type.isDynamicallySized())
2532
803
    ("neededLength", toCompactHexWithPrefix(_type.calldataEncodedTailSize()))
2533
803
    ("calldataStride", toCompactHexWithPrefix(_type.isDynamicallySized() ? dynamic_cast<ArrayType const&>(_type).calldataStride() : 0))
2534
803
    ("invalidCalldataTailOffset", revertReasonIfDebugFunction("Invalid calldata tail offset"))
2535
803
    ("invalidCalldataTailLength", revertReasonIfDebugFunction("Invalid calldata tail length"))
2536
803
    ("shortCalldataTail", revertReasonIfDebugFunction("Calldata tail too short"))
2537
803
    .render();
2538
803
  });
2539
14.5k
}
2540
2541
std::string YulUtilFunctions::nextArrayElementFunction(ArrayType const& _type)
2542
5.05k
{
2543
5.05k
  solAssert(!_type.isByteArrayOrString(), "");
2544
5.05k
  if (_type.dataStoredIn(DataLocation::Storage))
2545
5.05k
    solAssert(_type.baseType()->storageBytes() > 16, "");
2546
5.05k
  std::string functionName = "array_nextElement_" + _type.identifier();
2547
5.05k
  return m_functionCollector.createFunction(functionName, [&]() {
2548
5.04k
    Whiskers templ(R"(
2549
5.04k
      function <functionName>(ptr) -> next {
2550
5.04k
        next := add(ptr, <advance>)
2551
5.04k
      }
2552
5.04k
    )");
2553
5.04k
    templ("functionName", functionName);
2554
5.04k
    switch (_type.location())
2555
5.04k
    {
2556
3.95k
    case DataLocation::Memory:
2557
3.95k
      templ("advance", "0x20");
2558
3.95k
      break;
2559
435
    case DataLocation::Storage:
2560
435
    {
2561
435
      u256 size = _type.baseType()->storageSize();
2562
435
      solAssert(size >= 1, "");
2563
435
      templ("advance", toCompactHexWithPrefix(size));
2564
435
      break;
2565
435
    }
2566
0
    case DataLocation::Transient:
2567
0
      solUnimplemented("Transient data location is only supported for value types.");
2568
0
      break;
2569
654
    case DataLocation::CallData:
2570
654
    {
2571
654
      u256 size = _type.calldataStride();
2572
654
      solAssert(size >= 32 && size % 32 == 0, "");
2573
654
      templ("advance", toCompactHexWithPrefix(size));
2574
654
      break;
2575
654
    }
2576
5.04k
    }
2577
5.04k
    return templ.render();
2578
5.04k
  });
2579
5.05k
}
2580
2581
std::string YulUtilFunctions::copyArrayFromStorageToMemoryFunction(ArrayType const& _from, ArrayType const& _to)
2582
182
{
2583
182
  solAssert(_from.dataStoredIn(DataLocation::Storage), "");
2584
182
  solAssert(_to.dataStoredIn(DataLocation::Memory), "");
2585
182
  solAssert(_from.isDynamicallySized() == _to.isDynamicallySized(), "");
2586
182
  if (!_from.isDynamicallySized())
2587
182
    solAssert(_from.length() == _to.length(), "");
2588
2589
182
  std::string functionName = "copy_array_from_storage_to_memory_" + _from.identifier();
2590
2591
182
  return m_functionCollector.createFunction(functionName, [&]() {
2592
182
    if (_from.baseType()->isValueType())
2593
165
    {
2594
165
      solAssert(*_from.baseType() == *_to.baseType(), "");
2595
165
      ABIFunctions abi(m_evmVersion, m_eofVersion, m_revertStrings, m_functionCollector);
2596
165
      return Whiskers(R"(
2597
165
        function <functionName>(slot) -> memPtr {
2598
165
          memPtr := <allocateUnbounded>()
2599
165
          let end := <encode>(slot, memPtr)
2600
165
          <finalizeAllocation>(memPtr, sub(end, memPtr))
2601
165
        }
2602
165
      )")
2603
165
      ("functionName", functionName)
2604
165
      ("allocateUnbounded", allocateUnboundedFunction())
2605
165
      (
2606
165
        "encode",
2607
165
        abi.abiEncodeAndReturnUpdatedPosFunction(_from, _to, ABIFunctions::EncodingOptions{})
2608
165
      )
2609
165
      ("finalizeAllocation", finalizeAllocationFunction())
2610
165
      .render();
2611
165
    }
2612
17
    else
2613
17
    {
2614
17
      solAssert(_to.memoryStride() == 32, "");
2615
17
      solAssert(_to.baseType()->dataStoredIn(DataLocation::Memory), "");
2616
17
      solAssert(_from.baseType()->dataStoredIn(DataLocation::Storage), "");
2617
17
      solAssert(!_from.isByteArrayOrString(), "");
2618
17
      solAssert(*_to.withLocation(DataLocation::Storage, _from.isPointer()) == _from, "");
2619
17
      return Whiskers(R"(
2620
17
        function <functionName>(slot) -> memPtr {
2621
17
          let length := <lengthFunction>(slot)
2622
17
          memPtr := <allocateArray>(length)
2623
17
          let mpos := memPtr
2624
17
          <?dynamic>mpos := add(mpos, 0x20)</dynamic>
2625
17
          let spos := <arrayDataArea>(slot)
2626
17
          for { let i := 0 } lt(i, length) { i := add(i, 1) } {
2627
17
            mstore(mpos, <convert>(spos))
2628
17
            mpos := add(mpos, 0x20)
2629
17
            spos := add(spos, <baseStorageSize>)
2630
17
          }
2631
17
        }
2632
17
      )")
2633
17
      ("functionName", functionName)
2634
17
      ("lengthFunction", arrayLengthFunction(_from))
2635
17
      ("allocateArray", allocateMemoryArrayFunction(_to))
2636
17
      ("arrayDataArea", arrayDataAreaFunction(_from))
2637
17
      ("dynamic", _to.isDynamicallySized())
2638
17
      ("convert", conversionFunction(*_from.baseType(), *_to.baseType()))
2639
17
      ("baseStorageSize", _from.baseType()->storageSize().str())
2640
17
      .render();
2641
17
    }
2642
182
  });
2643
182
}
2644
2645
std::string YulUtilFunctions::bytesOrStringConcatFunction(
2646
  std::vector<Type const*> const& _argumentTypes,
2647
  FunctionType::Kind _functionTypeKind
2648
)
2649
43
{
2650
43
  solAssert(_functionTypeKind == FunctionType::Kind::BytesConcat || _functionTypeKind == FunctionType::Kind::StringConcat);
2651
43
  std::string functionName = (_functionTypeKind == FunctionType::Kind::StringConcat) ? "string_concat" : "bytes_concat";
2652
43
  size_t totalParams = 0;
2653
43
  std::vector<Type const*> targetTypes;
2654
2655
43
  for (Type const* argumentType: _argumentTypes)
2656
86
  {
2657
86
    if (_functionTypeKind == FunctionType::Kind::StringConcat)
2658
86
      solAssert(argumentType->isImplicitlyConvertibleTo(*TypeProvider::stringMemory()));
2659
86
    else if (_functionTypeKind == FunctionType::Kind::BytesConcat)
2660
86
      solAssert(
2661
86
        argumentType->isImplicitlyConvertibleTo(*TypeProvider::bytesMemory()) ||
2662
86
        argumentType->isImplicitlyConvertibleTo(*TypeProvider::fixedBytes(32))
2663
86
      );
2664
2665
86
    if (argumentType->category() == Type::Category::FixedBytes)
2666
8
      targetTypes.emplace_back(argumentType);
2667
78
    else if (
2668
78
      auto const* literalType = dynamic_cast<StringLiteralType const*>(argumentType);
2669
78
      literalType && !literalType->value().empty() && literalType->value().size() <= 32
2670
78
    )
2671
11
      targetTypes.emplace_back(TypeProvider::fixedBytes(static_cast<unsigned>(literalType->value().size())));
2672
67
    else
2673
67
    {
2674
67
      solAssert(!dynamic_cast<RationalNumberType const*>(argumentType));
2675
67
      targetTypes.emplace_back(
2676
67
        _functionTypeKind == FunctionType::Kind::StringConcat ?
2677
0
        TypeProvider::stringMemory() :
2678
67
        TypeProvider::bytesMemory()
2679
67
      );
2680
67
    }
2681
86
    totalParams += argumentType->sizeOnStack();
2682
86
    functionName += "_" + argumentType->identifier();
2683
86
  }
2684
43
  return m_functionCollector.createFunction(functionName, [&]() {
2685
40
    Whiskers templ(R"(
2686
40
      function <functionName>(<parameters>) -> outPtr {
2687
40
        outPtr := <allocateUnbounded>()
2688
40
        let dataStart := add(outPtr, 0x20)
2689
40
        let dataEnd := <encodePacked>(dataStart<?+parameters>, <parameters></+parameters>)
2690
40
        mstore(outPtr, sub(dataEnd, dataStart))
2691
40
        <finalizeAllocation>(outPtr, sub(dataEnd, outPtr))
2692
40
      }
2693
40
    )");
2694
40
    templ("functionName", functionName);
2695
40
    templ("parameters", suffixedVariableNameList("param_", 0, totalParams));
2696
40
    templ("allocateUnbounded", allocateUnboundedFunction());
2697
40
    templ("finalizeAllocation", finalizeAllocationFunction());
2698
40
    templ(
2699
40
      "encodePacked",
2700
40
      ABIFunctions{m_evmVersion, m_eofVersion, m_revertStrings, m_functionCollector}.tupleEncoderPacked(
2701
40
        _argumentTypes,
2702
40
        targetTypes
2703
40
      )
2704
40
    );
2705
40
    return templ.render();
2706
40
  });
2707
43
}
2708
2709
std::string YulUtilFunctions::mappingIndexAccessFunction(MappingType const& _mappingType, Type const& _keyType)
2710
407
{
2711
407
  std::string functionName = "mapping_index_access_" + _mappingType.identifier() + "_of_" + _keyType.identifier();
2712
407
  return m_functionCollector.createFunction(functionName, [&]() {
2713
235
    if (_mappingType.keyType()->isDynamicallySized())
2714
198
      return Whiskers(R"(
2715
198
        function <functionName>(slot <?+key>,</+key> <key>) -> dataSlot {
2716
198
          dataSlot := <hash>(<key> <?+key>,</+key> slot)
2717
198
        }
2718
198
      )")
2719
198
      ("functionName", functionName)
2720
198
      ("key", suffixedVariableNameList("key_", 0, _keyType.sizeOnStack()))
2721
198
      ("hash", packedHashFunction(
2722
198
        {&_keyType, TypeProvider::uint256()},
2723
198
        {_mappingType.keyType(), TypeProvider::uint256()}
2724
198
      ))
2725
198
      .render();
2726
37
    else
2727
37
    {
2728
37
      solAssert(CompilerUtils::freeMemoryPointer >= 0x40, "");
2729
37
      solAssert(!_mappingType.keyType()->isDynamicallyEncoded(), "");
2730
37
      solAssert(_mappingType.keyType()->calldataEncodedSize(false) <= 0x20, "");
2731
37
      Whiskers templ(R"(
2732
37
        function <functionName>(slot <key>) -> dataSlot {
2733
37
          mstore(0, <convertedKey>)
2734
37
          mstore(0x20, slot)
2735
37
          dataSlot := keccak256(0, 0x40)
2736
37
        }
2737
37
      )");
2738
37
      templ("functionName", functionName);
2739
37
      templ("key", _keyType.sizeOnStack() == 1 ? ", key" : "");
2740
37
      if (_keyType.sizeOnStack() == 0)
2741
0
        templ("convertedKey", conversionFunction(_keyType, *_mappingType.keyType()) + "()");
2742
37
      else
2743
37
        templ("convertedKey", conversionFunction(_keyType, *_mappingType.keyType()) + "(key)");
2744
37
      return templ.render();
2745
37
    }
2746
235
  });
2747
407
}
2748
2749
std::string YulUtilFunctions::readFromStorage(
2750
  Type const& _type,
2751
  size_t _offset,
2752
  bool _splitFunctionTypes,
2753
  VariableDeclaration::Location _location
2754
)
2755
599
{
2756
599
  if (_type.isValueType())
2757
592
    return readFromStorageValueType(_type, _offset, _splitFunctionTypes, _location);
2758
7
  else
2759
7
  {
2760
7
    solAssert(_location != VariableDeclaration::Location::Transient);
2761
7
    solAssert(_offset == 0, "");
2762
7
    return readFromStorageReferenceType(_type);
2763
7
  }
2764
599
}
2765
2766
std::string YulUtilFunctions::readFromStorageDynamic(
2767
  Type const& _type,
2768
  bool _splitFunctionTypes,
2769
  VariableDeclaration::Location _location
2770
)
2771
1.18k
{
2772
1.18k
  if (_type.isValueType())
2773
1.08k
    return readFromStorageValueType(_type, {}, _splitFunctionTypes, _location);
2774
2775
105
  solAssert(_location != VariableDeclaration::Location::Transient);
2776
105
  std::string functionName =
2777
105
    "read_from_storage__dynamic_" +
2778
105
    std::string(_splitFunctionTypes ? "split_" : "") +
2779
105
    _type.identifier();
2780
2781
105
  return m_functionCollector.createFunction(functionName, [&] {
2782
91
    return Whiskers(R"(
2783
91
      function <functionName>(slot, offset) -> value {
2784
91
        if gt(offset, 0) { <panic>() }
2785
91
        value := <readFromStorage>(slot)
2786
91
      }
2787
91
    )")
2788
91
    ("functionName", functionName)
2789
91
    ("panic", panicFunction(util::PanicCode::Generic))
2790
91
    ("readFromStorage", readFromStorageReferenceType(_type))
2791
91
    .render();
2792
91
  });
2793
105
}
2794
2795
std::string YulUtilFunctions::readFromStorageValueType(
2796
  Type const& _type,
2797
  std::optional<size_t> _offset,
2798
  bool _splitFunctionTypes,
2799
  VariableDeclaration::Location _location
2800
)
2801
1.68k
{
2802
1.68k
  solAssert(_type.isValueType(), "");
2803
1.68k
  solAssert(
2804
1.68k
    _location == VariableDeclaration::Location::Transient ||
2805
1.68k
    _location == VariableDeclaration::Location::Unspecified,
2806
1.68k
    "Variable location can only be transient or plain storage"
2807
1.68k
  );
2808
2809
1.68k
  std::string functionName =
2810
1.68k
    "read_from_" +
2811
1.68k
    (_location == VariableDeclaration::Location::Transient ? "transient_"s : "") +
2812
1.68k
    "storage_" +
2813
1.68k
    std::string(_splitFunctionTypes ? "split_" : "") + (
2814
1.68k
      _offset.has_value() ?
2815
604
      "offset_" + std::to_string(*_offset) :
2816
1.68k
      "dynamic"
2817
1.68k
    ) +
2818
1.68k
    "_" +
2819
1.68k
    _type.identifier();
2820
2821
1.68k
  return m_functionCollector.createFunction(functionName, [&] {
2822
1.24k
    Whiskers templ(R"(
2823
1.24k
      function <functionName>(slot<?dynamic>, offset</dynamic>) -> <?split>addr, selector<!split>value</split> {
2824
1.24k
        <?split>let</split> value := <extract>(<loadOpcode>(slot)<?dynamic>, offset</dynamic>)
2825
1.24k
        <?split>
2826
1.24k
          addr, selector := <splitFunction>(value)
2827
1.24k
        </split>
2828
1.24k
      }
2829
1.24k
    )");
2830
1.24k
    templ("functionName", functionName);
2831
1.24k
    templ("dynamic", !_offset.has_value());
2832
1.24k
    templ("loadOpcode", _location == VariableDeclaration::Location::Transient ? "tload" : "sload");
2833
1.24k
    if (_offset.has_value())
2834
441
      templ("extract", extractFromStorageValue(_type, *_offset));
2835
802
    else
2836
802
      templ("extract", extractFromStorageValueDynamic(_type));
2837
1.24k
    auto const* funType = dynamic_cast<FunctionType const*>(&_type);
2838
1.24k
    bool split = _splitFunctionTypes && funType && funType->kind() == FunctionType::Kind::External;
2839
1.24k
    templ("split", split);
2840
1.24k
    if (split)
2841
0
      templ("splitFunction", splitExternalFunctionIdFunction());
2842
1.24k
    return templ.render();
2843
1.24k
  });
2844
1.68k
}
2845
2846
std::string YulUtilFunctions::readFromStorageReferenceType(Type const& _type)
2847
98
{
2848
98
  if (auto const* arrayType = dynamic_cast<ArrayType const*>(&_type))
2849
98
  {
2850
98
    solAssert(arrayType->dataStoredIn(DataLocation::Memory), "");
2851
98
    return copyArrayFromStorageToMemoryFunction(
2852
98
      dynamic_cast<ArrayType const&>(*arrayType->copyForLocation(DataLocation::Storage, false)),
2853
98
      *arrayType
2854
98
    );
2855
98
  }
2856
0
  solAssert(_type.category() == Type::Category::Struct, "");
2857
2858
0
  std::string functionName = "read_from_storage_reference_type_" + _type.identifier();
2859
2860
0
  auto const& structType = dynamic_cast<StructType const&>(_type);
2861
0
  solAssert(structType.location() == DataLocation::Memory, "");
2862
0
  MemberList::MemberMap structMembers = structType.nativeMembers(nullptr);
2863
0
  std::vector<std::map<std::string, std::string>> memberSetValues(structMembers.size());
2864
0
  for (size_t i = 0; i < structMembers.size(); ++i)
2865
0
  {
2866
0
    auto const& [memberSlotDiff, memberStorageOffset] = structType.storageOffsetsOfMember(structMembers[i].name);
2867
0
    solAssert(structMembers[i].type->isValueType() || memberStorageOffset == 0, "");
2868
2869
0
    memberSetValues[i]["setMember"] = Whiskers(R"(
2870
0
      {
2871
0
        let <memberValues> := <readFromStorage>(add(slot, <memberSlotDiff>))
2872
0
        <writeToMemory>(add(value, <memberMemoryOffset>), <memberValues>)
2873
0
      }
2874
0
    )")
2875
0
    ("memberValues", suffixedVariableNameList("memberValue_", 0, structMembers[i].type->stackItems().size()))
2876
0
    ("memberMemoryOffset", structType.memoryOffsetOfMember(structMembers[i].name).str())
2877
0
    ("memberSlotDiff",  memberSlotDiff.str())
2878
0
    ("readFromStorage", readFromStorage(*structMembers[i].type, memberStorageOffset, true, VariableDeclaration::Location::Unspecified))
2879
0
    ("writeToMemory", writeToMemoryFunction(*structMembers[i].type))
2880
0
    .render();
2881
0
  }
2882
2883
0
  return m_functionCollector.createFunction(functionName, [&] {
2884
0
    return Whiskers(R"(
2885
0
      function <functionName>(slot) -> value {
2886
0
        value := <allocStruct>()
2887
0
        <#member>
2888
0
          <setMember>
2889
0
        </member>
2890
0
      }
2891
0
    )")
2892
0
    ("functionName", functionName)
2893
0
    ("allocStruct", allocateMemoryStructFunction(structType))
2894
0
    ("member", memberSetValues)
2895
0
    .render();
2896
0
  });
2897
0
}
2898
2899
std::string YulUtilFunctions::readFromMemory(Type const& _type)
2900
5.37k
{
2901
5.37k
  return readFromMemoryOrCalldata(_type, false);
2902
5.37k
}
2903
2904
std::string YulUtilFunctions::readFromCalldata(Type const& _type)
2905
60
{
2906
60
  return readFromMemoryOrCalldata(_type, true);
2907
60
}
2908
2909
std::string YulUtilFunctions::updateStorageValueFunction(
2910
  Type const& _fromType,
2911
  Type const& _toType,
2912
  VariableDeclaration::Location _location,
2913
  std::optional<unsigned> const& _offset
2914
)
2915
3.54k
{
2916
3.54k
  solAssert(
2917
3.54k
    _location == VariableDeclaration::Location::Transient ||
2918
3.54k
    _location == VariableDeclaration::Location::Unspecified,
2919
3.54k
    "Variable location can only be transient or plain storage"
2920
3.54k
  );
2921
2922
3.54k
  std::string const functionName =
2923
3.54k
    "update_" +
2924
3.54k
    (_location == VariableDeclaration::Location::Transient ? "transient_"s : "") +
2925
3.54k
    "storage_value_" +
2926
3.54k
    (_offset.has_value() ? ("offset_" + std::to_string(*_offset)) + "_" : "") +
2927
3.54k
    _fromType.identifier() +
2928
3.54k
    "_to_" +
2929
3.54k
    _toType.identifier();
2930
2931
3.54k
  return m_functionCollector.createFunction(functionName, [&] {
2932
2.99k
    if (_toType.isValueType())
2933
2.55k
    {
2934
2.55k
      solAssert(_fromType.isImplicitlyConvertibleTo(_toType), "");
2935
2.55k
      solAssert(_toType.storageBytes() <= 32, "Invalid storage bytes size.");
2936
2.55k
      solAssert(_toType.storageBytes() > 0, "Invalid storage bytes size.");
2937
2938
2.55k
      return Whiskers(R"(
2939
2.55k
        function <functionName>(slot, <offset><fromValues>) {
2940
2.55k
          let <toValues> := <convert>(<fromValues>)
2941
2.55k
          <storeOpcode>(slot, <update>(<loadOpcode>(slot), <offset><prepare>(<toValues>)))
2942
2.55k
        }
2943
2.55k
2944
2.55k
      )")
2945
2.55k
      ("functionName", functionName)
2946
2.55k
      ("update",
2947
2.55k
        _offset.has_value() ?
2948
1.05k
          updateByteSliceFunction(_toType.storageBytes(), *_offset) :
2949
2.55k
          updateByteSliceFunctionDynamic(_toType.storageBytes())
2950
2.55k
      )
2951
2.55k
      ("offset", _offset.has_value() ? "" : "offset, ")
2952
2.55k
      ("convert", conversionFunction(_fromType, _toType))
2953
2.55k
      ("fromValues", suffixedVariableNameList("value_", 0, _fromType.sizeOnStack()))
2954
2.55k
      ("toValues", suffixedVariableNameList("convertedValue_", 0, _toType.sizeOnStack()))
2955
2.55k
      ("storeOpcode", _location == VariableDeclaration::Location::Transient ? "tstore" : "sstore")
2956
2.55k
      ("loadOpcode", _location == VariableDeclaration::Location::Transient ? "tload" : "sload")
2957
2.55k
      ("prepare", prepareStoreFunction(_toType))
2958
2.55k
      .render();
2959
2.55k
    }
2960
2961
446
    solAssert(_location != VariableDeclaration::Location::Transient);
2962
446
    auto const* toReferenceType = dynamic_cast<ReferenceType const*>(&_toType);
2963
446
    auto const* fromReferenceType = dynamic_cast<ReferenceType const*>(&_fromType);
2964
446
    solAssert(toReferenceType, "");
2965
2966
446
    if (!fromReferenceType)
2967
212
    {
2968
212
      solAssert(_fromType.category() == Type::Category::StringLiteral, "");
2969
212
      solAssert(toReferenceType->category() == Type::Category::Array, "");
2970
212
      auto const& toArrayType = dynamic_cast<ArrayType const&>(*toReferenceType);
2971
212
      solAssert(toArrayType.isByteArrayOrString(), "");
2972
2973
212
      return Whiskers(R"(
2974
212
        function <functionName>(slot<?dynamicOffset>, offset</dynamicOffset>) {
2975
212
          <?dynamicOffset>if offset { <panic>() }</dynamicOffset>
2976
212
          <copyToStorage>(slot)
2977
212
        }
2978
212
      )")
2979
212
      ("functionName", functionName)
2980
212
      ("dynamicOffset", !_offset.has_value())
2981
212
      ("panic", panicFunction(PanicCode::Generic))
2982
212
      ("copyToStorage", copyLiteralToStorageFunction(dynamic_cast<StringLiteralType const&>(_fromType).value()))
2983
212
      .render();
2984
212
    }
2985
2986
234
    solAssert((*toReferenceType->copyForLocation(
2987
234
      fromReferenceType->location(),
2988
234
      fromReferenceType->isPointer()
2989
234
    ).get()).equals(*fromReferenceType), "");
2990
2991
234
    if (fromReferenceType->category() == Type::Category::ArraySlice)
2992
234
      solAssert(toReferenceType->category() == Type::Category::Array, "");
2993
234
    else
2994
234
      solAssert(toReferenceType->category() == fromReferenceType->category(), "");
2995
234
    solAssert(_offset.value_or(0) == 0, "");
2996
2997
234
    Whiskers templ(R"(
2998
234
      function <functionName>(slot, <?dynamicOffset>offset, </dynamicOffset><value>) {
2999
234
        <?dynamicOffset>if offset { <panic>() }</dynamicOffset>
3000
234
        <copyToStorage>(slot, <value>)
3001
234
      }
3002
234
    )");
3003
234
    templ("functionName", functionName);
3004
234
    templ("dynamicOffset", !_offset.has_value());
3005
234
    templ("panic", panicFunction(PanicCode::Generic));
3006
234
    templ("value", suffixedVariableNameList("value_", 0, _fromType.sizeOnStack()));
3007
234
    if (_fromType.category() == Type::Category::Array)
3008
199
      templ("copyToStorage", copyArrayToStorageFunction(
3009
199
        dynamic_cast<ArrayType const&>(_fromType),
3010
199
        dynamic_cast<ArrayType const&>(_toType)
3011
199
      ));
3012
35
    else if (_fromType.category() == Type::Category::ArraySlice)
3013
0
    {
3014
0
      solAssert(
3015
0
        _fromType.dataStoredIn(DataLocation::CallData),
3016
0
        "Currently only calldata array slices are supported!"
3017
0
      );
3018
0
      templ("copyToStorage", copyArrayToStorageFunction(
3019
0
        dynamic_cast<ArraySliceType const&>(_fromType).arrayType(),
3020
0
        dynamic_cast<ArrayType const&>(_toType)
3021
0
      ));
3022
0
    }
3023
35
    else
3024
35
      templ("copyToStorage", copyStructToStorageFunction(
3025
35
        dynamic_cast<StructType const&>(_fromType),
3026
35
        dynamic_cast<StructType const&>(_toType)
3027
35
      ));
3028
3029
234
    return templ.render();
3030
234
  });
3031
3.54k
}
3032
3033
std::string YulUtilFunctions::writeToMemoryFunction(Type const& _type)
3034
1.92k
{
3035
1.92k
  std::string const functionName = "write_to_memory_" + _type.identifier();
3036
3037
1.92k
  return m_functionCollector.createFunction(functionName, [&] {
3038
1.43k
    solAssert(!dynamic_cast<StringLiteralType const*>(&_type), "");
3039
1.43k
    if (auto ref = dynamic_cast<ReferenceType const*>(&_type))
3040
2
    {
3041
2
      solAssert(
3042
2
        ref->location() == DataLocation::Memory,
3043
2
        "Can only update types with location memory."
3044
2
      );
3045
3046
2
      return Whiskers(R"(
3047
2
        function <functionName>(memPtr, value) {
3048
2
          mstore(memPtr, value)
3049
2
        }
3050
2
      )")
3051
2
      ("functionName", functionName)
3052
2
      .render();
3053
2
    }
3054
1.43k
    else if (
3055
1.43k
      _type.category() == Type::Category::Function &&
3056
1.43k
      dynamic_cast<FunctionType const&>(_type).kind() == FunctionType::Kind::External
3057
1.43k
    )
3058
0
    {
3059
0
      return Whiskers(R"(
3060
0
        function <functionName>(memPtr, addr, selector) {
3061
0
          mstore(memPtr, <combine>(addr, selector))
3062
0
        }
3063
0
      )")
3064
0
      ("functionName", functionName)
3065
0
      ("combine", combineExternalFunctionIdFunction())
3066
0
      .render();
3067
0
    }
3068
1.43k
    else if (_type.isValueType())
3069
1.43k
    {
3070
1.43k
      return Whiskers(R"(
3071
1.43k
        function <functionName>(memPtr, value) {
3072
1.43k
          mstore(memPtr, <cleanup>(value))
3073
1.43k
        }
3074
1.43k
      )")
3075
1.43k
      ("functionName", functionName)
3076
1.43k
      ("cleanup", cleanupFunction(_type))
3077
1.43k
      .render();
3078
1.43k
    }
3079
0
    else // Should never happen
3080
0
    {
3081
0
      solAssert(
3082
0
        false,
3083
0
        "Memory store of type " + _type.toString(true) + " not allowed."
3084
0
      );
3085
0
    }
3086
1.43k
  });
3087
1.92k
}
3088
3089
std::string YulUtilFunctions::extractFromStorageValueDynamic(Type const& _type)
3090
876
{
3091
876
  std::string functionName =
3092
876
    "extract_from_storage_value_dynamic" +
3093
876
    _type.identifier();
3094
876
  return m_functionCollector.createFunction(functionName, [&] {
3095
858
    return Whiskers(R"(
3096
858
      function <functionName>(slot_value, offset) -> value {
3097
858
        value := <cleanupStorage>(<shr>(mul(offset, 8), slot_value))
3098
858
      }
3099
858
    )")
3100
858
    ("functionName", functionName)
3101
858
    ("shr", shiftRightFunctionDynamic())
3102
858
    ("cleanupStorage", cleanupFromStorageFunction(_type))
3103
858
    .render();
3104
858
  });
3105
876
}
3106
3107
std::string YulUtilFunctions::extractFromStorageValue(Type const& _type, size_t _offset)
3108
9.16k
{
3109
9.16k
  std::string functionName = "extract_from_storage_value_offset_" + std::to_string(_offset) + "_" + _type.identifier();
3110
9.16k
  return m_functionCollector.createFunction(functionName, [&] {
3111
4.29k
    return Whiskers(R"(
3112
4.29k
      function <functionName>(slot_value) -> value {
3113
4.29k
        value := <cleanupStorage>(<shr>(slot_value))
3114
4.29k
      }
3115
4.29k
    )")
3116
4.29k
    ("functionName", functionName)
3117
4.29k
    ("shr", shiftRightFunction(_offset * 8))
3118
4.29k
    ("cleanupStorage", cleanupFromStorageFunction(_type))
3119
4.29k
    .render();
3120
4.29k
  });
3121
9.16k
}
3122
3123
std::string YulUtilFunctions::cleanupFromStorageFunction(Type const& _type)
3124
5.15k
{
3125
5.15k
  solAssert(_type.isValueType(), "");
3126
3127
5.15k
  std::string functionName = std::string("cleanup_from_storage_") + _type.identifier();
3128
5.15k
  return m_functionCollector.createFunction(functionName, [&] {
3129
2.14k
    Whiskers templ(R"(
3130
2.14k
      function <functionName>(value) -> cleaned {
3131
2.14k
        cleaned := <cleaned>
3132
2.14k
      }
3133
2.14k
    )");
3134
2.14k
    templ("functionName", functionName);
3135
3136
2.14k
    Type const* encodingType = &_type;
3137
2.14k
    if (_type.category() == Type::Category::UserDefinedValueType)
3138
1
      encodingType = _type.encodingType();
3139
2.14k
    unsigned storageBytes = encodingType->storageBytes();
3140
2.14k
    if (IntegerType const* intType = dynamic_cast<IntegerType const*>(encodingType))
3141
1.46k
      if (intType->isSigned() && storageBytes != 32)
3142
269
      {
3143
269
        templ("cleaned", "signextend(" + std::to_string(storageBytes - 1) + ", value)");
3144
269
        return templ.render();
3145
269
      }
3146
3147
1.87k
    if (storageBytes == 32)
3148
696
      templ("cleaned", "value");
3149
1.17k
    else if (encodingType->leftAligned())
3150
277
      templ("cleaned", shiftLeftFunction(256 - 8 * storageBytes) + "(value)");
3151
900
    else
3152
900
      templ("cleaned", "and(value, " + toCompactHexWithPrefix((u256(1) << (8 * storageBytes)) - 1) + ")");
3153
3154
1.87k
    return templ.render();
3155
2.14k
  });
3156
5.15k
}
3157
3158
std::string YulUtilFunctions::prepareStoreFunction(Type const& _type)
3159
2.62k
{
3160
2.62k
  std::string functionName = "prepare_store_" + _type.identifier();
3161
2.62k
  return m_functionCollector.createFunction(functionName, [&]() {
3162
2.43k
    solAssert(_type.isValueType(), "");
3163
2.43k
    auto const* funType = dynamic_cast<FunctionType const*>(&_type);
3164
2.43k
    if (funType && funType->kind() == FunctionType::Kind::External)
3165
0
    {
3166
0
      Whiskers templ(R"(
3167
0
        function <functionName>(addr, selector) -> ret {
3168
0
          ret := <prepareBytes>(<combine>(addr, selector))
3169
0
        }
3170
0
      )");
3171
0
      templ("functionName", functionName);
3172
0
      templ("prepareBytes", prepareStoreFunction(*TypeProvider::fixedBytes(24)));
3173
0
      templ("combine", combineExternalFunctionIdFunction());
3174
0
      return templ.render();
3175
0
    }
3176
2.43k
    else
3177
2.43k
    {
3178
2.43k
      solAssert(_type.sizeOnStack() == 1, "");
3179
2.43k
      Whiskers templ(R"(
3180
2.43k
        function <functionName>(value) -> ret {
3181
2.43k
          ret := <actualPrepare>
3182
2.43k
        }
3183
2.43k
      )");
3184
2.43k
      templ("functionName", functionName);
3185
2.43k
      if (_type.leftAligned())
3186
63
        templ("actualPrepare", shiftRightFunction(256 - 8 * _type.storageBytes()) + "(value)");
3187
2.37k
      else
3188
2.37k
        templ("actualPrepare", "value");
3189
2.43k
      return templ.render();
3190
2.43k
    }
3191
2.43k
  });
3192
2.62k
}
3193
3194
std::string YulUtilFunctions::allocationFunction()
3195
13.2k
{
3196
13.2k
  std::string functionName = "allocate_memory";
3197
13.2k
  return m_functionCollector.createFunction(functionName, [&]() {
3198
2.94k
    return Whiskers(R"(
3199
2.94k
      function <functionName>(size) -> memPtr {
3200
2.94k
        memPtr := <allocateUnbounded>()
3201
2.94k
        <finalizeAllocation>(memPtr, size)
3202
2.94k
      }
3203
2.94k
    )")
3204
2.94k
    ("functionName", functionName)
3205
2.94k
    ("allocateUnbounded", allocateUnboundedFunction())
3206
2.94k
    ("finalizeAllocation", finalizeAllocationFunction())
3207
2.94k
    .render();
3208
2.94k
  });
3209
13.2k
}
3210
3211
std::string YulUtilFunctions::allocateUnboundedFunction()
3212
48.1k
{
3213
48.1k
  std::string functionName = "allocate_unbounded";
3214
48.1k
  return m_functionCollector.createFunction(functionName, [&]() {
3215
13.2k
    return Whiskers(R"(
3216
13.2k
      function <functionName>() -> memPtr {
3217
13.2k
        memPtr := mload(<freeMemoryPointer>)
3218
13.2k
      }
3219
13.2k
    )")
3220
13.2k
    ("freeMemoryPointer", std::to_string(CompilerUtils::freeMemoryPointer))
3221
13.2k
    ("functionName", functionName)
3222
13.2k
    .render();
3223
13.2k
  });
3224
48.1k
}
3225
3226
std::string YulUtilFunctions::finalizeAllocationFunction()
3227
3.38k
{
3228
3.38k
  std::string functionName = "finalize_allocation";
3229
3.38k
  return m_functionCollector.createFunction(functionName, [&]() {
3230
3.23k
    return Whiskers(R"(
3231
3.23k
      function <functionName>(memPtr, size) {
3232
3.23k
        let newFreePtr := add(memPtr, <roundUp>(size))
3233
3.23k
        // protect against overflow
3234
3.23k
        if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { <panic>() }
3235
3.23k
        mstore(<freeMemoryPointer>, newFreePtr)
3236
3.23k
      }
3237
3.23k
    )")
3238
3.23k
    ("functionName", functionName)
3239
3.23k
    ("freeMemoryPointer", std::to_string(CompilerUtils::freeMemoryPointer))
3240
3.23k
    ("roundUp", roundUpFunction())
3241
3.23k
    ("panic", panicFunction(PanicCode::ResourceError))
3242
3.23k
    .render();
3243
3.23k
  });
3244
3.38k
}
3245
3246
std::string YulUtilFunctions::zeroMemoryArrayFunction(ArrayType const& _type)
3247
857
{
3248
857
  if (_type.baseType()->hasSimpleZeroValueInMemory())
3249
296
    return zeroMemoryFunction(*_type.baseType());
3250
561
  return zeroComplexMemoryArrayFunction(_type);
3251
857
}
3252
3253
std::string YulUtilFunctions::zeroMemoryFunction(Type const& _type)
3254
296
{
3255
296
  solAssert(_type.hasSimpleZeroValueInMemory(), "");
3256
3257
296
  std::string functionName = "zero_memory_chunk_" + _type.identifier();
3258
296
  return m_functionCollector.createFunction(functionName, [&]() {
3259
296
    return Whiskers(R"(
3260
296
      function <functionName>(dataStart, dataSizeInBytes) {
3261
296
        calldatacopy(dataStart, calldatasize(), dataSizeInBytes)
3262
296
      }
3263
296
    )")
3264
296
    ("functionName", functionName)
3265
296
    .render();
3266
296
  });
3267
296
}
3268
3269
std::string YulUtilFunctions::zeroComplexMemoryArrayFunction(ArrayType const& _type)
3270
561
{
3271
561
  solAssert(!_type.baseType()->hasSimpleZeroValueInMemory(), "");
3272
3273
561
  std::string functionName = "zero_complex_memory_array_" + _type.identifier();
3274
561
  return m_functionCollector.createFunction(functionName, [&]() {
3275
561
    solAssert(_type.memoryStride() == 32, "");
3276
561
    return Whiskers(R"(
3277
561
      function <functionName>(dataStart, dataSizeInBytes) {
3278
561
        for {let i := 0} lt(i, dataSizeInBytes) { i := add(i, <stride>) } {
3279
561
          mstore(add(dataStart, i), <zeroValue>())
3280
561
        }
3281
561
      }
3282
561
    )")
3283
561
    ("functionName", functionName)
3284
561
    ("stride", std::to_string(_type.memoryStride()))
3285
561
    ("zeroValue", zeroValueFunction(*_type.baseType(), false))
3286
561
    .render();
3287
561
  });
3288
561
}
3289
3290
std::string YulUtilFunctions::allocateMemoryArrayFunction(ArrayType const& _type)
3291
2.25k
{
3292
2.25k
  std::string functionName = "allocate_memory_array_" + _type.identifier();
3293
2.25k
  return m_functionCollector.createFunction(functionName, [&]() {
3294
2.05k
    return Whiskers(R"(
3295
2.05k
        function <functionName>(length) -> memPtr {
3296
2.05k
          let allocSize := <allocSize>(length)
3297
2.05k
          memPtr := <alloc>(allocSize)
3298
2.05k
          <?dynamic>
3299
2.05k
          mstore(memPtr, length)
3300
2.05k
          </dynamic>
3301
2.05k
        }
3302
2.05k
      )")
3303
2.05k
      ("functionName", functionName)
3304
2.05k
      ("alloc", allocationFunction())
3305
2.05k
      ("allocSize", arrayAllocationSizeFunction(_type))
3306
2.05k
      ("dynamic", _type.isDynamicallySized())
3307
2.05k
      .render();
3308
2.05k
  });
3309
2.25k
}
3310
3311
std::string YulUtilFunctions::allocateAndInitializeMemoryArrayFunction(ArrayType const& _type)
3312
1.08k
{
3313
1.08k
  std::string functionName = "allocate_and_zero_memory_array_" + _type.identifier();
3314
1.08k
  return m_functionCollector.createFunction(functionName, [&]() {
3315
857
    return Whiskers(R"(
3316
857
        function <functionName>(length) -> memPtr {
3317
857
          memPtr := <allocArray>(length)
3318
857
          let dataStart := memPtr
3319
857
          let dataSize := <allocSize>(length)
3320
857
          <?dynamic>
3321
857
          dataStart := add(dataStart, 32)
3322
857
          dataSize := sub(dataSize, 32)
3323
857
          </dynamic>
3324
857
          <zeroArrayFunction>(dataStart, dataSize)
3325
857
        }
3326
857
      )")
3327
857
      ("functionName", functionName)
3328
857
      ("allocArray", allocateMemoryArrayFunction(_type))
3329
857
      ("allocSize", arrayAllocationSizeFunction(_type))
3330
857
      ("zeroArrayFunction", zeroMemoryArrayFunction(_type))
3331
857
      ("dynamic", _type.isDynamicallySized())
3332
857
      .render();
3333
857
  });
3334
1.08k
}
3335
3336
std::string YulUtilFunctions::allocateMemoryStructFunction(StructType const& _type)
3337
18
{
3338
18
  std::string functionName = "allocate_memory_struct_" + _type.identifier();
3339
18
  return m_functionCollector.createFunction(functionName, [&]() {
3340
18
    Whiskers templ(R"(
3341
18
    function <functionName>() -> memPtr {
3342
18
      memPtr := <alloc>(<allocSize>)
3343
18
    }
3344
18
    )");
3345
18
    templ("functionName", functionName);
3346
18
    templ("alloc", allocationFunction());
3347
18
    templ("allocSize", _type.memoryDataSize().str());
3348
3349
18
    return templ.render();
3350
18
  });
3351
18
}
3352
3353
std::string YulUtilFunctions::allocateAndInitializeMemoryStructFunction(StructType const& _type)
3354
18
{
3355
18
  std::string functionName = "allocate_and_zero_memory_struct_" + _type.identifier();
3356
18
  return m_functionCollector.createFunction(functionName, [&]() {
3357
18
    Whiskers templ(R"(
3358
18
    function <functionName>() -> memPtr {
3359
18
      memPtr := <allocStruct>()
3360
18
      let offset := memPtr
3361
18
      <#member>
3362
18
        mstore(offset, <zeroValue>())
3363
18
        offset := add(offset, 32)
3364
18
      </member>
3365
18
    }
3366
18
    )");
3367
18
    templ("functionName", functionName);
3368
18
    templ("allocStruct", allocateMemoryStructFunction(_type));
3369
3370
18
    TypePointers const& members = _type.memoryMemberTypes();
3371
3372
18
    std::vector<std::map<std::string, std::string>> memberParams(members.size());
3373
60
    for (size_t i = 0; i < members.size(); ++i)
3374
42
    {
3375
42
      solAssert(members[i]->memoryHeadSize() == 32, "");
3376
42
      memberParams[i]["zeroValue"] = zeroValueFunction(
3377
42
        *TypeProvider::withLocationIfReference(DataLocation::Memory, members[i]),
3378
42
        false
3379
42
      );
3380
42
    }
3381
18
    templ("member", memberParams);
3382
18
    return templ.render();
3383
18
  });
3384
18
}
3385
3386
std::string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to)
3387
27.4k
{
3388
27.4k
  if (_from.category() == Type::Category::UserDefinedValueType)
3389
58
  {
3390
58
    solAssert(_from == _to || _to == dynamic_cast<UserDefinedValueType const&>(_from).underlyingType(), "");
3391
58
    return conversionFunction(dynamic_cast<UserDefinedValueType const&>(_from).underlyingType(), _to);
3392
58
  }
3393
27.3k
  if (_to.category() == Type::Category::UserDefinedValueType)
3394
12
  {
3395
12
    solAssert(_from == _to || _from.isImplicitlyConvertibleTo(dynamic_cast<UserDefinedValueType const&>(_to).underlyingType()), "");
3396
12
    return conversionFunction(_from, dynamic_cast<UserDefinedValueType const&>(_to).underlyingType());
3397
12
  }
3398
27.3k
  if (_from.category() == Type::Category::Function)
3399
103
  {
3400
103
    solAssert(_to.category() == Type::Category::Function, "");
3401
103
    FunctionType const& fromType = dynamic_cast<FunctionType const&>(_from);
3402
103
    FunctionType const& targetType = dynamic_cast<FunctionType const&>(_to);
3403
103
    solAssert(
3404
103
      fromType.isImplicitlyConvertibleTo(targetType) &&
3405
103
      fromType.sizeOnStack() == targetType.sizeOnStack() &&
3406
103
      (fromType.kind() == FunctionType::Kind::Internal || fromType.kind() == FunctionType::Kind::External) &&
3407
103
      fromType.kind() == targetType.kind(),
3408
103
      "Invalid function type conversion requested."
3409
103
    );
3410
103
    std::string const functionName =
3411
103
      "convert_" +
3412
103
      _from.identifier() +
3413
103
      "_to_" +
3414
103
      _to.identifier();
3415
103
    return m_functionCollector.createFunction(functionName, [&]() {
3416
103
      return Whiskers(R"(
3417
103
        function <functionName>(<?external>addr, </external>functionId) -> <?external>outAddr, </external>outFunctionId {
3418
103
          <?external>outAddr := addr</external>
3419
103
          outFunctionId := functionId
3420
103
        }
3421
103
      )")
3422
103
      ("functionName", functionName)
3423
103
      ("external", fromType.kind() == FunctionType::Kind::External)
3424
103
      .render();
3425
103
    });
3426
103
  }
3427
27.2k
  else if (_from.category() == Type::Category::ArraySlice)
3428
3
  {
3429
3
    auto const& fromType = dynamic_cast<ArraySliceType const&>(_from);
3430
3
    if (_to.category() == Type::Category::FixedBytes)
3431
0
    {
3432
0
      solAssert(fromType.arrayType().isByteArray(), "Array types other than bytes not convertible to bytesNN.");
3433
0
      return bytesToFixedBytesConversionFunction(fromType.arrayType(), dynamic_cast<FixedBytesType const &>(_to));
3434
0
    }
3435
3
    solAssert(_to.category() == Type::Category::Array);
3436
3
    auto const& targetType = dynamic_cast<ArrayType const&>(_to);
3437
3438
3
    solAssert(
3439
3
      fromType.arrayType().isImplicitlyConvertibleTo(targetType) ||
3440
3
      (fromType.arrayType().isByteArrayOrString() && targetType.isByteArrayOrString())
3441
3
    );
3442
3
    solAssert(
3443
3
      fromType.arrayType().dataStoredIn(DataLocation::CallData) &&
3444
3
      fromType.arrayType().isDynamicallySized() &&
3445
3
      !fromType.arrayType().baseType()->isDynamicallyEncoded()
3446
3
    );
3447
3448
3
    if (!targetType.dataStoredIn(DataLocation::CallData))
3449
3
      return arrayConversionFunction(fromType.arrayType(), targetType);
3450
3451
0
    std::string const functionName =
3452
0
      "convert_" +
3453
0
      _from.identifier() +
3454
0
      "_to_" +
3455
0
      _to.identifier();
3456
0
    return m_functionCollector.createFunction(functionName, [&]() {
3457
0
      return Whiskers(R"(
3458
0
        function <functionName>(offset, length) -> outOffset, outLength {
3459
0
          outOffset := offset
3460
0
          outLength := length
3461
0
        }
3462
0
      )")
3463
0
      ("functionName", functionName)
3464
0
      .render();
3465
0
    });
3466
3
  }
3467
27.2k
  else if (_from.category() == Type::Category::Array)
3468
490
  {
3469
490
    auto const& fromArrayType =  dynamic_cast<ArrayType const&>(_from);
3470
490
    if (_to.category() == Type::Category::FixedBytes)
3471
0
    {
3472
0
      solAssert(fromArrayType.isByteArray(), "Array types other than bytes not convertible to bytesNN.");
3473
0
      return bytesToFixedBytesConversionFunction(fromArrayType, dynamic_cast<FixedBytesType const &>(_to));
3474
0
    }
3475
490
    solAssert(_to.category() == Type::Category::Array, "");
3476
490
    return arrayConversionFunction(fromArrayType, dynamic_cast<ArrayType const&>(_to));
3477
490
  }
3478
3479
26.7k
  if (_from.sizeOnStack() != 1 || _to.sizeOnStack() != 1)
3480
1.06k
    return conversionFunctionSpecial(_from, _to);
3481
3482
25.6k
  std::string functionName =
3483
25.6k
    "convert_" +
3484
25.6k
    _from.identifier() +
3485
25.6k
    "_to_" +
3486
25.6k
    _to.identifier();
3487
25.6k
  return m_functionCollector.createFunction(functionName, [&]() {
3488
17.4k
    Whiskers templ(R"(
3489
17.4k
      function <functionName>(value) -> converted {
3490
17.4k
        <body>
3491
17.4k
      }
3492
17.4k
    )");
3493
17.4k
    templ("functionName", functionName);
3494
17.4k
    std::string body;
3495
17.4k
    auto toCategory = _to.category();
3496
17.4k
    auto fromCategory = _from.category();
3497
17.4k
    switch (fromCategory)
3498
17.4k
    {
3499
51
    case Type::Category::Address:
3500
369
    case Type::Category::Contract:
3501
369
      body =
3502
369
        Whiskers("converted := <convert>(value)")
3503
369
          ("convert", conversionFunction(IntegerType(160), _to))
3504
369
          .render();
3505
369
      break;
3506
3.47k
    case Type::Category::Integer:
3507
16.9k
    case Type::Category::RationalNumber:
3508
16.9k
    {
3509
16.9k
      solAssert(_from.mobileType(), "");
3510
16.9k
      if (RationalNumberType const* rational = dynamic_cast<RationalNumberType const*>(&_from))
3511
13.4k
        if (rational->isFractional())
3512
13.4k
          solAssert(toCategory == Type::Category::FixedPoint, "");
3513
3514
16.9k
      if (toCategory == Type::Category::Address || toCategory == Type::Category::Contract)
3515
342
        body =
3516
342
          Whiskers("converted := <convert>(value)")
3517
342
          ("convert", conversionFunction(_from, IntegerType(160)))
3518
342
          .render();
3519
16.6k
      else
3520
16.6k
      {
3521
16.6k
        Whiskers bodyTemplate("converted := <cleanOutput>(<convert>(<cleanInput>(value)))");
3522
16.6k
        bodyTemplate("cleanInput", cleanupFunction(_from));
3523
16.6k
        bodyTemplate("cleanOutput", cleanupFunction(_to));
3524
16.6k
        std::string convert;
3525
3526
16.6k
        solAssert(_to.category() != Type::Category::UserDefinedValueType, "");
3527
16.6k
        if (auto const* toFixedBytes = dynamic_cast<FixedBytesType const*>(&_to))
3528
85
          convert = shiftLeftFunction(256 - toFixedBytes->numBytes() * 8);
3529
16.5k
        else if (dynamic_cast<FixedPointType const*>(&_to))
3530
16.5k
          solUnimplemented("");
3531
16.5k
        else if (dynamic_cast<IntegerType const*>(&_to))
3532
16.5k
        {
3533
16.5k
          solUnimplementedAssert(fromCategory != Type::Category::FixedPoint);
3534
16.5k
          convert = identityFunction();
3535
16.5k
        }
3536
3
        else if (toCategory == Type::Category::Enum)
3537
0
        {
3538
0
          solAssert(fromCategory != Type::Category::FixedPoint, "");
3539
0
          convert = identityFunction();
3540
0
        }
3541
3
        else
3542
3
          solAssert(false, "");
3543
16.6k
        solAssert(!convert.empty(), "");
3544
16.6k
        bodyTemplate("convert", convert);
3545
16.6k
        body = bodyTemplate.render();
3546
16.6k
      }
3547
16.9k
      break;
3548
16.9k
    }
3549
16.9k
    case Type::Category::Bool:
3550
16
    {
3551
16
      solAssert(_from == _to, "Invalid conversion for bool.");
3552
16
      body =
3553
16
        Whiskers("converted := <clean>(value)")
3554
16
        ("clean", cleanupFunction(_from))
3555
16
        .render();
3556
16
      break;
3557
16
    }
3558
0
    case Type::Category::FixedPoint:
3559
0
      solUnimplemented("Fixed point types not implemented.");
3560
0
      break;
3561
12
    case Type::Category::Struct:
3562
12
    {
3563
12
      solAssert(toCategory == Type::Category::Struct, "");
3564
12
      auto const& fromStructType = dynamic_cast<StructType const &>(_from);
3565
12
      auto const& toStructType = dynamic_cast<StructType const &>(_to);
3566
12
      solAssert(fromStructType.structDefinition() == toStructType.structDefinition(), "");
3567
3568
12
      if (fromStructType.location() == toStructType.location() && toStructType.isPointer())
3569
0
        body = "converted := value";
3570
12
      else
3571
12
      {
3572
12
        solUnimplementedAssert(toStructType.location() == DataLocation::Memory);
3573
12
        solUnimplementedAssert(fromStructType.location() != DataLocation::Memory);
3574
3575
12
        if (fromStructType.location() == DataLocation::CallData)
3576
12
          body = Whiskers(R"(
3577
12
            converted := <abiDecode>(value, calldatasize())
3578
12
          )")
3579
12
          (
3580
12
            "abiDecode",
3581
12
            ABIFunctions(m_evmVersion, m_eofVersion, m_revertStrings, m_functionCollector).abiDecodingFunctionStruct(
3582
12
              toStructType,
3583
12
              false
3584
12
            )
3585
12
          ).render();
3586
0
        else
3587
0
        {
3588
0
          solAssert(fromStructType.location() == DataLocation::Storage, "");
3589
3590
0
          body = Whiskers(R"(
3591
0
            converted := <readFromStorage>(value)
3592
0
          )")
3593
0
          ("readFromStorage", readFromStorage(toStructType, 0, true, VariableDeclaration::Location::Unspecified))
3594
0
          .render();
3595
0
        }
3596
12
      }
3597
3598
12
      break;
3599
12
    }
3600
80
    case Type::Category::FixedBytes:
3601
80
    {
3602
80
      FixedBytesType const& from = dynamic_cast<FixedBytesType const&>(_from);
3603
80
      if (toCategory == Type::Category::Integer)
3604
15
        body =
3605
15
          Whiskers("converted := <convert>(<shift>(value))")
3606
15
          ("shift", shiftRightFunction(256 - from.numBytes() * 8))
3607
15
          ("convert", conversionFunction(IntegerType(from.numBytes() * 8), _to))
3608
15
          .render();
3609
65
      else if (toCategory == Type::Category::Address)
3610
2
        body =
3611
2
          Whiskers("converted := <convert>(value)")
3612
2
            ("convert", conversionFunction(_from, IntegerType(160)))
3613
2
            .render();
3614
63
      else
3615
63
      {
3616
63
        solAssert(toCategory == Type::Category::FixedBytes, "Invalid type conversion requested.");
3617
63
        FixedBytesType const& to = dynamic_cast<FixedBytesType const&>(_to);
3618
63
        body =
3619
63
          Whiskers("converted := <clean>(value)")
3620
63
          ("clean", cleanupFunction((to.numBytes() <= from.numBytes()) ? to : from))
3621
63
          .render();
3622
63
      }
3623
80
      break;
3624
80
    }
3625
80
    case Type::Category::Function:
3626
0
    {
3627
0
      solAssert(false, "Conversion should not be called for function types.");
3628
0
      break;
3629
0
    }
3630
25
    case Type::Category::Enum:
3631
25
    {
3632
25
      solAssert(toCategory == Type::Category::Integer || _from == _to, "");
3633
25
      EnumType const& enumType = dynamic_cast<decltype(enumType)>(_from);
3634
25
      body =
3635
25
        Whiskers("converted := <clean>(value)")
3636
25
        ("clean", cleanupFunction(enumType))
3637
25
        .render();
3638
25
      break;
3639
25
    }
3640
0
    case Type::Category::Tuple:
3641
0
    {
3642
0
      solUnimplemented("Tuple conversion not implemented.");
3643
0
      break;
3644
0
    }
3645
5
    case Type::Category::TypeType:
3646
5
    {
3647
5
      TypeType const& typeType = dynamic_cast<decltype(typeType)>(_from);
3648
5
      if (
3649
5
        auto const* contractType = dynamic_cast<ContractType const*>(typeType.actualType());
3650
5
        contractType->contractDefinition().isLibrary() &&
3651
5
        _to == *TypeProvider::address()
3652
5
      )
3653
5
        body = "converted := value";
3654
0
      else
3655
5
        solAssert(false, "Invalid conversion from " + _from.canonicalName() + " to " + _to.canonicalName());
3656
5
      break;
3657
5
    }
3658
5
    case Type::Category::Mapping:
3659
0
    {
3660
0
      solAssert(_from == _to, "");
3661
0
      body = "converted := value";
3662
0
      break;
3663
0
    }
3664
0
    default:
3665
0
      solAssert(false, "Invalid conversion from " + _from.canonicalName() + " to " + _to.canonicalName());
3666
17.4k
    }
3667
3668
17.4k
    solAssert(!body.empty(), _from.canonicalName() + " to " + _to.canonicalName());
3669
17.4k
    templ("body", body);
3670
17.4k
    return templ.render();
3671
17.4k
  });
3672
26.7k
}
3673
3674
std::string YulUtilFunctions::bytesToFixedBytesConversionFunction(ArrayType const& _from, FixedBytesType const& _to)
3675
19
{
3676
19
  solAssert(_from.isByteArray(), "");
3677
19
  solAssert(_from.isDynamicallySized(), "");
3678
19
  std::string functionName = "convert_bytes_to_fixedbytes_from_" + _from.identifier() + "_to_" + _to.identifier();
3679
19
  return m_functionCollector.createFunction(functionName, [&](auto& _args, auto& _returnParams) {
3680
19
    _args = { "array" };
3681
19
    bool fromCalldata = _from.dataStoredIn(DataLocation::CallData);
3682
19
    if (fromCalldata)
3683
9
      _args.emplace_back("len");
3684
19
    _returnParams = {"value"};
3685
19
    Whiskers templ(R"(
3686
19
      let length := <arrayLen>(array<?fromCalldata>, len</fromCalldata>)
3687
19
      let dataArea := array
3688
19
      <?fromMemory>
3689
19
        dataArea := <dataArea>(array)
3690
19
      </fromMemory>
3691
19
      <?fromStorage>
3692
19
        if gt(length, 31) { dataArea := <dataArea>(array) }
3693
19
      </fromStorage>
3694
19
3695
19
      <?fromCalldata>
3696
19
        value := <cleanup>(calldataload(dataArea))
3697
19
      <!fromCalldata>
3698
19
        value := <extractValue>(dataArea)
3699
19
      </fromCalldata>
3700
19
3701
19
      if lt(length, <fixedBytesLen>) {
3702
19
        value := and(
3703
19
          value,
3704
19
          <shl>(
3705
19
            mul(8, sub(<fixedBytesLen>, length)),
3706
19
            <mask>
3707
19
          )
3708
19
        )
3709
19
      }
3710
19
    )");
3711
19
    templ("fromCalldata", fromCalldata);
3712
19
    templ("arrayLen", arrayLengthFunction(_from));
3713
19
    templ("fixedBytesLen", std::to_string(_to.numBytes()));
3714
19
    templ("fromMemory", _from.dataStoredIn(DataLocation::Memory));
3715
19
    templ("fromStorage", _from.dataStoredIn(DataLocation::Storage));
3716
19
    templ("dataArea", arrayDataAreaFunction(_from));
3717
19
    if (fromCalldata)
3718
9
      templ("cleanup", cleanupFunction(_to));
3719
10
    else
3720
10
      templ(
3721
10
        "extractValue",
3722
10
        _from.dataStoredIn(DataLocation::Storage) ?
3723
6
        readFromStorage(_to, 32 - _to.numBytes(), false, VariableDeclaration::Location::Unspecified) :
3724
10
        readFromMemory(_to)
3725
10
      );
3726
19
    templ("shl", shiftLeftFunctionDynamic());
3727
19
    templ("mask", formatNumber(~((u256(1) << (256 - _to.numBytes() * 8)) - 1)));
3728
19
    return templ.render();
3729
19
  });
3730
19
}
3731
3732
std::string YulUtilFunctions::copyStructToStorageFunction(StructType const& _from, StructType const& _to)
3733
35
{
3734
35
  solAssert(_to.dataStoredIn(DataLocation::Storage), "");
3735
35
  solAssert(_from.structDefinition() == _to.structDefinition(), "");
3736
3737
35
  std::string functionName =
3738
35
    "copy_struct_to_storage_from_" +
3739
35
    _from.identifier() +
3740
35
    "_to_" +
3741
35
    _to.identifier();
3742
3743
35
  return m_functionCollector.createFunction(functionName, [&](auto& _arguments, auto&) {
3744
35
    _arguments = {"slot", "value"};
3745
35
    Whiskers templ(R"(
3746
35
      <?fromStorage> if iszero(eq(slot, value)) { </fromStorage>
3747
35
      <#member>
3748
35
      {
3749
35
        <updateMemberCall>
3750
35
      }
3751
35
      </member>
3752
35
      <?fromStorage> } </fromStorage>
3753
35
    )");
3754
35
    templ("fromStorage", _from.dataStoredIn(DataLocation::Storage));
3755
3756
35
    MemberList::MemberMap structMembers = _from.nativeMembers(nullptr);
3757
35
    MemberList::MemberMap toStructMembers = _to.nativeMembers(nullptr);
3758
3759
35
    std::vector<std::map<std::string, std::string>> memberParams(structMembers.size());
3760
138
    for (size_t i = 0; i < structMembers.size(); ++i)
3761
103
    {
3762
103
      Type const& memberType = *structMembers[i].type;
3763
103
      solAssert(memberType.memoryHeadSize() == 32, "");
3764
103
      auto const&[slotDiff, offset] = _to.storageOffsetsOfMember(structMembers[i].name);
3765
3766
103
      Whiskers t(R"(
3767
103
        let memberSlot := add(slot, <memberStorageSlotDiff>)
3768
103
        let memberSrcPtr := add(value, <memberOffset>)
3769
103
3770
103
        <?fromCalldata>
3771
103
          let <memberValues> :=
3772
103
            <?dynamicallyEncodedMember>
3773
103
              <accessCalldataTail>(value, memberSrcPtr)
3774
103
            <!dynamicallyEncodedMember>
3775
103
              memberSrcPtr
3776
103
            </dynamicallyEncodedMember>
3777
103
3778
103
          <?isValueType>
3779
103
            <memberValues> := <read>(<memberValues>)
3780
103
          </isValueType>
3781
103
        </fromCalldata>
3782
103
3783
103
        <?fromMemory>
3784
103
          let <memberValues> := <read>(memberSrcPtr)
3785
103
        </fromMemory>
3786
103
3787
103
        <?fromStorage>
3788
103
          let <memberValues> :=
3789
103
            <?isValueType>
3790
103
              <read>(memberSrcPtr)
3791
103
            <!isValueType>
3792
103
              memberSrcPtr
3793
103
            </isValueType>
3794
103
        </fromStorage>
3795
103
3796
103
        <updateStorageValue>(memberSlot, <memberValues>)
3797
103
      )");
3798
103
      bool fromCalldata = _from.location() == DataLocation::CallData;
3799
103
      t("fromCalldata", fromCalldata);
3800
103
      bool fromMemory = _from.location() == DataLocation::Memory;
3801
103
      t("fromMemory", fromMemory);
3802
103
      bool fromStorage = _from.location() == DataLocation::Storage;
3803
103
      t("fromStorage", fromStorage);
3804
103
      t("isValueType", memberType.isValueType());
3805
103
      t("memberValues", suffixedVariableNameList("memberValue_", 0, memberType.stackItems().size()));
3806
3807
103
      t("memberStorageSlotDiff", slotDiff.str());
3808
103
      if (fromCalldata)
3809
67
      {
3810
67
        t("memberOffset", std::to_string(_from.calldataOffsetOfMember(structMembers[i].name)));
3811
67
        t("dynamicallyEncodedMember", memberType.isDynamicallyEncoded());
3812
67
        if (memberType.isDynamicallyEncoded())
3813
7
          t("accessCalldataTail", accessCalldataTailFunction(memberType));
3814
67
        if (memberType.isValueType())
3815
55
          t("read", readFromCalldata(memberType));
3816
67
      }
3817
36
      else if (fromMemory)
3818
24
      {
3819
24
        t("memberOffset", _from.memoryOffsetOfMember(structMembers[i].name).str());
3820
24
        t("read", readFromMemory(memberType));
3821
24
      }
3822
12
      else if (fromStorage)
3823
12
      {
3824
12
        auto const& [srcSlotOffset, srcOffset] = _from.storageOffsetsOfMember(structMembers[i].name);
3825
12
        t("memberOffset", formatNumber(srcSlotOffset));
3826
12
        if (memberType.isValueType())
3827
12
          t("read", readFromStorageValueType(memberType, srcOffset, true, VariableDeclaration::Location::Unspecified));
3828
0
        else
3829
12
          solAssert(srcOffset == 0, "");
3830
3831
12
      }
3832
103
      t("updateStorageValue", updateStorageValueFunction(
3833
103
        memberType,
3834
103
        *toStructMembers[i].type,
3835
103
        VariableDeclaration::Location::Unspecified,
3836
103
        std::optional<unsigned>{offset}
3837
103
      ));
3838
103
      memberParams[i]["updateMemberCall"] = t.render();
3839
103
    }
3840
35
    templ("member", memberParams);
3841
3842
35
    return templ.render();
3843
35
  });
3844
35
}
3845
3846
std::string YulUtilFunctions::arrayConversionFunction(ArrayType const& _from, ArrayType const& _to)
3847
493
{
3848
493
  if (_to.dataStoredIn(DataLocation::CallData))
3849
493
    solAssert(
3850
493
      _from.dataStoredIn(DataLocation::CallData) && _from.isByteArrayOrString() && _to.isByteArrayOrString(),
3851
493
      ""
3852
493
    );
3853
3854
  // Other cases are done explicitly in LValue::storeValue, and only possible by assignment.
3855
493
  if (_to.location() == DataLocation::Storage)
3856
493
    solAssert(
3857
493
      (_to.isPointer() || (_from.isByteArrayOrString() && _to.isByteArrayOrString())) &&
3858
493
      _from.location() == DataLocation::Storage,
3859
493
      "Invalid conversion to storage type."
3860
493
    );
3861
3862
493
  std::string functionName =
3863
493
    "convert_array_" +
3864
493
    _from.identifier() +
3865
493
    "_to_" +
3866
493
    _to.identifier();
3867
3868
493
  return m_functionCollector.createFunction(functionName, [&]() {
3869
269
    Whiskers templ(R"(
3870
269
      function <functionName>(value<?fromCalldataDynamic>, length</fromCalldataDynamic>) -> converted <?toCalldataDynamic>, outLength</toCalldataDynamic> {
3871
269
        <body>
3872
269
        <?toCalldataDynamic>
3873
269
          outLength := <length>
3874
269
        </toCalldataDynamic>
3875
269
      }
3876
269
    )");
3877
269
    templ("functionName", functionName);
3878
269
    templ("fromCalldataDynamic", _from.dataStoredIn(DataLocation::CallData) && _from.isDynamicallySized());
3879
269
    templ("toCalldataDynamic", _to.dataStoredIn(DataLocation::CallData) && _to.isDynamicallySized());
3880
269
    templ("length", _from.isDynamicallySized() ? "length" : _from.length().str());
3881
3882
269
    if (
3883
269
      _from == _to ||
3884
269
      (_from.dataStoredIn(DataLocation::Memory) && _to.dataStoredIn(DataLocation::Memory)) ||
3885
269
      (_from.dataStoredIn(DataLocation::CallData) && _to.dataStoredIn(DataLocation::CallData)) ||
3886
269
      _to.dataStoredIn(DataLocation::Storage)
3887
269
    )
3888
153
      templ("body", "converted := value");
3889
116
    else if (_to.dataStoredIn(DataLocation::Memory))
3890
116
      templ(
3891
116
        "body",
3892
116
        Whiskers(R"(
3893
116
          // Copy the array to a free position in memory
3894
116
          converted :=
3895
116
          <?fromStorage>
3896
116
            <arrayStorageToMem>(value)
3897
116
          </fromStorage>
3898
116
          <?fromCalldata>
3899
116
            <abiDecode>(value, <length>, calldatasize())
3900
116
          </fromCalldata>
3901
116
        )")
3902
116
        ("fromStorage", _from.dataStoredIn(DataLocation::Storage))
3903
116
        ("fromCalldata", _from.dataStoredIn(DataLocation::CallData))
3904
116
        ("length", _from.isDynamicallySized() ? "length" : _from.length().str())
3905
116
        (
3906
116
          "abiDecode",
3907
116
          _from.dataStoredIn(DataLocation::CallData) ?
3908
32
          ABIFunctions(
3909
32
            m_evmVersion,
3910
32
            m_eofVersion,
3911
32
            m_revertStrings,
3912
32
            m_functionCollector
3913
32
          ).abiDecodingFunctionArrayAvailableLength(_to, false) :
3914
116
          ""
3915
116
        )
3916
116
        (
3917
116
          "arrayStorageToMem",
3918
116
          _from.dataStoredIn(DataLocation::Storage) ? copyArrayFromStorageToMemoryFunction(_from, _to) : ""
3919
116
        )
3920
116
        .render()
3921
116
      );
3922
0
    else
3923
116
      solAssert(false, "");
3924
3925
269
    return templ.render();
3926
269
  });
3927
493
}
3928
3929
std::string YulUtilFunctions::cleanupFunction(Type const& _type)
3930
68.8k
{
3931
68.8k
  if (auto userDefinedValueType = dynamic_cast<UserDefinedValueType const*>(&_type))
3932
40
    return cleanupFunction(userDefinedValueType->underlyingType());
3933
3934
68.7k
  std::string functionName = std::string("cleanup_") + _type.identifier();
3935
68.7k
  return m_functionCollector.createFunction(functionName, [&]() {
3936
31.8k
    Whiskers templ(R"(
3937
31.8k
      function <functionName>(value) -> cleaned {
3938
31.8k
        <body>
3939
31.8k
      }
3940
31.8k
    )");
3941
31.8k
    templ("functionName", functionName);
3942
31.8k
    switch (_type.category())
3943
31.8k
    {
3944
852
    case Type::Category::Address:
3945
852
      templ("body", "cleaned := " + cleanupFunction(IntegerType(160)) + "(value)");
3946
852
      break;
3947
15.1k
    case Type::Category::Integer:
3948
15.1k
    {
3949
15.1k
      IntegerType const& type = dynamic_cast<IntegerType const&>(_type);
3950
15.1k
      if (type.numBits() == 256)
3951
9.04k
        templ("body", "cleaned := value");
3952
6.11k
      else if (type.isSigned())
3953
1.39k
        templ("body", "cleaned := signextend(" + std::to_string(type.numBits() / 8 - 1) + ", value)");
3954
4.72k
      else
3955
4.72k
        templ("body", "cleaned := and(value, " + toCompactHexWithPrefix((u256(1) << type.numBits()) - 1) + ")");
3956
15.1k
      break;
3957
0
    }
3958
12.4k
    case Type::Category::RationalNumber:
3959
12.4k
      templ("body", "cleaned := value");
3960
12.4k
      break;
3961
1.07k
    case Type::Category::Bool:
3962
1.07k
      templ("body", "cleaned := iszero(iszero(value))");
3963
1.07k
      break;
3964
4
    case Type::Category::FixedPoint:
3965
4
      solUnimplemented("Fixed point types not implemented.");
3966
0
      break;
3967
89
    case Type::Category::Function:
3968
89
      switch (dynamic_cast<FunctionType const&>(_type).kind())
3969
89
      {
3970
89
        case FunctionType::Kind::External:
3971
89
          templ("body", "cleaned := " + cleanupFunction(FixedBytesType(24)) + "(value)");
3972
89
          break;
3973
0
        case FunctionType::Kind::Internal:
3974
0
          templ("body", "cleaned := value");
3975
0
          break;
3976
0
        default:
3977
0
          solAssert(false, "");
3978
0
          break;
3979
89
      }
3980
89
      break;
3981
89
    case Type::Category::Array:
3982
45
    case Type::Category::Struct:
3983
71
    case Type::Category::Mapping:
3984
71
      solAssert(_type.dataStoredIn(DataLocation::Storage), "Cleanup requested for non-storage reference type.");
3985
71
      templ("body", "cleaned := value");
3986
71
      break;
3987
1.92k
    case Type::Category::FixedBytes:
3988
1.92k
    {
3989
1.92k
      FixedBytesType const& type = dynamic_cast<FixedBytesType const&>(_type);
3990
1.92k
      if (type.numBytes() == 32)
3991
177
        templ("body", "cleaned := value");
3992
1.74k
      else if (type.numBytes() == 0)
3993
        // This is disallowed in the type system.
3994
1.74k
        solAssert(false, "");
3995
1.74k
      else
3996
1.74k
      {
3997
1.74k
        size_t numBits = type.numBytes() * 8;
3998
1.74k
        u256 mask = ((u256(1) << numBits) - 1) << (256 - numBits);
3999
1.74k
        templ("body", "cleaned := and(value, " + toCompactHexWithPrefix(mask) + ")");
4000
1.74k
      }
4001
1.92k
      break;
4002
1.92k
    }
4003
1.92k
    case Type::Category::Contract:
4004
244
    {
4005
244
      AddressType addressType(dynamic_cast<ContractType const&>(_type).isPayable() ?
4006
2
        StateMutability::Payable :
4007
244
        StateMutability::NonPayable
4008
244
      );
4009
244
      templ("body", "cleaned := " + cleanupFunction(addressType) + "(value)");
4010
244
      break;
4011
1.92k
    }
4012
25
    case Type::Category::Enum:
4013
25
    {
4014
      // Out of range enums cannot be truncated unambiguously and therefore it should be an error.
4015
25
      templ("body", "cleaned := value " + validatorFunction(_type, false) + "(value)");
4016
25
      break;
4017
1.92k
    }
4018
0
    case Type::Category::InaccessibleDynamic:
4019
0
      templ("body", "cleaned := 0");
4020
0
      break;
4021
0
    default:
4022
0
      solAssert(false, "Cleanup of type " + _type.identifier() + " requested.");
4023
31.8k
    }
4024
4025
31.8k
    return templ.render();
4026
31.8k
  });
4027
68.8k
}
4028
4029
std::string YulUtilFunctions::validatorFunction(Type const& _type, bool _revertOnFailure)
4030
9.03k
{
4031
9.03k
  std::string functionName = std::string("validator_") + (_revertOnFailure ? "revert_" : "assert_") + _type.identifier();
4032
9.03k
  return m_functionCollector.createFunction(functionName, [&]() {
4033
8.67k
    Whiskers templ(R"(
4034
8.67k
      function <functionName>(value) {
4035
8.67k
        if iszero(<condition>) { <failure> }
4036
8.67k
      }
4037
8.67k
    )");
4038
8.67k
    templ("functionName", functionName);
4039
8.67k
    PanicCode panicCode = PanicCode::Generic;
4040
4041
8.67k
    switch (_type.category())
4042
8.67k
    {
4043
483
    case Type::Category::Address:
4044
6.39k
    case Type::Category::Integer:
4045
6.39k
    case Type::Category::RationalNumber:
4046
7.14k
    case Type::Category::Bool:
4047
7.14k
    case Type::Category::FixedPoint:
4048
7.22k
    case Type::Category::Function:
4049
7.24k
    case Type::Category::Array:
4050
7.27k
    case Type::Category::Struct:
4051
7.29k
    case Type::Category::Mapping:
4052
8.31k
    case Type::Category::FixedBytes:
4053
8.56k
    case Type::Category::Contract:
4054
8.60k
    case Type::Category::UserDefinedValueType:
4055
8.60k
    {
4056
8.60k
      templ("condition", "eq(value, " + cleanupFunction(_type) + "(value))");
4057
8.60k
      break;
4058
8.56k
    }
4059
59
    case Type::Category::Enum:
4060
59
    {
4061
59
      size_t members = dynamic_cast<EnumType const&>(_type).numberOfMembers();
4062
59
      solAssert(members > 0, "empty enum should have caused a parser error.");
4063
59
      panicCode = PanicCode::EnumConversionError;
4064
59
      templ("condition", "lt(value, " + std::to_string(members) + ")");
4065
59
      break;
4066
59
    }
4067
10
    case Type::Category::InaccessibleDynamic:
4068
10
      templ("condition", "1");
4069
10
      break;
4070
0
    default:
4071
0
      solAssert(false, "Validation of type " + _type.identifier() + " requested.");
4072
8.67k
    }
4073
4074
8.67k
    if (_revertOnFailure)
4075
8.64k
      templ("failure", "revert(0, 0)");
4076
25
    else
4077
25
      templ("failure", panicFunction(panicCode) + "()");
4078
4079
8.67k
    return templ.render();
4080
8.67k
  });
4081
9.03k
}
4082
4083
std::string YulUtilFunctions::packedHashFunction(
4084
  std::vector<Type const*> const& _givenTypes,
4085
  std::vector<Type const*> const& _targetTypes
4086
)
4087
198
{
4088
198
  std::string functionName = std::string("packed_hashed_");
4089
198
  for (auto const& t: _givenTypes)
4090
396
    functionName += t->identifier() + "_";
4091
198
  functionName += "_to_";
4092
198
  for (auto const& t: _targetTypes)
4093
396
    functionName += t->identifier() + "_";
4094
198
  size_t sizeOnStack = 0;
4095
198
  for (Type const* t: _givenTypes)
4096
396
    sizeOnStack += t->sizeOnStack();
4097
198
  return m_functionCollector.createFunction(functionName, [&]() {
4098
198
    Whiskers templ(R"(
4099
198
      function <functionName>(<variables>) -> hash {
4100
198
        let pos := <allocateUnbounded>()
4101
198
        let end := <packedEncode>(pos <comma> <variables>)
4102
198
        hash := keccak256(pos, sub(end, pos))
4103
198
      }
4104
198
    )");
4105
198
    templ("functionName", functionName);
4106
198
    templ("variables", suffixedVariableNameList("var_", 1, 1 + sizeOnStack));
4107
198
    templ("comma", sizeOnStack > 0 ? "," : "");
4108
198
    templ("allocateUnbounded", allocateUnboundedFunction());
4109
198
    templ(
4110
198
      "packedEncode",
4111
198
      ABIFunctions(m_evmVersion, m_eofVersion, m_revertStrings, m_functionCollector).tupleEncoderPacked(_givenTypes, _targetTypes)
4112
198
    );
4113
198
    return templ.render();
4114
198
  });
4115
198
}
4116
4117
std::string YulUtilFunctions::forwardingRevertFunction()
4118
346
{
4119
346
  bool forward = m_evmVersion.supportsReturndata();
4120
346
  std::string functionName = "revert_forward_" + std::to_string(forward);
4121
346
  return m_functionCollector.createFunction(functionName, [&]() {
4122
215
    if (forward)
4123
136
      return Whiskers(R"(
4124
136
        function <functionName>() {
4125
136
          let pos := <allocateUnbounded>()
4126
136
          returndatacopy(pos, 0, returndatasize())
4127
136
          revert(pos, returndatasize())
4128
136
        }
4129
136
      )")
4130
136
      ("functionName", functionName)
4131
136
      ("allocateUnbounded", allocateUnboundedFunction())
4132
136
      .render();
4133
79
    else
4134
79
      return Whiskers(R"(
4135
79
        function <functionName>() {
4136
79
          revert(0, 0)
4137
79
        }
4138
79
      )")
4139
79
      ("functionName", functionName)
4140
79
      .render();
4141
215
  });
4142
346
}
4143
4144
std::string YulUtilFunctions::decrementCheckedFunction(Type const& _type)
4145
371
{
4146
371
  solAssert(_type.category() == Type::Category::Integer, "");
4147
371
  IntegerType const& type = dynamic_cast<IntegerType const&>(_type);
4148
4149
371
  std::string const functionName = "decrement_" + _type.identifier();
4150
4151
371
  return m_functionCollector.createFunction(functionName, [&]() {
4152
239
    return Whiskers(R"(
4153
239
      function <functionName>(value) -> ret {
4154
239
        value := <cleanupFunction>(value)
4155
239
        if eq(value, <minval>) { <panic>() }
4156
239
        ret := sub(value, 1)
4157
239
      }
4158
239
    )")
4159
239
    ("functionName", functionName)
4160
239
    ("panic", panicFunction(PanicCode::UnderOverflow))
4161
239
    ("minval", toCompactHexWithPrefix(type.min()))
4162
239
    ("cleanupFunction", cleanupFunction(_type))
4163
239
    .render();
4164
239
  });
4165
371
}
4166
4167
std::string YulUtilFunctions::decrementWrappingFunction(Type const& _type)
4168
0
{
4169
0
  solAssert(_type.category() == Type::Category::Integer, "");
4170
0
  IntegerType const& type = dynamic_cast<IntegerType const&>(_type);
4171
4172
0
  std::string const functionName = "decrement_wrapping_" + _type.identifier();
4173
4174
0
  return m_functionCollector.createFunction(functionName, [&]() {
4175
0
    return Whiskers(R"(
4176
0
      function <functionName>(value) -> ret {
4177
0
        ret := <cleanupFunction>(sub(value, 1))
4178
0
      }
4179
0
    )")
4180
0
    ("functionName", functionName)
4181
0
    ("cleanupFunction", cleanupFunction(type))
4182
0
    .render();
4183
0
  });
4184
0
}
4185
4186
std::string YulUtilFunctions::incrementCheckedFunction(Type const& _type)
4187
864
{
4188
864
  solAssert(_type.category() == Type::Category::Integer, "");
4189
864
  IntegerType const& type = dynamic_cast<IntegerType const&>(_type);
4190
4191
864
  std::string const functionName = "increment_" + _type.identifier();
4192
4193
864
  return m_functionCollector.createFunction(functionName, [&]() {
4194
486
    return Whiskers(R"(
4195
486
      function <functionName>(value) -> ret {
4196
486
        value := <cleanupFunction>(value)
4197
486
        if eq(value, <maxval>) { <panic>() }
4198
486
        ret := add(value, 1)
4199
486
      }
4200
486
    )")
4201
486
    ("functionName", functionName)
4202
486
    ("maxval", toCompactHexWithPrefix(type.max()))
4203
486
    ("panic", panicFunction(PanicCode::UnderOverflow))
4204
486
    ("cleanupFunction", cleanupFunction(_type))
4205
486
    .render();
4206
486
  });
4207
864
}
4208
4209
std::string YulUtilFunctions::incrementWrappingFunction(Type const& _type)
4210
82
{
4211
82
  solAssert(_type.category() == Type::Category::Integer, "");
4212
82
  IntegerType const& type = dynamic_cast<IntegerType const&>(_type);
4213
4214
82
  std::string const functionName = "increment_wrapping_" + _type.identifier();
4215
4216
82
  return m_functionCollector.createFunction(functionName, [&]() {
4217
41
    return Whiskers(R"(
4218
41
      function <functionName>(value) -> ret {
4219
41
        ret := <cleanupFunction>(add(value, 1))
4220
41
      }
4221
41
    )")
4222
41
    ("functionName", functionName)
4223
41
    ("cleanupFunction", cleanupFunction(type))
4224
41
    .render();
4225
41
  });
4226
82
}
4227
4228
std::string YulUtilFunctions::negateNumberCheckedFunction(Type const& _type)
4229
139
{
4230
139
  solAssert(_type.category() == Type::Category::Integer, "");
4231
139
  IntegerType const& type = dynamic_cast<IntegerType const&>(_type);
4232
139
  solAssert(type.isSigned(), "Expected signed type!");
4233
4234
139
  std::string const functionName = "negate_" + _type.identifier();
4235
139
  return m_functionCollector.createFunction(functionName, [&]() {
4236
90
    return Whiskers(R"(
4237
90
      function <functionName>(value) -> ret {
4238
90
        value := <cleanupFunction>(value)
4239
90
        if eq(value, <minval>) { <panic>() }
4240
90
        ret := sub(0, value)
4241
90
      }
4242
90
    )")
4243
90
    ("functionName", functionName)
4244
90
    ("minval", toCompactHexWithPrefix(type.min()))
4245
90
    ("cleanupFunction", cleanupFunction(_type))
4246
90
    ("panic", panicFunction(PanicCode::UnderOverflow))
4247
90
    .render();
4248
90
  });
4249
139
}
4250
4251
std::string YulUtilFunctions::negateNumberWrappingFunction(Type const& _type)
4252
5
{
4253
5
  solAssert(_type.category() == Type::Category::Integer, "");
4254
5
  IntegerType const& type = dynamic_cast<IntegerType const&>(_type);
4255
5
  solAssert(type.isSigned(), "Expected signed type!");
4256
4257
5
  std::string const functionName = "negate_wrapping_" + _type.identifier();
4258
5
  return m_functionCollector.createFunction(functionName, [&]() {
4259
5
    return Whiskers(R"(
4260
5
      function <functionName>(value) -> ret {
4261
5
        ret := <cleanupFunction>(sub(0, value))
4262
5
      }
4263
5
    )")
4264
5
    ("functionName", functionName)
4265
5
    ("cleanupFunction", cleanupFunction(type))
4266
5
    .render();
4267
5
  });
4268
5
}
4269
4270
std::string YulUtilFunctions::zeroValueFunction(Type const& _type, bool _splitFunctionTypes)
4271
5.50k
{
4272
5.50k
  solAssert(_type.category() != Type::Category::Mapping, "");
4273
4274
5.50k
  std::string const functionName = "zero_value_for_" + std::string(_splitFunctionTypes ? "split_" : "") + _type.identifier();
4275
4276
5.50k
  return m_functionCollector.createFunction(functionName, [&]() {
4277
4.91k
    FunctionType const* fType = dynamic_cast<FunctionType const*>(&_type);
4278
4.91k
    if (fType && fType->kind() == FunctionType::Kind::External && _splitFunctionTypes)
4279
5
      return Whiskers(R"(
4280
5
        function <functionName>() -> retAddress, retFunction {
4281
5
          retAddress := 0
4282
5
          retFunction := 0
4283
5
        }
4284
5
      )")
4285
5
      ("functionName", functionName)
4286
5
      .render();
4287
4288
4.90k
    if (_type.dataStoredIn(DataLocation::CallData))
4289
0
    {
4290
0
      solAssert(
4291
0
        _type.category() == Type::Category::Struct ||
4292
0
        _type.category() == Type::Category::Array,
4293
0
      "");
4294
0
      Whiskers templ(R"(
4295
0
        function <functionName>() -> offset<?hasLength>, length</hasLength> {
4296
0
          offset := calldatasize()
4297
0
          <?hasLength> length := 0 </hasLength>
4298
0
        }
4299
0
      )");
4300
0
      templ("functionName", functionName);
4301
0
      templ("hasLength",
4302
0
        _type.category() == Type::Category::Array &&
4303
0
        dynamic_cast<ArrayType const&>(_type).isDynamicallySized()
4304
0
      );
4305
4306
0
      return templ.render();
4307
0
    }
4308
4309
4.90k
    Whiskers templ(R"(
4310
4.90k
      function <functionName>() -> ret {
4311
4.90k
        ret := <zeroValue>
4312
4.90k
      }
4313
4.90k
    )");
4314
4.90k
    templ("functionName", functionName);
4315
4316
4.90k
    if (_type.isValueType())
4317
2.52k
    {
4318
2.52k
      solAssert((
4319
2.52k
        _type.hasSimpleZeroValueInMemory() ||
4320
2.52k
        (fType && (fType->kind() == FunctionType::Kind::Internal || fType->kind() == FunctionType::Kind::External))
4321
2.52k
      ), "");
4322
2.52k
      templ("zeroValue", "0");
4323
2.52k
    }
4324
2.38k
    else
4325
2.38k
    {
4326
2.38k
      solAssert(_type.dataStoredIn(DataLocation::Memory), "");
4327
2.38k
      if (auto const* arrayType = dynamic_cast<ArrayType const*>(&_type))
4328
2.36k
      {
4329
2.36k
        if (_type.isDynamicallySized())
4330
2.28k
          templ("zeroValue", std::to_string(CompilerUtils::zeroPointer));
4331
80
        else
4332
80
          templ("zeroValue", allocateAndInitializeMemoryArrayFunction(*arrayType) + "(" + std::to_string(unsigned(arrayType->length())) + ")");
4333
4334
2.36k
      }
4335
18
      else if (auto const* structType = dynamic_cast<StructType const*>(&_type))
4336
18
        templ("zeroValue", allocateAndInitializeMemoryStructFunction(*structType) + "()");
4337
0
      else
4338
18
        solUnimplemented("");
4339
2.38k
    }
4340
4341
4.90k
    return templ.render();
4342
4.90k
  });
4343
5.50k
}
4344
4345
std::string YulUtilFunctions::storageSetToZeroFunction(Type const& _type, VariableDeclaration::Location _location)
4346
1.47k
{
4347
1.47k
  std::string const functionName = "storage_set_to_zero_" + _type.identifier();
4348
4349
1.47k
  return m_functionCollector.createFunction(functionName, [&]() {
4350
1.44k
    if (_type.isValueType())
4351
1.37k
      return Whiskers(R"(
4352
1.37k
        function <functionName>(slot, offset) {
4353
1.37k
          let <values> := <zeroValue>()
4354
1.37k
          <store>(slot, offset, <values>)
4355
1.37k
        }
4356
1.37k
      )")
4357
1.37k
      ("functionName", functionName)
4358
1.37k
      ("store", updateStorageValueFunction(_type, _type, _location))
4359
1.37k
      ("values", suffixedVariableNameList("zero_", 0, _type.sizeOnStack()))
4360
1.37k
      ("zeroValue", zeroValueFunction(_type))
4361
1.37k
      .render();
4362
67
    else if (_type.category() == Type::Category::Array)
4363
36
      return Whiskers(R"(
4364
36
        function <functionName>(slot, offset) {
4365
36
          if iszero(eq(offset, 0)) { <panic>() }
4366
36
          <clearArray>(slot)
4367
36
        }
4368
36
      )")
4369
36
      ("functionName", functionName)
4370
36
      ("clearArray", clearStorageArrayFunction(dynamic_cast<ArrayType const&>(_type)))
4371
36
      ("panic", panicFunction(PanicCode::Generic))
4372
36
      .render();
4373
31
    else if (_type.category() == Type::Category::Struct)
4374
31
      return Whiskers(R"(
4375
31
        function <functionName>(slot, offset) {
4376
31
          if iszero(eq(offset, 0)) { <panic>() }
4377
31
          <clearStruct>(slot)
4378
31
        }
4379
31
      )")
4380
31
      ("functionName", functionName)
4381
31
      ("clearStruct", clearStorageStructFunction(dynamic_cast<StructType const&>(_type)))
4382
31
      ("panic", panicFunction(PanicCode::Generic))
4383
31
      .render();
4384
0
    else
4385
31
      solUnimplemented("setToZero for type " + _type.identifier() + " not yet implemented!");
4386
1.44k
  });
4387
1.47k
}
4388
4389
std::string YulUtilFunctions::conversionFunctionSpecial(Type const& _from, Type const& _to)
4390
1.06k
{
4391
1.06k
  std::string functionName =
4392
1.06k
    "convert_" +
4393
1.06k
    _from.identifier() +
4394
1.06k
    "_to_" +
4395
1.06k
    _to.identifier();
4396
1.06k
  return m_functionCollector.createFunction(functionName, [&]() {
4397
983
    if (
4398
983
      auto fromTuple = dynamic_cast<TupleType const*>(&_from), toTuple = dynamic_cast<TupleType const*>(&_to);
4399
983
      fromTuple && toTuple && fromTuple->components().size() == toTuple->components().size()
4400
983
    )
4401
340
    {
4402
340
      size_t sourceStackSize = 0;
4403
340
      size_t destStackSize = 0;
4404
340
      std::string conversions;
4405
1.02k
      for (size_t i = 0; i < fromTuple->components().size(); ++i)
4406
680
      {
4407
680
        auto fromComponent = fromTuple->components()[i];
4408
680
        auto toComponent = toTuple->components()[i];
4409
680
        solAssert(fromComponent, "");
4410
680
        if (toComponent)
4411
680
        {
4412
680
          conversions +=
4413
680
            suffixedVariableNameList("converted", destStackSize, destStackSize + toComponent->sizeOnStack()) +
4414
680
            (toComponent->sizeOnStack() > 0 ? " := " : "") +
4415
680
            conversionFunction(*fromComponent, *toComponent) +
4416
680
            "(" +
4417
680
            suffixedVariableNameList("value", sourceStackSize, sourceStackSize + fromComponent->sizeOnStack()) +
4418
680
            ")\n";
4419
680
          destStackSize += toComponent->sizeOnStack();
4420
680
        }
4421
680
        sourceStackSize += fromComponent->sizeOnStack();
4422
680
      }
4423
340
      return Whiskers(R"(
4424
340
        function <functionName>(<values>) <arrow> <converted> {
4425
340
          <conversions>
4426
340
        }
4427
340
      )")
4428
340
      ("functionName", functionName)
4429
340
      ("values", suffixedVariableNameList("value", 0, sourceStackSize))
4430
340
      ("arrow", destStackSize > 0 ? "->" : "")
4431
340
      ("converted", suffixedVariableNameList("converted", 0, destStackSize))
4432
340
      ("conversions", conversions)
4433
340
      .render();
4434
340
    }
4435
4436
643
    solUnimplementedAssert(
4437
643
      _from.category() == Type::Category::StringLiteral,
4438
643
      "Type conversion " + _from.toString() + " -> " + _to.toString() + " not yet implemented."
4439
643
    );
4440
643
    std::string const& data = dynamic_cast<StringLiteralType const&>(_from).value();
4441
643
    if (_to.category() == Type::Category::FixedBytes)
4442
24
    {
4443
24
      unsigned const numBytes = dynamic_cast<FixedBytesType const&>(_to).numBytes();
4444
24
      solAssert(data.size() <= 32, "");
4445
24
      Whiskers templ(R"(
4446
24
        function <functionName>() -> converted {
4447
24
          converted := <data>
4448
24
        }
4449
24
      )");
4450
24
      templ("functionName", functionName);
4451
24
      templ("data", formatNumber(
4452
24
        h256::Arith(h256(data, h256::AlignLeft)) &
4453
24
        (~(u256(-1) >> (8 * numBytes)))
4454
24
      ));
4455
24
      return templ.render();
4456
24
    }
4457
619
    else if (_to.category() == Type::Category::Array)
4458
619
    {
4459
619
      solAssert(dynamic_cast<ArrayType const&>(_to).isByteArrayOrString(), "");
4460
619
      Whiskers templ(R"(
4461
619
        function <functionName>() -> converted {
4462
619
          converted := <copyLiteralToMemory>()
4463
619
        }
4464
619
      )");
4465
619
      templ("functionName", functionName);
4466
619
      templ("copyLiteralToMemory", copyLiteralToMemoryFunction(data));
4467
619
      return templ.render();
4468
619
    }
4469
0
    else
4470
619
      solAssert(
4471
643
        false,
4472
643
        "Invalid conversion from std::string literal to " + _to.toString() + " requested."
4473
643
      );
4474
643
  });
4475
1.06k
}
4476
4477
std::string YulUtilFunctions::readFromMemoryOrCalldata(Type const& _type, bool _fromCalldata)
4478
5.56k
{
4479
5.56k
  std::string functionName =
4480
5.56k
    std::string("read_from_") +
4481
5.56k
    (_fromCalldata ? "calldata" : "memory") +
4482
5.56k
    _type.identifier();
4483
4484
  // TODO use ABI functions for handling calldata
4485
5.56k
  if (_fromCalldata)
4486
5.56k
    solAssert(!_type.isDynamicallyEncoded(), "");
4487
4488
5.56k
  return m_functionCollector.createFunction(functionName, [&] {
4489
1.81k
    if (auto refType = dynamic_cast<ReferenceType const*>(&_type))
4490
8
    {
4491
8
      solAssert(refType->sizeOnStack() == 1, "");
4492
8
      solAssert(!_fromCalldata, "");
4493
4494
8
      return Whiskers(R"(
4495
8
        function <functionName>(memPtr) -> value {
4496
8
          value := mload(memPtr)
4497
8
        }
4498
8
      )")
4499
8
      ("functionName", functionName)
4500
8
      .render();
4501
8
    }
4502
4503
1.80k
    solAssert(_type.isValueType(), "");
4504
1.80k
    Whiskers templ(R"(
4505
1.80k
      function <functionName>(ptr) -> <returnVariables> {
4506
1.80k
        <?fromCalldata>
4507
1.80k
          let value := calldataload(ptr)
4508
1.80k
          <validate>(value)
4509
1.80k
        <!fromCalldata>
4510
1.80k
          let value := <cleanup>(mload(ptr))
4511
1.80k
        </fromCalldata>
4512
1.80k
4513
1.80k
        <returnVariables> :=
4514
1.80k
        <?externalFunction>
4515
1.80k
          <splitFunction>(value)
4516
1.80k
        <!externalFunction>
4517
1.80k
          value
4518
1.80k
        </externalFunction>
4519
1.80k
      }
4520
1.80k
    )");
4521
1.80k
    templ("functionName", functionName);
4522
1.80k
    templ("fromCalldata", _fromCalldata);
4523
1.80k
    if (_fromCalldata)
4524
68
      templ("validate", validatorFunction(_type, true));
4525
1.80k
    auto const* funType = dynamic_cast<FunctionType const*>(&_type);
4526
1.80k
    if (funType && funType->kind() == FunctionType::Kind::External)
4527
0
    {
4528
0
      templ("externalFunction", true);
4529
0
      templ("splitFunction", splitExternalFunctionIdFunction());
4530
0
      templ("returnVariables", "addr, selector");
4531
0
    }
4532
1.80k
    else
4533
1.80k
    {
4534
1.80k
      templ("externalFunction", false);
4535
1.80k
      templ("returnVariables", "returnValue");
4536
1.80k
    }
4537
4538
    // Byte array elements generally need cleanup.
4539
    // Other types are cleaned as well to account for dirty memory e.g. due to inline assembly.
4540
1.80k
    templ("cleanup", cleanupFunction(_type));
4541
1.80k
    return templ.render();
4542
1.80k
  });
4543
5.56k
}
4544
4545
std::string YulUtilFunctions::revertReasonIfDebugFunction(std::string const& _message)
4546
78.4k
{
4547
78.4k
  std::string functionName = "revert_error_" + util::toHex(util::keccak256(_message).asBytes());
4548
78.4k
  return m_functionCollector.createFunction(functionName, [&](auto&, auto&) -> std::string {
4549
33.0k
    return revertReasonIfDebugBody(m_revertStrings, allocateUnboundedFunction() + "()", _message);
4550
33.0k
  });
4551
78.4k
}
4552
4553
std::string YulUtilFunctions::revertReasonIfDebugBody(
4554
  RevertStrings _revertStrings,
4555
  std::string const& _allocation,
4556
  std::string const& _message
4557
)
4558
67.8k
{
4559
67.8k
  if (_revertStrings < RevertStrings::Debug || _message.empty())
4560
67.8k
    return "revert(0, 0)";
4561
4562
0
  Whiskers templ(R"(
4563
0
    let start := <allocate>
4564
0
    let pos := start
4565
0
    mstore(pos, <sig>)
4566
0
    pos := add(pos, 4)
4567
0
    mstore(pos, 0x20)
4568
0
    pos := add(pos, 0x20)
4569
0
    mstore(pos, <length>)
4570
0
    pos := add(pos, 0x20)
4571
0
    <#word>
4572
0
      mstore(add(pos, <offset>), <wordValue>)
4573
0
    </word>
4574
0
    revert(start, <overallLength>)
4575
0
  )");
4576
0
  templ("allocate", _allocation);
4577
0
  templ("sig", util::selectorFromSignatureU256("Error(string)").str());
4578
0
  templ("length", std::to_string(_message.length()));
4579
4580
0
  size_t words = (_message.length() + 31) / 32;
4581
0
  std::vector<std::map<std::string, std::string>> wordParams(words);
4582
0
  for (size_t i = 0; i < words; ++i)
4583
0
  {
4584
0
    wordParams[i]["offset"] = std::to_string(i * 32);
4585
0
    wordParams[i]["wordValue"] = formatAsStringOrNumber(_message.substr(32 * i, 32));
4586
0
  }
4587
0
  templ("word", wordParams);
4588
0
  templ("overallLength", std::to_string(4 + 0x20 + 0x20 + words * 32));
4589
4590
0
  return templ.render();
4591
67.8k
}
4592
4593
std::string YulUtilFunctions::panicFunction(util::PanicCode _code)
4594
172k
{
4595
172k
  std::string functionName = "panic_error_" + toCompactHexWithPrefix(uint64_t(_code));
4596
172k
  return m_functionCollector.createFunction(functionName, [&]() {
4597
16.4k
    return Whiskers(R"(
4598
16.4k
      function <functionName>() {
4599
16.4k
        mstore(0, <selector>)
4600
16.4k
        mstore(4, <code>)
4601
16.4k
        revert(0, 0x24)
4602
16.4k
      }
4603
16.4k
    )")
4604
16.4k
    ("functionName", functionName)
4605
16.4k
    ("selector", util::selectorFromSignatureU256("Panic(uint256)").str())
4606
16.4k
    ("code", toCompactHexWithPrefix(static_cast<unsigned>(_code)))
4607
16.4k
    .render();
4608
16.4k
  });
4609
172k
}
4610
4611
std::string YulUtilFunctions::returnDataSelectorFunction()
4612
17
{
4613
17
  std::string const functionName = "return_data_selector";
4614
17
  solAssert(m_evmVersion.supportsReturndata(), "");
4615
4616
17
  return m_functionCollector.createFunction(functionName, [&]() {
4617
17
    return util::Whiskers(R"(
4618
17
      function <functionName>() -> sig {
4619
17
        if gt(returndatasize(), 3) {
4620
17
          returndatacopy(0, 0, 4)
4621
17
          sig := <shr224>(mload(0))
4622
17
        }
4623
17
      }
4624
17
    )")
4625
17
    ("functionName", functionName)
4626
17
    ("shr224", shiftRightFunction(224))
4627
17
    .render();
4628
17
  });
4629
17
}
4630
4631
std::string YulUtilFunctions::tryDecodeErrorMessageFunction()
4632
14
{
4633
14
  std::string const functionName = "try_decode_error_message";
4634
14
  solAssert(m_evmVersion.supportsReturndata(), "");
4635
4636
14
  return m_functionCollector.createFunction(functionName, [&]() {
4637
14
    return util::Whiskers(R"(
4638
14
      function <functionName>() -> ret {
4639
14
        if lt(returndatasize(), 0x44) { leave }
4640
14
4641
14
        let data := <allocateUnbounded>()
4642
14
        returndatacopy(data, 4, sub(returndatasize(), 4))
4643
14
4644
14
        let offset := mload(data)
4645
14
        if or(
4646
14
          gt(offset, 0xffffffffffffffff),
4647
14
          gt(add(offset, 0x24), returndatasize())
4648
14
        ) {
4649
14
          leave
4650
14
        }
4651
14
4652
14
        let msg := add(data, offset)
4653
14
        let length := mload(msg)
4654
14
        if gt(length, 0xffffffffffffffff) { leave }
4655
14
4656
14
        let end := add(add(msg, 0x20), length)
4657
14
        if gt(end, add(data, sub(returndatasize(), 4))) { leave }
4658
14
4659
14
        <finalizeAllocation>(data, add(offset, add(0x20, length)))
4660
14
        ret := msg
4661
14
      }
4662
14
    )")
4663
14
    ("functionName", functionName)
4664
14
    ("allocateUnbounded", allocateUnboundedFunction())
4665
14
    ("finalizeAllocation", finalizeAllocationFunction())
4666
14
    .render();
4667
14
  });
4668
14
}
4669
4670
std::string YulUtilFunctions::tryDecodePanicDataFunction()
4671
7
{
4672
7
  std::string const functionName = "try_decode_panic_data";
4673
7
  solAssert(m_evmVersion.supportsReturndata(), "");
4674
4675
7
  return m_functionCollector.createFunction(functionName, [&]() {
4676
7
    return util::Whiskers(R"(
4677
7
      function <functionName>() -> success, data {
4678
7
        if gt(returndatasize(), 0x23) {
4679
7
          returndatacopy(0, 4, 0x20)
4680
7
          success := 1
4681
7
          data := mload(0)
4682
7
        }
4683
7
      }
4684
7
    )")
4685
7
    ("functionName", functionName)
4686
7
    .render();
4687
7
  });
4688
7
}
4689
4690
std::string YulUtilFunctions::extractReturndataFunction()
4691
76
{
4692
76
  std::string const functionName = "extract_returndata";
4693
4694
76
  return m_functionCollector.createFunction(functionName, [&]() {
4695
76
    return util::Whiskers(R"(
4696
76
      function <functionName>() -> data {
4697
76
        <?supportsReturndata>
4698
76
          switch returndatasize()
4699
76
          case 0 {
4700
76
            data := <emptyArray>()
4701
76
          }
4702
76
          default {
4703
76
            data := <allocateArray>(returndatasize())
4704
76
            returndatacopy(add(data, 0x20), 0, returndatasize())
4705
76
          }
4706
76
        <!supportsReturndata>
4707
76
          data := <emptyArray>()
4708
76
        </supportsReturndata>
4709
76
      }
4710
76
    )")
4711
76
    ("functionName", functionName)
4712
76
    ("supportsReturndata", m_evmVersion.supportsReturndata())
4713
76
    ("allocateArray", allocateMemoryArrayFunction(*TypeProvider::bytesMemory()))
4714
76
    ("emptyArray", zeroValueFunction(*TypeProvider::bytesMemory()))
4715
76
    .render();
4716
76
  });
4717
76
}
4718
4719
std::string YulUtilFunctions::copyConstructorArgumentsToMemoryFunction(
4720
  ContractDefinition const& _contract,
4721
  std::string const& _creationObjectName
4722
)
4723
241
{
4724
241
  std::string functionName = "copy_arguments_for_constructor_" +
4725
241
    toString(_contract.constructor()->id()) +
4726
241
    "_object_" +
4727
241
    _contract.name() +
4728
241
    "_" +
4729
241
    toString(_contract.id());
4730
4731
241
  return m_functionCollector.createFunction(functionName, [&]() {
4732
241
    std::string returnParams = suffixedVariableNameList("ret_param_",0, CompilerUtils::sizeOnStack(_contract.constructor()->parameters()));
4733
241
    ABIFunctions abiFunctions(m_evmVersion, m_eofVersion, m_revertStrings, m_functionCollector);
4734
4735
241
    return util::Whiskers(R"(
4736
241
      function <functionName>() -> <retParams> {
4737
241
        <?eof>
4738
241
          let argSize := calldatasize()
4739
241
          let memoryDataOffset := <allocate>(argSize)
4740
241
          calldatacopy(memoryDataOffset, 0, argSize)
4741
241
        <!eof>
4742
241
          let programSize := datasize("<object>")
4743
241
          let argSize := sub(codesize(), programSize)
4744
241
4745
241
          let memoryDataOffset := <allocate>(argSize)
4746
241
          codecopy(memoryDataOffset, programSize, argSize)
4747
241
        </eof>
4748
241
4749
241
        <retParams> := <abiDecode>(memoryDataOffset, add(memoryDataOffset, argSize))
4750
241
      }
4751
241
    )")
4752
241
    ("functionName", functionName)
4753
241
    ("retParams", returnParams)
4754
241
    ("object", _creationObjectName)
4755
241
    ("allocate", allocationFunction())
4756
241
    ("abiDecode", abiFunctions.tupleDecoder(FunctionType(*_contract.constructor()).parameterTypes(), true))
4757
241
    ("eof", m_eofVersion.has_value())
4758
241
    .render();
4759
241
  });
4760
241
}
4761
4762
std::string YulUtilFunctions::externalCodeFunction()
4763
9
{
4764
9
  std::string functionName = "external_code_at";
4765
4766
9
  return m_functionCollector.createFunction(functionName, [&]() {
4767
9
    return util::Whiskers(R"(
4768
9
      function <functionName>(addr) -> mpos {
4769
9
        let length := extcodesize(addr)
4770
9
        mpos := <allocateArray>(length)
4771
9
        extcodecopy(addr, add(mpos, 0x20), 0, length)
4772
9
      }
4773
9
    )")
4774
9
    ("functionName", functionName)
4775
9
    ("allocateArray", allocateMemoryArrayFunction(*TypeProvider::bytesMemory()))
4776
9
    .render();
4777
9
  });
4778
9
}
4779
4780
std::string YulUtilFunctions::externalFunctionPointersEqualFunction()
4781
0
{
4782
0
  std::string const functionName = "externalFunctionPointersEqualFunction";
4783
0
  return m_functionCollector.createFunction(functionName, [&]() {
4784
0
    return util::Whiskers(R"(
4785
0
      function <functionName>(
4786
0
        leftAddress,
4787
0
        leftSelector,
4788
0
        rightAddress,
4789
0
        rightSelector
4790
0
      ) -> result {
4791
0
        result := and(
4792
0
          eq(
4793
0
            <addressCleanUpFunction>(leftAddress), <addressCleanUpFunction>(rightAddress)
4794
0
          ),
4795
0
          eq(
4796
0
            <selectorCleanUpFunction>(leftSelector), <selectorCleanUpFunction>(rightSelector)
4797
0
          )
4798
0
        )
4799
0
      }
4800
0
    )")
4801
0
    ("functionName", functionName)
4802
0
    ("addressCleanUpFunction", cleanupFunction(*TypeProvider::address()))
4803
0
    ("selectorCleanUpFunction", cleanupFunction(*TypeProvider::uint(32)))
4804
0
    .render();
4805
0
  });
4806
0
}