From 9cc03f37017bb71ae6b24958e8e964ca1fa0824f Mon Sep 17 00:00:00 2001 From: stepan Date: Mon, 27 Apr 2026 15:40:04 +0200 Subject: [PATCH 1/2] Minor improvements in MroShape --- .../graal/python/builtins/objects/type/MroShape.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/MroShape.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/MroShape.java index be6ae769c6..07d5389d85 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/MroShape.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/type/MroShape.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 @@ -59,6 +59,7 @@ import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.object.DynamicObject; import com.oracle.truffle.api.object.DynamicObject.GetNode; +import com.oracle.truffle.api.object.DynamicObject.GetShapeFlagsNode; import com.oracle.truffle.api.object.Property; import com.oracle.truffle.api.object.Shape; import com.oracle.truffle.api.strings.TruffleString; @@ -94,16 +95,17 @@ public static MroShape createRoot() { public static MroShape create(MroSequenceStorage mro, PythonLanguage lang) { CompilerAsserts.neverPartOfCompilation(); MroShape mroShape = lang.getMroShapeRoot(); + GetShapeFlagsNode getShapeFlagsUncached = GetShapeFlagsNode.getUncached(); for (int i = mro.length() - 1; i >= 0; i--) { PythonAbstractClass element = mro.getPythonClassItemNormalized(i); if (PythonManagedClass.isInstance(element)) { PythonManagedClass managedClass = PythonManagedClass.cast(element); - if (GetDictIfExistsNode.getUncached().execute(managedClass) != null) { + if (GetDictIfExistsNode.getDictUncached(managedClass) != null) { // On top of not having a shape, the dictionary may also contain items with side // effecting __eq__ and/or __hash__ return null; } - if ((DynamicObject.GetShapeFlagsNode.getUncached().execute(managedClass) & HAS_NO_VALUE_PROPERTIES) != 0) { + if ((getShapeFlagsUncached.execute(managedClass) & HAS_NO_VALUE_PROPERTIES) != 0) { return null; } assert hasNoNoValueProperties(managedClass); @@ -232,12 +234,12 @@ public static boolean validate(Object obj, PythonLanguage language) { // We ignore difference for special attributes that should not influence the MRO // lookup results diff.remove(T___SLOTNAMES__); - if (diff.size() > 0) { + if (!diff.isEmpty()) { HashSet sDiff = new HashSet<>(diff.size()); for (Object o : diff) { sDiff.add(o.toString()); } - assert diff.isEmpty() : message + ",diff:" + String.join(",", sDiff); + assert false : message + ",diff:" + String.join(",", sDiff); } } currMroShape = currMroShape.parent; From 821aa0111ed8a349f93c9bc941db7b8bea8a4a44 Mon Sep 17 00:00:00 2001 From: stepan Date: Mon, 27 Apr 2026 15:43:51 +0200 Subject: [PATCH 2/2] Quickenings for GetMethod bytecode --- .../bytecode_dsl/PBytecodeDSLRootNode.java | 61 ++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) 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 56f0f6b6c2..93e00f5ba3 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 @@ -218,6 +218,7 @@ import com.oracle.graal.python.nodes.call.special.CallTernaryMethodNode; import com.oracle.graal.python.nodes.call.special.CallUnaryMethodNode; import com.oracle.graal.python.nodes.call.special.LookupSpecialMethodNode; +import com.oracle.graal.python.nodes.call.special.MaybeBindDescriptorNode; import com.oracle.graal.python.nodes.classes.IsSubtypeNode; import com.oracle.graal.python.nodes.exception.EncapsulateExceptionGroupNode; import com.oracle.graal.python.nodes.exception.ExceptMatchNode; @@ -1926,8 +1927,66 @@ public static boolean doIterator(VirtualFrame frame, LocalAccessor output, Objec @Operation(storeBytecodeIndex = true) @ConstantOperand(type = TruffleString.class) + @ImportStatic({PythonUtils.class, PGuards.class, GetAttribute.class}) public static final class GetMethod { - @Specialization + /** NO_VALUE result indicates that we should bail out to the full lookup */ + @NeverDefault + static Object findStringMethod(Node n, TruffleString name) { + Object descr = LookupAttributeInMRONode.findAttr(PythonContext.get(n), PythonBuiltinClassType.PString, name, ReadAttributeFromPythonObjectNode.getUncached()); + // NO_VALUE would mean we need to lookup on the instance, non-method descriptor means that it's going to be something non-callable. + // This is certainly going to lead to some error, so not interesting for fast-path + return MaybeBindDescriptorNode.isMethodDescriptor(descr) ? descr : PNone.NO_VALUE; + } + + // Strings are by far the most common builtin objects on which we look up methods + @ForceQuickening + @Specialization(guards = "!isNoValue(result)", excludeForUncached = false) + public static Object doStringFastPath(VirtualFrame frame, TruffleString name, TruffleString obj, + @Cached(value = "findStringMethod($node, name)", allowUncached = true, weak = true) Object result) { + return result; + } + + @ForceQuickening + @Specialization(guards = { + "!hasMaterializedDict(cachedShape)", "managedClass != null || isBuiltinWithObjectOrModuleGetattro(cachedShape)", // + "cachedShape.check(obj)", "result != null"}, limit = "2", excludeForUncached = true) + public static Object doFastPath(VirtualFrame frame, + TruffleString name, PythonObject obj, + @Bind Node inliningTarget, + @Cached("obj.getShape()") Shape cachedShape, + @Cached(value = "getManagedClassOrNull(cachedShape)", weak = true) PythonManagedClass managedClass, + @Cached("getPropertyGetterWithFinalAssumption(cachedShape, name)") PropertyGetter cachedPropertyGetter, + @Cached InlineWeakValueProfile slotsValueProfile, + @Cached InlinedBranchProfile hasInstanceValueBranchProfile, + @Cached LookupAttributeInMRONode.CachedKeyFastPath getMethod, + @Bind("getMethodFastPath(obj, name, inliningTarget, managedClass, cachedShape, cachedPropertyGetter, slotsValueProfile, hasInstanceValueBranchProfile, getMethod)") Object result) { + assert obj.checkDictFlags(); + return result; + } + + static Object getMethodFastPath(PythonObject obj, TruffleString name, Node inliningTarget, PythonManagedClass managedClass, Shape cachedShape, PropertyGetter cachedPropertyGetter, + InlineWeakValueProfile slotsValueProfile, InlinedBranchProfile hasInstanceValueBranchProfile, LookupAttributeInMRONode.CachedKeyFastPath getMethod) { + if (managedClass != null) { + if (!GetAttribute.hasObjectOrModuleGetattro(inliningTarget, managedClass, slotsValueProfile)) { + return null; + } + } + Object descr = getMethod.execute(inliningTarget, cachedShape.getDynamicType(), name); + if (descr == null || (descr != PNone.NO_VALUE && !MaybeBindDescriptorNode.isMethodDescriptor(descr))) { + return null; + } + if (cachedPropertyGetter != null) { + assert obj.checkDictFlags(); + Object instanceValue = cachedPropertyGetter.get(obj); + if (instanceValue != PNone.NO_VALUE) { + hasInstanceValueBranchProfile.enter(inliningTarget); + return new BoundDescriptor(instanceValue); + } + } + return descr != PNone.NO_VALUE ? descr : null; + } + + @Specialization(replaces = {"doStringFastPath", "doFastPath"}) public static Object doIt(VirtualFrame frame, TruffleString name, Object obj, @Bind Node inliningTarget,