mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2025-01-05 06:34:11 -08:00
Implement null check in C backend
Although initial purpose of this comment was null check, it took much time to complete it and it caused many unrelated changes. Besides just implementing null check in quite naive fashion (I could not use the trick with memory protection, since I have to maintain shadow stack, and support WebAssembly), I had to optimize things. I relied on my existing nullness analysis to eliminate as much null checks as possible. However, the whole nullness analysis was wrong. After some thoughts I came up with solution very close to range analysis, which required me to introduce extension to IR sometimes called e-SSA form with so called sigma nodes. Also, I found some bugs in few different places (by the time write this message I could only remember escape analysis/scalar replacement and after-inlining devirtualization) and fixed them.
This commit is contained in:
parent
744eb39e6d
commit
e77997c93f
|
@ -40,7 +40,7 @@ import org.teavm.model.instructions.IntegerConstantInstruction;
|
||||||
import org.teavm.model.instructions.InvokeInstruction;
|
import org.teavm.model.instructions.InvokeInstruction;
|
||||||
import org.teavm.model.optimization.ConstantConditionElimination;
|
import org.teavm.model.optimization.ConstantConditionElimination;
|
||||||
import org.teavm.model.optimization.GlobalValueNumbering;
|
import org.teavm.model.optimization.GlobalValueNumbering;
|
||||||
import org.teavm.model.optimization.UnreachableBasicBlockElimination;
|
import org.teavm.model.optimization.UnreachableBasicBlockEliminator;
|
||||||
|
|
||||||
public class PlatformMarkerSupport implements ClassHolderTransformer {
|
public class PlatformMarkerSupport implements ClassHolderTransformer {
|
||||||
private String[] tags;
|
private String[] tags;
|
||||||
|
@ -124,8 +124,8 @@ public class PlatformMarkerSupport implements ClassHolderTransformer {
|
||||||
boolean changed;
|
boolean changed;
|
||||||
do {
|
do {
|
||||||
changed = new GlobalValueNumbering(true).optimize(program)
|
changed = new GlobalValueNumbering(true).optimize(program)
|
||||||
| new ConstantConditionElimination().optimize(null, program)
|
| new ConstantConditionElimination().optimize(containingMethod.getDescriptor(), program);
|
||||||
| new UnreachableBasicBlockElimination().optimize(null, program);
|
new UnreachableBasicBlockEliminator().optimize(program);
|
||||||
} while (changed);
|
} while (changed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ package org.teavm.ast.decompilation;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import org.teavm.ast.ArrayType;
|
import org.teavm.ast.ArrayType;
|
||||||
|
@ -325,7 +325,7 @@ class StatementGenerator implements InstructionVisitor {
|
||||||
SwitchStatement stmt = new SwitchStatement();
|
SwitchStatement stmt = new SwitchStatement();
|
||||||
stmt.setId("sblock" + (lastSwitchId++));
|
stmt.setId("sblock" + (lastSwitchId++));
|
||||||
stmt.setValue(Expr.var(insn.getCondition().getIndex()));
|
stmt.setValue(Expr.var(insn.getCondition().getIndex()));
|
||||||
Map<Integer, List<Integer>> switchMap = new HashMap<>();
|
Map<Integer, List<Integer>> switchMap = new LinkedHashMap<>();
|
||||||
for (int i = 0; i < insn.getEntries().size(); ++i) {
|
for (int i = 0; i < insn.getEntries().size(); ++i) {
|
||||||
SwitchTableEntry entry = insn.getEntries().get(i);
|
SwitchTableEntry entry = insn.getEntries().get(i);
|
||||||
List<Integer> conditions = switchMap.computeIfAbsent(entry.getTarget().getIndex(), k -> new ArrayList<>());
|
List<Integer> conditions = switchMap.computeIfAbsent(entry.getTarget().getIndex(), k -> new ArrayList<>());
|
||||||
|
|
|
@ -15,8 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.ast.optimization;
|
package org.teavm.ast.optimization;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.HashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -41,8 +41,8 @@ import org.teavm.ast.TryCatchStatement;
|
||||||
import org.teavm.ast.WhileStatement;
|
import org.teavm.ast.WhileStatement;
|
||||||
|
|
||||||
class BreakEliminator implements StatementVisitor {
|
class BreakEliminator implements StatementVisitor {
|
||||||
private Map<BlockStatement, List<Statement>> blockSuccessors = new HashMap<>();
|
private Map<BlockStatement, List<Statement>> blockSuccessors = new LinkedHashMap<>();
|
||||||
private Set<IdentifiedStatement> outerStatements = new HashSet<>();
|
private Set<IdentifiedStatement> outerStatements = new LinkedHashSet<>();
|
||||||
private List<Statement> currentSequence;
|
private List<Statement> currentSequence;
|
||||||
private int currentIndex;
|
private int currentIndex;
|
||||||
private AllBlocksCountVisitor usageCounter;
|
private AllBlocksCountVisitor usageCounter;
|
||||||
|
@ -153,8 +153,8 @@ class BreakEliminator implements StatementVisitor {
|
||||||
public void visit(TryCatchStatement statement) {
|
public void visit(TryCatchStatement statement) {
|
||||||
Map<BlockStatement, List<Statement>> oldBlockSuccessors = blockSuccessors;
|
Map<BlockStatement, List<Statement>> oldBlockSuccessors = blockSuccessors;
|
||||||
Set<IdentifiedStatement> oldOuterStatements = outerStatements;
|
Set<IdentifiedStatement> oldOuterStatements = outerStatements;
|
||||||
outerStatements = new HashSet<>();
|
outerStatements = new LinkedHashSet<>();
|
||||||
blockSuccessors = new HashMap<>();
|
blockSuccessors = new LinkedHashMap<>();
|
||||||
processSequence(statement.getProtectedBody());
|
processSequence(statement.getProtectedBody());
|
||||||
outerStatements = oldOuterStatements;
|
outerStatements = oldOuterStatements;
|
||||||
blockSuccessors = oldBlockSuccessors;
|
blockSuccessors = oldBlockSuccessors;
|
||||||
|
|
|
@ -28,11 +28,11 @@ import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Deque;
|
import java.util.Deque;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import org.teavm.ast.decompilation.Decompiler;
|
import org.teavm.ast.decompilation.Decompiler;
|
||||||
import org.teavm.backend.c.analyze.CDependencyListener;
|
import org.teavm.backend.c.analyze.CDependencyListener;
|
||||||
import org.teavm.backend.c.analyze.Characteristics;
|
|
||||||
import org.teavm.backend.c.generate.BufferedCodeWriter;
|
import org.teavm.backend.c.generate.BufferedCodeWriter;
|
||||||
import org.teavm.backend.c.generate.ClassGenerator;
|
import org.teavm.backend.c.generate.ClassGenerator;
|
||||||
import org.teavm.backend.c.generate.CodeWriter;
|
import org.teavm.backend.c.generate.CodeWriter;
|
||||||
|
@ -79,8 +79,11 @@ import org.teavm.model.classes.VirtualTableProvider;
|
||||||
import org.teavm.model.instructions.CloneArrayInstruction;
|
import org.teavm.model.instructions.CloneArrayInstruction;
|
||||||
import org.teavm.model.instructions.InvocationType;
|
import org.teavm.model.instructions.InvocationType;
|
||||||
import org.teavm.model.instructions.InvokeInstruction;
|
import org.teavm.model.instructions.InvokeInstruction;
|
||||||
|
import org.teavm.model.lowlevel.Characteristics;
|
||||||
import org.teavm.model.lowlevel.ClassInitializerEliminator;
|
import org.teavm.model.lowlevel.ClassInitializerEliminator;
|
||||||
import org.teavm.model.lowlevel.ClassInitializerTransformer;
|
import org.teavm.model.lowlevel.ClassInitializerTransformer;
|
||||||
|
import org.teavm.model.lowlevel.NullCheckInsertion;
|
||||||
|
import org.teavm.model.lowlevel.NullCheckTransformation;
|
||||||
import org.teavm.model.lowlevel.ShadowStackTransformer;
|
import org.teavm.model.lowlevel.ShadowStackTransformer;
|
||||||
import org.teavm.model.transformation.ClassInitializerInsertionTransformer;
|
import org.teavm.model.transformation.ClassInitializerInsertionTransformer;
|
||||||
import org.teavm.model.transformation.ClassPatch;
|
import org.teavm.model.transformation.ClassPatch;
|
||||||
|
@ -101,6 +104,8 @@ public class CTarget implements TeaVMTarget {
|
||||||
private ClassInitializerEliminator classInitializerEliminator;
|
private ClassInitializerEliminator classInitializerEliminator;
|
||||||
private ClassInitializerTransformer classInitializerTransformer;
|
private ClassInitializerTransformer classInitializerTransformer;
|
||||||
private ShadowStackTransformer shadowStackTransformer;
|
private ShadowStackTransformer shadowStackTransformer;
|
||||||
|
private NullCheckInsertion nullCheckInsertion;
|
||||||
|
private NullCheckTransformation nullCheckTransformation;
|
||||||
private int minHeapSize = 32 * 1024 * 1024;
|
private int minHeapSize = 32 * 1024 * 1024;
|
||||||
|
|
||||||
public void setMinHeapSize(int minHeapSize) {
|
public void setMinHeapSize(int minHeapSize) {
|
||||||
|
@ -123,10 +128,13 @@ public class CTarget implements TeaVMTarget {
|
||||||
@Override
|
@Override
|
||||||
public void setController(TeaVMTargetController controller) {
|
public void setController(TeaVMTargetController controller) {
|
||||||
this.controller = controller;
|
this.controller = controller;
|
||||||
|
Characteristics characteristics = new Characteristics(controller.getUnprocessedClassSource());
|
||||||
classInitializerEliminator = new ClassInitializerEliminator(controller.getUnprocessedClassSource());
|
classInitializerEliminator = new ClassInitializerEliminator(controller.getUnprocessedClassSource());
|
||||||
classInitializerTransformer = new ClassInitializerTransformer();
|
classInitializerTransformer = new ClassInitializerTransformer();
|
||||||
shadowStackTransformer = new ShadowStackTransformer(controller.getUnprocessedClassSource());
|
shadowStackTransformer = new ShadowStackTransformer(characteristics);
|
||||||
clinitInsertionTransformer = new ClassInitializerInsertionTransformer(controller.getUnprocessedClassSource());
|
clinitInsertionTransformer = new ClassInitializerInsertionTransformer(controller.getUnprocessedClassSource());
|
||||||
|
nullCheckInsertion = new NullCheckInsertion(characteristics);
|
||||||
|
nullCheckTransformation = new NullCheckTransformation();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -154,6 +162,8 @@ public class CTarget implements TeaVMTarget {
|
||||||
Throwable.class, void.class), null).use();
|
Throwable.class, void.class), null).use();
|
||||||
dependencyAnalyzer.linkMethod(new MethodReference(ExceptionHandling.class, "throwClassCastException",
|
dependencyAnalyzer.linkMethod(new MethodReference(ExceptionHandling.class, "throwClassCastException",
|
||||||
void.class), null).use();
|
void.class), null).use();
|
||||||
|
dependencyAnalyzer.linkMethod(new MethodReference(ExceptionHandling.class, "throwNullPointerException",
|
||||||
|
void.class), null).use();
|
||||||
|
|
||||||
dependencyAnalyzer.linkMethod(new MethodReference(ExceptionHandling.class, "catchException",
|
dependencyAnalyzer.linkMethod(new MethodReference(ExceptionHandling.class, "catchException",
|
||||||
Throwable.class), null).use();
|
Throwable.class), null).use();
|
||||||
|
@ -171,11 +181,17 @@ public class CTarget implements TeaVMTarget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void beforeOptimizations(Program program, MethodReader method, ListableClassReaderSource classSource) {
|
||||||
|
nullCheckInsertion.transformProgram(program, method.getReference());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void afterOptimizations(Program program, MethodReader method, ListableClassReaderSource classSource) {
|
public void afterOptimizations(Program program, MethodReader method, ListableClassReaderSource classSource) {
|
||||||
clinitInsertionTransformer.apply(method, program);
|
clinitInsertionTransformer.apply(method, program);
|
||||||
classInitializerEliminator.apply(program);
|
classInitializerEliminator.apply(program);
|
||||||
classInitializerTransformer.transform(program);
|
classInitializerTransformer.transform(program);
|
||||||
|
nullCheckTransformation.apply(program, method.getResultType());
|
||||||
shadowStackTransformer.apply(program, method);
|
shadowStackTransformer.apply(program, method);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -289,7 +305,7 @@ public class CTarget implements TeaVMTarget {
|
||||||
}
|
}
|
||||||
|
|
||||||
private VirtualTableProvider createVirtualTableProvider(ListableClassHolderSource classes) {
|
private VirtualTableProvider createVirtualTableProvider(ListableClassHolderSource classes) {
|
||||||
Set<MethodReference> virtualMethods = new HashSet<>();
|
Set<MethodReference> virtualMethods = new LinkedHashSet<>();
|
||||||
|
|
||||||
for (String className : classes.getClassNames()) {
|
for (String className : classes.getClassNames()) {
|
||||||
ClassHolder cls = classes.get(className);
|
ClassHolder cls = classes.get(className);
|
||||||
|
|
|
@ -24,7 +24,6 @@ import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import org.teavm.ast.RegularMethodNode;
|
import org.teavm.ast.RegularMethodNode;
|
||||||
import org.teavm.ast.decompilation.Decompiler;
|
import org.teavm.ast.decompilation.Decompiler;
|
||||||
import org.teavm.backend.c.analyze.Characteristics;
|
|
||||||
import org.teavm.backend.c.generators.Generator;
|
import org.teavm.backend.c.generators.Generator;
|
||||||
import org.teavm.backend.c.generators.GeneratorContext;
|
import org.teavm.backend.c.generators.GeneratorContext;
|
||||||
import org.teavm.diagnostics.Diagnostics;
|
import org.teavm.diagnostics.Diagnostics;
|
||||||
|
@ -45,6 +44,7 @@ import org.teavm.model.ValueType;
|
||||||
import org.teavm.model.classes.TagRegistry;
|
import org.teavm.model.classes.TagRegistry;
|
||||||
import org.teavm.model.classes.VirtualTable;
|
import org.teavm.model.classes.VirtualTable;
|
||||||
import org.teavm.model.classes.VirtualTableEntry;
|
import org.teavm.model.classes.VirtualTableEntry;
|
||||||
|
import org.teavm.model.lowlevel.Characteristics;
|
||||||
import org.teavm.model.lowlevel.ShadowStackTransformer;
|
import org.teavm.model.lowlevel.ShadowStackTransformer;
|
||||||
import org.teavm.runtime.RuntimeClass;
|
import org.teavm.runtime.RuntimeClass;
|
||||||
import org.teavm.runtime.RuntimeObject;
|
import org.teavm.runtime.RuntimeObject;
|
||||||
|
|
|
@ -225,9 +225,7 @@ public class CodeGenerationVisitor implements ExprVisitor, StatementVisitor {
|
||||||
writer.print(")");
|
writer.print(")");
|
||||||
break;
|
break;
|
||||||
case NULL_CHECK:
|
case NULL_CHECK:
|
||||||
writer.print("(");
|
|
||||||
expr.getOperand().acceptVisitor(this);
|
expr.getOperand().acceptVisitor(this);
|
||||||
writer.print(" == NULL)");
|
|
||||||
break;
|
break;
|
||||||
case INT_TO_BYTE:
|
case INT_TO_BYTE:
|
||||||
writer.print("TO_BYTE(");
|
writer.print("TO_BYTE(");
|
||||||
|
|
|
@ -92,6 +92,9 @@ public class CodeGenerator {
|
||||||
int start = methodNode.getReference().parameterCount() + 1;
|
int start = methodNode.getReference().parameterCount() + 1;
|
||||||
for (int i = start; i < methodNode.getVariables().size(); ++i) {
|
for (int i = start; i < methodNode.getVariables().size(); ++i) {
|
||||||
VariableNode variableNode = methodNode.getVariables().get(i);
|
VariableNode variableNode = methodNode.getVariables().get(i);
|
||||||
|
if (variableNode.getType() == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
localsWriter.printType(variableNode.getType()).print(" local_").print(String.valueOf(i)).println(";");
|
localsWriter.printType(variableNode.getType()).print(" local_").print(String.valueOf(i)).println(";");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,13 +19,13 @@ import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import org.teavm.backend.c.analyze.Characteristics;
|
|
||||||
import org.teavm.backend.c.generators.Generator;
|
import org.teavm.backend.c.generators.Generator;
|
||||||
import org.teavm.backend.c.intrinsic.Intrinsic;
|
import org.teavm.backend.c.intrinsic.Intrinsic;
|
||||||
import org.teavm.diagnostics.Diagnostics;
|
import org.teavm.diagnostics.Diagnostics;
|
||||||
import org.teavm.model.ClassReaderSource;
|
import org.teavm.model.ClassReaderSource;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
import org.teavm.model.classes.VirtualTableProvider;
|
import org.teavm.model.classes.VirtualTableProvider;
|
||||||
|
import org.teavm.model.lowlevel.Characteristics;
|
||||||
|
|
||||||
public class GenerationContext {
|
public class GenerationContext {
|
||||||
private VirtualTableProvider virtualTableProvider;
|
private VirtualTableProvider virtualTableProvider;
|
||||||
|
|
|
@ -168,7 +168,9 @@ public class AddressIntrinsic implements Intrinsic {
|
||||||
String className = StructureIntrinsic.getClassLiteral(context, invocation,
|
String className = StructureIntrinsic.getClassLiteral(context, invocation,
|
||||||
invocation.getArguments().get(1));
|
invocation.getArguments().get(1));
|
||||||
context.emit(invocation.getArguments().get(2));
|
context.emit(invocation.getArguments().get(2));
|
||||||
context.writer().print(" * sizeof(").print(context.names().forClass(className)).print(")");
|
context.writer().print(" * sizeof(")
|
||||||
|
.print(className != null ? context.names().forClass(className) : "**")
|
||||||
|
.print(")");
|
||||||
context.writer().print(")");
|
context.writer().print(")");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -16,8 +16,8 @@
|
||||||
package org.teavm.backend.c.intrinsic;
|
package org.teavm.backend.c.intrinsic;
|
||||||
|
|
||||||
import org.teavm.ast.InvocationExpr;
|
import org.teavm.ast.InvocationExpr;
|
||||||
import org.teavm.backend.c.analyze.Characteristics;
|
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
|
import org.teavm.model.lowlevel.Characteristics;
|
||||||
|
|
||||||
public class FunctionIntrinsic implements Intrinsic {
|
public class FunctionIntrinsic implements Intrinsic {
|
||||||
private Characteristics characteristics;
|
private Characteristics characteristics;
|
||||||
|
|
|
@ -18,11 +18,11 @@ package org.teavm.backend.c.intrinsic;
|
||||||
import org.teavm.ast.ConstantExpr;
|
import org.teavm.ast.ConstantExpr;
|
||||||
import org.teavm.ast.Expr;
|
import org.teavm.ast.Expr;
|
||||||
import org.teavm.ast.InvocationExpr;
|
import org.teavm.ast.InvocationExpr;
|
||||||
import org.teavm.backend.c.analyze.Characteristics;
|
|
||||||
import org.teavm.interop.Structure;
|
import org.teavm.interop.Structure;
|
||||||
import org.teavm.model.CallLocation;
|
import org.teavm.model.CallLocation;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
import org.teavm.model.ValueType;
|
import org.teavm.model.ValueType;
|
||||||
|
import org.teavm.model.lowlevel.Characteristics;
|
||||||
|
|
||||||
public class StructureIntrinsic implements Intrinsic {
|
public class StructureIntrinsic implements Intrinsic {
|
||||||
private Characteristics characteristics;
|
private Characteristics characteristics;
|
||||||
|
@ -92,6 +92,6 @@ public class StructureIntrinsic implements Intrinsic {
|
||||||
context.getDiagnotics().error(
|
context.getDiagnotics().error(
|
||||||
new CallLocation(context.getCallingMethod(), invocation.getLocation()),
|
new CallLocation(context.getCallingMethod(), invocation.getLocation()),
|
||||||
"This method should take class literal");
|
"This method should take class literal");
|
||||||
return null;
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -230,6 +230,10 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void beforeOptimizations(Program program, MethodReader method, ListableClassReaderSource classSource) {
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void afterOptimizations(Program program, MethodReader method, ListableClassReaderSource classSource) {
|
public void afterOptimizations(Program program, MethodReader method, ListableClassReaderSource classSource) {
|
||||||
clinitInsertionTransformer.apply(method, program);
|
clinitInsertionTransformer.apply(method, program);
|
||||||
|
|
|
@ -214,6 +214,16 @@ public final class Example {
|
||||||
} catch (IllegalStateException e) {
|
} catch (IllegalStateException e) {
|
||||||
System.out.println("Caught 3: " + e.getMessage());
|
System.out.println("Caught 3: " + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Object[] objects = { "a", null };
|
||||||
|
for (Object o : objects) {
|
||||||
|
try {
|
||||||
|
System.out.println(o.toString());
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
System.out.println("Caught NPE");
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void testArrayReflection() {
|
private static void testArrayReflection() {
|
||||||
|
|
|
@ -110,6 +110,7 @@ import org.teavm.model.classes.VirtualTableProvider;
|
||||||
import org.teavm.model.instructions.CloneArrayInstruction;
|
import org.teavm.model.instructions.CloneArrayInstruction;
|
||||||
import org.teavm.model.instructions.InvocationType;
|
import org.teavm.model.instructions.InvocationType;
|
||||||
import org.teavm.model.instructions.InvokeInstruction;
|
import org.teavm.model.instructions.InvokeInstruction;
|
||||||
|
import org.teavm.model.lowlevel.Characteristics;
|
||||||
import org.teavm.model.lowlevel.ClassInitializerEliminator;
|
import org.teavm.model.lowlevel.ClassInitializerEliminator;
|
||||||
import org.teavm.model.lowlevel.ClassInitializerTransformer;
|
import org.teavm.model.lowlevel.ClassInitializerTransformer;
|
||||||
import org.teavm.model.lowlevel.ShadowStackTransformer;
|
import org.teavm.model.lowlevel.ShadowStackTransformer;
|
||||||
|
@ -142,9 +143,11 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
|
||||||
@Override
|
@Override
|
||||||
public void setController(TeaVMTargetController controller) {
|
public void setController(TeaVMTargetController controller) {
|
||||||
this.controller = controller;
|
this.controller = controller;
|
||||||
|
Characteristics managedMethodRepository = new Characteristics(
|
||||||
|
controller.getUnprocessedClassSource());
|
||||||
classInitializerEliminator = new ClassInitializerEliminator(controller.getUnprocessedClassSource());
|
classInitializerEliminator = new ClassInitializerEliminator(controller.getUnprocessedClassSource());
|
||||||
classInitializerTransformer = new ClassInitializerTransformer();
|
classInitializerTransformer = new ClassInitializerTransformer();
|
||||||
shadowStackTransformer = new ShadowStackTransformer(controller.getUnprocessedClassSource());
|
shadowStackTransformer = new ShadowStackTransformer(managedMethodRepository);
|
||||||
clinitInsertionTransformer = new ClassInitializerInsertionTransformer(controller.getUnprocessedClassSource());
|
clinitInsertionTransformer = new ClassInitializerInsertionTransformer(controller.getUnprocessedClassSource());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -273,6 +276,10 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void beforeOptimizations(Program program, MethodReader method, ListableClassReaderSource classSource) {
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void afterOptimizations(Program program, MethodReader method, ListableClassReaderSource classes) {
|
public void afterOptimizations(Program program, MethodReader method, ListableClassReaderSource classes) {
|
||||||
clinitInsertionTransformer.apply(method, program);
|
clinitInsertionTransformer.apply(method, program);
|
||||||
|
|
|
@ -24,8 +24,8 @@ import java.io.Serializable;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -48,7 +48,7 @@ public class DefaultCallGraph implements CallGraph, Serializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
void addFieldAccess(DefaultFieldAccessSite accessSite) {
|
void addFieldAccess(DefaultFieldAccessSite accessSite) {
|
||||||
fieldAccessSites.computeIfAbsent(accessSite.getField(), k -> new HashSet<>()).add(accessSite);
|
fieldAccessSites.computeIfAbsent(accessSite.getField(), k -> new LinkedHashSet<>()).add(accessSite);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writeObject(ObjectOutputStream out) throws IOException {
|
private void writeObject(ObjectOutputStream out) throws IOException {
|
||||||
|
|
|
@ -18,7 +18,7 @@ package org.teavm.callgraph;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import org.teavm.model.FieldReference;
|
import org.teavm.model.FieldReference;
|
||||||
|
@ -28,11 +28,11 @@ import org.teavm.model.TextLocation;
|
||||||
public class DefaultCallGraphNode implements CallGraphNode {
|
public class DefaultCallGraphNode implements CallGraphNode {
|
||||||
private DefaultCallGraph graph;
|
private DefaultCallGraph graph;
|
||||||
private MethodReference method;
|
private MethodReference method;
|
||||||
private Set<DefaultCallSite> callSites = new HashSet<>();
|
private Set<DefaultCallSite> callSites = new LinkedHashSet<>();
|
||||||
private Set<DefaultCallSite> safeCallSites;
|
private Set<DefaultCallSite> safeCallSites;
|
||||||
private List<DefaultCallSite> callerCallSites = new ArrayList<>();
|
private List<DefaultCallSite> callerCallSites = new ArrayList<>();
|
||||||
private List<DefaultCallSite> safeCallersCallSites;
|
private List<DefaultCallSite> safeCallersCallSites;
|
||||||
private Set<DefaultFieldAccessSite> fieldAccessSites = new HashSet<>();
|
private Set<DefaultFieldAccessSite> fieldAccessSites = new LinkedHashSet<>();
|
||||||
private Set<DefaultFieldAccessSite> safeFieldAccessSites;
|
private Set<DefaultFieldAccessSite> safeFieldAccessSites;
|
||||||
|
|
||||||
DefaultCallGraphNode(DefaultCallGraph graph, MethodReference method) {
|
DefaultCallGraphNode(DefaultCallGraph graph, MethodReference method) {
|
||||||
|
|
|
@ -67,6 +67,43 @@ public final class GraphUtils {
|
||||||
return result.getAll();
|
return result.getAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Graph removeLoops(Graph graph) {
|
||||||
|
int sz = graph.size();
|
||||||
|
int[] stack = new int[sz * 2];
|
||||||
|
int stackSize = 0;
|
||||||
|
byte[] state = new byte[sz];
|
||||||
|
for (int i = 0; i < sz; ++i) {
|
||||||
|
if (graph.incomingEdgesCount(i) == 0) {
|
||||||
|
stack[stackSize++] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
GraphBuilder builder = new GraphBuilder(graph.size());
|
||||||
|
while (stackSize > 0) {
|
||||||
|
int node = stack[--stackSize];
|
||||||
|
switch (state[node]) {
|
||||||
|
case NONE:
|
||||||
|
state[node] = VISITING;
|
||||||
|
stack[stackSize++] = node;
|
||||||
|
for (int next : graph.outgoingEdges(node)) {
|
||||||
|
switch (state[next]) {
|
||||||
|
case NONE:
|
||||||
|
stack[stackSize++] = next;
|
||||||
|
builder.addEdge(node, next);
|
||||||
|
break;
|
||||||
|
case VISITED:
|
||||||
|
builder.addEdge(node, next);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case VISITING:
|
||||||
|
state[node] = VISITED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean isIrreducible(Graph graph) {
|
public static boolean isIrreducible(Graph graph) {
|
||||||
DominatorTree dom = buildDominatorTree(graph);
|
DominatorTree dom = buildDominatorTree(graph);
|
||||||
int[] backEdges = findBackEdges(graph);
|
int[] backEdges = findBackEdges(graph);
|
||||||
|
|
|
@ -686,7 +686,9 @@ class DependencyGraphBuilder {
|
||||||
public void nullCheck(VariableReader receiver, VariableReader value) {
|
public void nullCheck(VariableReader receiver, VariableReader value) {
|
||||||
DependencyNode valueNode = nodes[value.getIndex()];
|
DependencyNode valueNode = nodes[value.getIndex()];
|
||||||
DependencyNode receiverNode = nodes[receiver.getIndex()];
|
DependencyNode receiverNode = nodes[receiver.getIndex()];
|
||||||
valueNode.connect(receiverNode);
|
if (valueNode != null) {
|
||||||
|
valueNode.connect(receiverNode);
|
||||||
|
}
|
||||||
dependencyAnalyzer.linkMethod(new MethodReference(NullPointerException.class, "<init>", void.class),
|
dependencyAnalyzer.linkMethod(new MethodReference(NullPointerException.class, "<init>", void.class),
|
||||||
new CallLocation(caller.getMethod(), currentLocation)).use();
|
new CallLocation(caller.getMethod(), currentLocation)).use();
|
||||||
currentExceptionConsumer.consume(dependencyAnalyzer.getType("java.lang.NullPointerException"));
|
currentExceptionConsumer.consume(dependencyAnalyzer.getType("java.lang.NullPointerException"));
|
||||||
|
|
|
@ -15,11 +15,11 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.model;
|
package org.teavm.model;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class AnnotationContainer implements AnnotationContainerReader {
|
public class AnnotationContainer implements AnnotationContainerReader {
|
||||||
private Map<String, AnnotationHolder> annotations = new HashMap<>();
|
private Map<String, AnnotationHolder> annotations = new LinkedHashMap<>();
|
||||||
|
|
||||||
public void add(AnnotationHolder annotation) {
|
public void add(AnnotationHolder annotation) {
|
||||||
if (annotations.containsKey(annotation.getType())) {
|
if (annotations.containsKey(annotation.getType())) {
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.model;
|
package org.teavm.model;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -24,7 +24,7 @@ import java.util.Map;
|
||||||
*/
|
*/
|
||||||
public class AnnotationHolder implements AnnotationReader {
|
public class AnnotationHolder implements AnnotationReader {
|
||||||
private String type;
|
private String type;
|
||||||
private Map<String, AnnotationValue> values = new HashMap<>();
|
private Map<String, AnnotationValue> values = new LinkedHashMap<>();
|
||||||
|
|
||||||
public AnnotationHolder(String type) {
|
public AnnotationHolder(String type) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
|
|
|
@ -15,8 +15,15 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.model;
|
package org.teavm.model;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.AbstractList;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
import java.util.Objects;
|
||||||
import org.teavm.model.instructions.InstructionReader;
|
import org.teavm.model.instructions.InstructionReader;
|
||||||
|
import org.teavm.model.util.TransitionExtractor;
|
||||||
|
|
||||||
public class BasicBlock implements BasicBlockReader, Iterable<Instruction> {
|
public class BasicBlock implements BasicBlockReader, Iterable<Instruction> {
|
||||||
private Program program;
|
private Program program;
|
||||||
|
@ -359,4 +366,32 @@ public class BasicBlock implements BasicBlockReader, Iterable<Instruction> {
|
||||||
public void setLabel(String label) {
|
public void setLabel(String label) {
|
||||||
this.label = label;
|
this.label = label;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void detachSuccessors() {
|
||||||
|
Instruction lastInstruction = getLastInstruction();
|
||||||
|
if (lastInstruction == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TransitionExtractor transitionExtractor = new TransitionExtractor();
|
||||||
|
lastInstruction.acceptVisitor(transitionExtractor);
|
||||||
|
if (transitionExtractor.getTargets() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (BasicBlock successor : transitionExtractor.getTargets()) {
|
||||||
|
List<Phi> phis = successor.getPhis();
|
||||||
|
for (int i = 0; i < phis.size(); i++) {
|
||||||
|
Phi phi = phis.get(i);
|
||||||
|
for (int j = 0; j < phi.getIncomings().size(); ++j) {
|
||||||
|
if (phi.getIncomings().get(j).getSource() == this) {
|
||||||
|
phi.getIncomings().remove(j--);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (phi.getIncomings().isEmpty()) {
|
||||||
|
phis.remove(i--);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
51
core/src/main/java/org/teavm/model/Outgoing.java
Normal file
51
core/src/main/java/org/teavm/model/Outgoing.java
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2018 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.model;
|
||||||
|
|
||||||
|
public class Outgoing {
|
||||||
|
private Sigma sigma;
|
||||||
|
private Variable value;
|
||||||
|
private BasicBlock target;
|
||||||
|
|
||||||
|
public Outgoing(Variable value, BasicBlock target) {
|
||||||
|
this.value = value;
|
||||||
|
this.target = target;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Sigma getSigma() {
|
||||||
|
return sigma;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setSigma(Sigma sigma) {
|
||||||
|
this.sigma = sigma;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Variable getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue(Variable value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BasicBlock getTarget() {
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTarget(BasicBlock target) {
|
||||||
|
this.target = target;
|
||||||
|
}
|
||||||
|
}
|
99
core/src/main/java/org/teavm/model/Sigma.java
Normal file
99
core/src/main/java/org/teavm/model/Sigma.java
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2018 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.model;
|
||||||
|
|
||||||
|
import java.util.AbstractList;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class Sigma {
|
||||||
|
private BasicBlock basicBlock;
|
||||||
|
private Variable value;
|
||||||
|
private List<Outgoing> outgoings = new ArrayList<>();
|
||||||
|
|
||||||
|
public Sigma(BasicBlock basicBlock, Variable value) {
|
||||||
|
this.basicBlock = basicBlock;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BasicBlock getBasicBlock() {
|
||||||
|
return basicBlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBasicBlock(BasicBlock basicBlock) {
|
||||||
|
this.basicBlock = basicBlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Variable getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue(Variable value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Outgoing> getOutgoings() {
|
||||||
|
return safeOutgoings;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Outgoing> safeOutgoings = new AbstractList<Outgoing>() {
|
||||||
|
@Override
|
||||||
|
public Outgoing get(int index) {
|
||||||
|
return outgoings.get(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Outgoing set(int index, Outgoing element) {
|
||||||
|
if (element.getSigma() != null) {
|
||||||
|
throw new IllegalArgumentException("This outgoing is already in some sigma");
|
||||||
|
}
|
||||||
|
element.setSigma(Sigma.this);
|
||||||
|
Outgoing old = outgoings.get(index);
|
||||||
|
old.setSigma(null);
|
||||||
|
outgoings.set(index, element);
|
||||||
|
return old;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(int index, Outgoing element) {
|
||||||
|
if (element.getSigma() != null) {
|
||||||
|
throw new IllegalArgumentException("This outgoing is already in some sigma");
|
||||||
|
}
|
||||||
|
element.setSigma(Sigma.this);
|
||||||
|
outgoings.add(index, element);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Outgoing remove(int index) {
|
||||||
|
Outgoing outgoing = outgoings.remove(index);
|
||||||
|
outgoing.setSigma(null);
|
||||||
|
return outgoing;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
for (Outgoing outgoing : outgoings) {
|
||||||
|
outgoing.setSigma(null);
|
||||||
|
}
|
||||||
|
outgoings.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return outgoings.size();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
|
@ -680,7 +680,7 @@ public class ClassInference {
|
||||||
virtualCallSites.add(callSite);
|
virtualCallSites.add(callSite);
|
||||||
|
|
||||||
if (insn.getReceiver() != null) {
|
if (insn.getReceiver() != null) {
|
||||||
for (int j = 1; j <= MAX_DEGREE; ++j) {
|
for (int j = 0; j <= MAX_DEGREE; ++j) {
|
||||||
getNodeTypes(packNodeAndDegree(callSite.receiver, j));
|
getNodeTypes(packNodeAndDegree(callSite.receiver, j));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,8 +64,8 @@ import org.teavm.model.instructions.RaiseInstruction;
|
||||||
import org.teavm.model.instructions.StringConstantInstruction;
|
import org.teavm.model.instructions.StringConstantInstruction;
|
||||||
import org.teavm.model.instructions.UnwrapArrayInstruction;
|
import org.teavm.model.instructions.UnwrapArrayInstruction;
|
||||||
import org.teavm.model.util.DefinitionExtractor;
|
import org.teavm.model.util.DefinitionExtractor;
|
||||||
import org.teavm.model.util.InstructionTransitionExtractor;
|
|
||||||
import org.teavm.model.util.LivenessAnalyzer;
|
import org.teavm.model.util.LivenessAnalyzer;
|
||||||
|
import org.teavm.model.util.TransitionExtractor;
|
||||||
import org.teavm.model.util.UsageExtractor;
|
import org.teavm.model.util.UsageExtractor;
|
||||||
|
|
||||||
public class EscapeAnalysis {
|
public class EscapeAnalysis {
|
||||||
|
@ -134,6 +134,16 @@ public class EscapeAnalysis {
|
||||||
if (usedVars.get(assign.getAssignee().getIndex())) {
|
if (usedVars.get(assign.getAssignee().getIndex())) {
|
||||||
queue.addLast(definitionClasses[assign.getAssignee().getIndex()]);
|
queue.addLast(definitionClasses[assign.getAssignee().getIndex()]);
|
||||||
}
|
}
|
||||||
|
} else if (insn instanceof NullCheckInstruction) {
|
||||||
|
NullCheckInstruction nullCheck = (NullCheckInstruction) insn;
|
||||||
|
if (usedVars.get(nullCheck.getValue().getIndex())) {
|
||||||
|
queue.addLast(definitionClasses[nullCheck.getValue().getIndex()]);
|
||||||
|
}
|
||||||
|
} else if (insn instanceof UnwrapArrayInstruction) {
|
||||||
|
UnwrapArrayInstruction unwrapArray = (UnwrapArrayInstruction) insn;
|
||||||
|
if (usedVars.get(unwrapArray.getArray().getIndex())) {
|
||||||
|
queue.addLast(definitionClasses[unwrapArray.getArray().getIndex()]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
insn.acceptVisitor(useExtractor);
|
insn.acceptVisitor(useExtractor);
|
||||||
|
@ -180,7 +190,7 @@ public class EscapeAnalysis {
|
||||||
|
|
||||||
private BitSet getUsedVarsInBlock(LivenessAnalyzer liveness, BasicBlock block) {
|
private BitSet getUsedVarsInBlock(LivenessAnalyzer liveness, BasicBlock block) {
|
||||||
BitSet usedVars = new BitSet();
|
BitSet usedVars = new BitSet();
|
||||||
InstructionTransitionExtractor transitionExtractor = new InstructionTransitionExtractor();
|
TransitionExtractor transitionExtractor = new TransitionExtractor();
|
||||||
block.getLastInstruction().acceptVisitor(transitionExtractor);
|
block.getLastInstruction().acceptVisitor(transitionExtractor);
|
||||||
for (BasicBlock successor : transitionExtractor.getTargets()) {
|
for (BasicBlock successor : transitionExtractor.getTargets()) {
|
||||||
usedVars.or(liveness.liveIn(successor.getIndex()));
|
usedVars.or(liveness.liveIn(successor.getIndex()));
|
||||||
|
@ -205,21 +215,36 @@ public class EscapeAnalysis {
|
||||||
|
|
||||||
GraphBuilder graphBuilder = new GraphBuilder(program.variableCount());
|
GraphBuilder graphBuilder = new GraphBuilder(program.variableCount());
|
||||||
for (BasicBlock block : program.getBasicBlocks()) {
|
for (BasicBlock block : program.getBasicBlocks()) {
|
||||||
|
for (Instruction insn : block) {
|
||||||
|
if (insn instanceof AssignInstruction) {
|
||||||
|
AssignInstruction assign = (AssignInstruction) insn;
|
||||||
|
graphBuilder.addEdge(assign.getReceiver().getIndex(), assign.getAssignee().getIndex());
|
||||||
|
} else if (insn instanceof NullCheckInstruction) {
|
||||||
|
NullCheckInstruction nullCheck = (NullCheckInstruction) insn;
|
||||||
|
graphBuilder.addEdge(nullCheck.getReceiver().getIndex(), nullCheck.getValue().getIndex());
|
||||||
|
} else if (insn instanceof UnwrapArrayInstruction) {
|
||||||
|
UnwrapArrayInstruction unwrapArray = (UnwrapArrayInstruction) insn;
|
||||||
|
graphBuilder.addEdge(unwrapArray.getReceiver().getIndex(), unwrapArray.getArray().getIndex());
|
||||||
|
}
|
||||||
|
}
|
||||||
for (Phi phi : block.getPhis()) {
|
for (Phi phi : block.getPhis()) {
|
||||||
for (Incoming incoming : phi.getIncomings()) {
|
for (Incoming incoming : phi.getIncomings()) {
|
||||||
graphBuilder.addEdge(phi.getReceiver().getIndex(), incoming.getValue().getIndex());
|
graphBuilder.addEdge(phi.getReceiver().getIndex(), incoming.getValue().getIndex());
|
||||||
Set<FieldReference> receiverFields = fields.get(phi.getReceiver().getIndex());
|
|
||||||
if (receiverFields != null) {
|
|
||||||
for (FieldReference field : receiverFields) {
|
|
||||||
queue.add(new Task(phi.getReceiver().getIndex(), field));
|
|
||||||
}
|
|
||||||
receiverFields.clear();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Graph graph = graphBuilder.build();
|
Graph graph = graphBuilder.build();
|
||||||
|
|
||||||
|
for (int i = 0; i < program.variableCount(); ++i) {
|
||||||
|
Set<FieldReference> receiverFields = fields.get(i);
|
||||||
|
if (receiverFields != null) {
|
||||||
|
for (FieldReference field : receiverFields) {
|
||||||
|
queue.add(new Task(i, field));
|
||||||
|
}
|
||||||
|
receiverFields.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
while (!queue.isEmpty()) {
|
while (!queue.isEmpty()) {
|
||||||
Task task = queue.remove();
|
Task task = queue.remove();
|
||||||
|
|
||||||
|
@ -263,13 +288,13 @@ public class EscapeAnalysis {
|
||||||
return packedFields;
|
return packedFields;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class InstructionEscapeVisitor extends AbstractInstructionVisitor {
|
static class InstructionEscapeVisitor extends AbstractInstructionVisitor {
|
||||||
DisjointSet definitionClasses;
|
DisjointSet definitionClasses;
|
||||||
boolean[] escapingVars;
|
boolean[] escapingVars;
|
||||||
List<Set<FieldReference>> fields;
|
List<Set<FieldReference>> fields;
|
||||||
Map<FieldReference, ValueType> fieldTypes = new HashMap<>();
|
Map<FieldReference, ValueType> fieldTypes = new HashMap<>();
|
||||||
|
|
||||||
public InstructionEscapeVisitor(int variableCount) {
|
InstructionEscapeVisitor(int variableCount) {
|
||||||
fields = new ArrayList<>(Collections.nCopies(variableCount, null));
|
fields = new ArrayList<>(Collections.nCopies(variableCount, null));
|
||||||
definitionClasses = new DisjointSet();
|
definitionClasses = new DisjointSet();
|
||||||
for (int i = 0; i < variableCount; ++i) {
|
for (int i = 0; i < variableCount; ++i) {
|
||||||
|
|
|
@ -25,6 +25,7 @@ import org.teavm.model.MethodDescriptor;
|
||||||
import org.teavm.model.Phi;
|
import org.teavm.model.Phi;
|
||||||
import org.teavm.model.Program;
|
import org.teavm.model.Program;
|
||||||
import org.teavm.model.Variable;
|
import org.teavm.model.Variable;
|
||||||
|
import org.teavm.model.analysis.NullnessInformationBuilder.Nullness;
|
||||||
import org.teavm.model.util.DefinitionExtractor;
|
import org.teavm.model.util.DefinitionExtractor;
|
||||||
import org.teavm.model.util.InstructionVariableMapper;
|
import org.teavm.model.util.InstructionVariableMapper;
|
||||||
import org.teavm.model.util.PhiUpdater;
|
import org.teavm.model.util.PhiUpdater;
|
||||||
|
@ -33,24 +34,26 @@ public class NullnessInformation {
|
||||||
private Program program;
|
private Program program;
|
||||||
private BitSet synthesizedVariables;
|
private BitSet synthesizedVariables;
|
||||||
private PhiUpdater phiUpdater;
|
private PhiUpdater phiUpdater;
|
||||||
private BitSet notNullVariables;
|
private Nullness[] nullnessArray;
|
||||||
private BitSet nullVariables;
|
|
||||||
|
|
||||||
NullnessInformation(Program program, BitSet synthesizedVariables, PhiUpdater phiUpdater, BitSet notNullVariables,
|
NullnessInformation(Program program, BitSet synthesizedVariables, PhiUpdater phiUpdater,
|
||||||
BitSet nullVariables) {
|
Nullness[] nullnessArray) {
|
||||||
this.program = program;
|
this.program = program;
|
||||||
this.synthesizedVariables = synthesizedVariables;
|
this.synthesizedVariables = synthesizedVariables;
|
||||||
this.phiUpdater = phiUpdater;
|
this.phiUpdater = phiUpdater;
|
||||||
this.notNullVariables = notNullVariables;
|
this.nullnessArray = nullnessArray;
|
||||||
this.nullVariables = nullVariables;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isNotNull(Variable variable) {
|
public boolean isNotNull(Variable variable) {
|
||||||
return notNullVariables.get(variable.getIndex());
|
return nullnessArray[variable.getIndex()] == Nullness.NOT_NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isNull(Variable variable) {
|
public boolean isNull(Variable variable) {
|
||||||
return nullVariables.get(variable.getIndex());
|
return nullnessArray[variable.getIndex()] == Nullness.NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSynthesized(Variable variable) {
|
||||||
|
return synthesizedVariables.get(variable.getIndex());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
|
@ -89,7 +92,6 @@ public class NullnessInformation {
|
||||||
public static NullnessInformation build(Program program, MethodDescriptor methodDescriptor) {
|
public static NullnessInformation build(Program program, MethodDescriptor methodDescriptor) {
|
||||||
NullnessInformationBuilder builder = new NullnessInformationBuilder(program, methodDescriptor);
|
NullnessInformationBuilder builder = new NullnessInformationBuilder(program, methodDescriptor);
|
||||||
builder.build();
|
builder.build();
|
||||||
return new NullnessInformation(program, builder.synthesizedVariables, builder.phiUpdater,
|
return new NullnessInformation(program, builder.synthesizedVariables, builder.phiUpdater, builder.statuses);
|
||||||
builder.notNullVariables, builder.nullVariables);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,11 +18,10 @@ package org.teavm.model.analysis;
|
||||||
import com.carrotsearch.hppc.IntArrayDeque;
|
import com.carrotsearch.hppc.IntArrayDeque;
|
||||||
import com.carrotsearch.hppc.IntDeque;
|
import com.carrotsearch.hppc.IntDeque;
|
||||||
import com.carrotsearch.hppc.IntHashSet;
|
import com.carrotsearch.hppc.IntHashSet;
|
||||||
import com.carrotsearch.hppc.IntIntHashMap;
|
|
||||||
import com.carrotsearch.hppc.IntIntMap;
|
|
||||||
import com.carrotsearch.hppc.IntSet;
|
import com.carrotsearch.hppc.IntSet;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.BitSet;
|
import java.util.BitSet;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import org.teavm.common.Graph;
|
import org.teavm.common.Graph;
|
||||||
import org.teavm.common.GraphBuilder;
|
import org.teavm.common.GraphBuilder;
|
||||||
|
@ -33,6 +32,7 @@ import org.teavm.model.Instruction;
|
||||||
import org.teavm.model.MethodDescriptor;
|
import org.teavm.model.MethodDescriptor;
|
||||||
import org.teavm.model.Phi;
|
import org.teavm.model.Phi;
|
||||||
import org.teavm.model.Program;
|
import org.teavm.model.Program;
|
||||||
|
import org.teavm.model.Sigma;
|
||||||
import org.teavm.model.Variable;
|
import org.teavm.model.Variable;
|
||||||
import org.teavm.model.instructions.AbstractInstructionVisitor;
|
import org.teavm.model.instructions.AbstractInstructionVisitor;
|
||||||
import org.teavm.model.instructions.ArrayLengthInstruction;
|
import org.teavm.model.instructions.ArrayLengthInstruction;
|
||||||
|
@ -60,15 +60,13 @@ import org.teavm.model.util.PhiUpdater;
|
||||||
class NullnessInformationBuilder {
|
class NullnessInformationBuilder {
|
||||||
private Program program;
|
private Program program;
|
||||||
private MethodDescriptor methodDescriptor;
|
private MethodDescriptor methodDescriptor;
|
||||||
BitSet notNullVariables = new BitSet();
|
|
||||||
BitSet nullVariables = new BitSet();
|
|
||||||
BitSet synthesizedVariables = new BitSet();
|
BitSet synthesizedVariables = new BitSet();
|
||||||
PhiUpdater phiUpdater;
|
PhiUpdater phiUpdater;
|
||||||
private List<NullConstantInstruction> nullInstructions = new ArrayList<>();
|
|
||||||
private List<NullCheckInstruction> notNullInstructions = new ArrayList<>();
|
private List<NullCheckInstruction> notNullInstructions = new ArrayList<>();
|
||||||
private Graph assignmentGraph;
|
private Graph assignmentGraph;
|
||||||
private int[] notNullPredecessorsLeft;
|
private int[] notNullPredecessorsLeft;
|
||||||
private int[] sccIndexes;
|
Nullness[] statuses;
|
||||||
|
private int[][] variablePairs;
|
||||||
|
|
||||||
NullnessInformationBuilder(Program program, MethodDescriptor methodDescriptor) {
|
NullnessInformationBuilder(Program program, MethodDescriptor methodDescriptor) {
|
||||||
this.program = program;
|
this.program = program;
|
||||||
|
@ -77,20 +75,65 @@ class NullnessInformationBuilder {
|
||||||
|
|
||||||
void build() {
|
void build() {
|
||||||
extendProgram();
|
extendProgram();
|
||||||
|
buildVariablePairs();
|
||||||
buildAssignmentGraph();
|
buildAssignmentGraph();
|
||||||
propagateNullness();
|
propagateNullness();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void buildVariablePairs() {
|
||||||
|
List<IntSet> pairsBuilder = new ArrayList<>(
|
||||||
|
Collections.nCopies(program.variableCount(), null));
|
||||||
|
|
||||||
|
for (BasicBlock block : program.getBasicBlocks()) {
|
||||||
|
Instruction lastInstruction = block.getLastInstruction();
|
||||||
|
if (lastInstruction instanceof BinaryBranchingInstruction) {
|
||||||
|
BinaryBranchingInstruction branching = (BinaryBranchingInstruction) lastInstruction;
|
||||||
|
addVariablePair(pairsBuilder, branching.getFirstOperand(), branching.getSecondOperand());
|
||||||
|
addVariablePair(pairsBuilder, branching.getSecondOperand(), branching.getFirstOperand());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
variablePairs = new int[pairsBuilder.size()][];
|
||||||
|
for (int i = 0; i < variablePairs.length; ++i) {
|
||||||
|
IntSet itemBuilder = pairsBuilder.get(i);
|
||||||
|
variablePairs[i] = itemBuilder != null ? itemBuilder.toArray() : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addVariablePair(List<IntSet> target, Variable first, Variable second) {
|
||||||
|
IntSet pairs = target.get(first.getIndex());
|
||||||
|
if (pairs == null) {
|
||||||
|
pairs = new IntHashSet();
|
||||||
|
target.set(first.getIndex(), pairs);
|
||||||
|
}
|
||||||
|
pairs.add(second.getIndex());
|
||||||
|
}
|
||||||
|
|
||||||
private void extendProgram() {
|
private void extendProgram() {
|
||||||
notNullVariables.set(0);
|
|
||||||
insertAdditionalVariables();
|
insertAdditionalVariables();
|
||||||
|
|
||||||
Variable[] parameters = new Variable[methodDescriptor.parameterCount() + 1];
|
|
||||||
for (int i = 0; i < parameters.length; ++i) {
|
|
||||||
parameters[i] = program.variableAt(i);
|
|
||||||
}
|
|
||||||
phiUpdater = new PhiUpdater();
|
phiUpdater = new PhiUpdater();
|
||||||
phiUpdater.updatePhis(program, parameters);
|
phiUpdater.setSigmaPredicate(instruction -> {
|
||||||
|
if (instruction instanceof BinaryBranchingInstruction) {
|
||||||
|
switch (((BinaryBranchingInstruction) instruction).getCondition()) {
|
||||||
|
case REFERENCE_EQUAL:
|
||||||
|
case REFERENCE_NOT_EQUAL:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (instruction instanceof BranchingInstruction) {
|
||||||
|
switch (((BranchingInstruction) instruction).getCondition()) {
|
||||||
|
case NULL:
|
||||||
|
case NOT_NULL:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
phiUpdater.updatePhis(program, methodDescriptor.parameterCount() + 1);
|
||||||
|
|
||||||
collectAdditionalVariables();
|
collectAdditionalVariables();
|
||||||
}
|
}
|
||||||
|
@ -102,16 +145,10 @@ class NullnessInformationBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void collectAdditionalVariables() {
|
private void collectAdditionalVariables() {
|
||||||
for (NullConstantInstruction nullInstruction : nullInstructions) {
|
|
||||||
nullVariables.set(nullInstruction.getReceiver().getIndex());
|
|
||||||
synthesizedVariables.set(nullInstruction.getReceiver().getIndex());
|
|
||||||
}
|
|
||||||
for (NullCheckInstruction notNullInstruction : notNullInstructions) {
|
for (NullCheckInstruction notNullInstruction : notNullInstructions) {
|
||||||
notNullVariables.set(notNullInstruction.getReceiver().getIndex());
|
|
||||||
synthesizedVariables.set(notNullInstruction.getReceiver().getIndex());
|
synthesizedVariables.set(notNullInstruction.getReceiver().getIndex());
|
||||||
}
|
}
|
||||||
|
|
||||||
nullInstructions.clear();
|
|
||||||
notNullInstructions.clear();
|
notNullInstructions.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,55 +169,110 @@ class NullnessInformationBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assignmentGraph = builder.build();
|
assignmentGraph = GraphUtils.removeLoops(builder.build());
|
||||||
|
|
||||||
sccIndexes = new int[program.variableCount()];
|
|
||||||
if (assignmentGraph.size() > 0) {
|
|
||||||
int[][] sccs = GraphUtils.findStronglyConnectedComponents(assignmentGraph);
|
|
||||||
for (int i = 0; i < sccs.length; ++i) {
|
|
||||||
for (int sccNode : sccs[i]) {
|
|
||||||
sccIndexes[sccNode] = i + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
notNullPredecessorsLeft = new int[assignmentGraph.size()];
|
notNullPredecessorsLeft = new int[assignmentGraph.size()];
|
||||||
for (int i = 0; i < assignmentGraph.size(); ++i) {
|
for (int i = 0; i < assignmentGraph.size(); ++i) {
|
||||||
notNullPredecessorsLeft[i] = assignmentGraph.incomingEdgesCount(i);
|
notNullPredecessorsLeft[i] = assignmentGraph.incomingEdgesCount(i);
|
||||||
if (sccIndexes[i] > 0) {
|
|
||||||
for (int predecessor : assignmentGraph.outgoingEdges(i)) {
|
|
||||||
if (sccIndexes[predecessor] == sccIndexes[i]) {
|
|
||||||
notNullPredecessorsLeft[i]--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void propagateNullness() {
|
private void initNullness(IntDeque queue) {
|
||||||
if (assignmentGraph.size() == 0) {
|
NullnessInitVisitor visitor = new NullnessInitVisitor(queue);
|
||||||
return;
|
for (BasicBlock block : program.getBasicBlocks()) {
|
||||||
|
for (Instruction instruction : block) {
|
||||||
|
instruction.acceptVisitor(visitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
Instruction last = block.getLastInstruction();
|
||||||
|
if (!(last instanceof BranchingInstruction)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
BranchingInstruction branching = (BranchingInstruction) last;
|
||||||
|
Sigma[] sigmas = phiUpdater.getSigmasAt(block.getIndex());
|
||||||
|
if (sigmas == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Sigma sigma = null;
|
||||||
|
for (int i = 0; i < sigmas.length; ++i) {
|
||||||
|
if (sigmas[i].getValue() == branching.getOperand()) {
|
||||||
|
sigma = sigmas[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (sigma == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Variable trueVar;
|
||||||
|
Variable falseVar;
|
||||||
|
if (sigma.getOutgoings().get(0).getTarget() == branching.getConsequent()) {
|
||||||
|
trueVar = sigma.getOutgoings().get(0).getValue();
|
||||||
|
falseVar = sigma.getOutgoings().get(1).getValue();
|
||||||
|
} else {
|
||||||
|
trueVar = sigma.getOutgoings().get(1).getValue();
|
||||||
|
falseVar = sigma.getOutgoings().get(0).getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (branching.getCondition()) {
|
||||||
|
case NULL:
|
||||||
|
queue.addLast(trueVar.getIndex());
|
||||||
|
queue.addLast(0);
|
||||||
|
queue.addLast(falseVar.getIndex());
|
||||||
|
queue.addLast(1);
|
||||||
|
break;
|
||||||
|
case NOT_NULL:
|
||||||
|
queue.addLast(trueVar.getIndex());
|
||||||
|
queue.addLast(1);
|
||||||
|
queue.addLast(falseVar.getIndex());
|
||||||
|
queue.addLast(0);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
queue.addLast(0);
|
||||||
|
queue.addLast(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void propagateNullness() {
|
||||||
|
statuses = new Nullness[program.variableCount()];
|
||||||
|
|
||||||
IntDeque deque = new IntArrayDeque();
|
IntDeque deque = new IntArrayDeque();
|
||||||
for (int i = notNullVariables.nextSetBit(0); i >= 0; i = notNullVariables.nextSetBit(i + 1)) {
|
initNullness(deque);
|
||||||
deque.addLast(i);
|
|
||||||
}
|
|
||||||
boolean[] visited = new boolean[program.variableCount()];
|
|
||||||
|
|
||||||
while (!deque.isEmpty()) {
|
while (!deque.isEmpty()) {
|
||||||
int node = deque.removeFirst();
|
int node = deque.removeFirst();
|
||||||
if (visited[node]) {
|
if (statuses[node] != null) {
|
||||||
|
deque.removeFirst();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
visited[node] = true;
|
Nullness status = deque.removeFirst() == 1 ? Nullness.NOT_NULL : Nullness.NULL;
|
||||||
notNullVariables.set(node);
|
statuses[node] = status;
|
||||||
for (int successor : assignmentGraph.outgoingEdges(node)) {
|
|
||||||
if (sccIndexes[successor] == 0 || sccIndexes[successor] != sccIndexes[node]) {
|
int[] pairs = variablePairs[node];
|
||||||
if (--notNullPredecessorsLeft[successor] == 0) {
|
if (pairs != null) {
|
||||||
deque.addLast(successor);
|
int pairStatus = status == Nullness.NULL ? 1 : 0;
|
||||||
|
for (int pair : pairs) {
|
||||||
|
deque.addLast(pair);
|
||||||
|
deque.addLast(pairStatus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
successors: for (int successor : assignmentGraph.outgoingEdges(node)) {
|
||||||
|
if (--notNullPredecessorsLeft[successor] != 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int sibling : assignmentGraph.incomingEdges(successor)) {
|
||||||
|
if (statuses[sibling] != statuses[node]) {
|
||||||
|
continue successors;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
deque.addLast(successor);
|
||||||
|
deque.addLast(statuses[node] == Nullness.NULL ? 0 : 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -188,8 +280,11 @@ class NullnessInformationBuilder {
|
||||||
class NullExtensionVisitor extends AbstractInstructionVisitor implements DominatorWalkerCallback<State> {
|
class NullExtensionVisitor extends AbstractInstructionVisitor implements DominatorWalkerCallback<State> {
|
||||||
State currentState;
|
State currentState;
|
||||||
BasicBlock currentBlock;
|
BasicBlock currentBlock;
|
||||||
IntIntMap nullSuccessors = new IntIntHashMap();
|
BitSet notNullVariables = new BitSet();
|
||||||
IntIntMap notNullSuccessors = new IntIntHashMap();
|
|
||||||
|
NullExtensionVisitor() {
|
||||||
|
notNullVariables.set(0);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public State visit(BasicBlock block) {
|
public State visit(BasicBlock block) {
|
||||||
|
@ -200,15 +295,6 @@ class NullnessInformationBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
currentBlock = block;
|
currentBlock = block;
|
||||||
if (nullSuccessors.containsKey(block.getIndex())) {
|
|
||||||
int varIndex = nullSuccessors.remove(block.getIndex());
|
|
||||||
insertNullInstruction(program.variableAt(varIndex));
|
|
||||||
}
|
|
||||||
if (notNullSuccessors.containsKey(block.getIndex())) {
|
|
||||||
int varIndex = notNullSuccessors.remove(block.getIndex());
|
|
||||||
insertNotNullInstruction(null, program.variableAt(varIndex));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Instruction insn : block) {
|
for (Instruction insn : block) {
|
||||||
insn.acceptVisitor(this);
|
insn.acceptVisitor(this);
|
||||||
}
|
}
|
||||||
|
@ -221,9 +307,6 @@ class NullnessInformationBuilder {
|
||||||
for (int rollbackToNull : state.newlyNonNull.toArray()) {
|
for (int rollbackToNull : state.newlyNonNull.toArray()) {
|
||||||
notNullVariables.clear(rollbackToNull);
|
notNullVariables.clear(rollbackToNull);
|
||||||
}
|
}
|
||||||
for (int rollbackToNotNull : state.newlyNull.toArray()) {
|
|
||||||
nullVariables.clear(rollbackToNotNull);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -274,104 +357,32 @@ class NullnessInformationBuilder {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(StringConstantInstruction insn) {
|
public void visit(StringConstantInstruction insn) {
|
||||||
notNullVariables.set(insn.getReceiver().getIndex());
|
markAsNotNull(insn.getReceiver());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(ClassConstantInstruction insn) {
|
public void visit(ClassConstantInstruction insn) {
|
||||||
notNullVariables.set(insn.getReceiver().getIndex());
|
markAsNotNull(insn.getReceiver());
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(NullConstantInstruction insn) {
|
|
||||||
nullVariables.set(insn.getReceiver().getIndex());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(AssignInstruction insn) {
|
|
||||||
notNullVariables.set(insn.getReceiver().getIndex(), notNullVariables.get(insn.getAssignee().getIndex()));
|
|
||||||
nullVariables.set(insn.getReceiver().getIndex(), nullVariables.get(insn.getAssignee().getIndex()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(ConstructArrayInstruction insn) {
|
public void visit(ConstructArrayInstruction insn) {
|
||||||
notNullVariables.set(insn.getReceiver().getIndex());
|
markAsNotNull(insn.getReceiver());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(ConstructInstruction insn) {
|
public void visit(ConstructInstruction insn) {
|
||||||
notNullVariables.set(insn.getReceiver().getIndex());
|
markAsNotNull(insn.getReceiver());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(ConstructMultiArrayInstruction insn) {
|
public void visit(ConstructMultiArrayInstruction insn) {
|
||||||
notNullVariables.set(insn.getReceiver().getIndex());
|
markAsNotNull(insn.getReceiver());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(NullCheckInstruction insn) {
|
public void visit(NullCheckInstruction insn) {
|
||||||
notNullVariables.set(insn.getReceiver().getIndex());
|
markAsNotNull(insn.getReceiver());
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(BranchingInstruction insn) {
|
|
||||||
switch (insn.getCondition()) {
|
|
||||||
case NOT_NULL:
|
|
||||||
setNotNullSuccessor(insn.getConsequent(), insn.getOperand());
|
|
||||||
setNullSuccessor(insn.getAlternative(), insn.getOperand());
|
|
||||||
break;
|
|
||||||
case NULL:
|
|
||||||
setNullSuccessor(insn.getConsequent(), insn.getOperand());
|
|
||||||
setNotNullSuccessor(insn.getAlternative(), insn.getOperand());
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void visit(BinaryBranchingInstruction insn) {
|
|
||||||
Variable first = insn.getFirstOperand();
|
|
||||||
Variable second = insn.getSecondOperand();
|
|
||||||
if (nullVariables.get(first.getIndex())) {
|
|
||||||
first = second;
|
|
||||||
} else if (!nullVariables.get(second.getIndex())) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (insn.getCondition()) {
|
|
||||||
case REFERENCE_EQUAL:
|
|
||||||
setNotNullSuccessor(insn.getConsequent(), first);
|
|
||||||
setNullSuccessor(insn.getAlternative(), first);
|
|
||||||
break;
|
|
||||||
case REFERENCE_NOT_EQUAL:
|
|
||||||
setNullSuccessor(insn.getConsequent(), first);
|
|
||||||
setNotNullSuccessor(insn.getAlternative(), first);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setNullSuccessor(BasicBlock successor, Variable value) {
|
|
||||||
if (shouldSetSuccessor(successor, value)) {
|
|
||||||
nullSuccessors.put(successor.getIndex(), value.getIndex());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setNotNullSuccessor(BasicBlock successor, Variable value) {
|
|
||||||
if (shouldSetSuccessor(successor, value)) {
|
|
||||||
notNullSuccessors.put(successor.getIndex(), value.getIndex());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean shouldSetSuccessor(BasicBlock successor, Variable value) {
|
|
||||||
for (Phi phi : successor.getPhis()) {
|
|
||||||
if (phi.getIncomings().stream().anyMatch(incoming -> incoming.getValue() == value)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void insertNotNullInstruction(Instruction currentInstruction, Variable var) {
|
private void insertNotNullInstruction(Instruction currentInstruction, Variable var) {
|
||||||
|
@ -387,39 +398,71 @@ class NullnessInformationBuilder {
|
||||||
} else {
|
} else {
|
||||||
currentBlock.addFirst(insn);
|
currentBlock.addFirst(insn);
|
||||||
}
|
}
|
||||||
markAsNonNull(var);
|
markAsNotNull(var);
|
||||||
}
|
|
||||||
|
|
||||||
private void insertNullInstruction(Variable var) {
|
|
||||||
if (nullVariables.get(var.getIndex())) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
NullConstantInstruction insn = new NullConstantInstruction();
|
|
||||||
insn.setReceiver(var);
|
|
||||||
nullInstructions.add(insn);
|
|
||||||
currentBlock.addFirst(insn);
|
|
||||||
markAsNull(var);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void markAsNonNull(Variable var) {
|
|
||||||
if (notNullVariables.get(var.getIndex())) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
notNullVariables.set(var.getIndex());
|
|
||||||
currentState.newlyNonNull.add(var.getIndex());
|
currentState.newlyNonNull.add(var.getIndex());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void markAsNull(Variable var) {
|
private void markAsNotNull(Variable var) {
|
||||||
if (nullVariables.get(var.getIndex())) {
|
notNullVariables.set(var.getIndex());
|
||||||
return;
|
|
||||||
}
|
|
||||||
nullVariables.set(var.getIndex());
|
|
||||||
currentState.newlyNull.add(var.getIndex());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static class State {
|
static class State {
|
||||||
IntSet newlyNonNull = new IntHashSet();
|
IntSet newlyNonNull = new IntHashSet();
|
||||||
IntSet newlyNull = new IntHashSet();
|
}
|
||||||
|
|
||||||
|
class NullnessInitVisitor extends AbstractInstructionVisitor {
|
||||||
|
private IntDeque queue;
|
||||||
|
|
||||||
|
NullnessInitVisitor(IntDeque queue) {
|
||||||
|
this.queue = queue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(NullCheckInstruction insn) {
|
||||||
|
queue.addLast(insn.getReceiver().getIndex());
|
||||||
|
queue.addLast(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(NullConstantInstruction insn) {
|
||||||
|
queue.addLast(insn.getReceiver().getIndex());
|
||||||
|
queue.addLast(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(StringConstantInstruction insn) {
|
||||||
|
queue.addLast(insn.getReceiver().getIndex());
|
||||||
|
queue.addLast(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(ClassConstantInstruction insn) {
|
||||||
|
queue.addLast(insn.getReceiver().getIndex());
|
||||||
|
queue.addLast(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(ConstructArrayInstruction insn) {
|
||||||
|
queue.addLast(insn.getReceiver().getIndex());
|
||||||
|
queue.addLast(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(ConstructInstruction insn) {
|
||||||
|
queue.addLast(insn.getReceiver().getIndex());
|
||||||
|
queue.addLast(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(ConstructMultiArrayInstruction insn) {
|
||||||
|
queue.addLast(insn.getReceiver().getIndex());
|
||||||
|
queue.addLast(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Nullness {
|
||||||
|
NULL,
|
||||||
|
NOT_NULL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
@ -106,7 +106,7 @@ public class TagRegistry {
|
||||||
|
|
||||||
private void markImplementor(ClassReaderSource classSource, String className, String ifaceName,
|
private void markImplementor(ClassReaderSource classSource, String className, String ifaceName,
|
||||||
Map<String, Set<String>> implementedBy) {
|
Map<String, Set<String>> implementedBy) {
|
||||||
if (!implementedBy.computeIfAbsent(ifaceName, key -> new HashSet<>()).add(className)) {
|
if (!implementedBy.computeIfAbsent(ifaceName, key -> new LinkedHashSet<>()).add(className)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,11 +120,11 @@ public class TagRegistry {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private int assignRange(int start, Map<String, List<String>> hierarhy, String className,
|
private int assignRange(int start, Map<String, List<String>> hierarchy, String className,
|
||||||
Map<String, Range> ranges) {
|
Map<String, Range> ranges) {
|
||||||
int end = start + 1;
|
int end = start + 1;
|
||||||
for (String childClass : hierarhy.getOrDefault(className, Collections.emptyList())) {
|
for (String childClass : hierarchy.getOrDefault(className, Collections.emptyList())) {
|
||||||
end = assignRange(end, hierarhy, childClass, ranges);
|
end = assignRange(end, hierarchy, childClass, ranges);
|
||||||
}
|
}
|
||||||
++end;
|
++end;
|
||||||
ranges.put(className, new Range(start, end));
|
ranges.put(className, new Range(start, end));
|
||||||
|
|
|
@ -18,6 +18,7 @@ package org.teavm.model.classes;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import org.teavm.model.ClassReader;
|
import org.teavm.model.ClassReader;
|
||||||
|
@ -45,7 +46,7 @@ public class VirtualTableProvider {
|
||||||
cls = virtualMethod.getClassName();
|
cls = virtualMethod.getClassName();
|
||||||
}
|
}
|
||||||
classNames.add(cls);
|
classNames.add(cls);
|
||||||
virtualMethodMap.computeIfAbsent(cls, c -> new HashSet<>()).add(virtualMethod.getDescriptor());
|
virtualMethodMap.computeIfAbsent(cls, c -> new LinkedHashSet<>()).add(virtualMethod.getDescriptor());
|
||||||
}
|
}
|
||||||
|
|
||||||
for (String className : classNames) {
|
for (String className : classNames) {
|
||||||
|
|
|
@ -46,7 +46,7 @@ import org.teavm.model.instructions.NullConstantInstruction;
|
||||||
import org.teavm.model.instructions.PutFieldInstruction;
|
import org.teavm.model.instructions.PutFieldInstruction;
|
||||||
import org.teavm.model.instructions.StringConstantInstruction;
|
import org.teavm.model.instructions.StringConstantInstruction;
|
||||||
import org.teavm.model.instructions.SwitchInstruction;
|
import org.teavm.model.instructions.SwitchInstruction;
|
||||||
import org.teavm.model.util.InstructionTransitionExtractor;
|
import org.teavm.model.util.TransitionExtractor;
|
||||||
|
|
||||||
public final class ProgramEmitter {
|
public final class ProgramEmitter {
|
||||||
private Program program;
|
private Program program;
|
||||||
|
@ -467,7 +467,7 @@ public final class ProgramEmitter {
|
||||||
if (insn == null) {
|
if (insn == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
InstructionTransitionExtractor extractor = new InstructionTransitionExtractor();
|
TransitionExtractor extractor = new TransitionExtractor();
|
||||||
insn.acceptVisitor(extractor);
|
insn.acceptVisitor(extractor);
|
||||||
return extractor.getTargets() != null;
|
return extractor.getTargets() != null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,21 +13,25 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.teavm.backend.c.analyze;
|
package org.teavm.model.lowlevel;
|
||||||
|
|
||||||
import com.carrotsearch.hppc.ObjectByteHashMap;
|
import com.carrotsearch.hppc.ObjectByteHashMap;
|
||||||
import com.carrotsearch.hppc.ObjectByteMap;
|
import com.carrotsearch.hppc.ObjectByteMap;
|
||||||
import org.teavm.interop.Function;
|
import org.teavm.interop.Function;
|
||||||
import org.teavm.interop.StaticInit;
|
import org.teavm.interop.StaticInit;
|
||||||
import org.teavm.interop.Structure;
|
import org.teavm.interop.Structure;
|
||||||
|
import org.teavm.interop.Unmanaged;
|
||||||
import org.teavm.model.ClassReader;
|
import org.teavm.model.ClassReader;
|
||||||
import org.teavm.model.ClassReaderSource;
|
import org.teavm.model.ClassReaderSource;
|
||||||
|
import org.teavm.model.MethodReader;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
|
||||||
public class Characteristics {
|
public class Characteristics {
|
||||||
private ClassReaderSource classSource;
|
private ClassReaderSource classSource;
|
||||||
private ObjectByteMap<String> isStructure = new ObjectByteHashMap<>();
|
private ObjectByteMap<String> isStructure = new ObjectByteHashMap<>();
|
||||||
private ObjectByteMap<String> isStaticInit = new ObjectByteHashMap<>();
|
private ObjectByteMap<String> isStaticInit = new ObjectByteHashMap<>();
|
||||||
private ObjectByteMap<String> isFunction = new ObjectByteHashMap<>();
|
private ObjectByteMap<String> isFunction = new ObjectByteHashMap<>();
|
||||||
|
private ObjectByteMap<MethodReference> isManaged = new ObjectByteHashMap<>();
|
||||||
|
|
||||||
public Characteristics(ClassReaderSource classSource) {
|
public Characteristics(ClassReaderSource classSource) {
|
||||||
this.classSource = classSource;
|
this.classSource = classSource;
|
||||||
|
@ -78,4 +82,26 @@ public class Characteristics {
|
||||||
}
|
}
|
||||||
return result != 0;
|
return result != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isManaged(MethodReference methodReference) {
|
||||||
|
byte result = isManaged.getOrDefault(methodReference, (byte) -1);
|
||||||
|
if (result < 0) {
|
||||||
|
result = computeIsManaged(methodReference) ? (byte) 1 : 0;
|
||||||
|
isManaged.put(methodReference, result);
|
||||||
|
}
|
||||||
|
return result != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean computeIsManaged(MethodReference methodReference) {
|
||||||
|
MethodReader method = classSource.resolve(methodReference);
|
||||||
|
if (method == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClassReader cls = classSource.get(method.getOwnerName());
|
||||||
|
if (cls.getAnnotations().get(Unmanaged.class.getName()) != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return method == null || method.getAnnotations().get(Unmanaged.class.getName()) == null;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -59,7 +59,7 @@ import org.teavm.runtime.ExceptionHandling;
|
||||||
import org.teavm.runtime.ShadowStack;
|
import org.teavm.runtime.ShadowStack;
|
||||||
|
|
||||||
public class ExceptionHandlingShadowStackContributor {
|
public class ExceptionHandlingShadowStackContributor {
|
||||||
private ManagedMethodRepository managedMethodRepository;
|
private Characteristics characteristics;
|
||||||
private List<CallSiteDescriptor> callSites;
|
private List<CallSiteDescriptor> callSites;
|
||||||
private BasicBlock defaultExceptionHandler;
|
private BasicBlock defaultExceptionHandler;
|
||||||
private MethodReference method;
|
private MethodReference method;
|
||||||
|
@ -67,10 +67,11 @@ public class ExceptionHandlingShadowStackContributor {
|
||||||
private DominatorTree dom;
|
private DominatorTree dom;
|
||||||
private BasicBlock[] variableDefinitionPlaces;
|
private BasicBlock[] variableDefinitionPlaces;
|
||||||
private boolean hasExceptionHandlers;
|
private boolean hasExceptionHandlers;
|
||||||
|
private int parameterCount;
|
||||||
|
|
||||||
public ExceptionHandlingShadowStackContributor(ManagedMethodRepository managedMethodRepository,
|
public ExceptionHandlingShadowStackContributor(Characteristics characteristics,
|
||||||
List<CallSiteDescriptor> callSites, MethodReference method, Program program) {
|
List<CallSiteDescriptor> callSites, MethodReference method, Program program) {
|
||||||
this.managedMethodRepository = managedMethodRepository;
|
this.characteristics = characteristics;
|
||||||
this.callSites = callSites;
|
this.callSites = callSites;
|
||||||
this.method = method;
|
this.method = method;
|
||||||
this.program = program;
|
this.program = program;
|
||||||
|
@ -78,6 +79,7 @@ public class ExceptionHandlingShadowStackContributor {
|
||||||
Graph cfg = ProgramUtils.buildControlFlowGraph(program);
|
Graph cfg = ProgramUtils.buildControlFlowGraph(program);
|
||||||
dom = GraphUtils.buildDominatorTree(cfg);
|
dom = GraphUtils.buildDominatorTree(cfg);
|
||||||
variableDefinitionPlaces = ProgramUtils.getVariableDefinitionPlaces(program);
|
variableDefinitionPlaces = ProgramUtils.getVariableDefinitionPlaces(program);
|
||||||
|
parameterCount = method.parameterCount() + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean contribute() {
|
public boolean contribute() {
|
||||||
|
@ -145,7 +147,8 @@ public class ExceptionHandlingShadowStackContributor {
|
||||||
|
|
||||||
for (Variable sourceVar : sourceVariables) {
|
for (Variable sourceVar : sourceVariables) {
|
||||||
BasicBlock sourceVarDefinedAt = variableDefinitionPlaces[sourceVar.getIndex()];
|
BasicBlock sourceVarDefinedAt = variableDefinitionPlaces[sourceVar.getIndex()];
|
||||||
if (dom.dominates(sourceVarDefinedAt.getIndex(), block.getIndex())) {
|
if (sourceVar.getIndex() < parameterCount
|
||||||
|
|| dom.dominates(sourceVarDefinedAt.getIndex(), block.getIndex())) {
|
||||||
currentJointSources[phi.getReceiver().getIndex()] = sourceVar.getIndex();
|
currentJointSources[phi.getReceiver().getIndex()] = sourceVar.getIndex();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -164,7 +167,14 @@ public class ExceptionHandlingShadowStackContributor {
|
||||||
if (isCallInstruction(insn)) {
|
if (isCallInstruction(insn)) {
|
||||||
BasicBlock next;
|
BasicBlock next;
|
||||||
boolean last = false;
|
boolean last = false;
|
||||||
if (insn instanceof RaiseInstruction) {
|
if (isSpecialCallInstruction(insn)) {
|
||||||
|
next = null;
|
||||||
|
while (insn.getNext() != null) {
|
||||||
|
Instruction nextInsn = insn.getNext();
|
||||||
|
nextInsn.delete();
|
||||||
|
}
|
||||||
|
last = true;
|
||||||
|
} else if (insn instanceof RaiseInstruction) {
|
||||||
InvokeInstruction raise = new InvokeInstruction();
|
InvokeInstruction raise = new InvokeInstruction();
|
||||||
raise.setMethod(new MethodReference(ExceptionHandling.class, "throwException", Throwable.class,
|
raise.setMethod(new MethodReference(ExceptionHandling.class, "throwException", Throwable.class,
|
||||||
void.class));
|
void.class));
|
||||||
|
@ -229,11 +239,24 @@ public class ExceptionHandlingShadowStackContributor {
|
||||||
|| insn instanceof CloneArrayInstruction || insn instanceof RaiseInstruction) {
|
|| insn instanceof CloneArrayInstruction || insn instanceof RaiseInstruction) {
|
||||||
return true;
|
return true;
|
||||||
} else if (insn instanceof InvokeInstruction) {
|
} else if (insn instanceof InvokeInstruction) {
|
||||||
return managedMethodRepository.isManaged(((InvokeInstruction) insn).getMethod());
|
MethodReference method = ((InvokeInstruction) insn).getMethod();
|
||||||
|
if (characteristics.isManaged(method)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return method.getClassName().equals(ExceptionHandling.class.getName())
|
||||||
|
&& method.getName().startsWith("throw");
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isSpecialCallInstruction(Instruction insn) {
|
||||||
|
if (!(insn instanceof InvokeInstruction)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
MethodReference method = ((InvokeInstruction) insn).getMethod();
|
||||||
|
return method.getClassName().equals(ExceptionHandling.class.getName()) && method.getName().startsWith("throw");
|
||||||
|
}
|
||||||
|
|
||||||
private List<Instruction> setLocation(List<Instruction> instructions, TextLocation location) {
|
private List<Instruction> setLocation(List<Instruction> instructions, TextLocation location) {
|
||||||
if (location != null) {
|
if (location != null) {
|
||||||
for (Instruction instruction : instructions) {
|
for (Instruction instruction : instructions) {
|
||||||
|
@ -318,7 +341,12 @@ public class ExceptionHandlingShadowStackContributor {
|
||||||
switchInsn.setDefaultTarget(getDefaultExceptionHandler());
|
switchInsn.setDefaultTarget(getDefaultExceptionHandler());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (switchInsn.getEntries().size() == 1) {
|
if (switchInsn.getEntries().isEmpty()) {
|
||||||
|
instructions.clear();
|
||||||
|
JumpInstruction jump = new JumpInstruction();
|
||||||
|
jump.setTarget(switchInsn.getDefaultTarget());
|
||||||
|
instructions.add(jump);
|
||||||
|
} else if (switchInsn.getEntries().size() == 1) {
|
||||||
SwitchTableEntry entry = switchInsn.getEntries().get(0);
|
SwitchTableEntry entry = switchInsn.getEntries().get(0);
|
||||||
|
|
||||||
IntegerConstantInstruction singleTestConstant = new IntegerConstantInstruction();
|
IntegerConstantInstruction singleTestConstant = new IntegerConstantInstruction();
|
||||||
|
|
|
@ -25,7 +25,8 @@ import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -60,10 +61,10 @@ import org.teavm.model.util.VariableType;
|
||||||
import org.teavm.runtime.ShadowStack;
|
import org.teavm.runtime.ShadowStack;
|
||||||
|
|
||||||
public class GCShadowStackContributor {
|
public class GCShadowStackContributor {
|
||||||
private ManagedMethodRepository managedMethodRepository;
|
private Characteristics characteristics;
|
||||||
|
|
||||||
public GCShadowStackContributor(ManagedMethodRepository managedMethodRepository) {
|
public GCShadowStackContributor(Characteristics characteristics) {
|
||||||
this.managedMethodRepository = managedMethodRepository;
|
this.characteristics = characteristics;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int contribute(Program program, MethodReader method) {
|
public int contribute(Program program, MethodReader method) {
|
||||||
|
@ -156,7 +157,7 @@ public class GCShadowStackContributor {
|
||||||
|| insn instanceof ConstructMultiArrayInstruction
|
|| insn instanceof ConstructMultiArrayInstruction
|
||||||
|| insn instanceof CloneArrayInstruction || insn instanceof RaiseInstruction) {
|
|| insn instanceof CloneArrayInstruction || insn instanceof RaiseInstruction) {
|
||||||
if (insn instanceof InvokeInstruction
|
if (insn instanceof InvokeInstruction
|
||||||
&& !managedMethodRepository.isManaged(((InvokeInstruction) insn).getMethod())) {
|
&& !characteristics.isManaged(((InvokeInstruction) insn).getMethod())) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -220,7 +221,7 @@ public class GCShadowStackContributor {
|
||||||
for (Incoming incoming : phi.getIncomings()) {
|
for (Incoming incoming : phi.getIncomings()) {
|
||||||
Set<Phi> phis = destinationPhis.get(incoming.getValue().getIndex());
|
Set<Phi> phis = destinationPhis.get(incoming.getValue().getIndex());
|
||||||
if (phis == null) {
|
if (phis == null) {
|
||||||
phis = new HashSet<>();
|
phis = new LinkedHashSet<>();
|
||||||
destinationPhis.set(incoming.getValue().getIndex(), phis);
|
destinationPhis.set(incoming.getValue().getIndex(), phis);
|
||||||
}
|
}
|
||||||
phis.add(phi);
|
phis.add(phi);
|
||||||
|
@ -256,7 +257,7 @@ public class GCShadowStackContributor {
|
||||||
|
|
||||||
List<Map<Instruction, int[]>> slotsToUpdate = new ArrayList<>();
|
List<Map<Instruction, int[]>> slotsToUpdate = new ArrayList<>();
|
||||||
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
||||||
slotsToUpdate.add(new HashMap<>());
|
slotsToUpdate.add(new LinkedHashMap<>());
|
||||||
}
|
}
|
||||||
|
|
||||||
Graph cfg = ProgramUtils.buildControlFlowGraph(program);
|
Graph cfg = ProgramUtils.buildControlFlowGraph(program);
|
||||||
|
|
|
@ -1,50 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2016 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.model.lowlevel;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import org.teavm.interop.Unmanaged;
|
|
||||||
import org.teavm.model.ClassReader;
|
|
||||||
import org.teavm.model.ClassReaderSource;
|
|
||||||
import org.teavm.model.MethodReader;
|
|
||||||
import org.teavm.model.MethodReference;
|
|
||||||
|
|
||||||
public class ManagedMethodRepository {
|
|
||||||
private ClassReaderSource classSource;
|
|
||||||
private Map<MethodReference, Boolean> cache = new HashMap<>();
|
|
||||||
|
|
||||||
public ManagedMethodRepository(ClassReaderSource classSource) {
|
|
||||||
this.classSource = classSource;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isManaged(MethodReference methodReference) {
|
|
||||||
return cache.computeIfAbsent(methodReference, this::computeIsManaged);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean computeIsManaged(MethodReference methodReference) {
|
|
||||||
MethodReader method = classSource.resolve(methodReference);
|
|
||||||
if (method == null) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
ClassReader cls = classSource.get(method.getOwnerName());
|
|
||||||
if (cls.getAnnotations().get(Unmanaged.class.getName()) != null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return method == null || method.getAnnotations().get(Unmanaged.class.getName()) == null;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,126 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2018 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.model.lowlevel;
|
||||||
|
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import org.teavm.model.BasicBlock;
|
||||||
|
import org.teavm.model.Instruction;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
import org.teavm.model.Program;
|
||||||
|
import org.teavm.model.Variable;
|
||||||
|
import org.teavm.model.instructions.AbstractInstructionVisitor;
|
||||||
|
import org.teavm.model.instructions.CloneArrayInstruction;
|
||||||
|
import org.teavm.model.instructions.GetFieldInstruction;
|
||||||
|
import org.teavm.model.instructions.InvokeInstruction;
|
||||||
|
import org.teavm.model.instructions.MonitorEnterInstruction;
|
||||||
|
import org.teavm.model.instructions.MonitorExitInstruction;
|
||||||
|
import org.teavm.model.instructions.NullCheckInstruction;
|
||||||
|
import org.teavm.model.instructions.PutFieldInstruction;
|
||||||
|
import org.teavm.model.instructions.RaiseInstruction;
|
||||||
|
import org.teavm.model.instructions.UnwrapArrayInstruction;
|
||||||
|
import org.teavm.model.util.PhiUpdater;
|
||||||
|
|
||||||
|
public class NullCheckInsertion {
|
||||||
|
private Characteristics characteristics;
|
||||||
|
|
||||||
|
public NullCheckInsertion(Characteristics characteristics) {
|
||||||
|
this.characteristics = characteristics;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void transformProgram(Program program, MethodReference methodReference) {
|
||||||
|
if (!characteristics.isManaged(methodReference)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
InsertionVisitor visitor = new InsertionVisitor();
|
||||||
|
for (BasicBlock block : program.getBasicBlocks()) {
|
||||||
|
for (Instruction instruction : block) {
|
||||||
|
instruction.acceptVisitor(visitor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (visitor.changed) {
|
||||||
|
new PhiUpdater().updatePhis(program, methodReference.parameterCount() + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class InsertionVisitor extends AbstractInstructionVisitor {
|
||||||
|
boolean changed;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(RaiseInstruction insn) {
|
||||||
|
addGuard(insn, RaiseInstruction::getException, RaiseInstruction::setException);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(GetFieldInstruction insn) {
|
||||||
|
if (!characteristics.isStructure(insn.getField().getClassName())) {
|
||||||
|
addGuard(insn, GetFieldInstruction::getInstance, GetFieldInstruction::setInstance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(PutFieldInstruction insn) {
|
||||||
|
if (!characteristics.isStructure(insn.getField().getClassName())) {
|
||||||
|
addGuard(insn, PutFieldInstruction::getInstance, PutFieldInstruction::setInstance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(CloneArrayInstruction insn) {
|
||||||
|
addGuard(insn, CloneArrayInstruction::getArray, CloneArrayInstruction::setArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(UnwrapArrayInstruction insn) {
|
||||||
|
addGuard(insn, UnwrapArrayInstruction::getArray, UnwrapArrayInstruction::setArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(InvokeInstruction insn) {
|
||||||
|
if (!characteristics.isStructure(insn.getMethod().getClassName())
|
||||||
|
&& characteristics.isManaged(insn.getMethod())) {
|
||||||
|
addGuard(insn, InvokeInstruction::getInstance, InvokeInstruction::setInstance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(MonitorEnterInstruction insn) {
|
||||||
|
addGuard(insn, MonitorEnterInstruction::getObjectRef, MonitorEnterInstruction::setObjectRef);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(MonitorExitInstruction insn) {
|
||||||
|
addGuard(insn, MonitorExitInstruction::getObjectRef, MonitorExitInstruction::setObjectRef);
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T extends Instruction> void addGuard(T instruction, Function<T, Variable> get,
|
||||||
|
BiConsumer<T, Variable> set) {
|
||||||
|
Variable value = get.apply(instruction);
|
||||||
|
if (value == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
NullCheckInstruction nullCheck = new NullCheckInstruction();
|
||||||
|
nullCheck.setValue(value);
|
||||||
|
nullCheck.setReceiver(value);
|
||||||
|
nullCheck.setLocation(instruction.getLocation());
|
||||||
|
set.accept(instruction, nullCheck.getReceiver());
|
||||||
|
instruction.insertPrevious(nullCheck);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,174 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2018 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.model.lowlevel;
|
||||||
|
|
||||||
|
import org.teavm.model.BasicBlock;
|
||||||
|
import org.teavm.model.Incoming;
|
||||||
|
import org.teavm.model.Instruction;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
import org.teavm.model.Phi;
|
||||||
|
import org.teavm.model.Program;
|
||||||
|
import org.teavm.model.ValueType;
|
||||||
|
import org.teavm.model.Variable;
|
||||||
|
import org.teavm.model.instructions.AssignInstruction;
|
||||||
|
import org.teavm.model.instructions.BranchingCondition;
|
||||||
|
import org.teavm.model.instructions.BranchingInstruction;
|
||||||
|
import org.teavm.model.instructions.DoubleConstantInstruction;
|
||||||
|
import org.teavm.model.instructions.ExitInstruction;
|
||||||
|
import org.teavm.model.instructions.FloatConstantInstruction;
|
||||||
|
import org.teavm.model.instructions.IntegerConstantInstruction;
|
||||||
|
import org.teavm.model.instructions.InvocationType;
|
||||||
|
import org.teavm.model.instructions.InvokeInstruction;
|
||||||
|
import org.teavm.model.instructions.JumpInstruction;
|
||||||
|
import org.teavm.model.instructions.LongConstantInstruction;
|
||||||
|
import org.teavm.model.instructions.NullCheckInstruction;
|
||||||
|
import org.teavm.model.instructions.NullConstantInstruction;
|
||||||
|
import org.teavm.model.optimization.RedundantJumpElimination;
|
||||||
|
import org.teavm.model.util.ProgramUtils;
|
||||||
|
import org.teavm.runtime.ExceptionHandling;
|
||||||
|
|
||||||
|
public class NullCheckTransformation {
|
||||||
|
public void apply(Program program, ValueType returnType) {
|
||||||
|
int[] mappings = new int[program.basicBlockCount()];
|
||||||
|
for (int i = 0; i < mappings.length; ++i) {
|
||||||
|
mappings[i] = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
BasicBlock returnBlock = null;
|
||||||
|
|
||||||
|
int count = program.basicBlockCount();
|
||||||
|
for (int i = 0; i < count; ++i) {
|
||||||
|
BasicBlock next = program.basicBlockAt(i);
|
||||||
|
BasicBlock block = null;
|
||||||
|
int newIndex = i;
|
||||||
|
while (next != null) {
|
||||||
|
block = next;
|
||||||
|
newIndex = block.getIndex();
|
||||||
|
next = null;
|
||||||
|
for (Instruction instruction : block) {
|
||||||
|
if (!(instruction instanceof NullCheckInstruction)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
NullCheckInstruction nullCheck = (NullCheckInstruction) instruction;
|
||||||
|
|
||||||
|
BasicBlock continueBlock = program.createBasicBlock();
|
||||||
|
|
||||||
|
continueBlock.getTryCatchBlocks().addAll(ProgramUtils.copyTryCatches(block, program));
|
||||||
|
while (nullCheck.getNext() != null) {
|
||||||
|
Instruction nextInstruction = nullCheck.getNext();
|
||||||
|
nextInstruction.delete();
|
||||||
|
continueBlock.add(nextInstruction);
|
||||||
|
}
|
||||||
|
|
||||||
|
BasicBlock throwBlock = program.createBasicBlock();
|
||||||
|
|
||||||
|
throwBlock.getTryCatchBlocks().addAll(ProgramUtils.copyTryCatches(block, program));
|
||||||
|
InvokeInstruction throwNPE = new InvokeInstruction();
|
||||||
|
throwNPE.setType(InvocationType.SPECIAL);
|
||||||
|
throwNPE.setMethod(new MethodReference(ExceptionHandling.class, "throwNullPointerException",
|
||||||
|
void.class));
|
||||||
|
throwNPE.setLocation(nullCheck.getLocation());
|
||||||
|
throwBlock.add(throwNPE);
|
||||||
|
|
||||||
|
if (returnBlock == null) {
|
||||||
|
returnBlock = program.createBasicBlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
JumpInstruction jumpToFakeReturn = new JumpInstruction();
|
||||||
|
jumpToFakeReturn.setTarget(returnBlock);
|
||||||
|
jumpToFakeReturn.setLocation(nullCheck.getLocation());
|
||||||
|
throwBlock.add(jumpToFakeReturn);
|
||||||
|
|
||||||
|
BranchingInstruction jumpIfNull = new BranchingInstruction(BranchingCondition.NULL);
|
||||||
|
jumpIfNull.setOperand(nullCheck.getValue());
|
||||||
|
jumpIfNull.setConsequent(throwBlock);
|
||||||
|
jumpIfNull.setAlternative(continueBlock);
|
||||||
|
jumpIfNull.setLocation(nullCheck.getLocation());
|
||||||
|
nullCheck.replace(jumpIfNull);
|
||||||
|
|
||||||
|
AssignInstruction assign = new AssignInstruction();
|
||||||
|
assign.setAssignee(nullCheck.getValue());
|
||||||
|
assign.setReceiver(nullCheck.getReceiver());
|
||||||
|
assign.setLocation(nullCheck.getLocation());
|
||||||
|
continueBlock.addFirst(assign);
|
||||||
|
|
||||||
|
next = continueBlock;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mappings[i] = newIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (returnBlock != null) {
|
||||||
|
ExitInstruction fakeExit = new ExitInstruction();
|
||||||
|
if (returnType != ValueType.VOID) {
|
||||||
|
Variable fakeReturnVar = program.createVariable();
|
||||||
|
createFakeReturnValue(returnBlock, fakeReturnVar, returnType);
|
||||||
|
fakeExit.setValueToReturn(fakeReturnVar);
|
||||||
|
}
|
||||||
|
returnBlock.add(fakeExit);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (BasicBlock block : program.getBasicBlocks()) {
|
||||||
|
for (Phi phi : block.getPhis()) {
|
||||||
|
for (Incoming incoming : phi.getIncomings()) {
|
||||||
|
int source = incoming.getSource().getIndex();
|
||||||
|
if (source < mappings.length && mappings[source] != source) {
|
||||||
|
incoming.setSource(program.basicBlockAt(mappings[source]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RedundantJumpElimination.optimize(program);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createFakeReturnValue(BasicBlock block, Variable variable, ValueType type) {
|
||||||
|
if (type instanceof ValueType.Primitive) {
|
||||||
|
switch (((ValueType.Primitive) type).getKind()) {
|
||||||
|
case BOOLEAN:
|
||||||
|
case BYTE:
|
||||||
|
case SHORT:
|
||||||
|
case INTEGER:
|
||||||
|
case CHARACTER:
|
||||||
|
IntegerConstantInstruction intZero = new IntegerConstantInstruction();
|
||||||
|
intZero.setReceiver(variable);
|
||||||
|
block.add(intZero);
|
||||||
|
return;
|
||||||
|
case LONG:
|
||||||
|
LongConstantInstruction longZero = new LongConstantInstruction();
|
||||||
|
longZero.setReceiver(variable);
|
||||||
|
block.add(longZero);
|
||||||
|
return;
|
||||||
|
case FLOAT:
|
||||||
|
FloatConstantInstruction floatZero = new FloatConstantInstruction();
|
||||||
|
floatZero.setReceiver(variable);
|
||||||
|
block.add(floatZero);
|
||||||
|
return;
|
||||||
|
case DOUBLE:
|
||||||
|
DoubleConstantInstruction doubleZero = new DoubleConstantInstruction();
|
||||||
|
doubleZero.setReceiver(variable);
|
||||||
|
block.add(doubleZero);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NullConstantInstruction nullConstant = new NullConstantInstruction();
|
||||||
|
nullConstant.setReceiver(variable);
|
||||||
|
block.add(nullConstant);
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,7 +18,6 @@ package org.teavm.model.lowlevel;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import org.teavm.model.BasicBlock;
|
import org.teavm.model.BasicBlock;
|
||||||
import org.teavm.model.ClassReaderSource;
|
|
||||||
import org.teavm.model.Incoming;
|
import org.teavm.model.Incoming;
|
||||||
import org.teavm.model.Instruction;
|
import org.teavm.model.Instruction;
|
||||||
import org.teavm.model.MethodReader;
|
import org.teavm.model.MethodReader;
|
||||||
|
@ -34,13 +33,13 @@ import org.teavm.model.instructions.JumpInstruction;
|
||||||
import org.teavm.runtime.ShadowStack;
|
import org.teavm.runtime.ShadowStack;
|
||||||
|
|
||||||
public class ShadowStackTransformer {
|
public class ShadowStackTransformer {
|
||||||
private ManagedMethodRepository managedMethodRepository;
|
private Characteristics managedMethodRepository;
|
||||||
private GCShadowStackContributor gcContributor;
|
private GCShadowStackContributor gcContributor;
|
||||||
private List<CallSiteDescriptor> callSites = new ArrayList<>();
|
private List<CallSiteDescriptor> callSites = new ArrayList<>();
|
||||||
|
|
||||||
public ShadowStackTransformer(ClassReaderSource classSource) {
|
public ShadowStackTransformer(Characteristics managedMethodRepository) {
|
||||||
managedMethodRepository = new ManagedMethodRepository(classSource);
|
|
||||||
gcContributor = new GCShadowStackContributor(managedMethodRepository);
|
gcContributor = new GCShadowStackContributor(managedMethodRepository);
|
||||||
|
this.managedMethodRepository = managedMethodRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<CallSiteDescriptor> getCallSites() {
|
public List<CallSiteDescriptor> getCallSites() {
|
||||||
|
|
|
@ -17,27 +17,33 @@ package org.teavm.model.optimization;
|
||||||
|
|
||||||
import org.teavm.model.BasicBlock;
|
import org.teavm.model.BasicBlock;
|
||||||
import org.teavm.model.Instruction;
|
import org.teavm.model.Instruction;
|
||||||
|
import org.teavm.model.MethodDescriptor;
|
||||||
import org.teavm.model.Phi;
|
import org.teavm.model.Phi;
|
||||||
import org.teavm.model.Program;
|
import org.teavm.model.Program;
|
||||||
|
import org.teavm.model.analysis.NullnessInformation;
|
||||||
import org.teavm.model.instructions.BinaryBranchingCondition;
|
import org.teavm.model.instructions.BinaryBranchingCondition;
|
||||||
import org.teavm.model.instructions.BinaryBranchingInstruction;
|
import org.teavm.model.instructions.BinaryBranchingInstruction;
|
||||||
import org.teavm.model.instructions.BranchingCondition;
|
import org.teavm.model.instructions.BranchingCondition;
|
||||||
import org.teavm.model.instructions.BranchingInstruction;
|
import org.teavm.model.instructions.BranchingInstruction;
|
||||||
import org.teavm.model.instructions.IntegerConstantInstruction;
|
import org.teavm.model.instructions.IntegerConstantInstruction;
|
||||||
import org.teavm.model.instructions.JumpInstruction;
|
import org.teavm.model.instructions.JumpInstruction;
|
||||||
import org.teavm.model.instructions.NullConstantInstruction;
|
import org.teavm.model.util.TransitionExtractor;
|
||||||
import org.teavm.model.util.InstructionTransitionExtractor;
|
|
||||||
|
|
||||||
public class ConstantConditionElimination implements MethodOptimization {
|
public class ConstantConditionElimination implements MethodOptimization {
|
||||||
private int[] constants;
|
private int[] constants;
|
||||||
private boolean[] constantDefined;
|
private boolean[] constantDefined;
|
||||||
private boolean[] nullConstants;
|
private NullnessInformation nullness;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean optimize(MethodOptimizationContext context, Program program) {
|
public boolean optimize(MethodOptimizationContext context, Program program) {
|
||||||
|
return optimize(context.getMethod().getDescriptor(), program);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean optimize(MethodDescriptor descriptor, Program program) {
|
||||||
constants = new int[program.variableCount()];
|
constants = new int[program.variableCount()];
|
||||||
constantDefined = new boolean[program.variableCount()];
|
constantDefined = new boolean[program.variableCount()];
|
||||||
nullConstants = new boolean[program.variableCount()];
|
nullness = NullnessInformation.build(program, descriptor);
|
||||||
|
|
||||||
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
||||||
BasicBlock block = program.basicBlockAt(i);
|
BasicBlock block = program.basicBlockAt(i);
|
||||||
for (Instruction insn : block) {
|
for (Instruction insn : block) {
|
||||||
|
@ -46,16 +52,12 @@ public class ConstantConditionElimination implements MethodOptimization {
|
||||||
int receiver = constInsn.getReceiver().getIndex();
|
int receiver = constInsn.getReceiver().getIndex();
|
||||||
constants[receiver] = constInsn.getConstant();
|
constants[receiver] = constInsn.getConstant();
|
||||||
constantDefined[receiver] = true;
|
constantDefined[receiver] = true;
|
||||||
} else if (insn instanceof NullConstantInstruction) {
|
|
||||||
NullConstantInstruction constInsn = (NullConstantInstruction) insn;
|
|
||||||
int receiver = constInsn.getReceiver().getIndex();
|
|
||||||
nullConstants[receiver] = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean changed = false;
|
boolean changed = false;
|
||||||
InstructionTransitionExtractor transitionExtractor = new InstructionTransitionExtractor();
|
TransitionExtractor transitionExtractor = new TransitionExtractor();
|
||||||
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
||||||
BasicBlock block = program.basicBlockAt(i);
|
BasicBlock block = program.basicBlockAt(i);
|
||||||
Instruction insn = block.getLastInstruction();
|
Instruction insn = block.getLastInstruction();
|
||||||
|
@ -84,6 +86,11 @@ public class ConstantConditionElimination implements MethodOptimization {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nullness.dispose();
|
||||||
|
nullness = null;
|
||||||
|
constantDefined = null;
|
||||||
|
constants = null;
|
||||||
|
|
||||||
if (changed) {
|
if (changed) {
|
||||||
new UnreachableBasicBlockEliminator().optimize(program);
|
new UnreachableBasicBlockEliminator().optimize(program);
|
||||||
}
|
}
|
||||||
|
@ -96,13 +103,17 @@ public class ConstantConditionElimination implements MethodOptimization {
|
||||||
BranchingInstruction branching = (BranchingInstruction) instruction;
|
BranchingInstruction branching = (BranchingInstruction) instruction;
|
||||||
switch (branching.getCondition()) {
|
switch (branching.getCondition()) {
|
||||||
case NULL:
|
case NULL:
|
||||||
if (nullConstants[branching.getOperand().getIndex()]) {
|
if (nullness.isNull(branching.getOperand())) {
|
||||||
return branching.getConsequent();
|
return branching.getConsequent();
|
||||||
|
} else if (nullness.isNotNull(branching.getOperand())) {
|
||||||
|
return branching.getAlternative();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case NOT_NULL:
|
case NOT_NULL:
|
||||||
if (nullConstants[branching.getOperand().getIndex()]) {
|
if (nullness.isNull(branching.getOperand())) {
|
||||||
return branching.getAlternative();
|
return branching.getAlternative();
|
||||||
|
} else if (nullness.isNotNull(branching.getOperand())) {
|
||||||
|
return branching.getConsequent();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default: {
|
default: {
|
||||||
|
|
|
@ -46,9 +46,9 @@ import org.teavm.model.instructions.InvokeInstruction;
|
||||||
import org.teavm.model.instructions.JumpInstruction;
|
import org.teavm.model.instructions.JumpInstruction;
|
||||||
import org.teavm.model.instructions.SwitchInstruction;
|
import org.teavm.model.instructions.SwitchInstruction;
|
||||||
import org.teavm.model.util.BasicBlockMapper;
|
import org.teavm.model.util.BasicBlockMapper;
|
||||||
import org.teavm.model.util.InstructionTransitionExtractor;
|
|
||||||
import org.teavm.model.util.InstructionVariableMapper;
|
import org.teavm.model.util.InstructionVariableMapper;
|
||||||
import org.teavm.model.util.ProgramUtils;
|
import org.teavm.model.util.ProgramUtils;
|
||||||
|
import org.teavm.model.util.TransitionExtractor;
|
||||||
|
|
||||||
public class Inlining {
|
public class Inlining {
|
||||||
private static final int DEFAULT_THRESHOLD = 17;
|
private static final int DEFAULT_THRESHOLD = 17;
|
||||||
|
@ -191,7 +191,7 @@ public class Inlining {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
InstructionTransitionExtractor transitionExtractor = new InstructionTransitionExtractor();
|
TransitionExtractor transitionExtractor = new TransitionExtractor();
|
||||||
Instruction splitLastInsn = splitBlock.getLastInstruction();
|
Instruction splitLastInsn = splitBlock.getLastInstruction();
|
||||||
if (splitLastInsn != null) {
|
if (splitLastInsn != null) {
|
||||||
splitLastInsn.acceptVisitor(transitionExtractor);
|
splitLastInsn.acceptVisitor(transitionExtractor);
|
||||||
|
|
|
@ -25,18 +25,22 @@ import org.teavm.model.Program;
|
||||||
import org.teavm.model.TryCatchBlock;
|
import org.teavm.model.TryCatchBlock;
|
||||||
import org.teavm.model.instructions.AssignInstruction;
|
import org.teavm.model.instructions.AssignInstruction;
|
||||||
import org.teavm.model.instructions.JumpInstruction;
|
import org.teavm.model.instructions.JumpInstruction;
|
||||||
import org.teavm.model.util.InstructionTransitionExtractor;
|
|
||||||
import org.teavm.model.util.ProgramUtils;
|
import org.teavm.model.util.ProgramUtils;
|
||||||
|
import org.teavm.model.util.TransitionExtractor;
|
||||||
|
|
||||||
public class RedundantJumpElimination implements MethodOptimization {
|
public class RedundantJumpElimination implements MethodOptimization {
|
||||||
@Override
|
@Override
|
||||||
public boolean optimize(MethodOptimizationContext context, Program program) {
|
public boolean optimize(MethodOptimizationContext context, Program program) {
|
||||||
|
return optimize(program);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean optimize(Program program) {
|
||||||
Graph cfg = ProgramUtils.buildControlFlowGraph(program);
|
Graph cfg = ProgramUtils.buildControlFlowGraph(program);
|
||||||
int[] incomingCount = new int[cfg.size()];
|
int[] incomingCount = new int[cfg.size()];
|
||||||
Arrays.setAll(incomingCount, cfg::incomingEdgesCount);
|
Arrays.setAll(incomingCount, cfg::incomingEdgesCount);
|
||||||
|
|
||||||
boolean changed = false;
|
boolean changed = false;
|
||||||
InstructionTransitionExtractor transitionExtractor = new InstructionTransitionExtractor();
|
TransitionExtractor transitionExtractor = new TransitionExtractor();
|
||||||
for (int i = 1; i < program.basicBlockCount(); ++i) {
|
for (int i = 1; i < program.basicBlockCount(); ++i) {
|
||||||
BasicBlock block = program.basicBlockAt(i);
|
BasicBlock block = program.basicBlockAt(i);
|
||||||
if (block == null) {
|
if (block == null) {
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2018 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.model.optimization;
|
||||||
|
|
||||||
|
import org.teavm.model.BasicBlock;
|
||||||
|
import org.teavm.model.Instruction;
|
||||||
|
import org.teavm.model.Program;
|
||||||
|
import org.teavm.model.analysis.NullnessInformation;
|
||||||
|
import org.teavm.model.instructions.AssignInstruction;
|
||||||
|
import org.teavm.model.instructions.NullCheckInstruction;
|
||||||
|
import org.teavm.model.util.ProgramUtils;
|
||||||
|
|
||||||
|
public class RedundantNullCheckElimination implements MethodOptimization {
|
||||||
|
@Override
|
||||||
|
public boolean optimize(MethodOptimizationContext context, Program program) {
|
||||||
|
NullnessInformation nullness = NullnessInformation.build(program, context.getMethod().getDescriptor());
|
||||||
|
|
||||||
|
boolean hasChanges = false;
|
||||||
|
for (BasicBlock block : program.getBasicBlocks()) {
|
||||||
|
for (Instruction instruction : block) {
|
||||||
|
if (!(instruction instanceof NullCheckInstruction)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
NullCheckInstruction nullCheck = (NullCheckInstruction) instruction;
|
||||||
|
if (nullness.isSynthesized(nullCheck.getReceiver())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nullness.isNotNull(nullCheck.getValue())) {
|
||||||
|
AssignInstruction assign = new AssignInstruction();
|
||||||
|
assign.setAssignee(nullCheck.getValue());
|
||||||
|
assign.setReceiver(nullCheck.getReceiver());
|
||||||
|
assign.setLocation(nullCheck.getLocation());
|
||||||
|
nullCheck.replace(assign);
|
||||||
|
hasChanges = true;
|
||||||
|
} else if (nullness.isNull(nullCheck.getValue())) {
|
||||||
|
block.detachSuccessors();
|
||||||
|
while (nullCheck.getNext() != null) {
|
||||||
|
nullCheck.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
nullCheck.insertPreviousAll(ProgramUtils.createThrowNPEInstructions(
|
||||||
|
program, nullCheck.getLocation()));
|
||||||
|
nullCheck.delete();
|
||||||
|
hasChanges = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasChanges) {
|
||||||
|
new UnreachableBasicBlockEliminator().optimize(program);
|
||||||
|
}
|
||||||
|
|
||||||
|
nullness.dispose();
|
||||||
|
return hasChanges;
|
||||||
|
}
|
||||||
|
}
|
|
@ -38,6 +38,7 @@ import org.teavm.model.instructions.FloatConstantInstruction;
|
||||||
import org.teavm.model.instructions.GetFieldInstruction;
|
import org.teavm.model.instructions.GetFieldInstruction;
|
||||||
import org.teavm.model.instructions.IntegerConstantInstruction;
|
import org.teavm.model.instructions.IntegerConstantInstruction;
|
||||||
import org.teavm.model.instructions.LongConstantInstruction;
|
import org.teavm.model.instructions.LongConstantInstruction;
|
||||||
|
import org.teavm.model.instructions.NullCheckInstruction;
|
||||||
import org.teavm.model.instructions.NullConstantInstruction;
|
import org.teavm.model.instructions.NullConstantInstruction;
|
||||||
import org.teavm.model.instructions.PutFieldInstruction;
|
import org.teavm.model.instructions.PutFieldInstruction;
|
||||||
import org.teavm.model.util.PhiUpdater;
|
import org.teavm.model.util.PhiUpdater;
|
||||||
|
@ -96,32 +97,26 @@ public class ScalarReplacement implements MethodOptimization {
|
||||||
}
|
}
|
||||||
|
|
||||||
FieldReference[] fields = escapeAnalysis.getFields(phi.getReceiver().getIndex());
|
FieldReference[] fields = escapeAnalysis.getFields(phi.getReceiver().getIndex());
|
||||||
if (fields == null) {
|
if (fields != null) {
|
||||||
continue;
|
for (FieldReference field : fields) {
|
||||||
}
|
Phi phiReplacement = new Phi();
|
||||||
|
phiReplacement.setReceiver(fieldMappings.get(phi.getReceiver().getIndex()).get(field));
|
||||||
|
|
||||||
for (FieldReference field : fields) {
|
for (Incoming incoming : phi.getIncomings()) {
|
||||||
boolean allIncomingsInitialized = true;
|
Incoming incomingReplacement = new Incoming();
|
||||||
for (Incoming incoming : phi.getIncomings()) {
|
incomingReplacement.setSource(incoming.getSource());
|
||||||
if (fieldMappings.get(incoming.getValue().getIndex()).get(field) == null) {
|
incomingReplacement.setValue(fieldMappings.get(incoming.getValue().getIndex()).get(field));
|
||||||
allIncomingsInitialized = false;
|
phiReplacement.getIncomings().add(incomingReplacement);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (!allIncomingsInitialized) {
|
additionalPhis.add(phiReplacement);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
boolean isClass = phi.getIncomings().stream()
|
||||||
|
.anyMatch(incoming -> escapeAnalysis.getFields(incoming.getValue().getIndex()) != null);
|
||||||
|
if (!isClass) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Phi phiReplacement = new Phi();
|
|
||||||
phiReplacement.setReceiver(fieldMappings.get(phi.getReceiver().getIndex()).get(field));
|
|
||||||
|
|
||||||
for (Incoming incoming : phi.getIncomings()) {
|
|
||||||
Incoming incomingReplacement = new Incoming();
|
|
||||||
incomingReplacement.setSource(incoming.getSource());
|
|
||||||
incomingReplacement.setValue(fieldMappings.get(incoming.getValue().getIndex()).get(field));
|
|
||||||
phiReplacement.getIncomings().add(incomingReplacement);
|
|
||||||
}
|
|
||||||
|
|
||||||
additionalPhis.add(phiReplacement);
|
|
||||||
}
|
}
|
||||||
block.getPhis().remove(i--);
|
block.getPhis().remove(i--);
|
||||||
}
|
}
|
||||||
|
@ -162,6 +157,11 @@ public class ScalarReplacement implements MethodOptimization {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(NullCheckInstruction insn) {
|
||||||
|
transfer(insn, insn.getValue(), insn.getReceiver());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(GetFieldInstruction insn) {
|
public void visit(GetFieldInstruction insn) {
|
||||||
if (insn.getInstance() != null && !escapeAnalysis.escapes(insn.getInstance().getIndex())) {
|
if (insn.getInstance() != null && !escapeAnalysis.escapes(insn.getInstance().getIndex())) {
|
||||||
|
@ -188,20 +188,24 @@ public class ScalarReplacement implements MethodOptimization {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(AssignInstruction insn) {
|
public void visit(AssignInstruction insn) {
|
||||||
if (escapeAnalysis.escapes(insn.getAssignee().getIndex())) {
|
transfer(insn, insn.getAssignee(), insn.getReceiver());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void transfer(Instruction insn, Variable from, Variable to) {
|
||||||
|
if (escapeAnalysis.escapes(from.getIndex())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
FieldReference[] fields = escapeAnalysis.getFields(insn.getReceiver().getIndex());
|
FieldReference[] fields = escapeAnalysis.getFields(to.getIndex());
|
||||||
if (fields == null) {
|
if (fields == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (FieldReference field : fields) {
|
for (FieldReference field : fields) {
|
||||||
Variable assignee = fieldMappings.get(insn.getAssignee().getIndex()).get(field);
|
Variable assignee = fieldMappings.get(from.getIndex()).get(field);
|
||||||
if (assignee == null) {
|
if (assignee == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Variable receiver = fieldMappings.get(insn.getReceiver().getIndex()).get(field);
|
Variable receiver = fieldMappings.get(to.getIndex()).get(field);
|
||||||
AssignInstruction assignment = new AssignInstruction();
|
AssignInstruction assignment = new AssignInstruction();
|
||||||
assignment.setReceiver(receiver);
|
assignment.setReceiver(receiver);
|
||||||
assignment.setAssignee(assignee);
|
assignment.setAssignee(assignee);
|
||||||
|
|
|
@ -22,14 +22,14 @@ import org.teavm.model.Incoming;
|
||||||
import org.teavm.model.Phi;
|
import org.teavm.model.Phi;
|
||||||
import org.teavm.model.Program;
|
import org.teavm.model.Program;
|
||||||
import org.teavm.model.TryCatchBlock;
|
import org.teavm.model.TryCatchBlock;
|
||||||
import org.teavm.model.util.InstructionTransitionExtractor;
|
import org.teavm.model.util.TransitionExtractor;
|
||||||
|
|
||||||
public class UnreachableBasicBlockEliminator {
|
public class UnreachableBasicBlockEliminator {
|
||||||
public void optimize(Program program) {
|
public void optimize(Program program) {
|
||||||
if (program.basicBlockCount() == 0) {
|
if (program.basicBlockCount() == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
InstructionTransitionExtractor transitionExtractor = new InstructionTransitionExtractor();
|
TransitionExtractor transitionExtractor = new TransitionExtractor();
|
||||||
boolean[] reachable = new boolean[program.basicBlockCount()];
|
boolean[] reachable = new boolean[program.basicBlockCount()];
|
||||||
IntegerStack stack = new IntegerStack(program.basicBlockCount());
|
IntegerStack stack = new IntegerStack(program.basicBlockCount());
|
||||||
stack.push(0);
|
stack.push(0);
|
||||||
|
|
|
@ -162,7 +162,7 @@ public class AsyncProgramSplitter {
|
||||||
queue.add(next);
|
queue.add(next);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
InstructionTransitionExtractor successorExtractor = new InstructionTransitionExtractor();
|
TransitionExtractor successorExtractor = new TransitionExtractor();
|
||||||
sourceBlock.getLastInstruction().acceptVisitor(successorExtractor);
|
sourceBlock.getLastInstruction().acceptVisitor(successorExtractor);
|
||||||
for (BasicBlock successor : successorExtractor.getTargets()) {
|
for (BasicBlock successor : successorExtractor.getTargets()) {
|
||||||
BasicBlock targetSuccessor = targetBlock.getProgram().basicBlockAt(successor.getIndex());
|
BasicBlock targetSuccessor = targetBlock.getProgram().basicBlockAt(successor.getIndex());
|
||||||
|
|
|
@ -27,7 +27,7 @@ class InterferenceGraphBuilder {
|
||||||
}
|
}
|
||||||
UsageExtractor useExtractor = new UsageExtractor();
|
UsageExtractor useExtractor = new UsageExtractor();
|
||||||
DefinitionExtractor defExtractor = new DefinitionExtractor();
|
DefinitionExtractor defExtractor = new DefinitionExtractor();
|
||||||
InstructionTransitionExtractor succExtractor = new InstructionTransitionExtractor();
|
TransitionExtractor succExtractor = new TransitionExtractor();
|
||||||
List<List<Incoming>> outgoings = ProgramUtils.getPhiOutputs(program);
|
List<List<Incoming>> outgoings = ProgramUtils.getPhiOutputs(program);
|
||||||
BitSet live = new BitSet();
|
BitSet live = new BitSet();
|
||||||
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
||||||
|
|
|
@ -80,7 +80,7 @@ public class MissingItemsProcessor {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void truncateBlock(Instruction instruction) {
|
private void truncateBlock(Instruction instruction) {
|
||||||
InstructionTransitionExtractor transitionExtractor = new InstructionTransitionExtractor();
|
TransitionExtractor transitionExtractor = new TransitionExtractor();
|
||||||
BasicBlock block = instruction.getBasicBlock();
|
BasicBlock block = instruction.getBasicBlock();
|
||||||
if (block.getLastInstruction() != null) {
|
if (block.getLastInstruction() != null) {
|
||||||
block.getLastInstruction().acceptVisitor(transitionExtractor);
|
block.getLastInstruction().acceptVisitor(transitionExtractor);
|
||||||
|
|
|
@ -23,11 +23,11 @@ import com.carrotsearch.hppc.IntObjectMap;
|
||||||
import com.carrotsearch.hppc.IntSet;
|
import com.carrotsearch.hppc.IntSet;
|
||||||
import java.util.ArrayDeque;
|
import java.util.ArrayDeque;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.BitSet;
|
import java.util.BitSet;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Deque;
|
import java.util.Deque;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.function.Predicate;
|
||||||
import org.teavm.common.DominatorTree;
|
import org.teavm.common.DominatorTree;
|
||||||
import org.teavm.common.Graph;
|
import org.teavm.common.Graph;
|
||||||
import org.teavm.common.GraphUtils;
|
import org.teavm.common.GraphUtils;
|
||||||
|
@ -36,8 +36,10 @@ import org.teavm.model.BasicBlock;
|
||||||
import org.teavm.model.Incoming;
|
import org.teavm.model.Incoming;
|
||||||
import org.teavm.model.Instruction;
|
import org.teavm.model.Instruction;
|
||||||
import org.teavm.model.InvokeDynamicInstruction;
|
import org.teavm.model.InvokeDynamicInstruction;
|
||||||
|
import org.teavm.model.Outgoing;
|
||||||
import org.teavm.model.Phi;
|
import org.teavm.model.Phi;
|
||||||
import org.teavm.model.Program;
|
import org.teavm.model.Program;
|
||||||
|
import org.teavm.model.Sigma;
|
||||||
import org.teavm.model.TryCatchBlock;
|
import org.teavm.model.TryCatchBlock;
|
||||||
import org.teavm.model.Variable;
|
import org.teavm.model.Variable;
|
||||||
import org.teavm.model.instructions.ArrayLengthInstruction;
|
import org.teavm.model.instructions.ArrayLengthInstruction;
|
||||||
|
@ -93,10 +95,11 @@ public class PhiUpdater {
|
||||||
private List<List<Phi>> synthesizedPhisByBlock = new ArrayList<>();
|
private List<List<Phi>> synthesizedPhisByBlock = new ArrayList<>();
|
||||||
private IntObjectMap<Phi> phisByReceiver = new IntObjectHashMap<>();
|
private IntObjectMap<Phi> phisByReceiver = new IntObjectHashMap<>();
|
||||||
private BitSet usedPhis = new BitSet();
|
private BitSet usedPhis = new BitSet();
|
||||||
private Variable[] originalExceptionVariables;
|
|
||||||
private boolean[] usedDefinitions;
|
private boolean[] usedDefinitions;
|
||||||
private IntegerArray variableToSourceMap = new IntegerArray(10);
|
private IntegerArray variableToSourceMap = new IntegerArray(10);
|
||||||
private List<Phi> synthesizedPhis = new ArrayList<>();
|
private List<Phi> synthesizedPhis = new ArrayList<>();
|
||||||
|
private Sigma[][] sigmas;
|
||||||
|
private Predicate<Instruction> sigmaPredicate = instruction -> false;
|
||||||
|
|
||||||
public int getSourceVariable(int var) {
|
public int getSourceVariable(int var) {
|
||||||
if (var >= variableToSourceMap.size()) {
|
if (var >= variableToSourceMap.size()) {
|
||||||
|
@ -109,7 +112,24 @@ public class PhiUpdater {
|
||||||
return synthesizedPhis;
|
return synthesizedPhis;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updatePhis(Program program, Variable[] arguments) {
|
public void updatePhis(Program program, int parameterCount) {
|
||||||
|
Variable[] parameters = new Variable[parameterCount];
|
||||||
|
for (int i = 0; i < parameters.length; ++i) {
|
||||||
|
parameters[i] = program.variableAt(i);
|
||||||
|
}
|
||||||
|
updatePhis(program, parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Sigma[] getSigmasAt(int blockIndex) {
|
||||||
|
Sigma[] result = sigmas[blockIndex];
|
||||||
|
return result != null ? result.clone() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSigmaPredicate(Predicate<Instruction> sigmaPredicate) {
|
||||||
|
this.sigmaPredicate = sigmaPredicate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updatePhis(Program program, Variable[] parameters) {
|
||||||
if (program.basicBlockCount() == 0) {
|
if (program.basicBlockCount() == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -122,8 +142,8 @@ public class PhiUpdater {
|
||||||
|
|
||||||
variableMap = new Variable[program.variableCount()];
|
variableMap = new Variable[program.variableCount()];
|
||||||
usedDefinitions = new boolean[program.variableCount()];
|
usedDefinitions = new boolean[program.variableCount()];
|
||||||
for (int i = 0; i < arguments.length; ++i) {
|
for (int i = 0; i < parameters.length; ++i) {
|
||||||
variableMap[i] = arguments[i];
|
variableMap[i] = parameters[i];
|
||||||
usedDefinitions[i] = true;
|
usedDefinitions[i] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,15 +166,55 @@ public class PhiUpdater {
|
||||||
synthesizedPhisByBlock.add(new ArrayList<>());
|
synthesizedPhisByBlock.add(new ArrayList<>());
|
||||||
}
|
}
|
||||||
|
|
||||||
originalExceptionVariables = new Variable[program.basicBlockCount()];
|
estimateSigmas();
|
||||||
Arrays.setAll(originalExceptionVariables, i -> program.basicBlockAt(i).getExceptionVariable());
|
|
||||||
|
|
||||||
estimatePhis();
|
estimatePhis();
|
||||||
renameVariables();
|
renameVariables();
|
||||||
propagatePhiUsageInformation();
|
propagatePhiUsageInformation();
|
||||||
addSynthesizedPhis();
|
addSynthesizedPhis();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void estimateSigmas() {
|
||||||
|
TransitionExtractor transitionExtractor = new TransitionExtractor();
|
||||||
|
UsageExtractor usageExtractor = new UsageExtractor();
|
||||||
|
|
||||||
|
sigmas = new Sigma[program.basicBlockCount()][];
|
||||||
|
for (int i = 0; i < sigmas.length; ++i) {
|
||||||
|
BasicBlock block = program.basicBlockAt(i);
|
||||||
|
Instruction instruction = block.getLastInstruction();
|
||||||
|
if (instruction == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
instruction.acceptVisitor(transitionExtractor);
|
||||||
|
BasicBlock[] targets = transitionExtractor.getTargets();
|
||||||
|
if (targets == null || targets.length < 2) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sigmaPredicate.test(instruction)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
instruction.acceptVisitor(usageExtractor);
|
||||||
|
Variable[] variables = usageExtractor.getUsedVariables();
|
||||||
|
|
||||||
|
Sigma[] sigmasInBlock = new Sigma[variables.length];
|
||||||
|
for (int j = 0; j < sigmasInBlock.length; ++j) {
|
||||||
|
Sigma sigma = new Sigma(block, variables[j]);
|
||||||
|
sigmasInBlock[j] = sigma;
|
||||||
|
for (BasicBlock target : targets) {
|
||||||
|
Variable outgoingVar = program.createVariable();
|
||||||
|
variableToSourceMap.add(sigma.getValue().getIndex());
|
||||||
|
outgoingVar.setDebugName(sigma.getValue().getDebugName());
|
||||||
|
outgoingVar.setLabel(sigma.getValue().getLabel());
|
||||||
|
Outgoing outgoing = new Outgoing(outgoingVar, target);
|
||||||
|
sigma.getOutgoings().add(outgoing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sigmas[i] = sigmasInBlock;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void estimatePhis() {
|
private void estimatePhis() {
|
||||||
DefinitionExtractor definitionExtractor = new DefinitionExtractor();
|
DefinitionExtractor definitionExtractor = new DefinitionExtractor();
|
||||||
variableDefined = new boolean[program.variableCount()];
|
variableDefined = new boolean[program.variableCount()];
|
||||||
|
@ -165,6 +225,23 @@ public class PhiUpdater {
|
||||||
int i = stack.removeLast();
|
int i = stack.removeLast();
|
||||||
currentBlock = program.basicBlockAt(i);
|
currentBlock = program.basicBlockAt(i);
|
||||||
|
|
||||||
|
for (int predecessor : cfg.incomingEdges(i)) {
|
||||||
|
if (sigmas[predecessor] == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (domTree.immediateDominatorOf(i) != predecessor) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (Sigma sigma : sigmas[predecessor]) {
|
||||||
|
for (Outgoing outgoing : sigma.getOutgoings()) {
|
||||||
|
if (outgoing.getTarget() == currentBlock) {
|
||||||
|
markAssignment(sigma.getValue());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (currentBlock.getExceptionVariable() != null) {
|
if (currentBlock.getExceptionVariable() != null) {
|
||||||
markAssignment(currentBlock.getExceptionVariable());
|
markAssignment(currentBlock.getExceptionVariable());
|
||||||
}
|
}
|
||||||
|
@ -181,6 +258,12 @@ public class PhiUpdater {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sigmas[i] != null) {
|
||||||
|
for (Sigma sigma : sigmas[i]) {
|
||||||
|
markAssignment(sigma.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (int successor : domGraph.outgoingEdges(i)) {
|
for (int successor : domGraph.outgoingEdges(i)) {
|
||||||
stack.addLast(successor);
|
stack.addLast(successor);
|
||||||
}
|
}
|
||||||
|
@ -237,14 +320,27 @@ public class PhiUpdater {
|
||||||
|
|
||||||
for (Incoming output : phiOutputs.get(index)) {
|
for (Incoming output : phiOutputs.get(index)) {
|
||||||
Variable var = output.getValue();
|
Variable var = output.getValue();
|
||||||
output.setValue(use(var));
|
Variable sigmaVar = applySigmaRename(output.getPhi().getBasicBlock(), var);
|
||||||
|
var = sigmaVar != var ? sigmaVar : use(var);
|
||||||
|
output.setValue(var);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Sigma[] nextSigmas = sigmas[index];
|
||||||
for (int j = successors.length - 1; j >= 0; --j) {
|
for (int j = successors.length - 1; j >= 0; --j) {
|
||||||
int successor = successors[j];
|
int successor = successors[j];
|
||||||
Task next = new Task();
|
Task next = new Task();
|
||||||
next.variables = variableMap.clone();
|
next.variables = variableMap.clone();
|
||||||
next.block = program.basicBlockAt(successor);
|
next.block = program.basicBlockAt(successor);
|
||||||
|
if (nextSigmas != null) {
|
||||||
|
for (Sigma sigma : nextSigmas) {
|
||||||
|
for (Outgoing outgoing : sigma.getOutgoings()) {
|
||||||
|
if (outgoing.getTarget().getIndex() == successor) {
|
||||||
|
next.variables[sigma.getValue().getIndex()] = outgoing.getValue();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
stack.push(next);
|
stack.push(next);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -256,6 +352,12 @@ public class PhiUpdater {
|
||||||
for (int successor : cfg.outgoingEdges(index)) {
|
for (int successor : cfg.outgoingEdges(index)) {
|
||||||
renameOutgoingPhis(successor, exceptionHandlingSuccessors.contains(successor));
|
renameOutgoingPhis(successor, exceptionHandlingSuccessors.contains(successor));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sigmas[index] != null) {
|
||||||
|
for (Sigma sigma : sigmas[index]) {
|
||||||
|
sigma.setValue(use(sigma.getValue()));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -306,6 +408,7 @@ public class PhiUpdater {
|
||||||
|
|
||||||
for (int j = 0; j < phis.size(); ++j) {
|
for (int j = 0; j < phis.size(); ++j) {
|
||||||
Phi phi = phis.get(j);
|
Phi phi = phis.get(j);
|
||||||
|
Variable originalVar = program.variableAt(phiIndexes[j]);
|
||||||
Variable var = variableMap[phiIndexes[j]];
|
Variable var = variableMap[phiIndexes[j]];
|
||||||
if (var != null) {
|
if (var != null) {
|
||||||
List<Variable> versions = definedVersions.get(phiIndexes[j]);
|
List<Variable> versions = definedVersions.get(phiIndexes[j]);
|
||||||
|
@ -318,15 +421,34 @@ public class PhiUpdater {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Variable sigmaVar = applySigmaRename(program.basicBlockAt(successor), originalVar);
|
||||||
Incoming incoming = new Incoming();
|
Incoming incoming = new Incoming();
|
||||||
incoming.setSource(currentBlock);
|
incoming.setSource(currentBlock);
|
||||||
incoming.setValue(var);
|
incoming.setValue(sigmaVar != originalVar ? sigmaVar : var);
|
||||||
phi.getIncomings().add(incoming);
|
phi.getIncomings().add(incoming);
|
||||||
phi.getReceiver().setDebugName(var.getDebugName());
|
phi.getReceiver().setDebugName(var.getDebugName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Variable applySigmaRename(BasicBlock target, Variable var) {
|
||||||
|
Sigma[] blockSigmas = sigmas[currentBlock.getIndex()];
|
||||||
|
if (blockSigmas == null) {
|
||||||
|
return var;
|
||||||
|
}
|
||||||
|
for (Sigma sigma : blockSigmas) {
|
||||||
|
if (sigma.getValue() != var) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (Outgoing outgoing : sigma.getOutgoings()) {
|
||||||
|
if (outgoing.getTarget() == target) {
|
||||||
|
return outgoing.getValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return var;
|
||||||
|
}
|
||||||
|
|
||||||
private void markAssignment(Variable var) {
|
private void markAssignment(Variable var) {
|
||||||
Deque<BasicBlock> worklist = new ArrayDeque<>();
|
Deque<BasicBlock> worklist = new ArrayDeque<>();
|
||||||
worklist.push(currentBlock);
|
worklist.push(currentBlock);
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
package org.teavm.model.util;
|
package org.teavm.model.util;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -29,6 +30,7 @@ import org.teavm.model.IncomingReader;
|
||||||
import org.teavm.model.Instruction;
|
import org.teavm.model.Instruction;
|
||||||
import org.teavm.model.InstructionIterator;
|
import org.teavm.model.InstructionIterator;
|
||||||
import org.teavm.model.InstructionReadVisitor;
|
import org.teavm.model.InstructionReadVisitor;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
import org.teavm.model.Phi;
|
import org.teavm.model.Phi;
|
||||||
import org.teavm.model.PhiReader;
|
import org.teavm.model.PhiReader;
|
||||||
import org.teavm.model.Program;
|
import org.teavm.model.Program;
|
||||||
|
@ -37,6 +39,10 @@ import org.teavm.model.TextLocation;
|
||||||
import org.teavm.model.TryCatchBlock;
|
import org.teavm.model.TryCatchBlock;
|
||||||
import org.teavm.model.TryCatchBlockReader;
|
import org.teavm.model.TryCatchBlockReader;
|
||||||
import org.teavm.model.Variable;
|
import org.teavm.model.Variable;
|
||||||
|
import org.teavm.model.instructions.ConstructInstruction;
|
||||||
|
import org.teavm.model.instructions.InvocationType;
|
||||||
|
import org.teavm.model.instructions.InvokeInstruction;
|
||||||
|
import org.teavm.model.instructions.RaiseInstruction;
|
||||||
|
|
||||||
public final class ProgramUtils {
|
public final class ProgramUtils {
|
||||||
private ProgramUtils() {
|
private ProgramUtils() {
|
||||||
|
@ -44,7 +50,7 @@ public final class ProgramUtils {
|
||||||
|
|
||||||
public static Graph buildControlFlowGraph(Program program) {
|
public static Graph buildControlFlowGraph(Program program) {
|
||||||
GraphBuilder graphBuilder = new GraphBuilder(program.basicBlockCount());
|
GraphBuilder graphBuilder = new GraphBuilder(program.basicBlockCount());
|
||||||
InstructionTransitionExtractor transitionExtractor = new InstructionTransitionExtractor();
|
TransitionExtractor transitionExtractor = new TransitionExtractor();
|
||||||
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
||||||
BasicBlock block = program.basicBlockAt(i);
|
BasicBlock block = program.basicBlockAt(i);
|
||||||
Instruction insn = block.getLastInstruction();
|
Instruction insn = block.getLastInstruction();
|
||||||
|
@ -203,4 +209,23 @@ public final class ProgramUtils {
|
||||||
var.setLabel(suggestedName);
|
var.setLabel(suggestedName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static List<Instruction> createThrowNPEInstructions(Program program, TextLocation location) {
|
||||||
|
ConstructInstruction newNPE = new ConstructInstruction();
|
||||||
|
newNPE.setType(NullPointerException.class.getName());
|
||||||
|
newNPE.setReceiver(program.createVariable());
|
||||||
|
newNPE.setLocation(location);
|
||||||
|
|
||||||
|
InvokeInstruction initNPE = new InvokeInstruction();
|
||||||
|
initNPE.setType(InvocationType.SPECIAL);
|
||||||
|
initNPE.setInstance(newNPE.getReceiver());
|
||||||
|
initNPE.setMethod(new MethodReference(NullPointerException.class, "<init>", void.class));
|
||||||
|
initNPE.setLocation(location);
|
||||||
|
|
||||||
|
RaiseInstruction raise = new RaiseInstruction();
|
||||||
|
raise.setException(newNPE.getReceiver());
|
||||||
|
raise.setLocation(location);
|
||||||
|
|
||||||
|
return Arrays.asList(newNPE, initNPE, raise);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ import org.teavm.model.BasicBlock;
|
||||||
import org.teavm.model.InvokeDynamicInstruction;
|
import org.teavm.model.InvokeDynamicInstruction;
|
||||||
import org.teavm.model.instructions.*;
|
import org.teavm.model.instructions.*;
|
||||||
|
|
||||||
public class InstructionTransitionExtractor implements InstructionVisitor {
|
public class TransitionExtractor implements InstructionVisitor {
|
||||||
private BasicBlock[] targets;
|
private BasicBlock[] targets;
|
||||||
|
|
||||||
public BasicBlock[] getTargets() {
|
public BasicBlock[] getTargets() {
|
|
@ -23,6 +23,7 @@ import org.teavm.model.*;
|
||||||
import org.teavm.model.instructions.*;
|
import org.teavm.model.instructions.*;
|
||||||
|
|
||||||
public class TypeInferer {
|
public class TypeInferer {
|
||||||
|
private static VariableType[] typesByOrdinal = VariableType.values();
|
||||||
VariableType[] types;
|
VariableType[] types;
|
||||||
GraphBuilder builder;
|
GraphBuilder builder;
|
||||||
GraphBuilder arrayElemBuilder;
|
GraphBuilder arrayElemBuilder;
|
||||||
|
@ -54,47 +55,35 @@ public class TypeInferer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
IntegerStack stack = new IntegerStack(sz);
|
IntegerStack stack = new IntegerStack(sz * 2);
|
||||||
Graph graph = builder.build();
|
Graph graph = builder.build();
|
||||||
Graph arrayElemGraph = arrayElemBuilder.build();
|
Graph arrayElemGraph = arrayElemBuilder.build();
|
||||||
for (int i = 0; i < sz; ++i) {
|
for (int i = 0; i < graph.size(); ++i) {
|
||||||
if ((i >= graph.size() || graph.incomingEdgesCount(i) == 0)
|
if (types[i] != null && graph.outgoingEdgesCount(i) > 0) {
|
||||||
&& (i >= arrayElemGraph.size() || arrayElemGraph.incomingEdgesCount(i) == 0)) {
|
stack.push(types[i].ordinal());
|
||||||
stack.push(i);
|
stack.push(i);
|
||||||
|
types[i] = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean[] visited = new boolean[sz];
|
|
||||||
while (!stack.isEmpty()) {
|
while (!stack.isEmpty()) {
|
||||||
int node = stack.pop();
|
int node = stack.pop();
|
||||||
if (visited[node]) {
|
VariableType type = typesByOrdinal[stack.pop()];
|
||||||
|
if (types[node] != null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
visited[node] = true;
|
types[node] = type;
|
||||||
if (types[node] == null) {
|
|
||||||
for (int pred : graph.incomingEdges(node)) {
|
for (int successor : graph.outgoingEdges(node)) {
|
||||||
if (types[pred] != null) {
|
if (types[successor] == null) {
|
||||||
types[node] = types[pred];
|
stack.push(type.ordinal());
|
||||||
break;
|
stack.push(successor);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (types[node] == null) {
|
for (int successor : arrayElemGraph.outgoingEdges(node)) {
|
||||||
for (int pred : arrayElemGraph.incomingEdges(node)) {
|
if (types[successor] == null) {
|
||||||
if (types[pred] != null) {
|
stack.push(convertFromArray(type).ordinal());
|
||||||
types[node] = convertFromArray(types[pred]);
|
stack.push(successor);
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (int succ : graph.outgoingEdges(node)) {
|
|
||||||
if (!visited[succ]) {
|
|
||||||
stack.push(succ);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (int succ : arrayElemGraph.outgoingEdges(node)) {
|
|
||||||
if (!visited[succ]) {
|
|
||||||
stack.push(succ);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,7 +63,7 @@ public class Parser {
|
||||||
this.referenceCache = referenceCache;
|
this.referenceCache = referenceCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MethodHolder parseMethod(MethodNode node, String className, String fileName) {
|
public MethodHolder parseMethod(MethodNode node, String fileName) {
|
||||||
MethodNode nodeWithoutJsr = new MethodNode(Opcodes.ASM5, node.access, node.name, node.desc, node.signature,
|
MethodNode nodeWithoutJsr = new MethodNode(Opcodes.ASM5, node.access, node.name, node.desc, node.signature,
|
||||||
node.exceptions.toArray(new String[0]));
|
node.exceptions.toArray(new String[0]));
|
||||||
JSRInlinerAdapter adapter = new JSRInlinerAdapter(nodeWithoutJsr, node.access, node.name, node.desc,
|
JSRInlinerAdapter adapter = new JSRInlinerAdapter(nodeWithoutJsr, node.access, node.name, node.desc,
|
||||||
|
@ -76,7 +76,7 @@ public class Parser {
|
||||||
|
|
||||||
ProgramParser programParser = new ProgramParser(referenceCache);
|
ProgramParser programParser = new ProgramParser(referenceCache);
|
||||||
programParser.setFileName(fileName);
|
programParser.setFileName(fileName);
|
||||||
Program program = programParser.parse(node, className);
|
Program program = programParser.parse(node);
|
||||||
new UnreachableBasicBlockEliminator().optimize(program);
|
new UnreachableBasicBlockEliminator().optimize(program);
|
||||||
PhiUpdater phiUpdater = new PhiUpdater();
|
PhiUpdater phiUpdater = new PhiUpdater();
|
||||||
Variable[] argumentMapping = applySignature(program, method.getParameterTypes());
|
Variable[] argumentMapping = applySignature(program, method.getParameterTypes());
|
||||||
|
@ -245,7 +245,7 @@ public class Parser {
|
||||||
}
|
}
|
||||||
String fullFileName = node.name.substring(0, node.name.lastIndexOf('/') + 1) + node.sourceFile;
|
String fullFileName = node.name.substring(0, node.name.lastIndexOf('/') + 1) + node.sourceFile;
|
||||||
for (MethodNode methodNode : node.methods) {
|
for (MethodNode methodNode : node.methods) {
|
||||||
cls.addMethod(parseMethod(methodNode, node.name, fullFileName));
|
cls.addMethod(parseMethod(methodNode, fullFileName));
|
||||||
}
|
}
|
||||||
if (node.outerClass != null) {
|
if (node.outerClass != null) {
|
||||||
cls.setOwnerName(node.outerClass.replace('/', '.'));
|
cls.setOwnerName(node.outerClass.replace('/', '.'));
|
||||||
|
|
|
@ -15,13 +15,87 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.parsing;
|
package org.teavm.parsing;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.ArrayDeque;
|
||||||
import org.objectweb.asm.*;
|
import java.util.ArrayList;
|
||||||
import org.objectweb.asm.tree.*;
|
import java.util.Arrays;
|
||||||
import org.teavm.model.*;
|
import java.util.Collections;
|
||||||
import org.teavm.model.instructions.*;
|
import java.util.Deque;
|
||||||
import org.teavm.model.util.InstructionTransitionExtractor;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.objectweb.asm.AnnotationVisitor;
|
||||||
|
import org.objectweb.asm.Attribute;
|
||||||
|
import org.objectweb.asm.Handle;
|
||||||
|
import org.objectweb.asm.Label;
|
||||||
|
import org.objectweb.asm.MethodVisitor;
|
||||||
|
import org.objectweb.asm.Opcodes;
|
||||||
|
import org.objectweb.asm.Type;
|
||||||
|
import org.objectweb.asm.tree.AbstractInsnNode;
|
||||||
|
import org.objectweb.asm.tree.InsnList;
|
||||||
|
import org.objectweb.asm.tree.LabelNode;
|
||||||
|
import org.objectweb.asm.tree.LineNumberNode;
|
||||||
|
import org.objectweb.asm.tree.LocalVariableNode;
|
||||||
|
import org.objectweb.asm.tree.MethodNode;
|
||||||
|
import org.objectweb.asm.tree.TryCatchBlockNode;
|
||||||
|
import org.teavm.model.BasicBlock;
|
||||||
|
import org.teavm.model.FieldReference;
|
||||||
|
import org.teavm.model.Instruction;
|
||||||
|
import org.teavm.model.InvokeDynamicInstruction;
|
||||||
|
import org.teavm.model.MethodDescriptor;
|
||||||
|
import org.teavm.model.MethodHandle;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
import org.teavm.model.Program;
|
||||||
|
import org.teavm.model.ReferenceCache;
|
||||||
|
import org.teavm.model.RuntimeConstant;
|
||||||
|
import org.teavm.model.TextLocation;
|
||||||
|
import org.teavm.model.TryCatchBlock;
|
||||||
|
import org.teavm.model.ValueType;
|
||||||
|
import org.teavm.model.Variable;
|
||||||
|
import org.teavm.model.instructions.ArrayElementType;
|
||||||
|
import org.teavm.model.instructions.ArrayLengthInstruction;
|
||||||
|
import org.teavm.model.instructions.AssignInstruction;
|
||||||
|
import org.teavm.model.instructions.BinaryBranchingCondition;
|
||||||
|
import org.teavm.model.instructions.BinaryBranchingInstruction;
|
||||||
|
import org.teavm.model.instructions.BinaryInstruction;
|
||||||
|
import org.teavm.model.instructions.BinaryOperation;
|
||||||
|
import org.teavm.model.instructions.BranchingCondition;
|
||||||
|
import org.teavm.model.instructions.BranchingInstruction;
|
||||||
|
import org.teavm.model.instructions.CastInstruction;
|
||||||
|
import org.teavm.model.instructions.CastIntegerDirection;
|
||||||
|
import org.teavm.model.instructions.CastIntegerInstruction;
|
||||||
|
import org.teavm.model.instructions.CastNumberInstruction;
|
||||||
|
import org.teavm.model.instructions.ClassConstantInstruction;
|
||||||
|
import org.teavm.model.instructions.CloneArrayInstruction;
|
||||||
|
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.EmptyInstruction;
|
||||||
|
import org.teavm.model.instructions.ExitInstruction;
|
||||||
|
import org.teavm.model.instructions.FloatConstantInstruction;
|
||||||
|
import org.teavm.model.instructions.GetElementInstruction;
|
||||||
|
import org.teavm.model.instructions.GetFieldInstruction;
|
||||||
|
import org.teavm.model.instructions.IntegerConstantInstruction;
|
||||||
|
import org.teavm.model.instructions.IntegerSubtype;
|
||||||
|
import org.teavm.model.instructions.InvocationType;
|
||||||
|
import org.teavm.model.instructions.InvokeInstruction;
|
||||||
|
import org.teavm.model.instructions.IsInstanceInstruction;
|
||||||
|
import org.teavm.model.instructions.JumpInstruction;
|
||||||
|
import org.teavm.model.instructions.LongConstantInstruction;
|
||||||
|
import org.teavm.model.instructions.MonitorEnterInstruction;
|
||||||
|
import org.teavm.model.instructions.MonitorExitInstruction;
|
||||||
|
import org.teavm.model.instructions.NegateInstruction;
|
||||||
|
import org.teavm.model.instructions.NullConstantInstruction;
|
||||||
|
import org.teavm.model.instructions.NumericOperandType;
|
||||||
|
import org.teavm.model.instructions.PutElementInstruction;
|
||||||
|
import org.teavm.model.instructions.PutFieldInstruction;
|
||||||
|
import org.teavm.model.instructions.RaiseInstruction;
|
||||||
|
import org.teavm.model.instructions.StringConstantInstruction;
|
||||||
|
import org.teavm.model.instructions.SwitchInstruction;
|
||||||
|
import org.teavm.model.instructions.SwitchTableEntry;
|
||||||
|
import org.teavm.model.instructions.UnwrapArrayInstruction;
|
||||||
import org.teavm.model.util.ProgramUtils;
|
import org.teavm.model.util.ProgramUtils;
|
||||||
|
import org.teavm.model.util.TransitionExtractor;
|
||||||
|
|
||||||
public class ProgramParser {
|
public class ProgramParser {
|
||||||
private ReferenceCache referenceCache;
|
private ReferenceCache referenceCache;
|
||||||
|
@ -42,7 +116,6 @@ public class ProgramParser {
|
||||||
private List<BasicBlock> basicBlocks = new ArrayList<>();
|
private List<BasicBlock> basicBlocks = new ArrayList<>();
|
||||||
private int minLocal;
|
private int minLocal;
|
||||||
private Program program;
|
private Program program;
|
||||||
private String currentClassName;
|
|
||||||
private Map<Integer, List<LocalVariableNode>> localVariableMap = new HashMap<>();
|
private Map<Integer, List<LocalVariableNode>> localVariableMap = new HashMap<>();
|
||||||
private Map<Instruction, Map<Integer, String>> variableDebugNames = new HashMap<>();
|
private Map<Instruction, Map<Integer, String>> variableDebugNames = new HashMap<>();
|
||||||
|
|
||||||
|
@ -86,9 +159,8 @@ public class ProgramParser {
|
||||||
this.fileName = fileName;
|
this.fileName = fileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Program parse(MethodNode method, String className) {
|
public Program parse(MethodNode method) {
|
||||||
program = new Program();
|
program = new Program();
|
||||||
this.currentClassName = className;
|
|
||||||
InsnList instructions = method.instructions;
|
InsnList instructions = method.instructions;
|
||||||
if (instructions.size() == 0) {
|
if (instructions.size() == 0) {
|
||||||
return program;
|
return program;
|
||||||
|
@ -315,7 +387,7 @@ public class ProgramParser {
|
||||||
if (lastInsn == null) {
|
if (lastInsn == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
InstructionTransitionExtractor extractor = new InstructionTransitionExtractor();
|
TransitionExtractor extractor = new TransitionExtractor();
|
||||||
lastInsn.acceptVisitor(extractor);
|
lastInsn.acceptVisitor(extractor);
|
||||||
return extractor.getTargets() != null;
|
return extractor.getTargets() != null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,6 +70,11 @@ public final class ExceptionHandling {
|
||||||
throw new ClassCastException();
|
throw new ClassCastException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Unmanaged
|
||||||
|
public static void throwNullPointerException() {
|
||||||
|
throw new NullPointerException();
|
||||||
|
}
|
||||||
|
|
||||||
@Unmanaged
|
@Unmanaged
|
||||||
public static int callStackSize() {
|
public static int callStackSize() {
|
||||||
Address stackFrame = ShadowStack.getStackTop();
|
Address stackFrame = ShadowStack.getStackTop();
|
||||||
|
|
|
@ -63,6 +63,7 @@ import org.teavm.model.optimization.LoopInvariantMotion;
|
||||||
import org.teavm.model.optimization.MethodOptimization;
|
import org.teavm.model.optimization.MethodOptimization;
|
||||||
import org.teavm.model.optimization.MethodOptimizationContext;
|
import org.teavm.model.optimization.MethodOptimizationContext;
|
||||||
import org.teavm.model.optimization.RedundantJumpElimination;
|
import org.teavm.model.optimization.RedundantJumpElimination;
|
||||||
|
import org.teavm.model.optimization.RedundantNullCheckElimination;
|
||||||
import org.teavm.model.optimization.ScalarReplacement;
|
import org.teavm.model.optimization.ScalarReplacement;
|
||||||
import org.teavm.model.optimization.UnreachableBasicBlockElimination;
|
import org.teavm.model.optimization.UnreachableBasicBlockElimination;
|
||||||
import org.teavm.model.optimization.UnusedVariableElimination;
|
import org.teavm.model.optimization.UnusedVariableElimination;
|
||||||
|
@ -512,6 +513,8 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
||||||
MethodOptimizationContextImpl context = new MethodOptimizationContextImpl(method, classSource);
|
MethodOptimizationContextImpl context = new MethodOptimizationContextImpl(method, classSource);
|
||||||
if (optimizedProgram == null) {
|
if (optimizedProgram == null) {
|
||||||
optimizedProgram = ProgramUtils.copy(method.getProgram());
|
optimizedProgram = ProgramUtils.copy(method.getProgram());
|
||||||
|
target.beforeOptimizations(optimizedProgram, method, classSource);
|
||||||
|
|
||||||
if (optimizedProgram.basicBlockCount() > 0) {
|
if (optimizedProgram.basicBlockCount() > 0) {
|
||||||
boolean changed;
|
boolean changed;
|
||||||
do {
|
do {
|
||||||
|
@ -578,6 +581,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
||||||
}
|
}
|
||||||
optimizations.add(new GlobalValueNumbering(optimizationLevel == TeaVMOptimizationLevel.SIMPLE));
|
optimizations.add(new GlobalValueNumbering(optimizationLevel == TeaVMOptimizationLevel.SIMPLE));
|
||||||
if (optimizationLevel.ordinal() >= TeaVMOptimizationLevel.ADVANCED.ordinal()) {
|
if (optimizationLevel.ordinal() >= TeaVMOptimizationLevel.ADVANCED.ordinal()) {
|
||||||
|
optimizations.add(new RedundantNullCheckElimination());
|
||||||
optimizations.add(new ConstantConditionElimination());
|
optimizations.add(new ConstantConditionElimination());
|
||||||
optimizations.add(new RedundantJumpElimination());
|
optimizations.add(new RedundantJumpElimination());
|
||||||
optimizations.add(new UnusedVariableElimination());
|
optimizations.add(new UnusedVariableElimination());
|
||||||
|
|
|
@ -39,6 +39,8 @@ public interface TeaVMTarget {
|
||||||
|
|
||||||
void contributeDependencies(DependencyAnalyzer dependencyAnalyzer);
|
void contributeDependencies(DependencyAnalyzer dependencyAnalyzer);
|
||||||
|
|
||||||
|
void beforeOptimizations(Program program, MethodReader method, ListableClassReaderSource classSource);
|
||||||
|
|
||||||
void afterOptimizations(Program program, MethodReader method, ListableClassReaderSource classSource);
|
void afterOptimizations(Program program, MethodReader method, ListableClassReaderSource classSource);
|
||||||
|
|
||||||
void emit(ListableClassHolderSource classes, BuildTarget buildTarget, String outputName) throws IOException;
|
void emit(ListableClassHolderSource classes, BuildTarget buildTarget, String outputName) throws IOException;
|
||||||
|
|
|
@ -85,6 +85,11 @@ public class NullnessAnalysisTest {
|
||||||
test();
|
test();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void nullAndNull() {
|
||||||
|
test();
|
||||||
|
}
|
||||||
|
|
||||||
private void test() {
|
private void test() {
|
||||||
String baseName = "model/analysis/nullness/" + name.getMethodName();
|
String baseName = "model/analysis/nullness/" + name.getMethodName();
|
||||||
String originalResourceName = baseName + ".original.txt";
|
String originalResourceName = baseName + ".original.txt";
|
||||||
|
|
|
@ -4,18 +4,16 @@ $start
|
||||||
@v := invokeStatic `Foo.get()LFoo;`
|
@v := invokeStatic `Foo.get()LFoo;`
|
||||||
if @v === null then goto $ifNull else goto $ifNotNull
|
if @v === null then goto $ifNull else goto $ifNotNull
|
||||||
$ifNull
|
$ifNull
|
||||||
@v_1 := null
|
|
||||||
invokeVirtual `Foo.bar()V` @v_1
|
invokeVirtual `Foo.bar()V` @v_1
|
||||||
@v_2 := nullCheck @v_1
|
@v_3 := nullCheck @v_1
|
||||||
goto $join
|
goto $join
|
||||||
$ifNotNull
|
$ifNotNull
|
||||||
@v_3 := nullCheck @v
|
invokeVirtual `Foo.baz()V` @v_2
|
||||||
invokeVirtual `Foo.baz()V` @v_3
|
@v_4 := nullCheck @v_2
|
||||||
goto $join
|
goto $join
|
||||||
$join
|
$join
|
||||||
return
|
return
|
||||||
|
|
||||||
// NULLABLE v
|
// NULLABLE v
|
||||||
// NULLABLE v_1
|
// NULLABLE v_1
|
||||||
// NOT_NULL v_2
|
|
||||||
// NOT_NULL v_3
|
// NOT_NULL v_3
|
|
@ -4,12 +4,14 @@ $start
|
||||||
@a := invokeStatic `Foo.f()LBar;`
|
@a := invokeStatic `Foo.f()LBar;`
|
||||||
if @a === null then goto $joint else goto $ifNonNull
|
if @a === null then goto $joint else goto $ifNonNull
|
||||||
$ifNonNull
|
$ifNonNull
|
||||||
@a_1 := nullCheck @a
|
|
||||||
@b := invokeStatic `Foo.g()LBar;`
|
@b := invokeStatic `Foo.g()LBar;`
|
||||||
if @b !== null then goto $joint else goto $ifNull
|
if @b !== null then goto $joint else goto $ifNull
|
||||||
$ifNull
|
$ifNull
|
||||||
@b_1 := null
|
return @a_2
|
||||||
return @a_1
|
|
||||||
$joint
|
$joint
|
||||||
@c := phi @a from $start, @b from $ifNonNull
|
@c := phi @a_1 from $start, @b_1 from $ifNonNull
|
||||||
return @c
|
return @c
|
||||||
|
|
||||||
|
// NULLABLE c
|
||||||
|
// NULLABLE a_1
|
||||||
|
// NOT_NULL a_2
|
|
@ -0,0 +1,25 @@
|
||||||
|
var @this as this
|
||||||
|
|
||||||
|
$start
|
||||||
|
@a := invokeStatic `Foo.get()LFoo;`
|
||||||
|
@b := invokeStatic `Foo.get2()LFoo;`
|
||||||
|
if @a === null then goto $ifANull else goto $join
|
||||||
|
$ifANull
|
||||||
|
@tmp := @a_1
|
||||||
|
if @b === null then goto $ifBNull else goto $join
|
||||||
|
$ifBNull
|
||||||
|
@p := @a_1
|
||||||
|
@q := @b_1
|
||||||
|
return
|
||||||
|
$join
|
||||||
|
@a_3 := phi @a_2 from $start, @a_1 from $ifANull
|
||||||
|
@b_3 := phi @b from $start, @b_2 from $ifANull
|
||||||
|
@u := @a_3
|
||||||
|
@v := @b_3
|
||||||
|
return
|
||||||
|
|
||||||
|
// NULLABLE tmp
|
||||||
|
// NULLABLE p
|
||||||
|
// NULLABLE q
|
||||||
|
// NULLABLE u
|
||||||
|
// NULLABLE v
|
|
@ -0,0 +1,17 @@
|
||||||
|
var @this as this
|
||||||
|
|
||||||
|
$start
|
||||||
|
@a := invokeStatic `Foo.get()LFoo;`
|
||||||
|
@b := invokeStatic `Foo.get2()LFoo;`
|
||||||
|
if @a === null then goto $ifANull else goto $join
|
||||||
|
$ifANull
|
||||||
|
@tmp := @a
|
||||||
|
if @b === null then goto $ifBNull else goto $join
|
||||||
|
$ifBNull
|
||||||
|
@p := @a
|
||||||
|
@q := @b
|
||||||
|
return
|
||||||
|
$join
|
||||||
|
@u := @a
|
||||||
|
@v := @b
|
||||||
|
return
|
|
@ -4,11 +4,9 @@ $start
|
||||||
@cond := invokeStatic `Cond.get()Ljava/lang/Object;`
|
@cond := invokeStatic `Cond.get()Ljava/lang/Object;`
|
||||||
if @cond === null then goto $ifNull else goto $ifNotNull
|
if @cond === null then goto $ifNull else goto $ifNotNull
|
||||||
$ifNull
|
$ifNull
|
||||||
@cond_1 := null
|
|
||||||
@a := 'qwe'
|
@a := 'qwe'
|
||||||
goto $join
|
goto $join
|
||||||
$ifNotNull
|
$ifNotNull
|
||||||
@cond_2 := nullCheck @cond
|
|
||||||
@b := invokeStatic `org.test.Foo.bar()Ljava/lang/String;`
|
@b := invokeStatic `org.test.Foo.bar()Ljava/lang/String;`
|
||||||
@b_1 := nullCheck @b
|
@b_1 := nullCheck @b
|
||||||
goto $join
|
goto $join
|
||||||
|
|
|
@ -55,7 +55,7 @@ import org.teavm.model.instructions.InvokeInstruction;
|
||||||
import org.teavm.model.instructions.JumpInstruction;
|
import org.teavm.model.instructions.JumpInstruction;
|
||||||
import org.teavm.model.instructions.LongConstantInstruction;
|
import org.teavm.model.instructions.LongConstantInstruction;
|
||||||
import org.teavm.model.instructions.NullConstantInstruction;
|
import org.teavm.model.instructions.NullConstantInstruction;
|
||||||
import org.teavm.model.util.InstructionTransitionExtractor;
|
import org.teavm.model.util.TransitionExtractor;
|
||||||
|
|
||||||
public final class MetaprogrammingImpl {
|
public final class MetaprogrammingImpl {
|
||||||
static Map<String, Integer> proxySuffixGenerators = new HashMap<>();
|
static Map<String, Integer> proxySuffixGenerators = new HashMap<>();
|
||||||
|
@ -340,7 +340,7 @@ public final class MetaprogrammingImpl {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void close() {
|
public static void close() {
|
||||||
InstructionTransitionExtractor transitionExtractor = new InstructionTransitionExtractor();
|
TransitionExtractor transitionExtractor = new TransitionExtractor();
|
||||||
BasicBlock block = generator.currentBlock();
|
BasicBlock block = generator.currentBlock();
|
||||||
Instruction lastInstruction = block.getLastInstruction();
|
Instruction lastInstruction = block.getLastInstruction();
|
||||||
if (lastInstruction != null) {
|
if (lastInstruction != null) {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user