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.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();

View File

@ -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:

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;
}
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());
}
}
}
}

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.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);

View File

@ -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<String> fieldLayout = new ObjectIntOpenHashMap<>();
DataValue data;

View File

@ -83,9 +83,8 @@ public class WasmGenerator {
new WasmInt32Constant(RuntimeClass.INITIALIZED));
WasmConditional conditional = new WasmConditional(initFlag);
MethodReference clinit = new MethodReference(method.getOwnerName(),
"<clinit>", 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);
}

View File

@ -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());
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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<? extends Structure> type, int offset);
}

View File

@ -21,6 +21,4 @@ public class Structure {
public final native Address toAddress();
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.plugin.PlatformGenerator;
/**
*
* @author Alexey Andreev
*/
public final class Platform {
private Platform() {
}