mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
commit
3d8b323d3b
@ -6,6 +6,7 @@ set(sources
|
|||||||
ErrorReporter.cpp
|
ErrorReporter.cpp
|
||||||
ErrorReporter.h
|
ErrorReporter.h
|
||||||
EVMVersion.h
|
EVMVersion.h
|
||||||
|
EVMVersion.cpp
|
||||||
Exceptions.cpp
|
Exceptions.cpp
|
||||||
Exceptions.h
|
Exceptions.h
|
||||||
ParserBase.cpp
|
ParserBase.cpp
|
||||||
|
47
liblangutil/EVMVersion.cpp
Normal file
47
liblangutil/EVMVersion.cpp
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
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/>.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* EVM versioning.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <liblangutil/EVMVersion.h>
|
||||||
|
|
||||||
|
using namespace langutil;
|
||||||
|
using namespace dev::eth;
|
||||||
|
|
||||||
|
bool EVMVersion::hasOpcode(Instruction _opcode) const
|
||||||
|
{
|
||||||
|
switch (_opcode)
|
||||||
|
{
|
||||||
|
case Instruction::RETURNDATACOPY:
|
||||||
|
case Instruction::RETURNDATASIZE:
|
||||||
|
return supportsReturndata();
|
||||||
|
case Instruction::STATICCALL:
|
||||||
|
return hasStaticCall();
|
||||||
|
case Instruction::SHL:
|
||||||
|
case Instruction::SHR:
|
||||||
|
case Instruction::SAR:
|
||||||
|
return hasBitwiseShifting();
|
||||||
|
case Instruction::CREATE2:
|
||||||
|
return hasCreate2();
|
||||||
|
case Instruction::EXTCODEHASH:
|
||||||
|
return hasExtCodeHash();
|
||||||
|
default:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -20,11 +20,14 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <libevmasm/Instruction.h>
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <boost/optional.hpp>
|
#include <boost/optional.hpp>
|
||||||
#include <boost/operators.hpp>
|
#include <boost/operators.hpp>
|
||||||
|
|
||||||
|
|
||||||
namespace langutil
|
namespace langutil
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -78,6 +81,8 @@ public:
|
|||||||
bool hasCreate2() const { return *this >= constantinople(); }
|
bool hasCreate2() const { return *this >= constantinople(); }
|
||||||
bool hasExtCodeHash() const { return *this >= constantinople(); }
|
bool hasExtCodeHash() const { return *this >= constantinople(); }
|
||||||
|
|
||||||
|
bool hasOpcode(dev::eth::Instruction _opcode) const;
|
||||||
|
|
||||||
/// Whether we have to retain the costs for the call opcode itself (false),
|
/// Whether we have to retain the costs for the call opcode itself (false),
|
||||||
/// or whether we can just forward easily all remaining gas (true).
|
/// or whether we can just forward easily all remaining gas (true).
|
||||||
bool canOverchargeGasForCall() const { return *this >= tangerineWhistle(); }
|
bool canOverchargeGasForCall() const { return *this >= tangerineWhistle(); }
|
||||||
@ -90,5 +95,4 @@ private:
|
|||||||
Version m_version = Version::Petersburg;
|
Version m_version = Version::Petersburg;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -169,6 +169,7 @@ Statement Parser::parseStatement()
|
|||||||
// literal,
|
// literal,
|
||||||
// identifier (might turn into label or functional assignment)
|
// identifier (might turn into label or functional assignment)
|
||||||
ElementaryOperation elementary(parseElementaryOperation());
|
ElementaryOperation elementary(parseElementaryOperation());
|
||||||
|
|
||||||
switch (currentToken())
|
switch (currentToken())
|
||||||
{
|
{
|
||||||
case Token::LParen:
|
case Token::LParen:
|
||||||
@ -243,10 +244,18 @@ Statement Parser::parseStatement()
|
|||||||
fatalParserError("Call or assignment expected.");
|
fatalParserError("Call or assignment expected.");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (elementary.type() == typeid(Identifier))
|
if (elementary.type() == typeid(Identifier))
|
||||||
{
|
{
|
||||||
Expression expr = boost::get<Identifier>(elementary);
|
Identifier& identifier = boost::get<Identifier>(elementary);
|
||||||
return ExpressionStatement{locationOf(expr), expr};
|
// Fallback from builtin function to Instruction for loose assembly.
|
||||||
|
if (
|
||||||
|
m_dialect.flavour == AsmFlavour::Loose &&
|
||||||
|
instructions().count(identifier.name.str())
|
||||||
|
)
|
||||||
|
return Instruction{identifier.location, instructions().at(identifier.name.str())};
|
||||||
|
else
|
||||||
|
return ExpressionStatement{identifier.location, { move(identifier) }};
|
||||||
}
|
}
|
||||||
else if (elementary.type() == typeid(Literal))
|
else if (elementary.type() == typeid(Literal))
|
||||||
{
|
{
|
||||||
@ -307,12 +316,13 @@ ForLoop Parser::parseForLoop()
|
|||||||
Expression Parser::parseExpression()
|
Expression Parser::parseExpression()
|
||||||
{
|
{
|
||||||
RecursionGuard recursionGuard(*this);
|
RecursionGuard recursionGuard(*this);
|
||||||
// In strict mode, this might parse a plain Instruction, but
|
|
||||||
// it will be converted to a FunctionalInstruction inside
|
|
||||||
// parseCall below.
|
|
||||||
ElementaryOperation operation = parseElementaryOperation();
|
ElementaryOperation operation = parseElementaryOperation();
|
||||||
if (operation.type() == typeid(Instruction))
|
if (operation.type() == typeid(FunctionCall))
|
||||||
|
return parseCall(std::move(operation));
|
||||||
|
else if (operation.type() == typeid(Instruction))
|
||||||
{
|
{
|
||||||
|
solAssert(m_dialect.flavour == AsmFlavour::Loose, "");
|
||||||
Instruction const& instr = boost::get<Instruction>(operation);
|
Instruction const& instr = boost::get<Instruction>(operation);
|
||||||
// Disallow instructions returning multiple values (and DUP/SWAP) as expression.
|
// Disallow instructions returning multiple values (and DUP/SWAP) as expression.
|
||||||
if (
|
if (
|
||||||
@ -394,8 +404,14 @@ Parser::ElementaryOperation Parser::parseElementaryOperation()
|
|||||||
literal = YulString{currentLiteral()};
|
literal = YulString{currentLiteral()};
|
||||||
// first search the set of builtins, then the instructions.
|
// first search the set of builtins, then the instructions.
|
||||||
if (m_dialect.builtin(literal))
|
if (m_dialect.builtin(literal))
|
||||||
ret = Identifier{location(), literal};
|
{
|
||||||
else if (m_dialect.flavour != AsmFlavour::Yul && instructions().count(literal.str()))
|
// For builtins we already check here that they are followed by `(`.
|
||||||
|
ret = FunctionCall{location(), Identifier{location(), literal}, {}};
|
||||||
|
advance();
|
||||||
|
expectToken(Token::LParen, false);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
else if (m_dialect.flavour == AsmFlavour::Loose && instructions().count(literal.str()))
|
||||||
{
|
{
|
||||||
dev::eth::Instruction const& instr = instructions().at(literal.str());
|
dev::eth::Instruction const& instr = instructions().at(literal.str());
|
||||||
ret = Instruction{location(), instr};
|
ret = Instruction{location(), instr};
|
||||||
@ -582,11 +598,16 @@ Expression Parser::parseCall(Parser::ElementaryOperation&& _initialOp)
|
|||||||
expectToken(Token::RParen);
|
expectToken(Token::RParen);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
else if (_initialOp.type() == typeid(Identifier))
|
else if (_initialOp.type() == typeid(Identifier) || _initialOp.type() == typeid(FunctionCall))
|
||||||
{
|
{
|
||||||
FunctionCall ret;
|
FunctionCall ret;
|
||||||
ret.functionName = std::move(boost::get<Identifier>(_initialOp));
|
if (_initialOp.type() == typeid(Identifier))
|
||||||
ret.location = ret.functionName.location;
|
{
|
||||||
|
ret.functionName = std::move(boost::get<Identifier>(_initialOp));
|
||||||
|
ret.location = ret.functionName.location;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
ret = std::move(boost::get<FunctionCall>(_initialOp));
|
||||||
expectToken(Token::LParen);
|
expectToken(Token::LParen);
|
||||||
while (currentToken() != Token::RParen)
|
while (currentToken() != Token::RParen)
|
||||||
{
|
{
|
||||||
|
@ -56,7 +56,7 @@ public:
|
|||||||
static std::map<std::string, dev::eth::Instruction> const& instructions();
|
static std::map<std::string, dev::eth::Instruction> const& instructions();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
using ElementaryOperation = boost::variant<Instruction, Literal, Identifier>;
|
using ElementaryOperation = boost::variant<Instruction, Literal, Identifier, FunctionCall>;
|
||||||
|
|
||||||
/// Creates an inline assembly node with the given source location.
|
/// Creates an inline assembly node with the given source location.
|
||||||
template <class T> T createWithLocation(langutil::SourceLocation const& _loc = {}) const
|
template <class T> T createWithLocation(langutil::SourceLocation const& _loc = {}) const
|
||||||
@ -81,9 +81,8 @@ protected:
|
|||||||
/// Parses a functional expression that has to push exactly one stack element
|
/// Parses a functional expression that has to push exactly one stack element
|
||||||
Expression parseExpression();
|
Expression parseExpression();
|
||||||
static std::map<dev::eth::Instruction, std::string> const& instructionNames();
|
static std::map<dev::eth::Instruction, std::string> const& instructionNames();
|
||||||
/// Parses an elementary operation, i.e. a literal, identifier or instruction.
|
/// Parses an elementary operation, i.e. a literal, identifier, instruction or
|
||||||
/// This will parse instructions even in strict mode as part of the full parser
|
/// builtin functian call (only the name).
|
||||||
/// for FunctionalInstruction.
|
|
||||||
ElementaryOperation parseElementaryOperation();
|
ElementaryOperation parseElementaryOperation();
|
||||||
VariableDeclaration parseVariableDeclaration();
|
VariableDeclaration parseVariableDeclaration();
|
||||||
FunctionDefinition parseFunctionDefinition();
|
FunctionDefinition parseFunctionDefinition();
|
||||||
|
@ -23,11 +23,14 @@
|
|||||||
#include <libyul/AsmAnalysisInfo.h>
|
#include <libyul/AsmAnalysisInfo.h>
|
||||||
#include <libyul/AsmData.h>
|
#include <libyul/AsmData.h>
|
||||||
#include <libyul/Object.h>
|
#include <libyul/Object.h>
|
||||||
|
#include <libyul/Exceptions.h>
|
||||||
|
#include <libyul/AsmParser.h>
|
||||||
#include <libyul/backends/evm/AbstractAssembly.h>
|
#include <libyul/backends/evm/AbstractAssembly.h>
|
||||||
|
|
||||||
#include <liblangutil/Exceptions.h>
|
#include <libevmasm/SemanticInformation.h>
|
||||||
|
#include <libevmasm/Instruction.h>
|
||||||
|
|
||||||
#include <libyul/Exceptions.h>
|
#include <liblangutil/Exceptions.h>
|
||||||
|
|
||||||
#include <boost/range/adaptor/reversed.hpp>
|
#include <boost/range/adaptor/reversed.hpp>
|
||||||
|
|
||||||
@ -37,6 +40,33 @@ using namespace yul;
|
|||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
pair<YulString, BuiltinFunctionForEVM> createEVMFunction(
|
||||||
|
string const& _name,
|
||||||
|
dev::eth::Instruction _instruction
|
||||||
|
)
|
||||||
|
{
|
||||||
|
eth::InstructionInfo info = dev::eth::instructionInfo(_instruction);
|
||||||
|
BuiltinFunctionForEVM f;
|
||||||
|
f.name = YulString{_name};
|
||||||
|
f.parameters.resize(info.args);
|
||||||
|
f.returns.resize(info.ret);
|
||||||
|
f.movable = eth::SemanticInformation::movable(_instruction);
|
||||||
|
f.sideEffectFree = eth::SemanticInformation::sideEffectFree(_instruction);
|
||||||
|
f.literalArguments = false;
|
||||||
|
f.instruction = _instruction;
|
||||||
|
f.generateCode = [_instruction](
|
||||||
|
FunctionCall const&,
|
||||||
|
AbstractAssembly& _assembly,
|
||||||
|
BuiltinContext&,
|
||||||
|
std::function<void()> _visitArguments
|
||||||
|
) {
|
||||||
|
_visitArguments();
|
||||||
|
_assembly.appendInstruction(_instruction);
|
||||||
|
};
|
||||||
|
|
||||||
|
return {f.name, move(f)};
|
||||||
|
}
|
||||||
|
|
||||||
pair<YulString, BuiltinFunctionForEVM> createFunction(
|
pair<YulString, BuiltinFunctionForEVM> createFunction(
|
||||||
string _name,
|
string _name,
|
||||||
size_t _params,
|
size_t _params,
|
||||||
@ -55,13 +85,25 @@ pair<YulString, BuiltinFunctionForEVM> createFunction(
|
|||||||
f.movable = _movable;
|
f.movable = _movable;
|
||||||
f.literalArguments = _literalArguments;
|
f.literalArguments = _literalArguments;
|
||||||
f.sideEffectFree = _sideEffectFree;
|
f.sideEffectFree = _sideEffectFree;
|
||||||
|
f.instruction = {};
|
||||||
f.generateCode = std::move(_generateCode);
|
f.generateCode = std::move(_generateCode);
|
||||||
return {name, f};
|
return {name, f};
|
||||||
}
|
}
|
||||||
|
|
||||||
map<YulString, BuiltinFunctionForEVM> createBuiltins(langutil::EVMVersion, bool _objectAccess)
|
map<YulString, BuiltinFunctionForEVM> createBuiltins(langutil::EVMVersion _evmVersion, bool _objectAccess, bool _evmOpcodes)
|
||||||
{
|
{
|
||||||
map<YulString, BuiltinFunctionForEVM> builtins;
|
map<YulString, BuiltinFunctionForEVM> builtins;
|
||||||
|
if (_evmOpcodes)
|
||||||
|
for (auto const& instr: Parser::instructions())
|
||||||
|
if (
|
||||||
|
!dev::eth::isDupInstruction(instr.second) &&
|
||||||
|
!dev::eth::isSwapInstruction(instr.second) &&
|
||||||
|
instr.second != eth::Instruction::JUMP &&
|
||||||
|
instr.second != eth::Instruction::JUMPI &&
|
||||||
|
_evmVersion.hasOpcode(instr.second)
|
||||||
|
)
|
||||||
|
builtins.emplace(createEVMFunction(instr.first, instr.second));
|
||||||
|
|
||||||
if (_objectAccess)
|
if (_objectAccess)
|
||||||
{
|
{
|
||||||
builtins.emplace(createFunction("datasize", 1, 1, true, true, true, [](
|
builtins.emplace(createFunction("datasize", 1, 1, true, true, true, [](
|
||||||
@ -125,7 +167,7 @@ EVMDialect::EVMDialect(AsmFlavour _flavour, bool _objectAccess, langutil::EVMVer
|
|||||||
Dialect{_flavour},
|
Dialect{_flavour},
|
||||||
m_objectAccess(_objectAccess),
|
m_objectAccess(_objectAccess),
|
||||||
m_evmVersion(_evmVersion),
|
m_evmVersion(_evmVersion),
|
||||||
m_functions(createBuiltins(_evmVersion, _objectAccess))
|
m_functions(createBuiltins(_evmVersion, _objectAccess, _flavour != AsmFlavour::Loose))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,6 +47,7 @@ struct BuiltinContext
|
|||||||
|
|
||||||
struct BuiltinFunctionForEVM: BuiltinFunction
|
struct BuiltinFunctionForEVM: BuiltinFunction
|
||||||
{
|
{
|
||||||
|
boost::optional<dev::eth::Instruction> instruction;
|
||||||
/// Function to generate code for the given function call and append it to the abstract
|
/// Function to generate code for the given function call and append it to the abstract
|
||||||
/// assembly. The fourth parameter is called to visit (and generate code for) the arguments
|
/// assembly. The fourth parameter is called to visit (and generate code for) the arguments
|
||||||
/// from right to left.
|
/// from right to left.
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
@ -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))
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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, "");
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
#include <libyul/optimiser/ASTCopier.h>
|
#include <libyul/optimiser/ASTCopier.h>
|
||||||
#include <libyul/optimiser/Semantics.h>
|
#include <libyul/optimiser/Semantics.h>
|
||||||
#include <libyul/optimiser/SyntacticalEquality.h>
|
#include <libyul/optimiser/SyntacticalEquality.h>
|
||||||
|
#include <libyul/backends/evm/EVMDialect.h>
|
||||||
#include <libyul/AsmData.h>
|
#include <libyul/AsmData.h>
|
||||||
#include <libyul/Utilities.h>
|
#include <libyul/Utilities.h>
|
||||||
|
|
||||||
@ -41,14 +42,14 @@ SimplificationRule<yul::Pattern> const* SimplificationRules::findFirstMatch(
|
|||||||
map<YulString, Expression const*> const& _ssaValues
|
map<YulString, Expression const*> const& _ssaValues
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (_expr.type() != typeid(FunctionalInstruction))
|
auto instruction = instructionAndArguments(_dialect, _expr);
|
||||||
|
if (!instruction)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
static SimplificationRules rules;
|
static SimplificationRules rules;
|
||||||
assertThrow(rules.isInitialized(), OptimizerException, "Rule list not properly initialized.");
|
assertThrow(rules.isInitialized(), OptimizerException, "Rule list not properly initialized.");
|
||||||
|
|
||||||
FunctionalInstruction const& instruction = boost::get<FunctionalInstruction>(_expr);
|
for (auto const& rule: rules.m_rules[uint8_t(instruction->first)])
|
||||||
for (auto const& rule: rules.m_rules[uint8_t(instruction.instruction)])
|
|
||||||
{
|
{
|
||||||
rules.resetMatchGroups();
|
rules.resetMatchGroups();
|
||||||
if (rule.pattern.matches(_expr, _dialect, _ssaValues))
|
if (rule.pattern.matches(_expr, _dialect, _ssaValues))
|
||||||
@ -63,6 +64,20 @@ bool SimplificationRules::isInitialized() const
|
|||||||
return !m_rules[uint8_t(dev::eth::Instruction::ADD)].empty();
|
return !m_rules[uint8_t(dev::eth::Instruction::ADD)].empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boost::optional<std::pair<dev::eth::Instruction, vector<Expression> const*>>
|
||||||
|
SimplificationRules::instructionAndArguments(Dialect const& _dialect, Expression const& _expr)
|
||||||
|
{
|
||||||
|
if (_expr.type() == typeid(FunctionalInstruction))
|
||||||
|
return make_pair(boost::get<FunctionalInstruction>(_expr).instruction, &boost::get<FunctionalInstruction>(_expr).arguments);
|
||||||
|
else if (_expr.type() == typeid(FunctionCall))
|
||||||
|
if (auto const* dialect = dynamic_cast<EVMDialect const*>(&_dialect))
|
||||||
|
if (auto const* builtin = dialect->builtin(boost::get<FunctionCall>(_expr).functionName.name))
|
||||||
|
if (builtin->instruction)
|
||||||
|
return make_pair(*builtin->instruction, &boost::get<FunctionCall>(_expr).arguments);
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
void SimplificationRules::addRules(vector<SimplificationRule<Pattern>> const& _rules)
|
void SimplificationRules::addRules(vector<SimplificationRule<Pattern>> const& _rules)
|
||||||
{
|
{
|
||||||
for (auto const& r: _rules)
|
for (auto const& r: _rules)
|
||||||
@ -139,14 +154,12 @@ bool Pattern::matches(
|
|||||||
}
|
}
|
||||||
else if (m_kind == PatternKind::Operation)
|
else if (m_kind == PatternKind::Operation)
|
||||||
{
|
{
|
||||||
if (expr->type() != typeid(FunctionalInstruction))
|
auto instrAndArgs = SimplificationRules::instructionAndArguments(_dialect, *expr);
|
||||||
|
if (!instrAndArgs || m_instruction != instrAndArgs->first)
|
||||||
return false;
|
return false;
|
||||||
FunctionalInstruction const& instr = boost::get<FunctionalInstruction>(*expr);
|
assertThrow(m_arguments.size() == instrAndArgs->second->size(), OptimizerException, "");
|
||||||
if (m_instruction != instr.instruction)
|
|
||||||
return false;
|
|
||||||
assertThrow(m_arguments.size() == instr.arguments.size(), OptimizerException, "");
|
|
||||||
for (size_t i = 0; i < m_arguments.size(); ++i)
|
for (size_t i = 0; i < m_arguments.size(); ++i)
|
||||||
if (!m_arguments[i].matches(instr.arguments.at(i), _dialect, _ssaValues))
|
if (!m_arguments[i].matches(instrAndArgs->second->at(i), _dialect, _ssaValues))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -208,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.");
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
#include <libyul/AsmData.h>
|
#include <libyul/AsmData.h>
|
||||||
|
|
||||||
#include <boost/noncopyable.hpp>
|
#include <boost/noncopyable.hpp>
|
||||||
|
#include <boost/optional.hpp>
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@ -55,6 +56,10 @@ public:
|
|||||||
/// Checks whether the rulelist is non-empty. This is usually enforced
|
/// Checks whether the rulelist is non-empty. This is usually enforced
|
||||||
/// by the constructor, but we had some issues with static initialization.
|
/// by the constructor, but we had some issues with static initialization.
|
||||||
bool isInitialized() const;
|
bool isInitialized() const;
|
||||||
|
|
||||||
|
static boost::optional<std::pair<dev::eth::Instruction, std::vector<Expression> const*>>
|
||||||
|
instructionAndArguments(Dialect const& _dialect, Expression const& _expr);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void addRules(std::vector<dev::eth::SimplificationRule<Pattern>> const& _rules);
|
void addRules(std::vector<dev::eth::SimplificationRule<Pattern>> const& _rules);
|
||||||
void addRule(dev::eth::SimplificationRule<Pattern> const& _rule);
|
void addRule(dev::eth::SimplificationRule<Pattern> const& _rule);
|
||||||
|
@ -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]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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) }
|
||||||
^-----^
|
^--^
|
||||||
|
@ -516,11 +516,11 @@ BOOST_AUTO_TEST_CASE(no_opcodes_in_strict)
|
|||||||
{
|
{
|
||||||
BOOST_CHECK(successParse("{ pop(callvalue) }"));
|
BOOST_CHECK(successParse("{ pop(callvalue) }"));
|
||||||
BOOST_CHECK(successParse("{ callvalue pop }"));
|
BOOST_CHECK(successParse("{ callvalue pop }"));
|
||||||
CHECK_STRICT_ERROR("{ pop(callvalue) }", ParserError, "Non-functional instructions are not allowed in this context.");
|
CHECK_STRICT_ERROR("{ pop(callvalue) }", ParserError, "Expected '(' but got ')'");
|
||||||
CHECK_STRICT_ERROR("{ callvalue pop }", ParserError, "Call or assignment expected");
|
CHECK_STRICT_ERROR("{ callvalue pop }", ParserError, "Expected '(' but got identifier");
|
||||||
SUCCESS_STRICT("{ pop(callvalue()) }");
|
SUCCESS_STRICT("{ pop(callvalue()) }");
|
||||||
BOOST_CHECK(successParse("{ switch callvalue case 0 {} }"));
|
BOOST_CHECK(successParse("{ switch callvalue case 0 {} }"));
|
||||||
CHECK_STRICT_ERROR("{ switch callvalue case 0 {} }", ParserError, "Non-functional instructions are not allowed in this context.");
|
CHECK_STRICT_ERROR("{ switch callvalue case 0 {} }", ParserError, "Expected '(' but got reserved keyword 'case'");
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(no_labels_in_strict)
|
BOOST_AUTO_TEST_CASE(no_labels_in_strict)
|
||||||
@ -546,7 +546,6 @@ BOOST_AUTO_TEST_CASE(no_dup_swap_in_strict)
|
|||||||
BOOST_CHECK(successParse("{ dup2 pop }"));
|
BOOST_CHECK(successParse("{ dup2 pop }"));
|
||||||
CHECK_STRICT_ERROR("{ dup2 pop }", ParserError, "Call or assignment expected.");
|
CHECK_STRICT_ERROR("{ dup2 pop }", ParserError, "Call or assignment expected.");
|
||||||
CHECK_PARSE_ERROR("{ switch dup1 case 0 {} }", ParserError, "Instruction \"dup1\" not allowed in this context");
|
CHECK_PARSE_ERROR("{ switch dup1 case 0 {} }", ParserError, "Instruction \"dup1\" not allowed in this context");
|
||||||
CHECK_STRICT_ERROR("{ switch dup1 case 0 {} }", ParserError, "Instruction \"dup1\" not allowed in this context");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_SUITE_END()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
@ -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)
|
||||||
|
@ -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")
|
||||||
{
|
{
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
Loading…
Reference in New Issue
Block a user