Modify parser and optimizer.

This commit is contained in:
chriseth 2019-05-20 14:30:32 +02:00
parent 46d9df7574
commit 1dc15d5864
20 changed files with 117 additions and 61 deletions

View File

@ -98,6 +98,8 @@ map<YulString, BuiltinFunctionForEVM> createBuiltins(langutil::EVMVersion _evmVe
if ( if (
!dev::eth::isDupInstruction(instr.second) && !dev::eth::isDupInstruction(instr.second) &&
!dev::eth::isSwapInstruction(instr.second) && !dev::eth::isSwapInstruction(instr.second) &&
instr.second != eth::Instruction::JUMP &&
instr.second != eth::Instruction::JUMPI &&
_evmVersion.hasOpcode(instr.second) _evmVersion.hasOpcode(instr.second)
) )
builtins.emplace(createEVMFunction(instr.first, instr.second)); builtins.emplace(createEVMFunction(instr.first, instr.second));

View File

@ -35,9 +35,9 @@ namespace
ExpressionStatement makePopExpressionStatement(langutil::SourceLocation const& _location, Expression&& _expression) ExpressionStatement makePopExpressionStatement(langutil::SourceLocation const& _location, Expression&& _expression)
{ {
return {_location, FunctionalInstruction{ return {_location, FunctionCall{
_location, _location,
dev::eth::Instruction::POP, Identifier{_location, "pop"_yulstring},
{std::move(_expression)} {std::move(_expression)}
}}; }};
} }
@ -84,9 +84,9 @@ OptionalStatements reduceSingleCaseSwitch(Switch& _switchStmt)
if (switchCase.value) if (switchCase.value)
return make_vector<Statement>(If{ return make_vector<Statement>(If{
std::move(_switchStmt.location), std::move(_switchStmt.location),
make_unique<Expression>(FunctionalInstruction{ make_unique<Expression>(FunctionCall{
std::move(loc), loc,
dev::eth::Instruction::EQ, Identifier{loc, "eq"_yulstring},
{std::move(*switchCase.value), std::move(*_switchStmt.expression)} {std::move(*switchCase.value), std::move(*_switchStmt.expression)}
}), }),
std::move(switchCase.body) std::move(switchCase.body)
@ -122,7 +122,7 @@ void ControlFlowSimplifier::visit(Statement& _st)
if (!forLoop.body.statements.empty()) if (!forLoop.body.statements.empty())
{ {
bool isTerminating = false; bool isTerminating = false;
TerminationFinder::ControlFlow controlFlow = TerminationFinder::controlFlowKind(forLoop.body.statements.back()); TerminationFinder::ControlFlow controlFlow = TerminationFinder{m_dialect}.controlFlowKind(forLoop.body.statements.back());
if (controlFlow == TerminationFinder::ControlFlow::Break) if (controlFlow == TerminationFinder::ControlFlow::Break)
{ {
isTerminating = true; isTerminating = true;

View File

@ -20,6 +20,7 @@
namespace yul namespace yul
{ {
struct Dialect;
/** /**
* Simplifies several control-flow structures: * Simplifies several control-flow structures:
@ -45,6 +46,8 @@ namespace yul
class ControlFlowSimplifier: public ASTModifier class ControlFlowSimplifier: public ASTModifier
{ {
public: public:
ControlFlowSimplifier(Dialect const& _dialect): m_dialect(_dialect) {}
using ASTModifier::operator(); using ASTModifier::operator();
void operator()(Break&) override { ++m_numBreakStatements; } void operator()(Break&) override { ++m_numBreakStatements; }
void operator()(Continue&) override { ++m_numContinueStatements; } void operator()(Continue&) override { ++m_numContinueStatements; }
@ -55,6 +58,7 @@ public:
private: private:
void simplify(std::vector<Statement>& _statements); void simplify(std::vector<Statement>& _statements);
Dialect const& m_dialect;
size_t m_numBreakStatements = 0; size_t m_numBreakStatements = 0;
size_t m_numContinueStatements = 0; size_t m_numContinueStatements = 0;
}; };

View File

@ -42,7 +42,7 @@ void DeadCodeEliminator::operator()(Block& _block)
{ {
TerminationFinder::ControlFlow controlFlowChange; TerminationFinder::ControlFlow controlFlowChange;
size_t index; size_t index;
tie(controlFlowChange, index) = TerminationFinder::firstUnconditionalControlFlowChange(_block.statements); tie(controlFlowChange, index) = TerminationFinder{m_dialect}.firstUnconditionalControlFlowChange(_block.statements);
// Erase everything after the terminating statement that is not a function definition. // Erase everything after the terminating statement that is not a function definition.
if (controlFlowChange != TerminationFinder::ControlFlow::FlowOut && index != size_t(-1)) if (controlFlowChange != TerminationFinder::ControlFlow::FlowOut && index != size_t(-1))

View File

@ -28,6 +28,7 @@
namespace yul namespace yul
{ {
struct Dialect;
/** /**
* Optimisation stage that removes unreachable code * Optimisation stage that removes unreachable code
@ -46,9 +47,14 @@ namespace yul
class DeadCodeEliminator: public ASTModifier class DeadCodeEliminator: public ASTModifier
{ {
public: public:
DeadCodeEliminator(Dialect const& _dialect): m_dialect(_dialect) {}
using ASTModifier::operator(); using ASTModifier::operator();
void operator()(ForLoop& _for) override; void operator()(ForLoop& _for) override;
void operator()(Block& _block) override; void operator()(Block& _block) override;
private:
Dialect const& m_dialect;
}; };
} }

View File

@ -22,6 +22,7 @@
#include <libyul/AsmData.h> #include <libyul/AsmData.h>
#include <libyul/Exceptions.h> #include <libyul/Exceptions.h>
#include <libyul/backends/evm/EVMDialect.h>
#include <libevmasm/Instruction.h> #include <libevmasm/Instruction.h>
@ -92,9 +93,9 @@ void CodeSize::visit(Expression const& _expression)
} }
size_t CodeCost::codeCost(Expression const& _expr) size_t CodeCost::codeCost(Dialect const& _dialect, Expression const& _expr)
{ {
CodeCost cc; CodeCost cc(_dialect);
cc.visit(_expr); cc.visit(_expr);
return cc.m_cost; return cc.m_cost;
} }
@ -102,23 +103,26 @@ size_t CodeCost::codeCost(Expression const& _expr)
void CodeCost::operator()(FunctionCall const& _funCall) void CodeCost::operator()(FunctionCall const& _funCall)
{ {
yulAssert(m_cost >= 1, "Should assign cost one in visit(Expression).");
m_cost += 49;
ASTWalker::operator()(_funCall); ASTWalker::operator()(_funCall);
if (EVMDialect const* dialect = dynamic_cast<EVMDialect const*>(&m_dialect))
if (BuiltinFunctionForEVM const* f = dialect->builtin(_funCall.functionName.name))
if (f->instruction)
{
addInstructionCost(*f->instruction);
return;
}
m_cost += 49;
} }
void CodeCost::operator()(FunctionalInstruction const& _instr) void CodeCost::operator()(FunctionalInstruction const& _instr)
{ {
yulAssert(m_cost >= 1, "Should assign cost one in visit(Expression)."); yulAssert(m_cost >= 1, "Should assign cost one in visit(Expression).");
dev::eth::Tier gasPriceTier = dev::eth::instructionInfo(_instr.instruction).gasPriceTier; addInstructionCost(_instr.instruction);
if (gasPriceTier < dev::eth::Tier::VeryLow)
m_cost -= 1;
else if (gasPriceTier < dev::eth::Tier::High)
m_cost += 1;
else
m_cost += 49;
ASTWalker::operator()(_instr); ASTWalker::operator()(_instr);
} }
void CodeCost::operator()(Literal const& _literal) void CodeCost::operator()(Literal const& _literal)
{ {
yulAssert(m_cost >= 1, "Should assign cost one in visit(Expression)."); yulAssert(m_cost >= 1, "Should assign cost one in visit(Expression).");
@ -151,6 +155,17 @@ void CodeCost::visit(Expression const& _expression)
ASTWalker::visit(_expression); ASTWalker::visit(_expression);
} }
void CodeCost::addInstructionCost(eth::Instruction _instruction)
{
dev::eth::Tier gasPriceTier = dev::eth::instructionInfo(_instruction).gasPriceTier;
if (gasPriceTier < dev::eth::Tier::VeryLow)
m_cost -= 1;
else if (gasPriceTier < dev::eth::Tier::High)
m_cost += 1;
else
m_cost += 49;
}
void AssignmentCounter::operator()(Assignment const& _assignment) void AssignmentCounter::operator()(Assignment const& _assignment)
{ {
for (auto const& variable: _assignment.variableNames) for (auto const& variable: _assignment.variableNames)

View File

@ -22,9 +22,13 @@
#include <libyul/optimiser/ASTWalker.h> #include <libyul/optimiser/ASTWalker.h>
#include <libevmasm/Instruction.h>
namespace yul namespace yul
{ {
struct Dialect;
/** /**
* Metric for the size of code. * Metric for the size of code.
* More specifically, the number of AST nodes. * More specifically, the number of AST nodes.
@ -71,9 +75,11 @@ private:
class CodeCost: public ASTWalker class CodeCost: public ASTWalker
{ {
public: public:
static size_t codeCost(Expression const& _expression); static size_t codeCost(Dialect const& _dialect, Expression const& _expression);
private: private:
CodeCost(Dialect const& _dialect): m_dialect(_dialect) {}
void operator()(FunctionCall const& _funCall) override; void operator()(FunctionCall const& _funCall) override;
void operator()(FunctionalInstruction const& _instr) override; void operator()(FunctionalInstruction const& _instr) override;
void operator()(Literal const& _literal) override; void operator()(Literal const& _literal) override;
@ -81,6 +87,9 @@ private:
void visit(Expression const& _expression) override; void visit(Expression const& _expression) override;
private: private:
void addInstructionCost(dev::eth::Instruction _instruction);
Dialect const& m_dialect;
size_t m_cost = 0; size_t m_cost = 0;
}; };

View File

@ -77,7 +77,7 @@ void Rematerialiser::visit(Expression& _e)
assertThrow(m_value.at(name), OptimizerException, ""); assertThrow(m_value.at(name), OptimizerException, "");
auto const& value = *m_value.at(name); auto const& value = *m_value.at(name);
size_t refs = m_referenceCounts[name]; size_t refs = m_referenceCounts[name];
size_t cost = CodeCost::codeCost(value); size_t cost = CodeCost::codeCost(m_dialect, value);
if (refs <= 1 || cost == 0 || (refs <= 5 && cost <= 1) || m_varsToAlwaysRematerialize.count(name)) if (refs <= 1 || cost == 0 || (refs <= 5 && cost <= 1) || m_varsToAlwaysRematerialize.count(name))
{ {
assertThrow(m_referenceCounts[name] > 0, OptimizerException, ""); assertThrow(m_referenceCounts[name] > 0, OptimizerException, "");

View File

@ -23,6 +23,7 @@
#include <libyul/Exceptions.h> #include <libyul/Exceptions.h>
#include <libyul/AsmData.h> #include <libyul/AsmData.h>
#include <libyul/Dialect.h> #include <libyul/Dialect.h>
#include <libyul/backends/evm/EVMDialect.h>
#include <libevmasm/SemanticInformation.h> #include <libevmasm/SemanticInformation.h>
@ -112,10 +113,14 @@ TerminationFinder::ControlFlow TerminationFinder::controlFlowKind(Statement cons
bool TerminationFinder::isTerminatingBuiltin(ExpressionStatement const& _exprStmnt) bool TerminationFinder::isTerminatingBuiltin(ExpressionStatement const& _exprStmnt)
{ {
if (_exprStmnt.expression.type() != typeid(FunctionalInstruction)) if (_exprStmnt.expression.type() == typeid(FunctionalInstruction))
return false; return eth::SemanticInformation::terminatesControlFlow(
boost::get<FunctionalInstruction>(_exprStmnt.expression).instruction
return eth::SemanticInformation::terminatesControlFlow( );
boost::get<FunctionalInstruction>(_exprStmnt.expression).instruction else if (_exprStmnt.expression.type() == typeid(FunctionCall))
); if (auto const* dialect = dynamic_cast<EVMDialect const*>(&m_dialect))
if (auto const* builtin = dialect->builtin(boost::get<FunctionCall>(_exprStmnt.expression).functionName.name))
if (builtin->instruction)
return eth::SemanticInformation::terminatesControlFlow(*builtin->instruction);
return false;
} }

View File

@ -70,6 +70,8 @@ class TerminationFinder
public: public:
enum class ControlFlow { FlowOut, Break, Continue, Terminate }; enum class ControlFlow { FlowOut, Break, Continue, Terminate };
TerminationFinder(Dialect const& _dialect): m_dialect(_dialect) {}
/// @returns the index of the first statement in the provided sequence /// @returns the index of the first statement in the provided sequence
/// that is an unconditional ``break``, ``continue`` or a /// that is an unconditional ``break``, ``continue`` or a
/// call to a terminating builtin function. /// call to a terminating builtin function.
@ -77,18 +79,21 @@ public:
/// returns `FlowOut` and ``size_t(-1)``. /// returns `FlowOut` and ``size_t(-1)``.
/// The function might return ``FlowOut`` even though control /// The function might return ``FlowOut`` even though control
/// flow cannot actually continue. /// flow cannot actually continue.
static std::pair<ControlFlow, size_t> firstUnconditionalControlFlowChange( std::pair<ControlFlow, size_t> firstUnconditionalControlFlowChange(
std::vector<Statement> const& _statements std::vector<Statement> const& _statements
); );
/// @returns the control flow type of the given statement. /// @returns the control flow type of the given statement.
/// This function could return FlowOut even if control flow never continues. /// This function could return FlowOut even if control flow never continues.
static ControlFlow controlFlowKind(Statement const& _statement); ControlFlow controlFlowKind(Statement const& _statement);
/// @returns true if the expression statement is a direct /// @returns true if the expression statement is a direct
/// call to a builtin terminating function like /// call to a builtin terminating function like
/// ``stop``, ``revert`` or ``return``. /// ``stop``, ``revert`` or ``return``.
static bool isTerminatingBuiltin(ExpressionStatement const& _exprStmnt); bool isTerminatingBuiltin(ExpressionStatement const& _exprStmnt);
private:
Dialect const& m_dialect;
}; };
} }

View File

@ -221,6 +221,7 @@ Expression Pattern::toExpression(SourceLocation const& _location) const
vector<Expression> arguments; vector<Expression> arguments;
for (auto const& arg: m_arguments) for (auto const& arg: m_arguments)
arguments.emplace_back(arg.toExpression(_location)); arguments.emplace_back(arg.toExpression(_location));
// TODO convert to FunctionCall
return FunctionalInstruction{_location, m_instruction, std::move(arguments)}; return FunctionalInstruction{_location, m_instruction, std::move(arguments)};
} }
assertThrow(false, OptimizerException, "Pattern of kind 'any', but no match group."); assertThrow(false, OptimizerException, "Pattern of kind 'any', but no match group.");

View File

@ -69,7 +69,7 @@ public:
{ {
YulString varName = _varDecl.variables.front().name; YulString varName = _varDecl.variables.front().name;
if (m_value.count(varName)) if (m_value.count(varName))
m_expressionCodeCost[varName] = CodeCost::codeCost(*m_value[varName]); m_expressionCodeCost[varName] = CodeCost::codeCost(m_dialect, *m_value[varName]);
} }
} }

View File

@ -73,14 +73,14 @@ void OptimiserSuite::run(
FunctionHoister{}(ast); FunctionHoister{}(ast);
BlockFlattener{}(ast); BlockFlattener{}(ast);
ForLoopInitRewriter{}(ast); ForLoopInitRewriter{}(ast);
DeadCodeEliminator{}(ast); DeadCodeEliminator{_dialect}(ast);
FunctionGrouper{}(ast); FunctionGrouper{}(ast);
EquivalentFunctionCombiner::run(ast); EquivalentFunctionCombiner::run(ast);
UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers); UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers);
BlockFlattener{}(ast); BlockFlattener{}(ast);
ControlFlowSimplifier{}(ast); ControlFlowSimplifier{_dialect}(ast);
StructuralSimplifier{_dialect}(ast); StructuralSimplifier{_dialect}(ast);
ControlFlowSimplifier{}(ast); ControlFlowSimplifier{_dialect}(ast);
BlockFlattener{}(ast); BlockFlattener{}(ast);
// None of the above can make stack problems worse. // None of the above can make stack problems worse.
@ -110,11 +110,11 @@ void OptimiserSuite::run(
{ {
// still in SSA, perform structural simplification // still in SSA, perform structural simplification
ControlFlowSimplifier{}(ast); ControlFlowSimplifier{_dialect}(ast);
StructuralSimplifier{_dialect}(ast); StructuralSimplifier{_dialect}(ast);
ControlFlowSimplifier{}(ast); ControlFlowSimplifier{_dialect}(ast);
BlockFlattener{}(ast); BlockFlattener{}(ast);
DeadCodeEliminator{}(ast); DeadCodeEliminator{_dialect}(ast);
UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers); UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers);
} }
{ {
@ -166,8 +166,8 @@ void OptimiserSuite::run(
ExpressionSimplifier::run(_dialect, ast); ExpressionSimplifier::run(_dialect, ast);
StructuralSimplifier{_dialect}(ast); StructuralSimplifier{_dialect}(ast);
BlockFlattener{}(ast); BlockFlattener{}(ast);
DeadCodeEliminator{}(ast); DeadCodeEliminator{_dialect}(ast);
ControlFlowSimplifier{}(ast); ControlFlowSimplifier{_dialect}(ast);
CommonSubexpressionEliminator{_dialect}(ast); CommonSubexpressionEliminator{_dialect}(ast);
SSATransform::run(ast, dispenser); SSATransform::run(ast, dispenser);
RedundantAssignEliminator::run(_dialect, ast); RedundantAssignEliminator::run(_dialect, ast);
@ -202,8 +202,8 @@ void OptimiserSuite::run(
// message once we perform code generation. // message once we perform code generation.
StackCompressor::run(_dialect, ast, _optimizeStackAllocation, stackCompressorMaxIterations); StackCompressor::run(_dialect, ast, _optimizeStackAllocation, stackCompressorMaxIterations);
BlockFlattener{}(ast); BlockFlattener{}(ast);
DeadCodeEliminator{}(ast); DeadCodeEliminator{_dialect}(ast);
ControlFlowSimplifier{}(ast); ControlFlowSimplifier{_dialect}(ast);
FunctionGrouper{}(ast); FunctionGrouper{}(ast);
VarNameCleaner{ast, _dialect, reservedIdentifiers}(ast); VarNameCleaner{ast, _dialect, reservedIdentifiers}(ast);

View File

@ -1,4 +1,4 @@
Warning: Yul and its optimizer are still experimental. Please use the output with care. Warning: Yul and its optimizer are still experimental. Please use the output with care.
strict_asm_jump/input.sol:1:3: Error: Jump instructions and labels are low-level EVM features that can lead to incorrect stack access. Because of that they are disallowed in strict assembly. Use functions, "switch", "if" or "for" statements instead. strict_asm_jump/input.sol:1:3: Error: Function not found.
{ jump(1) } { jump(1) }
^-----^ ^--^

View File

@ -549,8 +549,11 @@ BOOST_AUTO_TEST_CASE(builtins_parser)
SimpleDialect dialect; SimpleDialect dialect;
CHECK_ERROR_DIALECT("{ let builtin := 6 }", ParserError, "Cannot use builtin function name \"builtin\" as identifier name.", dialect); CHECK_ERROR_DIALECT("{ let builtin := 6 }", ParserError, "Cannot use builtin function name \"builtin\" as identifier name.", dialect);
CHECK_ERROR_DIALECT("{ function builtin() {} }", ParserError, "Cannot use builtin function name \"builtin\" as identifier name.", dialect); CHECK_ERROR_DIALECT("{ function builtin() {} }", ParserError, "Cannot use builtin function name \"builtin\" as identifier name.", dialect);
CHECK_ERROR_DIALECT("{ builtin := 6 }", ParserError, "Cannot assign to builtin function \"builtin\".", dialect); CHECK_ERROR_DIALECT("{ builtin := 6 }", ParserError, "Expected '(' but got ':='", dialect);
CHECK_ERROR_DIALECT("{ function g() -> a,b {} builtin, builtin2 := g() }", ParserError, "Cannot assign to builtin function \"builtin\".", dialect); CHECK_ERROR_DIALECT("{ function f(x) { f(builtin) } }", ParserError, "Expected '(' but got ')'", dialect);
CHECK_ERROR_DIALECT("{ function f(builtin) {}", ParserError, "Cannot use builtin function name \"builtin\" as identifier name.", dialect);
CHECK_ERROR_DIALECT("{ function f() -> builtin {}", ParserError, "Cannot use builtin function name \"builtin\" as identifier name.", dialect);
CHECK_ERROR_DIALECT("{ function g() -> a,b {} builtin, builtin2 := g() }", ParserError, "Expected '(' but got ','", dialect);
} }
BOOST_AUTO_TEST_CASE(builtins_analysis) BOOST_AUTO_TEST_CASE(builtins_analysis)

View File

@ -201,7 +201,7 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line
CommonSubexpressionEliminator{*m_dialect}(*m_ast); CommonSubexpressionEliminator{*m_dialect}(*m_ast);
ExpressionSimplifier::run(*m_dialect, *m_ast); ExpressionSimplifier::run(*m_dialect, *m_ast);
UnusedPruner::runUntilStabilised(*m_dialect, *m_ast); UnusedPruner::runUntilStabilised(*m_dialect, *m_ast);
DeadCodeEliminator{}(*m_ast); DeadCodeEliminator{*m_dialect}(*m_ast);
ExpressionJoiner::run(*m_ast); ExpressionJoiner::run(*m_ast);
ExpressionJoiner::run(*m_ast); ExpressionJoiner::run(*m_ast);
} }
@ -214,7 +214,7 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line
{ {
disambiguate(); disambiguate();
ForLoopInitRewriter{}(*m_ast); ForLoopInitRewriter{}(*m_ast);
DeadCodeEliminator{}(*m_ast); DeadCodeEliminator{*m_dialect}(*m_ast);
} }
else if (m_optimizerStep == "ssaTransform") else if (m_optimizerStep == "ssaTransform")
{ {
@ -237,7 +237,7 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line
else if (m_optimizerStep == "controlFlowSimplifier") else if (m_optimizerStep == "controlFlowSimplifier")
{ {
disambiguate(); disambiguate();
ControlFlowSimplifier{}(*m_ast); ControlFlowSimplifier{*m_dialect}(*m_ast);
} }
else if (m_optimizerStep == "structuralSimplifier") else if (m_optimizerStep == "structuralSimplifier")
{ {

View File

@ -22,6 +22,7 @@
#include <test/tools/yulInterpreter/Interpreter.h> #include <test/tools/yulInterpreter/Interpreter.h>
#include <libyul/backends/evm/EVMDialect.h>
#include <libyul/AsmData.h> #include <libyul/AsmData.h>
#include <libevmasm/Instruction.h> #include <libevmasm/Instruction.h>
@ -444,13 +445,15 @@ u256 EVMInstructionInterpreter::eval(
return 0; return 0;
} }
u256 EVMInstructionInterpreter::evalBuiltin(YulString _name, const std::vector<u256>& _arguments) u256 EVMInstructionInterpreter::evalBuiltin(BuiltinFunctionForEVM const& _fun, const std::vector<u256>& _arguments)
{ {
if (_name == "datasize"_yulstring) if (_fun.instruction)
return eval(*_fun.instruction, _arguments);
else if (_fun.name == "datasize"_yulstring)
return u256(keccak256(h256(_arguments.at(0)))) & 0xfff; return u256(keccak256(h256(_arguments.at(0)))) & 0xfff;
else if (_name == "dataoffset"_yulstring) else if (_fun.name == "dataoffset"_yulstring)
return u256(keccak256(h256(_arguments.at(0) + 2))) & 0xfff; return u256(keccak256(h256(_arguments.at(0) + 2))) & 0xfff;
else if (_name == "datacopy"_yulstring) else if (_fun.name == "datacopy"_yulstring)
{ {
// This is identical to codecopy. // This is identical to codecopy.
if (logMemoryWrite(_arguments.at(0), _arguments.at(2))) if (logMemoryWrite(_arguments.at(0), _arguments.at(2)))
@ -463,7 +466,7 @@ u256 EVMInstructionInterpreter::evalBuiltin(YulString _name, const std::vector<u
); );
} }
else else
yulAssert(false, "Unknown builtin: " + _name.str()); yulAssert(false, "Unknown builtin: " + _fun.name.str());
return 0; return 0;
} }

View File

@ -37,6 +37,8 @@ enum class Instruction: uint8_t;
namespace yul namespace yul
{ {
class YulString; class YulString;
struct BuiltinFunctionForEVM;
namespace test namespace test
{ {
@ -70,7 +72,7 @@ public:
/// Evaluate instruction /// Evaluate instruction
dev::u256 eval(dev::eth::Instruction _instruction, std::vector<dev::u256> const& _arguments); dev::u256 eval(dev::eth::Instruction _instruction, std::vector<dev::u256> const& _arguments);
/// Evaluate builtin function /// Evaluate builtin function
dev::u256 evalBuiltin(YulString _name, std::vector<dev::u256> const& _arguments); dev::u256 evalBuiltin(BuiltinFunctionForEVM const& _fun, std::vector<dev::u256> const& _arguments);
private: private:
/// Record a memory read in the trace. Also updates m_state.msize /// Record a memory read in the trace. Also updates m_state.msize

View File

@ -208,12 +208,13 @@ void ExpressionEvaluator::operator()(FunctionCall const& _funCall)
{ {
evaluateArgs(_funCall.arguments); evaluateArgs(_funCall.arguments);
if (dynamic_cast<EVMDialect const*>(&m_dialect) && m_dialect.builtin(_funCall.functionName.name)) if (EVMDialect const* dialect = dynamic_cast<EVMDialect const*>(&m_dialect))
{ if (BuiltinFunctionForEVM const* fun = dialect->builtin(_funCall.functionName.name))
EVMInstructionInterpreter interpreter(m_state); {
setValue(interpreter.evalBuiltin(_funCall.functionName.name, values())); EVMInstructionInterpreter interpreter(m_state);
return; setValue(interpreter.evalBuiltin(*fun, values()));
} return;
}
solAssert(m_functions.count(_funCall.functionName.name), ""); solAssert(m_functions.count(_funCall.functionName.name), "");
FunctionDefinition const& fun = *m_functions.at(_funCall.functionName.name); FunctionDefinition const& fun = *m_functions.at(_funCall.functionName.name);

View File

@ -179,13 +179,13 @@ public:
(StructuralSimplifier{m_dialect})(*m_ast); (StructuralSimplifier{m_dialect})(*m_ast);
break; break;
case 'n': case 'n':
(ControlFlowSimplifier{})(*m_ast); (ControlFlowSimplifier{m_dialect})(*m_ast);
break; break;
case 'u': case 'u':
UnusedPruner::runUntilStabilised(m_dialect, *m_ast); UnusedPruner::runUntilStabilised(m_dialect, *m_ast);
break; break;
case 'D': case 'D':
DeadCodeEliminator{}(*m_ast); DeadCodeEliminator{m_dialect}(*m_ast);
break; break;
case 'a': case 'a':
SSATransform::run(*m_ast, *m_nameDispenser); SSATransform::run(*m_ast, *m_nameDispenser);