/// <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));
            }
        }
Exemple #2
0
        /// <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);
            }
        }
Exemple #5
0
        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);
        }
Exemple #6
0
        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)));
        }
Exemple #7
0
        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));
        }
Exemple #10
0
        /// <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));
        }
Exemple #11
0
        /// <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);
        }
Exemple #12
0
        /// <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);
        }