From e4fa6bd364cac312dab3406ff7846ca7055bb388 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Fri, 2 Aug 2024 13:50:49 +0200 Subject: [PATCH] wasm gc: reduce number of generated virtual tables, fix non-matching types in case of null literals --- .../impl/console/JsConsolePrintStream.java | 4 +- .../backend/wasm/gc/PreciseTypeInference.java | 5 + .../gc/WasmGCVariableCategoryProvider.java | 1 + .../wasm/generate/WasmGenerationVisitor.java | 4 +- .../methods/BaseWasmGenerationVisitor.java | 37 +++-- .../gc/classes/WasmGCClassGenerator.java | 40 ++++-- .../gc/methods/WasmGCGenerationVisitor.java | 43 ++++-- .../gc/methods/WasmGCMethodGenerator.java | 3 +- .../model/analysis/BaseTypeInference.java | 133 ++++++++++++++++++ .../analysis/ClassMetadataRequirements.java | 18 +++ .../org/teavm/model/classes/VirtualTable.java | 21 +++ 11 files changed, 265 insertions(+), 44 deletions(-) diff --git a/classlib/src/main/java/org/teavm/classlib/impl/console/JsConsolePrintStream.java b/classlib/src/main/java/org/teavm/classlib/impl/console/JsConsolePrintStream.java index 83a145632..953cc161e 100644 --- a/classlib/src/main/java/org/teavm/classlib/impl/console/JsConsolePrintStream.java +++ b/classlib/src/main/java/org/teavm/classlib/impl/console/JsConsolePrintStream.java @@ -15,7 +15,7 @@ */ package org.teavm.classlib.impl.console; -import java.io.ByteArrayOutputStream; +import java.io.OutputStream; import java.io.PrintStream; import java.nio.ByteBuffer; import java.nio.CharBuffer; @@ -30,7 +30,7 @@ public abstract class JsConsolePrintStream extends PrintStream { private Runnable flushAction; public JsConsolePrintStream() { - super(new ByteArrayOutputStream()); + super((OutputStream) null); } @Override diff --git a/core/src/main/java/org/teavm/backend/wasm/gc/PreciseTypeInference.java b/core/src/main/java/org/teavm/backend/wasm/gc/PreciseTypeInference.java index 587fde704..bc1f64780 100644 --- a/core/src/main/java/org/teavm/backend/wasm/gc/PreciseTypeInference.java +++ b/core/src/main/java/org/teavm/backend/wasm/gc/PreciseTypeInference.java @@ -103,6 +103,11 @@ public class PreciseTypeInference extends BaseTypeInference { return new PreciseValueType(((ValueType.Array) valueType.valueType).getItemType(), false); } + @Override + protected PreciseValueType arrayType(PreciseValueType preciseValueType) { + return new PreciseValueType(ValueType.arrayOf(preciseValueType.valueType), false); + } + @Override protected PreciseValueType methodReturnType(InvocationType invocationType, MethodReference methodRef) { if (invocationType == InvocationType.SPECIAL) { diff --git a/core/src/main/java/org/teavm/backend/wasm/gc/WasmGCVariableCategoryProvider.java b/core/src/main/java/org/teavm/backend/wasm/gc/WasmGCVariableCategoryProvider.java index 0b47b5521..08060e4e6 100644 --- a/core/src/main/java/org/teavm/backend/wasm/gc/WasmGCVariableCategoryProvider.java +++ b/core/src/main/java/org/teavm/backend/wasm/gc/WasmGCVariableCategoryProvider.java @@ -38,6 +38,7 @@ public class WasmGCVariableCategoryProvider implements VariableCategoryProvider public Object[] getCategories(Program program, MethodReference method) { inference = new PreciseTypeInference(program, method, hierarchy, returnTypes); inference.setPhisSkipped(true); + inference.setBackPropagation(true); var result = new Object[program.variableCount()]; for (int i = 0; i < program.variableCount(); ++i) { var type = inference.typeOf(program.variableAt(i)); diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java index 6cabefef3..172367757 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java @@ -104,7 +104,6 @@ public class WasmGenerationVisitor extends BaseWasmGenerationVisitor { private WasmGenerationContext context; private WasmClassGenerator classGenerator; - private MethodReference currentMethod; private List handlers = new ArrayList<>(); private WasmBlock lastTryBlock; @@ -118,11 +117,10 @@ public class WasmGenerationVisitor extends BaseWasmGenerationVisitor { public WasmGenerationVisitor(WasmGenerationContext context, WasmClassGenerator classGenerator, BinaryWriter binaryWriter, WasmFunction function, MethodReference currentMethod, int firstVariable, boolean async) { - super(context, function, firstVariable, async); + super(context, currentMethod, function, firstVariable, async); this.context = context; this.classGenerator = classGenerator; this.binaryWriter = binaryWriter; - this.currentMethod = currentMethod; this.managed = context.characteristics.isManaged(currentMethod); } diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/common/methods/BaseWasmGenerationVisitor.java b/core/src/main/java/org/teavm/backend/wasm/generate/common/methods/BaseWasmGenerationVisitor.java index 747fd7c51..e1236af8e 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/common/methods/BaseWasmGenerationVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/common/methods/BaseWasmGenerationVisitor.java @@ -116,6 +116,7 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp private static final int SWITCH_TABLE_THRESHOLD = 256; private BaseWasmGenerationContext context; + protected final MethodReference currentMethod; protected final WasmTypeInference typeInference; protected final WasmFunction function; private int firstVariable; @@ -131,9 +132,10 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp protected WasmExpression result; protected List resultConsumer; - public BaseWasmGenerationVisitor(BaseWasmGenerationContext context, WasmFunction function, - int firstVariable, boolean async) { + public BaseWasmGenerationVisitor(BaseWasmGenerationContext context, MethodReference currentMethod, + WasmFunction function, int firstVariable, boolean async) { this.context = context; + this.currentMethod = currentMethod; this.function = function; this.firstVariable = firstVariable; tempVars = new TemporaryVariablePool(function); @@ -833,6 +835,10 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp protected abstract CallSiteIdentifier generateCallSiteId(TextLocation location); + protected void acceptWithType(Expr expr, ValueType type) { + accept(expr); + } + protected WasmExpression generateInvocation(InvocationExpr expr, CallSiteIdentifier callSiteId) { if (expr.getType() == InvocationType.STATIC || expr.getType() == InvocationType.SPECIAL) { var method = context.classes().resolve(expr.getMethod()); @@ -842,8 +848,13 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp : context.functions().forInstanceMethod(reference); var call = new WasmCall(function); - for (var argument : expr.getArguments()) { - accept(argument); + var arguments = expr.getArguments(); + 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); } if (expr.getType() == InvocationType.SPECIAL) { @@ -866,8 +877,12 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp var function = context.functions().forInstanceMethod(expr.getMethod()); var call = new WasmCall(function); call.getArguments().add(new WasmGetLocal(tmp)); - for (var argument : expr.getArguments()) { - accept(argument); + var arguments = expr.getArguments(); + 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); } if (callSiteId != null) { @@ -881,7 +896,7 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp return block; } else { var reference = expr.getMethod(); - accept(expr.getArguments().get(0)); + acceptWithType(expr.getArguments().get(0), ValueType.object(expr.getMethod().getClassName())); var instance = result; var block = new WasmBlock(false); block.setType(mapType(reference.getReturnType())); @@ -893,7 +908,7 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp var arguments = new ArrayList(); arguments.add(instance); 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); } if (callSiteId != null) { @@ -1091,7 +1106,7 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp @Override public void visit(ReturnStatement statement) { if (statement.getResult() != null) { - accept(statement.getResult()); + acceptWithType(statement.getResult(), currentMethod.getReturnType()); } else { result = null; } @@ -1102,7 +1117,7 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp @Override public void visit(InstanceOfExpr expr) { - accept(expr.getExpr()); + acceptWithType(expr.getExpr(), expr.getType()); var block = new WasmBlock(false); block.setType(WasmType.INT32); @@ -1143,7 +1158,7 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp block.setType(wasmTargetType); block.setLocation(expr.getLocation()); - accept(expr.getValue()); + acceptWithType(expr.getValue(), expr.getTarget()); result.acceptVisitor(typeInference); var wasmSourceType = typeInference.getResult(); var valueToCast = exprCache.create(result, wasmSourceType, expr.getLocation(), block.getBody()); diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassGenerator.java index 23e4debe7..4fc5b5d21 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassGenerator.java @@ -20,10 +20,12 @@ import com.carrotsearch.hppc.ObjectIntMap; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Queue; +import java.util.Set; import java.util.function.Consumer; import org.teavm.backend.lowlevel.generate.NameProvider; import org.teavm.backend.wasm.BaseWasmFunctionRepository; @@ -182,6 +184,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit classInfo = new WasmGCClassInfo(type); classInfoQueue.add(classInfo); classInfoMap.put(type, classInfo); + VirtualTable virtualTable = null; if (!(type instanceof ValueType.Primitive)) { var name = type instanceof ValueType.Object ? ((ValueType.Object) type).getClassName() @@ -189,6 +192,10 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit classInfo.structure = new WasmStructure(name != null ? names.forClass(name) : null); if (name != null) { 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) { classInfo.structure.setSupertype(getClassInfo(classReader.getParent()).structure); } @@ -199,7 +206,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit fillFields(classInfo, type); } var pointerName = names.forClassInstance(type); - var classStructure = type instanceof ValueType.Object + var classStructure = virtualTable != null && virtualTable.hasValidEntries() ? initRegularClassStructure(((ValueType.Object) type).getClassName()) : standardClasses.classClass().getStructure(); classInfo.virtualTableStructure = classStructure; @@ -213,7 +220,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit } else if (type instanceof ValueType.Array) { initArrayClass(classInfo, (ValueType.Array) type); } else if (type instanceof ValueType.Object) { - initRegularClass(classInfo, classStructure, ((ValueType.Object) type).getClassName()); + initRegularClass(classInfo, virtualTable, classStructure, ((ValueType.Object) type).getClassName()); } } 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); if (classInitializerInfo.isDynamicInitializer(name)) { if (cls != null && cls.getMethod(CLINIT_METHOD_DESC) != null) { @@ -290,35 +298,33 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit var ranges = tagRegistry.getRanges(name); int tag = ranges.stream().mapToInt(range -> range.lower).min().orElse(0); target.add(setClassField(classInfo, classTagOffset, new WasmInt32Constant(tag))); - var metadataReg = metadataRequirements.getInfo(name); - if (metadataReg.name()) { + var metadataReq = metadataRequirements.getInfo(name); + if (metadataReq.name()) { var namePtr = strings.getStringConstant(name).global; target.add(setClassField(classInfo, classNameOffset, new WasmGetGlobal(namePtr))); } if (cls != null) { - if (metadataReg.simpleName() && cls.getSimpleName() != null) { + if (metadataReq.simpleName() && cls.getSimpleName() != null) { var namePtr = strings.getStringConstant(cls.getSimpleName()).global; target.add(setClassField(classInfo, classNameOffset, new WasmGetGlobal(namePtr))); } - if (cls.getParent() != null) { + if (cls.getParent() != null && metadataReq.superclass()) { var parent = getClassInfo(cls.getParent()); target.add(setClassField(classInfo, classParentOffset, new WasmGetGlobal(parent.pointer))); } } - var virtualTable = virtualTables.lookup(name); - fillVirtualTableMethods(target, classStructure, classInfo.pointer, virtualTable, virtualTableFieldOffset, - name); + if (virtualTable != null && virtualTable.hasValidEntries()) { + fillVirtualTableMethods(target, classStructure, classInfo.pointer, virtualTable, + virtualTableFieldOffset, name, new HashSet<>()); + } }; } private int fillVirtualTableMethods(List target, WasmStructure structure, WasmGlobal global, - VirtualTable virtualTable, int index, String origin) { - if (virtualTable.getParent() != null) { - index = fillVirtualTableMethods(target, structure, global, virtualTable.getParent(), index, origin); - } + VirtualTable virtualTable, int index, String origin, Set filled) { for (var method : virtualTable.getMethods()) { 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()); if (!origin.equals(entry.getImplementor().getClassName())) { 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)); } } + if (virtualTable.getParent() != null) { + index = fillVirtualTableMethods(target, structure, global, virtualTable.getParent(), index, origin, + filled); + } return index; } diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationVisitor.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationVisitor.java index 368f6d7ae..b0d47a6b2 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationVisitor.java @@ -62,14 +62,32 @@ import org.teavm.model.ValueType; public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor { private WasmGCGenerationContext context; private WasmGCGenerationUtil generationUtil; + private WasmType expectedType; - public WasmGCGenerationVisitor(WasmGCGenerationContext context, WasmFunction function, - int firstVariable, boolean async) { - super(context, function, firstVariable, async); + public WasmGCGenerationVisitor(WasmGCGenerationContext context, MethodReference currentMethod, + WasmFunction function, int firstVariable, boolean async) { + super(context, currentMethod, function, firstVariable, async); this.context = context; 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 protected boolean isManaged() { return false; @@ -137,24 +155,23 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor { @Override protected void storeField(Expr qualified, FieldReference field, Expr value, TextLocation location) { if (qualified == null) { - accept(value); - var wasmValue = result; var global = context.classInfoProvider().getStaticFieldLocation(field); + accept(value, global.getType()); + var wasmValue = result; var result = new WasmSetGlobal(global, wasmValue); result.setLocation(location); resultConsumer.add(result); } else { - accept(qualified); + acceptWithType(qualified, ValueType.object(field.getClassName())); var target = result; - accept(value); - var wasmValue = result; - target.acceptVisitor(typeInference); var type = (WasmType.CompositeReference) typeInference.getResult(); var struct = (WasmStructure) type.composite; - var fieldIndex = context.classInfoProvider().getFieldIndex(field); + accept(value, struct.getFields().get(fieldIndex).asUnpackedType()); + var wasmValue = result; + var expr = new WasmStructSet(struct, target, fieldIndex, wasmValue); expr.setLocation(location); resultConsumer.add(expr); @@ -175,7 +192,9 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor { @Override protected WasmExpression nullLiteral() { - return new WasmNullConstant(WasmType.Reference.STRUCT); + return new WasmNullConstant(expectedType instanceof WasmType.Reference + ? (WasmType.Reference) expectedType + : WasmType.Reference.STRUCT); } @Override @@ -404,7 +423,7 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor { result = new WasmGetGlobal(global); result.setLocation(expr.getLocation()); } else { - accept(expr.getQualified()); + acceptWithType(expr.getQualified(), ValueType.object(expr.getField().getClassName())); var target = result; target.acceptVisitor(typeInference); diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCMethodGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCMethodGenerator.java index afac89c28..084b6c894 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCMethodGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCMethodGenerator.java @@ -249,7 +249,8 @@ public class WasmGCMethodGenerator implements BaseWasmFunctionRepository { } 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()); } diff --git a/core/src/main/java/org/teavm/model/analysis/BaseTypeInference.java b/core/src/main/java/org/teavm/model/analysis/BaseTypeInference.java index b9f0cabfe..b127b8661 100644 --- a/core/src/main/java/org/teavm/model/analysis/BaseTypeInference.java +++ b/core/src/main/java/org/teavm/model/analysis/BaseTypeInference.java @@ -17,6 +17,7 @@ package org.teavm.model.analysis; import com.carrotsearch.hppc.IntStack; import java.util.ArrayDeque; +import java.util.Deque; import java.util.Objects; import org.teavm.common.Graph; 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.ConstructMultiArrayInstruction; import org.teavm.model.instructions.DoubleConstantInstruction; +import org.teavm.model.instructions.ExitInstruction; import org.teavm.model.instructions.FloatConstantInstruction; import org.teavm.model.instructions.GetElementInstruction; 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.NullConstantInstruction; import org.teavm.model.instructions.NumericOperandType; +import org.teavm.model.instructions.PutFieldInstruction; import org.teavm.model.instructions.StringConstantInstruction; import org.teavm.model.instructions.UnwrapArrayInstruction; @@ -62,6 +65,7 @@ public abstract class BaseTypeInference { private Graph arrayGraph; private Graph arrayUnwrapGraph; private boolean phisSkipped; + private boolean backPropagation; public BaseTypeInference(Program program, MethodReference reference) { this.program = program; @@ -72,6 +76,10 @@ public abstract class BaseTypeInference { this.phisSkipped = phisSkipped; } + public void setBackPropagation(boolean backPropagation) { + this.backPropagation = backPropagation; + } + private void prepare() { types = new Object[program.variableCount()]; var visitor = new InitialTypeVisitor(program.variableCount()); @@ -153,10 +161,80 @@ public abstract class BaseTypeInference { } } + 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(); + 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() { if (types == null) { prepare(); propagate(); + propagateBack(); } } @@ -185,6 +263,10 @@ public abstract class BaseTypeInference { protected abstract T elementType(T t); + protected T arrayType(T t) { + throw new UnsupportedOperationException(); + } + protected T methodReturnType(InvocationType invocationType, MethodReference methodRef) { return mapType(methodRef.getReturnType()); } @@ -380,4 +462,55 @@ public abstract class BaseTypeInference { } } } + + private class BackPropagationVisitor extends AbstractInstructionVisitor { + private boolean[] nullTypes; + private IntStack stack; + private Deque typeStack; + + BackPropagationVisitor(boolean[] nullTypes, IntStack stack, Deque 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)); + } + } + } } diff --git a/core/src/main/java/org/teavm/model/analysis/ClassMetadataRequirements.java b/core/src/main/java/org/teavm/model/analysis/ClassMetadataRequirements.java index c3742d3f4..a5e56b260 100644 --- a/core/src/main/java/org/teavm/model/analysis/ClassMetadataRequirements.java +++ b/core/src/main/java/org/teavm/model/analysis/ClassMetadataRequirements.java @@ -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_SIMPLE_NAME_METHOD = new MethodReference(Class.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, "getDeclaringClass", 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); if (getDeclaringClassMethod != null) { String[] classNames = getDeclaringClassMethod.getVariable(0).getClassValueNode().getTypes(); @@ -95,6 +105,7 @@ public class ClassMetadataRequirements { boolean simpleName; boolean declaringClass; boolean enclosingClass; + boolean superclass; @Override public boolean name() { @@ -115,6 +126,11 @@ public class ClassMetadataRequirements { public boolean enclosingClass() { return enclosingClass; } + + @Override + public boolean superclass() { + return superclass; + } } public interface Info { @@ -125,5 +141,7 @@ public class ClassMetadataRequirements { boolean declaringClass(); boolean enclosingClass(); + + boolean superclass(); } } diff --git a/core/src/main/java/org/teavm/model/classes/VirtualTable.java b/core/src/main/java/org/teavm/model/classes/VirtualTable.java index 5d222dbab..362ded592 100644 --- a/core/src/main/java/org/teavm/model/classes/VirtualTable.java +++ b/core/src/main/java/org/teavm/model/classes/VirtualTable.java @@ -26,6 +26,8 @@ public class VirtualTable { private List methods; private Set methodSet; private Map entryMap; + private boolean hasValidEntries; + private boolean hasValidEntriesComputed; VirtualTable(String className, VirtualTable parent, List methods, Set methodSet, Map entryMap) { @@ -70,4 +72,23 @@ public class VirtualTable { public int size() { 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; + } }