protected internal override void VisitBlockContainer(BlockContainer container) { var oldExit = currentExit; var oldContainer = currentContainer; var thisExit = GetExit(container); currentExit = thisExit; currentContainer = container; base.VisitBlockContainer(container); if (thisExit == ExitNotYetDetermined && currentExit != ExitNotYetDetermined) { // This transform determined an exit point. Debug.Assert(!currentExit.MatchLeave(currentContainer)); ILInstruction inst = container; // traverse up to the block (we'll always find one because GetExit // only returns ExitNotYetDetermined if there's a block) while (inst.Parent.OpCode != OpCode.Block) { inst = inst.Parent; } Block block = (Block)inst.Parent; block.Instructions.Add(currentExit); } else { Debug.Assert(thisExit == currentExit); } currentExit = oldExit; currentContainer = oldContainer; }
protected internal override void VisitBlockContainer(BlockContainer container) { var oldExit = currentExit; var oldContainer = currentContainer; var oldPotentialExits = potentialExits; var thisExit = GetExit(container); currentExit = thisExit; currentContainer = container; potentialExits = (thisExit == ExitNotYetDetermined ? new List <ILInstruction>() : null); base.VisitBlockContainer(container); if (thisExit == ExitNotYetDetermined && potentialExits.Count > 0) { // This transform determined an exit point. currentExit = ChooseExit(potentialExits); foreach (var exit in potentialExits) { if (CompatibleExitInstruction(currentExit, exit)) { exit.ReplaceWith(new Leave(currentContainer) { ILRange = exit.ILRange }); } } Debug.Assert(!currentExit.MatchLeave(currentContainer)); ILInstruction inst = container; // traverse up to the block (we'll always find one because GetExit // only returns ExitNotYetDetermined if there's a block) while (inst.Parent.OpCode != OpCode.Block) { inst = inst.Parent; } Block block = (Block)inst.Parent; if (block.HasFlag(InstructionFlags.EndPointUnreachable)) { // Special case: despite replacing the exits with leave(currentContainer), // we still have an unreachable endpoint. // The appended currentExit instruction would not be reachable! // This happens in test case ExceptionHandling.ThrowInFinally() if (currentExit is Branch b) { blocksPotentiallyMadeUnreachable.Add(b.TargetBlock); } } else { block.Instructions.Add(currentExit); } } else { Debug.Assert(thisExit == currentExit); } currentExit = oldExit; currentContainer = oldContainer; potentialExits = oldPotentialExits; }
bool ShouldSwapIfTargets(ILInstruction inst1, ILInstruction inst2) { Block block1 = null, block2 = null; if (inst1.MatchBranch(out block1) && inst2.MatchBranch(out block2)) { // prefer arranging stuff in IL order return(block1.ILRange.Start > block2.ILRange.Start); } BlockContainer container1, container2; if (inst1.MatchLeave(out container1) && container1.Parent is TryInstruction) { // 'leave tryBlock' is considered to have a later target than // any branch within the container, and also a later target // than a return instruction. // This is necessary to avoid "goto" statements in the // ExceptionHandling.ConditionalReturnInThrow test. if (!inst2.MatchLeave(out container2)) { container2 = block2?.Parent as BlockContainer; } return(container2 == null || container2.IsDescendantOf(container1)); } if (inst1.MatchBranch(out block1) && inst2.MatchLeave(out container2) && block1.IncomingEdgeCount > 1) { // if (..) goto x; leave c; // Unless x can be inlined, it's better to swap the order if the 'leave' // has a chance to turn into a 'break;' or 'return;' if (container2.Parent is ILFunction) { return(true); // return } if (container2.EntryPoint.IncomingEdgeCount > 1) { // break return(BlockContainer.FindClosestContainer(inst2) == container2); } } return(false); }
protected internal override void VisitBlockContainer(BlockContainer container) { var oldExit = currentExit; var oldContainer = currentContainer; var oldPotentialExits = potentialExits; var thisExit = GetExit(container); currentExit = thisExit; currentContainer = container; potentialExits = (thisExit == ExitNotYetDetermined ? new List <ILInstruction>() : null); base.VisitBlockContainer(container); if (thisExit == ExitNotYetDetermined && potentialExits.Count > 0) { // This transform determined an exit point. currentExit = ChooseExit(potentialExits); foreach (var exit in potentialExits) { if (CompatibleExitInstruction(currentExit, exit)) { exit.ReplaceWith(new Leave(currentContainer) { ILRange = exit.ILRange }); } } Debug.Assert(!currentExit.MatchLeave(currentContainer)); ILInstruction inst = container; // traverse up to the block (we'll always find one because GetExit // only returns ExitNotYetDetermined if there's a block) while (inst.Parent.OpCode != OpCode.Block) { inst = inst.Parent; } Block block = (Block)inst.Parent; block.Instructions.Add(currentExit); } else { Debug.Assert(thisExit == currentExit); } currentExit = oldExit; currentContainer = oldContainer; potentialExits = oldPotentialExits; }