GeneralDivModNode.java

  1. /*
  2.  * Copyright (c) 2014 Oracle and/or its affiliates. All rights reserved. This
  3.  * code is released under a tri EPL/GPL/LGPL license. You can use it,
  4.  * redistribute it and/or modify it under the terms of the:
  5.  *
  6.  * Eclipse Public License version 1.0
  7.  * GNU General Public License version 2
  8.  * GNU Lesser General Public License version 2.1
  9.  */
  10. package org.jruby.truffle.nodes.core;

  11. import com.oracle.truffle.api.CompilerDirectives;
  12. import com.oracle.truffle.api.frame.VirtualFrame;
  13. import com.oracle.truffle.api.source.SourceSection;
  14. import com.oracle.truffle.api.utilities.BranchProfile;
  15. import org.jruby.truffle.nodes.RubyNode;
  16. import org.jruby.truffle.runtime.RubyContext;
  17. import org.jruby.truffle.runtime.core.RubyArray;
  18. import org.jruby.truffle.runtime.core.RubyBignum;

  19. import java.math.BigInteger;

  20. public class GeneralDivModNode extends RubyNode {

  21.     @Child protected FixnumOrBignumNode fixnumOrBignumQuotient;
  22.     @Child protected FixnumOrBignumNode fixnumOrBignumRemainder;

  23.     private final BranchProfile bZeroProfile = BranchProfile.create();
  24.     private final BranchProfile bMinusOneProfile = BranchProfile.create();
  25.     private final BranchProfile bigIntegerFixnumProfile = BranchProfile.create();
  26.     private final BranchProfile useFixnumPairProfile = BranchProfile.create();
  27.     private final BranchProfile useObjectPairProfile = BranchProfile.create();

  28.     public GeneralDivModNode(RubyContext context, SourceSection sourceSection) {
  29.         super(context, sourceSection);
  30.         fixnumOrBignumQuotient = new FixnumOrBignumNode(context, sourceSection);
  31.         fixnumOrBignumRemainder = new FixnumOrBignumNode(context, sourceSection);
  32.     }

  33.     public RubyArray execute(int a, int b) {
  34.         return divMod(a, b);
  35.     }

  36.     public RubyArray execute(int a, long b) {
  37.         return divMod(a, b);
  38.     }

  39.     public RubyArray execute(int a, BigInteger b) {
  40.         return divMod(BigInteger.valueOf(a), b);
  41.     }

  42.     public RubyArray execute(long a, int b) {
  43.         return divMod(a, b);
  44.     }

  45.     public RubyArray execute(long a, long b) {
  46.         return divMod(a, b);
  47.     }

  48.     public RubyArray execute(long a, RubyBignum b) {
  49.         return divMod(BigInteger.valueOf(a), b.bigIntegerValue());
  50.     }

  51.     public RubyArray execute(RubyBignum a, int b) {
  52.         return divMod(a.bigIntegerValue(), BigInteger.valueOf(b));
  53.     }

  54.     public RubyArray execute(RubyBignum a, long b) {
  55.         return divMod(a.bigIntegerValue(), BigInteger.valueOf(b));
  56.     }

  57.     public RubyArray execute(RubyBignum a, RubyBignum b) {
  58.         return divMod(a.bigIntegerValue(), b.bigIntegerValue());
  59.     }

  60.     /*
  61.      * div-mod algorithms copied from org.jruby.RubyFixnum and org.jruby.RubyBignum. See license and contributors there.
  62.      */

  63.     @CompilerDirectives.TruffleBoundary
  64.     private RubyArray divMod(long a, long b) {
  65.         if (b == 0) {
  66.             bZeroProfile.enter();
  67.             throw new ArithmeticException("divide by zero");
  68.         }

  69.         long mod;
  70.         Object integerDiv;

  71.         if (b == -1) {
  72.             bMinusOneProfile.enter();

  73.             if (a == Long.MIN_VALUE) {
  74.                 integerDiv = BigInteger.valueOf(a).negate();
  75.             } else {
  76.                 integerDiv = -a;
  77.             }
  78.             mod = 0;
  79.         } else {
  80.             long div = a / b;
  81.             mod = a - b * div;
  82.             if (mod < 0 && b > 0 || mod > 0 && b < 0) {
  83.                 div -= 1;
  84.                 mod += b;
  85.             }
  86.             integerDiv = div;
  87.         }

  88.         if (integerDiv instanceof Long && ((long) integerDiv) >= Integer.MIN_VALUE && ((long) integerDiv) <= Integer.MAX_VALUE && mod >= Integer.MIN_VALUE && mod <= Integer.MAX_VALUE) {
  89.             useFixnumPairProfile.enter();
  90.             return new RubyArray(getContext().getCoreLibrary().getArrayClass(), new int[]{(int) (long) integerDiv, (int) mod}, 2);
  91.         } else if (integerDiv instanceof Long) {
  92.             useObjectPairProfile.enter();
  93.             return new RubyArray(getContext().getCoreLibrary().getArrayClass(), new Object[]{integerDiv, mod}, 2);
  94.         } else {
  95.             useObjectPairProfile.enter();
  96.             return new RubyArray(getContext().getCoreLibrary().getArrayClass(), new Object[]{
  97.                     fixnumOrBignumQuotient.fixnumOrBignum(create((BigInteger) integerDiv)),
  98.                     mod}, 2);
  99.         }
  100.     }

  101.     @CompilerDirectives.TruffleBoundary
  102.     private RubyArray divMod(BigInteger a, BigInteger b) {
  103.         if (b.signum() == 0) {
  104.             bZeroProfile.enter();
  105.             throw new ArithmeticException("divide by zero");
  106.         }

  107.         final BigInteger[] bigIntegerResults = a.divideAndRemainder(b);

  108.         if ((a.signum() * b.signum()) == -1 && bigIntegerResults[1].signum() != 0) {
  109.             bigIntegerFixnumProfile.enter();
  110.             bigIntegerResults[0] = bigIntegerResults[0].subtract(BigInteger.ONE);
  111.             bigIntegerResults[1] = b.add(bigIntegerResults[1]);
  112.         }

  113.         return new RubyArray(getContext().getCoreLibrary().getArrayClass(), new Object[]{
  114.                 fixnumOrBignumQuotient.fixnumOrBignum(create(bigIntegerResults[0])),
  115.                 fixnumOrBignumRemainder.fixnumOrBignum(create(bigIntegerResults[1]))}, 2);
  116.     }

  117.     public RubyBignum create(BigInteger value) {
  118.         return new RubyBignum(getContext().getCoreLibrary().getBignumClass(), value);
  119.     }

  120.     @Override
  121.     public Object execute(VirtualFrame frame) {
  122.         throw new UnsupportedOperationException();
  123.     }

  124. }