mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-23 00:24:11 -08:00
Fix bug in repeated field read elimination.
There's a case that was missing in this optimization. We install invalidation points on a block's dominance frontiers when the block contains som invalidation instructions. However, if a block is an entry to exception handler, state is always invalidated. This should be done since exception handler may recover and proceed with some code that follows try/catch block. Without this change code after try/catch inherits state of `try` block, which is invalid, since `catch` is another source from where we can get there. We can't rely on regular instruction analysis in `catch` blocks, since we get into `catch` from an unpredictable point.
This commit is contained in:
parent
6892c4f0ca
commit
8f68c64193
|
@ -28,7 +28,9 @@ import java.util.ArrayList;
|
||||||
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.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import org.teavm.common.DominatorTree;
|
import org.teavm.common.DominatorTree;
|
||||||
import org.teavm.common.Graph;
|
import org.teavm.common.Graph;
|
||||||
|
@ -40,6 +42,7 @@ import org.teavm.model.FieldReader;
|
||||||
import org.teavm.model.FieldReference;
|
import org.teavm.model.FieldReference;
|
||||||
import org.teavm.model.Instruction;
|
import org.teavm.model.Instruction;
|
||||||
import org.teavm.model.Program;
|
import org.teavm.model.Program;
|
||||||
|
import org.teavm.model.TryCatchBlock;
|
||||||
import org.teavm.model.analysis.AliasAnalysis;
|
import org.teavm.model.analysis.AliasAnalysis;
|
||||||
import org.teavm.model.instructions.AbstractInstructionVisitor;
|
import org.teavm.model.instructions.AbstractInstructionVisitor;
|
||||||
import org.teavm.model.instructions.AssignInstruction;
|
import org.teavm.model.instructions.AssignInstruction;
|
||||||
|
@ -316,6 +319,12 @@ public class RepeatedFieldReadElimination implements MethodOptimization {
|
||||||
int[][] domFrontiers = GraphUtils.findDominanceFrontiers(cfg, dom);
|
int[][] domFrontiers = GraphUtils.findDominanceFrontiers(cfg, dom);
|
||||||
everythingInvalid = new boolean[program.basicBlockCount()];
|
everythingInvalid = new boolean[program.basicBlockCount()];
|
||||||
invalidFields.addAll(Collections.nCopies(program.basicBlockCount(), null));
|
invalidFields.addAll(Collections.nCopies(program.basicBlockCount(), null));
|
||||||
|
boolean[] exceptionHandlers = new boolean[program.basicBlockCount()];
|
||||||
|
for (BasicBlock block : program.getBasicBlocks()) {
|
||||||
|
for (TryCatchBlock tryCatchBlock : block.getTryCatchBlocks()) {
|
||||||
|
exceptionHandlers[tryCatchBlock.getHandler().getIndex()] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
IntArrayDeque worklist = new IntArrayDeque();
|
IntArrayDeque worklist = new IntArrayDeque();
|
||||||
InstructionAnalyzer instructionAnalyzer = new InstructionAnalyzer();
|
InstructionAnalyzer instructionAnalyzer = new InstructionAnalyzer();
|
||||||
|
@ -325,26 +334,43 @@ public class RepeatedFieldReadElimination implements MethodOptimization {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Instruction instruction : block) {
|
Set<FieldAndInstance> fieldsToInvalidate = new LinkedHashSet<>();
|
||||||
instructionAnalyzer.reset();
|
boolean allInvalid = false;
|
||||||
instruction.acceptVisitor(instructionAnalyzer);
|
if (exceptionHandlers[block.getIndex()]) {
|
||||||
|
allInvalid = true;
|
||||||
|
} else {
|
||||||
|
for (Instruction instruction : block) {
|
||||||
|
instructionAnalyzer.reset();
|
||||||
|
instruction.acceptVisitor(instructionAnalyzer);
|
||||||
|
|
||||||
if (instructionAnalyzer.invalidatesAll) {
|
if (instructionAnalyzer.invalidatesAll) {
|
||||||
worklist.addLast(frontiers);
|
allInvalid = true;
|
||||||
while (!worklist.isEmpty()) {
|
fieldsToInvalidate.clear();
|
||||||
int target = worklist.removeFirst();
|
break;
|
||||||
if (!everythingInvalid[target]) {
|
} else if (instructionAnalyzer.invalidatedField != null
|
||||||
everythingInvalid[target] = true;
|
&& !isVolatile(instructionAnalyzer.invalidatedField)) {
|
||||||
invalidFields.set(target, null);
|
fieldsToInvalidate.add(new FieldAndInstance(instructionAnalyzer.invalidatedField,
|
||||||
worklist.addLast(domFrontiers[target]);
|
instructionAnalyzer.instance));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if (instructionAnalyzer.invalidatedField != null
|
}
|
||||||
&& !isVolatile(instructionAnalyzer.invalidatedField)) {
|
}
|
||||||
|
|
||||||
|
if (allInvalid) {
|
||||||
|
worklist.addLast(frontiers);
|
||||||
|
while (!worklist.isEmpty()) {
|
||||||
|
int target = worklist.removeFirst();
|
||||||
|
if (!everythingInvalid[target]) {
|
||||||
|
everythingInvalid[target] = true;
|
||||||
|
invalidFields.set(target, null);
|
||||||
|
worklist.addLast(domFrontiers[target]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (FieldAndInstance fieldAndInstance : fieldsToInvalidate) {
|
||||||
worklist.addLast(frontiers);
|
worklist.addLast(frontiers);
|
||||||
|
|
||||||
int instance = instructionAnalyzer.instance;
|
int instance = fieldAndInstance.instance;
|
||||||
FieldReference field = instructionAnalyzer.invalidatedField;
|
FieldReference field = fieldAndInstance.field;
|
||||||
|
|
||||||
while (!worklist.isEmpty()) {
|
while (!worklist.isEmpty()) {
|
||||||
int target = worklist.removeFirst();
|
int target = worklist.removeFirst();
|
||||||
|
@ -401,4 +427,31 @@ public class RepeatedFieldReadElimination implements MethodOptimization {
|
||||||
invalidatesAll = true;
|
invalidatesAll = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class FieldAndInstance {
|
||||||
|
final FieldReference field;
|
||||||
|
final int instance;
|
||||||
|
|
||||||
|
FieldAndInstance(FieldReference field, int instance) {
|
||||||
|
this.field = field;
|
||||||
|
this.instance = instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!(o instanceof FieldAndInstance)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
FieldAndInstance that = (FieldAndInstance) o;
|
||||||
|
return instance == that.instance && field.equals(that.field);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(field, instance);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,6 +86,11 @@ public class RepeatedFieldReadEliminationTest {
|
||||||
doTest();
|
doTest();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void readAfterException() {
|
||||||
|
doTest();
|
||||||
|
}
|
||||||
|
|
||||||
private void doTest() {
|
private void doTest() {
|
||||||
String originalPath = PREFIX + name.getMethodName() + ".original.txt";
|
String originalPath = PREFIX + name.getMethodName() + ".original.txt";
|
||||||
String expectedPath = PREFIX + name.getMethodName() + ".expected.txt";
|
String expectedPath = PREFIX + name.getMethodName() + ".expected.txt";
|
||||||
|
@ -127,6 +132,11 @@ public class RepeatedFieldReadEliminationTest {
|
||||||
getFoo.getModifiers().add(ElementModifier.NATIVE);
|
getFoo.getModifiers().add(ElementModifier.NATIVE);
|
||||||
foo.addMethod(getFoo);
|
foo.addMethod(getFoo);
|
||||||
|
|
||||||
|
MethodHolder getIntValue = new MethodHolder("getIntValue", ValueType.INTEGER);
|
||||||
|
getIntValue.getModifiers().add(ElementModifier.STATIC);
|
||||||
|
getIntValue.getModifiers().add(ElementModifier.NATIVE);
|
||||||
|
foo.addMethod(getIntValue);
|
||||||
|
|
||||||
classSource.putClassHolder(foo);
|
classSource.putClassHolder(foo);
|
||||||
|
|
||||||
MethodOptimizationContext context = new MethodOptimizationContext() {
|
MethodOptimizationContext context = new MethodOptimizationContext() {
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
var @this as this
|
||||||
|
|
||||||
|
$start
|
||||||
|
@o := invokeStatic `Foo.getFoo()LFoo;`
|
||||||
|
@v := invokeStatic `Foo.getIntValue()I`
|
||||||
|
field Foo.intField @o := @v as I
|
||||||
|
goto $exit
|
||||||
|
catch java.lang.RuntimeException goto $handler
|
||||||
|
$handler
|
||||||
|
goto $exit
|
||||||
|
$exit
|
||||||
|
@x := field Foo.intField @o as I
|
||||||
|
return
|
|
@ -0,0 +1,13 @@
|
||||||
|
var @this as this
|
||||||
|
|
||||||
|
$start
|
||||||
|
@o := invokeStatic `Foo.getFoo()LFoo;`
|
||||||
|
@v := invokeStatic `Foo.getIntValue()I`
|
||||||
|
field Foo.intField @o := @v as I
|
||||||
|
goto $exit
|
||||||
|
catch java.lang.RuntimeException goto $handler
|
||||||
|
$handler
|
||||||
|
goto $exit
|
||||||
|
$exit
|
||||||
|
@x := field Foo.intField @o as I
|
||||||
|
return
|
Loading…
Reference in New Issue
Block a user