diff options
author | chriseth <chris@ethereum.org> | 2018-12-04 21:11:49 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-12-04 21:11:49 +0800 |
commit | 8654f8f6d4cb8f609d5b43df217aff1406acbe6a (patch) | |
tree | f6c984d082b7da3ce34e464f392d7bab8d445301 /libyul | |
parent | 4e5dabf832ddefc30035e67ad450a12f17c8345a (diff) | |
parent | 99db4e3ff45c2a8d5d9c645774f099b82b7618ec (diff) | |
download | dexon-solidity-8654f8f6d4cb8f609d5b43df217aff1406acbe6a.tar.gz dexon-solidity-8654f8f6d4cb8f609d5b43df217aff1406acbe6a.tar.zst dexon-solidity-8654f8f6d4cb8f609d5b43df217aff1406acbe6a.zip |
Merge pull request #5573 from ethereum/builtins
[Yul] Introduce the concept of builtin functions.
Diffstat (limited to 'libyul')
-rw-r--r-- | libyul/AsmAnalysis.cpp | 14 | ||||
-rw-r--r-- | libyul/AsmAnalysis.h | 10 | ||||
-rw-r--r-- | libyul/AsmDataForward.h | 7 | ||||
-rw-r--r-- | libyul/AsmParser.cpp | 26 | ||||
-rw-r--r-- | libyul/AsmParser.h | 13 | ||||
-rw-r--r-- | libyul/Dialect.h | 82 | ||||
-rw-r--r-- | libyul/ObjectParser.cpp | 2 | ||||
-rw-r--r-- | libyul/ObjectParser.h | 7 |
8 files changed, 120 insertions, 41 deletions
diff --git a/libyul/AsmAnalysis.cpp b/libyul/AsmAnalysis.cpp index d2efdd9f..5215e5c2 100644 --- a/libyul/AsmAnalysis.cpp +++ b/libyul/AsmAnalysis.cpp @@ -101,7 +101,7 @@ bool AsmAnalyzer::operator()(Literal const& _literal) } else if (_literal.kind == LiteralKind::Boolean) { - solAssert(m_flavour == AsmFlavour::Yul, ""); + solAssert(m_dialect.flavour == AsmFlavour::Yul, ""); solAssert(_literal.value == YulString{string("true")} || _literal.value == YulString{string("false")}, ""); } m_info.stackHeightInfo[&_literal] = m_stackHeight; @@ -164,7 +164,7 @@ bool AsmAnalyzer::operator()(Identifier const& _identifier) bool AsmAnalyzer::operator()(FunctionalInstruction const& _instr) { - solAssert(m_flavour != AsmFlavour::Yul, ""); + solAssert(m_dialect.flavour != AsmFlavour::Yul, ""); bool success = true; for (auto const& arg: _instr.arguments | boost::adaptors::reversed) if (!expectExpression(arg)) @@ -182,9 +182,9 @@ bool AsmAnalyzer::operator()(ExpressionStatement const& _statement) { int initialStackHeight = m_stackHeight; bool success = boost::apply_visitor(*this, _statement.expression); - if (m_stackHeight != initialStackHeight && (m_flavour != AsmFlavour::Loose || m_errorTypeForLoose)) + if (m_stackHeight != initialStackHeight && (m_dialect.flavour != AsmFlavour::Loose || m_errorTypeForLoose)) { - Error::Type errorType = m_flavour == AsmFlavour::Loose ? *m_errorTypeForLoose : Error::Type::TypeError; + Error::Type errorType = m_dialect.flavour == AsmFlavour::Loose ? *m_errorTypeForLoose : Error::Type::TypeError; string msg = "Top-level expressions are not supposed to return values (this expression returns " + to_string(m_stackHeight - initialStackHeight) + @@ -563,7 +563,7 @@ Scope& AsmAnalyzer::scope(Block const* _block) } void AsmAnalyzer::expectValidType(string const& type, SourceLocation const& _location) { - if (m_flavour != AsmFlavour::Yul) + if (m_dialect.flavour != AsmFlavour::Yul) return; if (!builtinTypes.count(type)) @@ -623,7 +623,7 @@ void AsmAnalyzer::warnOnInstructions(solidity::Instruction _instr, SourceLocatio if (_instr == solidity::Instruction::JUMP || _instr == solidity::Instruction::JUMPI || _instr == solidity::Instruction::JUMPDEST) { - solAssert(m_flavour == AsmFlavour::Loose, ""); + solAssert(m_dialect.flavour == AsmFlavour::Loose, ""); m_errorReporter.error( m_errorTypeForLoose ? *m_errorTypeForLoose : Error::Type::Warning, _location, @@ -636,7 +636,7 @@ void AsmAnalyzer::warnOnInstructions(solidity::Instruction _instr, SourceLocatio void AsmAnalyzer::checkLooseFeature(SourceLocation const& _location, string const& _description) { - if (m_flavour != AsmFlavour::Loose) + if (m_dialect.flavour != AsmFlavour::Loose) solAssert(false, _description); else if (m_errorTypeForLoose) m_errorReporter.error(*m_errorTypeForLoose, _location, _description); diff --git a/libyul/AsmAnalysis.h b/libyul/AsmAnalysis.h index 34e32eb0..ec2b8868 100644 --- a/libyul/AsmAnalysis.h +++ b/libyul/AsmAnalysis.h @@ -23,12 +23,12 @@ #include <liblangutil/Exceptions.h> #include <liblangutil/EVMVersion.h> +#include <libyul/Dialect.h> #include <libyul/AsmScope.h> +#include <libyul/AsmDataForward.h> #include <libyul/backends/evm/AbstractAssembly.h> -#include <libyul/AsmDataForward.h> - #include <boost/variant.hpp> #include <boost/optional.hpp> @@ -59,14 +59,14 @@ public: langutil::ErrorReporter& _errorReporter, dev::solidity::EVMVersion _evmVersion, boost::optional<langutil::Error::Type> _errorTypeForLoose, - AsmFlavour _flavour = AsmFlavour::Loose, + Dialect _dialect = Dialect::looseAssemblyForEVM(), ExternalIdentifierAccess::Resolver const& _resolver = ExternalIdentifierAccess::Resolver() ): m_resolver(_resolver), m_info(_analysisInfo), m_errorReporter(_errorReporter), m_evmVersion(_evmVersion), - m_flavour(_flavour), + m_dialect(std::move(_dialect)), m_errorTypeForLoose(_errorTypeForLoose) {} @@ -115,7 +115,7 @@ private: AsmAnalysisInfo& m_info; langutil::ErrorReporter& m_errorReporter; dev::solidity::EVMVersion m_evmVersion; - AsmFlavour m_flavour = AsmFlavour::Loose; + Dialect m_dialect = Dialect::looseAssemblyForEVM(); boost::optional<langutil::Error::Type> m_errorTypeForLoose; }; diff --git a/libyul/AsmDataForward.h b/libyul/AsmDataForward.h index 046c8248..de564425 100644 --- a/libyul/AsmDataForward.h +++ b/libyul/AsmDataForward.h @@ -49,11 +49,4 @@ struct TypedName; using Expression = boost::variant<FunctionalInstruction, FunctionCall, Identifier, Literal>; using Statement = boost::variant<ExpressionStatement, Instruction, Label, StackAssignment, Assignment, VariableDeclaration, FunctionDefinition, If, Switch, ForLoop, Block>; -enum class AsmFlavour -{ - Loose, // no types, EVM instructions as function, jumps and direct stack manipulations - Strict, // no types, EVM instructions as functions, but no jumps and no direct stack manipulations - Yul // same as Strict mode with types -}; - } diff --git a/libyul/AsmParser.cpp b/libyul/AsmParser.cpp index 417c0251..5f393b29 100644 --- a/libyul/AsmParser.cpp +++ b/libyul/AsmParser.cpp @@ -107,7 +107,7 @@ Statement Parser::parseStatement() return parseForLoop(); case Token::Assign: { - if (m_flavour != AsmFlavour::Loose) + if (m_dialect.flavour != AsmFlavour::Loose) break; StackAssignment assignment = createWithLocation<StackAssignment>(); advance(); @@ -174,7 +174,7 @@ Statement Parser::parseStatement() if (currentToken() == Token::Assign && peekNextToken() != Token::Colon) { Assignment assignment = createWithLocation<Assignment>(identifier.location); - if (m_flavour != AsmFlavour::Yul && instructions().count(identifier.name.str())) + if (m_dialect.flavour != AsmFlavour::Yul && instructions().count(identifier.name.str())) fatalParserError("Cannot use instruction names for identifier names."); advance(); assignment.variableNames.emplace_back(identifier); @@ -185,7 +185,7 @@ Statement Parser::parseStatement() else { // label - if (m_flavour != AsmFlavour::Loose) + if (m_dialect.flavour != AsmFlavour::Loose) fatalParserError("Labels are not supported."); Label label = createWithLocation<Label>(identifier.location); label.name = identifier.name; @@ -193,7 +193,7 @@ Statement Parser::parseStatement() } } default: - if (m_flavour != AsmFlavour::Loose) + if (m_dialect.flavour != AsmFlavour::Loose) fatalParserError("Call or assignment expected."); break; } @@ -269,7 +269,7 @@ Expression Parser::parseExpression() instructionNames().at(instr.instruction) + "\" not allowed in this context." ); - if (m_flavour != AsmFlavour::Loose && currentToken() != Token::LParen) + if (m_dialect.flavour != AsmFlavour::Loose && currentToken() != Token::LParen) fatalParserError( "Non-functional instructions are not allowed in this context." ); @@ -289,7 +289,7 @@ Expression Parser::parseExpression() else if (operation.type() == typeid(Instruction)) { // Instructions not taking arguments are allowed as expressions. - solAssert(m_flavour == AsmFlavour::Loose, ""); + solAssert(m_dialect.flavour == AsmFlavour::Loose, ""); Instruction& instr = boost::get<Instruction>(operation); return FunctionalInstruction{std::move(instr.location), instr.instruction, {}}; } @@ -358,7 +358,7 @@ Parser::ElementaryOperation Parser::parseElementaryOperation() else literal = YulString{currentLiteral()}; // first search the set of instructions. - if (m_flavour != AsmFlavour::Yul && instructions().count(literal.str())) + if (m_dialect.flavour != AsmFlavour::Yul && instructions().count(literal.str())) { dev::solidity::Instruction const& instr = instructions().at(literal.str()); ret = Instruction{location(), instr}; @@ -399,7 +399,7 @@ Parser::ElementaryOperation Parser::parseElementaryOperation() {} }; advance(); - if (m_flavour == AsmFlavour::Yul) + if (m_dialect.flavour == AsmFlavour::Yul) { expectToken(Token::Colon); literal.location.end = endPosition(); @@ -412,7 +412,7 @@ Parser::ElementaryOperation Parser::parseElementaryOperation() } default: fatalParserError( - m_flavour == AsmFlavour::Yul ? + m_dialect.flavour == AsmFlavour::Yul ? "Literal or identifier expected." : "Literal, identifier or instruction expected." ); @@ -482,7 +482,7 @@ Expression Parser::parseCall(Parser::ElementaryOperation&& _initialOp) RecursionGuard recursionGuard(*this); if (_initialOp.type() == typeid(Instruction)) { - solAssert(m_flavour != AsmFlavour::Yul, "Instructions are invalid in Yul"); + solAssert(m_dialect.flavour != AsmFlavour::Yul, "Instructions are invalid in Yul"); Instruction& instruction = boost::get<Instruction>(_initialOp); FunctionalInstruction ret; ret.instruction = instruction.instruction; @@ -553,7 +553,7 @@ Expression Parser::parseCall(Parser::ElementaryOperation&& _initialOp) } else fatalParserError( - m_flavour == AsmFlavour::Yul ? + m_dialect.flavour == AsmFlavour::Yul ? "Function name expected." : "Assembly instruction or function name required in front of \"(\")" ); @@ -566,7 +566,7 @@ TypedName Parser::parseTypedName() RecursionGuard recursionGuard(*this); TypedName typedName = createWithLocation<TypedName>(); typedName.name = expectAsmIdentifier(); - if (m_flavour == AsmFlavour::Yul) + if (m_dialect.flavour == AsmFlavour::Yul) { expectToken(Token::Colon); typedName.location.end = endPosition(); @@ -578,7 +578,7 @@ TypedName Parser::parseTypedName() YulString Parser::expectAsmIdentifier() { YulString name = YulString{currentLiteral()}; - if (m_flavour == AsmFlavour::Yul) + if (m_dialect.flavour == AsmFlavour::Yul) { switch (currentToken()) { diff --git a/libyul/AsmParser.h b/libyul/AsmParser.h index c1b22334..b40a717c 100644 --- a/libyul/AsmParser.h +++ b/libyul/AsmParser.h @@ -22,21 +22,24 @@ #pragma once -#include <memory> -#include <vector> #include <libyul/AsmData.h> +#include <libyul/Dialect.h> + #include <liblangutil/SourceLocation.h> #include <liblangutil/Scanner.h> #include <liblangutil/ParserBase.h> +#include <memory> +#include <vector> + namespace yul { class Parser: public langutil::ParserBase { public: - explicit Parser(langutil::ErrorReporter& _errorReporter, AsmFlavour _flavour = AsmFlavour::Loose): - ParserBase(_errorReporter), m_flavour(_flavour) {} + explicit Parser(langutil::ErrorReporter& _errorReporter, Dialect _dialect = Dialect::looseAssemblyForEVM()): + ParserBase(_errorReporter), m_dialect(std::move(_dialect)) {} /// Parses an inline assembly block starting with `{` and ending with `}`. /// @param _reuseScanner if true, do check for end of input after the `}`. @@ -83,7 +86,7 @@ protected: static bool isValidNumberLiteral(std::string const& _literal); private: - AsmFlavour m_flavour = AsmFlavour::Loose; + Dialect m_dialect = Dialect::looseAssemblyForEVM(); }; } diff --git a/libyul/Dialect.h b/libyul/Dialect.h new file mode 100644 index 00000000..3ea16014 --- /dev/null +++ b/libyul/Dialect.h @@ -0,0 +1,82 @@ +/* + 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/>. +*/ +/** + * Yul dialect. + */ + +#pragma once + +#include <libyul/YulString.h> + +#include <memory> + +namespace yul +{ + +class YulString; +using Type = YulString; + +enum class AsmFlavour +{ + Loose, // no types, EVM instructions as function, jumps and direct stack manipulations + Strict, // no types, EVM instructions as functions, but no jumps and no direct stack manipulations + Yul // same as Strict mode with types +}; + +struct BuiltinFunction +{ + YulString name; + std::vector<Type> parameters; + std::vector<Type> returns; + bool movable; +}; + +/** + * Class to query for builtin functions and their semantics. + */ +struct Builtins +{ + virtual ~Builtins() = default; + /// @returns the builtin function of the given name or a nullptr if it is not a builtin function. + virtual BuiltinFunction const* query(YulString /*_name*/) const { return nullptr; } +}; + +struct Dialect +{ + AsmFlavour flavour = AsmFlavour::Loose; + std::shared_ptr<Builtins> builtins; + + Dialect(AsmFlavour _flavour, std::shared_ptr<Builtins> _builtins): + flavour(_flavour), builtins(std::move(_builtins)) + {} + static Dialect looseAssemblyForEVM() + { + return Dialect{AsmFlavour::Loose, std::make_shared<Builtins>()}; + } + static Dialect strictAssemblyForEVM() + { + // The EVM instructions will be moved to builtins at some point. + return Dialect{AsmFlavour::Strict, std::make_shared<Builtins>()}; + } + static Dialect yul() + { + // Will have to add builtins later. + return Dialect{AsmFlavour::Yul, std::make_shared<Builtins>()}; + } +}; + +} diff --git a/libyul/ObjectParser.cpp b/libyul/ObjectParser.cpp index 43dd4be9..ba19e099 100644 --- a/libyul/ObjectParser.cpp +++ b/libyul/ObjectParser.cpp @@ -104,7 +104,7 @@ shared_ptr<Block> ObjectParser::parseCode() shared_ptr<Block> ObjectParser::parseBlock() { - Parser parser(m_errorReporter, m_flavour); + Parser parser(m_errorReporter, m_dialect); shared_ptr<Block> block = parser.parse(m_scanner, true); yulAssert(block || m_errorReporter.hasErrors(), "Invalid block but no error!"); return block; diff --git a/libyul/ObjectParser.h b/libyul/ObjectParser.h index 1d88a119..59efb8ab 100644 --- a/libyul/ObjectParser.h +++ b/libyul/ObjectParser.h @@ -22,6 +22,7 @@ #include <libyul/YulString.h> #include <libyul/Object.h> +#include <libyul/Dialect.h> #include <liblangutil/ErrorReporter.h> #include <liblangutil/ParserBase.h> @@ -46,9 +47,9 @@ class ObjectParser: public langutil::ParserBase public: explicit ObjectParser( langutil::ErrorReporter& _errorReporter, - yul::AsmFlavour _flavour = yul::AsmFlavour::Loose + Dialect _dialect = Dialect::looseAssemblyForEVM() ): - ParserBase(_errorReporter), m_flavour(_flavour) {} + ParserBase(_errorReporter), m_dialect(std::move(_dialect)) {} /// Parses a Yul object. /// Falls back to code-only parsing if the source starts with `{`. @@ -66,7 +67,7 @@ private: YulString parseUniqueName(Object const* _containingObject); void addNamedSubObject(Object& _container, YulString _name, std::shared_ptr<ObjectNode> _subObject); - yul::AsmFlavour m_flavour; + Dialect m_dialect; }; } |