Skip to content

Commit

Permalink
Aggressively try to use Integer objects
Browse files Browse the repository at this point in the history
When doing math, try to do integer arithmetic when the arguments passed
are Integer objects, rather than always falling back to floating-point
math. This speeds up some benchmarks that rely heavily on integer
arithmetic.
  • Loading branch information
gbrail committed Aug 23, 2024
1 parent cbb0e2f commit 8b7b580
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,8 @@ public static Object iterativeMethod(
break;
case FIND_INDEX:
case FIND_LAST_INDEX:
if (ScriptRuntime.toBoolean(result)) return ScriptRuntime.wrapNumber(i);
if (ScriptRuntime.toBoolean(result))
return ScriptRuntime.wrapNumber((double) i);
break;
}
}
Expand Down
83 changes: 82 additions & 1 deletion rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,7 @@ public static double toNumber(Object[] args, int index) {
// Preserve backward-compatibility with historical value of this.
public static final double negativeZero = Double.longBitsToDouble(0x8000000000000000L);

public static final Double zeroObj = Double.valueOf(0.0);
public static final Integer zeroObj = Integer.valueOf(0);
public static final Double negativeZeroObj = Double.valueOf(-0.0);

static double stringPrefixToNumber(String s, int start, int radix) {
Expand Down Expand Up @@ -2991,6 +2991,9 @@ public static Object add(Object val1, Object val2, Context cx) {
|| (val1 instanceof BigInteger && val2 instanceof Number)) {
throw ScriptRuntime.typeErrorById("msg.cant.convert.to.number", "BigInt");
}
if (val1 instanceof Integer && val2 instanceof Integer) {
return add((Integer) val1, (Integer) val2);
}
if (val1 instanceof Number && val2 instanceof Number) {
return wrapNumber(((Number) val1).doubleValue() + ((Number) val2).doubleValue());
}
Expand Down Expand Up @@ -3064,6 +3067,8 @@ public static Number subtract(Number val1, Number val2) {
return ((BigInteger) val1).subtract((BigInteger) val2);
} else if (val1 instanceof BigInteger || val2 instanceof BigInteger) {
throw ScriptRuntime.typeErrorById("msg.cant.convert.to.number", "BigInt");
} else if (val1 instanceof Integer && val2 instanceof Integer) {
return subtract((Integer) val1, (Integer) val2);
} else {
return val1.doubleValue() - val2.doubleValue();
}
Expand All @@ -3074,6 +3079,8 @@ public static Number multiply(Number val1, Number val2) {
return ((BigInteger) val1).multiply((BigInteger) val2);
} else if (val1 instanceof BigInteger || val2 instanceof BigInteger) {
throw ScriptRuntime.typeErrorById("msg.cant.convert.to.number", "BigInt");
} else if (val1 instanceof Integer && val2 instanceof Integer) {
return multiply((Integer) val1, (Integer) val2);
} else {
return val1.doubleValue() * val2.doubleValue();
}
Expand All @@ -3088,6 +3095,8 @@ public static Number divide(Number val1, Number val2) {
} else if (val1 instanceof BigInteger || val2 instanceof BigInteger) {
throw ScriptRuntime.typeErrorById("msg.cant.convert.to.number", "BigInt");
} else {
// Do not try to optimize for the integer case because JS doesn't
// have an integer type.
return val1.doubleValue() / val2.doubleValue();
}
}
Expand All @@ -3101,10 +3110,41 @@ public static Number remainder(Number val1, Number val2) {
} else if (val1 instanceof BigInteger || val2 instanceof BigInteger) {
throw ScriptRuntime.typeErrorById("msg.cant.convert.to.number", "BigInt");
} else {
// Do not try an integer-specific optimization because we need to get
// both +0 and -0 right.
return val1.doubleValue() % val2.doubleValue();
}
}

// Integer-optimized methods.

public static Object add(Integer i1, Integer i2) {
// Try to add integers for efficiency, but account for overflow
long r = (long) i1.intValue() + (long) i2.intValue();
if ((r >= Integer.MIN_VALUE) && (r <= Integer.MAX_VALUE)) {
return Integer.valueOf((int) r);
}
return Double.valueOf((double) r);
}

public static Number subtract(Integer i1, Integer i2) {
// Account for overflow
long r = (long) i1.intValue() - (long) i2.intValue();
if ((r >= Integer.MIN_VALUE) && (r <= Integer.MAX_VALUE)) {
return Integer.valueOf((int) r);
}
return Double.valueOf((double) r);
}

public static Number multiply(Integer i1, Integer i2) {
// Aunt for overflow
long r = (long) i1.intValue() * (long) i2.intValue();
if ((r >= Integer.MIN_VALUE) && (r <= Integer.MAX_VALUE)) {
return Integer.valueOf((int) r);
}
return Double.valueOf((double) r);
}

public static Number exponentiate(Number val1, Number val2) {
if (val1 instanceof BigInteger && val2 instanceof BigInteger) {
if (((BigInteger) val2).signum() == -1) {
Expand All @@ -3130,6 +3170,8 @@ public static Number bitwiseAND(Number val1, Number val2) {
return ((BigInteger) val1).and((BigInteger) val2);
} else if (val1 instanceof BigInteger || val2 instanceof BigInteger) {
throw ScriptRuntime.typeErrorById("msg.cant.convert.to.number", "BigInt");
} else if (val1 instanceof Integer && val2 instanceof Integer) {
return Integer.valueOf(((Integer) val1).intValue() & ((Integer) val2).intValue());
} else {
int result = toInt32(val1.doubleValue()) & toInt32(val2.doubleValue());
return Double.valueOf(result);
Expand All @@ -3141,6 +3183,8 @@ public static Number bitwiseOR(Number val1, Number val2) {
return ((BigInteger) val1).or((BigInteger) val2);
} else if (val1 instanceof BigInteger || val2 instanceof BigInteger) {
throw ScriptRuntime.typeErrorById("msg.cant.convert.to.number", "BigInt");
} else if (val1 instanceof Integer && val2 instanceof Integer) {
return Integer.valueOf(((Integer) val1).intValue() | ((Integer) val2).intValue());
} else {
int result = toInt32(val1.doubleValue()) | toInt32(val2.doubleValue());
return Double.valueOf(result);
Expand All @@ -3152,6 +3196,8 @@ public static Number bitwiseXOR(Number val1, Number val2) {
return ((BigInteger) val1).xor((BigInteger) val2);
} else if (val1 instanceof BigInteger || val2 instanceof BigInteger) {
throw ScriptRuntime.typeErrorById("msg.cant.convert.to.number", "BigInt");
} else if (val1 instanceof Integer && val2 instanceof Integer) {
return Integer.valueOf(((Integer) val1).intValue() ^ ((Integer) val2).intValue());
} else {
int result = toInt32(val1.doubleValue()) ^ toInt32(val2.doubleValue());
return Double.valueOf(result);
Expand All @@ -3169,6 +3215,8 @@ public static Number leftShift(Number val1, Number val2) {
}
} else if (val1 instanceof BigInteger || val2 instanceof BigInteger) {
throw ScriptRuntime.typeErrorById("msg.cant.convert.to.number", "BigInt");
} else if (val1 instanceof Integer && val2 instanceof Integer) {
return Integer.valueOf(((Integer) val1).intValue() << ((Integer) val2).intValue());
} else {
int result = toInt32(val1.doubleValue()) << toInt32(val2.doubleValue());
return Double.valueOf(result);
Expand All @@ -3186,6 +3234,8 @@ public static Number signedRightShift(Number val1, Number val2) {
}
} else if (val1 instanceof BigInteger || val2 instanceof BigInteger) {
throw ScriptRuntime.typeErrorById("msg.cant.convert.to.number", "BigInt");
} else if (val1 instanceof Integer && val2 instanceof Integer) {
return Integer.valueOf(((Integer) val1).intValue() >> ((Integer) val2).intValue());
} else {
int result = toInt32(val1.doubleValue()) >> toInt32(val2.doubleValue());
return Double.valueOf(result);
Expand All @@ -3195,6 +3245,8 @@ public static Number signedRightShift(Number val1, Number val2) {
public static Number bitwiseNOT(Number val) {
if (val instanceof BigInteger) {
return ((BigInteger) val).not();
} else if (val instanceof Integer) {
return Integer.valueOf(~((Integer) val).intValue());
} else {
int result = ~toInt32(val.doubleValue());
return Double.valueOf(result);
Expand Down Expand Up @@ -3292,6 +3344,12 @@ private static Object doScriptableIncrDecr(
} else {
result = ((BigInteger) number).subtract(BigInteger.ONE);
}
} else if (number instanceof Integer) {
if ((incrDecrMask & Node.DECR_FLAG) == 0) {
result = ((Integer) number).intValue() + 1;
} else {
result = ((Integer) number).intValue() - 1;
}
} else {
if ((incrDecrMask & Node.DECR_FLAG) == 0) {
result = number.doubleValue() + 1.0;
Expand Down Expand Up @@ -3332,6 +3390,12 @@ public static Object elemIncrDecr(
} else {
result = ((BigInteger) number).subtract(BigInteger.ONE);
}
} else if (number instanceof Integer) {
if ((incrDecrMask & Node.DECR_FLAG) == 0) {
result = ((Integer) number).intValue() + 1;
} else {
result = ((Integer) number).intValue() - 1;
}
} else {
if ((incrDecrMask & Node.DECR_FLAG) == 0) {
result = number.doubleValue() + 1.0;
Expand Down Expand Up @@ -3371,6 +3435,12 @@ public static Object refIncrDecr(Ref ref, Context cx, Scriptable scope, int incr
} else {
result = ((BigInteger) number).subtract(BigInteger.ONE);
}
} else if (number instanceof Integer) {
if ((incrDecrMask & Node.DECR_FLAG) == 0) {
result = ((Integer) number).intValue() + 1;
} else {
result = ((Integer) number).intValue() - 1;
}
} else {
if ((incrDecrMask & Node.DECR_FLAG) == 0) {
result = number.doubleValue() + 1.0;
Expand All @@ -3390,6 +3460,17 @@ public static Number negate(Number val) {
if (val instanceof BigInteger) {
return ((BigInteger) val).negate();
}
if (val instanceof Integer) {
int iv = (Integer) val;
if (iv == 0) {
return negativeZeroObj;
}
if (iv > Integer.MIN_VALUE && iv < Integer.MAX_VALUE) {
// Account for twos-complement representation by not trying
// to negate values at the extremes
return Integer.valueOf(-((Integer) val).intValue());
}
}
return -val.doubleValue();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1143,7 +1143,7 @@ void pushNumberAsObject(ClassFileWriter cfw, double num) {
ByteCode.GETSTATIC,
"org/mozilla/javascript/ScriptRuntime",
"zeroObj",
"Ljava/lang/Double;");
"Ljava/lang/Integer;");
} else {
cfw.addPush(num);
addDoubleWrap(cfw);
Expand All @@ -1154,15 +1154,15 @@ void pushNumberAsObject(ClassFileWriter cfw, double num) {
ByteCode.GETSTATIC,
"org/mozilla/javascript/optimizer/OptRuntime",
"oneObj",
"Ljava/lang/Double;");
"Ljava/lang/Integer;");
return;

} else if (num == -1.0) {
cfw.add(
ByteCode.GETSTATIC,
"org/mozilla/javascript/optimizer/OptRuntime",
"minusOneObj",
"Ljava/lang/Double;");
"Ljava/lang/Integer;");

} else if (Double.isNaN(num)) {
cfw.add(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@
import org.mozilla.javascript.Undefined;

public final class OptRuntime extends ScriptRuntime {
public static final Double oneObj = Double.valueOf(1.0);
public static final Double minusOneObj = Double.valueOf(-1.0);
public static final Integer oneObj = Integer.valueOf(1);
public static final Integer minusOneObj = Integer.valueOf(-1);

/** Implement ....() call shrinking optimizer code. */
public static Object call0(Callable fun, Scriptable thisObj, Context cx, Scriptable scope) {
Expand Down Expand Up @@ -145,16 +145,7 @@ public static Object newObjectSpecial(
}

public static Double wrapDouble(double num) {
if (num == 0.0) {
if (1 / num > 0) {
// +0.0
return zeroObj;
}
} else if (num == 1.0) {
return oneObj;
} else if (num == -1.0) {
return minusOneObj;
} else if (Double.isNaN(num)) {
if (Double.isNaN(num)) {
return NaNobj;
}
return Double.valueOf(num);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,9 @@ private String toSource(CharSequence cs) {
public void eval() {
// test EmptyStatement node doesn't infer with return values (in
// contrast to wrapping EmptyExpression into an ExpressionStatement)
assertEquals(1d, eval("1;;;;"));
assertEquals(1, eval("1;;;;"));
assertEquals(Undefined.instance, eval("(function(){1;;;;})()"));
assertEquals(1d, eval("(function(){return 1;;;;})()"));
assertEquals(1, eval("(function(){return 1;;;;})()"));
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public static int f(int a) {
public void simpleFunction() {
try (Context cx = Context.enter()) {
Object result = cx.evaluateString(global, "f(7) + 1", "test source", 1, null);
assertEquals(15.0, result);
assertEquals(15, result);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -440,8 +440,7 @@ public void testAssert() {
assertPrintCalls(
"console.assert(false, 'Fail', 1)",
Collections.singletonList(
new PrinterCall(
Level.ERROR, new Object[] {"Assertion failed: Fail", 1.0})));
new PrinterCall(Level.ERROR, new Object[] {"Assertion failed: Fail", 1})));
assertPrintMsg("console.assert(false, 'Fail', 1)", "Assertion failed: Fail 1");

assertPrintCalls(
Expand Down

0 comments on commit 8b7b580

Please sign in to comment.