From ca3258417b1815fe5b2eb30801825d1bd884bf7b Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Sat, 27 Aug 2016 18:22:49 +0300 Subject: [PATCH] Implementing System.arraycopy --- .../org/teavm/classlib/java/lang/TSystem.java | 27 +++- .../java/org/teavm/backend/wasm/Example.java | 20 +++ .../org/teavm/backend/wasm/WasmRuntime.java | 121 +++++++++++++++++- .../org/teavm/backend/wasm/WasmTarget.java | 6 +- .../wasm/generate/WasmClassGenerator.java | 15 ++- .../backend/wasm/generate/WasmGenerator.java | 5 +- ...ssIntrinsic.java => AddressIntrinsic.java} | 47 ++++++- .../wasm/intrinsics/AllocatorIntrinsic.java | 9 +- .../java/org/teavm/runtime/Allocator.java | 2 + .../main/java/org/teavm/interop/Address.java | 6 + .../java/org/teavm/interop/Structure.java | 2 - .../java/org/teavm/platform/Platform.java | 4 - 12 files changed, 236 insertions(+), 28 deletions(-) rename core/src/main/java/org/teavm/backend/wasm/intrinsics/{WasmAddressIntrinsic.java => AddressIntrinsic.java} (72%) diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/TSystem.java b/classlib/src/main/java/org/teavm/classlib/java/lang/TSystem.java index 0624ac602..af7e4c67f 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/TSystem.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/TSystem.java @@ -21,11 +21,13 @@ import org.teavm.classlib.java.io.TPrintStream; import org.teavm.classlib.java.lang.reflect.TArray; import org.teavm.dependency.PluggableDependency; import org.teavm.backend.javascript.spi.GeneratedBy; +import org.teavm.interop.Address; +import org.teavm.interop.DelegateTo; +import org.teavm.interop.Structure; +import org.teavm.runtime.Allocator; +import org.teavm.runtime.RuntimeArray; +import org.teavm.runtime.RuntimeClass; -/** - * - * @author Alexey Andreev - */ public final class TSystem extends TObject { public static final TPrintStream out = new TPrintStream(new TConsoleOutputStreamStdout(), false); public static final TPrintStream err = new TPrintStream(new TConsoleOutputStreamStderr(), false); @@ -74,8 +76,25 @@ public final class TSystem extends TObject { } @GeneratedBy(SystemNativeGenerator.class) + @DelegateTo("doArrayCopyLowLevel") private static native void doArrayCopy(Object src, int srcPos, Object dest, int destPos, int length); + static void doArrayCopyLowLevel(RuntimeArray src, int srcPos, RuntimeArray dest, int destPos, int length) { + RuntimeClass type = Address.fromInt(src.classReference << 3).toStructure(); + int itemSize = type.itemType.size; + if ((type.itemType.flags & RuntimeClass.PRIMITIVE) == 0) { + itemSize = Address.sizeOf(); + } + + Address srcAddress = Address.align(src.toAddress().add(RuntimeArray.class, 1), itemSize); + srcAddress = srcAddress.add(itemSize * srcPos); + + Address destAddress = Address.align(dest.toAddress().add(RuntimeArray.class, 1), itemSize); + destAddress = destAddress.add(itemSize * destPos); + + Allocator.moveMemoryBlock(srcAddress, destAddress, length * itemSize); + } + @GeneratedBy(SystemNativeGenerator.class) public static native long currentTimeMillis(); diff --git a/core/src/main/java/org/teavm/backend/wasm/Example.java b/core/src/main/java/org/teavm/backend/wasm/Example.java index 1cd93d5ee..aad1a5e25 100644 --- a/core/src/main/java/org/teavm/backend/wasm/Example.java +++ b/core/src/main/java/org/teavm/backend/wasm/Example.java @@ -32,6 +32,7 @@ public final class Example { testLazyInitialization(); testHashCode(); testArrayList(); + //testArrayCopy(); } private static void testFibonacci() { @@ -101,6 +102,25 @@ public final class Example { } } + private static void testArrayCopy() { + byte[] array = new byte[100]; + byte[] negatives = new byte[100]; + for (int i = 0; i < array.length; ++i) { + array[i] = (byte) i; + negatives[i] = (byte) -i; + } + System.arraycopy(negatives, 4, array, 0, 12); + System.arraycopy(negatives, 1, array, 21, 12); + System.arraycopy(negatives, 2, array, 35, 12); + System.arraycopy(negatives, 1, array, 8, 3); + System.arraycopy(array, 50, array, 54, 12); + StringBuilder sb = new StringBuilder("arrayCopy result:"); + for (int i = 0; i < array.length; ++i) { + sb.append(" ").append(array[i]); + } + println(sb.toString()); + } + private static Base instance(int index) { switch (index) { case 0: 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 8c90cf5ef..38d8c8c74 100644 --- a/core/src/main/java/org/teavm/backend/wasm/WasmRuntime.java +++ b/core/src/main/java/org/teavm/backend/wasm/WasmRuntime.java @@ -166,7 +166,7 @@ public final class WasmRuntime { return (a << 56) | (b << 48) | (c << 40) | (d << 32) | (e << 24) | (f << 16) | (g << 8) | h; } - private static Address align(Address address, int alignment) { + public static Address align(Address address, int alignment) { int value = address.toInt(); if (value == 0) { return address; @@ -232,8 +232,125 @@ public final class WasmRuntime { break; } - for (address = Address.fromInt(alignedStart + 1); address.toInt() < alignedEnd; address = address.add(4)) { + for (address = Address.fromInt(alignedStart + 4); address.toInt() < alignedEnd; address = address.add(4)) { address.putInt(0); } } + + public static void moveMemoryBlock(Address source, Address target, int count) { + if (count < 8) { + slowMemoryMove(source, target, count); + } + int diff = source.toInt() - target.toInt(); + if (diff == 0) { + return; + } + if ((diff & 3) != 0) { + slowMemoryMove(source, target, count); + return; + } + + Address alignedSourceStart = Address.fromInt(source.toInt() >>> 2 << 2); + Address alignedTargetStart = Address.fromInt(target.toInt() >>> 2 << 2); + + Address alignedSourceEnd = Address.fromInt((source.toInt() + count) >>> 2 << 2); + Address alignedTargetEnd = Address.fromInt((target.toInt() + count) >>> 2 << 2); + + if (source.toInt() > target.toInt()) { + switch (source.toInt() - alignedSourceStart.toInt()) { + case 0: + alignedTargetStart.putInt(alignedSourceStart.getInt()); + break; + case 1: + alignedTargetStart.add(3).putByte(alignedSourceStart.add(3).getByte()); + break; + case 2: + alignedTargetStart.add(2).putShort(alignedSourceStart.add(2).getShort()); + break; + case 3: + alignedTargetStart.add(1).putByte(alignedSourceStart.add(1).getByte()); + alignedTargetStart.add(2).putShort(alignedSourceStart.add(2).getShort()); + break; + } + + alignedSourceStart = alignedSourceStart.add(4); + alignedTargetStart = alignedTargetStart.add(4); + + while (alignedSourceStart.toInt() < alignedSourceEnd.toInt()) { + alignedTargetStart.putInt(alignedSourceStart.getInt()); + alignedSourceStart = alignedSourceStart.add(4); + alignedTargetStart = alignedTargetStart.add(4); + } + + switch (source.getInt() + count - alignedSourceEnd.getInt()) { + case 0: + break; + case 1: + alignedTargetEnd.putByte(alignedSourceEnd.getByte()); + break; + case 2: + alignedTargetEnd.putShort(alignedSourceEnd.getShort()); + break; + case 3: + alignedTargetEnd.putShort(alignedSourceEnd.getShort()); + alignedTargetEnd.add(2).putByte(alignedSourceEnd.add(2).getByte()); + break; + } + } else { + switch (source.getInt() + count - alignedSourceEnd.getInt()) { + case 0: + break; + case 1: + alignedTargetEnd.putByte(alignedSourceEnd.getByte()); + break; + case 2: + alignedTargetEnd.putShort(alignedSourceEnd.getShort()); + break; + case 3: + alignedTargetEnd.add(2).putByte(alignedSourceEnd.add(2).getByte()); + alignedTargetEnd.putShort(alignedSourceEnd.getShort()); + break; + } + + while (alignedSourceEnd.toInt() > alignedTargetEnd.toInt()) { + alignedTargetEnd.putInt(alignedSourceEnd.getInt()); + alignedSourceEnd = alignedSourceEnd.add(-4); + alignedTargetEnd = alignedTargetEnd.add(-4); + } + + switch (source.toInt() - alignedSourceStart.toInt()) { + case 0: + alignedTargetStart.putInt(alignedSourceStart.getInt()); + break; + case 1: + alignedTargetStart.add(3).putByte(alignedSourceStart.add(3).getByte()); + break; + case 2: + alignedTargetStart.add(2).putShort(alignedSourceStart.add(2).getShort()); + break; + case 3: + alignedTargetStart.add(2).putShort(alignedSourceStart.add(2).getShort()); + alignedTargetStart.add(1).putByte(alignedSourceStart.add(1).getByte()); + break; + } + } + } + + private static void slowMemoryMove(Address source, Address target, int count) { + if (source.toInt() > target.toInt()) { + while (count-- > 0) { + target.putByte(source.getByte()); + target = target.add(1); + source = source.add(1); + } + } else { + source.add(count); + target.add(count); + while (count-- > 0) { + target = target.add(-1); + source = source.add(-1); + target.putByte(source.getByte()); + } + } + } } 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 8d44f0c3e..73d23ced0 100644 --- a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java +++ b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java @@ -33,13 +33,13 @@ import org.teavm.backend.wasm.generate.WasmGenerationContext; import org.teavm.backend.wasm.generate.WasmGenerator; import org.teavm.backend.wasm.generate.WasmMangling; import org.teavm.backend.wasm.generate.WasmStringPool; +import org.teavm.backend.wasm.intrinsics.AddressIntrinsic; import org.teavm.backend.wasm.intrinsics.AllocatorIntrinsic; import org.teavm.backend.wasm.intrinsics.ClassIntrinsic; import org.teavm.backend.wasm.intrinsics.PlatformClassIntrinsic; import org.teavm.backend.wasm.intrinsics.PlatformClassMetadataIntrinsic; import org.teavm.backend.wasm.intrinsics.PlatformIntrinsic; import org.teavm.backend.wasm.intrinsics.PlatformObjectIntrinsic; -import org.teavm.backend.wasm.intrinsics.WasmAddressIntrinsic; import org.teavm.backend.wasm.intrinsics.WasmRuntimeIntrinsic; import org.teavm.backend.wasm.intrinsics.WasmStructureIntrinsic; import org.teavm.backend.wasm.model.WasmFunction; @@ -196,7 +196,7 @@ public class WasmTarget implements TeaVMTarget { WasmGenerationContext context = new WasmGenerationContext(classes, controller.getDiagnostics(), vtableProvider, tagRegistry, stringPool); - context.addIntrinsic(new WasmAddressIntrinsic()); + context.addIntrinsic(new AddressIntrinsic(classGenerator)); context.addIntrinsic(new WasmStructureIntrinsic(classGenerator)); context.addIntrinsic(new WasmRuntimeIntrinsic()); context.addIntrinsic(new AllocatorIntrinsic()); @@ -293,7 +293,7 @@ public class WasmTarget implements TeaVMTarget { if (clinit == null) { continue; } - initFunction.getBody().add(new WasmCall(WasmMangling.mangleMethod(clinit.getReference()))); + initFunction.getBody().add(new WasmCall(WasmMangling.mangleInitializer(className))); } module.add(initFunction); module.setStartFunction(initFunction); 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 ab820937b..bea2780c6 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 @@ -202,6 +202,13 @@ public class WasmClassGenerator { return data.size; } + public int getClassAlignment(String className) { + ValueType type = ValueType.object(className); + addClass(type); + ClassBinaryData data = binaryDataMap.get(type); + return data.alignment; + } + public boolean isStructure(String className) { ValueType type = ValueType.object(className); addClass(type); @@ -217,11 +224,13 @@ public class WasmClassGenerator { addClass(ValueType.object(cls.getParent())); ClassBinaryData parentData = binaryDataMap.get(ValueType.object(cls.getParent())); data.size = parentData.size; + data.alignment = parentData.alignment; if (parentData.start == -1) { data.start = -1; } } else { data.size = 4; + data.alignment = 4; } for (FieldReader field : cls.getFields()) { @@ -234,6 +243,9 @@ public class WasmClassGenerator { data.fieldLayout.put(field.getName(), offset); data.size = offset + desiredAlignment; } + if (data.alignment == 0) { + data.alignment = desiredAlignment; + } } } @@ -259,7 +271,7 @@ public class WasmClassGenerator { return DataPrimitives.ADDRESS; } - private static int align(int base, int alignment) { + public static int align(int base, int alignment) { if (base == 0) { return 0; } @@ -312,6 +324,7 @@ public class WasmClassGenerator { private class ClassBinaryData { ValueType type; int size; + int alignment; int start; ObjectIntMap fieldLayout = new ObjectIntOpenHashMap<>(); DataValue data; diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerator.java index ee2933749..24ea059fa 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerator.java @@ -83,9 +83,8 @@ public class WasmGenerator { new WasmInt32Constant(RuntimeClass.INITIALIZED)); WasmConditional conditional = new WasmConditional(initFlag); - MethodReference clinit = new MethodReference(method.getOwnerName(), - "", ValueType.VOID); - conditional.getThenBlock().getBody().add(new WasmCall(WasmMangling.mangleMethod(clinit))); + conditional.getThenBlock().getBody().add(new WasmCall( + WasmMangling.mangleInitializer(method.getOwnerName()))); function.getBody().add(conditional); } diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/WasmAddressIntrinsic.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/AddressIntrinsic.java similarity index 72% rename from core/src/main/java/org/teavm/backend/wasm/intrinsics/WasmAddressIntrinsic.java rename to core/src/main/java/org/teavm/backend/wasm/intrinsics/AddressIntrinsic.java index 88229dc12..c528da0b6 100644 --- a/core/src/main/java/org/teavm/backend/wasm/intrinsics/WasmAddressIntrinsic.java +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/AddressIntrinsic.java @@ -15,10 +15,17 @@ */ package org.teavm.backend.wasm.intrinsics; +import java.util.stream.Collectors; +import org.teavm.ast.ConstantExpr; import org.teavm.ast.InvocationExpr; +import org.teavm.backend.wasm.WasmRuntime; +import org.teavm.backend.wasm.generate.WasmClassGenerator; +import org.teavm.backend.wasm.generate.WasmMangling; import org.teavm.backend.wasm.model.WasmType; +import org.teavm.backend.wasm.model.expression.WasmCall; import org.teavm.backend.wasm.model.expression.WasmConversion; import org.teavm.backend.wasm.model.expression.WasmExpression; +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; @@ -36,7 +43,13 @@ import org.teavm.interop.Address; import org.teavm.model.MethodReference; import org.teavm.model.ValueType; -public class WasmAddressIntrinsic implements WasmIntrinsic { +public class AddressIntrinsic implements WasmIntrinsic { + private WasmClassGenerator classGenerator; + + public AddressIntrinsic(WasmClassGenerator classGenerator) { + this.classGenerator = classGenerator; + } + @Override public boolean isApplicable(MethodReference methodReference) { return methodReference.getClassName().equals(Address.class.getName()); @@ -61,11 +74,24 @@ public class WasmAddressIntrinsic implements WasmIntrinsic { } case "add": { WasmExpression base = manager.generate(invocation.getArguments().get(0)); - WasmExpression offset = manager.generate(invocation.getArguments().get(1)); - if (invocation.getMethod().parameterType(0) == ValueType.LONG) { - offset = new WasmConversion(WasmType.INT64, WasmType.INT32, false, offset); + if (invocation.getMethod().parameterCount() == 1) { + WasmExpression offset = manager.generate(invocation.getArguments().get(1)); + if (invocation.getMethod().parameterType(0) == ValueType.LONG) { + offset = new WasmConversion(WasmType.INT64, WasmType.INT32, false, offset); + } + return new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD, base, offset); + } else { + WasmExpression offset = manager.generate(invocation.getArguments().get(2)); + Object type = ((ConstantExpr) invocation.getArguments().get(1)).getValue(); + String className = ((ValueType.Object) type).getClassName(); + int size = classGenerator.getClassSize(className); + int alignment = classGenerator.getClassAlignment(className); + size = WasmClassGenerator.align(size, alignment); + + offset = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.MUL, offset, + new WasmInt32Constant(size)); + return new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD, base, offset); } - return new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD, base, offset); } case "getByte": return new WasmLoadInt32(1, manager.generate(invocation.getArguments().get(0)), @@ -121,6 +147,17 @@ public class WasmAddressIntrinsic implements WasmIntrinsic { WasmExpression value = manager.generate(invocation.getArguments().get(1)); return new WasmStoreFloat64(8, address, value); } + case "sizeOf": + return new WasmInt32Constant(4); + case "align": { + MethodReference delegate = new MethodReference(WasmRuntime.class.getName(), + invocation.getMethod().getDescriptor()); + WasmCall call = new WasmCall(WasmMangling.mangleMethod(delegate)); + call.getArguments().addAll(invocation.getArguments().stream() + .map(arg -> manager.generate(arg)) + .collect(Collectors.toSet())); + return call; + } default: throw new IllegalArgumentException(invocation.getMethod().toString()); } diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/AllocatorIntrinsic.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/AllocatorIntrinsic.java index fde320b46..fa5ce0707 100644 --- a/core/src/main/java/org/teavm/backend/wasm/intrinsics/AllocatorIntrinsic.java +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/AllocatorIntrinsic.java @@ -21,7 +21,6 @@ import org.teavm.backend.wasm.WasmRuntime; import org.teavm.backend.wasm.generate.WasmMangling; import org.teavm.backend.wasm.model.expression.WasmCall; import org.teavm.backend.wasm.model.expression.WasmExpression; -import org.teavm.interop.Address; import org.teavm.model.MethodReference; import org.teavm.runtime.Allocator; @@ -35,9 +34,11 @@ public class AllocatorIntrinsic implements WasmIntrinsic { @Override public WasmExpression apply(InvocationExpr invocation, WasmIntrinsicManager manager) { switch (invocation.getMethod().getName()) { - case "fillZero": { - WasmCall call = new WasmCall(WasmMangling.mangleMethod(new MethodReference(WasmRuntime.class, - "fillZero", Address.class, int.class, void.class))); + case "fillZero": + case "moveMemoryBlock": { + MethodReference delegateMetod = new MethodReference(WasmRuntime.class.getName(), + invocation.getMethod().getDescriptor()); + WasmCall call = new WasmCall(WasmMangling.mangleMethod(delegateMetod)); call.getArguments().addAll(invocation.getArguments().stream().map(manager::generate) .collect(Collectors.toList())); return call; diff --git a/core/src/main/java/org/teavm/runtime/Allocator.java b/core/src/main/java/org/teavm/runtime/Allocator.java index 27fd00dc3..e5578ae36 100644 --- a/core/src/main/java/org/teavm/runtime/Allocator.java +++ b/core/src/main/java/org/teavm/runtime/Allocator.java @@ -51,4 +51,6 @@ public final class Allocator { } public static native void fillZero(Address address, int count); + + public static native void moveMemoryBlock(Address source, Address target, int count); } diff --git a/interop/core/src/main/java/org/teavm/interop/Address.java b/interop/core/src/main/java/org/teavm/interop/Address.java index 3fcfc84b9..dd4875ec2 100644 --- a/interop/core/src/main/java/org/teavm/interop/Address.java +++ b/interop/core/src/main/java/org/teavm/interop/Address.java @@ -59,4 +59,10 @@ public final class Address { public static native Address fromLong(long value); public static native Address ofObject(Object obj); + + public static native Address align(Address address, int alignment); + + public static native int sizeOf(); + + public native Address add(Class type, int offset); } diff --git a/interop/core/src/main/java/org/teavm/interop/Structure.java b/interop/core/src/main/java/org/teavm/interop/Structure.java index 162c55b15..5ec2b71bb 100644 --- a/interop/core/src/main/java/org/teavm/interop/Structure.java +++ b/interop/core/src/main/java/org/teavm/interop/Structure.java @@ -21,6 +21,4 @@ public class Structure { public final native Address toAddress(); public static native int sizeOf(Class type); - - public static native T add(T base, int offset); } diff --git a/platform/src/main/java/org/teavm/platform/Platform.java b/platform/src/main/java/org/teavm/platform/Platform.java index dc25502b5..8e0c314b3 100644 --- a/platform/src/main/java/org/teavm/platform/Platform.java +++ b/platform/src/main/java/org/teavm/platform/Platform.java @@ -26,10 +26,6 @@ import org.teavm.platform.metadata.ClassResource; import org.teavm.platform.metadata.StaticFieldResource; import org.teavm.platform.plugin.PlatformGenerator; -/** - * - * @author Alexey Andreev - */ public final class Platform { private Platform() { }