mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-23 00:24:11 -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.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]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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