protected override ICode VisitIf(StmtIf s) { if (!VisitorFindContinuations.Any(s)) { // 'If' contains no continuations, so no distribution can be done return(s); } if (VisitorOnlyStatements.Only(s, Stmt.NodeType.If, Stmt.NodeType.Continuation) && this.ifInfo == null) { // 'If' only contains continuations, so no distribution can be done // Must visit base method to find contained continuations return(base.VisitIf(s)); } bool finalise = false; if (this.ifInfo == null) { finalise = true; this.ifInfo = new IfInfo(); } this.ifInfo.Conditions.Push(s.Condition); var then = this.Visit(s.Then); this.ifInfo.Conditions.Pop(); this.ifInfo.Conditions.Push(this.ctx.ExprGen.NotAutoSimplify(s.Condition)); var @else = this.Visit(s.Else); this.ifInfo.Conditions.Pop(); if (then != s.Then || @else != s.Else) { var @if = new StmtIf(s.Ctx, s.Condition, (Stmt)then, (Stmt)@else); if (finalise && this.ifInfo.AddToIf.Any()) { var ifStmts = this.ifInfo.AddToIf.GroupBy(x => x.Item1.To, x => x.Item2).Select(x => new StmtIf(s.Ctx, x.Aggregate((a, b) => this.ctx.ExprGen.Or(a, b)), this.ifInfo.AddToIf.First(y => y.Item1.To == x.Key).Item1, null) ); var stmts = new Stmt[] { @if }.Concat(ifStmts).ToArray(); this.ifInfo = null; return(new StmtBlock(s.Ctx, stmts)); } else { if (finalise) { this.ifInfo = null; } return(@if); } } else { // In this case, no continuations will have been found, so there cannot be any conditions to add if (finalise) { this.ifInfo = null; } return(s); } }
protected override ICode VisitDoLoop(StmtDoLoop s) { var body = (Stmt)this.Visit(s.Body); StmtIf lastIf = null; IEnumerable <Stmt> preIf = null; if (body.StmtType == Stmt.NodeType.Block) { var sBlock = (StmtBlock)body; if (sBlock.Statements.Any()) { var sLast = sBlock.Statements.Last(); if (sLast.StmtType == Stmt.NodeType.If) { lastIf = (StmtIf)sLast; preIf = sBlock.Statements.Take(sBlock.Statements.Count() - 1).ToArray(); } } } else if (body.StmtType == Stmt.NodeType.If) { lastIf = (StmtIf)body; preIf = Enumerable.Empty <Stmt>(); } if (lastIf != null) { Stmt afterLoop = null; StmtIf newIf = null; // See if final 'if' condition is same as the 'do' condition. // TODO: This may lead to a non-terminating situation... if (lastIf.Condition.DoesEqual(s.While)) { afterLoop = lastIf.Else; newIf = new StmtIf(s.Ctx, lastIf.Condition, lastIf.Then, null); } else if (lastIf.Condition.DoesEqualNot(s.While)) { afterLoop = lastIf.Then; newIf = new StmtIf(s.Ctx, lastIf.Condition, null, lastIf.Else); } if (afterLoop != null) { var loopBody = new StmtBlock(s.Ctx, preIf.Concat(newIf)); var loop = new StmtDoLoop(s.Ctx, loopBody, s.While); var ret = new StmtBlock(s.Ctx, loop, afterLoop); return(ret); } } if (body != s.Body) { return(new StmtDoLoop(s.Ctx, body, s.While)); } else { return(s); } }
public static Stmt get_Value(Ctx ctx) { var excCtor = ctx.Module.Import(typeof(InvalidOperationException).GetConstructor(Type.EmptyTypes)); var invalidOperationException = new ExprNewObj(ctx, excCtor); var throwInvalidOperationException = new StmtThrow(ctx, invalidOperationException); var cond = new ExprJsExplicit(ctx, "(this === null)", ctx.Boolean, ctx.ThisNamed); var throwIf = new StmtIf(ctx, cond, throwInvalidOperationException, null); var ret = new StmtReturn(ctx, ctx.This); var stmt = new StmtBlock(ctx, throwIf, ret); return(stmt); }
internal override void EmitStmt(ILGenerator ilg) { Cond.Emit(ilg, true); var lb = ilg.DefineLabel(); var le = ilg.DefineLabel(); ilg.Emit(OpCodes.Brtrue, lb); StmtElse?.Emit(ilg); ilg.Emit(OpCodes.Br, le); ilg.MarkLabel(lb); StmtIf?.Emit(ilg); ilg.MarkLabel(le); }
protected override ICode VisitIf(StmtIf s) { var ctx = s.Ctx; var condition = (Expr)this.Visit(s.Condition); this.stack.Push(new List <ExprVar>()); var then = (Stmt)this.Visit(s.Then); var thenDA = this.stack.Pop(); this.stack.Push(new List <ExprVar>()); var @else = (Stmt)this.Visit(s.Else); var elseDA = this.stack.Pop(); var intersection = thenDA.Intersect(elseDA, (IEqualityComparer <ExprVar>) this.phiComparer).ToArray(); this.stack.Peek().AddRange(intersection); var conditionVars = VisitorFindVars.V(condition); var needAssigning = conditionVars.Except(this.stack.SelectMany(x => x), (IEqualityComparer <ExprVar>) this.phiComparer).ToArray(); if (needAssigning.Any()) { var replacements = needAssigning.Select(x => { var newExpr = ctx.Local(x.Type); var phi = new ExprVarPhi(ctx) { Exprs = new[] { x, newExpr } }; return(new { orgExpr = x, newExpr, phi }); }).ToArray(); foreach (var replace in replacements) { this.stack.Peek().Add(replace.newExpr); this.stack.Peek().Add(replace.phi); condition = (Expr)VisitorReplace.V(condition, replace.orgExpr, replace.phi); } this.ensureAssigned.Add(s, replacements.Select(x => x.newExpr).ToArray()); } if (condition != s.Condition || then != s.Then || @else != s.Else) { var newS = new StmtIf(ctx, condition, then, @else); this.stmtMap.Add(newS, s); return(newS); } else { return(s); } }
protected virtual ICode VisitIf(StmtIf s) { this.ThrowOnNoOverride(); var condition = this.Visit(s.Condition); var then = this.Visit(s.Then); var @else = this.Visit(s.Else); if (condition != s.Condition || then != s.Then || @else != s.Else) { return(new StmtIf(s.Ctx, (Expr)condition, (Stmt)then, (Stmt)@else)); } else { return(s); } }
protected override ICode VisitIf(StmtIf s) { this.NewLine(); this.js.Append("if ("); this.Visit(s.Condition); this.js.Append(") {"); this.indent++; this.Visit(s.Then); this.indent--; if (s.Else != null) { this.NewLine(); this.js.Append("} else {"); this.indent++; this.Visit(s.Else); this.indent--; } this.NewLine(); this.js.Append("}"); return(s); }
private Stmt ParseIf() { StmtIf ifStmt = new StmtIf(); ifStmt.IfToken = tape.Current; StmtIf.StmtIfTrue ifPart = ParseTruePart(); ifStmt.Parts.Add(ifPart); while (tape.CurrentKind == TokenKind.ELSEIF) { StmtIf.StmtIfTrue elseifPart = ParseTruePart(); ifStmt.Parts.Add(elseifPart); } if (tape.CurrentKind == TokenKind.ELSE) { CodePosition pos = tape.Current.Position; tape.MoveNext(); ifStmt.ElsePart = ParseStmtBlock(pos); } return(ifStmt); }
protected override ICode VisitBlock(StmtBlock s) { var ctx = s.Ctx; var statements = s.Statements .Select(x => (Stmt)this.Visit(x)) .Where(x => x != null) .ToArray(); var stNew = statements.Combine((a, b) => { // If two 'if' statements are both continuations, then bring a recursive continuation forwards if possible if (a.StmtType == Stmt.NodeType.If && b.StmtType == Stmt.NodeType.If) { var aIf = (StmtIf)a; var bIf = (StmtIf)b; if (aIf.Then != null && bIf.Then != null && aIf.Then.StmtType == Stmt.NodeType.Continuation && bIf.Then.StmtType == Stmt.NodeType.Continuation && aIf.Else == null && bIf.Else == null) { var aCont = (StmtContinuation)aIf.Then; var bCont = (StmtContinuation)bIf.Then; if (aCont.To != this.block && bCont.To == this.block) { return(new StmtBlock(ctx, b, a)); } } } return(null); }) .ToArray(); if (!statements.SequenceEqual(stNew)) { return(new StmtBlock(ctx, stNew)); } // If an 'if' statement containing only a continuation is followed by any other kind of statement then swap them // (with suitable 'if' guard). Look ahead and encase as much as possible in the 'if' guard for (int i = 0; i < statements.Length - 1; i++) { var stmt = statements[i]; if (stmt.StmtType == Stmt.NodeType.If) { var sIf = (StmtIf)stmt; if (sIf.Then != null && sIf.Then.StmtType == Stmt.NodeType.Continuation && sIf.Else == null) { var moveCount = 0; for (int j = i + 1; j < statements.Length; j++) { var b = statements[j]; bool move; if (b.StmtType == Stmt.NodeType.Continuation) { move = false; } else if (b.StmtType != Stmt.NodeType.If) { move = true; } else { var bIf = (StmtIf)b; move = bIf.Then == null || bIf.Then.StmtType != Stmt.NodeType.Continuation || bIf.Else != null; } if (move) { moveCount++; } else { break; } } if (moveCount > 0) { var moveBlock = new StmtBlock(ctx, statements.Skip(i + 1).Take(moveCount)); var ifBlock = new StmtIf(ctx, ctx.ExprGen.Not(sIf.Condition), moveBlock, null); var allStmts = statements.Take(i) .Concat(ifBlock) .Concat(statements[i]) .Concat(statements.Skip(i + 1 + moveCount)) .ToArray(); return(new StmtBlock(ctx, allStmts)); } } } } // Reorder if/continuations at end of block to group by continuation target var finalContsRev = statements.Reverse().TakeWhile(x => { if (x.StmtType == Stmt.NodeType.Continuation) { return(true); } if (x.StmtType == Stmt.NodeType.If) { var xIf = (StmtIf)x; if (xIf.Else == null && xIf.Then.StmtType == Stmt.NodeType.Continuation) { return(true); } } return(false); }) .Select(x => { Stmt to; if (x.StmtType == Stmt.NodeType.Continuation) { to = ((StmtContinuation)x).To; } else { to = ((StmtContinuation)((StmtIf)x).Then).To; } return(new { stmt = x, to }); }).ToArray(); var lookForMidCont = finalContsRev.Reverse().TakeWhile(x => x.stmt.StmtType != Stmt.NodeType.Continuation).ToArray(); if (lookForMidCont.Length < finalContsRev.Length - 1) { // Look for non-if continuation in middle of final continuations // I suspect that this cannot ever occur var stmts = statements.Take(statements.Length - finalContsRev.Length) .Concat(lookForMidCont.Select(x => x.stmt)) .Concat(finalContsRev.Select(x => x.stmt).Reverse().ElementAt(lookForMidCont.Length)).ToArray(); return(new StmtBlock(s.Ctx, stmts)); } if (finalContsRev.Count() >= 2) { var newFinal = finalContsRev.Combine((a, b) => { if (a.stmt.StmtType == Stmt.NodeType.Continuation && a.to == b.to) { return(a); } return(null); }); if (!finalContsRev.SequenceEqual(newFinal)) { var stmts = statements.Take(statements.Length - finalContsRev.Length).Concat(newFinal.Reverse().Select(x => x.stmt)); return(new StmtBlock(s.Ctx, stmts)); } var dups = finalContsRev.GroupBy(x => x.to).Select(x => new { to = x.Key, count = x.Count() }).Where(x => x.count >= 2).ToArray(); if (dups.Any()) { var finalStmts = finalContsRev.Reverse().ToArray(); foreach (var dup in dups) { var movingBack = new List <Stmt>(); var addIfs = new Dictionary <Stmt, IEnumerable <Expr> >(); foreach (var stmt in finalStmts) { if (stmt.to == dup.to) { movingBack.Add(stmt.stmt); } else { addIfs.Add(stmt.stmt, movingBack.Select(x => s.Ctx.ExprGen.Not(((StmtIf)x).Condition)).ToArray()); } if (movingBack.Count == dup.count) { finalStmts = finalStmts.SelectMany(x => { if (movingBack.Contains(x.stmt)) { if (x.stmt == movingBack.Last()) { return(movingBack.Select(y => new { stmt = (Stmt)y, to = x.to })); } else { return(finalStmts.EmptyOf()); } } else { var addIf = addIfs.ValueOrDefault(x.stmt); if (addIf != null && addIf.Any()) { var @if = addIf.Aggregate((a, b) => s.Ctx.ExprGen.And(a, b)); var newIf = new StmtIf(s.Ctx, @if, x.stmt, null); return(new[] { new { stmt = (Stmt)newIf, to = x.to } }); } else { return(new[] { x }); } } }).ToArray(); break; } } } var stmts = statements.Take(statements.Length - finalContsRev.Length).Concat(finalStmts.Select(x => x.stmt)); return(new StmtBlock(s.Ctx, stmts)); } } if (!statements.SequenceEqual(s.Statements)) { return(new StmtBlock(ctx, statements)); } else { return(s); } }
private void BuildBlock(IEnumerable <Instruction> insts, Instruction startOfNextPart) { var inst0 = insts.First(); var instN = insts.Last(); // Any instruction in the method could target this block var blockStarts = this.methodBlockStarts .Where(x => x.Offset >= inst0.Offset && x.Offset <= instN.Offset) .ToArray(); // For each block create a StmtCil, with the correct ending. // Endings that require a expression have them set to null; they will be filled in during CIL decoding for (int i = 0; i < blockStarts.Length; i++) { var start = blockStarts[i]; var end = i == blockStarts.Length - 1 ? insts.Last() : blockStarts[i + 1].Previous; var blockInsts = start.GetRange(end); Stmt blockEndStmt; var code = end.OpCode.Code; switch (end.OpCode.FlowControl) { case FlowControl.Cond_Branch: if (code == Code.Switch) { var cases = ((Instruction[])end.Operand).Select(x => new StmtContinuation(this.ctx, x, false)).ToArray(); foreach (var @case in cases) { this.mappable.Add(@case); } var @default = new StmtContinuation(this.ctx, end.Next, false); this.mappable.Add(@default); blockEndStmt = new StmtSwitch(this.ctx, new ExprVarInstResult(this.ctx, end, this.ctx.Int32), cases.Select((x, value) => new StmtSwitch.Case(value, x)).ToArray(), @default); } else { var ifTrue = new StmtContinuation(this.ctx, (Instruction)end.Operand, false); var ifFalse = new StmtContinuation(this.ctx, end.Next, false); this.mappable.Add(ifTrue); this.mappable.Add(ifFalse); blockEndStmt = new StmtIf(this.ctx, new ExprVarInstResult(this.ctx, end, this.ctx.Boolean), ifTrue, ifFalse); } break; case FlowControl.Branch: var leaveProtectedRegion = code == Code.Leave || code == Code.Leave_S; blockEndStmt = new StmtContinuation(this.ctx, (Instruction)end.Operand, leaveProtectedRegion); this.mappable.Add((StmtContinuation)blockEndStmt); break; case FlowControl.Next: case FlowControl.Call: blockEndStmt = new StmtContinuation(this.ctx, end.Next, false); this.mappable.Add((StmtContinuation)blockEndStmt); break; case FlowControl.Return: switch (code) { case Code.Endfinally: blockEndStmt = new StmtContinuation(this.ctx, startOfNextPart, true); this.mappable.Add((StmtContinuation)blockEndStmt); break; case Code.Ret: blockEndStmt = new StmtContinuation(this.ctx, this.endBlock, false); blockInsts = start == end?Enumerable.Empty <Instruction>() : start.GetRange(end.Previous); // Remove 'ret' from statements break; default: blockEndStmt = null; break; } break; case FlowControl.Throw: blockEndStmt = null; break; default: throw new NotImplementedException("Cannot handle: " + end.OpCode.FlowControl); } var block = new StmtCil(this.ctx, blockInsts, blockEndStmt); this.blockMap.Add(start, new List <Stmt> { block }); } }
protected override ICode VisitIf(StmtIf s) { var then = (Stmt)this.Visit(s.Then); var @else = (Stmt)this.Visit(s.Else); if (then == null && @else == null) { return(null); } // Remove 'if' if condition is just true or false if (s.Condition.ExprType == Expr.NodeType.Literal) { if (s.Condition.IsLiteralBoolean(true)) { return(then); } if (s.Condition.IsLiteralBoolean(false)) { return(@else); } } // If 'then' and 'else' are identical, then remove 'if' if (then.DoesEqual(@else)) { return(then); } // If 'if' only has an 'else' case, not a 'then' case, then swap if (then == null) { return(new StmtIf(s.Ctx, s.Ctx.ExprGen.NotAutoSimplify(s.Condition), @else, null)); } // If both 'if' parts only contain an assignment to the same (with phi clustering) target, then turn into ternary assignment if (then != null && @else != null && then.StmtType == Stmt.NodeType.Assignment && @else.StmtType == Stmt.NodeType.Assignment) { var thenAssign = (StmtAssignment)then; var elseAssign = (StmtAssignment)@else; if (this.AreClustered(thenAssign.Target, elseAssign.Target)) { this.replaceVars.Add(Tuple.Create(elseAssign.Target, thenAssign.Target)); var ternary = new ExprTernary(s.Ctx, s.Condition, thenAssign.Expr, elseAssign.Expr); return(new StmtAssignment(s.Ctx, thenAssign.Target, ternary)); } } // If 'if' contains only 'if' then combine condition with 'and' if (@else == null && then.StmtType == Stmt.NodeType.If) { var thenIf = (StmtIf)then; if (thenIf.Else == null) { return(new StmtIf(s.Ctx, s.Ctx.ExprGen.And(s.Condition, thenIf.Condition), thenIf.Then, null)); } } if (then != s.Then || @else != s.Else) { return(new StmtIf(s.Ctx, s.Condition, then, @else)); } else { return(s); } }
protected override ICode VisitSwitch(StmtSwitch s) { // If switch statement contains no continuations then it doesn't need processing if (!VisitorFindContinuations.Any(s)) { return(base.VisitSwitch(s)); } var ctx = s.Ctx; // If any cases go to the same continuation as the default case, remove them if (s.Default != null && s.Default.StmtType == Stmt.NodeType.Continuation) { var defaultCont = (StmtContinuation)s.Default; var sameAsDefault = s.Cases .Where(x => x.Stmt != null && x.Stmt.StmtType == Stmt.NodeType.Continuation && ((StmtContinuation)x.Stmt).To == defaultCont.To) .ToArray(); if (sameAsDefault.Any()) { var cases = s.Cases.Except(sameAsDefault); return(new StmtSwitch(ctx, s.Expr, cases, s.Default)); } } // If multiple case statements all go the same continuation, then put them consecutively var groupedByTo = s.Cases .Where(x => x.Stmt != null && x.Stmt.StmtType == Stmt.NodeType.Continuation) .GroupBy(x => ((StmtContinuation)x.Stmt).To) .Where(x => x.Count() >= 2) .ToArray(); if (groupedByTo.Any()) { var cases = s.Cases.Except(groupedByTo.SelectMany(x => x)); var combinedCases = groupedByTo.SelectMany(x => { var same = x.ToArray(); var last = same.Last(); var sameCases = same.Take(same.Length - 1).Select(y => new StmtSwitch.Case(y.Value, null)) .Concat(new StmtSwitch.Case(last.Value, last.Stmt)); return(sameCases); }); var allCases = cases.Concat(combinedCases).ToArray(); return(new StmtSwitch(ctx, s.Expr, allCases, s.Default)); } Func <Stmt, IEnumerable <StmtContinuation> > getSingleFinalContinuation = stmt => { if (stmt == null) { return(Enumerable.Empty <StmtContinuation>()); } var contCount = VisitorFindContinuations.Get(stmt).Count(); if (contCount == 0) { // Case contains return or throw return(Enumerable.Empty <StmtContinuation>()); } if (contCount == 1) { if (stmt.StmtType == Stmt.NodeType.Continuation) { return(new[] { (StmtContinuation)stmt }); } if (stmt.StmtType == Stmt.NodeType.Block) { var stmtBlock = (StmtBlock)stmt; var last = stmtBlock.Statements.LastOrDefault(); if (last != null && last.StmtType == Stmt.NodeType.Continuation) { return(new[] { (StmtContinuation)last }); } } } return(new StmtContinuation[] { null }); }; var conts = s.Cases.Select(x => x.Stmt).Concat(s.Default).SelectMany(x => getSingleFinalContinuation(x)).ToArray(); if (conts.All(x => x != null)) { // If all cases end with a continuation to the same stmt, then put that stmt after the switch and remove all continuations if (conts.AllSame(x => x.To)) { Func <Stmt, Stmt> removeCont = stmt => { if (stmt == null) { return(null); } switch (stmt.StmtType) { case Stmt.NodeType.Continuation: return(new StmtBreak(ctx)); case Stmt.NodeType.Block: var sBlock = (StmtBlock)stmt; var stmts = sBlock.Statements.ToArray(); if (stmts.Last().StmtType == Stmt.NodeType.Continuation) { stmts = stmts.Take(stmts.Length - 1).Concat(new StmtBreak(ctx)).ToArray(); return(new StmtBlock(ctx, stmts)); } else { return(stmt); } default: return(stmt); } }; var cases = s.Cases.Select(x => new StmtSwitch.Case(x.Value, removeCont(x.Stmt))).ToArray(); var @switch = new StmtSwitch(ctx, s.Expr, cases, removeCont(s.Default)); return(new StmtBlock(ctx, @switch, conts[0])); } else if (this.lastChance) { // HACK: Change it into multiple if statements var multiValues = new List <int>(); var converted = s.Cases.Aggregate(s.Default, (@else, @case) => { multiValues.Add(@case.Value); if (@case.Stmt == null) { return(@else); } else { var cond = multiValues.Aggregate((Expr)ctx.Literal(false), (expr, caseValue) => { return(ctx.ExprGen.Or(expr, ctx.ExprGen.Equal(s.Expr, ctx.Literal(caseValue)))); }); multiValues.Clear(); var @if = new StmtIf(ctx, cond, @case.Stmt, @else); return(@if); } }); return(converted); } // If some cases end in a continuation that itself ends in a continuation that other cases end with // then use an extra variable to store whether to execute the intermediate code // TODO: This is too specific, need a more general-purpose solution to the problem where cases // don't all end by going to the same place //var contTos = conts.Select(x => x.To).Distinct().ToArray(); //var finalContTos = contTos.Select(x => getSingleFinalContinuation(x).Select(y => y.NullThru(z => z.To))).SelectMany(x => x).ToArray(); //if (!finalContTos.Any(x => x == null)) { // // All continuations are fully substituted // var distinctFinalContTos = finalContTos.Distinct().ToArray(); // if (distinctFinalContTos.Length == 1) { // var selector = ctx.Local(ctx.Int32); // var inIfCont = contTos.Single(x => x != distinctFinalContTos[0]); // var inIf = new StmtContinuation(ctx, inIfCont, false); // var afterIf = new StmtContinuation(ctx, distinctFinalContTos[0], false); // var allCasesTo = new StmtBlock(ctx, // new StmtIf(ctx, ctx.ExprGen.Equal(selector, ctx.Literal(1)), inIf, null), // afterIf); // Func<Stmt, Stmt> adjustCont = stmt => { // var cont = VisitorFindContinuations.Get(stmt).Single(); // var newCont = new StmtContinuation(ctx, allCasesTo, false); // var contChanged = (Stmt)VisitorReplace.V(stmt, cont, newCont); // var sValue = cont.To == inIf.To ? 1 : 0; // var withSelectorSet = new StmtBlock(ctx, // new StmtAssignment(ctx, selector, ctx.Literal(sValue)), // contChanged); // return withSelectorSet; // }; // var cases = s.Cases.Select(x => new StmtSwitch.Case(x.Value, adjustCont(x.Stmt))).ToArray(); // var @switch = new StmtSwitch(ctx, s.Expr, cases, adjustCont(s.Default)); // return @switch; // } else { // throw new NotImplementedException(); // } //} } return(base.VisitSwitch(s)); }
public static ICode V(ICode ast) { var ctx = ast.Ctx; var blockInfo = FindSuitableBlocks.GetInfo(ast); var bestBlock = blockInfo .Where(x => !x.Value.containsLeaveProtectedRegion && x.Value.numConts <= 1) .OrderBy(x => x.Value.numConts == 0 ? 1000 : x.Value.numConts) .ThenBy(x => x.Value.numICodes) .FirstOrDefault(); if (bestBlock.Key == null) { return(ast); } if (bestBlock.Value.numConts > 1) { // Best block must have just one continuation return(ast); } Stmt addContTo = null; if (bestBlock.Value.numConts == 0) { addContTo = (Stmt)blockInfo.FirstOrDefault(x => x.Value.numConts == 0 && x.Key != bestBlock.Key).Key; if (addContTo == null) { return(ast); } } var blockAst = (Stmt)bestBlock.Key; if (blockAst.StmtType != Stmt.NodeType.Block) { // Best block must be StmtBlock return(ast); } var stmtBlock = (StmtBlock)blockAst; var stmts = stmtBlock.Statements.ToArray(); var cont = bestBlock.Value.numConts == 0 ? new StmtContinuation(ctx, addContTo, false) : (StmtContinuation)stmts.Last(); var ifSkipContentPhi = new ExprVarPhi(ctx); var ifSkipInitialVar = ctx.Local(ctx.Boolean); var ifSkipContentVar = ctx.Local(ctx.Boolean); var ifSkipContentPhiExprs = new List <Expr> { ifSkipInitialVar, ifSkipContentVar }; var ifSkipReset = new StmtAssignment(ctx, ifSkipContentVar, ctx.Literal(false)); var inIfBlock = new StmtBlock(ctx, stmts.Take(stmts.Length - (bestBlock.Value.numConts == 0 ? 0 : 1))); var ifBlock = new StmtIf(ctx, ctx.ExprGen.Not(ifSkipContentPhi), inIfBlock, null); var newBlock = new StmtBlock(ctx, ifBlock, ifSkipReset, cont); ast = VisitorReplace.V(ast, blockAst, newBlock); var allConts = VisitorFindContinuationsRecursive.Get(ast); var contsNeedChanging = allConts.Where(x => x != cont && x.To == cont.To).ToArray(); var contsReplaceInfo = contsNeedChanging .Select(x => { var ifVar = ctx.Local(ctx.Boolean); var newCont = new StmtBlock(ctx, new StmtAssignment(ctx, ifVar, ctx.Literal(true)), new StmtContinuation(ctx, newBlock, x.LeaveProtectedRegion)); return(new { oldCont = x, ifVar, newCont }); }) .ToArray(); foreach (var contReplace in contsReplaceInfo) { ifSkipContentPhiExprs.Add(contReplace.ifVar); ast = VisitorReplace.V(ast, contReplace.oldCont, contReplace.newCont); } ifSkipContentPhi.Exprs = ifSkipContentPhiExprs; // TODO: Shouldn't be required, but definite-assignment doesn't quite work properly, so is required var initalSkipVarAssignment = new StmtAssignment(ctx, ifSkipInitialVar, ctx.Literal(false)); var newAst = new StmtBlock(ctx, initalSkipVarAssignment, (Stmt)ast); return(newAst); }
protected override ICode VisitIf(StmtIf s) { var ctx = s.Ctx; var eg = ctx.ExprGen; var condition = (Expr)this.Visit(s.Condition); var notCondition = eg.Not(condition); var assignmentsTo = this.assignmentsTo; this.assignmentsTo = new List <Expr>(); this.knownTrue.Push(new List <Expr>()); this.AddKnownTrue(condition); var then = (Stmt)this.Visit(s.Then); var thenTrue = this.knownTrue.Pop(); var thenAssignmentsTo = this.assignmentsTo; this.assignmentsTo = new List <Expr>(); this.knownTrue.Push(new List <Expr>()); this.AddKnownTrue(notCondition); var @else = (Stmt)this.Visit(s.Else); var elseTrue = this.knownTrue.Pop(); var elseAssignmentsTo = this.assignmentsTo; this.assignmentsTo = assignmentsTo.Concat(thenAssignmentsTo).Concat(elseAssignmentsTo).ToList(); if (this.HasBlockExit(s.Then)) { this.AddKnownTrue(elseTrue); } else { var known = thenTrue.Skip(1).Select(x => eg.Or(notCondition, x)).ToArray(); this.AddKnownTrue(known); } if (this.HasBlockExit(s.Else)) { this.AddKnownTrue(thenTrue); } else { var known = elseTrue.Skip(1).Select(x => eg.Or(condition, x)).ToArray(); this.AddKnownTrue(known); } var thenAssignmentTruths = thenAssignmentsTo.Select(x => eg.Or(condition, eg.Not(x))).ToArray(); this.AddKnownTrue(thenAssignmentTruths); var elseAssignmentTruths = elseAssignmentsTo.Select(x => eg.Or(notCondition, eg.Not(x))).ToArray(); this.AddKnownTrue(elseAssignmentTruths); if (condition != s.Condition || then != s.Then || @else != s.Else) { return(new StmtIf(ctx, condition, then, @else)); } else { return(s); } }