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: 7a7e3233de02
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 0bc371a63925
Choose a head ref
  • 3 commits
  • 10 files changed
  • 1 contributor

Commits on Feb 13, 2015

  1. [Truffle] Add Hash#invert.

    eregon committed Feb 13, 2015
    Copy the full SHA
    59c00ff View commit details
  2. [Truffle] Move Fixnum#** to Ruby for correctness.

    * Most notably a few checks for exponent < 0 were missing.
    eregon committed Feb 13, 2015
    Copy the full SHA
    71a393a View commit details
  3. [Truffle] Fix Fixnum#**(Bignum) always returning a Float.

    * This is wrong if the Bignum has a low value (which is a bug).
    * Try to be closer to MRI behavior.
    * Move Bignum#* to Ruby.
    eregon committed Feb 13, 2015
    Copy the full SHA
    0bc371a View commit details
56 changes: 11 additions & 45 deletions truffle/src/main/java/org/jruby/truffle/nodes/core/BignumNodes.java
Original file line number Diff line number Diff line change
@@ -132,7 +132,7 @@ public Object sub(RubyBignum a, RubyBignum b) {
}

@CoreMethod(names = "*", required = 1)
public abstract static class MulNode extends CoreMethodNode {
public abstract static class MulNode extends BignumCoreMethodNode {

public MulNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
@@ -143,13 +143,13 @@ public MulNode(MulNode prev) {
}

@Specialization
public RubyBignum mul(RubyBignum a, int b) {
return a.multiply(b);
public Object mul(RubyBignum a, int b) {
return fixnumOrBignum(a.multiply(b));
}

@Specialization
public RubyBignum mul(RubyBignum a, long b) {
return a.multiply(b);
public Object mul(RubyBignum a, long b) {
return fixnumOrBignum(a.multiply(b));
}

@Specialization
@@ -158,41 +158,8 @@ public double mul(RubyBignum a, double b) {
}

@Specialization
public RubyBignum mul(RubyBignum a, RubyBignum b) {
return a.multiply(b);
}

}

@CoreMethod(names = "**", required = 1)
public abstract static class PowNode extends CoreMethodNode {

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

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

@Specialization
public RubyBignum pow(RubyBignum a, int b) {
return a.pow(b);
}

@Specialization
public RubyBignum pow(RubyBignum a, long b) {
return a.pow(b);
}

@Specialization
public double pow(RubyBignum a, double b) {
return Math.pow(a.doubleValue(), b);
}

@Specialization
public RubyBignum pow(RubyBignum a, RubyBignum b) {
return a.pow(b);
public Object mul(RubyBignum a, RubyBignum b) {
return fixnumOrBignum(a.multiply(b));
}

}
@@ -214,8 +181,8 @@ public Object div(RubyBignum a, int b) {
}

@Specialization
public RubyBignum div(RubyBignum a, long b) {
return a.divide(b);
public Object div(RubyBignum a, long b) {
return fixnumOrBignum(a.divide(b));
}

@Specialization
@@ -225,7 +192,7 @@ public double div(RubyBignum a, double b) {

@Specialization
public Object div(RubyBignum a, RubyBignum b) {
return a.divide(b);
return fixnumOrBignum(a.divide(b));
}

}
@@ -739,10 +706,9 @@ public ToSNode(ToSNode prev) {
super(prev);
}

@CompilerDirectives.TruffleBoundary
@Specialization
public RubyString toS(RubyBignum value) {
return getContext().makeString(value.bigIntegerValue().toString());
return getContext().makeString(value.toString());
}

}
177 changes: 31 additions & 146 deletions truffle/src/main/java/org/jruby/truffle/nodes/core/FixnumNodes.java
Original file line number Diff line number Diff line change
@@ -222,9 +222,9 @@ public Object sub(VirtualFrame frame, int a, RubyBasicObject b) {
rationalSubNode = insert(DispatchHeadNodeFactory.createMethodCall(getContext()));
}

final Object aComplex = rationalConvertNode.call(frame, getContext().getCoreLibrary().getRationalClass(), "convert", null, a, 1);
final Object aRational = rationalConvertNode.call(frame, getContext().getCoreLibrary().getRationalClass(), "convert", null, a, 1);

return rationalSubNode.call(frame, aComplex, "-", null, b);
return rationalSubNode.call(frame, aRational, "-", null, b);
}

@Specialization(rewriteOn = ArithmeticException.class)
@@ -262,12 +262,15 @@ public Object sub(long a, RubyBignum b) {
@CoreMethod(names = "*", required = 1)
public abstract static class MulNode extends BignumNodes.BignumCoreMethodNode {

@Child private CallDispatchHeadNode rationalMulNode;

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

public MulNode(MulNode prev) {
super(prev);
rationalMulNode = prev.rationalMulNode;
}

@Specialization(rewriteOn = ArithmeticException.class)
@@ -330,162 +333,29 @@ public Object mul(long a, RubyBignum b) {
return fixnumOrBignum(bignum(a).multiply(b));
}

}

@CoreMethod(names = "**", required = 1)
public abstract static class PowNode extends BignumNodes.BignumCoreMethodNode {

@Child private CallDispatchHeadNode complexConvertNode;
@Child private CallDispatchHeadNode complexPowNode;

@Child private CallDispatchHeadNode rationalConvertNode;
@Child private CallDispatchHeadNode rationalPowNode;

private final ConditionProfile negativeProfile = ConditionProfile.createBinaryProfile();
private final ConditionProfile complexProfile = ConditionProfile.createBinaryProfile();

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

public PowNode(PowNode prev) {
super(prev);
complexConvertNode = prev.complexConvertNode;
complexPowNode = prev.complexPowNode;
rationalConvertNode = prev.rationalConvertNode;
rationalPowNode = prev.rationalPowNode;
}

@Specialization(guards = "canShiftIntoInt")
public int powTwo(int a, int b) {
return 1 << b;
}

@Specialization(guards = "canShiftIntoInt")
public int powTwo(int a, long b) {
return 1 << b;
}

@Specialization
public Object pow(int a, int b) {
return pow(a, (long) b);
}

@Specialization
public Object pow(int a, long b) {
return pow((long) a, b);
}

@Specialization
public Object pow(VirtualFrame frame, int a, double b) {
return pow(frame, (long) a, b);
}

@Specialization
public Object pow(int a, RubyBignum b) {
return pow((long) a, b);
}

@Specialization(guards = "canShiftIntoLong")
public long powTwo(long a, int b) {
return 1 << b;
}

@Specialization(guards = "canShiftIntoLong")
public long powTwo(long a, long b) {
return 1 << b;
}

@Specialization
public Object pow(long a, int b) {
return pow(a, (long) b);
}

@Specialization
public Object pow(long a, long b) {
if (negativeProfile.profile(b < 0)) {
return Math.pow(a, b);
} else {
return fixnumOrBignum(bignum(a).pow(b));
}
}

@Specialization
public Object pow(VirtualFrame frame, long a, double b) {
if (complexProfile.profile(a < 0 && b != Math.round(b))) {
if (complexConvertNode == null) {
CompilerDirectives.transferToInterpreter();
complexConvertNode = insert(DispatchHeadNodeFactory.createMethodCall(getContext(), true));
complexPowNode = insert(DispatchHeadNodeFactory.createMethodCall(getContext()));
}

final Object aComplex = complexConvertNode.call(frame, getContext().getCoreLibrary().getComplexClass(), "convert", null, a, 0);

return complexPowNode.call(frame, aComplex, "**", null, b);
} else {
return Math.pow(a, b);
}
}

@Specialization
public Object pow(long a, RubyBignum b) {
notDesignedForCompilation();

if (a == 0) {
return 0;
}

if (a == 1) {
return 1;
}

if (a == -1) {
if (b.bigIntegerValue().testBit(0)) {
return -1;
} else {
return 1;
}
}

return Math.pow(a, b.doubleValue());
@Specialization(guards = "isRational(arguments[1])")
public Object mul(VirtualFrame frame, int a, RubyBasicObject b) {
return mulRational(frame, (long) a, b);
}

@Specialization(guards = "isRational(arguments[1])")
public Object pow(VirtualFrame frame, Object a, RubyBasicObject b) {
if (rationalConvertNode == null) {
CompilerDirectives.transferToInterpreter();
rationalConvertNode = insert(DispatchHeadNodeFactory.createMethodCall(getContext(), true));
rationalPowNode = insert(DispatchHeadNodeFactory.createMethodCall(getContext()));
public Object mulRational(VirtualFrame frame, long a, RubyBasicObject b) {
if (rationalMulNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
rationalMulNode = insert(DispatchHeadNodeFactory.createMethodCall(getContext()));
}

final Object aRational = rationalConvertNode.call(frame, getContext().getCoreLibrary().getRationalClass(), "convert", null, a, 1);

return rationalPowNode.call(frame, aRational, "**", null, b);
}

protected static boolean canShiftIntoInt(int a, int b) {
return canShiftIntoInt(a, (long) b);
}

protected static boolean canShiftIntoInt(int a, long b) {
// Highest bit we can set is the 30th due to sign
return a == 2 && b <= 32 - 2;
}

protected static boolean canShiftIntoLong(long a, int b) {
return canShiftIntoLong(a, (long) b);
}

protected static boolean canShiftIntoLong(long a, long b) {
// Highest bit we can set is the 30th due to sign
return a == 2 && b <= 64 - 2;
return rationalMulNode.call(frame, b, "*", null, a);
}

}

@CoreMethod(names = {"/", "__slash__"}, required = 1)
public abstract static class DivNode extends CoreMethodNode {

@Child private CallDispatchHeadNode rationalConvertNode;
@Child private CallDispatchHeadNode rationalDivNode;

private final BranchProfile bGreaterZero = BranchProfile.create();
private final BranchProfile bGreaterZeroAGreaterEqualZero = BranchProfile.create();
private final BranchProfile bGreaterZeroALessZero = BranchProfile.create();
@@ -501,6 +371,8 @@ public DivNode(RubyContext context, SourceSection sourceSection) {

public DivNode(DivNode prev) {
super(prev);
rationalConvertNode = prev.rationalConvertNode;
rationalDivNode = prev.rationalDivNode;
}

@Specialization(rewriteOn = UnexpectedResultException.class)
@@ -661,6 +533,19 @@ public int div(@SuppressWarnings("unused") long a, @SuppressWarnings("unused") R
return 0;
}

@Specialization(guards = "isRational(arguments[1])")
public Object div(VirtualFrame frame, int a, RubyBasicObject b) {
if (rationalConvertNode == null) {
CompilerDirectives.transferToInterpreter();
rationalConvertNode = insert(DispatchHeadNodeFactory.createMethodCall(getContext(), true));
rationalDivNode = insert(DispatchHeadNodeFactory.createMethodCall(getContext()));
}

final Object aRational = rationalConvertNode.call(frame, getContext().getCoreLibrary().getRationalClass(), "convert", null, a, 1);

return rationalDivNode.call(frame, aRational, "/", null, b);
}

}

@CoreMethod(names = "%", required = 1)
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* Copyright (c) 2015 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.rubinius;

import org.jruby.truffle.runtime.RubyContext;

import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.source.SourceSection;
import com.oracle.truffle.api.utilities.ConditionProfile;

import org.jruby.truffle.runtime.core.*;

/**
* Rubinius primitives associated with the Ruby {@code Bignum} class.
*/
public abstract class BignumPrimitiveNodes {

@RubiniusPrimitive(name = "bignum_pow")
public static abstract class BignumPowPrimitiveNode extends RubiniusPrimitiveNode {

private final ConditionProfile negativeProfile = ConditionProfile.createBinaryProfile();

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

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

@Specialization
public RubyBignum pow(RubyBignum a, int b) {
return pow(a, (long) b);
}

@Specialization
public RubyBignum pow(RubyBignum a, long b) {
if (negativeProfile.profile(b < 0)) {
return null; // Primitive failure
} else {
return a.pow(b);
}
}

@Specialization
public double pow(RubyBignum a, double b) {
return Math.pow(a.doubleValue(), b);
}

@Specialization
public RubyBignum pow(RubyBignum a, RubyBignum b) {
return a.pow(b);
}

}

}
Original file line number Diff line number Diff line change
@@ -9,13 +9,22 @@
*/
package org.jruby.truffle.nodes.rubinius;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.Node.Child;
import com.oracle.truffle.api.source.SourceSection;
import com.oracle.truffle.api.utilities.ConditionProfile;

import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.nodes.core.BignumNodes;
import org.jruby.truffle.nodes.core.CoreMethod;
import org.jruby.truffle.nodes.dispatch.*;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.control.RaiseException;
import org.jruby.truffle.runtime.core.RubyArray;
import org.jruby.truffle.runtime.core.RubyBasicObject;
import org.jruby.truffle.runtime.core.RubyBignum;
import org.jruby.truffle.runtime.core.RubyString;

/**
@@ -72,4 +81,146 @@ public RubyArray coerce(VirtualFrame frame, int a, Object b) {

}

@RubiniusPrimitive(name = "fixnum_pow")
public abstract static class FixnumPowPrimitiveNode extends BignumNodes.BignumCoreMethodNode {

private final ConditionProfile negativeProfile = ConditionProfile.createBinaryProfile();
private final ConditionProfile complexProfile = ConditionProfile.createBinaryProfile();

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

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

@Specialization(guards = "canShiftIntoInt")
public int powTwo(int a, int b) {
return 1 << b;
}

@Specialization(guards = "canShiftIntoInt")
public int powTwo(int a, long b) {
return 1 << b;
}

@Specialization
public Object pow(int a, int b) {
return pow(a, (long) b);
}

@Specialization
public Object pow(int a, long b) {
return pow((long) a, b);
}

@Specialization
public Object pow(int a, double b) {
return pow((long) a, b);
}

@Specialization
public Object pow(int a, RubyBignum b) {
return pow((long) a, b);
}

@Specialization(guards = "canShiftIntoLong")
public long powTwo(long a, int b) {
return 1 << b;
}

@Specialization(guards = "canShiftIntoLong")
public long powTwo(long a, long b) {
return 1 << b;
}

@Specialization
public Object pow(long a, int b) {
return pow(a, (long) b);
}

@Specialization
public Object pow(long a, long b) {
if (negativeProfile.profile(b < 0)) {
return null; // Primitive failure
} else {
return fixnumOrBignum(bignum(a).pow(b));
}
}

@Specialization
public Object pow(long a, double b) {
if (complexProfile.profile(a < 0 && b != Math.round(b))) {
return null; // Primitive failure
} else {
return Math.pow(a, b);
}
}

@Specialization
public Object pow(long a, RubyBignum b) {
notDesignedForCompilation();

if (a == 0) {
return 0;
}

if (a == 1) {
return 1;
}

if (a == -1) {
if (b.bigIntegerValue().testBit(0)) {
return -1;
} else {
return 1;
}
}

if (b.compare(0) < 0) {
return null; // Primitive failure
}

if (b.isEqualTo(b.longValue())) {
// This is a bug of course
return pow(a, b.longValue());
}

getContext().getRuntime().getWarnings().warn("in a**b, b may be too big");
// b >= 2**63 && (a > 1 || a < -1) => larger than largest double
// MRI behavior/bug: always positive Infinity even if a negative and b odd (likely due to libc pow(a, +inf)).
return Double.POSITIVE_INFINITY;
}

@Specialization(guards = "!isRubyBignum(arguments[1])")
public Object pow(int a, RubyBasicObject b) {
return null; // Primitive failure
}

@Specialization(guards = "!isRubyBignum(arguments[1])")
public Object pow(long a, RubyBasicObject b) {
return null; // Primitive failure
}

protected static boolean canShiftIntoInt(int a, int b) {
return canShiftIntoInt(a, (long) b);
}

protected static boolean canShiftIntoInt(int a, long b) {
// Highest bit we can set is the 30th due to sign
return a == 2 && b >= 0 && b <= 32 - 2;
}

protected static boolean canShiftIntoLong(long a, int b) {
return canShiftIntoLong(a, (long) b);
}

protected static boolean canShiftIntoLong(long a, long b) {
// Highest bit we can set is the 30th due to sign
return a == 2 && b >= 0 && b <= 64 - 2;
}

}

}
Original file line number Diff line number Diff line change
@@ -48,6 +48,7 @@ public static RubiniusPrimitiveManager create() {
nodeFactories.addAll(StringPrimitiveNodesFactory.getFactories());
nodeFactories.addAll(SymbolPrimitiveNodesFactory.getFactories());
nodeFactories.addAll(FixnumPrimitiveNodesFactory.getFactories());
nodeFactories.addAll(BignumPrimitiveNodesFactory.getFactories());
nodeFactories.addAll(FloatPrimitiveNodesFactory.getFactories());
nodeFactories.addAll(EncodingPrimitiveNodesFactory.getFactories());
nodeFactories.addAll(RegexpPrimitiveNodesFactory.getFactories());
Original file line number Diff line number Diff line change
@@ -213,6 +213,12 @@ public boolean isZero() {
return value.equals(BigInteger.ZERO);
}

@CompilerDirectives.TruffleBoundary
@Override
public String toString() {
return value.toString();
}

@CompilerDirectives.TruffleBoundary
public String toHexString() {
return value.toString(16);
3 changes: 2 additions & 1 deletion truffle/src/main/ruby/core.rb
Original file line number Diff line number Diff line change
@@ -42,10 +42,11 @@
require_relative 'core/rubinius/common/type'
require_relative 'core/rubinius/common/array'
require_relative 'core/rubinius/common/kernel'
require_relative 'core/rubinius/common/identity_map'
require_relative 'core/rubinius/common/comparable'
require_relative 'core/rubinius/common/numeric'
require_relative 'core/rubinius/common/identity_map'
require_relative 'core/rubinius/common/integer'
require_relative 'core/rubinius/common/bignum'
require_relative 'core/rubinius/common/fixnum'
require_relative 'core/rubinius/common/false'
require_relative 'core/rubinius/common/float'
8 changes: 8 additions & 0 deletions truffle/src/main/ruby/core/hash.rb
Original file line number Diff line number Diff line change
@@ -8,6 +8,14 @@

class Hash

def invert
inverted = {}
each_pair { |key, value|
inverted[value] = key
}
inverted
end

def to_hash
self
end
43 changes: 43 additions & 0 deletions truffle/src/main/ruby/core/rubinius/common/bignum.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Copyright (c) 2007-2014, Evan Phoenix and contributors
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# * Neither the name of Rubinius nor the names of its contributors
# may be used to endorse or promote products derived from this software
# without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

# Only part of Rubinius' bignnum.rb

class Bignum < Integer

def **(o)
Rubinius.primitive :bignum_pow

if o.is_a?(Float) && self < 0 && o != o.round
return Complex.new(self, 0) ** o
elsif o.is_a?(Integer) && o < 0
return Rational.new(self, 1) ** o
end

redo_coerced :**, o
end

end
12 changes: 12 additions & 0 deletions truffle/src/main/ruby/core/rubinius/common/fixnum.rb
Original file line number Diff line number Diff line change
@@ -43,4 +43,16 @@ def fdiv(n)
end
end

def **(o)
Rubinius.primitive :fixnum_pow

if o.is_a?(Float) && self < 0 && o != o.round
return Complex.new(self, 0) ** o
elsif o.is_a?(Integer) && o < 0
return Rational.new(self, 1) ** o
end

redo_coerced :**, o
end

end