From fc3d36ec4cc6a9c1d272ca85be2a1bcb525cbc91 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Wed, 21 Sep 2016 00:47:55 +0300 Subject: [PATCH] WASM: improving algorithm that generates instructions to store variables in shadow stack --- .../org/teavm/backend/wasm/WasmRuntime.java | 1 - .../src/main/java/org/teavm/common/Graph.java | 4 - .../teavm/common/MutableDirectedGraph.java | 4 - .../org/teavm/common/MutableGraphNode.java | 23 +- .../GcRootMaintainingTransformer.java | 275 +++++++++++++++--- .../org/teavm/model/util/GraphColorer.java | 17 +- .../teavm/model/util/RegisterAllocator.java | 2 +- 7 files changed, 259 insertions(+), 67 deletions(-) diff --git a/core/src/main/java/org/teavm/backend/wasm/WasmRuntime.java b/core/src/main/java/org/teavm/backend/wasm/WasmRuntime.java index 1ed670676..23609a12b 100644 --- a/core/src/main/java/org/teavm/backend/wasm/WasmRuntime.java +++ b/core/src/main/java/org/teavm/backend/wasm/WasmRuntime.java @@ -246,7 +246,6 @@ public final class WasmRuntime { public static Address allocStack(int size) { Address result = stack.add(4); stack = result.add(size << 2); - fillZero(result, size << 2); stack.putInt(size); return result; } diff --git a/core/src/main/java/org/teavm/common/Graph.java b/core/src/main/java/org/teavm/common/Graph.java index 85a4da62d..f0e9b34a1 100644 --- a/core/src/main/java/org/teavm/common/Graph.java +++ b/core/src/main/java/org/teavm/common/Graph.java @@ -15,10 +15,6 @@ */ package org.teavm.common; -/** - * - * @author Alexey Andreev - */ public interface Graph { int size(); diff --git a/core/src/main/java/org/teavm/common/MutableDirectedGraph.java b/core/src/main/java/org/teavm/common/MutableDirectedGraph.java index 362e7d276..38c532cab 100644 --- a/core/src/main/java/org/teavm/common/MutableDirectedGraph.java +++ b/core/src/main/java/org/teavm/common/MutableDirectedGraph.java @@ -21,10 +21,6 @@ import com.carrotsearch.hppc.cursors.IntCursor; import java.util.ArrayList; import java.util.List; -/** - * - * @author Alexey Andreev - */ public class MutableDirectedGraph implements Graph { private List successors = new ArrayList<>(); private List predecessors = new ArrayList<>(); diff --git a/core/src/main/java/org/teavm/common/MutableGraphNode.java b/core/src/main/java/org/teavm/common/MutableGraphNode.java index 29e9a6640..2444b28e2 100644 --- a/core/src/main/java/org/teavm/common/MutableGraphNode.java +++ b/core/src/main/java/org/teavm/common/MutableGraphNode.java @@ -15,12 +15,10 @@ */ package org.teavm.common; +import com.carrotsearch.hppc.ObjectIntMap; +import com.carrotsearch.hppc.ObjectIntOpenHashMap; import java.util.*; -/** - * - * @author Alexey Andreev - */ public class MutableGraphNode { private int tag; final Map edges = new LinkedHashMap<>(); @@ -77,4 +75,21 @@ public class MutableGraphNode { } return sb.toString(); } + + public static Graph toGraph(List nodes) { + ObjectIntMap map = new ObjectIntOpenHashMap<>(); + for (int i = 0; i < nodes.size(); ++i) { + map.put(nodes.get(i), i); + } + + GraphBuilder builder = new GraphBuilder(nodes.size()); + for (int i = 0; i < nodes.size(); ++i) { + for (MutableGraphEdge edge : nodes.get(i).getEdges()) { + int successor = map.get(edge.getSecond()); + builder.addEdge(i, successor); + } + } + + return builder.build(); + } } diff --git a/core/src/main/java/org/teavm/model/lowlevel/GcRootMaintainingTransformer.java b/core/src/main/java/org/teavm/model/lowlevel/GcRootMaintainingTransformer.java index e6ac8a5f8..29df5627a 100644 --- a/core/src/main/java/org/teavm/model/lowlevel/GcRootMaintainingTransformer.java +++ b/core/src/main/java/org/teavm/model/lowlevel/GcRootMaintainingTransformer.java @@ -15,13 +15,21 @@ */ package org.teavm.model.lowlevel; +import com.carrotsearch.hppc.IntArrayList; import com.carrotsearch.hppc.IntObjectMap; import com.carrotsearch.hppc.IntObjectOpenHashMap; import com.carrotsearch.hppc.cursors.ObjectCursor; import java.util.ArrayList; +import java.util.Arrays; import java.util.BitSet; +import java.util.Collections; +import java.util.HashSet; import java.util.List; +import java.util.Set; +import org.teavm.common.DominatorTree; import org.teavm.common.Graph; +import org.teavm.common.GraphBuilder; +import org.teavm.common.GraphUtils; import org.teavm.interop.NoGC; import org.teavm.model.BasicBlock; import org.teavm.model.ClassReader; @@ -44,6 +52,7 @@ import org.teavm.model.instructions.InvokeInstruction; import org.teavm.model.instructions.JumpInstruction; import org.teavm.model.instructions.RaiseInstruction; import org.teavm.model.util.DefinitionExtractor; +import org.teavm.model.util.GraphColorer; import org.teavm.model.util.LivenessAnalyzer; import org.teavm.model.util.ProgramUtils; import org.teavm.model.util.TypeInferer; @@ -59,14 +68,61 @@ public class GcRootMaintainingTransformer { } public void apply(Program program, MethodReader method) { - if (!requiresGc(method.getReference())) { + if (!requiresGC(method.getReference())) { return; } List> liveInInformation = findCallSiteLiveIns(program, method); - int maxDepth = putLiveInGcRoots(program, liveInInformation); - if (maxDepth > 0) { - addStackAllocation(program, maxDepth); - addStackRelease(program, maxDepth); + + Graph interferenceGraph = buildInterferenceGraph(liveInInformation, program); + boolean[] spilled = getAffectedVariables(liveInInformation, program); + int[] colors = new int[interferenceGraph.size()]; + Arrays.fill(colors, -1); + new GraphColorer().colorize(interferenceGraph, colors); + + int usedColors = 0; + for (int var = 0; var < colors.length; ++var) { + if (spilled[var]) { + usedColors = Math.max(usedColors, colors[var]); + colors[var]--; + } + } + if (usedColors == 0) { + return; + } + + // If a variable is spilled to stack, then phi which input this variable 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); + int[] inputCount = getInputCount(program); + boolean[] autoSpilled = new boolean[spilled.length]; + for (int i = 0; i < spilled.length; ++i) { + findAutoSpilledPhis(spilled, destinationPhis, inputCount, autoSpilled, i); + } + + List> liveInStores = reduceGcRootStores(program, usedColors, liveInInformation, + colors, autoSpilled); + putLiveInGcRoots(program, liveInStores); + addStackAllocation(program, usedColors); + addStackRelease(program, usedColors); + } + + 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); + } + } + } + } } } @@ -102,7 +158,7 @@ public class GcRootMaintainingTransformer { if (insn instanceof InvokeInstruction || insn instanceof InitClassInstruction || insn instanceof ConstructInstruction || insn instanceof ConstructArrayInstruction || insn instanceof CloneArrayInstruction || insn instanceof RaiseInstruction) { - if (insn instanceof InvokeInstruction && !requiresGc(((InvokeInstruction) insn).getMethod())) { + if (insn instanceof InvokeInstruction && !requiresGC(((InvokeInstruction) insn).getMethod())) { continue; } @@ -124,69 +180,202 @@ public class GcRootMaintainingTransformer { return liveInInformation; } - private int putLiveInGcRoots(Program program, List> liveInInformation) { - int maxDepth = 0; - for (IntObjectMap liveInsMap : liveInInformation) { - for (ObjectCursor liveIns : liveInsMap.values()) { - maxDepth = Math.max(maxDepth, liveIns.value.cardinality()); + private Graph buildInterferenceGraph(List> liveInInformation, Program program) { + GraphBuilder builder = new GraphBuilder(program.variableCount()); + for (IntObjectMap blockLiveIn : liveInInformation) { + for (ObjectCursor callSiteLiveIn : blockLiveIn.values()) { + BitSet liveVarsSet = callSiteLiveIn.value; + IntArrayList liveVars = new IntArrayList(); + for (int i = liveVarsSet.nextSetBit(0); i >= 0; i = liveVarsSet.nextSetBit(i + 1)) { + liveVars.add(i); + } + int[] liveVarArray = liveVars.toArray(); + for (int i = 0; i < liveVarArray.length - 1; ++i) { + for (int j = i + 1; j < liveVarArray.length; ++j) { + builder.addEdge(liveVarArray[i], liveVarArray[j]); + builder.addEdge(liveVarArray[j], liveVarArray[i]); + } + } } } + return builder.build(); + } + + private boolean[] getAffectedVariables(List> liveInInformation, Program program) { + boolean[] affectedVariables = new boolean[program.variableCount()]; + for (IntObjectMap blockLiveIn : liveInInformation) { + for (ObjectCursor callSiteLiveIn : blockLiveIn.values()) { + BitSet liveVarsSet = callSiteLiveIn.value; + for (int i = liveVarsSet.nextSetBit(0); i >= 0; i = liveVarsSet.nextSetBit(i + 1)) { + affectedVariables[i] = true; + } + } + } + return affectedVariables; + } + + private List> getDestinationPhis(Program program) { + List> destinationPhis = new ArrayList<>(); + destinationPhis.addAll(Collections.nCopies(program.variableCount(), null)); for (int i = 0; i < program.basicBlockCount(); ++i) { BasicBlock block = program.basicBlockAt(i); - List instructions = block.getInstructions(); - IntObjectMap liveInsByIndex = liveInInformation.get(i); - for (int j = instructions.size() - 1; j >= 0; --j) { - BitSet liveIns = liveInsByIndex.get(j); - if (liveIns == null) { - continue; + for (Phi phi : block.getPhis()) { + for (Incoming incoming : phi.getIncomings()) { + Set phis = destinationPhis.get(incoming.getValue().getIndex()); + if (phis == null) { + phis = new HashSet<>(); + destinationPhis.set(incoming.getValue().getIndex(), phis); + } + phis.add(phi); } - storeLiveIns(block, j, liveIns, maxDepth); } } - return maxDepth; + + return destinationPhis; } - private void storeLiveIns(BasicBlock block, int index, BitSet liveIns, int maxDepth) { + private int[] getInputCount(Program program) { + int[] inputCount = new int[program.variableCount()]; + + for (int i = 0; i < program.basicBlockCount(); ++i) { + BasicBlock block = program.basicBlockAt(i); + for (Phi phi : block.getPhis()) { + inputCount[phi.getReceiver().getIndex()] = phi.getIncomings().size(); + } + } + + return inputCount; + } + + private List> reduceGcRootStores(Program program, int usedColors, + List> liveInInformation, int[] colors, boolean[] autoSpilled) { + class Step { + private final int node; + private final int[] slotStates = new int[usedColors]; + private Step(int node) { + this.node = node; + } + } + + List> slotsToUpdate = new ArrayList<>(); + for (int i = 0; i < program.basicBlockCount(); ++i) { + slotsToUpdate.add(new IntObjectOpenHashMap<>()); + } + + Graph cfg = ProgramUtils.buildControlFlowGraph(program); + DominatorTree dom = GraphUtils.buildDominatorTree(cfg); + Graph domGraph = GraphUtils.buildDominatorGraph(dom, cfg.size()); + + Step[] stack = new Step[program.basicBlockCount() * 2]; + int head = 0; + Step start = new Step(0); + Arrays.fill(start.slotStates, usedColors); + stack[head++] = start; + + while (head > 0) { + Step step = stack[--head]; + + int[] previousStates = step.slotStates; + int[] states = previousStates.clone(); + + IntObjectMap callSites = liveInInformation.get(step.node); + IntObjectMap updatesByCallSite = slotsToUpdate.get(step.node); + int[] callSiteLocations = callSites.keys().toArray(); + Arrays.sort(callSiteLocations); + for (int callSiteLocation : callSiteLocations) { + BitSet liveIns = callSites.get(callSiteLocation); + for (int liveVar = liveIns.nextSetBit(0); liveVar >= 0; liveVar = liveIns.nextSetBit(liveVar + 1)) { + int slot = colors[liveVar]; + states[slot] = liveVar; + } + for (int slot = 0; slot < states.length; ++slot) { + if (states[slot] >= 0 && !liveIns.get(states[slot])) { + states[slot] = -1; + } + } + + updatesByCallSite.put(callSiteLocation, compareStates(previousStates, states, autoSpilled)); + previousStates = states; + states = states.clone(); + } + + for (int succ : domGraph.outgoingEdges(step.node)) { + Step next = new Step(succ); + System.arraycopy(states, 0, next.slotStates, 0, usedColors); + stack[head++] = next; + } + } + + return slotsToUpdate; + } + + private static int[] compareStates(int[] oldStates, int[] newStates, boolean[] autoSpilled) { + int[] comparison = new int[oldStates.length]; + Arrays.fill(comparison, -2); + + for (int i = 0; i < oldStates.length; ++i) { + if (oldStates[i] != newStates[i]) { + comparison[i] = newStates[i]; + } + } + + for (int i = 0; i < newStates.length; ++i) { + if (newStates[i] >= 0 && autoSpilled[newStates[i]]) { + comparison[i] = -2; + } + } + + return comparison; + } + + private void putLiveInGcRoots(Program program, List> updateInformation) { + for (int i = 0; i < program.basicBlockCount(); ++i) { + BasicBlock block = program.basicBlockAt(i); + IntObjectMap updatesByIndex = updateInformation.get(i); + int[] callSiteLocations = updatesByIndex.keys().toArray(); + Arrays.sort(callSiteLocations); + for (int j = callSiteLocations.length - 1; j >= 0; --j) { + int callSiteLocation = callSiteLocations[j]; + int[] updates = updatesByIndex.get(callSiteLocation); + storeLiveIns(block, callSiteLocation, updates); + } + } + } + + private void storeLiveIns(BasicBlock block, int index, int[] updates) { Program program = block.getProgram(); List instructions = block.getInstructions(); Instruction callInstruction = instructions.get(index); List instructionsToAdd = new ArrayList<>(); - int slot = 0; - for (int liveVar = liveIns.nextSetBit(0); liveVar >= 0; liveVar = liveIns.nextSetBit(liveVar + 1)) { + for (int slot = 0; slot < updates.length; ++slot) { + int var = updates[slot]; + if (var == -2) { + continue; + } + Variable slotVar = program.createVariable(); IntegerConstantInstruction slotConstant = new IntegerConstantInstruction(); slotConstant.setReceiver(slotVar); - slotConstant.setConstant(slot++); + slotConstant.setConstant(slot); slotConstant.setLocation(callInstruction.getLocation()); instructionsToAdd.add(slotConstant); InvokeInstruction registerInvocation = new InvokeInstruction(); registerInvocation.setType(InvocationType.SPECIAL); - registerInvocation.setMethod(new MethodReference(Mutator.class, "registerGcRoot", int.class, - Object.class, void.class)); registerInvocation.getArguments().add(slotVar); - registerInvocation.getArguments().add(program.variableAt(liveVar)); + if (var >= 0) { + registerInvocation.setMethod(new MethodReference(Mutator.class, "registerGcRoot", int.class, + Object.class, void.class)); + registerInvocation.getArguments().add(program.variableAt(var)); + } else { + registerInvocation.setMethod(new MethodReference(Mutator.class, "removeGcRoot", int.class, + void.class)); + } instructionsToAdd.add(registerInvocation); } - while (slot < maxDepth) { - Variable slotVar = program.createVariable(); - IntegerConstantInstruction slotConstant = new IntegerConstantInstruction(); - slotConstant.setReceiver(slotVar); - slotConstant.setConstant(slot++); - slotConstant.setLocation(callInstruction.getLocation()); - instructionsToAdd.add(slotConstant); - - InvokeInstruction clearInvocation = new InvokeInstruction(); - clearInvocation.setType(InvocationType.SPECIAL); - clearInvocation.setMethod(new MethodReference(Mutator.class, "removeGcRoot", int.class, void.class)); - clearInvocation.getArguments().add(slotVar); - clearInvocation.setLocation(callInstruction.getLocation()); - instructionsToAdd.add(clearInvocation); - } - instructions.addAll(index, instructionsToAdd); } @@ -288,7 +477,7 @@ public class GcRootMaintainingTransformer { } } - private boolean requiresGc(MethodReference methodReference) { + private boolean requiresGC(MethodReference methodReference) { ClassReader cls = classSource.get(methodReference.getClassName()); if (cls == null) { return true; diff --git a/core/src/main/java/org/teavm/model/util/GraphColorer.java b/core/src/main/java/org/teavm/model/util/GraphColorer.java index b41682127..e61de3aa5 100644 --- a/core/src/main/java/org/teavm/model/util/GraphColorer.java +++ b/core/src/main/java/org/teavm/model/util/GraphColorer.java @@ -18,16 +18,15 @@ package org.teavm.model.util; import java.util.ArrayList; import java.util.BitSet; import java.util.List; +import org.teavm.common.Graph; import org.teavm.common.IntegerArray; -import org.teavm.common.MutableGraphEdge; -import org.teavm.common.MutableGraphNode; -class GraphColorer { - public void colorize(List graph, int[] colors) { +public class GraphColorer { + public void colorize(Graph graph, int[] colors) { colorize(graph, colors, new int[graph.size()], new String[graph.size()]); } - public void colorize(List graph, int[] colors, int[] categories, String[] names) { + public void colorize(Graph graph, int[] colors, int[] categories, String[] names) { IntegerArray colorCategories = new IntegerArray(graph.size()); List colorNames = new ArrayList<>(); for (int i = 0; i < colors.length; ++i) { @@ -51,8 +50,7 @@ class GraphColorer { } usedColors.clear(); usedColors.set(0); - for (MutableGraphEdge edge : graph.get(v).getEdges()) { - int succ = edge.getSecond().getTag(); + for (int succ : graph.outgoingEdges(v)) { if (colors[succ] >= 0) { usedColors.set(colors[succ]); } @@ -82,7 +80,7 @@ class GraphColorer { } } - private int[] getOrdering(List graph) { + private int[] getOrdering(Graph graph) { boolean[] visited = new boolean[graph.size()]; int[] ordering = new int[graph.size()]; int index = 0; @@ -104,8 +102,7 @@ class GraphColorer { } visited[v] = true; ordering[index++] = v; - for (MutableGraphEdge edge : graph.get(v).getEdges()) { - int succ = edge.getSecond().getTag(); + for (int succ : graph.outgoingEdges(v)) { if (visited[succ]) { continue; } diff --git a/core/src/main/java/org/teavm/model/util/RegisterAllocator.java b/core/src/main/java/org/teavm/model/util/RegisterAllocator.java index 6d400cc32..1f2da8065 100644 --- a/core/src/main/java/org/teavm/model/util/RegisterAllocator.java +++ b/core/src/main/java/org/teavm/model/util/RegisterAllocator.java @@ -68,7 +68,7 @@ public class RegisterAllocator { } int[] categories = getVariableCategories(program, method.getReference()); String[] names = getVariableNames(program); - colorer.colorize(interferenceGraph, colors, categories, names); + colorer.colorize(MutableGraphNode.toGraph(interferenceGraph), colors, categories, names); int maxColor = 0; for (int i = 0; i < colors.length; ++i) {