III. Structural Operational Semantics (2.2.1) 3. semantics of the while language (2.2.1) ------------------------------------------ TRANSITIONS (Table 2.6) [asg] ([x := a]^l, s) --> s[x |-> A[[a]]s] [skip] ([skip]^l, s) --> s (S1, s) --> (S1', s') [seq1] -------------------------- (S1;S2, s) --> (S1';S2, s') (S1, s) --> s' [seq2] -------------------------- (S1;S2, s) --> (S2, s') [if1] (if [b]^l then S1 else S2, s) --> (S1, s) if B[[b]]s = true [if2] (if [b]^l then S1 else S2, s) --> (S2, s) if B[[b]]s = false [wh1] (while [b]^l do S, s) --> (S; while [b]^l do S, s) if B[[b]]s = true [wh2] (while [b]^l do S, s) --> s if B[[b]]s = false ------------------------------------------ What do the sequence rules do? Do the rules allow evaluation of the true or false part of an if-statement before evaluating the condition? How do the wh1 and wh2 rules work? ------------------------------------------ EXAMPLE Program [q := 0]^1; [r := x]^2; while [r >= y]^3 do ([r := r-y]^4; [q := q+1]^5) Name each statement S_i if it has label i Let S_123 be the whole program (a sequence) Let S_3 be the while loop Let S_23 be the sequential composition of S_2 and S_3 Let S_45 be the body of the while loop Let sqrxy be the state in which q has value q, ..., y has value y E.g, s7062(q) = 7, s0062(x) = 6. (S_123, s7062) --> {by [seq2]} * ([q := 0]^1, s7062) --> {by [asg]} s0062 . (S_23, s0062) --> {by [seq2]} * <[r := x]^2, s0062) --> {by [asg]} s0662 . (S_3, s0662) --> {by [wh1], since B[[r >= y]](s0662) = true} (S_45; S_3, s0662) --> {by [seq1]} * (S_45, s0662) --> {by [seq2]} * <[r := r-y]^4, s0662) --> {by [asg]} s0462 . (S_5, s0462) . (S_5; S_3, s0462) --> ------------------------------------------ How are the labels used in the semantics? What would be the rule(s) for a one-armed if-statement? How would you add rule(s) for a for loop of the form for x := a1..a2 do S ? What would be the rule(s) for a exitblock statement, where blocks have the syntax begin S end and the exitblock statement has the syntax exitblock ? What would need to be done to have a semantics of assert statements with syntax: assert [b]^l ? What would need to be done to have a semantics of case statements with syntax case e of left(x) then S1 [] right(y) of S2 end that observes a tagged union expression (e) ? 4. properties of WHILE's semantics As the configurations evolve, does the CFG for the statement in the configuration change? If so, how? ------------------------------------------ PROPERTIES OF WHILE's SEMANTICS How does the flow graph change as the configurations change? Case 1: (S,s) --> (S',s') Compare vs. final(S) final(S') flow(S) flow(S') blocks(S) blocks(S') Case 2: (S,s) --> s' what can we say about the graph of S? ------------------------------------------ IV. Correctness of the Live Variables Analysis (2.2.2) A. failed attempt 1. characterizing the dataflow equations as generators ------------------------------------------ EQUATION SYSTEM LV^=(S*) defined by: LVexit(l) = if l \in final(S*) then {} else \bigcup { LVentry(l') | (l', l) \in flow^R(S*) } LVentry(l) = (LVexit(l) \ killLV(B^l)) \cup genLV(B^l) where B^l \in blocks(S*) Functional form, for a given S*: F^S*_LV: ({entry,exit} -> Lab* -> P(Var*)) -> ({entry,exit} -> Lab* -> P(Var*)) F^S*_LV(F)(exit)(l) = if l \in final(S*) then {} else \bigcup { F(entry)(l') | (l', l) \in flow^R(S*) } F^S*_LV(F)(entry)(l) = (F(exit)(l) - killLV(B^l)) \cup genLV(B^l) where B^l \in blocks(S*) Solutions: live: {entry,exit} -> Lab* -> P(Var*) def: live solves LV^=(S*), written live |= LV^=(S*), iff live is a fixpoint of F^S*_LV. ------------------------------------------ Why use functions instead of tuples for solutions? ------------------------------------------ EXAMPLE Q: What is F^S*_LV for: [x := 3]^1; [y := x+2]^2; [y := y+1]^3 ------------------------------------------ What do we want to prove for correctness? 2. Specification of Live Variables Analysis ------------------------------------------ DEFINING LIVE VARIABLES SEMANTICALLY What should a solution to LV mean? At a given point, only the live variables matter. def: States s1 and s2 are *similar with respect to a set of variables V *, written s1 ~_V s2, iff (\forall x \in V :: s1(x) = s2(x)). def: functional liv: {entry,exit} -> Lab -> State *solves a set LV^=(S)* iff for all d in {entry,exit}, l in Lab: if V = liv(d)(l), then liv(d)(l) satisfies LV^=(S)(d)(l) Conjecture: Let S be label consistent. Suppose live |= LV^=(S) and s1 ~_{live(entry)(init(S))} s2. Then: (1) if (S, s1) --> (S', s1'), then there is some s2' such that (S, s2) --> (S', s2') and s1' ~_{live(entry)(init(S'))} s2'. (2) if (S, s1) --> s1' then there is some s2' such that (S, s2) --> s2' and s1' ~_{live(exit)(init(S))} s2'. ------------------------------------------ What happens to ~_V as V shrinks? a. failed proof How would we prove the theorem? B. fix using constraint system 1. Constraint characterization of live variables ------------------------------------------ CONSTRAINT SYSTEM LV^{\subseteq}(S*) defined by: LVexit(l) \supseteq if l \in final(S*) then {} else \bigcup { LVentry(l') | (l', l) \in flow^R(S*) } LVentry(l) \supseteq (LVexit(l) \ killLV(B^l)) \cup genLV(B^l) where B^l \in blocks(S*) Functional form, for a given S*, as above F^S*_LV: ({entry,exit} -> Lab* -> P(Var*)) -> ({entry,exit} -> Lab* -> P(Var*)) F^S*_LV(F)(exit)(l) = if l \in final(S*) then {} else \bigcup { F(entry)(l') | (l', l) \in flow^R(S*) } F^S*_LV(F)(entry)(l) = (F(exit)(l) \ killLV(B^l)) \cup genLV(B^l) where B^l \in blocks(S*) ------------------------------------------ 2. strengthening the induction ------------------------------------------ Solutions: live: {entry,exit} -> Lab* -> P(Var*) def: live solves LV^{\subseteq}(S*), written live |= LV^{\subseteq}(S*), iff live \sqsupseteq F^S*_LV(live). def: lv1 \sqsupseteq lv2 iff (\forall e \in {entry,exit} :: (\forall l in Lab* :: lv1(e)(l) \supseteq lv2(e)(l))) Lemma 2.15: If S* is label consistent, then live |= LV^=(S*) implies live |= LV^{\subseteq}(S*). Furthermore, the least solutions coincide. ------------------------------------------ What lemma would give us the property where we got stuck? ------------------------------------------ SOLUTIONS WORK FOR SUBSTATEMENTS Lemma 2.16: Suppose S1 is label consistent, and live |= LV^{\subseteq}(S1). If flow(S1) \supseteq flow(S2) and blocks(S1) \supseteq blocks(S2), then S2 is label consistent and live |= LV^{\subseteq}(S2). ------------------------------------------ Why is this true? ------------------------------------------ SOLUTIONS PRESERVED Corollary 2.17: Suppose S is label consistent, and live |= LV^{\subseteq}(S). If (S,s) --> (S',s'), then live |= LV^{\subseteq}(S'). ------------------------------------------ Why does that follow? ------------------------------------------ SOLUTION CAN ONLY SHRINK FORWARD Lemma 2.18: Suppose S is label consistent, and live |= LV^{\subseteq}(S). Then for all (l,l') \in flow(S), live(exit)(l) \supseteq live(entry)(l'). Proof: construction of LV^{\subseteq}(S). ------------------------------------------ 3. A proper proof ------------------------------------------ GETTING CORRECTNESS RIGHT Theorem 2.21: Let S be label consistent. Suppose live |= LV^{\subseteq}(S) and s1 ~_{live(entry)(init(S))} s2. Then: (1) if (S, s1) --> (S', s1'), then there is some s2' such that (S, s2) --> (S', s2') and s1' ~_{live(entry)(init(S'))} s2'. (2) if (S, s1) --> s1' then there is some s2' such that (S, s2) --> s2' and s1' ~_{live(exit)(init(S))} s2'. ------------------------------------------ What does this theorem tell us about execution sequences?