wasm gc: general implementation of System.arraycopy

This commit is contained in:
Alexey Andreev 2024-09-25 20:52:02 +02:00
parent dd24425de0
commit b9f406dcaa
13 changed files with 353 additions and 28 deletions

View File

@ -870,6 +870,15 @@ public class DisassemblyCodeListener extends BaseDisassemblyListener implements
writer.eol(); writer.eol();
} }
@Override
public void arrayCopy(int targetTypeIndex, int sourceTypeIndex) {
writer.address().write("array.copy ");
writeTypeRef(targetTypeIndex);
writer.write(" ");
writeTypeRef(sourceTypeIndex);
writer.eol();
}
@Override @Override
public void functionReference(int functionIndex) { public void functionReference(int functionIndex) {
writer.address().write("ref.func "); writer.address().write("ref.func ");

View File

@ -0,0 +1,37 @@
/*
* Copyright 2024 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.gc;
import org.teavm.dependency.AbstractDependencyListener;
import org.teavm.dependency.DependencyAgent;
import org.teavm.dependency.MethodDependency;
import org.teavm.model.MethodReference;
public class SystemArrayCopyDependencySupport extends AbstractDependencyListener {
private static final MethodReference COPY_METHOD = new MethodReference(System.class,
"arraycopy", Object.class, int.class, Object.class, int.class, int.class, void.class);
@Override
public void methodReached(DependencyAgent agent, MethodDependency method) {
if (method.getMethod().getReference().equals(COPY_METHOD)) {
var implMethod = agent.linkMethod(new MethodReference(System.class,
"arrayCopyImpl", Object.class, int.class, Object.class, int.class, int.class, void.class));
method.getVariable(1).connect(implMethod.getVariable(1));
method.getVariable(3).connect(implMethod.getVariable(3));
implMethod.use();
}
}
}

View File

@ -38,6 +38,7 @@ public class WasmGCDependencies {
contributeString(); contributeString();
analyzer.addDependencyListener(new WasmGCReferenceQueueDependency()); analyzer.addDependencyListener(new WasmGCReferenceQueueDependency());
analyzer.addDependencyListener(new WasmGCResourceDependency()); analyzer.addDependencyListener(new WasmGCResourceDependency());
analyzer.addDependencyListener(new SystemArrayCopyDependencySupport());
} }
public void contributeStandardExports() { public void contributeStandardExports() {

View File

@ -145,13 +145,16 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
private int enumConstantsFunctionOffset; private int enumConstantsFunctionOffset;
private int arrayLengthOffset = -1; private int arrayLengthOffset = -1;
private int arrayGetOffset = -1; private int arrayGetOffset = -1;
private int arrayCopyOffset = -1;
private int cloneOffset = -1; private int cloneOffset = -1;
private int servicesOffset = -1; private int servicesOffset = -1;
private WasmStructure arrayVirtualTableStruct; private WasmStructure arrayVirtualTableStruct;
private WasmFunction arrayGetObjectFunction; private WasmFunction arrayGetObjectFunction;
private WasmFunction arrayLengthObjectFunction; private WasmFunction arrayLengthObjectFunction;
private WasmFunction arrayCopyObjectFunction;
private WasmFunctionType arrayGetType; private WasmFunctionType arrayGetType;
private WasmFunctionType arrayLengthType; private WasmFunctionType arrayLengthType;
private WasmFunctionType arrayCopyType;
private List<WasmStructure> nonInitializedStructures = new ArrayList<>(); private List<WasmStructure> nonInitializedStructures = new ArrayList<>();
private WasmArray objectArrayType; private WasmArray objectArrayType;
private boolean hasLoadServices; private boolean hasLoadServices;
@ -176,7 +179,8 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
standardClasses = new WasmGCStandardClasses(this); standardClasses = new WasmGCStandardClasses(this);
strings = new WasmGCStringPool(standardClasses, module, functionProvider, names, functionTypes, strings = new WasmGCStringPool(standardClasses, module, functionProvider, names, functionTypes,
dependencyInfo); dependencyInfo);
supertypeGenerator = new WasmGCSupertypeFunctionGenerator(module, this, names, tagRegistry, functionTypes); supertypeGenerator = new WasmGCSupertypeFunctionGenerator(module, this, names, tagRegistry, functionTypes,
queue);
newArrayGenerator = new WasmGCNewArrayFunctionGenerator(module, functionTypes, this, names, queue); newArrayGenerator = new WasmGCNewArrayFunctionGenerator(module, functionTypes, this, names, queue);
typeMapper = new WasmGCTypeMapper(classSource, this, functionTypes, module); typeMapper = new WasmGCTypeMapper(classSource, this, functionTypes, module);
var customTypeMapperFactoryContext = customTypeMapperFactoryContext(); var customTypeMapperFactoryContext = customTypeMapperFactoryContext();
@ -664,6 +668,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
WasmStructure objectStructure) { WasmStructure objectStructure) {
var virtualTable = virtualTables.lookup("java.lang.Object"); var virtualTable = virtualTables.lookup("java.lang.Object");
var structure = getArrayVirtualTableStructure(); var structure = getArrayVirtualTableStructure();
structure.init();
var itemType = ((ValueType.Array) type).getItemType(); var itemType = ((ValueType.Array) type).getItemType();
for (var entry : virtualTable.getEntries()) { for (var entry : virtualTable.getEntries()) {
@ -681,9 +686,13 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
target.add(new WasmStructSet(structure, new WasmGetGlobal(global), arrayGetOffset, target.add(new WasmStructSet(structure, new WasmGetGlobal(global), arrayGetOffset,
new WasmFunctionReference(getFunction))); new WasmFunctionReference(getFunction)));
} }
if (info.arrayCopy()) {
var copyFunction = getArrayCopyFunction(itemType);
target.add(new WasmStructSet(structure, new WasmGetGlobal(global), arrayCopyOffset,
new WasmFunctionReference(copyFunction)));
}
} }
private WasmFunction getArrayLengthFunction(WasmStructure objectStructure) { private WasmFunction getArrayLengthFunction(WasmStructure objectStructure) {
var arrayTypeRef = (WasmType.CompositeReference) objectStructure.getFields().get(ARRAY_DATA_FIELD_OFFSET) var arrayTypeRef = (WasmType.CompositeReference) objectStructure.getFields().get(ARRAY_DATA_FIELD_OFFSET)
.getUnpackedType(); .getUnpackedType();
@ -815,6 +824,53 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
return function; return function;
} }
private WasmFunction getArrayCopyFunction(ValueType itemType) {
if (itemType instanceof ValueType.Primitive) {
return createArrayCopyFunction(itemType);
}
return getArrayCopyObjectFunction();
}
private WasmFunction getArrayCopyObjectFunction() {
if (arrayCopyObjectFunction == null) {
arrayCopyObjectFunction = createArrayCopyFunction(OBJECT_TYPE);
}
return arrayCopyObjectFunction;
}
private WasmFunction createArrayCopyFunction(ValueType type) {
var function = new WasmFunction(getArrayCopyType());
function.setName(names.topLevel("Array<" + names.suggestForType(type) + ">::copy"));
module.functions.add(function);
function.setReferenced(true);
var arrayStruct = getClassInfo(ValueType.arrayOf(type)).structure;
var arrayDataTypeRef = (WasmType.CompositeReference) arrayStruct.getFields()
.get(ARRAY_DATA_FIELD_OFFSET).getUnpackedType();
var arrayDataType = (WasmArray) arrayDataTypeRef.composite;
var sourceLocal = new WasmLocal(standardClasses.objectClass().getType(), "source");
var sourceIndexLocal = new WasmLocal(WasmType.INT32, "sourceIndex");
var targetLocal = new WasmLocal(standardClasses.objectClass().getType(), "target");
var targetIndexLocal = new WasmLocal(WasmType.INT32, "targetIndex");
var countLocal = new WasmLocal(WasmType.INT32, "count");
function.add(sourceLocal);
function.add(sourceIndexLocal);
function.add(targetLocal);
function.add(targetIndexLocal);
function.add(countLocal);
var sourceArray = new WasmCast(new WasmGetLocal(sourceLocal), arrayStruct.getNonNullReference());
var sourceArrayData = new WasmStructGet(arrayStruct, sourceArray, ARRAY_DATA_FIELD_OFFSET);
var targetArray = new WasmCast(new WasmGetLocal(targetLocal), arrayStruct.getNonNullReference());
var targetArrayData = new WasmStructGet(arrayStruct, targetArray, ARRAY_DATA_FIELD_OFFSET);
function.getBody().add(new WasmArrayCopy(
arrayDataType, targetArrayData, new WasmGetLocal(sourceIndexLocal),
arrayDataType, sourceArrayData, new WasmGetLocal(targetIndexLocal),
new WasmGetLocal(countLocal)));
return function;
}
private WasmFunctionType getArrayGetType() { private WasmFunctionType getArrayGetType() {
if (arrayGetType == null) { if (arrayGetType == null) {
arrayGetType = functionTypes.of(standardClasses.objectClass().getType(), arrayGetType = functionTypes.of(standardClasses.objectClass().getType(),
@ -830,6 +886,14 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
return arrayLengthType; return arrayLengthType;
} }
private WasmFunctionType getArrayCopyType() {
if (arrayCopyType == null) {
arrayCopyType = functionTypes.of(null, standardClasses.objectClass().getType(), WasmType.INT32,
standardClasses.objectClass().getType(), WasmType.INT32, WasmType.INT32);
}
return arrayCopyType;
}
private void fillVirtualTableEntry(List<WasmExpression> target, WasmGlobal global, private void fillVirtualTableEntry(List<WasmExpression> target, WasmGlobal global,
WasmStructure structure, WasmGCVirtualTable virtualTable, WasmGCVirtualTableEntry entry) { WasmStructure structure, WasmGCVirtualTable virtualTable, WasmGCVirtualTableEntry entry) {
var implementor = virtualTable.implementor(entry); var implementor = virtualTable.implementor(entry);
@ -966,6 +1030,12 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
fields.add(new WasmField(arrayGetType.getReference().asStorage(), fields.add(new WasmField(arrayGetType.getReference().asStorage(),
names.structureField("@arrayGet"))); names.structureField("@arrayGet")));
} }
if (metadataRequirements.hasArrayCopy()) {
arrayCopyOffset = fields.size();
var arrayCopyType = getArrayCopyType();
fields.add(new WasmField(arrayCopyType.getReference().asStorage(),
names.structureField("@arrayCopy")));
}
}); });
arrayVirtualTableStruct.setSupertype(standardClasses.objectClass().getVirtualTableStructure()); arrayVirtualTableStruct.setSupertype(standardClasses.objectClass().getVirtualTableStructure());
module.types.add(arrayVirtualTableStruct); module.types.add(arrayVirtualTableStruct);
@ -986,6 +1056,12 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
return arrayGetOffset; return arrayGetOffset;
} }
@Override
public int getArrayCopyOffset() {
initStructures();
return arrayCopyOffset;
}
@Override @Override
public int getEnumConstantsFunctionOffset() { public int getEnumConstantsFunctionOffset() {
return enumConstantsFunctionOffset; return enumConstantsFunctionOffset;

View File

@ -65,6 +65,8 @@ public interface WasmGCClassInfoProvider {
int getArrayLengthOffset(); int getArrayLengthOffset();
int getArrayCopyOffset();
int getEnumConstantsFunctionOffset(); int getEnumConstantsFunctionOffset();
int getCloneOffset(); int getCloneOffset();

View File

@ -19,6 +19,7 @@ import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Queue;
import org.teavm.backend.wasm.WasmFunctionTypes; import org.teavm.backend.wasm.WasmFunctionTypes;
import org.teavm.backend.wasm.generate.gc.WasmGCNameProvider; import org.teavm.backend.wasm.generate.gc.WasmGCNameProvider;
import org.teavm.backend.wasm.model.WasmFunction; import org.teavm.backend.wasm.model.WasmFunction;
@ -51,19 +52,22 @@ public class WasmGCSupertypeFunctionGenerator implements WasmGCSupertypeFunction
private TagRegistry tagRegistry; private TagRegistry tagRegistry;
private WasmFunctionTypes functionTypes; private WasmFunctionTypes functionTypes;
private WasmFunctionType functionType; private WasmFunctionType functionType;
private Queue<Runnable> queue;
WasmGCSupertypeFunctionGenerator( WasmGCSupertypeFunctionGenerator(
WasmModule module, WasmModule module,
WasmGCClassGenerator classGenerator, WasmGCClassGenerator classGenerator,
WasmGCNameProvider nameProvider, WasmGCNameProvider nameProvider,
TagRegistry tagRegistry, TagRegistry tagRegistry,
WasmFunctionTypes functionTypes WasmFunctionTypes functionTypes,
Queue<Runnable> queue
) { ) {
this.module = module; this.module = module;
this.classGenerator = classGenerator; this.classGenerator = classGenerator;
this.nameProvider = nameProvider; this.nameProvider = nameProvider;
this.tagRegistry = tagRegistry; this.tagRegistry = tagRegistry;
this.functionTypes = functionTypes; this.functionTypes = functionTypes;
this.queue = queue;
} }
@Override @Override
@ -83,17 +87,19 @@ public class WasmGCSupertypeFunctionGenerator implements WasmGCSupertypeFunction
function.add(subtypeVar); function.add(subtypeVar);
module.functions.add(function); module.functions.add(function);
if (type instanceof ValueType.Object) { queue.add(() -> {
var className = ((ValueType.Object) type).getClassName(); if (type instanceof ValueType.Object) {
generateIsClass(subtypeVar, className, function); var className = ((ValueType.Object) type).getClassName();
} else if (type instanceof ValueType.Array) { generateIsClass(subtypeVar, className, function);
ValueType itemType = ((ValueType.Array) type).getItemType(); } else if (type instanceof ValueType.Array) {
generateIsArray(subtypeVar, itemType, function.getBody()); ValueType itemType = ((ValueType.Array) type).getItemType();
} else { generateIsArray(subtypeVar, itemType, function.getBody());
var expected = classGenerator.getClassInfo(type).pointer; } else {
var condition = new WasmReferencesEqual(new WasmGetLocal(subtypeVar), new WasmGetGlobal(expected)); var expected = classGenerator.getClassInfo(type).pointer;
function.getBody().add(condition); var condition = new WasmReferencesEqual(new WasmGetLocal(subtypeVar), new WasmGetGlobal(expected));
} function.getBody().add(condition);
}
});
return function; return function;
} }

View File

@ -48,6 +48,7 @@ import org.teavm.backend.wasm.model.WasmFunctionType;
import org.teavm.backend.wasm.model.WasmLocal; import org.teavm.backend.wasm.model.WasmLocal;
import org.teavm.backend.wasm.model.WasmModule; import org.teavm.backend.wasm.model.WasmModule;
import org.teavm.backend.wasm.model.WasmStructure; import org.teavm.backend.wasm.model.WasmStructure;
import org.teavm.backend.wasm.model.WasmTag;
import org.teavm.backend.wasm.model.WasmType; import org.teavm.backend.wasm.model.WasmType;
import org.teavm.backend.wasm.model.expression.WasmArrayGet; import org.teavm.backend.wasm.model.expression.WasmArrayGet;
import org.teavm.backend.wasm.model.expression.WasmArrayLength; import org.teavm.backend.wasm.model.expression.WasmArrayLength;
@ -822,5 +823,10 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
public WasmGCStringProvider strings() { public WasmGCStringProvider strings() {
return context.strings(); return context.strings();
} }
@Override
public WasmTag exceptionTag() {
return context.getExceptionTag();
}
}; };
} }

View File

@ -19,20 +19,47 @@ import org.teavm.ast.InvocationExpr;
import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassInfoProvider; import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassInfoProvider;
import org.teavm.backend.wasm.model.WasmArray; import org.teavm.backend.wasm.model.WasmArray;
import org.teavm.backend.wasm.model.WasmFunction; import org.teavm.backend.wasm.model.WasmFunction;
import org.teavm.backend.wasm.model.WasmFunctionType;
import org.teavm.backend.wasm.model.WasmLocal;
import org.teavm.backend.wasm.model.WasmStructure; import org.teavm.backend.wasm.model.WasmStructure;
import org.teavm.backend.wasm.model.WasmType; import org.teavm.backend.wasm.model.WasmType;
import org.teavm.backend.wasm.model.expression.WasmArrayCopy; import org.teavm.backend.wasm.model.expression.WasmArrayCopy;
import org.teavm.backend.wasm.model.expression.WasmArrayLength;
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.WasmCall;
import org.teavm.backend.wasm.model.expression.WasmCallReference;
import org.teavm.backend.wasm.model.expression.WasmCast;
import org.teavm.backend.wasm.model.expression.WasmExpression; 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.WasmIntBinary;
import org.teavm.backend.wasm.model.expression.WasmIntBinaryOperation;
import org.teavm.backend.wasm.model.expression.WasmIntType;
import org.teavm.backend.wasm.model.expression.WasmReturn;
import org.teavm.backend.wasm.model.expression.WasmStructGet; import org.teavm.backend.wasm.model.expression.WasmStructGet;
import org.teavm.backend.wasm.model.expression.WasmThrow;
import org.teavm.backend.wasm.runtime.gc.WasmGCSupport;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
import org.teavm.model.ValueType; import org.teavm.model.ValueType;
public class SystemArrayCopyIntrinsic implements WasmGCIntrinsic { public class SystemArrayCopyIntrinsic implements WasmGCIntrinsic {
private WasmFunction defaultFunction; private WasmFunction defaultFunction;
private WasmFunction argsCheckFunction;
@Override @Override
public WasmExpression apply(InvocationExpr invocation, WasmGCIntrinsicContext context) { public WasmExpression apply(InvocationExpr invocation, WasmGCIntrinsicContext context) {
switch (invocation.getMethod().getName()) {
case "arraycopy":
return generateArrayCopy(invocation, context);
case "doArrayCopy":
return generateDoArrayCopy(invocation, context);
default:
throw new IllegalArgumentException();
}
}
private WasmExpression generateArrayCopy(InvocationExpr invocation, WasmGCIntrinsicContext context) {
var result = tryGenerateSpecialCase(invocation, context); var result = tryGenerateSpecialCase(invocation, context);
if (result == null) { if (result == null) {
tryGenerateSpecialCase(invocation, context); tryGenerateSpecialCase(invocation, context);
@ -46,6 +73,32 @@ public class SystemArrayCopyIntrinsic implements WasmGCIntrinsic {
return result; return result;
} }
private WasmExpression generateDoArrayCopy(InvocationExpr invocation, WasmGCIntrinsicContext context) {
var objStruct = context.classInfoProvider().getClassInfo(Object.class.getName()).getStructure();
var arrayClsStruct = context.classInfoProvider().getArrayVirtualTableStructure();
var block = new WasmBlock(false);
var source = context.exprCache().create(context.generate(invocation.getArguments().get(0)),
objStruct.getReference(), invocation.getLocation(), block.getBody());
WasmExpression sourceCls = new WasmStructGet(objStruct, source.expr(),
WasmGCClassInfoProvider.CLASS_FIELD_OFFSET);
sourceCls = new WasmCast(sourceCls, arrayClsStruct.getNonNullReference());
var copyFunction = new WasmStructGet(arrayClsStruct, sourceCls,
context.classInfoProvider().getArrayCopyOffset());
var functionTypeRef = (WasmType.CompositeReference) arrayClsStruct.getFields().get(
context.classInfoProvider().getArrayCopyOffset()).getUnpackedType();
var functionType = (WasmFunctionType) functionTypeRef.composite;
var call = new WasmCallReference(copyFunction, functionType);
call.getArguments().add(source.expr());
call.getArguments().add(context.generate(invocation.getArguments().get(1)));
call.getArguments().add(context.generate(invocation.getArguments().get(2)));
call.getArguments().add(context.generate(invocation.getArguments().get(3)));
call.getArguments().add(context.generate(invocation.getArguments().get(4)));
block.getBody().add(call);
source.release();
return block;
}
private WasmExpression tryGenerateSpecialCase(InvocationExpr invocation, WasmGCIntrinsicContext context) { private WasmExpression tryGenerateSpecialCase(InvocationExpr invocation, WasmGCIntrinsicContext context) {
var sourceArray = invocation.getArguments().get(0); var sourceArray = invocation.getArguments().get(0);
var targetArray = invocation.getArguments().get(2); var targetArray = invocation.getArguments().get(2);
@ -69,35 +122,120 @@ public class SystemArrayCopyIntrinsic implements WasmGCIntrinsic {
return null; return null;
} }
var block = new WasmBlock(false);
var wasmTargetArrayType = (WasmType.CompositeReference) context.typeMapper().mapType( var wasmTargetArrayType = (WasmType.CompositeReference) context.typeMapper().mapType(
ValueType.arrayOf(targetItemType)); ValueType.arrayOf(targetItemType));
var wasmTargetArrayStruct = (WasmStructure) wasmTargetArrayType.composite; var wasmTargetArrayStruct = (WasmStructure) wasmTargetArrayType.composite;
var wasmTargetArrayWrapper = context.generate(invocation.getArguments().get(2)); var wasmTargetArrayWrapper = context.generate(invocation.getArguments().get(2));
var wasmTargetArray = new WasmStructGet(wasmTargetArrayStruct, wasmTargetArrayWrapper, var wasmTargetArrayTypeRef = (WasmType.CompositeReference) wasmTargetArrayStruct.getFields()
WasmGCClassInfoProvider.ARRAY_DATA_FIELD_OFFSET); .get(WasmGCClassInfoProvider.ARRAY_DATA_FIELD_OFFSET).getUnpackedType();
var wasmTargetIndex = context.generate(invocation.getArguments().get(3)); var wasmTargetArray = context.exprCache().create(new WasmStructGet(wasmTargetArrayStruct,
wasmTargetArrayWrapper, WasmGCClassInfoProvider.ARRAY_DATA_FIELD_OFFSET),
wasmTargetArrayTypeRef, null, block.getBody());
var wasmTargetIndex = context.exprCache().create(context.generate(invocation.getArguments().get(3)),
WasmType.INT32, null, block.getBody());
var wasmSourceArrayType = (WasmType.CompositeReference) context.typeMapper().mapType( var wasmSourceArrayType = (WasmType.CompositeReference) context.typeMapper().mapType(
ValueType.arrayOf(sourceItemType)); ValueType.arrayOf(sourceItemType));
var wasmSourceArrayStruct = (WasmStructure) wasmSourceArrayType.composite; var wasmSourceArrayStruct = (WasmStructure) wasmSourceArrayType.composite;
var wasmSourceArrayWrapper = context.generate(invocation.getArguments().get(0)); var wasmSourceArrayWrapper = context.generate(invocation.getArguments().get(0));
var wasmSourceArray = new WasmStructGet(wasmSourceArrayStruct, wasmSourceArrayWrapper,
WasmGCClassInfoProvider.ARRAY_DATA_FIELD_OFFSET);
var wasmSourceIndex = context.generate(invocation.getArguments().get(1));
var wasmSize = context.generate(invocation.getArguments().get(4));
var wasmTargetArrayTypeRef = (WasmType.CompositeReference) wasmTargetArrayStruct.getFields()
.get(WasmGCClassInfoProvider.ARRAY_DATA_FIELD_OFFSET).getUnpackedType();
var wasmSourceArrayTypeRef = (WasmType.CompositeReference) wasmSourceArrayStruct.getFields() var wasmSourceArrayTypeRef = (WasmType.CompositeReference) wasmSourceArrayStruct.getFields()
.get(WasmGCClassInfoProvider.ARRAY_DATA_FIELD_OFFSET).getUnpackedType(); .get(WasmGCClassInfoProvider.ARRAY_DATA_FIELD_OFFSET).getUnpackedType();
var wasmSourceArray = context.exprCache().create(new WasmStructGet(wasmSourceArrayStruct,
wasmSourceArrayWrapper, WasmGCClassInfoProvider.ARRAY_DATA_FIELD_OFFSET),
wasmSourceArrayTypeRef, null, block.getBody());
var wasmSourceIndex = context.exprCache().create(context.generate(invocation.getArguments().get(1)),
WasmType.INT32, null, block.getBody());
var wasmSize = context.exprCache().create(context.generate(invocation.getArguments().get(4)),
WasmType.INT32, null, block.getBody());
return new WasmArrayCopy((WasmArray) wasmTargetArrayTypeRef.composite, wasmTargetArray, wasmTargetIndex,
(WasmArray) wasmSourceArrayTypeRef.composite, wasmSourceArray, wasmSourceIndex, wasmSize); block.getBody().add(new WasmCall(
getArgsCheckFunction(context),
wasmTargetArray.expr(), wasmTargetIndex.expr(),
wasmSourceArray.expr(), wasmSourceIndex.expr(),
wasmSize.expr()
));
block.getBody().add(new WasmArrayCopy(
(WasmArray) wasmTargetArrayTypeRef.composite, wasmTargetArray.expr(), wasmTargetIndex.expr(),
(WasmArray) wasmSourceArrayTypeRef.composite, wasmSourceArray.expr(), wasmSourceIndex.expr(),
wasmSize.expr()
));
wasmTargetArray.release();
wasmTargetIndex.release();
wasmSourceArray.release();
wasmSourceIndex.release();
wasmSize.release();
return block;
}
private WasmFunction getArgsCheckFunction(WasmGCIntrinsicContext context) {
if (argsCheckFunction == null) {
argsCheckFunction = createArgsCheckFunction(context);
}
return argsCheckFunction;
}
private WasmFunction createArgsCheckFunction(WasmGCIntrinsicContext context) {
var function = new WasmFunction(context.functionTypes().of(null,
WasmType.Reference.ARRAY, WasmType.INT32, WasmType.Reference.ARRAY,
WasmType.INT32, WasmType.INT32));
function.setName(context.names().topLevel("teavm@checkArrayCopy"));
context.module().functions.add(function);
var targetArrayLocal = new WasmLocal(WasmType.Reference.ARRAY, "targetArray");
var targetArrayIndexLocal = new WasmLocal(WasmType.INT32, "targetIndex");
var sourceArrayLocal = new WasmLocal(WasmType.Reference.ARRAY, "sourceArray");
var sourceArrayIndexLocal = new WasmLocal(WasmType.INT32, "sourceIndex");
var countLocal = new WasmLocal(WasmType.INT32, "count");
function.add(targetArrayLocal);
function.add(targetArrayIndexLocal);
function.add(sourceArrayLocal);
function.add(sourceArrayIndexLocal);
function.add(countLocal);
var block = new WasmBlock(false);
var targetIndexLessThanZero = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.LT_SIGNED,
new WasmGetLocal(targetArrayIndexLocal), new WasmInt32Constant(0));
block.getBody().add(new WasmBranch(targetIndexLessThanZero, block));
var sourceIndexLessThanZero = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.LT_SIGNED,
new WasmGetLocal(sourceArrayIndexLocal), new WasmInt32Constant(0));
block.getBody().add(new WasmBranch(sourceIndexLessThanZero, block));
var countPositive = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.LT_SIGNED,
new WasmGetLocal(countLocal), new WasmInt32Constant(0));
block.getBody().add(new WasmBranch(countPositive, block));
var targetSize = new WasmArrayLength(new WasmGetLocal(targetArrayLocal));
var targetIndexLimit = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SUB,
targetSize, new WasmGetLocal(countLocal));
var targetIndexGreaterThanSize = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.GT_SIGNED,
new WasmGetLocal(targetArrayIndexLocal), targetIndexLimit);
block.getBody().add(new WasmBranch(targetIndexGreaterThanSize, block));
var sourceSize = new WasmArrayLength(new WasmGetLocal(sourceArrayLocal));
var sourceIndexLimit = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SUB,
sourceSize, new WasmGetLocal(countLocal));
var sourceIndexGreaterThanSize = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.GT_SIGNED,
new WasmGetLocal(sourceArrayIndexLocal), sourceIndexLimit);
block.getBody().add(new WasmBranch(sourceIndexGreaterThanSize, block));
block.getBody().add(new WasmReturn());
function.getBody().add(block);
var aioobeFunction = context.functions().forStaticMethod(new MethodReference(WasmGCSupport.class, "aiiobe",
ArrayIndexOutOfBoundsException.class));
var throwExpr = new WasmThrow(context.exceptionTag());
throwExpr.getArguments().add(new WasmCall(aioobeFunction));
function.getBody().add(throwExpr);
return function;
} }
private WasmFunction getDefaultFunction(WasmGCIntrinsicContext manager) { private WasmFunction getDefaultFunction(WasmGCIntrinsicContext manager) {
if (defaultFunction == null) { if (defaultFunction == null) {
defaultFunction = manager.functions().forStaticMethod(new MethodReference(System.class, defaultFunction = manager.functions().forStaticMethod(new MethodReference(System.class,
"arraycopy", Object.class, int.class, Object.class, int.class, int.class, void.class)); "arrayCopyImpl", Object.class, int.class, Object.class, int.class, int.class, void.class));
} }
return defaultFunction; return defaultFunction;
} }

View File

@ -26,6 +26,7 @@ import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassInfoProvider;
import org.teavm.backend.wasm.generate.gc.classes.WasmGCTypeMapper; import org.teavm.backend.wasm.generate.gc.classes.WasmGCTypeMapper;
import org.teavm.backend.wasm.generate.gc.strings.WasmGCStringProvider; import org.teavm.backend.wasm.generate.gc.strings.WasmGCStringProvider;
import org.teavm.backend.wasm.model.WasmModule; import org.teavm.backend.wasm.model.WasmModule;
import org.teavm.backend.wasm.model.WasmTag;
import org.teavm.backend.wasm.model.expression.WasmExpression; import org.teavm.backend.wasm.model.expression.WasmExpression;
import org.teavm.model.ClassHierarchy; import org.teavm.model.ClassHierarchy;
@ -55,4 +56,6 @@ public interface WasmGCIntrinsicContext {
WasmGCStringProvider strings(); WasmGCStringProvider strings();
ClassLoader classLoader(); ClassLoader classLoader();
WasmTag exceptionTag();
} }

View File

@ -108,8 +108,11 @@ public class WasmGCIntrinsics implements WasmGCIntrinsicProvider {
} }
private void fillSystem() { private void fillSystem() {
var arrayCopyIntrinsic = new SystemArrayCopyIntrinsic();
add(new MethodReference(System.class, "arraycopy", Object.class, int.class, Object.class, add(new MethodReference(System.class, "arraycopy", Object.class, int.class, Object.class,
int.class, int.class, void.class), new SystemArrayCopyIntrinsic()); int.class, int.class, void.class), arrayCopyIntrinsic);
add(new MethodReference(System.class, "doArrayCopy", Object.class, int.class, Object.class,
int.class, int.class, void.class), arrayCopyIntrinsic);
add(new MethodReference(System.class, "currentTimeMillis", long.class), new SystemIntrinsic()); add(new MethodReference(System.class, "currentTimeMillis", long.class), new SystemIntrinsic());
} }

View File

@ -25,6 +25,7 @@ public class WasmStructSet extends WasmExpression {
private WasmExpression value; private WasmExpression value;
public WasmStructSet(WasmStructure type, WasmExpression instance, int fieldIndex, WasmExpression value) { public WasmStructSet(WasmStructure type, WasmExpression instance, int fieldIndex, WasmExpression value) {
checkFieldIndex(fieldIndex);
this.type = Objects.requireNonNull(type); this.type = Objects.requireNonNull(type);
this.instance = Objects.requireNonNull(instance); this.instance = Objects.requireNonNull(instance);
this.fieldIndex = fieldIndex; this.fieldIndex = fieldIndex;
@ -52,6 +53,7 @@ public class WasmStructSet extends WasmExpression {
} }
public void setFieldIndex(int fieldIndex) { public void setFieldIndex(int fieldIndex) {
checkFieldIndex(fieldIndex);
this.fieldIndex = fieldIndex; this.fieldIndex = fieldIndex;
} }
@ -67,4 +69,11 @@ public class WasmStructSet extends WasmExpression {
public void acceptVisitor(WasmExpressionVisitor visitor) { public void acceptVisitor(WasmExpressionVisitor visitor) {
visitor.visit(this); visitor.visit(this);
} }
private static void checkFieldIndex(int fieldIndex) {
if (fieldIndex < 0) {
throw new IllegalArgumentException("Field index must be >= 0");
}
}
} }

View File

@ -83,11 +83,15 @@ public class BaseClassesTransformation implements ClassHolderTransformer {
} }
} }
} else if (cls.getName().equals("java.lang.System")) { } else if (cls.getName().equals("java.lang.System")) {
Program arrayCopyProgram = null;
MethodDescriptor arrayCopyDescriptor = null;
for (var method : cls.getMethods()) { for (var method : cls.getMethods()) {
switch (method.getName()) { switch (method.getName()) {
case "arraycopy": case "arraycopy":
arrayCopyProgram = method.getProgram();
method.setProgram(null); method.setProgram(null);
method.getModifiers().add(ElementModifier.NATIVE); method.getModifiers().add(ElementModifier.NATIVE);
arrayCopyDescriptor = method.getDescriptor();
break; break;
case "currentTimeMillis": { case "currentTimeMillis": {
var annotation = new AnnotationHolder(Import.class.getName()); var annotation = new AnnotationHolder(Import.class.getName());
@ -97,6 +101,13 @@ public class BaseClassesTransformation implements ClassHolderTransformer {
} }
} }
} }
if (arrayCopyProgram != null) {
var arrayCopyImpl = new MethodHolder(new MethodDescriptor("arrayCopyImpl",
arrayCopyDescriptor.getSignature()));
arrayCopyImpl.setProgram(arrayCopyProgram);
arrayCopyImpl.getModifiers().add(ElementModifier.STATIC);
cls.addMethod(arrayCopyImpl);
}
} else if (cls.getName().equals("java.lang.ref.WeakReference")) { } else if (cls.getName().equals("java.lang.ref.WeakReference")) {
var constructor = cls.getMethod(new MethodDescriptor("<init>", Object.class, ReferenceQueue.class, var constructor = cls.getMethod(new MethodDescriptor("<init>", Object.class, ReferenceQueue.class,
void.class)); void.class));

View File

@ -42,10 +42,13 @@ public class ClassMetadataRequirements {
"get", Object.class, int.class, Object.class); "get", Object.class, int.class, Object.class);
private static final MethodReference ARRAY_LENGTH = new MethodReference(Array.class, private static final MethodReference ARRAY_LENGTH = new MethodReference(Array.class,
"getLength", Object.class, int.class); "getLength", Object.class, int.class);
private static final MethodReference ARRAY_COPY = new MethodReference(System.class,
"arraycopy", Object.class, int.class, Object.class, int.class, int.class, void.class);
private static final ClassInfo EMPTY_INFO = new ClassInfo(); private static final ClassInfo EMPTY_INFO = new ClassInfo();
private Map<ValueType, ClassInfo> requirements = new HashMap<>(); private Map<ValueType, ClassInfo> requirements = new HashMap<>();
private boolean hasArrayGet; private boolean hasArrayGet;
private boolean hasArrayLength; private boolean hasArrayLength;
private boolean hasArrayCopy;
private boolean hasEnumConstants; private boolean hasEnumConstants;
private boolean hasSuperclass; private boolean hasSuperclass;
private boolean hasIsAssignable; private boolean hasIsAssignable;
@ -137,6 +140,15 @@ public class ClassMetadataRequirements {
} }
} }
var arrayCopy = dependencyInfo.getMethod(ARRAY_COPY);
if (arrayCopy != null) {
hasArrayCopy = arrayCopy.isUsed();
var classNames = arrayCopy.getVariable(1).getTypes();
for (var className : classNames) {
requirements.computeIfAbsent(decodeType(className), k -> new ClassInfo()).arrayCopy = true;
}
}
var clone = dependencyInfo.getMethod(new MethodReference(Object.class, "cloneObject", Object.class)); var clone = dependencyInfo.getMethod(new MethodReference(Object.class, "cloneObject", Object.class));
if (clone != null) { if (clone != null) {
var classNames = clone.getVariable(0).getTypes(); var classNames = clone.getVariable(0).getTypes();
@ -182,6 +194,10 @@ public class ClassMetadataRequirements {
return hasArrayLength; return hasArrayLength;
} }
public boolean hasArrayCopy() {
return hasArrayCopy;
}
public boolean hasEnumConstants() { public boolean hasEnumConstants() {
return hasEnumConstants; return hasEnumConstants;
} }
@ -240,6 +256,7 @@ public class ClassMetadataRequirements {
boolean newArray; boolean newArray;
boolean arrayLength; boolean arrayLength;
boolean arrayGet; boolean arrayGet;
boolean arrayCopy;
boolean cloneMethod; boolean cloneMethod;
boolean enumConstants; boolean enumConstants;
@ -283,6 +300,11 @@ public class ClassMetadataRequirements {
return arrayLength; return arrayLength;
} }
@Override
public boolean arrayCopy() {
return arrayCopy;
}
@Override @Override
public boolean arrayGet() { public boolean arrayGet() {
return arrayGet; return arrayGet;
@ -318,6 +340,8 @@ public class ClassMetadataRequirements {
boolean arrayGet(); boolean arrayGet();
boolean arrayCopy();
boolean cloneMethod(); boolean cloneMethod();
boolean enumConstants(); boolean enumConstants();