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 4fb4a32a03..078dc71c25 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 @@ -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 @@ -139,7 +139,6 @@ abstract static class GraalPyPrivate_StructSequence_NewType extends CApiQuaterna Object doGeneric(TruffleString typeName, TruffleString typeDoc, Object fields, int nInSequence, @Cached GraalPyPrivate_StructSequence_InitType2 initNode, @Cached ReadAttributeFromModuleNode readTypeBuiltinNode, - @Cached DynamicObject.GetShapeFlagsNode getShapeFlagsNode, @Cached DynamicObject.SetShapeFlagsNode setShapeFlagsNode, @Cached CallNode callTypeNewNode, @Bind PythonLanguage language) { @@ -149,7 +148,7 @@ Object doGeneric(TruffleString typeName, TruffleString typeDoc, Object fields, i Object cls = callTypeNewNode.executeWithoutFrame(typeBuiltin, typeName, bases, namespace); initNode.execute(cls, fields, nInSequence); if (cls instanceof PythonClass pythonClass) { - pythonClass.makeStaticBase(getShapeFlagsNode, setShapeFlagsNode); + pythonClass.makeStaticBase(setShapeFlagsNode); } return cls; } 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 a7731d67cf..b61ca62fcd 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 @@ -144,12 +144,4 @@ public String toString() { public static int getCallSiteInlineCacheMaxDepth() { return PythonOptions.getCallSiteInlineCacheMaxDepth(); } - - public final void addShapeFlag(int flag, DynamicObject.GetShapeFlagsNode getShapeFlagsNode, DynamicObject.SetShapeFlagsNode setShapeFlagsNode) { - int oldFlags = getShapeFlagsNode.execute(this); - int newFlags = oldFlags | flag; - if (newFlags != oldFlags) { - setShapeFlagsNode.execute(this, newFlags); - } - } } 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 730884dd00..1838052635 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 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2025, Oracle and/or its affiliates. + * Copyright (c) 2017, 2026, Oracle and/or its affiliates. * Copyright (c) 2013, Regents of the University of California * * All rights reserved. @@ -217,8 +217,8 @@ public void setDictHiddenProp(Node inliningTarget, HiddenAttr.WriteNode writeNod } } - public void makeStaticBase(DynamicObject.GetShapeFlagsNode getShapeFlagsNode, DynamicObject.SetShapeFlagsNode setShapeFlagsNode) { - addShapeFlag(IS_STATIC_BASE, getShapeFlagsNode, setShapeFlagsNode); + public void makeStaticBase(DynamicObject.SetShapeFlagsNode setShapeFlagsNode) { + setShapeFlagsNode.executeAdd(this, IS_STATIC_BASE); } public boolean isStaticBase() { 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 1f8d100762..d340d9bc39 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 @@ -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 @@ -233,7 +233,9 @@ import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.interop.InteropLibrary; import com.oracle.truffle.api.interop.UnsupportedMessageException; +import com.oracle.truffle.api.nodes.DenyReplace; import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.nodes.UnadoptableNode; import com.oracle.truffle.api.object.DynamicObject; import com.oracle.truffle.api.profiles.InlinedBranchProfile; import com.oracle.truffle.api.strings.TruffleString; @@ -2024,8 +2026,9 @@ static TpSlots doOtherCached(Node inliningTarget, Object klass, return weakValueProfile.execute(inliningTarget, getSlots.execute(inliningTarget, klass)); } + @DenyReplace @GenerateCached(false) - private static final class Uncached extends GetCachedTpSlotsNode { + private static final class Uncached extends GetCachedTpSlotsNode implements UnadoptableNode { private static final Uncached INSTANCE = new Uncached(); @Override 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 7cfa1ce2b6..944f037bfa 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 @@ -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 @@ -205,6 +205,7 @@ import com.oracle.graal.python.nodes.object.GetOrCreateDictNode; import com.oracle.graal.python.nodes.util.CannotCastException; import com.oracle.graal.python.nodes.util.CastToTruffleStringNode; +import com.oracle.graal.python.nodes.util.LazyInteropLibrary; import com.oracle.graal.python.runtime.ExecutionContext.BoundaryCallContext; import com.oracle.graal.python.runtime.IndirectCallData.BoundaryCallData; import com.oracle.graal.python.runtime.PythonContext; @@ -1510,10 +1511,15 @@ static boolean doNative(PythonAbstractNativeObject left, PythonAbstractNativeObj return lib.isIdentical(left.getPtr(), right.getPtr(), lib); } - @Specialization(guards = {"isForeignObject(left)", "isForeignObject(right)"}) + @Fallback @InliningCutoff - static boolean doOther(Object left, Object right, - @CachedLibrary(limit = "2") InteropLibrary lib) { + static boolean doOther(Node inliningTarget, Object left, Object right, + @Cached LazyInteropLibrary lazyInterop) { + if (!(PGuards.isForeignObject(left) && PGuards.isForeignObject(right))) { + return false; + } + + InteropLibrary lib = lazyInterop.get(inliningTarget); if (lib.isMetaObject(left) && lib.isMetaObject(right)) { if (left == right) { return true; @@ -1540,11 +1546,6 @@ static boolean doOther(Object left, Object right, } return false; } - - @Fallback - static boolean doOther(@SuppressWarnings("unused") Object left, @SuppressWarnings("unused") Object right) { - return false; - } } @GenerateUncached 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 6921d55b81..d5eb259251 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 @@ -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 @@ -94,7 +94,6 @@ @GenerateUncached @GenerateInline(inlineByDefault = true) @GenerateCached -@ImportStatic({SpecialMethodNames.class, PGuards.class}) public abstract class PyObjectLookupAttr extends Node { public static Object executeUncached(Object receiver, TruffleString name) { return PyObjectLookupAttrNodeGen.getUncached().execute(null, null, receiver, name); @@ -106,187 +105,198 @@ 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) { - CompilerAsserts.neverPartOfCompilation("only used in asserts"); - return LookupAttributeInMRONode.Dynamic.getUncached().execute(lazyClass, T___GETATTR__) == PNone.NO_VALUE; + @Specialization + static Object doIt(VirtualFrame frame, Node inliningTarget, Object receiver, TruffleString name, + @Cached Inner inner, + @Cached GetClassNode getClassNode) { + Object type = getClassNode.execute(inliningTarget, receiver); + return inner.execute(frame, inliningTarget, receiver, name, type); } - protected static boolean getAttributeIs(Node inliningTarget, GetCachedTpSlotsNode getSlotsNode, Object lazyClass, TpSlot slot) { - TpSlots slots = getSlotsNode.execute(inliningTarget, lazyClass); - return slots.tp_getattro() == slot; - } + /** + * Implementation of {@link PyObjectLookupAttr} with the receiver class already resolved. + */ + @GenerateUncached + @GenerateInline + @GenerateCached(false) + @ImportStatic({SpecialMethodNames.class, PGuards.class}) + abstract static class Inner extends Node { - protected static boolean isObjectGetAttribute(Node inliningTarget, GetCachedTpSlotsNode getSlotsNode, Object lazyClass) { - return getAttributeIs(inliningTarget, getSlotsNode, lazyClass, ObjectBuiltins.SLOTS.tp_getattro()); - } + abstract Object execute(Frame frame, Node inliningTarget, Object receiver, TruffleString name, Object type); - protected static boolean isModuleGetAttribute(Node inliningTarget, GetCachedTpSlotsNode getSlotsNode, Object lazyClass) { - return getAttributeIs(inliningTarget, getSlotsNode, lazyClass, ModuleBuiltins.SLOTS.tp_getattro()); - } + protected static boolean hasNoGetAttr(Object lazyClass) { + CompilerAsserts.neverPartOfCompilation("only used in asserts"); + return LookupAttributeInMRONode.Dynamic.getUncached().execute(lazyClass, T___GETATTR__) == PNone.NO_VALUE; + } - protected static boolean isTypeGetAttribute(Node inliningTarget, GetCachedTpSlotsNode getSlotsNode, Object lazyClass) { - return getAttributeIs(inliningTarget, getSlotsNode, lazyClass, TypeBuiltins.SLOTS.tp_getattro()); - } + protected static boolean getAttributeIs(Node inliningTarget, GetCachedTpSlotsNode getSlotsNode, Object lazyClass, TpSlot slot) { + TpSlots slots = getSlotsNode.execute(inliningTarget, lazyClass); + return slots.tp_getattro() == slot; + } - protected static boolean isBuiltinTypeType(Object type) { - return type == PythonBuiltinClassType.PythonClass; - } + protected static boolean isObjectGetAttribute(Node inliningTarget, GetCachedTpSlotsNode getSlotsNode, Object lazyClass) { + return getAttributeIs(inliningTarget, getSlotsNode, lazyClass, ObjectBuiltins.SLOTS.tp_getattro()); + } - protected static boolean isTypeSlot(TruffleString name, TruffleString.CodePointLengthNode codePointLengthNode, TruffleString.CodePointAtIndexUTF32Node codePointAtIndexNode) { - return TpSlots.canBeSpecialMethod(name, codePointLengthNode, codePointAtIndexNode) || name.equalsUncached(T_MRO, TS_ENCODING); - } + protected static boolean isModuleGetAttribute(Node inliningTarget, GetCachedTpSlotsNode getSlotsNode, Object lazyClass) { + return getAttributeIs(inliningTarget, getSlotsNode, lazyClass, ModuleBuiltins.SLOTS.tp_getattro()); + } - // simple version that needs no calls and only reads from the object directly - @SuppressWarnings("unused") - @Specialization(guards = {"isObjectGetAttribute(inliningTarget, getSlotsNode, type)", "name == cachedName", "isNoValue(descr)"}, limit = "3") - static Object doBuiltinObject(VirtualFrame frame, Node inliningTarget, Object object, TruffleString name, - @Cached("name") TruffleString cachedName, - /* GR-44836 @Shared */ @Exclusive @Cached GetClassNode getClass, - @Exclusive @Cached GetCachedTpSlotsNode getSlotsNode, - @Bind("getClass.execute(inliningTarget, object)") Object type, - @Cached("create(name)") LookupAttributeInMRONode lookupName, - @Bind("lookupName.execute(type)") Object descr, - @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); - return readNode.execute(object, cachedName); - } + protected static boolean isTypeGetAttribute(Node inliningTarget, GetCachedTpSlotsNode getSlotsNode, Object lazyClass) { + return getAttributeIs(inliningTarget, getSlotsNode, lazyClass, TypeBuiltins.SLOTS.tp_getattro()); + } + + protected static boolean isBuiltinTypeType(Object type) { + return type == PythonBuiltinClassType.PythonClass; + } + + protected static boolean isTypeSlot(TruffleString name, TruffleString.CodePointLengthNode codePointLengthNode, TruffleString.CodePointAtIndexUTF32Node codePointAtIndexNode) { + return TpSlots.canBeSpecialMethod(name, codePointLengthNode, codePointAtIndexNode) || name.equalsUncached(T_MRO, TS_ENCODING); + } + + // simple version that needs no calls and only reads from the object directly + @SuppressWarnings("unused") + @Specialization(guards = {"isObjectGetAttribute(inliningTarget, getSlotsNode, type)", "name == cachedName", "isNoValue(descr)"}, limit = "3") + static Object doBuiltinObject(VirtualFrame frame, Node inliningTarget, Object object, TruffleString name, Object type, + @Cached("name") TruffleString cachedName, + @Exclusive @Cached GetCachedTpSlotsNode getSlotsNode, + @Cached("create(name)") LookupAttributeInMRONode lookupName, + @Bind("lookupName.execute(type)") Object descr, + @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); + return readNode.execute(object, cachedName); + } - // simple version that needs no calls and only reads from the object directly. the only - // difference for module.__getattribute__ over object.__getattribute__ is that it looks for a - // module-level __getattr__ as well - @SuppressWarnings("unused") - @Specialization(guards = {"isModuleGetAttribute(inliningTarget, getSlotsNode, type)", "name == cachedName", "isNoValue(descr)"}, limit = "1") - static Object doBuiltinModule(VirtualFrame frame, Node inliningTarget, Object object, TruffleString name, - @Cached("name") TruffleString cachedName, - /* GR-44836 @Shared */ @Exclusive @Cached GetClassNode getClass, - @Exclusive @Cached GetCachedTpSlotsNode getSlotsNode, - @Bind("getClass.execute(inliningTarget, object)") Object type, - @Cached("create(name)") LookupAttributeInMRONode lookupName, - @Bind("lookupName.execute(type)") Object descr, - @Shared @Cached(inline = false) ReadAttributeFromObjectNode readNode, - @Exclusive @Cached(inline = false) ReadAttributeFromObjectNode readGetattr, - /* GR-44836 @Shared */ @Exclusive @Cached IsBuiltinObjectProfile errorProfile, - @Exclusive @Cached InlinedConditionProfile noValueFound, - @Cached(inline = false) CallNode callGetattr) { - assert hasNoGetAttr(type); - Object value = readNode.execute(object, cachedName); - if (noValueFound.profile(inliningTarget, value == PNone.NO_VALUE)) { - Object getAttr = readGetattr.execute(object, SpecialMethodNames.T___GETATTR__); - if (getAttr != PNone.NO_VALUE) { - // (tfel): I'm not profiling this, since modules with __getattr__ are kind of rare - try { - return callGetattr.execute(frame, getAttr, name); - } catch (PException e) { - e.expect(inliningTarget, PythonBuiltinClassType.AttributeError, errorProfile); + // simple version that needs no calls and only reads from the object directly. the only + // difference for module.__getattribute__ over object.__getattribute__ is that it looks for + // a module-level __getattr__ as well + @SuppressWarnings("unused") + @Specialization(guards = {"isModuleGetAttribute(inliningTarget, getSlotsNode, type)", "name == cachedName", "isNoValue(descr)"}, limit = "1") + static Object doBuiltinModule(VirtualFrame frame, Node inliningTarget, Object object, TruffleString name, Object type, + @Cached("name") TruffleString cachedName, + @Exclusive @Cached GetCachedTpSlotsNode getSlotsNode, + @Cached("create(name)") LookupAttributeInMRONode lookupName, + @Bind("lookupName.execute(type)") Object descr, + @Shared @Cached(inline = false) ReadAttributeFromObjectNode readNode, + @Exclusive @Cached(inline = false) ReadAttributeFromObjectNode readGetattr, + /* GR-44836 @Shared */ @Exclusive @Cached IsBuiltinObjectProfile errorProfile, + @Exclusive @Cached InlinedConditionProfile noValueFound, + @Cached(inline = false) CallNode callGetattr) { + assert hasNoGetAttr(type); + Object value = readNode.execute(object, cachedName); + if (noValueFound.profile(inliningTarget, value == PNone.NO_VALUE)) { + Object getAttr = readGetattr.execute(object, SpecialMethodNames.T___GETATTR__); + if (getAttr != PNone.NO_VALUE) { + // (tfel): I'm not profiling this, since modules with __getattr__ are kind of + // rare + try { + return callGetattr.execute(frame, getAttr, name); + } catch (PException e) { + e.expect(inliningTarget, PythonBuiltinClassType.AttributeError, errorProfile); + return PNone.NO_VALUE; + } + } else { return PNone.NO_VALUE; } } else { - return PNone.NO_VALUE; + return value; } - } else { - return value; } - } - // If the class of an object is "type", the object must be a class and as "type" is the base - // metaclass, which defines only certain type slots, it can not have inherited other - // attributes via metaclass inheritance. For all non-type-slot attributes it therefore - // suffices to only check for inheritance via super classes. - @SuppressWarnings("unused") - @Specialization(guards = {"isTypeGetAttribute(inliningTarget, getTypeSlotsNode, type)", "isBuiltinTypeType(type)", "!isTypeSlot(name, codePointLengthNode, codePointAtIndexNode)"}, limit = "1") - static Object doBuiltinTypeType(VirtualFrame frame, Node inliningTarget, Object object, TruffleString name, - /* GR-44836 @Shared */ @Exclusive @Cached GetClassNode getClass, - @Exclusive @Cached GetCachedTpSlotsNode getTypeSlotsNode, - @SuppressWarnings("unused") @Exclusive @Cached GetObjectSlotsNode getSlotsNode, - @Bind("getClass.execute(inliningTarget, object)") Object type, - @Cached(inline = false) LookupAttributeInMRONode.Dynamic readNode, - @Exclusive @Cached InlinedConditionProfile valueFound, - @Exclusive @Cached InlinedConditionProfile noGetMethod, - @Exclusive @Cached CallSlotDescrGet callGetSlot, - /* GR-44836 @Shared */ @Exclusive @Cached IsBuiltinObjectProfile errorProfile, - @Shared @Cached TruffleString.CodePointLengthNode codePointLengthNode, - @Shared @Cached TruffleString.CodePointAtIndexUTF32Node codePointAtIndexNode) { - Object value = readNode.execute(object, name); - if (valueFound.profile(inliningTarget, value != PNone.NO_VALUE)) { - var valueSlots = getSlotsNode.execute(inliningTarget, value); - var valueGet = valueSlots.tp_descr_get(); - if (noGetMethod.profile(inliningTarget, valueGet == null)) { - return value; - } else { - try { - return callGetSlot.execute(frame, inliningTarget, valueGet, value, PNone.NO_VALUE, object); - } catch (PException e) { - e.expect(inliningTarget, PythonBuiltinClassType.AttributeError, errorProfile); - return PNone.NO_VALUE; + // If the class of an object is "type", the object must be a class and as "type" is the base + // metaclass, which defines only certain type slots, it can not have inherited other + // attributes via metaclass inheritance. For all non-type-slot attributes it therefore + // suffices to only check for inheritance via super classes. + @SuppressWarnings("unused") + @Specialization(guards = {"isTypeGetAttribute(inliningTarget, getTypeSlotsNode, type)", "isBuiltinTypeType(type)", "!isTypeSlot(name, codePointLengthNode, codePointAtIndexNode)"}, limit = "1") + static Object doBuiltinTypeType(VirtualFrame frame, Node inliningTarget, Object object, TruffleString name, Object type, + @Exclusive @Cached GetCachedTpSlotsNode getTypeSlotsNode, + @SuppressWarnings("unused") @Exclusive @Cached GetObjectSlotsNode getSlotsNode, + @Cached(inline = false) LookupAttributeInMRONode.Dynamic readNode, + @Exclusive @Cached InlinedConditionProfile valueFound, + @Exclusive @Cached InlinedConditionProfile noGetMethod, + @Exclusive @Cached CallSlotDescrGet callGetSlot, + /* GR-44836 @Shared */ @Exclusive @Cached IsBuiltinObjectProfile errorProfile, + @Shared @Cached TruffleString.CodePointLengthNode codePointLengthNode, + @Shared @Cached TruffleString.CodePointAtIndexUTF32Node codePointAtIndexNode) { + Object value = readNode.execute(object, name); + if (valueFound.profile(inliningTarget, value != PNone.NO_VALUE)) { + var valueSlots = getSlotsNode.execute(inliningTarget, value); + var valueGet = valueSlots.tp_descr_get(); + if (noGetMethod.profile(inliningTarget, valueGet == null)) { + return value; + } else { + try { + return callGetSlot.execute(frame, inliningTarget, valueGet, value, PNone.NO_VALUE, object); + } catch (PException e) { + e.expect(inliningTarget, PythonBuiltinClassType.AttributeError, errorProfile); + return PNone.NO_VALUE; + } } } + return PNone.NO_VALUE; } - return PNone.NO_VALUE; - } - // simple version that only reads attributes from (super) class inheritance and the object - // itself. the only difference for type.__getattribute__ over object.__getattribute__ - // is that it looks for a __get__ method on the value and invokes it if it is callable. - @SuppressWarnings("unused") - @Specialization(guards = {"isTypeGetAttribute(inliningTarget, getTypeSlotsNode, type)", "name == cachedName", "isNoValue(metaClassDescr)"}, replaces = "doBuiltinTypeType", limit = "1") - static Object doBuiltinType(VirtualFrame frame, Node inliningTarget, Object object, TruffleString name, - @Cached("name") TruffleString cachedName, - @Exclusive @Cached GetCachedTpSlotsNode getTypeSlotsNode, - /* GR-44836 @Shared */ @Exclusive @Cached GetClassNode getClass, - @Exclusive @Cached GetObjectSlotsNode getSlotsNode, - @Bind("getClass.execute(inliningTarget, object)") Object type, - @Cached(value = "create(name)", inline = false) LookupAttributeInMRONode lookupInMetaclassHierachy, - @Bind("lookupInMetaclassHierachy.execute(type)") Object metaClassDescr, - @Cached(value = "create(name)", inline = false) LookupAttributeInMRONode readNode, - @Exclusive @Cached InlinedConditionProfile valueFound, - @Exclusive @Cached InlinedConditionProfile noGetMethod, - @Exclusive @Cached CallSlotDescrGet callGetSlot, - /* GR-44836 @Shared */ @Exclusive @Cached IsBuiltinObjectProfile errorProfile) { - assert hasNoGetAttr(type); - Object value = readNode.execute(object); - if (valueFound.profile(inliningTarget, value != PNone.NO_VALUE)) { - var valueSlots = getSlotsNode.execute(inliningTarget, value); - var valueGet = valueSlots.tp_descr_get(); - if (noGetMethod.profile(inliningTarget, valueGet == null)) { - return value; - } else { - try { - return callGetSlot.execute(frame, inliningTarget, valueGet, value, PNone.NO_VALUE, object); - } catch (PException e) { - e.expect(inliningTarget, PythonBuiltinClassType.AttributeError, errorProfile); - return PNone.NO_VALUE; + // simple version that only reads attributes from (super) class inheritance and the object + // itself. the only difference for type.__getattribute__ over object.__getattribute__ + // is that it looks for a __get__ method on the value and invokes it if it is callable. + @SuppressWarnings("unused") + @Specialization(guards = {"isTypeGetAttribute(inliningTarget, getTypeSlotsNode, type)", "name == cachedName", "isNoValue(metaClassDescr)"}, replaces = "doBuiltinTypeType", limit = "1") + static Object doBuiltinType(VirtualFrame frame, Node inliningTarget, Object object, TruffleString name, Object type, + @Cached("name") TruffleString cachedName, + @Exclusive @Cached GetCachedTpSlotsNode getTypeSlotsNode, + @Exclusive @Cached GetObjectSlotsNode getSlotsNode, + @Cached(value = "create(name)", inline = false) LookupAttributeInMRONode lookupInMetaclassHierarchy, + @Bind("lookupInMetaclassHierarchy.execute(type)") Object metaClassDescr, + @Cached(value = "create(name)", inline = false) LookupAttributeInMRONode readNode, + @Exclusive @Cached InlinedConditionProfile valueFound, + @Exclusive @Cached InlinedConditionProfile noGetMethod, + @Exclusive @Cached CallSlotDescrGet callGetSlot, + /* GR-44836 @Shared */ @Exclusive @Cached IsBuiltinObjectProfile errorProfile) { + assert hasNoGetAttr(type); + Object value = readNode.execute(object); + if (valueFound.profile(inliningTarget, value != PNone.NO_VALUE)) { + var valueSlots = getSlotsNode.execute(inliningTarget, value); + var valueGet = valueSlots.tp_descr_get(); + if (noGetMethod.profile(inliningTarget, valueGet == null)) { + return value; + } else { + try { + return callGetSlot.execute(frame, inliningTarget, valueGet, value, PNone.NO_VALUE, object); + } catch (PException e) { + e.expect(inliningTarget, PythonBuiltinClassType.AttributeError, errorProfile); + return PNone.NO_VALUE; + } } } + return PNone.NO_VALUE; } - return PNone.NO_VALUE; - } - @Specialization(replaces = {"doBuiltinObject", "doBuiltinModule", "doBuiltinType"}) - static Object getDynamicAttr(Frame frame, Node inliningTarget, Object receiver, TruffleString name, - /* GR-44836 @Shared */ @Exclusive @Cached GetClassNode getClass, - @Exclusive @Cached GetCachedTpSlotsNode getSlotsNode, - @Exclusive @Cached CallSlotGetAttrNode callGetattribute, - /* GR-44836 @Shared */ @Exclusive @Cached IsBuiltinObjectProfile errorProfile, - @Shared @Cached TruffleString.CodePointLengthNode codePointLengthNode, - @Shared @Cached TruffleString.CodePointAtIndexUTF32Node codePointAtIndexNode) { - Object type = getClass.execute(inliningTarget, receiver); - TpSlots slots = getSlotsNode.execute(inliningTarget, type); - if (!codePointLengthNode.isAdoptable()) { - // It pays to try this in the uncached case, avoiding a full call to tp_getattr(o) - Object result = readAttributeQuickly(type, slots, receiver, name, codePointLengthNode, codePointAtIndexNode); - if (result != null) { - return result; + @Specialization(replaces = {"doBuiltinObject", "doBuiltinModule", "doBuiltinType"}) + static Object getDynamicAttr(Frame frame, Node inliningTarget, Object receiver, TruffleString name, Object type, + @Exclusive @Cached GetCachedTpSlotsNode getSlotsNode, + @Exclusive @Cached CallSlotGetAttrNode callGetattribute, + /* GR-44836 @Shared */ @Exclusive @Cached IsBuiltinObjectProfile errorProfile, + @Shared @Cached TruffleString.CodePointLengthNode codePointLengthNode, + @Shared @Cached TruffleString.CodePointAtIndexUTF32Node codePointAtIndexNode) { + TpSlots slots = getSlotsNode.execute(inliningTarget, type); + if (!codePointLengthNode.isAdoptable()) { + // It pays to try this in the uncached case, avoiding a full call to tp_getattr(o) + Object result = readAttributeQuickly(type, slots, receiver, name, codePointLengthNode, codePointAtIndexNode); + if (result != null) { + return result; + } + // Otherwise fallback to tp_getattr(o) } - // Otherwise fallback to tp_getattr(o) - } - try { - return callGetattribute.execute((VirtualFrame) frame, inliningTarget, slots, receiver, name); - } catch (PException e) { - e.expect(inliningTarget, PythonBuiltinClassType.AttributeError, errorProfile); + try { + return callGetattribute.execute((VirtualFrame) frame, inliningTarget, slots, receiver, name); + } catch (PException e) { + e.expect(inliningTarget, PythonBuiltinClassType.AttributeError, errorProfile); + } + return PNone.NO_VALUE; } - return PNone.NO_VALUE; } @NeverDefault @@ -294,6 +304,7 @@ public static PyObjectLookupAttr create() { return PyObjectLookupAttrNodeGen.create(); } + @NeverDefault public static PyObjectLookupAttr getUncached() { return PyObjectLookupAttrNodeGen.getUncached(); } @@ -306,8 +317,8 @@ public static PyObjectLookupAttr getUncached() { * really use it then, because when we only use it in the interpreter, the compiled code would * skip this and immediately deopt, if the code after was never run and initialized. And anyway, * the hope is that in the cached case, we just stay in the above specializations - * {@link #doBuiltinObject}, {@link #doBuiltinModule}, or {@link #doBuiltinType} and get the - * fast path through them. + * {@link Inner#doBuiltinObject}, {@link Inner#doBuiltinModule}, or {@link Inner#doBuiltinType} + * and get the fast path through them. * * This inlines parts of the logic of the {@code ObjectBuiltins.GetAttributeNode} and {@code * ModuleBuiltins.GetAttributeNode}. This method returns {@code PNone.NO_VALUE} when the @@ -315,7 +326,8 @@ public static PyObjectLookupAttr getUncached() { * null} when no shortcut was applicable. If {@code PNone.NO_VALUE} was returned, name is * guaranteed to be a {@code java.lang.TruffleString}. */ - static Object readAttributeQuickly(Object type, TpSlots slots, Object receiver, TruffleString stringName, TruffleString.CodePointLengthNode codePointLengthNode, + static Object readAttributeQuickly(Object type, TpSlots slots, Object receiver, TruffleString stringName, + TruffleString.CodePointLengthNode codePointLengthNode, TruffleString.CodePointAtIndexUTF32Node codePointAtIndexNode) { if (slots.tp_getattro() == ObjectBuiltins.SLOTS.tp_getattro() && type instanceof PythonManagedClass) { PythonAbstractClass[] bases = ((PythonManagedClass) type).getBaseClasses(); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectRichCompareBool.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectRichCompareBool.java index ac25d33f2c..f2453de2ee 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectRichCompareBool.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectRichCompareBool.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 @@ -47,6 +47,7 @@ import com.oracle.graal.python.nodes.truffle.PythonIntegerTypes; import com.oracle.graal.python.util.PythonUtils; import com.oracle.truffle.api.CompilerDirectives; +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; @@ -129,6 +130,7 @@ static boolean doStrings(TruffleString a, TruffleString b, RichCmpOp op, } @Fallback + @InliningCutoff static boolean doIt(VirtualFrame frame, Object a, Object b, RichCmpOp op, @Cached IsNode isNode, @Cached(inline = false) GenericRichCompare richCompare, 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 75aeb5f238..5aca3355ed 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 @@ -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 @@ -167,10 +167,9 @@ public static void executeUncached(PythonAbstractObject self, HiddenAttr attr, O @Specialization(guards = "attr == DICT") static void doPythonObjectDict(PythonObject self, HiddenAttr attr, Object value, - @Cached DynamicObject.GetShapeFlagsNode getShapeFlagsNode, @Cached DynamicObject.SetShapeFlagsNode setShapeFlagsNode, @Shared @Cached DynamicObject.PutNode putNode) { - self.addShapeFlag(HAS_MATERIALIZED_DICT, getShapeFlagsNode, setShapeFlagsNode); + setShapeFlagsNode.executeAdd(self, HAS_MATERIALIZED_DICT); putNode.execute(self, DICT.key, 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 b50a750fb2..ea138dbaf4 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 @@ -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 @@ -79,6 +79,8 @@ public static ReadAttributeFromObjectNode getUncached() { public abstract Object execute(Object object, TruffleString key); + public abstract Object execute(PythonObject object, TruffleString key); + public abstract Object execute(PythonAbstractNativeObject object, TruffleString key); // any python object attribute read 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 d0892dee31..9ab937a7e4 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 @@ -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 @@ -136,11 +136,10 @@ static boolean writeToDynamicStoragePythonClass(PythonClass klass, TruffleString @SuppressWarnings("unused") @Shared("getDict") @Cached GetDictIfExistsNode getDict, @Exclusive @Cached InlinedBranchProfile updateFlags, @Cached DynamicObject.PutNode putNode, - @Cached DynamicObject.GetShapeFlagsNode getShapeFlagsNode, @Cached DynamicObject.SetShapeFlagsNode setShapeFlagsNode) { if (value == PNone.NO_VALUE) { updateFlags.enter(inliningTarget); - klass.addShapeFlag(HAS_NO_VALUE_PROPERTIES, getShapeFlagsNode, setShapeFlagsNode); + setShapeFlagsNode.executeAdd(klass, HAS_NO_VALUE_PROPERTIES); } return writeToDynamicStorageManagedClass(klass, key, value, putNode); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/util/InlineWeakValueProfile.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/util/InlineWeakValueProfile.java index 3853309008..b90ef74daa 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/util/InlineWeakValueProfile.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/util/InlineWeakValueProfile.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 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 @@ -44,7 +44,9 @@ 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.nodes.DenyReplace; import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.nodes.UnadoptableNode; @GenerateInline @GenerateCached(false) @@ -67,8 +69,9 @@ static Object doUncached(Object value) { return value; } + @DenyReplace @GenerateCached(false) - private static final class Uncached extends InlineWeakValueProfile { + private static final class Uncached extends InlineWeakValueProfile implements UnadoptableNode { private static final Uncached INSTANCE = new Uncached(); @Override