mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 16:14:10 -08:00
C/Wasm: fix bugs in GC root detector
This commit is contained in:
parent
5a346fd3a4
commit
ea5dd80199
|
@ -30,6 +30,7 @@ import java.util.LinkedHashSet;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import org.teavm.common.DisjointSet;
|
||||
import org.teavm.common.DominatorTree;
|
||||
import org.teavm.common.Graph;
|
||||
import org.teavm.common.GraphBuilder;
|
||||
|
@ -64,19 +65,26 @@ import org.teavm.runtime.ShadowStack;
|
|||
|
||||
public class GCShadowStackContributor {
|
||||
private Characteristics characteristics;
|
||||
private NativePointerFinder nativePointerFinder;
|
||||
|
||||
public GCShadowStackContributor(Characteristics characteristics) {
|
||||
this.characteristics = characteristics;
|
||||
nativePointerFinder = new NativePointerFinder(characteristics);
|
||||
}
|
||||
|
||||
public int contribute(Program program, MethodReader method) {
|
||||
List<Map<Instruction, BitSet>> liveInInformation = findCallSiteLiveIns(program, method);
|
||||
|
||||
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()];
|
||||
Arrays.fill(colors, -1);
|
||||
new GraphColorer().colorize(interferenceGraph, colors);
|
||||
for (int i = 0; i < colors.length; ++i) {
|
||||
colors[i] = classColors[variableClasses[i]];
|
||||
}
|
||||
|
||||
int usedColors = 0;
|
||||
for (int var = 0; var < colors.length; ++var) {
|
||||
|
@ -91,7 +99,7 @@ public class GCShadowStackContributor {
|
|||
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
|
||||
// for this phi.
|
||||
List<Set<Phi>> destinationPhis = getDestinationPhis(program);
|
||||
|
@ -108,9 +116,26 @@ public class GCShadowStackContributor {
|
|||
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,
|
||||
boolean[] autoSpilled, int i) {
|
||||
if (spilled[i]) {
|
||||
if (!spilled[i]) {
|
||||
return;
|
||||
}
|
||||
Set<Phi> phis = destinationPhis.get(i);
|
||||
if (phis != null) {
|
||||
for (Phi phi : destinationPhis.get(i)) {
|
||||
|
@ -125,10 +150,10 @@ public class GCShadowStackContributor {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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.inferTypes(program, method.getReference());
|
||||
List<Map<Instruction, BitSet>> liveInInformation = new ArrayList<>();
|
||||
|
@ -142,10 +167,7 @@ public class GCShadowStackContributor {
|
|||
BasicBlock block = program.basicBlockAt(i);
|
||||
Map<Instruction, BitSet> blockLiveIn = new HashMap<>();
|
||||
liveInInformation.add(blockLiveIn);
|
||||
BitSet currentLiveOut = new BitSet();
|
||||
for (int successor : cfg.outgoingEdges(i)) {
|
||||
currentLiveOut.or(livenessAnalyzer.liveIn(successor));
|
||||
}
|
||||
BitSet currentLiveOut = livenessAnalyzer.liveOut(i);
|
||||
|
||||
for (Instruction insn = block.getLastInstruction(); insn != null; insn = insn.getPrevious()) {
|
||||
insn.acceptVisitor(defExtractor);
|
||||
|
@ -168,7 +190,7 @@ public class GCShadowStackContributor {
|
|||
|
||||
BitSet csLiveIn = (BitSet) currentLiveOut.clone();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -185,7 +207,7 @@ public class GCShadowStackContributor {
|
|||
}
|
||||
|
||||
private Graph buildInterferenceGraph(List<Map<Instruction, BitSet>> liveInInformation, Program program,
|
||||
boolean[] spilled) {
|
||||
boolean[] spilled, int[] variableClasses) {
|
||||
GraphBuilder builder = new GraphBuilder(program.variableCount());
|
||||
for (Map<Instruction, BitSet> blockLiveIn : liveInInformation) {
|
||||
for (BitSet liveVarsSet : blockLiveIn.values()) {
|
||||
|
@ -199,8 +221,8 @@ public class GCShadowStackContributor {
|
|||
int a = liveVarArray[i];
|
||||
int b = liveVarArray[j];
|
||||
if (spilled[a] && spilled[b]) {
|
||||
builder.addEdge(a, b);
|
||||
builder.addEdge(b, a);
|
||||
builder.addEdge(variableClasses[a], variableClasses[b]);
|
||||
builder.addEdge(variableClasses[b], variableClasses[a]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user