Skip to content

Commit 3706c53

Browse files
authored
Bytecode parity (#7535)
* Add CFG block splitting, jump threading, backward jump normalization, genexpr StopIteration wrapper - split_blocks_at_jumps: split blocks at branch points so each has one exit - jump_threading: thread jumps through single-jump blocks (flowgraph.c jump_thread) - Backward conditional jump normalization: invert and create NOT_TAKEN+JUMP block - Follow empty blocks in jump-to-return optimization (next_nonempty_block) - Add PEP 479 StopIteration handler to compile_comprehension for generators * Add ConstantData::Slice and constant slice folding - Add Slice variant to ConstantData and BorrowedConstant - Fold constant slices (x[:3], x[1:4]) into LOAD_CONST(slice(...)) - Marshal serialization/deserialization for Slice type - Box::leak in borrow_obj_constant for PySlice roundtrip * Add ConstantData::Frozenset variant (type only, folding deferred) Add Frozenset to ConstantData, BorrowedConstant, and marshal support. Actual frozenset folding (BUILD_SET + CONTAINS_OP → LOAD_CONST frozenset) requires VirtualMachine for element hashing and is deferred. * Add duplicate_exits_without_lineno (disabled) and Block: Clone Prepare infrastructure for exit block duplication optimization. Currently disabled pending stackdepth integration.
1 parent e6bcd64 commit 3706c53

File tree

6 files changed

+540
-58
lines changed

6 files changed

+540
-58
lines changed

crates/codegen/src/compile.rs

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7574,6 +7574,11 @@ impl Compiler {
75747574
ast::Expr::Slice(ast::ExprSlice {
75757575
lower, upper, step, ..
75767576
}) => {
7577+
// Try constant slice folding first
7578+
if self.try_fold_constant_slice(lower.as_deref(), upper.as_deref(), step.as_deref())
7579+
{
7580+
return Ok(());
7581+
}
75777582
let mut compile_bound = |bound: Option<&ast::Expr>| match bound {
75787583
Some(exp) => self.compile_expression(exp),
75797584
None => {
@@ -8407,6 +8412,24 @@ impl Compiler {
84078412
let arg0 = self.varname(".0")?;
84088413

84098414
let return_none = init_collection.is_none();
8415+
8416+
// PEP 479: Wrap generator/coroutine body with StopIteration handler
8417+
let is_gen_scope = self.current_symbol_table().is_generator || is_async;
8418+
let stop_iteration_block = if is_gen_scope {
8419+
let handler_block = self.new_block();
8420+
emit!(
8421+
self,
8422+
PseudoInstruction::SetupCleanup {
8423+
delta: handler_block
8424+
}
8425+
);
8426+
self.set_no_location();
8427+
self.push_fblock(FBlockType::StopIteration, handler_block, handler_block)?;
8428+
Some(handler_block)
8429+
} else {
8430+
None
8431+
};
8432+
84108433
// Create empty object of proper type:
84118434
if let Some(init_collection) = init_collection {
84128435
self._emit(init_collection, OpArg::new(0), BlockIdx::NULL)
@@ -8496,6 +8519,23 @@ impl Compiler {
84968519

84978520
self.emit_return_value();
84988521

8522+
// Close StopIteration handler and emit handler code
8523+
if let Some(handler_block) = stop_iteration_block {
8524+
emit!(self, PseudoInstruction::PopBlock);
8525+
self.set_no_location();
8526+
self.pop_fblock(FBlockType::StopIteration);
8527+
self.switch_to_block(handler_block);
8528+
emit!(
8529+
self,
8530+
Instruction::CallIntrinsic1 {
8531+
func: oparg::IntrinsicFunction1::StopIterationError
8532+
}
8533+
);
8534+
self.set_no_location();
8535+
emit!(self, Instruction::Reraise { depth: 1u32 });
8536+
self.set_no_location();
8537+
}
8538+
84998539
let code = self.exit_scope();
85008540

85018541
self.ctx = prev_ctx;
@@ -8949,6 +8989,46 @@ impl Compiler {
89498989
self.emit_arg(idx, |consti| Instruction::LoadConst { consti })
89508990
}
89518991

8992+
/// Fold constant slice: if all parts are compile-time constants, emit LOAD_CONST(slice).
8993+
fn try_fold_constant_slice(
8994+
&mut self,
8995+
lower: Option<&ast::Expr>,
8996+
upper: Option<&ast::Expr>,
8997+
step: Option<&ast::Expr>,
8998+
) -> bool {
8999+
fn to_const(expr: Option<&ast::Expr>) -> Option<ConstantData> {
9000+
match expr {
9001+
None | Some(ast::Expr::NoneLiteral(_)) => Some(ConstantData::None),
9002+
Some(ast::Expr::NumberLiteral(ast::ExprNumberLiteral { value, .. })) => match value
9003+
{
9004+
ast::Number::Int(i) => Some(ConstantData::Integer {
9005+
value: i.as_i64()?.into(),
9006+
}),
9007+
ast::Number::Float(f) => Some(ConstantData::Float { value: *f }),
9008+
ast::Number::Complex { real, imag } => Some(ConstantData::Complex {
9009+
value: num_complex::Complex64::new(*real, *imag),
9010+
}),
9011+
},
9012+
Some(ast::Expr::BooleanLiteral(ast::ExprBooleanLiteral { value, .. })) => {
9013+
Some(ConstantData::Boolean { value: *value })
9014+
}
9015+
// Only match Constant_kind nodes (no UnaryOp)
9016+
_ => None,
9017+
}
9018+
}
9019+
9020+
let (Some(start), Some(stop), Some(step_val)) =
9021+
(to_const(lower), to_const(upper), to_const(step))
9022+
else {
9023+
return false;
9024+
};
9025+
9026+
self.emit_load_const(ConstantData::Slice {
9027+
elements: Box::new([start, stop, step_val]),
9028+
});
9029+
true
9030+
}
9031+
89529032
fn emit_return_const(&mut self, constant: ConstantData) {
89539033
self.emit_load_const(constant);
89549034
emit!(self, Instruction::ReturnValue)

0 commit comments

Comments
 (0)