diff options
author | chriseth <chris@ethereum.org> | 2018-12-03 22:48:03 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-12-03 22:48:03 +0800 |
commit | c8a2cb62832afb2dc09ccee6fd42c1516dfdb981 (patch) | |
tree | 7977e9dcbbc215088c05b847f849871ef5d4ae66 /libyul | |
parent | 1d4f565a64988a3400847d2655ca24f73f234bc6 (diff) | |
parent | 590be1d84cea9850ce69b68be3dc5294b39041e5 (diff) | |
download | dexon-solidity-c8a2cb62832afb2dc09ccee6fd42c1516dfdb981.tar dexon-solidity-c8a2cb62832afb2dc09ccee6fd42c1516dfdb981.tar.gz dexon-solidity-c8a2cb62832afb2dc09ccee6fd42c1516dfdb981.tar.bz2 dexon-solidity-c8a2cb62832afb2dc09ccee6fd42c1516dfdb981.tar.lz dexon-solidity-c8a2cb62832afb2dc09ccee6fd42c1516dfdb981.tar.xz dexon-solidity-c8a2cb62832afb2dc09ccee6fd42c1516dfdb981.tar.zst dexon-solidity-c8a2cb62832afb2dc09ccee6fd42c1516dfdb981.zip |
Merge pull request #5571 from ethereum/develop
Version 0.5.1
Diffstat (limited to 'libyul')
91 files changed, 3389 insertions, 427 deletions
diff --git a/libyul/ASTDataForward.h b/libyul/ASTDataForward.h deleted file mode 100644 index 8c49e68f..00000000 --- a/libyul/ASTDataForward.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - 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/>. -*/ -/** - * @date 2017 - * Pull in some identifiers from the solidity::assembly namespace. - */ - -#pragma once - -#include <libsolidity/inlineasm/AsmDataForward.h> - -namespace dev -{ -namespace yul -{ - -using Instruction = solidity::assembly::Instruction; -using Literal = solidity::assembly::Literal; -using Label = solidity::assembly::Label; -using StackAssignment = solidity::assembly::StackAssignment; -using Identifier = solidity::assembly::Identifier; -using Assignment = solidity::assembly::Assignment; -using VariableDeclaration = solidity::assembly::VariableDeclaration; -using FunctionalInstruction = solidity::assembly::FunctionalInstruction; -using FunctionDefinition = solidity::assembly::FunctionDefinition; -using FunctionCall = solidity::assembly::FunctionCall; -using If = solidity::assembly::If; -using Case = solidity::assembly::Case; -using Switch = solidity::assembly::Switch; -using ForLoop = solidity::assembly::ForLoop; -using ExpressionStatement = solidity::assembly::ExpressionStatement; -using Block = solidity::assembly::Block; - -using TypedName = solidity::assembly::TypedName; -class YulString; - -using Expression = boost::variant<FunctionalInstruction, FunctionCall, Identifier, Literal>; -using Statement = boost::variant<ExpressionStatement, Instruction, Label, StackAssignment, Assignment, VariableDeclaration, FunctionDefinition, If, Switch, ForLoop, Block>; - -} -} diff --git a/libyul/AsmAnalysis.cpp b/libyul/AsmAnalysis.cpp new file mode 100644 index 00000000..d3f6de84 --- /dev/null +++ b/libyul/AsmAnalysis.cpp @@ -0,0 +1,632 @@ +/* + 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/>. +*/ +/** + * Analyzer part of inline assembly. + */ + +#include <libyul/AsmAnalysis.h> + +#include <libyul/AsmData.h> +#include <libyul/AsmScopeFiller.h> +#include <libyul/AsmScope.h> +#include <libyul/AsmAnalysisInfo.h> + +#include <liblangutil/ErrorReporter.h> + +#include <boost/range/adaptor/reversed.hpp> +#include <boost/algorithm/string.hpp> + +#include <memory> +#include <functional> + +using namespace std; +using namespace dev; +using namespace langutil; +using namespace yul; +using namespace dev; +using namespace dev::solidity; + +namespace { + +set<string> const builtinTypes{"bool", "u8", "s8", "u32", "s32", "u64", "s64", "u128", "s128", "u256", "s256"}; + +} + +bool AsmAnalyzer::analyze(Block const& _block) +{ + if (!(ScopeFiller(m_info, m_errorReporter))(_block)) + return false; + + return (*this)(_block); +} + +bool AsmAnalyzer::operator()(Label const& _label) +{ + solAssert(!_label.name.empty(), ""); + checkLooseFeature( + _label.location, + "The use of labels is disallowed. Please use \"if\", \"switch\", \"for\" or function calls instead." + ); + m_info.stackHeightInfo[&_label] = m_stackHeight; + warnOnInstructions(solidity::Instruction::JUMPDEST, _label.location); + return true; +} + +bool AsmAnalyzer::operator()(yul::Instruction const& _instruction) +{ + checkLooseFeature( + _instruction.location, + "The use of non-functional instructions is disallowed. Please use functional notation instead." + ); + auto const& info = instructionInfo(_instruction.instruction); + m_stackHeight += info.ret - info.args; + m_info.stackHeightInfo[&_instruction] = m_stackHeight; + warnOnInstructions(_instruction.instruction, _instruction.location); + return true; +} + +bool AsmAnalyzer::operator()(Literal const& _literal) +{ + expectValidType(_literal.type.str(), _literal.location); + ++m_stackHeight; + if (_literal.kind == LiteralKind::String && _literal.value.str().size() > 32) + { + m_errorReporter.typeError( + _literal.location, + "String literal too long (" + to_string(_literal.value.str().size()) + " > 32)" + ); + return false; + } + else if (_literal.kind == LiteralKind::Number && bigint(_literal.value.str()) > u256(-1)) + { + m_errorReporter.typeError( + _literal.location, + "Number literal too large (> 256 bits)" + ); + return false; + } + else if (_literal.kind == LiteralKind::Boolean) + { + solAssert(m_flavour == AsmFlavour::Yul, ""); + solAssert(_literal.value == YulString{string("true")} || _literal.value == YulString{string("false")}, ""); + } + m_info.stackHeightInfo[&_literal] = m_stackHeight; + return true; +} + +bool AsmAnalyzer::operator()(Identifier const& _identifier) +{ + solAssert(!_identifier.name.empty(), ""); + size_t numErrorsBefore = m_errorReporter.errors().size(); + bool success = true; + if (m_currentScope->lookup(_identifier.name, Scope::Visitor( + [&](Scope::Variable const& _var) + { + if (!m_activeVariables.count(&_var)) + { + m_errorReporter.declarationError( + _identifier.location, + "Variable " + _identifier.name.str() + " used before it was declared." + ); + success = false; + } + ++m_stackHeight; + }, + [&](Scope::Label const&) + { + ++m_stackHeight; + }, + [&](Scope::Function const&) + { + m_errorReporter.typeError( + _identifier.location, + "Function " + _identifier.name.str() + " used without being called." + ); + success = false; + } + ))) + { + } + else + { + size_t stackSize(-1); + if (m_resolver) + { + bool insideFunction = m_currentScope->insideFunction(); + stackSize = m_resolver(_identifier, yul::IdentifierContext::RValue, insideFunction); + } + if (stackSize == size_t(-1)) + { + // Only add an error message if the callback did not do it. + if (numErrorsBefore == m_errorReporter.errors().size()) + m_errorReporter.declarationError(_identifier.location, "Identifier not found."); + success = false; + } + m_stackHeight += stackSize == size_t(-1) ? 1 : stackSize; + } + m_info.stackHeightInfo[&_identifier] = m_stackHeight; + return success; +} + +bool AsmAnalyzer::operator()(FunctionalInstruction const& _instr) +{ + solAssert(m_flavour != AsmFlavour::Yul, ""); + bool success = true; + for (auto const& arg: _instr.arguments | boost::adaptors::reversed) + if (!expectExpression(arg)) + success = false; + // Parser already checks that the number of arguments is correct. + auto const& info = instructionInfo(_instr.instruction); + solAssert(info.args == int(_instr.arguments.size()), ""); + m_stackHeight += info.ret - info.args; + m_info.stackHeightInfo[&_instr] = m_stackHeight; + warnOnInstructions(_instr.instruction, _instr.location); + return success; +} + +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)) + { + Error::Type errorType = m_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) + + " value" + + (m_stackHeight - initialStackHeight == 1 ? "" : "s") + + "). Use ``pop()`` or assign them."; + m_errorReporter.error(errorType, _statement.location, msg); + if (errorType != Error::Type::Warning) + success = false; + } + m_info.stackHeightInfo[&_statement] = m_stackHeight; + return success; +} + +bool AsmAnalyzer::operator()(StackAssignment const& _assignment) +{ + checkLooseFeature( + _assignment.location, + "The use of stack assignment is disallowed. Please use assignment in functional notation instead." + ); + bool success = checkAssignment(_assignment.variableName, size_t(-1)); + m_info.stackHeightInfo[&_assignment] = m_stackHeight; + return success; +} + +bool AsmAnalyzer::operator()(Assignment const& _assignment) +{ + solAssert(_assignment.value, ""); + int const expectedItems = _assignment.variableNames.size(); + solAssert(expectedItems >= 1, ""); + int const stackHeight = m_stackHeight; + bool success = boost::apply_visitor(*this, *_assignment.value); + if ((m_stackHeight - stackHeight) != expectedItems) + { + m_errorReporter.declarationError( + _assignment.location, + "Variable count does not match number of values (" + + to_string(expectedItems) + + " vs. " + + to_string(m_stackHeight - stackHeight) + + ")" + ); + return false; + } + for (auto const& variableName: _assignment.variableNames) + if (!checkAssignment(variableName, 1)) + success = false; + m_info.stackHeightInfo[&_assignment] = m_stackHeight; + return success; +} + +bool AsmAnalyzer::operator()(VariableDeclaration const& _varDecl) +{ + bool success = true; + int const numVariables = _varDecl.variables.size(); + if (_varDecl.value) + { + int const stackHeight = m_stackHeight; + success = boost::apply_visitor(*this, *_varDecl.value); + if ((m_stackHeight - stackHeight) != numVariables) + { + m_errorReporter.declarationError(_varDecl.location, "Variable count mismatch."); + return false; + } + } + else + m_stackHeight += numVariables; + + for (auto const& variable: _varDecl.variables) + { + expectValidType(variable.type.str(), variable.location); + m_activeVariables.insert(&boost::get<Scope::Variable>(m_currentScope->identifiers.at(variable.name))); + } + m_info.stackHeightInfo[&_varDecl] = m_stackHeight; + return success; +} + +bool AsmAnalyzer::operator()(FunctionDefinition const& _funDef) +{ + solAssert(!_funDef.name.empty(), ""); + Block const* virtualBlock = m_info.virtualBlocks.at(&_funDef).get(); + solAssert(virtualBlock, ""); + Scope& varScope = scope(virtualBlock); + for (auto const& var: _funDef.parameters + _funDef.returnVariables) + { + expectValidType(var.type.str(), var.location); + m_activeVariables.insert(&boost::get<Scope::Variable>(varScope.identifiers.at(var.name))); + } + + int const stackHeight = m_stackHeight; + m_stackHeight = _funDef.parameters.size() + _funDef.returnVariables.size(); + + bool success = (*this)(_funDef.body); + + m_stackHeight = stackHeight; + m_info.stackHeightInfo[&_funDef] = m_stackHeight; + return success; +} + +bool AsmAnalyzer::operator()(FunctionCall const& _funCall) +{ + solAssert(!_funCall.functionName.name.empty(), ""); + bool success = true; + size_t arguments = 0; + size_t returns = 0; + if (!m_currentScope->lookup(_funCall.functionName.name, Scope::Visitor( + [&](Scope::Variable const&) + { + m_errorReporter.typeError( + _funCall.functionName.location, + "Attempt to call variable instead of function." + ); + success = false; + }, + [&](Scope::Label const&) + { + m_errorReporter.typeError( + _funCall.functionName.location, + "Attempt to call label instead of function." + ); + success = false; + }, + [&](Scope::Function const& _fun) + { + /// TODO: compare types too + arguments = _fun.arguments.size(); + returns = _fun.returns.size(); + } + ))) + { + m_errorReporter.declarationError(_funCall.functionName.location, "Function not found."); + success = false; + } + if (success) + { + if (_funCall.arguments.size() != arguments) + { + m_errorReporter.typeError( + _funCall.functionName.location, + "Expected " + to_string(arguments) + " arguments but got " + + to_string(_funCall.arguments.size()) + "." + ); + success = false; + } + } + for (auto const& arg: _funCall.arguments | boost::adaptors::reversed) + if (!expectExpression(arg)) + success = false; + m_stackHeight += int(returns) - int(arguments); + m_info.stackHeightInfo[&_funCall] = m_stackHeight; + return success; +} + +bool AsmAnalyzer::operator()(If const& _if) +{ + bool success = true; + + if (!expectExpression(*_if.condition)) + success = false; + m_stackHeight--; + + if (!(*this)(_if.body)) + success = false; + + m_info.stackHeightInfo[&_if] = m_stackHeight; + + return success; +} + +bool AsmAnalyzer::operator()(Switch const& _switch) +{ + solAssert(_switch.expression, ""); + + bool success = true; + + if (!expectExpression(*_switch.expression)) + success = false; + + set<tuple<LiteralKind, YulString>> cases; + for (auto const& _case: _switch.cases) + { + if (_case.value) + { + int const initialStackHeight = m_stackHeight; + // We cannot use "expectExpression" here because *_case.value is not a + // Statement and would be converted to a Statement otherwise. + if (!(*this)(*_case.value)) + success = false; + expectDeposit(1, initialStackHeight, _case.value->location); + m_stackHeight--; + + /// Note: the parser ensures there is only one default case + auto val = make_tuple(_case.value->kind, _case.value->value); + if (!cases.insert(val).second) + { + m_errorReporter.declarationError( + _case.location, + "Duplicate case defined" + ); + success = false; + } + } + + if (!(*this)(_case.body)) + success = false; + } + + m_stackHeight--; + m_info.stackHeightInfo[&_switch] = m_stackHeight; + + return success; +} + +bool AsmAnalyzer::operator()(ForLoop const& _for) +{ + solAssert(_for.condition, ""); + + Scope* originalScope = m_currentScope; + + bool success = true; + if (!(*this)(_for.pre)) + success = false; + // The block was closed already, but we re-open it again and stuff the + // condition, the body and the post part inside. + m_stackHeight += scope(&_for.pre).numberOfVariables(); + m_currentScope = &scope(&_for.pre); + + if (!expectExpression(*_for.condition)) + success = false; + m_stackHeight--; + if (!(*this)(_for.body)) + success = false; + if (!(*this)(_for.post)) + success = false; + + m_stackHeight -= scope(&_for.pre).numberOfVariables(); + m_info.stackHeightInfo[&_for] = m_stackHeight; + m_currentScope = originalScope; + + return success; +} + +bool AsmAnalyzer::operator()(Block const& _block) +{ + bool success = true; + auto previousScope = m_currentScope; + m_currentScope = &scope(&_block); + + int const initialStackHeight = m_stackHeight; + + for (auto const& s: _block.statements) + if (!boost::apply_visitor(*this, s)) + success = false; + + m_stackHeight -= scope(&_block).numberOfVariables(); + + int const stackDiff = m_stackHeight - initialStackHeight; + if (stackDiff != 0) + { + m_errorReporter.declarationError( + _block.location, + "Unbalanced stack at the end of a block: " + + ( + stackDiff > 0 ? + to_string(stackDiff) + string(" surplus item(s).") : + to_string(-stackDiff) + string(" missing item(s).") + ) + ); + success = false; + } + + m_info.stackHeightInfo[&_block] = m_stackHeight; + m_currentScope = previousScope; + return success; +} + +bool AsmAnalyzer::expectExpression(Expression const& _expr) +{ + bool success = true; + int const initialHeight = m_stackHeight; + if (!boost::apply_visitor(*this, _expr)) + success = false; + if (!expectDeposit(1, initialHeight, locationOf(_expr))) + success = false; + return success; +} + +bool AsmAnalyzer::expectDeposit(int _deposit, int _oldHeight, SourceLocation const& _location) +{ + if (m_stackHeight - _oldHeight != _deposit) + { + m_errorReporter.typeError( + _location, + "Expected expression to return one item to the stack, but did return " + + to_string(m_stackHeight - _oldHeight) + + " items." + ); + return false; + } + return true; +} + +bool AsmAnalyzer::checkAssignment(Identifier const& _variable, size_t _valueSize) +{ + solAssert(!_variable.name.empty(), ""); + bool success = true; + size_t numErrorsBefore = m_errorReporter.errors().size(); + size_t variableSize(-1); + if (Scope::Identifier const* var = m_currentScope->lookup(_variable.name)) + { + // Check that it is a variable + if (var->type() != typeid(Scope::Variable)) + { + m_errorReporter.typeError(_variable.location, "Assignment requires variable."); + success = false; + } + else if (!m_activeVariables.count(&boost::get<Scope::Variable>(*var))) + { + m_errorReporter.declarationError( + _variable.location, + "Variable " + _variable.name.str() + " used before it was declared." + ); + success = false; + } + variableSize = 1; + } + else if (m_resolver) + { + bool insideFunction = m_currentScope->insideFunction(); + variableSize = m_resolver(_variable, yul::IdentifierContext::LValue, insideFunction); + } + if (variableSize == size_t(-1)) + { + // Only add message if the callback did not. + if (numErrorsBefore == m_errorReporter.errors().size()) + m_errorReporter.declarationError(_variable.location, "Variable not found or variable not lvalue."); + success = false; + } + if (_valueSize == size_t(-1)) + _valueSize = variableSize == size_t(-1) ? 1 : variableSize; + + m_stackHeight -= _valueSize; + + if (_valueSize != variableSize && variableSize != size_t(-1)) + { + m_errorReporter.typeError( + _variable.location, + "Variable size (" + + to_string(variableSize) + + ") and value size (" + + to_string(_valueSize) + + ") do not match." + ); + success = false; + } + return success; +} + +Scope& AsmAnalyzer::scope(Block const* _block) +{ + solAssert(m_info.scopes.count(_block) == 1, "Scope requested but not present."); + auto scopePtr = m_info.scopes.at(_block); + solAssert(scopePtr, "Scope requested but not present."); + return *scopePtr; +} +void AsmAnalyzer::expectValidType(string const& type, SourceLocation const& _location) +{ + if (m_flavour != AsmFlavour::Yul) + return; + + if (!builtinTypes.count(type)) + m_errorReporter.typeError( + _location, + "\"" + type + "\" is not a valid type (user defined types are not yet supported)." + ); +} + +void AsmAnalyzer::warnOnInstructions(solidity::Instruction _instr, SourceLocation const& _location) +{ + // We assume that returndatacopy, returndatasize and staticcall are either all available + // or all not available. + solAssert(m_evmVersion.supportsReturndata() == m_evmVersion.hasStaticCall(), ""); + // Similarly we assume bitwise shifting and create2 go together. + solAssert(m_evmVersion.hasBitwiseShifting() == m_evmVersion.hasCreate2(), ""); + + if (_instr == solidity::Instruction::EXTCODEHASH) + m_errorReporter.warning( + _location, + "The \"" + + boost::to_lower_copy(instructionInfo(_instr).name) + + "\" instruction is not supported by the VM version \"" + + "" + m_evmVersion.name() + + "\" you are currently compiling for. " + + "It will be interpreted as an invalid instruction on this VM." + ); + else if (( + _instr == solidity::Instruction::RETURNDATACOPY || + _instr == solidity::Instruction::RETURNDATASIZE || + _instr == solidity::Instruction::STATICCALL + ) && !m_evmVersion.supportsReturndata()) + m_errorReporter.warning( + _location, + "The \"" + + boost::to_lower_copy(instructionInfo(_instr).name) + + "\" instruction is only available for Byzantium-compatible VMs. " + + "You are currently compiling for \"" + + m_evmVersion.name() + + "\", where it will be interpreted as an invalid instruction." + ); + else if (( + _instr == solidity::Instruction::SHL || + _instr == solidity::Instruction::SHR || + _instr == solidity::Instruction::SAR || + _instr == solidity::Instruction::CREATE2 + ) && !m_evmVersion.hasBitwiseShifting()) + m_errorReporter.warning( + _location, + "The \"" + + boost::to_lower_copy(instructionInfo(_instr).name) + + "\" instruction is only available for Constantinople-compatible VMs. " + + "You are currently compiling for \"" + + m_evmVersion.name() + + "\", where it will be interpreted as an invalid instruction." + ); + + if (_instr == solidity::Instruction::JUMP || _instr == solidity::Instruction::JUMPI || _instr == solidity::Instruction::JUMPDEST) + { + solAssert(m_flavour == AsmFlavour::Loose, ""); + m_errorReporter.error( + m_errorTypeForLoose ? *m_errorTypeForLoose : Error::Type::Warning, + _location, + "Jump instructions and labels are low-level EVM features that can lead to " + "incorrect stack access. Because of that they are discouraged. " + "Please consider using \"switch\", \"if\" or \"for\" statements instead." + ); + } +} + +void AsmAnalyzer::checkLooseFeature(SourceLocation const& _location, string const& _description) +{ + if (m_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 new file mode 100644 index 00000000..34e32eb0 --- /dev/null +++ b/libyul/AsmAnalysis.h @@ -0,0 +1,122 @@ +/* + 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/>. +*/ +/** + * Analysis part of inline assembly. + */ + +#pragma once + +#include <liblangutil/Exceptions.h> +#include <liblangutil/EVMVersion.h> + +#include <libyul/AsmScope.h> + +#include <libyul/backends/evm/AbstractAssembly.h> + +#include <libyul/AsmDataForward.h> + +#include <boost/variant.hpp> +#include <boost/optional.hpp> + +#include <functional> +#include <memory> + +namespace langutil +{ +class ErrorReporter; +struct SourceLocation; +} + +namespace yul +{ + +struct AsmAnalysisInfo; + +/** + * Performs the full analysis stage, calls the ScopeFiller internally, then resolves + * references and performs other checks. + * If all these checks pass, code generation should not throw errors. + */ +class AsmAnalyzer: public boost::static_visitor<bool> +{ +public: + explicit AsmAnalyzer( + AsmAnalysisInfo& _analysisInfo, + langutil::ErrorReporter& _errorReporter, + dev::solidity::EVMVersion _evmVersion, + boost::optional<langutil::Error::Type> _errorTypeForLoose, + AsmFlavour _flavour = AsmFlavour::Loose, + ExternalIdentifierAccess::Resolver const& _resolver = ExternalIdentifierAccess::Resolver() + ): + m_resolver(_resolver), + m_info(_analysisInfo), + m_errorReporter(_errorReporter), + m_evmVersion(_evmVersion), + m_flavour(_flavour), + m_errorTypeForLoose(_errorTypeForLoose) + {} + + bool analyze(Block const& _block); + + bool operator()(Instruction const&); + bool operator()(Literal const& _literal); + bool operator()(Identifier const&); + bool operator()(FunctionalInstruction const& _functionalInstruction); + bool operator()(Label const& _label); + bool operator()(ExpressionStatement const&); + bool operator()(StackAssignment const&); + bool operator()(Assignment const& _assignment); + bool operator()(VariableDeclaration const& _variableDeclaration); + bool operator()(FunctionDefinition const& _functionDefinition); + bool operator()(FunctionCall const& _functionCall); + bool operator()(If const& _if); + bool operator()(Switch const& _switch); + bool operator()(ForLoop const& _forLoop); + bool operator()(Block const& _block); + +private: + /// Visits the statement and expects it to deposit one item onto the stack. + bool expectExpression(Expression const& _expr); + bool expectDeposit(int _deposit, int _oldHeight, langutil::SourceLocation const& _location); + + /// Verifies that a variable to be assigned to exists and has the same size + /// as the value, @a _valueSize, unless that is equal to -1. + 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 warnOnInstructions(dev::solidity::Instruction _instr, langutil::SourceLocation const& _location); + + /// Depending on @a m_flavour and @a m_errorTypeForLoose, throws an internal compiler + /// exception (if the flavour is not Loose), reports an error/warning + /// (if m_errorTypeForLoose is set) or does nothing. + void checkLooseFeature(langutil::SourceLocation const& _location, std::string const& _description); + + int m_stackHeight = 0; + yul::ExternalIdentifierAccess::Resolver m_resolver; + Scope* m_currentScope = nullptr; + /// Variables that are active at the current point in assembly (as opposed to + /// "part of the scope but not yet declared") + std::set<Scope::Variable const*> m_activeVariables; + AsmAnalysisInfo& m_info; + langutil::ErrorReporter& m_errorReporter; + dev::solidity::EVMVersion m_evmVersion; + AsmFlavour m_flavour = AsmFlavour::Loose; + boost::optional<langutil::Error::Type> m_errorTypeForLoose; +}; + +} diff --git a/libyul/AsmAnalysisInfo.cpp b/libyul/AsmAnalysisInfo.cpp new file mode 100644 index 00000000..450c0f8f --- /dev/null +++ b/libyul/AsmAnalysisInfo.cpp @@ -0,0 +1,26 @@ +/* + 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/>. +*/ +/** + * Information generated during analyzer part of inline assembly. + */ + +#include <libyul/AsmAnalysisInfo.h> + +#include <libyul/AsmScope.h> + +#include <ostream> + diff --git a/libyul/AsmAnalysisInfo.h b/libyul/AsmAnalysisInfo.h new file mode 100644 index 00000000..08a35ade --- /dev/null +++ b/libyul/AsmAnalysisInfo.h @@ -0,0 +1,46 @@ +/* + 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/>. +*/ +/** + * Information generated during analyzer part of inline assembly. + */ + +#pragma once + +#include <libyul/AsmDataForward.h> + +#include <boost/variant.hpp> + +#include <map> +#include <memory> +#include <vector> + +namespace yul +{ + +struct Scope; + +struct AsmAnalysisInfo +{ + using StackHeightInfo = std::map<void const*, int>; + using Scopes = std::map<Block const*, std::shared_ptr<Scope>>; + Scopes scopes; + StackHeightInfo stackHeightInfo; + /// Virtual blocks which will be used for scopes for function arguments and return values. + std::map<FunctionDefinition const*, std::shared_ptr<Block const>> virtualBlocks; +}; + +} diff --git a/libyul/AsmCodeGen.cpp b/libyul/AsmCodeGen.cpp new file mode 100644 index 00000000..23bf395d --- /dev/null +++ b/libyul/AsmCodeGen.cpp @@ -0,0 +1,163 @@ +/* + 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/>. +*/ +/** + * @author Christian <c@ethdev.com> + * @date 2016 + * Code-generating part of inline assembly. + */ + +#include <libyul/AsmCodeGen.h> + +#include <libyul/AsmParser.h> +#include <libyul/AsmData.h> +#include <libyul/AsmScope.h> +#include <libyul/AsmAnalysis.h> +#include <libyul/AsmAnalysisInfo.h> + +#include <libyul/backends/evm/AbstractAssembly.h> +#include <libyul/backends/evm/EVMCodeTransform.h> + +#include <libevmasm/Assembly.h> +#include <libevmasm/Instruction.h> + +#include <liblangutil/SourceLocation.h> + +#include <libdevcore/CommonIO.h> + +#include <boost/range/adaptor/reversed.hpp> +#include <boost/range/adaptor/map.hpp> +#include <boost/range/algorithm/count_if.hpp> + +#include <memory> +#include <functional> + +using namespace std; +using namespace dev; +using namespace langutil; +using namespace yul; +using namespace dev::solidity; + +class EthAssemblyAdapter: public AbstractAssembly +{ +public: + explicit EthAssemblyAdapter(eth::Assembly& _assembly): + m_assembly(_assembly) + { + } + virtual void setSourceLocation(SourceLocation const& _location) override + { + m_assembly.setSourceLocation(_location); + } + virtual int stackHeight() const override { return m_assembly.deposit(); } + virtual void appendInstruction(solidity::Instruction _instruction) override + { + m_assembly.append(_instruction); + } + virtual void appendConstant(u256 const& _constant) override + { + m_assembly.append(_constant); + } + /// Append a label. + virtual void appendLabel(LabelID _labelId) override + { + m_assembly.append(eth::AssemblyItem(eth::Tag, _labelId)); + } + /// Append a label reference. + virtual void appendLabelReference(LabelID _labelId) override + { + m_assembly.append(eth::AssemblyItem(eth::PushTag, _labelId)); + } + virtual size_t newLabelId() override + { + return assemblyTagToIdentifier(m_assembly.newTag()); + } + virtual size_t namedLabel(std::string const& _name) override + { + return assemblyTagToIdentifier(m_assembly.namedTag(_name)); + } + virtual void appendLinkerSymbol(std::string const& _linkerSymbol) override + { + m_assembly.appendLibraryAddress(_linkerSymbol); + } + virtual void appendJump(int _stackDiffAfter) override + { + appendInstruction(solidity::Instruction::JUMP); + m_assembly.adjustDeposit(_stackDiffAfter); + } + virtual void appendJumpTo(LabelID _labelId, int _stackDiffAfter) override + { + appendLabelReference(_labelId); + appendJump(_stackDiffAfter); + } + virtual void appendJumpToIf(LabelID _labelId) override + { + appendLabelReference(_labelId); + appendInstruction(solidity::Instruction::JUMPI); + } + virtual void appendBeginsub(LabelID, int) override + { + // TODO we could emulate that, though + solAssert(false, "BEGINSUB not implemented for EVM 1.0"); + } + /// Call a subroutine. + virtual void appendJumpsub(LabelID, int, int) override + { + // TODO we could emulate that, though + solAssert(false, "JUMPSUB not implemented for EVM 1.0"); + } + + /// Return from a subroutine. + virtual void appendReturnsub(int, int) override + { + // TODO we could emulate that, though + solAssert(false, "RETURNSUB not implemented for EVM 1.0"); + } + + virtual void appendAssemblySize() override + { + m_assembly.appendProgramSize(); + } + +private: + static LabelID assemblyTagToIdentifier(eth::AssemblyItem const& _tag) + { + u256 id = _tag.data(); + solAssert(id <= std::numeric_limits<LabelID>::max(), "Tag id too large."); + return LabelID(id); + } + + eth::Assembly& m_assembly; +}; + +void CodeGenerator::assemble( + Block const& _parsedData, + AsmAnalysisInfo& _analysisInfo, + eth::Assembly& _assembly, + ExternalIdentifierAccess const& _identifierAccess, + bool _useNamedLabelsForFunctions +) +{ + EthAssemblyAdapter assemblyAdapter(_assembly); + CodeTransform( + assemblyAdapter, + _analysisInfo, + false, + false, + _identifierAccess, + _useNamedLabelsForFunctions + )(_parsedData); +} diff --git a/libyul/AsmCodeGen.h b/libyul/AsmCodeGen.h new file mode 100644 index 00000000..fd5ac0a1 --- /dev/null +++ b/libyul/AsmCodeGen.h @@ -0,0 +1,54 @@ +/* + 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/>. +*/ +/** + * @author Christian <c@ethdev.com> + * @date 2016 + * Code-generating part of inline assembly. + */ + +#pragma once + +#include <libyul/AsmAnalysis.h> + +#include <functional> + +namespace dev +{ +namespace eth +{ +class Assembly; +} +} + +namespace yul +{ +struct Block; + +class CodeGenerator +{ +public: + /// Performs code generation and appends generated to _assembly. + static void assemble( + Block const& _parsedData, + AsmAnalysisInfo& _analysisInfo, + dev::eth::Assembly& _assembly, + yul::ExternalIdentifierAccess const& _identifierAccess = yul::ExternalIdentifierAccess(), + bool _useNamedLabelsForFunctions = false + ); +}; + +} diff --git a/libyul/AsmData.h b/libyul/AsmData.h new file mode 100644 index 00000000..86c373a4 --- /dev/null +++ b/libyul/AsmData.h @@ -0,0 +1,96 @@ +/* + 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/>. +*/ +/** + * @author Christian <c@ethdev.com> + * @date 2016 + * Parsed inline assembly to be used by the AST + */ + +#pragma once + +#include <libyul/AsmDataForward.h> +#include <libyul/YulString.h> + +#include <libevmasm/Instruction.h> +#include <liblangutil/SourceLocation.h> + +#include <boost/variant.hpp> +#include <boost/noncopyable.hpp> + +#include <map> +#include <memory> + +namespace yul +{ + +using Type = YulString; + +struct TypedName { langutil::SourceLocation location; YulString name; Type type; }; +using TypedNameList = std::vector<TypedName>; + +/// Direct EVM instruction (except PUSHi and JUMPDEST) +struct Instruction { langutil::SourceLocation location; dev::solidity::Instruction instruction; }; +/// Literal number or string (up to 32 bytes) +enum class LiteralKind { Number, Boolean, String }; +struct Literal { langutil::SourceLocation location; LiteralKind kind; YulString value; Type type; }; +/// External / internal identifier or label reference +struct Identifier { langutil::SourceLocation location; YulString name; }; +/// Jump label ("name:") +struct Label { langutil::SourceLocation location; YulString name; }; +/// Assignment from stack (":= x", moves stack top into x, potentially multiple slots) +struct StackAssignment { langutil::SourceLocation location; Identifier variableName; }; +/// Assignment ("x := mload(20:u256)", expects push-1-expression on the right hand +/// side and requires x to occupy exactly one stack slot. +/// +/// Multiple assignment ("x, y := f()"), where the left hand side variables each occupy +/// a single stack slot and expects a single expression on the right hand returning +/// the same amount of items as the number of variables. +struct Assignment { langutil::SourceLocation location; std::vector<Identifier> variableNames; std::shared_ptr<Expression> value; }; +/// Functional instruction, e.g. "mul(mload(20:u256), add(2:u256, x))" +struct FunctionalInstruction { langutil::SourceLocation location; dev::solidity::Instruction instruction; std::vector<Expression> arguments; }; +struct FunctionCall { langutil::SourceLocation location; Identifier functionName; std::vector<Expression> arguments; }; +/// Statement that contains only a single expression +struct ExpressionStatement { langutil::SourceLocation location; Expression expression; }; +/// Block-scope variable declaration ("let x:u256 := mload(20:u256)"), non-hoisted +struct VariableDeclaration { langutil::SourceLocation location; TypedNameList variables; std::shared_ptr<Expression> value; }; +/// Block that creates a scope (frees declared stack variables) +struct Block { langutil::SourceLocation location; std::vector<Statement> statements; }; +/// Function definition ("function f(a, b) -> (d, e) { ... }") +struct FunctionDefinition { langutil::SourceLocation location; YulString name; TypedNameList parameters; TypedNameList returnVariables; Block body; }; +/// Conditional execution without "else" part. +struct If { langutil::SourceLocation location; std::shared_ptr<Expression> condition; Block body; }; +/// Switch case or default case +struct Case { langutil::SourceLocation location; std::shared_ptr<Literal> value; Block body; }; +/// Switch statement +struct Switch { langutil::SourceLocation location; std::shared_ptr<Expression> expression; std::vector<Case> cases; }; +struct ForLoop { langutil::SourceLocation location; Block pre; std::shared_ptr<Expression> condition; Block post; Block body; }; + +struct LocationExtractor: boost::static_visitor<langutil::SourceLocation> +{ + template <class T> langutil::SourceLocation operator()(T const& _node) const + { + return _node.location; + } +}; + +/// Extracts the source location from an inline assembly node. +template <class T> inline langutil::SourceLocation locationOf(T const& _node) +{ + return boost::apply_visitor(LocationExtractor(), _node); +} + +} diff --git a/libyul/AsmDataForward.h b/libyul/AsmDataForward.h new file mode 100644 index 00000000..046c8248 --- /dev/null +++ b/libyul/AsmDataForward.h @@ -0,0 +1,59 @@ +/* + 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/>. +*/ +/** + * @author Christian <c@ethdev.com> + * @date 2016 + * Forward declaration of classes for inline assembly / Yul AST + */ + +#pragma once + +#include <boost/variant.hpp> + +namespace yul +{ + +struct Instruction; +struct Literal; +struct Label; +struct StackAssignment; +struct Identifier; +struct Assignment; +struct VariableDeclaration; +struct FunctionalInstruction; +struct FunctionDefinition; +struct FunctionCall; +struct If; +struct Switch; +struct Case; +struct ForLoop; +struct ExpressionStatement; +struct Block; + +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 new file mode 100644 index 00000000..2ce94f85 --- /dev/null +++ b/libyul/AsmParser.cpp @@ -0,0 +1,617 @@ +/* + 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/>. +*/ +/** + * @author Christian <c@ethdev.com> + * @date 2016 + * Solidity inline assembly parser. + */ + +#include <libyul/AsmParser.h> +#include <liblangutil/Scanner.h> +#include <liblangutil/ErrorReporter.h> + +#include <boost/algorithm/string.hpp> + +#include <cctype> +#include <algorithm> + +using namespace std; +using namespace dev; +using namespace langutil; +using namespace yul; +using namespace dev::solidity; + +shared_ptr<Block> Parser::parse(std::shared_ptr<Scanner> const& _scanner, bool _reuseScanner) +{ + m_recursionDepth = 0; + try + { + m_scanner = _scanner; + auto block = make_shared<Block>(parseBlock()); + if (!_reuseScanner) + expectToken(Token::EOS); + return block; + } + catch (FatalError const&) + { + if (m_errorReporter.errors().empty()) + throw; // Something is weird here, rather throw again. + } + return nullptr; +} + +Block Parser::parseBlock() +{ + RecursionGuard recursionGuard(*this); + Block block = createWithLocation<Block>(); + expectToken(Token::LBrace); + while (currentToken() != Token::RBrace) + block.statements.emplace_back(parseStatement()); + block.location.end = endPosition(); + advance(); + return block; +} + +Statement Parser::parseStatement() +{ + RecursionGuard recursionGuard(*this); + switch (currentToken()) + { + case Token::Let: + return parseVariableDeclaration(); + case Token::Function: + return parseFunctionDefinition(); + case Token::LBrace: + return parseBlock(); + case Token::If: + { + If _if = createWithLocation<If>(); + m_scanner->next(); + _if.condition = make_shared<Expression>(parseExpression()); + _if.body = parseBlock(); + return _if; + } + case Token::Switch: + { + Switch _switch = createWithLocation<Switch>(); + m_scanner->next(); + _switch.expression = make_shared<Expression>(parseExpression()); + while (m_scanner->currentToken() == Token::Case) + _switch.cases.emplace_back(parseCase()); + if (m_scanner->currentToken() == Token::Default) + _switch.cases.emplace_back(parseCase()); + if (m_scanner->currentToken() == Token::Default) + fatalParserError("Only one default case allowed."); + else if (m_scanner->currentToken() == Token::Case) + fatalParserError("Case not allowed after default case."); + if (_switch.cases.empty()) + fatalParserError("Switch statement without any cases."); + _switch.location.end = _switch.cases.back().body.location.end; + return _switch; + } + case Token::For: + return parseForLoop(); + case Token::Assign: + { + if (m_flavour != AsmFlavour::Loose) + break; + StackAssignment assignment = createWithLocation<StackAssignment>(); + advance(); + expectToken(Token::Colon); + assignment.variableName.location = location(); + assignment.variableName.name = YulString(currentLiteral()); + if (instructions().count(assignment.variableName.name.str())) + fatalParserError("Identifier expected, got instruction name."); + assignment.location.end = endPosition(); + expectToken(Token::Identifier); + return assignment; + } + default: + break; + } + // Options left: + // Simple instruction (might turn into functional), + // literal, + // identifier (might turn into label or functional assignment) + ElementaryOperation elementary(parseElementaryOperation()); + switch (currentToken()) + { + case Token::LParen: + { + Expression expr = parseCall(std::move(elementary)); + return ExpressionStatement{locationOf(expr), expr}; + } + case Token::Comma: + { + // if a comma follows, a multiple assignment is assumed + + if (elementary.type() != typeid(Identifier)) + fatalParserError("Label name / variable name must precede \",\" (multiple assignment)."); + Identifier const& identifier = boost::get<Identifier>(elementary); + + Assignment assignment = createWithLocation<Assignment>(identifier.location); + assignment.variableNames.emplace_back(identifier); + + do + { + expectToken(Token::Comma); + elementary = parseElementaryOperation(); + if (elementary.type() != typeid(Identifier)) + fatalParserError("Variable name expected in multiple assignment."); + assignment.variableNames.emplace_back(boost::get<Identifier>(elementary)); + } + while (currentToken() == Token::Comma); + + expectToken(Token::Colon); + expectToken(Token::Assign); + + assignment.value.reset(new Expression(parseExpression())); + assignment.location.end = locationOf(*assignment.value).end; + return assignment; + } + case Token::Colon: + { + if (elementary.type() != typeid(Identifier)) + fatalParserError("Label name / variable name must precede \":\"."); + Identifier const& identifier = boost::get<Identifier>(elementary); + advance(); + // identifier:=: should be parsed as identifier: =: (i.e. a label), + // while identifier:= (being followed by a non-colon) as identifier := (assignment). + if (currentToken() == Token::Assign && peekNextToken() != Token::Colon) + { + Assignment assignment = createWithLocation<Assignment>(identifier.location); + if (m_flavour != AsmFlavour::Yul && instructions().count(identifier.name.str())) + fatalParserError("Cannot use instruction names for identifier names."); + advance(); + assignment.variableNames.emplace_back(identifier); + assignment.value.reset(new Expression(parseExpression())); + assignment.location.end = locationOf(*assignment.value).end; + return assignment; + } + else + { + // label + if (m_flavour != AsmFlavour::Loose) + fatalParserError("Labels are not supported."); + Label label = createWithLocation<Label>(identifier.location); + label.name = identifier.name; + return label; + } + } + default: + if (m_flavour != AsmFlavour::Loose) + fatalParserError("Call or assignment expected."); + break; + } + if (elementary.type() == typeid(Identifier)) + { + Expression expr = boost::get<Identifier>(elementary); + return ExpressionStatement{locationOf(expr), expr}; + } + else if (elementary.type() == typeid(Literal)) + { + Expression expr = boost::get<Literal>(elementary); + return ExpressionStatement{locationOf(expr), expr}; + } + else + { + solAssert(elementary.type() == typeid(Instruction), "Invalid elementary operation."); + return boost::get<Instruction>(elementary); + } +} + +Case Parser::parseCase() +{ + RecursionGuard recursionGuard(*this); + Case _case = createWithLocation<Case>(); + if (m_scanner->currentToken() == Token::Default) + m_scanner->next(); + else if (m_scanner->currentToken() == Token::Case) + { + m_scanner->next(); + ElementaryOperation literal = parseElementaryOperation(); + if (literal.type() != typeid(Literal)) + fatalParserError("Literal expected."); + _case.value = make_shared<Literal>(boost::get<Literal>(std::move(literal))); + } + else + fatalParserError("Case or default case expected."); + _case.body = parseBlock(); + _case.location.end = _case.body.location.end; + return _case; +} + +ForLoop Parser::parseForLoop() +{ + RecursionGuard recursionGuard(*this); + ForLoop forLoop = createWithLocation<ForLoop>(); + expectToken(Token::For); + forLoop.pre = parseBlock(); + forLoop.condition = make_shared<Expression>(parseExpression()); + forLoop.post = parseBlock(); + forLoop.body = parseBlock(); + forLoop.location.end = forLoop.body.location.end; + return forLoop; +} + +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)) + { + Instruction const& instr = boost::get<Instruction>(operation); + // Disallow instructions returning multiple values (and DUP/SWAP) as expression. + if ( + instructionInfo(instr.instruction).ret != 1 || + isDupInstruction(instr.instruction) || + isSwapInstruction(instr.instruction) + ) + fatalParserError( + "Instruction \"" + + instructionNames().at(instr.instruction) + + "\" not allowed in this context." + ); + if (m_flavour != AsmFlavour::Loose && currentToken() != Token::LParen) + fatalParserError( + "Non-functional instructions are not allowed in this context." + ); + // Enforce functional notation for instructions requiring multiple arguments. + int args = instructionInfo(instr.instruction).args; + if (args > 0 && currentToken() != Token::LParen) + fatalParserError(string( + "Expected '(' (instruction \"" + + instructionNames().at(instr.instruction) + + "\" expects " + + to_string(args) + + " arguments)" + )); + } + if (currentToken() == Token::LParen) + return parseCall(std::move(operation)); + else if (operation.type() == typeid(Instruction)) + { + // Instructions not taking arguments are allowed as expressions. + solAssert(m_flavour == AsmFlavour::Loose, ""); + Instruction& instr = boost::get<Instruction>(operation); + return FunctionalInstruction{std::move(instr.location), instr.instruction, {}}; + } + else if (operation.type() == typeid(Identifier)) + return boost::get<Identifier>(operation); + else + { + solAssert(operation.type() == typeid(Literal), ""); + return boost::get<Literal>(operation); + } +} + +std::map<string, dev::solidity::Instruction> const& Parser::instructions() +{ + // Allowed instructions, lowercase names. + static map<string, dev::solidity::Instruction> s_instructions; + if (s_instructions.empty()) + { + for (auto const& instruction: solidity::c_instructions) + { + if ( + instruction.second == solidity::Instruction::JUMPDEST || + solidity::isPushInstruction(instruction.second) + ) + continue; + string name = instruction.first; + transform(name.begin(), name.end(), name.begin(), [](unsigned char _c) { return tolower(_c); }); + s_instructions[name] = instruction.second; + } + } + return s_instructions; +} + +std::map<dev::solidity::Instruction, string> const& Parser::instructionNames() +{ + static map<dev::solidity::Instruction, string> s_instructionNames; + if (s_instructionNames.empty()) + { + for (auto const& instr: instructions()) + s_instructionNames[instr.second] = instr.first; + // set the ambiguous instructions to a clear default + s_instructionNames[solidity::Instruction::SELFDESTRUCT] = "selfdestruct"; + s_instructionNames[solidity::Instruction::KECCAK256] = "keccak256"; + } + return s_instructionNames; +} + +Parser::ElementaryOperation Parser::parseElementaryOperation() +{ + RecursionGuard recursionGuard(*this); + ElementaryOperation ret; + switch (currentToken()) + { + case Token::Identifier: + case Token::Return: + case Token::Byte: + case Token::Address: + { + string literal; + if (currentToken() == Token::Return) + literal = "return"; + else if (currentToken() == Token::Byte) + literal = "byte"; + else if (currentToken() == Token::Address) + literal = "address"; + else + literal = currentLiteral(); + // first search the set of instructions. + if (m_flavour != AsmFlavour::Yul && instructions().count(literal)) + { + dev::solidity::Instruction const& instr = instructions().at(literal); + ret = Instruction{location(), instr}; + } + else + ret = Identifier{location(), YulString{literal}}; + advance(); + break; + } + case Token::StringLiteral: + case Token::Number: + case Token::TrueLiteral: + case Token::FalseLiteral: + { + LiteralKind kind = LiteralKind::Number; + switch (currentToken()) + { + case Token::StringLiteral: + kind = LiteralKind::String; + break; + case Token::Number: + if (!isValidNumberLiteral(currentLiteral())) + fatalParserError("Invalid number literal."); + kind = LiteralKind::Number; + break; + case Token::TrueLiteral: + case Token::FalseLiteral: + kind = LiteralKind::Boolean; + break; + default: + break; + } + + Literal literal{ + location(), + kind, + YulString{currentLiteral()}, + {} + }; + advance(); + if (m_flavour == AsmFlavour::Yul) + { + expectToken(Token::Colon); + literal.location.end = endPosition(); + literal.type = YulString{expectAsmIdentifier()}; + } + else if (kind == LiteralKind::Boolean) + fatalParserError("True and false are not valid literals."); + ret = std::move(literal); + break; + } + default: + fatalParserError( + m_flavour == AsmFlavour::Yul ? + "Literal or identifier expected." : + "Literal, identifier or instruction expected." + ); + } + return ret; +} + +VariableDeclaration Parser::parseVariableDeclaration() +{ + RecursionGuard recursionGuard(*this); + VariableDeclaration varDecl = createWithLocation<VariableDeclaration>(); + expectToken(Token::Let); + while (true) + { + varDecl.variables.emplace_back(parseTypedName()); + if (currentToken() == Token::Comma) + expectToken(Token::Comma); + else + break; + } + if (currentToken() == Token::Colon) + { + expectToken(Token::Colon); + expectToken(Token::Assign); + varDecl.value.reset(new Expression(parseExpression())); + varDecl.location.end = locationOf(*varDecl.value).end; + } + else + varDecl.location.end = varDecl.variables.back().location.end; + return varDecl; +} + +FunctionDefinition Parser::parseFunctionDefinition() +{ + RecursionGuard recursionGuard(*this); + FunctionDefinition funDef = createWithLocation<FunctionDefinition>(); + expectToken(Token::Function); + funDef.name = YulString{expectAsmIdentifier()}; + expectToken(Token::LParen); + while (currentToken() != Token::RParen) + { + funDef.parameters.emplace_back(parseTypedName()); + if (currentToken() == Token::RParen) + break; + expectToken(Token::Comma); + } + expectToken(Token::RParen); + if (currentToken() == Token::Sub) + { + expectToken(Token::Sub); + expectToken(Token::GreaterThan); + while (true) + { + funDef.returnVariables.emplace_back(parseTypedName()); + if (currentToken() == Token::LBrace) + break; + expectToken(Token::Comma); + } + } + funDef.body = parseBlock(); + funDef.location.end = funDef.body.location.end; + return funDef; +} + +Expression Parser::parseCall(Parser::ElementaryOperation&& _initialOp) +{ + RecursionGuard recursionGuard(*this); + if (_initialOp.type() == typeid(Instruction)) + { + solAssert(m_flavour != AsmFlavour::Yul, "Instructions are invalid in Yul"); + Instruction& instruction = boost::get<Instruction>(_initialOp); + FunctionalInstruction ret; + ret.instruction = instruction.instruction; + ret.location = std::move(instruction.location); + solidity::Instruction instr = ret.instruction; + InstructionInfo instrInfo = instructionInfo(instr); + if (solidity::isDupInstruction(instr)) + fatalParserError("DUPi instructions not allowed for functional notation"); + if (solidity::isSwapInstruction(instr)) + fatalParserError("SWAPi instructions not allowed for functional notation"); + expectToken(Token::LParen); + unsigned args = unsigned(instrInfo.args); + for (unsigned i = 0; i < args; ++i) + { + /// check for premature closing parentheses + if (currentToken() == Token::RParen) + fatalParserError(string( + "Expected expression (instruction \"" + + instructionNames().at(instr) + + "\" expects " + + to_string(args) + + " arguments)" + )); + + ret.arguments.emplace_back(parseExpression()); + if (i != args - 1) + { + if (currentToken() != Token::Comma) + fatalParserError(string( + "Expected ',' (instruction \"" + + instructionNames().at(instr) + + "\" expects " + + to_string(args) + + " arguments)" + )); + else + advance(); + } + } + ret.location.end = endPosition(); + if (currentToken() == Token::Comma) + fatalParserError(string( + "Expected ')' (instruction \"" + + instructionNames().at(instr) + + "\" expects " + + to_string(args) + + " arguments)" + )); + expectToken(Token::RParen); + return ret; + } + else if (_initialOp.type() == typeid(Identifier)) + { + FunctionCall ret; + ret.functionName = std::move(boost::get<Identifier>(_initialOp)); + ret.location = ret.functionName.location; + expectToken(Token::LParen); + while (currentToken() != Token::RParen) + { + ret.arguments.emplace_back(parseExpression()); + if (currentToken() == Token::RParen) + break; + expectToken(Token::Comma); + } + ret.location.end = endPosition(); + expectToken(Token::RParen); + return ret; + } + else + fatalParserError( + m_flavour == AsmFlavour::Yul ? + "Function name expected." : + "Assembly instruction or function name required in front of \"(\")" + ); + + return {}; +} + +TypedName Parser::parseTypedName() +{ + RecursionGuard recursionGuard(*this); + TypedName typedName = createWithLocation<TypedName>(); + typedName.name = YulString{expectAsmIdentifier()}; + if (m_flavour == AsmFlavour::Yul) + { + expectToken(Token::Colon); + typedName.location.end = endPosition(); + typedName.type = YulString{expectAsmIdentifier()}; + } + return typedName; +} + +string Parser::expectAsmIdentifier() +{ + string name = currentLiteral(); + if (m_flavour == AsmFlavour::Yul) + { + switch (currentToken()) + { + case Token::Return: + case Token::Byte: + case Token::Address: + case Token::Bool: + advance(); + return name; + default: + break; + } + } + else if (instructions().count(name)) + fatalParserError("Cannot use instruction names for identifier names."); + expectToken(Token::Identifier); + return name; +} + +bool Parser::isValidNumberLiteral(string const& _literal) +{ + try + { + // Try to convert _literal to u256. + auto tmp = u256(_literal); + (void) tmp; + } + catch (...) + { + return false; + } + if (boost::starts_with(_literal, "0x")) + return true; + else + return _literal.find_first_not_of("0123456789") == string::npos; +} diff --git a/libyul/AsmParser.h b/libyul/AsmParser.h new file mode 100644 index 00000000..52166a20 --- /dev/null +++ b/libyul/AsmParser.h @@ -0,0 +1,89 @@ +/* + 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/>. +*/ +/** + * @author Christian <c@ethdev.com> + * @date 2016 + * Solidity inline assembly parser. + */ + +#pragma once + +#include <memory> +#include <vector> +#include <libyul/AsmData.h> +#include <liblangutil/SourceLocation.h> +#include <liblangutil/Scanner.h> +#include <liblangutil/ParserBase.h> + +namespace yul +{ + +class Parser: public langutil::ParserBase +{ +public: + explicit Parser(langutil::ErrorReporter& _errorReporter, AsmFlavour _flavour = AsmFlavour::Loose): + ParserBase(_errorReporter), m_flavour(_flavour) {} + + /// Parses an inline assembly block starting with `{` and ending with `}`. + /// @param _reuseScanner if true, do check for end of input after the `}`. + /// @returns an empty shared pointer on error. + std::shared_ptr<Block> parse(std::shared_ptr<langutil::Scanner> const& _scanner, bool _reuseScanner); + +protected: + using ElementaryOperation = boost::variant<Instruction, Literal, Identifier>; + + /// Creates an inline assembly node with the given source location. + template <class T> T createWithLocation(langutil::SourceLocation const& _loc = {}) const + { + T r; + r.location = _loc; + if (r.location.isEmpty()) + { + r.location.start = position(); + r.location.end = endPosition(); + } + if (!r.location.source) + r.location.source = m_scanner->charStream(); + return r; + } + langutil::SourceLocation location() const { return {position(), endPosition(), m_scanner->charStream()}; } + + Block parseBlock(); + Statement parseStatement(); + Case parseCase(); + ForLoop parseForLoop(); + /// Parses a functional expression that has to push exactly one stack element + Expression parseExpression(); + static std::map<std::string, dev::solidity::Instruction> const& instructions(); + static std::map<dev::solidity::Instruction, std::string> 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. + ElementaryOperation parseElementaryOperation(); + VariableDeclaration parseVariableDeclaration(); + FunctionDefinition parseFunctionDefinition(); + Expression parseCall(ElementaryOperation&& _initialOp); + TypedName parseTypedName(); + std::string expectAsmIdentifier(); + + static bool isValidNumberLiteral(std::string const& _literal); + +private: + AsmFlavour m_flavour = AsmFlavour::Loose; +}; + +} diff --git a/libyul/AsmPrinter.cpp b/libyul/AsmPrinter.cpp new file mode 100644 index 00000000..eaaba9f3 --- /dev/null +++ b/libyul/AsmPrinter.cpp @@ -0,0 +1,250 @@ +/* + 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/>. +*/ +/** + * @author Christian <c@ethdev.com> + * @date 2017 + * Converts a parsed assembly into its textual form. + */ + +#include <libyul/AsmPrinter.h> +#include <libyul/AsmData.h> +#include <liblangutil/Exceptions.h> + +#include <libdevcore/CommonData.h> + +#include <boost/algorithm/string.hpp> +#include <boost/algorithm/string/replace.hpp> +#include <boost/range/adaptor/transformed.hpp> + +#include <memory> +#include <functional> + +using namespace std; +using namespace dev; +using namespace yul; +using namespace dev::solidity; + +//@TODO source locations + +string AsmPrinter::operator()(yul::Instruction const& _instruction) +{ + solAssert(!m_yul, ""); + solAssert(isValidInstruction(_instruction.instruction), "Invalid instruction"); + return boost::to_lower_copy(instructionInfo(_instruction.instruction).name); +} + +string AsmPrinter::operator()(Literal const& _literal) +{ + switch (_literal.kind) + { + case LiteralKind::Number: + solAssert(isValidDecimal(_literal.value.str()) || isValidHex(_literal.value.str()), "Invalid number literal"); + return _literal.value.str() + appendTypeName(_literal.type); + case LiteralKind::Boolean: + solAssert(_literal.value.str() == "true" || _literal.value.str() == "false", "Invalid bool literal."); + return ((_literal.value.str() == "true") ? "true" : "false") + appendTypeName(_literal.type); + case LiteralKind::String: + break; + } + + string out; + for (char c: _literal.value.str()) + if (c == '\\') + out += "\\\\"; + else if (c == '"') + out += "\\\""; + else if (c == '\b') + out += "\\b"; + else if (c == '\f') + out += "\\f"; + else if (c == '\n') + out += "\\n"; + else if (c == '\r') + out += "\\r"; + else if (c == '\t') + out += "\\t"; + else if (c == '\v') + out += "\\v"; + else if (!isprint(c, locale::classic())) + { + ostringstream o; + o << std::hex << setfill('0') << setw(2) << (unsigned)(unsigned char)(c); + out += "\\x" + o.str(); + } + else + out += c; + return "\"" + out + "\"" + appendTypeName(_literal.type); +} + +string AsmPrinter::operator()(Identifier const& _identifier) +{ + solAssert(!_identifier.name.empty(), "Invalid identifier."); + return _identifier.name.str(); +} + +string AsmPrinter::operator()(FunctionalInstruction const& _functionalInstruction) +{ + solAssert(!m_yul, ""); + solAssert(isValidInstruction(_functionalInstruction.instruction), "Invalid instruction"); + return + boost::to_lower_copy(instructionInfo(_functionalInstruction.instruction).name) + + "(" + + boost::algorithm::join( + _functionalInstruction.arguments | boost::adaptors::transformed(boost::apply_visitor(*this)), + ", ") + + ")"; +} + +string AsmPrinter::operator()(ExpressionStatement const& _statement) +{ + return boost::apply_visitor(*this, _statement.expression); +} + +string AsmPrinter::operator()(Label const& _label) +{ + solAssert(!m_yul, ""); + solAssert(!_label.name.empty(), "Invalid label."); + return _label.name.str() + ":"; +} + +string AsmPrinter::operator()(StackAssignment const& _assignment) +{ + solAssert(!m_yul, ""); + solAssert(!_assignment.variableName.name.empty(), "Invalid variable name."); + return "=: " + (*this)(_assignment.variableName); +} + +string AsmPrinter::operator()(Assignment const& _assignment) +{ + solAssert(_assignment.variableNames.size() >= 1, ""); + string variables = (*this)(_assignment.variableNames.front()); + for (size_t i = 1; i < _assignment.variableNames.size(); ++i) + variables += ", " + (*this)(_assignment.variableNames[i]); + return variables + " := " + boost::apply_visitor(*this, *_assignment.value); +} + +string AsmPrinter::operator()(VariableDeclaration const& _variableDeclaration) +{ + string out = "let "; + out += boost::algorithm::join( + _variableDeclaration.variables | boost::adaptors::transformed( + [this](TypedName argument) { return formatTypedName(argument); } + ), + ", " + ); + if (_variableDeclaration.value) + { + out += " := "; + out += boost::apply_visitor(*this, *_variableDeclaration.value); + } + return out; +} + +string AsmPrinter::operator()(FunctionDefinition const& _functionDefinition) +{ + solAssert(!_functionDefinition.name.empty(), "Invalid function name."); + string out = "function " + _functionDefinition.name.str() + "("; + out += boost::algorithm::join( + _functionDefinition.parameters | boost::adaptors::transformed( + [this](TypedName argument) { return formatTypedName(argument); } + ), + ", " + ); + out += ")"; + if (!_functionDefinition.returnVariables.empty()) + { + out += " -> "; + out += boost::algorithm::join( + _functionDefinition.returnVariables | boost::adaptors::transformed( + [this](TypedName argument) { return formatTypedName(argument); } + ), + ", " + ); + } + + return out + "\n" + (*this)(_functionDefinition.body); +} + +string AsmPrinter::operator()(FunctionCall const& _functionCall) +{ + return + (*this)(_functionCall.functionName) + "(" + + boost::algorithm::join( + _functionCall.arguments | boost::adaptors::transformed(boost::apply_visitor(*this)), + ", " ) + + ")"; +} + +string AsmPrinter::operator()(If const& _if) +{ + solAssert(_if.condition, "Invalid if condition."); + return "if " + boost::apply_visitor(*this, *_if.condition) + "\n" + (*this)(_if.body); +} + +string AsmPrinter::operator()(Switch const& _switch) +{ + solAssert(_switch.expression, "Invalid expression pointer."); + string out = "switch " + boost::apply_visitor(*this, *_switch.expression); + for (auto const& _case: _switch.cases) + { + if (!_case.value) + out += "\ndefault "; + else + out += "\ncase " + (*this)(*_case.value) + " "; + out += (*this)(_case.body); + } + return out; +} + +string AsmPrinter::operator()(ForLoop const& _forLoop) +{ + solAssert(_forLoop.condition, "Invalid for loop condition."); + string out = "for "; + out += (*this)(_forLoop.pre); + out += "\n"; + out += boost::apply_visitor(*this, *_forLoop.condition); + out += "\n"; + out += (*this)(_forLoop.post); + out += "\n"; + out += (*this)(_forLoop.body); + return out; +} + +string AsmPrinter::operator()(Block const& _block) +{ + if (_block.statements.empty()) + return "{\n}"; + string body = boost::algorithm::join( + _block.statements | boost::adaptors::transformed(boost::apply_visitor(*this)), + "\n" + ); + boost::replace_all(body, "\n", "\n "); + return "{\n " + body + "\n}"; +} + +string AsmPrinter::formatTypedName(TypedName _variable) const +{ + solAssert(!_variable.name.empty(), "Invalid variable name."); + return _variable.name.str() + appendTypeName(_variable.type); +} + +string AsmPrinter::appendTypeName(YulString _type) const +{ + if (m_yul) + return ":" + _type.str(); + return ""; +} diff --git a/libyul/AsmPrinter.h b/libyul/AsmPrinter.h new file mode 100644 index 00000000..61dfc18c --- /dev/null +++ b/libyul/AsmPrinter.h @@ -0,0 +1,62 @@ +/* + 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/>. +*/ +/** + * @author Christian <c@ethdev.com> + * @date 2017 + * Converts a parsed assembly into its textual form. + */ + +#pragma once + +#include <libyul/AsmDataForward.h> + +#include <libyul/YulString.h> + +#include <boost/variant.hpp> + +namespace yul +{ + +class AsmPrinter: public boost::static_visitor<std::string> +{ +public: + explicit AsmPrinter(bool _yul = false): m_yul(_yul) {} + + std::string operator()(Instruction const& _instruction); + std::string operator()(Literal const& _literal); + std::string operator()(Identifier const& _identifier); + std::string operator()(FunctionalInstruction const& _functionalInstruction); + std::string operator()(ExpressionStatement const& _expr); + std::string operator()(Label const& _label); + std::string operator()(StackAssignment const& _assignment); + std::string operator()(Assignment const& _assignment); + std::string operator()(VariableDeclaration const& _variableDeclaration); + std::string operator()(FunctionDefinition const& _functionDefinition); + std::string operator()(FunctionCall const& _functionCall); + std::string operator()(If const& _if); + std::string operator()(Switch const& _switch); + std::string operator()(ForLoop const& _forLoop); + std::string operator()(Block const& _block); + +private: + std::string formatTypedName(TypedName _variable) const; + std::string appendTypeName(YulString _type) const; + + bool m_yul = false; +}; + +} diff --git a/libyul/AsmScope.cpp b/libyul/AsmScope.cpp new file mode 100644 index 00000000..b71f2367 --- /dev/null +++ b/libyul/AsmScope.cpp @@ -0,0 +1,98 @@ +/* + 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/>. +*/ +/** + * Scopes for identifiers. + */ + +#include <libyul/AsmScope.h> + +using namespace std; +using namespace dev; +using namespace yul; + +bool Scope::registerLabel(YulString _name) +{ + if (exists(_name)) + return false; + identifiers[_name] = Label(); + return true; +} + +bool Scope::registerVariable(YulString _name, YulType const& _type) +{ + if (exists(_name)) + return false; + Variable variable; + variable.type = _type; + identifiers[_name] = variable; + return true; +} + +bool Scope::registerFunction(YulString _name, std::vector<YulType> const& _arguments, std::vector<YulType> const& _returns) +{ + if (exists(_name)) + return false; + identifiers[_name] = Function{_arguments, _returns}; + return true; +} + +Scope::Identifier* Scope::lookup(YulString _name) +{ + bool crossedFunctionBoundary = false; + for (Scope* s = this; s; s = s->superScope) + { + auto id = s->identifiers.find(_name); + if (id != s->identifiers.end()) + { + if (crossedFunctionBoundary && id->second.type() == typeid(Scope::Variable)) + return nullptr; + else + return &id->second; + } + + if (s->functionScope) + crossedFunctionBoundary = true; + } + return nullptr; +} + +bool Scope::exists(YulString _name) const +{ + if (identifiers.count(_name)) + return true; + else if (superScope) + return superScope->exists(_name); + else + return false; +} + +size_t Scope::numberOfVariables() const +{ + size_t count = 0; + for (auto const& identifier: identifiers) + if (identifier.second.type() == typeid(Scope::Variable)) + count++; + return count; +} + +bool Scope::insideFunction() const +{ + for (Scope const* s = this; s; s = s->superScope) + if (s->functionScope) + return true; + return false; +} diff --git a/libyul/AsmScope.h b/libyul/AsmScope.h new file mode 100644 index 00000000..2a8ef49e --- /dev/null +++ b/libyul/AsmScope.h @@ -0,0 +1,99 @@ +/* + 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/>. +*/ +/** + * Scopes for identifiers. + */ + +#pragma once + +#include <liblangutil/Exceptions.h> + +#include <libyul/YulString.h> + +#include <libdevcore/Visitor.h> + +#include <boost/variant.hpp> +#include <boost/optional.hpp> + +#include <functional> +#include <memory> + +namespace yul +{ + +struct Scope +{ + using YulType = YulString; + using LabelID = size_t; + + struct Variable { YulType type; }; + struct Label { }; + struct Function + { + std::vector<YulType> arguments; + std::vector<YulType> returns; + }; + + using Identifier = boost::variant<Variable, Label, Function>; + using Visitor = dev::GenericVisitor<Variable const, Label const, Function const>; + using NonconstVisitor = dev::GenericVisitor<Variable, Label, Function>; + + bool registerVariable(YulString _name, YulType const& _type); + bool registerLabel(YulString _name); + bool registerFunction( + YulString _name, + std::vector<YulType> const& _arguments, + std::vector<YulType> const& _returns + ); + + /// Looks up the identifier in this or super scopes and returns a valid pointer if found + /// or a nullptr if not found. Variable lookups up across function boundaries will fail, as + /// will any lookups across assembly boundaries. + /// The pointer will be invalidated if the scope is modified. + /// @param _crossedFunction if true, we already crossed a function boundary during recursive lookup + Identifier* lookup(YulString _name); + /// Looks up the identifier in this and super scopes (will not find variables across function + /// boundaries and generally stops at assembly boundaries) and calls the visitor, returns + /// false if not found. + template <class V> + bool lookup(YulString _name, V const& _visitor) + { + if (Identifier* id = lookup(_name)) + { + boost::apply_visitor(_visitor, *id); + return true; + } + else + return false; + } + /// @returns true if the name exists in this scope or in super scopes (also searches + /// across function and assembly boundaries). + bool exists(YulString _name) const; + + /// @returns the number of variables directly registered inside the scope. + size_t numberOfVariables() const; + /// @returns true if this scope is inside a function. + bool insideFunction() const; + + Scope* superScope = nullptr; + /// If true, variables from the super scope are not visible here (other identifiers are), + /// but they are still taken into account to prevent shadowing. + bool functionScope = false; + std::map<YulString, Identifier> identifiers; +}; + +} diff --git a/libyul/AsmScopeFiller.cpp b/libyul/AsmScopeFiller.cpp new file mode 100644 index 00000000..ee797d6a --- /dev/null +++ b/libyul/AsmScopeFiller.cpp @@ -0,0 +1,181 @@ +/* + 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/>. +*/ +/** + * Module responsible for registering identifiers inside their scopes. + */ + +#include <libyul/AsmScopeFiller.h> + +#include <libyul/AsmData.h> +#include <libyul/AsmScope.h> +#include <libyul/AsmAnalysisInfo.h> + +#include <liblangutil/ErrorReporter.h> +#include <liblangutil/Exceptions.h> + +#include <libdevcore/CommonData.h> + +#include <boost/range/adaptor/reversed.hpp> + +#include <memory> +#include <functional> + +using namespace std; +using namespace dev; +using namespace langutil; +using namespace yul; +using namespace dev::solidity; + +ScopeFiller::ScopeFiller(AsmAnalysisInfo& _info, ErrorReporter& _errorReporter): + m_info(_info), m_errorReporter(_errorReporter) +{ + m_currentScope = &scope(nullptr); +} + +bool ScopeFiller::operator()(ExpressionStatement const& _expr) +{ + return boost::apply_visitor(*this, _expr.expression); +} + +bool ScopeFiller::operator()(Label const& _item) +{ + if (!m_currentScope->registerLabel(_item.name)) + { + //@TODO secondary location + m_errorReporter.declarationError( + _item.location, + "Label name " + _item.name.str() + " already taken in this scope." + ); + return false; + } + return true; +} + +bool ScopeFiller::operator()(VariableDeclaration const& _varDecl) +{ + for (auto const& variable: _varDecl.variables) + if (!registerVariable(variable, _varDecl.location, *m_currentScope)) + return false; + return true; +} + +bool ScopeFiller::operator()(FunctionDefinition const& _funDef) +{ + bool success = true; + vector<Scope::YulType> arguments; + for (auto const& _argument: _funDef.parameters) + arguments.emplace_back(_argument.type.str()); + vector<Scope::YulType> returns; + for (auto const& _return: _funDef.returnVariables) + returns.emplace_back(_return.type.str()); + if (!m_currentScope->registerFunction(_funDef.name, arguments, returns)) + { + //@TODO secondary location + m_errorReporter.declarationError( + _funDef.location, + "Function name " + _funDef.name.str() + " already taken in this scope." + ); + success = false; + } + + auto virtualBlock = m_info.virtualBlocks[&_funDef] = make_shared<Block>(); + Scope& varScope = scope(virtualBlock.get()); + varScope.superScope = m_currentScope; + m_currentScope = &varScope; + varScope.functionScope = true; + for (auto const& var: _funDef.parameters + _funDef.returnVariables) + if (!registerVariable(var, _funDef.location, varScope)) + success = false; + + if (!(*this)(_funDef.body)) + success = false; + + solAssert(m_currentScope == &varScope, ""); + m_currentScope = m_currentScope->superScope; + + return success; +} + +bool ScopeFiller::operator()(If const& _if) +{ + return (*this)(_if.body); +} + +bool ScopeFiller::operator()(Switch const& _switch) +{ + bool success = true; + for (auto const& _case: _switch.cases) + if (!(*this)(_case.body)) + success = false; + return success; +} + +bool ScopeFiller::operator()(ForLoop const& _forLoop) +{ + Scope* originalScope = m_currentScope; + + bool success = true; + if (!(*this)(_forLoop.pre)) + success = false; + m_currentScope = &scope(&_forLoop.pre); + if (!boost::apply_visitor(*this, *_forLoop.condition)) + success = false; + if (!(*this)(_forLoop.body)) + success = false; + if (!(*this)(_forLoop.post)) + success = false; + + m_currentScope = originalScope; + + return success; +} + +bool ScopeFiller::operator()(Block const& _block) +{ + bool success = true; + scope(&_block).superScope = m_currentScope; + m_currentScope = &scope(&_block); + + for (auto const& s: _block.statements) + if (!boost::apply_visitor(*this, s)) + success = false; + + m_currentScope = m_currentScope->superScope; + return success; +} + +bool ScopeFiller::registerVariable(TypedName const& _name, SourceLocation const& _location, Scope& _scope) +{ + if (!_scope.registerVariable(_name.name, _name.type)) + { + //@TODO secondary location + m_errorReporter.declarationError( + _location, + "Variable name " + _name.name.str() + " already taken in this scope." + ); + return false; + } + return true; +} + +Scope& ScopeFiller::scope(Block const* _block) +{ + auto& scope = m_info.scopes[_block]; + if (!scope) + scope = make_shared<Scope>(); + return *scope; +} diff --git a/libyul/AsmScopeFiller.h b/libyul/AsmScopeFiller.h new file mode 100644 index 00000000..e8fb88d5 --- /dev/null +++ b/libyul/AsmScopeFiller.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/>. +*/ +/** + * Module responsible for registering identifiers inside their scopes. + */ + +#pragma once + +#include <libyul/AsmDataForward.h> + +#include <boost/variant.hpp> + +#include <functional> +#include <memory> + +namespace langutil +{ +class ErrorReporter; +struct SourceLocation; +} + +namespace yul +{ + +struct TypedName; +struct Scope; +struct AsmAnalysisInfo; + +/** + * Fills scopes with identifiers and checks for name clashes. + * Does not resolve references. + */ +class ScopeFiller: public boost::static_visitor<bool> +{ +public: + ScopeFiller(AsmAnalysisInfo& _info, langutil::ErrorReporter& _errorReporter); + + bool operator()(Instruction const&) { return true; } + bool operator()(Literal const&) { return true; } + bool operator()(Identifier const&) { return true; } + bool operator()(FunctionalInstruction const&) { return true; } + bool operator()(ExpressionStatement const& _expr); + bool operator()(Label const& _label); + bool operator()(StackAssignment const&) { return true; } + bool operator()(Assignment const&) { return true; } + bool operator()(VariableDeclaration const& _variableDeclaration); + bool operator()(FunctionDefinition const& _functionDefinition); + bool operator()(FunctionCall const&) { return true; } + bool operator()(If const& _if); + bool operator()(Switch const& _switch); + bool operator()(ForLoop const& _forLoop); + bool operator()(Block const& _block); + +private: + bool registerVariable( + TypedName const& _name, + langutil::SourceLocation const& _location, + Scope& _scope + ); + + Scope& scope(Block const* _block); + + Scope* m_currentScope = nullptr; + AsmAnalysisInfo& m_info; + langutil::ErrorReporter& m_errorReporter; +}; + +} diff --git a/libyul/CMakeLists.txt b/libyul/CMakeLists.txt new file mode 100644 index 00000000..7ed84ff5 --- /dev/null +++ b/libyul/CMakeLists.txt @@ -0,0 +1,45 @@ +add_library(yul + AsmAnalysis.cpp + AsmAnalysisInfo.cpp + AsmCodeGen.cpp + AsmParser.cpp + AsmPrinter.cpp + AsmScope.cpp + AsmScopeFiller.cpp + Object.cpp + ObjectParser.cpp + backends/evm/EVMAssembly.cpp + backends/evm/EVMCodeTransform.cpp + optimiser/ASTCopier.cpp + optimiser/ASTWalker.cpp + optimiser/BlockFlattener.cpp + optimiser/CommonSubexpressionEliminator.cpp + optimiser/DataFlowAnalyzer.cpp + optimiser/Disambiguator.cpp + optimiser/ExpressionInliner.cpp + optimiser/ExpressionJoiner.cpp + optimiser/ExpressionSimplifier.cpp + optimiser/ExpressionSplitter.cpp + optimiser/ForLoopInitRewriter.cpp + optimiser/FullInliner.cpp + optimiser/FunctionGrouper.cpp + optimiser/FunctionHoister.cpp + optimiser/InlinableExpressionFunctionFinder.cpp + optimiser/MainFunction.cpp + optimiser/Metrics.cpp + optimiser/NameCollector.cpp + optimiser/NameDispenser.cpp + optimiser/RedundantAssignEliminator.cpp + optimiser/Rematerialiser.cpp + optimiser/SSATransform.cpp + optimiser/SSAValueTracker.cpp + optimiser/Semantics.cpp + optimiser/SimplificationRules.cpp + optimiser/Substitution.cpp + optimiser/Suite.cpp + optimiser/SyntacticalEquality.cpp + optimiser/UnusedPruner.cpp + optimiser/Utilities.cpp + optimiser/VarDeclPropagator.cpp +) +target_link_libraries(yul PUBLIC devcore) diff --git a/libyul/Exceptions.h b/libyul/Exceptions.h index 0c421dbf..e10e53ef 100644 --- a/libyul/Exceptions.h +++ b/libyul/Exceptions.h @@ -23,18 +23,15 @@ #include <libdevcore/Exceptions.h> #include <libdevcore/Assertions.h> -namespace dev -{ namespace yul { -struct YulException: virtual Exception {}; +struct YulException: virtual dev::Exception {}; struct OptimizerException: virtual YulException {}; struct YulAssertion: virtual YulException {}; /// Assertion that throws an YulAssertion containing the given description if it is not met. #define yulAssert(CONDITION, DESCRIPTION) \ - assertThrow(CONDITION, ::dev::yul::YulException, DESCRIPTION) + assertThrow(CONDITION, ::yul::YulException, DESCRIPTION) } -} diff --git a/libyul/Object.cpp b/libyul/Object.cpp new file mode 100644 index 00000000..a5228793 --- /dev/null +++ b/libyul/Object.cpp @@ -0,0 +1,61 @@ +/* + 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 code and data object container. + */ + +#include <libyul/Object.h> + +#include <libyul/AsmPrinter.h> +#include <libyul/Exceptions.h> + +#include <libdevcore/Visitor.h> +#include <libdevcore/CommonData.h> + +#include <boost/algorithm/string/replace.hpp> + +using namespace dev; +using namespace yul; +using namespace std; + +namespace +{ + +string indent(std::string const& _input) +{ + if (_input.empty()) + return _input; + return boost::replace_all_copy(" " + _input, "\n", "\n "); +} + +} + +string Data::toString(bool) const +{ + return "data \"" + name.str() + "\" hex\"" + dev::toHex(data) + "\""; +} + +string Object::toString(bool _yul) const +{ + yulAssert(code, "No code"); + string inner = "code " + AsmPrinter{_yul}(*code); + + for (auto const& obj: subObjects) + inner += "\n" + obj->toString(_yul); + + return "object \"" + name.str() + "\" {\n" + indent(inner) + "\n}"; +} diff --git a/libyul/Object.h b/libyul/Object.h new file mode 100644 index 00000000..cfd8d02d --- /dev/null +++ b/libyul/Object.h @@ -0,0 +1,72 @@ +/* + 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 code and data object container. + */ + +#pragma once + +#include <libyul/AsmDataForward.h> +#include <libyul/YulString.h> + +#include <libdevcore/Common.h> + +#include <memory> + +namespace yul +{ +struct AsmAnalysisInfo; + + +/** + * Generic base class for both Yul objects and Yul data. + */ +struct ObjectNode +{ + virtual ~ObjectNode() {} + virtual std::string toString(bool _yul) const = 0; + + YulString name; +}; + +/** + * Named data in Yul objects. + */ +struct Data: ObjectNode +{ + Data(YulString _name, dev::bytes _data): data(std::move(_data)) { name = _name; } + std::string toString(bool _yul) const override; + + dev::bytes data; +}; + +/** + * Yul code and data object container. + */ +struct Object: ObjectNode +{ +public: + /// @returns a (parseable) string representation. Includes types if @a _yul is set. + std::string toString(bool _yul) const override; + + std::shared_ptr<Block> code; + std::vector<std::shared_ptr<ObjectNode>> subObjects; + std::map<YulString, size_t> subIndexByName; + std::shared_ptr<yul::AsmAnalysisInfo> analysisInfo; +}; + +} diff --git a/libyul/ObjectParser.cpp b/libyul/ObjectParser.cpp new file mode 100644 index 00000000..43dd4be9 --- /dev/null +++ b/libyul/ObjectParser.cpp @@ -0,0 +1,146 @@ +/* + 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/>. +*/ +/** + * Parser for Yul code and data object container. + */ + +#include <libyul/ObjectParser.h> + +#include <libyul/AsmParser.h> +#include <libyul/Exceptions.h> + +#include <liblangutil/Token.h> + +using namespace dev; +using namespace langutil; +using namespace yul; +using namespace std; + + +shared_ptr<Object> ObjectParser::parse(shared_ptr<Scanner> const& _scanner, bool _reuseScanner) +{ + m_recursionDepth = 0; + try + { + shared_ptr<Object> object; + m_scanner = _scanner; + if (currentToken() == Token::LBrace) + { + // Special case: Code-only form. + object = make_shared<Object>(); + object->name = YulString{"object"}; + object->code = parseBlock(); + if (!object->code) + return nullptr; + } + else + object = parseObject(); + if (object && !_reuseScanner) + expectToken(Token::EOS); + return object; + } + catch (FatalError const&) + { + if (m_errorReporter.errors().empty()) + throw; // Something is weird here, rather throw again. + } + return nullptr; +} + +shared_ptr<Object> ObjectParser::parseObject(Object* _containingObject) +{ + RecursionGuard guard(*this); + + if (currentToken() != Token::Identifier || currentLiteral() != "object") + fatalParserError("Expected keyword \"object\"."); + advance(); + + shared_ptr<Object> ret = make_shared<Object>(); + ret->name = parseUniqueName(_containingObject); + + expectToken(Token::LBrace); + + ret->code = parseCode(); + + while (currentToken() != Token::RBrace) + { + if (currentToken() == Token::Identifier && currentLiteral() == "object") + parseObject(ret.get()); + else if (currentToken() == Token::Identifier && currentLiteral() == "data") + parseData(*ret); + else + fatalParserError("Expected keyword \"data\" or \"object\" or \"}\"."); + } + if (_containingObject) + addNamedSubObject(*_containingObject, ret->name, ret); + + expectToken(Token::RBrace); + + return ret; +} + +shared_ptr<Block> ObjectParser::parseCode() +{ + if (currentToken() != Token::Identifier || currentLiteral() != "code") + fatalParserError("Expected keyword \"code\"."); + advance(); + + return parseBlock(); +} + +shared_ptr<Block> ObjectParser::parseBlock() +{ + Parser parser(m_errorReporter, m_flavour); + shared_ptr<Block> block = parser.parse(m_scanner, true); + yulAssert(block || m_errorReporter.hasErrors(), "Invalid block but no error!"); + return block; +} + +void ObjectParser::parseData(Object& _containingObject) +{ + solAssert( + currentToken() == Token::Identifier && currentLiteral() == "data", + "parseData called on wrong input." + ); + advance(); + + YulString name = parseUniqueName(&_containingObject); + + expectToken(Token::StringLiteral, false); + addNamedSubObject(_containingObject, name, make_shared<Data>(name, asBytes(currentLiteral()))); + advance(); +} + +YulString ObjectParser::parseUniqueName(Object const* _containingObject) +{ + expectToken(Token::StringLiteral, false); + YulString name{currentLiteral()}; + if (name.empty()) + parserError("Object name cannot be empty."); + else if (_containingObject && _containingObject->name == name) + parserError("Object name cannot be the same as the name of the containing object."); + else if (_containingObject && _containingObject->subIndexByName.count(name)) + parserError("Object name \"" + name.str() + "\" already exists inside the containing object."); + advance(); + return name; +} + +void ObjectParser::addNamedSubObject(Object& _container, YulString _name, shared_ptr<ObjectNode> _subObject) +{ + _container.subIndexByName[_name] = _container.subObjects.size(); + _container.subObjects.emplace_back(std::move(_subObject)); +} diff --git a/libyul/ObjectParser.h b/libyul/ObjectParser.h new file mode 100644 index 00000000..1d88a119 --- /dev/null +++ b/libyul/ObjectParser.h @@ -0,0 +1,72 @@ +/* + 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/>. +*/ +/** + * Parser for Yul code and data object container. + */ + +#pragma once + +#include <libyul/YulString.h> +#include <libyul/Object.h> + +#include <liblangutil/ErrorReporter.h> +#include <liblangutil/ParserBase.h> + +#include <libdevcore/Common.h> + +#include <memory> + +namespace langutil +{ +class Scanner; +} + +namespace yul +{ + +/** + * Yul object parser. Invokes the inline assembly parser. + */ +class ObjectParser: public langutil::ParserBase +{ +public: + explicit ObjectParser( + langutil::ErrorReporter& _errorReporter, + yul::AsmFlavour _flavour = yul::AsmFlavour::Loose + ): + ParserBase(_errorReporter), m_flavour(_flavour) {} + + /// Parses a Yul object. + /// Falls back to code-only parsing if the source starts with `{`. + /// @param _reuseScanner if true, do check for end of input after the last `}`. + /// @returns an empty shared pointer on error. + std::shared_ptr<Object> parse(std::shared_ptr<langutil::Scanner> const& _scanner, bool _reuseScanner); + +private: + std::shared_ptr<Object> parseObject(Object* _containingObject = nullptr); + std::shared_ptr<Block> parseCode(); + std::shared_ptr<Block> parseBlock(); + void parseData(Object& _containingObject); + + /// Tries to parse a name that is non-empty and unique inside the containing object. + YulString parseUniqueName(Object const* _containingObject); + void addNamedSubObject(Object& _container, YulString _name, std::shared_ptr<ObjectNode> _subObject); + + yul::AsmFlavour m_flavour; +}; + +} diff --git a/libyul/YulString.h b/libyul/YulString.h index ad900a70..2179c23b 100644 --- a/libyul/YulString.h +++ b/libyul/YulString.h @@ -27,8 +27,6 @@ #include <vector> #include <string> -namespace dev -{ namespace yul { @@ -130,4 +128,3 @@ private: }; } -} diff --git a/libyul/backends/evm/AbstractAssembly.h b/libyul/backends/evm/AbstractAssembly.h index d75058f7..97b1d305 100644 --- a/libyul/backends/evm/AbstractAssembly.h +++ b/libyul/backends/evm/AbstractAssembly.h @@ -22,24 +22,28 @@ #pragma once +#include <libdevcore/Common.h> #include <libdevcore/CommonData.h> #include <functional> -namespace dev +namespace langutil { struct SourceLocation; +} + +namespace dev +{ namespace solidity { enum class Instruction: uint8_t; -namespace assembly -{ -struct Instruction; -struct Identifier; } } + namespace yul { +struct Instruction; +struct Identifier; /// /// Assembly class that abstracts both the libevmasm assembly and the new Yul assembly. @@ -52,14 +56,14 @@ public: virtual ~AbstractAssembly() {} /// Set a new source location valid starting from the next instruction. - virtual void setSourceLocation(SourceLocation const& _location) = 0; + virtual void setSourceLocation(langutil::SourceLocation const& _location) = 0; /// Retrieve the current height of the stack. This does not have to be zero /// at the beginning. virtual int stackHeight() const = 0; /// Append an EVM instruction. - virtual void appendInstruction(solidity::Instruction _instruction) = 0; + virtual void appendInstruction(dev::solidity::Instruction _instruction) = 0; /// Append a constant. - virtual void appendConstant(u256 const& _constant) = 0; + virtual void appendConstant(dev::u256 const& _constant) = 0; /// Append a label. virtual void appendLabel(LabelID _labelId) = 0; /// Append a label reference. @@ -102,18 +106,15 @@ enum class IdentifierContext { LValue, RValue }; /// to inline assembly (not used in standalone assembly mode). struct ExternalIdentifierAccess { - using Resolver = std::function<size_t(solidity::assembly::Identifier const&, IdentifierContext, bool /*_crossesFunctionBoundary*/)>; + using Resolver = std::function<size_t(Identifier const&, IdentifierContext, bool /*_crossesFunctionBoundary*/)>; /// Resolve an external reference given by the identifier in the given context. /// @returns the size of the value (number of stack slots) or size_t(-1) if not found. Resolver resolve; - using CodeGenerator = std::function<void(solidity::assembly::Identifier const&, IdentifierContext, yul::AbstractAssembly&)>; + using CodeGenerator = std::function<void(Identifier const&, IdentifierContext, yul::AbstractAssembly&)>; /// Generate code for retrieving the value (rvalue context) or storing the value (lvalue context) /// of an identifier. The code should be appended to the assembly. In rvalue context, the value is supposed /// to be put onto the stack, in lvalue context, the value is assumed to be at the top of the stack. CodeGenerator generateCode; }; - - -} } diff --git a/libyul/backends/evm/EVMAssembly.cpp b/libyul/backends/evm/EVMAssembly.cpp index b37a3231..99506317 100644 --- a/libyul/backends/evm/EVMAssembly.cpp +++ b/libyul/backends/evm/EVMAssembly.cpp @@ -22,11 +22,12 @@ #include <libevmasm/Instruction.h> -#include <libsolidity/interface/Exceptions.h> +#include <liblangutil/Exceptions.h> using namespace std; using namespace dev; -using namespace dev::yul; +using namespace langutil; +using namespace yul; namespace { diff --git a/libyul/backends/evm/EVMAssembly.h b/libyul/backends/evm/EVMAssembly.h index 556ed5a5..d0a437cc 100644 --- a/libyul/backends/evm/EVMAssembly.h +++ b/libyul/backends/evm/EVMAssembly.h @@ -26,8 +26,11 @@ #include <map> -namespace dev +namespace langutil { +struct SourceLocation; +} + namespace yul { @@ -38,55 +41,55 @@ public: virtual ~EVMAssembly() {} /// Set a new source location valid starting from the next instruction. - virtual void setSourceLocation(SourceLocation const& _location) override; + void setSourceLocation(langutil::SourceLocation const& _location) override; /// Retrieve the current height of the stack. This does not have to be zero /// at the beginning. - virtual int stackHeight() const override { return m_stackHeight; } + int stackHeight() const override { return m_stackHeight; } /// Append an EVM instruction. - virtual void appendInstruction(solidity::Instruction _instruction) override; + void appendInstruction(dev::solidity::Instruction _instruction) override; /// Append a constant. - virtual void appendConstant(u256 const& _constant) override; + void appendConstant(dev::u256 const& _constant) override; /// Append a label. - virtual void appendLabel(LabelID _labelId) override; + void appendLabel(LabelID _labelId) override; /// Append a label reference. - virtual void appendLabelReference(LabelID _labelId) override; + void appendLabelReference(LabelID _labelId) override; /// Generate a new unique label. - virtual LabelID newLabelId() override; + LabelID newLabelId() override; /// Returns a label identified by the given name. Creates it if it does not yet exist. - virtual LabelID namedLabel(std::string const& _name) override; + LabelID namedLabel(std::string const& _name) override; /// Append a reference to a to-be-linked symbol. /// Currently, we assume that the value is always a 20 byte number. - virtual void appendLinkerSymbol(std::string const& _name) override; + void appendLinkerSymbol(std::string const& _name) override; /// Append a jump instruction. /// @param _stackDiffAfter the stack adjustment after this instruction. - virtual void appendJump(int _stackDiffAfter) override; + void appendJump(int _stackDiffAfter) override; /// Append a jump-to-immediate operation. - virtual void appendJumpTo(LabelID _labelId, int _stackDiffAfter) override; + void appendJumpTo(LabelID _labelId, int _stackDiffAfter) override; /// Append a jump-to-if-immediate operation. - virtual void appendJumpToIf(LabelID _labelId) override; + void appendJumpToIf(LabelID _labelId) override; /// Start a subroutine. - virtual void appendBeginsub(LabelID _labelId, int _arguments) override; + void appendBeginsub(LabelID _labelId, int _arguments) override; /// Call a subroutine. - virtual void appendJumpsub(LabelID _labelId, int _arguments, int _returns) override; + void appendJumpsub(LabelID _labelId, int _arguments, int _returns) override; /// Return from a subroutine. - virtual void appendReturnsub(int _returns, int _stackDiffAfter) override; + void appendReturnsub(int _returns, int _stackDiffAfter) override; /// Append the assembled size as a constant. - virtual void appendAssemblySize() override; + void appendAssemblySize() override; /// Resolves references inside the bytecode and returns the linker object. - eth::LinkerObject finalize(); + dev::eth::LinkerObject finalize(); private: void setLabelToCurrentPosition(AbstractAssembly::LabelID _labelId); void appendLabelReferenceInternal(AbstractAssembly::LabelID _labelId); - void updateReference(size_t pos, size_t size, u256 value); + void updateReference(size_t pos, size_t size, dev::u256 value); bool m_evm15 = false; ///< if true, switch to evm1.5 mode LabelID m_nextLabelId = 0; int m_stackHeight = 0; - bytes m_bytecode; + dev::bytes m_bytecode; std::map<std::string, LabelID> m_namedLabels; std::map<LabelID, size_t> m_labelPositions; std::map<size_t, LabelID> m_labelReferences; @@ -94,4 +97,3 @@ private: }; } -} diff --git a/libyul/backends/evm/EVMCodeTransform.cpp b/libyul/backends/evm/EVMCodeTransform.cpp index 650a8c0a..12abd754 100644 --- a/libyul/backends/evm/EVMCodeTransform.cpp +++ b/libyul/backends/evm/EVMCodeTransform.cpp @@ -20,20 +20,18 @@ #include <libyul/backends/evm/EVMCodeTransform.h> -#include <libsolidity/inlineasm/AsmAnalysisInfo.h> -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmAnalysisInfo.h> +#include <libyul/AsmData.h> -#include <libsolidity/interface/Exceptions.h> +#include <liblangutil/Exceptions.h> #include <boost/range/adaptor/reversed.hpp> using namespace std; using namespace dev; -using namespace dev::yul; +using namespace yul; using namespace dev::solidity; -using Scope = dev::solidity::assembly::Scope; - void CodeTransform::operator()(VariableDeclaration const& _varDecl) { solAssert(m_scope, ""); @@ -147,7 +145,7 @@ void CodeTransform::operator()(FunctionalInstruction const& _instruction) solAssert(_instruction.arguments.size() == 1, ""); } m_assembly.setSourceLocation(_instruction.location); - auto label = labelFromIdentifier(boost::get<assembly::Identifier>(_instruction.arguments.at(0))); + auto label = labelFromIdentifier(boost::get<Identifier>(_instruction.arguments.at(0))); if (isJumpI) m_assembly.appendJumpToIf(label); else @@ -163,7 +161,7 @@ void CodeTransform::operator()(FunctionalInstruction const& _instruction) checkStackHeight(&_instruction); } -void CodeTransform::operator()(assembly::Identifier const& _identifier) +void CodeTransform::operator()(Identifier const& _identifier) { m_assembly.setSourceLocation(_identifier.location); // First search internals, then externals. @@ -197,12 +195,12 @@ void CodeTransform::operator()(assembly::Identifier const& _identifier) checkStackHeight(&_identifier); } -void CodeTransform::operator()(assembly::Literal const& _literal) +void CodeTransform::operator()(Literal const& _literal) { m_assembly.setSourceLocation(_literal.location); - if (_literal.kind == assembly::LiteralKind::Number) + if (_literal.kind == LiteralKind::Number) m_assembly.appendConstant(u256(_literal.value.str())); - else if (_literal.kind == assembly::LiteralKind::Boolean) + else if (_literal.kind == LiteralKind::Boolean) { if (_literal.value.str() == "true") m_assembly.appendConstant(u256(1)); @@ -217,7 +215,7 @@ void CodeTransform::operator()(assembly::Literal const& _literal) checkStackHeight(&_literal); } -void CodeTransform::operator()(assembly::Instruction const& _instruction) +void CodeTransform::operator()(yul::Instruction const& _instruction) { solAssert(!m_evm15 || _instruction.instruction != solidity::Instruction::JUMP, "Bare JUMP instruction used for EVM1.5"); solAssert(!m_evm15 || _instruction.instruction != solidity::Instruction::JUMPI, "Bare JUMPI instruction used for EVM1.5"); @@ -522,7 +520,7 @@ void CodeTransform::generateAssignment(Identifier const& _variableName) } } -int CodeTransform::variableHeightDiff(solidity::assembly::Scope::Variable const& _var, bool _forSwap) const +int CodeTransform::variableHeightDiff(Scope::Variable const& _var, bool _forSwap) const { solAssert(m_context->variableStackHeights.count(&_var), ""); int heightDiff = m_assembly.stackHeight() - m_context->variableStackHeights[&_var]; diff --git a/libyul/backends/evm/EVMCodeTransform.h b/libyul/backends/evm/EVMCodeTransform.h index c0de8ad6..d559f85a 100644 --- a/libyul/backends/evm/EVMCodeTransform.h +++ b/libyul/backends/evm/EVMCodeTransform.h @@ -20,25 +20,21 @@ #include <libyul/backends/evm/EVMAssembly.h> -#include <libyul/ASTDataForward.h> +#include <libyul/AsmDataForward.h> -#include <libsolidity/inlineasm/AsmScope.h> +#include <libyul/AsmScope.h> #include <boost/variant.hpp> #include <boost/optional.hpp> -namespace dev -{ -namespace solidity +namespace langutil { class ErrorReporter; -namespace assembly -{ -struct AsmAnalysisInfo; -} } + namespace yul { +struct AsmAnalysisInfo; class EVMAssembly; class CodeTransform: public boost::static_visitor<> @@ -47,8 +43,8 @@ public: /// Create the code transformer. /// @param _identifierAccess used to resolve identifiers external to the inline assembly CodeTransform( - yul::AbstractAssembly& _assembly, - solidity::assembly::AsmAnalysisInfo& _analysisInfo, + AbstractAssembly& _assembly, + AsmAnalysisInfo& _analysisInfo, bool _yul = false, bool _evm15 = false, ExternalIdentifierAccess const& _identifierAccess = ExternalIdentifierAccess(), @@ -69,15 +65,14 @@ public: protected: struct Context { - using Scope = solidity::assembly::Scope; std::map<Scope::Label const*, AbstractAssembly::LabelID> labelIDs; std::map<Scope::Function const*, AbstractAssembly::LabelID> functionEntryIDs; std::map<Scope::Variable const*, int> variableStackHeights; }; CodeTransform( - yul::AbstractAssembly& _assembly, - solidity::assembly::AsmAnalysisInfo& _analysisInfo, + AbstractAssembly& _assembly, + AsmAnalysisInfo& _analysisInfo, bool _yul, bool _evm15, ExternalIdentifierAccess const& _identifierAccess, @@ -116,8 +111,8 @@ private: AbstractAssembly::LabelID labelFromIdentifier(Identifier const& _identifier); /// @returns the label ID corresponding to the given label, allocating a new one if /// necessary. - AbstractAssembly::LabelID labelID(solidity::assembly::Scope::Label const& _label); - AbstractAssembly::LabelID functionEntryID(YulString _name, solidity::assembly::Scope::Function const& _function); + AbstractAssembly::LabelID labelID(Scope::Label const& _label); + AbstractAssembly::LabelID functionEntryID(YulString _name, Scope::Function const& _function); /// Generates code for an expression that is supposed to return a single value. void visitExpression(Expression const& _expression); @@ -133,15 +128,15 @@ private: /// Determines the stack height difference to the given variables. Throws /// if it is not yet in scope or the height difference is too large. Returns /// the (positive) stack height difference otherwise. - int variableHeightDiff(solidity::assembly::Scope::Variable const& _var, bool _forSwap) const; + int variableHeightDiff(Scope::Variable const& _var, bool _forSwap) const; void expectDeposit(int _deposit, int _oldHeight) const; void checkStackHeight(void const* _astElement) const; - yul::AbstractAssembly& m_assembly; - solidity::assembly::AsmAnalysisInfo& m_info; - solidity::assembly::Scope* m_scope = nullptr; + AbstractAssembly& m_assembly; + AsmAnalysisInfo& m_info; + Scope* m_scope = nullptr; bool m_yul = false; bool m_evm15 = false; bool m_useNamedLabelsForFunctions = false; @@ -155,4 +150,3 @@ private: }; } -} diff --git a/libyul/optimiser/ASTCopier.cpp b/libyul/optimiser/ASTCopier.cpp index d0c8dd45..f18b0e6b 100644 --- a/libyul/optimiser/ASTCopier.cpp +++ b/libyul/optimiser/ASTCopier.cpp @@ -22,13 +22,13 @@ #include <libyul/Exceptions.h> -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmData.h> #include <libdevcore/Common.h> using namespace std; using namespace dev; -using namespace dev::yul; +using namespace yul; Statement ASTCopier::operator()(Instruction const&) { diff --git a/libyul/optimiser/ASTCopier.h b/libyul/optimiser/ASTCopier.h index b6aceee3..4d2f18ae 100644 --- a/libyul/optimiser/ASTCopier.h +++ b/libyul/optimiser/ASTCopier.h @@ -20,7 +20,7 @@ #pragma once -#include <libyul/ASTDataForward.h> +#include <libyul/AsmDataForward.h> #include <libyul/YulString.h> @@ -31,8 +31,6 @@ #include <set> #include <memory> -namespace dev -{ namespace yul { @@ -71,21 +69,21 @@ class ASTCopier: public ExpressionCopier, public StatementCopier { public: virtual ~ASTCopier() = default; - virtual Expression operator()(Literal const& _literal) override; - virtual Statement operator()(Instruction const& _instruction) override; - virtual Expression operator()(Identifier const& _identifier) override; - virtual Expression operator()(FunctionalInstruction const& _instr) override; - virtual Expression operator()(FunctionCall const&) override; - virtual Statement operator()(ExpressionStatement const& _statement) override; - virtual Statement operator()(Label const& _label) override; - virtual Statement operator()(StackAssignment const& _assignment) override; - virtual Statement operator()(Assignment const& _assignment) override; - virtual Statement operator()(VariableDeclaration const& _varDecl) override; - virtual Statement operator()(If const& _if) override; - virtual Statement operator()(Switch const& _switch) override; - virtual Statement operator()(FunctionDefinition const&) override; - virtual Statement operator()(ForLoop const&) override; - virtual Statement operator()(Block const& _block) override; + Expression operator()(Literal const& _literal) override; + Statement operator()(Instruction const& _instruction) override; + Expression operator()(Identifier const& _identifier) override; + Expression operator()(FunctionalInstruction const& _instr) override; + Expression operator()(FunctionCall const&) override; + Statement operator()(ExpressionStatement const& _statement) override; + Statement operator()(Label const& _label) override; + Statement operator()(StackAssignment const& _assignment) override; + Statement operator()(Assignment const& _assignment) override; + Statement operator()(VariableDeclaration const& _varDecl) override; + Statement operator()(If const& _if) override; + Statement operator()(Switch const& _switch) override; + Statement operator()(FunctionDefinition const&) override; + Statement operator()(ForLoop const&) override; + Statement operator()(Block const& _block) override; virtual Expression translate(Expression const& _expression); virtual Statement translate(Statement const& _statement); @@ -123,4 +121,3 @@ std::vector<T> ASTCopier::translateVector(std::vector<T> const& _values) } -} diff --git a/libyul/optimiser/ASTWalker.cpp b/libyul/optimiser/ASTWalker.cpp index e29dda6b..0d568007 100644 --- a/libyul/optimiser/ASTWalker.cpp +++ b/libyul/optimiser/ASTWalker.cpp @@ -20,13 +20,13 @@ #include <libyul/optimiser/ASTWalker.h> -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmData.h> #include <boost/range/adaptor/reversed.hpp> using namespace std; using namespace dev; -using namespace dev::yul; +using namespace yul; using namespace dev::solidity; diff --git a/libyul/optimiser/ASTWalker.h b/libyul/optimiser/ASTWalker.h index 38cb85ea..b59b405e 100644 --- a/libyul/optimiser/ASTWalker.h +++ b/libyul/optimiser/ASTWalker.h @@ -20,7 +20,7 @@ #pragma once -#include <libyul/ASTDataForward.h> +#include <libyul/AsmDataForward.h> #include <libyul/Exceptions.h> #include <libyul/YulString.h> @@ -32,8 +32,6 @@ #include <set> #include <map> -namespace dev -{ namespace yul { @@ -120,4 +118,3 @@ protected: }; } -} diff --git a/libyul/optimiser/BlockFlattener.cpp b/libyul/optimiser/BlockFlattener.cpp index 04f3ad7f..e6f08524 100644 --- a/libyul/optimiser/BlockFlattener.cpp +++ b/libyul/optimiser/BlockFlattener.cpp @@ -15,14 +15,14 @@ along with solidity. If not, see <http://www.gnu.org/licenses/>. */ #include <libyul/optimiser/BlockFlattener.h> -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmData.h> #include <libdevcore/Visitor.h> #include <libdevcore/CommonData.h> #include <functional> using namespace std; using namespace dev; -using namespace dev::yul; +using namespace yul; void BlockFlattener::operator()(Block& _block) { diff --git a/libyul/optimiser/BlockFlattener.h b/libyul/optimiser/BlockFlattener.h index 88c49dda..b732422d 100644 --- a/libyul/optimiser/BlockFlattener.h +++ b/libyul/optimiser/BlockFlattener.h @@ -18,8 +18,6 @@ #include <libyul/optimiser/ASTWalker.h> -namespace dev -{ namespace yul { @@ -31,4 +29,3 @@ public: }; } -} diff --git a/libyul/optimiser/CommonSubexpressionEliminator.cpp b/libyul/optimiser/CommonSubexpressionEliminator.cpp index 64605362..9b851333 100644 --- a/libyul/optimiser/CommonSubexpressionEliminator.cpp +++ b/libyul/optimiser/CommonSubexpressionEliminator.cpp @@ -24,12 +24,11 @@ #include <libyul/optimiser/Metrics.h> #include <libyul/optimiser/SyntacticalEquality.h> #include <libyul/Exceptions.h> - -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmData.h> using namespace std; using namespace dev; -using namespace dev::yul; +using namespace yul; void CommonSubexpressionEliminator::visit(Expression& _e) { diff --git a/libyul/optimiser/CommonSubexpressionEliminator.h b/libyul/optimiser/CommonSubexpressionEliminator.h index f8aa0ee1..ac1ebe3a 100644 --- a/libyul/optimiser/CommonSubexpressionEliminator.h +++ b/libyul/optimiser/CommonSubexpressionEliminator.h @@ -23,8 +23,6 @@ #include <libyul/optimiser/DataFlowAnalyzer.h> -namespace dev -{ namespace yul { @@ -38,8 +36,7 @@ class CommonSubexpressionEliminator: public DataFlowAnalyzer { protected: using ASTModifier::visit; - virtual void visit(Expression& _e) override; + void visit(Expression& _e) override; }; } -} diff --git a/libyul/optimiser/DataFlowAnalyzer.cpp b/libyul/optimiser/DataFlowAnalyzer.cpp index 134777d0..64c67b38 100644 --- a/libyul/optimiser/DataFlowAnalyzer.cpp +++ b/libyul/optimiser/DataFlowAnalyzer.cpp @@ -25,8 +25,7 @@ #include <libyul/optimiser/NameCollector.h> #include <libyul/optimiser/Semantics.h> #include <libyul/Exceptions.h> - -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmData.h> #include <libdevcore/CommonData.h> @@ -34,7 +33,7 @@ using namespace std; using namespace dev; -using namespace dev::yul; +using namespace yul; void DataFlowAnalyzer::operator()(Assignment& _assignment) { diff --git a/libyul/optimiser/DataFlowAnalyzer.h b/libyul/optimiser/DataFlowAnalyzer.h index a0c21eee..cd134d48 100644 --- a/libyul/optimiser/DataFlowAnalyzer.h +++ b/libyul/optimiser/DataFlowAnalyzer.h @@ -23,14 +23,11 @@ #pragma once #include <libyul/optimiser/ASTWalker.h> - #include <libyul/YulString.h> #include <map> #include <set> -namespace dev -{ namespace yul { @@ -45,13 +42,13 @@ class DataFlowAnalyzer: public ASTModifier { public: using ASTModifier::operator(); - virtual void operator()(Assignment& _assignment) override; - virtual void operator()(VariableDeclaration& _varDecl) override; - virtual void operator()(If& _if) override; - virtual void operator()(Switch& _switch) override; - virtual void operator()(FunctionDefinition&) override; - virtual void operator()(ForLoop&) override; - virtual void operator()(Block& _block) override; + void operator()(Assignment& _assignment) override; + void operator()(VariableDeclaration& _varDecl) override; + void operator()(If& _if) override; + void operator()(Switch& _switch) override; + void operator()(FunctionDefinition&) override; + void operator()(ForLoop&) override; + void operator()(Block& _block) override; protected: /// Registers the assignment. @@ -88,4 +85,3 @@ protected: }; } -} diff --git a/libyul/optimiser/Disambiguator.cpp b/libyul/optimiser/Disambiguator.cpp index 4303f412..fda5895b 100644 --- a/libyul/optimiser/Disambiguator.cpp +++ b/libyul/optimiser/Disambiguator.cpp @@ -21,17 +21,14 @@ #include <libyul/optimiser/Disambiguator.h> #include <libyul/Exceptions.h> - -#include <libsolidity/inlineasm/AsmData.h> -#include <libsolidity/inlineasm/AsmScope.h> +#include <libyul/AsmData.h> +#include <libyul/AsmScope.h> using namespace std; using namespace dev; -using namespace dev::yul; +using namespace yul; using namespace dev::solidity; -using Scope = dev::solidity::assembly::Scope; - YulString Disambiguator::translateIdentifier(YulString _originalName) { if ((m_externallyUsedIdentifiers.count(_originalName))) diff --git a/libyul/optimiser/Disambiguator.h b/libyul/optimiser/Disambiguator.h index bfb65682..bb83417b 100644 --- a/libyul/optimiser/Disambiguator.h +++ b/libyul/optimiser/Disambiguator.h @@ -20,20 +20,16 @@ #pragma once -#include <libyul/ASTDataForward.h> - +#include <libyul/AsmDataForward.h> +#include <libyul/AsmAnalysisInfo.h> #include <libyul/optimiser/ASTCopier.h> #include <libyul/optimiser/NameDispenser.h> -#include <libsolidity/inlineasm/AsmAnalysisInfo.h> - #include <boost/variant.hpp> #include <boost/optional.hpp> #include <set> -namespace dev -{ namespace yul { @@ -44,7 +40,7 @@ class Disambiguator: public ASTCopier { public: explicit Disambiguator( - solidity::assembly::AsmAnalysisInfo const& _analysisInfo, + AsmAnalysisInfo const& _analysisInfo, std::set<YulString> const& _externallyUsedIdentifiers = {} ): m_info(_analysisInfo), m_externallyUsedIdentifiers(_externallyUsedIdentifiers), m_nameDispenser(m_externallyUsedIdentifiers) @@ -52,22 +48,21 @@ public: } protected: - virtual void enterScope(Block const& _block) override; - virtual void leaveScope(Block const& _block) override; - virtual void enterFunction(FunctionDefinition const& _function) override; - virtual void leaveFunction(FunctionDefinition const& _function) override; - virtual YulString translateIdentifier(YulString _name) override; + void enterScope(Block const& _block) override; + void leaveScope(Block const& _block) override; + void enterFunction(FunctionDefinition const& _function) override; + void leaveFunction(FunctionDefinition const& _function) override; + YulString translateIdentifier(YulString _name) override; - void enterScopeInternal(solidity::assembly::Scope& _scope); - void leaveScopeInternal(solidity::assembly::Scope& _scope); + void enterScopeInternal(Scope& _scope); + void leaveScopeInternal(Scope& _scope); - solidity::assembly::AsmAnalysisInfo const& m_info; + AsmAnalysisInfo const& m_info; std::set<YulString> const& m_externallyUsedIdentifiers; - std::vector<solidity::assembly::Scope*> m_scopes; + std::vector<Scope*> m_scopes; std::map<void const*, YulString> m_translations; NameDispenser m_nameDispenser; }; } -} diff --git a/libyul/optimiser/ExpressionInliner.cpp b/libyul/optimiser/ExpressionInliner.cpp index 07e88191..27d43ac0 100644 --- a/libyul/optimiser/ExpressionInliner.cpp +++ b/libyul/optimiser/ExpressionInliner.cpp @@ -23,14 +23,13 @@ #include <libyul/optimiser/InlinableExpressionFunctionFinder.h> #include <libyul/optimiser/Substitution.h> #include <libyul/optimiser/Semantics.h> - -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmData.h> #include <boost/algorithm/cxx11/all_of.hpp> using namespace std; using namespace dev; -using namespace dev::yul; +using namespace yul; using namespace dev::solidity; void ExpressionInliner::run() diff --git a/libyul/optimiser/ExpressionInliner.h b/libyul/optimiser/ExpressionInliner.h index d903664f..14e80c0a 100644 --- a/libyul/optimiser/ExpressionInliner.h +++ b/libyul/optimiser/ExpressionInliner.h @@ -20,16 +20,13 @@ #pragma once #include <libyul/optimiser/ASTWalker.h> - -#include <libyul/ASTDataForward.h> +#include <libyul/AsmDataForward.h> #include <boost/variant.hpp> #include <boost/optional.hpp> #include <set> -namespace dev -{ namespace yul { @@ -54,9 +51,9 @@ public: void run(); using ASTModifier::operator(); - virtual void operator()(FunctionDefinition& _fun) override; + void operator()(FunctionDefinition& _fun) override; - virtual void visit(Expression& _expression) override; + void visit(Expression& _expression) override; private: std::map<YulString, FunctionDefinition const*> m_inlinableFunctions; @@ -69,4 +66,3 @@ private: } -} diff --git a/libyul/optimiser/ExpressionJoiner.cpp b/libyul/optimiser/ExpressionJoiner.cpp index 7e57a629..de2b5d53 100644 --- a/libyul/optimiser/ExpressionJoiner.cpp +++ b/libyul/optimiser/ExpressionJoiner.cpp @@ -24,8 +24,7 @@ #include <libyul/optimiser/NameCollector.h> #include <libyul/optimiser/Utilities.h> #include <libyul/Exceptions.h> - -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmData.h> #include <libdevcore/CommonData.h> @@ -33,7 +32,7 @@ using namespace std; using namespace dev; -using namespace dev::yul; +using namespace yul; using namespace dev::solidity; void ExpressionJoiner::operator()(FunctionalInstruction& _instruction) diff --git a/libyul/optimiser/ExpressionJoiner.h b/libyul/optimiser/ExpressionJoiner.h index 0cc61981..643d62b0 100644 --- a/libyul/optimiser/ExpressionJoiner.h +++ b/libyul/optimiser/ExpressionJoiner.h @@ -20,14 +20,11 @@ */ #pragma once -#include <libyul/ASTDataForward.h> - +#include <libyul/AsmDataForward.h> #include <libyul/optimiser/ASTWalker.h> #include <map> -namespace dev -{ namespace yul { @@ -99,4 +96,3 @@ private: }; } -} diff --git a/libyul/optimiser/ExpressionSimplifier.cpp b/libyul/optimiser/ExpressionSimplifier.cpp index 64e9d7e7..cda44e8e 100644 --- a/libyul/optimiser/ExpressionSimplifier.cpp +++ b/libyul/optimiser/ExpressionSimplifier.cpp @@ -23,14 +23,13 @@ #include <libyul/optimiser/SimplificationRules.h> #include <libyul/optimiser/Semantics.h> #include <libyul/optimiser/SSAValueTracker.h> - -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmData.h> #include <libdevcore/CommonData.h> using namespace std; using namespace dev; -using namespace dev::yul; +using namespace yul; using namespace dev::solidity; diff --git a/libyul/optimiser/ExpressionSimplifier.h b/libyul/optimiser/ExpressionSimplifier.h index 5965a1bb..fe3507f8 100644 --- a/libyul/optimiser/ExpressionSimplifier.h +++ b/libyul/optimiser/ExpressionSimplifier.h @@ -20,12 +20,10 @@ #pragma once -#include <libyul/ASTDataForward.h> +#include <libyul/AsmDataForward.h> #include <libyul/optimiser/ASTWalker.h> -namespace dev -{ namespace yul { @@ -52,4 +50,3 @@ private: }; } -} diff --git a/libyul/optimiser/ExpressionSplitter.cpp b/libyul/optimiser/ExpressionSplitter.cpp index a4b7a909..a3b2dc11 100644 --- a/libyul/optimiser/ExpressionSplitter.cpp +++ b/libyul/optimiser/ExpressionSplitter.cpp @@ -23,7 +23,7 @@ #include <libyul/optimiser/ASTWalker.h> -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmData.h> #include <libdevcore/CommonData.h> @@ -31,7 +31,8 @@ using namespace std; using namespace dev; -using namespace dev::yul; +using namespace langutil; +using namespace yul; using namespace dev::solidity; void ExpressionSplitter::operator()(FunctionalInstruction& _instruction) diff --git a/libyul/optimiser/ExpressionSplitter.h b/libyul/optimiser/ExpressionSplitter.h index 339acbf0..d4d2b3f6 100644 --- a/libyul/optimiser/ExpressionSplitter.h +++ b/libyul/optimiser/ExpressionSplitter.h @@ -20,15 +20,13 @@ */ #pragma once -#include <libyul/ASTDataForward.h> +#include <libyul/AsmDataForward.h> #include <libyul/optimiser/ASTWalker.h> #include <libyul/optimiser/NameDispenser.h> #include <vector> -namespace dev -{ namespace yul { @@ -63,12 +61,12 @@ public: m_nameDispenser(_nameDispenser) { } - virtual void operator()(FunctionalInstruction&) override; - virtual void operator()(FunctionCall&) override; - virtual void operator()(If&) override; - virtual void operator()(Switch&) override; - virtual void operator()(ForLoop&) override; - virtual void operator()(Block& _block) override; + void operator()(FunctionalInstruction&) override; + void operator()(FunctionCall&) override; + void operator()(If&) override; + void operator()(Switch&) override; + void operator()(ForLoop&) override; + void operator()(Block& _block) override; private: /// Replaces the expression by a variable if it is a function call or functional @@ -83,4 +81,3 @@ private: }; } -} diff --git a/libyul/optimiser/ForLoopInitRewriter.cpp b/libyul/optimiser/ForLoopInitRewriter.cpp new file mode 100644 index 00000000..80d39248 --- /dev/null +++ b/libyul/optimiser/ForLoopInitRewriter.cpp @@ -0,0 +1,43 @@ +/* + 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/>. +*/ +#include <libyul/optimiser/ForLoopInitRewriter.h> +#include <libyul/AsmData.h> +#include <libdevcore/CommonData.h> +#include <functional> + +using namespace std; +using namespace dev; +using namespace yul; + +void ForLoopInitRewriter::operator()(Block& _block) +{ + iterateReplacing( + _block.statements, + [](Statement& _stmt) -> boost::optional<vector<Statement>> + { + if (_stmt.type() == typeid(ForLoop)) + { + auto& forLoop = boost::get<ForLoop>(_stmt); + vector<Statement> rewrite; + swap(rewrite, forLoop.pre.statements); + rewrite.emplace_back(move(forLoop)); + return rewrite; + } + return {}; + } + ); +} diff --git a/libyul/optimiser/ForLoopInitRewriter.h b/libyul/optimiser/ForLoopInitRewriter.h new file mode 100644 index 00000000..e925c6c2 --- /dev/null +++ b/libyul/optimiser/ForLoopInitRewriter.h @@ -0,0 +1,36 @@ +/* + 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/>. +*/ +#pragma once + +#include <libyul/optimiser/ASTWalker.h> + +namespace yul +{ + +/** + * Rewrites ForLoop by moving the pre statement block in front of the ForLoop. + * Requirements: + * - The Disambiguator must be run upfront. + */ +class ForLoopInitRewriter: public ASTModifier +{ +public: + using ASTModifier::operator(); + void operator()(Block& _block) override; +}; + +} diff --git a/libyul/optimiser/FullInliner.cpp b/libyul/optimiser/FullInliner.cpp index c9057cf3..8ae26fbb 100644 --- a/libyul/optimiser/FullInliner.cpp +++ b/libyul/optimiser/FullInliner.cpp @@ -27,8 +27,7 @@ #include <libyul/optimiser/Metrics.h> #include <libyul/optimiser/SSAValueTracker.h> #include <libyul/Exceptions.h> - -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmData.h> #include <libdevcore/CommonData.h> #include <libdevcore/Visitor.h> @@ -37,7 +36,7 @@ using namespace std; using namespace dev; -using namespace dev::yul; +using namespace yul; using namespace dev::solidity; FullInliner::FullInliner(Block& _ast, NameDispenser& _dispenser): diff --git a/libyul/optimiser/FullInliner.h b/libyul/optimiser/FullInliner.h index 66ce8e2f..a8fe76c6 100644 --- a/libyul/optimiser/FullInliner.h +++ b/libyul/optimiser/FullInliner.h @@ -19,22 +19,20 @@ */ #pragma once -#include <libyul/ASTDataForward.h> +#include <libyul/AsmDataForward.h> #include <libyul/optimiser/ASTCopier.h> #include <libyul/optimiser/ASTWalker.h> #include <libyul/optimiser/NameDispenser.h> #include <libyul/Exceptions.h> -#include <libevmasm/SourceLocation.h> +#include <liblangutil/SourceLocation.h> #include <boost/variant.hpp> #include <boost/optional.hpp> #include <set> -namespace dev -{ namespace yul { @@ -110,7 +108,7 @@ public: m_nameDispenser(_nameDispenser) { } - virtual void operator()(Block& _block) override; + void operator()(Block& _block) override; private: boost::optional<std::vector<Statement>> tryInlineStatement(Statement& _statement); @@ -141,10 +139,10 @@ public: using ASTCopier::operator (); - virtual Statement operator()(VariableDeclaration const& _varDecl) override; - virtual Statement operator()(FunctionDefinition const& _funDef) override; + Statement operator()(VariableDeclaration const& _varDecl) override; + Statement operator()(FunctionDefinition const& _funDef) override; - virtual YulString translateIdentifier(YulString _name) override; + YulString translateIdentifier(YulString _name) override; NameDispenser& m_nameDispenser; YulString m_varNamePrefix; @@ -153,4 +151,3 @@ public: } -} diff --git a/libyul/optimiser/FunctionGrouper.cpp b/libyul/optimiser/FunctionGrouper.cpp index 3d2e5322..02ce22cd 100644 --- a/libyul/optimiser/FunctionGrouper.cpp +++ b/libyul/optimiser/FunctionGrouper.cpp @@ -21,13 +21,13 @@ #include <libyul/optimiser/FunctionGrouper.h> -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmData.h> #include <boost/range/algorithm_ext/erase.hpp> using namespace std; using namespace dev; -using namespace dev::yul; +using namespace yul; using namespace dev::solidity; diff --git a/libyul/optimiser/FunctionGrouper.h b/libyul/optimiser/FunctionGrouper.h index 63cfbfb1..3b3f48a7 100644 --- a/libyul/optimiser/FunctionGrouper.h +++ b/libyul/optimiser/FunctionGrouper.h @@ -21,10 +21,8 @@ #pragma once -#include <libyul/ASTDataForward.h> +#include <libyul/AsmDataForward.h> -namespace dev -{ namespace yul { @@ -43,4 +41,3 @@ public: }; } -} diff --git a/libyul/optimiser/FunctionHoister.cpp b/libyul/optimiser/FunctionHoister.cpp index c196dead..bd1c781b 100644 --- a/libyul/optimiser/FunctionHoister.cpp +++ b/libyul/optimiser/FunctionHoister.cpp @@ -22,14 +22,13 @@ #include <libyul/optimiser/FunctionHoister.h> #include <libyul/optimiser/Utilities.h> - -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmData.h> #include <libdevcore/CommonData.h> using namespace std; using namespace dev; -using namespace dev::yul; +using namespace yul; using namespace dev::solidity; void FunctionHoister::operator()(Block& _block) diff --git a/libyul/optimiser/FunctionHoister.h b/libyul/optimiser/FunctionHoister.h index 823b9e2b..31092069 100644 --- a/libyul/optimiser/FunctionHoister.h +++ b/libyul/optimiser/FunctionHoister.h @@ -21,12 +21,9 @@ #pragma once -#include <libyul/ASTDataForward.h> - +#include <libyul/AsmDataForward.h> #include <libyul/optimiser/ASTWalker.h> -namespace dev -{ namespace yul { @@ -49,4 +46,3 @@ private: }; } -} diff --git a/libyul/optimiser/InlinableExpressionFunctionFinder.cpp b/libyul/optimiser/InlinableExpressionFunctionFinder.cpp index deaaee97..662cdf25 100644 --- a/libyul/optimiser/InlinableExpressionFunctionFinder.cpp +++ b/libyul/optimiser/InlinableExpressionFunctionFinder.cpp @@ -21,12 +21,11 @@ #include <libyul/optimiser/InlinableExpressionFunctionFinder.h> #include <libyul/optimiser/Utilities.h> - -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmData.h> using namespace std; using namespace dev; -using namespace dev::yul; +using namespace yul; void InlinableExpressionFunctionFinder::operator()(Identifier const& _identifier) { diff --git a/libyul/optimiser/InlinableExpressionFunctionFinder.h b/libyul/optimiser/InlinableExpressionFunctionFinder.h index baf4bbfc..afde8a2a 100644 --- a/libyul/optimiser/InlinableExpressionFunctionFinder.h +++ b/libyul/optimiser/InlinableExpressionFunctionFinder.h @@ -20,13 +20,11 @@ #pragma once -#include <libyul/ASTDataForward.h> +#include <libyul/AsmDataForward.h> #include <libyul/optimiser/ASTWalker.h> #include <set> -namespace dev -{ namespace yul { @@ -49,9 +47,9 @@ public: } using ASTWalker::operator(); - virtual void operator()(Identifier const& _identifier) override; - virtual void operator()(FunctionCall const& _funCall) override; - virtual void operator()(FunctionDefinition const& _function) override; + void operator()(Identifier const& _identifier) override; + void operator()(FunctionCall const& _funCall) override; + void operator()(FunctionDefinition const& _function) override; private: void checkAllowed(YulString _name) @@ -66,4 +64,3 @@ private: }; } -} diff --git a/libyul/optimiser/MainFunction.cpp b/libyul/optimiser/MainFunction.cpp index f3306598..63eea2db 100644 --- a/libyul/optimiser/MainFunction.cpp +++ b/libyul/optimiser/MainFunction.cpp @@ -24,13 +24,13 @@ #include <libyul/optimiser/NameCollector.h> #include <libyul/Exceptions.h> -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmData.h> #include <libdevcore/CommonData.h> using namespace std; using namespace dev; -using namespace dev::yul; +using namespace yul; using namespace dev::solidity; void MainFunction::operator()(Block& _block) diff --git a/libyul/optimiser/MainFunction.h b/libyul/optimiser/MainFunction.h index 4a73283a..96acc0ac 100644 --- a/libyul/optimiser/MainFunction.h +++ b/libyul/optimiser/MainFunction.h @@ -21,10 +21,8 @@ #pragma once -#include <libyul/ASTDataForward.h> +#include <libyul/AsmDataForward.h> -namespace dev -{ namespace yul { @@ -38,4 +36,3 @@ public: }; } -} diff --git a/libyul/optimiser/Metrics.cpp b/libyul/optimiser/Metrics.cpp index 066c6b58..a5557fb3 100644 --- a/libyul/optimiser/Metrics.cpp +++ b/libyul/optimiser/Metrics.cpp @@ -20,10 +20,10 @@ #include <libyul/optimiser/Metrics.h> -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmData.h> using namespace dev; -using namespace dev::yul; +using namespace yul; size_t CodeSize::codeSize(Statement const& _statement) { diff --git a/libyul/optimiser/Metrics.h b/libyul/optimiser/Metrics.h index 47c7ec79..ca244600 100644 --- a/libyul/optimiser/Metrics.h +++ b/libyul/optimiser/Metrics.h @@ -22,8 +22,6 @@ #include <libyul/optimiser/ASTWalker.h> -namespace dev -{ namespace yul { @@ -41,12 +39,11 @@ public: static size_t codeSize(Block const& _block); private: - virtual void visit(Statement const& _statement) override; - virtual void visit(Expression const& _expression) override; + void visit(Statement const& _statement) override; + void visit(Expression const& _expression) override; private: size_t m_size = 0; }; } -} diff --git a/libyul/optimiser/NameCollector.cpp b/libyul/optimiser/NameCollector.cpp index 36f55b99..f9079827 100644 --- a/libyul/optimiser/NameCollector.cpp +++ b/libyul/optimiser/NameCollector.cpp @@ -20,11 +20,11 @@ #include <libyul/optimiser/NameCollector.h> -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmData.h> using namespace std; using namespace dev; -using namespace dev::yul; +using namespace yul; void NameCollector::operator()(VariableDeclaration const& _varDecl) { diff --git a/libyul/optimiser/NameCollector.h b/libyul/optimiser/NameCollector.h index b76eec30..c177a399 100644 --- a/libyul/optimiser/NameCollector.h +++ b/libyul/optimiser/NameCollector.h @@ -25,8 +25,6 @@ #include <map> #include <set> -namespace dev -{ namespace yul { @@ -42,8 +40,8 @@ public: } using ASTWalker::operator (); - virtual void operator()(VariableDeclaration const& _varDecl) override; - virtual void operator()(FunctionDefinition const& _funDef) override; + void operator()(VariableDeclaration const& _varDecl) override; + void operator()(FunctionDefinition const& _funDef) override; std::set<YulString> names() const { return m_names; } private: @@ -75,7 +73,7 @@ class Assignments: public ASTWalker { public: using ASTWalker::operator (); - virtual void operator()(Assignment const& _assignment) override; + void operator()(Assignment const& _assignment) override; std::set<YulString> const& names() const { return m_names; } private: @@ -83,4 +81,3 @@ private: }; } -} diff --git a/libyul/optimiser/NameDispenser.cpp b/libyul/optimiser/NameDispenser.cpp index 3c870fa5..e7cdc60f 100644 --- a/libyul/optimiser/NameDispenser.cpp +++ b/libyul/optimiser/NameDispenser.cpp @@ -21,12 +21,11 @@ #include <libyul/optimiser/NameDispenser.h> #include <libyul/optimiser/NameCollector.h> - -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmData.h> using namespace std; using namespace dev; -using namespace dev::yul; +using namespace yul; NameDispenser::NameDispenser(Block const& _ast): NameDispenser(NameCollector(_ast).names()) diff --git a/libyul/optimiser/NameDispenser.h b/libyul/optimiser/NameDispenser.h index 7311440b..664a5265 100644 --- a/libyul/optimiser/NameDispenser.h +++ b/libyul/optimiser/NameDispenser.h @@ -19,14 +19,12 @@ */ #pragma once -#include <libyul/ASTDataForward.h> +#include <libyul/AsmDataForward.h> #include <libyul/YulString.h> #include <set> -namespace dev -{ namespace yul { @@ -58,4 +56,3 @@ private: }; } -} diff --git a/libyul/optimiser/RedundantAssignEliminator.cpp b/libyul/optimiser/RedundantAssignEliminator.cpp index b7217074..7b18e8ca 100644 --- a/libyul/optimiser/RedundantAssignEliminator.cpp +++ b/libyul/optimiser/RedundantAssignEliminator.cpp @@ -22,8 +22,7 @@ #include <libyul/optimiser/RedundantAssignEliminator.h> #include <libyul/optimiser/Semantics.h> - -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmData.h> #include <libdevcore/CommonData.h> @@ -31,7 +30,7 @@ using namespace std; using namespace dev; -using namespace dev::yul; +using namespace yul; using namespace dev::solidity; void RedundantAssignEliminator::operator()(Identifier const& _identifier) diff --git a/libyul/optimiser/RedundantAssignEliminator.h b/libyul/optimiser/RedundantAssignEliminator.h index 76106aae..54d65823 100644 --- a/libyul/optimiser/RedundantAssignEliminator.h +++ b/libyul/optimiser/RedundantAssignEliminator.h @@ -21,14 +21,11 @@ #pragma once -#include <libyul/ASTDataForward.h> - +#include <libyul/AsmDataForward.h> #include <libyul/optimiser/ASTWalker.h> #include <map> -namespace dev -{ namespace yul { @@ -190,4 +187,3 @@ private: }; } -} diff --git a/libyul/optimiser/Rematerialiser.cpp b/libyul/optimiser/Rematerialiser.cpp index 38d50ef4..4180bfc3 100644 --- a/libyul/optimiser/Rematerialiser.cpp +++ b/libyul/optimiser/Rematerialiser.cpp @@ -23,12 +23,11 @@ #include <libyul/optimiser/Metrics.h> #include <libyul/optimiser/ASTCopier.h> #include <libyul/Exceptions.h> - -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmData.h> using namespace std; using namespace dev; -using namespace dev::yul; +using namespace yul; void Rematerialiser::visit(Expression& _e) { diff --git a/libyul/optimiser/Rematerialiser.h b/libyul/optimiser/Rematerialiser.h index f82465eb..b3841519 100644 --- a/libyul/optimiser/Rematerialiser.h +++ b/libyul/optimiser/Rematerialiser.h @@ -22,8 +22,6 @@ #include <libyul/optimiser/DataFlowAnalyzer.h> -namespace dev -{ namespace yul { @@ -36,9 +34,8 @@ class Rematerialiser: public DataFlowAnalyzer { protected: using ASTModifier::visit; - virtual void visit(Expression& _e) override; + void visit(Expression& _e) override; }; } -} diff --git a/libyul/optimiser/SSATransform.cpp b/libyul/optimiser/SSATransform.cpp index f209ee7b..928c0859 100644 --- a/libyul/optimiser/SSATransform.cpp +++ b/libyul/optimiser/SSATransform.cpp @@ -23,15 +23,15 @@ #include <libyul/optimiser/NameCollector.h> #include <libyul/optimiser/NameDispenser.h> - -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmData.h> #include <libdevcore/CommonData.h> using namespace std; using namespace dev; -using namespace dev::yul; +using namespace langutil; +using namespace yul; using namespace dev::solidity; void SSATransform::operator()(Identifier& _identifier) diff --git a/libyul/optimiser/SSATransform.h b/libyul/optimiser/SSATransform.h index bb642549..4cb62f23 100644 --- a/libyul/optimiser/SSATransform.h +++ b/libyul/optimiser/SSATransform.h @@ -20,14 +20,11 @@ */ #pragma once -#include <libyul/ASTDataForward.h> - +#include <libyul/AsmDataForward.h> #include <libyul/optimiser/ASTWalker.h> #include <vector> -namespace dev -{ namespace yul { @@ -95,4 +92,3 @@ private: }; } -} diff --git a/libyul/optimiser/SSAValueTracker.cpp b/libyul/optimiser/SSAValueTracker.cpp index 491117da..35b29b04 100644 --- a/libyul/optimiser/SSAValueTracker.cpp +++ b/libyul/optimiser/SSAValueTracker.cpp @@ -21,11 +21,11 @@ #include <libyul/optimiser/SSAValueTracker.h> -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmData.h> using namespace std; using namespace dev; -using namespace dev::yul; +using namespace yul; void SSAValueTracker::operator()(Assignment const& _assignment) { diff --git a/libyul/optimiser/SSAValueTracker.h b/libyul/optimiser/SSAValueTracker.h index d1539c86..e182e013 100644 --- a/libyul/optimiser/SSAValueTracker.h +++ b/libyul/optimiser/SSAValueTracker.h @@ -26,8 +26,6 @@ #include <map> #include <set> -namespace dev -{ namespace yul { @@ -41,8 +39,8 @@ class SSAValueTracker: public ASTWalker { public: using ASTWalker::operator(); - virtual void operator()(VariableDeclaration const& _varDecl) override; - virtual void operator()(Assignment const& _assignment) override; + void operator()(VariableDeclaration const& _varDecl) override; + void operator()(Assignment const& _assignment) override; std::map<YulString, Expression const*> const& values() const { return m_values; } Expression const* value(YulString _name) const { return m_values.at(_name); } @@ -54,4 +52,3 @@ private: }; } -} diff --git a/libyul/optimiser/Semantics.cpp b/libyul/optimiser/Semantics.cpp index 3c49016e..91bb2709 100644 --- a/libyul/optimiser/Semantics.cpp +++ b/libyul/optimiser/Semantics.cpp @@ -21,8 +21,7 @@ #include <libyul/optimiser/Semantics.h> #include <libyul/Exceptions.h> - -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmData.h> #include <libevmasm/SemanticInformation.h> @@ -30,7 +29,7 @@ using namespace std; using namespace dev; -using namespace dev::yul; +using namespace yul; MovableChecker::MovableChecker(Expression const& _expression) { diff --git a/libyul/optimiser/Semantics.h b/libyul/optimiser/Semantics.h index 620a91cb..70c50806 100644 --- a/libyul/optimiser/Semantics.h +++ b/libyul/optimiser/Semantics.h @@ -24,8 +24,6 @@ #include <set> -namespace dev -{ namespace yul { @@ -38,12 +36,12 @@ public: MovableChecker() = default; explicit MovableChecker(Expression const& _expression); - virtual void operator()(Identifier const& _identifier) override; - virtual void operator()(FunctionalInstruction const& _functionalInstruction) override; - virtual void operator()(FunctionCall const& _functionCall) override; + void operator()(Identifier const& _identifier) override; + void operator()(FunctionalInstruction const& _functionalInstruction) override; + void operator()(FunctionCall const& _functionCall) override; /// Disallow visiting anything apart from Expressions (this throws). - virtual void visit(Statement const&) override; + void visit(Statement const&) override; using ASTWalker::visit; bool movable() const { return m_movable; } @@ -57,4 +55,3 @@ private: }; } -} diff --git a/libyul/optimiser/SimplificationRules.cpp b/libyul/optimiser/SimplificationRules.cpp index 5721042f..b3190fef 100644 --- a/libyul/optimiser/SimplificationRules.cpp +++ b/libyul/optimiser/SimplificationRules.cpp @@ -24,14 +24,14 @@ #include <libyul/optimiser/ASTCopier.h> #include <libyul/optimiser/Semantics.h> #include <libyul/optimiser/SyntacticalEquality.h> - -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmData.h> #include <libevmasm/RuleList.h> using namespace std; using namespace dev; -using namespace dev::yul; +using namespace langutil; +using namespace yul; SimplificationRule<Pattern> const* SimplificationRules::findFirstMatch( @@ -123,7 +123,7 @@ bool Pattern::matches(Expression const& _expr, map<YulString, Expression const*> if (expr->type() != typeid(Literal)) return false; Literal const& literal = boost::get<Literal>(*expr); - if (literal.kind != assembly::LiteralKind::Number) + if (literal.kind != LiteralKind::Number) return false; if (m_data && *m_data != u256(literal.value.str())) return false; @@ -193,7 +193,7 @@ Expression Pattern::toExpression(SourceLocation const& _location) const if (m_kind == PatternKind::Constant) { assertThrow(m_data, OptimizerException, "No match group and no constant value given."); - return Literal{_location, assembly::LiteralKind::Number, YulString{formatNumber(*m_data)}, {}}; + return Literal{_location, LiteralKind::Number, YulString{formatNumber(*m_data)}, {}}; } else if (m_kind == PatternKind::Operation) { @@ -208,7 +208,7 @@ Expression Pattern::toExpression(SourceLocation const& _location) const u256 Pattern::d() const { Literal const& literal = boost::get<Literal>(matchGroupValue()); - assertThrow(literal.kind == assembly::LiteralKind::Number, OptimizerException, ""); + assertThrow(literal.kind == LiteralKind::Number, OptimizerException, ""); assertThrow(isValidDecimal(literal.value.str()) || isValidHex(literal.value.str()), OptimizerException, ""); return u256(literal.value.str()); } diff --git a/libyul/optimiser/SimplificationRules.h b/libyul/optimiser/SimplificationRules.h index b608ca91..16aaba04 100644 --- a/libyul/optimiser/SimplificationRules.h +++ b/libyul/optimiser/SimplificationRules.h @@ -23,17 +23,14 @@ #include <libevmasm/ExpressionClasses.h> #include <libevmasm/SimplificationRule.h> -#include <libyul/ASTDataForward.h> - -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmDataForward.h> +#include <libyul/AsmData.h> #include <boost/noncopyable.hpp> #include <functional> #include <vector> -namespace dev -{ namespace yul { @@ -86,11 +83,11 @@ public: /// Matches any expression. Pattern(PatternKind _kind = PatternKind::Any): m_kind(_kind) {} // Matches a specific constant value. - Pattern(unsigned _value): Pattern(u256(_value)) {} + Pattern(unsigned _value): Pattern(dev::u256(_value)) {} // Matches a specific constant value. - Pattern(u256 const& _value): m_kind(PatternKind::Constant), m_data(std::make_shared<u256>(_value)) {} + Pattern(dev::u256 const& _value): m_kind(PatternKind::Constant), m_data(std::make_shared<dev::u256>(_value)) {} // Matches a given instruction with given arguments - Pattern(solidity::Instruction _instruction, std::vector<Pattern> const& _arguments = {}); + Pattern(dev::solidity::Instruction _instruction, std::vector<Pattern> const& _arguments = {}); /// Sets this pattern to be part of the match group with the identifier @a _group. /// Inside one rule, all patterns in the same match group have to match expressions from the /// same expression equivalence class. @@ -101,24 +98,23 @@ public: std::vector<Pattern> arguments() const { return m_arguments; } /// @returns the data of the matched expression if this pattern is part of a match group. - u256 d() const; + dev::u256 d() const; - solidity::Instruction instruction() const; + dev::solidity::Instruction instruction() const; /// Turns this pattern into an actual expression. Should only be called /// for patterns resulting from an action, i.e. with match groups assigned. - Expression toExpression(SourceLocation const& _location) const; + Expression toExpression(langutil::SourceLocation const& _location) const; private: Expression const& matchGroupValue() const; PatternKind m_kind = PatternKind::Any; - solidity::Instruction m_instruction; ///< Only valid if m_kind is Operation - std::shared_ptr<u256> m_data; ///< Only valid if m_kind is Constant + dev::solidity::Instruction m_instruction; ///< Only valid if m_kind is Operation + std::shared_ptr<dev::u256> m_data; ///< Only valid if m_kind is Constant std::vector<Pattern> m_arguments; unsigned m_matchGroup = 0; std::map<unsigned, Expression const*>* m_matchGroups = nullptr; }; } -} diff --git a/libyul/optimiser/Substitution.cpp b/libyul/optimiser/Substitution.cpp index 9b3d4c03..bc9efe96 100644 --- a/libyul/optimiser/Substitution.cpp +++ b/libyul/optimiser/Substitution.cpp @@ -20,11 +20,11 @@ #include <libyul/optimiser/Substitution.h> -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmData.h> using namespace std; using namespace dev; -using namespace dev::yul; +using namespace yul; Expression Substitution::translate(Expression const& _expression) { diff --git a/libyul/optimiser/Substitution.h b/libyul/optimiser/Substitution.h index 59ee4620..41f73b92 100644 --- a/libyul/optimiser/Substitution.h +++ b/libyul/optimiser/Substitution.h @@ -21,13 +21,10 @@ #pragma once #include <libyul/optimiser/ASTCopier.h> - #include <libyul/YulString.h> #include <map> -namespace dev -{ namespace yul { @@ -40,11 +37,10 @@ public: Substitution(std::map<YulString, Expression const*> const& _substitutions): m_substitutions(_substitutions) {} - virtual Expression translate(Expression const& _expression) override; + Expression translate(Expression const& _expression) override; private: std::map<YulString, Expression const*> const& m_substitutions; }; } -} diff --git a/libyul/optimiser/Suite.cpp b/libyul/optimiser/Suite.cpp index 7d52a5a8..36f0e1eb 100644 --- a/libyul/optimiser/Suite.cpp +++ b/libyul/optimiser/Suite.cpp @@ -27,6 +27,7 @@ #include <libyul/optimiser/ExpressionJoiner.h> #include <libyul/optimiser/ExpressionInliner.h> #include <libyul/optimiser/FullInliner.h> +#include <libyul/optimiser/ForLoopInitRewriter.h> #include <libyul/optimiser/Rematerialiser.h> #include <libyul/optimiser/UnusedPruner.h> #include <libyul/optimiser/ExpressionSimplifier.h> @@ -34,21 +35,19 @@ #include <libyul/optimiser/SSATransform.h> #include <libyul/optimiser/RedundantAssignEliminator.h> #include <libyul/optimiser/VarDeclPropagator.h> - -#include <libsolidity/inlineasm/AsmAnalysisInfo.h> -#include <libsolidity/inlineasm/AsmData.h> - -#include <libsolidity/inlineasm/AsmPrinter.h> +#include <libyul/AsmAnalysisInfo.h> +#include <libyul/AsmData.h> +#include <libyul/AsmPrinter.h> #include <libdevcore/CommonData.h> using namespace std; using namespace dev; -using namespace dev::yul; +using namespace yul; void OptimiserSuite::run( Block& _ast, - solidity::assembly::AsmAnalysisInfo const& _analysisInfo, + AsmAnalysisInfo const& _analysisInfo, set<YulString> const& _externallyUsedIdentifiers ) { @@ -58,6 +57,7 @@ void OptimiserSuite::run( (FunctionHoister{})(ast); (FunctionGrouper{})(ast); + (ForLoopInitRewriter{})(ast); NameDispenser dispenser{ast}; diff --git a/libyul/optimiser/Suite.h b/libyul/optimiser/Suite.h index 5b564c56..795326b4 100644 --- a/libyul/optimiser/Suite.h +++ b/libyul/optimiser/Suite.h @@ -20,23 +20,16 @@ #pragma once -#include <libyul/ASTDataForward.h> +#include <libyul/AsmDataForward.h> #include <libyul/YulString.h> #include <set> -namespace dev -{ -namespace solidity -{ -namespace assembly -{ -struct AsmAnalysisInfo; -} -} namespace yul { +struct AsmAnalysisInfo; + /** * Optimiser suite that combines all steps and also provides the settings for the heuristics */ @@ -45,11 +38,10 @@ class OptimiserSuite public: static void run( Block& _ast, - solidity::assembly::AsmAnalysisInfo const& _analysisInfo, + AsmAnalysisInfo const& _analysisInfo, std::set<YulString> const& _externallyUsedIdentifiers = {} ); }; } -} diff --git a/libyul/optimiser/SyntacticalEquality.cpp b/libyul/optimiser/SyntacticalEquality.cpp index 66912383..99ce06e5 100644 --- a/libyul/optimiser/SyntacticalEquality.cpp +++ b/libyul/optimiser/SyntacticalEquality.cpp @@ -21,14 +21,13 @@ #include <libyul/optimiser/SyntacticalEquality.h> #include <libyul/Exceptions.h> - -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmData.h> #include <libdevcore/CommonData.h> using namespace std; using namespace dev; -using namespace dev::yul; +using namespace yul; bool SyntacticalEqualityChecker::equal(Expression const& _e1, Expression const& _e2) { diff --git a/libyul/optimiser/SyntacticalEquality.h b/libyul/optimiser/SyntacticalEquality.h index e9fbebe0..63c51b4f 100644 --- a/libyul/optimiser/SyntacticalEquality.h +++ b/libyul/optimiser/SyntacticalEquality.h @@ -20,12 +20,10 @@ #pragma once -#include <libyul/ASTDataForward.h> +#include <libyul/AsmDataForward.h> #include <vector> -namespace dev -{ namespace yul { @@ -47,4 +45,3 @@ protected: }; } -} diff --git a/libyul/optimiser/UnusedPruner.cpp b/libyul/optimiser/UnusedPruner.cpp index 71e86798..31aead82 100644 --- a/libyul/optimiser/UnusedPruner.cpp +++ b/libyul/optimiser/UnusedPruner.cpp @@ -24,14 +24,13 @@ #include <libyul/optimiser/Semantics.h> #include <libyul/optimiser/Utilities.h> #include <libyul/Exceptions.h> - -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmData.h> #include <boost/algorithm/cxx11/none_of.hpp> using namespace std; using namespace dev; -using namespace dev::yul; +using namespace yul; UnusedPruner::UnusedPruner(Block& _ast, set<YulString> const& _externallyUsedFunctions) { diff --git a/libyul/optimiser/UnusedPruner.h b/libyul/optimiser/UnusedPruner.h index b5aea3dd..64e02b35 100644 --- a/libyul/optimiser/UnusedPruner.h +++ b/libyul/optimiser/UnusedPruner.h @@ -26,8 +26,6 @@ #include <map> #include <set> -namespace dev -{ namespace yul { @@ -45,7 +43,7 @@ public: explicit UnusedPruner(Block& _ast, std::set<YulString> const& _externallyUsedFunctions = {}); using ASTModifier::operator(); - virtual void operator()(Block& _block) override; + void operator()(Block& _block) override; // @returns true iff the code changed in the previous run. bool shouldRunAgain() const { return m_shouldRunAgain; } @@ -62,4 +60,3 @@ private: }; } -} diff --git a/libyul/optimiser/Utilities.cpp b/libyul/optimiser/Utilities.cpp index df01ed39..b8cdd339 100644 --- a/libyul/optimiser/Utilities.cpp +++ b/libyul/optimiser/Utilities.cpp @@ -20,7 +20,7 @@ #include <libyul/optimiser/Utilities.h> -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmData.h> #include <libdevcore/CommonData.h> @@ -28,9 +28,9 @@ using namespace std; using namespace dev; -using namespace dev::yul; +using namespace yul; -void dev::yul::removeEmptyBlocks(Block& _block) +void yul::removeEmptyBlocks(Block& _block) { auto isEmptyBlock = [](Statement const& _st) -> bool { return _st.type() == typeid(Block) && boost::get<Block>(_st).statements.empty(); diff --git a/libyul/optimiser/Utilities.h b/libyul/optimiser/Utilities.h index 5b18a27c..c543b119 100644 --- a/libyul/optimiser/Utilities.h +++ b/libyul/optimiser/Utilities.h @@ -20,10 +20,8 @@ #pragma once -#include <libyul/ASTDataForward.h> +#include <libyul/AsmDataForward.h> -namespace dev -{ namespace yul { @@ -31,4 +29,3 @@ namespace yul void removeEmptyBlocks(Block& _block); } -} diff --git a/libyul/optimiser/VarDeclPropagator.cpp b/libyul/optimiser/VarDeclPropagator.cpp index 537b7020..bf974f44 100644 --- a/libyul/optimiser/VarDeclPropagator.cpp +++ b/libyul/optimiser/VarDeclPropagator.cpp @@ -16,7 +16,7 @@ */ #include <libyul/optimiser/VarDeclPropagator.h> -#include <libsolidity/inlineasm/AsmData.h> +#include <libyul/AsmData.h> #include <libdevcore/CommonData.h> #include <boost/range/algorithm_ext/erase.hpp> #include <algorithm> @@ -24,10 +24,7 @@ using namespace std; using namespace dev; -using namespace dev::yul; - -using dev::solidity::assembly::TypedName; -using dev::solidity::assembly::TypedNameList; +using namespace yul; void VarDeclPropagator::operator()(Block& _block) { diff --git a/libyul/optimiser/VarDeclPropagator.h b/libyul/optimiser/VarDeclPropagator.h index 4522d23a..1908c214 100644 --- a/libyul/optimiser/VarDeclPropagator.h +++ b/libyul/optimiser/VarDeclPropagator.h @@ -17,16 +17,14 @@ #pragma once -#include <libyul/ASTDataForward.h> +#include <libyul/AsmDataForward.h> #include <libyul/optimiser/ASTWalker.h> #include <libyul/Exceptions.h> -#include <libsolidity/inlineasm/AsmDataForward.h> +#include <libyul/AsmDataForward.h> #include <vector> #include <set> #include <map> -namespace dev -{ namespace yul { @@ -60,4 +58,3 @@ private: }; } -} |