/*
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 .
*/
#include "interpreter.h"
#include "VM.h"
#include
extern "C" evmc_instance* evmc_create_interpreter() noexcept
{
return new (std::nothrow) dev::eth::VM;
}
namespace
{
void destroy(evmc_instance* _instance)
{
delete static_cast(_instance);
}
void delete_output(const evmc_result* result)
{
delete[] result->output_data;
}
evmc_result execute(evmc_instance* _instance, evmc_context* _context, evmc_revision _rev,
const evmc_message* _msg, uint8_t const* _code, size_t _codeSize) noexcept
{
auto vm = static_cast(_instance);
evmc_result result = {};
dev::eth::owning_bytes_ref output;
try
{
output = vm->exec(_context, _rev, _msg, _code, _codeSize);
result.status_code = EVMC_SUCCESS;
result.gas_left = vm->m_io_gas;
}
catch (dev::eth::RevertInstruction& ex)
{
result.status_code = EVMC_REVERT;
result.gas_left = vm->m_io_gas;
output = ex.output(); // This moves the output from the exception!
}
catch (dev::eth::VMException const&)
{
result.status_code = EVMC_FAILURE;
}
catch (...)
{
result.status_code = EVMC_INTERNAL_ERROR;
}
if (!output.empty())
{
// Make a copy of the output.
auto outputData = new uint8_t[output.size()];
std::memcpy(outputData, output.data(), output.size());
result.output_data = outputData;
result.output_size = output.size();
result.release = delete_output;
}
return result;
}
}
namespace dev
{
namespace eth
{
VM::VM()
: evmc_instance{
EVMC_ABI_VERSION,
"interpreter",
eth_get_buildinfo()->project_version,
::destroy,
::execute,
nullptr,
}
{}
uint64_t VM::memNeed(u256 _offset, u256 _size)
{
return toInt63(_size ? u512(_offset) + _size : u512(0));
}
template S divWorkaround(S const& _a, S const& _b)
{
return (S)(s512(_a) / s512(_b));
}
template S modWorkaround(S const& _a, S const& _b)
{
return (S)(s512(_a) % s512(_b));
}
//
// for decoding destinations of JUMPTO, JUMPV, JUMPSUB and JUMPSUBV
//
uint64_t VM::decodeJumpDest(const byte* const _code, uint64_t& _pc)
{
// turn 2 MSB-first bytes in the code into a native-order integer
uint64_t dest = _code[_pc++];
dest = (dest << 8) | _code[_pc++];
return dest;
}
uint64_t VM::decodeJumpvDest(const byte* const _code, uint64_t& _pc, byte _voff)
{
// Layout of jump table in bytecode...
// byte opcode
// byte n_jumps
// byte table[n_jumps][2]
//
uint64_t pc = _pc;
byte n = _code[++pc]; // byte after opcode is number of jumps
if (_voff >= n) _voff = n - 1; // if offset overflows use default jump
pc += _voff * 2; // adjust inout pc before index destination in table
uint64_t dest = decodeJumpDest(_code, pc);
_pc += 1 + n * 2; // adust inout _pc to opcode after table
return dest;
}
//
// set current SP to SP', adjust SP' per _removed and _added items
//
void VM::adjustStack(unsigned _removed, unsigned _added)
{
m_SP = m_SPP;
// adjust stack and check bounds
m_SPP += _removed;
if (m_stackEnd < m_SPP)
throwBadStack(_removed, _added);
m_SPP -= _added;
if (m_SPP < m_stack)
throwBadStack(_removed, _added);
}
void VM::updateSSGas()
{
evmc_uint256be key = toEvmC(m_SP[0]);
evmc_uint256be rawValue;
m_context->fn_table->get_storage(&rawValue, m_context, &m_message->destination, &key);
u256 value = fromEvmC(rawValue);
m_runGas = (!value && m_SP[1]) ? VMSchedule::sstoreSetGas : VMSchedule::sstoreResetGas;
}
uint64_t VM::gasForMem(u512 _size)
{
constexpr int64_t memoryGas = VMSchedule::memoryGas;
constexpr int64_t quadCoeffDiv = VMSchedule::quadCoeffDiv;
u512 s = _size / 32;
return toInt63(memoryGas * s + s * s / quadCoeffDiv);
}
void VM::updateIOGas()
{
if (m_io_gas < m_runGas)
throwOutOfGas();
m_io_gas -= m_runGas;
}
void VM::updateGas()
{
if (m_newMemSize > m_mem.size())
m_runGas += toInt63(gasForMem(m_newMemSize) - gasForMem(m_mem.size()));
m_runGas += (VMSchedule::copyGas * ((m_copyMemSize + 31) / 32));
if (m_io_gas < m_runGas)
throwOutOfGas();
}
void VM::updateMem(uint64_t _newMem)
{
m_newMemSize = (_newMem + 31) / 32 * 32;
updateGas();
if (m_newMemSize > m_mem.size())
m_mem.resize(m_newMemSize);
}
void VM::logGasMem()
{
unsigned n = (unsigned) m_OP - (unsigned) Instruction::LOG0;
constexpr int64_t logDataGas = VMSchedule::logDataGas;
m_runGas = toInt63(
VMSchedule::logGas + VMSchedule::logTopicGas * n + logDataGas * u512(m_SP[1]));
updateMem(memNeed(m_SP[0], m_SP[1]));
}
void VM::fetchInstruction()
{
m_OP = Instruction(m_code[m_PC]);
const InstructionMetric& metric = c_metrics[static_cast(m_OP)];
adjustStack(metric.args, metric.ret);
// FEES...
std::array tierStepGas{
{VMSchedule::stepGas0, VMSchedule::stepGas1, VMSchedule::stepGas2, VMSchedule::stepGas3,
VMSchedule::stepGas4, VMSchedule::stepGas5, VMSchedule::stepGas6, 0, 0}};
m_runGas = tierStepGas[static_cast(metric.gasPriceTier)];
m_newMemSize = m_mem.size();
m_copyMemSize = 0;
}
evmc_tx_context const& VM::getTxContext()
{
if (!m_tx_context)
{
m_tx_context.emplace();
m_context->fn_table->get_tx_context(&m_tx_context.value(), m_context);
}
return m_tx_context.value();
}
///////////////////////////////////////////////////////////////////////////////
//
// interpreter entry point
owning_bytes_ref VM::exec(evmc_context* _context, evmc_revision _rev, const evmc_message* _msg,
uint8_t const* _code, size_t _codeSize)
{
m_context = _context;
m_rev = _rev;
m_message = _msg;
m_io_gas = uint64_t(_msg->gas);
m_PC = 0;
m_pCode = _code;
m_codeSize = _codeSize;
// trampoline to minimize depth of call stack when calling out
m_bounce = &VM::initEntry;
do
(this->*m_bounce)();
while (m_bounce);
return std::move(m_output);
}
//
// main interpreter loop and switch
//
void VM::interpretCases()
{
INIT_CASES
DO_CASES
{
//
// Call-related instructions
//
CASE(CREATE2)
{
ON_OP();
// TODO: Bring back support for CREATE2.
throwBadInstruction();
m_bounce = &VM::caseCreate;
}
BREAK
CASE(CREATE)
{
ON_OP();
if (m_message->flags & EVMC_STATIC)
throwDisallowedStateChange();
m_bounce = &VM::caseCreate;
}
BREAK
CASE(DELEGATECALL)
CASE(STATICCALL)
CASE(CALL)
CASE(CALLCODE)
{
ON_OP();
if (m_OP == Instruction::DELEGATECALL && m_rev < EVMC_HOMESTEAD)
throwBadInstruction();
if (m_OP == Instruction::STATICCALL && m_rev < EVMC_BYZANTIUM)
throwBadInstruction();
if (m_OP == Instruction::CALL && m_message->flags & EVMC_STATIC && m_SP[2] != 0)
throwDisallowedStateChange();
m_bounce = &VM::caseCall;
}
BREAK
CASE(RETURN)
{
ON_OP();
m_copyMemSize = 0;
updateMem(memNeed(m_SP[0], m_SP[1]));
updateIOGas();
uint64_t b = (uint64_t)m_SP[0];
uint64_t s = (uint64_t)m_SP[1];
m_output = owning_bytes_ref{std::move(m_mem), b, s};
m_bounce = 0;
}
BREAK
CASE(REVERT)
{
// Pre-byzantium
if (m_rev < EVMC_BYZANTIUM)
throwBadInstruction();
ON_OP();
m_copyMemSize = 0;
updateMem(memNeed(m_SP[0], m_SP[1]));
updateIOGas();
uint64_t b = (uint64_t)m_SP[0];
uint64_t s = (uint64_t)m_SP[1];
owning_bytes_ref output{std::move(m_mem), b, s};
throwRevertInstruction(std::move(output));
}
BREAK;
CASE(SUICIDE)
{
ON_OP();
if (m_message->flags & EVMC_STATIC)
throwDisallowedStateChange();
m_runGas = m_rev >= EVMC_TANGERINE_WHISTLE ? 5000 : 0;
evmc_address destination = toEvmC(asAddress(m_SP[0]));
// After EIP158 zero-value suicides do not have to pay account creation gas.
evmc_uint256be rawBalance;
m_context->fn_table->get_balance(&rawBalance, m_context, &m_message->destination);
u256 balance = fromEvmC(rawBalance);
if (balance > 0 || m_rev < EVMC_SPURIOUS_DRAGON)
{
// After EIP150 hard fork charge additional cost of sending
// ethers to non-existing account.
int destinationExists =
m_context->fn_table->account_exists(m_context, &destination);
if (m_rev >= EVMC_TANGERINE_WHISTLE && !destinationExists)
m_runGas += VMSchedule::callNewAccount;
}
updateIOGas();
m_context->fn_table->selfdestruct(m_context, &m_message->destination, &destination);
m_bounce = nullptr;
}
BREAK
CASE(STOP)
{
ON_OP();
updateIOGas();
m_bounce = 0;
}
BREAK
//
// instructions potentially expanding memory
//
CASE(MLOAD)
{
ON_OP();
updateMem(toInt63(m_SP[0]) + 32);
updateIOGas();
m_SPP[0] = (u256)*(h256 const*)(m_mem.data() + (unsigned)m_SP[0]);
}
NEXT
CASE(MSTORE)
{
ON_OP();
updateMem(toInt63(m_SP[0]) + 32);
updateIOGas();
*(h256*)&m_mem[(unsigned)m_SP[0]] = (h256)m_SP[1];
}
NEXT
CASE(MSTORE8)
{
ON_OP();
updateMem(toInt63(m_SP[0]) + 1);
updateIOGas();
m_mem[(unsigned)m_SP[0]] = (byte)(m_SP[1] & 0xff);
}
NEXT
CASE(SHA3)
{
ON_OP();
constexpr int64_t sha3Gas = VMSchedule::sha3Gas;
constexpr int64_t sha3WordGas = VMSchedule::sha3WordGas;
m_runGas = toInt63(sha3Gas + (u512(m_SP[1]) + 31) / 32 * sha3WordGas);
updateMem(memNeed(m_SP[0], m_SP[1]));
updateIOGas();
uint64_t inOff = (uint64_t)m_SP[0];
uint64_t inSize = (uint64_t)m_SP[1];
m_SPP[0] = (u256)sha3(bytesConstRef(m_mem.data() + inOff, inSize));
}
NEXT
CASE(LOG0)
{
ON_OP();
if (m_message->flags & EVMC_STATIC)
throwDisallowedStateChange();
logGasMem();
updateIOGas();
uint8_t const* data = m_mem.data() + size_t(m_SP[0]);
size_t dataSize = size_t(m_SP[1]);
m_context->fn_table->emit_log(
m_context, &m_message->destination, data, dataSize, nullptr, 0);
}
NEXT
CASE(LOG1)
{
ON_OP();
if (m_message->flags & EVMC_STATIC)
throwDisallowedStateChange();
logGasMem();
updateIOGas();
uint8_t const* data = m_mem.data() + size_t(m_SP[0]);
size_t dataSize = size_t(m_SP[1]);
evmc_uint256be topics[] = {toEvmC(m_SP[2])};
size_t numTopics = sizeof(topics) / sizeof(topics[0]);
m_context->fn_table->emit_log(
m_context, &m_message->destination, data, dataSize, topics, numTopics);
}
NEXT
CASE(LOG2)
{
ON_OP();
if (m_message->flags & EVMC_STATIC)
throwDisallowedStateChange();
logGasMem();
updateIOGas();
uint8_t const* data = m_mem.data() + size_t(m_SP[0]);
size_t dataSize = size_t(m_SP[1]);
evmc_uint256be topics[] = {toEvmC(m_SP[2]), toEvmC(m_SP[3])};
size_t numTopics = sizeof(topics) / sizeof(topics[0]);
m_context->fn_table->emit_log(
m_context, &m_message->destination, data, dataSize, topics, numTopics);
}
NEXT
CASE(LOG3)
{
ON_OP();
if (m_message->flags & EVMC_STATIC)
throwDisallowedStateChange();
logGasMem();
updateIOGas();
uint8_t const* data = m_mem.data() + size_t(m_SP[0]);
size_t dataSize = size_t(m_SP[1]);
evmc_uint256be topics[] = {toEvmC(m_SP[2]), toEvmC(m_SP[3]), toEvmC(m_SP[4])};
size_t numTopics = sizeof(topics) / sizeof(topics[0]);
m_context->fn_table->emit_log(
m_context, &m_message->destination, data, dataSize, topics, numTopics);
}
NEXT
CASE(LOG4)
{
ON_OP();
if (m_message->flags & EVMC_STATIC)
throwDisallowedStateChange();
logGasMem();
updateIOGas();
uint8_t const* data = m_mem.data() + size_t(m_SP[0]);
size_t dataSize = size_t(m_SP[1]);
evmc_uint256be topics[] = {
toEvmC(m_SP[2]), toEvmC(m_SP[3]), toEvmC(m_SP[4]), toEvmC(m_SP[5])};
size_t numTopics = sizeof(topics) / sizeof(topics[0]);
m_context->fn_table->emit_log(
m_context, &m_message->destination, data, dataSize, topics, numTopics);
}
NEXT
CASE(EXP)
{
u256 expon = m_SP[1];
const int64_t byteCost = m_rev >= EVMC_SPURIOUS_DRAGON ? 50 : 10;
m_runGas = toInt63(VMSchedule::stepGas5 + byteCost * (32 - (h256(expon).firstBitSet() / 8)));
ON_OP();
updateIOGas();
u256 base = m_SP[0];
m_SPP[0] = exp256(base, expon);
}
NEXT
//
// ordinary instructions
//
CASE(ADD)
{
ON_OP();
updateIOGas();
//pops two items and pushes their sum mod 2^256.
m_SPP[0] = m_SP[0] + m_SP[1];
}
NEXT
CASE(MUL)
{
ON_OP();
updateIOGas();
//pops two items and pushes their product mod 2^256.
m_SPP[0] = m_SP[0] * m_SP[1];
}
NEXT
CASE(SUB)
{
ON_OP();
updateIOGas();
m_SPP[0] = m_SP[0] - m_SP[1];
}
NEXT
CASE(DIV)
{
ON_OP();
updateIOGas();
m_SPP[0] = m_SP[1] ? divWorkaround(m_SP[0], m_SP[1]) : 0;
}
NEXT
CASE(SDIV)
{
ON_OP();
updateIOGas();
m_SPP[0] = m_SP[1] ? s2u(divWorkaround(u2s(m_SP[0]), u2s(m_SP[1]))) : 0;
--m_SP;
}
NEXT
CASE(MOD)
{
ON_OP();
updateIOGas();
m_SPP[0] = m_SP[1] ? modWorkaround(m_SP[0], m_SP[1]) : 0;
}
NEXT
CASE(SMOD)
{
ON_OP();
updateIOGas();
m_SPP[0] = m_SP[1] ? s2u(modWorkaround(u2s(m_SP[0]), u2s(m_SP[1]))) : 0;
}
NEXT
CASE(NOT)
{
ON_OP();
updateIOGas();
m_SPP[0] = ~m_SP[0];
}
NEXT
CASE(LT)
{
ON_OP();
updateIOGas();
m_SPP[0] = m_SP[0] < m_SP[1] ? 1 : 0;
}
NEXT
CASE(GT)
{
ON_OP();
updateIOGas();
m_SPP[0] = m_SP[0] > m_SP[1] ? 1 : 0;
}
NEXT
CASE(SLT)
{
ON_OP();
updateIOGas();
m_SPP[0] = u2s(m_SP[0]) < u2s(m_SP[1]) ? 1 : 0;
}
NEXT
CASE(SGT)
{
ON_OP();
updateIOGas();
m_SPP[0] = u2s(m_SP[0]) > u2s(m_SP[1]) ? 1 : 0;
}
NEXT
CASE(EQ)
{
ON_OP();
updateIOGas();
m_SPP[0] = m_SP[0] == m_SP[1] ? 1 : 0;
}
NEXT
CASE(ISZERO)
{
ON_OP();
updateIOGas();
m_SPP[0] = m_SP[0] ? 0 : 1;
}
NEXT
CASE(AND)
{
ON_OP();
updateIOGas();
m_SPP[0] = m_SP[0] & m_SP[1];
}
NEXT
CASE(OR)
{
ON_OP();
updateIOGas();
m_SPP[0] = m_SP[0] | m_SP[1];
}
NEXT
CASE(XOR)
{
ON_OP();
updateIOGas();
m_SPP[0] = m_SP[0] ^ m_SP[1];
}
NEXT
CASE(BYTE)
{
ON_OP();
updateIOGas();
m_SPP[0] = m_SP[0] < 32 ? (m_SP[1] >> (unsigned)(8 * (31 - m_SP[0]))) & 0xff : 0;
}
NEXT
CASE(SHL)
{
// Pre-constantinople
if (m_rev < EVMC_CONSTANTINOPLE)
throwBadInstruction();
ON_OP();
updateIOGas();
if (m_SP[0] >= 256)
m_SPP[0] = 0;
else
m_SPP[0] = m_SP[1] << unsigned(m_SP[0]);
}
NEXT
CASE(SHR)
{
// Pre-constantinople
if (m_rev < EVMC_CONSTANTINOPLE)
throwBadInstruction();
ON_OP();
updateIOGas();
if (m_SP[0] >= 256)
m_SPP[0] = 0;
else
m_SPP[0] = m_SP[1] >> unsigned(m_SP[0]);
}
NEXT
CASE(SAR)
{
// Pre-constantinople
if (m_rev < EVMC_CONSTANTINOPLE)
throwBadInstruction();
ON_OP();
updateIOGas();
static u256 const hibit = u256(1) << 255;
static u256 const allbits =
u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
u256 shiftee = m_SP[1];
if (m_SP[0] >= 256)
{
if (shiftee & hibit)
m_SPP[0] = allbits;
else
m_SPP[0] = 0;
}
else
{
unsigned amount = unsigned(m_SP[0]);
m_SPP[0] = shiftee >> amount;
if (shiftee & hibit)
m_SPP[0] |= allbits << (256 - amount);
}
}
NEXT
CASE(ADDMOD)
{
ON_OP();
updateIOGas();
m_SPP[0] = m_SP[2] ? u256((u512(m_SP[0]) + u512(m_SP[1])) % m_SP[2]) : 0;
}
NEXT
CASE(MULMOD)
{
ON_OP();
updateIOGas();
m_SPP[0] = m_SP[2] ? u256((u512(m_SP[0]) * u512(m_SP[1])) % m_SP[2]) : 0;
}
NEXT
CASE(SIGNEXTEND)
{
ON_OP();
updateIOGas();
if (m_SP[0] < 31)
{
unsigned testBit = static_cast(m_SP[0]) * 8 + 7;
u256& number = m_SP[1];
u256 mask = ((u256(1) << testBit) - 1);
if (boost::multiprecision::bit_test(number, testBit))
number |= ~mask;
else
number &= mask;
}
}
NEXT
#if EIP_615
CASE(JUMPTO)
{
ON_OP();
updateIOGas();
m_PC = decodeJumpDest(m_code.data(), m_PC);
}
CONTINUE
CASE(JUMPIF)
{
ON_OP();
updateIOGas();
if (m_SP[0])
m_PC = decodeJumpDest(m_code.data(), m_PC);
else
++m_PC;
}
CONTINUE
CASE(JUMPV)
{
ON_OP();
updateIOGas();
m_PC = decodeJumpvDest(m_code.data(), m_PC, byte(m_SP[0]));
}
CONTINUE
CASE(JUMPSUB)
{
ON_OP();
updateIOGas();
*m_RP++ = m_PC++;
m_PC = decodeJumpDest(m_code.data(), m_PC);
}
CONTINUE
CASE(JUMPSUBV)
{
ON_OP();
updateIOGas();
*m_RP++ = m_PC;
m_PC = decodeJumpvDest(m_code.data(), m_PC, byte(m_SP[0]));
}
CONTINUE
CASE(RETURNSUB)
{
ON_OP();
updateIOGas();
m_PC = *m_RP--;
}
NEXT
CASE(BEGINSUB)
{
ON_OP();
updateIOGas();
}
NEXT
CASE(BEGINDATA)
{
ON_OP();
updateIOGas();
}
NEXT
CASE(GETLOCAL)
{
ON_OP();
updateIOGas();
}
NEXT
CASE(PUTLOCAL)
{
ON_OP();
updateIOGas();
}
NEXT
#else
CASE(JUMPTO)
CASE(JUMPIF)
CASE(JUMPV)
CASE(JUMPSUB)
CASE(JUMPSUBV)
CASE(RETURNSUB)
CASE(BEGINSUB)
CASE(BEGINDATA)
CASE(GETLOCAL)
CASE(PUTLOCAL)
{
throwBadInstruction();
}
CONTINUE
#endif
#if EIP_616
CASE(XADD)
{
ON_OP();
updateIOGas();
xadd(simdType());
}
CONTINUE
CASE(XMUL)
{
ON_OP();
updateIOGas();
xmul(simdType());
}
CONTINUE
CASE(XSUB)
{
ON_OP();
updateIOGas();
xsub(simdType());
}
CONTINUE
CASE(XDIV)
{
ON_OP();
updateIOGas();
xdiv(simdType());
}
CONTINUE
CASE(XSDIV)
{
ON_OP();
updateIOGas();
xsdiv(simdType());
}
CONTINUE
CASE(XMOD)
{
ON_OP();
updateIOGas();
xmod(simdType());
}
CONTINUE
CASE(XSMOD)
{
ON_OP();
updateIOGas();
xsmod(simdType());
}
CONTINUE
CASE(XLT)
{
ON_OP();
updateIOGas();
xlt(simdType());
}
CONTINUE
CASE(XGT)
{
ON_OP();
updateIOGas();
xgt(simdType());
}
CONTINUE
CASE(XSLT)
{
ON_OP();
updateIOGas();
xslt(simdType());
}
CONTINUE
CASE(XSGT)
{
ON_OP();
updateIOGas();
xsgt(simdType());
}
CONTINUE
CASE(XEQ)
{
ON_OP();
updateIOGas();
xeq(simdType());
}
CONTINUE
CASE(XISZERO)
{
ON_OP();
updateIOGas();
xzero(simdType());
}
CONTINUE
CASE(XAND)
{
ON_OP();
updateIOGas();
xand(simdType());
}
CONTINUE
CASE(XOOR)
{
ON_OP();
updateIOGas();
xoor(simdType());
}
CONTINUE
CASE(XXOR)
{
ON_OP();
updateIOGas();
xxor(simdType());
}
CONTINUE
CASE(XNOT)
{
ON_OP();
updateIOGas();
xnot(simdType());
}
CONTINUE
CASE(XSHL)
{
ON_OP();
updateIOGas();
xshl(simdType());
}
CONTINUE
CASE(XSHR)
{
ON_OP();
updateIOGas();
xshr(simdType());
}
CONTINUE
CASE(XSAR)
{
ON_OP();
updateIOGas();
xsar(simdType());
}
CONTINUE
CASE(XROL)
{
ON_OP();
updateIOGas();
xrol(simdType());
}
CONTINUE
CASE(XROR)
{
ON_OP();
updateIOGas();
xror(simdType());
}
CONTINUE
CASE(XMLOAD)
{
updateMem(toInt63(m_SP[0]) + 32);
ON_OP();
updateIOGas();
xmload(simdType());
}
CONTINUE
CASE(XMSTORE)
{
updateMem(toInt63(m_SP[0]) + 32);
ON_OP();
updateIOGas();
xmstore(simdType());
}
CONTINUE
CASE(XSLOAD)
{
m_runGas = toInt63(m_schedule->sloadGas);
ON_OP();
updateIOGas();
xsload(simdType());
}
CONTINUE
CASE(XSSTORE)
{
if (m_message->flags & EVMC_STATIC)
throwDisallowedStateChange();
updateSSGas();
ON_OP();
updateIOGas();
xsstore(simdType());
}
CONTINUE
CASE(XVTOWIDE)
{
ON_OP();
updateIOGas();
xvtowide(simdType());
}
CONTINUE
CASE(XWIDETOV)
{
ON_OP();
updateIOGas();
xwidetov(simdType());
}
CONTINUE
CASE(XPUSH)
{
ON_OP();
updateIOGas();
xpush(simdType());
}
CONTINUE
CASE(XPUT)
{
ON_OP();
updateIOGas();
uint8_t b = ++m_PC;
uint8_t c = ++m_PC;
xput(m_code[b], m_code[c]);
++m_PC;
}
CONTINUE
CASE(XGET)
{
ON_OP();
updateIOGas();
uint8_t b = ++m_PC;
uint8_t c = ++m_PC;
xget(m_code[b], m_code[c]);
++m_PC;
}
CONTINUE
CASE(XSWIZZLE)
{
ON_OP();
updateIOGas();
xswizzle(simdType());
}
CONTINUE
CASE(XSHUFFLE)
{
ON_OP();
updateIOGas();
xshuffle(simdType());
}
CONTINUE
#else
CASE(XADD)
CASE(XMUL)
CASE(XSUB)
CASE(XDIV)
CASE(XSDIV)
CASE(XMOD)
CASE(XSMOD)
CASE(XLT)
CASE(XGT)
CASE(XSLT)
CASE(XSGT)
CASE(XEQ)
CASE(XISZERO)
CASE(XAND)
CASE(XOOR)
CASE(XXOR)
CASE(XNOT)
CASE(XSHL)
CASE(XSHR)
CASE(XSAR)
CASE(XROL)
CASE(XROR)
CASE(XMLOAD)
CASE(XMSTORE)
CASE(XSLOAD)
CASE(XSSTORE)
CASE(XVTOWIDE)
CASE(XWIDETOV)
CASE(XPUSH)
CASE(XPUT)
CASE(XGET)
CASE(XSWIZZLE)
CASE(XSHUFFLE)
{
throwBadInstruction();
}
CONTINUE
#endif
CASE(ADDRESS)
{
ON_OP();
updateIOGas();
m_SPP[0] = fromAddress(fromEvmC(m_message->destination));
}
NEXT
CASE(ORIGIN)
{
ON_OP();
updateIOGas();
m_SPP[0] = fromAddress(fromEvmC(getTxContext().tx_origin));
}
NEXT
CASE(BALANCE)
{
m_runGas = m_rev >= EVMC_TANGERINE_WHISTLE ? 400 : 20;
ON_OP();
updateIOGas();
evmc_address address = toEvmC(asAddress(m_SP[0]));
evmc_uint256be rawBalance;
m_context->fn_table->get_balance(&rawBalance, m_context, &address);
m_SPP[0] = fromEvmC(rawBalance);
}
NEXT
CASE(CALLER)
{
ON_OP();
updateIOGas();
m_SPP[0] = fromAddress(fromEvmC(m_message->sender));
}
NEXT
CASE(CALLVALUE)
{
ON_OP();
updateIOGas();
m_SPP[0] = fromEvmC(m_message->value);
}
NEXT
CASE(CALLDATALOAD)
{
ON_OP();
updateIOGas();
size_t const dataSize = m_message->input_size;
uint8_t const* const data = m_message->input_data;
if (u512(m_SP[0]) + 31 < dataSize)
m_SP[0] = (u256)*(h256 const*)(data + (size_t)m_SP[0]);
else if (m_SP[0] >= dataSize)
m_SP[0] = u256(0);
else
{ h256 r;
for (uint64_t i = (uint64_t)m_SP[0], e = (uint64_t)m_SP[0] + (uint64_t)32, j = 0; i < e; ++i, ++j)
r[j] = i < dataSize ? data[i] : 0;
m_SP[0] = (u256)r;
};
}
NEXT
CASE(CALLDATASIZE)
{
ON_OP();
updateIOGas();
m_SPP[0] = m_message->input_size;
}
NEXT
CASE(RETURNDATASIZE)
{
if (m_rev < EVMC_BYZANTIUM)
throwBadInstruction();
ON_OP();
updateIOGas();
m_SPP[0] = m_returnData.size();
}
NEXT
CASE(CODESIZE)
{
ON_OP();
updateIOGas();
m_SPP[0] = m_codeSize;
}
NEXT
CASE(EXTCODESIZE)
{
m_runGas = m_rev >= EVMC_TANGERINE_WHISTLE ? 700 : 20;
ON_OP();
updateIOGas();
evmc_address address = toEvmC(asAddress(m_SP[0]));
m_SPP[0] = m_context->fn_table->get_code_size(m_context, &address);
}
NEXT
CASE(CALLDATACOPY)
{
ON_OP();
m_copyMemSize = toInt63(m_SP[2]);
updateMem(memNeed(m_SP[0], m_SP[2]));
updateIOGas();
bytesConstRef data{m_message->input_data, m_message->input_size};
copyDataToMemory(data, m_SP);
}
NEXT
CASE(RETURNDATACOPY)
{
ON_OP();
if (m_rev < EVMC_BYZANTIUM)
throwBadInstruction();
bigint const endOfAccess = bigint(m_SP[1]) + bigint(m_SP[2]);
if (m_returnData.size() < endOfAccess)
throwBufferOverrun(endOfAccess);
m_copyMemSize = toInt63(m_SP[2]);
updateMem(memNeed(m_SP[0], m_SP[2]));
updateIOGas();
copyDataToMemory(&m_returnData, m_SP);
}
NEXT
CASE(CODECOPY)
{
ON_OP();
m_copyMemSize = toInt63(m_SP[2]);
updateMem(memNeed(m_SP[0], m_SP[2]));
updateIOGas();
copyDataToMemory({m_pCode, m_codeSize}, m_SP);
}
NEXT
CASE(EXTCODECOPY)
{
ON_OP();
m_runGas = m_rev >= EVMC_TANGERINE_WHISTLE ? 700 : 20;
uint64_t copyMemSize = toInt63(m_SP[3]);
m_copyMemSize = copyMemSize;
updateMem(memNeed(m_SP[1], m_SP[3]));
updateIOGas();
evmc_address address = toEvmC(asAddress(m_SP[0]));
size_t memoryOffset = static_cast(m_SP[1]);
constexpr size_t codeOffsetMax = std::numeric_limits::max();
size_t codeOffset =
m_SP[2] > codeOffsetMax ? codeOffsetMax : static_cast(m_SP[2]);
size_t size = static_cast(copyMemSize);
size_t numCopied = m_context->fn_table->copy_code(
m_context, &address, codeOffset, &m_mem[memoryOffset], size);
std::fill_n(&m_mem[memoryOffset + numCopied], size - numCopied, 0);
}
NEXT
CASE(GASPRICE)
{
ON_OP();
updateIOGas();
m_SPP[0] = fromEvmC(getTxContext().tx_gas_price);
}
NEXT
CASE(BLOCKHASH)
{
ON_OP();
m_runGas = VMSchedule::stepGas6;
updateIOGas();
const int64_t blockNumber = getTxContext().block_number;
u256 number = m_SP[0];
if (number < blockNumber && number >= std::max(int64_t(256), blockNumber) - 256)
{
evmc_uint256be hash;
m_context->fn_table->get_block_hash(&hash, m_context, int64_t(number));
m_SPP[0] = fromEvmC(hash);
}
else
m_SPP[0] = 0;
}
NEXT
CASE(COINBASE)
{
ON_OP();
updateIOGas();
m_SPP[0] = fromAddress(fromEvmC(getTxContext().block_coinbase));
}
NEXT
CASE(TIMESTAMP)
{
ON_OP();
updateIOGas();
m_SPP[0] = getTxContext().block_timestamp;
}
NEXT
CASE(NUMBER)
{
ON_OP();
updateIOGas();
m_SPP[0] = getTxContext().block_number;
}
NEXT
CASE(DIFFICULTY)
{
ON_OP();
updateIOGas();
m_SPP[0] = fromEvmC(getTxContext().block_difficulty);
}
NEXT
CASE(GASLIMIT)
{
ON_OP();
updateIOGas();
m_SPP[0] = getTxContext().block_gas_limit;
}
NEXT
CASE(POP)
{
ON_OP();
updateIOGas();
--m_SP;
}
NEXT
CASE(PUSHC)
{
#if EVM_USE_CONSTANT_POOL
ON_OP();
updateIOGas();
// get val at two-byte offset into const pool and advance pc by one-byte remainder
TRACE_OP(2, m_PC, m_OP);
unsigned off;
++m_PC;
off = m_code[m_PC++] << 8;
off |= m_code[m_PC++];
m_PC += m_code[m_PC];
m_SPP[0] = m_pool[off];
TRACE_VAL(2, "Retrieved pooled const", m_SPP[0]);
#else
throwBadInstruction();
#endif
}
CONTINUE
CASE(PUSH1)
{
ON_OP();
updateIOGas();
++m_PC;
m_SPP[0] = m_code[m_PC];
++m_PC;
}
CONTINUE
CASE(PUSH2)
CASE(PUSH3)
CASE(PUSH4)
CASE(PUSH5)
CASE(PUSH6)
CASE(PUSH7)
CASE(PUSH8)
CASE(PUSH9)
CASE(PUSH10)
CASE(PUSH11)
CASE(PUSH12)
CASE(PUSH13)
CASE(PUSH14)
CASE(PUSH15)
CASE(PUSH16)
CASE(PUSH17)
CASE(PUSH18)
CASE(PUSH19)
CASE(PUSH20)
CASE(PUSH21)
CASE(PUSH22)
CASE(PUSH23)
CASE(PUSH24)
CASE(PUSH25)
CASE(PUSH26)
CASE(PUSH27)
CASE(PUSH28)
CASE(PUSH29)
CASE(PUSH30)
CASE(PUSH31)
CASE(PUSH32)
{
ON_OP();
updateIOGas();
int numBytes = (int)m_OP - (int)Instruction::PUSH1 + 1;
m_SPP[0] = 0;
// Construct a number out of PUSH bytes.
// This requires the code has been copied and extended by 32 zero
// bytes to handle "out of code" push data here.
for (++m_PC; numBytes--; ++m_PC)
m_SPP[0] = (m_SPP[0] << 8) | m_code[m_PC];
}
CONTINUE
CASE(JUMP)
{
ON_OP();
updateIOGas();
m_PC = verifyJumpDest(m_SP[0]);
}
CONTINUE
CASE(JUMPI)
{
ON_OP();
updateIOGas();
if (m_SP[1])
m_PC = verifyJumpDest(m_SP[0]);
else
++m_PC;
}
CONTINUE
CASE(JUMPC)
{
#if EVM_REPLACE_CONST_JUMP
ON_OP();
updateIOGas();
m_PC = uint64_t(m_SP[0]);
#else
throwBadInstruction();
#endif
}
CONTINUE
CASE(JUMPCI)
{
#if EVM_REPLACE_CONST_JUMP
ON_OP();
updateIOGas();
if (m_SP[1])
m_PC = uint64_t(m_SP[0]);
else
++m_PC;
#else
throwBadInstruction();
#endif
}
CONTINUE
CASE(DUP1)
CASE(DUP2)
CASE(DUP3)
CASE(DUP4)
CASE(DUP5)
CASE(DUP6)
CASE(DUP7)
CASE(DUP8)
CASE(DUP9)
CASE(DUP10)
CASE(DUP11)
CASE(DUP12)
CASE(DUP13)
CASE(DUP14)
CASE(DUP15)
CASE(DUP16)
{
ON_OP();
updateIOGas();
unsigned n = (unsigned)m_OP - (unsigned)Instruction::DUP1;
*(uint64_t*)m_SPP = *(uint64_t*)(m_SP + n);
// the stack slot being copied into may no longer hold a u256
// so we construct a new one in the memory, rather than assign
new(m_SPP) u256(m_SP[n]);
}
NEXT
CASE(SWAP1)
CASE(SWAP2)
CASE(SWAP3)
CASE(SWAP4)
CASE(SWAP5)
CASE(SWAP6)
CASE(SWAP7)
CASE(SWAP8)
CASE(SWAP9)
CASE(SWAP10)
CASE(SWAP11)
CASE(SWAP12)
CASE(SWAP13)
CASE(SWAP14)
CASE(SWAP15)
CASE(SWAP16)
{
ON_OP();
updateIOGas();
unsigned n = (unsigned)m_OP - (unsigned)Instruction::SWAP1 + 1;
std::swap(m_SP[0], m_SP[n]);
}
NEXT
CASE(SLOAD)
{
m_runGas = m_rev >= EVMC_TANGERINE_WHISTLE ? 200 : 50;
ON_OP();
updateIOGas();
evmc_uint256be key = toEvmC(m_SP[0]);
evmc_uint256be value;
m_context->fn_table->get_storage(&value, m_context, &m_message->destination, &key);
m_SPP[0] = fromEvmC(value);
}
NEXT
CASE(SSTORE)
{
ON_OP();
if (m_message->flags & EVMC_STATIC)
throwDisallowedStateChange();
updateSSGas();
updateIOGas();
evmc_uint256be key = toEvmC(m_SP[0]);
evmc_uint256be value = toEvmC(m_SP[1]);
m_context->fn_table->set_storage(m_context, &m_message->destination, &key, &value);
}
NEXT
CASE(PC)
{
ON_OP();
updateIOGas();
m_SPP[0] = m_PC;
}
NEXT
CASE(MSIZE)
{
ON_OP();
updateIOGas();
m_SPP[0] = m_mem.size();
}
NEXT
CASE(GAS)
{
ON_OP();
updateIOGas();
m_SPP[0] = m_io_gas;
}
NEXT
CASE(JUMPDEST)
{
m_runGas = VMSchedule::jumpdestGas;
ON_OP();
updateIOGas();
}
NEXT
CASE(INVALID)
DEFAULT
{
throwBadInstruction();
}
}
WHILE_CASES
}
}
}