GeneralDivModNode.java
- /*
- * 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.core;
- import com.oracle.truffle.api.CompilerDirectives;
- import com.oracle.truffle.api.frame.VirtualFrame;
- import com.oracle.truffle.api.source.SourceSection;
- import com.oracle.truffle.api.utilities.BranchProfile;
- import org.jruby.truffle.nodes.RubyNode;
- import org.jruby.truffle.runtime.RubyContext;
- import org.jruby.truffle.runtime.core.RubyArray;
- import org.jruby.truffle.runtime.core.RubyBignum;
- import java.math.BigInteger;
- public class GeneralDivModNode extends RubyNode {
- @Child protected FixnumOrBignumNode fixnumOrBignumQuotient;
- @Child protected FixnumOrBignumNode fixnumOrBignumRemainder;
- private final BranchProfile bZeroProfile = BranchProfile.create();
- private final BranchProfile bMinusOneProfile = BranchProfile.create();
- private final BranchProfile bigIntegerFixnumProfile = BranchProfile.create();
- private final BranchProfile useFixnumPairProfile = BranchProfile.create();
- private final BranchProfile useObjectPairProfile = BranchProfile.create();
- public GeneralDivModNode(RubyContext context, SourceSection sourceSection) {
- super(context, sourceSection);
- fixnumOrBignumQuotient = new FixnumOrBignumNode(context, sourceSection);
- fixnumOrBignumRemainder = new FixnumOrBignumNode(context, sourceSection);
- }
- public RubyArray execute(int a, int b) {
- return divMod(a, b);
- }
- public RubyArray execute(int a, long b) {
- return divMod(a, b);
- }
- public RubyArray execute(int a, BigInteger b) {
- return divMod(BigInteger.valueOf(a), b);
- }
- public RubyArray execute(long a, int b) {
- return divMod(a, b);
- }
- public RubyArray execute(long a, long b) {
- return divMod(a, b);
- }
- public RubyArray execute(long a, RubyBignum b) {
- return divMod(BigInteger.valueOf(a), b.bigIntegerValue());
- }
- public RubyArray execute(RubyBignum a, int b) {
- return divMod(a.bigIntegerValue(), BigInteger.valueOf(b));
- }
- public RubyArray execute(RubyBignum a, long b) {
- return divMod(a.bigIntegerValue(), BigInteger.valueOf(b));
- }
- public RubyArray execute(RubyBignum a, RubyBignum b) {
- return divMod(a.bigIntegerValue(), b.bigIntegerValue());
- }
- /*
- * div-mod algorithms copied from org.jruby.RubyFixnum and org.jruby.RubyBignum. See license and contributors there.
- */
- @CompilerDirectives.TruffleBoundary
- private RubyArray divMod(long a, long b) {
- if (b == 0) {
- bZeroProfile.enter();
- throw new ArithmeticException("divide by zero");
- }
- long mod;
- Object integerDiv;
- if (b == -1) {
- bMinusOneProfile.enter();
- if (a == Long.MIN_VALUE) {
- integerDiv = BigInteger.valueOf(a).negate();
- } else {
- integerDiv = -a;
- }
- mod = 0;
- } else {
- long div = a / b;
- mod = a - b * div;
- if (mod < 0 && b > 0 || mod > 0 && b < 0) {
- div -= 1;
- mod += b;
- }
- integerDiv = div;
- }
- if (integerDiv instanceof Long && ((long) integerDiv) >= Integer.MIN_VALUE && ((long) integerDiv) <= Integer.MAX_VALUE && mod >= Integer.MIN_VALUE && mod <= Integer.MAX_VALUE) {
- useFixnumPairProfile.enter();
- return new RubyArray(getContext().getCoreLibrary().getArrayClass(), new int[]{(int) (long) integerDiv, (int) mod}, 2);
- } else if (integerDiv instanceof Long) {
- useObjectPairProfile.enter();
- return new RubyArray(getContext().getCoreLibrary().getArrayClass(), new Object[]{integerDiv, mod}, 2);
- } else {
- useObjectPairProfile.enter();
- return new RubyArray(getContext().getCoreLibrary().getArrayClass(), new Object[]{
- fixnumOrBignumQuotient.fixnumOrBignum(create((BigInteger) integerDiv)),
- mod}, 2);
- }
- }
- @CompilerDirectives.TruffleBoundary
- private RubyArray divMod(BigInteger a, BigInteger b) {
- if (b.signum() == 0) {
- bZeroProfile.enter();
- throw new ArithmeticException("divide by zero");
- }
- final BigInteger[] bigIntegerResults = a.divideAndRemainder(b);
- if ((a.signum() * b.signum()) == -1 && bigIntegerResults[1].signum() != 0) {
- bigIntegerFixnumProfile.enter();
- bigIntegerResults[0] = bigIntegerResults[0].subtract(BigInteger.ONE);
- bigIntegerResults[1] = b.add(bigIntegerResults[1]);
- }
- return new RubyArray(getContext().getCoreLibrary().getArrayClass(), new Object[]{
- fixnumOrBignumQuotient.fixnumOrBignum(create(bigIntegerResults[0])),
- fixnumOrBignumRemainder.fixnumOrBignum(create(bigIntegerResults[1]))}, 2);
- }
- public RubyBignum create(BigInteger value) {
- return new RubyBignum(getContext().getCoreLibrary().getBignumClass(), value);
- }
- @Override
- public Object execute(VirtualFrame frame) {
- throw new UnsupportedOperationException();
- }
- }