Skip to content

Commit 3dfcaa0

Browse files
committed
[GR-19220] If a call target supplier is fulfilled, just use it (#2686)
PullRequest: truffleruby/3411
2 parents 99825c7 + 56458ee commit 3dfcaa0

File tree

6 files changed

+68
-53
lines changed

6 files changed

+68
-53
lines changed

src/main/java/org/truffleruby/builtins/CoreMethodNodeManager.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
import com.oracle.truffle.api.TruffleSafepoint;
1717
import org.truffleruby.RubyContext;
1818
import org.truffleruby.RubyLanguage;
19-
import org.truffleruby.collections.CachedSupplier;
19+
import org.truffleruby.language.methods.CachedLazyCallTargetSupplier;
2020
import org.truffleruby.core.CoreLibrary;
2121
import org.truffleruby.core.DummyNode;
2222
import org.truffleruby.core.array.ArrayUtils;
@@ -253,7 +253,7 @@ private static void addMethod(
253253
arity);
254254

255255
final RootCallTarget callTarget;
256-
final CachedSupplier<RootCallTarget> callTargetSupplier;
256+
final CachedLazyCallTargetSupplier callTargetSupplier;
257257
final NodeFactory<? extends RubyBaseNode> alwaysInlinedNodeFactory;
258258
if (alwaysInlined) {
259259
callTarget = callTargetFactory.apply(sharedMethodInfo);
@@ -262,7 +262,8 @@ private static void addMethod(
262262
} else {
263263
if (context.getLanguageSlow().options.LAZY_CALLTARGETS) {
264264
callTarget = null;
265-
callTargetSupplier = new CachedSupplier<>(() -> callTargetFactory.apply(sharedMethodInfo));
265+
callTargetSupplier = new CachedLazyCallTargetSupplier(
266+
() -> callTargetFactory.apply(sharedMethodInfo));
266267
} else {
267268
callTarget = callTargetFactory.apply(sharedMethodInfo);
268269
callTargetSupplier = null;

src/main/java/org/truffleruby/collections/CachedSupplier.java

Lines changed: 0 additions & 36 deletions
This file was deleted.
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright (c) 2020, 2022 Oracle and/or its affiliates. All rights reserved. This
3+
* code is released under a tri EPL/GPL/LGPL license. You can use it,
4+
* redistribute it and/or modify it under the terms of the:
5+
*
6+
* Eclipse Public License version 2.0, or
7+
* GNU General Public License version 2, or
8+
* GNU Lesser General Public License version 2.1.
9+
*/
10+
package org.truffleruby.language.methods;
11+
12+
import com.oracle.truffle.api.CompilerAsserts;
13+
import com.oracle.truffle.api.RootCallTarget;
14+
15+
import java.util.function.Supplier;
16+
17+
public class CachedLazyCallTargetSupplier {
18+
19+
// Volatile, so that writes from another thread will finish publishing the RootCallTarget first
20+
private volatile RootCallTarget callTarget = null;
21+
private Supplier<RootCallTarget> supplier;
22+
23+
public CachedLazyCallTargetSupplier(Supplier<RootCallTarget> supplier) {
24+
this.supplier = supplier;
25+
}
26+
27+
public RootCallTarget get() {
28+
CompilerAsserts.neverPartOfCompilation("Only behind a transfer, must not PE the Supplier");
29+
30+
RootCallTarget alreadySet = this.callTarget;
31+
if (alreadySet != null) {
32+
return alreadySet;
33+
}
34+
35+
synchronized (this) {
36+
if (callTarget == null) {
37+
callTarget = supplier.get();
38+
supplier = null;
39+
}
40+
return callTarget;
41+
}
42+
}
43+
44+
public RootCallTarget getWhenAvailable() {
45+
return callTarget;
46+
}
47+
48+
}

src/main/java/org/truffleruby/language/methods/InternalMethod.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
1616
import com.oracle.truffle.api.dsl.NodeFactory;
1717
import org.truffleruby.RubyContext;
18-
import org.truffleruby.collections.CachedSupplier;
1918
import org.truffleruby.core.klass.RubyClass;
2019
import org.truffleruby.core.module.RubyModule;
2120
import org.truffleruby.core.proc.RubyProc;
@@ -53,7 +52,7 @@ public class InternalMethod implements ObjectGraphNode {
5352
public final NodeFactory<? extends RubyBaseNode> alwaysInlinedNodeFactory;
5453
private final RubyProc proc; // only if method is created from a Proc
5554

56-
private final CachedSupplier<RootCallTarget> callTargetSupplier;
55+
private final CachedLazyCallTargetSupplier callTargetSupplier;
5756
@CompilationFinal private RootCallTarget callTarget;
5857

5958
public static InternalMethod fromProc(
@@ -117,7 +116,7 @@ public InternalMethod(
117116
NodeFactory<? extends RubyBaseNode> alwaysInlined,
118117
RubyProc proc,
119118
RootCallTarget callTarget,
120-
CachedSupplier<RootCallTarget> callTargetSupplier) {
119+
CachedLazyCallTargetSupplier callTargetSupplier) {
121120
this(
122121
sharedMethodInfo,
123122
lexicalScope,
@@ -151,7 +150,7 @@ private InternalMethod(
151150
DeclarationContext activeRefinements,
152151
RubyProc proc,
153152
RootCallTarget callTarget,
154-
CachedSupplier<RootCallTarget> callTargetSupplier) {
153+
CachedLazyCallTargetSupplier callTargetSupplier) {
155154
assert declaringModule != null;
156155
assert lexicalScope != null;
157156
assert !sharedMethodInfo.isBlock() : sharedMethodInfo;
@@ -171,6 +170,12 @@ private InternalMethod(
171170
this.proc = proc;
172171
this.callTarget = callTarget;
173172
this.callTargetSupplier = callTargetSupplier;
173+
174+
/* If the call target supplier has already been run, then don't wait until the first time the InternalMethod is
175+
* asked for the call target, because this would be a deoptimization in getCallTarget(). */
176+
if (callTarget == null && callTargetSupplier != null) {
177+
this.callTarget = callTargetSupplier.getWhenAvailable();
178+
}
174179
}
175180

176181
public SharedMethodInfo getSharedMethodInfo() {

src/main/java/org/truffleruby/language/methods/LiteralMethodDefinitionNode.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,12 @@
1313
import com.oracle.truffle.api.instrumentation.InstrumentableNode;
1414
import com.oracle.truffle.api.instrumentation.StandardTags;
1515
import com.oracle.truffle.api.instrumentation.Tag;
16-
import org.truffleruby.collections.CachedSupplier;
1716
import org.truffleruby.core.module.RubyModule;
1817
import org.truffleruby.language.RubyContextSourceNode;
1918
import org.truffleruby.language.RubyNode;
2019
import org.truffleruby.language.Visibility;
2120
import org.truffleruby.language.arguments.RubyArguments;
2221

23-
import com.oracle.truffle.api.RootCallTarget;
2422
import com.oracle.truffle.api.frame.VirtualFrame;
2523

2624
import java.util.Set;
@@ -32,7 +30,7 @@ public class LiteralMethodDefinitionNode extends RubyContextSourceNode {
3230
private final String name;
3331
private final SharedMethodInfo sharedMethodInfo;
3432
private final boolean isDefSingleton;
35-
private final CachedSupplier<RootCallTarget> callTargetSupplier;
33+
private final CachedLazyCallTargetSupplier callTargetSupplier;
3634

3735
@Child private RubyNode moduleNode;
3836

@@ -41,7 +39,7 @@ public LiteralMethodDefinitionNode(
4139
String name,
4240
SharedMethodInfo sharedMethodInfo,
4341
boolean isDefSingleton,
44-
CachedSupplier<RootCallTarget> callTargetSupplier) {
42+
CachedLazyCallTargetSupplier callTargetSupplier) {
4543
this.name = name;
4644
this.sharedMethodInfo = sharedMethodInfo;
4745
this.isDefSingleton = isDefSingleton;

src/main/java/org/truffleruby/parser/MethodTranslator.java

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
import java.util.function.Supplier;
1414

1515
import org.truffleruby.RubyLanguage;
16-
import org.truffleruby.collections.CachedSupplier;
16+
import org.truffleruby.language.methods.CachedLazyCallTargetSupplier;
1717
import org.truffleruby.core.IsNilNode;
1818
import org.truffleruby.core.cast.SplatCastNode;
1919
import org.truffleruby.core.cast.SplatCastNode.NilBehavior;
@@ -418,16 +418,15 @@ private RubyMethodRootNode translateMethodNode(SourceIndexLength sourceSection,
418418
argsNode.getArity());
419419
}
420420

421-
public CachedSupplier<RootCallTarget> buildMethodNodeCompiler(SourceIndexLength sourceSection,
421+
public CachedLazyCallTargetSupplier buildMethodNodeCompiler(SourceIndexLength sourceSection,
422422
MethodDefParseNode defNode, ParseNode bodyNode) {
423423

424424
if (shouldLazyTranslate) {
425-
return new CachedSupplier<>(() -> {
426-
return translateMethodNode(sourceSection, defNode, bodyNode).getCallTarget();
427-
});
425+
return new CachedLazyCallTargetSupplier(
426+
() -> translateMethodNode(sourceSection, defNode, bodyNode).getCallTarget());
428427
} else {
429428
final RubyMethodRootNode root = translateMethodNode(sourceSection, defNode, bodyNode);
430-
return new CachedSupplier<>(() -> root.getCallTarget());
429+
return new CachedLazyCallTargetSupplier(() -> root.getCallTarget());
431430
}
432431
}
433432

0 commit comments

Comments
 (0)