Skip to content

Commit

Permalink
Implement JUMPF
Browse files Browse the repository at this point in the history
  • Loading branch information
gumb0 committed Jul 25, 2023
1 parent 18a8972 commit f566571
Show file tree
Hide file tree
Showing 9 changed files with 51 additions and 7 deletions.
1 change: 1 addition & 0 deletions lib/evmone/advanced_instructions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,7 @@ constexpr std::array<instruction_exec_fn, 256> instruction_implementations = [](
table[OP_DATALOADN] = op_undefined;
table[OP_DATASIZE] = op_undefined;
table[OP_DATACOPY] = op_undefined;
table[OP_JUMPF] = op_undefined;

table[OP_DUPN] = op_undefined;
table[OP_SWAPN] = op_undefined;
Expand Down
26 changes: 23 additions & 3 deletions lib/evmone/eof.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ constexpr auto CODE_SECTION_NUMBER_LIMIT = 1024;
constexpr auto MAX_STACK_HEIGHT = 0x03FF;
constexpr auto OUTPUTS_INPUTS_NUMBER_LIMIT = 0x7F;
constexpr auto REL_OFFSET_SIZE = sizeof(int16_t);
constexpr auto STACK_SIZE_LIMIT = 1024;

using EOFSectionHeaders = std::array<std::vector<uint16_t>, MAX_SECTION + 1>;

Expand Down Expand Up @@ -224,7 +225,7 @@ EOFValidationError validate_instructions(
if (i >= code.size())
return EOFValidationError::truncated_instruction;
}
else if (op == OP_CALLF)
else if (op == OP_CALLF || op == OP_JUMPF)
{
const auto fid = read_uint16_be(&code[i + 1]);
if (fid >= header.types.size())
Expand Down Expand Up @@ -331,6 +332,9 @@ std::variant<EOFValidationError, int32_t> validate_max_stack_height(
auto stack_height_required = instr::traits[opcode].stack_height_required;
auto stack_height_change = instr::traits[opcode].stack_height_change;

auto stack_height = stack_heights[i];
assert(stack_height != LOC_UNVISITED);

if (opcode == OP_CALLF)
{
const auto fid = read_uint16_be(&code[i + 1]);
Expand All @@ -339,9 +343,21 @@ std::variant<EOFValidationError, int32_t> validate_max_stack_height(
stack_height_change =
static_cast<int8_t>(code_types[fid].outputs - stack_height_required);
}
else if (opcode == OP_JUMPF)
{
const auto fid = read_uint16_be(&code[i + 1]);

auto stack_height = stack_heights[i];
assert(stack_height != LOC_UNVISITED);
if (code_types[func_index].outputs < code_types[fid].outputs)
return EOFValidationError::jumpf_destination_incompatible_outputs;

if (stack_height + code_types[fid].max_stack_height > STACK_SIZE_LIMIT)
return EOFValidationError::stack_overflow;

stack_height_required = static_cast<int8_t>(
code_types[func_index].outputs + code_types[fid].inputs - code_types[fid].outputs);
if (stack_heights[i] > stack_height_required)
return EOFValidationError::non_empty_stack_on_terminating_instruction;
}

if (stack_height < stack_height_required)
return EOFValidationError::stack_underflow;
Expand Down Expand Up @@ -616,10 +632,14 @@ std::string_view get_error_message(EOFValidationError err) noexcept
return "unreachable_instructions";
case EOFValidationError::stack_underflow:
return "stack_underflow";
case EOFValidationError::stack_overflow:
return "stack_overflow";
case EOFValidationError::invalid_code_section_index:
return "invalid_code_section_index";
case EOFValidationError::invalid_dataloadn_index:
return "invalid_dataloadn_index";
case EOFValidationError::jumpf_destination_incompatible_outputs:
return "jumpf_destination_incompatible_outputs";
case EOFValidationError::impossible:
return "impossible";
}
Expand Down
2 changes: 2 additions & 0 deletions lib/evmone/eof.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,10 @@ enum class EOFValidationError
inputs_outputs_num_above_limit,
unreachable_instructions,
stack_underflow,
stack_overflow,
invalid_code_section_index,
invalid_dataloadn_index,
jumpf_destination_incompatible_outputs,

impossible,
};
Expand Down
16 changes: 16 additions & 0 deletions lib/evmone/instructions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1051,6 +1051,22 @@ inline code_iterator retf(StackTop /*stack*/, ExecutionState& state, code_iterat
return p;
}

inline code_iterator jumpf(StackTop stack, ExecutionState& state, code_iterator pos) noexcept
{
const auto index = read_uint16_be(&pos[1]);
const auto& header = state.analysis.baseline->eof_header;
const auto stack_size = &stack.top() - state.stack_space.bottom();
if (stack_size + header.types[index].max_stack_height > StackSpace::limit)
{
state.status = EVMC_STACK_OVERFLOW;
return nullptr;
}

const auto offset = header.code_offsets[index] - header.code_offsets[0];
auto code = state.analysis.baseline->executable_code;
return code.data() + offset;
}

template <evmc_status_code StatusCode>
inline TermResult return_impl(StackTop stack, int64_t gas_left, ExecutionState& state) noexcept
{
Expand Down
1 change: 1 addition & 0 deletions lib/evmone/instructions_opcodes.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ enum Opcode : uint8_t
OP_RJUMPV = 0xe2,
OP_CALLF = 0xe3,
OP_RETF = 0xe4,
OP_JUMPF = 0xe5,

OP_DUPN = 0xe6,
OP_SWAPN = 0xe7,
Expand Down
2 changes: 2 additions & 0 deletions lib/evmone/instructions_traits.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ constexpr inline GasCostTable gas_costs = []() noexcept {
table[EVMC_CANCUN][OP_RJUMPV] = 4;
table[EVMC_CANCUN][OP_CALLF] = 5;
table[EVMC_CANCUN][OP_RETF] = 3;
table[EVMC_CANCUN][OP_JUMPF] = 5;
table[EVMC_CANCUN][OP_DATALOAD] = 3;
table[EVMC_CANCUN][OP_DATALOADN] = 2;
table[EVMC_CANCUN][OP_DATASIZE] = 2;
Expand Down Expand Up @@ -395,6 +396,7 @@ constexpr inline std::array<Traits, 256> traits = []() noexcept {
table[OP_STATICCALL] = {"STATICCALL", 0, false, 6, -5, EVMC_BYZANTIUM};
table[OP_CALLF] = {"CALLF", 2, false, 0, 0, EVMC_CANCUN};
table[OP_RETF] = {"RETF", 0, true, 0, 0, EVMC_CANCUN};
table[OP_JUMPF] = {"JUMPF", 2, true, 0, 0, EVMC_CANCUN};
table[OP_REVERT] = {"REVERT", 0, true, 2, -2, EVMC_BYZANTIUM};
table[OP_INVALID] = {"INVALID", 0, true, 0, 0, EVMC_FRONTIER};
table[OP_SELFDESTRUCT] = {"SELFDESTRUCT", 0, true, 1, -1, EVMC_FRONTIER};
Expand Down
2 changes: 1 addition & 1 deletion lib/evmone/instructions_xmacro.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@
ON_OPCODE_IDENTIFIER(OP_RJUMPV, rjumpv) \
ON_OPCODE_IDENTIFIER(OP_CALLF, callf) \
ON_OPCODE_IDENTIFIER(OP_RETF, retf) \
ON_OPCODE_UNDEFINED(0xe5) \
ON_OPCODE_IDENTIFIER(OP_JUMPF, jumpf) \
ON_OPCODE_IDENTIFIER(OP_DUPN, dupn) \
ON_OPCODE_IDENTIFIER(OP_SWAPN, swapn) \
ON_OPCODE_IDENTIFIER(OP_DATALOAD, dataload) \
Expand Down
4 changes: 2 additions & 2 deletions test/unittests/eof_validation_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -317,11 +317,11 @@ TEST(eof_validation, EOF1_undefined_opcodes)

for (uint16_t opcode = 0; opcode <= 0xff; ++opcode)
{
// PUSH*, DUPN, SWAPN, RJUMP*, CALLF require immediate argument to be valid,
// PUSH*, DUPN, SWAPN, RJUMP*, CALLF, JUMPF require immediate argument to be valid,
// checked in a separate test.
if ((opcode >= OP_PUSH1 && opcode <= OP_PUSH32) || opcode == OP_DUPN ||
opcode == OP_SWAPN || opcode == OP_RJUMP || opcode == OP_RJUMPI || opcode == OP_CALLF ||
opcode == OP_RJUMPV || opcode == OP_DATALOADN)
opcode == OP_RJUMPV || opcode == OP_DATALOADN || opcode == OP_JUMPF)
continue;
// These opcodes are deprecated since Cancun.
// gas_cost table current implementation does not allow to undef instructions.
Expand Down
4 changes: 3 additions & 1 deletion test/unittests/instructions_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ constexpr bool is_terminating(Opcode op) noexcept
case OP_STOP:
case OP_RETURN:
case OP_RETF:
case OP_JUMPF:
case OP_REVERT:
case OP_INVALID:
case OP_SELFDESTRUCT:
Expand All @@ -56,7 +57,7 @@ constexpr void validate_traits_of() noexcept
// immediate_size
if constexpr (Op >= OP_PUSH1 && Op <= OP_PUSH32)
static_assert(tr.immediate_size == Op - OP_PUSH1 + 1);
else if constexpr (Op == OP_RJUMP || Op == OP_RJUMPI || Op == OP_CALLF)
else if constexpr (Op == OP_RJUMP || Op == OP_RJUMPI || Op == OP_CALLF || Op == OP_JUMPF)
static_assert(tr.immediate_size == 2);
else if constexpr (Op == OP_RJUMPV)
static_assert(tr.immediate_size == 1);
Expand Down Expand Up @@ -112,6 +113,7 @@ constexpr bool instruction_only_in_evmone(evmc_revision rev, Opcode op) noexcept
case OP_RJUMPV:
case OP_CALLF:
case OP_RETF:
case OP_JUMPF:
case OP_DUPN:
case OP_SWAPN:
case OP_MCOPY:
Expand Down

0 comments on commit f566571

Please sign in to comment.