diff --git a/core/src/main/java/org/teavm/model/lowlevel/GCShadowStackContributor.java b/core/src/main/java/org/teavm/model/lowlevel/GCShadowStackContributor.java index e1f434fba..bf716e58f 100644 --- a/core/src/main/java/org/teavm/model/lowlevel/GCShadowStackContributor.java +++ b/core/src/main/java/org/teavm/model/lowlevel/GCShadowStackContributor.java @@ -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> 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> destinationPhis = getDestinationPhis(program); @@ -108,19 +116,35 @@ 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> destinationPhis, int[] inputCount, boolean[] autoSpilled, int i) { - if (spilled[i]) { - Set phis = destinationPhis.get(i); - if (phis != null) { - for (Phi phi : destinationPhis.get(i)) { - int destination = phi.getReceiver().getIndex(); - autoSpilled[destination] = --inputCount[destination] == 0; - if (!spilled[destination]) { - spilled[destination] = true; - if (i > destination) { - findAutoSpilledPhis(spilled, destinationPhis, inputCount, autoSpilled, destination); - } + if (!spilled[i]) { + return; + } + Set phis = destinationPhis.get(i); + if (phis != null) { + for (Phi phi : destinationPhis.get(i)) { + int destination = phi.getReceiver().getIndex(); + autoSpilled[destination] = --inputCount[destination] == 0; + if (!spilled[destination]) { + spilled[destination] = true; + if (i > destination) { + findAutoSpilledPhis(spilled, destinationPhis, inputCount, autoSpilled, destination); } } } @@ -128,7 +152,8 @@ public class GCShadowStackContributor { } private List> 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> liveInInformation = new ArrayList<>(); @@ -142,10 +167,7 @@ public class GCShadowStackContributor { BasicBlock block = program.basicBlockAt(i); Map 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> liveInInformation, Program program, - boolean[] spilled) { + boolean[] spilled, int[] variableClasses) { GraphBuilder builder = new GraphBuilder(program.variableCount()); for (Map 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]); } } } diff --git a/core/src/main/java/org/teavm/model/lowlevel/NativePointerFinder.java b/core/src/main/java/org/teavm/model/lowlevel/NativePointerFinder.java new file mode 100644 index 000000000..8fa025363 --- /dev/null +++ b/core/src/main/java/org/teavm/model/lowlevel/NativePointerFinder.java @@ -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 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()); + } +}