/// <summary> /// if (comp(ldloc v == ldnull)) { /// stloc v(DelegateConstruction) /// } /// => /// stloc v(DelegateConstruction) /// </summary> bool CachedDelegateInitializationWithLocal(IfInstruction inst) { Block trueInst = inst.TrueInst as Block; if (trueInst == null || (trueInst.Instructions.Count != 1) || !inst.FalseInst.MatchNop()) { return(false); } if (!inst.Condition.MatchCompEquals(out ILInstruction left, out ILInstruction right) || !left.MatchLdLoc(out ILVariable v) || !right.MatchLdNull()) { return(false); } var storeInst = trueInst.Instructions.Last(); if (!storeInst.MatchStLoc(v, out ILInstruction value)) { return(false); } if (!DelegateConstruction.IsDelegateConstruction(value as NewObj, true)) { return(false); } // do not transform if there are other stores/loads of this variable if (v.StoreCount != 2 || v.StoreInstructions.Count != 2 || v.LoadCount != 2 || v.AddressCount != 0) { return(false); } // do not transform if the first assignment is not assigning null: var otherStore = v.StoreInstructions.OfType <StLoc>().SingleOrDefault(store => store != storeInst); if (otherStore == null || !otherStore.Value.MatchLdNull() || !(otherStore.Parent is Block)) { return(false); } // do not transform if there is no usage directly afterwards var nextInstruction = inst.Parent.Children.ElementAtOrDefault(inst.ChildIndex + 1); if (nextInstruction == null) { return(false); } var usages = nextInstruction.Descendants.Where(i => i.MatchLdLoc(v)).ToArray(); if (usages.Length != 1) { return(false); } context.Step("CachedDelegateInitializationWithLocal", inst); ((Block)otherStore.Parent).Instructions.Remove(otherStore); inst.ReplaceWith(storeInst); return(true); }
public virtual void VisitIf(IfInstruction x) { x.Condition.VisitMe(this); x.IfBranch.VisitMe(this); if (x.ElseBranch != null) { x.ElseBranch.VisitMe(this); } VisitInstruction(x); }
protected internal override void VisitIfInstruction(IfInstruction inst) { DebugStartPoint(inst); inst.Condition.AcceptVisitor(this); State branchState = state.Clone(); inst.TrueInst.AcceptVisitor(this); State afterTrueState = state; state = branchState; inst.FalseInst.AcceptVisitor(this); state.JoinWith(afterTrueState); DebugEndPoint(inst); }
/// <summary> /// Builds structured control flow for the block associated with the control flow node. /// </summary> /// <remarks> /// After a block was processed, it should use structured control flow /// and have just a single 'regular' exit point (last branch instruction in the block) /// </remarks> public void Run(Block block, BlockTransformContext context) { this.context = context; this.currentContainer = (BlockContainer)block.Parent; // We only embed blocks into this block if they aren't referenced anywhere else, // so those blocks are dominated by this block. // BlockILTransform thus guarantees that the blocks being embedded are already // fully processed. var cfgNode = context.ControlFlowNode; Debug.Assert(cfgNode.UserData == block); // Because this transform runs at the beginning of the block transforms, // we know that `block` is still a (non-extended) basic block. // Last instruction is one with unreachable endpoint // (guaranteed by combination of BlockContainer and Block invariants) Debug.Assert(block.Instructions.Last().HasFlag(InstructionFlags.EndPointUnreachable)); ILInstruction exitInst = block.Instructions.Last(); // Previous-to-last instruction might have conditional control flow, // usually an IfInstruction with a branch: IfInstruction ifInst = block.Instructions.SecondToLastOrDefault() as IfInstruction; if (ifInst != null && ifInst.FalseInst.OpCode == OpCode.Nop) { HandleIfInstruction(cfgNode, block, ifInst, ref exitInst); } else { SwitchInstruction switchInst = block.Instructions.SecondToLastOrDefault() as SwitchInstruction; if (switchInst != null) { HandleSwitchInstruction(cfgNode, block, switchInst, ref exitInst); } } if (IsUsableBranchToChild(cfgNode, exitInst)) { // "...; goto usableblock;" // -> embed target block in this block context.Step("Inline target block of unconditional branch", exitInst); var targetBlock = ((Branch)exitInst).TargetBlock; Debug.Assert(exitInst == block.Instructions.Last()); block.Instructions.RemoveAt(block.Instructions.Count - 1); block.Instructions.AddRange(targetBlock.Instructions); targetBlock.Remove(); } }
public void TestValues() { VirtualMachine vm = new VirtualMachine(); List <byte> actual = new IfInstruction() .Condition(_condition) .Body(b => { b.Literal(_val); }) .ToInstructions(); TestHelper.AssertResultsEqual(_expected, actual); }
public void TestExpressions() { VirtualMachine vm = new VirtualMachine(); List <byte> actual = new IfInstruction() .Condition(new LiteralInstruction((byte)(_condition ? 1 : 0))) .Body(b => { b.Literal(_val); }) .ToInstructions(); TestHelper.AssertResultsEqual(_expected, actual); }
/// <summary> /// if (comp.i4(comp.o(ldobj delegateType(ldsflda CachedAnonMethodDelegate) != ldnull) == ldc.i4 0)) Block { /// stloc s(stobj(ldflda(CachedAnonMethodDelegate), DelegateConstruction)) /// } else Block { /// stloc s(ldobj System.Action(ldsflda $I4-1)) /// } /// => /// stloc s(DelegateConstruction) /// </summary> bool CachedDelegateInitializationVB(IfInstruction inst) { if (!(inst.TrueInst is Block trueInst && inst.FalseInst is Block falseInst)) { return(false); } if (trueInst.Instructions.Count != 1 || falseInst.Instructions.Count != 1) { return(false); } if (!(trueInst.Instructions[0].MatchStLoc(out var s, out var trueInitValue) && falseInst.Instructions[0].MatchStLoc(s, out var falseInitValue))) { return(false); } if (s.Kind != VariableKind.StackSlot || s.StoreCount != 2 || s.LoadCount != 1) { return(false); } if (!(trueInitValue is StObj stobj) || !(falseInitValue is LdObj ldobj)) { return(false); } if (!(stobj.Value is NewObj delegateConstruction)) { return(false); } if (!stobj.Target.MatchLdsFlda(out var field1) || !ldobj.Target.MatchLdsFlda(out var field2) || !field1.Equals(field2)) { return(false); } if (!inst.Condition.MatchCompEquals(out ILInstruction left, out ILInstruction right) || !right.MatchLdNull()) { return(false); } if (!ldobj.Match(left).Success) { return(false); } if (!DelegateConstruction.MatchDelegateConstruction(delegateConstruction, out _, out _, out _, true)) { return(false); } context.Step("CachedDelegateInitializationVB", inst); inst.ReplaceWith(new StLoc(s, delegateConstruction)); return(true); }
public void TestNoConditionOrBody() { VirtualMachine vm = new VirtualMachine(); _expected = new List <byte>() { (byte)InstructionsEnum.If, (byte)InstructionsEnum.EndIf }; List <byte> actual = new IfInstruction() .ToInstructions(); TestHelper.AssertResultsEqual(_expected, actual); }
/// <summary> /// Main entry point into the normal code path of this transform. /// Called by expression transform. /// </summary> public bool Run(IfInstruction ifInst) { if (!context.Settings.LiftNullables) { return(false); } var lifted = Lift(ifInst, ifInst.TrueInst, ifInst.FalseInst); if (lifted != null) { ifInst.ReplaceWith(lifted); return(true); } return(false); }
void VisitLogicNot(Comp inst, ILInstruction arg) { ILInstruction lhs, rhs; if (arg is Comp comp) { if ((!comp.InputType.IsFloatType() && !comp.IsLifted) || comp.Kind.IsEqualityOrInequality()) { context.Step("push negation into comparison", inst); comp.Kind = comp.Kind.Negate(); comp.AddILRange(inst); inst.ReplaceWith(comp); } comp.AcceptVisitor(this); } else if (arg.MatchLogicAnd(out lhs, out rhs)) { // logic.not(if (lhs) rhs else ldc.i4 0) // ==> if (logic.not(lhs)) ldc.i4 1 else logic.not(rhs) context.Step("push negation into logic.and", inst); IfInstruction ifInst = (IfInstruction)arg; var ldc0 = ifInst.FalseInst; Debug.Assert(ldc0.MatchLdcI4(0)); ifInst.Condition = Comp.LogicNot(lhs).WithILRange(inst); ifInst.TrueInst = new LdcI4(1).WithILRange(ldc0); ifInst.FalseInst = Comp.LogicNot(rhs).WithILRange(inst); inst.ReplaceWith(ifInst); ifInst.AcceptVisitor(this); } else if (arg.MatchLogicOr(out lhs, out rhs)) { // logic.not(if (lhs) ldc.i4 1 else rhs) // ==> if (logic.not(lhs)) logic.not(rhs) else ldc.i4 0) context.Step("push negation into logic.or", inst); IfInstruction ifInst = (IfInstruction)arg; var ldc1 = ifInst.TrueInst; Debug.Assert(ldc1.MatchLdcI4(1)); ifInst.Condition = Comp.LogicNot(lhs).WithILRange(inst); ifInst.TrueInst = Comp.LogicNot(rhs).WithILRange(inst); ifInst.FalseInst = new LdcI4(0).WithILRange(ldc1); inst.ReplaceWith(ifInst); ifInst.AcceptVisitor(this); } else { arg.AcceptVisitor(this); } }
ILInstruction Lift(IfInstruction ifInst, ILInstruction trueInst, ILInstruction falseInst) { if (!MatchNullableCtor(trueInst, out var utype, out var exprToLift)) { return(null); } if (!MatchNull(falseInst, utype)) { return(null); } ILInstruction lifted; if (nullableVars.Count == 1 && MatchGetValueOrDefault(exprToLift, nullableVars[0])) { // v != null ? call GetValueOrDefault(ldloca v) : null // => conv.nop.lifted(ldloc v) // This case is handled separately from DoLift() because // that doesn't introduce nop-conversions. context.Step("if => conv.nop.lifted", ifInst); var inputUType = NullableType.GetUnderlyingType(nullableVars[0].Type); lifted = new Conv( new LdLoc(nullableVars[0]), inputUType.GetStackType(), inputUType.GetSign(), utype.ToPrimitiveType(), checkForOverflow: false, isLifted: true ) { ILRange = ifInst.ILRange }; } else { context.Step("NullableLiftingTransform.DoLift", ifInst); BitSet bits; (lifted, bits) = DoLift(exprToLift); if (lifted != null && !bits.All(0, nullableVars.Count)) { // don't lift if a nullableVar doesn't contribute to the result lifted = null; } } if (lifted != null) { Debug.Assert(lifted is ILiftableInstruction liftable && liftable.IsLifted && liftable.UnderlyingResultType == exprToLift.ResultType); } return(lifted); }
protected internal override void VisitIfInstruction(IfInstruction inst) { inst.TrueInst.AcceptVisitor(this); inst.FalseInst.AcceptVisitor(this); inst = HandleConditionalOperator(inst); // Bring LogicAnd/LogicOr into their canonical forms: // if (cond) ldc.i4 0 else RHS --> if (!cond) RHS else ldc.i4 0 // if (cond) RHS else ldc.i4 1 --> if (!cond) ldc.i4 1 else RHS // Be careful: when both LHS and RHS are the constant 1, we must not // swap the arguments as it would lead to an infinite transform loop. if (inst.TrueInst.MatchLdcI4(0) && !inst.FalseInst.MatchLdcI4(0) || inst.FalseInst.MatchLdcI4(1) && !inst.TrueInst.MatchLdcI4(1)) { context.Step("canonicalize logic and/or", inst); var t = inst.TrueInst; inst.TrueInst = inst.FalseInst; inst.FalseInst = t; inst.Condition = Comp.LogicNot(inst.Condition); } // Process condition after our potential modifications. inst.Condition.AcceptVisitor(this); if (new NullableLiftingTransform(context).Run(inst)) { return; } if (TransformDynamicAddAssignOrRemoveAssign(inst)) { return; } if (inst.MatchIfInstructionPositiveCondition(out var condition, out var trueInst, out var falseInst)) { ILInstruction transformed = UserDefinedLogicTransform.Transform(condition, trueInst, falseInst); if (transformed == null) { transformed = UserDefinedLogicTransform.TransformDynamic(condition, trueInst, falseInst); } if (transformed != null) { context.Step("User-defined short-circuiting logic operator (roslyn pattern)", condition); transformed.AddILRange(inst); inst.ReplaceWith(transformed); return; } } }
/// <summary> /// if (...) br trueBlock; /// -> /// if (...) { trueBlock... } /// /// Only inlines branches that are strictly dominated by this block (incoming edge count == 1) /// </summary> private bool InlineTrueBranch(IfInstruction ifInst) { if (!CanInline(ifInst.TrueInst)) { 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.Remove(); ifInst.TrueInst = targetBlock; return(true); }
private ILInstruction CombineConditions(ILInstruction[] conditions) { ILInstruction condition = null; foreach (var c in conditions) { if (condition == null) { condition = Comp.LogicNot(c); } else { condition = IfInstruction.LogicAnd(Comp.LogicNot(c), condition); } } return(condition); }
protected internal override void VisitIfInstruction(IfInstruction inst) { // Bring LogicAnd/LogicOr into their canonical forms: // if (cond) ldc.i4 0 else RHS --> if (!cond) RHS else ldc.i4 0 // if (cond) RHS else ldc.i4 1 --> if (!cond) ldc.i4 1 else RHS // Be careful: when both LHS and RHS are the constant 1, we must not // swap the arguments as it would lead to an infinite transform loop. if (inst.TrueInst.MatchLdcI4(0) && !inst.FalseInst.MatchLdcI4(0) || inst.FalseInst.MatchLdcI4(1) && !inst.TrueInst.MatchLdcI4(1)) { context.Step("canonicalize logic and/or", inst); var t = inst.TrueInst; inst.TrueInst = inst.FalseInst; inst.FalseInst = t; inst.Condition = new LogicNot(inst.Condition); } base.VisitIfInstruction(inst); // if (cond) stloc (A, V1) else stloc (A, V2) --> stloc (A, if (cond) V1 else V2) Block trueInst = inst.TrueInst as Block; if (trueInst == null || trueInst.Instructions.Count != 1) { return; } Block falseInst = inst.FalseInst as Block; if (falseInst == null || falseInst.Instructions.Count != 1) { return; } ILVariable v; ILInstruction value1, value2; if (trueInst.Instructions[0].MatchStLoc(out v, out value1) && falseInst.Instructions[0].MatchStLoc(v, out value2)) { context.Step("conditional operator", inst); inst.ReplaceWith(new StLoc(v, new IfInstruction(new LogicNot(inst.Condition), value2, value1))); } }
/// <summary> /// stloc s(ldobj(ldflda(CachedAnonMethodDelegate)) /// if (comp(ldloc s == null)) { /// stloc s(stobj(ldflda(CachedAnonMethodDelegate), DelegateConstruction)) /// } /// => /// stloc s(DelegateConstruction) /// </summary> bool CachedDelegateInitializationRoslynWithLocal(IfInstruction inst) { Block trueInst = inst.TrueInst as Block; if (trueInst == null || (trueInst.Instructions.Count != 1) || !inst.FalseInst.MatchNop()) { return(false); } if (!inst.Condition.MatchCompEquals(out ILInstruction left, out ILInstruction right) || !left.MatchLdLoc(out ILVariable s) || !right.MatchLdNull()) { return(false); } var storeInst = trueInst.Instructions.Last() as StLoc; var storeBeforeIf = inst.Parent.Children.ElementAtOrDefault(inst.ChildIndex - 1) as StLoc; if (storeBeforeIf == null || storeInst == null || storeBeforeIf.Variable != s || storeInst.Variable != s) { return(false); } if (!(storeInst.Value is StObj stobj) || !(storeBeforeIf.Value is LdObj ldobj)) { return(false); } if (!(stobj.Value is NewObj)) { return(false); } if (!stobj.Target.MatchLdFlda(out var _, out var field1) || !ldobj.Target.MatchLdFlda(out var __, out var field2) || !field1.Equals(field2)) { return(false); } if (!DelegateConstruction.IsDelegateConstruction((NewObj)stobj.Value, true)) { return(false); } context.Step("CachedDelegateInitializationRoslynWithLocal", inst); storeBeforeIf.Value = stobj.Value; return(true); }
public override void VisitIf(IfInstruction x) { var condition = getValue(x.Condition); //TODO needs to be compiled as satement var tmpVariable = _compiler.GetTemporaryVariable(x.ReturnType); var ifBranch = compileBranch(x.IfBranch, tmpVariable); var elseBranch = compileBranch(x.ElseBranch, tmpVariable); Expression ifStatement; if (elseBranch == null) { ifStatement = Expression.IfThen(condition, ifBranch); } else { ifStatement = Expression.IfThenElse(condition, ifBranch, elseBranch); } emit(Expression.Block(ifStatement, tmpVariable)); }
protected internal override void VisitIfInstruction(IfInstruction inst) { // Bring LogicAnd/LogicOr into their canonical forms: // if (cond) ldc.i4 0 else RHS --> if (!cond) RHS else ldc.i4 0 // if (cond) RHS else ldc.i4 1 --> if (!cond) ldc.i4 1 else RHS // Be careful: when both LHS and RHS are the constant 1, we must not // swap the arguments as it would lead to an infinite transform loop. if (inst.TrueInst.MatchLdcI4(0) && !inst.FalseInst.MatchLdcI4(0) || inst.FalseInst.MatchLdcI4(1) && !inst.TrueInst.MatchLdcI4(1)) { context.Step("canonicalize logic and/or", inst); var t = inst.TrueInst; inst.TrueInst = inst.FalseInst; inst.FalseInst = t; inst.Condition = new LogicNot(inst.Condition); } base.VisitIfInstruction(inst); inst = HandleConditionalOperator(inst); new NullableLiftingTransform(context).Run(inst); }
/// <summary> /// if (comp(ldsfld CachedAnonMethodDelegate == ldnull)) { /// stsfld CachedAnonMethodDelegate(DelegateConstruction) /// } /// ... one usage of CachedAnonMethodDelegate ... /// => /// ... one usage of DelegateConstruction ... /// </summary> bool CachedDelegateInitializationWithField(IfInstruction inst) { Block trueInst = inst.TrueInst as Block; if (trueInst == null || trueInst.Instructions.Count != 1 || !inst.FalseInst.MatchNop()) { return(false); } var storeInst = trueInst.Instructions[0]; if (!inst.Condition.MatchCompEquals(out ILInstruction left, out ILInstruction right) || !left.MatchLdsFld(out IField field) || !right.MatchLdNull()) { return(false); } if (!storeInst.MatchStsFld(out IField field2, out ILInstruction value) || !field.Equals(field2) || !field.IsCompilerGeneratedOrIsInCompilerGeneratedClass()) { return(false); } if (!DelegateConstruction.IsDelegateConstruction(value as NewObj, true)) { return(false); } var nextInstruction = inst.Parent.Children.ElementAtOrDefault(inst.ChildIndex + 1); if (nextInstruction == null) { return(false); } var usages = nextInstruction.Descendants.Where(i => i.MatchLdsFld(field)).ToArray(); if (usages.Length != 1) { return(false); } context.Step("CachedDelegateInitializationWithField", inst); usages[0].ReplaceWith(value); return(true); }
ILInstruction Lift(IfInstruction ifInst, ILInstruction trueInst, ILInstruction falseInst) { ILInstruction condition = ifInst.Condition; while (condition.MatchLogicNot(out var arg)) { condition = arg; Swap(ref trueInst, ref falseInst); } if (AnalyzeCondition(condition)) { // (v1 != null && ... && vn != null) ? trueInst : falseInst // => normal lifting return(LiftNormal(trueInst, falseInst, ilrange: ifInst.ILRange)); } if (condition is Comp comp && !comp.IsLifted) { // This might be a C#-style lifted comparison // (C# checks the underlying value before checking the HasValue bits) if (comp.Kind.IsEqualityOrInequality()) { // for equality/inequality, the HasValue bits must also compare equal/inequal if (comp.Kind == ComparisonKind.Inequality) { // handle inequality by swapping one last time Swap(ref trueInst, ref falseInst); } if (falseInst.MatchLdcI4(0)) { // (a.GetValueOrDefault() == b.GetValueOrDefault()) ? (a.HasValue == b.HasValue) : false // => a == b return(LiftCSharpEqualityComparison(comp, ComparisonKind.Equality, trueInst)); } else if (falseInst.MatchLdcI4(1)) { // (a.GetValueOrDefault() == b.GetValueOrDefault()) ? (a.HasValue != b.HasValue) : true // => a != b return(LiftCSharpEqualityComparison(comp, ComparisonKind.Inequality, trueInst)); } else if (IsGenericNewPattern(condition, trueInst, falseInst)) { // (default(T) == null) ? Activator.CreateInstance<T>() : default(T) // => Activator.CreateInstance<T>() return(trueInst); } } else { // Not (in)equality, but one of < <= > >=. // Returns false unless all HasValue bits are true. if (falseInst.MatchLdcI4(0) && AnalyzeCondition(trueInst)) { // comp(lhs, rhs) ? (v1 != null && ... && vn != null) : false // => comp.lifted[C#](lhs, rhs) return(LiftCSharpComparison(comp, comp.Kind)); } else if (trueInst.MatchLdcI4(0) && AnalyzeCondition(falseInst)) { // comp(lhs, rhs) ? false : (v1 != null && ... && vn != null) return(LiftCSharpComparison(comp, comp.Kind.Negate())); } } } ILVariable v; if (MatchGetValueOrDefault(condition, out v) && NullableType.GetUnderlyingType(v.Type).IsKnownType(KnownTypeCode.Boolean)) { if (MatchHasValueCall(trueInst, v) && falseInst.MatchLdcI4(0)) { // v.GetValueOrDefault() ? v.HasValue : false // ==> v == true context.Step("NullableLiftingTransform: v == true", ifInst); return(new Comp(ComparisonKind.Equality, ComparisonLiftingKind.CSharp, StackType.I4, Sign.None, new LdLoc(v) { ILRange = trueInst.ILRange }, new LdcI4(1) { ILRange = falseInst.ILRange } ) { ILRange = ifInst.ILRange }); } else if (trueInst.MatchLdcI4(0) && MatchHasValueCall(falseInst, v)) { // v.GetValueOrDefault() ? false : v.HasValue // ==> v == false context.Step("NullableLiftingTransform: v == false", ifInst); return(new Comp(ComparisonKind.Equality, ComparisonLiftingKind.CSharp, StackType.I4, Sign.None, new LdLoc(v) { ILRange = falseInst.ILRange }, trueInst // LdcI4(0) ) { ILRange = ifInst.ILRange }); } else if (MatchNegatedHasValueCall(trueInst, v) && falseInst.MatchLdcI4(1)) { // v.GetValueOrDefault() ? !v.HasValue : true // ==> v != true context.Step("NullableLiftingTransform: v != true", ifInst); return(new Comp(ComparisonKind.Inequality, ComparisonLiftingKind.CSharp, StackType.I4, Sign.None, new LdLoc(v) { ILRange = trueInst.ILRange }, falseInst // LdcI4(1) ) { ILRange = ifInst.ILRange }); } else if (trueInst.MatchLdcI4(1) && MatchNegatedHasValueCall(falseInst, v)) { // v.GetValueOrDefault() ? true : !v.HasValue // ==> v != false context.Step("NullableLiftingTransform: v != false", ifInst); return(new Comp(ComparisonKind.Inequality, ComparisonLiftingKind.CSharp, StackType.I4, Sign.None, new LdLoc(v) { ILRange = falseInst.ILRange }, new LdcI4(0) { ILRange = trueInst.ILRange } ) { ILRange = ifInst.ILRange }); } } if (trueInst.MatchLdLoc(out v)) { if (MatchNullableCtor(falseInst, out var utype, out var arg) && utype.IsKnownType(KnownTypeCode.Boolean) && arg.MatchLdcI4(0)) { // condition ? v : (bool?)false // => condition & v context.Step("NullableLiftingTransform: 3vl.logic.and(bool, bool?)", ifInst); return(new ThreeValuedLogicAnd(condition, trueInst) { ILRange = ifInst.ILRange }); } if (falseInst.MatchLdLoc(out var v2)) { // condition ? v : v2 if (MatchThreeValuedLogicConditionPattern(condition, out var nullable1, out var nullable2)) { // (nullable1.GetValueOrDefault() || (!nullable2.GetValueOrDefault() && !nullable1.HasValue)) ? v : v2 if (v == nullable1 && v2 == nullable2) { context.Step("NullableLiftingTransform: 3vl.logic.or(bool?, bool?)", ifInst); return(new ThreeValuedLogicOr(trueInst, falseInst) { ILRange = ifInst.ILRange }); } else if (v == nullable2 && v2 == nullable1) { context.Step("NullableLiftingTransform: 3vl.logic.and(bool?, bool?)", ifInst); return(new ThreeValuedLogicAnd(falseInst, trueInst) { ILRange = ifInst.ILRange }); } } } } else if (falseInst.MatchLdLoc(out v)) { if (MatchNullableCtor(trueInst, out var utype, out var arg) && utype.IsKnownType(KnownTypeCode.Boolean) && arg.MatchLdcI4(1)) { // condition ? (bool?)true : v // => condition | v context.Step("NullableLiftingTransform: 3vl.logic.or(bool, bool?)", ifInst); return(new ThreeValuedLogicOr(condition, falseInst) { ILRange = ifInst.ILRange }); } } return(null); }
protected internal override void VisitComp(Comp inst) { // "logic.not(arg)" is sugar for "comp(arg != ldc.i4 0)" if (inst.MatchLogicNot(out var arg)) { VisitLogicNot(inst, arg); return; } else if (inst.Kind == ComparisonKind.Inequality && inst.LiftingKind == ComparisonLiftingKind.None && inst.Right.MatchLdcI4(0) && (IfInstruction.IsInConditionSlot(inst) || inst.Left is Comp) ) { // if (comp(x != 0)) ==> if (x) // comp(comp(...) != 0) => comp(...) context.Step("Remove redundant comp(... != 0)", inst); inst.Left.AddILRange(inst); inst.ReplaceWith(inst.Left); inst.Left.AcceptVisitor(this); return; } base.VisitComp(inst); if (inst.IsLifted) { return; } if (inst.Right.MatchLdNull()) { if (inst.Kind == ComparisonKind.GreaterThan) { context.Step("comp(left > ldnull) => comp(left != ldnull)", inst); inst.Kind = ComparisonKind.Inequality; } else if (inst.Kind == ComparisonKind.LessThanOrEqual) { context.Step("comp(left <= ldnull) => comp(left == ldnull)", inst); inst.Kind = ComparisonKind.Equality; } } else if (inst.Left.MatchLdNull()) { if (inst.Kind == ComparisonKind.LessThan) { context.Step("comp(ldnull < right) => comp(ldnull != right)", inst); inst.Kind = ComparisonKind.Inequality; } else if (inst.Kind == ComparisonKind.GreaterThanOrEqual) { context.Step("comp(ldnull >= right) => comp(ldnull == right)", inst); inst.Kind = ComparisonKind.Equality; } } var rightWithoutConv = inst.Right.UnwrapConv(ConversionKind.SignExtend).UnwrapConv(ConversionKind.ZeroExtend); if (rightWithoutConv.MatchLdcI4(0) && inst.Sign == Sign.Unsigned && (inst.Kind == ComparisonKind.GreaterThan || inst.Kind == ComparisonKind.LessThanOrEqual)) { if (inst.Kind == ComparisonKind.GreaterThan) { context.Step("comp.unsigned(left > ldc.i4 0) => comp(left != ldc.i4 0)", inst); inst.Kind = ComparisonKind.Inequality; VisitComp(inst); return; } else if (inst.Kind == ComparisonKind.LessThanOrEqual) { context.Step("comp.unsigned(left <= ldc.i4 0) => comp(left == ldc.i4 0)", inst); inst.Kind = ComparisonKind.Equality; VisitComp(inst); return; } } else if (rightWithoutConv.MatchLdcI4(0) && inst.Kind.IsEqualityOrInequality()) { if (inst.Left.MatchLdLen(StackType.I, out ILInstruction array)) { // comp.unsigned(ldlen array == conv i4->i(ldc.i4 0)) // => comp(ldlen.i4 array == ldc.i4 0) // This is a special case where the C# compiler doesn't generate conv.i4 after ldlen. context.Step("comp(ldlen.i4 array == ldc.i4 0)", inst); inst.InputType = StackType.I4; inst.Left.ReplaceWith(new LdLen(StackType.I4, array).WithILRange(inst.Left)); inst.Right = rightWithoutConv; } else if (inst.Left is Conv conv && conv.TargetType == PrimitiveType.I && conv.Argument.ResultType == StackType.O) { // C++/CLI sometimes uses this weird comparison with null: context.Step("comp(conv o->i (ldloc obj) == conv i4->i <sign extend>(ldc.i4 0))", inst); // -> comp(ldloc obj == ldnull) inst.InputType = StackType.O; inst.Left = conv.Argument; inst.Right = new LdNull().WithILRange(inst.Right); inst.Right.AddILRange(rightWithoutConv); } } if (inst.Right.MatchLdNull() && inst.Left.MatchBox(out arg, out var type) && type.Kind == TypeKind.TypeParameter) { if (inst.Kind == ComparisonKind.Equality) { context.Step("comp(box T(..) == ldnull) -> comp(.. == ldnull)", inst); inst.Left = arg; } if (inst.Kind == ComparisonKind.Inequality) { context.Step("comp(box T(..) != ldnull) -> comp(.. != ldnull)", inst); inst.Left = arg; } } }
/// <summary> /// op is either add or remove/subtract: /// if (dynamic.isevent (target)) { /// dynamic.invokemember.invokespecial.discard op_Name(target, value) /// } else { /// dynamic.compound.op (dynamic.getmember Name(target), value) /// } /// => /// dynamic.compound.op (dynamic.getmember Name(target), value) /// </summary> bool TransformDynamicAddAssignOrRemoveAssign(IfInstruction inst) { if (!inst.MatchIfInstructionPositiveCondition(out var condition, out var trueInst, out var falseInst)) { return(false); } if (!(condition is DynamicIsEventInstruction isEvent)) { return(false); } trueInst = Block.Unwrap(trueInst); falseInst = Block.Unwrap(falseInst); if (!(falseInst is DynamicCompoundAssign dynamicCompoundAssign)) { return(false); } if (!(dynamicCompoundAssign.Target is DynamicGetMemberInstruction getMember)) { return(false); } if (!SemanticHelper.IsPure(isEvent.Argument.Flags)) { return(false); } if (!isEvent.Argument.Match(getMember.Target).Success) { return(false); } if (!(trueInst is DynamicInvokeMemberInstruction invokeMember)) { return(false); } if (!(invokeMember.BinderFlags.HasFlag(CSharpBinderFlags.InvokeSpecialName) && invokeMember.BinderFlags.HasFlag(CSharpBinderFlags.ResultDiscarded))) { return(false); } switch (dynamicCompoundAssign.Operation) { case ExpressionType.AddAssign: if (invokeMember.Name != "add_" + getMember.Name) { return(false); } break; case ExpressionType.SubtractAssign: if (invokeMember.Name != "remove_" + getMember.Name) { return(false); } break; default: return(false); } if (!dynamicCompoundAssign.Value.Match(invokeMember.Arguments[1]).Success) { return(false); } if (!invokeMember.Arguments[0].Match(getMember.Target).Success) { return(false); } context.Step("+= / -= dynamic.isevent pattern -> dynamic.compound.op", inst); inst.ReplaceWith(dynamicCompoundAssign); return(true); }
protected override void SetMyBlockInternalArg() { myBlockInternalArg = new IfInstruction(this); }
protected internal override void VisitLogicNot(LogicNot inst) { ILInstruction arg, lhs, rhs; if (inst.Argument.MatchLogicNot(out arg)) { context.Step("logic.not(logic.not(arg)) => arg", inst); Debug.Assert(arg.ResultType == StackType.I4); arg.AddILRange(inst.ILRange); arg.AddILRange(inst.Argument.ILRange); inst.ReplaceWith(arg); arg.AcceptVisitor(this); } else if (inst.Argument is Comp comp) { if ((comp.InputType != StackType.F && !comp.IsLifted) || comp.Kind.IsEqualityOrInequality()) { context.Step("push negation into comparison", inst); comp.Kind = comp.Kind.Negate(); comp.AddILRange(inst.ILRange); inst.ReplaceWith(comp); } comp.AcceptVisitor(this); } else if (inst.Argument.MatchLogicAnd(out lhs, out rhs)) { // logic.not(if (lhs) rhs else ldc.i4 0) // ==> if (logic.not(lhs)) ldc.i4 1 else logic.not(rhs) context.Step("push negation into logic.and", inst); IfInstruction ifInst = (IfInstruction)inst.Argument; var ldc0 = ifInst.FalseInst; Debug.Assert(ldc0.MatchLdcI4(0)); ifInst.Condition = new LogicNot(lhs) { ILRange = inst.ILRange }; ifInst.TrueInst = new LdcI4(1) { ILRange = ldc0.ILRange }; ifInst.FalseInst = new LogicNot(rhs) { ILRange = inst.ILRange }; inst.ReplaceWith(ifInst); ifInst.AcceptVisitor(this); } else if (inst.Argument.MatchLogicOr(out lhs, out rhs)) { // logic.not(if (lhs) ldc.i4 1 else rhs) // ==> if (logic.not(lhs)) logic.not(rhs) else ldc.i4 0) context.Step("push negation into logic.or", inst); IfInstruction ifInst = (IfInstruction)inst.Argument; var ldc1 = ifInst.TrueInst; Debug.Assert(ldc1.MatchLdcI4(1)); ifInst.Condition = new LogicNot(lhs) { ILRange = inst.ILRange }; ifInst.TrueInst = new LogicNot(rhs) { ILRange = inst.ILRange }; ifInst.FalseInst = new LdcI4(0) { ILRange = ldc1.ILRange }; inst.ReplaceWith(ifInst); ifInst.AcceptVisitor(this); } else { inst.Argument.AcceptVisitor(this); } }
bool MatchWhileLoop(BlockContainer loop, out IfInstruction condition, out Block loopBody) { // ConditionDetection favours leave inside if and branch at end of block // while-loop: // if (!loop-condition) leave loop-container // ... condition = null; loopBody = null; if (!(loop.EntryPoint.Instructions[0] is IfInstruction ifInstruction)) { return(false); } if (!ifInstruction.FalseInst.MatchNop()) { return(false); } if (UsesVariableCapturedInLoop(loop, ifInstruction.Condition)) { return(false); } condition = ifInstruction; if (!ifInstruction.TrueInst.MatchLeave(loop)) { return(false); } context.Step("Transform to while (condition) loop", loop); loop.Kind = ContainerKind.While; //invert comparison ifInstruction.Condition = Comp.LogicNot(ifInstruction.Condition); ifInstruction.FalseInst = ifInstruction.TrueInst; //move the rest of the body into a new block loopBody = ConditionDetection.ExtractBlock(loop.EntryPoint, 1, loop.EntryPoint.Instructions.Count); loop.Blocks.Insert(1, loopBody); if (!loopBody.HasFlag(InstructionFlags.EndPointUnreachable)) { loopBody.Instructions.Add(new Leave(loop)); } ifInstruction.TrueInst = new Branch(loopBody); ExpressionTransforms.RunOnSingleStatement(ifInstruction, context); // Analyze conditions and decide whether to move some of them out of the condition block: /*var conditions = new List<ILInstruction>(); * SplitConditions(condition.Condition, conditions); * // Break apart conditions that could be a MoveNext call followed by a Current accessor call: * if (MightBeHeaderOfForEach(loop, conditions)) { * ifInstruction.Condition = conditions[0]; * foreach (var cond in conditions.Skip(1).Reverse()) { * IfInstruction inst; * loopBody.Instructions.Insert(0, inst = new IfInstruction(Comp.LogicNot(cond), new Leave(loop))); * ExpressionTransforms.RunOnSingleStatment(inst, context); * } * }*/ // Invert condition and unwrap nested block, if loop ends in a break or return statement preceeded by an IfInstruction. /*while (loopBody.Instructions.Last() is Leave leave && loopBody.Instructions.SecondToLastOrDefault() is IfInstruction nestedIf && nestedIf.FalseInst.MatchNop()) { * switch (nestedIf.TrueInst) { * case Block nestedBlock: * loopBody.Instructions.RemoveAt(leave.ChildIndex); * loopBody.Instructions.AddRange(nestedBlock.Instructions); * break; * case Branch br: * leave.ReplaceWith(nestedIf.TrueInst); * break; * default: * return true; * } * nestedIf.Condition = Comp.LogicNot(nestedIf.Condition); * nestedIf.TrueInst = leave; * ExpressionTransforms.RunOnSingleStatment(nestedIf, context); * if (!loopBody.HasFlag(InstructionFlags.EndPointUnreachable)) * loopBody.Instructions.Add(new Leave(loop)); * }*/ return(true); }
bool MatchForLoop(BlockContainer loop, IfInstruction whileCondition, Block whileLoopBody) { // for loops have exactly two incoming edges at the entry point. if (loop.EntryPoint.IncomingEdgeCount != 2) { return(false); } // try to find an increment block: // consists of simple statements only. var incrementBlock = GetIncrementBlock(loop, whileLoopBody); if (incrementBlock != null) { // we found a possible increment block, just make sure, that there are at least three blocks: // - condition block // - loop body // - increment block if (incrementBlock.Instructions.Count <= 1 || loop.Blocks.Count < 3) { return(false); } context.Step("Transform to for loop", loop); // move the block to the end of the loop: loop.Blocks.MoveElementToEnd(incrementBlock); loop.Kind = ContainerKind.For; } else { // we need to move the increment statements into its own block: // last must be a branch entry-point var last = whileLoopBody.Instructions.LastOrDefault(); var secondToLast = whileLoopBody.Instructions.SecondToLastOrDefault(); if (last == null || secondToLast == null) { return(false); } if (!last.MatchBranch(loop.EntryPoint)) { return(false); } // we only deal with 'numeric' increments if (!MatchIncrement(secondToLast, out var incrementVariable)) { return(false); } // the increment variable must be local/stack variable if (incrementVariable.Kind == VariableKind.Parameter) { return(false); } // split conditions: var conditions = new List <ILInstruction>(); SplitConditions(whileCondition.Condition, conditions); IfInstruction forCondition = null; int numberOfConditions = 0; foreach (var condition in conditions) { // the increment variable must be used in the condition if (!condition.Descendants.Any(inst => inst.MatchLdLoc(incrementVariable))) { break; } // condition should not contain an assignment if (condition.Descendants.Any(IsAssignment)) { break; } if (forCondition == null) { forCondition = new IfInstruction(condition, whileCondition.TrueInst, whileCondition.FalseInst); } else { forCondition.Condition = IfInstruction.LogicAnd(forCondition.Condition, condition); } numberOfConditions++; } if (numberOfConditions == 0) { return(false); } context.Step("Transform to for loop", loop); // split condition block: whileCondition.ReplaceWith(forCondition); ExpressionTransforms.RunOnSingleStatement(forCondition, context); for (int i = conditions.Count - 1; i >= numberOfConditions; i--) { IfInstruction inst; whileLoopBody.Instructions.Insert(0, inst = new IfInstruction(Comp.LogicNot(conditions[i]), new Leave(loop))); ExpressionTransforms.RunOnSingleStatement(inst, context); } // create a new increment block and add it at the end: int secondToLastIndex = secondToLast.ChildIndex; var newIncremenBlock = new Block(); loop.Blocks.Add(newIncremenBlock); // move the increment instruction: newIncremenBlock.Instructions.Add(secondToLast); newIncremenBlock.Instructions.Add(last); newIncremenBlock.AddILRange(secondToLast.ILRange); whileLoopBody.Instructions.RemoveRange(secondToLastIndex, 2); whileLoopBody.Instructions.Add(new Branch(newIncremenBlock)); // complete transform. loop.Kind = ContainerKind.For; } return(true); }
/// <summary> /// Matches a do-while loop and performs the following transformations: /// - combine all compatible conditions into one IfInstruction. /// - extract conditions into a condition block, or move the existing condition block to the end. /// </summary> bool MatchDoWhileLoop(BlockContainer loop) { (List <IfInstruction> conditions, ILInstruction exit, bool swap, bool split, bool unwrap) = AnalyzeDoWhileConditions(loop); // not a do-while loop, exit. if (conditions == null || conditions.Count == 0) { return(false); } context.Step("Transform to do-while loop", loop); Block conditionBlock; // first we remove all extracted instructions from the original block. var originalBlock = (Block)exit.Parent; if (unwrap) { // we found a condition block nested in a condition that is followed by a return statement: // we flip the condition and swap the blocks Debug.Assert(originalBlock.Parent is IfInstruction); var returnCondition = (IfInstruction)originalBlock.Parent; var topLevelBlock = (Block)returnCondition.Parent; Debug.Assert(topLevelBlock.Parent == loop); var leaveFunction = topLevelBlock.Instructions[returnCondition.ChildIndex + 1]; Debug.Assert(leaveFunction.MatchReturn(out _)); returnCondition.Condition = Comp.LogicNot(returnCondition.Condition); returnCondition.TrueInst = leaveFunction; // simplify the condition: ExpressionTransforms.RunOnSingleStatement(returnCondition, context); topLevelBlock.Instructions.RemoveAt(returnCondition.ChildIndex + 1); topLevelBlock.Instructions.AddRange(originalBlock.Instructions); originalBlock = topLevelBlock; split = true; } originalBlock.Instructions.RemoveRange(originalBlock.Instructions.Count - conditions.Count - 1, conditions.Count + 1); // we need to split the block: if (split) { // add a new block at the end and add a branch to the new block. conditionBlock = new Block(); loop.Blocks.Add(conditionBlock); originalBlock.Instructions.Add(new Branch(conditionBlock)); } else { // move the condition block to the end. conditionBlock = originalBlock; loop.Blocks.MoveElementToEnd(originalBlock); } // combine all conditions and the exit instruction into one IfInstruction: IfInstruction condition = null; conditionBlock.AddILRange(exit.ILRange); foreach (var inst in conditions) { conditionBlock.AddILRange(inst.ILRange); if (condition == null) { condition = inst; if (swap) { // branches must be swapped and condition negated: condition.Condition = Comp.LogicNot(condition.Condition); condition.FalseInst = condition.TrueInst; condition.TrueInst = exit; } else { condition.FalseInst = exit; } } else { if (swap) { condition.Condition = IfInstruction.LogicAnd(Comp.LogicNot(inst.Condition), condition.Condition); } else { condition.Condition = IfInstruction.LogicAnd(inst.Condition, condition.Condition); } } } // insert the combined conditions into the condition block: conditionBlock.Instructions.Add(condition); // simplify the condition: ExpressionTransforms.RunOnSingleStatement(condition, context); // transform complete loop.Kind = ContainerKind.DoWhile; return(true); }
private void HandleIfInstruction(ControlFlowNode cfgNode, Block block, IfInstruction ifInst, ref ILInstruction exitInst) { if (ShouldSwapIfTargets(ifInst.TrueInst, exitInst)) { // "if (c) goto lateBlock; goto earlierBlock;" // -> "if (!c)" goto earlierBlock; goto lateBlock; // This reordering should make the if structure correspond more closely to the original C# source code context.Step("Negate if", ifInst); block.Instructions[block.Instructions.Count - 1] = ifInst.TrueInst; ifInst.TrueInst = exitInst; exitInst = block.Instructions.Last(); ifInst.Condition = Comp.LogicNot(ifInst.Condition); } ILInstruction trueExitInst; if (IsUsableBranchToChild(cfgNode, ifInst.TrueInst)) { // "if (...) goto targetBlock; exitInst;" // -> "if (...) { targetBlock } exitInst;" context.Step("Inline block as then-branch", ifInst); var targetBlock = ((Branch)ifInst.TrueInst).TargetBlock; // The targetBlock was already processed, we can embed it into the if statement: targetBlock.Remove(); ifInst.TrueInst = targetBlock; ILInstruction nestedCondition, nestedTrueInst; while (targetBlock.Instructions.Count > 0 && targetBlock.Instructions[0].MatchIfInstruction(out nestedCondition, out nestedTrueInst)) { nestedTrueInst = UnpackBlockContainingOnlyBranch(nestedTrueInst); if (DetectExitPoints.CompatibleExitInstruction(exitInst, nestedTrueInst)) { // "if (...) { if (nestedCondition) goto exitPoint; ... } goto exitPoint;" // -> "if (... && !nestedCondition) { ... } goto exitPoint;" context.Step("Combine 'if (cond1 && !cond2)' in then-branch", ifInst); ifInst.Condition = IfInstruction.LogicAnd(ifInst.Condition, Comp.LogicNot(nestedCondition)); targetBlock.Instructions.RemoveAt(0); // Update targetBlock label now that we've removed the first instruction if (targetBlock.Instructions.FirstOrDefault()?.ILRange.IsEmpty == false) { int offset = targetBlock.Instructions[0].ILRange.Start; targetBlock.ILRange = new Interval(offset, offset); } continue; // try to find more nested conditions } if (nestedTrueInst is Block nestedTrueBlock && DetectExitPoints.CompatibleExitInstruction(exitInst, nestedTrueBlock.Instructions.Last()) && targetBlock.HasFlag(InstructionFlags.EndPointUnreachable)) { // "if (...) { if (nestedCondition) { trueInst...; goto exitPoint; } falseInst...; } goto exitPoint;" // -> "if (...) { if (!nestedCondition) { falseInst...; } trueInst... } goto exitPoint;" // (only if end-point of 'falseInst...' is unreachable) context.Step("Invert nested condition to reduce number of gotos", ifInst); var nestedIfInst = (IfInstruction)targetBlock.Instructions[0]; nestedIfInst.Condition = Comp.LogicNot(nestedCondition); nestedTrueBlock.Instructions.RemoveAt(nestedTrueBlock.Instructions.Count - 1); // remove nested goto exitPoint; // remove falseInsts from outer block var falseInsts = targetBlock.Instructions.Skip(1).ToArray(); targetBlock.Instructions.RemoveRange(1, targetBlock.Instructions.Count - 1); // add trueInsts to outer block targetBlock.Instructions.AddRange(nestedTrueBlock.Instructions); // add falseInsts to inner block nestedTrueBlock.Instructions.ReplaceList(falseInsts); nestedIfInst.Condition.AcceptVisitor(new ExpressionTransforms { context = new StatementTransformContext(context) }); } break; } trueExitInst = targetBlock.Instructions.LastOrDefault(); if (DetectExitPoints.CompatibleExitInstruction(exitInst, trueExitInst)) { // "if (...) { ...; goto exitPoint } goto exitPoint;" // -> "if (...) { ... } goto exitPoint;" context.Step("Remove redundant 'goto exitPoint;' in then-branch", ifInst); targetBlock.Instructions.RemoveAt(targetBlock.Instructions.Count - 1); trueExitInst = null; if (targetBlock.Instructions.Count == 1 && targetBlock.Instructions[0].MatchIfInstruction(out nestedCondition, out nestedTrueInst)) { // "if (...) { if (nestedCondition) nestedTrueInst; } exitInst;" // --> "if (... && nestedCondition) nestedTrueInst; } exitInst" context.Step("Combine if conditions into logic.and (in then-branch)", ifInst); ifInst.Condition = IfInstruction.LogicAnd(ifInst.Condition, nestedCondition); ifInst.TrueInst = nestedTrueInst; trueExitInst = (nestedTrueInst as Block)?.Instructions.LastOrDefault(); } } } else { trueExitInst = ifInst.TrueInst; } if (IsUsableBranchToChild(cfgNode, exitInst)) { var targetBlock = ((Branch)exitInst).TargetBlock; var falseExitInst = targetBlock.Instructions.LastOrDefault(); if (DetectExitPoints.CompatibleExitInstruction(trueExitInst, falseExitInst)) { // if (...) { ...; goto exitPoint; } goto nextBlock; nextBlock: ...; goto exitPoint; // -> if (...) { ... } else { ... } goto exitPoint; context.Step("Inline block as else-branch", ifInst); targetBlock.Instructions.RemoveAt(targetBlock.Instructions.Count - 1); targetBlock.Remove(); ifInst.FalseInst = targetBlock; exitInst = block.Instructions[block.Instructions.Count - 1] = falseExitInst; Block trueBlock = ifInst.TrueInst as Block; if (trueBlock != null) { Debug.Assert(trueExitInst == trueBlock.Instructions.Last()); trueBlock.Instructions.RemoveAt(trueBlock.Instructions.Count - 1); } else { Debug.Assert(trueExitInst == ifInst.TrueInst); ifInst.TrueInst = new Nop { ILRange = ifInst.TrueInst.ILRange }; } } } if (IsEmpty(ifInst.TrueInst)) { // prefer empty true-branch to empty-else branch context.Step("Swap empty then-branch with else-branch", ifInst); var oldTrue = ifInst.TrueInst; ifInst.TrueInst = ifInst.FalseInst; ifInst.FalseInst = new Nop { ILRange = oldTrue.ILRange }; ifInst.Condition = Comp.LogicNot(ifInst.Condition); // After swapping, it's possible that we can introduce a short-circuit operator: Block trueBlock = ifInst.TrueInst as Block; ILInstruction nestedCondition, nestedTrueInst; if (trueBlock != null && trueBlock.Instructions.Count == 1 && trueBlock.FinalInstruction is Nop && trueBlock.Instructions[0].MatchIfInstruction(out nestedCondition, out nestedTrueInst)) { // if (cond) if (nestedCond) nestedTrueInst // ==> if (cond && nestedCond) nestedTrueInst context.Step("Combine if conditions into logic.and (after branch swapping)", ifInst); ifInst.Condition = IfInstruction.LogicAnd(ifInst.Condition, nestedCondition); ifInst.TrueInst = nestedTrueInst; } } else if (ifInst.FalseInst.OpCode != OpCode.Nop && ifInst.FalseInst.ILRange.Start < ifInst.TrueInst.ILRange.Start) { // swap true and false branches of if/else construct, // to bring them in the same order as the IL code context.Step("Swap then-branch with else-branch", ifInst); var oldTrue = ifInst.TrueInst; ifInst.TrueInst = ifInst.FalseInst; ifInst.FalseInst = oldTrue; ifInst.Condition = Comp.LogicNot(ifInst.Condition); } }
/// <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); }
bool MatchWhileLoop(BlockContainer loop, out IfInstruction condition, out Block loopBody) { // ConditionDetection favours leave inside if and branch at end of block // while-loop: // if (!loop-condition) leave loop-container // ... condition = null; loopBody = loop.EntryPoint; if (!(loopBody.Instructions[0] is IfInstruction ifInstruction)) { return(false); } if (!ifInstruction.FalseInst.MatchNop()) { return(false); } if (UsesVariableCapturedInLoop(loop, ifInstruction.Condition)) { return(false); } condition = ifInstruction; if (!ifInstruction.TrueInst.MatchLeave(loop)) { // sometimes the loop-body is nested within the if // if (loop-condition) { loop-body } // leave loop-container if (loopBody.Instructions.Count != 2 || !loop.EntryPoint.Instructions.Last().MatchLeave(loop)) { return(false); } if (!ifInstruction.TrueInst.HasFlag(InstructionFlags.EndPointUnreachable)) { ((Block)ifInstruction.TrueInst).Instructions.Add(new Leave(loop)); } ConditionDetection.InvertIf(loopBody, ifInstruction, context); } context.Step("Transform to while (condition) loop", loop); loop.Kind = ContainerKind.While; //invert comparison ifInstruction.Condition = Comp.LogicNot(ifInstruction.Condition); ifInstruction.FalseInst = ifInstruction.TrueInst; //move the rest of the body into a new block loopBody = ConditionDetection.ExtractBlock(loop.EntryPoint, 1, loop.EntryPoint.Instructions.Count); loop.Blocks.Insert(1, loopBody); if (!loopBody.HasFlag(InstructionFlags.EndPointUnreachable)) { loopBody.Instructions.Add(new Leave(loop)); } ifInstruction.TrueInst = new Branch(loopBody); ExpressionTransforms.RunOnSingleStatement(ifInstruction, context); // Analyze conditions and decide whether to move some of them out of the condition block: /*var conditions = new List<ILInstruction>(); * SplitConditions(condition.Condition, conditions); * // Break apart conditions that could be a MoveNext call followed by a Current accessor call: * if (MightBeHeaderOfForEach(loop, conditions)) { * ifInstruction.Condition = conditions[0]; * foreach (var cond in conditions.Skip(1).Reverse()) { * IfInstruction inst; * loopBody.Instructions.Insert(0, inst = new IfInstruction(Comp.LogicNot(cond), new Leave(loop))); * ExpressionTransforms.RunOnSingleStatment(inst, context); * } * }*/ return(true); }