diff --git a/core/src/main/java/org/teavm/wasm/Example.java b/core/src/main/java/org/teavm/wasm/Example.java index 9ad86e29d..51f4b157a 100644 --- a/core/src/main/java/org/teavm/wasm/Example.java +++ b/core/src/main/java/org/teavm/wasm/Example.java @@ -15,8 +15,6 @@ */ package org.teavm.wasm; -import org.teavm.wasm.runtime.WasmRuntime; - public final class Example { private Example() { } diff --git a/core/src/main/java/org/teavm/wasm/runtime/WasmRuntime.java b/core/src/main/java/org/teavm/wasm/WasmRuntime.java similarity index 65% rename from core/src/main/java/org/teavm/wasm/runtime/WasmRuntime.java rename to core/src/main/java/org/teavm/wasm/WasmRuntime.java index 96cf96882..173967262 100644 --- a/core/src/main/java/org/teavm/wasm/runtime/WasmRuntime.java +++ b/core/src/main/java/org/teavm/wasm/WasmRuntime.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.teavm.wasm.runtime; +package org.teavm.wasm; import org.teavm.interop.Import; @@ -22,19 +22,19 @@ public final class WasmRuntime { } public static int compare(int a, int b) { - return a > b ? 1 : a < b ? -1 : 0; + return gt(a, b) ? 1 : lt(a, b) ? -1 : 0; } public static int compare(long a, long b) { - return a > b ? 1 : a < b ? -1 : 0; + return gt(a, b) ? 1 : lt(a, b) ? -1 : 0; } public static int compare(float a, float b) { - return a > b ? 1 : a < b ? -1 : 0; + return gt(a, b) ? 1 : lt(a, b) ? -1 : 0; } public static int compare(double a, double b) { - return a > b ? 1 : a < b ? -1 : 0; + return gt(a, b) ? 1 : lt(a, b) ? -1 : 0; } public static float remainder(float a, float b) { @@ -45,6 +45,22 @@ public final class WasmRuntime { return a - (double) (long) (a / b) * b; } + private static native boolean lt(int a, int b); + + private static native boolean gt(int a, int b); + + private static native boolean lt(long a, long b); + + private static native boolean gt(long a, long b); + + private static native boolean lt(float a, float b); + + private static native boolean gt(float a, float b); + + private static native boolean lt(double a, double b); + + private static native boolean gt(double a, double b); + @Import(name = "print", module = "spectest") public static native void print(int a); } diff --git a/core/src/main/java/org/teavm/wasm/WasmTarget.java b/core/src/main/java/org/teavm/wasm/WasmTarget.java index 622ebb261..40f3859b2 100644 --- a/core/src/main/java/org/teavm/wasm/WasmTarget.java +++ b/core/src/main/java/org/teavm/wasm/WasmTarget.java @@ -53,6 +53,7 @@ import org.teavm.wasm.generate.WasmClassGenerator; import org.teavm.wasm.generate.WasmGenerationContext; import org.teavm.wasm.generate.WasmGenerator; import org.teavm.wasm.generate.WasmMangling; +import org.teavm.wasm.intrinsics.WasmRuntimeIntrinsic; import org.teavm.wasm.model.WasmFunction; import org.teavm.wasm.model.WasmModule; import org.teavm.wasm.model.WasmType; @@ -69,7 +70,6 @@ import org.teavm.wasm.model.expression.WasmLoadInt32; import org.teavm.wasm.model.expression.WasmReturn; import org.teavm.wasm.model.expression.WasmStoreInt32; import org.teavm.wasm.render.WasmRenderer; -import org.teavm.wasm.runtime.WasmRuntime; public class WasmTarget implements TeaVMTarget { private TeaVMTargetController controller; @@ -121,6 +121,7 @@ public class WasmTarget implements TeaVMTarget { Decompiler decompiler = new Decompiler(classes, controller.getClassLoader(), new HashSet<>(), new HashSet<>()); WasmGenerationContext context = new WasmGenerationContext(classes); + context.addIntrinsic(new WasmRuntimeIntrinsic()); WasmGenerator generator = new WasmGenerator(decompiler, classes, context, classGenerator); WasmModule module = new WasmModule(); @@ -133,6 +134,9 @@ public class WasmTarget implements TeaVMTarget { && method.getName().equals("initialize")) { continue; } + if (context.getIntrinsic(method.getReference()) != null) { + continue; + } if (method.hasModifier(ElementModifier.NATIVE)) { if (method.getOwnerName().equals(Structure.class.getName()) diff --git a/core/src/main/java/org/teavm/wasm/generate/WasmGenerationContext.java b/core/src/main/java/org/teavm/wasm/generate/WasmGenerationContext.java index b7c844fc6..59c16be25 100644 --- a/core/src/main/java/org/teavm/wasm/generate/WasmGenerationContext.java +++ b/core/src/main/java/org/teavm/wasm/generate/WasmGenerationContext.java @@ -15,7 +15,9 @@ */ package org.teavm.wasm.generate; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import org.teavm.interop.Import; import org.teavm.model.AnnotationReader; @@ -27,15 +29,28 @@ import org.teavm.model.FieldReference; import org.teavm.model.MethodReader; import org.teavm.model.MethodReference; import org.teavm.model.ValueType; +import org.teavm.wasm.intrinsics.WasmIntrinsic; public class WasmGenerationContext { private ClassReaderSource classSource; private Map importedMethods = new HashMap<>(); + private List intrinsics = new ArrayList<>(); + private Map intrinsicCache = new HashMap<>(); public WasmGenerationContext(ClassReaderSource classSource) { this.classSource = classSource; } + public void addIntrinsic(WasmIntrinsic intrinsic) { + intrinsics.add(intrinsic); + } + + public WasmIntrinsic getIntrinsic(MethodReference method) { + return intrinsicCache.computeIfAbsent(method, key -> intrinsics.stream() + .filter(intrinsic -> intrinsic.isApplicable(key)) + .findFirst().orElse(null)); + } + public ImportedMethod getImportedMethod(MethodReference reference) { return importedMethods.computeIfAbsent(reference, ref -> { ClassReader cls = classSource.get(ref.getClassName()); diff --git a/core/src/main/java/org/teavm/wasm/generate/WasmGenerationVisitor.java b/core/src/main/java/org/teavm/wasm/generate/WasmGenerationVisitor.java index 00d5678db..bdd562da4 100644 --- a/core/src/main/java/org/teavm/wasm/generate/WasmGenerationVisitor.java +++ b/core/src/main/java/org/teavm/wasm/generate/WasmGenerationVisitor.java @@ -67,6 +67,8 @@ import org.teavm.model.MethodReference; import org.teavm.model.ValueType; import org.teavm.runtime.Allocator; import org.teavm.runtime.RuntimeClass; +import org.teavm.wasm.intrinsics.WasmIntrinsic; +import org.teavm.wasm.intrinsics.WasmIntrinsicManager; import org.teavm.wasm.model.WasmFunction; import org.teavm.wasm.model.WasmLocal; import org.teavm.wasm.model.WasmType; @@ -102,7 +104,7 @@ import org.teavm.wasm.model.expression.WasmStoreFloat64; import org.teavm.wasm.model.expression.WasmStoreInt32; import org.teavm.wasm.model.expression.WasmStoreInt64; import org.teavm.wasm.model.expression.WasmSwitch; -import org.teavm.wasm.runtime.WasmRuntime; +import org.teavm.wasm.WasmRuntime; class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { private WasmGenerationContext context; @@ -416,11 +418,11 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { @Override public void visit(ConditionalExpr expr) { expr.getCondition().acceptVisitor(this); - WasmConditional conditional = new WasmConditional(result); + WasmConditional conditional = new WasmConditional(forCondition(result)); expr.getConsequent().acceptVisitor(this); conditional.getThenBlock().getBody().add(result); expr.getAlternative().acceptVisitor(this); - conditional.getThenBlock().getBody().add(result); + conditional.getElseBlock().getBody().add(result); result = conditional; } @@ -456,7 +458,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { @Override public void visit(ConditionalStatement statement) { statement.getCondition().acceptVisitor(this); - WasmConditional conditional = new WasmConditional(result); + WasmConditional conditional = new WasmConditional(forCondition(result)); for (Statement part : statement.getConsequent()) { part.acceptVisitor(this); if (result != null) { @@ -569,6 +571,12 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { return; } + WasmIntrinsic intrinsic = context.getIntrinsic(expr.getMethod()); + if (intrinsic != null) { + result = intrinsic.apply(expr, intrinsicManager); + return; + } + if (expr.getType() == InvocationType.STATIC || expr.getType() == InvocationType.SPECIAL) { String methodName = WasmMangling.mangleMethod(expr.getMethod()); @@ -900,6 +908,35 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { return expression instanceof WasmInt32Constant && ((WasmInt32Constant) expression).getValue() == 1; } + private boolean isZero(WasmExpression expression) { + return expression instanceof WasmInt32Constant && ((WasmInt32Constant) expression).getValue() == 0; + } + + private WasmExpression forCondition(WasmExpression expression) { + if (expression instanceof WasmIntBinary) { + WasmIntBinary binary = (WasmIntBinary) expression; + switch (binary.getOperation()) { + case EQ: + if (isZero(binary.getFirst())) { + return negate(binary.getSecond()); + } else if (isZero(binary.getSecond())) { + return negate(binary.getFirst()); + } + break; + case NE: + if (isZero(binary.getFirst())) { + return binary.getSecond(); + } else if (isZero(binary.getSecond())) { + return binary.getFirst(); + } + break; + default: + break; + } + } + return expression; + } + private WasmIntBinaryOperation negate(WasmIntBinaryOperation op) { switch (op) { case EQ: @@ -945,4 +982,12 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { return null; } } + + private WasmIntrinsicManager intrinsicManager = new WasmIntrinsicManager() { + @Override + public WasmExpression generate(Expr expr) { + expr.acceptVisitor(WasmGenerationVisitor.this); + return result; + } + }; } diff --git a/core/src/main/java/org/teavm/wasm/intrinsics/WasmIntrinsic.java b/core/src/main/java/org/teavm/wasm/intrinsics/WasmIntrinsic.java new file mode 100644 index 000000000..306c24e15 --- /dev/null +++ b/core/src/main/java/org/teavm/wasm/intrinsics/WasmIntrinsic.java @@ -0,0 +1,26 @@ +/* + * Copyright 2016 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.wasm.intrinsics; + +import org.teavm.ast.InvocationExpr; +import org.teavm.model.MethodReference; +import org.teavm.wasm.model.expression.WasmExpression; + +public interface WasmIntrinsic { + boolean isApplicable(MethodReference methodReference); + + WasmExpression apply(InvocationExpr invocation, WasmIntrinsicManager manager); +} diff --git a/core/src/main/java/org/teavm/wasm/intrinsics/WasmIntrinsicManager.java b/core/src/main/java/org/teavm/wasm/intrinsics/WasmIntrinsicManager.java new file mode 100644 index 000000000..2bba2adcc --- /dev/null +++ b/core/src/main/java/org/teavm/wasm/intrinsics/WasmIntrinsicManager.java @@ -0,0 +1,23 @@ +/* + * Copyright 2016 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.wasm.intrinsics; + +import org.teavm.ast.Expr; +import org.teavm.wasm.model.expression.WasmExpression; + +public interface WasmIntrinsicManager { + WasmExpression generate(Expr expr); +} diff --git a/core/src/main/java/org/teavm/wasm/intrinsics/WasmRuntimeIntrinsic.java b/core/src/main/java/org/teavm/wasm/intrinsics/WasmRuntimeIntrinsic.java new file mode 100644 index 000000000..0f5aac4fd --- /dev/null +++ b/core/src/main/java/org/teavm/wasm/intrinsics/WasmRuntimeIntrinsic.java @@ -0,0 +1,73 @@ +/* + * Copyright 2016 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.wasm.intrinsics; + +import org.teavm.ast.InvocationExpr; +import org.teavm.model.MethodReference; +import org.teavm.wasm.WasmRuntime; +import org.teavm.wasm.generate.WasmGeneratorUtil; +import org.teavm.wasm.model.WasmType; +import org.teavm.wasm.model.expression.WasmExpression; +import org.teavm.wasm.model.expression.WasmFloatBinary; +import org.teavm.wasm.model.expression.WasmFloatBinaryOperation; +import org.teavm.wasm.model.expression.WasmFloatType; +import org.teavm.wasm.model.expression.WasmIntBinary; +import org.teavm.wasm.model.expression.WasmIntBinaryOperation; +import org.teavm.wasm.model.expression.WasmIntType; + +public class WasmRuntimeIntrinsic implements WasmIntrinsic { + @Override + public boolean isApplicable(MethodReference methodReference) { + return methodReference.getClassName().equals(WasmRuntime.class.getName()) && + (methodReference.getName().equals("lt") || methodReference.getName().equals("gt")); + } + + @Override + public WasmExpression apply(InvocationExpr invocation, WasmIntrinsicManager manager) { + WasmType type = WasmGeneratorUtil.mapType(invocation.getMethod().parameterType(0)); + + WasmExpression first = manager.generate(invocation.getArguments().get(0)); + WasmExpression second = manager.generate(invocation.getArguments().get(1)); + + WasmIntBinaryOperation intOp; + WasmFloatBinaryOperation floatOp; + switch (invocation.getMethod().getName()) { + case "lt": + intOp = WasmIntBinaryOperation.LT_SIGNED; + floatOp = WasmFloatBinaryOperation.LT; + break; + case "gt": + intOp = WasmIntBinaryOperation.GT_SIGNED; + floatOp = WasmFloatBinaryOperation.GT; + break; + default: + throw new IllegalArgumentException(invocation.getMethod().getName()); + } + + switch (type) { + case INT32: + return new WasmIntBinary(WasmIntType.INT32, intOp, first, second); + case INT64: + return new WasmIntBinary(WasmIntType.INT64, intOp, first, second); + case FLOAT32: + return new WasmFloatBinary(WasmFloatType.FLOAT32, floatOp, first, second); + case FLOAT64: + return new WasmFloatBinary(WasmFloatType.FLOAT64, floatOp, first, second); + } + + return null; + } +} diff --git a/core/src/main/java/org/teavm/wasm/render/WasmRenderingVisitor.java b/core/src/main/java/org/teavm/wasm/render/WasmRenderingVisitor.java index b3f539fa7..c3191640e 100644 --- a/core/src/main/java/org/teavm/wasm/render/WasmRenderingVisitor.java +++ b/core/src/main/java/org/teavm/wasm/render/WasmRenderingVisitor.java @@ -180,7 +180,7 @@ class WasmRenderingVisitor implements WasmExpressionVisitor { renderBlock(expression.getThenBlock(), "then"); lf(); - renderBlock(expression.getThenBlock(), "else"); + renderBlock(expression.getElseBlock(), "else"); close(); }