pub struct StackScheduler {
pub stack: StackModel,
pub spills: SpillManager,
/* private fields */
}Expand description
Stack scheduler that generates stack manipulation operations.
Fields§
§stack: StackModelCurrent stack state.
spills: SpillManagerSpill manager for values beyond stack depth 16.
Implementations§
Source§impl StackScheduler
impl StackScheduler
Sourcepub fn new() -> StackScheduler
pub fn new() -> StackScheduler
Creates a new stack scheduler.
Sourcepub fn take_ops(&mut self) -> Vec<ScheduledOp>
pub fn take_ops(&mut self) -> Vec<ScheduledOp>
Takes the scheduled operations.
Sourcepub fn ensure_on_top(
&mut self,
value: ValueId,
func: &Function,
) -> &[ScheduledOp]
pub fn ensure_on_top( &mut self, value: ValueId, func: &Function, ) -> &[ScheduledOp]
Ensures a value is on top of the stack. Returns the operations needed to achieve this.
Sourcepub fn ensure_operand_on_top(
&mut self,
value: ValueId,
func: &Function,
) -> &[ScheduledOp]
pub fn ensure_operand_on_top( &mut self, value: ValueId, func: &Function, ) -> &[ScheduledOp]
Emits a fresh operand occurrence for a consuming instruction.
If value is already on top, ensure_on_top can claim that existing stack item. That is
correct for a single use, but wrong for instructions that consume the same MIR value more
than once, such as revert(x, x) or log1(x, x, x). In those cases every operand
occurrence needs its own stack item, so a top-of-stack value must be duplicated.
Sourcepub fn can_emit_value(&self, value: ValueId, func: &Function) -> bool
pub fn can_emit_value(&self, value: ValueId, func: &Function) -> bool
Checks if we can emit a value (it’s an immediate, arg, on stack, or spilled). Returns false for instruction results that aren’t tracked.
Sourcepub fn ensure_on_top_many(
&mut self,
values: &[ValueId],
func: &Function,
) -> Vec<ScheduledOp>
pub fn ensure_on_top_many( &mut self, values: &[ValueId], func: &Function, ) -> Vec<ScheduledOp>
Ensures multiple values are on top of the stack in order. The first value will be at the top, second below it, etc.
Sourcepub fn bring_to_top(&mut self, value: ValueId) -> Option<StackOp>
pub fn bring_to_top(&mut self, value: ValueId) -> Option<StackOp>
Brings a specific value to the top of the stack using SWAP. The value must already be on the stack within accessible range.
Sourcepub fn instruction_executed(
&mut self,
consumed: usize,
produced: Option<ValueId>,
)
pub fn instruction_executed( &mut self, consumed: usize, produced: Option<ValueId>, )
Records that an instruction consumed its operands and produced a result. This updates the stack model accordingly.
Sourcepub fn instruction_executed_untracked(&mut self, consumed: usize)
pub fn instruction_executed_untracked(&mut self, consumed: usize)
Records that an instruction consumed inputs and produced an untracked output. The output is on the EVM stack but we don’t track which ValueId it corresponds to. This is used for MLOAD where the value may become stale in loops.
Sourcepub fn has_untracked_on_top(&self) -> bool
pub fn has_untracked_on_top(&self) -> bool
Checks if there’s an untracked value on top of the stack.
Sourcepub fn has_untracked_at_depth(&self, depth: usize) -> bool
pub fn has_untracked_at_depth(&self, depth: usize) -> bool
Checks if there’s an untracked value at a specific depth.
Sourcepub fn stack_swapped(&mut self)
pub fn stack_swapped(&mut self)
Records that a SWAP1 was executed, updating the stack model.
Sourcepub fn drop_dead_values(
&mut self,
liveness: &Liveness,
block: BlockId,
inst_idx: usize,
) -> Vec<StackOp>
pub fn drop_dead_values( &mut self, liveness: &Liveness, block: BlockId, inst_idx: usize, ) -> Vec<StackOp>
Drops dead values from the stack. Returns operations (SWAPs and POPs) to remove dead values.
Sourcepub fn spill_excess_values(&mut self) -> Vec<ScheduledOp>
pub fn spill_excess_values(&mut self) -> Vec<ScheduledOp>
Spills values to memory to make room on the stack. This is needed when stack depth exceeds 16.
Sourcepub fn stack_depth(&self) -> usize
pub fn stack_depth(&self) -> usize
Returns the current stack depth.
Sourcepub fn clear_stack(&mut self)
pub fn clear_stack(&mut self)
Clears the stack model (used at block boundaries).
Sourcepub fn shuffle_to_layout(&mut self, target: &[TargetSlot]) -> ShuffleResult
pub fn shuffle_to_layout(&mut self, target: &[TargetSlot]) -> ShuffleResult
Shuffles the current stack to match the target layout.
This uses the backward layout optimization approach:
- Given a target layout (what we want the stack to look like)
- Generate the minimal sequence of DUP/SWAP/POP operations
Returns the shuffle result containing the operations to emit.
Sourcepub fn prepare_binary_op(
&mut self,
a: ValueId,
b: ValueId,
_func: &Function,
) -> ShuffleResult
pub fn prepare_binary_op( &mut self, a: ValueId, b: ValueId, _func: &Function, ) -> ShuffleResult
Prepares the stack for a binary operation.
Given operands (a, b) where a should be on top and b below:
- Computes the target layout [a, b, …rest]
- Shuffles current stack to match
- Returns operations to emit
Sourcepub fn prepare_unary_op(
&mut self,
operand: ValueId,
_func: &Function,
) -> ShuffleResult
pub fn prepare_unary_op( &mut self, operand: ValueId, _func: &Function, ) -> ShuffleResult
Prepares the stack for a unary operation.
Given operand that should be on top:
- Computes the target layout [operand, …rest]
- Shuffles current stack to match
- Returns operations to emit
Sourcepub fn compute_binary_entry_layout(
a: ValueId,
b: ValueId,
result: Option<ValueId>,
exit_layout: &[TargetSlot],
) -> Vec<TargetSlot>
pub fn compute_binary_entry_layout( a: ValueId, b: ValueId, result: Option<ValueId>, exit_layout: &[TargetSlot], ) -> Vec<TargetSlot>
Computes the ideal entry layout for a binary operation given the exit layout.
For ADD(a, b) -> result, if exit layout is [result, x, y]:
- Entry layout should be [a, b, x, y]
Sourcepub fn compute_unary_entry_layout(
operand: ValueId,
result: Option<ValueId>,
exit_layout: &[TargetSlot],
) -> Vec<TargetSlot>
pub fn compute_unary_entry_layout( operand: ValueId, result: Option<ValueId>, exit_layout: &[TargetSlot], ) -> Vec<TargetSlot>
Computes the ideal entry layout for a unary operation given the exit layout.
Sourcepub fn set_block_entry_layout(
&mut self,
block: BlockId,
layout: BlockStackLayout,
)
pub fn set_block_entry_layout( &mut self, block: BlockId, layout: BlockStackLayout, )
Sets the entry layout for a block.
This is used to specify what the stack should look like when entering a block. Predecessors will shuffle their stacks to match this layout.
Sourcepub fn get_block_entry_layout(
&self,
block: BlockId,
) -> Option<&BlockStackLayout>
pub fn get_block_entry_layout( &self, block: BlockId, ) -> Option<&BlockStackLayout>
Gets the entry layout for a block, if one has been set.
Sourcepub fn record_block_exit_layout(&mut self, block: BlockId)
pub fn record_block_exit_layout(&mut self, block: BlockId)
Records the exit layout for a block (current stack state).
This is called after generating a block’s instructions, before the terminator. The layout is used to determine what values are on the stack when exiting.
Sourcepub fn get_block_exit_layout(&self, block: BlockId) -> Option<&BlockStackLayout>
pub fn get_block_exit_layout(&self, block: BlockId) -> Option<&BlockStackLayout>
Gets the exit layout for a block, if one has been recorded.
Sourcepub fn compute_merge_layout(
&self,
predecessors: &[BlockId],
live_in: impl IntoIterator<Item = ValueId>,
) -> BlockStackLayout
pub fn compute_merge_layout( &self, predecessors: &[BlockId], live_in: impl IntoIterator<Item = ValueId>, ) -> BlockStackLayout
Computes the entry layout for a merge block from its predecessors’ exit layouts.
This is the key function for phi node handling. It finds a common stack layout that all predecessors can shuffle to with minimal cost.
The function also considers the live-in values for the block to ensure all needed values are on the stack.
Sourcepub fn shuffle_to_block_entry(&mut self, target_block: BlockId) -> ShuffleResult
pub fn shuffle_to_block_entry(&mut self, target_block: BlockId) -> ShuffleResult
Shuffles the current stack to match a block’s entry layout.
Returns the shuffle operations needed. The caller is responsible for emitting the actual opcodes.
Sourcepub fn init_from_block_entry_layout(&mut self, block: BlockId)
pub fn init_from_block_entry_layout(&mut self, block: BlockId)
Initializes the stack from a block’s entry layout.
This is called when starting to generate a block that has an entry layout. It sets up the stack model to match the expected entry state.
Sourcepub fn clear_block_layouts(&mut self)
pub fn clear_block_layouts(&mut self)
Clears all block layout information.
Called when starting to generate a new function.
Sourcepub fn has_block_entry_layout(&self, block: BlockId) -> bool
pub fn has_block_entry_layout(&self, block: BlockId) -> bool
Returns true if a block has an entry layout set.
Sourcepub fn stack_matches_entry_layout(&self, target_block: BlockId) -> bool
pub fn stack_matches_entry_layout(&self, target_block: BlockId) -> bool
Checks if the current stack matches a block’s entry layout.
Returns true if the stack already matches the target layout (no shuffling needed).
Trait Implementations§
Source§impl Default for StackScheduler
impl Default for StackScheduler
Source§fn default() -> StackScheduler
fn default() -> StackScheduler
Auto Trait Implementations§
impl Freeze for StackScheduler
impl RefUnwindSafe for StackScheduler
impl Send for StackScheduler
impl Sync for StackScheduler
impl Unpin for StackScheduler
impl UnsafeUnpin for StackScheduler
impl UnwindSafe for StackScheduler
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T, R> CollectAndApply<T, R> for T
impl<T, R> CollectAndApply<T, R> for T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more