/// <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); } }
/// <summary> /// Tries to move an instruction from its /// current location to just before another instruction. /// </summary> /// <param name="instruction"> /// The instruction to try and move. /// </param> /// <param name="block"> /// The block that defines the insertion point. /// </param> /// <param name="insertionPoint"> /// An instruction to which the instruction should /// be moved. It must be defined in the same basic /// block as <paramref name="instruction"/>. /// </param> /// <param name="graph"> /// The graph that defines both instructions. /// </param> /// <returns> /// <c>true</c> if <paramref name="instruction"/> was /// successfully reordered; otherwise, <c>false</c>. /// </returns> private static bool TryReorder( ValueTag instruction, BasicBlockTag block, ref LinkedListNode <ValueTag> insertionPoint, FlowGraph graph) { if (graph.GetValueParent(instruction).Tag != block || !graph.ContainsInstruction(instruction)) { return(false); } // Grab the ordering to which we should adhere. var ordering = graph.GetAnalysisResult <InstructionOrdering>(); // Start at the linked list node belonging to the instruction // to move and work our way toward the insertion point. // Check the must-run-before relation as we traverse the list. var instructionNode = GetInstructionNode(instruction, insertionPoint); var currentNode = instructionNode; while (currentNode != insertionPoint) { if (ordering.MustRunBefore(instruction, currentNode.Value)) { // Aw snap, we encountered a dependency. // Time to abandon ship. return(false); } currentNode = currentNode.Next; } // Looks like we can reorder the instruction! if (insertionPoint != instructionNode) { insertionPoint.List.Remove(instructionNode); insertionPoint.List.AddBefore(insertionPoint, instructionNode); } insertionPoint = instructionNode; return(true); }
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); }
/// <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)); }
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); }
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)); }