Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions category/vm/compiler/ir/basic_blocks.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,37 @@ namespace monad::vm::compiler::basic_blocks
template <Traits traits, typename... Args>
BasicBlocksIR unsafe_make_ir(Args &&...);

// EIP-8024: the immediate gives an operand-dependent stack effect (computed
// here; raw byte stashed in index for codegen). A disallowed immediate is
// INVALID and returns without consuming the byte, so a following 0x5B is
// still discoverable as a JUMPDEST.
inline std::variant<Instruction, Terminator, JumpDest> decode_eip8024(
uint8_t const opcode, uint32_t const opcode_offset,
OpCodeInfo const &info, std::span<uint8_t const> const bytes,
uint32_t &current_offset)
{
uint8_t const imm =
current_offset < bytes.size() ? bytes[current_offset] : uint8_t{0};

if (!eip8024_immediate_valid(opcode, imm)) {
return Terminator::InvalidInstruction;
}

current_offset++;

auto const [min_stack, stack_increase] =
eip8024_stack_effect(opcode, imm);

return Instruction(
opcode_offset,
evm_op_to_opcode(opcode),
info.min_gas,
min_stack,
imm,
stack_increase,
info.dynamic_gas);
}

template <Traits traits>
std::variant<Instruction, Terminator, JumpDest> BasicBlocksIR::scan_from(
std::span<uint8_t const> bytes, uint32_t &current_offset)
Expand Down Expand Up @@ -389,6 +420,13 @@ namespace monad::vm::compiler::basic_blocks
break;
}

if constexpr (traits::eip_8024_active()) {
if (is_eip8024_opcode(opcode)) {
return decode_eip8024(
opcode, opcode_offset, info, bytes, current_offset);
}
}

auto const imm_size = info.num_args;
uint256_t imm_value{0};

Expand Down
34 changes: 33 additions & 1 deletion category/vm/compiler/ir/instruction.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

#include <category/core/assert.h>
#include <category/core/runtime/uint256.hpp>
#include <category/vm/evm/opcodes.hpp>

#include <evmc/evmc.hpp>

Expand Down Expand Up @@ -102,6 +103,9 @@ namespace monad::vm::compiler
Dup = 0x80,
Swap = 0x90,
Log = 0xA0,
DupN = 0xE6,
SwapN = 0xE7,
Exchange = 0xE8,
Create = 0xF0,
Call = 0xF1,
CallCode = 0xF2,
Expand All @@ -128,6 +132,8 @@ namespace monad::vm::compiler
constexpr uint32_t static_gas_cost() const noexcept;
constexpr OpCode opcode() const noexcept;
constexpr uint8_t stack_args() const noexcept;
// N for PUSHN, DUP1-16, SWAP1-16 and LOGN. For the EIP-8024 opcodes it
// is the raw encoded immediate instead; decode with eip8024_decode_*.
constexpr uint8_t index() const noexcept;
constexpr bool increases_stack() const noexcept;
constexpr uint8_t stack_increase() const noexcept;
Expand Down Expand Up @@ -210,7 +216,9 @@ namespace monad::vm::compiler
{
MONAD_ASSERT(
opcode() == OpCode::Push || opcode() == OpCode::Swap ||
opcode() == OpCode::Dup || opcode() == OpCode::Log);
opcode() == OpCode::Dup || opcode() == OpCode::Log ||
opcode() == OpCode::DupN || opcode() == OpCode::SwapN ||
opcode() == OpCode::Exchange);
return index_;
}

Expand Down Expand Up @@ -393,6 +401,12 @@ namespace monad::vm::compiler
return "SWAP";
case Log:
return "LOG";
case DupN:
return "DUPN";
case SwapN:
return "SWAPN";
case Exchange:
return "EXCHANGE";
case Create:
return "CREATE";
case Call:
Expand Down Expand Up @@ -453,6 +467,24 @@ struct std::formatter<monad::vm::compiler::Instruction>
inst.immediate_value());
}

// index() holds the raw encoded immediate for these, so decode it --
// printing it directly would render DUPN 17 as "DUPN128". Matches the
// mnemonic evm-as emits.
if (inst.opcode() == DupN || inst.opcode() == SwapN) {
return std::format_to(
ctx.out(),
"{} {}",
inst.opcode(),
+monad::vm::compiler::eip8024_decode_single(inst.index()));
}

if (inst.opcode() == Exchange) {
auto const [n, m] =
monad::vm::compiler::eip8024_decode_pair(inst.index());
return std::format_to(
ctx.out(), "{} {}, {}", inst.opcode(), +n, +m);
}

if (inst.opcode() == Push || inst.opcode() == Dup ||
inst.opcode() == Swap || inst.opcode() == Log) {
return std::format_to(
Expand Down
12 changes: 12 additions & 0 deletions category/vm/compiler/ir/x86.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include <category/vm/compiler/ir/x86/types.hpp>
#include <category/vm/compiler/types.hpp>
#include <category/vm/evm/explicit_traits.hpp>
#include <category/vm/evm/opcodes.hpp>
#include <category/vm/evm/traits.hpp>
#include <category/vm/interpreter/intercode.hpp>
#include <category/vm/runtime/types.hpp>
Expand Down Expand Up @@ -262,6 +263,17 @@ namespace
case Swap:
emit.swap(instr.index());
break;
case DupN:
emit.dup(eip8024_decode_single(instr.index()));
break;
case SwapN:
emit.swap(eip8024_decode_single(instr.index()));
break;
case Exchange: {
auto const [n, m] = eip8024_decode_pair(instr.index());
emit.exchange(n, m);
break;
}
case Log:
switch (instr.index()) {
case 0:
Expand Down
10 changes: 10 additions & 0 deletions category/vm/compiler/ir/x86/emitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2334,6 +2334,16 @@ namespace monad::vm::compiler::native
stack_.swap(stack_.top_index() - static_cast<int32_t>(swap_ix));
}

// No discharge
void Emitter::exchange(uint8_t const n, uint8_t const m)
{
MONAD_ASSERT(n > 0);
MONAD_ASSERT(m > n);
stack_.exchange(
stack_.top_index() - static_cast<int32_t>(n),
stack_.top_index() - static_cast<int32_t>(m));
}

// Discharge through `lt` overload
void Emitter::lt()
{
Expand Down
1 change: 1 addition & 0 deletions category/vm/compiler/ir/x86/emitter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,7 @@ namespace monad::vm::compiler::native
void pop();
void dup(uint8_t dup_index);
void swap(uint8_t swap_index);
void exchange(uint8_t n, uint8_t m);

void lt();
void gt();
Expand Down
40 changes: 26 additions & 14 deletions category/vm/compiler/ir/x86/virtual_stack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,7 @@ namespace monad::vm::compiler::native
auto &e = at(top_index_);

auto const rem = e->stack_indices_.erase(top_index_);
MONAD_DEBUG_ASSERT(rem == 1);
MONAD_ASSERT(rem == 1);
Comment thread
brett-monad marked this conversation as resolved.

// Note that it's valid for stack indices to become negative here.
top_index_ -= 1;
Expand All @@ -404,7 +404,7 @@ namespace monad::vm::compiler::native
{
top_index_ += 1;
auto [_, ins] = e->stack_indices_.insert(top_index_);
MONAD_DEBUG_ASSERT(ins);
MONAD_ASSERT(ins);
at(top_index_) = std::move(e);
}

Expand Down Expand Up @@ -464,25 +464,37 @@ namespace monad::vm::compiler::native

void Stack::swap(int32_t const swap_index)
{
// swap is the top-anchored special case of exchange.
MONAD_ASSERT(swap_index < top_index_);
exchange(top_index_, swap_index);
}

void Stack::exchange(int32_t const index_a, int32_t const index_b)
Comment thread
brett-monad marked this conversation as resolved.
{
MONAD_ASSERT(index_a != index_b);
MONAD_ASSERT(index_a <= top_index_);
MONAD_ASSERT(index_b <= top_index_);

auto t = top();
auto &e = at(swap_index);
// Erase both indices before inserting the swapped ones: when the two
// positions alias one duplicated element, inserting first would
// collide.
auto a = at(index_a);
auto &b = at(index_b);

auto const rem_t = t->stack_indices_.erase(top_index_);
MONAD_DEBUG_ASSERT(rem_t == 1);
auto const rem_a = a->stack_indices_.erase(index_a);
MONAD_ASSERT(rem_a == 1);

auto const rem_e = e->stack_indices_.erase(swap_index);
MONAD_DEBUG_ASSERT(rem_e == 1);
auto const rem_b = b->stack_indices_.erase(index_b);
MONAD_ASSERT(rem_b == 1);

auto const ins_t = t->stack_indices_.insert(swap_index);
MONAD_DEBUG_ASSERT(ins_t.second);
auto const ins_a = a->stack_indices_.insert(index_b);
MONAD_ASSERT(ins_a.second);

auto const ins_e = e->stack_indices_.insert(top_index_);
MONAD_DEBUG_ASSERT(ins_e.second);
auto const ins_b = b->stack_indices_.insert(index_a);
MONAD_ASSERT(ins_b.second);

at(top_index_) = std::move(e);
e = std::move(t);
at(index_a) = std::move(b);
b = std::move(a);
}

DeferredComparison Stack::discharge_deferred_comparison()
Expand Down
7 changes: 7 additions & 0 deletions category/vm/compiler/ir/x86/virtual_stack.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,13 @@ namespace monad::vm::compiler::native
*/
void swap(int32_t swap_index);

/**
* Swap the two stack elements at the given absolute indices.
* Generalises `swap` for EIP-8024 EXCHANGE. The indices must differ and
* both must be at or below the top.
*/
void exchange(int32_t index_a, int32_t index_b);

/**
* Clear deferred comparison and insert a stack offset to the
* corresponding stack elements. Returns the old `DeferredComparison`
Expand Down
Loading
Loading