Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[Truffle] Refactor ReferenceEqualNode with @CreateCast.
  • Loading branch information
eregon committed Nov 6, 2014
1 parent b2341f8 commit 2a04675
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 36 deletions.
49 changes: 49 additions & 0 deletions core/src/main/java/org/jruby/truffle/nodes/cast/UnboxingNode.java
@@ -0,0 +1,49 @@
/*
* Copyright (c) 2014 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.nodes.cast;

import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.source.SourceSection;
import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.core.Unboxable;

import java.math.BigInteger;

@NodeChild("child")
public abstract class UnboxingNode extends RubyNode {

public UnboxingNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

public UnboxingNode(UnboxingNode prev) {
super(prev);
}

public abstract Object executeUnbox(VirtualFrame frame, Object value);

@Specialization public boolean unbox(boolean value) { return value; }
@Specialization public int unbox(int value) { return value; }
@Specialization public long unbox(long value) { return value; }
@Specialization public double unbox(double value) { return value; }
@Specialization public BigInteger unbox(BigInteger value) { return value; }

@Specialization
public Object unbox(Object value) {
if (value instanceof Unboxable) {
return ((Unboxable) value).unbox();
} else {
return value;
}
}
}
Expand Up @@ -20,6 +20,8 @@
import com.oracle.truffle.api.frame.VirtualFrame;
import org.jruby.runtime.Visibility;
import org.jruby.truffle.nodes.RubyCallNode;
import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.nodes.cast.UnboxingNodeFactory;
import org.jruby.truffle.nodes.dispatch.Dispatch;
import org.jruby.truffle.nodes.dispatch.DispatchHeadNode;
import org.jruby.truffle.nodes.yield.YieldDispatchHeadNode;
Expand Down Expand Up @@ -102,52 +104,51 @@ public ReferenceEqualNode(ReferenceEqualNode prev) {
super(prev);
}

public abstract boolean executeEqual(VirtualFrame frame, Object a, Object b);
// The @CreateCast is not applied when using this, so the caller needs to unbox itself.
protected abstract boolean executeEqualWithUnboxed(VirtualFrame frame, Object a, Object b);

@CreateCast("arguments") public RubyNode[] createCast(RubyNode[] arguments) {
return new RubyNode[]{
UnboxingNodeFactory.create(getContext(), getSourceSection(), arguments[0]),
UnboxingNodeFactory.create(getContext(), getSourceSection(), arguments[1])
};
}

@Specialization public boolean equal(boolean a, boolean b) { return a == b; }
@Specialization public boolean equal(int a, int b) { return a == b; }
@Specialization public boolean equal(long a, long b) { return a == b; }
@Specialization public boolean equal(double a, double b) { return a == b; }
@Specialization public boolean equal(BigInteger a, BigInteger b) { return a == b; }
@Specialization public boolean equal(BigInteger a, BigInteger b) { return a == b; } // On purpose, Bignum are not #equal?

@Specialization(guards = {"firstUnboxable", "secondUnboxable"})
public boolean equalUnboxable(Object a, Object b) {
return ((Unboxable) a).unbox().equals(((Unboxable) b).unbox());
@Specialization public boolean equal(RubyBasicObject a, RubyBasicObject b) {
assert !(a instanceof Unboxable) && !(b instanceof Unboxable);
return a == b;
}

@Specialization(guards = {"firstUnboxable", "!secondUnboxable"})
public boolean equalFirstUnboxable(Object a, Object b) {
return ((Unboxable) a).unbox().equals(b);
@Specialization(guards = {"isNotRubyBasicObject(arguments[0])", "isNotRubyBasicObject(arguments[1])", "notSameClass"})
public boolean equal(Object a, Object b) {
assert !(a instanceof Unboxable) && !(b instanceof Unboxable);
return false;
}

@Specialization(guards = {"!firstUnboxable", "secondUnboxable"})
public boolean equalSecondUnboxable(Object a, Object b) {
return a.equals(((Unboxable) b).unbox());
@Specialization(guards = "isNotRubyBasicObject(arguments[0])")
public boolean equal(Object a, RubyBasicObject b) {
assert !(b instanceof Unboxable);
return false;
}

@Specialization
public boolean equal(Object a, Object b) {
if (a instanceof Unboxable) {
if (b instanceof Unboxable) {
return ((Unboxable) a).unbox().equals(((Unboxable) b).unbox());
} else {
return ((Unboxable) a).unbox().equals(b);
}
} else {
if (b instanceof Unboxable) {
return a.equals(((Unboxable) b).unbox());
} else {
return a == b;
}
}
@Specialization(guards = "isNotRubyBasicObject(arguments[1])")
public boolean equal(RubyBasicObject a, Object b) {
assert !(a instanceof Unboxable);
return false;
}

protected boolean firstUnboxable(Object a, Object b) {
return a instanceof Unboxable;
protected boolean isNotRubyBasicObject(Object value) {
return !(value instanceof RubyBasicObject);
}

protected boolean secondUnboxable(Object a, Object b) {
return b instanceof Unboxable;
protected boolean notSameClass(Object a, Object b) {
return a.getClass() != b.getClass();
}

}
Expand Down
42 changes: 36 additions & 6 deletions core/src/main/java/org/jruby/truffle/nodes/core/KernelNodes.java
Expand Up @@ -46,28 +46,58 @@ public abstract class KernelNodes {
@CoreMethod(names = "===", required = 1)
public abstract static class SameOrEqualNode extends CoreMethodNode {

@Child protected UnboxingNode unboxLeftNode;
@Child protected UnboxingNode unboxRightNode;
@Child protected BasicObjectNodes.ReferenceEqualNode referenceEqualNode;
@Child protected DispatchHeadNode equalNode;

public SameOrEqualNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
referenceEqualNode = BasicObjectNodesFactory.ReferenceEqualNodeFactory.create(context, sourceSection, new RubyNode[]{null, null});
equalNode = new DispatchHeadNode(context);
}

public SameOrEqualNode(SameOrEqualNode prev) {
super(prev);
referenceEqualNode = prev.referenceEqualNode;
equalNode = prev.equalNode;
}

protected Object unboxLeft(VirtualFrame frame, Object value) {
if (unboxLeftNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
unboxLeftNode = insert(UnboxingNodeFactory.create(getContext(), getSourceSection(), null));
}
return unboxLeftNode.executeUnbox(frame, value);
}

protected Object unboxRight(VirtualFrame frame, Object value) {
if (unboxRightNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
unboxRightNode = insert(UnboxingNodeFactory.create(getContext(), getSourceSection(), null));
}
return unboxRightNode.executeUnbox(frame, value);
}

protected boolean areSame(VirtualFrame frame, Object left, Object right) {
if (referenceEqualNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
referenceEqualNode = insert(BasicObjectNodesFactory.ReferenceEqualNodeFactory.create(getContext(), getSourceSection(), new RubyNode[]{null, null}));
}
return referenceEqualNode.executeEqualWithUnboxed(frame, left, right);
}

protected boolean areEqual(VirtualFrame frame, Object left, Object right) {
if (equalNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
equalNode = insert(new DispatchHeadNode(getContext()));
}
return equalNode.callIsTruthy(frame, left, "==", null, right);
}

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

@Specialization
public boolean sameOrEqual(VirtualFrame frame, Object a, Object b) {
if (referenceEqualNode.executeEqual(frame, a, b))
if (areSame(frame, unboxLeft(frame, a), unboxRight(frame, b)))
return true;
return equalNode.callIsTruthy(frame, a, "==", null, b);
return areEqual(frame, a, b);
}

}
Expand Down

0 comments on commit 2a04675

Please sign in to comment.