Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: jruby/jruby
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 0aef038c4319
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: aba18f3ff4ac
Choose a head ref
  • 8 commits
  • 8 files changed
  • 1 contributor

Commits on Nov 15, 2016

  1. Copy the full SHA
    2395792 View commit details
  2. Copy the full SHA
    6ef3980 View commit details
  3. Copy the full SHA
    a494271 View commit details
  4. Copy the full SHA
    177fc1e View commit details
  5. Copy the full SHA
    1b26386 View commit details
  6. Copy the full SHA
    9548541 View commit details
  7. Copy the full SHA
    b5c4f55 View commit details
  8. Copy the full SHA
    aba18f3 View commit details
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
* redistribute it and/or modify it under the terms of the:
*
* Eclipse Public License version 1.0
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/
package org.jruby.truffle.core.hash;

import org.jruby.truffle.core.basicobject.BasicObjectNodes.ReferenceEqualNode;
import org.jruby.truffle.core.basicobject.BasicObjectNodesFactory.ReferenceEqualNodeFactory;
import org.jruby.truffle.core.kernel.KernelNodes.SameOrEqlNode;
import org.jruby.truffle.core.kernel.KernelNodesFactory.SameOrEqlNodeFactory;
import org.jruby.truffle.language.RubyBaseNode;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.frame.VirtualFrame;

public class CompareHashKeysNode extends RubyBaseNode {

@Child private SameOrEqlNode sameOrEqlNode;
@Child private ReferenceEqualNode equalNode;

public boolean equalKeys(VirtualFrame frame, boolean compareByIdentity, Object key, int hashed, Object otherKey, int otherHashed) {
if (compareByIdentity) {
return equal(key, otherKey);
} else {
return hashed == otherHashed && sameOrEql(frame, key, otherKey);
}
}

private boolean sameOrEql(VirtualFrame frame, Object key1, Object key2) {
if (sameOrEqlNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
sameOrEqlNode = insert(SameOrEqlNodeFactory.create(null));
}
return sameOrEqlNode.executeSameOrEql(frame, key1, key2);
}

private boolean equal(Object key1, Object key2) {
if (equalNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
equalNode = insert(ReferenceEqualNodeFactory.create(null));
}
return equalNode.executeReferenceEqual(key1, key2);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Copyright (c) 2016 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
* redistribute it and/or modify it under the terms of the:
*
* Eclipse Public License version 1.0
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/
package org.jruby.truffle.core.hash;

import org.jruby.truffle.language.RubyNode;
import org.jruby.truffle.language.dispatch.CallDispatchHeadNode;
import org.jruby.truffle.language.dispatch.DispatchHeadNodeFactory;
import org.jruby.truffle.language.objects.IsFrozenNode;
import org.jruby.truffle.language.objects.IsFrozenNodeGen;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.NodeChildren;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.object.DynamicObject;

@NodeChildren({@NodeChild("key"), @NodeChild("compareByIdentity")})
public abstract class FreezeHashKeyIfNeededNode extends RubyNode {

@Child private IsFrozenNode isFrozenNode;
@Child private CallDispatchHeadNode dupNode;
@Child private CallDispatchHeadNode freezeNode;

public abstract Object executeFreezeIfNeeded(VirtualFrame frame, Object key, boolean compareByIdentity);

@Specialization(guards = {"isRubyString(string)", "isFrozen(string)"})
Object alreadyFrozen(DynamicObject string, boolean compareByIdentity) {
return string;
}

@Specialization(guards = {"isRubyString(string)", "!isFrozen(string)", "!compareByIdentity"})
Object dupAndFreeze(VirtualFrame frame, DynamicObject string, boolean compareByIdentity) {
return freeze(frame, dup(frame, string));
}

@Specialization(guards = {"isRubyString(string)", "!isFrozen(string)", "compareByIdentity"})
Object compareByIdentity(VirtualFrame frame, DynamicObject string, boolean compareByIdentity) {
return string;
}

@Specialization(guards = "!isRubyString(value)")
Object passThrough(Object value, boolean compareByIdentity) {
return value;
}

protected boolean isFrozen(Object value) {
if (isFrozenNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
isFrozenNode = insert(IsFrozenNodeGen.create(getContext(), null, null));
}
return isFrozenNode.executeIsFrozen(value);
}

private Object dup(VirtualFrame frame, Object value) {
if (dupNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
dupNode = insert(DispatchHeadNodeFactory.createMethodCall(getContext()));
}
return dupNode.call(frame, value, "dup");
}

private Object freeze(VirtualFrame frame, Object value) {
if (freezeNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
freezeNode = insert(DispatchHeadNodeFactory.createMethodCall(getContext()));
}
return freezeNode.call(frame, value, "freeze");
}

}
Original file line number Diff line number Diff line change
@@ -92,7 +92,7 @@ public static class SmallHashLiteralNode extends HashLiteralNode {

public SmallHashLiteralNode(RubyContext context, SourceSection sourceSection, RubyNode[] keyValues) {
super(context, sourceSection, keyValues);
hashNode = new HashNode(context, sourceSection);
hashNode = new HashNode();
equalNode = DispatchHeadNodeFactory.createMethodCall(context);
}

@@ -156,7 +156,7 @@ public GenericHashLiteralNode(RubyContext context, SourceSection sourceSection,
public Object execute(VirtualFrame frame) {
if (setNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
setNode = insert(SetNodeGen.create(getContext(), null, null, null, null, null));
setNode = insert(SetNode.create());
}

final int bucketsCount = BucketsStrategy.capacityGreaterThan(keyValues.length / 2) * BucketsStrategy.OVERALLOCATE_FACTOR;
18 changes: 10 additions & 8 deletions truffle/src/main/java/org/jruby/truffle/core/hash/HashNode.java
Original file line number Diff line number Diff line change
@@ -12,8 +12,7 @@
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.profiles.ConditionProfile;
import com.oracle.truffle.api.source.SourceSection;
import org.jruby.truffle.RubyContext;

import org.jruby.truffle.core.ObjectNodes.ObjectIDPrimitiveNode;
import org.jruby.truffle.core.ObjectNodesFactory.ObjectIDPrimitiveNodeFactory;
import org.jruby.truffle.language.RubyBaseNode;
@@ -28,17 +27,12 @@ public class HashNode extends RubyBaseNode {
private final ConditionProfile isIntegerProfile = ConditionProfile.createBinaryProfile();
private final ConditionProfile isLongProfile = ConditionProfile.createBinaryProfile();

public HashNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
hashNode = DispatchHeadNodeFactory.createMethodCall(context, true);
}

public int hash(VirtualFrame frame, Object key, boolean compareByIdentity) {
final Object hashedObject;
if (compareByIdentity) {
hashedObject = objectID(key);
} else {
hashedObject = hashNode.call(frame, key, "hash");
hashedObject = hash(frame, key);
}

if (isIntegerProfile.profile(hashedObject instanceof Integer)) {
@@ -50,6 +44,14 @@ public int hash(VirtualFrame frame, Object key, boolean compareByIdentity) {
}
}

private Object hash(VirtualFrame frame, Object object) {
if (hashNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
hashNode = insert(DispatchHeadNodeFactory.createMethodCall(getContext(), true));
}
return hashNode.call(frame, object, "hash");
}

private Object objectID(Object object) {
if (objectIDNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
532 changes: 191 additions & 341 deletions truffle/src/main/java/org/jruby/truffle/core/hash/HashNodes.java

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -9,32 +9,19 @@
*/
package org.jruby.truffle.core.hash;

import org.jruby.truffle.Layouts;
import org.jruby.truffle.language.RubyBaseNode;

import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.profiles.ConditionProfile;
import com.oracle.truffle.api.source.SourceSection;
import org.jruby.truffle.Layouts;
import org.jruby.truffle.RubyContext;
import org.jruby.truffle.core.basicobject.BasicObjectNodes;
import org.jruby.truffle.core.basicobject.BasicObjectNodesFactory;
import org.jruby.truffle.language.RubyBaseNode;
import org.jruby.truffle.language.dispatch.CallDispatchHeadNode;
import org.jruby.truffle.language.dispatch.DispatchHeadNodeFactory;

public class LookupEntryNode extends RubyBaseNode {

@Child HashNode hashNode;
@Child CallDispatchHeadNode eqlNode;
@Child BasicObjectNodes.ReferenceEqualNode equalNode;

private final ConditionProfile byIdentityProfile = ConditionProfile.createBinaryProfile();
@Child HashNode hashNode = new HashNode();
@Child CompareHashKeysNode compareHashKeysNode = new CompareHashKeysNode();

public LookupEntryNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
hashNode = new HashNode(context, sourceSection);
eqlNode = DispatchHeadNodeFactory.createMethodCall(context);
equalNode = BasicObjectNodesFactory.ReferenceEqualNodeFactory.create(null);
}
private final ConditionProfile byIdentityProfile = ConditionProfile.createBinaryProfile();

public HashLookupResult lookup(VirtualFrame frame, DynamicObject hash, Object key) {
final boolean compareByIdentity = byIdentityProfile.profile(Layouts.HASH.getCompareByIdentity(hash));
@@ -47,14 +34,8 @@ public HashLookupResult lookup(VirtualFrame frame, DynamicObject hash, Object ke
Entry previousEntry = null;

while (entry != null) {
if (compareByIdentity) {
if (equalNode.executeReferenceEqual(key, entry.getKey())) {
return new HashLookupResult(hashed, index, previousEntry, entry);
}
} else {
if (eqlNode.callBoolean(frame, key, "eql?", null, entry.getKey())) {
return new HashLookupResult(hashed, index, previousEntry, entry);
}
if (equalKeys(frame, compareByIdentity, key, hashed, entry.getKey(), entry.getHashed())) {
return new HashLookupResult(hashed, index, previousEntry, entry);
}

previousEntry = entry;
@@ -64,4 +45,8 @@ public HashLookupResult lookup(VirtualFrame frame, DynamicObject hash, Object ke
return new HashLookupResult(hashed, index, previousEntry, null);
}

protected boolean equalKeys(VirtualFrame frame, boolean compareByIdentity, Object key, int hashed, Object otherKey, int otherHashed) {
return compareHashKeysNode.equalKeys(frame, compareByIdentity, key, hashed, otherKey, otherHashed);
}

}
156 changes: 40 additions & 116 deletions truffle/src/main/java/org/jruby/truffle/core/hash/SetNode.java
Original file line number Diff line number Diff line change
@@ -9,7 +9,11 @@
*/
package org.jruby.truffle.core.hash;

import org.jruby.truffle.Layouts;
import org.jruby.truffle.language.RubyNode;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.ImportStatic;
import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.NodeChildren;
@@ -19,16 +23,6 @@
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.profiles.BranchProfile;
import com.oracle.truffle.api.profiles.ConditionProfile;
import com.oracle.truffle.api.source.SourceSection;
import org.jruby.truffle.Layouts;
import org.jruby.truffle.RubyContext;
import org.jruby.truffle.core.basicobject.BasicObjectNodes;
import org.jruby.truffle.core.basicobject.BasicObjectNodesFactory;
import org.jruby.truffle.language.RubyNode;
import org.jruby.truffle.language.dispatch.CallDispatchHeadNode;
import org.jruby.truffle.language.dispatch.DispatchHeadNodeFactory;
import org.jruby.truffle.language.objects.IsFrozenNode;
import org.jruby.truffle.language.objects.IsFrozenNodeGen;

@ImportStatic(HashGuards.class)
@NodeChildren({
@@ -39,35 +33,25 @@
})
public abstract class SetNode extends RubyNode {

@Child private HashNode hashNode;
@Child private CallDispatchHeadNode eqlNode;
@Child private BasicObjectNodes.ReferenceEqualNode equalNode;
@Child private HashNode hashNode = new HashNode();
@Child private LookupEntryNode lookupEntryNode;
@Child private CompareHashKeysNode compareHashKeysNode = new CompareHashKeysNode();
@Child private FreezeHashKeyIfNeededNode freezeHashKeyIfNeededNode = FreezeHashKeyIfNeededNodeGen.create(null, null);

private final ConditionProfile byIdentityProfile = ConditionProfile.createBinaryProfile();

private final BranchProfile extendProfile = BranchProfile.create();
private final ConditionProfile strategyProfile = ConditionProfile.createBinaryProfile();

@Child private IsFrozenNode isFrozenNode;
@Child private CallDispatchHeadNode dupNode;
@Child private CallDispatchHeadNode freezeNode;

private final ConditionProfile frozenProfile = ConditionProfile.createBinaryProfile();

public SetNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
hashNode = new HashNode(context, sourceSection);
eqlNode = DispatchHeadNodeFactory.createMethodCall(context);
equalNode = BasicObjectNodesFactory.ReferenceEqualNodeFactory.create(null);
public static SetNode create() {
return SetNodeGen.create(null, null, null, null);
}

public abstract Object executeSet(VirtualFrame frame, DynamicObject hash, Object key, Object value, boolean byIdentity);

@Specialization(guards = { "isNullHash(hash)", "!isRubyString(key)" })
public Object setNull(VirtualFrame frame, DynamicObject hash, Object key, Object value, boolean byIdentity) {
boolean profiledByIdentity = byIdentityProfile.profile(byIdentity);
final int hashed = hashNode.hash(frame, key, profiledByIdentity);
@Specialization(guards = "isNullHash(hash)")
public Object setNull(VirtualFrame frame, DynamicObject hash, Object originalKey, Object value, boolean byIdentity,
@Cached("createBinaryProfile()") ConditionProfile byIdentityProfile) {
assert HashOperations.verifyStore(getContext(), hash);
boolean compareByIdentity = byIdentityProfile.profile(byIdentity);
final Object key = freezeHashKeyIfNeededNode.executeFreezeIfNeeded(frame, originalKey, compareByIdentity);

final int hashed = hashNode.hash(frame, key, compareByIdentity);

Object store = PackedArrayStrategy.createStore(getContext(), hashed, key, value);
assert HashOperations.verifyStore(getContext(), store, 1, null, null);
@@ -80,39 +64,26 @@ public Object setNull(VirtualFrame frame, DynamicObject hash, Object key, Object
return value;
}

@Specialization(guards = {"isNullHash(hash)", "byIdentity", "isRubyString(key)"})
public Object setNullByIdentity(VirtualFrame frame, DynamicObject hash, DynamicObject key, Object value, boolean byIdentity) {
return setNull(frame, hash, key, value, byIdentity);
}

@Specialization(guards = {"isNullHash(hash)", "!byIdentity", "isRubyString(key)"})
public Object setNullNotByIdentity(VirtualFrame frame, DynamicObject hash, DynamicObject key, Object value, boolean byIdentity) {
return setNull(frame, hash, freezeAndDupIfNeeded(frame, key), value, byIdentity);
}

@ExplodeLoop
@Specialization(guards = {"isPackedHash(hash)", "!isRubyString(key)"})
public Object setPackedArray(VirtualFrame frame, DynamicObject hash, Object key, Object value, boolean byIdentity) {
@Specialization(guards = "isPackedHash(hash)")
public Object setPackedArray(VirtualFrame frame, DynamicObject hash, Object originalKey, Object value, boolean byIdentity,
@Cached("createBinaryProfile()") ConditionProfile byIdentityProfile,
@Cached("createBinaryProfile()") ConditionProfile strategyProfile,
@Cached("create()") BranchProfile extendProfile) {
assert HashOperations.verifyStore(getContext(), hash);
final boolean compareByIdentity = byIdentityProfile.profile(byIdentity);
final Object key = freezeHashKeyIfNeededNode.executeFreezeIfNeeded(frame, originalKey, compareByIdentity);

boolean profiledByIdentity = byIdentityProfile.profile(byIdentity);

int hashed = hashNode.hash(frame, key, profiledByIdentity);
final int hashed = hashNode.hash(frame, key, compareByIdentity);

final Object[] store = (Object[]) Layouts.HASH.getStore(hash);
final int size = Layouts.HASH.getSize(hash);

for (int n = 0; n < getContext().getOptions().HASH_PACKED_ARRAY_MAX; n++) {
if (n < size) {
final boolean equal;
if (profiledByIdentity) {
equal = equalNode.executeReferenceEqual(key, PackedArrayStrategy.getKey(store, n));
} else {
equal = hashed == PackedArrayStrategy.getHashed(store, n) &&
eqlNode.callBoolean(frame, key, "eql?", null, PackedArrayStrategy.getKey(store, n));
}

if (equal) {
final int otherHashed = PackedArrayStrategy.getHashed(store, n);
final Object otherKey = PackedArrayStrategy.getKey(store, n);
if (equalKeys(frame, compareByIdentity, key, hashed, otherKey, otherHashed)) {
PackedArrayStrategy.setValue(store, n, value);
assert HashOperations.verifyStore(getContext(), hash);
return value;
@@ -136,25 +107,16 @@ public Object setPackedArray(VirtualFrame frame, DynamicObject hash, Object key,
return value;
}

@Specialization(guards = {"isPackedHash(hash)", "byIdentity", "isRubyString(key)"})
public Object setPackedArrayByIdentity(VirtualFrame frame, DynamicObject hash, DynamicObject key, Object value, boolean byIdentity) {
return setPackedArray(frame, hash, key, value, byIdentity);
}

@Specialization(guards = {"isPackedHash(hash)", "!byIdentity", "isRubyString(key)"})
public Object setPackedArrayNotByIdentity(VirtualFrame frame, DynamicObject hash, DynamicObject key, Object value, boolean byIdentity) {
return setPackedArray(frame, hash, freezeAndDupIfNeeded(frame, key), value, byIdentity);
}

// Can't be @Cached yet as we call from the RubyString specialisation
private final ConditionProfile foundProfile = ConditionProfile.createBinaryProfile();
private final ConditionProfile bucketCollisionProfile = ConditionProfile.createBinaryProfile();
private final ConditionProfile appendingProfile = ConditionProfile.createBinaryProfile();
private final ConditionProfile resizeProfile = ConditionProfile.createBinaryProfile();

@Specialization(guards = {"isBucketHash(hash)", "!isRubyString(key)"})
public Object setBuckets(VirtualFrame frame, DynamicObject hash, Object key, Object value, boolean byIdentity) {
@Specialization(guards = "isBucketHash(hash)")
public Object setBuckets(VirtualFrame frame, DynamicObject hash, Object originalKey, Object value, boolean byIdentity,
@Cached("createBinaryProfile()") ConditionProfile byIdentityProfile,
@Cached("createBinaryProfile()") ConditionProfile foundProfile,
@Cached("createBinaryProfile()") ConditionProfile bucketCollisionProfile,
@Cached("createBinaryProfile()") ConditionProfile appendingProfile,
@Cached("createBinaryProfile()") ConditionProfile resizeProfile) {
assert HashOperations.verifyStore(getContext(), hash);
final boolean compareByIdentity = byIdentityProfile.profile(byIdentity);
final Object key = freezeHashKeyIfNeededNode.executeFreezeIfNeeded(frame, originalKey, compareByIdentity);

final HashLookupResult result = lookup(frame, hash, key);
final Entry entry = result.getEntry();
@@ -202,51 +164,13 @@ public Object setBuckets(VirtualFrame frame, DynamicObject hash, Object key, Obj
private HashLookupResult lookup(VirtualFrame frame, DynamicObject hash, Object key) {
if (lookupEntryNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
lookupEntryNode = insert(new LookupEntryNode(getContext(), null));
lookupEntryNode = insert(new LookupEntryNode());
}
return lookupEntryNode.lookup(frame, hash, key);
}

@Specialization(guards = {"isBucketHash(hash)", "byIdentity", "isRubyString(key)"})
public Object setBucketsByIdentity(VirtualFrame frame, DynamicObject hash, DynamicObject key, Object value, boolean byIdentity) {
return setBuckets(frame, hash, key, value, byIdentity);
}

@Specialization(guards = {"isBucketHash(hash)", "!byIdentity", "isRubyString(key)"})
public Object setBucketsNotByIdentity(VirtualFrame frame, DynamicObject hash, DynamicObject key, Object value, boolean byIdentity) {
return setBuckets(frame, hash, freezeAndDupIfNeeded(frame, key), value, byIdentity);
}

private Object freezeAndDupIfNeeded(VirtualFrame frame, DynamicObject key) {
if (!frozenProfile.profile(isFrozen(key))) {
return freeze(frame, dup(frame, key));
} else {
return key;
}
}

private boolean isFrozen(Object value) {
if (isFrozenNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
isFrozenNode = insert(IsFrozenNodeGen.create(getContext(), null, null));
}
return isFrozenNode.executeIsFrozen(value);
}

private Object dup(VirtualFrame frame, Object value) {
if (dupNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
dupNode = insert(DispatchHeadNodeFactory.createMethodCall(getContext()));
}
return dupNode.call(frame, value, "dup");
}

private Object freeze(VirtualFrame frame, Object value) {
if (freezeNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
freezeNode = insert(DispatchHeadNodeFactory.createMethodCall(getContext()));
}
return freezeNode.call(frame, value, "freeze");
protected boolean equalKeys(VirtualFrame frame, boolean compareByIdentity, Object key, int hashed, Object otherKey, int otherHashed) {
return compareHashKeysNode.equalKeys(frame, compareByIdentity, key, hashed, otherKey, otherHashed);
}

}
Original file line number Diff line number Diff line change
@@ -317,6 +317,35 @@ private boolean areEqual(VirtualFrame frame, Object left, Object right) {

}

/** Check if operands are the same object or call #eql? */
public abstract static class SameOrEqlNode extends CoreMethodArrayArgumentsNode {

@Child private CallDispatchHeadNode eqlNode;

private final ConditionProfile sameProfile = ConditionProfile.createBinaryProfile();

public abstract boolean executeSameOrEql(VirtualFrame frame, Object a, Object b);

@Specialization
public boolean sameOrEql(VirtualFrame frame, Object a, Object b,
@Cached("create()") ReferenceEqualNode referenceEqualNode) {
if (sameProfile.profile(referenceEqualNode.executeReferenceEqual(a, b))) {
return true;
} else {
return areEql(frame, a, b);
}
}

private boolean areEql(VirtualFrame frame, Object left, Object right) {
if (eqlNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
eqlNode = insert(DispatchHeadNodeFactory.createMethodCall(getContext()));
}
return eqlNode.callBoolean(frame, left, "eql?", null, right);
}

}

@CoreMethod(names = { "<=>" }, required = 1)
public abstract static class CompareNode extends CoreMethodArrayArgumentsNode {