wasm gc: fix issues with casts

This commit is contained in:
Alexey Andreev 2024-09-15 20:35:07 +02:00
parent 7e622d8bc7
commit 7784969bb8
8 changed files with 125 additions and 117 deletions

View File

@ -121,9 +121,15 @@ public class WasmGCTarget implements TeaVMTarget, TeaVMWasmGCHost {
} }
@Override @Override
public void beforeOptimizations(Program program, MethodReader method) { public void beforeInlining(Program program, MethodReader method) {
if (strict) { if (strict) {
nullCheckInsertion.transformProgram(program, method.getReference()); nullCheckInsertion.transformProgram(program, method.getReference());
}
}
@Override
public void beforeOptimizations(Program program, MethodReader method) {
if (strict) {
boundCheckInsertion.transformProgram(program, method.getReference()); boundCheckInsertion.transformProgram(program, method.getReference());
} }
} }

View File

@ -782,7 +782,7 @@ public class DisassemblyCodeListener extends BaseDisassemblyListener implements
@Override @Override
public void cast(WasmHollowType.Reference type) { public void cast(WasmHollowType.Reference type) {
writer.address().write("ref.cast (ref "); writer.address().write("ref.cast ");
writeType(type); writeType(type);
writer.eol(); writer.eol();
} }

View File

@ -164,21 +164,6 @@ public class WasmGenerationVisitor extends BaseWasmGenerationVisitor {
super.visit(expr); super.visit(expr);
} }
@Override
protected WasmExpression generateCast(WasmExpression value, WasmType targetType) {
return value;
}
@Override
protected WasmType mapCastSourceType(WasmType type) {
return type;
}
@Override
protected boolean validateCastTypes(WasmType sourceType, WasmType targetType, TextLocation location) {
return true;
}
@Override @Override
protected WasmType mapType(ValueType type) { protected WasmType mapType(ValueType type) {
return WasmGeneratorUtil.mapType(type); return WasmGeneratorUtil.mapType(type);

View File

@ -1186,19 +1186,13 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
public void visit(CastExpr expr) { public void visit(CastExpr expr) {
var wasmTargetType = mapType(expr.getTarget()); var wasmTargetType = mapType(expr.getTarget());
acceptWithType(expr.getValue(), expr.getTarget()); acceptWithType(expr.getValue(), expr.getTarget());
if (!expr.isWeak()) {
result.acceptVisitor(typeInference); result.acceptVisitor(typeInference);
var wasmSourceType = typeInference.getResult(); var wasmSourceType = typeInference.getResult();
if (!expr.isWeak()) {
if (wasmSourceType == null) { if (wasmSourceType == null) {
return; return;
} }
wasmSourceType = mapCastSourceType(wasmSourceType);
if (!validateCastTypes(wasmSourceType, wasmTargetType, expr.getLocation())) {
return;
}
var block = new WasmBlock(false); var block = new WasmBlock(false);
block.setType(wasmSourceType); block.setType(wasmSourceType);
block.setLocation(expr.getLocation()); block.setLocation(expr.getLocation());
@ -1223,16 +1217,9 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
valueToCast.release(); valueToCast.release();
result = block; result = block;
} }
result = generateCast(result, wasmTargetType);
result.setLocation(expr.getLocation()); result.setLocation(expr.getLocation());
} }
protected abstract WasmType mapCastSourceType(WasmType type);
protected abstract boolean validateCastTypes(WasmType sourceType, WasmType targetType, TextLocation location);
protected abstract WasmExpression generateCast(WasmExpression value, WasmType targetType);
@Override @Override
public void visit(InitClassStatement statement) { public void visit(InitClassStatement statement) {
if (needsClassInitializer(statement.getClassName())) { if (needsClassInitializer(statement.getClassName())) {

View File

@ -108,7 +108,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
private WasmGCVirtualTableProvider virtualTables; private WasmGCVirtualTableProvider virtualTables;
private BaseWasmFunctionRepository functionProvider; private BaseWasmFunctionRepository functionProvider;
private Map<ValueType, WasmGCClassInfo> classInfoMap = new LinkedHashMap<>(); private Map<ValueType, WasmGCClassInfo> classInfoMap = new LinkedHashMap<>();
private Queue<WasmGCClassInfo> classInfoQueue = new ArrayDeque<>(); private Queue<Runnable> queue = new ArrayDeque<>();
private ObjectIntMap<FieldReference> fieldIndexes = new ObjectIntHashMap<>(); private ObjectIntMap<FieldReference> fieldIndexes = new ObjectIntHashMap<>();
private Map<FieldReference, WasmGlobal> staticFieldLocations = new HashMap<>(); private Map<FieldReference, WasmGlobal> staticFieldLocations = new HashMap<>();
private List<Consumer<WasmFunction>> staticFieldInitializers = new ArrayList<>(); private List<Consumer<WasmFunction>> staticFieldInitializers = new ArrayList<>();
@ -171,7 +171,7 @@ 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);
supertypeGenerator = new WasmGCSupertypeFunctionGenerator(module, this, names, tagRegistry, functionTypes); supertypeGenerator = new WasmGCSupertypeFunctionGenerator(module, this, names, tagRegistry, functionTypes);
newArrayGenerator = new WasmGCNewArrayFunctionGenerator(module, functionTypes, this, names); 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();
typeMapper.setCustomTypeMappers(customTypeMapperFactories.stream() typeMapper.setCustomTypeMappers(customTypeMapperFactories.stream()
@ -213,13 +213,12 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
} }
public boolean process() { public boolean process() {
if (classInfoQueue.isEmpty()) { if (queue.isEmpty()) {
return false; return false;
} }
while (!classInfoQueue.isEmpty()) { while (!queue.isEmpty()) {
var classInfo = classInfoQueue.remove(); var action = queue.remove();
classInfo.initializer.accept(initializerFunctionStatements); action.run();
classInfo.initializer = null;
initStructures(); initStructures();
} }
return true; return true;
@ -245,20 +244,13 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
function.getBody().addAll(initializerFunctionStatements); function.getBody().addAll(initializerFunctionStatements);
initializerFunctionStatements.clear(); initializerFunctionStatements.clear();
for (var classInfo : classInfoMap.values()) { for (var classInfo : classInfoMap.values()) {
var req = metadataRequirements.getInfo(classInfo.getValueType()); if (classInfo.supertypeFunction != null) {
if (req != null) {
if (req.isAssignable()) {
var supertypeFunction = supertypeGenerator.getIsSupertypeFunction(classInfo.getValueType());
supertypeFunction.setReferenced(true);
function.getBody().add(setClassField(classInfo, classSupertypeFunctionOffset, function.getBody().add(setClassField(classInfo, classSupertypeFunctionOffset,
new WasmFunctionReference(supertypeFunction))); new WasmFunctionReference(classInfo.supertypeFunction)));
} }
if (req.newArray()) { if (classInfo.initArrayFunction != null) {
var newArrayFunction = getArrayConstructor(ValueType.arrayOf(classInfo.getValueType()));
newArrayFunction.setReferenced(true);
function.getBody().add(setClassField(classInfo, classNewArrayOffset, function.getBody().add(setClassField(classInfo, classNewArrayOffset,
new WasmFunctionReference(newArrayFunction))); new WasmFunctionReference(classInfo.initArrayFunction)));
}
} }
} }
for (var consumer : staticFieldInitializers) { for (var consumer : staticFieldInitializers) {
@ -271,7 +263,11 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
var classInfo = classInfoMap.get(type); var classInfo = classInfoMap.get(type);
if (classInfo == null) { if (classInfo == null) {
classInfo = new WasmGCClassInfo(type); classInfo = new WasmGCClassInfo(type);
classInfoQueue.add(classInfo); var finalClassInfo = classInfo;
queue.add(() -> {
finalClassInfo.initializer.accept(initializerFunctionStatements);
finalClassInfo.initializer = null;
});
classInfoMap.put(type, classInfo); classInfoMap.put(type, classInfo);
WasmGCVirtualTable virtualTable = null; WasmGCVirtualTable virtualTable = null;
if (!(type instanceof ValueType.Primitive)) { if (!(type instanceof ValueType.Primitive)) {
@ -284,7 +280,6 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
isInterface = true; isInterface = true;
classInfo.structure = standardClasses.objectClass().structure; classInfo.structure = standardClasses.objectClass().structure;
} else { } else {
var finalClassInfo = classInfo;
if (type instanceof ValueType.Array) { if (type instanceof ValueType.Array) {
var itemType = ((ValueType.Array) type).getItemType(); var itemType = ((ValueType.Array) type).getItemType();
if (!(itemType instanceof ValueType.Primitive) && !itemType.equals(OBJECT_TYPE)) { if (!(itemType instanceof ValueType.Primitive) && !itemType.equals(OBJECT_TYPE)) {
@ -346,6 +341,18 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
} else if (type instanceof ValueType.Object) { } else if (type instanceof ValueType.Object) {
initRegularClass(classInfo, virtualTable, classStructure, ((ValueType.Object) type).getClassName()); initRegularClass(classInfo, virtualTable, classStructure, ((ValueType.Object) type).getClassName());
} }
var req = metadataRequirements.getInfo(type);
if (req != null) {
if (req.newArray()) {
classInfo.initArrayFunction = getArrayConstructor(ValueType.arrayOf(classInfo.getValueType()));
classInfo.initArrayFunction.setReferenced(true);
}
if (req.isAssignable()) {
var supertypeFunction = supertypeGenerator.getIsSupertypeFunction(classInfo.getValueType());
supertypeFunction.setReferenced(true);
classInfo.supertypeFunction = supertypeFunction;
}
}
} }
return classInfo; return classInfo;
} }
@ -490,6 +497,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
private void initRegularClass(WasmGCClassInfo classInfo, WasmGCVirtualTable virtualTable, private void initRegularClass(WasmGCClassInfo classInfo, WasmGCVirtualTable virtualTable,
WasmStructure classStructure, String name) { WasmStructure classStructure, String name) {
var cls = classSource.get(name); var cls = classSource.get(name);
if (classInitializerInfo.isDynamicInitializer(name)) { if (classInitializerInfo.isDynamicInitializer(name)) {
if (cls != null && cls.getMethod(CLINIT_METHOD_DESC) != null) { if (cls != null && cls.getMethod(CLINIT_METHOD_DESC) != null) {
var clinitType = functionTypes.of(null); var clinitType = functionTypes.of(null);

View File

@ -33,6 +33,8 @@ public class WasmGCClassInfo {
WasmGlobal initializerPointer; WasmGlobal initializerPointer;
Consumer<List<WasmExpression>> initializer; Consumer<List<WasmExpression>> initializer;
WasmFunction newArrayFunction; WasmFunction newArrayFunction;
WasmFunction initArrayFunction;
WasmFunction supertypeFunction;
WasmGCClassInfo(ValueType valueType) { WasmGCClassInfo(ValueType valueType) {
this.valueType = valueType; this.valueType = valueType;

View File

@ -16,6 +16,7 @@
package org.teavm.backend.wasm.generate.gc.classes; package org.teavm.backend.wasm.generate.gc.classes;
import java.util.List; import java.util.List;
import java.util.Queue;
import org.teavm.backend.wasm.WasmFunctionTypes; import org.teavm.backend.wasm.WasmFunctionTypes;
import org.teavm.backend.wasm.generate.TemporaryVariablePool; import org.teavm.backend.wasm.generate.TemporaryVariablePool;
import org.teavm.backend.wasm.generate.gc.WasmGCNameProvider; import org.teavm.backend.wasm.generate.gc.WasmGCNameProvider;
@ -35,13 +36,16 @@ class WasmGCNewArrayFunctionGenerator {
private WasmGCClassInfoProvider classInfoProvider; private WasmGCClassInfoProvider classInfoProvider;
private WasmFunctionType newArrayFunctionType; private WasmFunctionType newArrayFunctionType;
private WasmGCNameProvider names; private WasmGCNameProvider names;
private Queue<Runnable> queue;
WasmGCNewArrayFunctionGenerator(WasmModule module, WasmFunctionTypes functionTypes, WasmGCNewArrayFunctionGenerator(WasmModule module, WasmFunctionTypes functionTypes,
WasmGCClassInfoProvider classInfoProvider, WasmGCNameProvider names) { WasmGCClassInfoProvider classInfoProvider, WasmGCNameProvider names,
Queue<Runnable> queue) {
this.module = module; this.module = module;
this.functionTypes = functionTypes; this.functionTypes = functionTypes;
this.classInfoProvider = classInfoProvider; this.classInfoProvider = classInfoProvider;
this.names = names; this.names = names;
this.queue = queue;
} }
WasmFunction generateNewArrayFunction(ValueType itemType) { WasmFunction generateNewArrayFunction(ValueType itemType) {
@ -53,6 +57,8 @@ class WasmGCNewArrayFunctionGenerator {
var function = new WasmFunction(functionType); var function = new WasmFunction(functionType);
function.setName(names.topLevel("Array<" + names.suggestForType(itemType) + ">@new")); function.setName(names.topLevel("Array<" + names.suggestForType(itemType) + ">@new"));
module.functions.add(function); module.functions.add(function);
queue.add(() -> {
var sizeLocal = new WasmLocal(WasmType.INT32, "length"); var sizeLocal = new WasmLocal(WasmType.INT32, "length");
function.add(sizeLocal); function.add(sizeLocal);
var tempVars = new TemporaryVariablePool(function); var tempVars = new TemporaryVariablePool(function);
@ -61,6 +67,7 @@ class WasmGCNewArrayFunctionGenerator {
function.add(targetVar); function.add(targetVar);
genUtil.allocateArray(itemType, () -> new WasmGetLocal(sizeLocal), null, targetVar, function.getBody()); genUtil.allocateArray(itemType, () -> new WasmGetLocal(sizeLocal), null, targetVar, function.getBody());
function.getBody().add(new WasmReturn(new WasmGetLocal(targetVar))); function.getBody().add(new WasmReturn(new WasmGetLocal(targetVar)));
});
return function; return function;
} }

View File

@ -53,6 +53,7 @@ import org.teavm.backend.wasm.model.expression.WasmArrayGet;
import org.teavm.backend.wasm.model.expression.WasmArrayLength; import org.teavm.backend.wasm.model.expression.WasmArrayLength;
import org.teavm.backend.wasm.model.expression.WasmArraySet; import org.teavm.backend.wasm.model.expression.WasmArraySet;
import org.teavm.backend.wasm.model.expression.WasmBlock; 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.WasmCallReference;
import org.teavm.backend.wasm.model.expression.WasmCast; import org.teavm.backend.wasm.model.expression.WasmCast;
@ -443,26 +444,76 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
@Override @Override
public void visit(CastExpr expr) { public void visit(CastExpr expr) {
var type = expr.getTarget(); var needsCast = true;
if (!expr.isWeak() && canCastNatively(type)) { acceptWithType(expr.getValue(), expr.getTarget());
var wasmType = context.classInfoProvider().getClassInfo(type).getType();
var block = new WasmBlock(false);
acceptWithType(expr.getValue(), type);
var wasmValue = result;
result.acceptVisitor(typeInference); result.acceptVisitor(typeInference);
var sourceWasmType = (WasmType.Reference) typeInference.getResult(); var sourceType = (WasmType.Reference) typeInference.getResult();
if (sourceWasmType == null || !validateCastTypes(sourceWasmType, wasmType, expr.getLocation())) { if (sourceType == null) {
return; return;
} }
block.setType(wasmType); var targetType = (WasmType.Reference) context.typeMapper().mapType(expr.getTarget());
WasmStructure targetStruct = null;
if (targetType instanceof WasmType.CompositeReference) {
var targetComposite = ((WasmType.CompositeReference) targetType).composite;
if (targetComposite instanceof WasmStructure) {
targetStruct = (WasmStructure) targetComposite;
}
}
var canInsertCast = true;
if (targetStruct != null && sourceType instanceof WasmType.CompositeReference) {
var sourceComposite = (WasmType.CompositeReference) sourceType;
if (!sourceType.isNullable()) {
sourceType = sourceComposite.composite.getReference();
}
var sourceStruct = (WasmStructure) sourceComposite.composite;
if (targetStruct.isSupertypeOf(sourceStruct)) {
canInsertCast = false;
} else if (!sourceStruct.isSupertypeOf(targetStruct)) {
var block = new WasmBlock(false);
block.setLocation(expr.getLocation()); block.setLocation(expr.getLocation());
block.getBody().add(new WasmCastBranch(WasmCastCondition.SUCCESS, wasmValue, sourceWasmType, block.getBody().add(result);
wasmType, block)); block.getBody().add(new WasmUnreachable());
generateThrowCCE(expr.getLocation(), block.getBody()); result = block;
return;
}
}
if (!expr.isWeak()) {
result.acceptVisitor(typeInference);
var block = new WasmBlock(false);
block.setLocation(expr.getLocation());
block.setType(targetType);
if (canCastNatively(expr.getTarget())) {
if (!canInsertCast) {
return;
}
block.getBody().add(new WasmCastBranch(WasmCastCondition.SUCCESS, result, sourceType,
targetType, block));
result = block; result = block;
} else { } else {
super.visit(expr); var nonNullValue = new WasmNullBranch(WasmNullCondition.NULL, result, block);
nonNullValue.setResult(new WasmNullConstant(sourceType));
var valueToCast = exprCache.create(nonNullValue, sourceType, expr.getLocation(), block.getBody());
var supertypeCall = generateInstanceOf(valueToCast.expr(), expr.getTarget());
var breakIfPassed = new WasmBranch(supertypeCall, block);
breakIfPassed.setResult(valueToCast.expr());
block.getBody().add(new WasmDrop(breakIfPassed));
result = block;
if (canInsertCast) {
var cast = new WasmCast(result, targetType);
cast.setLocation(expr.getLocation());
result = cast;
}
}
generateThrowCCE(expr.getLocation(), block.getBody());
} else if (canInsertCast) {
result = new WasmCast(result, targetType);
result.setLocation(expr.getLocation());
} }
} }
@ -478,44 +529,6 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
return !cls.hasModifier(ElementModifier.INTERFACE); return !cls.hasModifier(ElementModifier.INTERFACE);
} }
@Override
protected WasmExpression generateCast(WasmExpression value, WasmType targetType) {
return new WasmCast(value, (WasmType.Reference) targetType);
}
@Override
protected WasmType mapCastSourceType(WasmType type) {
if (!(type instanceof WasmType.CompositeReference)) {
return type;
}
var refType = (WasmType.CompositeReference) type;
return refType.isNullable() ? refType : refType.composite.getReference();
}
@Override
protected boolean validateCastTypes(WasmType sourceType, WasmType targetType, TextLocation location) {
if (!(sourceType instanceof WasmType.CompositeReference)
|| !(targetType instanceof WasmType.CompositeReference)) {
return false;
}
var sourceRefType = (WasmType.CompositeReference) sourceType;
var targetRefType = (WasmType.CompositeReference) targetType;
if (sourceRefType.composite instanceof WasmStructure
&& targetRefType.composite instanceof WasmStructure) {
var sourceStruct = (WasmStructure) sourceRefType.composite;
var targetStruct = (WasmStructure) targetRefType.composite;
if (targetStruct.isSupertypeOf(sourceStruct)) {
return false;
}
if (!sourceStruct.isSupertypeOf(targetStruct)) {
result = new WasmUnreachable();
result.setLocation(location);
return false;
}
}
return true;
}
@Override @Override
protected boolean needsClassInitializer(String className) { protected boolean needsClassInitializer(String className) {
return context.classInfoProvider().getClassInfo(className).getInitializerPointer() != null; return context.classInfoProvider().getClassInfo(className).getInitializerPointer() != null;