JS: improve performance float<->int reinterpretation conversion, improve performance of Double.equals.

This commit is contained in:
Alexey Andreev 2023-10-16 17:26:05 +02:00
parent 658ef711ab
commit cd9db17d73
4 changed files with 59 additions and 15 deletions

View File

@ -23,6 +23,14 @@ import org.teavm.model.MethodReference;
public class DoubleGenerator implements Injector { public class DoubleGenerator implements Injector {
@Override @Override
public void generate(InjectorContext context, MethodReference methodRef) throws IOException { public void generate(InjectorContext context, MethodReference methodRef) throws IOException {
if (methodRef.getName().equals("doubleEqualsJs")) {
context.getWriter().appendFunction("$rt_equalDoubles").append("(");
context.writeExpr(context.getArgument(0));
context.getWriter().append(',').ws();
context.writeExpr(context.getArgument(1));
context.getWriter().append(')');
return;
}
context.getWriter().append("$rt_").append(methodRef.getName()).append("("); context.getWriter().append("$rt_").append(methodRef.getName()).append("(");
context.writeExpr(context.getArgument(0)); context.writeExpr(context.getArgument(0));
context.getWriter().append(")"); context.getWriter().append(")");

View File

@ -16,6 +16,7 @@
package org.teavm.classlib.java.lang; package org.teavm.classlib.java.lang;
import org.teavm.backend.javascript.spi.InjectedBy; import org.teavm.backend.javascript.spi.InjectedBy;
import org.teavm.classlib.PlatformDetector;
import org.teavm.classlib.impl.text.DoubleSynthesizer; import org.teavm.classlib.impl.text.DoubleSynthesizer;
import org.teavm.interop.Import; import org.teavm.interop.Import;
import org.teavm.interop.NoSideEffects; import org.teavm.interop.NoSideEffects;
@ -201,7 +202,18 @@ public class TDouble extends TNumber implements TComparable<TDouble> {
if (this == other) { if (this == other) {
return true; return true;
} }
return other instanceof TDouble && doubleToLongBits(((TDouble) other).value) == doubleToLongBits(value); return other instanceof TDouble && equals(value, ((TDouble) other).value);
}
private static boolean equals(double a, double b) {
return PlatformDetector.isJavaScript() ? doubleEqualsJs(a, b) : equalsWithBits(a, b);
}
@InjectedBy(DoubleGenerator.class)
private static native boolean doubleEqualsJs(double a, double b);
private static boolean equalsWithBits(double a, double b) {
return a != a ? b != b : doubleToRawLongBits(a) == doubleToRawLongBits(b);
} }
@Override @Override

View File

@ -85,7 +85,11 @@ public class TFloat extends TNumber implements TComparable<TFloat> {
if (this == other) { if (this == other) {
return true; return true;
} }
return other instanceof TFloat && floatToIntBits(((TFloat) other).value) == floatToIntBits(value); return other instanceof TFloat && equals(value, ((TFloat) other).value);
}
private static boolean equals(float a, float b) {
return a != a ? b != b : floatToRawIntBits(a) == floatToRawIntBits(b);
} }
@Override @Override

View File

@ -656,7 +656,11 @@ function $rt_eraseClinit(target) {
return target.$clinit = function() {}; return target.$clinit = function() {};
} }
var $rt_numberConversionView = new DataView(new ArrayBuffer(8)); var $rt_numberConversionBuffer = new ArrayBuffer(16);
var $rt_numberConversionView = new DataView($rt_numberConversionBuffer);
var $rt_numberConversionFloatArray = new Float32Array($rt_numberConversionBuffer);
var $rt_numberConversionDoubleArray = new Float64Array($rt_numberConversionBuffer);
var $rt_numberConversionIntArray = new Int32Array($rt_numberConversionBuffer);
var $rt_doubleToRawLongBits; var $rt_doubleToRawLongBits;
var $rt_longBitsToDouble; var $rt_longBitsToDouble;
@ -670,31 +674,47 @@ if (typeof BigInt !== 'function') {
$rt_numberConversionView.setInt32(4, n.hi, true); $rt_numberConversionView.setInt32(4, n.hi, true);
return $rt_numberConversionView.getFloat64(0, true); return $rt_numberConversionView.getFloat64(0, true);
} }
} else { } else if (typeof BigInt64Array !== 'function') {
$rt_doubleToRawLongBits = function(n) { $rt_doubleToRawLongBits = function(n) {
$rt_numberConversionView.setFloat64(0, n, true); $rt_numberConversionView.setFloat64(0, n, true);
// For compatibility with Safari
var lo = $rt_numberConversionView.getInt32(0, true); var lo = $rt_numberConversionView.getInt32(0, true);
var hi = $rt_numberConversionView.getInt32(4, true); var hi = $rt_numberConversionView.getInt32(4, true);
return BigInt.asIntN(64, BigInt.asUintN(32, BigInt(lo)) | (BigInt(hi) << BigInt(32))); return BigInt.asIntN(64, BigInt.asUintN(32, BigInt(lo)) | (BigInt(hi) << BigInt(32)));
} }
$rt_longBitsToDouble = function(n) { $rt_longBitsToDouble = function(n) {
// For compatibility with Safari $rt_numberConversionView.setFloat64(0, n, true);
var hi = Number(BigInt.asIntN(32, n >> BigInt(32))); var lo = $rt_numberConversionView.getInt32(0, true);
var lo = Number(BigInt.asIntN(32, n & BigInt(0xFFFFFFFF))); var hi = $rt_numberConversionView.getInt32(4, true);
$rt_numberConversionView.setInt32(0, lo, true); return BigInt.asIntN(64, BigInt.asUintN(32, BigInt(lo)) | (BigInt(hi) << BigInt(32)));
$rt_numberConversionView.setInt32(4, hi, true); }
return $rt_numberConversionView.getFloat64(0, true); } else {
var $rt_numberConversionLongArray = new BigInt64Array($rt_numberConversionBuffer);
$rt_doubleToRawLongBits = function(n) {
$rt_numberConversionDoubleArray[0] = n;
return $rt_numberConversionLongArray[0];
}
$rt_longBitsToDouble = function(n) {
$rt_numberConversionLongArray[0] = n;
return $rt_numberConversionDoubleArray[0];
} }
} }
function $rt_floatToRawIntBits(n) { function $rt_floatToRawIntBits(n) {
$rt_numberConversionView.setFloat32(0, n); $rt_numberConversionFloatArray[0] = n;
return $rt_numberConversionView.getInt32(0); return $rt_numberConversionIntArray[0];
} }
function $rt_intBitsToFloat(n) { function $rt_intBitsToFloat(n) {
$rt_numberConversionView.setInt32(0, n); $rt_numberConversionIntArray[0] = n;
return $rt_numberConversionView.getFloat32(0); return $rt_numberConversionFloatArray[0];
}
function $rt_equalDoubles(a, b) {
if (a !== a) {
return b !== b;
}
$rt_numberConversionDoubleArray[0] = a;
$rt_numberConversionDoubleArray[1] = b;
return $rt_numberConversionIntArray[0] === $rt_numberConversionIntArray[2]
&& $rt_numberConversionIntArray[1] === $rt_numberConversionIntArray[3];
} }
var JavaError; var JavaError;