Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Aggressively try to use Integers for arithmetic #1563

Merged
merged 2 commits into from
Aug 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
6 changes: 2 additions & 4 deletions rhino/src/main/java/org/mozilla/javascript/Interpreter.java
Original file line number Diff line number Diff line change
Expand Up @@ -2150,13 +2150,11 @@ private static Object interpretLoop(Context cx, CallFrame frame, Object throwabl
}
case Icode_ZERO:
++stackTop;
stack[stackTop] = DBL_MRK;
sDbl[stackTop] = 0;
stack[stackTop] = Integer.valueOf(0);
continue Loop;
case Icode_ONE:
++stackTop;
stack[stackTop] = DBL_MRK;
sDbl[stackTop] = 1;
stack[stackTop] = Integer.valueOf(1);
continue Loop;
case Token.NULL:
stack[++stackTop] = null;
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
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public class NumericSeparatorTest {
/** Special Tokenizer test for numeric constant at end. */
@Test
public void numericAtEndOneDigit() {
Utils.assertWithAllOptimizationLevelsES6(1.0, "1");
Utils.assertWithAllOptimizationLevelsES6(1, "1");
}

/** Special Tokenizer test for numeric constant at end. */
Expand Down
1 change: 0 additions & 1 deletion tests/testsrc/test262.properties
Original file line number Diff line number Diff line change
Expand Up @@ -6187,7 +6187,6 @@ language/expressions/object 867/1169 (74.17%)
accessor-name-literal-numeric-exponent.js {strict: [-1], non-strict: [-1]}
accessor-name-literal-numeric-hex.js {strict: [-1], non-strict: [-1]}
accessor-name-literal-numeric-octal.js {strict: [-1], non-strict: [-1]}
accessor-name-literal-numeric-zero.js
computed-__proto__.js
concise-generator.js
cpn-obj-lit-computed-property-name-from-assignment-expression-coalesce.js
Expand Down
Loading