Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
19 changes: 15 additions & 4 deletions pyre/pyre-interpreter/src/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3516,11 +3516,22 @@ pub fn funccall_valuestack(
if fast_natural_arity == crate::PASSTHROUGHARGS1 as usize && nargs >= 1 {
let w_obj = frame.peekvalue(nargs - 1);
let rest = frame.make_arguments(nargs - 1, false, func);
let mut args_w = Vec::with_capacity(nargs);
args_w.push(w_obj);
args_w.extend_from_slice(&rest);
// Same live-variable set as the fixed-arity arm above, for the same
// two reasons: `dropvalues()` retires the frame slots that root these
// values, and `args_w` is a native Vec no root walker updates.
// `builtin_code_call` roots nothing of its own, so the whole
// `[code, w_obj, ...rest]` set has to be published here and read back
// for the call.
let _roots = pyre_object::gc_roots::push_roots();
let root_base = _roots.base();
_roots.pin_root(code as PyObjectRef);
_roots.pin_root(w_obj);
for &w_arg in &rest {
_roots.pin_root(w_arg);
}
frame.dropvalues(dropvalues);
return match unsafe { crate::builtin_code_call(code as PyObjectRef, &args_w) } {
let args_w: Vec<PyObjectRef> = (0..nargs).map(|i| _roots.get(root_base + 1 + i)).collect();
return match unsafe { crate::builtin_code_call(_roots.get(root_base), &args_w) } {
Ok(v) => v,
Err(e) => {
crate::call::set_call_error(e);
Expand Down
8 changes: 8 additions & 0 deletions pyre/pyre-interpreter/src/gateway.rs
Original file line number Diff line number Diff line change
Expand Up @@ -829,6 +829,14 @@ pub unsafe fn builtin_code_set_owner(obj: PyObjectRef, owner: &'static MethodOwn
/// here, so a call can never reach the implementation with an unchecked
/// receiver or a slice the implementation is not written for.
///
/// Roots nothing of its own: `args` is a native slice the collector does not
/// update, and the implementation it dispatches to runs Python. Every caller
/// therefore has to publish `[obj, args...]` on the shadow stack and read the
/// slice back from it, so the set stays live for the whole body and the slice
/// is current at entry. `dropvalues()` retires the frame slots that would
/// otherwise root a peeked argument, so a frame dispatch has to publish before
/// it drops, not after.
///
/// # Safety
/// `obj` must point to a valid `BuiltinCode`.
#[inline]
Expand Down
35 changes: 26 additions & 9 deletions pyre/pyre-interpreter/src/runtime_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,17 +223,34 @@ pub(crate) fn jit_publish_exception(exc_obj: PyObjectRef) {
}
}

/// Widest `jit_call_known_builtin_N` published by [`known_builtin_call_helper`],
/// plus the leading code slot.
const MAX_KNOWN_BUILTIN_ARGS: usize = 9;

fn call_builtin_with_args(callable: i64, args: &[i64]) -> i64 {
debug_assert!(args.len() <= MAX_KNOWN_BUILTIN_ARGS);
let callable = callable as PyObjectRef;
unsafe {
let code = crate::getcode(callable);
let arg_slice = std::slice::from_raw_parts(args.as_ptr() as *const PyObjectRef, args.len());
match crate::builtin_code_call(code as PyObjectRef, arg_slice) {
Ok(result) => result as i64,
Err(mut e) => {
jit_publish_exception(e.to_exc_object());
0 // garbage — GuardNoException will fire
}
let code = unsafe { crate::getcode(callable) } as PyObjectRef;
// `args` is the compiled call's own argument array; no root walker updates
// it, and `builtin_code_call` roots nothing of its own — the direct call
// sites hand it an already-live slice. Publish `[code, args...]` for the
// duration of the body and read the call arguments back from it, the same
// frame `call_builtin_code_positional` builds for the interpreter entry.
let _roots = pyre_object::gc_roots::push_roots();
let root_base = _roots.base();
_roots.pin_root(code);
for &arg in args {
_roots.pin_root(arg as PyObjectRef);
}
let mut rooted = [PY_NULL; MAX_KNOWN_BUILTIN_ARGS];
for (index, slot) in rooted[..args.len()].iter_mut().enumerate() {
*slot = _roots.get(root_base + 1 + index);
}
match unsafe { crate::builtin_code_call(_roots.get(root_base), &rooted[..args.len()]) } {
Ok(result) => result as i64,
Err(mut e) => {
jit_publish_exception(e.to_exc_object());
0 // garbage — GuardNoException will fire
}
}
}
Expand Down
Loading