mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Initial still broken version.
This commit is contained in:
@@ -33,6 +33,7 @@ using namespace solidity::util;
|
||||
|
||||
CompilabilityChecker::CompilabilityChecker(
|
||||
Dialect const& _dialect,
|
||||
std::optional<uint8_t> _eofVersion,
|
||||
Object const& _object,
|
||||
bool _optimizeStackAllocation
|
||||
)
|
||||
@@ -50,7 +51,7 @@ CompilabilityChecker::CompilabilityChecker(
|
||||
builtinContext.subIDs[_object.name] = 1;
|
||||
for (auto const& subNode: _object.subObjects)
|
||||
builtinContext.subIDs[subNode->name] = 1;
|
||||
NoOutputAssembly assembly;
|
||||
NoOutputAssembly assembly(_eofVersion.has_value());
|
||||
CodeTransform transform(
|
||||
assembly,
|
||||
analysisInfo,
|
||||
|
||||
@@ -44,7 +44,12 @@ namespace solidity::yul
|
||||
*/
|
||||
struct CompilabilityChecker
|
||||
{
|
||||
CompilabilityChecker(Dialect const& _dialect, Object const& _object, bool _optimizeStackAllocation);
|
||||
CompilabilityChecker(
|
||||
Dialect const& _dialect,
|
||||
std::optional<uint8_t> _eofVersion,
|
||||
Object const& _object,
|
||||
bool _optimizeStackAllocation
|
||||
);
|
||||
std::map<YulString, std::set<YulString>> unreachableVariables;
|
||||
std::map<YulString, int> stackDeficit;
|
||||
};
|
||||
|
||||
@@ -184,6 +184,7 @@ void YulStack::optimize(Object& _object, bool _isCreation)
|
||||
meter = make_unique<GasMeter>(*evmDialect, _isCreation, m_optimiserSettings.expectedExecutionsPerDeployment);
|
||||
OptimiserSuite::run(
|
||||
dialect,
|
||||
m_eofVersion,
|
||||
meter.get(),
|
||||
_object,
|
||||
m_optimiserSettings.optimizeStackAllocation,
|
||||
|
||||
@@ -55,10 +55,12 @@ class AbstractAssembly
|
||||
public:
|
||||
using LabelID = size_t;
|
||||
using SubID = size_t;
|
||||
using FunctionID = uint16_t;
|
||||
enum class JumpType { Ordinary, IntoFunction, OutOfFunction };
|
||||
|
||||
virtual ~AbstractAssembly() = default;
|
||||
|
||||
virtual bool supportsFunctions() const = 0;
|
||||
/// Set a new source location valid starting from the next instruction.
|
||||
virtual void setSourceLocation(langutil::SourceLocation const& _location) = 0;
|
||||
/// Retrieve the current height of the stack. This does not have to be zero
|
||||
@@ -99,6 +101,14 @@ public:
|
||||
virtual void appendAssemblySize() = 0;
|
||||
/// Creates a new sub-assembly, which can be referenced using dataSize and dataOffset.
|
||||
virtual std::pair<std::shared_ptr<AbstractAssembly>, SubID> createSubAssembly(bool _creation, std::optional<uint8_t> _eofVersion, std::string _name = "") = 0;
|
||||
|
||||
virtual FunctionID createFunction(uint8_t _args, uint8_t _rets) = 0;
|
||||
virtual void beginFunction(FunctionID _functionID) = 0;
|
||||
virtual void endFunction() = 0;
|
||||
|
||||
virtual void appendFunctionCall(FunctionID _functionID) = 0;
|
||||
virtual void appendFunctionReturn() = 0;
|
||||
|
||||
/// Appends the offset of the given sub-assembly or data.
|
||||
virtual void appendDataOffset(std::vector<SubID> const& _subPath) = 0;
|
||||
/// Appends the size of the given sub-assembly or data.
|
||||
|
||||
@@ -123,7 +123,7 @@ inline bool canBeFreelyGenerated(StackSlot const& _slot)
|
||||
/// Control flow graph consisting of ``CFG::BasicBlock``s connected by control flow.
|
||||
struct CFG
|
||||
{
|
||||
explicit CFG() {}
|
||||
explicit CFG(bool _useFunctions): useFunctions(_useFunctions) {}
|
||||
CFG(CFG const&) = delete;
|
||||
CFG(CFG&&) = delete;
|
||||
CFG& operator=(CFG const&) = delete;
|
||||
@@ -220,6 +220,7 @@ struct CFG
|
||||
bool canContinue = true;
|
||||
};
|
||||
|
||||
bool useFunctions = false;
|
||||
/// The main entry point, i.e. the start of the outermost Yul block.
|
||||
BasicBlock* entry = nullptr;
|
||||
/// Subgraphs for functions.
|
||||
|
||||
@@ -209,10 +209,11 @@ void markNeedsCleanStack(CFG& _cfg)
|
||||
std::unique_ptr<CFG> ControlFlowGraphBuilder::build(
|
||||
AsmAnalysisInfo const& _analysisInfo,
|
||||
Dialect const& _dialect,
|
||||
std::optional<uint8_t> _eofVersion,
|
||||
Block const& _block
|
||||
)
|
||||
{
|
||||
auto result = std::make_unique<CFG>();
|
||||
auto result = std::make_unique<CFG>(_eofVersion.has_value());
|
||||
result->entry = &result->makeBlock(debugDataOf(_block));
|
||||
|
||||
ControlFlowSideEffectsCollector sideEffects(_dialect, _block);
|
||||
@@ -541,7 +542,7 @@ Stack const& ControlFlowGraphBuilder::visitFunctionCall(FunctionCall const& _cal
|
||||
Scope::Function const& function = lookupFunction(_call.functionName.name);
|
||||
canContinue = m_graph.functionInfo.at(&function).canContinue;
|
||||
Stack inputs;
|
||||
if (canContinue)
|
||||
if (!m_graph.useFunctions && canContinue)
|
||||
inputs.emplace_back(FunctionCallReturnLabelSlot{_call});
|
||||
for (auto const& arg: _call.arguments | ranges::views::reverse)
|
||||
inputs.emplace_back(std::visit(*this, arg));
|
||||
|
||||
@@ -31,7 +31,12 @@ class ControlFlowGraphBuilder
|
||||
public:
|
||||
ControlFlowGraphBuilder(ControlFlowGraphBuilder const&) = delete;
|
||||
ControlFlowGraphBuilder& operator=(ControlFlowGraphBuilder const&) = delete;
|
||||
static std::unique_ptr<CFG> build(AsmAnalysisInfo const& _analysisInfo, Dialect const& _dialect, Block const& _block);
|
||||
static std::unique_ptr<CFG> build(
|
||||
AsmAnalysisInfo const& _analysisInfo,
|
||||
Dialect const& _dialect,
|
||||
std::optional<uint8_t> _eofVersion,
|
||||
Block const& _block
|
||||
);
|
||||
|
||||
StackSlot operator()(Expression const& _literal);
|
||||
StackSlot operator()(Literal const& _literal);
|
||||
@@ -81,6 +86,7 @@ private:
|
||||
AsmAnalysisInfo const& m_info;
|
||||
std::map<FunctionDefinition const*, ControlFlowSideEffects> const& m_functionSideEffects;
|
||||
Dialect const& m_dialect;
|
||||
std::optional<uint8_t> m_eofVersion;
|
||||
CFG::BasicBlock* m_currentBlock = nullptr;
|
||||
Scope* m_scope = nullptr;
|
||||
struct ForLoopInfo
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <libyul/Dialect.h>
|
||||
#include <libyul/Scope.h>
|
||||
|
||||
#include <libyul/backends/evm/AbstractAssembly.h>
|
||||
#include <libyul/ASTForward.h>
|
||||
@@ -46,6 +47,8 @@ struct BuiltinContext
|
||||
Object const* currentObject = nullptr;
|
||||
/// Mapping from named objects to abstract assembly sub IDs.
|
||||
std::map<YulString, AbstractAssembly::SubID> subIDs;
|
||||
|
||||
std::map<Scope::Function const*, AbstractAssembly::FunctionID> functionIDs;
|
||||
};
|
||||
|
||||
struct BuiltinFunctionForEVM: public BuiltinFunction
|
||||
|
||||
@@ -86,6 +86,7 @@ void EVMObjectCompiler::run(Object& _object, bool _optimize)
|
||||
*_object.analysisInfo,
|
||||
*_object.code,
|
||||
m_dialect,
|
||||
m_eofVersion,
|
||||
context,
|
||||
OptimizedEVMCodeTransform::UseNamedLabels::ForFirstFunctionOfEachName
|
||||
);
|
||||
|
||||
@@ -44,6 +44,11 @@ EthAssemblyAdapter::EthAssemblyAdapter(evmasm::Assembly& _assembly):
|
||||
{
|
||||
}
|
||||
|
||||
bool EthAssemblyAdapter::supportsFunctions() const
|
||||
{
|
||||
return m_assembly.supportsFunctions();
|
||||
}
|
||||
|
||||
void EthAssemblyAdapter::setSourceLocation(SourceLocation const& _location)
|
||||
{
|
||||
m_assembly.setSourceLocation(_location);
|
||||
@@ -129,6 +134,31 @@ pair<shared_ptr<AbstractAssembly>, AbstractAssembly::SubID> EthAssemblyAdapter::
|
||||
return {make_shared<EthAssemblyAdapter>(*assembly), static_cast<size_t>(sub.data())};
|
||||
}
|
||||
|
||||
AbstractAssembly::FunctionID EthAssemblyAdapter::createFunction(uint8_t _args, uint8_t _rets)
|
||||
{
|
||||
return m_assembly.createFunction(_args, _rets);
|
||||
}
|
||||
|
||||
void EthAssemblyAdapter::beginFunction(AbstractAssembly::FunctionID _functionID)
|
||||
{
|
||||
m_assembly.beginFunction(_functionID);
|
||||
}
|
||||
|
||||
void EthAssemblyAdapter::endFunction()
|
||||
{
|
||||
m_assembly.endFunction();
|
||||
}
|
||||
|
||||
void EthAssemblyAdapter::appendFunctionReturn()
|
||||
{
|
||||
m_assembly.appendFunctionReturn();
|
||||
}
|
||||
|
||||
void EthAssemblyAdapter::appendFunctionCall(FunctionID _functionID)
|
||||
{
|
||||
m_assembly.appendFunctionCall(_functionID);
|
||||
}
|
||||
|
||||
void EthAssemblyAdapter::appendDataOffset(vector<AbstractAssembly::SubID> const& _subPath)
|
||||
{
|
||||
if (auto it = m_dataHashBySubId.find(_subPath[0]); it != m_dataHashBySubId.end())
|
||||
|
||||
@@ -40,6 +40,7 @@ class EthAssemblyAdapter: public AbstractAssembly
|
||||
{
|
||||
public:
|
||||
explicit EthAssemblyAdapter(evmasm::Assembly& _assembly);
|
||||
bool supportsFunctions() const override;
|
||||
void setSourceLocation(langutil::SourceLocation const& _location) override;
|
||||
int stackHeight() const override;
|
||||
void setStackHeight(int height) override;
|
||||
@@ -56,6 +57,11 @@ public:
|
||||
void appendJumpToIf(LabelID _labelId, JumpType _jumpType) override;
|
||||
void appendAssemblySize() override;
|
||||
std::pair<std::shared_ptr<AbstractAssembly>, SubID> createSubAssembly(bool _creation, std::optional<uint8_t> _eofVersion, std::string _name = {}) override;
|
||||
AbstractAssembly::FunctionID createFunction(uint8_t _args, uint8_t _rets) override;
|
||||
void beginFunction(AbstractAssembly::FunctionID _functionID) override;
|
||||
void endFunction() override;
|
||||
void appendFunctionCall(FunctionID _functionID) override;
|
||||
void appendFunctionReturn() override;
|
||||
void appendDataOffset(std::vector<SubID> const& _subPath) override;
|
||||
void appendDataSize(std::vector<SubID> const& _subPath) override;
|
||||
SubID appendData(bytes const& _data) override;
|
||||
|
||||
@@ -104,6 +104,33 @@ pair<shared_ptr<AbstractAssembly>, AbstractAssembly::SubID> NoOutputAssembly::cr
|
||||
return {};
|
||||
}
|
||||
|
||||
AbstractAssembly::FunctionID NoOutputAssembly::createFunction(uint8_t _args, uint8_t _rets)
|
||||
{
|
||||
yulAssert(m_context->numFunctions <= std::numeric_limits<AbstractAssembly::FunctionID>::max());
|
||||
AbstractAssembly::FunctionID id = static_cast<AbstractAssembly::FunctionID>(m_context->numFunctions++);
|
||||
m_context->functionSignatures[id] = std::make_pair(_args, _rets);
|
||||
return id;
|
||||
}
|
||||
|
||||
void NoOutputAssembly::beginFunction(FunctionID)
|
||||
{
|
||||
}
|
||||
|
||||
void NoOutputAssembly::endFunction()
|
||||
{
|
||||
}
|
||||
|
||||
void NoOutputAssembly::appendFunctionCall(FunctionID _functionID)
|
||||
{
|
||||
auto [args, rets] = m_context->functionSignatures.at(_functionID);
|
||||
m_stackHeight += static_cast<int>(rets) - static_cast<int>(args);
|
||||
}
|
||||
|
||||
void NoOutputAssembly::appendFunctionReturn()
|
||||
{
|
||||
m_stackHeight = 0;
|
||||
}
|
||||
|
||||
void NoOutputAssembly::appendDataOffset(std::vector<AbstractAssembly::SubID> const&)
|
||||
{
|
||||
appendInstruction(evmasm::Instruction::PUSH1);
|
||||
|
||||
@@ -37,6 +37,13 @@ struct SourceLocation;
|
||||
namespace solidity::yul
|
||||
{
|
||||
|
||||
class NoOutputAssembly;
|
||||
|
||||
struct NoOutputAssemblyContext
|
||||
{
|
||||
size_t numFunctions = 0;
|
||||
std::map<uint16_t, std::pair<uint8_t, uint8_t>> functionSignatures;
|
||||
};
|
||||
|
||||
/**
|
||||
* Assembly class that just ignores everything and only performs stack counting.
|
||||
@@ -45,9 +52,13 @@ namespace solidity::yul
|
||||
class NoOutputAssembly: public AbstractAssembly
|
||||
{
|
||||
public:
|
||||
explicit NoOutputAssembly() { }
|
||||
explicit NoOutputAssembly(bool _hasFunctions): m_hasFunctions(_hasFunctions), m_context(std::make_shared<NoOutputAssemblyContext>()) { }
|
||||
NoOutputAssembly(bool _hasFunctions, std::shared_ptr<NoOutputAssemblyContext> _context): m_hasFunctions(_hasFunctions), m_context(_context) {}
|
||||
|
||||
~NoOutputAssembly() override = default;
|
||||
|
||||
bool supportsFunctions() const override { return m_hasFunctions; }
|
||||
|
||||
void setSourceLocation(langutil::SourceLocation const&) override {}
|
||||
int stackHeight() const override { return m_stackHeight; }
|
||||
void setStackHeight(int height) override { m_stackHeight = height; }
|
||||
@@ -66,6 +77,11 @@ public:
|
||||
|
||||
void appendAssemblySize() override;
|
||||
std::pair<std::shared_ptr<AbstractAssembly>, SubID> createSubAssembly(bool _creation, std::optional<uint8_t> _eofVersion, std::string _name = "") override;
|
||||
FunctionID createFunction(uint8_t _args, uint8_t rets) override;
|
||||
void beginFunction(FunctionID) override;
|
||||
void endFunction() override;
|
||||
void appendFunctionCall(FunctionID _functionID) override;
|
||||
void appendFunctionReturn() override;
|
||||
void appendDataOffset(std::vector<SubID> const& _subPath) override;
|
||||
void appendDataSize(std::vector<SubID> const& _subPath) override;
|
||||
SubID appendData(bytes const& _data) override;
|
||||
@@ -78,6 +94,8 @@ public:
|
||||
void markAsInvalid() override {}
|
||||
|
||||
private:
|
||||
bool m_hasFunctions = false;
|
||||
std::shared_ptr<NoOutputAssemblyContext> m_context;
|
||||
int m_stackHeight = 0;
|
||||
};
|
||||
|
||||
|
||||
@@ -45,12 +45,28 @@ vector<StackTooDeepError> OptimizedEVMCodeTransform::run(
|
||||
AsmAnalysisInfo& _analysisInfo,
|
||||
Block const& _block,
|
||||
EVMDialect const& _dialect,
|
||||
std::optional<uint8_t> _eofVersion,
|
||||
BuiltinContext& _builtinContext,
|
||||
UseNamedLabels _useNamedLabelsForFunctions
|
||||
)
|
||||
{
|
||||
std::unique_ptr<CFG> dfg = ControlFlowGraphBuilder::build(_analysisInfo, _dialect, _block);
|
||||
std::unique_ptr<CFG> dfg = ControlFlowGraphBuilder::build(_analysisInfo, _dialect, _eofVersion, _block);
|
||||
yulAssert(_eofVersion.has_value() == dfg->useFunctions);
|
||||
StackLayout stackLayout = StackLayoutGenerator::run(*dfg);
|
||||
|
||||
if (dfg->useFunctions)
|
||||
for (Scope::Function const* function: dfg->functions)
|
||||
{
|
||||
auto const& info = dfg->functionInfo.at(function);
|
||||
yulAssert(info.parameters.size() <= 0xFF);
|
||||
yulAssert(info.returnVariables.size() <= 0xFF);
|
||||
auto functionID = _assembly.createFunction(
|
||||
static_cast<uint8_t>(info.parameters.size()),
|
||||
static_cast<uint8_t>(info.returnVariables.size())
|
||||
);
|
||||
_builtinContext.functionIDs[function] = functionID;
|
||||
}
|
||||
|
||||
OptimizedEVMCodeTransform optimizedCodeTransform(
|
||||
_assembly,
|
||||
_builtinContext,
|
||||
@@ -68,10 +84,11 @@ vector<StackTooDeepError> OptimizedEVMCodeTransform::run(
|
||||
|
||||
void OptimizedEVMCodeTransform::operator()(CFG::FunctionCall const& _call)
|
||||
{
|
||||
bool useReturnLabel = !m_dfg.useFunctions && _call.canContinue;
|
||||
// Validate stack.
|
||||
{
|
||||
yulAssert(m_assembly.stackHeight() == static_cast<int>(m_stack.size()), "");
|
||||
yulAssert(m_stack.size() >= _call.function.get().arguments.size() + (_call.canContinue ? 1 : 0), "");
|
||||
yulAssert(m_stack.size() >= _call.function.get().arguments.size() + (useReturnLabel ? 1 : 0), "");
|
||||
// Assert that we got the correct arguments on stack for the call.
|
||||
for (auto&& [arg, slot]: ranges::zip_view(
|
||||
_call.functionCall.get().arguments | ranges::views::reverse,
|
||||
@@ -79,7 +96,7 @@ void OptimizedEVMCodeTransform::operator()(CFG::FunctionCall const& _call)
|
||||
))
|
||||
validateSlot(slot, arg);
|
||||
// Assert that we got the correct return label on stack.
|
||||
if (_call.canContinue)
|
||||
if (useReturnLabel)
|
||||
{
|
||||
auto const* returnLabelSlot = get_if<FunctionCallReturnLabelSlot>(
|
||||
&m_stack.at(m_stack.size() - _call.functionCall.get().arguments.size() - 1)
|
||||
@@ -91,19 +108,22 @@ void OptimizedEVMCodeTransform::operator()(CFG::FunctionCall const& _call)
|
||||
// Emit code.
|
||||
{
|
||||
m_assembly.setSourceLocation(originLocationOf(_call));
|
||||
m_assembly.appendJumpTo(
|
||||
getFunctionLabel(_call.function),
|
||||
static_cast<int>(_call.function.get().returns.size() - _call.function.get().arguments.size()) - (_call.canContinue ? 1 : 0),
|
||||
AbstractAssembly::JumpType::IntoFunction
|
||||
);
|
||||
if (_call.canContinue)
|
||||
if (m_dfg.useFunctions)
|
||||
m_assembly.appendFunctionCall(m_builtinContext.functionIDs.at(&_call.function.get()));
|
||||
else
|
||||
m_assembly.appendJumpTo(
|
||||
getFunctionLabel(_call.function),
|
||||
static_cast<int>(_call.function.get().returns.size() - _call.function.get().arguments.size()) - (_call.canContinue ? 1 : 0),
|
||||
AbstractAssembly::JumpType::IntoFunction
|
||||
);
|
||||
if (useReturnLabel)
|
||||
m_assembly.appendLabel(m_returnLabels.at(&_call.functionCall.get()));
|
||||
}
|
||||
|
||||
// Update stack.
|
||||
{
|
||||
// Remove arguments and return label from m_stack.
|
||||
for (size_t i = 0; i < _call.function.get().arguments.size() + (_call.canContinue ? 1 : 0); ++i)
|
||||
for (size_t i = 0; i < _call.function.get().arguments.size() + (useReturnLabel ? 1 : 0); ++i)
|
||||
m_stack.pop_back();
|
||||
// Push return values to m_stack.
|
||||
for (size_t index: ranges::views::iota(0u, _call.function.get().returns.size()))
|
||||
@@ -184,7 +204,7 @@ OptimizedEVMCodeTransform::OptimizedEVMCodeTransform(
|
||||
m_builtinContext(_builtinContext),
|
||||
m_dfg(_dfg),
|
||||
m_stackLayout(_stackLayout),
|
||||
m_functionLabels([&](){
|
||||
m_functionLabels(_dfg.useFunctions ? decltype(m_functionLabels)() : [&](){
|
||||
map<CFG::FunctionInfo const*, AbstractAssembly::LabelID> functionLabels;
|
||||
set<YulString> assignedFunctionNames;
|
||||
for (Scope::Function const* function: m_dfg.functions)
|
||||
@@ -217,6 +237,7 @@ void OptimizedEVMCodeTransform::assertLayoutCompatibility(Stack const& _currentS
|
||||
|
||||
AbstractAssembly::LabelID OptimizedEVMCodeTransform::getFunctionLabel(Scope::Function const& _function)
|
||||
{
|
||||
yulAssert(!m_dfg.useFunctions);
|
||||
return m_functionLabels.at(&m_dfg.functionInfo.at(&_function));
|
||||
}
|
||||
|
||||
@@ -491,11 +512,15 @@ void OptimizedEVMCodeTransform::operator()(CFG::BasicBlock const& _block)
|
||||
Stack exitStack = m_currentFunctionInfo->returnVariables | ranges::views::transform([](auto const& _varSlot){
|
||||
return StackSlot{_varSlot};
|
||||
}) | ranges::to<Stack>;
|
||||
exitStack.emplace_back(FunctionReturnLabelSlot{_functionReturn.info->function});
|
||||
if (!m_dfg.useFunctions)
|
||||
exitStack.emplace_back(FunctionReturnLabelSlot{_functionReturn.info->function});
|
||||
|
||||
// Create the function return layout and jump.
|
||||
createStackLayout(debugDataOf(_functionReturn), exitStack);
|
||||
m_assembly.appendJump(0, AbstractAssembly::JumpType::OutOfFunction);
|
||||
if (m_dfg.useFunctions)
|
||||
m_assembly.appendFunctionReturn();
|
||||
else
|
||||
m_assembly.appendJump(0, AbstractAssembly::JumpType::OutOfFunction);
|
||||
},
|
||||
[&](CFG::BasicBlock::Terminated const&)
|
||||
{
|
||||
@@ -516,25 +541,31 @@ void OptimizedEVMCodeTransform::operator()(CFG::BasicBlock const& _block)
|
||||
|
||||
void OptimizedEVMCodeTransform::operator()(CFG::FunctionInfo const& _functionInfo)
|
||||
{
|
||||
bool useReturnLabel = !m_dfg.useFunctions && _functionInfo.canContinue;
|
||||
yulAssert(!m_currentFunctionInfo, "");
|
||||
ScopedSaveAndRestore currentFunctionInfoRestore(m_currentFunctionInfo, &_functionInfo);
|
||||
|
||||
yulAssert(m_stack.empty() && m_assembly.stackHeight() == 0, "");
|
||||
|
||||
// Create function entry layout in m_stack.
|
||||
if (_functionInfo.canContinue)
|
||||
if (useReturnLabel)
|
||||
m_stack.emplace_back(FunctionReturnLabelSlot{_functionInfo.function});
|
||||
for (auto const& param: _functionInfo.parameters | ranges::views::reverse)
|
||||
m_stack.emplace_back(param);
|
||||
if (m_dfg.useFunctions)
|
||||
m_assembly.beginFunction(m_builtinContext.functionIDs[&_functionInfo.function]);
|
||||
m_assembly.setStackHeight(static_cast<int>(m_stack.size()));
|
||||
|
||||
m_assembly.setSourceLocation(originLocationOf(_functionInfo));
|
||||
m_assembly.appendLabel(getFunctionLabel(_functionInfo.function));
|
||||
if (!m_dfg.useFunctions)
|
||||
m_assembly.appendLabel(getFunctionLabel(_functionInfo.function));
|
||||
|
||||
// Create the entry layout of the function body block and visit.
|
||||
createStackLayout(debugDataOf(_functionInfo), m_stackLayout.blockInfos.at(_functionInfo.entry).entryLayout);
|
||||
(*this)(*_functionInfo.entry);
|
||||
|
||||
m_stack.clear();
|
||||
if (m_dfg.useFunctions)
|
||||
m_assembly.endFunction();
|
||||
m_assembly.setStackHeight(0);
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ public:
|
||||
AsmAnalysisInfo& _analysisInfo,
|
||||
Block const& _block,
|
||||
EVMDialect const& _dialect,
|
||||
std::optional<uint8_t> _eofVersion,
|
||||
BuiltinContext& _builtinContext,
|
||||
UseNamedLabels _useNamedLabelsForFunctions
|
||||
);
|
||||
|
||||
@@ -50,7 +50,7 @@ using namespace std;
|
||||
|
||||
StackLayout StackLayoutGenerator::run(CFG const& _cfg)
|
||||
{
|
||||
StackLayout stackLayout;
|
||||
StackLayout stackLayout{_cfg.useFunctions, {}, {}};
|
||||
StackLayoutGenerator{stackLayout}.processEntryPoint(*_cfg.entry);
|
||||
|
||||
for (auto& functionInfo: _cfg.functionInfo | ranges::views::values)
|
||||
@@ -71,7 +71,7 @@ map<YulString, vector<StackLayoutGenerator::StackTooDeep>> StackLayoutGenerator:
|
||||
|
||||
vector<StackLayoutGenerator::StackTooDeep> StackLayoutGenerator::reportStackTooDeep(CFG const& _cfg, YulString _functionName)
|
||||
{
|
||||
StackLayout stackLayout;
|
||||
StackLayout stackLayout{_cfg.useFunctions, {}, {}};
|
||||
CFG::FunctionInfo const* functionInfo = nullptr;
|
||||
if (!_functionName.empty())
|
||||
{
|
||||
@@ -463,7 +463,9 @@ optional<Stack> StackLayoutGenerator::getExitLayoutOrStageDependencies(
|
||||
Stack stack = _functionReturn.info->returnVariables | ranges::views::transform([](auto const& _varSlot){
|
||||
return StackSlot{_varSlot};
|
||||
}) | ranges::to<Stack>;
|
||||
stack.emplace_back(FunctionReturnLabelSlot{_functionReturn.info->function});
|
||||
|
||||
if (!m_layout.useFunctions)
|
||||
stack.emplace_back(FunctionReturnLabelSlot{_functionReturn.info->function});
|
||||
return stack;
|
||||
},
|
||||
[&](CFG::BasicBlock::Terminated const&) -> std::optional<Stack>
|
||||
|
||||
@@ -37,6 +37,7 @@ struct StackLayout
|
||||
/// The resulting stack layout after executing the block.
|
||||
Stack exitLayout;
|
||||
};
|
||||
bool useFunctions = false;
|
||||
std::map<CFG::BasicBlock const*, BlockInfo> blockInfos;
|
||||
/// For each operation the complete stack layout that:
|
||||
/// - has the slots required for the operation at the stack top.
|
||||
|
||||
@@ -74,6 +74,7 @@ Object EVMToEwasmTranslator::run(Object const& _object)
|
||||
// expectedExecutionsPerDeployment is currently unused.
|
||||
OptimiserStepContext context{
|
||||
m_dialect,
|
||||
nullopt,
|
||||
nameDispenser,
|
||||
reservedIdentifiers,
|
||||
frontend::OptimiserSettings::standard().expectedExecutionsPerDeployment
|
||||
|
||||
@@ -35,6 +35,7 @@ class NameDispenser;
|
||||
struct OptimiserStepContext
|
||||
{
|
||||
Dialect const& dialect;
|
||||
std::optional<uint8_t> eofVersion;
|
||||
NameDispenser& dispenser;
|
||||
std::set<YulString> const& reservedIdentifiers;
|
||||
/// The value nullopt represents creation code
|
||||
|
||||
@@ -238,6 +238,7 @@ void eliminateVariablesOptimizedCodegen(
|
||||
|
||||
bool StackCompressor::run(
|
||||
Dialect const& _dialect,
|
||||
std::optional<uint8_t> _eofVersion,
|
||||
Object& _object,
|
||||
bool _optimizeStackAllocation,
|
||||
size_t _maxIterations
|
||||
@@ -258,7 +259,7 @@ bool StackCompressor::run(
|
||||
if (usesOptimizedCodeGenerator)
|
||||
{
|
||||
yul::AsmAnalysisInfo analysisInfo = yul::AsmAnalyzer::analyzeStrictAssertCorrect(_dialect, _object);
|
||||
unique_ptr<CFG> cfg = ControlFlowGraphBuilder::build(analysisInfo, _dialect, *_object.code);
|
||||
unique_ptr<CFG> cfg = ControlFlowGraphBuilder::build(analysisInfo, _dialect, _eofVersion, *_object.code);
|
||||
eliminateVariablesOptimizedCodegen(
|
||||
_dialect,
|
||||
*_object.code,
|
||||
@@ -269,7 +270,7 @@ bool StackCompressor::run(
|
||||
else
|
||||
for (size_t iterations = 0; iterations < _maxIterations; iterations++)
|
||||
{
|
||||
map<YulString, int> stackSurplus = CompilabilityChecker(_dialect, _object, _optimizeStackAllocation).stackDeficit;
|
||||
map<YulString, int> stackSurplus = CompilabilityChecker(_dialect, _eofVersion, _object, _optimizeStackAllocation).stackDeficit;
|
||||
if (stackSurplus.empty())
|
||||
return true;
|
||||
eliminateVariables(
|
||||
|
||||
@@ -48,6 +48,7 @@ public:
|
||||
/// @returns true if it was successful.
|
||||
static bool run(
|
||||
Dialect const& _dialect,
|
||||
std::optional<uint8_t> _eofVersion,
|
||||
Object& _object,
|
||||
bool _optimizeStackAllocation,
|
||||
size_t _maxIterations
|
||||
|
||||
@@ -131,12 +131,13 @@ void StackLimitEvader::run(
|
||||
if (evmDialect && evmDialect->evmVersion().canOverchargeGasForCall())
|
||||
{
|
||||
yul::AsmAnalysisInfo analysisInfo = yul::AsmAnalyzer::analyzeStrictAssertCorrect(*evmDialect, _object);
|
||||
unique_ptr<CFG> cfg = ControlFlowGraphBuilder::build(analysisInfo, *evmDialect, *_object.code);
|
||||
unique_ptr<CFG> cfg = ControlFlowGraphBuilder::build(analysisInfo, *evmDialect, _context.eofVersion, *_object.code);
|
||||
run(_context, _object, StackLayoutGenerator::reportStackTooDeep(*cfg));
|
||||
}
|
||||
else
|
||||
run(_context, _object, CompilabilityChecker{
|
||||
_context.dialect,
|
||||
_context.eofVersion,
|
||||
_object,
|
||||
true
|
||||
}.unreachableVariables);
|
||||
|
||||
@@ -135,6 +135,7 @@ void outputPerformanceMetrics(map<string, int64_t> const& _metrics)
|
||||
|
||||
void OptimiserSuite::run(
|
||||
Dialect const& _dialect,
|
||||
std::optional<uint8_t> _eofVersion,
|
||||
GasMeter const* _meter,
|
||||
Object& _object,
|
||||
bool _optimizeStackAllocation,
|
||||
@@ -161,7 +162,7 @@ void OptimiserSuite::run(
|
||||
Block& ast = *_object.code;
|
||||
|
||||
NameDispenser dispenser{_dialect, ast, reservedIdentifiers};
|
||||
OptimiserStepContext context{_dialect, dispenser, reservedIdentifiers, _expectedExecutionsPerDeployment};
|
||||
OptimiserStepContext context{_dialect, _eofVersion, dispenser, reservedIdentifiers, _expectedExecutionsPerDeployment};
|
||||
|
||||
OptimiserSuite suite(context, Debug::None);
|
||||
|
||||
@@ -182,6 +183,7 @@ void OptimiserSuite::run(
|
||||
if (!usesOptimizedCodeGenerator)
|
||||
StackCompressor::run(
|
||||
_dialect,
|
||||
_eofVersion,
|
||||
_object,
|
||||
_optimizeStackAllocation,
|
||||
stackCompressorMaxIterations
|
||||
@@ -202,6 +204,7 @@ void OptimiserSuite::run(
|
||||
{
|
||||
StackCompressor::run(
|
||||
_dialect,
|
||||
_eofVersion,
|
||||
_object,
|
||||
_optimizeStackAllocation,
|
||||
stackCompressorMaxIterations
|
||||
|
||||
@@ -64,6 +64,7 @@ public:
|
||||
/// The value nullopt for `_expectedExecutionsPerDeployment` represents creation code.
|
||||
static void run(
|
||||
Dialect const& _dialect,
|
||||
std::optional<uint8_t> _eofVersion,
|
||||
GasMeter const* _meter,
|
||||
Object& _object,
|
||||
bool _optimizeStackAllocation,
|
||||
|
||||
Reference in New Issue
Block a user