Skip to content

Commit adde3b7

Browse files
committed
[GR-72611] Optimize reading globals/builtins in the Bytecode DSL interpreter.
PullRequest: graalpython/4196
2 parents d038e6a + d3a60de commit adde3b7

10 files changed

Lines changed: 183 additions & 170 deletions

File tree

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/GraalPythonModuleBuiltins.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -401,7 +401,7 @@ private void runFile(PythonContext context, TruffleString inputFilePath) {
401401
Object[] arguments = PArguments.create();
402402
PythonModule mainModule = context.getMainModule();
403403
PDict mainDict = GetOrCreateDictNode.executeUncached(mainModule);
404-
PArguments.setGlobals(arguments, mainModule);
404+
PArguments.setGlobals(arguments, mainDict);
405405
PArguments.setSpecialArgument(arguments, mainDict);
406406
PArguments.setException(arguments, PException.NO_EXCEPTION);
407407
context.initializeMainModule(inputFilePath);

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/function/PArguments.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2017, 2025, Oracle and/or its affiliates.
2+
* Copyright (c) 2017, 2026, Oracle and/or its affiliates.
33
* Copyright (c) 2013, Regents of the University of California
44
*
55
* All rights reserved.
@@ -26,9 +26,11 @@
2626
package com.oracle.graal.python.builtins.objects.function;
2727

2828
import com.oracle.graal.python.builtins.objects.frame.PFrame;
29+
import com.oracle.graal.python.builtins.objects.module.PythonModule;
2930
import com.oracle.graal.python.builtins.objects.object.PythonObject;
3031
import com.oracle.graal.python.nodes.argument.CreateArgumentsNode;
3132
import com.oracle.graal.python.runtime.exception.PException;
33+
import com.oracle.truffle.api.CompilerAsserts;
3234
import com.oracle.truffle.api.exception.AbstractTruffleException;
3335
import com.oracle.truffle.api.frame.Frame;
3436

@@ -70,9 +72,10 @@ public static boolean isPythonFrame(Object[] frameArgs) {
7072
return frameArgs.length >= USER_ARGUMENTS_OFFSET && frameArgs[INDEX_CURRENT_FRAME_INFO] instanceof PFrame.Reference;
7173
}
7274

73-
public static Object[] withGlobals(PythonObject globals) {
75+
public static Object[] withGlobals(PythonModule globals) {
76+
CompilerAsserts.neverPartOfCompilation();
7477
Object[] arguments = create();
75-
setGlobals(arguments, globals);
78+
setGlobals(arguments, globals.getDict());
7679
return arguments;
7780
}
7881

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/PythonModule.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2017, 2025, Oracle and/or its affiliates.
2+
* Copyright (c) 2017, 2026, Oracle and/or its affiliates.
33
* Copyright (c) 2013, Regents of the University of California
44
*
55
* All rights reserved.
@@ -37,9 +37,12 @@
3737
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
3838
import com.oracle.graal.python.builtins.PythonBuiltins;
3939
import com.oracle.graal.python.builtins.objects.PNone;
40+
import com.oracle.graal.python.builtins.objects.dict.PDict;
4041
import com.oracle.graal.python.builtins.objects.object.PythonObject;
4142
import com.oracle.graal.python.nodes.PGuards;
43+
import com.oracle.graal.python.nodes.object.GetDictIfExistsNode;
4244
import com.oracle.graal.python.nodes.object.GetOrCreateDictNode;
45+
import com.oracle.truffle.api.CompilerAsserts;
4346
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
4447
import com.oracle.truffle.api.object.Shape;
4548
import com.oracle.truffle.api.strings.TruffleString;
@@ -61,7 +64,7 @@ public final class PythonModule extends PythonObject {
6164
* table reference weak in order to break possible reference cycles. This field will ever only
6265
* be set if the module's native definition provides a traverse function (see
6366
* {@code moduleobject.c: module_traverse}). The condition for this is:
64-
*
67+
*
6568
* <pre>
6669
* {@code
6770
* 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) {
162165
public Object[] getReplicatedNativeReferences() {
163166
return this.replicatedNativeReferences;
164167
}
168+
169+
public PDict getDict() {
170+
// PythonModule always have a dict
171+
CompilerAsserts.neverPartOfCompilation();
172+
return GetDictIfExistsNode.getDictUncached(this);
173+
}
165174
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/object/PythonObject.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2017, 2025, Oracle and/or its affiliates.
2+
* Copyright (c) 2017, 2026, Oracle and/or its affiliates.
33
* Copyright (c) 2013, Regents of the University of California
44
*
55
* All rights reserved.
@@ -83,11 +83,11 @@ public void setDict(Node inliningTarget, HiddenAttr.WriteNode writeNode, PDict d
8383
}
8484

8585
@NeverDefault
86-
public Object getPythonClass() {
86+
public final Object getPythonClass() {
8787
return pythonClass;
8888
}
8989

90-
public void setPythonClass(Object pythonClass) {
90+
public final void setPythonClass(Object pythonClass) {
9191
assert getShape().getDynamicType() == PNone.NO_VALUE;
9292
this.pythonClass = pythonClass;
9393
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/lib/PyObjectGetItem.java

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -42,10 +42,12 @@
4242

4343
import static com.oracle.graal.python.builtins.PythonBuiltinClassType.TypeError;
4444
import static com.oracle.graal.python.nodes.SpecialMethodNames.T___CLASS_GETITEM__;
45+
import static com.oracle.graal.python.runtime.exception.PythonErrorType.KeyError;
4546

4647
import com.oracle.graal.python.PythonLanguage;
4748
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
4849
import com.oracle.graal.python.builtins.objects.PNone;
50+
import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageGetItem;
4951
import com.oracle.graal.python.builtins.objects.dict.DictBuiltins;
5052
import com.oracle.graal.python.builtins.objects.dict.PDict;
5153
import com.oracle.graal.python.builtins.objects.list.ListBuiltins;
@@ -63,6 +65,8 @@
6365
import com.oracle.graal.python.nodes.PRaiseNode;
6466
import com.oracle.graal.python.nodes.call.CallNode;
6567
import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinClassExactProfile;
68+
import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinObjectProfile;
69+
import com.oracle.graal.python.runtime.exception.PException;
6670
import com.oracle.graal.python.runtime.object.PFactory;
6771
import com.oracle.truffle.api.HostCompilerDirectives.InliningCutoff;
6872
import com.oracle.truffle.api.dsl.Cached;
@@ -76,6 +80,7 @@
7680
import com.oracle.truffle.api.frame.Frame;
7781
import com.oracle.truffle.api.frame.VirtualFrame;
7882
import com.oracle.truffle.api.nodes.Node;
83+
import com.oracle.truffle.api.strings.TruffleString;
7984

8085
/**
8186
* Equivalent of CPython's {@code PyObject_GetItem}.
@@ -121,6 +126,38 @@ static Object doGeneric(VirtualFrame frame, Node inliningTarget, Object object,
121126
return genericNode.execute(frame, inliningTarget, object, slots, key);
122127
}
123128

129+
/**
130+
* A version of {@link PyObjectGetItem} optimized for builtin dictionaries and
131+
* {@link TruffleString} keys that suppresses the {@code KeyError} and returns {@code null} if
132+
* key was not found.
133+
*/
134+
@GenerateUncached
135+
@GenerateInline
136+
@GenerateCached(false)
137+
public abstract static class PyObjectGetItemOrNull extends PNodeWithContext {
138+
public abstract Object execute(VirtualFrame frame, Node inliningTarget, Object dict, TruffleString key);
139+
140+
@Specialization(guards = "isBuiltinDict(dict)")
141+
static Object doPDict(VirtualFrame frame, Node inliningTarget, PDict dict, TruffleString key,
142+
@Cached HashingStorageGetItem getItem) {
143+
return getItem.execute(frame, inliningTarget, dict.getDictStorage(), key);
144+
}
145+
146+
@Fallback
147+
@InliningCutoff
148+
static Object doPDictGeneric(VirtualFrame frame, Node inliningTarget, Object object, TruffleString key,
149+
@Cached GetObjectSlotsNode getSlotsNode,
150+
@Cached PyObjectGetItemGeneric genericNode,
151+
@Cached IsBuiltinObjectProfile errorProfile) {
152+
try {
153+
return doGeneric(frame, inliningTarget, object, key, getSlotsNode, genericNode);
154+
} catch (PException e) {
155+
e.expect(inliningTarget, KeyError, errorProfile);
156+
return null;
157+
}
158+
}
159+
}
160+
124161
@GenerateUncached
125162
@GenerateInline
126163
@GenerateCached(false)

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/bytecode_dsl/PBytecodeDSLRootNode.java

Lines changed: 40 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
import com.oracle.graal.python.builtins.objects.asyncio.PAsyncGenWrappedValue;
7373
import com.oracle.graal.python.builtins.objects.cell.PCell;
7474
import com.oracle.graal.python.builtins.objects.code.PCode;
75+
import com.oracle.graal.python.builtins.objects.common.DynamicObjectStorage;
7576
import com.oracle.graal.python.builtins.objects.common.EconomicMapStorage;
7677
import com.oracle.graal.python.builtins.objects.common.HashingStorage;
7778
import com.oracle.graal.python.builtins.objects.common.HashingStorageNodes.HashingStorageSetItem;
@@ -151,6 +152,7 @@
151152
import com.oracle.graal.python.lib.PyObjectFunctionStr;
152153
import com.oracle.graal.python.lib.PyObjectGetAttr;
153154
import com.oracle.graal.python.lib.PyObjectGetItem;
155+
import com.oracle.graal.python.lib.PyObjectGetItem.PyObjectGetItemOrNull;
154156
import com.oracle.graal.python.lib.PyObjectGetIter;
155157
import com.oracle.graal.python.lib.PyObjectGetMethod;
156158
import com.oracle.graal.python.lib.PyObjectHashNode;
@@ -178,6 +180,7 @@
178180
import com.oracle.graal.python.nodes.argument.keywords.NonMappingException;
179181
import com.oracle.graal.python.nodes.argument.keywords.SameDictKeyException;
180182
import com.oracle.graal.python.nodes.attributes.GetFixedAttributeNode;
183+
import com.oracle.graal.python.nodes.attributes.ReadAttributeFromPythonObjectNode;
181184
import com.oracle.graal.python.nodes.builtins.ListNodes;
182185
import com.oracle.graal.python.nodes.bytecode.CopyDictWithoutKeysNode;
183186
import com.oracle.graal.python.nodes.bytecode.GetAIterNode;
@@ -211,7 +214,6 @@
211214
import com.oracle.graal.python.nodes.frame.ReadBuiltinNode;
212215
import com.oracle.graal.python.nodes.frame.ReadFromLocalsNode;
213216
import com.oracle.graal.python.nodes.frame.ReadGlobalOrBuiltinNode;
214-
import com.oracle.graal.python.nodes.frame.ReadNameNode;
215217
import com.oracle.graal.python.nodes.frame.WriteGlobalNode;
216218
import com.oracle.graal.python.nodes.frame.WriteNameNode;
217219
import com.oracle.graal.python.nodes.object.BuiltinClassProfiles.IsBuiltinObjectProfile;
@@ -259,6 +261,7 @@
259261
import com.oracle.truffle.api.bytecode.ContinuationRootNode;
260262
import com.oracle.truffle.api.bytecode.EpilogExceptional;
261263
import com.oracle.truffle.api.bytecode.EpilogReturn;
264+
import com.oracle.truffle.api.bytecode.ForceQuickening;
262265
import com.oracle.truffle.api.bytecode.GenerateBytecode;
263266
import com.oracle.truffle.api.bytecode.Instruction;
264267
import com.oracle.truffle.api.bytecode.Instrumentation;
@@ -349,6 +352,7 @@
349352
@OperationProxy(SetupAnnotationsNode.class)
350353
@OperationProxy(GetAIterNode.class)
351354
@OperationProxy(GetANextNode.class)
355+
@OperationProxy(value = ReadGlobalOrBuiltinNode.class, name = "ReadGlobal")
352356
@OperationProxy(value = CopyDictWithoutKeysNode.class, name = "CopyDictWithoutKeys")
353357
@OperationProxy(value = PyObjectIsTrueNode.class, name = "Yes")
354358
@OperationProxy(value = PyObjectIsNotTrueNode.class, name = "Not")
@@ -1223,13 +1227,42 @@ public static void perform(VirtualFrame frame, TruffleString name, Object value,
12231227
}
12241228
}
12251229

1226-
@Operation(storeBytecodeIndex = true)
1230+
@Operation(storeBytecodeIndex = false)
12271231
@ConstantOperand(type = TruffleString.class)
1232+
@ImportStatic(PGuards.class)
12281233
public static final class ReadName {
1229-
@Specialization
1230-
public static Object perform(VirtualFrame frame, TruffleString name,
1231-
@Cached ReadNameNode readNode) {
1232-
return readNode.execute(frame, name);
1234+
static Object readFromLocalsFastPath(VirtualFrame frame, TruffleString attributeId, ReadAttributeFromPythonObjectNode readNode) {
1235+
Object specialArgument = PArguments.getSpecialArgument(frame);
1236+
if (specialArgument instanceof PDict dict && dict.getDictStorage() instanceof DynamicObjectStorage s) {
1237+
return readNode.execute(s.getStore(), attributeId, PNone.NO_VALUE);
1238+
}
1239+
return PNone.NO_VALUE;
1240+
}
1241+
1242+
@ForceQuickening
1243+
@Specialization(guards = "!isNoValue(result)", limit = "1")
1244+
public static Object doLocalFastPath(VirtualFrame frame, TruffleString name,
1245+
@Cached ReadAttributeFromPythonObjectNode readAttrNode,
1246+
@Bind("readFromLocalsFastPath(frame, name, readAttrNode)") Object result) {
1247+
return result;
1248+
}
1249+
1250+
@StoreBytecodeIndex
1251+
@Specialization(replaces = "doLocalFastPath")
1252+
public static Object doFull(VirtualFrame frame, TruffleString name,
1253+
@Bind Node inliningTarget,
1254+
@Cached PyObjectGetItemOrNull getLocal,
1255+
@Cached ReadGlobalOrBuiltinNode readGlobalOrBuiltinNode,
1256+
@Cached InlinedConditionProfile hasLocalsProfile) {
1257+
Object locals = PArguments.getSpecialArgument(frame);
1258+
Object result = null;
1259+
if (hasLocalsProfile.profile(inliningTarget, locals != null)) {
1260+
result = getLocal.execute(frame, inliningTarget, locals, name);
1261+
}
1262+
if (result == null) {
1263+
return readGlobalOrBuiltinNode.execute(frame, name);
1264+
}
1265+
return result;
12331266
}
12341267
}
12351268

@@ -1640,16 +1673,6 @@ public static void doWithFrame(VirtualFrame frame, Object primary, Object index,
16401673
}
16411674
}
16421675

1643-
@Operation(storeBytecodeIndex = true)
1644-
@ConstantOperand(type = TruffleString.class)
1645-
public static final class ReadGlobal {
1646-
@Specialization
1647-
public static Object perform(VirtualFrame frame, TruffleString name,
1648-
@Cached ReadGlobalOrBuiltinNode readNode) {
1649-
return readNode.execute(frame, name);
1650-
}
1651-
}
1652-
16531676
@Operation(storeBytecodeIndex = true)
16541677
@ConstantOperand(type = TruffleString.class)
16551678
public static final class WriteGlobal {
@@ -2676,7 +2699,7 @@ public static Object doLoadCell(VirtualFrame frame, TruffleString name, Object d
26762699
value = getItemNode.execute(frame, inliningTarget, dict, name);
26772700
} catch (PException e) {
26782701
e.expect(inliningTarget, KeyError, errorProfile);
2679-
value = readGlobal.read(frame, PArguments.getGlobals(frame), name);
2702+
value = readGlobal.execute(frame, name);
26802703
}
26812704
return value;
26822705
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/nodes/exception/TopLevelExceptionHandler.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2017, 2026, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -323,7 +323,7 @@ private Object run(VirtualFrame frame) {
323323
} else {
324324
mainModule = pythonContext.getMainModule();
325325
PDict mainDict = GetOrCreateDictNode.executeUncached(mainModule);
326-
PArguments.setGlobals(arguments, mainModule);
326+
PArguments.setGlobals(arguments, mainDict);
327327
PArguments.setSpecialArgument(arguments, mainDict);
328328
PArguments.setException(arguments, PException.NO_EXCEPTION);
329329
}

0 commit comments

Comments
 (0)