Skip to content

Commit b0d5098

Browse files
committed
[GR-17457] Create less nodes for WriteBarrierNode and profile RubyLibrary#isFrozen() usages
PullRequest: truffleruby/3169
2 parents 8762037 + 1247f83 commit b0d5098

File tree

7 files changed

+53
-61
lines changed

7 files changed

+53
-61
lines changed

src/main/java/org/truffleruby/core/hash/FreezeHashKeyIfNeededNode.java

Lines changed: 8 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,8 @@
1515
import org.truffleruby.core.string.ImmutableRubyString;
1616
import org.truffleruby.language.RubyBaseNode;
1717
import org.truffleruby.language.dispatch.DispatchNode;
18-
import org.truffleruby.language.library.RubyLibrary;
1918

2019
import com.oracle.truffle.api.dsl.Specialization;
21-
import com.oracle.truffle.api.library.CachedLibrary;
2220

2321
@GenerateUncached
2422
public abstract class FreezeHashKeyIfNeededNode extends RubyBaseNode {
@@ -30,31 +28,21 @@ protected Object immutable(ImmutableRubyString string, boolean compareByIdentity
3028
return string;
3129
}
3230

33-
@Specialization(
34-
guards = "rubyLibrary.isFrozen(string)",
35-
limit = "getRubyLibraryCacheLimit()")
36-
protected Object alreadyFrozen(RubyString string, boolean compareByIdentity,
37-
@CachedLibrary("string") RubyLibrary rubyLibrary) {
31+
@Specialization(guards = "string.isFrozen()")
32+
protected Object alreadyFrozen(RubyString string, boolean compareByIdentity) {
3833
return string;
3934
}
4035

41-
@Specialization(
42-
guards = { "!rubyLibrary.isFrozen(string)", "!compareByIdentity" },
43-
limit = "getRubyLibraryCacheLimit()")
36+
@Specialization(guards = { "!string.isFrozen()", "!compareByIdentity" })
4437
protected Object dupAndFreeze(RubyString string, boolean compareByIdentity,
45-
@CachedLibrary("string") RubyLibrary rubyLibrary,
46-
@CachedLibrary(limit = "getRubyLibraryCacheLimit()") RubyLibrary rubyLibraryObject,
4738
@Cached DispatchNode dupNode) {
48-
final Object object = dupNode.call(string, "dup");
49-
rubyLibraryObject.freeze(object);
50-
return object;
39+
final RubyString copy = (RubyString) dupNode.call(string, "dup");
40+
copy.freeze();
41+
return copy;
5142
}
5243

53-
@Specialization(
54-
guards = { "!rubyLibrary.isFrozen(string)", "compareByIdentity" },
55-
limit = "getRubyLibraryCacheLimit()")
56-
protected Object compareByIdentity(RubyString string, boolean compareByIdentity,
57-
@CachedLibrary("string") RubyLibrary rubyLibrary) {
44+
@Specialization(guards = { "!string.isFrozen()", "compareByIdentity" })
45+
protected Object compareByIdentity(RubyString string, boolean compareByIdentity) {
5846
return string;
5947
}
6048

src/main/java/org/truffleruby/core/kernel/KernelNodes.java

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -567,7 +567,7 @@ protected RubyDynamicObject clone(RubyDynamicObject object, Object freeze,
567567
}
568568

569569
// Default behavior - is just to copy the frozen state of the original object
570-
if (forceFrozen(freeze) || (copyFrozen && rubyLibrary.isFrozen(object))) {
570+
if (forceFrozen(freeze) || (copyFrozen && rubyLibrary.isFrozen(object))) { // Profiled through lazy usage of rubyLibraryFreeze
571571
rubyLibraryFreeze.freeze(newObject);
572572
}
573573

@@ -857,14 +857,13 @@ protected Object freeze(Object self,
857857
protected Object freezeDynamicObject(Object self,
858858
@CachedLibrary("self") RubyLibrary rubyLibrary,
859859
@CachedLibrary(limit = "1") RubyLibrary rubyLibraryMetaClass,
860-
@Cached ConditionProfile singletonProfile,
860+
@Cached ConditionProfile singletonClassUnfrozenProfile,
861861
@Cached MetaClassNode metaClassNode) {
862862
final RubyClass metaClass = metaClassNode.execute(self);
863-
if (singletonProfile.profile(metaClass.isSingleton &&
864-
!(RubyGuards.isRubyClass(self) && ((RubyClass) self).isSingleton))) {
865-
if (!rubyLibraryMetaClass.isFrozen(metaClass)) {
866-
rubyLibraryMetaClass.freeze(metaClass);
867-
}
863+
if (singletonClassUnfrozenProfile.profile(metaClass.isSingleton &&
864+
!(RubyGuards.isRubyClass(self) && ((RubyClass) self).isSingleton) &&
865+
!rubyLibraryMetaClass.isFrozen(metaClass))) {
866+
rubyLibraryMetaClass.freeze(metaClass);
868867
}
869868
rubyLibrary.freeze(self);
870869
return self;

src/main/java/org/truffleruby/core/string/RubyString.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,12 +90,12 @@ protected String getJavaString() {
9090

9191
// region RubyLibrary messages
9292
@ExportMessage
93-
protected void freeze() {
93+
public void freeze() {
9494
frozen = true;
9595
}
9696

9797
@ExportMessage
98-
protected boolean isFrozen() {
98+
public boolean isFrozen() {
9999
return frozen;
100100
}
101101
// endregion

src/main/java/org/truffleruby/language/library/RubyLibrary.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ public static RubyLibrary getUncached() {
3434

3535
public abstract void freeze(Object object);
3636

37+
/** The result is not always a PE constant, specifically for RubyString and RubyRange. For RubyDynamicObject it's
38+
* only constant if not frozen. */
3739
public abstract boolean isFrozen(Object object);
3840

3941
}

src/main/java/org/truffleruby/language/objects/WriteInstanceVariableNode.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
*/
1010
package org.truffleruby.language.objects;
1111

12+
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
1213
import org.truffleruby.RubyContext;
1314
import org.truffleruby.RubyLanguage;
1415
import org.truffleruby.core.array.AssignableNode;
@@ -31,6 +32,8 @@ public class WriteInstanceVariableNode extends RubyContextSourceNode implements
3132
@Child private RubyNode rhs;
3233
@Child private WriteObjectFieldNode writeNode;
3334

35+
@CompilationFinal private boolean frozenProfile;
36+
3437
public WriteInstanceVariableNode(String name, RubyNode receiver, RubyNode rhs) {
3538
this.name = name;
3639
this.receiver = receiver;
@@ -53,6 +56,10 @@ public void assign(VirtualFrame frame, Object value) {
5356

5457
private void write(Object object, Object value) {
5558
if (getRubyLibrary().isFrozen(object)) {
59+
if (!frozenProfile) {
60+
CompilerDirectives.transferToInterpreterAndInvalidate();
61+
frozenProfile = true;
62+
}
5663
throw new RaiseException(getContext(), coreExceptions().frozenError(object, this));
5764
}
5865

src/main/java/org/truffleruby/language/objects/WriteObjectFieldNode.java

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import com.oracle.truffle.api.dsl.Specialization;
2020
import com.oracle.truffle.api.library.CachedLibrary;
2121
import com.oracle.truffle.api.object.DynamicObjectLibrary;
22-
import com.oracle.truffle.api.profiles.BranchProfile;
2322

2423
import java.lang.invoke.VarHandle;
2524

@@ -39,11 +38,10 @@ protected void writeLocal(RubyDynamicObject object, Object name, Object value,
3938
objectLibrary.put(object, name, value);
4039
}
4140

42-
@Specialization(guards = "objectLibrary.isShared(object)", limit = "getCacheLimit()")
41+
@Specialization(guards = "objectLibrary.isShared(object)")
4342
protected void writeShared(RubyDynamicObject object, Object name, Object value,
44-
@CachedLibrary("object") DynamicObjectLibrary objectLibrary,
45-
@Cached WriteBarrierNode writeBarrierNode,
46-
@Cached BranchProfile shapeRaceProfile) {
43+
@CachedLibrary(limit = "getCacheLimit()") DynamicObjectLibrary objectLibrary,
44+
@Cached WriteBarrierNode writeBarrierNode) {
4745

4846
// Share `value` before it becomes reachable through `object`
4947
writeBarrierNode.executeWriteBarrier(value);
@@ -55,14 +53,6 @@ protected void writeShared(RubyDynamicObject object, Object name, Object value,
5553
VarHandle.storeStoreFence();
5654

5755
synchronized (object) {
58-
// Re-check the shape under the monitor as another thread might have changed it
59-
// by adding a field or upgrading an existing field to Object storage
60-
// (need to use the new storage)
61-
if (!objectLibrary.accepts(object)) {
62-
shapeRaceProfile.enter();
63-
DynamicObjectLibrary.getUncached().put(object, name, value);
64-
return;
65-
}
6656
objectLibrary.put(object, name, value);
6757
}
6858
}

src/main/java/org/truffleruby/language/objects/shared/WriteBarrierNode.java

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
@NodeField(name = "depth", type = int.class)
2626
public abstract class WriteBarrierNode extends RubyBaseNode {
2727

28-
protected static final int CACHE_LIMIT = 8;
2928
protected static final int MAX_DEPTH = 3;
3029

3130
protected abstract int getDepth();
@@ -36,39 +35,46 @@ public static WriteBarrierNode create() {
3635

3736
public abstract void executeWriteBarrier(Object value);
3837

38+
@Specialization(guards = "!isRubyDynamicObject(value)")
39+
protected void noWriteBarrier(Object value) {
40+
}
41+
3942
@Specialization(
40-
guards = { "value.getShape() == cachedShape", "getDepth() < MAX_DEPTH" },
43+
guards = { "value.getShape() == cachedShape", "cachedShape.isShared()" },
44+
limit = "1") // limit of 1 as the next specialization is cheap
45+
protected void alreadySharedCached(RubyDynamicObject value,
46+
@Cached("value.getShape()") Shape cachedShape) {
47+
}
48+
49+
@Specialization(guards = "value.getShape().isShared()", replaces = "alreadySharedCached")
50+
protected void alreadySharedUncached(RubyDynamicObject value) {
51+
}
52+
53+
@Specialization(
54+
guards = { "getDepth() < MAX_DEPTH", "value.getShape() == cachedShape", "!cachedShape.isShared()" },
4155
assumptions = "cachedShape.getValidAssumption()",
42-
limit = "CACHE_LIMIT")
56+
// limit of 1 to avoid creating many nodes if the value's Shape is polymorphic.
57+
// GR-36904: Not simply using "1" so the cached nodes are cleared when writeBarrierUncached() is activated.
58+
limit = "getIdentityCacheLimit()")
4359
protected void writeBarrierCached(RubyDynamicObject value,
4460
@Cached("value.getShape()") Shape cachedShape,
45-
@Cached("cachedShape.isShared()") boolean alreadyShared,
46-
@Cached("createShareObjectNode(alreadyShared)") ShareObjectNode shareObjectNode) {
47-
if (!alreadyShared) {
48-
shareObjectNode.executeShare(value);
49-
}
61+
@Cached("createShareObjectNode()") ShareObjectNode shareObjectNode) {
62+
shareObjectNode.executeShare(value);
5063
}
5164

5265
@Specialization(guards = "updateShape(value)")
5366
protected void updateShapeAndWriteBarrier(RubyDynamicObject value) {
5467
executeWriteBarrier(value);
5568
}
5669

57-
@Specialization(replaces = { "writeBarrierCached", "updateShapeAndWriteBarrier" })
70+
@Specialization(guards = "!value.getShape().isShared()",
71+
replaces = { "writeBarrierCached", "updateShapeAndWriteBarrier" })
5872
protected void writeBarrierUncached(RubyDynamicObject value) {
5973
SharedObjects.writeBarrier(getLanguage(), value);
6074
}
6175

62-
@Specialization(guards = "!isRubyDynamicObject(value)")
63-
protected void noWriteBarrier(Object value) {
64-
}
65-
66-
protected ShareObjectNode createShareObjectNode(boolean alreadyShared) {
67-
if (!alreadyShared) {
68-
return ShareObjectNodeGen.create(getDepth() + 1);
69-
} else {
70-
return null;
71-
}
76+
protected ShareObjectNode createShareObjectNode() {
77+
return ShareObjectNodeGen.create(getDepth() + 1);
7278
}
7379

7480
}

0 commit comments

Comments
 (0)