From f0494307232e52dcc268f5f32d26cc89d7e98e3a Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 1 Mar 2016 22:56:39 +0100 Subject: Code generation (missing external access and source locations). --- libsolidity/inlineasm/AsmCodeGen.cpp | 263 +++++++++++++++++++++++++++++++++++ 1 file changed, 263 insertions(+) create mode 100644 libsolidity/inlineasm/AsmCodeGen.cpp (limited to 'libsolidity/inlineasm/AsmCodeGen.cpp') diff --git a/libsolidity/inlineasm/AsmCodeGen.cpp b/libsolidity/inlineasm/AsmCodeGen.cpp new file mode 100644 index 00000000..89e95bc1 --- /dev/null +++ b/libsolidity/inlineasm/AsmCodeGen.cpp @@ -0,0 +1,263 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum 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. + + cpp-ethereum 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 cpp-ethereum. If not, see . +*/ +/** + * @author Christian + * @date 2016 + * Code-generating part of inline assembly. + */ + +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace dev; +using namespace dev::solidity; +using namespace dev::solidity::assembly; + +struct GeneratorState +{ + explicit GeneratorState(ErrorList& _errors): errors(_errors) {} + + void addError(Error::Type _type, std::string const& _description, SourceLocation const& _location = SourceLocation()) + { + auto err = make_shared(_type); + if (!_location.isEmpty()) + *err << errinfo_sourceLocation(_location); + *err << errinfo_comment(_description); + errors.push_back(err); + } + + int const* findVariable(string const& _variableName) const + { + auto localVariable = find_if( + variables.rbegin(), + variables.rend(), + [&](pair const& _var) { return _var.first == _variableName; } + ); + return localVariable != variables.rend() ? &localVariable->second : nullptr; + } + eth::AssemblyItem const* findLabel(string const& _labelName) const + { + auto label = find_if( + labels.begin(), + labels.end(), + [&](pair const& _label) { return _label.first == _labelName; } + ); + return label != labels.end() ? &label->second : nullptr; + } + + eth::Assembly assembly; + map labels; + vector> variables; ///< name plus stack height + ErrorList& errors; +}; + +/** + * Scans the inline assembly data for labels, creates tags in the assembly and searches for + * duplicate labels. + */ +class LabelOrganizer: public boost::static_visitor<> +{ +public: + LabelOrganizer(GeneratorState& _state): m_state(_state) {} + + template + void operator()(T const& /*_item*/) { } + void operator()(Label const& _item) + { + if (m_state.labels.count(_item.name)) + //@TODO location and secondary location + m_state.addError(Error::Type::DeclarationError, "Label " + _item.name + " declared twice."); + m_state.labels.insert(make_pair(_item.name, m_state.assembly.newTag())); + } + void operator()(assembly::Block const& _block) + { + std::for_each(_block.statements.begin(), _block.statements.end(), boost::apply_visitor(*this)); + } + +private: + GeneratorState& m_state; +}; + +class CodeTransform: public boost::static_visitor<> +{ +public: + /// Create the code transformer which appends assembly to _state.assembly when called + /// with parsed assembly data. + /// @param _identifierAccess used to resolve identifiers external to the inline assembly + explicit CodeTransform( + GeneratorState& _state, + assembly::CodeGenerator::IdentifierAccess const& _identifierAccess = assembly::CodeGenerator::IdentifierAccess() + ): + m_state(_state) + { + if (_identifierAccess) + m_identifierAccess = _identifierAccess; + else + m_identifierAccess = [](assembly::Identifier const&, eth::Assembly&, CodeGenerator::IdentifierContext) { return false; }; + } + + void operator()(Instruction const& _instruction) + { + m_state.assembly.append(_instruction.instruction); + } + void operator()(assembly::Literal const& _literal) + { + if (_literal.isNumber) + m_state.assembly.append(u256(_literal.value)); + else if (_literal.value.size() > 32) + m_state.addError( + Error::Type::TypeError, + "String literal too long (" + boost::lexical_cast(_literal.value.size()) + " > 32)" + ); + else + m_state.assembly.append(_literal.value); + } + void operator()(assembly::Identifier const& _identifier) + { + // First search local variables, then labels, then externals. + if (int const* stackHeight = m_state.findVariable(_identifier.name)) + { + int heightDiff = m_state.assembly.deposit() - *stackHeight; + if (heightDiff <= 0 || heightDiff > 16) + //@TODO location + m_state.addError( + Error::Type::TypeError, + "Variable inaccessible, too deep inside stack (" + boost::lexical_cast(heightDiff) + ")" + ); + else + m_state.assembly.append(eth::dupInstruction(heightDiff)); + return; + } + else if (eth::AssemblyItem const* label = m_state.findLabel(_identifier.name)) + m_state.assembly.append(label->pushTag()); + else if (!m_identifierAccess(_identifier, m_state.assembly, CodeGenerator::IdentifierContext::RValue)) + m_state.addError( + Error::Type::DeclarationError, + "Identifier \"" + string(_identifier.name) + "\" not found or not unique" + ); + } + void operator()(FunctionalInstruction const& _instr) + { + for (auto it = _instr.arguments.rbegin(); it != _instr.arguments.rend(); ++it) + { + int height = m_state.assembly.deposit(); + boost::apply_visitor(*this, *it); + expectDeposit(1, height); + } + (*this)(_instr.instruction); + } + void operator()(Label const& _label) + { + m_state.assembly.append(m_state.labels.at(_label.name)); + } + void operator()(assembly::Assignment const& _assignment) + { + generateAssignment(_assignment.variableName); + } + void operator()(FunctionalAssignment const& _assignment) + { + int height = m_state.assembly.deposit(); + boost::apply_visitor(*this, *_assignment.value); + expectDeposit(1, height); + generateAssignment(_assignment.variableName); + } + void operator()(assembly::VariableDeclaration const& _varDecl) + { + int height = m_state.assembly.deposit(); + boost::apply_visitor(*this, *_varDecl.value); + expectDeposit(1, height); + m_state.variables.push_back(make_pair(_varDecl.name, height)); + } + void operator()(assembly::Block const& _block) + { + size_t numVariables = m_state.variables.size(); + std::for_each(_block.statements.begin(), _block.statements.end(), boost::apply_visitor(*this)); + // pop variables + //@TODO check height before and after + while (m_state.variables.size() > numVariables) + { + m_state.assembly.append(eth::Instruction::POP); + m_state.variables.pop_back(); + } + } + +private: + void generateAssignment(assembly::Identifier const& _variableName) + { + if (int const* stackHeight = m_state.findVariable(_variableName.name)) + { + int heightDiff = m_state.assembly.deposit() - *stackHeight - 1; + if (heightDiff <= 0 || heightDiff > 16) + //@TODO location + m_state.addError( + Error::Type::TypeError, + "Variable inaccessible, too deep inside stack (" + boost::lexical_cast(heightDiff) + ")" + ); + else + { + m_state.assembly.append(eth::swapInstruction(heightDiff)); + m_state.assembly.append(eth::Instruction::POP); + } + return; + } + else if (!m_identifierAccess(_variableName, m_state.assembly, CodeGenerator::IdentifierContext::LValue)) + m_state.addError( + Error::Type::DeclarationError, + "Identifier \"" + string(_variableName.name) + "\" not found, not unique or not lvalue." + ); + } + + void expectDeposit(int _deposit, int _oldHeight) + { + if (m_state.assembly.deposit() != _oldHeight + 1) + //@TODO location + m_state.addError(Error::Type::TypeError, + "Expected instruction(s) to deposit " + + boost::lexical_cast(_deposit) + + " item(s) to the stack, but did deposit " + + boost::lexical_cast(m_state.assembly.deposit() - _oldHeight) + + " item(s)." + ); + } + + GeneratorState& m_state; + assembly::CodeGenerator::IdentifierAccess m_identifierAccess; +}; + +bool assembly::CodeGenerator::typeCheck(assembly::CodeGenerator::IdentifierAccess const& _identifierAccess) +{ + size_t initialErrorLen = m_errors.size(); + GeneratorState state(m_errors); + (LabelOrganizer(state))(m_parsedData); + (CodeTransform(state, _identifierAccess))(m_parsedData); + return m_errors.size() == initialErrorLen; +} + +eth::Assembly assembly::CodeGenerator::assemble(assembly::CodeGenerator::IdentifierAccess const& _identifierAccess) +{ + GeneratorState state(m_errors); + (LabelOrganizer(state))(m_parsedData); + (CodeTransform(state, _identifierAccess))(m_parsedData); + return state.assembly; +} + -- cgit v1.2.3