C/Wasm: fix bugs in GC root detector

This commit is contained in:
Alexey Andreev 2019-04-29 15:44:23 +03:00
parent 5a346fd3a4
commit ea5dd80199
2 changed files with 170 additions and 24 deletions

View File

@ -30,6 +30,7 @@ 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;
import org.teavm.common.DisjointSet;
import org.teavm.common.DominatorTree; import org.teavm.common.DominatorTree;
import org.teavm.common.Graph; import org.teavm.common.Graph;
import org.teavm.common.GraphBuilder; import org.teavm.common.GraphBuilder;
@ -64,19 +65,26 @@ import org.teavm.runtime.ShadowStack;
public class GCShadowStackContributor { public class GCShadowStackContributor {
private Characteristics characteristics; private Characteristics characteristics;
private NativePointerFinder nativePointerFinder;
public GCShadowStackContributor(Characteristics characteristics) { public GCShadowStackContributor(Characteristics characteristics) {
this.characteristics = characteristics; this.characteristics = characteristics;
nativePointerFinder = new NativePointerFinder(characteristics);
} }
public int contribute(Program program, MethodReader method) { public int contribute(Program program, MethodReader method) {
List<Map<Instruction, BitSet>> liveInInformation = findCallSiteLiveIns(program, method); List<Map<Instruction, BitSet>> liveInInformation = findCallSiteLiveIns(program, method);
boolean[] spilled = getAffectedVariables(liveInInformation, program); boolean[] spilled = getAffectedVariables(liveInInformation, program);
Graph interferenceGraph = buildInterferenceGraph(liveInInformation, program, spilled); int[] variableClasses = getVariableClasses(program);
Graph interferenceGraph = buildInterferenceGraph(liveInInformation, program, spilled, variableClasses);
int[] classColors = new int[interferenceGraph.size()];
Arrays.fill(classColors, -1);
new GraphColorer().colorize(interferenceGraph, classColors);
int[] colors = new int[interferenceGraph.size()]; int[] colors = new int[interferenceGraph.size()];
Arrays.fill(colors, -1); for (int i = 0; i < colors.length; ++i) {
new GraphColorer().colorize(interferenceGraph, colors); colors[i] = classColors[variableClasses[i]];
}
int usedColors = 0; int usedColors = 0;
for (int var = 0; var < colors.length; ++var) { for (int var = 0; var < colors.length; ++var) {
@ -91,7 +99,7 @@ public class GCShadowStackContributor {
return 0; return 0;
} }
// If a variable is spilled to stack, then phi which input this variable also spilled to stack // If a variable is spilled to stack, then phi which takes this variable as input also spilled to stack
// If all of phi inputs are spilled to stack, then we don't need to insert spilling instruction // If all of phi inputs are spilled to stack, then we don't need to insert spilling instruction
// for this phi. // for this phi.
List<Set<Phi>> destinationPhis = getDestinationPhis(program); List<Set<Phi>> destinationPhis = getDestinationPhis(program);
@ -108,19 +116,35 @@ public class GCShadowStackContributor {
return usedColors; return usedColors;
} }
private int[] getVariableClasses(Program program) {
DisjointSet variableClasses = new DisjointSet();
for (int i = 0; i < program.variableCount(); ++i) {
variableClasses.create();
}
for (BasicBlock block : program.getBasicBlocks()) {
for (Phi phi : block.getPhis()) {
for (Incoming incoming : phi.getIncomings()) {
variableClasses.union(phi.getReceiver().getIndex(), incoming.getValue().getIndex());
}
}
}
return variableClasses.pack(program.variableCount());
}
private void findAutoSpilledPhis(boolean[] spilled, List<Set<Phi>> destinationPhis, int[] inputCount, private void findAutoSpilledPhis(boolean[] spilled, List<Set<Phi>> destinationPhis, int[] inputCount,
boolean[] autoSpilled, int i) { boolean[] autoSpilled, int i) {
if (spilled[i]) { if (!spilled[i]) {
Set<Phi> phis = destinationPhis.get(i); return;
if (phis != null) { }
for (Phi phi : destinationPhis.get(i)) { Set<Phi> phis = destinationPhis.get(i);
int destination = phi.getReceiver().getIndex(); if (phis != null) {
autoSpilled[destination] = --inputCount[destination] == 0; for (Phi phi : destinationPhis.get(i)) {
if (!spilled[destination]) { int destination = phi.getReceiver().getIndex();
spilled[destination] = true; autoSpilled[destination] = --inputCount[destination] == 0;
if (i > destination) { if (!spilled[destination]) {
findAutoSpilledPhis(spilled, destinationPhis, inputCount, autoSpilled, destination); spilled[destination] = true;
} if (i > destination) {
findAutoSpilledPhis(spilled, destinationPhis, inputCount, autoSpilled, destination);
} }
} }
} }
@ -128,7 +152,8 @@ public class GCShadowStackContributor {
} }
private List<Map<Instruction, BitSet>> findCallSiteLiveIns(Program program, MethodReader method) { private List<Map<Instruction, BitSet>> findCallSiteLiveIns(Program program, MethodReader method) {
Graph cfg = ProgramUtils.buildControlFlowGraph(program); boolean[] nativePointers = nativePointerFinder.findNativePointers(method.getReference(), program);
TypeInferer typeInferer = new TypeInferer(); TypeInferer typeInferer = new TypeInferer();
typeInferer.inferTypes(program, method.getReference()); typeInferer.inferTypes(program, method.getReference());
List<Map<Instruction, BitSet>> liveInInformation = new ArrayList<>(); List<Map<Instruction, BitSet>> liveInInformation = new ArrayList<>();
@ -142,10 +167,7 @@ public class GCShadowStackContributor {
BasicBlock block = program.basicBlockAt(i); BasicBlock block = program.basicBlockAt(i);
Map<Instruction, BitSet> blockLiveIn = new HashMap<>(); Map<Instruction, BitSet> blockLiveIn = new HashMap<>();
liveInInformation.add(blockLiveIn); liveInInformation.add(blockLiveIn);
BitSet currentLiveOut = new BitSet(); BitSet currentLiveOut = livenessAnalyzer.liveOut(i);
for (int successor : cfg.outgoingEdges(i)) {
currentLiveOut.or(livenessAnalyzer.liveIn(successor));
}
for (Instruction insn = block.getLastInstruction(); insn != null; insn = insn.getPrevious()) { for (Instruction insn = block.getLastInstruction(); insn != null; insn = insn.getPrevious()) {
insn.acceptVisitor(defExtractor); insn.acceptVisitor(defExtractor);
@ -168,7 +190,7 @@ public class GCShadowStackContributor {
BitSet csLiveIn = (BitSet) currentLiveOut.clone(); BitSet csLiveIn = (BitSet) currentLiveOut.clone();
for (int v = csLiveIn.nextSetBit(0); v >= 0; v = csLiveIn.nextSetBit(v + 1)) { for (int v = csLiveIn.nextSetBit(0); v >= 0; v = csLiveIn.nextSetBit(v + 1)) {
if (!isReference(typeInferer, v)) { if (!isReference(typeInferer, v) || nativePointers[v]) {
csLiveIn.clear(v); csLiveIn.clear(v);
} }
} }
@ -185,7 +207,7 @@ public class GCShadowStackContributor {
} }
private Graph buildInterferenceGraph(List<Map<Instruction, BitSet>> liveInInformation, Program program, private Graph buildInterferenceGraph(List<Map<Instruction, BitSet>> liveInInformation, Program program,
boolean[] spilled) { boolean[] spilled, int[] variableClasses) {
GraphBuilder builder = new GraphBuilder(program.variableCount()); GraphBuilder builder = new GraphBuilder(program.variableCount());
for (Map<Instruction, BitSet> blockLiveIn : liveInInformation) { for (Map<Instruction, BitSet> blockLiveIn : liveInInformation) {
for (BitSet liveVarsSet : blockLiveIn.values()) { for (BitSet liveVarsSet : blockLiveIn.values()) {
@ -199,8 +221,8 @@ public class GCShadowStackContributor {
int a = liveVarArray[i]; int a = liveVarArray[i];
int b = liveVarArray[j]; int b = liveVarArray[j];
if (spilled[a] && spilled[b]) { if (spilled[a] && spilled[b]) {
builder.addEdge(a, b); builder.addEdge(variableClasses[a], variableClasses[b]);
builder.addEdge(b, a); builder.addEdge(variableClasses[b], variableClasses[a]);
} }
} }
} }

View File

@ -0,0 +1,124 @@
/*
* Copyright 2019 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 com.carrotsearch.hppc.IntArrayDeque;
import com.carrotsearch.hppc.IntDeque;
import java.util.List;
import org.teavm.common.Graph;
import org.teavm.common.GraphBuilder;
import org.teavm.interop.Address;
import org.teavm.model.BasicBlockReader;
import org.teavm.model.FieldReference;
import org.teavm.model.IncomingReader;
import org.teavm.model.MethodReference;
import org.teavm.model.PhiReader;
import org.teavm.model.ProgramReader;
import org.teavm.model.ValueType;
import org.teavm.model.VariableReader;
import org.teavm.model.instructions.AbstractInstructionReader;
import org.teavm.model.instructions.InvocationType;
public class NativePointerFinder {
private Characteristics characteristics;
public NativePointerFinder(Characteristics characteristics) {
this.characteristics = characteristics;
}
public boolean[] findNativePointers(MethodReference method, ProgramReader program) {
IntDeque stack = new IntArrayDeque();
for (int i = 0; i < method.parameterCount(); ++i) {
if (isNativeType(method.parameterType(i))) {
stack.addLast(i + 1);
}
}
Analyzer analyzer = new Analyzer(program.variableCount(), stack);
for (BasicBlockReader block : program.getBasicBlocks()) {
for (PhiReader phi : block.readPhis()) {
for (IncomingReader incoming : phi.readIncomings()) {
analyzer.assignmentGraph.addEdge(incoming.getValue().getIndex(), phi.getReceiver().getIndex());
}
}
block.readAllInstructions(analyzer);
}
boolean[] result = new boolean[program.variableCount()];
Graph graph = analyzer.assignmentGraph.build();
while (!stack.isEmpty()) {
int v = stack.removeLast();
if (result[v]) {
continue;
}
result[v] = true;
for (int succ : graph.outgoingEdges(v)) {
if (!result[succ]) {
stack.addLast(succ);
}
}
}
return result;
}
class Analyzer extends AbstractInstructionReader {
GraphBuilder assignmentGraph;
IntDeque steps;
Analyzer(int variableCount, IntDeque steps) {
assignmentGraph = new GraphBuilder(variableCount);
this.steps = steps;
}
@Override
public void assign(VariableReader receiver, VariableReader assignee) {
assignmentGraph.addEdge(assignee.getIndex(), receiver.getIndex());
}
@Override
public void cast(VariableReader receiver, VariableReader value, ValueType targetType) {
assignmentGraph.addEdge(value.getIndex(), receiver.getIndex());
}
@Override
public void invoke(VariableReader receiver, VariableReader instance, MethodReference method,
List<? extends VariableReader> arguments, InvocationType type) {
if (receiver != null && isNativeType(method.getReturnType())) {
steps.addLast(receiver.getIndex());
}
}
@Override
public void getField(VariableReader receiver, VariableReader instance, FieldReference field,
ValueType fieldType) {
if (isNativeType(fieldType)) {
steps.addLast(receiver.getIndex());
}
}
}
private boolean isNativeType(ValueType type) {
if (!(type instanceof ValueType.Object)) {
return false;
}
String className = ((ValueType.Object) type).getClassName();
return characteristics.isStructure(className) || className.equals(Address.class.getName());
}
}