-
Notifications
You must be signed in to change notification settings - Fork 19
interpreter: reload the frame in PyFrame::push instead of anchoring each opcode #1411
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -75,9 +75,8 @@ fn pop_n<H: SharedOpcodeHandler + ?Sized>( | |
|
|
||
| pub fn opcode_make_function<H: SharedOpcodeHandler + ?Sized>(handler: &mut H) -> OpcodeResult<()> { | ||
| let code_obj = handler.pop_value()?; | ||
| let anchor = handler.anchor(); | ||
| let func = handler.make_function(code_obj)?; | ||
| H::push_anchored(&anchor, func) | ||
| handler.push_value(func) | ||
| } | ||
|
|
||
| pub fn opcode_call<H: SharedOpcodeHandler + ?Sized>( | ||
|
|
@@ -92,44 +91,39 @@ pub fn opcode_call<H: SharedOpcodeHandler + ?Sized>( | |
| 0 => { | ||
| let _null_or_self = handler.pop_value()?; | ||
| let callable = handler.pop_value()?; | ||
| let anchor = handler.anchor(); | ||
| let result = handler.call_callable(callable, &[])?; | ||
| H::push_anchored(&anchor, result) | ||
| handler.push_value(result) | ||
| } | ||
| 1 => { | ||
| let a0 = handler.pop_value()?; | ||
| let _null_or_self = handler.pop_value()?; | ||
| let callable = handler.pop_value()?; | ||
| let anchor = handler.anchor(); | ||
| let result = handler.call_callable(callable, &[a0])?; | ||
| H::push_anchored(&anchor, result) | ||
| handler.push_value(result) | ||
| } | ||
| 2 => { | ||
| let a1 = handler.pop_value()?; | ||
| let a0 = handler.pop_value()?; | ||
| let _null_or_self = handler.pop_value()?; | ||
| let callable = handler.pop_value()?; | ||
| let anchor = handler.anchor(); | ||
| let result = handler.call_callable(callable, &[a0, a1])?; | ||
| H::push_anchored(&anchor, result) | ||
| handler.push_value(result) | ||
| } | ||
| 3 => { | ||
| let a2 = handler.pop_value()?; | ||
| let a1 = handler.pop_value()?; | ||
| let a0 = handler.pop_value()?; | ||
| let _null_or_self = handler.pop_value()?; | ||
| let callable = handler.pop_value()?; | ||
| let anchor = handler.anchor(); | ||
| let result = handler.call_callable(callable, &[a0, a1, a2])?; | ||
| H::push_anchored(&anchor, result) | ||
| handler.push_value(result) | ||
| } | ||
| _ => { | ||
| let args = pop_n(handler, nargs)?; | ||
| let _null_or_self = handler.pop_value()?; | ||
| let callable = handler.pop_value()?; | ||
| let anchor = handler.anchor(); | ||
| let result = handler.call_callable(callable, &args)?; | ||
| H::push_anchored(&anchor, result) | ||
| handler.push_value(result) | ||
|
Comment on lines
+95
to
+126
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
rg -n -P --type rust -C 5 \
'impl(?:<[^>]*>)?\s+SharedOpcodeHandler\s+for|fn\s+push_value\s*\(' \
pyre majitRepository: youknowone/pyre Length of output: 2772 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '--- shared opcode trait and anchor documentation ---'
sed -n '1,70p' pyre/pyre-interpreter/src/shared_opcode.rs
printf '%s\n' '--- affected shared opcode helpers ---'
sed -n '70,210p' pyre/pyre-interpreter/src/shared_opcode.rs
printf '%s\n' '--- PyFrame SharedOpcodeHandler implementation ---'
sed -n '2380,2445p' pyre/pyre-interpreter/src/eval.rs
printf '%s\n' '--- affected pyopcode helpers ---'
sed -n '705,745p' pyre/pyre-interpreter/src/pyopcode.rs
sed -n '1145,1185p' pyre/pyre-interpreter/src/pyopcode.rs
printf '%s\n' '--- all implementations and push-related definitions ---'
rg -n -P --type rust \
'impl(?:<[^>]*>)?\s+SharedOpcodeHandler\s+for|fn\s+(?:push_value|push_anchored|anchor)\s*\(' \
.
printf '%s\n' '--- relevant call sites ---'
rg -n -P --type rust \
'SharedOpcodeHandler::|opcode_(?:call|make_function|build_list|build_tuple|build_map|unpack_sequence|load_attr)|push_value\s*\(' \
pyre/pyre-interpreter/src/shared_opcode.rs pyre/pyre-interpreter/src/pyopcode.rsRepository: youknowone/pyre Length of output: 20348 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '--- FrameAnchor and frame stack operations ---'
rg -n -C 8 --type rust \
'struct\s+FrameAnchor|type\s+FrameAnchor|impl\s+FrameAnchor|fn\s+(?:new|live|push)\s*\(' \
pyre/pyre-interpreter/src
printf '%s\n' '--- anchor usage ---'
rg -n -C 4 --type rust \
'\.(?:anchor|push_anchored)\s*\(|::push_anchored\s*\(|\banchor\s*\(' \
pyre majit
printf '%s\n' '--- allocation-capable SharedOpcodeHandler operations ---'
rg -n -C 5 --type rust \
'fn\s+(?:make_function|call_callable|build_list|build_tuple|build_map|unpack_sequence|load_attr|load_special_attr|iter_value|null_value)\s*\(' \
pyre/pyre-interpreter/src
printf '%s\n' '--- repository change summary ---'
git diff --stat
git status --shortRepository: youknowone/pyre Length of output: 50374 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '--- FrameAnchor implementation ---'
sed -n '180,255p' pyre/pyre-interpreter/src/eval.rs
printf '%s\n' '--- live frame reload and push ---'
sed -n '3165,3210p' pyre/pyre-interpreter/src/pyframe.rs
printf '%s\n' '--- exact anchor references ---'
rg -n --type rust \
'push_anchored|\.anchor\s*\(|::anchor\s*\(|\banchor\s*=' \
pyre majit || true
printf '%s\n' '--- opcode handler traits and PyFrame implementations ---'
rg -n -C 6 --type rust \
'trait\s+(?:IterOpcodeHandler|NamespaceOpcodeHandler|SharedOpcodeHandler)|impl\s+(?:IterOpcodeHandler|NamespaceOpcodeHandler|SharedOpcodeHandler)\s+for\s+PyFrame' \
pyre/pyre-interpreter/src
printf '%s\n' '--- null and iterator implementations ---'
rg -n -C 8 --type rust \
'fn\s+(?:null_value|iter_value)\s*\(' \
pyre/pyre-interpreter/src
printf '%s\n' '--- change state ---'
git diff --stat
git status --shortRepository: youknowone/pyre Length of output: 25261 🏁 Script executed: #!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
import re
root = Path(".")
rust_files = [p for p in root.rglob("*.rs") if ".git" not in p.parts]
impls = []
for path in rust_files:
text = path.read_text(errors="replace")
for m in re.finditer(r"impl(?:<[^{}]*>)?\s+SharedOpcodeHandler\s+for\s+([^{\s]+)\s*\{", text):
start = m.end()
depth = 1
i = start
while i < len(text) and depth:
if text[i] == "{":
depth += 1
elif text[i] == "}":
depth -= 1
i += 1
body = text[start:i]
push = re.search(r"fn\s+push_value\s*\([^)]*\)\s*->\s*[^{]+\{(?P<body>.*?)\n\s*\}", body, re.S)
impls.append((str(path), m.group(1), bool(push), push.group("body") if push else ""))
print("SharedOpcodeHandler implementations:", len(impls))
for path, ty, has_push, body in impls:
print(f"{path}: {ty}; push_value={has_push}; reloads_live_frame={'live_mut' in body}")
shared = Path("pyre/pyre-interpreter/src/shared_opcode.rs").read_text()
affected = [
"opcode_make_function",
"opcode_call",
"opcode_build_list",
"opcode_build_tuple",
"opcode_build_map",
"opcode_unpack_sequence",
"opcode_load_attr",
]
for name in affected:
m = re.search(rf"pub fn {name}\b.*?(?=\n(?:pub fn|$))", shared, re.S)
if not m:
raise SystemExit(f"missing {name}")
body = m.group(0)
print(f"{name}: direct push_value={body.count('handler.push_value(')}")
eval_text = Path("pyre/pyre-interpreter/src/eval.rs").read_text()
print("push_anchored call sites in eval.rs:", eval_text.count("Self::push_anchored("))
PYRepository: youknowone/pyre Length of output: 592 🏁 Script executed: #!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
import re
eval_text = Path("pyre/pyre-interpreter/src/eval.rs").read_text()
pyframe_text = Path("pyre/pyre-interpreter/src/pyframe.rs").read_text()
push_value = re.search(
r"fn\s+push_value\s*\(\s*&mut self,\s+value:\s+Self::Value\s*\)"
r".*?\{\s*(?P<body>.*?)\n\s*\}",
eval_text,
re.S,
)
if not push_value:
raise SystemExit("PyFrame::push_value not found")
body = push_value.group("body")
print("PyFrame::push_value delegates to PyFrame::push:", "self.push(value)" in body)
push = re.search(
r"pub fn\s+push\s*\(\s*&mut self,\s+value:\s+PyObjectRef\s*\)"
r".*?\{\s*(?P<body>.*?)\n\s*\}",
pyframe_text,
re.S,
)
if not push:
raise SystemExit("PyFrame::push not found")
body = push.group("body")
print("PyFrame::push reloads with live_mut:", "self.live_mut()" in body)
for path, needles in {
"pyre/pyre-interpreter/src/shared_opcode.rs": [
"handler.push_value(func)",
"handler.push_value(result)",
"handler.push_value(list)",
"handler.push_value(tuple)",
"handler.push_value(dict)",
"handler.push_value(item)?",
"handler.push_value(attr)",
],
"pyre/pyre-interpreter/src/pyopcode.rs": [
"handler.push_value(iterator)",
"self.push_value(attr)?",
"self.push_value(null)",
],
}.items():
text = Path(path).read_text()
print(path)
for needle in needles:
print(f" {needle}: {needle in text}")
print("remaining push_anchored sites:",
len(re.findall(r"Self::push_anchored\s*\(", eval_text)))
PYRepository: youknowone/pyre Length of output: 696 Update the
📍 Affects 2 files
🤖 Prompt for AI AgentsSource: Coding guidelines |
||
| } | ||
| } | ||
| } | ||
|
|
@@ -139,29 +133,26 @@ pub fn opcode_build_list<H: SharedOpcodeHandler + ?Sized>( | |
| size: usize, | ||
| ) -> OpcodeResult<()> { | ||
| let items = pop_n(handler, size)?; | ||
| let anchor = handler.anchor(); | ||
| let list = handler.build_list(&items)?; | ||
| H::push_anchored(&anchor, list) | ||
| handler.push_value(list) | ||
| } | ||
|
|
||
| pub fn opcode_build_tuple<H: SharedOpcodeHandler + ?Sized>( | ||
| handler: &mut H, | ||
| size: usize, | ||
| ) -> OpcodeResult<()> { | ||
| let items = pop_n(handler, size)?; | ||
| let anchor = handler.anchor(); | ||
| let tuple = handler.build_tuple(&items)?; | ||
| H::push_anchored(&anchor, tuple) | ||
| handler.push_value(tuple) | ||
| } | ||
|
|
||
| pub fn opcode_build_map<H: SharedOpcodeHandler + ?Sized>( | ||
| handler: &mut H, | ||
| size: usize, | ||
| ) -> OpcodeResult<()> { | ||
| let items = pop_n(handler, size * 2)?; | ||
| let anchor = handler.anchor(); | ||
| let dict = handler.build_map(&items)?; | ||
| H::push_anchored(&anchor, dict) | ||
| handler.push_value(dict) | ||
| } | ||
|
|
||
| pub fn opcode_store_subscr<H: SharedOpcodeHandler + ?Sized>(handler: &mut H) -> OpcodeResult<()> { | ||
|
|
@@ -188,10 +179,9 @@ pub fn opcode_unpack_sequence<H: SharedOpcodeHandler + ?Sized>( | |
| count: usize, | ||
| ) -> OpcodeResult<()> { | ||
| let seq = handler.pop_value()?; | ||
| let anchor = handler.anchor(); | ||
| let items = handler.unpack_sequence(seq, count)?; | ||
| for item in items.into_iter().rev() { | ||
| H::push_anchored(&anchor, item)?; | ||
| handler.push_value(item)?; | ||
| } | ||
| Ok(()) | ||
| } | ||
|
|
@@ -201,9 +191,8 @@ pub fn opcode_load_attr<H: SharedOpcodeHandler + ?Sized>( | |
| name: &str, | ||
| ) -> OpcodeResult<()> { | ||
| let obj = handler.pop_value()?; | ||
| let anchor = handler.anchor(); | ||
| let attr = handler.load_attr(obj, name)?; | ||
| H::push_anchored(&anchor, attr) | ||
| handler.push_value(attr) | ||
| } | ||
|
|
||
| pub fn opcode_store_attr<H: SharedOpcodeHandler + ?Sized>( | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When an allocating opcode such as
CALLor a user-defined__iter__triggers a minor collection and then keeps allocating long enough to reuse the moved-from nursery slot before returning, this lookup still starts from the original stale&mut self.gc_current_object_addresscan only follow a forwarding header that remains at that address, whileMiniMarkGC::reset_nursery_with_pinnedmakes moved-from ranges immediately reusable; once overwritten,pushwrites through an abandoned or unrelated frame and corrupts the value stack. The removedFrameAnchorremained updated across every collection, so the centralized replacement must reload from a durable root/current-frame slot or implement the actual GC live-variable update rather than recover through stale nursery bytes.AGENTS.md reference: AGENTS.md:L212-L217
Useful? React with 👍 / 👎.