bool DoTransformStackAllocInitializer(Block body, int pos) { if (pos >= body.Instructions.Count - 2) { return(false); } ILInstruction inst = body.Instructions[pos]; if (inst.MatchStLoc(out var v, out var locallocExpr) && locallocExpr.MatchLocAlloc(out var lengthInst)) { if (lengthInst.MatchLdcI(out var lengthInBytes) && HandleCpblkInitializer(body, pos + 1, v, lengthInBytes, out var blob, out var elementType)) { context.Step("HandleCpblkInitializer", inst); var block = new Block(BlockKind.StackAllocInitializer); var tempStore = context.Function.RegisterVariable(VariableKind.InitializerTarget, new PointerType(elementType)); block.Instructions.Add(new StLoc(tempStore, locallocExpr)); while (blob.RemainingBytes > 0) { block.Instructions.Add(StElemPtr(tempStore, blob.Offset, new LdcI4(blob.ReadByte()), elementType)); } block.FinalInstruction = new LdLoc(tempStore); body.Instructions[pos] = new StLoc(v, block); body.Instructions.RemoveAt(pos + 1); ILInlining.InlineIfPossible(body, pos, context); ExpressionTransforms.RunOnSingleStatement(body.Instructions[pos], context); return(true); } if (HandleSequentialLocAllocInitializer(body, pos + 1, v, locallocExpr, out elementType, out StObj[] values, out int instructionsToRemove)) { context.Step("HandleSequentialLocAllocInitializer", inst); var block = new Block(BlockKind.StackAllocInitializer); var tempStore = context.Function.RegisterVariable(VariableKind.InitializerTarget, new PointerType(elementType)); block.Instructions.Add(new StLoc(tempStore, locallocExpr)); block.Instructions.AddRange(values.Where(value => value != null).Select(value => RewrapStore(tempStore, value, elementType))); block.FinalInstruction = new LdLoc(tempStore); body.Instructions[pos] = new StLoc(v, block); body.Instructions.RemoveRange(pos + 1, instructionsToRemove); ILInlining.InlineIfPossible(body, pos, context); ExpressionTransforms.RunOnSingleStatement(body.Instructions[pos], context); return(true); } }
/// stloc V_1(dynamic.isevent (target)) /// if (logic.not(ldloc V_1)) Block IL_004a { /// stloc V_2(dynamic.getmember B(target)) /// } /// [stloc copyOfValue(value)] /// if (logic.not(ldloc V_1)) Block IL_0149 { /// dynamic.setmember.compound B(target, dynamic.binary.operator AddAssign(ldloc V_2, value)) /// } else Block IL_0151 { /// dynamic.invokemember.invokespecial.discard add_B(target, value) /// } /// => /// if (logic.not(dynamic.isevent (target))) Block IL_0149 { /// dynamic.setmember.compound B(target, dynamic.binary.operator AddAssign(dynamic.getmember B(target), value)) /// } else Block IL_0151 { /// dynamic.invokemember.invokespecial.discard add_B(target, value) /// } public void Run(Block block, int pos, StatementTransformContext context) { if (!(pos + 3 < block.Instructions.Count && block.Instructions[pos].MatchStLoc(out var flagVar, out var inst) && inst is DynamicIsEventInstruction isEvent)) { return; } if (!(flagVar.IsSingleDefinition && flagVar.LoadCount == 2)) { return; } if (!MatchLhsCacheIfInstruction(block.Instructions[pos + 1], flagVar, out var dynamicGetMemberStore)) { return; } if (!(dynamicGetMemberStore.MatchStLoc(out var getMemberVar, out inst) && inst is DynamicGetMemberInstruction getMemberInst)) { return; } int offset = 2; if (block.Instructions[pos + offset].MatchStLoc(out var valueVariable) && pos + 4 < block.Instructions.Count && valueVariable.IsSingleDefinition && valueVariable.LoadCount == 2 && valueVariable.LoadInstructions.All(ld => ld.Parent is DynamicInstruction)) { offset++; } foreach (var descendant in block.Instructions[pos + offset].Descendants) { if (!MatchIsEventAssignmentIfInstruction(descendant, isEvent, flagVar, getMemberVar, out var setMemberInst, out var getMemberVarUse, out var isEventConditionUse)) { continue; } context.Step("DynamicIsEventAssignmentTransform", block.Instructions[pos]); // Collapse duplicate condition getMemberVarUse.ReplaceWith(getMemberInst); isEventConditionUse.ReplaceWith(isEvent); block.Instructions.RemoveRange(pos, 2); // Reuse ExpressionTransforms ExpressionTransforms.TransformDynamicSetMemberInstruction(setMemberInst, context); context.RequestRerun(); break; } }
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); }
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); }
bool MatchWhileLoop(BlockContainer loop, out IfInstruction condition, out Block loopBody) { // while-loop: // if (loop-condition) br loop-content-block // leave loop-container // -or- // if (loop-condition) block content-block // leave loop-container condition = null; loopBody = null; if (loop.EntryPoint.Instructions.Count != 2) { return(false); } 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; var trueInst = ifInstruction.TrueInst; if (!loop.EntryPoint.Instructions[1].MatchLeave(loop)) { return(false); } if (trueInst is Block b) { context.Step("Transform to while (condition) loop", loop); // move the loop body to its own block: loopBody = b; trueInst.ReplaceWith(new Branch(loopBody)); loop.Blocks.Insert(1, loopBody); // sometimes the loop-content-block does not end with a leave/branch if (!loopBody.HasFlag(InstructionFlags.EndPointUnreachable)) { loopBody.Instructions.Add(new Leave(loop)); } } else if (trueInst is Branch br) { context.Step("Transform to while (condition) loop", loop); loopBody = br.TargetBlock; } else { return(false); } // 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); } } // move the branch/leave instruction into the condition block ifInstruction.FalseInst = loop.EntryPoint.Instructions[1]; loop.EntryPoint.Instructions.RemoveAt(1); loop.Kind = ContainerKind.While; // 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); }