wasm gc: support Array.newInstance

This commit is contained in:
Alexey Andreev 2024-08-17 20:48:03 +02:00
parent 40fbce0ddd
commit 199032d48a
11 changed files with 205 additions and 9 deletions

View File

@ -16,6 +16,7 @@
package org.teavm.classlib.java.lang.reflect; package org.teavm.classlib.java.lang.reflect;
import org.teavm.backend.javascript.spi.GeneratedBy; import org.teavm.backend.javascript.spi.GeneratedBy;
import org.teavm.classlib.PlatformDetector;
import org.teavm.classlib.java.lang.TArrayIndexOutOfBoundsException; import org.teavm.classlib.java.lang.TArrayIndexOutOfBoundsException;
import org.teavm.classlib.java.lang.TClass; import org.teavm.classlib.java.lang.TClass;
import org.teavm.classlib.java.lang.TIllegalArgumentException; import org.teavm.classlib.java.lang.TIllegalArgumentException;
@ -59,8 +60,14 @@ public final class TArray extends TObject {
if (length < 0) { if (length < 0) {
throw new TNegativeArraySizeException(); throw new TNegativeArraySizeException();
} }
if (PlatformDetector.isWebAssemblyGC()) {
return newInstanceImpl(componentType, length);
} else {
return newInstanceImpl(((TClass<?>) (Object) componentType).getPlatformClass(), length); return newInstanceImpl(((TClass<?>) (Object) componentType).getPlatformClass(), length);
} }
}
private static native TObject newInstanceImpl(Class<?> componentType, int length);
@GeneratedBy(ArrayNativeGenerator.class) @GeneratedBy(ArrayNativeGenerator.class)
@DelegateTo("newInstanceLowLevel") @DelegateTo("newInstanceLowLevel")

View File

@ -304,6 +304,11 @@ public class WasmGenerationVisitor extends BaseWasmGenerationVisitor {
return new WasmInt32Constant(0); return new WasmInt32Constant(0);
} }
@Override
protected WasmExpression nullLiteral(WasmType type) {
return new WasmInt32Constant(0);
}
@Override @Override
protected WasmExpression genIsNull(WasmExpression value) { protected WasmExpression genIsNull(WasmExpression value) {
return new WasmIntUnary(WasmIntType.INT32, WasmIntUnaryOperation.EQZ, value); return new WasmIntUnary(WasmIntType.INT32, WasmIntUnaryOperation.EQZ, value);

View File

@ -575,6 +575,8 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
protected abstract WasmExpression nullLiteral(Expr expr); protected abstract WasmExpression nullLiteral(Expr expr);
protected abstract WasmExpression nullLiteral(WasmType type);
protected abstract WasmExpression stringLiteral(String s); protected abstract WasmExpression stringLiteral(String s);
protected abstract WasmExpression classLiteral(ValueType type); protected abstract WasmExpression classLiteral(ValueType type);
@ -1179,13 +1181,13 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
var valueToCast = exprCache.create(result, wasmSourceType, expr.getLocation(), block.getBody()); var valueToCast = exprCache.create(result, wasmSourceType, expr.getLocation(), block.getBody());
var nullCheck = new WasmBranch(genIsNull(valueToCast.expr()), block); var nullCheck = new WasmBranch(genIsNull(valueToCast.expr()), block);
nullCheck.setResult(valueToCast.expr()); nullCheck.setResult(nullLiteral(wasmTargetType));
block.getBody().add(new WasmDrop(nullCheck)); block.getBody().add(new WasmDrop(nullCheck));
var supertypeCall = generateInstanceOf(valueToCast.expr(), expr.getTarget()); var supertypeCall = generateInstanceOf(valueToCast.expr(), expr.getTarget());
var breakIfPassed = new WasmBranch(supertypeCall, block); var breakIfPassed = new WasmBranch(supertypeCall, block);
breakIfPassed.setResult(valueToCast.expr()); breakIfPassed.setResult(generateCast(valueToCast.expr(), wasmTargetType));
block.getBody().add(new WasmDrop(breakIfPassed)); block.getBody().add(new WasmDrop(breakIfPassed));
var callSiteId = generateCallSiteId(expr.getLocation()); var callSiteId = generateCallSiteId(expr.getLocation());
@ -1194,7 +1196,7 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
callSiteId.generateThrow(block.getBody(), expr.getLocation()); callSiteId.generateThrow(block.getBody(), expr.getLocation());
valueToCast.release(); valueToCast.release();
result = generateCast(block, wasmTargetType); result = block;
} }
protected abstract WasmExpression generateCast(WasmExpression value, WasmType targetType); protected abstract WasmExpression generateCast(WasmExpression value, WasmType targetType);

View File

@ -96,6 +96,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
private WasmFunction createPrimitiveClassFunction; private WasmFunction createPrimitiveClassFunction;
private WasmFunction createArrayClassFunction; private WasmFunction createArrayClassFunction;
private final WasmGCSupertypeFunctionGenerator supertypeGenerator; private final WasmGCSupertypeFunctionGenerator supertypeGenerator;
private final WasmGCNewArrayFunctionGenerator newArrayGenerator;
private int classTagOffset; private int classTagOffset;
private int classFlagsOffset; private int classFlagsOffset;
@ -103,6 +104,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
private int classParentOffset; private int classParentOffset;
private int classArrayOffset; private int classArrayOffset;
private int classArrayItemOffset; private int classArrayItemOffset;
private int classNewArrayOffset;
private int classSupertypeFunctionOffset; private int classSupertypeFunctionOffset;
private int virtualTableFieldOffset; private int virtualTableFieldOffset;
@ -123,6 +125,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
standardClasses = new WasmGCStandardClasses(this); standardClasses = new WasmGCStandardClasses(this);
strings = new WasmGCStringPool(standardClasses, module, functionProvider); strings = new WasmGCStringPool(standardClasses, module, functionProvider);
supertypeGenerator = new WasmGCSupertypeFunctionGenerator(module, this, names, tagRegistry, functionTypes); supertypeGenerator = new WasmGCSupertypeFunctionGenerator(module, this, names, tagRegistry, functionTypes);
newArrayGenerator = new WasmGCNewArrayFunctionGenerator(module, functionTypes, this);
typeMapper = new WasmGCTypeMapper(this, functionTypes, module); typeMapper = new WasmGCTypeMapper(this, functionTypes, module);
} }
@ -190,12 +193,20 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
initializerFunctionStatements.clear(); initializerFunctionStatements.clear();
for (var classInfo : classInfoMap.values()) { for (var classInfo : classInfoMap.values()) {
var req = metadataRequirements.getInfo(classInfo.getValueType()); var req = metadataRequirements.getInfo(classInfo.getValueType());
if (req != null && req.isAssignable()) { if (req != null) {
if (req.isAssignable()) {
var supertypeFunction = supertypeGenerator.getIsSupertypeFunction(classInfo.getValueType()); var supertypeFunction = supertypeGenerator.getIsSupertypeFunction(classInfo.getValueType());
supertypeFunction.setReferenced(true); supertypeFunction.setReferenced(true);
function.getBody().add(setClassField(classInfo, classSupertypeFunctionOffset, function.getBody().add(setClassField(classInfo, classSupertypeFunctionOffset,
new WasmFunctionReference(supertypeFunction))); new WasmFunctionReference(supertypeFunction)));
} }
if (req.newArray()) {
var newArrayFunction = newArrayGenerator.generateNewArrayFunction(classInfo.getValueType());
newArrayFunction.setReferenced(true);
function.getBody().add(setClassField(classInfo, classNewArrayOffset,
new WasmFunctionReference(newArrayFunction)));
}
}
function.getBody().add(setClassField(classInfo, CLASS_FIELD_OFFSET, function.getBody().add(setClassField(classInfo, CLASS_FIELD_OFFSET,
new WasmGetGlobal(classClass.pointer))); new WasmGetGlobal(classClass.pointer)));
if (classInfo.initializerPointer != null) { if (classInfo.initializerPointer != null) {
@ -275,6 +286,16 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
return classSupertypeFunctionOffset; return classSupertypeFunctionOffset;
} }
@Override
public int getClassNameOffset() {
return classNameOffset;
}
@Override
public int getNewArrayFunctionOffset() {
return classNewArrayOffset;
}
@Override @Override
public int getVirtualMethodsOffset() { public int getVirtualMethodsOffset() {
return virtualTableFieldOffset; return virtualTableFieldOffset;
@ -546,6 +567,8 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
fields.add(standardClasses.classClass().getType().asStorage()); fields.add(standardClasses.classClass().getType().asStorage());
classSupertypeFunctionOffset = fields.size(); classSupertypeFunctionOffset = fields.size();
fields.add(supertypeGenerator.getFunctionType().getReference().asStorage()); fields.add(supertypeGenerator.getFunctionType().getReference().asStorage());
classNewArrayOffset = fields.size();
fields.add(newArrayGenerator.getNewArrayFunctionType().getReference().asStorage());
virtualTableFieldOffset = fields.size(); virtualTableFieldOffset = fields.size();
classNameOffset = fieldIndexes.getOrDefault(new FieldReference(className, "name"), -1); classNameOffset = fieldIndexes.getOrDefault(new FieldReference(className, "name"), -1);
} }

View File

@ -37,6 +37,10 @@ public interface WasmGCClassInfoProvider {
int getClassSupertypeFunctionOffset(); int getClassSupertypeFunctionOffset();
int getNewArrayFunctionOffset();
int getClassNameOffset();
default WasmGCClassInfo getClassInfo(String name) { default WasmGCClassInfo getClassInfo(String name) {
return getClassInfo(ValueType.object(name)); return getClassInfo(ValueType.object(name));
} }

View File

@ -0,0 +1,64 @@
/*
* 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.generate.gc.classes;
import org.teavm.backend.wasm.WasmFunctionTypes;
import org.teavm.backend.wasm.generate.TemporaryVariablePool;
import org.teavm.backend.wasm.generate.gc.methods.WasmGCGenerationUtil;
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.WasmModule;
import org.teavm.backend.wasm.model.WasmType;
import org.teavm.backend.wasm.model.expression.WasmGetLocal;
import org.teavm.backend.wasm.model.expression.WasmReturn;
import org.teavm.model.ValueType;
class WasmGCNewArrayFunctionGenerator {
private WasmModule module;
private WasmFunctionTypes functionTypes;
private WasmGCClassInfoProvider classInfoProvider;
private WasmFunctionType newArrayFunctionType;
WasmGCNewArrayFunctionGenerator(WasmModule module, WasmFunctionTypes functionTypes,
WasmGCClassInfoProvider classInfoProvider) {
this.module = module;
this.functionTypes = functionTypes;
this.classInfoProvider = classInfoProvider;
}
WasmFunction generateNewArrayFunction(ValueType itemType) {
var function = new WasmFunction(getNewArrayFunctionType());
module.functions.add(function);
var sizeLocal = new WasmLocal(WasmType.INT32);
function.add(sizeLocal);
var tempVars = new TemporaryVariablePool(function);
var genUtil = new WasmGCGenerationUtil(classInfoProvider, tempVars);
var targetVar = new WasmLocal(classInfoProvider.getClassInfo(ValueType.arrayOf(itemType)).getType());
function.add(targetVar);
genUtil.allocateArray(itemType, new WasmGetLocal(sizeLocal), null, targetVar, function.getBody());
function.getBody().add(new WasmReturn(new WasmGetLocal(targetVar)));
return function;
}
WasmFunctionType getNewArrayFunctionType() {
if (newArrayFunctionType == null) {
newArrayFunctionType = functionTypes.of(classInfoProvider.getClassInfo("java.lang.Object").getType(),
WasmType.INT32);
}
return newArrayFunctionType;
}
}

View File

@ -211,6 +211,11 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
: WasmType.Reference.STRUCT); : WasmType.Reference.STRUCT);
} }
@Override
protected WasmExpression nullLiteral(WasmType type) {
return new WasmNullConstant((WasmType.Reference) type);
}
@Override @Override
protected WasmExpression genIsNull(WasmExpression value) { protected WasmExpression genIsNull(WasmExpression value) {
return new WasmReferencesEqual(value, new WasmNullConstant(WasmType.Reference.STRUCT)); return new WasmReferencesEqual(value, new WasmNullConstant(WasmType.Reference.STRUCT));

View File

@ -0,0 +1,45 @@
/*
* 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.generators.gc;
import org.teavm.backend.wasm.model.WasmFunction;
import org.teavm.backend.wasm.model.WasmLocal;
import org.teavm.backend.wasm.model.WasmType;
import org.teavm.backend.wasm.model.expression.WasmCallReference;
import org.teavm.backend.wasm.model.expression.WasmGetLocal;
import org.teavm.backend.wasm.model.expression.WasmReturn;
import org.teavm.backend.wasm.model.expression.WasmStructGet;
import org.teavm.model.MethodReference;
public class ArrayGenerator implements WasmGCCustomGenerator {
@Override
public void apply(MethodReference method, WasmFunction function, WasmGCCustomGeneratorContext context) {
var clsStruct = context.classInfoProvider().getClassInfo("java.lang.Class").getStructure();
var classLocal = new WasmLocal(clsStruct.getReference());
var sizeLocal = new WasmLocal(WasmType.INT32);
function.add(classLocal);
function.add(sizeLocal);
var constructorRef = new WasmStructGet(clsStruct, new WasmGetLocal(classLocal),
context.classInfoProvider().getNewArrayFunctionOffset());
var functionType = context.functionTypes().of(
context.classInfoProvider().getClassInfo("java.lang.Object").getType(),
WasmType.INT32
);
var result = new WasmCallReference(constructorRef, functionType);
result.getArguments().add(new WasmGetLocal(sizeLocal));
function.getBody().add(new WasmReturn(result));
}
}

View File

@ -36,6 +36,9 @@ public class ClassGenerators implements WasmGCCustomGenerator {
case "isInstance": case "isInstance":
generateIsInstance(function, context); generateIsInstance(function, context);
break; break;
case "getName":
generateGetName(function, context);
break;
default: default:
throw new IllegalArgumentException("Unsupported method: " + method); throw new IllegalArgumentException("Unsupported method: " + method);
} }
@ -65,4 +68,14 @@ public class ClassGenerators implements WasmGCCustomGenerator {
function.getBody().add(new WasmReturn(conditional)); function.getBody().add(new WasmReturn(conditional));
} }
private void generateGetName(WasmFunction function, WasmGCCustomGeneratorContext context) {
var classCls = context.classInfoProvider().getClassInfo("java.lang.Class");
var thisVar = new WasmLocal(classCls.getType());
function.add(thisVar);
var nameRef = new WasmStructGet(classCls.getStructure(), new WasmGetLocal(thisVar),
context.classInfoProvider().getClassNameOffset());
function.getBody().add(new WasmReturn(nameRef));
}
} }

View File

@ -15,6 +15,7 @@
*/ */
package org.teavm.backend.wasm.generators.gc; package org.teavm.backend.wasm.generators.gc;
import java.lang.reflect.Array;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.teavm.backend.wasm.generate.gc.methods.WasmGCCustomGeneratorProvider; import org.teavm.backend.wasm.generate.gc.methods.WasmGCCustomGeneratorProvider;
@ -28,11 +29,13 @@ public class WasmGCCustomGenerators implements WasmGCCustomGeneratorProvider {
fillClass(); fillClass();
fillStringPool(); fillStringPool();
fillSystem(); fillSystem();
fillArray();
} }
private void fillClass() { private void fillClass() {
var classGenerators = new ClassGenerators(); var classGenerators = new ClassGenerators();
generators.put(new MethodReference(Class.class, "isInstance", Object.class, boolean.class), classGenerators); generators.put(new MethodReference(Class.class, "isInstance", Object.class, boolean.class), classGenerators);
generators.put(new MethodReference(Class.class, "getName", String.class), classGenerators);
} }
private void fillStringPool() { private void fillStringPool() {
@ -50,6 +53,12 @@ public class WasmGCCustomGenerators implements WasmGCCustomGeneratorProvider {
); );
} }
private void fillArray() {
var arrayGenerator = new ArrayGenerator();
generators.put(new MethodReference(Array.class, "newInstanceImpl", Class.class, int.class, Object.class),
arrayGenerator);
}
@Override @Override
public WasmGCCustomGenerator get(MethodReference method) { public WasmGCCustomGenerator get(MethodReference method) {
return generators.get(method); return generators.get(method);

View File

@ -15,6 +15,7 @@
*/ */
package org.teavm.model.analysis; package org.teavm.model.analysis;
import java.lang.reflect.Array;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.teavm.dependency.DependencyInfo; import org.teavm.dependency.DependencyInfo;
@ -34,6 +35,8 @@ public class ClassMetadataRequirements {
"getDeclaringClass", Class.class); "getDeclaringClass", Class.class);
private static final MethodReference GET_ENCLOSING_CLASS_METHOD = new MethodReference(Class.class, private static final MethodReference GET_ENCLOSING_CLASS_METHOD = new MethodReference(Class.class,
"getEnclosingClass", Class.class); "getEnclosingClass", Class.class);
private static final MethodReference NEW_ARRAY = new MethodReference(Array.class,
"newInstance", Class.class, int.class, Object.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<>();
@ -85,6 +88,14 @@ public class ClassMetadataRequirements {
requirements.computeIfAbsent(decodeType(className), k -> new ClassInfo()).enclosingClass = true; requirements.computeIfAbsent(decodeType(className), k -> new ClassInfo()).enclosingClass = true;
} }
} }
var newArrayMethod = dependencyInfo.getMethod(NEW_ARRAY);
if (newArrayMethod != null) {
var classNames = newArrayMethod.getVariable(1).getClassValueNode().getTypes();
for (var className : classNames) {
requirements.computeIfAbsent(decodeType(className), k -> new ClassInfo()).newArray = true;
}
}
} }
public Info getInfo(String className) { public Info getInfo(String className) {
@ -122,6 +133,7 @@ public class ClassMetadataRequirements {
boolean enclosingClass; boolean enclosingClass;
boolean superclass; boolean superclass;
boolean isAssignable; boolean isAssignable;
boolean newArray;
@Override @Override
public boolean name() { public boolean name() {
@ -152,6 +164,11 @@ public class ClassMetadataRequirements {
public boolean isAssignable() { public boolean isAssignable() {
return isAssignable; return isAssignable;
} }
@Override
public boolean newArray() {
return newArray;
}
} }
public interface Info { public interface Info {
@ -166,5 +183,7 @@ public class ClassMetadataRequirements {
boolean superclass(); boolean superclass();
boolean isAssignable(); boolean isAssignable();
boolean newArray();
} }
} }