Coverage Report

Created: 2026-06-30 07:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/solidity/libsolidity/codegen/CompilerContext.cpp
Line
Count
Source
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 Christian <c@ethdev.com>
20
 * @date 2014
21
 * Utilities for the solidity compiler.
22
 */
23
24
#include <libsolidity/codegen/CompilerContext.h>
25
26
#include <libsolidity/ast/AST.h>
27
#include <libsolidity/codegen/Compiler.h>
28
#include <libsolidity/codegen/CompilerUtils.h>
29
#include <libsolidity/interface/Version.h>
30
31
#include <libyul/AST.h>
32
#include <libyul/AsmParser.h>
33
#include <libyul/AsmPrinter.h>
34
#include <libyul/AsmAnalysis.h>
35
#include <libyul/AsmAnalysisInfo.h>
36
#include <libyul/backends/evm/AsmCodeGen.h>
37
#include <libyul/backends/evm/EVMDialect.h>
38
#include <libyul/backends/evm/EVMMetrics.h>
39
#include <libyul/optimiser/Suite.h>
40
#include <libyul/Object.h>
41
#include <libyul/YulName.h>
42
#include <libyul/Utilities.h>
43
44
#include <libsolutil/Whiskers.h>
45
#include <libsolutil/FunctionSelector.h>
46
#include <libsolutil/StackTooDeepString.h>
47
48
#include <liblangutil/ErrorReporter.h>
49
#include <liblangutil/Scanner.h>
50
#include <liblangutil/SourceReferenceFormatter.h>
51
52
#include <utility>
53
54
// Change to "define" to output all intermediate code
55
#undef SOL_OUTPUT_ASM
56
57
58
using namespace solidity;
59
using namespace solidity::util;
60
using namespace solidity::evmasm;
61
using namespace solidity::frontend;
62
using namespace solidity::langutil;
63
64
void CompilerContext::addStateVariable(
65
  VariableDeclaration const& _declaration,
66
  u256 const& _storageOffset,
67
  unsigned _byteOffset
68
)
69
11.2k
{
70
11.2k
  m_stateVariables[&_declaration] = std::make_pair(_storageOffset, _byteOffset);
71
11.2k
}
72
73
void CompilerContext::addImmutable(VariableDeclaration const& _variable)
74
312
{
75
312
  solAssert(_variable.immutable(), "Attempted to register a non-immutable variable as immutable.");
76
312
  solUnimplementedAssert(_variable.annotation().type->isValueType(), "Only immutable variables of value type are supported.");
77
312
  solAssert(m_runtimeContext, "Attempted to register an immutable variable for runtime code generation.");
78
312
  m_immutableVariables[&_variable] = CompilerUtils::generalPurposeMemoryStart + *m_reservedMemory;
79
312
  solAssert(_variable.annotation().type->memoryHeadSize() == 32, "Memory writes might overlap.");
80
312
  *m_reservedMemory += _variable.annotation().type->memoryHeadSize();
81
312
}
82
83
size_t CompilerContext::immutableMemoryOffset(VariableDeclaration const& _variable) const
84
813
{
85
813
  solAssert(m_immutableVariables.count(&_variable), "Memory offset of unknown immutable queried.");
86
813
  solAssert(m_runtimeContext, "Attempted to fetch the memory offset of an immutable variable during runtime code generation.");
87
813
  return m_immutableVariables.at(&_variable);
88
813
}
89
90
std::vector<std::string> CompilerContext::immutableVariableSlotNames(VariableDeclaration const& _variable)
91
443
{
92
443
  std::string baseName = std::to_string(_variable.id());
93
443
  solAssert(_variable.annotation().type->sizeOnStack() > 0, "");
94
443
  if (_variable.annotation().type->sizeOnStack() == 1)
95
443
    return {baseName};
96
0
  std::vector<std::string> names;
97
0
  auto collectSlotNames = [&](std::string const& _baseName, Type const* type, auto const& _recurse) -> void {
98
0
    for (auto const& [slot, type]: type->stackItems())
99
0
      if (type)
100
0
        _recurse(_baseName + " " + slot, type, _recurse);
101
0
      else
102
0
        names.emplace_back(_baseName);
103
0
  };
104
0
  collectSlotNames(baseName, _variable.annotation().type, collectSlotNames);
105
0
  return names;
106
443
}
107
108
size_t CompilerContext::reservedMemory()
109
21.4k
{
110
21.4k
  solAssert(m_reservedMemory.has_value(), "Reserved memory was used before ");
111
21.4k
  size_t reservedMemory = *m_reservedMemory;
112
21.4k
  m_reservedMemory = std::nullopt;
113
21.4k
  return reservedMemory;
114
21.4k
}
115
116
void CompilerContext::startFunction(Declaration const& _function)
117
15.4k
{
118
15.4k
  m_functionCompilationQueue.startFunction(_function);
119
15.4k
  *this << functionEntryLabel(_function);
120
15.4k
}
121
122
void CompilerContext::callLowLevelFunction(
123
  std::string const& _name,
124
  unsigned _inArgs,
125
  unsigned _outArgs,
126
  std::function<void(CompilerContext&)> const& _generator
127
)
128
4.04k
{
129
4.04k
  evmasm::AssemblyItem retTag = pushNewTag();
130
4.04k
  CompilerUtils(*this).moveIntoStack(_inArgs);
131
132
4.04k
  *this << lowLevelFunctionTag(_name, _inArgs, _outArgs, _generator);
133
134
4.04k
  appendJump(evmasm::AssemblyItem::JumpType::IntoFunction);
135
4.04k
  adjustStackOffset(static_cast<int>(_outArgs) - 1 - static_cast<int>(_inArgs));
136
4.04k
  *this << retTag.tag();
137
4.04k
}
138
139
void CompilerContext::callYulFunction(
140
  std::string const& _name,
141
  unsigned _inArgs,
142
  unsigned _outArgs
143
)
144
160k
{
145
160k
  m_externallyUsedYulFunctions.insert(_name);
146
160k
  auto const retTag = pushNewTag();
147
160k
  CompilerUtils(*this).moveIntoStack(_inArgs);
148
160k
  appendJumpTo(namedTag(_name, _inArgs, _outArgs, {}), evmasm::AssemblyItem::JumpType::IntoFunction);
149
160k
  adjustStackOffset(static_cast<int>(_outArgs) - 1 - static_cast<int>(_inArgs));
150
160k
  *this << retTag.tag();
151
160k
}
152
153
evmasm::AssemblyItem CompilerContext::lowLevelFunctionTag(
154
  std::string const& _name,
155
  unsigned _inArgs,
156
  unsigned _outArgs,
157
  std::function<void(CompilerContext&)> const& _generator
158
)
159
4.24k
{
160
4.24k
  auto it = m_lowLevelFunctions.find(_name);
161
4.24k
  if (it == m_lowLevelFunctions.end())
162
3.76k
  {
163
3.76k
    evmasm::AssemblyItem tag = newTag().pushTag();
164
3.76k
    m_lowLevelFunctions.insert(make_pair(_name, tag));
165
3.76k
    m_lowLevelFunctionGenerationQueue.push(make_tuple(_name, _inArgs, _outArgs, _generator));
166
3.76k
    return tag;
167
3.76k
  }
168
482
  else
169
482
    return it->second;
170
4.24k
}
171
172
void CompilerContext::appendMissingLowLevelFunctions()
173
21.6k
{
174
25.3k
  while (!m_lowLevelFunctionGenerationQueue.empty())
175
3.76k
  {
176
3.76k
    std::string name;
177
3.76k
    unsigned inArgs;
178
3.76k
    unsigned outArgs;
179
3.76k
    std::function<void(CompilerContext&)> generator;
180
3.76k
    tie(name, inArgs, outArgs, generator) = m_lowLevelFunctionGenerationQueue.front();
181
3.76k
    m_lowLevelFunctionGenerationQueue.pop();
182
183
3.76k
    setStackOffset(static_cast<int>(inArgs) + 1);
184
3.76k
    *this << m_lowLevelFunctions.at(name).tag();
185
3.76k
    generator(*this);
186
3.76k
    CompilerUtils(*this).moveToStackTop(outArgs);
187
3.76k
    appendJump(evmasm::AssemblyItem::JumpType::OutOfFunction);
188
3.76k
    solAssert(stackHeight() == outArgs, "Invalid stack height in low-level function " + name + ".");
189
3.76k
  }
190
21.6k
}
191
192
void CompilerContext::appendYulUtilityFunctions(OptimiserSettings const& _optimiserSettings)
193
21.6k
{
194
21.6k
  solAssert(!m_appendYulUtilityFunctionsRan, "requestedYulFunctions called more than once.");
195
21.6k
  m_appendYulUtilityFunctionsRan = true;
196
197
21.6k
  std::string code = m_yulFunctionCollector.requestedFunctions();
198
21.6k
  if (!code.empty())
199
7.59k
  {
200
7.59k
    appendInlineAssembly(
201
7.59k
      yul::reindent("{\n" + std::move(code) + "\n}"),
202
7.59k
      {},
203
7.59k
      m_externallyUsedYulFunctions,
204
7.59k
      true,
205
7.59k
      _optimiserSettings,
206
7.59k
      yulUtilityFileName()
207
7.59k
    );
208
7.59k
    solAssert(!m_generatedYulUtilityCode.empty(), "");
209
7.59k
  }
210
21.6k
}
211
212
void CompilerContext::addVariable(
213
  VariableDeclaration const& _declaration,
214
  unsigned _offsetToCurrent
215
)
216
26.2k
{
217
26.2k
  solAssert(m_asm->deposit() >= 0 && unsigned(m_asm->deposit()) >= _offsetToCurrent, "");
218
26.2k
  unsigned sizeOnStack = _declaration.annotation().type->sizeOnStack();
219
  // Variables should not have stack size other than [1, 2],
220
  // but that might change when new types are introduced.
221
26.2k
  solAssert(sizeOnStack == 1 || sizeOnStack == 2, "");
222
26.2k
  m_localVariables[&_declaration].push_back(unsigned(m_asm->deposit()) - _offsetToCurrent);
223
26.2k
}
224
225
void CompilerContext::removeVariable(Declaration const& _declaration)
226
26.2k
{
227
26.2k
  solAssert(m_localVariables.count(&_declaration) && !m_localVariables[&_declaration].empty(), "");
228
26.2k
  m_localVariables[&_declaration].pop_back();
229
26.2k
  if (m_localVariables[&_declaration].empty())
230
26.0k
    m_localVariables.erase(&_declaration);
231
26.2k
}
232
233
void CompilerContext::removeVariablesAboveStackHeight(unsigned _stackHeight)
234
17.9k
{
235
17.9k
  std::vector<Declaration const*> toRemove;
236
17.9k
  for (auto _var: m_localVariables)
237
39.0k
  {
238
39.0k
    solAssert(!_var.second.empty(), "");
239
39.0k
    solAssert(_var.second.back() <= stackHeight(), "");
240
39.0k
    if (_var.second.back() >= _stackHeight)
241
7.84k
      toRemove.push_back(_var.first);
242
39.0k
  }
243
17.9k
  for (auto _var: toRemove)
244
7.84k
    removeVariable(*_var);
245
17.9k
}
246
247
unsigned CompilerContext::numberOfLocalVariables() const
248
12.2k
{
249
12.2k
  return static_cast<unsigned>(m_localVariables.size());
250
12.2k
}
251
252
std::shared_ptr<evmasm::Assembly> CompilerContext::compiledContract(ContractDefinition const& _contract) const
253
176
{
254
176
  auto ret = m_otherCompilers.find(&_contract);
255
176
  solAssert(ret != m_otherCompilers.end(), "Compiled contract not found.");
256
176
  return ret->second->assemblyPtr();
257
176
}
258
259
std::shared_ptr<evmasm::Assembly> CompilerContext::compiledContractRuntime(ContractDefinition const& _contract) const
260
8
{
261
8
  auto ret = m_otherCompilers.find(&_contract);
262
8
  solAssert(ret != m_otherCompilers.end(), "Compiled contract not found.");
263
8
  return ret->second->runtimeAssemblyPtr();
264
8
}
265
266
bool CompilerContext::isLocalVariable(Declaration const* _declaration) const
267
96.4k
{
268
96.4k
  return !!m_localVariables.count(_declaration);
269
96.4k
}
270
271
evmasm::AssemblyItem CompilerContext::functionEntryLabel(Declaration const& _declaration)
272
37.0k
{
273
37.0k
  return m_functionCompilationQueue.entryLabel(_declaration, *this);
274
37.0k
}
275
276
evmasm::AssemblyItem CompilerContext::functionEntryLabelIfExists(Declaration const& _declaration) const
277
0
{
278
0
  return m_functionCompilationQueue.entryLabelIfExists(_declaration);
279
0
}
280
281
FunctionDefinition const& CompilerContext::superFunction(FunctionDefinition const& _function, ContractDefinition const& _base)
282
61
{
283
61
  solAssert(m_mostDerivedContract, "No most derived contract set.");
284
61
  ContractDefinition const* super = _base.superContract(mostDerivedContract());
285
61
  solAssert(super, "Super contract not available.");
286
287
61
  FunctionDefinition const& resolvedFunction = _function.resolveVirtual(mostDerivedContract(), super);
288
61
  solAssert(resolvedFunction.isImplemented(), "");
289
290
61
  return resolvedFunction;
291
61
}
292
293
ContractDefinition const& CompilerContext::mostDerivedContract() const
294
20.2k
{
295
20.2k
  solAssert(m_mostDerivedContract, "Most derived contract not set.");
296
20.2k
  return *m_mostDerivedContract;
297
20.2k
}
298
299
Declaration const* CompilerContext::nextFunctionToCompile() const
300
49.1k
{
301
49.1k
  return m_functionCompilationQueue.nextFunctionToCompile();
302
49.1k
}
303
304
unsigned CompilerContext::baseStackOffsetOfVariable(Declaration const& _declaration) const
305
124k
{
306
124k
  auto res = m_localVariables.find(&_declaration);
307
124k
  solAssert(res != m_localVariables.end(), "Variable not found on stack.");
308
124k
  solAssert(!res->second.empty(), "");
309
124k
  return res->second.back();
310
124k
}
311
312
unsigned CompilerContext::baseToCurrentStackOffset(unsigned _baseOffset) const
313
132k
{
314
132k
  return static_cast<unsigned>(m_asm->deposit()) - _baseOffset - 1;
315
132k
}
316
317
unsigned CompilerContext::currentToBaseStackOffset(unsigned _offset) const
318
8.91k
{
319
8.91k
  return static_cast<unsigned>(m_asm->deposit()) - _offset - 1;
320
8.91k
}
321
322
std::pair<u256, unsigned> CompilerContext::storageLocationOfVariable(Declaration const& _declaration) const
323
26.2k
{
324
26.2k
  auto it = m_stateVariables.find(&_declaration);
325
26.2k
  solAssert(it != m_stateVariables.end(), "Variable not found in storage.");
326
26.2k
  return it->second;
327
26.2k
}
328
329
CompilerContext& CompilerContext::appendJump(evmasm::AssemblyItem::JumpType _jumpType)
330
259k
{
331
259k
  evmasm::AssemblyItem item(Instruction::JUMP);
332
259k
  item.setJumpType(_jumpType);
333
259k
  return *this << item;
334
259k
}
335
336
CompilerContext& CompilerContext::appendPanic(util::PanicCode _code)
337
113k
{
338
113k
  callYulFunction(utilFunctions().panicFunction(_code), 0, 0);
339
113k
  return *this;
340
113k
}
341
342
CompilerContext& CompilerContext::appendConditionalPanic(util::PanicCode _code)
343
111k
{
344
111k
  *this << Instruction::ISZERO;
345
111k
  evmasm::AssemblyItem afterTag = appendConditionalJump();
346
111k
  appendPanic(_code);
347
111k
  *this << afterTag;
348
111k
  return *this;
349
111k
}
350
351
CompilerContext& CompilerContext::appendRevert(std::string const& _message)
352
11.1k
{
353
11.1k
  appendInlineAssembly("{ " + revertReasonIfDebug(_message) + " }");
354
11.1k
  return *this;
355
11.1k
}
356
357
CompilerContext& CompilerContext::appendConditionalRevert(bool _forwardReturnData, std::string const& _message)
358
21.1k
{
359
21.1k
  if (_forwardReturnData && m_evmVersion.supportsReturndata())
360
2.17k
    appendInlineAssembly(R"({
361
2.17k
      if condition {
362
2.17k
        returndatacopy(0, 0, returndatasize())
363
2.17k
        revert(0, returndatasize())
364
2.17k
      }
365
2.17k
    })", {"condition"});
366
18.9k
  else
367
18.9k
    appendInlineAssembly("{ if condition { " + revertReasonIfDebug(_message) + " } }", {"condition"});
368
21.1k
  *this << Instruction::POP;
369
21.1k
  return *this;
370
21.1k
}
371
372
void CompilerContext::resetVisitedNodes(ASTNode const* _node)
373
21.4k
{
374
21.4k
  std::stack<ASTNode const*> newStack;
375
21.4k
  newStack.push(_node);
376
21.4k
  std::swap(m_visitedNodes, newStack);
377
21.4k
  updateSourceLocation();
378
21.4k
}
379
380
void CompilerContext::appendInlineAssembly(
381
  std::string const& _assembly,
382
  std::vector<std::string> const& _localVariables,
383
  std::set<std::string> const& _externallyUsedFunctions,
384
  bool _system,
385
  OptimiserSettings const& _optimiserSettings,
386
  std::string _sourceName
387
)
388
48.0k
{
389
48.0k
  unsigned startStackHeight = stackHeight();
390
391
48.0k
  std::set<yul::YulName> externallyUsedIdentifiers;
392
48.0k
  for (auto const& fun: _externallyUsedFunctions)
393
20.6k
    externallyUsedIdentifiers.insert(yul::YulName(fun));
394
48.0k
  for (auto const& var: _localVariables)
395
35.6k
    externallyUsedIdentifiers.insert(yul::YulName(var));
396
397
48.0k
  yul::ExternalIdentifierAccess identifierAccess;
398
48.0k
  identifierAccess.resolve = [&](
399
48.0k
    yul::Identifier const& _identifier,
400
48.0k
    yul::IdentifierContext,
401
48.0k
    bool _insideFunction
402
48.0k
  ) -> bool
403
1.32M
  {
404
1.32M
    if (_insideFunction)
405
1.25M
      return false;
406
71.3k
    return util::contains(_localVariables, _identifier.name.str());
407
1.32M
  };
408
48.0k
  identifierAccess.generateCode = [&](
409
48.0k
    yul::Identifier const& _identifier,
410
48.0k
    yul::IdentifierContext _context,
411
48.0k
    yul::AbstractAssembly& _assembly
412
48.0k
  )
413
53.3k
  {
414
53.3k
    solAssert(_context == yul::IdentifierContext::RValue || _context == yul::IdentifierContext::LValue, "");
415
53.3k
    auto it = std::find(_localVariables.begin(), _localVariables.end(), _identifier.name.str());
416
53.3k
    solAssert(it != _localVariables.end(), "");
417
53.3k
    auto stackDepth = static_cast<size_t>(distance(it, _localVariables.end()));
418
53.3k
    size_t stackDiff = static_cast<size_t>(_assembly.stackHeight()) - startStackHeight + stackDepth;
419
53.3k
    if (_context == yul::IdentifierContext::LValue)
420
6.58k
      stackDiff -= 1;
421
53.3k
    if (stackDiff < 1 || stackDiff > reachableStackDepth())
422
53.3k
      BOOST_THROW_EXCEPTION(
423
53.3k
        StackTooDeepError() <<
424
53.3k
        errinfo_sourceLocation(nativeLocationOf(_identifier)) <<
425
53.3k
        util::errinfo_comment(util::stackTooDeepString)
426
53.3k
      );
427
53.3k
    if (_context == yul::IdentifierContext::RValue)
428
46.7k
      _assembly.appendInstruction(dupInstruction(static_cast<unsigned>(stackDiff)));
429
6.58k
    else
430
6.58k
    {
431
6.58k
      _assembly.appendInstruction(swapInstruction(static_cast<unsigned>(stackDiff)));
432
6.58k
      _assembly.appendInstruction(Instruction::POP);
433
6.58k
    }
434
53.3k
  };
435
436
48.0k
  ErrorList errors;
437
48.0k
  ErrorReporter errorReporter(errors);
438
48.0k
  langutil::CharStream charStream(_assembly, _sourceName);
439
48.0k
  yul::EVMDialect const& dialect = yul::EVMDialect::strictAssemblyForEVM(m_evmVersion);
440
48.0k
  std::optional<langutil::SourceLocation> locationOverride;
441
48.0k
  if (!_system)
442
40.4k
    locationOverride = m_asm->currentSourceLocation();
443
48.0k
  std::shared_ptr<yul::AST> parserResult =
444
48.0k
    yul::Parser(errorReporter, dialect, std::move(locationOverride))
445
48.0k
    .parse(charStream);
446
#ifdef SOL_OUTPUT_ASM
447
  std::cout << yul::AsmPrinter::format(*parserResult) << std::endl;
448
#endif
449
450
48.0k
  auto reportError = [&](std::string const& _context)
451
48.0k
  {
452
0
    std::string message =
453
0
      "Error parsing/analyzing inline assembly block:\n" +
454
0
      _context + "\n"
455
0
      "------------------ Input: -----------------\n" +
456
0
      _assembly + "\n"
457
0
      "------------------ Errors: ----------------\n";
458
0
    for (auto const& error: errorReporter.errors())
459
      // TODO if we have "locationOverride", it will be the wrong char stream,
460
      // but we do not have access to the solidity scanner.
461
0
      message += SourceReferenceFormatter::formatErrorInformation(*error, charStream);
462
0
    message += "-------------------------------------------\n";
463
464
0
    solAssert(false, message);
465
0
  };
466
467
48.0k
  yul::AsmAnalysisInfo analysisInfo;
468
48.0k
  bool analyzerResult = false;
469
48.0k
  if (parserResult)
470
48.0k
    analyzerResult = yul::AsmAnalyzer(
471
48.0k
      analysisInfo,
472
48.0k
      errorReporter,
473
48.0k
      dialect,
474
48.0k
      identifierAccess.resolve
475
48.0k
    ).analyze(parserResult->root());
476
48.0k
  if (!parserResult || errorReporter.hasErrorsWarningsOrInfos() || !analyzerResult)
477
0
    reportError("Invalid assembly generated by code generator.");
478
48.0k
  std::shared_ptr<yul::AST const> toBeAssembledAST = parserResult;
479
480
  // Several optimizer steps cannot handle externally supplied stack variables,
481
  // so we essentially only optimize the ABI functions.
482
48.0k
  if (_optimiserSettings.runYulOptimiser && _localVariables.empty())
483
1.50k
  {
484
1.50k
    yul::Object obj;
485
1.50k
    obj.setCode(parserResult, std::make_shared<yul::AsmAnalysisInfo>(analysisInfo));
486
487
1.50k
    solAssert(!dialect.providesObjectAccess());
488
1.50k
    optimizeYul(obj, _optimiserSettings, externallyUsedIdentifiers);
489
490
1.50k
    if (_system)
491
1.50k
    {
492
      // Store as generated sources, but first re-parse to update the source references.
493
1.50k
      solAssert(m_generatedYulUtilityCode.empty(), "");
494
1.50k
      m_generatedYulUtilityCode = yul::AsmPrinter::format(*obj.code());
495
1.50k
      langutil::CharStream charStream(m_generatedYulUtilityCode, _sourceName);
496
1.50k
      obj.setCode(yul::Parser(errorReporter, dialect).parse(charStream));
497
1.50k
      obj.analysisInfo = std::make_shared<yul::AsmAnalysisInfo>(yul::AsmAnalyzer::analyzeStrictAssertCorrect(obj));
498
1.50k
    }
499
500
1.50k
    analysisInfo = std::move(*obj.analysisInfo);
501
1.50k
    toBeAssembledAST = obj.code();
502
503
#ifdef SOL_OUTPUT_ASM
504
    std::cout << "After optimizer:" << std::endl;
505
    std::cout << yul::AsmPrinter::format(*parserResult) << std::endl;
506
#endif
507
1.50k
  }
508
46.5k
  else if (_system)
509
6.09k
  {
510
    // Store as generated source.
511
6.09k
    solAssert(m_generatedYulUtilityCode.empty(), "");
512
6.09k
    m_generatedYulUtilityCode = _assembly;
513
6.09k
  }
514
515
48.0k
  if (errorReporter.hasErrorsWarningsOrInfos())
516
0
    reportError("Failed to analyze inline assembly block.");
517
518
48.0k
  solAssert(!errorReporter.hasErrorsWarningsOrInfos(), "Failed to analyze inline assembly block.");
519
48.0k
  yul::CodeGenerator::assemble(
520
48.0k
    toBeAssembledAST->root(),
521
48.0k
    analysisInfo,
522
48.0k
    *m_asm,
523
48.0k
    m_evmVersion,
524
48.0k
    identifierAccess.generateCode,
525
48.0k
    _system,
526
48.0k
    _optimiserSettings.optimizeStackAllocation
527
48.0k
  );
528
529
  // Reset the source location to the one of the node (instead of the CODEGEN source location)
530
48.0k
  updateSourceLocation();
531
48.0k
}
532
533
534
void CompilerContext::optimizeYul(yul::Object& _object, OptimiserSettings const& _optimiserSettings, std::set<yul::YulName> const& _externalIdentifiers)
535
2.55k
{
536
2.55k
  yulAssert(_object.dialect());
537
2.55k
  auto const* evmDialect = dynamic_cast<yul::EVMDialect const*>(_object.dialect());
538
2.55k
  yulAssert(evmDialect);
539
#ifdef SOL_OUTPUT_ASM
540
  std::cout << yul::AsmPrinter::format(*_object.code()) << std::endl;
541
#endif
542
543
2.55k
  bool const isCreation = runtimeContext() != nullptr;
544
2.55k
  yul::GasMeter meter(*evmDialect, isCreation, _optimiserSettings.expectedExecutionsPerDeployment);
545
2.55k
  yul::OptimiserSuite::run(
546
2.55k
    &meter,
547
2.55k
    _object,
548
2.55k
    _optimiserSettings.optimizeStackAllocation,
549
2.55k
    _optimiserSettings.yulOptimiserSteps,
550
2.55k
    _optimiserSettings.yulOptimiserCleanupSteps,
551
2.55k
    isCreation? std::nullopt : std::make_optional(_optimiserSettings.expectedExecutionsPerDeployment),
552
2.55k
    _externalIdentifiers
553
2.55k
  );
554
555
#ifdef SOL_OUTPUT_ASM
556
  std::cout << "After optimizer:" << std::endl;
557
  std::cout << yul::AsmPrinter::format(*_object.code()) << std::endl;
558
#endif
559
2.55k
}
560
561
std::string CompilerContext::revertReasonIfDebug(std::string const& _message)
562
30.3k
{
563
30.3k
  return YulUtilFunctions::revertReasonIfDebugBody(
564
30.3k
    m_revertStrings,
565
30.3k
    "mload(" + std::to_string(CompilerUtils::freeMemoryPointer) + ")",
566
30.3k
    _message
567
30.3k
  );
568
30.3k
}
569
570
void CompilerContext::updateSourceLocation()
571
1.97M
{
572
1.97M
  m_asm->setSourceLocation(m_visitedNodes.empty() ? SourceLocation() : m_visitedNodes.top()->location());
573
1.97M
}
574
575
evmasm::AssemblyItem CompilerContext::FunctionCompilationQueue::entryLabel(
576
  Declaration const& _declaration,
577
  CompilerContext& _context
578
)
579
37.0k
{
580
37.0k
  auto res = m_entryLabels.find(&_declaration);
581
37.0k
  if (res == m_entryLabels.end())
582
15.4k
  {
583
15.4k
    size_t params = 0;
584
15.4k
    size_t returns = 0;
585
15.4k
    if (auto const* function = dynamic_cast<FunctionDefinition const*>(&_declaration))
586
13.6k
    {
587
13.6k
      FunctionType functionType(*function, FunctionType::Kind::Internal);
588
13.6k
      params = CompilerUtils::sizeOnStack(functionType.parameterTypes());
589
13.6k
      returns = CompilerUtils::sizeOnStack(functionType.returnParameterTypes());
590
13.6k
    }
591
592
    // some name that cannot clash with yul function names.
593
15.4k
    std::string labelName = "@" + _declaration.name() + "_" + std::to_string(_declaration.id());
594
15.4k
    evmasm::AssemblyItem tag = _context.namedTag(
595
15.4k
      labelName,
596
15.4k
      params,
597
15.4k
      returns,
598
15.4k
      _declaration.id()
599
15.4k
    );
600
15.4k
    m_entryLabels.insert(std::make_pair(&_declaration, tag));
601
15.4k
    m_functionsToCompile.push(&_declaration);
602
15.4k
    return tag.tag();
603
15.4k
  }
604
21.6k
  else
605
21.6k
    return res->second.tag();
606
607
37.0k
}
608
609
evmasm::AssemblyItem CompilerContext::FunctionCompilationQueue::entryLabelIfExists(Declaration const& _declaration) const
610
0
{
611
0
  auto res = m_entryLabels.find(&_declaration);
612
0
  return res == m_entryLabels.end() ? evmasm::AssemblyItem(evmasm::UndefinedItem) : res->second.tag();
613
0
}
614
615
Declaration const* CompilerContext::FunctionCompilationQueue::nextFunctionToCompile() const
616
49.1k
{
617
50.7k
  while (!m_functionsToCompile.empty())
618
21.2k
  {
619
21.2k
    if (m_alreadyCompiledFunctions.count(m_functionsToCompile.front()))
620
1.55k
      m_functionsToCompile.pop();
621
19.7k
    else
622
19.7k
      return m_functionsToCompile.front();
623
21.2k
  }
624
29.4k
  return nullptr;
625
49.1k
}
626
627
void CompilerContext::FunctionCompilationQueue::startFunction(Declaration const& _function)
628
15.4k
{
629
15.4k
  if (!m_functionsToCompile.empty() && m_functionsToCompile.front() == &_function)
630
13.8k
    m_functionsToCompile.pop();
631
15.4k
  m_alreadyCompiledFunctions.insert(&_function);
632
15.4k
}