From caf4713b3a3fec5918f4399d4c9dd50660123daf Mon Sep 17 00:00:00 2001 From: ijl Date: Wed, 23 Oct 2019 21:40:38 +0000 Subject: [PATCH] FFI for PEP 590 Vectorcall https://www.python.org/dev/peps/pep-0590/ This was tested on 3.7 using _PyCFunctionFast and 3.8 using PyObject_Vectorcall. Extending pyo3-derive-backend to generate code using vectorcall is not done here. This exposes PyObject_Vectorcall with a link_name to the underscored name on 3.8 because it is expected to be stabilized on 3.9. This fixes the "fast" objects being new in 3.7, not 3.6. --- CHANGELOG.md | 6 ++++++ src/ffi/methodobject.rs | 40 +++++++++++++++++++++++++++++++++++++--- src/ffi/object.rs | 10 ++++++++++ 3 files changed, 53 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cd12b553c0f..4861ad83aa1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## Unreleased + +### Added + +* FFI compatibility for PEP 590 Vectorcall. + ## [0.8.1] ### Added diff --git a/src/ffi/methodobject.rs b/src/ffi/methodobject.rs index d23cfbed436..fadcdf0763c 100644 --- a/src/ffi/methodobject.rs +++ b/src/ffi/methodobject.rs @@ -15,7 +15,39 @@ pub unsafe fn PyCFunction_Check(op: *mut PyObject) -> c_int { pub type PyCFunction = unsafe extern "C" fn(slf: *mut PyObject, args: *mut PyObject) -> *mut PyObject; -#[cfg(all(Py_3_6, not(Py_LIMITED_API)))] +#[cfg(all(Py_3_8, not(Py_LIMITED_API)))] +#[cfg_attr(Py_3_8, link_name = "_PyObject_Vectorcall")] +pub type PyObject_Vectorcall = unsafe extern "C" fn( + slf: *mut PyObject, + // positional and keyword arguments + args: *const *mut PyObject, + // number of position arguments in args, after which values are kwargs + nargs: crate::ffi::pyport::Py_ssize_t, + // tuple of kwargs, if given, or null + kwnames: *mut PyObject, +) -> *mut PyObject; + +#[cfg(all(Py_3_8, not(Py_LIMITED_API)))] +#[cfg_attr(Py_3_8, link_name = "PyVectorcall_Call")] +pub type PyVectorcall_Call = unsafe extern "C" fn( + obj: *mut PyObject, + tuple: *mut PyObject, + dict: *mut PyObject, +) -> *mut PyObject; + +#[cfg(all(Py_3_7, not(Py_LIMITED_API)))] +const PY_VECTORCALL_ARGUMENTS_OFFSET: crate::ffi::pyport::Py_ssize_t = + 1 << (8 * std::mem::size_of::() - 1); + +#[cfg(all(Py_3_7, not(Py_LIMITED_API)))] +#[inline(always)] +pub unsafe fn PyVectorcall_NARGS( + n: crate::ffi::pyport::Py_ssize_t, +) -> crate::ffi::pyport::Py_ssize_t { + n & !PY_VECTORCALL_ARGUMENTS_OFFSET +} + +#[cfg(all(Py_3_7, not(Py_LIMITED_API)))] pub type _PyCFunctionFast = unsafe extern "C" fn( slf: *mut PyObject, args: *mut *mut PyObject, @@ -102,8 +134,10 @@ slot like sq_contains. */ pub const METH_COEXIST: c_int = 0x0040; -#[cfg(all(Py_3_6, not(Py_LIMITED_API)))] -pub const METHOD_FASTCALL: c_int = 0x0080; +/* METH_FASTCALL indicates the PEP 590 Vectorcall calling format. It may +be specified alone or with METH_KEYWORDS. */ +#[cfg(all(Py_3_7, not(Py_LIMITED_API)))] +pub const METH_FASTCALL: c_int = 0x0080; #[cfg_attr(windows, link(name = "pythonXY"))] extern "C" { diff --git a/src/ffi/object.rs b/src/ffi/object.rs index 69f5ed0b615..b06c45b4f96 100644 --- a/src/ffi/object.rs +++ b/src/ffi/object.rs @@ -463,7 +463,10 @@ mod typeobject { pub tp_basicsize: Py_ssize_t, pub tp_itemsize: Py_ssize_t, pub tp_dealloc: Option, + #[cfg(not(Py_3_8))] pub tp_print: Option, + #[cfg(Py_3_8)] + pub tp_vectorcall_offset: Py_ssize_t, pub tp_getattr: Option, pub tp_setattr: Option, pub tp_as_async: *mut PyAsyncMethods, @@ -529,7 +532,10 @@ mod typeobject { tp_basicsize: 0, tp_itemsize: 0, tp_dealloc: None, + #[cfg(not(Py_3_8))] tp_print: None, + #[cfg(Py_3_8)] + tp_vectorcall_offset: 0, tp_getattr: None, tp_setattr: None, $tp_as_async: ptr::null_mut(), @@ -847,6 +853,10 @@ pub const Py_TPFLAGS_HEAPTYPE: c_ulong = (1 << 9); /// Set if the type allows subclassing pub const Py_TPFLAGS_BASETYPE: c_ulong = (1 << 10); +/// Set if the type implements the vectorcall protocol (PEP 590) +#[cfg(all(Py_3_8, not(Py_LIMITED_API)))] +pub const _Py_TPFLAGS_HAVE_VECTORCALL: c_ulong = (1 << 11); + /// Set if the type is 'ready' -- fully initialized pub const Py_TPFLAGS_READY: c_ulong = (1 << 12);