private void ClearGraph() { m_CurSelectAsset = null; m_CurFlowGraph = null; m_CurAssetPath = ""; }
public void MinCostFlowStressTest() { for (var ph = 0; ph < 1000; ph++) { var n = Utilities.RandomInteger(2, 20); var m = Utilities.RandomInteger(1, 100); var(s, t) = Utilities.RandomPair(0, n - 1); if (Utilities.RandomBool()) { (s, t) = (t, s); } var mfg = new FlowGraph(n); var mcfg = new FlowGraph(n); for (var i = 0; i < m; i++) { var u = Utilities.RandomInteger(0, n - 1); var v = Utilities.RandomInteger(0, n - 1); var cap = Utilities.RandomInteger(0, 10); var cost = Utilities.RandomInteger(0, 10000); mcfg.AddEdge(u, v, cap, cost); mfg.AddEdge(u, v, cap); } var(flow, cost1) = mcfg.MinCostFlow(s, t); Assert.That(flow, Is.EqualTo(mfg.MaxFlow(s, t))); var cost2 = 0L; var capacities = new long[n]; foreach (var edge in mcfg.GetEdges()) { capacities[edge.From] -= edge.Flow; capacities[edge.To] += edge.Flow; cost2 += edge.Flow * edge.Cost; } Assert.That(cost1, Is.EqualTo(cost2)); for (var i = 0; i < n; i++) { if (i == s) { Assert.That(capacities[i], Is.EqualTo(-flow)); } else if (i == t) { Assert.That(capacities[i], Is.EqualTo(flow)); } else { Assert.That(capacities[i], Is.EqualTo(0)); } } Assert.DoesNotThrow(() => { var dist = new long[n]; while (true) { var update = false; foreach (var edge in mcfg.GetEdges()) { if (edge.Flow < edge.Capacity) { var ndist = dist[edge.From] + edge.Cost; if (ndist < dist[edge.To]) { update = true; dist[edge.To] = ndist; } } if (edge.Flow == 0) { continue; } { var ndist = dist[edge.To] - edge.Cost; if (ndist < dist[edge.From]) { update = true; dist[edge.From] = ndist; } } } if (!update) { break; } } }); } }
private void DrawMain() { if (m_CurFlowGraph == null) { if (GUI.Button(new Rect(m_SplitterX / 2f - 50f, position.height / 2f - 15f, 100f, 30f), "Create")) { m_CurFlowGraph = CreateInstance <FlowGraph>(); } } else { DrawMiniMap(); if (m_CurFlowGraph.nodeCount > 0) { Handles.BeginGUI(); foreach (var node in m_CurFlowGraph.nodeList) { if (node == null) { Debug.Log("[FlowEditorWindow] node is null"); continue; } if (node.linkList == null) { continue; } FlowNode deleteNode = null; foreach (int linkId in node.linkList) { var linkNode = m_CurFlowGraph.GetNode(linkId); var nodeRect = node.GetRectInGraph(m_CurFlowGraph); var linkRect = linkNode.GetRectInGraph(m_CurFlowGraph); if (DrawBezier(new Vector2(nodeRect.x + nodeRect.width, nodeRect.y + LINK_ICON_WIDTH / 2f), new Vector2(linkRect.x, linkRect.y + LINK_ICON_WIDTH / 2f), Color.yellow)) { deleteNode = linkNode; } } if (deleteNode != null) { node.RemoveLinkNode(deleteNode); deleteNode.RemovePreNode(node); } } Handles.EndGUI(); } BeginWindows(); var nodeList = m_CurFlowGraph.nodeList; int nodeCount = m_CurFlowGraph.nodeCount; for (int i = 0; i < nodeCount; i++) { var node = nodeList[i]; var rect = node.GetRectInGraph(m_CurFlowGraph); var topLeft = new Vector2(rect.x, rect.y); var topRight = new Vector2(rect.x + node.NodeWidth, rect.y); var bottomLeft = new Vector2(rect.x, rect.y + node.NodeHeight); var bottomRight = new Vector2(rect.x + node.NodeWidth, rect.y + node.NodeHeight); if (m_RectMain.Contains(topLeft) || m_RectMain.Contains(topRight) || m_RectMain.Contains(bottomLeft) || m_RectMain.Contains(bottomRight)) { if (node == m_CurSelectFlowNode) { GUI.color = nodeSelectedColor; } else { GUI.color = node.GetColor(); } rect = GUI.Window(node.id, rect, DrawNode, node.NodeName); GUI.color = Color.white; } node.SetRectInGraph(m_CurFlowGraph, rect.position); } DrawLinking(); EndWindows(); } }
public LatticeCell Evaluate( Instruction instruction, IReadOnlyDictionary <ValueTag, LatticeCell> cells, FlowGraph graph) { if (instruction.Prototype is CopyPrototype) { // Special case on copy instructions because they're // easy to deal with: just return the lattice cell for // the argument. return(cells[instruction.Arguments[0]]); } var foundTop = false; var args = new Constant[instruction.Arguments.Count]; for (int i = 0; i < args.Length; i++) { var argCell = cells[instruction.Arguments[i]]; if (argCell.Kind == LatticeCellKind.Top) { // We can't evaluate this value *yet*. Keep looking // for bottom argument cells and set a flag to record // that this value cannot be evaluated yet. foundTop = true; } else if (argCell.Kind == LatticeCellKind.Constant) { // Yay. We found a compile-time constant. args[i] = argCell.Value; } else { // We can't evaluate this value at compile-time // because one if its arguments is unknown. // Time to early-out. return(EvaluateNonConstantInstruction(instruction, graph)); } } if (foundTop) { // We can't evaluate this value yet. return(LatticeCell.Top); } else { // Evaluate the instruction. var constant = EvaluateAsConstant(instruction.Prototype, args); if (constant == null) { // Turns out we can't evaluate the instruction. But maybe // we can say something sensible about its nullability? return(EvaluateNonConstantInstruction(instruction, graph)); } else { return(LatticeCell.Constant(constant)); } } }
/// <summary> /// Computes the set of all live values in the graph. /// </summary> /// <param name="graph">The graph to inspect.</param> /// <returns>A set of live values.</returns> private static HashSet <ValueTag> ComputeLiveValues(FlowGraph graph) { var liveValues = new HashSet <ValueTag>(); // Effectful instructions are always live. liveValues.UnionWith(graph.GetAnalysisResult <EffectfulInstructions>().Instructions); // Remove stores to local variables from the set of // effectful instructions. Our reason for doing this is // that stores to dead local variables are effectively // dead themselves. However, if we assert a priori that all // stores are live, then we will end up keeping both the // stores and the local variables, as the former take the // latter as arguments. // // When local variables become live, we will mark stores to // those variables as live as well. We will not do so before then. var localStores = GetLocalStores(graph); foreach (var set in localStores.Values) { liveValues.ExceptWith(set); } // Entry point parameters are always live too. liveValues.UnionWith(graph.GetBasicBlock(graph.EntryPointTag).ParameterTags); // Also construct a mapping of basic block parameters to the // arguments they take. var phiArgs = new Dictionary <ValueTag, HashSet <ValueTag> >(); foreach (var param in graph.ParameterTags) { phiArgs[param] = new HashSet <ValueTag>(); } // Instructions that are part of block flows are live. foreach (var block in graph.BasicBlocks) { var flow = block.Flow; foreach (var instruction in flow.Instructions) { liveValues.UnionWith(instruction.Arguments); } // While we're at it, we might as well populate `phiArgs`. foreach (var branch in flow.Branches) { foreach (var pair in branch.ZipArgumentsWithParameters(graph)) { if (pair.Value.IsValue) { phiArgs[pair.Key].Add(pair.Value.ValueOrNull); } } } } // Now we simply compute the transitive closure of // the set of live values. var worklist = new Queue <ValueTag>(liveValues); liveValues.Clear(); while (worklist.Count > 0) { var value = worklist.Dequeue(); if (liveValues.Add(value)) { HashSet <ValueTag> args; if (phiArgs.TryGetValue(value, out args)) { foreach (var arg in args) { worklist.Enqueue(arg); } } else { foreach (var arg in graph.GetInstruction(value).Instruction.Arguments) { worklist.Enqueue(arg); } HashSet <ValueTag> storeDependencies; if (localStores.TryGetValue(value, out storeDependencies)) { // A local variable has become live. We must mark any store instructions // that depend on said local as live as well. foreach (var dep in storeDependencies) { worklist.Enqueue(dep); } } } } } return(liveValues); }
public LabelArgs(string label, FlowGraph graph) { this.label = label; this.graph = graph; }
/// <inheritdoc/> public RegisterAllocation <TRegister> Analyze( FlowGraph graph) { // Run the related values and interference graph analyses. var related = graph.GetAnalysisResult <RelatedValues>(); var interference = graph.GetAnalysisResult <InterferenceGraph>(); // Create a mapping of values to registers. This will become our // return value. var allocation = new Dictionary <ValueTag, TRegister>(); // Create a mapping of registers to the set of all values they // interfere with due to the registers getting allocated to values. var registerInterference = new Dictionary <TRegister, HashSet <ValueTag> >(); // Before we do any real register allocation, we should set up // preallocated registers. The reason for doing this now instead // of later on in the allocation loop is that preallocated registers // are "free:" they don't incur register allocations. At the same time, // preallocated registers can be recycled, so we'll have more // registers to recycle (and hopefully create fewer new ones) if we // handle preallocated registers first. foreach (var value in graph.ValueTags) { TRegister assignedRegister; if (TryGetPreallocatedRegister(value, graph, out assignedRegister)) { // If we have a preallocated register, then we should just accept it. allocation[value] = assignedRegister; registerInterference[assignedRegister] = new HashSet <ValueTag>( interference.GetInterferingValues(value)); } } // Iterate over all values in the graph. foreach (var value in graph.ValueTags) { if (!RequiresRegister(value, graph) || allocation.ContainsKey(value)) { // The value may not need a register or may already have one. // If so, then we shouldn't allocate one. continue; } // Compose a set of registers we might be able to recycle. // Specifically, we'll look for all registers that are not // allocated to values that interfere with the current value. var recyclable = new HashSet <TRegister>(); foreach (var pair in registerInterference) { if (!pair.Value.Contains(value)) { // If the value is not in the interference set of // the register, then we're good to go. recyclable.Add(pair.Key); } } // We would like to recycle a register that has been // allocated to a related but non-interfering value. // To do so, we'll build a set of candidate registers. var relatedRegisters = new HashSet <TRegister>(); foreach (var relatedValue in related.GetRelatedValues(value)) { TRegister reg; if (allocation.TryGetValue(relatedValue, out reg) && recyclable.Contains(reg)) { relatedRegisters.Add(reg); } } // If at all possible, try to recycle a related register. If that // doesn't work out, try to recycle a non-related register. If // that fails as well, then we'll create a new register. var valueType = graph.GetValueType(value); TRegister assignedRegister; if (!TryRecycleRegister(valueType, relatedRegisters, out assignedRegister) && !TryRecycleRegister(valueType, recyclable, out assignedRegister)) { assignedRegister = CreateRegister(valueType); registerInterference[assignedRegister] = new HashSet <ValueTag>(); } // Allocate the register we recycled or created to the value. allocation[value] = assignedRegister; registerInterference[assignedRegister].UnionWith( interference.GetInterferingValues(value)); } return(new RegisterAllocation <TRegister>(allocation)); }
public T Analyze(FlowGraph graph) { return(Result); }
public T AnalyzeWithUpdates(FlowGraph graph, T previousResult, IReadOnlyList <FlowGraphUpdate> updates) { return(previousResult); }
public void AddTag(string tag, FlowGraph graph) { VariableDeclarations variableDeclarations = Variables.Graph(graph); tags.Add(tag, variableDeclarations); }
public FlowGraphView(EditorWindow host, FlowGraph graph, UnityEngine.Object target) : base(host) { this.m_Graph = graph; this.m_Target = target; }
static void Main(string[] args) { //var input = "{ int x; x := 3; }"; var input = @" { int x; int [10] a; int y; x := 0 + 1 + 2; y := x; if (not true & false) { x := 3; } else { y := 3; } a[1+1] := (1+1)*2; } "; var aeinput = @" { int x; int [10] a; int y; x := 0 + 1 + 2; y := x; if (not true & false) { x := 3; } else { y := 3; } a[1+1] := (1+1)*2; }"; var rdinput = @" { int x; int y; int q; int r; if (x >= 0 & y >0) { q := 0; r := x; while ( r >= y) { r := r - y; q := q + 1; while ( r >= y) { r := r - y; q := q + 1; while ( r >= y) { r := r - y; q := q + 1; } } } } else { while ( r >= y) { r := r - y; q := q + 1; while ( r >= y) { r := r - y; q := q + 1; while ( r >= y) { r := r - y; q := q + 1; } } } while ( r >= y) { r := r - y; q := q + 1; while ( r >= y) { r := r - y; q := q + 1; } } } write r; } "; var lvinput = @" { int x; int y; int q; int r; if (x >= 0 & y >0) { q := 0; r := x; while ( r >= y) { r := r - y; q := q + 1; while ( r >= y) { r := r - y; q := q + 1; while ( r >= y) { r := r - y; q := q + 1; } } } } else { while ( r >= y) { r := r - y; q := q + 1; while ( r >= y) { r := r - y; q := q + 1; while ( r >= y) { r := r - y; q := q + 1; } } } while ( r >= y) { r := r - y; q := q + 1; while ( r >= y) { r := r - y; q := q + 1; } } } write r; } "; var input1 = @" { { int fst; int snd } r; int x; r := (0,1); while (x > 0) { r.fst := r.fst + x; r.snd := r.snd * x; x := x - 1; } r := (0, 0); } "; var input2 = @" { int x; int y; int q; int r; if (x >= 0 & y >0) { q := 0; r := x; while ( r >= y) { r := r - y; q := q + 1; while ( r >= y) { r := r - y; q := q + 1; while ( r >= y) { r := r - y; q := q + 1; } } } } write r; } "; var input3 = @" { int x; int y; int q; int r; if (x >= 0 & y >0) { q := 0; r := x; while ( r >= y) { r := r - y; q := q + 1; while ( r >= y) { r := r - y; q := q + 1; while ( r >= y) { r := r - y; q := q + 1; } } } } else { while ( r >= y) { r := r - y; q := q + 1; while ( r >= y) { r := r - y; q := q + 1; while ( r >= y) { r := r - y; q := q + 1; } } } while ( r >= y) { r := r - y; q := q + 1; while ( r >= y) { r := r - y; q := q + 1; } } } write r; } "; var result1 = Parser.Util.StringToAst(input1); var result2 = Parser.Util.StringToAst(input2); var result3 = Parser.Util.StringToAst(input3); var result = Parser.Util.StringToAst(input); var aeresult = Parser.Util.StringToAst(aeinput); var rdresult = Parser.Util.StringToAst(rdinput); var lvresult = Parser.Util.StringToAst(lvinput); Console.WriteLine(result); var fg = new FlowGraph(result); Console.WriteLine(fg.Inital); Console.WriteLine(string.Join(" ", fg.Final)); Console.WriteLine(string.Join("\n", fg.Blocks.Select(s => s.PrintBlock()))); Console.WriteLine(fg.Edges.Count()); Console.WriteLine(string.Join("\r\n", fg.Edges)); var fv = Analysis.Analysis.AnalysisUtil.FreeVariables(result); Console.WriteLine(fv.Count); var exp1 = new ABinOp(new IntLit(3), new IntLit(4), ABinOperator.Plus); var exp2 = new ABinOp(new IntLit(3), new IntLit(4), ABinOperator.Plus); var exp3 = new ABinOp(new IntLit(4), new IntLit(4), ABinOperator.Plus); var exp4 = new ABinOp(exp1, exp2, ABinOperator.Mult); var exp5 = new ABinOp(exp4, exp3, ABinOperator.Div); Console.WriteLine(exp1 == exp2); // Should be false as we are not overloading the operator Console.WriteLine(exp1.Equals(exp2)); // Should be true as we have implemented the IEquatable interface Console.WriteLine(exp1.Equals(exp3)); // Should be false, different expressions var ae = Analysis.Analysis.AnalysisUtil.AvailableExpressions(exp5); var ae2 = Analysis.Analysis.AnalysisUtil.AvailableExpressions(result); Console.WriteLine("========="); Console.WriteLine(ae); Console.WriteLine(ae2); //// Overflowing program debug //var overflow = "{int x; x:=0; Point.x := 0;}"; //var overflowParse = Parser.Util.StringToAst(overflow); // //Console.WriteLine(overflowParse); //var x = new Identifier("x", "int", 0); //var y = new Identifier("y", "int", 2); //var A = new Identifier("A", "array", 1); //var d1 = new Dictionary<Identifier, HashSet<int>>(); //d1[x] = new HashSet<int> {1,2}; //d1[A] = new HashSet<int> {1}; //d1[y] = new HashSet<int>() {3}; //var d2 = new Dictionary<Identifier, HashSet<int>>(); //d2[x] = new HashSet<int> {1,2,3}; //d2[A] = new HashSet<int> {2}; //var l1 = new RDLattice(d1); //var l2 = new RDLattice(d2); //Console.WriteLine(l1.PartialOrder(l2)); //var joined = l1.Join(l2); //Console.WriteLine(joined); // var n = d2.Except(d1); // var s = string.Join("\n", n.Select(x => $"{x.Key}: {string.Join(",", x.Value)}")); // Console.WriteLine(s); //var hs = new HashSet<int?>(); //hs.Add(1); //hs.Add(null); //hs.Add(null); //hs.Add(2); //Console.WriteLine(hs.Count); //var t = new AELattice(ae2); //var t2 = new AELattice(ae); //Console.WriteLine(t); //Console.WriteLine(t2); //Console.WriteLine(t <= t); //Console.WriteLine(t2 <= (t & t2)); // //var analysis = new AEAnalysis(aeresult); //Console.WriteLine(analysis); //var analysis = new RDAnalysis(rdresult); //Console.WriteLine(analysis); Console.WriteLine("------- Analysis 1 --------"); var analysis1 = new FVAnalysis(result1); Console.WriteLine("--------- Analysis 2 --------"); var analysis2 = new FVAnalysis(result2); Console.WriteLine("------- Analysis 3 -------"); var analysis3 = new FVAnalysis(result3); Console.WriteLine(string.Join(" ", AnalysisUtil.InterestingValues(result1).Select(x => x.ToString()))); var analysis4 = new IAAnalysis(result1); Console.WriteLine(analysis4); //Console.WriteLine(analysis1); //Console.WriteLine(analysis2); //Console.WriteLine(analysis3); //var analysis2 = new LVAnalysis(lvresult); //var analysis3 = new FVAnalysis(lvresult); //var analysis4 = new DSAnalysis(lvresult); //Console.WriteLine(analysis2); //Console.WriteLine(analysis3); //Console.WriteLine(analysis4); }
public void ArgumentExceptionInMinCostSlope() { var fg = new FlowGraph(2); Assert.Throws <ArgumentException>(() => fg.MinCostSlope(1, 1)); }
public void ArgumentOutOfRangeExceptionInMinCostSlope(int u, int v) { var fg = new FlowGraph(2); Assert.Throws <ArgumentOutOfRangeException>(() => fg.MinCostSlope(u, v)); }
public void CreateGraph(Object asset) { m_CurSelectAsset = asset; m_CurAssetPath = AssetDatabase.GetAssetPath(m_CurSelectAsset); m_CurFlowGraph = FlowGraph.LoadFromAsset(m_CurSelectAsset); }
/// <inheritdoc/> public LatticeAnalysisResult <TCell> Analyze(FlowGraph graph) { return(FillCells(graph)); }
public static void GotoLabel(GraphReference reference, string label, FlowGraph graph) { reference.TriggerEventHandler <LabelArgs>(hook => hook.name == "Label", new LabelArgs(label, graph), parent => true, true); }
private LatticeAnalysisResult <TCell> FillCells(FlowGraph graph) { var uses = graph.GetAnalysisResult <ValueUses>(); // Create a mapping of values to their corresponding lattice cells. var valueCells = new Dictionary <ValueTag, TCell>(); var parameterArgs = new Dictionary <ValueTag, HashSet <ValueTag> >(); var entryPointBlock = graph.GetBasicBlock(graph.EntryPointTag); // Assign 'top' to all values in the graph. foreach (var tag in graph.ValueTags) { valueCells[tag] = Top; } foreach (var methodParameter in entryPointBlock.ParameterTags) { // The values of method parameters cannot be inferred by // just looking at a method's body. Mark them as bottom cells // so we don't fool ourselves into thinking they are constants. valueCells[methodParameter] = Bottom; } var visitedBlocks = new HashSet <BasicBlockTag>(); var liveBlockSet = new HashSet <BasicBlockTag>(); var valueWorklist = new Queue <ValueTag>(entryPointBlock.InstructionTags); var flowWorklist = new Queue <BasicBlockTag>(); flowWorklist.Enqueue(entryPointBlock); visitedBlocks.Add(entryPointBlock); liveBlockSet.Add(entryPointBlock); while (valueWorklist.Count > 0 || flowWorklist.Count > 0) { // Process all values in the worklist. while (valueWorklist.Count > 0) { var value = valueWorklist.Dequeue(); var cell = valueCells[value]; var newCell = graph.ContainsInstruction(value) ? UpdateInstructionCell(value, valueCells, graph) : UpdateBlockParameterCell(value, valueCells, parameterArgs); valueCells[value] = newCell; if (!Equals(cell, newCell)) { // Visit all instructions and flows that depend on // this instruction. foreach (var item in uses.GetInstructionUses(value)) { valueWorklist.Enqueue(item); } foreach (var item in uses.GetFlowUses(value)) { flowWorklist.Enqueue(item); } } } if (flowWorklist.Count > 0) { var block = graph.GetBasicBlock(flowWorklist.Dequeue()); if (visitedBlocks.Add(block)) { // When a block is visited for the first time, add // all of its instructions to the value worklist. foreach (var item in block.InstructionTags) { valueWorklist.Enqueue(item); } } // Process the live branches. foreach (var branch in GetLiveBranches(block.Flow, valueCells, graph)) { // The target of every branch we visit is live. liveBlockSet.Add(branch.Target); // Add the branch target to the worklist of blocks // to process if we haven't processed it already. if (!visitedBlocks.Contains(branch.Target)) { flowWorklist.Enqueue(branch.Target); } foreach (var pair in branch.ZipArgumentsWithParameters(graph)) { if (pair.Value.IsValue) { HashSet <ValueTag> args; if (!parameterArgs.TryGetValue(pair.Key, out args)) { args = new HashSet <ValueTag>(); parameterArgs[pair.Key] = args; } args.Add(pair.Value.ValueOrNull); valueWorklist.Enqueue(pair.Key); } else { valueCells[pair.Key] = Bottom; } valueWorklist.Enqueue(pair.Key); } } } } return(new LatticeAnalysisResult <TCell>(valueCells, liveBlockSet)); }
/// <summary> /// Tells if a register should be allocated for a /// particular value. /// </summary> /// <param name="value"> /// The value for which register allocation may or may /// not be necessary. /// </param> /// <param name="graph"> /// The control flow graph that defines <paramref name="value"/>. /// </param> /// <returns> /// <c>true</c> if a register must be allocated to /// <paramref name="value"/>; otherwise, <c>false</c>. /// </returns> /// <remarks> /// Implementations may override this method to suppress /// register allocation for values that are, e.g., stored /// on an evaluation stack. /// </remarks> protected virtual bool RequiresRegister( ValueTag value, FlowGraph graph) { return(true); }
/// <inheritdoc/> public LazyBlockReachability Analyze(FlowGraph graph) { return(new LazyBlockReachability(graph)); }
/// <inheritdoc/> public override FlowGraph Apply(FlowGraph graph) { // Do the fancy analysis. var analysis = Analyzer.Analyze(graph); var cells = analysis.ValueCells; var liveBlocks = analysis.LiveBlocks; // Get ready to rewrite the flow graph. var graphBuilder = graph.ToBuilder(); // Eliminate switch cases whenever possible. SimplifySwitches(graphBuilder, cells); // Replace instructions with constants. foreach (var selection in graphBuilder.NamedInstructions) { LatticeCell cell; if (cells.TryGetValue(selection, out cell) && cell.IsConstant) { selection.Instruction = Instruction.CreateConstant( cell.Value, selection.Instruction.ResultType); } } // Replace block parameters with constants if possible. var phiReplacements = new Dictionary <ValueTag, ValueTag>(); var entryPoint = graphBuilder.GetBasicBlock(graphBuilder.EntryPointTag); foreach (var block in graphBuilder.BasicBlocks) { foreach (var param in block.Parameters) { LatticeCell cell; if (cells.TryGetValue(param.Tag, out cell) && cell.IsConstant) { phiReplacements[param.Tag] = entryPoint.InsertInstruction( 0, Instruction.CreateConstant(cell.Value, param.Type)); } } var flowInstructions = block.Flow.Instructions; var newFlowInstructions = new Instruction[flowInstructions.Count]; bool anyChanged = false; for (int i = 0; i < newFlowInstructions.Length; i++) { var cell = Analyzer.Evaluate(flowInstructions[i], cells, graph); if (cell.IsConstant) { anyChanged = true; newFlowInstructions[i] = Instruction.CreateConstant( cell.Value, flowInstructions[i].ResultType); } else { newFlowInstructions[i] = flowInstructions[i]; } } if (anyChanged) { block.Flow = block.Flow.WithInstructions(newFlowInstructions); } } graphBuilder.ReplaceUses(phiReplacements); graphBuilder.RemoveDefinitions(phiReplacements.Keys); // Remove all instructions from dead blocks and mark the blocks // themselves as unreachable. foreach (var tag in graphBuilder.BasicBlockTags.Except(liveBlocks).ToArray()) { var block = graphBuilder.GetBasicBlock(tag); // Turn the block's flow into unreachable flow. block.Flow = UnreachableFlow.Instance; // Delete the block's instructions. graphBuilder.RemoveInstructionDefinitions(block.InstructionTags); } return(graphBuilder.ToImmutable()); }
/// <summary> /// Creates a lazy block reachability analysis for a particular graph. /// </summary> /// <param name="graph">The graph to create a reachability analysis for.</param> public LazyBlockReachability(FlowGraph graph) { this.Graph = graph; this.results = new Dictionary <BasicBlockTag, HashSet <BasicBlockTag> >(); }
/// <inheritdoc/> public override FlowGraph Apply(FlowGraph graph) { var memSSA = graph.GetAnalysisResult <MemorySSA>(); var aliasing = graph.GetAnalysisResult <AliasAnalysisResult>(); var effectfulness = graph.GetAnalysisResult <EffectfulInstructions>(); var builder = graph.ToBuilder(); // First try and eliminate loads and stores based on their memory SSA // states. foreach (var instruction in builder.NamedInstructions) { var proto = instruction.Prototype; if (proto is LoadPrototype) { // Loads can be eliminated if we know the memory contents. var loadProto = (LoadPrototype)proto; var state = memSSA.GetMemoryAfter(instruction); ValueTag value; if (state.TryGetValueAt( loadProto.GetPointer(instruction.Instruction), graph, out value)) { instruction.Instruction = Instruction.CreateCopy( instruction.ResultType, value); } } else if (proto is StorePrototype) { // Stores can be eliminated if they don't affect the memory state. var storeProto = (StorePrototype)proto; var stateBefore = memSSA.GetMemoryBefore(instruction); var stateAfter = memSSA.GetMemoryAfter(instruction); if (stateBefore == stateAfter) { EliminateStore(instruction); } } } // Then try to coalesce stores by iterating through basic blocks. foreach (var block in builder.BasicBlocks) { var pendingStores = new List <NamedInstructionBuilder>(); foreach (var instruction in block.NamedInstructions) { var proto = instruction.Prototype; if (proto is StorePrototype) { var storeProto = (StorePrototype)proto; var pointer = storeProto.GetPointer(instruction.Instruction); var newPending = new List <NamedInstructionBuilder>(); foreach (var pending in pendingStores) { var pendingProto = (StorePrototype)pending.Prototype; var pendingPointer = pendingProto.GetPointer(pending.Instruction); var aliasState = aliasing.GetAliasing(pointer, pendingPointer); if (aliasState == Aliasing.MustAlias) { // Yes, do it. Delete the pending store. EliminateStore(pending); } else if (aliasState == Aliasing.NoAlias) { // We can't eliminate the pending store, but we can keep it // in the pending list. newPending.Add(pending); } } pendingStores = newPending; // Add this store to the list of pending stores as well. pendingStores.Add(instruction); } else if (proto is LoadPrototype || proto is CopyPrototype) { // Loads are perfectly benign. They *may* be effectful in the sense // that they can trigger a segfault and make the program blow up, but // there's no way we can catch that anyway. We'll just allow store // coalescing across load boundaries. // // We also want to test for copy instructions here because our effectfulness // analysis is slightly outdated and we may have turned loads/stores into copies. } else if (effectfulness.Instructions.Contains(instruction)) { // Effectful instructions are a barrier for store coalescing. pendingStores.Clear(); } } } return(builder.ToImmutable()); }
/// <summary> /// Takes a flow graph and translates it to an instruction stream. /// </summary> /// <param name="graph"> /// The flow graph to translate. /// </param> /// <returns> /// A linear sequence of target-specific instructions. /// </returns> public IReadOnlyList <TInstruction> ToInstructionStream(FlowGraph graph) { // One thing to keep in mind when selecting instructions is that // not all IR instructions must be translated to target-specific instructions. // // For example, suppose that the target has a specialized add-constant-integer // instruction---let's call it `addi`. Then the following sequence of instructions // // one = const(1, System::Int32)(); // addition = intrinsic(@arith.add, System::Int32, #(System::Int32, System::Int32))(arg, one); // // should get translated to // // <addition> = addi <arg>, 1 // // Note how the `one` instruction doesn't get emitted despite the fact that // is it *not* dead from an IR point of view. That's definitely a good thing // and it's not something we'll get if we naively create a linear stream of // instructions by simply selecting instructions for each IR instruction. // // To ensure that we select only the instructions we actually need, we will // start at // // 1. "root" instructions: reachable instructions that may have side-effects // and hence *must* be selected, and // // 2. block flows. // // Furthermore, we also want to make sure that we emit only reachable basic blocks. // All other basic blocks are dead code and we shouldn't bother selecting instructions // for them. // // To figure out which instructions are effectful instructions and which blocks are // reachable, we will rely on a couple of analyses. var reachability = graph.GetAnalysisResult <BlockReachability>(); var effectful = graph.GetAnalysisResult <EffectfulInstructions>(); // Find the exact set of root instructions. Add them all // to a queue of instructions to select. var selectionWorklist = new Queue <ValueTag>(); foreach (var block in graph.BasicBlocks) { if (!reachability.IsReachableFrom(graph.EntryPointTag, block)) { continue; } foreach (var tag in block.InstructionTags.Reverse()) { if (effectful.Instructions.Contains(tag)) { selectionWorklist.Enqueue(tag); } } } // Select target-specific instructions for reachable block flows. // Also order the basic blocks. var flowLayout = new List <BasicBlockTag>(); var flowSelection = new Dictionary <BasicBlockTag, IReadOnlyList <TInstruction> >(); var flowWorklist = new Stack <BasicBlockTag>(); flowWorklist.Push(graph.EntryPointTag); while (flowWorklist.Count > 0) { var tag = flowWorklist.Pop(); if (flowSelection.ContainsKey(tag)) { // Never select blocks twice. continue; } // Assign a dummy value (null) to the block tag so we don't fool // ourselves into thinking that the block isn't being processed // yet. flowSelection[tag] = null; // Add the block to the flow layout. flowLayout.Add(tag); // Fetch the block's flow from the graph. var block = graph.GetBasicBlock(tag); var flow = block.Flow; // Select instructions for the flow. BasicBlockTag fallthrough; var selection = InstructionSelector.SelectInstructions( flow, block.Tag, graph, flow.BranchTargets.FirstOrDefault(target => !flowSelection.ContainsKey(target)), out fallthrough); // Emit all branch targets. foreach (var target in flow.BranchTargets) { flowWorklist.Push(target); } var selInstructions = selection.Instructions; if (fallthrough != null) { if (flowSelection.ContainsKey(fallthrough)) { // We found a fallthrough block that has already been selected. // This is quite unfortunate; we'll have to introduce a branch. var insns = new List <TInstruction>(selInstructions); insns.AddRange(InstructionSelector.CreateJumpTo(fallthrough)); selInstructions = insns; } else { // We found a fallthrough block that has not been selected yet. // Add it to the flow worklist last (because the "worklist" is // actually a stack) to see it get emitted right after this block. flowWorklist.Push(fallthrough); } } flowSelection[tag] = selInstructions; foreach (var item in selection.Dependencies) { selectionWorklist.Enqueue(item); } } // Select target-specific instructions. var instructionSelection = new Dictionary <ValueTag, IReadOnlyList <TInstruction> >(); while (selectionWorklist.Count > 0) { var tag = selectionWorklist.Dequeue(); if (instructionSelection.ContainsKey(tag) || graph.ContainsBlockParameter(tag)) { // Never select instructions twice. Also, don't try // to "select" block parameters. continue; } var instruction = graph.GetInstruction(tag); var selection = InstructionSelector.SelectInstructions(instruction); instructionSelection[tag] = selection.Instructions; foreach (var item in selection.Dependencies) { selectionWorklist.Enqueue(item); } } // We have selected target-specific instructions for reachable IR block // flows and required IR instructions. All we need to do now is patch them // together into a linear sequence of target-specific instructions. var instructionStream = new List <TInstruction>(); foreach (var blockTag in flowLayout) { instructionStream.AddRange(InstructionSelector.CreateBlockMarker(graph.GetBasicBlock(blockTag))); foreach (var insnTag in graph.GetBasicBlock(blockTag).InstructionTags) { IReadOnlyList <TInstruction> selection; if (instructionSelection.TryGetValue(insnTag, out selection)) { instructionStream.AddRange(selection); } } instructionStream.AddRange(flowSelection[blockTag]); } return(instructionStream); }
/// <inheritdoc/> public override FlowGraph Apply(FlowGraph graph) { // Figure out which aggregates can be replaced by scalars. var eligible = FindEligibleAllocas(graph); var builder = graph.ToBuilder(); // Create allocas for fields. var replacements = new Dictionary <ValueTag, Dictionary <IField, ValueTag> >(); foreach (var allocaTag in eligible) { var allocaInstruction = builder.GetInstruction(allocaTag); var allocaProto = (AllocaPrototype)allocaInstruction.Prototype; var fieldSlots = new Dictionary <IField, ValueTag>(); foreach (var field in allocaProto.ElementType.Fields) { fieldSlots[field] = allocaInstruction.InsertAfter( Instruction.CreateAlloca(field.FieldType), allocaTag.Name + "_field_" + field.Name); } replacements[allocaTag] = fieldSlots; } // Rewrite instructions. foreach (var instruction in builder.NamedInstructions) { var proto = instruction.Prototype; if (proto is GetFieldPointerPrototype) { var gfpProto = (GetFieldPointerPrototype)proto; var basePointer = gfpProto.GetBasePointer(instruction.Instruction); if (eligible.Contains(basePointer)) { instruction.Instruction = Instruction.CreateCopy( instruction.Instruction.ResultType, replacements[basePointer][gfpProto.Field]); } } else if (IsDefaultInitialization(instruction.ToImmutable())) { var storeProto = (StorePrototype)proto; var pointer = storeProto.GetPointer(instruction.Instruction); if (eligible.Contains(pointer)) { foreach (var pair in replacements[pointer]) { // Initialize each field with // // c = const(#default, field_type)(); // _ = store(field_pointer, c); // var constant = instruction.InsertAfter( Instruction.CreateDefaultConstant(pair.Key.FieldType)); constant.InsertAfter( Instruction.CreateStore( pair.Key.FieldType, pair.Value, constant)); } // Replace the store with a copy, in case someone is // using the value it returns. instruction.Instruction = Instruction.CreateCopy( instruction.Instruction.ResultType, storeProto.GetValue(instruction.Instruction)); } } } // Delete the replaced allocas. builder.RemoveInstructionDefinitions(eligible); return(builder.ToImmutable()); }
public LocalFlowVariableOverlay <T> this[FlowGraph graph] { get { return(this.localVariableOverlay[graph]); } set { this.localVariableOverlay[graph] = value; } }