This commit is contained in:
Daniel Kirchner 2022-02-01 11:57:48 +01:00
parent 731a61b9fc
commit 4a3f1fa9cd
5 changed files with 596 additions and 27 deletions

View File

@ -71,6 +71,8 @@ add_library(yul
backends/evm/EVMObjectCompiler.h
backends/evm/EVMMetrics.cpp
backends/evm/EVMMetrics.h
backends/evm/DirectEVMCodeTransform.cpp
backends/evm/DirectEVMCodeTransform.h
backends/evm/DirectStackLayoutGenerator.cpp
backends/evm/DirectStackLayoutGenerator.h
backends/evm/NoOutputAssembly.h

View File

@ -0,0 +1,233 @@
/*
This file is part of solidity.
solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
// SPDX-License-Identifier: GPL-3.0
#include <libyul/backends/evm/DirectEVMCodeTransform.h>
#include <libyul/backends/evm/StackHelpers.h>
#include <libsolutil/Visitor.h>
#include <range/v3/range/conversion.hpp>
using namespace solidity;
using namespace solidity::yul;
using namespace std;
void DirectEVMCodeTransform::run(
AbstractAssembly& _assembly,
BuiltinContext& _builtinContext,
UseNamedLabels _useNamedLabelsForFunctions,
AsmAnalysisInfo const& _analysisInfo,
Dialect const& _dialect,
Block const& _ast
)
{
DirectStackLayoutGenerator::Context context = DirectStackLayoutGenerator::run(_analysisInfo, _dialect, _ast);
DirectEVMCodeTransform codeTransform{
_assembly,
_builtinContext,
_useNamedLabelsForFunctions,
context,
_analysisInfo,
_dialect
};
codeTransform(_ast);
}
void DirectEVMCodeTransform::operator()(Block const& _block)
{
auto const& blockInfo = m_context.layout.blockInfos.at(&_block);
createStackLayout(debugDataOf(_block), blockInfo.entry);
for(auto const& _statement: _block.statements)
{
auto const& statementInfo = m_context.layout.statementInfos.at(&_statement);
createStackLayout(debugDataOf(_statement), statementInfo);
visit(_statement);
}
}
void DirectEVMCodeTransform::operator()(FunctionCall const&)
{
}
void DirectEVMCodeTransform::operator()(ExpressionStatement const&)
{
}
void DirectEVMCodeTransform::operator()(Assignment const&)
{
}
void DirectEVMCodeTransform::operator()(VariableDeclaration const&)
{
}
void DirectEVMCodeTransform::operator()(If const&)
{
yulAssert(false, "");
}
void DirectEVMCodeTransform::operator()(Switch const&)
{
yulAssert(false, "");
}
void DirectEVMCodeTransform::operator()(ForLoop const&)
{
yulAssert(false, "");
}
void DirectEVMCodeTransform::operator()(FunctionDefinition const&)
{
}
void DirectEVMCodeTransform::operator()(Break const&)
{
yulAssert(false, "");
}
void DirectEVMCodeTransform::operator()(Continue const&)
{
yulAssert(false, "");
}
void DirectEVMCodeTransform::operator()(Leave const&)
{
yulAssert(false, "");
}
void DirectEVMCodeTransform::createStackLayout(std::shared_ptr<DebugData const> _debugData, Stack _targetStack)
{
static constexpr auto slotVariableName = [](StackSlot const& _slot) {
return std::visit(util::GenericVisitor{
[](VariableSlot const& _var) { return _var.variable.get().name; },
[](auto const&) { return YulString{}; }
}, _slot);
};
yulAssert(m_assembly.stackHeight() == static_cast<int>(m_stack.size()), "");
// ::createStackLayout asserts that it has successfully achieved the target layout.
langutil::SourceLocation sourceLocation = _debugData ? _debugData->originLocation : langutil::SourceLocation{};
m_assembly.setSourceLocation(sourceLocation);
::createStackLayout(
m_stack,
_targetStack | ranges::to<Stack>,
// Swap callback.
[&](unsigned _i)
{
yulAssert(static_cast<int>(m_stack.size()) == m_assembly.stackHeight(), "");
yulAssert(_i > 0 && _i < m_stack.size(), "");
if (_i <= 16)
m_assembly.appendInstruction(evmasm::swapInstruction(_i));
else
{
int deficit = static_cast<int>(_i) - 16;
StackSlot const& deepSlot = m_stack.at(m_stack.size() - _i - 1);
YulString varNameDeep = slotVariableName(deepSlot);
YulString varNameTop = slotVariableName(m_stack.back());
string msg =
"Cannot swap " + (varNameDeep.empty() ? "Slot " + stackSlotToString(deepSlot) : "Variable " + varNameDeep.str()) +
" with " + (varNameTop.empty() ? "Slot " + stackSlotToString(m_stack.back()) : "Variable " + varNameTop.str()) +
": too deep in the stack by " + to_string(deficit) + " slots in " + stackToString(m_stack);
m_stackErrors.emplace_back(StackTooDeepError(
m_currentFunctionInfo ? m_currentFunctionInfo->function.name : YulString{},
varNameDeep.empty() ? varNameTop : varNameDeep,
deficit,
msg
));
m_assembly.markAsInvalid();
}
},
// Push or dup callback.
[&](StackSlot const& _slot)
{
yulAssert(static_cast<int>(m_stack.size()) == m_assembly.stackHeight(), "");
// Dup the slot, if already on stack and reachable.
if (auto depth = util::findOffset(m_stack | ranges::views::reverse, _slot))
{
if (*depth < 16)
{
m_assembly.appendInstruction(evmasm::dupInstruction(static_cast<unsigned>(*depth + 1)));
return;
}
else if (!canBeFreelyGenerated(_slot))
{
int deficit = static_cast<int>(*depth - 15);
YulString varName = slotVariableName(_slot);
string msg =
(varName.empty() ? "Slot " + stackSlotToString(_slot) : "Variable " + varName.str())
+ " is " + to_string(*depth - 15) + " too deep in the stack " + stackToString(m_stack);
m_stackErrors.emplace_back(StackTooDeepError(
m_currentFunctionInfo ? m_currentFunctionInfo->function.name : YulString{},
varName,
deficit,
msg
));
m_assembly.markAsInvalid();
m_assembly.appendConstant(u256(0xCAFFEE));
return;
}
// else: the slot is too deep in stack, but can be freely generated, we fall through to push it again.
}
// The slot can be freely generated or is an unassigned return variable. Push it.
std::visit(util::GenericVisitor{
[&](LiteralSlot const& _literal)
{
m_assembly.setSourceLocation(originLocationOf(_literal));
m_assembly.appendConstant(_literal.value);
m_assembly.setSourceLocation(sourceLocation);
},
[&](FunctionReturnLabelSlot const&)
{
yulAssert(false, "Cannot produce function return label.");
},
[&](FunctionCallReturnLabelSlot const& _returnLabel)
{
if (!m_returnLabels.count(&_returnLabel.call.get()))
m_returnLabels[&_returnLabel.call.get()] = m_assembly.newLabelId();
m_assembly.setSourceLocation(originLocationOf(_returnLabel.call.get()));
m_assembly.appendLabelReference(m_returnLabels.at(&_returnLabel.call.get()));
m_assembly.setSourceLocation(sourceLocation);
},
[&](VariableSlot const& _variable)
{
if (m_currentFunctionInfo && util::contains(m_currentFunctionInfo->returnVariables, _variable))
{
m_assembly.setSourceLocation(originLocationOf(_variable));
m_assembly.appendConstant(0);
m_assembly.setSourceLocation(sourceLocation);
return;
}
yulAssert(false, "Variable not found on stack.");
},
[&](TemporarySlot const&)
{
yulAssert(false, "Function call result requested, but not found on stack.");
},
[&](JunkSlot const&)
{
// Note: this will always be popped, so we can push anything.
m_assembly.appendInstruction(evmasm::Instruction::CODESIZE);
}
}, _slot);
},
// Pop callback.
[&]()
{
m_assembly.appendInstruction(evmasm::Instruction::POP);
}
);
yulAssert(m_assembly.stackHeight() == static_cast<int>(m_stack.size()), "");
}

View File

@ -0,0 +1,98 @@
/*
This file is part of solidity.
solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
// SPDX-License-Identifier: GPL-3.0
/**
* Code generator for translating Yul / inline assembly to EVM.
*/
#pragma once
#include <libyul/backends/evm/AbstractAssembly.h>
#include <libyul/backends/evm/DirectStackLayoutGenerator.h>
#include <libyul/backends/evm/EVMDialect.h>
namespace solidity::yul
{
class DirectEVMCodeTransform: public ASTWalker
{
public:
/// Use named labels for functions 1) Yes and check that the names are unique
/// 2) For none of the functions 3) for the first function of each name.
enum class UseNamedLabels { YesAndForceUnique, Never, ForFirstFunctionOfEachName };
static void run(
AbstractAssembly& _assembly,
BuiltinContext& _builtinContext,
UseNamedLabels _useNamedLabelsForFunctions,
AsmAnalysisInfo const& _analysisInfo,
Dialect const& _dialect,
Block const& _ast
);
void operator()(Literal const&) override;
void operator()(Identifier const&) override;
void operator()(FunctionCall const& _funCall) override;
void operator()(ExpressionStatement const& _statement) override;
void operator()(Assignment const& _assignment) override;
void operator()(VariableDeclaration const& _varDecl) override;
void operator()(If const& _if) override;
void operator()(Switch const& _switch) override;
void operator()(ForLoop const&) override;
void operator()(FunctionDefinition const&) override;
void operator()(Break const&) override;
void operator()(Continue const&) override;
void operator()(Leave const&) override;
void operator()(Block const& _block) override;
void visit(Statement const& _stmt) override;
using ASTWalker::visit;
private:
DirectEVMCodeTransform(
AbstractAssembly& _assembly,
BuiltinContext& _builtinContext,
UseNamedLabels _useNamedLabelsForFunctions,
DirectStackLayoutGenerator::Context const& _context,
AsmAnalysisInfo const& _analysisInfo,
Dialect const& _dialect
):
m_assembly(_assembly),
m_builtinContext(_builtinContext),
m_useNamedLabelsForFunctions(_useNamedLabelsForFunctions),
m_context(_context),
m_info(_analysisInfo),
m_dialect(_dialect)
{}
/// Shuffles m_stack to the desired @a _targetStack while emitting the shuffling code to m_assembly.
/// Sets the source locations to the one in @a _debugData.
void createStackLayout(std::shared_ptr<DebugData const> _debugData, Stack _targetStack);
AbstractAssembly& m_assembly;
BuiltinContext& m_builtinContext;
UseNamedLabels m_useNamedLabelsForFunctions;
DirectStackLayoutGenerator::Context const& m_context;
AsmAnalysisInfo const& m_info;
Dialect const& m_dialect;
Stack m_stack;
CFG::FunctionInfo const* m_currentFunctionInfo = nullptr;
std::map<yul::FunctionCall const*, AbstractAssembly::LabelID> m_returnLabels;
std::map<CFG::BasicBlock const*, AbstractAssembly::LabelID> m_blockLabels;
std::map<CFG::FunctionInfo const*, AbstractAssembly::LabelID> const m_functionLabels;
std::vector<StackTooDeepError> m_stackErrors;
};
}

View File

@ -22,63 +22,266 @@
#include <libyul/backends/evm/DirectStackLayoutGenerator.h>
#include <libyul/AST.h>
#include <libyul/Utilities.h>
#include <libsolutil/cxx20.h>
#include <libsolutil/Visitor.h>
#include <range/v3/view/drop_exactly.hpp>
#include <range/v3/view/enumerate.hpp>
#include <range/v3/view/reverse.hpp>
#include <range/v3/view/transform.hpp>
using namespace solidity;
using namespace solidity::yul;
using namespace std;
void DirectStackLayoutGenerator::run(Block const& _block)
DirectStackLayoutGenerator::Context DirectStackLayoutGenerator::run(AsmAnalysisInfo const& _analysisInfo, Dialect const& _dialect, Block const& _block)
{
/*
yulAssert(_block.statements.size() > 0, "");
yulAssert(holds_alternative<Block>(_block.statements.front()), "");
{
DirectStackLayoutGenerator generator;
DirectStackLayoutGenerator generator{_analysisInfo, _dialect};
generator(get<Block>(_block.statements.front()));
}
for (Statement const& statement: _block.statements | ranges::views::drop_exactly(1))
{
FunctionDefinition const* functionDefinition = get_if<FunctionDefinition>(&statement);
yulAssert(functionDefinition, "");
DirectStackLayoutGenerator generator;
DirectStackLayoutGenerator generator{_analysisInfo, _dialect};
generator(*functionDefinition);
}
}*/
Context context;
DirectStackLayoutGenerator generator{context, _analysisInfo, _dialect};
generator(_block);
return context;
}
void DirectStackLayoutGenerator::operator()(Block const& _block)
{
ScopedSaveAndRestore saveScope(m_scope, m_info.scopes.at(&_block).get());
for (auto const& statement: _block.statements)
if (auto const* function = get_if<FunctionDefinition>(&statement))
registerFunction(*function);
for (Statement const& statement: _block.statements | ranges::views::reverse)
visit(statement);
m_context.layout.blockInfos[&_block].entry = m_stack;
}
void DirectStackLayoutGenerator::operator()(VariableDeclaration const& _variableDeclaration)
{
auto declaredVariables = _variableDeclaration.variables | ranges::views::transform([&](TypedName const& _var) {
return VariableSlot{lookupVariable(_var.name), _var.debugData};
}) | ranges::to<vector<VariableSlot>>;
visitAssignmentOrDeclaration(
debugDataOf(_variableDeclaration),
declaredVariables,
_variableDeclaration.value ? _variableDeclaration.value.get() : nullptr
);
}
void DirectStackLayoutGenerator::operator()(Assignment const& _assignment)
{
auto assignedVariables = _assignment.variableNames | ranges::views::transform([&](Identifier const& _var) {
return VariableSlot{lookupVariable(_var.name), _var.debugData};
}) | ranges::to<vector<VariableSlot>>;
visitAssignmentOrDeclaration(
debugDataOf(_assignment),
assignedVariables,
_assignment.value.get()
);
}
void DirectStackLayoutGenerator::operator()(Literal const& _literal)
{
m_stack.emplace_back(LiteralSlot{valueOfLiteral(_literal), debugDataOf(_literal)});
}
void DirectStackLayoutGenerator::operator()(Identifier const& _identifier)
{
m_stack.emplace_back(VariableSlot{lookupVariable(_identifier.name), debugDataOf(_identifier)});
}
void DirectStackLayoutGenerator::operator()(FunctionCall const& _funCall)
{
yulAssert(visitFunctionCall(_funCall) == 1, "");
}
void DirectStackLayoutGenerator::operator()(ExpressionStatement const& _expressionStatement)
{
std::visit(util::GenericVisitor{
[&](FunctionCall const& _call) {
yulAssert(visitFunctionCall(_call) == 0, "");
},
[&](auto const&) { yulAssert(false, ""); }
}, _expressionStatement.expression);
}
void DirectStackLayoutGenerator::operator()(If const& _if)
{
DirectStackLayoutGenerator bodyGenerator{m_context, m_info, m_dialect, m_stack};
bodyGenerator(_if.body);
m_stack += move(bodyGenerator.m_stack);
visit(*_if.condition);
}
void DirectStackLayoutGenerator::operator()(Switch const& _switch)
{
Stack combinedStack;
for (auto const& switchCase: _switch.cases)
{
DirectStackLayoutGenerator bodyGenerator{m_context, m_info, m_dialect, m_stack};
bodyGenerator(switchCase.body);
for (auto const& slot: bodyGenerator.m_stack)
if (!util::contains(combinedStack, slot))
combinedStack.emplace_back(slot);
}
m_stack += move(combinedStack);
visit(*_switch.expression);
}
void DirectStackLayoutGenerator::operator()(ForLoop const& _forLoop)
{
yulAssert(_forLoop.pre.statements.empty(), "");
DirectStackLayoutGenerator bodyGenerator{m_context, m_info, m_dialect, m_stack};
bodyGenerator(_forLoop.post);
bodyGenerator(_forLoop.body);
m_stack += move(bodyGenerator.m_stack);
visit(*_forLoop.condition);
}
void DirectStackLayoutGenerator::operator()(FunctionDefinition const& _functionDefinition)
void DirectStackLayoutGenerator::operator()(FunctionDefinition const& _function)
{
}
yulAssert(m_scope, "");
yulAssert(m_scope->identifiers.count(_function.name), "");
Scope::Function& function = std::get<Scope::Function>(m_scope->identifiers.at(_function.name));
auto const& functionInfo = m_context.functionInfo.at(&function);
yulAssert(m_info.scopes.at(&_function.body), "");
Scope* virtualFunctionScope = m_info.scopes.at(m_info.virtualBlocks.at(&_function).get()).get();
yulAssert(virtualFunctionScope, "");
DirectStackLayoutGenerator bodyGenerator{m_context, m_info, m_dialect, m_stack};
bodyGenerator.m_stack = functionInfo.returnVariables | ranges::views::transform([](auto const& _varSlot){
return StackSlot{_varSlot};
}) | ranges::to<Stack>;
bodyGenerator.m_stack.emplace_back(FunctionReturnLabelSlot{function});
bodyGenerator(_function.body);
}
void DirectStackLayoutGenerator::visit(Statement const& _statement)
{
ASTWalker::visit(_statement);
m_context.layout.statementInfos[&_statement] = m_stack;
}
size_t DirectStackLayoutGenerator::visitFunctionCall(FunctionCall const& _call)
{
if (BuiltinFunction const* builtin = m_dialect.builtin(_call.functionName.name))
{
for (auto&& [idx, arg]: _call.arguments | ranges::views::enumerate | ranges::views::reverse)
if (!builtin->literalArgument(idx).has_value())
std::visit(*this, arg);
return builtin->returns.size();
}
else
{
m_stack.emplace_back(FunctionCallReturnLabelSlot{_call});
for (auto const& arg: _call.arguments | ranges::views::reverse)
std::visit(*this, arg);
Scope::Function const& function = lookupFunction(_call.functionName.name);
return function.returns.size();
}
}
void DirectStackLayoutGenerator::visitAssignmentOrDeclaration(
std::shared_ptr<DebugData const> _debugData,
vector<VariableSlot> const& _variables,
Expression const* _expression
)
{
// TODO: reproduce proper createIdealLayout here
cxx20::erase_if(m_stack, [&](StackSlot const& slot) {
if (auto const* varSlot = get_if<VariableSlot>(&slot))
if (util::contains(_variables, *varSlot))
return true;
return false;
});
if (!_expression)
{
m_stack += Stack(_variables.size(), LiteralSlot{0, _debugData});
return;
}
if (_variables.size() == 1 && !holds_alternative<FunctionCall>(*_expression))
{
visit(*_expression);
return;
}
auto const* call = get_if<FunctionCall>(_expression);
yulAssert(call, "");
yulAssert(visitFunctionCall(*call) == _variables.size(), "");
}
Scope::Function const& DirectStackLayoutGenerator::lookupFunction(YulString _name) const
{
Scope::Function const* function = nullptr;
yulAssert(m_scope->lookup(_name, util::GenericVisitor{
[](Scope::Variable&) { yulAssert(false, "Expected function name."); },
[&](Scope::Function& _function) { function = &_function; }
}), "Function name not found.");
yulAssert(function, "");
return *function;
}
Scope::Variable const& DirectStackLayoutGenerator::lookupVariable(YulString _name) const
{
yulAssert(m_scope, "");
Scope::Variable const* var = nullptr;
if (m_scope->lookup(_name, util::GenericVisitor{
[&](Scope::Variable& _var) { var = &_var; },
[](Scope::Function&)
{
yulAssert(false, "Function not removed during desugaring.");
}
}))
{
yulAssert(var, "");
return *var;
};
yulAssert(false, "External identifier access unimplemented.");
}
void DirectStackLayoutGenerator::registerFunction(FunctionDefinition const& _function)
{
yulAssert(m_scope, "");
yulAssert(m_scope->identifiers.count(_function.name), "");
Scope::Function& function = std::get<Scope::Function>(m_scope->identifiers.at(_function.name));
m_context.functionList.emplace_back(&function);
yulAssert(m_info.scopes.at(&_function.body), "");
Scope* virtualFunctionScope = m_info.scopes.at(m_info.virtualBlocks.at(&_function).get()).get();
yulAssert(virtualFunctionScope, "");
bool inserted = m_context.functionInfo.emplace(std::make_pair(&function, FunctionInfo{
_function.debugData,
function,
_function.parameters | ranges::views::transform([&](auto const& _param) {
return VariableSlot{
std::get<Scope::Variable>(virtualFunctionScope->identifiers.at(_param.name)),
_param.debugData
};
}) | ranges::to<vector>,
_function.returnVariables | ranges::views::transform([&](auto const& _retVar) {
return VariableSlot{
std::get<Scope::Variable>(virtualFunctionScope->identifiers.at(_retVar.name)),
_retVar.debugData
};
}) | ranges::to<vector>
})).second;
yulAssert(inserted);
}

View File

@ -25,42 +25,75 @@
#include <libyul/optimiser/ASTWalker.h>
#include <libyul/AsmAnalysisInfo.h>
namespace solidity::yul
{
struct StackLayout
{
struct StackInfo
struct BlockInfo
{
Stack entry;
Stack exit;
};
std::map<Block const*, StackInfo> blockInfos;
std::map<Statement const*, StackInfo> statementInfos;
std::map<Block const*, BlockInfo> blockInfos;
std::map<Statement const*, Stack> statementInfos;
};
class DirectStackLayoutGenerator: public ASTWalker
{
public:
static void run(Block const& _ast);
struct FunctionInfo {
std::shared_ptr<DebugData const> debugData;
Scope::Function const& function;
std::vector<VariableSlot> parameters;
std::vector<VariableSlot> returnVariables;
};
struct Context {
StackLayout layout;
std::vector<Scope::Function const*> functionList;
std::map<Scope::Function const*, FunctionInfo> functionInfo;
};
static Context run(AsmAnalysisInfo const& _analysisInfo, Dialect const& _dialect, Block const& _ast);
void operator()(Literal const&) override;
void operator()(Identifier const&) override;
void operator()(FunctionCall const& _funCall) override;
void operator()(ExpressionStatement const& _statement) override;
void operator()(Assignment const& _assignment) override;
void operator()(VariableDeclaration const& _varDecl) override;
void operator()(If const& _if) override;
void operator()(Switch const& _switch) override;
void operator()(ForLoop const&) override;
void operator()(FunctionDefinition const&) override;
void operator()(Break const&) override;
void operator()(Continue const&) override;
void operator()(Leave const&) override;
void operator()(Block const& _block) override;
void visit(Statement const& _stmt) override;
using ASTWalker::visit;
virtual void operator()(Literal const&) {}
virtual void operator()(Identifier const&) {}
virtual void operator()(FunctionCall const& _funCall);
virtual void operator()(ExpressionStatement const& _statement);
virtual void operator()(Assignment const& _assignment);
virtual void operator()(VariableDeclaration const& _varDecl);
virtual void operator()(If const& _if);
virtual void operator()(Switch const& _switch);
virtual void operator()(ForLoop const&);
virtual void operator()(FunctionDefinition const&);
virtual void operator()(Break const&);
virtual void operator()(Continue const&);
virtual void operator()(Leave const&);
virtual void operator()(Block const& _block);
private:
DirectStackLayoutGenerator() {}
DirectStackLayoutGenerator(Context& _context, AsmAnalysisInfo const& _info, Dialect const& _dialect, Stack _prefix = {}):
m_context(_context), m_info(_info), m_dialect(_dialect), m_prefix(std::move(_prefix)) {}
size_t visitFunctionCall(FunctionCall const&);
void visitAssignmentOrDeclaration(
std::shared_ptr<DebugData const> _debugData,
std::vector<VariableSlot> const& _variables,
Expression const* _expression
);
Scope::Function const& lookupFunction(YulString _name) const;
Scope::Variable const& lookupVariable(YulString _name) const;
void registerFunction(FunctionDefinition const& _function);
Context& m_context;
AsmAnalysisInfo const& m_info;
Dialect const& m_dialect;
Scope* m_scope = nullptr;
Stack m_prefix;
Stack m_stack;
};