mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2025-01-02 21:34:11 -08:00
wasm gc: reduce number of generated virtual tables, fix non-matching types in case of null literals
This commit is contained in:
parent
9b5e1e7661
commit
e4fa6bd364
|
@ -15,7 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.classlib.impl.console;
|
package org.teavm.classlib.impl.console;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.OutputStream;
|
||||||
import java.io.PrintStream;
|
import java.io.PrintStream;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.CharBuffer;
|
import java.nio.CharBuffer;
|
||||||
|
@ -30,7 +30,7 @@ public abstract class JsConsolePrintStream extends PrintStream {
|
||||||
private Runnable flushAction;
|
private Runnable flushAction;
|
||||||
|
|
||||||
public JsConsolePrintStream() {
|
public JsConsolePrintStream() {
|
||||||
super(new ByteArrayOutputStream());
|
super((OutputStream) null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -103,6 +103,11 @@ public class PreciseTypeInference extends BaseTypeInference<PreciseValueType> {
|
||||||
return new PreciseValueType(((ValueType.Array) valueType.valueType).getItemType(), false);
|
return new PreciseValueType(((ValueType.Array) valueType.valueType).getItemType(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected PreciseValueType arrayType(PreciseValueType preciseValueType) {
|
||||||
|
return new PreciseValueType(ValueType.arrayOf(preciseValueType.valueType), false);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected PreciseValueType methodReturnType(InvocationType invocationType, MethodReference methodRef) {
|
protected PreciseValueType methodReturnType(InvocationType invocationType, MethodReference methodRef) {
|
||||||
if (invocationType == InvocationType.SPECIAL) {
|
if (invocationType == InvocationType.SPECIAL) {
|
||||||
|
|
|
@ -38,6 +38,7 @@ public class WasmGCVariableCategoryProvider implements VariableCategoryProvider
|
||||||
public Object[] getCategories(Program program, MethodReference method) {
|
public Object[] getCategories(Program program, MethodReference method) {
|
||||||
inference = new PreciseTypeInference(program, method, hierarchy, returnTypes);
|
inference = new PreciseTypeInference(program, method, hierarchy, returnTypes);
|
||||||
inference.setPhisSkipped(true);
|
inference.setPhisSkipped(true);
|
||||||
|
inference.setBackPropagation(true);
|
||||||
var result = new Object[program.variableCount()];
|
var result = new Object[program.variableCount()];
|
||||||
for (int i = 0; i < program.variableCount(); ++i) {
|
for (int i = 0; i < program.variableCount(); ++i) {
|
||||||
var type = inference.typeOf(program.variableAt(i));
|
var type = inference.typeOf(program.variableAt(i));
|
||||||
|
|
|
@ -104,7 +104,6 @@ public class WasmGenerationVisitor extends BaseWasmGenerationVisitor {
|
||||||
|
|
||||||
private WasmGenerationContext context;
|
private WasmGenerationContext context;
|
||||||
private WasmClassGenerator classGenerator;
|
private WasmClassGenerator classGenerator;
|
||||||
private MethodReference currentMethod;
|
|
||||||
|
|
||||||
private List<ExceptionHandlerDescriptor> handlers = new ArrayList<>();
|
private List<ExceptionHandlerDescriptor> handlers = new ArrayList<>();
|
||||||
private WasmBlock lastTryBlock;
|
private WasmBlock lastTryBlock;
|
||||||
|
@ -118,11 +117,10 @@ public class WasmGenerationVisitor extends BaseWasmGenerationVisitor {
|
||||||
public WasmGenerationVisitor(WasmGenerationContext context, WasmClassGenerator classGenerator,
|
public WasmGenerationVisitor(WasmGenerationContext context, WasmClassGenerator classGenerator,
|
||||||
BinaryWriter binaryWriter, WasmFunction function, MethodReference currentMethod,
|
BinaryWriter binaryWriter, WasmFunction function, MethodReference currentMethod,
|
||||||
int firstVariable, boolean async) {
|
int firstVariable, boolean async) {
|
||||||
super(context, function, firstVariable, async);
|
super(context, currentMethod, function, firstVariable, async);
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.classGenerator = classGenerator;
|
this.classGenerator = classGenerator;
|
||||||
this.binaryWriter = binaryWriter;
|
this.binaryWriter = binaryWriter;
|
||||||
this.currentMethod = currentMethod;
|
|
||||||
this.managed = context.characteristics.isManaged(currentMethod);
|
this.managed = context.characteristics.isManaged(currentMethod);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -116,6 +116,7 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
|
||||||
|
|
||||||
private static final int SWITCH_TABLE_THRESHOLD = 256;
|
private static final int SWITCH_TABLE_THRESHOLD = 256;
|
||||||
private BaseWasmGenerationContext context;
|
private BaseWasmGenerationContext context;
|
||||||
|
protected final MethodReference currentMethod;
|
||||||
protected final WasmTypeInference typeInference;
|
protected final WasmTypeInference typeInference;
|
||||||
protected final WasmFunction function;
|
protected final WasmFunction function;
|
||||||
private int firstVariable;
|
private int firstVariable;
|
||||||
|
@ -131,9 +132,10 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
|
||||||
protected WasmExpression result;
|
protected WasmExpression result;
|
||||||
protected List<WasmExpression> resultConsumer;
|
protected List<WasmExpression> resultConsumer;
|
||||||
|
|
||||||
public BaseWasmGenerationVisitor(BaseWasmGenerationContext context, WasmFunction function,
|
public BaseWasmGenerationVisitor(BaseWasmGenerationContext context, MethodReference currentMethod,
|
||||||
int firstVariable, boolean async) {
|
WasmFunction function, int firstVariable, boolean async) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
|
this.currentMethod = currentMethod;
|
||||||
this.function = function;
|
this.function = function;
|
||||||
this.firstVariable = firstVariable;
|
this.firstVariable = firstVariable;
|
||||||
tempVars = new TemporaryVariablePool(function);
|
tempVars = new TemporaryVariablePool(function);
|
||||||
|
@ -833,6 +835,10 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
|
||||||
|
|
||||||
protected abstract CallSiteIdentifier generateCallSiteId(TextLocation location);
|
protected abstract CallSiteIdentifier generateCallSiteId(TextLocation location);
|
||||||
|
|
||||||
|
protected void acceptWithType(Expr expr, ValueType type) {
|
||||||
|
accept(expr);
|
||||||
|
}
|
||||||
|
|
||||||
protected WasmExpression generateInvocation(InvocationExpr expr, CallSiteIdentifier callSiteId) {
|
protected WasmExpression generateInvocation(InvocationExpr expr, CallSiteIdentifier callSiteId) {
|
||||||
if (expr.getType() == InvocationType.STATIC || expr.getType() == InvocationType.SPECIAL) {
|
if (expr.getType() == InvocationType.STATIC || expr.getType() == InvocationType.SPECIAL) {
|
||||||
var method = context.classes().resolve(expr.getMethod());
|
var method = context.classes().resolve(expr.getMethod());
|
||||||
|
@ -842,8 +848,13 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
|
||||||
: context.functions().forInstanceMethod(reference);
|
: context.functions().forInstanceMethod(reference);
|
||||||
|
|
||||||
var call = new WasmCall(function);
|
var call = new WasmCall(function);
|
||||||
for (var argument : expr.getArguments()) {
|
var arguments = expr.getArguments();
|
||||||
accept(argument);
|
for (int i = 0, argumentsSize = arguments.size(); i < argumentsSize; i++) {
|
||||||
|
var argument = arguments.get(i);
|
||||||
|
var type = expr.getType() == InvocationType.STATIC
|
||||||
|
? reference.parameterType(i)
|
||||||
|
: i == 0 ? ValueType.object(reference.getClassName()) : reference.parameterType(i - 1);
|
||||||
|
acceptWithType(argument, type);
|
||||||
call.getArguments().add(result);
|
call.getArguments().add(result);
|
||||||
}
|
}
|
||||||
if (expr.getType() == InvocationType.SPECIAL) {
|
if (expr.getType() == InvocationType.SPECIAL) {
|
||||||
|
@ -866,8 +877,12 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
|
||||||
var function = context.functions().forInstanceMethod(expr.getMethod());
|
var function = context.functions().forInstanceMethod(expr.getMethod());
|
||||||
var call = new WasmCall(function);
|
var call = new WasmCall(function);
|
||||||
call.getArguments().add(new WasmGetLocal(tmp));
|
call.getArguments().add(new WasmGetLocal(tmp));
|
||||||
for (var argument : expr.getArguments()) {
|
var arguments = expr.getArguments();
|
||||||
accept(argument);
|
acceptWithType(arguments.get(0), ValueType.object(expr.getMethod().getClassName()));
|
||||||
|
call.getArguments().add(result);
|
||||||
|
for (int i = 1; i < arguments.size(); i++) {
|
||||||
|
var argument = arguments.get(i);
|
||||||
|
acceptWithType(argument, expr.getMethod().parameterType(i));
|
||||||
call.getArguments().add(result);
|
call.getArguments().add(result);
|
||||||
}
|
}
|
||||||
if (callSiteId != null) {
|
if (callSiteId != null) {
|
||||||
|
@ -881,7 +896,7 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
|
||||||
return block;
|
return block;
|
||||||
} else {
|
} else {
|
||||||
var reference = expr.getMethod();
|
var reference = expr.getMethod();
|
||||||
accept(expr.getArguments().get(0));
|
acceptWithType(expr.getArguments().get(0), ValueType.object(expr.getMethod().getClassName()));
|
||||||
var instance = result;
|
var instance = result;
|
||||||
var block = new WasmBlock(false);
|
var block = new WasmBlock(false);
|
||||||
block.setType(mapType(reference.getReturnType()));
|
block.setType(mapType(reference.getReturnType()));
|
||||||
|
@ -893,7 +908,7 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
|
||||||
var arguments = new ArrayList<WasmExpression>();
|
var arguments = new ArrayList<WasmExpression>();
|
||||||
arguments.add(instance);
|
arguments.add(instance);
|
||||||
for (int i = 1; i < expr.getArguments().size(); ++i) {
|
for (int i = 1; i < expr.getArguments().size(); ++i) {
|
||||||
accept(expr.getArguments().get(i));
|
acceptWithType(expr.getArguments().get(i), expr.getMethod().parameterType(i - 1));
|
||||||
arguments.add(result);
|
arguments.add(result);
|
||||||
}
|
}
|
||||||
if (callSiteId != null) {
|
if (callSiteId != null) {
|
||||||
|
@ -1091,7 +1106,7 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
|
||||||
@Override
|
@Override
|
||||||
public void visit(ReturnStatement statement) {
|
public void visit(ReturnStatement statement) {
|
||||||
if (statement.getResult() != null) {
|
if (statement.getResult() != null) {
|
||||||
accept(statement.getResult());
|
acceptWithType(statement.getResult(), currentMethod.getReturnType());
|
||||||
} else {
|
} else {
|
||||||
result = null;
|
result = null;
|
||||||
}
|
}
|
||||||
|
@ -1102,7 +1117,7 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(InstanceOfExpr expr) {
|
public void visit(InstanceOfExpr expr) {
|
||||||
accept(expr.getExpr());
|
acceptWithType(expr.getExpr(), expr.getType());
|
||||||
|
|
||||||
var block = new WasmBlock(false);
|
var block = new WasmBlock(false);
|
||||||
block.setType(WasmType.INT32);
|
block.setType(WasmType.INT32);
|
||||||
|
@ -1143,7 +1158,7 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
|
||||||
block.setType(wasmTargetType);
|
block.setType(wasmTargetType);
|
||||||
block.setLocation(expr.getLocation());
|
block.setLocation(expr.getLocation());
|
||||||
|
|
||||||
accept(expr.getValue());
|
acceptWithType(expr.getValue(), expr.getTarget());
|
||||||
result.acceptVisitor(typeInference);
|
result.acceptVisitor(typeInference);
|
||||||
var wasmSourceType = typeInference.getResult();
|
var wasmSourceType = typeInference.getResult();
|
||||||
var valueToCast = exprCache.create(result, wasmSourceType, expr.getLocation(), block.getBody());
|
var valueToCast = exprCache.create(result, wasmSourceType, expr.getLocation(), block.getBody());
|
||||||
|
|
|
@ -20,10 +20,12 @@ import com.carrotsearch.hppc.ObjectIntMap;
|
||||||
import java.util.ArrayDeque;
|
import java.util.ArrayDeque;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import org.teavm.backend.lowlevel.generate.NameProvider;
|
import org.teavm.backend.lowlevel.generate.NameProvider;
|
||||||
import org.teavm.backend.wasm.BaseWasmFunctionRepository;
|
import org.teavm.backend.wasm.BaseWasmFunctionRepository;
|
||||||
|
@ -182,6 +184,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
|
||||||
classInfo = new WasmGCClassInfo(type);
|
classInfo = new WasmGCClassInfo(type);
|
||||||
classInfoQueue.add(classInfo);
|
classInfoQueue.add(classInfo);
|
||||||
classInfoMap.put(type, classInfo);
|
classInfoMap.put(type, classInfo);
|
||||||
|
VirtualTable virtualTable = null;
|
||||||
if (!(type instanceof ValueType.Primitive)) {
|
if (!(type instanceof ValueType.Primitive)) {
|
||||||
var name = type instanceof ValueType.Object
|
var name = type instanceof ValueType.Object
|
||||||
? ((ValueType.Object) type).getClassName()
|
? ((ValueType.Object) type).getClassName()
|
||||||
|
@ -189,6 +192,10 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
|
||||||
classInfo.structure = new WasmStructure(name != null ? names.forClass(name) : null);
|
classInfo.structure = new WasmStructure(name != null ? names.forClass(name) : null);
|
||||||
if (name != null) {
|
if (name != null) {
|
||||||
var classReader = classSource.get(name);
|
var classReader = classSource.get(name);
|
||||||
|
if (classReader == null || !classReader.hasModifier(ElementModifier.ABSTRACT)
|
||||||
|
&& !classReader.hasModifier(ElementModifier.INTERFACE)) {
|
||||||
|
virtualTable = virtualTables.lookup(name);
|
||||||
|
}
|
||||||
if (classReader != null && classReader.getParent() != null) {
|
if (classReader != null && classReader.getParent() != null) {
|
||||||
classInfo.structure.setSupertype(getClassInfo(classReader.getParent()).structure);
|
classInfo.structure.setSupertype(getClassInfo(classReader.getParent()).structure);
|
||||||
}
|
}
|
||||||
|
@ -199,7 +206,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
|
||||||
fillFields(classInfo, type);
|
fillFields(classInfo, type);
|
||||||
}
|
}
|
||||||
var pointerName = names.forClassInstance(type);
|
var pointerName = names.forClassInstance(type);
|
||||||
var classStructure = type instanceof ValueType.Object
|
var classStructure = virtualTable != null && virtualTable.hasValidEntries()
|
||||||
? initRegularClassStructure(((ValueType.Object) type).getClassName())
|
? initRegularClassStructure(((ValueType.Object) type).getClassName())
|
||||||
: standardClasses.classClass().getStructure();
|
: standardClasses.classClass().getStructure();
|
||||||
classInfo.virtualTableStructure = classStructure;
|
classInfo.virtualTableStructure = classStructure;
|
||||||
|
@ -213,7 +220,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
|
||||||
} else if (type instanceof ValueType.Array) {
|
} else if (type instanceof ValueType.Array) {
|
||||||
initArrayClass(classInfo, (ValueType.Array) type);
|
initArrayClass(classInfo, (ValueType.Array) type);
|
||||||
} else if (type instanceof ValueType.Object) {
|
} else if (type instanceof ValueType.Object) {
|
||||||
initRegularClass(classInfo, classStructure, ((ValueType.Object) type).getClassName());
|
initRegularClass(classInfo, virtualTable, classStructure, ((ValueType.Object) type).getClassName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return classInfo;
|
return classInfo;
|
||||||
|
@ -276,7 +283,8 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initRegularClass(WasmGCClassInfo classInfo, WasmStructure classStructure, String name) {
|
private void initRegularClass(WasmGCClassInfo classInfo, VirtualTable virtualTable, 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) {
|
||||||
|
@ -290,35 +298,33 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
|
||||||
var ranges = tagRegistry.getRanges(name);
|
var ranges = tagRegistry.getRanges(name);
|
||||||
int tag = ranges.stream().mapToInt(range -> range.lower).min().orElse(0);
|
int tag = ranges.stream().mapToInt(range -> range.lower).min().orElse(0);
|
||||||
target.add(setClassField(classInfo, classTagOffset, new WasmInt32Constant(tag)));
|
target.add(setClassField(classInfo, classTagOffset, new WasmInt32Constant(tag)));
|
||||||
var metadataReg = metadataRequirements.getInfo(name);
|
var metadataReq = metadataRequirements.getInfo(name);
|
||||||
if (metadataReg.name()) {
|
if (metadataReq.name()) {
|
||||||
var namePtr = strings.getStringConstant(name).global;
|
var namePtr = strings.getStringConstant(name).global;
|
||||||
target.add(setClassField(classInfo, classNameOffset, new WasmGetGlobal(namePtr)));
|
target.add(setClassField(classInfo, classNameOffset, new WasmGetGlobal(namePtr)));
|
||||||
}
|
}
|
||||||
if (cls != null) {
|
if (cls != null) {
|
||||||
if (metadataReg.simpleName() && cls.getSimpleName() != null) {
|
if (metadataReq.simpleName() && cls.getSimpleName() != null) {
|
||||||
var namePtr = strings.getStringConstant(cls.getSimpleName()).global;
|
var namePtr = strings.getStringConstant(cls.getSimpleName()).global;
|
||||||
target.add(setClassField(classInfo, classNameOffset, new WasmGetGlobal(namePtr)));
|
target.add(setClassField(classInfo, classNameOffset, new WasmGetGlobal(namePtr)));
|
||||||
}
|
}
|
||||||
if (cls.getParent() != null) {
|
if (cls.getParent() != null && metadataReq.superclass()) {
|
||||||
var parent = getClassInfo(cls.getParent());
|
var parent = getClassInfo(cls.getParent());
|
||||||
target.add(setClassField(classInfo, classParentOffset, new WasmGetGlobal(parent.pointer)));
|
target.add(setClassField(classInfo, classParentOffset, new WasmGetGlobal(parent.pointer)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var virtualTable = virtualTables.lookup(name);
|
if (virtualTable != null && virtualTable.hasValidEntries()) {
|
||||||
fillVirtualTableMethods(target, classStructure, classInfo.pointer, virtualTable, virtualTableFieldOffset,
|
fillVirtualTableMethods(target, classStructure, classInfo.pointer, virtualTable,
|
||||||
name);
|
virtualTableFieldOffset, name, new HashSet<>());
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private int fillVirtualTableMethods(List<WasmExpression> target, WasmStructure structure, WasmGlobal global,
|
private int fillVirtualTableMethods(List<WasmExpression> target, WasmStructure structure, WasmGlobal global,
|
||||||
VirtualTable virtualTable, int index, String origin) {
|
VirtualTable virtualTable, int index, String origin, Set<MethodDescriptor> filled) {
|
||||||
if (virtualTable.getParent() != null) {
|
|
||||||
index = fillVirtualTableMethods(target, structure, global, virtualTable.getParent(), index, origin);
|
|
||||||
}
|
|
||||||
for (var method : virtualTable.getMethods()) {
|
for (var method : virtualTable.getMethods()) {
|
||||||
var entry = virtualTable.getEntry(method);
|
var entry = virtualTable.getEntry(method);
|
||||||
if (entry != null && entry.getImplementor() != null) {
|
if (entry != null && entry.getImplementor() != null && filled.add(method)) {
|
||||||
var function = functionProvider.forInstanceMethod(entry.getImplementor());
|
var function = functionProvider.forInstanceMethod(entry.getImplementor());
|
||||||
if (!origin.equals(entry.getImplementor().getClassName())) {
|
if (!origin.equals(entry.getImplementor().getClassName())) {
|
||||||
var functionType = getFunctionType(virtualTable.getClassName(), method);
|
var functionType = getFunctionType(virtualTable.getClassName(), method);
|
||||||
|
@ -341,6 +347,10 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
|
||||||
target.add(new WasmStructSet(structure, new WasmGetGlobal(global), index, ref));
|
target.add(new WasmStructSet(structure, new WasmGetGlobal(global), index, ref));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (virtualTable.getParent() != null) {
|
||||||
|
index = fillVirtualTableMethods(target, structure, global, virtualTable.getParent(), index, origin,
|
||||||
|
filled);
|
||||||
|
}
|
||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -62,14 +62,32 @@ import org.teavm.model.ValueType;
|
||||||
public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
|
public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
|
||||||
private WasmGCGenerationContext context;
|
private WasmGCGenerationContext context;
|
||||||
private WasmGCGenerationUtil generationUtil;
|
private WasmGCGenerationUtil generationUtil;
|
||||||
|
private WasmType expectedType;
|
||||||
|
|
||||||
public WasmGCGenerationVisitor(WasmGCGenerationContext context, WasmFunction function,
|
public WasmGCGenerationVisitor(WasmGCGenerationContext context, MethodReference currentMethod,
|
||||||
int firstVariable, boolean async) {
|
WasmFunction function, int firstVariable, boolean async) {
|
||||||
super(context, function, firstVariable, async);
|
super(context, currentMethod, function, firstVariable, async);
|
||||||
this.context = context;
|
this.context = context;
|
||||||
generationUtil = new WasmGCGenerationUtil(context.classInfoProvider(), tempVars);
|
generationUtil = new WasmGCGenerationUtil(context.classInfoProvider(), tempVars);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void accept(Expr expr) {
|
||||||
|
accept(expr, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void accept(Expr expr, WasmType type) {
|
||||||
|
var previousExpectedType = expectedType;
|
||||||
|
expectedType = type;
|
||||||
|
super.accept(expr);
|
||||||
|
expectedType = previousExpectedType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void acceptWithType(Expr expr, ValueType type) {
|
||||||
|
accept(expr, mapType(type));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean isManaged() {
|
protected boolean isManaged() {
|
||||||
return false;
|
return false;
|
||||||
|
@ -137,24 +155,23 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
|
||||||
@Override
|
@Override
|
||||||
protected void storeField(Expr qualified, FieldReference field, Expr value, TextLocation location) {
|
protected void storeField(Expr qualified, FieldReference field, Expr value, TextLocation location) {
|
||||||
if (qualified == null) {
|
if (qualified == null) {
|
||||||
accept(value);
|
|
||||||
var wasmValue = result;
|
|
||||||
var global = context.classInfoProvider().getStaticFieldLocation(field);
|
var global = context.classInfoProvider().getStaticFieldLocation(field);
|
||||||
|
accept(value, global.getType());
|
||||||
|
var wasmValue = result;
|
||||||
var result = new WasmSetGlobal(global, wasmValue);
|
var result = new WasmSetGlobal(global, wasmValue);
|
||||||
result.setLocation(location);
|
result.setLocation(location);
|
||||||
resultConsumer.add(result);
|
resultConsumer.add(result);
|
||||||
} else {
|
} else {
|
||||||
accept(qualified);
|
acceptWithType(qualified, ValueType.object(field.getClassName()));
|
||||||
var target = result;
|
var target = result;
|
||||||
accept(value);
|
|
||||||
var wasmValue = result;
|
|
||||||
|
|
||||||
target.acceptVisitor(typeInference);
|
target.acceptVisitor(typeInference);
|
||||||
var type = (WasmType.CompositeReference) typeInference.getResult();
|
var type = (WasmType.CompositeReference) typeInference.getResult();
|
||||||
var struct = (WasmStructure) type.composite;
|
var struct = (WasmStructure) type.composite;
|
||||||
|
|
||||||
var fieldIndex = context.classInfoProvider().getFieldIndex(field);
|
var fieldIndex = context.classInfoProvider().getFieldIndex(field);
|
||||||
|
|
||||||
|
accept(value, struct.getFields().get(fieldIndex).asUnpackedType());
|
||||||
|
var wasmValue = result;
|
||||||
|
|
||||||
var expr = new WasmStructSet(struct, target, fieldIndex, wasmValue);
|
var expr = new WasmStructSet(struct, target, fieldIndex, wasmValue);
|
||||||
expr.setLocation(location);
|
expr.setLocation(location);
|
||||||
resultConsumer.add(expr);
|
resultConsumer.add(expr);
|
||||||
|
@ -175,7 +192,9 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected WasmExpression nullLiteral() {
|
protected WasmExpression nullLiteral() {
|
||||||
return new WasmNullConstant(WasmType.Reference.STRUCT);
|
return new WasmNullConstant(expectedType instanceof WasmType.Reference
|
||||||
|
? (WasmType.Reference) expectedType
|
||||||
|
: WasmType.Reference.STRUCT);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -404,7 +423,7 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
|
||||||
result = new WasmGetGlobal(global);
|
result = new WasmGetGlobal(global);
|
||||||
result.setLocation(expr.getLocation());
|
result.setLocation(expr.getLocation());
|
||||||
} else {
|
} else {
|
||||||
accept(expr.getQualified());
|
acceptWithType(expr.getQualified(), ValueType.object(expr.getField().getClassName()));
|
||||||
var target = result;
|
var target = result;
|
||||||
|
|
||||||
target.acceptVisitor(typeInference);
|
target.acceptVisitor(typeInference);
|
||||||
|
|
|
@ -249,7 +249,8 @@ public class WasmGCMethodGenerator implements BaseWasmFunctionRepository {
|
||||||
}
|
}
|
||||||
|
|
||||||
addInitializerErase(method, function);
|
addInitializerErase(method, function);
|
||||||
var visitor = new WasmGCGenerationVisitor(getGenerationContext(), function, firstVar, false);
|
var visitor = new WasmGCGenerationVisitor(getGenerationContext(), method.getReference(),
|
||||||
|
function, firstVar, false);
|
||||||
visitor.generate(ast.getBody(), function.getBody());
|
visitor.generate(ast.getBody(), function.getBody());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ package org.teavm.model.analysis;
|
||||||
|
|
||||||
import com.carrotsearch.hppc.IntStack;
|
import com.carrotsearch.hppc.IntStack;
|
||||||
import java.util.ArrayDeque;
|
import java.util.ArrayDeque;
|
||||||
|
import java.util.Deque;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import org.teavm.common.Graph;
|
import org.teavm.common.Graph;
|
||||||
import org.teavm.common.GraphBuilder;
|
import org.teavm.common.GraphBuilder;
|
||||||
|
@ -39,6 +40,7 @@ import org.teavm.model.instructions.ConstructArrayInstruction;
|
||||||
import org.teavm.model.instructions.ConstructInstruction;
|
import org.teavm.model.instructions.ConstructInstruction;
|
||||||
import org.teavm.model.instructions.ConstructMultiArrayInstruction;
|
import org.teavm.model.instructions.ConstructMultiArrayInstruction;
|
||||||
import org.teavm.model.instructions.DoubleConstantInstruction;
|
import org.teavm.model.instructions.DoubleConstantInstruction;
|
||||||
|
import org.teavm.model.instructions.ExitInstruction;
|
||||||
import org.teavm.model.instructions.FloatConstantInstruction;
|
import org.teavm.model.instructions.FloatConstantInstruction;
|
||||||
import org.teavm.model.instructions.GetElementInstruction;
|
import org.teavm.model.instructions.GetElementInstruction;
|
||||||
import org.teavm.model.instructions.GetFieldInstruction;
|
import org.teavm.model.instructions.GetFieldInstruction;
|
||||||
|
@ -51,6 +53,7 @@ import org.teavm.model.instructions.NegateInstruction;
|
||||||
import org.teavm.model.instructions.NullCheckInstruction;
|
import org.teavm.model.instructions.NullCheckInstruction;
|
||||||
import org.teavm.model.instructions.NullConstantInstruction;
|
import org.teavm.model.instructions.NullConstantInstruction;
|
||||||
import org.teavm.model.instructions.NumericOperandType;
|
import org.teavm.model.instructions.NumericOperandType;
|
||||||
|
import org.teavm.model.instructions.PutFieldInstruction;
|
||||||
import org.teavm.model.instructions.StringConstantInstruction;
|
import org.teavm.model.instructions.StringConstantInstruction;
|
||||||
import org.teavm.model.instructions.UnwrapArrayInstruction;
|
import org.teavm.model.instructions.UnwrapArrayInstruction;
|
||||||
|
|
||||||
|
@ -62,6 +65,7 @@ public abstract class BaseTypeInference<T> {
|
||||||
private Graph arrayGraph;
|
private Graph arrayGraph;
|
||||||
private Graph arrayUnwrapGraph;
|
private Graph arrayUnwrapGraph;
|
||||||
private boolean phisSkipped;
|
private boolean phisSkipped;
|
||||||
|
private boolean backPropagation;
|
||||||
|
|
||||||
public BaseTypeInference(Program program, MethodReference reference) {
|
public BaseTypeInference(Program program, MethodReference reference) {
|
||||||
this.program = program;
|
this.program = program;
|
||||||
|
@ -72,6 +76,10 @@ public abstract class BaseTypeInference<T> {
|
||||||
this.phisSkipped = phisSkipped;
|
this.phisSkipped = phisSkipped;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setBackPropagation(boolean backPropagation) {
|
||||||
|
this.backPropagation = backPropagation;
|
||||||
|
}
|
||||||
|
|
||||||
private void prepare() {
|
private void prepare() {
|
||||||
types = new Object[program.variableCount()];
|
types = new Object[program.variableCount()];
|
||||||
var visitor = new InitialTypeVisitor(program.variableCount());
|
var visitor = new InitialTypeVisitor(program.variableCount());
|
||||||
|
@ -153,10 +161,80 @@ public abstract class BaseTypeInference<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void propagateBack() {
|
||||||
|
if (!backPropagation) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var hasNullTypes = false;
|
||||||
|
for (var type : types) {
|
||||||
|
if (type == null) {
|
||||||
|
hasNullTypes = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!hasNullTypes) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var nullTypes = new boolean[program.variableCount()];
|
||||||
|
for (var i = 0; i < types.length; ++i) {
|
||||||
|
nullTypes[i] = types[i] == null;
|
||||||
|
}
|
||||||
|
var stack = new IntStack();
|
||||||
|
var typeStack = new ArrayDeque<T>();
|
||||||
|
for (var i = 0; i < types.length; ++i) {
|
||||||
|
if (nullTypes[i]) {
|
||||||
|
for (var j : graph.outgoingEdges(i)) {
|
||||||
|
if (!nullTypes[j]) {
|
||||||
|
typeStack.push((T) types[j]);
|
||||||
|
stack.push(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var visitor = new BackPropagationVisitor(nullTypes, stack, typeStack);
|
||||||
|
for (var block : program.getBasicBlocks()) {
|
||||||
|
for (var insn : block) {
|
||||||
|
insn.acceptVisitor(visitor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!stack.isEmpty()) {
|
||||||
|
var variable = stack.pop();
|
||||||
|
var type = typeStack.pop();
|
||||||
|
var formerType = (T) types[variable];
|
||||||
|
if (Objects.equals(formerType, type)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
type = doMerge(type, formerType);
|
||||||
|
if (Objects.equals(type, formerType) || type == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
types[variable] = type;
|
||||||
|
for (var pred : graph.incomingEdges(variable)) {
|
||||||
|
if (nullTypes[pred] && !Objects.equals(types[pred], type)) {
|
||||||
|
stack.push(pred);
|
||||||
|
typeStack.push(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (arrayGraph.incomingEdgesCount(variable) > 0) {
|
||||||
|
var arrayType = arrayType(type);
|
||||||
|
for (var pred : arrayGraph.incomingEdges(variable)) {
|
||||||
|
if (nullTypes[pred] && !Objects.equals(types[pred], arrayType)) {
|
||||||
|
stack.push(pred);
|
||||||
|
typeStack.push(arrayType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void ensure() {
|
public void ensure() {
|
||||||
if (types == null) {
|
if (types == null) {
|
||||||
prepare();
|
prepare();
|
||||||
propagate();
|
propagate();
|
||||||
|
propagateBack();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -185,6 +263,10 @@ public abstract class BaseTypeInference<T> {
|
||||||
|
|
||||||
protected abstract T elementType(T t);
|
protected abstract T elementType(T t);
|
||||||
|
|
||||||
|
protected T arrayType(T t) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
protected T methodReturnType(InvocationType invocationType, MethodReference methodRef) {
|
protected T methodReturnType(InvocationType invocationType, MethodReference methodRef) {
|
||||||
return mapType(methodRef.getReturnType());
|
return mapType(methodRef.getReturnType());
|
||||||
}
|
}
|
||||||
|
@ -380,4 +462,55 @@ public abstract class BaseTypeInference<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class BackPropagationVisitor extends AbstractInstructionVisitor {
|
||||||
|
private boolean[] nullTypes;
|
||||||
|
private IntStack stack;
|
||||||
|
private Deque<T> typeStack;
|
||||||
|
|
||||||
|
BackPropagationVisitor(boolean[] nullTypes, IntStack stack, Deque<T> typeStack) {
|
||||||
|
this.nullTypes = nullTypes;
|
||||||
|
this.stack = stack;
|
||||||
|
this.typeStack = typeStack;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(ExitInstruction insn) {
|
||||||
|
if (insn.getValueToReturn() != null) {
|
||||||
|
push(insn.getValueToReturn(), reference.getReturnType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(InvokeInstruction insn) {
|
||||||
|
if (insn.getInstance() != null) {
|
||||||
|
push(insn.getInstance(), ValueType.object(reference.getClassName()));
|
||||||
|
}
|
||||||
|
for (var i = 0; i < insn.getArguments().size(); ++i) {
|
||||||
|
push(insn.getArguments().get(i), reference.parameterType(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(GetFieldInstruction insn) {
|
||||||
|
if (insn.getInstance() != null) {
|
||||||
|
push(insn.getInstance(), ValueType.object(reference.getClassName()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(PutFieldInstruction insn) {
|
||||||
|
if (insn.getInstance() != null) {
|
||||||
|
push(insn.getInstance(), ValueType.object(reference.getClassName()));
|
||||||
|
}
|
||||||
|
push(insn.getValue(), insn.getFieldType());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void push(Variable variable, ValueType type) {
|
||||||
|
if (nullTypes[variable.getIndex()]) {
|
||||||
|
stack.push(variable.getIndex());
|
||||||
|
typeStack.push(mapType(type));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,8 @@ public class ClassMetadataRequirements {
|
||||||
private static final MethodReference GET_NAME_METHOD = new MethodReference(Class.class, "getName", String.class);
|
private static final MethodReference GET_NAME_METHOD = new MethodReference(Class.class, "getName", String.class);
|
||||||
private static final MethodReference GET_SIMPLE_NAME_METHOD = new MethodReference(Class.class,
|
private static final MethodReference GET_SIMPLE_NAME_METHOD = new MethodReference(Class.class,
|
||||||
"getSimpleName", String.class);
|
"getSimpleName", String.class);
|
||||||
|
private static final MethodReference GET_SUPERCLASS_METHOD = new MethodReference(Class.class, "getSuperclass",
|
||||||
|
Class.class);
|
||||||
private static final MethodReference GET_DECLARING_CLASS_METHOD = new MethodReference(Class.class,
|
private static final MethodReference GET_DECLARING_CLASS_METHOD = new MethodReference(Class.class,
|
||||||
"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,
|
||||||
|
@ -49,6 +51,14 @@ public class ClassMetadataRequirements {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var getSuperclassMethod = dependencyInfo.getMethod(GET_SUPERCLASS_METHOD);
|
||||||
|
if (getSuperclassMethod != null) {
|
||||||
|
String[] classNames = getSuperclassMethod.getVariable(0).getClassValueNode().getTypes();
|
||||||
|
for (String className : classNames) {
|
||||||
|
requirements.computeIfAbsent(className, k -> new ClassInfo()).declaringClass = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
MethodDependencyInfo getDeclaringClassMethod = dependencyInfo.getMethod(GET_DECLARING_CLASS_METHOD);
|
MethodDependencyInfo getDeclaringClassMethod = dependencyInfo.getMethod(GET_DECLARING_CLASS_METHOD);
|
||||||
if (getDeclaringClassMethod != null) {
|
if (getDeclaringClassMethod != null) {
|
||||||
String[] classNames = getDeclaringClassMethod.getVariable(0).getClassValueNode().getTypes();
|
String[] classNames = getDeclaringClassMethod.getVariable(0).getClassValueNode().getTypes();
|
||||||
|
@ -95,6 +105,7 @@ public class ClassMetadataRequirements {
|
||||||
boolean simpleName;
|
boolean simpleName;
|
||||||
boolean declaringClass;
|
boolean declaringClass;
|
||||||
boolean enclosingClass;
|
boolean enclosingClass;
|
||||||
|
boolean superclass;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean name() {
|
public boolean name() {
|
||||||
|
@ -115,6 +126,11 @@ public class ClassMetadataRequirements {
|
||||||
public boolean enclosingClass() {
|
public boolean enclosingClass() {
|
||||||
return enclosingClass;
|
return enclosingClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean superclass() {
|
||||||
|
return superclass;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface Info {
|
public interface Info {
|
||||||
|
@ -125,5 +141,7 @@ public class ClassMetadataRequirements {
|
||||||
boolean declaringClass();
|
boolean declaringClass();
|
||||||
|
|
||||||
boolean enclosingClass();
|
boolean enclosingClass();
|
||||||
|
|
||||||
|
boolean superclass();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,8 @@ public class VirtualTable {
|
||||||
private List<? extends MethodDescriptor> methods;
|
private List<? extends MethodDescriptor> methods;
|
||||||
private Set<MethodDescriptor> methodSet;
|
private Set<MethodDescriptor> methodSet;
|
||||||
private Map<MethodDescriptor, VirtualTableEntry> entryMap;
|
private Map<MethodDescriptor, VirtualTableEntry> entryMap;
|
||||||
|
private boolean hasValidEntries;
|
||||||
|
private boolean hasValidEntriesComputed;
|
||||||
|
|
||||||
VirtualTable(String className, VirtualTable parent, List<? extends MethodDescriptor> methods,
|
VirtualTable(String className, VirtualTable parent, List<? extends MethodDescriptor> methods,
|
||||||
Set<MethodDescriptor> methodSet, Map<MethodDescriptor, VirtualTableEntry> entryMap) {
|
Set<MethodDescriptor> methodSet, Map<MethodDescriptor, VirtualTableEntry> entryMap) {
|
||||||
|
@ -70,4 +72,23 @@ public class VirtualTable {
|
||||||
public int size() {
|
public int size() {
|
||||||
return methods.size() + (parent != null ? parent.size() : 0);
|
return methods.size() + (parent != null ? parent.size() : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean hasValidEntries() {
|
||||||
|
if (!hasValidEntriesComputed) {
|
||||||
|
hasValidEntriesComputed = true;
|
||||||
|
hasValidEntries = false;
|
||||||
|
if (entryMap != null) {
|
||||||
|
for (var entry : entryMap.values()) {
|
||||||
|
if (entry.getImplementor() != null) {
|
||||||
|
hasValidEntries = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (parent != null && parent.hasValidEntries()) {
|
||||||
|
hasValidEntries = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hasValidEntries;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user