From c9ebe2e2e9c7dc8e1c0ee6a85fcf1100404dd98b Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Sun, 13 May 2018 19:17:45 +0300 Subject: [PATCH] Wasm backend: fix more tests. Implement Array.get. Implement x instanceof C for all cases. --- .../backend/c/generate/ClassGenerator.java | 30 +-- .../lowlevel/generate/ClassGeneratorUtil.java | 56 ++++++ .../org/teavm/backend/wasm/WasmRuntime.java | 13 +- .../org/teavm/backend/wasm/WasmTarget.java | 82 +++++++- .../wasm/generate/WasmClassGenerator.java | 48 ++++- .../wasm/generate/WasmGenerationContext.java | 30 ++- .../wasm/generate/WasmGenerationVisitor.java | 67 ++----- .../wasm/generators/ArrayGenerator.java | 181 ++++++++++++++++++ .../wasm/generators/WasmMethodGenerator.java | 25 +++ .../WasmMethodGeneratorContext.java | 37 ++++ .../java/math/BigIntegerOperateBitsTest.java | 5 +- 11 files changed, 467 insertions(+), 107 deletions(-) create mode 100644 core/src/main/java/org/teavm/backend/lowlevel/generate/ClassGeneratorUtil.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/generators/ArrayGenerator.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/generators/WasmMethodGenerator.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/generators/WasmMethodGeneratorContext.java diff --git a/core/src/main/java/org/teavm/backend/c/generate/ClassGenerator.java b/core/src/main/java/org/teavm/backend/c/generate/ClassGenerator.java index 01cc3f549..3072963f4 100644 --- a/core/src/main/java/org/teavm/backend/c/generate/ClassGenerator.java +++ b/core/src/main/java/org/teavm/backend/c/generate/ClassGenerator.java @@ -27,6 +27,7 @@ import org.teavm.ast.RegularMethodNode; import org.teavm.ast.decompilation.Decompiler; import org.teavm.backend.c.generators.Generator; import org.teavm.backend.c.generators.GeneratorContext; +import org.teavm.backend.lowlevel.generate.ClassGeneratorUtil; import org.teavm.diagnostics.Diagnostics; import org.teavm.interop.Address; import org.teavm.interop.DelegateTo; @@ -442,11 +443,7 @@ public class ClassGenerator { tag = 0; sizeExpr = "sizeof(" + CodeWriter.strictTypeAsString(type) + ")"; flags |= RuntimeClass.PRIMITIVE; - if (type instanceof ValueType.Primitive) { - flags |= getPrimitiveFlag((ValueType.Primitive) type) << RuntimeClass.PRIMITIVE_SHIFT; - } else { - flags |= RuntimeClass.VOID_PRIMITIVE << RuntimeClass.PRIMITIVE_SHIFT; - } + flags = ClassGeneratorUtil.applyPrimitiveFlags(flags, type); itemTypeExpr = "NULL"; } @@ -551,29 +548,6 @@ public class ClassGenerator { layoutWriter.println().outdent().println("};"); } - private int getPrimitiveFlag(ValueType.Primitive type) { - switch (type.getKind()) { - case BOOLEAN: - return RuntimeClass.BOOLEAN_PRIMITIVE; - case BYTE: - return RuntimeClass.BYTE_PRIMITIVE; - case SHORT: - return RuntimeClass.SHORT_PRIMITIVE; - case CHARACTER: - return RuntimeClass.CHAR_PRIMITIVE; - case INTEGER: - return RuntimeClass.INT_PRIMITIVE; - case LONG: - return RuntimeClass.LONG_PRIMITIVE; - case FLOAT: - return RuntimeClass.FLOAT_PRIMITIVE; - case DOUBLE: - return RuntimeClass.DOUBLE_PRIMITIVE; - default: - throw new AssertionError(); - } - } - private String classFieldName(String field) { return context.getNames().forMemberField(new FieldReference(RuntimeClass.class.getName(), field)); } diff --git a/core/src/main/java/org/teavm/backend/lowlevel/generate/ClassGeneratorUtil.java b/core/src/main/java/org/teavm/backend/lowlevel/generate/ClassGeneratorUtil.java new file mode 100644 index 000000000..c69e08d47 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/lowlevel/generate/ClassGeneratorUtil.java @@ -0,0 +1,56 @@ +/* + * Copyright 2018 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.backend.lowlevel.generate; + +import org.teavm.model.ValueType; +import org.teavm.runtime.RuntimeClass; + +public final class ClassGeneratorUtil { + private ClassGeneratorUtil() { + } + + public static int applyPrimitiveFlags(int flags, ValueType type) { + if (type instanceof ValueType.Primitive) { + flags |= getPrimitiveFlag((ValueType.Primitive) type) << RuntimeClass.PRIMITIVE_SHIFT; + } else { + flags |= RuntimeClass.VOID_PRIMITIVE << RuntimeClass.PRIMITIVE_SHIFT; + } + return flags; + } + + private static int getPrimitiveFlag(ValueType.Primitive type) { + switch (type.getKind()) { + case BOOLEAN: + return RuntimeClass.BOOLEAN_PRIMITIVE; + case BYTE: + return RuntimeClass.BYTE_PRIMITIVE; + case SHORT: + return RuntimeClass.SHORT_PRIMITIVE; + case CHARACTER: + return RuntimeClass.CHAR_PRIMITIVE; + case INTEGER: + return RuntimeClass.INT_PRIMITIVE; + case LONG: + return RuntimeClass.LONG_PRIMITIVE; + case FLOAT: + return RuntimeClass.FLOAT_PRIMITIVE; + case DOUBLE: + return RuntimeClass.DOUBLE_PRIMITIVE; + default: + throw new AssertionError(); + } + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/WasmRuntime.java b/core/src/main/java/org/teavm/backend/wasm/WasmRuntime.java index 4cdaddb77..10b380f1f 100644 --- a/core/src/main/java/org/teavm/backend/wasm/WasmRuntime.java +++ b/core/src/main/java/org/teavm/backend/wasm/WasmRuntime.java @@ -203,24 +203,21 @@ public final class WasmRuntime { } while (alignedSourceEnd.toInt() > alignedSourceStart.toInt()) { - alignedTargetEnd.putInt(alignedSourceEnd.getInt()); alignedSourceEnd = alignedSourceEnd.add(-4); alignedTargetEnd = alignedTargetEnd.add(-4); + alignedTargetEnd.putInt(alignedSourceEnd.getInt()); } switch (source.toInt() - alignedSourceStart.toInt()) { - case 0: - alignedTargetStart.putInt(alignedSourceStart.getInt()); - break; case 1: - alignedTargetStart.add(2).putShort(alignedSourceStart.add(2).getShort()); - alignedTargetStart.add(1).putByte(alignedSourceStart.add(1).getByte()); + alignedTargetStart.add(-2).putShort(alignedSourceStart.add(-2).getShort()); + alignedTargetStart.add(-3).putByte(alignedSourceStart.add(-3).getByte()); break; case 2: - alignedTargetStart.add(2).putShort(alignedSourceStart.add(2).getShort()); + alignedTargetStart.add(-2).putShort(alignedSourceStart.add(-2).getShort()); break; case 3: - alignedTargetStart.add(3).putByte(alignedSourceStart.add(3).getByte()); + alignedTargetStart.add(-1).putByte(alignedSourceStart.add(-1).getByte()); break; } } diff --git a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java index d2f9743af..ed9ca0bd9 100644 --- a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java +++ b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java @@ -35,6 +35,9 @@ import org.teavm.backend.wasm.generate.WasmDependencyListener; import org.teavm.backend.wasm.generate.WasmGenerationContext; import org.teavm.backend.wasm.generate.WasmGenerator; import org.teavm.backend.wasm.generate.WasmStringPool; +import org.teavm.backend.wasm.generators.ArrayGenerator; +import org.teavm.backend.wasm.generators.WasmMethodGenerator; +import org.teavm.backend.wasm.generators.WasmMethodGeneratorContext; import org.teavm.backend.wasm.intrinsics.AddressIntrinsic; import org.teavm.backend.wasm.intrinsics.AllocatorIntrinsic; import org.teavm.backend.wasm.intrinsics.ClassIntrinsic; @@ -83,6 +86,7 @@ import org.teavm.common.ServiceRepository; import org.teavm.dependency.ClassDependency; import org.teavm.dependency.DependencyAnalyzer; import org.teavm.dependency.DependencyListener; +import org.teavm.diagnostics.Diagnostics; import org.teavm.interop.Address; import org.teavm.interop.DelegateTo; import org.teavm.interop.Import; @@ -305,8 +309,8 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { TagRegistry tagRegistry = new TagRegistry(classes); BinaryWriter binaryWriter = new BinaryWriter(256); NameProvider names = new NameProvider(controller.getUnprocessedClassSource()); - WasmClassGenerator classGenerator = new WasmClassGenerator( - controller.getUnprocessedClassSource(), vtableProvider, tagRegistry, binaryWriter, names); + WasmClassGenerator classGenerator = new WasmClassGenerator(classes, controller.getUnprocessedClassSource(), + vtableProvider, tagRegistry, binaryWriter, names); Decompiler decompiler = new Decompiler(classes, controller.getClassLoader(), new HashSet<>(), new HashSet<>(), false, true); @@ -325,6 +329,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { context.addIntrinsic(new PlatformObjectIntrinsic(classGenerator)); context.addIntrinsic(new PlatformClassMetadataIntrinsic()); context.addIntrinsic(new ClassIntrinsic()); + context.addGenerator(new ArrayGenerator()); IntrinsicFactoryContext intrinsicFactoryContext = new IntrinsicFactoryContext(); for (WasmIntrinsicFactory additionalIntrinsicFactory : additionalIntrinsics) { @@ -345,7 +350,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { int pageSize = 1 << 16; int pages = (minHeapSize + pageSize - 1) / pageSize; module.setMemorySize(pages); - generateMethods(classes, context, generator, module); + generateMethods(classes, context, generator, classGenerator, binaryWriter, module); exceptionHandlingIntrinsic.postProcess(shadowStackTransformer.getCallSites()); generateIsSupertypeFunctions(tagRegistry, module, classGenerator); classGenerator.postProcess(); @@ -465,7 +470,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { } private void generateMethods(ListableClassHolderSource classes, WasmGenerationContext context, - WasmGenerator generator, WasmModule module) { + WasmGenerator generator, WasmClassGenerator classGenerator, BinaryWriter binaryWriter, WasmModule module) { List methods = new ArrayList<>(); for (String className : classes.getClassNames()) { ClassHolder cls = classes.get(className); @@ -478,6 +483,9 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { } } + MethodGeneratorContextImpl methodGeneratorContext = new MethodGeneratorContextImpl(binaryWriter, + context.getStringPool(), context.getDiagnostics(), context.names, classGenerator, classes); + for (MethodHolder method : methods) { ClassHolder cls = classes.get(method.getOwnerName()); @@ -501,12 +509,18 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { } if (implementor.hasModifier(ElementModifier.NATIVE)) { - if (context.getImportedMethod(method.getReference()) == null) { - CallLocation location = new CallLocation(method.getReference()); - controller.getDiagnostics().error(location, "Method {{m0}} is native but " - + "has no {{c1}} annotation on it", method.getReference(), Import.class.getName()); + WasmMethodGenerator methodGenerator = context.getGenerator(method.getReference()); + if (methodGenerator != null) { + WasmFunction function = context.getFunction(context.names.forMethod(method.getReference())); + methodGenerator.apply(method.getReference(), function, methodGeneratorContext); + } else { + if (context.getImportedMethod(method.getReference()) == null) { + CallLocation location = new CallLocation(method.getReference()); + controller.getDiagnostics().error(location, "Method {{m0}} is native but " + + "has no {{c1}} annotation on it", method.getReference(), Import.class.getName()); + } + generator.generateNative(method.getReference()); } - generator.generateNative(method.getReference()); continue; } if (implementor.getProgram() == null || implementor.getProgram().basicBlockCount() == 0) { @@ -757,4 +771,54 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { public boolean isAsyncSupported() { return false; } + + static class MethodGeneratorContextImpl implements WasmMethodGeneratorContext { + private BinaryWriter binaryWriter; + private WasmStringPool stringPool; + private Diagnostics diagnostics; + private NameProvider names; + private WasmClassGenerator classGenerator; + private ClassReaderSource classSource; + + MethodGeneratorContextImpl(BinaryWriter binaryWriter, WasmStringPool stringPool, + Diagnostics diagnostics, NameProvider names, WasmClassGenerator classGenerator, + ClassReaderSource classSource) { + this.binaryWriter = binaryWriter; + this.stringPool = stringPool; + this.diagnostics = diagnostics; + this.names = names; + this.classGenerator = classGenerator; + this.classSource = classSource; + } + + @Override + public BinaryWriter getBinaryWriter() { + return binaryWriter; + } + + @Override + public WasmStringPool getStringPool() { + return stringPool; + } + + @Override + public Diagnostics getDiagnostics() { + return diagnostics; + } + + @Override + public NameProvider getNames() { + return names; + } + + @Override + public WasmClassGenerator getClassGenerator() { + return classGenerator; + } + + @Override + public ClassReaderSource getClassSource() { + return classSource; + } + } } \ No newline at end of file diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/WasmClassGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/WasmClassGenerator.java index 301bfdca8..e0461dedc 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/WasmClassGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/WasmClassGenerator.java @@ -50,6 +50,7 @@ import org.teavm.runtime.RuntimeClass; import org.teavm.runtime.RuntimeObject; public class WasmClassGenerator { + private ClassReaderSource processedClassSource; private ClassReaderSource classSource; public final NameProvider names; private Map binaryDataMap = new LinkedHashMap<>(); @@ -95,8 +96,10 @@ public class WasmClassGenerator { private static final int CLASS_LAYOUT = 12; private static final int CLASS_SIMPLE_NAME = 13; - public WasmClassGenerator(ClassReaderSource classSource, VirtualTableProvider vtableProvider, - TagRegistry tagRegistry, BinaryWriter binaryWriter, NameProvider names) { + public WasmClassGenerator(ClassReaderSource processedClassSource, ClassReaderSource classSource, + VirtualTableProvider vtableProvider, TagRegistry tagRegistry, BinaryWriter binaryWriter, + NameProvider names) { + this.processedClassSource = processedClassSource; this.classSource = classSource; this.vtableProvider = vtableProvider; this.tagRegistry = tagRegistry; @@ -175,7 +178,7 @@ public class WasmClassGenerator { binaryData.data.setInt(CLASS_IS_INSTANCE, functionTable.size()); binaryData.data.setInt(CLASS_CANARY, RuntimeClass.computeCanary(4, 0)); functionTable.add(names.forSupertypeFunction(type)); - binaryData.data.setAddress(CLASS_NAME, stringPool.getStringPointer(type.toString())); + binaryData.data.setAddress(CLASS_NAME, stringPool.getStringPointer(type.toString().replace('/', '.'))); binaryData.data.setAddress(CLASS_SIMPLE_NAME, 0); binaryData.data.setInt(CLASS_INIT, -1); binaryData.data.setAddress(CLASS_PARENT, getClassPointer(ValueType.object("java.lang.Object"))); @@ -193,6 +196,43 @@ public class WasmClassGenerator { value.setAddress(CLASS_SIMPLE_NAME, 0); value.setInt(CLASS_INIT, -1); functionTable.add(names.forSupertypeFunction(type)); + + String name; + if (type == ValueType.VOID) { + name = "void"; + } else { + switch (((ValueType.Primitive) type).getKind()) { + case BOOLEAN: + name = "boolean"; + break; + case BYTE: + name = "byte"; + break; + case SHORT: + name = "short"; + break; + case CHARACTER: + name = "char"; + break; + case INTEGER: + name = "int"; + break; + case LONG: + name = "long"; + break; + case FLOAT: + name = "float"; + break; + case DOUBLE: + name = "double"; + break; + default: + name = ""; + } + } + + value.setAddress(CLASS_NAME, stringPool.getStringPointer(name)); + return value; } @@ -253,7 +293,7 @@ public class WasmClassGenerator { staticGcRoots.add(binaryData.fieldLayout.get(field.getFieldName())); } - ClassReader cls = classSource.get(name); + ClassReader cls = processedClassSource.get(name); if (cls != null && cls.hasModifier(ElementModifier.ENUM)) { header.setAddress(CLASS_ENUM_VALUES, generateEnumValues(cls, binaryData)); flags |= RuntimeClass.ENUM; diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationContext.java b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationContext.java index e423764ea..c08e090c0 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationContext.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationContext.java @@ -19,6 +19,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import org.teavm.backend.wasm.generators.WasmMethodGenerator; import org.teavm.backend.wasm.intrinsics.WasmIntrinsic; import org.teavm.backend.wasm.model.WasmFunction; import org.teavm.backend.wasm.model.WasmModule; @@ -46,7 +47,9 @@ public class WasmGenerationContext { public final NameProvider names; private Map importedMethods = new HashMap<>(); private List intrinsics = new ArrayList<>(); - private Map intrinsicCache = new HashMap<>(); + private List generators = new ArrayList<>(); + private Map intrinsicCache = new HashMap<>(); + private Map generatorCache = new HashMap<>(); public WasmGenerationContext(ClassReaderSource classSource, WasmModule module, Diagnostics diagnostics, VirtualTableProvider vtableProvider, TagRegistry tagRegistry, WasmStringPool stringPool, @@ -64,17 +67,36 @@ public class WasmGenerationContext { intrinsics.add(intrinsic); } + public void addGenerator(WasmMethodGenerator generator) { + generators.add(generator); + } + public WasmIntrinsic getIntrinsic(MethodReference method) { - return intrinsicCache.computeIfAbsent(method, key -> new WasmIntrinsicHolder(intrinsics.stream() + return intrinsicCache.computeIfAbsent(method, key -> new IntrinsicHolder(intrinsics.stream() .filter(intrinsic -> intrinsic.isApplicable(key)) .findFirst().orElse(null))) .value; } - private static class WasmIntrinsicHolder { + public WasmMethodGenerator getGenerator(MethodReference method) { + return generatorCache.computeIfAbsent(method, key -> new GeneratorHolder(generators.stream() + .filter(generator -> generator.isApplicable(key)) + .findFirst().orElse(null))) + .value; + } + + static class IntrinsicHolder { WasmIntrinsic value; - public WasmIntrinsicHolder(WasmIntrinsic value) { + IntrinsicHolder(WasmIntrinsic value) { + this.value = value; + } + } + + static class GeneratorHolder { + WasmMethodGenerator value; + + GeneratorHolder(WasmMethodGenerator value) { this.value = value; } } diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java index 44ef81ada..28f09c6bf 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java @@ -113,7 +113,6 @@ import org.teavm.model.FieldReference; import org.teavm.model.MethodReference; import org.teavm.model.TextLocation; import org.teavm.model.ValueType; -import org.teavm.model.classes.TagRegistry; import org.teavm.model.classes.VirtualTableEntry; import org.teavm.runtime.Allocator; import org.teavm.runtime.RuntimeArray; @@ -690,7 +689,7 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { break; } - int base = classGenerator.getClassSize(RuntimeArray.class.getName()); + int base = BinaryWriter.align(classGenerator.getClassSize(RuntimeArray.class.getName()), 1 << size); array = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD, array, new WasmInt32Constant(base)); if (size != 0) { index = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHL, index, @@ -1270,58 +1269,24 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { public void visit(InstanceOfExpr expr) { accept(expr.getExpr()); - if (expr.getType() instanceof ValueType.Object) { - ValueType.Object cls = (ValueType.Object) expr.getType(); - List ranges = context.getTagRegistry().getRanges(cls.getClassName()); - ranges.sort(Comparator.comparingInt(range -> range.lower)); + WasmBlock block = new WasmBlock(false); + block.setType(WasmType.INT32); + block.setLocation(expr.getLocation()); - WasmBlock block = new WasmBlock(false); - block.setType(WasmType.INT32); - block.setLocation(expr.getLocation()); + WasmLocal objectVar = getTemporary(WasmType.INT32); + block.getBody().add(new WasmSetLocal(objectVar, result)); - WasmLocal tagVar = getTemporary(WasmType.INT32); - int tagOffset = classGenerator.getFieldOffset(tagField); - WasmExpression tagPtr = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD, - getReferenceToClass(result), new WasmInt32Constant(tagOffset)); - block.getBody().add(new WasmSetLocal(tagVar, new WasmLoadInt32(4, tagPtr, WasmInt32Subtype.INT32))); + WasmBranch ifNull = new WasmBranch(new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.EQ, + new WasmGetLocal(objectVar), new WasmInt32Constant(0)), block); + ifNull.setResult(new WasmInt32Constant(0)); + block.getBody().add(ifNull); - WasmExpression lowerThanMinCond = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.LT_SIGNED, - new WasmGetLocal(tagVar), new WasmInt32Constant(ranges.get(0).lower)); - WasmBranch lowerThanMin = new WasmBranch(lowerThanMinCond, block); - lowerThanMin.setResult(new WasmInt32Constant(0)); - block.getBody().add(new WasmDrop(lowerThanMin)); - - WasmExpression upperThanMaxCond = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.GT_SIGNED, - new WasmGetLocal(tagVar), new WasmInt32Constant(ranges.get(ranges.size() - 1).upper)); - WasmBranch upperThanMax = new WasmBranch(upperThanMaxCond, block); - upperThanMax.setResult(new WasmInt32Constant(0)); - block.getBody().add(new WasmDrop(upperThanMax)); - - for (int i = 1; i < ranges.size(); ++i) { - WasmExpression upperThanExcluded = new WasmIntBinary(WasmIntType.INT32, - WasmIntBinaryOperation.GT_SIGNED, new WasmGetLocal(tagVar), - new WasmInt32Constant(ranges.get(i - 1).upper)); - WasmConditional conditional = new WasmConditional(upperThanExcluded); - WasmExpression lowerThanExcluded = new WasmIntBinary(WasmIntType.INT32, - WasmIntBinaryOperation.LT_SIGNED, new WasmGetLocal(tagVar), - new WasmInt32Constant(ranges.get(i).lower)); - - WasmBranch branch = new WasmBranch(lowerThanExcluded, block); - branch.setResult(new WasmInt32Constant(0)); - conditional.getThenBlock().getBody().add(new WasmDrop(branch)); - - block.getBody().add(conditional); - } - - block.getBody().add(new WasmInt32Constant(1)); - releaseTemporary(tagVar); - - result = block; - } else if (expr.getType() instanceof ValueType.Array) { - throw new UnsupportedOperationException(); - } else { - throw new AssertionError(); - } + WasmCall supertypeCall = new WasmCall(context.names.forSupertypeFunction(expr.getType())); + int tagOffset = classGenerator.getFieldOffset(tagField); + WasmExpression classRef = new WasmLoadInt32(4, result, WasmInt32Subtype.INT32, tagOffset); + classRef = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHL, classRef, new WasmInt32Constant(3)); + supertypeCall.getArguments().add(classRef); + block.getBody().add(supertypeCall); } @Override diff --git a/core/src/main/java/org/teavm/backend/wasm/generators/ArrayGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generators/ArrayGenerator.java new file mode 100644 index 000000000..ccf09af28 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/generators/ArrayGenerator.java @@ -0,0 +1,181 @@ +/* + * Copyright 2018 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.backend.wasm.generators; + +import java.lang.reflect.Array; +import org.teavm.backend.wasm.binary.BinaryWriter; +import org.teavm.backend.wasm.model.WasmFunction; +import org.teavm.backend.wasm.model.WasmLocal; +import org.teavm.backend.wasm.model.WasmType; +import org.teavm.backend.wasm.model.expression.WasmBlock; +import org.teavm.backend.wasm.model.expression.WasmBranch; +import org.teavm.backend.wasm.model.expression.WasmCall; +import org.teavm.backend.wasm.model.expression.WasmExpression; +import org.teavm.backend.wasm.model.expression.WasmGetLocal; +import org.teavm.backend.wasm.model.expression.WasmInt32Constant; +import org.teavm.backend.wasm.model.expression.WasmInt32Subtype; +import org.teavm.backend.wasm.model.expression.WasmInt64Subtype; +import org.teavm.backend.wasm.model.expression.WasmIntBinary; +import org.teavm.backend.wasm.model.expression.WasmIntBinaryOperation; +import org.teavm.backend.wasm.model.expression.WasmIntType; +import org.teavm.backend.wasm.model.expression.WasmLoadFloat32; +import org.teavm.backend.wasm.model.expression.WasmLoadInt32; +import org.teavm.backend.wasm.model.expression.WasmLoadInt64; +import org.teavm.backend.wasm.model.expression.WasmReturn; +import org.teavm.backend.wasm.model.expression.WasmSetLocal; +import org.teavm.backend.wasm.model.expression.WasmSwitch; +import org.teavm.model.ClassReader; +import org.teavm.model.FieldReference; +import org.teavm.model.MethodReference; +import org.teavm.model.ValueType; +import org.teavm.runtime.RuntimeArray; +import org.teavm.runtime.RuntimeClass; + +public class ArrayGenerator implements WasmMethodGenerator { + private static FieldReference tagField = new FieldReference(RuntimeClass.class.getName(), "tag"); + private static FieldReference componentField = new FieldReference(RuntimeClass.class.getName(), "itemType"); + private static FieldReference flagsField = new FieldReference(RuntimeClass.class.getName(), "flags"); + private static final int[] primitives = { + RuntimeClass.BYTE_PRIMITIVE, + RuntimeClass.SHORT_PRIMITIVE, + RuntimeClass.CHAR_PRIMITIVE, + RuntimeClass.INT_PRIMITIVE, + RuntimeClass.LONG_PRIMITIVE, + RuntimeClass.FLOAT_PRIMITIVE, + RuntimeClass.DOUBLE_PRIMITIVE, + RuntimeClass.BOOLEAN_PRIMITIVE + }; + private static final String[] primitiveWrappers = { "Byte", "Short", "Character", "Integer", "Long", + "Float", "Double", "Boolean" }; + private static final ValueType.Primitive[] primitiveTypes = { ValueType.BYTE, ValueType.SHORT, ValueType.CHARACTER, + ValueType.INTEGER, ValueType.LONG, ValueType.FLOAT, ValueType.DOUBLE, ValueType.BOOLEAN }; + private static final int[] shift = new int[] { 0, 1, 1, 2, 3, 2, 3, 0 }; + + @Override + public boolean isApplicable(MethodReference methodReference) { + if (!methodReference.getClassName().equals(Array.class.getName())) { + return false; + } + + switch (methodReference.getName()) { + case "getImpl": + return true; + default: + return false; + } + } + + @Override + public void apply(MethodReference method, WasmFunction function, WasmMethodGeneratorContext context) { + WasmLocal arrayVar = new WasmLocal(WasmType.INT32, "_array"); + WasmLocal indexVar = new WasmLocal(WasmType.INT32, "_index"); + WasmLocal flagsVar = new WasmLocal(WasmType.INT32, "_flags"); + function.add(arrayVar); + function.add(indexVar); + function.add(flagsVar); + + int tagOffset = context.getClassGenerator().getFieldOffset(tagField); + WasmExpression arrayClass = new WasmLoadInt32(4, new WasmGetLocal(arrayVar), + WasmInt32Subtype.INT32, tagOffset); + arrayClass = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHL, arrayClass, + new WasmInt32Constant(3)); + + int componentOffset = context.getClassGenerator().getFieldOffset(componentField); + WasmExpression componentClass = new WasmLoadInt32(4, arrayClass, WasmInt32Subtype.INT32, componentOffset); + + int flagsOffset = context.getClassGenerator().getFieldOffset(flagsField); + WasmExpression flags = new WasmLoadInt32(4, componentClass, WasmInt32Subtype.INT32, flagsOffset); + function.getBody().add(new WasmSetLocal(flagsVar, flags)); + + WasmExpression isPrimitive = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.AND, + new WasmGetLocal(flagsVar), new WasmInt32Constant(RuntimeClass.PRIMITIVE)); + WasmBlock objectBlock = new WasmBlock(false); + objectBlock.getBody().add(new WasmBranch(isPrimitive, objectBlock)); + function.getBody().add(objectBlock); + int base = context.getClassGenerator().getClassSize(RuntimeArray.class.getName()); + + WasmBlock currentBlock = new WasmBlock(false); + function.getBody().add(currentBlock); + function.getBody().add(new WasmReturn(new WasmInt32Constant(0))); + + WasmSwitch primitiveSwitch = new WasmSwitch(new WasmGetLocal(flagsVar), currentBlock); + for (int i = 0; i <= 8; ++i) { + primitiveSwitch.getTargets().add(currentBlock); + } + + for (int i = 0; i < primitiveWrappers.length; ++i) { + String wrapper = "java.lang." + primitiveWrappers[i]; + MethodReference methodRef = new MethodReference(wrapper, "valueOf", + primitiveTypes[i], ValueType.object(wrapper)); + ClassReader cls = context.getClassSource().get(methodRef.getClassName()); + if (cls == null || cls.getMethod(methodRef.getDescriptor()) == null) { + continue; + } + + WasmBlock nextBlock = new WasmBlock(false); + primitiveSwitch.getTargets().set(i, nextBlock); + + WasmExpression offset = new WasmGetLocal(indexVar); + if (shift[i] != 0) { + offset = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHL, + new WasmGetLocal(indexVar), new WasmInt32Constant(shift[i])); + } + offset = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD, offset, + new WasmGetLocal(arrayVar)); + int baseAddr = BinaryWriter.align(base, 1 << shift[i]); + + WasmCall call = new WasmCall(context.getNames().forMethod(methodRef)); + + switch (primitiveTypes[i].getKind()) { + case BOOLEAN: + case BYTE: + call.getArguments().add(new WasmLoadInt32(0, offset, WasmInt32Subtype.INT8, baseAddr)); + break; + case SHORT: + call.getArguments().add(new WasmLoadInt32(1, offset, WasmInt32Subtype.INT16, baseAddr)); + break; + case CHARACTER: + call.getArguments().add(new WasmLoadInt32(1, offset, WasmInt32Subtype.UINT16, baseAddr)); + break; + case INTEGER: + call.getArguments().add(new WasmLoadInt32(2, offset, WasmInt32Subtype.INT16, baseAddr)); + break; + case LONG: + call.getArguments().add(new WasmLoadInt64(3, offset, WasmInt64Subtype.INT64, baseAddr)); + break; + case FLOAT: + call.getArguments().add(new WasmLoadFloat32(2, offset, baseAddr)); + break; + case DOUBLE: + call.getArguments().add(new WasmLoadFloat32(3, offset, baseAddr)); + break; + } + + currentBlock.getBody().add(nextBlock); + currentBlock.getBody().add(new WasmReturn(call)); + currentBlock = nextBlock; + } + + currentBlock.getBody().add(primitiveSwitch); + + WasmExpression objectOffset = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHL, + new WasmGetLocal(indexVar), new WasmInt32Constant(2)); + objectOffset = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD, objectOffset, + new WasmGetLocal(arrayVar)); + objectBlock.getBody().add(new WasmReturn( + new WasmLoadInt32(4, objectOffset, WasmInt32Subtype.INT32, BinaryWriter.align(base, 4)))); + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/generators/WasmMethodGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generators/WasmMethodGenerator.java new file mode 100644 index 000000000..bc7253e51 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/generators/WasmMethodGenerator.java @@ -0,0 +1,25 @@ +/* + * Copyright 2018 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.backend.wasm.generators; + +import org.teavm.backend.wasm.model.WasmFunction; +import org.teavm.model.MethodReference; + +public interface WasmMethodGenerator { + boolean isApplicable(MethodReference methodReference); + + void apply(MethodReference method, WasmFunction function, WasmMethodGeneratorContext context); +} diff --git a/core/src/main/java/org/teavm/backend/wasm/generators/WasmMethodGeneratorContext.java b/core/src/main/java/org/teavm/backend/wasm/generators/WasmMethodGeneratorContext.java new file mode 100644 index 000000000..75af3c984 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/generators/WasmMethodGeneratorContext.java @@ -0,0 +1,37 @@ +/* + * Copyright 2018 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.backend.wasm.generators; + +import org.teavm.backend.wasm.binary.BinaryWriter; +import org.teavm.backend.wasm.generate.NameProvider; +import org.teavm.backend.wasm.generate.WasmClassGenerator; +import org.teavm.backend.wasm.generate.WasmStringPool; +import org.teavm.diagnostics.Diagnostics; +import org.teavm.model.ClassReaderSource; + +public interface WasmMethodGeneratorContext { + BinaryWriter getBinaryWriter(); + + WasmStringPool getStringPool(); + + Diagnostics getDiagnostics(); + + NameProvider getNames(); + + ClassReaderSource getClassSource(); + + WasmClassGenerator getClassGenerator(); +} diff --git a/tests/src/test/java/org/teavm/classlib/java/math/BigIntegerOperateBitsTest.java b/tests/src/test/java/org/teavm/classlib/java/math/BigIntegerOperateBitsTest.java index a8bd5a624..251e0574b 100644 --- a/tests/src/test/java/org/teavm/classlib/java/math/BigIntegerOperateBitsTest.java +++ b/tests/src/test/java/org/teavm/classlib/java/math/BigIntegerOperateBitsTest.java @@ -214,10 +214,9 @@ public class BigIntegerOperateBitsTest { byte[] rBytes = {-2, 127, -57, -101, 1, 75, -90, -46, -92, -4, 14, 92, -26}; BigInteger aNumber = new BigInteger(aSign, aBytes); BigInteger result = aNumber.clearBit(number); - byte[] resBytes = new byte[rBytes.length]; - resBytes = result.toByteArray(); + byte[] resBytes = result.toByteArray(); for (int i = 0; i < resBytes.length; i++) { - assertTrue(resBytes[i] == rBytes[i]); + assertEquals("Byte " + i, rBytes[i], resBytes[i]); } assertEquals("incorrect sign", -1, result.signum()); }