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 (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.LoadCount != 2 || v.AddressCount != 0) { return(false); } // do not transform if there is no usage directly aftewards 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); inst.ReplaceWith(new StLoc(v, value)); return(true); }
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> /// 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); }
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); }