mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2025-01-05 06:34:11 -08:00
Fix bug in nullness analysis when variable graph has irreducible loops
This commit is contained in:
parent
1fabe4c5b9
commit
3c8184c3b7
|
@ -169,7 +169,7 @@ class NullnessInformationBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assignmentGraph = GraphUtils.removeLoops(builder.build());
|
assignmentGraph = removeLoops(builder.build());
|
||||||
|
|
||||||
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) {
|
||||||
|
@ -177,6 +177,46 @@ class NullnessInformationBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Graph removeLoops(Graph graph) {
|
||||||
|
int[][] sccs = GraphUtils.findStronglyConnectedComponents(graph);
|
||||||
|
if (sccs.length == 0) {
|
||||||
|
return graph;
|
||||||
|
}
|
||||||
|
|
||||||
|
int[] backMap = new int[graph.size()];
|
||||||
|
for (int i = 0; i < backMap.length; ++i) {
|
||||||
|
backMap[i] = i;
|
||||||
|
}
|
||||||
|
boolean hasNonTrivialSccs = false;
|
||||||
|
GraphBuilder builder = new GraphBuilder(graph.size());
|
||||||
|
for (int[] scc : sccs) {
|
||||||
|
if (scc.length == 1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
hasNonTrivialSccs = true;
|
||||||
|
|
||||||
|
for (int i = 1; i < scc.length; ++i) {
|
||||||
|
backMap[scc[i]] = scc[0];
|
||||||
|
builder.addEdge(scc[0], scc[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!hasNonTrivialSccs) {
|
||||||
|
return graph;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < graph.size(); ++i) {
|
||||||
|
for (int j : graph.outgoingEdges(i)) {
|
||||||
|
if (backMap[j] == i) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.addEdge(i, backMap[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
private void initNullness(IntDeque queue) {
|
private void initNullness(IntDeque queue) {
|
||||||
NullnessInitVisitor visitor = new NullnessInitVisitor(queue);
|
NullnessInitVisitor visitor = new NullnessInitVisitor(queue);
|
||||||
for (BasicBlock block : program.getBasicBlocks()) {
|
for (BasicBlock block : program.getBasicBlocks()) {
|
||||||
|
|
|
@ -16,7 +16,9 @@
|
||||||
package org.teavm.model.analysis.test;
|
package org.teavm.model.analysis.test;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
import com.carrotsearch.hppc.ObjectByteHashMap;
|
import com.carrotsearch.hppc.ObjectByteHashMap;
|
||||||
import com.carrotsearch.hppc.ObjectByteMap;
|
import com.carrotsearch.hppc.ObjectByteMap;
|
||||||
import com.carrotsearch.hppc.cursors.ObjectCursor;
|
import com.carrotsearch.hppc.cursors.ObjectCursor;
|
||||||
|
@ -24,6 +26,7 @@ import java.io.BufferedReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
|
@ -44,6 +47,7 @@ public class NullnessAnalysisTest {
|
||||||
|
|
||||||
private static final String NOT_NULL_DIRECTIVE = "// NOT_NULL ";
|
private static final String NOT_NULL_DIRECTIVE = "// NOT_NULL ";
|
||||||
private static final String NULLABLE_DIRECTIVE = "// NULLABLE ";
|
private static final String NULLABLE_DIRECTIVE = "// NULLABLE ";
|
||||||
|
private static final String NULL_DIRECTIVE = "// NULL ";
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void simple() {
|
public void simple() {
|
||||||
|
@ -90,6 +94,11 @@ public class NullnessAnalysisTest {
|
||||||
test();
|
test();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void irreduciblePhiLoop() {
|
||||||
|
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";
|
||||||
|
@ -115,8 +124,20 @@ public class NullnessAnalysisTest {
|
||||||
String varName = varNameCursor.value;
|
String varName = varNameCursor.value;
|
||||||
Variable var = variablesByLabel.get(varName);
|
Variable var = variablesByLabel.get(varName);
|
||||||
assertNotNull("Variable " + varName + " is missing", var);
|
assertNotNull("Variable " + varName + " is missing", var);
|
||||||
boolean notNull = expectedNullness.get(varName) != 0;
|
byte nullness = expectedNullness.get(varName);
|
||||||
assertEquals("Variable " + varName + " non-null", notNull, information.isNotNull(var));
|
switch (nullness) {
|
||||||
|
case 0:
|
||||||
|
assertTrue("Variable " + varName + " must be null", information.isNull(var));
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
assertTrue("Variable " + varName + " must be non-null", information.isNotNull(var));
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
assertFalse("Variable " + varName + " must not be null", information.isNull(var));
|
||||||
|
assertFalse("Variable " + varName + " must not be non-null", information.isNotNull(var));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
information.dispose();
|
information.dispose();
|
||||||
|
@ -127,7 +148,7 @@ public class NullnessAnalysisTest {
|
||||||
private ObjectByteMap<String> extractExpectedNullness(String name) {
|
private ObjectByteMap<String> extractExpectedNullness(String name) {
|
||||||
ClassLoader classLoader = NullnessAnalysisTest.class.getClassLoader();
|
ClassLoader classLoader = NullnessAnalysisTest.class.getClassLoader();
|
||||||
try (InputStream input = classLoader.getResourceAsStream(name);
|
try (InputStream input = classLoader.getResourceAsStream(name);
|
||||||
BufferedReader reader = new BufferedReader(new InputStreamReader(input, "UTF-8"))) {
|
BufferedReader reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8))) {
|
||||||
ObjectByteMap<String> result = new ObjectByteHashMap<>();
|
ObjectByteMap<String> result = new ObjectByteHashMap<>();
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
|
@ -136,7 +157,13 @@ public class NullnessAnalysisTest {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
int index = line.indexOf(NOT_NULL_DIRECTIVE);
|
int index = line.indexOf(NULL_DIRECTIVE);
|
||||||
|
if (index >= 0) {
|
||||||
|
String variable = line.substring(index + NULL_DIRECTIVE.length()).trim();
|
||||||
|
result.put(variable, (byte) 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
index = line.indexOf(NOT_NULL_DIRECTIVE);
|
||||||
if (index >= 0) {
|
if (index >= 0) {
|
||||||
String variable = line.substring(index + NOT_NULL_DIRECTIVE.length()).trim();
|
String variable = line.substring(index + NOT_NULL_DIRECTIVE.length()).trim();
|
||||||
result.put(variable, (byte) 1);
|
result.put(variable, (byte) 1);
|
||||||
|
@ -145,7 +172,7 @@ public class NullnessAnalysisTest {
|
||||||
index = line.indexOf(NULLABLE_DIRECTIVE);
|
index = line.indexOf(NULLABLE_DIRECTIVE);
|
||||||
if (index >= 0) {
|
if (index >= 0) {
|
||||||
String variable = line.substring(index + NULLABLE_DIRECTIVE.length()).trim();
|
String variable = line.substring(index + NULLABLE_DIRECTIVE.length()).trim();
|
||||||
result.put(variable, (byte) 0);
|
result.put(variable, (byte) 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,5 +15,5 @@ $join
|
||||||
return
|
return
|
||||||
|
|
||||||
// NULLABLE v
|
// NULLABLE v
|
||||||
// NULLABLE v_1
|
// NULL v_1
|
||||||
// NOT_NULL v_3
|
// NOT_NULL v_3
|
|
@ -0,0 +1,29 @@
|
||||||
|
var @this as this
|
||||||
|
$start
|
||||||
|
@init := 'qwe'
|
||||||
|
@n := null
|
||||||
|
@xInit := 0
|
||||||
|
goto $head
|
||||||
|
$head
|
||||||
|
@a := phi @init from $start, @b from $bodyEnd
|
||||||
|
@x := phi @xInit from $start, @xUpdate from $bodyEnd
|
||||||
|
@twenty := 20
|
||||||
|
@xCmp := @x compareTo @twenty as int
|
||||||
|
if @xCmp == 0 then goto $exit else goto $body
|
||||||
|
$body
|
||||||
|
@ten := 10
|
||||||
|
@cmp := @x compareTo @ten as int
|
||||||
|
if @cmp == 0 then goto $update else goto $bodyEnd
|
||||||
|
$update
|
||||||
|
goto $bodyEnd
|
||||||
|
$bodyEnd
|
||||||
|
@b := phi @a from $body, @n from $update
|
||||||
|
@one := 1
|
||||||
|
@xUpdate := @x + @one as int
|
||||||
|
goto $head
|
||||||
|
$exit
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
// NULLABLE a
|
||||||
|
// NULLABLE b
|
|
@ -0,0 +1,25 @@
|
||||||
|
var @this as this
|
||||||
|
$start
|
||||||
|
@init := 'qwe'
|
||||||
|
@n := null
|
||||||
|
@xInit := 0
|
||||||
|
goto $head
|
||||||
|
$head
|
||||||
|
@a := phi @init from $start, @b from $bodyEnd
|
||||||
|
@x := phi @xInit from $start, @xUpdate from $bodyEnd
|
||||||
|
@twenty := 20
|
||||||
|
@xCmp := @x compareTo @twenty as int
|
||||||
|
if @xCmp == 0 then goto $exit else goto $body
|
||||||
|
$body
|
||||||
|
@ten := 10
|
||||||
|
@cmp := @x compareTo @ten as int
|
||||||
|
if @cmp == 0 then goto $update else goto $bodyEnd
|
||||||
|
$update
|
||||||
|
goto $bodyEnd
|
||||||
|
$bodyEnd
|
||||||
|
@b := phi @a from $body, @n from $update
|
||||||
|
@one := 1
|
||||||
|
@xUpdate := @x + @one as int
|
||||||
|
goto $head
|
||||||
|
$exit
|
||||||
|
return
|
|
@ -13,5 +13,5 @@ $joint
|
||||||
return @c
|
return @c
|
||||||
|
|
||||||
// NULLABLE c
|
// NULLABLE c
|
||||||
// NULLABLE a_1
|
// NULL a_1
|
||||||
// NOT_NULL a_2
|
// NOT_NULL a_2
|
|
@ -18,8 +18,8 @@ $join
|
||||||
@v := @b_3
|
@v := @b_3
|
||||||
return
|
return
|
||||||
|
|
||||||
// NULLABLE tmp
|
// NULL tmp
|
||||||
// NULLABLE p
|
// NULL p
|
||||||
// NULLABLE q
|
// NULL q
|
||||||
// NULLABLE u
|
// NULLABLE u
|
||||||
// NULLABLE v
|
// NULLABLE v
|
Loading…
Reference in New Issue
Block a user