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 VisitContinuation(StmtContinuation s) { // TODO: Why is this de-recursing blocks in continuations? Why doesn't it just derecurse itself (if possible)??? if (s.To.StmtType != Stmt.NodeType.Block) { return(base.VisitContinuation(s)); } if (!this.seen.Add(s.To)) { return(base.VisitContinuation(s)); } var block = (StmtBlock)s.To; foreach (var stmt in block.Statements) { if (stmt.StmtType == Stmt.NodeType.Continuation) { // Continuation not inside 'if' var sCont = (StmtContinuation)stmt; if (sCont.To == block) { // Recursive, so derecurse with no condition on loop var body = new StmtBlock(s.Ctx, block.Statements.TakeWhile(x => x != stmt).ToArray()); var replaceWith = new StmtDoLoop(s.Ctx, body, new ExprLiteral(s.Ctx, true, s.Ctx.Boolean)); this.replaces.Add(s.To, replaceWith); return(base.VisitContinuation(s)); } } if (stmt.StmtType == Stmt.NodeType.If) { // Continuation only statement within 'if' with only a 'then' clause var sIf = (StmtIf)stmt; if (sIf.Else == null && sIf.Then.StmtType == Stmt.NodeType.Continuation) { var sThen = (StmtContinuation)sIf.Then; if (sThen.To == block) { // Recursive, so derecurse var condition = sIf.Condition; var bodyStmts = block.Statements.TakeWhile(x => x != stmt).ToArray(); var bodyLast = bodyStmts.LastOrDefault(); var body = new StmtBlock(s.Ctx, bodyStmts); var loop = new StmtDoLoop(s.Ctx, body, condition); var afterLoop = block.Statements.SkipWhile(x => x != stmt).Skip(1).ToArray(); if (VisitorFindContinuations.Get(new StmtBlock(s.Ctx, afterLoop)).Any(x => x.To == block)) { // Cannot de-recurse yet, must wait for continuations to be merged return(base.VisitContinuation(s)); } Stmt replaceWith; if (afterLoop.Any()) { var loopAndAfter = new[] { loop }.Concat(afterLoop).ToArray(); replaceWith = new StmtBlock(s.Ctx, loopAndAfter); } else { replaceWith = loop; } this.replaces.Add(s.To, replaceWith); return(base.VisitContinuation(s)); } } } if (VisitorFindContinuations.Any(stmt)) { // Another continuation present, cannot derecurse break; } } return(base.VisitContinuation(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)); }