8061957: Some arithmetic operations have unnecessary widening
authorattila
Mon, 03 Nov 2014 07:28:08 +0100
changeset 27358 8898e06e68c1
parent 27357 8caab9a29b62
child 27359 59d61197a3f3
8061957: Some arithmetic operations have unnecessary widening Reviewed-by: hannesw, lagergren
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CodeGenerator.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/types/IntType.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/types/LongType.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/JSType.java
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CodeGenerator.java	Fri Oct 31 16:50:31 2014 +0100
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CodeGenerator.java	Mon Nov 03 07:28:08 2014 +0100
@@ -3569,7 +3569,8 @@
                     operandBounds = new TypeBounds(binaryNode.getType(), Type.OBJECT);
                 } else {
                     // Non-optimistic, non-FP +. Allow it to overflow.
-                    operandBounds = new TypeBounds(binaryNode.getWidestOperandType(), Type.OBJECT);
+                    operandBounds = new TypeBounds(Type.narrowest(binaryNode.getWidestOperandType(), resultBounds.widest),
+                            Type.OBJECT);
                     forceConversionSeparation = binaryNode.getWidestOperationType().narrowerThan(resultBounds.widest);
                 }
                 loadBinaryOperands(binaryNode.lhs(), binaryNode.rhs(), operandBounds, false, forceConversionSeparation);
@@ -3856,12 +3857,8 @@
                         operandBounds = numericBounds;
                     } else {
                         final boolean isOptimistic = isValid(getProgramPoint());
-                        if(isOptimistic) {
+                        if(isOptimistic || node.isTokenType(TokenType.DIV) || node.isTokenType(TokenType.MOD)) {
                             operandBounds = new TypeBounds(node.getType(), Type.NUMBER);
-                        } else if(node.isTokenType(TokenType.DIV) || node.isTokenType(TokenType.MOD)) {
-                            // Non-optimistic division must always take double arguments as its result must also be
-                            // double.
-                            operandBounds = TypeBounds.NUMBER;
                         } else {
                             // Non-optimistic, non-FP subtraction or multiplication. Allow them to overflow.
                             operandBounds = new TypeBounds(Type.narrowest(node.getWidestOperandType(),
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/types/IntType.java	Fri Oct 31 16:50:31 2014 +0100
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/types/IntType.java	Mon Nov 03 07:28:08 2014 +0100
@@ -55,6 +55,7 @@
 
 import jdk.internal.org.objectweb.asm.MethodVisitor;
 import jdk.nashorn.internal.codegen.CompilerConstants;
+import jdk.nashorn.internal.runtime.JSType;
 
 /**
  * Type class: INT
@@ -230,19 +231,21 @@
 
     @Override
     public Type div(final MethodVisitor method, final int programPoint) {
-        // Never perform non-optimistic integer division in JavaScript.
-        assert programPoint != INVALID_PROGRAM_POINT;
-
-        method.visitInvokeDynamicInsn("idiv", "(II)I", MATHBOOTSTRAP, programPoint);
+        if (programPoint == INVALID_PROGRAM_POINT) {
+            JSType.DIV_ZERO.invoke(method);
+        } else {
+            method.visitInvokeDynamicInsn("idiv", "(II)I", MATHBOOTSTRAP, programPoint);
+        }
         return INT;
     }
 
     @Override
     public Type rem(final MethodVisitor method, final int programPoint) {
-        // Never perform non-optimistic integer remainder in JavaScript.
-        assert programPoint != INVALID_PROGRAM_POINT;
-
-        method.visitInvokeDynamicInsn("irem", "(II)I", MATHBOOTSTRAP, programPoint);
+        if (programPoint == INVALID_PROGRAM_POINT) {
+            JSType.REM_ZERO.invoke(method);
+        } else {
+            method.visitInvokeDynamicInsn("irem", "(II)I", MATHBOOTSTRAP, programPoint);
+        }
         return INT;
     }
 
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/types/LongType.java	Fri Oct 31 16:50:31 2014 +0100
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/types/LongType.java	Mon Nov 03 07:28:08 2014 +0100
@@ -170,19 +170,21 @@
 
     @Override
     public Type div(final MethodVisitor method, final int programPoint) {
-        // Never perform non-optimistic integer division in JavaScript.
-        assert programPoint != INVALID_PROGRAM_POINT;
-
-        method.visitInvokeDynamicInsn("ldiv", "(JJ)J", MATHBOOTSTRAP, programPoint);
+        if (programPoint == INVALID_PROGRAM_POINT) {
+            JSType.DIV_ZERO_LONG.invoke(method);
+        } else {
+            method.visitInvokeDynamicInsn("ldiv", "(JJ)J", MATHBOOTSTRAP, programPoint);
+        }
         return LONG;
     }
 
     @Override
     public Type rem(final MethodVisitor method, final int programPoint) {
-        // Never perform non-optimistic integer remainder in JavaScript.
-        assert programPoint != INVALID_PROGRAM_POINT;
-
-        method.visitInvokeDynamicInsn("lrem", "(JJ)J", MATHBOOTSTRAP, programPoint);
+        if (programPoint == INVALID_PROGRAM_POINT) {
+            JSType.REM_ZERO_LONG.invoke(method);
+        } else {
+            method.visitInvokeDynamicInsn("lrem", "(JJ)J", MATHBOOTSTRAP, programPoint);
+        }
         return LONG;
     }
 
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/JSType.java	Fri Oct 31 16:50:31 2014 +0100
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/JSType.java	Mon Nov 03 07:28:08 2014 +0100
@@ -150,6 +150,12 @@
     /** Div exact wrapper for potentially integer division that turns into float point */
     public static final Call DIV_EXACT       = staticCall(JSTYPE_LOOKUP, JSType.class, "divExact", int.class, int.class, int.class, int.class);
 
+    /** Div zero wrapper for integer division that handles (0/0)|0 == 0 */
+    public static final Call DIV_ZERO        = staticCall(JSTYPE_LOOKUP, JSType.class, "divZero", int.class, int.class, int.class);
+
+    /** Mod zero wrapper for integer division that handles (0%0)|0 == 0 */
+    public static final Call REM_ZERO        = staticCall(JSTYPE_LOOKUP, JSType.class, "remZero", int.class, int.class, int.class);
+
     /** Mod exact wrapper for potentially integer remainders that turns into float point */
     public static final Call REM_EXACT       = staticCall(JSTYPE_LOOKUP, JSType.class, "remExact", int.class, int.class, int.class, int.class);
 
@@ -174,6 +180,12 @@
     /** Div exact wrapper for potentially integer division that turns into float point */
     public static final Call DIV_EXACT_LONG       = staticCall(JSTYPE_LOOKUP, JSType.class, "divExact", long.class, long.class, long.class, int.class);
 
+    /** Div zero wrapper for long division that handles (0/0) >>> 0 == 0 */
+    public static final Call DIV_ZERO_LONG        = staticCall(JSTYPE_LOOKUP, JSType.class, "divZero", long.class, long.class, long.class);
+
+    /** Mod zero wrapper for long division that handles (0%0) >>> 0 == 0 */
+    public static final Call REM_ZERO_LONG       = staticCall(JSTYPE_LOOKUP, JSType.class, "remZero", long.class, long.class, long.class);
+
     /** Mod exact wrapper for potentially integer remainders that turns into float point */
     public static final Call REM_EXACT_LONG       = staticCall(JSTYPE_LOOKUP, JSType.class, "remExact", long.class, long.class, long.class, int.class);
 
@@ -1486,6 +1498,28 @@
     }
 
     /**
+     * Implements int division but allows {@code x / 0} to be represented as 0. Basically equivalent to
+     * {@code (x / y)|0} JavaScript expression (division of two ints coerced to int).
+     * @param x the dividend
+     * @param y the divisor
+     * @return the result
+     */
+    public static int divZero(final int x, final int y) {
+        return y == 0 ? 0 : x / y;
+    }
+
+    /**
+     * Implements int remainder but allows {@code x % 0} to be represented as 0. Basically equivalent to
+     * {@code (x % y)|0} JavaScript expression (remainder of two ints coerced to int).
+     * @param x the dividend
+     * @param y the divisor
+     * @return the remainder
+     */
+    public static int remZero(final int x, final int y) {
+        return y == 0 ? 0 : x % y;
+    }
+
+    /**
      * Wrapper for modExact. Throws UnwarrantedOptimismException if the modulo can't be represented as int.
      *
      * @param x first term
@@ -1529,6 +1563,28 @@
     }
 
     /**
+     * Implements long division but allows {@code x / 0} to be represented as 0. Useful when division of two longs
+     * is coerced to long.
+     * @param x the dividend
+     * @param y the divisor
+     * @return the result
+     */
+    public static long divZero(final long x, final long y) {
+        return y == 0L ? 0L : x / y;
+    }
+
+    /**
+     * Implements long remainder but allows {@code x % 0} to be represented as 0. Useful when remainder of two longs
+     * is coerced to long.
+     * @param x the dividend
+     * @param y the divisor
+     * @return the remainder
+     */
+    public static long remZero(final long x, final long y) {
+        return y == 0L ? 0L : x % y;
+    }
+
+    /**
      * Wrapper for modExact. Throws UnwarrantedOptimismException if the modulo can't be represented as int.
      *
      * @param x first term