Finish the projection migration for reaching definitions by replacing the accumulator-style runOfTrace*From definitions and their hand-rolled re-association lemmas with a single analysis-agnostic projection: Trace.steps / Traceₗ.steps, the chronological List of executed (index, statement) pairs. Its four simp lemmas are one-line inductions, with all re-association falling out of mathlib's List.append_assoc and List.reverse_append. Run is now an abbrev for List (State × BasicStmt) (latest-first, so LastAssign keeps its first-match structure) and runOfTrace is just steps.reverse. Also hoist the generic reaches_final_post into Forward.lean, letting analyze_correct' be stated directly about S.Post (prog.trace hrun). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
136 lines
5.1 KiB
Lean4
136 lines
5.1 KiB
Lean4
import Spa.Analysis.Forward
|
||
import Spa.Lattice.Finset
|
||
import Spa.Language.Tagged.Graphs
|
||
import Spa.Showable
|
||
|
||
namespace Spa
|
||
|
||
open Forward
|
||
|
||
instance {n : ℕ} : Showable (Finset (Fin n)) :=
|
||
⟨fun s =>
|
||
"{" ++ (List.finRange n).foldr
|
||
(fun i rest => if i ∈ s then show' i ++ ", " ++ rest else rest) ""
|
||
++ "}"⟩
|
||
|
||
abbrev DefSet (prog : Program) : Type := Finset prog.NodeId
|
||
|
||
namespace ReachingAnalysis
|
||
|
||
variable (prog : Program)
|
||
|
||
def genSet (s : prog.State) : DefSet prog := (prog.nodeIdOf s).elim {} (fun x => {x})
|
||
|
||
def eval (s : prog.State) (vs : VariableValues (DefSet prog) prog) : VariableValues (DefSet prog) prog :=
|
||
match prog.code s with
|
||
| none => vs
|
||
| some bs =>
|
||
match bs with
|
||
| .assign k _ => FiniteMap.generalizedUpdate id (fun _ _ => genSet prog s) [k] vs
|
||
| .noop => vs
|
||
|
||
lemma eval_mono (s : prog.State) :
|
||
Monotone (eval prog s) := by
|
||
intros vs₁ vs₂ hle
|
||
unfold eval; split <;> try simpa
|
||
split <;> try simpa
|
||
apply FiniteMap.generalizedUpdate_monotone monotone_id (fun _ => monotone_const)
|
||
assumption
|
||
|
||
instance stmtEvaluator : StmtEvaluator (DefSet prog) prog :=
|
||
⟨eval prog, eval_mono prog⟩
|
||
|
||
def output : String :=
|
||
show' (result (DefSet prog) prog)
|
||
|
||
/-- The statements a trace executed, paired with the state each executed at,
|
||
most recent first (matching `LastAssign`, which scans for the most recent
|
||
assignment). This is `Trace.steps` (chronological) reversed, so facts about
|
||
concatenating traces reduce to mathlib's `List.append`/`List.reverse` lemmas. -/
|
||
abbrev Run (prog : Program) : Type := List (prog.State × BasicStmt)
|
||
|
||
@[aesop unsafe cases]
|
||
inductive LastAssign (prog : Program) (x : String) : Run prog → prog.NodeId → Prop
|
||
| here (s : prog.State) (e : Expr) (hc : prog.code s = some (.assign x e))
|
||
(rest : Run prog) :
|
||
LastAssign prog x ((s, .assign x e) :: rest) (prog.nodeIdOfNonempty s hc)
|
||
| there (s : prog.State) (bs : BasicStmt) (hc : prog.code s = some bs)
|
||
(rest : Run prog) {n : prog.NodeId} :
|
||
(∀ e, bs ≠ .assign x e) → LastAssign prog x rest n →
|
||
LastAssign prog x ((s, bs) :: rest) n
|
||
|
||
def runOfTraceₗ {s₁ s₂ : prog.State} {ρ₁ ρ₂ : Env}
|
||
(tr : Traceₗ prog.cfg s₁ s₂ ρ₁ ρ₂) : Run prog :=
|
||
tr.steps.reverse
|
||
|
||
def runOfTrace {s₁ s₂ : prog.State} {ρ₁ ρ₂ : Env}
|
||
(tr : Trace prog.cfg s₁ s₂ ρ₁ ρ₂) : Run prog :=
|
||
tr.steps.reverse
|
||
|
||
instance stateInterp : StateInterpretation (DefSet prog) prog where
|
||
Proj := Run prog
|
||
Pre := @runOfTraceₗ prog
|
||
Post := @runOfTrace prog
|
||
|
||
interp vs run := ∀ (x : String) (assigners : DefSet prog), (x, assigners) ∈ vs →
|
||
∀ (n : prog.NodeId), LastAssign prog x run n → n ∈ assigners
|
||
interp_sup := by
|
||
intro vs₁ vs₂ run h x assigners hmem n hla
|
||
obtain ⟨a₁, a₂, rfl, h₁, h₂⟩ := FiniteMap.mem_sup hmem
|
||
aesop (add simp Finset.mem_union)
|
||
interp_inf := by
|
||
intro vs₁ vs₂ run h x assigners hmem n hla
|
||
obtain ⟨a₁, a₂, rfl, h₁, h₂⟩ := FiniteMap.mem_inf hmem
|
||
aesop (add simp Finset.mem_inter)
|
||
|
||
post_pre := by
|
||
intro vs s₁ s₂ s₃ ρ₁ ρ₂ tr hedge hvs
|
||
simpa [runOfTrace, runOfTraceₗ] using hvs
|
||
|
||
private lemma valid_step (s : prog.State) {ρ₁ ρ₂ : Env}
|
||
{obs : Option BasicStmt} (hcode : prog.code s = obs)
|
||
(hbs : EvalBasicStmtOpt ρ₁ obs ρ₂)
|
||
{vs : VariableValues (DefSet prog) prog} {run : Run prog}
|
||
(hvs : ⟦vs⟧ run) :
|
||
⟦eval prog s vs⟧ ((hbs.steps s).reverse ++ run) := by
|
||
cases hbs with
|
||
| none => simpa [eval, hcode, EvalBasicStmtOpt.steps] using hvs
|
||
| some hbs =>
|
||
cases hbs with
|
||
| noop =>
|
||
simp [eval, hcode, EvalBasicStmtOpt.steps]
|
||
intro x assigners hmem n hla; aesop
|
||
| assign x e v hev =>
|
||
simp [eval, hcode, EvalBasicStmtOpt.steps]; intro k assigners hmem n hla
|
||
by_cases hx : k = x
|
||
· subst hx
|
||
have hd := FiniteMap.generalizedUpdate_mem_eq (List.mem_singleton.mpr rfl) hmem
|
||
rcases hla
|
||
<;> simp [Program.nodeIdOfNonempty, hd, genSet, Option.get] <;> aesop
|
||
· have hmem' := FiniteMap.generalizedUpdate_not_mem_backward
|
||
(fun hc => hx (List.mem_singleton.mp hc)) hmem
|
||
aesop
|
||
|
||
instance validStateEvaluator : ValidStateEvaluator (DefSet prog) prog where
|
||
valid := by
|
||
intro s₁ s₂ ρ₁ ρ₂ ρ₃ vs tr hbs hvs
|
||
show ⟦eval prog s₂ vs⟧ (runOfTrace prog (tr ++ hbs))
|
||
simpa [runOfTrace, runOfTraceₗ] using valid_step prog s₂ rfl hbs hvs
|
||
botV_init := by intro x assigners _ n hla; cases hla
|
||
|
||
theorem analyze_correct {ρ : Env} (hrun : EvalStmt [] prog.rootStmt ρ) :
|
||
⟦ variablesAt prog.finalState (result (DefSet prog) prog) ⟧
|
||
(runOfTrace prog (prog.trace hrun)) :=
|
||
Forward.analyze_correct' (DefSet prog) prog hrun
|
||
|
||
theorem analyze_correct_at {ρf : Env} (hrun : EvalStmt [] prog.rootStmt ρf)
|
||
{s : prog.State} {ρin ρout : Env}
|
||
(hr : Reaches (prog.trace hrun) s ρin ρout) :
|
||
⟦ joinForKey s (result (DefSet prog) prog) ⟧ (runOfTraceₗ prog hr.pre)
|
||
∧ ⟦ variablesAt s (result (DefSet prog) prog) ⟧ (runOfTrace prog hr.post) :=
|
||
Forward.analyze_correct_at (DefSet prog) prog hrun hr
|
||
|
||
end ReachingAnalysis
|
||
|
||
end Spa
|