mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge remote-tracking branch 'origin/develop' into breaking
This commit is contained in:
@@ -176,9 +176,9 @@ StackSlot ControlFlowGraphBuilder::operator()(Expression const& _expression)
|
||||
|
||||
StackSlot ControlFlowGraphBuilder::operator()(FunctionCall const& _call)
|
||||
{
|
||||
CFG::Operation const& operation = visitFunctionCall(_call);
|
||||
yulAssert(operation.output.size() == 1, "");
|
||||
return operation.output.front();
|
||||
Stack const& output = visitFunctionCall(_call);
|
||||
yulAssert(output.size() == 1, "");
|
||||
return output.front();
|
||||
}
|
||||
|
||||
void ControlFlowGraphBuilder::operator()(VariableDeclaration const& _varDecl)
|
||||
@@ -219,8 +219,8 @@ void ControlFlowGraphBuilder::operator()(ExpressionStatement const& _exprStmt)
|
||||
yulAssert(m_currentBlock, "");
|
||||
std::visit(util::GenericVisitor{
|
||||
[&](FunctionCall const& _call) {
|
||||
CFG::Operation const& operation = visitFunctionCall(_call);
|
||||
yulAssert(operation.output.empty(), "");
|
||||
Stack const& output = visitFunctionCall(_call);
|
||||
yulAssert(output.empty(), "");
|
||||
},
|
||||
[&](auto const&) { yulAssert(false, ""); }
|
||||
}, _exprStmt.expression);
|
||||
@@ -239,6 +239,9 @@ void ControlFlowGraphBuilder::operator()(ExpressionStatement const& _exprStmt)
|
||||
void ControlFlowGraphBuilder::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 (auto const& statement: _block.statements)
|
||||
std::visit(*this, statement);
|
||||
}
|
||||
@@ -386,11 +389,26 @@ void ControlFlowGraphBuilder::operator()(FunctionDefinition const& _function)
|
||||
Scope::Function& function = std::get<Scope::Function>(m_scope->identifiers.at(_function.name));
|
||||
m_graph.functions.emplace_back(&function);
|
||||
|
||||
CFG::FunctionInfo& functionInfo = m_graph.functionInfo.at(&function);
|
||||
|
||||
ControlFlowGraphBuilder builder{m_graph, m_info, m_dialect};
|
||||
builder.m_currentFunction = &functionInfo;
|
||||
builder.m_currentBlock = functionInfo.entry;
|
||||
builder(_function.body);
|
||||
builder.m_currentBlock->exit = CFG::BasicBlock::FunctionReturn{debugDataOf(_function), &functionInfo};
|
||||
}
|
||||
|
||||
void ControlFlowGraphBuilder::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));
|
||||
|
||||
yulAssert(m_info.scopes.at(&_function.body), "");
|
||||
Scope* virtualFunctionScope = m_info.scopes.at(m_info.virtualBlocks.at(&_function).get()).get();
|
||||
yulAssert(virtualFunctionScope, "");
|
||||
|
||||
auto&& [it, inserted] = m_graph.functionInfo.emplace(std::make_pair(&function, CFG::FunctionInfo{
|
||||
bool inserted = m_graph.functionInfo.emplace(std::make_pair(&function, CFG::FunctionInfo{
|
||||
_function.debugData,
|
||||
function,
|
||||
&m_graph.makeBlock(debugDataOf(_function.body)),
|
||||
@@ -406,19 +424,11 @@ void ControlFlowGraphBuilder::operator()(FunctionDefinition const& _function)
|
||||
_retVar.debugData
|
||||
};
|
||||
}) | ranges::to<vector>
|
||||
}));
|
||||
yulAssert(inserted, "");
|
||||
CFG::FunctionInfo& functionInfo = it->second;
|
||||
|
||||
ControlFlowGraphBuilder builder{m_graph, m_info, m_dialect};
|
||||
builder.m_currentFunction = &functionInfo;
|
||||
builder.m_currentBlock = functionInfo.entry;
|
||||
builder(_function.body);
|
||||
builder.m_currentBlock->exit = CFG::BasicBlock::FunctionReturn{debugDataOf(_function), &functionInfo};
|
||||
})).second;
|
||||
yulAssert(inserted);
|
||||
}
|
||||
|
||||
|
||||
CFG::Operation const& ControlFlowGraphBuilder::visitFunctionCall(FunctionCall const& _call)
|
||||
Stack const& ControlFlowGraphBuilder::visitFunctionCall(FunctionCall const& _call)
|
||||
{
|
||||
yulAssert(m_scope, "");
|
||||
yulAssert(m_currentBlock, "");
|
||||
@@ -439,7 +449,7 @@ CFG::Operation const& ControlFlowGraphBuilder::visitFunctionCall(FunctionCall co
|
||||
}) | ranges::to<Stack>,
|
||||
// operation
|
||||
move(builtinCall)
|
||||
});
|
||||
}).output;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -456,7 +466,7 @@ CFG::Operation const& ControlFlowGraphBuilder::visitFunctionCall(FunctionCall co
|
||||
}) | ranges::to<Stack>,
|
||||
// operation
|
||||
CFG::FunctionCall{_call.debugData, function, _call}
|
||||
});
|
||||
}).output;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -464,9 +474,9 @@ Stack ControlFlowGraphBuilder::visitAssignmentRightHandSide(Expression const& _e
|
||||
{
|
||||
return std::visit(util::GenericVisitor{
|
||||
[&](FunctionCall const& _call) -> Stack {
|
||||
CFG::Operation const& operation = visitFunctionCall(_call);
|
||||
yulAssert(_expectedSlotCount == operation.output.size(), "");
|
||||
return operation.output;
|
||||
Stack const& output = visitFunctionCall(_call);
|
||||
yulAssert(_expectedSlotCount == output.size(), "");
|
||||
return output;
|
||||
},
|
||||
[&](auto const& _identifierOrLiteral) -> Stack {
|
||||
yulAssert(_expectedSlotCount == 1, "");
|
||||
|
||||
@@ -57,7 +57,8 @@ private:
|
||||
AsmAnalysisInfo const& _analysisInfo,
|
||||
Dialect const& _dialect
|
||||
);
|
||||
CFG::Operation const& visitFunctionCall(FunctionCall const&);
|
||||
void registerFunction(FunctionDefinition const& _function);
|
||||
Stack const& visitFunctionCall(FunctionCall const&);
|
||||
Stack visitAssignmentRightHandSide(Expression const& _expression, size_t _expectedSlotCount);
|
||||
|
||||
Scope::Function const& lookupFunction(YulString _name) const;
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
#include <libyul/backends/evm/EVMCodeTransform.h>
|
||||
#include <libyul/backends/evm/EVMDialect.h>
|
||||
#include <libyul/backends/evm/OptimizedEVMCodeTransform.h>
|
||||
|
||||
#include <libyul/Object.h>
|
||||
#include <libyul/Exceptions.h>
|
||||
@@ -62,19 +63,35 @@ void EVMObjectCompiler::run(Object& _object, bool _optimize)
|
||||
|
||||
yulAssert(_object.analysisInfo, "No analysis info.");
|
||||
yulAssert(_object.code, "No code.");
|
||||
// We do not catch and re-throw the stack too deep exception here because it is a YulException,
|
||||
// which should be native to this part of the code.
|
||||
CodeTransform transform{
|
||||
m_assembly,
|
||||
*_object.analysisInfo,
|
||||
*_object.code,
|
||||
m_dialect,
|
||||
context,
|
||||
_optimize,
|
||||
{},
|
||||
CodeTransform::UseNamedLabels::ForFirstFunctionOfEachName
|
||||
};
|
||||
transform(*_object.code);
|
||||
if (!transform.stackErrors().empty())
|
||||
BOOST_THROW_EXCEPTION(transform.stackErrors().front());
|
||||
if (_optimize && m_dialect.evmVersion().canOverchargeGasForCall())
|
||||
{
|
||||
auto stackErrors = OptimizedEVMCodeTransform::run(
|
||||
m_assembly,
|
||||
*_object.analysisInfo,
|
||||
*_object.code,
|
||||
m_dialect,
|
||||
context,
|
||||
OptimizedEVMCodeTransform::UseNamedLabels::ForFirstFunctionOfEachName
|
||||
);
|
||||
if (!stackErrors.empty())
|
||||
BOOST_THROW_EXCEPTION(stackErrors.front());
|
||||
}
|
||||
else
|
||||
{
|
||||
// We do not catch and re-throw the stack too deep exception here because it is a YulException,
|
||||
// which should be native to this part of the code.
|
||||
CodeTransform transform{
|
||||
m_assembly,
|
||||
*_object.analysisInfo,
|
||||
*_object.code,
|
||||
m_dialect,
|
||||
context,
|
||||
_optimize,
|
||||
{},
|
||||
CodeTransform::UseNamedLabels::ForFirstFunctionOfEachName
|
||||
};
|
||||
transform(*_object.code);
|
||||
if (!transform.stackErrors().empty())
|
||||
BOOST_THROW_EXCEPTION(transform.stackErrors().front());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ vector<StackTooDeepError> OptimizedEVMCodeTransform::run(
|
||||
Block const& _block,
|
||||
EVMDialect const& _dialect,
|
||||
BuiltinContext& _builtinContext,
|
||||
bool _useNamedLabelsForFunctions
|
||||
UseNamedLabels _useNamedLabelsForFunctions
|
||||
)
|
||||
{
|
||||
std::unique_ptr<CFG> dfg = ControlFlowGraphBuilder::build(_analysisInfo, _dialect, _block);
|
||||
@@ -170,15 +170,35 @@ void OptimizedEVMCodeTransform::operator()(CFG::Assignment const& _assignment)
|
||||
OptimizedEVMCodeTransform::OptimizedEVMCodeTransform(
|
||||
AbstractAssembly& _assembly,
|
||||
BuiltinContext& _builtinContext,
|
||||
bool _useNamedLabelsForFunctions,
|
||||
UseNamedLabels _useNamedLabelsForFunctions,
|
||||
CFG const& _dfg,
|
||||
StackLayout const& _stackLayout
|
||||
):
|
||||
m_assembly(_assembly),
|
||||
m_builtinContext(_builtinContext),
|
||||
m_useNamedLabelsForFunctions(_useNamedLabelsForFunctions),
|
||||
m_dfg(_dfg),
|
||||
m_stackLayout(_stackLayout)
|
||||
m_stackLayout(_stackLayout),
|
||||
m_functionLabels([&](){
|
||||
map<CFG::FunctionInfo const*, AbstractAssembly::LabelID> functionLabels;
|
||||
set<YulString> assignedFunctionNames;
|
||||
for (Scope::Function const* function: m_dfg.functions)
|
||||
{
|
||||
CFG::FunctionInfo const& functionInfo = m_dfg.functionInfo.at(function);
|
||||
bool nameAlreadySeen = !assignedFunctionNames.insert(function->name).second;
|
||||
if (_useNamedLabelsForFunctions == UseNamedLabels::YesAndForceUnique)
|
||||
yulAssert(!nameAlreadySeen);
|
||||
bool useNamedLabel = _useNamedLabelsForFunctions != UseNamedLabels::Never && !nameAlreadySeen;
|
||||
functionLabels[&functionInfo] = useNamedLabel ?
|
||||
m_assembly.namedLabel(
|
||||
function->name.str(),
|
||||
function->arguments.size(),
|
||||
function->returns.size(),
|
||||
functionInfo.debugData ? functionInfo.debugData->astID : nullopt
|
||||
) :
|
||||
m_assembly.newLabelId();
|
||||
}
|
||||
return functionLabels;
|
||||
}())
|
||||
{
|
||||
}
|
||||
|
||||
@@ -191,15 +211,7 @@ void OptimizedEVMCodeTransform::assertLayoutCompatibility(Stack const& _currentS
|
||||
|
||||
AbstractAssembly::LabelID OptimizedEVMCodeTransform::getFunctionLabel(Scope::Function const& _function)
|
||||
{
|
||||
CFG::FunctionInfo const& functionInfo = m_dfg.functionInfo.at(&_function);
|
||||
if (!m_functionLabels.count(&functionInfo))
|
||||
m_functionLabels[&functionInfo] = m_useNamedLabelsForFunctions ? m_assembly.namedLabel(
|
||||
functionInfo.function.name.str(),
|
||||
functionInfo.function.arguments.size(),
|
||||
functionInfo.function.returns.size(),
|
||||
{}
|
||||
) : m_assembly.newLabelId();
|
||||
return m_functionLabels[&functionInfo];
|
||||
return m_functionLabels.at(&m_dfg.functionInfo.at(&_function));
|
||||
}
|
||||
|
||||
void OptimizedEVMCodeTransform::validateSlot(StackSlot const& _slot, Expression const& _expression)
|
||||
|
||||
@@ -43,13 +43,17 @@ struct StackLayout;
|
||||
class OptimizedEVMCodeTransform
|
||||
{
|
||||
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 };
|
||||
|
||||
[[nodiscard]] static std::vector<StackTooDeepError> run(
|
||||
AbstractAssembly& _assembly,
|
||||
AsmAnalysisInfo& _analysisInfo,
|
||||
Block const& _block,
|
||||
EVMDialect const& _dialect,
|
||||
BuiltinContext& _builtinContext,
|
||||
bool _useNamedLabelsForFunctions = false
|
||||
UseNamedLabels _useNamedLabelsForFunctions
|
||||
);
|
||||
|
||||
/// Generate code for the function call @a _call. Only public for using with std::visit.
|
||||
@@ -62,7 +66,7 @@ private:
|
||||
OptimizedEVMCodeTransform(
|
||||
AbstractAssembly& _assembly,
|
||||
BuiltinContext& _builtinContext,
|
||||
bool _useNamedLabelsForFunctions,
|
||||
UseNamedLabels _useNamedLabelsForFunctions,
|
||||
CFG const& _dfg,
|
||||
StackLayout const& _stackLayout
|
||||
);
|
||||
@@ -70,6 +74,7 @@ private:
|
||||
/// Assert that it is valid to transition from @a _currentStack to @a _desiredStack.
|
||||
/// That is @a _currentStack matches each slot in @a _desiredStack that is not a JunkSlot exactly.
|
||||
static void assertLayoutCompatibility(Stack const& _currentStack, Stack const& _desiredStack);
|
||||
|
||||
/// @returns The label of the entry point of the given @a _function.
|
||||
/// Creates and stores a new label, if none exists already.
|
||||
AbstractAssembly::LabelID getFunctionLabel(Scope::Function const& _function);
|
||||
@@ -94,13 +99,12 @@ private:
|
||||
|
||||
AbstractAssembly& m_assembly;
|
||||
BuiltinContext& m_builtinContext;
|
||||
bool m_useNamedLabelsForFunctions = true;
|
||||
CFG const& m_dfg;
|
||||
StackLayout const& m_stackLayout;
|
||||
Stack m_stack;
|
||||
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> m_functionLabels;
|
||||
std::map<CFG::FunctionInfo const*, AbstractAssembly::LabelID> const m_functionLabels;
|
||||
/// Set of blocks already generated. If any of the contained blocks is ever jumped to, m_blockLabels should
|
||||
/// contain a jump label for it.
|
||||
std::set<CFG::BasicBlock const*> m_generated;
|
||||
|
||||
Reference in New Issue
Block a user