/- Copyright (c) 2019 Microsoft Corporation. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Leonardo de Moura -/ prelude import init.lean.compiler.ir.basic import init.lean.compiler.ir.freevars import init.control.reader import init.control.conditional namespace Lean namespace IR /- Remark: in the paper "Counting Immutable Beans" the concepts of free and live variables coincide because the paper does *not* consider join points. For example, consider the function body `B` ``` let x := ctor_0; jmp block_1 x ``` in a context where we have the join point `block_1` defined as ``` block_1 (x : obj) : obj := let z := ctor_0 x y; ret z `` The variable `y` is live in the function body `B` since it occurs in `block_1` which is "invoked" by `B`. -/ namespace IsLive /- We use `State Context` instead of `ReaderT Context Id` because we remove non local joint points from `Context` whenever we visit them instead of maintaining a set of visited non local join points. Remark: we don't need to track local join points because we assume there is no variable or join point shadowing in our IR. -/ abbrev M := State LocalContext @[inline] def visitVar (w : Index) (x : VarId) : M Bool := pure (HasIndex.visitVar w x) @[inline] def visitJP (w : Index) (x : JoinPointId) : M Bool := pure (HasIndex.visitJP w x) @[inline] def visitArg (w : Index) (a : Arg) : M Bool := pure (HasIndex.visitArg w a) @[inline] def visitArgs (w : Index) (as : Array Arg) : M Bool := pure (HasIndex.visitArgs w as) @[inline] def visitExpr (w : Index) (e : Expr) : M Bool := pure (HasIndex.visitExpr w e) partial def visitFnBody (w : Index) : FnBody → M Bool | (FnBody.vdecl x _ v b) := visitExpr w v <||> visitFnBody b | (FnBody.jdecl j ys v b) := visitFnBody v <||> visitFnBody b | (FnBody.set x _ y b) := visitVar w x <||> visitArg w y <||> visitFnBody b | (FnBody.uset x _ y b) := visitVar w x <||> visitVar w y <||> visitFnBody b | (FnBody.sset x _ _ y _ b) := visitVar w x <||> visitVar w y <||> visitFnBody b | (FnBody.setTag x _ b) := visitVar w x <||> visitFnBody b | (FnBody.inc x _ _ b) := visitVar w x <||> visitFnBody b | (FnBody.dec x _ _ b) := visitVar w x <||> visitFnBody b | (FnBody.del x b) := visitVar w x <||> visitFnBody b | (FnBody.mdata _ b) := visitFnBody b | (FnBody.jmp j ys) := visitArgs w ys <||> do { ctx ← get; match ctx.getJPBody j with | some b => -- `j` is not a local join point since we assume we cannot shadow join point declarations. -- Instead of marking the join points that we have already been visited, we permanently remove `j` from the context. set (ctx.eraseJoinPointDecl j) *> visitFnBody b | none => -- `j` must be a local join point. So do nothing since we have already visite its body. pure false } | (FnBody.ret x) := visitArg w x | (FnBody.case _ x alts) := visitVar w x <||> alts.anyM (fun alt => visitFnBody alt.body) | (FnBody.unreachable) := pure false end IsLive /- Return true if `x` is live in the function body `b` in the context `ctx`. Remark: the context only needs to contain all (free) join point declarations. Recall that we say that a join point `j` is free in `b` if `b` contains `FnBody.jmp j ys` and `j` is not local. -/ def FnBody.hasLiveVar (b : FnBody) (ctx : LocalContext) (x : VarId) : Bool := (IsLive.visitFnBody x.idx b).run' ctx abbrev LiveVarSet := VarIdSet abbrev JPLiveVarMap := RBMap JoinPointId LiveVarSet (fun j₁ j₂ => j₁.idx < j₂.idx) instance LiveVarSet.inhabited : Inhabited LiveVarSet := ⟨{}⟩ def mkLiveVarSet (x : VarId) : LiveVarSet := RBTree.empty.insert x namespace LiveVars abbrev Collector := LiveVarSet → LiveVarSet @[inline] private def skip : Collector := fun s => s @[inline] private def collectVar (x : VarId) : Collector := fun s => s.insert x private def collectArg : Arg → Collector | (Arg.var x) := collectVar x | irrelevant := skip @[specialize] private def collectArray {α : Type} (as : Array α) (f : α → Collector) : Collector := fun s => as.foldl (fun s a => f a s) s private def collectArgs (as : Array Arg) : Collector := collectArray as collectArg private def accumulate (s' : LiveVarSet) : Collector := fun s => s'.fold (fun s x => s.insert x) s private def collectJP (m : JPLiveVarMap) (j : JoinPointId) : Collector := match m.find j with | some xs => accumulate xs | none => skip -- unreachable for well-formed code private def bindVar (x : VarId) : Collector := fun s => s.erase x private def bindParams (ps : Array Param) : Collector := fun s => ps.foldl (fun s p => s.erase p.x) s def collectExpr : Expr → Collector | (Expr.ctor _ ys) := collectArgs ys | (Expr.reset _ x) := collectVar x | (Expr.reuse x _ _ ys) := collectVar x ∘ collectArgs ys | (Expr.proj _ x) := collectVar x | (Expr.uproj _ x) := collectVar x | (Expr.sproj _ _ x) := collectVar x | (Expr.fap _ ys) := collectArgs ys | (Expr.pap _ ys) := collectArgs ys | (Expr.ap x ys) := collectVar x ∘ collectArgs ys | (Expr.box _ x) := collectVar x | (Expr.unbox x) := collectVar x | (Expr.lit v) := skip | (Expr.isShared x) := collectVar x | (Expr.isTaggedPtr x) := collectVar x partial def collectFnBody : FnBody → JPLiveVarMap → Collector | (FnBody.vdecl x _ v b) m := collectExpr v ∘ collectFnBody b m ∘ bindVar x | (FnBody.jdecl j ys v b) m := let jLiveVars := (collectFnBody v m ∘ bindParams ys) {}; let m := m.insert j jLiveVars; collectFnBody b m | (FnBody.set x _ y b) m := collectVar x ∘ collectArg y ∘ collectFnBody b m | (FnBody.setTag x _ b) m := collectVar x ∘ collectFnBody b m | (FnBody.uset x _ y b) m := collectVar x ∘ collectVar y ∘ collectFnBody b m | (FnBody.sset x _ _ y _ b) m := collectVar x ∘ collectVar y ∘ collectFnBody b m | (FnBody.inc x _ _ b) m := collectVar x ∘ collectFnBody b m | (FnBody.dec x _ _ b) m := collectVar x ∘ collectFnBody b m | (FnBody.del x b) m := collectVar x ∘ collectFnBody b m | (FnBody.mdata _ b) m := collectFnBody b m | (FnBody.ret x) m := collectArg x | (FnBody.case _ x alts) m := collectVar x ∘ collectArray alts (fun alt => collectFnBody alt.body m) | (FnBody.unreachable) m := skip | (FnBody.jmp j xs) m := collectJP m j ∘ collectArgs xs def updateJPLiveVarMap (j : JoinPointId) (ys : Array Param) (v : FnBody) (m : JPLiveVarMap) : JPLiveVarMap := let jLiveVars := (collectFnBody v m ∘ bindParams ys) {}; m.insert j jLiveVars end LiveVars def updateLiveVars (e : Expr) (v : LiveVarSet) : LiveVarSet := LiveVars.collectExpr e v def collectLiveVars (b : FnBody) (m : JPLiveVarMap) (v : LiveVarSet := {}) : LiveVarSet := LiveVars.collectFnBody b m v export LiveVars (updateJPLiveVarMap) end IR end Lean