static IMethod ImplementIntoCaseConversion(this VirtualType type, IType valueType, IMethod caseCtor, bool isImplicit = true) { Debug.Assert(valueType == caseCtor.Parameters.Single().Type); Debug.Assert(valueType.Kind != TypeKind.Interface); // can't have conversion from interface Debug.Assert(valueType != type); // can't have conversion from itself var caseFactory = new VirtualMethod(type, Accessibility.Public, isImplicit ? "op_Implicit" : "op_Explicit", new[] { new VirtualParameter(valueType, "item") }, returnType: type, isStatic: true ); caseFactory.BodyFactory = () => { var @this = new IL.ILVariable(IL.VariableKind.Parameter, valueType, 0); IL.ILInstruction body = new IL.NewObj(caseCtor) { Arguments = { new IL.LdLoc(@this) } }; if (valueType.IsReferenceType == true) { // pass nulls body = new IL.IfInstruction( new IL.Comp(IL.ComparisonKind.Inequality, Sign.None, new IL.LdLoc(@this), new IL.LdNull()), body, new IL.LdNull() ); } return(EmitExtensions.CreateExpressionFunction(caseFactory, body)); }; type.Methods.Add(caseFactory); return(caseFactory); }
/// <summary> /// if (cond) { ...; exit; } else { ... } /// ...; /// -> /// if (cond) { ...; exit; } /// ...; /// ...; /// </summary> /// <param name="ifInst"></param> private void ExtractElseBlock(IfInstruction ifInst) { Debug.Assert(ifInst.TrueInst.HasFlag(InstructionFlags.EndPointUnreachable)); var block = (Block)ifInst.Parent; var falseBlock = (Block)ifInst.FalseInst; context.Step("Extract else block", ifInst); int insertAt = block.Instructions.IndexOf(ifInst) + 1; for (int i = 0; i < falseBlock.Instructions.Count; i++) { block.Instructions.Insert(insertAt++, falseBlock.Instructions[i]); } ifInst.FalseInst = new Nop(); }
/// <summary> /// For an if statement with an unreachable end point and no else block, /// inverts to match IL order of the first statement of each branch /// </summary> private void ImproveILOrdering(Block block, IfInstruction ifInst) { if (!block.HasFlag(InstructionFlags.EndPointUnreachable) || !ifInst.TrueInst.HasFlag(InstructionFlags.EndPointUnreachable) || !ifInst.FalseInst.MatchNop()) { return; } Debug.Assert(ifInst != block.Instructions.Last()); var trueRangeStart = ConditionDetection.GetStartILOffset(ifInst.TrueInst, out bool trueRangeIsEmpty); var falseRangeStart = ConditionDetection.GetStartILOffset(block.Instructions[block.Instructions.IndexOf(ifInst) + 1], out bool falseRangeIsEmpty); if (!trueRangeIsEmpty && !falseRangeIsEmpty && falseRangeStart < trueRangeStart) { ConditionDetection.InvertIf(block, ifInst, context); } }
/// <summary> /// Reduce Nesting in if/else statements by duplicating an exit instruction. /// Does not affect IL order /// </summary> private bool ReduceNesting(Block block, IfInstruction ifInst, ILInstruction exitInst) { // start tallying stats for heuristics from then and else-if blocks int maxStatements = 0, maxDepth = 0; UpdateStats(ifInst.TrueInst, ref maxStatements, ref maxDepth); // if (cond) { ... } exit; if (ifInst.FalseInst.MatchNop()) { // a separate heuristic tp ShouldReduceNesting as there is visual balancing to be performed based on number of statments if (maxDepth < 2) { return(false); } // -> // if (!cond) exit; // ...; exit; EnsureEndPointUnreachable(ifInst.TrueInst, exitInst); EnsureEndPointUnreachable(block, exitInst); ConditionDetection.InvertIf(block, ifInst, context); return(true); } // else-if trees are considered as a single group from the root IfInstruction if (GetElseIfParent(ifInst) != null) { return(false); } // find the else block and tally stats for each else-if block while (Block.Unwrap(ifInst.FalseInst) is IfInstruction elseIfInst) { UpdateStats(elseIfInst.TrueInst, ref maxStatements, ref maxDepth); ifInst = elseIfInst; } if (!ShouldReduceNesting(ifInst.FalseInst, maxStatements, maxDepth)) { return(false); } // extract the else block and insert exit points all the way up the else-if tree do { var elseIfInst = GetElseIfParent(ifInst); // if (cond) { ... } else { ... } exit; // -> // if (cond) { ...; exit; } // ...; exit; EnsureEndPointUnreachable(ifInst.TrueInst, exitInst); if (ifInst.FalseInst.HasFlag(InstructionFlags.EndPointUnreachable)) { Debug.Assert(ifInst.HasFlag(InstructionFlags.EndPointUnreachable)); Debug.Assert(ifInst.Parent == block); int removeAfter = ifInst.ChildIndex + 1; if (removeAfter < block.Instructions.Count) { // Remove all instructions that ended up dead // (this should just be exitInst itself) Debug.Assert(block.Instructions.SecondToLastOrDefault() == ifInst); Debug.Assert(block.Instructions.Last() == exitInst); block.Instructions.RemoveRange(removeAfter, block.Instructions.Count - removeAfter); } } ExtractElseBlock(ifInst); ifInst = elseIfInst; } while (ifInst != null); return(true); }