Skip to content

Commit 92b8e59

Browse files
committed
Fast locals part 2
1 parent 97029af commit 92b8e59

File tree

14 files changed

+505
-213
lines changed

14 files changed

+505
-213
lines changed

bytecode/src/bytecode.rs

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ pub struct CodeObject<C: Constant = ConstantData> {
102102
pub source_path: String,
103103
pub first_line_number: usize,
104104
pub obj_name: String, // Name of the object that created this code object
105+
pub cell2arg: Option<Box<[isize]>>,
105106
pub constants: Vec<C>,
106107
#[serde(bound(
107108
deserialize = "C::Name: serde::Deserialize<'de>",
@@ -184,7 +185,7 @@ pub enum Instruction {
184185
idx: NameIdx,
185186
},
186187
LoadFast(NameIdx),
187-
LoadLocal(NameIdx),
188+
LoadNameAny(NameIdx),
188189
LoadGlobal(NameIdx),
189190
LoadDeref(NameIdx),
190191
LoadClassDeref(NameIdx),
@@ -550,6 +551,7 @@ impl<C: Constant> CodeObject<C> {
550551
source_path,
551552
first_line_number,
552553
obj_name,
554+
cell2arg: None,
553555
constants: Vec::new(),
554556
names: Vec::new(),
555557
varnames: Vec::new(),
@@ -611,7 +613,7 @@ impl<C: Constant> CodeObject<C> {
611613
}
612614

613615
#[rustfmt::skip]
614-
Import { .. } | ImportStar | ImportFrom { .. } | LoadFast(_) | LoadLocal(_)
616+
Import { .. } | ImportStar | ImportFrom { .. } | LoadFast(_) | LoadNameAny(_)
615617
| LoadGlobal(_) | LoadDeref(_) | LoadClassDeref(_) | StoreFast(_) | StoreLocal(_)
616618
| StoreGlobal(_) | StoreDeref(_) | DeleteFast(_) | DeleteLocal(_) | DeleteGlobal(_)
617619
| DeleteDeref(_) | LoadClosure(_) | Subscript | StoreSubscript | DeleteSubscript
@@ -699,6 +701,7 @@ impl<C: Constant> CodeObject<C> {
699701
source_path: self.source_path,
700702
first_line_number: self.first_line_number,
701703
obj_name: self.obj_name,
704+
cell2arg: self.cell2arg,
702705
}
703706
}
704707

@@ -729,6 +732,7 @@ impl<C: Constant> CodeObject<C> {
729732
source_path: self.source_path.clone(),
730733
first_line_number: self.first_line_number,
731734
obj_name: self.obj_name.clone(),
735+
cell2arg: self.cell2arg.clone(),
732736
}
733737
}
734738
}
@@ -798,7 +802,12 @@ impl Instruction {
798802
};
799803
}
800804

801-
let cellname = |i: usize| cellvars.get(i).unwrap_or_else(|| &freevars[i]).as_ref();
805+
let cellname = |i: usize| {
806+
cellvars
807+
.get(i)
808+
.unwrap_or_else(|| &freevars[i - cellvars.len()])
809+
.as_ref()
810+
};
802811

803812
match self {
804813
Import {
@@ -819,20 +828,20 @@ impl Instruction {
819828
),
820829
ImportStar => w!(ImportStar),
821830
ImportFrom { idx } => w!(ImportFrom, names[*idx].as_ref()),
822-
LoadFast(idx) => w!(LoadFast, varnames[*idx].as_ref()),
823-
LoadLocal(idx) => w!(LoadLocal, names[*idx].as_ref()),
824-
LoadGlobal(idx) => w!(LoadGlobal, names[*idx].as_ref()),
825-
LoadDeref(idx) => w!(LoadDeref, cellname(*idx)),
826-
LoadClassDeref(idx) => w!(LoadClassDeref, cellname(*idx)),
827-
StoreFast(idx) => w!(StoreFast, varnames[*idx].as_ref()),
828-
StoreLocal(idx) => w!(StoreLocal, names[*idx].as_ref()),
829-
StoreGlobal(idx) => w!(StoreGlobal, names[*idx].as_ref()),
830-
StoreDeref(idx) => w!(StoreDeref, cellname(*idx)),
831-
DeleteFast(idx) => w!(DeleteFast, varnames[*idx].as_ref()),
832-
DeleteLocal(idx) => w!(DeleteLocal, names[*idx].as_ref()),
833-
DeleteGlobal(idx) => w!(DeleteGlobal, names[*idx].as_ref()),
834-
DeleteDeref(idx) => w!(DeleteDeref, cellname(*idx)),
835-
LoadClosure(i) => w!(LoadClosure, cellname(*i)),
831+
LoadFast(idx) => w!(LoadFast, *idx, varnames[*idx].as_ref()),
832+
LoadNameAny(idx) => w!(LoadNameAny, *idx, names[*idx].as_ref()),
833+
LoadGlobal(idx) => w!(LoadGlobal, *idx, names[*idx].as_ref()),
834+
LoadDeref(idx) => w!(LoadDeref, *idx, cellname(*idx)),
835+
LoadClassDeref(idx) => w!(LoadClassDeref, *idx, cellname(*idx)),
836+
StoreFast(idx) => w!(StoreFast, *idx, varnames[*idx].as_ref()),
837+
StoreLocal(idx) => w!(StoreLocal, *idx, names[*idx].as_ref()),
838+
StoreGlobal(idx) => w!(StoreGlobal, *idx, names[*idx].as_ref()),
839+
StoreDeref(idx) => w!(StoreDeref, *idx, cellname(*idx)),
840+
DeleteFast(idx) => w!(DeleteFast, *idx, varnames[*idx].as_ref()),
841+
DeleteLocal(idx) => w!(DeleteLocal, *idx, names[*idx].as_ref()),
842+
DeleteGlobal(idx) => w!(DeleteGlobal, *idx, names[*idx].as_ref()),
843+
DeleteDeref(idx) => w!(DeleteDeref, *idx, cellname(*idx)),
844+
LoadClosure(i) => w!(LoadClosure, *i, cellname(*i)),
836845
Subscript => w!(Subscript),
837846
StoreSubscript => w!(StoreSubscript),
838847
DeleteSubscript => w!(DeleteSubscript),

compiler/src/compile.rs

Lines changed: 107 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,31 @@ impl CodeInfo {
4040
code.cellvars.extend(cellvar_cache);
4141
code.freevars.extend(freevar_cache);
4242

43+
if !code.cellvars.is_empty() {
44+
let total_args = code.arg_count
45+
+ code.kwonlyarg_count
46+
+ code.flags.contains(bytecode::CodeFlags::HAS_VARARGS) as usize
47+
+ code.flags.contains(bytecode::CodeFlags::HAS_VARKEYWORDS) as usize;
48+
let all_args = &code.varnames[..total_args];
49+
let mut found_cellarg = false;
50+
let cell2arg = code
51+
.cellvars
52+
.iter()
53+
.map(|var| {
54+
for (i, arg) in all_args.iter().enumerate() {
55+
if var == arg {
56+
found_cellarg = true;
57+
return i as isize;
58+
}
59+
}
60+
-1
61+
})
62+
.collect::<Box<[_]>>();
63+
if found_cellarg {
64+
code.cell2arg = Some(cell2arg);
65+
}
66+
}
67+
4368
for instruction in &mut code.instructions {
4469
use Instruction::*;
4570
// this is a little bit hacky, as until now the data stored inside Labels in
@@ -64,7 +89,7 @@ impl CodeInfo {
6489
}
6590

6691
#[rustfmt::skip]
67-
Import { .. } | ImportStar | ImportFrom { .. } | LoadFast(_) | LoadLocal(_)
92+
Import { .. } | ImportStar | ImportFrom { .. } | LoadFast(_) | LoadNameAny(_)
6893
| LoadGlobal(_) | LoadDeref(_) | LoadClassDeref(_) | StoreFast(_) | StoreLocal(_)
6994
| StoreGlobal(_) | StoreDeref(_) | DeleteFast(_) | DeleteLocal(_) | DeleteGlobal(_)
7095
| DeleteDeref(_) | LoadClosure(_) | Subscript | StoreSubscript | DeleteSubscript
@@ -114,14 +139,14 @@ impl Default for CompileOpts {
114139
}
115140
}
116141

117-
#[derive(Clone, Copy)]
142+
#[derive(Debug, Clone, Copy)]
118143
struct CompileContext {
119144
in_loop: bool,
120145
in_class: bool,
121146
func: FunctionContext,
122147
}
123148

124-
#[derive(Clone, Copy, PartialEq)]
149+
#[derive(Debug, Clone, Copy, PartialEq)]
125150
enum FunctionContext {
126151
NoFunction,
127152
Function,
@@ -130,7 +155,7 @@ enum FunctionContext {
130155

131156
impl CompileContext {
132157
fn in_func(self) -> bool {
133-
!matches!(self.func, FunctionContext::NoFunction)
158+
self.func != FunctionContext::NoFunction
134159
}
135160
}
136161

@@ -408,9 +433,8 @@ impl Compiler {
408433
cache = &mut info.varname_cache;
409434
NameOpType::Fast
410435
}
411-
SymbolScope::Local => NameOpType::Local,
412436
SymbolScope::GlobalImplicit if self.ctx.in_func() => NameOpType::Global,
413-
SymbolScope::GlobalImplicit => NameOpType::Local,
437+
SymbolScope::Local | SymbolScope::GlobalImplicit => NameOpType::Local,
414438
SymbolScope::GlobalExplicit => NameOpType::Global,
415439
SymbolScope::Free => {
416440
cache = &mut info.freevar_cache;
@@ -423,9 +447,12 @@ impl Compiler {
423447
// TODO: is this right?
424448
SymbolScope::Unknown => NameOpType::Global,
425449
};
426-
let idx = cache
450+
let mut idx = cache
427451
.get_index_of(name)
428452
.unwrap_or_else(|| cache.insert_full(name.to_owned()).0);
453+
if let SymbolScope::Free = symbol.scope {
454+
idx += info.cellvar_cache.len();
455+
}
429456
let op = match op_typ {
430457
NameOpType::Fast => match usage {
431458
NameUsage::Load => Instruction::LoadFast,
@@ -438,12 +465,15 @@ impl Compiler {
438465
NameUsage::Delete => Instruction::DeleteGlobal,
439466
},
440467
NameOpType::Deref => match usage {
468+
NameUsage::Load if !self.ctx.in_func() && self.ctx.in_class => {
469+
Instruction::LoadClassDeref
470+
}
441471
NameUsage::Load => Instruction::LoadDeref,
442472
NameUsage::Store => Instruction::StoreDeref,
443473
NameUsage::Delete => Instruction::DeleteDeref,
444474
},
445475
NameOpType::Local => match usage {
446-
NameUsage::Load => Instruction::LoadLocal,
476+
NameUsage::Load => Instruction::LoadNameAny,
447477
NameUsage::Store => Instruction::StoreLocal,
448478
NameUsage::Delete => Instruction::DeleteLocal,
449479
},
@@ -844,17 +874,17 @@ impl Compiler {
844874
));
845875

846876
for name in &args.args {
847-
self.name(&name.arg);
877+
self.varname(&name.arg);
848878
}
849879
for name in &args.kwonlyargs {
850-
self.name(&name.arg);
880+
self.varname(&name.arg);
851881
}
852882

853883
let mut compile_varargs = |va: &ast::Varargs, flag| match va {
854884
ast::Varargs::None | ast::Varargs::Unnamed => {}
855885
ast::Varargs::Named(name) => {
856886
self.current_code().flags |= flag;
857-
self.name(&name.arg);
887+
self.varname(&name.arg);
858888
}
859889
};
860890

@@ -1002,6 +1032,10 @@ impl Compiler {
10021032
is_async: bool,
10031033
) -> CompileResult<()> {
10041034
// Create bytecode for this function:
1035+
1036+
self.prepare_decorators(decorator_list)?;
1037+
self.enter_function(name, args)?;
1038+
10051039
// remember to restore self.ctx.in_loop to the original after the function is compiled
10061040
let prev_ctx = self.ctx;
10071041

@@ -1019,10 +1053,6 @@ impl Compiler {
10191053
let old_qualified_path = self.current_qualified_path.take();
10201054
self.current_qualified_path = Some(self.create_qualified_name(name, ".<locals>"));
10211055

1022-
self.prepare_decorators(decorator_list)?;
1023-
1024-
self.enter_function(name, args)?;
1025-
10261056
let (body, doc_str) = get_doc(body);
10271057

10281058
self.compile_statements(body)?;
@@ -1090,26 +1120,7 @@ impl Compiler {
10901120
code.flags |= bytecode::CodeFlags::IS_COROUTINE;
10911121
}
10921122

1093-
if !code.freevars.is_empty() {
1094-
for var in &code.freevars {
1095-
let symbol = self.symbol_table_stack.last().unwrap().lookup(var).unwrap();
1096-
let parent_code = self.code_stack.last().unwrap();
1097-
let vars = match symbol.scope {
1098-
SymbolScope::Free => &parent_code.freevar_cache,
1099-
SymbolScope::Cell => &parent_code.cellvar_cache,
1100-
_ => unreachable!(),
1101-
};
1102-
let mut idx = vars.get_index_of(var).unwrap();
1103-
if let SymbolScope::Free = symbol.scope {
1104-
idx += parent_code.cellvar_cache.len();
1105-
}
1106-
self.emit(Instruction::LoadClosure(idx))
1107-
}
1108-
self.emit(Instruction::BuildTuple {
1109-
size: code.freevars.len(),
1110-
unpack: false,
1111-
})
1112-
}
1123+
self.build_closure(&code);
11131124

11141125
self.emit_constant(bytecode::ConstantData::Code {
11151126
code: Box::new(code),
@@ -1126,15 +1137,40 @@ impl Compiler {
11261137
self.emit(Instruction::Rotate { amount: 2 });
11271138
let doc = self.name("__doc__");
11281139
self.emit(Instruction::StoreAttr { idx: doc });
1140+
1141+
self.current_qualified_path = old_qualified_path;
1142+
self.ctx = prev_ctx;
1143+
11291144
self.apply_decorators(decorator_list);
11301145

11311146
self.store_name(name);
11321147

1133-
self.current_qualified_path = old_qualified_path;
1134-
self.ctx = prev_ctx;
11351148
Ok(())
11361149
}
11371150

1151+
fn build_closure(&mut self, code: &CodeObject) {
1152+
if !code.freevars.is_empty() {
1153+
for var in &code.freevars {
1154+
let symbol = self.symbol_table_stack.last().unwrap().lookup(var).unwrap();
1155+
let parent_code = self.code_stack.last().unwrap();
1156+
let vars = match symbol.scope {
1157+
SymbolScope::Free => &parent_code.freevar_cache,
1158+
SymbolScope::Cell => &parent_code.cellvar_cache,
1159+
_ => unreachable!(),
1160+
};
1161+
let mut idx = vars.get_index_of(var).unwrap();
1162+
if let SymbolScope::Free = symbol.scope {
1163+
idx += parent_code.cellvar_cache.len();
1164+
}
1165+
self.emit(Instruction::LoadClosure(idx))
1166+
}
1167+
self.emit(Instruction::BuildTuple {
1168+
size: code.freevars.len(),
1169+
unpack: false,
1170+
})
1171+
}
1172+
}
1173+
11381174
fn find_ann(&self, body: &[ast::Statement]) -> bool {
11391175
use ast::StatementType::*;
11401176
let option_stmt_to_bool = |suit: &Option<ast::Suite>| -> bool {
@@ -1243,11 +1279,30 @@ impl Compiler {
12431279
self.emit(Instruction::SetupAnnotation);
12441280
}
12451281
self.compile_statements(new_body)?;
1246-
self.emit_constant(bytecode::ConstantData::None);
1282+
1283+
let classcell_idx = self
1284+
.code_stack
1285+
.last_mut()
1286+
.unwrap()
1287+
.cellvar_cache
1288+
.iter()
1289+
.position(|var| *var == "__class__");
1290+
1291+
if let Some(classcell_idx) = classcell_idx {
1292+
self.emit(Instruction::LoadClosure(classcell_idx));
1293+
self.emit(Instruction::Duplicate);
1294+
let classcell = self.name("__classcell__");
1295+
self.emit(Instruction::StoreLocal(classcell));
1296+
} else {
1297+
self.emit_constant(bytecode::ConstantData::None);
1298+
}
1299+
12471300
self.emit(Instruction::ReturnValue);
12481301

12491302
let code = self.pop_code_object();
12501303

1304+
self.build_closure(&code);
1305+
12511306
self.emit_constant(bytecode::ConstantData::Code {
12521307
code: Box::new(code),
12531308
});
@@ -1526,7 +1581,7 @@ impl Compiler {
15261581
// Store as dict entry in __annotations__ dict:
15271582
if !self.ctx.in_func() {
15281583
let annotations = self.name("__annotations__");
1529-
self.emit(Instruction::LoadLocal(annotations));
1584+
self.emit(Instruction::LoadNameAny(annotations));
15301585
self.emit_constant(bytecode::ConstantData::Str {
15311586
value: name.to_owned(),
15321587
});
@@ -1942,6 +1997,7 @@ impl Compiler {
19421997
self.compile_expression(body)?;
19431998
self.emit(Instruction::ReturnValue);
19441999
let code = self.pop_code_object();
2000+
self.build_closure(&code);
19452001
self.emit_constant(bytecode::ConstantData::Code {
19462002
code: Box::new(code),
19472003
});
@@ -2109,6 +2165,14 @@ impl Compiler {
21092165
kind: &ast::ComprehensionKind,
21102166
generators: &[ast::Comprehension],
21112167
) -> CompileResult<()> {
2168+
let prev_ctx = self.ctx;
2169+
2170+
self.ctx = CompileContext {
2171+
in_loop: false,
2172+
in_class: prev_ctx.in_class,
2173+
func: FunctionContext::Function,
2174+
};
2175+
21122176
// We must have at least one generator:
21132177
assert!(!generators.is_empty());
21142178

@@ -2252,6 +2316,10 @@ impl Compiler {
22522316
// Fetch code for listcomp function:
22532317
let code = self.pop_code_object();
22542318

2319+
self.ctx = prev_ctx;
2320+
2321+
self.build_closure(&code);
2322+
22552323
// List comprehension code:
22562324
self.emit_constant(bytecode::ConstantData::Code {
22572325
code: Box::new(code),

0 commit comments

Comments
 (0)