/// <inheritdoc/> public Result Analyze(FlowGraph graph) { // Create the input state for the entry point. var inputs = new Dictionary <BasicBlockTag, TBlockState>(); inputs[graph.EntryPointTag] = CreateEntryPointInput(graph.EntryPoint); var outputs = ImmutableDictionary <BasicBlockTag, TBlockState> .Empty.ToBuilder(); var worklist = new HashSet <BasicBlockTag>() { graph.EntryPointTag }; while (worklist.Count > 0) { // Pop a block from the worklist. var blockTag = worklist.First(); worklist.Remove(blockTag); // Process the block. var block = graph.GetBasicBlock(blockTag); var blockOutput = Process(block, inputs[block]); outputs[block] = blockOutput; // Process the block's outgoing branches. foreach (var pair in GetOutgoingInputs(block, blockOutput)) { var target = pair.Key; TBlockState targetInput; if (inputs.TryGetValue(target, out targetInput)) { // If a branch target has already been processed, then we will // merge this block's output with the branch target's input. If // they are the same, then we'll do nothing. Otherwise, we'll // re-process the branch with the new input. var merged = Merge(pair.Value, targetInput); if (!Equals(merged, targetInput)) { inputs[target] = merged; worklist.Add(target); } } else { // If the branch target hasn't been processed yet, then it's about // time that we do. Add it to the worklist. inputs[target] = pair.Value; worklist.Add(target); } } } return(new Result(outputs.ToImmutable())); }
/// <summary> /// Computes a mapping from basic block tags to their immediate /// dominators. The entry point block is mapped to <c>null</c>. /// </summary> private static IReadOnlyDictionary <BasicBlockTag, BasicBlockTag> GetImmediateDominators( FlowGraph graph) { var preds = graph.GetAnalysisResult <BasicBlockPredecessors>(); return(GetImmediateDominators( graph.BasicBlockTags, graph.EntryPointTag, tag => graph.GetBasicBlock(tag).Flow.BranchTargets, preds.GetPredecessorsOf)); }
private Dictionary <ValueTag, Mono.Cecil.ParameterDefinition> GetPreallocatedRegisters( FlowGraph graph) { var entryPoint = graph.GetBasicBlock(graph.EntryPointTag); var extendedParams = TypeHelpers.GetExtendedParameters(Method); int regCount = Math.Min(extendedParams.Count, entryPoint.Parameters.Count); var preallocRegisters = new Dictionary <ValueTag, Mono.Cecil.ParameterDefinition>(); for (int i = 0; i < regCount; i++) { preallocRegisters[entryPoint.Parameters[i].Tag] = extendedParams[i]; } return(preallocRegisters); }
private static void SortPostorder( FlowGraph graph, BasicBlockTag tag, HashSet <BasicBlockTag> processed, List <BasicBlockTag> results) { if (!processed.Add(tag)) { return; } foreach (var child in graph.GetBasicBlock(tag).Flow.BranchTargets) { SortPostorder(graph, child, processed, results); } results.Add(tag); }
internal static bool TryGetFusibleTail( BasicBlockTag head, FlowGraph graph, BasicBlockPredecessors predecessors, out BasicBlockTag tail) { var block = graph.GetBasicBlock(head); var jumpFlow = block.Flow as JumpFlow; if (jumpFlow != null && graph.EntryPointTag != jumpFlow.Branch.Target) { var preds = predecessors.GetPredecessorsOf(jumpFlow.Branch.Target).ToArray(); if (preds.Length == 1 && preds[0] == block.Tag) { tail = jumpFlow.Branch.Target; return(true); } } tail = null; return(false); }
/// <inheritdoc/> public RelatedValues Analyze(FlowGraph graph) { var relation = new SymmetricRelation <ValueTag>(); // Examine direct copies. foreach (var selection in graph.NamedInstructions) { var instruction = selection.Instruction; var prototype = instruction.Prototype as CopyPrototype; if (prototype != null) { relation.Add( selection.Tag, prototype.GetCopiedValue(instruction)); } } // Examine copies produced by branches. foreach (var block in graph.BasicBlocks) { foreach (var branch in block.Flow.Branches) { var parameters = graph.GetBasicBlock(branch.Target).Parameters; foreach (var pair in branch.Arguments.Zip(parameters, Tuple.Create)) { if (pair.Item1.IsValue) { relation.Add( pair.Item1.ValueOrNull, pair.Item2.Tag); } } } } return(new RelatedValues(relation)); }
private Dictionary <ValueTag, LatticeCell> FillCells( FlowGraph graph, out IEnumerable <BasicBlockTag> liveBlocks) { var uses = graph.GetAnalysisResult <ValueUses>(); // Create a mapping of values to their corresponding lattice cells. var valueCells = new Dictionary <ValueTag, LatticeCell>(); var parameterArgs = new Dictionary <ValueTag, HashSet <ValueTag> >(); var entryPointBlock = graph.GetBasicBlock(graph.EntryPointTag); 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] = LatticeCell.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 = GetCellForValue(value, valueCells); var newCell = graph.ContainsInstruction(value) ? UpdateInstructionCell(value, valueCells, graph) : UpdateBlockParameterCell(value, valueCells, parameterArgs); valueCells[value] = newCell; if (cell.Kind != newCell.Kind) { // 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); } } var flow = block.Flow; IReadOnlyList <Branch> branches; if (flow is SwitchFlow) { var switchFlow = (SwitchFlow)flow; var condition = EvaluateInstruction(switchFlow.SwitchValue, valueCells, graph); if (condition.Kind == LatticeCellKind.Top) { // Do nothing for now. branches = EmptyArray <Branch> .Value; } else if (condition.Kind == LatticeCellKind.Constant) { // If a switch flow has a constant condition (for now), // then pick a single branch. var valuesToBranches = switchFlow.ValueToBranchMap; branches = new[] { valuesToBranches.ContainsKey(condition.Value) ? valuesToBranches[condition.Value] : switchFlow.DefaultBranch }; } else if (condition.Kind == LatticeCellKind.NonNull) { // If a switch flow has a non-null condition, then we // work on all branches except for the null branch, if // it exists. branches = switchFlow.ValueToBranchMap .Where(pair => pair.Key != NullConstant.Instance) .Select(pair => pair.Value) .Concat(new[] { switchFlow.DefaultBranch }) .ToArray(); } else { // If a switch flow has a bottom condition, then everything // is possible. branches = flow.Branches; } } else { branches = flow.Branches; } // Process the selected branches. foreach (var branch in branches) { // 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] = LatticeCell.Bottom; } valueWorklist.Enqueue(pair.Key); } } } } liveBlocks = liveBlockSet; return(valueCells); }
/// <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, SelectedFlowInstructions <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] = default(SelectedFlowInstructions <TInstruction>); // 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); } 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. selection = selection.Append(InstructionSelector.CreateJumpTo(fallthrough)); } 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] = selection; foreach (var item in selection.Dependencies) { selectionWorklist.Enqueue(item); } } // Select target-specific instructions. var instructionSelection = new Dictionary <ValueTag, SelectedInstructions <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; 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. return(ToInstructionStream( flowLayout.Select(graph.GetBasicBlock), instructionSelection, flowSelection)); }
/// <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); }
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> /// 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); // 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); } } } } return(liveValues); }
/// <summary> /// Gets the exception captured by this catch handler. /// </summary> /// <param name="graph">The control-flow graph that defines the landing pad.</param> /// <returns>A value that identifies the exception captured by this catch handler.</returns> public ValueTag GetCapturedException(FlowGraph graph) { return(graph.GetBasicBlock(landingPadTag).Parameters[0]); }