mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
tmp
This commit is contained in:
parent
731a61b9fc
commit
4a3f1fa9cd
@ -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
|
||||
|
233
libyul/backends/evm/DirectEVMCodeTransform.cpp
Normal file
233
libyul/backends/evm/DirectEVMCodeTransform.cpp
Normal 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()), "");
|
||||
}
|
98
libyul/backends/evm/DirectEVMCodeTransform.h
Normal file
98
libyul/backends/evm/DirectEVMCodeTransform.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user