/// <summary> /// Tells if a particular value is strictly dominated by another value, /// that is, if control cannot flow to the value unless it first flowed /// through the dominator value. /// </summary> /// <param name="value"> /// An value that might be dominated by <paramref name="dominator"/>. /// </param> /// <param name="dominator"> /// An value that might dominate <paramref name="value"/>. /// </param> /// <param name="graph"> /// A graph that defines both values. /// </param> /// <returns> /// <c>true</c> if <paramref name="value"/> is strictly dominated by /// <paramref name="dominator"/>; otherwise, <c>false</c>. /// </returns> public bool IsStrictlyDominatedBy(ValueTag value, ValueTag dominator, FlowGraph graph) { var valueBlock = graph.GetValueParent(value); var dominatorBlock = graph.GetValueParent(dominator); if (valueBlock.Tag == dominatorBlock.Tag) { if (graph.ContainsBlockParameter(dominator)) { return(!graph.ContainsBlockParameter(value)); } else if (graph.ContainsBlockParameter(value)) { return(false); } else { var valueInsn = graph.GetInstruction(value); var domInsn = graph.GetInstruction(dominator); return(valueInsn.InstructionIndex > domInsn.InstructionIndex); } } else { return(IsStrictlyDominatedBy(valueBlock, dominatorBlock)); } }
/// <summary> /// Tells if a value that uses a pointer allows said pointer to escape. /// </summary> /// <param name="value">A value in the flow graph.</param> /// <param name="pointer"> /// A pointer in the flow graph that is used by <paramref name="value"/>. /// </param> /// <param name="graph">A flow graph.</param> /// <returns> /// <c>true</c> if <paramref name="pointer"/> may escape; otherwise, <c>false</c>. /// </returns> private bool AllowsEscape(ValueTag value, ValueTag pointer, FlowGraph graph) { if (!graph.ContainsInstruction(value)) { return(true); } var instruction = graph.GetInstruction(value).Instruction; if (instruction.Prototype is LoadPrototype) { // Loads never allow anything to escape. return(false); } else if (instruction.Prototype is StorePrototype) { // Stores to the pointer don't allow the pointer to escape, // but stores of the pointer to some other location may well // allow the pointer to escape. return(((StorePrototype)instruction.Prototype).GetValue(instruction) != pointer); } else { // Assume that all other instructions allow the pointer to // escape. This primitive escape analysis isn't refined enough // to actually track values, something we'd need to handle most // other benign instructions. return(true); } }
/// <inheritdoc/> public override FlowGraph Apply(FlowGraph graph) { var ordering = graph.GetAnalysisResult <InstructionOrdering>(); var builder = graph.ToBuilder(); foreach (var block in builder.BasicBlocks) { // Create a linked list that contains all instructions. var schedule = new LinkedList <ValueTag>(block.InstructionTags); // Plus a dummy node for the block's flow. schedule.AddLast((ValueTag)null); // Reorder branch arguments. foreach (var branch in block.Flow.Branches.Reverse()) { ReorderArguments( branch.Arguments .Select(arg => arg.ValueOrNull) .Where(arg => arg != null), block, schedule.Last, ordering, builder.ImmutableGraph); } // Reorder anonymous instruction arguments. foreach (var instruction in block.Flow.Instructions.Reverse()) { ReorderArguments(instruction, block, schedule.Last, ordering, builder.ImmutableGraph); } // Reorder named instruction arguments in reverse. var finger = schedule.Last.Previous; while (finger != null) { var instruction = graph.GetInstruction(finger.Value); ReorderArguments(instruction.Instruction, block, finger, ordering, builder.ImmutableGraph); finger = finger.Previous; } // Materialize the new schedule. int index = 0; foreach (var value in schedule) { if (value == null) { continue; } var instruction = builder.GetInstruction(value); instruction.MoveTo(index, block); index++; } } return(builder.ToImmutable()); }
/// <summary> /// Tells if it is permissible to delay exceptions thrown by a /// particular instruction until the instruction's result is used /// by an effectful instruction. /// If the instruction's result is never used that way, /// the exception may even be deleted altogether. /// </summary> /// <param name="graph"> /// The flow graph that defines the instruction. /// </param> /// <param name="instruction"> /// An instruction tag to examine. /// </param> /// <returns> /// <c>true</c> if exceptions thrown by <paramref name="instruction"/> /// may be delayed until its value is used by an effectful instruction; /// otherwise, <c>false</c>. /// </returns> public static bool CanDelayExceptions(this FlowGraph graph, ValueTag instruction) { ExceptionDelayability delayability; if (graph.TryGetAnalysisResult(out delayability)) { return(delayability.CanDelayExceptions( graph.GetInstruction(instruction).Prototype)); } else { return(false); } }
private static bool IsDefaultInitialization(Instruction instruction, FlowGraph graph) { var proto = instruction.Prototype; if (proto is StorePrototype) { var storeProto = (StorePrototype)proto; var value = storeProto.GetValue(instruction); if (graph.ContainsInstruction(value)) { var valueProto = graph.GetInstruction(value).Prototype as ConstantPrototype; if (valueProto != null && valueProto.Value == DefaultConstant.Instance) { return(true); } } } return(false); }
private LatticeCell UpdateInstructionCell( ValueTag value, Dictionary <ValueTag, LatticeCell> cells, FlowGraph graph) { LatticeCell cell; if (!cells.TryGetValue(value, out cell)) { cell = LatticeCell.Top; } if (cell.Kind == LatticeCellKind.Bottom) { // Early-out if we're already dealing // with a bottom cell. return(cell); } var instruction = graph.GetInstruction(value).Instruction; return(cell.Meet(EvaluateInstruction(instruction, cells, graph))); }
private TCell UpdateInstructionCell( ValueTag value, Dictionary <ValueTag, TCell> cells, FlowGraph graph) { TCell cell; if (!cells.TryGetValue(value, out cell)) { cell = Top; } if (Equals(cell, Bottom)) { // Early-out if we're already dealing // with a bottom cell. return(cell); } var instruction = graph.GetInstruction(value); return(Meet(cell, Evaluate(instruction, cells))); }
/// <summary> /// Looks for an instruction that is semantically /// equivalent to a given instruction but minimizes /// the number of layers of indirection erected by /// copies. /// </summary> /// <param name="instruction"> /// The instruction to simplify. /// </param> /// <param name="graph"> /// The graph that defines the instruction. /// </param> /// <returns> /// A semantically equivalent instruction.</returns> private static Instruction SimplifyInstruction( Instruction instruction, FlowGraph graph) { if (instruction.Prototype is CopyPrototype) { var copyProto = (CopyPrototype)instruction.Prototype; var copiedVal = copyProto.GetCopiedValue(instruction); if (graph.ContainsInstruction(copiedVal)) { var copiedInsn = graph.GetInstruction(copiedVal).Instruction; // Only simplify copies of arithmetic intriniscs and constants. // Those are the only instructions we're actually // interested in and they don't have any "funny" behavior. if (copiedInsn.Prototype is ConstantPrototype || copiedInsn.Prototype is CopyPrototype || ArithmeticIntrinsics.IsArithmeticIntrinsicPrototype(copiedInsn.Prototype)) { return(SimplifyInstruction(copiedInsn, graph)); } } } return(instruction); }
/// <inheritdoc/> public InstructionOrdering Analyze(FlowGraph graph) { // This analysis is based on the following rules: // // 1. There is a total ordering between effectful // instructions: effectful instructions can never // be reordered wrt each other. That is, every // effectful instruction depends on the previous // effectful instruction. // // 2. `load` instructions depend on the last effectful // instruction. // // 3. All instructions depend on their arguments, provided // that these arguments refer to instructions inside the // same basic block. // // 4. Dependencies are transitive. var effectfuls = graph.GetAnalysisResult <EffectfulInstructions>(); var dependencies = new Dictionary <ValueTag, HashSet <ValueTag> >(); foreach (var block in graph.BasicBlocks) { ValueTag lastEffectfulTag = null; foreach (var selection in block.NamedInstructions) { var insnDependencies = new HashSet <ValueTag>(); var instruction = selection.Instruction; if (instruction.Prototype is LoadPrototype && lastEffectfulTag != null) { // Rule #2: `load` instructions depend on the last effectful // instruction. if (lastEffectfulTag != null) { insnDependencies.Add(lastEffectfulTag); } } if (effectfuls.Instructions.Contains(selection.Tag)) { // Rule #1: every effectful instruction depends on the previous // effectful instruction. if (lastEffectfulTag != null) { insnDependencies.Add(lastEffectfulTag); } lastEffectfulTag = selection.Tag; } // Rule #3: all instructions depend on their arguments, provided // that these arguments refer to instructions inside the // same basic block. foreach (var arg in instruction.Arguments) { if (graph.ContainsInstruction(arg) && graph.GetInstruction(arg).Block.Tag == block.Tag) { insnDependencies.Add(arg); } } // Rule #4: dependencies are transitive. foreach (var item in insnDependencies.ToArray()) { if (dependencies.ContainsKey(item)) { insnDependencies.UnionWith(dependencies[item]); } } dependencies[selection.Tag] = insnDependencies; } } return(new DependencyBasedInstructionOrdering(dependencies)); }
/// <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); }
/// <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); }