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 57fca05976..149cb08fb9 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 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -250,9 +250,9 @@ abstract static class GraalPyPrivate_Module_AddFunctionToModule extends CApi7Bui static Object moduleFunction(Object methodDefPtr, PythonModule mod, TruffleString name, Object cfunc, int flags, int wrapper, Object doc, @Bind Node inliningTarget, @Cached ObjectBuiltins.SetattrNode setattrNode, - @Cached ReadAttributeFromPythonObjectNode readAttrNode, + @Cached(inline = true) ReadAttributeFromPythonObjectNode readAttrNode, @Cached CFunctionNewExMethodNode cFunctionNewExMethodNode) { - Object modName = readAttrNode.execute(mod, T___NAME__, null); + Object modName = readAttrNode.execute(inliningTarget, mod, T___NAME__, null); assert modName != null : "module name is missing!"; Object func = cFunctionNewExMethodNode.execute(inliningTarget, methodDefPtr, name, cfunc, flags, wrapper, mod, modName, doc); setattrNode.executeSetAttr(null, mod, name, func); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/DynamicObjectStorage.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/DynamicObjectStorage.java index 1c2d2fce81..30be65426e 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/DynamicObjectStorage.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/DynamicObjectStorage.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -108,12 +108,7 @@ public DynamicObject getStore() { } protected static Object[] keyArray(DynamicObjectStorage self) { - return DynamicObjectStorage.keyArray(self.store.getShape()); - } - - @TruffleBoundary - protected static Object[] keyArray(Shape shape) { - return shape.getKeyList().toArray(); + return DynamicObject.GetKeyArrayNode.getUncached().execute(self.store); } @GenerateUncached @@ -131,7 +126,7 @@ public abstract static class LengthNode extends Node { static int cachedLen(DynamicObjectStorage self, @SuppressWarnings("unused") @Cached("self.store.getShape()") Shape cachedShape, @Cached(value = "keyArray(self)", dimensions = 1) Object[] keys, - @Shared @Cached ReadAttributeFromPythonObjectNode readNode) { + @Shared @Cached(inline = false) ReadAttributeFromPythonObjectNode readNode) { int len = 0; for (Object key : keys) { len = incrementLen(self, readNode, len, key); @@ -139,11 +134,11 @@ static int cachedLen(DynamicObjectStorage self, return len; } - @Specialization(replaces = "cachedLen", guards = {"cachedShape == self.store.getShape()"}, limit = "3") - static int cachedKeys(DynamicObjectStorage self, - @SuppressWarnings("unused") @Cached("self.store.getShape()") Shape cachedShape, - @Cached(value = "keyArray(self)", dimensions = 1) Object[] keys, - @Shared @Cached ReadAttributeFromPythonObjectNode readNode) { + @Specialization(replaces = "cachedLen") + static int length(DynamicObjectStorage self, + @Shared @Cached(inline = false) ReadAttributeFromPythonObjectNode readNode, + @Cached DynamicObject.GetKeyArrayNode keyArrayNode) { + Object[] keys = keyArrayNode.execute(self.store); int len = 0; for (Object key : keys) { len = incrementLen(self, readNode, len, key); @@ -151,12 +146,6 @@ static int cachedKeys(DynamicObjectStorage self, return len; } - @Specialization(replaces = "cachedKeys") - static int length(DynamicObjectStorage self, - @Shared @Cached ReadAttributeFromPythonObjectNode readNode) { - return cachedKeys(self, self.store.getShape(), keyArray(self), readNode); - } - private static boolean hasStringKey(DynamicObjectStorage self, TruffleString key, ReadAttributeFromPythonObjectNode readNode) { return readNode.execute(self.store, key, PNone.NO_VALUE) != PNone.NO_VALUE; } @@ -186,19 +175,19 @@ abstract static class GetItemNode extends Node { @Specialization static Object string(Node inliningTarget, DynamicObjectStorage self, TruffleString key, @SuppressWarnings("unused") long keyHash, - @Shared("readKey") @Cached(inline = false) ReadAttributeFromPythonObjectNode readKey, - @Exclusive @Cached InlinedConditionProfile noValueProfile) { - Object result = readKey.execute(self.store, key, PNone.NO_VALUE); + @Shared @Cached ReadAttributeFromPythonObjectNode readKey, + @Shared @Cached InlinedConditionProfile noValueProfile) { + Object result = readKey.execute(inliningTarget, self.store, key, PNone.NO_VALUE); return noValueProfile.profile(inliningTarget, result == PNone.NO_VALUE) ? null : result; } - @Specialization(guards = "isBuiltinString.execute(inliningTarget, key)", limit = "1") + @Specialization(guards = "isBuiltinString.execute(inliningTarget, key)") @InliningCutoff static Object pstring(Node inliningTarget, DynamicObjectStorage self, PString key, @SuppressWarnings("unused") long keyHash, - @SuppressWarnings("unused") @Cached PyUnicodeCheckExactNode isBuiltinString, - @Cached CastToTruffleStringNode castStr, - @Shared("readKey") @Cached(inline = false) ReadAttributeFromPythonObjectNode readKey, - @Exclusive @Cached InlinedConditionProfile noValueProfile) { + @SuppressWarnings("unused") @Shared @Cached PyUnicodeCheckExactNode isBuiltinString, + @Cached(inline = false) CastToTruffleStringNode castStr, + @Shared @Cached ReadAttributeFromPythonObjectNode readKey, + @Shared @Cached InlinedConditionProfile noValueProfile) { return string(inliningTarget, self, castStr.execute(inliningTarget, key), -1, readKey, noValueProfile); } @@ -219,9 +208,9 @@ abstract static class GetItemNoStringKeyNode extends Node { @ExplodeLoop(kind = LoopExplosionKind.FULL_UNROLL_UNTIL_RETURN) static Object notString(Frame frame, DynamicObjectStorage self, Object key, long hashIn, @Bind Node inliningTarget, - @Shared("readKey") @Cached ReadAttributeFromPythonObjectNode readKey, + @Shared("readKey") @Cached(inline = false) ReadAttributeFromPythonObjectNode readKey, @Exclusive @Cached("self.store.getShape()") Shape cachedShape, - @Exclusive @Cached(value = "keyArray(cachedShape)", dimensions = 1) Object[] keyList, + @Exclusive @Cached(value = "keyArray(self)", dimensions = 1) Object[] keyList, @Shared("eqNode") @Cached PyObjectRichCompareBool eqNode, @Shared("hashNode") @Cached PyObjectHashNode hashNode, @Shared("noValueProfile") @Cached InlinedConditionProfile noValueProfile) { @@ -240,7 +229,7 @@ static Object notString(Frame frame, DynamicObjectStorage self, Object key, long @Specialization(replaces = "notString") static Object notStringLoop(Frame frame, DynamicObjectStorage self, Object key, long hashIn, @Bind Node inliningTarget, - @Shared("readKey") @Cached ReadAttributeFromPythonObjectNode readKey, + @Shared("readKey") @Cached(inline = false) ReadAttributeFromPythonObjectNode readKey, @Shared("eqNode") @Cached PyObjectRichCompareBool eqNode, @Shared("hashNode") @Cached PyObjectHashNode hashNode, @Shared("noValueProfile") @Cached InlinedConditionProfile noValueProfile) { @@ -280,7 +269,7 @@ void setStringKey(TruffleString key, Object value, DynamicObject.PutNode putNode } boolean shouldTransitionOnPut() { - // For now we do not use SIZE_THRESHOLD condition to transition storages that wrap + // For now, we do not use SIZE_THRESHOLD condition to transition storages that wrap // dictionaries retrieved via object's __dict__ boolean notDunderDict = store instanceof Store; int propertyCount = store.getShape().getPropertyCount(); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/HashingStorageNodes.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/HashingStorageNodes.java index cfb38025d2..1311d76254 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/HashingStorageNodes.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/common/HashingStorageNodes.java @@ -69,6 +69,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.ReadAttributeFromPythonObjectNode; import com.oracle.graal.python.nodes.interop.PForeignToPTypeNode; import com.oracle.graal.python.nodes.object.IsForeignObjectNode; import com.oracle.graal.python.nodes.util.CastBuiltinStringToTruffleStringNode; @@ -146,6 +147,46 @@ static Object foreign(Node inliningTarget, ForeignHashingStorage self, Object ke } } + @GenerateUncached + @GenerateInline + @GenerateCached(false) + public abstract static class HashingStorageGetItemStringKey extends Node { + public abstract Object execute(Node inliningTarget, HashingStorage self, TruffleString key); + + @Specialization + static Object economicMap(Node inliningTarget, EconomicMapStorage self, TruffleString key, + @Cached TruffleString.HashCodeNode hashCodeNode, + @Cached ObjectHashMap.GetNode getNode) { + return getNode.execute(null, inliningTarget, self.map, key, PyObjectHashNode.hash(key, hashCodeNode)); + } + + @Specialization + static Object dom(Node inliningTarget, DynamicObjectStorage self, TruffleString key, + @Cached ReadAttributeFromPythonObjectNode readKey, + @Cached InlinedConditionProfile noValueProfile) { + return DynamicObjectStorage.GetItemNode.string(inliningTarget, self, key, -1, readKey, noValueProfile); + } + + @Specialization + @SuppressWarnings("unused") + static Object empty(EmptyStorage self, TruffleString key) { + return null; + } + + @Specialization + @InliningCutoff + static Object keywords(Node inliningTarget, KeywordsStorage self, TruffleString key, + @Cached GetKeywordsStorageItemNode getNode) { + return getNode.execute(null, inliningTarget, self, key, -1); + } + + @Specialization + static Object foreign(Node inliningTarget, ForeignHashingStorage self, TruffleString key, + @Cached ForeignHashingStorage.GetNode getNode) { + return getNode.execute(inliningTarget, self, key); + } + } + @GenerateUncached @GenerateInline @GenerateCached(false) 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 aeb261bc99..faf2ca6e8a 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 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -360,7 +360,7 @@ int getExceptionExitStatus( @Cached CastToJavaIntExactNode castToInt, @Bind Node inliningTarget, @Exclusive @Cached GetClassNode getClassNode, - @Cached ReadAttributeFromPythonObjectNode readNode, + @Cached(inline = true) ReadAttributeFromPythonObjectNode readNode, @Exclusive @Cached InlinedBranchProfile unsupportedProfile, @Shared("gil") @Cached GilNode gil) throws UnsupportedMessageException { boolean mustRelease = gil.acquire(); @@ -368,7 +368,7 @@ int getExceptionExitStatus( if (getExceptionType(inliningTarget, getClassNode, gil) == ExceptionType.EXIT) { try { // Avoiding getattr because this message shouldn't have side-effects - Object code = readNode.execute(this, T_CODE); + Object code = readNode.execute(inliningTarget, this, T_CODE); if (code == PNone.NO_VALUE) { return 1; } 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 4bc2f8dc4b..a227db49be 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 @@ -224,9 +224,9 @@ static Object getModule(VirtualFrame frame, PBuiltinMethod self, @SuppressWarnin @Bind Node inliningTarget, @Cached("createFor($node)") BoundaryCallData boundaryCallData, @Cached PyObjectLookupAttr lookup, - @Cached ReadAttributeFromPythonObjectNode readAttrNode) { + @Cached(inline = true) ReadAttributeFromPythonObjectNode readAttrNode) { // No profiling, performance here is not very important - Object module = readAttrNode.execute(self, T___MODULE__, PNone.NO_VALUE); + Object module = readAttrNode.execute(inliningTarget, self, T___MODULE__, PNone.NO_VALUE); if (module != PNone.NO_VALUE) { return module; } 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 f84fc13fd2..5a6129a3b4 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 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -86,7 +86,6 @@ 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.MergedObjectTypeModuleGetAttributeNode; import com.oracle.graal.python.nodes.attributes.ReadAttributeFromModuleNode; import com.oracle.graal.python.nodes.attributes.ReadAttributeFromObjectNode; import com.oracle.graal.python.nodes.attributes.WriteAttributeToObjectNode; @@ -238,7 +237,8 @@ static Object doError(Object self, @SuppressWarnings("unused") Object dict, @GenerateNodeFactory public abstract static class ModuleGetattributeNode extends GetAttrBuiltinNode { /** - * Keep in sync with {@link MergedObjectTypeModuleGetAttributeNode} + * Keep in sync with + * {@link com.oracle.graal.python.nodes.attributes.MergedObjectTypeModuleGetFixedAttributeNode} */ @Specialization static Object getattribute(VirtualFrame frame, PythonModule self, Object keyObj, 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 3840a52b06..266c15d2ba 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 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. + * Copyright (c) 2017, 2026, Oracle and/or its affiliates. * Copyright (c) 2014, Regents of the University of California * * All rights reserved. @@ -119,7 +119,6 @@ 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; @@ -510,7 +509,8 @@ public abstract static class GetAttributeNode extends GetAttrBuiltinNode { * Keep in sync with * {@link com.oracle.graal.python.builtins.objects.type.TypeBuiltins.GetattributeNode} and * {@link com.oracle.graal.python.builtins.objects.thread.ThreadLocalBuiltins.GetAttributeNode} - * and {@link MergedObjectTypeModuleGetAttributeNode} + * and + * {@link com.oracle.graal.python.nodes.attributes.MergedObjectTypeModuleGetFixedAttributeNode} */ @Specialization @SuppressWarnings("truffle-static-method") 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 ddb1db140c..867f8e9652 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 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -55,10 +55,8 @@ import com.oracle.graal.python.util.PythonUtils; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; -import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff; import com.oracle.truffle.api.dsl.Cached; 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.GenerateUncached; @@ -586,30 +584,6 @@ public static int getCodePoint(String characterName) { } } - /** - * Like {@link com.oracle.truffle.api.strings.TruffleString.EqualNode} but with the proper - * {@link InliningCutoff} since {@link com.oracle.truffle.api.strings.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); - } - } - public static int codepointIndexToByteIndex(int codepointIndex) { assert TS_ENCODING == TruffleString.Encoding.UTF_32 : "must be adapted when switching to a different encoding"; return codepointIndex << 2; 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 adc8aa0c17..4942e722f3 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 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -72,7 +72,6 @@ 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; @@ -141,7 +140,8 @@ public abstract static class GetAttributeNode extends GetAttrBuiltinNode { * Keep in sync with * {@link com.oracle.graal.python.builtins.objects.object.ObjectBuiltins.GetAttributeNode} * and {@link com.oracle.graal.python.builtins.objects.type.TypeBuiltins.GetattributeNode} - * and {@link MergedObjectTypeModuleGetAttributeNode} + * and + * {@link com.oracle.graal.python.nodes.attributes.MergedObjectTypeModuleGetFixedAttributeNode} */ @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 b97416bddd..4d0c34e6e8 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 @@ -140,7 +140,6 @@ 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; @@ -537,7 +536,8 @@ public abstract static class GetattributeNode extends GetAttrBuiltinNode { * {@link com.oracle.graal.python.builtins.objects.object.ObjectBuiltins.GetAttributeNode} * and * {@link com.oracle.graal.python.builtins.objects.thread.ThreadLocalBuiltins.GetAttributeNode} - * and {@link MergedObjectTypeModuleGetAttributeNode} + * and + * {@link com.oracle.graal.python.nodes.attributes.MergedObjectTypeModuleGetFixedAttributeNode} */ @Specialization protected Object doIt(VirtualFrame frame, Object object, Object keyObj, 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 505c6954e0..2316f2e283 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 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -215,6 +215,7 @@ static Object callPython(VirtualFrame frame, Node inliningTarget, TpSlotPythonSi return dispatcherNode.execute(frame, inliningTarget, slot.getCallable(), slot.getType(), self, obj, type); } + @InliningCutoff @Specialization static Object callNative(VirtualFrame frame, Node inliningTarget, TpSlotNative slot, Object self, Object obj, Object value, @Cached GetThreadStateNode getThreadStateNode, @@ -298,8 +299,8 @@ protected static Object doCachedPFunction(VirtualFrame frame, Node inliningTarge return invoke.execute(frame, inliningTarget, callNode, cachedCallee, arguments); } - private static Object normalizeNoValue(InlinedConditionProfile profile, Node inlinintTarget, Object o) { - return profile.profile(inlinintTarget, o == PNone.NO_VALUE) ? PNone.NONE : o; + private static Object normalizeNoValue(InlinedConditionProfile profile, Node inliningTarget, Object o) { + return profile.profile(inliningTarget, o == PNone.NO_VALUE) ? PNone.NONE : o; } @Specialization(replaces = "doCachedPFunction") diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotDescrSet.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotDescrSet.java index f2010e0ca9..015f411c02 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotDescrSet.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/slots/TpSlotDescrSet.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -208,6 +208,7 @@ static void callNative(VirtualFrame frame, Node inliningTarget, TpSlotNative slo } } + @InliningCutoff private static PException raiseAttributeError(Node inliningTarget, PRaiseNode raiseNode, TruffleString attrName) { return raiseNode.raise(inliningTarget, PythonBuiltinClassType.AttributeError, attrName); } 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 5f8aa4aa6b..c7d4265fe6 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 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -66,7 +66,7 @@ Object doIt(VirtualFrame frame, Object object, @Bind Node inliningTarget, @Cached GetClassNode getClassNode, @Cached GetCachedTpSlotsNode getSlotsNode, - @Cached MergedObjectTypeModuleGetAttributeInnerNode innerNode) { + @Cached(inline = true) MergedObjectTypeModuleGetFixedAttributeNode innerNode) { Object type = getClassNode.execute(inliningTarget, object); TpSlots slots = getSlotsNode.execute(inliningTarget, type); return innerNode.execute(frame, inliningTarget, object, key, type, slots); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/GetFixedModuleAttributeNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/GetFixedModuleAttributeNode.java new file mode 100644 index 0000000000..900ece1de6 --- /dev/null +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/GetFixedModuleAttributeNode.java @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * 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.attributes.MergedObjectTypeModuleGetFixedAttributeNode.hasNoGetAttr; + +import com.oracle.graal.python.builtins.objects.PNone; +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.type.TpSlots.GetObjectSlotsNode; +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.PNodeWithContext; +import com.oracle.graal.python.nodes.PRaiseNode; +import com.oracle.graal.python.runtime.exception.PException; +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.Specialization; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.profiles.InlinedConditionProfile; +import com.oracle.truffle.api.strings.TruffleString; + +@GenerateInline +@GenerateCached +public abstract class GetFixedModuleAttributeNode extends PNodeWithContext { + + public abstract Object execute(VirtualFrame frame, Node inliningTarget, Object object, TruffleString key, Object type); + + /** + * @see com.oracle.graal.python.builtins.objects.module.ModuleBuiltins.ModuleGetattributeNode + */ + @Specialization + static Object doIt(VirtualFrame frame, Node inliningTarget, Object object, TruffleString key, Object type, + @Cached(value = "create(key)", inline = false) LookupAttributeInMRONode lookup, + @Cached GetObjectSlotsNode getDescrSlotsNode, + @Cached ReadAttributeFromModuleNode readAttributeOfModuleNode, + @Cached InlinedConditionProfile hasDescrProfile, + @Cached InlinedConditionProfile hasDescrGetProfile, + @Cached InlinedConditionProfile hasValueProfile, + @Cached CallSlotDescrGet.Lazy callSlotDescrGet, + @Cached ModuleBuiltins.LazyHandleGetattrExceptionNode handleException, + @Cached PRaiseNode raiseNode) { + assert hasNoGetAttr(type); + + PythonModule module = (PythonModule) object; + try { + Object descr = lookup.execute(type); + boolean hasDescr = hasDescrProfile.profile(inliningTarget, descr != PNone.NO_VALUE); + + TpSlot get = null; + boolean hasDescrGet = false; + boolean getValue = true; + if (hasDescr) { + var descrSlots = getDescrSlotsNode.execute(inliningTarget, descr); + get = descrSlots.tp_descr_get(); + hasDescrGet = hasDescrGetProfile.profile(inliningTarget, get != null); + if (hasDescrGet && TpSlotDescrSet.PyDescr_IsData(descrSlots)) { + // fall through to callSlotDescrGet below to avoid duplicating the call site + getValue = false; + } + } + + if (getValue) { + Object value = readAttributeOfModuleNode.execute(module, key); + if (hasValueProfile.profile(inliningTarget, value != PNone.NO_VALUE)) { + return value; + } + } + + if (hasDescr) { + if (hasDescrGet) { + return callSlotDescrGet.get(inliningTarget).execute(frame, inliningTarget, get, descr, module, type); + } else { + return descr; + } + } + + throw raiseNode.raiseAttributeError(inliningTarget, ErrorMessages.OBJ_P_HAS_NO_ATTR_S, module, key); + } catch (PException e) { + return handleException(frame, inliningTarget, module, key, e, handleException); + } + } + + @InliningCutoff + private static Object handleException(VirtualFrame frame, Node inliningTarget, PythonModule object, TruffleString key, PException e, + ModuleBuiltins.LazyHandleGetattrExceptionNode handleException) { + return handleException.get(inliningTarget).execute(frame, object, key, e); + } +} diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/GetFixedObjectAttributeNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/GetFixedObjectAttributeNode.java new file mode 100644 index 0000000000..e062864482 --- /dev/null +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/GetFixedObjectAttributeNode.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * 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.attributes.MergedObjectTypeModuleGetFixedAttributeNode.hasNoGetAttr; + +import com.oracle.graal.python.builtins.objects.PNone; +import com.oracle.graal.python.builtins.objects.type.TpSlots.GetObjectSlotsNode; +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.PNodeWithContext; +import com.oracle.graal.python.nodes.PRaiseNode; +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.Specialization; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.profiles.InlinedConditionProfile; +import com.oracle.truffle.api.strings.TruffleString; + +@GenerateInline +@GenerateCached +public abstract class GetFixedObjectAttributeNode extends PNodeWithContext { + + public abstract Object execute(VirtualFrame frame, Node inliningTarget, Object object, TruffleString key, Object type); + + /** + * @see com.oracle.graal.python.builtins.objects.object.ObjectBuiltins.GetAttributeNode + */ + @Specialization + static Object doIt(VirtualFrame frame, Node inliningTarget, Object object, TruffleString key, Object type, + @Cached(value = "create(key)", inline = false) LookupAttributeInMRONode lookup, + @Cached GetObjectSlotsNode getDescrSlotsNode, + @Cached ReadAttributeFromObjectNode readAttributeOfObjectNode, + @Cached InlinedConditionProfile hasDescrProfile, + @Cached InlinedConditionProfile hasDescrGetProfile, + @Cached InlinedConditionProfile hasValueProfile, + @Cached CallSlotDescrGet.Lazy callSlotDescrGet, + @Cached PRaiseNode raiseNode) { + assert hasNoGetAttr(type); + + Object descr = lookup.execute(type); + boolean hasDescr = hasDescrProfile.profile(inliningTarget, descr != PNone.NO_VALUE); + + TpSlot get = null; + boolean hasDescrGet = false; + boolean getValue = true; + if (hasDescr) { + var descrSlots = getDescrSlotsNode.execute(inliningTarget, descr); + get = descrSlots.tp_descr_get(); + hasDescrGet = hasDescrGetProfile.profile(inliningTarget, get != null); + if (hasDescrGet && TpSlotDescrSet.PyDescr_IsData(descrSlots)) { + // fall through to callSlotDescrGet below to avoid duplicating the call site + getValue = false; + } + } + + if (getValue) { + Object value = readAttributeOfObjectNode.execute(object, key); + if (hasValueProfile.profile(inliningTarget, value != PNone.NO_VALUE)) { + return value; + } + } + + if (hasDescr) { + if (hasDescrGet) { + return callSlotDescrGet.get(inliningTarget).execute(frame, inliningTarget, get, descr, object, type); + } else { + return descr; + } + } + + throw raiseNode.raiseAttributeError(inliningTarget, ErrorMessages.OBJ_P_HAS_NO_ATTR_S, object, key); + } +} diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/GetFixedTypeAttributeNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/GetFixedTypeAttributeNode.java new file mode 100644 index 0000000000..bc371425e0 --- /dev/null +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/GetFixedTypeAttributeNode.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * 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.attributes.MergedObjectTypeModuleGetFixedAttributeNode.hasNoGetAttr; + +import com.oracle.graal.python.builtins.objects.PNone; +import com.oracle.graal.python.builtins.objects.type.TpSlots.GetObjectSlotsNode; +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.PNodeWithContext; +import com.oracle.graal.python.nodes.PRaiseNode; +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.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; + +@GenerateInline +@GenerateCached +public abstract class GetFixedTypeAttributeNode extends PNodeWithContext { + + public abstract Object execute(VirtualFrame frame, Node inliningTarget, Object object, TruffleString key, Object type); + + /** + * @see com.oracle.graal.python.builtins.objects.module.ModuleBuiltins.ModuleGetattributeNode + */ + @Specialization + static Object doIt(VirtualFrame frame, Node inliningTarget, Object object, TruffleString key, Object type, + @Cached(value = "create(key)", inline = false) LookupAttributeInMRONode lookup, + @Cached(value = "create(key)", inline = false) LookupAttributeInMRONode readAttributeOfClassNode, + @Cached GetObjectSlotsNode getDescrSlotsNode, + @Cached GetObjectSlotsNode getValueSlotsNode, + @Cached InlinedConditionProfile hasDescrProfile, + @Cached InlinedConditionProfile hasDescrGetProfile, + @Cached InlinedConditionProfile hasValueProfile, + @Cached InlinedBranchProfile hasNonDescriptorValueProfile, + @Cached CallSlotDescrGet.Lazy callSlotDescrGet, + @Cached CallSlotDescrGet.Lazy callSlotValueGet, + @Cached PRaiseNode raiseNode) { + assert hasNoGetAttr(type); + + Object descr = lookup.execute(type); + boolean hasDescr = hasDescrProfile.profile(inliningTarget, descr != PNone.NO_VALUE); + + TpSlot get = null; + boolean hasDescrGet = false; + boolean getValue = true; + if (hasDescr) { + var descrSlots = getDescrSlotsNode.execute(inliningTarget, descr); + get = descrSlots.tp_descr_get(); + hasDescrGet = hasDescrGetProfile.profile(inliningTarget, get != null); + if (hasDescrGet && TpSlotDescrSet.PyDescr_IsData(descrSlots)) { + // fall through to callSlotDescrGet below to avoid duplicating the call site + getValue = false; + } + } + + if (getValue) { + Object value = readAttributeOfClassNode.execute(object); + if (hasValueProfile.profile(inliningTarget, value != PNone.NO_VALUE)) { + var valueGet = getValueSlotsNode.execute(inliningTarget, value).tp_descr_get(); + if (valueGet == null) { + hasNonDescriptorValueProfile.enter(inliningTarget); + return value; + } else { + return callSlotValueGet.get(inliningTarget).execute(frame, inliningTarget, valueGet, value, PNone.NO_VALUE, object); + } + } + } + + if (hasDescr) { + if (hasDescrGet) { + return callSlotDescrGet.get(inliningTarget).execute(frame, inliningTarget, get, descr, object, type); + } else { + return descr; + } + } + + throw raiseNode.raiseAttributeError(inliningTarget, ErrorMessages.TYPE_N_HAS_NO_ATTR, object, key); + } +} 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 5f73173525..93885bcf16 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,7 +44,6 @@ 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; @@ -56,6 +55,7 @@ import com.oracle.graal.python.runtime.PythonOptions; import com.oracle.graal.python.runtime.exception.StacktracelessCheckedException; import com.oracle.graal.python.runtime.sequence.storage.MroSequenceStorage; +import com.oracle.graal.python.util.PythonUtils; import com.oracle.truffle.api.Assumption; import com.oracle.truffle.api.CompilerAsserts; import com.oracle.truffle.api.CompilerDirectives; @@ -93,16 +93,17 @@ public final Object execute(Object klass) { protected abstract Object executeInternal(Object klass) throws MROChangedException; + @ImportStatic(PythonUtils.class) @GenerateUncached - @GenerateInline(false) // footprint reduction 36 -> 17 + @GenerateInline(false) public abstract static class Dynamic extends PNodeWithContext { public abstract Object execute(Object klass, TruffleString key); - @Specialization(guards = "equalNode.execute(inliningTarget, key, cachedKey)", limit = "2") + @Specialization(guards = "equalNode.execute(key, cachedKey, TS_ENCODING)", limit = "2") static Object lookupConstantMROEquals(Object klass, TruffleString key, @Bind Node inliningTarget, @Cached("key") TruffleString cachedKey, - @Cached @Shared StringUtils.EqualNode equalNode, + @Cached @Shared TruffleString.EqualNode equalNode, @Cached("create(cachedKey)") LookupAttributeInMRONode lookup) { return lookup.execute(klass); } @@ -113,7 +114,7 @@ static Object lookupConstantMROEquals(Object klass, TruffleString key, static Object lookupGeneric(Object klass, TruffleString key, @Bind Node inliningTarget, @Cached InlinedConditionProfile pbctProfile, - @Cached ReadAttributeFromPythonObjectNode readPBCTAttrNode, + @Cached(inline = false) ReadAttributeFromPythonObjectNode readPBCTAttrNode, @Cached GetMroStorageNode getMroNode, @Cached ReadAttributeFromObjectNode readAttrNode) { if (pbctProfile.profile(inliningTarget, klass instanceof PythonBuiltinClassType)) { @@ -286,7 +287,7 @@ static PythonBuiltinClassType findOwnerInMro(Python3Core core, PythonBuiltinClas Object lookupPBCTCachedOwner(PythonBuiltinClassType klass, TruffleString key, boolean skipNonStaticBases, @Cached("klass") PythonBuiltinClassType cachedKlass, @Cached("findOwnerInMro(getContext(), cachedKlass, key)") PythonBuiltinClassType ownerKlass, - @Shared @Cached ReadAttributeFromPythonObjectNode readAttrNode) { + @Shared @Cached(inline = false) ReadAttributeFromPythonObjectNode readAttrNode) { if (ownerKlass == null) { return PNone.NO_VALUE; } else { @@ -296,7 +297,7 @@ Object lookupPBCTCachedOwner(PythonBuiltinClassType klass, TruffleString key, bo @Specialization(replaces = "lookupPBCTCachedOwner") Object lookupPBCTGeneric(PythonBuiltinClassType klass, TruffleString key, boolean skipNonStaticBases, - @Shared @Cached ReadAttributeFromPythonObjectNode readAttrNode) { + @Shared @Cached(inline = false) ReadAttributeFromPythonObjectNode readAttrNode) { return findAttr(PythonContext.get(this), klass, key, readAttrNode); } 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/MergedObjectTypeModuleGetFixedAttributeNode.java similarity index 71% rename from graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/MergedObjectTypeModuleGetAttributeNode.java rename to graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/attributes/MergedObjectTypeModuleGetFixedAttributeNode.java index 233ed8654a..d74a71d49c 100644 --- 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/MergedObjectTypeModuleGetFixedAttributeNode.java @@ -47,9 +47,7 @@ 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.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; @@ -59,14 +57,12 @@ 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; @@ -80,32 +76,14 @@ * {@link com.oracle.graal.python.builtins.objects.object.ObjectBuiltins.GetAttributeNode}, * {@link com.oracle.graal.python.builtins.objects.type.TypeBuiltins.GetattributeNode} and * {@link com.oracle.graal.python.builtins.objects.module.ModuleBuiltins.ModuleGetattributeNode} to - * reduce code size by about 3x for host inlining + * reduce code size for host inlining. + * + * Fixed-key version used by {@link GetFixedAttributeNode}. The key is passed via execute for + * inlining, but the caller is expected to keep its identity stable for the lifetime of the node. */ -@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 { +@GenerateCached +public abstract class MergedObjectTypeModuleGetFixedAttributeNode extends PNodeWithContext { public abstract Object execute(VirtualFrame frame, Node inliningTarget, Object object, TruffleString key, Object type, TpSlots slots); @@ -122,7 +100,7 @@ static Object doIt(VirtualFrame frame, Node inliningTarget, Object object, Truff @Cached("slots.tp_getattro()") TpSlot cachedSlot, // Common @Cached GetObjectSlotsNode getDescrSlotsNode, - @Cached LookupAttributeInMRONode.Dynamic lookup, + @Cached(value = "create(key)", inline = false) LookupAttributeInMRONode lookup, @Cached InlinedConditionProfile hasDescProfile, @Cached InlinedConditionProfile hasDescrGetProfile, @Cached InlinedConditionProfile hasValueProfile, @@ -130,69 +108,78 @@ static Object doIt(VirtualFrame frame, Node inliningTarget, Object object, Truff @Cached CallSlotDescrGet.Lazy callSlotDescrGet, // Specific to a given tp_getattro, some should probably be lazy @Cached ReadAttributeFromObjectNode readAttributeOfObjectNode, - @Cached LookupAttributeInMRONode.Dynamic readAttributeOfClassNode, + @Cached(value = "create(key)", inline = false) LookupAttributeInMRONode readAttributeOfClassNode, @Cached GetObjectSlotsNode getValueSlotsNode, @Cached InlinedBranchProfile hasNonDescriptorValueProfile, @Cached CallSlotDescrGet.Lazy callSlotValueGet, @Cached ModuleBuiltins.LazyHandleGetattrExceptionNode handleException) { - assert hasNoGetAttr(object); + assert hasNoGetAttr(type); try { - Object descr = lookup.execute(type, key); + Object descr = lookup.execute(type); boolean hasDescr = hasDescProfile.profile(inliningTarget, descr != PNone.NO_VALUE); TpSlot get = null; boolean hasDescrGet = false; + boolean getValue = true; if (hasDescr) { 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); + // fall through to callSlotDescrGet below to avoid duplicating the call site + getValue = false; } } - // 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 (hasValueProfile.profile(inliningTarget, value != PNone.NO_VALUE)) { - return value; - } - } else { - // TypeBuiltins.SLOTS.tp_getattro() - value = readAttributeOfClassNode.execute(object, key); - if (hasValueProfile.profile(inliningTarget, value != PNone.NO_VALUE)) { - var valueGet = getValueSlotsNode.execute(inliningTarget, value).tp_descr_get(); - if (valueGet == null) { - hasNonDescriptorValueProfile.enter(inliningTarget); + if (getValue) { + // The main difference between all 3 nodes + if (cachedSlot != TypeBuiltins.SLOTS.tp_getattro()) { + // ObjectBuiltins.SLOTS.tp_getattro() || ModuleBuiltins.SLOTS.tp_getattro() + Object value = readAttributeOfObjectNode.execute(object, key); + if (hasValueProfile.profile(inliningTarget, value != PNone.NO_VALUE)) { return value; - } else { - return callSlotValueGet.get(inliningTarget).executeCached(frame, valueGet, value, PNone.NO_VALUE, object); + } + } else { + // TypeBuiltins.SLOTS.tp_getattro() + Object value = readAttributeOfClassNode.execute(object); + if (hasValueProfile.profile(inliningTarget, value != PNone.NO_VALUE)) { + 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) { - if (!hasDescrGet) { - return descr; - } else { + if (hasDescrGet) { return callSlotDescrGet.get(inliningTarget).executeCached(frame, get, descr, object, type); + } else { + return descr; } } - TruffleString errorMessage = cachedSlot != TypeBuiltins.SLOTS.tp_getattro() ? ErrorMessages.OBJ_P_HAS_NO_ATTR_S : ErrorMessages.TYPE_N_HAS_NO_ATTR; + TruffleString errorMessage = cachedSlot == TypeBuiltins.SLOTS.tp_getattro() ? ErrorMessages.TYPE_N_HAS_NO_ATTR : ErrorMessages.OBJ_P_HAS_NO_ATTR_S; throw raiseNode.raiseAttributeError(inliningTarget, errorMessage, 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); + return handleModuleException(frame, inliningTarget, object, key, e, handleException); } else { throw e; } } } + @InliningCutoff + private static Object handleModuleException(VirtualFrame frame, Node inliningTarget, Object object, TruffleString key, PException e, + ModuleBuiltins.LazyHandleGetattrExceptionNode handleException) { + return handleException.get(inliningTarget).execute(frame, (PythonModule) object, key, e); + } + @InliningCutoff @Specialization(replaces = "doIt") static Object doGeneric(VirtualFrame frame, Node inliningTarget, Object object, TruffleString key, @SuppressWarnings("unused") Object type, TpSlots slots, @@ -210,8 +197,8 @@ 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) { + static boolean hasNoGetAttr(Object type) { CompilerAsserts.neverPartOfCompilation("only used in asserts"); - return LookupAttributeInMRONode.Dynamic.getUncached().execute(GetClassNode.executeUncached(obj), T___GETATTR__) == PNone.NO_VALUE; + return LookupAttributeInMRONode.Dynamic.getUncached().execute(type, T___GETATTR__) == PNone.NO_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 index bffff3949e..522ef0f9fd 100644 --- 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 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -41,7 +41,7 @@ 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.common.HashingStorageNodes.HashingStorageGetItemStringKey; import com.oracle.graal.python.builtins.objects.module.PythonModule; import com.oracle.graal.python.nodes.PNodeWithContext; import com.oracle.graal.python.nodes.object.GetDictIfExistsNode; @@ -74,7 +74,7 @@ public static ReadAttributeFromModuleNode getUncached() { static Object readModuleAttribute(PythonModule object, TruffleString key, @Bind Node inliningTarget, @Cached GetDictIfExistsNode getDict, - @Cached HashingStorageGetItem getItem) { + @Cached HashingStorageGetItemStringKey getItem) { var dict = getDict.execute(object); Object value = getItem.execute(inliningTarget, dict.getDictStorage(), key); if (value == null) { 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 ea138dbaf4..5c1a3f8c1a 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 @@ -42,15 +42,14 @@ import com.oracle.graal.python.builtins.objects.PNone; import com.oracle.graal.python.builtins.objects.cext.PythonAbstractNativeObject; -import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageGetItem; +import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageGetItemStringKey; import com.oracle.graal.python.builtins.objects.dict.PDict; import com.oracle.graal.python.builtins.objects.object.PythonObject; import com.oracle.graal.python.nodes.PNodeWithContext; import com.oracle.graal.python.nodes.object.GetDictIfExistsNode; -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; import com.oracle.truffle.api.dsl.NeverDefault; @@ -65,7 +64,7 @@ */ @ReportPolymorphism @GenerateUncached -@GenerateInline(false) // footprint reduction 64 -> 47 +@GenerateInline(false) public abstract class ReadAttributeFromObjectNode extends PNodeWithContext { @NeverDefault @@ -87,13 +86,13 @@ public static ReadAttributeFromObjectNode getUncached() { @Specialization static Object readObjectAttribute(PythonObject object, TruffleString key, @Bind Node inliningTarget, - @Cached InlinedConditionProfile profileHasDict, - @Exclusive @Cached GetDictIfExistsNode getDict, - @Cached ReadAttributeFromPythonObjectNode readAttributeFromPythonObjectNode, - @Exclusive @Cached HashingStorageGetItem getItem) { + @Shared @Cached InlinedConditionProfile profileHasDict, + @Shared @Cached GetDictIfExistsNode getDict, + @Shared @Cached(inline = true) ReadAttributeFromPythonObjectNode readAttributeFromPythonObjectNode, + @Shared @Cached HashingStorageGetItemStringKey getItem) { var dict = getDict.execute(object); if (profileHasDict.profile(inliningTarget, dict == null)) { - return readAttributeFromPythonObjectNode.execute(object, key); + return readAttributeFromPythonObjectNode.execute(inliningTarget, object, key); } else { Object value = getItem.execute(inliningTarget, dict.getDictStorage(), key); if (value == null) { @@ -107,11 +106,11 @@ static Object readObjectAttribute(PythonObject object, TruffleString key, @Specialization static Object readNativeObject(PythonAbstractNativeObject object, TruffleString key, @Bind Node inliningTarget, - @Exclusive @Cached GetDictIfExistsNode getDict, - @Exclusive @Cached HashingStorageGetItem getItem) { + @Shared @Cached GetDictIfExistsNode getDict, + @Shared @Cached HashingStorageGetItemStringKey getItem) { PDict dict = getDict.execute(object); if (dict != null) { - Object result = getItem.execute(null, inliningTarget, dict.getDictStorage(), key); + Object result = getItem.execute(inliningTarget, dict.getDictStorage(), key); if (result != null) { return result; } @@ -120,7 +119,6 @@ static Object readNativeObject(PythonAbstractNativeObject object, TruffleString } // foreign object or primitive - @InliningCutoff @Specialization(guards = {"!isPythonObject(object)", "!isNativeObject(object)"}) static Object readForeignOrPrimitive(Object object, TruffleString key) { // Foreign members are tried after the regular attribute lookup, see 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 4e71c58b60..528520e27b 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 @@ -45,18 +45,15 @@ import com.oracle.graal.python.builtins.objects.object.PythonObject; import com.oracle.graal.python.builtins.objects.type.PythonManagedClass; import com.oracle.graal.python.nodes.PNodeWithContext; -import com.oracle.graal.python.util.PythonUtils; 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.NeverDefault; -import com.oracle.truffle.api.dsl.NonIdempotent; import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.object.DynamicObject; -import com.oracle.truffle.api.object.Location; -import com.oracle.truffle.api.object.Property; -import com.oracle.truffle.api.object.Shape; import com.oracle.truffle.api.strings.TruffleString; /** @@ -64,7 +61,7 @@ * object has dict, also bypasses any other additional logic in {@link ReadAttributeFromObjectNode}. */ @GenerateUncached -@GenerateInline(false) // footprint reduction 44 -> 25 +@GenerateInline public abstract class ReadAttributeFromPythonObjectNode extends PNodeWithContext { @NeverDefault public static ReadAttributeFromPythonObjectNode create() { @@ -76,87 +73,65 @@ public static ReadAttributeFromPythonObjectNode getUncached() { } public static Object executeUncached(PythonObject object, TruffleString key, Object defaultValue) { - return getUncached().execute(object, key, defaultValue); + return getUncached().execute(getUncached(), object, key, defaultValue); } - public abstract Object execute(PythonObject object, TruffleString key, Object defaultValue); + public final Object execute(PythonObject object, TruffleString key, Object defaultValue) { + return execute(this, object, key, defaultValue); + } public final Object execute(PythonObject object, TruffleString key) { - return execute(object, key, PNone.NO_VALUE); + return execute(this, object, key, PNone.NO_VALUE); } // used only by DynamicObjectStorage, which will be removed during the transition from // DynamicObject to ObjectHashMap - public abstract Object execute(DynamicObject object, TruffleString key, Object defaultValue); - - protected static Object getAttribute(DynamicObject object, TruffleString key, Object defaultValue) { - return DynamicObject.GetNode.getUncached().execute(object, key, defaultValue); + public final Object execute(DynamicObject object, TruffleString key, Object defaultValue) { + return execute(this, object, key, defaultValue); } - @Idempotent - protected static boolean isLongLivedObject(DynamicObject object) { - return object instanceof PythonModule || object instanceof PythonManagedClass; - } + public abstract Object execute(Node inliningTarget, PythonObject object, TruffleString key, Object defaultValue); - @Idempotent - protected static boolean isPrimitive(Object value) { - return PythonUtils.isPrimitive(value); + public final Object execute(Node inliningTarget, PythonObject object, TruffleString key) { + return execute(inliningTarget, object, key, PNone.NO_VALUE); } - @NonIdempotent - protected static boolean locationIsAssumedFinal(Location loc) { - return loc != null && loc.isAssumedFinal(); - } + public abstract Object execute(Node inliningTarget, DynamicObject object, TruffleString key, Object defaultValue); - protected static Location getLocationOrNull(Property prop) { - return prop == null ? null : prop.getLocation(); + @Specialization + protected static Object read(Node inliningTarget, DynamicObject dynamicObject, TruffleString key, Object defaultValue, + @Cached ReceiverCast receiverCast, + @Cached DynamicObject.GetNode getNode) { + return getNode.execute(receiverCast.execute(inliningTarget, dynamicObject), key, defaultValue); } - @SuppressWarnings("unused") - @Specialization(limit = "1", // - guards = { - "isSingleContext()", - "dynamicObject == cachedObject", - "isLongLivedObject(cachedObject)", - "key == cachedKey", - "dynamicObject.getShape() == cachedShape", - "locationIsAssumedFinal(loc)", - "!isPrimitive(value)" - }, // - assumptions = {"cachedShape.getValidAssumption()", "loc.getFinalAssumption()"}) - protected static Object readFinalAttr(DynamicObject dynamicObject, TruffleString key, Object defaultValue, - @Cached("key") TruffleString cachedKey, - @Cached(value = "dynamicObject", weak = true) DynamicObject cachedObject, - @Cached("dynamicObject.getShape()") Shape cachedShape, - @Cached("getLocationOrNull(cachedShape.getProperty(cachedKey))") Location loc, - @Cached(value = "getAttribute(dynamicObject, key, defaultValue)", weak = true) Object value) { - return value; - } + @GenerateCached(false) + @GenerateUncached + @GenerateInline + protected abstract static class ReceiverCast extends PNodeWithContext { - @SuppressWarnings("unused") - @Specialization(limit = "1", // - guards = { - "isSingleContext()", - "dynamicObject == cachedObject", - "isLongLivedObject(cachedObject)", - "key == cachedKey", - "dynamicObject.getShape() == cachedShape", - "locationIsAssumedFinal(loc)", - "isPrimitive(value)" - }, // - assumptions = {"cachedShape.getValidAssumption()", "loc.getFinalAssumption()"}) - protected static Object readFinalPrimitiveAttr(DynamicObject dynamicObject, TruffleString key, Object defaultValue, - @Cached("key") TruffleString cachedKey, - @Cached(value = "dynamicObject", weak = true) DynamicObject cachedObject, - @Cached("dynamicObject.getShape()") Shape cachedShape, - @Cached("getLocationOrNull(cachedShape.getProperty(cachedKey))") Location loc, - @Cached(value = "getAttribute(dynamicObject, key, defaultValue)") Object value) { - return value; - } + protected abstract DynamicObject execute(Node inliningTarget, DynamicObject object); - @Specialization(replaces = {"readFinalAttr", "readFinalPrimitiveAttr"}) - protected static Object readDirect(DynamicObject dynamicObject, TruffleString key, Object defaultValue, - @Cached DynamicObject.GetNode getNode) { - return getNode.execute(dynamicObject, key, defaultValue); + @Idempotent + protected static boolean isLongLivedObject(DynamicObject object) { + return object instanceof PythonModule || object instanceof PythonManagedClass; + } + + @SuppressWarnings("unused") + @Specialization(limit = "1", // + guards = { + "isSingleContext()", + "dynamicObject == cachedObject", + "isLongLivedObject(cachedObject)", + }) + protected static DynamicObject readFinalAttr(DynamicObject dynamicObject, + @Cached(value = "dynamicObject", weak = true) DynamicObject cachedObject) { + return cachedObject; + } + + @Specialization(replaces = {"readFinalAttr"}) + protected static DynamicObject readDirect(DynamicObject dynamicObject) { + return dynamicObject; + } } } 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 45c74cfa8b..9ebc46b456 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 @@ -103,12 +103,16 @@ import com.oracle.graal.python.builtins.objects.iterator.PLongSequenceIterator; import com.oracle.graal.python.builtins.objects.iterator.PObjectSequenceIterator; import com.oracle.graal.python.builtins.objects.list.PList; +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.set.PFrozenSet; import com.oracle.graal.python.builtins.objects.set.PSet; import com.oracle.graal.python.builtins.objects.set.SetNodes; 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.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.TpSlotIterNext.CallSlotTpIterNextNode; import com.oracle.graal.python.builtins.objects.typing.PTypeAliasType; import com.oracle.graal.python.compiler.CodeUnit; @@ -179,7 +183,10 @@ 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.GetFixedAttributeNode; +import com.oracle.graal.python.nodes.attributes.GetFixedModuleAttributeNode; +import com.oracle.graal.python.nodes.attributes.GetFixedObjectAttributeNode; +import com.oracle.graal.python.nodes.attributes.GetFixedTypeAttributeNode; +import com.oracle.graal.python.nodes.attributes.MergedObjectTypeModuleGetFixedAttributeNode; import com.oracle.graal.python.nodes.attributes.ReadAttributeFromPythonObjectNode; import com.oracle.graal.python.nodes.builtins.ListNodes; import com.oracle.graal.python.nodes.bytecode.CopyDictWithoutKeysNode; @@ -496,7 +503,7 @@ private InstrumentationData getInstrumentationData(VirtualFrame frame, BytecodeN return current; } - private void resetInstrumenationData(VirtualFrame frame, BytecodeNode bytecode) { + private void resetInstrumentationData(VirtualFrame frame, BytecodeNode bytecode) { InstrumentationData current = (InstrumentationData) bytecode.getLocalValue(0, frame, instrumentationDataIndex); if (current == null) { current = new InstrumentationData(); @@ -691,6 +698,7 @@ private void invokeTraceFunction(VirtualFrame virtualFrame, BytecodeNode locatio } } + @InliningCutoff private void traceOrProfileCall(VirtualFrame frame, BytecodeNode bytecode, int bci) { PythonThreadState threadState = getThreadState(); Object traceFun = threadState.getTraceFun(); @@ -758,6 +766,7 @@ private void traceLineAtLoopHeader(VirtualFrame frame, BytecodeNode location, in } } + @InliningCutoff private void traceOrProfileReturn(VirtualFrame frame, BytecodeNode location, Object value) { PythonThreadState threadState = getThreadState(); Object traceFun = threadState.getTraceFun(); @@ -1172,7 +1181,7 @@ public static Object doObject(VirtualFrame frame, Object generator, @Bind PBytecodeDSLRootNode root, @Bind BytecodeNode bytecode, @Bind("$bytecodeIndex") int bci) { - root.resetInstrumenationData(frame, bytecode); + root.resetInstrumentationData(frame, bytecode); root.traceOrProfileCall(frame, bytecode, bci); return generator; } @@ -1257,7 +1266,7 @@ static Object readFromLocalsFastPath(VirtualFrame frame, TruffleString attribute @ForceQuickening @Specialization(guards = "!isNoValue(result)", limit = "1") public static Object doLocalFastPath(VirtualFrame frame, TruffleString name, - @Cached ReadAttributeFromPythonObjectNode readAttrNode, + @Cached(inline = false) ReadAttributeFromPythonObjectNode readAttrNode, @Bind("readFromLocalsFastPath(frame, name, readAttrNode)") Object result) { return result; } @@ -1655,19 +1664,79 @@ public static Object doIt(VirtualFrame frame, @Operation(storeBytecodeIndex = true) @ConstantOperand(type = TruffleString.class) public static final class GetAttribute { - @Specialization(excludeForUncached = true) + 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(); + } + + @ForceQuickening + @Specialization(guards = "isObjectGetAttribute(slots)", excludeForUncached = true) + public static Object doObject(VirtualFrame frame, + TruffleString name, + Object obj, + @Bind Node inliningTarget, + @Shared @Cached GetClassNode getClassNode, + @Bind("getClassNode.execute(inliningTarget, obj)") Object type, + @Shared @Cached GetCachedTpSlotsNode getSlotsNode, + @Bind("getSlotsNode.execute(inliningTarget, type)") TpSlots slots, + @Shared @Cached(inline = false) GetFixedObjectAttributeNode getObjectAttributeNode) { + return getObjectAttributeNode.execute(frame, inliningTarget, obj, name, type); + } + + @ForceQuickening + @Specialization(guards = "isModuleGetAttribute(slots)", excludeForUncached = true) + public static Object doModule(VirtualFrame frame, + TruffleString name, + Object obj, + @Bind Node inliningTarget, + @Shared @Cached GetClassNode getClassNode, + @Bind("getClassNode.execute(inliningTarget, obj)") Object type, + @Shared @Cached GetCachedTpSlotsNode getSlotsNode, + @Bind("getSlotsNode.execute(inliningTarget, type)") TpSlots slots, + @Shared @Cached(inline = false) GetFixedModuleAttributeNode getModuleAttributeNode) { + return getModuleAttributeNode.execute(frame, inliningTarget, obj, name, type); + } + + @ForceQuickening + @Specialization(guards = "isTypeGetAttribute(slots)", excludeForUncached = true) + public static Object doType(VirtualFrame frame, + TruffleString name, + Object obj, + @Bind Node inliningTarget, + @Shared @Cached GetClassNode getClassNode, + @Bind("getClassNode.execute(inliningTarget, obj)") Object type, + @Shared @Cached GetCachedTpSlotsNode getSlotsNode, + @Bind("getSlotsNode.execute(inliningTarget, type)") TpSlots slots, + @Shared @Cached(inline = false) GetFixedTypeAttributeNode getTypeAttributeNode) { + return getTypeAttributeNode.execute(frame, inliningTarget, obj, name, type); + } + + @Specialization(replaces = {"doObject", "doModule", "doType"}, excludeForUncached = true) public static Object doIt(VirtualFrame frame, TruffleString name, Object obj, - @Cached("create(name)") GetFixedAttributeNode getAttributeNode) { - return getAttributeNode.execute(frame, obj); + @Bind Node inliningTarget, + @Shared @Cached GetClassNode getClassNode, + @Shared @Cached GetCachedTpSlotsNode getSlotsNode, + @Shared @Cached(inline = false) MergedObjectTypeModuleGetFixedAttributeNode getAttributeNode) { + Object type = getClassNode.execute(inliningTarget, obj); + TpSlots slots = getSlotsNode.execute(inliningTarget, type); + return getAttributeNode.execute(frame, inliningTarget, obj, name, type, slots); } @Specialization(replaces = "doIt") @InliningCutoff public static Object doItUncached(VirtualFrame frame, TruffleString name, Object obj, + @Bind Node inliningTarget, @Cached PyObjectGetAttr dummyToForceStoreBCI) { - return PyObjectGetAttr.getUncached().execute(frame, null, obj, name); + return PyObjectGetAttr.getUncached().execute(frame, inliningTarget, obj, name); } } @@ -3486,7 +3555,7 @@ public static Object doObject(VirtualFrame frame, Object sendValue, @Bind PBytecodeDSLRootNode root, @Bind BytecodeNode bytecode, @Bind("$bytecodeIndex") int bci) { - root.resetInstrumenationData(frame, bytecode); + root.resetInstrumentationData(frame, bytecode); root.traceOrProfileCall(frame, bytecode, bci); return sendValue; } 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 b50355146c..3234ff7a8d 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 @@ -126,7 +126,7 @@ public static Object readFastFromGlobalStore(VirtualFrame frame, TruffleString n @ForceQuickening @Specialization(guards = "!isNoValue(result)", replaces = "readBuiltinFastPath", excludeForUncached = true, limit = "1") public static Object readGlobalFastPath(VirtualFrame frame, TruffleString attributeId, - @Cached ReadAttributeFromPythonObjectNode readNode, + @Cached(inline = false) ReadAttributeFromPythonObjectNode readNode, @Bind("readFastFromGlobalStore(frame, attributeId, readNode)") Object result) { return result; } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/statement/AbstractImportNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/statement/AbstractImportNode.java index 7bc12b1cfc..212c1cde89 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/statement/AbstractImportNode.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/statement/AbstractImportNode.java @@ -184,19 +184,19 @@ protected final Object importModule(VirtualFrame frame, TruffleString name, Obje * what it's set to in the frame and globals. */ @GenerateUncached - @GenerateInline(false) // footprint reduction 48 -> 29 + @GenerateInline(false) public abstract static class ImportName extends Node { public abstract Object execute(Frame frame, PythonContext context, PythonModule builtins, TruffleString name, Object globals, TruffleString[] fromList, int level); @Specialization static Object importName(VirtualFrame frame, PythonContext context, PythonModule builtins, TruffleString name, Object globals, TruffleString[] fromList, int level, - @Cached ReadAttributeFromPythonObjectNode readAttrNode, @Bind Node inliningTarget, + @Cached(inline = true) ReadAttributeFromPythonObjectNode readAttrNode, @Cached InlinedConditionProfile importFuncProfile, @Cached PConstructAndRaiseNode.Lazy raiseNode, @Cached CallNode importCallNode, @Cached PyImportImportModuleLevelObject importModuleLevel) { - Object importFunc = readAttrNode.execute(builtins, T___IMPORT__, null); + Object importFunc = readAttrNode.execute(inliningTarget, builtins, T___IMPORT__, null); if (importFunc == null) { throw raiseNode.get(inliningTarget).raiseImportError(frame, IMPORT_NOT_FOUND); }