Ejemplo n.º 1
0
        /// <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()));
        }
Ejemplo n.º 2
0
        /// <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));
        }
Ejemplo n.º 3
0
        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);
        }
Ejemplo n.º 4
0
        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);
        }
Ejemplo n.º 5
0
        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);
        }
Ejemplo n.º 6
0
        /// <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));
        }
Ejemplo n.º 7
0
        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);
        }
Ejemplo n.º 8
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));
        }
Ejemplo n.º 9
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);
        }
Ejemplo n.º 10
0
        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));
        }
Ejemplo n.º 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);

            // 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);
        }
Ejemplo n.º 12
0
 /// <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]);
 }