Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
173 commits
Select commit Hold shift + click to select a range
215741b
fix: decode reordered comprehension restores in loaded bytecode
owenthcarey Oct 9, 2026
06f4e6d
perf: allocate bare instances from the cached plan
owenthcarey Oct 9, 2026
712b1f8
perf: call functions that take defaults directly
owenthcarey Oct 10, 2026
0049ed5
perf: construct store-only __init__ classes by their stores
owenthcarey Oct 10, 2026
1720135
perf: read globals in line in lean compiled bodies
owenthcarey Oct 10, 2026
74354b5
perf: cheaper raises, deletes and with statements
owenthcarey Oct 10, 2026
4ef65e1
perf: run except-clause cleanups and deletes in the core loop
owenthcarey Oct 10, 2026
616e3eb
perf: load instance methods in line in compiled code
owenthcarey Oct 10, 2026
648a08b
perf: remember where a method name sits among a class's names
owenthcarey Oct 10, 2026
e0465de
perf: compile coroutine bodies and step closure genexprs natively
owenthcarey Oct 10, 2026
2fb29e7
perf: send into generators without a bound method
owenthcarey Oct 10, 2026
b4f447f
perf: resume compiled generators straight from native consumers
owenthcarey Oct 10, 2026
4e617f9
perf: recycle generator frame boxes; warm only delegating bodies
owenthcarey Oct 10, 2026
3c3fbc7
perf: run container ops and builtin methods in place in the frame JIT
owenthcarey Oct 10, 2026
f8407c9
perf: take a local container's other subscripts in place too
owenthcarey Oct 10, 2026
1f49693
perf: run settled leaf bodies from the leaf-site call helper
owenthcarey Oct 10, 2026
bdde30d
perf: call effect leaves that call directly
owenthcarey Oct 10, 2026
1e3849b
perf: bind a direct call's locals in one pass
owenthcarey Oct 10, 2026
6b76cbd
perf: keep two callees per direct-call site
owenthcarey Oct 10, 2026
0eb82d2
perf: keep a method site's other receiver classes
owenthcarey Oct 10, 2026
29231dd
perf: move a call's few arguments in straight-line code
owenthcarey Oct 10, 2026
30ab7a3
perf: native posixpath accelerators, cheaper struct_time and lazy frames
owenthcarey Oct 10, 2026
8888401
perf: render plain %-formats in one pass
owenthcarey Oct 10, 2026
b570eed
fix: report a tier-2 caller's call line to introspection
owenthcarey Oct 10, 2026
b03ed74
perf: look up asyncio loop attributes by remembered position
owenthcarey Oct 10, 2026
91a9cd3
perf: clear the globals-__missing__ gate once no owner is left
owenthcarey Oct 10, 2026
329461d
perf: read asyncio loop attributes and future handles without guards
owenthcarey Oct 10, 2026
0f09882
perf: build iter(obj) generators in place and memo chain resends
owenthcarey Oct 10, 2026
dac3140
perf: keep the cycle collector's registry in a slab
owenthcarey Oct 10, 2026
ff44d8a
perf: mark reachability along recorded edges, not a second walk
owenthcarey Oct 10, 2026
e62332c
perf: cheaper container deferral and sweeps
owenthcarey Oct 10, 2026
5051ac4
perf: replace IndexMap with a compact dict table
owenthcarey Oct 10, 2026
fcdb5f6
perf: settle str and int dict probes without scopes or rehashing
owenthcarey Oct 10, 2026
e444500
perf: set OrderedDict items with one probe per table
owenthcarey Oct 10, 2026
467c304
perf: test an in-line release's gates with one load
owenthcarey Oct 10, 2026
0afc0d6
perf: finish more calls directly and refill the GIL countdown in place
owenthcarey Oct 10, 2026
42bff23
perf: compile compiled code's callees sooner and finish constructors …
owenthcarey Oct 10, 2026
a0b4dbe
perf: run CALL_FUNCTION_EX from compiled code through the call lanes
owenthcarey Oct 10, 2026
e58c3c3
perf: keep method calls of calling effect leaves off the in-place helper
owenthcarey Oct 10, 2026
6e9534a
perf: read a live frame's line from its shell instead of syncing it
owenthcarey Oct 10, 2026
b724fba
perf: read frame fields in line and flush only the frames asked for
owenthcarey Oct 10, 2026
77818a4
perf: recycle frame shells whose frame object died, keep code names
owenthcarey Oct 10, 2026
078cce6
perf: let warnings.warn read lean frames and skip unused instances
owenthcarey Oct 10, 2026
44d5972
perf: build co_positions once per code object
owenthcarey Oct 10, 2026
4fff0ea
perf: box the tier-2 cache's per-code entries
owenthcarey Oct 10, 2026
e30c062
perf: read cached directory listings in the stdlib shadow scan
owenthcarey Oct 10, 2026
fec8749
perf: give the per-site cache tables an entry per site
owenthcarey Oct 10, 2026
8c0f34e
perf: build the tier-2 engine at the first compile, not at start-up
owenthcarey Oct 10, 2026
fa9e3f8
style: rustfmt the per-site table and shadow-scan changes
owenthcarey Oct 10, 2026
d16e5fb
perf: run a with block's exception exit as an inline call
owenthcarey Oct 10, 2026
b889414
perf: cheaper generator creation, member loads and native resumes
owenthcarey Oct 10, 2026
5524d4b
perf: share the split-dict tables that attribute deletions make
owenthcarey Oct 10, 2026
89d4d36
perf: fold sum over generator iterables and resume next(gen, default)…
owenthcarey Oct 10, 2026
880d5c7
perf: raise next(gen)'s StopIteration from the core loop
owenthcarey Oct 10, 2026
740b199
perf: keep ExitStack's per-use steps out of the slow path
owenthcarey Oct 10, 2026
f9dcae1
perf: await native asyncio futures in the core loop
owenthcarey Oct 10, 2026
bc7cab4
perf: make a compiled caller's generators without the call prologue
owenthcarey Oct 10, 2026
23155dd
perf: cheaper float ratios, type(x), codec error args, isinstance misses
owenthcarey Oct 10, 2026
e2c184f
perf: constant-time type cache names and memoized class verdicts
owenthcarey Oct 10, 2026
7465181
perf: leaf builtin dunders, positional startswith, shared chars, gcd
owenthcarey Oct 10, 2026
deee936
perf: serve struct.Struct.pack and unpack natively
owenthcarey Oct 10, 2026
cfaff96
perf: one-character regex asserts in place, ASCII translate memo
owenthcarey Oct 10, 2026
7be5635
perf: fixed-point float formatting in integer arithmetic
owenthcarey Oct 10, 2026
581bb09
perf: module_attr off sys.modules, JIT empty dicts and temp tuple loops
owenthcarey Oct 10, 2026
e733273
perf: keep the regex matcher's AT test in line
owenthcarey Oct 10, 2026
785d1aa
perf: keep os.PathLike's structural check on isinstance misses
owenthcarey Oct 10, 2026
5ca81fd
perf: pool the identifiers in code objects' name tables
owenthcarey Oct 10, 2026
285c905
perf: start each class's leaf attribute cache at eight entries
owenthcarey Oct 10, 2026
6d834ff
perf: keep tier 2's analysis and code generator out of imports
owenthcarey Oct 10, 2026
2734d9e
perf: fit a code object in mimalloc's 384-byte size class
owenthcarey Oct 10, 2026
b9342cc
perf: hash pooled names eight bytes at a time; leave qualnames out
owenthcarey Oct 10, 2026
96f6110
perf: keep the code cache's position tables in one section
owenthcarey Oct 10, 2026
9abf021
perf: map large table sections of cached modules instead of reading them
owenthcarey Oct 10, 2026
fd76b9a
perf: share the frame compiler's exit tails per status
owenthcarey Oct 10, 2026
da46e7c
perf: remap the stale linker order file onto current symbols
owenthcarey Oct 10, 2026
83f65b9
perf: trim the leaf attribute cache's lookup and tier 2's phase reads
owenthcarey Oct 10, 2026
665d035
perf: copy a Name's text without the formatting machinery
owenthcarey Oct 10, 2026
543014e
perf: read and write __slots__ members by their layout position
owenthcarey Oct 10, 2026
21788bf
perf: find a lock's context-manager methods by their dict position
owenthcarey Oct 10, 2026
342831c
perf: trim the builtin call lane and probe hasattr without binding
owenthcarey Oct 10, 2026
2f9321d
perf: read a lock's method entry without a dict borrow
owenthcarey Oct 10, 2026
a019437
perf: pool instances a compiled loop discards
owenthcarey Oct 10, 2026
0b7bd04
perf: build a materialized instance dict in one pass
owenthcarey Oct 10, 2026
0132b92
perf: run wider store-only __init__ bodies by their stores
owenthcarey Oct 10, 2026
ac9d066
perf: store a split attribute from one view of the values
owenthcarey Oct 10, 2026
f464e42
perf: count laid-out slot reads in the pure-leaf coverage test
owenthcarey Oct 10, 2026
802abcf
perf: lay a store-only constructor's values down in one pass
owenthcarey Oct 10, 2026
e72928c
perf: drop discarded instances plainly in compiled pops again
owenthcarey Oct 10, 2026
79c0deb
perf: lay out exception slots from birth and slim handled-exception r…
owenthcarey Oct 10, 2026
2b4c24f
perf: trim generator creation, finish and exception slot stores
owenthcarey Oct 10, 2026
ce1fe92
perf: start an awaited coroutine as its call makes it
owenthcarey Oct 10, 2026
383023c
perf: lay out the slots of exceptions with a Python __init__
owenthcarey Oct 10, 2026
75982fb
perf: raise a cooling attribute miss without the resolution passes
owenthcarey Oct 10, 2026
649d7f6
perf: enter an awaited coroutine without re-checking its fresh state
owenthcarey Oct 10, 2026
24c28a5
perf: trim the direct call's checks and its return's releases
owenthcarey Oct 10, 2026
54e9940
perf: let a direct call's site cache a frameless leaf callee too
owenthcarey Oct 10, 2026
e415181
perf: finalize a call's released arguments before the caller goes on
owenthcarey Oct 10, 2026
9ac6b26
perf: release an in-place call's operands as one run
owenthcarey Oct 10, 2026
f0cfe0e
perf: keep a direct call's activation slot at its site between calls
owenthcarey Oct 10, 2026
1bd2e4f
perf: run a compiled callee of CALL_KW directly
owenthcarey Oct 10, 2026
457c284
perf: share one frameless leaf call between the call paths
owenthcarey Oct 10, 2026
d1b8105
perf: keep two frameless leaf callees per direct call site
owenthcarey Oct 10, 2026
d0f31c2
perf: prove a slotted constructor's member positions once per class v…
owenthcarey Oct 11, 2026
e7434f3
perf: write the stdlib code cache under -B too
owenthcarey Oct 11, 2026
c31d728
perf: sets on the compact dict table
owenthcarey Oct 10, 2026
8ac16fd
perf: step builtin adapters natively in every FOR_ITER
owenthcarey Oct 10, 2026
bac2d01
perf: probe str and int membership of sets and dicts in line
owenthcarey Oct 10, 2026
7486530
perf: bind lane keyword calls by the call site's name objects
owenthcarey Oct 10, 2026
a67eacd
perf: cheaper pure adapter steps over sequence cursors
owenthcarey Oct 10, 2026
263012b
perf: class membership probes in line
owenthcarey Oct 10, 2026
1c52c64
perf: dict iterators and unpacked pairs in the frame JIT's FOR_ITER
owenthcarey Oct 10, 2026
58a0a9c
perf: retire shared exhausted iterators in the frame JIT's FOR_ITER
owenthcarey Oct 11, 2026
0f77a88
perf: step itertools sources over sequence cursors in one view
owenthcarey Oct 11, 2026
f206c1e
perf: rustfmt the adapter, set and keyword changes
owenthcarey Oct 11, 2026
d3b5922
perf: settle an lru_cache call on native keys under one cache hold
owenthcarey Oct 11, 2026
73826e3
perf: keep decorator wrappers in compiled code
owenthcarey Oct 11, 2026
eecaba2
perf: build a function with its defaults and closure at once
owenthcarey Oct 11, 2026
6c37c3a
perf: read and write function attributes with the name's stored hash
owenthcarey Oct 11, 2026
70a28ab
perf: call a compiled site's settled builtin through its own lean helper
owenthcarey Oct 11, 2026
6115896
perf: give kernel and leaf builtin sites their own small helpers
owenthcarey Oct 11, 2026
165af40
perf: load exact builtin receivers' methods in line and settle bound …
owenthcarey Oct 11, 2026
4988dba
perf: give more leaf container methods kernels
owenthcarey Oct 11, 2026
545438d
perf: call a compiled site's settled builtin type through its own helper
owenthcarey Oct 11, 2026
f7821e1
perf: check and unwrap a builtin subclass receiver without allocating
owenthcarey Oct 11, 2026
9521437
perf: hand core-loop instructions over without leaving the loop
owenthcarey Oct 11, 2026
a18e8da
perf: start bursts in the right core-loop copy and run scalar unary o…
owenthcarey Oct 11, 2026
fa91666
perf: read a LOAD_GLOBAL site's value in place and fuse LOAD_FAST by …
owenthcarey Oct 11, 2026
2b55177
perf: branch on a numeric compare without pushing its bool
owenthcarey Oct 11, 2026
639efff
perf: keep a collapsed yield-from chain collapsed across yields
owenthcarey Oct 11, 2026
469ac4c
perf: step a simple generator in place for next() and native consumers
owenthcarey Oct 11, 2026
0f30b6e
perf: collapse a yield-from chain from FOR_ITER and next() too
owenthcarey Oct 11, 2026
4291703
perf: step a simple delegate in place for yield from and await
owenthcarey Oct 11, 2026
54c9a3d
perf: park a fast-stepped generator without re-checking its state
owenthcarey Oct 11, 2026
ee5121b
perf: keep native fast steps to next()-like consumers
owenthcarey Oct 11, 2026
05fa45f
perf: skip doomed first fast steps and finish lean fast runs in place
owenthcarey Oct 11, 2026
e78f5e2
perf: look before collapsing a chain from FOR_ITER and next()
owenthcarey Oct 11, 2026
5cb1fc3
perf: serve module attributes, __dict__, and super() arms from site c…
owenthcarey Oct 11, 2026
712a6fe
perf: raise unset-slot and other native AttributeErrors cheaply
owenthcarey Oct 11, 2026
a6bb2f1
perf: store class attributes and read property sites without the lanes
owenthcarey Oct 11, 2026
87067d6
perf: cheaper class lookups for names, stores and comparison dunders
owenthcarey Oct 11, 2026
71036ee
perf: read inst.__class__ and local functions' attributes without the…
owenthcarey Oct 19, 2026
1f29e78
perf: settle hasattr on builtin classes per class version
owenthcarey Oct 11, 2026
9cf936d
perf: raise at a failing attribute site without trying every lane
owenthcarey Oct 11, 2026
0bbee25
perf: skip the property lanes at failing attribute sites in the core …
owenthcarey Oct 11, 2026
7840802
perf: read a ChainMap's maps without resolving the name each lookup
owenthcarey Oct 11, 2026
8a298eb
perf: remember a stored callable's slot at its method-form load site
owenthcarey Oct 11, 2026
5632434
fix: keep keyword permutations off slot 15
owenthcarey Oct 19, 2026
0610b53
perf: release JIT compile contexts and pack frame exit constants
owenthcarey Oct 11, 2026
69b332b
perf: trim lean frame bodies' IR for compile memory
owenthcarey Oct 11, 2026
3c7605c
perf: hash the compiler's name sets with FxHash instead of SipHash
owenthcarey Oct 11, 2026
bb39959
perf: skip the String allocation when a scope scan sees a known name
owenthcarey Oct 11, 2026
b168c2e
perf: specialize keyword call sites from the core loop
owenthcarey Oct 11, 2026
f5305dc
perf: run a metaclass's Python __call__ as the call's inline method
owenthcarey Oct 11, 2026
c872c6d
perf: never specialize an attribute load on a super proxy by its class
owenthcarey Oct 11, 2026
6aede41
perf: resolve super() dunders and metaclass receivers in the core loop
owenthcarey Oct 11, 2026
91b90bb
perf: remember attribute misses on values of built-in types
owenthcarey Oct 11, 2026
07179c6
perf: copy a SharedStr straight into a String in to_string
owenthcarey Oct 11, 2026
5f8438c
perf: serve a class __dict__ view's methods through the builtin metho…
owenthcarey Oct 11, 2026
a3fd2c2
perf: run a Python __setitem__ as an inline activation from STORE_SUBSCR
owenthcarey Oct 11, 2026
0675d61
perf: keep inspect's class probes in the core loop
owenthcarey Oct 11, 2026
b2c1903
perf: settle `in` over IntEnum members and functions without the slow…
owenthcarey Oct 11, 2026
ca9f552
perf: read a class __dict__ view's present keys in the core loop
owenthcarey Oct 11, 2026
ba76510
fix: compile the posixpath accelerators only where they're registered
owenthcarey Oct 19, 2026
1131f92
style: satisfy clippy and rustfmt
owenthcarey Oct 19, 2026
59674d9
perf: run calling effect leaves in place from the core loop
owenthcarey Oct 19, 2026
27ad3f2
perf: send only recursive effect leaves to direct calls
owenthcarey Oct 19, 2026
ba20d32
perf: drop purged pages from the resident size on macOS
owenthcarey Oct 19, 2026
a1e8c13
fix: hold the GIL wherever objects are built or released
owenthcarey Oct 19, 2026
dedcad0
fix: isolate the tests that change the recursion limit
owenthcarey Oct 19, 2026
b136b1b
perf: warm the code generator in the background as start-up begins
owenthcarey Oct 20, 2026
e8d5065
test: deselect Pillow's AVIF peak-RSS leak test on macOS only
owenthcarey Oct 20, 2026
dc20d42
Revert "perf: drop purged pages from the resident size on macOS"
owenthcarey Oct 20, 2026
2b174f2
perf: skip the empty memset when clearing a small dict table
owenthcarey Oct 20, 2026
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
4 changes: 2 additions & 2 deletions crates/weavepy-capi/src/abi314.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1574,13 +1574,13 @@ unsafe fn code_names(
/// `PyCode_GetCellvars(code)` — new reference to `co_cellvars`.
#[no_mangle]
pub unsafe extern "C" fn PyCode_GetCellvars(code: *mut PyObject) -> *mut PyObject {
unsafe { code_names(code, |c| c.cellvars.clone()) }
unsafe { code_names(code, |c| c.cellvars.iter().map(String::from).collect()) }
}

/// `PyCode_GetFreevars(code)` — new reference to `co_freevars`.
#[no_mangle]
pub unsafe extern "C" fn PyCode_GetFreevars(code: *mut PyObject) -> *mut PyObject {
unsafe { code_names(code, |c| c.freevars.clone()) }
unsafe { code_names(code, |c| c.freevars.iter().map(String::from).collect()) }
}

// ---------------------------------------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion crates/weavepy-capi/src/abstract_.rs
Original file line number Diff line number Diff line change
Expand Up @@ -701,7 +701,7 @@ fn type_name(o: &Object) -> &'static str {
fn callable_label(o: &Object) -> String {
use Object as O;
match o {
O::Function(f) => f.code().qualname.clone(),
O::Function(f) => f.code().qualname.to_string(),
O::Builtin(b) => b.name.to_string(),
O::Type(t) => format!("type:{}", t.name),
O::BoundMethod(bm) => format!("bound:{}", callable_label(&bm.function)),
Expand Down
9 changes: 5 additions & 4 deletions crates/weavepy-capi/src/gc_bridge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,10 @@ fn has_clear(obj: &Object) -> bool {
/// once from [`crate::interp::ensure_initialised`], before any extension
/// code (hence any C GC type) can run.
pub fn install() {
weavepy_vm::gc_trace::register_traverse(has_traverse, traverse);
weavepy_vm::gc_trace::register_clear(has_clear, clear);
// (Both match instances only; see `instance_type_ptr`.)
let kind = weavepy_vm::gc_trace::hook_kind::INSTANCE;
weavepy_vm::gc_trace::register_traverse_for(kind, has_traverse, traverse);
weavepy_vm::gc_trace::register_clear_for(kind, has_clear, clear);
}

// ====================================================================
Expand Down Expand Up @@ -253,8 +255,7 @@ pub unsafe extern "C" fn PyObject_GC_IsTracked(op: *mut c_void) -> c_int {
return 0;
}
let obj = unsafe { crate::object::clone_object(op as *mut PyObject) };
let id = weavepy_vm::weakref_registry::id_of(&obj);
c_int::from(weavepy_vm::gc_trace::is_tracked(id))
c_int::from(weavepy_vm::gc_trace::is_tracked(&obj))
}

/// `PyObject_GC_Del(op)` — free a GC object's storage (a GC type's
Expand Down
8,680 changes: 5,049 additions & 3,631 deletions crates/weavepy-cli/weavepy.order

Large diffs are not rendered by default.

39 changes: 26 additions & 13 deletions crates/weavepy-compiler/src/cpython_code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -731,7 +731,7 @@ pub const CO_FAST_FREE: u8 = 0x80;
/// `CO_FAST_LOCAL|CO_FAST_CELL` rather than getting its own entry;
/// the argument slots carry their `CO_FAST_ARG_*` kind and hidden
/// comprehension locals `CO_FAST_HIDDEN`.
fn build_localsplus(code: &CodeObject) -> (Vec<String>, Vec<u8>) {
fn build_localsplus(code: &CodeObject) -> (Vec<crate::Name>, Vec<u8>) {
let mut names = Vec::with_capacity(code.varnames.len() + code.cellvars.len());
let mut kinds = Vec::with_capacity(names.capacity());
// `argvarkinds`: pos-only, pos-or-kw, kw-only, *args, **kwargs.
Expand Down Expand Up @@ -1053,7 +1053,7 @@ pub fn encode(code: &CodeObject) -> CpythonCode {
co_linetable: encode_linetable(code, &ext, &mapped, &zero_width, firstlineno),
co_exceptiontable: encode_exception_table(code, &starts),
co_code,
localsplusnames,
localsplusnames: localsplusnames.into_iter().map(String::from).collect(),
localspluskinds,
stacksize: code.stacksize.unwrap_or_else(|| compute_stacksize(code)),
firstlineno,
Expand Down Expand Up @@ -2061,7 +2061,7 @@ impl SlotMap {
/// Build from a code object's variable lists (mirrors
/// [`DerefSlots::from_code`], inverted).
#[must_use]
pub fn from_code_vars(varnames: &[String], cellvars: &[String], freevars: &[String]) -> Self {
pub fn from_code_vars<S: PartialEq>(varnames: &[S], cellvars: &[S], freevars: &[S]) -> Self {
let nlocals = varnames.len() as u32;
let mut nslots = varnames.len() + freevars.len();
for c in cellvars {
Expand Down Expand Up @@ -2144,16 +2144,29 @@ fn decode_instructions(
// A comprehension whose own target is a cell announces itself with
// `MAKE_CELL s` right after the save and keeps closure semantics.
// Nested comprehensions re-binding the same name push the slot
// again, so each restore pops one entry.
// again, so each restore pops one entry. The restore usually
// follows a `SWAP`, but the optimizer may move the result's store
// ahead of it (`POP_ITER; STORE_FAST result; STORE_FAST s`), so a
// store in the run of stores and swaps right after the loop's
// `END_FOR`/`POP_ITER` ends the region too.
let mut plain_shared: Vec<u32> = Vec::new();
let mut loop_tail = false;
let shared_local = |slot: u32| slot < slots.nlocals && slots.is_cellish(slot);
let end_region = |plain_shared: &mut Vec<u32>, slot: u32| {
if let Some(p) = plain_shared.iter().rposition(|&s| s == slot) {
plain_shared.remove(p);
}
};
for (idx, r) in raws.iter().enumerate() {
let after_swap = idx > 0 && raws[idx - 1].cp_op == op::SWAP;
let in_tail = loop_tail;
loop_tail = match r.cp_op {
op::END_FOR | op::POP_ITER => true,
op::SWAP | op::STORE_FAST | op::STORE_FAST_STORE_FAST | op::POP_TOP | op::NOP => {
loop_tail
}
_ => false,
};
let after_swap = in_tail || (idx > 0 && raws[idx - 1].cp_op == op::SWAP);
match r.cp_op {
op::LOAD_FAST_AND_CLEAR
if shared_local(r.arg)
Expand Down Expand Up @@ -2800,7 +2813,7 @@ mod tests {
..CodeObject::default()
};
// Give a couple of locals so LOAD_FAST vs LOAD_CLOSURE disambiguates.
c.varnames = vec!["a".to_owned(), "b".to_owned()];
c.varnames = vec!["a".into(), "b".into()];
c
}

Expand Down Expand Up @@ -3097,9 +3110,9 @@ mod tests {
linetable: vec![1, 2, 2, 3, 3, 4, 4].into(),
..CodeObject::default()
};
code.varnames = vec!["a".to_owned(), "b".to_owned()];
code.cellvars = vec!["c".to_owned()];
code.freevars = vec!["f".to_owned()];
code.varnames = vec!["a".into(), "b".into()];
code.cellvars = vec!["c".into()];
code.freevars = vec!["f".into()];
code.exception_table.push(ExcHandler {
start: 1,
end: 4,
Expand Down Expand Up @@ -3134,9 +3147,9 @@ mod tests {
linetable: dc.linetable.into(),
..CodeObject::default()
};
code2.varnames = dc.varnames;
code2.cellvars = dc.cellvars;
code2.freevars = dc.freevars;
code2.varnames = dc.varnames.iter().map(crate::Name::from).collect();
code2.cellvars = dc.cellvars.iter().map(crate::Name::from).collect();
code2.freevars = dc.freevars.iter().map(crate::Name::from).collect();
code2.exception_table = dc.exception_table;
let cp2 = encode(&code2);
assert_eq!(cp2.co_code, cp.co_code);
Expand Down Expand Up @@ -3302,7 +3315,7 @@ def f():
/// mapping pattern under-reported by one).
#[test]
fn stack_effects_agree_with_wire_shape() {
let slots = SlotMap::from_code_vars(&[], &[], &[]);
let slots = SlotMap::from_code_vars::<crate::Name>(&[], &[], &[]);
// Opcodes whose WeavePy arg is not the wire arg, or whose
// fallthrough effect legitimately differs from the wire shape
// (jumps take their real effect on the edge; FOR_ITER/END_FOR
Expand Down
12 changes: 6 additions & 6 deletions crates/weavepy-compiler/src/flowgraph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -398,19 +398,19 @@ fn is_unary_not(i: &CfgInstr) -> bool {
pub(crate) struct BuildInput<'a> {
/// Unconditional jumps CPython's codegen emits as the plain `JUMP`
/// pseudo-op; every other one is `JUMP_NO_INTERRUPT`.
pub plain_jumps: &'a std::collections::HashSet<u32>,
pub plain_jumps: &'a crate::fxhash::HashSet<u32>,
/// Unconditional jumps codegen flagged `JUMP_NO_INTERRUPT`.
pub no_interrupt_jumps: &'a std::collections::HashSet<u32>,
pub no_interrupt_jumps: &'a crate::fxhash::HashSet<u32>,
/// `Nop`s standing in for a `SETUP_*` pseudo-op.
pub setup_nops: &'a std::collections::HashMap<u32, OpCode>,
pub setup_nops: &'a crate::fxhash::HashMap<u32, OpCode>,
/// `Nop`s standing in for a `POP_BLOCK` pseudo-op.
pub popblock_nops: &'a std::collections::HashSet<u32>,
pub popblock_nops: &'a crate::fxhash::HashSet<u32>,
/// Conditional jumps that, together with the `COPY 1; TO_BOOL`
/// preceding them, stand for a `JUMP_IF_FALSE` / `JUMP_IF_TRUE`
/// pseudo-op.
pub pseudo_cond_jumps: &'a std::collections::HashSet<u32>,
pub pseudo_cond_jumps: &'a crate::fxhash::HashSet<u32>,
/// Conditional jumps whose `arg` is a *backward* distance.
pub backward_conds: &'a std::collections::HashSet<u32>,
pub backward_conds: &'a crate::fxhash::HashSet<u32>,
/// The code unit carries the PEP 479 `SETUP_CLEANUP` wrap (its
/// range is the last exception-table entry).
pub stopiteration_wrap: bool,
Expand Down
97 changes: 97 additions & 0 deletions crates/weavepy-compiler/src/fxhash.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
//! A fast, unkeyed hasher for the compiler's name sets and binding maps.
//!
//! Scope analysis builds many small sets of identifiers per code object.
//! The standard library's keyed SipHash costs several times more per short
//! string than this multiply-rotate hash (rustc's `FxHasher`), and the
//! compiler never hashes attacker-chosen keys at a scale where hash
//! flooding matters: a source's names are bounded by its own size.

use std::hash::{BuildHasherDefault, Hasher};

/// rustc's `FxHasher`: one rotate, xor and multiply per word.
#[derive(Default, Clone, Copy)]
pub(crate) struct FxHasher {
hash: u64,
}

const SEED: u64 = 0x51_7c_c1_b7_27_22_0a_95;

impl FxHasher {
#[inline]
fn add(&mut self, word: u64) {
self.hash = (self.hash.rotate_left(5) ^ word).wrapping_mul(SEED);
}
}

impl Hasher for FxHasher {
#[inline]
fn write(&mut self, mut bytes: &[u8]) {
while let Some((head, rest)) = bytes.split_first_chunk::<8>() {
self.add(u64::from_le_bytes(*head));
bytes = rest;
}
if let Some((head, rest)) = bytes.split_first_chunk::<4>() {
self.add(u64::from(u32::from_le_bytes(*head)));
bytes = rest;
}
if let Some((head, rest)) = bytes.split_first_chunk::<2>() {
self.add(u64::from(u16::from_le_bytes(*head)));
bytes = rest;
}
if let Some(&b) = bytes.first() {
self.add(u64::from(b));
}
}

#[inline]
fn write_u8(&mut self, i: u8) {
self.add(u64::from(i));
}

#[inline]
fn write_u32(&mut self, i: u32) {
self.add(u64::from(i));
}

#[inline]
fn write_u64(&mut self, i: u64) {
self.add(i);
}

#[inline]
fn write_usize(&mut self, i: usize) {
self.add(i as u64);
}

#[inline]
fn finish(&self) -> u64 {
self.hash
}
}

pub(crate) type FxBuild = BuildHasherDefault<FxHasher>;

/// A `HashSet` over [`FxHasher`]; build one with `HashSet::default()`.
pub(crate) type HashSet<T> = std::collections::HashSet<T, FxBuild>;

/// Adding a borrowed name to a set of owned names, allocating only when
/// the name is new: scope scans see the same few names (`self`, the
/// parameters) over and over.
pub(crate) trait NameSet {
fn add_name(&mut self, name: &str);
}

impl NameSet for HashSet<String> {
#[inline]
fn add_name(&mut self, name: &str) {
if !self.contains(name) {
self.insert(name.to_owned());
}
}
}

/// A `HashMap` over [`FxHasher`]; build one with `HashMap::default()`.
pub(crate) type HashMap<K, V> = std::collections::HashMap<K, V, FxBuild>;

/// An `IndexMap` over [`FxHasher`]; build one with `IndexMap::default()`.
pub(crate) type IndexMap<K, V> = indexmap::IndexMap<K, V, FxBuild>;
4 changes: 2 additions & 2 deletions crates/weavepy-compiler/src/intern.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ impl InternIndex {
i
}

pub(crate) fn intern_name(&mut self, values: &mut Vec<String>, name: &str) -> u32 {
pub(crate) fn intern_name(&mut self, values: &mut Vec<crate::Name>, name: &str) -> u32 {
if values.len() < 32 {
if let Some(i) = values.iter().position(|v| v == name) {
return i as u32;
Expand All @@ -55,7 +55,7 @@ impl InternIndex {
self.next.push(self.heads.insert(key, values.len() as u32));
}
let i = values.len() as u32;
values.push(name.to_owned());
values.push(crate::Name::new(name));
i
}

Expand Down
Loading
Loading