Implementing System.arraycopy

This commit is contained in:
Alexey Andreev 2016-08-27 18:22:49 +03:00
parent cc9af93021
commit ca3258417b
12 changed files with 236 additions and 28 deletions

View File

@ -21,11 +21,13 @@ import org.teavm.classlib.java.io.TPrintStream;
import org.teavm.classlib.java.lang.reflect.TArray; import org.teavm.classlib.java.lang.reflect.TArray;
import org.teavm.dependency.PluggableDependency; import org.teavm.dependency.PluggableDependency;
import org.teavm.backend.javascript.spi.GeneratedBy; 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 final class TSystem extends TObject {
public static final TPrintStream out = new TPrintStream(new TConsoleOutputStreamStdout(), false); public static final TPrintStream out = new TPrintStream(new TConsoleOutputStreamStdout(), false);
public static final TPrintStream err = new TPrintStream(new TConsoleOutputStreamStderr(), false); public static final TPrintStream err = new TPrintStream(new TConsoleOutputStreamStderr(), false);
@ -74,8 +76,25 @@ public final class TSystem extends TObject {
} }
@GeneratedBy(SystemNativeGenerator.class) @GeneratedBy(SystemNativeGenerator.class)
@DelegateTo("doArrayCopyLowLevel")
private static native void doArrayCopy(Object src, int srcPos, Object dest, int destPos, int length); 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) @GeneratedBy(SystemNativeGenerator.class)
public static native long currentTimeMillis(); public static native long currentTimeMillis();

View File

@ -32,6 +32,7 @@ public final class Example {
testLazyInitialization(); testLazyInitialization();
testHashCode(); testHashCode();
testArrayList(); testArrayList();
//testArrayCopy();
} }
private static void testFibonacci() { 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) { private static Base instance(int index) {
switch (index) { switch (index) {
case 0: case 0:

View File

@ -166,7 +166,7 @@ public final class WasmRuntime {
return (a << 56) | (b << 48) | (c << 40) | (d << 32) | (e << 24) | (f << 16) | (g << 8) | h; 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(); int value = address.toInt();
if (value == 0) { if (value == 0) {
return address; return address;
@ -232,8 +232,125 @@ public final class WasmRuntime {
break; 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); 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());
}
}
}
} }

View File

@ -33,13 +33,13 @@ import org.teavm.backend.wasm.generate.WasmGenerationContext;
import org.teavm.backend.wasm.generate.WasmGenerator; import org.teavm.backend.wasm.generate.WasmGenerator;
import org.teavm.backend.wasm.generate.WasmMangling; import org.teavm.backend.wasm.generate.WasmMangling;
import org.teavm.backend.wasm.generate.WasmStringPool; 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.AllocatorIntrinsic;
import org.teavm.backend.wasm.intrinsics.ClassIntrinsic; import org.teavm.backend.wasm.intrinsics.ClassIntrinsic;
import org.teavm.backend.wasm.intrinsics.PlatformClassIntrinsic; import org.teavm.backend.wasm.intrinsics.PlatformClassIntrinsic;
import org.teavm.backend.wasm.intrinsics.PlatformClassMetadataIntrinsic; import org.teavm.backend.wasm.intrinsics.PlatformClassMetadataIntrinsic;
import org.teavm.backend.wasm.intrinsics.PlatformIntrinsic; import org.teavm.backend.wasm.intrinsics.PlatformIntrinsic;
import org.teavm.backend.wasm.intrinsics.PlatformObjectIntrinsic; 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.WasmRuntimeIntrinsic;
import org.teavm.backend.wasm.intrinsics.WasmStructureIntrinsic; import org.teavm.backend.wasm.intrinsics.WasmStructureIntrinsic;
import org.teavm.backend.wasm.model.WasmFunction; import org.teavm.backend.wasm.model.WasmFunction;
@ -196,7 +196,7 @@ public class WasmTarget implements TeaVMTarget {
WasmGenerationContext context = new WasmGenerationContext(classes, controller.getDiagnostics(), WasmGenerationContext context = new WasmGenerationContext(classes, controller.getDiagnostics(),
vtableProvider, tagRegistry, stringPool); vtableProvider, tagRegistry, stringPool);
context.addIntrinsic(new WasmAddressIntrinsic()); context.addIntrinsic(new AddressIntrinsic(classGenerator));
context.addIntrinsic(new WasmStructureIntrinsic(classGenerator)); context.addIntrinsic(new WasmStructureIntrinsic(classGenerator));
context.addIntrinsic(new WasmRuntimeIntrinsic()); context.addIntrinsic(new WasmRuntimeIntrinsic());
context.addIntrinsic(new AllocatorIntrinsic()); context.addIntrinsic(new AllocatorIntrinsic());
@ -293,7 +293,7 @@ public class WasmTarget implements TeaVMTarget {
if (clinit == null) { if (clinit == null) {
continue; continue;
} }
initFunction.getBody().add(new WasmCall(WasmMangling.mangleMethod(clinit.getReference()))); initFunction.getBody().add(new WasmCall(WasmMangling.mangleInitializer(className)));
} }
module.add(initFunction); module.add(initFunction);
module.setStartFunction(initFunction); module.setStartFunction(initFunction);

View File

@ -202,6 +202,13 @@ public class WasmClassGenerator {
return data.size; 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) { public boolean isStructure(String className) {
ValueType type = ValueType.object(className); ValueType type = ValueType.object(className);
addClass(type); addClass(type);
@ -217,11 +224,13 @@ public class WasmClassGenerator {
addClass(ValueType.object(cls.getParent())); addClass(ValueType.object(cls.getParent()));
ClassBinaryData parentData = binaryDataMap.get(ValueType.object(cls.getParent())); ClassBinaryData parentData = binaryDataMap.get(ValueType.object(cls.getParent()));
data.size = parentData.size; data.size = parentData.size;
data.alignment = parentData.alignment;
if (parentData.start == -1) { if (parentData.start == -1) {
data.start = -1; data.start = -1;
} }
} else { } else {
data.size = 4; data.size = 4;
data.alignment = 4;
} }
for (FieldReader field : cls.getFields()) { for (FieldReader field : cls.getFields()) {
@ -234,6 +243,9 @@ public class WasmClassGenerator {
data.fieldLayout.put(field.getName(), offset); data.fieldLayout.put(field.getName(), offset);
data.size = offset + desiredAlignment; data.size = offset + desiredAlignment;
} }
if (data.alignment == 0) {
data.alignment = desiredAlignment;
}
} }
} }
@ -259,7 +271,7 @@ public class WasmClassGenerator {
return DataPrimitives.ADDRESS; return DataPrimitives.ADDRESS;
} }
private static int align(int base, int alignment) { public static int align(int base, int alignment) {
if (base == 0) { if (base == 0) {
return 0; return 0;
} }
@ -312,6 +324,7 @@ public class WasmClassGenerator {
private class ClassBinaryData { private class ClassBinaryData {
ValueType type; ValueType type;
int size; int size;
int alignment;
int start; int start;
ObjectIntMap<String> fieldLayout = new ObjectIntOpenHashMap<>(); ObjectIntMap<String> fieldLayout = new ObjectIntOpenHashMap<>();
DataValue data; DataValue data;

View File

@ -83,9 +83,8 @@ public class WasmGenerator {
new WasmInt32Constant(RuntimeClass.INITIALIZED)); new WasmInt32Constant(RuntimeClass.INITIALIZED));
WasmConditional conditional = new WasmConditional(initFlag); WasmConditional conditional = new WasmConditional(initFlag);
MethodReference clinit = new MethodReference(method.getOwnerName(), conditional.getThenBlock().getBody().add(new WasmCall(
"<clinit>", ValueType.VOID); WasmMangling.mangleInitializer(method.getOwnerName())));
conditional.getThenBlock().getBody().add(new WasmCall(WasmMangling.mangleMethod(clinit)));
function.getBody().add(conditional); function.getBody().add(conditional);
} }

View File

@ -15,10 +15,17 @@
*/ */
package org.teavm.backend.wasm.intrinsics; package org.teavm.backend.wasm.intrinsics;
import java.util.stream.Collectors;
import org.teavm.ast.ConstantExpr;
import org.teavm.ast.InvocationExpr; 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.WasmType;
import org.teavm.backend.wasm.model.expression.WasmCall;
import org.teavm.backend.wasm.model.expression.WasmConversion; import org.teavm.backend.wasm.model.expression.WasmConversion;
import org.teavm.backend.wasm.model.expression.WasmExpression; 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.WasmInt32Subtype;
import org.teavm.backend.wasm.model.expression.WasmInt64Subtype; import org.teavm.backend.wasm.model.expression.WasmInt64Subtype;
import org.teavm.backend.wasm.model.expression.WasmIntBinary; 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.MethodReference;
import org.teavm.model.ValueType; 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 @Override
public boolean isApplicable(MethodReference methodReference) { public boolean isApplicable(MethodReference methodReference) {
return methodReference.getClassName().equals(Address.class.getName()); return methodReference.getClassName().equals(Address.class.getName());
@ -61,11 +74,24 @@ public class WasmAddressIntrinsic implements WasmIntrinsic {
} }
case "add": { case "add": {
WasmExpression base = manager.generate(invocation.getArguments().get(0)); WasmExpression base = manager.generate(invocation.getArguments().get(0));
WasmExpression offset = manager.generate(invocation.getArguments().get(1)); if (invocation.getMethod().parameterCount() == 1) {
if (invocation.getMethod().parameterType(0) == ValueType.LONG) { WasmExpression offset = manager.generate(invocation.getArguments().get(1));
offset = new WasmConversion(WasmType.INT64, WasmType.INT32, false, offset); 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": case "getByte":
return new WasmLoadInt32(1, manager.generate(invocation.getArguments().get(0)), 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)); WasmExpression value = manager.generate(invocation.getArguments().get(1));
return new WasmStoreFloat64(8, address, value); 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: default:
throw new IllegalArgumentException(invocation.getMethod().toString()); throw new IllegalArgumentException(invocation.getMethod().toString());
} }

View File

@ -21,7 +21,6 @@ import org.teavm.backend.wasm.WasmRuntime;
import org.teavm.backend.wasm.generate.WasmMangling; import org.teavm.backend.wasm.generate.WasmMangling;
import org.teavm.backend.wasm.model.expression.WasmCall; import org.teavm.backend.wasm.model.expression.WasmCall;
import org.teavm.backend.wasm.model.expression.WasmExpression; import org.teavm.backend.wasm.model.expression.WasmExpression;
import org.teavm.interop.Address;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
import org.teavm.runtime.Allocator; import org.teavm.runtime.Allocator;
@ -35,9 +34,11 @@ public class AllocatorIntrinsic implements WasmIntrinsic {
@Override @Override
public WasmExpression apply(InvocationExpr invocation, WasmIntrinsicManager manager) { public WasmExpression apply(InvocationExpr invocation, WasmIntrinsicManager manager) {
switch (invocation.getMethod().getName()) { switch (invocation.getMethod().getName()) {
case "fillZero": { case "fillZero":
WasmCall call = new WasmCall(WasmMangling.mangleMethod(new MethodReference(WasmRuntime.class, case "moveMemoryBlock": {
"fillZero", Address.class, int.class, void.class))); 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) call.getArguments().addAll(invocation.getArguments().stream().map(manager::generate)
.collect(Collectors.toList())); .collect(Collectors.toList()));
return call; return call;

View File

@ -51,4 +51,6 @@ public final class Allocator {
} }
public static native void fillZero(Address address, int count); public static native void fillZero(Address address, int count);
public static native void moveMemoryBlock(Address source, Address target, int count);
} }

View File

@ -59,4 +59,10 @@ public final class Address {
public static native Address fromLong(long value); public static native Address fromLong(long value);
public static native Address ofObject(Object obj); 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<? extends Structure> type, int offset);
} }

View File

@ -21,6 +21,4 @@ public class Structure {
public final native Address toAddress(); public final native Address toAddress();
public static native int sizeOf(Class<? extends Structure> type); public static native int sizeOf(Class<? extends Structure> type);
public static native <T extends Structure> T add(T base, int offset);
} }

View File

@ -26,10 +26,6 @@ import org.teavm.platform.metadata.ClassResource;
import org.teavm.platform.metadata.StaticFieldResource; import org.teavm.platform.metadata.StaticFieldResource;
import org.teavm.platform.plugin.PlatformGenerator; import org.teavm.platform.plugin.PlatformGenerator;
/**
*
* @author Alexey Andreev
*/
public final class Platform { public final class Platform {
private Platform() { private Platform() {
} }