Coverage Report

Created: 2025-06-24 07:59

/src/solidity/libsolidity/codegen/ir/IRGenerator.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
 * @author Alex Beregszaszi
20
 * @date 2017
21
 * Component that translates Solidity code into Yul.
22
 */
23
24
#include <libsolidity/codegen/ir/Common.h>
25
#include <libsolidity/codegen/ir/IRGenerator.h>
26
#include <libsolidity/codegen/ir/IRGeneratorForStatements.h>
27
28
#include <libsolidity/ast/AST.h>
29
#include <libsolidity/ast/ASTVisitor.h>
30
#include <libsolidity/codegen/ABIFunctions.h>
31
#include <libsolidity/codegen/CompilerUtils.h>
32
33
#include <libyul/Object.h>
34
#include <libyul/Utilities.h>
35
36
#include <libsolutil/Algorithms.h>
37
#include <libsolutil/CommonData.h>
38
#include <libsolutil/StringUtils.h>
39
#include <libsolutil/Whiskers.h>
40
#include <libsolutil/JSON.h>
41
42
#include <range/v3/algorithm/all_of.hpp>
43
44
#include <sstream>
45
#include <variant>
46
47
using namespace solidity;
48
using namespace solidity::frontend;
49
using namespace solidity::langutil;
50
using namespace solidity::util;
51
using namespace std::string_literals;
52
53
namespace
54
{
55
56
void verifyCallGraph(
57
  std::set<CallableDeclaration const*, ASTNode::CompareByID> const& _expectedCallables,
58
  std::set<FunctionDefinition const*> _generatedFunctions
59
)
60
7.88k
{
61
7.88k
  for (auto const& expectedCallable: _expectedCallables)
62
3.90k
    if (auto const* expectedFunction = dynamic_cast<FunctionDefinition const*>(expectedCallable))
63
3.84k
    {
64
3.84k
      solAssert(
65
3.84k
        _generatedFunctions.count(expectedFunction) == 1 || expectedFunction->isConstructor(),
66
3.84k
        "No code generated for function " + expectedFunction->name() + " even though it is not a constructor."
67
3.84k
      );
68
3.84k
      _generatedFunctions.erase(expectedFunction);
69
3.84k
    }
70
71
7.88k
  solAssert(
72
7.88k
    _generatedFunctions.size() == 0,
73
7.88k
    "Of the generated functions " + toString(_generatedFunctions.size()) + " are not in the call graph."
74
7.88k
  );
75
7.88k
}
76
77
std::set<CallableDeclaration const*, ASTNode::CompareByID> collectReachableCallables(
78
  CallGraph const& _graph
79
)
80
7.88k
{
81
7.88k
  std::set<CallableDeclaration const*, ASTNode::CompareByID> reachableCallables;
82
7.88k
  for (CallGraph::Node const& reachableNode: _graph.edges | ranges::views::keys)
83
7.44k
    if (std::holds_alternative<CallableDeclaration const*>(reachableNode))
84
3.90k
      reachableCallables.emplace(std::get<CallableDeclaration const*>(reachableNode));
85
86
7.88k
  return reachableCallables;
87
7.88k
}
88
89
}
90
91
std::string IRGenerator::run(
92
  ContractDefinition const& _contract,
93
  bytes const& _cborMetadata,
94
  std::map<ContractDefinition const*, std::string_view const> const& _otherYulSources
95
)
96
3.94k
{
97
3.94k
  return yul::reindent(generate(_contract, _cborMetadata, _otherYulSources));
98
3.94k
}
99
100
std::string IRGenerator::generate(
101
  ContractDefinition const& _contract,
102
  bytes const& _cborMetadata,
103
  std::map<ContractDefinition const*, std::string_view const> const& _otherYulSources
104
)
105
3.94k
{
106
3.94k
  auto subObjectSources = [&_otherYulSources](UniqueVector<ContractDefinition const*> const& _subObjects) -> std::string
107
7.88k
  {
108
7.88k
    std::string subObjectsSources;
109
7.88k
    for (ContractDefinition const* subObject: _subObjects)
110
106
      subObjectsSources += _otherYulSources.at(subObject);
111
7.88k
    return subObjectsSources;
112
7.88k
  };
113
3.94k
  auto formatUseSrcMap = [](IRGenerationContext const& _context) -> std::string
114
7.88k
  {
115
7.88k
    return joinHumanReadable(
116
7.89k
      ranges::views::transform(_context.usedSourceNames(), [_context](std::string const& _sourceName) {
117
7.89k
        return std::to_string(_context.sourceIndices().at(_sourceName)) + ":" + escapeAndQuoteString(_sourceName);
118
7.89k
      }),
119
7.88k
      ", "
120
7.88k
    );
121
7.88k
  };
122
123
3.94k
  Whiskers t(R"(<?isEthdebugEnabled>/// ethdebug: enabled</isEthdebugEnabled>
124
3.94k
    /// @use-src <useSrcMapCreation>
125
3.94k
    object "<CreationObject>" {
126
3.94k
      code {
127
3.94k
        <sourceLocationCommentCreation>
128
3.94k
        <memoryInitCreation>
129
3.94k
        <callValueCheck>
130
3.94k
        <?library>
131
3.94k
        <!library>
132
3.94k
        <?constructorHasParams> let <constructorParams> := <copyConstructorArguments>() </constructorHasParams>
133
3.94k
        <constructor>(<constructorParams>)
134
3.94k
        </library>
135
3.94k
        <deploy>
136
3.94k
        <functions>
137
3.94k
      }
138
3.94k
      /// @use-src <useSrcMapDeployed>
139
3.94k
      object "<DeployedObject>" {
140
3.94k
        code {
141
3.94k
          <sourceLocationCommentDeployed>
142
3.94k
          <memoryInitDeployed>
143
3.94k
          <?library>
144
3.94k
            <?eof>
145
3.94k
              let called_via_delegatecall := iszero(eq(auxdataloadn(<library_address_immutable_offset>), address()))
146
3.94k
            <!eof>
147
3.94k
              let called_via_delegatecall := iszero(eq(loadimmutable("<library_address>"), address()))
148
3.94k
            </eof>
149
3.94k
          </library>
150
3.94k
          <dispatch>
151
3.94k
          <deployedFunctions>
152
3.94k
        }
153
3.94k
        <deployedSubObjects>
154
3.94k
        data "<metadataName>" hex"<cborMetadata>"
155
3.94k
      }
156
3.94k
      <subObjects>
157
3.94k
    }
158
3.94k
  )");
159
160
3.94k
  resetContext(_contract, ExecutionContext::Creation);
161
3.94k
  auto const eof = m_context.eofVersion().has_value();
162
3.94k
  if (eof && _contract.isLibrary())
163
0
    m_context.registerLibraryAddressImmutable();
164
3.94k
  for (VariableDeclaration const* var: ContractType(_contract).immutableVariables())
165
90
    m_context.registerImmutableVariable(*var);
166
167
3.94k
  t("isEthdebugEnabled", m_context.debugInfoSelection().ethdebug);
168
3.94k
  t("CreationObject", IRNames::creationObject(_contract));
169
3.94k
  t("sourceLocationCommentCreation", dispenseLocationComment(_contract));
170
3.94k
  t("library", _contract.isLibrary());
171
172
3.94k
  FunctionDefinition const* constructor = _contract.constructor();
173
3.94k
  t("callValueCheck", !constructor || !constructor->isPayable() ? callValueCheck() : "");
174
3.94k
  std::vector<std::string> constructorParams;
175
3.94k
  if (constructor && !constructor->parameters().empty())
176
155
  {
177
324
    for (size_t i = 0; i < CompilerUtils::sizeOnStack(constructor->parameters()); ++i)
178
169
      constructorParams.emplace_back(m_context.newYulVariable());
179
155
    t(
180
155
      "copyConstructorArguments",
181
155
      m_utils.copyConstructorArgumentsToMemoryFunction(
182
155
        _contract,
183
155
        IRNames::creationObject(_contract)
184
155
      )
185
155
    );
186
155
  }
187
3.94k
  t("constructorParams", joinHumanReadable(constructorParams));
188
3.94k
  t("constructorHasParams", !constructorParams.empty());
189
3.94k
  t("constructor", IRNames::constructor(_contract));
190
191
3.94k
  t("deploy", deployCode(_contract));
192
3.94k
  generateConstructors(_contract);
193
3.94k
  std::set<FunctionDefinition const*> creationFunctionList = generateQueuedFunctions();
194
3.94k
  InternalDispatchMap internalDispatchMap = generateInternalDispatchFunctions(_contract);
195
196
3.94k
  t("functions", m_context.functionCollector().requestedFunctions());
197
3.94k
  t("subObjects", subObjectSources(m_context.subObjectsCreated()));
198
199
  // This has to be called only after all other code generation for the creation object is complete.
200
3.94k
  bool creationInvolvesMemoryUnsafeAssembly = m_context.memoryUnsafeInlineAssemblySeen();
201
3.94k
  t("memoryInitCreation", memoryInit(!creationInvolvesMemoryUnsafeAssembly));
202
3.94k
  t("useSrcMapCreation", formatUseSrcMap(m_context));
203
204
3.94k
  auto const immutableVariables = m_context.immutableVariables();
205
3.94k
  auto const libraryAddressImmutableOffset = (_contract.isLibrary() && eof) ?
206
3.94k
    m_context.libraryAddressImmutableOffset() : 0;
207
208
3.94k
  resetContext(_contract, ExecutionContext::Deployed);
209
210
  // When generating to EOF we have to initialize these two members, because they store offsets in EOF data section
211
  // which is used during deployed container generation
212
3.94k
  if (m_eofVersion.has_value())
213
0
  {
214
0
    m_context.setImmutableVariables(std::move(immutableVariables));
215
0
    if (_contract.isLibrary())
216
0
      m_context.setLibraryAddressImmutableOffset(libraryAddressImmutableOffset);
217
0
  }
218
219
  // NOTE: Function pointers can be passed from creation code via storage variables. We need to
220
  // get all the functions they could point to into the dispatch functions even if they're never
221
  // referenced by name in the deployed code.
222
3.94k
  m_context.initializeInternalDispatch(std::move(internalDispatchMap));
223
224
  // Do not register immutables to avoid assignment.
225
3.94k
  t("DeployedObject", IRNames::deployedObject(_contract));
226
3.94k
  t("sourceLocationCommentDeployed", dispenseLocationComment(_contract));
227
3.94k
  t("eof", eof);
228
3.94k
  if (_contract.isLibrary())
229
33
  {
230
33
    if (!eof)
231
33
      t("library_address", IRNames::libraryAddressImmutable());
232
0
    else
233
0
      t("library_address_immutable_offset", std::to_string(m_context.libraryAddressImmutableOffsetRelative()));
234
33
  }
235
236
3.94k
  t("dispatch", dispatchRoutine(_contract));
237
3.94k
  std::set<FunctionDefinition const*> deployedFunctionList = generateQueuedFunctions();
238
3.94k
  generateInternalDispatchFunctions(_contract);
239
3.94k
  t("deployedFunctions", m_context.functionCollector().requestedFunctions());
240
3.94k
  t("deployedSubObjects", subObjectSources(m_context.subObjectsCreated()));
241
3.94k
  t("metadataName", yul::Object::metadataName());
242
3.94k
  t("cborMetadata", util::toHex(_cborMetadata));
243
244
3.94k
  t("useSrcMapDeployed", formatUseSrcMap(m_context));
245
246
  // This has to be called only after all other code generation for the deployed object is complete.
247
3.94k
  bool deployedInvolvesMemoryUnsafeAssembly = m_context.memoryUnsafeInlineAssemblySeen();
248
3.94k
  t("memoryInitDeployed", memoryInit(!deployedInvolvesMemoryUnsafeAssembly));
249
250
3.94k
  solAssert(_contract.annotation().creationCallGraph->get() != nullptr, "");
251
3.94k
  solAssert(_contract.annotation().deployedCallGraph->get() != nullptr, "");
252
3.94k
  verifyCallGraph(collectReachableCallables(**_contract.annotation().creationCallGraph), std::move(creationFunctionList));
253
3.94k
  verifyCallGraph(collectReachableCallables(**_contract.annotation().deployedCallGraph), std::move(deployedFunctionList));
254
255
3.94k
  return t.render();
256
3.94k
}
257
258
std::string IRGenerator::generate(Block const& _block)
259
3.84k
{
260
3.84k
  IRGeneratorForStatements generator(m_context, m_utils, m_optimiserSettings);
261
3.84k
  generator.generate(_block);
262
3.84k
  return generator.code();
263
3.84k
}
264
265
std::set<FunctionDefinition const*> IRGenerator::generateQueuedFunctions()
266
7.88k
{
267
7.88k
  std::set<FunctionDefinition const*> functions;
268
269
11.1k
  while (!m_context.functionGenerationQueueEmpty())
270
3.30k
  {
271
3.30k
    FunctionDefinition const& functionDefinition = *m_context.dequeueFunctionForCodeGeneration();
272
273
3.30k
    functions.emplace(&functionDefinition);
274
    // NOTE: generateFunction() may modify function generation queue
275
3.30k
    generateFunction(functionDefinition);
276
3.30k
  }
277
278
7.88k
  return functions;
279
7.88k
}
280
281
InternalDispatchMap IRGenerator::generateInternalDispatchFunctions(ContractDefinition const& _contract)
282
7.88k
{
283
7.88k
  solAssert(
284
7.88k
    m_context.functionGenerationQueueEmpty(),
285
7.88k
    "At this point all the enqueued functions should have been generated. "
286
7.88k
    "Otherwise the dispatch may be incomplete."
287
7.88k
  );
288
289
7.88k
  InternalDispatchMap internalDispatchMap = m_context.consumeInternalDispatchMap();
290
7.88k
  for (YulArity const& arity: internalDispatchMap | ranges::views::keys)
291
28
  {
292
28
    std::string funName = IRNames::internalDispatch(arity);
293
28
    m_context.functionCollector().createFunction(funName, [&]() {
294
28
      Whiskers templ(R"(
295
28
        <sourceLocationComment>
296
28
        function <functionName>(fun<?+in>, <in></+in>) <?+out>-> <out></+out> {
297
28
          switch fun
298
28
          <#cases>
299
28
          case <funID>
300
28
          {
301
28
            <?+out> <out> :=</+out> <name>(<in>)
302
28
          }
303
28
          </cases>
304
28
          default { <panic>() }
305
28
        }
306
28
        <sourceLocationComment>
307
28
      )");
308
28
      templ("sourceLocationComment", dispenseLocationComment(_contract));
309
28
      templ("functionName", funName);
310
28
      templ("panic", m_utils.panicFunction(PanicCode::InvalidInternalFunction));
311
28
      templ("in", suffixedVariableNameList("in_", 0, arity.in));
312
28
      templ("out", suffixedVariableNameList("out_", 0, arity.out));
313
314
28
      std::vector<std::map<std::string, std::string>> cases;
315
28
      std::set<int64_t> caseValues;
316
28
      for (FunctionDefinition const* function: internalDispatchMap.at(arity))
317
18
      {
318
18
        solAssert(function, "");
319
18
        solAssert(
320
18
          YulArity::fromType(*TypeProvider::function(*function, FunctionType::Kind::Internal)) == arity,
321
18
          "A single dispatch function can only handle functions of one arity"
322
18
        );
323
18
        solAssert(!function->isConstructor(), "");
324
        // 0 is reserved for uninitialized function pointers
325
18
        solAssert(function->id() != 0, "Unexpected function ID: 0");
326
18
        solAssert(caseValues.count(function->id()) == 0, "Duplicate function ID");
327
18
        solAssert(m_context.functionCollector().contains(IRNames::function(*function)), "");
328
329
18
        cases.emplace_back(std::map<std::string, std::string>{
330
18
          {"funID", std::to_string(m_context.mostDerivedContract().annotation().internalFunctionIDs.at(function))},
331
18
          {"name", IRNames::function(*function)}
332
18
        });
333
18
        caseValues.insert(function->id());
334
18
      }
335
336
28
      templ("cases", std::move(cases));
337
28
      return templ.render();
338
28
    });
339
28
  }
340
341
7.88k
  solAssert(m_context.internalDispatchClean(), "");
342
7.88k
  solAssert(
343
7.88k
    m_context.functionGenerationQueueEmpty(),
344
7.88k
    "Internal dispatch generation must not add new functions to generation queue because they won't be proeessed."
345
7.88k
  );
346
347
7.88k
  return internalDispatchMap;
348
7.88k
}
349
350
std::string IRGenerator::generateFunction(FunctionDefinition const& _function)
351
3.30k
{
352
3.30k
  std::string functionName = IRNames::function(_function);
353
3.30k
  return m_context.functionCollector().createFunction(functionName, [&]() {
354
3.25k
    m_context.resetLocalVariables();
355
3.25k
    Whiskers t(R"(
356
3.25k
      <astIDComment><sourceLocationComment>
357
3.25k
      function <functionName>(<params>)<?+retParams> -> <retParams></+retParams> {
358
3.25k
        <retInit>
359
3.25k
        <body>
360
3.25k
      }
361
3.25k
      <contractSourceLocationComment>
362
3.25k
    )");
363
364
3.25k
    if (m_context.debugInfoSelection().astID)
365
3.25k
      t("astIDComment", "/// @ast-id " + std::to_string(_function.id()) + "\n");
366
0
    else
367
0
      t("astIDComment", "");
368
3.25k
    t("sourceLocationComment", dispenseLocationComment(_function));
369
3.25k
    t(
370
3.25k
      "contractSourceLocationComment",
371
3.25k
      dispenseLocationComment(m_context.mostDerivedContract())
372
3.25k
    );
373
374
3.25k
    t("functionName", functionName);
375
3.25k
    std::vector<std::string> params;
376
3.25k
    for (auto const& varDecl: _function.parameters())
377
1.90k
      params += m_context.addLocalVariable(*varDecl).stackSlots();
378
3.25k
    t("params", joinHumanReadable(params));
379
3.25k
    std::vector<std::string> retParams;
380
3.25k
    std::string retInit;
381
3.25k
    for (auto const& varDecl: _function.returnParameters())
382
1.96k
    {
383
1.96k
      retParams += m_context.addLocalVariable(*varDecl).stackSlots();
384
1.96k
      retInit += generateInitialAssignment(*varDecl);
385
1.96k
    }
386
387
3.25k
    t("retParams", joinHumanReadable(retParams));
388
3.25k
    t("retInit", retInit);
389
390
3.25k
    if (_function.modifiers().empty())
391
3.19k
      t("body", generate(_function.body()));
392
58
    else
393
58
    {
394
138
      for (size_t i = 0; i < _function.modifiers().size(); ++i)
395
80
      {
396
80
        ModifierInvocation const& modifier = *_function.modifiers().at(i);
397
80
        std::string next =
398
80
          i + 1 < _function.modifiers().size() ?
399
22
          IRNames::modifierInvocation(*_function.modifiers().at(i + 1)) :
400
80
          IRNames::functionWithModifierInner(_function);
401
80
        generateModifier(modifier, _function, next);
402
80
      }
403
58
      t("body",
404
58
        (retParams.empty() ? std::string{} : joinHumanReadable(retParams) + " := ") +
405
58
        IRNames::modifierInvocation(*_function.modifiers().at(0)) +
406
58
        "(" +
407
58
        joinHumanReadable(retParams + params) +
408
58
        ")"
409
58
      );
410
      // Now generate the actual inner function.
411
58
      generateFunctionWithModifierInner(_function);
412
58
    }
413
3.25k
    return t.render();
414
3.25k
  });
415
3.30k
}
416
417
std::string IRGenerator::generateModifier(
418
  ModifierInvocation const& _modifierInvocation,
419
  FunctionDefinition const& _function,
420
  std::string const& _nextFunction
421
)
422
82
{
423
82
  std::string functionName = IRNames::modifierInvocation(_modifierInvocation);
424
82
  return m_context.functionCollector().createFunction(functionName, [&]() {
425
82
    m_context.resetLocalVariables();
426
82
    Whiskers t(R"(
427
82
      <astIDComment><sourceLocationComment>
428
82
      function <functionName>(<params>)<?+retParams> -> <retParams></+retParams> {
429
82
        <assignRetParams>
430
82
        <evalArgs>
431
82
        <body>
432
82
      }
433
82
      <contractSourceLocationComment>
434
82
    )");
435
436
82
    t("functionName", functionName);
437
82
    std::vector<std::string> retParamsIn;
438
82
    for (auto const& varDecl: _function.returnParameters())
439
120
      retParamsIn += m_context.addLocalVariable(*varDecl).stackSlots();
440
82
    std::vector<std::string> params = retParamsIn;
441
82
    for (auto const& varDecl: _function.parameters())
442
21
      params += m_context.addLocalVariable(*varDecl).stackSlots();
443
82
    t("params", joinHumanReadable(params));
444
82
    std::vector<std::string> retParams;
445
82
    std::string assignRetParams;
446
202
    for (size_t i = 0; i < retParamsIn.size(); ++i)
447
120
    {
448
120
      retParams.emplace_back(m_context.newYulVariable());
449
120
      assignRetParams += retParams.at(i) + " := " + retParamsIn.at(i) + "\n";
450
120
    }
451
82
    t("retParams", joinHumanReadable(retParams));
452
82
    t("assignRetParams", assignRetParams);
453
454
82
    ModifierDefinition const* modifier = dynamic_cast<ModifierDefinition const*>(
455
82
      _modifierInvocation.name().annotation().referencedDeclaration
456
82
    );
457
82
    solAssert(modifier, "");
458
459
82
    if (m_context.debugInfoSelection().astID)
460
82
      t("astIDComment", "/// @ast-id " + std::to_string(modifier->id()) + "\n");
461
0
    else
462
0
      t("astIDComment", "");
463
82
    t("sourceLocationComment", dispenseLocationComment(*modifier));
464
82
    t(
465
82
      "contractSourceLocationComment",
466
82
      dispenseLocationComment(m_context.mostDerivedContract())
467
82
    );
468
469
82
    switch (*_modifierInvocation.name().annotation().requiredLookup)
470
82
    {
471
82
    case VirtualLookup::Virtual:
472
82
      modifier = &modifier->resolveVirtual(m_context.mostDerivedContract());
473
82
      solAssert(modifier, "");
474
82
      break;
475
82
    case VirtualLookup::Static:
476
0
      break;
477
0
    case VirtualLookup::Super:
478
0
      solAssert(false, "");
479
82
    }
480
481
82
    solAssert(
482
82
      modifier->parameters().empty() ==
483
82
      (!_modifierInvocation.arguments() || _modifierInvocation.arguments()->empty()),
484
82
      ""
485
82
    );
486
82
    IRGeneratorForStatements expressionEvaluator(m_context, m_utils, m_optimiserSettings);
487
82
    if (_modifierInvocation.arguments())
488
60
      for (size_t i = 0; i < _modifierInvocation.arguments()->size(); i++)
489
29
      {
490
29
        IRVariable argument = expressionEvaluator.evaluateExpression(
491
29
          *_modifierInvocation.arguments()->at(i),
492
29
          *modifier->parameters()[i]->annotation().type
493
29
        );
494
29
        expressionEvaluator.define(
495
29
          m_context.addLocalVariable(*modifier->parameters()[i]),
496
29
          argument
497
29
        );
498
29
      }
499
500
82
    t("evalArgs", expressionEvaluator.code());
501
93
    IRGeneratorForStatements generator(m_context, m_utils, m_optimiserSettings, [&]() {
502
93
      std::string ret = joinHumanReadable(retParams);
503
93
      return
504
93
        (ret.empty() ? "" : ret + " := ") +
505
93
        _nextFunction + "(" + joinHumanReadable(params) + ")\n";
506
93
    });
507
82
    generator.generate(modifier->body());
508
82
    t("body", generator.code());
509
82
    return t.render();
510
82
  });
511
82
}
512
513
std::string IRGenerator::generateFunctionWithModifierInner(FunctionDefinition const& _function)
514
60
{
515
60
  std::string functionName = IRNames::functionWithModifierInner(_function);
516
60
  return m_context.functionCollector().createFunction(functionName, [&]() {
517
60
    m_context.resetLocalVariables();
518
60
    Whiskers t(R"(
519
60
      <sourceLocationComment>
520
60
      function <functionName>(<params>)<?+retParams> -> <retParams></+retParams> {
521
60
        <assignRetParams>
522
60
        <body>
523
60
      }
524
60
      <contractSourceLocationComment>
525
60
    )");
526
60
    t("sourceLocationComment", dispenseLocationComment(_function));
527
60
    t(
528
60
      "contractSourceLocationComment",
529
60
      dispenseLocationComment(m_context.mostDerivedContract())
530
60
    );
531
60
    t("functionName", functionName);
532
60
    std::vector<std::string> retParams;
533
60
    std::vector<std::string> retParamsIn;
534
60
    for (auto const& varDecl: _function.returnParameters())
535
90
      retParams += m_context.addLocalVariable(*varDecl).stackSlots();
536
60
    std::string assignRetParams;
537
150
    for (size_t i = 0; i < retParams.size(); ++i)
538
90
    {
539
90
      retParamsIn.emplace_back(m_context.newYulVariable());
540
90
      assignRetParams += retParams.at(i) + " := " + retParamsIn.at(i) + "\n";
541
90
    }
542
60
    std::vector<std::string> params = retParamsIn;
543
60
    for (auto const& varDecl: _function.parameters())
544
19
      params += m_context.addLocalVariable(*varDecl).stackSlots();
545
60
    t("params", joinHumanReadable(params));
546
60
    t("retParams", joinHumanReadable(retParams));
547
60
    t("assignRetParams", assignRetParams);
548
60
    t("body", generate(_function.body()));
549
60
    return t.render();
550
60
  });
551
60
}
552
553
std::string IRGenerator::generateGetter(VariableDeclaration const& _varDecl)
554
934
{
555
934
  std::string functionName = IRNames::function(_varDecl);
556
934
  return m_context.functionCollector().createFunction(functionName, [&]() {
557
934
    Type const* type = _varDecl.annotation().type;
558
559
934
    solAssert(_varDecl.isStateVariable(), "");
560
561
934
    FunctionType accessorType(_varDecl);
562
934
    TypePointers paramTypes = accessorType.parameterTypes();
563
934
    if (_varDecl.immutable())
564
38
    {
565
38
      solAssert(paramTypes.empty(), "");
566
38
      solUnimplementedAssert(type->sizeOnStack() == 1);
567
568
38
      auto t = Whiskers(R"(
569
38
        <astIDComment><sourceLocationComment>
570
38
        function <functionName>() -> rval {
571
38
          <?eof>
572
38
            rval := auxdataloadn(<immutableOffset>)
573
38
          <!eof>
574
38
            rval := loadimmutable("<id>")
575
38
          </eof>
576
38
        }
577
38
        <contractSourceLocationComment>
578
38
        )");
579
38
      t(
580
38
        "astIDComment",
581
38
        m_context.debugInfoSelection().astID ?
582
38
          "/// @ast-id " + std::to_string(_varDecl.id()) + "\n" :
583
38
          ""
584
38
      );
585
38
      t("sourceLocationComment", dispenseLocationComment(_varDecl));
586
38
      t(
587
38
        "contractSourceLocationComment",
588
38
        dispenseLocationComment(m_context.mostDerivedContract())
589
38
      );
590
38
      t("functionName", functionName);
591
592
38
      auto const eof = m_context.eofVersion().has_value();
593
38
      t("eof", eof);
594
38
      if (!eof)
595
38
        t("id", std::to_string(_varDecl.id()));
596
0
      else
597
0
        t("immutableOffset", std::to_string(m_context.immutableMemoryOffsetRelative(_varDecl)));
598
599
38
      return t.render();
600
38
    }
601
896
    else if (_varDecl.isConstant())
602
0
    {
603
0
      solAssert(paramTypes.empty(), "");
604
0
      return Whiskers(R"(
605
0
        <astIDComment><sourceLocationComment>
606
0
        function <functionName>() -> <ret> {
607
0
          <ret> := <constantValueFunction>()
608
0
        }
609
0
        <contractSourceLocationComment>
610
0
      )")
611
0
      (
612
0
        "astIDComment",
613
0
        m_context.debugInfoSelection().astID ?
614
0
          "/// @ast-id " + std::to_string(_varDecl.id()) + "\n" :
615
0
          ""
616
0
      )
617
0
      ("sourceLocationComment", dispenseLocationComment(_varDecl))
618
0
      (
619
0
        "contractSourceLocationComment",
620
0
        dispenseLocationComment(m_context.mostDerivedContract())
621
0
      )
622
0
      ("functionName", functionName)
623
0
      ("constantValueFunction", IRGeneratorForStatements(m_context, m_utils, m_optimiserSettings).constantValueFunction(_varDecl))
624
0
      ("ret", suffixedVariableNameList("ret_", 0, _varDecl.type()->sizeOnStack()))
625
0
      .render();
626
0
    }
627
628
896
    std::string code;
629
630
896
    auto const& location = m_context.storageLocationOfStateVariable(_varDecl);
631
896
    code += Whiskers(R"(
632
896
      let slot := <slot>
633
896
      let offset := <offset>
634
896
    )")
635
896
    ("slot", location.first.str())
636
896
    ("offset", std::to_string(location.second))
637
896
    .render();
638
639
896
    if (!paramTypes.empty())
640
896
      solAssert(
641
896
        location.second == 0,
642
896
        "If there are parameters, we are dealing with structs or mappings and thus should have offset zero."
643
896
      );
644
645
    // The code of an accessor is of the form `x[a][b][c]` (it is slightly more complicated
646
    // if the final type is a struct).
647
    // In each iteration of the loop below, we consume one parameter, perform an
648
    // index access, reassign the yul variable `slot` and move @a currentType further "down".
649
    // The initial value of @a currentType is only used if we skip the loop completely.
650
896
    Type const* currentType = _varDecl.annotation().type;
651
652
896
    std::vector<std::string> parameters;
653
896
    std::vector<std::string> returnVariables;
654
655
1.31k
    for (size_t i = 0; i < paramTypes.size(); ++i)
656
417
    {
657
417
      MappingType const* mappingType = dynamic_cast<MappingType const*>(currentType);
658
417
      ArrayType const* arrayType = dynamic_cast<ArrayType const*>(currentType);
659
417
      solAssert(mappingType || arrayType, "");
660
661
417
      std::vector<std::string> keys = IRVariable("key_" + std::to_string(i),
662
417
        mappingType ? *mappingType->keyType() : *TypeProvider::uint256()
663
417
      ).stackSlots();
664
417
      parameters += keys;
665
666
417
      Whiskers templ(R"(
667
417
        <?array>
668
417
          if iszero(lt(<keys>, <length>(slot))) { revert(0, 0) }
669
417
        </array>
670
417
        slot<?array>, offset</array> := <indexAccess>(slot<?+keys>, <keys></+keys>)
671
417
      )");
672
417
      templ(
673
417
        "indexAccess",
674
417
        mappingType ?
675
88
        m_utils.mappingIndexAccessFunction(*mappingType, *mappingType->keyType()) :
676
417
        m_utils.storageArrayIndexAccessFunction(*arrayType)
677
417
      )
678
417
      ("array", arrayType != nullptr)
679
417
      ("keys", joinHumanReadable(keys));
680
417
      if (arrayType)
681
329
        templ("length", m_utils.arrayLengthFunction(*arrayType));
682
683
417
      code += templ.render();
684
685
417
      currentType = mappingType ? mappingType->valueType() : arrayType->baseType();
686
417
    }
687
688
896
    auto returnTypes = accessorType.returnParameterTypes();
689
896
    solAssert(returnTypes.size() >= 1, "");
690
896
    if (StructType const* structType = dynamic_cast<StructType const*>(currentType))
691
6
    {
692
6
      solAssert(location.second == 0, "");
693
6
      auto const& names = accessorType.returnParameterNames();
694
24
      for (size_t i = 0; i < names.size(); ++i)
695
18
      {
696
18
        if (returnTypes[i]->category() == Type::Category::Mapping)
697
0
          continue;
698
18
        if (
699
18
          auto const* arrayType = dynamic_cast<ArrayType const*>(returnTypes[i]);
700
18
          arrayType && !arrayType->isByteArrayOrString()
701
18
        )
702
0
          continue;
703
704
18
        std::pair<u256, unsigned> const& offsets = structType->storageOffsetsOfMember(names[i]);
705
18
        std::vector<std::string> retVars = IRVariable("ret_" + std::to_string(returnVariables.size()), *returnTypes[i]).stackSlots();
706
18
        returnVariables += retVars;
707
18
        code += Whiskers(R"(
708
18
          <ret> := <readStorage>(add(slot, <slotOffset>))
709
18
        )")
710
18
        ("ret", joinHumanReadable(retVars))
711
18
        ("readStorage", m_utils.readFromStorage(*returnTypes[i], offsets.second, true, _varDecl.referenceLocation()))
712
18
        ("slotOffset", offsets.first.str())
713
18
        .render();
714
18
      }
715
6
    }
716
890
    else
717
890
    {
718
890
      solAssert(returnTypes.size() == 1, "");
719
890
      auto const* arrayType = dynamic_cast<ArrayType const*>(returnTypes.front());
720
890
      if (arrayType)
721
890
        solAssert(arrayType->isByteArrayOrString(), "");
722
890
      std::vector<std::string> retVars = IRVariable("ret", *returnTypes.front()).stackSlots();
723
890
      returnVariables += retVars;
724
890
      code += Whiskers(R"(
725
890
        <ret> := <readStorage>(slot, offset)
726
890
      )")
727
890
      ("ret", joinHumanReadable(retVars))
728
890
      ("readStorage", m_utils.readFromStorageDynamic(*returnTypes.front(), true, _varDecl.referenceLocation()))
729
890
      .render();
730
890
    }
731
732
896
    return Whiskers(R"(
733
896
      <astIDComment><sourceLocationComment>
734
896
      function <functionName>(<params>) -> <retVariables> {
735
896
        <code>
736
896
      }
737
896
      <contractSourceLocationComment>
738
896
    )")
739
896
    ("functionName", functionName)
740
896
    ("params", joinHumanReadable(parameters))
741
896
    ("retVariables", joinHumanReadable(returnVariables))
742
896
    ("code", std::move(code))
743
896
    (
744
896
      "astIDComment",
745
896
      m_context.debugInfoSelection().astID ?
746
896
        "/// @ast-id " + std::to_string(_varDecl.id()) + "\n" :
747
896
        ""
748
896
    )
749
896
    ("sourceLocationComment", dispenseLocationComment(_varDecl))
750
896
    (
751
896
      "contractSourceLocationComment",
752
896
      dispenseLocationComment(m_context.mostDerivedContract())
753
896
    )
754
896
    .render();
755
896
  });
756
934
}
757
758
std::string IRGenerator::generateExternalFunction(ContractDefinition const& _contract, FunctionType const& _functionType)
759
4.02k
{
760
4.02k
  std::string functionName = IRNames::externalFunctionABIWrapper(_functionType.declaration());
761
4.02k
  return m_context.functionCollector().createFunction(functionName, [&](std::vector<std::string>&, std::vector<std::string>&) -> std::string {
762
4.02k
    Whiskers t(R"X(
763
4.02k
      <callValueCheck>
764
4.02k
      <?+params>let <params> := </+params> <abiDecode>(4, calldatasize())
765
4.02k
      <?+retParams>let <retParams> := </+retParams> <function>(<params>)
766
4.02k
      let memPos := <allocateUnbounded>()
767
4.02k
      let memEnd := <abiEncode>(memPos <?+retParams>,</+retParams> <retParams>)
768
4.02k
      return(memPos, sub(memEnd, memPos))
769
4.02k
    )X");
770
4.02k
    t("callValueCheck", (_functionType.isPayable() || _contract.isLibrary()) ? "" : callValueCheck());
771
772
4.02k
    unsigned paramVars = std::make_shared<TupleType>(_functionType.parameterTypes())->sizeOnStack();
773
4.02k
    unsigned retVars = std::make_shared<TupleType>(_functionType.returnParameterTypes())->sizeOnStack();
774
775
4.02k
    ABIFunctions abiFunctions(m_evmVersion, m_eofVersion, m_context.revertStrings(), m_context.functionCollector());
776
4.02k
    t("abiDecode", abiFunctions.tupleDecoder(_functionType.parameterTypes()));
777
4.02k
    t("params",  suffixedVariableNameList("param_", 0, paramVars));
778
4.02k
    t("retParams",  suffixedVariableNameList("ret_", 0, retVars));
779
780
4.02k
    if (FunctionDefinition const* funDef = dynamic_cast<FunctionDefinition const*>(&_functionType.declaration()))
781
3.09k
    {
782
3.09k
      solAssert(!funDef->isConstructor());
783
3.09k
      t("function", m_context.enqueueFunctionForCodeGeneration(*funDef));
784
3.09k
    }
785
936
    else if (VariableDeclaration const* varDecl = dynamic_cast<VariableDeclaration const*>(&_functionType.declaration()))
786
934
      t("function", generateGetter(*varDecl));
787
2
    else
788
936
      solAssert(false, "Unexpected declaration for function!");
789
790
4.02k
    t("allocateUnbounded", m_utils.allocateUnboundedFunction());
791
4.02k
    t("abiEncode", abiFunctions.tupleEncoder(_functionType.returnParameterTypes(), _functionType.returnParameterTypes(), _contract.isLibrary()));
792
4.02k
    return t.render();
793
4.02k
  });
794
4.02k
}
795
796
std::string IRGenerator::generateInitialAssignment(VariableDeclaration const& _varDecl)
797
1.96k
{
798
1.96k
  IRGeneratorForStatements generator(m_context, m_utils, m_optimiserSettings);
799
1.96k
  generator.initializeLocalVar(_varDecl);
800
1.96k
  return generator.code();
801
1.96k
}
802
803
std::pair<std::string, std::map<ContractDefinition const*, std::vector<std::string>>> IRGenerator::evaluateConstructorArguments(
804
  ContractDefinition const& _contract
805
)
806
4.07k
{
807
4.07k
  struct InheritanceOrder
808
4.07k
  {
809
4.07k
    bool operator()(ContractDefinition const* _c1, ContractDefinition const* _c2) const
810
4.07k
    {
811
118
      solAssert(util::contains(linearizedBaseContracts, _c1) && util::contains(linearizedBaseContracts, _c2), "");
812
118
      auto it1 = find(linearizedBaseContracts.begin(), linearizedBaseContracts.end(), _c1);
813
118
      auto it2 = find(linearizedBaseContracts.begin(), linearizedBaseContracts.end(), _c2);
814
118
      return it1 < it2;
815
118
    }
816
4.07k
    std::vector<ContractDefinition const*> const& linearizedBaseContracts;
817
4.07k
  } inheritanceOrder{_contract.annotation().linearizedBaseContracts};
818
819
4.07k
  std::map<ContractDefinition const*, std::vector<std::string>> constructorParams;
820
821
4.07k
  std::map<ContractDefinition const*, std::vector<ASTPointer<Expression>>const *, InheritanceOrder>
822
4.07k
    baseConstructorArguments(inheritanceOrder);
823
824
4.07k
  for (ASTPointer<InheritanceSpecifier> const& base: _contract.baseContracts())
825
131
    if (FunctionDefinition const* baseConstructor = dynamic_cast<ContractDefinition const*>(
826
131
        base->name().annotation().referencedDeclaration
827
131
    )->constructor(); baseConstructor && base->arguments())
828
131
      solAssert(baseConstructorArguments.emplace(
829
4.07k
        dynamic_cast<ContractDefinition const*>(baseConstructor->scope()),
830
4.07k
        base->arguments()
831
4.07k
      ).second, "");
832
833
4.07k
  if (FunctionDefinition const* constructor = _contract.constructor())
834
595
    for (ASTPointer<ModifierInvocation> const& modifier: constructor->modifiers())
835
94
      if (auto const* baseContract = dynamic_cast<ContractDefinition const*>(
836
94
        modifier->name().annotation().referencedDeclaration
837
94
      ))
838
92
        if (
839
92
          FunctionDefinition const* baseConstructor = baseContract->constructor();
840
92
          baseConstructor && modifier->arguments()
841
92
        )
842
92
          solAssert(baseConstructorArguments.emplace(
843
4.07k
            dynamic_cast<ContractDefinition const*>(baseConstructor->scope()),
844
4.07k
            modifier->arguments()
845
4.07k
          ).second, "");
846
847
4.07k
  IRGeneratorForStatements generator{m_context, m_utils, m_optimiserSettings};
848
4.07k
  for (auto&& [baseContract, arguments]: baseConstructorArguments)
849
90
  {
850
90
    solAssert(baseContract && arguments, "");
851
90
    if (baseContract->constructor() && !arguments->empty())
852
90
    {
853
90
      std::vector<std::string> params;
854
191
      for (size_t i = 0; i < arguments->size(); ++i)
855
101
        params += generator.evaluateExpression(
856
101
          *(arguments->at(i)),
857
101
          *(baseContract->constructor()->parameters()[i]->type())
858
101
        ).stackSlots();
859
90
      constructorParams[baseContract] = std::move(params);
860
90
    }
861
90
  }
862
863
4.07k
  return {generator.code(), constructorParams};
864
4.07k
}
865
866
std::string IRGenerator::initStateVariables(ContractDefinition const& _contract)
867
4.07k
{
868
4.07k
  IRGeneratorForStatements generator{m_context, m_utils, m_optimiserSettings};
869
4.07k
  for (VariableDeclaration const* variable: _contract.stateVariables())
870
1.92k
  {
871
1.92k
    if (!variable->isConstant())
872
1.89k
      generator.initializeStateVar(*variable);
873
1.92k
  }
874
875
4.07k
  return generator.code();
876
4.07k
}
877
878
879
void IRGenerator::generateConstructors(ContractDefinition const& _contract)
880
3.94k
{
881
3.94k
  auto listAllParams =
882
3.94k
    [&](std::map<ContractDefinition const*, std::vector<std::string>> const& baseParams) -> std::vector<std::string>
883
4.20k
    {
884
4.20k
      std::vector<std::string> params;
885
4.20k
      for (ContractDefinition const* contract: _contract.annotation().linearizedBaseContracts)
886
4.91k
        if (baseParams.count(contract))
887
250
          params += baseParams.at(contract);
888
4.20k
      return params;
889
4.20k
    };
890
891
3.94k
  std::map<ContractDefinition const*, std::vector<std::string>> baseConstructorParams;
892
8.01k
  for (size_t i = 0; i < _contract.annotation().linearizedBaseContracts.size(); ++i)
893
4.07k
  {
894
4.07k
    ContractDefinition const* contract = _contract.annotation().linearizedBaseContracts[i];
895
4.07k
    baseConstructorParams.erase(contract);
896
897
4.07k
    m_context.resetLocalVariables();
898
4.07k
    m_context.functionCollector().createFunction(IRNames::constructor(*contract), [&]() {
899
4.07k
      Whiskers t(R"(
900
4.07k
        <astIDComment><sourceLocationComment>
901
4.07k
        function <functionName>(<params><comma><baseParams>) {
902
4.07k
          <evalBaseArguments>
903
4.07k
          <sourceLocationComment>
904
4.07k
          <?hasNextConstructor> <nextConstructor>(<nextParams>) </hasNextConstructor>
905
4.07k
          <initStateVariables>
906
4.07k
          <userDefinedConstructorBody>
907
4.07k
        }
908
4.07k
        <contractSourceLocationComment>
909
4.07k
      )");
910
4.07k
      std::vector<std::string> params;
911
4.07k
      if (contract->constructor())
912
595
        for (ASTPointer<VariableDeclaration> const& varDecl: contract->constructor()->parameters())
913
270
          params += m_context.addLocalVariable(*varDecl).stackSlots();
914
915
4.07k
      if (m_context.debugInfoSelection().astID && contract->constructor())
916
595
        t("astIDComment", "/// @ast-id " + std::to_string(contract->constructor()->id()) + "\n");
917
3.47k
      else
918
3.47k
        t("astIDComment", "");
919
4.07k
      t("sourceLocationComment", dispenseLocationComment(
920
4.07k
        contract->constructor() ?
921
595
        dynamic_cast<ASTNode const&>(*contract->constructor()) :
922
4.07k
        dynamic_cast<ASTNode const&>(*contract)
923
4.07k
      ));
924
4.07k
      t(
925
4.07k
        "contractSourceLocationComment",
926
4.07k
        dispenseLocationComment(m_context.mostDerivedContract())
927
4.07k
      );
928
929
4.07k
      t("params", joinHumanReadable(params));
930
4.07k
      std::vector<std::string> baseParams = listAllParams(baseConstructorParams);
931
4.07k
      t("baseParams", joinHumanReadable(baseParams));
932
4.07k
      t("comma", !params.empty() && !baseParams.empty() ? ", " : "");
933
4.07k
      t("functionName", IRNames::constructor(*contract));
934
4.07k
      std::pair<std::string, std::map<ContractDefinition const*, std::vector<std::string>>> evaluatedArgs = evaluateConstructorArguments(*contract);
935
4.07k
      baseConstructorParams.insert(evaluatedArgs.second.begin(), evaluatedArgs.second.end());
936
4.07k
      t("evalBaseArguments", evaluatedArgs.first);
937
4.07k
      if (i < _contract.annotation().linearizedBaseContracts.size() - 1)
938
130
      {
939
130
        t("hasNextConstructor", true);
940
130
        ContractDefinition const* nextContract = _contract.annotation().linearizedBaseContracts[i + 1];
941
130
        t("nextConstructor", IRNames::constructor(*nextContract));
942
130
        t("nextParams", joinHumanReadable(listAllParams(baseConstructorParams)));
943
130
      }
944
3.94k
      else
945
3.94k
        t("hasNextConstructor", false);
946
4.07k
      t("initStateVariables", initStateVariables(*contract));
947
4.07k
      std::string body;
948
4.07k
      if (FunctionDefinition const* constructor = contract->constructor())
949
595
      {
950
595
        std::vector<ModifierInvocation*> realModifiers;
951
595
        for (auto const& modifierInvocation: constructor->modifiers())
952
          // Filter out the base constructor calls
953
94
          if (dynamic_cast<ModifierDefinition const*>(modifierInvocation->name().annotation().referencedDeclaration))
954
2
            realModifiers.emplace_back(modifierInvocation.get());
955
595
        if (realModifiers.empty())
956
593
          body = generate(constructor->body());
957
2
        else
958
2
        {
959
4
          for (size_t i = 0; i < realModifiers.size(); ++i)
960
2
          {
961
2
            ModifierInvocation const& modifier = *realModifiers.at(i);
962
2
            std::string next =
963
2
              i + 1 < realModifiers.size() ?
964
0
              IRNames::modifierInvocation(*realModifiers.at(i + 1)) :
965
2
              IRNames::functionWithModifierInner(*constructor);
966
2
            generateModifier(modifier, *constructor, next);
967
2
          }
968
2
          body =
969
2
            IRNames::modifierInvocation(*realModifiers.at(0)) +
970
2
            "(" +
971
2
            joinHumanReadable(params) +
972
2
            ")";
973
          // Now generate the actual inner function.
974
2
          generateFunctionWithModifierInner(*constructor);
975
2
        }
976
595
      }
977
4.07k
      t("userDefinedConstructorBody", std::move(body));
978
979
4.07k
      return t.render();
980
4.07k
    });
981
4.07k
  }
982
3.94k
}
983
984
std::string IRGenerator::deployCode(ContractDefinition const& _contract)
985
3.94k
{
986
3.94k
  Whiskers t(R"X(
987
3.94k
    <?eof>
988
3.94k
      <?library>
989
3.94k
        mstore(<libraryAddressImmutableOffset>, address())
990
3.94k
      </library>
991
3.94k
      returncontract("<object>", <auxDataStart>, <auxDataSize>)
992
3.94k
    <!eof>
993
3.94k
      let <codeOffset> := <allocateUnbounded>()
994
3.94k
      codecopy(<codeOffset>, dataoffset("<object>"), datasize("<object>"))
995
3.94k
      <#immutables>
996
3.94k
        setimmutable(<codeOffset>, "<immutableName>", <value>)
997
3.94k
      </immutables>
998
3.94k
      return(<codeOffset>, datasize("<object>"))
999
3.94k
    </eof>
1000
3.94k
  )X");
1001
3.94k
  auto const eof = m_context.eofVersion().has_value();
1002
3.94k
  t("eof", eof);
1003
3.94k
  t("allocateUnbounded", m_utils.allocateUnboundedFunction());
1004
3.94k
  t("codeOffset", m_context.newYulVariable());
1005
3.94k
  t("object", IRNames::deployedObject(_contract));
1006
1007
3.94k
  std::vector<std::map<std::string, std::string>> immutables;
1008
3.94k
  if (_contract.isLibrary())
1009
33
  {
1010
33
    solAssert(ContractType(_contract).immutableVariables().empty(), "");
1011
33
    if (!eof)
1012
33
      immutables.emplace_back(std::map<std::string, std::string>{
1013
33
        {"immutableName"s, IRNames::libraryAddressImmutable()},
1014
33
        {"value"s, "address()"}
1015
33
      });
1016
0
    else
1017
0
      t("libraryAddressImmutableOffset", std::to_string(m_context.libraryAddressImmutableOffset()));
1018
33
  }
1019
3.91k
  else
1020
3.91k
  {
1021
3.91k
    for (VariableDeclaration const* immutable: ContractType(_contract).immutableVariables())
1022
90
    {
1023
90
      solUnimplementedAssert(immutable->type()->isValueType());
1024
90
      solUnimplementedAssert(immutable->type()->sizeOnStack() == 1);
1025
90
      if (!eof)
1026
90
        immutables.emplace_back(std::map<std::string, std::string>{
1027
90
          {"immutableName"s, std::to_string(immutable->id())},
1028
90
          {"value"s, "mload(" + std::to_string(m_context.immutableMemoryOffset(*immutable)) + ")"}
1029
90
        });
1030
90
    }
1031
3.91k
  }
1032
1033
3.94k
  if (eof)
1034
0
  {
1035
0
    t("library", _contract.isLibrary());
1036
0
    t("auxDataStart", std::to_string(CompilerUtils::generalPurposeMemoryStart));
1037
0
    solAssert(m_context.reservedMemorySize() <= 0xFFFF, "Reserved memory size exceeded maximum allowed EOF data section size.");
1038
0
    t("auxDataSize", std::to_string(m_context.reservedMemorySize()));
1039
0
  }
1040
3.94k
  else
1041
3.94k
    t("immutables", std::move(immutables));
1042
1043
3.94k
  return t.render();
1044
3.94k
}
1045
1046
std::string IRGenerator::callValueCheck()
1047
7.85k
{
1048
7.85k
  return "if callvalue() { " + m_utils.revertReasonIfDebugFunction("Ether sent to non-payable function") + "() }";
1049
7.85k
}
1050
1051
std::string IRGenerator::dispatchRoutine(ContractDefinition const& _contract)
1052
3.94k
{
1053
3.94k
  Whiskers t(R"X(
1054
3.94k
    <?+cases>if iszero(lt(calldatasize(), 4))
1055
3.94k
    {
1056
3.94k
      let selector := <shr224>(calldataload(0))
1057
3.94k
      switch selector
1058
3.94k
      <#cases>
1059
3.94k
      case <functionSelector>
1060
3.94k
      {
1061
3.94k
        // <functionName>
1062
3.94k
        <delegatecallCheck>
1063
3.94k
        <externalFunction>()
1064
3.94k
      }
1065
3.94k
      </cases>
1066
3.94k
      default {}
1067
3.94k
    }</+cases>
1068
3.94k
    <?+receiveEther>if iszero(calldatasize()) { <receiveEther> }</+receiveEther>
1069
3.94k
    <fallback>
1070
3.94k
  )X");
1071
3.94k
  t("shr224", m_utils.shiftRightFunction(224));
1072
3.94k
  std::vector<std::map<std::string, std::string>> functions;
1073
3.94k
  for (auto const& function: _contract.interfaceFunctions())
1074
4.02k
  {
1075
4.02k
    functions.emplace_back();
1076
4.02k
    std::map<std::string, std::string>& templ = functions.back();
1077
4.02k
    templ["functionSelector"] = "0x" + function.first.hex();
1078
4.02k
    FunctionTypePointer const& type = function.second;
1079
4.02k
    templ["functionName"] = type->externalSignature();
1080
4.02k
    std::string delegatecallCheck;
1081
4.02k
    if (_contract.isLibrary())
1082
27
    {
1083
27
      solAssert(!type->isPayable(), "");
1084
27
      if (type->stateMutability() > StateMutability::View)
1085
        // If the function is not a view function and is called without DELEGATECALL,
1086
        // we revert.
1087
26
        delegatecallCheck =
1088
26
          "if iszero(called_via_delegatecall) { " +
1089
26
          m_utils.revertReasonIfDebugFunction("Non-view function of library called without DELEGATECALL") +
1090
26
          "() }";
1091
27
    }
1092
4.02k
    templ["delegatecallCheck"] = delegatecallCheck;
1093
1094
4.02k
    templ["externalFunction"] = generateExternalFunction(_contract, *type);
1095
4.02k
  }
1096
3.94k
  t("cases", functions);
1097
3.94k
  FunctionDefinition const* etherReceiver = _contract.receiveFunction();
1098
3.94k
  if (etherReceiver)
1099
13
  {
1100
13
    solAssert(!_contract.isLibrary(), "");
1101
13
    t("receiveEther", m_context.enqueueFunctionForCodeGeneration(*etherReceiver) + "() stop()");
1102
13
  }
1103
3.93k
  else
1104
3.93k
    t("receiveEther", "");
1105
3.94k
  if (FunctionDefinition const* fallback = _contract.fallbackFunction())
1106
18
  {
1107
18
    solAssert(!_contract.isLibrary(), "");
1108
18
    std::string fallbackCode;
1109
18
    if (!fallback->isPayable())
1110
13
      fallbackCode += callValueCheck() + "\n";
1111
18
    if (fallback->parameters().empty())
1112
13
      fallbackCode += m_context.enqueueFunctionForCodeGeneration(*fallback) + "() stop()";
1113
5
    else
1114
5
    {
1115
5
      solAssert(fallback->parameters().size() == 1 && fallback->returnParameters().size() == 1, "");
1116
5
      fallbackCode += "let retval := " + m_context.enqueueFunctionForCodeGeneration(*fallback) + "(0, calldatasize())\n";
1117
5
      fallbackCode += "return(add(retval, 0x20), mload(retval))\n";
1118
1119
5
    }
1120
1121
18
    t("fallback", fallbackCode);
1122
18
  }
1123
3.92k
  else
1124
3.92k
    t("fallback", (
1125
3.92k
      etherReceiver ?
1126
11
      m_utils.revertReasonIfDebugFunction("Unknown signature and no fallback defined") :
1127
3.92k
      m_utils.revertReasonIfDebugFunction("Contract does not have fallback nor receive functions")
1128
3.92k
    ) + "()");
1129
3.94k
  return t.render();
1130
3.94k
}
1131
1132
std::string IRGenerator::memoryInit(bool _useMemoryGuard)
1133
7.88k
{
1134
  // This function should be called at the beginning of the EVM call frame
1135
  // and thus can assume all memory to be zero, including the contents of
1136
  // the "zero memory area" (the position CompilerUtils::zeroPointer points to).
1137
7.88k
  return
1138
7.88k
    Whiskers{
1139
7.88k
      _useMemoryGuard ?
1140
7.82k
      "mstore(<memPtr>, memoryguard(<freeMemoryStart>))" :
1141
7.88k
      "mstore(<memPtr>, <freeMemoryStart>)"
1142
7.88k
    }
1143
7.88k
    ("memPtr", std::to_string(CompilerUtils::freeMemoryPointer))
1144
7.88k
    (
1145
7.88k
      "freeMemoryStart",
1146
7.88k
      std::to_string(CompilerUtils::generalPurposeMemoryStart + m_context.reservedMemory())
1147
7.88k
    ).render();
1148
7.88k
}
1149
1150
void IRGenerator::resetContext(ContractDefinition const& _contract, ExecutionContext _context)
1151
7.88k
{
1152
7.88k
  solAssert(
1153
7.88k
    m_context.functionGenerationQueueEmpty(),
1154
7.88k
    "Reset function generation queue while it still had functions."
1155
7.88k
  );
1156
7.88k
  solAssert(
1157
7.88k
    m_context.functionCollector().requestedFunctions().empty(),
1158
7.88k
    "Reset context while it still had functions."
1159
7.88k
  );
1160
7.88k
  solAssert(
1161
7.88k
    m_context.internalDispatchClean(),
1162
7.88k
    "Reset internal dispatch map without consuming it."
1163
7.88k
  );
1164
7.88k
  IRGenerationContext newContext(
1165
7.88k
    m_evmVersion,
1166
7.88k
    m_eofVersion,
1167
7.88k
    _context,
1168
7.88k
    m_context.revertStrings(),
1169
7.88k
    m_context.sourceIndices(),
1170
7.88k
    m_context.debugInfoSelection(),
1171
7.88k
    m_context.soliditySourceProvider()
1172
7.88k
  );
1173
1174
7.88k
  m_context = std::move(newContext);
1175
1176
7.88k
  m_context.setMostDerivedContract(_contract);
1177
7.88k
  for (auto const location: {DataLocation::Storage, DataLocation::Transient})
1178
15.7k
    for (auto const& var: ContractType(_contract).linearizedStateVariables(location))
1179
3.60k
      m_context.addStateVariable(*std::get<0>(var), std::get<1>(var), std::get<2>(var));
1180
7.88k
}
1181
1182
std::string IRGenerator::dispenseLocationComment(ASTNode const& _node)
1183
24.7k
{
1184
24.7k
  return ::dispenseLocationComment(_node, m_context);
1185
24.7k
}