Skip to content
Open
Show file tree
Hide file tree
Changes from 4 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
129 changes: 129 additions & 0 deletions openspec/changes/add-evm-jit-fallback/design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
# EVM JIT Fallback Design

## Architecture Overview

The fallback mechanism involves three main components:

1. **EVMMirBuilder Fallback Interface**: Generates MIR instructions to capture state and call runtime
2. **Runtime Fallback Function**: Transfers execution state and creates interpreter instance
3. **Interpreter State Restoration**: Resumes execution from provided EVM state

## State Transfer Design

### EVM State Components
The following state must be preserved during fallback:

- **Program Counter (PC)**: Current bytecode position
- **Stack State**: Complete evaluation stack contents and size
- **Memory State**: Memory contents and size
- **Storage State**: Already handled by EVMInstance (no transfer needed)
- **Gas State**: Remaining gas and gas costs
- **Call Context**: Caller, value, calldata (already in EVMInstance)

### State Capture Mechanism

```cpp
// In EVMMirBuilder
void fallbackToInterpreter(uint64_t targetPC) {
// 1. Save current PC
// 2. Flush stack state to EVMInstance
// 3. Sync memory state
// 4. Call runtime fallback function
callRuntimeFor(RuntimeFunctions.HandleFallback, targetPC);
}
```

### Runtime Interface Design

```cpp
// New runtime function signature
using FallbackFn = void (*)(zen::runtime::EVMInstance *, uint64_t);

// Implementation
void evmHandleFallback(zen::runtime::EVMInstance *Instance, uint64_t PC);
```

## Interpreter Integration

### State Restoration
The interpreter must be enhanced to accept initial state:

```cpp
class EVMInterpreter {
// New method for state-based execution
evmc_result executeFromState(EVMInstance* instance, uint64_t startPC);

// Restore stack from EVMInstance
void restoreStackState(EVMInstance* instance);

// Memory is already accessible via EVMInstance
};
```

### Execution Flow

1. **JIT Execution**: Normal compiled execution until fallback trigger
2. **State Capture**: EVMMirBuilder saves all volatile state to EVMInstance
3. **Runtime Transition**: Call evmHandleFallback with target PC
4. **Interpreter Creation**: Runtime creates new interpreter instance
5. **State Restoration**: Interpreter loads state from EVMInstance
6. **Continued Execution**: Interpreter resumes from specified PC

## Implementation Phases

### Phase 1: Basic Infrastructure
- Add fallbackToInterpreter method to EVMMirBuilder
- Implement evmHandleFallback runtime function
- Add executeFromState method to interpreter

### Phase 2: State Management
- Implement stack state synchronization
- Add memory state consistency checks
- Handle gas accounting across transition

### Phase 3: Integration & Testing
- Add fallback triggers for unsupported opcodes
- Implement comprehensive test coverage
- Performance optimization and validation

## Error Handling

### Fallback Triggers
- Unsupported opcodes in current JIT implementation
- Complex control flow that exceeds JIT capabilities
- Runtime conditions requiring interpreter flexibility
- Explicit fallback requests for debugging/testing

### Error Conditions
- Invalid PC values (must point to valid instruction boundary)
- Stack overflow/underflow during state transfer
- Memory inconsistencies between JIT and interpreter views
- Gas exhaustion during fallback process

## Performance Considerations

### Optimization Strategies
- Minimize state synchronization overhead
- Use efficient stack flushing mechanisms
- Avoid unnecessary memory copies
- Batch state updates when possible

### Performance Monitoring
- Track fallback frequency and triggers
- Measure state transfer overhead
- Monitor interpreter performance post-fallback
- Identify optimization opportunities

## Security & Determinism

### Deterministic Execution
- Ensure identical results across JIT/interpreter boundary
- Maintain consistent gas accounting
- Preserve exact stack and memory semantics
- Handle edge cases identically

### Security Considerations
- Validate all transferred state for consistency
- Prevent state corruption during transition
- Ensure proper error propagation
- Maintain execution context isolation
34 changes: 34 additions & 0 deletions openspec/changes/add-evm-jit-fallback/proposal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Add EVM JIT Fallback to Interpreter

## Summary
Add fallback mechanism from EVM JIT compilation to interpreter execution, enabling seamless transition when JIT compilation encounters unsupported operations or runtime conditions.

## Motivation
Currently, EVM JIT compilation is an all-or-nothing approach. When the JIT compiler encounters unsupported opcodes, complex control flow, or runtime conditions that cannot be efficiently compiled, the entire execution must fall back to interpreter mode from the beginning. This results in:

1. **Performance degradation**: Losing all JIT optimization benefits for the entire execution
2. **Complexity**: Requiring complete re-execution from the start
3. **Resource waste**: Discarding partially compiled code and optimization work

A mid-execution fallback mechanism would allow:
- Preserving JIT performance benefits for successfully compiled portions
- Graceful degradation only for problematic code sections
- Better overall performance for mixed workloads

## Goals
- Enable EVMMirBuilder to generate fallback calls to interpreter
- Preserve complete EVM execution state (PC, stack, memory) during transition
- Allow interpreter to resume execution from arbitrary EVM state
- Maintain deterministic execution semantics across JIT/interpreter boundary

## Non-Goals
- Fallback from interpreter to JIT (one-way transition only)
- Automatic re-compilation after fallback
- Cross-function fallback (limited to single function scope)

## Success Criteria
- JIT-compiled EVM code can fallback to interpreter at any instruction boundary
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

design how to test this feature

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

more test cases: stack flush/restore, memory consistency, gas, fallback triggers, error conditions, OutOfGas and revert in fallbacked interpreter, etc.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a unittest is added

- All EVM execution state is correctly preserved and transferred
- Interpreter can resume execution from transferred state
- Execution results are identical to pure interpreter or pure JIT execution
- Performance degradation is minimal for fallback transition overhead
34 changes: 34 additions & 0 deletions openspec/changes/add-evm-jit-fallback/specs/evm-execution/spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# evm-execution Specification Delta

## MODIFIED Requirements

### Requirement: EVM execution mode flexibility
The system SHALL support seamless transitions between JIT and interpreter execution modes within a single contract execution.

#### Scenario: Mid-execution mode transition
- **WHEN** JIT execution encounters a fallback condition
- **THEN** execution SHALL transition to interpreter mode
- **AND** the transition SHALL preserve all execution state
- **AND** execution results SHALL be identical to single-mode execution

#### Scenario: Fallback trigger conditions
- **WHEN** JIT encounters unsupported opcodes
- **OR** complex control flow exceeds JIT capabilities
- **OR** runtime conditions require interpreter flexibility
- **THEN** the system SHALL trigger fallback to interpreter
- **AND** the fallback SHALL be transparent to the calling context

### Requirement: Execution state management across modes
The system SHALL maintain consistent EVM execution state regardless of execution mode transitions.

#### Scenario: State consistency validation
- **WHEN** execution mode changes occur
- **THEN** all EVM state components SHALL be validated for consistency
- **AND** any state corruption SHALL result in execution failure
- **AND** deterministic execution SHALL be maintained across transitions

#### Scenario: Cross-mode gas accounting
- **WHEN** execution transitions between JIT and interpreter
- **THEN** gas consumption SHALL be tracked continuously
- **AND** gas costs SHALL be identical regardless of execution mode
- **AND** gas exhaustion SHALL be detected consistently
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# evm-jit-fallback Specification Delta

## ADDED Requirements

### Requirement: JIT fallback interface in EVMMirBuilder
The EVMMirBuilder SHALL provide a fallbackToInterpreter method to transition execution from JIT to interpreter mode.

#### Scenario: Fallback method interface
- **WHEN** EVMMirBuilder needs to fallback to interpreter
- **THEN** it SHALL call fallbackToInterpreter(uint64_t targetPC)
- **AND** the method SHALL generate MIR instructions to save current execution state
- **AND** it SHALL call the runtime fallback function via callRuntimeFor

#### Scenario: State synchronization before fallback
- **WHEN** fallbackToInterpreter is called
- **THEN** the current stack state SHALL be flushed to EVMInstance
- **AND** memory state SHALL be synchronized with EVMInstance
- **AND** the target PC SHALL be validated as a valid instruction boundary

#### Scenario: Runtime function invocation
- **WHEN** state synchronization completes
- **THEN** EVMMirBuilder SHALL call RuntimeFunctions.HandleFallback
- **AND** the call SHALL pass the EVMInstance pointer and target PC
- **AND** the call SHALL use the existing callRuntimeFor template mechanism

### Requirement: Runtime fallback function
The runtime system SHALL provide evmHandleFallback function to manage JIT-to-interpreter transition.

#### Scenario: Fallback function signature
- **WHEN** the runtime fallback function is defined
- **THEN** it SHALL have signature void evmHandleFallback(zen::runtime::EVMInstance *, uint64_t)
- **AND** it SHALL be registered in the RuntimeFunctions structure
- **AND** it SHALL be accessible via the existing function table mechanism

#### Scenario: Interpreter instance creation
- **WHEN** evmHandleFallback is called
- **THEN** it SHALL create a new interpreter instance
- **AND** the interpreter SHALL be initialized with the provided EVMInstance
- **AND** execution SHALL resume from the specified PC

#### Scenario: State validation during transition
- **WHEN** the fallback function processes the transition
- **THEN** it SHALL validate the target PC is within bytecode bounds
- **AND** it SHALL verify stack state consistency
- **AND** it SHALL ensure memory state integrity

### Requirement: Interpreter state restoration
The EVM interpreter SHALL support execution from arbitrary EVM state provided by JIT fallback.

#### Scenario: State-based execution entry point
- **WHEN** interpreter receives fallback execution request
- **THEN** it SHALL provide executeFromState(EVMInstance*, uint64_t) method
- **AND** the method SHALL restore execution context from EVMInstance
- **AND** execution SHALL begin at the specified PC

#### Scenario: Stack state restoration
- **WHEN** executeFromState is called
- **THEN** the interpreter SHALL restore stack contents from EVMInstance
- **AND** it SHALL set the stack pointer to the correct position
- **AND** it SHALL validate stack size constraints

#### Scenario: Memory state consistency
- **WHEN** interpreter resumes execution
- **THEN** it SHALL use the existing memory from EVMInstance
- **AND** memory operations SHALL be consistent with JIT memory semantics
- **AND** memory size and growth behavior SHALL remain unchanged

### Requirement: Execution state preservation
The fallback mechanism SHALL preserve complete EVM execution state across the JIT-interpreter boundary.

#### Scenario: Program counter preservation
- **WHEN** fallback occurs at instruction boundary
- **THEN** the exact PC value SHALL be preserved
- **AND** interpreter SHALL resume at the correct bytecode position
- **AND** no instructions SHALL be skipped or repeated

#### Scenario: Stack state preservation
- **WHEN** JIT execution has modified the stack
- **THEN** all stack values SHALL be preserved in correct order
- **AND** stack size SHALL be maintained accurately
- **AND** stack overflow/underflow conditions SHALL be preserved

#### Scenario: Gas accounting continuity
- **WHEN** execution transitions from JIT to interpreter
- **THEN** remaining gas SHALL be preserved exactly
- **AND** gas costs SHALL continue to be tracked consistently
- **AND** gas exhaustion conditions SHALL be handled identically
Loading