/// <summary> /// Checks if an exit is a duplicable keyword exit (return; break; continue;) /// </summary> private bool CanDuplicateExit(ILInstruction exit, Block continueTarget, out ILInstruction keywordExit) { keywordExit = exit; if (exit != null && exit.MatchBranch(continueTarget)) { return(true); // keyword is continue } if (!(exit is Leave leave && leave.Value.MatchNop())) { return(false); // don't duplicate valued returns } if (leave.IsLeavingFunction || leave.TargetContainer.Kind != ContainerKind.Normal) { return(true); // keyword is return || break } // leave from a try/pinned/lock etc, check if the target (the instruction following the target container) is duplicable, if so, set keywordExit to that ILInstruction leavingInst = leave.TargetContainer; Debug.Assert(!leavingInst.HasFlag(InstructionFlags.EndPointUnreachable)); while (!(leavingInst.Parent is Block b) || leavingInst == b.Instructions.Last()) { if (leavingInst.Parent is TryFinally tryFinally) { if (leavingInst.SlotInfo == TryFinally.FinallyBlockSlot) // cannot duplicate leaves from finally containers { Debug.Assert(leave.TargetContainer == tryFinally.FinallyBlock); //finally cannot have control flow return(false); } if (tryFinally.HasFlag(InstructionFlags.EndPointUnreachable)) // finally block changes return value/throws an exception? Yikes. Lets leave it alone { Debug.Assert(tryFinally.FinallyBlock.HasFlag(InstructionFlags.EndPointUnreachable)); return(false); } } else if (leavingInst.Parent is TryFault tryFault && leavingInst.SlotInfo == TryFault.FaultBlockSlot) // cannot duplicate leaves from fault containers either { Debug.Assert(leave.TargetContainer == tryFault.FaultBlock); return(false); } leavingInst = leavingInst.Parent; Debug.Assert(!leavingInst.HasFlag(InstructionFlags.EndPointUnreachable)); Debug.Assert(!(leavingInst is ILFunction)); } var block = (Block)leavingInst.Parent; var targetInst = block.Instructions[block.Instructions.IndexOf(leavingInst) + 1]; return(CanDuplicateExit(targetInst, continueTarget, out keywordExit)); }
static bool Inst2MightWriteToVariableReadByInst1(ILInstruction inst1, ILInstruction inst2) { if (!inst1.HasFlag(InstructionFlags.MayReadLocals)) { // quick exit if inst1 doesn't read any variables return(false); } var variables = inst1.Descendants.OfType <LdLoc>().Select(load => load.Variable).ToHashSet(); if (inst2.HasFlag(InstructionFlags.SideEffect) && variables.Any(v => v.AddressCount > 0)) { // If inst2 might have indirect writes, we cannot reorder with any loads of variables that have their address taken. return(true); } foreach (var inst in inst2.Descendants) { if (inst.HasDirectFlag(InstructionFlags.MayWriteLocals)) { ILVariable v = ((IInstructionWithVariableOperand)inst).Variable; if (variables.Contains(v)) { return(true); } } } return(false); }
/// <summary> /// Ensures the end point of a block is unreachable by duplicating and appending the [exit] instruction following the end point /// </summary> /// <param name="inst">The instruction/block of interest</param> /// <param name="fallthroughExit">The next instruction to be executed (provided inst does not exit)</param> private void EnsureEndPointUnreachable(ILInstruction inst, ILInstruction fallthroughExit) { if (!(inst is Block block)) { Debug.Assert(inst.HasFlag(InstructionFlags.EndPointUnreachable)); return; } if (!block.HasFlag(InstructionFlags.EndPointUnreachable)) { context.Step("Duplicate block exit", fallthroughExit); block.Instructions.Add(fallthroughExit.Clone()); } }