From 37f07b80c39df801dfabe47a41c9a5da8c611d89 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Wed, 4 Mar 2020 13:23:19 +0300 Subject: [PATCH] JS: generate shorter names for Long operations and array creation in minified mode --- .../backend/javascript/JavaScriptTarget.java | 1 + .../rendering/NameFrequencyEstimator.java | 128 +++++++++++++++++- .../javascript/rendering/Renderer.java | 20 ++- .../rendering/RenderingContext.java | 7 +- .../rendering/StatementRenderer.java | 44 +++--- .../org/teavm/backend/javascript/long.js | 3 + 6 files changed, 173 insertions(+), 30 deletions(-) diff --git a/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java b/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java index cecd83720..06939910d 100644 --- a/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java +++ b/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java @@ -417,6 +417,7 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { if (renderer.isLongLibraryUsed()) { runtimeRenderer.renderHandWrittenRuntime("long.js"); + renderer.renderLongRuntimeAliases(); } if (renderer.isThreadLibraryUsed()) { runtimeRenderer.renderHandWrittenRuntime("thread.js"); diff --git a/core/src/main/java/org/teavm/backend/javascript/rendering/NameFrequencyEstimator.java b/core/src/main/java/org/teavm/backend/javascript/rendering/NameFrequencyEstimator.java index 4f2e29f1c..356d74338 100644 --- a/core/src/main/java/org/teavm/backend/javascript/rendering/NameFrequencyEstimator.java +++ b/core/src/main/java/org/teavm/backend/javascript/rendering/NameFrequencyEstimator.java @@ -33,6 +33,7 @@ import org.teavm.ast.NewArrayExpr; import org.teavm.ast.NewExpr; import org.teavm.ast.NewMultiArrayExpr; import org.teavm.ast.OperationType; +import org.teavm.ast.PrimitiveCastExpr; import org.teavm.ast.QualificationExpr; import org.teavm.ast.RecursiveVisitor; import org.teavm.ast.RegularMethodNode; @@ -209,6 +210,65 @@ class NameFrequencyEstimator extends RecursiveVisitor implements MethodNodeVisit @Override public void visit(BinaryExpr expr) { super.visit(expr); + if (expr.getType() == OperationType.LONG) { + switch (expr.getOperation()) { + case ADD: + consumer.consumeFunction("Long_add"); + break; + case SUBTRACT: + consumer.consumeFunction("Long_sub"); + break; + case MULTIPLY: + consumer.consumeFunction("Long_mul"); + break; + case DIVIDE: + consumer.consumeFunction("Long_div"); + break; + case MODULO: + consumer.consumeFunction("Long_rem"); + break; + case BITWISE_OR: + consumer.consumeFunction("Long_or"); + break; + case BITWISE_AND: + consumer.consumeFunction("Long_and"); + break; + case BITWISE_XOR: + consumer.consumeFunction("Long_xor"); + break; + case LEFT_SHIFT: + consumer.consumeFunction("Long_shl"); + break; + case RIGHT_SHIFT: + consumer.consumeFunction("Long_shr"); + break; + case UNSIGNED_RIGHT_SHIFT: + consumer.consumeFunction("Long_shru"); + break; + case COMPARE: + consumer.consumeFunction("Long_compare"); + break; + case EQUALS: + consumer.consumeFunction("Long_eq"); + break; + case NOT_EQUALS: + consumer.consumeFunction("Long_ne"); + break; + case LESS: + consumer.consumeFunction("Long_lt"); + break; + case LESS_OR_EQUALS: + consumer.consumeFunction("Long_le"); + break; + case GREATER: + consumer.consumeFunction("Long_gt"); + break; + case GREATER_OR_EQUALS: + consumer.consumeFunction("Long_ge"); + break; + } + return; + } switch (expr.getOperation()) { case COMPARE: consumer.consumeFunction("$rt_compare"); @@ -231,17 +291,56 @@ class NameFrequencyEstimator extends RecursiveVisitor implements MethodNodeVisit case NULL_CHECK: consumer.consumeFunction("$rt_nullCheck"); break; + case NEGATE: + if (expr.getType() == OperationType.LONG) { + consumer.consumeFunction("Long_neg"); + } + break; + case NOT: + if (expr.getType() == OperationType.LONG) { + consumer.consumeFunction("Long_not"); + } + break; default: break; } } + @Override + public void visit(PrimitiveCastExpr expr) { + super.visit(expr); + if (expr.getSource() == OperationType.LONG) { + if (expr.getTarget() == OperationType.DOUBLE || expr.getTarget() == OperationType.FLOAT) { + consumer.consumeFunction("Long_toNumber"); + } + } else { + switch (expr.getSource()) { + case INT: + consumer.consumeFunction("Long_fromInt"); + break; + case FLOAT: + case DOUBLE: + consumer.consumeFunction("Long_fromNUmber"); + break; + } + } + } + @Override public void visit(ConstantExpr expr) { if (expr.getValue() instanceof ValueType) { visitType((ValueType) expr.getValue()); } else if (expr.getValue() instanceof String) { consumer.consumeFunction("$rt_s"); + } else if (expr.getValue() instanceof Long) { + long value = (Long) expr.getValue(); + if (value == 0) { + consumer.consumeFunction("Long_ZERO"); + } else if ((int) value == value) { + consumer.consumeFunction("Long_fromInt"); + } else { + consumer.consumeFunction("Long"); + } } } @@ -291,7 +390,34 @@ class NameFrequencyEstimator extends RecursiveVisitor implements MethodNodeVisit public void visit(NewArrayExpr expr) { super.visit(expr); visitType(expr.getType()); - if (!(expr.getType() instanceof ValueType.Primitive)) { + if (expr.getType() instanceof ValueType.Primitive) { + switch (((ValueType.Primitive) expr.getType()).getKind()) { + case BOOLEAN: + consumer.consumeFunction("$rt_createBooleanArray"); + break; + case BYTE: + consumer.consumeFunction("$rt_createByteArray"); + break; + case SHORT: + consumer.consumeFunction("$rt_createShortArray"); + break; + case CHARACTER: + consumer.consumeFunction("$rt_createCharArray"); + break; + case INTEGER: + consumer.consumeFunction("$rt_createIntArray"); + break; + case LONG: + consumer.consumeFunction("$rt_createLongArray"); + break; + case FLOAT: + consumer.consumeFunction("$rt_createFloatArray"); + break; + case DOUBLE: + consumer.consumeFunction("$rt_createDoubleArray"); + break; + } + } else { consumer.consumeFunction("$rt_createArray"); } } diff --git a/core/src/main/java/org/teavm/backend/javascript/rendering/Renderer.java b/core/src/main/java/org/teavm/backend/javascript/rendering/Renderer.java index e24ad3299..b9a73bac3 100644 --- a/core/src/main/java/org/teavm/backend/javascript/rendering/Renderer.java +++ b/core/src/main/java/org/teavm/backend/javascript/rendering/Renderer.java @@ -249,8 +249,8 @@ public class Renderer implements RenderingManager { sizeByClass.put(className, sizeByClass.getOrDefault(className, 0) + sz); } - private void renderRuntimeAliases() throws IOException { - String[] names = { "$rt_throw", "$rt_compare", "$rt_nullCheck", "$rt_cls", "$rt_createArray", + private void renderCommonRuntimeAliases() throws IOException { + renderRuntimeAliases("$rt_throw", "$rt_compare", "$rt_nullCheck", "$rt_cls", "$rt_createArray", "$rt_isInstance", "$rt_nativeThread", "$rt_suspending", "$rt_resuming", "$rt_invalidPointer", "$rt_s", "$rt_eraseClinit", "$rt_imul", "$rt_wrapException", "$rt_checkBounds", "$rt_checkUpperBound", "$rt_checkLowerBound", "$rt_wrapFunction0", "$rt_wrapFunction1", @@ -258,7 +258,19 @@ public class Renderer implements RenderingManager { "$rt_classWithoutFields", "$rt_createArrayFromData", "$rt_createCharArrayFromData", "$rt_createByteArrayFromData", "$rt_createShortArrayFromData", "$rt_createIntArrayFromData", "$rt_createBooleanArrayFromData", "$rt_createFloatArrayFromData", "$rt_createDoubleArrayFromData", - "$rt_createLongArrayFromData" }; + "$rt_createLongArrayFromData", "$rt_createBooleanArray", "$rt_createByteArray", + "$rt_createShortArray", "$rt_createCharArray", "$rt_createIntArray", "$rt_createLongArray", + "$rt_createFloatArray", "$rt_createDoubleArray", "$rt_compare", + "Long_toNumber", "Long_fromInt", "Long_fromNumber", "Long", "Long_ZERO"); + } + + public void renderLongRuntimeAliases() throws IOException { + renderRuntimeAliases("Long_add", "Long_sub", "Long_mul", "Long_div", "Long_rem", "Long_or", "Long_and", + "Long_xor", "Long_shl", "Long_shr", "Long_shru", "Long_compare", "Long_eq", "Long_ne", + "Long_lt", "Long_le", "Long_gt", "Long_ge", "Long_not", "Long_neg"); + } + + private void renderRuntimeAliases(String... names) throws IOException { boolean first = true; for (String name : names) { if (!first) { @@ -286,7 +298,7 @@ public class Renderer implements RenderingManager { public boolean render(List classes) throws RenderingException { if (minifying) { try { - renderRuntimeAliases(); + renderCommonRuntimeAliases(); } catch (IOException e) { throw new RenderingException(e); } diff --git a/core/src/main/java/org/teavm/backend/javascript/rendering/RenderingContext.java b/core/src/main/java/org/teavm/backend/javascript/rendering/RenderingContext.java index b4564063c..f1f087ed1 100644 --- a/core/src/main/java/org/teavm/backend/javascript/rendering/RenderingContext.java +++ b/core/src/main/java/org/teavm/backend/javascript/rendering/RenderingContext.java @@ -235,11 +235,12 @@ public class RenderingContext { } else if (cst instanceof Long) { long value = (Long) cst; if (value == 0) { - writer.append("Long_ZERO"); + writer.appendFunction("Long_ZERO"); } else if ((int) value == value) { - writer.append("Long_fromInt(" + value + ")"); + writer.appendFunction("Long_fromInt").append("(").append(String.valueOf(value)).append(")"); } else { - writer.append("new Long(" + (value & 0xFFFFFFFFL) + ", " + (value >>> 32) + ")"); + writer.append("new ").appendFunction("Long").append("(" + (value & 0xFFFFFFFFL) + + ", " + (value >>> 32) + ")"); } } else if (cst instanceof Character) { writer.append(Integer.toString((Character) cst)); diff --git a/core/src/main/java/org/teavm/backend/javascript/rendering/StatementRenderer.java b/core/src/main/java/org/teavm/backend/javascript/rendering/StatementRenderer.java index 87612076b..402c9616f 100644 --- a/core/src/main/java/org/teavm/backend/javascript/rendering/StatementRenderer.java +++ b/core/src/main/java/org/teavm/backend/javascript/rendering/StatementRenderer.java @@ -644,7 +644,7 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor { if (expr.getLocation() != null) { pushLocation(expr.getLocation()); } - writer.append(function); + writer.appendFunction(function); writer.append('('); precedence = Precedence.min(); expr.getFirstOperand().acceptVisitor(this); @@ -656,7 +656,7 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor { popLocation(); } } catch (IOException e) { - throw new RenderingException("IO error occured", e); + throw new RenderingException("IO error occurred", e); } } @@ -735,7 +735,7 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor { || RenderingUtil.isSmallInteger(expr.getSecondOperand())) { visitBinary(expr, "*", expr.getType() == OperationType.INT); } else { - visitBinaryFunction(expr, naming.getNameForFunction("$rt_imul")); + visitBinaryFunction(expr, "$rt_imul"); } break; case DIVIDE: @@ -771,7 +771,7 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor { visitBinary(expr, "<=", false); break; case COMPARE: - visitBinaryFunction(expr, naming.getNameForFunction("$rt_compare")); + visitBinaryFunction(expr, "$rt_compare"); break; case OR: visitBinary(expr, "||", false); @@ -812,7 +812,7 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor { case NOT: { if (expr.getType() == OperationType.LONG) { longLibraryUsed = true; - writer.append("Long_not("); + writer.appendFunction("Long_not").append("("); precedence = Precedence.min(); expr.getOperand().acceptVisitor(this); writer.append(')'); @@ -832,7 +832,7 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor { case NEGATE: if (expr.getType() == OperationType.LONG) { longLibraryUsed = true; - writer.append("Long_neg("); + writer.appendFunction("Long_neg").append("("); precedence = Precedence.min(); expr.getOperand().acceptVisitor(this); writer.append(')'); @@ -897,7 +897,7 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor { popLocation(); } } catch (IOException e) { - throw new RenderingException("IO error occured", e); + throw new RenderingException("IO error occurred", e); } } @@ -915,7 +915,7 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor { switch (expr.getSource()) { case INT: if (expr.getTarget() == OperationType.LONG) { - writer.append("Long_fromInt("); + writer.appendFunction("Long_fromInt").append("("); precedence = Precedence.min(); expr.getValue().acceptVisitor(this); writer.append(')'); @@ -938,7 +938,7 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor { break; case FLOAT: case DOUBLE: - writer.append("Long_toNumber("); + writer.appendFunction("Long_toNumber").append("("); precedence = Precedence.min(); expr.getValue().acceptVisitor(this); writer.append(')'); @@ -951,7 +951,7 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor { case DOUBLE: switch (expr.getTarget()) { case LONG: - writer.append("Long_fromNumber("); + writer.appendFunction("Long_fromNumber").append("("); precedence = Precedence.min(); expr.getValue().acceptVisitor(this); writer.append(')'); @@ -975,7 +975,7 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor { popLocation(); } } catch (IOException e) { - throw new RenderingException("IO error occured", e); + throw new RenderingException("IO error occurred", e); } } @@ -1033,7 +1033,7 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor { popLocation(); } } catch (IOException e) { - throw new RenderingException("IO error occured", e); + throw new RenderingException("IO error occurred", e); } } @@ -1048,7 +1048,7 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor { popLocation(); } } catch (IOException e) { - throw new RenderingException("IO error occured", e); + throw new RenderingException("IO error occurred", e); } } @@ -1063,7 +1063,7 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor { popLocation(); } } catch (IOException e) { - throw new RenderingException("IO error occured", e); + throw new RenderingException("IO error occurred", e); } } @@ -1263,49 +1263,49 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor { if (type instanceof ValueType.Primitive) { switch (((ValueType.Primitive) type).getKind()) { case BOOLEAN: - writer.append("$rt_createBooleanArray("); + writer.appendFunction("$rt_createBooleanArray").append("("); precedence = Precedence.min(); expr.getLength().acceptVisitor(this); writer.append(")"); break; case BYTE: - writer.append("$rt_createByteArray("); + writer.appendFunction("$rt_createByteArray").append("("); precedence = Precedence.min(); expr.getLength().acceptVisitor(this); writer.append(")"); break; case SHORT: - writer.append("$rt_createShortArray("); + writer.appendFunction("$rt_createShortArray").append("("); precedence = Precedence.min(); expr.getLength().acceptVisitor(this); writer.append(")"); break; case INTEGER: - writer.append("$rt_createIntArray("); + writer.appendFunction("$rt_createIntArray").append("("); precedence = Precedence.min(); expr.getLength().acceptVisitor(this); writer.append(")"); break; case LONG: - writer.append("$rt_createLongArray("); + writer.appendFunction("$rt_createLongArray").append("("); precedence = Precedence.min(); expr.getLength().acceptVisitor(this); writer.append(")"); break; case FLOAT: - writer.append("$rt_createFloatArray("); + writer.appendFunction("$rt_createFloatArray").append("("); precedence = Precedence.min(); expr.getLength().acceptVisitor(this); writer.append(")"); break; case DOUBLE: - writer.append("$rt_createDoubleArray("); + writer.appendFunction("$rt_createDoubleArray").append("("); precedence = Precedence.min(); expr.getLength().acceptVisitor(this); writer.append(")"); break; case CHARACTER: - writer.append("$rt_createCharArray("); + writer.appendFunction("$rt_createCharArray").append("("); precedence = Precedence.min(); expr.getLength().acceptVisitor(this); writer.append(")"); diff --git a/core/src/main/resources/org/teavm/backend/javascript/long.js b/core/src/main/resources/org/teavm/backend/javascript/long.js index d2c6af669..420a9a452 100644 --- a/core/src/main/resources/org/teavm/backend/javascript/long.js +++ b/core/src/main/resources/org/teavm/backend/javascript/long.js @@ -295,6 +295,9 @@ function Long_shru(a, b) { return new Long((a.hi >>> (b - 32)), 0); } } +function Long_not(a) { + return new Long(~a.hi, ~a.lo); +} // Represents a mutable 80-bit unsigned integer function LongInt(lo, hi, sup) {