/// <summary>
        ///   if (...) br trueBlock;
        /// ->
        ///   if (...) { trueBlock... }
        ///
        /// Only inlines branches that are strictly dominated by this block (incoming edge count == 1)
        /// </summary>
        private bool InlineTrueBranch(Block block, IfInstruction ifInst)
        {
            if (!CanInline(ifInst.TrueInst))
            {
                if (block.Instructions.SecondToLastOrDefault() == ifInst && ifInst.FalseInst.MatchNop())
                {
                    var exitInst = block.Instructions.Last();
                    if (DetectExitPoints.CompatibleExitInstruction(ifInst.TrueInst, exitInst))
                    {
                        // if (...) exitInst; exitInst;
                        context.Step("Use empty block as then-branch", ifInst.TrueInst);
                        ifInst.TrueInst = new Nop().WithILRange(ifInst.TrueInst);
                        // false, because we didn't inline a real block
                        // this will cause HandleIfInstruction() to attempt to inline the exitInst.
                        return(false);
                    }
                }
                return(false);
            }

            context.Step("Inline block as then-branch", ifInst.TrueInst);
            // The targetBlock was already processed, and is ready to embed
            var targetBlock = ((Branch)ifInst.TrueInst).TargetBlock;

            targetBlock.AddRef();              // Peformance: avoid temporarily disconnecting targetBlock
            targetBlock.Remove();
            ifInst.TrueInst = targetBlock;
            targetBlock.ReleaseRef();

            return(true);
        }
        /// <summary>
        /// Looks for common exits in the inlined then and else branches of an if instruction
        /// and performs inversions and simplifications to merge them provided they don't
        /// isolate a higher priority block exit
        /// </summary>
        private void MergeCommonBranches(Block block, IfInstruction ifInst)
        {
            var thenExits = new List <ILInstruction>();

            AddExits(ifInst.TrueInst, 0, thenExits);
            if (thenExits.Count == 0)
            {
                return;
            }

            // if there are any exits from the then branch, then the else is redundant and shouldn't exist
            Debug.Assert(IsEmpty(ifInst.FalseInst));
            Debug.Assert(ifInst.Parent == block);
            var elseExits      = new List <ILInstruction>();
            int falseInstIndex = block.Instructions.IndexOf(ifInst) + 1;

            AddExits(block, falseInstIndex, elseExits);

            var commonExits = elseExits.Where(e1 => thenExits.Any(e2 => DetectExitPoints.CompatibleExitInstruction(e1, e2)));

            // find the common exit with the highest block exit priority
            ILInstruction commonExit = null;

            foreach (var exit in commonExits)
            {
                if (commonExit == null || CompareBlockExitPriority(exit, commonExit) > 0)
                {
                    commonExit = exit;
                }
            }

            if (commonExit == null)
            {
                return;
            }

            // if the current block exit has higher priority than the exits to merge,
            // determine if this merge will isolate the current block exit
            // that is, no sequence of inversions can restore it to the block exit position
            var blockExit = block.Instructions.Last();

            if (CompareBlockExitPriority(blockExit, commonExit, true) > 0 && !WillShortCircuit(block, ifInst, commonExit))
            {
                return;
            }

            // could improve performance by directly implementing the || short-circuit when WillShortCircuit
            // currently the same general sequence of transformations introduces both operators

            context.StepStartGroup("Merge common branches " + commonExit, ifInst);
            ProduceExit(ifInst.TrueInst, 0, commonExit);
            ProduceExit(block, falseInstIndex, commonExit);

            // if (...) { ...; blockExit; } ...; blockExit;
            // -> if (...) { ...; blockExit; } else { ... } blockExit;
            if (ifInst != block.Instructions.SecondToLastOrDefault())
            {
                context.Step("Embed else-block for goto removal", ifInst);
                Debug.Assert(IsEmpty(ifInst.FalseInst));
                ifInst.FalseInst = ExtractBlock(block, block.Instructions.IndexOf(ifInst) + 1, block.Instructions.Count - 1);
            }

            // if (...) { ...; goto blockExit; } blockExit;
            // -> if (...) { ... } blockExit;
            // OR
            // if (...) { ...; goto blockExit; } else { ... } blockExit;
            // -> if (...) { ... } else { ... } blockExit;
            context.Step("Remove redundant 'goto blockExit;' in then-branch", ifInst);
            if (!(ifInst.TrueInst is Block trueBlock) || trueBlock.Instructions.Count == 1)
            {
                ifInst.TrueInst = new Nop().WithILRange(ifInst.TrueInst);
            }