Skip to content

Commit

Permalink
Merge pull request #600 from ethereum/gas
Browse files Browse the repository at this point in the history
Pass gas to instructions by value
  • Loading branch information
chfast authored Apr 13, 2023
2 parents f1928c6 + f2dc3cf commit 7028b6b
Show file tree
Hide file tree
Showing 10 changed files with 239 additions and 219 deletions.
3 changes: 3 additions & 0 deletions lib/evmone/advanced_analysis.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ class Stack
/// The execution state specialized for the Advanced interpreter.
struct AdvancedExecutionState : ExecutionState
{
int64_t gas_left = 0;
Stack stack;

/// The gas cost of the current block.
Expand All @@ -93,6 +94,7 @@ struct AdvancedExecutionState : ExecutionState
const evmc_host_interface& host_interface, evmc_host_context* host_ctx,
bytes_view _code) noexcept
: ExecutionState{message, revision, host_interface, host_ctx, _code},
gas_left{message.gas},
stack{stack_space.bottom()}
{}

Expand All @@ -109,6 +111,7 @@ struct AdvancedExecutionState : ExecutionState
bytes_view _code) noexcept
{
ExecutionState::reset(message, revision, host_interface, host_ctx, _code);
gas_left = message.gas;
stack.reset(stack_space.bottom());
analysis.advanced = nullptr; // For consistency with previous behavior.
current_block_cost = 0;
Expand Down
31 changes: 23 additions & 8 deletions lib/evmone/advanced_instructions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,17 +36,30 @@ inline evmc_status_code impl(AdvancedExecutionState& state) noexcept
return status;
}

template <Opcode Op, StopToken CoreFn() noexcept = core::impl<Op>>
inline StopToken impl(AdvancedExecutionState& /*state*/) noexcept
template <Opcode Op,
evmc_status_code CoreFn(StackTop, int64_t&, ExecutionState&) noexcept = core::impl<Op>>
inline evmc_status_code impl(AdvancedExecutionState& state) noexcept
{
const auto status = CoreFn(state.stack.top_item, state.gas_left, state);
state.stack.top_item += instr::traits[Op].stack_height_change;
return status;
}

template <Opcode Op, Result CoreFn(StackTop, int64_t, ExecutionState&) noexcept = core::impl<Op>>
inline evmc_status_code impl(AdvancedExecutionState& state) noexcept
{
return CoreFn();
const auto status = CoreFn(state.stack.top_item, state.gas_left, state);
state.gas_left = status.gas_left;
state.stack.top_item += instr::traits[Op].stack_height_change;
return status.status;
}

template <Opcode Op, StopToken CoreFn(StackTop, ExecutionState&) noexcept = core::impl<Op>>
inline StopToken impl(AdvancedExecutionState& state) noexcept
template <Opcode Op,
TermResult CoreFn(StackTop, int64_t, ExecutionState&) noexcept = core::impl<Op>>
inline TermResult impl(AdvancedExecutionState& state) noexcept
{
// Stack height adjustment may be omitted.
return CoreFn(state.stack.top_item, state);
return CoreFn(state.stack.top_item, state.gas_left, state);
}

template <Opcode Op,
Expand Down Expand Up @@ -88,10 +101,12 @@ const Instruction* op(const Instruction* instr, AdvancedExecutionState& state) n
}

/// Wraps the generic instruction implementation to advanced instruction function signature.
template <StopToken InstrFn(AdvancedExecutionState&) noexcept>
template <TermResult InstrFn(AdvancedExecutionState&) noexcept>
const Instruction* op(const Instruction* /*instr*/, AdvancedExecutionState& state) noexcept
{
return state.exit(InstrFn(state).status);
const auto result = InstrFn(state);
state.gas_left = result.gas_left;
return state.exit(result.status);
}

const Instruction* op_sstore(const Instruction* instr, AdvancedExecutionState& state) noexcept
Expand Down
118 changes: 58 additions & 60 deletions lib/evmone/baseline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -159,76 +159,72 @@ struct Position

/// Helpers for invoking instruction implementations of different signatures.
/// @{
[[release_inline]] inline code_iterator invoke(
void (*instr_fn)(StackTop) noexcept, Position pos, ExecutionState& /*state*/) noexcept
[[release_inline]] inline code_iterator invoke(void (*instr_fn)(StackTop) noexcept, Position pos,
int64_t& /*gas*/, ExecutionState& /*state*/) noexcept
{
instr_fn(pos.stack_top);
return pos.code_it + 1;
}

[[release_inline]] inline code_iterator invoke(
StopToken (*instr_fn)() noexcept, Position /*pos*/, ExecutionState& state) noexcept
{
state.status = instr_fn().status;
return nullptr;
}

[[release_inline]] inline code_iterator invoke(
evmc_status_code (*instr_fn)(StackTop, ExecutionState&) noexcept, Position pos,
Result (*instr_fn)(StackTop, int64_t, ExecutionState&) noexcept, Position pos, int64_t& gas,
ExecutionState& state) noexcept
{
if (const auto status = instr_fn(pos.stack_top, state); status != EVMC_SUCCESS)
const auto o = instr_fn(pos.stack_top, gas, state);
gas = o.gas_left;
if (o.status != EVMC_SUCCESS)
{
state.status = status;
state.status = o.status;
return nullptr;
}
return pos.code_it + 1;
}

[[release_inline]] inline code_iterator invoke(void (*instr_fn)(StackTop, ExecutionState&) noexcept,
Position pos, ExecutionState& state) noexcept
Position pos, int64_t& /*gas*/, ExecutionState& state) noexcept
{
instr_fn(pos.stack_top, state);
return pos.code_it + 1;
}

[[release_inline]] inline code_iterator invoke(
code_iterator (*instr_fn)(StackTop, ExecutionState&, code_iterator) noexcept, Position pos,
ExecutionState& state) noexcept
int64_t& /*gas*/, ExecutionState& state) noexcept
{
return instr_fn(pos.stack_top, state, pos.code_it);
}

[[release_inline]] inline code_iterator invoke(
StopToken (*instr_fn)(StackTop, ExecutionState&) noexcept, Position pos,
TermResult (*instr_fn)(StackTop, int64_t, ExecutionState&) noexcept, Position pos, int64_t& gas,
ExecutionState& state) noexcept
{
state.status = instr_fn(pos.stack_top, state).status;
const auto result = instr_fn(pos.stack_top, gas, state);
gas = result.gas_left;
state.status = result.status;
return nullptr;
}
/// @}

/// A helper to invoke the instruction implementation of the given opcode Op.
template <Opcode Op>
[[release_inline]] inline Position invoke(const CostTable& cost_table, const uint256* stack_bottom,
Position pos, ExecutionState& state) noexcept
Position pos, int64_t& gas, ExecutionState& state) noexcept
{
if (const auto status =
check_requirements<Op>(cost_table, state.gas_left, pos.stack_top, stack_bottom);
if (const auto status = check_requirements<Op>(cost_table, gas, pos.stack_top, stack_bottom);
status != EVMC_SUCCESS)
{
state.status = status;
return {nullptr, pos.stack_top};
}
const auto new_pos = invoke(instr::core::impl<Op>, pos, state);
const auto new_pos = invoke(instr::core::impl<Op>, pos, gas, state);
const auto new_stack_top = pos.stack_top + instr::traits[Op].stack_height_change;
return {new_pos, new_stack_top};
}


template <bool TracingEnabled>
void dispatch(const CostTable& cost_table, ExecutionState& state, const uint8_t* code,
Tracer* tracer = nullptr) noexcept
int64_t dispatch(const CostTable& cost_table, ExecutionState& state, int64_t gas,
const uint8_t* code, Tracer* tracer = nullptr) noexcept
{
const auto stack_bottom = state.stack_space.bottom();

Expand All @@ -244,42 +240,43 @@ void dispatch(const CostTable& cost_table, ExecutionState& state, const uint8_t*
if (offset < state.original_code.size()) // Skip STOP from code padding.
{
tracer->notify_instruction_start(
offset, position.stack_top, stack_height, state.gas_left, state);
offset, position.stack_top, stack_height, gas, state);
}
}

const auto op = *position.code_it;
switch (op)
{
#define ON_OPCODE(OPCODE) \
case OPCODE: \
ASM_COMMENT(OPCODE); \
if (const auto next = invoke<OPCODE>(cost_table, stack_bottom, position, state); \
next.code_it == nullptr) \
{ \
return; \
} \
else \
{ \
/* Update current position only when no error, \
this improves compiler optimization. */ \
position = next; \
} \
#define ON_OPCODE(OPCODE) \
case OPCODE: \
ASM_COMMENT(OPCODE); \
if (const auto next = invoke<OPCODE>(cost_table, stack_bottom, position, gas, state); \
next.code_it == nullptr) \
{ \
return gas; \
} \
else \
{ \
/* Update current position only when no error, \
this improves compiler optimization. */ \
position = next; \
} \
break;

MAP_OPCODES
#undef ON_OPCODE

default:
state.status = EVMC_UNDEFINED_INSTRUCTION;
return;
return gas;
}
}
INTX_UNREACHABLE();
}

#if EVMONE_CGOTO_SUPPORTED
void dispatch_cgoto(
const CostTable& cost_table, ExecutionState& state, const uint8_t* code) noexcept
int64_t dispatch_cgoto(
const CostTable& cost_table, ExecutionState& state, int64_t gas, const uint8_t* code) noexcept
{
#pragma GCC diagnostic ignored "-Wpedantic"

Expand All @@ -301,31 +298,33 @@ void dispatch_cgoto(

goto* cgoto_table[*position.code_it];

#define ON_OPCODE(OPCODE) \
TARGET_##OPCODE : ASM_COMMENT(OPCODE); \
if (const auto next = invoke<OPCODE>(cost_table, stack_bottom, position, state); \
next.code_it == nullptr) \
{ \
return; \
} \
else \
{ \
/* Update current position only when no error, \
this improves compiler optimization. */ \
position = next; \
} \
#define ON_OPCODE(OPCODE) \
TARGET_##OPCODE : ASM_COMMENT(OPCODE); \
if (const auto next = invoke<OPCODE>(cost_table, stack_bottom, position, gas, state); \
next.code_it == nullptr) \
{ \
return gas; \
} \
else \
{ \
/* Update current position only when no error, \
this improves compiler optimization. */ \
position = next; \
} \
goto* cgoto_table[*position.code_it];

MAP_OPCODES
#undef ON_OPCODE

TARGET_OP_UNDEFINED:
state.status = EVMC_UNDEFINED_INSTRUCTION;
return gas;
}
#endif
} // namespace

evmc_result execute(const VM& vm, ExecutionState& state, const CodeAnalysis& analysis) noexcept
evmc_result execute(
const VM& vm, int64_t gas, ExecutionState& state, const CodeAnalysis& analysis) noexcept
{
state.analysis.baseline = &analysis; // Assign code analysis for instruction implementations.

Expand All @@ -337,20 +336,19 @@ evmc_result execute(const VM& vm, ExecutionState& state, const CodeAnalysis& ana
if (INTX_UNLIKELY(tracer != nullptr))
{
tracer->notify_execution_start(state.rev, *state.msg, analysis.executable_code);
dispatch<true>(cost_table, state, code.data(), tracer);
gas = dispatch<true>(cost_table, state, gas, code.data(), tracer);
}
else
{
#if EVMONE_CGOTO_SUPPORTED
if (vm.cgoto)
dispatch_cgoto(cost_table, state, code.data());
gas = dispatch_cgoto(cost_table, state, gas, code.data());
else
#endif
dispatch<false>(cost_table, state, code.data());
gas = dispatch<false>(cost_table, state, gas, code.data());
}

const auto gas_left =
(state.status == EVMC_SUCCESS || state.status == EVMC_REVERT) ? state.gas_left : 0;
const auto gas_left = (state.status == EVMC_SUCCESS || state.status == EVMC_REVERT) ? gas : 0;
const auto gas_refund = (state.status == EVMC_SUCCESS) ? state.gas_refund : 0;

assert(state.output_size != 0 || state.output_offset == 0);
Expand All @@ -370,6 +368,6 @@ evmc_result execute(evmc_vm* c_vm, const evmc_host_interface* host, evmc_host_co
const auto jumpdest_map = analyze(rev, {code, code_size});
auto state =
std::make_unique<ExecutionState>(*msg, rev, *host, ctx, bytes_view{code, code_size});
return execute(*vm, *state, jumpdest_map);
return execute(*vm, msg->gas, *state, jumpdest_map);
}
} // namespace evmone::baseline
2 changes: 1 addition & 1 deletion lib/evmone/baseline.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ evmc_result execute(evmc_vm* vm, const evmc_host_interface* host, evmc_host_cont

/// Executes in Baseline interpreter on the given external and initialized state.
EVMC_EXPORT evmc_result execute(
const VM&, ExecutionState& state, const CodeAnalysis& analysis) noexcept;
const VM&, int64_t gas_limit, ExecutionState& state, const CodeAnalysis& analysis) noexcept;

} // namespace baseline
} // namespace evmone
8 changes: 1 addition & 7 deletions lib/evmone/execution_state.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,6 @@ class Memory
class ExecutionState
{
public:
int64_t gas_left = 0;
int64_t gas_refund = 0;
Memory memory;
const evmc_message* msg = nullptr;
Expand Down Expand Up @@ -166,19 +165,14 @@ class ExecutionState
ExecutionState(const evmc_message& message, evmc_revision revision,
const evmc_host_interface& host_interface, evmc_host_context* host_ctx,
bytes_view _code) noexcept
: gas_left{message.gas},
msg{&message},
host{host_interface, host_ctx},
rev{revision},
original_code{_code}
: msg{&message}, host{host_interface, host_ctx}, rev{revision}, original_code{_code}
{}

/// Resets the contents of the ExecutionState so that it could be reused.
void reset(const evmc_message& message, evmc_revision revision,
const evmc_host_interface& host_interface, evmc_host_context* host_ctx,
bytes_view _code) noexcept
{
gas_left = message.gas;
gas_refund = 0;
memory.clear();
msg = &message;
Expand Down
Loading

0 comments on commit 7028b6b

Please sign in to comment.