From 26c277d43fdab881b42f94f7ae43f572ee53ddd1 Mon Sep 17 00:00:00 2001 From: Florian Angerer Date: Tue, 21 Jul 2026 22:04:16 +0200 Subject: [PATCH 1/3] Implement PyTuple_Size purely in native --- .../src/tupleobject.c | 14 +++++++++++++- .../modules/cext/PythonCextTupleBuiltins.java | 15 --------------- .../builtins/objects/cext/capi/CApiFunction.java | 1 + 3 files changed, 14 insertions(+), 16 deletions(-) diff --git a/graalpython/com.oracle.graal.python.cext/src/tupleobject.c b/graalpython/com.oracle.graal.python.cext/src/tupleobject.c index 5b4730b249..9ed10d506e 100644 --- a/graalpython/com.oracle.graal.python.cext/src/tupleobject.c +++ b/graalpython/com.oracle.graal.python.cext/src/tupleobject.c @@ -92,6 +92,7 @@ PyTuple_New(Py_ssize_t size) _PyObject_GC_TRACK(op); return (PyObject *) op; } +#endif // GraalPy change Py_ssize_t PyTuple_Size(PyObject *op) @@ -100,10 +101,21 @@ PyTuple_Size(PyObject *op) PyErr_BadInternalCall(); return -1; } +#if 0 // GraalPy change else return Py_SIZE(op); -} +#else // GraalPy change + /* + * CPython uses Py_SIZE here but our Py_SIZE would redundantly check the + * type again. Since performance is really crucial for tuples, we inline + * the minimal code here. + */ + if (points_to_py_handle_space(op)) { + return ((GraalPyVarObject *) pointer_to_stub(op))->ob_size; + } + return _PyVarObject_CAST(op)->ob_size; #endif // GraalPy change +} /* Allocate an uninitialized tuple object. Before making it public, following steps must be done: diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextTupleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextTupleBuiltins.java index b210f92929..c820355956 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextTupleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextTupleBuiltins.java @@ -56,7 +56,6 @@ import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiBinaryBuiltinNode; import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiBuiltin; import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiTernaryBuiltinNode; -import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiUnaryBuiltinNode; import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject; import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes.EnsurePythonObjectNode; @@ -70,7 +69,6 @@ import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes.SetLenNode; import com.oracle.graal.python.builtins.objects.tuple.PTuple; import com.oracle.graal.python.lib.PySliceNew; -import com.oracle.graal.python.lib.PyTupleSizeNode; import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.builtins.TupleNodes.GetTupleStorage; @@ -157,19 +155,6 @@ private static int checkIndex(Node inliningTarget, long key, SequenceStorage seq } } - /* - * The best attempt at moving this to pure C regressed by about 1.09x by median time. - */ - @CApiBuiltin(ret = Py_ssize_t, args = {PyObject}, call = Direct) - abstract static class PyTuple_Size extends CApiUnaryBuiltinNode { - @Specialization - public static long size(Object tuple, - @Bind Node inliningTarget, - @Cached PyTupleSizeNode pyTupleSizeNode) { - return pyTupleSizeNode.execute(inliningTarget, tuple); - } - } - @CApiBuiltin(ret = PyObjectTransfer, args = {PyObject, Py_ssize_t, Py_ssize_t}, call = Direct) abstract static class PyTuple_GetSlice extends CApiTernaryBuiltinNode { @Specialization diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CApiFunction.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CApiFunction.java index c05ec2e0c4..4a1ab42a4d 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CApiFunction.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CApiFunction.java @@ -514,6 +514,7 @@ public final class CApiFunction { @CApiBuiltin(name = "PyTuple_Pack", ret = PyObject, args = {Py_ssize_t, VARARGS}, call = CImpl) @CApiBuiltin(name = "PyTuple_GetItem", ret = PyObjectBorrowed, args = {PyObject, Py_ssize_t}, call = CImpl) @CApiBuiltin(name = "PyTuple_SetItem", ret = Int, args = {PyObject, Py_ssize_t, PyObjectTransfer}, call = CImpl) + @CApiBuiltin(name = "PyTuple_Size", ret = Py_ssize_t, args = {PyObject}, call = CImpl) @CApiBuiltin(name = "PyType_FromModuleAndSpec", ret = PyObject, args = {PyObject, PY_TYPE_SPEC, PyObject}, call = CImpl) @CApiBuiltin(name = "PyType_FromSpec", ret = PyObject, args = {PY_TYPE_SPEC}, call = CImpl) @CApiBuiltin(name = "PyType_FromSpecWithBases", ret = PyObject, args = {PY_TYPE_SPEC, PyObject}, call = CImpl) From dec5d9503945d8272dada33284a9e400d0866a7b Mon Sep 17 00:00:00 2001 From: Florian Angerer Date: Tue, 21 Jul 2026 22:04:25 +0200 Subject: [PATCH 2/3] Avoid nodes for assertions --- .../builtins/modules/cext/PythonCextSlotBuiltins.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextSlotBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextSlotBuiltins.java index 249648cc6a..7f30dc2894 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextSlotBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextSlotBuiltins.java @@ -72,13 +72,13 @@ import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.getter; import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.setter; import static com.oracle.graal.python.builtins.objects.cext.capi.transitions.ArgDescriptor.vectorcallfunc; -import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.NULLPTR; import static com.oracle.graal.python.nodes.HiddenAttr.METHOD_DEF_PTR; import static com.oracle.graal.python.nodes.HiddenAttr.PROMOTED_START; import static com.oracle.graal.python.nodes.HiddenAttr.PROMOTED_STEP; import static com.oracle.graal.python.nodes.HiddenAttr.PROMOTED_STOP; import static com.oracle.graal.python.nodes.SpecialAttributeNames.T___MODULE__; import static com.oracle.graal.python.runtime.PythonContext.NATIVE_NULL; +import static com.oracle.graal.python.runtime.nativeaccess.NativeMemory.NULLPTR; import com.oracle.graal.python.builtins.PythonBuiltinClassType; import com.oracle.graal.python.builtins.modules.cext.PythonCextBuiltins.CApiBinaryBuiltinNode; @@ -108,11 +108,11 @@ import com.oracle.graal.python.builtins.objects.set.PBaseSet; import com.oracle.graal.python.builtins.objects.slice.PSlice; import com.oracle.graal.python.builtins.objects.tuple.PTuple; -import com.oracle.graal.python.builtins.objects.type.TypeNodes; import com.oracle.graal.python.lib.PyObjectLookupAttr; import com.oracle.graal.python.lib.PyObjectSetAttr; import com.oracle.graal.python.nodes.HiddenAttr; import com.oracle.graal.python.nodes.attributes.GetFixedAttributeNode; +import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinObjectExactProfile; import com.oracle.graal.python.nodes.object.GetClassNode; import com.oracle.graal.python.runtime.PythonContext; import com.oracle.graal.python.runtime.sequence.storage.NativeByteSequenceStorage; @@ -571,11 +571,9 @@ abstract static class GraalPyPrivate_Get_PyVarObject_ob_size extends CApiUnaryBu @Specialization static long get(Object object, @Bind Node inliningTarget, - @Cached GetClassNode getClassNode, - @Cached TypeNodes.IsSameTypeNode isSameTypeNode, @Cached ObSizeNode obSizeNode) { - assert !isSameTypeNode.execute(inliningTarget, getClassNode.execute(inliningTarget, object), PythonBuiltinClassType.PInt); - assert !isSameTypeNode.execute(inliningTarget, getClassNode.execute(inliningTarget, object), PythonBuiltinClassType.Boolean); + assert !IsBuiltinObjectExactProfile.profileObjectUncached(object, PythonBuiltinClassType.PInt); + assert !IsBuiltinObjectExactProfile.profileObjectUncached(object, PythonBuiltinClassType.Boolean); return obSizeNode.execute(inliningTarget, object); } } From 3470866319a7fdd445de2fe4c8dabb2e1fd362cf Mon Sep 17 00:00:00 2001 From: Florian Angerer Date: Tue, 21 Jul 2026 22:04:49 +0200 Subject: [PATCH 3/3] Improve PyTupleSizeNode for native tuples --- .../graal/python/lib/PyTupleSizeNode.java | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyTupleSizeNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyTupleSizeNode.java index 31b1997cb4..742d370ddd 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyTupleSizeNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyTupleSizeNode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -45,27 +45,26 @@ import static com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess.readLongField; import static com.oracle.graal.python.nodes.ErrorMessages.BAD_ARG_TO_INTERNAL_FUNC_S; -import com.oracle.graal.python.builtins.PythonBuiltinClassType; import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject; import com.oracle.graal.python.builtins.objects.tuple.PTuple; +import com.oracle.graal.python.nodes.PGuards; import com.oracle.graal.python.nodes.PNodeWithContext; import com.oracle.graal.python.nodes.PRaiseNode; -import com.oracle.graal.python.nodes.classes.IsSubtypeNode; -import com.oracle.graal.python.nodes.object.GetClassNode; import com.oracle.graal.python.util.PythonUtils; import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff; import com.oracle.truffle.api.dsl.Bind; -import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.GenerateCached; import com.oracle.truffle.api.dsl.GenerateInline; import com.oracle.truffle.api.dsl.GenerateUncached; +import com.oracle.truffle.api.dsl.ImportStatic; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.nodes.Node; @GenerateUncached @GenerateInline @GenerateCached(false) +@ImportStatic(PGuards.class) public abstract class PyTupleSizeNode extends PNodeWithContext { public static int executeUncached(Object tuple) { return PyTupleSizeNodeGen.getUncached().execute(null, tuple); @@ -78,11 +77,9 @@ static int size(PTuple tuple) { return tuple.getSequenceStorage().length(); } - @Specialization(guards = "isTupleSubtype(tuple, inliningTarget, getClassNode, isSubtypeNode)", limit = "1") + @Specialization(guards = "isNativeTuple(tuple)") @InliningCutoff - static int sizeNative(Node inliningTarget, PythonAbstractNativeObject tuple, - @SuppressWarnings("unused") @Cached GetClassNode getClassNode, - @SuppressWarnings("unused") @Cached(inline = false) IsSubtypeNode isSubtypeNode) { + static int sizeNative(PythonAbstractNativeObject tuple) { return PythonUtils.toIntError(readLongField(tuple.getPtr(), PyVarObject__ob_size)); } @@ -93,8 +90,4 @@ static int size(Object obj, @Bind Node inliningTarget) { throw PRaiseNode.raiseStatic(inliningTarget, SystemError, BAD_ARG_TO_INTERNAL_FUNC_S, "PyTuple_Size"); } - - protected boolean isTupleSubtype(Object obj, Node inliningTarget, GetClassNode getClassNode, IsSubtypeNode isSubtypeNode) { - return isSubtypeNode.execute(getClassNode.execute(inliningTarget, obj), PythonBuiltinClassType.PTuple); - } }