Skip to content

Commit 9dd1867

Browse files
committed
python-native: Use fastcall for explicit-args functions with right sig
1 parent 42d18fd commit 9dd1867

File tree

3 files changed

+91
-9
lines changed

3 files changed

+91
-9
lines changed

src/interrogate/interfaceMakerPythonNative.cxx

Lines changed: 79 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1725,16 +1725,33 @@ write_module_support(ostream &out, ostream *out_h, InterrogateModuleDef *def) {
17251725
string name1 = methodNameFromCppName(func, "", false);
17261726
string name2 = methodNameFromCppName(func, "", true);
17271727

1728+
bool exclusive_fastcall = true;
1729+
for (FunctionRemap *remap : func->_remaps) {
1730+
if (((remap->_flags & FunctionRemap::F_fastcall) == 0 ||
1731+
(remap->_flags & FunctionRemap::F_explicit_args) == 0) && is_remap_legal(remap)) {
1732+
exclusive_fastcall = false;
1733+
}
1734+
}
1735+
17281736
string flags;
17291737
string fptr = "&" + func->_name;
17301738
switch (func->_args_type) {
17311739
case AT_keyword_args:
1732-
flags = "METH_VARARGS | METH_KEYWORDS";
1740+
if (exclusive_fastcall) {
1741+
flags = "METH_FASTCALL_OR_VARARGS | METH_KEYWORDS";
1742+
} else {
1743+
flags = "METH_VARARGS | METH_KEYWORDS";
1744+
}
17331745
fptr = "(PyCFunction) " + fptr;
17341746
break;
17351747

17361748
case AT_varargs:
1737-
flags = "METH_VARARGS";
1749+
if (exclusive_fastcall) {
1750+
flags = "METH_FASTCALL_OR_VARARGS";
1751+
fptr = "(PyCFunction) " + fptr;
1752+
} else {
1753+
flags = "METH_VARARGS";
1754+
}
17381755
break;
17391756

17401757
case AT_single_arg:
@@ -1903,19 +1920,36 @@ write_module_class(ostream &out, Object *obj) {
19031920
got_deepcopy = true;
19041921
}
19051922

1923+
bool exclusive_fastcall = true;
1924+
for (FunctionRemap *remap : func->_remaps) {
1925+
if (((remap->_flags & FunctionRemap::F_fastcall) == 0 ||
1926+
(remap->_flags & FunctionRemap::F_explicit_args) == 0) && is_remap_legal(remap)) {
1927+
exclusive_fastcall = false;
1928+
}
1929+
}
1930+
19061931
string name1 = methodNameFromCppName(func, export_class_name, false);
19071932
string name2 = methodNameFromCppName(func, export_class_name, true);
19081933

19091934
string flags;
19101935
string fptr = "&" + func->_name;
19111936
switch (func->_args_type) {
19121937
case AT_keyword_args:
1913-
flags = "METH_VARARGS | METH_KEYWORDS";
1938+
if (exclusive_fastcall) {
1939+
flags = "METH_FASTCALL_OR_VARARGS | METH_KEYWORDS";
1940+
} else {
1941+
flags = "METH_VARARGS | METH_KEYWORDS";
1942+
}
19141943
fptr = "(PyCFunction) " + fptr;
19151944
break;
19161945

19171946
case AT_varargs:
1918-
flags = "METH_VARARGS";
1947+
if (exclusive_fastcall) {
1948+
flags = "METH_FASTCALL_OR_VARARGS";
1949+
fptr = "(PyCFunction) " + fptr;
1950+
} else {
1951+
flags = "METH_VARARGS";
1952+
}
19191953
break;
19201954

19211955
case AT_single_arg:
@@ -3945,12 +3979,18 @@ write_function_for_top(ostream &out, InterfaceMaker::Object *obj, InterfaceMaker
39453979
// First check if this function has non-slotted and legal remaps, ie. if we
39463980
// should even write it.
39473981
bool has_remaps = false;
3982+
bool exclusive_fastcall = true;
39483983

39493984
for (FunctionRemap *remap : func->_remaps) {
39503985
if (!is_remap_legal(remap)) {
39513986
continue;
39523987
}
39533988

3989+
if (((remap->_flags & FunctionRemap::F_fastcall) == 0 ||
3990+
(remap->_flags & FunctionRemap::F_explicit_args) == 0) && is_remap_legal(remap)) {
3991+
exclusive_fastcall = false;
3992+
}
3993+
39543994
SlottedFunctionDef slotted_def;
39553995
if (!get_slotted_function_def(obj, func, remap, slotted_def) || slotted_def._keep_method) {
39563996
// It has a non-slotted remap, so we should write it.
@@ -3994,11 +4034,19 @@ write_function_for_top(ostream &out, InterfaceMaker::Object *obj, InterfaceMaker
39944034

39954035
switch (func->_args_type) {
39964036
case AT_keyword_args:
3997-
prototype += ", PyObject *args, PyObject *kwds";
4037+
if (exclusive_fastcall) {
4038+
prototype += ", FASTCALL_OR_VARARGS_KEYWORDS_ARGS";
4039+
} else {
4040+
prototype += ", PyObject *args, PyObject *kwds";
4041+
}
39984042
break;
39994043

40004044
case AT_varargs:
4001-
prototype += ", PyObject *args";
4045+
if (exclusive_fastcall) {
4046+
prototype += ", FASTCALL_OR_VARARGS_ARGS";
4047+
} else {
4048+
prototype += ", PyObject *args";
4049+
}
40024050
break;
40034051

40044052
case AT_single_arg:
@@ -4012,7 +4060,7 @@ write_function_for_top(ostream &out, InterfaceMaker::Object *obj, InterfaceMaker
40124060
prototype += ")";
40134061

40144062
string expected_params;
4015-
write_function_for_name(out, obj, func->_remaps, prototype, expected_params, true, func->_args_type, RF_pyobject | RF_err_null);
4063+
write_function_for_name(out, obj, func->_remaps, prototype, expected_params, true, func->_args_type, RF_pyobject | RF_err_null, exclusive_fastcall);
40164064

40174065
// Now synthesize a variable for the docstring.
40184066
ostringstream comment;
@@ -4047,7 +4095,8 @@ write_function_for_name(ostream &out, Object *obj,
40474095
const string &function_name,
40484096
string &expected_params,
40494097
bool coercion_allowed,
4050-
ArgsType args_type, int return_flags) {
4098+
ArgsType args_type, int return_flags,
4099+
bool exclusive_fastcall) {
40514100
std::map<int, std::set<FunctionRemap *> > map_sets;
40524101
std::map<int, std::set<FunctionRemap *> >::iterator mii;
40534102
std::set<FunctionRemap *>::iterator sii;
@@ -4151,7 +4200,15 @@ write_function_for_name(ostream &out, Object *obj,
41514200

41524201
if (args_type == AT_keyword_args && !has_keywords) {
41534202
// We don't actually take keyword arguments. Make sure we didn't get any.
4203+
if (exclusive_fastcall) {
4204+
out << "#if METH_FASTCALL_OR_VARARGS == METH_FASTCALL\n";
4205+
out << " if (fc_kwnames != nullptr && PyTuple_GET_SIZE(fc_kwnames) > 0) {\n";
4206+
out << "#else\n";
4207+
}
41544208
out << " if (kwds != nullptr && PyDict_Size(kwds) > 0) {\n";
4209+
if (exclusive_fastcall) {
4210+
out << "#endif\n";
4211+
}
41554212
out << "#ifdef NDEBUG\n";
41564213
error_raise_return(out, 4, return_flags, "TypeError", "function takes no keyword arguments");
41574214
out << "#else\n";
@@ -4175,6 +4232,11 @@ write_function_for_name(ostream &out, Object *obj,
41754232
}
41764233

41774234
if (has_fastcall) {
4235+
// Generate backward compatibility for older Python versions that don't
4236+
// support the fastcall protocol.
4237+
if (exclusive_fastcall) {
4238+
out << "#if METH_FASTCALL_OR_VARARGS != METH_FASTCALL\n";
4239+
}
41784240
if (args_type == AT_keyword_args && has_keywords) {
41794241
out << " Py_ssize_t fc_nargs = PyTuple_GET_SIZE(args);\n";
41804242
out << " PyObject *const *fc_args;\n";
@@ -4224,6 +4286,15 @@ write_function_for_name(ostream &out, Object *obj,
42244286
out << " PyObject *fc_kwnames = nullptr;\n";
42254287
}
42264288
}
4289+
if (exclusive_fastcall) {
4290+
if (return_flags & RF_decref_kwnames) {
4291+
// If we decref this later (unconditionally), we need to unref this
4292+
// in the case where we don't construct in ourselves
4293+
out << "#else\n";
4294+
out << " Py_XINCREF(fc_kwnames);\n";
4295+
}
4296+
out << "#endif\n";
4297+
}
42274298
}
42284299

42294300
if (remap->_flags & FunctionRemap::F_explicit_args) {

src/interrogate/interfaceMakerPythonNative.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,8 @@ class InterfaceMakerPythonNative : public InterfaceMakerPython {
152152
const Function::Remaps &remaps,
153153
const std::string &name, std::string &expected_params,
154154
bool coercion_allowed,
155-
ArgsType args_type, int return_flags);
155+
ArgsType args_type, int return_flags,
156+
bool exclusive_fastcall = false);
156157
void write_coerce_constructor(std::ostream &out, Object *obj, bool is_const);
157158

158159
int collapse_default_remaps(std::map<int, std::set<FunctionRemap *> > &map_sets,

src/interrogatedb/py_support.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,16 @@
1717

1818
#include "py_panda.h"
1919

20+
#if PY_VERSION_HEX >= 0x03080000
21+
#define METH_FASTCALL_OR_VARARGS METH_FASTCALL
22+
#define FASTCALL_OR_VARARGS_ARGS PyObject *const *fc_args, Py_ssize_t fc_nargs
23+
#define FASTCALL_OR_VARARGS_KEYWORDS_ARGS PyObject *const *fc_args, Py_ssize_t fc_nargs, PyObject *fc_kwnames
24+
#else
25+
#define METH_FASTCALL_OR_VARARGS METH_VARARGS
26+
#define FASTCALL_OR_VARARGS_ARGS PyObject *args
27+
#define FASTCALL_OR_VARARGS_KEYWORDS_ARGS PyObject *args, PyObject *kwds
28+
#endif
29+
2030
using namespace std;
2131

2232
// This is now simply a forward declaration. The actual definition is created

0 commit comments

Comments
 (0)