From 444f6934b46f87d193b5bad88a7d812d470958d1 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Mon, 7 Jul 2025 18:53:05 +0200 Subject: [PATCH 01/38] Replace GetAttributeNode by GetFixedAttributeNode * GetAttributeNode was just an extra unnecessary wrapper around GetFixedAttributeNode. --- .../modules/AsyncioModuleBuiltins.java | 6 +- .../builtins/modules/BuiltinFunctions.java | 4 +- .../modules/PolyglotModuleBuiltins.java | 4 +- .../builtins/modules/SREModuleBuiltins.java | 14 +- .../modules/cext/PythonCextSlotBuiltins.java | 2 +- .../modules/ctypes/CDataBuiltins.java | 6 +- .../modules/ctypes/CDataTypeBuiltins.java | 4 +- .../modules/ctypes/CtypesModuleBuiltins.java | 4 +- .../modules/ctypes/StgDictBuiltins.java | 4 +- .../modules/json/JSONScannerBuiltins.java | 14 +- .../getsetdescriptor/DescriptorBuiltins.java | 2 +- .../method/AbstractBuiltinMethodBuiltins.java | 16 +- .../method/AbstractMethodBuiltins.java | 4 +- .../BuiltinFunctionOrMethodBuiltins.java | 14 +- .../objects/method/MethodWrapperBuiltins.java | 10 +- .../builtins/objects/type/TypeBuiltins.java | 2 +- .../builtins/objects/type/TypeNodes.java | 4 +- .../graal/python/lib/PyObjectGetAttr.java | 2 +- .../nodes/attributes/GetAttributeNode.java | 160 ------------------ .../attributes/GetFixedAttributeNode.java | 141 +++++++++++++++ .../bytecode_dsl/PBytecodeDSLRootNode.java | 2 +- 21 files changed, 200 insertions(+), 219 deletions(-) delete mode 100644 graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/GetAttributeNode.java create mode 100644 graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/GetFixedAttributeNode.java diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/AsyncioModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/AsyncioModuleBuiltins.java index eca264cca0..f7a76378ac 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/AsyncioModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/AsyncioModuleBuiltins.java @@ -72,7 +72,7 @@ import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PGuards; import com.oracle.graal.python.nodes.PRaiseNode; -import com.oracle.graal.python.nodes.attributes.GetAttributeNode; +import com.oracle.graal.python.nodes.attributes.GetFixedAttributeNode; import com.oracle.graal.python.nodes.call.CallNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.PythonBuiltinNode; @@ -178,8 +178,8 @@ static Object getCurrentLoop(VirtualFrame frame, Object ignored, @Cached CallNode callGetPolicy, @Cached CallNode callGetLoop, @Cached AbstractImportNode.ImportName importName, - @Cached(parameters = "T_GET_EVENT_LOOP") GetAttributeNode.GetFixedAttributeNode getGetLoop, - @Cached(parameters = "T_GET_EVENT_LOOP_POLICY") GetAttributeNode.GetFixedAttributeNode getGetLoopPolicy) { + @Cached(parameters = "T_GET_EVENT_LOOP") GetFixedAttributeNode getGetLoop, + @Cached(parameters = "T_GET_EVENT_LOOP_POLICY") GetFixedAttributeNode getGetLoopPolicy) { Object eventLoop = context.getThreadState(context.getLanguage(inliningTarget)).getRunningEventLoop(); if (eventLoop == null) { Object asyncio = importName.execute(frame, context, context.getBuiltins(), T_ASYNCIO_EVENTS, PNone.NONE, PythonUtils.EMPTY_TRUFFLESTRING_ARRAY, 0); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/BuiltinFunctions.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/BuiltinFunctions.java index 1e41bb73cd..35ee2532f2 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/BuiltinFunctions.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/BuiltinFunctions.java @@ -208,7 +208,7 @@ import com.oracle.graal.python.nodes.SpecialMethodNames; import com.oracle.graal.python.nodes.StringLiterals; import com.oracle.graal.python.nodes.argument.ReadArgumentNode; -import com.oracle.graal.python.nodes.attributes.GetAttributeNode; +import com.oracle.graal.python.nodes.attributes.GetFixedAttributeNode; import com.oracle.graal.python.nodes.attributes.ReadAttributeFromObjectNode; import com.oracle.graal.python.nodes.builtins.ListNodes; import com.oracle.graal.python.nodes.builtins.ListNodes.ConstructListNode; @@ -2435,7 +2435,7 @@ protected Object doItNonFunction(VirtualFrame frame, Object function, Object[] a @Cached CastToTruffleStringNode castToTruffleStringNode, @Cached("createFor($node)") IndirectCallData indirectCallData, @Cached CalculateMetaclassNode calculateMetaClass, - @Cached("create(T___PREPARE__)") GetAttributeNode getPrepare, + @Cached("create(T___PREPARE__)") GetFixedAttributeNode getPrepare, @Cached PyMappingCheckNode pyMappingCheckNode, @Cached CallNode callPrep, @Cached CallNode callType, diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/PolyglotModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/PolyglotModuleBuiltins.java index 86c10ebc6f..b69624443c 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/PolyglotModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/PolyglotModuleBuiltins.java @@ -102,7 +102,7 @@ import com.oracle.graal.python.nodes.HiddenAttr; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.SpecialAttributeNames; -import com.oracle.graal.python.nodes.attributes.GetAttributeNode; +import com.oracle.graal.python.nodes.attributes.GetFixedAttributeNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.PythonBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.PythonClinicBuiltinNode; @@ -352,7 +352,7 @@ Object exportSymbol(PBuiltinFunction fun, @SuppressWarnings("unused") PNone name @Specialization(guards = "isModuleMethod(fun)") static Object exportSymbol(VirtualFrame frame, Object fun, @SuppressWarnings("unused") PNone name, @Bind Node inliningTarget, - @Cached("create(T___NAME__)") GetAttributeNode.GetFixedAttributeNode getNameAttributeNode, + @Cached("create(T___NAME__)") GetFixedAttributeNode getNameAttributeNode, @Cached CastToJavaStringNode castToStringNode, @Cached PRaiseNode raiseNode) { Object attrNameValue = getNameAttributeNode.executeObject(frame, fun); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SREModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SREModuleBuiltins.java index f81f030085..b9c555fce2 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SREModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SREModuleBuiltins.java @@ -79,7 +79,7 @@ import com.oracle.graal.python.nodes.HiddenAttr; import com.oracle.graal.python.nodes.PNodeWithContext; import com.oracle.graal.python.nodes.PRaiseNode; -import com.oracle.graal.python.nodes.attributes.GetAttributeNode; +import com.oracle.graal.python.nodes.attributes.GetFixedAttributeNode; import com.oracle.graal.python.nodes.attributes.ReadAttributeFromObjectNode; import com.oracle.graal.python.nodes.call.CallNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; @@ -643,7 +643,7 @@ abstract static class TRegexSearch extends PythonSenaryBuiltinNode { protected static final TruffleString T__PATTERN__FALLBACK_COMPILE = tsLiteral("_Pattern__fallback_compile"); @Child private HiddenAttr.ReadNode readCacheNode = HiddenAttr.ReadNode.create(); - @Child private GetAttributeNode getFallbackCompileNode; + @Child private GetFixedAttributeNode getFallbackCompileNode; @Child private CallNode callFallbackCompileNode; @Child private CallNode callFallbackMethodNode; @Child private SliceNodes.CreateSliceNode createSliceNode; @@ -666,7 +666,7 @@ protected Object doCached(VirtualFrame frame, PythonObject pattern, Object input @Cached @Shared PyNumberAsSizeNode asSizeNode, @Cached @Shared PyObjectSizeNode lengthNode, @CachedLibrary(limit = "1") @Shared InteropLibrary libCompiledRegex, - @Cached("create(method.getMethodName())") GetAttributeNode getFallbackMethodNode, + @Cached("create(method.getMethodName())") GetFixedAttributeNode getFallbackMethodNode, @Cached @Shared TRegexCallExec tRegexCallExec, @Cached @Shared CreateMatchFromTRegexResultNode createMatchFromTRegexResultNode) { int pos = asSizeNode.executeExact(frame, inliningTarget, indexNode.execute(frame, inliningTarget, posArg)); @@ -716,7 +716,7 @@ protected Object doCachedRegex(VirtualFrame frame, PythonObject pattern, Object @Cached @Shared PyNumberAsSizeNode asSizeNode, @Cached @Shared PyObjectSizeNode lengthNode, @CachedLibrary(limit = "1") @Shared InteropLibrary libCompiledRegex, - @Cached("create(method.getMethodName())") GetAttributeNode getFallbackMethodNode, + @Cached("create(method.getMethodName())") GetFixedAttributeNode getFallbackMethodNode, @Cached @Shared TRegexCallExec tRegexCallExec, @Cached @Shared CreateMatchFromTRegexResultNode createMatchFromTRegexResultNode) { return doCached(frame, pattern, input, posArg, endPosArg, method, mustAdvance, inliningTarget, pattern, cachedMethod, mustAdvance, tRegexCompileNode, tRegexCache, @@ -738,7 +738,7 @@ protected Object doDynamic(VirtualFrame frame, PythonObject pattern, Object inpu @Cached @Shared InlinedConditionProfile fallbackProfile, @Cached @Shared InlinedConditionProfile truncatingInputProfile, @CachedLibrary(limit = "1") @Shared InteropLibrary libCompiledRegex, - @Cached("create(method.getMethodName())") GetAttributeNode getFallbackMethodNode, + @Cached("create(method.getMethodName())") GetFixedAttributeNode getFallbackMethodNode, @Cached @Shared TRegexCallExec tRegexCallExec, @Cached @Shared CreateMatchFromTRegexResultNode createMatchFromTRegexResultNode) { TRegexCache tRegexCache = getTRegexCache(pattern); @@ -752,10 +752,10 @@ protected TRegexCache getTRegexCache(PythonObject pattern) { return (TRegexCache) readCacheNode.executeCached(pattern, HiddenAttr.TREGEX_CACHE, null); } - private GetAttributeNode getGetFallbackCompileNode() { + private GetFixedAttributeNode getGetFallbackCompileNode() { if (getFallbackCompileNode == null) { CompilerDirectives.transferToInterpreterAndInvalidate(); - getFallbackCompileNode = insert(GetAttributeNode.create(T__PATTERN__FALLBACK_COMPILE)); + getFallbackCompileNode = insert(GetFixedAttributeNode.create(T__PATTERN__FALLBACK_COMPILE)); } return getFallbackCompileNode; } 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 c23f69cc92..d1fb62ea24 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 @@ -120,7 +120,7 @@ import com.oracle.graal.python.lib.PyObjectSetAttr; import com.oracle.graal.python.nodes.HiddenAttr; import com.oracle.graal.python.nodes.PGuards; -import com.oracle.graal.python.nodes.attributes.GetAttributeNode.GetFixedAttributeNode; +import com.oracle.graal.python.nodes.attributes.GetFixedAttributeNode; import com.oracle.graal.python.nodes.object.GetClassNode; import com.oracle.graal.python.runtime.PythonContext; import com.oracle.graal.python.runtime.sequence.PSequence; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ctypes/CDataBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ctypes/CDataBuiltins.java index 9c051a144d..6c2b562da2 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ctypes/CDataBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ctypes/CDataBuiltins.java @@ -81,7 +81,7 @@ import com.oracle.graal.python.nodes.PGuards; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.SpecialAttributeNames; -import com.oracle.graal.python.nodes.attributes.GetAttributeNode; +import com.oracle.graal.python.nodes.attributes.GetFixedAttributeNode; import com.oracle.graal.python.nodes.attributes.ReadAttributeFromPythonObjectNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.builtins.PythonBinaryBuiltinNode; @@ -175,7 +175,7 @@ static Object reduce(VirtualFrame frame, CDataObject self, @Bind Node inliningTarget, @Bind PythonLanguage language, @Cached PyObjectStgDictNode pyObjectStgDictNode, - @Cached("create(T___DICT__)") GetAttributeNode getAttributeNode, + @Cached("create(T___DICT__)") GetFixedAttributeNode getAttributeNode, @Cached ReadAttributeFromPythonObjectNode readAttrNode, @Cached PointerNodes.ReadBytesNode readBytesNode, @Cached GetClassNode getClassNode, @@ -209,7 +209,7 @@ abstract static class SetStateNode extends PythonBinaryBuiltinNode { static Object PyCData_setstate(VirtualFrame frame, CDataObject self, PTuple args, @Bind Node inliningTarget, @Cached SequenceStorageNodes.GetInternalObjectArrayNode getArray, - @Cached("create(T___DICT__)") GetAttributeNode getAttributeNode, + @Cached("create(T___DICT__)") GetFixedAttributeNode getAttributeNode, @Cached PyNumberAsSizeNode asSizeNode, @Cached HashingStorageAddAllToOther addAllToOtherNode, @Cached PRaiseNode raiseNode) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ctypes/CDataTypeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ctypes/CDataTypeBuiltins.java index 0ed57e8c71..ba33026bdc 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ctypes/CDataTypeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ctypes/CDataTypeBuiltins.java @@ -93,7 +93,7 @@ import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PGuards; import com.oracle.graal.python.nodes.PRaiseNode; -import com.oracle.graal.python.nodes.attributes.GetAttributeNode; +import com.oracle.graal.python.nodes.attributes.GetFixedAttributeNode; import com.oracle.graal.python.nodes.call.CallNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.builtins.PythonBinaryBuiltinNode; @@ -327,7 +327,7 @@ protected ArgumentClinicProvider getArgumentClinic() { static Object CDataType_in_dll(VirtualFrame frame, Object type, Object dll, TruffleString name, @Bind Node inliningTarget, @Cached PyLongCheckNode longCheckNode, - @Cached("create(T__HANDLE)") GetAttributeNode getAttributeNode, + @Cached("create(T__HANDLE)") GetFixedAttributeNode getAttributeNode, @Cached PyCDataAtAddress atAddress, @Cached AuditNode auditNode, @Cached PointerNodes.PointerFromLongNode pointerFromLongNode, diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ctypes/CtypesModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ctypes/CtypesModuleBuiltins.java index e0a5577eab..afa520de22 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ctypes/CtypesModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ctypes/CtypesModuleBuiltins.java @@ -144,7 +144,7 @@ import com.oracle.graal.python.nodes.PNodeWithContext; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.StringLiterals; -import com.oracle.graal.python.nodes.attributes.GetAttributeNode; +import com.oracle.graal.python.nodes.attributes.GetFixedAttributeNode; import com.oracle.graal.python.nodes.attributes.ReadAttributeFromObjectNode; import com.oracle.graal.python.nodes.attributes.WriteAttributeToPythonObjectNode; import com.oracle.graal.python.nodes.call.CallNode; @@ -546,7 +546,7 @@ Object unpickle(VirtualFrame frame, Object typ, PTuple state, // This shouldn't call the slot directly to make sure the check in the // wrapper runs @Cached("create(T___NEW__)") LookupAndCallUnaryNode lookupAndCallUnaryNode, - @Cached("create(T___SETSTATE__)") GetAttributeNode setStateAttr) { + @Cached("create(T___SETSTATE__)") GetFixedAttributeNode setStateAttr) { Object obj = lookupAndCallUnaryNode.executeObject(frame, typ); Object meth = setStateAttr.executeObject(frame, obj); callNode.execute(frame, meth, state); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ctypes/StgDictBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ctypes/StgDictBuiltins.java index 41bdf076e4..7577d4e8d8 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ctypes/StgDictBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ctypes/StgDictBuiltins.java @@ -82,7 +82,7 @@ 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.attributes.GetAttributeNode; +import com.oracle.graal.python.nodes.attributes.GetFixedAttributeNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.PythonVarargsBuiltinNode; @@ -184,7 +184,7 @@ static void MakeFields(VirtualFrame frame, Object type, CFieldObject descr, int @Cached PyObjectSizeNode sizeNode, @Cached PyObjectGetItem getItemNode, @Cached GetInternalObjectArrayNode getArray, - @Cached("create(T__FIELDS_)") GetAttributeNode getAttrString, + @Cached("create(T__FIELDS_)") GetFixedAttributeNode getAttrString, @Cached MakeFieldsNode recursiveNode, @Cached("createFor($node)") IndirectCallData indirectCallData, @Cached PRaiseNode raiseNode) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/json/JSONScannerBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/json/JSONScannerBuiltins.java index 940948248d..9635513880 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/json/JSONScannerBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/json/JSONScannerBuiltins.java @@ -36,7 +36,7 @@ import com.oracle.graal.python.lib.PyObjectLookupAttr; import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PRaiseNode; -import com.oracle.graal.python.nodes.attributes.GetAttributeNode; +import com.oracle.graal.python.nodes.attributes.GetFixedAttributeNode; import com.oracle.graal.python.nodes.call.CallNode; import com.oracle.graal.python.nodes.call.special.CallUnaryMethodNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; @@ -79,12 +79,12 @@ protected List> getNodeFa @GenerateNodeFactory public abstract static class MakeScanner extends PythonBinaryBuiltinNode { - @Child private GetAttributeNode.GetFixedAttributeNode getStrict = GetAttributeNode.GetFixedAttributeNode.create(T_STRICT); - @Child private GetAttributeNode.GetFixedAttributeNode getObjectHook = GetAttributeNode.GetFixedAttributeNode.create(tsLiteral("object_hook")); - @Child private GetAttributeNode.GetFixedAttributeNode getObjectPairsHook = GetAttributeNode.GetFixedAttributeNode.create(tsLiteral("object_pairs_hook")); - @Child private GetAttributeNode.GetFixedAttributeNode getParseFloat = GetAttributeNode.GetFixedAttributeNode.create(tsLiteral("parse_float")); - @Child private GetAttributeNode.GetFixedAttributeNode getParseInt = GetAttributeNode.GetFixedAttributeNode.create(tsLiteral("parse_int")); - @Child private GetAttributeNode.GetFixedAttributeNode getParseConstant = GetAttributeNode.GetFixedAttributeNode.create(tsLiteral("parse_constant")); + @Child private GetFixedAttributeNode getStrict = GetFixedAttributeNode.create(T_STRICT); + @Child private GetFixedAttributeNode getObjectHook = GetFixedAttributeNode.create(tsLiteral("object_hook")); + @Child private GetFixedAttributeNode getObjectPairsHook = GetFixedAttributeNode.create(tsLiteral("object_pairs_hook")); + @Child private GetFixedAttributeNode getParseFloat = GetFixedAttributeNode.create(tsLiteral("parse_float")); + @Child private GetFixedAttributeNode getParseInt = GetFixedAttributeNode.create(tsLiteral("parse_int")); + @Child private GetFixedAttributeNode getParseConstant = GetFixedAttributeNode.create(tsLiteral("parse_constant")); @Specialization public PJSONScanner doNew(VirtualFrame frame, Object cls, Object context, diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/getsetdescriptor/DescriptorBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/getsetdescriptor/DescriptorBuiltins.java index dbc4c2b045..ba6bb31d46 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/getsetdescriptor/DescriptorBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/getsetdescriptor/DescriptorBuiltins.java @@ -57,7 +57,7 @@ import com.oracle.graal.python.builtins.objects.type.TypeNodes.GetIndexedSlotsCountNode; import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PRaiseNode; -import com.oracle.graal.python.nodes.attributes.GetAttributeNode.GetFixedAttributeNode; +import com.oracle.graal.python.nodes.attributes.GetFixedAttributeNode; import com.oracle.graal.python.nodes.call.special.CallBinaryMethodNode; import com.oracle.graal.python.nodes.call.special.CallUnaryMethodNode; import com.oracle.graal.python.nodes.classes.IsSubtypeNode; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/AbstractBuiltinMethodBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/AbstractBuiltinMethodBuiltins.java index 9a7b27d146..e15569fcba 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/AbstractBuiltinMethodBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/AbstractBuiltinMethodBuiltins.java @@ -45,7 +45,7 @@ import com.oracle.graal.python.builtins.objects.type.TypeNodes; import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PRaiseNode; -import com.oracle.graal.python.nodes.attributes.GetAttributeNode; +import com.oracle.graal.python.nodes.attributes.GetFixedAttributeNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.builtins.PythonBinaryBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; @@ -92,13 +92,13 @@ Object getTextSignature(VirtualFrame frame, PMethod self, Object value) { public abstract static class MethodName extends PythonUnaryBuiltinNode { @Specialization static Object getName(VirtualFrame frame, PBuiltinMethod method, - @Shared @Cached("create(T___NAME__)") GetAttributeNode getNameAttrNode) { + @Shared @Cached("create(T___NAME__)") GetFixedAttributeNode getNameAttrNode) { return getNameAttrNode.executeObject(frame, method.getFunction()); } @Specialization static Object getName(VirtualFrame frame, PMethod method, - @Shared @Cached("create(T___NAME__)") GetAttributeNode getNameAttrNode) { + @Shared @Cached("create(T___NAME__)") GetFixedAttributeNode getNameAttrNode) { return getNameAttrNode.executeObject(frame, method.getFunction()); } } @@ -109,8 +109,8 @@ public abstract static class MethodQualName extends PythonUnaryBuiltinNode { @Specialization static TruffleString getQualName(VirtualFrame frame, PMethod method, @Bind Node inliningTarget, - @Shared @Cached("create(T___NAME__)") GetAttributeNode getNameAttrNode, - @Shared @Cached("create(T___QUALNAME__)") GetAttributeNode getQualNameAttrNode, + @Shared @Cached("create(T___NAME__)") GetFixedAttributeNode getNameAttrNode, + @Shared @Cached("create(T___QUALNAME__)") GetFixedAttributeNode getQualNameAttrNode, @Shared @Cached TypeNodes.IsTypeNode isTypeNode, @Shared @Cached CastToTruffleStringNode castToStringNode, @Shared @Cached InlinedConditionProfile isGlobalProfile, @@ -124,8 +124,8 @@ static TruffleString getQualName(VirtualFrame frame, PMethod method, @Specialization static TruffleString getQualName(VirtualFrame frame, PBuiltinMethod method, @Bind Node inliningTarget, - @Shared @Cached("create(T___NAME__)") GetAttributeNode getNameAttrNode, - @Shared @Cached("create(T___QUALNAME__)") GetAttributeNode getQualNameAttrNode, + @Shared @Cached("create(T___NAME__)") GetFixedAttributeNode getNameAttrNode, + @Shared @Cached("create(T___QUALNAME__)") GetFixedAttributeNode getQualNameAttrNode, @Shared @Cached TypeNodes.IsTypeNode isTypeNode, @Shared @Cached CastToTruffleStringNode castToStringNode, @Shared @Cached InlinedConditionProfile isGlobalProfile, @@ -136,7 +136,7 @@ static TruffleString getQualName(VirtualFrame frame, PBuiltinMethod method, simpleTruffleStringFormatNode, raiseNode); } - private static TruffleString makeQualname(VirtualFrame frame, Node inliningTarget, Object method, Object self, GetAttributeNode getQualNameAttrNode, GetAttributeNode getNameAttrNode, + private static TruffleString makeQualname(VirtualFrame frame, Node inliningTarget, Object method, Object self, GetFixedAttributeNode getQualNameAttrNode, GetFixedAttributeNode getNameAttrNode, CastToTruffleStringNode castToStringNode, GetClassNode getClassNode, TypeNodes.IsTypeNode isTypeNode, InlinedConditionProfile isGlobalProfile, SimpleTruffleStringFormatNode simpleTruffleStringFormatNode, PRaiseNode raiseNode) { TruffleString methodName; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/AbstractMethodBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/AbstractMethodBuiltins.java index 77cc55d972..9bd29adcf3 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/AbstractMethodBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/AbstractMethodBuiltins.java @@ -67,7 +67,7 @@ import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PGuards; import com.oracle.graal.python.nodes.PRaiseNode; -import com.oracle.graal.python.nodes.attributes.GetAttributeNode; +import com.oracle.graal.python.nodes.attributes.GetFixedAttributeNode; import com.oracle.graal.python.nodes.attributes.ReadAttributeFromPythonObjectNode; import com.oracle.graal.python.nodes.attributes.WriteAttributeToPythonObjectNode; import com.oracle.graal.python.nodes.call.CallDispatchers; @@ -256,7 +256,7 @@ static Object getModule(PBuiltinMethod self, Object value, @Specialization(guards = "isNoValue(value)") static Object getModule(VirtualFrame frame, PMethod self, @SuppressWarnings("unused") Object value, - @Cached("create(T___MODULE__)") GetAttributeNode getAttributeNode) { + @Cached("create(T___MODULE__)") GetFixedAttributeNode getAttributeNode) { return getAttributeNode.executeObject(frame, self.getFunction()); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/BuiltinFunctionOrMethodBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/BuiltinFunctionOrMethodBuiltins.java index 58ffd303c0..1908950abf 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/BuiltinFunctionOrMethodBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/BuiltinFunctionOrMethodBuiltins.java @@ -64,7 +64,7 @@ import com.oracle.graal.python.builtins.objects.type.TypeNodes.GetNameNode; import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PRaiseNode; -import com.oracle.graal.python.nodes.attributes.GetAttributeNode; +import com.oracle.graal.python.nodes.attributes.GetFixedAttributeNode; import com.oracle.graal.python.nodes.builtins.FunctionNodes.GetSignatureNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; @@ -104,7 +104,7 @@ static boolean isBuiltinFunction(PMethod self) { @Specialization(guards = "isBuiltinFunction(self)") static TruffleString reprBuiltinFunction(VirtualFrame frame, PMethod self, - @Shared @Cached("createGetAttributeNode()") GetAttributeNode getNameNode, + @Shared @Cached("createGetAttributeNode()") GetFixedAttributeNode getNameNode, @Shared("formatter") @Cached SimpleTruffleStringFormatNode simpleTruffleStringFormatNode) { // (tfel): this only happens for builtin modules ... I think return simpleTruffleStringFormatNode.format("", getNameNode.executeObject(frame, self.getFunction())); @@ -112,7 +112,7 @@ static TruffleString reprBuiltinFunction(VirtualFrame frame, PMethod self, @Specialization(guards = "isBuiltinFunction(self)") static TruffleString reprBuiltinFunction(VirtualFrame frame, PBuiltinMethod self, - @Shared @Cached("createGetAttributeNode()") GetAttributeNode getNameNode, + @Shared @Cached("createGetAttributeNode()") GetFixedAttributeNode getNameNode, @Shared("formatter") @Cached SimpleTruffleStringFormatNode simpleTruffleStringFormatNode) { return simpleTruffleStringFormatNode.format("", getNameNode.executeObject(frame, self.getFunction())); } @@ -121,7 +121,7 @@ static TruffleString reprBuiltinFunction(VirtualFrame frame, PBuiltinMethod self static TruffleString reprBuiltinMethod(VirtualFrame frame, PBuiltinMethod self, @Bind Node inliningTarget, @Shared @Cached GetClassNode getClassNode, - @Shared @Cached("createGetAttributeNode()") GetAttributeNode getNameNode, + @Shared @Cached("createGetAttributeNode()") GetFixedAttributeNode getNameNode, @Shared @Cached GetNameNode getTypeNameNode, @Shared("formatter") @Cached SimpleTruffleStringFormatNode simpleTruffleStringFormatNode) { TruffleString typeName = getTypeNameNode.execute(inliningTarget, getClassNode.execute(inliningTarget, self.getSelf())); @@ -133,7 +133,7 @@ static TruffleString reprBuiltinMethod(VirtualFrame frame, PBuiltinMethod self, static TruffleString reprBuiltinMethod(VirtualFrame frame, PMethod self, @Bind Node inliningTarget, @Shared @Cached GetClassNode getClassNode, - @Shared @Cached("createGetAttributeNode()") GetAttributeNode getNameNode, + @Shared @Cached("createGetAttributeNode()") GetFixedAttributeNode getNameNode, @Shared @Cached GetNameNode getTypeNameNode, @Shared("formatter") @Cached SimpleTruffleStringFormatNode simpleTruffleStringFormatNode) { TruffleString typeName = getTypeNameNode.execute(inliningTarget, getClassNode.execute(inliningTarget, self.getSelf())); @@ -142,8 +142,8 @@ static TruffleString reprBuiltinMethod(VirtualFrame frame, PMethod self, } @NeverDefault - protected static GetAttributeNode createGetAttributeNode() { - return GetAttributeNode.create(T___NAME__); + protected static GetFixedAttributeNode createGetAttributeNode() { + return GetFixedAttributeNode.create(T___NAME__); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/MethodWrapperBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/MethodWrapperBuiltins.java index 9617daf677..66981606c8 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/MethodWrapperBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/MethodWrapperBuiltins.java @@ -55,7 +55,7 @@ import com.oracle.graal.python.builtins.objects.str.StringUtils.SimpleTruffleStringFormatNode; import com.oracle.graal.python.builtins.objects.type.TpSlots; import com.oracle.graal.python.builtins.objects.type.TypeNodes.GetNameNode; -import com.oracle.graal.python.nodes.attributes.GetAttributeNode; +import com.oracle.graal.python.nodes.attributes.GetFixedAttributeNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; import com.oracle.graal.python.nodes.object.GetClassNode; @@ -96,7 +96,7 @@ abstract static class ReprNode extends PythonUnaryBuiltinNode { static TruffleString reprBuiltinMethod(VirtualFrame frame, PBuiltinMethod self, @Bind Node inliningTarget, @Shared @Cached GetClassNode getClassNode, - @Shared @Cached("createGetAttributeNode()") GetAttributeNode getNameNode, + @Shared @Cached("createGetAttributeNode()") GetFixedAttributeNode getNameNode, @Shared @Cached GetNameNode getTypeNameNode, @Shared("formatter") @Cached SimpleTruffleStringFormatNode simpleTruffleStringFormatNode) { TruffleString typeName = getTypeNameNode.execute(inliningTarget, getClassNode.execute(inliningTarget, self.getSelf())); @@ -108,7 +108,7 @@ static TruffleString reprBuiltinMethod(VirtualFrame frame, PBuiltinMethod self, static TruffleString reprBuiltinMethod(VirtualFrame frame, PMethod self, @Bind Node inliningTarget, @Shared @Cached GetClassNode getClassNode, - @Shared @Cached("createGetAttributeNode()") GetAttributeNode getNameNode, + @Shared @Cached("createGetAttributeNode()") GetFixedAttributeNode getNameNode, @Shared @Cached GetNameNode getTypeNameNode, @Shared("formatter") @Cached SimpleTruffleStringFormatNode simpleTruffleStringFormatNode) { TruffleString typeName = getTypeNameNode.execute(inliningTarget, getClassNode.execute(inliningTarget, self.getSelf())); @@ -117,8 +117,8 @@ static TruffleString reprBuiltinMethod(VirtualFrame frame, PMethod self, } @NeverDefault - protected static GetAttributeNode createGetAttributeNode() { - return GetAttributeNode.create(T___NAME__); + protected static GetFixedAttributeNode createGetAttributeNode() { + return GetFixedAttributeNode.create(T___NAME__); } } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeBuiltins.java index 026803da3e..8aa492468b 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeBuiltins.java @@ -135,7 +135,7 @@ import com.oracle.graal.python.nodes.PNodeWithContext; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.SpecialAttributeNames; -import com.oracle.graal.python.nodes.attributes.GetAttributeNode.GetFixedAttributeNode; +import com.oracle.graal.python.nodes.attributes.GetFixedAttributeNode; import com.oracle.graal.python.nodes.attributes.LookupAttributeInMRONode; import com.oracle.graal.python.nodes.attributes.ReadAttributeFromObjectNode; import com.oracle.graal.python.nodes.attributes.WriteAttributeToObjectNode; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeNodes.java index a6280fe41c..3e4c014f4a 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeNodes.java @@ -188,7 +188,7 @@ import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.SpecialAttributeNames; import com.oracle.graal.python.nodes.SpecialMethodNames; -import com.oracle.graal.python.nodes.attributes.GetAttributeNode; +import com.oracle.graal.python.nodes.attributes.GetFixedAttributeNode; import com.oracle.graal.python.nodes.attributes.LookupAttributeInMRONode; import com.oracle.graal.python.nodes.attributes.ReadAttributeFromObjectNode; import com.oracle.graal.python.nodes.call.CallNode; @@ -1946,7 +1946,7 @@ protected PythonClass makeType(VirtualFrame frame, PDict namespaceOrig, TruffleS @Cached("create(T___SET_NAME__)") LookupSpecialMethodNode getSetNameNode, @Cached CallNode callSetNameNode, @Cached CallNode callInitSubclassNode, - @Cached("create(T___INIT_SUBCLASS__)") GetAttributeNode getInitSubclassNode, + @Cached("create(T___INIT_SUBCLASS__)") GetFixedAttributeNode getInitSubclassNode, @Cached GetMroStorageNode getMroStorageNode, @Bind PythonLanguage language, @Cached PRaiseNode raise, diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectGetAttr.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectGetAttr.java index 19944d57aa..07cdafd703 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectGetAttr.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectGetAttr.java @@ -45,7 +45,7 @@ import com.oracle.graal.python.builtins.objects.type.TpSlots.GetObjectSlotsNode; import com.oracle.graal.python.builtins.objects.type.slots.TpSlotGetAttr.CallSlotGetAttrNode; import com.oracle.graal.python.nodes.PRaiseNode; -import com.oracle.graal.python.nodes.attributes.GetAttributeNode.GetFixedAttributeNode; +import com.oracle.graal.python.nodes.attributes.GetFixedAttributeNode; import com.oracle.graal.python.nodes.object.GetClassNode; import com.oracle.graal.python.runtime.exception.PException; import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/GetAttributeNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/GetAttributeNode.java deleted file mode 100644 index 16d5eb25b3..0000000000 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/GetAttributeNode.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright (c) 2017, 2025, 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 - * - * Subject to the condition set forth below, permission is hereby granted to any - * person obtaining a copy of this software, associated documentation and/or - * data (collectively the "Software"), free of charge and under any and all - * copyright rights in the Software, and any and all patent rights owned or - * freely licensable by each licensor hereunder covering either (i) the - * unmodified Software as contributed to or provided by such licensor, or (ii) - * the Larger Works (as defined below), to deal in both - * - * (a) the Software, and - * - * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if - * one is included with the Software each a "Larger Work" to which the Software - * is contributed by such licensors), - * - * without restriction, including without limitation the rights to copy, create - * derivative works of, display, perform, and distribute the Software and make, - * use, sell, offer for sale, import, export, have made, and have sold the - * Software and the Larger Work(s), and to sublicense the foregoing rights on - * either these or other terms. - * - * This license is subject to the following condition: - * - * The above copyright notice and either this complete permission notice or at a - * minimum a reference to the UPL must be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package com.oracle.graal.python.nodes.attributes; - -import static com.oracle.graal.python.nodes.SpecialMethodNames.T___GETATTR__; - -import com.oracle.graal.python.builtins.objects.PNone; -import com.oracle.graal.python.builtins.objects.exception.AttributeErrorBuiltins; -import com.oracle.graal.python.builtins.objects.module.ModuleBuiltins; -import com.oracle.graal.python.builtins.objects.object.ObjectBuiltins; -import com.oracle.graal.python.builtins.objects.type.TpSlots; -import com.oracle.graal.python.builtins.objects.type.TpSlots.GetObjectSlotsNode; -import com.oracle.graal.python.builtins.objects.type.TypeBuiltins; -import com.oracle.graal.python.builtins.objects.type.slots.TpSlotGetAttr.CallSlotGetAttrNode; -import com.oracle.graal.python.nodes.PNodeWithContext; -import com.oracle.graal.python.nodes.attributes.GetAttributeNodeFactory.GetFixedAttributeNodeGen; -import com.oracle.graal.python.nodes.object.GetClassNode; -import com.oracle.graal.python.runtime.exception.PException; -import com.oracle.truffle.api.dsl.Bind; -import com.oracle.truffle.api.dsl.Cached; -import com.oracle.truffle.api.dsl.NeverDefault; -import com.oracle.truffle.api.dsl.Specialization; -import com.oracle.truffle.api.frame.VirtualFrame; -import com.oracle.truffle.api.nodes.Node; -import com.oracle.truffle.api.strings.TruffleString; - -public final class GetAttributeNode extends PNodeWithContext { - - @Child private GetFixedAttributeNode getFixedAttributeNode; - - public Object executeObject(VirtualFrame frame, Object object) { - return getFixedAttributeNode.executeObject(frame, object); - } - - protected GetAttributeNode(TruffleString key) { - getFixedAttributeNode = GetFixedAttributeNode.create(key); - } - - @NeverDefault - public static GetAttributeNode create(TruffleString key) { - return new GetAttributeNode(key); - } - - @SuppressWarnings("truffle-static-method") - public abstract static class GetFixedAttributeNode extends PNodeWithContext { - private final TruffleString key; - @Child private GetObjectSlotsNode getSlotsNode = GetObjectSlotsNode.create(); - - public GetFixedAttributeNode(TruffleString key) { - this.key = key; - } - - public final TruffleString getKey() { - return key; - } - - public final Object executeObject(VirtualFrame frame, Object object) { - return execute(frame, object); - } - - public final Object execute(VirtualFrame frame, Object object) { - return executeImpl(frame, object, getSlotsNode.executeCached(object)); - } - - abstract Object executeImpl(VirtualFrame frame, Object object, TpSlots slots); - - protected static boolean hasNoGetAttr(Object obj) { - // only used in asserts - return LookupAttributeInMRONode.Dynamic.getUncached().execute(GetClassNode.executeUncached(obj), T___GETATTR__) == PNone.NO_VALUE; - } - - protected static boolean isObjectGetAttribute(TpSlots slots) { - return slots.tp_getattro() == ObjectBuiltins.SLOTS.tp_getattro(); - } - - protected static boolean isModuleGetAttribute(TpSlots slots) { - return slots.tp_getattro() == ModuleBuiltins.SLOTS.tp_getattro(); - } - - protected static boolean isTypeGetAttribute(TpSlots slots) { - return slots.tp_getattro() == TypeBuiltins.SLOTS.tp_getattro(); - } - - @Specialization(guards = "isObjectGetAttribute(slots)") - final Object doBuiltinObject(VirtualFrame frame, Object object, @SuppressWarnings("unused") TpSlots slots, - @Cached ObjectBuiltins.GetAttributeNode getAttributeNode) { - assert hasNoGetAttr(object); - return getAttributeNode.execute(frame, object, key); - } - - @Specialization(guards = "isTypeGetAttribute(slots)") - final Object doBuiltinType(VirtualFrame frame, Object object, @SuppressWarnings("unused") TpSlots slots, - @Cached TypeBuiltins.GetattributeNode getAttributeNode) { - assert hasNoGetAttr(object); - return getAttributeNode.execute(frame, object, key); - } - - @Specialization(guards = "isModuleGetAttribute(slots)") - final Object doBuiltinModule(VirtualFrame frame, Object object, @SuppressWarnings("unused") TpSlots slots, - @Cached ModuleBuiltins.ModuleGetattributeNode getAttributeNode) { - assert hasNoGetAttr(object); - return getAttributeNode.execute(frame, object, key); - } - - @Specialization(replaces = {"doBuiltinObject", "doBuiltinType", "doBuiltinModule"}) - final Object doGeneric(VirtualFrame frame, Object object, TpSlots slots, - @Bind Node inliningTarget, - @Cached CallSlotGetAttrNode callGetAttrNode, - @Cached AttributeErrorBuiltins.SetAttributeErrorContext setContext) { - try { - return callGetAttrNode.execute(frame, inliningTarget, slots, object, key); - } catch (PException e) { - throw setContext.execute(inliningTarget, e, object, key); - } - } - - @NeverDefault - public static GetFixedAttributeNode create(TruffleString key) { - return GetFixedAttributeNodeGen.create(key); - } - } -} diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/GetFixedAttributeNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/GetFixedAttributeNode.java new file mode 100644 index 0000000000..8dd83df2d3 --- /dev/null +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/GetFixedAttributeNode.java @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2017, 2025, 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 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.oracle.graal.python.nodes.attributes; + +import com.oracle.graal.python.builtins.objects.PNone; +import com.oracle.graal.python.builtins.objects.exception.AttributeErrorBuiltins; +import com.oracle.graal.python.builtins.objects.module.ModuleBuiltins; +import com.oracle.graal.python.builtins.objects.object.ObjectBuiltins; +import com.oracle.graal.python.builtins.objects.type.TpSlots; +import com.oracle.graal.python.builtins.objects.type.TpSlots.GetObjectSlotsNode; +import com.oracle.graal.python.builtins.objects.type.TypeBuiltins; +import com.oracle.graal.python.builtins.objects.type.slots.TpSlotGetAttr.CallSlotGetAttrNode; +import com.oracle.graal.python.nodes.PNodeWithContext; +import com.oracle.graal.python.nodes.object.GetClassNode; +import com.oracle.graal.python.runtime.exception.PException; +import com.oracle.truffle.api.dsl.Bind; +import com.oracle.truffle.api.dsl.Cached; +import com.oracle.truffle.api.dsl.NeverDefault; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.strings.TruffleString; + +import static com.oracle.graal.python.nodes.SpecialMethodNames.T___GETATTR__; + +@SuppressWarnings("truffle-static-method") +public abstract class GetFixedAttributeNode extends PNodeWithContext { + private final TruffleString key; + @Child private GetObjectSlotsNode getSlotsNode = GetObjectSlotsNode.create(); + + public GetFixedAttributeNode(TruffleString key) { + this.key = key; + } + + public final TruffleString getKey() { + return key; + } + + public final Object executeObject(VirtualFrame frame, Object object) { + return execute(frame, object); + } + + public final Object execute(VirtualFrame frame, Object object) { + return executeImpl(frame, object, getSlotsNode.executeCached(object)); + } + + abstract Object executeImpl(VirtualFrame frame, Object object, TpSlots slots); + + protected static boolean hasNoGetAttr(Object obj) { + // only used in asserts + return LookupAttributeInMRONode.Dynamic.getUncached().execute(GetClassNode.executeUncached(obj), T___GETATTR__) == PNone.NO_VALUE; + } + + protected static boolean isObjectGetAttribute(TpSlots slots) { + return slots.tp_getattro() == ObjectBuiltins.SLOTS.tp_getattro(); + } + + protected static boolean isModuleGetAttribute(TpSlots slots) { + return slots.tp_getattro() == ModuleBuiltins.SLOTS.tp_getattro(); + } + + protected static boolean isTypeGetAttribute(TpSlots slots) { + return slots.tp_getattro() == TypeBuiltins.SLOTS.tp_getattro(); + } + + @Specialization(guards = "isObjectGetAttribute(slots)") + final Object doBuiltinObject(VirtualFrame frame, Object object, @SuppressWarnings("unused") TpSlots slots, + @Cached ObjectBuiltins.GetAttributeNode getAttributeNode) { + assert hasNoGetAttr(object); + return getAttributeNode.execute(frame, object, key); + } + + @Specialization(guards = "isTypeGetAttribute(slots)") + final Object doBuiltinType(VirtualFrame frame, Object object, @SuppressWarnings("unused") TpSlots slots, + @Cached TypeBuiltins.GetattributeNode getAttributeNode) { + assert hasNoGetAttr(object); + return getAttributeNode.execute(frame, object, key); + } + + @Specialization(guards = "isModuleGetAttribute(slots)") + final Object doBuiltinModule(VirtualFrame frame, Object object, @SuppressWarnings("unused") TpSlots slots, + @Cached ModuleBuiltins.ModuleGetattributeNode getAttributeNode) { + assert hasNoGetAttr(object); + return getAttributeNode.execute(frame, object, key); + } + + @Specialization(replaces = {"doBuiltinObject", "doBuiltinType", "doBuiltinModule"}) + final Object doGeneric(VirtualFrame frame, Object object, TpSlots slots, + @Bind("this") Node inliningTarget, + @Cached CallSlotGetAttrNode callGetAttrNode, + @Cached AttributeErrorBuiltins.SetAttributeErrorContext setContext) { + try { + return callGetAttrNode.execute(frame, inliningTarget, slots, object, key); + } catch (PException e) { + throw setContext.execute(inliningTarget, e, object, key); + } + } + + @NeverDefault + public static GetFixedAttributeNode create(TruffleString key) { + return GetFixedAttributeNodeGen.create(key); + } +} diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode_dsl/PBytecodeDSLRootNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode_dsl/PBytecodeDSLRootNode.java index 5f6e2969c0..f7bc5413a6 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode_dsl/PBytecodeDSLRootNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode_dsl/PBytecodeDSLRootNode.java @@ -168,7 +168,7 @@ import com.oracle.graal.python.nodes.argument.keywords.ExpandKeywordStarargsNode; import com.oracle.graal.python.nodes.argument.keywords.NonMappingException; import com.oracle.graal.python.nodes.argument.keywords.SameDictKeyException; -import com.oracle.graal.python.nodes.attributes.GetAttributeNode.GetFixedAttributeNode; +import com.oracle.graal.python.nodes.attributes.GetFixedAttributeNode; import com.oracle.graal.python.nodes.builtins.ListNodes; import com.oracle.graal.python.nodes.bytecode.CopyDictWithoutKeysNode; import com.oracle.graal.python.nodes.bytecode.GetSendValueNode; From 6a81c7726503579dc3eaea9d1e07bda2f3dd0451 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Tue, 8 Jul 2025 14:29:07 +0200 Subject: [PATCH 02/38] Add env files to conveniently dump host inlining decisions --- mx.graalpython/native-ce-host-inlining | 6 ++++++ mx.graalpython/native-ee-host-inlining | 6 ++++++ 2 files changed, 12 insertions(+) create mode 100644 mx.graalpython/native-ce-host-inlining create mode 100644 mx.graalpython/native-ee-host-inlining diff --git a/mx.graalpython/native-ce-host-inlining b/mx.graalpython/native-ce-host-inlining new file mode 100644 index 0000000000..3c20b6d582 --- /dev/null +++ b/mx.graalpython/native-ce-host-inlining @@ -0,0 +1,6 @@ +DYNAMIC_IMPORTS=/tools,/compiler,/substratevm +BUILD_TARGETS=GRAALPY_NATIVE_STANDALONE +COMPONENTS=SubstrateVM,Truffle SVM Macro +NATIVE_IMAGES=lib:pythonvm +EXTRA_IMAGE_BUILDER_ARGUMENTS=pythonvm:-H:+UnlockExperimentalVMOptions pythonvm:-H:Log=HostInliningPhase,~CanonicalizerPhase,~GraphBuilderPhase pythonvm:-H:+TruffleHostInliningPrintExplored pythonvm:-H:MethodFilter=com.oracle.graal.python.*.* pythonvm:-H:-UnlockExperimentalVMOptions pythonvm:-Dgraal.LogFile=host-inlining.txt +GRAALVM_SKIP_ARCHIVE=true diff --git a/mx.graalpython/native-ee-host-inlining b/mx.graalpython/native-ee-host-inlining new file mode 100644 index 0000000000..6b69aacb64 --- /dev/null +++ b/mx.graalpython/native-ee-host-inlining @@ -0,0 +1,6 @@ +DYNAMIC_IMPORTS=/tools,/graal-enterprise,/truffle-enterprise,/vm-enterprise,/substratevm-enterprise,substratevm-enterprise-gcs +BUILD_TARGETS=GRAALPY_NATIVE_STANDALONE +COMPONENTS=SubstrateVM Enterprise,Truffle SVM Macro Enterprise,suite:substratevm-enterprise-gcs +NATIVE_IMAGES=lib:pythonvm +EXTRA_IMAGE_BUILDER_ARGUMENTS=pythonvm:-H:+UnlockExperimentalVMOptions pythonvm:-H:Log=HostInliningPhase,~CanonicalizerPhase,~GraphBuilderPhase pythonvm:-H:+TruffleHostInliningPrintExplored pythonvm:-H:MethodFilter=com.oracle.graal.python.*.* pythonvm:-H:-UnlockExperimentalVMOptions pythonvm:-Dgraal.LogFile=host-inlining.txt +GRAALVM_SKIP_ARCHIVE=true From e7e153eae1e714ce6f49d5a52aa661cd20333b73 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Tue, 8 Jul 2025 15:11:17 +0200 Subject: [PATCH 03/38] Remove duplicate specializations in GetClassNode * Seems host inlining was somehow already removing those as the node cost does not change before and after, when looking at e.g. com.oracle.graal.python.nodes.object.GetClassNodeGen.execute com.oracle.graal.python.nodes.object.GetClassNodeGen$Inlined.execute com.oracle.graal.python.builtins.objects.object.ObjectBuiltinsFactory$ClassNodeFactory$ClassNodeGen.execute --- .../oracle/graal/python/nodes/object/GetClassNode.java | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/GetClassNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/GetClassNode.java index 92939aaae0..f3cb43f6b1 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/GetClassNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/GetClassNode.java @@ -91,16 +91,6 @@ public static Object executeUncached(Object object) { return GetClassNodeGen.getUncached().execute(null, object); } - @SuppressWarnings("static-method") - public final Object execute(@SuppressWarnings("unused") int i) { - return PythonBuiltinClassType.PInt; - } - - @SuppressWarnings("static-method") - public final Object execute(@SuppressWarnings("unused") double d) { - return PythonBuiltinClassType.PFloat; - } - @Specialization static Object getBoolean(@SuppressWarnings("unused") Boolean object) { return PythonBuiltinClassType.Boolean; From c4fe230b308c764ce2430aab5e0187c4fefb7348 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Tue, 8 Jul 2025 18:02:12 +0200 Subject: [PATCH 04/38] Use an explicit guard for GetClassNode#getForeign to save code size * The generated GetClassNodeGen.fallbackGuard_ had a node cost of 316. * Much better for host inlining: com.oracle.graal.python.nodes.object.GetClassNodeGen#execute 688 -> 556 com.oracle.graal.python.nodes.object.GetClassNodeGen.Inlined#execute 1366 -> 1230 com.oracle.graal.python.builtins.objects.object.ObjectBuiltinsFactory.ClassNodeFactory.ClassNodeGen#execute 579 -> 485 Truffle host inlining completed after 2 rounds. Graph cost changed from 439 to 688 after inlining: Root[com.oracle.graal.python.nodes.object.GetClassNodeGen.execute] INLINE com.oracle.graal.python.nodes.object.GetClassNodeGen$GetPythonObjectClassNodeGen.executeImpl(Node, PythonAbstractObject) cost 213, subTreeCost 201, treeInvokes 2, reason within budget] CUTOFF com.oracle.graal.python.nodes.object.GetClassNode$GetPythonObjectClassNode.getPythonObject(Node, PythonObject, HiddenAttr$ReadNode) cost -, subTreeCost -, treeInvokes -, reason method annotated with @InliningCutoff] CUTOFF com.oracle.graal.python.nodes.object.GetClassNode$GetPythonObjectClassNode.getNativeObject(...) cost -, subTreeCost -, treeInvokes -, reason method annotated with @InliningCutoff] INLINE com.oracle.graal.python.nodes.object.GetClassNodeGen.fallbackGuard_(int, Node, Object) cost 316, subTreeCost 316, treeInvokes 0, reason within budget] CUTOFF com.oracle.graal.python.nodes.object.GetClassNode.getForeign(Object, GetRegisteredClassNode) cost -, subTreeCost -, treeInvokes -, reason method annotated with @InliningCutoff] -> Truffle host inlining completed after 2 rounds. Graph cost changed from 426 to 556 after inlining: Root[com.oracle.graal.python.nodes.object.GetClassNodeGen.execute] INLINE com.oracle.graal.python.nodes.object.GetClassNodeGen$GetPythonObjectClassNodeGen.executeImpl(Node, PythonAbstractObject) cost 213, subTreeCost 201, treeInvokes 2, reason within budget] CUTOFF com.oracle.graal.python.nodes.object.GetClassNode$GetPythonObjectClassNode.getPythonObject(Node, PythonObject, HiddenAttr$ReadNode) cost -, subTreeCost -, treeInvokes -, reason method annotated with @InliningCutoff] CUTOFF com.oracle.graal.python.nodes.object.GetClassNode$GetPythonObjectClassNode.getNativeObject(...) cost -, subTreeCost -, treeInvokes -, reason method annotated with @InliningCutoff] CUTOFF com.oracle.graal.python.nodes.object.GetClassNode.getForeign(Object, GetRegisteredClassNode) cost -, subTreeCost -, treeInvokes -, reason method annotated with @InliningCutoff] * Using `@Cached IsForeignObjectNode isForeignObjectNode` + `isForeignObjectNode.execute(inliningTarget, object)` is a lot worse: com.oracle.graal.python.nodes.object.GetClassNodeGen#execute 688 -> 698 com.oracle.graal.python.nodes.object.GetClassNodeGen.Inlined#execute 1366 -> 1637 com.oracle.graal.python.builtins.objects.object.ObjectBuiltinsFactory.ClassNodeFactory.ClassNodeGen#execute 579 -> 622 Truffle host inlining completed after 2 rounds. Graph cost changed from 461 to 698 after inlining: Root[com.oracle.graal.python.nodes.object.GetClassNodeGen.execute] INLINE com.oracle.graal.python.nodes.object.GetClassNodeGen$GetPythonObjectClassNodeGen.executeImpl(Node, PythonAbstractObject) cost 213, subTreeCost 201, treeInvokes 2, reason within budget] CUTOFF com.oracle.graal.python.nodes.object.GetClassNode$GetPythonObjectClassNode.getPythonObject(Node, PythonObject, HiddenAttr$ReadNode) cost -, subTreeCost -, treeInvokes -, reason method annotated with @InliningCutoff] CUTOFF com.oracle.graal.python.nodes.object.GetClassNode$GetPythonObjectClassNode.getNativeObject(...) cost -, subTreeCost -, treeInvokes -, reason method annotated with @InliningCutoff] INLINE com.oracle.graal.python.nodes.object.IsForeignObjectNodeGen$Inlined.execute(Node, Object) cost 253, subTreeCost 223, treeInvokes 0, reason within budget] INLINE com.oracle.graal.python.nodes.object.IsForeignObjectNodeGen$Inlined.fallbackGuard_(int, Node, Object) cost 140, subTreeCost -, treeInvokes -, reason null] CUTOFF com.oracle.graal.python.nodes.object.GetClassNode.getForeign(Node, Object, IsForeignObjectNode, GetRegisteredClassNode) cost -, subTreeCost -, treeInvokes -, reason method annotated with @InliningCutoff] --- .../src/com/oracle/graal/python/nodes/PGuards.java | 5 +++++ .../graal/python/nodes/object/GetClassNode.java | 3 +-- .../python/nodes/object/IsForeignObjectNode.java | 11 ++++++++++- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/PGuards.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/PGuards.java index b25e29ddaf..3e460179a4 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/PGuards.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/PGuards.java @@ -96,6 +96,7 @@ import com.oracle.graal.python.builtins.objects.type.TypeNodes; import com.oracle.graal.python.lib.PyIndexCheckNode; import com.oracle.graal.python.nodes.object.GetClassNode.GetPythonObjectClassNode; +import com.oracle.graal.python.nodes.object.IsForeignObjectNode; import com.oracle.graal.python.runtime.PythonOptions; import com.oracle.graal.python.runtime.exception.PException; import com.oracle.graal.python.runtime.sequence.PSequence; @@ -294,6 +295,10 @@ public static boolean isAnyPythonObject(Object obj) { return obj instanceof PythonAbstractObject; } + public static boolean isForeignObject(Object obj) { + return IsForeignObjectNode.executeUncached(obj); + } + public static boolean canBeInteger(Object idx) { return isBoolean(idx) || isInteger(idx) || isPInt(idx); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/GetClassNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/GetClassNode.java index f3cb43f6b1..9179d286a0 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/GetClassNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/GetClassNode.java @@ -58,7 +58,6 @@ 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.GenerateInline; import com.oracle.truffle.api.dsl.GenerateUncached; import com.oracle.truffle.api.dsl.Idempotent; @@ -230,7 +229,7 @@ static Object getNativeVoidPtr(@SuppressWarnings("unused") PythonNativeVoidPtr o } @InliningCutoff - @Fallback + @Specialization(guards = "isForeignObject(object)") static Object getForeign(Object object, @Cached(inline = false) GetRegisteredClassNode getRegisteredClassNode) { return getRegisteredClassNode.execute(object); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/IsForeignObjectNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/IsForeignObjectNode.java index 8dfe622eb8..3ba977af77 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/IsForeignObjectNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/IsForeignObjectNode.java @@ -62,8 +62,17 @@ public abstract class IsForeignObjectNode extends PNodeWithContext { public abstract boolean execute(Node inliningTarget, Object object); + // This is useful as a guard and much better than the inlined node in terms of host inlining + // code size. + // The generated uncached node doesn't work as a guard because of @TruffleBoundary. public static boolean executeUncached(Object object) { - return IsForeignObjectNodeGen.getUncached().execute(null, object); + return !(object instanceof Boolean || + object instanceof Integer || + object instanceof Long || + object instanceof Double || + object instanceof TruffleString || + object instanceof PythonAbstractObject || + object instanceof PythonBuiltinClassType); } @Specialization From 714871632745e52beb05a2b732db1cc1af271d62 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Wed, 9 Jul 2025 16:08:49 +0200 Subject: [PATCH 05/38] Use CompilerAsserts.neverPartOfCompilation() instead of comment --- .../src/com/oracle/graal/python/lib/PyObjectLookupAttr.java | 3 ++- .../graal/python/nodes/attributes/GetFixedAttributeNode.java | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectLookupAttr.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectLookupAttr.java index f537089835..17fd1d9e43 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectLookupAttr.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectLookupAttr.java @@ -66,6 +66,7 @@ import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinObjectProfile; import com.oracle.graal.python.nodes.object.GetClassNode; import com.oracle.graal.python.runtime.exception.PException; +import com.oracle.truffle.api.CompilerAsserts; import com.oracle.truffle.api.dsl.Bind; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Cached.Exclusive; @@ -106,7 +107,7 @@ public final Object executeCached(Frame frame, Object receiver, TruffleString na public abstract Object execute(Frame frame, Node inliningTarget, Object receiver, TruffleString name); protected static boolean hasNoGetAttr(Object lazyClass) { - // only used in asserts + CompilerAsserts.neverPartOfCompilation("only used in asserts"); return LookupAttributeInMRONode.Dynamic.getUncached().execute(lazyClass, T___GETATTR__) == PNone.NO_VALUE; } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/GetFixedAttributeNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/GetFixedAttributeNode.java index 8dd83df2d3..2e7a655879 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/GetFixedAttributeNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/GetFixedAttributeNode.java @@ -51,6 +51,7 @@ import com.oracle.graal.python.nodes.PNodeWithContext; import com.oracle.graal.python.nodes.object.GetClassNode; import com.oracle.graal.python.runtime.exception.PException; +import com.oracle.truffle.api.CompilerAsserts; import com.oracle.truffle.api.dsl.Bind; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.NeverDefault; @@ -85,7 +86,7 @@ public final Object execute(VirtualFrame frame, Object object) { abstract Object executeImpl(VirtualFrame frame, Object object, TpSlots slots); protected static boolean hasNoGetAttr(Object obj) { - // only used in asserts + CompilerAsserts.neverPartOfCompilation("only used in asserts"); return LookupAttributeInMRONode.Dynamic.getUncached().execute(GetClassNode.executeUncached(obj), T___GETATTR__) == PNone.NO_VALUE; } From e571daaf8dfc6a2ae400bc12023ddb2427031a9d Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Mon, 7 Jul 2025 18:38:05 +0200 Subject: [PATCH 06/38] Remove extra specialization for ObjectBuiltins.GetAttributeNode * It makes no difference for graalpy --experimental-options --engine.Compilation=false graalpython/com.oracle.graal.python.benchmarks/python/harness.py graalpython/com.oracle.graal.python.benchmarks/python/micro/attribute-access.py --live-results -i 10 50 --- .../objects/object/ObjectBuiltins.java | 24 ------------------- 1 file changed, 24 deletions(-) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/ObjectBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/ObjectBuiltins.java index 188e318f49..cbe5c9f6e2 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/ObjectBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/ObjectBuiltins.java @@ -54,7 +54,6 @@ import static com.oracle.graal.python.nodes.StringLiterals.T_SINGLE_QUOTE_COMMA_SPACE; import static com.oracle.graal.python.runtime.exception.PythonErrorType.AttributeError; import static com.oracle.graal.python.runtime.exception.PythonErrorType.TypeError; -import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING; import java.util.List; @@ -146,7 +145,6 @@ import com.oracle.graal.python.runtime.IndirectCallData; import com.oracle.graal.python.runtime.exception.PException; import com.oracle.graal.python.runtime.object.PFactory; -import com.oracle.truffle.api.CompilerAsserts; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; @@ -159,7 +157,6 @@ import com.oracle.truffle.api.dsl.GenerateCached; import com.oracle.truffle.api.dsl.GenerateInline; import com.oracle.truffle.api.dsl.GenerateNodeFactory; -import com.oracle.truffle.api.dsl.Idempotent; import com.oracle.truffle.api.dsl.ImportStatic; import com.oracle.truffle.api.dsl.NeverDefault; import com.oracle.truffle.api.dsl.NodeFactory; @@ -510,27 +507,6 @@ public abstract static class GetAttributeNode extends GetAttrBuiltinNode { @Child private CallSlotDescrGet callSlotDescrGet; @Child private ReadAttributeFromObjectNode attrRead; - @Idempotent - protected static int tsLen(TruffleString ts) { - CompilerAsserts.neverPartOfCompilation(); - return TruffleString.CodePointLengthNode.getUncached().execute(ts, TS_ENCODING) + 1; - } - - // Shortcut, only useful for interpreter performance, but doesn't hurt peak - @Specialization(guards = {"keyObj == cachedKey", "tsLen(cachedKey) < 32"}, limit = "1") - @SuppressWarnings("truffle-static-method") - Object doItTruffleString(VirtualFrame frame, Object object, @SuppressWarnings("unused") TruffleString keyObj, - @Bind Node inliningTarget, - @SuppressWarnings("unused") @Cached("keyObj") TruffleString cachedKey, - @Exclusive @Cached GetClassNode getClassNode, - @Exclusive @Cached GetObjectSlotsNode getSlotsNode, - @Cached("create(cachedKey)") LookupAttributeInMRONode lookup, - @Exclusive @Cached PRaiseNode raiseNode) { - Object type = getClassNode.execute(inliningTarget, object); - Object descr = lookup.execute(type); - return fullLookup(frame, inliningTarget, object, cachedKey, type, descr, getSlotsNode, raiseNode); - } - @Specialization @SuppressWarnings("truffle-static-method") Object doIt(VirtualFrame frame, Object object, Object keyObj, From b9354bae01cd9c2bb1c4d9a512e6d3da2d8fa270 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Thu, 10 Jul 2025 12:11:00 +0200 Subject: [PATCH 07/38] Refactor ObjectBuiltins.GetAttributeNode to be as similar as possible to TypeBuiltins.GetattributeNode * Faster on graalpy --experimental-options --engine.Compilation=false graalpython/com.oracle.graal.python.benchmarks/python/harness.py graalpython/com.oracle.graal.python.benchmarks/python/micro/attribute-access.py --live-results -i 10 50 AVG (all runs): 1.237s -> 1.218s --- .../objects/object/ObjectBuiltins.java | 80 +++++++++---------- .../objects/thread/ThreadLocalBuiltins.java | 2 +- .../builtins/objects/type/TypeBuiltins.java | 31 ++++--- .../graal/python/lib/PyObjectGetAttr.java | 3 +- .../graal/python/lib/PyObjectGetMethod.java | 5 +- .../oracle/graal/python/nodes/PRaiseNode.java | 8 +- 6 files changed, 64 insertions(+), 65 deletions(-) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/ObjectBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/ObjectBuiltins.java index cbe5c9f6e2..393c7bc4b0 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/ObjectBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/ObjectBuiltins.java @@ -26,6 +26,7 @@ package com.oracle.graal.python.builtins.objects.object; +import static com.oracle.graal.python.builtins.PythonBuiltinClassType.TypeError; import static com.oracle.graal.python.builtins.objects.cext.capi.NativeCAPISymbol.FUN_PY_OBJECT_NEW; import static com.oracle.graal.python.nodes.BuiltinNames.J_OBJECT; import static com.oracle.graal.python.nodes.PGuards.isDeleteMarker; @@ -52,8 +53,6 @@ import static com.oracle.graal.python.nodes.SpecialMethodNames.T___REDUCE__; import static com.oracle.graal.python.nodes.StringLiterals.T_NONE; import static com.oracle.graal.python.nodes.StringLiterals.T_SINGLE_QUOTE_COMMA_SPACE; -import static com.oracle.graal.python.runtime.exception.PythonErrorType.AttributeError; -import static com.oracle.graal.python.runtime.exception.PythonErrorType.TypeError; import java.util.List; @@ -93,6 +92,7 @@ import com.oracle.graal.python.builtins.objects.type.TpSlots; import com.oracle.graal.python.builtins.objects.type.TpSlots.GetCachedTpSlotsNode; import com.oracle.graal.python.builtins.objects.type.TpSlots.GetObjectSlotsNode; +import com.oracle.graal.python.builtins.objects.type.TypeBuiltins; import com.oracle.graal.python.builtins.objects.type.TypeFlags; import com.oracle.graal.python.builtins.objects.type.TypeNodes; import com.oracle.graal.python.builtins.objects.type.TypeNodes.CheckCompatibleForAssigmentNode; @@ -144,9 +144,9 @@ import com.oracle.graal.python.runtime.ExecutionContext.IndirectCallContext; import com.oracle.graal.python.runtime.IndirectCallData; import com.oracle.graal.python.runtime.exception.PException; +import com.oracle.graal.python.runtime.exception.PythonErrorType; import com.oracle.graal.python.runtime.object.PFactory; import com.oracle.truffle.api.CompilerDirectives; -import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff; import com.oracle.truffle.api.dsl.Bind; @@ -499,73 +499,65 @@ static TruffleString repr(VirtualFrame frame, Object self, @Slot(value = SlotKind.tp_getattro, isComplex = true) @GenerateNodeFactory public abstract static class GetAttributeNode extends GetAttrBuiltinNode { - @CompilationFinal private int profileFlags = 0; - private static final int HAS_DESCR = 1; - private static final int HAS_VALUE = 2; - private static final int HAS_NO_VALUE = 4; - @Child private CallSlotDescrGet callSlotDescrGet; @Child private ReadAttributeFromObjectNode attrRead; + /** Keep in sync with {@link TypeBuiltins.GetattributeNode} */ @Specialization @SuppressWarnings("truffle-static-method") Object doIt(VirtualFrame frame, Object object, Object keyObj, @Bind Node inliningTarget, + @Cached GetClassNode getClassNode, + @Cached GetObjectSlotsNode getDescrSlotsNode, @Cached LookupAttributeInMRONode.Dynamic lookup, - @Exclusive @Cached GetClassNode getClassNode, - @Exclusive @Cached GetObjectSlotsNode getSlotsNode, - @Cached CastToTruffleStringNode castKeyToStringNode, - @Exclusive @Cached PRaiseNode raiseNode) { + @Cached CastToTruffleStringNode castToString, + @Cached InlinedBranchProfile hasDescProfile, + @Cached InlinedConditionProfile hasDescrGetProfile, + @Cached InlinedBranchProfile hasValueProfile, + @Cached PRaiseNode raiseNode) { TruffleString key; try { - key = castKeyToStringNode.execute(inliningTarget, keyObj); + key = castToString.execute(inliningTarget, keyObj); } catch (CannotCastException e) { throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.ATTR_NAME_MUST_BE_STRING, keyObj); } Object type = getClassNode.execute(inliningTarget, object); Object descr = lookup.execute(type, key); - return fullLookup(frame, inliningTarget, object, key, type, descr, getSlotsNode, raiseNode); - } - - private Object fullLookup(VirtualFrame frame, Node inliningTarget, Object object, TruffleString key, Object type, Object descr, GetObjectSlotsNode getSlotsNode, PRaiseNode raiseNode) { boolean hasDescr = descr != PNone.NO_VALUE; - if (hasDescr && (profileFlags & HAS_DESCR) == 0) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - profileFlags |= HAS_DESCR; - } - TpSlot descrGetSlot = null; + + TpSlot get = null; + boolean hasDescrGet = false; if (hasDescr) { - var descrSlots = getSlotsNode.execute(inliningTarget, descr); - descrGetSlot = descrSlots.tp_descr_get(); - if (descrGetSlot != null && TpSlotDescrSet.PyDescr_IsData(descrSlots)) { - return dispatch(frame, object, type, descr, descrGetSlot); + hasDescProfile.enter(inliningTarget); + var descrSlots = getDescrSlotsNode.execute(inliningTarget, descr); + get = descrSlots.tp_descr_get(); + hasDescrGet = hasDescrGetProfile.profile(inliningTarget, get != null); + if (hasDescrGet && TpSlotDescrSet.PyDescr_IsData(descrSlots)) { + return dispatchDescrGet(frame, object, type, descr, get); } } - Object value = readAttribute(object, key); - boolean hasValue = value != PNone.NO_VALUE; - if (hasValue && (profileFlags & HAS_VALUE) == 0) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - profileFlags |= HAS_VALUE; - } - if (hasValue) { + + // The difference with TypeBuiltins.GetattributeNode (+ error message below) + Object value = readAttributeOfObject(object, key); + if (value != PNone.NO_VALUE) { + hasValueProfile.enter(inliningTarget); return value; } - if ((profileFlags & HAS_NO_VALUE) == 0) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - profileFlags |= HAS_NO_VALUE; - } + if (hasDescr) { - if (descrGetSlot == null) { + hasDescProfile.enter(inliningTarget); + if (!hasDescrGet) { return descr; } else { - return dispatch(frame, object, type, descr, descrGetSlot); + return dispatchDescrGet(frame, object, type, descr, get); } } - throw raiseNode.raiseAttributeError(inliningTarget, object, key); + + throw raiseNode.raiseAttributeError(inliningTarget, ErrorMessages.OBJ_P_HAS_NO_ATTR_S, object, key); } - private Object readAttribute(Object object, TruffleString key) { + private Object readAttributeOfObject(Object object, TruffleString key) { if (attrRead == null) { CompilerDirectives.transferToInterpreterAndInvalidate(); attrRead = insert(ReadAttributeFromObjectNode.create()); @@ -573,7 +565,7 @@ private Object readAttribute(Object object, TruffleString key) { return attrRead.execute(object, key); } - private Object dispatch(VirtualFrame frame, Object object, Object type, Object descr, TpSlot getSlot) { + private Object dispatchDescrGet(VirtualFrame frame, Object object, Object type, Object descr, TpSlot getSlot) { if (callSlotDescrGet == null) { CompilerDirectives.transferToInterpreterAndInvalidate(); callSlotDescrGet = insert(CallSlotDescrGet.create()); @@ -725,7 +717,7 @@ static Object raise(Object self, Object mapping, @Exclusive @Cached IsOtherBuiltinClassProfile otherBuiltinClassProfile, @Exclusive @Cached IsBuiltinClassExactProfile isBuiltinClassProfile, @Exclusive @Cached GetClassNode getClassNode) { - throw PRaiseNode.raiseStatic(inliningTarget, AttributeError, ErrorMessages.OBJ_P_HAS_NO_ATTR_S, self, "__dict__"); + throw PRaiseNode.raiseStatic(inliningTarget, PythonErrorType.AttributeError, ErrorMessages.OBJ_P_HAS_NO_ATTR_S, self, "__dict__"); } static boolean isFallback(Object self, Object mapping, Node inliningTarget, @@ -883,7 +875,7 @@ static Object dir(VirtualFrame frame, Object obj, if (klass != PNone.NO_VALUE) { Object state = IndirectCallContext.enter(frame, inliningTarget, indirectCallData); try { - com.oracle.graal.python.builtins.objects.type.TypeBuiltins.DirNode.dir(names, klass); + TypeBuiltins.DirNode.dir(names, klass); } finally { IndirectCallContext.exit(frame, inliningTarget, indirectCallData, state); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/thread/ThreadLocalBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/thread/ThreadLocalBuiltins.java index c673405948..640fa4d0ba 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/thread/ThreadLocalBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/thread/ThreadLocalBuiltins.java @@ -184,7 +184,7 @@ Object doIt(VirtualFrame frame, PThreadLocal object, Object keyObj, return dispatch(frame, object, type, descr, descrGetSlot); } } - throw raiseNode.raiseAttributeError(inliningTarget, object, key); + throw raiseNode.raiseAttributeError(inliningTarget, ErrorMessages.OBJ_P_HAS_NO_ATTR_S, object, key); } private Object dispatch(VirtualFrame frame, Object object, Object type, Object descr, TpSlot get) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeBuiltins.java index 8aa492468b..e22a1f2d2d 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeBuiltins.java @@ -95,10 +95,10 @@ import com.oracle.graal.python.builtins.objects.common.SequenceNodes.GetObjectArrayNode; import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes.ToArrayNode; import com.oracle.graal.python.builtins.objects.dict.PDict; -import com.oracle.graal.python.builtins.objects.exception.AttributeErrorBuiltins; import com.oracle.graal.python.builtins.objects.function.PKeyword; import com.oracle.graal.python.builtins.objects.getsetdescriptor.DescriptorDeleteMarker; import com.oracle.graal.python.builtins.objects.list.PList; +import com.oracle.graal.python.builtins.objects.object.ObjectBuiltins; import com.oracle.graal.python.builtins.objects.object.ObjectNodes; import com.oracle.graal.python.builtins.objects.set.PSet; import com.oracle.graal.python.builtins.objects.set.SetBuiltins.UpdateSingleNode; @@ -522,6 +522,7 @@ public abstract static class GetattributeNode extends GetAttrBuiltinNode { @Child private CallSlotDescrGet callSlotValueGet; @Child private LookupAttributeInMRONode.Dynamic lookupAsClass; + /** Keep in sync with {@link ObjectBuiltins.GetAttributeNode} */ @Specialization protected Object doIt(VirtualFrame frame, Object object, Object keyObj, @Bind Node inliningTarget, @@ -534,7 +535,6 @@ protected Object doIt(VirtualFrame frame, Object object, Object keyObj, @Cached InlinedConditionProfile hasDescrGetProfile, @Cached InlinedBranchProfile hasValueProfile, @Cached InlinedBranchProfile hasNonDescriptorValueProfile, - @Cached InlinedBranchProfile errorProfile, @Cached PRaiseNode raiseNode) { TruffleString key; try { @@ -543,20 +543,24 @@ protected Object doIt(VirtualFrame frame, Object object, Object keyObj, throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.ATTR_NAME_MUST_BE_STRING, keyObj); } - Object metatype = getClassNode.execute(inliningTarget, object); - Object descr = lookup.execute(metatype, key); + Object type = getClassNode.execute(inliningTarget, object); + Object descr = lookup.execute(type, key); + boolean hasDescr = descr != PNone.NO_VALUE; + TpSlot get = null; boolean hasDescrGet = false; - if (descr != NO_VALUE) { - // acts as a branch profile + if (hasDescr) { + hasDescProfile.enter(inliningTarget); var descrSlots = getDescrSlotsNode.execute(inliningTarget, descr); get = descrSlots.tp_descr_get(); hasDescrGet = hasDescrGetProfile.profile(inliningTarget, get != null); if (hasDescrGet && TpSlotDescrSet.PyDescr_IsData(descrSlots)) { - return dispatchDescrGet(frame, object, metatype, descr, get); + return dispatchDescrGet(frame, object, type, descr, get); } } - Object value = readAttribute(object, key); + + // The difference with ObjectBuiltins.GetAttributeNode (+ error message below) + Object value = readAttributeOfClass(object, key); if (value != NO_VALUE) { hasValueProfile.enter(inliningTarget); var valueSlots = getValueSlotsNode.execute(inliningTarget, value); @@ -568,19 +572,20 @@ protected Object doIt(VirtualFrame frame, Object object, Object keyObj, return dispatchValueGet(frame, object, value, valueGet); } } - if (descr != NO_VALUE) { + + if (hasDescr) { hasDescProfile.enter(inliningTarget); if (!hasDescrGet) { return descr; } else { - return dispatchDescrGet(frame, object, metatype, descr, get); + return dispatchDescrGet(frame, object, type, descr, get); } } - errorProfile.enter(inliningTarget); - throw raiseNode.raiseWithData(inliningTarget, AttributeError, AttributeErrorBuiltins.dataForObjKey(object, key), ErrorMessages.OBJ_N_HAS_NO_ATTR_S, object, key); + + throw raiseNode.raiseAttributeError(inliningTarget, ErrorMessages.OBJ_N_HAS_NO_ATTR_S, object, key); } - private Object readAttribute(Object object, TruffleString key) { + private Object readAttributeOfClass(Object object, TruffleString key) { if (lookupAsClass == null) { CompilerDirectives.transferToInterpreterAndInvalidate(); lookupAsClass = insert(LookupAttributeInMRONode.Dynamic.create()); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectGetAttr.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectGetAttr.java index 07cdafd703..9910df78d5 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectGetAttr.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectGetAttr.java @@ -44,6 +44,7 @@ import com.oracle.graal.python.builtins.objects.exception.AttributeErrorBuiltins; import com.oracle.graal.python.builtins.objects.type.TpSlots.GetObjectSlotsNode; import com.oracle.graal.python.builtins.objects.type.slots.TpSlotGetAttr.CallSlotGetAttrNode; +import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.attributes.GetFixedAttributeNode; import com.oracle.graal.python.nodes.object.GetClassNode; @@ -106,7 +107,7 @@ static Object getDynamicAttr(Frame frame, Node inliningTarget, Object receiver, Object result = PyObjectLookupAttr.readAttributeQuickly(type, slots, receiver, name, codePointLengthNode, codePointAtIndexNode); if (result != null) { if (result == PNone.NO_VALUE) { - throw PRaiseNode.raiseAttributeErrorStatic(inliningTarget, receiver, name); + throw PRaiseNode.raiseAttributeErrorStatic(inliningTarget, ErrorMessages.OBJ_P_HAS_NO_ATTR_S, receiver, name); } return result; } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectGetMethod.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectGetMethod.java index 5cc725f3a2..7df9bf9fcf 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectGetMethod.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectGetMethod.java @@ -50,6 +50,7 @@ import com.oracle.graal.python.builtins.objects.type.slots.TpSlot; import com.oracle.graal.python.builtins.objects.type.slots.TpSlotDescrGet.CallSlotDescrGet; import com.oracle.graal.python.builtins.objects.type.slots.TpSlotDescrSet; +import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.attributes.LookupAttributeInMRONode; import com.oracle.graal.python.nodes.attributes.ReadAttributeFromObjectNode; @@ -157,7 +158,7 @@ static Object getFixedAttr(VirtualFrame frame, Node inliningTarget, Object recei if (descr != PNone.NO_VALUE) { return new BoundDescriptor(descr); } - throw raiseNode.raiseAttributeError(inliningTarget, receiver, name); + throw raiseNode.raiseAttributeError(inliningTarget, ErrorMessages.OBJ_P_HAS_NO_ATTR_S, receiver, name); } // No explicit branch profiling when we're looking up multiple things @@ -202,7 +203,7 @@ static Object getDynamicAttr(Frame frame, Node inliningTarget, Object receiver, if (descr != PNone.NO_VALUE) { return new BoundDescriptor(descr); } - throw raiseNode.raiseAttributeError(inliningTarget, receiver, name); + throw raiseNode.raiseAttributeError(inliningTarget, ErrorMessages.OBJ_P_HAS_NO_ATTR_S, receiver, name); } @Specialization(guards = "isForeignObject(inliningTarget, isForeignObjectNode, receiver)", limit = "1") diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/PRaiseNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/PRaiseNode.java index 4193ff53e6..146f44b7b9 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/PRaiseNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/PRaiseNode.java @@ -130,13 +130,13 @@ public static PException raiseWithDataStatic(Node node, PythonBuiltinClassType t throw raiseExceptionObjectStatic(node, pythonException, language); } - public final PException raiseAttributeError(Node inliningTarget, Object obj, Object key) { + public final PException raiseAttributeError(Node inliningTarget, TruffleString errorMessage, Object obj, Object key) { executeEnterProfile(inliningTarget); - throw raiseAttributeErrorStatic(inliningTarget, obj, key); + throw raiseAttributeErrorStatic(inliningTarget, errorMessage, obj, key); } - public static PException raiseAttributeErrorStatic(Node inliningTarget, Object obj, Object key) { - throw raiseWithDataStatic(inliningTarget, PythonBuiltinClassType.AttributeError, AttributeErrorBuiltins.dataForObjKey(obj, key), ErrorMessages.OBJ_P_HAS_NO_ATTR_S, obj, key); + public static PException raiseAttributeErrorStatic(Node inliningTarget, TruffleString errorMessage, Object obj, Object key) { + throw raiseWithDataStatic(inliningTarget, PythonBuiltinClassType.AttributeError, AttributeErrorBuiltins.dataForObjKey(obj, key), errorMessage, obj, key); } public final PException raiseWithData(Node inliningTarget, PythonBuiltinClassType type, Object[] data, TruffleString format, Object... formatArgs) { From 8a11c5a3042b469a25bed9fa135b0a183c473028 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Thu, 10 Jul 2025 13:45:14 +0200 Subject: [PATCH 08/38] Replace CastToTruffleStringCheckedNode by arity-specific variants to avoid extra Object[] allocations in interpreter --- .../modules/WarningsModuleBuiltins.java | 2 +- .../modules/codecs/ErrorHandlers.java | 4 +- .../modules/ctypes/PyCStructTypeBuiltins.java | 4 +- .../modules/io/TextIOWrapperNodes.java | 4 +- .../builtins/objects/array/ArrayBuiltins.java | 4 +- .../objects/common/BufferStorageNodes.java | 2 +- .../exception/UnicodeDecodeErrorBuiltins.java | 4 +- .../exception/UnicodeEncodeErrorBuiltins.java | 6 +- .../objects/function/FunctionBuiltins.java | 4 +- .../objects/generator/GeneratorBuiltins.java | 4 +- .../objects/module/ModuleBuiltins.java | 4 +- .../objects/ssl/SSLContextBuiltins.java | 4 +- .../builtins/objects/str/StringBuiltins.java | 134 ++++++++++-------- .../builtins/objects/str/StringNodes.java | 134 ++++++++++++++++-- .../objects/superobject/SuperBuiltins.java | 4 +- .../keywords/ConcatDictToStorageNode.java | 4 +- 16 files changed, 225 insertions(+), 97 deletions(-) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/WarningsModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/WarningsModuleBuiltins.java index ed74c933fa..3c364f0cc7 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/WarningsModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/WarningsModuleBuiltins.java @@ -1001,7 +1001,7 @@ Object doWarn(VirtualFrame frame, PythonModule mod, Object message, Object categ @Bind Node inliningTarget, @Cached("createFor($node)") IndirectCallData indirectCallData, @Cached SequenceStorageNodes.GetItemScalarNode getItemScalarNode, - @Cached StringNodes.CastToTruffleStringCheckedNode castToStringChecked, + @Cached StringNodes.CastToTruffleStringChecked1Node castToStringChecked, @Cached PRaiseNode raiseNode, @Cached WarningsModuleNode moduleFunctionsNode) { // warnings_warn_impl diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/codecs/ErrorHandlers.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/codecs/ErrorHandlers.java index 20c459ab27..d4dc935831 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/codecs/ErrorHandlers.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/codecs/ErrorHandlers.java @@ -79,7 +79,7 @@ import com.oracle.graal.python.builtins.objects.exception.UnicodeEncodeErrorBuiltins.PyUnicodeEncodeOrTranslateErrorGetEndNode; import com.oracle.graal.python.builtins.objects.exception.UnicodeEncodeErrorBuiltins.PyUnicodeEncodeOrTranslateErrorGetObjectNode; import com.oracle.graal.python.builtins.objects.exception.UnicodeEncodeErrorBuiltins.PyUnicodeEncodeOrTranslateErrorGetStartNode; -import com.oracle.graal.python.builtins.objects.str.StringNodes.CastToTruffleStringCheckedNode; +import com.oracle.graal.python.builtins.objects.str.StringNodes.CastToTruffleStringChecked0Node; import com.oracle.graal.python.builtins.objects.tuple.PTuple; import com.oracle.graal.python.lib.PyBytesCheckNode; import com.oracle.graal.python.lib.PyObjectSizeNode; @@ -852,7 +852,7 @@ abstract static class ParseDecodingErrorHandlerResultNode extends Node { static DecodingErrorHandlerResult doTuple(Node inliningTarget, PTuple result, @Cached SequenceNodes.LenNode lenNode, @Cached SequenceNodes.GetObjectArrayNode getObjectArrayNode, - @Cached CastToTruffleStringCheckedNode castToTruffleStringCheckedNode, + @Cached CastToTruffleStringChecked0Node castToTruffleStringCheckedNode, @Cached CastToJavaIntExactNode castToJavaIntExactNode, @Cached PRaiseNode raiseNode) { if (lenNode.execute(inliningTarget, result) != 2) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ctypes/PyCStructTypeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ctypes/PyCStructTypeBuiltins.java index c626125ab8..70bc866c5a 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ctypes/PyCStructTypeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ctypes/PyCStructTypeBuiltins.java @@ -51,7 +51,7 @@ import com.oracle.graal.python.builtins.PythonBuiltinClassType; import com.oracle.graal.python.builtins.PythonBuiltins; import com.oracle.graal.python.builtins.modules.ctypes.StructUnionTypeBuiltins.PyCStructUnionTypeUpdateStgDict; -import com.oracle.graal.python.builtins.objects.str.StringNodes.CastToTruffleStringCheckedNode; +import com.oracle.graal.python.builtins.objects.str.StringNodes.CastToTruffleStringChecked1Node; import com.oracle.graal.python.builtins.objects.type.TpSlots; import com.oracle.graal.python.builtins.objects.type.TypeBuiltins; import com.oracle.graal.python.builtins.objects.type.slots.TpSlotSetAttr.SetAttrBuiltinNode; @@ -96,7 +96,7 @@ void doStringKey(VirtualFrame frame, Object object, TruffleString key, Object va @InliningCutoff void doGeneric(VirtualFrame frame, Object object, Object keyObject, Object value, @Bind Node inliningTarget, - @Cached CastToTruffleStringCheckedNode castKeyToStringNode, + @Cached CastToTruffleStringChecked1Node castKeyToStringNode, @Shared @Cached TypeBuiltins.SetattrNode typeSetAttr, @Shared @Cached TruffleString.EqualNode equalNode, @Shared @Cached PyCStructUnionTypeUpdateStgDict updateStgDict) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/TextIOWrapperNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/TextIOWrapperNodes.java index 34635c2162..fbcbd707e4 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/TextIOWrapperNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/TextIOWrapperNodes.java @@ -85,7 +85,7 @@ import com.oracle.graal.python.builtins.objects.buffer.PythonBufferAcquireLibrary; import com.oracle.graal.python.builtins.objects.bytes.PBytes; import com.oracle.graal.python.builtins.objects.common.SequenceNodes; -import com.oracle.graal.python.builtins.objects.str.StringNodes.CastToTruffleStringCheckedNode; +import com.oracle.graal.python.builtins.objects.str.StringNodes.CastToTruffleStringChecked1Node; import com.oracle.graal.python.builtins.objects.tuple.PTuple; import com.oracle.graal.python.lib.PyNumberAsSizeNode; import com.oracle.graal.python.lib.PyObjectCallMethodObjArgs; @@ -620,7 +620,7 @@ protected abstract static class DecodeNode extends Node { @Specialization static TruffleString decodeGeneric(VirtualFrame frame, Object decoder, Object o, boolean eof, @Bind Node inliningTarget, - @Cached CastToTruffleStringCheckedNode castNode, + @Cached CastToTruffleStringChecked1Node castNode, @Cached PyObjectCallMethodObjArgs callMethodDecode) { Object decoded = callMethodDecode.execute(frame, inliningTarget, decoder, T_DECODE, o, eof); return castNode.cast(inliningTarget, decoded, DECODER_SHOULD_RETURN_A_STRING_RESULT_NOT_P, decoded); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/array/ArrayBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/array/ArrayBuiltins.java index 2c61d5e9f4..1f723e4265 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/array/ArrayBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/array/ArrayBuiltins.java @@ -81,7 +81,7 @@ import com.oracle.graal.python.builtins.objects.range.PIntRange; import com.oracle.graal.python.builtins.objects.slice.PSlice; import com.oracle.graal.python.builtins.objects.slice.SliceNodes; -import com.oracle.graal.python.builtins.objects.str.StringNodes.CastToTruffleStringCheckedNode; +import com.oracle.graal.python.builtins.objects.str.StringNodes.CastToTruffleStringChecked1Node; import com.oracle.graal.python.builtins.objects.tuple.PTuple; import com.oracle.graal.python.builtins.objects.type.TpSlots; import com.oracle.graal.python.builtins.objects.type.TypeNodes; @@ -185,7 +185,7 @@ static Object array2(VirtualFrame frame, Object cls, Object[] args, PKeyword[] k @Bind Node inliningTarget, @Cached InlinedConditionProfile hasInitializerProfile, @Cached IsBuiltinClassExactProfile isNotSubtypeProfile, - @Cached CastToTruffleStringCheckedNode cast, + @Cached CastToTruffleStringChecked1Node cast, @Cached ArrayNodeInternal arrayNodeInternal, @Cached PRaiseNode raise) { if (isNotSubtypeProfile.profileClass(inliningTarget, cls, PythonBuiltinClassType.PArray)) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/BufferStorageNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/BufferStorageNodes.java index 14993f3c03..304c4d3a62 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/BufferStorageNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/BufferStorageNodes.java @@ -344,7 +344,7 @@ static void packChar(Node inliningTarget, BufferFormat format, Object object, Ob @Specialization(guards = "format == UNICODE") static void packDouble(Node inliningTarget, @SuppressWarnings("unused") BufferFormat format, Object object, Object buffer, int offset, @Shared @CachedLibrary(limit = "3") PythonBufferAccessLibrary bufferLib, - @Cached StringNodes.CastToTruffleStringCheckedNode cast, + @Cached StringNodes.CastToTruffleStringChecked0Node cast, @Cached(inline = false) TruffleString.CodePointLengthNode codePointLengthNode, @Cached(inline = false) TruffleString.CodePointAtIndexNode codePointAtIndexNode, @Exclusive @Cached PRaiseNode raiseNode) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/UnicodeDecodeErrorBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/UnicodeDecodeErrorBuiltins.java index 25a7bbd88c..0d2fcafed8 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/UnicodeDecodeErrorBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/UnicodeDecodeErrorBuiltins.java @@ -65,7 +65,7 @@ import com.oracle.graal.python.builtins.objects.bytes.PBytesLike; import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes; import com.oracle.graal.python.builtins.objects.function.PKeyword; -import com.oracle.graal.python.builtins.objects.str.StringNodes.CastToTruffleStringCheckedNode; +import com.oracle.graal.python.builtins.objects.str.StringNodes.CastToTruffleStringChecked1Node; import com.oracle.graal.python.builtins.objects.str.StringUtils.SimpleTruffleStringFormatNode; import com.oracle.graal.python.builtins.objects.type.TpSlots; import com.oracle.graal.python.lib.PyObjectSizeNode; @@ -301,7 +301,7 @@ public abstract static class PyUnicodeDecodeErrorGetEncodingNode extends Node { @Specialization static TruffleString doIt(Node inliningTarget, PBaseException exceptionObject, @Cached(inline = false) BaseExceptionAttrNode attrNode, - @Cached CastToTruffleStringCheckedNode castToStringNode, + @Cached CastToTruffleStringChecked1Node castToStringNode, @Cached PRaiseNode raiseNode) { Object obj = attrNode.get(exceptionObject, IDX_ENCODING, UNICODE_ERROR_ATTR_FACTORY); if (obj == null) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/UnicodeEncodeErrorBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/UnicodeEncodeErrorBuiltins.java index 57ea0cc797..1efa6b7fbb 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/UnicodeEncodeErrorBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/UnicodeEncodeErrorBuiltins.java @@ -62,7 +62,7 @@ import com.oracle.graal.python.builtins.PythonBuiltins; import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.function.PKeyword; -import com.oracle.graal.python.builtins.objects.str.StringNodes.CastToTruffleStringCheckedNode; +import com.oracle.graal.python.builtins.objects.str.StringNodes.CastToTruffleStringChecked1Node; import com.oracle.graal.python.builtins.objects.str.StringUtils.SimpleTruffleStringFormatNode; import com.oracle.graal.python.builtins.objects.type.TpSlots; import com.oracle.graal.python.lib.PyObjectStrAsTruffleStringNode; @@ -218,7 +218,7 @@ public abstract static class PyUnicodeEncodeOrTranslateErrorGetObjectNode extend @Specialization static TruffleString doIt(Node inliningTarget, PBaseException exceptionObject, @Cached(inline = false) BaseExceptionAttrNode attrNode, - @Cached CastToTruffleStringCheckedNode castToStringNode, + @Cached CastToTruffleStringChecked1Node castToStringNode, @Cached PRaiseNode raiseNode) { Object obj = attrNode.get(exceptionObject, IDX_OBJECT, UNICODE_ERROR_ATTR_FACTORY); if (obj == null) { @@ -298,7 +298,7 @@ public abstract static class PyUnicodeEncodeErrorGetEncodingNode extends Node { @Specialization static TruffleString doIt(Node inliningTarget, PBaseException exceptionObject, @Cached(inline = false) BaseExceptionAttrNode attrNode, - @Cached CastToTruffleStringCheckedNode castToStringNode, + @Cached CastToTruffleStringChecked1Node castToStringNode, @Cached PRaiseNode raiseNode) { Object obj = attrNode.get(exceptionObject, IDX_ENCODING, UNICODE_ERROR_ATTR_FACTORY); if (obj == null) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/FunctionBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/FunctionBuiltins.java index 350cb13005..443a80cd77 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/FunctionBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/FunctionBuiltins.java @@ -223,7 +223,7 @@ static Object setName(PFunction self, TruffleString value) { @Specialization(guards = "!isNoValue(value)") static Object setName(PFunction self, Object value, @Bind Node inliningTarget, - @Cached StringNodes.CastToTruffleStringCheckedNode cast) { + @Cached StringNodes.CastToTruffleStringChecked2Node cast) { return setName(self, cast.cast(inliningTarget, value, ErrorMessages.MUST_BE_SET_TO_S_OBJ, T___NAME__, "string")); } } @@ -245,7 +245,7 @@ static Object setQualname(PFunction self, TruffleString value) { @Specialization(guards = "!isNoValue(value)") static Object setQualname(PFunction self, Object value, @Bind Node inliningTarget, - @Cached StringNodes.CastToTruffleStringCheckedNode cast) { + @Cached StringNodes.CastToTruffleStringChecked2Node cast) { return setQualname(self, cast.cast(inliningTarget, value, ErrorMessages.MUST_BE_SET_TO_S_OBJ, T___QUALNAME__, "string")); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/generator/GeneratorBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/generator/GeneratorBuiltins.java index d547249cc4..360cd9d8f7 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/generator/GeneratorBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/generator/GeneratorBuiltins.java @@ -109,7 +109,7 @@ static Object setName(PGenerator self, TruffleString value) { @Specialization(guards = "!isNoValue(value)") static Object setName(PGenerator self, Object value, @Bind Node inliningTarget, - @Cached StringNodes.CastToTruffleStringCheckedNode cast) { + @Cached StringNodes.CastToTruffleStringChecked2Node cast) { return setName(self, cast.cast(inliningTarget, value, ErrorMessages.MUST_BE_SET_TO_S_OBJ, T___NAME__, "string")); } } @@ -131,7 +131,7 @@ static Object setQualname(PGenerator self, TruffleString value) { @Specialization(guards = "!isNoValue(value)") static Object setQualname(PGenerator self, Object value, @Bind Node inliningTarget, - @Cached StringNodes.CastToTruffleStringCheckedNode cast) { + @Cached StringNodes.CastToTruffleStringChecked2Node cast) { return setQualname(self, cast.cast(inliningTarget, value, ErrorMessages.MUST_BE_SET_TO_S_OBJ, T___QUALNAME__, "string")); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/ModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/ModuleBuiltins.java index f18b46b6e7..4ce69ecb8b 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/ModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/ModuleBuiltins.java @@ -75,7 +75,7 @@ import com.oracle.graal.python.builtins.objects.function.PKeyword; import com.oracle.graal.python.builtins.objects.module.ModuleBuiltinsClinicProviders.ModuleNodeClinicProviderGen; import com.oracle.graal.python.builtins.objects.object.ObjectBuiltins; -import com.oracle.graal.python.builtins.objects.str.StringNodes.CastToTruffleStringCheckedNode; +import com.oracle.graal.python.builtins.objects.str.StringNodes.CastToTruffleStringChecked1Node; import com.oracle.graal.python.builtins.objects.type.TpSlots; import com.oracle.graal.python.builtins.objects.type.TypeNodes; import com.oracle.graal.python.builtins.objects.type.slots.TpSlotGetAttr.GetAttrBuiltinNode; @@ -267,7 +267,7 @@ static Object getattributeStr(VirtualFrame frame, PythonModule self, TruffleStri @InliningCutoff static Object getattribute(VirtualFrame frame, PythonModule self, Object keyObj, @Bind Node inliningTarget, - @Cached CastToTruffleStringCheckedNode castKeyToStringNode, + @Cached CastToTruffleStringChecked1Node castKeyToStringNode, @Shared @Cached ObjectBuiltins.GetAttributeNode objectGetattrNode, @Shared @Cached HandleGetattrExceptionNode handleException) { TruffleString key = castKeyToStringNode.cast(inliningTarget, keyObj, ErrorMessages.ATTR_NAME_MUST_BE_STRING, keyObj); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/ssl/SSLContextBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/ssl/SSLContextBuiltins.java index 67e35a863a..cac643f291 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/ssl/SSLContextBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/ssl/SSLContextBuiltins.java @@ -289,7 +289,7 @@ abstract static class WrapSocketNode extends PythonClinicBuiltinNode { static Object wrap(PSSLContext context, PSocket sock, boolean serverSide, Object serverHostnameObj, Object owner, @SuppressWarnings("unused") PNone session, @Bind Node inliningTarget, @Bind PythonLanguage language, - @Cached StringNodes.CastToTruffleStringCheckedNode cast, + @Cached StringNodes.CastToTruffleStringChecked2Node cast, @Cached TruffleString.ToJavaStringNode toJavaStringNode) { TruffleString serverHostname = null; if (!(serverHostnameObj instanceof PNone)) { @@ -326,7 +326,7 @@ static Object wrap(PSSLContext context, PMemoryBIO incoming, PMemoryBIO outgoing @SuppressWarnings("unused") PNone session, @Bind Node inliningTarget, @Bind PythonLanguage language, - @Cached StringNodes.CastToTruffleStringCheckedNode cast, + @Cached StringNodes.CastToTruffleStringChecked2Node cast, @Cached TruffleString.ToJavaStringNode toJavaStringNode) { TruffleString serverHostname = null; if (!(serverHostnameObj instanceof PNone)) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/StringBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/StringBuiltins.java index b1a4f992c5..8f788bd730 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/StringBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/StringBuiltins.java @@ -62,6 +62,11 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import com.oracle.graal.python.builtins.objects.str.StringNodes.CastToTruffleStringChecked0Node; +import com.oracle.graal.python.builtins.objects.str.StringNodes.CastToTruffleStringChecked1Node; +import com.oracle.graal.python.builtins.objects.str.StringNodes.CastToTruffleStringChecked2Node; +import com.oracle.graal.python.builtins.objects.str.StringNodes.CastToTruffleStringChecked3Node; +import com.oracle.graal.python.builtins.objects.str.StringNodes.CastToTruffleStringChecked4Node; import org.graalvm.shadowed.com.ibm.icu.lang.UCharacter; import org.graalvm.shadowed.com.ibm.icu.lang.UProperty; import org.graalvm.shadowed.com.ibm.icu.text.CaseMap; @@ -110,7 +115,6 @@ import com.oracle.graal.python.builtins.objects.str.StringBuiltinsClinicProviders.FormatNodeClinicProviderGen; import com.oracle.graal.python.builtins.objects.str.StringBuiltinsClinicProviders.SplitNodeClinicProviderGen; import com.oracle.graal.python.builtins.objects.str.StringNodes.CastToJavaStringCheckedNode; -import com.oracle.graal.python.builtins.objects.str.StringNodes.CastToTruffleStringCheckedNode; import com.oracle.graal.python.builtins.objects.str.StringNodes.JoinInternalNode; import com.oracle.graal.python.builtins.objects.str.StringNodes.SpliceNode; import com.oracle.graal.python.builtins.objects.str.StringNodes.StringLenNode; @@ -393,7 +397,7 @@ static TruffleString doString(TruffleString self) { @Specialization(guards = "!isTruffleString(self)") static TruffleString doGeneric(Object self, @Bind Node inliningTarget, - @Cached CastToTruffleStringCheckedNode castToTruffleStringNode) { + @Cached CastToTruffleStringChecked2Node castToTruffleStringNode) { return castToTruffleStringNode.cast(inliningTarget, self, ErrorMessages.REQUIRES_STR_OBJECT_BUT_RECEIVED_P, T___STR__, self); } } @@ -510,7 +514,7 @@ abstract static class ReprNode extends PythonUnaryBuiltinNode { @Specialization static TruffleString doGeneric(Object self, @Bind Node inliningTarget, - @Cached CastToTruffleStringCheckedNode castToStringNode, + @Cached CastToTruffleStringChecked2Node castToStringNode, @Cached StringNodes.StringReprNode reprNode) { return reprNode.execute(castToStringNode.cast(inliningTarget, self, ErrorMessages.REQUIRES_STR_OBJECT_BUT_RECEIVED_P, T___REPR__, self)); } @@ -523,7 +527,7 @@ public abstract static class GetNewargsNode extends PythonUnaryBuiltinNode { PTuple doGeneric(Object self, @Bind Node inliningTarget, @Bind PythonLanguage language, - @Cached CastToTruffleStringCheckedNode cast) { + @Cached CastToTruffleStringChecked2Node cast) { TruffleString selfStr = cast.cast(inliningTarget, self, ErrorMessages.REQUIRES_STR_OBJECT_BUT_RECEIVED_P, T___GETNEWARGS__, self); // CPython requires the resulting string not to be the same object as the original for // some reason @@ -578,7 +582,7 @@ public abstract static class ContainsNode extends SqContainsBuiltinNode { @Specialization static boolean doit(Object self, Object other, @Bind Node inliningTarget, - @Cached CastToTruffleStringCheckedNode castStr, + @Cached CastToTruffleStringChecked1Node castStr, @Cached TruffleString.CodePointLengthNode codePointLengthNode, @Cached TruffleString.IndexOfStringNode indexOfStringNode) { TruffleString selfStr = castStr.cast(inliningTarget, self, ErrorMessages.REQUIRES_STRING_AS_LEFT_OPERAND, other); @@ -645,8 +649,8 @@ public final boolean endsWith(Object self, Object subStr, int start, int end) { @Specialization(guards = "!isPTuple(subStrObj)") static boolean doString(Object selfObj, Object subStrObj, int start, int end, Op op, @Bind Node inliningTarget, - @Exclusive @Cached CastToTruffleStringCheckedNode castSelfNode, - @Exclusive @Cached CastToTruffleStringCheckedNode castPrefixNode, + @Exclusive @Cached CastToTruffleStringChecked2Node castSelfNode, + @Exclusive @Cached CastToTruffleStringChecked3Node castPrefixNode, @Shared @Cached TruffleString.CodePointLengthNode codePointLengthNode, @Shared @Cached TruffleString.RegionEqualNode regionEqualNode) { TruffleString self = castSelfNode.cast(inliningTarget, selfObj, ErrorMessages.REQUIRES_STR_OBJECT_BUT_RECEIVED_P, op.methodName(), selfObj); @@ -660,8 +664,8 @@ static boolean doString(Object selfObj, Object subStrObj, int start, int end, Op static boolean doTuple(Object selfObj, PTuple subStrs, int start, int end, Op op, @Bind Node inliningTarget, @Cached GetObjectArrayNode getObjectArrayNode, - @Exclusive @Cached CastToTruffleStringCheckedNode castSelfNode, - @Exclusive @Cached CastToTruffleStringCheckedNode castPrefixNode, + @Exclusive @Cached CastToTruffleStringChecked2Node castSelfNode, + @Exclusive @Cached CastToTruffleStringChecked2Node castPrefixNode, @Shared @Cached TruffleString.CodePointLengthNode codePointLengthNode, @Shared @Cached TruffleString.RegionEqualNode regionEqualNode) { TruffleString self = castSelfNode.cast(inliningTarget, selfObj, ErrorMessages.REQUIRES_STR_OBJECT_BUT_RECEIVED_P, op.methodName(), selfObj); @@ -755,11 +759,12 @@ static int rfind(TruffleString self, TruffleString sub, int start, int end, @Specialization static int rfind(Object self, Object sub, int start, int end, @Bind Node inliningTarget, - @Cached CastToTruffleStringCheckedNode castNode, + @Cached CastToTruffleStringChecked1Node cast1Node, + @Cached CastToTruffleStringChecked2Node cast2Node, @Shared("cpLen") @Cached TruffleString.CodePointLengthNode codePointLengthNode, @Shared("lastIndexOf") @Cached TruffleString.LastIndexOfStringNode lastIndexOfStringNode) { - TruffleString selfStr = castNode.cast(inliningTarget, self, ErrorMessages.REQUIRES_STR_OBJECT_BUT_RECEIVED_P, "rfind", self); - TruffleString subStr = castNode.cast(inliningTarget, sub, ErrorMessages.MUST_BE_STR_NOT_P, sub); + TruffleString selfStr = cast2Node.cast(inliningTarget, self, ErrorMessages.REQUIRES_STR_OBJECT_BUT_RECEIVED_P, "rfind", self); + TruffleString subStr = cast1Node.cast(inliningTarget, sub, ErrorMessages.MUST_BE_STR_NOT_P, sub); return rfind(selfStr, subStr, start, end, codePointLengthNode, lastIndexOfStringNode); } } @@ -787,11 +792,12 @@ static int find(TruffleString self, TruffleString sub, int start, int end, @Specialization static int find(Object self, Object sub, int start, int end, @Bind Node inliningTarget, - @Cached CastToTruffleStringCheckedNode castNode, + @Cached CastToTruffleStringChecked1Node cast1Node, + @Cached CastToTruffleStringChecked2Node cast2Node, @Shared("cpLen") @Cached TruffleString.CodePointLengthNode codePointLengthNode, @Shared("indexOf") @Cached TruffleString.IndexOfStringNode indexOfStringNode) { - TruffleString selfStr = castNode.cast(inliningTarget, self, ErrorMessages.REQUIRES_STR_OBJECT_BUT_RECEIVED_P, "find", self); - TruffleString subStr = castNode.cast(inliningTarget, sub, ErrorMessages.MUST_BE_STR_NOT_P, sub); + TruffleString selfStr = cast2Node.cast(inliningTarget, self, ErrorMessages.REQUIRES_STR_OBJECT_BUT_RECEIVED_P, "find", self); + TruffleString subStr = cast1Node.cast(inliningTarget, sub, ErrorMessages.MUST_BE_STR_NOT_P, sub); return find(selfStr, subStr, start, end, codePointLengthNode, indexOfStringNode); } } @@ -845,11 +851,12 @@ static int count(TruffleString self, TruffleString sub, int start, int end, @Specialization static int count(Object self, Object sub, int start, int end, @Bind Node inliningTarget, - @Cached CastToTruffleStringCheckedNode castNode, + @Cached CastToTruffleStringChecked1Node cast1Node, + @Cached CastToTruffleStringChecked2Node cast2Node, @Shared("cpLen") @Cached TruffleString.CodePointLengthNode codePointLengthNode, @Shared("indexOf") @Cached TruffleString.IndexOfStringNode indexOfStringNode) { - TruffleString selfStr = castNode.cast(inliningTarget, self, ErrorMessages.REQUIRES_STR_OBJECT_BUT_RECEIVED_P, "count", self); - TruffleString subStr = castNode.cast(inliningTarget, sub, ErrorMessages.MUST_BE_STR_NOT_P, sub); + TruffleString selfStr = cast2Node.cast(inliningTarget, self, ErrorMessages.REQUIRES_STR_OBJECT_BUT_RECEIVED_P, "count", self); + TruffleString subStr = cast1Node.cast(inliningTarget, sub, ErrorMessages.MUST_BE_STR_NOT_P, sub); return count(selfStr, subStr, start, end, codePointLengthNode, indexOfStringNode); } } @@ -862,7 +869,7 @@ public abstract static class JoinNode extends PythonBinaryBuiltinNode { @Specialization static TruffleString join(VirtualFrame frame, Object self, Object iterable, @Bind Node inliningTarget, - @Cached CastToTruffleStringCheckedNode castToStringNode, + @Cached CastToTruffleStringChecked2Node castToStringNode, @Cached JoinInternalNode join) { return join.execute(frame, castToStringNode.cast(inliningTarget, self, ErrorMessages.REQUIRES_STR_OBJECT_BUT_RECEIVED_P, "join", self), iterable); } @@ -906,7 +913,7 @@ static TruffleString lower(TruffleString self, @Specialization static Object doGeneric(VirtualFrame frame, Object self, @Bind Node inliningTarget, - @Cached CastToTruffleStringCheckedNode castToStringNode, + @Cached CastToTruffleStringChecked2Node castToStringNode, @Cached LowerNode lowerNode) { return lowerNode.execute(frame, castToStringNode.cast(inliningTarget, self, ErrorMessages.REQUIRES_STR_OBJECT_BUT_RECEIVED_P, "lower", self)); } @@ -962,7 +969,7 @@ static TruffleString upper(TruffleString self, @Specialization static Object doGeneric(VirtualFrame frame, Object self, @Bind Node inliningTarget, - @Cached CastToTruffleStringCheckedNode castToStringNode, + @Cached CastToTruffleStringChecked2Node castToStringNode, @Cached UpperNode upperNode) { return upperNode.execute(frame, castToStringNode.cast(inliningTarget, self, ErrorMessages.REQUIRES_STR_OBJECT_BUT_RECEIVED_P, "upper", self)); } @@ -989,9 +996,9 @@ public abstract static class MakeTransNode extends PythonQuaternaryBuiltinNode { @SuppressWarnings("unused") static PDict doString(VirtualFrame frame, Object cls, Object from, Object to, Object z, @Bind Node inliningTarget, - @Exclusive @Cached CastToTruffleStringCheckedNode castFromNode, - @Exclusive @Cached CastToTruffleStringCheckedNode castToNode, - @Exclusive @Cached CastToTruffleStringCheckedNode castZNode, + @Exclusive @Cached CastToTruffleStringChecked0Node castFromNode, + @Exclusive @Cached CastToTruffleStringChecked3Node castToNode, + @Exclusive @Cached CastToTruffleStringChecked4Node castZNode, @Shared("cpLen") @Cached TruffleString.CodePointLengthNode codePointLengthNode, @Cached TruffleString.CreateCodePointIteratorNode createCodePointIteratorNode, @Cached TruffleStringIterator.NextNode nextNode, @@ -1035,7 +1042,7 @@ static PDict doString(VirtualFrame frame, Object cls, Object from, Object to, Ob @SuppressWarnings("unused") static PDict doDict(VirtualFrame frame, Object cls, PDict from, Object to, Object z, @Bind Node inliningTarget, - @Exclusive @Cached CastToTruffleStringCheckedNode cast, + @Exclusive @Cached CastToTruffleStringChecked0Node cast, @Shared("cpLen") @Cached TruffleString.CodePointLengthNode codePointLengthNode, @Cached TruffleString.CodePointAtIndexNode codePointAtIndexNode, @Exclusive @Cached HashingStorageSetItem setHashingStorageItem, @@ -1101,7 +1108,7 @@ static TruffleString doStringString(TruffleString self, TruffleString table, @Specialization static TruffleString doGeneric(VirtualFrame frame, Object self, Object table, @Bind Node inliningTarget, - @Cached CastToTruffleStringCheckedNode castSelfNode, + @Cached CastToTruffleStringChecked2Node castSelfNode, @Cached PyObjectGetItem getItemNode, @Cached GetClassNode getClassNode, @Cached IsSubtypeNode isSubtypeNode, @@ -1186,8 +1193,8 @@ public abstract static class PartitionNode extends PythonBinaryBuiltinNode { @Specialization static PTuple doGeneric(Object self, Object sep, @Bind Node inliningTarget, - @Cached CastToTruffleStringCheckedNode castSelfNode, - @Cached CastToTruffleStringCheckedNode castSepNode, + @Cached CastToTruffleStringChecked2Node castSelfNode, + @Cached CastToTruffleStringChecked1Node castSepNode, @Cached TruffleString.CodePointLengthNode codePointLengthNode, @Cached TruffleString.IndexOfStringNode indexOfStringNode, @Cached TruffleString.SubstringNode substringNode, @@ -1222,8 +1229,8 @@ public abstract static class RPartitionNode extends PythonBinaryBuiltinNode { @Specialization static Object doGeneric(Object self, Object sep, @Bind Node inliningTarget, - @Cached CastToTruffleStringCheckedNode castSelfNode, - @Cached CastToTruffleStringCheckedNode castSepNode, + @Cached CastToTruffleStringChecked2Node castSelfNode, + @Cached CastToTruffleStringChecked1Node castSepNode, @Cached TruffleString.CodePointLengthNode codePointLengthNode, @Cached TruffleString.LastIndexOfStringNode lastIndexOfStringNode, @Cached TruffleString.SubstringNode substringNode, @@ -1540,7 +1547,7 @@ private static Matcher getMatcher(String self) { @Specialization(replaces = {"doString", "doStringKeepends"}) static PList doGeneric(Object self, Object keepends, @Bind Node inliningTarget, - @Cached CastToTruffleStringCheckedNode castSelfNode, + @Cached CastToTruffleStringChecked2Node castSelfNode, @Cached CastToJavaIntExactNode castToJavaIntNode, @Shared("ts2js") @Cached TruffleString.ToJavaStringNode toJavaStringNode, @Shared("js2ts") @Cached TruffleString.FromJavaStringNode fromJavaStringNode, @@ -1571,12 +1578,13 @@ static TruffleString doReplace(TruffleString self, TruffleString old, TruffleStr @Specialization static TruffleString doGeneric(VirtualFrame frame, Object self, Object old, Object with, Object maxCount, @Bind Node inliningTarget, - @Cached CastToTruffleStringCheckedNode castSelfNode, + @Cached CastToTruffleStringChecked2Node cast2Node, + @Cached CastToTruffleStringChecked4Node cast4Node, @Cached PyNumberAsSizeNode asSizeNode, @Shared("replace") @Cached StringReplaceNode replaceNode) { - TruffleString selfStr = castSelfNode.cast(inliningTarget, self, ErrorMessages.REQUIRES_STR_OBJECT_BUT_RECEIVED_P, "replace", self); - TruffleString oldStr = castSelfNode.cast(inliningTarget, old, ErrorMessages.S_BRACKETS_ARG_S_MUST_BE_S_NOT_P, "replace", "1", "str", old); - TruffleString withStr = castSelfNode.cast(inliningTarget, with, ErrorMessages.S_BRACKETS_ARG_S_MUST_BE_S_NOT_P, "replace", "2", "str", with); + TruffleString selfStr = cast2Node.cast(inliningTarget, self, ErrorMessages.REQUIRES_STR_OBJECT_BUT_RECEIVED_P, "replace", self); + TruffleString oldStr = cast4Node.cast(inliningTarget, old, ErrorMessages.S_BRACKETS_ARG_S_MUST_BE_S_NOT_P, "replace", "1", "str", old); + TruffleString withStr = cast4Node.cast(inliningTarget, with, ErrorMessages.S_BRACKETS_ARG_S_MUST_BE_S_NOT_P, "replace", "2", "str", with); int iMaxCount; if (PGuards.isPNone(maxCount)) { iMaxCount = -1; @@ -1610,8 +1618,8 @@ static TruffleString doStringNone(TruffleString self, @SuppressWarnings("unused" @Specialization(replaces = {"doStringString", "doStringNone"}) static TruffleString doGeneric(Object self, Object chars, @Bind Node inliningTarget, - @Cached CastToTruffleStringCheckedNode castSelfNode, - @Cached CastToTruffleStringCheckedNode castCharsNode, + @Cached CastToTruffleStringChecked2Node castSelfNode, + @Cached CastToTruffleStringChecked2Node castCharsNode, @Shared("cpLen") @Cached TruffleString.CodePointLengthNode codePointLengthNode, @Shared("cpAtIndex") @Cached TruffleString.CodePointAtIndexNode codePointAtIndexNode, @Shared("indexOf") @Cached TruffleString.IndexOfCodePointNode indexOfCodePointNode, @@ -1648,8 +1656,8 @@ static TruffleString doStringNone(TruffleString self, @SuppressWarnings("unused" @Specialization(replaces = {"doStringString", "doStringNone"}) static TruffleString doGeneric(Object self, Object chars, @Bind Node inliningTarget, - @Cached CastToTruffleStringCheckedNode castSelfNode, - @Cached CastToTruffleStringCheckedNode castCharsNode, + @Cached CastToTruffleStringChecked2Node castSelfNode, + @Cached CastToTruffleStringChecked2Node castCharsNode, @Shared("cpLen") @Cached TruffleString.CodePointLengthNode codePointLengthNode, @Shared("cpAtIndex") @Cached TruffleString.CodePointAtIndexNode codePointAtIndexNode, @Shared("indexOf") @Cached TruffleString.IndexOfCodePointNode indexOfCodePointNode, @@ -1686,8 +1694,8 @@ static TruffleString doStringNone(TruffleString self, @SuppressWarnings("unused" @Specialization(replaces = {"doStringString", "doStringNone"}) static TruffleString doGeneric(Object self, Object chars, @Bind Node inliningTarget, - @Cached CastToTruffleStringCheckedNode castSelfNode, - @Cached CastToTruffleStringCheckedNode castCharsNode, + @Cached CastToTruffleStringChecked2Node castSelfNode, + @Cached CastToTruffleStringChecked2Node castCharsNode, @Shared("cpLen") @Cached TruffleString.CodePointLengthNode codePointLengthNode, @Shared("cpAtIndex") @Cached TruffleString.CodePointAtIndexNode codePointAtIndexNode, @Shared("indexOf") @Cached TruffleString.IndexOfCodePointNode indexOfCodePointNode, @@ -1750,12 +1758,13 @@ protected ArgumentClinicProvider getArgumentClinic() { @Specialization static int index(Object selfObj, Object subObj, int start, int end, @Bind Node inliningTarget, - @Cached CastToTruffleStringCheckedNode castNode, + @Cached CastToTruffleStringChecked1Node cast1Node, + @Cached CastToTruffleStringChecked2Node cast2Node, @Cached TruffleString.CodePointLengthNode codePointLengthNode, @Cached TruffleString.IndexOfStringNode indexOfStringNode, @Cached PRaiseNode raiseNode) { - TruffleString self = castNode.cast(inliningTarget, selfObj, ErrorMessages.REQUIRES_STR_OBJECT_BUT_RECEIVED_P, "index", selfObj); - TruffleString sub = castNode.cast(inliningTarget, subObj, ErrorMessages.MUST_BE_STR_NOT_P, subObj); + TruffleString self = cast2Node.cast(inliningTarget, selfObj, ErrorMessages.REQUIRES_STR_OBJECT_BUT_RECEIVED_P, "index", selfObj); + TruffleString sub = cast1Node.cast(inliningTarget, subObj, ErrorMessages.MUST_BE_STR_NOT_P, subObj); int idx = indexOf(self, sub, start, end, codePointLengthNode, indexOfStringNode); if (idx < 0) { @@ -1778,12 +1787,13 @@ protected ArgumentClinicProvider getArgumentClinic() { @Specialization static int rindex(Object selfObj, Object subObj, int start, int end, @Bind Node inliningTarget, - @Cached CastToTruffleStringCheckedNode castNode, + @Cached CastToTruffleStringChecked1Node cast1Node, + @Cached CastToTruffleStringChecked2Node cast2Node, @Cached TruffleString.CodePointLengthNode codePointLengthNode, @Cached TruffleString.LastIndexOfStringNode lastIndexOfStringNode, @Cached PRaiseNode raiseNode) { - TruffleString self = castNode.cast(inliningTarget, selfObj, ErrorMessages.REQUIRES_STR_OBJECT_BUT_RECEIVED_P, "rindex", selfObj); - TruffleString sub = castNode.cast(inliningTarget, subObj, ErrorMessages.MUST_BE_STR_NOT_P, subObj); + TruffleString self = cast2Node.cast(inliningTarget, selfObj, ErrorMessages.REQUIRES_STR_OBJECT_BUT_RECEIVED_P, "rindex", selfObj); + TruffleString sub = cast1Node.cast(inliningTarget, subObj, ErrorMessages.MUST_BE_STR_NOT_P, subObj); int idx = lastIndexOf(self, sub, start, end, codePointLengthNode, lastIndexOfStringNode); if (idx < 0) { throw raiseNode.raise(inliningTarget, ValueError, ErrorMessages.SUBSTRING_NOT_FOUND); @@ -1814,7 +1824,7 @@ protected ArgumentClinicProvider getArgumentClinic() { @Specialization static Object doIt(VirtualFrame frame, Object selfObj, TruffleString encoding, TruffleString errors, @Bind Node inliningTarget, - @Cached CastToTruffleStringCheckedNode castSelfNode, + @Cached CastToTruffleStringChecked2Node castSelfNode, @Cached CodecsModuleBuiltins.EncodeNode encodeNode, @Cached SequenceStorageNodes.CopyNode copyNode, @Cached InlinedBranchProfile convertByteArray, @@ -1850,7 +1860,7 @@ static TruffleString doStringIntPositive(TruffleString left, int right, @Specialization static TruffleString doGeneric(Object self, int right, @Bind Node inliningTarget, - @Cached CastToTruffleStringCheckedNode castSelfNode, + @Cached CastToTruffleStringChecked2Node castSelfNode, @Cached InlinedConditionProfile isNegativeProfile, @Shared("repeat") @Cached TruffleString.RepeatNode repeatNode) { TruffleString selfStr = castSelfNode.cast(inliningTarget, self, ErrorMessages.REQUIRES_STR_OBJECT_BUT_RECEIVED_P, "index", self); @@ -1900,7 +1910,7 @@ boolean doString(TruffleString self, @Specialization(replaces = "doString") boolean doGeneric(Object self, @Bind Node inliningTarget, - @Cached CastToTruffleStringCheckedNode castSelfNode, + @Cached CastToTruffleStringChecked2Node castSelfNode, @Shared("getCodeRange") @Cached TruffleString.GetCodeRangeNode getCodeRangeNode) { return doString(castSelfNode.cast(inliningTarget, self, ErrorMessages.REQUIRES_STR_OBJECT_BUT_RECEIVED_P, "isascii", self), getCodeRangeNode); } @@ -1910,7 +1920,7 @@ abstract static class IsCategoryBaseNode extends PythonUnaryBuiltinNode { @Specialization boolean doGeneric(Object selfObj, @Bind Node inliningTarget, - @Cached CastToTruffleStringCheckedNode castSelfNode, + @Cached CastToTruffleStringChecked2Node castSelfNode, @Cached InlinedConditionProfile isEmptyProfile, @Cached TruffleString.CreateCodePointIteratorNode createCodePointIteratorNode, @Cached TruffleStringIterator.NextNode nextNode) { @@ -2026,7 +2036,7 @@ public abstract static class IsIdentifierNode extends PythonUnaryBuiltinNode { @Specialization boolean doGeneric(Object selfObj, @Bind Node inliningTarget, - @Cached CastToTruffleStringCheckedNode castSelfNode, + @Cached CastToTruffleStringChecked2Node castSelfNode, @Cached StringUtils.IsIdentifierNode isIdentifierNode) { TruffleString self = castSelfNode.cast(inliningTarget, selfObj, ErrorMessages.REQUIRES_STR_OBJECT_BUT_RECEIVED_P, "isidentifier", selfObj); return isIdentifierNode.execute(inliningTarget, self); @@ -2049,7 +2059,7 @@ private static boolean isLower(int codePoint) { @Specialization static boolean doIt(Object selfObj, @Bind Node inliningTarget, - @Cached CastToTruffleStringCheckedNode castSelfNode, + @Cached CastToTruffleStringChecked2Node castSelfNode, @Cached TruffleString.CreateCodePointIteratorNode createCodePointIteratorNode, @Cached TruffleStringIterator.NextNode nextNode) { TruffleString self = castSelfNode.cast(inliningTarget, selfObj, ErrorMessages.REQUIRES_STR_OBJECT_BUT_RECEIVED_P, "islower", selfObj); @@ -2146,7 +2156,7 @@ private static boolean isLower(int codePoint) { @Specialization(replaces = "doString") static boolean doGeneric(Object self, @Bind Node inliningTarget, - @Cached CastToTruffleStringCheckedNode castSelfNode, + @Cached CastToTruffleStringChecked2Node castSelfNode, @Shared("createCpIterator") @Cached TruffleString.CreateCodePointIteratorNode createCodePointIteratorNode, @Shared("next") @Cached TruffleStringIterator.NextNode nextNode) { return doString(castSelfNode.cast(inliningTarget, self, ErrorMessages.REQUIRES_STR_OBJECT_BUT_RECEIVED_P, "istitle", self), createCodePointIteratorNode, nextNode); @@ -2187,7 +2197,7 @@ private static boolean isUpper(int codePoint) { @Specialization(replaces = "doString") static boolean doGeneric(Object self, @Bind Node inliningTarget, - @Cached CastToTruffleStringCheckedNode castSelfNode, + @Cached CastToTruffleStringChecked2Node castSelfNode, @Shared("createCpIterator") @Cached TruffleString.CreateCodePointIteratorNode createCodePointIteratorNode, @Shared("next") @Cached TruffleStringIterator.NextNode nextNode) { return doString(castSelfNode.cast(inliningTarget, self, ErrorMessages.REQUIRES_STR_OBJECT_BUT_RECEIVED_P, "isupper", self), createCodePointIteratorNode, nextNode); @@ -2201,7 +2211,7 @@ abstract static class ZFillNode extends PythonBinaryBuiltinNode { @Specialization static TruffleString doGeneric(VirtualFrame frame, Object selfObj, Object widthObj, @Bind Node inliningTarget, - @Cached CastToTruffleStringCheckedNode castSelfNode, + @Cached CastToTruffleStringChecked2Node castSelfNode, @Cached PyNumberAsSizeNode asSizeNode, @Cached TruffleString.CodePointLengthNode codePointLengthNode, @Cached TruffleString.CodePointAtIndexNode codePointAtIndexNode, @@ -2300,9 +2310,9 @@ abstract static class CenterNode extends PythonBuiltinNode { @Specialization TruffleString doIt(VirtualFrame frame, Object selfObj, Object width, Object fill, @Bind Node inliningTarget, - @Cached CastToTruffleStringCheckedNode castSelfNode, + @Cached CastToTruffleStringChecked2Node castSelfNode, @Cached PyNumberAsSizeNode asSizeNode, - @Cached CastToTruffleStringCheckedNode castFillNode, + @Cached CastToTruffleStringChecked1Node castFillNode, @Cached TruffleString.CodePointLengthNode codePointLengthNode, @Cached TruffleString.CodePointAtIndexNode codePointAtIndexNode, @Cached TruffleStringBuilder.AppendCodePointNode appendCodePointNode, @@ -2441,7 +2451,7 @@ public abstract static class StrSqItemNode extends SqItemBuiltinNode { @Specialization static Object doIt(VirtualFrame frame, Object self, int index, @Bind Node inliningTarget, - @Cached CastToTruffleStringCheckedNode castToString, + @Cached CastToTruffleStringChecked3Node castToString, @Cached TruffleString.SubstringNode substringNode) { TruffleString str = castToString.cast(inliningTarget, self, ErrorMessages.DESCRIPTOR_S_REQUIRES_S_OBJ_RECEIVED_P, T___GETITEM__, "str", self); return substringNode.execute(str, index, 1, TS_ENCODING, false); @@ -2455,7 +2465,7 @@ public abstract static class StrGetItemNode extends MpSubscriptBuiltinNode { @Specialization static TruffleString doString(VirtualFrame frame, Object self, PSlice slice, @Bind Node inliningTarget, - @Exclusive @Cached CastToTruffleStringCheckedNode castToString, + @Exclusive @Cached CastToTruffleStringChecked3Node castToString, @Cached CoerceToIntSlice sliceCast, @Cached ComputeIndices compute, @Cached StrGetItemNodeWithSlice getItemNodeWithSlice, @@ -2468,7 +2478,7 @@ static TruffleString doString(VirtualFrame frame, Object self, PSlice slice, @Specialization(guards = "!isPSlice(idx)") static TruffleString doString(VirtualFrame frame, Object self, Object idx, @Bind Node inliningTarget, - @Exclusive @Cached CastToTruffleStringCheckedNode castToString, + @Exclusive @Cached CastToTruffleStringChecked3Node castToString, @Cached PyIndexCheckNode indexCheckNode, @Cached PyNumberAsSizeNode asSizeNode, @Shared("cpLen") @Cached TruffleString.CodePointLengthNode codePointLengthNode, @@ -2502,7 +2512,7 @@ public abstract static class IterNode extends PythonUnaryBuiltinNode { @Specialization static PStringIterator doGeneric(Object self, @Bind Node inliningTarget, - @Cached CastToTruffleStringCheckedNode castSelfNode) { + @Cached CastToTruffleStringChecked2Node castSelfNode) { TruffleString string = castSelfNode.cast(inliningTarget, self, ErrorMessages.REQUIRES_STR_OBJECT_BUT_RECEIVED_P, T___ITER__, self); return PFactory.createStringIterator(PythonLanguage.get(inliningTarget), string); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/StringNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/StringNodes.java index 5c520e6cb7..780c0828fb 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/StringNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/StringNodes.java @@ -202,7 +202,7 @@ static int doNativeObject(PythonNativeObject x, @InliningCutoff static int other(Object x, @Bind Node inliningTarget, - @Cached CastToTruffleStringCheckedNode cast, + @Cached CastToTruffleStringChecked2Node cast, @Shared @Cached TruffleString.CodePointLengthNode codePointLengthNode) { TruffleString tstring = cast.cast(inliningTarget, x, ErrorMessages.DESCRIPTOR_REQUIRES_S_OBJ_RECEIVED_P, "str", x); return doString(tstring, codePointLengthNode); @@ -242,30 +242,148 @@ static String doConvert(Node inliningTarget, Object self, TruffleString errMsgFo } } + // One variant per arity to avoid extra Object[] allocations @GenerateUncached @GenerateInline @GenerateCached(false) - public abstract static class CastToTruffleStringCheckedNode extends PNodeWithContext { - public final TruffleString cast(Node inliningTarget, Object object, TruffleString errMsgFormat, Object... errMsgArgs) { - return execute(inliningTarget, object, errMsgFormat, errMsgArgs); + public abstract static class CastToTruffleStringChecked0Node extends PNodeWithContext { + public final TruffleString cast(Node inliningTarget, Object object, TruffleString errMsg) { + return execute(inliningTarget, object, errMsg); } - abstract TruffleString execute(Node inliningTarget, Object object, TruffleString errMsgFormat, Object[] errMsgArgs); + abstract TruffleString execute(Node inliningTarget, Object object, TruffleString errMsg); + @SuppressWarnings("unused") @Specialization - static TruffleString doTruffleString(TruffleString self, @SuppressWarnings("unused") TruffleString errMsgFormat, @SuppressWarnings("unused") Object[] errMsgArgs) { + static TruffleString doTruffleString(TruffleString self, TruffleString errMsg) { return self; } @Specialization(guards = "!isTruffleString(self)") @InliningCutoff - static TruffleString doConvert(Node inliningTarget, Object self, TruffleString errMsgFormat, Object[] errMsgArgs, + static TruffleString doConvert(Node inliningTarget, Object self, TruffleString errMsg, @Cached CastToTruffleStringNode castToTruffleStringNode, @Cached PRaiseNode raiseNode) { try { return castToTruffleStringNode.execute(inliningTarget, self); } catch (CannotCastException e) { - throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.TypeError, errMsgFormat, errMsgArgs); + throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.TypeError, errMsg); + } + } + } + + @GenerateUncached + @GenerateInline + @GenerateCached(false) + public abstract static class CastToTruffleStringChecked1Node extends PNodeWithContext { + public final TruffleString cast(Node inliningTarget, Object object, TruffleString errMsgFormat, Object errMsgArg1) { + return execute(inliningTarget, object, errMsgFormat, errMsgArg1); + } + + abstract TruffleString execute(Node inliningTarget, Object object, TruffleString errMsgFormat, Object errMsgArg1); + + @SuppressWarnings("unused") + @Specialization + static TruffleString doTruffleString(TruffleString self, TruffleString errMsgFormat, Object errMsgArg1) { + return self; + } + + @Specialization(guards = "!isTruffleString(self)") + @InliningCutoff + static TruffleString doConvert(Node inliningTarget, Object self, TruffleString errMsgFormat, Object errMsgArg1, + @Cached CastToTruffleStringNode castToTruffleStringNode, + @Cached PRaiseNode raiseNode) { + try { + return castToTruffleStringNode.execute(inliningTarget, self); + } catch (CannotCastException e) { + throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.TypeError, errMsgFormat, errMsgArg1); + } + } + } + + @GenerateUncached + @GenerateInline + @GenerateCached(false) + public abstract static class CastToTruffleStringChecked2Node extends PNodeWithContext { + public final TruffleString cast(Node inliningTarget, Object object, TruffleString errMsgFormat, Object errMsgArg1, Object errMsgArg2) { + return execute(inliningTarget, object, errMsgFormat, errMsgArg1, errMsgArg2); + } + + abstract TruffleString execute(Node inliningTarget, Object object, TruffleString errMsgFormat, Object errMsgArg1, Object errMsgArg2); + + @SuppressWarnings("unused") + @Specialization + static TruffleString doTruffleString(TruffleString self, TruffleString errMsgFormat, Object errMsgArg1, Object errMsgArg2) { + return self; + } + + @Specialization(guards = "!isTruffleString(self)") + @InliningCutoff + static TruffleString doConvert(Node inliningTarget, Object self, TruffleString errMsgFormat, Object errMsgArg1, Object errMsgArg2, + @Cached CastToTruffleStringNode castToTruffleStringNode, + @Cached PRaiseNode raiseNode) { + try { + return castToTruffleStringNode.execute(inliningTarget, self); + } catch (CannotCastException e) { + throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.TypeError, errMsgFormat, errMsgArg1, errMsgArg2); + } + } + } + + @GenerateUncached + @GenerateInline + @GenerateCached(false) + public abstract static class CastToTruffleStringChecked3Node extends PNodeWithContext { + public final TruffleString cast(Node inliningTarget, Object object, TruffleString errMsgFormat, Object errMsgArg1, Object errMsgArg2, Object errMsgArg3) { + return execute(inliningTarget, object, errMsgFormat, errMsgArg1, errMsgArg2, errMsgArg3); + } + + abstract TruffleString execute(Node inliningTarget, Object object, TruffleString errMsgFormat, Object errMsgArg1, Object errMsgArg2, Object errMsgArg3); + + @SuppressWarnings("unused") + @Specialization + static TruffleString doTruffleString(TruffleString self, TruffleString errMsgFormat, Object errMsgArg1, Object errMsgArg2, Object errMsgArg3) { + return self; + } + + @Specialization(guards = "!isTruffleString(self)") + @InliningCutoff + static TruffleString doConvert(Node inliningTarget, Object self, TruffleString errMsgFormat, Object errMsgArg1, Object errMsgArg2, Object errMsgArg3, + @Cached CastToTruffleStringNode castToTruffleStringNode, + @Cached PRaiseNode raiseNode) { + try { + return castToTruffleStringNode.execute(inliningTarget, self); + } catch (CannotCastException e) { + throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.TypeError, errMsgFormat, errMsgArg1, errMsgArg2, errMsgArg3); + } + } + } + + @GenerateUncached + @GenerateInline + @GenerateCached(false) + public abstract static class CastToTruffleStringChecked4Node extends PNodeWithContext { + public final TruffleString cast(Node inliningTarget, Object object, TruffleString errMsgFormat, Object errMsgArg1, Object errMsgArg2, Object errMsgArg3, Object errMsgArg4) { + return execute(inliningTarget, object, errMsgFormat, errMsgArg1, errMsgArg2, errMsgArg3, errMsgArg4); + } + + abstract TruffleString execute(Node inliningTarget, Object object, TruffleString errMsgFormat, Object errMsgArg1, Object errMsgArg2, Object errMsgArg3, Object errMsgArg4); + + @SuppressWarnings("unused") + @Specialization + static TruffleString doTruffleString(TruffleString self, TruffleString errMsgFormat, Object errMsgArg1, Object errMsgArg2, Object errMsgArg3, Object errMsgArg4) { + return self; + } + + @Specialization(guards = "!isTruffleString(self)") + @InliningCutoff + static TruffleString doConvert(Node inliningTarget, Object self, TruffleString errMsgFormat, Object errMsgArg1, Object errMsgArg2, Object errMsgArg3, Object errMsgArg4, + @Cached CastToTruffleStringNode castToTruffleStringNode, + @Cached PRaiseNode raiseNode) { + try { + return castToTruffleStringNode.execute(inliningTarget, self); + } catch (CannotCastException e) { + throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.TypeError, errMsgFormat, errMsgArg1, errMsgArg2, errMsgArg3, errMsgArg4); } } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/superobject/SuperBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/superobject/SuperBuiltins.java index 846bb62e4c..97e56e08f4 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/superobject/SuperBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/superobject/SuperBuiltins.java @@ -67,7 +67,7 @@ import com.oracle.graal.python.builtins.objects.function.PKeyword; import com.oracle.graal.python.builtins.objects.object.ObjectBuiltins; import com.oracle.graal.python.builtins.objects.object.ObjectBuiltinsFactory; -import com.oracle.graal.python.builtins.objects.str.StringNodes.CastToTruffleStringCheckedNode; +import com.oracle.graal.python.builtins.objects.str.StringNodes.CastToTruffleStringChecked1Node; import com.oracle.graal.python.builtins.objects.str.StringUtils.SimpleTruffleStringFormatNode; import com.oracle.graal.python.builtins.objects.superobject.SuperBuiltinsFactory.GetObjectNodeGen; import com.oracle.graal.python.builtins.objects.superobject.SuperBuiltinsFactory.GetTypeNodeGen; @@ -494,7 +494,7 @@ Object get(VirtualFrame frame, SuperObject self, Object attr, @Cached GetObjectSlotsNode getSlotsNode, @Cached TruffleString.EqualNode equalNode, @Cached GetObjectTypeNode getObjectType, - @Cached CastToTruffleStringCheckedNode castToTruffleStringNode, + @Cached CastToTruffleStringChecked1Node castToTruffleStringNode, @Cached InlinedConditionProfile hasDescrGetProfile, @Cached InlinedConditionProfile getObjectIsStartObjectProfile, @Cached IsForeignObjectNode isForeignObjectNode, diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/argument/keywords/ConcatDictToStorageNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/argument/keywords/ConcatDictToStorageNode.java index 48130441df..b6247f006a 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/argument/keywords/ConcatDictToStorageNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/argument/keywords/ConcatDictToStorageNode.java @@ -100,7 +100,7 @@ static HashingStorage doBuiltinDict(VirtualFrame frame, HashingStorage dest, PDi @Cached HashingStorageNodes.HashingStorageIteratorKey iterKey, @Cached HashingStorageNodes.HashingStorageIteratorValue iterValue, @Exclusive @Cached InlinedLoopConditionProfile loopProfile, - @Exclusive @Cached StringNodes.CastToTruffleStringCheckedNode castToStringNode, + @Exclusive @Cached StringNodes.CastToTruffleStringChecked0Node castToStringNode, @Exclusive @Cached InlinedBranchProfile sameKeyProfile) throws SameDictKeyException { HashingStorage result = dest; HashingStorage otherStorage = other.getDictStorage(); @@ -129,7 +129,7 @@ static HashingStorage doMapping(VirtualFrame frame, HashingStorage dest, Object @SuppressWarnings("unused") @Exclusive @Cached GetPythonObjectClassNode getClassNode, @SuppressWarnings("unused") @Exclusive @Cached GetCachedTpSlotsNode getSlots, @Exclusive @Cached InlinedBranchProfile sameKeyProfile, - @Exclusive @Cached StringNodes.CastToTruffleStringCheckedNode castToStringNode, + @Exclusive @Cached StringNodes.CastToTruffleStringChecked0Node castToStringNode, @Cached PyObjectCallMethodObjArgs callKeys, @Cached IsBuiltinObjectProfile errorProfile, @Cached ListNodes.FastConstructListNode asList, From b1bf8b3e7511ea3d2e2f84c5721f6c866344b0ae Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Thu, 10 Jul 2025 13:57:16 +0200 Subject: [PATCH 09/38] Fix docs --- docs/contributor/CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/contributor/CONTRIBUTING.md b/docs/contributor/CONTRIBUTING.md index 7b5d80b559..68b47fa603 100644 --- a/docs/contributor/CONTRIBUTING.md +++ b/docs/contributor/CONTRIBUTING.md @@ -127,10 +127,10 @@ If the IDE was initialized properly by using the command mentioned above, the ex Both of these commands also work when you have a `graalpy` executable, e.g. inside a `venv`. -For debugging the C API and native extensions, first make sure you rebuild (`mx clean` first!) graalpything with the environment variable `CFLAGS=-g` set. +For debugging the C API and native extensions, first make sure you rebuild (`mx clean` first!) graalpython with the environment variable `CFLAGS=-g` set. This will keep debug symbols in our C API implementation which should allow you to use `gdb` or [`rr`](https://rr-project.org/) to debug. When you build an SVM image, debugging the entire application is possible, and there are [docs](https://www.graalvm.org/reference-manual/native-image/guides/debug-native-image-process/) to see Java code when inside the native debugger. -Make sure you find and keep the `libpythonvm.so.debug` file around next to your GraalPy build, you can find it somewhere under `graal/sdk/mxbuild`. +Make sure you find and keep the `libpythonvm.so.debug` file around next to your GraalPy build, you can find it under `mxbuild/*/libpythonvm`. ## Advanced Commands to Develop and Debug From b748204b539f5b574c47d07ac7ffea11f082a21f Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Thu, 10 Jul 2025 14:02:36 +0200 Subject: [PATCH 10/38] Remove duplicate specialization in ModuleBuiltins.ModuleGetattributeNode * CastToTruffleStringChecked1Node is little overhead. --- .../builtins/objects/module/ModuleBuiltins.java | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/ModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/ModuleBuiltins.java index 4ce69ecb8b..89a7847d63 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/ModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/ModuleBuiltins.java @@ -105,7 +105,6 @@ import com.oracle.graal.python.runtime.PythonContext; import com.oracle.graal.python.runtime.exception.PException; import com.oracle.graal.python.runtime.object.PFactory; -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.Cached.Exclusive; @@ -253,23 +252,11 @@ private static PDict createDict(Node inliningTarget, PythonModule self, SetDictN @GenerateNodeFactory public abstract static class ModuleGetattributeNode extends GetAttrBuiltinNode { @Specialization - static Object getattributeStr(VirtualFrame frame, PythonModule self, TruffleString key, - @Shared @Cached ObjectBuiltins.GetAttributeNode objectGetattrNode, - @Shared @Cached HandleGetattrExceptionNode handleException) { - try { - return objectGetattrNode.execute(frame, self, key); - } catch (PException e) { - return handleException.execute(frame, self, key, e); - } - } - - @Specialization(replaces = "getattributeStr") - @InliningCutoff static Object getattribute(VirtualFrame frame, PythonModule self, Object keyObj, @Bind Node inliningTarget, @Cached CastToTruffleStringChecked1Node castKeyToStringNode, - @Shared @Cached ObjectBuiltins.GetAttributeNode objectGetattrNode, - @Shared @Cached HandleGetattrExceptionNode handleException) { + @Cached ObjectBuiltins.GetAttributeNode objectGetattrNode, + @Cached HandleGetattrExceptionNode handleException) { TruffleString key = castKeyToStringNode.cast(inliningTarget, keyObj, ErrorMessages.ATTR_NAME_MUST_BE_STRING, keyObj); try { return objectGetattrNode.execute(frame, self, key); From 120a1768ad4dc40b9e0df1093b49935e48585338 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Thu, 10 Jul 2025 14:07:07 +0200 Subject: [PATCH 11/38] Use CastToTruffleStringChecked*Node instead of CastToTruffleStringNode * Much better for host inlining as CastToTruffleStringChecked*Node has a 1 @InliningCutoff but CastToTruffleStringNode has 3 @InliningCutoff. --- .../builtins/objects/module/ModuleBuiltins.java | 4 ++-- .../builtins/objects/object/ObjectBuiltins.java | 11 +++-------- .../python/builtins/objects/type/TypeBuiltins.java | 10 +++------- .../python/nodes/util/CastToTruffleStringNode.java | 6 ++++++ 4 files changed, 14 insertions(+), 17 deletions(-) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/ModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/ModuleBuiltins.java index 89a7847d63..0cfc5b81da 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/ModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/ModuleBuiltins.java @@ -254,10 +254,10 @@ public abstract static class ModuleGetattributeNode extends GetAttrBuiltinNode { @Specialization static Object getattribute(VirtualFrame frame, PythonModule self, Object keyObj, @Bind Node inliningTarget, - @Cached CastToTruffleStringChecked1Node castKeyToStringNode, + @Cached CastToTruffleStringChecked1Node castToString, @Cached ObjectBuiltins.GetAttributeNode objectGetattrNode, @Cached HandleGetattrExceptionNode handleException) { - TruffleString key = castKeyToStringNode.cast(inliningTarget, keyObj, ErrorMessages.ATTR_NAME_MUST_BE_STRING, keyObj); + TruffleString key = castToString.cast(inliningTarget, keyObj, ErrorMessages.ATTR_NAME_MUST_BE_STRING, keyObj); try { return objectGetattrNode.execute(frame, self, key); } catch (PException e) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/ObjectBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/ObjectBuiltins.java index 393c7bc4b0..0d53263451 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/ObjectBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/ObjectBuiltins.java @@ -87,6 +87,7 @@ import com.oracle.graal.python.builtins.objects.object.ObjectBuiltinsFactory.GetAttributeNodeFactory; import com.oracle.graal.python.builtins.objects.set.PSet; import com.oracle.graal.python.builtins.objects.set.SetBuiltins; +import com.oracle.graal.python.builtins.objects.str.StringNodes.CastToTruffleStringChecked1Node; import com.oracle.graal.python.builtins.objects.type.PythonBuiltinClass; import com.oracle.graal.python.builtins.objects.type.PythonManagedClass; import com.oracle.graal.python.builtins.objects.type.TpSlots; @@ -139,7 +140,6 @@ import com.oracle.graal.python.nodes.object.GetOrCreateDictNode; import com.oracle.graal.python.nodes.object.IsNode; import com.oracle.graal.python.nodes.object.SetDictNode; -import com.oracle.graal.python.nodes.util.CannotCastException; import com.oracle.graal.python.nodes.util.CastToTruffleStringNode; import com.oracle.graal.python.runtime.ExecutionContext.IndirectCallContext; import com.oracle.graal.python.runtime.IndirectCallData; @@ -510,17 +510,12 @@ Object doIt(VirtualFrame frame, Object object, Object keyObj, @Cached GetClassNode getClassNode, @Cached GetObjectSlotsNode getDescrSlotsNode, @Cached LookupAttributeInMRONode.Dynamic lookup, - @Cached CastToTruffleStringNode castToString, + @Cached CastToTruffleStringChecked1Node castToString, @Cached InlinedBranchProfile hasDescProfile, @Cached InlinedConditionProfile hasDescrGetProfile, @Cached InlinedBranchProfile hasValueProfile, @Cached PRaiseNode raiseNode) { - TruffleString key; - try { - key = castToString.execute(inliningTarget, keyObj); - } catch (CannotCastException e) { - throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.ATTR_NAME_MUST_BE_STRING, keyObj); - } + TruffleString key = castToString.cast(inliningTarget, keyObj, ErrorMessages.ATTR_NAME_MUST_BE_STRING, keyObj); Object type = getClassNode.execute(inliningTarget, object); Object descr = lookup.execute(type, key); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeBuiltins.java index e22a1f2d2d..dee2ab50dd 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeBuiltins.java @@ -103,6 +103,7 @@ import com.oracle.graal.python.builtins.objects.set.PSet; import com.oracle.graal.python.builtins.objects.set.SetBuiltins.UpdateSingleNode; import com.oracle.graal.python.builtins.objects.str.PString; +import com.oracle.graal.python.builtins.objects.str.StringNodes.CastToTruffleStringChecked1Node; import com.oracle.graal.python.builtins.objects.str.StringUtils.SimpleTruffleStringFormatNode; import com.oracle.graal.python.builtins.objects.tuple.PTuple; import com.oracle.graal.python.builtins.objects.type.TpSlots.GetCachedTpSlotsNode; @@ -530,18 +531,13 @@ protected Object doIt(VirtualFrame frame, Object object, Object keyObj, @Cached GetObjectSlotsNode getDescrSlotsNode, @Cached GetObjectSlotsNode getValueSlotsNode, @Cached LookupAttributeInMRONode.Dynamic lookup, - @Cached CastToTruffleStringNode castToString, + @Cached CastToTruffleStringChecked1Node castToString, @Cached InlinedBranchProfile hasDescProfile, @Cached InlinedConditionProfile hasDescrGetProfile, @Cached InlinedBranchProfile hasValueProfile, @Cached InlinedBranchProfile hasNonDescriptorValueProfile, @Cached PRaiseNode raiseNode) { - TruffleString key; - try { - key = castToString.execute(inliningTarget, keyObj); - } catch (CannotCastException e) { - throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.ATTR_NAME_MUST_BE_STRING, keyObj); - } + TruffleString key = castToString.cast(inliningTarget, keyObj, ErrorMessages.ATTR_NAME_MUST_BE_STRING, keyObj); Object type = getClassNode.execute(inliningTarget, object); Object descr = lookup.execute(type, key); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/util/CastToTruffleStringNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/util/CastToTruffleStringNode.java index 0598fa2c93..5ddb860d30 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/util/CastToTruffleStringNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/util/CastToTruffleStringNode.java @@ -50,6 +50,8 @@ import com.oracle.graal.python.builtins.objects.cext.structs.CFields; import com.oracle.graal.python.builtins.objects.cext.structs.CStructAccess; import com.oracle.graal.python.builtins.objects.str.PString; +import com.oracle.graal.python.builtins.objects.str.StringNodes.CastToTruffleStringChecked0Node; +import com.oracle.graal.python.builtins.objects.str.StringNodes.CastToTruffleStringChecked1Node; import com.oracle.graal.python.builtins.objects.str.StringNodes.StringMaterializeNode; import com.oracle.graal.python.nodes.PGuards; import com.oracle.graal.python.nodes.PNodeWithContext; @@ -75,6 +77,10 @@ /** * Casts a Python string to a TruffleString without coercion. ATTENTION: If the cast fails, * because the object is not a Python string, the node will throw a {@link CannotCastException}. + *
+ * Prefer {@link CastToTruffleStringChecked0Node}, {@link CastToTruffleStringChecked1Node}, etc when + * possible as they are much better for host inlining with only 1 @InliningCutoff vs + * 3 @InliningCutoff in this node. */ @GenerateUncached @GenerateInline(inlineByDefault = true) From 592c519da9ca9173fe682c99b2bac8125fb069b6 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Thu, 10 Jul 2025 15:03:28 +0200 Subject: [PATCH 12/38] Remove redundant GetFixedAttributeNode#executeObject() method --- .../graal/python/builtins/modules/BuiltinFunctions.java | 2 +- .../python/builtins/modules/PolyglotModuleBuiltins.java | 2 +- .../graal/python/builtins/modules/SREModuleBuiltins.java | 5 +++-- .../python/builtins/modules/ctypes/CDataBuiltins.java | 4 ++-- .../python/builtins/modules/ctypes/CDataTypeBuiltins.java | 2 +- .../builtins/modules/ctypes/CtypesModuleBuiltins.java | 2 +- .../python/builtins/modules/ctypes/StgDictBuiltins.java | 2 +- .../objects/getsetdescriptor/DescriptorBuiltins.java | 4 ++-- .../objects/method/AbstractBuiltinMethodBuiltins.java | 8 ++++---- .../builtins/objects/method/AbstractMethodBuiltins.java | 2 +- .../objects/method/BuiltinFunctionOrMethodBuiltins.java | 8 ++++---- .../builtins/objects/method/MethodWrapperBuiltins.java | 4 ++-- .../graal/python/builtins/objects/type/TypeBuiltins.java | 4 ++-- .../graal/python/builtins/objects/type/TypeNodes.java | 2 +- .../python/nodes/attributes/GetFixedAttributeNode.java | 4 ---- 15 files changed, 26 insertions(+), 29 deletions(-) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/BuiltinFunctions.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/BuiltinFunctions.java index 35ee2532f2..18530ab9f5 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/BuiltinFunctions.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/BuiltinFunctions.java @@ -2527,7 +2527,7 @@ class InitializeBuildClass { Object ns; try { - Object prep = getPrepare.executeObject(frame, init.meta); + Object prep = getPrepare.execute(frame, init.meta); ns = callPrep.execute(frame, prep, new Object[]{name, init.bases}, init.mkw); } catch (PException p) { p.expectAttributeError(inliningTarget, noAttributeProfile); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/PolyglotModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/PolyglotModuleBuiltins.java index b69624443c..74e6538850 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/PolyglotModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/PolyglotModuleBuiltins.java @@ -355,7 +355,7 @@ static Object exportSymbol(VirtualFrame frame, Object fun, @SuppressWarnings("un @Cached("create(T___NAME__)") GetFixedAttributeNode getNameAttributeNode, @Cached CastToJavaStringNode castToStringNode, @Cached PRaiseNode raiseNode) { - Object attrNameValue = getNameAttributeNode.executeObject(frame, fun); + Object attrNameValue = getNameAttributeNode.execute(frame, fun); String methodName; try { methodName = castToStringNode.execute(attrNameValue); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SREModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SREModuleBuiltins.java index b9c555fce2..38851dff8b 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SREModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SREModuleBuiltins.java @@ -686,8 +686,9 @@ protected Object doCached(VirtualFrame frame, PythonObject pattern, Object input reCheckInputTypeNode.execute(frame, input, tRegexCache.isBinary()); if (fallbackProfile.profile(inliningTarget, libCompiledRegex.isNull(compiledRegex))) { - Object fallbackRegex = getCallFallbackCompileNode().executeWithoutFrame(getGetFallbackCompileNode().executeObject(frame, pattern)); - return getCallFallbackMethodNode().executeWithoutFrame(getFallbackMethodNode.executeObject(frame, fallbackRegex), input, pos, endPos); + GetFixedAttributeNode getFixedAttributeNode = getGetFallbackCompileNode(); + Object fallbackRegex = getCallFallbackCompileNode().executeWithoutFrame(getFixedAttributeNode.execute(frame, pattern)); + return getCallFallbackMethodNode().executeWithoutFrame(getFallbackMethodNode.execute(frame, fallbackRegex), input, pos, endPos); } Object truncatedInput = input; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ctypes/CDataBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ctypes/CDataBuiltins.java index 6c2b562da2..d3ee0d66e9 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ctypes/CDataBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ctypes/CDataBuiltins.java @@ -184,7 +184,7 @@ static Object reduce(VirtualFrame frame, CDataObject self, if ((stgDict.flags & (TYPEFLAG_ISPOINTER | TYPEFLAG_HASPOINTER)) != 0) { throw raiseNode.raise(inliningTarget, ValueError, CTYPES_OBJECTS_CONTAINING_POINTERS_CANNOT_BE_PICKLED); } - Object dict = getAttributeNode.executeObject(frame, self); + Object dict = getAttributeNode.execute(frame, self); Object[] t1 = new Object[]{dict, null}; t1[1] = PFactory.createBytes(language, readBytesNode.execute(inliningTarget, self.b_ptr, self.b_size)); Object clazz = getClassNode.execute(inliningTarget, self); @@ -227,7 +227,7 @@ static Object PyCData_setstate(VirtualFrame frame, CDataObject self, PTuple args len = self.b_size; } memmove(inliningTarget, self.b_ptr, data, len); - Object mydict = getAttributeNode.executeObject(frame, self); + Object mydict = getAttributeNode.execute(frame, self); if (!PGuards.isDict(mydict)) { throw raiseNode.raise(inliningTarget, TypeError, P_DICT_MUST_BE_A_DICTIONARY_NOT_P, self, mydict); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ctypes/CDataTypeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ctypes/CDataTypeBuiltins.java index ba33026bdc..9501b4a1cc 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ctypes/CDataTypeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ctypes/CDataTypeBuiltins.java @@ -334,7 +334,7 @@ static Object CDataType_in_dll(VirtualFrame frame, Object type, Object dll, Truf @Cached CtypesDlSymNode dlSymNode, @Cached PRaiseNode raiseNode) { auditNode.audit(inliningTarget, "ctypes.dlsym", dll, name); - Object obj = getAttributeNode.executeObject(frame, dll); + Object obj = getAttributeNode.execute(frame, dll); if (!longCheckNode.execute(inliningTarget, obj)) { throw raiseNode.raise(inliningTarget, TypeError, THE_HANDLE_ATTRIBUTE_OF_THE_SECOND_ARGUMENT_MUST_BE_AN_INTEGER); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ctypes/CtypesModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ctypes/CtypesModuleBuiltins.java index afa520de22..bc2a971c28 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ctypes/CtypesModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ctypes/CtypesModuleBuiltins.java @@ -548,7 +548,7 @@ Object unpickle(VirtualFrame frame, Object typ, PTuple state, @Cached("create(T___NEW__)") LookupAndCallUnaryNode lookupAndCallUnaryNode, @Cached("create(T___SETSTATE__)") GetFixedAttributeNode setStateAttr) { Object obj = lookupAndCallUnaryNode.executeObject(frame, typ); - Object meth = setStateAttr.executeObject(frame, obj); + Object meth = setStateAttr.execute(frame, obj); callNode.execute(frame, meth, state); return obj; } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ctypes/StgDictBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ctypes/StgDictBuiltins.java index 7577d4e8d8..2a1973eedf 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ctypes/StgDictBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ctypes/StgDictBuiltins.java @@ -188,7 +188,7 @@ static void MakeFields(VirtualFrame frame, Object type, CFieldObject descr, int @Cached MakeFieldsNode recursiveNode, @Cached("createFor($node)") IndirectCallData indirectCallData, @Cached PRaiseNode raiseNode) { - Object fields = getAttrString.executeObject(frame, descr.proto); + Object fields = getAttrString.execute(frame, descr.proto); if (!sequenceCheckNode.execute(inliningTarget, fields)) { throw raiseNode.raise(inliningTarget, TypeError, FIELDS_MUST_BE_A_SEQUENCE); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/getsetdescriptor/DescriptorBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/getsetdescriptor/DescriptorBuiltins.java index ba6bb31d46..27d91080e3 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/getsetdescriptor/DescriptorBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/getsetdescriptor/DescriptorBuiltins.java @@ -99,14 +99,14 @@ public abstract static class QualnameNode extends PythonUnaryBuiltinNode { static TruffleString doGetSetDescriptor(VirtualFrame frame, GetSetDescriptor self, @Shared @Cached("create(T___QUALNAME__)") GetFixedAttributeNode readQualNameNode, @Shared("formatter") @Cached SimpleTruffleStringFormatNode simpleTruffleStringFormatNode) { - return simpleTruffleStringFormatNode.format("%s.%s", toStr(readQualNameNode.executeObject(frame, self.getType())), self.getName()); + return simpleTruffleStringFormatNode.format("%s.%s", toStr(readQualNameNode.execute(frame, self.getType())), self.getName()); } @Specialization static TruffleString doIndexedSlotDescriptor(VirtualFrame frame, IndexedSlotDescriptor self, @Shared @Cached("create(T___QUALNAME__)") GetFixedAttributeNode readQualNameNode, @Shared("formatter") @Cached SimpleTruffleStringFormatNode simpleTruffleStringFormatNode) { - return simpleTruffleStringFormatNode.format("%s.%s", toStr(readQualNameNode.executeObject(frame, self.getType())), self.getName()); + return simpleTruffleStringFormatNode.format("%s.%s", toStr(readQualNameNode.execute(frame, self.getType())), self.getName()); } @TruffleBoundary diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/AbstractBuiltinMethodBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/AbstractBuiltinMethodBuiltins.java index e15569fcba..6b150391bb 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/AbstractBuiltinMethodBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/AbstractBuiltinMethodBuiltins.java @@ -93,13 +93,13 @@ public abstract static class MethodName extends PythonUnaryBuiltinNode { @Specialization static Object getName(VirtualFrame frame, PBuiltinMethod method, @Shared @Cached("create(T___NAME__)") GetFixedAttributeNode getNameAttrNode) { - return getNameAttrNode.executeObject(frame, method.getFunction()); + return getNameAttrNode.execute(frame, method.getFunction()); } @Specialization static Object getName(VirtualFrame frame, PMethod method, @Shared @Cached("create(T___NAME__)") GetFixedAttributeNode getNameAttrNode) { - return getNameAttrNode.executeObject(frame, method.getFunction()); + return getNameAttrNode.execute(frame, method.getFunction()); } } @@ -141,7 +141,7 @@ private static TruffleString makeQualname(VirtualFrame frame, Node inliningTarge SimpleTruffleStringFormatNode simpleTruffleStringFormatNode, PRaiseNode raiseNode) { TruffleString methodName; try { - methodName = castToStringNode.execute(inliningTarget, getNameAttrNode.executeObject(frame, method)); + methodName = castToStringNode.execute(inliningTarget, getNameAttrNode.execute(frame, method)); } catch (CannotCastException e) { throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.IS_NOT_A_UNICODE_OBJECT, T___NAME__); } @@ -152,7 +152,7 @@ private static TruffleString makeQualname(VirtualFrame frame, Node inliningTarge Object type = isTypeNode.execute(inliningTarget, self) ? self : getClassNode.execute(inliningTarget, self); TruffleString typeQualName; try { - typeQualName = castToStringNode.execute(inliningTarget, getQualNameAttrNode.executeObject(frame, type)); + typeQualName = castToStringNode.execute(inliningTarget, getQualNameAttrNode.execute(frame, type)); } catch (CannotCastException e) { throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.IS_NOT_A_UNICODE_OBJECT, T___QUALNAME__); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/AbstractMethodBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/AbstractMethodBuiltins.java index 9bd29adcf3..20856fed8e 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/AbstractMethodBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/AbstractMethodBuiltins.java @@ -257,7 +257,7 @@ static Object getModule(PBuiltinMethod self, Object value, @Specialization(guards = "isNoValue(value)") static Object getModule(VirtualFrame frame, PMethod self, @SuppressWarnings("unused") Object value, @Cached("create(T___MODULE__)") GetFixedAttributeNode getAttributeNode) { - return getAttributeNode.executeObject(frame, self.getFunction()); + return getAttributeNode.execute(frame, self.getFunction()); } @Specialization(guards = "!isNoValue(value)") diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/BuiltinFunctionOrMethodBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/BuiltinFunctionOrMethodBuiltins.java index 1908950abf..2092ed25ef 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/BuiltinFunctionOrMethodBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/BuiltinFunctionOrMethodBuiltins.java @@ -107,14 +107,14 @@ static TruffleString reprBuiltinFunction(VirtualFrame frame, PMethod self, @Shared @Cached("createGetAttributeNode()") GetFixedAttributeNode getNameNode, @Shared("formatter") @Cached SimpleTruffleStringFormatNode simpleTruffleStringFormatNode) { // (tfel): this only happens for builtin modules ... I think - return simpleTruffleStringFormatNode.format("", getNameNode.executeObject(frame, self.getFunction())); + return simpleTruffleStringFormatNode.format("", getNameNode.execute(frame, self.getFunction())); } @Specialization(guards = "isBuiltinFunction(self)") static TruffleString reprBuiltinFunction(VirtualFrame frame, PBuiltinMethod self, @Shared @Cached("createGetAttributeNode()") GetFixedAttributeNode getNameNode, @Shared("formatter") @Cached SimpleTruffleStringFormatNode simpleTruffleStringFormatNode) { - return simpleTruffleStringFormatNode.format("", getNameNode.executeObject(frame, self.getFunction())); + return simpleTruffleStringFormatNode.format("", getNameNode.execute(frame, self.getFunction())); } @Specialization(guards = "!isBuiltinFunction(self)") @@ -125,7 +125,7 @@ static TruffleString reprBuiltinMethod(VirtualFrame frame, PBuiltinMethod self, @Shared @Cached GetNameNode getTypeNameNode, @Shared("formatter") @Cached SimpleTruffleStringFormatNode simpleTruffleStringFormatNode) { TruffleString typeName = getTypeNameNode.execute(inliningTarget, getClassNode.execute(inliningTarget, self.getSelf())); - return simpleTruffleStringFormatNode.format("", getNameNode.executeObject(frame, self.getFunction()), typeName, + return simpleTruffleStringFormatNode.format("", getNameNode.execute(frame, self.getFunction()), typeName, PythonAbstractObject.systemHashCodeAsHexString(self.getSelf())); } @@ -137,7 +137,7 @@ static TruffleString reprBuiltinMethod(VirtualFrame frame, PMethod self, @Shared @Cached GetNameNode getTypeNameNode, @Shared("formatter") @Cached SimpleTruffleStringFormatNode simpleTruffleStringFormatNode) { TruffleString typeName = getTypeNameNode.execute(inliningTarget, getClassNode.execute(inliningTarget, self.getSelf())); - return simpleTruffleStringFormatNode.format("", getNameNode.executeObject(frame, self.getFunction()), typeName, + return simpleTruffleStringFormatNode.format("", getNameNode.execute(frame, self.getFunction()), typeName, PythonAbstractObject.systemHashCodeAsHexString(self.getSelf())); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/MethodWrapperBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/MethodWrapperBuiltins.java index 66981606c8..bcdb1de472 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/MethodWrapperBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/method/MethodWrapperBuiltins.java @@ -100,7 +100,7 @@ static TruffleString reprBuiltinMethod(VirtualFrame frame, PBuiltinMethod self, @Shared @Cached GetNameNode getTypeNameNode, @Shared("formatter") @Cached SimpleTruffleStringFormatNode simpleTruffleStringFormatNode) { TruffleString typeName = getTypeNameNode.execute(inliningTarget, getClassNode.execute(inliningTarget, self.getSelf())); - return simpleTruffleStringFormatNode.format("", getNameNode.executeObject(frame, self.getFunction()), typeName, + return simpleTruffleStringFormatNode.format("", getNameNode.execute(frame, self.getFunction()), typeName, PythonAbstractObject.systemHashCodeAsHexString(self.getSelf())); } @@ -112,7 +112,7 @@ static TruffleString reprBuiltinMethod(VirtualFrame frame, PMethod self, @Shared @Cached GetNameNode getTypeNameNode, @Shared("formatter") @Cached SimpleTruffleStringFormatNode simpleTruffleStringFormatNode) { TruffleString typeName = getTypeNameNode.execute(inliningTarget, getClassNode.execute(inliningTarget, self.getSelf())); - return simpleTruffleStringFormatNode.format("", getNameNode.executeObject(frame, self.getFunction()), typeName, + return simpleTruffleStringFormatNode.format("", getNameNode.execute(frame, self.getFunction()), typeName, PythonAbstractObject.systemHashCodeAsHexString(self.getSelf())); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeBuiltins.java index dee2ab50dd..1d0c1f2eb6 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeBuiltins.java @@ -202,8 +202,8 @@ static TruffleString repr(VirtualFrame frame, Object self, @Cached CastToTruffleStringNode castToStringNode, @Cached TruffleString.EqualNode equalNode, @Cached SimpleTruffleStringFormatNode simpleTruffleStringFormatNode) { - Object moduleNameObj = readModuleNode.executeObject(frame, self); - Object qualNameObj = readQualNameNode.executeObject(frame, self); + Object moduleNameObj = readModuleNode.execute(frame, self); + Object qualNameObj = readQualNameNode.execute(frame, self); TruffleString moduleName = null; if (moduleNameObj != NO_VALUE) { try { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeNodes.java index 3e4c014f4a..08006e47f5 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeNodes.java @@ -2062,7 +2062,7 @@ protected PythonClass makeType(VirtualFrame frame, PDict namespaceOrig, TruffleS // Call __init_subclass__ on the parent of a newly generated type SuperObject superObject = PFactory.createSuperObject(language); superObject.init(newType, newType, newType); - callInitSubclassNode.execute(frame, getInitSubclassNode.executeObject(frame, superObject), PythonUtils.EMPTY_OBJECT_ARRAY, kwds); + callInitSubclassNode.execute(frame, getInitSubclassNode.execute(frame, superObject), PythonUtils.EMPTY_OBJECT_ARRAY, kwds); newType.initializeMroShape(language); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/GetFixedAttributeNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/GetFixedAttributeNode.java index 2e7a655879..a62c52c6c9 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/GetFixedAttributeNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/GetFixedAttributeNode.java @@ -75,10 +75,6 @@ public final TruffleString getKey() { return key; } - public final Object executeObject(VirtualFrame frame, Object object) { - return execute(frame, object); - } - public final Object execute(VirtualFrame frame, Object object) { return executeImpl(frame, object, getSlotsNode.executeCached(object)); } From 9567c2b215d9ca607800ef4b10741980c14f4db3 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Thu, 10 Jul 2025 15:14:51 +0200 Subject: [PATCH 13/38] Add StringUtils.EqualNode to wrap TruffleString.EqualNode with @InliningCutoff * And use it in LookupAttributeInMRONode.Dynamic. * Much better for host inlining, TruffleString.EqualNode is huge for host inlining. --- .../builtins/objects/str/StringUtils.java | 26 +++++++++++++++++++ .../attributes/LookupAttributeInMRONode.java | 14 +++++----- 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/StringUtils.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/StringUtils.java index d90f9576f7..7cf0b37326 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/StringUtils.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/StringUtils.java @@ -48,6 +48,8 @@ import java.util.List; import java.util.Locale; +import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff; +import com.oracle.truffle.api.dsl.Fallback; import org.graalvm.nativeimage.ImageInfo; import org.graalvm.shadowed.com.ibm.icu.lang.UCharacter; import org.graalvm.shadowed.com.ibm.icu.lang.UCharacterCategory; @@ -582,4 +584,28 @@ public static int getCodePoint(String characterName) { return UCharacter.getCharFromName(characterName); } } + + /** + * Like {@link TruffleString.EqualNode} but with the proper {@link InliningCutoff} since + * {@link TruffleString.EqualNode} is too big for host inlining, at least when used in node + * guards. + */ + @GenerateInline + @GenerateCached(false) + @GenerateUncached + public abstract static class EqualNode extends Node { + public abstract boolean execute(Node inliningTarget, TruffleString left, TruffleString right); + + @Specialization(guards = "left == right") + static boolean doIdentity(TruffleString left, TruffleString right) { + return true; + } + + @InliningCutoff + @Fallback + static boolean doEquality(TruffleString left, TruffleString right, + @Cached TruffleString.EqualNode equalNode) { + return equalNode.execute(left, right, TS_ENCODING); + } + } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/LookupAttributeInMRONode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/LookupAttributeInMRONode.java index 9010e15245..14b231bc2d 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/LookupAttributeInMRONode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/LookupAttributeInMRONode.java @@ -44,6 +44,7 @@ import com.oracle.graal.python.builtins.Python3Core; import com.oracle.graal.python.builtins.PythonBuiltinClassType; import com.oracle.graal.python.builtins.objects.PNone; +import com.oracle.graal.python.builtins.objects.str.StringUtils; import com.oracle.graal.python.builtins.objects.type.MroShape; import com.oracle.graal.python.builtins.objects.type.MroShape.MroShapeLookupResult; import com.oracle.graal.python.builtins.objects.type.PythonAbstractClass; @@ -89,23 +90,24 @@ public abstract class LookupAttributeInMRONode extends PNodeWithContext { public abstract static class Dynamic extends PNodeWithContext { public abstract Object execute(Object klass, TruffleString key); - @Specialization(guards = "stringEquals(key, cachedKey, equalNode)", limit = "2") - protected static Object lookupConstantMRO(Object klass, @SuppressWarnings("unused") TruffleString key, + @Specialization(guards = "equalNode.execute(inliningTarget, key, cachedKey)", limit = "2") + protected static Object lookupConstantMROEquals(Object klass, @SuppressWarnings("unused") TruffleString key, + @Bind Node inliningTarget, @Cached("key") @SuppressWarnings("unused") TruffleString cachedKey, - @Cached @SuppressWarnings("unused") TruffleString.EqualNode equalNode, - @Cached("create(key)") LookupAttributeInMRONode lookup) { + @Cached @Shared @SuppressWarnings("unused") StringUtils.EqualNode equalNode, + @Cached("create(cachedKey)") LookupAttributeInMRONode lookup) { return lookup.execute(klass); } - @Specialization(replaces = "lookupConstantMRO") @InliningCutoff + @Specialization(replaces = "lookupConstantMROEquals") protected Object lookupInBuiltinType(PythonBuiltinClassType klass, TruffleString key, @Cached ReadAttributeFromPythonObjectNode readAttrNode) { return findAttr(PythonContext.get(this), klass, key, readAttrNode); } - @Specialization(replaces = "lookupConstantMRO") @InliningCutoff + @Specialization(replaces = "lookupConstantMROEquals") protected static Object lookupGeneric(Object klass, TruffleString key, @Bind Node inliningTarget, @Cached GetMroStorageNode getMroNode, From 7ed17a7d7a3d57d5a4b0d2b2b71564111d5c414b Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Thu, 10 Jul 2025 15:53:02 +0200 Subject: [PATCH 14/38] Align ThreadLocalBuiltins.GetAttributeNode with ObjectBuiltins.GetAttributeNode --- .../objects/object/ObjectBuiltins.java | 8 +++- .../objects/thread/ThreadLocalBuiltins.java | 41 +++++++++++-------- .../builtins/objects/type/TypeBuiltins.java | 8 +++- .../objects/type/slots/TpSlotGetAttr.java | 4 +- .../objects/types/UnionTypeBuiltins.java | 12 ++---- 5 files changed, 42 insertions(+), 31 deletions(-) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/ObjectBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/ObjectBuiltins.java index 0d53263451..ca0e6b7191 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/ObjectBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/ObjectBuiltins.java @@ -88,6 +88,7 @@ import com.oracle.graal.python.builtins.objects.set.PSet; import com.oracle.graal.python.builtins.objects.set.SetBuiltins; import com.oracle.graal.python.builtins.objects.str.StringNodes.CastToTruffleStringChecked1Node; +import com.oracle.graal.python.builtins.objects.thread.ThreadLocalBuiltins; import com.oracle.graal.python.builtins.objects.type.PythonBuiltinClass; import com.oracle.graal.python.builtins.objects.type.PythonManagedClass; import com.oracle.graal.python.builtins.objects.type.TpSlots; @@ -502,7 +503,10 @@ public abstract static class GetAttributeNode extends GetAttrBuiltinNode { @Child private CallSlotDescrGet callSlotDescrGet; @Child private ReadAttributeFromObjectNode attrRead; - /** Keep in sync with {@link TypeBuiltins.GetattributeNode} */ + /** + * Keep in sync with {@link TypeBuiltins.GetattributeNode} and + * {@link ThreadLocalBuiltins.GetAttributeNode} + */ @Specialization @SuppressWarnings("truffle-static-method") Object doIt(VirtualFrame frame, Object object, Object keyObj, @@ -533,7 +537,7 @@ Object doIt(VirtualFrame frame, Object object, Object keyObj, } } - // The difference with TypeBuiltins.GetattributeNode (+ error message below) + // The only difference between all 3 nodes Object value = readAttributeOfObject(object, key); if (value != PNone.NO_VALUE) { hasValueProfile.enter(inliningTarget); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/thread/ThreadLocalBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/thread/ThreadLocalBuiltins.java index 640fa4d0ba..3e57a3d1bc 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/thread/ThreadLocalBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/thread/ThreadLocalBuiltins.java @@ -59,8 +59,10 @@ import com.oracle.graal.python.builtins.objects.object.ObjectBuiltins; import com.oracle.graal.python.builtins.objects.object.ObjectNodes; import com.oracle.graal.python.builtins.objects.object.ObjectNodes.GenericSetAttrWithDictNode; +import com.oracle.graal.python.builtins.objects.str.StringNodes.CastToTruffleStringChecked1Node; import com.oracle.graal.python.builtins.objects.type.TpSlots; import com.oracle.graal.python.builtins.objects.type.TpSlots.GetObjectSlotsNode; +import com.oracle.graal.python.builtins.objects.type.TypeBuiltins; import com.oracle.graal.python.builtins.objects.type.TypeNodes; import com.oracle.graal.python.builtins.objects.type.slots.TpSlot; import com.oracle.graal.python.builtins.objects.type.slots.TpSlotDescrGet.CallSlotDescrGet; @@ -75,7 +77,6 @@ import com.oracle.graal.python.nodes.function.PythonBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; import com.oracle.graal.python.nodes.object.GetClassNode; -import com.oracle.graal.python.nodes.util.CannotCastException; import com.oracle.graal.python.nodes.util.CastToTruffleStringNode; import com.oracle.graal.python.runtime.object.PFactory; import com.oracle.truffle.api.CompilerDirectives; @@ -89,6 +90,7 @@ import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.profiles.InlinedBranchProfile; import com.oracle.truffle.api.profiles.InlinedConditionProfile; import com.oracle.truffle.api.strings.TruffleString; @@ -138,6 +140,10 @@ PDict repr(VirtualFrame frame, PThreadLocal self, public abstract static class GetAttributeNode extends GetAttrBuiltinNode { @Child private CallSlotDescrGet callGetNode; + /** + * Keep in sync with {@link ObjectBuiltins.GetAttributeNode} and + * {@link TypeBuiltins.GetattributeNode} + */ @Specialization Object doIt(VirtualFrame frame, PThreadLocal object, Object keyObj, @Bind Node inliningTarget, @@ -145,49 +151,52 @@ Object doIt(VirtualFrame frame, PThreadLocal object, Object keyObj, @Cached LookupAttributeInMRONode.Dynamic lookup, @Cached GetClassNode getClassNode, @Cached GetObjectSlotsNode getDescrSlotsNode, - @Cached CastToTruffleStringNode castKeyToStringNode, + @Cached CastToTruffleStringChecked1Node castToString, @Cached HashingStorageGetItem getDictStorageItem, - @Cached InlinedConditionProfile hasDescrProfile, + @Cached InlinedBranchProfile hasDescProfile, @Cached InlinedConditionProfile hasDescrGetProfile, @Cached InlinedConditionProfile hasValueProfile, @Cached PRaiseNode raiseNode) { // Note: getting thread local dict has potential side-effects, don't move PDict localDict = getThreadLocalDict.execute(frame, object); - TruffleString key; - try { - key = castKeyToStringNode.execute(inliningTarget, keyObj); - } catch (CannotCastException e) { - throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.ATTR_NAME_MUST_BE_STRING, keyObj); - } + + TruffleString key = castToString.cast(inliningTarget, keyObj, ErrorMessages.ATTR_NAME_MUST_BE_STRING, keyObj); Object type = getClassNode.execute(inliningTarget, object); Object descr = lookup.execute(type, key); - TpSlot descrGetSlot = null; - boolean hasDescr = hasDescrProfile.profile(inliningTarget, descr != PNone.NO_VALUE); + boolean hasDescr = descr != PNone.NO_VALUE; + + TpSlot get = null; boolean hasDescrGet = false; if (hasDescr) { + hasDescProfile.enter(inliningTarget); var descrSlots = getDescrSlotsNode.execute(inliningTarget, descr); - descrGetSlot = descrSlots.tp_descr_get(); - hasDescrGet = hasDescrGetProfile.profile(inliningTarget, descrGetSlot != null); + get = descrSlots.tp_descr_get(); + hasDescrGet = hasDescrGetProfile.profile(inliningTarget, get != null); if (hasDescrGet && TpSlotDescrSet.PyDescr_IsData(descrSlots)) { - return dispatch(frame, object, type, descr, descrGetSlot); + return dispatchDescrGet(frame, object, type, descr, get); } } + + // The only difference between all 3 nodes Object value = getDictStorageItem.execute(frame, inliningTarget, localDict.getDictStorage(), key); if (hasValueProfile.profile(inliningTarget, value != null)) { return value; } + if (hasDescr) { + hasDescProfile.enter(inliningTarget); if (!hasDescrGet) { return descr; } else { - return dispatch(frame, object, type, descr, descrGetSlot); + return dispatchDescrGet(frame, object, type, descr, get); } } + throw raiseNode.raiseAttributeError(inliningTarget, ErrorMessages.OBJ_P_HAS_NO_ATTR_S, object, key); } - private Object dispatch(VirtualFrame frame, Object object, Object type, Object descr, TpSlot get) { + private Object dispatchDescrGet(VirtualFrame frame, Object object, Object type, Object descr, TpSlot get) { if (callGetNode == null) { CompilerDirectives.transferToInterpreterAndInvalidate(); callGetNode = insert(CallSlotDescrGet.create()); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeBuiltins.java index 1d0c1f2eb6..ec2965918e 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeBuiltins.java @@ -105,6 +105,7 @@ import com.oracle.graal.python.builtins.objects.str.PString; import com.oracle.graal.python.builtins.objects.str.StringNodes.CastToTruffleStringChecked1Node; import com.oracle.graal.python.builtins.objects.str.StringUtils.SimpleTruffleStringFormatNode; +import com.oracle.graal.python.builtins.objects.thread.ThreadLocalBuiltins; import com.oracle.graal.python.builtins.objects.tuple.PTuple; import com.oracle.graal.python.builtins.objects.type.TpSlots.GetCachedTpSlotsNode; import com.oracle.graal.python.builtins.objects.type.TpSlots.GetObjectSlotsNode; @@ -523,7 +524,10 @@ public abstract static class GetattributeNode extends GetAttrBuiltinNode { @Child private CallSlotDescrGet callSlotValueGet; @Child private LookupAttributeInMRONode.Dynamic lookupAsClass; - /** Keep in sync with {@link ObjectBuiltins.GetAttributeNode} */ + /** + * Keep in sync with {@link ObjectBuiltins.GetAttributeNode} and + * {@link ThreadLocalBuiltins.GetAttributeNode} + */ @Specialization protected Object doIt(VirtualFrame frame, Object object, Object keyObj, @Bind Node inliningTarget, @@ -555,7 +559,7 @@ protected Object doIt(VirtualFrame frame, Object object, Object keyObj, } } - // The difference with ObjectBuiltins.GetAttributeNode (+ error message below) + // The only difference between all 3 nodes Object value = readAttributeOfClass(object, key); if (value != NO_VALUE) { hasValueProfile.enter(inliningTarget); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotGetAttr.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotGetAttr.java index 1d4f19d977..24a5ec9013 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotGetAttr.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotGetAttr.java @@ -122,8 +122,8 @@ public abstract static class GetAttrBuiltinNode extends PythonBinaryBuiltinNode // "__getattr__" can be AST inlined, and such that when the Python wrapper is explicitly // called, the builtin does the conversion of the second argument to TruffleString itself. // - // The builtins should have a @Specialization that does the coercion to TruffleString and - // also fast-path specialization for TruffleString as the 2nd argument. + // The builtins should have a single @Specialization that does the coercion to + // TruffleString. } public static final class TpSlotGetAttrPython extends TpSlotPython { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/types/UnionTypeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/types/UnionTypeBuiltins.java index 632e2ee604..6f4c7d7614 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/types/UnionTypeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/types/UnionTypeBuiltins.java @@ -64,6 +64,7 @@ import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes; import com.oracle.graal.python.builtins.objects.object.ObjectBuiltins; import com.oracle.graal.python.builtins.objects.set.PFrozenSet; +import com.oracle.graal.python.builtins.objects.str.StringNodes.CastToTruffleStringChecked1Node; import com.oracle.graal.python.builtins.objects.type.TpSlots; import com.oracle.graal.python.builtins.objects.type.TypeNodes; import com.oracle.graal.python.builtins.objects.type.TypeNodes.IsSameTypeNode; @@ -84,8 +85,6 @@ import com.oracle.graal.python.nodes.function.builtins.PythonBinaryBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; import com.oracle.graal.python.nodes.object.GetClassNode; -import com.oracle.graal.python.nodes.util.CannotCastException; -import com.oracle.graal.python.nodes.util.CastToTruffleStringNode; import com.oracle.graal.python.runtime.object.PFactory; import com.oracle.graal.python.runtime.sequence.storage.SequenceStorage; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; @@ -192,17 +191,12 @@ abstract static class GetAttributeNode extends GetAttrBuiltinNode { @Specialization Object getattribute(VirtualFrame frame, PUnionType self, Object nameObj, @Bind Node inliningTarget, - @Cached CastToTruffleStringNode cast, + @Cached CastToTruffleStringChecked1Node castToString, @Cached TruffleString.EqualNode equalNode, @Cached GetClassNode getClassNode, @Cached PyObjectGetAttr getAttr, @Cached ObjectBuiltins.GetAttributeNode genericGetAttribute) { - TruffleString name; - try { - name = cast.execute(inliningTarget, nameObj); - } catch (CannotCastException e) { - return genericGetAttribute.execute(frame, self, nameObj); - } + TruffleString name = castToString.cast(inliningTarget, nameObj, ErrorMessages.ATTR_NAME_MUST_BE_STRING, nameObj); if (equalNode.execute(name, T___MODULE__, TS_ENCODING)) { return getAttr.execute(frame, inliningTarget, getClassNode.execute(inliningTarget, self), name); } From d76ad7bcbe26c6695d287d32ef077c7e3bb3bd65 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Thu, 10 Jul 2025 17:15:43 +0200 Subject: [PATCH 15/38] Add MergedObjectTypeModuleGetAttributeNode --- .../objects/module/ModuleBuiltins.java | 104 +++++---- .../objects/object/ObjectBuiltins.java | 4 +- .../objects/thread/ThreadLocalBuiltins.java | 3 +- .../builtins/objects/type/TypeBuiltins.java | 11 +- .../objects/type/slots/TpSlotDescrGet.java | 18 ++ .../attributes/GetFixedAttributeNode.java | 85 ++----- ...ergedObjectTypeModuleGetAttributeNode.java | 216 ++++++++++++++++++ 7 files changed, 319 insertions(+), 122 deletions(-) create mode 100644 graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/MergedObjectTypeModuleGetAttributeNode.java diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/ModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/ModuleBuiltins.java index 0cfc5b81da..275c629851 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/ModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/ModuleBuiltins.java @@ -110,6 +110,7 @@ import com.oracle.truffle.api.dsl.Cached.Exclusive; import com.oracle.truffle.api.dsl.Cached.Shared; 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.GenerateNodeFactory; import com.oracle.truffle.api.dsl.NodeFactory; @@ -256,63 +257,80 @@ static Object getattribute(VirtualFrame frame, PythonModule self, Object keyObj, @Bind Node inliningTarget, @Cached CastToTruffleStringChecked1Node castToString, @Cached ObjectBuiltins.GetAttributeNode objectGetattrNode, - @Cached HandleGetattrExceptionNode handleException) { + @Cached LazyHandleGetattrExceptionNode handleException) { TruffleString key = castToString.cast(inliningTarget, keyObj, ErrorMessages.ATTR_NAME_MUST_BE_STRING, keyObj); try { return objectGetattrNode.execute(frame, self, key); } catch (PException e) { - return handleException.execute(frame, self, key, e); + return handleException.get(inliningTarget).execute(frame, self, key, e); } } - // Note: this is similar to the "use __getattribute__, if error fallback to __getattr__" - // dance that is normally done in the slot wrapper of __getattribute__/__getattr__ Python - // level methods. This case is, however, slightly different. - @GenerateInline(false) // footprint reduction 56 -> 37 - protected abstract static class HandleGetattrExceptionNode extends PNodeWithContext { - public abstract Object execute(VirtualFrame frame, PythonModule self, TruffleString key, PException e); + @Specialization(guards = "!isPythonModule(self)") + static Object getattribute(Object self, @SuppressWarnings("unused") Object key, + @Bind Node inliningTarget) { + throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ErrorMessages.DESCRIPTOR_S_REQUIRES_S_OBJ_RECEIVED_P, T___GETATTRIBUTE__, "module", self); + } + } - @Specialization - static Object getattribute(VirtualFrame frame, PythonModule self, TruffleString key, PException e, - @Bind Node inliningTarget, - @Cached IsBuiltinObjectProfile isAttrError, - @Cached ReadAttributeFromObjectNode readGetattr, - @Cached InlinedConditionProfile customGetAttr, - @Cached CallNode callNode, - @Cached PyObjectIsTrueNode castToBooleanNode, - @Cached CastToTruffleStringNode castNameToStringNode, - @Cached PRaiseNode raiseNode) { - e.expect(inliningTarget, PythonBuiltinClassType.AttributeError, isAttrError); - Object getAttr = readGetattr.execute(self, T___GETATTR__); - if (customGetAttr.profile(inliningTarget, getAttr != PNone.NO_VALUE)) { - return callNode.execute(frame, getAttr, key); - } else { - TruffleString moduleName; - try { - moduleName = castNameToStringNode.execute(inliningTarget, readGetattr.execute(self, T___NAME__)); - } catch (CannotCastException ce) { - // we just don't have the module name - moduleName = null; - } - if (moduleName != null) { - Object moduleSpec = readGetattr.execute(self, T___SPEC__); - if (moduleSpec != PNone.NO_VALUE) { - Object isInitializing = readGetattr.execute(moduleSpec, T__INITIALIZING); - if (isInitializing != PNone.NO_VALUE && castToBooleanNode.execute(frame, isInitializing)) { - throw raiseNode.raise(inliningTarget, AttributeError, ErrorMessages.MODULE_PARTIALLY_INITIALIZED_S_HAS_NO_ATTR_S, moduleName, key); - } + // Note: this is similar to the "use __getattribute__, if error fallback to __getattr__" + // dance that is normally done in the slot wrapper of __getattribute__/__getattr__ Python + // level methods. This case is, however, slightly different. + @GenerateInline(false) // footprint reduction 56 -> 37 + public abstract static class HandleGetattrExceptionNode extends PNodeWithContext { + public abstract Object execute(VirtualFrame frame, PythonModule self, TruffleString key, PException e); + + @Specialization + static Object getattribute(VirtualFrame frame, PythonModule self, TruffleString key, PException e, + @Bind Node inliningTarget, + @Cached IsBuiltinObjectProfile isAttrError, + @Cached ReadAttributeFromObjectNode readGetattr, + @Cached InlinedConditionProfile customGetAttr, + @Cached CallNode callNode, + @Cached PyObjectIsTrueNode castToBooleanNode, + @Cached CastToTruffleStringNode castNameToStringNode, + @Cached PRaiseNode raiseNode) { + e.expect(inliningTarget, PythonBuiltinClassType.AttributeError, isAttrError); + Object getAttr = readGetattr.execute(self, T___GETATTR__); + if (customGetAttr.profile(inliningTarget, getAttr != PNone.NO_VALUE)) { + return callNode.execute(frame, getAttr, key); + } else { + TruffleString moduleName; + try { + moduleName = castNameToStringNode.execute(inliningTarget, readGetattr.execute(self, T___NAME__)); + } catch (CannotCastException ce) { + // we just don't have the module name + moduleName = null; + } + if (moduleName != null) { + Object moduleSpec = readGetattr.execute(self, T___SPEC__); + if (moduleSpec != PNone.NO_VALUE) { + Object isInitializing = readGetattr.execute(moduleSpec, T__INITIALIZING); + if (isInitializing != PNone.NO_VALUE && castToBooleanNode.execute(frame, isInitializing)) { + throw raiseNode.raise(inliningTarget, AttributeError, ErrorMessages.MODULE_PARTIALLY_INITIALIZED_S_HAS_NO_ATTR_S, moduleName, key); } - throw raiseNode.raise(inliningTarget, AttributeError, ErrorMessages.MODULE_S_HAS_NO_ATTR_S, moduleName, key); } - throw raiseNode.raise(inliningTarget, AttributeError, ErrorMessages.MODULE_HAS_NO_ATTR_S, key); + throw raiseNode.raise(inliningTarget, AttributeError, ErrorMessages.MODULE_S_HAS_NO_ATTR_S, moduleName, key); } + throw raiseNode.raise(inliningTarget, AttributeError, ErrorMessages.MODULE_HAS_NO_ATTR_S, key); } } + } - @Specialization(guards = "!isPythonModule(self)") - static Object getattribute(Object self, @SuppressWarnings("unused") Object key, - @Bind Node inliningTarget) { - throw PRaiseNode.raiseStatic(inliningTarget, TypeError, ErrorMessages.DESCRIPTOR_S_REQUIRES_S_OBJ_RECEIVED_P, T___GETATTRIBUTE__, "module", self); + // GR-67751: Should be HandleGetattrExceptionNode.Lazy but that fails to compile + @GenerateInline + @GenerateCached(false) + public abstract static class LazyHandleGetattrExceptionNode extends Node { + + public final HandleGetattrExceptionNode get(Node inliningTarget) { + return execute(inliningTarget); + } + + abstract HandleGetattrExceptionNode execute(Node inliningTarget); + + @Specialization + static HandleGetattrExceptionNode doIt(@Cached(inline = false) HandleGetattrExceptionNode node) { + return node; } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/ObjectBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/ObjectBuiltins.java index ca0e6b7191..df12baa1fc 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/ObjectBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/ObjectBuiltins.java @@ -119,6 +119,7 @@ import com.oracle.graal.python.nodes.PNodeWithContext; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.attributes.LookupAttributeInMRONode; +import com.oracle.graal.python.nodes.attributes.MergedObjectTypeModuleGetAttributeNode; import com.oracle.graal.python.nodes.attributes.ReadAttributeFromObjectNode; import com.oracle.graal.python.nodes.attributes.WriteAttributeToObjectNode; import com.oracle.graal.python.nodes.builtins.ListNodes; @@ -505,7 +506,8 @@ public abstract static class GetAttributeNode extends GetAttrBuiltinNode { /** * Keep in sync with {@link TypeBuiltins.GetattributeNode} and - * {@link ThreadLocalBuiltins.GetAttributeNode} + * {@link ThreadLocalBuiltins.GetAttributeNode} and + * {@link MergedObjectTypeModuleGetAttributeNode} */ @Specialization @SuppressWarnings("truffle-static-method") diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/thread/ThreadLocalBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/thread/ThreadLocalBuiltins.java index 3e57a3d1bc..b250c86469 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/thread/ThreadLocalBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/thread/ThreadLocalBuiltins.java @@ -73,6 +73,7 @@ import com.oracle.graal.python.nodes.PGuards; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.attributes.LookupAttributeInMRONode; +import com.oracle.graal.python.nodes.attributes.MergedObjectTypeModuleGetAttributeNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.PythonBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; @@ -142,7 +143,7 @@ public abstract static class GetAttributeNode extends GetAttrBuiltinNode { /** * Keep in sync with {@link ObjectBuiltins.GetAttributeNode} and - * {@link TypeBuiltins.GetattributeNode} + * {@link TypeBuiltins.GetattributeNode} and {@link MergedObjectTypeModuleGetAttributeNode} */ @Specialization Object doIt(VirtualFrame frame, PThreadLocal object, Object keyObj, diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeBuiltins.java index ec2965918e..2b56ab2d01 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeBuiltins.java @@ -139,6 +139,7 @@ import com.oracle.graal.python.nodes.SpecialAttributeNames; import com.oracle.graal.python.nodes.attributes.GetFixedAttributeNode; import com.oracle.graal.python.nodes.attributes.LookupAttributeInMRONode; +import com.oracle.graal.python.nodes.attributes.MergedObjectTypeModuleGetAttributeNode; import com.oracle.graal.python.nodes.attributes.ReadAttributeFromObjectNode; import com.oracle.graal.python.nodes.attributes.WriteAttributeToObjectNode; import com.oracle.graal.python.nodes.builtins.ListNodes.ConstructListNode; @@ -526,7 +527,8 @@ public abstract static class GetattributeNode extends GetAttrBuiltinNode { /** * Keep in sync with {@link ObjectBuiltins.GetAttributeNode} and - * {@link ThreadLocalBuiltins.GetAttributeNode} + * {@link ThreadLocalBuiltins.GetAttributeNode} and + * {@link MergedObjectTypeModuleGetAttributeNode} */ @Specialization protected Object doIt(VirtualFrame frame, Object object, Object keyObj, @@ -561,10 +563,9 @@ protected Object doIt(VirtualFrame frame, Object object, Object keyObj, // The only difference between all 3 nodes Object value = readAttributeOfClass(object, key); - if (value != NO_VALUE) { + if (value != PNone.NO_VALUE) { hasValueProfile.enter(inliningTarget); - var valueSlots = getValueSlotsNode.execute(inliningTarget, value); - var valueGet = valueSlots.tp_descr_get(); + var valueGet = getValueSlotsNode.execute(inliningTarget, value).tp_descr_get(); if (valueGet == null) { hasNonDescriptorValueProfile.enter(inliningTarget); return value; @@ -608,7 +609,7 @@ private Object dispatchValueGet(VirtualFrame frame, Object type, Object descr, T } // NO_VALUE 2nd argument indicates the descriptor was found on the target object itself // (or a base) - return callSlotValueGet.executeCached(frame, getSlot, descr, NO_VALUE, type); + return callSlotValueGet.executeCached(frame, getSlot, descr, PNone.NO_VALUE, type); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotDescrGet.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotDescrGet.java index 7ee448e3ab..505c6954e0 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotDescrGet.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotDescrGet.java @@ -81,6 +81,7 @@ 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.NeverDefault; import com.oracle.truffle.api.dsl.NodeFactory; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.VirtualFrame; @@ -196,6 +197,7 @@ public final Object executeCached(VirtualFrame frame, TpSlot slot, Object self, return execute(frame, this, slot, self, obj, type); } + @NeverDefault public static CallSlotDescrGet create() { return CallSlotDescrGetNodeGen.create(); } @@ -249,6 +251,22 @@ static Object callGenericBuiltin(VirtualFrame frame, Node inliningTarget, TpSlot RootCallTarget callTarget = PythonLanguage.get(inliningTarget).getBuiltinSlotCallTarget(slot.callTargetIndex); return invoke.execute(frame, inliningTarget, callTarget, arguments); } + + @GenerateInline + @GenerateCached(false) + public abstract static class Lazy extends Node { + + public final CallSlotDescrGet get(Node inliningTarget) { + return execute(inliningTarget); + } + + abstract CallSlotDescrGet execute(Node inliningTarget); + + @Specialization + static CallSlotDescrGet doIt(@Cached(inline = false) CallSlotDescrGet node) { + return node; + } + } } /** diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/GetFixedAttributeNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/GetFixedAttributeNode.java index a62c52c6c9..5f8aa4aa6b 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/GetFixedAttributeNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/GetFixedAttributeNode.java @@ -40,18 +40,10 @@ */ package com.oracle.graal.python.nodes.attributes; -import com.oracle.graal.python.builtins.objects.PNone; -import com.oracle.graal.python.builtins.objects.exception.AttributeErrorBuiltins; -import com.oracle.graal.python.builtins.objects.module.ModuleBuiltins; -import com.oracle.graal.python.builtins.objects.object.ObjectBuiltins; import com.oracle.graal.python.builtins.objects.type.TpSlots; -import com.oracle.graal.python.builtins.objects.type.TpSlots.GetObjectSlotsNode; -import com.oracle.graal.python.builtins.objects.type.TypeBuiltins; -import com.oracle.graal.python.builtins.objects.type.slots.TpSlotGetAttr.CallSlotGetAttrNode; +import com.oracle.graal.python.builtins.objects.type.TpSlots.GetCachedTpSlotsNode; import com.oracle.graal.python.nodes.PNodeWithContext; import com.oracle.graal.python.nodes.object.GetClassNode; -import com.oracle.graal.python.runtime.exception.PException; -import com.oracle.truffle.api.CompilerAsserts; import com.oracle.truffle.api.dsl.Bind; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.NeverDefault; @@ -60,75 +52,24 @@ import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.strings.TruffleString; -import static com.oracle.graal.python.nodes.SpecialMethodNames.T___GETATTR__; - -@SuppressWarnings("truffle-static-method") public abstract class GetFixedAttributeNode extends PNodeWithContext { private final TruffleString key; - @Child private GetObjectSlotsNode getSlotsNode = GetObjectSlotsNode.create(); - public GetFixedAttributeNode(TruffleString key) { + GetFixedAttributeNode(TruffleString key) { this.key = key; } - public final TruffleString getKey() { - return key; - } - - public final Object execute(VirtualFrame frame, Object object) { - return executeImpl(frame, object, getSlotsNode.executeCached(object)); - } - - abstract Object executeImpl(VirtualFrame frame, Object object, TpSlots slots); - - protected static boolean hasNoGetAttr(Object obj) { - CompilerAsserts.neverPartOfCompilation("only used in asserts"); - return LookupAttributeInMRONode.Dynamic.getUncached().execute(GetClassNode.executeUncached(obj), T___GETATTR__) == PNone.NO_VALUE; - } - - protected static boolean isObjectGetAttribute(TpSlots slots) { - return slots.tp_getattro() == ObjectBuiltins.SLOTS.tp_getattro(); - } - - protected static boolean isModuleGetAttribute(TpSlots slots) { - return slots.tp_getattro() == ModuleBuiltins.SLOTS.tp_getattro(); - } - - protected static boolean isTypeGetAttribute(TpSlots slots) { - return slots.tp_getattro() == TypeBuiltins.SLOTS.tp_getattro(); - } - - @Specialization(guards = "isObjectGetAttribute(slots)") - final Object doBuiltinObject(VirtualFrame frame, Object object, @SuppressWarnings("unused") TpSlots slots, - @Cached ObjectBuiltins.GetAttributeNode getAttributeNode) { - assert hasNoGetAttr(object); - return getAttributeNode.execute(frame, object, key); - } - - @Specialization(guards = "isTypeGetAttribute(slots)") - final Object doBuiltinType(VirtualFrame frame, Object object, @SuppressWarnings("unused") TpSlots slots, - @Cached TypeBuiltins.GetattributeNode getAttributeNode) { - assert hasNoGetAttr(object); - return getAttributeNode.execute(frame, object, key); - } - - @Specialization(guards = "isModuleGetAttribute(slots)") - final Object doBuiltinModule(VirtualFrame frame, Object object, @SuppressWarnings("unused") TpSlots slots, - @Cached ModuleBuiltins.ModuleGetattributeNode getAttributeNode) { - assert hasNoGetAttr(object); - return getAttributeNode.execute(frame, object, key); - } - - @Specialization(replaces = {"doBuiltinObject", "doBuiltinType", "doBuiltinModule"}) - final Object doGeneric(VirtualFrame frame, Object object, TpSlots slots, - @Bind("this") Node inliningTarget, - @Cached CallSlotGetAttrNode callGetAttrNode, - @Cached AttributeErrorBuiltins.SetAttributeErrorContext setContext) { - try { - return callGetAttrNode.execute(frame, inliningTarget, slots, object, key); - } catch (PException e) { - throw setContext.execute(inliningTarget, e, object, key); - } + public abstract Object execute(VirtualFrame frame, Object object); + + @Specialization + Object doIt(VirtualFrame frame, Object object, + @Bind Node inliningTarget, + @Cached GetClassNode getClassNode, + @Cached GetCachedTpSlotsNode getSlotsNode, + @Cached MergedObjectTypeModuleGetAttributeInnerNode innerNode) { + Object type = getClassNode.execute(inliningTarget, object); + TpSlots slots = getSlotsNode.execute(inliningTarget, type); + return innerNode.execute(frame, inliningTarget, object, key, type, slots); } @NeverDefault diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/MergedObjectTypeModuleGetAttributeNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/MergedObjectTypeModuleGetAttributeNode.java new file mode 100644 index 0000000000..86eb9cf947 --- /dev/null +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/MergedObjectTypeModuleGetAttributeNode.java @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2017, 2025, 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 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.oracle.graal.python.nodes.attributes; + +import static com.oracle.graal.python.nodes.SpecialMethodNames.T___GETATTR__; + +import com.oracle.graal.python.builtins.objects.PNone; +import com.oracle.graal.python.builtins.objects.exception.AttributeErrorBuiltins; +import com.oracle.graal.python.builtins.objects.module.ModuleBuiltins; +import com.oracle.graal.python.builtins.objects.module.PythonModule; +import com.oracle.graal.python.builtins.objects.object.ObjectBuiltins; +import com.oracle.graal.python.builtins.objects.str.StringNodes.CastToTruffleStringChecked1Node; +import com.oracle.graal.python.builtins.objects.thread.ThreadLocalBuiltins; +import com.oracle.graal.python.builtins.objects.type.TpSlots; +import com.oracle.graal.python.builtins.objects.type.TpSlots.GetCachedTpSlotsNode; +import com.oracle.graal.python.builtins.objects.type.TpSlots.GetObjectSlotsNode; +import com.oracle.graal.python.builtins.objects.type.TypeBuiltins; +import com.oracle.graal.python.builtins.objects.type.slots.TpSlot; +import com.oracle.graal.python.builtins.objects.type.slots.TpSlotDescrGet.CallSlotDescrGet; +import com.oracle.graal.python.builtins.objects.type.slots.TpSlotDescrSet; +import com.oracle.graal.python.builtins.objects.type.slots.TpSlotGetAttr.CallSlotGetAttrNode; +import com.oracle.graal.python.nodes.ErrorMessages; +import com.oracle.graal.python.nodes.PNodeWithContext; +import com.oracle.graal.python.nodes.PRaiseNode; +import com.oracle.graal.python.nodes.object.GetClassNode; +import com.oracle.graal.python.runtime.exception.PException; +import com.oracle.truffle.api.CompilerAsserts; +import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff; +import com.oracle.truffle.api.dsl.Cached; +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.Idempotent; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.profiles.InlinedBranchProfile; +import com.oracle.truffle.api.profiles.InlinedConditionProfile; +import com.oracle.truffle.api.strings.TruffleString; + +/** + * A node merging the logic of {@link ObjectBuiltins.GetAttributeNode}, + * {@link TypeBuiltins.GetattributeNode} and {@link ModuleBuiltins.ModuleGetattributeNode} to reduce + * code size by about 3x for host inlining + */ +@GenerateUncached +@GenerateInline +@GenerateCached(false) +public abstract class MergedObjectTypeModuleGetAttributeNode extends PNodeWithContext { + + public abstract Object execute(VirtualFrame frame, Node inliningTarget, Object object, Object keyObj); + + @Specialization + static Object doIt(VirtualFrame frame, Node inliningTarget, Object object, Object keyObj, + @Cached CastToTruffleStringChecked1Node castToString, + @Cached GetClassNode getClassNode, + @Cached GetCachedTpSlotsNode getSlotsNode, + @Cached MergedObjectTypeModuleGetAttributeInnerNode innerNode) { + TruffleString key = castToString.cast(inliningTarget, keyObj, ErrorMessages.ATTR_NAME_MUST_BE_STRING, keyObj); + Object type = getClassNode.execute(inliningTarget, object); + TpSlots slots = getSlotsNode.execute(inliningTarget, type); + return innerNode.execute(frame, inliningTarget, object, key, type, slots); + } +} + +@GenerateUncached +@GenerateInline +@GenerateCached(false) +abstract class MergedObjectTypeModuleGetAttributeInnerNode extends PNodeWithContext { + + public abstract Object execute(VirtualFrame frame, Node inliningTarget, Object object, TruffleString key, Object type, TpSlots slots); + + /** + * Keep in sync with {@link ObjectBuiltins.GetAttributeNode} and + * {@link TypeBuiltins.GetattributeNode} and {@link ModuleBuiltins.ModuleGetattributeNode} and + * {@link ThreadLocalBuiltins.GetAttributeNode} + */ + @Specialization(guards = {"slots.tp_getattro() == cachedSlot", "isObjectTypeModuleGetAttribute(cachedSlot)"}, limit = "1") + static Object doIt(VirtualFrame frame, Node inliningTarget, Object object, TruffleString key, Object type, @SuppressWarnings("unused") TpSlots slots, + @Cached("slots.tp_getattro()") TpSlot cachedSlot, + // Common + @Cached GetObjectSlotsNode getDescrSlotsNode, + @Cached LookupAttributeInMRONode.Dynamic lookup, + @Cached InlinedBranchProfile hasDescProfile, + @Cached InlinedConditionProfile hasDescrGetProfile, + @Cached InlinedBranchProfile hasValueProfile, + @Cached PRaiseNode raiseNode, + @Cached CallSlotDescrGet.Lazy callSlotDescrGet, + // Specific to a given tp_getattro, some should probably be lazy + @Cached ReadAttributeFromObjectNode readAttributeOfObjectNode, + @Cached LookupAttributeInMRONode.Dynamic readAttributeOfClassNode, + @Cached GetObjectSlotsNode getValueSlotsNode, + @Cached InlinedBranchProfile hasNonDescriptorValueProfile, + @Cached CallSlotDescrGet.Lazy callSlotValueGet, + @Cached ModuleBuiltins.LazyHandleGetattrExceptionNode handleException) { + assert hasNoGetAttr(object); + try { + Object descr = lookup.execute(type, key); + boolean hasDescr = descr != PNone.NO_VALUE; + + TpSlot get = null; + boolean hasDescrGet = false; + if (hasDescr) { + hasDescProfile.enter(inliningTarget); + var descrSlots = getDescrSlotsNode.execute(inliningTarget, descr); + get = descrSlots.tp_descr_get(); + hasDescrGet = hasDescrGetProfile.profile(inliningTarget, get != null); + if (hasDescrGet && TpSlotDescrSet.PyDescr_IsData(descrSlots)) { + return callSlotDescrGet.get(inliningTarget).executeCached(frame, get, descr, object, type); + } + } + + // The main difference between all 3 nodes + Object value; + if (cachedSlot != TypeBuiltins.SLOTS.tp_getattro()) { + // ObjectBuiltins.SLOTS.tp_getattro() || ModuleBuiltins.SLOTS.tp_getattro() + value = readAttributeOfObjectNode.execute(object, key); + if (value != PNone.NO_VALUE) { + hasValueProfile.enter(inliningTarget); + return value; + } + } else { + // TypeBuiltins.SLOTS.tp_getattro() + value = readAttributeOfClassNode.execute(object, key); + if (value != PNone.NO_VALUE) { + hasValueProfile.enter(inliningTarget); + var valueGet = getValueSlotsNode.execute(inliningTarget, value).tp_descr_get(); + if (valueGet == null) { + hasNonDescriptorValueProfile.enter(inliningTarget); + return value; + } else { + return callSlotValueGet.get(inliningTarget).executeCached(frame, valueGet, value, PNone.NO_VALUE, object); + } + } + } + + if (hasDescr) { + hasDescProfile.enter(inliningTarget); + if (!hasDescrGet) { + return descr; + } else { + return callSlotDescrGet.get(inliningTarget).executeCached(frame, get, descr, object, type); + } + } + + throw raiseNode.raiseAttributeError(inliningTarget, ErrorMessages.OBJ_P_HAS_NO_ATTR_S, object, key); + } catch (PException e) { + // Extra behavior for module.__getattribute__ + if (cachedSlot == ModuleBuiltins.SLOTS.tp_getattro()) { + return handleException.get(inliningTarget).execute(frame, (PythonModule) object, key, e); + } else { + throw e; + } + } + } + + @InliningCutoff + @Specialization(replaces = "doIt") + static Object doGeneric(VirtualFrame frame, Node inliningTarget, Object object, TruffleString key, @SuppressWarnings("unused") Object type, TpSlots slots, + @Cached CallSlotGetAttrNode callGetAttrNode, + @Cached AttributeErrorBuiltins.SetAttributeErrorContext setContext) { + try { + return callGetAttrNode.execute(frame, inliningTarget, slots, object, key); + } catch (PException e) { + throw setContext.execute(inliningTarget, e, object, key); + } + } + + @Idempotent + static boolean isObjectTypeModuleGetAttribute(TpSlot slot) { + return slot == ObjectBuiltins.SLOTS.tp_getattro() || slot == TypeBuiltins.SLOTS.tp_getattro() || slot == ModuleBuiltins.SLOTS.tp_getattro(); + } + + static boolean hasNoGetAttr(Object obj) { + CompilerAsserts.neverPartOfCompilation("only used in asserts"); + return LookupAttributeInMRONode.Dynamic.getUncached().execute(GetClassNode.executeUncached(obj), T___GETATTR__) == PNone.NO_VALUE; + } +} From d7d2f616f4eb0fdeb70e21eec30fa1bcfcda1ba4 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Fri, 18 Jul 2025 13:24:33 +0200 Subject: [PATCH 16/38] Rename GetPythonObjectClassNode for clarity in host inlining decisions --- .../oracle/graal/python/nodes/object/GetClassNode.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/GetClassNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/GetClassNode.java index 9179d286a0..9b5babf707 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/GetClassNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/GetClassNode.java @@ -170,29 +170,29 @@ final Object executeCached(PythonAbstractObject object) { public abstract Object execute(Node inliningTarget, PythonAbstractNativeObject object); @Specialization(guards = {"isSingleContext()", "klass != null", "object.getShape() == cachedShape", "hasInitialClass(cachedShape)"}, limit = "1") - static Object getPythonObjectConstantClass(@SuppressWarnings("unused") PythonObject object, + static Object doConstantClass(@SuppressWarnings("unused") PythonObject object, @SuppressWarnings("unused") @Cached(value = "object.getShape()") Shape cachedShape, @Cached(value = "object.getInitialPythonClass()", weak = true) Object klass) { return klass; } @Specialization(guards = "hasInitialClass(object.getShape())") - static Object getPythonObject(@SuppressWarnings("unused") PythonObject object, + static Object doInitialClass(@SuppressWarnings("unused") PythonObject object, @Bind("object.getInitialPythonClass()") Object klass) { assert klass != null; return klass; } @InliningCutoff - @Specialization(guards = "!hasInitialClass(object.getShape())", replaces = "getPythonObjectConstantClass") - static Object getPythonObject(Node inliningTarget, PythonObject object, + @Specialization(guards = "!hasInitialClass(object.getShape())", replaces = "doConstantClass") + static Object doReadHiddenAttrNode(Node inliningTarget, PythonObject object, @Cached HiddenAttr.ReadNode readHiddenAttrNode) { return readHiddenAttrNode.execute(inliningTarget, object, HiddenAttr.CLASS, object.getInitialPythonClass()); } @InliningCutoff @Specialization - static Object getNativeObject(Node inliningTarget, PythonAbstractNativeObject object, + static Object doNativeObject(Node inliningTarget, PythonAbstractNativeObject object, @Cached CExtNodes.GetNativeClassNode getNativeClassNode) { return getNativeClassNode.execute(inliningTarget, object); } From f0b1a1cdbd9eda39a6d1e25b1b2735948d8cb4a6 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Fri, 18 Jul 2025 13:45:40 +0200 Subject: [PATCH 17/38] [GR-66471] DSL-inline GetPythonObjectClassNode in GetClassNode to host-inline it * GetPythonObjectClassNode needs 3 references, so it might be best to not DSL-inline to save footprint. OTOH, this case is frequent and not DSL-inlining it also means not host inlining it due to GR-66471. So we DSL-inline it in order to host-inline it. * Actually the DSL is smart and creates a Specialization data class here, so there is no footprint overhead and this is win-win. --- .../python/nodes/object/GetClassNode.java | 24 ++++++++----------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/GetClassNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/GetClassNode.java index 9b5babf707..df5b739fe2 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/GetClassNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/GetClassNode.java @@ -58,6 +58,7 @@ 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.GenerateCached; import com.oracle.truffle.api.dsl.GenerateInline; import com.oracle.truffle.api.dsl.GenerateUncached; import com.oracle.truffle.api.dsl.Idempotent; @@ -135,13 +136,10 @@ static Object getBuiltinFunction(@SuppressWarnings("unused") PBuiltinFunction ob return object.getInitialPythonClass(); } - // GetPythonObjectClassNode needs 3 references, so we do not inline it here, to save footprint - // if only the fast-path specializations are activated - @Specialization(guards = "isPythonObject(object) || isNativeObject(object)") - static Object getPythonObjectOrNative(PythonAbstractObject object, - @Cached(inline = false) GetPythonObjectClassNode getClassNode) { - return getClassNode.executeCached(object); + static Object getPythonObjectOrNative(Node inliningTarget, PythonAbstractObject object, + @Cached GetPythonObjectClassNode getPythonObjectClassNode) { + return getPythonObjectClassNode.executeImpl(inliningTarget, object); } /* @@ -150,19 +148,17 @@ static Object getPythonObjectOrNative(PythonAbstractObject object, * when not necessary. */ @GenerateUncached - @GenerateInline(inlineByDefault = true) + @GenerateInline + @GenerateCached(false) public abstract static class GetPythonObjectClassNode extends PNodeWithContext { public static Object executeUncached(PythonObject object) { return GetClassNodeGen.getUncached().execute(null, object); } - // Intended only for internal usage in this node. The caller must make sure that the object - // is of one of the expected Java types. - final Object executeCached(PythonAbstractObject object) { - assert object instanceof PythonObject || object instanceof PythonAbstractNativeObject; - return executeImpl(this, object); - } - + /* + * Intended only for internal usage in the outer node. The caller must make sure that the + * object is either a PythonObject or PythonAbstractNativeObject. + */ abstract Object executeImpl(Node inliningTarget, PythonAbstractObject object); public abstract Object execute(Node inliningTarget, PythonObject object); From cca7cd9290d6583114a439a9341a01dfab4fe935 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Fri, 18 Jul 2025 17:12:31 +0200 Subject: [PATCH 18/38] Add @InliningCutoff to ModuleBuiltins.HandleGetattrExceptionNode * It generates more than 300 method calls with host inlining and blows up the budget. * The common case by far is to not call this node at all, as the attribute is typically available directly on the module, and there is no need to call __getattr__. --- .../python/builtins/objects/module/ModuleBuiltins.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/ModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/ModuleBuiltins.java index 275c629851..d1dbb98d2e 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/ModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/ModuleBuiltins.java @@ -105,6 +105,7 @@ import com.oracle.graal.python.runtime.PythonContext; import com.oracle.graal.python.runtime.exception.PException; import com.oracle.graal.python.runtime.object.PFactory; +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.Cached.Exclusive; @@ -276,9 +277,14 @@ static Object getattribute(Object self, @SuppressWarnings("unused") Object key, // Note: this is similar to the "use __getattribute__, if error fallback to __getattr__" // dance that is normally done in the slot wrapper of __getattribute__/__getattr__ Python // level methods. This case is, however, slightly different. - @GenerateInline(false) // footprint reduction 56 -> 37 + @GenerateInline(false) public abstract static class HandleGetattrExceptionNode extends PNodeWithContext { - public abstract Object execute(VirtualFrame frame, PythonModule self, TruffleString key, PException e); + @InliningCutoff + public final Object execute(VirtualFrame frame, PythonModule self, TruffleString key, PException e) { + return executeInternal(frame, self, key, e); + } + + abstract Object executeInternal(VirtualFrame frame, PythonModule self, TruffleString key, PException e); @Specialization static Object getattribute(VirtualFrame frame, PythonModule self, TruffleString key, PException e, From 587b618d966c76a87902cc9b8807affe4560af9b Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Fri, 18 Jul 2025 17:32:24 +0200 Subject: [PATCH 19/38] Add @InliningCutoff in PRaiseNode * These methods all lead to unwind, better to cutoff early and spend the host inlining budget on the non-exception cases. * Host inlining already CUTOFF some method because of "leads to unwind" but it does not realize all of this logic leads to unwind. --- .../oracle/graal/python/nodes/PRaiseNode.java | 29 +++++++++++++++++-- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/PRaiseNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/PRaiseNode.java index 146f44b7b9..69fce9795d 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/PRaiseNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/PRaiseNode.java @@ -52,6 +52,7 @@ import com.oracle.graal.python.runtime.PythonOptions; import com.oracle.graal.python.runtime.exception.PException; import com.oracle.graal.python.runtime.object.PFactory; +import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.GenerateCached; import com.oracle.truffle.api.dsl.GenerateInline; @@ -80,6 +81,7 @@ public final PException raise(Node inliningTarget, PythonBuiltinClassType type) throw raiseStatic(inliningTarget, type); } + @InliningCutoff public static PException raiseStatic(Node node, PythonBuiltinClassType type) { PythonLanguage language = PythonLanguage.get(node); PBaseException pythonException = PFactory.createBaseException(language, type); @@ -91,6 +93,7 @@ public final PException raise(Node inliningTarget, PythonBuiltinClassType type, throw raiseStatic(inliningTarget, type, message); } + @InliningCutoff public static PException raiseStatic(Node node, PythonBuiltinClassType type, TruffleString message) { PythonLanguage language = PythonLanguage.get(node); PBaseException pythonException = PFactory.createBaseException(language, type, message); @@ -102,6 +105,7 @@ public final PException raise(Node inliningTarget, PythonBuiltinClassType type, throw raiseStatic(inliningTarget, type, format, formatArgs); } + @InliningCutoff public static PException raiseStatic(Node node, PythonBuiltinClassType type, TruffleString message, Object... formatArgs) { PythonLanguage language = PythonLanguage.get(node); PBaseException pythonException = PFactory.createBaseException(language, type, message, formatArgs); @@ -113,6 +117,7 @@ public final PException raise(Node inliningTarget, PythonBuiltinClassType type, throw raiseStatic(inliningTarget, type, arguments); } + @InliningCutoff public static PException raiseStatic(Node node, PythonBuiltinClassType type, Object[] arguments) { PythonLanguage language = PythonLanguage.get(node); PBaseException pythonException = PFactory.createBaseException(language, type, PFactory.createTuple(language, arguments)); @@ -124,6 +129,7 @@ public final PException raiseWithData(Node inliningTarget, PythonBuiltinClassTyp throw raiseWithDataStatic(inliningTarget, type, data); } + @InliningCutoff public static PException raiseWithDataStatic(Node node, PythonBuiltinClassType type, Object[] data) { PythonLanguage language = PythonLanguage.get(node); PBaseException pythonException = PFactory.createBaseException(language, type, data, null); @@ -135,6 +141,7 @@ public final PException raiseAttributeError(Node inliningTarget, TruffleString e throw raiseAttributeErrorStatic(inliningTarget, errorMessage, obj, key); } + @InliningCutoff public static PException raiseAttributeErrorStatic(Node inliningTarget, TruffleString errorMessage, Object obj, Object key) { throw raiseWithDataStatic(inliningTarget, PythonBuiltinClassType.AttributeError, AttributeErrorBuiltins.dataForObjKey(obj, key), errorMessage, obj, key); } @@ -144,6 +151,7 @@ public final PException raiseWithData(Node inliningTarget, PythonBuiltinClassTyp throw raiseWithDataStatic(inliningTarget, type, data, format, formatArgs); } + @InliningCutoff public static PException raiseWithDataStatic(Node node, PythonBuiltinClassType type, Object[] data, TruffleString format, Object... formatArgs) { PythonLanguage language = PythonLanguage.get(node); PBaseException pythonException = PFactory.createBaseException(language, type, data, format, formatArgs); @@ -155,6 +163,7 @@ public final PException raiseWithData(Node inliningTarget, PythonBuiltinClassTyp throw raiseWithDataStatic(inliningTarget, type, data, arguments); } + @InliningCutoff public static PException raiseWithDataStatic(Node node, PythonBuiltinClassType type, Object[] data, Object[] arguments) { PythonLanguage language = PythonLanguage.get(node); PBaseException pythonException = PFactory.createBaseException(language, type, data, PFactory.createTuple(language, arguments)); @@ -166,6 +175,7 @@ public final PException raise(Node inliningTarget, PythonBuiltinClassType type, throw raiseStatic(inliningTarget, type, e); } + @InliningCutoff public static PException raiseStatic(Node node, PythonBuiltinClassType type, Exception e) { PythonLanguage language = PythonLanguage.get(node); PBaseException pythonException = PFactory.createBaseException(language, type, ErrorMessages.M, new Object[]{e}); @@ -184,6 +194,7 @@ public final PException raiseWithCause(Node inliningTarget, PythonBuiltinClassTy throw raiseWithCauseStatic(inliningTarget, type, cause, format); } + @InliningCutoff public static PException raiseWithCauseStatic(Node node, PythonBuiltinClassType type, PException cause, TruffleString format) { PythonLanguage language = PythonLanguage.get(node); PBaseException pythonException = PFactory.createBaseException(language, type, format); @@ -197,6 +208,7 @@ public final PException raiseWithCause(Node inliningTarget, PythonBuiltinClassTy throw raiseWithCauseStatic(inliningTarget, type, cause, format, arguments); } + @InliningCutoff public static PException raiseWithCauseStatic(Node node, PythonBuiltinClassType type, PException cause, TruffleString format, Object... formatArgs) { assert PyExceptionInstanceCheckNode.executeUncached(cause); PythonLanguage language = PythonLanguage.get(node); @@ -209,6 +221,7 @@ public final PException raiseOverflow(Node inliningTarget) { throw raise(inliningTarget, OverflowError, ErrorMessages.CANNOT_FIT_P_INTO_INDEXSIZED_INT, 0); } + @InliningCutoff public static PException raiseSystemExitStatic(Node inliningTarget, Object code) { throw raiseWithDataStatic(inliningTarget, PythonBuiltinClassType.SystemExit, new Object[]{code}, new Object[]{code}); } @@ -229,18 +242,28 @@ public final PException raiseBadInternalCall(Node inliningTarget) { throw raise(inliningTarget, PythonBuiltinClassType.SystemError, BAD_ARG_TO_INTERNAL_FUNC); } - public final PException raiseExceptionObject(Node raisingNode, Object exc) { - throw raiseExceptionObjectStatic(raisingNode, exc); + public final PException raiseExceptionObject(Node inliningTarget, Object exc) { + executeEnterProfile(inliningTarget); + throw raiseExceptionObjectStaticCutoff(inliningTarget, exc); + } + + @InliningCutoff + private static PException raiseExceptionObjectStaticCutoff(Node inliningTarget, Object exc) { + return raiseExceptionObjectStatic(inliningTarget, exc); } + // No @InliningCutoff, callers either cutoff already, or they are nodes which only throw an + // exception and so we want to host inline public static PException raiseExceptionObjectStatic(Node raisingNode, Object exc) { throw raiseExceptionObjectStatic(raisingNode, exc, PythonOptions.isPExceptionWithJavaStacktrace(PythonLanguage.get(raisingNode))); } - public static PException raiseExceptionObjectStatic(Node raisingNode, Object exc, PythonLanguage language) { + // No @InliningCutoff, done in callers already + private static PException raiseExceptionObjectStatic(Node raisingNode, Object exc, PythonLanguage language) { throw raiseExceptionObjectStatic(raisingNode, exc, PythonOptions.isPExceptionWithJavaStacktrace(language)); } + // No @InliningCutoff, done in callers already public static PException raiseExceptionObjectStatic(Node raisingNode, Object exc, boolean withJavaStacktrace) { if (raisingNode != null && raisingNode.isAdoptable()) { throw PException.fromObject(exc, raisingNode, withJavaStacktrace); From 8c5ae76055039a2c744a64472da591c7e140d585 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Fri, 18 Jul 2025 17:39:41 +0200 Subject: [PATCH 20/38] GetPythonObjectClassNode#doInitialClass should replace doConstantClass --- .../src/com/oracle/graal/python/nodes/object/GetClassNode.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/GetClassNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/GetClassNode.java index df5b739fe2..dfb3ede402 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/GetClassNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/GetClassNode.java @@ -172,7 +172,7 @@ static Object doConstantClass(@SuppressWarnings("unused") PythonObject object, return klass; } - @Specialization(guards = "hasInitialClass(object.getShape())") + @Specialization(guards = "hasInitialClass(object.getShape())", replaces = "doConstantClass") static Object doInitialClass(@SuppressWarnings("unused") PythonObject object, @Bind("object.getInitialPythonClass()") Object klass) { assert klass != null; From 44d74a1181a3d26ae6275bd5051e66736ac38fd5 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Fri, 18 Jul 2025 18:21:59 +0200 Subject: [PATCH 21/38] Do not use DynamicObjectLibrary for just getting the Shape flags * Some usages used the uncached DynamicObjectLibrary which has a @TruffleBoundary on getShapeFlags(). Actually it seems to not have the boundary due to having a copy in the code, but that's brittle and wouldn't work anymore with a @GenerateUncached node instead. * The library is used on various classes which might have different shapes, so the caching on the Shape seems not very useful. --- .../builtins/objects/type/PythonClass.java | 4 +-- .../builtins/objects/type/TypeNodes.java | 7 ++--- .../attributes/LookupAttributeInMRONode.java | 27 +++++++------------ 3 files changed, 14 insertions(+), 24 deletions(-) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/PythonClass.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/PythonClass.java index d8de4a4914..afb2eb7ead 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/PythonClass.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/PythonClass.java @@ -247,8 +247,8 @@ public void makeStaticBase(DynamicObjectLibrary dylib) { dylib.setShapeFlags(this, dylib.getShapeFlags(this) | IS_STATIC_BASE); } - public boolean isStaticBase(DynamicObjectLibrary dylib) { - return (dylib.getShapeFlags(this) & IS_STATIC_BASE) != 0; + public boolean isStaticBase() { + return (getShape().getFlags() & IS_STATIC_BASE) != 0; } public MroShape getMroShape() { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeNodes.java index 08006e47f5..2ff9bbfba8 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeNodes.java @@ -241,7 +241,6 @@ import com.oracle.truffle.api.library.CachedLibrary; import com.oracle.truffle.api.nodes.ControlFlowException; import com.oracle.truffle.api.nodes.Node; -import com.oracle.truffle.api.object.DynamicObjectLibrary; import com.oracle.truffle.api.object.Shape; import com.oracle.truffle.api.profiles.InlinedBranchProfile; import com.oracle.truffle.api.profiles.InlinedConditionProfile; @@ -1438,10 +1437,8 @@ static boolean shapeDiffers(Object type, Object base, ReadAttributeFromObjectNod if (typeSlots != null && length(typeSlots) != 0) { return true; } - Object typeNewMethod = LookupAttributeInMRONode.lookup(T___NEW__, GetMroStorageNode.executeUncached(type), ReadAttributeFromObjectNode.getUncached(), true, - DynamicObjectLibrary.getUncached()); - Object baseNewMethod = LookupAttributeInMRONode.lookup(T___NEW__, GetMroStorageNode.executeUncached(base), ReadAttributeFromObjectNode.getUncached(), true, - DynamicObjectLibrary.getUncached()); + Object typeNewMethod = LookupAttributeInMRONode.lookup(T___NEW__, GetMroStorageNode.executeUncached(type), ReadAttributeFromObjectNode.getUncached(), true); + Object baseNewMethod = LookupAttributeInMRONode.lookup(T___NEW__, GetMroStorageNode.executeUncached(base), ReadAttributeFromObjectNode.getUncached(), true); return typeNewMethod != baseNewMethod; } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/LookupAttributeInMRONode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/LookupAttributeInMRONode.java index 14b231bc2d..f1fc7be61f 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/LookupAttributeInMRONode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/LookupAttributeInMRONode.java @@ -63,7 +63,6 @@ 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.Cached.Exclusive; import com.oracle.truffle.api.dsl.Cached.Shared; import com.oracle.truffle.api.dsl.GenerateInline; import com.oracle.truffle.api.dsl.GenerateUncached; @@ -72,11 +71,9 @@ import com.oracle.truffle.api.dsl.NeverDefault; import com.oracle.truffle.api.dsl.ReportPolymorphism.Megamorphic; import com.oracle.truffle.api.dsl.Specialization; -import com.oracle.truffle.api.library.CachedLibrary; import com.oracle.truffle.api.nodes.ControlFlowException; import com.oracle.truffle.api.nodes.ExplodeLoop; import com.oracle.truffle.api.nodes.Node; -import com.oracle.truffle.api.object.DynamicObjectLibrary; import com.oracle.truffle.api.profiles.InlinedBranchProfile; import com.oracle.truffle.api.strings.TruffleString; @@ -112,7 +109,7 @@ protected static Object lookupGeneric(Object klass, TruffleString key, @Bind Node inliningTarget, @Cached GetMroStorageNode getMroNode, @Cached(value = "createForceType()", uncached = "getUncachedForceType()") ReadAttributeFromObjectNode readAttrNode) { - return lookup(key, getMroNode.execute(inliningTarget, klass), readAttrNode, false, DynamicObjectLibrary.getUncached()); + return lookup(key, getMroNode.execute(inliningTarget, klass), readAttrNode, false); } @NeverDefault @@ -232,13 +229,12 @@ static final class InvalidateLookupException extends ControlFlowException { private static final InvalidateLookupException INSTANCE = new InvalidateLookupException(); } - private static boolean skipNonStaticBase(Object clsObj, boolean skipNonStaticBases, DynamicObjectLibrary dylib) { - return skipNonStaticBases && clsObj instanceof PythonClass && !((PythonClass) clsObj).isStaticBase(dylib); + private static boolean skipNonStaticBase(Object clsObj, boolean skipNonStaticBases) { + return skipNonStaticBases && clsObj instanceof PythonClass && !((PythonClass) clsObj).isStaticBase(); } protected AttributeAssumptionPair findAttrAndAssumptionInMRO(Object klass) { CompilerAsserts.neverPartOfCompilation(); - DynamicObjectLibrary dylib = DynamicObjectLibrary.getUncached(); // Regarding potential side effects to MRO caused by __eq__ of the keys in the dicts that we // search through: CPython seems to read the MRO once and then compute the result also // ignoring the side effects. Moreover, CPython has lookup cache, so the side effects @@ -254,7 +250,7 @@ protected AttributeAssumptionPair findAttrAndAssumptionInMRO(Object klass) { assert clsObj != klass : "MRO chain is incorrect: '" + klass + "' was found at position " + i; getMro(clsObj).addAttributeInMROFinalAssumption(key, attrAssumption); } - if (skipNonStaticBase(clsObj, skipNonStaticBases, dylib)) { + if (skipNonStaticBase(clsObj, skipNonStaticBases)) { continue; } Object value = ReadAttributeFromObjectNode.getUncachedForceType().execute(clsObj, key); @@ -326,11 +322,10 @@ protected Object lookupConstantMRO(@SuppressWarnings("unused") Object klass, @Cached("getMro(cachedKlass)") MroSequenceStorage mro, @Cached("mro.getLookupStableAssumption()") @SuppressWarnings("unused") Assumption lookupStable, @Cached("mro.length()") int mroLength, - @Exclusive @CachedLibrary(limit = "1") DynamicObjectLibrary dylib, @Cached("create(mroLength)") ReadAttributeFromObjectNode[] readAttrNodes) { for (int i = 0; i < mroLength; i++) { Object kls = mro.getPythonClassItemNormalized(i); - if (skipNonStaticBase(kls, skipNonStaticBases, dylib)) { + if (skipNonStaticBase(kls, skipNonStaticBases)) { continue; } Object value = readAttrNodes[i].execute(kls, key); @@ -350,11 +345,10 @@ protected Object lookupCachedLen(@SuppressWarnings("unused") Object klass, @Bind("getMro(klass)") MroSequenceStorage mro, @Bind("mro.length()") @SuppressWarnings("unused") int mroLength, @Cached("mro.length()") int cachedMroLength, - @Exclusive @CachedLibrary(limit = "1") DynamicObjectLibrary dylib, @Cached("create(cachedMroLength)") ReadAttributeFromObjectNode[] readAttrNodes) { for (int i = 0; i < cachedMroLength; i++) { Object kls = mro.getPythonClassItemNormalized(i); - if (skipNonStaticBase(kls, skipNonStaticBases, dylib)) { + if (skipNonStaticBase(kls, skipNonStaticBases)) { continue; } Object value = readAttrNodes[i].execute(kls, key); @@ -369,9 +363,8 @@ protected Object lookupCachedLen(@SuppressWarnings("unused") Object klass, @Megamorphic @InliningCutoff protected Object lookupGeneric(Object klass, - @Exclusive @CachedLibrary(limit = "1") DynamicObjectLibrary dylib, @Cached("createForceType()") ReadAttributeFromObjectNode readAttrNode) { - return lookup(key, getMro(klass), readAttrNode, skipNonStaticBases, dylib); + return lookup(key, getMro(klass), readAttrNode, skipNonStaticBases); } protected GetMroStorageNode ensureGetMroNode() { @@ -388,13 +381,13 @@ protected MroSequenceStorage getMro(Object clazz) { @TruffleBoundary public static Object lookupSlowPath(Object klass, TruffleString key) { - return lookup(key, GetMroStorageNode.executeUncached(klass), ReadAttributeFromObjectNode.getUncachedForceType(), false, DynamicObjectLibrary.getUncached()); + return lookup(key, GetMroStorageNode.executeUncached(klass), ReadAttributeFromObjectNode.getUncachedForceType(), false); } - public static Object lookup(TruffleString key, MroSequenceStorage mro, ReadAttributeFromObjectNode readAttrNode, boolean skipNonStaticBases, DynamicObjectLibrary dylib) { + public static Object lookup(TruffleString key, MroSequenceStorage mro, ReadAttributeFromObjectNode readAttrNode, boolean skipNonStaticBases) { for (int i = 0; i < mro.length(); i++) { Object kls = mro.getPythonClassItemNormalized(i); - if (skipNonStaticBase(kls, skipNonStaticBases, dylib)) { + if (skipNonStaticBase(kls, skipNonStaticBases)) { continue; } Object value = readAttrNode.execute(kls, key); From 5ad4d167b7d9bab174ba37137ed2c574f0fa531d Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Tue, 29 Jul 2025 15:47:11 +0200 Subject: [PATCH 22/38] Update imports --- ci/graal/common.json | 14 +++++++------- mx.graalpython/suite.py | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ci/graal/common.json b/ci/graal/common.json index 86497d63f4..83b9c68d83 100644 --- a/ci/graal/common.json +++ b/ci/graal/common.json @@ -53,13 +53,13 @@ "labsjdk-ee-25Debug": {"name": "labsjdk", "version": "ee-25+30-jvmci-b01-debug", "platformspecific": true }, "labsjdk-ee-25-llvm": {"name": "labsjdk", "version": "ee-25+30-jvmci-b01-sulong", "platformspecific": true }, - "oraclejdk-latest": {"name": "jpg-jdk", "version": "26", "build_id": "jdk-26+7", "platformspecific": true, "extrabundles": ["static-libs"]}, - "labsjdk-ce-latest": {"name": "labsjdk", "version": "ce-26+7-jvmci-b01", "platformspecific": true }, - "labsjdk-ce-latestDebug": {"name": "labsjdk", "version": "ce-26+7-jvmci-b01-debug", "platformspecific": true }, - "labsjdk-ce-latest-llvm": {"name": "labsjdk", "version": "ce-26+7-jvmci-b01-sulong", "platformspecific": true }, - "labsjdk-ee-latest": {"name": "labsjdk", "version": "ee-26+7-jvmci-b01", "platformspecific": true }, - "labsjdk-ee-latestDebug": {"name": "labsjdk", "version": "ee-26+7-jvmci-b01-debug", "platformspecific": true }, - "labsjdk-ee-latest-llvm": {"name": "labsjdk", "version": "ee-26+7-jvmci-b01-sulong", "platformspecific": true } + "oraclejdk-latest": {"name": "jpg-jdk", "version": "26", "build_id": "jdk-26+8", "platformspecific": true, "extrabundles": ["static-libs"]}, + "labsjdk-ce-latest": {"name": "labsjdk", "version": "ce-26+8-jvmci-b01", "platformspecific": true }, + "labsjdk-ce-latestDebug": {"name": "labsjdk", "version": "ce-26+8-jvmci-b01-debug", "platformspecific": true }, + "labsjdk-ce-latest-llvm": {"name": "labsjdk", "version": "ce-26+8-jvmci-b01-sulong", "platformspecific": true }, + "labsjdk-ee-latest": {"name": "labsjdk", "version": "ee-26+8-jvmci-b01", "platformspecific": true }, + "labsjdk-ee-latestDebug": {"name": "labsjdk", "version": "ee-26+8-jvmci-b01-debug", "platformspecific": true }, + "labsjdk-ee-latest-llvm": {"name": "labsjdk", "version": "ee-26+8-jvmci-b01-sulong", "platformspecific": true } }, "eclipse": { diff --git a/mx.graalpython/suite.py b/mx.graalpython/suite.py index 8df7a7f58d..a3b956e8cd 100644 --- a/mx.graalpython/suite.py +++ b/mx.graalpython/suite.py @@ -53,7 +53,7 @@ }, { "name": "tools", - "version": "c18c686390ee4a15c3c3769a553d378f0c23ea1c", + "version": "8bfc1ec4c051d3d201e92d70c6f8bcc99903405c", "subdir": True, "urls": [ {"url": "https://github.com/oracle/graal", "kind": "git"}, @@ -61,7 +61,7 @@ }, { "name": "regex", - "version": "c18c686390ee4a15c3c3769a553d378f0c23ea1c", + "version": "8bfc1ec4c051d3d201e92d70c6f8bcc99903405c", "subdir": True, "urls": [ {"url": "https://github.com/oracle/graal", "kind": "git"}, From 76afa98c03a9e29aefd6fdbaae382205da819328 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Fri, 18 Jul 2025 18:06:45 +0200 Subject: [PATCH 23/38] Make PythonObject#initialPythonClass non-final and rename to PythonObject#pythonClass * This simplifies GetClassNode a lot, and it still folds in single-context mode. * In multi-context mode it would basically never fold as the object wouldn't be constant. * This saves 2 reads (getShape().getFlags() & CLASS_CHANGED_FLAG) in multi-context mode. * This saves a DynamicObject read (1-2 reads: getShape() + Location#get potentially in extension array) when the class has been changed. * Use Shape#getDynamicType() instead of a constant property and flags: just a field read from the Shape instead of a property map lookup + Location#get. Also easier to keep in sync as everything is in a single field. * Do not make the cached reference to the class weak in GetClassNode, the Shape through the constant property already referenced it strongly and the Shape still does with dynamicType. * Much better for host inlining of GetClassNode and notably 2 @InliningCutoff vs 3 before: com.oracle.graal.python.nodes.object.GetClassNodeGen#execute 598 -> 482 com.oracle.graal.python.nodes.object.GetClassNodeGen.Inlined#execute 2151 -> 2035 com.oracle.graal.python.builtins.objects.object.ObjectBuiltinsFactory.ClassNodeFactory.ClassNodeGen#execute 657 -> 554 Truffle host inlining completed after 2 rounds. Graph cost changed from 426 to 598 after inlining: Root[com.oracle.graal.python.nodes.object.GetClassNodeGen.execute] INLINE com.oracle.graal.python.nodes.object.GetClassNodeGen$GetPythonObjectClassNodeGen$Inlined.executeImpl(Node, PythonAbstractObject) cost 376, subTreeCost 243, treeInvokes 2, reason within budget] CUTOFF com.oracle.graal.python.nodes.object.GetClassNode$GetPythonObjectClassNode.doReadHiddenAttrNode(Node, PythonObject, HiddenAttr$ReadNode) cost -, subTreeCost -, treeInvokes -, reason method annotated with @InliningCutoff] CUTOFF com.oracle.graal.python.nodes.object.GetClassNode$GetPythonObjectClassNode.doNativeObject(...) cost -, subTreeCost -, treeInvokes -, reason method annotated with @InliningCutoff] CUTOFF com.oracle.graal.python.nodes.object.GetClassNode.getForeign(Object, GetRegisteredClassNode) cost -, subTreeCost -, treeInvokes -, reason method annotated with @InliningCutoff] -> Truffle host inlining completed after 2 rounds. Graph cost changed from 363 to 482 after inlining: Root[com.oracle.graal.python.nodes.object.GetClassNodeGen.execute] INLINE com.oracle.graal.python.nodes.object.GetClassNodeGen$GetPythonObjectClassNodeGen$Inlined.executeImpl(Node, PythonAbstractObject) cost 319, subTreeCost 186, treeInvokes 1, reason within budget] CUTOFF com.oracle.graal.python.nodes.object.GetClassNode$GetPythonObjectClassNode.doNativeObject(...) cost -, subTreeCost -, treeInvokes -, reason method annotated with @InliningCutoff] CUTOFF com.oracle.graal.python.nodes.object.GetClassNode.getForeign(Object, GetRegisteredClassNode) cost -, subTreeCost -, treeInvokes -, reason method annotated with @InliningCutoff] --- .../oracle/graal/python/PythonLanguage.java | 5 +- .../modules/hashlib/DigestObject.java | 2 +- .../asyncio/ANextAwaitableBuiltins.java | 2 +- .../objects/exception/PBaseException.java | 2 +- .../objects/object/ObjectBuiltins.java | 12 +++-- .../builtins/objects/object/PythonObject.java | 51 +++++++----------- .../objects/type/slots/TpSlotVarargs.java | 2 +- .../oracle/graal/python/nodes/HiddenAttr.java | 24 +-------- .../oracle/graal/python/nodes/PGuards.java | 16 +----- .../python/nodes/object/GetClassNode.java | 52 +++++-------------- .../graal/python/runtime/object/PFactory.java | 2 +- 11 files changed, 49 insertions(+), 121 deletions(-) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/PythonLanguage.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/PythonLanguage.java index bd72b0ef5d..4737d12371 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/PythonLanguage.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/PythonLanguage.java @@ -82,7 +82,6 @@ import com.oracle.graal.python.compiler.ParserCallbacksImpl; import com.oracle.graal.python.compiler.bytecode_dsl.BytecodeDSLCompiler; import com.oracle.graal.python.compiler.bytecode_dsl.BytecodeDSLCompiler.BytecodeDSLCompilerResult; -import com.oracle.graal.python.nodes.HiddenAttr; import com.oracle.graal.python.nodes.bytecode.PBytecodeRootNode; import com.oracle.graal.python.nodes.bytecode_dsl.BytecodeDSLCodeUnit; import com.oracle.graal.python.nodes.call.CallDispatchers; @@ -1043,7 +1042,7 @@ public Shape getEmptyShape() { public Shape getShapeForClass(PythonAbstractClass klass) { if (isSingleContext()) { - return Shape.newBuilder(getEmptyShape()).addConstantProperty(HiddenAttr.getClassHiddenKey(), klass, 0).build(); + return Shape.newBuilder(getEmptyShape()).dynamicType(klass).build(); } else { return getEmptyShape(); } @@ -1065,7 +1064,7 @@ public Shape getBuiltinTypeInstanceShape(PythonBuiltinClassType type) { private Shape createBuiltinShape(PythonBuiltinClassType type, int ordinal) { Shape shape; - Shape.DerivedBuilder shapeBuilder = Shape.newBuilder(getEmptyShape()).addConstantProperty(HiddenAttr.getClassHiddenKey(), type, 0); + Shape.DerivedBuilder shapeBuilder = Shape.newBuilder(getEmptyShape()).dynamicType(type); if (!type.isBuiltinWithDict()) { shapeBuilder.shapeFlags(PythonObject.HAS_SLOTS_BUT_NO_DICT_FLAG); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/hashlib/DigestObject.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/hashlib/DigestObject.java index 180a253237..9ae3f00753 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/hashlib/DigestObject.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/hashlib/DigestObject.java @@ -81,7 +81,7 @@ public static DigestObject create(PythonBuiltinClassType digestType, Shape insta } public PythonBuiltinClassType getType() { - return (PythonBuiltinClassType) getInitialPythonClass(); + return (PythonBuiltinClassType) getPythonClass(); } // The JDK does not expose the block sizes used by digests, so diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/asyncio/ANextAwaitableBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/asyncio/ANextAwaitableBuiltins.java index 1e268bfbff..46c32b79c8 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/asyncio/ANextAwaitableBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/asyncio/ANextAwaitableBuiltins.java @@ -95,7 +95,7 @@ abstract static class GetIterNode extends Node { static Object getIter(VirtualFrame frame, Node inliningTarget, PANextAwaitable self, @Cached GetAwaitableNode getAwaitableNode) { Object awaitable = getAwaitableNode.execute(frame, self.getWrapped()); - if (awaitable instanceof PGenerator coroutine && coroutine.getInitialPythonClass() == PythonBuiltinClassType.PCoroutine) { + if (awaitable instanceof PGenerator coroutine && coroutine.getPythonClass() == PythonBuiltinClassType.PCoroutine) { return PFactory.createCoroutineWrapper(PythonLanguage.get(inliningTarget), coroutine); } return awaitable; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/PBaseException.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/PBaseException.java index ae2ae740e7..a4d7ae3817 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/PBaseException.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/exception/PBaseException.java @@ -234,7 +234,7 @@ public Object[] getMessageArgs() { public String toString() { CompilerAsserts.neverPartOfCompilation(); // We *MUST NOT* call anything here that may need a context! - StringBuilder sb = new StringBuilder(this.getInitialPythonClass().toString()); + StringBuilder sb = new StringBuilder(this.getPythonClass().toString()); if (messageArgs != null && messageArgs.length > 0) { sb.append("(fmt=\"").append(messageFormat.toJavaStringUncached()).append("\", args = ("); for (Object arg : messageArgs) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/ObjectBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/ObjectBuiltins.java index df12baa1fc..557d92a317 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/ObjectBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/ObjectBuiltins.java @@ -114,7 +114,6 @@ import com.oracle.graal.python.lib.PyObjectStrAsObjectNode; import com.oracle.graal.python.lib.RichCmpOp; import com.oracle.graal.python.nodes.ErrorMessages; -import com.oracle.graal.python.nodes.HiddenAttr; import com.oracle.graal.python.nodes.PGuards; import com.oracle.graal.python.nodes.PNodeWithContext; import com.oracle.graal.python.nodes.PRaiseNode; @@ -164,7 +163,9 @@ import com.oracle.truffle.api.dsl.NodeFactory; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.library.CachedLibrary; import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.object.DynamicObjectLibrary; import com.oracle.truffle.api.profiles.InlinedBranchProfile; import com.oracle.truffle.api.profiles.InlinedConditionProfile; import com.oracle.truffle.api.strings.TruffleString; @@ -222,9 +223,12 @@ abstract static class SetClassNode extends Node { public abstract void execute(Node inliningTarget, Object self, Object newClass); @Specialization - static void doPythonObject(Node inliningTarget, PythonObject self, Object newClass, - @Cached HiddenAttr.WriteNode writeHiddenAttrNode) { - writeHiddenAttrNode.execute(inliningTarget, self, HiddenAttr.CLASS, newClass); + static void doPythonObject(PythonObject self, Object newClass, + @CachedLibrary(limit = "3") DynamicObjectLibrary dylib) { + // Clear the dynamic type when setting the class, so further class changes do not + // create new shapes + dylib.setDynamicType(self, null); + self.setPythonClass(newClass); } @Specialization diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/PythonObject.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/PythonObject.java index b2366ab49d..7c9596608c 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/PythonObject.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/PythonObject.java @@ -33,9 +33,7 @@ import com.oracle.graal.python.builtins.PythonBuiltinClassType; import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.PythonAbstractObject; -import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject; import com.oracle.graal.python.builtins.objects.dict.PDict; -import com.oracle.graal.python.builtins.objects.type.PythonBuiltinClass; import com.oracle.graal.python.builtins.objects.type.PythonManagedClass; import com.oracle.graal.python.nodes.HiddenAttr; import com.oracle.graal.python.nodes.PGuards; @@ -49,49 +47,34 @@ import com.oracle.truffle.api.strings.TruffleString; public class PythonObject extends PythonAbstractObject { - public static final byte CLASS_CHANGED_FLAG = 0b1; /** * Indicates that the object doesn't allow {@code __dict__}, but may have slots */ - public static final byte HAS_SLOTS_BUT_NO_DICT_FLAG = 0b10; + public static final byte HAS_SLOTS_BUT_NO_DICT_FLAG = 0b1; /** * Indicates that the shape has some properties that may contain {@link PNone#NO_VALUE} and * therefore the shape itself is not enough to resolve any lookups. */ - public static final byte HAS_NO_VALUE_PROPERTIES = 0b100; + public static final byte HAS_NO_VALUE_PROPERTIES = 0b10; /** * Indicates that the object has a dict in the form of an actual dictionary */ - public static final byte HAS_MATERIALIZED_DICT = 0b1000; + public static final byte HAS_MATERIALIZED_DICT = 0b100; /** * Indicates that the object is a static base in the CPython's tp_new_wrapper sense. * * @see com.oracle.graal.python.nodes.function.builtins.WrapTpNew */ - public static final byte IS_STATIC_BASE = 0b10000; + public static final byte IS_STATIC_BASE = 0b1000; - private final Object initialPythonClass; + private Object pythonClass; @SuppressWarnings("this-escape") // escapes in the assertion public PythonObject(Object pythonClass, Shape instanceShape) { super(instanceShape); assert pythonClass != null; - assert consistentStorage(pythonClass); - this.initialPythonClass = pythonClass; - } - - private boolean consistentStorage(Object pythonClass) { - Object constantClass = HiddenAttr.ReadNode.executeUncached(this, HiddenAttr.CLASS, null); - if (constantClass == null) { - return true; - } - if (constantClass instanceof PythonBuiltinClass) { - constantClass = ((PythonBuiltinClass) constantClass).getType(); - } - if (constantClass instanceof PythonAbstractNativeObject && pythonClass instanceof PythonAbstractNativeObject) { - return true; - } - return constantClass == (pythonClass instanceof PythonBuiltinClass ? ((PythonBuiltinClass) pythonClass).getType() : pythonClass); + assert getShape().getDynamicType() == null || getShape().getDynamicType() == pythonClass : getShape().getDynamicType() + " vs " + pythonClass; + this.pythonClass = pythonClass; } public void setDict(Node inliningTarget, HiddenAttr.WriteNode writeNode, PDict dict) { @@ -99,8 +82,13 @@ public void setDict(Node inliningTarget, HiddenAttr.WriteNode writeNode, PDict d } @NeverDefault - public Object getInitialPythonClass() { - return initialPythonClass; + public Object getPythonClass() { + return pythonClass; + } + + public void setPythonClass(Object pythonClass) { + assert getShape().getDynamicType() == null; + this.pythonClass = pythonClass; } @TruffleBoundary @@ -141,12 +129,11 @@ public int compareTo(Object o) { @Override public String toString() { String className = "unknown"; - Object storedPythonClass = HiddenAttr.ReadNode.executeUncached(this, HiddenAttr.CLASS, null); - if (storedPythonClass instanceof PythonManagedClass) { - className = ((PythonManagedClass) storedPythonClass).getQualName().toJavaStringUncached(); - } else if (storedPythonClass instanceof PythonBuiltinClassType) { - className = ((PythonBuiltinClassType) storedPythonClass).getName().toJavaStringUncached(); - } else if (PGuards.isNativeClass(storedPythonClass)) { + if (pythonClass instanceof PythonManagedClass managedClass) { + className = managedClass.getQualName().toJavaStringUncached(); + } else if (pythonClass instanceof PythonBuiltinClassType pbct) { + className = pbct.getName().toJavaStringUncached(); + } else if (PGuards.isNativeClass(pythonClass)) { className = "native"; } return "<" + className + " object at 0x" + Integer.toHexString(hashCode()) + ">"; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotVarargs.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotVarargs.java index 14499295bf..0b13ed9c58 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotVarargs.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotVarargs.java @@ -374,7 +374,7 @@ static Object doStaticmethod(PDecoratedMethod descriptor, @SuppressWarnings("unu } protected static boolean isStaticmethod(PDecoratedMethod descriptor) { - return descriptor.getInitialPythonClass() == PythonBuiltinClassType.PStaticmethod; + return descriptor.getPythonClass() == PythonBuiltinClassType.PStaticmethod; } @Specialization diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/HiddenAttr.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/HiddenAttr.java index eef07b14b5..703bb29735 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/HiddenAttr.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/HiddenAttr.java @@ -40,7 +40,6 @@ */ package com.oracle.graal.python.nodes; -import static com.oracle.graal.python.builtins.objects.object.PythonObject.CLASS_CHANGED_FLAG; import static com.oracle.graal.python.builtins.objects.object.PythonObject.HAS_MATERIALIZED_DICT; import static com.oracle.graal.python.nodes.BuiltinNames.J___GRAALPYTHON_INTEROP_BEHAVIOR__; import static com.oracle.graal.python.nodes.SpecialAttributeNames.J___BASICSIZE__; @@ -69,7 +68,6 @@ public final class HiddenAttr { public static final HiddenAttr OBJECT_ID = new HiddenAttr("_id"); // ObjectNodes - public static final HiddenAttr CLASS = new HiddenAttr("ob_type"); public static final HiddenAttr DICT = new HiddenAttr("ob_dict"); public static final HiddenAttr DICTOFFSET = new HiddenAttr(J___DICTOFFSET__); public static final HiddenAttr WEAKLISTOFFSET = new HiddenAttr(J___WEAKLISTOFFSET__); @@ -111,11 +109,6 @@ private HiddenAttr(String keyName) { key = new HiddenKey(keyName); } - // temporary, will be removed when we have no DynamicObjects and Shapes - public static HiddenKey getClassHiddenKey() { - return CLASS.key; - } - public String getName() { return key.getName(); } @@ -178,18 +171,7 @@ static void doPythonObjectDict(PythonObject self, HiddenAttr attr, Object value, dylib.put(self, DICT.key, value); } - @Specialization(guards = "attr == CLASS") - static void doPythonObjectClass(PythonObject self, HiddenAttr attr, Object value, - @Shared @CachedLibrary(limit = "3") DynamicObjectLibrary dylib) { - // n.b.: the CLASS property is usually a constant property that is stored in the shape - // in - // single-context-mode. If we change it for the first time, there's an implicit shape - // transition - dylib.setShapeFlags(self, dylib.getShapeFlags(self) | CLASS_CHANGED_FLAG); - dylib.put(self, CLASS.key, value); - } - - @Specialization(guards = "!isSpecialCaseAttr(attr) || !isPythonObject(self)") + @Specialization(guards = "attr != DICT || !isPythonObject(self)") static void doGeneric(PythonAbstractObject self, HiddenAttr attr, Object value, @Shared @CachedLibrary(limit = "3") DynamicObjectLibrary dylib) { dylib.put(self, attr.key, value); @@ -199,10 +181,6 @@ protected static boolean isPythonObject(Object object) { return object instanceof PythonObject; } - protected static boolean isSpecialCaseAttr(HiddenAttr attr) { - return attr == DICT || attr == CLASS; - } - @NeverDefault public static WriteNode create() { return WriteNodeGen.create(); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/PGuards.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/PGuards.java index 3e460179a4..a24f9c8a6d 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/PGuards.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/PGuards.java @@ -312,12 +312,7 @@ public static boolean isPInt(Object obj) { } public static boolean isBuiltinPInt(PInt obj) { - /* - * int's __class__ cannot be reassigned and other objects cannot have their class assigned - * to builtin int, so it is enough to look at the initial class. PInt constructor ensures - * that it cannot be PythonBuiltinClass. - */ - return obj.getInitialPythonClass() == PythonBuiltinClassType.PInt; + return obj.getPythonClass() == PythonBuiltinClassType.PInt; } public static boolean isPString(Object obj) { @@ -465,14 +460,7 @@ public static double expectDouble(Object result) throws UnexpectedResultExceptio } private static boolean isBuiltinImmutableTypeInstance(PythonObject dict, PythonBuiltinClassType type) { - /* - * Immutable types' __class__ cannot be reassigned and other objects cannot have their class - * assigned to immutable types, so it is enough to look at the initial class. The Java - * constructor of the object must ensure that it cannot be PythonBuiltinClass, see PDict for - * an example. - */ - assert !(dict.getInitialPythonClass() instanceof PythonBuiltinClass pbc) || pbc.getType() != type; - return dict.getInitialPythonClass() == type; + return dict.getPythonClass() == type; } public static boolean isBuiltinDict(PythonObject dict) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/GetClassNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/GetClassNode.java index dfb3ede402..3cb58d0fb7 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/GetClassNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/GetClassNode.java @@ -49,19 +49,15 @@ import com.oracle.graal.python.builtins.objects.cext.PythonNativeVoidPtr; import com.oracle.graal.python.builtins.objects.cext.capi.CExtNodes; import com.oracle.graal.python.builtins.objects.ellipsis.PEllipsis; -import com.oracle.graal.python.builtins.objects.function.PBuiltinFunction; -import com.oracle.graal.python.builtins.objects.function.PFunction; import com.oracle.graal.python.builtins.objects.object.PythonObject; -import com.oracle.graal.python.builtins.objects.type.PythonBuiltinClass; -import com.oracle.graal.python.nodes.HiddenAttr; import com.oracle.graal.python.nodes.PNodeWithContext; 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.GenerateCached; import com.oracle.truffle.api.dsl.GenerateInline; import com.oracle.truffle.api.dsl.GenerateUncached; import com.oracle.truffle.api.dsl.Idempotent; +import com.oracle.truffle.api.dsl.ImportStatic; import com.oracle.truffle.api.dsl.NeverDefault; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.nodes.Node; @@ -121,21 +117,6 @@ static Object getNone(@SuppressWarnings("unused") PNone object) { return PythonBuiltinClassType.PNone; } - @Specialization - static Object getBuiltinClass(PythonBuiltinClass object) { - return object.getInitialPythonClass(); - } - - @Specialization - static Object getFunction(@SuppressWarnings("unused") PFunction object) { - return object.getInitialPythonClass(); - } - - @Specialization - static Object getBuiltinFunction(@SuppressWarnings("unused") PBuiltinFunction object) { - return object.getInitialPythonClass(); - } - @Specialization(guards = "isPythonObject(object) || isNativeObject(object)") static Object getPythonObjectOrNative(Node inliningTarget, PythonAbstractObject object, @Cached GetPythonObjectClassNode getPythonObjectClassNode) { @@ -150,6 +131,7 @@ static Object getPythonObjectOrNative(Node inliningTarget, PythonAbstractObject @GenerateUncached @GenerateInline @GenerateCached(false) + @ImportStatic(PythonObject.class) public abstract static class GetPythonObjectClassNode extends PNodeWithContext { public static Object executeUncached(PythonObject object) { return GetClassNodeGen.getUncached().execute(null, object); @@ -165,25 +147,20 @@ public static Object executeUncached(PythonObject object) { public abstract Object execute(Node inliningTarget, PythonAbstractNativeObject object); - @Specialization(guards = {"isSingleContext()", "klass != null", "object.getShape() == cachedShape", "hasInitialClass(cachedShape)"}, limit = "1") - static Object doConstantClass(@SuppressWarnings("unused") PythonObject object, - @SuppressWarnings("unused") @Cached(value = "object.getShape()") Shape cachedShape, - @Cached(value = "object.getInitialPythonClass()", weak = true) Object klass) { - return klass; + @Idempotent + static Object getDynamicType(Shape shape) { + return shape.getDynamicType(); } - @Specialization(guards = "hasInitialClass(object.getShape())", replaces = "doConstantClass") - static Object doInitialClass(@SuppressWarnings("unused") PythonObject object, - @Bind("object.getInitialPythonClass()") Object klass) { - assert klass != null; - return klass; + @Specialization(guards = {"object.getShape() == cachedShape", "getDynamicType(cachedShape) != null"}, limit = "1") + static Object doConstantClass(@SuppressWarnings("unused") PythonObject object, + @Cached(value = "object.getShape()") Shape cachedShape) { + return cachedShape.getDynamicType(); } - @InliningCutoff - @Specialization(guards = "!hasInitialClass(object.getShape())", replaces = "doConstantClass") - static Object doReadHiddenAttrNode(Node inliningTarget, PythonObject object, - @Cached HiddenAttr.ReadNode readHiddenAttrNode) { - return readHiddenAttrNode.execute(inliningTarget, object, HiddenAttr.CLASS, object.getInitialPythonClass()); + @Specialization(replaces = "doConstantClass") + static Object doReadClassField(PythonObject object) { + return object.getPythonClass(); } @InliningCutoff @@ -192,11 +169,6 @@ static Object doNativeObject(Node inliningTarget, PythonAbstractNativeObject obj @Cached CExtNodes.GetNativeClassNode getNativeClassNode) { return getNativeClassNode.execute(inliningTarget, object); } - - @Idempotent - protected static boolean hasInitialClass(Shape shape) { - return (shape.getFlags() & PythonObject.CLASS_CHANGED_FLAG) == 0; - } } @Specialization diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/object/PFactory.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/object/PFactory.java index f98d33f97b..4fc9f3dbad 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/object/PFactory.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/object/PFactory.java @@ -579,7 +579,7 @@ public static PBuiltinMethod createNewWrapper(PythonLanguage language, Object ty } public static PBuiltinFunction createBuiltinFunction(PythonLanguage language, PBuiltinFunction function, Object klass) { - PythonBuiltinClassType type = (PythonBuiltinClassType) function.getInitialPythonClass(); + PythonBuiltinClassType type = (PythonBuiltinClassType) function.getPythonClass(); return trace(language, new PBuiltinFunction(type, type.getInstanceShape(language), function.getName(), klass, function.getDefaults(), function.getKwDefaults(), function.getFlags(), function.getCallTarget(), function.getSlot(), function.getSlotWrapper())); From d06b0f0337fb72f0141b9825f660ee7b7ce681e7 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Fri, 25 Jul 2025 13:27:53 +0200 Subject: [PATCH 24/38] Use PNone.NO_VALUE instead of null for DynamicObjectLibrary#setDynamicType() * DynamicObjectLibrary#setDynamicType() does not allow null, and also has a non-null default (ObjectType.DEFAULT). --- .../graal/python/builtins/objects/object/ObjectBuiltins.java | 2 +- .../graal/python/builtins/objects/object/PythonObject.java | 5 +++-- .../src/com/oracle/graal/python/nodes/PGuards.java | 1 + .../com/oracle/graal/python/nodes/object/GetClassNode.java | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/ObjectBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/ObjectBuiltins.java index 557d92a317..ad47a5cb84 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/ObjectBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/ObjectBuiltins.java @@ -227,7 +227,7 @@ static void doPythonObject(PythonObject self, Object newClass, @CachedLibrary(limit = "3") DynamicObjectLibrary dylib) { // Clear the dynamic type when setting the class, so further class changes do not // create new shapes - dylib.setDynamicType(self, null); + dylib.setDynamicType(self, PNone.NO_VALUE); self.setPythonClass(newClass); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/PythonObject.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/PythonObject.java index 7c9596608c..30a2d88dd3 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/PythonObject.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/PythonObject.java @@ -35,6 +35,7 @@ import com.oracle.graal.python.builtins.objects.PythonAbstractObject; import com.oracle.graal.python.builtins.objects.dict.PDict; import com.oracle.graal.python.builtins.objects.type.PythonManagedClass; +import com.oracle.graal.python.builtins.objects.type.TypeNodes.IsSameTypeNode; import com.oracle.graal.python.nodes.HiddenAttr; import com.oracle.graal.python.nodes.PGuards; import com.oracle.graal.python.runtime.PythonOptions; @@ -73,7 +74,7 @@ public class PythonObject extends PythonAbstractObject { public PythonObject(Object pythonClass, Shape instanceShape) { super(instanceShape); assert pythonClass != null; - assert getShape().getDynamicType() == null || getShape().getDynamicType() == pythonClass : getShape().getDynamicType() + " vs " + pythonClass; + assert !PGuards.isPythonClass(getShape().getDynamicType()) || IsSameTypeNode.executeUncached(getShape().getDynamicType(), pythonClass) : getShape().getDynamicType() + " vs " + pythonClass; this.pythonClass = pythonClass; } @@ -87,7 +88,7 @@ public Object getPythonClass() { } public void setPythonClass(Object pythonClass) { - assert getShape().getDynamicType() == null; + assert getShape().getDynamicType() == PNone.NO_VALUE; this.pythonClass = pythonClass; } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/PGuards.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/PGuards.java index a24f9c8a6d..582abf13d2 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/PGuards.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/PGuards.java @@ -255,6 +255,7 @@ public static boolean isNativeClass(Object klass) { return PythonNativeClass.isInstance(klass); } + @Idempotent public static boolean isPythonClass(Object klass) { return PythonAbstractClass.isInstance(klass) || klass instanceof PythonBuiltinClassType; } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/GetClassNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/GetClassNode.java index 3cb58d0fb7..4806ec3693 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/GetClassNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/GetClassNode.java @@ -152,7 +152,7 @@ static Object getDynamicType(Shape shape) { return shape.getDynamicType(); } - @Specialization(guards = {"object.getShape() == cachedShape", "getDynamicType(cachedShape) != null"}, limit = "1") + @Specialization(guards = {"object.getShape() == cachedShape", "isPythonClass(getDynamicType(cachedShape))"}, limit = "1") static Object doConstantClass(@SuppressWarnings("unused") PythonObject object, @Cached(value = "object.getShape()") Shape cachedShape) { return cachedShape.getDynamicType(); From 6ba79c192eab715f47889f54ae23d79b36ae1af1 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Mon, 21 Jul 2025 12:19:37 +0200 Subject: [PATCH 25/38] Merged generic cases in LookupAttributeInMRONode.Dynamic to have a single @InliningCutoff --- .../attributes/LookupAttributeInMRONode.java | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/LookupAttributeInMRONode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/LookupAttributeInMRONode.java index f1fc7be61f..07fcec685f 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/LookupAttributeInMRONode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/LookupAttributeInMRONode.java @@ -75,6 +75,7 @@ import com.oracle.truffle.api.nodes.ExplodeLoop; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.profiles.InlinedBranchProfile; +import com.oracle.truffle.api.profiles.InlinedConditionProfile; import com.oracle.truffle.api.strings.TruffleString; @ImportStatic(PythonOptions.class) @@ -96,20 +97,20 @@ protected static Object lookupConstantMROEquals(Object klass, @SuppressWarnings( return lookup.execute(klass); } - @InliningCutoff - @Specialization(replaces = "lookupConstantMROEquals") - protected Object lookupInBuiltinType(PythonBuiltinClassType klass, TruffleString key, - @Cached ReadAttributeFromPythonObjectNode readAttrNode) { - return findAttr(PythonContext.get(this), klass, key, readAttrNode); - } - + // Merged PythonBuiltinClassType and generic cases to have single @InliningCutoff @InliningCutoff @Specialization(replaces = "lookupConstantMROEquals") protected static Object lookupGeneric(Object klass, TruffleString key, @Bind Node inliningTarget, + @Cached InlinedConditionProfile pbctProfile, + @Cached ReadAttributeFromPythonObjectNode readPBCTAttrNode, @Cached GetMroStorageNode getMroNode, @Cached(value = "createForceType()", uncached = "getUncachedForceType()") ReadAttributeFromObjectNode readAttrNode) { - return lookup(key, getMroNode.execute(inliningTarget, klass), readAttrNode, false); + if (pbctProfile.profile(inliningTarget, klass instanceof PythonBuiltinClassType)) { + return findAttr(PythonContext.get(inliningTarget), (PythonBuiltinClassType) klass, key, readPBCTAttrNode); + } else { + return lookup(key, getMroNode.execute(inliningTarget, klass), readAttrNode, false); + } } @NeverDefault From 37de3e63634d7b4d28e9d02067d097ea6b24730c Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Mon, 21 Jul 2025 12:28:57 +0200 Subject: [PATCH 26/38] Merge the first two specializations of LookupAttributeInMRONode --- .../nodes/attributes/LookupAttributeInMRONode.java | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/LookupAttributeInMRONode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/LookupAttributeInMRONode.java index 07fcec685f..580e77eee4 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/LookupAttributeInMRONode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/LookupAttributeInMRONode.java @@ -164,13 +164,6 @@ public static Object findAttr(Python3Core core, PythonBuiltinClassType klass, Tr return value; } - @Specialization(guards = {"isSingleContext()", "klass == cachedKlass"}, limit = "getAttributeAccessInlineCacheMaxDepth()") - protected static Object lookupPBCTCached(@SuppressWarnings("unused") PythonBuiltinClassType klass, - @Cached("klass") @SuppressWarnings("unused") PythonBuiltinClassType cachedKlass, - @Cached("findAttr(getContext(), cachedKlass, key)") Object cachedValue) { - return cachedValue; - } - @Idempotent protected static boolean canCache(Object value) { return value instanceof Long || @@ -180,8 +173,8 @@ protected static boolean canCache(Object value) { value instanceof PNone; } - @Specialization(guards = {"klass == cachedKlass", "canCache(cachedValue)"}, limit = "getAttributeAccessInlineCacheMaxDepth()") - protected static Object lookupPBCTCachedMulti(@SuppressWarnings("unused") PythonBuiltinClassType klass, + @Specialization(guards = {"klass == cachedKlass", "isSingleContext() || canCache(cachedValue)"}, limit = "getAttributeAccessInlineCacheMaxDepth()") + protected static Object lookupPBCTCached(@SuppressWarnings("unused") PythonBuiltinClassType klass, @Cached("klass") @SuppressWarnings("unused") PythonBuiltinClassType cachedKlass, @Cached("findAttr(getContext(), cachedKlass, key)") Object cachedValue) { return cachedValue; @@ -199,7 +192,7 @@ public static PythonBuiltinClassType findOwnerInMro(Python3Core core, PythonBuil return null; } - @Specialization(replaces = {"lookupPBCTCached", "lookupPBCTCachedMulti"}, guards = "klass == cachedKlass", // + @Specialization(replaces = "lookupPBCTCached", guards = "klass == cachedKlass", // limit = "getAttributeAccessInlineCacheMaxDepth()") @InliningCutoff protected Object lookupPBCTCachedOwner(@SuppressWarnings("unused") PythonBuiltinClassType klass, From 29e374f6d2fbab0f643a0a8fb859673e80dbd4d9 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Mon, 21 Jul 2025 15:40:52 +0200 Subject: [PATCH 27/38] Extract slow-path/@InliningCutoff specializations of LookupAttributeInMRONode in a different node * Better for host inlining of GetFixedAttributeNodeGen.execute: Before: Root[com.oracle.graal.python.nodes.attributes.GetFixedAttributeNodeGen.execute] INLINE com.oracle.graal.python.nodes.attributes.GetFixedAttributeNode.doIt(...) cost 59, subTreeCost 6333, treeInvokes 42, reason within budget] After: Root[com.oracle.graal.python.nodes.attributes.GetFixedAttributeNodeGen.execute] INLINE com.oracle.graal.python.nodes.attributes.GetFixedAttributeNode.doIt(...) cost 59, subTreeCost 4355, treeInvokes 24, reason within budget] --- .../attributes/LookupAttributeInMRONode.java | 283 +++++++++--------- 1 file changed, 147 insertions(+), 136 deletions(-) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/LookupAttributeInMRONode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/LookupAttributeInMRONode.java index 580e77eee4..4bf16606b7 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/LookupAttributeInMRONode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/LookupAttributeInMRONode.java @@ -49,9 +49,8 @@ import com.oracle.graal.python.builtins.objects.type.MroShape.MroShapeLookupResult; import com.oracle.graal.python.builtins.objects.type.PythonAbstractClass; import com.oracle.graal.python.builtins.objects.type.PythonClass; -import com.oracle.graal.python.builtins.objects.type.TypeNodes; import com.oracle.graal.python.builtins.objects.type.TypeNodes.GetMroStorageNode; -import com.oracle.graal.python.builtins.objects.type.TypeNodesFactory.IsSameTypeNodeGen; +import com.oracle.graal.python.builtins.objects.type.TypeNodes.IsSameTypeNode; import com.oracle.graal.python.nodes.PNodeWithContext; import com.oracle.graal.python.runtime.PythonContext; import com.oracle.graal.python.runtime.PythonOptions; @@ -73,6 +72,7 @@ import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.nodes.ControlFlowException; import com.oracle.truffle.api.nodes.ExplodeLoop; +import com.oracle.truffle.api.nodes.ExplodeLoop.LoopExplosionKind; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.profiles.InlinedBranchProfile; import com.oracle.truffle.api.profiles.InlinedConditionProfile; @@ -89,10 +89,10 @@ public abstract static class Dynamic extends PNodeWithContext { public abstract Object execute(Object klass, TruffleString key); @Specialization(guards = "equalNode.execute(inliningTarget, key, cachedKey)", limit = "2") - protected static Object lookupConstantMROEquals(Object klass, @SuppressWarnings("unused") TruffleString key, + static Object lookupConstantMROEquals(Object klass, TruffleString key, @Bind Node inliningTarget, - @Cached("key") @SuppressWarnings("unused") TruffleString cachedKey, - @Cached @Shared @SuppressWarnings("unused") StringUtils.EqualNode equalNode, + @Cached("key") TruffleString cachedKey, + @Cached @Shared StringUtils.EqualNode equalNode, @Cached("create(cachedKey)") LookupAttributeInMRONode lookup) { return lookup.execute(klass); } @@ -100,7 +100,7 @@ protected static Object lookupConstantMROEquals(Object klass, @SuppressWarnings( // Merged PythonBuiltinClassType and generic cases to have single @InliningCutoff @InliningCutoff @Specialization(replaces = "lookupConstantMROEquals") - protected static Object lookupGeneric(Object klass, TruffleString key, + static Object lookupGeneric(Object klass, TruffleString key, @Bind Node inliningTarget, @Cached InlinedConditionProfile pbctProfile, @Cached ReadAttributeFromPythonObjectNode readPBCTAttrNode, @@ -124,9 +124,7 @@ public static LookupAttributeInMRONode.Dynamic getUncached() { } private final boolean skipNonStaticBases; - protected final TruffleString key; - @Child private TypeNodes.IsSameTypeNode isSameTypeNode; - @Child private GetMroStorageNode getMroNode; + final TruffleString key; public LookupAttributeInMRONode(TruffleString key, boolean skipNonStaticBases) { this.key = key; @@ -147,7 +145,7 @@ public static LookupAttributeInMRONode createForLookupOfUnmanagedClasses(Truffle return LookupAttributeInMRONodeGen.create(key, true); } - protected static Object findAttr(Python3Core core, PythonBuiltinClassType klass, TruffleString key) { + static Object findAttr(Python3Core core, PythonBuiltinClassType klass, TruffleString key) { return findAttr(core, klass, key, ReadAttributeFromPythonObjectNode.getUncached()); } @@ -165,7 +163,7 @@ public static Object findAttr(Python3Core core, PythonBuiltinClassType klass, Tr } @Idempotent - protected static boolean canCache(Object value) { + static boolean canCache(Object value) { return value instanceof Long || value instanceof Integer || value instanceof Boolean || @@ -174,45 +172,12 @@ protected static boolean canCache(Object value) { } @Specialization(guards = {"klass == cachedKlass", "isSingleContext() || canCache(cachedValue)"}, limit = "getAttributeAccessInlineCacheMaxDepth()") - protected static Object lookupPBCTCached(@SuppressWarnings("unused") PythonBuiltinClassType klass, - @Cached("klass") @SuppressWarnings("unused") PythonBuiltinClassType cachedKlass, + static Object lookupPBCTCached(PythonBuiltinClassType klass, + @Cached("klass") PythonBuiltinClassType cachedKlass, @Cached("findAttr(getContext(), cachedKlass, key)") Object cachedValue) { return cachedValue; } - public static PythonBuiltinClassType findOwnerInMro(Python3Core core, PythonBuiltinClassType klass, TruffleString key) { - PythonBuiltinClassType current = klass; - ReadAttributeFromPythonObjectNode readNode = ReadAttributeFromPythonObjectNode.getUncached(); - while (current != null) { - if (readNode.execute(core.lookupType(current), key) != PNone.NO_VALUE) { - return current; - } - current = current.getBase(); - } - return null; - } - - @Specialization(replaces = "lookupPBCTCached", guards = "klass == cachedKlass", // - limit = "getAttributeAccessInlineCacheMaxDepth()") - @InliningCutoff - protected Object lookupPBCTCachedOwner(@SuppressWarnings("unused") PythonBuiltinClassType klass, - @Cached("klass") @SuppressWarnings("unused") PythonBuiltinClassType cachedKlass, - @Cached("findOwnerInMro(getContext(), cachedKlass, key)") PythonBuiltinClassType ownerKlass, - @Shared @Cached ReadAttributeFromPythonObjectNode readAttrNode) { - if (ownerKlass == null) { - return PNone.NO_VALUE; - } else { - return readAttrNode.execute(PythonContext.get(this).lookupType(ownerKlass), key); - } - } - - @Specialization(replaces = "lookupPBCTCachedOwner") - @InliningCutoff - protected Object lookupPBCTGeneric(PythonBuiltinClassType klass, - @Shared @Cached ReadAttributeFromPythonObjectNode readAttrNode) { - return findAttr(PythonContext.get(this), klass, key, readAttrNode); - } - // PythonClass specializations: record AttributeAssumptionPair(Assumption assumption, Object value, boolean invalidate) { @@ -227,7 +192,7 @@ private static boolean skipNonStaticBase(Object clsObj, boolean skipNonStaticBas return skipNonStaticBases && clsObj instanceof PythonClass && !((PythonClass) clsObj).isStaticBase(); } - protected AttributeAssumptionPair findAttrAndAssumptionInMRO(Object klass) { + AttributeAssumptionPair findAttrAndAssumptionInMRO(Object klass) { CompilerAsserts.neverPartOfCompilation(); // Regarding potential side effects to MRO caused by __eq__ of the keys in the dicts that we // search through: CPython seems to read the MRO once and then compute the result also @@ -235,14 +200,14 @@ protected AttributeAssumptionPair findAttrAndAssumptionInMRO(Object klass) { // may or may not be visible during subsequent lookups. We want to avoid triggering the side // effects twice, so we succeed this lookup no matter what, however, we will invalidate on // the next lookup if there were some MRO side effects. - MroSequenceStorage mro = getMro(klass); + MroSequenceStorage mro = GetMroStorageNode.executeUncached(klass); Assumption attrAssumption = mro.createAttributeInMROFinalAssumption(key); Object result = PNone.NO_VALUE; for (int i = 0; i < mro.length(); i++) { PythonAbstractClass clsObj = mro.getPythonClassItemNormalized(i); if (i > 0) { assert clsObj != klass : "MRO chain is incorrect: '" + klass + "' was found at position " + i; - getMro(clsObj).addAttributeInMROFinalAssumption(key, attrAssumption); + GetMroStorageNode.executeUncached(clsObj).addAttributeInMROFinalAssumption(key, attrAssumption); } if (skipNonStaticBase(clsObj, skipNonStaticBases)) { continue; @@ -260,13 +225,14 @@ protected AttributeAssumptionPair findAttrAndAssumptionInMRO(Object klass) { } } - @Specialization(guards = {"isSingleContext()", "isSameType(cachedKlass, klass)", "cachedAttrInMROInfo != null"}, // + @Specialization(guards = {"isSingleContext()", "isSameTypeNode.execute(inliningTarget, cachedKlass, klass)", "cachedAttrInMROInfo != null"}, // limit = "getAttributeAccessInlineCacheMaxDepth()", // assumptions = "cachedAttrInMROInfo.assumption()", // rewriteOn = InvalidateLookupException.class) - protected static Object lookupConstantMROCached(@SuppressWarnings("unused") Object klass, + static Object lookupConstantMROCached(Object klass, @Bind Node inliningTarget, - @Cached("klass") @SuppressWarnings("unused") Object cachedKlass, + @Cached("klass") Object cachedKlass, + @Cached IsSameTypeNode isSameTypeNode, @Cached InlinedBranchProfile shouldInvalidate, @Cached("findAttrAndAssumptionInMRO(cachedKlass)") AttributeAssumptionPair cachedAttrInMROInfo) { if (shouldInvalidate.wasEntered(inliningTarget)) { @@ -279,98 +245,151 @@ protected static Object lookupConstantMROCached(@SuppressWarnings("unused") Obje return cachedAttrInMROInfo.value; } - public MroShapeLookupResult lookupInMroShape(MroShape shape, Object klass) { - assert MroShape.validate(klass, PythonLanguage.get(this)); - return shape.lookup(key); - } - - // This specialization works well only for multi-context mode - // Note: MroShape creation and updates are disabled in multi-context mode, see - // PythonClass#initializeMroShape - @Specialization(guards = {"!isSingleContext()", "cachedMroShape != null", "klass.getMroShape() == cachedMroShape"}, // - limit = "getAttributeAccessInlineCacheMaxDepth()") + // The slow-path specializations are extracted in a separate node to have a single cutoff from + // the fast path @InliningCutoff - protected Object lookupConstantMROShape(PythonClass klass, - @SuppressWarnings("unused") @Cached("klass.getMroShape()") MroShape cachedMroShape, - @Cached("lookupInMroShape(cachedMroShape, klass)") MroShapeLookupResult lookupResult) { - return lookupResult.getFromMro(getMro(klass), key); + @Specialization(replaces = {"lookupPBCTCached", "lookupConstantMROCached"}) + Object lookupSlowPath(Object klass, + @Cached SlowPath slowPathNode) { + return slowPathNode.execute(klass, key, skipNonStaticBases); } - @NeverDefault - protected static ReadAttributeFromObjectNode[] create(int size) { - ReadAttributeFromObjectNode[] nodes = new ReadAttributeFromObjectNode[size]; - for (int i = 0; i < size; i++) { - nodes[i] = ReadAttributeFromObjectNode.createForceType(); + public abstract static class SlowPath extends PNodeWithContext { + + @Child private GetMroStorageNode getMroNode; + + public abstract Object execute(Object klass, TruffleString key, boolean skipNonStaticBases); + + static PythonBuiltinClassType findOwnerInMro(Python3Core core, PythonBuiltinClassType klass, TruffleString key) { + PythonBuiltinClassType current = klass; + ReadAttributeFromPythonObjectNode readNode = ReadAttributeFromPythonObjectNode.getUncached(); + while (current != null) { + if (readNode.execute(core.lookupType(current), key) != PNone.NO_VALUE) { + return current; + } + current = current.getBase(); + } + return null; } - return nodes; - } - @Specialization(guards = {"isSingleContext()", "isSameType(cachedKlass, klass)", "mroLength < 32"}, // - limit = "getAttributeAccessInlineCacheMaxDepth()", // - replaces = "lookupConstantMROShape", // - assumptions = "lookupStable") - @ExplodeLoop(kind = ExplodeLoop.LoopExplosionKind.FULL_UNROLL_UNTIL_RETURN) - @InliningCutoff - protected Object lookupConstantMRO(@SuppressWarnings("unused") Object klass, - @Cached("klass") @SuppressWarnings("unused") Object cachedKlass, - @Cached("getMro(cachedKlass)") MroSequenceStorage mro, - @Cached("mro.getLookupStableAssumption()") @SuppressWarnings("unused") Assumption lookupStable, - @Cached("mro.length()") int mroLength, - @Cached("create(mroLength)") ReadAttributeFromObjectNode[] readAttrNodes) { - for (int i = 0; i < mroLength; i++) { - Object kls = mro.getPythonClassItemNormalized(i); - if (skipNonStaticBase(kls, skipNonStaticBases)) { - continue; + // used to be replaces = lookupPBCTCached + @Specialization(guards = "klass == cachedKlass", limit = "getAttributeAccessInlineCacheMaxDepth()") + Object lookupPBCTCachedOwner(PythonBuiltinClassType klass, TruffleString key, boolean skipNonStaticBases, + @Cached("klass") PythonBuiltinClassType cachedKlass, + @Cached("findOwnerInMro(getContext(), cachedKlass, key)") PythonBuiltinClassType ownerKlass, + @Shared @Cached ReadAttributeFromPythonObjectNode readAttrNode) { + if (ownerKlass == null) { + return PNone.NO_VALUE; + } else { + return readAttrNode.execute(PythonContext.get(this).lookupType(ownerKlass), key); } - Object value = readAttrNodes[i].execute(kls, key); - if (value != PNone.NO_VALUE) { - return value; + } + + @Specialization(replaces = "lookupPBCTCachedOwner") + Object lookupPBCTGeneric(PythonBuiltinClassType klass, TruffleString key, boolean skipNonStaticBases, + @Shared @Cached ReadAttributeFromPythonObjectNode readAttrNode) { + return findAttr(PythonContext.get(this), klass, key, readAttrNode); + } + + // This specialization works well only for multi-context mode + // Note: MroShape creation and updates are disabled in multi-context mode, see + // PythonClass#initializeMroShape + @Specialization(guards = {"!isSingleContext()", "cachedMroShape != null", "klass.getMroShape() == cachedMroShape"}, // + limit = "getAttributeAccessInlineCacheMaxDepth()") + Object lookupConstantMROShape(PythonClass klass, TruffleString key, boolean skipNonStaticBases, + @Cached("klass.getMroShape()") MroShape cachedMroShape, + @Cached("lookupInMroShape(cachedMroShape, klass, key)") MroShapeLookupResult lookupResult) { + return lookupResult.getFromMro(getMro(klass), key); + } + + @NeverDefault + static ReadAttributeFromObjectNode[] create(int size) { + ReadAttributeFromObjectNode[] nodes = new ReadAttributeFromObjectNode[size]; + for (int i = 0; i < size; i++) { + nodes[i] = ReadAttributeFromObjectNode.createForceType(); } + return nodes; } - return PNone.NO_VALUE; - } - @Specialization(guards = {"mroLength == cachedMroLength", "cachedMroLength < 32"}, // - replaces = {"lookupConstantMROCached", "lookupConstantMRO"}, // - limit = "getAttributeAccessInlineCacheMaxDepth()") - @ExplodeLoop(kind = ExplodeLoop.LoopExplosionKind.FULL_UNROLL_UNTIL_RETURN) - @InliningCutoff - protected Object lookupCachedLen(@SuppressWarnings("unused") Object klass, - @Bind("getMro(klass)") MroSequenceStorage mro, - @Bind("mro.length()") @SuppressWarnings("unused") int mroLength, - @Cached("mro.length()") int cachedMroLength, - @Cached("create(cachedMroLength)") ReadAttributeFromObjectNode[] readAttrNodes) { - for (int i = 0; i < cachedMroLength; i++) { - Object kls = mro.getPythonClassItemNormalized(i); - if (skipNonStaticBase(kls, skipNonStaticBases)) { - continue; + @Specialization(guards = {"isSingleContext()", "isSameTypeNode.execute(inliningTarget, cachedKlass, klass)", "mroLength < 32"}, // + limit = "getAttributeAccessInlineCacheMaxDepth()", // + replaces = "lookupConstantMROShape", // + assumptions = "lookupStable") + @ExplodeLoop(kind = LoopExplosionKind.FULL_UNROLL_UNTIL_RETURN) + static Object lookupConstantMRO(Object klass, TruffleString key, boolean skipNonStaticBases, + @Bind Node inliningTarget, + @Cached("klass") Object cachedKlass, + @Cached IsSameTypeNode isSameTypeNode, + @Cached("getMroUncached(cachedKlass)") MroSequenceStorage mro, + @Cached("mro.getLookupStableAssumption()") Assumption lookupStable, + @Cached("mro.length()") int mroLength, + @Cached("create(mroLength)") ReadAttributeFromObjectNode[] readAttrNodes) { + for (int i = 0; i < mroLength; i++) { + Object kls = mro.getPythonClassItemNormalized(i); + if (skipNonStaticBase(kls, skipNonStaticBases)) { + continue; + } + Object value = readAttrNodes[i].execute(kls, key); + if (value != PNone.NO_VALUE) { + return value; + } } - Object value = readAttrNodes[i].execute(kls, key); - if (value != PNone.NO_VALUE) { - return value; + return PNone.NO_VALUE; + } + + // used to be replaces = lookupConstantMROCached + @Specialization(guards = {"mroLength == cachedMroLength", "cachedMroLength < 32"}, // + replaces = "lookupConstantMRO", // + limit = "getAttributeAccessInlineCacheMaxDepth()") + @ExplodeLoop(kind = LoopExplosionKind.FULL_UNROLL_UNTIL_RETURN) + Object lookupCachedLen(Object klass, TruffleString key, boolean skipNonStaticBases, + @Bind("getMro(klass)") MroSequenceStorage mro, + @Bind("mro.length()") int mroLength, + @Cached("mro.length()") int cachedMroLength, + @Cached("create(cachedMroLength)") ReadAttributeFromObjectNode[] readAttrNodes) { + for (int i = 0; i < cachedMroLength; i++) { + Object kls = mro.getPythonClassItemNormalized(i); + if (skipNonStaticBase(kls, skipNonStaticBases)) { + continue; + } + Object value = readAttrNodes[i].execute(kls, key); + if (value != PNone.NO_VALUE) { + return value; + } + } + return PNone.NO_VALUE; + } + + // used to be replaces = lookupConstantMROCached + @Specialization(replaces = {"lookupConstantMRO", "lookupCachedLen"}) + @Megamorphic + @InliningCutoff + Object lookupGeneric(Object klass, TruffleString key, boolean skipNonStaticBases, + @Cached("createForceType()") ReadAttributeFromObjectNode readAttrNode) { + return lookup(key, getMro(klass), readAttrNode, skipNonStaticBases); + } + + public MroShapeLookupResult lookupInMroShape(MroShape shape, Object klass, TruffleString key) { + assert MroShape.validate(klass, PythonLanguage.get(this)); + return shape.lookup(key); + } + + GetMroStorageNode ensureGetMroNode() { + if (getMroNode == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + getMroNode = insert(GetMroStorageNode.create()); } + return getMroNode; } - return PNone.NO_VALUE; - } - @Specialization(replaces = {"lookupConstantMROCached", "lookupConstantMRO", "lookupCachedLen"}) - @Megamorphic - @InliningCutoff - protected Object lookupGeneric(Object klass, - @Cached("createForceType()") ReadAttributeFromObjectNode readAttrNode) { - return lookup(key, getMro(klass), readAttrNode, skipNonStaticBases); - } + MroSequenceStorage getMro(Object clazz) { + return ensureGetMroNode().executeCached(clazz); + } - protected GetMroStorageNode ensureGetMroNode() { - if (getMroNode == null) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - getMroNode = insert(GetMroStorageNode.create()); + static MroSequenceStorage getMroUncached(Object clazz) { + return GetMroStorageNode.executeUncached(clazz); } - return getMroNode; - } - protected MroSequenceStorage getMro(Object clazz) { - return ensureGetMroNode().executeCached(clazz); } @TruffleBoundary @@ -391,12 +410,4 @@ public static Object lookup(TruffleString key, MroSequenceStorage mro, ReadAttri } return PNone.NO_VALUE; } - - protected boolean isSameType(Object cachedKlass, Object klass) { - if (isSameTypeNode == null) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - isSameTypeNode = insert(IsSameTypeNodeGen.create()); - } - return isSameTypeNode.executeCached(cachedKlass, klass); - } } From dc9fc0665a3154ac7ff0a321ad340004d8f2d34e Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Mon, 21 Jul 2025 17:10:25 +0200 Subject: [PATCH 28/38] Remove duplicated specialization in PyObjectGetMethod * Avoiding a LookupAttributeInMRONode.Dynamic is not worth this duplication. --- .../graal/python/lib/PyObjectGetMethod.java | 67 +++---------------- 1 file changed, 10 insertions(+), 57 deletions(-) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectGetMethod.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectGetMethod.java index 7df9bf9fcf..695a081960 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectGetMethod.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectGetMethod.java @@ -67,7 +67,6 @@ import com.oracle.truffle.api.dsl.Bind; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Cached.Exclusive; -import com.oracle.truffle.api.dsl.Cached.Shared; import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.GenerateCached; import com.oracle.truffle.api.dsl.GenerateInline; @@ -105,24 +104,23 @@ protected static boolean isObjectGetAttribute(Node inliningTarget, GetCachedTpSl } // isObjectGetAttribute implies not foreign - @Specialization(guards = {"isObjectGetAttribute(inliningTarget, getTypeSlotsNode, lazyClass)", "name == cachedName"}, limit = "1") - static Object getFixedAttr(VirtualFrame frame, Node inliningTarget, Object receiver, @SuppressWarnings("unused") TruffleString name, - @SuppressWarnings("unused") /* Truffle bug: @Shared("getClassNode") */ @Exclusive @Cached GetClassNode getClass, - @SuppressWarnings("unused") @Exclusive @Cached GetCachedTpSlotsNode getTypeSlotsNode, + @Specialization(guards = "isObjectGetAttribute(inliningTarget, getTypeSlotsNode, lazyClass)", limit = "1") + static Object getAttr(VirtualFrame frame, Node inliningTarget, Object receiver, TruffleString name, + @Cached GetClassNode getClass, + @Cached GetCachedTpSlotsNode getTypeSlotsNode, @Bind("getClass.execute(inliningTarget, receiver)") Object lazyClass, - @SuppressWarnings("unused") @Cached("name") TruffleString cachedName, - @Cached("create(name)") LookupAttributeInMRONode lookupNode, - @Exclusive @Cached GetObjectSlotsNode getSlotsNode, - @Exclusive @Cached CallSlotDescrGet callGetNode, - @Shared("readAttr") @Cached(inline = false) ReadAttributeFromObjectNode readAttr, - @Exclusive @Cached PRaiseNode raiseNode, + @Cached LookupAttributeInMRONode.Dynamic lookupNode, + @Cached GetObjectSlotsNode getSlotsNode, + @Cached CallSlotDescrGet callGetNode, + @Cached ReadAttributeFromObjectNode readAttr, + @Cached PRaiseNode raiseNode, @Cached InlinedBranchProfile hasDescr, @Cached InlinedBranchProfile returnDataDescr, @Cached InlinedBranchProfile returnAttr, @Cached InlinedBranchProfile returnUnboundMethod, @Cached InlinedBranchProfile returnBoundDescr) { boolean methodFound = false; - Object descr = lookupNode.execute(lazyClass); + Object descr = lookupNode.execute(lazyClass, name); TpSlot getMethod = null; if (descr != PNone.NO_VALUE) { hasDescr.enter(inliningTarget); @@ -161,51 +159,6 @@ static Object getFixedAttr(VirtualFrame frame, Node inliningTarget, Object recei throw raiseNode.raiseAttributeError(inliningTarget, ErrorMessages.OBJ_P_HAS_NO_ATTR_S, receiver, name); } - // No explicit branch profiling when we're looking up multiple things - // isObjectGetAttribute implies not foreign - @Specialization(guards = "isObjectGetAttribute(inliningTarget, getTypeSlotsNode, lazyClass)", replaces = "getFixedAttr", limit = "1") - @InliningCutoff - static Object getDynamicAttr(Frame frame, Node inliningTarget, Object receiver, TruffleString name, - @SuppressWarnings("unused") @Exclusive @Cached GetClassNode getClass, - @SuppressWarnings("unused") @Exclusive @Cached GetCachedTpSlotsNode getTypeSlotsNode, - @Bind("getClass.execute(inliningTarget, receiver)") Object lazyClass, - @Cached(inline = false) LookupAttributeInMRONode.Dynamic lookupNode, - @Exclusive @Cached GetObjectSlotsNode getSlotsNode, - @Exclusive @Cached CallSlotDescrGet callGetNode, - @Shared("readAttr") @Cached(inline = false) ReadAttributeFromObjectNode readAttr, - /* Truffle bug: @Shared("raiseNode") */ @Exclusive @Cached PRaiseNode raiseNode) { - boolean methodFound = false; - Object descr = lookupNode.execute(lazyClass, name); - TpSlot getMethod = null; - if (descr != PNone.NO_VALUE) { - if (MaybeBindDescriptorNode.isMethodDescriptor(descr)) { - methodFound = true; - } else { - var descrSlots = getSlotsNode.execute(inliningTarget, descr); - getMethod = descrSlots.tp_descr_get(); - if (getMethod != null && TpSlotDescrSet.PyDescr_IsData(descrSlots)) { - return new BoundDescriptor(callGetNode.execute((VirtualFrame) frame, inliningTarget, getMethod, descr, receiver, lazyClass)); - } - } - } - if (receiver instanceof PythonAbstractObject) { - Object attr = readAttr.execute(receiver, name); - if (attr != PNone.NO_VALUE) { - return new BoundDescriptor(attr); - } - } - if (methodFound) { - return descr; - } - if (getMethod != null) { - return new BoundDescriptor(callGetNode.execute((VirtualFrame) frame, inliningTarget, getMethod, descr, receiver, lazyClass)); - } - if (descr != PNone.NO_VALUE) { - return new BoundDescriptor(descr); - } - throw raiseNode.raiseAttributeError(inliningTarget, ErrorMessages.OBJ_P_HAS_NO_ATTR_S, receiver, name); - } - @Specialization(guards = "isForeignObject(inliningTarget, isForeignObjectNode, receiver)", limit = "1") @InliningCutoff static Object getForeignMethod(VirtualFrame frame, Node inliningTarget, Object receiver, TruffleString name, From 4160318fdcd551cb0c6a8db064f6b92d12065e9d Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Tue, 22 Jul 2025 09:50:26 +0200 Subject: [PATCH 29/38] Use @NonIdempotent instead of passing an extra dynamic argument --- .../attributes/ReadAttributeFromObjectNode.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/ReadAttributeFromObjectNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/ReadAttributeFromObjectNode.java index f331799bab..46956accc0 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/ReadAttributeFromObjectNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/ReadAttributeFromObjectNode.java @@ -65,6 +65,7 @@ import com.oracle.truffle.api.dsl.GenerateUncached; import com.oracle.truffle.api.dsl.ImportStatic; import com.oracle.truffle.api.dsl.NeverDefault; +import com.oracle.truffle.api.dsl.NonIdempotent; import com.oracle.truffle.api.dsl.ReportPolymorphism; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.nodes.Node; @@ -97,10 +98,8 @@ public static ReadAttributeFromObjectNode getUncachedForceType() { public abstract Object execute(PythonModule object, TruffleString key); - /** - * @param module Non-cached parameter to help the DSL produce a guard, not an assertion - */ - protected static HashingStorage getStorage(Object module, PHashingCollection cachedGlobals) { + @NonIdempotent + protected static HashingStorage getStorage(PHashingCollection cachedGlobals) { return cachedGlobals.getDictStorage(); } @@ -111,15 +110,16 @@ protected static PDict getDict(Object object) { // special case for the very common module read @Specialization(guards = {"isSingleContext()", "cachedObject == object", - // no need to check the cachedDict for equality, module.__dict__ is read-only - "getStorage(object, cachedDict) == cachedStorage" + // no need to check "getDict(object) == cachedDict", module.__dict__ is + // read-only + "getStorage(cachedDict) == cachedStorage" }, limit = "1") @SuppressWarnings("unused") protected static Object readFromBuiltinModuleDict(PythonModule object, TruffleString key, @Bind Node inliningTarget, @Cached(value = "object", weak = true) PythonModule cachedObject, @Cached(value = "getDict(object)", weak = true) PHashingCollection cachedDict, - @Cached(value = "getStorage(object, getDict(object))", weak = true) HashingStorage cachedStorage, + @Cached(value = "getStorage(getDict(object))", weak = true) HashingStorage cachedStorage, @Exclusive @Cached HashingStorageGetItem getItem) { // note that we don't need to pass the state here - string keys are hashable by definition Object value = getItem.execute(inliningTarget, cachedStorage, key); From 110a75ccd6ac920118c698f0f36e89b865d0a98c Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Tue, 22 Jul 2025 16:51:11 +0200 Subject: [PATCH 30/38] Remove duplicate specializations in GenericSetAttrNode and callers --- .../modules/ctypes/UnionTypeBuiltins.java | 41 +++---------- .../objects/object/ObjectBuiltins.java | 19 +++--- .../builtins/objects/object/ObjectNodes.java | 60 +++---------------- .../builtins/objects/str/StringNodes.java | 5 ++ .../objects/thread/ThreadLocalBuiltins.java | 19 ++---- .../builtins/objects/type/TypeBuiltins.java | 24 +++----- 6 files changed, 43 insertions(+), 125 deletions(-) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ctypes/UnionTypeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ctypes/UnionTypeBuiltins.java index 3fd652cfdb..64a13a518f 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ctypes/UnionTypeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ctypes/UnionTypeBuiltins.java @@ -40,6 +40,7 @@ */ package com.oracle.graal.python.builtins.modules.ctypes; +import static com.oracle.graal.python.nodes.ErrorMessages.ATTR_NAME_MUST_BE_STRING; import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING; import java.util.List; @@ -53,25 +54,19 @@ import com.oracle.graal.python.builtins.modules.ctypes.StructUnionTypeBuiltins.PyCStructUnionTypeUpdateStgDict; import com.oracle.graal.python.builtins.modules.ctypes.StructUnionTypeBuiltins.StructUnionTypeNewNode; import com.oracle.graal.python.builtins.objects.object.ObjectNodes; -import com.oracle.graal.python.builtins.objects.object.ObjectNodes.GenericSetAttrNode; +import com.oracle.graal.python.builtins.objects.str.StringNodes.CastToTruffleStringChecked0Node; import com.oracle.graal.python.builtins.objects.type.TpSlots; import com.oracle.graal.python.builtins.objects.type.slots.TpSlotSetAttr.SetAttrBuiltinNode; -import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.attributes.WriteAttributeToObjectNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; -import com.oracle.graal.python.nodes.util.CastToTruffleStringNode; -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.Cached.Exclusive; -import com.oracle.truffle.api.dsl.Cached.Shared; import com.oracle.truffle.api.dsl.GenerateNodeFactory; import com.oracle.truffle.api.dsl.NodeFactory; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.strings.TruffleString; -import com.oracle.truffle.api.strings.TruffleString.EqualNode; @CoreFunctions(extendClasses = PythonBuiltinClassType.UnionType) public final class UnionTypeBuiltins extends PythonBuiltins { @@ -96,38 +91,20 @@ protected boolean isStruct() { @GenerateNodeFactory protected abstract static class SetattrNode extends SetAttrBuiltinNode { @Specialization - static void doStringKey(VirtualFrame frame, Object object, TruffleString key, Object value, - @Bind Node inliningTarget, - @Exclusive @Cached ObjectNodes.GenericSetAttrNode genericSetAttrNode, - @Shared @Cached WriteAttributeToObjectNode write, - @Shared @Cached TruffleString.EqualNode equalNode, - @Shared @Cached PyCStructUnionTypeUpdateStgDict updateStgDict) { - genericSetAttrNode.execute(inliningTarget, frame, object, key, value, write); - updateStgDictIfNecessary(frame, object, key, value, equalNode, updateStgDict); - } - - // @Exclusive to address warning - @Specialization - @InliningCutoff static void doGenericKey(VirtualFrame frame, Object object, Object keyObject, Object value, @Bind Node inliningTarget, - @Cached CastToTruffleStringNode castKeyNode, - @Cached PRaiseNode raiseNode, - @Exclusive @Cached ObjectNodes.GenericSetAttrNode genericSetAttrNode, - @Shared @Cached WriteAttributeToObjectNode write, - @Shared @Cached TruffleString.EqualNode equalNode, - @Shared @Cached PyCStructUnionTypeUpdateStgDict updateStgDict) { - TruffleString key = GenericSetAttrNode.castAttributeKey(inliningTarget, keyObject, castKeyNode, raiseNode); + @Cached CastToTruffleStringChecked0Node castKeyNode, + @Cached ObjectNodes.GenericSetAttrNode genericSetAttrNode, + @Cached WriteAttributeToObjectNode write, + @Cached TruffleString.EqualNode equalNode, + @Cached PyCStructUnionTypeUpdateStgDict updateStgDict) { + TruffleString key = castKeyNode.cast(inliningTarget, keyObject, ATTR_NAME_MUST_BE_STRING); genericSetAttrNode.execute(inliningTarget, frame, object, key, value, write); - updateStgDictIfNecessary(frame, object, key, value, equalNode, updateStgDict); - } - - private static void updateStgDictIfNecessary(VirtualFrame frame, Object object, TruffleString key, Object value, - EqualNode equalNode, PyCStructUnionTypeUpdateStgDict updateStgDict) { if (equalNode.execute(key, StructUnionTypeBuiltins.T__FIELDS_, TS_ENCODING)) { updateStgDict.execute(frame, object, value, false); } } + } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/ObjectBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/ObjectBuiltins.java index ad47a5cb84..ff400146e8 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/ObjectBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/ObjectBuiltins.java @@ -29,6 +29,7 @@ import static com.oracle.graal.python.builtins.PythonBuiltinClassType.TypeError; import static com.oracle.graal.python.builtins.objects.cext.capi.NativeCAPISymbol.FUN_PY_OBJECT_NEW; import static com.oracle.graal.python.nodes.BuiltinNames.J_OBJECT; +import static com.oracle.graal.python.nodes.ErrorMessages.ATTR_NAME_MUST_BE_STRING; import static com.oracle.graal.python.nodes.PGuards.isDeleteMarker; import static com.oracle.graal.python.nodes.PGuards.isDict; import static com.oracle.graal.python.nodes.PGuards.isNoValue; @@ -87,6 +88,7 @@ import com.oracle.graal.python.builtins.objects.object.ObjectBuiltinsFactory.GetAttributeNodeFactory; import com.oracle.graal.python.builtins.objects.set.PSet; import com.oracle.graal.python.builtins.objects.set.SetBuiltins; +import com.oracle.graal.python.builtins.objects.str.StringNodes.CastToTruffleStringChecked0Node; import com.oracle.graal.python.builtins.objects.str.StringNodes.CastToTruffleStringChecked1Node; import com.oracle.graal.python.builtins.objects.thread.ThreadLocalBuiltins; import com.oracle.graal.python.builtins.objects.type.PythonBuiltinClass; @@ -589,19 +591,12 @@ public static GetAttributeNode create() { @GenerateNodeFactory public abstract static class SetattrNode extends SetAttrBuiltinNode { @Specialization - void setString(VirtualFrame frame, Object object, TruffleString key, Object value, + void set(VirtualFrame frame, Object object, Object keyObject, Object value, @Bind Node inliningTarget, - @Shared @Cached ObjectNodes.GenericSetAttrNode genericSetAttrNode, - @Shared @Cached WriteAttributeToObjectNode write) { - genericSetAttrNode.execute(inliningTarget, frame, object, key, value, write); - } - - @Specialization - @InliningCutoff - void setGeneric(VirtualFrame frame, Object object, Object key, Object value, - @Bind Node inliningTarget, - @Shared @Cached ObjectNodes.GenericSetAttrNode genericSetAttrNode, - @Shared @Cached WriteAttributeToObjectNode write) { + @Cached CastToTruffleStringChecked0Node castKeyNode, + @Cached ObjectNodes.GenericSetAttrNode genericSetAttrNode, + @Cached WriteAttributeToObjectNode write) { + TruffleString key = castKeyNode.cast(inliningTarget, keyObject, ATTR_NAME_MUST_BE_STRING); genericSetAttrNode.execute(inliningTarget, frame, object, key, value, write); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/ObjectNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/ObjectNodes.java index 1ac1e4da16..3fc39fc4d3 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/ObjectNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/ObjectNodes.java @@ -43,7 +43,6 @@ import static com.oracle.graal.python.builtins.PythonBuiltinClassType.ValueError; import static com.oracle.graal.python.builtins.objects.cext.capi.NativeCAPISymbol.FUN_CHECK_BASICSIZE_FOR_GETSTATE; import static com.oracle.graal.python.nodes.BuiltinNames.T_COPYREG; -import static com.oracle.graal.python.nodes.ErrorMessages.ATTR_NAME_MUST_BE_STRING; import static com.oracle.graal.python.nodes.ErrorMessages.CANNOT_PICKLE_OBJECT_TYPE; import static com.oracle.graal.python.nodes.ErrorMessages.MUST_BE_TYPE_A_NOT_TYPE_B; import static com.oracle.graal.python.nodes.ErrorMessages.SHOULD_RETURN_A_NOT_B; @@ -137,7 +136,6 @@ import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsAnyBuiltinObjectProfile; import com.oracle.graal.python.nodes.object.GetClassNode; import com.oracle.graal.python.nodes.object.IsForeignObjectNode; -import com.oracle.graal.python.nodes.util.CannotCastException; import com.oracle.graal.python.nodes.util.CastToTruffleStringNode; import com.oracle.graal.python.runtime.PythonContext; import com.oracle.graal.python.runtime.PythonOptions; @@ -839,56 +837,16 @@ public abstract static class GenericSetAttrNode extends Node { */ public abstract void execute(Node inliningTarget, VirtualFrame frame, Object object, TruffleString key, Object value, WriteAttributeToObjectNode writeNode); - public abstract void execute(Node inliningTarget, VirtualFrame frame, Object object, Object key, Object value, WriteAttributeToObjectNode writeNode); - - @Specialization - static void doStringKey(Node inliningTarget, VirtualFrame frame, Object object, TruffleString key, Object value, WriteAttributeToObjectNode writeNode, - @SuppressWarnings("unused") @Shared @Cached CastToTruffleStringNode castKeyToStringNode, - @Shared @Cached GetClassNode getClassNode, - @Shared @Cached InlinedConditionProfile hasDescriptor, - @Shared @Cached GetObjectSlotsNode getDescrSlotsNode, - @Shared @Cached CallSlotDescrSet callSetNode, - @Shared @Cached(inline = false) LookupAttributeInMRONode.Dynamic getExisting, - @Shared @Cached(inline = false) ReadAttributeFromObjectNode attrRead, - @Shared @Cached InlinedBranchProfile deleteNonExistingBranchProfile, - @Shared @Cached PRaiseNode raiseNode) { - setAttr(inliningTarget, frame, object, key, value, writeNode, getClassNode, hasDescriptor, - getDescrSlotsNode, callSetNode, getExisting, attrRead, deleteNonExistingBranchProfile, - raiseNode); - } - @Specialization - @InliningCutoff - static void doGeneric(Node inliningTarget, VirtualFrame frame, Object object, Object keyObject, Object value, WriteAttributeToObjectNode writeNode, - @Shared @Cached CastToTruffleStringNode castKeyToStringNode, - @Shared @Cached GetClassNode getClassNode, - @Shared @Cached InlinedConditionProfile hasDescriptor, - @Shared @Cached GetObjectSlotsNode getDescrSlotsNode, - @Shared @Cached CallSlotDescrSet callSetNode, - @Shared @Cached(inline = false) LookupAttributeInMRONode.Dynamic getExisting, - @Shared @Cached(inline = false) ReadAttributeFromObjectNode attrRead, - @Shared @Cached InlinedBranchProfile deleteNonExistingBranchProfile, - @Shared @Cached PRaiseNode raiseNode) { - TruffleString key = castAttributeKey(inliningTarget, keyObject, castKeyToStringNode, raiseNode); - setAttr(inliningTarget, frame, object, key, value, writeNode, getClassNode, hasDescriptor, - getDescrSlotsNode, callSetNode, getExisting, attrRead, deleteNonExistingBranchProfile, - raiseNode); - } - - public static TruffleString castAttributeKey(Node inliningTarget, Object keyObject, CastToTruffleStringNode castKeyToStringNode, PRaiseNode raiseNode) { - try { - return castKeyToStringNode.execute(inliningTarget, keyObject); - } catch (CannotCastException e) { - throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.TypeError, ATTR_NAME_MUST_BE_STRING, keyObject); - } - } - - private static void setAttr(Node inliningTarget, VirtualFrame frame, Object object, TruffleString key, - Object value, WriteAttributeToObjectNode writeNode, GetClassNode getClassNode, - InlinedConditionProfile hasDescriptor, GetObjectSlotsNode getDescrSlotsNode, - CallSlotDescrSet callSetNode, LookupAttributeInMRONode.Dynamic getExisting, - ReadAttributeFromObjectNode attrRead, InlinedBranchProfile deleteNonExistingBranchProfile, - PRaiseNode raiseNode) { + static void doGeneric(Node inliningTarget, VirtualFrame frame, Object object, TruffleString key, Object value, WriteAttributeToObjectNode writeNode, + @Cached GetClassNode getClassNode, + @Cached InlinedConditionProfile hasDescriptor, + @Cached GetObjectSlotsNode getDescrSlotsNode, + @Cached CallSlotDescrSet callSetNode, + @Cached(inline = false) LookupAttributeInMRONode.Dynamic getExisting, + @Cached ReadAttributeFromObjectNode attrRead, + @Cached InlinedBranchProfile deleteNonExistingBranchProfile, + @Cached PRaiseNode raiseNode) { Object type = getClassNode.execute(inliningTarget, object); Object descr = getExisting.execute(type, key); if (hasDescriptor.profile(inliningTarget, !PGuards.isNoValue(descr))) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/StringNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/StringNodes.java index 780c0828fb..331e6e2f2c 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/StringNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/str/StringNodes.java @@ -61,6 +61,7 @@ import com.oracle.graal.python.builtins.objects.common.SequenceNodes; import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes; import com.oracle.graal.python.builtins.objects.ints.PInt; +import com.oracle.graal.python.builtins.objects.str.StringNodesFactory.CastToTruffleStringChecked0NodeGen; import com.oracle.graal.python.builtins.objects.str.StringNodesFactory.IsInternedStringNodeGen; import com.oracle.graal.python.builtins.objects.str.StringNodesFactory.StringMaterializeNodeGen; import com.oracle.graal.python.lib.IteratorExhausted; @@ -247,6 +248,10 @@ static String doConvert(Node inliningTarget, Object self, TruffleString errMsgFo @GenerateInline @GenerateCached(false) public abstract static class CastToTruffleStringChecked0Node extends PNodeWithContext { + public static CastToTruffleStringChecked0Node getUncached() { + return CastToTruffleStringChecked0NodeGen.getUncached(); + } + public final TruffleString cast(Node inliningTarget, Object object, TruffleString errMsg) { return execute(inliningTarget, object, errMsg); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/thread/ThreadLocalBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/thread/ThreadLocalBuiltins.java index b250c86469..6e2dc4c8b2 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/thread/ThreadLocalBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/thread/ThreadLocalBuiltins.java @@ -40,6 +40,7 @@ */ package com.oracle.graal.python.builtins.objects.thread; +import static com.oracle.graal.python.nodes.ErrorMessages.ATTR_NAME_MUST_BE_STRING; import static com.oracle.graal.python.nodes.SpecialAttributeNames.J___DICT__; import java.util.List; @@ -57,8 +58,8 @@ import com.oracle.graal.python.builtins.objects.dict.PDict; import com.oracle.graal.python.builtins.objects.function.PKeyword; import com.oracle.graal.python.builtins.objects.object.ObjectBuiltins; -import com.oracle.graal.python.builtins.objects.object.ObjectNodes; import com.oracle.graal.python.builtins.objects.object.ObjectNodes.GenericSetAttrWithDictNode; +import com.oracle.graal.python.builtins.objects.str.StringNodes.CastToTruffleStringChecked0Node; import com.oracle.graal.python.builtins.objects.str.StringNodes.CastToTruffleStringChecked1Node; import com.oracle.graal.python.builtins.objects.type.TpSlots; import com.oracle.graal.python.builtins.objects.type.TpSlots.GetObjectSlotsNode; @@ -78,7 +79,6 @@ import com.oracle.graal.python.nodes.function.PythonBuiltinNode; import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; import com.oracle.graal.python.nodes.object.GetClassNode; -import com.oracle.graal.python.nodes.util.CastToTruffleStringNode; import com.oracle.graal.python.runtime.object.PFactory; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff; @@ -210,27 +210,16 @@ private Object dispatchDescrGet(VirtualFrame frame, Object object, Object type, @Slot(value = SlotKind.tp_setattro, isComplex = true) @GenerateNodeFactory public abstract static class SetattrNode extends SetAttrBuiltinNode { - @Specialization - static void doStringKey(VirtualFrame frame, PThreadLocal object, TruffleString key, Object value, - @Bind Node inliningTarget, - @Exclusive @Cached ThreadLocalNodes.GetThreadLocalDict getThreadLocalDict, - @Exclusive @Cached GenericSetAttrWithDictNode setAttrWithDictNode) { - // Note: getting thread local dict has potential side-effects, don't move - PDict localDict = getThreadLocalDict.execute(frame, object); - setAttrWithDictNode.execute(inliningTarget, frame, object, key, value, localDict); - } - @Specialization @InliningCutoff static void doGeneric(VirtualFrame frame, PThreadLocal object, Object keyObject, Object value, @Bind Node inliningTarget, @Exclusive @Cached ThreadLocalNodes.GetThreadLocalDict getThreadLocalDict, - @Cached CastToTruffleStringNode castKeyToStringNode, - @Exclusive @Cached PRaiseNode raiseNode, + @Cached CastToTruffleStringChecked0Node castKeyNode, @Exclusive @Cached GenericSetAttrWithDictNode setAttrWithDictNode) { // Note: getting thread local dict has potential side-effects, don't move PDict localDict = getThreadLocalDict.execute(frame, object); - TruffleString key = ObjectNodes.GenericSetAttrNode.castAttributeKey(inliningTarget, keyObject, castKeyToStringNode, raiseNode); + TruffleString key = castKeyNode.cast(inliningTarget, keyObject, ATTR_NAME_MUST_BE_STRING); setAttrWithDictNode.execute(inliningTarget, frame, object, key, value, localDict); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeBuiltins.java index 2b56ab2d01..861ed02178 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeBuiltins.java @@ -31,6 +31,7 @@ import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyHeapTypeObject__ht_qualname; import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyTypeObject__tp_name; import static com.oracle.graal.python.nodes.BuiltinNames.T_BUILTINS; +import static com.oracle.graal.python.nodes.ErrorMessages.ATTR_NAME_MUST_BE_STRING; import static com.oracle.graal.python.nodes.SpecialAttributeNames.J___ABSTRACTMETHODS__; import static com.oracle.graal.python.nodes.SpecialAttributeNames.J___ANNOTATIONS__; import static com.oracle.graal.python.nodes.SpecialAttributeNames.J___BASES__; @@ -103,6 +104,7 @@ import com.oracle.graal.python.builtins.objects.set.PSet; import com.oracle.graal.python.builtins.objects.set.SetBuiltins.UpdateSingleNode; import com.oracle.graal.python.builtins.objects.str.PString; +import com.oracle.graal.python.builtins.objects.str.StringNodes.CastToTruffleStringChecked0Node; import com.oracle.graal.python.builtins.objects.str.StringNodes.CastToTruffleStringChecked1Node; import com.oracle.graal.python.builtins.objects.str.StringUtils.SimpleTruffleStringFormatNode; import com.oracle.graal.python.builtins.objects.thread.ThreadLocalBuiltins; @@ -163,7 +165,6 @@ import com.oracle.graal.python.util.PythonUtils; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; -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.Cached.Exclusive; @@ -617,29 +618,22 @@ private Object dispatchValueGet(VirtualFrame frame, Object type, Object descr, T @GenerateNodeFactory public abstract static class SetattrNode extends SetAttrBuiltinNode { @Specialization(guards = "!isImmutable(object)") - void setString(VirtualFrame frame, Object object, TruffleString key, Object value, + static void set(VirtualFrame frame, Object object, Object keyObject, Object value, @Bind Node inliningTarget, - @Shared @Cached ObjectNodes.GenericSetAttrNode genericSetAttrNode, - @Shared @Cached("createForceType()") WriteAttributeToObjectNode write) { + @Cached CastToTruffleStringChecked0Node castKeyNode, + @Cached ObjectNodes.GenericSetAttrNode genericSetAttrNode, + @Cached("createForceType()") WriteAttributeToObjectNode write) { + TruffleString key = castKeyNode.cast(inliningTarget, keyObject, ATTR_NAME_MUST_BE_STRING); genericSetAttrNode.execute(inliningTarget, frame, object, key, value, write); } - @Specialization(guards = "!isImmutable(object)") - @InliningCutoff - static void set(VirtualFrame frame, Object object, Object key, Object value, - @Bind Node inliningTarget, - @Shared @Cached ObjectNodes.GenericSetAttrNode genericSetAttrNode, - @Shared @Cached("createForceType()") WriteAttributeToObjectNode write) { - genericSetAttrNode.execute(inliningTarget, frame, object, key, value, write); - } - - @Specialization(guards = "isImmutable(object)") @TruffleBoundary + @Specialization(guards = "isImmutable(object)") void setBuiltin(Object object, Object key, Object value) { if (PythonContext.get(this).isInitialized()) { throw PRaiseNode.raiseStatic(this, TypeError, ErrorMessages.CANT_SET_ATTRIBUTE_R_OF_IMMUTABLE_TYPE_N, PyObjectReprAsTruffleStringNode.executeUncached(key), object); } else { - set(null, object, key, value, null, ObjectNodes.GenericSetAttrNode.getUncached(), WriteAttributeToObjectNode.getUncached(true)); + set(null, object, key, value, null, CastToTruffleStringChecked0Node.getUncached(), ObjectNodes.GenericSetAttrNode.getUncached(), WriteAttributeToObjectNode.getUncached(true)); } } From c154af03042dd2a62eaf2b223447fc84d42175c4 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Fri, 25 Jul 2025 11:48:48 +0200 Subject: [PATCH 31/38] Optimize IsTypeNode by avoiding native call for native objects --- graalpython/com.oracle.graal.python.cext/src/capi.c | 4 ---- .../builtins/objects/cext/capi/NativeCAPISymbol.java | 1 - .../graal/python/builtins/objects/type/TypeNodes.java | 9 +++++---- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/graalpython/com.oracle.graal.python.cext/src/capi.c b/graalpython/com.oracle.graal.python.cext/src/capi.c index cb2bd845be..30321ff113 100644 --- a/graalpython/com.oracle.graal.python.cext/src/capi.c +++ b/graalpython/com.oracle.graal.python.cext/src/capi.c @@ -616,10 +616,6 @@ PyAPI_FUNC(void*) GraalPyPrivate_PointerAddOffset(void* x, Py_ssize_t y) { return (char *)x + y; } -PyAPI_FUNC(int) GraalPyPrivate_SubclassCheck(PyObject* type) { - return PyType_FastSubclass(Py_TYPE(type), Py_TPFLAGS_TYPE_SUBCLASS); -} - // Implements the basesisze check in typeobject.c:_PyObject_GetState PyAPI_FUNC(int) GraalPyPrivate_CheckBasicsizeForGetstate(PyTypeObject* type, int slot_num) { Py_ssize_t basicsize = PyBaseObject_Type.tp_basicsize; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/NativeCAPISymbol.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/NativeCAPISymbol.java index 553e9f5296..5652f38f61 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/NativeCAPISymbol.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/NativeCAPISymbol.java @@ -123,7 +123,6 @@ public enum NativeCAPISymbol implements NativeCExtSymbol { FUN_FLOAT_SUBTYPE_NEW("GraalPyPrivate_Float_SubtypeNew", PyObjectTransfer, PyTypeObject, ArgDescriptor.Double), FUN_COMPLEX_SUBTYPE_FROM_DOUBLES("GraalPyPrivate_Complex_SubtypeFromDoubles", PyObjectTransfer, PyTypeObject, ArgDescriptor.Double, ArgDescriptor.Double), FUN_EXCEPTION_SUBTYPE_NEW("GraalPyPrivate_Exception_SubtypeNew", PyObjectTransfer, PyTypeObject, PyObject), - FUN_SUBCLASS_CHECK("GraalPyPrivate_SubclassCheck", Int, PyObject), FUN_UNICODE_SUBTYPE_NEW("GraalPyPrivate_Unicode_SubtypeNew", PyObjectTransfer, PyTypeObject, PyObject), FUN_CHECK_BASICSIZE_FOR_GETSTATE("GraalPyPrivate_CheckBasicsizeForGetstate", Int, PyTypeObject, Int), FUN_MMAP_INIT_BUFFERPROTOCOL("GraalPyPrivate_MMap_InitBufferProtocol", ArgDescriptor.Void, PyTypeObject), diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeNodes.java index 2ff9bbfba8..b528b23d75 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeNodes.java @@ -43,7 +43,6 @@ import static com.oracle.graal.python.builtins.PythonBuiltinClassType.SystemError; import static com.oracle.graal.python.builtins.PythonBuiltinClassType.TypeError; import static com.oracle.graal.python.builtins.objects.PNone.NO_VALUE; -import static com.oracle.graal.python.builtins.objects.cext.capi.NativeCAPISymbol.FUN_SUBCLASS_CHECK; import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyHeapTypeObject__ht_qualname; import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyTypeObject__tp_base; import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyTypeObject__tp_bases; @@ -1752,17 +1751,19 @@ static boolean doBuiltinType(@SuppressWarnings("unused") PythonBuiltinClassType } @Specialization - @InliningCutoff static boolean doNativeClass(Node inliningTarget, PythonAbstractNativeObject obj, @Cached IsBuiltinClassProfile profile, @Cached GetPythonObjectClassNode getClassNode, - @Cached(inline = false) CExtNodes.PCallCapiFunction nativeTypeCheck) { + @Cached CStructAccess.ReadI64Node getTpFlagsNode) { Object type = getClassNode.execute(inliningTarget, obj); if (profile.profileClass(inliningTarget, type, PythonBuiltinClassType.PythonClass)) { return true; } + if (PythonNativeClass.isInstance(type)) { - return (int) nativeTypeCheck.call(FUN_SUBCLASS_CHECK, obj.getPtr()) == 1; + // Equivalent of PyType_FastSubclass(Py_TYPE(type), Py_TPFLAGS_TYPE_SUBCLASS); + long tp_flags = getTpFlagsNode.readFromObj(PythonNativeClass.cast(type), PyTypeObject__tp_flags); + return (tp_flags & TYPE_SUBCLASS) != 0; } return false; } From 8bb10ead1ee75397f8749c9f8bf7d41221bb9e1f Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Fri, 25 Jul 2025 12:38:03 +0200 Subject: [PATCH 32/38] Optimize GetDictIfExistsNode for native types * Instead of having to do this optimization in callers of GetDictIfExistsNode. --- .../python/nodes/object/GetDictIfExistsNode.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/GetDictIfExistsNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/GetDictIfExistsNode.java index 713aac4584..8350f0b75a 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/GetDictIfExistsNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/GetDictIfExistsNode.java @@ -42,6 +42,7 @@ import static com.oracle.graal.python.builtins.PythonBuiltinClassType.SystemError; import static com.oracle.graal.python.builtins.objects.cext.capi.NativeCAPISymbol.FUN_PY_OBJECT_GET_DICT_PTR; +import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyTypeObject__tp_dict; import com.oracle.graal.python.PythonLanguage; import com.oracle.graal.python.builtins.objects.PNone; @@ -53,6 +54,7 @@ import com.oracle.graal.python.builtins.objects.module.PythonModule; import com.oracle.graal.python.builtins.objects.object.PythonObject; import com.oracle.graal.python.builtins.objects.type.PythonManagedClass; +import com.oracle.graal.python.builtins.objects.type.TypeNodes.IsTypeNode; import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.HiddenAttr; import com.oracle.graal.python.nodes.PNodeWithContext; @@ -125,12 +127,25 @@ static PDict doPythonObject(PythonObject object, @InliningCutoff static PDict doNativeObject(PythonAbstractNativeObject object, @Bind Node inliningTarget, + @Cached IsTypeNode isTypeNode, + @Cached CStructAccess.ReadObjectNode getNativeDict, @CachedLibrary(limit = "1") InteropLibrary lib, @Cached PythonToNativeNode toNative, @Cached CStructAccess.ReadObjectNode readObjectNode, @Cached CStructAccess.WriteObjectNewRefNode writeObjectNode, @Cached InlinedBranchProfile createDict, @Cached CExtNodes.PCallCapiFunction callGetDictPtr) { + if (isTypeNode.execute(inliningTarget, object)) { + // Optimization for native types: read at the known offset instead of calling + // _PyObject_GetDictPtr() + Object dict = getNativeDict.readFromObj(object, PyTypeObject__tp_dict); + if (dict instanceof PDict pdict) { + return pdict; + } else { + return null; + } + } + Object dictPtr = callGetDictPtr.call(FUN_PY_OBJECT_GET_DICT_PTR, toNative.execute(object)); if (lib.isNull(dictPtr)) { return null; From 3c16c81b4c18c5412441a1bbc6468f8bd449fc7b Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Tue, 22 Jul 2025 11:31:22 +0200 Subject: [PATCH 33/38] Unify ReadAttributeFromObjectNode 2 subclasses into ReadAttributeFromObjectNode * So the correct semantics and the native type optimization is always used, reviewing usages revealed several incorrect usages. * Subclasses make host inlining unable to see through any `@Cached ReadAttributeFromObjectNode`. * Add ReadAttributeFromModuleNode for the simple case of reading from a PythonModule. * Remove extra specialization for PythonModule in ReadAttributeFromObjectNode: * The specialization below readObjectAttribute() does exactly the same optimization, so it is redundant. * Also checked by running micro/function-call-sized.py on jvm-ce-libgraal with and without --engine.Compilation=false that the performance is the same. * Add execute() overloads for ReadAttributeFromObjectNode, more efficent and also clearer which kind of input is passed. --- .../builtins/modules/BuiltinFunctions.java | 5 +- .../builtins/modules/SREModuleBuiltins.java | 4 +- .../cext/PythonCextModuleBuiltins.java | 6 +- .../cext/PythonCextStructSeqBuiltins.java | 5 +- .../modules/csv/CSVModuleBuiltins.java | 10 +- .../modules/ctypes/CtypesModuleBuiltins.java | 4 +- .../objects/PythonAbstractObject.java | 5 +- .../builtins/objects/cext/capi/CExtNodes.java | 2 +- .../MemberDescriptorBuiltins.java | 4 +- .../objects/module/ModuleBuiltins.java | 6 +- .../objects/module/ModuleGetNameNode.java | 4 +- .../builtins/objects/object/ObjectNodes.java | 2 +- .../objects/superobject/SuperBuiltins.java | 2 +- .../objects/tuple/StructSequenceBuiltins.java | 4 +- .../python/builtins/objects/type/TpSlots.java | 2 +- .../builtins/objects/type/TypeBuiltins.java | 8 +- .../builtins/objects/type/TypeNodes.java | 8 +- .../graal/python/lib/PyObjectLookupAttr.java | 2 +- .../attributes/LookupAttributeInMRONode.java | 14 +- .../ReadAttributeFromModuleNode.java | 87 ++++++++++++ .../ReadAttributeFromObjectNode.java | 126 ++++-------------- .../nodes/bytecode/SetupAnnotationsNode.java | 4 +- .../python/nodes/frame/ReadBuiltinNode.java | 8 +- .../nodes/frame/ReadGlobalOrBuiltinNode.java | 6 +- .../graal/python/runtime/PythonContext.java | 3 +- 25 files changed, 175 insertions(+), 156 deletions(-) create mode 100644 graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/ReadAttributeFromModuleNode.java diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/BuiltinFunctions.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/BuiltinFunctions.java index 18530ab9f5..adb3725352 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/BuiltinFunctions.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/BuiltinFunctions.java @@ -209,6 +209,7 @@ import com.oracle.graal.python.nodes.StringLiterals; import com.oracle.graal.python.nodes.argument.ReadArgumentNode; import com.oracle.graal.python.nodes.attributes.GetFixedAttributeNode; +import com.oracle.graal.python.nodes.attributes.ReadAttributeFromModuleNode; import com.oracle.graal.python.nodes.attributes.ReadAttributeFromObjectNode; import com.oracle.graal.python.nodes.builtins.ListNodes; import com.oracle.graal.python.nodes.builtins.ListNodes.ConstructListNode; @@ -1730,7 +1731,7 @@ static Object ord(@SuppressWarnings("unused") Object obj, "flush: whether to forcibly flush the stream.") @GenerateNodeFactory public abstract static class PrintNode extends PythonBuiltinNode { - @Child private ReadAttributeFromObjectNode readStdout; + @Child private ReadAttributeFromModuleNode readStdout; @CompilationFinal private PythonModule cachedSys; @Specialization @@ -1836,7 +1837,7 @@ private Object getStdout() { } if (readStdout == null) { CompilerDirectives.transferToInterpreterAndInvalidate(); - readStdout = insert(ReadAttributeFromObjectNode.create()); + readStdout = insert(ReadAttributeFromModuleNode.create()); } Object stdout = readStdout.execute(sys, T_STDOUT); if (stdout == NO_VALUE) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SREModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SREModuleBuiltins.java index 38851dff8b..2d0ff3db5a 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SREModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SREModuleBuiltins.java @@ -49,6 +49,7 @@ import java.util.List; import java.util.Objects; +import com.oracle.graal.python.nodes.attributes.ReadAttributeFromModuleNode; import org.graalvm.collections.EconomicMap; import com.oracle.graal.python.builtins.Builtin; @@ -80,7 +81,6 @@ import com.oracle.graal.python.nodes.PNodeWithContext; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.attributes.GetFixedAttributeNode; -import com.oracle.graal.python.nodes.attributes.ReadAttributeFromObjectNode; import com.oracle.graal.python.nodes.call.CallNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.builtins.PythonSenaryBuiltinNode; @@ -622,7 +622,7 @@ static Object doSingleContext( @Specialization(replaces = "doSingleContext") static Object doRead( @Bind PythonContext context, - @Cached ReadAttributeFromObjectNode read) { + @Cached ReadAttributeFromModuleNode read) { PythonModule module = context.lookupBuiltinModule(BuiltinNames.T__SRE); return read.execute(module, T_MATCH_CONSTRUCTOR); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextModuleBuiltins.java index 921283f9bb..57fca05976 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextModuleBuiltins.java @@ -85,7 +85,7 @@ import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.StringLiterals; -import com.oracle.graal.python.nodes.attributes.ReadAttributeFromObjectNode; +import com.oracle.graal.python.nodes.attributes.ReadAttributeFromModuleNode; import com.oracle.graal.python.nodes.attributes.ReadAttributeFromPythonObjectNode; import com.oracle.graal.python.nodes.attributes.WriteAttributeToObjectNode; import com.oracle.graal.python.nodes.call.CallNode; @@ -172,7 +172,7 @@ static Object getName(PythonModule module, @Cached PythonCextBuiltins.PromoteBorrowedValue promoteBorrowedValue, @Cached PyUnicodeCheckNode pyUnicodeCheckNode, // CPython reads from the module dict directly - @Cached ReadAttributeFromObjectNode read, + @Cached ReadAttributeFromModuleNode read, @Cached WriteAttributeToObjectNode write) { /* * Even thought the function returns a new reference, CPython assumes that the unicode @@ -308,7 +308,7 @@ abstract static class PyModule_GetFilenameObject extends CApiUnaryBuiltinNode { @Specialization static Object getFilename(PythonModule module, @Bind Node inliningTarget, - @Cached ReadAttributeFromObjectNode read, + @Cached ReadAttributeFromModuleNode read, @Cached PyUnicodeCheckNode check, @Cached PRaiseNode raiseNode) { Object file = read.execute(module, T___FILE__); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextStructSeqBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextStructSeqBuiltins.java index 31cffab05a..c16287b838 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextStructSeqBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextStructSeqBuiltins.java @@ -74,6 +74,7 @@ import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.SpecialAttributeNames; +import com.oracle.graal.python.nodes.attributes.ReadAttributeFromModuleNode; import com.oracle.graal.python.nodes.attributes.ReadAttributeFromObjectNode; import com.oracle.graal.python.nodes.call.CallNode; import com.oracle.graal.python.nodes.util.CannotCastException; @@ -137,7 +138,7 @@ abstract static class GraalPyPrivate_StructSequence_NewType extends CApiQuaterna @TruffleBoundary Object doGeneric(TruffleString typeName, TruffleString typeDoc, Object fields, int nInSequence, @Cached GraalPyPrivate_StructSequence_InitType2 initNode, - @Cached ReadAttributeFromObjectNode readTypeBuiltinNode, + @Cached ReadAttributeFromModuleNode readTypeBuiltinNode, @CachedLibrary(limit = "1") DynamicObjectLibrary dylib, @Cached CallNode callTypeNewNode, @Bind PythonLanguage language) { @@ -159,7 +160,7 @@ abstract static class PyStructSequence_New extends CApiUnaryBuiltinNode { @Specialization static Object doGeneric(Object cls, @Bind Node inliningTarget, - @Cached("createForceType()") ReadAttributeFromObjectNode readRealSizeNode, + @Cached ReadAttributeFromObjectNode readRealSizeNode, @Cached CastToJavaIntExactNode castToIntNode, @Bind PythonLanguage language, @Cached TypeNodes.GetInstanceShape getInstanceShape, diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/csv/CSVModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/csv/CSVModuleBuiltins.java index d3fd753b22..1523a421e8 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/csv/CSVModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/csv/CSVModuleBuiltins.java @@ -74,7 +74,7 @@ import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.SpecialAttributeNames; -import com.oracle.graal.python.nodes.attributes.ReadAttributeFromObjectNode; +import com.oracle.graal.python.nodes.attributes.ReadAttributeFromModuleNode; import com.oracle.graal.python.nodes.builtins.ListNodes; import com.oracle.graal.python.nodes.call.CallNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; @@ -134,7 +134,7 @@ public abstract static class CSVRegisterDialectNode extends PythonBuiltinNode { static PNone register(VirtualFrame frame, PythonModule module, Object nameObj, Object dialectObj, PKeyword[] keywords, @Bind Node inliningTarget, @Cached CastToTruffleStringNode nameNode, - @Cached ReadAttributeFromObjectNode readNode, + @Cached ReadAttributeFromModuleNode readNode, @Cached CallNode callNode, @Cached PyDictSetItem setItem, @Cached PRaiseNode raiseNode) { @@ -164,7 +164,7 @@ public abstract static class CSVUnregisterDialectNode extends PythonBuiltinNode @Specialization static PNone unregister(VirtualFrame frame, PythonModule module, Object nameObj, @Bind Node inliningTarget, - @Cached ReadAttributeFromObjectNode readNode, + @Cached ReadAttributeFromModuleNode readNode, @Cached PyDictDelItem delItem, @Cached HashingStorageGetItem getItem, @Cached PRaiseNode raiseNode) { @@ -198,7 +198,7 @@ protected static CSVGetDialectNode create() { static CSVDialect get(VirtualFrame frame, PythonModule module, Object nameObj, @Bind Node inliningTarget, @Cached PyDictGetItem getItemNode, - @Cached ReadAttributeFromObjectNode readNode, + @Cached ReadAttributeFromModuleNode readNode, @Cached PRaiseNode raiseNode) { // TODO GR-38165: unchecked cast to PDict @@ -220,7 +220,7 @@ static CSVDialect get(VirtualFrame frame, PythonModule module, Object nameObj, public abstract static class CSVListDialectsNode extends PythonBuiltinNode { @Specialization PList listDialects(VirtualFrame frame, PythonModule module, - @Cached ReadAttributeFromObjectNode readNode, + @Cached ReadAttributeFromModuleNode readNode, @Cached ListNodes.ConstructListNode constructListNode) { Object dialects = readNode.execute(module, T__DIALECTS); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ctypes/CtypesModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ctypes/CtypesModuleBuiltins.java index bc2a971c28..690d295e1c 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ctypes/CtypesModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ctypes/CtypesModuleBuiltins.java @@ -145,7 +145,7 @@ import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.StringLiterals; import com.oracle.graal.python.nodes.attributes.GetFixedAttributeNode; -import com.oracle.graal.python.nodes.attributes.ReadAttributeFromObjectNode; +import com.oracle.graal.python.nodes.attributes.ReadAttributeFromModuleNode; import com.oracle.graal.python.nodes.attributes.WriteAttributeToPythonObjectNode; import com.oracle.graal.python.nodes.call.CallNode; import com.oracle.graal.python.nodes.call.special.LookupAndCallUnaryNode; @@ -269,7 +269,7 @@ public void postInitialize(Python3Core core) { handle = DlOpenNode.loadNFILibrary(context, NFIBackend.NATIVE, J_DEFAULT_LIBRARY, rtldLocal); if (PythonOS.getPythonOS() == PythonOS.PLATFORM_WIN32) { PythonModule sysModule = context.getSysModule(); - Object loadLibraryMethod = ReadAttributeFromObjectNode.getUncached().execute(ctypesModule, toTruffleStringUncached("LoadLibrary")); + Object loadLibraryMethod = ReadAttributeFromModuleNode.getUncached().execute(ctypesModule, toTruffleStringUncached("LoadLibrary")); Object pythonLib = CallNode.executeUncached(loadLibraryMethod, toTruffleStringUncached(PythonContext.getSupportLibName("python-native")), 0); WriteAttributeToPythonObjectNode.getUncached().execute(sysModule, toTruffleStringUncached("dllhandle"), pythonLib); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/PythonAbstractObject.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/PythonAbstractObject.java index af1b65f61a..ca0e8b025b 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/PythonAbstractObject.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/PythonAbstractObject.java @@ -126,6 +126,7 @@ import com.oracle.graal.python.nodes.argument.keywords.NonMappingException; import com.oracle.graal.python.nodes.argument.keywords.SameDictKeyException; import com.oracle.graal.python.nodes.attributes.LookupInheritedAttributeNode; +import com.oracle.graal.python.nodes.attributes.ReadAttributeFromModuleNode; import com.oracle.graal.python.nodes.attributes.ReadAttributeFromObjectNode; import com.oracle.graal.python.nodes.call.CallNode; import com.oracle.graal.python.nodes.call.special.CallBinaryMethodNode; @@ -1165,7 +1166,7 @@ public abstract static class PKeyInfoNode extends Node { @Specialization static boolean access(Object object, TruffleString attrKeyName, int type, @Bind Node inliningTarget, - @Cached("createForceType()") ReadAttributeFromObjectNode readTypeAttrNode, + @Cached ReadAttributeFromObjectNode readTypeAttrNode, @Cached ReadAttributeFromObjectNode readObjectAttrNode, @Cached PyCallableCheckNode callableCheck, @Cached LookupInheritedAttributeNode.Dynamic getGetNode, @@ -1477,7 +1478,7 @@ public abstract static class ToDisplaySideEffectingNode extends Node { @Specialization public static TruffleString doDefault(Node inliningTarget, PythonAbstractObject receiver, - @Cached(inline = false) ReadAttributeFromObjectNode readStr, + @Cached ReadAttributeFromModuleNode readStr, @Cached(inline = false) CallNode callNode, @Cached CastToTruffleStringNode castStr, @Cached InlinedConditionProfile toStringUsed) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CExtNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CExtNodes.java index 7f932115fa..b6afb03253 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CExtNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/cext/capi/CExtNodes.java @@ -943,7 +943,7 @@ public static long lookupNativeI64MemberInMRO(Object cls, CFields nativeMemberNa if (managedMemberName instanceof HiddenAttr ha) { attr = HiddenAttr.ReadNode.executeUncached((PythonAbstractObject) mroCls, ha, NO_VALUE); } else { - attr = ReadAttributeFromObjectNode.getUncachedForceType().execute(mroCls, CompilerDirectives.castExact(managedMemberName, TruffleString.class)); + attr = ReadAttributeFromObjectNode.getUncached().execute(mroCls, CompilerDirectives.castExact(managedMemberName, TruffleString.class)); } if (attr != NO_VALUE) { return PyNumberAsSizeNode.executeExactUncached(attr); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/getsetdescriptor/MemberDescriptorBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/getsetdescriptor/MemberDescriptorBuiltins.java index 3555d429eb..7cb6408b4f 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/getsetdescriptor/MemberDescriptorBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/getsetdescriptor/MemberDescriptorBuiltins.java @@ -63,7 +63,7 @@ import com.oracle.graal.python.builtins.objects.type.slots.TpSlotDescrGet.DescrGetBuiltinNode; import com.oracle.graal.python.builtins.objects.type.slots.TpSlotDescrSet.DescrSetBuiltinNode; import com.oracle.graal.python.nodes.BuiltinNames; -import com.oracle.graal.python.nodes.attributes.ReadAttributeFromObjectNode; +import com.oracle.graal.python.nodes.attributes.ReadAttributeFromModuleNode; import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode; import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode; import com.oracle.graal.python.runtime.object.PFactory; @@ -107,7 +107,7 @@ static TruffleString repr(GetSetDescriptor descr, abstract static class MemberDescriptorReduceNode extends PythonUnaryBuiltinNode { @Specialization Object doGeneric(GetSetDescriptor descr, - @Cached ReadAttributeFromObjectNode readAttributeFromObjectNode, + @Cached ReadAttributeFromModuleNode readAttributeFromObjectNode, @Cached GetIdNode getIdNode, @Bind PythonLanguage language) { Object getattr = readAttributeFromObjectNode.execute(getContext().getBuiltins(), BuiltinNames.T_GETATTR); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/ModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/ModuleBuiltins.java index d1dbb98d2e..7321fcff9e 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/ModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/ModuleBuiltins.java @@ -86,6 +86,7 @@ import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PNodeWithContext; import com.oracle.graal.python.nodes.PRaiseNode; +import com.oracle.graal.python.nodes.attributes.ReadAttributeFromModuleNode; import com.oracle.graal.python.nodes.attributes.ReadAttributeFromObjectNode; import com.oracle.graal.python.nodes.attributes.WriteAttributeToObjectNode; import com.oracle.graal.python.nodes.builtins.ListNodes; @@ -290,7 +291,8 @@ public final Object execute(VirtualFrame frame, PythonModule self, TruffleString static Object getattribute(VirtualFrame frame, PythonModule self, TruffleString key, PException e, @Bind Node inliningTarget, @Cached IsBuiltinObjectProfile isAttrError, - @Cached ReadAttributeFromObjectNode readGetattr, + @Cached ReadAttributeFromModuleNode readGetattr, + @Cached ReadAttributeFromObjectNode readInitializing, @Cached InlinedConditionProfile customGetAttr, @Cached CallNode callNode, @Cached PyObjectIsTrueNode castToBooleanNode, @@ -311,7 +313,7 @@ static Object getattribute(VirtualFrame frame, PythonModule self, TruffleString if (moduleName != null) { Object moduleSpec = readGetattr.execute(self, T___SPEC__); if (moduleSpec != PNone.NO_VALUE) { - Object isInitializing = readGetattr.execute(moduleSpec, T__INITIALIZING); + Object isInitializing = readInitializing.execute(moduleSpec, T__INITIALIZING); if (isInitializing != PNone.NO_VALUE && castToBooleanNode.execute(frame, isInitializing)) { throw raiseNode.raise(inliningTarget, AttributeError, ErrorMessages.MODULE_PARTIALLY_INITIALIZED_S_HAS_NO_ATTR_S, moduleName, key); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/ModuleGetNameNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/ModuleGetNameNode.java index 88118ab53b..ec50558e10 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/ModuleGetNameNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/ModuleGetNameNode.java @@ -44,7 +44,7 @@ import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PRaiseNode; import com.oracle.graal.python.nodes.SpecialAttributeNames; -import com.oracle.graal.python.nodes.attributes.ReadAttributeFromObjectNode; +import com.oracle.graal.python.nodes.attributes.ReadAttributeFromModuleNode; import com.oracle.graal.python.nodes.util.CannotCastException; import com.oracle.graal.python.nodes.util.CastToTruffleStringNode; import com.oracle.truffle.api.dsl.Cached; @@ -67,7 +67,7 @@ public abstract class ModuleGetNameNode extends Node { @Specialization static TruffleString doPythonModule(Node inliningTarget, PythonModule module, - @Cached(inline = false) ReadAttributeFromObjectNode readNameNode, + @Cached ReadAttributeFromModuleNode readNameNode, @Cached CastToTruffleStringNode castToTruffleStringNode, @Cached PRaiseNode raiseNode) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/ObjectNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/ObjectNodes.java index 3fc39fc4d3..a8881ac04e 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/ObjectNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/ObjectNodes.java @@ -516,7 +516,7 @@ public abstract static class GetSlotNamesNode extends Node { @Specialization static Object[] getstate(VirtualFrame frame, Node inliningTarget, Object type, - @Cached(value = "createForceType()", inline = false) ReadAttributeFromObjectNode read, + @Cached ReadAttributeFromObjectNode read, @Cached SequenceStorageNodes.ToArrayNode toArrayNode, @Cached PyImportImport importNode, @Cached PyObjectCallMethodObjArgs callMethod, diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/superobject/SuperBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/superobject/SuperBuiltins.java index 97e56e08f4..f4b5dd04b6 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/superobject/SuperBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/superobject/SuperBuiltins.java @@ -472,7 +472,7 @@ static Object doIt(Node inliningTarget, SuperObject self, Object obj, @Slot(value = SlotKind.tp_getattro, isComplex = true) @GenerateNodeFactory public abstract static class GetattributeNode extends GetAttrBuiltinNode { - @Child private ReadAttributeFromObjectNode readFromDict = ReadAttributeFromObjectNode.createForceType(); + @Child private ReadAttributeFromObjectNode readFromDict = ReadAttributeFromObjectNode.create(); @Child private CallSlotDescrGet callGetSlotNode; @Child private GetTypeNode getType; @Child private GetObjectNode getObject = GetObjectNodeGen.create(); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/tuple/StructSequenceBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/tuple/StructSequenceBuiltins.java index 77ef8b3f0b..877d71d52b 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/tuple/StructSequenceBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/tuple/StructSequenceBuiltins.java @@ -133,14 +133,14 @@ abstract static class GetSizeNode extends Node { @Specialization static int doPBCT(VirtualFrame frame, Node inliningTarget, PythonBuiltinClassType type, TruffleString key, - @Shared @Cached("createForceType()") ReadAttributeFromObjectNode read, + @Shared @Cached ReadAttributeFromObjectNode read, @Shared @Cached PyNumberAsSizeNode asSizeNode) { return doGeneric(frame, inliningTarget, PythonContext.get(inliningTarget).lookupType(type), key, read, asSizeNode); } @Fallback static int doGeneric(VirtualFrame frame, Node inliningTarget, Object type, TruffleString key, - @Shared @Cached("createForceType()") ReadAttributeFromObjectNode read, + @Shared @Cached ReadAttributeFromObjectNode read, @Shared @Cached PyNumberAsSizeNode asSizeNode) { return asSizeNode.executeExact(frame, inliningTarget, read.execute(type, key)); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TpSlots.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TpSlots.java index 5d1334dd19..76694f0f76 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TpSlots.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TpSlots.java @@ -1736,7 +1736,7 @@ private static boolean checkNoMagicOverrides(Python3Core core, PythonBuiltinClas // Check that no one is trying to define magic methods directly // If the assertion fires: you should define @Slot instead of @Builtin // We do not look in MRO, we may have already called addOperatorsToBuiltin on super - var readAttr = ReadAttributeFromObjectNode.getUncachedForceType(); + var readAttr = ReadAttributeFromObjectNode.getUncached(); PythonBuiltinClass typeObj = core.lookupType(type); for (TruffleString name : SPECIAL2SLOT.keySet()) { assert readAttr.execute(typeObj, name) == PNone.NO_VALUE : type.name() + ":" + name; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeBuiltins.java index 861ed02178..d280d524b5 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeBuiltins.java @@ -259,7 +259,7 @@ static Object getDoc(VirtualFrame frame, PythonClass self, @SuppressWarnings("un @Specialization static Object getDoc(PythonAbstractNativeObject self, @SuppressWarnings("unused") PNone value) { - return ReadAttributeFromObjectNode.getUncachedForceType().execute(self, T___DOC__); + return ReadAttributeFromObjectNode.getUncached().execute(self, T___DOC__); } @Specialization(guards = {"!isNoValue(value)", "!isDeleteMarker(value)", "!isPythonBuiltinClass(self)"}) @@ -942,7 +942,7 @@ static TruffleString getModuleBuiltin(PythonBuiltinClass cls, @SuppressWarnings( @Specialization(guards = "isNoValue(value)") static Object getModule(PythonClass cls, @SuppressWarnings("unused") PNone value, @Bind Node inliningTarget, - @Cached ReadAttributeFromObjectNode readAttrNode, + @Shared @Cached ReadAttributeFromObjectNode readAttrNode, @Shared @Cached PRaiseNode raiseNode) { Object module = readAttrNode.execute(cls, T___MODULE__); if (module == NO_VALUE) { @@ -961,7 +961,7 @@ static Object setModule(PythonClass cls, Object value, @Specialization(guards = "isNoValue(value)") static Object getModule(PythonAbstractNativeObject cls, @SuppressWarnings("unused") PNone value, @Bind Node inliningTarget, - @Cached("createForceType()") ReadAttributeFromObjectNode readAttr, + @Shared @Cached ReadAttributeFromObjectNode readAttrNode, @Shared @Cached GetTypeFlagsNode getFlags, @Cached CStructAccess.ReadCharPtrNode getTpNameNode, @Cached TruffleString.CodePointLengthNode codePointLengthNode, @@ -970,7 +970,7 @@ static Object getModule(PythonAbstractNativeObject cls, @SuppressWarnings("unuse @Shared @Cached PRaiseNode raiseNode) { // see function 'typeobject.c: type_module' if ((getFlags.execute(cls) & TypeFlags.HEAPTYPE) != 0) { - Object module = readAttr.execute(cls, T___MODULE__); + Object module = readAttrNode.execute(cls, T___MODULE__); if (module == NO_VALUE) { throw raiseNode.raise(inliningTarget, AttributeError); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeNodes.java index b528b23d75..145070c42d 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeNodes.java @@ -1239,7 +1239,7 @@ private boolean instancesHaveDict(Object type) { private ReadAttributeFromObjectNode getReadAttr() { if (readAttr == null) { CompilerDirectives.transferToInterpreterAndInvalidate(); - readAttr = insert(ReadAttributeFromObjectNode.createForceType()); + readAttr = insert(ReadAttributeFromObjectNode.create()); } return readAttr; } @@ -1321,7 +1321,7 @@ public static boolean executeUncached(Object type) { @Specialization static boolean check(Node inliningTarget, Object type, @Cached NeedsNativeAllocationNode needsNativeAllocationNode, - @Cached(inline = false) ReadAttributeFromObjectNode read, + @Cached ReadAttributeFromObjectNode read, @Cached GetWeakListOffsetNode getWeakListOffsetNode) { if (needsNativeAllocationNode.execute(inliningTarget, type)) { return getWeakListOffsetNode.execute(inliningTarget, type) != 0; @@ -1376,7 +1376,7 @@ static Object executeUncached(Object type) { @Specialization protected static Object getSolid(Node inliningTarget, Object type, @Cached GetBaseClassNode getBaseClassNode, - @Cached(value = "createForceType()", inline = false) ReadAttributeFromObjectNode readAttr, + @Cached ReadAttributeFromObjectNode readAttr, @Cached InlinedBranchProfile typeIsNotBase, @Cached InlinedBranchProfile hasBase, @Cached InlinedBranchProfile hasNoBase) { @@ -1386,7 +1386,7 @@ protected static Object getSolid(Node inliningTarget, Object type, @TruffleBoundary protected static Object solidBaseTB(Object type, Node inliningTarget, GetBaseClassNode getBaseClassNode, PythonContext context, int depth) { - return solidBase(type, inliningTarget, getBaseClassNode, context, ReadAttributeFromObjectNode.getUncachedForceType(), InlinedBranchProfile.getUncached(), + return solidBase(type, inliningTarget, getBaseClassNode, context, ReadAttributeFromObjectNode.getUncached(), InlinedBranchProfile.getUncached(), InlinedBranchProfile.getUncached(), InlinedBranchProfile.getUncached(), depth); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectLookupAttr.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectLookupAttr.java index 17fd1d9e43..5cde8dbecb 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectLookupAttr.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectLookupAttr.java @@ -146,7 +146,7 @@ static Object doBuiltinObject(VirtualFrame frame, Node inliningTarget, Object ob @Bind("getClass.execute(inliningTarget, object)") Object type, @Cached("create(name)") LookupAttributeInMRONode lookupName, @Bind("lookupName.execute(type)") Object descr, - @Shared @Cached(inline = false) ReadAttributeFromObjectNode readNode) { + @Shared @Cached ReadAttributeFromObjectNode readNode) { // It should not have __getattr__, because otherwise it would not have builtin // object#tp_getattro, but slot wrapper dispatching to __getattribute__ or __getattr__ assert hasNoGetAttr(type); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/LookupAttributeInMRONode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/LookupAttributeInMRONode.java index 4bf16606b7..62e11326e7 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/LookupAttributeInMRONode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/LookupAttributeInMRONode.java @@ -105,7 +105,7 @@ static Object lookupGeneric(Object klass, TruffleString key, @Cached InlinedConditionProfile pbctProfile, @Cached ReadAttributeFromPythonObjectNode readPBCTAttrNode, @Cached GetMroStorageNode getMroNode, - @Cached(value = "createForceType()", uncached = "getUncachedForceType()") ReadAttributeFromObjectNode readAttrNode) { + @Cached ReadAttributeFromObjectNode readAttrNode) { if (pbctProfile.profile(inliningTarget, klass instanceof PythonBuiltinClassType)) { return findAttr(PythonContext.get(inliningTarget), (PythonBuiltinClassType) klass, key, readPBCTAttrNode); } else { @@ -212,7 +212,7 @@ AttributeAssumptionPair findAttrAndAssumptionInMRO(Object klass) { if (skipNonStaticBase(clsObj, skipNonStaticBases)) { continue; } - Object value = ReadAttributeFromObjectNode.getUncachedForceType().execute(clsObj, key); + Object value = ReadAttributeFromObjectNode.getUncached().execute(clsObj, key); if (value != PNone.NO_VALUE) { result = value; break; @@ -306,7 +306,7 @@ Object lookupConstantMROShape(PythonClass klass, TruffleString key, boolean skip static ReadAttributeFromObjectNode[] create(int size) { ReadAttributeFromObjectNode[] nodes = new ReadAttributeFromObjectNode[size]; for (int i = 0; i < size; i++) { - nodes[i] = ReadAttributeFromObjectNode.createForceType(); + nodes[i] = ReadAttributeFromObjectNode.create(); } return nodes; } @@ -365,7 +365,7 @@ Object lookupCachedLen(Object klass, TruffleString key, boolean skipNonStaticBas @Megamorphic @InliningCutoff Object lookupGeneric(Object klass, TruffleString key, boolean skipNonStaticBases, - @Cached("createForceType()") ReadAttributeFromObjectNode readAttrNode) { + @Cached ReadAttributeFromObjectNode readAttrNode) { return lookup(key, getMro(klass), readAttrNode, skipNonStaticBases); } @@ -394,16 +394,16 @@ static MroSequenceStorage getMroUncached(Object clazz) { @TruffleBoundary public static Object lookupSlowPath(Object klass, TruffleString key) { - return lookup(key, GetMroStorageNode.executeUncached(klass), ReadAttributeFromObjectNode.getUncachedForceType(), false); + return lookup(key, GetMroStorageNode.executeUncached(klass), ReadAttributeFromObjectNode.getUncached(), false); } - public static Object lookup(TruffleString key, MroSequenceStorage mro, ReadAttributeFromObjectNode readAttrNode, boolean skipNonStaticBases) { + public static Object lookup(TruffleString key, MroSequenceStorage mro, ReadAttributeFromObjectNode readTypeAttrNode, boolean skipNonStaticBases) { for (int i = 0; i < mro.length(); i++) { Object kls = mro.getPythonClassItemNormalized(i); if (skipNonStaticBase(kls, skipNonStaticBases)) { continue; } - Object value = readAttrNode.execute(kls, key); + Object value = readTypeAttrNode.execute(kls, key); if (value != PNone.NO_VALUE) { return value; } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/ReadAttributeFromModuleNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/ReadAttributeFromModuleNode.java new file mode 100644 index 0000000000..bffff3949e --- /dev/null +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/ReadAttributeFromModuleNode.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2017, 2025, 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 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.oracle.graal.python.nodes.attributes; + +import com.oracle.graal.python.builtins.objects.PNone; +import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageGetItem; +import com.oracle.graal.python.builtins.objects.module.PythonModule; +import com.oracle.graal.python.nodes.PNodeWithContext; +import com.oracle.graal.python.nodes.object.GetDictIfExistsNode; +import com.oracle.truffle.api.dsl.Bind; +import com.oracle.truffle.api.dsl.Cached; +import com.oracle.truffle.api.dsl.GenerateInline; +import com.oracle.truffle.api.dsl.GenerateUncached; +import com.oracle.truffle.api.dsl.NeverDefault; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.strings.TruffleString; + +@GenerateUncached +@GenerateInline(false) +public abstract class ReadAttributeFromModuleNode extends PNodeWithContext { + + @NeverDefault + public static ReadAttributeFromModuleNode create() { + return ReadAttributeFromModuleNodeGen.create(); + } + + public static ReadAttributeFromModuleNode getUncached() { + return ReadAttributeFromModuleNodeGen.getUncached(); + } + + public abstract Object execute(PythonModule object, TruffleString key); + + // PythonModule always have a dict + @Specialization + static Object readModuleAttribute(PythonModule object, TruffleString key, + @Bind Node inliningTarget, + @Cached GetDictIfExistsNode getDict, + @Cached HashingStorageGetItem getItem) { + var dict = getDict.execute(object); + Object value = getItem.execute(inliningTarget, dict.getDictStorage(), key); + if (value == null) { + return PNone.NO_VALUE; + } else { + return value; + } + } + +} diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/ReadAttributeFromObjectNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/ReadAttributeFromObjectNode.java index 46956accc0..b50a750fb2 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/ReadAttributeFromObjectNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/ReadAttributeFromObjectNode.java @@ -40,99 +40,50 @@ */ package com.oracle.graal.python.nodes.attributes; -import static com.oracle.graal.python.builtins.objects.cext.structs.CFields.PyTypeObject__tp_dict; - 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.structs.CStructAccess; -import com.oracle.graal.python.builtins.objects.common.HashingStorage; import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageGetItem; -import com.oracle.graal.python.builtins.objects.common.PHashingCollection; import com.oracle.graal.python.builtins.objects.dict.PDict; -import com.oracle.graal.python.builtins.objects.module.PythonModule; import com.oracle.graal.python.builtins.objects.object.PythonObject; -import com.oracle.graal.python.nodes.PGuards; import com.oracle.graal.python.nodes.PNodeWithContext; -import com.oracle.graal.python.nodes.attributes.ReadAttributeFromObjectNodeGen.ReadAttributeFromObjectNotTypeNodeGen; -import com.oracle.graal.python.nodes.attributes.ReadAttributeFromObjectNodeGen.ReadAttributeFromObjectTpDictNodeGen; import com.oracle.graal.python.nodes.object.GetDictIfExistsNode; -import com.oracle.graal.python.runtime.PythonOptions; 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.Cached.Exclusive; 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.NeverDefault; -import com.oracle.truffle.api.dsl.NonIdempotent; import com.oracle.truffle.api.dsl.ReportPolymorphism; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.profiles.InlinedConditionProfile; import com.oracle.truffle.api.strings.TruffleString; -@ImportStatic({PGuards.class, PythonOptions.class}) +/** + * See {@link ReadAttributeFromModuleNode} which is much simpler for modules. + */ @ReportPolymorphism +@GenerateUncached @GenerateInline(false) // footprint reduction 64 -> 47 public abstract class ReadAttributeFromObjectNode extends PNodeWithContext { - @NeverDefault - public static ReadAttributeFromObjectNode create() { - return ReadAttributeFromObjectNotTypeNodeGen.create(); - } @NeverDefault - public static ReadAttributeFromObjectNode createForceType() { - return ReadAttributeFromObjectTpDictNodeGen.create(); + public static ReadAttributeFromObjectNode create() { + return ReadAttributeFromObjectNodeGen.create(); } public static ReadAttributeFromObjectNode getUncached() { - return ReadAttributeFromObjectNotTypeNodeGen.getUncached(); - } - - public static ReadAttributeFromObjectNode getUncachedForceType() { - return ReadAttributeFromObjectTpDictNodeGen.getUncached(); + return ReadAttributeFromObjectNodeGen.getUncached(); } public abstract Object execute(Object object, TruffleString key); - public abstract Object execute(PythonModule object, TruffleString key); - - @NonIdempotent - protected static HashingStorage getStorage(PHashingCollection cachedGlobals) { - return cachedGlobals.getDictStorage(); - } - - protected static PDict getDict(Object object) { - return GetDictIfExistsNode.getUncached().execute(object); - } - - // special case for the very common module read - @Specialization(guards = {"isSingleContext()", - "cachedObject == object", - // no need to check "getDict(object) == cachedDict", module.__dict__ is - // read-only - "getStorage(cachedDict) == cachedStorage" - }, limit = "1") - @SuppressWarnings("unused") - protected static Object readFromBuiltinModuleDict(PythonModule object, TruffleString key, - @Bind Node inliningTarget, - @Cached(value = "object", weak = true) PythonModule cachedObject, - @Cached(value = "getDict(object)", weak = true) PHashingCollection cachedDict, - @Cached(value = "getStorage(getDict(object))", weak = true) HashingStorage cachedStorage, - @Exclusive @Cached HashingStorageGetItem getItem) { - // note that we don't need to pass the state here - string keys are hashable by definition - Object value = getItem.execute(inliningTarget, cachedStorage, key); - if (value == null) { - return PNone.NO_VALUE; - } else { - return value; - } - } + public abstract Object execute(PythonAbstractNativeObject object, TruffleString key); // any python object attribute read @Specialization - protected static Object readObjectAttribute(PythonObject object, TruffleString key, + static Object readObjectAttribute(PythonObject object, TruffleString key, @Bind Node inliningTarget, @Cached InlinedConditionProfile profileHasDict, @Exclusive @Cached GetDictIfExistsNode getDict, @@ -142,9 +93,7 @@ protected static Object readObjectAttribute(PythonObject object, TruffleString k if (profileHasDict.profile(inliningTarget, dict == null)) { return readAttributeFromPythonObjectNode.execute(object, key); } else { - // Note: we should pass the frame. In theory a subclass of a string may override - // __hash__ or __eq__ and run some side effects in there. - Object value = getItem.execute(null, inliningTarget, dict.getDictStorage(), key); + Object value = getItem.execute(inliningTarget, dict.getDictStorage(), key); if (value == null) { return PNone.NO_VALUE; } else { @@ -153,52 +102,29 @@ protected static Object readObjectAttribute(PythonObject object, TruffleString k } } + @Specialization + static Object readNativeObject(PythonAbstractNativeObject object, TruffleString key, + @Bind Node inliningTarget, + @Exclusive @Cached GetDictIfExistsNode getDict, + @Exclusive @Cached HashingStorageGetItem getItem) { + PDict dict = getDict.execute(object); + if (dict != null) { + Object result = getItem.execute(null, inliningTarget, dict.getDictStorage(), key); + if (result != null) { + return result; + } + } + return PNone.NO_VALUE; + } + // foreign object or primitive @InliningCutoff @Specialization(guards = {"!isPythonObject(object)", "!isNativeObject(object)"}) - protected static Object readForeignOrPrimitive(Object object, TruffleString key) { + static Object readForeignOrPrimitive(Object object, TruffleString key) { // Foreign members are tried after the regular attribute lookup, see // ForeignObjectBuiltins.GetAttributeNode. If we looked them up here // they would get precedence over attributes in the MRO. return PNone.NO_VALUE; } - // native objects. We distinguish reading at the objects dictoffset or the tp_dict - // these are also the two nodes that generate uncached versions, because they encode - // the boolean flag forceType for the fallback in their type - - @GenerateUncached - @GenerateInline(false) // footprint reduction 64 -> 47 - protected abstract static class ReadAttributeFromObjectNotTypeNode extends ReadAttributeFromObjectNode { - @Specialization(insertBefore = "readForeignOrPrimitive") - protected static Object readNativeObject(PythonAbstractNativeObject object, TruffleString key, - @Bind Node inliningTarget, - @Exclusive @Cached GetDictIfExistsNode getDict, - @Exclusive @Cached HashingStorageGetItem getItem) { - return readNative(inliningTarget, key, getDict.execute(object), getItem); - } - } - - @GenerateUncached - @GenerateInline(false) // footprint reduction 68 -> 51 - protected abstract static class ReadAttributeFromObjectTpDictNode extends ReadAttributeFromObjectNode { - @Specialization(insertBefore = "readForeignOrPrimitive") - protected static Object readNativeClass(PythonAbstractNativeObject object, TruffleString key, - @Bind Node inliningTarget, - @Cached CStructAccess.ReadObjectNode getNativeDict, - @Exclusive @Cached HashingStorageGetItem getItem) { - return readNative(inliningTarget, key, getNativeDict.readFromObj(object, PyTypeObject__tp_dict), getItem); - } - } - - private static Object readNative(Node inliningTarget, TruffleString key, Object dict, HashingStorageGetItem getItem) { - if (dict instanceof PHashingCollection) { - Object result = getItem.execute(null, inliningTarget, ((PHashingCollection) dict).getDictStorage(), key); - if (result != null) { - return result; - } - } - return PNone.NO_VALUE; - } - } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/SetupAnnotationsNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/SetupAnnotationsNode.java index 9c51eed2d9..c802e3c7c8 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/SetupAnnotationsNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode/SetupAnnotationsNode.java @@ -54,7 +54,7 @@ import com.oracle.graal.python.lib.PyObjectGetItem; import com.oracle.graal.python.lib.PyObjectSetItem; import com.oracle.graal.python.nodes.PNodeWithContext; -import com.oracle.graal.python.nodes.attributes.ReadAttributeFromObjectNode; +import com.oracle.graal.python.nodes.attributes.ReadAttributeFromModuleNode; import com.oracle.graal.python.nodes.attributes.WriteAttributeToObjectNode; import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinObjectProfile; import com.oracle.graal.python.runtime.exception.PException; @@ -103,7 +103,7 @@ public abstract static class SetupAnnotationsFromDictOrModuleNode extends PNodeW @Specialization static void doModule(Node inliningTarget, PythonModule locals, - @Cached(inline = false) ReadAttributeFromObjectNode read, + @Cached ReadAttributeFromModuleNode read, @Cached(inline = false) WriteAttributeToObjectNode write, @Cached @Exclusive InlinedBranchProfile create) { Object annotations = read.execute(locals, T___ANNOTATIONS__); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/ReadBuiltinNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/ReadBuiltinNode.java index 327471de58..70f48acd3e 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/ReadBuiltinNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/ReadBuiltinNode.java @@ -48,7 +48,7 @@ import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PNodeWithContext; import com.oracle.graal.python.nodes.PRaiseNode; -import com.oracle.graal.python.nodes.attributes.ReadAttributeFromObjectNode; +import com.oracle.graal.python.nodes.attributes.ReadAttributeFromModuleNode; import com.oracle.graal.python.runtime.PythonContext; import com.oracle.graal.python.runtime.exception.PException; import com.oracle.truffle.api.CompilerAsserts; @@ -77,7 +77,7 @@ Object returnBuiltinFromConstantModule(TruffleString attributeId, @Bind Node inliningTarget, @Exclusive @Cached PRaiseNode raiseNode, @Exclusive @Cached InlinedConditionProfile isBuiltinProfile, - @Shared @Cached ReadAttributeFromObjectNode readFromBuiltinsNode, + @Shared @Cached ReadAttributeFromModuleNode readFromBuiltinsNode, @Cached(value = "getBuiltins()", allowUncached = true) PythonModule builtins) { return readBuiltinFromModule(attributeId, raiseNode, inliningTarget, isBuiltinProfile, builtins, readFromBuiltinsNode); } @@ -93,7 +93,7 @@ Object returnBuiltin(TruffleString attributeId, @Bind Node inliningTarget, @Exclusive @Cached PRaiseNode raiseNode, @Exclusive @Cached InlinedConditionProfile isBuiltinProfile, - @Shared @Cached ReadAttributeFromObjectNode readFromBuiltinsNode, + @Shared @Cached ReadAttributeFromModuleNode readFromBuiltinsNode, @Exclusive @Cached InlinedConditionProfile ctxInitializedProfile) { PythonModule builtins = getBuiltins(inliningTarget, ctxInitializedProfile); return returnBuiltinFromConstantModule(attributeId, inliningTarget, raiseNode, isBuiltinProfile, readFromBuiltinsNode, builtins); @@ -101,7 +101,7 @@ Object returnBuiltin(TruffleString attributeId, private static Object readBuiltinFromModule(TruffleString attributeId, PRaiseNode raiseNode, Node inliningTarget, InlinedConditionProfile isBuiltinProfile, PythonModule builtins, - ReadAttributeFromObjectNode readFromBuiltinsNode) { + ReadAttributeFromModuleNode readFromBuiltinsNode) { Object builtin = readFromBuiltinsNode.execute(builtins, attributeId); if (isBuiltinProfile.profile(inliningTarget, builtin != PNone.NO_VALUE)) { return builtin; diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/ReadGlobalOrBuiltinNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/ReadGlobalOrBuiltinNode.java index 4bb08e8307..87d0564e58 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/ReadGlobalOrBuiltinNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/ReadGlobalOrBuiltinNode.java @@ -38,7 +38,7 @@ import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PNodeWithContext; import com.oracle.graal.python.nodes.PRaiseNode; -import com.oracle.graal.python.nodes.attributes.ReadAttributeFromObjectNode; +import com.oracle.graal.python.nodes.attributes.ReadAttributeFromModuleNode; import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinObjectProfile; import com.oracle.graal.python.runtime.exception.PException; import com.oracle.truffle.api.CompilerAsserts; @@ -88,7 +88,7 @@ protected static Object readGlobalCached(@SuppressWarnings("unused") PythonModul @Bind Node inliningTarget, @Shared("readFromBuiltinsNode") @Cached ReadBuiltinNode readFromBuiltinsNode, @Exclusive @Cached InlinedBranchProfile wasReadFromModule, - @Shared("readFromModule") @Cached ReadAttributeFromObjectNode readFromModuleNode, + @Shared("readFromModule") @Cached ReadAttributeFromModuleNode readFromModuleNode, @Cached(value = "globals", weak = true) PythonModule cachedGlobals) { Object result = readFromModuleNode.execute(cachedGlobals, attributeId); return returnGlobalOrBuiltin(result, attributeId, readFromBuiltinsNode, inliningTarget, wasReadFromModule); @@ -100,7 +100,7 @@ protected static Object readGlobal(PythonModule globals, TruffleString attribute @Bind Node inliningTarget, @Shared("readFromBuiltinsNode") @Cached ReadBuiltinNode readFromBuiltinsNode, @Exclusive @Cached InlinedBranchProfile wasReadFromModule, - @Shared("readFromModule") @Cached ReadAttributeFromObjectNode readFromModuleNode) { + @Shared("readFromModule") @Cached ReadAttributeFromModuleNode readFromModuleNode) { Object result = readFromModuleNode.execute(globals, attributeId); return returnGlobalOrBuiltin(result, attributeId, readFromBuiltinsNode, inliningTarget, wasReadFromModule); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonContext.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonContext.java index f6133f4463..216b1f8187 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonContext.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonContext.java @@ -97,6 +97,7 @@ import java.util.concurrent.locks.ReentrantLock; import java.util.logging.Level; +import com.oracle.graal.python.nodes.attributes.ReadAttributeFromModuleNode; import org.graalvm.options.OptionKey; import com.oracle.graal.python.PythonLanguage; @@ -2690,7 +2691,7 @@ public void ensureNFILanguage(Node nodeForRaise, String optionName, String optio public TruffleString getSoAbi() { if (soABI == null) { PythonModule sysModule = this.lookupBuiltinModule(T_SYS); - Object implementationObj = ReadAttributeFromObjectNode.getUncached().execute(sysModule, T_IMPLEMENTATION); + Object implementationObj = ReadAttributeFromModuleNode.getUncached().execute(sysModule, T_IMPLEMENTATION); // sys.implementation.cache_tag TruffleString cacheTag = (TruffleString) PyObjectGetAttr.executeUncached(implementationObj, T_CACHE_TAG); // sys.implementation._multiarch From dc57688e496b7a8325e06d084bb2db52a253159d Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Wed, 30 Jul 2025 13:42:03 +0200 Subject: [PATCH 34/38] Create the dict eagerly for modules so it's always there * graalpython/lib-python/3/test/test_descr.py::test.test_descr.ClassPropertiesAndMethods.test_uninitialized_modules caught a case where module.tp_new is called without module.tp_init and that caused an NPE from ReadAttributeFromModuleNode. * It seems much simpler and more efficient to always have that guarantee. --- .../objects/module/ModuleBuiltins.java | 27 ++++--------------- .../builtins/objects/module/PythonModule.java | 12 ++++----- 2 files changed, 10 insertions(+), 29 deletions(-) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/ModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/ModuleBuiltins.java index 7321fcff9e..634d012365 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/ModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/ModuleBuiltins.java @@ -99,8 +99,6 @@ import com.oracle.graal.python.nodes.function.builtins.clinic.ArgumentClinicProvider; import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinObjectProfile; import com.oracle.graal.python.nodes.object.GetDictIfExistsNode; -import com.oracle.graal.python.nodes.object.GetOrCreateDictNode; -import com.oracle.graal.python.nodes.object.SetDictNode; import com.oracle.graal.python.nodes.util.CannotCastException; import com.oracle.graal.python.nodes.util.CastToTruffleStringNode; import com.oracle.graal.python.runtime.PythonContext; @@ -141,8 +139,7 @@ public abstract static class ModuleNewNode extends PythonBuiltinNode { @Specialization @SuppressWarnings("unused") - static Object doGeneric(Object cls, Object[] varargs, PKeyword[] kwargs, - @Bind Node inliningTarget, + static PythonModule doGeneric(Object cls, Object[] varargs, PKeyword[] kwargs, @Bind PythonLanguage language, @Cached TypeNodes.GetInstanceShape getInstanceShape) { return PFactory.createPythonModule(language, cls, getInstanceShape.execute(cls)); @@ -161,15 +158,12 @@ protected ArgumentClinicProvider getArgumentClinic() { @Specialization public PNone module(PythonModule self, TruffleString name, Object doc, - @Bind Node inliningTarget, @Cached WriteAttributeToObjectNode writeName, @Cached WriteAttributeToObjectNode writeDoc, @Cached WriteAttributeToObjectNode writePackage, @Cached WriteAttributeToObjectNode writeLoader, - @Cached WriteAttributeToObjectNode writeSpec, - @Cached GetOrCreateDictNode getDict) { - // create dict if missing - getDict.execute(inliningTarget, self); + @Cached WriteAttributeToObjectNode writeSpec) { + assert GetDictIfExistsNode.getUncached().execute(self) != null : "PythonModule always have a dict"; // init writeName.execute(self, T___NAME__, name); @@ -216,19 +210,14 @@ public abstract static class ModuleDictNode extends PythonBinaryBuiltinNode { @Specialization(guards = "isNoValue(none)") static Object doManaged(PythonModule self, @SuppressWarnings("unused") PNone none, - @Bind Node inliningTarget, - @Exclusive @Cached GetDictIfExistsNode getDict, - @Cached SetDictNode setDict) { + @Exclusive @Cached GetDictIfExistsNode getDict) { PDict dict = getDict.execute(self); - if (dict == null) { - dict = createDict(inliningTarget, self, setDict); - } + assert dict != null : "PythonModule always have a dict"; return dict; } @Specialization(guards = "isNoValue(none)") static Object doNativeObject(PythonAbstractNativeObject self, @SuppressWarnings("unused") PNone none, - @Bind Node inliningTarget, @Exclusive @Cached GetDictIfExistsNode getDict, @Cached PRaiseNode raiseNode) { PDict dict = getDict.execute(self); @@ -243,12 +232,6 @@ static Object doError(Object self, @SuppressWarnings("unused") Object dict, @Bind Node inliningTarget) { throw PRaiseNode.raiseStatic(inliningTarget, PythonBuiltinClassType.TypeError, ErrorMessages.DESCRIPTOR_DICT_FOR_MOD_OBJ_DOES_NOT_APPLY_FOR_P, self); } - - private static PDict createDict(Node inliningTarget, PythonModule self, SetDictNode setDict) { - PDict dict = PFactory.createDictFixedStorage(PythonLanguage.get(inliningTarget), self); - setDict.execute(inliningTarget, self, dict); - return dict; - } } @Slot(value = SlotKind.tp_getattro, isComplex = true) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/PythonModule.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/PythonModule.java index ba26300675..1eff04dab1 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/PythonModule.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/PythonModule.java @@ -37,11 +37,9 @@ import com.oracle.graal.python.builtins.PythonBuiltinClassType; import com.oracle.graal.python.builtins.PythonBuiltins; import com.oracle.graal.python.builtins.objects.PNone; -import com.oracle.graal.python.builtins.objects.dict.PDict; import com.oracle.graal.python.builtins.objects.object.PythonObject; import com.oracle.graal.python.nodes.PGuards; -import com.oracle.graal.python.nodes.object.SetDictNode; -import com.oracle.graal.python.runtime.object.PFactory; +import com.oracle.graal.python.nodes.object.GetOrCreateDictNode; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.object.Shape; import com.oracle.truffle.api.strings.TruffleString; @@ -78,6 +76,7 @@ public final class PythonModule extends PythonObject { private PythonBuiltins builtins; private Object moduleState; + @TruffleBoundary public PythonModule(Object clazz, Shape instanceShape) { super(clazz, instanceShape); setAttribute(T___NAME__, PNone.NO_VALUE); @@ -87,6 +86,7 @@ public PythonModule(Object clazz, Shape instanceShape) { setAttribute(T___SPEC__, PNone.NO_VALUE); setAttribute(T___CACHED__, PNone.NO_VALUE); setAttribute(T___FILE__, PNone.NO_VALUE); + GetOrCreateDictNode.executeUncached(this); } /** @@ -102,6 +102,7 @@ private PythonModule(PythonLanguage lang, TruffleString moduleName) { setAttribute(T___SPEC__, PNone.NONE); setAttribute(T___CACHED__, PNone.NO_VALUE); setAttribute(T___FILE__, PNone.NO_VALUE); + GetOrCreateDictNode.executeUncached(this); } /** @@ -110,10 +111,7 @@ private PythonModule(PythonLanguage lang, TruffleString moduleName) { @TruffleBoundary public static PythonModule createInternal(TruffleString moduleName) { PythonLanguage language = PythonLanguage.get(null); - PythonModule pythonModule = new PythonModule(language, moduleName); - PDict dict = PFactory.createDictFixedStorage(language, pythonModule); - SetDictNode.executeUncached(pythonModule, dict); - return pythonModule; + return new PythonModule(language, moduleName); } public PythonBuiltins getBuiltins() { From dc3b6c67f2b3dc5b47ee750ff5a6b17fa0c1a8f2 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Tue, 22 Jul 2025 17:21:50 +0200 Subject: [PATCH 35/38] Avoid subclasses in WriteAttributeToObjectNode * Using the correct subclass is prone to errors. * Subclasses prevent host inlining. --- .../modules/cext/PythonCextBuiltins.java | 6 +- .../ctypes/LazyPyCArrayTypeBuiltins.java | 2 +- .../ctypes/LazyPyCSimpleTypeBuiltins.java | 2 +- .../objects/tuple/StructSequence.java | 6 +- .../builtins/objects/type/TypeBuiltins.java | 10 +- .../WriteAttributeToObjectNode.java | 225 ++++++------------ .../oracle/graal/python/util/PythonUtils.java | 2 +- 7 files changed, 86 insertions(+), 167 deletions(-) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextBuiltins.java index 0d6758a8ca..8a65ca99e2 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/cext/PythonCextBuiltins.java @@ -1014,19 +1014,19 @@ abstract static class PyObjectSetAttrNode extends PNodeWithContext { @Specialization static void doBuiltinClass(PythonBuiltinClass object, TruffleString key, Object value, - @Exclusive @Cached(value = "createForceType()", inline = false) WriteAttributeToObjectNode writeAttrNode) { + @Exclusive @Cached WriteAttributeToObjectNode writeAttrNode) { writeAttrNode.execute(object, key, value); } @Specialization static void doNativeClass(PythonNativeClass object, TruffleString key, Object value, - @Exclusive @Cached(value = "createForceType()", inline = false) WriteAttributeToObjectNode writeAttrNode) { + @Exclusive @Cached WriteAttributeToObjectNode writeAttrNode) { writeAttrNode.execute(object, key, value); } @Specialization(guards = {"!isPythonBuiltinClass(object)"}) static void doObject(PythonObject object, TruffleString key, Object value, - @Exclusive @Cached(inline = false) WriteAttributeToPythonObjectNode writeAttrToPythonObjectNode) { + @Exclusive @Cached WriteAttributeToPythonObjectNode writeAttrToPythonObjectNode) { writeAttrToPythonObjectNode.execute(object, key, value); } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ctypes/LazyPyCArrayTypeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ctypes/LazyPyCArrayTypeBuiltins.java index 55dbe15053..c3e9d4fc7e 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ctypes/LazyPyCArrayTypeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ctypes/LazyPyCArrayTypeBuiltins.java @@ -135,7 +135,7 @@ private static void createGetSet(PythonLanguage language, Object type, NodeFacto PBuiltinFunction getter = PFactory.createBuiltinFunction(language, name, type, 1, flags, rawCallTarget); GetSetDescriptor callable = PFactory.createGetSetDescriptor(language, getter, getter, name, type, false); callable.setAttribute(T___DOC__, toTruffleStringUncached(builtin.doc())); - WriteAttributeToObjectNode.getUncached(true).execute(type, name, callable); + WriteAttributeToObjectNode.getUncached().execute(type, name, callable); } @Builtin(name = "raw", minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2, isGetter = true, isSetter = true, doc = "value") diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ctypes/LazyPyCSimpleTypeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ctypes/LazyPyCSimpleTypeBuiltins.java index d0d7dc527c..c95f216be5 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ctypes/LazyPyCSimpleTypeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ctypes/LazyPyCSimpleTypeBuiltins.java @@ -132,7 +132,7 @@ private static void addClassMethod(PythonLanguage language, Object type, NodeFac PBuiltinFunction function = PFactory.createBuiltinFunction(language, name, type, 1, flags, callTarget); PDecoratedMethod classMethod = PFactory.createClassmethodFromCallableObj(language, function); function.setAttribute(T___DOC__, builtinDoc); - WriteAttributeToObjectNode.getUncached(true).execute(type, name, classMethod); + WriteAttributeToObjectNode.getUncached().execute(type, name, classMethod); } @ImportStatic(CDataTypeBuiltins.class) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/tuple/StructSequence.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/tuple/StructSequence.java index 71ae4abd50..a8390b41f0 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/tuple/StructSequence.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/tuple/StructSequence.java @@ -182,7 +182,7 @@ public static void initType(PythonContext context, PythonAbstractClass klass, De unnamedFields++; } } - WriteAttributeToObjectNode writeAttrNode = WriteAttributeToObjectNode.getUncached(true); + WriteAttributeToObjectNode writeAttrNode = WriteAttributeToObjectNode.getUncached(); if (klass instanceof PythonManagedClass managedClass) { /* * The methods and slots are already set for each PBCT, but we need to store the field @@ -220,7 +220,7 @@ public static void initType(PythonContext context, PythonAbstractClass klass, De private static void copyMethod(PythonLanguage language, PythonAbstractClass klass, TruffleString name, PythonBuiltinClass template) { PBuiltinFunction templateMethod = (PBuiltinFunction) template.getAttribute(name); PBuiltinFunction method = PFactory.createBuiltinFunction(language, templateMethod, klass); - WriteAttributeToObjectNode.getUncached(true).execute(klass, name, method); + WriteAttributeToObjectNode.getUncached().execute(klass, name, method); } private static void createMember(PythonLanguage language, Object klass, TruffleString name, TruffleString doc, int idx) { @@ -230,7 +230,7 @@ private static void createMember(PythonLanguage language, Object klass, TruffleS if (doc != null) { callable.setAttribute(T___DOC__, doc); } - WriteAttributeToObjectNode.getUncached(true).execute(klass, name, callable); + WriteAttributeToObjectNode.getUncached().execute(klass, name, callable); } private static class GetStructMemberNode extends PRootNode { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeBuiltins.java index d280d524b5..67c4830833 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/TypeBuiltins.java @@ -622,7 +622,7 @@ static void set(VirtualFrame frame, Object object, Object keyObject, Object valu @Bind Node inliningTarget, @Cached CastToTruffleStringChecked0Node castKeyNode, @Cached ObjectNodes.GenericSetAttrNode genericSetAttrNode, - @Cached("createForceType()") WriteAttributeToObjectNode write) { + @Cached WriteAttributeToObjectNode write) { TruffleString key = castKeyNode.cast(inliningTarget, keyObject, ATTR_NAME_MUST_BE_STRING); genericSetAttrNode.execute(inliningTarget, frame, object, key, value, write); } @@ -633,7 +633,7 @@ void setBuiltin(Object object, Object key, Object value) { if (PythonContext.get(this).isInitialized()) { throw PRaiseNode.raiseStatic(this, TypeError, ErrorMessages.CANT_SET_ATTRIBUTE_R_OF_IMMUTABLE_TYPE_N, PyObjectReprAsTruffleStringNode.executeUncached(key), object); } else { - set(null, object, key, value, null, CastToTruffleStringChecked0Node.getUncached(), ObjectNodes.GenericSetAttrNode.getUncached(), WriteAttributeToObjectNode.getUncached(true)); + set(null, object, key, value, null, CastToTruffleStringChecked0Node.getUncached(), ObjectNodes.GenericSetAttrNode.getUncached(), WriteAttributeToObjectNode.getUncached()); } } @@ -953,7 +953,7 @@ static Object getModule(PythonClass cls, @SuppressWarnings("unused") PNone value @Specialization(guards = "!isNoValue(value)") static Object setModule(PythonClass cls, Object value, - @Cached WriteAttributeToObjectNode writeAttrNode) { + @Shared @Cached WriteAttributeToObjectNode writeAttrNode) { writeAttrNode.execute(cls, T___MODULE__, value); return PNone.NONE; } @@ -991,13 +991,13 @@ static Object getModule(PythonAbstractNativeObject cls, @SuppressWarnings("unuse static Object setNative(PythonAbstractNativeObject cls, Object value, @Bind Node inliningTarget, @Shared @Cached GetTypeFlagsNode getFlags, - @Cached("createForceType()") WriteAttributeToObjectNode writeAttr, + @Shared @Cached WriteAttributeToObjectNode writeAttrNode, @Shared @Cached PRaiseNode raiseNode) { long flags = getFlags.execute(cls); if ((flags & TypeFlags.HEAPTYPE) == 0) { throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.CANT_SET_N_S, cls, T___MODULE__); } - writeAttr.execute(cls, T___MODULE__, value); + writeAttrNode.execute(cls, T___MODULE__, value); return PNone.NONE; } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/WriteAttributeToObjectNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/WriteAttributeToObjectNode.java index 1ffc0eea22..e225ad0792 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/WriteAttributeToObjectNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/WriteAttributeToObjectNode.java @@ -64,11 +64,8 @@ import com.oracle.graal.python.nodes.ErrorMessages; import com.oracle.graal.python.nodes.PNodeWithContext; import com.oracle.graal.python.nodes.PRaiseNode; -import com.oracle.graal.python.nodes.attributes.WriteAttributeToObjectNodeGen.WriteAttributeToObjectNotTypeNodeGen; -import com.oracle.graal.python.nodes.attributes.WriteAttributeToObjectNodeGen.WriteAttributeToObjectTpDictNodeGen; import com.oracle.graal.python.nodes.object.GetDictIfExistsNode; import com.oracle.graal.python.runtime.PythonContext; -import com.oracle.graal.python.runtime.PythonOptions; import com.oracle.truffle.api.CompilerAsserts; import com.oracle.truffle.api.dsl.Bind; import com.oracle.truffle.api.dsl.Cached; @@ -76,55 +73,29 @@ import com.oracle.truffle.api.dsl.Cached.Shared; 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.NeverDefault; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.library.CachedLibrary; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.object.DynamicObjectLibrary; import com.oracle.truffle.api.profiles.InlinedBranchProfile; +import com.oracle.truffle.api.profiles.InlinedConditionProfile; import com.oracle.truffle.api.strings.TruffleString; -@ImportStatic(PythonOptions.class) +@GenerateUncached @GenerateInline(false) // footprint reduction 120 -> 103 public abstract class WriteAttributeToObjectNode extends PNodeWithContext { public abstract boolean execute(Object primary, TruffleString key, Object value); - @NeverDefault - public static WriteAttributeToObjectNode create() { - return WriteAttributeToObjectNotTypeNodeGen.create(); - } - - @NeverDefault - public static WriteAttributeToObjectNode create(boolean forceType) { - if (forceType) { - return WriteAttributeToObjectTpDictNodeGen.create(); - } - return WriteAttributeToObjectNotTypeNodeGen.create(); - } - - @NeverDefault - public static WriteAttributeToObjectNode createForceType() { - return WriteAttributeToObjectTpDictNodeGen.create(); - } - public static WriteAttributeToObjectNode getUncached() { - return WriteAttributeToObjectNotTypeNodeGen.getUncached(); - } - - public static WriteAttributeToObjectNode getUncached(boolean forceType) { - if (forceType) { - return WriteAttributeToObjectTpDictNodeGen.getUncached(); - } - return WriteAttributeToObjectNotTypeNodeGen.getUncached(); + return WriteAttributeToObjectNodeGen.getUncached(); } - protected static boolean isAttrWritable(PythonObject self) { + static boolean isAttrWritable(PythonObject self) { return (self.getShape().getFlags() & PythonObject.HAS_SLOTS_BUT_NO_DICT_FLAG) == 0; } - protected static boolean writeToDynamicStorageNoTypeGuard(Object obj, GetDictIfExistsNode getDict) { + static boolean writeToDynamicStorageNoTypeGuard(Object obj, GetDictIfExistsNode getDict) { return getDict.execute(obj) == null && !PythonManagedClass.isInstance(obj); } @@ -296,7 +267,73 @@ static boolean doPBCT(PythonBuiltinClassType object, TruffleString key, Object v return recursive.execute(PythonContext.get(recursive).lookupType(object), key, value); } - protected static boolean isErrorCase(GetDictIfExistsNode getDict, Object object) { + private static void checkNativeImmutable(Node inliningTarget, PythonAbstractNativeObject object, TruffleString key, + CStructAccess.ReadI64Node getNativeFlags, + PRaiseNode raiseNode) { + long flags = getNativeFlags.readFromObj(object, CFields.PyTypeObject__tp_flags); + if ((flags & TypeFlags.IMMUTABLETYPE) != 0) { + throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.CANT_SET_ATTRIBUTE_R_OF_IMMUTABLE_TYPE_N, key, object); + } + } + + @Specialization + static boolean writeNativeObjectOrClass(PythonAbstractNativeObject object, TruffleString key, Object value, + @Bind Node inliningTarget, + @Cached InlinedConditionProfile isTypeProfile, + @Shared("getDict") @Cached GetDictIfExistsNode getDict, + @Cached CStructAccess.ReadI64Node getNativeFlags, + @Cached CStructAccess.ReadObjectNode getNativeDict, + @Exclusive @Cached HashingStorageSetItem setHashingStorageItem, + @Exclusive @Cached InlinedBranchProfile updateStorage, + @Exclusive @Cached InlinedBranchProfile canBeSpecialSlot, + @Cached IsTypeNode isTypeNode, + @Exclusive @Cached PRaiseNode raiseNode, + @Shared("cpLen") @Cached TruffleString.CodePointLengthNode codePointLengthNode, + @Shared("cpAtIndex") @Cached TruffleString.CodePointAtIndexNode codePointAtIndexNode) { + boolean isType = isTypeProfile.profile(inliningTarget, isTypeNode.execute(inliningTarget, object)); + try { + Object dict; + if (isType) { + checkNativeImmutable(inliningTarget, object, key, getNativeFlags, raiseNode); + /* + * For native types, the type attributes are stored in a dict that is located in + * 'typePtr->tp_dict'. So, this is different to a native object (that is not a type) + * and we need to load the dict differently. We must not use + * 'PythonObjectLibrary.getDict' here but read member 'tp_dict'. + */ + dict = getNativeDict.readFromObj(object, PyTypeObject__tp_dict); + } else { + /* + * The dict of native objects that stores the object attributes is located at + * 'objectPtr + Py_TYPE(objectPtr)->tp_dictoffset'. 'PythonObjectLibrary.getDict' + * will exactly load the dict from there. + */ + dict = getDict.execute(object); + } + if (dict instanceof PDict) { + return writeToDict((PDict) dict, key, value, inliningTarget, updateStorage, setHashingStorageItem); + } + throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.AttributeError, ErrorMessages.OBJ_P_HAS_NO_ATTR_S, object, key); + } finally { + if (isType && TpSlots.canBeSpecialMethod(key, codePointLengthNode, codePointAtIndexNode)) { + canBeSpecialSlot.enter(inliningTarget); + // In theory, we should do this only in type's default tp_setattr(o) slots, + // one could probably bypass that by using different metaclass and + // overriding tp_setattr and delegate to object's tp_setattr that does not + // have this hook + TpSlots.updateSlot(object, key); + } + } + } + + @Specialization(guards = "isErrorCase(getDict, object)") + static boolean doError(Object object, TruffleString key, Object value, + @Shared("getDict") @Cached GetDictIfExistsNode getDict, + @Bind Node inliningTarget) { + throw PRaiseNode.raiseStatic(inliningTarget, PythonBuiltinClassType.AttributeError, ErrorMessages.OBJ_P_HAS_NO_ATTR_S, object, key); + } + + static boolean isErrorCase(GetDictIfExistsNode getDict, Object object) { if (object instanceof PythonObject self) { if (isAttrWritable(self) && (getDict.execute(self) == null)) { return false; @@ -314,122 +351,4 @@ protected static boolean isErrorCase(GetDictIfExistsNode getDict, Object object) return true; } - @GenerateUncached - @GenerateInline(false) // footprint reduction 124 -> 107 - protected abstract static class WriteAttributeToObjectNotTypeNode extends WriteAttributeToObjectNode { - @Specialization - static boolean writeNativeObject(PythonAbstractNativeObject object, TruffleString key, Object value, - @Bind Node inliningTarget, - @Shared("getDict") @Cached GetDictIfExistsNode getDict, - @Shared("setHashingStorageItem") @Cached HashingStorageSetItem setHashingStorageItem, - @Shared("updateStorage") @Cached InlinedBranchProfile updateStorage, - @Cached PRaiseNode raiseNode) { - /* - * The dict of native objects that stores the object attributes is located at 'objectPtr - * + Py_TYPE(objectPtr)->tp_dictoffset'. 'PythonObjectLibrary.getDict' will exactly load - * the dict from there. - */ - PDict dict = getDict.execute(object); - if (dict != null) { - return writeToDict(dict, key, value, inliningTarget, updateStorage, setHashingStorageItem); - } - throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.AttributeError, ErrorMessages.OBJ_P_HAS_NO_ATTR_S, object, key); - } - - @Specialization(guards = "isErrorCase(getDict, object)") - static boolean doError(Object object, TruffleString key, @SuppressWarnings("unused") Object value, - @SuppressWarnings("unused") @Shared("getDict") @Cached GetDictIfExistsNode getDict, - @Bind Node inliningTarget) { - throw PRaiseNode.raiseStatic(inliningTarget, PythonBuiltinClassType.AttributeError, ErrorMessages.OBJ_P_HAS_NO_ATTR_S, object, key); - } - } - - @GenerateUncached - @GenerateInline(false) // footprint reduction 132 -> 115 - @ImportStatic(TpSlots.class) - protected abstract static class WriteAttributeToObjectTpDictNode extends WriteAttributeToObjectNode { - - private static void checkNativeImmutable(Node inliningTarget, PythonAbstractNativeObject object, TruffleString key, - CStructAccess.ReadI64Node getNativeFlags, - PRaiseNode raiseNode) { - long flags = getNativeFlags.readFromObj(object, CFields.PyTypeObject__tp_flags); - if ((flags & TypeFlags.IMMUTABLETYPE) != 0) { - throw raiseNode.raise(inliningTarget, TypeError, ErrorMessages.CANT_SET_ATTRIBUTE_R_OF_IMMUTABLE_TYPE_N, key, object); - } - } - - /* - * Simplest case: the key object is a String (so it cannot be a hidden key) and it's not a - * special method slot. - */ - @Specialization(guards = "!canBeSpecialMethod(key, codePointLengthNode, codePointAtIndexNode)") - static boolean writeNativeClassSimple(PythonAbstractNativeObject object, TruffleString key, Object value, - @Bind Node inliningTarget, - @Shared @Cached CStructAccess.ReadI64Node getNativeFlags, - @Shared @Cached CStructAccess.ReadObjectNode getNativeDict, - @Shared("setHashingStorageItem") @Cached HashingStorageSetItem setHashingStorageItem, - @Shared("updateStorage") @Cached InlinedBranchProfile updateStorage, - @Exclusive @Cached PRaiseNode raiseNode, - @SuppressWarnings("unused") @Shared("cpLen") @Cached TruffleString.CodePointLengthNode codePointLengthNode, - @SuppressWarnings("unused") @Shared("cpAtIndex") @Cached TruffleString.CodePointAtIndexNode codePointAtIndexNode) { - checkNativeImmutable(inliningTarget, object, key, getNativeFlags, raiseNode); - /* - * For native types, the type attributes are stored in a dict that is located in - * 'typePtr->tp_dict'. So, this is different to a native object (that is not a type) and - * we need to load the dict differently. We must not use 'PythonObjectLibrary.getDict' - * here but read member 'tp_dict'. - */ - Object dict = getNativeDict.readFromObj(object, PyTypeObject__tp_dict); - if (dict instanceof PDict) { - return writeToDict((PDict) dict, key, value, inliningTarget, updateStorage, setHashingStorageItem); - } - throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.AttributeError, ErrorMessages.OBJ_P_HAS_NO_ATTR_S, object, key); - } - - @Specialization(replaces = "writeNativeClassSimple") - static boolean writeNativeClassGeneric(PythonAbstractNativeObject object, TruffleString key, Object value, - @Bind Node inliningTarget, - @Shared @Cached CStructAccess.ReadI64Node getNativeFlags, - @Shared @Cached CStructAccess.ReadObjectNode getNativeDict, - @Exclusive @Cached HashingStorageSetItem setHashingStorageItem, - @Exclusive @Cached InlinedBranchProfile updateStorage, - @Exclusive @Cached InlinedBranchProfile canBeSpecialSlot, - @Cached IsTypeNode isTypeNode, - @Exclusive @Cached PRaiseNode raiseNode, - @Shared("cpLen") @Cached TruffleString.CodePointLengthNode codePointLengthNode, - @Shared("cpAtIndex") @Cached TruffleString.CodePointAtIndexNode codePointAtIndexNode) { - try { - checkNativeImmutable(inliningTarget, object, key, getNativeFlags, raiseNode); - /* - * For native types, the type attributes are stored in a dict that is located in - * 'typePtr->tp_dict'. So, this is different to a native object (that is not a type) - * and we need to load the dict differently. We must not use - * 'PythonObjectLibrary.getDict' here but read member 'tp_dict'. - */ - Object dict = getNativeDict.readFromObj(object, PyTypeObject__tp_dict); - if (dict instanceof PDict) { - return writeToDict((PDict) dict, key, value, inliningTarget, updateStorage, setHashingStorageItem); - } - throw raiseNode.raise(inliningTarget, PythonBuiltinClassType.AttributeError, ErrorMessages.OBJ_P_HAS_NO_ATTR_S, object, key); - } finally { - if (TpSlots.canBeSpecialMethod(key, codePointLengthNode, codePointAtIndexNode)) { - canBeSpecialSlot.enter(inliningTarget); - // In theory, we should do this only in type's default tp_setattr(o) slots, - // one could probably bypass that by using different metaclass and - // overriding tp_setattr and delegate to object's tp_setattr that does not - // have this hook - if (isTypeNode.execute(inliningTarget, object)) { - TpSlots.updateSlot(object, key); - } - } - } - } - - @Specialization(guards = "isErrorCase(getDict, object)") - static boolean doError(Object object, TruffleString key, @SuppressWarnings("unused") Object value, - @SuppressWarnings("unused") @Shared("getDict") @Cached GetDictIfExistsNode getDict, - @Bind Node inliningTarget) { - throw PRaiseNode.raiseStatic(inliningTarget, PythonBuiltinClassType.AttributeError, ErrorMessages.OBJ_P_HAS_NO_ATTR_S, object, key); - } - } } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/util/PythonUtils.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/util/PythonUtils.java index 30ac35de92..21d0b9c14d 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/util/PythonUtils.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/util/PythonUtils.java @@ -719,7 +719,7 @@ public static PBuiltinFunction createMethod(Object klass, Builtin builtin, RootC TruffleString name = toTruffleStringUncached(builtin.name()); PBuiltinFunction function = PFactory.createBuiltinFunction(PythonLanguage.get(null), name, type, numDefaults, flags, callTarget); if (klass != null) { - WriteAttributeToObjectNode.getUncached(true).execute(klass, name, function); + WriteAttributeToObjectNode.getUncached().execute(klass, name, function); } return function; } From 67b6b2f11f1fe5d9f1f1cc4211b60b2dccc66ebd Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Fri, 25 Jul 2025 15:18:50 +0200 Subject: [PATCH 36/38] Document StgDictObject --- .../graal/python/builtins/modules/ctypes/StgDictObject.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ctypes/StgDictObject.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ctypes/StgDictObject.java index 4c8f74c76c..025b9be923 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ctypes/StgDictObject.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ctypes/StgDictObject.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2025, 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 @@ -48,6 +48,10 @@ import com.oracle.truffle.api.object.Shape; import com.oracle.truffle.api.strings.TruffleString; +/** + * Extra information for ctypes stored in a type's dict. See + * https://github.com/python/cpython/blob/fece15d29f28e89f1231afa80508c80ed28dc37d/Modules/_ctypes/ctypes.h#L333 + */ public final class StgDictObject extends PDict { protected static final int VOID_PTR_SIZE = Long.BYTES; // sizeof(void *) From 256fb7b59850e5d924e6ad3ea5aa409e3f4f524d Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Mon, 28 Jul 2025 11:29:54 +0200 Subject: [PATCH 37/38] Document ReadAttributeFromPythonObjectNode --- .../attributes/ReadAttributeFromPythonObjectNode.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/ReadAttributeFromPythonObjectNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/ReadAttributeFromPythonObjectNode.java index b2f9dff187..3181604833 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/ReadAttributeFromPythonObjectNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/ReadAttributeFromPythonObjectNode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2025, 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 @@ -44,15 +44,12 @@ import com.oracle.graal.python.builtins.objects.module.PythonModule; import com.oracle.graal.python.builtins.objects.object.PythonObject; import com.oracle.graal.python.builtins.objects.type.PythonManagedClass; -import com.oracle.graal.python.nodes.PGuards; import com.oracle.graal.python.nodes.PNodeWithContext; -import com.oracle.graal.python.runtime.PythonOptions; import com.oracle.truffle.api.Assumption; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.GenerateInline; import com.oracle.truffle.api.dsl.GenerateUncached; import com.oracle.truffle.api.dsl.Idempotent; -import com.oracle.truffle.api.dsl.ImportStatic; import com.oracle.truffle.api.dsl.NeverDefault; import com.oracle.truffle.api.dsl.NonIdempotent; import com.oracle.truffle.api.dsl.Specialization; @@ -64,7 +61,10 @@ import com.oracle.truffle.api.object.Shape; import com.oracle.truffle.api.strings.TruffleString; -@ImportStatic({PGuards.class, PythonOptions.class}) +/** + * Reads attribute directly from the underlying {@link DynamicObject} regardless of whether the + * object has dict, also bypasses any other additional logic in {@link ReadAttributeFromObjectNode}. + */ @GenerateUncached @GenerateInline(false) // footprint reduction 44 -> 25 public abstract class ReadAttributeFromPythonObjectNode extends PNodeWithContext { From cfd056fdd7aa0d3d9e6fd304dede19d5b7f65d1c Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Wed, 30 Jul 2025 13:40:56 +0200 Subject: [PATCH 38/38] Document how to print a Python traceback in the Java debugger --- docs/contributor/CONTRIBUTING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/contributor/CONTRIBUTING.md b/docs/contributor/CONTRIBUTING.md index 68b47fa603..f5f1432d38 100644 --- a/docs/contributor/CONTRIBUTING.md +++ b/docs/contributor/CONTRIBUTING.md @@ -149,6 +149,7 @@ mx python-gate --tags python-unittest If some of the tests fail, you can re-run just a single test like this, substituting TEST-SELECTOR with the test you want to run. You can use the whole failed test name including the path as the selector. Note that you can insert `-d` to debug on the Java level or use `--inspect` to debug in the Chrome debugger. +Use `com.oracle.graal.python.runtime.exception.ExceptionUtils.printPythonLikeStackTrace()` in the Java debugger to print the current Python traceback on `stderr`. ```bash mx [-d] graalpytest [--inspect] TEST-SELECTOR