private void CreateOrMergeBsi(Stmt s, Expr[] stack, Expr[] locals, Expr[] args) { if (s.StmtType == Stmt.NodeType.Try) { var sTry = (StmtTry)s; // It is fine to use 'locals' and 'args' in catch/finally because the phi clustering performed later // will conglomerate all the necessary variables if (sTry.Catches != null) { var catch0 = sTry.Catches.First(); this.CreateOrMergeBsi(catch0.Stmt, new Expr[] { catch0.ExceptionVar }, locals, args); } if (sTry.Finally != null) { this.CreateOrMergeBsi(sTry.Finally, new Expr[0], locals, args); } this.CreateOrMergeBsi(sTry.Try, stack, locals, args); return; } if (s.StmtType != Stmt.NodeType.Cil) { throw new InvalidCastException("Should not be seeing: " + s.StmtType); } // Perform create/merge Func<Expr, IEnumerable<Expr>> flattenPhiExprs = null; flattenPhiExprs = e => { if (e.ExprType == Expr.NodeType.VarPhi) { return ((ExprVarPhi)e).Exprs.SelectMany(x => flattenPhiExprs(x)); } return new[] { e }; }; Action<ExprVarPhi[], IEnumerable<Expr>> merge = (bsiVars, thisVars) => { foreach (var v in bsiVars.Zip(thisVars, (a, b) => new { phi = a, add = b })) { if (v.add != null) { v.phi.Exprs = flattenPhiExprs(v.add).Concat(v.phi.Exprs).Where(x => x != null && x != v.phi).Distinct().ToArray(); } } }; BlockInitInfo bsi; if (!this.blockStartInfos.TryGetValue(s, out bsi)) { Func<IEnumerable<Expr>, ExprVarPhi[]> create = exprs => exprs.Select(x => { if (x == null) { return new ExprVarPhi(this.ctx) { Exprs = new Expr[0] }; } if (x.ExprType == Expr.NodeType.VarPhi) { return (ExprVarPhi)x; } return new ExprVarPhi(this.ctx) { Exprs = new[] { x } }; }).ToArray(); bsi = new BlockInitInfo { Stack = create(stack), Locals = create(locals), Args = create(args), }; this.blockStartInfos.Add(s, bsi); } else { merge(bsi.Stack, stack); merge(bsi.Locals, locals); merge(bsi.Args, args); // Forward-merge through already-processed nodes for vars that are not changed in a node var fmSeen = new HashSet<Stmt>(); Action<Stmt> forwardMerge = null; forwardMerge = (stmt) => { if (fmSeen.Add(stmt)) { var fmBsi = this.blockStartInfos.ValueOrDefault(stmt); var fmChanges = this.stmtVarsChanged.ValueOrDefault(stmt); if (fmBsi != null && fmChanges != null) { var fmStack = fmBsi.Stack.Take(fmChanges.Stack.Length).Select((x, i) => fmChanges.Stack[i] ? x : null).ToArray(); var fmLocals = fmBsi.Locals.Take(fmChanges.Locals.Length).Select((x, i) => fmChanges.Locals[i] ? x : null).ToArray(); var fmArgs = fmBsi.Args.Take(fmChanges.Args.Length).Select((x, i) => fmChanges.Args[i] ? x : null).ToArray(); if (fmStack.Any(x => x != null) || fmLocals.Any(x => x != null) || fmArgs.Any(x => x != null)) { var fmConts = VisitorFindContinuations.Get(stmt); foreach (var fmCont in fmConts) { var fmContBsi = this.blockStartInfos.ValueOrDefault(fmCont.To); if (fmContBsi != null) { merge(fmContBsi.Stack, fmStack); merge(fmContBsi.Locals, fmLocals); merge(fmContBsi.Args, fmArgs); forwardMerge(fmCont.To); } } } } } }; forwardMerge(s); } }
private void CreateOrMergeBsi(Stmt s, Expr[] stack, Expr[] locals, Expr[] args) { if (s.StmtType == Stmt.NodeType.Try) { var sTry = (StmtTry)s; // It is fine to use 'locals' and 'args' in catch/finally because the phi clustering performed later // will conglomerate all the necessary variables if (sTry.Catches != null) { var catch0 = sTry.Catches.First(); this.CreateOrMergeBsi(catch0.Stmt, new Expr[] { catch0.ExceptionVar }, locals, args); } if (sTry.Finally != null) { this.CreateOrMergeBsi(sTry.Finally, new Expr[0], locals, args); } this.CreateOrMergeBsi(sTry.Try, stack, locals, args); return; } if (s.StmtType != Stmt.NodeType.Cil) { throw new InvalidCastException("Should not be seeing: " + s.StmtType); } // Perform create/merge Func <Expr, IEnumerable <Expr> > flattenPhiExprs = null; flattenPhiExprs = e => { if (e.ExprType == Expr.NodeType.VarPhi) { return(((ExprVarPhi)e).Exprs.SelectMany(x => flattenPhiExprs(x))); } return(new[] { e }); }; Action <ExprVarPhi[], IEnumerable <Expr> > merge = (bsiVars, thisVars) => { foreach (var v in bsiVars.Zip(thisVars, (a, b) => new { phi = a, add = b })) { if (v.add != null) { v.phi.Exprs = flattenPhiExprs(v.add).Concat(v.phi.Exprs).Where(x => x != null && x != v.phi).Distinct().ToArray(); } } }; BlockInitInfo bsi; if (!this.blockStartInfos.TryGetValue(s, out bsi)) { Func <IEnumerable <Expr>, ExprVarPhi[]> create = exprs => exprs.Select(x => { if (x == null) { return(new ExprVarPhi(this.ctx) { Exprs = new Expr[0] }); } if (x.ExprType == Expr.NodeType.VarPhi) { return((ExprVarPhi)x); } return(new ExprVarPhi(this.ctx) { Exprs = new[] { x } }); }).ToArray(); bsi = new BlockInitInfo { Stack = create(stack), Locals = create(locals), Args = create(args), }; this.blockStartInfos.Add(s, bsi); } else { merge(bsi.Stack, stack); merge(bsi.Locals, locals); merge(bsi.Args, args); // Forward-merge through already-processed nodes for vars that are not changed in a node var fmSeen = new HashSet <Stmt>(); Action <Stmt> forwardMerge = null; forwardMerge = (stmt) => { if (fmSeen.Add(stmt)) { var fmBsi = this.blockStartInfos.ValueOrDefault(stmt); var fmChanges = this.stmtVarsChanged.ValueOrDefault(stmt); if (fmBsi != null && fmChanges != null) { var fmStack = fmBsi.Stack.Take(fmChanges.Stack.Length).Select((x, i) => fmChanges.Stack[i] ? x : null).ToArray(); var fmLocals = fmBsi.Locals.Take(fmChanges.Locals.Length).Select((x, i) => fmChanges.Locals[i] ? x : null).ToArray(); var fmArgs = fmBsi.Args.Take(fmChanges.Args.Length).Select((x, i) => fmChanges.Args[i] ? x : null).ToArray(); if (fmStack.Any(x => x != null) || fmLocals.Any(x => x != null) || fmArgs.Any(x => x != null)) { var fmConts = VisitorFindContinuations.Get(stmt); foreach (var fmCont in fmConts) { var fmContBsi = this.blockStartInfos.ValueOrDefault(fmCont.To); if (fmContBsi != null) { merge(fmContBsi.Stack, fmStack); merge(fmContBsi.Locals, fmLocals); merge(fmContBsi.Args, fmArgs); forwardMerge(fmCont.To); } } } } } }; forwardMerge(s); } }