/// <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); }