From 123ea0a2509c06fe2e5fa13581522324f258ffb5 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 19 Dec 2019 17:58:20 +0100 Subject: [PATCH 1/4] Remove asm flavour. --- .../codegen/ir/IRGeneratorForStatements.cpp | 2 +- libyul/AsmAnalysis.cpp | 27 +++------- libyul/AsmAnalysis.h | 2 +- libyul/AsmParser.cpp | 19 ++----- libyul/AsmPrinter.cpp | 2 +- libyul/AsmPrinter.h | 5 +- libyul/CMakeLists.txt | 1 + libyul/CompilabilityChecker.cpp | 5 -- libyul/Dialect.cpp | 53 +++++++++++++++++++ libyul/Dialect.h | 20 +++---- libyul/Object.cpp | 2 +- libyul/backends/evm/EVMDialect.cpp | 9 ++-- libyul/backends/evm/EVMDialect.h | 2 +- libyul/backends/evm/NoOutputAssembly.cpp | 2 +- libyul/backends/wasm/WasmDialect.cpp | 7 ++- libyul/backends/wasm/WordSizeTransform.cpp | 23 ++++---- libyul/backends/wasm/WordSizeTransform.h | 10 +++- test/cmdlineTests/evm_to_wasm/output | 10 ++-- .../standard_eWasm_requested/output.json | 12 ++--- test/libsolidity/InlineAssembly.cpp | 8 +-- .../invalid/empty_fun_arg_beginning.sol | 2 +- .../invalid/empty_fun_arg_end.sol | 2 +- .../invalid/empty_fun_arg_middle.sol | 2 +- .../inlineAssembly/invalid/invalid_number.sol | 2 +- .../invalid/missing_variable_in_assign.sol | 2 +- .../invalid/whitespace_in_assignment.sol | 2 +- .../whitespace_in_multiple_assignment.sol | 2 +- test/libyul/Common.cpp | 23 +++++++- test/libyul/Common.h | 6 +++ test/libyul/Parser.cpp | 18 +++---- test/libyul/YulOptimizerTest.cpp | 17 +++--- 31 files changed, 173 insertions(+), 126 deletions(-) create mode 100644 libyul/Dialect.cpp diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index 87782a4a3..214640e5f 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -78,7 +78,7 @@ struct CopyTranslate: public yul::ASTCopier _identifier.location, yul::LiteralKind::Number, yul::YulString{value}, - yul::YulString{"uint256"} + {} }; } } diff --git a/libyul/AsmAnalysis.cpp b/libyul/AsmAnalysis.cpp index 3f82b169f..d47ce115c 100644 --- a/libyul/AsmAnalysis.cpp +++ b/libyul/AsmAnalysis.cpp @@ -43,13 +43,6 @@ using namespace solidity::yul; using namespace solidity::util; using namespace solidity::langutil; -namespace -{ - -set const builtinTypes{"bool", "u8", "s8", "u32", "s32", "u64", "s64", "u128", "s128", "u256", "s256"}; - -} - bool AsmAnalyzer::analyze(Block const& _block) { bool success = false; @@ -88,7 +81,7 @@ AsmAnalysisInfo AsmAnalyzer::analyzeStrictAssertCorrect(Dialect const& _dialect, bool AsmAnalyzer::operator()(Literal const& _literal) { - expectValidType(_literal.type.str(), _literal.location); + expectValidType(_literal.type, _literal.location); ++m_stackHeight; if (_literal.kind == LiteralKind::String && _literal.value.str().size() > 32) { @@ -107,10 +100,7 @@ bool AsmAnalyzer::operator()(Literal const& _literal) return false; } else if (_literal.kind == LiteralKind::Boolean) - { - yulAssert(m_dialect.flavour == AsmFlavour::Yul, ""); yulAssert(_literal.value == "true"_yulstring || _literal.value == "false"_yulstring, ""); - } m_info.stackHeightInfo[&_literal] = m_stackHeight; return true; } @@ -250,7 +240,7 @@ bool AsmAnalyzer::operator()(VariableDeclaration const& _varDecl) for (auto const& variable: _varDecl.variables) { - expectValidType(variable.type.str(), variable.location); + expectValidType(variable.type, variable.location); m_activeVariables.insert(&std::get(m_currentScope->identifiers.at(variable.name))); } m_info.stackHeightInfo[&_varDecl] = m_stackHeight; @@ -265,7 +255,7 @@ bool AsmAnalyzer::operator()(FunctionDefinition const& _funDef) Scope& varScope = scope(virtualBlock); for (auto const& var: _funDef.parameters + _funDef.returnVariables) { - expectValidType(var.type.str(), var.location); + expectValidType(var.type, var.location); m_activeVariables.insert(&std::get(varScope.identifiers.at(var.name))); } @@ -388,7 +378,6 @@ bool AsmAnalyzer::operator()(Switch const& _switch) if (!expectExpression(*_switch.expression)) success = false; - if (m_dialect.flavour == AsmFlavour::Yul) { YulString caseType; bool mismatchingTypes = false; @@ -630,15 +619,12 @@ Scope& AsmAnalyzer::scope(Block const* _block) yulAssert(scopePtr, "Scope requested but not present."); return *scopePtr; } -void AsmAnalyzer::expectValidType(string const& type, SourceLocation const& _location) +void AsmAnalyzer::expectValidType(YulString _type, SourceLocation const& _location) { - if (m_dialect.flavour != AsmFlavour::Yul) - return; - - if (!builtinTypes.count(type)) + if (!_type.empty() && !contains(m_dialect.types, _type)) m_errorReporter.typeError( _location, - "\"" + type + "\" is not a valid type (user defined types are not yet supported)." + "\"" + _type.str() + "\" is not a valid type (user defined types are not yet supported)." ); } @@ -658,7 +644,6 @@ bool AsmAnalyzer::warnOnInstructions(evmasm::Instruction _instr, SourceLocation yulAssert(m_evmVersion.supportsReturndata() == m_evmVersion.hasStaticCall(), ""); // Similarly we assume bitwise shifting and create2 go together. yulAssert(m_evmVersion.hasBitwiseShifting() == m_evmVersion.hasCreate2(), ""); - yulAssert(m_dialect.flavour != AsmFlavour::Yul, ""); auto errorForVM = [=](string const& vmKindMessage) { m_errorReporter.typeError( diff --git a/libyul/AsmAnalysis.h b/libyul/AsmAnalysis.h index 9bd9fc1b1..0bc1884df 100644 --- a/libyul/AsmAnalysis.h +++ b/libyul/AsmAnalysis.h @@ -102,7 +102,7 @@ private: bool checkAssignment(Identifier const& _assignment, size_t _valueSize = size_t(-1)); Scope& scope(Block const* _block); - void expectValidType(std::string const& type, langutil::SourceLocation const& _location); + void expectValidType(YulString _type, langutil::SourceLocation const& _location); bool warnOnInstructions(evmasm::Instruction _instr, langutil::SourceLocation const& _location); bool warnOnInstructions(std::string const& _instrIdentifier, langutil::SourceLocation const& _location); diff --git a/libyul/AsmParser.cpp b/libyul/AsmParser.cpp index 0dd2a633c..3b955b96f 100644 --- a/libyul/AsmParser.cpp +++ b/libyul/AsmParser.cpp @@ -369,23 +369,18 @@ Parser::ElementaryOperation Parser::parseElementaryOperation() {} }; advance(); - if (m_dialect.flavour == AsmFlavour::Yul) + if (currentToken() == Token::Colon) { expectToken(Token::Colon); literal.location.end = endPosition(); literal.type = expectAsmIdentifier(); } - else if (kind == LiteralKind::Boolean) - fatalParserError("True and false are not valid literals."); + ret = std::move(literal); break; } default: - fatalParserError( - m_dialect.flavour == AsmFlavour::Yul ? - "Literal or identifier expected." : - "Literal, identifier or instruction expected." - ); + fatalParserError("Literal or identifier expected."); } return ret; } @@ -474,11 +469,7 @@ Expression Parser::parseCall(Parser::ElementaryOperation&& _initialOp) else if (holds_alternative(_initialOp)) ret = std::move(std::get(_initialOp)); else - fatalParserError( - m_dialect.flavour == AsmFlavour::Yul ? - "Function name expected." : - "Assembly instruction or function name required in front of \"(\")" - ); + fatalParserError("Function name expected."); expectToken(Token::LParen); if (currentToken() != Token::RParen) @@ -500,7 +491,7 @@ TypedName Parser::parseTypedName() RecursionGuard recursionGuard(*this); TypedName typedName = createWithLocation(); typedName.name = expectAsmIdentifier(); - if (m_dialect.flavour == AsmFlavour::Yul) + if (currentToken() == Token::Colon) { expectToken(Token::Colon); typedName.location.end = endPosition(); diff --git a/libyul/AsmPrinter.cpp b/libyul/AsmPrinter.cpp index eb894f0a1..a62db6123 100644 --- a/libyul/AsmPrinter.cpp +++ b/libyul/AsmPrinter.cpp @@ -238,7 +238,7 @@ string AsmPrinter::formatTypedName(TypedName _variable) const string AsmPrinter::appendTypeName(YulString _type) const { - if (m_yul && !_type.empty()) + if (!_type.empty()) return ":" + _type.str(); return ""; } diff --git a/libyul/AsmPrinter.h b/libyul/AsmPrinter.h index 331b502fa..30569922d 100644 --- a/libyul/AsmPrinter.h +++ b/libyul/AsmPrinter.h @@ -28,11 +28,12 @@ namespace solidity::yul { +struct Dialect; class AsmPrinter { public: - explicit AsmPrinter(bool _yul = false): m_yul(_yul) {} + explicit AsmPrinter() {} std::string operator()(Literal const& _literal) const; std::string operator()(Identifier const& _identifier) const; @@ -52,8 +53,6 @@ public: private: std::string formatTypedName(TypedName _variable) const; std::string appendTypeName(YulString _type) const; - - bool m_yul = false; }; } diff --git a/libyul/CMakeLists.txt b/libyul/CMakeLists.txt index ac766d918..416a02455 100644 --- a/libyul/CMakeLists.txt +++ b/libyul/CMakeLists.txt @@ -18,6 +18,7 @@ add_library(yul AssemblyStack.cpp CompilabilityChecker.cpp CompilabilityChecker.h + Dialect.cpp Dialect.h Exceptions.h Object.cpp diff --git a/libyul/CompilabilityChecker.cpp b/libyul/CompilabilityChecker.cpp index bb61c7d97..344266a12 100644 --- a/libyul/CompilabilityChecker.cpp +++ b/libyul/CompilabilityChecker.cpp @@ -39,11 +39,6 @@ map CompilabilityChecker::run( bool _optimizeStackAllocation ) { - if (_dialect.flavour == AsmFlavour::Yul) - return {}; - - yulAssert(_dialect.flavour == AsmFlavour::Strict, ""); - if (EVMDialect const* evmDialect = dynamic_cast(&_dialect)) { NoOutputEVMDialect noOutputDialect(*evmDialect); diff --git a/libyul/Dialect.cpp b/libyul/Dialect.cpp new file mode 100644 index 000000000..fd9f54d0a --- /dev/null +++ b/libyul/Dialect.cpp @@ -0,0 +1,53 @@ +/* + 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 . +*/ +/** + * Yul dialect. + */ + +#include + +using namespace solidity::yul; +using namespace std; + +Dialect const& Dialect::yul() +{ + static unique_ptr dialect; + static YulStringRepository::ResetCallback callback{[&] { dialect.reset(); }}; + + if (!dialect) + { + // TODO will probably change, especially the list of types. + dialect = make_unique(); + dialect->defaultType = "u256"_yulstring; + dialect->boolType = "bool"_yulstring; + dialect->types = { + "bool"_yulstring, + "u8"_yulstring, + "s8"_yulstring, + "u32"_yulstring, + "s32"_yulstring, + "u64"_yulstring, + "s64"_yulstring, + "u128"_yulstring, + "s128"_yulstring, + "u256"_yulstring, + "s256"_yulstring + }; + }; + + return *dialect; +} diff --git a/libyul/Dialect.h b/libyul/Dialect.h index c0a7d7581..b96e1851c 100644 --- a/libyul/Dialect.h +++ b/libyul/Dialect.h @@ -34,12 +34,6 @@ namespace solidity::yul class YulString; using Type = YulString; -enum class AsmFlavour -{ - 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; @@ -54,7 +48,11 @@ struct BuiltinFunction struct Dialect: boost::noncopyable { - AsmFlavour const flavour = AsmFlavour::Strict; + YulString defaultType; + /// Type used for the literals "true" and "false". + YulString boolType; + std::vector types; + /// @returns the builtin function of the given name or a nullptr if it is not a builtin function. virtual BuiltinFunction const* builtin(YulString /*_name*/) const { return nullptr; } @@ -64,14 +62,10 @@ struct Dialect: boost::noncopyable virtual std::set fixedFunctionNames() const { return {}; } - Dialect(AsmFlavour _flavour): flavour(_flavour) {} + Dialect() = default; virtual ~Dialect() = default; - static Dialect const& yul() - { - static Dialect yulDialect(AsmFlavour::Yul); - return yulDialect; - } + static Dialect const& yul(); }; } diff --git a/libyul/Object.cpp b/libyul/Object.cpp index 4006f6d81..2d4a5e33a 100644 --- a/libyul/Object.cpp +++ b/libyul/Object.cpp @@ -53,7 +53,7 @@ string Data::toString(bool) const string Object::toString(bool _yul) const { yulAssert(code, "No code"); - string inner = "code " + AsmPrinter{_yul}(*code); + string inner = "code " + AsmPrinter{}(*code); for (auto const& obj: subObjects) inner += "\n" + obj->toString(_yul); diff --git a/libyul/backends/evm/EVMDialect.cpp b/libyul/backends/evm/EVMDialect.cpp index 8a7a17b9a..6e2407ced 100644 --- a/libyul/backends/evm/EVMDialect.cpp +++ b/libyul/backends/evm/EVMDialect.cpp @@ -169,8 +169,7 @@ map createBuiltins(langutil::EVMVersion _evmVe } -EVMDialect::EVMDialect(AsmFlavour _flavour, bool _objectAccess, langutil::EVMVersion _evmVersion): - Dialect{_flavour}, +EVMDialect::EVMDialect(langutil::EVMVersion _evmVersion, bool _objectAccess): m_objectAccess(_objectAccess), m_evmVersion(_evmVersion), m_functions(createBuiltins(_evmVersion, _objectAccess)) @@ -191,7 +190,7 @@ EVMDialect const& EVMDialect::strictAssemblyForEVM(langutil::EVMVersion _version static map> dialects; static YulStringRepository::ResetCallback callback{[&] { dialects.clear(); }}; if (!dialects[_version]) - dialects[_version] = make_unique(AsmFlavour::Strict, false, _version); + dialects[_version] = make_unique(_version, false); return *dialects[_version]; } @@ -200,7 +199,7 @@ EVMDialect const& EVMDialect::strictAssemblyForEVMObjects(langutil::EVMVersion _ static map> dialects; static YulStringRepository::ResetCallback callback{[&] { dialects.clear(); }}; if (!dialects[_version]) - dialects[_version] = make_unique(AsmFlavour::Strict, true, _version); + dialects[_version] = make_unique(_version, true); return *dialects[_version]; } @@ -209,7 +208,7 @@ EVMDialect const& EVMDialect::yulForEVM(langutil::EVMVersion _version) static map> dialects; static YulStringRepository::ResetCallback callback{[&] { dialects.clear(); }}; if (!dialects[_version]) - dialects[_version] = make_unique(AsmFlavour::Yul, false, _version); + dialects[_version] = make_unique(_version, false); return *dialects[_version]; } diff --git a/libyul/backends/evm/EVMDialect.h b/libyul/backends/evm/EVMDialect.h index 58b08b486..0d7d8b6d9 100644 --- a/libyul/backends/evm/EVMDialect.h +++ b/libyul/backends/evm/EVMDialect.h @@ -63,7 +63,7 @@ struct BuiltinFunctionForEVM: BuiltinFunction struct EVMDialect: public Dialect { /// Constructor, should only be used internally. Use the factory functions below. - EVMDialect(AsmFlavour _flavour, bool _objectAccess, langutil::EVMVersion _evmVersion); + EVMDialect(langutil::EVMVersion _evmVersion, bool _objectAccess); /// @returns the builtin function of the given name or a nullptr if it is not a builtin function. BuiltinFunctionForEVM const* builtin(YulString _name) const override; diff --git a/libyul/backends/evm/NoOutputAssembly.cpp b/libyul/backends/evm/NoOutputAssembly.cpp index b3e4feba4..8d01c2c5e 100644 --- a/libyul/backends/evm/NoOutputAssembly.cpp +++ b/libyul/backends/evm/NoOutputAssembly.cpp @@ -143,7 +143,7 @@ AbstractAssembly::SubID NoOutputAssembly::appendData(bytes const&) } NoOutputEVMDialect::NoOutputEVMDialect(EVMDialect const& _copyFrom): - EVMDialect(_copyFrom.flavour, _copyFrom.providesObjectAccess(), _copyFrom.evmVersion()) + EVMDialect(_copyFrom.evmVersion(), _copyFrom.providesObjectAccess()) { for (auto& fun: m_functions) { diff --git a/libyul/backends/wasm/WasmDialect.cpp b/libyul/backends/wasm/WasmDialect.cpp index 78f9b045c..896059870 100644 --- a/libyul/backends/wasm/WasmDialect.cpp +++ b/libyul/backends/wasm/WasmDialect.cpp @@ -23,9 +23,12 @@ using namespace std; using namespace solidity::yul; -WasmDialect::WasmDialect(): - Dialect{AsmFlavour::Strict} +WasmDialect::WasmDialect() { + defaultType = "i64"_yulstring; + boolType = "i64"_yulstring; + types = {"i64"_yulstring, "i32"_yulstring}; + for (auto const& name: { "i64.add", "i64.sub", diff --git a/libyul/backends/wasm/WordSizeTransform.cpp b/libyul/backends/wasm/WordSizeTransform.cpp index 79df8be9a..47d91b7d1 100644 --- a/libyul/backends/wasm/WordSizeTransform.cpp +++ b/libyul/backends/wasm/WordSizeTransform.cpp @@ -97,12 +97,12 @@ void WordSizeTransform::operator()(Block& _block) for (int i = 0; i < 3; i++) ret.push_back(VariableDeclaration{ varDecl.location, - {TypedName{varDecl.location, newLhs[i], "u64"_yulstring}}, - make_unique(Literal{locationOf(*varDecl.value), LiteralKind::Number, "0"_yulstring, "u64"_yulstring}) + {TypedName{varDecl.location, newLhs[i], m_defaultType}}, + make_unique(Literal{locationOf(*varDecl.value), LiteralKind::Number, "0"_yulstring, m_defaultType}) }); ret.push_back(VariableDeclaration{ varDecl.location, - {TypedName{varDecl.location, newLhs[3], "u64"_yulstring}}, + {TypedName{varDecl.location, newLhs[3], m_defaultType}}, std::move(varDecl.value) }); return {std::move(ret)}; @@ -130,7 +130,7 @@ void WordSizeTransform::operator()(Block& _block) ret.push_back( VariableDeclaration{ varDecl.location, - {TypedName{varDecl.location, newLhs[i], "u64"_yulstring}}, + {TypedName{varDecl.location, newLhs[i], m_defaultType}}, std::move(newRhs[i]) } ); @@ -157,7 +157,7 @@ void WordSizeTransform::operator()(Block& _block) ret.push_back(Assignment{ assignment.location, {Identifier{assignment.location, newLhs[i]}}, - make_unique(Literal{locationOf(*assignment.value), LiteralKind::Number, "0"_yulstring, "u64"_yulstring}) + make_unique(Literal{locationOf(*assignment.value), LiteralKind::Number, "0"_yulstring, m_defaultType}) }); ret.push_back(Assignment{ assignment.location, @@ -208,7 +208,8 @@ void WordSizeTransform::run(Dialect const& _inputDialect, Block& _ast, NameDispe { // Free the name `or_bool`. NameDisplacer{_nameDispenser, {"or_bool"_yulstring}}(_ast); - WordSizeTransform{_inputDialect, _nameDispenser}(_ast); + YulString defaultType; // should be i64 at some point. + WordSizeTransform{_inputDialect, _nameDispenser, defaultType}(_ast); } void WordSizeTransform::rewriteVarDeclList(TypedNameList& _nameList) @@ -219,7 +220,7 @@ void WordSizeTransform::rewriteVarDeclList(TypedNameList& _nameList) { TypedNameList ret; for (auto newName: generateU64IdentifierNames(_n.name)) - ret.emplace_back(TypedName{_n.location, newName, "u64"_yulstring}); + ret.emplace_back(TypedName{_n.location, newName, m_defaultType}); return ret; } ); @@ -283,7 +284,7 @@ vector WordSizeTransform::handleSwitchInternal( for (auto& c: cases) { - Literal label{_location, LiteralKind::Number, YulString(c.first.str()), "u64"_yulstring}; + Literal label{_location, LiteralKind::Number, YulString(c.first.str()), m_defaultType}; ret.cases.emplace_back(Case{ c.second.front().location, make_unique(std::move(label)), @@ -304,7 +305,7 @@ vector WordSizeTransform::handleSwitchInternal( Assignment{ _location, {{_location, _runDefaultFlag}}, - make_unique(Literal{_location, LiteralKind::Number, "1"_yulstring, "u64"_yulstring}) + make_unique(Literal{_location, LiteralKind::Number, "1"_yulstring, m_defaultType}) } )} }); @@ -329,7 +330,7 @@ std::vector WordSizeTransform::handleSwitch(Switch& _switch) _switch.cases.pop_back(); ret.emplace_back(VariableDeclaration{ _switch.location, - {TypedName{_switch.location, runDefaultFlag, "u64"_yulstring}}, + {TypedName{_switch.location, runDefaultFlag, m_defaultType}}, {} }); } @@ -384,7 +385,7 @@ array, 4> WordSizeTransform::expandValue(Expression const lit.location, LiteralKind::Number, YulString(currentVal.str()), - "u64"_yulstring + m_defaultType } ); } diff --git a/libyul/backends/wasm/WordSizeTransform.h b/libyul/backends/wasm/WordSizeTransform.h index 199b01b49..75afa279e 100644 --- a/libyul/backends/wasm/WordSizeTransform.h +++ b/libyul/backends/wasm/WordSizeTransform.h @@ -70,9 +70,14 @@ public: static void run(Dialect const& _inputDialect, Block& _ast, NameDispenser& _nameDispenser); private: - explicit WordSizeTransform(Dialect const& _inputDialect, NameDispenser& _nameDispenser): + explicit WordSizeTransform( + Dialect const& _inputDialect, + NameDispenser& _nameDispenser, + YulString _defaultType + ): m_inputDialect(_inputDialect), - m_nameDispenser(_nameDispenser) + m_nameDispenser(_nameDispenser), + m_defaultType(_defaultType) { } void rewriteVarDeclList(std::vector&); @@ -94,6 +99,7 @@ private: Dialect const& m_inputDialect; NameDispenser& m_nameDispenser; + YulString m_defaultType; /// maps original u256 variable's name to corresponding u64 variables' names std::map> m_variableMapping; }; diff --git a/test/cmdlineTests/evm_to_wasm/output b/test/cmdlineTests/evm_to_wasm/output index a85239b23..1c31f9488 100644 --- a/test/cmdlineTests/evm_to_wasm/output +++ b/test/cmdlineTests/evm_to_wasm/output @@ -15,9 +15,9 @@ object "object" { function main() { let _1 := 0 - mstore_internal(0, _1, _1, _1, _1) + mstore_internal(_1, _1, _1, _1, _1) mstore_internal(32, _1, _1, _1, 1) - eth.storageStore(0, 32) + eth.storageStore(_1, 32) } function endian_swap_16(x) -> y { @@ -45,7 +45,7 @@ object "object" { Binary representation: -0061736d0100000001160460000060017e017e60057e7e7e7e7e0060027f7f0002190108657468657265756d0c73746f7261676553746f7265000303060500010101020503010001060100071102066d656d6f72790200046d61696e00010ab501052801017e420021004200200020002000200010054220200020002000420110054200a74220a710000b1c01017e20004208864280fe0383200042088842ff018384210120010b1b01027e20001002421086210220022000421088100284210120010b1b01027e20001003422086210220022000422088100384210120010b3501007e2000a720011004370300200042087ca720021004370300200042107ca720031004370300200042187ca7200410043703000b +0061736d0100000001160460000060017e017e60057e7e7e7e7e0060027f7f0002190108657468657265756d0c73746f7261676553746f7265000303060500010101020503010001060100071102066d656d6f72790200046d61696e00010ab501052801017e420021002000200020002000200010054220200020002000420110052000a74220a710000b1c01017e20004208864280fe0383200042088842ff018384210120010b1b01027e20001002421086210220022000421088100284210120010b1b01027e20001003422086210220022000422088100384210120010b3501007e2000a720011004370300200042087ca720021004370300200042107ca720031004370300200042187ca7200410043703000b Text representation: (module @@ -56,9 +56,9 @@ Text representation: (func $main (local $_1 i64) (local.set $_1 (i64.const 0)) - (call $mstore_internal (i64.const 0) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1)) + (call $mstore_internal (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1)) (call $mstore_internal (i64.const 32) (local.get $_1) (local.get $_1) (local.get $_1) (i64.const 1)) - (call $eth.storageStore (i32.wrap_i64 (i64.const 0)) (i32.wrap_i64 (i64.const 32))) + (call $eth.storageStore (i32.wrap_i64 (local.get $_1)) (i32.wrap_i64 (i64.const 32))) ) (func $endian_swap_16 diff --git a/test/cmdlineTests/standard_eWasm_requested/output.json b/test/cmdlineTests/standard_eWasm_requested/output.json index a0c6849f1..11e7bb252 100644 --- a/test/cmdlineTests/standard_eWasm_requested/output.json +++ b/test/cmdlineTests/standard_eWasm_requested/output.json @@ -10,23 +10,21 @@ (local $p i64) (local $r i64) (local $hi i64) - (local $hi_1 i64) (local $y i64) - (local $hi_2 i64) + (local $hi_1 i64) (local $_2 i64) (local.set $_1 (i64.const 0)) (local.set $p (call $u256_to_i32 (local.get $_1) (local.get $_1) (local.get $_1) (i64.const 64))) (local.set $r (i64.add (local.get $p) (i64.const 64))) (if (i64.ne (i64.extend_i32_u (i64.lt_u (local.get $r) (local.get $p))) (i64.const 0)) (then (unreachable))) - (local.set $hi (i64.shl (call $endian_swap_16 (local.get $_1)) (i64.const 16))) - (local.set $hi_1 (i64.shl (i64.or (local.get $hi) (call $endian_swap_16 (i64.shr_u (local.get $_1) (i64.const 16)))) (i64.const 32))) - (local.set $y (i64.or (local.get $hi_1) (call $endian_swap_32 (i64.shr_u (local.get $_1) (i64.const 32))))) + (local.set $hi (i64.shl (i64.or (i64.shl (i64.or (i64.and (i64.shl (local.get $_1) (i64.const 8)) (i64.const 65280)) (i64.and (i64.shr_u (local.get $_1) (i64.const 8)) (i64.const 255))) (i64.const 16)) (call $endian_swap_16 (i64.shr_u (local.get $_1) (i64.const 16)))) (i64.const 32))) + (local.set $y (i64.or (local.get $hi) (call $endian_swap_32 (i64.shr_u (local.get $_1) (i64.const 32))))) (i64.store (i32.wrap_i64 (local.get $r)) (local.get $y)) (i64.store (i32.wrap_i64 (i64.add (local.get $r) (i64.const 8))) (local.get $y)) (i64.store (i32.wrap_i64 (i64.add (local.get $r) (i64.const 16))) (local.get $y)) - (local.set $hi_2 (i64.shl (call $endian_swap_32 (i64.const 128)) (i64.const 32))) - (i64.store (i32.wrap_i64 (i64.add (local.get $r) (i64.const 24))) (i64.or (local.get $hi_2) (call $endian_swap_32 (i64.shr_u (i64.const 128) (i64.const 32))))) + (local.set $hi_1 (i64.shl (call $endian_swap_32 (i64.const 128)) (i64.const 32))) + (i64.store (i32.wrap_i64 (i64.add (local.get $r) (i64.const 24))) (i64.or (local.get $hi_1) (call $endian_swap_32 (i64.shr_u (i64.const 128) (i64.const 32))))) (local.set $_2 (datasize \"C_2_deployed\")) (call $eth.codeCopy (i32.wrap_i64 (call $to_internal_i32ptr (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1))) (i32.wrap_i64 (call $u256_to_i32 (local.get $_1) (local.get $_1) (local.get $_1) (dataoffset \"C_2_deployed\"))) (i32.wrap_i64 (call $u256_to_i32 (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_2)))) (call $eth.finish (i32.wrap_i64 (call $to_internal_i32ptr (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_1))) (i32.wrap_i64 (call $u256_to_i32 (local.get $_1) (local.get $_1) (local.get $_1) (local.get $_2)))) diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp index c4fa1ab8c..8d55f9a08 100644 --- a/test/libsolidity/InlineAssembly.cpp +++ b/test/libsolidity/InlineAssembly.cpp @@ -224,8 +224,8 @@ BOOST_AUTO_TEST_CASE(vardecl_multi_conflict) BOOST_AUTO_TEST_CASE(vardecl_bool) { - CHECK_PARSE_ERROR("{ let x := true }", ParserError, "True and false are not valid literals."); - CHECK_PARSE_ERROR("{ let x := false }", ParserError, "True and false are not valid literals."); + successParse("{ let x := true }"); + successParse("{ let x := false }"); } BOOST_AUTO_TEST_CASE(vardecl_empty) @@ -313,7 +313,7 @@ BOOST_AUTO_TEST_CASE(switch_duplicate_case) BOOST_AUTO_TEST_CASE(switch_invalid_expression) { - CHECK_PARSE_ERROR("{ switch {} default {} }", ParserError, "Literal, identifier or instruction expected."); + CHECK_PARSE_ERROR("{ switch {} default {} }", ParserError, "Literal or identifier expected."); CHECK_PARSE_ERROR("{ switch mload default {} }", ParserError, "Expected '(' but got reserved keyword 'default'"); CHECK_PARSE_ERROR("{ switch mstore(1, 1) default {} }", TypeError, "Expected expression to return one item to the stack, but did return 0 items"); } @@ -346,7 +346,7 @@ BOOST_AUTO_TEST_CASE(for_statement) BOOST_AUTO_TEST_CASE(for_invalid_expression) { - CHECK_PARSE_ERROR("{ for {} {} {} {} }", ParserError, "Literal, identifier or instruction expected."); + CHECK_PARSE_ERROR("{ for {} {} {} {} }", ParserError, "Literal or identifier expected."); CHECK_PARSE_ERROR("{ for 1 1 {} {} }", ParserError, "Expected '{' but got 'Number'"); CHECK_PARSE_ERROR("{ for {} 1 1 {} }", ParserError, "Expected '{' but got 'Number'"); CHECK_PARSE_ERROR("{ for {} 1 {} 1 }", ParserError, "Expected '{' but got 'Number'"); diff --git a/test/libsolidity/syntaxTests/inlineAssembly/invalid/empty_fun_arg_beginning.sol b/test/libsolidity/syntaxTests/inlineAssembly/invalid/empty_fun_arg_beginning.sol index 9d99c8b4d..114c76afa 100644 --- a/test/libsolidity/syntaxTests/inlineAssembly/invalid/empty_fun_arg_beginning.sol +++ b/test/libsolidity/syntaxTests/inlineAssembly/invalid/empty_fun_arg_beginning.sol @@ -8,4 +8,4 @@ contract C { } } // ---- -// ParserError: (101-102): Literal, identifier or instruction expected. +// ParserError: (101-102): Literal or identifier expected. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/invalid/empty_fun_arg_end.sol b/test/libsolidity/syntaxTests/inlineAssembly/invalid/empty_fun_arg_end.sol index 0d041492d..a501405b4 100644 --- a/test/libsolidity/syntaxTests/inlineAssembly/invalid/empty_fun_arg_end.sol +++ b/test/libsolidity/syntaxTests/inlineAssembly/invalid/empty_fun_arg_end.sol @@ -8,4 +8,4 @@ contract C { } } // ---- -// ParserError: (103-104): Literal, identifier or instruction expected. +// ParserError: (103-104): Literal or identifier expected. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/invalid/empty_fun_arg_middle.sol b/test/libsolidity/syntaxTests/inlineAssembly/invalid/empty_fun_arg_middle.sol index 09e0ba038..ef9202ac4 100644 --- a/test/libsolidity/syntaxTests/inlineAssembly/invalid/empty_fun_arg_middle.sol +++ b/test/libsolidity/syntaxTests/inlineAssembly/invalid/empty_fun_arg_middle.sol @@ -7,4 +7,4 @@ contract C { } } // ---- -// ParserError: (96-97): Literal, identifier or instruction expected. +// ParserError: (96-97): Literal or identifier expected. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/invalid/invalid_number.sol b/test/libsolidity/syntaxTests/inlineAssembly/invalid/invalid_number.sol index 739c45bfe..a446097e1 100644 --- a/test/libsolidity/syntaxTests/inlineAssembly/invalid/invalid_number.sol +++ b/test/libsolidity/syntaxTests/inlineAssembly/invalid/invalid_number.sol @@ -6,4 +6,4 @@ contract C { } } // ---- -// ParserError: (72-73): Literal, identifier or instruction expected. +// ParserError: (72-73): Literal or identifier expected. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/invalid/missing_variable_in_assign.sol b/test/libsolidity/syntaxTests/inlineAssembly/invalid/missing_variable_in_assign.sol index ec1cc2baf..d8361268d 100644 --- a/test/libsolidity/syntaxTests/inlineAssembly/invalid/missing_variable_in_assign.sol +++ b/test/libsolidity/syntaxTests/inlineAssembly/invalid/missing_variable_in_assign.sol @@ -7,4 +7,4 @@ contract C { } } // ---- -// ParserError: (87-89): Literal, identifier or instruction expected. +// ParserError: (87-89): Literal or identifier expected. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/invalid/whitespace_in_assignment.sol b/test/libsolidity/syntaxTests/inlineAssembly/invalid/whitespace_in_assignment.sol index 940863276..582733bef 100644 --- a/test/libsolidity/syntaxTests/inlineAssembly/invalid/whitespace_in_assignment.sol +++ b/test/libsolidity/syntaxTests/inlineAssembly/invalid/whitespace_in_assignment.sol @@ -6,4 +6,4 @@ contract C { } } // ---- -// ParserError: (69-70): Literal, identifier or instruction expected. +// ParserError: (71-72): Expected identifier but got '=' diff --git a/test/libsolidity/syntaxTests/inlineAssembly/invalid/whitespace_in_multiple_assignment.sol b/test/libsolidity/syntaxTests/inlineAssembly/invalid/whitespace_in_multiple_assignment.sol index 3601c3117..478e14e8f 100644 --- a/test/libsolidity/syntaxTests/inlineAssembly/invalid/whitespace_in_multiple_assignment.sol +++ b/test/libsolidity/syntaxTests/inlineAssembly/invalid/whitespace_in_multiple_assignment.sol @@ -7,4 +7,4 @@ contract C { } } // ---- -// ParserError: (107-108): Literal, identifier or instruction expected. +// ParserError: (109-110): Expected identifier but got '=' diff --git a/test/libyul/Common.cpp b/test/libyul/Common.cpp index e50239807..c80b09fa7 100644 --- a/test/libyul/Common.cpp +++ b/test/libyul/Common.cpp @@ -75,6 +75,27 @@ pair, shared_ptr> yul::test::parse(strin return make_pair(stack.parserResult()->code, stack.parserResult()->analysisInfo); } +pair, shared_ptr> yul::test::parse( + string const& _source, + Dialect const& _dialect, + ErrorList& _errors +) +{ + ErrorReporter errorReporter(_errors); + shared_ptr scanner = make_shared(CharStream(_source, "")); + shared_ptr parserResult = yul::ObjectParser(errorReporter, _dialect).parse(scanner, false); + if (!parserResult) + return {}; + if (!parserResult->code || !errorReporter.errors().empty()) + return {}; + shared_ptr analysisInfo = make_shared(); + AsmAnalyzer analyzer(*analysisInfo, errorReporter, _dialect, {}, parserResult->dataNames()); + // TODO this should be done recursively. + if (!analyzer.analyze(*parserResult->code) || !errorReporter.errors().empty()) + return {}; + return {std::move(parserResult->code), std::move(analysisInfo)}; +} + yul::Block yul::test::disambiguate(string const& _source, bool _yul) { auto result = parse(_source, _yul); @@ -83,5 +104,5 @@ yul::Block yul::test::disambiguate(string const& _source, bool _yul) string yul::test::format(string const& _source, bool _yul) { - return yul::AsmPrinter(_yul)(*parse(_source, _yul).first); + return yul::AsmPrinter()(*parse(_source, _yul).first); } diff --git a/test/libyul/Common.h b/test/libyul/Common.h index 96de22aa9..0b154e69c 100644 --- a/test/libyul/Common.h +++ b/test/libyul/Common.h @@ -36,14 +36,20 @@ using ErrorList = std::vector>; namespace solidity::yul { struct AsmAnalysisInfo; +struct Dialect; } namespace solidity::yul::test { void printErrors(langutil::ErrorList const& _errors); + std::pair, std::shared_ptr> parse(std::string const& _source, bool _yul = true); + +std::pair, std::shared_ptr> +parse(std::string const& _source, Dialect const& _dialect, langutil::ErrorList& _errors); + Block disambiguate(std::string const& _source, bool _yul = true); std::string format(std::string const& _source, bool _yul = true); diff --git a/test/libyul/Parser.cpp b/test/libyul/Parser.cpp index 6f25304d8..749ec5ea5 100644 --- a/test/libyul/Parser.cpp +++ b/test/libyul/Parser.cpp @@ -161,9 +161,9 @@ BOOST_AUTO_TEST_CASE(period_not_as_identifier_start) BOOST_AUTO_TEST_CASE(period_in_identifier_spaced) { - CHECK_ERROR("{ let x. y:u256 }", ParserError, "Expected ':' but got identifier"); - CHECK_ERROR("{ let x .y:u256 }", ParserError, "Expected ':' but got '.'"); - CHECK_ERROR("{ let x . y:u256 }", ParserError, "Expected ':' but got '.'"); + CHECK_ERROR("{ let x. y:u256 }", ParserError, "Call or assignment expected"); + CHECK_ERROR("{ let x .y:u256 }", ParserError, "Literal or identifier expected"); + CHECK_ERROR("{ let x . y:u256 }", ParserError, "Literal or identifier expected"); } BOOST_AUTO_TEST_CASE(period_in_identifier_start) @@ -234,12 +234,12 @@ BOOST_AUTO_TEST_CASE(tokens_as_identifers) BOOST_CHECK(successParse("{ let bool:u256 := 1:u256 }")); } -BOOST_AUTO_TEST_CASE(lacking_types) +BOOST_AUTO_TEST_CASE(optional_types) { - CHECK_ERROR("{ let x := 1:u256 }", ParserError, "Expected ':' but got ':='"); - CHECK_ERROR("{ let x:u256 := 1 }", ParserError, "Expected ':' but got '}'"); - CHECK_ERROR("{ function f(a) {} }", ParserError, "Expected ':' but got ')'"); - CHECK_ERROR("{ function f(a:u256) -> b {} }", ParserError, "Expected ':' but got '{'"); + BOOST_CHECK(successParse("{ let x := 1:u256 }")); + BOOST_CHECK(successParse("{ let x:u256 := 1 }")); + BOOST_CHECK(successParse("{ function f(a) {} }")); + BOOST_CHECK(successParse("{ function f(a:u256) -> b {} }")); } BOOST_AUTO_TEST_CASE(invalid_types) @@ -531,7 +531,6 @@ BOOST_AUTO_TEST_CASE(builtins_parser) { struct SimpleDialect: public Dialect { - SimpleDialect(): Dialect(AsmFlavour::Strict) {} BuiltinFunction const* builtin(YulString _name) const override { return _name == "builtin"_yulstring ? &f : nullptr; @@ -551,7 +550,6 @@ BOOST_AUTO_TEST_CASE(builtins_analysis) { struct SimpleDialect: public Dialect { - SimpleDialect(): Dialect(AsmFlavour::Strict) {} BuiltinFunction const* builtin(YulString _name) const override { return _name == "builtin"_yulstring ? &f : nullptr; diff --git a/test/libyul/YulOptimizerTest.cpp b/test/libyul/YulOptimizerTest.cpp index 053b7772e..791551bc6 100644 --- a/test/libyul/YulOptimizerTest.cpp +++ b/test/libyul/YulOptimizerTest.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include @@ -364,7 +365,7 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line return TestResult::FatalError; } - m_obtainedResult = AsmPrinter{m_dialect->flavour == AsmFlavour::Yul}(*m_ast) + "\n"; + m_obtainedResult = AsmPrinter{}(*m_ast) + "\n"; if (m_optimizerStep != m_validatedSettings["step"]) { @@ -418,19 +419,15 @@ void YulOptimizerTest::printIndented(ostream& _stream, string const& _output, st bool YulOptimizerTest::parse(ostream& _stream, string const& _linePrefix, bool const _formatted) { - AssemblyStack stack( - solidity::test::Options::get().evmVersion(), - m_dialect->flavour == AsmFlavour::Yul ? AssemblyStack::Language::Yul : AssemblyStack::Language::StrictAssembly, - solidity::frontend::OptimiserSettings::none() - ); - if (!stack.parseAndAnalyze("", m_source) || !stack.errors().empty()) + ErrorList errors; + soltestAssert(m_dialect, ""); + std::tie(m_ast, m_analysisInfo) = yul::test::parse(m_source, *m_dialect, errors); + if (!m_ast || !m_analysisInfo || !errors.empty()) { AnsiColorized(_stream, _formatted, {formatting::BOLD, formatting::RED}) << _linePrefix << "Error parsing source." << endl; - printErrors(_stream, stack.errors()); + printErrors(_stream, errors); return false; } - m_ast = stack.parserResult()->code; - m_analysisInfo = stack.parserResult()->analysisInfo; return true; } From 0f014144c8fd7114768e7bc853664a0173f57ee4 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 20 Dec 2019 09:24:20 +0100 Subject: [PATCH 2/4] Test for true/false in inline assembly. --- .../semanticTests/inlineAssembly/truefalse.sol | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 test/libsolidity/semanticTests/inlineAssembly/truefalse.sol diff --git a/test/libsolidity/semanticTests/inlineAssembly/truefalse.sol b/test/libsolidity/semanticTests/inlineAssembly/truefalse.sol new file mode 100644 index 000000000..c10cdd65a --- /dev/null +++ b/test/libsolidity/semanticTests/inlineAssembly/truefalse.sol @@ -0,0 +1,12 @@ +contract C { + function f() public returns (uint x, uint y) { + assembly { + x := true + y := false + } + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> 1, 0 From 180344ab17243276e03a45bbb58e0cfbaa159442 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 20 Dec 2019 12:06:40 +0100 Subject: [PATCH 3/4] Changelog entry. --- Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.md b/Changelog.md index 08c790155..c59cf6417 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,6 +2,7 @@ Language Features: * Allow accessing external functions via contract and interface names to obtain their selector. + * Inline Assembly: Support literals ``true`` and ``false``. Compiler Features: From fe35df4b7823c5e94a80a05b684f8e790ee189ec Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 14 Jan 2020 17:17:20 +0100 Subject: [PATCH 4/4] Style. --- libyul/AsmAnalysis.cpp | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/libyul/AsmAnalysis.cpp b/libyul/AsmAnalysis.cpp index d47ce115c..5a88671ce 100644 --- a/libyul/AsmAnalysis.cpp +++ b/libyul/AsmAnalysis.cpp @@ -378,26 +378,25 @@ bool AsmAnalyzer::operator()(Switch const& _switch) if (!expectExpression(*_switch.expression)) success = false; - { - YulString caseType; - bool mismatchingTypes = false; - for (auto const& _case: _switch.cases) - if (_case.value) + YulString caseType; + bool mismatchingTypes = false; + for (auto const& _case: _switch.cases) + if (_case.value) + { + if (caseType.empty()) + caseType = _case.value->type; + else if (caseType != _case.value->type) { - if (caseType.empty()) - caseType = _case.value->type; - else if (caseType != _case.value->type) - { - mismatchingTypes = true; - break; - } + mismatchingTypes = true; + break; } - if (mismatchingTypes) - m_errorReporter.typeError( - _switch.location, - "Switch cases have non-matching types." - ); - } + } + + if (mismatchingTypes) + m_errorReporter.typeError( + _switch.location, + "Switch cases have non-matching types." + ); set cases; for (auto const& _case: _switch.cases)