Com S 641 Lecture -*- Outline -*- * Interprocedural Analysis (2.5) This section looks at analysis for languages with procedures, including the structural operational semantics for such a language. we'll study whole-program analysis (non-modular) complications: matching calls and returns, parameter passing mechanisms, aliasing (from call by reference), higher-order procedures ** syntax ------------------------------------------ SYNTAX Procedures with 1 value parameter, and 1 result parameter. P \in Program D \in Declaration P ::= begin D S end D ::= proc p(val x, res y) is^ln S end^lx | D D S ::= ... | [call p(a,z)]^lc_lr Example: begin proc fact(val n, res v) is^1 if [n = 0]^2 then [v := 1]^3 else [call fact(n-1, v)]^4_5; end^6; [call fact(3,v)]^7_8; [call fact(v,w)]^9_10 end ------------------------------------------ assume unique labels (label consistency) assume no redeclarations, only declared procedures are called assume names of all formals in a proc decl are distinct (x =/= y) Q: What else do we need to do analysis and correctness proofs? operational semantics flows, etc. ** operational semantics (2.5.1) Q: How is a procedure different than a macro? procedures have local variables (formals), different in each call so names aren't sufficient to distinguish each instantiation of a formal parameter, need locations... ------------------------------------------ OPERATIONAL SEMANTICS xi in Loc locations rho in Env = Var* -> Loc environments s in Store = Loc ->_fin Z stores Assume s o r is total: ran(rho) \subseteq dom(s) ------------------------------------------ (they use \varsigma for this kind of store, instead of \sigma) Loc ->_fin Z is set of partial functions with a finite domain Q: How do these states relate to the states we had previously? old ones are the composition of a store and an environment Q: How should we deal with global variables? top-level environment, rho* assume injective ------------------------------------------ OPERATIONAL SEMANTICS [skip] rho |-* ([skip]^l, s) --> s [ass] rho |-* ([x:=a]^l,s) --> s[x |-> A[[A]](s o rho)] if s o rho is total ------------------------------------------ Q: What would the sequence rules look like? while? if? Q: What would the calls look like in the operational semantics? two parts, evaluation of actual parameters, and parameter passing ------------------------------------------ CALL AND BIND RULES [call] rho |-* ([call p(a,z)]^lc_lr, s) --> (bind rho*[x |-> xi1, y |-> xi2] in S then z := y, s[xi1 |-> A[[a]](s o r), xi2 |-> v]) if xi1, xi2 not in dom(s), v in Z, proc p(val x, res y) is^ln S end^lx is in D* rho' |-* (S, s) --> (S', s') [bind1]________________________________ rho |-* (bind rho' in S then z := y, s) --> (bind rho' in S' then z := y, s') rho' |-* (S, s) --> s' [bind2]________________________________ rho |-* (bind rho' in S then z := y, s) --> s'[rho(z) |-> s'(rho'(y))] ------------------------------------------ bind-in-then is a 3 part tag, used for scoping Q: How do you parse the first rule? Q: Where is bind-in-then in the surface syntax? it isn't, just used for the operational semantics Q: What is [bind2] doing? termination of call, pass by result for z Q: Do we have to deal with bind-in-then for proofs? yes! ** flow graphs Q: How should we make flow graphs for calls? ------------------------------------------ FLOW GRAPHS FOR CALLS init([call p(a, z)]^lc_lr) = final([call p(a, z)]^lc_lr) = blocks([call p(a, z)]^lc_lr) = labels([call p(a, z)]^lc_lr) = flow([call p(a, z)]^lc_lr) = {(lc;ln), (lx;lr)} if proc p(val x, res y) is^ln S end lx is in D* ------------------------------------------ Q: What should these be? Q: Why use semicolons for the flows? to see which ones are procedure flows... Q: What would happen if p was a program variable? dynamic dispatch ==> harder ------------------------------------------ FLOW GRAPHS FOR PROCEDURES For each procedure declaration proc p(val x, res y) is^ln S end lx init(p) = final(p) = blocks(p) = {is^ln, end^lx} \cup blocks(S) labels(p) = flow(p) = ------------------------------------------ Q: What should these be? ------------------------------------------ FLOW GRAPHS FOR PROGRAMS For program P* = begin D* S* end init* = init(S*) final* = final(S*) blocks* = blocks(S*) \cup \bigcup{blocks(p) | proc p... in D*} labels* = blocks(S*) \cup \bigcup{labels(p) | proc p... in D*} flow* = blocks(S*) \cup \bigcup{blocks(p) | proc p... in D*} ------------------------------------------ Q: What is Lab* for such a program? label* ------------------------------------------ INTERPROCEDURAL FLOWS inter-flow* = { (lc, ln, lx, lr) | P* contains [call p(a,z)]^lc_lr and proc p(val x, res y) is^ln S end^lx } Notation: IF is an abstraction of inter-flow* for forward analysis: IF = inter-flow* for backward analysis: IF = inter-flow^R* ------------------------------------------ Q: How could we use inter-flow*? relates calls and returns Suppose we have (lpc, lpn, lpx, lpr) and (lqc, lqn, lqx, lqr) in inter-flow* Then flow* has (lpc;lqn), (lqc;lpn), which can't happen. ------------------------------------------ EXAMPLE begin proc fact(val n, res v) is^1 if [n = 0]^2 then [v := 1]^3 else [call fact(n-1, v)]^4_5; end^6; [call fact(3,w)]^7_8; [call fact(w,w)]^9_10 end What is flow*? What is inter-flow*? ------------------------------------------ Draw the flow graph Q: What else do we need to do analysis? (nothing?) ** problems with adapting earlier techniques (2.5.2) Q: If we don't use inter-flow* in an analysis, what happens? ------------------------------------------ PROBLEMS WITH NAIVE APPROACH RDentry(l) = if l = init(S*) then {(x,?)|x in Var*} else \bigcup {RDexit(l') | (l',l) \in flow* or (l';l) \in flow* } RDexit(l) = f_l(RDentry(l)) where f_l(RD) = if ([x:=a]^l in blocks*) then (RD \ {(x,l')|l' in Lab^?_*}) \cup {(x,l)} else if ([call p(a,z)]^l_lr in blocks* and proc p(val x, res y) is^ln S end^lx in D*) then (RD \ {(x,l')|l' in Lab^?_*}) \cup {(x,l)} else if ([call p(a,z)]^lc_l in blocks*) then (RD \ {(z,l')|l' in Lab^?_*}) \cup {(z,l)} else RD ------------------------------------------ Q: What are the transfer functions for ln an lx for a procedure definition of the form proc p (val x, res y) is^ln S end^lx ? the identity function Q: So, what flows are permitted for our example? paths like [7,1,2,3,6,10] and [9,1,2,3,6,8] Q: What's wrong with that? The extra paths lead to imprecision (but still safety is preserved) ** making context explicit (2.5.3) *** embellished monotone framework ------------------------------------------ MAKING CONTEXT EXPLICIT context information: \delta \in \Delta def: Suppose (L, Funs, F, E, i, f_.) is an instance of a monotone framework. Then (\hat{L},\hat{Funs},F,E,\hat{i},\hat{f_.}) is an *embellished monotone framework* iff \hat{L} = \Delta -> L each \hat{g} in \hat{Funs} is monotone and \hat{g} : (\Delta -> L) -> (\Delta -> L) forall all Y in \hat{L}, \hat{f_l}(Y)(\delta) = f_l(Y (\delta)) ------------------------------------------ *** example ------------------------------------------ EXAMPLE (REACHING DEFINITIONS) \hat{L} = \Delta -> Powerset(Var* x Lab^?_*) RDentry(l) = if l = init(S*) then lambda delta . {(x,?)|x in Var*} else lambda delta . \bigcup {RDexit(l')(delta) | (l',l) \in flow* or (l';l) \in flow* } RDexit(l) = if (not (callOrReturn(l))) then \hat{f_l}(RDentry(l)) else if call(l) then \hat{f^1_l}(RDentry(l)) else // for returns \hat{f^2_{lc,l}}(RDentry(lc), RDentry(l)) for all lc such that (lc, ln, lx, l) \in inter-flow* where call(l) = (\exists ln, lx, lr \in Lab*:: (l, ln, lx, lr) \in inter-flow*) return(l) = (\exists lc, ln, lx, \in Lab*:: (lc, ln, lx, l) \in inter-flow*) callOrReturn(l) = call(l) or return(l) \hat{f_l}(Y) = lambda delta . if (B^l is a block of form [x:=a]^l) then (Y(delta) \ {(x,l')|l' in Lab^?_*}) \cup {(x,l)} else Y(delta) \hat{f^1_lc}(Y) = lambda delta . (Y(delta) \ {(x,l')|l' in Lab^?_*, x \in val-formals(lc) or x \in res-formals(lc)}) \cup {(x,l) | x \in val-formals(lc)} \cup {(y,?) | y \in res-formals(lc)} \hat{f^2_{lc,lr}}(X, Y) = lambda delta . (({(x,l)| (x, l) \in X(delta), x \in val-formals(lr) or x \in res-formals(lr)} \cup (Y(delta) \ {(x,l')|l' in Lab^?_*, x in val-formals(lr) or x in res-formals(lr)})) \ {(z,l') | z in res-actuals(lr)}) \cup {(z,lr)| z in res-actuals(lr)} x \in val-formals(l) = ([call p(a,z)]^l_lr in blocks* or [call p(a,z)]^lc_l in blocks*) and proc p(val x, res y) is^ln S end^lx is in D* y \in res-formals(l) = ([call p(a,z)]^l_lr in blocks* or [call p(a,z)]^lc_l in blocks*) and proc p(val x, res y) is^ln S end^lx is in D* z \in res-actuals(l) = ([call p(a,z)]^l_lr in blocks* or [call p(a,z)]^lc_l in blocks*) and proc p(val x, res y) is^ln S end^lx is in D* ------------------------------------------ In \hat{f^2_{lc,lr}}(X, Y) we: - restore the old analysis information for the formals - pass through all information about variables that are not formals - and then make the assignment to the acutal result parameter(s) Q: How does the analysis use the context, delta ? to distinguish between different incarnations of each procedure Q: What happens if the context is a constant in the program? all calls lumped together Q: Is the context ever changed by these equations? *NO*, so the above isn't quite right, as we don't manipulate the context, delta, which thus never changes *** general form of transfer functions ------------------------------------------ GENERAL FORM OF DATAFLOW EQUATIONS FOR EMBELLISHED MONOTONE FRAMEWORKS A_o(l) = if l \in E then \hat{i} else \bigsqcup {A_.(l')(delta) | (l',l) \in F or (l';l) \in F } A_.(l) = if (not (callOrReturn(l))) then \hat{f_l}(A_o(l)) else if call(l) then \hat{f^1_l}(A_o(l)) else // for returns \hat{f^2_{lc,l}}(A_o(lc), A_o(l)) for all lc such that (lc, ln, lx, l) \in IF ------------------------------------------ Q: What kinds of things can \hat{f^2_l) do? ignore either argument, take the join of the information before and after the call. *** using the context context sensitive = distinguishing between call sites context insensitive = combining all information and analysising proc only once What's the tradeoff? precision vs. efficiency ** modeling context (2.5.4) What should be in the context type, \Delta ? *** call strings of unbounded length ------------------------------------------ MODELING THE CONTEXT def: a *call string*, in Lab^*, is a sequence of labels [l_1, ... , l_n], n >= 0, such that for each 0 <= i <= n, call(l_i) holds. Example: begin proc fact(val n, res v) is^1 if [n = 0]^2 then [v := 1]^3 else [call fact(n-1, v)]^4_5; end^6; [call fact(3,w)]^7_8; [call fact(w,w)]^9_10 end has call strings: [], [7], [7, 4], [7, 4, 4], ... [9], [9, 4], [9, 4, 4], ... ------------------------------------------ ------------------------------------------ EXAMPLE, FIXED REACHING DEFINITIONS \hat{L} = \Delta -> Powerset(Var* x Lab^?_*) \Delta = call strings delta \in \Delta only changes on call and return: RDentry(l) = lambda delta . if delta = [] and l = init(S*) then {(x,?)|x in Var*} else \bigcup {RDexit(l')(delta) | (l',l) \in flow* or (l';l) \in flow* } RDexit(l) = lambda delta . if (not (callOrReturn(l))) then \hat{f_l}(RDentry(l))(delta) else if call(l) then \hat{f^1_l}(RDentry(l))(delta : l) else // for returns \hat{f^2_{lc,l}}(RDentry(lc), RDentry(l))(delta) for all lc such that (lc, ln, lx, l) \in inter-flow* where \hat{f^1_lc}(Y) = lambda delta' . (Y(pop(delta')) \ {(x,l') | l' in Lab^?_*, x \in val-formals(lc), or x \in res-formals(lc)}) \cup {(x,lc) | x \in val-formals(lc)} \cup {(y,?) | y \in res-formals(lc)} pop(delta') = if delta' == (delta:lc) then delta else error \hat{f^2_{lc,lr}}(X, Y) = lambda delta . (({(x,l)| (x, l) \in X(delta), x \in val-formals(lr) or x \in res-formals(lr)} \cup (Y(delta : lc) \ {(x,l')| l' in Lab^?_*, x in val-formals(lr) or x in res-formals(lr)})) \ {(z,l') | z in res-actuals(lr)}) \cup {(z,lr) | z in res-actuals(lr)} ------------------------------------------ In \hat{f^2_{lc,lr}}(X, Y) we: - restore the old analysis information for the formals - pass through all information about variables that are not formals - and then make the assignment to the acutal result parameter(s) recall that \hat{f_l}(Y) = lambda delta . if (B^l is a block of form [x:=a]^l) then (Y(delta) \ {(x,l')|l' in Lab^?_*}) \cup {(x,l)} else Y(delta) Note the change in initial conditions for RDentry, which tests delta now. Q: How is the context manipulated by these transfer functions? in \hat{f^1_lc} we effectively assume that lc is added to the call string, by taking it off when using Y. in \hat{f^2_{lc,lr}} we assume it's not there, and so add it back in when working with the result from the procedure call Look at example 2.38 for another example of this, with isomorphic domain (back in example 2.36). Q: How do these work with the formal parameters? Q: Does this work if a formal has the same name as an actual parameter? Example: begin proc fact(val n, res v) is^1 if [n = 0]^2 then [v := 1]^3 else [call fact(n-1, v)]^4_5; end^6; [call fact(3,v)]^7_8 end RDentry(1) = lambda delta . RDexit(7)(delta) \cup RDexit(4)(delta) RDexit(1) = lambda delta . (RDentry(l))(delta) RDentry(2) = lambda delta . RDexit(1)(delta) RDexit(2) = lambda delta . (RDentry(2))(delta) RDentry(3) = lambda delta . RDexit(2)(delta) RDexit(3) = lambda delta . ((RDentry(3))(delta) \ { (v,l')|l' in Lab^?_*}) \cup {(v,3)} RDentry(4) = lambda delta . RDexit(2)(delta) RDexit(4) = lambda delta' . (RDentry(4)(pop(delta')) \ {(x,l')|l' in Lab^?_*, x == n or x == v}) \cup {(n,4)} \cup {(v,?)} RDentry(5) = lambda delta . RDexit(6)(delta) RDexit(5) = lambda delta . (({(x,l)| (x, l) \in RDentry(4)(delta), x == n or x == v} \cup (RDentry(5)(delta : 4) \ {(x,l')|l' in Lab^?_*, x == n or x == v})) \ {(v,l')||l' in Lab^?_*}) \cup {(v,5)} RDentry(6) = lambda delta . RDexit(3)(delta) \cup RDexit(5)(delta) RDexit(6) = lambda delta . RDentry(6)(delta) RDentry(7) = lambda delta . {(x,?)|x in Var*} RDexit(7) = lambda delta' . (RDentry(7)(pop(delta')) \ {(x,l')|l' in Lab^?_*, x == n or x == v}) \cup {(n,7)} \cup {(v,?)} RDentry(8) = lambda delta . RDexit(6)(delta) RDexit(8) = lambda delta . (({(x,l)| (x,l) \in RDentry(7)(delta), x == n or x == v} \cup (RDentry(8)(delta : 7) \ {(x,l')|l' in Lab^?_*, x == n or x == v})) \ {(v,l')|l' in Lab^?_*}) \cup {(v,8)} Simplifying: RDentry(7) = {by Var* = {n,v} for this program} lambda delta . {(n,?),(v,?)} RDexit(7) = {by def.} lambda delta' . (RDentry(7)(pop(delta')) \ {(x,l')|l' in Lab^?_*, x == n or x == v}) \cup {(n,7)} \cup {(v,?)} = {by above calculation for RDentry(7)} lambda delta' . ({(n,?),(v,?)} \ {(x,l')|l' in Lab^?_*, x == n or x == v}) \cup {(n,7)} \cup {(v,?)} = {by set theory} lambda delta' . {(n,7),(v,?)} RDexit(8) = {by def.} lambda delta . (({(x,l)| (x,l) \in RDentry(7)(delta), x == n or x == v} \cup (RDentry(8)(delta : 7) \ {(x,l')|l' in Lab^?_*, x == n or x == v})) \ {(v,l')|l' in Lab^?_*}) \cup {(v,8)} = {by above calculation for RDentry(7)} lambda delta . (({(x,l)| (x,l) \in {(n,?),(v,?)}, x == n or x == v} \cup (RDentry(8)(delta : 7) \ {(x,l')|l' in Lab^?_*, x == n or x == v})) \ {(v,l')|l' in Lab^?_*}) \cup {(v,8)} = {by set theory} lambda delta . (({(n,?),(v,?)} \cup (RDentry(8)(delta : 7) \ {(x,l')|l' in Lab^?_*, x == n or x == v})) \ {(v,l')|l' in Lab^?_*}) \cup {(v,8)} = {by set theory can drop the inner v bindings that will be removed} lambda delta . ({(n,?)} \cup (RDentry(8)(delta : 7) \ {(n,l')|l' in Lab^?_*}) \ {(v,l')|l' in Lab^?_*}) \cup {(v,8)} RDexit(5) = {by def.} lambda delta . (({(x,l)| (x, l) \in RDentry(4)(delta), x == n or x == v} \cup (RDentry(5)(delta : 4) \ {(x,l')|l' in Lab^?_*, x == n or x == v})) \ {(v,l')||l' in Lab^?_*}) \cup {(v,5)} = {by set theory can drop the inner uses of v} lambda delta . (({(n,l)| (n, l) \in RDentry(4)(delta)} \cup (RDentry(5)(delta : 4) \ {(n,l')|l' in Lab^?_*})) \ {(v,l')||l' in Lab^?_*}) \cup {(v,5)} = {by def of RDentry(4)} lambda delta . (({(n,l)| (n, l) \in RDexit(2)(delta)} \cup (RDentry(5)(delta : 4) \ {(n,l')|l' in Lab^?_*})) \ {(v,l')||l' in Lab^?_*}) \cup {(v,5)} = {by def of RDexit(2) = RDexit(1) = RDentry(1)} lambda delta . (({(n,l)| (n, l) \in RDentry(1)(delta)} \cup (RDentry(5)(delta : 4) \ {(n,l')|l' in Lab^?_*})) \ {(v,l')||l' in Lab^?_*}) \cup {(v,5)} = {by def of RDentry(5)} lambda delta . (({(n,l)| (n, l) \in RDentry(1)(delta)} \cup (RDexit(6)(delta : 4) \ {(n,l')|l' in Lab^?_*})) \ {(v,l')||l' in Lab^?_*}) \cup {(v,5)} RDexit(4) = {by def.} lambda delta' . (RDentry(4)(pop(delta')) \ {(x,l')|l' in Lab^?_*, x == n or x == v}) \cup {(n,4)} \cup {(v,?)} = {by Var* = {n,v}} lambda delta' . {(n,4)} \cup {(v,?)} = {by set theory} lambda delta' . {(n,4),(v,?)} RDentry(4) = {by def.} lambda delta . RDexit(2)(delta) = {by def. RDexit(2) = RDentry(2) = RDexit(1) = RDentry(1)} lambda delta . RDexit(7)(delta) \cup RDexit(4)(delta) RDexit(6)(delta) = {by def of RDexit(6)} RDentry(6)(delta) = {by def of RDentry(6)} RDexit(3)(delta) \cup RDexit(5)(delta) = {by def of RDexit(3)} ((RDentry(3))(delta) \ { (v,l')|l' in Lab^?_*}) \cup {(v,3)} \cup RDexit(5)(delta) = {by def of RDentry(3) = RDexit(2) = RDentry(2) = RDexit(1) = RDentry(1)} ((RDentry(1))(delta) \ { (v,l')|l' in Lab^?_*}) \cup {(v,3)} \cup RDexit(5)(delta) = {by def of RDentry(1)} ((RDexit(7)(delta) \cup RDexit(4)(delta)) \ { (v,l')|l' in Lab^?_*}) \cup {(v,3)} \cup RDexit(5)(delta) = {by above calculation for RDexit(7)} (({(n,7),(v,?)} \cup RDexit(4)(delta)) \ { (v,l')|l' in Lab^?_*}) \cup {(v,3)} \cup RDexit(5)(delta) = {by set theory, can drop the inner (v,?)} (({(n,7)} \cup RDexit(4)(delta)) \ { (v,l')|l' in Lab^?_*}) \cup {(v,3)} \cup RDexit(5)(delta) = {by Var* = {n,v} and set theory} {(n,7),(v,3)} \cup {(n,l) | (n,l) \in RDexit(4)(delta)} \cup RDexit(5)(delta) = {by above calculation for RDexit(5)} {(n,7),(v,3)} \cup {(n,l) | (n,l) \in RDexit(4)(delta)} \cup (({(n,l)| (n, l) \in RDentry(1)(delta)} \cup (RDexit(6)(delta : 4) \ {(n,l')|l' in Lab^?_*})) \ {(v,l')||l' in Lab^?_*}) \cup {(v,5)} = {by set theory} {(n,7),(v,3),(v,5)} \cup {(n,l) | (n,l) \in RDexit(4)(delta)} \cup (({(n,l)| (n, l) \in RDentry(1)(delta)} \cup (RDexit(6)(delta : 4) \ {(n,l')|l' in Lab^?_*})) \ {(v,l')||l' in Lab^?_*}) = {by Var* = {n,v}, so the call to RDexit(6) can't affect the result} {(n,7),(v,3),(v,5)} \cup {(n,l) | (n,l) \in RDexit(4)(delta)} \cup ({(n,l) | (n,l) \in RDentry(1)(delta)} \ {(v,l')||l' in Lab^?_*}) = {by set theory} {(n,7),(v,3),(v,5)} \cup {(n,l) | (n,l) \in RDexit(4)(delta)} \cup {(n,l) | (n,l) \in RDentry(1)(delta)} = {by above calculation for RDexit(4)} {(n,7),(v,3),(v,5)} \cup {(n,l) | (n,l) \in {(n,4)} \cup {(v,?)}} \cup {(n,l) | (n,l) \in RDentry(1)(delta)} = {by set theory} {(n,7),(n,4),(v,3),(v,5)} \cup {(n,l) | (n,l) \in RDentry(1)(delta)} = {by def. of RDentry(1)} {(n,7),(n,4),(v,3),(v,5)} \cup {(n,l) | (n,l) \in RDexit(7)(delta) \cup RDexit(4)(delta)} = {by above calculation for RDexit(7)} {(n,7),(n,4),(v,3),(v,5)} \cup {(n,l) | (n,l) \in {(n,7),(v,?)} \cup RDexit(4)(delta)} = {by above calculation for RDexit(4)} {(n,7),(n,4),(v,3),(v,5)} \cup {(n,l) | (n,l) \in {(n,7),(v,?)} \cup {(n,4),(v,?)}} = {by set theory} {(n,7),(n,4),(v,3),(v,5)} {(n,7),(n,4)} = {by set theory} {(n,4),(n,7),(v,3),(v,5)} So we have (by def.): RDentry(7)([]) = {(n,?),(v,?)} RDexit(7)([]) = {(n,7),(v,?)} RDentry(8)([7]) = {by def.} RDexit(6)([7]) = {by above calculation for RDexit(6)} {(n,4),(n,7),(v,3),(v,5)} RDexit(8)([]) = {by above calculation for RDexit(8)} ({(n,?)} \cup (RDentry(8)([7]) \ {(n,l')|l' in Lab^?_*}) \ {(v,l')|l' in Lab^?_*}) \cup {(v,8)} = {by above calculation for RDentry(8)([7]) ({(n,?)} \cup ({(n,4),(n,7),(v,3),(v,5)} \ {(n,l')|l' in Lab^?_*}) \ {(v,l')|l' in Lab^?_*}) \cup {(v,8)} = {by set theory} ({(n,?)} \cup {(v,3),(v,5)}} \ {(v,l')|l' in Lab^?_*}) \cup {(v,8)} = {by set theory} {(n,?)} \cup {(v,8)} = {by set theory} {(n,?),(v,8)} *** call strings of bounded length ------------------------------------------ CALL STRINGS OF BOUNDED LENGTH Only record the last k calls \Delta = Lab^{<= k} Truncation from left truncate([l_1, ..., l_n], k) = if n <= k then [l_1, ..., l_n] else [l_{n-k+1}, ..., l_n] e.g, truncate([1, 2, 3, 4], 3) = [2, 3, 4] ------------------------------------------ Q: What is the extremal value now? \hat{i}(delta) = if delta = [] then i else \bot Q: What happens if k = 0? it's context insensitive. Q: How would the transfer functions look? \hat{f^1_lc}(Y)(delta') = \bigsqcup { f^1_lc(Y(delta)) | delta' = truncate(delta : lc, k) } Why use \bigsqcup, because delta isn't always determined by the above formula Here f^1_lc is a function. \hat{f^2_{lc,lr}}(X, Y)(delta) = f^2_{lc,lr}(X(delta), Y(truncate(delta:lc, k))) Q: How would this look in the reaching definitions analysis? See example 2.40 *** assumption sets (2.5.5) (briefly, if short on time) idea: make the context be (a string of) analysis information; that is, what's known at the point of the call **** large assumption sets To simplify the discussion assume that the property space contains sets: ------------------------------------------ ASSUMPTION SETS Simplifying assumption: for some set D, L = Powerset(D) Idea: let context be analysis information at call site E.g., (like k = 1): \Delta = L = Powerset(D) so \hat{L} is isomorphic to: Powerset(Powerset(D) x D) and the extremal value is \hat{i} = {({i},i)} ------------------------------------------ for the embellished property space, \hat{L} = Powerset(D) -> L = Powerset(D) -> Powerset(D) However, they use an isomorphic definition \hat{L} = Powerset(Powerset(D) x D) ------------------------------------------ REACHING DEFINITIONS WITH ASSUMPTION SETS \hat{f^1_lc}(Z) = \bigcup { {delta'} x phi^1_lc(X) | (delta, X) \in Z, delta' = {Y | (delta,Y) \in Z} } where phi^1_lc(X) = (X \ {(x,l') | l' in Lab^?_*, x \in val-formals(lc) or x in res-formals(lc)}) \cup {(x,lc) | x \in val-formals(lc)} \cup {(y,?) | y \in res-formals(lc)} \hat{f^2_{lc,lr}}(Z, Z') = \bigcup { {delta} x phi^2_{lc,lr}(X,X') | (delta, X) \in Z, (delta', X') \in Z', delta' = {Y | (delta,Y) \in Z} } where phi^2_{lc,lr}(X,X') = (({(x,l)| (x, l) \in X, x \in val-formals(lr) or x \in res-formals(lr)} \cup (X' \ {(x,l')| l' in Lab^?_*, x in val-formals(lr) or y in res-formals(lr)})) \ {(z,l') | z in res-actuals(lr)}) \cup {(z,lr) | z in res-actuals(lr)} ------------------------------------------ Note the cross product in these definitions Q: How are the contexts manipulated? for \hat{f^1_lc}(Z): (delta,X) in Z is a possible context and analysis info for the current call To take call into account, use the states in which the call could happen: delta' = {Y | (delta,Y) \in Z} for \hat{f^2_{lc,lr}}(Z, Z'): (delta,X) in Z is a possible context and analysis info for the entry to the current call, (delta',X') in Z' is a possible context and analysis info for the end of the current call The condition delta' = {Y | (delta,Y) \in Z} selects matching calls Q: How do these compare with the call strings case? See the phi functions. **** smaller assumption sets ------------------------------------------ SMALLER ASSUMPTION SETS like k = 0: \Delta = D so \hat{L} is isomorphic to: Powerset(D x D) and the extremal value is \hat{i} = {(i,i)} Results: - loss of precision + size of data flow properties reduced ------------------------------------------ ** flow insensitive analyses (2.5.6) ------------------------------------------ FLOW INSENSITIVE ANALYSIS (2.5.6) Flow sensitive: order of execution matters A_o(S1;S2) <> A_o(S2;S1) Flow insensitive: order of execution ignored A_o(S1;S2) = A_o(S2;S1) Results: - much less precise analysis + much cheaper ------------------------------------------ Q: Don't we always want precise results? No, not it takes forever. Especially important for interprocedural analyses Q: Is type checking flow sensitive? No *** may-loop analysis ------------------------------------------ EXAMPLE: MAY-LOOP ANALYSIS Goal: Determine whether a procedure may loop when called. IML(p) built on ML(S) and CP(S) ML analysis L = Boolean ML([x := a]^l) = false ML([skip]^l) = false ML(while [b]^l do S) = true ML(S1; S2) = ML(S1) or ML(S2) ML(if [b]^l then S1 else S2) = ML(S1) or ML(S2) ML([call p(a,z)]^lc_lr) = false CP analysis L = Powerset(ProcName) CP([x := a]^l) = {} CP([skip]^l) = {} CP(while [b]^l do S) = {} CP(S1; S2) = CP(S1) \cup CP(S2) CP(if [b]^l then S1 else S2) = CP(S1) \cup CP(S2) CP([call p(a,z)]^lc_lr) = {p} IML analysis L = Boolean IML(p) = ------------------------------------------ We use "or" instead of \cup on the booleans. Q: If this is a may analysis, why aren't we using union? because union isn't defined on the Booleans Q: How to write IML(p) with this? IML(p) = ML(S) or (\exists p' \in CP(S) :: IML(p'))? where proc p(val x, res y) is^ln S end^lx is in D* But ... Q: How to stop recursive calls? Order the procedures, and don't allow procedures to call procedures later in the list of declarations IML(p) = ML(S) or (\exists p' \in CP(S) :: IML(p') or p' = p or p' appears after p in D*) where proc p(val x, res y) is^ln S end^lx is in D* (assigned variables is an example in the book). Q: How does this compare to a type system? ------------------------------------------ EXAMPLE begin proc inc(val x, res y) is y := x+1 end proc add5(val z, res w) is w := z while w < z + 5 do call inc(w, w); end; call add5(7, v) end IML(inc) = false or (\exists p' \in CP(inc) :: IML(p') or p' = p or p' appears after p in D*) = false IML(add5) = true or (\exists p' \in CP(add5) :: IML(p') or p' = p or p' appears after p in D*) = true ------------------------------------------ In general, there is a equation system to solve, see for example example 2.42 in the book