Skip to content

Commit

Permalink
Initial EVM1.5 assembly implementation.
Browse files Browse the repository at this point in the history
  • Loading branch information
chriseth committed Jun 8, 2017
1 parent 21e0b69 commit 97cc968
Show file tree
Hide file tree
Showing 20 changed files with 857 additions and 139 deletions.
9 changes: 9 additions & 0 deletions libevmasm/Instruction.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,13 @@ enum class Instruction: uint8_t
DIFFICULTY, ///< get the block's difficulty
GASLIMIT, ///< get the block's gas limit

JUMPTO = 0x4a, ///< alter the program counter to a jumpdest -- not part of Instructions.cpp
JUMPIF, ///< conditionally alter the program counter -- not part of Instructions.cpp
JUMPV, ///< alter the program counter to a jumpdest -- not part of Instructions.cpp
JUMPSUB, ///< alter the program counter to a beginsub -- not part of Instructions.cpp
JUMPSUBV, ///< alter the program counter to a beginsub -- not part of Instructions.cpp
RETURNSUB, ///< return to subroutine jumped from -- not part of Instructions.cpp

POP = 0x50, ///< remove item from stack
MLOAD, ///< load word from memory
MSTORE, ///< save word to memory
Expand All @@ -97,6 +104,8 @@ enum class Instruction: uint8_t
MSIZE, ///< get the size of active memory
GAS, ///< get the amount of available gas
JUMPDEST, ///< set a potential jump destination
BEGINSUB, ///< set a potential jumpsub destination -- not part of Instructions.cpp
BEGINDATA, ///< begine the data section -- not part of Instructions.cpp

PUSH1 = 0x60, ///< place 1 byte item on stack
PUSH2, ///< place 2 byte item on stack
Expand Down
20 changes: 19 additions & 1 deletion libjulia/backends/evm/AbstractAssembly.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ struct Identifier;
namespace julia
{

///
/// Assembly class that abstracts both the libevmasm assembly and the new julia evm assembly.
///
class AbstractAssembly
{
public:
Expand All @@ -66,6 +69,21 @@ class AbstractAssembly
/// Append a reference to a to-be-linked symobl.
/// Currently, we assume that the value is always a 20 byte number.
virtual void appendLinkerSymbol(std::string const& _name) = 0;

/// Append a jump instruction.
/// @param _stackDiffAfter the stack adjustment after this instruction.
virtual void appendJump(int _stackDiffAfter) = 0;

/// Append a jump-to-immediate operation.
virtual void appendJumpTo(LabelID _label, int _stackDiffAfter = 0) = 0;
/// Append a jump-to-if-immediate operation.
virtual void appendJumpToIf(LabelID _label) = 0;
/// Start a subroutine.
virtual void appendBeginsub(LabelID _label, int _arguments) = 0;
/// Call a subroutine.
virtual void appendJumpsub(LabelID _label, int _arguments, int _returns) = 0;
/// Return from a subroutine.
virtual void appendReturnsub(int _returns) = 0;
};

enum class IdentifierContext { LValue, RValue };
Expand All @@ -74,7 +92,7 @@ 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)>;
using Resolver = std::function<size_t(solidity::assembly::Identifier const&, IdentifierContext, bool /*_crossesFunctionBoundary*/)>;
/// Resolve a 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;
Expand Down
174 changes: 174 additions & 0 deletions libjulia/backends/evm/EVMAssembly.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
/*
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/>.
*/
/**
* Assembly interface for EVM and EVM1.5.
*/

#include <libjulia/backends/evm/EVMAssembly.h>

#include <libevmasm/Instruction.h>

#include <libsolidity/interface/Utils.h>

using namespace std;
using namespace dev;
using namespace julia;

namespace
{
size_t constexpr labelReferenceSize = 4;
}


void EVMAssembly::setSourceLocation(SourceLocation const&)
{
// Ignored for now;
}

void EVMAssembly::appendInstruction(solidity::Instruction _instr)
{
m_bytecode.push_back(byte(_instr));
m_stackHeight += solidity::instructionInfo(_instr).ret - solidity::instructionInfo(_instr).args;
}

void EVMAssembly::appendConstant(u256 const& _constant)
{
bytes data = toCompactBigEndian(_constant, 1);
appendInstruction(solidity::pushInstruction(data.size()));
m_bytecode += data;
}

void EVMAssembly::appendLabel(LabelID _labelId)
{
setLabelToCurrentPosition(_labelId);
appendInstruction(solidity::Instruction::JUMPDEST);
}

void EVMAssembly::appendLabelReference(LabelID _labelId)
{
solAssert(!m_evm15, "Cannot use plain label references in EMV1.5 mode.");
// @TODO we now always use labelReferenceSize for all labels, it could be shortened
// for some of them.
appendInstruction(solidity::pushInstruction(labelReferenceSize));
m_labelReferences[m_bytecode.size()] = _labelId;
m_bytecode += bytes(labelReferenceSize);
}

EVMAssembly::LabelID EVMAssembly::newLabelId()
{
m_labelPositions[m_nextLabelID] = size_t(-1);
return m_nextLabelID++;
}

void EVMAssembly::appendLinkerSymbol(string const&)
{
solAssert(false, "Linker symbols not yet implemented.");
}

void EVMAssembly::appendJump(int _stackDiffAfter)
{
solAssert(!m_evm15, "Plain JUMP used for EVM 1.5");
appendInstruction(solidity::Instruction::JUMP);
m_stackHeight += _stackDiffAfter;
}

void EVMAssembly::appendJumpTo(AbstractAssembly::LabelID _labelId, int _stackDiffAfter)
{
if (m_evm15)
{
m_bytecode.push_back(byte(solidity::Instruction::JUMPTO));
appendLabelReferenceInternal(_labelId);
m_stackHeight += _stackDiffAfter;
}
else
{
appendLabelReference(_labelId);
appendJump(_stackDiffAfter);
}
}

void EVMAssembly::appendJumpToIf(AbstractAssembly::LabelID _labelId)
{
if (m_evm15)
{
m_bytecode.push_back(byte(solidity::Instruction::JUMPIF));
appendLabelReferenceInternal(_labelId);
m_stackHeight--;
}
else
{
appendLabelReference(_labelId);
appendInstruction(solidity::Instruction::JUMPI);
}
}

void EVMAssembly::appendBeginsub(AbstractAssembly::LabelID _labelId, int _arguments)
{
solAssert(m_evm15, "BEGINSUB used for EVM 1.0");
solAssert(_arguments >= 0, "");
setLabelToCurrentPosition(_labelId);
m_bytecode.push_back(byte(solidity::Instruction::BEGINSUB));
m_stackHeight += _arguments;
}

void EVMAssembly::appendJumpsub(AbstractAssembly::LabelID _labelId, int _arguments, int _returns)
{
solAssert(m_evm15, "JUMPSUB used for EVM 1.0");
solAssert(_arguments >= 0 && _returns >= 0, "");
m_bytecode.push_back(byte(solidity::Instruction::JUMPSUB));
appendLabelReferenceInternal(_labelId);
m_stackHeight += _returns - _arguments;
}

void EVMAssembly::appendReturnsub(int _returns)
{
solAssert(m_evm15, "RETURNSUB used for EVM 1.0");
solAssert(_returns >= 0, "");
m_bytecode.push_back(byte(solidity::Instruction::RETURNSUB));
m_stackHeight -= _returns;
}

eth::LinkerObject EVMAssembly::finalize()
{
for (auto const& ref: m_labelReferences)
{
size_t referencePos = ref.first;
solAssert(m_labelPositions.count(ref.second), "");
size_t labelPos = m_labelPositions.at(ref.second);
solAssert(labelPos != size_t(-1), "Undefined but allocated label used.");
solAssert(m_bytecode.size() >= 4 && referencePos <= m_bytecode.size() - 4, "");
solAssert(labelPos < (uint64_t(1) << (8 * labelReferenceSize)), "");
for (size_t i = 0; i < labelReferenceSize; i++)
m_bytecode[referencePos + i] = byte((labelPos >> (8 * (labelReferenceSize - i - 1))) & 0xff);
}
eth::LinkerObject obj;
obj.bytecode = m_bytecode;
return obj;
}

void EVMAssembly::setLabelToCurrentPosition(AbstractAssembly::LabelID _labelId)
{
solAssert(m_labelPositions.count(_labelId), "Label not found.");
solAssert(m_labelPositions[_labelId] == size_t(-1), "Label already set.");
m_labelPositions[_labelId] = m_bytecode.size();
}

void EVMAssembly::appendLabelReferenceInternal(AbstractAssembly::LabelID _labelId)
{
m_labelReferences[m_bytecode.size()] = _labelId;
m_bytecode += bytes(labelReferenceSize);
}
90 changes: 90 additions & 0 deletions libjulia/backends/evm/EVMAssembly.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
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/>.
*/
/**
* Assembly interface for EVM and EVM1.5.
*/

#pragma once

#include <libjulia/backends/evm/AbstractAssembly.h>

#include <libevmasm/LinkerObject.h>

#include <map>

namespace dev
{
namespace julia
{

class EVMAssembly: public AbstractAssembly
{
public:
explicit EVMAssembly(bool _evm15 = false): m_evm15(_evm15) { }
virtual ~EVMAssembly() {}

/// Set a new source location valid starting from the next instruction.
virtual void setSourceLocation(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; }
/// Append an EVM instruction.
virtual void appendInstruction(solidity::Instruction _instruction) override;
/// Append a constant.
virtual void appendConstant(u256 const& _constant) override;
/// Append a label.
virtual void appendLabel(LabelID _labelId) override;
/// Append a label reference.
virtual void appendLabelReference(LabelID _labelId) override;
/// Generate a new unique label.
virtual LabelID newLabelId() override;
/// Append a reference to a to-be-linked symobl.
/// Currently, we assume that the value is always a 20 byte number.
virtual 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;
/// Append a jump-to-immediate operation.
virtual void appendJumpTo(LabelID _label, int _stackDiffAfter) override;
/// Append a jump-to-if-immediate operation.
virtual void appendJumpToIf(LabelID _label) override;
/// Start a subroutine.
virtual void appendBeginsub(LabelID _label, int _arguments) override;
/// Call a subroutine.
virtual void appendJumpsub(LabelID _label, int _arguments, int _returns) override;
/// Return from a subroutine.
virtual void appendReturnsub(int _returns) override;


/// Resolves references inside the bytecode and returns the linker object.
eth::LinkerObject finalize();

private:
void setLabelToCurrentPosition(AbstractAssembly::LabelID _labelId);
void appendLabelReferenceInternal(AbstractAssembly::LabelID _labelId);

bool m_evm15 = false; ///< if true, switch to evm1.5 mode
LabelID m_nextLabelID = 0;
int m_stackHeight = 0;
bytes m_bytecode;
std::map<LabelID, size_t> m_labelPositions;
std::map<size_t, LabelID> m_labelReferences;
};

}
}
Loading

0 comments on commit 97cc968

Please sign in to comment.