diff --git a/liblangutil/CMakeLists.txt b/liblangutil/CMakeLists.txt
index 4cac9d8bb..63c9de39d 100644
--- a/liblangutil/CMakeLists.txt
+++ b/liblangutil/CMakeLists.txt
@@ -6,6 +6,7 @@ set(sources
ErrorReporter.cpp
ErrorReporter.h
EVMVersion.h
+ EVMVersion.cpp
Exceptions.cpp
Exceptions.h
ParserBase.cpp
diff --git a/liblangutil/EVMVersion.cpp b/liblangutil/EVMVersion.cpp
new file mode 100644
index 000000000..eb87d4832
--- /dev/null
+++ b/liblangutil/EVMVersion.cpp
@@ -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 .
+*/
+/**
+ * EVM versioning.
+ */
+
+#include
+
+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;
+ }
+}
+
diff --git a/liblangutil/EVMVersion.h b/liblangutil/EVMVersion.h
index 21a29cb82..f80a18708 100644
--- a/liblangutil/EVMVersion.h
+++ b/liblangutil/EVMVersion.h
@@ -20,11 +20,14 @@
#pragma once
+#include
+
#include
#include
#include
+
namespace langutil
{
@@ -78,6 +81,8 @@ public:
bool hasCreate2() 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),
/// or whether we can just forward easily all remaining gas (true).
bool canOverchargeGasForCall() const { return *this >= tangerineWhistle(); }
@@ -90,5 +95,4 @@ private:
Version m_version = Version::Petersburg;
};
-
}
diff --git a/libyul/AsmParser.cpp b/libyul/AsmParser.cpp
index 71bc1313f..a81c3dbc6 100644
--- a/libyul/AsmParser.cpp
+++ b/libyul/AsmParser.cpp
@@ -169,6 +169,7 @@ Statement Parser::parseStatement()
// literal,
// identifier (might turn into label or functional assignment)
ElementaryOperation elementary(parseElementaryOperation());
+
switch (currentToken())
{
case Token::LParen:
@@ -243,10 +244,18 @@ Statement Parser::parseStatement()
fatalParserError("Call or assignment expected.");
break;
}
+
if (elementary.type() == typeid(Identifier))
{
- Expression expr = boost::get(elementary);
- return ExpressionStatement{locationOf(expr), expr};
+ Identifier& identifier = boost::get(elementary);
+ // 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))
{
@@ -307,12 +316,13 @@ ForLoop Parser::parseForLoop()
Expression Parser::parseExpression()
{
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();
- 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(operation);
// Disallow instructions returning multiple values (and DUP/SWAP) as expression.
if (
@@ -394,8 +404,14 @@ Parser::ElementaryOperation Parser::parseElementaryOperation()
literal = YulString{currentLiteral()};
// first search the set of builtins, then the instructions.
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());
ret = Instruction{location(), instr};
@@ -582,11 +598,16 @@ Expression Parser::parseCall(Parser::ElementaryOperation&& _initialOp)
expectToken(Token::RParen);
return ret;
}
- else if (_initialOp.type() == typeid(Identifier))
+ else if (_initialOp.type() == typeid(Identifier) || _initialOp.type() == typeid(FunctionCall))
{
FunctionCall ret;
- ret.functionName = std::move(boost::get(_initialOp));
- ret.location = ret.functionName.location;
+ if (_initialOp.type() == typeid(Identifier))
+ {
+ ret.functionName = std::move(boost::get(_initialOp));
+ ret.location = ret.functionName.location;
+ }
+ else
+ ret = std::move(boost::get(_initialOp));
expectToken(Token::LParen);
while (currentToken() != Token::RParen)
{
diff --git a/libyul/AsmParser.h b/libyul/AsmParser.h
index cc49681a5..6f88a211e 100644
--- a/libyul/AsmParser.h
+++ b/libyul/AsmParser.h
@@ -56,7 +56,7 @@ public:
static std::map const& instructions();
protected:
- using ElementaryOperation = boost::variant;
+ using ElementaryOperation = boost::variant;
/// Creates an inline assembly node with the given source location.
template T createWithLocation(langutil::SourceLocation const& _loc = {}) const
@@ -81,9 +81,8 @@ protected:
/// Parses a functional expression that has to push exactly one stack element
Expression parseExpression();
static std::map const& instructionNames();
- /// Parses an elementary operation, i.e. a literal, identifier or instruction.
- /// This will parse instructions even in strict mode as part of the full parser
- /// for FunctionalInstruction.
+ /// Parses an elementary operation, i.e. a literal, identifier, instruction or
+ /// builtin functian call (only the name).
ElementaryOperation parseElementaryOperation();
VariableDeclaration parseVariableDeclaration();
FunctionDefinition parseFunctionDefinition();
diff --git a/libyul/backends/evm/EVMDialect.cpp b/libyul/backends/evm/EVMDialect.cpp
index f3ab44ce6..375d0c897 100644
--- a/libyul/backends/evm/EVMDialect.cpp
+++ b/libyul/backends/evm/EVMDialect.cpp
@@ -23,11 +23,14 @@
#include
#include
#include
+#include
+#include
#include
-#include
+#include
+#include
-#include
+#include
#include
@@ -37,6 +40,33 @@ using namespace yul;
namespace
{
+pair 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 _visitArguments
+ ) {
+ _visitArguments();
+ _assembly.appendInstruction(_instruction);
+ };
+
+ return {f.name, move(f)};
+}
+
pair createFunction(
string _name,
size_t _params,
@@ -55,13 +85,25 @@ pair createFunction(
f.movable = _movable;
f.literalArguments = _literalArguments;
f.sideEffectFree = _sideEffectFree;
+ f.instruction = {};
f.generateCode = std::move(_generateCode);
return {name, f};
}
-map createBuiltins(langutil::EVMVersion, bool _objectAccess)
+map createBuiltins(langutil::EVMVersion _evmVersion, bool _objectAccess, bool _evmOpcodes)
{
map 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)
{
builtins.emplace(createFunction("datasize", 1, 1, true, true, true, [](
@@ -125,7 +167,7 @@ EVMDialect::EVMDialect(AsmFlavour _flavour, bool _objectAccess, langutil::EVMVer
Dialect{_flavour},
m_objectAccess(_objectAccess),
m_evmVersion(_evmVersion),
- m_functions(createBuiltins(_evmVersion, _objectAccess))
+ m_functions(createBuiltins(_evmVersion, _objectAccess, _flavour != AsmFlavour::Loose))
{
}
diff --git a/libyul/backends/evm/EVMDialect.h b/libyul/backends/evm/EVMDialect.h
index 3e14737dc..56dc99f87 100644
--- a/libyul/backends/evm/EVMDialect.h
+++ b/libyul/backends/evm/EVMDialect.h
@@ -47,6 +47,7 @@ struct BuiltinContext
struct BuiltinFunctionForEVM: BuiltinFunction
{
+ boost::optional instruction;
/// 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
/// from right to left.
diff --git a/libyul/optimiser/ControlFlowSimplifier.cpp b/libyul/optimiser/ControlFlowSimplifier.cpp
index 46d5b5bc4..ccd88051f 100644
--- a/libyul/optimiser/ControlFlowSimplifier.cpp
+++ b/libyul/optimiser/ControlFlowSimplifier.cpp
@@ -35,9 +35,9 @@ namespace
ExpressionStatement makePopExpressionStatement(langutil::SourceLocation const& _location, Expression&& _expression)
{
- return {_location, FunctionalInstruction{
+ return {_location, FunctionCall{
_location,
- dev::eth::Instruction::POP,
+ Identifier{_location, "pop"_yulstring},
{std::move(_expression)}
}};
}
@@ -84,9 +84,9 @@ OptionalStatements reduceSingleCaseSwitch(Switch& _switchStmt)
if (switchCase.value)
return make_vector(If{
std::move(_switchStmt.location),
- make_unique(FunctionalInstruction{
- std::move(loc),
- dev::eth::Instruction::EQ,
+ make_unique(FunctionCall{
+ loc,
+ Identifier{loc, "eq"_yulstring},
{std::move(*switchCase.value), std::move(*_switchStmt.expression)}
}),
std::move(switchCase.body)
@@ -122,7 +122,7 @@ void ControlFlowSimplifier::visit(Statement& _st)
if (!forLoop.body.statements.empty())
{
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)
{
isTerminating = true;
diff --git a/libyul/optimiser/ControlFlowSimplifier.h b/libyul/optimiser/ControlFlowSimplifier.h
index a2975e5a0..23dc3075b 100644
--- a/libyul/optimiser/ControlFlowSimplifier.h
+++ b/libyul/optimiser/ControlFlowSimplifier.h
@@ -20,6 +20,7 @@
namespace yul
{
+struct Dialect;
/**
* Simplifies several control-flow structures:
@@ -45,6 +46,8 @@ namespace yul
class ControlFlowSimplifier: public ASTModifier
{
public:
+ ControlFlowSimplifier(Dialect const& _dialect): m_dialect(_dialect) {}
+
using ASTModifier::operator();
void operator()(Break&) override { ++m_numBreakStatements; }
void operator()(Continue&) override { ++m_numContinueStatements; }
@@ -55,6 +58,7 @@ public:
private:
void simplify(std::vector& _statements);
+ Dialect const& m_dialect;
size_t m_numBreakStatements = 0;
size_t m_numContinueStatements = 0;
};
diff --git a/libyul/optimiser/DeadCodeEliminator.cpp b/libyul/optimiser/DeadCodeEliminator.cpp
index b39473758..5d123ca74 100644
--- a/libyul/optimiser/DeadCodeEliminator.cpp
+++ b/libyul/optimiser/DeadCodeEliminator.cpp
@@ -42,7 +42,7 @@ void DeadCodeEliminator::operator()(Block& _block)
{
TerminationFinder::ControlFlow controlFlowChange;
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.
if (controlFlowChange != TerminationFinder::ControlFlow::FlowOut && index != size_t(-1))
diff --git a/libyul/optimiser/DeadCodeEliminator.h b/libyul/optimiser/DeadCodeEliminator.h
index 16c8b6d99..50dbaaa75 100644
--- a/libyul/optimiser/DeadCodeEliminator.h
+++ b/libyul/optimiser/DeadCodeEliminator.h
@@ -28,6 +28,7 @@
namespace yul
{
+struct Dialect;
/**
* Optimisation stage that removes unreachable code
@@ -46,9 +47,14 @@ namespace yul
class DeadCodeEliminator: public ASTModifier
{
public:
+ DeadCodeEliminator(Dialect const& _dialect): m_dialect(_dialect) {}
+
using ASTModifier::operator();
void operator()(ForLoop& _for) override;
void operator()(Block& _block) override;
+
+private:
+ Dialect const& m_dialect;
};
}
diff --git a/libyul/optimiser/Metrics.cpp b/libyul/optimiser/Metrics.cpp
index 02b5a7179..11e0b31c3 100644
--- a/libyul/optimiser/Metrics.cpp
+++ b/libyul/optimiser/Metrics.cpp
@@ -22,6 +22,7 @@
#include
#include
+#include
#include
@@ -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);
return cc.m_cost;
}
@@ -102,23 +103,26 @@ size_t CodeCost::codeCost(Expression const& _expr)
void CodeCost::operator()(FunctionCall const& _funCall)
{
- yulAssert(m_cost >= 1, "Should assign cost one in visit(Expression).");
- m_cost += 49;
ASTWalker::operator()(_funCall);
+
+ if (EVMDialect const* dialect = dynamic_cast(&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)
{
yulAssert(m_cost >= 1, "Should assign cost one in visit(Expression).");
- dev::eth::Tier gasPriceTier = dev::eth::instructionInfo(_instr.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;
+ addInstructionCost(_instr.instruction);
ASTWalker::operator()(_instr);
}
+
void CodeCost::operator()(Literal const& _literal)
{
yulAssert(m_cost >= 1, "Should assign cost one in visit(Expression).");
@@ -151,6 +155,17 @@ void CodeCost::visit(Expression const& _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)
{
for (auto const& variable: _assignment.variableNames)
diff --git a/libyul/optimiser/Metrics.h b/libyul/optimiser/Metrics.h
index 1620d4d33..98041a606 100644
--- a/libyul/optimiser/Metrics.h
+++ b/libyul/optimiser/Metrics.h
@@ -22,9 +22,13 @@
#include
+#include
+
namespace yul
{
+struct Dialect;
+
/**
* Metric for the size of code.
* More specifically, the number of AST nodes.
@@ -71,9 +75,11 @@ private:
class CodeCost: public ASTWalker
{
public:
- static size_t codeCost(Expression const& _expression);
+ static size_t codeCost(Dialect const& _dialect, Expression const& _expression);
private:
+ CodeCost(Dialect const& _dialect): m_dialect(_dialect) {}
+
void operator()(FunctionCall const& _funCall) override;
void operator()(FunctionalInstruction const& _instr) override;
void operator()(Literal const& _literal) override;
@@ -81,6 +87,9 @@ private:
void visit(Expression const& _expression) override;
private:
+ void addInstructionCost(dev::eth::Instruction _instruction);
+
+ Dialect const& m_dialect;
size_t m_cost = 0;
};
diff --git a/libyul/optimiser/Rematerialiser.cpp b/libyul/optimiser/Rematerialiser.cpp
index de1b91439..c70b02423 100644
--- a/libyul/optimiser/Rematerialiser.cpp
+++ b/libyul/optimiser/Rematerialiser.cpp
@@ -77,7 +77,7 @@ void Rematerialiser::visit(Expression& _e)
assertThrow(m_value.at(name), OptimizerException, "");
auto const& value = *m_value.at(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))
{
assertThrow(m_referenceCounts[name] > 0, OptimizerException, "");
diff --git a/libyul/optimiser/Semantics.cpp b/libyul/optimiser/Semantics.cpp
index df3d0cbb0..6291ce688 100644
--- a/libyul/optimiser/Semantics.cpp
+++ b/libyul/optimiser/Semantics.cpp
@@ -23,6 +23,7 @@
#include
#include
#include
+#include
#include
@@ -112,10 +113,14 @@ TerminationFinder::ControlFlow TerminationFinder::controlFlowKind(Statement cons
bool TerminationFinder::isTerminatingBuiltin(ExpressionStatement const& _exprStmnt)
{
- if (_exprStmnt.expression.type() != typeid(FunctionalInstruction))
- return false;
-
- return eth::SemanticInformation::terminatesControlFlow(
- boost::get(_exprStmnt.expression).instruction
- );
+ if (_exprStmnt.expression.type() == typeid(FunctionalInstruction))
+ return eth::SemanticInformation::terminatesControlFlow(
+ boost::get(_exprStmnt.expression).instruction
+ );
+ else if (_exprStmnt.expression.type() == typeid(FunctionCall))
+ if (auto const* dialect = dynamic_cast(&m_dialect))
+ if (auto const* builtin = dialect->builtin(boost::get(_exprStmnt.expression).functionName.name))
+ if (builtin->instruction)
+ return eth::SemanticInformation::terminatesControlFlow(*builtin->instruction);
+ return false;
}
diff --git a/libyul/optimiser/Semantics.h b/libyul/optimiser/Semantics.h
index 0899fc4c0..b50169f97 100644
--- a/libyul/optimiser/Semantics.h
+++ b/libyul/optimiser/Semantics.h
@@ -70,6 +70,8 @@ class TerminationFinder
public:
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
/// that is an unconditional ``break``, ``continue`` or a
/// call to a terminating builtin function.
@@ -77,18 +79,21 @@ public:
/// returns `FlowOut` and ``size_t(-1)``.
/// The function might return ``FlowOut`` even though control
/// flow cannot actually continue.
- static std::pair firstUnconditionalControlFlowChange(
+ std::pair firstUnconditionalControlFlowChange(
std::vector const& _statements
);
/// @returns the control flow type of the given statement.
/// 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
/// call to a builtin terminating function like
/// ``stop``, ``revert`` or ``return``.
- static bool isTerminatingBuiltin(ExpressionStatement const& _exprStmnt);
+ bool isTerminatingBuiltin(ExpressionStatement const& _exprStmnt);
+
+private:
+ Dialect const& m_dialect;
};
}
diff --git a/libyul/optimiser/SimplificationRules.cpp b/libyul/optimiser/SimplificationRules.cpp
index 205b42c07..861f8b25b 100644
--- a/libyul/optimiser/SimplificationRules.cpp
+++ b/libyul/optimiser/SimplificationRules.cpp
@@ -23,6 +23,7 @@
#include
#include
#include
+#include
#include
#include
@@ -41,14 +42,14 @@ SimplificationRule const* SimplificationRules::findFirstMatch(
map const& _ssaValues
)
{
- if (_expr.type() != typeid(FunctionalInstruction))
+ auto instruction = instructionAndArguments(_dialect, _expr);
+ if (!instruction)
return nullptr;
static SimplificationRules rules;
assertThrow(rules.isInitialized(), OptimizerException, "Rule list not properly initialized.");
- FunctionalInstruction const& instruction = boost::get(_expr);
- for (auto const& rule: rules.m_rules[uint8_t(instruction.instruction)])
+ for (auto const& rule: rules.m_rules[uint8_t(instruction->first)])
{
rules.resetMatchGroups();
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();
}
+boost::optional const*>>
+ SimplificationRules::instructionAndArguments(Dialect const& _dialect, Expression const& _expr)
+{
+ if (_expr.type() == typeid(FunctionalInstruction))
+ return make_pair(boost::get(_expr).instruction, &boost::get(_expr).arguments);
+ else if (_expr.type() == typeid(FunctionCall))
+ if (auto const* dialect = dynamic_cast(&_dialect))
+ if (auto const* builtin = dialect->builtin(boost::get(_expr).functionName.name))
+ if (builtin->instruction)
+ return make_pair(*builtin->instruction, &boost::get(_expr).arguments);
+
+ return {};
+}
+
void SimplificationRules::addRules(vector> const& _rules)
{
for (auto const& r: _rules)
@@ -139,14 +154,12 @@ bool Pattern::matches(
}
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;
- FunctionalInstruction const& instr = boost::get(*expr);
- if (m_instruction != instr.instruction)
- return false;
- assertThrow(m_arguments.size() == instr.arguments.size(), OptimizerException, "");
+ assertThrow(m_arguments.size() == instrAndArgs->second->size(), OptimizerException, "");
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;
}
else
@@ -208,6 +221,7 @@ Expression Pattern::toExpression(SourceLocation const& _location) const
vector arguments;
for (auto const& arg: m_arguments)
arguments.emplace_back(arg.toExpression(_location));
+ // TODO convert to FunctionCall
return FunctionalInstruction{_location, m_instruction, std::move(arguments)};
}
assertThrow(false, OptimizerException, "Pattern of kind 'any', but no match group.");
diff --git a/libyul/optimiser/SimplificationRules.h b/libyul/optimiser/SimplificationRules.h
index 40ae43260..a63071b9f 100644
--- a/libyul/optimiser/SimplificationRules.h
+++ b/libyul/optimiser/SimplificationRules.h
@@ -26,6 +26,7 @@
#include
#include
+#include
#include
#include
@@ -55,6 +56,10 @@ public:
/// Checks whether the rulelist is non-empty. This is usually enforced
/// by the constructor, but we had some issues with static initialization.
bool isInitialized() const;
+
+ static boost::optional const*>>
+ instructionAndArguments(Dialect const& _dialect, Expression const& _expr);
+
private:
void addRules(std::vector> const& _rules);
void addRule(dev::eth::SimplificationRule const& _rule);
diff --git a/libyul/optimiser/StackCompressor.cpp b/libyul/optimiser/StackCompressor.cpp
index 94b886074..f61e8925c 100644
--- a/libyul/optimiser/StackCompressor.cpp
+++ b/libyul/optimiser/StackCompressor.cpp
@@ -69,7 +69,7 @@ public:
{
YulString varName = _varDecl.variables.front().name;
if (m_value.count(varName))
- m_expressionCodeCost[varName] = CodeCost::codeCost(*m_value[varName]);
+ m_expressionCodeCost[varName] = CodeCost::codeCost(m_dialect, *m_value[varName]);
}
}
diff --git a/libyul/optimiser/Suite.cpp b/libyul/optimiser/Suite.cpp
index 3b497a117..a71b0ead8 100644
--- a/libyul/optimiser/Suite.cpp
+++ b/libyul/optimiser/Suite.cpp
@@ -73,14 +73,14 @@ void OptimiserSuite::run(
FunctionHoister{}(ast);
BlockFlattener{}(ast);
ForLoopInitRewriter{}(ast);
- DeadCodeEliminator{}(ast);
+ DeadCodeEliminator{_dialect}(ast);
FunctionGrouper{}(ast);
EquivalentFunctionCombiner::run(ast);
UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers);
BlockFlattener{}(ast);
- ControlFlowSimplifier{}(ast);
+ ControlFlowSimplifier{_dialect}(ast);
StructuralSimplifier{_dialect}(ast);
- ControlFlowSimplifier{}(ast);
+ ControlFlowSimplifier{_dialect}(ast);
BlockFlattener{}(ast);
// None of the above can make stack problems worse.
@@ -110,11 +110,11 @@ void OptimiserSuite::run(
{
// still in SSA, perform structural simplification
- ControlFlowSimplifier{}(ast);
+ ControlFlowSimplifier{_dialect}(ast);
StructuralSimplifier{_dialect}(ast);
- ControlFlowSimplifier{}(ast);
+ ControlFlowSimplifier{_dialect}(ast);
BlockFlattener{}(ast);
- DeadCodeEliminator{}(ast);
+ DeadCodeEliminator{_dialect}(ast);
UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers);
}
{
@@ -166,8 +166,8 @@ void OptimiserSuite::run(
ExpressionSimplifier::run(_dialect, ast);
StructuralSimplifier{_dialect}(ast);
BlockFlattener{}(ast);
- DeadCodeEliminator{}(ast);
- ControlFlowSimplifier{}(ast);
+ DeadCodeEliminator{_dialect}(ast);
+ ControlFlowSimplifier{_dialect}(ast);
CommonSubexpressionEliminator{_dialect}(ast);
SSATransform::run(ast, dispenser);
RedundantAssignEliminator::run(_dialect, ast);
@@ -202,8 +202,8 @@ void OptimiserSuite::run(
// message once we perform code generation.
StackCompressor::run(_dialect, ast, _optimizeStackAllocation, stackCompressorMaxIterations);
BlockFlattener{}(ast);
- DeadCodeEliminator{}(ast);
- ControlFlowSimplifier{}(ast);
+ DeadCodeEliminator{_dialect}(ast);
+ ControlFlowSimplifier{_dialect}(ast);
FunctionGrouper{}(ast);
VarNameCleaner{ast, _dialect, reservedIdentifiers}(ast);
diff --git a/test/cmdlineTests/strict_asm_jump/err b/test/cmdlineTests/strict_asm_jump/err
index ba4623090..95dd70c75 100644
--- a/test/cmdlineTests/strict_asm_jump/err
+++ b/test/cmdlineTests/strict_asm_jump/err
@@ -1,4 +1,4 @@
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) }
- ^-----^
+ ^--^
diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp
index 809a72636..364e46ee0 100644
--- a/test/libsolidity/InlineAssembly.cpp
+++ b/test/libsolidity/InlineAssembly.cpp
@@ -516,11 +516,11 @@ BOOST_AUTO_TEST_CASE(no_opcodes_in_strict)
{
BOOST_CHECK(successParse("{ pop(callvalue) }"));
BOOST_CHECK(successParse("{ callvalue pop }"));
- CHECK_STRICT_ERROR("{ pop(callvalue) }", ParserError, "Non-functional instructions are not allowed in this context.");
- CHECK_STRICT_ERROR("{ callvalue pop }", ParserError, "Call or assignment expected");
+ CHECK_STRICT_ERROR("{ pop(callvalue) }", ParserError, "Expected '(' but got ')'");
+ CHECK_STRICT_ERROR("{ callvalue pop }", ParserError, "Expected '(' but got identifier");
SUCCESS_STRICT("{ pop(callvalue()) }");
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)
@@ -546,7 +546,6 @@ BOOST_AUTO_TEST_CASE(no_dup_swap_in_strict)
BOOST_CHECK(successParse("{ dup2 pop }"));
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_STRICT_ERROR("{ switch dup1 case 0 {} }", ParserError, "Instruction \"dup1\" not allowed in this context");
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/test/libyul/Parser.cpp b/test/libyul/Parser.cpp
index 37d6389dd..396ae5c30 100644
--- a/test/libyul/Parser.cpp
+++ b/test/libyul/Parser.cpp
@@ -549,8 +549,11 @@ BOOST_AUTO_TEST_CASE(builtins_parser)
SimpleDialect 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("{ builtin := 6 }", ParserError, "Cannot assign to builtin function \"builtin\".", dialect);
- CHECK_ERROR_DIALECT("{ function g() -> a,b {} builtin, builtin2 := g() }", ParserError, "Cannot assign to builtin function \"builtin\".", dialect);
+ CHECK_ERROR_DIALECT("{ builtin := 6 }", ParserError, "Expected '(' but got ':='", 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)
diff --git a/test/libyul/YulOptimizerTest.cpp b/test/libyul/YulOptimizerTest.cpp
index 6f0f8df22..2964b212d 100644
--- a/test/libyul/YulOptimizerTest.cpp
+++ b/test/libyul/YulOptimizerTest.cpp
@@ -201,7 +201,7 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line
CommonSubexpressionEliminator{*m_dialect}(*m_ast);
ExpressionSimplifier::run(*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);
}
@@ -214,7 +214,7 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line
{
disambiguate();
ForLoopInitRewriter{}(*m_ast);
- DeadCodeEliminator{}(*m_ast);
+ DeadCodeEliminator{*m_dialect}(*m_ast);
}
else if (m_optimizerStep == "ssaTransform")
{
@@ -237,7 +237,7 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line
else if (m_optimizerStep == "controlFlowSimplifier")
{
disambiguate();
- ControlFlowSimplifier{}(*m_ast);
+ ControlFlowSimplifier{*m_dialect}(*m_ast);
}
else if (m_optimizerStep == "structuralSimplifier")
{
diff --git a/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp b/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp
index 1a03704b4..13b8066fa 100644
--- a/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp
+++ b/test/tools/yulInterpreter/EVMInstructionInterpreter.cpp
@@ -22,6 +22,7 @@
#include
+#include
#include
#include
@@ -444,13 +445,15 @@ u256 EVMInstructionInterpreter::eval(
return 0;
}
-u256 EVMInstructionInterpreter::evalBuiltin(YulString _name, const std::vector& _arguments)
+u256 EVMInstructionInterpreter::evalBuiltin(BuiltinFunctionForEVM const& _fun, const std::vector& _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;
- else if (_name == "dataoffset"_yulstring)
+ else if (_fun.name == "dataoffset"_yulstring)
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.
if (logMemoryWrite(_arguments.at(0), _arguments.at(2)))
@@ -463,7 +466,7 @@ u256 EVMInstructionInterpreter::evalBuiltin(YulString _name, const std::vector const& _arguments);
/// Evaluate builtin function
- dev::u256 evalBuiltin(YulString _name, std::vector const& _arguments);
+ dev::u256 evalBuiltin(BuiltinFunctionForEVM const& _fun, std::vector const& _arguments);
private:
/// Record a memory read in the trace. Also updates m_state.msize
diff --git a/test/tools/yulInterpreter/Interpreter.cpp b/test/tools/yulInterpreter/Interpreter.cpp
index f63a73cda..ff6a0cc99 100644
--- a/test/tools/yulInterpreter/Interpreter.cpp
+++ b/test/tools/yulInterpreter/Interpreter.cpp
@@ -208,12 +208,13 @@ void ExpressionEvaluator::operator()(FunctionCall const& _funCall)
{
evaluateArgs(_funCall.arguments);
- if (dynamic_cast(&m_dialect) && m_dialect.builtin(_funCall.functionName.name))
- {
- EVMInstructionInterpreter interpreter(m_state);
- setValue(interpreter.evalBuiltin(_funCall.functionName.name, values()));
- return;
- }
+ if (EVMDialect const* dialect = dynamic_cast(&m_dialect))
+ if (BuiltinFunctionForEVM const* fun = dialect->builtin(_funCall.functionName.name))
+ {
+ EVMInstructionInterpreter interpreter(m_state);
+ setValue(interpreter.evalBuiltin(*fun, values()));
+ return;
+ }
solAssert(m_functions.count(_funCall.functionName.name), "");
FunctionDefinition const& fun = *m_functions.at(_funCall.functionName.name);
diff --git a/test/tools/yulopti.cpp b/test/tools/yulopti.cpp
index 5ba1549ed..816361246 100644
--- a/test/tools/yulopti.cpp
+++ b/test/tools/yulopti.cpp
@@ -179,13 +179,13 @@ public:
(StructuralSimplifier{m_dialect})(*m_ast);
break;
case 'n':
- (ControlFlowSimplifier{})(*m_ast);
+ (ControlFlowSimplifier{m_dialect})(*m_ast);
break;
case 'u':
UnusedPruner::runUntilStabilised(m_dialect, *m_ast);
break;
case 'D':
- DeadCodeEliminator{}(*m_ast);
+ DeadCodeEliminator{m_dialect}(*m_ast);
break;
case 'a':
SSATransform::run(*m_ast, *m_nameDispenser);