Skip to content

Commit 0022383

Browse files
horakivotimfel
authored andcommitted
Add lazy init of calltargets for builtins
1 parent 8d0faaa commit 0022383

File tree

5 files changed

+132
-13
lines changed

5 files changed

+132
-13
lines changed
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* The Universal Permissive License (UPL), Version 1.0
6+
*
7+
* Subject to the condition set forth below, permission is hereby granted to any
8+
* person obtaining a copy of this software, associated documentation and/or
9+
* data (collectively the "Software"), free of charge and under any and all
10+
* copyright rights in the Software, and any and all patent rights owned or
11+
* freely licensable by each licensor hereunder covering either (i) the
12+
* unmodified Software as contributed to or provided by such licensor, or (ii)
13+
* the Larger Works (as defined below), to deal in both
14+
*
15+
* (a) the Software, and
16+
*
17+
* (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
18+
* one is included with the Software each a "Larger Work" to which the Software
19+
* is contributed by such licensors),
20+
*
21+
* without restriction, including without limitation the rights to copy, create
22+
* derivative works of, display, perform, and distribute the Software and make,
23+
* use, sell, offer for sale, import, export, have made, and have sold the
24+
* Software and the Larger Work(s), and to sublicense the foregoing rights on
25+
* either these or other terms.
26+
*
27+
* This license is subject to the following condition:
28+
*
29+
* The above copyright notice and either this complete permission notice or at a
30+
* minimum a reference to the UPL must be included in all copies or substantial
31+
* portions of the Software.
32+
*
33+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
36+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
38+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
39+
* SOFTWARE.
40+
*/
41+
package com.oracle.graal.python.builtins;
42+
43+
import com.oracle.graal.python.util.Supplier;
44+
import com.oracle.truffle.api.CompilerAsserts;
45+
import com.oracle.truffle.api.RootCallTarget;
46+
47+
public class CachedLazyCalltargetSupplier {
48+
49+
private volatile RootCallTarget callTarget = null;
50+
private Supplier<RootCallTarget> supplier;
51+
52+
53+
public CachedLazyCalltargetSupplier(Supplier<RootCallTarget> supplier) {
54+
this.supplier = supplier;
55+
}
56+
57+
public RootCallTarget get() {
58+
CompilerAsserts.neverPartOfCompilation("Must not be part of the PE");
59+
60+
// avoids two volatile reads
61+
var alreadySetCallTarget = this.callTarget;
62+
if (alreadySetCallTarget != null) {
63+
return alreadySetCallTarget;
64+
}
65+
66+
synchronized (this) {
67+
if (callTarget == null) {
68+
callTarget = supplier.get();
69+
supplier = null;
70+
}
71+
return callTarget;
72+
}
73+
}
74+
75+
public RootCallTarget getIfExists() {
76+
return callTarget;
77+
}
78+
}

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltins.java

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,13 @@
4141
import com.oracle.graal.python.builtins.modules.ImpModuleBuiltins.ExecBuiltin;
4242
import com.oracle.graal.python.builtins.objects.PNone;
4343
import com.oracle.graal.python.builtins.objects.function.PBuiltinFunction;
44+
import com.oracle.graal.python.builtins.objects.function.Signature;
4445
import com.oracle.graal.python.builtins.objects.module.PythonModule;
4546
import com.oracle.graal.python.builtins.objects.object.PythonObject;
4647
import com.oracle.graal.python.builtins.objects.type.PythonBuiltinClass;
4748
import com.oracle.graal.python.nodes.function.BuiltinFunctionRootNode;
4849
import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode;
50+
import com.oracle.graal.python.runtime.PythonOptions;
4951
import com.oracle.graal.python.runtime.object.PFactory;
5052
import com.oracle.graal.python.util.BiConsumer;
5153
import com.oracle.truffle.api.RootCallTarget;
@@ -93,11 +95,22 @@ public void initialize(Python3Core core) {
9395
}
9496
TruffleString tsName = toTruffleStringUncached(builtin.name());
9597
PythonLanguage language = core.getLanguage();
96-
RootCallTarget callTarget = language.initBuiltinCallTarget(l -> new BuiltinFunctionRootNode(l, builtin, factory, declaresExplicitSelf), factory.getNodeClass(),
97-
builtin.name());
98+
boolean lazyCallTargets = core.getContext().getOption(PythonOptions.BuiltinLazyCallTargets);
99+
PBuiltinFunction function;
100+
if (lazyCallTargets) {
101+
Signature signature = BuiltinFunctionRootNode.createSignature(factory, builtin, declaresExplicitSelf, false);
102+
int flags = PBuiltinFunction.getFlags(builtin, signature);
103+
var callTargetSupplier = new CachedLazyCalltargetSupplier(() -> language.initBuiltinCallTarget(l -> new BuiltinFunctionRootNode(l, signature, builtin, factory, declaresExplicitSelf, null), factory.getNodeClass(),
104+
builtin.name()));
105+
function = PFactory.createBuiltinFunction(language, tsName, numDefaults(builtin),signature, flags, callTargetSupplier);
106+
} else {
107+
RootCallTarget callTarget = language.initBuiltinCallTarget(l -> new BuiltinFunctionRootNode(l, builtin, factory, declaresExplicitSelf), factory.getNodeClass(),
108+
builtin.name());
109+
int flags = PBuiltinFunction.getFlags(builtin, callTarget);
110+
function = PFactory.createBuiltinFunction(language, tsName, null, numDefaults(builtin), flags, callTarget);
111+
}
112+
98113
Object builtinDoc = builtin.doc().isEmpty() ? PNone.NONE : toTruffleStringUncached(builtin.doc());
99-
int flags = PBuiltinFunction.getFlags(builtin, callTarget);
100-
PBuiltinFunction function = PFactory.createBuiltinFunction(language, tsName, null, numDefaults(builtin), flags, callTarget);
101114
function.setAttribute(T___DOC__, builtinDoc);
102115
BoundBuiltinCallable<?> callable = function;
103116
if (builtin.isGetter() || builtin.isSetter()) {

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

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import com.oracle.graal.python.PythonLanguage;
3434
import com.oracle.graal.python.builtins.BoundBuiltinCallable;
3535
import com.oracle.graal.python.annotations.Builtin;
36+
import com.oracle.graal.python.builtins.CachedLazyCalltargetSupplier;
3637
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
3738
import com.oracle.graal.python.builtins.PythonBuiltins;
3839
import com.oracle.graal.python.builtins.objects.PNone;
@@ -49,6 +50,7 @@
4950
import com.oracle.graal.python.runtime.object.PFactory;
5051
import com.oracle.graal.python.util.PythonUtils;
5152
import com.oracle.truffle.api.CompilerAsserts;
53+
import com.oracle.truffle.api.CompilerDirectives;
5254
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
5355
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
5456
import com.oracle.truffle.api.RootCallTarget;
@@ -75,15 +77,16 @@ public final class PBuiltinFunction extends PythonBuiltinObject implements Bound
7577
private final PString name;
7678
private final TruffleString qualname;
7779
private final Object enclosingType;
78-
private final RootCallTarget callTarget;
80+
@CompilationFinal private RootCallTarget callTarget;
81+
private final CachedLazyCalltargetSupplier callTargetSupplier;
7982
private final Signature signature;
8083
private final int flags;
8184
private final TpSlot slot;
8285
private final PExternalFunctionWrapper slotWrapper;
8386
@CompilationFinal(dimensions = 1) private final Object[] defaults;
8487
@CompilationFinal(dimensions = 1) private final PKeyword[] kwDefaults;
8588

86-
public PBuiltinFunction(PythonBuiltinClassType cls, Shape shape, TruffleString name, Object enclosingType, Object[] defaults, PKeyword[] kwDefaults, int flags, RootCallTarget callTarget,
89+
public PBuiltinFunction(PythonBuiltinClassType cls, Shape shape, TruffleString name, Object enclosingType, Object[] defaults, PKeyword[] kwDefaults, Signature signature, int flags, RootCallTarget callTarget, CachedLazyCalltargetSupplier callTargetSupplier,
8790
TpSlot slot, PExternalFunctionWrapper slotWrapper) {
8891
super(cls, shape);
8992
this.name = PythonUtils.toPString(name);
@@ -94,16 +97,27 @@ public PBuiltinFunction(PythonBuiltinClassType cls, Shape shape, TruffleString n
9497
}
9598
this.enclosingType = enclosingType;
9699
this.callTarget = callTarget;
97-
this.signature = ((PRootNode) callTarget.getRootNode()).getSignature();
100+
this.signature = signature;
98101
this.flags = flags;
99102
this.defaults = defaults;
100103
this.kwDefaults = kwDefaults != null ? kwDefaults : generateKwDefaults(signature);
101104
this.slot = slot;
102105
this.slotWrapper = slotWrapper;
106+
this.callTargetSupplier = callTargetSupplier;
107+
108+
/* If the call target supplier has already been run, then don't wait until the first time the InternalMethod is
109+
* asked for the call target, because can cause deoptimization in getCallTarget(). */
110+
if (callTarget == null && callTargetSupplier != null) {
111+
this.callTarget = callTargetSupplier.getIfExists();
112+
}
103113
}
104114

105115
public PBuiltinFunction(PythonBuiltinClassType cls, Shape shape, TruffleString name, Object enclosingType, Object[] defaults, PKeyword[] kwDefaults, int flags, RootCallTarget callTarget) {
106-
this(cls, shape, name, enclosingType, defaults, kwDefaults, flags, callTarget, null, null);
116+
this(cls, shape, name, enclosingType, defaults, kwDefaults, ((PRootNode) callTarget.getRootNode()).getSignature(), flags, callTarget, null, null, null);
117+
}
118+
119+
public PBuiltinFunction(PythonBuiltinClassType cls, Shape shape, TruffleString name, Object enclosingType, Object[] defaults, PKeyword[] kwDefaults, Signature signature, int flags, CachedLazyCalltargetSupplier callTargetSupplier) {
120+
this(cls, shape, name, enclosingType, defaults, kwDefaults, signature, flags, null, callTargetSupplier, null, null);
107121
}
108122

109123
public static PKeyword[] generateKwDefaults(Signature signature) {
@@ -129,7 +143,7 @@ public static Object[] generateDefaults(int numDefaults) {
129143
}
130144

131145
public RootNode getFunctionRootNode() {
132-
return callTarget.getRootNode();
146+
return getCallTarget().getRootNode();
133147
}
134148

135149
/**
@@ -211,6 +225,10 @@ public Signature getSignature() {
211225
}
212226

213227
public RootCallTarget getCallTarget() {
228+
if (callTarget == null) {
229+
CompilerDirectives.transferToInterpreterAndInvalidate();
230+
callTarget = callTargetSupplier.get();
231+
}
214232
return callTarget;
215233
}
216234

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonOptions.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,9 @@ public static void checkBytecodeDSLEnv() {
389389
@EngineOption @Option(category = OptionCategory.INTERNAL, usageSyntax = "true|false", help = "If true, uses native storage strategy for primitive types") //
390390
public static final OptionKey<Boolean> UseNativePrimitiveStorageStrategy = new OptionKey<>(false);
391391

392+
@Option(category = OptionCategory.INTERNAL, usageSyntax = "true|false", help = "If true, CallTargets for builtins are initialized lazily") //
393+
public static final OptionKey<Boolean> BuiltinLazyCallTargets = new OptionKey<>(true);
394+
392395
@Option(category = OptionCategory.EXPERT, usageSyntax = "true|false", help = "Print warnings when using experimental features at runtime.", stability = OptionStability.STABLE) //
393396
public static final OptionKey<Boolean> WarnExperimentalFeatures = new OptionKey<>(true);
394397

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/object/PFactory.java

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import javax.net.ssl.SSLEngine;
3838

3939
import com.oracle.graal.python.PythonLanguage;
40+
import com.oracle.graal.python.builtins.CachedLazyCalltargetSupplier;
4041
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
4142
import com.oracle.graal.python.builtins.modules.PosixModuleBuiltins.PosixFileHandle;
4243
import com.oracle.graal.python.builtins.modules.bz2.BZ2Object;
@@ -239,6 +240,7 @@
239240
import com.oracle.graal.python.builtins.objects.typing.PTypeVar;
240241
import com.oracle.graal.python.builtins.objects.typing.PTypeVarTuple;
241242
import com.oracle.graal.python.compiler.BytecodeCodeUnit;
243+
import com.oracle.graal.python.nodes.PRootNode;
242244
import com.oracle.graal.python.nodes.bytecode.PBytecodeRootNode;
243245
import com.oracle.graal.python.nodes.bytecode_dsl.BytecodeDSLCodeUnit;
244246
import com.oracle.graal.python.nodes.bytecode_dsl.PBytecodeDSLRootNode;
@@ -543,6 +545,11 @@ public static PFunction createFunction(PythonLanguage language, TruffleString na
543545
return trace(language, new PFunction(language, name, qualname, code, globals, defaultValues, kwDefaultValues, closure, codeStableAssumption));
544546
}
545547

548+
public static PBuiltinFunction createBuiltinFunction(PythonLanguage language, TruffleString name, int numDefaults, Signature signature, int flags, CachedLazyCalltargetSupplier callTargetSupplier) {
549+
return trace(language, new PBuiltinFunction(PythonBuiltinClassType.PBuiltinFunction, PythonBuiltinClassType.PBuiltinFunction.getInstanceShape(language), name, null,
550+
PBuiltinFunction.generateDefaults(numDefaults), null, signature, flags, callTargetSupplier));
551+
}
552+
546553
public static PBuiltinFunction createBuiltinFunction(PythonLanguage language, TruffleString name, Object type, int numDefaults, int flags, RootCallTarget callTarget) {
547554
return trace(language, new PBuiltinFunction(PythonBuiltinClassType.PBuiltinFunction, PythonBuiltinClassType.PBuiltinFunction.getInstanceShape(language), name, type,
548555
PBuiltinFunction.generateDefaults(numDefaults), null, flags, callTarget));
@@ -556,13 +563,13 @@ public static PBuiltinFunction createBuiltinFunction(PythonLanguage language, Tr
556563
public static PBuiltinFunction createWrapperDescriptor(PythonLanguage language, TruffleString name, Object type, int numDefaults, int flags, RootCallTarget callTarget, TpSlot slot,
557564
PExternalFunctionWrapper slotWrapper) {
558565
return trace(language, new PBuiltinFunction(PythonBuiltinClassType.WrapperDescriptor, PythonBuiltinClassType.WrapperDescriptor.getInstanceShape(language), name, type,
559-
PBuiltinFunction.generateDefaults(numDefaults), null, flags, callTarget, slot, slotWrapper));
566+
PBuiltinFunction.generateDefaults(numDefaults), null, ((PRootNode) callTarget.getRootNode()).getSignature(), flags, callTarget, null, slot, slotWrapper));
560567
}
561568

562569
public static PBuiltinFunction createWrapperDescriptor(PythonLanguage language, TruffleString name, Object type, Object[] defaults, PKeyword[] kw, int flags, RootCallTarget callTarget,
563570
TpSlot slot, PExternalFunctionWrapper slotWrapper) {
564-
return trace(language, new PBuiltinFunction(PythonBuiltinClassType.WrapperDescriptor, PythonBuiltinClassType.WrapperDescriptor.getInstanceShape(language), name, type, defaults, kw, flags,
565-
callTarget, slot, slotWrapper));
571+
return trace(language, new PBuiltinFunction(PythonBuiltinClassType.WrapperDescriptor, PythonBuiltinClassType.WrapperDescriptor.getInstanceShape(language), name, type, defaults, kw, ((PRootNode) callTarget.getRootNode()).getSignature(), flags,
572+
callTarget, null, slot, slotWrapper));
566573
}
567574

568575
public static PBuiltinMethod createNewWrapper(PythonLanguage language, Object type, Object[] defaults, PKeyword[] kwdefaults, RootCallTarget callTarget, TpSlot slot) {
@@ -574,7 +581,7 @@ public static PBuiltinMethod createNewWrapper(PythonLanguage language, Object ty
574581
public static PBuiltinFunction createBuiltinFunction(PythonLanguage language, PBuiltinFunction function, Object klass) {
575582
PythonBuiltinClassType type = (PythonBuiltinClassType) function.getPythonClass();
576583
return trace(language, new PBuiltinFunction(type, type.getInstanceShape(language), function.getName(), klass,
577-
function.getDefaults(), function.getKwDefaults(), function.getFlags(), function.getCallTarget(),
584+
function.getDefaults(), function.getKwDefaults(), function.getSignature(), function.getFlags(), function.getCallTarget(), null,
578585
function.getSlot(), function.getSlotWrapper()));
579586
}
580587

0 commit comments

Comments
 (0)