Skip to content

Commit 4ff29ff

Browse files
committed
Avoid temporary arg tuples in PyObject_Call and related
1 parent ab7bffe commit 4ff29ff

File tree

2 files changed

+125
-43
lines changed

2 files changed

+125
-43
lines changed

graalpython/com.oracle.graal.python.cext/src/object.c

Lines changed: 23 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -199,76 +199,65 @@ PyObject * PyVectorcall_Call(PyObject *callable, PyObject *tuple, PyObject *kwar
199199
return _Py_CheckFunctionResult(callable, result, NULL);
200200
}
201201

202+
typedef PyObject *(*call_fun_t)(PyObject *, PyObject *, PyObject *);
203+
UPCALL_TYPED_ID(PyObject_Call, call_fun_t);
202204
PyObject* PyObject_Call(PyObject* callable, PyObject* args, PyObject* kwargs) {
203-
return polyglot_invoke(PY_TRUFFLE_CEXT, "PyObject_Call", native_to_java(callable), native_to_java(args), native_to_java(kwargs));
205+
return _jls_PyObject_Call(native_to_java(callable), native_to_java(args), native_to_java(kwargs));
204206
}
205207

206208
PyObject* PyObject_CallObject(PyObject* callable, PyObject* args) {
207-
return PyObject_Call(callable, args, PyDict_New());
209+
return _jls_PyObject_Call(native_to_java(callable), native_to_java(args), NULL);
208210
}
209211

210-
NO_INLINE
211212
PyObject* PyObject_CallFunction(PyObject* callable, const char* fmt, ...) {
212213
if (fmt == NULL || fmt[0] == '\0') {
213-
return PyObject_CallObject(callable, NULL);
214+
return _jls_PyObject_Call(native_to_java(callable), NULL, NULL);
214215
}
215-
216216
va_list va;
217217
va_start(va, fmt);
218218
PyObject* args = Py_VaBuildValue(fmt, va);
219219
va_end(va);
220220

221-
if (strlen(fmt) < 2) {
221+
if (strlen(fmt) == 1) {
222222
PyObject* singleArg = args;
223-
args = PyTuple_New(strlen(fmt));
224-
if (strlen(fmt) == 1) {
225-
Py_XINCREF(singleArg);
226-
PyTuple_SetItem(args, 0, singleArg);
227-
}
223+
args = PyTuple_New(1);
224+
Py_XINCREF(singleArg);
225+
PyTuple_SetItem(args, 0, singleArg);
228226
}
229-
return PyObject_CallObject(callable, args);
227+
return _jls_PyObject_Call(native_to_java(callable), native_to_java(args), NULL);
230228
}
231229

232-
NO_INLINE
233230
PyObject* _PyObject_CallFunction_SizeT(PyObject* callable, const char* fmt, ...) {
234231
if (fmt == NULL || fmt[0] == '\0') {
235-
return PyObject_CallObject(callable, NULL);
232+
return _jls_PyObject_Call(native_to_java(callable), NULL, NULL);
236233
}
237234

238235
va_list va;
239236
va_start(va, fmt);
240237
PyObject* args = Py_VaBuildValue(fmt, va);
241238
va_end(va);
242239

243-
if (strlen(fmt) < 2) {
240+
if (strlen(fmt) == 1) {
244241
PyObject* singleArg = args;
245-
args = PyTuple_New(strlen(fmt));
246-
if (strlen(fmt) == 1) {
247-
Py_XINCREF(singleArg);
248-
PyTuple_SetItem(args, 0, singleArg);
249-
}
242+
args = PyTuple_New(1);
243+
Py_XINCREF(singleArg);
244+
PyTuple_SetItem(args, 0, singleArg);
250245
}
251-
return PyObject_CallObject(callable, args);
246+
return _jls_PyObject_Call(native_to_java(callable), native_to_java(args), NULL);
252247
}
253248

254-
NO_INLINE
249+
typedef PyObject *(*call_fun_obj_args_t)(PyObject *, void *);
250+
UPCALL_TYPED_ID(PyObject_CallFunctionObjArgs, call_fun_obj_args_t);
255251
PyObject* PyObject_CallFunctionObjArgs(PyObject *callable, ...) {
256252
va_list vargs;
257253
va_start(vargs, callable);
258254
// the arguments are given as a variable list followed by NULL
259-
int nargs = polyglot_get_array_size(vargs) - 1;
260-
PyObject* args = PyTuple_New(nargs);
261-
for (int i = 0; i < nargs; i++) {
262-
PyObject* arg = (PyObject*) va_arg(vargs, PyObject *);
263-
Py_INCREF(arg);
264-
PyTuple_SetItem(args, i, arg);
265-
}
255+
PyObject *result = _jls_PyObject_CallFunctionObjArgs(native_to_java(callable), &vargs);
266256
va_end(vargs);
267-
return PyObject_CallObject(callable, args);
257+
return result;
268258
}
269259

270260
UPCALL_ID(PyObject_CallMethod);
271-
NO_INLINE
272261
PyObject* PyObject_CallMethod(PyObject* object, const char* method, const char* fmt, ...) {
273262
PyObject* args;
274263
if (fmt == NULL || fmt[0] == '\0') {
@@ -282,7 +271,6 @@ PyObject* PyObject_CallMethod(PyObject* object, const char* method, const char*
282271
return UPCALL_CEXT_O(_jls_PyObject_CallMethod, native_to_java(object), polyglot_from_string(method, SRC_CS), native_to_java(args));
283272
}
284273

285-
NO_INLINE
286274
PyObject* PyObject_CallMethodObjArgs(PyObject *callable, PyObject *name, ...) {
287275
va_list vargs;
288276
va_start(vargs, name);
@@ -298,7 +286,6 @@ PyObject* PyObject_CallMethodObjArgs(PyObject *callable, PyObject *name, ...) {
298286
return UPCALL_CEXT_O(_jls_PyObject_CallMethod, native_to_java(callable), native_to_java(name), native_to_java(args));
299287
}
300288

301-
NO_INLINE
302289
PyObject* _PyObject_CallMethod_SizeT(PyObject* object, const char* method, const char* fmt, ...) {
303290
PyObject* args;
304291
if (fmt == NULL || fmt[0] == '\0') {
@@ -312,16 +299,10 @@ PyObject* _PyObject_CallMethod_SizeT(PyObject* object, const char* method, const
312299
return UPCALL_CEXT_O(_jls_PyObject_CallMethod, native_to_java(object), polyglot_from_string(method, SRC_CS), native_to_java(args));
313300
}
314301

302+
typedef PyObject *(*fast_call_dict_fun_t)(PyObject *, void *, PyObject *);
303+
UPCALL_TYPED_ID(PyObject_FastCallDict, fast_call_dict_fun_t);
315304
PyObject * _PyObject_FastCallDict(PyObject *func, PyObject *const *args, size_t nargs, PyObject *kwargs) {
316-
PyObject* targs = PyTuple_New(nargs);
317-
Py_ssize_t i;
318-
PyObject* arg;
319-
for(i=0; i < nargs; i++) {
320-
arg = args[i];
321-
Py_XINCREF(arg);
322-
PyTuple_SetItem(targs, i, arg);
323-
}
324-
return polyglot_invoke(PY_TRUFFLE_CEXT, "PyObject_Call", native_to_java(func), native_to_java(targs), native_to_java(kwargs));
305+
return _jls_PyObject_FastCallDict(native_to_java(func), polyglot_from_PyObjectPtr_array(args, nargs), native_to_java(kwargs));
325306
}
326307

327308
PyObject* PyObject_Type(PyObject* obj) {

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/PythonCextBuiltins.java

Lines changed: 102 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3261,9 +3261,9 @@ protected static boolean isMappingOrSequence(Object obj) {
32613261
}
32623262
}
32633263

3264+
// directly called without landing function
32643265
@Builtin(name = "PyObject_Call", parameterNames = {"callee", "args", "kwargs"})
32653266
@GenerateNodeFactory
3266-
@ReportPolymorphism
32673267
abstract static class PyObjectCallNode extends PythonTernaryBuiltinNode {
32683268

32693269
@Specialization
@@ -3288,7 +3288,108 @@ static Object doGeneric(VirtualFrame frame, Object callableObj, Object argsObj,
32883288
return nullToSulongNode.execute(getNativeNullNode.execute());
32893289
}
32903290
}
3291+
}
3292+
3293+
// directly called without landing function
3294+
@Builtin(name = "PyObject_CallFunctionObjArgs", parameterNames = {"callable", "va_list"})
3295+
@GenerateNodeFactory
3296+
abstract static class PyObjectCallFunctionObjArgsNode extends PythonBinaryBuiltinNode {
3297+
3298+
@Specialization(limit = "1")
3299+
static Object doGeneric(VirtualFrame frame, Object callableObj, Object vaList,
3300+
@CachedLibrary("vaList") InteropLibrary argsArrayLib,
3301+
@CachedLibrary(limit = "2") InteropLibrary argLib,
3302+
@Cached AsPythonObjectNode asPythonObjectNode,
3303+
@Cached CallNode callNode,
3304+
@Cached ToNewRefNode toNewRefNode,
3305+
@Cached GetNativeNullNode getNativeNullNode,
3306+
@Cached CExtNodes.ToSulongNode nullToSulongNode,
3307+
@Cached TransformExceptionToNativeNode transformExceptionToNativeNode) {
3308+
if (argsArrayLib.hasArrayElements(vaList)) {
3309+
try {
3310+
try {
3311+
/*
3312+
* Function 'PyObject_CallFunctionObjArgs' expects a va_list that contains
3313+
* just 'PyObject *' and is terminated by 'NULL'. Hence, we allocate an
3314+
* argument array with one element less than the va_list object says (since
3315+
* the last element is expected to be 'NULL'; this is best effort). However,
3316+
* we must also stop at the first 'NULL' element we encounter since a user
3317+
* could pass several 'NULL'.
3318+
*/
3319+
long arraySize = argsArrayLib.getArraySize(vaList);
3320+
Object[] args = new Object[PInt.intValueExact(arraySize) - 1];
3321+
for (int i = 0; i < args.length; i++) {
3322+
try {
3323+
Object object = argsArrayLib.readArrayElement(vaList, i);
3324+
if (argLib.isNull(object)) {
3325+
break;
3326+
}
3327+
args[i] = asPythonObjectNode.execute(object);
3328+
} catch (InvalidArrayIndexException e) {
3329+
throw CompilerDirectives.shouldNotReachHere();
3330+
}
3331+
}
3332+
Object callable = asPythonObjectNode.execute(callableObj);
3333+
return toNewRefNode.execute(callNode.execute(frame, callable, args, PKeyword.EMPTY_KEYWORDS));
3334+
} catch (UnsupportedMessageException | OverflowException e) {
3335+
// I think we can just assume that there won't be more than
3336+
// Integer.MAX_VALUE arguments.
3337+
throw CompilerDirectives.shouldNotReachHere();
3338+
}
3339+
} catch (PException e) {
3340+
// transformExceptionToNativeNode acts as a branch profile
3341+
transformExceptionToNativeNode.execute(frame, e);
3342+
return nullToSulongNode.execute(getNativeNullNode.execute());
3343+
}
3344+
}
3345+
throw CompilerDirectives.shouldNotReachHere();
3346+
}
3347+
}
3348+
3349+
// directly called without landing function
3350+
@Builtin(name = "PyObject_FastCallDict", parameterNames = {"callable", "argsArray", "kwargs"})
3351+
@GenerateNodeFactory
3352+
abstract static class PyObjectFastCallDictNode extends PythonTernaryBuiltinNode {
32913353

3354+
@Specialization(limit = "1")
3355+
static Object doGeneric(VirtualFrame frame, Object callableObj, Object argsArray, Object kwargsObj,
3356+
@CachedLibrary("argsArray") InteropLibrary argsArrayLib,
3357+
@Cached AsPythonObjectNode asPythonObjectNode,
3358+
@Cached CastKwargsNode castKwargsNode,
3359+
@Cached CallNode callNode,
3360+
@Cached ToNewRefNode toNewRefNode,
3361+
@Cached GetNativeNullNode getNativeNullNode,
3362+
@Cached CExtNodes.ToSulongNode nullToSulongNode,
3363+
@Cached TransformExceptionToNativeNode transformExceptionToNativeNode) {
3364+
if (argsArrayLib.hasArrayElements(argsArray)) {
3365+
try {
3366+
try {
3367+
// consume all arguments of the va_list
3368+
long arraySize = argsArrayLib.getArraySize(argsArray);
3369+
Object[] args = new Object[PInt.intValueExact(arraySize)];
3370+
for (int i = 0; i < args.length; i++) {
3371+
try {
3372+
args[i] = asPythonObjectNode.execute(argsArrayLib.readArrayElement(argsArray, i));
3373+
} catch (InvalidArrayIndexException e) {
3374+
throw CompilerDirectives.shouldNotReachHere();
3375+
}
3376+
}
3377+
Object callable = asPythonObjectNode.execute(callableObj);
3378+
PKeyword[] keywords = castKwargsNode.execute(kwargsObj);
3379+
return toNewRefNode.execute(callNode.execute(frame, callable, args, keywords));
3380+
} catch (UnsupportedMessageException | OverflowException e) {
3381+
// I think we can just assume that there won't be more than
3382+
// Integer.MAX_VALUE arguments.
3383+
throw CompilerDirectives.shouldNotReachHere();
3384+
}
3385+
} catch (PException e) {
3386+
// transformExceptionToNativeNode acts as a branch profile
3387+
transformExceptionToNativeNode.execute(frame, e);
3388+
return nullToSulongNode.execute(getNativeNullNode.execute());
3389+
}
3390+
}
3391+
throw CompilerDirectives.shouldNotReachHere();
3392+
}
32923393
}
32933394

32943395
@ReportPolymorphism

0 commit comments

Comments
 (0)