From 59b5e950f42781c083d14a210845148b01e39eb2 Mon Sep 17 00:00:00 2001 From: Christian Date: Mon, 20 Oct 2014 12:41:56 +0200 Subject: Expression compiler. --- Compiler.h | 140 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 Compiler.h (limited to 'Compiler.h') diff --git a/Compiler.h b/Compiler.h new file mode 100644 index 00000000..bddc4bef --- /dev/null +++ b/Compiler.h @@ -0,0 +1,140 @@ +/* + 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 2014 + * Solidity AST to EVM bytecode compiler. + */ + +#include +#include +#include +#include + +namespace dev { +namespace solidity { + +/// A single item of compiled code that can be assembled to a single byte value in the final +/// bytecode. Its main purpose is to inject jump labels and label references into the opcode stream, +/// which can be resolved in the final step. +class AssemblyItem +{ +public: + enum class Type + { + CODE, //< m_data is opcode, m_label is empty. + DATA, //< m_data is actual data, m_label is empty + LABEL, //< m_data is JUMPDEST opcode, m_label is id of label + LABELREF //< m_data is empty, m_label is id of label + }; + + explicit AssemblyItem(eth::Instruction _instruction) : m_type(Type::CODE), m_data(byte(_instruction)) {} + explicit AssemblyItem(byte _data): m_type(Type::DATA), m_data(_data) {} + + /// Factory functions + static AssemblyItem labelRef(uint32_t _label) { return AssemblyItem(Type::LABELREF, 0, _label); } + static AssemblyItem label(uint32_t _label) { return AssemblyItem(Type::LABEL, byte(eth::Instruction::JUMPDEST), _label); } + + Type getType() const { return m_type; } + byte getData() const { return m_data; } + uint32_t getLabel() const { return m_label; } + +private: + AssemblyItem(Type _type, byte _data, uint32_t _label): m_type(_type), m_data(_data), m_label(_label) {} + + Type m_type; + byte m_data; //< data to be written to the bytecode stream (or filled by a label if this is a LABELREF) + uint32_t m_label; //< the id of a label either referenced or defined by this item +}; + +using AssemblyItems = std::vector; + + +/// Context to be shared by all units that compile the same contract. Its current usage only +/// concerns dispensing unique jump label IDs and storing their actual positions in the bytecode +/// stream. +class CompilerContext +{ +public: + CompilerContext(): m_nextLabel(0) {} + uint32_t dispenseNewLabel() { return m_nextLabel++; } + void setLabelPosition(uint32_t _label, uint32_t _position); + uint32_t getLabelPosition(uint32_t _label) const; + +private: + uint32_t m_nextLabel; + + std::map m_labelPositions; +}; + +/// Compiler for expressions, i.e. converts an AST tree whose root is an Expression into a stream +/// of EVM instructions. It needs a compiler context that is the same for the whole compilation +/// unit. +class ExpressionCompiler: public ASTVisitor +{ +public: + ExpressionCompiler(CompilerContext& _compilerContext): m_context(_compilerContext) {} + + /// Compile the given expression and (re-)populate the assembly item list. + void compile(Expression& _expression); + AssemblyItems const& getAssemblyItems() const { return m_assemblyItems; } + bytes getAssembledBytecode() const; + + /// Compile the given expression and return the assembly items right away. + static AssemblyItems compileExpression(CompilerContext& _context, Expression& _expression); + +private: + virtual void endVisit(Assignment& _assignment) override; + virtual void endVisit(UnaryOperation& _unaryOperation) override; + virtual bool visit(BinaryOperation& _binaryOperation) override; + virtual void endVisit(FunctionCall& _functionCall) override; + virtual void endVisit(MemberAccess& _memberAccess) override; + virtual void endVisit(IndexAccess& _indexAccess) override; + virtual void endVisit(Identifier& _identifier) override; + virtual void endVisit(Literal& _literal) override; + + /// Appends code to remove dirty higher order bits in case of an implicit promotion to a wider type. + void cleanHigherOrderBitsIfNeeded(Type const& _typeOnStack, Type const& _targetType); + + /// Append code for various operator types + /// @{ + void appendAndOrOperatorCode(BinaryOperation& _binaryOperation); + void appendCompareOperatorCode(Token::Value _operator, Type const& _type); + void appendOrdinaryBinaryOperatorCode(Token::Value _operator, Type const& _type); + + void appendArithmeticOperatorCode(Token::Value _operator, Type const& _type); + void appendBitOperatorCode(Token::Value _operator); + void appendShiftOperatorCode(Token::Value _operator); + /// @} + + /// Appends a JUMPI instruction to a new label and returns the label + uint32_t appendConditionalJump(); + + /// Append elements to the current instruction list. + void append(eth::Instruction const& _instruction) { m_assemblyItems.push_back(AssemblyItem(_instruction)); } + void append(byte _value) { m_assemblyItems.push_back(AssemblyItem(_value)); } + void append(bytes const& _data); + void appendLabelref(byte _label) { m_assemblyItems.push_back(AssemblyItem::labelRef(_label)); } + void appendLabel(byte _label) { m_assemblyItems.push_back(AssemblyItem::label(_label)); } + + AssemblyItems m_assemblyItems; + CompilerContext& m_context; +}; + + +} +} -- cgit