diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/GraalPythonModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/GraalPythonModuleBuiltins.java index 25858c5e9d..9dbeec9804 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/GraalPythonModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/GraalPythonModuleBuiltins.java @@ -401,7 +401,7 @@ private void runFile(PythonContext context, TruffleString inputFilePath) { Object[] arguments = PArguments.create(); PythonModule mainModule = context.getMainModule(); PDict mainDict = GetOrCreateDictNode.executeUncached(mainModule); - PArguments.setGlobals(arguments, mainModule); + PArguments.setGlobals(arguments, mainDict); PArguments.setSpecialArgument(arguments, mainDict); PArguments.setException(arguments, PException.NO_EXCEPTION); context.initializeMainModule(inputFilePath); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/PArguments.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/PArguments.java index f40824f156..2790c91638 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/PArguments.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/PArguments.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. @@ -26,9 +26,11 @@ package com.oracle.graal.python.builtins.objects.function; import com.oracle.graal.python.builtins.objects.frame.PFrame; +import com.oracle.graal.python.builtins.objects.module.PythonModule; import com.oracle.graal.python.builtins.objects.object.PythonObject; import com.oracle.graal.python.nodes.argument.CreateArgumentsNode; import com.oracle.graal.python.runtime.exception.PException; +import com.oracle.truffle.api.CompilerAsserts; import com.oracle.truffle.api.exception.AbstractTruffleException; import com.oracle.truffle.api.frame.Frame; @@ -70,9 +72,10 @@ public static boolean isPythonFrame(Object[] frameArgs) { return frameArgs.length >= USER_ARGUMENTS_OFFSET && frameArgs[INDEX_CURRENT_FRAME_INFO] instanceof PFrame.Reference; } - public static Object[] withGlobals(PythonObject globals) { + public static Object[] withGlobals(PythonModule globals) { + CompilerAsserts.neverPartOfCompilation(); Object[] arguments = create(); - setGlobals(arguments, globals); + setGlobals(arguments, globals.getDict()); return arguments; } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/PythonModule.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/PythonModule.java index 10f69de0b2..92edd0cf17 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/PythonModule.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/PythonModule.java @@ -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. @@ -37,9 +37,12 @@ import com.oracle.graal.python.builtins.PythonBuiltinClassType; import com.oracle.graal.python.builtins.PythonBuiltins; import com.oracle.graal.python.builtins.objects.PNone; +import com.oracle.graal.python.builtins.objects.dict.PDict; import com.oracle.graal.python.builtins.objects.object.PythonObject; import com.oracle.graal.python.nodes.PGuards; +import com.oracle.graal.python.nodes.object.GetDictIfExistsNode; import com.oracle.graal.python.nodes.object.GetOrCreateDictNode; +import com.oracle.truffle.api.CompilerAsserts; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.object.Shape; import com.oracle.truffle.api.strings.TruffleString; @@ -61,7 +64,7 @@ public final class PythonModule extends PythonObject { * table reference weak in order to break possible reference cycles. This field will ever only * be set if the module's native definition provides a traverse function (see * {@code moduleobject.c: module_traverse}). The condition for this is: - * + * *
      * {@code
      * if (m -> md_def && m -> md_def -> m_traverse && (m -> md_def -> m_size <= 0 || m -> md_state != NULL)) {
@@ -162,4 +165,10 @@ public void setReplicatedNativeReferences(Object[] replicatedNativeReferences) {
     public Object[] getReplicatedNativeReferences() {
         return this.replicatedNativeReferences;
     }
+
+    public PDict getDict() {
+        // PythonModule always have a dict
+        CompilerAsserts.neverPartOfCompilation();
+        return GetDictIfExistsNode.getDictUncached(this);
+    }
 }
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 86718564dc..a7731d67cf 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
@@ -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.
@@ -83,11 +83,11 @@ public void setDict(Node inliningTarget, HiddenAttr.WriteNode writeNode, PDict d
     }
 
     @NeverDefault
-    public Object getPythonClass() {
+    public final Object getPythonClass() {
         return pythonClass;
     }
 
-    public void setPythonClass(Object pythonClass) {
+    public final void setPythonClass(Object pythonClass) {
         assert getShape().getDynamicType() == PNone.NO_VALUE;
         this.pythonClass = pythonClass;
     }
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectGetItem.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectGetItem.java
index f20435b8b3..2a78ecb393 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectGetItem.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectGetItem.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
@@ -42,10 +42,12 @@
 
 import static com.oracle.graal.python.builtins.PythonBuiltinClassType.TypeError;
 import static com.oracle.graal.python.nodes.SpecialMethodNames.T___CLASS_GETITEM__;
+import static com.oracle.graal.python.runtime.exception.PythonErrorType.KeyError;
 
 import com.oracle.graal.python.PythonLanguage;
 import com.oracle.graal.python.builtins.PythonBuiltinClassType;
 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.dict.DictBuiltins;
 import com.oracle.graal.python.builtins.objects.dict.PDict;
 import com.oracle.graal.python.builtins.objects.list.ListBuiltins;
@@ -63,6 +65,8 @@
 import com.oracle.graal.python.nodes.PRaiseNode;
 import com.oracle.graal.python.nodes.call.CallNode;
 import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinClassExactProfile;
+import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinObjectProfile;
+import com.oracle.graal.python.runtime.exception.PException;
 import com.oracle.graal.python.runtime.object.PFactory;
 import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff;
 import com.oracle.truffle.api.dsl.Cached;
@@ -76,6 +80,7 @@
 import com.oracle.truffle.api.frame.Frame;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.strings.TruffleString;
 
 /**
  * Equivalent of CPython's {@code PyObject_GetItem}.
@@ -121,6 +126,38 @@ static Object doGeneric(VirtualFrame frame, Node inliningTarget, Object object,
         return genericNode.execute(frame, inliningTarget, object, slots, key);
     }
 
+    /**
+     * A version of {@link PyObjectGetItem} optimized for builtin dictionaries and
+     * {@link TruffleString} keys that suppresses the {@code KeyError} and returns {@code null} if
+     * key was not found.
+     */
+    @GenerateUncached
+    @GenerateInline
+    @GenerateCached(false)
+    public abstract static class PyObjectGetItemOrNull extends PNodeWithContext {
+        public abstract Object execute(VirtualFrame frame, Node inliningTarget, Object dict, TruffleString key);
+
+        @Specialization(guards = "isBuiltinDict(dict)")
+        static Object doPDict(VirtualFrame frame, Node inliningTarget, PDict dict, TruffleString key,
+                        @Cached HashingStorageGetItem getItem) {
+            return getItem.execute(frame, inliningTarget, dict.getDictStorage(), key);
+        }
+
+        @Fallback
+        @InliningCutoff
+        static Object doPDictGeneric(VirtualFrame frame, Node inliningTarget, Object object, TruffleString key,
+                        @Cached GetObjectSlotsNode getSlotsNode,
+                        @Cached PyObjectGetItemGeneric genericNode,
+                        @Cached IsBuiltinObjectProfile errorProfile) {
+            try {
+                return doGeneric(frame, inliningTarget, object, key, getSlotsNode, genericNode);
+            } catch (PException e) {
+                e.expect(inliningTarget, KeyError, errorProfile);
+                return null;
+            }
+        }
+    }
+
     @GenerateUncached
     @GenerateInline
     @GenerateCached(false)
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 63d7526ad8..b6634096cb 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
@@ -72,6 +72,7 @@
 import com.oracle.graal.python.builtins.objects.asyncio.PAsyncGenWrappedValue;
 import com.oracle.graal.python.builtins.objects.cell.PCell;
 import com.oracle.graal.python.builtins.objects.code.PCode;
+import com.oracle.graal.python.builtins.objects.common.DynamicObjectStorage;
 import com.oracle.graal.python.builtins.objects.common.EconomicMapStorage;
 import com.oracle.graal.python.builtins.objects.common.HashingStorage;
 import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageSetItem;
@@ -151,6 +152,7 @@
 import com.oracle.graal.python.lib.PyObjectFunctionStr;
 import com.oracle.graal.python.lib.PyObjectGetAttr;
 import com.oracle.graal.python.lib.PyObjectGetItem;
+import com.oracle.graal.python.lib.PyObjectGetItem.PyObjectGetItemOrNull;
 import com.oracle.graal.python.lib.PyObjectGetIter;
 import com.oracle.graal.python.lib.PyObjectGetMethod;
 import com.oracle.graal.python.lib.PyObjectHashNode;
@@ -178,6 +180,7 @@
 import com.oracle.graal.python.nodes.argument.keywords.NonMappingException;
 import com.oracle.graal.python.nodes.argument.keywords.SameDictKeyException;
 import com.oracle.graal.python.nodes.attributes.GetFixedAttributeNode;
+import com.oracle.graal.python.nodes.attributes.ReadAttributeFromPythonObjectNode;
 import com.oracle.graal.python.nodes.builtins.ListNodes;
 import com.oracle.graal.python.nodes.bytecode.CopyDictWithoutKeysNode;
 import com.oracle.graal.python.nodes.bytecode.GetAIterNode;
@@ -211,7 +214,6 @@
 import com.oracle.graal.python.nodes.frame.ReadBuiltinNode;
 import com.oracle.graal.python.nodes.frame.ReadFromLocalsNode;
 import com.oracle.graal.python.nodes.frame.ReadGlobalOrBuiltinNode;
-import com.oracle.graal.python.nodes.frame.ReadNameNode;
 import com.oracle.graal.python.nodes.frame.WriteGlobalNode;
 import com.oracle.graal.python.nodes.frame.WriteNameNode;
 import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinObjectProfile;
@@ -258,6 +260,7 @@
 import com.oracle.truffle.api.bytecode.ContinuationRootNode;
 import com.oracle.truffle.api.bytecode.EpilogExceptional;
 import com.oracle.truffle.api.bytecode.EpilogReturn;
+import com.oracle.truffle.api.bytecode.ForceQuickening;
 import com.oracle.truffle.api.bytecode.GenerateBytecode;
 import com.oracle.truffle.api.bytecode.Instruction;
 import com.oracle.truffle.api.bytecode.Instrumentation;
@@ -348,6 +351,7 @@
 @OperationProxy(SetupAnnotationsNode.class)
 @OperationProxy(GetAIterNode.class)
 @OperationProxy(GetANextNode.class)
+@OperationProxy(value = ReadGlobalOrBuiltinNode.class, name = "ReadGlobal")
 @OperationProxy(value = CopyDictWithoutKeysNode.class, name = "CopyDictWithoutKeys")
 @OperationProxy(value = PyObjectIsTrueNode.class, name = "Yes")
 @OperationProxy(value = PyObjectIsNotTrueNode.class, name = "Not")
@@ -1204,13 +1208,42 @@ public static void perform(VirtualFrame frame, TruffleString name, Object value,
         }
     }
 
-    @Operation(storeBytecodeIndex = true)
+    @Operation(storeBytecodeIndex = false)
     @ConstantOperand(type = TruffleString.class)
+    @ImportStatic(PGuards.class)
     public static final class ReadName {
-        @Specialization
-        public static Object perform(VirtualFrame frame, TruffleString name,
-                        @Cached ReadNameNode readNode) {
-            return readNode.execute(frame, name);
+        static Object readFromLocalsFastPath(VirtualFrame frame, TruffleString attributeId, ReadAttributeFromPythonObjectNode readNode) {
+            Object specialArgument = PArguments.getSpecialArgument(frame);
+            if (specialArgument instanceof PDict dict && dict.getDictStorage() instanceof DynamicObjectStorage s) {
+                return readNode.execute(s.getStore(), attributeId, PNone.NO_VALUE);
+            }
+            return PNone.NO_VALUE;
+        }
+
+        @ForceQuickening
+        @Specialization(guards = "!isNoValue(result)", limit = "1")
+        public static Object doLocalFastPath(VirtualFrame frame, TruffleString name,
+                        @Cached ReadAttributeFromPythonObjectNode readAttrNode,
+                        @Bind("readFromLocalsFastPath(frame, name, readAttrNode)") Object result) {
+            return result;
+        }
+
+        @StoreBytecodeIndex
+        @Specialization(replaces = "doLocalFastPath")
+        public static Object doFull(VirtualFrame frame, TruffleString name,
+                        @Bind Node inliningTarget,
+                        @Cached PyObjectGetItemOrNull getLocal,
+                        @Cached ReadGlobalOrBuiltinNode readGlobalOrBuiltinNode,
+                        @Cached InlinedConditionProfile hasLocalsProfile) {
+            Object locals = PArguments.getSpecialArgument(frame);
+            Object result = null;
+            if (hasLocalsProfile.profile(inliningTarget, locals != null)) {
+                result = getLocal.execute(frame, inliningTarget, locals, name);
+            }
+            if (result == null) {
+                return readGlobalOrBuiltinNode.execute(frame, name);
+            }
+            return result;
         }
     }
 
@@ -1621,16 +1654,6 @@ public static void doWithFrame(VirtualFrame frame, Object primary, Object index,
         }
     }
 
-    @Operation(storeBytecodeIndex = true)
-    @ConstantOperand(type = TruffleString.class)
-    public static final class ReadGlobal {
-        @Specialization
-        public static Object perform(VirtualFrame frame, TruffleString name,
-                        @Cached ReadGlobalOrBuiltinNode readNode) {
-            return readNode.execute(frame, name);
-        }
-    }
-
     @Operation(storeBytecodeIndex = true)
     @ConstantOperand(type = TruffleString.class)
     public static final class WriteGlobal {
@@ -2657,7 +2680,7 @@ public static Object doLoadCell(VirtualFrame frame, TruffleString name, Object d
                 value = getItemNode.execute(frame, inliningTarget, dict, name);
             } catch (PException e) {
                 e.expect(inliningTarget, KeyError, errorProfile);
-                value = readGlobal.read(frame, PArguments.getGlobals(frame), name);
+                value = readGlobal.execute(frame, name);
             }
             return value;
         }
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/exception/TopLevelExceptionHandler.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/exception/TopLevelExceptionHandler.java
index 5f4d7cc659..216a0c2602 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/exception/TopLevelExceptionHandler.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/exception/TopLevelExceptionHandler.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
@@ -323,7 +323,7 @@ private Object run(VirtualFrame frame) {
         } else {
             mainModule = pythonContext.getMainModule();
             PDict mainDict = GetOrCreateDictNode.executeUncached(mainModule);
-            PArguments.setGlobals(arguments, mainModule);
+            PArguments.setGlobals(arguments, mainDict);
             PArguments.setSpecialArgument(arguments, mainDict);
             PArguments.setException(arguments, PException.NO_EXCEPTION);
         }
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 bbfbbba72b..56bb66ba48 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
@@ -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.
@@ -26,52 +26,56 @@
 package com.oracle.graal.python.nodes.frame;
 
 import static com.oracle.graal.python.builtins.PythonBuiltinClassType.SystemError;
-import static com.oracle.graal.python.runtime.exception.PythonErrorType.KeyError;
 
 import com.oracle.graal.python.builtins.objects.PNone;
-import com.oracle.graal.python.builtins.objects.common.HashingStorage;
-import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageGetItem;
+import com.oracle.graal.python.builtins.objects.common.DynamicObjectStorage;
 import com.oracle.graal.python.builtins.objects.dict.PDict;
 import com.oracle.graal.python.builtins.objects.function.PArguments;
-import com.oracle.graal.python.builtins.objects.module.PythonModule;
-import com.oracle.graal.python.lib.PyObjectGetItem;
+import com.oracle.graal.python.builtins.objects.object.PythonObject;
+import com.oracle.graal.python.lib.PyObjectGetItem.PyObjectGetItemOrNull;
 import com.oracle.graal.python.nodes.ErrorMessages;
-import com.oracle.graal.python.nodes.PNodeWithContext;
+import com.oracle.graal.python.nodes.PGuards;
 import com.oracle.graal.python.nodes.PRaiseNode;
-import com.oracle.graal.python.nodes.attributes.ReadAttributeFromModuleNode;
-import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinObjectProfile;
+import com.oracle.graal.python.nodes.attributes.ReadAttributeFromPythonObjectNode;
 import com.oracle.graal.python.runtime.exception.PException;
 import com.oracle.truffle.api.CompilerAsserts;
+import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff;
+import com.oracle.truffle.api.bytecode.ConstantOperand;
+import com.oracle.truffle.api.bytecode.ForceQuickening;
+import com.oracle.truffle.api.bytecode.OperationProxy.Proxyable;
+import com.oracle.truffle.api.bytecode.StoreBytecodeIndex;
 import com.oracle.truffle.api.dsl.Bind;
 import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Cached.Exclusive;
 import com.oracle.truffle.api.dsl.Cached.Shared;
-import com.oracle.truffle.api.dsl.Fallback;
-import com.oracle.truffle.api.dsl.GenerateCached;
 import com.oracle.truffle.api.dsl.GenerateInline;
 import com.oracle.truffle.api.dsl.GenerateUncached;
+import com.oracle.truffle.api.dsl.ImportStatic;
 import com.oracle.truffle.api.dsl.NeverDefault;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.frame.Frame;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.object.Shape;
 import com.oracle.truffle.api.profiles.InlinedBranchProfile;
 import com.oracle.truffle.api.strings.TruffleString;
 
 @GenerateUncached
 @GenerateInline(false)       // footprint reduction 48 -> 30
-public abstract class ReadGlobalOrBuiltinNode extends PNodeWithContext {
-    public final Object execute(VirtualFrame frame, TruffleString name) {
-        CompilerAsserts.partialEvaluationConstant(name);
-        return executeWithGlobals(frame, PArguments.getGlobals(frame), name);
-    }
-
-    protected abstract Object executeWithGlobals(VirtualFrame frame, Object globals, TruffleString name);
+@Proxyable(storeBytecodeIndex = false)
+@ConstantOperand(type = TruffleString.class)
+@ImportStatic(PGuards.class)
+public abstract class ReadGlobalOrBuiltinNode extends Node {
+    public abstract Object execute(VirtualFrame frame, TruffleString name);
 
     public Object read(Frame frame, Object globals, TruffleString name) {
         CompilerAsserts.partialEvaluationConstant(name);
-        return executeWithGlobals((VirtualFrame) frame, globals, name);
+        // reloading globals is not efficient, but this entry point is here just because it is used
+        // from the manual interpreter and only for the time being until the manual interpreter is
+        // removed
+        assert PArguments.getGlobals(frame) == globals;
+        return execute((VirtualFrame) frame, name);
     }
 
     @NeverDefault
@@ -83,107 +87,63 @@ public static ReadGlobalOrBuiltinNode getUncached() {
         return ReadGlobalOrBuiltinNodeGen.getUncached();
     }
 
-    @Specialization(guards = {"isSingleContext()", "globals == cachedGlobals"}, limit = "1")
-    protected static Object readGlobalCached(@SuppressWarnings("unused") PythonModule globals, TruffleString attributeId,
-                    @Bind Node inliningTarget,
-                    @Shared("readFromBuiltinsNode") @Cached ReadBuiltinNode readFromBuiltinsNode,
-                    @Exclusive @Cached InlinedBranchProfile wasReadFromModule,
-                    @Shared("readFromModule") @Cached ReadAttributeFromModuleNode readFromModuleNode,
-                    @Cached(value = "globals", weak = true) PythonModule cachedGlobals) {
-        Object result = readFromModuleNode.execute(cachedGlobals, attributeId);
-        return returnGlobalOrBuiltin(result, attributeId, readFromBuiltinsNode, inliningTarget, wasReadFromModule);
-    }
-
-    @InliningCutoff
-    @Specialization(replaces = "readGlobalCached")
-    protected static Object readGlobal(PythonModule globals, TruffleString attributeId,
-                    @Bind Node inliningTarget,
-                    @Shared("readFromBuiltinsNode") @Cached ReadBuiltinNode readFromBuiltinsNode,
-                    @Exclusive @Cached InlinedBranchProfile wasReadFromModule,
-                    @Shared("readFromModule") @Cached ReadAttributeFromModuleNode readFromModuleNode) {
-        Object result = readFromModuleNode.execute(globals, attributeId);
-        return returnGlobalOrBuiltin(result, attributeId, readFromBuiltinsNode, inliningTarget, wasReadFromModule);
-    }
-
-    static final class GlobalsDictStorageChanged extends RuntimeException {
-        private static final GlobalsDictStorageChanged INSTANCE = new GlobalsDictStorageChanged();
-        private static final long serialVersionUID = 2982918866373996561L;
-
-        GlobalsDictStorageChanged() {
-            super(null, null);
+    public static Shape getGlobalsStorageShape(VirtualFrame frame) {
+        Object obj = PArguments.getGlobals(frame);
+        if (obj instanceof PDict dict && dict.getDictStorage() instanceof DynamicObjectStorage dom) {
+            return dom.getStore().getShape();
         }
+        return null;
+    }
 
-        @SuppressWarnings("sync-override")
-        @Override
-        public Throwable fillInStackTrace() {
-            return this;
+    public static Shape getGlobalsStorageShapeIfPropMissing(VirtualFrame frame, TruffleString name) {
+        Object obj = PArguments.getGlobals(frame);
+        if (obj instanceof PDict dict && dict.getDictStorage() instanceof DynamicObjectStorage dom) {
+            Shape shape = dom.getStore().getShape();
+            if (!shape.hasProperty(name)) {
+                return shape;
+            }
         }
+        return null;
     }
 
-    @Specialization(guards = {"isSingleContext()", "globals == cachedGlobals", "isBuiltinDict(cachedGlobals)"}, limit = "1", rewriteOn = GlobalsDictStorageChanged.class)
-    protected static Object readGlobalBuiltinDictCachedUnchangedStorage(@SuppressWarnings("unused") PDict globals, TruffleString attributeId,
-                    @Bind Node inliningTarget,
-                    @Shared("readFromBuiltinsNode") @Cached ReadBuiltinNode readFromBuiltinsNode,
-                    @Exclusive @Cached InlinedBranchProfile wasReadFromModule,
-                    @SuppressWarnings("unused") @Cached(value = "globals", weak = true) PDict cachedGlobals,
-                    @Cached(value = "globals.getDictStorage()", weak = true) HashingStorage cachedStorage,
-                    @Exclusive @Cached HashingStorageGetItem getItem) {
-        if (cachedGlobals.getDictStorage() != cachedStorage) {
-            throw GlobalsDictStorageChanged.INSTANCE;
-        }
-        Object result = getItem.execute(inliningTarget, cachedStorage, attributeId);
-        return returnGlobalOrBuiltin(result == null ? PNone.NO_VALUE : result, attributeId, readFromBuiltinsNode, inliningTarget, wasReadFromModule);
+    @ForceQuickening
+    @Specialization(guards = {"cachedGlobalsShape != null", "cachedGlobalsShape == getGlobalsStorageShape(frame)"}, //
+                    excludeForUncached = true, limit = "1")
+    public static Object readBuiltinFastPath(VirtualFrame frame, TruffleString attributeId,
+                    @Cached("getGlobalsStorageShapeIfPropMissing(frame, attributeId)") Shape cachedGlobalsShape,
+                    @Shared("readFromBuiltinsNode") @Cached ReadBuiltinNode readFromBuiltinsNode) {
+        return readFromBuiltinsNode.execute(attributeId);
     }
 
-    @InliningCutoff
-    @Specialization(guards = {"isSingleContext()", "globals == cachedGlobals",
-                    "isBuiltinDict(cachedGlobals)"}, replaces = "readGlobalBuiltinDictCachedUnchangedStorage", limit = "1")
-    protected static Object readGlobalBuiltinDictCached(@SuppressWarnings("unused") PDict globals, TruffleString attributeId,
-                    @Bind Node inliningTarget,
-                    @Shared("readFromBuiltinsNode") @Cached ReadBuiltinNode readFromBuiltinsNode,
-                    @Exclusive @Cached InlinedBranchProfile wasReadFromModule,
-                    @Cached(value = "globals", weak = true) PDict cachedGlobals,
-                    @Exclusive @Cached HashingStorageGetItem getItem) {
-        Object result = getItem.execute(inliningTarget, cachedGlobals.getDictStorage(), attributeId);
-        return returnGlobalOrBuiltin(result == null ? PNone.NO_VALUE : result, attributeId, readFromBuiltinsNode, inliningTarget, wasReadFromModule);
+    public static Object readFastFromGlobalStore(VirtualFrame frame, TruffleString name, ReadAttributeFromPythonObjectNode readNode) {
+        Object obj = PArguments.getGlobals(frame);
+        if (obj instanceof PDict dict && dict.getDictStorage() instanceof DynamicObjectStorage dom) {
+            return readNode.execute(dom.getStore(), name, PNone.NO_VALUE);
+        }
+        return PNone.NO_VALUE;
     }
 
-    @InliningCutoff
-    @Specialization(guards = "isBuiltinDict(globals)", replaces = {"readGlobalBuiltinDictCached", "readGlobalBuiltinDictCachedUnchangedStorage"})
-    protected static Object readGlobalBuiltinDict(@SuppressWarnings("unused") PDict globals, TruffleString attributeId,
-                    @Bind Node inliningTarget,
-                    @Shared("readFromBuiltinsNode") @Cached ReadBuiltinNode readFromBuiltinsNode,
-                    @Exclusive @Cached InlinedBranchProfile wasReadFromModule,
-                    @Bind("globals.getDictStorage()") HashingStorage storage,
-                    @Exclusive @Cached HashingStorageGetItem getItem) {
-        Object result = getItem.execute(inliningTarget, storage, attributeId);
-        return returnGlobalOrBuiltin(result == null ? PNone.NO_VALUE : result, attributeId, readFromBuiltinsNode, inliningTarget, wasReadFromModule);
+    @ForceQuickening
+    @Specialization(guards = "!isNoValue(result)", replaces = "readBuiltinFastPath", excludeForUncached = true, limit = "1")
+    public static Object readGlobalFastPath(VirtualFrame frame, TruffleString attributeId,
+                    @Cached ReadAttributeFromPythonObjectNode readNode,
+                    @Bind("readFastFromGlobalStore(frame, attributeId, readNode)") Object result) {
+        return result;
     }
 
-    @InliningCutoff
-    @Specialization
-    protected static Object readGlobalDictGeneric(VirtualFrame frame, PDict globals, TruffleString attributeId,
+    @StoreBytecodeIndex
+    @Specialization(replaces = {"readBuiltinFastPath", "readGlobalFastPath"})
+    public static Object readGlobalOrBuiltinGeneric(VirtualFrame frame, TruffleString attributeId,
                     @Bind Node inliningTarget,
                     @Shared("readFromBuiltinsNode") @Cached ReadBuiltinNode readFromBuiltinsNode,
                     @Exclusive @Cached InlinedBranchProfile wasReadFromModule,
-                    @Cached PyObjectGetItem getItemNode,
-                    @Cached IsBuiltinObjectProfile errorProfile) {
-        try {
-            Object result = getItemNode.execute(frame, inliningTarget, globals, attributeId);
-            return returnGlobalOrBuiltin(result, attributeId, readFromBuiltinsNode, inliningTarget, wasReadFromModule);
-        } catch (PException e) {
-            e.expect(inliningTarget, KeyError, errorProfile);
-            return returnGlobalOrBuiltin(PNone.NO_VALUE, attributeId, readFromBuiltinsNode, inliningTarget, wasReadFromModule);
+                    @Cached PyObjectGetItemOrNull getItemNode) {
+        PythonObject globalsObj = PArguments.getGlobals(frame);
+        if (!(globalsObj instanceof PDict globals)) {
+            throw raiseSystemError(inliningTarget);
         }
-    }
-
-    @Fallback
-    protected Object syserr(@SuppressWarnings("unused") VirtualFrame frame, @SuppressWarnings("unused") Object dict, @SuppressWarnings("unused") TruffleString attributeId) {
-        throw PRaiseNode.raiseStatic(this, SystemError, ErrorMessages.BAD_ARG_TO_INTERNAL_FUNC);
-    }
-
-    private static Object returnGlobalOrBuiltin(Object result, TruffleString attributeId, ReadBuiltinNode readFromBuiltinsNode, Node inliningTarget, InlinedBranchProfile wasReadFromModule) {
-        if (result != PNone.NO_VALUE) {
+        Object result = getItemNode.execute(frame, inliningTarget, globals, attributeId);
+        if (result != null) {
             wasReadFromModule.enter(inliningTarget);
             return result;
         } else {
@@ -191,19 +151,9 @@ private static Object returnGlobalOrBuiltin(Object result, TruffleString attribu
         }
     }
 
-    @GenerateUncached
-    @GenerateInline
-    @GenerateCached(false)
-    public abstract static class Lazy extends Node {
-        public final ReadGlobalOrBuiltinNode get(Node inliningTarget) {
-            return execute(inliningTarget);
-        }
-
-        public abstract ReadGlobalOrBuiltinNode execute(Node inliningTarget);
-
-        @Specialization
-        static ReadGlobalOrBuiltinNode doIt(@Cached(inline = false) ReadGlobalOrBuiltinNode node) {
-            return node;
-        }
+    @InliningCutoff
+    private static PException raiseSystemError(Node inliningTarget) {
+        CompilerDirectives.transferToInterpreter();
+        throw PRaiseNode.raiseStatic(inliningTarget, SystemError, ErrorMessages.BAD_ARG_TO_INTERNAL_FUNC);
     }
 }
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/ReadNameNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/ReadNameNode.java
index 033554ba22..f1d833b6d1 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/ReadNameNode.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/frame/ReadNameNode.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
@@ -40,18 +40,13 @@
  */
 package com.oracle.graal.python.nodes.frame;
 
-import com.oracle.graal.python.builtins.PythonBuiltinClassType;
 import com.oracle.graal.python.builtins.objects.PNone;
-import com.oracle.graal.python.builtins.objects.common.HashingStorage;
-import com.oracle.graal.python.builtins.objects.dict.PDict;
 import com.oracle.graal.python.builtins.objects.function.PArguments;
 import com.oracle.graal.python.nodes.PNodeWithContext;
-import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinObjectProfile;
-import com.oracle.graal.python.runtime.exception.PException;
 import com.oracle.truffle.api.CompilerAsserts;
 import com.oracle.truffle.api.dsl.Bind;
 import com.oracle.truffle.api.dsl.Cached;
-import com.oracle.truffle.api.dsl.Cached.Exclusive;
+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;
@@ -75,30 +70,20 @@ public static ReadNameNode create() {
         return ReadNameNodeGen.create();
     }
 
-    private static Object readGlobalsIfKeyError(VirtualFrame frame, Node inliningTarget, TruffleString attributeId, ReadGlobalOrBuiltinNode readGlobalNode, PException e,
-                    IsBuiltinObjectProfile keyError) {
-        e.expect(inliningTarget, PythonBuiltinClassType.KeyError, keyError);
-        return readGlobalNode.execute(frame, attributeId);
-    }
-
-    protected static HashingStorage getStorage(VirtualFrame frame) {
-        return ((PDict) PArguments.getSpecialArgument(frame)).getDictStorage();
-    }
-
     @Specialization(guards = "!hasLocals(frame)")
     protected static Object readFromLocals(VirtualFrame frame, TruffleString attributeId,
-                    @Exclusive @Cached ReadGlobalOrBuiltinNode readGlobalNode) {
+                    @Shared @Cached ReadGlobalOrBuiltinNode readGlobalNode) {
         return readGlobalNode.execute(frame, attributeId);
     }
 
     @Specialization(guards = "hasLocals(frame)")
     protected static Object readFromLocalsDict(VirtualFrame frame, TruffleString attributeId,
                     @Bind Node inliningTarget,
-                    @Cached ReadGlobalOrBuiltinNode.Lazy readGlobalOrBuiltinNode,
+                    @Shared @Cached ReadGlobalOrBuiltinNode readGlobalNode,
                     @Cached ReadFromLocalsNode readFromLocals) {
         Object result = readFromLocals.execute(frame, inliningTarget, PArguments.getSpecialArgument(frame), attributeId);
         if (result == PNone.NO_VALUE) {
-            return readGlobalOrBuiltinNode.get(inliningTarget).execute(frame, attributeId);
+            return readGlobalNode.execute(frame, attributeId);
         } else {
             return result;
         }
diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/GetDictIfExistsNode.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/GetDictIfExistsNode.java
index 8350f0b75a..fc77df4d78 100644
--- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/GetDictIfExistsNode.java
+++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/object/GetDictIfExistsNode.java
@@ -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
@@ -68,6 +68,7 @@
 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.Specialization;
 import com.oracle.truffle.api.interop.InteropLibrary;
 import com.oracle.truffle.api.library.CachedLibrary;
@@ -78,6 +79,11 @@
 @GenerateUncached
 @GenerateInline(false)       // footprint reduction 36 -> 17
 public abstract class GetDictIfExistsNode extends PNodeWithContext {
+    @NeverDefault
+    public static GetDictIfExistsNode create() {
+        return GetDictIfExistsNodeGen.create();
+    }
+
     public abstract PDict execute(Object object);
 
     public abstract PDict execute(PythonObject object);
@@ -111,7 +117,7 @@ protected boolean dictIsConstant(PythonObject object) {
         return object instanceof PythonModule || object instanceof PythonManagedClass;
     }
 
-    protected static PDict getDictUncached(PythonObject object) {
+    public static PDict getDictUncached(PythonObject object) {
         return (PDict) HiddenAttr.ReadNode.executeUncached(object, HiddenAttr.DICT, null);
     }