JS: optimize integer overflow emulation when possible

This commit is contained in:
Alexey Andreev
2016-12-07 17:00:52 +03:00
parent eb5c79c70d
commit 7d8df5c81c
3 changed files with 85 additions and 12 deletions
@@ -19,10 +19,7 @@ package org.jetbrains.kotlin.js.translate.intrinsic.functions.factories;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableMap;
import com.google.dart.compiler.backend.js.ast.JsBinaryOperation;
import com.google.dart.compiler.backend.js.ast.JsBinaryOperator;
import com.google.dart.compiler.backend.js.ast.JsExpression;
import com.google.dart.compiler.backend.js.ast.JsInvocation;
import com.google.dart.compiler.backend.js.ast.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
@@ -64,13 +61,24 @@ public enum PrimitiveBinaryOperationFIF implements FunctionIntrinsicFactory {
}
private static final BinaryOperationIntrinsicBase INT_MULTIPLICATION_INTRINSIC = new BinaryOperationIntrinsicBase() {
// IEEE 754 mantissa is 52 bits long. Assuming one argument may be up to 2^31, the second argument should be
// not greater than 2^21 in order their product don't exceed 2^52. We preserve two extra bits to be more safe.
private static final int SAFE_THRESHOLD = 2 << 19;
@NotNull
@Override
public JsExpression doApply(
@NotNull JsExpression left, @NotNull JsExpression right, @NotNull TranslationContext context
) {
public JsExpression doApply(@NotNull JsExpression left, @NotNull JsExpression right, @NotNull TranslationContext context) {
if (isSafeConstant(left) || isSafeConstant(right)) {
return JsAstUtils.toInt32(JsAstUtils.mul(left, right));
}
return new JsInvocation(Namer.imul(), left, right);
}
private boolean isSafeConstant(@NotNull JsExpression expression) {
if (!(expression instanceof JsNumberLiteral.JsIntLiteral)) return false;
int value = ((JsNumberLiteral.JsIntLiteral) expression).value;
return Math.abs(value) < SAFE_THRESHOLD;
}
};
@NotNull
@@ -94,7 +102,7 @@ public enum PrimitiveBinaryOperationFIF implements FunctionIntrinsicFactory {
@NotNull
private static final NamePredicate BINARY_OPERATIONS = new NamePredicate(OperatorNameConventions.BINARY_OPERATION_NAMES);
private static final DescriptorPredicate INT_BINARY_OPERATIONS = pattern("Int.plus|minus|div(Int)");
private static final DescriptorPredicate INT_BINARY_OPERATIONS = pattern("Int.plus|minus(Int)");
private static final DescriptorPredicate SIMPLE_INT_MULTIPLICATION = pattern("Byte|Short.times(Byte|Short)");
private static final DescriptorPredicate INT_DIVISION = pattern("Byte|Short|Int.div(Byte|Short|Int)");
private static final DescriptorPredicate PRIMITIVE_NUMBERS_BINARY_OPERATIONS =
@@ -155,11 +163,14 @@ public enum PrimitiveBinaryOperationFIF implements FunctionIntrinsicFactory {
if (INT_WITH_BIT_OPERATIONS.apply(descriptor)) {
JsBinaryOperator op = BINARY_BITWISE_OPERATIONS.get(descriptor.getName().asString());
if (op != null) {
return new PrimitiveBinaryOperationFunctionIntrinsic(op);
return new OptimizedIntBinaryOperationInstrinsic(op);
}
}
JsBinaryOperator operator = getOperator(descriptor);
if (INT_BINARY_OPERATIONS.apply(descriptor) || SIMPLE_INT_MULTIPLICATION.apply(descriptor) || INT_DIVISION.apply(descriptor)) {
if (INT_BINARY_OPERATIONS.apply(descriptor)) {
return new AdditiveIntBinaryOperationInstrinsic(operator);
}
if (SIMPLE_INT_MULTIPLICATION.apply(descriptor) || INT_DIVISION.apply(descriptor)) {
return new IntBinaryOperationFunctionIntrinsic(operator);
}
BinaryOperationIntrinsicBase result = new PrimitiveBinaryOperationFunctionIntrinsic(operator);
@@ -217,6 +228,48 @@ public enum PrimitiveBinaryOperationFIF implements FunctionIntrinsicFactory {
}
}
private static class OptimizedIntBinaryOperationInstrinsic extends PrimitiveBinaryOperationFunctionIntrinsic {
public OptimizedIntBinaryOperationInstrinsic(@NotNull JsBinaryOperator operator) {
super(operator);
}
@NotNull
@Override
public JsExpression doApply(@NotNull JsExpression left, @NotNull JsExpression right, @NotNull TranslationContext context) {
left = unwrapAdditive(left);
right = unwrapAdditive(right);
return super.doApply(left, right, context);
}
@NotNull
private static JsExpression unwrapAdditive(@NotNull JsExpression expression) {
JsExpression toIntArgument = JsAstUtils.extractToInt32Argument(expression);
if (toIntArgument == null) return expression;
if (!(toIntArgument instanceof JsBinaryOperation)) return expression;
JsBinaryOperator operator = ((JsBinaryOperation) toIntArgument).getOperator();
switch (operator) {
case ADD:
case SUB:
return toIntArgument;
default:
return expression;
}
}
}
private static class AdditiveIntBinaryOperationInstrinsic extends OptimizedIntBinaryOperationInstrinsic {
public AdditiveIntBinaryOperationInstrinsic(@NotNull JsBinaryOperator operator) {
super(operator);
}
@NotNull
@Override
public JsExpression doApply(@NotNull JsExpression left, @NotNull JsExpression right, @NotNull TranslationContext context) {
return JsAstUtils.toInt32(super.doApply(left, right, context));
}
}
private static class CharAndIntBinaryOperationFunctionIntrinsic extends BinaryOperationIntrinsicBase {
@NotNull
@@ -135,6 +135,18 @@ public final class JsAstUtils {
return new JsBinaryOperation(JsBinaryOperator.BIT_OR, expression, JsNumberLiteral.ZERO);
}
@Nullable
public static JsExpression extractToInt32Argument(@NotNull JsExpression expression) {
if (!(expression instanceof JsBinaryOperation)) return null;
JsBinaryOperation binary = (JsBinaryOperation) expression;
if (binary.getOperator() != JsBinaryOperator.BIT_OR) return null;
if (!(binary.getArg2() instanceof JsNumberLiteral.JsIntLiteral)) return null;
JsNumberLiteral.JsIntLiteral arg2 = (JsNumberLiteral.JsIntLiteral) binary.getArg2();
return arg2.value == 0 ? binary.getArg1() : null;
}
@NotNull
public static JsExpression charToInt(@NotNull JsExpression expression) {
return invokeMethod(expression, "charCodeAt", JsNumberLiteral.ZERO);
+10 -2
View File
@@ -4,6 +4,8 @@ fun bigValue() = 0x7FFFFFFC
fun mediumValue() = 0x12345
fun four() = 4
fun box(): String {
var v = bigValue() + 1
if (v != 0x7FFFFFFD) return "fail1: $v"
@@ -11,11 +13,17 @@ fun box(): String {
v = bigValue() + 8
if (v != -0x7FFFFFFC) return "fail2: $v"
v = bigValue() + four() + 4
if (v != -0x7FFFFFFC) return "fail3: $v"
v = (bigValue() + four() - 4) shr 1
if (v != 0x3FFFFFFE) return "fail3: $v"
v = mediumValue() * 0x23456
if (v != -2112496338) return "fail3: $v"
if (v != -2112496338) return "fail5: $v"
v = bigValue() * bigValue()
if (v != 16) return "fail4: $v"
if (v != 16) return "fail6: $v"
return "OK"
}