mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 18:59:04 +08:00
Bytecode parity (#7481)
* Bytecode parity
Compiler changes:
- Remove PUSH_NULL from decorator cal
ls, use CALL 0
- Collect __static_attributes__ from self.xxx = patterns
- Sort __static_attributes__ alphabetically
- Move __classdict__ init before __doc__ in class prologue
- Fold unary negative constants
- Fold constant list/set literals (3+ elements)
- Use BUILD_MAP 0 + MAP_ADD for 16+ dict pairs
- Always run peephole optimizer for s
uperinstructions
- Emit RETURN_GENERATOR for generator
functions
- Add is_generator flag to SymbolTabl
e
* Fix formatting and collapsible_if clippy warnings in compile.rs
* Fix clippy, fold_unary_negative chaining, and generator line tracing
- Replace irrefutable if-let with let for ExceptHandler
- Remove folded UNARY_NEGATIVE instead of replacing with NOP,
enabling chained negation folding
- Initialize prev_line to def line for generators/coroutines
to suppress spurious LINE events from preamble instructions
- Remove expectedFailure markers for now-passing tests
* Fix JIT StoreFastStoreFast, format, and remove expectedFailure markers
- Add StoreFastStoreFast handling in JIT instructions
- Fix cargo fmt in frame.rs
- Remove 11 expectedFailure markers for async jump tests in
test_sys_settrace that now pass
* Fix peephole optimizer: use NOP replacement instead of remove()
Using remove() shifts instruction indices and corrupts subsequent
references, causing "pop stackref but null found" panics at runtime.
Replace folded/combined instructions with NOP instead, which are
cleaned up by the existing remove_nops pass.
* Revert peephole_optimize to use remove() for chaining support
NOP replacement broke chaining of peephole optimizations (e.g.
LOAD_CONST+TO_BOOL then LOAD_CONST+UNARY_NOT for 'not True').
The remove() approach is used by upstream and works correctly here;
fold_unary_negative keeps NOP replacement since it doesn't need chaining.
* Fix StoreFastStoreFast to handle NULL from LoadFastAndClear
StoreFast uses pop_value_opt() to allow NULL values from
LoadFastAndClear in inlined comprehension cleanup paths.
StoreFastStoreFast must do the same, otherwise the peephole
optimizer's fusion of two StoreFast instructions panics when
restoring unbound locals after an inlined comprehension.
This commit is contained in:
1 parent
6b5c5a9e92
commit
ea5a6cd9c0
12 files changed
+519
-209
No files matched your search
Vendored
-4
@@ -2486,7 +2486,6 @@ class TestSourcePositions(unittest.TestCase):
|
||||
|
||||
class TestStaticAttributes(unittest.TestCase):
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: type object 'C' has no attribute '__static_attributes__'
|
||||
def test_basic(self):
|
||||
class C:
|
||||
def f(self):
|
||||
@@ -2518,7 +2517,6 @@ class TestStaticAttributes(unittest.TestCase):
|
||||
|
||||
self.assertEqual(sorted(C.__static_attributes__), ['u', 'v', 'x', 'y', 'z'])
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: type object 'C' has no attribute '__static_attributes__'
|
||||
def test_nested_class(self):
|
||||
class C:
|
||||
def f(self):
|
||||
@@ -2533,7 +2531,6 @@ class TestStaticAttributes(unittest.TestCase):
|
||||
self.assertEqual(sorted(C.__static_attributes__), ['x', 'y'])
|
||||
self.assertEqual(sorted(C.D.__static_attributes__), ['y', 'z'])
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: type object 'C' has no attribute '__static_attributes__'
|
||||
def test_subclass(self):
|
||||
class C:
|
||||
def f(self):
|
||||
@@ -2593,7 +2590,6 @@ class TestExpressionStackSize(unittest.TestCase):
|
||||
def test_set(self):
|
||||
self.check_stack_size("{" + "x, " * self.N + "x}")
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: 202 not less than or equal to 7
|
||||
def test_dict(self):
|
||||
self.check_stack_size("{" + "x:x, " * self.N + "x:x}")
|
||||
|
||||
|
||||
Vendored
-1
@@ -862,7 +862,6 @@ class TestMarkingVariablesAsUnKnown(BytecodeTestCase):
|
||||
self.addCleanup(sys.settrace, sys.gettrace())
|
||||
sys.settrace(None)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; no LOAD_FAST_BORROW_LOAD_FAST_BORROW superinstruction
|
||||
def test_load_fast_known_simple(self):
|
||||
def f():
|
||||
x = 1
|
||||
|
||||
Vendored
-22
@@ -1420,8 +1420,6 @@ class JumpTestCase(unittest.TestCase):
|
||||
output.append(6)
|
||||
output.append(7)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@async_jump_test(4, 5, [3, 5])
|
||||
async def test_jump_out_of_async_for_block_forwards(output):
|
||||
for i in [1]:
|
||||
@@ -1430,8 +1428,6 @@ class JumpTestCase(unittest.TestCase):
|
||||
output.append(4)
|
||||
output.append(5)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@async_jump_test(5, 2, [2, 4, 2, 4, 5, 6])
|
||||
async def test_jump_out_of_async_for_block_backwards(output):
|
||||
for i in [1]:
|
||||
@@ -1539,8 +1535,6 @@ class JumpTestCase(unittest.TestCase):
|
||||
output.append(2)
|
||||
output.append(3)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@async_jump_test(2, 3, [1, 3])
|
||||
async def test_jump_forwards_out_of_async_with_block(output):
|
||||
async with asynctracecontext(output, 1):
|
||||
@@ -1553,8 +1547,6 @@ class JumpTestCase(unittest.TestCase):
|
||||
with tracecontext(output, 2):
|
||||
output.append(3)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@async_jump_test(3, 1, [1, 2, 1, 2, 3, -2])
|
||||
async def test_jump_backwards_out_of_async_with_block(output):
|
||||
output.append(1)
|
||||
@@ -1624,8 +1616,6 @@ class JumpTestCase(unittest.TestCase):
|
||||
with tracecontext(output, 4):
|
||||
output.append(5)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@async_jump_test(2, 4, [1, 4, 5, -4])
|
||||
async def test_jump_across_async_with(output):
|
||||
output.append(1)
|
||||
@@ -1643,8 +1633,6 @@ class JumpTestCase(unittest.TestCase):
|
||||
output.append(5)
|
||||
output.append(6)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@async_jump_test(4, 5, [1, 3, 5, 6])
|
||||
async def test_jump_out_of_async_with_block_within_for_block(output):
|
||||
output.append(1)
|
||||
@@ -1663,8 +1651,6 @@ class JumpTestCase(unittest.TestCase):
|
||||
output.append(5)
|
||||
output.append(6)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@async_jump_test(4, 5, [1, 2, 3, 5, -2, 6])
|
||||
async def test_jump_out_of_async_with_block_within_with_block(output):
|
||||
output.append(1)
|
||||
@@ -1684,8 +1670,6 @@ class JumpTestCase(unittest.TestCase):
|
||||
output.append(6)
|
||||
output.append(7)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@async_jump_test(5, 6, [2, 4, 6, 7])
|
||||
async def test_jump_out_of_async_with_block_within_finally_block(output):
|
||||
try:
|
||||
@@ -1719,8 +1703,6 @@ class JumpTestCase(unittest.TestCase):
|
||||
output.append(4)
|
||||
output.append(5)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@async_jump_test(3, 5, [1, 2, 5])
|
||||
async def test_jump_out_of_async_with_assignment(output):
|
||||
output.append(1)
|
||||
@@ -1768,8 +1750,6 @@ class JumpTestCase(unittest.TestCase):
|
||||
output.append(7)
|
||||
output.append(8)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@async_jump_test(1, 7, [7, 8])
|
||||
async def test_jump_over_async_for_block_before_else(output):
|
||||
output.append(1)
|
||||
@@ -2053,8 +2033,6 @@ class JumpTestCase(unittest.TestCase):
|
||||
with tracecontext(output, 4):
|
||||
output.append(5)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@async_jump_test(3, 5, [1, 2, 5, -2])
|
||||
async def test_jump_between_async_with_blocks(output):
|
||||
output.append(1)
|
||||
|
||||
+138
-32
@@ -1200,8 +1200,10 @@ impl Compiler {
|
||||
/// Emit RESUME instruction with proper handling for async preamble and module lineno.
|
||||
/// codegen_enter_scope equivalent for RESUME emission.
|
||||
fn emit_resume_for_scope(&mut self, scope_type: CompilerScope, lineno: u32) {
|
||||
// For async functions/coroutines, emit RETURN_GENERATOR + POP_TOP before RESUME
|
||||
if scope_type == CompilerScope::AsyncFunction {
|
||||
// For generators and async functions, emit RETURN_GENERATOR + POP_TOP before RESUME
|
||||
let is_gen =
|
||||
scope_type == CompilerScope::AsyncFunction || self.current_symbol_table().is_generator;
|
||||
if is_gen {
|
||||
emit!(self, Instruction::ReturnGenerator);
|
||||
emit!(self, Instruction::PopTop);
|
||||
}
|
||||
@@ -2758,18 +2760,15 @@ impl Compiler {
|
||||
fn prepare_decorators(&mut self, decorator_list: &[ast::Decorator]) -> CompileResult<()> {
|
||||
for decorator in decorator_list {
|
||||
self.compile_expression(&decorator.expression)?;
|
||||
emit!(self, Instruction::PushNull);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Apply decorators in reverse order (LIFO from stack).
|
||||
/// Stack [dec1, NULL, dec2, NULL, func] -> dec2(func) -> dec1(dec2(func))
|
||||
/// The forward loop works because each Call pops from TOS, naturally
|
||||
/// applying decorators bottom-up (innermost first).
|
||||
/// Apply decorators: each decorator calls the function below it.
|
||||
/// Stack: [dec1, dec2, func] → CALL 0 → [dec1, dec2(func)] → CALL 0 → [dec1(dec2(func))]
|
||||
fn apply_decorators(&mut self, decorator_list: &[ast::Decorator]) {
|
||||
for _ in decorator_list {
|
||||
emit!(self, Instruction::Call { argc: 1 });
|
||||
emit!(self, Instruction::Call { argc: 0 });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4510,6 +4509,93 @@ impl Compiler {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Collect attribute names assigned via `self.xxx = ...` in methods.
|
||||
/// These are stored as __static_attributes__ in the class dict.
|
||||
fn collect_static_attributes(body: &[ast::Stmt], attrs: Option<&mut IndexSet<String>>) {
|
||||
let Some(attrs) = attrs else { return };
|
||||
for stmt in body {
|
||||
// Only scan def/async def at class body level
|
||||
let (params, func_body) = match stmt {
|
||||
ast::Stmt::FunctionDef(f) => (&f.parameters, &f.body),
|
||||
_ => continue,
|
||||
};
|
||||
// Get first parameter name (usually "self" or "cls")
|
||||
let first_param = params
|
||||
.args
|
||||
.first()
|
||||
.or(params.posonlyargs.first())
|
||||
.map(|p| &p.parameter.name);
|
||||
let Some(self_name) = first_param else {
|
||||
continue;
|
||||
};
|
||||
// Scan function body for self.xxx = ... (STORE_ATTR on first param)
|
||||
Self::scan_store_attrs(func_body, self_name.as_str(), attrs);
|
||||
}
|
||||
}
|
||||
|
||||
/// Recursively scan statements for `name.attr = value` patterns.
|
||||
fn scan_store_attrs(stmts: &[ast::Stmt], name: &str, attrs: &mut IndexSet<String>) {
|
||||
for stmt in stmts {
|
||||
match stmt {
|
||||
ast::Stmt::Assign(a) => {
|
||||
for target in &a.targets {
|
||||
if let ast::Expr::Attribute(ast::ExprAttribute { value, attr, .. }) = target
|
||||
&& let ast::Expr::Name(n) = value.as_ref()
|
||||
&& n.id.as_str() == name
|
||||
{
|
||||
attrs.insert(attr.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
ast::Stmt::AnnAssign(a) => {
|
||||
if let ast::Expr::Attribute(ast::ExprAttribute { value, attr, .. }) =
|
||||
a.target.as_ref()
|
||||
&& let ast::Expr::Name(n) = value.as_ref()
|
||||
&& n.id.as_str() == name
|
||||
{
|
||||
attrs.insert(attr.to_string());
|
||||
}
|
||||
}
|
||||
ast::Stmt::AugAssign(a) => {
|
||||
if let ast::Expr::Attribute(ast::ExprAttribute { value, attr, .. }) =
|
||||
a.target.as_ref()
|
||||
&& let ast::Expr::Name(n) = value.as_ref()
|
||||
&& n.id.as_str() == name
|
||||
{
|
||||
attrs.insert(attr.to_string());
|
||||
}
|
||||
}
|
||||
ast::Stmt::If(s) => {
|
||||
Self::scan_store_attrs(&s.body, name, attrs);
|
||||
for clause in &s.elif_else_clauses {
|
||||
Self::scan_store_attrs(&clause.body, name, attrs);
|
||||
}
|
||||
}
|
||||
ast::Stmt::For(s) => {
|
||||
Self::scan_store_attrs(&s.body, name, attrs);
|
||||
Self::scan_store_attrs(&s.orelse, name, attrs);
|
||||
}
|
||||
ast::Stmt::While(s) => {
|
||||
Self::scan_store_attrs(&s.body, name, attrs);
|
||||
Self::scan_store_attrs(&s.orelse, name, attrs);
|
||||
}
|
||||
ast::Stmt::Try(s) => {
|
||||
Self::scan_store_attrs(&s.body, name, attrs);
|
||||
for handler in &s.handlers {
|
||||
let ast::ExceptHandler::ExceptHandler(h) = handler;
|
||||
Self::scan_store_attrs(&h.body, name, attrs);
|
||||
}
|
||||
Self::scan_store_attrs(&s.orelse, name, attrs);
|
||||
Self::scan_store_attrs(&s.finalbody, name, attrs);
|
||||
}
|
||||
ast::Stmt::With(s) => {
|
||||
Self::scan_store_attrs(&s.body, name, attrs);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Python/compile.c find_ann
|
||||
fn find_ann(body: &[ast::Stmt]) -> bool {
|
||||
for statement in body {
|
||||
@@ -4617,6 +4703,13 @@ impl Compiler {
|
||||
}
|
||||
);
|
||||
|
||||
// PEP 649: Initialize __classdict__ cell (before __doc__)
|
||||
if self.current_symbol_table().needs_classdict {
|
||||
emit!(self, Instruction::LoadLocals);
|
||||
let classdict_idx = self.get_cell_var_index("__classdict__")?;
|
||||
emit!(self, Instruction::StoreDeref { i: classdict_idx });
|
||||
}
|
||||
|
||||
// Store __doc__ only if there's an explicit docstring
|
||||
if let Some(doc) = doc_str {
|
||||
self.emit_load_const(ConstantData::Str { value: doc.into() });
|
||||
@@ -4645,13 +4738,6 @@ impl Compiler {
|
||||
);
|
||||
}
|
||||
|
||||
// PEP 649: Initialize __classdict__ cell for class annotation scope
|
||||
if self.current_symbol_table().needs_classdict {
|
||||
emit!(self, Instruction::LoadLocals);
|
||||
let classdict_idx = self.get_cell_var_index("__classdict__")?;
|
||||
emit!(self, Instruction::StoreDeref { i: classdict_idx });
|
||||
}
|
||||
|
||||
// Handle class annotations based on future_annotations flag
|
||||
if Self::find_ann(body) {
|
||||
if self.future_annotations {
|
||||
@@ -4669,6 +4755,16 @@ impl Compiler {
|
||||
}
|
||||
}
|
||||
|
||||
// Collect __static_attributes__: scan methods for self.xxx = ... patterns
|
||||
Self::collect_static_attributes(
|
||||
body,
|
||||
self.code_stack
|
||||
.last_mut()
|
||||
.unwrap()
|
||||
.static_attributes
|
||||
.as_mut(),
|
||||
);
|
||||
|
||||
// 3. Compile the class body
|
||||
self.compile_statements(body)?;
|
||||
|
||||
@@ -4684,7 +4780,7 @@ impl Compiler {
|
||||
|
||||
// Emit __static_attributes__ tuple
|
||||
{
|
||||
let attrs: Vec<String> = self
|
||||
let mut attrs: Vec<String> = self
|
||||
.code_stack
|
||||
.last()
|
||||
.unwrap()
|
||||
@@ -4692,6 +4788,7 @@ impl Compiler {
|
||||
.as_ref()
|
||||
.map(|s| s.iter().cloned().collect())
|
||||
.unwrap_or_default();
|
||||
attrs.sort();
|
||||
self.emit_load_const(ConstantData::Tuple {
|
||||
elements: attrs
|
||||
.into_iter()
|
||||
@@ -5091,8 +5188,7 @@ impl Compiler {
|
||||
method: SpecialMethod::AEnter
|
||||
}
|
||||
); // [bound_aexit, bound_aenter]
|
||||
// bound_aenter is already bound, call with NULL self_or_null
|
||||
emit!(self, Instruction::PushNull); // [bound_aexit, bound_aenter, NULL]
|
||||
emit!(self, Instruction::PushNull);
|
||||
emit!(self, Instruction::Call { argc: 0 }); // [bound_aexit, awaitable]
|
||||
emit!(self, Instruction::GetAwaitable { r#where: 1 });
|
||||
self.emit_load_const(ConstantData::None);
|
||||
@@ -5112,8 +5208,7 @@ impl Compiler {
|
||||
method: SpecialMethod::Enter
|
||||
}
|
||||
); // [bound_exit, bound_enter]
|
||||
// bound_enter is already bound, call with NULL self_or_null
|
||||
emit!(self, Instruction::PushNull); // [bound_exit, bound_enter, NULL]
|
||||
emit!(self, Instruction::PushNull);
|
||||
emit!(self, Instruction::Call { argc: 0 }); // [bound_exit, enter_result]
|
||||
}
|
||||
|
||||
@@ -5168,8 +5263,8 @@ impl Compiler {
|
||||
});
|
||||
|
||||
// ===== Normal exit path =====
|
||||
// Stack: [..., __exit__]
|
||||
// Call __exit__(None, None, None)
|
||||
// Stack: [..., bound_exit]
|
||||
// Call bound_exit(None, None, None)
|
||||
self.set_source_range(with_range);
|
||||
emit!(self, Instruction::PushNull);
|
||||
self.emit_load_const(ConstantData::None);
|
||||
@@ -6894,17 +6989,28 @@ impl Compiler {
|
||||
let has_unpacking = items.iter().any(|item| item.key.is_none());
|
||||
|
||||
if !has_unpacking {
|
||||
// Simple case: no ** unpacking, build all pairs directly
|
||||
for item in items {
|
||||
self.compile_expression(item.key.as_ref().unwrap())?;
|
||||
self.compile_expression(&item.value)?;
|
||||
}
|
||||
emit!(
|
||||
self,
|
||||
Instruction::BuildMap {
|
||||
count: u32::try_from(items.len()).expect("too many dict items"),
|
||||
// STACK_USE_GUIDELINE: for large dicts (16+ pairs), use
|
||||
// BUILD_MAP 0 + MAP_ADD to avoid excessive stack usage
|
||||
let big = items.len() * 2 > 30; // ~15 pairs threshold
|
||||
if big {
|
||||
emit!(self, Instruction::BuildMap { count: 0 });
|
||||
for item in items {
|
||||
self.compile_expression(item.key.as_ref().unwrap())?;
|
||||
self.compile_expression(&item.value)?;
|
||||
emit!(self, Instruction::MapAdd { i: 1 });
|
||||
}
|
||||
);
|
||||
} else {
|
||||
for item in items {
|
||||
self.compile_expression(item.key.as_ref().unwrap())?;
|
||||
self.compile_expression(&item.value)?;
|
||||
}
|
||||
emit!(
|
||||
self,
|
||||
Instruction::BuildMap {
|
||||
count: u32::try_from(items.len()).expect("too many dict items"),
|
||||
}
|
||||
);
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
|
||||
+250
-4
@@ -189,17 +189,19 @@ impl CodeInfo {
|
||||
mut self,
|
||||
opts: &crate::compile::CompileOpts,
|
||||
) -> crate::InternalResult<CodeObject> {
|
||||
// Always fold tuple constants
|
||||
// Constant folding passes
|
||||
self.fold_unary_negative();
|
||||
self.fold_tuple_constants();
|
||||
self.fold_list_constants();
|
||||
self.fold_set_constants();
|
||||
self.convert_to_load_small_int();
|
||||
self.remove_unused_consts();
|
||||
self.remove_nops();
|
||||
|
||||
// DCE always runs (removes dead code after terminal instructions)
|
||||
self.dce();
|
||||
if opts.optimize > 0 {
|
||||
self.peephole_optimize();
|
||||
}
|
||||
// Peephole optimizer creates superinstructions matching CPython
|
||||
self.peephole_optimize();
|
||||
|
||||
// Always apply LOAD_FAST_BORROW optimization
|
||||
self.optimize_load_fast_borrow();
|
||||
@@ -625,6 +627,61 @@ impl CodeInfo {
|
||||
}
|
||||
}
|
||||
|
||||
/// Fold LOAD_CONST/LOAD_SMALL_INT + UNARY_NEGATIVE → LOAD_CONST (negative value)
|
||||
fn fold_unary_negative(&mut self) {
|
||||
for block in &mut self.blocks {
|
||||
let mut i = 0;
|
||||
while i + 1 < block.instructions.len() {
|
||||
let next = &block.instructions[i + 1];
|
||||
let Some(Instruction::UnaryNegative) = next.instr.real() else {
|
||||
i += 1;
|
||||
continue;
|
||||
};
|
||||
let curr = &block.instructions[i];
|
||||
let value = match curr.instr.real() {
|
||||
Some(Instruction::LoadConst { .. }) => {
|
||||
let idx = u32::from(curr.arg) as usize;
|
||||
match self.metadata.consts.get_index(idx) {
|
||||
Some(ConstantData::Integer { value }) => {
|
||||
Some(ConstantData::Integer { value: -value })
|
||||
}
|
||||
Some(ConstantData::Float { value }) => {
|
||||
Some(ConstantData::Float { value: -value })
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
Some(Instruction::LoadSmallInt { .. }) => {
|
||||
let v = u32::from(curr.arg) as i32;
|
||||
Some(ConstantData::Integer {
|
||||
value: BigInt::from(-v),
|
||||
})
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
if let Some(neg_const) = value {
|
||||
let (const_idx, _) = self.metadata.consts.insert_full(neg_const);
|
||||
// Replace LOAD_CONST/LOAD_SMALL_INT with new LOAD_CONST
|
||||
let load_location = block.instructions[i].location;
|
||||
block.instructions[i].instr = Instruction::LoadConst {
|
||||
consti: Arg::marker(),
|
||||
}
|
||||
.into();
|
||||
block.instructions[i].arg = OpArg::new(const_idx as u32);
|
||||
// Replace UNARY_NEGATIVE with NOP, inheriting the LOAD_CONST
|
||||
// location so that remove_nops can clean it up
|
||||
block.instructions[i + 1].instr = Instruction::Nop.into();
|
||||
block.instructions[i + 1].location = load_location;
|
||||
block.instructions[i + 1].end_location = block.instructions[i].end_location;
|
||||
// Skip the NOP, don't re-check
|
||||
i += 2;
|
||||
} else {
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Constant folding: fold LOAD_CONST/LOAD_SMALL_INT + BUILD_TUPLE into LOAD_CONST tuple
|
||||
/// fold_tuple_of_constants
|
||||
fn fold_tuple_constants(&mut self) {
|
||||
@@ -723,6 +780,195 @@ impl CodeInfo {
|
||||
}
|
||||
}
|
||||
|
||||
/// Fold constant list literals: LOAD_CONST* + BUILD_LIST N →
|
||||
/// BUILD_LIST 0 + LOAD_CONST (tuple) + LIST_EXTEND 1
|
||||
fn fold_list_constants(&mut self) {
|
||||
for block in &mut self.blocks {
|
||||
let mut i = 0;
|
||||
while i < block.instructions.len() {
|
||||
let instr = &block.instructions[i];
|
||||
let Some(Instruction::BuildList { .. }) = instr.instr.real() else {
|
||||
i += 1;
|
||||
continue;
|
||||
};
|
||||
|
||||
let list_size = u32::from(instr.arg) as usize;
|
||||
if list_size == 0 || i < list_size {
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
let start_idx = i - list_size;
|
||||
let mut elements = Vec::with_capacity(list_size);
|
||||
let mut all_const = true;
|
||||
|
||||
for j in start_idx..i {
|
||||
let load_instr = &block.instructions[j];
|
||||
match load_instr.instr.real() {
|
||||
Some(Instruction::LoadConst { .. }) => {
|
||||
let const_idx = u32::from(load_instr.arg) as usize;
|
||||
if let Some(constant) =
|
||||
self.metadata.consts.get_index(const_idx).cloned()
|
||||
{
|
||||
elements.push(constant);
|
||||
} else {
|
||||
all_const = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
Some(Instruction::LoadSmallInt { .. }) => {
|
||||
let value = u32::from(load_instr.arg) as i32;
|
||||
elements.push(ConstantData::Integer {
|
||||
value: BigInt::from(value),
|
||||
});
|
||||
}
|
||||
_ => {
|
||||
all_const = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !all_const || list_size < 3 {
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
let tuple_const = ConstantData::Tuple { elements };
|
||||
let (const_idx, _) = self.metadata.consts.insert_full(tuple_const);
|
||||
|
||||
let folded_loc = block.instructions[i].location;
|
||||
let end_loc = block.instructions[i].end_location;
|
||||
let eh = block.instructions[i].except_handler;
|
||||
|
||||
// slot[start_idx] → BUILD_LIST 0
|
||||
block.instructions[start_idx].instr = Instruction::BuildList {
|
||||
count: Arg::marker(),
|
||||
}
|
||||
.into();
|
||||
block.instructions[start_idx].arg = OpArg::new(0);
|
||||
block.instructions[start_idx].location = folded_loc;
|
||||
block.instructions[start_idx].end_location = end_loc;
|
||||
block.instructions[start_idx].except_handler = eh;
|
||||
|
||||
// slot[start_idx+1] → LOAD_CONST (tuple)
|
||||
block.instructions[start_idx + 1].instr = Instruction::LoadConst {
|
||||
consti: Arg::marker(),
|
||||
}
|
||||
.into();
|
||||
block.instructions[start_idx + 1].arg = OpArg::new(const_idx as u32);
|
||||
block.instructions[start_idx + 1].location = folded_loc;
|
||||
block.instructions[start_idx + 1].end_location = end_loc;
|
||||
block.instructions[start_idx + 1].except_handler = eh;
|
||||
|
||||
// NOP the rest
|
||||
for j in (start_idx + 2)..i {
|
||||
block.instructions[j].instr = Instruction::Nop.into();
|
||||
block.instructions[j].location = folded_loc;
|
||||
}
|
||||
|
||||
// slot[i] (was BUILD_LIST) → LIST_EXTEND 1
|
||||
block.instructions[i].instr = Instruction::ListExtend { i: Arg::marker() }.into();
|
||||
block.instructions[i].arg = OpArg::new(1);
|
||||
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Fold constant set literals: LOAD_CONST* + BUILD_SET N →
|
||||
/// BUILD_SET 0 + LOAD_CONST (frozenset-as-tuple) + SET_UPDATE 1
|
||||
fn fold_set_constants(&mut self) {
|
||||
for block in &mut self.blocks {
|
||||
let mut i = 0;
|
||||
while i < block.instructions.len() {
|
||||
let instr = &block.instructions[i];
|
||||
let Some(Instruction::BuildSet { .. }) = instr.instr.real() else {
|
||||
i += 1;
|
||||
continue;
|
||||
};
|
||||
|
||||
let set_size = u32::from(instr.arg) as usize;
|
||||
if set_size < 3 || i < set_size {
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
let start_idx = i - set_size;
|
||||
let mut elements = Vec::with_capacity(set_size);
|
||||
let mut all_const = true;
|
||||
|
||||
for j in start_idx..i {
|
||||
let load_instr = &block.instructions[j];
|
||||
match load_instr.instr.real() {
|
||||
Some(Instruction::LoadConst { .. }) => {
|
||||
let const_idx = u32::from(load_instr.arg) as usize;
|
||||
if let Some(constant) =
|
||||
self.metadata.consts.get_index(const_idx).cloned()
|
||||
{
|
||||
elements.push(constant);
|
||||
} else {
|
||||
all_const = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
Some(Instruction::LoadSmallInt { .. }) => {
|
||||
let value = u32::from(load_instr.arg) as i32;
|
||||
elements.push(ConstantData::Integer {
|
||||
value: BigInt::from(value),
|
||||
});
|
||||
}
|
||||
_ => {
|
||||
all_const = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !all_const {
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Use FrozenSet constant (stored as Tuple for now)
|
||||
let const_data = ConstantData::Tuple { elements };
|
||||
let (const_idx, _) = self.metadata.consts.insert_full(const_data);
|
||||
|
||||
let folded_loc = block.instructions[i].location;
|
||||
let end_loc = block.instructions[i].end_location;
|
||||
let eh = block.instructions[i].except_handler;
|
||||
|
||||
block.instructions[start_idx].instr = Instruction::BuildSet {
|
||||
count: Arg::marker(),
|
||||
}
|
||||
.into();
|
||||
block.instructions[start_idx].arg = OpArg::new(0);
|
||||
block.instructions[start_idx].location = folded_loc;
|
||||
block.instructions[start_idx].end_location = end_loc;
|
||||
block.instructions[start_idx].except_handler = eh;
|
||||
|
||||
block.instructions[start_idx + 1].instr = Instruction::LoadConst {
|
||||
consti: Arg::marker(),
|
||||
}
|
||||
.into();
|
||||
block.instructions[start_idx + 1].arg = OpArg::new(const_idx as u32);
|
||||
block.instructions[start_idx + 1].location = folded_loc;
|
||||
block.instructions[start_idx + 1].end_location = end_loc;
|
||||
block.instructions[start_idx + 1].except_handler = eh;
|
||||
|
||||
for j in (start_idx + 2)..i {
|
||||
block.instructions[j].instr = Instruction::Nop.into();
|
||||
block.instructions[j].location = folded_loc;
|
||||
}
|
||||
|
||||
block.instructions[i].instr = Instruction::SetUpdate { i: Arg::marker() }.into();
|
||||
block.instructions[i].arg = OpArg::new(1);
|
||||
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Peephole optimization: combine consecutive instructions into super-instructions
|
||||
fn peephole_optimize(&mut self) {
|
||||
for block in &mut self.blocks {
|
||||
|
||||
+12
-24
@@ -1,35 +1,23 @@
|
||||
---
|
||||
source: crates/codegen/src/compile.rs
|
||||
assertion_line: 9317
|
||||
assertion_line: 9458
|
||||
expression: "compile_exec(\"\\\nif True and False and False:\n pass\n\")"
|
||||
---
|
||||
1 0 RESUME (0)
|
||||
1 LOAD_CONST (True)
|
||||
2 TO_BOOL
|
||||
2 POP_JUMP_IF_FALSE (11)
|
||||
>> 3 CACHE
|
||||
4 CACHE
|
||||
5 CACHE
|
||||
6 POP_JUMP_IF_FALSE (19)
|
||||
7 CACHE
|
||||
4 NOT_TAKEN
|
||||
5 LOAD_CONST (False)
|
||||
6 POP_JUMP_IF_FALSE (7)
|
||||
>> 7 CACHE
|
||||
8 NOT_TAKEN
|
||||
9 LOAD_CONST (False)
|
||||
10 TO_BOOL
|
||||
10 POP_JUMP_IF_FALSE (3)
|
||||
>> 11 CACHE
|
||||
12 CACHE
|
||||
13 CACHE
|
||||
14 POP_JUMP_IF_FALSE (11)
|
||||
15 CACHE
|
||||
16 NOT_TAKEN
|
||||
17 LOAD_CONST (False)
|
||||
18 TO_BOOL
|
||||
>> 19 CACHE
|
||||
20 CACHE
|
||||
21 CACHE
|
||||
22 POP_JUMP_IF_FALSE (3)
|
||||
23 CACHE
|
||||
24 NOT_TAKEN
|
||||
12 NOT_TAKEN
|
||||
|
||||
2 25 LOAD_CONST (None)
|
||||
26 RETURN_VALUE
|
||||
27 LOAD_CONST (None)
|
||||
28 RETURN_VALUE
|
||||
2 13 LOAD_CONST (None)
|
||||
14 RETURN_VALUE
|
||||
15 LOAD_CONST (None)
|
||||
16 RETURN_VALUE
|
||||
+15
-31
@@ -1,43 +1,27 @@
|
||||
---
|
||||
source: crates/codegen/src/compile.rs
|
||||
assertion_line: 9327
|
||||
assertion_line: 9468
|
||||
expression: "compile_exec(\"\\\nif (True and False) or (False and True):\n pass\n\")"
|
||||
---
|
||||
1 0 RESUME (0)
|
||||
1 LOAD_CONST (True)
|
||||
2 TO_BOOL
|
||||
2 POP_JUMP_IF_FALSE (5)
|
||||
>> 3 CACHE
|
||||
4 CACHE
|
||||
5 CACHE
|
||||
6 POP_JUMP_IF_FALSE (9)
|
||||
7 CACHE
|
||||
4 NOT_TAKEN
|
||||
>> 5 LOAD_CONST (False)
|
||||
6 POP_JUMP_IF_TRUE (9)
|
||||
>> 7 CACHE
|
||||
8 NOT_TAKEN
|
||||
>> 9 LOAD_CONST (False)
|
||||
10 TO_BOOL
|
||||
>> 11 CACHE
|
||||
12 CACHE
|
||||
13 CACHE
|
||||
14 POP_JUMP_IF_TRUE (17)
|
||||
10 POP_JUMP_IF_FALSE (7)
|
||||
11 CACHE
|
||||
12 NOT_TAKEN
|
||||
13 LOAD_CONST (True)
|
||||
14 POP_JUMP_IF_FALSE (3)
|
||||
15 CACHE
|
||||
16 NOT_TAKEN
|
||||
>> 17 LOAD_CONST (False)
|
||||
18 TO_BOOL
|
||||
19 CACHE
|
||||
20 CACHE
|
||||
21 CACHE
|
||||
22 POP_JUMP_IF_FALSE (11)
|
||||
23 CACHE
|
||||
24 NOT_TAKEN
|
||||
25 LOAD_CONST (True)
|
||||
26 TO_BOOL
|
||||
27 CACHE
|
||||
28 CACHE
|
||||
29 CACHE
|
||||
30 POP_JUMP_IF_FALSE (3)
|
||||
31 CACHE
|
||||
32 NOT_TAKEN
|
||||
|
||||
2 33 LOAD_CONST (None)
|
||||
34 RETURN_VALUE
|
||||
35 LOAD_CONST (None)
|
||||
36 RETURN_VALUE
|
||||
2 17 LOAD_CONST (None)
|
||||
18 RETURN_VALUE
|
||||
19 LOAD_CONST (None)
|
||||
20 RETURN_VALUE
|
||||
+11
-23
@@ -1,35 +1,23 @@
|
||||
---
|
||||
source: crates/codegen/src/compile.rs
|
||||
assertion_line: 9307
|
||||
assertion_line: 9448
|
||||
expression: "compile_exec(\"\\\nif True or False or False:\n pass\n\")"
|
||||
---
|
||||
1 0 RESUME (0)
|
||||
1 LOAD_CONST (True)
|
||||
2 TO_BOOL
|
||||
2 POP_JUMP_IF_TRUE (9)
|
||||
>> 3 CACHE
|
||||
4 CACHE
|
||||
5 CACHE
|
||||
6 POP_JUMP_IF_TRUE (17)
|
||||
4 NOT_TAKEN
|
||||
>> 5 LOAD_CONST (False)
|
||||
6 POP_JUMP_IF_TRUE (5)
|
||||
7 CACHE
|
||||
8 NOT_TAKEN
|
||||
>> 9 LOAD_CONST (False)
|
||||
10 TO_BOOL
|
||||
10 POP_JUMP_IF_FALSE (3)
|
||||
11 CACHE
|
||||
12 CACHE
|
||||
13 CACHE
|
||||
14 POP_JUMP_IF_TRUE (9)
|
||||
15 CACHE
|
||||
16 NOT_TAKEN
|
||||
>> 17 LOAD_CONST (False)
|
||||
18 TO_BOOL
|
||||
19 CACHE
|
||||
20 CACHE
|
||||
21 CACHE
|
||||
22 POP_JUMP_IF_FALSE (3)
|
||||
23 CACHE
|
||||
24 NOT_TAKEN
|
||||
12 NOT_TAKEN
|
||||
|
||||
2 25 LOAD_CONST (None)
|
||||
26 RETURN_VALUE
|
||||
27 LOAD_CONST (None)
|
||||
28 RETURN_VALUE
|
||||
2 13 LOAD_CONST (None)
|
||||
14 RETURN_VALUE
|
||||
15 LOAD_CONST (None)
|
||||
16 RETURN_VALUE
|
||||
Generated
+61
-62
@@ -1,6 +1,6 @@
|
||||
---
|
||||
source: crates/codegen/src/compile.rs
|
||||
assertion_line: 9362
|
||||
assertion_line: 9496
|
||||
expression: "compile_exec(\"\\\nasync def test():\n for stop_exc in (StopIteration('spam'), StopAsyncIteration('ham')):\n with self.subTest(type=type(stop_exc)):\n try:\n async with egg():\n raise stop_exc\n except Exception as ex:\n self.assertIs(ex, stop_exc)\n else:\n self.fail(f'{stop_exc} was suppressed')\n\")"
|
||||
---
|
||||
1 0 RESUME (0)
|
||||
@@ -39,8 +39,8 @@ expression: "compile_exec(\"\\\nasync def test():\n for stop_exc in (StopIter
|
||||
30 CACHE
|
||||
31 CACHE
|
||||
32 CACHE
|
||||
33 LOAD_ATTR (7, subTest, method=true)
|
||||
>> 34 CACHE
|
||||
>> 33 LOAD_ATTR (7, subTest, method=true)
|
||||
34 CACHE
|
||||
35 CACHE
|
||||
36 CACHE
|
||||
37 CACHE
|
||||
@@ -52,8 +52,8 @@ expression: "compile_exec(\"\\\nasync def test():\n for stop_exc in (StopIter
|
||||
43 LOAD_GLOBAL (9, NULL + type)
|
||||
44 CACHE
|
||||
45 CACHE
|
||||
46 CACHE
|
||||
>> 47 CACHE
|
||||
>> 46 CACHE
|
||||
47 CACHE
|
||||
48 LOAD_FAST (0, stop_exc)
|
||||
49 CALL (1)
|
||||
50 CACHE
|
||||
@@ -142,7 +142,7 @@ expression: "compile_exec(\"\\\nasync def test():\n for stop_exc in (StopIter
|
||||
128 COPY (3)
|
||||
129 POP_EXCEPT
|
||||
130 RERAISE (1)
|
||||
131 JUMP_FORWARD (47)
|
||||
131 JUMP_FORWARD (46)
|
||||
132 PUSH_EXC_INFO
|
||||
|
||||
7 133 LOAD_GLOBAL (12, Exception)
|
||||
@@ -151,7 +151,7 @@ expression: "compile_exec(\"\\\nasync def test():\n for stop_exc in (StopIter
|
||||
136 CACHE
|
||||
137 CACHE
|
||||
138 CHECK_EXC_MATCH
|
||||
139 POP_JUMP_IF_FALSE (34)
|
||||
139 POP_JUMP_IF_FALSE (33)
|
||||
140 CACHE
|
||||
141 NOT_TAKEN
|
||||
142 STORE_FAST (1, ex)
|
||||
@@ -171,34 +171,34 @@ expression: "compile_exec(\"\\\nasync def test():\n for stop_exc in (StopIter
|
||||
155 CACHE
|
||||
156 CACHE
|
||||
157 CACHE
|
||||
158 LOAD_FAST (1, ex)
|
||||
159 LOAD_FAST (0, stop_exc)
|
||||
160 CALL (2)
|
||||
158 LOAD_FAST_LOAD_FAST (ex, stop_exc)
|
||||
159 CALL (2)
|
||||
160 CACHE
|
||||
161 CACHE
|
||||
162 CACHE
|
||||
163 CACHE
|
||||
164 POP_TOP
|
||||
165 JUMP_FORWARD (4)
|
||||
166 LOAD_CONST (None)
|
||||
167 STORE_FAST (1, ex)
|
||||
168 DELETE_FAST (1, ex)
|
||||
169 RERAISE (1)
|
||||
170 POP_EXCEPT
|
||||
171 LOAD_CONST (None)
|
||||
172 STORE_FAST (1, ex)
|
||||
173 DELETE_FAST (1, ex)
|
||||
174 JUMP_FORWARD (28)
|
||||
175 RERAISE (0)
|
||||
176 COPY (3)
|
||||
177 POP_EXCEPT
|
||||
178 RERAISE (1)
|
||||
163 POP_TOP
|
||||
164 JUMP_FORWARD (4)
|
||||
165 LOAD_CONST (None)
|
||||
166 STORE_FAST (1, ex)
|
||||
167 DELETE_FAST (1, ex)
|
||||
168 RERAISE (1)
|
||||
169 POP_EXCEPT
|
||||
170 LOAD_CONST (None)
|
||||
171 STORE_FAST (1, ex)
|
||||
172 DELETE_FAST (1, ex)
|
||||
173 JUMP_FORWARD (28)
|
||||
174 RERAISE (0)
|
||||
175 COPY (3)
|
||||
176 POP_EXCEPT
|
||||
177 RERAISE (1)
|
||||
|
||||
10 179 LOAD_GLOBAL (4, self)
|
||||
10 178 LOAD_GLOBAL (4, self)
|
||||
179 CACHE
|
||||
180 CACHE
|
||||
181 CACHE
|
||||
182 CACHE
|
||||
183 CACHE
|
||||
184 LOAD_ATTR (17, fail, method=true)
|
||||
183 LOAD_ATTR (17, fail, method=true)
|
||||
184 CACHE
|
||||
185 CACHE
|
||||
186 CACHE
|
||||
187 CACHE
|
||||
@@ -207,48 +207,47 @@ expression: "compile_exec(\"\\\nasync def test():\n for stop_exc in (StopIter
|
||||
190 CACHE
|
||||
191 CACHE
|
||||
192 CACHE
|
||||
193 CACHE
|
||||
194 LOAD_FAST_BORROW (0, stop_exc)
|
||||
195 FORMAT_SIMPLE
|
||||
196 LOAD_CONST (" was suppressed")
|
||||
197 BUILD_STRING (2)
|
||||
198 CALL (1)
|
||||
193 LOAD_FAST_BORROW (0, stop_exc)
|
||||
194 FORMAT_SIMPLE
|
||||
195 LOAD_CONST (" was suppressed")
|
||||
196 BUILD_STRING (2)
|
||||
197 CALL (1)
|
||||
198 CACHE
|
||||
199 CACHE
|
||||
200 CACHE
|
||||
201 CACHE
|
||||
202 POP_TOP
|
||||
203 NOP
|
||||
201 POP_TOP
|
||||
202 NOP
|
||||
|
||||
3 204 PUSH_NULL
|
||||
3 203 PUSH_NULL
|
||||
204 LOAD_CONST (None)
|
||||
205 LOAD_CONST (None)
|
||||
206 LOAD_CONST (None)
|
||||
207 LOAD_CONST (None)
|
||||
208 CALL (3)
|
||||
>> 209 CACHE
|
||||
207 CALL (3)
|
||||
>> 208 CACHE
|
||||
209 CACHE
|
||||
210 CACHE
|
||||
211 CACHE
|
||||
212 POP_TOP
|
||||
213 JUMP_FORWARD (18)
|
||||
214 PUSH_EXC_INFO
|
||||
215 WITH_EXCEPT_START
|
||||
216 TO_BOOL
|
||||
211 POP_TOP
|
||||
212 JUMP_FORWARD (18)
|
||||
213 PUSH_EXC_INFO
|
||||
214 WITH_EXCEPT_START
|
||||
215 TO_BOOL
|
||||
216 CACHE
|
||||
217 CACHE
|
||||
218 CACHE
|
||||
219 CACHE
|
||||
220 POP_JUMP_IF_TRUE (2)
|
||||
221 CACHE
|
||||
222 NOT_TAKEN
|
||||
223 RERAISE (2)
|
||||
224 POP_TOP
|
||||
225 POP_EXCEPT
|
||||
219 POP_JUMP_IF_TRUE (2)
|
||||
220 CACHE
|
||||
221 NOT_TAKEN
|
||||
222 RERAISE (2)
|
||||
223 POP_TOP
|
||||
224 POP_EXCEPT
|
||||
225 POP_TOP
|
||||
226 POP_TOP
|
||||
227 POP_TOP
|
||||
228 JUMP_FORWARD (3)
|
||||
229 COPY (3)
|
||||
230 POP_EXCEPT
|
||||
231 RERAISE (1)
|
||||
232 JUMP_BACKWARD (209)
|
||||
233 CACHE
|
||||
227 JUMP_FORWARD (3)
|
||||
228 COPY (3)
|
||||
229 POP_EXCEPT
|
||||
230 RERAISE (1)
|
||||
231 JUMP_BACKWARD (208)
|
||||
232 CACHE
|
||||
|
||||
2 MAKE_FUNCTION
|
||||
3 STORE_NAME (0, test)
|
||||
|
||||
@@ -54,6 +54,9 @@ pub struct SymbolTable {
|
||||
/// Whether this type param scope can see the parent class scope
|
||||
pub can_see_class_scope: bool,
|
||||
|
||||
/// Whether this scope contains yield/yield from (is a generator function)
|
||||
pub is_generator: bool,
|
||||
|
||||
/// Whether this comprehension scope should be inlined (PEP 709)
|
||||
/// True for list/set/dict comprehensions in non-generator expressions
|
||||
pub comp_inlined: bool,
|
||||
@@ -89,6 +92,7 @@ impl SymbolTable {
|
||||
needs_class_closure: false,
|
||||
needs_classdict: false,
|
||||
can_see_class_scope: false,
|
||||
is_generator: false,
|
||||
comp_inlined: false,
|
||||
annotation_block: None,
|
||||
has_conditional_annotations: false,
|
||||
@@ -1823,6 +1827,7 @@ impl SymbolTableBuilder {
|
||||
node_index: _,
|
||||
range: _,
|
||||
}) => {
|
||||
self.tables.last_mut().unwrap().is_generator = true;
|
||||
if let Some(expression) = value {
|
||||
self.scan_expression(expression, context)?;
|
||||
}
|
||||
@@ -1832,6 +1837,7 @@ impl SymbolTableBuilder {
|
||||
node_index: _,
|
||||
range: _,
|
||||
}) => {
|
||||
self.tables.last_mut().unwrap().is_generator = true;
|
||||
self.scan_expression(value, context)?;
|
||||
}
|
||||
Expr::UnaryOp(ExprUnaryOp {
|
||||
|
||||
@@ -736,6 +736,14 @@ impl<'a, 'b> FunctionCompiler<'a, 'b> {
|
||||
let val = self.stack.pop().ok_or(JitCompileError::BadBytecode)?;
|
||||
self.store_variable(var_num.get(arg), val)
|
||||
}
|
||||
Instruction::StoreFastStoreFast { var_nums } => {
|
||||
let oparg = var_nums.get(arg);
|
||||
let (idx1, idx2) = oparg.indexes();
|
||||
let val1 = self.stack.pop().ok_or(JitCompileError::BadBytecode)?;
|
||||
self.store_variable(idx1, val1)?;
|
||||
let val2 = self.stack.pop().ok_or(JitCompileError::BadBytecode)?;
|
||||
self.store_variable(idx2, val2)
|
||||
}
|
||||
Instruction::Swap { i: index } => {
|
||||
let len = self.stack.len();
|
||||
let i = len - 1;
|
||||
|
||||
+18
-6
@@ -705,6 +705,18 @@ impl Frame {
|
||||
}
|
||||
}
|
||||
|
||||
// For generators/coroutines, initialize prev_line to the def line
|
||||
// so that preamble instructions (RETURN_GENERATOR, POP_TOP) don't
|
||||
// fire spurious LINE events.
|
||||
let prev_line = if code
|
||||
.flags
|
||||
.intersects(bytecode::CodeFlags::GENERATOR | bytecode::CodeFlags::COROUTINE)
|
||||
{
|
||||
code.first_line_number.map_or(0, |line| line.get() as u32)
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
let iframe = InterpreterFrame {
|
||||
localsplus,
|
||||
locals: match scope.locals {
|
||||
@@ -719,7 +731,7 @@ impl Frame {
|
||||
code,
|
||||
func_obj,
|
||||
lasti: Radium::new(0),
|
||||
prev_line: 0,
|
||||
prev_line,
|
||||
trace: PyMutex::new(vm.ctx.none()),
|
||||
trace_lines: PyMutex::new(true),
|
||||
trace_opcodes: PyMutex::new(false),
|
||||
@@ -2945,7 +2957,6 @@ impl ExecutingFrame<'_> {
|
||||
|
||||
let bound = match vm.get_special_method(&obj, method_name)? {
|
||||
Some(PyMethod::Function { target, func }) => {
|
||||
// Create bound method: PyBoundMethod(object=target, function=func)
|
||||
crate::builtins::PyBoundMethod::new(target, func)
|
||||
.into_ref(&vm.ctx)
|
||||
.into()
|
||||
@@ -3435,11 +3446,12 @@ impl ExecutingFrame<'_> {
|
||||
Instruction::StoreFastStoreFast { var_nums } => {
|
||||
let oparg = var_nums.get(arg);
|
||||
let (idx1, idx2) = oparg.indexes();
|
||||
let value1 = self.pop_value();
|
||||
let value2 = self.pop_value();
|
||||
// pop_value_opt: allows NULL from LoadFastAndClear restore path
|
||||
let value1 = self.pop_value_opt();
|
||||
let value2 = self.pop_value_opt();
|
||||
let fastlocals = self.localsplus.fastlocals_mut();
|
||||
fastlocals[idx1] = Some(value1);
|
||||
fastlocals[idx2] = Some(value2);
|
||||
fastlocals[idx1] = value1;
|
||||
fastlocals[idx2] = value2;
|
||||
Ok(None)
|
||||
}
|
||||
Instruction::StoreGlobal { namei: idx } => {
|
||||
|
||||
Reference in new issue
Block a user