Add files using upload-large-folder tool
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .gitattributes +2 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/Boxing.lean +346 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/Checker.lean +192 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/CompilerM.lean +239 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/ElimDeadBranches.lean +351 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/ElimDeadVars.lean +48 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/EmitC.lean +775 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/EmitLLVM.lean +1657 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/EmitUtil.lean +86 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/ExpandResetReuse.lean +281 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/Format.lean +143 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/FreeVars.lean +233 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/LLVMBindings.lean +470 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/LiveVars.lean +170 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/NormIds.lean +177 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/PushProj.lean +57 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/RC.lean +285 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/ResetReuse.lean +246 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/SimpCase.lean +78 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/Sorry.lean +80 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/ToIR.lean +350 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/ToIRType.lean +126 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/UnboxResult.lean +30 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/AlphaEqv.lean +137 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/AuxDeclCache.lean +29 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/BaseTypes.lean +29 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/Basic.lean +755 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/Bind.lean +132 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/CSE.lean +128 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/Check.lean +307 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/Closure.lean +172 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/CompatibleTypes.lean +146 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/CompilerM.lean +504 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/ConfigOptions.lean +67 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/DeclHash.lean +44 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/DependsOn.lean +62 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/ElimDead.lean +100 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/ElimDeadBranches.lean +630 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/ExtractClosed.lean +161 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/FVarUtil.lean +213 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/FixedParams.lean +181 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/FloatLetIn.lean +310 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/InferType.lean +348 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/Internalize.lean +131 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/JoinPoints.lean +636 -0
- backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/LambdaLifting.lean +190 -0
- external/alphageometry/.venv-ag/Lib/site-packages/fontTools/subset/__init__.py +0 -0
- external/alphageometry/.venv-ag/Lib/site-packages/fontTools/subset/__main__.py +6 -0
- external/alphageometry/.venv-ag/Lib/site-packages/fontTools/subset/cff.py +184 -0
- external/alphageometry/.venv-ag/Lib/site-packages/fontTools/subset/svg.py +253 -0
.gitattributes
CHANGED
|
@@ -4387,3 +4387,5 @@ backend/core/leanprover--lean4---v4.22.0/lib/lean/Std/Tactic/BVDecide/Bitblast/B
|
|
| 4387 |
backend/core/leanprover--lean4---v4.22.0/lib/lean/Std/Tactic/BVDecide/Reflect.olean filter=lfs diff=lfs merge=lfs -text
|
| 4388 |
backend/core/leanprover--lean4---v4.22.0/lib/lean/Std/Tactic/BVDecide/LRAT/Internal/Convert.olean filter=lfs diff=lfs merge=lfs -text
|
| 4389 |
backend/core/leanprover--lean4---v4.22.0/lib/lean/Std/Data/HashSet/Raw.olean filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
| 4387 |
backend/core/leanprover--lean4---v4.22.0/lib/lean/Std/Tactic/BVDecide/Reflect.olean filter=lfs diff=lfs merge=lfs -text
|
| 4388 |
backend/core/leanprover--lean4---v4.22.0/lib/lean/Std/Tactic/BVDecide/LRAT/Internal/Convert.olean filter=lfs diff=lfs merge=lfs -text
|
| 4389 |
backend/core/leanprover--lean4---v4.22.0/lib/lean/Std/Data/HashSet/Raw.olean filter=lfs diff=lfs merge=lfs -text
|
| 4390 |
+
external/alphageometry/.venv-ag/Lib/site-packages/numpy.libs/libscipy_openblas64_-13e2df515630b4a41f92893938845698.dll filter=lfs diff=lfs merge=lfs -text
|
| 4391 |
+
external/alphageometry/.venv-ag/Lib/site-packages/numpy.libs/msvcp140-263139962577ecda4cd9469ca360a746.dll filter=lfs diff=lfs merge=lfs -text
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/Boxing.lean
ADDED
|
@@ -0,0 +1,346 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2019 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Runtime
|
| 8 |
+
import Lean.Compiler.ClosedTermCache
|
| 9 |
+
import Lean.Compiler.ExternAttr
|
| 10 |
+
import Lean.Compiler.IR.Basic
|
| 11 |
+
import Lean.Compiler.IR.CompilerM
|
| 12 |
+
import Lean.Compiler.IR.FreeVars
|
| 13 |
+
import Lean.Compiler.IR.ElimDeadVars
|
| 14 |
+
import Lean.Data.AssocList
|
| 15 |
+
|
| 16 |
+
namespace Lean.IR.ExplicitBoxing
|
| 17 |
+
/-!
|
| 18 |
+
Add explicit boxing and unboxing instructions.
|
| 19 |
+
Recall that the Lean to λ_pure compiler produces code without these instructions.
|
| 20 |
+
|
| 21 |
+
Assumptions:
|
| 22 |
+
- This transformation is applied before explicit RC instructions (`inc`, `dec`) are inserted.
|
| 23 |
+
- This transformation is applied before `FnBody.case` has been simplified and `Alt.default` is used.
|
| 24 |
+
Reason: if there is no `Alt.default` branch, then we can decide whether `x` at `FnBody.case x alts` is an
|
| 25 |
+
enumeration type by simply inspecting the `CtorInfo` values at `alts`.
|
| 26 |
+
- This transformation is applied before lower level optimizations are applied which use
|
| 27 |
+
`Expr.isShared`, `Expr.isTaggedPtr`, and `FnBody.set`.
|
| 28 |
+
- This transformation is applied after `reset` and `reuse` instructions have been added.
|
| 29 |
+
Reason: `resetreuse.lean` ignores `box` and `unbox` instructions.
|
| 30 |
+
-/
|
| 31 |
+
|
| 32 |
+
def mkBoxedName (n : Name) : Name :=
|
| 33 |
+
Name.mkStr n "_boxed"
|
| 34 |
+
|
| 35 |
+
def isBoxedName (name : Name) : Bool :=
|
| 36 |
+
name matches .str _ "_boxed"
|
| 37 |
+
|
| 38 |
+
abbrev N := StateM Nat
|
| 39 |
+
|
| 40 |
+
private def N.mkFresh : N VarId :=
|
| 41 |
+
modifyGet fun n => ({ idx := n }, n + 1)
|
| 42 |
+
|
| 43 |
+
def requiresBoxedVersion (env : Environment) (decl : Decl) : Bool :=
|
| 44 |
+
let ps := decl.params
|
| 45 |
+
(ps.size > 0 && (decl.resultType.isScalar || ps.any (fun p => p.ty.isScalar || p.borrow) || isExtern env decl.name))
|
| 46 |
+
|| ps.size > closureMaxArgs
|
| 47 |
+
|
| 48 |
+
def mkBoxedVersionAux (decl : Decl) : N Decl := do
|
| 49 |
+
let ps := decl.params
|
| 50 |
+
let qs ← ps.mapM fun _ => do let x ← N.mkFresh; pure { x := x, ty := IRType.object, borrow := false : Param }
|
| 51 |
+
let (newVDecls, xs) ← qs.size.foldM (init := (#[], #[])) fun i _ (newVDecls, xs) => do
|
| 52 |
+
let p := ps[i]!
|
| 53 |
+
let q := qs[i]
|
| 54 |
+
if !p.ty.isScalar then
|
| 55 |
+
pure (newVDecls, xs.push (Arg.var q.x))
|
| 56 |
+
else
|
| 57 |
+
let x ← N.mkFresh
|
| 58 |
+
pure (newVDecls.push (FnBody.vdecl x p.ty (Expr.unbox q.x) default), xs.push (Arg.var x))
|
| 59 |
+
let r ← N.mkFresh
|
| 60 |
+
let newVDecls := newVDecls.push (FnBody.vdecl r decl.resultType (Expr.fap decl.name xs) default)
|
| 61 |
+
let body ← if !decl.resultType.isScalar then
|
| 62 |
+
pure <| reshape newVDecls (FnBody.ret (Arg.var r))
|
| 63 |
+
else
|
| 64 |
+
let newR ← N.mkFresh
|
| 65 |
+
let newVDecls := newVDecls.push (FnBody.vdecl newR IRType.object (Expr.box decl.resultType r) default)
|
| 66 |
+
pure <| reshape newVDecls (FnBody.ret (Arg.var newR))
|
| 67 |
+
return Decl.fdecl (mkBoxedName decl.name) qs IRType.object body decl.getInfo
|
| 68 |
+
|
| 69 |
+
def mkBoxedVersion (decl : Decl) : Decl :=
|
| 70 |
+
(mkBoxedVersionAux decl).run' 1
|
| 71 |
+
|
| 72 |
+
def addBoxedVersions (env : Environment) (decls : Array Decl) : Array Decl :=
|
| 73 |
+
let boxedDecls := decls.foldl (init := #[]) fun newDecls decl =>
|
| 74 |
+
if requiresBoxedVersion env decl then newDecls.push (mkBoxedVersion decl) else newDecls
|
| 75 |
+
decls ++ boxedDecls
|
| 76 |
+
|
| 77 |
+
/-- Infer scrutinee type using `case` alternatives.
|
| 78 |
+
This can be done whenever `alts` does not contain an `Alt.default _` value. -/
|
| 79 |
+
def getScrutineeType (alts : Array Alt) : IRType :=
|
| 80 |
+
let isScalar :=
|
| 81 |
+
alts.size > 1 && -- Recall that we encode Unit and PUnit using `object`.
|
| 82 |
+
alts.all fun
|
| 83 |
+
| Alt.ctor c _ => c.isScalar
|
| 84 |
+
| Alt.default _ => false
|
| 85 |
+
match isScalar with
|
| 86 |
+
| false => IRType.object
|
| 87 |
+
| true =>
|
| 88 |
+
let n := alts.size
|
| 89 |
+
if n < 256 then IRType.uint8
|
| 90 |
+
else if n < 65536 then IRType.uint16
|
| 91 |
+
else if n < 4294967296 then IRType.uint32
|
| 92 |
+
else IRType.object -- in practice this should be unreachable
|
| 93 |
+
|
| 94 |
+
def eqvTypes (t₁ t₂ : IRType) : Bool :=
|
| 95 |
+
(t₁.isScalar == t₂.isScalar) && (!t₁.isScalar || t₁ == t₂)
|
| 96 |
+
|
| 97 |
+
structure BoxingContext where
|
| 98 |
+
f : FunId := default
|
| 99 |
+
localCtx : LocalContext := {}
|
| 100 |
+
resultType : IRType := IRType.irrelevant
|
| 101 |
+
decls : Array Decl
|
| 102 |
+
env : Environment
|
| 103 |
+
|
| 104 |
+
structure BoxingState where
|
| 105 |
+
nextIdx : Index
|
| 106 |
+
/-- We create auxiliary declarations when boxing constant and literals.
|
| 107 |
+
The idea is to avoid code such as
|
| 108 |
+
```
|
| 109 |
+
let x1 := Uint64.inhabited;
|
| 110 |
+
let x2 := box x1;
|
| 111 |
+
...
|
| 112 |
+
```
|
| 113 |
+
We currently do not cache these declarations in an environment extension, but
|
| 114 |
+
we use auxDeclCache to avoid creating equivalent auxiliary declarations more than once when
|
| 115 |
+
processing the same IR declaration.
|
| 116 |
+
-/
|
| 117 |
+
auxDecls : Array Decl := #[]
|
| 118 |
+
auxDeclCache : AssocList FnBody Expr := AssocList.empty
|
| 119 |
+
nextAuxId : Nat := 1
|
| 120 |
+
|
| 121 |
+
abbrev M := ReaderT BoxingContext (StateT BoxingState Id)
|
| 122 |
+
|
| 123 |
+
private def M.mkFresh : M VarId := do
|
| 124 |
+
let oldS ← getModify fun s => { s with nextIdx := s.nextIdx + 1 }
|
| 125 |
+
pure { idx := oldS.nextIdx }
|
| 126 |
+
|
| 127 |
+
def getEnv : M Environment := BoxingContext.env <$> read
|
| 128 |
+
def getLocalContext : M LocalContext := BoxingContext.localCtx <$> read
|
| 129 |
+
def getResultType : M IRType := BoxingContext.resultType <$> read
|
| 130 |
+
|
| 131 |
+
def getVarType (x : VarId) : M IRType := do
|
| 132 |
+
let localCtx ← getLocalContext
|
| 133 |
+
match localCtx.getType x with
|
| 134 |
+
| some t => pure t
|
| 135 |
+
| none => pure IRType.object -- unreachable, we assume the code is well formed
|
| 136 |
+
|
| 137 |
+
def getJPParams (j : JoinPointId) : M (Array Param) := do
|
| 138 |
+
let localCtx ← getLocalContext
|
| 139 |
+
match localCtx.getJPParams j with
|
| 140 |
+
| some ys => pure ys
|
| 141 |
+
| none => pure #[] -- unreachable, we assume the code is well formed
|
| 142 |
+
|
| 143 |
+
def getDecl (fid : FunId) : M Decl := do
|
| 144 |
+
let ctx ← read
|
| 145 |
+
match findEnvDecl' ctx.env fid ctx.decls with
|
| 146 |
+
| some decl => pure decl
|
| 147 |
+
| none => pure default -- unreachable if well-formed
|
| 148 |
+
|
| 149 |
+
@[inline] def withParams {α : Type} (xs : Array Param) (k : M α) : M α :=
|
| 150 |
+
withReader (fun ctx => { ctx with localCtx := ctx.localCtx.addParams xs }) k
|
| 151 |
+
|
| 152 |
+
@[inline] def withVDecl {α : Type} (x : VarId) (ty : IRType) (v : Expr) (k : M α) : M α :=
|
| 153 |
+
withReader (fun ctx => { ctx with localCtx := ctx.localCtx.addLocal x ty v }) k
|
| 154 |
+
|
| 155 |
+
@[inline] def withJDecl {α : Type} (j : JoinPointId) (xs : Array Param) (v : FnBody) (k : M α) : M α :=
|
| 156 |
+
withReader (fun ctx => { ctx with localCtx := ctx.localCtx.addJP j xs v }) k
|
| 157 |
+
|
| 158 |
+
/-- If `x` declaration is of the form `x := Expr.lit _` or `x := Expr.fap c #[]`,
|
| 159 |
+
and `x`'s type is not cheap to box (e.g., it is `UInt64), then return its value. -/
|
| 160 |
+
private def isExpensiveConstantValueBoxing (x : VarId) (xType : IRType) : M (Option Expr) :=
|
| 161 |
+
if !xType.isScalar then
|
| 162 |
+
return none -- We assume unboxing is always cheap
|
| 163 |
+
else match xType with
|
| 164 |
+
| IRType.uint8 => return none
|
| 165 |
+
| IRType.uint16 => return none
|
| 166 |
+
| _ => do
|
| 167 |
+
let localCtx ← getLocalContext
|
| 168 |
+
match localCtx.getValue x with
|
| 169 |
+
| some val =>
|
| 170 |
+
match val with
|
| 171 |
+
| Expr.lit _ => return some val
|
| 172 |
+
| Expr.fap _ args => return if args.size == 0 then some val else none
|
| 173 |
+
| _ => return none
|
| 174 |
+
| _ => return none
|
| 175 |
+
|
| 176 |
+
/-- Auxiliary function used by castVarIfNeeded.
|
| 177 |
+
It is used when the expected type does not match `xType`.
|
| 178 |
+
If `xType` is scalar, then we need to "box" it. Otherwise, we need to "unbox" it. -/
|
| 179 |
+
def mkCast (x : VarId) (xType : IRType) (expectedType : IRType) : M Expr := do
|
| 180 |
+
match (← isExpensiveConstantValueBoxing x xType) with
|
| 181 |
+
| some v => do
|
| 182 |
+
let ctx ← read
|
| 183 |
+
let s ← get
|
| 184 |
+
/- Create auxiliary FnBody
|
| 185 |
+
```
|
| 186 |
+
let x_1 : xType := v;
|
| 187 |
+
let x_2 : expectedType := Expr.box xType x_1;
|
| 188 |
+
ret x_2
|
| 189 |
+
```
|
| 190 |
+
-/
|
| 191 |
+
let body : FnBody :=
|
| 192 |
+
FnBody.vdecl { idx := 1 } xType v $
|
| 193 |
+
FnBody.vdecl { idx := 2 } expectedType (Expr.box xType { idx := 1 }) $
|
| 194 |
+
FnBody.ret (mkVarArg { idx := 2 })
|
| 195 |
+
match s.auxDeclCache.find? body with
|
| 196 |
+
| some v => pure v
|
| 197 |
+
| none => do
|
| 198 |
+
let auxName := ctx.f ++ ((`_boxed_const).appendIndexAfter s.nextAuxId)
|
| 199 |
+
let auxConst := Expr.fap auxName #[]
|
| 200 |
+
let auxDecl := Decl.fdecl auxName #[] expectedType body {}
|
| 201 |
+
modify fun s => { s with
|
| 202 |
+
auxDecls := s.auxDecls.push auxDecl
|
| 203 |
+
auxDeclCache := s.auxDeclCache.cons body auxConst
|
| 204 |
+
nextAuxId := s.nextAuxId + 1
|
| 205 |
+
}
|
| 206 |
+
pure auxConst
|
| 207 |
+
| none => pure $ if xType.isScalar then Expr.box xType x else Expr.unbox x
|
| 208 |
+
|
| 209 |
+
@[inline] def castVarIfNeeded (x : VarId) (expected : IRType) (k : VarId → M FnBody) : M FnBody := do
|
| 210 |
+
let xType ← getVarType x
|
| 211 |
+
if eqvTypes xType expected then
|
| 212 |
+
k x
|
| 213 |
+
else
|
| 214 |
+
let y ← M.mkFresh
|
| 215 |
+
let v ← mkCast x xType expected
|
| 216 |
+
FnBody.vdecl y expected v <$> k y
|
| 217 |
+
|
| 218 |
+
@[inline] def castArgIfNeeded (x : Arg) (expected : IRType) (k : Arg → M FnBody) : M FnBody :=
|
| 219 |
+
match x with
|
| 220 |
+
| Arg.var x => castVarIfNeeded x expected (fun x => k (Arg.var x))
|
| 221 |
+
| _ => k x
|
| 222 |
+
|
| 223 |
+
def castArgsIfNeededAux (xs : Array Arg) (typeFromIdx : Nat → IRType) : M (Array Arg × Array FnBody) := do
|
| 224 |
+
let mut xs' := #[]
|
| 225 |
+
let mut bs := #[]
|
| 226 |
+
let mut i := 0
|
| 227 |
+
for x in xs do
|
| 228 |
+
let expected := typeFromIdx i
|
| 229 |
+
match x with
|
| 230 |
+
| Arg.irrelevant =>
|
| 231 |
+
xs' := xs'.push x
|
| 232 |
+
| Arg.var x =>
|
| 233 |
+
let xType ← getVarType x
|
| 234 |
+
if eqvTypes xType expected then
|
| 235 |
+
xs' := xs'.push (Arg.var x)
|
| 236 |
+
else
|
| 237 |
+
let y ← M.mkFresh
|
| 238 |
+
let v ← mkCast x xType expected
|
| 239 |
+
let b := FnBody.vdecl y expected v FnBody.nil
|
| 240 |
+
xs' := xs'.push (Arg.var y)
|
| 241 |
+
bs := bs.push b
|
| 242 |
+
i := i + 1
|
| 243 |
+
return (xs', bs)
|
| 244 |
+
|
| 245 |
+
@[inline] def castArgsIfNeeded (xs : Array Arg) (ps : Array Param) (k : Array Arg → M FnBody) : M FnBody := do
|
| 246 |
+
let (ys, bs) ← castArgsIfNeededAux xs fun i => ps[i]!.ty
|
| 247 |
+
let b ← k ys
|
| 248 |
+
pure (reshape bs b)
|
| 249 |
+
|
| 250 |
+
@[inline] def boxArgsIfNeeded (xs : Array Arg) (k : Array Arg → M FnBody) : M FnBody := do
|
| 251 |
+
let (ys, bs) ← castArgsIfNeededAux xs (fun _ => IRType.object)
|
| 252 |
+
let b ← k ys
|
| 253 |
+
pure (reshape bs b)
|
| 254 |
+
|
| 255 |
+
def unboxResultIfNeeded (x : VarId) (ty : IRType) (e : Expr) (b : FnBody) : M FnBody := do
|
| 256 |
+
if ty.isScalar then
|
| 257 |
+
let y ← M.mkFresh
|
| 258 |
+
return FnBody.vdecl y IRType.object e (FnBody.vdecl x ty (Expr.unbox y) b)
|
| 259 |
+
else
|
| 260 |
+
return FnBody.vdecl x ty e b
|
| 261 |
+
|
| 262 |
+
def castResultIfNeeded (x : VarId) (ty : IRType) (e : Expr) (eType : IRType) (b : FnBody) : M FnBody := do
|
| 263 |
+
if eqvTypes ty eType then
|
| 264 |
+
return FnBody.vdecl x ty e b
|
| 265 |
+
else
|
| 266 |
+
let y ← M.mkFresh
|
| 267 |
+
let v ← mkCast y eType ty
|
| 268 |
+
return FnBody.vdecl y eType e (FnBody.vdecl x ty v b)
|
| 269 |
+
|
| 270 |
+
def visitVDeclExpr (x : VarId) (ty : IRType) (e : Expr) (b : FnBody) : M FnBody :=
|
| 271 |
+
match e with
|
| 272 |
+
| Expr.ctor c ys =>
|
| 273 |
+
if c.isScalar && ty.isScalar then
|
| 274 |
+
return FnBody.vdecl x ty (Expr.lit (LitVal.num c.cidx)) b
|
| 275 |
+
else
|
| 276 |
+
boxArgsIfNeeded ys fun ys => return FnBody.vdecl x ty (Expr.ctor c ys) b
|
| 277 |
+
| Expr.reuse w c u ys =>
|
| 278 |
+
boxArgsIfNeeded ys fun ys => return FnBody.vdecl x ty (Expr.reuse w c u ys) b
|
| 279 |
+
| Expr.fap f ys => do
|
| 280 |
+
let decl ← getDecl f
|
| 281 |
+
castArgsIfNeeded ys decl.params fun ys =>
|
| 282 |
+
castResultIfNeeded x ty (Expr.fap f ys) decl.resultType b
|
| 283 |
+
| Expr.pap f ys => do
|
| 284 |
+
let env ← getEnv
|
| 285 |
+
let decl ← getDecl f
|
| 286 |
+
let f := if requiresBoxedVersion env decl then mkBoxedName f else f
|
| 287 |
+
boxArgsIfNeeded ys fun ys => return FnBody.vdecl x ty (Expr.pap f ys) b
|
| 288 |
+
| Expr.ap f ys =>
|
| 289 |
+
boxArgsIfNeeded ys fun ys =>
|
| 290 |
+
unboxResultIfNeeded x ty (Expr.ap f ys) b
|
| 291 |
+
| _ =>
|
| 292 |
+
return FnBody.vdecl x ty e b
|
| 293 |
+
|
| 294 |
+
partial def visitFnBody : FnBody → M FnBody
|
| 295 |
+
| FnBody.vdecl x t v b => do
|
| 296 |
+
let b ← withVDecl x t v (visitFnBody b)
|
| 297 |
+
visitVDeclExpr x t v b
|
| 298 |
+
| FnBody.jdecl j xs v b => do
|
| 299 |
+
let v ← withParams xs (visitFnBody v)
|
| 300 |
+
let b ← withJDecl j xs v (visitFnBody b)
|
| 301 |
+
return FnBody.jdecl j xs v b
|
| 302 |
+
| FnBody.uset x i y b => do
|
| 303 |
+
let b ← visitFnBody b
|
| 304 |
+
castVarIfNeeded y IRType.usize fun y =>
|
| 305 |
+
return FnBody.uset x i y b
|
| 306 |
+
| FnBody.sset x i o y ty b => do
|
| 307 |
+
let b ← visitFnBody b
|
| 308 |
+
castVarIfNeeded y ty fun y =>
|
| 309 |
+
return FnBody.sset x i o y ty b
|
| 310 |
+
| FnBody.mdata d b =>
|
| 311 |
+
FnBody.mdata d <$> visitFnBody b
|
| 312 |
+
| FnBody.case tid x _ alts => do
|
| 313 |
+
let expected := getScrutineeType alts
|
| 314 |
+
let alts ← alts.mapM fun alt => alt.mmodifyBody visitFnBody
|
| 315 |
+
castVarIfNeeded x expected fun x => do
|
| 316 |
+
return FnBody.case tid x expected alts
|
| 317 |
+
| FnBody.ret x => do
|
| 318 |
+
let expected ← getResultType
|
| 319 |
+
castArgIfNeeded x expected (fun x => return FnBody.ret x)
|
| 320 |
+
| FnBody.jmp j ys => do
|
| 321 |
+
let ps ← getJPParams j
|
| 322 |
+
castArgsIfNeeded ys ps fun ys => return FnBody.jmp j ys
|
| 323 |
+
| other =>
|
| 324 |
+
pure other
|
| 325 |
+
|
| 326 |
+
def run (env : Environment) (decls : Array Decl) : Array Decl :=
|
| 327 |
+
let ctx : BoxingContext := { decls := decls, env := env }
|
| 328 |
+
let decls := decls.foldl (init := #[]) fun newDecls decl =>
|
| 329 |
+
match decl with
|
| 330 |
+
| .fdecl (f := f) (xs := xs) (type := t) (body := b) .. =>
|
| 331 |
+
let nextIdx := decl.maxIndex + 1
|
| 332 |
+
let (b, s) := (withParams xs (visitFnBody b) { ctx with f := f, resultType := t }).run { nextIdx := nextIdx }
|
| 333 |
+
let newDecls := newDecls ++ s.auxDecls
|
| 334 |
+
let newDecl := decl.updateBody! b
|
| 335 |
+
let newDecl := newDecl.elimDead
|
| 336 |
+
newDecls.push newDecl
|
| 337 |
+
| d => newDecls.push d
|
| 338 |
+
addBoxedVersions env decls
|
| 339 |
+
|
| 340 |
+
end ExplicitBoxing
|
| 341 |
+
|
| 342 |
+
def explicitBoxing (decls : Array Decl) : CompilerM (Array Decl) := do
|
| 343 |
+
let env ← getEnv
|
| 344 |
+
return ExplicitBoxing.run env decls
|
| 345 |
+
|
| 346 |
+
end Lean.IR
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/Checker.lean
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2019 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Compiler.IR.CompilerM
|
| 8 |
+
import Lean.Compiler.IR.Format
|
| 9 |
+
|
| 10 |
+
namespace Lean.IR.Checker
|
| 11 |
+
|
| 12 |
+
@[extern "lean_get_max_ctor_fields"]
|
| 13 |
+
opaque getMaxCtorFields : Unit → Nat
|
| 14 |
+
def maxCtorFields := getMaxCtorFields ()
|
| 15 |
+
|
| 16 |
+
@[extern "lean_get_max_ctor_scalars_size"]
|
| 17 |
+
opaque getMaxCtorScalarsSize : Unit → Nat
|
| 18 |
+
def maxCtorScalarsSize := getMaxCtorScalarsSize ()
|
| 19 |
+
|
| 20 |
+
@[extern "lean_get_max_ctor_tag"]
|
| 21 |
+
opaque getMaxCtorTag : Unit → Nat
|
| 22 |
+
def maxCtorTag := getMaxCtorTag ()
|
| 23 |
+
|
| 24 |
+
@[extern "lean_get_usize_size"]
|
| 25 |
+
opaque getUSizeSize : Unit → Nat
|
| 26 |
+
def usizeSize := getUSizeSize ()
|
| 27 |
+
|
| 28 |
+
structure CheckerContext where
|
| 29 |
+
env : Environment
|
| 30 |
+
localCtx : LocalContext := {}
|
| 31 |
+
decls : Array Decl
|
| 32 |
+
|
| 33 |
+
structure CheckerState where
|
| 34 |
+
foundVars : IndexSet := {}
|
| 35 |
+
|
| 36 |
+
abbrev M := ReaderT CheckerContext (ExceptT String (StateT CheckerState Id))
|
| 37 |
+
|
| 38 |
+
def markIndex (i : Index) : M Unit := do
|
| 39 |
+
let s ← get
|
| 40 |
+
if s.foundVars.contains i then
|
| 41 |
+
throw s!"variable / joinpoint index {i} has already been used"
|
| 42 |
+
modify fun s => { s with foundVars := s.foundVars.insert i }
|
| 43 |
+
|
| 44 |
+
def markVar (x : VarId) : M Unit :=
|
| 45 |
+
markIndex x.idx
|
| 46 |
+
|
| 47 |
+
def markJP (j : JoinPointId) : M Unit :=
|
| 48 |
+
markIndex j.idx
|
| 49 |
+
|
| 50 |
+
def getDecl (c : Name) : M Decl := do
|
| 51 |
+
let ctx ← read
|
| 52 |
+
match findEnvDecl' ctx.env c ctx.decls with
|
| 53 |
+
| none => throw s!"depends on declaration '{c}', which has no executable code; consider marking definition as 'noncomputable'"
|
| 54 |
+
| some d => pure d
|
| 55 |
+
|
| 56 |
+
def checkVar (x : VarId) : M Unit := do
|
| 57 |
+
let ctx ← read
|
| 58 |
+
unless ctx.localCtx.isLocalVar x.idx || ctx.localCtx.isParam x.idx do
|
| 59 |
+
throw s!"unknown variable '{x}'"
|
| 60 |
+
|
| 61 |
+
def checkJP (j : JoinPointId) : M Unit := do
|
| 62 |
+
let ctx ← read
|
| 63 |
+
unless ctx.localCtx.isJP j.idx do
|
| 64 |
+
throw s!"unknown join point '{j}'"
|
| 65 |
+
|
| 66 |
+
def checkArg (a : Arg) : M Unit :=
|
| 67 |
+
match a with
|
| 68 |
+
| Arg.var x => checkVar x
|
| 69 |
+
| _ => pure ()
|
| 70 |
+
|
| 71 |
+
def checkArgs (as : Array Arg) : M Unit :=
|
| 72 |
+
as.forM checkArg
|
| 73 |
+
|
| 74 |
+
@[inline] def checkEqTypes (ty₁ ty₂ : IRType) : M Unit := do
|
| 75 |
+
unless ty₁ == ty₂ do
|
| 76 |
+
throw "unexpected type '{ty₁}' != '{ty₂}'"
|
| 77 |
+
|
| 78 |
+
@[inline] def checkType (ty : IRType) (p : IRType → Bool) (suffix? : Option String := none): M Unit := do
|
| 79 |
+
unless p ty do
|
| 80 |
+
let mut msg := s!"unexpected type '{ty}'"
|
| 81 |
+
if let some suffix := suffix? then
|
| 82 |
+
msg := s!"{msg}, {suffix}"
|
| 83 |
+
throw msg
|
| 84 |
+
|
| 85 |
+
def checkObjType (ty : IRType) : M Unit := checkType ty IRType.isObj "object expected"
|
| 86 |
+
|
| 87 |
+
def checkScalarType (ty : IRType) : M Unit := checkType ty IRType.isScalar "scalar expected"
|
| 88 |
+
|
| 89 |
+
def getType (x : VarId) : M IRType := do
|
| 90 |
+
let ctx ← read
|
| 91 |
+
match ctx.localCtx.getType x with
|
| 92 |
+
| some ty => pure ty
|
| 93 |
+
| none => throw s!"unknown variable '{x}'"
|
| 94 |
+
|
| 95 |
+
@[inline] def checkVarType (x : VarId) (p : IRType → Bool) (suffix? : Option String := none) : M Unit := do
|
| 96 |
+
let ty ← getType x; checkType ty p suffix?
|
| 97 |
+
|
| 98 |
+
def checkObjVar (x : VarId) : M Unit :=
|
| 99 |
+
checkVarType x IRType.isObj "object expected"
|
| 100 |
+
|
| 101 |
+
def checkScalarVar (x : VarId) : M Unit :=
|
| 102 |
+
checkVarType x IRType.isScalar "scalar expected"
|
| 103 |
+
|
| 104 |
+
def checkFullApp (c : FunId) (ys : Array Arg) : M Unit := do
|
| 105 |
+
let decl ← getDecl c
|
| 106 |
+
unless ys.size == decl.params.size do
|
| 107 |
+
throw s!"incorrect number of arguments to '{c}', {ys.size} provided, {decl.params.size} expected"
|
| 108 |
+
checkArgs ys
|
| 109 |
+
|
| 110 |
+
def checkPartialApp (c : FunId) (ys : Array Arg) : M Unit := do
|
| 111 |
+
let decl ← getDecl c
|
| 112 |
+
unless ys.size < decl.params.size do
|
| 113 |
+
throw s!"too many arguments to partial application '{c}', num. args: {ys.size}, arity: {decl.params.size}"
|
| 114 |
+
checkArgs ys
|
| 115 |
+
|
| 116 |
+
def checkExpr (ty : IRType) : Expr → M Unit
|
| 117 |
+
| Expr.pap f ys => checkPartialApp f ys *> checkObjType ty -- partial applications should always produce a closure object
|
| 118 |
+
| Expr.ap x ys => checkObjVar x *> checkArgs ys
|
| 119 |
+
| Expr.fap f ys => checkFullApp f ys
|
| 120 |
+
| Expr.ctor c ys => do
|
| 121 |
+
if c.cidx > maxCtorTag && (c.size > 0 || c.usize > 0 || c.ssize > 0) then
|
| 122 |
+
throw s!"tag for constructor '{c.name}' is too big, this is a limitation of the current runtime"
|
| 123 |
+
if c.size > maxCtorFields then
|
| 124 |
+
throw s!"constructor '{c.name}' has too many fields"
|
| 125 |
+
if c.ssize + c.usize * usizeSize > maxCtorScalarsSize then
|
| 126 |
+
throw s!"constructor '{c.name}' has too many scalar fields"
|
| 127 |
+
if !ty.isStruct && !ty.isUnion && c.isRef then
|
| 128 |
+
(checkObjType ty) *> checkArgs ys
|
| 129 |
+
| Expr.reset _ x => checkObjVar x *> checkObjType ty
|
| 130 |
+
| Expr.reuse x _ _ ys => checkObjVar x *> checkArgs ys *> checkObjType ty
|
| 131 |
+
| Expr.box xty x => checkObjType ty *> checkScalarVar x *> checkVarType x (fun t => t == xty)
|
| 132 |
+
| Expr.unbox x => checkScalarType ty *> checkObjVar x
|
| 133 |
+
| Expr.proj i x => do
|
| 134 |
+
let xType ← getType x;
|
| 135 |
+
match xType with
|
| 136 |
+
| IRType.object => checkObjType ty
|
| 137 |
+
| IRType.tobject => checkObjType ty
|
| 138 |
+
| IRType.struct _ tys => if h : i < tys.size then checkEqTypes (tys[i]) ty else throw "invalid proj index"
|
| 139 |
+
| IRType.union _ tys => if h : i < tys.size then checkEqTypes (tys[i]) ty else throw "invalid proj index"
|
| 140 |
+
| _ => throw s!"unexpected IR type '{xType}'"
|
| 141 |
+
| Expr.uproj _ x => checkObjVar x *> checkType ty (fun t => t == IRType.usize)
|
| 142 |
+
| Expr.sproj _ _ x => checkObjVar x *> checkScalarType ty
|
| 143 |
+
| Expr.isShared x => checkObjVar x *> checkType ty (fun t => t == IRType.uint8)
|
| 144 |
+
| Expr.lit (LitVal.str _) => checkObjType ty
|
| 145 |
+
| Expr.lit _ => pure ()
|
| 146 |
+
|
| 147 |
+
@[inline] def withParams (ps : Array Param) (k : M Unit) : M Unit := do
|
| 148 |
+
let ctx ← read
|
| 149 |
+
let localCtx ← ps.foldlM (init := ctx.localCtx) fun (ctx : LocalContext) p => do
|
| 150 |
+
markVar p.x
|
| 151 |
+
pure $ ctx.addParam p
|
| 152 |
+
withReader (fun _ => { ctx with localCtx := localCtx }) k
|
| 153 |
+
|
| 154 |
+
partial def checkFnBody : FnBody → M Unit
|
| 155 |
+
| .vdecl x t v b => do
|
| 156 |
+
checkExpr t v
|
| 157 |
+
markVar x
|
| 158 |
+
withReader (fun ctx => { ctx with localCtx := ctx.localCtx.addLocal x t v }) (checkFnBody b)
|
| 159 |
+
| .jdecl j ys v b => do
|
| 160 |
+
markJP j
|
| 161 |
+
withParams ys (checkFnBody v)
|
| 162 |
+
withReader (fun ctx => { ctx with localCtx := ctx.localCtx.addJP j ys v }) (checkFnBody b)
|
| 163 |
+
| .set x _ y b => checkVar x *> checkArg y *> checkFnBody b
|
| 164 |
+
| .uset x _ y b => checkVar x *> checkVar y *> checkFnBody b
|
| 165 |
+
| .sset x _ _ y _ b => checkVar x *> checkVar y *> checkFnBody b
|
| 166 |
+
| .setTag x _ b => checkVar x *> checkFnBody b
|
| 167 |
+
| .inc x _ _ _ b => checkVar x *> checkFnBody b
|
| 168 |
+
| .dec x _ _ _ b => checkVar x *> checkFnBody b
|
| 169 |
+
| .del x b => checkVar x *> checkFnBody b
|
| 170 |
+
| .mdata _ b => checkFnBody b
|
| 171 |
+
| .jmp j ys => checkJP j *> checkArgs ys
|
| 172 |
+
| .ret x => checkArg x
|
| 173 |
+
| .case _ x _ alts => checkVar x *> alts.forM (fun alt => checkFnBody alt.body)
|
| 174 |
+
| .unreachable => pure ()
|
| 175 |
+
|
| 176 |
+
def checkDecl : Decl → M Unit
|
| 177 |
+
| .fdecl (xs := xs) (body := b) .. => withParams xs (checkFnBody b)
|
| 178 |
+
| .extern (xs := xs) .. => withParams xs (pure ())
|
| 179 |
+
|
| 180 |
+
end Checker
|
| 181 |
+
|
| 182 |
+
def checkDecl (decls : Array Decl) (decl : Decl) : CompilerM Unit := do
|
| 183 |
+
let env ← getEnv
|
| 184 |
+
match (Checker.checkDecl decl { env := env, decls := decls }).run' {} with
|
| 185 |
+
| .error msg => throw s!"failed to compile definition, compiler IR check failed at '{decl.name}'. Error: {msg}"
|
| 186 |
+
| _ => pure ()
|
| 187 |
+
|
| 188 |
+
def checkDecls (decls : Array Decl) : CompilerM Unit :=
|
| 189 |
+
decls.forM (checkDecl decls)
|
| 190 |
+
|
| 191 |
+
end IR
|
| 192 |
+
end Lean
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/CompilerM.lean
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2019 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Environment
|
| 8 |
+
import Lean.Compiler.IR.Basic
|
| 9 |
+
import Lean.Compiler.IR.Format
|
| 10 |
+
import Lean.Compiler.MetaAttr
|
| 11 |
+
import Lean.Compiler.ExportAttr
|
| 12 |
+
|
| 13 |
+
namespace Lean.IR
|
| 14 |
+
|
| 15 |
+
inductive LogEntry where
|
| 16 |
+
| step (cls : Name) (decls : Array Decl)
|
| 17 |
+
| message (msg : Format)
|
| 18 |
+
|
| 19 |
+
namespace LogEntry
|
| 20 |
+
protected def fmt : LogEntry → Format
|
| 21 |
+
| step cls decls => Format.bracket "[" (format cls) "]" ++ decls.foldl (fun fmt decl => fmt ++ Format.line ++ format decl) Format.nil
|
| 22 |
+
| message msg => msg
|
| 23 |
+
|
| 24 |
+
instance : ToFormat LogEntry := ⟨LogEntry.fmt⟩
|
| 25 |
+
end LogEntry
|
| 26 |
+
|
| 27 |
+
abbrev Log := Array LogEntry
|
| 28 |
+
|
| 29 |
+
def Log.format (log : Log) : Format :=
|
| 30 |
+
log.foldl (init := Format.nil) fun fmt entry =>
|
| 31 |
+
f!"{fmt}{Format.line}{entry}"
|
| 32 |
+
|
| 33 |
+
@[export lean_ir_log_to_string]
|
| 34 |
+
def Log.toString (log : Log) : String :=
|
| 35 |
+
log.format.pretty
|
| 36 |
+
|
| 37 |
+
structure CompilerState where
|
| 38 |
+
env : Environment
|
| 39 |
+
log : Log := #[]
|
| 40 |
+
|
| 41 |
+
abbrev CompilerM := ReaderT Options (EStateM String CompilerState)
|
| 42 |
+
|
| 43 |
+
def log (entry : LogEntry) : CompilerM Unit :=
|
| 44 |
+
modify fun s => { s with log := s.log.push entry }
|
| 45 |
+
|
| 46 |
+
def tracePrefixOptionName := `trace.compiler.ir
|
| 47 |
+
|
| 48 |
+
private def isLogEnabledFor (opts : Options) (optName : Name) : Bool :=
|
| 49 |
+
match opts.find optName with
|
| 50 |
+
| some (DataValue.ofBool v) => v
|
| 51 |
+
| _ => opts.getBool tracePrefixOptionName
|
| 52 |
+
|
| 53 |
+
private def logDeclsAux (optName : Name) (cls : Name) (decls : Array Decl) : CompilerM Unit := do
|
| 54 |
+
let opts ← read
|
| 55 |
+
if isLogEnabledFor opts optName then
|
| 56 |
+
log (LogEntry.step cls decls)
|
| 57 |
+
|
| 58 |
+
@[inline] def logDecls (cls : Name) (decl : Array Decl) : CompilerM Unit :=
|
| 59 |
+
logDeclsAux (tracePrefixOptionName ++ cls) cls decl
|
| 60 |
+
|
| 61 |
+
private def logMessageIfAux {α : Type} [ToFormat α] (optName : Name) (a : α) : CompilerM Unit := do
|
| 62 |
+
let opts ← read
|
| 63 |
+
if isLogEnabledFor opts optName then
|
| 64 |
+
log (LogEntry.message (format a))
|
| 65 |
+
|
| 66 |
+
@[inline] def logMessageIf {α : Type} [ToFormat α] (cls : Name) (a : α) : CompilerM Unit :=
|
| 67 |
+
logMessageIfAux (tracePrefixOptionName ++ cls) a
|
| 68 |
+
|
| 69 |
+
@[inline] def logMessage {α : Type} [ToFormat α] (a : α) : CompilerM Unit :=
|
| 70 |
+
logMessageIfAux tracePrefixOptionName a
|
| 71 |
+
|
| 72 |
+
@[inline] def modifyEnv (f : Environment → Environment) : CompilerM Unit :=
|
| 73 |
+
modify fun s => { s with env := f s.env }
|
| 74 |
+
|
| 75 |
+
abbrev DeclMap := PHashMap Name Decl
|
| 76 |
+
|
| 77 |
+
private abbrev declLt (a b : Decl) :=
|
| 78 |
+
Name.quickLt a.name b.name
|
| 79 |
+
|
| 80 |
+
private abbrev sortDecls (decls : Array Decl) : Array Decl :=
|
| 81 |
+
decls.qsort declLt
|
| 82 |
+
|
| 83 |
+
private abbrev findAtSorted? (decls : Array Decl) (declName : Name) : Option Decl :=
|
| 84 |
+
let tmpDecl := Decl.extern declName #[] default default
|
| 85 |
+
decls.binSearch tmpDecl declLt
|
| 86 |
+
|
| 87 |
+
namespace CollectUsedFDecls
|
| 88 |
+
|
| 89 |
+
abbrev M := StateM NameSet
|
| 90 |
+
|
| 91 |
+
@[inline] def collect (f : FunId) : M Unit :=
|
| 92 |
+
modify fun s => s.insert f
|
| 93 |
+
|
| 94 |
+
partial def collectFnBody : FnBody → M Unit
|
| 95 |
+
| .vdecl _ _ v b =>
|
| 96 |
+
match v with
|
| 97 |
+
| .fap f _ => collect f *> collectFnBody b
|
| 98 |
+
| .pap f _ => collect f *> collectFnBody b
|
| 99 |
+
| _ => collectFnBody b
|
| 100 |
+
| .jdecl _ _ v b => collectFnBody v *> collectFnBody b
|
| 101 |
+
| .case _ _ _ alts => alts.forM fun alt => collectFnBody alt.body
|
| 102 |
+
| e => unless e.isTerminal do collectFnBody e.body
|
| 103 |
+
|
| 104 |
+
def collectDecl : Decl → M NameSet
|
| 105 |
+
| .fdecl (body := b) .. => collectFnBody b *> get
|
| 106 |
+
| .extern .. => get
|
| 107 |
+
|
| 108 |
+
end CollectUsedFDecls
|
| 109 |
+
|
| 110 |
+
/-- Adds to `used` all `Decl.fdecl`s referenced directly by `decl`. -/
|
| 111 |
+
def collectUsedFDecls (decl : Decl) (used : NameSet := {}) : NameSet :=
|
| 112 |
+
(CollectUsedFDecls.collectDecl decl).run' used
|
| 113 |
+
|
| 114 |
+
/-- Computes the closure of `Decl.fdecl`s referenced by `decl`. -/
|
| 115 |
+
def getFDeclClosure (m : DeclMap) (decls : Array Decl) : NameSet := Id.run do
|
| 116 |
+
let mut toVisit := decls.map (·.name) |>.toList
|
| 117 |
+
let mut res : NameSet := .ofList toVisit
|
| 118 |
+
while !toVisit.isEmpty do
|
| 119 |
+
let n :: toVisit' := toVisit | continue
|
| 120 |
+
toVisit := toVisit'
|
| 121 |
+
let some d := m.find? n | continue
|
| 122 |
+
for d' in collectUsedFDecls d do
|
| 123 |
+
if !res.contains d' then
|
| 124 |
+
res := res.insert d'
|
| 125 |
+
toVisit := d' :: toVisit
|
| 126 |
+
return res
|
| 127 |
+
|
| 128 |
+
builtin_initialize declMapExt : SimplePersistentEnvExtension Decl DeclMap ←
|
| 129 |
+
registerSimplePersistentEnvExtension {
|
| 130 |
+
addImportedFn := fun _ => {}
|
| 131 |
+
addEntryFn := fun s d => s.insert d.name d
|
| 132 |
+
-- Store `meta` closure only in `.olean`, turn all other decls into opaque externs.
|
| 133 |
+
-- Leave storing the remainder for `meta import` and server `#eval` to `exportIREntries` below.
|
| 134 |
+
exportEntriesFnEx? := some fun env s entries level =>
|
| 135 |
+
let decls := entries.foldl (init := #[]) fun decls decl => decls.push decl
|
| 136 |
+
let entries := sortDecls decls
|
| 137 |
+
let metaClosure := getFDeclClosure s (decls.filter (isMeta env ·.name))
|
| 138 |
+
if env.header.isModule then
|
| 139 |
+
entries.map fun
|
| 140 |
+
| d@(.fdecl f xs ty b info) =>
|
| 141 |
+
-- The interpreter may call the boxed variant even if the IR does not directly reference
|
| 142 |
+
-- it.
|
| 143 |
+
let n := match f with
|
| 144 |
+
| .str n "_boxed" => n
|
| 145 |
+
| n => n
|
| 146 |
+
if metaClosure.contains n then
|
| 147 |
+
d
|
| 148 |
+
else if let some (.str _ s) := getExportNameFor? env n then
|
| 149 |
+
.extern f xs ty { arity? := xs.size, entries := [.standard `all s] }
|
| 150 |
+
else
|
| 151 |
+
.extern f xs ty { arity? := xs.size, entries := [.opaque f] }
|
| 152 |
+
| d => d
|
| 153 |
+
else entries
|
| 154 |
+
-- Written to on codegen environment branch but accessed from other elaboration branches when
|
| 155 |
+
-- calling into the interpreter. We cannot use `async` as the IR declarations added may not
|
| 156 |
+
-- share a name prefix with the top-level Lean declaration being compiled, e.g. from
|
| 157 |
+
-- specialization.
|
| 158 |
+
asyncMode := .sync
|
| 159 |
+
replay? := some <| SimplePersistentEnvExtension.replayOfFilter (!·.contains ·.name) (fun s d => s.insert d.name d)
|
| 160 |
+
}
|
| 161 |
+
|
| 162 |
+
@[export lean_ir_export_entries]
|
| 163 |
+
private def exportIREntries (env : Environment) : Array (Name × Array EnvExtensionEntry) :=
|
| 164 |
+
let decls := declMapExt.getEntries env |>.foldl (init := #[]) fun decls decl => decls.push decl
|
| 165 |
+
-- safety: cast to erased type
|
| 166 |
+
let entries : Array EnvExtensionEntry := unsafe unsafeCast <| sortDecls decls
|
| 167 |
+
#[(``declMapExt, entries)]
|
| 168 |
+
|
| 169 |
+
/-- Retrieves IR for codegen purposes, i.e. independent of `meta import`. -/
|
| 170 |
+
def findEnvDecl (env : Environment) (declName : Name) : Option Decl :=
|
| 171 |
+
match env.getModuleIdxFor? declName with
|
| 172 |
+
| some modIdx => findAtSorted? (declMapExt.getModuleEntries env modIdx) declName
|
| 173 |
+
| none => declMapExt.getState env |>.find? declName
|
| 174 |
+
|
| 175 |
+
@[export lean_ir_find_env_decl]
|
| 176 |
+
private def findInterpreterDecl (env : Environment) (declName : Name) : Option Decl :=
|
| 177 |
+
match env.getModuleIdxFor? declName with
|
| 178 |
+
| some modIdx => do
|
| 179 |
+
let decl ←
|
| 180 |
+
-- `meta import` and server `#eval`
|
| 181 |
+
findAtSorted? (declMapExt.getModuleIREntries env modIdx) declName <|>
|
| 182 |
+
-- (closure of) `meta def`; will report `.extern`s for other `def`s so needs to come second
|
| 183 |
+
findAtSorted? (declMapExt.getModuleEntries env modIdx) declName
|
| 184 |
+
guard !decl matches .extern _ _ _ { entries := [.opaque _], .. }
|
| 185 |
+
return decl
|
| 186 |
+
| none => declMapExt.getState env |>.find? declName
|
| 187 |
+
|
| 188 |
+
def findDecl (n : Name) : CompilerM (Option Decl) :=
|
| 189 |
+
return findEnvDecl (← get).env n
|
| 190 |
+
|
| 191 |
+
def containsDecl (n : Name) : CompilerM Bool :=
|
| 192 |
+
return (← findDecl n).isSome
|
| 193 |
+
|
| 194 |
+
def getDecl (n : Name) : CompilerM Decl := do
|
| 195 |
+
let (some decl) ← findDecl n | throw s!"unknown declaration '{n}'"
|
| 196 |
+
return decl
|
| 197 |
+
|
| 198 |
+
@[export lean_ir_add_decl]
|
| 199 |
+
def addDeclAux (env : Environment) (decl : Decl) : Environment :=
|
| 200 |
+
declMapExt.addEntry (env.addExtraName decl.name) decl
|
| 201 |
+
|
| 202 |
+
def getDecls (env : Environment) : List Decl :=
|
| 203 |
+
declMapExt.getEntries env
|
| 204 |
+
|
| 205 |
+
def getEnv : CompilerM Environment := do
|
| 206 |
+
let s ← get; pure s.env
|
| 207 |
+
|
| 208 |
+
def addDecl (decl : Decl) : CompilerM Unit :=
|
| 209 |
+
modifyEnv fun env => declMapExt.addEntry (env.addExtraName decl.name) decl
|
| 210 |
+
|
| 211 |
+
def addDecls (decls : Array Decl) : CompilerM Unit :=
|
| 212 |
+
decls.forM addDecl
|
| 213 |
+
|
| 214 |
+
def findEnvDecl' (env : Environment) (n : Name) (decls : Array Decl) : Option Decl :=
|
| 215 |
+
match decls.find? (fun decl => decl.name == n) with
|
| 216 |
+
| some decl => some decl
|
| 217 |
+
| none => findEnvDecl env n
|
| 218 |
+
|
| 219 |
+
def findDecl' (n : Name) (decls : Array Decl) : CompilerM (Option Decl) :=
|
| 220 |
+
return findEnvDecl' (← get).env n decls
|
| 221 |
+
|
| 222 |
+
def containsDecl' (n : Name) (decls : Array Decl) : CompilerM Bool := do
|
| 223 |
+
if decls.any fun decl => decl.name == n then
|
| 224 |
+
return true
|
| 225 |
+
else
|
| 226 |
+
containsDecl n
|
| 227 |
+
|
| 228 |
+
def getDecl' (n : Name) (decls : Array Decl) : CompilerM Decl := do
|
| 229 |
+
let (some decl) ← findDecl' n decls | throw s!"unknown declaration '{n}'"
|
| 230 |
+
return decl
|
| 231 |
+
|
| 232 |
+
@[export lean_decl_get_sorry_dep]
|
| 233 |
+
def getSorryDep (env : Environment) (declName : Name) : Option Name :=
|
| 234 |
+
match findEnvDecl env declName with
|
| 235 |
+
| some (.fdecl (info := { sorryDep? := dep?, .. }) ..) => dep?
|
| 236 |
+
| _ => none
|
| 237 |
+
|
| 238 |
+
end IR
|
| 239 |
+
end Lean
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/ElimDeadBranches.lean
ADDED
|
@@ -0,0 +1,351 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2019 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Compiler.IR.Format
|
| 8 |
+
import Lean.Compiler.IR.Basic
|
| 9 |
+
import Lean.Compiler.IR.CompilerM
|
| 10 |
+
|
| 11 |
+
namespace Lean.IR.UnreachableBranches
|
| 12 |
+
|
| 13 |
+
/-- Value used in the abstract interpreter -/
|
| 14 |
+
inductive Value where
|
| 15 |
+
| bot -- undefined
|
| 16 |
+
| top -- any value
|
| 17 |
+
| ctor (i : CtorInfo) (vs : Array Value)
|
| 18 |
+
| choice (vs : List Value)
|
| 19 |
+
deriving Inhabited, Repr
|
| 20 |
+
|
| 21 |
+
protected partial def Value.toFormat : Value → Format
|
| 22 |
+
| Value.bot => "⊥"
|
| 23 |
+
| Value.top => "⊤"
|
| 24 |
+
| Value.ctor info vs =>
|
| 25 |
+
if vs.isEmpty then
|
| 26 |
+
format info.name
|
| 27 |
+
else
|
| 28 |
+
Format.paren <| format info.name ++ Std.Format.join (vs.toList.map fun v => " " ++ Value.toFormat v)
|
| 29 |
+
| Value.choice vs =>
|
| 30 |
+
Format.paren <| Std.Format.joinSep (vs.map Value.toFormat) " | "
|
| 31 |
+
|
| 32 |
+
instance : ToFormat Value where
|
| 33 |
+
format := Value.toFormat
|
| 34 |
+
|
| 35 |
+
instance : ToString Value where
|
| 36 |
+
toString v := toString (format v)
|
| 37 |
+
|
| 38 |
+
namespace Value
|
| 39 |
+
|
| 40 |
+
protected partial def beq : Value → Value → Bool
|
| 41 |
+
| bot, bot => true
|
| 42 |
+
| top, top => true
|
| 43 |
+
| ctor i₁ vs₁, ctor i₂ vs₂ => i₁ == i₂ && Array.isEqv vs₁ vs₂ Value.beq
|
| 44 |
+
| choice vs₁, choice vs₂ =>
|
| 45 |
+
vs₁.all (fun v₁ => vs₂.any fun v₂ => Value.beq v₁ v₂)
|
| 46 |
+
&&
|
| 47 |
+
vs₂.all (fun v₂ => vs₁.any fun v₁ => Value.beq v₁ v₂)
|
| 48 |
+
| _, _ => false
|
| 49 |
+
|
| 50 |
+
instance : BEq Value := ⟨Value.beq⟩
|
| 51 |
+
|
| 52 |
+
partial def addChoice (merge : Value → Value → Value) : List Value → Value → List Value
|
| 53 |
+
| [], v => [v]
|
| 54 |
+
| v₁@(ctor i₁ _) :: cs, v₂@(ctor i₂ _) =>
|
| 55 |
+
if i₁ == i₂ then merge v₁ v₂ :: cs
|
| 56 |
+
else v₁ :: addChoice merge cs v₂
|
| 57 |
+
| _, _ => panic! "invalid addChoice"
|
| 58 |
+
|
| 59 |
+
partial def merge (v₁ v₂ : Value) : Value :=
|
| 60 |
+
match v₁, v₂ with
|
| 61 |
+
| bot, v => v
|
| 62 |
+
| v, bot => v
|
| 63 |
+
| top, _ => top
|
| 64 |
+
| _, top => top
|
| 65 |
+
| v₁@(ctor i₁ vs₁), v₂@(ctor i₂ vs₂) =>
|
| 66 |
+
if i₁ == i₂ then ctor i₁ <| vs₁.size.fold (init := #[]) fun i _ r => r.push (merge vs₁[i] vs₂[i]!)
|
| 67 |
+
else choice [v₁, v₂]
|
| 68 |
+
| choice vs₁, choice vs₂ => choice <| vs₁.foldl (addChoice merge) vs₂
|
| 69 |
+
| choice vs, v => choice <| addChoice merge vs v
|
| 70 |
+
| v, choice vs => choice <| addChoice merge vs v
|
| 71 |
+
|
| 72 |
+
/--
|
| 73 |
+
In `truncate`, we approximate a value as `top` if depth > `truncateMaxDepth`.
|
| 74 |
+
TODO: add option to control this parameter.
|
| 75 |
+
-/
|
| 76 |
+
def truncateMaxDepth := 8
|
| 77 |
+
|
| 78 |
+
/--
|
| 79 |
+
Make sure constructors of recursive inductive datatypes can only occur once in each path.
|
| 80 |
+
Values at depth > truncateMaxDepth are also approximated at `top`.
|
| 81 |
+
We use this function this function to implement a simple widening operation for our abstract
|
| 82 |
+
interpreter.
|
| 83 |
+
Recall the widening functions is used to ensure termination in abstract interpreters.
|
| 84 |
+
-/
|
| 85 |
+
partial def truncate (env : Environment) (v : Value) (s : NameSet) : Value :=
|
| 86 |
+
go v s truncateMaxDepth
|
| 87 |
+
where
|
| 88 |
+
go (v : Value) (s : NameSet) (depth : Nat) : Value :=
|
| 89 |
+
match depth with
|
| 90 |
+
| 0 => top
|
| 91 |
+
| depth+1 =>
|
| 92 |
+
match v, s with
|
| 93 |
+
| ctor i vs, found =>
|
| 94 |
+
let I := i.name.getPrefix
|
| 95 |
+
if found.contains I then
|
| 96 |
+
top
|
| 97 |
+
else
|
| 98 |
+
let cont (found' : NameSet) : Value :=
|
| 99 |
+
ctor i (vs.map fun v => go v found' depth)
|
| 100 |
+
match env.find? I with
|
| 101 |
+
| some (ConstantInfo.inductInfo d) =>
|
| 102 |
+
if d.isRec then cont (found.insert I)
|
| 103 |
+
else cont found
|
| 104 |
+
| _ => cont found
|
| 105 |
+
| choice vs, found =>
|
| 106 |
+
let newVs := vs.map fun v => go v found depth
|
| 107 |
+
if newVs.elem top then top
|
| 108 |
+
else choice newVs
|
| 109 |
+
| v, _ => v
|
| 110 |
+
|
| 111 |
+
/-- Widening operator that guarantees termination in our abstract interpreter. -/
|
| 112 |
+
def widening (env : Environment) (v₁ v₂ : Value) : Value :=
|
| 113 |
+
truncate env (merge v₁ v₂) {}
|
| 114 |
+
|
| 115 |
+
end Value
|
| 116 |
+
|
| 117 |
+
abbrev FunctionSummaries := PHashMap FunId Value
|
| 118 |
+
|
| 119 |
+
private abbrev declLt (a b : FunId × Value) :=
|
| 120 |
+
Name.quickLt a.1 b.1
|
| 121 |
+
|
| 122 |
+
private abbrev sortEntries (entries : Array (FunId × Value)) : Array (FunId × Value) :=
|
| 123 |
+
entries.qsort declLt
|
| 124 |
+
|
| 125 |
+
private abbrev findAtSorted? (entries : Array (FunId × Value)) (fid : FunId) : Option Value :=
|
| 126 |
+
if let some (_, value) := entries.binSearch (fid, default) declLt then
|
| 127 |
+
some value
|
| 128 |
+
else
|
| 129 |
+
none
|
| 130 |
+
|
| 131 |
+
builtin_initialize functionSummariesExt : SimplePersistentEnvExtension (FunId × Value) FunctionSummaries ←
|
| 132 |
+
registerSimplePersistentEnvExtension {
|
| 133 |
+
addImportedFn := fun _ => {}
|
| 134 |
+
addEntryFn := fun s ⟨e, n⟩ => s.insert e n
|
| 135 |
+
toArrayFn := fun s => sortEntries s.toArray
|
| 136 |
+
asyncMode := .sync -- compilation is non-parallel anyway
|
| 137 |
+
replay? := some <| SimplePersistentEnvExtension.replayOfFilter (!·.contains ·.1) (fun s ⟨e, n⟩ => s.insert e n)
|
| 138 |
+
}
|
| 139 |
+
|
| 140 |
+
def addFunctionSummary (env : Environment) (fid : FunId) (v : Value) : Environment :=
|
| 141 |
+
functionSummariesExt.addEntry (env.addExtraName fid) (fid, v)
|
| 142 |
+
|
| 143 |
+
def getFunctionSummary? (env : Environment) (fid : FunId) : Option Value :=
|
| 144 |
+
match env.getModuleIdxFor? fid with
|
| 145 |
+
| some modIdx => findAtSorted? (functionSummariesExt.getModuleEntries env modIdx) fid
|
| 146 |
+
| none => functionSummariesExt.getState env |>.find? fid
|
| 147 |
+
|
| 148 |
+
abbrev Assignment := Std.HashMap VarId Value
|
| 149 |
+
|
| 150 |
+
structure InterpContext where
|
| 151 |
+
currFnIdx : Nat := 0
|
| 152 |
+
decls : Array Decl
|
| 153 |
+
env : Environment
|
| 154 |
+
lctx : LocalContext := {}
|
| 155 |
+
|
| 156 |
+
structure InterpState where
|
| 157 |
+
assignments : Array Assignment
|
| 158 |
+
funVals : PArray Value -- we take snapshots during fixpoint computations
|
| 159 |
+
visitedJps : Array (Std.HashSet JoinPointId)
|
| 160 |
+
|
| 161 |
+
abbrev M := ReaderT InterpContext (StateM InterpState)
|
| 162 |
+
|
| 163 |
+
open Value
|
| 164 |
+
|
| 165 |
+
def findVarValue (x : VarId) : M Value := do
|
| 166 |
+
let ctx ← read
|
| 167 |
+
let s ← get
|
| 168 |
+
let assignment := s.assignments[ctx.currFnIdx]!
|
| 169 |
+
return assignment.getD x bot
|
| 170 |
+
|
| 171 |
+
def findArgValue (arg : Arg) : M Value :=
|
| 172 |
+
match arg with
|
| 173 |
+
| Arg.var x => findVarValue x
|
| 174 |
+
| _ => pure top
|
| 175 |
+
|
| 176 |
+
def updateVarAssignment (x : VarId) (v : Value) : M Unit := do
|
| 177 |
+
let v' ← findVarValue x
|
| 178 |
+
let ctx ← read
|
| 179 |
+
modify fun s => { s with assignments := s.assignments.modify ctx.currFnIdx fun a => a.insert x (merge v v') }
|
| 180 |
+
|
| 181 |
+
def resetVarAssignment (x : VarId) : M Unit := do
|
| 182 |
+
let ctx ← read
|
| 183 |
+
modify fun s => { s with assignments := s.assignments.modify ctx.currFnIdx fun a => a.insert x Value.bot }
|
| 184 |
+
|
| 185 |
+
def resetParamAssignment (y : Param) : M Unit :=
|
| 186 |
+
resetVarAssignment y.x
|
| 187 |
+
|
| 188 |
+
partial def projValue : Value → Nat → Value
|
| 189 |
+
| ctor _ vs, i => vs.getD i bot
|
| 190 |
+
| choice vs, i => vs.foldl (fun r v => merge r (projValue v i)) bot
|
| 191 |
+
| v, _ => v
|
| 192 |
+
|
| 193 |
+
def interpExpr : Expr → M Value
|
| 194 |
+
| Expr.ctor i ys => return ctor i (← ys.mapM fun y => findArgValue y)
|
| 195 |
+
| Expr.proj i x => return projValue (← findVarValue x) i
|
| 196 |
+
| Expr.fap fid _ => do
|
| 197 |
+
let ctx ← read
|
| 198 |
+
match getFunctionSummary? ctx.env fid with
|
| 199 |
+
| some v => pure v
|
| 200 |
+
| none => do
|
| 201 |
+
let s ← get
|
| 202 |
+
match ctx.decls.findIdx? (fun decl => decl.name == fid) with
|
| 203 |
+
| some idx => pure s.funVals[idx]!
|
| 204 |
+
| none => pure top
|
| 205 |
+
| _ => pure top
|
| 206 |
+
|
| 207 |
+
partial def containsCtor : Value → CtorInfo → Bool
|
| 208 |
+
| top, _ => true
|
| 209 |
+
| ctor i _, j => i == j
|
| 210 |
+
| choice vs, j => vs.any fun v => containsCtor v j
|
| 211 |
+
| _, _ => false
|
| 212 |
+
|
| 213 |
+
def updateCurrFnSummary (v : Value) : M Unit := do
|
| 214 |
+
let ctx ← read
|
| 215 |
+
let currFnIdx := ctx.currFnIdx
|
| 216 |
+
modify fun s => { s with funVals := s.funVals.modify currFnIdx (fun v' => widening ctx.env v v') }
|
| 217 |
+
|
| 218 |
+
def markJPVisited (j : JoinPointId) : M Bool := do
|
| 219 |
+
let currFnIdx := (← read).currFnIdx
|
| 220 |
+
modifyGet fun s =>
|
| 221 |
+
⟨!(s.visitedJps[currFnIdx]!.contains j),
|
| 222 |
+
{ s with visitedJps := s.visitedJps.modify currFnIdx fun a => a.insert j }⟩
|
| 223 |
+
|
| 224 |
+
/-- Return true if the assignment of at least one parameter has been updated. -/
|
| 225 |
+
def updateJPParamsAssignment (j : JoinPointId) (ys : Array Param) (xs : Array Arg) : M Bool := do
|
| 226 |
+
let ctx ← read
|
| 227 |
+
let currFnIdx := ctx.currFnIdx
|
| 228 |
+
let isFirstVisit ← markJPVisited j
|
| 229 |
+
ys.size.foldM (init := isFirstVisit) fun i _ r => do
|
| 230 |
+
let y := ys[i]
|
| 231 |
+
let x := xs[i]!
|
| 232 |
+
let yVal ← findVarValue y.x
|
| 233 |
+
let xVal ← findArgValue x
|
| 234 |
+
let newVal := merge yVal xVal
|
| 235 |
+
if newVal == yVal then
|
| 236 |
+
pure r
|
| 237 |
+
else
|
| 238 |
+
modify fun s => { s with assignments := s.assignments.modify currFnIdx fun a => a.insert y.x newVal }
|
| 239 |
+
pure true
|
| 240 |
+
|
| 241 |
+
private partial def resetNestedJPParams : FnBody → M Unit
|
| 242 |
+
| FnBody.jdecl _ ys _ k => do
|
| 243 |
+
ys.forM resetParamAssignment
|
| 244 |
+
/- Remark we don't need to reset the parameters of joint-points
|
| 245 |
+
nested in `b` since they will be reset if this JP is used. -/
|
| 246 |
+
resetNestedJPParams k
|
| 247 |
+
| FnBody.case _ _ _ alts =>
|
| 248 |
+
alts.forM fun alt => match alt with
|
| 249 |
+
| Alt.ctor _ b => resetNestedJPParams b
|
| 250 |
+
| Alt.default b => resetNestedJPParams b
|
| 251 |
+
| e => do unless e.isTerminal do resetNestedJPParams e.body
|
| 252 |
+
|
| 253 |
+
partial def interpFnBody : FnBody → M Unit
|
| 254 |
+
| FnBody.vdecl x _ e b => do
|
| 255 |
+
let v ← interpExpr e
|
| 256 |
+
updateVarAssignment x v
|
| 257 |
+
interpFnBody b
|
| 258 |
+
| FnBody.jdecl j ys v b =>
|
| 259 |
+
withReader (fun ctx => { ctx with lctx := ctx.lctx.addJP j ys v }) do
|
| 260 |
+
interpFnBody b
|
| 261 |
+
| FnBody.case _ x _ alts => do
|
| 262 |
+
let v ← findVarValue x
|
| 263 |
+
alts.forM fun alt => do
|
| 264 |
+
match alt with
|
| 265 |
+
| Alt.ctor i b => if containsCtor v i then interpFnBody b
|
| 266 |
+
| Alt.default b => interpFnBody b
|
| 267 |
+
| FnBody.ret x => do
|
| 268 |
+
let v ← findArgValue x
|
| 269 |
+
updateCurrFnSummary v
|
| 270 |
+
| FnBody.jmp j xs => do
|
| 271 |
+
let ctx ← read
|
| 272 |
+
let ys := (ctx.lctx.getJPParams j).get!
|
| 273 |
+
let b := (ctx.lctx.getJPBody j).get!
|
| 274 |
+
let updated ← updateJPParamsAssignment j ys xs
|
| 275 |
+
if updated then
|
| 276 |
+
-- We must reset the value of nested join-point parameters since they depend on `ys` values
|
| 277 |
+
resetNestedJPParams b
|
| 278 |
+
interpFnBody b
|
| 279 |
+
| e => do
|
| 280 |
+
unless e.isTerminal do
|
| 281 |
+
interpFnBody e.body
|
| 282 |
+
|
| 283 |
+
def inferStep : M Bool := do
|
| 284 |
+
let ctx ← read
|
| 285 |
+
modify fun s => { s with assignments := ctx.decls.map fun _ => {},
|
| 286 |
+
visitedJps := ctx.decls.map fun _ => {} }
|
| 287 |
+
ctx.decls.size.foldM (init := false) fun idx _ modified => do
|
| 288 |
+
match ctx.decls[idx] with
|
| 289 |
+
| .fdecl (xs := ys) (body := b) .. => do
|
| 290 |
+
let s ← get
|
| 291 |
+
let currVals := s.funVals[idx]!
|
| 292 |
+
withReader (fun ctx => { ctx with currFnIdx := idx }) do
|
| 293 |
+
ys.forM fun y => updateVarAssignment y.x top
|
| 294 |
+
interpFnBody b
|
| 295 |
+
let s ← get
|
| 296 |
+
let newVals := s.funVals[idx]!
|
| 297 |
+
pure (modified || currVals != newVals)
|
| 298 |
+
| .extern .. => do
|
| 299 |
+
let currVals := (← get).funVals[idx]!
|
| 300 |
+
updateCurrFnSummary .top
|
| 301 |
+
let newVals := (← get).funVals[idx]!
|
| 302 |
+
pure (modified || currVals != newVals)
|
| 303 |
+
|
| 304 |
+
partial def inferMain : M Unit := do
|
| 305 |
+
let modified ← inferStep
|
| 306 |
+
if modified then inferMain else pure ()
|
| 307 |
+
|
| 308 |
+
partial def elimDeadAux (assignment : Assignment) : FnBody → FnBody
|
| 309 |
+
| FnBody.vdecl x t e b => FnBody.vdecl x t e (elimDeadAux assignment b)
|
| 310 |
+
| FnBody.jdecl j ys v b => FnBody.jdecl j ys (elimDeadAux assignment v) (elimDeadAux assignment b)
|
| 311 |
+
| FnBody.case tid x xType alts =>
|
| 312 |
+
let v := assignment.getD x bot
|
| 313 |
+
let alts := alts.map fun alt =>
|
| 314 |
+
match alt with
|
| 315 |
+
| Alt.ctor i b => Alt.ctor i <| if containsCtor v i then elimDeadAux assignment b else FnBody.unreachable
|
| 316 |
+
| Alt.default b => Alt.default (elimDeadAux assignment b)
|
| 317 |
+
FnBody.case tid x xType alts
|
| 318 |
+
| e =>
|
| 319 |
+
if e.isTerminal then e
|
| 320 |
+
else
|
| 321 |
+
let (instr, b) := e.split
|
| 322 |
+
let b := elimDeadAux assignment b
|
| 323 |
+
instr.setBody b
|
| 324 |
+
|
| 325 |
+
partial def elimDead (assignment : Assignment) (d : Decl) : Decl :=
|
| 326 |
+
match d with
|
| 327 |
+
| .fdecl (body := b) .. => d.updateBody! <| elimDeadAux assignment b
|
| 328 |
+
| other => other
|
| 329 |
+
|
| 330 |
+
end UnreachableBranches
|
| 331 |
+
|
| 332 |
+
open UnreachableBranches
|
| 333 |
+
|
| 334 |
+
def elimDeadBranches (decls : Array Decl) : CompilerM (Array Decl) := do
|
| 335 |
+
let s ← get
|
| 336 |
+
let env := s.env
|
| 337 |
+
let assignments : Array Assignment := decls.map fun _ => {}
|
| 338 |
+
let funVals := mkPArray decls.size Value.bot
|
| 339 |
+
let visitedJps := decls.map fun _ => {}
|
| 340 |
+
let ctx : InterpContext := { decls := decls, env := env }
|
| 341 |
+
let s : InterpState := { assignments, funVals, visitedJps }
|
| 342 |
+
let (_, s) := (inferMain ctx).run s
|
| 343 |
+
let funVals := s.funVals
|
| 344 |
+
let assignments := s.assignments
|
| 345 |
+
modify fun s =>
|
| 346 |
+
let env := decls.size.fold (init := s.env) fun i _ env =>
|
| 347 |
+
addFunctionSummary env decls[i].name funVals[i]!
|
| 348 |
+
{ s with env := env }
|
| 349 |
+
return decls.mapIdx fun i decl => elimDead assignments[i]! decl
|
| 350 |
+
|
| 351 |
+
end Lean.IR
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/ElimDeadVars.lean
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2019 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Compiler.IR.Basic
|
| 8 |
+
import Lean.Compiler.IR.FreeVars
|
| 9 |
+
|
| 10 |
+
namespace Lean.IR
|
| 11 |
+
|
| 12 |
+
partial def reshapeWithoutDead (bs : Array FnBody) (term : FnBody) : FnBody :=
|
| 13 |
+
let rec reshape (bs : Array FnBody) (b : FnBody) (used : IndexSet) :=
|
| 14 |
+
if bs.isEmpty then b
|
| 15 |
+
else
|
| 16 |
+
let curr := bs.back!
|
| 17 |
+
let bs := bs.pop
|
| 18 |
+
let keep (_ : Unit) :=
|
| 19 |
+
let used := curr.collectFreeIndices used
|
| 20 |
+
let b := curr.setBody b
|
| 21 |
+
reshape bs b used
|
| 22 |
+
let keepIfUsed (vidx : Index) :=
|
| 23 |
+
if used.contains vidx then keep ()
|
| 24 |
+
else reshape bs b used
|
| 25 |
+
match curr with
|
| 26 |
+
| FnBody.vdecl x _ _ _ => keepIfUsed x.idx
|
| 27 |
+
-- TODO: we should keep all struct/union projections because they are used to ensure struct/union values are fully consumed.
|
| 28 |
+
| FnBody.jdecl j _ _ _ => keepIfUsed j.idx
|
| 29 |
+
| _ => keep ()
|
| 30 |
+
reshape bs term term.freeIndices
|
| 31 |
+
|
| 32 |
+
partial def FnBody.elimDead (b : FnBody) : FnBody :=
|
| 33 |
+
let (bs, term) := b.flatten
|
| 34 |
+
let bs := modifyJPs bs elimDead
|
| 35 |
+
let term := match term with
|
| 36 |
+
| FnBody.case tid x xType alts =>
|
| 37 |
+
let alts := alts.map fun alt => alt.modifyBody elimDead
|
| 38 |
+
FnBody.case tid x xType alts
|
| 39 |
+
| other => other
|
| 40 |
+
reshapeWithoutDead bs term
|
| 41 |
+
|
| 42 |
+
/-- Eliminate dead let-declarations and join points -/
|
| 43 |
+
def Decl.elimDead (d : Decl) : Decl :=
|
| 44 |
+
match d with
|
| 45 |
+
| .fdecl (body := b) .. => d.updateBody! b.elimDead
|
| 46 |
+
| other => other
|
| 47 |
+
|
| 48 |
+
end Lean.IR
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/EmitC.lean
ADDED
|
@@ -0,0 +1,775 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2019 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Runtime
|
| 8 |
+
import Lean.Compiler.NameMangling
|
| 9 |
+
import Lean.Compiler.ExportAttr
|
| 10 |
+
import Lean.Compiler.InitAttr
|
| 11 |
+
import Lean.Compiler.IR.CompilerM
|
| 12 |
+
import Lean.Compiler.IR.EmitUtil
|
| 13 |
+
import Lean.Compiler.IR.NormIds
|
| 14 |
+
import Lean.Compiler.IR.SimpCase
|
| 15 |
+
import Lean.Compiler.IR.Boxing
|
| 16 |
+
|
| 17 |
+
namespace Lean.IR.EmitC
|
| 18 |
+
open ExplicitBoxing (requiresBoxedVersion mkBoxedName isBoxedName)
|
| 19 |
+
|
| 20 |
+
def leanMainFn := "_lean_main"
|
| 21 |
+
|
| 22 |
+
structure Context where
|
| 23 |
+
env : Environment
|
| 24 |
+
modName : Name
|
| 25 |
+
jpMap : JPParamsMap := {}
|
| 26 |
+
mainFn : FunId := default
|
| 27 |
+
mainParams : Array Param := #[]
|
| 28 |
+
|
| 29 |
+
abbrev M := ReaderT Context (EStateM String String)
|
| 30 |
+
|
| 31 |
+
def getEnv : M Environment := Context.env <$> read
|
| 32 |
+
def getModName : M Name := Context.modName <$> read
|
| 33 |
+
def getDecl (n : Name) : M Decl := do
|
| 34 |
+
let env ← getEnv
|
| 35 |
+
match findEnvDecl env n with
|
| 36 |
+
| some d => pure d
|
| 37 |
+
| none => throw s!"unknown declaration '{n}'"
|
| 38 |
+
|
| 39 |
+
@[inline] def emit {α : Type} [ToString α] (a : α) : M Unit :=
|
| 40 |
+
modify fun out => out ++ toString a
|
| 41 |
+
|
| 42 |
+
@[inline] def emitLn {α : Type} [ToString α] (a : α) : M Unit := do
|
| 43 |
+
emit a; emit "\n"
|
| 44 |
+
|
| 45 |
+
def emitLns {α : Type} [ToString α] (as : List α) : M Unit :=
|
| 46 |
+
as.forM fun a => emitLn a
|
| 47 |
+
|
| 48 |
+
def argToCString (x : Arg) : String :=
|
| 49 |
+
match x with
|
| 50 |
+
| Arg.var x => toString x
|
| 51 |
+
| _ => "lean_box(0)"
|
| 52 |
+
|
| 53 |
+
def emitArg (x : Arg) : M Unit :=
|
| 54 |
+
emit (argToCString x)
|
| 55 |
+
|
| 56 |
+
def toCType : IRType → String
|
| 57 |
+
| IRType.float => "double"
|
| 58 |
+
| IRType.float32 => "float"
|
| 59 |
+
| IRType.uint8 => "uint8_t"
|
| 60 |
+
| IRType.uint16 => "uint16_t"
|
| 61 |
+
| IRType.uint32 => "uint32_t"
|
| 62 |
+
| IRType.uint64 => "uint64_t"
|
| 63 |
+
| IRType.usize => "size_t"
|
| 64 |
+
| IRType.object => "lean_object*"
|
| 65 |
+
| IRType.tobject => "lean_object*"
|
| 66 |
+
| IRType.irrelevant => "lean_object*"
|
| 67 |
+
| IRType.struct _ _ => panic! "not implemented yet"
|
| 68 |
+
| IRType.union _ _ => panic! "not implemented yet"
|
| 69 |
+
|
| 70 |
+
def throwInvalidExportName {α : Type} (n : Name) : M α :=
|
| 71 |
+
throw s!"invalid export name '{n}'"
|
| 72 |
+
|
| 73 |
+
def toCName (n : Name) : M String := do
|
| 74 |
+
let env ← getEnv;
|
| 75 |
+
-- TODO: we should support simple export names only
|
| 76 |
+
match getExportNameFor? env n with
|
| 77 |
+
| some (.str .anonymous s) => pure s
|
| 78 |
+
| some _ => throwInvalidExportName n
|
| 79 |
+
| none => if n == `main then pure leanMainFn else pure n.mangle
|
| 80 |
+
|
| 81 |
+
def emitCName (n : Name) : M Unit :=
|
| 82 |
+
toCName n >>= emit
|
| 83 |
+
|
| 84 |
+
def toCInitName (n : Name) : M String := do
|
| 85 |
+
let env ← getEnv;
|
| 86 |
+
-- TODO: we should support simple export names only
|
| 87 |
+
match getExportNameFor? env n with
|
| 88 |
+
| some (.str .anonymous s) => return "_init_" ++ s
|
| 89 |
+
| some _ => throwInvalidExportName n
|
| 90 |
+
| none => pure ("_init_" ++ n.mangle)
|
| 91 |
+
|
| 92 |
+
def emitCInitName (n : Name) : M Unit :=
|
| 93 |
+
toCInitName n >>= emit
|
| 94 |
+
|
| 95 |
+
def emitFnDeclAux (decl : Decl) (cppBaseName : String) (isExternal : Bool) : M Unit := do
|
| 96 |
+
let ps := decl.params
|
| 97 |
+
let env ← getEnv
|
| 98 |
+
if ps.isEmpty then
|
| 99 |
+
if isClosedTermName env decl.name then emit "static "
|
| 100 |
+
else if isExternal then emit "extern "
|
| 101 |
+
else emit "LEAN_EXPORT "
|
| 102 |
+
else
|
| 103 |
+
if !isExternal then emit "LEAN_EXPORT "
|
| 104 |
+
emit (toCType decl.resultType ++ " " ++ cppBaseName)
|
| 105 |
+
unless ps.isEmpty do
|
| 106 |
+
emit "("
|
| 107 |
+
-- We omit irrelevant parameters for extern constants
|
| 108 |
+
let ps := if isExternC env decl.name then ps.filter (fun p => !p.ty.isIrrelevant) else ps
|
| 109 |
+
if ps.size > closureMaxArgs && isBoxedName decl.name then
|
| 110 |
+
emit "lean_object**"
|
| 111 |
+
else
|
| 112 |
+
ps.size.forM fun i _ => do
|
| 113 |
+
if i > 0 then emit ", "
|
| 114 |
+
emit (toCType ps[i].ty)
|
| 115 |
+
emit ")"
|
| 116 |
+
emitLn ";"
|
| 117 |
+
|
| 118 |
+
def emitFnDecl (decl : Decl) (isExternal : Bool) : M Unit := do
|
| 119 |
+
let cppBaseName ← toCName decl.name
|
| 120 |
+
emitFnDeclAux decl cppBaseName isExternal
|
| 121 |
+
|
| 122 |
+
def emitExternDeclAux (decl : Decl) (cNameStr : String) : M Unit := do
|
| 123 |
+
let env ← getEnv
|
| 124 |
+
let extC := isExternC env decl.name
|
| 125 |
+
emitFnDeclAux decl cNameStr extC
|
| 126 |
+
|
| 127 |
+
def emitFnDecls : M Unit := do
|
| 128 |
+
let env ← getEnv
|
| 129 |
+
let decls := getDecls env
|
| 130 |
+
let modDecls : NameSet := decls.foldl (fun s d => s.insert d.name) {}
|
| 131 |
+
let usedDecls : NameSet := decls.foldl (fun s d => collectUsedDecls env d (s.insert d.name)) {}
|
| 132 |
+
let usedDecls := usedDecls.toList
|
| 133 |
+
usedDecls.forM fun n => do
|
| 134 |
+
let decl ← getDecl n;
|
| 135 |
+
match getExternNameFor env `c decl.name with
|
| 136 |
+
| some cName => emitExternDeclAux decl cName
|
| 137 |
+
| none => emitFnDecl decl (!modDecls.contains n)
|
| 138 |
+
|
| 139 |
+
def emitMainFn : M Unit := do
|
| 140 |
+
let d ← getDecl `main
|
| 141 |
+
match d with
|
| 142 |
+
| .fdecl (xs := xs) .. => do
|
| 143 |
+
unless xs.size == 2 || xs.size == 1 do throw "invalid main function, incorrect arity when generating code"
|
| 144 |
+
let env ← getEnv
|
| 145 |
+
let usesLeanAPI := usesModuleFrom env `Lean
|
| 146 |
+
emitLn "char ** lean_setup_args(int argc, char ** argv);";
|
| 147 |
+
if usesLeanAPI then
|
| 148 |
+
emitLn "void lean_initialize();"
|
| 149 |
+
else
|
| 150 |
+
emitLn "void lean_initialize_runtime_module();";
|
| 151 |
+
emitLn "
|
| 152 |
+
#if defined(WIN32) || defined(_WIN32)
|
| 153 |
+
#include <windows.h>
|
| 154 |
+
#endif
|
| 155 |
+
|
| 156 |
+
int main(int argc, char ** argv) {
|
| 157 |
+
#if defined(WIN32) || defined(_WIN32)
|
| 158 |
+
SetErrorMode(SEM_FAILCRITICALERRORS);
|
| 159 |
+
SetConsoleOutputCP(CP_UTF8);
|
| 160 |
+
#endif
|
| 161 |
+
lean_object* in; lean_object* res;";
|
| 162 |
+
emitLn "argv = lean_setup_args(argc, argv);";
|
| 163 |
+
if usesLeanAPI then
|
| 164 |
+
emitLn "lean_initialize();"
|
| 165 |
+
else
|
| 166 |
+
emitLn "lean_initialize_runtime_module();"
|
| 167 |
+
let modName ← getModName
|
| 168 |
+
/- We disable panic messages because they do not mesh well with extracted closed terms.
|
| 169 |
+
See issue #534. We can remove this workaround after we implement issue #467. -/
|
| 170 |
+
emitLn "lean_set_panic_messages(false);"
|
| 171 |
+
emitLn ("res = " ++ mkModuleInitializationFunctionName modName ++ "(1 /* builtin */, lean_io_mk_world());")
|
| 172 |
+
emitLn "lean_set_panic_messages(true);"
|
| 173 |
+
emitLns ["lean_io_mark_end_initialization();",
|
| 174 |
+
"if (lean_io_result_is_ok(res)) {",
|
| 175 |
+
"lean_dec_ref(res);",
|
| 176 |
+
"lean_init_task_manager();"];
|
| 177 |
+
if xs.size == 2 then
|
| 178 |
+
emitLns ["in = lean_box(0);",
|
| 179 |
+
"int i = argc;",
|
| 180 |
+
"while (i > 1) {",
|
| 181 |
+
" lean_object* n;",
|
| 182 |
+
" i--;",
|
| 183 |
+
" n = lean_alloc_ctor(1,2,0); lean_ctor_set(n, 0, lean_mk_string(argv[i])); lean_ctor_set(n, 1, in);",
|
| 184 |
+
" in = n;",
|
| 185 |
+
"}"]
|
| 186 |
+
emitLn ("res = " ++ leanMainFn ++ "(in, lean_io_mk_world());")
|
| 187 |
+
else
|
| 188 |
+
emitLn ("res = " ++ leanMainFn ++ "(lean_io_mk_world());")
|
| 189 |
+
emitLn "}"
|
| 190 |
+
-- `IO _`
|
| 191 |
+
let retTy := env.find? `main |>.get! |>.type |>.getForallBody
|
| 192 |
+
-- either `UInt32` or `(P)Unit`
|
| 193 |
+
let retTy := retTy.appArg!
|
| 194 |
+
-- finalize at least the task manager to avoid leak sanitizer false positives from tasks outliving the main thread
|
| 195 |
+
emitLns ["lean_finalize_task_manager();",
|
| 196 |
+
"if (lean_io_result_is_ok(res)) {",
|
| 197 |
+
" int ret = " ++ if retTy.constName? == some ``UInt32 then "lean_unbox_uint32(lean_io_result_get_value(res));" else "0;",
|
| 198 |
+
" lean_dec_ref(res);",
|
| 199 |
+
" return ret;",
|
| 200 |
+
"} else {",
|
| 201 |
+
" lean_io_result_show_error(res);",
|
| 202 |
+
" lean_dec_ref(res);",
|
| 203 |
+
" return 1;",
|
| 204 |
+
"}"]
|
| 205 |
+
emitLn "}"
|
| 206 |
+
| _ => throw "function declaration expected"
|
| 207 |
+
|
| 208 |
+
def hasMainFn : M Bool := do
|
| 209 |
+
let env ← getEnv
|
| 210 |
+
let decls := getDecls env
|
| 211 |
+
return decls.any (fun d => d.name == `main)
|
| 212 |
+
|
| 213 |
+
def emitMainFnIfNeeded : M Unit := do
|
| 214 |
+
if (← hasMainFn) then emitMainFn
|
| 215 |
+
|
| 216 |
+
def emitFileHeader : M Unit := do
|
| 217 |
+
let env ← getEnv
|
| 218 |
+
let modName ← getModName
|
| 219 |
+
emitLn "// Lean compiler output"
|
| 220 |
+
emitLn ("// Module: " ++ toString modName)
|
| 221 |
+
emit "// Imports:"
|
| 222 |
+
env.imports.forM fun m => emit (" " ++ toString m)
|
| 223 |
+
emitLn ""
|
| 224 |
+
emitLn "#include <lean/lean.h>"
|
| 225 |
+
emitLns [
|
| 226 |
+
"#if defined(__clang__)",
|
| 227 |
+
"#pragma clang diagnostic ignored \"-Wunused-parameter\"",
|
| 228 |
+
"#pragma clang diagnostic ignored \"-Wunused-label\"",
|
| 229 |
+
"#elif defined(__GNUC__) && !defined(__CLANG__)",
|
| 230 |
+
"#pragma GCC diagnostic ignored \"-Wunused-parameter\"",
|
| 231 |
+
"#pragma GCC diagnostic ignored \"-Wunused-label\"",
|
| 232 |
+
"#pragma GCC diagnostic ignored \"-Wunused-but-set-variable\"",
|
| 233 |
+
"#endif",
|
| 234 |
+
"#ifdef __cplusplus",
|
| 235 |
+
"extern \"C\" {",
|
| 236 |
+
"#endif"
|
| 237 |
+
]
|
| 238 |
+
|
| 239 |
+
def emitFileFooter : M Unit :=
|
| 240 |
+
emitLns [
|
| 241 |
+
"#ifdef __cplusplus",
|
| 242 |
+
"}",
|
| 243 |
+
"#endif"
|
| 244 |
+
]
|
| 245 |
+
|
| 246 |
+
def throwUnknownVar {α : Type} (x : VarId) : M α :=
|
| 247 |
+
throw s!"unknown variable '{x}'"
|
| 248 |
+
|
| 249 |
+
def getJPParams (j : JoinPointId) : M (Array Param) := do
|
| 250 |
+
let ctx ← read;
|
| 251 |
+
match ctx.jpMap[j]? with
|
| 252 |
+
| some ps => pure ps
|
| 253 |
+
| none => throw "unknown join point"
|
| 254 |
+
|
| 255 |
+
def declareVar (x : VarId) (t : IRType) : M Unit := do
|
| 256 |
+
emit (toCType t); emit " "; emit x; emit "; "
|
| 257 |
+
|
| 258 |
+
def declareParams (ps : Array Param) : M Unit :=
|
| 259 |
+
ps.forM fun p => declareVar p.x p.ty
|
| 260 |
+
|
| 261 |
+
partial def declareVars : FnBody → Bool → M Bool
|
| 262 |
+
| e@(FnBody.vdecl x t _ b), d => do
|
| 263 |
+
let ctx ← read
|
| 264 |
+
if isTailCallTo ctx.mainFn e then
|
| 265 |
+
pure d
|
| 266 |
+
else
|
| 267 |
+
declareVar x t; declareVars b true
|
| 268 |
+
| FnBody.jdecl _ xs _ b, d => do declareParams xs; declareVars b (d || xs.size > 0)
|
| 269 |
+
| e, d => if e.isTerminal then pure d else declareVars e.body d
|
| 270 |
+
|
| 271 |
+
def emitTag (x : VarId) (xType : IRType) : M Unit := do
|
| 272 |
+
if xType.isObj then do
|
| 273 |
+
emit "lean_obj_tag("; emit x; emit ")"
|
| 274 |
+
else
|
| 275 |
+
emit x
|
| 276 |
+
|
| 277 |
+
def isIf (alts : Array Alt) : Option (Nat × FnBody × FnBody) :=
|
| 278 |
+
if h : alts.size ≠ 2 then none
|
| 279 |
+
else match alts[0] with
|
| 280 |
+
| Alt.ctor c b => some (c.cidx, b, alts[1].body)
|
| 281 |
+
| _ => none
|
| 282 |
+
|
| 283 |
+
def emitInc (x : VarId) (n : Nat) (checkRef : Bool) : M Unit := do
|
| 284 |
+
emit $
|
| 285 |
+
if checkRef then (if n == 1 then "lean_inc" else "lean_inc_n")
|
| 286 |
+
else (if n == 1 then "lean_inc_ref" else "lean_inc_ref_n")
|
| 287 |
+
emit "("; emit x
|
| 288 |
+
if n != 1 then emit ", "; emit n
|
| 289 |
+
emitLn ");"
|
| 290 |
+
|
| 291 |
+
def emitDec (x : VarId) (n : Nat) (checkRef : Bool) : M Unit := do
|
| 292 |
+
emit (if checkRef then "lean_dec" else "lean_dec_ref");
|
| 293 |
+
emit "("; emit x;
|
| 294 |
+
if n != 1 then emit ", "; emit n
|
| 295 |
+
emitLn ");"
|
| 296 |
+
|
| 297 |
+
def emitDel (x : VarId) : M Unit := do
|
| 298 |
+
emit "lean_free_object("; emit x; emitLn ");"
|
| 299 |
+
|
| 300 |
+
def emitSetTag (x : VarId) (i : Nat) : M Unit := do
|
| 301 |
+
emit "lean_ctor_set_tag("; emit x; emit ", "; emit i; emitLn ");"
|
| 302 |
+
|
| 303 |
+
def emitSet (x : VarId) (i : Nat) (y : Arg) : M Unit := do
|
| 304 |
+
emit "lean_ctor_set("; emit x; emit ", "; emit i; emit ", "; emitArg y; emitLn ");"
|
| 305 |
+
|
| 306 |
+
def emitOffset (n : Nat) (offset : Nat) : M Unit := do
|
| 307 |
+
if n > 0 then
|
| 308 |
+
emit "sizeof(void*)*"; emit n;
|
| 309 |
+
if offset > 0 then emit " + "; emit offset
|
| 310 |
+
else
|
| 311 |
+
emit offset
|
| 312 |
+
|
| 313 |
+
def emitUSet (x : VarId) (n : Nat) (y : VarId) : M Unit := do
|
| 314 |
+
emit "lean_ctor_set_usize("; emit x; emit ", "; emit n; emit ", "; emit y; emitLn ");"
|
| 315 |
+
|
| 316 |
+
def emitSSet (x : VarId) (n : Nat) (offset : Nat) (y : VarId) (t : IRType) : M Unit := do
|
| 317 |
+
match t with
|
| 318 |
+
| IRType.float => emit "lean_ctor_set_float"
|
| 319 |
+
| IRType.float32 => emit "lean_ctor_set_float32"
|
| 320 |
+
| IRType.uint8 => emit "lean_ctor_set_uint8"
|
| 321 |
+
| IRType.uint16 => emit "lean_ctor_set_uint16"
|
| 322 |
+
| IRType.uint32 => emit "lean_ctor_set_uint32"
|
| 323 |
+
| IRType.uint64 => emit "lean_ctor_set_uint64"
|
| 324 |
+
| _ => throw "invalid instruction";
|
| 325 |
+
emit "("; emit x; emit ", "; emitOffset n offset; emit ", "; emit y; emitLn ");"
|
| 326 |
+
|
| 327 |
+
def emitJmp (j : JoinPointId) (xs : Array Arg) : M Unit := do
|
| 328 |
+
let ps ← getJPParams j
|
| 329 |
+
if h : xs.size = ps.size then
|
| 330 |
+
xs.size.forM fun i _ => do
|
| 331 |
+
let p := ps[i]
|
| 332 |
+
let x := xs[i]
|
| 333 |
+
emit p.x; emit " = "; emitArg x; emitLn ";"
|
| 334 |
+
emit "goto "; emit j; emitLn ";"
|
| 335 |
+
else
|
| 336 |
+
do throw "invalid goto"
|
| 337 |
+
|
| 338 |
+
def emitLhs (z : VarId) : M Unit := do
|
| 339 |
+
emit z; emit " = "
|
| 340 |
+
|
| 341 |
+
def emitArgs (ys : Array Arg) : M Unit :=
|
| 342 |
+
ys.size.forM fun i _ => do
|
| 343 |
+
if i > 0 then emit ", "
|
| 344 |
+
emitArg ys[i]
|
| 345 |
+
|
| 346 |
+
def emitCtorScalarSize (usize : Nat) (ssize : Nat) : M Unit := do
|
| 347 |
+
if usize == 0 then emit ssize
|
| 348 |
+
else if ssize == 0 then emit "sizeof(size_t)*"; emit usize
|
| 349 |
+
else emit "sizeof(size_t)*"; emit usize; emit " + "; emit ssize
|
| 350 |
+
|
| 351 |
+
def emitAllocCtor (c : CtorInfo) : M Unit := do
|
| 352 |
+
emit "lean_alloc_ctor("; emit c.cidx; emit ", "; emit c.size; emit ", "
|
| 353 |
+
emitCtorScalarSize c.usize c.ssize; emitLn ");"
|
| 354 |
+
|
| 355 |
+
def emitCtorSetArgs (z : VarId) (ys : Array Arg) : M Unit :=
|
| 356 |
+
ys.size.forM fun i _ => do
|
| 357 |
+
emit "lean_ctor_set("; emit z; emit ", "; emit i; emit ", "; emitArg ys[i]; emitLn ");"
|
| 358 |
+
|
| 359 |
+
def emitCtor (z : VarId) (c : CtorInfo) (ys : Array Arg) : M Unit := do
|
| 360 |
+
emitLhs z;
|
| 361 |
+
if c.size == 0 && c.usize == 0 && c.ssize == 0 then do
|
| 362 |
+
emit "lean_box("; emit c.cidx; emitLn ");"
|
| 363 |
+
else do
|
| 364 |
+
emitAllocCtor c; emitCtorSetArgs z ys
|
| 365 |
+
|
| 366 |
+
def emitReset (z : VarId) (n : Nat) (x : VarId) : M Unit := do
|
| 367 |
+
emit "if (lean_is_exclusive("; emit x; emitLn ")) {";
|
| 368 |
+
n.forM fun i _ => do
|
| 369 |
+
emit " lean_ctor_release("; emit x; emit ", "; emit i; emitLn ");"
|
| 370 |
+
emit " "; emitLhs z; emit x; emitLn ";";
|
| 371 |
+
emitLn "} else {";
|
| 372 |
+
emit " lean_dec_ref("; emit x; emitLn ");";
|
| 373 |
+
emit " "; emitLhs z; emitLn "lean_box(0);";
|
| 374 |
+
emitLn "}"
|
| 375 |
+
|
| 376 |
+
def emitReuse (z : VarId) (x : VarId) (c : CtorInfo) (updtHeader : Bool) (ys : Array Arg) : M Unit := do
|
| 377 |
+
emit "if (lean_is_scalar("; emit x; emitLn ")) {";
|
| 378 |
+
emit " "; emitLhs z; emitAllocCtor c;
|
| 379 |
+
emitLn "} else {";
|
| 380 |
+
emit " "; emitLhs z; emit x; emitLn ";";
|
| 381 |
+
if updtHeader then emit " lean_ctor_set_tag("; emit z; emit ", "; emit c.cidx; emitLn ");"
|
| 382 |
+
emitLn "}";
|
| 383 |
+
emitCtorSetArgs z ys
|
| 384 |
+
|
| 385 |
+
def emitProj (z : VarId) (i : Nat) (x : VarId) : M Unit := do
|
| 386 |
+
emitLhs z; emit "lean_ctor_get("; emit x; emit ", "; emit i; emitLn ");"
|
| 387 |
+
|
| 388 |
+
def emitUProj (z : VarId) (i : Nat) (x : VarId) : M Unit := do
|
| 389 |
+
emitLhs z; emit "lean_ctor_get_usize("; emit x; emit ", "; emit i; emitLn ");"
|
| 390 |
+
|
| 391 |
+
def emitSProj (z : VarId) (t : IRType) (n offset : Nat) (x : VarId) : M Unit := do
|
| 392 |
+
emitLhs z;
|
| 393 |
+
match t with
|
| 394 |
+
| IRType.float => emit "lean_ctor_get_float"
|
| 395 |
+
| IRType.float32 => emit "lean_ctor_get_float32"
|
| 396 |
+
| IRType.uint8 => emit "lean_ctor_get_uint8"
|
| 397 |
+
| IRType.uint16 => emit "lean_ctor_get_uint16"
|
| 398 |
+
| IRType.uint32 => emit "lean_ctor_get_uint32"
|
| 399 |
+
| IRType.uint64 => emit "lean_ctor_get_uint64"
|
| 400 |
+
| _ => throw "invalid instruction"
|
| 401 |
+
emit "("; emit x; emit ", "; emitOffset n offset; emitLn ");"
|
| 402 |
+
|
| 403 |
+
def toStringArgs (ys : Array Arg) : List String :=
|
| 404 |
+
ys.toList.map argToCString
|
| 405 |
+
|
| 406 |
+
def emitSimpleExternalCall (f : String) (ps : Array Param) (ys : Array Arg) : M Unit := do
|
| 407 |
+
emit f; emit "("
|
| 408 |
+
-- We must remove irrelevant arguments to extern calls.
|
| 409 |
+
discard <| ys.size.foldM
|
| 410 |
+
(fun i _ (first : Bool) =>
|
| 411 |
+
if ps[i]!.ty.isIrrelevant then
|
| 412 |
+
pure first
|
| 413 |
+
else do
|
| 414 |
+
unless first do emit ", "
|
| 415 |
+
emitArg ys[i]
|
| 416 |
+
pure false)
|
| 417 |
+
true
|
| 418 |
+
emitLn ");"
|
| 419 |
+
pure ()
|
| 420 |
+
|
| 421 |
+
def emitExternCall (f : FunId) (ps : Array Param) (extData : ExternAttrData) (ys : Array Arg) : M Unit :=
|
| 422 |
+
match getExternEntryFor extData `c with
|
| 423 |
+
| some (ExternEntry.standard _ extFn) => emitSimpleExternalCall extFn ps ys
|
| 424 |
+
| some (ExternEntry.inline _ pat) => do emit (expandExternPattern pat (toStringArgs ys)); emitLn ";"
|
| 425 |
+
| _ => throw s!"failed to emit extern application '{f}'"
|
| 426 |
+
|
| 427 |
+
def emitFullApp (z : VarId) (f : FunId) (ys : Array Arg) : M Unit := do
|
| 428 |
+
emitLhs z
|
| 429 |
+
let decl ← getDecl f
|
| 430 |
+
match decl with
|
| 431 |
+
| .fdecl .. | .extern _ _ _ { entries := [.opaque _], .. } =>
|
| 432 |
+
emitCName f
|
| 433 |
+
if ys.size > 0 then emit "("; emitArgs ys; emit ")"
|
| 434 |
+
emitLn ";"
|
| 435 |
+
| Decl.extern _ ps _ extData => emitExternCall f ps extData ys
|
| 436 |
+
|
| 437 |
+
def emitPartialApp (z : VarId) (f : FunId) (ys : Array Arg) : M Unit := do
|
| 438 |
+
let decl ← getDecl f
|
| 439 |
+
let arity := decl.params.size;
|
| 440 |
+
emitLhs z; emit "lean_alloc_closure((void*)("; emitCName f; emit "), "; emit arity; emit ", "; emit ys.size; emitLn ");";
|
| 441 |
+
ys.size.forM fun i _ => do
|
| 442 |
+
let y := ys[i]
|
| 443 |
+
emit "lean_closure_set("; emit z; emit ", "; emit i; emit ", "; emitArg y; emitLn ");"
|
| 444 |
+
|
| 445 |
+
def emitApp (z : VarId) (f : VarId) (ys : Array Arg) : M Unit :=
|
| 446 |
+
if ys.size > closureMaxArgs then do
|
| 447 |
+
emit "{ lean_object* _aargs[] = {"; emitArgs ys; emitLn "};";
|
| 448 |
+
emitLhs z; emit "lean_apply_m("; emit f; emit ", "; emit ys.size; emitLn ", _aargs); }"
|
| 449 |
+
else do
|
| 450 |
+
emitLhs z; emit "lean_apply_"; emit ys.size; emit "("; emit f; emit ", "; emitArgs ys; emitLn ");"
|
| 451 |
+
|
| 452 |
+
def emitBoxFn (xType : IRType) : M Unit :=
|
| 453 |
+
match xType with
|
| 454 |
+
| IRType.usize => emit "lean_box_usize"
|
| 455 |
+
| IRType.uint32 => emit "lean_box_uint32"
|
| 456 |
+
| IRType.uint64 => emit "lean_box_uint64"
|
| 457 |
+
| IRType.float => emit "lean_box_float"
|
| 458 |
+
| IRType.float32 => emit "lean_box_float32"
|
| 459 |
+
| _ => emit "lean_box"
|
| 460 |
+
|
| 461 |
+
def emitBox (z : VarId) (x : VarId) (xType : IRType) : M Unit := do
|
| 462 |
+
emitLhs z; emitBoxFn xType; emit "("; emit x; emitLn ");"
|
| 463 |
+
|
| 464 |
+
def emitUnbox (z : VarId) (t : IRType) (x : VarId) : M Unit := do
|
| 465 |
+
emitLhs z
|
| 466 |
+
emit (getUnboxOpName t)
|
| 467 |
+
emit "("; emit x; emitLn ");"
|
| 468 |
+
|
| 469 |
+
def emitIsShared (z : VarId) (x : VarId) : M Unit := do
|
| 470 |
+
emitLhs z; emit "!lean_is_exclusive("; emit x; emitLn ");"
|
| 471 |
+
|
| 472 |
+
def toHexDigit (c : Nat) : String :=
|
| 473 |
+
String.singleton c.digitChar
|
| 474 |
+
|
| 475 |
+
def quoteString (s : String) : String :=
|
| 476 |
+
let q := "\"";
|
| 477 |
+
let q := s.foldl
|
| 478 |
+
(fun q c => q ++
|
| 479 |
+
if c == '\n' then "\\n"
|
| 480 |
+
else if c == '\r' then "\\r"
|
| 481 |
+
else if c == '\t' then "\\t"
|
| 482 |
+
else if c == '\\' then "\\\\"
|
| 483 |
+
else if c == '\"' then "\\\""
|
| 484 |
+
else if c == '?' then "\\?" -- avoid trigraphs
|
| 485 |
+
else if c.toNat <= 31 then
|
| 486 |
+
"\\x" ++ toHexDigit (c.toNat / 16) ++ toHexDigit (c.toNat % 16)
|
| 487 |
+
-- TODO(Leo): we should use `\unnnn` for escaping unicode characters.
|
| 488 |
+
else String.singleton c)
|
| 489 |
+
q;
|
| 490 |
+
q ++ "\""
|
| 491 |
+
|
| 492 |
+
def emitNumLit (t : IRType) (v : Nat) : M Unit := do
|
| 493 |
+
if t.isObj then
|
| 494 |
+
if v < UInt32.size then
|
| 495 |
+
emit "lean_unsigned_to_nat("; emit v; emit "u)"
|
| 496 |
+
else
|
| 497 |
+
emit "lean_cstr_to_nat(\""; emit v; emit "\")"
|
| 498 |
+
else
|
| 499 |
+
if v < UInt32.size then
|
| 500 |
+
emit v
|
| 501 |
+
else if t == .usize then
|
| 502 |
+
emit "((size_t)"
|
| 503 |
+
emit v
|
| 504 |
+
emit "ULL)"
|
| 505 |
+
else
|
| 506 |
+
emit v
|
| 507 |
+
emit "ULL"
|
| 508 |
+
|
| 509 |
+
def emitLit (z : VarId) (t : IRType) (v : LitVal) : M Unit := do
|
| 510 |
+
emitLhs z;
|
| 511 |
+
match v with
|
| 512 |
+
| LitVal.num v => emitNumLit t v; emitLn ";"
|
| 513 |
+
| LitVal.str v =>
|
| 514 |
+
emit "lean_mk_string_unchecked(";
|
| 515 |
+
emit (quoteString v); emit ", ";
|
| 516 |
+
emit v.utf8ByteSize; emit ", ";
|
| 517 |
+
emit v.length; emitLn ");"
|
| 518 |
+
|
| 519 |
+
def emitVDecl (z : VarId) (t : IRType) (v : Expr) : M Unit :=
|
| 520 |
+
match v with
|
| 521 |
+
| Expr.ctor c ys => emitCtor z c ys
|
| 522 |
+
| Expr.reset n x => emitReset z n x
|
| 523 |
+
| Expr.reuse x c u ys => emitReuse z x c u ys
|
| 524 |
+
| Expr.proj i x => emitProj z i x
|
| 525 |
+
| Expr.uproj i x => emitUProj z i x
|
| 526 |
+
| Expr.sproj n o x => emitSProj z t n o x
|
| 527 |
+
| Expr.fap c ys => emitFullApp z c ys
|
| 528 |
+
| Expr.pap c ys => emitPartialApp z c ys
|
| 529 |
+
| Expr.ap x ys => emitApp z x ys
|
| 530 |
+
| Expr.box t x => emitBox z x t
|
| 531 |
+
| Expr.unbox x => emitUnbox z t x
|
| 532 |
+
| Expr.isShared x => emitIsShared z x
|
| 533 |
+
| Expr.lit v => emitLit z t v
|
| 534 |
+
|
| 535 |
+
def isTailCall (x : VarId) (v : Expr) (b : FnBody) : M Bool := do
|
| 536 |
+
let ctx ← read;
|
| 537 |
+
match v, b with
|
| 538 |
+
| Expr.fap f _, FnBody.ret (Arg.var y) => return f == ctx.mainFn && x == y
|
| 539 |
+
| _, _ => pure false
|
| 540 |
+
|
| 541 |
+
def paramEqArg (p : Param) (x : Arg) : Bool :=
|
| 542 |
+
match x with
|
| 543 |
+
| Arg.var x => p.x == x
|
| 544 |
+
| _ => false
|
| 545 |
+
|
| 546 |
+
/--
|
| 547 |
+
Given `[p_0, ..., p_{n-1}]`, `[y_0, ..., y_{n-1}]`, representing the assignments
|
| 548 |
+
```
|
| 549 |
+
p_0 := y_0,
|
| 550 |
+
...
|
| 551 |
+
p_{n-1} := y_{n-1}
|
| 552 |
+
```
|
| 553 |
+
Return true iff we have `(i, j)` where `j > i`, and `y_j == p_i`.
|
| 554 |
+
That is, we have
|
| 555 |
+
```
|
| 556 |
+
p_i := y_i,
|
| 557 |
+
...
|
| 558 |
+
p_j := p_i, -- p_i was overwritten above
|
| 559 |
+
```
|
| 560 |
+
-/
|
| 561 |
+
def overwriteParam (ps : Array Param) (ys : Array Arg) : Bool :=
|
| 562 |
+
let n := ps.size;
|
| 563 |
+
n.any fun i _ =>
|
| 564 |
+
let p := ps[i]
|
| 565 |
+
(i+1, n).anyI fun j _ _ => paramEqArg p ys[j]!
|
| 566 |
+
|
| 567 |
+
def emitTailCall (v : Expr) : M Unit :=
|
| 568 |
+
match v with
|
| 569 |
+
| Expr.fap _ ys => do
|
| 570 |
+
let ctx ← read
|
| 571 |
+
let ps := ctx.mainParams
|
| 572 |
+
if h : ps.size = ys.size then
|
| 573 |
+
if overwriteParam ps ys then
|
| 574 |
+
emitLn "{"
|
| 575 |
+
ps.size.forM fun i _ => do
|
| 576 |
+
let p := ps[i]
|
| 577 |
+
let y := ys[i]
|
| 578 |
+
unless paramEqArg p y do
|
| 579 |
+
emit (toCType p.ty); emit " _tmp_"; emit i; emit " = "; emitArg y; emitLn ";"
|
| 580 |
+
ps.size.forM fun i _ => do
|
| 581 |
+
let p := ps[i]
|
| 582 |
+
let y := ys[i]
|
| 583 |
+
unless paramEqArg p y do emit p.x; emit " = _tmp_"; emit i; emitLn ";"
|
| 584 |
+
emitLn "}"
|
| 585 |
+
else
|
| 586 |
+
ys.size.forM fun i _ => do
|
| 587 |
+
let p := ps[i]
|
| 588 |
+
let y := ys[i]
|
| 589 |
+
unless paramEqArg p y do emit p.x; emit " = "; emitArg y; emitLn ";"
|
| 590 |
+
emitLn "goto _start;"
|
| 591 |
+
else
|
| 592 |
+
throw "invalid tail call"
|
| 593 |
+
| _ => throw "bug at emitTailCall"
|
| 594 |
+
|
| 595 |
+
mutual
|
| 596 |
+
|
| 597 |
+
partial def emitIf (x : VarId) (xType : IRType) (tag : Nat) (t : FnBody) (e : FnBody) : M Unit := do
|
| 598 |
+
emit "if ("; emitTag x xType; emit " == "; emit tag; emitLn ")";
|
| 599 |
+
emitFnBody t;
|
| 600 |
+
emitLn "else";
|
| 601 |
+
emitFnBody e
|
| 602 |
+
|
| 603 |
+
partial def emitCase (x : VarId) (xType : IRType) (alts : Array Alt) : M Unit :=
|
| 604 |
+
match isIf alts with
|
| 605 |
+
| some (tag, t, e) => emitIf x xType tag t e
|
| 606 |
+
| _ => do
|
| 607 |
+
emit "switch ("; emitTag x xType; emitLn ") {";
|
| 608 |
+
let alts := ensureHasDefault alts;
|
| 609 |
+
alts.forM fun alt => do
|
| 610 |
+
match alt with
|
| 611 |
+
| Alt.ctor c b => emit "case "; emit c.cidx; emitLn ":"; emitFnBody b
|
| 612 |
+
| Alt.default b => emitLn "default: "; emitFnBody b
|
| 613 |
+
emitLn "}"
|
| 614 |
+
|
| 615 |
+
partial def emitBlock (b : FnBody) : M Unit := do
|
| 616 |
+
match b with
|
| 617 |
+
| FnBody.jdecl _ _ _ b => emitBlock b
|
| 618 |
+
| d@(FnBody.vdecl x t v b) =>
|
| 619 |
+
let ctx ← read
|
| 620 |
+
if isTailCallTo ctx.mainFn d then
|
| 621 |
+
emitTailCall v
|
| 622 |
+
else
|
| 623 |
+
emitVDecl x t v
|
| 624 |
+
emitBlock b
|
| 625 |
+
| FnBody.inc x n c p b =>
|
| 626 |
+
unless p do emitInc x n c
|
| 627 |
+
emitBlock b
|
| 628 |
+
| FnBody.dec x n c p b =>
|
| 629 |
+
unless p do emitDec x n c
|
| 630 |
+
emitBlock b
|
| 631 |
+
| FnBody.del x b => emitDel x; emitBlock b
|
| 632 |
+
| FnBody.setTag x i b => emitSetTag x i; emitBlock b
|
| 633 |
+
| FnBody.set x i y b => emitSet x i y; emitBlock b
|
| 634 |
+
| FnBody.uset x i y b => emitUSet x i y; emitBlock b
|
| 635 |
+
| FnBody.sset x i o y t b => emitSSet x i o y t; emitBlock b
|
| 636 |
+
| FnBody.mdata _ b => emitBlock b
|
| 637 |
+
| FnBody.ret x => emit "return "; emitArg x; emitLn ";"
|
| 638 |
+
| FnBody.case _ x xType alts => emitCase x xType alts
|
| 639 |
+
| FnBody.jmp j xs => emitJmp j xs
|
| 640 |
+
| FnBody.unreachable => emitLn "lean_internal_panic_unreachable();"
|
| 641 |
+
|
| 642 |
+
partial def emitJPs : FnBody → M Unit
|
| 643 |
+
| FnBody.jdecl j _ v b => do emit j; emitLn ":"; emitFnBody v; emitJPs b
|
| 644 |
+
| e => do unless e.isTerminal do emitJPs e.body
|
| 645 |
+
|
| 646 |
+
partial def emitFnBody (b : FnBody) : M Unit := do
|
| 647 |
+
emitLn "{"
|
| 648 |
+
let declared ← declareVars b false
|
| 649 |
+
if declared then emitLn ""
|
| 650 |
+
emitBlock b
|
| 651 |
+
emitJPs b
|
| 652 |
+
emitLn "}"
|
| 653 |
+
|
| 654 |
+
end
|
| 655 |
+
|
| 656 |
+
def emitDeclAux (d : Decl) : M Unit := do
|
| 657 |
+
let env ← getEnv
|
| 658 |
+
let (_, jpMap) := mkVarJPMaps d
|
| 659 |
+
withReader (fun ctx => { ctx with jpMap := jpMap }) do
|
| 660 |
+
unless hasInitAttr env d.name do
|
| 661 |
+
match d with
|
| 662 |
+
| .fdecl (f := f) (xs := xs) (type := t) (body := b) .. =>
|
| 663 |
+
let baseName ← toCName f;
|
| 664 |
+
if xs.size == 0 then
|
| 665 |
+
emit "static "
|
| 666 |
+
else
|
| 667 |
+
emit "LEAN_EXPORT " -- make symbol visible to the interpreter
|
| 668 |
+
emit (toCType t); emit " ";
|
| 669 |
+
if xs.size > 0 then
|
| 670 |
+
emit baseName;
|
| 671 |
+
emit "(";
|
| 672 |
+
if xs.size > closureMaxArgs && isBoxedName d.name then
|
| 673 |
+
emit "lean_object** _args"
|
| 674 |
+
else
|
| 675 |
+
xs.size.forM fun i _ => do
|
| 676 |
+
if i > 0 then emit ", "
|
| 677 |
+
let x := xs[i]
|
| 678 |
+
emit (toCType x.ty); emit " "; emit x.x
|
| 679 |
+
emit ")"
|
| 680 |
+
else
|
| 681 |
+
emit ("_init_" ++ baseName ++ "()")
|
| 682 |
+
emitLn " {";
|
| 683 |
+
if xs.size > closureMaxArgs && isBoxedName d.name then
|
| 684 |
+
xs.size.forM fun i _ => do
|
| 685 |
+
let x := xs[i]!
|
| 686 |
+
emit "lean_object* "; emit x.x; emit " = _args["; emit i; emitLn "];"
|
| 687 |
+
emitLn "_start:";
|
| 688 |
+
withReader (fun ctx => { ctx with mainFn := f, mainParams := xs }) (emitFnBody b);
|
| 689 |
+
emitLn "}"
|
| 690 |
+
| _ => pure ()
|
| 691 |
+
|
| 692 |
+
def emitDecl (d : Decl) : M Unit := do
|
| 693 |
+
let d := d.normalizeIds; -- ensure we don't have gaps in the variable indices
|
| 694 |
+
try
|
| 695 |
+
emitDeclAux d
|
| 696 |
+
catch err =>
|
| 697 |
+
throw s!"{err}\ncompiling:\n{d}"
|
| 698 |
+
|
| 699 |
+
def emitFns : M Unit := do
|
| 700 |
+
let env ← getEnv;
|
| 701 |
+
let decls := getDecls env;
|
| 702 |
+
decls.reverse.forM emitDecl
|
| 703 |
+
|
| 704 |
+
def emitMarkPersistent (d : Decl) (n : Name) : M Unit := do
|
| 705 |
+
if d.resultType.isObj then
|
| 706 |
+
emit "lean_mark_persistent("
|
| 707 |
+
emitCName n
|
| 708 |
+
emitLn ");"
|
| 709 |
+
|
| 710 |
+
def emitDeclInit (d : Decl) : M Unit := do
|
| 711 |
+
let env ← getEnv
|
| 712 |
+
let n := d.name
|
| 713 |
+
if isIOUnitInitFn env n then
|
| 714 |
+
if isIOUnitBuiltinInitFn env n then
|
| 715 |
+
emit "if (builtin) {"
|
| 716 |
+
emit "res = "; emitCName n; emitLn "(lean_io_mk_world());"
|
| 717 |
+
emitLn "if (lean_io_result_is_error(res)) return res;"
|
| 718 |
+
emitLn "lean_dec_ref(res);"
|
| 719 |
+
if isIOUnitBuiltinInitFn env n then
|
| 720 |
+
emit "}"
|
| 721 |
+
else if d.params.size == 0 then
|
| 722 |
+
match getInitFnNameFor? env d.name with
|
| 723 |
+
| some initFn =>
|
| 724 |
+
if getBuiltinInitFnNameFor? env d.name |>.isSome then
|
| 725 |
+
emit "if (builtin) {"
|
| 726 |
+
emit "res = "; emitCName initFn; emitLn "(lean_io_mk_world());"
|
| 727 |
+
emitLn "if (lean_io_result_is_error(res)) return res;"
|
| 728 |
+
emitCName n
|
| 729 |
+
if d.resultType.isScalar then
|
| 730 |
+
emitLn (" = " ++ getUnboxOpName d.resultType ++ "(lean_io_result_get_value(res));")
|
| 731 |
+
else
|
| 732 |
+
emitLn " = lean_io_result_get_value(res);"
|
| 733 |
+
emitMarkPersistent d n
|
| 734 |
+
emitLn "lean_dec_ref(res);"
|
| 735 |
+
if getBuiltinInitFnNameFor? env d.name |>.isSome then
|
| 736 |
+
emit "}"
|
| 737 |
+
| _ =>
|
| 738 |
+
emitCName n; emit " = "; emitCInitName n; emitLn "();"; emitMarkPersistent d n
|
| 739 |
+
|
| 740 |
+
def emitInitFn : M Unit := do
|
| 741 |
+
let env ← getEnv
|
| 742 |
+
let modName ← getModName
|
| 743 |
+
env.imports.forM fun imp => emitLn ("lean_object* " ++ mkModuleInitializationFunctionName imp.module ++ "(uint8_t builtin, lean_object*);")
|
| 744 |
+
emitLns [
|
| 745 |
+
"static bool _G_initialized = false;",
|
| 746 |
+
"LEAN_EXPORT lean_object* " ++ mkModuleInitializationFunctionName modName ++ "(uint8_t builtin, lean_object* w) {",
|
| 747 |
+
"lean_object * res;",
|
| 748 |
+
"if (_G_initialized) return lean_io_result_mk_ok(lean_box(0));",
|
| 749 |
+
"_G_initialized = true;"
|
| 750 |
+
]
|
| 751 |
+
env.imports.forM fun imp => emitLns [
|
| 752 |
+
"res = " ++ mkModuleInitializationFunctionName imp.module ++ "(builtin, lean_io_mk_world());",
|
| 753 |
+
"if (lean_io_result_is_error(res)) return res;",
|
| 754 |
+
"lean_dec_ref(res);"]
|
| 755 |
+
let decls := getDecls env
|
| 756 |
+
decls.reverse.forM emitDeclInit
|
| 757 |
+
emitLns ["return lean_io_result_mk_ok(lean_box(0));", "}"]
|
| 758 |
+
|
| 759 |
+
def main : M Unit := do
|
| 760 |
+
emitFileHeader
|
| 761 |
+
emitFnDecls
|
| 762 |
+
emitFns
|
| 763 |
+
emitInitFn
|
| 764 |
+
emitMainFnIfNeeded
|
| 765 |
+
emitFileFooter
|
| 766 |
+
|
| 767 |
+
end EmitC
|
| 768 |
+
|
| 769 |
+
@[export lean_ir_emit_c]
|
| 770 |
+
def emitC (env : Environment) (modName : Name) : Except String String :=
|
| 771 |
+
match (EmitC.main { env := env, modName := modName }).run "" with
|
| 772 |
+
| EStateM.Result.ok _ s => Except.ok s
|
| 773 |
+
| EStateM.Result.error err _ => Except.error err
|
| 774 |
+
|
| 775 |
+
end Lean.IR
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/EmitLLVM.lean
ADDED
|
@@ -0,0 +1,1657 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2022 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Siddharth Bhat
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Runtime
|
| 8 |
+
import Lean.Compiler.NameMangling
|
| 9 |
+
import Lean.Compiler.ExportAttr
|
| 10 |
+
import Lean.Compiler.InitAttr
|
| 11 |
+
import Lean.Compiler.IR.CompilerM
|
| 12 |
+
import Lean.Compiler.IR.EmitUtil
|
| 13 |
+
import Lean.Compiler.IR.NormIds
|
| 14 |
+
import Lean.Compiler.IR.SimpCase
|
| 15 |
+
import Lean.Compiler.IR.Boxing
|
| 16 |
+
import Lean.Compiler.IR.ResetReuse
|
| 17 |
+
import Lean.Compiler.IR.LLVMBindings
|
| 18 |
+
|
| 19 |
+
open Lean.IR.ExplicitBoxing (isBoxedName)
|
| 20 |
+
|
| 21 |
+
namespace Lean.IR
|
| 22 |
+
|
| 23 |
+
def leanMainFn := "_lean_main"
|
| 24 |
+
|
| 25 |
+
namespace LLVM
|
| 26 |
+
-- TODO(bollu): instantiate target triple and find out what size_t is.
|
| 27 |
+
def size_tType (llvmctx : LLVM.Context) : BaseIO (LLVM.LLVMType llvmctx) :=
|
| 28 |
+
LLVM.i64Type llvmctx
|
| 29 |
+
|
| 30 |
+
-- TODO(bollu): instantiate target triple and find out what unsigned is.
|
| 31 |
+
def unsignedType (llvmctx : LLVM.Context) : BaseIO (LLVM.LLVMType llvmctx) :=
|
| 32 |
+
LLVM.i32Type llvmctx
|
| 33 |
+
|
| 34 |
+
-- Helper to add a function if it does not exist, and to return the function handle if it does.
|
| 35 |
+
def getOrAddFunction (m : LLVM.Module ctx) (name : String) (type : LLVM.LLVMType ctx) : BaseIO (LLVM.Value ctx) := do
|
| 36 |
+
match (← LLVM.getNamedFunction m name) with
|
| 37 |
+
| some fn => return fn
|
| 38 |
+
| none =>
|
| 39 |
+
/-
|
| 40 |
+
By the evidence shown in: https://github.com/leanprover/lean4/issues/2373#issuecomment-1658743284
|
| 41 |
+
this is how clang implements `-fstack-clash-protection` in the LLVM IR, we want this feature
|
| 42 |
+
for robust stack overflow detection.
|
| 43 |
+
-/
|
| 44 |
+
let fn ← LLVM.addFunction m name type
|
| 45 |
+
let attr ← LLVM.createStringAttribute "probe-stack" "inline-asm"
|
| 46 |
+
LLVM.addAttributeAtIndex fn LLVM.AttributeIndex.AttributeFunctionIndex attr
|
| 47 |
+
return fn
|
| 48 |
+
|
| 49 |
+
def getOrAddGlobal (m : LLVM.Module ctx) (name : String) (type : LLVM.LLVMType ctx) : BaseIO (LLVM.Value ctx) := do
|
| 50 |
+
match (← LLVM.getNamedGlobal m name) with
|
| 51 |
+
| .some fn => return fn
|
| 52 |
+
| .none => LLVM.addGlobal m name type
|
| 53 |
+
|
| 54 |
+
end LLVM
|
| 55 |
+
|
| 56 |
+
namespace EmitLLVM
|
| 57 |
+
|
| 58 |
+
structure Context (llvmctx : LLVM.Context) where
|
| 59 |
+
env : Environment
|
| 60 |
+
modName : Name
|
| 61 |
+
jpMap : JPParamsMap := {}
|
| 62 |
+
mainFn : FunId := default
|
| 63 |
+
mainParams : Array Param := #[]
|
| 64 |
+
llvmmodule : LLVM.Module llvmctx
|
| 65 |
+
|
| 66 |
+
structure State (llvmctx : LLVM.Context) where
|
| 67 |
+
var2val : Std.HashMap VarId (LLVM.LLVMType llvmctx × LLVM.Value llvmctx)
|
| 68 |
+
jp2bb : Std.HashMap JoinPointId (LLVM.BasicBlock llvmctx)
|
| 69 |
+
|
| 70 |
+
abbrev Error := String
|
| 71 |
+
|
| 72 |
+
abbrev M (llvmctx : LLVM.Context) :=
|
| 73 |
+
StateRefT (State llvmctx) (ReaderT (Context llvmctx) (ExceptT Error IO))
|
| 74 |
+
|
| 75 |
+
instance : Inhabited (M llvmctx α) where
|
| 76 |
+
default := throw "Error: inhabitant"
|
| 77 |
+
|
| 78 |
+
def addVartoState (x : VarId) (v : LLVM.Value llvmctx) (ty : LLVM.LLVMType llvmctx) : M llvmctx Unit := do
|
| 79 |
+
modify (fun s => { s with var2val := s.var2val.insert x (ty, v) }) -- add new variable
|
| 80 |
+
|
| 81 |
+
def addJpTostate (jp : JoinPointId) (bb : LLVM.BasicBlock llvmctx) : M llvmctx Unit :=
|
| 82 |
+
modify (fun s => { s with jp2bb := s.jp2bb.insert jp bb })
|
| 83 |
+
|
| 84 |
+
def emitJp (jp : JoinPointId) : M llvmctx (LLVM.BasicBlock llvmctx) := do
|
| 85 |
+
let state ← get
|
| 86 |
+
match state.jp2bb[jp]? with
|
| 87 |
+
| .some bb => return bb
|
| 88 |
+
| .none => throw s!"unable to find join point {jp}"
|
| 89 |
+
|
| 90 |
+
def getLLVMModule : M llvmctx (LLVM.Module llvmctx) := Context.llvmmodule <$> read
|
| 91 |
+
|
| 92 |
+
def getEnv : M llvmctx Environment := Context.env <$> read
|
| 93 |
+
|
| 94 |
+
def getModName : M llvmctx Name := Context.modName <$> read
|
| 95 |
+
|
| 96 |
+
def getDecl (n : Name) : M llvmctx Decl := do
|
| 97 |
+
let env ← getEnv
|
| 98 |
+
match findEnvDecl env n with
|
| 99 |
+
| some d => pure d
|
| 100 |
+
| none => throw s!"unknown declaration {n}"
|
| 101 |
+
|
| 102 |
+
def constInt8 (n : Nat) : M llvmctx (LLVM.Value llvmctx) := do
|
| 103 |
+
LLVM.constInt8 llvmctx (UInt64.ofNat n)
|
| 104 |
+
|
| 105 |
+
def constInt64 (n : Nat) : M llvmctx (LLVM.Value llvmctx) := do
|
| 106 |
+
LLVM.constInt64 llvmctx (UInt64.ofNat n)
|
| 107 |
+
|
| 108 |
+
def constIntSizeT (n : Nat) : M llvmctx (LLVM.Value llvmctx) := do
|
| 109 |
+
LLVM.constIntSizeT llvmctx (UInt64.ofNat n)
|
| 110 |
+
|
| 111 |
+
def constIntUnsigned (n : Nat) : M llvmctx (LLVM.Value llvmctx) := do
|
| 112 |
+
LLVM.constIntUnsigned llvmctx (UInt64.ofNat n)
|
| 113 |
+
|
| 114 |
+
def getOrCreateFunctionPrototype (mod : LLVM.Module llvmctx)
|
| 115 |
+
(retty : LLVM.LLVMType llvmctx) (name : String) (args : Array (LLVM.LLVMType llvmctx)) : M llvmctx (LLVM.Value llvmctx) := do
|
| 116 |
+
LLVM.getOrAddFunction mod name $ ← LLVM.functionType retty args (isVarArg := false)
|
| 117 |
+
|
| 118 |
+
def callLeanBox (builder : LLVM.Builder llvmctx)
|
| 119 |
+
(arg : LLVM.Value llvmctx) (name : String := "") : M llvmctx (LLVM.Value llvmctx) := do
|
| 120 |
+
let fnName := "lean_box"
|
| 121 |
+
let retty ← LLVM.voidPtrType llvmctx
|
| 122 |
+
let argtys := #[ ← LLVM.size_tType llvmctx ]
|
| 123 |
+
let fn ← getOrCreateFunctionPrototype (← getLLVMModule) retty fnName argtys
|
| 124 |
+
let fnty ← LLVM.functionType retty argtys
|
| 125 |
+
LLVM.buildCall2 builder fnty fn #[arg] name
|
| 126 |
+
|
| 127 |
+
def callLeanMarkPersistentFn (builder : LLVM.Builder llvmctx) (arg : LLVM.Value llvmctx) : M llvmctx Unit := do
|
| 128 |
+
let fnName := "lean_mark_persistent"
|
| 129 |
+
let retty ← LLVM.voidType llvmctx
|
| 130 |
+
let argtys := #[ ← LLVM.voidPtrType llvmctx ]
|
| 131 |
+
let fn ← getOrCreateFunctionPrototype (← getLLVMModule) retty fnName argtys
|
| 132 |
+
let fnty ← LLVM.functionType retty argtys
|
| 133 |
+
let _ ← LLVM.buildCall2 builder fnty fn #[arg]
|
| 134 |
+
|
| 135 |
+
-- `lean_{inc, dec}_{ref?}_{1,n}`
|
| 136 |
+
inductive RefcountKind where
|
| 137 |
+
| inc | dec
|
| 138 |
+
|
| 139 |
+
instance : ToString RefcountKind where
|
| 140 |
+
toString
|
| 141 |
+
| .inc => "inc"
|
| 142 |
+
| .dec => "dec"
|
| 143 |
+
|
| 144 |
+
def callLeanRefcountFn (builder : LLVM.Builder llvmctx)
|
| 145 |
+
(kind : RefcountKind) (checkRef? : Bool) (arg : LLVM.Value llvmctx)
|
| 146 |
+
(delta : Option (LLVM.Value llvmctx) := Option.none) : M llvmctx Unit := do
|
| 147 |
+
let fnName := s!"lean_{kind}{if checkRef? then "" else "_ref"}{if delta.isNone then "" else "_n"}"
|
| 148 |
+
let retty ← LLVM.voidType llvmctx
|
| 149 |
+
let argtys ← if delta.isNone then pure #[← LLVM.voidPtrType llvmctx] else pure #[← LLVM.voidPtrType llvmctx, ← LLVM.size_tType llvmctx]
|
| 150 |
+
let fn ← getOrCreateFunctionPrototype (← getLLVMModule) retty fnName argtys
|
| 151 |
+
let fnty ← LLVM.functionType retty argtys
|
| 152 |
+
match delta with
|
| 153 |
+
| .none => do
|
| 154 |
+
-- since refcount δ is 1, we only supply the pointer.
|
| 155 |
+
let _ ← LLVM.buildCall2 builder fnty fn #[arg]
|
| 156 |
+
| .some n => do
|
| 157 |
+
let _ ← LLVM.buildCall2 builder fnty fn #[arg, n]
|
| 158 |
+
|
| 159 |
+
-- `decRef1`
|
| 160 |
+
-- Do NOT attempt to merge this code with callLeanRefcountFn, because of the uber confusing
|
| 161 |
+
-- semantics of 'ref?'. If 'ref?' is true, it calls the version that is lean_dec
|
| 162 |
+
def callLeanDecRef (builder : LLVM.Builder llvmctx) (res : LLVM.Value llvmctx) : M llvmctx Unit := do
|
| 163 |
+
let fnName := "lean_dec_ref"
|
| 164 |
+
let retty ← LLVM.voidType llvmctx
|
| 165 |
+
let argtys := #[ ← LLVM.i8PtrType llvmctx ]
|
| 166 |
+
let fn ← getOrCreateFunctionPrototype (← getLLVMModule) retty fnName argtys
|
| 167 |
+
let fnty ← LLVM.functionType retty argtys
|
| 168 |
+
let _ ← LLVM.buildCall2 builder fnty fn #[res]
|
| 169 |
+
|
| 170 |
+
def callLeanUnsignedToNatFn (builder : LLVM.Builder llvmctx)
|
| 171 |
+
(n : Nat) (name : String := "") : M llvmctx (LLVM.Value llvmctx) := do
|
| 172 |
+
let mod ← getLLVMModule
|
| 173 |
+
let argtys := #[← LLVM.i32Type llvmctx]
|
| 174 |
+
let retty ← LLVM.voidPtrType llvmctx
|
| 175 |
+
let f ← getOrCreateFunctionPrototype mod retty "lean_unsigned_to_nat" argtys
|
| 176 |
+
let fnty ← LLVM.functionType retty argtys
|
| 177 |
+
let nv ← constIntUnsigned n
|
| 178 |
+
LLVM.buildCall2 builder fnty f #[nv] name
|
| 179 |
+
|
| 180 |
+
def callLeanMkStringUncheckedFn (builder : LLVM.Builder llvmctx)
|
| 181 |
+
(strPtr nBytes nChars : LLVM.Value llvmctx) (name : String) : M llvmctx (LLVM.Value llvmctx) := do
|
| 182 |
+
let fnName := "lean_mk_string_unchecked"
|
| 183 |
+
let retty ← LLVM.voidPtrType llvmctx
|
| 184 |
+
let argtys := #[← LLVM.voidPtrType llvmctx, ← LLVM.size_tType llvmctx, ← LLVM.size_tType llvmctx]
|
| 185 |
+
let fn ← getOrCreateFunctionPrototype (← getLLVMModule) retty fnName argtys
|
| 186 |
+
let fnty ← LLVM.functionType retty argtys
|
| 187 |
+
LLVM.buildCall2 builder fnty fn #[strPtr, nBytes, nChars] name
|
| 188 |
+
|
| 189 |
+
def callLeanMkString (builder : LLVM.Builder llvmctx)
|
| 190 |
+
(strPtr : LLVM.Value llvmctx) (name : String) : M llvmctx (LLVM.Value llvmctx) := do
|
| 191 |
+
let retty ← LLVM.voidPtrType llvmctx
|
| 192 |
+
let argtys := #[← LLVM.voidPtrType llvmctx]
|
| 193 |
+
let fn ← getOrCreateFunctionPrototype (← getLLVMModule) retty "lean_mk_string" argtys
|
| 194 |
+
let fnty ← LLVM.functionType retty argtys
|
| 195 |
+
LLVM.buildCall2 builder fnty fn #[strPtr] name
|
| 196 |
+
|
| 197 |
+
def callLeanCStrToNatFn (builder : LLVM.Builder llvmctx)
|
| 198 |
+
(n : Nat) (name : String := "") : M llvmctx (LLVM.Value llvmctx) := do
|
| 199 |
+
let fnName := "lean_cstr_to_nat"
|
| 200 |
+
let retty ← LLVM.voidPtrType llvmctx
|
| 201 |
+
let argtys := #[← LLVM.voidPtrType llvmctx]
|
| 202 |
+
let fn ← getOrCreateFunctionPrototype (← getLLVMModule) retty fnName argtys
|
| 203 |
+
let fnty ← LLVM.functionType retty argtys
|
| 204 |
+
let s ← LLVM.buildGlobalString builder (value := toString n)
|
| 205 |
+
LLVM.buildCall2 builder fnty fn #[s] name
|
| 206 |
+
|
| 207 |
+
def callLeanIOMkWorld (builder : LLVM.Builder llvmctx) : M llvmctx (LLVM.Value llvmctx) := do
|
| 208 |
+
let fnName := "lean_io_mk_world"
|
| 209 |
+
let retty ← LLVM.voidPtrType llvmctx
|
| 210 |
+
let argtys := #[]
|
| 211 |
+
let fn ← getOrCreateFunctionPrototype (← getLLVMModule) retty fnName argtys
|
| 212 |
+
let fnty ← LLVM.functionType retty argtys
|
| 213 |
+
LLVM.buildCall2 builder fnty fn #[] "mk_io_out"
|
| 214 |
+
|
| 215 |
+
def callLeanIOResultIsError (builder : LLVM.Builder llvmctx)
|
| 216 |
+
(arg : LLVM.Value llvmctx) (name : String := "") : M llvmctx (LLVM.Value llvmctx) := do
|
| 217 |
+
let fnName := "lean_io_result_is_error"
|
| 218 |
+
let retty ← LLVM.i1Type llvmctx
|
| 219 |
+
let argtys := #[← LLVM.voidPtrType llvmctx]
|
| 220 |
+
let fn ← getOrCreateFunctionPrototype (← getLLVMModule) retty fnName argtys
|
| 221 |
+
let fnty ← LLVM.functionType retty argtys
|
| 222 |
+
LLVM.buildCall2 builder fnty fn #[arg] name
|
| 223 |
+
|
| 224 |
+
def callLeanAllocCtor (builder : LLVM.Builder llvmctx)
|
| 225 |
+
(tag num_objs scalar_sz : Nat) (name : String := "") : M llvmctx (LLVM.Value llvmctx) := do
|
| 226 |
+
let fnName := "lean_alloc_ctor"
|
| 227 |
+
let retty ← LLVM.voidPtrType llvmctx
|
| 228 |
+
let i32 ← LLVM.i32Type llvmctx
|
| 229 |
+
let argtys := #[i32, i32, i32]
|
| 230 |
+
let fn ← getOrCreateFunctionPrototype (← getLLVMModule) retty fnName argtys
|
| 231 |
+
let fnty ← LLVM.functionType retty argtys
|
| 232 |
+
|
| 233 |
+
let tag ← constIntUnsigned tag
|
| 234 |
+
let num_objs ← constIntUnsigned num_objs
|
| 235 |
+
let scalar_sz ← constIntUnsigned scalar_sz
|
| 236 |
+
LLVM.buildCall2 builder fnty fn #[tag, num_objs, scalar_sz] name
|
| 237 |
+
|
| 238 |
+
def callLeanCtorSet (builder : LLVM.Builder llvmctx)
|
| 239 |
+
(o i v : LLVM.Value llvmctx) : M llvmctx Unit := do
|
| 240 |
+
let fnName := "lean_ctor_set"
|
| 241 |
+
let retty ← LLVM.voidType llvmctx
|
| 242 |
+
let voidptr ← LLVM.voidPtrType llvmctx
|
| 243 |
+
let unsigned ← LLVM.unsignedType llvmctx
|
| 244 |
+
let argtys := #[voidptr, unsigned, voidptr]
|
| 245 |
+
let fn ← getOrCreateFunctionPrototype (← getLLVMModule) retty fnName argtys
|
| 246 |
+
let fnty ← LLVM.functionType retty argtys
|
| 247 |
+
_ <- LLVM.buildCall2 builder fnty fn #[o, i, v]
|
| 248 |
+
|
| 249 |
+
def callLeanIOResultMKOk (builder : LLVM.Builder llvmctx)
|
| 250 |
+
(v : LLVM.Value llvmctx) (name : String := "") : M llvmctx (LLVM.Value llvmctx) := do
|
| 251 |
+
let fnName := "lean_io_result_mk_ok"
|
| 252 |
+
let voidptr ← LLVM.voidPtrType llvmctx
|
| 253 |
+
let retty := voidptr
|
| 254 |
+
let argtys := #[voidptr]
|
| 255 |
+
let fn ← getOrCreateFunctionPrototype (← getLLVMModule) retty fnName argtys
|
| 256 |
+
let fnty ← LLVM.functionType retty argtys
|
| 257 |
+
LLVM.buildCall2 builder fnty fn #[v] name
|
| 258 |
+
|
| 259 |
+
def callLeanAllocClosureFn (builder : LLVM.Builder llvmctx)
|
| 260 |
+
(f arity nys : LLVM.Value llvmctx) (retName : String := "") : M llvmctx (LLVM.Value llvmctx) := do
|
| 261 |
+
let fnName := "lean_alloc_closure"
|
| 262 |
+
let retty ← LLVM.voidPtrType llvmctx
|
| 263 |
+
let argtys := #[ ← LLVM.voidPtrType llvmctx, ← LLVM.unsignedType llvmctx, ← LLVM.unsignedType llvmctx]
|
| 264 |
+
let fn ← getOrCreateFunctionPrototype (← getLLVMModule) retty fnName argtys
|
| 265 |
+
let fnty ← LLVM.functionType retty argtys
|
| 266 |
+
LLVM.buildCall2 builder fnty fn #[f, arity, nys] retName
|
| 267 |
+
|
| 268 |
+
def callLeanClosureSetFn (builder : LLVM.Builder llvmctx)
|
| 269 |
+
(closure ix arg : LLVM.Value llvmctx) (retName : String := "") : M llvmctx Unit := do
|
| 270 |
+
let fnName := "lean_closure_set"
|
| 271 |
+
let retty ← LLVM.voidType llvmctx
|
| 272 |
+
let argtys := #[ ← LLVM.voidPtrType llvmctx, ← LLVM.unsignedType llvmctx, ← LLVM.voidPtrType llvmctx]
|
| 273 |
+
let fn ← getOrCreateFunctionPrototype (← getLLVMModule) retty fnName argtys
|
| 274 |
+
let fnty ← LLVM.functionType retty argtys
|
| 275 |
+
let _ ← LLVM.buildCall2 builder fnty fn #[closure, ix, arg] retName
|
| 276 |
+
|
| 277 |
+
def callLeanObjTag (builder : LLVM.Builder llvmctx)
|
| 278 |
+
(closure : LLVM.Value llvmctx) (retName : String := "") : M llvmctx (LLVM.Value llvmctx) := do
|
| 279 |
+
let fnName := "lean_obj_tag"
|
| 280 |
+
let retty ← LLVM.i32Type llvmctx
|
| 281 |
+
let argtys := #[ ← LLVM.voidPtrType llvmctx]
|
| 282 |
+
let fn ← getOrCreateFunctionPrototype (← getLLVMModule) retty fnName argtys
|
| 283 |
+
let fnty ← LLVM.functionType retty argtys
|
| 284 |
+
let out ← LLVM.buildCall2 builder fnty fn #[closure] retName
|
| 285 |
+
LLVM.buildSextOrTrunc builder out (← LLVM.i64Type llvmctx)
|
| 286 |
+
|
| 287 |
+
def callLeanIOResultGetValue (builder : LLVM.Builder llvmctx)
|
| 288 |
+
(v : LLVM.Value llvmctx) (name : String := "") : M llvmctx (LLVM.Value llvmctx) := do
|
| 289 |
+
let fnName := "lean_io_result_get_value"
|
| 290 |
+
let retty ← LLVM.voidPtrType llvmctx
|
| 291 |
+
let argtys := #[ ← LLVM.voidPtrType llvmctx]
|
| 292 |
+
let fn ← getOrCreateFunctionPrototype (← getLLVMModule) retty fnName argtys
|
| 293 |
+
let fnty ← LLVM.functionType retty argtys
|
| 294 |
+
LLVM.buildCall2 builder fnty fn #[v] name
|
| 295 |
+
|
| 296 |
+
def callLeanCtorRelease (builder : LLVM.Builder llvmctx)
|
| 297 |
+
(closure i : LLVM.Value llvmctx) (retName : String := "") : M llvmctx Unit := do
|
| 298 |
+
let fnName := "lean_ctor_release"
|
| 299 |
+
let retty ← LLVM.voidType llvmctx
|
| 300 |
+
let argtys := #[ ← LLVM.voidPtrType llvmctx, ← LLVM.unsignedType llvmctx]
|
| 301 |
+
let fn ← getOrCreateFunctionPrototype (← getLLVMModule) retty fnName argtys
|
| 302 |
+
let fnty ← LLVM.functionType retty argtys
|
| 303 |
+
let _ ← LLVM.buildCall2 builder fnty fn #[closure, i] retName
|
| 304 |
+
|
| 305 |
+
def callLeanCtorSetTag (builder : LLVM.Builder llvmctx)
|
| 306 |
+
(closure i : LLVM.Value llvmctx) (retName : String := "") : M llvmctx Unit := do
|
| 307 |
+
let fnName := "lean_ctor_set_tag"
|
| 308 |
+
let retty ← LLVM.voidType llvmctx
|
| 309 |
+
let argtys := #[ ← LLVM.voidPtrType llvmctx, ← LLVM.i8Type llvmctx]
|
| 310 |
+
let fn ← getOrCreateFunctionPrototype (← getLLVMModule) retty fnName argtys
|
| 311 |
+
let fnty ← LLVM.functionType retty argtys
|
| 312 |
+
let _ ← LLVM.buildCall2 builder fnty fn #[closure, i] retName
|
| 313 |
+
|
| 314 |
+
def toLLVMType (t : IRType) : M llvmctx (LLVM.LLVMType llvmctx) := do
|
| 315 |
+
match t with
|
| 316 |
+
| IRType.float => LLVM.doubleTypeInContext llvmctx
|
| 317 |
+
| IRType.float32 => LLVM.floatTypeInContext llvmctx
|
| 318 |
+
| IRType.uint8 => LLVM.intTypeInContext llvmctx 8
|
| 319 |
+
| IRType.uint16 => LLVM.intTypeInContext llvmctx 16
|
| 320 |
+
| IRType.uint32 => LLVM.intTypeInContext llvmctx 32
|
| 321 |
+
| IRType.uint64 => LLVM.intTypeInContext llvmctx 64
|
| 322 |
+
-- TODO: how to cleanly size_t in LLVM? We can do eg. instantiate the current target and query for size.
|
| 323 |
+
| IRType.usize => LLVM.size_tType llvmctx
|
| 324 |
+
| IRType.object => do LLVM.pointerType (← LLVM.i8Type llvmctx)
|
| 325 |
+
| IRType.tobject => do LLVM.pointerType (← LLVM.i8Type llvmctx)
|
| 326 |
+
| IRType.irrelevant => do LLVM.pointerType (← LLVM.i8Type llvmctx)
|
| 327 |
+
| IRType.struct _ _ => panic! "not implemented yet"
|
| 328 |
+
| IRType.union _ _ => panic! "not implemented yet"
|
| 329 |
+
|
| 330 |
+
def throwInvalidExportName {α : Type} (n : Name) : M llvmctx α := do
|
| 331 |
+
throw s!"invalid export name {n.toString}"
|
| 332 |
+
|
| 333 |
+
def toCName (n : Name) : M llvmctx String := do
|
| 334 |
+
match getExportNameFor? (← getEnv) n with
|
| 335 |
+
| some (.str .anonymous s) => pure s
|
| 336 |
+
| some _ => throwInvalidExportName n
|
| 337 |
+
| none => if n == `main then pure leanMainFn else pure n.mangle
|
| 338 |
+
|
| 339 |
+
def toCInitName (n : Name) : M llvmctx String := do
|
| 340 |
+
match getExportNameFor? (← getEnv) n with
|
| 341 |
+
| some (.str .anonymous s) => return "_init_" ++ s
|
| 342 |
+
| some _ => throwInvalidExportName n
|
| 343 |
+
| none => pure ("_init_" ++ n.mangle)
|
| 344 |
+
|
| 345 |
+
/--
|
| 346 |
+
## LLVM Control flow Utilities
|
| 347 |
+
-/
|
| 348 |
+
|
| 349 |
+
-- Indicates whether the API for building the blocks for then/else should
|
| 350 |
+
-- forward the control flow to the merge block.
|
| 351 |
+
inductive ShouldForwardControlFlow where
|
| 352 |
+
| yes | no
|
| 353 |
+
|
| 354 |
+
-- Get the function we are currently inserting into.
|
| 355 |
+
def builderGetInsertionFn (builder : LLVM.Builder llvmctx) : M llvmctx (LLVM.Value llvmctx) := do
|
| 356 |
+
let builderBB ← LLVM.getInsertBlock builder
|
| 357 |
+
LLVM.getBasicBlockParent builderBB
|
| 358 |
+
|
| 359 |
+
def builderAppendBasicBlock (builder : LLVM.Builder llvmctx) (name : String) : M llvmctx (LLVM.BasicBlock llvmctx) := do
|
| 360 |
+
let fn ← builderGetInsertionFn builder
|
| 361 |
+
LLVM.appendBasicBlockInContext llvmctx fn name
|
| 362 |
+
|
| 363 |
+
/--
|
| 364 |
+
Add an alloca to the first BB of the current function. The builders final position
|
| 365 |
+
will be the end of the BB that we came from.
|
| 366 |
+
|
| 367 |
+
If it is possible to put an alloca in the first BB this approach is to be preferred
|
| 368 |
+
over putting it in other BBs. This is because mem2reg only inspects allocas in the first BB,
|
| 369 |
+
leading to missed optimizations for allocas in other BBs.
|
| 370 |
+
-/
|
| 371 |
+
def buildPrologueAlloca (builder : LLVM.Builder llvmctx) (ty : LLVM.LLVMType llvmctx) (name : @&String := "") : M llvmctx (LLVM.Value llvmctx) := do
|
| 372 |
+
let origBB ← LLVM.getInsertBlock builder
|
| 373 |
+
|
| 374 |
+
let fn ← builderGetInsertionFn builder
|
| 375 |
+
if (← LLVM.countBasicBlocks fn) == 0 then
|
| 376 |
+
throw "Attempt to obtain first BB of function without BBs"
|
| 377 |
+
|
| 378 |
+
let entryBB ← LLVM.getEntryBasicBlock fn
|
| 379 |
+
match ← LLVM.getFirstInstruction entryBB with
|
| 380 |
+
| some instr => LLVM.positionBuilderBefore builder instr
|
| 381 |
+
| none => LLVM.positionBuilderAtEnd builder entryBB
|
| 382 |
+
|
| 383 |
+
let alloca ← LLVM.buildAlloca builder ty name
|
| 384 |
+
LLVM.positionBuilderAtEnd builder origBB
|
| 385 |
+
return alloca
|
| 386 |
+
|
| 387 |
+
|
| 388 |
+
def buildWhile_ (builder : LLVM.Builder llvmctx) (name : String)
|
| 389 |
+
(condcodegen : LLVM.Builder llvmctx → M llvmctx (LLVM.Value llvmctx))
|
| 390 |
+
(bodycodegen : LLVM.Builder llvmctx → M llvmctx Unit) : M llvmctx Unit := do
|
| 391 |
+
let fn ← builderGetInsertionFn builder
|
| 392 |
+
|
| 393 |
+
let nameHeader := name ++ "header"
|
| 394 |
+
let nameBody := name ++ "body"
|
| 395 |
+
let nameMerge := name ++ "merge"
|
| 396 |
+
|
| 397 |
+
-- cur → header
|
| 398 |
+
let headerbb ← LLVM.appendBasicBlockInContext llvmctx fn nameHeader
|
| 399 |
+
let _ ← LLVM.buildBr builder headerbb
|
| 400 |
+
|
| 401 |
+
let bodybb ← LLVM.appendBasicBlockInContext llvmctx fn nameBody
|
| 402 |
+
let mergebb ← LLVM.appendBasicBlockInContext llvmctx fn nameMerge
|
| 403 |
+
|
| 404 |
+
-- header → {body, merge}
|
| 405 |
+
LLVM.positionBuilderAtEnd builder headerbb
|
| 406 |
+
let cond ← condcodegen builder
|
| 407 |
+
let _ ← LLVM.buildCondBr builder cond bodybb mergebb
|
| 408 |
+
|
| 409 |
+
-- body → header
|
| 410 |
+
LLVM.positionBuilderAtEnd builder bodybb
|
| 411 |
+
bodycodegen builder
|
| 412 |
+
let _ ← LLVM.buildBr builder headerbb
|
| 413 |
+
|
| 414 |
+
-- merge
|
| 415 |
+
LLVM.positionBuilderAtEnd builder mergebb
|
| 416 |
+
|
| 417 |
+
-- build an if, and position the builder at the merge basic block after execution.
|
| 418 |
+
-- The '_' denotes that we return Unit on each branch.
|
| 419 |
+
def buildIfThen_ (builder : LLVM.Builder llvmctx) (name : String) (brval : LLVM.Value llvmctx)
|
| 420 |
+
(thencodegen : LLVM.Builder llvmctx → M llvmctx ShouldForwardControlFlow) : M llvmctx Unit := do
|
| 421 |
+
let fn ← builderGetInsertionFn builder
|
| 422 |
+
|
| 423 |
+
let nameThen := name ++ "Then"
|
| 424 |
+
let nameElse := name ++ "Else"
|
| 425 |
+
let nameMerge := name ++ "Merge"
|
| 426 |
+
|
| 427 |
+
let thenbb ← LLVM.appendBasicBlockInContext llvmctx fn nameThen
|
| 428 |
+
let elsebb ← LLVM.appendBasicBlockInContext llvmctx fn nameElse
|
| 429 |
+
let mergebb ← LLVM.appendBasicBlockInContext llvmctx fn nameMerge
|
| 430 |
+
let _ ← LLVM.buildCondBr builder brval thenbb elsebb
|
| 431 |
+
-- then
|
| 432 |
+
LLVM.positionBuilderAtEnd builder thenbb
|
| 433 |
+
let fwd? ← thencodegen builder
|
| 434 |
+
match fwd? with
|
| 435 |
+
| .yes => let _ ← LLVM.buildBr builder mergebb
|
| 436 |
+
| .no => pure ()
|
| 437 |
+
-- else
|
| 438 |
+
LLVM.positionBuilderAtEnd builder elsebb
|
| 439 |
+
let _ ← LLVM.buildBr builder mergebb
|
| 440 |
+
-- merge
|
| 441 |
+
LLVM.positionBuilderAtEnd builder mergebb
|
| 442 |
+
|
| 443 |
+
def buildIfThenElse_ (builder : LLVM.Builder llvmctx) (name : String) (brval : LLVM.Value llvmctx)
|
| 444 |
+
(thencodegen : LLVM.Builder llvmctx → M llvmctx ShouldForwardControlFlow)
|
| 445 |
+
(elsecodegen : LLVM.Builder llvmctx → M llvmctx ShouldForwardControlFlow) : M llvmctx Unit := do
|
| 446 |
+
let fn ← LLVM.getBasicBlockParent (← LLVM.getInsertBlock builder)
|
| 447 |
+
let thenbb ← LLVM.appendBasicBlockInContext llvmctx fn (name ++ "Then")
|
| 448 |
+
let elsebb ← LLVM.appendBasicBlockInContext llvmctx fn (name ++ "Else")
|
| 449 |
+
let mergebb ← LLVM.appendBasicBlockInContext llvmctx fn (name ++ "Merge")
|
| 450 |
+
let _ ← LLVM.buildCondBr builder brval thenbb elsebb
|
| 451 |
+
-- then
|
| 452 |
+
LLVM.positionBuilderAtEnd builder thenbb
|
| 453 |
+
let fwd? ← thencodegen builder
|
| 454 |
+
match fwd? with
|
| 455 |
+
| .yes => let _ ← LLVM.buildBr builder mergebb
|
| 456 |
+
| .no => pure ()
|
| 457 |
+
-- else
|
| 458 |
+
LLVM.positionBuilderAtEnd builder elsebb
|
| 459 |
+
let fwd? ← elsecodegen builder
|
| 460 |
+
match fwd? with
|
| 461 |
+
| .yes => let _ ← LLVM.buildBr builder mergebb
|
| 462 |
+
| .no => pure ()
|
| 463 |
+
-- merge
|
| 464 |
+
LLVM.positionBuilderAtEnd builder mergebb
|
| 465 |
+
|
| 466 |
+
-- Recall that lean uses `i8` for booleans, not `i1`, so we need to compare with `true`.
|
| 467 |
+
def buildLeanBoolTrue? (builder : LLVM.Builder llvmctx)
|
| 468 |
+
(b : LLVM.Value llvmctx) (name : String := "") : M llvmctx (LLVM.Value llvmctx) := do
|
| 469 |
+
LLVM.buildICmp builder LLVM.IntPredicate.NE b (← constInt8 0) name
|
| 470 |
+
|
| 471 |
+
def emitFnDeclAux (mod : LLVM.Module llvmctx)
|
| 472 |
+
(decl : Decl) (cppBaseName : String) (isExternal : Bool) : M llvmctx (LLVM.Value llvmctx) := do
|
| 473 |
+
let ps := decl.params
|
| 474 |
+
let env ← getEnv
|
| 475 |
+
-- bollu: if we have a declaration with no parameters, then we emit it as a global pointer.
|
| 476 |
+
-- bollu: Otherwise, we emit it as a function
|
| 477 |
+
let global ←
|
| 478 |
+
if ps.isEmpty then
|
| 479 |
+
let retty ← (toLLVMType decl.resultType)
|
| 480 |
+
let global ← LLVM.getOrAddGlobal mod cppBaseName retty
|
| 481 |
+
if !isExternal then
|
| 482 |
+
LLVM.setInitializer global (← LLVM.getUndef retty)
|
| 483 |
+
pure global
|
| 484 |
+
else
|
| 485 |
+
let retty ← (toLLVMType decl.resultType)
|
| 486 |
+
let mut argtys := #[]
|
| 487 |
+
for p in ps do
|
| 488 |
+
-- if it is extern, then we must not add irrelevant args
|
| 489 |
+
if !(isExternC env decl.name) || !p.ty.isIrrelevant then
|
| 490 |
+
argtys := argtys.push (← toLLVMType p.ty)
|
| 491 |
+
-- TODO (bollu): simplify this API, this code of `closureMaxArgs` is duplicated in multiple places.
|
| 492 |
+
if argtys.size > closureMaxArgs && isBoxedName decl.name then
|
| 493 |
+
argtys := #[← LLVM.pointerType (← LLVM.voidPtrType llvmctx)]
|
| 494 |
+
let fnty ← LLVM.functionType retty argtys (isVarArg := false)
|
| 495 |
+
LLVM.getOrAddFunction mod cppBaseName fnty
|
| 496 |
+
-- we must now set symbol visibility for global.
|
| 497 |
+
if ps.isEmpty then
|
| 498 |
+
if isClosedTermName env decl.name then LLVM.setVisibility global LLVM.Visibility.hidden -- static
|
| 499 |
+
else if isExternal then pure () -- extern (Recall that C/LLVM funcs are extern linkage by default.)
|
| 500 |
+
else LLVM.setDLLStorageClass global LLVM.DLLStorageClass.export -- LEAN_EXPORT
|
| 501 |
+
else if !isExternal
|
| 502 |
+
-- An extern decl might be linked in from a different translation unit.
|
| 503 |
+
-- Thus, we cannot export an external declaration as we do not define it,
|
| 504 |
+
-- only declare its presence.
|
| 505 |
+
-- So, we only export non-external definitions.
|
| 506 |
+
then LLVM.setDLLStorageClass global LLVM.DLLStorageClass.export
|
| 507 |
+
return global
|
| 508 |
+
|
| 509 |
+
|
| 510 |
+
def emitFnDecl (decl : Decl) (isExternal : Bool) : M llvmctx Unit := do
|
| 511 |
+
let cppBaseName ← toCName decl.name
|
| 512 |
+
let _ ← emitFnDeclAux (← getLLVMModule) decl cppBaseName isExternal
|
| 513 |
+
|
| 514 |
+
def emitExternDeclAux (decl : Decl) (cNameStr : String) : M llvmctx Unit := do
|
| 515 |
+
let env ← getEnv
|
| 516 |
+
let extC := isExternC env decl.name
|
| 517 |
+
let _ ← emitFnDeclAux (← getLLVMModule) decl cNameStr extC
|
| 518 |
+
|
| 519 |
+
def emitFnDecls : M llvmctx Unit := do
|
| 520 |
+
let env ← getEnv
|
| 521 |
+
let decls := getDecls env
|
| 522 |
+
let modDecls : NameSet := decls.foldl (fun s d => s.insert d.name) {}
|
| 523 |
+
let usedDecls : NameSet := decls.foldl (fun s d => collectUsedDecls env d (s.insert d.name)) {}
|
| 524 |
+
let usedDecls := usedDecls.toList
|
| 525 |
+
for n in usedDecls do
|
| 526 |
+
let decl ← getDecl n
|
| 527 |
+
match getExternNameFor env `c decl.name with
|
| 528 |
+
| some cName => emitExternDeclAux decl cName
|
| 529 |
+
| none => emitFnDecl decl (!modDecls.contains n)
|
| 530 |
+
return ()
|
| 531 |
+
|
| 532 |
+
def emitLhsSlot_ (x : VarId) : M llvmctx (LLVM.LLVMType llvmctx × LLVM.Value llvmctx) := do
|
| 533 |
+
let state ← get
|
| 534 |
+
match state.var2val[x]? with
|
| 535 |
+
| .some v => return v
|
| 536 |
+
| .none => throw s!"unable to find variable {x}"
|
| 537 |
+
|
| 538 |
+
def emitLhsVal (builder : LLVM.Builder llvmctx)
|
| 539 |
+
(x : VarId) (name : String := "") : M llvmctx (LLVM.Value llvmctx) := do
|
| 540 |
+
let (xty, xslot) ← emitLhsSlot_ x
|
| 541 |
+
LLVM.buildLoad2 builder xty xslot name
|
| 542 |
+
|
| 543 |
+
def emitLhsSlotStore (builder : LLVM.Builder llvmctx)
|
| 544 |
+
(x : VarId) (v : LLVM.Value llvmctx) : M llvmctx Unit := do
|
| 545 |
+
let (_, slot) ← emitLhsSlot_ x
|
| 546 |
+
LLVM.buildStore builder v slot
|
| 547 |
+
|
| 548 |
+
def emitArgSlot_ (builder : LLVM.Builder llvmctx)
|
| 549 |
+
(x : Arg) : M llvmctx (LLVM.LLVMType llvmctx × LLVM.Value llvmctx) := do
|
| 550 |
+
match x with
|
| 551 |
+
| Arg.var x => emitLhsSlot_ x
|
| 552 |
+
| _ => do
|
| 553 |
+
let slotty ← LLVM.voidPtrType llvmctx
|
| 554 |
+
let slot ← buildPrologueAlloca builder slotty "irrelevant_slot"
|
| 555 |
+
let v ← callLeanBox builder (← constIntSizeT 0) "irrelevant_val"
|
| 556 |
+
let _ ← LLVM.buildStore builder v slot
|
| 557 |
+
return (slotty, slot)
|
| 558 |
+
|
| 559 |
+
def emitArgVal (builder : LLVM.Builder llvmctx)
|
| 560 |
+
(x : Arg) (name : String := "") : M llvmctx (LLVM.LLVMType llvmctx × LLVM.Value llvmctx) := do
|
| 561 |
+
let (xty, xslot) ← emitArgSlot_ builder x
|
| 562 |
+
let xval ← LLVM.buildLoad2 builder xty xslot name
|
| 563 |
+
return (xty, xval)
|
| 564 |
+
|
| 565 |
+
def emitAllocCtor (builder : LLVM.Builder llvmctx)
|
| 566 |
+
(c : CtorInfo) : M llvmctx (LLVM.Value llvmctx) := do
|
| 567 |
+
-- TODO(bollu) : find the correct size, don't assume 'void*' size is 8
|
| 568 |
+
let hackSizeofVoidPtr := 8
|
| 569 |
+
let scalarSize := hackSizeofVoidPtr * c.usize + c.ssize
|
| 570 |
+
callLeanAllocCtor builder c.cidx c.size scalarSize "lean_alloc_ctor_out"
|
| 571 |
+
|
| 572 |
+
def emitCtorSetArgs (builder : LLVM.Builder llvmctx)
|
| 573 |
+
(z : VarId) (ys : Array Arg) : M llvmctx Unit := do
|
| 574 |
+
ys.size.forM fun i _ => do
|
| 575 |
+
let zv ← emitLhsVal builder z
|
| 576 |
+
let (_yty, yv) ← emitArgVal builder ys[i]
|
| 577 |
+
let iv ← constIntUnsigned i
|
| 578 |
+
callLeanCtorSet builder zv iv yv
|
| 579 |
+
emitLhsSlotStore builder z zv
|
| 580 |
+
pure ()
|
| 581 |
+
|
| 582 |
+
def emitCtor (builder : LLVM.Builder llvmctx)
|
| 583 |
+
(z : VarId) (c : CtorInfo) (ys : Array Arg) : M llvmctx Unit := do
|
| 584 |
+
let (_llvmty, slot) ← emitLhsSlot_ z
|
| 585 |
+
if c.size == 0 && c.usize == 0 && c.ssize == 0 then do
|
| 586 |
+
let v ← callLeanBox builder (← constIntSizeT c.cidx) "lean_box_outv"
|
| 587 |
+
let _ ← LLVM.buildStore builder v slot
|
| 588 |
+
else do
|
| 589 |
+
let v ← emitAllocCtor builder c
|
| 590 |
+
let _ ← LLVM.buildStore builder v slot
|
| 591 |
+
emitCtorSetArgs builder z ys
|
| 592 |
+
|
| 593 |
+
def emitInc (builder : LLVM.Builder llvmctx)
|
| 594 |
+
(x : VarId) (n : Nat) (checkRef? : Bool) : M llvmctx Unit := do
|
| 595 |
+
let xv ← emitLhsVal builder x
|
| 596 |
+
if n != 1
|
| 597 |
+
then do
|
| 598 |
+
let nv ← constIntSizeT n
|
| 599 |
+
callLeanRefcountFn builder (kind := RefcountKind.inc) (checkRef? := checkRef?) (delta := nv) xv
|
| 600 |
+
else callLeanRefcountFn builder (kind := RefcountKind.inc) (checkRef? := checkRef?) xv
|
| 601 |
+
|
| 602 |
+
def emitDec (builder : LLVM.Builder llvmctx)
|
| 603 |
+
(x : VarId) (n : Nat) (checkRef? : Bool) : M llvmctx Unit := do
|
| 604 |
+
let xv ← emitLhsVal builder x
|
| 605 |
+
if n != 1
|
| 606 |
+
then throw "expected n = 1 for emitDec"
|
| 607 |
+
else callLeanRefcountFn builder (kind := RefcountKind.dec) (checkRef? := checkRef?) xv
|
| 608 |
+
|
| 609 |
+
def emitNumLit (builder : LLVM.Builder llvmctx)
|
| 610 |
+
(t : IRType) (v : Nat) : M llvmctx (LLVM.Value llvmctx) := do
|
| 611 |
+
if t.isObj then
|
| 612 |
+
if v < UInt32.size then
|
| 613 |
+
callLeanUnsignedToNatFn builder v
|
| 614 |
+
else
|
| 615 |
+
callLeanCStrToNatFn builder v
|
| 616 |
+
else
|
| 617 |
+
LLVM.constInt (← toLLVMType t) (UInt64.ofNat v)
|
| 618 |
+
|
| 619 |
+
def toHexDigit (c : Nat) : String :=
|
| 620 |
+
String.singleton c.digitChar
|
| 621 |
+
|
| 622 |
+
-- TODO(bollu) : Setup code sharing between 'EmitC' and 'EmitLLVM'
|
| 623 |
+
def quoteString (s : String) : String :=
|
| 624 |
+
let q := "\"";
|
| 625 |
+
let q := s.foldl
|
| 626 |
+
(fun q c => q ++
|
| 627 |
+
if c == '\n' then "\\n"
|
| 628 |
+
else if c == '\r' then "\\r"
|
| 629 |
+
else if c == '\t' then "\\t"
|
| 630 |
+
else if c == '\\' then "\\\\"
|
| 631 |
+
else if c == '\"' then "\\\""
|
| 632 |
+
else if c.toNat <= 31 then
|
| 633 |
+
"\\x" ++ toHexDigit (c.toNat / 16) ++ toHexDigit (c.toNat % 16)
|
| 634 |
+
-- TODO(Leo) : we should use `\unnnn` for escaping unicode characters.
|
| 635 |
+
else String.singleton c)
|
| 636 |
+
q;
|
| 637 |
+
q ++ "\""
|
| 638 |
+
|
| 639 |
+
def emitSimpleExternalCall (builder : LLVM.Builder llvmctx)
|
| 640 |
+
(f : String)
|
| 641 |
+
(ps : Array Param)
|
| 642 |
+
(ys : Array Arg)
|
| 643 |
+
(retty : IRType)
|
| 644 |
+
(name : String) : M llvmctx (LLVM.Value llvmctx) := do
|
| 645 |
+
let mut args := #[]
|
| 646 |
+
let mut argTys := #[]
|
| 647 |
+
for (p, y) in ps.zip ys do
|
| 648 |
+
if !p.ty.isIrrelevant then
|
| 649 |
+
let (_yty, yv) ← emitArgVal builder y ""
|
| 650 |
+
argTys := argTys.push (← toLLVMType p.ty)
|
| 651 |
+
args := args.push yv
|
| 652 |
+
let fnty ← LLVM.functionType (← toLLVMType retty) argTys
|
| 653 |
+
let fn ← LLVM.getOrAddFunction (← getLLVMModule) f fnty
|
| 654 |
+
LLVM.buildCall2 builder fnty fn args name
|
| 655 |
+
|
| 656 |
+
-- TODO: if the external call is one that we cannot code generate, give up and
|
| 657 |
+
-- generate fallback code.
|
| 658 |
+
def emitExternCall (builder : LLVM.Builder llvmctx)
|
| 659 |
+
(f : FunId)
|
| 660 |
+
(ps : Array Param)
|
| 661 |
+
(extData : ExternAttrData)
|
| 662 |
+
(ys : Array Arg) (retty : IRType)
|
| 663 |
+
(name : String := "") : M llvmctx (LLVM.Value llvmctx) :=
|
| 664 |
+
match getExternEntryFor extData `c with
|
| 665 |
+
| some (ExternEntry.standard _ extFn) => emitSimpleExternalCall builder extFn ps ys retty name
|
| 666 |
+
| some (ExternEntry.inline `llvm _pat) => throw "Unimplemented codegen of inline LLVM"
|
| 667 |
+
| some (ExternEntry.inline _ pat) => throw s!"Cannot codegen non-LLVM inline code '{pat}'."
|
| 668 |
+
| some (ExternEntry.opaque _) => unreachable!
|
| 669 |
+
| _ => throw s!"Failed to emit extern application '{f}'."
|
| 670 |
+
|
| 671 |
+
def getFunIdTy (f : FunId) : M llvmctx (LLVM.LLVMType llvmctx) := do
|
| 672 |
+
let decl ← getDecl f
|
| 673 |
+
let retty ← toLLVMType decl.resultType
|
| 674 |
+
let argtys ← decl.params.mapM (fun p => do toLLVMType p.ty)
|
| 675 |
+
LLVM.functionType retty argtys
|
| 676 |
+
|
| 677 |
+
/--
|
| 678 |
+
Create a function declaration and return a pointer to the function.
|
| 679 |
+
If the function actually takes arguments, then we must have a function pointer in scope.
|
| 680 |
+
If the function takes no arguments, then it is a top-level closed term, and its value will
|
| 681 |
+
be stored in a global pointer. So, we load from the global pointer. The type of the global is function pointer pointer.
|
| 682 |
+
This returns a *function pointer.*
|
| 683 |
+
-/
|
| 684 |
+
def getOrAddFunIdValue (builder : LLVM.Builder llvmctx) (f : FunId) : M llvmctx (LLVM.Value llvmctx) := do
|
| 685 |
+
let decl ← getDecl f
|
| 686 |
+
let fcname ← toCName f
|
| 687 |
+
let retty �� toLLVMType decl.resultType
|
| 688 |
+
if decl.params.isEmpty then
|
| 689 |
+
let gslot ← LLVM.getOrAddGlobal (← getLLVMModule) fcname retty
|
| 690 |
+
LLVM.buildLoad2 builder retty gslot
|
| 691 |
+
else
|
| 692 |
+
let argtys ← decl.params.mapM (fun p => do toLLVMType p.ty)
|
| 693 |
+
let fnty ← LLVM.functionType retty argtys
|
| 694 |
+
LLVM.getOrAddFunction (← getLLVMModule) fcname fnty
|
| 695 |
+
|
| 696 |
+
def emitPartialApp (builder : LLVM.Builder llvmctx) (z : VarId) (f : FunId) (ys : Array Arg) : M llvmctx Unit := do
|
| 697 |
+
let decl ← getDecl f
|
| 698 |
+
let fv ← getOrAddFunIdValue builder f
|
| 699 |
+
let arity := decl.params.size
|
| 700 |
+
let (_zty, zslot) ← emitLhsSlot_ z
|
| 701 |
+
let zval ← callLeanAllocClosureFn builder fv
|
| 702 |
+
(← constIntUnsigned arity)
|
| 703 |
+
(← constIntUnsigned ys.size)
|
| 704 |
+
LLVM.buildStore builder zval zslot
|
| 705 |
+
ys.size.forM fun i _ => do
|
| 706 |
+
let (yty, yslot) ← emitArgSlot_ builder ys[i]
|
| 707 |
+
let yval ← LLVM.buildLoad2 builder yty yslot
|
| 708 |
+
callLeanClosureSetFn builder zval (← constIntUnsigned i) yval
|
| 709 |
+
|
| 710 |
+
def emitApp (builder : LLVM.Builder llvmctx) (z : VarId) (f : VarId) (ys : Array Arg) : M llvmctx Unit := do
|
| 711 |
+
if ys.size > closureMaxArgs then do
|
| 712 |
+
let aargs ← buildPrologueAlloca builder (← LLVM.arrayType (← LLVM.voidPtrType llvmctx) (UInt64.ofNat ys.size)) "aargs"
|
| 713 |
+
for i in List.range ys.size do
|
| 714 |
+
let (yty, yv) ← emitArgVal builder ys[i]!
|
| 715 |
+
let aslot ← LLVM.buildInBoundsGEP2 builder yty aargs #[← constIntUnsigned 0, ← constIntUnsigned i] s!"param_{i}_slot"
|
| 716 |
+
LLVM.buildStore builder yv aslot
|
| 717 |
+
let fnName := s!"lean_apply_m"
|
| 718 |
+
let retty ← LLVM.voidPtrType llvmctx
|
| 719 |
+
let args := #[← emitLhsVal builder f, ← constIntUnsigned ys.size, aargs]
|
| 720 |
+
-- '1 + ...'. '1' for the fn and 'args' for the arguments
|
| 721 |
+
let argtys := #[← LLVM.voidPtrType llvmctx, ← LLVM.unsignedType llvmctx, ← LLVM.voidPtrType llvmctx]
|
| 722 |
+
let fn ← getOrCreateFunctionPrototype (← getLLVMModule) retty fnName argtys
|
| 723 |
+
let fnty ← LLVM.functionType retty argtys
|
| 724 |
+
let zv ← LLVM.buildCall2 builder fnty fn args
|
| 725 |
+
emitLhsSlotStore builder z zv
|
| 726 |
+
else do
|
| 727 |
+
|
| 728 |
+
let fnName := s!"lean_apply_{ys.size}"
|
| 729 |
+
let retty ← LLVM.voidPtrType llvmctx
|
| 730 |
+
let args : Array (LLVM.Value llvmctx) := #[← emitLhsVal builder f] ++ (← ys.mapM (fun y => Prod.snd <$> (emitArgVal builder y)))
|
| 731 |
+
-- '1 + ...'. '1' for the fn and 'args' for the arguments
|
| 732 |
+
let argtys := (List.replicate (1 + ys.size) (← LLVM.voidPtrType llvmctx)).toArray
|
| 733 |
+
let fn ← getOrCreateFunctionPrototype (← getLLVMModule) retty fnName argtys
|
| 734 |
+
let fnty ← LLVM.functionType retty argtys
|
| 735 |
+
let zv ← LLVM.buildCall2 builder fnty fn args
|
| 736 |
+
emitLhsSlotStore builder z zv
|
| 737 |
+
|
| 738 |
+
def emitFullApp (builder : LLVM.Builder llvmctx)
|
| 739 |
+
(z : VarId) (f : FunId) (ys : Array Arg) : M llvmctx Unit := do
|
| 740 |
+
let (__zty, zslot) ← emitLhsSlot_ z
|
| 741 |
+
let decl ← getDecl f
|
| 742 |
+
match decl with
|
| 743 |
+
| .fdecl .. | .extern _ _ _ { entries := [.opaque _] } =>
|
| 744 |
+
if ys.size > 0 then
|
| 745 |
+
let fv ← getOrAddFunIdValue builder f
|
| 746 |
+
let ys ← ys.mapM (fun y => do
|
| 747 |
+
let (yty, yslot) ← emitArgSlot_ builder y
|
| 748 |
+
let yv ← LLVM.buildLoad2 builder yty yslot
|
| 749 |
+
return yv)
|
| 750 |
+
let zv ← LLVM.buildCall2 builder (← getFunIdTy f) fv ys
|
| 751 |
+
LLVM.buildStore builder zv zslot
|
| 752 |
+
else
|
| 753 |
+
let zv ← getOrAddFunIdValue builder f
|
| 754 |
+
LLVM.buildStore builder zv zslot
|
| 755 |
+
| Decl.extern _ ps retty extData =>
|
| 756 |
+
let zv ← emitExternCall builder f ps extData ys retty
|
| 757 |
+
LLVM.buildStore builder zv zslot
|
| 758 |
+
|
| 759 |
+
-- Note that this returns a *slot*, just like `emitLhsSlot_`.
|
| 760 |
+
def emitLit (builder : LLVM.Builder llvmctx)
|
| 761 |
+
(z : VarId) (t : IRType) (v : LitVal) : M llvmctx (LLVM.Value llvmctx) := do
|
| 762 |
+
let llvmty ← toLLVMType t
|
| 763 |
+
let zslot ← buildPrologueAlloca builder llvmty
|
| 764 |
+
addVartoState z zslot llvmty
|
| 765 |
+
let zv ← match v with
|
| 766 |
+
| LitVal.num v => emitNumLit builder t v
|
| 767 |
+
| LitVal.str v =>
|
| 768 |
+
let zero ← constIntUnsigned 0
|
| 769 |
+
let str_global ← LLVM.buildGlobalString builder v
|
| 770 |
+
-- access through the global, into the 0th index of the array
|
| 771 |
+
let strPtr ← LLVM.buildInBoundsGEP2 builder
|
| 772 |
+
(← LLVM.opaquePointerTypeInContext llvmctx)
|
| 773 |
+
str_global #[zero] ""
|
| 774 |
+
let nbytes ← constIntSizeT v.utf8ByteSize
|
| 775 |
+
let nchars ← constIntSizeT v.length
|
| 776 |
+
callLeanMkStringUncheckedFn builder strPtr nbytes nchars ""
|
| 777 |
+
LLVM.buildStore builder zv zslot
|
| 778 |
+
return zslot
|
| 779 |
+
|
| 780 |
+
def callLeanCtorGet (builder : LLVM.Builder llvmctx)
|
| 781 |
+
(x i : LLVM.Value llvmctx) (retName : String) : M llvmctx (LLVM.Value llvmctx) := do
|
| 782 |
+
let fnName := "lean_ctor_get"
|
| 783 |
+
let retty ← LLVM.voidPtrType llvmctx
|
| 784 |
+
let argtys := #[ ← LLVM.voidPtrType llvmctx, ← LLVM.i32Type llvmctx]
|
| 785 |
+
let fnty ← LLVM.functionType retty argtys
|
| 786 |
+
let fn ← getOrCreateFunctionPrototype (← getLLVMModule) retty fnName argtys
|
| 787 |
+
let i ← LLVM.buildSextOrTrunc builder i (← LLVM.i32Type llvmctx)
|
| 788 |
+
LLVM.buildCall2 builder fnty fn #[x, i] retName
|
| 789 |
+
|
| 790 |
+
def emitProj (builder : LLVM.Builder llvmctx) (z : VarId) (i : Nat) (x : VarId) : M llvmctx Unit := do
|
| 791 |
+
let xval ← emitLhsVal builder x
|
| 792 |
+
let zval ← callLeanCtorGet builder xval (← constIntUnsigned i) ""
|
| 793 |
+
emitLhsSlotStore builder z zval
|
| 794 |
+
|
| 795 |
+
def callLeanCtorGetUsize (builder : LLVM.Builder llvmctx)
|
| 796 |
+
(x i : LLVM.Value llvmctx) (retName : String) : M llvmctx (LLVM.Value llvmctx) := do
|
| 797 |
+
let fnName := "lean_ctor_get_usize"
|
| 798 |
+
let retty ← LLVM.size_tType llvmctx
|
| 799 |
+
let argtys := #[ ← LLVM.voidPtrType llvmctx, ← LLVM.unsignedType llvmctx]
|
| 800 |
+
let fnty ← LLVM.functionType retty argtys
|
| 801 |
+
let fn ← getOrCreateFunctionPrototype (← getLLVMModule) retty fnName argtys
|
| 802 |
+
LLVM.buildCall2 builder fnty fn #[x, i] retName
|
| 803 |
+
|
| 804 |
+
def emitUProj (builder : LLVM.Builder llvmctx) (z : VarId) (i : Nat) (x : VarId) : M llvmctx Unit := do
|
| 805 |
+
let xval ← emitLhsVal builder x
|
| 806 |
+
let zval ← callLeanCtorGetUsize builder xval (← constIntUnsigned i) ""
|
| 807 |
+
emitLhsSlotStore builder z zval
|
| 808 |
+
|
| 809 |
+
def emitOffset (builder : LLVM.Builder llvmctx)
|
| 810 |
+
(n : Nat) (offset : Nat) : M llvmctx (LLVM.Value llvmctx) := do
|
| 811 |
+
-- TODO(bollu) : replace 8 with sizeof(void*)
|
| 812 |
+
let out ← constIntUnsigned 8
|
| 813 |
+
let out ← LLVM.buildMul builder out (← constIntUnsigned n) "" -- sizeof(void*)*n
|
| 814 |
+
LLVM.buildAdd builder out (← constIntUnsigned offset) "" -- sizeof(void*)*n+offset
|
| 815 |
+
|
| 816 |
+
def emitSProj (builder : LLVM.Builder llvmctx)
|
| 817 |
+
(z : VarId) (t : IRType) (n offset : Nat) (x : VarId) : M llvmctx Unit := do
|
| 818 |
+
let (fnName, retty) ←
|
| 819 |
+
match t with
|
| 820 |
+
| IRType.float => pure ("lean_ctor_get_float", ← LLVM.doubleTypeInContext llvmctx)
|
| 821 |
+
| IRType.float32 => pure ("lean_ctor_get_float32", ← LLVM.floatTypeInContext llvmctx)
|
| 822 |
+
| IRType.uint8 => pure ("lean_ctor_get_uint8", ← LLVM.i8Type llvmctx)
|
| 823 |
+
| IRType.uint16 => pure ("lean_ctor_get_uint16", ← LLVM.i16Type llvmctx)
|
| 824 |
+
| IRType.uint32 => pure ("lean_ctor_get_uint32", ← LLVM.i32Type llvmctx)
|
| 825 |
+
| IRType.uint64 => pure ("lean_ctor_get_uint64", ← LLVM.i64Type llvmctx)
|
| 826 |
+
| _ => throw s!"Invalid type for lean_ctor_get: '{t}'"
|
| 827 |
+
let argtys := #[ ← LLVM.voidPtrType llvmctx, ← LLVM.unsignedType llvmctx]
|
| 828 |
+
let fn ← getOrCreateFunctionPrototype (← getLLVMModule) retty fnName argtys
|
| 829 |
+
let xval ← emitLhsVal builder x
|
| 830 |
+
let offset ← emitOffset builder n offset
|
| 831 |
+
let fnty ← LLVM.functionType retty argtys
|
| 832 |
+
let zval ← LLVM.buildCall2 builder fnty fn #[xval, offset]
|
| 833 |
+
emitLhsSlotStore builder z zval
|
| 834 |
+
|
| 835 |
+
def callLeanIsExclusive (builder : LLVM.Builder llvmctx)
|
| 836 |
+
(closure : LLVM.Value llvmctx) (retName : String := "") : M llvmctx (LLVM.Value llvmctx) := do
|
| 837 |
+
let fnName := "lean_is_exclusive"
|
| 838 |
+
let retty ← LLVM.i1Type llvmctx
|
| 839 |
+
let argtys := #[ ← LLVM.voidPtrType llvmctx]
|
| 840 |
+
let fn ← getOrCreateFunctionPrototype (← getLLVMModule) retty fnName argtys
|
| 841 |
+
let fnty ← LLVM.functionType retty argtys
|
| 842 |
+
let out ← LLVM.buildCall2 builder fnty fn #[closure] retName
|
| 843 |
+
LLVM.buildSextOrTrunc builder out (← LLVM.i8Type llvmctx)
|
| 844 |
+
|
| 845 |
+
def callLeanIsScalar (builder : LLVM.Builder llvmctx)
|
| 846 |
+
(closure : LLVM.Value llvmctx) (retName : String := "") : M llvmctx (LLVM.Value llvmctx) := do
|
| 847 |
+
let fnName := "lean_is_scalar"
|
| 848 |
+
let retty ← LLVM.i8Type llvmctx
|
| 849 |
+
let argtys := #[ ← LLVM.voidPtrType llvmctx]
|
| 850 |
+
let fn ← getOrCreateFunctionPrototype (← getLLVMModule) retty fnName argtys
|
| 851 |
+
let fnty ← LLVM.functionType retty argtys
|
| 852 |
+
LLVM.buildCall2 builder fnty fn #[closure] retName
|
| 853 |
+
|
| 854 |
+
def emitIsShared (builder : LLVM.Builder llvmctx) (z : VarId) (x : VarId) : M llvmctx Unit := do
|
| 855 |
+
let xv ← emitLhsVal builder x
|
| 856 |
+
let exclusive? ← callLeanIsExclusive builder xv
|
| 857 |
+
let exclusive? ← LLVM.buildSextOrTrunc builder exclusive? (← LLVM.i1Type llvmctx)
|
| 858 |
+
let shared? ← LLVM.buildNot builder exclusive?
|
| 859 |
+
let shared? ← LLVM.buildSext builder shared? (← LLVM.i8Type llvmctx)
|
| 860 |
+
emitLhsSlotStore builder z shared?
|
| 861 |
+
|
| 862 |
+
def emitBox (builder : LLVM.Builder llvmctx) (z : VarId) (x : VarId) (xType : IRType) : M llvmctx Unit := do
|
| 863 |
+
let xv ← emitLhsVal builder x
|
| 864 |
+
let (fnName, argTy, xv) ←
|
| 865 |
+
match xType with
|
| 866 |
+
| IRType.usize => pure ("lean_box_usize", ← LLVM.size_tType llvmctx, xv)
|
| 867 |
+
| IRType.uint32 => pure ("lean_box_uint32", ← LLVM.i32Type llvmctx, xv)
|
| 868 |
+
| IRType.uint64 => pure ("lean_box_uint64", ← LLVM.size_tType llvmctx, xv)
|
| 869 |
+
| IRType.float => pure ("lean_box_float", ← LLVM.doubleTypeInContext llvmctx, xv)
|
| 870 |
+
| IRType.float32 => pure ("lean_box_float32", ← LLVM.floatTypeInContext llvmctx, xv)
|
| 871 |
+
| _ =>
|
| 872 |
+
-- sign extend smaller values into i64
|
| 873 |
+
let xv ← LLVM.buildSext builder xv (← LLVM.size_tType llvmctx)
|
| 874 |
+
pure ("lean_box", ← LLVM.size_tType llvmctx, xv)
|
| 875 |
+
let retty ← LLVM.voidPtrType llvmctx
|
| 876 |
+
let argtys := #[argTy]
|
| 877 |
+
let fn ← getOrCreateFunctionPrototype (← getLLVMModule) retty fnName argtys
|
| 878 |
+
let fnty ← LLVM.functionType retty argtys
|
| 879 |
+
let zv ← LLVM.buildCall2 builder fnty fn #[xv]
|
| 880 |
+
emitLhsSlotStore builder z zv
|
| 881 |
+
|
| 882 |
+
def IRType.isIntegerType (t : IRType) : Bool :=
|
| 883 |
+
match t with
|
| 884 |
+
| .uint8 => true
|
| 885 |
+
| .uint16 => true
|
| 886 |
+
| .uint32 => true
|
| 887 |
+
| .uint64 => true
|
| 888 |
+
| .usize => true
|
| 889 |
+
| _ => false
|
| 890 |
+
|
| 891 |
+
def callUnboxForType (builder : LLVM.Builder llvmctx)
|
| 892 |
+
(t : IRType)
|
| 893 |
+
(v : LLVM.Value llvmctx)
|
| 894 |
+
(retName : String := "") : M llvmctx (LLVM.Value llvmctx) := do
|
| 895 |
+
let (fnName, retty) ←
|
| 896 |
+
match t with
|
| 897 |
+
| IRType.usize => pure ("lean_unbox_usize", ← toLLVMType t)
|
| 898 |
+
| IRType.uint32 => pure ("lean_unbox_uint32", ← toLLVMType t)
|
| 899 |
+
| IRType.uint64 => pure ("lean_unbox_uint64", ← toLLVMType t)
|
| 900 |
+
| IRType.float => pure ("lean_unbox_float", ← toLLVMType t)
|
| 901 |
+
| IRType.float32 => pure ("lean_unbox_float32", ← toLLVMType t)
|
| 902 |
+
| _ => pure ("lean_unbox", ← LLVM.size_tType llvmctx)
|
| 903 |
+
let argtys := #[← LLVM.voidPtrType llvmctx ]
|
| 904 |
+
let fn ← getOrCreateFunctionPrototype (← getLLVMModule) retty fnName argtys
|
| 905 |
+
let fnty ← LLVM.functionType retty argtys
|
| 906 |
+
LLVM.buildCall2 builder fnty fn #[v] retName
|
| 907 |
+
|
| 908 |
+
|
| 909 |
+
|
| 910 |
+
def emitUnbox (builder : LLVM.Builder llvmctx)
|
| 911 |
+
(z : VarId) (t : IRType) (x : VarId) (retName : String := "") : M llvmctx Unit := do
|
| 912 |
+
let zval ← callUnboxForType builder t (← emitLhsVal builder x) retName
|
| 913 |
+
-- NOTE(bollu) : note that lean_unbox only returns an i64, but we may need to truncate to
|
| 914 |
+
-- smaller widths. see `phashmap` for an example of this occurring at calls to `lean_unbox`
|
| 915 |
+
let zval ←
|
| 916 |
+
if IRType.isIntegerType t
|
| 917 |
+
then LLVM.buildSextOrTrunc builder zval (← toLLVMType t)
|
| 918 |
+
else pure zval
|
| 919 |
+
emitLhsSlotStore builder z zval
|
| 920 |
+
|
| 921 |
+
def emitReset (builder : LLVM.Builder llvmctx) (z : VarId) (n : Nat) (x : VarId) : M llvmctx Unit := do
|
| 922 |
+
let xv ← emitLhsVal builder x
|
| 923 |
+
let isExclusive ← callLeanIsExclusive builder xv
|
| 924 |
+
let isExclusive ← buildLeanBoolTrue? builder isExclusive
|
| 925 |
+
buildIfThenElse_ builder "isExclusive" isExclusive
|
| 926 |
+
(fun builder => do
|
| 927 |
+
let xv ← emitLhsVal builder x
|
| 928 |
+
n.forM fun i _ => do
|
| 929 |
+
callLeanCtorRelease builder xv (← constIntUnsigned i)
|
| 930 |
+
emitLhsSlotStore builder z xv
|
| 931 |
+
return ShouldForwardControlFlow.yes
|
| 932 |
+
)
|
| 933 |
+
(fun builder => do
|
| 934 |
+
let xv ← emitLhsVal builder x
|
| 935 |
+
callLeanDecRef builder xv
|
| 936 |
+
let box0 ← callLeanBox builder (← constIntSizeT 0) "box0"
|
| 937 |
+
emitLhsSlotStore builder z box0
|
| 938 |
+
return ShouldForwardControlFlow.yes
|
| 939 |
+
)
|
| 940 |
+
|
| 941 |
+
def emitReuse (builder : LLVM.Builder llvmctx)
|
| 942 |
+
(z : VarId) (x : VarId) (c : CtorInfo) (updtHeader : Bool) (ys : Array Arg) : M llvmctx Unit := do
|
| 943 |
+
let xv ← emitLhsVal builder x
|
| 944 |
+
let isScalar ← callLeanIsScalar builder xv
|
| 945 |
+
let isScalar ← buildLeanBoolTrue? builder isScalar
|
| 946 |
+
buildIfThenElse_ builder "isScalar" isScalar
|
| 947 |
+
(fun builder => do
|
| 948 |
+
let cv ← emitAllocCtor builder c
|
| 949 |
+
emitLhsSlotStore builder z cv
|
| 950 |
+
return ShouldForwardControlFlow.yes
|
| 951 |
+
)
|
| 952 |
+
(fun builder => do
|
| 953 |
+
let xv ← emitLhsVal builder x
|
| 954 |
+
emitLhsSlotStore builder z xv
|
| 955 |
+
if updtHeader then
|
| 956 |
+
let zv ← emitLhsVal builder z
|
| 957 |
+
callLeanCtorSetTag builder zv (← constInt8 c.cidx)
|
| 958 |
+
return ShouldForwardControlFlow.yes
|
| 959 |
+
)
|
| 960 |
+
emitCtorSetArgs builder z ys
|
| 961 |
+
|
| 962 |
+
def emitVDecl (builder : LLVM.Builder llvmctx) (z : VarId) (t : IRType) (v : Expr) : M llvmctx Unit := do
|
| 963 |
+
match v with
|
| 964 |
+
| Expr.ctor c ys => emitCtor builder z c ys
|
| 965 |
+
| Expr.reset n x => emitReset builder z n x
|
| 966 |
+
| Expr.reuse x c u ys => emitReuse builder z x c u ys
|
| 967 |
+
| Expr.proj i x => emitProj builder z i x
|
| 968 |
+
| Expr.uproj i x => emitUProj builder z i x
|
| 969 |
+
| Expr.sproj n o x => emitSProj builder z t n o x
|
| 970 |
+
| Expr.fap c ys => emitFullApp builder z c ys
|
| 971 |
+
| Expr.pap c ys => emitPartialApp builder z c ys
|
| 972 |
+
| Expr.ap x ys => emitApp builder z x ys
|
| 973 |
+
| Expr.box t x => emitBox builder z x t
|
| 974 |
+
| Expr.unbox x => emitUnbox builder z t x
|
| 975 |
+
| Expr.isShared x => emitIsShared builder z x
|
| 976 |
+
| Expr.lit v => let _ ← emitLit builder z t v
|
| 977 |
+
|
| 978 |
+
def declareVar (builder : LLVM.Builder llvmctx) (x : VarId) (t : IRType) : M llvmctx Unit := do
|
| 979 |
+
let llvmty ← toLLVMType t
|
| 980 |
+
let alloca ← buildPrologueAlloca builder llvmty "varx"
|
| 981 |
+
addVartoState x alloca llvmty
|
| 982 |
+
|
| 983 |
+
partial def declareVars (builder : LLVM.Builder llvmctx) (f : FnBody) : M llvmctx Unit := do
|
| 984 |
+
match f with
|
| 985 |
+
| FnBody.vdecl x t _ b => do
|
| 986 |
+
declareVar builder x t
|
| 987 |
+
declareVars builder b
|
| 988 |
+
| FnBody.jdecl _ xs _ b => do
|
| 989 |
+
for param in xs do declareVar builder param.x param.ty
|
| 990 |
+
declareVars builder b
|
| 991 |
+
| e => do
|
| 992 |
+
if e.isTerminal then pure () else declareVars builder e.body
|
| 993 |
+
|
| 994 |
+
def emitTag (builder : LLVM.Builder llvmctx) (x : VarId) (xType : IRType) : M llvmctx (LLVM.Value llvmctx) := do
|
| 995 |
+
if xType.isObj then do
|
| 996 |
+
let xval ← emitLhsVal builder x
|
| 997 |
+
callLeanObjTag builder xval
|
| 998 |
+
else if xType.isScalar then do
|
| 999 |
+
emitLhsVal builder x
|
| 1000 |
+
else
|
| 1001 |
+
throw "Do not know how to `emitTag` in general."
|
| 1002 |
+
|
| 1003 |
+
def emitSet (builder : LLVM.Builder llvmctx) (x : VarId) (i : Nat) (y : Arg) : M llvmctx Unit := do
|
| 1004 |
+
let fnName := "lean_ctor_set"
|
| 1005 |
+
let retty ← LLVM.voidType llvmctx
|
| 1006 |
+
let argtys := #[ ← LLVM.voidPtrType llvmctx, ← LLVM.unsignedType llvmctx , ← LLVM.voidPtrType llvmctx]
|
| 1007 |
+
let fn ← getOrCreateFunctionPrototype (← getLLVMModule) retty fnName argtys
|
| 1008 |
+
let fnty ← LLVM.functionType retty argtys
|
| 1009 |
+
let _ ← LLVM.buildCall2 builder fnty fn #[← emitLhsVal builder x, ← constIntUnsigned i, (← emitArgVal builder y).2]
|
| 1010 |
+
|
| 1011 |
+
def emitUSet (builder : LLVM.Builder llvmctx) (x : VarId) (i : Nat) (y : VarId) : M llvmctx Unit := do
|
| 1012 |
+
let fnName := "lean_ctor_set_usize"
|
| 1013 |
+
let retty ← LLVM.voidType llvmctx
|
| 1014 |
+
let argtys := #[ ← LLVM.voidPtrType llvmctx, ← LLVM.unsignedType llvmctx, ← LLVM.size_tType llvmctx]
|
| 1015 |
+
let fn ← getOrCreateFunctionPrototype (← getLLVMModule) retty fnName argtys
|
| 1016 |
+
let fnty ← LLVM.functionType retty argtys
|
| 1017 |
+
let _ ← LLVM.buildCall2 builder fnty fn #[← emitLhsVal builder x, ← constIntUnsigned i, (← emitLhsVal builder y)]
|
| 1018 |
+
|
| 1019 |
+
def emitTailCall (builder : LLVM.Builder llvmctx) (f : FunId) (v : Expr) : M llvmctx Unit := do
|
| 1020 |
+
match v with
|
| 1021 |
+
| Expr.fap _ ys => do
|
| 1022 |
+
let llvmctx ← read
|
| 1023 |
+
let ps := llvmctx.mainParams
|
| 1024 |
+
unless ps.size == ys.size do throw s!"Invalid tail call. f:'{f}' v:'{v}'"
|
| 1025 |
+
let args ← ys.mapM (fun y => Prod.snd <$> emitArgVal builder y)
|
| 1026 |
+
let fn ← builderGetInsertionFn builder
|
| 1027 |
+
let call ← LLVM.buildCall2 builder (← getFunIdTy f) fn args
|
| 1028 |
+
-- TODO (bollu) : add 'musttail' attribute using the C API.
|
| 1029 |
+
LLVM.setTailCall call true -- mark as tail call
|
| 1030 |
+
let _ ← LLVM.buildRet builder call
|
| 1031 |
+
| _ => throw s!"EmitTailCall expects function application, found '{v}'"
|
| 1032 |
+
|
| 1033 |
+
def emitJmp (builder : LLVM.Builder llvmctx) (jp : JoinPointId) (xs : Array Arg) : M llvmctx Unit := do
|
| 1034 |
+
let llvmctx ← read
|
| 1035 |
+
let ps ← match llvmctx.jpMap[jp]? with
|
| 1036 |
+
| some ps => pure ps
|
| 1037 |
+
| none => throw s!"Unknown join point {jp}"
|
| 1038 |
+
unless xs.size == ps.size do throw s!"Invalid goto, mismatched sizes between arguments, formal parameters."
|
| 1039 |
+
for (p, x) in ps.zip xs do
|
| 1040 |
+
let (_xty, xv) ← emitArgVal builder x
|
| 1041 |
+
emitLhsSlotStore builder p.x xv
|
| 1042 |
+
let _ ← LLVM.buildBr builder (← emitJp jp)
|
| 1043 |
+
|
| 1044 |
+
def emitSSet (builder : LLVM.Builder llvmctx) (x : VarId) (n : Nat) (offset : Nat) (y : VarId) (t : IRType) : M llvmctx Unit := do
|
| 1045 |
+
let (fnName, setty) ←
|
| 1046 |
+
match t with
|
| 1047 |
+
| IRType.float => pure ("lean_ctor_set_float", ← LLVM.doubleTypeInContext llvmctx)
|
| 1048 |
+
| IRType.float32 => pure ("lean_ctor_set_float32", ← LLVM.floatTypeInContext llvmctx)
|
| 1049 |
+
| IRType.uint8 => pure ("lean_ctor_set_uint8", ← LLVM.i8Type llvmctx)
|
| 1050 |
+
| IRType.uint16 => pure ("lean_ctor_set_uint16", ← LLVM.i16Type llvmctx)
|
| 1051 |
+
| IRType.uint32 => pure ("lean_ctor_set_uint32", ← LLVM.i32Type llvmctx)
|
| 1052 |
+
| IRType.uint64 => pure ("lean_ctor_set_uint64", ← LLVM.i64Type llvmctx)
|
| 1053 |
+
| _ => throw s!"invalid type for 'lean_ctor_set': '{t}'"
|
| 1054 |
+
let argtys := #[ ← LLVM.voidPtrType llvmctx, ← LLVM.unsignedType llvmctx, setty]
|
| 1055 |
+
let retty ← LLVM.voidType llvmctx
|
| 1056 |
+
let fn ← getOrCreateFunctionPrototype (← getLLVMModule) retty fnName argtys
|
| 1057 |
+
let xv ← emitLhsVal builder x
|
| 1058 |
+
let offset ← emitOffset builder n offset
|
| 1059 |
+
let yv ← emitLhsVal builder y
|
| 1060 |
+
let fnty ← LLVM.functionType retty argtys
|
| 1061 |
+
let _ ← LLVM.buildCall2 builder fnty fn #[xv, offset, yv]
|
| 1062 |
+
|
| 1063 |
+
def emitDel (builder : LLVM.Builder llvmctx) (x : VarId) : M llvmctx Unit := do
|
| 1064 |
+
let argtys := #[ ← LLVM.voidPtrType llvmctx]
|
| 1065 |
+
let retty ← LLVM.voidType llvmctx
|
| 1066 |
+
let fn ← getOrCreateFunctionPrototype (← getLLVMModule) retty "lean_free_object" argtys
|
| 1067 |
+
let xv ← emitLhsVal builder x
|
| 1068 |
+
let fnty ← LLVM.functionType retty argtys
|
| 1069 |
+
let _ ← LLVM.buildCall2 builder fnty fn #[xv]
|
| 1070 |
+
|
| 1071 |
+
def emitSetTag (builder : LLVM.Builder llvmctx) (x : VarId) (i : Nat) : M llvmctx Unit := do
|
| 1072 |
+
let argtys := #[← LLVM.voidPtrType llvmctx, ← LLVM.i8Type llvmctx]
|
| 1073 |
+
let retty ← LLVM.voidType llvmctx
|
| 1074 |
+
let fn ← getOrCreateFunctionPrototype (← getLLVMModule) retty "lean_ctor_set_tag" argtys
|
| 1075 |
+
let xv ← emitLhsVal builder x
|
| 1076 |
+
let fnty ← LLVM.functionType retty argtys
|
| 1077 |
+
let _ ← LLVM.buildCall2 builder fnty fn #[xv, ← constInt8 i]
|
| 1078 |
+
|
| 1079 |
+
def ensureHasDefault' (alts : Array Alt) : Array Alt :=
|
| 1080 |
+
if alts.any Alt.isDefault then alts
|
| 1081 |
+
else
|
| 1082 |
+
let last := alts.back!
|
| 1083 |
+
let alts := alts.pop
|
| 1084 |
+
alts.push (Alt.default last.body)
|
| 1085 |
+
|
| 1086 |
+
mutual
|
| 1087 |
+
partial def emitCase (builder : LLVM.Builder llvmctx)
|
| 1088 |
+
(x : VarId) (xType : IRType) (alts : Array Alt) : M llvmctx Unit := do
|
| 1089 |
+
let oldBB ← LLVM.getInsertBlock builder
|
| 1090 |
+
-- NOTE: In this context, 'Zext' versus 'Sext' have a meaningful semantic difference.
|
| 1091 |
+
-- We perform a zero extend so that one-bit tags of `0/-1` actually extend to `0/1`
|
| 1092 |
+
-- in 64-bit space.
|
| 1093 |
+
let tag ← emitTag builder x xType
|
| 1094 |
+
let tag ← LLVM.buildZext builder tag (← LLVM.i64Type llvmctx)
|
| 1095 |
+
let alts := ensureHasDefault' alts
|
| 1096 |
+
let defaultBB ← builderAppendBasicBlock builder s!"case_{xType}_default"
|
| 1097 |
+
let numCasesHint := alts.size
|
| 1098 |
+
let switch ← LLVM.buildSwitch builder tag defaultBB (UInt64.ofNat numCasesHint)
|
| 1099 |
+
alts.forM fun alt => do
|
| 1100 |
+
match alt with
|
| 1101 |
+
| Alt.ctor c b =>
|
| 1102 |
+
let destbb ← builderAppendBasicBlock builder s!"case_{xType}_{c.name}_{c.cidx}"
|
| 1103 |
+
LLVM.addCase switch (← constIntSizeT c.cidx) destbb
|
| 1104 |
+
LLVM.positionBuilderAtEnd builder destbb
|
| 1105 |
+
emitFnBody builder b
|
| 1106 |
+
| Alt.default b =>
|
| 1107 |
+
LLVM.positionBuilderAtEnd builder defaultBB
|
| 1108 |
+
emitFnBody builder b
|
| 1109 |
+
LLVM.clearInsertionPosition builder
|
| 1110 |
+
LLVM.positionBuilderAtEnd builder oldBB -- reset state to previous insertion point.
|
| 1111 |
+
|
| 1112 |
+
-- NOTE: emitJP promises to keep the builder context untouched.
|
| 1113 |
+
partial def emitJDecl (builder : LLVM.Builder llvmctx)
|
| 1114 |
+
(jp : JoinPointId) (_ps : Array Param) (b : FnBody) : M llvmctx Unit := do
|
| 1115 |
+
let oldBB ← LLVM.getInsertBlock builder
|
| 1116 |
+
let jpbb ← builderAppendBasicBlock builder s!"jp_{jp.idx}"
|
| 1117 |
+
addJpTostate jp jpbb
|
| 1118 |
+
LLVM.positionBuilderAtEnd builder jpbb
|
| 1119 |
+
-- NOTE(bollu) : Note that we declare the slots for the variables that are inside
|
| 1120 |
+
-- the join point body before emitting the join point body.
|
| 1121 |
+
-- This ensures reachability via dominance.
|
| 1122 |
+
-- TODO(bollu) : Eliminate the need entirely for 'alloca'/slots by generating SSA phi nodes
|
| 1123 |
+
-- directly as discussed with digamma(Mario Carneiro <di.gama@gmail.com>)
|
| 1124 |
+
declareVars builder b
|
| 1125 |
+
emitBlock builder b
|
| 1126 |
+
LLVM.positionBuilderAtEnd builder oldBB -- reset state
|
| 1127 |
+
|
| 1128 |
+
partial def emitUnreachable (builder : LLVM.Builder llvmctx) : M llvmctx Unit := do
|
| 1129 |
+
let retty ← LLVM.voidType llvmctx
|
| 1130 |
+
let argtys := #[]
|
| 1131 |
+
let fn ← getOrCreateFunctionPrototype (← getLLVMModule) retty "lean_internal_panic_unreachable" argtys
|
| 1132 |
+
let fnty ← LLVM.functionType retty argtys
|
| 1133 |
+
let _ ← LLVM.buildCall2 builder fnty fn #[]
|
| 1134 |
+
let _ ← LLVM.buildUnreachable builder
|
| 1135 |
+
|
| 1136 |
+
partial def emitBlock (builder : LLVM.Builder llvmctx) (b : FnBody) : M llvmctx Unit := do
|
| 1137 |
+
match b with
|
| 1138 |
+
| FnBody.jdecl j xs v b =>
|
| 1139 |
+
emitJDecl builder j xs v
|
| 1140 |
+
emitBlock builder b
|
| 1141 |
+
| d@(FnBody.vdecl x t v b) => do
|
| 1142 |
+
let llvmctx ← read
|
| 1143 |
+
if isTailCallTo llvmctx.mainFn d then
|
| 1144 |
+
emitTailCall builder llvmctx.mainFn v
|
| 1145 |
+
else
|
| 1146 |
+
emitVDecl builder x t v
|
| 1147 |
+
emitBlock builder b
|
| 1148 |
+
| FnBody.inc x n c p b =>
|
| 1149 |
+
unless p do emitInc builder x n c
|
| 1150 |
+
emitBlock builder b
|
| 1151 |
+
| FnBody.dec x n c p b =>
|
| 1152 |
+
unless p do emitDec builder x n c
|
| 1153 |
+
emitBlock builder b
|
| 1154 |
+
| FnBody.del x b => emitDel builder x; emitBlock builder b
|
| 1155 |
+
| FnBody.setTag x i b => emitSetTag builder x i; emitBlock builder b
|
| 1156 |
+
| FnBody.set x i y b => emitSet builder x i y; emitBlock builder b
|
| 1157 |
+
| FnBody.uset x i y b => emitUSet builder x i y; emitBlock builder b
|
| 1158 |
+
| FnBody.sset x i o y t b => emitSSet builder x i o y t; emitBlock builder b
|
| 1159 |
+
| FnBody.mdata _ b => emitBlock builder b
|
| 1160 |
+
| FnBody.ret x => do
|
| 1161 |
+
let (_xty, xv) ← emitArgVal builder x "ret_val"
|
| 1162 |
+
let _ ← LLVM.buildRet builder xv
|
| 1163 |
+
| FnBody.case _ x xType alts =>
|
| 1164 |
+
emitCase builder x xType alts
|
| 1165 |
+
| FnBody.jmp j xs =>
|
| 1166 |
+
emitJmp builder j xs
|
| 1167 |
+
| FnBody.unreachable => emitUnreachable builder
|
| 1168 |
+
|
| 1169 |
+
partial def emitFnBody (builder : LLVM.Builder llvmctx) (b : FnBody) : M llvmctx Unit := do
|
| 1170 |
+
declareVars builder b
|
| 1171 |
+
emitBlock builder b
|
| 1172 |
+
|
| 1173 |
+
end
|
| 1174 |
+
|
| 1175 |
+
def emitFnArgs (builder : LLVM.Builder llvmctx)
|
| 1176 |
+
(needsPackedArgs? : Bool) (llvmfn : LLVM.Value llvmctx) (params : Array Param) : M llvmctx Unit := do
|
| 1177 |
+
if needsPackedArgs? then do
|
| 1178 |
+
let argsp ← LLVM.getParam llvmfn 0 -- lean_object **args
|
| 1179 |
+
for h : i in [:params.size] do
|
| 1180 |
+
let param := params[i]
|
| 1181 |
+
-- argsi := (args + i)
|
| 1182 |
+
let argsi ← LLVM.buildGEP2 builder (← LLVM.voidPtrType llvmctx) argsp #[← constIntUnsigned i] s!"packed_arg_{i}_slot"
|
| 1183 |
+
let llvmty ← toLLVMType param.ty
|
| 1184 |
+
-- pv := *(argsi) = *(args + i)
|
| 1185 |
+
let pv ← LLVM.buildLoad2 builder llvmty argsi
|
| 1186 |
+
-- slot for arg[i] which is always void* ?
|
| 1187 |
+
let alloca ← buildPrologueAlloca builder llvmty s!"arg_{i}"
|
| 1188 |
+
LLVM.buildStore builder pv alloca
|
| 1189 |
+
addVartoState param.x alloca llvmty
|
| 1190 |
+
else
|
| 1191 |
+
let n ← LLVM.countParams llvmfn
|
| 1192 |
+
for i in [:n.toNat] do
|
| 1193 |
+
let param := params[i]!
|
| 1194 |
+
let llvmty ← toLLVMType param.ty
|
| 1195 |
+
let alloca ← buildPrologueAlloca builder llvmty s!"arg_{i}"
|
| 1196 |
+
let arg ← LLVM.getParam llvmfn (UInt64.ofNat i)
|
| 1197 |
+
let _ ← LLVM.buildStore builder arg alloca
|
| 1198 |
+
addVartoState param.x alloca llvmty
|
| 1199 |
+
|
| 1200 |
+
def emitDeclAux (mod : LLVM.Module llvmctx) (builder : LLVM.Builder llvmctx) (d : Decl) : M llvmctx Unit := do
|
| 1201 |
+
let env ← getEnv
|
| 1202 |
+
let (_, jpMap) := mkVarJPMaps d
|
| 1203 |
+
withReader (fun llvmctx => { llvmctx with jpMap := jpMap }) do
|
| 1204 |
+
unless hasInitAttr env d.name do
|
| 1205 |
+
match d with
|
| 1206 |
+
| .fdecl (f := f) (xs := xs) (type := t) (body := b) .. =>
|
| 1207 |
+
let baseName ← toCName f
|
| 1208 |
+
let name := if xs.size > 0 then baseName else "_init_" ++ baseName
|
| 1209 |
+
let retty ← toLLVMType t
|
| 1210 |
+
let mut argtys := #[]
|
| 1211 |
+
let needsPackedArgs? := xs.size > closureMaxArgs && isBoxedName d.name
|
| 1212 |
+
if needsPackedArgs? then
|
| 1213 |
+
argtys := #[← LLVM.pointerType (← LLVM.voidPtrType llvmctx)]
|
| 1214 |
+
else
|
| 1215 |
+
for x in xs do
|
| 1216 |
+
argtys := argtys.push (← toLLVMType x.ty)
|
| 1217 |
+
let fnty ← LLVM.functionType retty argtys (isVarArg := false)
|
| 1218 |
+
let llvmfn ← LLVM.getOrAddFunction mod name fnty
|
| 1219 |
+
-- set linkage and visibility
|
| 1220 |
+
-- TODO: consider refactoring these into a separate concept (e.g. 'setLinkageAndVisibility')
|
| 1221 |
+
-- Find the spots where this refactor needs to happen by grepping for 'LEAN_EXPORT'
|
| 1222 |
+
-- in the C backend
|
| 1223 |
+
if xs.size == 0 then
|
| 1224 |
+
LLVM.setVisibility llvmfn LLVM.Visibility.hidden -- "static "
|
| 1225 |
+
else
|
| 1226 |
+
LLVM.setDLLStorageClass llvmfn LLVM.DLLStorageClass.export -- LEAN_EXPORT: make symbol visible to the interpreter
|
| 1227 |
+
withReader (fun llvmctx => { llvmctx with mainFn := f, mainParams := xs }) do
|
| 1228 |
+
set { var2val := default, jp2bb := default : EmitLLVM.State llvmctx } -- flush variable map
|
| 1229 |
+
let bb ← LLVM.appendBasicBlockInContext llvmctx llvmfn "entry"
|
| 1230 |
+
LLVM.positionBuilderAtEnd builder bb
|
| 1231 |
+
emitFnArgs builder needsPackedArgs? llvmfn xs
|
| 1232 |
+
emitFnBody builder b
|
| 1233 |
+
pure ()
|
| 1234 |
+
| _ => pure ()
|
| 1235 |
+
|
| 1236 |
+
def emitDecl (mod : LLVM.Module llvmctx) (builder : LLVM.Builder llvmctx) (d : Decl) : M llvmctx Unit := do
|
| 1237 |
+
let d := d.normalizeIds -- ensure we don't have gaps in the variable indices
|
| 1238 |
+
try
|
| 1239 |
+
emitDeclAux mod builder d
|
| 1240 |
+
return ()
|
| 1241 |
+
catch err =>
|
| 1242 |
+
throw (s!"emitDecl:\ncompiling:\n{d}\nerr:\n{err}\n")
|
| 1243 |
+
|
| 1244 |
+
def emitFns (mod : LLVM.Module llvmctx) (builder : LLVM.Builder llvmctx) : M llvmctx Unit := do
|
| 1245 |
+
let env ← getEnv
|
| 1246 |
+
let decls := getDecls env
|
| 1247 |
+
decls.reverse.forM (emitDecl mod builder)
|
| 1248 |
+
|
| 1249 |
+
def callIODeclInitFn (builder : LLVM.Builder llvmctx)
|
| 1250 |
+
(initFnName : String)
|
| 1251 |
+
(world : LLVM.Value llvmctx): M llvmctx (LLVM.Value llvmctx) := do
|
| 1252 |
+
let retty ← LLVM.voidPtrType llvmctx
|
| 1253 |
+
let argtys := #[← LLVM.voidPtrType llvmctx]
|
| 1254 |
+
let fn ← getOrCreateFunctionPrototype (← getLLVMModule) retty initFnName argtys
|
| 1255 |
+
let fnty ← LLVM.functionType retty argtys
|
| 1256 |
+
LLVM.buildCall2 builder fnty fn #[world]
|
| 1257 |
+
|
| 1258 |
+
def callPureDeclInitFn (builder : LLVM.Builder llvmctx)
|
| 1259 |
+
(initFnName : String)
|
| 1260 |
+
(retty : LLVM.LLVMType llvmctx): M llvmctx (LLVM.Value llvmctx) := do
|
| 1261 |
+
let argtys := #[]
|
| 1262 |
+
let fn ← getOrCreateFunctionPrototype (← getLLVMModule) retty initFnName argtys
|
| 1263 |
+
let fnty ← LLVM.functionType retty argtys
|
| 1264 |
+
LLVM.buildCall2 builder fnty fn #[]
|
| 1265 |
+
|
| 1266 |
+
def emitDeclInit (builder : LLVM.Builder llvmctx)
|
| 1267 |
+
(parentFn : LLVM.Value llvmctx) (d : Decl) : M llvmctx Unit := do
|
| 1268 |
+
let env ← getEnv
|
| 1269 |
+
if isIOUnitInitFn env d.name then do
|
| 1270 |
+
let world ← callLeanIOMkWorld builder
|
| 1271 |
+
let resv ← callIODeclInitFn builder (← toCName d.name) world
|
| 1272 |
+
let err? ← callLeanIOResultIsError builder resv "is_error"
|
| 1273 |
+
buildIfThen_ builder s!"init_{d.name}_isError" err?
|
| 1274 |
+
(fun builder => do
|
| 1275 |
+
let _ ← LLVM.buildRet builder resv
|
| 1276 |
+
pure ShouldForwardControlFlow.no)
|
| 1277 |
+
-- TODO (bollu) : emit lean_dec_ref. For now, it does not matter.
|
| 1278 |
+
else if d.params.size == 0 then
|
| 1279 |
+
match getInitFnNameFor? env d.name with
|
| 1280 |
+
| some initFn =>
|
| 1281 |
+
let llvmty ← toLLVMType d.resultType
|
| 1282 |
+
let dslot ← LLVM.getOrAddGlobal (← getLLVMModule) (← toCName d.name) llvmty
|
| 1283 |
+
LLVM.setInitializer dslot (← LLVM.getUndef llvmty)
|
| 1284 |
+
let initBB ← builderAppendBasicBlock builder s!"do_{d.name}_init"
|
| 1285 |
+
let restBB ← builderAppendBasicBlock builder s!"post_{d.name}_init"
|
| 1286 |
+
let checkBuiltin? := getBuiltinInitFnNameFor? env d.name |>.isSome
|
| 1287 |
+
if checkBuiltin? then
|
| 1288 |
+
-- `builtin` is set to true if the initializer is part of the executable,
|
| 1289 |
+
-- and not loaded dynamically.
|
| 1290 |
+
let builtinParam ← LLVM.getParam parentFn 0
|
| 1291 |
+
let cond ← buildLeanBoolTrue? builder builtinParam "is_builtin_true"
|
| 1292 |
+
let _ ← LLVM.buildCondBr builder cond initBB restBB
|
| 1293 |
+
else
|
| 1294 |
+
let _ ← LLVM.buildBr builder initBB
|
| 1295 |
+
LLVM.positionBuilderAtEnd builder initBB
|
| 1296 |
+
let world ← callLeanIOMkWorld builder
|
| 1297 |
+
let resv ← callIODeclInitFn builder (← toCName initFn) world
|
| 1298 |
+
let err? ← callLeanIOResultIsError builder resv s!"{d.name}_is_error"
|
| 1299 |
+
buildIfThen_ builder s!"init_{d.name}_isError" err?
|
| 1300 |
+
(fun builder => do
|
| 1301 |
+
let _ ← LLVM.buildRet builder resv
|
| 1302 |
+
pure ShouldForwardControlFlow.no)
|
| 1303 |
+
if d.resultType.isScalar then
|
| 1304 |
+
let dval ← callLeanIOResultGetValue builder resv s!"{d.name}_res"
|
| 1305 |
+
let dval ← callUnboxForType builder d.resultType dval
|
| 1306 |
+
LLVM.buildStore builder dval dslot
|
| 1307 |
+
else
|
| 1308 |
+
let dval ← callLeanIOResultGetValue builder resv s!"{d.name}_res"
|
| 1309 |
+
LLVM.buildStore builder dval dslot
|
| 1310 |
+
callLeanMarkPersistentFn builder dval
|
| 1311 |
+
let _ ← LLVM.buildBr builder restBB
|
| 1312 |
+
LLVM.positionBuilderAtEnd builder restBB
|
| 1313 |
+
| none => do
|
| 1314 |
+
let llvmty ← toLLVMType d.resultType
|
| 1315 |
+
let dslot ← LLVM.getOrAddGlobal (← getLLVMModule) (← toCName d.name) llvmty
|
| 1316 |
+
LLVM.setInitializer dslot (← LLVM.getUndef llvmty)
|
| 1317 |
+
let dval ← callPureDeclInitFn builder (← toCInitName d.name) (← toLLVMType d.resultType)
|
| 1318 |
+
LLVM.buildStore builder dval dslot
|
| 1319 |
+
if d.resultType.isObj then
|
| 1320 |
+
callLeanMarkPersistentFn builder dval
|
| 1321 |
+
|
| 1322 |
+
def callModInitFn (builder : LLVM.Builder llvmctx)
|
| 1323 |
+
(modName : Name) (input world : LLVM.Value llvmctx) (retName : String): M llvmctx (LLVM.Value llvmctx) := do
|
| 1324 |
+
let fnName := mkModuleInitializationFunctionName modName
|
| 1325 |
+
let retty ← LLVM.voidPtrType llvmctx
|
| 1326 |
+
let argtys := #[ (← LLVM.i8Type llvmctx), (← LLVM.voidPtrType llvmctx)]
|
| 1327 |
+
let fn ← getOrCreateFunctionPrototype (← getLLVMModule) retty fnName argtys
|
| 1328 |
+
let fnty ← LLVM.functionType retty argtys
|
| 1329 |
+
LLVM.buildCall2 builder fnty fn #[input, world] retName
|
| 1330 |
+
|
| 1331 |
+
def emitInitFn (mod : LLVM.Module llvmctx) (builder : LLVM.Builder llvmctx) : M llvmctx Unit := do
|
| 1332 |
+
let env ← getEnv
|
| 1333 |
+
let modName ← getModName
|
| 1334 |
+
|
| 1335 |
+
let initFnTy ← LLVM.functionType (← LLVM.voidPtrType llvmctx) #[ (← LLVM.i8Type llvmctx), (← LLVM.voidPtrType llvmctx)] (isVarArg := false)
|
| 1336 |
+
let initFn ← LLVM.getOrAddFunction mod (mkModuleInitializationFunctionName modName) initFnTy
|
| 1337 |
+
LLVM.setDLLStorageClass initFn LLVM.DLLStorageClass.export -- LEAN_EXPORT
|
| 1338 |
+
let entryBB ← LLVM.appendBasicBlockInContext llvmctx initFn "entry"
|
| 1339 |
+
LLVM.positionBuilderAtEnd builder entryBB
|
| 1340 |
+
let ginit?ty := ← LLVM.i1Type llvmctx
|
| 1341 |
+
let ginit?slot ← LLVM.getOrAddGlobal mod (modName.mangle ++ "_G_initialized") ginit?ty
|
| 1342 |
+
LLVM.setVisibility ginit?slot LLVM.Visibility.hidden -- static
|
| 1343 |
+
LLVM.setInitializer ginit?slot (← LLVM.constFalse llvmctx)
|
| 1344 |
+
let ginit?v ← LLVM.buildLoad2 builder ginit?ty ginit?slot "init_v"
|
| 1345 |
+
buildIfThen_ builder "isGInitialized" ginit?v
|
| 1346 |
+
(fun builder => do
|
| 1347 |
+
let box0 ← callLeanBox builder (← constIntSizeT 0) "box0"
|
| 1348 |
+
let out ← callLeanIOResultMKOk builder box0 "retval"
|
| 1349 |
+
let _ ← LLVM.buildRet builder out
|
| 1350 |
+
pure ShouldForwardControlFlow.no)
|
| 1351 |
+
LLVM.buildStore builder (← LLVM.constTrue llvmctx) ginit?slot
|
| 1352 |
+
|
| 1353 |
+
env.imports.forM fun import_ => do
|
| 1354 |
+
let builtin ← LLVM.getParam initFn 0
|
| 1355 |
+
let world ← callLeanIOMkWorld builder
|
| 1356 |
+
let res ← callModInitFn builder import_.module builtin world ("res_" ++ import_.module.mangle)
|
| 1357 |
+
let err? ← callLeanIOResultIsError builder res ("res_is_error_" ++ import_.module.mangle)
|
| 1358 |
+
buildIfThen_ builder ("IsError" ++ import_.module.mangle) err?
|
| 1359 |
+
(fun builder => do
|
| 1360 |
+
let _ ← LLVM.buildRet builder res
|
| 1361 |
+
pure ShouldForwardControlFlow.no)
|
| 1362 |
+
callLeanDecRef builder res
|
| 1363 |
+
let decls := getDecls env
|
| 1364 |
+
decls.reverse.forM (emitDeclInit builder initFn)
|
| 1365 |
+
let box0 ← callLeanBox builder (← constIntSizeT 0) "box0"
|
| 1366 |
+
let out ← callLeanIOResultMKOk builder box0 "retval"
|
| 1367 |
+
let _ ← LLVM.buildRet builder out
|
| 1368 |
+
|
| 1369 |
+
def callLeanInitialize (builder : LLVM.Builder llvmctx) : M llvmctx Unit := do
|
| 1370 |
+
let fnName := "lean_initialize"
|
| 1371 |
+
let retty ← LLVM.voidType llvmctx
|
| 1372 |
+
let argtys := #[]
|
| 1373 |
+
let fnty ← LLVM.functionType retty argtys
|
| 1374 |
+
let fn ← getOrCreateFunctionPrototype (← getLLVMModule) retty fnName argtys
|
| 1375 |
+
let _ ← LLVM.buildCall2 builder fnty fn #[]
|
| 1376 |
+
|
| 1377 |
+
def callLeanSetupLibUV (builder : LLVM.Builder llvmctx) (argc argv : LLVM.Value llvmctx) : M llvmctx (LLVM.Value llvmctx) := do
|
| 1378 |
+
let fnName := "lean_setup_args"
|
| 1379 |
+
let intTy ← LLVM.i32Type llvmctx
|
| 1380 |
+
let charPtrPtrTy ← LLVM.pointerType (← LLVM.pointerType (← LLVM.i8Type llvmctx))
|
| 1381 |
+
let argtys := #[intTy, charPtrPtrTy]
|
| 1382 |
+
let fnty ← LLVM.functionType charPtrPtrTy argtys
|
| 1383 |
+
let fn ← getOrCreateFunctionPrototype (← getLLVMModule) intTy fnName argtys
|
| 1384 |
+
LLVM.buildCall2 builder fnty fn #[argc, argv]
|
| 1385 |
+
|
| 1386 |
+
def callLeanInitializeRuntimeModule (builder : LLVM.Builder llvmctx) : M llvmctx Unit := do
|
| 1387 |
+
let fnName := "lean_initialize_runtime_module"
|
| 1388 |
+
let retty ← LLVM.voidType llvmctx
|
| 1389 |
+
let argtys := #[]
|
| 1390 |
+
let fnty ← LLVM.functionType retty argtys
|
| 1391 |
+
let fn ← getOrCreateFunctionPrototype (← getLLVMModule) retty fnName argtys
|
| 1392 |
+
let _ ← LLVM.buildCall2 builder fnty fn #[]
|
| 1393 |
+
|
| 1394 |
+
def callLeanSetPanicMessages (builder : LLVM.Builder llvmctx)
|
| 1395 |
+
(enable? : LLVM.Value llvmctx) : M llvmctx Unit := do
|
| 1396 |
+
let fnName := "lean_set_panic_messages"
|
| 1397 |
+
let retty ← LLVM.voidType llvmctx
|
| 1398 |
+
let argtys := #[ ← LLVM.i1Type llvmctx ]
|
| 1399 |
+
let fn ← getOrCreateFunctionPrototype (← getLLVMModule) retty fnName argtys
|
| 1400 |
+
let fnty ← LLVM.functionType retty argtys
|
| 1401 |
+
let _ ← LLVM.buildCall2 builder fnty fn #[enable?]
|
| 1402 |
+
|
| 1403 |
+
def callLeanIOMarkEndInitialization (builder : LLVM.Builder llvmctx) : M llvmctx Unit := do
|
| 1404 |
+
let fnName := "lean_io_mark_end_initialization"
|
| 1405 |
+
let retty ← LLVM.voidType llvmctx
|
| 1406 |
+
let argtys := #[]
|
| 1407 |
+
let fn ← getOrCreateFunctionPrototype (← getLLVMModule) retty fnName argtys
|
| 1408 |
+
let fnty ← LLVM.functionType retty argtys
|
| 1409 |
+
let _ ← LLVM.buildCall2 builder fnty fn #[]
|
| 1410 |
+
|
| 1411 |
+
def callLeanIOResultIsOk (builder : LLVM.Builder llvmctx)
|
| 1412 |
+
(arg : LLVM.Value llvmctx) (name : String := "") : M llvmctx (LLVM.Value llvmctx) := do
|
| 1413 |
+
let fnName := "lean_io_result_is_ok"
|
| 1414 |
+
let retty ← LLVM.i1Type llvmctx
|
| 1415 |
+
let argtys := #[ ← LLVM.voidPtrType llvmctx ]
|
| 1416 |
+
let fn ← getOrCreateFunctionPrototype (← getLLVMModule) retty fnName argtys
|
| 1417 |
+
let fnty ← LLVM.functionType retty argtys
|
| 1418 |
+
LLVM.buildCall2 builder fnty fn #[arg] name
|
| 1419 |
+
|
| 1420 |
+
def callLeanInitTaskManager (builder : LLVM.Builder llvmctx) : M llvmctx Unit := do
|
| 1421 |
+
let fnName := "lean_init_task_manager"
|
| 1422 |
+
let retty ← LLVM.voidType llvmctx
|
| 1423 |
+
let argtys := #[]
|
| 1424 |
+
let fn ← getOrCreateFunctionPrototype (← getLLVMModule) retty fnName argtys
|
| 1425 |
+
let fnty ← LLVM.functionType retty argtys
|
| 1426 |
+
let _ ← LLVM.buildCall2 builder fnty fn #[]
|
| 1427 |
+
|
| 1428 |
+
def callLeanFinalizeTaskManager (builder : LLVM.Builder llvmctx) : M llvmctx Unit := do
|
| 1429 |
+
let fnName := "lean_finalize_task_manager"
|
| 1430 |
+
let retty ← LLVM.voidPtrType llvmctx
|
| 1431 |
+
let argtys := #[]
|
| 1432 |
+
let fn ← getOrCreateFunctionPrototype (← getLLVMModule) retty fnName argtys
|
| 1433 |
+
let fnty ← LLVM.functionType retty argtys
|
| 1434 |
+
let _ ← LLVM.buildCall2 builder fnty fn #[]
|
| 1435 |
+
|
| 1436 |
+
def callLeanUnboxUint32 (builder : LLVM.Builder llvmctx)
|
| 1437 |
+
(v : LLVM.Value llvmctx) (name : String := "") : M llvmctx (LLVM.Value llvmctx) := do
|
| 1438 |
+
let fnName := "lean_unbox_uint32"
|
| 1439 |
+
let retty ← LLVM.i32Type llvmctx
|
| 1440 |
+
let argtys := #[ ← LLVM.voidPtrType llvmctx ]
|
| 1441 |
+
let fn ← getOrCreateFunctionPrototype (← getLLVMModule) retty fnName argtys
|
| 1442 |
+
let fnty ← LLVM.functionType retty argtys
|
| 1443 |
+
LLVM.buildCall2 builder fnty fn #[v] name
|
| 1444 |
+
|
| 1445 |
+
def callLeanIOResultShowError (builder : LLVM.Builder llvmctx)
|
| 1446 |
+
(v : LLVM.Value llvmctx) (name : String := "") : M llvmctx Unit := do
|
| 1447 |
+
let fnName := "lean_io_result_show_error"
|
| 1448 |
+
let retty ← LLVM.voidType llvmctx
|
| 1449 |
+
let argtys := #[ ← LLVM.voidPtrType llvmctx ]
|
| 1450 |
+
let fn ← getOrCreateFunctionPrototype (← getLLVMModule) retty fnName argtys
|
| 1451 |
+
let fnty ← LLVM.functionType retty argtys
|
| 1452 |
+
let _ ← LLVM.buildCall2 builder fnty fn #[v] name
|
| 1453 |
+
|
| 1454 |
+
def callLeanMainFn (builder : LLVM.Builder llvmctx)
|
| 1455 |
+
(argv? : Option (LLVM.Value llvmctx))
|
| 1456 |
+
(world : LLVM.Value llvmctx)
|
| 1457 |
+
(name : String) : M llvmctx (LLVM.Value llvmctx) := do
|
| 1458 |
+
let retty ← LLVM.voidPtrType llvmctx
|
| 1459 |
+
let voidptr ← LLVM.voidPtrType llvmctx
|
| 1460 |
+
let argtys := if argv?.isSome then #[ voidptr, voidptr ] else #[ voidptr ]
|
| 1461 |
+
let fn ← getOrCreateFunctionPrototype (← getLLVMModule) retty leanMainFn argtys
|
| 1462 |
+
let fnty ← LLVM.functionType retty argtys
|
| 1463 |
+
let args := match argv? with
|
| 1464 |
+
| .some argv => #[argv, world]
|
| 1465 |
+
| .none => #[world]
|
| 1466 |
+
LLVM.buildCall2 builder fnty fn args name
|
| 1467 |
+
|
| 1468 |
+
def emitMainFn (mod : LLVM.Module llvmctx) (builder : LLVM.Builder llvmctx) : M llvmctx Unit := do
|
| 1469 |
+
let d ← getDecl `main
|
| 1470 |
+
let xs ← match d with
|
| 1471 |
+
| .fdecl (xs := xs) .. => pure xs
|
| 1472 |
+
| _ => throw "Function declaration expected for 'main'"
|
| 1473 |
+
|
| 1474 |
+
unless xs.size == 2 || xs.size == 1 do throw s!"Invalid main function, main expected to have '2' or '1' arguments, found '{xs.size}' arguments"
|
| 1475 |
+
let env ← getEnv
|
| 1476 |
+
let usesLeanAPI := usesModuleFrom env `Lean
|
| 1477 |
+
let mainTy ← LLVM.functionType (← LLVM.i64Type llvmctx)
|
| 1478 |
+
#[(← LLVM.i64Type llvmctx), (← LLVM.pointerType (← LLVM.voidPtrType llvmctx))]
|
| 1479 |
+
let main ← LLVM.getOrAddFunction mod "main" mainTy
|
| 1480 |
+
let entry ← LLVM.appendBasicBlockInContext llvmctx main "entry"
|
| 1481 |
+
LLVM.positionBuilderAtEnd builder entry
|
| 1482 |
+
/-
|
| 1483 |
+
#if defined(WIN32) || defined(_WIN32)
|
| 1484 |
+
SetErrorMode(SEM_FAILCRITICALERRORS);
|
| 1485 |
+
#endif
|
| 1486 |
+
-/
|
| 1487 |
+
let inty ← LLVM.voidPtrType llvmctx
|
| 1488 |
+
let inslot ← buildPrologueAlloca builder (← LLVM.pointerType inty) "in"
|
| 1489 |
+
let resty ← LLVM.voidPtrType llvmctx
|
| 1490 |
+
let res ← buildPrologueAlloca builder (← LLVM.pointerType resty) "res"
|
| 1491 |
+
|
| 1492 |
+
let argcval ← LLVM.getParam main 0
|
| 1493 |
+
let argvval ← LLVM.getParam main 1
|
| 1494 |
+
let truncArgcval ← LLVM.buildSextOrTrunc builder argcval (← LLVM.i32Type llvmctx)
|
| 1495 |
+
let argvval ← callLeanSetupLibUV builder truncArgcval argvval
|
| 1496 |
+
|
| 1497 |
+
if usesLeanAPI then callLeanInitialize builder else callLeanInitializeRuntimeModule builder
|
| 1498 |
+
/- We disable panic messages because they do not mesh well with extracted closed terms.
|
| 1499 |
+
See issue #534. We can remove this workaround after we implement issue #467. -/
|
| 1500 |
+
callLeanSetPanicMessages builder (← LLVM.constFalse llvmctx)
|
| 1501 |
+
let world ← callLeanIOMkWorld builder
|
| 1502 |
+
let resv ← callModInitFn builder (← getModName) (← constInt8 1) world ((← getModName).toString ++ "_init_out")
|
| 1503 |
+
let _ ← LLVM.buildStore builder resv res
|
| 1504 |
+
|
| 1505 |
+
callLeanSetPanicMessages builder (← LLVM.constTrue llvmctx)
|
| 1506 |
+
callLeanIOMarkEndInitialization builder
|
| 1507 |
+
|
| 1508 |
+
let resv ← LLVM.buildLoad2 builder resty res "resv"
|
| 1509 |
+
let res_is_ok ← callLeanIOResultIsOk builder resv "res_is_ok"
|
| 1510 |
+
buildIfThen_ builder "resIsOkBranches" res_is_ok
|
| 1511 |
+
(fun builder => do -- then clause of the builder)
|
| 1512 |
+
callLeanDecRef builder resv
|
| 1513 |
+
callLeanInitTaskManager builder
|
| 1514 |
+
if xs.size == 2 then
|
| 1515 |
+
let inv ← callLeanBox builder (← constIntSizeT 0) "inv"
|
| 1516 |
+
let _ ← LLVM.buildStore builder inv inslot
|
| 1517 |
+
let ity ← LLVM.size_tType llvmctx
|
| 1518 |
+
let islot ← buildPrologueAlloca builder ity "islot"
|
| 1519 |
+
LLVM.buildStore builder argcval islot
|
| 1520 |
+
buildWhile_ builder "argv"
|
| 1521 |
+
(condcodegen := fun builder => do
|
| 1522 |
+
let iv ← LLVM.buildLoad2 builder ity islot "iv"
|
| 1523 |
+
let i_gt_1 ← LLVM.buildICmp builder LLVM.IntPredicate.UGT iv (← constIntSizeT 1) "i_gt_1"
|
| 1524 |
+
return i_gt_1)
|
| 1525 |
+
(bodycodegen := fun builder => do
|
| 1526 |
+
let iv ← LLVM.buildLoad2 builder ity islot "iv"
|
| 1527 |
+
let iv_next ← LLVM.buildSub builder iv (← constIntSizeT 1) "iv.next"
|
| 1528 |
+
LLVM.buildStore builder iv_next islot
|
| 1529 |
+
let nv ← callLeanAllocCtor builder 1 2 0 "nv"
|
| 1530 |
+
let argv_i_next_slot ← LLVM.buildGEP2 builder (← LLVM.voidPtrType llvmctx) argvval #[iv_next] "argv.i.next.slot"
|
| 1531 |
+
let argv_i_next_val ← LLVM.buildLoad2 builder (← LLVM.voidPtrType llvmctx) argv_i_next_slot "argv.i.next.val"
|
| 1532 |
+
let argv_i_next_val_str ← callLeanMkString builder argv_i_next_val "arg.i.next.val.str"
|
| 1533 |
+
callLeanCtorSet builder nv (← constIntUnsigned 0) argv_i_next_val_str
|
| 1534 |
+
let inv ← LLVM.buildLoad2 builder inty inslot "inv"
|
| 1535 |
+
callLeanCtorSet builder nv (← constIntUnsigned 1) inv
|
| 1536 |
+
LLVM.buildStore builder nv inslot)
|
| 1537 |
+
let world ← callLeanIOMkWorld builder
|
| 1538 |
+
let inv ← LLVM.buildLoad2 builder inty inslot "inv"
|
| 1539 |
+
let resv ← callLeanMainFn builder (argv? := .some inv) (world := world) "resv"
|
| 1540 |
+
let _ ← LLVM.buildStore builder resv res
|
| 1541 |
+
pure ShouldForwardControlFlow.yes
|
| 1542 |
+
else
|
| 1543 |
+
let world ← callLeanIOMkWorld builder
|
| 1544 |
+
let resv ← callLeanMainFn builder (argv? := .none) (world := world) "resv"
|
| 1545 |
+
let _ ← LLVM.buildStore builder resv res
|
| 1546 |
+
pure ShouldForwardControlFlow.yes
|
| 1547 |
+
)
|
| 1548 |
+
|
| 1549 |
+
-- `IO _`
|
| 1550 |
+
let retTy := env.find? `main |>.get! |>.type |>.getForallBody
|
| 1551 |
+
-- either `UInt32` or `(P)Unit`
|
| 1552 |
+
let retTy := retTy.appArg!
|
| 1553 |
+
-- finalize at least the task manager to avoid leak sanitizer false positives
|
| 1554 |
+
-- from tasks outliving the main thread
|
| 1555 |
+
callLeanFinalizeTaskManager builder
|
| 1556 |
+
let resv ← LLVM.buildLoad2 builder resty res "resv"
|
| 1557 |
+
let res_is_ok ← callLeanIOResultIsOk builder resv "res_is_ok"
|
| 1558 |
+
buildIfThenElse_ builder "res.is.ok" res_is_ok
|
| 1559 |
+
(fun builder => -- then builder
|
| 1560 |
+
if retTy.constName? == some ``UInt32 then do
|
| 1561 |
+
let resv ← LLVM.buildLoad2 builder resty res "resv"
|
| 1562 |
+
let retv ← callLeanUnboxUint32 builder (← callLeanIOResultGetValue builder resv "io_val") "retv"
|
| 1563 |
+
let retv ← LLVM.buildSext builder retv (← LLVM.i64Type llvmctx) "retv_sext"
|
| 1564 |
+
callLeanDecRef builder resv
|
| 1565 |
+
let _ ← LLVM.buildRet builder retv
|
| 1566 |
+
pure ShouldForwardControlFlow.no
|
| 1567 |
+
else do
|
| 1568 |
+
callLeanDecRef builder resv
|
| 1569 |
+
let _ ← LLVM.buildRet builder (← constInt64 0)
|
| 1570 |
+
pure ShouldForwardControlFlow.no
|
| 1571 |
+
|
| 1572 |
+
)
|
| 1573 |
+
(fun builder => do -- else builder
|
| 1574 |
+
let resv ← LLVM.buildLoad2 builder resty res "resv"
|
| 1575 |
+
callLeanIOResultShowError builder resv
|
| 1576 |
+
callLeanDecRef builder resv
|
| 1577 |
+
let _ ← LLVM.buildRet builder (← constInt64 1)
|
| 1578 |
+
pure ShouldForwardControlFlow.no)
|
| 1579 |
+
-- at the merge
|
| 1580 |
+
let _ ← LLVM.buildUnreachable builder
|
| 1581 |
+
|
| 1582 |
+
def hasMainFn : M llvmctx Bool := do
|
| 1583 |
+
let env ← getEnv
|
| 1584 |
+
let decls := getDecls env
|
| 1585 |
+
return decls.any (fun d => d.name == `main)
|
| 1586 |
+
|
| 1587 |
+
def emitMainFnIfNeeded (mod : LLVM.Module llvmctx) (builder : LLVM.Builder llvmctx) : M llvmctx Unit := do
|
| 1588 |
+
if (← hasMainFn) then emitMainFn mod builder
|
| 1589 |
+
|
| 1590 |
+
def main : M llvmctx Unit := do
|
| 1591 |
+
emitFnDecls
|
| 1592 |
+
let builder ← LLVM.createBuilderInContext llvmctx
|
| 1593 |
+
emitFns (← getLLVMModule) builder
|
| 1594 |
+
emitInitFn (← getLLVMModule) builder
|
| 1595 |
+
emitMainFnIfNeeded (← getLLVMModule) builder
|
| 1596 |
+
end EmitLLVM
|
| 1597 |
+
|
| 1598 |
+
def getLeanHBcPath : IO System.FilePath := do
|
| 1599 |
+
return (← getLibDir (← getBuildDir)) / "lean.h.bc"
|
| 1600 |
+
|
| 1601 |
+
/-- Get the names of all global symbols in the module -/
|
| 1602 |
+
partial def getModuleGlobals (mod : LLVM.Module llvmctx) : IO (Array (LLVM.Value llvmctx)) := do
|
| 1603 |
+
let rec go (v : LLVM.Value llvmctx) (acc : Array (LLVM.Value llvmctx)) : IO (Array (LLVM.Value llvmctx)) := do
|
| 1604 |
+
if v.isNull then return acc
|
| 1605 |
+
else go (← LLVM.getNextGlobal v) (acc.push v)
|
| 1606 |
+
go (← LLVM.getFirstGlobal mod) #[]
|
| 1607 |
+
|
| 1608 |
+
/-- Get the names of all global functions in the module -/
|
| 1609 |
+
partial def getModuleFunctions (mod : LLVM.Module llvmctx) : IO (Array (LLVM.Value llvmctx)) := do
|
| 1610 |
+
let rec go (v : LLVM.Value llvmctx) (acc : Array (LLVM.Value llvmctx)) : IO (Array (LLVM.Value llvmctx)) := do
|
| 1611 |
+
if v.isNull then return acc
|
| 1612 |
+
else go (← LLVM.getNextFunction v) (acc.push v)
|
| 1613 |
+
go (← LLVM.getFirstFunction mod) #[]
|
| 1614 |
+
|
| 1615 |
+
/--
|
| 1616 |
+
`emitLLVM` is the entrypoint for the lean shell to code generate LLVM.
|
| 1617 |
+
-/
|
| 1618 |
+
@[export lean_ir_emit_llvm]
|
| 1619 |
+
def emitLLVM (env : Environment) (modName : Name) (filepath : String) : IO Unit := do
|
| 1620 |
+
LLVM.llvmInitializeTargetInfo
|
| 1621 |
+
let llvmctx ← LLVM.createContext
|
| 1622 |
+
let module ← LLVM.createModule llvmctx modName.toString
|
| 1623 |
+
let emitLLVMCtx : EmitLLVM.Context llvmctx := {env := env, modName := modName, llvmmodule := module}
|
| 1624 |
+
let initState := { var2val := default, jp2bb := default : EmitLLVM.State llvmctx}
|
| 1625 |
+
let out? ← ((EmitLLVM.main (llvmctx := llvmctx)).run initState).run emitLLVMCtx
|
| 1626 |
+
match out? with
|
| 1627 |
+
| .ok _ => do
|
| 1628 |
+
let membuf ← LLVM.createMemoryBufferWithContentsOfFile (← getLeanHBcPath).toString
|
| 1629 |
+
let modruntime ← LLVM.parseBitcode llvmctx membuf
|
| 1630 |
+
/- It is important that we extract the names here because
|
| 1631 |
+
pointers into modruntime get invalidated by linkModules -/
|
| 1632 |
+
let runtimeGlobals ← (← getModuleGlobals modruntime).mapM (·.getName)
|
| 1633 |
+
let filter func := do
|
| 1634 |
+
-- | Do not insert internal linkage for
|
| 1635 |
+
-- intrinsics such as `@llvm.umul.with.overflow.i64` which clang generates, and also
|
| 1636 |
+
-- for declarations such as `lean_inc_ref_cold` which are externally defined.
|
| 1637 |
+
if (← LLVM.isDeclaration func) then
|
| 1638 |
+
return none
|
| 1639 |
+
else
|
| 1640 |
+
return some (← func.getName)
|
| 1641 |
+
let runtimeFunctions ← (← getModuleFunctions modruntime).filterMapM filter
|
| 1642 |
+
LLVM.linkModules (dest := emitLLVMCtx.llvmmodule) (src := modruntime)
|
| 1643 |
+
-- Mark every global and function as having internal linkage.
|
| 1644 |
+
for name in runtimeGlobals do
|
| 1645 |
+
let some global ← LLVM.getNamedGlobal emitLLVMCtx.llvmmodule name
|
| 1646 |
+
| throw <| IO.Error.userError s!"ERROR: linked module must have global from runtime module: '{name}'"
|
| 1647 |
+
LLVM.setLinkage global LLVM.Linkage.internal
|
| 1648 |
+
for name in runtimeFunctions do
|
| 1649 |
+
let some fn ← LLVM.getNamedFunction emitLLVMCtx.llvmmodule name
|
| 1650 |
+
| throw <| IO.Error.userError s!"ERROR: linked module must have function from runtime module: '{name}'"
|
| 1651 |
+
LLVM.setLinkage fn LLVM.Linkage.internal
|
| 1652 |
+
if let some err ← LLVM.verifyModule emitLLVMCtx.llvmmodule then
|
| 1653 |
+
throw <| .userError err
|
| 1654 |
+
LLVM.writeBitcodeToFile emitLLVMCtx.llvmmodule filepath
|
| 1655 |
+
LLVM.disposeModule emitLLVMCtx.llvmmodule
|
| 1656 |
+
| .error err => throw (IO.Error.userError err)
|
| 1657 |
+
end Lean.IR
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/EmitUtil.lean
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2019 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Compiler.InitAttr
|
| 8 |
+
import Lean.Compiler.IR.CompilerM
|
| 9 |
+
|
| 10 |
+
/-! # Helper functions for backend code generators -/
|
| 11 |
+
|
| 12 |
+
namespace Lean.IR
|
| 13 |
+
/-- Return true iff `b` is of the form `let x := g ys; ret x` -/
|
| 14 |
+
def isTailCallTo (g : Name) (b : FnBody) : Bool :=
|
| 15 |
+
match b with
|
| 16 |
+
| FnBody.vdecl x _ (Expr.fap f _) (FnBody.ret (Arg.var y)) => x == y && f == g
|
| 17 |
+
| _ => false
|
| 18 |
+
|
| 19 |
+
def usesModuleFrom (env : Environment) (modulePrefix : Name) : Bool :=
|
| 20 |
+
env.allImportedModuleNames.toList.any fun modName => modulePrefix.isPrefixOf modName
|
| 21 |
+
|
| 22 |
+
namespace CollectUsedDecls
|
| 23 |
+
|
| 24 |
+
abbrev M := ReaderT Environment (StateM NameSet)
|
| 25 |
+
|
| 26 |
+
@[inline] def collect (f : FunId) : M Unit :=
|
| 27 |
+
modify fun s => s.insert f
|
| 28 |
+
|
| 29 |
+
partial def collectFnBody : FnBody → M Unit
|
| 30 |
+
| .vdecl _ _ v b =>
|
| 31 |
+
match v with
|
| 32 |
+
| .fap f _ => collect f *> collectFnBody b
|
| 33 |
+
| .pap f _ => collect f *> collectFnBody b
|
| 34 |
+
| _ => collectFnBody b
|
| 35 |
+
| .jdecl _ _ v b => collectFnBody v *> collectFnBody b
|
| 36 |
+
| .case _ _ _ alts => alts.forM fun alt => collectFnBody alt.body
|
| 37 |
+
| e => do unless e.isTerminal do collectFnBody e.body
|
| 38 |
+
|
| 39 |
+
def collectInitDecl (fn : Name) : M Unit := do
|
| 40 |
+
let env ← read
|
| 41 |
+
match getInitFnNameFor? env fn with
|
| 42 |
+
| some initFn => collect initFn
|
| 43 |
+
| _ => pure ()
|
| 44 |
+
|
| 45 |
+
def collectDecl : Decl → M NameSet
|
| 46 |
+
| .fdecl (f := f) (body := b) .. => collectInitDecl f *> CollectUsedDecls.collectFnBody b *> get
|
| 47 |
+
| .extern (f := f) .. => collectInitDecl f *> get
|
| 48 |
+
|
| 49 |
+
end CollectUsedDecls
|
| 50 |
+
|
| 51 |
+
def collectUsedDecls (env : Environment) (decl : Decl) (used : NameSet := {}) : NameSet :=
|
| 52 |
+
(CollectUsedDecls.collectDecl decl env).run' used
|
| 53 |
+
|
| 54 |
+
abbrev VarTypeMap := Std.HashMap VarId IRType
|
| 55 |
+
abbrev JPParamsMap := Std.HashMap JoinPointId (Array Param)
|
| 56 |
+
|
| 57 |
+
namespace CollectMaps
|
| 58 |
+
abbrev Collector := (VarTypeMap × JPParamsMap) → (VarTypeMap × JPParamsMap)
|
| 59 |
+
@[inline] def collectVar (x : VarId) (t : IRType) : Collector
|
| 60 |
+
| (vs, js) => (vs.insert x t, js)
|
| 61 |
+
def collectParams (ps : Array Param) : Collector :=
|
| 62 |
+
fun s => ps.foldl (fun s p => collectVar p.x p.ty s) s
|
| 63 |
+
@[inline] def collectJP (j : JoinPointId) (xs : Array Param) : Collector
|
| 64 |
+
| (vs, js) => (vs, js.insert j xs)
|
| 65 |
+
|
| 66 |
+
/-- `collectFnBody` assumes the variables in -/
|
| 67 |
+
partial def collectFnBody : FnBody → Collector
|
| 68 |
+
| .vdecl x t _ b => collectVar x t ∘ collectFnBody b
|
| 69 |
+
| .jdecl j xs v b => collectJP j xs ∘ collectParams xs ∘ collectFnBody v ∘ collectFnBody b
|
| 70 |
+
| .case _ _ _ alts => fun s => alts.foldl (fun s alt => collectFnBody alt.body s) s
|
| 71 |
+
| e => if e.isTerminal then id else collectFnBody e.body
|
| 72 |
+
|
| 73 |
+
def collectDecl : Decl → Collector
|
| 74 |
+
| .fdecl (xs := xs) (body := b) .. => collectParams xs ∘ collectFnBody b
|
| 75 |
+
| _ => id
|
| 76 |
+
|
| 77 |
+
end CollectMaps
|
| 78 |
+
|
| 79 |
+
/-- Return a pair `(v, j)`, where `v` is a mapping from variable/parameter to type,
|
| 80 |
+
and `j` is a mapping from join point to parameters.
|
| 81 |
+
This function assumes `d` has normalized indexes (see `normids.lean`). -/
|
| 82 |
+
def mkVarJPMaps (d : Decl) : VarTypeMap × JPParamsMap :=
|
| 83 |
+
CollectMaps.collectDecl d ({}, {})
|
| 84 |
+
|
| 85 |
+
end IR
|
| 86 |
+
end Lean
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/ExpandResetReuse.lean
ADDED
|
@@ -0,0 +1,281 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2019 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Compiler.IR.CompilerM
|
| 8 |
+
import Lean.Compiler.IR.NormIds
|
| 9 |
+
import Lean.Compiler.IR.FreeVars
|
| 10 |
+
|
| 11 |
+
namespace Lean.IR.ExpandResetReuse
|
| 12 |
+
/-- Mapping from variable to projections -/
|
| 13 |
+
abbrev ProjMap := Std.HashMap VarId Expr
|
| 14 |
+
namespace CollectProjMap
|
| 15 |
+
abbrev Collector := ProjMap → ProjMap
|
| 16 |
+
@[inline] def collectVDecl (x : VarId) (v : Expr) : Collector := fun m =>
|
| 17 |
+
match v with
|
| 18 |
+
| .proj .. => m.insert x v
|
| 19 |
+
| .sproj .. => m.insert x v
|
| 20 |
+
| .uproj .. => m.insert x v
|
| 21 |
+
| _ => m
|
| 22 |
+
|
| 23 |
+
partial def collectFnBody : FnBody → Collector
|
| 24 |
+
| .vdecl x _ v b => collectVDecl x v ∘ collectFnBody b
|
| 25 |
+
| .jdecl _ _ v b => collectFnBody v ∘ collectFnBody b
|
| 26 |
+
| .case _ _ _ alts => fun s => alts.foldl (fun s alt => collectFnBody alt.body s) s
|
| 27 |
+
| e => if e.isTerminal then id else collectFnBody e.body
|
| 28 |
+
end CollectProjMap
|
| 29 |
+
|
| 30 |
+
/-- Create a mapping from variables to projections.
|
| 31 |
+
This function assumes variable ids have been normalized -/
|
| 32 |
+
def mkProjMap (d : Decl) : ProjMap :=
|
| 33 |
+
match d with
|
| 34 |
+
| .fdecl (body := b) .. => CollectProjMap.collectFnBody b {}
|
| 35 |
+
| _ => {}
|
| 36 |
+
|
| 37 |
+
structure Context where
|
| 38 |
+
projMap : ProjMap
|
| 39 |
+
|
| 40 |
+
/-- Return true iff `x` is consumed in all branches of the current block.
|
| 41 |
+
Here consumption means the block contains a `dec x` or `reuse x ...`. -/
|
| 42 |
+
partial def consumed (x : VarId) : FnBody → Bool
|
| 43 |
+
| .vdecl _ _ v b =>
|
| 44 |
+
match v with
|
| 45 |
+
| Expr.reuse y _ _ _ => x == y || consumed x b
|
| 46 |
+
| _ => consumed x b
|
| 47 |
+
| .dec y _ _ _ b => x == y || consumed x b
|
| 48 |
+
| .case _ _ _ alts => alts.all fun alt => consumed x alt.body
|
| 49 |
+
| e => !e.isTerminal && consumed x e.body
|
| 50 |
+
|
| 51 |
+
abbrev Mask := Array (Option VarId)
|
| 52 |
+
|
| 53 |
+
/-- Auxiliary function for eraseProjIncFor -/
|
| 54 |
+
partial def eraseProjIncForAux (y : VarId) (bs : Array FnBody) (mask : Mask) (keep : Array FnBody) : Array FnBody × Mask :=
|
| 55 |
+
let done (_ : Unit) := (bs ++ keep.reverse, mask)
|
| 56 |
+
let keepInstr (b : FnBody) := eraseProjIncForAux y bs.pop mask (keep.push b)
|
| 57 |
+
if h : bs.size < 2 then done ()
|
| 58 |
+
else
|
| 59 |
+
let b := bs.back!
|
| 60 |
+
match b with
|
| 61 |
+
| .vdecl _ _ (.sproj _ _ _) _ => keepInstr b
|
| 62 |
+
| .vdecl _ _ (.uproj _ _) _ => keepInstr b
|
| 63 |
+
| .inc z n c p _ =>
|
| 64 |
+
if n == 0 then done () else
|
| 65 |
+
let b' := bs[bs.size - 2]
|
| 66 |
+
match b' with
|
| 67 |
+
| .vdecl w _ (.proj i x) _ =>
|
| 68 |
+
if w == z && y == x then
|
| 69 |
+
/- Found
|
| 70 |
+
```
|
| 71 |
+
let z := proj[i] y
|
| 72 |
+
inc z n c
|
| 73 |
+
```
|
| 74 |
+
We keep `proj`, and `inc` when `n > 1`
|
| 75 |
+
-/
|
| 76 |
+
let bs := bs.pop.pop
|
| 77 |
+
let mask := mask.set! i (some z)
|
| 78 |
+
let keep := keep.push b'
|
| 79 |
+
let keep := if n == 1 then keep else keep.push (FnBody.inc z (n-1) c p FnBody.nil)
|
| 80 |
+
eraseProjIncForAux y bs mask keep
|
| 81 |
+
else done ()
|
| 82 |
+
| _ => done ()
|
| 83 |
+
| _ => done ()
|
| 84 |
+
|
| 85 |
+
/-- Try to erase `inc` instructions on projections of `y` occurring in the tail of `bs`.
|
| 86 |
+
Return the updated `bs` and a bit mask specifying which `inc`s have been removed. -/
|
| 87 |
+
def eraseProjIncFor (n : Nat) (y : VarId) (bs : Array FnBody) : Array FnBody × Mask :=
|
| 88 |
+
eraseProjIncForAux y bs (.replicate n none) #[]
|
| 89 |
+
|
| 90 |
+
/-- Replace `reuse x ctor ...` with `ctor ...`, and remove `dec x` -/
|
| 91 |
+
partial def reuseToCtor (x : VarId) : FnBody → FnBody
|
| 92 |
+
| FnBody.dec y n c p b =>
|
| 93 |
+
if x == y then b -- n must be 1 since `x := reset ...`
|
| 94 |
+
else FnBody.dec y n c p (reuseToCtor x b)
|
| 95 |
+
| FnBody.vdecl z t v b =>
|
| 96 |
+
match v with
|
| 97 |
+
| Expr.reuse y c _ xs =>
|
| 98 |
+
if x == y then FnBody.vdecl z t (Expr.ctor c xs) b
|
| 99 |
+
else FnBody.vdecl z t v (reuseToCtor x b)
|
| 100 |
+
| _ =>
|
| 101 |
+
FnBody.vdecl z t v (reuseToCtor x b)
|
| 102 |
+
| FnBody.case tid y yType alts =>
|
| 103 |
+
let alts := alts.map fun alt => alt.modifyBody (reuseToCtor x)
|
| 104 |
+
FnBody.case tid y yType alts
|
| 105 |
+
| e =>
|
| 106 |
+
if e.isTerminal then
|
| 107 |
+
e
|
| 108 |
+
else
|
| 109 |
+
let (instr, b) := e.split
|
| 110 |
+
let b := reuseToCtor x b
|
| 111 |
+
instr.setBody b
|
| 112 |
+
|
| 113 |
+
/--
|
| 114 |
+
replace
|
| 115 |
+
```
|
| 116 |
+
x := reset y; b
|
| 117 |
+
```
|
| 118 |
+
with
|
| 119 |
+
```
|
| 120 |
+
inc z_1; ...; inc z_i; dec y; b'
|
| 121 |
+
```
|
| 122 |
+
where `z_i`'s are the variables in `mask`,
|
| 123 |
+
and `b'` is `b` where we removed `dec x` and replaced `reuse x ctor_i ...` with `ctor_i ...`.
|
| 124 |
+
-/
|
| 125 |
+
def mkSlowPath (x y : VarId) (mask : Mask) (b : FnBody) : FnBody :=
|
| 126 |
+
let b := reuseToCtor x b
|
| 127 |
+
let b := FnBody.dec y 1 true false b
|
| 128 |
+
mask.foldl (init := b) fun b m => match m with
|
| 129 |
+
| some z => FnBody.inc z 1 true false b
|
| 130 |
+
| none => b
|
| 131 |
+
|
| 132 |
+
abbrev M := ReaderT Context (StateM Nat)
|
| 133 |
+
def mkFresh : M VarId :=
|
| 134 |
+
modifyGet fun n => ({ idx := n }, n + 1)
|
| 135 |
+
|
| 136 |
+
def releaseUnreadFields (y : VarId) (mask : Mask) (b : FnBody) : M FnBody :=
|
| 137 |
+
mask.size.foldM (init := b) fun i _ b =>
|
| 138 |
+
match mask[i] with
|
| 139 |
+
| some _ => pure b -- code took ownership of this field
|
| 140 |
+
| none => do
|
| 141 |
+
let fld ← mkFresh
|
| 142 |
+
pure (FnBody.vdecl fld IRType.object (Expr.proj i y) (FnBody.dec fld 1 true false b))
|
| 143 |
+
|
| 144 |
+
def setFields (y : VarId) (zs : Array Arg) (b : FnBody) : FnBody :=
|
| 145 |
+
zs.size.fold (init := b) fun i _ b => FnBody.set y i zs[i] b
|
| 146 |
+
|
| 147 |
+
/-- Given `set x[i] := y`, return true iff `y := proj[i] x` -/
|
| 148 |
+
def isSelfSet (ctx : Context) (x : VarId) (i : Nat) (y : Arg) : Bool :=
|
| 149 |
+
match y with
|
| 150 |
+
| Arg.var y =>
|
| 151 |
+
match ctx.projMap[y]? with
|
| 152 |
+
| some (Expr.proj j w) => j == i && w == x
|
| 153 |
+
| _ => false
|
| 154 |
+
| _ => false
|
| 155 |
+
|
| 156 |
+
/-- Given `uset x[i] := y`, return true iff `y := uproj[i] x` -/
|
| 157 |
+
def isSelfUSet (ctx : Context) (x : VarId) (i : Nat) (y : VarId) : Bool :=
|
| 158 |
+
match ctx.projMap[y]? with
|
| 159 |
+
| some (Expr.uproj j w) => j == i && w == x
|
| 160 |
+
| _ => false
|
| 161 |
+
|
| 162 |
+
/-- Given `sset x[n, i] := y`, return true iff `y := sproj[n, i] x` -/
|
| 163 |
+
def isSelfSSet (ctx : Context) (x : VarId) (n : Nat) (i : Nat) (y : VarId) : Bool :=
|
| 164 |
+
match ctx.projMap[y]? with
|
| 165 |
+
| some (Expr.sproj m j w) => n == m && j == i && w == x
|
| 166 |
+
| _ => false
|
| 167 |
+
|
| 168 |
+
/-- Remove unnecessary `set/uset/sset` operations -/
|
| 169 |
+
partial def removeSelfSet (ctx : Context) : FnBody → FnBody
|
| 170 |
+
| FnBody.set x i y b =>
|
| 171 |
+
if isSelfSet ctx x i y then removeSelfSet ctx b
|
| 172 |
+
else FnBody.set x i y (removeSelfSet ctx b)
|
| 173 |
+
| FnBody.uset x i y b =>
|
| 174 |
+
if isSelfUSet ctx x i y then removeSelfSet ctx b
|
| 175 |
+
else FnBody.uset x i y (removeSelfSet ctx b)
|
| 176 |
+
| FnBody.sset x n i y t b =>
|
| 177 |
+
if isSelfSSet ctx x n i y then removeSelfSet ctx b
|
| 178 |
+
else FnBody.sset x n i y t (removeSelfSet ctx b)
|
| 179 |
+
| FnBody.case tid y yType alts =>
|
| 180 |
+
let alts := alts.map fun alt => alt.modifyBody (removeSelfSet ctx)
|
| 181 |
+
FnBody.case tid y yType alts
|
| 182 |
+
| e =>
|
| 183 |
+
if e.isTerminal then e
|
| 184 |
+
else
|
| 185 |
+
let (instr, b) := e.split
|
| 186 |
+
let b := removeSelfSet ctx b
|
| 187 |
+
instr.setBody b
|
| 188 |
+
|
| 189 |
+
partial def reuseToSet (ctx : Context) (x y : VarId) : FnBody → FnBody
|
| 190 |
+
| FnBody.dec z n c p b =>
|
| 191 |
+
if x == z then FnBody.del y b
|
| 192 |
+
else FnBody.dec z n c p (reuseToSet ctx x y b)
|
| 193 |
+
| FnBody.vdecl z t v b =>
|
| 194 |
+
match v with
|
| 195 |
+
| Expr.reuse w c u zs =>
|
| 196 |
+
if x == w then
|
| 197 |
+
let b := setFields y zs (b.replaceVar z y)
|
| 198 |
+
let b := if u then FnBody.setTag y c.cidx b else b
|
| 199 |
+
removeSelfSet ctx b
|
| 200 |
+
else FnBody.vdecl z t v (reuseToSet ctx x y b)
|
| 201 |
+
| _ => FnBody.vdecl z t v (reuseToSet ctx x y b)
|
| 202 |
+
| FnBody.case tid z zType alts =>
|
| 203 |
+
let alts := alts.map fun alt => alt.modifyBody (reuseToSet ctx x y)
|
| 204 |
+
FnBody.case tid z zType alts
|
| 205 |
+
| e =>
|
| 206 |
+
if e.isTerminal then e
|
| 207 |
+
else
|
| 208 |
+
let (instr, b) := e.split
|
| 209 |
+
let b := reuseToSet ctx x y b
|
| 210 |
+
instr.setBody b
|
| 211 |
+
|
| 212 |
+
/--
|
| 213 |
+
replace
|
| 214 |
+
```
|
| 215 |
+
x := reset y; b
|
| 216 |
+
```
|
| 217 |
+
with
|
| 218 |
+
```
|
| 219 |
+
let f_i_1 := proj[i_1] y;
|
| 220 |
+
...
|
| 221 |
+
let f_i_k := proj[i_k] y;
|
| 222 |
+
b'
|
| 223 |
+
```
|
| 224 |
+
where `i_j`s are the field indexes
|
| 225 |
+
that the code did not touch immediately before the reset.
|
| 226 |
+
That is `mask[j] == none`.
|
| 227 |
+
`b'` is `b` where `y` `dec x` is replaced with `del y`,
|
| 228 |
+
and `z := reuse x ctor_i ws; F` is replaced with
|
| 229 |
+
`set x i ws[i]` operations, and we replace `z` with `x` in `F`
|
| 230 |
+
-/
|
| 231 |
+
def mkFastPath (x y : VarId) (mask : Mask) (b : FnBody) : M FnBody := do
|
| 232 |
+
let ctx ← read
|
| 233 |
+
let b := reuseToSet ctx x y b
|
| 234 |
+
releaseUnreadFields y mask b
|
| 235 |
+
|
| 236 |
+
-- Expand `bs; x := reset[n] y; b`
|
| 237 |
+
partial def expand (mainFn : FnBody → Array FnBody → M FnBody)
|
| 238 |
+
(bs : Array FnBody) (x : VarId) (n : Nat) (y : VarId) (b : FnBody) : M FnBody := do
|
| 239 |
+
let (bs, mask) := eraseProjIncFor n y bs
|
| 240 |
+
/- Remark: we may be duplicating variable/JP indices. That is, `bSlow` and `bFast` may
|
| 241 |
+
have duplicate indices. We run `normalizeIds` to fix the ids after we have expand them. -/
|
| 242 |
+
let bSlow := mkSlowPath x y mask b
|
| 243 |
+
let bFast ← mkFastPath x y mask b
|
| 244 |
+
/- We only optimize recursively the fast. -/
|
| 245 |
+
let bFast ← mainFn bFast #[]
|
| 246 |
+
let c ← mkFresh
|
| 247 |
+
let b := FnBody.vdecl c IRType.uint8 (Expr.isShared y) (mkIf c bSlow bFast)
|
| 248 |
+
return reshape bs b
|
| 249 |
+
|
| 250 |
+
partial def searchAndExpand : FnBody → Array FnBody → M FnBody
|
| 251 |
+
| d@(FnBody.vdecl x _ (Expr.reset n y) b), bs =>
|
| 252 |
+
if consumed x b then do
|
| 253 |
+
expand searchAndExpand bs x n y b
|
| 254 |
+
else
|
| 255 |
+
searchAndExpand b (push bs d)
|
| 256 |
+
| FnBody.jdecl j xs v b, bs => do
|
| 257 |
+
let v ← searchAndExpand v #[]
|
| 258 |
+
searchAndExpand b (push bs (FnBody.jdecl j xs v FnBody.nil))
|
| 259 |
+
| FnBody.case tid x xType alts, bs => do
|
| 260 |
+
let alts ← alts.mapM fun alt => alt.mmodifyBody fun b => searchAndExpand b #[]
|
| 261 |
+
return reshape bs (FnBody.case tid x xType alts)
|
| 262 |
+
| b, bs =>
|
| 263 |
+
if b.isTerminal then return reshape bs b
|
| 264 |
+
else searchAndExpand b.body (push bs b)
|
| 265 |
+
|
| 266 |
+
def main (d : Decl) : Decl :=
|
| 267 |
+
match d with
|
| 268 |
+
| .fdecl (body := b) .. =>
|
| 269 |
+
let m := mkProjMap d
|
| 270 |
+
let nextIdx := d.maxIndex + 1
|
| 271 |
+
let bNew := (searchAndExpand b #[] { projMap := m }).run' nextIdx
|
| 272 |
+
d.updateBody! bNew
|
| 273 |
+
| d => d
|
| 274 |
+
|
| 275 |
+
end ExpandResetReuse
|
| 276 |
+
|
| 277 |
+
/-- (Try to) expand `reset` and `reuse` instructions. -/
|
| 278 |
+
def Decl.expandResetReuse (d : Decl) : Decl :=
|
| 279 |
+
(ExpandResetReuse.main d).normalizeIds
|
| 280 |
+
|
| 281 |
+
end Lean.IR
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/Format.lean
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2019 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Compiler.IR.Basic
|
| 8 |
+
|
| 9 |
+
namespace Lean
|
| 10 |
+
namespace IR
|
| 11 |
+
|
| 12 |
+
private def formatArg : Arg → Format
|
| 13 |
+
| Arg.var id => format id
|
| 14 |
+
| Arg.irrelevant => "◾"
|
| 15 |
+
|
| 16 |
+
instance : ToFormat Arg := ⟨formatArg⟩
|
| 17 |
+
|
| 18 |
+
def formatArray {α : Type} [ToFormat α] (args : Array α) : Format :=
|
| 19 |
+
args.foldl (fun r a => r ++ " " ++ format a) Format.nil
|
| 20 |
+
|
| 21 |
+
private def formatLitVal : LitVal → Format
|
| 22 |
+
| LitVal.num v => format v
|
| 23 |
+
| LitVal.str v => format (repr v)
|
| 24 |
+
|
| 25 |
+
instance : ToFormat LitVal := ⟨formatLitVal⟩
|
| 26 |
+
|
| 27 |
+
private def formatCtorInfo : CtorInfo → Format
|
| 28 |
+
| { name := name, cidx := cidx, usize := usize, ssize := ssize, .. } => Id.run do
|
| 29 |
+
let mut r := f!"ctor_{cidx}"
|
| 30 |
+
if usize > 0 || ssize > 0 then
|
| 31 |
+
r := f!"{r}.{usize}.{ssize}"
|
| 32 |
+
if name != Name.anonymous then
|
| 33 |
+
r := f!"{r}[{name}]"
|
| 34 |
+
r
|
| 35 |
+
|
| 36 |
+
instance : ToFormat CtorInfo := ⟨formatCtorInfo⟩
|
| 37 |
+
|
| 38 |
+
private def formatExpr : Expr → Format
|
| 39 |
+
| Expr.ctor i ys => format i ++ formatArray ys
|
| 40 |
+
| Expr.reset n x => "reset[" ++ format n ++ "] " ++ format x
|
| 41 |
+
| Expr.reuse x i u ys => "reuse" ++ (if u then "!" else "") ++ " " ++ format x ++ " in " ++ format i ++ formatArray ys
|
| 42 |
+
| Expr.proj i x => "proj[" ++ format i ++ "] " ++ format x
|
| 43 |
+
| Expr.uproj i x => "uproj[" ++ format i ++ "] " ++ format x
|
| 44 |
+
| Expr.sproj n o x => "sproj[" ++ format n ++ ", " ++ format o ++ "] " ++ format x
|
| 45 |
+
| Expr.fap c ys => format c ++ formatArray ys
|
| 46 |
+
| Expr.pap c ys => "pap " ++ format c ++ formatArray ys
|
| 47 |
+
| Expr.ap x ys => "app " ++ format x ++ formatArray ys
|
| 48 |
+
| Expr.box _ x => "box " ++ format x
|
| 49 |
+
| Expr.unbox x => "unbox " ++ format x
|
| 50 |
+
| Expr.lit v => format v
|
| 51 |
+
| Expr.isShared x => "isShared " ++ format x
|
| 52 |
+
|
| 53 |
+
instance : ToFormat Expr := ⟨formatExpr⟩
|
| 54 |
+
instance : ToString Expr := ⟨fun e => Format.pretty (format e)⟩
|
| 55 |
+
|
| 56 |
+
private partial def formatIRType : IRType → Format
|
| 57 |
+
| IRType.float => "float"
|
| 58 |
+
| IRType.float32 => "float32"
|
| 59 |
+
| IRType.uint8 => "u8"
|
| 60 |
+
| IRType.uint16 => "u16"
|
| 61 |
+
| IRType.uint32 => "u32"
|
| 62 |
+
| IRType.uint64 => "u64"
|
| 63 |
+
| IRType.usize => "usize"
|
| 64 |
+
| IRType.irrelevant => "◾"
|
| 65 |
+
| IRType.object => "obj"
|
| 66 |
+
| IRType.tobject => "tobj"
|
| 67 |
+
| IRType.struct _ tys =>
|
| 68 |
+
let _ : ToFormat IRType := ⟨formatIRType⟩
|
| 69 |
+
"struct " ++ Format.bracket "{" (Format.joinSep tys.toList ", ") "}"
|
| 70 |
+
| IRType.union _ tys =>
|
| 71 |
+
let _ : ToFormat IRType := ⟨formatIRType⟩
|
| 72 |
+
"union " ++ Format.bracket "{" (Format.joinSep tys.toList ", ") "}"
|
| 73 |
+
|
| 74 |
+
instance : ToFormat IRType := ⟨formatIRType⟩
|
| 75 |
+
instance : ToString IRType := ⟨toString ∘ format⟩
|
| 76 |
+
|
| 77 |
+
private def formatParam : Param → Format
|
| 78 |
+
| { x := name, borrow := b, ty := ty } => "(" ++ format name ++ " : " ++ (if b then "@& " else "") ++ format ty ++ ")"
|
| 79 |
+
|
| 80 |
+
instance : ToFormat Param := ⟨formatParam⟩
|
| 81 |
+
|
| 82 |
+
def formatAlt (fmt : FnBody → Format) (indent : Nat) : Alt → Format
|
| 83 |
+
| Alt.ctor i b => format i.name ++ " →" ++ Format.nest indent (Format.line ++ fmt b)
|
| 84 |
+
| Alt.default b => "default →" ++ Format.nest indent (Format.line ++ fmt b)
|
| 85 |
+
|
| 86 |
+
def formatParams (ps : Array Param) : Format :=
|
| 87 |
+
formatArray ps
|
| 88 |
+
|
| 89 |
+
def formatFnBodyHead : FnBody → Format
|
| 90 |
+
| FnBody.vdecl x ty e _ => "let " ++ format x ++ " : " ++ format ty ++ " := " ++ format e
|
| 91 |
+
| FnBody.jdecl j xs _ _ => format j ++ formatParams xs ++ " := ..."
|
| 92 |
+
| FnBody.set x i y _ => "set " ++ format x ++ "[" ++ format i ++ "] := " ++ format y
|
| 93 |
+
| FnBody.uset x i y _ => "uset " ++ format x ++ "[" ++ format i ++ "] := " ++ format y
|
| 94 |
+
| FnBody.sset x i o y ty _ => "sset " ++ format x ++ "[" ++ format i ++ ", " ++ format o ++ "] : " ++ format ty ++ " := " ++ format y
|
| 95 |
+
| FnBody.setTag x cidx _ => "setTag " ++ format x ++ " := " ++ format cidx
|
| 96 |
+
| FnBody.inc x n _ _ _ => "inc" ++ (if n != 1 then Format.sbracket (format n) else "") ++ " " ++ format x
|
| 97 |
+
| FnBody.dec x n _ _ _ => "dec" ++ (if n != 1 then Format.sbracket (format n) else "") ++ " " ++ format x
|
| 98 |
+
| FnBody.del x _ => "del " ++ format x
|
| 99 |
+
| FnBody.mdata d _ => "mdata " ++ format d
|
| 100 |
+
| FnBody.case _ x _ _ => "case " ++ format x ++ " of ..."
|
| 101 |
+
| FnBody.jmp j ys => "jmp " ++ format j ++ formatArray ys
|
| 102 |
+
| FnBody.ret x => "ret " ++ format x
|
| 103 |
+
| FnBody.unreachable => "⊥"
|
| 104 |
+
|
| 105 |
+
@[export lean_ir_format_fn_body_head]
|
| 106 |
+
private def formatFnBodyHead' (fn : FnBody) : String :=
|
| 107 |
+
formatFnBodyHead fn |>.pretty
|
| 108 |
+
|
| 109 |
+
partial def formatFnBody (fnBody : FnBody) (indent : Nat := 2) : Format :=
|
| 110 |
+
let rec loop : FnBody → Format
|
| 111 |
+
| FnBody.vdecl x ty e b => "let " ++ format x ++ " : " ++ format ty ++ " := " ++ format e ++ ";" ++ Format.line ++ loop b
|
| 112 |
+
| FnBody.jdecl j xs v b => format j ++ formatParams xs ++ " :=" ++ Format.nest indent (Format.line ++ loop v) ++ ";" ++ Format.line ++ loop b
|
| 113 |
+
| FnBody.set x i y b => "set " ++ format x ++ "[" ++ format i ++ "] := " ++ format y ++ ";" ++ Format.line ++ loop b
|
| 114 |
+
| FnBody.uset x i y b => "uset " ++ format x ++ "[" ++ format i ++ "] := " ++ format y ++ ";" ++ Format.line ++ loop b
|
| 115 |
+
| FnBody.sset x i o y ty b => "sset " ++ format x ++ "[" ++ format i ++ ", " ++ format o ++ "] : " ++ format ty ++ " := " ++ format y ++ ";" ++ Format.line ++ loop b
|
| 116 |
+
| FnBody.setTag x cidx b => "setTag " ++ format x ++ " := " ++ format cidx ++ ";" ++ Format.line ++ loop b
|
| 117 |
+
| FnBody.inc x n _ _ b => "inc" ++ (if n != 1 then Format.sbracket (format n) else "") ++ " " ++ format x ++ ";" ++ Format.line ++ loop b
|
| 118 |
+
| FnBody.dec x n _ _ b => "dec" ++ (if n != 1 then Format.sbracket (format n) else "") ++ " " ++ format x ++ ";" ++ Format.line ++ loop b
|
| 119 |
+
| FnBody.del x b => "del " ++ format x ++ ";" ++ Format.line ++ loop b
|
| 120 |
+
| FnBody.mdata d b => "mdata " ++ format d ++ ";" ++ Format.line ++ loop b
|
| 121 |
+
| FnBody.case _ x xType cs => "case " ++ format x ++ " : " ++ format xType ++ " of" ++ cs.foldl (fun r c => r ++ Format.line ++ formatAlt loop indent c) Format.nil
|
| 122 |
+
| FnBody.jmp j ys => "jmp " ++ format j ++ formatArray ys
|
| 123 |
+
| FnBody.ret x => "ret " ++ format x
|
| 124 |
+
| FnBody.unreachable => "⊥"
|
| 125 |
+
loop fnBody
|
| 126 |
+
|
| 127 |
+
instance : ToFormat FnBody := ⟨formatFnBody⟩
|
| 128 |
+
instance : ToString FnBody := ⟨fun b => (format b).pretty⟩
|
| 129 |
+
|
| 130 |
+
def formatDecl (decl : Decl) (indent : Nat := 2) : Format :=
|
| 131 |
+
match decl with
|
| 132 |
+
| Decl.fdecl f xs ty b _ => "def " ++ format f ++ formatParams xs ++ format " : " ++ format ty ++ " :=" ++ Format.nest indent (Format.line ++ formatFnBody b indent)
|
| 133 |
+
| Decl.extern f xs ty _ => "extern " ++ format f ++ formatParams xs ++ format " : " ++ format ty
|
| 134 |
+
|
| 135 |
+
instance : ToFormat Decl := ⟨formatDecl⟩
|
| 136 |
+
|
| 137 |
+
@[export lean_ir_decl_to_string]
|
| 138 |
+
def declToString (d : Decl) : String :=
|
| 139 |
+
(format d).pretty
|
| 140 |
+
|
| 141 |
+
instance : ToString Decl := ⟨declToString⟩
|
| 142 |
+
|
| 143 |
+
end Lean.IR
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/FreeVars.lean
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2019 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Compiler.IR.Basic
|
| 8 |
+
|
| 9 |
+
namespace Lean.IR
|
| 10 |
+
|
| 11 |
+
namespace MaxIndex
|
| 12 |
+
/-! Compute the maximum index `M` used in a declaration.
|
| 13 |
+
We `M` to initialize the fresh index generator used to create fresh
|
| 14 |
+
variable and join point names.
|
| 15 |
+
|
| 16 |
+
Recall that we variable and join points share the same namespace in
|
| 17 |
+
our implementation.
|
| 18 |
+
-/
|
| 19 |
+
|
| 20 |
+
abbrev Collector := Index → Index
|
| 21 |
+
|
| 22 |
+
@[inline] private def skip : Collector := id
|
| 23 |
+
@[inline] private def collect (x : Index) : Collector := fun y => if x > y then x else y
|
| 24 |
+
@[inline] private def collectVar (x : VarId) : Collector := collect x.idx
|
| 25 |
+
@[inline] private def collectJP (j : JoinPointId) : Collector := collect j.idx
|
| 26 |
+
@[inline] private def seq (k₁ k₂ : Collector) : Collector := k₂ ∘ k₁
|
| 27 |
+
instance : AndThen Collector where
|
| 28 |
+
andThen a b := seq a (b ())
|
| 29 |
+
|
| 30 |
+
private def collectArg : Arg → Collector
|
| 31 |
+
| Arg.var x => collectVar x
|
| 32 |
+
| _ => skip
|
| 33 |
+
|
| 34 |
+
private def collectArray {α : Type} (as : Array α) (f : α → Collector) : Collector :=
|
| 35 |
+
fun m => as.foldl (fun m a => f a m) m
|
| 36 |
+
|
| 37 |
+
private def collectArgs (as : Array Arg) : Collector := collectArray as collectArg
|
| 38 |
+
private def collectParam (p : Param) : Collector := collectVar p.x
|
| 39 |
+
private def collectParams (ps : Array Param) : Collector := collectArray ps collectParam
|
| 40 |
+
|
| 41 |
+
private def collectExpr : Expr → Collector
|
| 42 |
+
| Expr.ctor _ ys => collectArgs ys
|
| 43 |
+
| Expr.reset _ x => collectVar x
|
| 44 |
+
| Expr.reuse x _ _ ys => collectVar x >> collectArgs ys
|
| 45 |
+
| Expr.proj _ x => collectVar x
|
| 46 |
+
| Expr.uproj _ x => collectVar x
|
| 47 |
+
| Expr.sproj _ _ x => collectVar x
|
| 48 |
+
| Expr.fap _ ys => collectArgs ys
|
| 49 |
+
| Expr.pap _ ys => collectArgs ys
|
| 50 |
+
| Expr.ap x ys => collectVar x >> collectArgs ys
|
| 51 |
+
| Expr.box _ x => collectVar x
|
| 52 |
+
| Expr.unbox x => collectVar x
|
| 53 |
+
| Expr.lit _ => skip
|
| 54 |
+
| Expr.isShared x => collectVar x
|
| 55 |
+
|
| 56 |
+
private def collectAlts (f : FnBody → Collector) (alts : Array Alt) : Collector :=
|
| 57 |
+
collectArray alts fun alt => f alt.body
|
| 58 |
+
|
| 59 |
+
partial def collectFnBody : FnBody → Collector
|
| 60 |
+
| FnBody.vdecl x _ v b => collectVar x >> collectExpr v >> collectFnBody b
|
| 61 |
+
| FnBody.jdecl j ys v b => collectJP j >> collectFnBody v >> collectParams ys >> collectFnBody b
|
| 62 |
+
| FnBody.set x _ y b => collectVar x >> collectArg y >> collectFnBody b
|
| 63 |
+
| FnBody.uset x _ y b => collectVar x >> collectVar y >> collectFnBody b
|
| 64 |
+
| FnBody.sset x _ _ y _ b => collectVar x >> collectVar y >> collectFnBody b
|
| 65 |
+
| FnBody.setTag x _ b => collectVar x >> collectFnBody b
|
| 66 |
+
| FnBody.inc x _ _ _ b => collectVar x >> collectFnBody b
|
| 67 |
+
| FnBody.dec x _ _ _ b => collectVar x >> collectFnBody b
|
| 68 |
+
| FnBody.del x b => collectVar x >> collectFnBody b
|
| 69 |
+
| FnBody.mdata _ b => collectFnBody b
|
| 70 |
+
| FnBody.case _ x _ alts => collectVar x >> collectAlts collectFnBody alts
|
| 71 |
+
| FnBody.jmp j ys => collectJP j >> collectArgs ys
|
| 72 |
+
| FnBody.ret x => collectArg x
|
| 73 |
+
| FnBody.unreachable => skip
|
| 74 |
+
|
| 75 |
+
partial def collectDecl : Decl → Collector
|
| 76 |
+
| .fdecl (xs := xs) (body := b) .. => collectParams xs >> collectFnBody b
|
| 77 |
+
| .extern (xs := xs) .. => collectParams xs
|
| 78 |
+
|
| 79 |
+
end MaxIndex
|
| 80 |
+
|
| 81 |
+
def FnBody.maxIndex (b : FnBody) : Index :=
|
| 82 |
+
MaxIndex.collectFnBody b 0
|
| 83 |
+
|
| 84 |
+
def Decl.maxIndex (d : Decl) : Index :=
|
| 85 |
+
MaxIndex.collectDecl d 0
|
| 86 |
+
|
| 87 |
+
namespace FreeIndices
|
| 88 |
+
/-! We say a variable (join point) index (aka name) is free in a function body
|
| 89 |
+
if there isn't a `FnBody.vdecl` (`Fnbody.jdecl`) binding it. -/
|
| 90 |
+
|
| 91 |
+
abbrev Collector := IndexSet → IndexSet → IndexSet
|
| 92 |
+
|
| 93 |
+
@[inline] private def skip : Collector :=
|
| 94 |
+
fun _ fv => fv
|
| 95 |
+
|
| 96 |
+
@[inline] private def collectIndex (x : Index) : Collector :=
|
| 97 |
+
fun bv fv => if bv.contains x then fv else fv.insert x
|
| 98 |
+
|
| 99 |
+
@[inline] private def collectVar (x : VarId) : Collector :=
|
| 100 |
+
collectIndex x.idx
|
| 101 |
+
|
| 102 |
+
@[inline] private def collectJP (x : JoinPointId) : Collector :=
|
| 103 |
+
collectIndex x.idx
|
| 104 |
+
|
| 105 |
+
@[inline] private def withIndex (x : Index) : Collector → Collector :=
|
| 106 |
+
fun k bv fv => k (bv.insert x) fv
|
| 107 |
+
|
| 108 |
+
@[inline] private def withVar (x : VarId) : Collector → Collector :=
|
| 109 |
+
withIndex x.idx
|
| 110 |
+
|
| 111 |
+
@[inline] private def withJP (x : JoinPointId) : Collector → Collector :=
|
| 112 |
+
withIndex x.idx
|
| 113 |
+
|
| 114 |
+
def insertParams (s : IndexSet) (ys : Array Param) : IndexSet :=
|
| 115 |
+
ys.foldl (init := s) fun s p => s.insert p.x.idx
|
| 116 |
+
|
| 117 |
+
@[inline] private def withParams (ys : Array Param) : Collector → Collector :=
|
| 118 |
+
fun k bv fv => k (insertParams bv ys) fv
|
| 119 |
+
|
| 120 |
+
@[inline] private def seq : Collector → Collector → Collector :=
|
| 121 |
+
fun k₁ k₂ bv fv => k₂ bv (k₁ bv fv)
|
| 122 |
+
|
| 123 |
+
instance : AndThen Collector where
|
| 124 |
+
andThen a b := seq a (b ())
|
| 125 |
+
|
| 126 |
+
private def collectArg : Arg → Collector
|
| 127 |
+
| Arg.var x => collectVar x
|
| 128 |
+
| _ => skip
|
| 129 |
+
|
| 130 |
+
private def collectArray {α : Type} (as : Array α) (f : α → Collector) : Collector :=
|
| 131 |
+
fun bv fv => as.foldl (fun fv a => f a bv fv) fv
|
| 132 |
+
|
| 133 |
+
private def collectArgs (as : Array Arg) : Collector :=
|
| 134 |
+
collectArray as collectArg
|
| 135 |
+
|
| 136 |
+
private def collectExpr : Expr → Collector
|
| 137 |
+
| Expr.ctor _ ys => collectArgs ys
|
| 138 |
+
| Expr.reset _ x => collectVar x
|
| 139 |
+
| Expr.reuse x _ _ ys => collectVar x >> collectArgs ys
|
| 140 |
+
| Expr.proj _ x => collectVar x
|
| 141 |
+
| Expr.uproj _ x => collectVar x
|
| 142 |
+
| Expr.sproj _ _ x => collectVar x
|
| 143 |
+
| Expr.fap _ ys => collectArgs ys
|
| 144 |
+
| Expr.pap _ ys => collectArgs ys
|
| 145 |
+
| Expr.ap x ys => collectVar x >> collectArgs ys
|
| 146 |
+
| Expr.box _ x => collectVar x
|
| 147 |
+
| Expr.unbox x => collectVar x
|
| 148 |
+
| Expr.lit _ => skip
|
| 149 |
+
| Expr.isShared x => collectVar x
|
| 150 |
+
|
| 151 |
+
private def collectAlts (f : FnBody → Collector) (alts : Array Alt) : Collector :=
|
| 152 |
+
collectArray alts fun alt => f alt.body
|
| 153 |
+
|
| 154 |
+
partial def collectFnBody : FnBody → Collector
|
| 155 |
+
| FnBody.vdecl x _ v b => collectExpr v >> withVar x (collectFnBody b)
|
| 156 |
+
| FnBody.jdecl j ys v b => withParams ys (collectFnBody v) >> withJP j (collectFnBody b)
|
| 157 |
+
| FnBody.set x _ y b => collectVar x >> collectArg y >> collectFnBody b
|
| 158 |
+
| FnBody.uset x _ y b => collectVar x >> collectVar y >> collectFnBody b
|
| 159 |
+
| FnBody.sset x _ _ y _ b => collectVar x >> collectVar y >> collectFnBody b
|
| 160 |
+
| FnBody.setTag x _ b => collectVar x >> collectFnBody b
|
| 161 |
+
| FnBody.inc x _ _ _ b => collectVar x >> collectFnBody b
|
| 162 |
+
| FnBody.dec x _ _ _ b => collectVar x >> collectFnBody b
|
| 163 |
+
| FnBody.del x b => collectVar x >> collectFnBody b
|
| 164 |
+
| FnBody.mdata _ b => collectFnBody b
|
| 165 |
+
| FnBody.case _ x _ alts => collectVar x >> collectAlts collectFnBody alts
|
| 166 |
+
| FnBody.jmp j ys => collectJP j >> collectArgs ys
|
| 167 |
+
| FnBody.ret x => collectArg x
|
| 168 |
+
| FnBody.unreachable => skip
|
| 169 |
+
|
| 170 |
+
end FreeIndices
|
| 171 |
+
|
| 172 |
+
def FnBody.collectFreeIndices (b : FnBody) (vs : IndexSet) : IndexSet :=
|
| 173 |
+
FreeIndices.collectFnBody b {} vs
|
| 174 |
+
|
| 175 |
+
def FnBody.freeIndices (b : FnBody) : IndexSet :=
|
| 176 |
+
b.collectFreeIndices {}
|
| 177 |
+
|
| 178 |
+
namespace HasIndex
|
| 179 |
+
/-! In principle, we can check whether a function body `b` contains an index `i` using
|
| 180 |
+
`b.freeIndices.contains i`, but it is more efficient to avoid the construction
|
| 181 |
+
of the set of freeIndices and just search whether `i` occurs in `b` or not.
|
| 182 |
+
-/
|
| 183 |
+
def visitVar (w : Index) (x : VarId) : Bool := w == x.idx
|
| 184 |
+
def visitJP (w : Index) (x : JoinPointId) : Bool := w == x.idx
|
| 185 |
+
|
| 186 |
+
def visitArg (w : Index) : Arg → Bool
|
| 187 |
+
| Arg.var x => visitVar w x
|
| 188 |
+
| _ => false
|
| 189 |
+
|
| 190 |
+
def visitArgs (w : Index) (xs : Array Arg) : Bool :=
|
| 191 |
+
xs.any (visitArg w)
|
| 192 |
+
|
| 193 |
+
def visitParams (w : Index) (ps : Array Param) : Bool :=
|
| 194 |
+
ps.any (fun p => w == p.x.idx)
|
| 195 |
+
|
| 196 |
+
def visitExpr (w : Index) : Expr → Bool
|
| 197 |
+
| Expr.ctor _ ys => visitArgs w ys
|
| 198 |
+
| Expr.reset _ x => visitVar w x
|
| 199 |
+
| Expr.reuse x _ _ ys => visitVar w x || visitArgs w ys
|
| 200 |
+
| Expr.proj _ x => visitVar w x
|
| 201 |
+
| Expr.uproj _ x => visitVar w x
|
| 202 |
+
| Expr.sproj _ _ x => visitVar w x
|
| 203 |
+
| Expr.fap _ ys => visitArgs w ys
|
| 204 |
+
| Expr.pap _ ys => visitArgs w ys
|
| 205 |
+
| Expr.ap x ys => visitVar w x || visitArgs w ys
|
| 206 |
+
| Expr.box _ x => visitVar w x
|
| 207 |
+
| Expr.unbox x => visitVar w x
|
| 208 |
+
| Expr.lit _ => false
|
| 209 |
+
| Expr.isShared x => visitVar w x
|
| 210 |
+
|
| 211 |
+
partial def visitFnBody (w : Index) : FnBody → Bool
|
| 212 |
+
| FnBody.vdecl _ _ v b => visitExpr w v || visitFnBody w b
|
| 213 |
+
| FnBody.jdecl _ _ v b => visitFnBody w v || visitFnBody w b
|
| 214 |
+
| FnBody.set x _ y b => visitVar w x || visitArg w y || visitFnBody w b
|
| 215 |
+
| FnBody.uset x _ y b => visitVar w x || visitVar w y || visitFnBody w b
|
| 216 |
+
| FnBody.sset x _ _ y _ b => visitVar w x || visitVar w y || visitFnBody w b
|
| 217 |
+
| FnBody.setTag x _ b => visitVar w x || visitFnBody w b
|
| 218 |
+
| FnBody.inc x _ _ _ b => visitVar w x || visitFnBody w b
|
| 219 |
+
| FnBody.dec x _ _ _ b => visitVar w x || visitFnBody w b
|
| 220 |
+
| FnBody.del x b => visitVar w x || visitFnBody w b
|
| 221 |
+
| FnBody.mdata _ b => visitFnBody w b
|
| 222 |
+
| FnBody.jmp j ys => visitJP w j || visitArgs w ys
|
| 223 |
+
| FnBody.ret x => visitArg w x
|
| 224 |
+
| FnBody.case _ x _ alts => visitVar w x || alts.any (fun alt => visitFnBody w alt.body)
|
| 225 |
+
| FnBody.unreachable => false
|
| 226 |
+
|
| 227 |
+
end HasIndex
|
| 228 |
+
|
| 229 |
+
def Arg.hasFreeVar (arg : Arg) (x : VarId) : Bool := HasIndex.visitArg x.idx arg
|
| 230 |
+
def Expr.hasFreeVar (e : Expr) (x : VarId) : Bool := HasIndex.visitExpr x.idx e
|
| 231 |
+
def FnBody.hasFreeVar (b : FnBody) (x : VarId) : Bool := HasIndex.visitFnBody x.idx b
|
| 232 |
+
|
| 233 |
+
end Lean.IR
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/LLVMBindings.lean
ADDED
|
@@ -0,0 +1,470 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2022 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Siddharth Bhat
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Init.System.IO
|
| 8 |
+
|
| 9 |
+
namespace LLVM
|
| 10 |
+
/-!
|
| 11 |
+
This file defines bindings for LLVM. The main mechanisms
|
| 12 |
+
are to:
|
| 13 |
+
- Mirror the values of C enumerations.
|
| 14 |
+
- Create opaque values for LLVM's pointer based API
|
| 15 |
+
- Write bindings that are `IO` based, which mutate the underlying in-memory
|
| 16 |
+
data structures to build IR.
|
| 17 |
+
-/
|
| 18 |
+
|
| 19 |
+
-- https://github.com/llvm/llvm-project/blob/c3e073bcbdc523b0f758d44a89a6333e38bff863/llvm/include/llvm-c/TargetMachine.h#L64
|
| 20 |
+
structure CodegenFileType where
|
| 21 |
+
private mk :: val : UInt64
|
| 22 |
+
|
| 23 |
+
def CodegenFileType.AssemblyFile : CodegenFileType := { val := 0 }
|
| 24 |
+
def CodegenFileType.ObjectFile : CodegenFileType := { val := 1 }
|
| 25 |
+
|
| 26 |
+
-- https://github.com/llvm/llvm-project/blob/c3e073bcbdc523b0f758d44a89a6333e38bff863/llvm/include/llvm-c/Core.h#L290
|
| 27 |
+
structure IntPredicate where
|
| 28 |
+
private mk :: val : UInt64
|
| 29 |
+
|
| 30 |
+
def IntPredicate.EQ : IntPredicate := { val := 32 }
|
| 31 |
+
def IntPredicate.NE : IntPredicate := { val := IntPredicate.EQ.val + 1 }
|
| 32 |
+
def IntPredicate.UGT : IntPredicate := { val := IntPredicate.NE.val + 1 }
|
| 33 |
+
|
| 34 |
+
-- https://github.com/llvm/llvm-project/blob/c3e073bcbdc523b0f758d44a89a6333e38bff863/llvm/include/llvm-c/Core.h#L457
|
| 35 |
+
structure AttributeIndex where
|
| 36 |
+
private mk :: val : UInt64
|
| 37 |
+
|
| 38 |
+
def AttributeIndex.AttributeReturnIndex : AttributeIndex := { val := 0 }
|
| 39 |
+
-- This value is ~0 for 64 bit
|
| 40 |
+
def AttributeIndex.AttributeFunctionIndex : AttributeIndex := { val := 18446744073709551615 }
|
| 41 |
+
|
| 42 |
+
structure BasicBlock (ctx : Context) where
|
| 43 |
+
private mk :: ptr : USize
|
| 44 |
+
instance : Nonempty (BasicBlock ctx) := ⟨{ ptr := default }⟩
|
| 45 |
+
|
| 46 |
+
structure Builder (ctx : Context) where
|
| 47 |
+
private mk :: ptr : USize
|
| 48 |
+
instance : Nonempty (Builder ctx) := ⟨{ ptr := default }⟩
|
| 49 |
+
|
| 50 |
+
structure Context where
|
| 51 |
+
private mk :: ptr : USize
|
| 52 |
+
instance : Nonempty Context := ⟨{ ptr := default }⟩
|
| 53 |
+
|
| 54 |
+
structure LLVMType (ctx : Context) where
|
| 55 |
+
private mk :: ptr : USize
|
| 56 |
+
instance : Nonempty (LLVMType ctx) := ⟨{ ptr := default }⟩
|
| 57 |
+
|
| 58 |
+
structure MemoryBuffer (ctx : Context) where
|
| 59 |
+
private mk :: ptr : USize
|
| 60 |
+
instance : Nonempty (MemoryBuffer ctx) := ⟨{ ptr := default }⟩
|
| 61 |
+
|
| 62 |
+
structure Module (ctx : Context) where
|
| 63 |
+
private mk :: ptr : USize
|
| 64 |
+
instance : Nonempty (Module ctx) := ⟨{ ptr := default }⟩
|
| 65 |
+
|
| 66 |
+
/-
|
| 67 |
+
structure PassManager (ctx : Context) where
|
| 68 |
+
private mk :: ptr : USize
|
| 69 |
+
instance : Nonempty (PassManager ctx) := ⟨{ ptr := default }⟩
|
| 70 |
+
|
| 71 |
+
structure PassManagerBuilder (ctx : Context) where
|
| 72 |
+
private mk :: ptr : USize
|
| 73 |
+
instance : Nonempty (PassManagerBuilder ctx) := ⟨{ ptr := default }⟩
|
| 74 |
+
-/
|
| 75 |
+
|
| 76 |
+
structure Target (ctx : Context) where
|
| 77 |
+
private mk :: ptr : USize
|
| 78 |
+
instance : Nonempty (Target ctx) := ⟨{ ptr := default }⟩
|
| 79 |
+
|
| 80 |
+
structure TargetMachine (ctx : Context) where
|
| 81 |
+
private mk :: ptr : USize
|
| 82 |
+
instance : Nonempty (TargetMachine ctx) := ⟨{ ptr := default }⟩
|
| 83 |
+
|
| 84 |
+
structure Value (ctx : Context) where
|
| 85 |
+
private mk :: ptr : USize
|
| 86 |
+
instance : Nonempty (Value ctx) := ⟨{ ptr := default }⟩
|
| 87 |
+
|
| 88 |
+
/-- Check if the value is a null pointer. --/
|
| 89 |
+
def Value.isNull (v : Value ctx) : Bool := v.ptr == 0
|
| 90 |
+
|
| 91 |
+
@[extern "lean_llvm_get_value_name2"]
|
| 92 |
+
opaque Value.getName {ctx : Context} (value : Value ctx) : BaseIO String
|
| 93 |
+
|
| 94 |
+
structure Attribute (ctx : Context) where
|
| 95 |
+
private mk :: ptr : USize
|
| 96 |
+
instance : Nonempty (Attribute ctx) := ⟨{ ptr := default }⟩
|
| 97 |
+
|
| 98 |
+
@[extern "lean_llvm_initialize_target_info"]
|
| 99 |
+
opaque llvmInitializeTargetInfo : BaseIO (Unit)
|
| 100 |
+
|
| 101 |
+
@[extern "lean_llvm_create_context"]
|
| 102 |
+
opaque createContext : BaseIO (Context)
|
| 103 |
+
|
| 104 |
+
@[extern "lean_llvm_create_module"]
|
| 105 |
+
opaque createModule (ctx : Context) (name : @&String) : BaseIO (Module ctx)
|
| 106 |
+
|
| 107 |
+
@[extern "lean_llvm_module_to_string"]
|
| 108 |
+
opaque moduleToString (m : Module ctx) : BaseIO String
|
| 109 |
+
|
| 110 |
+
@[extern "lean_llvm_write_bitcode_to_file"]
|
| 111 |
+
opaque writeBitcodeToFile (m : Module ctx) (path : @&String) : BaseIO Unit
|
| 112 |
+
|
| 113 |
+
@[extern "lean_llvm_add_function"]
|
| 114 |
+
opaque addFunction (m : Module ctx) (name : @&String) (type : LLVMType ctx) : BaseIO (Value ctx)
|
| 115 |
+
|
| 116 |
+
@[extern "lean_llvm_get_first_function"]
|
| 117 |
+
opaque getFirstFunction (m : Module ctx) : BaseIO (Value ctx)
|
| 118 |
+
|
| 119 |
+
@[extern "lean_llvm_get_next_function"]
|
| 120 |
+
opaque getNextFunction (glbl : Value ctx) : BaseIO (Value ctx)
|
| 121 |
+
|
| 122 |
+
@[extern "lean_llvm_get_named_function"]
|
| 123 |
+
opaque getNamedFunction (m : Module ctx) (name : @&String) : BaseIO (Option (Value ctx))
|
| 124 |
+
|
| 125 |
+
@[extern "lean_llvm_add_global"]
|
| 126 |
+
opaque addGlobal (m : Module ctx) (name : @&String) (type : LLVMType ctx) : BaseIO (Value ctx)
|
| 127 |
+
|
| 128 |
+
@[extern "lean_llvm_get_named_global"]
|
| 129 |
+
opaque getNamedGlobal (m : Module ctx) (name : @&String) : BaseIO (Option (Value ctx))
|
| 130 |
+
|
| 131 |
+
@[extern "lean_llvm_get_first_global"]
|
| 132 |
+
opaque getFirstGlobal (m : Module ctx) : BaseIO (Value ctx)
|
| 133 |
+
|
| 134 |
+
@[extern "lean_llvm_get_next_global"]
|
| 135 |
+
opaque getNextGlobal (glbl : Value ctx) : BaseIO (Value ctx)
|
| 136 |
+
|
| 137 |
+
@[extern "lean_llvm_build_global_string"]
|
| 138 |
+
opaque buildGlobalString (builder : Builder ctx) (value : @&String) (name : @&String := "") : BaseIO (Value ctx)
|
| 139 |
+
|
| 140 |
+
@[extern "llvm_is_declaration"]
|
| 141 |
+
opaque isDeclaration (global : Value ctx) : BaseIO Bool
|
| 142 |
+
|
| 143 |
+
@[extern "lean_llvm_set_initializer"]
|
| 144 |
+
opaque setInitializer (glbl : Value ctx) (val : Value ctx) : BaseIO Unit
|
| 145 |
+
|
| 146 |
+
@[extern "lean_llvm_function_type"]
|
| 147 |
+
opaque functionType (retty : LLVMType ctx) (args : @&Array (LLVMType ctx)) (isVarArg : Bool := false) : BaseIO (LLVMType ctx)
|
| 148 |
+
|
| 149 |
+
@[extern "lean_llvm_void_type_in_context"]
|
| 150 |
+
opaque voidType (ctx : Context) : BaseIO (LLVMType ctx)
|
| 151 |
+
|
| 152 |
+
@[extern "lean_llvm_int_type_in_context"]
|
| 153 |
+
opaque intTypeInContext (ctx : Context) (width : UInt64) : BaseIO (LLVMType ctx)
|
| 154 |
+
|
| 155 |
+
@[extern "lean_llvm_opaque_pointer_type_in_context"]
|
| 156 |
+
opaque opaquePointerTypeInContext (ctx : Context) (addrspace: UInt64 := 0) : BaseIO (LLVMType ctx)
|
| 157 |
+
|
| 158 |
+
@[extern "lean_llvm_float_type_in_context"]
|
| 159 |
+
opaque floatTypeInContext (ctx : Context) : BaseIO (LLVMType ctx)
|
| 160 |
+
|
| 161 |
+
@[extern "lean_llvm_double_type_in_context"]
|
| 162 |
+
opaque doubleTypeInContext (ctx : Context) : BaseIO (LLVMType ctx)
|
| 163 |
+
|
| 164 |
+
@[extern "lean_llvm_pointer_type"]
|
| 165 |
+
opaque pointerType (elemty : LLVMType ctx) : BaseIO (LLVMType ctx)
|
| 166 |
+
|
| 167 |
+
@[extern "lean_llvm_array_type"]
|
| 168 |
+
opaque arrayType (elemty : LLVMType ctx) (nelem : UInt64) : BaseIO (LLVMType ctx)
|
| 169 |
+
|
| 170 |
+
@[extern "lean_llvm_const_array"]
|
| 171 |
+
opaque constArray (elemty : LLVMType ctx) (vals : @&Array (Value ctx)) : BaseIO (LLVMType ctx)
|
| 172 |
+
|
| 173 |
+
-- `constString` provides a `String` as a constant array of element type `i8`
|
| 174 |
+
@[extern "lean_llvm_const_string"]
|
| 175 |
+
opaque constString (ctx : Context) (str : @&String) : BaseIO (Value ctx)
|
| 176 |
+
|
| 177 |
+
@[extern "lean_llvm_const_pointer_null"]
|
| 178 |
+
opaque constPointerNull (elemty : LLVMType ctx) : BaseIO (Value ctx)
|
| 179 |
+
|
| 180 |
+
@[extern "lean_llvm_get_undef"]
|
| 181 |
+
opaque getUndef (elemty : LLVMType ctx) : BaseIO (Value ctx)
|
| 182 |
+
|
| 183 |
+
@[extern "lean_llvm_create_builder_in_context"]
|
| 184 |
+
opaque createBuilderInContext (ctx : Context) : BaseIO (Builder ctx)
|
| 185 |
+
|
| 186 |
+
@[extern "lean_llvm_append_basic_block_in_context"]
|
| 187 |
+
opaque appendBasicBlockInContext (ctx : Context) (fn : Value ctx) (name : @&String) : BaseIO (BasicBlock ctx)
|
| 188 |
+
|
| 189 |
+
@[extern "lean_llvm_count_basic_blocks"]
|
| 190 |
+
opaque countBasicBlocks (fn : Value ctx) : BaseIO UInt64
|
| 191 |
+
|
| 192 |
+
@[extern "lean_llvm_get_entry_basic_block"]
|
| 193 |
+
opaque getEntryBasicBlock (fn : Value ctx) : BaseIO (BasicBlock ctx)
|
| 194 |
+
|
| 195 |
+
@[extern "lean_llvm_get_first_instruction"]
|
| 196 |
+
opaque getFirstInstruction (bb : BasicBlock ctx) : BaseIO (Option (Value ctx))
|
| 197 |
+
|
| 198 |
+
@[extern "lean_llvm_position_builder_before"]
|
| 199 |
+
opaque positionBuilderBefore (builder : Builder ctx) (instr : Value ctx) : BaseIO Unit
|
| 200 |
+
|
| 201 |
+
@[extern "lean_llvm_position_builder_at_end"]
|
| 202 |
+
opaque positionBuilderAtEnd (builder : Builder ctx) (bb : BasicBlock ctx) : BaseIO Unit
|
| 203 |
+
|
| 204 |
+
@[extern "lean_llvm_build_call2"]
|
| 205 |
+
opaque buildCall2 (builder : Builder ctx) (ty: LLVMType ctx) (fn : Value ctx) (args : @&Array (Value ctx)) (name : @&String := "") : BaseIO (Value ctx)
|
| 206 |
+
|
| 207 |
+
@[extern "lean_llvm_set_tail_call"]
|
| 208 |
+
opaque setTailCall (fn : Value ctx) (istail : Bool) : BaseIO Unit
|
| 209 |
+
|
| 210 |
+
@[extern "lean_llvm_build_cond_br"]
|
| 211 |
+
opaque buildCondBr (builder : Builder ctx) (if_ : Value ctx) (thenbb : BasicBlock ctx) (elsebb : BasicBlock ctx) : BaseIO (Value ctx)
|
| 212 |
+
|
| 213 |
+
@[extern "lean_llvm_build_br"]
|
| 214 |
+
opaque buildBr (builder : Builder ctx) (bb : BasicBlock ctx) : BaseIO (Value ctx)
|
| 215 |
+
|
| 216 |
+
@[extern "lean_llvm_build_alloca"]
|
| 217 |
+
opaque buildAlloca (builder : Builder ctx) (ty : LLVMType ctx) (name : @&String := "") : BaseIO (Value ctx)
|
| 218 |
+
|
| 219 |
+
@[extern "lean_llvm_build_load2"]
|
| 220 |
+
opaque buildLoad2 (builder : Builder ctx) (ty: LLVMType ctx) (val : Value ctx) (name : @&String := "") : BaseIO (Value ctx)
|
| 221 |
+
|
| 222 |
+
@[extern "lean_llvm_build_store"]
|
| 223 |
+
opaque buildStore (builder : Builder ctx) (val : Value ctx) (store_loc_ptr : Value ctx) : BaseIO Unit
|
| 224 |
+
|
| 225 |
+
@[extern "lean_llvm_build_ret"]
|
| 226 |
+
opaque buildRet (builder : Builder ctx) (val : Value ctx) : BaseIO (Value ctx)
|
| 227 |
+
|
| 228 |
+
@[extern "lean_llvm_build_unreachable"]
|
| 229 |
+
opaque buildUnreachable (builder : Builder ctx) : BaseIO (Value ctx)
|
| 230 |
+
|
| 231 |
+
@[extern "lean_llvm_build_gep2"]
|
| 232 |
+
opaque buildGEP2 (builder : Builder ctx) (ty: LLVMType ctx) (base : Value ctx) (ixs : @&Array (Value ctx)) (name : @&String := "") : BaseIO (Value ctx)
|
| 233 |
+
|
| 234 |
+
@[extern "lean_llvm_build_inbounds_gep2"]
|
| 235 |
+
opaque buildInBoundsGEP2 (builder : Builder ctx) (ty: LLVMType ctx) (base : Value ctx) (ixs : @&Array (Value ctx)) (name : @&String := "") : BaseIO (Value ctx)
|
| 236 |
+
|
| 237 |
+
@[extern "lean_llvm_build_sext"]
|
| 238 |
+
opaque buildSext (builder : Builder ctx) (val : Value ctx) (destTy : LLVMType ctx) (name : @&String := "") : BaseIO (Value ctx)
|
| 239 |
+
|
| 240 |
+
@[extern "lean_llvm_build_zext"]
|
| 241 |
+
opaque buildZext (builder : Builder ctx) (val : Value ctx) (destTy : LLVMType ctx) (name : @&String := "") : BaseIO (Value ctx)
|
| 242 |
+
|
| 243 |
+
@[extern "lean_llvm_build_sext_or_trunc"]
|
| 244 |
+
opaque buildSextOrTrunc (builder : Builder ctx) (val : Value ctx) (destTy : LLVMType ctx) (name : @&String := "") : BaseIO (Value ctx)
|
| 245 |
+
|
| 246 |
+
@[extern "lean_llvm_build_switch"]
|
| 247 |
+
opaque buildSwitch (builder : Builder ctx) (val : Value ctx) (elseBB : BasicBlock ctx) (numCasesHint : UInt64) : BaseIO (Value ctx)
|
| 248 |
+
|
| 249 |
+
@[extern "lean_llvm_build_ptr_to_int"]
|
| 250 |
+
opaque buildPtrToInt (builder : Builder ctx) (ptr : Value ctx) (destTy : LLVMType ctx) (name : @&String := "") : BaseIO (Value ctx)
|
| 251 |
+
|
| 252 |
+
@[extern "lean_llvm_build_mul"]
|
| 253 |
+
opaque buildMul (builder : Builder ctx) (x y : Value ctx) (name : @&String := "") : BaseIO (Value ctx)
|
| 254 |
+
|
| 255 |
+
@[extern "lean_llvm_build_add"]
|
| 256 |
+
opaque buildAdd (builder : Builder ctx) (x y : Value ctx) (name : @&String := "") : BaseIO (Value ctx)
|
| 257 |
+
|
| 258 |
+
@[extern "lean_llvm_build_sub"]
|
| 259 |
+
opaque buildSub (builder : Builder ctx) (x y : Value ctx) (name : @&String := "") : BaseIO (Value ctx)
|
| 260 |
+
|
| 261 |
+
@[extern "lean_llvm_build_not"]
|
| 262 |
+
opaque buildNot (builder : Builder ctx) (x : Value ctx) (name : @&String := "") : BaseIO (Value ctx)
|
| 263 |
+
|
| 264 |
+
@[extern "lean_llvm_build_icmp"]
|
| 265 |
+
opaque buildICmp (builder : Builder ctx) (predicate : IntPredicate) (x y : Value ctx) (name : @&String := "") : BaseIO (Value ctx)
|
| 266 |
+
|
| 267 |
+
@[extern "lean_llvm_add_case"]
|
| 268 |
+
opaque addCase (switch onVal : Value ctx) (destBB : BasicBlock ctx) : BaseIO Unit
|
| 269 |
+
|
| 270 |
+
@[extern "lean_llvm_get_insert_block"]
|
| 271 |
+
opaque getInsertBlock (builder : Builder ctx) : BaseIO (BasicBlock ctx)
|
| 272 |
+
|
| 273 |
+
@[extern "lean_llvm_clear_insertion_position"]
|
| 274 |
+
opaque clearInsertionPosition (builder : Builder ctx) : BaseIO Unit
|
| 275 |
+
|
| 276 |
+
@[extern "lean_llvm_get_basic_block_parent"]
|
| 277 |
+
opaque getBasicBlockParent (bb : BasicBlock ctx) : BaseIO (Value ctx)
|
| 278 |
+
|
| 279 |
+
@[extern "lean_llvm_type_of"]
|
| 280 |
+
opaque typeOf (val : Value ctx) : BaseIO (LLVMType ctx)
|
| 281 |
+
|
| 282 |
+
@[extern "lean_llvm_const_int"]
|
| 283 |
+
opaque constInt (intty : LLVMType ctx) (value : UInt64) (signExtend : @Bool := false) : BaseIO (Value ctx)
|
| 284 |
+
|
| 285 |
+
@[extern "lean_llvm_print_module_to_string"]
|
| 286 |
+
opaque printModuletoString (mod : Module ctx) : BaseIO (String)
|
| 287 |
+
|
| 288 |
+
@[extern "lean_llvm_print_module_to_file"]
|
| 289 |
+
opaque printModuletoFile (mod : Module ctx) (file : @&String) : BaseIO Unit
|
| 290 |
+
|
| 291 |
+
@[extern "llvm_count_params"]
|
| 292 |
+
opaque countParams (fn : Value ctx) : BaseIO UInt64
|
| 293 |
+
|
| 294 |
+
@[extern "llvm_get_param"]
|
| 295 |
+
opaque getParam (fn : Value ctx) (ix : UInt64) : BaseIO (Value ctx)
|
| 296 |
+
|
| 297 |
+
@[extern "lean_llvm_create_memory_buffer_with_contents_of_file"]
|
| 298 |
+
opaque createMemoryBufferWithContentsOfFile (path : @&String) : BaseIO (MemoryBuffer ctx)
|
| 299 |
+
|
| 300 |
+
@[extern "lean_llvm_parse_bitcode"]
|
| 301 |
+
opaque parseBitcode (ctx : Context) (membuf : MemoryBuffer ctx) : BaseIO (Module ctx)
|
| 302 |
+
|
| 303 |
+
@[extern "lean_llvm_link_modules"]
|
| 304 |
+
opaque linkModules (dest : Module ctx) (src : Module ctx) : BaseIO Unit
|
| 305 |
+
|
| 306 |
+
@[extern "lean_llvm_get_default_target_triple"]
|
| 307 |
+
opaque getDefaultTargetTriple : BaseIO String
|
| 308 |
+
|
| 309 |
+
@[extern "lean_llvm_get_target_from_triple"]
|
| 310 |
+
opaque getTargetFromTriple (triple : @&String) : BaseIO (Target ctx)
|
| 311 |
+
|
| 312 |
+
@[extern "lean_llvm_create_target_machine"]
|
| 313 |
+
opaque createTargetMachine (target : Target ctx) (tripleStr : @&String) (cpu : @&String) (features : @&String) : BaseIO (TargetMachine ctx)
|
| 314 |
+
|
| 315 |
+
@[extern "lean_llvm_target_machine_emit_to_file"]
|
| 316 |
+
opaque targetMachineEmitToFile (targetMachine : TargetMachine ctx) (module : Module ctx) (filepath : @&String) (codegenType : LLVM.CodegenFileType) : BaseIO Unit
|
| 317 |
+
|
| 318 |
+
/-
|
| 319 |
+
@[extern "lean_llvm_create_pass_manager"]
|
| 320 |
+
opaque createPassManager : BaseIO (PassManager ctx)
|
| 321 |
+
|
| 322 |
+
@[extern "lean_llvm_dispose_pass_manager"]
|
| 323 |
+
opaque disposePassManager (pm : PassManager ctx) : BaseIO Unit
|
| 324 |
+
|
| 325 |
+
@[extern "lean_llvm_run_pass_manager"]
|
| 326 |
+
opaque runPassManager (pm : PassManager ctx) (mod : Module ctx): BaseIO Unit
|
| 327 |
+
|
| 328 |
+
@[extern "lean_llvm_create_pass_manager_builder"]
|
| 329 |
+
opaque createPassManagerBuilder : BaseIO (PassManagerBuilder ctx)
|
| 330 |
+
|
| 331 |
+
@[extern "lean_llvm_dispose_pass_manager_builder"]
|
| 332 |
+
opaque disposePassManagerBuilder (pmb : PassManagerBuilder ctx) : BaseIO Unit
|
| 333 |
+
|
| 334 |
+
@[extern "lean_llvm_pass_manager_builder_set_opt_level"]
|
| 335 |
+
opaque PassManagerBuilder.setOptLevel (pmb : PassManagerBuilder ctx) (optLevel : unsigned) : BaseIO Unit
|
| 336 |
+
|
| 337 |
+
@[extern "lean_llvm_pass_manager_builder_populate_module_pass_manager"]
|
| 338 |
+
opaque PassManagerBuilder.populateModulePassManager (pmb : PassManagerBuilder ctx) (pm : PassManager ctx): BaseIO Unit
|
| 339 |
+
-/
|
| 340 |
+
|
| 341 |
+
@[extern "lean_llvm_dispose_target_machine"]
|
| 342 |
+
opaque disposeTargetMachine (tm : TargetMachine ctx) : BaseIO Unit
|
| 343 |
+
|
| 344 |
+
@[extern "lean_llvm_dispose_module"]
|
| 345 |
+
opaque disposeModule (m : Module ctx) : BaseIO Unit
|
| 346 |
+
|
| 347 |
+
@[extern "lean_llvm_verify_module"]
|
| 348 |
+
opaque verifyModule (m : Module ctx) : BaseIO (Option String)
|
| 349 |
+
|
| 350 |
+
@[extern "lean_llvm_create_string_attribute"]
|
| 351 |
+
opaque createStringAttribute (key : String) (value : String) : BaseIO (Attribute ctx)
|
| 352 |
+
|
| 353 |
+
@[extern "lean_llvm_add_attribute_at_index"]
|
| 354 |
+
opaque addAttributeAtIndex (fn : Value ctx) (idx: AttributeIndex) (attr: Attribute ctx) : BaseIO Unit
|
| 355 |
+
|
| 356 |
+
|
| 357 |
+
-- https://github.com/llvm/llvm-project/blob/c3e073bcbdc523b0f758d44a89a6333e38bff863/llvm/include/llvm-c/Core.h#L198
|
| 358 |
+
structure Visibility where
|
| 359 |
+
private mk :: val : UInt64
|
| 360 |
+
|
| 361 |
+
def Visibility.default : Visibility := { val := 0 }
|
| 362 |
+
def Visibility.hidden : Visibility := { val := 1 }
|
| 363 |
+
def Visibility.protected : Visibility := { val := 2 }
|
| 364 |
+
|
| 365 |
+
@[extern "lean_llvm_set_visibility"]
|
| 366 |
+
opaque setVisibility {ctx : Context} (value : Value ctx) (visibility : Visibility) : BaseIO Unit
|
| 367 |
+
|
| 368 |
+
-- https://github.com/llvm/llvm-project/blob/c3e073bcbdc523b0f758d44a89a6333e38bff863/llvm/include/llvm-c/Core.h#L210
|
| 369 |
+
structure DLLStorageClass where
|
| 370 |
+
private mk :: val : UInt64
|
| 371 |
+
|
| 372 |
+
def DLLStorageClass.default : DLLStorageClass := { val := 0 }
|
| 373 |
+
def DLLStorageClass.import : DLLStorageClass := { val := 1 }
|
| 374 |
+
def DLLStorageClass.export : DLLStorageClass := { val := 2 }
|
| 375 |
+
|
| 376 |
+
@[extern "lean_llvm_set_dll_storage_class"]
|
| 377 |
+
opaque setDLLStorageClass {ctx : Context} (value : Value ctx) (dllStorageClass : DLLStorageClass) : BaseIO Unit
|
| 378 |
+
|
| 379 |
+
-- https://github.com/llvm/llvm-project/blob/c3e073bcbdc523b0f758d44a89a6333e38bff863/llvm/include/llvm-c/Core.h#L192
|
| 380 |
+
structure Linkage where
|
| 381 |
+
private mk :: val : UInt64
|
| 382 |
+
|
| 383 |
+
/-- Externally visible function -/
|
| 384 |
+
def Linkage.external : Linkage := { val := 0 }
|
| 385 |
+
def Linkage.availableExternally : Linkage := { val := 1 }
|
| 386 |
+
/-- Keep one copy of function when linking (inline) -/
|
| 387 |
+
def Linkage.linkOnceAny : Linkage := { val := 2 }
|
| 388 |
+
/-- Same, but only replaced by something equivalent -/
|
| 389 |
+
def Linkage.linkOnceODR : Linkage := { val := 3 }
|
| 390 |
+
/-- Obsolete -/
|
| 391 |
+
def Linkage.linkOnceODRAutoHide : Linkage := { val := 4 }
|
| 392 |
+
/-- Keep one copy of function when linking (weak) -/
|
| 393 |
+
def Linkage.weakAny : Linkage := { val := 5 }
|
| 394 |
+
/-- Same, but only replaced by something equivalent -/
|
| 395 |
+
def Linkage.weakODR : Linkage := { val := 6 }
|
| 396 |
+
/-- Special purpose, only applies to global arrays -/
|
| 397 |
+
def Linkage.appending : Linkage := { val := 7 }
|
| 398 |
+
/-- Rename collisions when linking (static functions) -/
|
| 399 |
+
def Linkage.internal : Linkage := { val := 8 }
|
| 400 |
+
/-- Like Internal, but omit from symbol table -/
|
| 401 |
+
def Linkage.private : Linkage := { val := 9 }
|
| 402 |
+
/-- Obsolete -/
|
| 403 |
+
def Linkage.dllImport : Linkage := { val := 10 }
|
| 404 |
+
/-- Obsolete -/
|
| 405 |
+
def Linkage.dllExport : Linkage := { val := 11 }
|
| 406 |
+
/-- ExternalWeak linkage description -/
|
| 407 |
+
def Linkage.externalWeak : Linkage := { val := 12 }
|
| 408 |
+
/-- Obsolete -/
|
| 409 |
+
def Linkage.ghost : Linkage := { val := 13 }
|
| 410 |
+
/-- Tentative definitions -/
|
| 411 |
+
def Linkage.common : Linkage := { val := 14 }
|
| 412 |
+
/-- Like Private, but linker removes. -/
|
| 413 |
+
def Linkage.linkerPrivate : Linkage := { val := 15 }
|
| 414 |
+
/-- Like LinkerPrivate, but is weak. -/
|
| 415 |
+
def Linkage.linkerPrivateWeak : Linkage := { val := 16 }
|
| 416 |
+
|
| 417 |
+
@[extern "lean_llvm_set_linkage"]
|
| 418 |
+
opaque setLinkage {ctx : Context} (value : Value ctx) (linkage : Linkage) : BaseIO Unit
|
| 419 |
+
|
| 420 |
+
|
| 421 |
+
def i1Type (ctx : LLVM.Context) : BaseIO (LLVM.LLVMType ctx) :=
|
| 422 |
+
LLVM.intTypeInContext ctx 1
|
| 423 |
+
|
| 424 |
+
def i8Type (ctx : LLVM.Context) : BaseIO (LLVM.LLVMType ctx) :=
|
| 425 |
+
LLVM.intTypeInContext ctx 8
|
| 426 |
+
|
| 427 |
+
def i16Type (ctx : LLVM.Context) : BaseIO (LLVM.LLVMType ctx) :=
|
| 428 |
+
LLVM.intTypeInContext ctx 16
|
| 429 |
+
|
| 430 |
+
def i32Type (ctx : LLVM.Context) : BaseIO (LLVM.LLVMType ctx) :=
|
| 431 |
+
LLVM.intTypeInContext ctx 32
|
| 432 |
+
|
| 433 |
+
def i64Type (ctx : LLVM.Context) : BaseIO (LLVM.LLVMType ctx) :=
|
| 434 |
+
LLVM.intTypeInContext ctx 64
|
| 435 |
+
|
| 436 |
+
def voidPtrType (ctx : LLVM.Context) : BaseIO (LLVM.LLVMType ctx) :=
|
| 437 |
+
do LLVM.pointerType (← LLVM.intTypeInContext ctx 8)
|
| 438 |
+
|
| 439 |
+
def i8PtrType (ctx : LLVM.Context) : BaseIO (LLVM.LLVMType ctx) :=
|
| 440 |
+
voidPtrType ctx
|
| 441 |
+
|
| 442 |
+
def constTrue (ctx : Context) : BaseIO (Value ctx) :=
|
| 443 |
+
do constInt (← i1Type ctx) 1 (signExtend := false)
|
| 444 |
+
|
| 445 |
+
def constFalse (ctx : Context) : BaseIO (Value ctx) :=
|
| 446 |
+
do constInt (← i1Type ctx) 0 (signExtend := false)
|
| 447 |
+
|
| 448 |
+
def constInt' (ctx : Context) (width : UInt64) (value : UInt64) (signExtend : Bool := false) : BaseIO (Value ctx) :=
|
| 449 |
+
do constInt (← LLVM.intTypeInContext ctx width) value signExtend
|
| 450 |
+
|
| 451 |
+
def constInt1 (ctx : Context) (value : UInt64) (signExtend : Bool := false) : BaseIO (Value ctx) :=
|
| 452 |
+
constInt' ctx 1 value signExtend
|
| 453 |
+
|
| 454 |
+
def constInt8 (ctx : Context) (value : UInt64) (signExtend : Bool := false) : BaseIO (Value ctx) :=
|
| 455 |
+
constInt' ctx 8 value signExtend
|
| 456 |
+
|
| 457 |
+
def constInt32 (ctx : Context) (value : UInt64) (signExtend : Bool := false) : BaseIO (Value ctx) :=
|
| 458 |
+
constInt' ctx 32 value signExtend
|
| 459 |
+
|
| 460 |
+
def constInt64 (ctx : Context) (value : UInt64) (signExtend : Bool := false) : BaseIO (Value ctx) :=
|
| 461 |
+
constInt' ctx 64 value signExtend
|
| 462 |
+
|
| 463 |
+
def constIntSizeT (ctx : Context) (value : UInt64) (signExtend : Bool := false) : BaseIO (Value ctx) :=
|
| 464 |
+
-- TODO: make this stick to the actual size_t of the target machine
|
| 465 |
+
constInt' ctx 64 value signExtend
|
| 466 |
+
|
| 467 |
+
def constIntUnsigned (ctx : Context) (value : UInt64) (signExtend : Bool := false) : BaseIO (Value ctx) :=
|
| 468 |
+
-- TODO: make this stick to the actual unsigned of the target machine
|
| 469 |
+
constInt' ctx 32 value signExtend
|
| 470 |
+
end LLVM
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/LiveVars.lean
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2019 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Compiler.IR.Basic
|
| 8 |
+
import Lean.Compiler.IR.FreeVars
|
| 9 |
+
|
| 10 |
+
namespace Lean.IR
|
| 11 |
+
|
| 12 |
+
/-! Remark: in the paper "Counting Immutable Beans" the concepts of
|
| 13 |
+
free and live variables coincide because the paper does *not* consider
|
| 14 |
+
join points. For example, consider the function body `B`
|
| 15 |
+
```
|
| 16 |
+
let x := ctor_0;
|
| 17 |
+
jmp block_1 x
|
| 18 |
+
```
|
| 19 |
+
in a context where we have the join point `block_1` defined as
|
| 20 |
+
```
|
| 21 |
+
block_1 (x : obj) : obj :=
|
| 22 |
+
let z := ctor_0 x y;
|
| 23 |
+
ret z
|
| 24 |
+
```
|
| 25 |
+
The variable `y` is live in the function body `B` since it occurs in
|
| 26 |
+
`block_1` which is "invoked" by `B`.
|
| 27 |
+
-/
|
| 28 |
+
|
| 29 |
+
namespace IsLive
|
| 30 |
+
/--
|
| 31 |
+
We use `State Context` instead of `ReaderT Context Id` because we remove
|
| 32 |
+
non local joint points from `Context` whenever we visit them instead of
|
| 33 |
+
maintaining a set of visited non local join points.
|
| 34 |
+
|
| 35 |
+
Remark: we don't need to track local join points because we assume there is
|
| 36 |
+
no variable or join point shadowing in our IR.
|
| 37 |
+
-/
|
| 38 |
+
abbrev M := StateM LocalContext
|
| 39 |
+
|
| 40 |
+
abbrev visitVar (w : Index) (x : VarId) : M Bool := pure (HasIndex.visitVar w x)
|
| 41 |
+
abbrev visitJP (w : Index) (x : JoinPointId) : M Bool := pure (HasIndex.visitJP w x)
|
| 42 |
+
abbrev visitArg (w : Index) (a : Arg) : M Bool := pure (HasIndex.visitArg w a)
|
| 43 |
+
abbrev visitArgs (w : Index) (as : Array Arg) : M Bool := pure (HasIndex.visitArgs w as)
|
| 44 |
+
abbrev visitExpr (w : Index) (e : Expr) : M Bool := pure (HasIndex.visitExpr w e)
|
| 45 |
+
|
| 46 |
+
partial def visitFnBody (w : Index) : FnBody → M Bool
|
| 47 |
+
| FnBody.vdecl _ _ v b => visitExpr w v <||> visitFnBody w b
|
| 48 |
+
| FnBody.jdecl _ _ v b => visitFnBody w v <||> visitFnBody w b
|
| 49 |
+
| FnBody.set x _ y b => visitVar w x <||> visitArg w y <||> visitFnBody w b
|
| 50 |
+
| FnBody.uset x _ y b => visitVar w x <||> visitVar w y <||> visitFnBody w b
|
| 51 |
+
| FnBody.sset x _ _ y _ b => visitVar w x <||> visitVar w y <||> visitFnBody w b
|
| 52 |
+
| FnBody.setTag x _ b => visitVar w x <||> visitFnBody w b
|
| 53 |
+
| FnBody.inc x _ _ _ b => visitVar w x <||> visitFnBody w b
|
| 54 |
+
| FnBody.dec x _ _ _ b => visitVar w x <||> visitFnBody w b
|
| 55 |
+
| FnBody.del x b => visitVar w x <||> visitFnBody w b
|
| 56 |
+
| FnBody.mdata _ b => visitFnBody w b
|
| 57 |
+
| FnBody.jmp j ys => visitArgs w ys <||> do
|
| 58 |
+
let ctx ← get
|
| 59 |
+
match ctx.getJPBody j with
|
| 60 |
+
| some b =>
|
| 61 |
+
-- `j` is not a local join point since we assume we cannot shadow join point declarations.
|
| 62 |
+
-- Instead of marking the join points that we have already been visited, we permanently remove `j` from the context.
|
| 63 |
+
set (ctx.eraseJoinPointDecl j) *> visitFnBody w b
|
| 64 |
+
| none =>
|
| 65 |
+
-- `j` must be a local join point. So do nothing since we have already visite its body.
|
| 66 |
+
pure false
|
| 67 |
+
| FnBody.ret x => visitArg w x
|
| 68 |
+
| FnBody.case _ x _ alts => visitVar w x <||> alts.anyM (fun alt => visitFnBody w alt.body)
|
| 69 |
+
| FnBody.unreachable => pure false
|
| 70 |
+
|
| 71 |
+
end IsLive
|
| 72 |
+
|
| 73 |
+
/-- Return true if `x` is live in the function body `b` in the context `ctx`.
|
| 74 |
+
|
| 75 |
+
Remark: the context only needs to contain all (free) join point declarations.
|
| 76 |
+
|
| 77 |
+
Recall that we say that a join point `j` is free in `b` if `b` contains
|
| 78 |
+
`FnBody.jmp j ys` and `j` is not local. -/
|
| 79 |
+
def FnBody.hasLiveVar (b : FnBody) (ctx : LocalContext) (x : VarId) : Bool :=
|
| 80 |
+
(IsLive.visitFnBody x.idx b).run' ctx
|
| 81 |
+
|
| 82 |
+
abbrev LiveVarSet := VarIdSet
|
| 83 |
+
abbrev JPLiveVarMap := RBMap JoinPointId LiveVarSet (fun j₁ j₂ => compare j₁.idx j₂.idx)
|
| 84 |
+
|
| 85 |
+
instance : Inhabited LiveVarSet where
|
| 86 |
+
default := {}
|
| 87 |
+
|
| 88 |
+
def mkLiveVarSet (x : VarId) : LiveVarSet :=
|
| 89 |
+
RBTree.empty.insert x
|
| 90 |
+
|
| 91 |
+
namespace LiveVars
|
| 92 |
+
|
| 93 |
+
abbrev Collector := LiveVarSet → LiveVarSet
|
| 94 |
+
|
| 95 |
+
@[inline] private def skip : Collector := fun s => s
|
| 96 |
+
@[inline] private def collectVar (x : VarId) : Collector := fun s => s.insert x
|
| 97 |
+
|
| 98 |
+
private def collectArg : Arg → Collector
|
| 99 |
+
| Arg.var x => collectVar x
|
| 100 |
+
| _ => skip
|
| 101 |
+
|
| 102 |
+
private def collectArray {α : Type} (as : Array α) (f : α → Collector) : Collector := fun s =>
|
| 103 |
+
as.foldl (fun s a => f a s) s
|
| 104 |
+
|
| 105 |
+
private def collectArgs (as : Array Arg) : Collector :=
|
| 106 |
+
collectArray as collectArg
|
| 107 |
+
|
| 108 |
+
private def accumulate (s' : LiveVarSet) : Collector :=
|
| 109 |
+
fun s => s'.fold (fun s x => s.insert x) s
|
| 110 |
+
|
| 111 |
+
private def collectJP (m : JPLiveVarMap) (j : JoinPointId) : Collector :=
|
| 112 |
+
match m.find? j with
|
| 113 |
+
| some xs => accumulate xs
|
| 114 |
+
| none => skip -- unreachable for well-formed code
|
| 115 |
+
|
| 116 |
+
private def bindVar (x : VarId) : Collector := fun s =>
|
| 117 |
+
s.erase x
|
| 118 |
+
|
| 119 |
+
private def bindParams (ps : Array Param) : Collector := fun s =>
|
| 120 |
+
ps.foldl (fun s p => s.erase p.x) s
|
| 121 |
+
|
| 122 |
+
def collectExpr : Expr → Collector
|
| 123 |
+
| Expr.ctor _ ys => collectArgs ys
|
| 124 |
+
| Expr.reset _ x => collectVar x
|
| 125 |
+
| Expr.reuse x _ _ ys => collectVar x ∘ collectArgs ys
|
| 126 |
+
| Expr.proj _ x => collectVar x
|
| 127 |
+
| Expr.uproj _ x => collectVar x
|
| 128 |
+
| Expr.sproj _ _ x => collectVar x
|
| 129 |
+
| Expr.fap _ ys => collectArgs ys
|
| 130 |
+
| Expr.pap _ ys => collectArgs ys
|
| 131 |
+
| Expr.ap x ys => collectVar x ∘ collectArgs ys
|
| 132 |
+
| Expr.box _ x => collectVar x
|
| 133 |
+
| Expr.unbox x => collectVar x
|
| 134 |
+
| Expr.lit _ => skip
|
| 135 |
+
| Expr.isShared x => collectVar x
|
| 136 |
+
|
| 137 |
+
partial def collectFnBody : FnBody → JPLiveVarMap → Collector
|
| 138 |
+
| FnBody.vdecl x _ v b, m => collectExpr v ∘ bindVar x ∘ collectFnBody b m
|
| 139 |
+
| FnBody.jdecl j ys v b, m =>
|
| 140 |
+
let jLiveVars := (bindParams ys ∘ collectFnBody v m) {};
|
| 141 |
+
let m := m.insert j jLiveVars;
|
| 142 |
+
collectFnBody b m
|
| 143 |
+
| FnBody.set x _ y b, m => collectVar x ∘ collectArg y ∘ collectFnBody b m
|
| 144 |
+
| FnBody.setTag x _ b, m => collectVar x ∘ collectFnBody b m
|
| 145 |
+
| FnBody.uset x _ y b, m => collectVar x ∘ collectVar y ∘ collectFnBody b m
|
| 146 |
+
| FnBody.sset x _ _ y _ b, m => collectVar x ∘ collectVar y ∘ collectFnBody b m
|
| 147 |
+
| FnBody.inc x _ _ _ b, m => collectVar x ∘ collectFnBody b m
|
| 148 |
+
| FnBody.dec x _ _ _ b, m => collectVar x ∘ collectFnBody b m
|
| 149 |
+
| FnBody.del x b, m => collectVar x ∘ collectFnBody b m
|
| 150 |
+
| FnBody.mdata _ b, m => collectFnBody b m
|
| 151 |
+
| FnBody.ret x, _ => collectArg x
|
| 152 |
+
| FnBody.case _ x _ alts, m => collectVar x ∘ collectArray alts (fun alt => collectFnBody alt.body m)
|
| 153 |
+
| FnBody.unreachable, _ => skip
|
| 154 |
+
| FnBody.jmp j xs, m => collectJP m j ∘ collectArgs xs
|
| 155 |
+
|
| 156 |
+
def updateJPLiveVarMap (j : JoinPointId) (ys : Array Param) (v : FnBody) (m : JPLiveVarMap) : JPLiveVarMap :=
|
| 157 |
+
let jLiveVars := (bindParams ys ∘ collectFnBody v m) {};
|
| 158 |
+
m.insert j jLiveVars
|
| 159 |
+
|
| 160 |
+
end LiveVars
|
| 161 |
+
|
| 162 |
+
def updateLiveVars (e : Expr) (v : LiveVarSet) : LiveVarSet :=
|
| 163 |
+
LiveVars.collectExpr e v
|
| 164 |
+
|
| 165 |
+
def collectLiveVars (b : FnBody) (m : JPLiveVarMap) (v : LiveVarSet := {}) : LiveVarSet :=
|
| 166 |
+
LiveVars.collectFnBody b m v
|
| 167 |
+
|
| 168 |
+
export LiveVars (updateJPLiveVarMap)
|
| 169 |
+
|
| 170 |
+
end Lean.IR
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/NormIds.lean
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2019 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Compiler.IR.Basic
|
| 8 |
+
|
| 9 |
+
namespace Lean.IR.UniqueIds
|
| 10 |
+
|
| 11 |
+
abbrev M := StateT IndexSet Id
|
| 12 |
+
|
| 13 |
+
def checkId (id : Index) : M Bool :=
|
| 14 |
+
modifyGet fun s =>
|
| 15 |
+
if s.contains id then (false, s)
|
| 16 |
+
else (true, s.insert id)
|
| 17 |
+
|
| 18 |
+
def checkParams (ps : Array Param) : M Bool :=
|
| 19 |
+
ps.allM fun p => checkId p.x.idx
|
| 20 |
+
|
| 21 |
+
partial def checkFnBody : FnBody → M Bool
|
| 22 |
+
| .vdecl x _ _ b => checkId x.idx <&&> checkFnBody b
|
| 23 |
+
| .jdecl j ys _ b => checkId j.idx <&&> checkParams ys <&&> checkFnBody b
|
| 24 |
+
| .case _ _ _ alts => alts.allM fun alt => checkFnBody alt.body
|
| 25 |
+
| b => if b.isTerminal then pure true else checkFnBody b.body
|
| 26 |
+
|
| 27 |
+
partial def checkDecl : Decl → M Bool
|
| 28 |
+
| .fdecl (xs := xs) (body := b) .. => checkParams xs <&&> checkFnBody b
|
| 29 |
+
| .extern (xs := xs) .. => checkParams xs
|
| 30 |
+
|
| 31 |
+
end UniqueIds
|
| 32 |
+
|
| 33 |
+
/-- Return true if variable, parameter and join point ids are unique -/
|
| 34 |
+
def Decl.uniqueIds (d : Decl) : Bool :=
|
| 35 |
+
(UniqueIds.checkDecl d).run' {}
|
| 36 |
+
|
| 37 |
+
namespace NormalizeIds
|
| 38 |
+
|
| 39 |
+
abbrev M := ReaderT IndexRenaming Id
|
| 40 |
+
|
| 41 |
+
def normIndex (x : Index) : M Index := fun m =>
|
| 42 |
+
match m.find? x with
|
| 43 |
+
| some y => y
|
| 44 |
+
| none => x
|
| 45 |
+
|
| 46 |
+
def normVar (x : VarId) : M VarId :=
|
| 47 |
+
VarId.mk <$> normIndex x.idx
|
| 48 |
+
|
| 49 |
+
def normJP (x : JoinPointId) : M JoinPointId :=
|
| 50 |
+
JoinPointId.mk <$> normIndex x.idx
|
| 51 |
+
|
| 52 |
+
def normArg : Arg → M Arg
|
| 53 |
+
| Arg.var x => Arg.var <$> normVar x
|
| 54 |
+
| other => pure other
|
| 55 |
+
|
| 56 |
+
def normArgs (as : Array Arg) : M (Array Arg) := fun m =>
|
| 57 |
+
as.map fun a => normArg a m
|
| 58 |
+
|
| 59 |
+
def normExpr : Expr → M Expr
|
| 60 |
+
| Expr.ctor c ys, m => Expr.ctor c (normArgs ys m)
|
| 61 |
+
| Expr.reset n x, m => Expr.reset n (normVar x m)
|
| 62 |
+
| Expr.reuse x c u ys, m => Expr.reuse (normVar x m) c u (normArgs ys m)
|
| 63 |
+
| Expr.proj i x, m => Expr.proj i (normVar x m)
|
| 64 |
+
| Expr.uproj i x, m => Expr.uproj i (normVar x m)
|
| 65 |
+
| Expr.sproj n o x, m => Expr.sproj n o (normVar x m)
|
| 66 |
+
| Expr.fap c ys, m => Expr.fap c (normArgs ys m)
|
| 67 |
+
| Expr.pap c ys, m => Expr.pap c (normArgs ys m)
|
| 68 |
+
| Expr.ap x ys, m => Expr.ap (normVar x m) (normArgs ys m)
|
| 69 |
+
| Expr.box t x, m => Expr.box t (normVar x m)
|
| 70 |
+
| Expr.unbox x, m => Expr.unbox (normVar x m)
|
| 71 |
+
| Expr.isShared x, m => Expr.isShared (normVar x m)
|
| 72 |
+
| e@(Expr.lit _), _ => e
|
| 73 |
+
|
| 74 |
+
abbrev N := ReaderT IndexRenaming (StateM Nat)
|
| 75 |
+
|
| 76 |
+
@[inline] def withVar {α : Type} (x : VarId) (k : VarId → N α) : N α := fun m => do
|
| 77 |
+
let n ← getModify (fun n => n + 1)
|
| 78 |
+
k { idx := n } (m.insert x.idx n)
|
| 79 |
+
|
| 80 |
+
@[inline] def withJP {α : Type} (x : JoinPointId) (k : JoinPointId → N α) : N α := fun m => do
|
| 81 |
+
let n ← getModify (fun n => n + 1)
|
| 82 |
+
k { idx := n } (m.insert x.idx n)
|
| 83 |
+
|
| 84 |
+
@[inline] def withParams {α : Type} (ps : Array Param) (k : Array Param → N α) : N α := fun m => do
|
| 85 |
+
let m ← ps.foldlM (init := m) fun m p => do
|
| 86 |
+
let n ← getModify fun n => n + 1
|
| 87 |
+
return m.insert p.x.idx n
|
| 88 |
+
let ps := ps.map fun p => { p with x := normVar p.x m }
|
| 89 |
+
k ps m
|
| 90 |
+
|
| 91 |
+
instance : MonadLift M N :=
|
| 92 |
+
⟨fun x m => return x m⟩
|
| 93 |
+
|
| 94 |
+
partial def normFnBody : FnBody → N FnBody
|
| 95 |
+
| FnBody.vdecl x t v b => do let v ← normExpr v; withVar x fun x => return FnBody.vdecl x t v (← normFnBody b)
|
| 96 |
+
| FnBody.jdecl j ys v b => do
|
| 97 |
+
let (ys, v) ← withParams ys fun ys => do let v ← normFnBody v; pure (ys, v)
|
| 98 |
+
withJP j fun j => return FnBody.jdecl j ys v (← normFnBody b)
|
| 99 |
+
| FnBody.set x i y b => return FnBody.set (← normVar x) i (← normArg y) (← normFnBody b)
|
| 100 |
+
| FnBody.uset x i y b => return FnBody.uset (← normVar x) i (← normVar y) (← normFnBody b)
|
| 101 |
+
| FnBody.sset x i o y t b => return FnBody.sset (← normVar x) i o (← normVar y) t (← normFnBody b)
|
| 102 |
+
| FnBody.setTag x i b => return FnBody.setTag (← normVar x) i (← normFnBody b)
|
| 103 |
+
| FnBody.inc x n c p b => return FnBody.inc (← normVar x) n c p (← normFnBody b)
|
| 104 |
+
| FnBody.dec x n c p b => return FnBody.dec (← normVar x) n c p (← normFnBody b)
|
| 105 |
+
| FnBody.del x b => return FnBody.del (← normVar x) (← normFnBody b)
|
| 106 |
+
| FnBody.mdata d b => return FnBody.mdata d (← normFnBody b)
|
| 107 |
+
| FnBody.case tid x xType alts => do
|
| 108 |
+
let x ← normVar x
|
| 109 |
+
let alts ← alts.mapM fun alt => alt.mmodifyBody normFnBody
|
| 110 |
+
return FnBody.case tid x xType alts
|
| 111 |
+
| FnBody.jmp j ys => return FnBody.jmp (← normJP j) (← normArgs ys)
|
| 112 |
+
| FnBody.ret x => return FnBody.ret (← normArg x)
|
| 113 |
+
| FnBody.unreachable => pure FnBody.unreachable
|
| 114 |
+
|
| 115 |
+
def normDecl (d : Decl) : N Decl :=
|
| 116 |
+
match d with
|
| 117 |
+
| Decl.fdecl (xs := xs) (body := b) .. => withParams xs fun _ => return d.updateBody! (← normFnBody b)
|
| 118 |
+
| other => pure other
|
| 119 |
+
|
| 120 |
+
end NormalizeIds
|
| 121 |
+
|
| 122 |
+
/-- Create a declaration equivalent to `d` s.t. `d.normalizeIds.uniqueIds == true` -/
|
| 123 |
+
def Decl.normalizeIds (d : Decl) : Decl :=
|
| 124 |
+
(NormalizeIds.normDecl d {}).run' 1
|
| 125 |
+
|
| 126 |
+
/-! Apply a function `f : VarId → VarId` to variable occurrences.
|
| 127 |
+
The following functions assume the IR code does not have variable shadowing. -/
|
| 128 |
+
namespace MapVars
|
| 129 |
+
|
| 130 |
+
@[inline] def mapArg (f : VarId → VarId) : Arg → Arg
|
| 131 |
+
| Arg.var x => Arg.var (f x)
|
| 132 |
+
| a => a
|
| 133 |
+
|
| 134 |
+
def mapArgs (f : VarId → VarId) (as : Array Arg) : Array Arg :=
|
| 135 |
+
as.map (mapArg f)
|
| 136 |
+
|
| 137 |
+
def mapExpr (f : VarId → VarId) : Expr → Expr
|
| 138 |
+
| Expr.ctor c ys => Expr.ctor c (mapArgs f ys)
|
| 139 |
+
| Expr.reset n x => Expr.reset n (f x)
|
| 140 |
+
| Expr.reuse x c u ys => Expr.reuse (f x) c u (mapArgs f ys)
|
| 141 |
+
| Expr.proj i x => Expr.proj i (f x)
|
| 142 |
+
| Expr.uproj i x => Expr.uproj i (f x)
|
| 143 |
+
| Expr.sproj n o x => Expr.sproj n o (f x)
|
| 144 |
+
| Expr.fap c ys => Expr.fap c (mapArgs f ys)
|
| 145 |
+
| Expr.pap c ys => Expr.pap c (mapArgs f ys)
|
| 146 |
+
| Expr.ap x ys => Expr.ap (f x) (mapArgs f ys)
|
| 147 |
+
| Expr.box t x => Expr.box t (f x)
|
| 148 |
+
| Expr.unbox x => Expr.unbox (f x)
|
| 149 |
+
| Expr.isShared x => Expr.isShared (f x)
|
| 150 |
+
| e@(Expr.lit _) => e
|
| 151 |
+
|
| 152 |
+
partial def mapFnBody (f : VarId → VarId) : FnBody → FnBody
|
| 153 |
+
| FnBody.vdecl x t v b => FnBody.vdecl x t (mapExpr f v) (mapFnBody f b)
|
| 154 |
+
| FnBody.jdecl j ys v b => FnBody.jdecl j ys (mapFnBody f v) (mapFnBody f b)
|
| 155 |
+
| FnBody.set x i y b => FnBody.set (f x) i (mapArg f y) (mapFnBody f b)
|
| 156 |
+
| FnBody.setTag x i b => FnBody.setTag (f x) i (mapFnBody f b)
|
| 157 |
+
| FnBody.uset x i y b => FnBody.uset (f x) i (f y) (mapFnBody f b)
|
| 158 |
+
| FnBody.sset x i o y t b => FnBody.sset (f x) i o (f y) t (mapFnBody f b)
|
| 159 |
+
| FnBody.inc x n c p b => FnBody.inc (f x) n c p (mapFnBody f b)
|
| 160 |
+
| FnBody.dec x n c p b => FnBody.dec (f x) n c p (mapFnBody f b)
|
| 161 |
+
| FnBody.del x b => FnBody.del (f x) (mapFnBody f b)
|
| 162 |
+
| FnBody.mdata d b => FnBody.mdata d (mapFnBody f b)
|
| 163 |
+
| FnBody.case tid x xType alts => FnBody.case tid (f x) xType (alts.map fun alt => alt.modifyBody (mapFnBody f))
|
| 164 |
+
| FnBody.jmp j ys => FnBody.jmp j (mapArgs f ys)
|
| 165 |
+
| FnBody.ret x => FnBody.ret (mapArg f x)
|
| 166 |
+
| FnBody.unreachable => FnBody.unreachable
|
| 167 |
+
|
| 168 |
+
end MapVars
|
| 169 |
+
|
| 170 |
+
@[inline] def FnBody.mapVars (f : VarId → VarId) (b : FnBody) : FnBody :=
|
| 171 |
+
MapVars.mapFnBody f b
|
| 172 |
+
|
| 173 |
+
/-- Replace `x` with `y` in `b`. This function assumes `b` does not shadow `x` -/
|
| 174 |
+
def FnBody.replaceVar (x y : VarId) (b : FnBody) : FnBody :=
|
| 175 |
+
b.mapVars fun z => if x == z then y else z
|
| 176 |
+
|
| 177 |
+
end Lean.IR
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/PushProj.lean
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2019 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Compiler.IR.Basic
|
| 8 |
+
import Lean.Compiler.IR.FreeVars
|
| 9 |
+
import Lean.Compiler.IR.NormIds
|
| 10 |
+
|
| 11 |
+
namespace Lean.IR
|
| 12 |
+
|
| 13 |
+
partial def pushProjs (bs : Array FnBody) (alts : Array Alt) (altsF : Array IndexSet) (ctx : Array FnBody) (ctxF : IndexSet) : Array FnBody × Array Alt :=
|
| 14 |
+
if bs.isEmpty then (ctx.reverse, alts)
|
| 15 |
+
else
|
| 16 |
+
let b := bs.back!
|
| 17 |
+
let bs := bs.pop
|
| 18 |
+
let done (_ : Unit) := (bs.push b ++ ctx.reverse, alts)
|
| 19 |
+
let skip (_ : Unit) := pushProjs bs alts altsF (ctx.push b) (b.collectFreeIndices ctxF)
|
| 20 |
+
let push (x : VarId) :=
|
| 21 |
+
if !ctxF.contains x.idx then
|
| 22 |
+
let alts := alts.mapIdx fun i alt => alt.modifyBody fun b' =>
|
| 23 |
+
if altsF[i]!.contains x.idx then b.setBody b'
|
| 24 |
+
else b'
|
| 25 |
+
let altsF := altsF.map fun s => if s.contains x.idx then b.collectFreeIndices s else s
|
| 26 |
+
pushProjs bs alts altsF ctx ctxF
|
| 27 |
+
else
|
| 28 |
+
skip ()
|
| 29 |
+
match b with
|
| 30 |
+
| FnBody.vdecl x _ v _ =>
|
| 31 |
+
match v with
|
| 32 |
+
| Expr.proj _ _ => push x
|
| 33 |
+
| Expr.uproj _ _ => push x
|
| 34 |
+
| Expr.sproj _ _ _ => push x
|
| 35 |
+
| Expr.isShared _ => skip ()
|
| 36 |
+
| _ => done ()
|
| 37 |
+
| _ => done ()
|
| 38 |
+
|
| 39 |
+
partial def FnBody.pushProj (b : FnBody) : FnBody :=
|
| 40 |
+
let (bs, term) := b.flatten
|
| 41 |
+
let bs := modifyJPs bs pushProj
|
| 42 |
+
match term with
|
| 43 |
+
| .case tid x xType alts =>
|
| 44 |
+
let altsF := alts.map fun alt => alt.body.freeIndices
|
| 45 |
+
let (bs, alts) := pushProjs bs alts altsF #[] (mkIndexSet x.idx)
|
| 46 |
+
let alts := alts.map fun alt => alt.modifyBody pushProj
|
| 47 |
+
let term := FnBody.case tid x xType alts
|
| 48 |
+
reshape bs term
|
| 49 |
+
| _ => reshape bs term
|
| 50 |
+
|
| 51 |
+
/-- Push projections inside `case` branches. -/
|
| 52 |
+
def Decl.pushProj (d : Decl) : Decl :=
|
| 53 |
+
match d with
|
| 54 |
+
| .fdecl (body := b) .. => d.updateBody! b.pushProj |>.normalizeIds
|
| 55 |
+
| other => other
|
| 56 |
+
|
| 57 |
+
end Lean.IR
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/RC.lean
ADDED
|
@@ -0,0 +1,285 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2019 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Runtime
|
| 8 |
+
import Lean.Compiler.IR.CompilerM
|
| 9 |
+
import Lean.Compiler.IR.LiveVars
|
| 10 |
+
|
| 11 |
+
namespace Lean.IR.ExplicitRC
|
| 12 |
+
/-!
|
| 13 |
+
Insert explicit RC instructions. So, it assumes the input code does not contain `inc` nor `dec` instructions.
|
| 14 |
+
This transformation is applied before lower level optimizations
|
| 15 |
+
that introduce the instructions `release` and `set`
|
| 16 |
+
-/
|
| 17 |
+
|
| 18 |
+
structure VarInfo where
|
| 19 |
+
ref : Bool := true -- true if the variable may be a reference (aka pointer) at runtime
|
| 20 |
+
persistent : Bool := false -- true if the variable is statically known to be marked a Persistent at runtime
|
| 21 |
+
consume : Bool := false -- true if the variable RC must be "consumed"
|
| 22 |
+
deriving Inhabited
|
| 23 |
+
|
| 24 |
+
abbrev VarMap := RBMap VarId VarInfo (fun x y => compare x.idx y.idx)
|
| 25 |
+
|
| 26 |
+
structure Context where
|
| 27 |
+
env : Environment
|
| 28 |
+
decls : Array Decl
|
| 29 |
+
varMap : VarMap := {}
|
| 30 |
+
jpLiveVarMap : JPLiveVarMap := {} -- map: join point => live variables
|
| 31 |
+
localCtx : LocalContext := {} -- we use it to store the join point declarations
|
| 32 |
+
|
| 33 |
+
def getDecl (ctx : Context) (fid : FunId) : Decl :=
|
| 34 |
+
match findEnvDecl' ctx.env fid ctx.decls with
|
| 35 |
+
| some decl => decl
|
| 36 |
+
| none => unreachable!
|
| 37 |
+
|
| 38 |
+
def getVarInfo (ctx : Context) (x : VarId) : VarInfo :=
|
| 39 |
+
match ctx.varMap.find? x with
|
| 40 |
+
| some info => info
|
| 41 |
+
| none => unreachable!
|
| 42 |
+
|
| 43 |
+
def getJPParams (ctx : Context) (j : JoinPointId) : Array Param :=
|
| 44 |
+
match ctx.localCtx.getJPParams j with
|
| 45 |
+
| some ps => ps
|
| 46 |
+
| none => unreachable!
|
| 47 |
+
|
| 48 |
+
def getJPLiveVars (ctx : Context) (j : JoinPointId) : LiveVarSet :=
|
| 49 |
+
match ctx.jpLiveVarMap.find? j with
|
| 50 |
+
| some s => s
|
| 51 |
+
| none => {}
|
| 52 |
+
|
| 53 |
+
def mustConsume (ctx : Context) (x : VarId) : Bool :=
|
| 54 |
+
let info := getVarInfo ctx x
|
| 55 |
+
info.ref && info.consume
|
| 56 |
+
|
| 57 |
+
@[inline] def addInc (ctx : Context) (x : VarId) (b : FnBody) (n := 1) : FnBody :=
|
| 58 |
+
let info := getVarInfo ctx x
|
| 59 |
+
if n == 0 then b else FnBody.inc x n true info.persistent b
|
| 60 |
+
|
| 61 |
+
@[inline] def addDec (ctx : Context) (x : VarId) (b : FnBody) : FnBody :=
|
| 62 |
+
let info := getVarInfo ctx x
|
| 63 |
+
FnBody.dec x 1 true info.persistent b
|
| 64 |
+
|
| 65 |
+
private def updateRefUsingCtorInfo (ctx : Context) (x : VarId) (c : CtorInfo) : Context :=
|
| 66 |
+
if c.isRef then
|
| 67 |
+
ctx
|
| 68 |
+
else
|
| 69 |
+
let m := ctx.varMap
|
| 70 |
+
{ ctx with
|
| 71 |
+
varMap := match m.find? x with
|
| 72 |
+
| some info => m.insert x { info with ref := false } -- I really want a Lenses library + notation
|
| 73 |
+
| none => m }
|
| 74 |
+
|
| 75 |
+
private def addDecForAlt (ctx : Context) (caseLiveVars altLiveVars : LiveVarSet) (b : FnBody) : FnBody :=
|
| 76 |
+
caseLiveVars.fold (init := b) fun b x =>
|
| 77 |
+
if !altLiveVars.contains x && mustConsume ctx x then addDec ctx x b else b
|
| 78 |
+
|
| 79 |
+
/-- `isFirstOcc xs x i = true` if `xs[i]` is the first occurrence of `xs[i]` in `xs` -/
|
| 80 |
+
private def isFirstOcc (xs : Array Arg) (i : Nat) : Bool :=
|
| 81 |
+
let x := xs[i]!
|
| 82 |
+
i.all fun j _ => xs[j]! != x
|
| 83 |
+
|
| 84 |
+
/-- Return true if `x` also occurs in `ys` in a position that is not consumed.
|
| 85 |
+
That is, it is also passed as a borrow reference. -/
|
| 86 |
+
private def isBorrowParamAux (x : VarId) (ys : Array Arg) (consumeParamPred : Nat → Bool) : Bool :=
|
| 87 |
+
ys.size.any fun i _ =>
|
| 88 |
+
let y := ys[i]
|
| 89 |
+
match y with
|
| 90 |
+
| Arg.irrelevant => false
|
| 91 |
+
| Arg.var y => x == y && !consumeParamPred i
|
| 92 |
+
|
| 93 |
+
private def isBorrowParam (x : VarId) (ys : Array Arg) (ps : Array Param) : Bool :=
|
| 94 |
+
isBorrowParamAux x ys fun i => ! ps[i]!.borrow
|
| 95 |
+
|
| 96 |
+
/--
|
| 97 |
+
Return `n`, the number of times `x` is consumed.
|
| 98 |
+
- `ys` is a sequence of instruction parameters where we search for `x`.
|
| 99 |
+
- `consumeParamPred i = true` if parameter `i` is consumed.
|
| 100 |
+
-/
|
| 101 |
+
private def getNumConsumptions (x : VarId) (ys : Array Arg) (consumeParamPred : Nat → Bool) : Nat :=
|
| 102 |
+
ys.size.fold (init := 0) fun i _ n =>
|
| 103 |
+
let y := ys[i]
|
| 104 |
+
match y with
|
| 105 |
+
| Arg.irrelevant => n
|
| 106 |
+
| Arg.var y => if x == y && consumeParamPred i then n+1 else n
|
| 107 |
+
|
| 108 |
+
private def addIncBeforeAux (ctx : Context) (xs : Array Arg) (consumeParamPred : Nat → Bool) (b : FnBody) (liveVarsAfter : LiveVarSet) : FnBody :=
|
| 109 |
+
xs.size.fold (init := b) fun i _ b =>
|
| 110 |
+
let x := xs[i]
|
| 111 |
+
match x with
|
| 112 |
+
| Arg.irrelevant => b
|
| 113 |
+
| Arg.var x =>
|
| 114 |
+
let info := getVarInfo ctx x
|
| 115 |
+
if !info.ref || !isFirstOcc xs i then b
|
| 116 |
+
else
|
| 117 |
+
let numConsuptions := getNumConsumptions x xs consumeParamPred -- number of times the argument is
|
| 118 |
+
let numIncs :=
|
| 119 |
+
if !info.consume || -- `x` is not a variable that must be consumed by the current procedure
|
| 120 |
+
liveVarsAfter.contains x || -- `x` is live after executing instruction
|
| 121 |
+
isBorrowParamAux x xs consumeParamPred -- `x` is used in a position that is passed as a borrow reference
|
| 122 |
+
then numConsuptions
|
| 123 |
+
else numConsuptions - 1
|
| 124 |
+
addInc ctx x b numIncs
|
| 125 |
+
|
| 126 |
+
private def addIncBefore (ctx : Context) (xs : Array Arg) (ps : Array Param) (b : FnBody) (liveVarsAfter : LiveVarSet) : FnBody :=
|
| 127 |
+
addIncBeforeAux ctx xs (fun i => ! ps[i]!.borrow) b liveVarsAfter
|
| 128 |
+
|
| 129 |
+
/-- See `addIncBeforeAux`/`addIncBefore` for the procedure that inserts `inc` operations before an application. -/
|
| 130 |
+
private def addDecAfterFullApp (ctx : Context) (xs : Array Arg) (ps : Array Param) (b : FnBody) (bLiveVars : LiveVarSet) : FnBody :=
|
| 131 |
+
xs.size.fold (init := b) fun i _ b =>
|
| 132 |
+
match xs[i] with
|
| 133 |
+
| Arg.irrelevant => b
|
| 134 |
+
| Arg.var x =>
|
| 135 |
+
/- We must add a `dec` if `x` must be consumed, it is alive after the application,
|
| 136 |
+
and it has been borrowed by the application.
|
| 137 |
+
Remark: `x` may occur multiple times in the application (e.g., `f x y x`).
|
| 138 |
+
This is why we check whether it is the first occurrence. -/
|
| 139 |
+
if mustConsume ctx x && isFirstOcc xs i && isBorrowParam x xs ps && !bLiveVars.contains x then
|
| 140 |
+
addDec ctx x b
|
| 141 |
+
else b
|
| 142 |
+
|
| 143 |
+
private def addIncBeforeConsumeAll (ctx : Context) (xs : Array Arg) (b : FnBody) (liveVarsAfter : LiveVarSet) : FnBody :=
|
| 144 |
+
addIncBeforeAux ctx xs (fun _ => true) b liveVarsAfter
|
| 145 |
+
|
| 146 |
+
/-- Add `dec` instructions for parameters that are references, are not alive in `b`, and are not borrow.
|
| 147 |
+
That is, we must make sure these parameters are consumed. -/
|
| 148 |
+
private def addDecForDeadParams (ctx : Context) (ps : Array Param) (b : FnBody) (bLiveVars : LiveVarSet) : FnBody :=
|
| 149 |
+
ps.foldl (init := b) fun b p =>
|
| 150 |
+
if !p.borrow && p.ty.isObj && !bLiveVars.contains p.x then addDec ctx p.x b else b
|
| 151 |
+
|
| 152 |
+
private def isPersistent : Expr → Bool
|
| 153 |
+
| Expr.fap _ xs => xs.isEmpty -- all global constants are persistent objects
|
| 154 |
+
| _ => false
|
| 155 |
+
|
| 156 |
+
/-- We do not need to consume the projection of a variable that is not consumed -/
|
| 157 |
+
private def consumeExpr (m : VarMap) : Expr → Bool
|
| 158 |
+
| Expr.proj _ x => match m.find? x with
|
| 159 |
+
| some info => info.consume
|
| 160 |
+
| none => true
|
| 161 |
+
| _ => true
|
| 162 |
+
|
| 163 |
+
/-- Return true iff `v` at runtime is a scalar value stored in a tagged pointer.
|
| 164 |
+
We do not need RC operations for this kind of value. -/
|
| 165 |
+
private def isScalarBoxedInTaggedPtr (v : Expr) : Bool :=
|
| 166 |
+
match v with
|
| 167 |
+
| Expr.ctor c _ => c.size == 0 && c.ssize == 0 && c.usize == 0
|
| 168 |
+
| Expr.lit (LitVal.num n) => n ≤ maxSmallNat
|
| 169 |
+
| _ => false
|
| 170 |
+
|
| 171 |
+
private def updateVarInfo (ctx : Context) (x : VarId) (t : IRType) (v : Expr) : Context :=
|
| 172 |
+
{ ctx with
|
| 173 |
+
varMap := ctx.varMap.insert x {
|
| 174 |
+
ref := t.isObj && !isScalarBoxedInTaggedPtr v,
|
| 175 |
+
persistent := isPersistent v,
|
| 176 |
+
consume := consumeExpr ctx.varMap v
|
| 177 |
+
}
|
| 178 |
+
}
|
| 179 |
+
|
| 180 |
+
private def addDecIfNeeded (ctx : Context) (x : VarId) (b : FnBody) (bLiveVars : LiveVarSet) : FnBody :=
|
| 181 |
+
if mustConsume ctx x && !bLiveVars.contains x then addDec ctx x b else b
|
| 182 |
+
|
| 183 |
+
private def processVDecl (ctx : Context) (z : VarId) (t : IRType) (v : Expr) (b : FnBody) (bLiveVars : LiveVarSet) : FnBody × LiveVarSet :=
|
| 184 |
+
let b := match v with
|
| 185 |
+
| (Expr.ctor _ ys) => addIncBeforeConsumeAll ctx ys (FnBody.vdecl z t v b) bLiveVars
|
| 186 |
+
| (Expr.reuse _ _ _ ys) => addIncBeforeConsumeAll ctx ys (FnBody.vdecl z t v b) bLiveVars
|
| 187 |
+
| (Expr.proj _ x) =>
|
| 188 |
+
let b := addDecIfNeeded ctx x b bLiveVars
|
| 189 |
+
let b := if (getVarInfo ctx x).consume then addInc ctx z b else b
|
| 190 |
+
(FnBody.vdecl z t v b)
|
| 191 |
+
| (Expr.uproj _ x) => FnBody.vdecl z t v (addDecIfNeeded ctx x b bLiveVars)
|
| 192 |
+
| (Expr.sproj _ _ x) => FnBody.vdecl z t v (addDecIfNeeded ctx x b bLiveVars)
|
| 193 |
+
| (Expr.fap f ys) =>
|
| 194 |
+
let ps := (getDecl ctx f).params
|
| 195 |
+
let b := addDecAfterFullApp ctx ys ps b bLiveVars
|
| 196 |
+
let b := FnBody.vdecl z t v b
|
| 197 |
+
addIncBefore ctx ys ps b bLiveVars
|
| 198 |
+
| (Expr.pap _ ys) => addIncBeforeConsumeAll ctx ys (FnBody.vdecl z t v b) bLiveVars
|
| 199 |
+
| (Expr.ap x ys) =>
|
| 200 |
+
let ysx := ys.push (Arg.var x) -- TODO: avoid temporary array allocation
|
| 201 |
+
addIncBeforeConsumeAll ctx ysx (FnBody.vdecl z t v b) bLiveVars
|
| 202 |
+
| (Expr.unbox x) => FnBody.vdecl z t v (addDecIfNeeded ctx x b bLiveVars)
|
| 203 |
+
| _ => FnBody.vdecl z t v b -- Expr.reset, Expr.box, Expr.lit are handled here
|
| 204 |
+
let liveVars := updateLiveVars v bLiveVars
|
| 205 |
+
let liveVars := liveVars.erase z
|
| 206 |
+
(b, liveVars)
|
| 207 |
+
|
| 208 |
+
def updateVarInfoWithParams (ctx : Context) (ps : Array Param) : Context :=
|
| 209 |
+
let m := ps.foldl (init := ctx.varMap) fun m p =>
|
| 210 |
+
m.insert p.x { ref := p.ty.isObj, consume := !p.borrow }
|
| 211 |
+
{ ctx with varMap := m }
|
| 212 |
+
|
| 213 |
+
partial def visitFnBody : FnBody → Context → (FnBody × LiveVarSet)
|
| 214 |
+
| FnBody.vdecl x t v b, ctx =>
|
| 215 |
+
let ctx := updateVarInfo ctx x t v
|
| 216 |
+
let (b, bLiveVars) := visitFnBody b ctx
|
| 217 |
+
processVDecl ctx x t v b bLiveVars
|
| 218 |
+
| FnBody.jdecl j xs v b, ctx =>
|
| 219 |
+
let ctxAtV := updateVarInfoWithParams ctx xs
|
| 220 |
+
let (v, vLiveVars) := visitFnBody v ctxAtV
|
| 221 |
+
let v := addDecForDeadParams ctxAtV xs v vLiveVars
|
| 222 |
+
let ctx := { ctx with
|
| 223 |
+
localCtx := ctx.localCtx.addJP j xs v
|
| 224 |
+
jpLiveVarMap := updateJPLiveVarMap j xs v ctx.jpLiveVarMap
|
| 225 |
+
}
|
| 226 |
+
let (b, bLiveVars) := visitFnBody b ctx
|
| 227 |
+
(FnBody.jdecl j xs v b, bLiveVars)
|
| 228 |
+
| FnBody.uset x i y b, ctx =>
|
| 229 |
+
let (b, s) := visitFnBody b ctx
|
| 230 |
+
-- We don't need to insert `y` since we only need to track live variables that are references at runtime
|
| 231 |
+
let s := s.insert x
|
| 232 |
+
(FnBody.uset x i y b, s)
|
| 233 |
+
| FnBody.sset x i o y t b, ctx =>
|
| 234 |
+
let (b, s) := visitFnBody b ctx
|
| 235 |
+
-- We don't need to insert `y` since we only need to track live variables that are references at runtime
|
| 236 |
+
let s := s.insert x
|
| 237 |
+
(FnBody.sset x i o y t b, s)
|
| 238 |
+
| FnBody.mdata m b, ctx =>
|
| 239 |
+
let (b, s) := visitFnBody b ctx
|
| 240 |
+
(FnBody.mdata m b, s)
|
| 241 |
+
| b@(FnBody.case tid x xType alts), ctx =>
|
| 242 |
+
let caseLiveVars := collectLiveVars b ctx.jpLiveVarMap
|
| 243 |
+
let alts := alts.map fun alt => match alt with
|
| 244 |
+
| Alt.ctor c b =>
|
| 245 |
+
let ctx := updateRefUsingCtorInfo ctx x c
|
| 246 |
+
let (b, altLiveVars) := visitFnBody b ctx
|
| 247 |
+
let b := addDecForAlt ctx caseLiveVars altLiveVars b
|
| 248 |
+
Alt.ctor c b
|
| 249 |
+
| Alt.default b =>
|
| 250 |
+
let (b, altLiveVars) := visitFnBody b ctx
|
| 251 |
+
let b := addDecForAlt ctx caseLiveVars altLiveVars b
|
| 252 |
+
Alt.default b
|
| 253 |
+
(FnBody.case tid x xType alts, caseLiveVars)
|
| 254 |
+
| b@(FnBody.ret x), ctx =>
|
| 255 |
+
match x with
|
| 256 |
+
| Arg.var x =>
|
| 257 |
+
let info := getVarInfo ctx x
|
| 258 |
+
if info.ref && !info.consume then (addInc ctx x b, mkLiveVarSet x) else (b, mkLiveVarSet x)
|
| 259 |
+
| _ => (b, {})
|
| 260 |
+
| b@(FnBody.jmp j xs), ctx =>
|
| 261 |
+
let jLiveVars := getJPLiveVars ctx j
|
| 262 |
+
let ps := getJPParams ctx j
|
| 263 |
+
let b := addIncBefore ctx xs ps b jLiveVars
|
| 264 |
+
let bLiveVars := collectLiveVars b ctx.jpLiveVarMap
|
| 265 |
+
(b, bLiveVars)
|
| 266 |
+
| FnBody.unreachable, _ => (FnBody.unreachable, {})
|
| 267 |
+
| other, _ => (other, {}) -- unreachable if well-formed
|
| 268 |
+
|
| 269 |
+
partial def visitDecl (env : Environment) (decls : Array Decl) (d : Decl) : Decl :=
|
| 270 |
+
match d with
|
| 271 |
+
| .fdecl (xs := xs) (body := b) .. =>
|
| 272 |
+
let ctx : Context := { env := env, decls := decls }
|
| 273 |
+
let ctx := updateVarInfoWithParams ctx xs
|
| 274 |
+
let (b, bLiveVars) := visitFnBody b ctx
|
| 275 |
+
let b := addDecForDeadParams ctx xs b bLiveVars
|
| 276 |
+
d.updateBody! b
|
| 277 |
+
| other => other
|
| 278 |
+
|
| 279 |
+
end ExplicitRC
|
| 280 |
+
|
| 281 |
+
def explicitRC (decls : Array Decl) : CompilerM (Array Decl) := do
|
| 282 |
+
let env ← getEnv
|
| 283 |
+
return decls.map (ExplicitRC.visitDecl env decls)
|
| 284 |
+
|
| 285 |
+
end Lean.IR
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/ResetReuse.lean
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2019 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Compiler.IR.Basic
|
| 8 |
+
import Lean.Compiler.IR.LiveVars
|
| 9 |
+
import Lean.Compiler.IR.Format
|
| 10 |
+
|
| 11 |
+
namespace Lean.IR.ResetReuse
|
| 12 |
+
/-!
|
| 13 |
+
Remark: the insertResetReuse transformation is applied before we have
|
| 14 |
+
inserted `inc/dec` instructions, and performed lower level optimizations
|
| 15 |
+
that introduce the instructions `release` and `set`.
|
| 16 |
+
-/
|
| 17 |
+
|
| 18 |
+
/-!
|
| 19 |
+
Remark: the functions `S`, `D` and `R` defined here implement the
|
| 20 |
+
corresponding functions in the paper "Counting Immutable Beans"
|
| 21 |
+
|
| 22 |
+
Here are the main differences:
|
| 23 |
+
- We use the State monad to manage the generation of fresh variable names.
|
| 24 |
+
- Support for join points, and `uset` and `sset` instructions for unboxed data.
|
| 25 |
+
- `D` uses the auxiliary function `Dmain`.
|
| 26 |
+
- `Dmain` returns a pair `(b, found)` to avoid quadratic behavior when checking
|
| 27 |
+
the last occurrence of the variable `x`.
|
| 28 |
+
- Because we have join points in the actual implementation, a variable may be live even if it
|
| 29 |
+
does not occur in a function body. See example at `livevars.lean`.
|
| 30 |
+
-/
|
| 31 |
+
|
| 32 |
+
private def mayReuse (c₁ c₂ : CtorInfo) (relaxedReuse : Bool) : Bool :=
|
| 33 |
+
c₁.size == c₂.size && c₁.usize == c₂.usize && c₁.ssize == c₂.ssize &&
|
| 34 |
+
/- The following condition is a heuristic.
|
| 35 |
+
If `relaxedReuse := false`, then we don't want to reuse cells from
|
| 36 |
+
different constructors even when they are compatible
|
| 37 |
+
because it produces counterintuitive behavior. -/
|
| 38 |
+
(relaxedReuse || c₁.name.getPrefix == c₂.name.getPrefix)
|
| 39 |
+
|
| 40 |
+
/--
|
| 41 |
+
Replace `ctor` applications with `reuse` applications if compatible.
|
| 42 |
+
`w` contains the "memory cell" being reused.
|
| 43 |
+
-/
|
| 44 |
+
private partial def S (w : VarId) (c : CtorInfo) (relaxedReuse : Bool) (b : FnBody) : FnBody :=
|
| 45 |
+
go b
|
| 46 |
+
where
|
| 47 |
+
go : FnBody → FnBody
|
| 48 |
+
| .vdecl x t v@(.ctor c' ys) b =>
|
| 49 |
+
if mayReuse c c' relaxedReuse then
|
| 50 |
+
let updtCidx := c.cidx != c'.cidx
|
| 51 |
+
.vdecl x t (.reuse w c' updtCidx ys) b
|
| 52 |
+
else
|
| 53 |
+
.vdecl x t v (go b)
|
| 54 |
+
| .jdecl j ys v b =>
|
| 55 |
+
let v' := go v
|
| 56 |
+
if v == v' then
|
| 57 |
+
.jdecl j ys v (go b)
|
| 58 |
+
else
|
| 59 |
+
.jdecl j ys v' b
|
| 60 |
+
| .case tid x xType alts =>
|
| 61 |
+
.case tid x xType <| alts.map fun alt => alt.modifyBody go
|
| 62 |
+
| b =>
|
| 63 |
+
if b.isTerminal then
|
| 64 |
+
b
|
| 65 |
+
else
|
| 66 |
+
let (instr, b) := b.split
|
| 67 |
+
instr.setBody (go b)
|
| 68 |
+
|
| 69 |
+
structure Context where
|
| 70 |
+
lctx : LocalContext := {}
|
| 71 |
+
/--
|
| 72 |
+
Contains all variables in `cases` statements in the current path
|
| 73 |
+
and variables that are already in `reset` statements when we
|
| 74 |
+
invoke `R`.
|
| 75 |
+
|
| 76 |
+
We use this information to prevent double-reset in code such as
|
| 77 |
+
```
|
| 78 |
+
case x_i : obj of
|
| 79 |
+
Prod.mk →
|
| 80 |
+
case x_i : obj of
|
| 81 |
+
Prod.mk →
|
| 82 |
+
...
|
| 83 |
+
```
|
| 84 |
+
|
| 85 |
+
A variable can already be in a `reset` statement when we
|
| 86 |
+
invoke `R` because we execute it with and without `relaxedReuse`.
|
| 87 |
+
-/
|
| 88 |
+
alreadyFound : PHashSet VarId := {}
|
| 89 |
+
/--
|
| 90 |
+
If `relaxedReuse := true`, then allow memory cells from different
|
| 91 |
+
constructors to be reused. For example, we can reuse a `PSigma.mk`
|
| 92 |
+
to allocate a `Prod.mk`. To avoid counterintuitive behavior,
|
| 93 |
+
we first try `relaxedReuse := false`, and then `relaxedReuse := true`.
|
| 94 |
+
-/
|
| 95 |
+
relaxedReuse : Bool := false
|
| 96 |
+
|
| 97 |
+
/-- We use `Context` to track join points in scope. -/
|
| 98 |
+
abbrev M := ReaderT Context (StateT Index Id)
|
| 99 |
+
|
| 100 |
+
private def mkFresh : M VarId := do
|
| 101 |
+
let idx ← getModify fun n => n + 1
|
| 102 |
+
return { idx := idx }
|
| 103 |
+
|
| 104 |
+
/--
|
| 105 |
+
Helper function for applying `S`. We only introduce a `reset` if we managed
|
| 106 |
+
to replace a `ctor` with `reuse` in `b`.
|
| 107 |
+
-/
|
| 108 |
+
private def tryS (x : VarId) (c : CtorInfo) (b : FnBody) : M FnBody := do
|
| 109 |
+
let w ← mkFresh
|
| 110 |
+
let b' := S w c (← read).relaxedReuse b
|
| 111 |
+
if b == b' then
|
| 112 |
+
return b
|
| 113 |
+
else
|
| 114 |
+
return .vdecl w IRType.object (.reset c.size x) b'
|
| 115 |
+
|
| 116 |
+
private def Dfinalize (x : VarId) (c : CtorInfo) : FnBody × Bool → M FnBody
|
| 117 |
+
| (b, true) => return b
|
| 118 |
+
| (b, false) => tryS x c b
|
| 119 |
+
|
| 120 |
+
private def argsContainsVar (ys : Array Arg) (x : VarId) : Bool :=
|
| 121 |
+
ys.any fun arg => match arg with
|
| 122 |
+
| .var y => x == y
|
| 123 |
+
| _ => false
|
| 124 |
+
|
| 125 |
+
private def isCtorUsing (b : FnBody) (x : VarId) : Bool :=
|
| 126 |
+
match b with
|
| 127 |
+
| .vdecl _ _ (.ctor _ ys) _ => argsContainsVar ys x
|
| 128 |
+
| _ => false
|
| 129 |
+
|
| 130 |
+
/--
|
| 131 |
+
Given `Dmain b`, the resulting pair `(new_b, flag)` contains the new body `new_b`,
|
| 132 |
+
and `flag == true` if `x` is live in `b`.
|
| 133 |
+
|
| 134 |
+
Note that, in the function `D` defined in the paper, for each `let x := e; F`,
|
| 135 |
+
`D` checks whether `x` is live in `F` or not. This is great for clarity but it
|
| 136 |
+
is expensive: `O(n^2)` where `n` is the size of the function body. -/
|
| 137 |
+
private partial def Dmain (x : VarId) (c : CtorInfo) (e : FnBody) : M (FnBody × Bool) := do
|
| 138 |
+
match e with
|
| 139 |
+
| .case tid y yType alts =>
|
| 140 |
+
if e.hasLiveVar (← read).lctx x then
|
| 141 |
+
/- If `x` is live in `e`, we recursively process each branch. -/
|
| 142 |
+
let alts ← alts.mapM fun alt => alt.mmodifyBody fun b => Dmain x c b >>= Dfinalize x c
|
| 143 |
+
return (.case tid y yType alts, true)
|
| 144 |
+
else
|
| 145 |
+
return (e, false)
|
| 146 |
+
| .jdecl j ys v b =>
|
| 147 |
+
let (b, found) ← withReader (fun ctx => { ctx with lctx := ctx.lctx.addJP j ys v }) (Dmain x c b)
|
| 148 |
+
let (v, _ /- found' -/) ← Dmain x c v
|
| 149 |
+
/- If `found' == true`, then `Dmain b` must also have returned `(b, true)` since
|
| 150 |
+
we assume the IR does not have dead join points. So, if `x` is live in `j` (i.e., `v`),
|
| 151 |
+
then it must also live in `b` since `j` is reachable from `b` with a `jmp`.
|
| 152 |
+
On the other hand, `x` may be live in `b` but dead in `j` (i.e., `v`). -/
|
| 153 |
+
return (.jdecl j ys v b, found)
|
| 154 |
+
| e =>
|
| 155 |
+
if e.isTerminal then
|
| 156 |
+
return (e, e.hasLiveVar (← read).lctx x)
|
| 157 |
+
else do
|
| 158 |
+
let (instr, b) := e.split
|
| 159 |
+
if isCtorUsing instr x then
|
| 160 |
+
/- If the scrutinee `x` (the one that is providing memory) is being
|
| 161 |
+
stored in a constructor, then reuse will probably not be able to reuse memory at runtime.
|
| 162 |
+
It may work only if the new cell is consumed, but we ignore this case. -/
|
| 163 |
+
return (e, true)
|
| 164 |
+
else
|
| 165 |
+
let (b, found) ← Dmain x c b
|
| 166 |
+
/- Remark: it is fine to use `hasFreeVar` instead of `hasLiveVar`
|
| 167 |
+
since `instr` is not a `FnBody.jmp` (it is not a terminal) nor
|
| 168 |
+
it is a `FnBody.jdecl`. -/
|
| 169 |
+
if found || !instr.hasFreeVar x then
|
| 170 |
+
return (instr.setBody b, found)
|
| 171 |
+
else
|
| 172 |
+
let b ← tryS x c b
|
| 173 |
+
return (instr.setBody b, true)
|
| 174 |
+
|
| 175 |
+
private def D (x : VarId) (c : CtorInfo) (b : FnBody) : M FnBody :=
|
| 176 |
+
Dmain x c b >>= Dfinalize x c
|
| 177 |
+
|
| 178 |
+
partial def R (e : FnBody) : M FnBody := do
|
| 179 |
+
match e with
|
| 180 |
+
| .case tid x xType alts =>
|
| 181 |
+
let alreadyFound := (← read).alreadyFound.contains x
|
| 182 |
+
withReader (fun ctx => { ctx with alreadyFound := ctx.alreadyFound.insert x }) do
|
| 183 |
+
let alts ← alts.mapM fun alt => do
|
| 184 |
+
let alt ← alt.mmodifyBody R
|
| 185 |
+
match alt with
|
| 186 |
+
| .ctor c b =>
|
| 187 |
+
if c.isScalar || alreadyFound then
|
| 188 |
+
-- If `alreadyFound`, then we don't try to reuse memory cell to avoid
|
| 189 |
+
-- double reset.
|
| 190 |
+
return alt
|
| 191 |
+
else
|
| 192 |
+
.ctor c <$> D x c b
|
| 193 |
+
| .default _ => return alt
|
| 194 |
+
return .case tid x xType alts
|
| 195 |
+
| .jdecl j ys v b =>
|
| 196 |
+
let v ← R v
|
| 197 |
+
let b ← withReader (fun ctx => { ctx with lctx := ctx.lctx.addJP j ys v }) (R b)
|
| 198 |
+
return .jdecl j ys v b
|
| 199 |
+
| e =>
|
| 200 |
+
if e.isTerminal then
|
| 201 |
+
return e
|
| 202 |
+
else
|
| 203 |
+
let (instr, b) := e.split
|
| 204 |
+
let b ← R b
|
| 205 |
+
return instr.setBody b
|
| 206 |
+
|
| 207 |
+
abbrev N := StateT (PHashSet VarId) Id
|
| 208 |
+
|
| 209 |
+
partial def collectResets (e : FnBody) : N Unit := do
|
| 210 |
+
match e with
|
| 211 |
+
| .case _ _ _ alts => alts.forM fun alt => collectResets alt.body
|
| 212 |
+
| .jdecl _ _ v b => collectResets v; collectResets b
|
| 213 |
+
| .vdecl _ _ (.reset _ x) b => modify fun s => s.insert x; collectResets b
|
| 214 |
+
| e => unless e.isTerminal do
|
| 215 |
+
let (_, b) := e.split
|
| 216 |
+
collectResets b
|
| 217 |
+
|
| 218 |
+
end ResetReuse
|
| 219 |
+
open ResetReuse
|
| 220 |
+
|
| 221 |
+
|
| 222 |
+
def Decl.insertResetReuseCore (d : Decl) (relaxedReuse : Bool) : Decl :=
|
| 223 |
+
match d with
|
| 224 |
+
| .fdecl (body := b) .. =>
|
| 225 |
+
let nextIndex := d.maxIndex + 1
|
| 226 |
+
-- First time we execute `insertResetReuseCore`, `relaxedReuse := false`.
|
| 227 |
+
let alreadyFound : PHashSet VarId := if relaxedReuse then (collectResets b *> get).run' {} else {}
|
| 228 |
+
let bNew := R b { relaxedReuse, alreadyFound } |>.run' nextIndex
|
| 229 |
+
d.updateBody! bNew
|
| 230 |
+
| other => other
|
| 231 |
+
|
| 232 |
+
def Decl.insertResetReuse (d : Decl) : Decl :=
|
| 233 |
+
/-
|
| 234 |
+
We execute the reset/reuse algorithm twice. The first time, we only reuse memory cells
|
| 235 |
+
between identical constructor memory cells. That is, we do not reuse a `PSigma.mk` memory cell
|
| 236 |
+
when allocating a `Prod.mk` memory cell, even though they have the same layout. Recall
|
| 237 |
+
that the reset/reuse placement algorithm is a heuristic, and the first pass prevents reuses
|
| 238 |
+
that are unlikely to be useful at runtime. Then, we run the procedure again,
|
| 239 |
+
relaxing this restriction. If there are still opportunities for reuse, we will take advantage of them.
|
| 240 |
+
|
| 241 |
+
The second pass addresses issue #4089.
|
| 242 |
+
-/
|
| 243 |
+
d.insertResetReuseCore (relaxedReuse := false)
|
| 244 |
+
|>.insertResetReuseCore (relaxedReuse := true)
|
| 245 |
+
|
| 246 |
+
end Lean.IR
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/SimpCase.lean
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2019 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Compiler.IR.Basic
|
| 8 |
+
import Lean.Compiler.IR.Format
|
| 9 |
+
|
| 10 |
+
namespace Lean.IR
|
| 11 |
+
|
| 12 |
+
def ensureHasDefault (alts : Array Alt) : Array Alt :=
|
| 13 |
+
if alts.any Alt.isDefault then alts
|
| 14 |
+
else if alts.size < 2 then alts
|
| 15 |
+
else
|
| 16 |
+
let last := alts.back!
|
| 17 |
+
let alts := alts.pop
|
| 18 |
+
alts.push (Alt.default last.body)
|
| 19 |
+
|
| 20 |
+
private def getOccsOf (alts : Array Alt) (i : Nat) : Nat := Id.run do
|
| 21 |
+
let aBody := alts[i]!.body
|
| 22 |
+
let mut n := 1
|
| 23 |
+
for h : j in [i+1:alts.size] do
|
| 24 |
+
if alts[j].body == aBody then
|
| 25 |
+
n := n+1
|
| 26 |
+
return n
|
| 27 |
+
|
| 28 |
+
private def maxOccs (alts : Array Alt) : Alt × Nat := Id.run do
|
| 29 |
+
let mut maxAlt := alts[0]!
|
| 30 |
+
let mut max := getOccsOf alts 0
|
| 31 |
+
for h : i in [1:alts.size] do
|
| 32 |
+
let curr := getOccsOf alts i
|
| 33 |
+
if curr > max then
|
| 34 |
+
maxAlt := alts[i]
|
| 35 |
+
max := curr
|
| 36 |
+
return (maxAlt, max)
|
| 37 |
+
|
| 38 |
+
private def addDefault (alts : Array Alt) : Array Alt :=
|
| 39 |
+
if alts.size <= 1 || alts.any Alt.isDefault then alts
|
| 40 |
+
else
|
| 41 |
+
let (max, noccs) := maxOccs alts
|
| 42 |
+
if noccs == 1 then alts
|
| 43 |
+
else
|
| 44 |
+
let alts := alts.filter fun alt => alt.body != max.body
|
| 45 |
+
alts.push (Alt.default max.body)
|
| 46 |
+
|
| 47 |
+
private def filterUnreachable (alts : Array Alt) : Array Alt :=
|
| 48 |
+
alts.filter fun alt => alt.body != FnBody.unreachable
|
| 49 |
+
|
| 50 |
+
private def mkSimpCase (tid : Name) (x : VarId) (xType : IRType) (alts : Array Alt) : FnBody :=
|
| 51 |
+
let alts := filterUnreachable alts
|
| 52 |
+
let alts := addDefault alts;
|
| 53 |
+
if alts.size == 0 then
|
| 54 |
+
FnBody.unreachable
|
| 55 |
+
else if _ : alts.size = 1 then
|
| 56 |
+
alts[0].body
|
| 57 |
+
else
|
| 58 |
+
FnBody.case tid x xType alts
|
| 59 |
+
|
| 60 |
+
partial def FnBody.simpCase (b : FnBody) : FnBody :=
|
| 61 |
+
let (bs, term) := b.flatten;
|
| 62 |
+
let bs := modifyJPs bs simpCase;
|
| 63 |
+
match term with
|
| 64 |
+
| FnBody.case tid x xType alts =>
|
| 65 |
+
let alts := alts.map fun alt => alt.modifyBody simpCase;
|
| 66 |
+
reshape bs (mkSimpCase tid x xType alts)
|
| 67 |
+
| _ => reshape bs term
|
| 68 |
+
|
| 69 |
+
/-- Simplify `case`
|
| 70 |
+
- Remove unreachable branches.
|
| 71 |
+
- Remove `case` if there is only one branch.
|
| 72 |
+
- Merge most common branches using `Alt.default`. -/
|
| 73 |
+
def Decl.simpCase (d : Decl) : Decl :=
|
| 74 |
+
match d with
|
| 75 |
+
| .fdecl (body := b) .. => d.updateBody! b.simpCase
|
| 76 |
+
| other => other
|
| 77 |
+
|
| 78 |
+
end Lean.IR
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/Sorry.lean
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2021 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Compiler.IR.CompilerM
|
| 8 |
+
|
| 9 |
+
namespace Lean.IR
|
| 10 |
+
namespace Sorry
|
| 11 |
+
|
| 12 |
+
structure State where
|
| 13 |
+
localSorryMap : NameMap Name := {}
|
| 14 |
+
modified : Bool := false
|
| 15 |
+
|
| 16 |
+
abbrev M := StateT State CompilerM
|
| 17 |
+
|
| 18 |
+
def visitExpr : Expr → ExceptT Name M Unit
|
| 19 |
+
| Expr.fap f _ => getSorryDepFor? f
|
| 20 |
+
| Expr.pap f _ => getSorryDepFor? f
|
| 21 |
+
| _ => return ()
|
| 22 |
+
where
|
| 23 |
+
getSorryDepFor? (f : Name) : ExceptT Name M Unit := do
|
| 24 |
+
let found (g : Name) :=
|
| 25 |
+
if g == ``sorryAx then
|
| 26 |
+
throw f
|
| 27 |
+
else
|
| 28 |
+
throw g
|
| 29 |
+
if f == ``sorryAx then
|
| 30 |
+
throw f
|
| 31 |
+
else if let some g := (← get).localSorryMap.find? f then
|
| 32 |
+
found g
|
| 33 |
+
else match (← findDecl f) with
|
| 34 |
+
| some (.fdecl (info := { sorryDep? := some g, .. }) ..) => found g
|
| 35 |
+
| _ => return ()
|
| 36 |
+
|
| 37 |
+
partial def visitFndBody (b : FnBody) : ExceptT Name M Unit := do
|
| 38 |
+
match b with
|
| 39 |
+
| .vdecl _ _ v b => visitExpr v; visitFndBody b
|
| 40 |
+
| .jdecl _ _ v b => visitFndBody v; visitFndBody b
|
| 41 |
+
| .case _ _ _ alts => alts.forM fun alt => visitFndBody alt.body
|
| 42 |
+
| _ =>
|
| 43 |
+
unless b.isTerminal do
|
| 44 |
+
let (_, b) := b.split
|
| 45 |
+
visitFndBody b
|
| 46 |
+
|
| 47 |
+
def visitDecl (d : Decl) : M Unit := do
|
| 48 |
+
match d with
|
| 49 |
+
| .fdecl (f := f) (body := b) .. =>
|
| 50 |
+
match (← get).localSorryMap.find? f with
|
| 51 |
+
| some _ => return ()
|
| 52 |
+
| none =>
|
| 53 |
+
match (← visitFndBody b |>.run) with
|
| 54 |
+
| .ok _ => return ()
|
| 55 |
+
| .error g =>
|
| 56 |
+
modify fun s => {
|
| 57 |
+
localSorryMap := s.localSorryMap.insert f g
|
| 58 |
+
modified := true
|
| 59 |
+
}
|
| 60 |
+
| _ => return ()
|
| 61 |
+
|
| 62 |
+
partial def collect (decls : Array Decl) : M Unit := do
|
| 63 |
+
modify fun s => { s with modified := false }
|
| 64 |
+
decls.forM visitDecl
|
| 65 |
+
if (← get).modified then
|
| 66 |
+
collect decls
|
| 67 |
+
|
| 68 |
+
end Sorry
|
| 69 |
+
|
| 70 |
+
def updateSorryDep (decls : Array Decl) : CompilerM (Array Decl) := do
|
| 71 |
+
let (_, s) ← Sorry.collect decls |>.run {}
|
| 72 |
+
return decls.map fun decl =>
|
| 73 |
+
match decl with
|
| 74 |
+
| Decl.fdecl f xs t b _ =>
|
| 75 |
+
match s.localSorryMap.find? f with
|
| 76 |
+
| some g => Decl.fdecl f xs t b { sorryDep? := some g }
|
| 77 |
+
| _ => decl
|
| 78 |
+
| _ => decl
|
| 79 |
+
|
| 80 |
+
end Lean.IR
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/ToIR.lean
ADDED
|
@@ -0,0 +1,350 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2024 Lean FRO, LLC. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Cameron Zwarich
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Compiler.LCNF.Basic
|
| 8 |
+
import Lean.Compiler.LCNF.CompilerM
|
| 9 |
+
import Lean.Compiler.LCNF.PhaseExt
|
| 10 |
+
import Lean.Compiler.IR.Basic
|
| 11 |
+
import Lean.Compiler.IR.CompilerM
|
| 12 |
+
import Lean.Compiler.IR.ToIRType
|
| 13 |
+
import Lean.CoreM
|
| 14 |
+
import Lean.Environment
|
| 15 |
+
|
| 16 |
+
namespace Lean.IR
|
| 17 |
+
|
| 18 |
+
open Lean.Compiler (LCNF.Alt LCNF.Arg LCNF.Code LCNF.Decl LCNF.DeclValue LCNF.LCtx LCNF.LetDecl
|
| 19 |
+
LCNF.LetValue LCNF.LitValue LCNF.Param LCNF.getMonoDecl?)
|
| 20 |
+
|
| 21 |
+
namespace ToIR
|
| 22 |
+
|
| 23 |
+
inductive FVarClassification where
|
| 24 |
+
| var (id : VarId)
|
| 25 |
+
| joinPoint (id : JoinPointId)
|
| 26 |
+
| erased
|
| 27 |
+
|
| 28 |
+
structure BuilderState where
|
| 29 |
+
fvars : Std.HashMap FVarId FVarClassification := {}
|
| 30 |
+
nextId : Nat := 1
|
| 31 |
+
|
| 32 |
+
abbrev M := StateRefT BuilderState CoreM
|
| 33 |
+
|
| 34 |
+
def M.run (x : M α) : CoreM α := do
|
| 35 |
+
x.run' {}
|
| 36 |
+
|
| 37 |
+
def bindVar (fvarId : FVarId) : M VarId := do
|
| 38 |
+
modifyGet fun s =>
|
| 39 |
+
let varId := { idx := s.nextId }
|
| 40 |
+
⟨varId, { s with fvars := s.fvars.insertIfNew fvarId (.var varId),
|
| 41 |
+
nextId := s.nextId + 1 }⟩
|
| 42 |
+
|
| 43 |
+
def bindVarToVarId (fvarId : FVarId) (varId : VarId) : M Unit := do
|
| 44 |
+
modify fun s => { s with fvars := s.fvars.insertIfNew fvarId (.var varId) }
|
| 45 |
+
|
| 46 |
+
def newVar : M VarId := do
|
| 47 |
+
modifyGet fun s =>
|
| 48 |
+
let varId := { idx := s.nextId }
|
| 49 |
+
⟨varId, { s with nextId := s.nextId + 1 }⟩
|
| 50 |
+
|
| 51 |
+
def bindJoinPoint (fvarId : FVarId) : M JoinPointId := do
|
| 52 |
+
modifyGet fun s =>
|
| 53 |
+
let joinPointId := { idx := s.nextId }
|
| 54 |
+
⟨joinPointId, { s with fvars := s.fvars.insertIfNew fvarId (.joinPoint joinPointId),
|
| 55 |
+
nextId := s.nextId + 1 }⟩
|
| 56 |
+
|
| 57 |
+
def bindErased (fvarId : FVarId) : M Unit := do
|
| 58 |
+
modify fun s => { s with fvars := s.fvars.insertIfNew fvarId .erased }
|
| 59 |
+
|
| 60 |
+
def findDecl (n : Name) : M (Option Decl) :=
|
| 61 |
+
return findEnvDecl (← Lean.getEnv) n
|
| 62 |
+
|
| 63 |
+
def addDecl (d : Decl) : M Unit :=
|
| 64 |
+
Lean.modifyEnv fun env => declMapExt.addEntry (env.addExtraName d.name) d
|
| 65 |
+
|
| 66 |
+
def lowerLitValue (v : LCNF.LitValue) : LitVal :=
|
| 67 |
+
match v with
|
| 68 |
+
| .nat n => .num n
|
| 69 |
+
| .str s => .str s
|
| 70 |
+
| .uint8 v => .num (UInt8.toNat v)
|
| 71 |
+
| .uint16 v => .num (UInt16.toNat v)
|
| 72 |
+
| .uint32 v => .num (UInt32.toNat v)
|
| 73 |
+
| .uint64 v | .usize v => .num (UInt64.toNat v)
|
| 74 |
+
|
| 75 |
+
def lowerArg (a : LCNF.Arg) : M Arg := do
|
| 76 |
+
match a with
|
| 77 |
+
| .fvar fvarId =>
|
| 78 |
+
match (← get).fvars[fvarId]? with
|
| 79 |
+
| some (.var varId) => return .var varId
|
| 80 |
+
| some .erased => return .irrelevant
|
| 81 |
+
| some (.joinPoint ..) | none => panic! "unexpected value"
|
| 82 |
+
| .erased | .type .. => return .irrelevant
|
| 83 |
+
|
| 84 |
+
inductive TranslatedProj where
|
| 85 |
+
| expr (e : Expr)
|
| 86 |
+
| erased
|
| 87 |
+
deriving Inhabited
|
| 88 |
+
|
| 89 |
+
def lowerProj (base : VarId) (ctorInfo : CtorInfo) (field : CtorFieldInfo)
|
| 90 |
+
: TranslatedProj × IRType :=
|
| 91 |
+
match field with
|
| 92 |
+
| .object i => ⟨.expr (.proj i base), .object⟩
|
| 93 |
+
| .usize i => ⟨.expr (.uproj i base), .usize⟩
|
| 94 |
+
| .scalar _ offset irType => ⟨.expr (.sproj (ctorInfo.size + ctorInfo.usize) offset base), irType⟩
|
| 95 |
+
| .irrelevant => ⟨.erased, .irrelevant⟩
|
| 96 |
+
|
| 97 |
+
def lowerParam (p : LCNF.Param) : M Param := do
|
| 98 |
+
let x ← bindVar p.fvarId
|
| 99 |
+
let ty ← toIRType p.type
|
| 100 |
+
return { x, borrow := p.borrow, ty }
|
| 101 |
+
|
| 102 |
+
mutual
|
| 103 |
+
partial def lowerCode (c : LCNF.Code) : M FnBody := do
|
| 104 |
+
match c with
|
| 105 |
+
| .let decl k => lowerLet decl k
|
| 106 |
+
| .jp decl k =>
|
| 107 |
+
let joinPoint ← bindJoinPoint decl.fvarId
|
| 108 |
+
let params ← decl.params.mapM lowerParam
|
| 109 |
+
let body ← lowerCode decl.value
|
| 110 |
+
return .jdecl joinPoint params body (← lowerCode k)
|
| 111 |
+
| .jmp fvarId args =>
|
| 112 |
+
match (← get).fvars[fvarId]? with
|
| 113 |
+
| some (.joinPoint joinPointId) =>
|
| 114 |
+
return .jmp joinPointId (← args.mapM lowerArg)
|
| 115 |
+
| some (.var ..) | some .erased | none => panic! "unexpected value"
|
| 116 |
+
| .cases cases =>
|
| 117 |
+
match (← get).fvars[cases.discr]? with
|
| 118 |
+
| some (.var varId) =>
|
| 119 |
+
return .case cases.typeName
|
| 120 |
+
varId
|
| 121 |
+
(← toIRType cases.resultType)
|
| 122 |
+
(← cases.alts.mapM (lowerAlt varId))
|
| 123 |
+
| some (.joinPoint ..) | some .erased | none => panic! "unexpected value"
|
| 124 |
+
| .return fvarId =>
|
| 125 |
+
let arg := match (← get).fvars[fvarId]? with
|
| 126 |
+
| some (.var varId) => .var varId
|
| 127 |
+
| some .erased => .irrelevant
|
| 128 |
+
| some (.joinPoint ..) | none => panic! "unexpected value"
|
| 129 |
+
return .ret arg
|
| 130 |
+
| .unreach .. => return .unreachable
|
| 131 |
+
| .fun .. => panic! "all local functions should be λ-lifted"
|
| 132 |
+
|
| 133 |
+
partial def lowerLet (decl : LCNF.LetDecl) (k : LCNF.Code) : M FnBody := do
|
| 134 |
+
-- temporary fix: the old compiler inlines these too much as regular `let`s
|
| 135 |
+
let rec mkVar (v : VarId) : M FnBody := do
|
| 136 |
+
bindVarToVarId decl.fvarId v
|
| 137 |
+
lowerCode k
|
| 138 |
+
let rec mkExpr (e : Expr) : M FnBody := do
|
| 139 |
+
let var ← bindVar decl.fvarId
|
| 140 |
+
let type ← match e with
|
| 141 |
+
| .ctor .. | .pap .. | .proj .. => pure <| .object
|
| 142 |
+
| _ => toIRType decl.type
|
| 143 |
+
return .vdecl var type e (← lowerCode k)
|
| 144 |
+
let rec mkErased (_ : Unit) : M FnBody := do
|
| 145 |
+
bindErased decl.fvarId
|
| 146 |
+
lowerCode k
|
| 147 |
+
let rec mkPartialApp (e : Expr) (restArgs : Array Arg) : M FnBody := do
|
| 148 |
+
let var ← bindVar decl.fvarId
|
| 149 |
+
let tmpVar ← newVar
|
| 150 |
+
let type ← match e with
|
| 151 |
+
| .ctor .. | .pap .. | .proj .. => pure <| .object
|
| 152 |
+
| _ => toIRType decl.type
|
| 153 |
+
return .vdecl tmpVar .object e (.vdecl var type (.ap tmpVar restArgs) (← lowerCode k))
|
| 154 |
+
let rec tryIrDecl? (name : Name) (args : Array Arg) : M (Option FnBody) := do
|
| 155 |
+
if let some decl ← LCNF.getMonoDecl? name then
|
| 156 |
+
let numArgs := args.size
|
| 157 |
+
let numParams := decl.params.size
|
| 158 |
+
if numArgs < numParams then
|
| 159 |
+
return some (← mkExpr (.pap name args))
|
| 160 |
+
else if numArgs == numParams then
|
| 161 |
+
return some (← mkExpr (.fap name args))
|
| 162 |
+
else
|
| 163 |
+
let firstArgs := args.extract 0 numParams
|
| 164 |
+
let restArgs := args.extract numParams numArgs
|
| 165 |
+
return some (← mkPartialApp (.fap name firstArgs) restArgs)
|
| 166 |
+
else
|
| 167 |
+
return none
|
| 168 |
+
|
| 169 |
+
match decl.value with
|
| 170 |
+
| .lit litValue =>
|
| 171 |
+
mkExpr (.lit (lowerLitValue litValue))
|
| 172 |
+
| .proj typeName i fvarId =>
|
| 173 |
+
match (← get).fvars[fvarId]? with
|
| 174 |
+
| some (.var varId) =>
|
| 175 |
+
-- TODO: have better pattern matching here
|
| 176 |
+
let some (.inductInfo { ctors, .. }) := (← Lean.getEnv).find? typeName
|
| 177 |
+
| panic! "projection of non-inductive type"
|
| 178 |
+
let ctorName := ctors[0]!
|
| 179 |
+
let ⟨ctorInfo, fields⟩ ← getCtorInfo ctorName
|
| 180 |
+
let ⟨result, type⟩ := lowerProj varId ctorInfo fields[i]!
|
| 181 |
+
match result with
|
| 182 |
+
| .expr e =>
|
| 183 |
+
let var ← bindVar decl.fvarId
|
| 184 |
+
return .vdecl var type e (← lowerCode k)
|
| 185 |
+
| .erased =>
|
| 186 |
+
bindErased decl.fvarId
|
| 187 |
+
lowerCode k
|
| 188 |
+
| some .erased =>
|
| 189 |
+
bindErased decl.fvarId
|
| 190 |
+
lowerCode k
|
| 191 |
+
| some (.joinPoint ..) | none => panic! "unexpected value"
|
| 192 |
+
| .const ``Nat.succ _ args =>
|
| 193 |
+
let irArgs ← args.mapM lowerArg
|
| 194 |
+
let var ← bindVar decl.fvarId
|
| 195 |
+
let tmpVar ← newVar
|
| 196 |
+
let k := (.vdecl var .object (.fap ``Nat.add #[irArgs[0]!, (.var tmpVar)]) (← lowerCode k))
|
| 197 |
+
return .vdecl tmpVar .object (.lit (.num 1)) k
|
| 198 |
+
| .const name _ args =>
|
| 199 |
+
let irArgs ← args.mapM lowerArg
|
| 200 |
+
if let some code ← tryIrDecl? name irArgs then
|
| 201 |
+
return code
|
| 202 |
+
else
|
| 203 |
+
let env ← Lean.getEnv
|
| 204 |
+
match env.find? name with
|
| 205 |
+
| some (.ctorInfo ctorVal) =>
|
| 206 |
+
if isExtern env name then
|
| 207 |
+
if let some code ← tryIrDecl? name irArgs then
|
| 208 |
+
return code
|
| 209 |
+
else
|
| 210 |
+
mkExpr (.fap name irArgs)
|
| 211 |
+
else if let some scalarType ← lowerEnumToScalarType? ctorVal.name then
|
| 212 |
+
assert! args.isEmpty
|
| 213 |
+
let var ← bindVar decl.fvarId
|
| 214 |
+
return .vdecl var scalarType (.lit (.num ctorVal.cidx)) (← lowerCode k)
|
| 215 |
+
else
|
| 216 |
+
let ⟨ctorInfo, fields⟩ ← getCtorInfo name
|
| 217 |
+
let irArgs := irArgs.extract (start := ctorVal.numParams)
|
| 218 |
+
let objArgs : Array Arg ← do
|
| 219 |
+
let mut result : Array Arg := #[]
|
| 220 |
+
for h : i in [:fields.size] do
|
| 221 |
+
if fields[i] matches .object .. then
|
| 222 |
+
result := result.push irArgs[i]!
|
| 223 |
+
pure result
|
| 224 |
+
let objVar ← bindVar decl.fvarId
|
| 225 |
+
let rec lowerNonObjectFields (_ : Unit) : M FnBody :=
|
| 226 |
+
let rec loop (usizeCount : Nat) (i : Nat) : M FnBody := do
|
| 227 |
+
match irArgs[i]? with
|
| 228 |
+
| some (.var varId) =>
|
| 229 |
+
match fields[i]! with
|
| 230 |
+
| .usize .. =>
|
| 231 |
+
let k ← loop (usizeCount + 1) (i + 1)
|
| 232 |
+
return .uset objVar (ctorInfo.size + usizeCount) varId k
|
| 233 |
+
| .scalar _ offset argType =>
|
| 234 |
+
let k ← loop usizeCount (i + 1)
|
| 235 |
+
return .sset objVar (ctorInfo.size + ctorInfo.usize) offset varId argType k
|
| 236 |
+
| .object .. | .irrelevant => loop usizeCount (i + 1)
|
| 237 |
+
| some .irrelevant => loop usizeCount (i + 1)
|
| 238 |
+
| none => lowerCode k
|
| 239 |
+
loop 0 0
|
| 240 |
+
return .vdecl objVar .object (.ctor ctorInfo objArgs) (← lowerNonObjectFields ())
|
| 241 |
+
| some (.axiomInfo ..) =>
|
| 242 |
+
if name == ``Quot.lcInv then
|
| 243 |
+
match irArgs[2]! with
|
| 244 |
+
| .var varId => mkVar varId
|
| 245 |
+
| .irrelevant => mkErased ()
|
| 246 |
+
else if name == ``lcUnreachable then
|
| 247 |
+
return .unreachable
|
| 248 |
+
else if let some irDecl ← findDecl name then
|
| 249 |
+
let numArgs := irArgs.size
|
| 250 |
+
let numParams := irDecl.params.size
|
| 251 |
+
if numArgs < numParams then
|
| 252 |
+
mkExpr (.pap name irArgs)
|
| 253 |
+
else if numArgs == numParams then
|
| 254 |
+
mkExpr (.fap name irArgs)
|
| 255 |
+
else
|
| 256 |
+
let firstArgs := irArgs.extract 0 numParams
|
| 257 |
+
let restArgs := irArgs.extract numParams irArgs.size
|
| 258 |
+
mkPartialApp (.fap name firstArgs) restArgs
|
| 259 |
+
else
|
| 260 |
+
throwNamedError lean.dependsOnNoncomputable f!"axiom '{name}' not supported by code generator; consider marking definition as 'noncomputable'"
|
| 261 |
+
| some (.quotInfo ..) =>
|
| 262 |
+
if name == ``Quot.mk then
|
| 263 |
+
match irArgs[2]! with
|
| 264 |
+
| .var varId => mkVar varId
|
| 265 |
+
| .irrelevant => mkErased ()
|
| 266 |
+
else
|
| 267 |
+
throwError f!"quot {name} unsupported by code generator"
|
| 268 |
+
| some (.defnInfo ..) | some (.opaqueInfo ..) =>
|
| 269 |
+
if let some code ← tryIrDecl? name irArgs then
|
| 270 |
+
return code
|
| 271 |
+
else
|
| 272 |
+
mkExpr (.fap name irArgs)
|
| 273 |
+
| some (.recInfo ..) =>
|
| 274 |
+
throwError f!"code generator does not support recursor '{name}' yet, consider using 'match ... with' and/or structural recursion"
|
| 275 |
+
| some (.inductInfo ..) => panic! "induct unsupported by code generator"
|
| 276 |
+
| some (.thmInfo ..) => panic! "thm unsupported by code generator"
|
| 277 |
+
| none => panic! "reference to unbound name"
|
| 278 |
+
| .fvar fvarId args =>
|
| 279 |
+
match (← get).fvars[fvarId]? with
|
| 280 |
+
| some (.var id) =>
|
| 281 |
+
let irArgs ← args.mapM lowerArg
|
| 282 |
+
mkExpr (.ap id irArgs)
|
| 283 |
+
| some .erased => mkErased ()
|
| 284 |
+
| some (.joinPoint ..) | none => panic! "unexpected value"
|
| 285 |
+
| .erased => mkErased ()
|
| 286 |
+
|
| 287 |
+
partial def lowerAlt (discr : VarId) (a : LCNF.Alt) : M Alt := do
|
| 288 |
+
match a with
|
| 289 |
+
| .alt ctorName params code =>
|
| 290 |
+
let ⟨ctorInfo, fields⟩ ← getCtorInfo ctorName
|
| 291 |
+
let lowerParams (params : Array LCNF.Param) (fields : Array CtorFieldInfo) : M FnBody := do
|
| 292 |
+
let rec loop (i : Nat) : M FnBody := do
|
| 293 |
+
match params[i]?, fields[i]? with
|
| 294 |
+
| some param, some field =>
|
| 295 |
+
let ⟨result, type⟩ := lowerProj discr ctorInfo field
|
| 296 |
+
match result with
|
| 297 |
+
| .expr e =>
|
| 298 |
+
return .vdecl (← bindVar param.fvarId)
|
| 299 |
+
type
|
| 300 |
+
e
|
| 301 |
+
(← loop (i + 1))
|
| 302 |
+
| .erased =>
|
| 303 |
+
bindErased param.fvarId
|
| 304 |
+
loop (i + 1)
|
| 305 |
+
| none, none => lowerCode code
|
| 306 |
+
| _, _ => panic! "mismatched fields and params"
|
| 307 |
+
loop 0
|
| 308 |
+
let body ← lowerParams params fields
|
| 309 |
+
return .ctor ctorInfo body
|
| 310 |
+
| .default code =>
|
| 311 |
+
return .default (← lowerCode code)
|
| 312 |
+
end
|
| 313 |
+
|
| 314 |
+
def lowerResultType (type : Lean.Expr) (arity : Nat) : M IRType :=
|
| 315 |
+
toIRType (resultTypeForArity type arity)
|
| 316 |
+
where resultTypeForArity (type : Lean.Expr) (arity : Nat) : Lean.Expr :=
|
| 317 |
+
if arity == 0 then
|
| 318 |
+
type
|
| 319 |
+
else
|
| 320 |
+
match type with
|
| 321 |
+
| .forallE _ _ b _ => resultTypeForArity b (arity - 1)
|
| 322 |
+
| .const ``lcErased _ => mkConst ``lcErased
|
| 323 |
+
| _ => panic! "invalid arity"
|
| 324 |
+
|
| 325 |
+
def lowerDecl (d : LCNF.Decl) : M (Option Decl) := do
|
| 326 |
+
let params ← d.params.mapM lowerParam
|
| 327 |
+
let resultType ← lowerResultType d.type d.params.size
|
| 328 |
+
match d.value with
|
| 329 |
+
| .code code =>
|
| 330 |
+
let body ← lowerCode code
|
| 331 |
+
pure <| some <| .fdecl d.name params resultType body {}
|
| 332 |
+
| .extern externAttrData =>
|
| 333 |
+
if externAttrData.entries.isEmpty then
|
| 334 |
+
-- TODO: This matches the behavior of the old compiler, but we should
|
| 335 |
+
-- find a better way to handle this.
|
| 336 |
+
addDecl (mkDummyExternDecl d.name params resultType)
|
| 337 |
+
pure <| none
|
| 338 |
+
else
|
| 339 |
+
pure <| some <| .extern d.name params resultType externAttrData
|
| 340 |
+
|
| 341 |
+
end ToIR
|
| 342 |
+
|
| 343 |
+
def toIR (decls: Array LCNF.Decl) : CoreM (Array Decl) := do
|
| 344 |
+
let mut irDecls := #[]
|
| 345 |
+
for decl in decls do
|
| 346 |
+
if let some irDecl ← ToIR.lowerDecl decl |>.run then
|
| 347 |
+
irDecls := irDecls.push irDecl
|
| 348 |
+
return irDecls
|
| 349 |
+
|
| 350 |
+
end Lean.IR
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/ToIRType.lean
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2019 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Environment
|
| 8 |
+
import Lean.Compiler.IR.Format
|
| 9 |
+
import Lean.Compiler.LCNF.CompilerM
|
| 10 |
+
|
| 11 |
+
namespace Lean
|
| 12 |
+
namespace IR
|
| 13 |
+
|
| 14 |
+
open Lean.Compiler (LCNF.CacheExtension)
|
| 15 |
+
|
| 16 |
+
builtin_initialize scalarTypeExt : LCNF.CacheExtension Name (Option IRType) ←
|
| 17 |
+
LCNF.CacheExtension.register
|
| 18 |
+
|
| 19 |
+
def lowerEnumToScalarType? (name : Name) : CoreM (Option IRType) := do
|
| 20 |
+
match (← scalarTypeExt.find? name) with
|
| 21 |
+
| some info? => return info?
|
| 22 |
+
| none =>
|
| 23 |
+
let info? ← fillCache
|
| 24 |
+
scalarTypeExt.insert name info?
|
| 25 |
+
return info?
|
| 26 |
+
where fillCache : CoreM (Option IRType) := do
|
| 27 |
+
let env ← Lean.getEnv
|
| 28 |
+
let some (.inductInfo inductiveVal) := env.find? name | return none
|
| 29 |
+
let ctorNames := inductiveVal.ctors
|
| 30 |
+
let numCtors := ctorNames.length
|
| 31 |
+
for ctorName in ctorNames do
|
| 32 |
+
let some (.ctorInfo ctorVal) := env.find? ctorName | panic! "expected valid constructor name"
|
| 33 |
+
if ctorVal.type.isForall then return none
|
| 34 |
+
return if numCtors == 1 then
|
| 35 |
+
none
|
| 36 |
+
else if numCtors < Nat.pow 2 8 then
|
| 37 |
+
some .uint8
|
| 38 |
+
else if numCtors < Nat.pow 2 16 then
|
| 39 |
+
some .uint16
|
| 40 |
+
else if numCtors < Nat.pow 2 32 then
|
| 41 |
+
some .uint32
|
| 42 |
+
else
|
| 43 |
+
none
|
| 44 |
+
|
| 45 |
+
def toIRType (e : Lean.Expr) : CoreM IRType := do
|
| 46 |
+
match e with
|
| 47 |
+
| .const name .. =>
|
| 48 |
+
match name with
|
| 49 |
+
| ``UInt8 | ``Bool => return .uint8
|
| 50 |
+
| ``UInt16 => return .uint16
|
| 51 |
+
| ``UInt32 => return .uint32
|
| 52 |
+
| ``UInt64 => return .uint64
|
| 53 |
+
| ``USize => return .usize
|
| 54 |
+
| ``Float => return .float
|
| 55 |
+
| ``Float32 => return .float32
|
| 56 |
+
| ``lcErased => return .irrelevant
|
| 57 |
+
| _ =>
|
| 58 |
+
if let some scalarType ← lowerEnumToScalarType? name then
|
| 59 |
+
return scalarType
|
| 60 |
+
else
|
| 61 |
+
return .object
|
| 62 |
+
| .app f _ =>
|
| 63 |
+
-- All mono types are in headBeta form.
|
| 64 |
+
if let .const name _ := f then
|
| 65 |
+
if let some scalarType ← lowerEnumToScalarType? name then
|
| 66 |
+
return scalarType
|
| 67 |
+
else
|
| 68 |
+
return .object
|
| 69 |
+
else
|
| 70 |
+
return .object
|
| 71 |
+
| .forallE .. => return .object
|
| 72 |
+
| _ => panic! "invalid type"
|
| 73 |
+
|
| 74 |
+
inductive CtorFieldInfo where
|
| 75 |
+
| irrelevant
|
| 76 |
+
| object (i : Nat)
|
| 77 |
+
| usize (i : Nat)
|
| 78 |
+
| scalar (sz : Nat) (offset : Nat) (type : IRType)
|
| 79 |
+
deriving Inhabited
|
| 80 |
+
|
| 81 |
+
namespace CtorFieldInfo
|
| 82 |
+
|
| 83 |
+
def format : CtorFieldInfo → Format
|
| 84 |
+
| irrelevant => "◾"
|
| 85 |
+
| object i => f!"obj@{i}"
|
| 86 |
+
| usize i => f!"usize@{i}"
|
| 87 |
+
| scalar sz offset type => f!"scalar#{sz}@{offset}:{type}"
|
| 88 |
+
|
| 89 |
+
instance : ToFormat CtorFieldInfo := ⟨format⟩
|
| 90 |
+
|
| 91 |
+
end CtorFieldInfo
|
| 92 |
+
|
| 93 |
+
structure CtorLayout where
|
| 94 |
+
cidx : Nat
|
| 95 |
+
fieldInfo : List CtorFieldInfo
|
| 96 |
+
numObjs : Nat
|
| 97 |
+
numUSize : Nat
|
| 98 |
+
scalarSize : Nat
|
| 99 |
+
|
| 100 |
+
@[extern "lean_ir_get_ctor_layout"]
|
| 101 |
+
opaque getCtorLayout (env : @& Environment) (ctorName : @& Name) : Except String CtorLayout
|
| 102 |
+
|
| 103 |
+
builtin_initialize ctorInfoExt : LCNF.CacheExtension Name (CtorInfo × (Array CtorFieldInfo)) ←
|
| 104 |
+
LCNF.CacheExtension.register
|
| 105 |
+
|
| 106 |
+
def getCtorInfo (name : Name) : CoreM (CtorInfo × (Array CtorFieldInfo)) := do
|
| 107 |
+
match (← ctorInfoExt.find? name) with
|
| 108 |
+
| some info => return info
|
| 109 |
+
| none =>
|
| 110 |
+
let info ← fillCache
|
| 111 |
+
ctorInfoExt.insert name info
|
| 112 |
+
return info
|
| 113 |
+
where fillCache := do
|
| 114 |
+
match getCtorLayout (← Lean.getEnv) name with
|
| 115 |
+
| .ok ctorLayout =>
|
| 116 |
+
return ⟨{
|
| 117 |
+
name,
|
| 118 |
+
cidx := ctorLayout.cidx,
|
| 119 |
+
size := ctorLayout.numObjs,
|
| 120 |
+
usize := ctorLayout.numUSize,
|
| 121 |
+
ssize := ctorLayout.scalarSize
|
| 122 |
+
}, ctorLayout.fieldInfo.toArray⟩
|
| 123 |
+
| .error .. => panic! "unrecognized constructor"
|
| 124 |
+
|
| 125 |
+
end IR
|
| 126 |
+
end Lean
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/UnboxResult.lean
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2019 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Data.Format
|
| 8 |
+
import Lean.Compiler.IR.Basic
|
| 9 |
+
|
| 10 |
+
namespace Lean.IR.UnboxResult
|
| 11 |
+
|
| 12 |
+
/--
|
| 13 |
+
Tags types that the compiler should unbox if they occur in result values.
|
| 14 |
+
|
| 15 |
+
This attribute currently has no effect.
|
| 16 |
+
-/
|
| 17 |
+
@[builtin_doc]
|
| 18 |
+
builtin_initialize unboxAttr : TagAttribute ←
|
| 19 |
+
registerTagAttribute `unbox "compiler tries to unbox result values if their types are tagged with `[unbox]`" fun declName => do
|
| 20 |
+
let cinfo ← getConstInfo declName;
|
| 21 |
+
match cinfo with
|
| 22 |
+
| ConstantInfo.inductInfo v =>
|
| 23 |
+
if v.isRec then throwError "recursive inductive datatypes are not supported"
|
| 24 |
+
else pure ()
|
| 25 |
+
| _ => throwError "constant must be an inductive type"
|
| 26 |
+
|
| 27 |
+
def hasUnboxAttr (env : Environment) (n : Name) : Bool :=
|
| 28 |
+
unboxAttr.hasTag env n
|
| 29 |
+
|
| 30 |
+
end Lean.IR.UnboxResult
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/AlphaEqv.lean
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2022 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Compiler.LCNF.Basic
|
| 8 |
+
|
| 9 |
+
namespace Lean.Compiler.LCNF
|
| 10 |
+
|
| 11 |
+
/-!
|
| 12 |
+
Alpha equivalence for LCNF Code
|
| 13 |
+
-/
|
| 14 |
+
|
| 15 |
+
namespace AlphaEqv
|
| 16 |
+
|
| 17 |
+
abbrev EqvM := ReaderM (FVarIdMap FVarId)
|
| 18 |
+
|
| 19 |
+
def eqvFVar (fvarId₁ fvarId₂ : FVarId) : EqvM Bool := do
|
| 20 |
+
let fvarId₂ := (← read).find? fvarId₂ |>.getD fvarId₂
|
| 21 |
+
return fvarId₁ == fvarId₂
|
| 22 |
+
|
| 23 |
+
def eqvType (e₁ e₂ : Expr) : EqvM Bool := do
|
| 24 |
+
match e₁, e₂ with
|
| 25 |
+
| .app f₁ a₁, .app f₂ a₂ => eqvType a₁ a₂ <&&> eqvType f₁ f₂
|
| 26 |
+
| .fvar fvarId₁, .fvar fvarId₂ => eqvFVar fvarId₁ fvarId₂
|
| 27 |
+
| .forallE _ d₁ b₁ _, .forallE _ d₂ b₂ _ => eqvType d₁ d₂ <&&> eqvType b₁ b₂
|
| 28 |
+
| _, _ => return e₁ == e₂
|
| 29 |
+
|
| 30 |
+
def eqvTypes (es₁ es₂ : Array Expr) : EqvM Bool := do
|
| 31 |
+
if es₁.size = es₂.size then
|
| 32 |
+
for e₁ in es₁, e₂ in es₂ do
|
| 33 |
+
unless (← eqvType e₁ e₂) do
|
| 34 |
+
return false
|
| 35 |
+
return true
|
| 36 |
+
else
|
| 37 |
+
return false
|
| 38 |
+
|
| 39 |
+
def eqvArg (a₁ a₂ : Arg) : EqvM Bool := do
|
| 40 |
+
match a₁, a₂ with
|
| 41 |
+
| .type e₁, .type e₂ => eqvType e₁ e₂
|
| 42 |
+
| .fvar x₁, .fvar x₂ => eqvFVar x₁ x₂
|
| 43 |
+
| .erased, .erased => return true
|
| 44 |
+
| _, _ => return false
|
| 45 |
+
|
| 46 |
+
def eqvArgs (as₁ as₂ : Array Arg) : EqvM Bool := do
|
| 47 |
+
if as₁.size = as₂.size then
|
| 48 |
+
for a₁ in as₁, a₂ in as₂ do
|
| 49 |
+
unless (← eqvArg a₁ a₂) do
|
| 50 |
+
return false
|
| 51 |
+
return true
|
| 52 |
+
else
|
| 53 |
+
return false
|
| 54 |
+
|
| 55 |
+
def eqvLetValue (e₁ e₂ : LetValue) : EqvM Bool := do
|
| 56 |
+
match e₁, e₂ with
|
| 57 |
+
| .lit v₁, .lit v₂ => return v₁ == v₂
|
| 58 |
+
| .erased, .erased => return true
|
| 59 |
+
| .proj s₁ i₁ x₁, .proj s₂ i₂ x₂ => pure (s₁ == s₂ && i₁ == i₂) <&&> eqvFVar x₁ x₂
|
| 60 |
+
| .const n₁ us₁ as₁, .const n₂ us₂ as₂ => pure (n₁ == n₂ && us₁ == us₂) <&&> eqvArgs as₁ as₂
|
| 61 |
+
| .fvar f₁ as₁, .fvar f₂ as₂ => eqvFVar f₁ f₂ <&&> eqvArgs as₁ as₂
|
| 62 |
+
| _, _ => return false
|
| 63 |
+
|
| 64 |
+
@[inline] def withFVar (fvarId₁ fvarId₂ : FVarId) (x : EqvM α) : EqvM α :=
|
| 65 |
+
withReader (·.insert fvarId₂ fvarId₁) x
|
| 66 |
+
|
| 67 |
+
@[inline] def withParams (params₁ params₂ : Array Param) (x : EqvM Bool) : EqvM Bool := do
|
| 68 |
+
if h : params₂.size = params₁.size then
|
| 69 |
+
let rec @[specialize] go (i : Nat) : EqvM Bool := do
|
| 70 |
+
if h : i < params₁.size then
|
| 71 |
+
let p₁ := params₁[i]
|
| 72 |
+
have : i < params₂.size := by simp_all +arith
|
| 73 |
+
let p₂ := params₂[i]
|
| 74 |
+
unless (← eqvType p₁.type p₂.type) do return false
|
| 75 |
+
withFVar p₁.fvarId p₂.fvarId do
|
| 76 |
+
go (i+1)
|
| 77 |
+
else
|
| 78 |
+
x
|
| 79 |
+
termination_by params₁.size - i
|
| 80 |
+
go 0
|
| 81 |
+
else
|
| 82 |
+
return false
|
| 83 |
+
|
| 84 |
+
def sortAlts (alts : Array Alt) : Array Alt :=
|
| 85 |
+
alts.qsort fun
|
| 86 |
+
| .alt .., .default .. => true
|
| 87 |
+
| .alt ctorName₁ .., .alt ctorName₂ .. => Name.lt ctorName₁ ctorName₂
|
| 88 |
+
| _, _ => false
|
| 89 |
+
|
| 90 |
+
mutual
|
| 91 |
+
|
| 92 |
+
partial def eqvAlts (alts₁ alts₂ : Array Alt) : EqvM Bool := do
|
| 93 |
+
if alts₁.size = alts₂.size then
|
| 94 |
+
let alts₁ := sortAlts alts₁
|
| 95 |
+
let alts₂ := sortAlts alts₂
|
| 96 |
+
for alt₁ in alts₁, alt₂ in alts₂ do
|
| 97 |
+
match alt₁, alt₂ with
|
| 98 |
+
| .alt ctorName₁ ps₁ k₁, .alt ctorName₂ ps₂ k₂ =>
|
| 99 |
+
unless ctorName₁ == ctorName₂ do return false
|
| 100 |
+
unless (← withParams ps₁ ps₂ (eqv k₁ k₂)) do return false
|
| 101 |
+
| .default k₁, .default k₂ => unless (← eqv k₁ k₂) do return false
|
| 102 |
+
| _, _ => return false
|
| 103 |
+
return true
|
| 104 |
+
else
|
| 105 |
+
return false
|
| 106 |
+
|
| 107 |
+
partial def eqv (code₁ code₂ : Code) : EqvM Bool := do
|
| 108 |
+
match code₁, code₂ with
|
| 109 |
+
| .let decl₁ k₁, .let decl₂ k₂ =>
|
| 110 |
+
eqvType decl₁.type decl₂.type <&&>
|
| 111 |
+
eqvLetValue decl₁.value decl₂.value <&&>
|
| 112 |
+
withFVar decl₁.fvarId decl₂.fvarId (eqv k₁ k₂)
|
| 113 |
+
| .fun decl₁ k₁, .fun decl₂ k₂
|
| 114 |
+
| .jp decl₁ k₁, .jp decl₂ k₂ =>
|
| 115 |
+
eqvType decl₁.type decl₂.type <&&>
|
| 116 |
+
withParams decl₁.params decl₂.params (eqv decl₁.value decl₂.value) <&&>
|
| 117 |
+
withFVar decl₁.fvarId decl₂.fvarId (eqv k₁ k₂)
|
| 118 |
+
| .return fvarId₁, .return fvarId₂ => eqvFVar fvarId₁ fvarId₂
|
| 119 |
+
| .unreach type₁, .unreach type₂ => eqvType type₁ type₂
|
| 120 |
+
| .jmp fvarId₁ args₁, .jmp fvarId₂ args₂ => eqvFVar fvarId₁ fvarId₂ <&&> eqvArgs args₁ args₂
|
| 121 |
+
| .cases c₁, .cases c₂ =>
|
| 122 |
+
eqvFVar c₁.discr c₂.discr <&&>
|
| 123 |
+
eqvType c₁.resultType c₂.resultType <&&>
|
| 124 |
+
eqvAlts c₁.alts c₂.alts
|
| 125 |
+
| _, _ => return false
|
| 126 |
+
|
| 127 |
+
end
|
| 128 |
+
|
| 129 |
+
end AlphaEqv
|
| 130 |
+
|
| 131 |
+
/--
|
| 132 |
+
Return `true` if `c₁` and `c₂` are alpha equivalent.
|
| 133 |
+
-/
|
| 134 |
+
def Code.alphaEqv (c₁ c₂ : Code) : Bool :=
|
| 135 |
+
AlphaEqv.eqv c₁ c₂ |>.run {}
|
| 136 |
+
|
| 137 |
+
end Lean.Compiler.LCNF
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/AuxDeclCache.lean
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2022 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Compiler.LCNF.CompilerM
|
| 8 |
+
import Lean.Compiler.LCNF.DeclHash
|
| 9 |
+
import Lean.Compiler.LCNF.Internalize
|
| 10 |
+
|
| 11 |
+
namespace Lean.Compiler.LCNF
|
| 12 |
+
|
| 13 |
+
builtin_initialize auxDeclCacheExt : CacheExtension Decl Name ← CacheExtension.register
|
| 14 |
+
|
| 15 |
+
inductive CacheAuxDeclResult where
|
| 16 |
+
| new
|
| 17 |
+
| alreadyCached (declName : Name)
|
| 18 |
+
|
| 19 |
+
def cacheAuxDecl (decl : Decl) : CompilerM CacheAuxDeclResult := do
|
| 20 |
+
let key := { decl with name := .anonymous }
|
| 21 |
+
let key ← normalizeFVarIds key
|
| 22 |
+
match (← auxDeclCacheExt.find? key) with
|
| 23 |
+
| some declName =>
|
| 24 |
+
return .alreadyCached declName
|
| 25 |
+
| none =>
|
| 26 |
+
auxDeclCacheExt.insert key decl.name
|
| 27 |
+
return .new
|
| 28 |
+
|
| 29 |
+
end Lean.Compiler.LCNF
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/BaseTypes.lean
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2022 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Compiler.LCNF.CompilerM
|
| 8 |
+
import Lean.Compiler.LCNF.Types
|
| 9 |
+
|
| 10 |
+
namespace Lean.Compiler.LCNF
|
| 11 |
+
|
| 12 |
+
/--
|
| 13 |
+
State for the environment extension used to save the LCNF base phase type for declarations
|
| 14 |
+
that do not have code associated with them.
|
| 15 |
+
Example: constructors, inductive types, foreign functions.
|
| 16 |
+
-/
|
| 17 |
+
builtin_initialize baseTypeExt : CacheExtension Name Expr ← CacheExtension.register
|
| 18 |
+
|
| 19 |
+
def getOtherDeclBaseType (declName : Name) (us : List Level) : CoreM Expr := do
|
| 20 |
+
let info ← getConstInfo declName
|
| 21 |
+
let type ← match (← baseTypeExt.find? declName) with
|
| 22 |
+
| some type => pure type
|
| 23 |
+
| none =>
|
| 24 |
+
let type ← Meta.MetaM.run' <| toLCNFType info.type
|
| 25 |
+
baseTypeExt.insert declName type
|
| 26 |
+
pure type
|
| 27 |
+
return type.instantiateLevelParamsNoCache info.levelParams us
|
| 28 |
+
|
| 29 |
+
end Lean.Compiler.LCNF
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/Basic.lean
ADDED
|
@@ -0,0 +1,755 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2022 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Init.Data.List.BasicAux
|
| 8 |
+
import Lean.Expr
|
| 9 |
+
import Lean.Meta.Instances
|
| 10 |
+
import Lean.Compiler.ExternAttr
|
| 11 |
+
import Lean.Compiler.InlineAttrs
|
| 12 |
+
import Lean.Compiler.Specialize
|
| 13 |
+
import Lean.Compiler.LCNF.Types
|
| 14 |
+
|
| 15 |
+
namespace Lean.Compiler.LCNF
|
| 16 |
+
|
| 17 |
+
/-!
|
| 18 |
+
# Lean Compiler Normal Form (LCNF)
|
| 19 |
+
|
| 20 |
+
It is based on the [A-normal form](https://en.wikipedia.org/wiki/A-normal_form),
|
| 21 |
+
and the approach described in the paper
|
| 22 |
+
[Compiling without continuations](https://www.microsoft.com/en-us/research/wp-content/uploads/2016/11/compiling-without-continuations.pdf).
|
| 23 |
+
|
| 24 |
+
-/
|
| 25 |
+
|
| 26 |
+
structure Param where
|
| 27 |
+
fvarId : FVarId
|
| 28 |
+
binderName : Name
|
| 29 |
+
type : Expr
|
| 30 |
+
borrow : Bool
|
| 31 |
+
deriving Inhabited, BEq
|
| 32 |
+
|
| 33 |
+
def Param.toExpr (p : Param) : Expr :=
|
| 34 |
+
.fvar p.fvarId
|
| 35 |
+
|
| 36 |
+
inductive LitValue where
|
| 37 |
+
| nat (val : Nat)
|
| 38 |
+
| str (val : String)
|
| 39 |
+
| uint8 (val : UInt8)
|
| 40 |
+
| uint16 (val : UInt16)
|
| 41 |
+
| uint32 (val : UInt32)
|
| 42 |
+
| uint64 (val : UInt64)
|
| 43 |
+
-- USize has a maximum size of 64 bits
|
| 44 |
+
| usize (val : UInt64)
|
| 45 |
+
-- TODO: add constructors for `Int`, `Float`, ...
|
| 46 |
+
deriving Inhabited, BEq, Hashable
|
| 47 |
+
|
| 48 |
+
def LitValue.toExpr : LitValue → Expr
|
| 49 |
+
| .nat v => .lit (.natVal v)
|
| 50 |
+
| .str v => .lit (.strVal v)
|
| 51 |
+
| .uint8 v => .app (.const ``UInt8.ofNat []) (.lit (.natVal (UInt8.toNat v)))
|
| 52 |
+
| .uint16 v => .app (.const ``UInt16.ofNat []) (.lit (.natVal (UInt16.toNat v)))
|
| 53 |
+
| .uint32 v => .app (.const ``UInt32.ofNat []) (.lit (.natVal (UInt32.toNat v)))
|
| 54 |
+
| .uint64 v => .app (.const ``UInt64.ofNat []) (.lit (.natVal (UInt64.toNat v)))
|
| 55 |
+
| .usize v => .app (.const ``USize.ofNat []) (.lit (.natVal (UInt64.toNat v)))
|
| 56 |
+
|
| 57 |
+
inductive Arg where
|
| 58 |
+
| erased
|
| 59 |
+
| fvar (fvarId : FVarId)
|
| 60 |
+
| type (expr : Expr)
|
| 61 |
+
deriving Inhabited, BEq, Hashable
|
| 62 |
+
|
| 63 |
+
def Param.toArg (p : Param) : Arg :=
|
| 64 |
+
.fvar p.fvarId
|
| 65 |
+
|
| 66 |
+
def Arg.toExpr (arg : Arg) : Expr :=
|
| 67 |
+
match arg with
|
| 68 |
+
| .erased => erasedExpr
|
| 69 |
+
| .fvar fvarId => .fvar fvarId
|
| 70 |
+
| .type e => e
|
| 71 |
+
|
| 72 |
+
private unsafe def Arg.updateTypeImp (arg : Arg) (type' : Expr) : Arg :=
|
| 73 |
+
match arg with
|
| 74 |
+
| .type ty => if ptrEq ty type' then arg else .type type'
|
| 75 |
+
| _ => unreachable!
|
| 76 |
+
|
| 77 |
+
@[implemented_by Arg.updateTypeImp] opaque Arg.updateType! (arg : Arg) (type : Expr) : Arg
|
| 78 |
+
|
| 79 |
+
private unsafe def Arg.updateFVarImp (arg : Arg) (fvarId' : FVarId) : Arg :=
|
| 80 |
+
match arg with
|
| 81 |
+
| .fvar fvarId => if fvarId' == fvarId then arg else .fvar fvarId'
|
| 82 |
+
| _ => unreachable!
|
| 83 |
+
|
| 84 |
+
@[implemented_by Arg.updateFVarImp] opaque Arg.updateFVar! (arg : Arg) (fvarId' : FVarId) : Arg
|
| 85 |
+
|
| 86 |
+
inductive LetValue where
|
| 87 |
+
| lit (value : LitValue)
|
| 88 |
+
| erased
|
| 89 |
+
| proj (typeName : Name) (idx : Nat) (struct : FVarId)
|
| 90 |
+
| const (declName : Name) (us : List Level) (args : Array Arg)
|
| 91 |
+
| fvar (fvarId : FVarId) (args : Array Arg)
|
| 92 |
+
deriving Inhabited, BEq, Hashable
|
| 93 |
+
|
| 94 |
+
def Arg.toLetValue (arg : Arg) : LetValue :=
|
| 95 |
+
match arg with
|
| 96 |
+
| .fvar fvarId => .fvar fvarId #[]
|
| 97 |
+
| .erased | .type .. => .erased
|
| 98 |
+
|
| 99 |
+
private unsafe def LetValue.updateProjImp (e : LetValue) (fvarId' : FVarId) : LetValue :=
|
| 100 |
+
match e with
|
| 101 |
+
| .proj s i fvarId => if fvarId == fvarId' then e else .proj s i fvarId'
|
| 102 |
+
| _ => unreachable!
|
| 103 |
+
|
| 104 |
+
@[implemented_by LetValue.updateProjImp] opaque LetValue.updateProj! (e : LetValue) (fvarId' : FVarId) : LetValue
|
| 105 |
+
|
| 106 |
+
private unsafe def LetValue.updateConstImp (e : LetValue) (declName' : Name) (us' : List Level) (args' : Array Arg) : LetValue :=
|
| 107 |
+
match e with
|
| 108 |
+
| .const declName us args => if declName == declName' && ptrEq us us' && ptrEq args args' then e else .const declName' us' args'
|
| 109 |
+
| _ => unreachable!
|
| 110 |
+
|
| 111 |
+
@[implemented_by LetValue.updateConstImp] opaque LetValue.updateConst! (e : LetValue) (declName' : Name) (us' : List Level) (args' : Array Arg) : LetValue
|
| 112 |
+
|
| 113 |
+
private unsafe def LetValue.updateFVarImp (e : LetValue) (fvarId' : FVarId) (args' : Array Arg) : LetValue :=
|
| 114 |
+
match e with
|
| 115 |
+
| .fvar fvarId args => if fvarId == fvarId' && ptrEq args args' then e else .fvar fvarId' args'
|
| 116 |
+
| _ => unreachable!
|
| 117 |
+
|
| 118 |
+
@[implemented_by LetValue.updateFVarImp] opaque LetValue.updateFVar! (e : LetValue) (fvarId' : FVarId) (args' : Array Arg) : LetValue
|
| 119 |
+
|
| 120 |
+
private unsafe def LetValue.updateArgsImp (e : LetValue) (args' : Array Arg) : LetValue :=
|
| 121 |
+
match e with
|
| 122 |
+
| .const declName us args => if ptrEq args args' then e else .const declName us args'
|
| 123 |
+
| .fvar fvarId args => if ptrEq args args' then e else .fvar fvarId args'
|
| 124 |
+
| _ => unreachable!
|
| 125 |
+
|
| 126 |
+
@[implemented_by LetValue.updateArgsImp] opaque LetValue.updateArgs! (e : LetValue) (args' : Array Arg) : LetValue
|
| 127 |
+
|
| 128 |
+
def LetValue.toExpr (e : LetValue) : Expr :=
|
| 129 |
+
match e with
|
| 130 |
+
| .lit v => v.toExpr
|
| 131 |
+
| .erased => erasedExpr
|
| 132 |
+
| .proj n i s => .proj n i (.fvar s)
|
| 133 |
+
| .const n us as => mkAppN (.const n us) (as.map Arg.toExpr)
|
| 134 |
+
| .fvar fvarId as => mkAppN (.fvar fvarId) (as.map Arg.toExpr)
|
| 135 |
+
|
| 136 |
+
structure LetDecl where
|
| 137 |
+
fvarId : FVarId
|
| 138 |
+
binderName : Name
|
| 139 |
+
type : Expr
|
| 140 |
+
value : LetValue
|
| 141 |
+
deriving Inhabited, BEq
|
| 142 |
+
|
| 143 |
+
mutual
|
| 144 |
+
|
| 145 |
+
inductive Alt where
|
| 146 |
+
| alt (ctorName : Name) (params : Array Param) (code : Code)
|
| 147 |
+
| default (code : Code)
|
| 148 |
+
|
| 149 |
+
structure FunDecl where
|
| 150 |
+
fvarId : FVarId
|
| 151 |
+
binderName : Name
|
| 152 |
+
params : Array Param
|
| 153 |
+
type : Expr
|
| 154 |
+
value : Code
|
| 155 |
+
|
| 156 |
+
structure Cases where
|
| 157 |
+
typeName : Name
|
| 158 |
+
resultType : Expr
|
| 159 |
+
discr : FVarId
|
| 160 |
+
alts : Array Alt
|
| 161 |
+
deriving Inhabited
|
| 162 |
+
|
| 163 |
+
inductive Code where
|
| 164 |
+
| let (decl : LetDecl) (k : Code)
|
| 165 |
+
| fun (decl : FunDecl) (k : Code)
|
| 166 |
+
| jp (decl : FunDecl) (k : Code)
|
| 167 |
+
| jmp (fvarId : FVarId) (args : Array Arg)
|
| 168 |
+
| cases (cases : Cases)
|
| 169 |
+
| return (fvarId : FVarId)
|
| 170 |
+
| unreach (type : Expr)
|
| 171 |
+
deriving Inhabited
|
| 172 |
+
|
| 173 |
+
end
|
| 174 |
+
|
| 175 |
+
deriving instance Inhabited for Alt
|
| 176 |
+
deriving instance Inhabited for FunDecl
|
| 177 |
+
|
| 178 |
+
def FunDecl.getArity (decl : FunDecl) : Nat :=
|
| 179 |
+
decl.params.size
|
| 180 |
+
|
| 181 |
+
/--
|
| 182 |
+
Return the constructor names that have an explicit (non-default) alternative.
|
| 183 |
+
-/
|
| 184 |
+
def Cases.getCtorNames (c : Cases) : NameSet :=
|
| 185 |
+
c.alts.foldl (init := {}) fun ctorNames alt =>
|
| 186 |
+
match alt with
|
| 187 |
+
| .default _ => ctorNames
|
| 188 |
+
| .alt ctorName .. => ctorNames.insert ctorName
|
| 189 |
+
|
| 190 |
+
inductive CodeDecl where
|
| 191 |
+
| let (decl : LetDecl)
|
| 192 |
+
| fun (decl : FunDecl)
|
| 193 |
+
| jp (decl : FunDecl)
|
| 194 |
+
deriving Inhabited
|
| 195 |
+
|
| 196 |
+
def CodeDecl.fvarId : CodeDecl → FVarId
|
| 197 |
+
| .let decl | .fun decl | .jp decl => decl.fvarId
|
| 198 |
+
|
| 199 |
+
def attachCodeDecls (decls : Array CodeDecl) (code : Code) : Code :=
|
| 200 |
+
go decls.size code
|
| 201 |
+
where
|
| 202 |
+
go (i : Nat) (code : Code) : Code :=
|
| 203 |
+
if i > 0 then
|
| 204 |
+
match decls[i-1]! with
|
| 205 |
+
| .let decl => go (i-1) (.let decl code)
|
| 206 |
+
| .fun decl => go (i-1) (.fun decl code)
|
| 207 |
+
| .jp decl => go (i-1) (.jp decl code)
|
| 208 |
+
else
|
| 209 |
+
code
|
| 210 |
+
|
| 211 |
+
mutual
|
| 212 |
+
private unsafe def eqImp (c₁ c₂ : Code) : Bool :=
|
| 213 |
+
if ptrEq c₁ c₂ then
|
| 214 |
+
true
|
| 215 |
+
else match c₁, c₂ with
|
| 216 |
+
| .let d₁ k₁, .let d₂ k₂ => d₁ == d₂ && eqImp k₁ k₂
|
| 217 |
+
| .fun d₁ k₁, .fun d₂ k₂
|
| 218 |
+
| .jp d₁ k₁, .jp d₂ k₂ => eqFunDecl d₁ d₂ && eqImp k₁ k₂
|
| 219 |
+
| .cases c₁, .cases c₂ => eqCases c₁ c₂
|
| 220 |
+
| .jmp j₁ as₁, .jmp j₂ as₂ => j₁ == j₂ && as₁ == as₂
|
| 221 |
+
| .return r₁, .return r₂ => r₁ == r₂
|
| 222 |
+
| .unreach t₁, .unreach t₂ => t₁ == t₂
|
| 223 |
+
| _, _ => false
|
| 224 |
+
|
| 225 |
+
private unsafe def eqFunDecl (d₁ d₂ : FunDecl) : Bool :=
|
| 226 |
+
if ptrEq d₁ d₂ then
|
| 227 |
+
true
|
| 228 |
+
else
|
| 229 |
+
d₁.fvarId == d₂.fvarId && d₁.binderName == d₂.binderName &&
|
| 230 |
+
d₁.params == d₂.params && d₁.type == d₂.type &&
|
| 231 |
+
eqImp d₁.value d₂.value
|
| 232 |
+
|
| 233 |
+
private unsafe def eqCases (c₁ c₂ : Cases) : Bool :=
|
| 234 |
+
c₁.resultType == c₂.resultType && c₁.discr == c₂.discr &&
|
| 235 |
+
c₁.typeName == c₂.typeName && c₁.alts.isEqv c₂.alts eqAlt
|
| 236 |
+
|
| 237 |
+
private unsafe def eqAlt (a₁ a₂ : Alt) : Bool :=
|
| 238 |
+
match a₁, a₂ with
|
| 239 |
+
| .default k₁, .default k₂ => eqImp k₁ k₂
|
| 240 |
+
| .alt c₁ ps₁ k₁, .alt c₂ ps₂ k₂ => c₁ == c₂ && ps₁ == ps₂ && eqImp k₁ k₂
|
| 241 |
+
| _, _ => false
|
| 242 |
+
end
|
| 243 |
+
|
| 244 |
+
@[implemented_by eqImp] protected opaque Code.beq : Code → Code → Bool
|
| 245 |
+
|
| 246 |
+
instance : BEq Code where
|
| 247 |
+
beq := Code.beq
|
| 248 |
+
|
| 249 |
+
@[implemented_by eqFunDecl] protected opaque FunDecl.beq : FunDecl → FunDecl → Bool
|
| 250 |
+
|
| 251 |
+
instance : BEq FunDecl where
|
| 252 |
+
beq := FunDecl.beq
|
| 253 |
+
|
| 254 |
+
def Alt.getCode : Alt → Code
|
| 255 |
+
| .default k => k
|
| 256 |
+
| .alt _ _ k => k
|
| 257 |
+
|
| 258 |
+
def Alt.getParams : Alt → Array Param
|
| 259 |
+
| .default _ => #[]
|
| 260 |
+
| .alt _ ps _ => ps
|
| 261 |
+
|
| 262 |
+
def Alt.forCodeM [Monad m] (alt : Alt) (f : Code → m Unit) : m Unit := do
|
| 263 |
+
match alt with
|
| 264 |
+
| .default k => f k
|
| 265 |
+
| .alt _ _ k => f k
|
| 266 |
+
|
| 267 |
+
private unsafe def updateAltCodeImp (alt : Alt) (k' : Code) : Alt :=
|
| 268 |
+
match alt with
|
| 269 |
+
| .default k => if ptrEq k k' then alt else .default k'
|
| 270 |
+
| .alt ctorName ps k => if ptrEq k k' then alt else .alt ctorName ps k'
|
| 271 |
+
|
| 272 |
+
@[implemented_by updateAltCodeImp] opaque Alt.updateCode (alt : Alt) (c : Code) : Alt
|
| 273 |
+
|
| 274 |
+
private unsafe def updateAltImp (alt : Alt) (ps' : Array Param) (k' : Code) : Alt :=
|
| 275 |
+
match alt with
|
| 276 |
+
| .alt ctorName ps k => if ptrEq k k' && ptrEq ps ps' then alt else .alt ctorName ps' k'
|
| 277 |
+
| _ => unreachable!
|
| 278 |
+
|
| 279 |
+
@[implemented_by updateAltImp] opaque Alt.updateAlt! (alt : Alt) (ps' : Array Param) (k' : Code) : Alt
|
| 280 |
+
|
| 281 |
+
@[inline] private unsafe def updateAltsImp (c : Code) (alts : Array Alt) : Code :=
|
| 282 |
+
match c with
|
| 283 |
+
| .cases cs => if ptrEq cs.alts alts then c else .cases { cs with alts }
|
| 284 |
+
| _ => unreachable!
|
| 285 |
+
|
| 286 |
+
@[implemented_by updateAltsImp] opaque Code.updateAlts! (c : Code) (alts : Array Alt) : Code
|
| 287 |
+
|
| 288 |
+
@[inline] private unsafe def updateCasesImp (c : Code) (resultType : Expr) (discr : FVarId) (alts : Array Alt) : Code :=
|
| 289 |
+
match c with
|
| 290 |
+
| .cases cs => if ptrEq cs.alts alts && ptrEq cs.resultType resultType && cs.discr == discr then c else .cases { cs with discr, resultType, alts }
|
| 291 |
+
| _ => unreachable!
|
| 292 |
+
|
| 293 |
+
@[implemented_by updateCasesImp] opaque Code.updateCases! (c : Code) (resultType : Expr) (discr : FVarId) (alts : Array Alt) : Code
|
| 294 |
+
|
| 295 |
+
@[inline] private unsafe def updateLetImp (c : Code) (decl' : LetDecl) (k' : Code) : Code :=
|
| 296 |
+
match c with
|
| 297 |
+
| .let decl k => if ptrEq k k' && ptrEq decl decl' then c else .let decl' k'
|
| 298 |
+
| _ => unreachable!
|
| 299 |
+
|
| 300 |
+
@[implemented_by updateLetImp] opaque Code.updateLet! (c : Code) (decl' : LetDecl) (k' : Code) : Code
|
| 301 |
+
|
| 302 |
+
@[inline] private unsafe def updateContImp (c : Code) (k' : Code) : Code :=
|
| 303 |
+
match c with
|
| 304 |
+
| .let decl k => if ptrEq k k' then c else .let decl k'
|
| 305 |
+
| .fun decl k => if ptrEq k k' then c else .fun decl k'
|
| 306 |
+
| .jp decl k => if ptrEq k k' then c else .jp decl k'
|
| 307 |
+
| _ => unreachable!
|
| 308 |
+
|
| 309 |
+
@[implemented_by updateContImp] opaque Code.updateCont! (c : Code) (k' : Code) : Code
|
| 310 |
+
|
| 311 |
+
@[inline] private unsafe def updateFunImp (c : Code) (decl' : FunDecl) (k' : Code) : Code :=
|
| 312 |
+
match c with
|
| 313 |
+
| .fun decl k => if ptrEq k k' && ptrEq decl decl' then c else .fun decl' k'
|
| 314 |
+
| .jp decl k => if ptrEq k k' && ptrEq decl decl' then c else .jp decl' k'
|
| 315 |
+
| _ => unreachable!
|
| 316 |
+
|
| 317 |
+
@[implemented_by updateFunImp] opaque Code.updateFun! (c : Code) (decl' : FunDecl) (k' : Code) : Code
|
| 318 |
+
|
| 319 |
+
@[inline] private unsafe def updateReturnImp (c : Code) (fvarId' : FVarId) : Code :=
|
| 320 |
+
match c with
|
| 321 |
+
| .return fvarId => if fvarId == fvarId' then c else .return fvarId'
|
| 322 |
+
| _ => unreachable!
|
| 323 |
+
|
| 324 |
+
@[implemented_by updateReturnImp] opaque Code.updateReturn! (c : Code) (fvarId' : FVarId) : Code
|
| 325 |
+
|
| 326 |
+
@[inline] private unsafe def updateJmpImp (c : Code) (fvarId' : FVarId) (args' : Array Arg) : Code :=
|
| 327 |
+
match c with
|
| 328 |
+
| .jmp fvarId args => if fvarId == fvarId' && ptrEq args args' then c else .jmp fvarId' args'
|
| 329 |
+
| _ => unreachable!
|
| 330 |
+
|
| 331 |
+
@[implemented_by updateJmpImp] opaque Code.updateJmp! (c : Code) (fvarId' : FVarId) (args' : Array Arg) : Code
|
| 332 |
+
|
| 333 |
+
@[inline] private unsafe def updateUnreachImp (c : Code) (type' : Expr) : Code :=
|
| 334 |
+
match c with
|
| 335 |
+
| .unreach type => if ptrEq type type' then c else .unreach type'
|
| 336 |
+
| _ => unreachable!
|
| 337 |
+
|
| 338 |
+
@[implemented_by updateUnreachImp] opaque Code.updateUnreach! (c : Code) (type' : Expr) : Code
|
| 339 |
+
|
| 340 |
+
private unsafe def updateParamCoreImp (p : Param) (type : Expr) : Param :=
|
| 341 |
+
if ptrEq type p.type then
|
| 342 |
+
p
|
| 343 |
+
else
|
| 344 |
+
{ p with type }
|
| 345 |
+
|
| 346 |
+
/--
|
| 347 |
+
Low-level update `Param` function. It does not update the local context.
|
| 348 |
+
Consider using `Param.update : Param → Expr → CompilerM Param` if you want the local context
|
| 349 |
+
to be updated.
|
| 350 |
+
-/
|
| 351 |
+
@[implemented_by updateParamCoreImp] opaque Param.updateCore (p : Param) (type : Expr) : Param
|
| 352 |
+
|
| 353 |
+
private unsafe def updateLetDeclCoreImp (decl : LetDecl) (type : Expr) (value : LetValue) : LetDecl :=
|
| 354 |
+
if ptrEq type decl.type && ptrEq value decl.value then
|
| 355 |
+
decl
|
| 356 |
+
else
|
| 357 |
+
{ decl with type, value }
|
| 358 |
+
|
| 359 |
+
/--
|
| 360 |
+
Low-level update `LetDecl` function. It does not update the local context.
|
| 361 |
+
Consider using `LetDecl.update : LetDecl → Expr → Expr → CompilerM LetDecl` if you want the local context
|
| 362 |
+
to be updated.
|
| 363 |
+
-/
|
| 364 |
+
@[implemented_by updateLetDeclCoreImp] opaque LetDecl.updateCore (decl : LetDecl) (type : Expr) (value : LetValue) : LetDecl
|
| 365 |
+
|
| 366 |
+
private unsafe def updateFunDeclCoreImp (decl: FunDecl) (type : Expr) (params : Array Param) (value : Code) : FunDecl :=
|
| 367 |
+
if ptrEq type decl.type && ptrEq params decl.params && ptrEq value decl.value then
|
| 368 |
+
decl
|
| 369 |
+
else
|
| 370 |
+
{ decl with type, params, value }
|
| 371 |
+
|
| 372 |
+
/--
|
| 373 |
+
Low-level update `FunDecl` function. It does not update the local context.
|
| 374 |
+
Consider using `FunDecl.update : LetDecl → Expr → Array Param → Code → CompilerM FunDecl` if you want the local context
|
| 375 |
+
to be updated.
|
| 376 |
+
-/
|
| 377 |
+
@[implemented_by updateFunDeclCoreImp] opaque FunDecl.updateCore (decl : FunDecl) (type : Expr) (params : Array Param) (value : Code) : FunDecl
|
| 378 |
+
|
| 379 |
+
def Cases.extractAlt! (cases : Cases) (ctorName : Name) : Alt × Cases :=
|
| 380 |
+
let found i := (cases.alts[i], { cases with alts := cases.alts.eraseIdx i })
|
| 381 |
+
if let some i := cases.alts.findFinIdx? fun | .alt ctorName' .. => ctorName == ctorName' | _ => false then
|
| 382 |
+
found i
|
| 383 |
+
else if let some i := cases.alts.findFinIdx? fun | .default _ => true | _ => false then
|
| 384 |
+
found i
|
| 385 |
+
else
|
| 386 |
+
unreachable!
|
| 387 |
+
|
| 388 |
+
def Alt.mapCodeM [Monad m] (alt : Alt) (f : Code → m Code) : m Alt := do
|
| 389 |
+
return alt.updateCode (← f alt.getCode)
|
| 390 |
+
|
| 391 |
+
def Code.isDecl : Code → Bool
|
| 392 |
+
| .let .. | .fun .. | .jp .. => true
|
| 393 |
+
| _ => false
|
| 394 |
+
|
| 395 |
+
def Code.isFun : Code → Bool
|
| 396 |
+
| .fun .. => true
|
| 397 |
+
| _ => false
|
| 398 |
+
|
| 399 |
+
def Code.isReturnOf : Code → FVarId → Bool
|
| 400 |
+
| .return fvarId, fvarId' => fvarId == fvarId'
|
| 401 |
+
| _, _ => false
|
| 402 |
+
|
| 403 |
+
partial def Code.size (c : Code) : Nat :=
|
| 404 |
+
go c 0
|
| 405 |
+
where
|
| 406 |
+
go (c : Code) (n : Nat) : Nat :=
|
| 407 |
+
match c with
|
| 408 |
+
| .let _ k => go k (n+1)
|
| 409 |
+
| .jp decl k | .fun decl k => go k <| go decl.value n
|
| 410 |
+
| .cases c => c.alts.foldl (init := n+1) fun n alt => go alt.getCode (n+1)
|
| 411 |
+
| .jmp .. => n+1
|
| 412 |
+
| .return .. | unreach .. => n -- `return` & `unreach` have weight zero
|
| 413 |
+
|
| 414 |
+
/-- Return true iff `c.size ≤ n` -/
|
| 415 |
+
partial def Code.sizeLe (c : Code) (n : Nat) : Bool :=
|
| 416 |
+
match go c |>.run 0 with
|
| 417 |
+
| .ok .. => true
|
| 418 |
+
| .error .. => false
|
| 419 |
+
where
|
| 420 |
+
inc : EStateM Unit Nat Unit := do
|
| 421 |
+
modify (·+1)
|
| 422 |
+
unless (← get) <= n do throw ()
|
| 423 |
+
|
| 424 |
+
go (c : Code) : EStateM Unit Nat Unit := do
|
| 425 |
+
match c with
|
| 426 |
+
| .let _ k => inc; go k
|
| 427 |
+
| .jp decl k | .fun decl k => inc; go decl.value; go k
|
| 428 |
+
| .cases c => inc; c.alts.forM fun alt => go alt.getCode
|
| 429 |
+
| .jmp .. => inc
|
| 430 |
+
| .return .. | unreach .. => return ()
|
| 431 |
+
|
| 432 |
+
partial def Code.forM [Monad m] (c : Code) (f : Code → m Unit) : m Unit :=
|
| 433 |
+
go c
|
| 434 |
+
where
|
| 435 |
+
go (c : Code) : m Unit := do
|
| 436 |
+
f c
|
| 437 |
+
match c with
|
| 438 |
+
| .let _ k => go k
|
| 439 |
+
| .fun decl k | .jp decl k => go decl.value; go k
|
| 440 |
+
| .cases c => c.alts.forM fun alt => go alt.getCode
|
| 441 |
+
| .unreach .. | .return .. | .jmp .. => return ()
|
| 442 |
+
|
| 443 |
+
partial def Code.instantiateValueLevelParams (code : Code) (levelParams : List Name) (us : List Level) : Code :=
|
| 444 |
+
instCode code
|
| 445 |
+
where
|
| 446 |
+
instLevel (u : Level) :=
|
| 447 |
+
u.instantiateParams levelParams us
|
| 448 |
+
|
| 449 |
+
instExpr (e : Expr) :=
|
| 450 |
+
e.instantiateLevelParamsNoCache levelParams us
|
| 451 |
+
|
| 452 |
+
instParams (ps : Array Param) :=
|
| 453 |
+
ps.mapMono fun p => p.updateCore (instExpr p.type)
|
| 454 |
+
|
| 455 |
+
instAlt (alt : Alt) :=
|
| 456 |
+
match alt with
|
| 457 |
+
| .default k => alt.updateCode (instCode k)
|
| 458 |
+
| .alt _ ps k => alt.updateAlt! (instParams ps) (instCode k)
|
| 459 |
+
|
| 460 |
+
instArg (arg : Arg) : Arg :=
|
| 461 |
+
match arg with
|
| 462 |
+
| .type e => arg.updateType! (instExpr e)
|
| 463 |
+
| .fvar .. | .erased => arg
|
| 464 |
+
|
| 465 |
+
instLetValue (e : LetValue) : LetValue :=
|
| 466 |
+
match e with
|
| 467 |
+
| .const declName vs args => e.updateConst! declName (vs.mapMono instLevel) (args.mapMono instArg)
|
| 468 |
+
| .fvar fvarId args => e.updateFVar! fvarId (args.mapMono instArg)
|
| 469 |
+
| .proj .. | .lit .. | .erased => e
|
| 470 |
+
|
| 471 |
+
instLetDecl (decl : LetDecl) :=
|
| 472 |
+
decl.updateCore (instExpr decl.type) (instLetValue decl.value)
|
| 473 |
+
|
| 474 |
+
instFunDecl (decl : FunDecl) :=
|
| 475 |
+
decl.updateCore (instExpr decl.type) (instParams decl.params) (instCode decl.value)
|
| 476 |
+
|
| 477 |
+
instCode (code : Code) :=
|
| 478 |
+
match code with
|
| 479 |
+
| .let decl k => code.updateLet! (instLetDecl decl) (instCode k)
|
| 480 |
+
| .jp decl k | .fun decl k => code.updateFun! (instFunDecl decl) (instCode k)
|
| 481 |
+
| .cases c => code.updateCases! (instExpr c.resultType) c.discr (c.alts.mapMono instAlt)
|
| 482 |
+
| .jmp fvarId args => code.updateJmp! fvarId (args.mapMono instArg)
|
| 483 |
+
| .return .. => code
|
| 484 |
+
| .unreach type => code.updateUnreach! (instExpr type)
|
| 485 |
+
|
| 486 |
+
inductive DeclValue where
|
| 487 |
+
| code (code : Code)
|
| 488 |
+
| extern (externAttrData : ExternAttrData)
|
| 489 |
+
deriving Inhabited, BEq
|
| 490 |
+
|
| 491 |
+
partial def DeclValue.size : DeclValue → Nat
|
| 492 |
+
| .code c => c.size
|
| 493 |
+
| .extern .. => 0
|
| 494 |
+
|
| 495 |
+
def DeclValue.mapCode (f : Code → Code) : DeclValue → DeclValue :=
|
| 496 |
+
fun
|
| 497 |
+
| .code c => .code (f c)
|
| 498 |
+
| .extern e => .extern e
|
| 499 |
+
|
| 500 |
+
def DeclValue.mapCodeM [Monad m] (f : Code → m Code) : DeclValue → m DeclValue :=
|
| 501 |
+
fun v => do
|
| 502 |
+
match v with
|
| 503 |
+
| .code c => return .code (← f c)
|
| 504 |
+
| .extern .. => return v
|
| 505 |
+
|
| 506 |
+
def DeclValue.forCodeM [Monad m] (f : Code → m Unit) : DeclValue → m Unit :=
|
| 507 |
+
fun v => do
|
| 508 |
+
match v with
|
| 509 |
+
| .code c => f c
|
| 510 |
+
| .extern .. => return ()
|
| 511 |
+
|
| 512 |
+
def DeclValue.isCodeAndM [Monad m] (v : DeclValue) (f : Code → m Bool) : m Bool :=
|
| 513 |
+
match v with
|
| 514 |
+
| .code c => f c
|
| 515 |
+
| .extern .. => pure false
|
| 516 |
+
|
| 517 |
+
/--
|
| 518 |
+
Declaration being processed by the Lean to Lean compiler passes.
|
| 519 |
+
-/
|
| 520 |
+
structure Decl where
|
| 521 |
+
/--
|
| 522 |
+
The name of the declaration from the `Environment` it came from
|
| 523 |
+
-/
|
| 524 |
+
name : Name
|
| 525 |
+
/--
|
| 526 |
+
Universe level parameter names.
|
| 527 |
+
-/
|
| 528 |
+
levelParams : List Name
|
| 529 |
+
/--
|
| 530 |
+
The type of the declaration. Note that this is an erased LCNF type
|
| 531 |
+
instead of the fully dependent one that might have been the original
|
| 532 |
+
type of the declaration in the `Environment`.
|
| 533 |
+
-/
|
| 534 |
+
type : Expr
|
| 535 |
+
/--
|
| 536 |
+
Parameters.
|
| 537 |
+
-/
|
| 538 |
+
params : Array Param
|
| 539 |
+
/--
|
| 540 |
+
The body of the declaration, usually changes as it progresses
|
| 541 |
+
through compiler passes.
|
| 542 |
+
-/
|
| 543 |
+
value : DeclValue
|
| 544 |
+
/--
|
| 545 |
+
We set this flag to true during LCNF conversion. When we receive
|
| 546 |
+
a block of functions to be compiled, we set this flag to `true`
|
| 547 |
+
if there is an application to the function in the block containing
|
| 548 |
+
it. This is an approximation, but it should be good enough because
|
| 549 |
+
in the frontend, we invoke the compiler with blocks of strongly connected
|
| 550 |
+
components only.
|
| 551 |
+
We use this information to control inlining.
|
| 552 |
+
-/
|
| 553 |
+
recursive : Bool := false
|
| 554 |
+
/--
|
| 555 |
+
We set this flag to false during LCNF conversion if the Lean function
|
| 556 |
+
associated with this function was tagged as partial or unsafe. This
|
| 557 |
+
information affects how static analyzers treat function applications
|
| 558 |
+
of this kind. See `DefinitionSafety`.
|
| 559 |
+
`partial` and `unsafe` functions may not be terminating, but Lean
|
| 560 |
+
functions terminate, and some static analyzers exploit this
|
| 561 |
+
fact. So, we use the following semantics. Suppose we have a (large) natural
|
| 562 |
+
number `C`. We consider a nondeterministic model for computation of Lean expressions as
|
| 563 |
+
follows:
|
| 564 |
+
Each call to a partial/unsafe function uses up one "recursion token".
|
| 565 |
+
Prior to consuming `C` recursion tokens all partial functions must be called
|
| 566 |
+
as normal. Once the model has used up `C` recursion tokens, a subsequent call to
|
| 567 |
+
a partial function has the following nondeterministic options: it can either call
|
| 568 |
+
the function again, or return any value of the target type (even a noncomputable one).
|
| 569 |
+
Larger values of `C` yield less nondeterminism in the model, but even the intersection of
|
| 570 |
+
all choices of `C` yields nondeterminism where `def loop : A := loop` returns any value of type `A`.
|
| 571 |
+
The compiler fixes a choice for `C`. This is a fixed constant greater than 2^2^64,
|
| 572 |
+
which is allowed to be compiler and architecture dependent, and promises that it will
|
| 573 |
+
produce an execution consistent with every possible nondeterministic outcome of the `C`-model.
|
| 574 |
+
In the event that different nondeterministic executions disagree, the compiler is required to
|
| 575 |
+
exhaust resources or output a looping computation.
|
| 576 |
+
-/
|
| 577 |
+
safe : Bool := true
|
| 578 |
+
/--
|
| 579 |
+
We store the inline attribute at LCNF declarations to make sure we can set them for
|
| 580 |
+
auxiliary declarations created during compilation.
|
| 581 |
+
-/
|
| 582 |
+
inlineAttr? : Option InlineAttributeKind
|
| 583 |
+
deriving Inhabited, BEq
|
| 584 |
+
|
| 585 |
+
def Decl.size (decl : Decl) : Nat :=
|
| 586 |
+
decl.value.size
|
| 587 |
+
|
| 588 |
+
def Decl.getArity (decl : Decl) : Nat :=
|
| 589 |
+
decl.params.size
|
| 590 |
+
|
| 591 |
+
def Decl.inlineAttr (decl : Decl) : Bool :=
|
| 592 |
+
decl.inlineAttr? matches some .inline
|
| 593 |
+
|
| 594 |
+
def Decl.noinlineAttr (decl : Decl) : Bool :=
|
| 595 |
+
decl.inlineAttr? matches some .noinline
|
| 596 |
+
|
| 597 |
+
def Decl.inlineIfReduceAttr (decl : Decl) : Bool :=
|
| 598 |
+
decl.inlineAttr? matches some .inlineIfReduce
|
| 599 |
+
|
| 600 |
+
def Decl.alwaysInlineAttr (decl : Decl) : Bool :=
|
| 601 |
+
decl.inlineAttr? matches some .alwaysInline
|
| 602 |
+
|
| 603 |
+
/-- Return `true` if the given declaration has been annotated with `[inline]`, `[inline_if_reduce]`, `[macro_inline]`, or `[always_inline]` -/
|
| 604 |
+
def Decl.inlineable (decl : Decl) : Bool :=
|
| 605 |
+
match decl.inlineAttr? with
|
| 606 |
+
| some .noinline => false
|
| 607 |
+
| some _ => true
|
| 608 |
+
| none => false
|
| 609 |
+
|
| 610 |
+
/--
|
| 611 |
+
Return `some i` if `decl` is of the form
|
| 612 |
+
```
|
| 613 |
+
def f (a_0 ... a_i ...) :=
|
| 614 |
+
...
|
| 615 |
+
cases a_i
|
| 616 |
+
| ...
|
| 617 |
+
| ...
|
| 618 |
+
```
|
| 619 |
+
That is, `f` is a sequence of declarations followed by a `cases` on the parameter `i`.
|
| 620 |
+
We use this function to decide whether we should inline a declaration tagged with
|
| 621 |
+
`[inline_if_reduce]` or not.
|
| 622 |
+
-/
|
| 623 |
+
def Decl.isCasesOnParam? (decl : Decl) : Option Nat :=
|
| 624 |
+
match decl.value with
|
| 625 |
+
| .code c => go c
|
| 626 |
+
| .extern .. => none
|
| 627 |
+
where
|
| 628 |
+
go (code : Code) : Option Nat :=
|
| 629 |
+
match code with
|
| 630 |
+
| .let _ k | .jp _ k | .fun _ k => go k
|
| 631 |
+
| .cases c => decl.params.findIdx? fun param => param.fvarId == c.discr
|
| 632 |
+
| _ => none
|
| 633 |
+
|
| 634 |
+
def Decl.instantiateTypeLevelParams (decl : Decl) (us : List Level) : Expr :=
|
| 635 |
+
decl.type.instantiateLevelParamsNoCache decl.levelParams us
|
| 636 |
+
|
| 637 |
+
def Decl.instantiateParamsLevelParams (decl : Decl) (us : List Level) : Array Param :=
|
| 638 |
+
decl.params.mapMono fun param => param.updateCore (param.type.instantiateLevelParamsNoCache decl.levelParams us)
|
| 639 |
+
|
| 640 |
+
/--
|
| 641 |
+
Return `true` if the arrow type contains an instance implicit argument.
|
| 642 |
+
-/
|
| 643 |
+
def hasLocalInst (type : Expr) : Bool :=
|
| 644 |
+
match type with
|
| 645 |
+
| .forallE _ _ b bi => bi.isInstImplicit || hasLocalInst b
|
| 646 |
+
| _ => false
|
| 647 |
+
|
| 648 |
+
/--
|
| 649 |
+
Return `true` if `decl` is supposed to be inlined/specialized.
|
| 650 |
+
-/
|
| 651 |
+
def Decl.isTemplateLike (decl : Decl) : CoreM Bool := do
|
| 652 |
+
if hasLocalInst decl.type then
|
| 653 |
+
return true -- `decl` applications will be specialized
|
| 654 |
+
else if (← Meta.isInstance decl.name) then
|
| 655 |
+
return true -- `decl` is "fuel" for code specialization
|
| 656 |
+
else if decl.inlineable || hasSpecializeAttribute (← getEnv) decl.name then
|
| 657 |
+
return true -- `decl` is going to be inlined or specialized
|
| 658 |
+
else
|
| 659 |
+
return false
|
| 660 |
+
|
| 661 |
+
private partial def collectType (e : Expr) : FVarIdHashSet → FVarIdHashSet :=
|
| 662 |
+
match e with
|
| 663 |
+
| .forallE _ d b _ => collectType b ∘ collectType d
|
| 664 |
+
| .lam _ d b _ => collectType b ∘ collectType d
|
| 665 |
+
| .app f a => collectType f ∘ collectType a
|
| 666 |
+
| .fvar fvarId => fun s => s.insert fvarId
|
| 667 |
+
| .mdata _ b => collectType b
|
| 668 |
+
| .proj .. | .letE .. => unreachable!
|
| 669 |
+
| _ => id
|
| 670 |
+
|
| 671 |
+
private def collectArg (arg : Arg) (s : FVarIdHashSet) : FVarIdHashSet :=
|
| 672 |
+
match arg with
|
| 673 |
+
| .erased => s
|
| 674 |
+
| .fvar fvarId => s.insert fvarId
|
| 675 |
+
| .type e => collectType e s
|
| 676 |
+
|
| 677 |
+
private def collectArgs (args : Array Arg) (s : FVarIdHashSet) : FVarIdHashSet :=
|
| 678 |
+
args.foldl (init := s) fun s arg => collectArg arg s
|
| 679 |
+
|
| 680 |
+
private def collectLetValue (e : LetValue) (s : FVarIdHashSet) : FVarIdHashSet :=
|
| 681 |
+
match e with
|
| 682 |
+
| .fvar fvarId args => collectArgs args <| s.insert fvarId
|
| 683 |
+
| .const _ _ args => collectArgs args s
|
| 684 |
+
| .proj _ _ fvarId => s.insert fvarId
|
| 685 |
+
| .lit .. | .erased => s
|
| 686 |
+
|
| 687 |
+
private partial def collectParams (ps : Array Param) (s : FVarIdHashSet) : FVarIdHashSet :=
|
| 688 |
+
ps.foldl (init := s) fun s p => collectType p.type s
|
| 689 |
+
|
| 690 |
+
mutual
|
| 691 |
+
partial def FunDecl.collectUsed (decl : FunDecl) (s : FVarIdHashSet := {}) : FVarIdHashSet :=
|
| 692 |
+
decl.value.collectUsed <| collectParams decl.params <| collectType decl.type s
|
| 693 |
+
|
| 694 |
+
partial def Code.collectUsed (code : Code) (s : FVarIdHashSet := {}) : FVarIdHashSet :=
|
| 695 |
+
match code with
|
| 696 |
+
| .let decl k => k.collectUsed <| collectLetValue decl.value <| collectType decl.type s
|
| 697 |
+
| .jp decl k | .fun decl k => k.collectUsed <| decl.collectUsed s
|
| 698 |
+
| .cases c =>
|
| 699 |
+
let s := s.insert c.discr
|
| 700 |
+
let s := collectType c.resultType s
|
| 701 |
+
c.alts.foldl (init := s) fun s alt =>
|
| 702 |
+
match alt with
|
| 703 |
+
| .default k => k.collectUsed s
|
| 704 |
+
| .alt _ ps k => k.collectUsed <| collectParams ps s
|
| 705 |
+
| .return fvarId => s.insert fvarId
|
| 706 |
+
| .unreach type => collectType type s
|
| 707 |
+
| .jmp fvarId args => collectArgs args <| s.insert fvarId
|
| 708 |
+
end
|
| 709 |
+
|
| 710 |
+
abbrev collectUsedAtExpr (s : FVarIdHashSet) (e : Expr) : FVarIdHashSet :=
|
| 711 |
+
collectType e s
|
| 712 |
+
|
| 713 |
+
/--
|
| 714 |
+
Traverse the given block of potentially mutually recursive functions
|
| 715 |
+
and mark a declaration `f` as recursive if there is an application
|
| 716 |
+
`f ...` in the block.
|
| 717 |
+
This is an overapproximation, and relies on the fact that our frontend
|
| 718 |
+
computes strongly connected components.
|
| 719 |
+
See comment at `recursive` field.
|
| 720 |
+
-/
|
| 721 |
+
partial def markRecDecls (decls : Array Decl) : Array Decl :=
|
| 722 |
+
let (_, isRec) := go |>.run {}
|
| 723 |
+
decls.map fun decl =>
|
| 724 |
+
if isRec.contains decl.name then
|
| 725 |
+
{ decl with recursive := true }
|
| 726 |
+
else
|
| 727 |
+
decl
|
| 728 |
+
where
|
| 729 |
+
visit (code : Code) : StateM NameSet Unit := do
|
| 730 |
+
match code with
|
| 731 |
+
| .jp decl k | .fun decl k => visit decl.value; visit k
|
| 732 |
+
| .cases c => c.alts.forM fun alt => visit alt.getCode
|
| 733 |
+
| .unreach .. | .jmp .. | .return .. => return ()
|
| 734 |
+
| .let decl k =>
|
| 735 |
+
if let .const declName _ _ := decl.value then
|
| 736 |
+
if decls.any (·.name == declName) then
|
| 737 |
+
modify fun s => s.insert declName
|
| 738 |
+
visit k
|
| 739 |
+
|
| 740 |
+
go : StateM NameSet Unit :=
|
| 741 |
+
decls.forM (·.value.forCodeM visit)
|
| 742 |
+
|
| 743 |
+
def instantiateRangeArgs (e : Expr) (beginIdx endIdx : Nat) (args : Array Arg) : Expr :=
|
| 744 |
+
if !e.hasLooseBVars then
|
| 745 |
+
e
|
| 746 |
+
else
|
| 747 |
+
e.instantiateRange beginIdx endIdx (args.map (·.toExpr))
|
| 748 |
+
|
| 749 |
+
def instantiateRevRangeArgs (e : Expr) (beginIdx endIdx : Nat) (args : Array Arg) : Expr :=
|
| 750 |
+
if !e.hasLooseBVars then
|
| 751 |
+
e
|
| 752 |
+
else
|
| 753 |
+
e.instantiateRevRange beginIdx endIdx (args.map (·.toExpr))
|
| 754 |
+
|
| 755 |
+
end Lean.Compiler.LCNF
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/Bind.lean
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2022 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Compiler.LCNF.InferType
|
| 8 |
+
|
| 9 |
+
namespace Lean.Compiler.LCNF
|
| 10 |
+
|
| 11 |
+
/-- Helper class for lifting `CompilerM.codeBind` -/
|
| 12 |
+
class MonadCodeBind (m : Type → Type) where
|
| 13 |
+
codeBind : (c : Code) → (f : FVarId → m Code) → m Code
|
| 14 |
+
|
| 15 |
+
/--
|
| 16 |
+
Return code that is equivalent to `c >>= f`. That is, executes `c`, and then `f x`, where
|
| 17 |
+
`x` is a variable that contains the result of `c`'s computation.
|
| 18 |
+
|
| 19 |
+
If `c` contains a jump to a join point `jp_i` not declared in `c`, we throw an exception because
|
| 20 |
+
an invalid block would be generated. It would be invalid because `f` would not
|
| 21 |
+
be applied to `jp_i`. Note that, we could have decided to create a copy of `jp_i` where we apply `f` to it,
|
| 22 |
+
by we decided to not do it to avoid code duplication.
|
| 23 |
+
-/
|
| 24 |
+
abbrev Code.bind [MonadCodeBind m] (c : Code) (f : FVarId → m Code) : m Code :=
|
| 25 |
+
MonadCodeBind.codeBind c f
|
| 26 |
+
|
| 27 |
+
partial def CompilerM.codeBind (c : Code) (f : FVarId → CompilerM Code) : CompilerM Code := do
|
| 28 |
+
go c |>.run {}
|
| 29 |
+
where
|
| 30 |
+
go (c : Code) : ReaderT FVarIdSet CompilerM Code := do
|
| 31 |
+
match c with
|
| 32 |
+
| .let decl k => return .let decl (← go k)
|
| 33 |
+
| .fun decl k => return .fun decl (← go k)
|
| 34 |
+
| .jp decl k =>
|
| 35 |
+
let value ← go decl.value
|
| 36 |
+
let type ← value.inferParamType decl.params
|
| 37 |
+
let decl ← decl.update' type value
|
| 38 |
+
withReader (fun s => s.insert decl.fvarId) do
|
| 39 |
+
return .jp decl (← go k)
|
| 40 |
+
| .cases c =>
|
| 41 |
+
let alts ← c.alts.mapM fun
|
| 42 |
+
| .alt ctorName params k => return .alt ctorName params (← go k)
|
| 43 |
+
| .default k => return .default (← go k)
|
| 44 |
+
if alts.isEmpty then
|
| 45 |
+
throwError "`Code.bind` failed, empty `cases` found"
|
| 46 |
+
let resultType ← mkCasesResultType alts
|
| 47 |
+
return .cases { c with alts, resultType }
|
| 48 |
+
| .return fvarId => f fvarId
|
| 49 |
+
| .jmp fvarId .. =>
|
| 50 |
+
unless (← read).contains fvarId do
|
| 51 |
+
throwError "`Code.bind` failed, it contains a out of scope join point"
|
| 52 |
+
return c
|
| 53 |
+
| .unreach type =>
|
| 54 |
+
/-
|
| 55 |
+
Create an auxiliary parameter `aux : type` to compute the resulting type of `f aux`.
|
| 56 |
+
This code is not very efficient, we could ask caller to provide the type of `c >>= f`,
|
| 57 |
+
but this is more convenient, and this case is seldom reached.
|
| 58 |
+
-/
|
| 59 |
+
let auxParam ← mkAuxParam type
|
| 60 |
+
let k ← f auxParam.fvarId
|
| 61 |
+
let typeNew ← k.inferType
|
| 62 |
+
eraseCode k
|
| 63 |
+
eraseParam auxParam
|
| 64 |
+
return .unreach typeNew
|
| 65 |
+
|
| 66 |
+
instance : MonadCodeBind CompilerM where
|
| 67 |
+
codeBind := CompilerM.codeBind
|
| 68 |
+
|
| 69 |
+
instance [MonadCodeBind m] : MonadCodeBind (ReaderT ρ m) where
|
| 70 |
+
codeBind c f ctx := c.bind fun fvarId => f fvarId ctx
|
| 71 |
+
|
| 72 |
+
instance [STWorld ω m] [MonadCodeBind m] : MonadCodeBind (StateRefT' ω σ m) where
|
| 73 |
+
codeBind c f sref := c.bind fun fvarId => f fvarId sref
|
| 74 |
+
|
| 75 |
+
/--
|
| 76 |
+
Create new parameters for the given arrow type.
|
| 77 |
+
Example: if `type` is `Nat → Bool → Int`, the result is
|
| 78 |
+
an array containing two new parameters with types `Nat` and `Bool`.
|
| 79 |
+
-/
|
| 80 |
+
partial def mkNewParams (type : Expr) : CompilerM (Array Param) :=
|
| 81 |
+
go type #[] #[]
|
| 82 |
+
where
|
| 83 |
+
go (type : Expr) (xs : Array Expr) (ps : Array Param) : CompilerM (Array Param) := do
|
| 84 |
+
match type with
|
| 85 |
+
| .forallE _ d b _ =>
|
| 86 |
+
let d := d.instantiateRev xs
|
| 87 |
+
let p ← mkAuxParam d
|
| 88 |
+
go b (xs.push (.fvar p.fvarId)) (ps.push p)
|
| 89 |
+
| _ =>
|
| 90 |
+
let type := type.instantiateRev xs
|
| 91 |
+
let type' := type.headBeta
|
| 92 |
+
if type' != type then
|
| 93 |
+
go type' #[] ps
|
| 94 |
+
else
|
| 95 |
+
return ps
|
| 96 |
+
|
| 97 |
+
def isEtaExpandCandidateCore (type : Expr) (params : Array Param) : Bool :=
|
| 98 |
+
let typeArity := getArrowArity type
|
| 99 |
+
let valueArity := params.size
|
| 100 |
+
typeArity > valueArity
|
| 101 |
+
|
| 102 |
+
abbrev FunDecl.isEtaExpandCandidate (decl : FunDecl) : Bool :=
|
| 103 |
+
isEtaExpandCandidateCore decl.type decl.params
|
| 104 |
+
|
| 105 |
+
def etaExpandCore (type : Expr) (params : Array Param) (value : Code) : CompilerM (Array Param × Code) := do
|
| 106 |
+
let valueType ← instantiateForall type (params.map (mkFVar ·.fvarId))
|
| 107 |
+
let psNew ← mkNewParams valueType
|
| 108 |
+
let params := params ++ psNew
|
| 109 |
+
let xs := psNew.map fun p => .fvar p.fvarId
|
| 110 |
+
let value ← value.bind fun fvarId => do
|
| 111 |
+
let auxDecl ← mkAuxLetDecl (.fvar fvarId xs)
|
| 112 |
+
return .let auxDecl (.return auxDecl.fvarId)
|
| 113 |
+
return (params, value)
|
| 114 |
+
|
| 115 |
+
def etaExpandCore? (type : Expr) (params : Array Param) (value : Code) : CompilerM (Option (Array Param × Code)) := do
|
| 116 |
+
if isEtaExpandCandidateCore type params then
|
| 117 |
+
etaExpandCore type params value
|
| 118 |
+
else
|
| 119 |
+
return none
|
| 120 |
+
|
| 121 |
+
def FunDecl.etaExpand (decl : FunDecl) : CompilerM FunDecl := do
|
| 122 |
+
let some (params, value) ← etaExpandCore? decl.type decl.params decl.value | return decl
|
| 123 |
+
decl.update decl.type params value
|
| 124 |
+
|
| 125 |
+
def Decl.etaExpand (decl : Decl) : CompilerM Decl := do
|
| 126 |
+
match decl.value with
|
| 127 |
+
| .code code =>
|
| 128 |
+
let some (params, newCode) ← etaExpandCore? decl.type decl.params code | return decl
|
| 129 |
+
return { decl with params, value := .code newCode}
|
| 130 |
+
| .extern .. => return decl
|
| 131 |
+
|
| 132 |
+
end Lean.Compiler.LCNF
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/CSE.lean
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2022 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Compiler.LCNF.CompilerM
|
| 8 |
+
import Lean.Compiler.LCNF.ToExpr
|
| 9 |
+
import Lean.Compiler.LCNF.PassManager
|
| 10 |
+
import Lean.Compiler.NeverExtractAttr
|
| 11 |
+
|
| 12 |
+
namespace Lean.Compiler.LCNF
|
| 13 |
+
|
| 14 |
+
/-! Common Sub-expression Elimination -/
|
| 15 |
+
|
| 16 |
+
namespace CSE
|
| 17 |
+
|
| 18 |
+
structure State where
|
| 19 |
+
map : PHashMap Expr FVarId := {}
|
| 20 |
+
subst : FVarSubst := {}
|
| 21 |
+
|
| 22 |
+
abbrev M := StateRefT State CompilerM
|
| 23 |
+
|
| 24 |
+
instance : MonadFVarSubst M false where
|
| 25 |
+
getSubst := return (← get).subst
|
| 26 |
+
|
| 27 |
+
instance : MonadFVarSubstState M where
|
| 28 |
+
modifySubst f := modify fun s => { s with subst := f s.subst }
|
| 29 |
+
|
| 30 |
+
@[inline] def getSubst : M FVarSubst :=
|
| 31 |
+
return (← get).subst
|
| 32 |
+
|
| 33 |
+
@[inline] def addEntry (value : Expr) (fvarId : FVarId) : M Unit :=
|
| 34 |
+
modify fun s => { s with map := s.map.insert value fvarId }
|
| 35 |
+
|
| 36 |
+
@[inline] def withNewScope (x : M α) : M α := do
|
| 37 |
+
let map := (← get).map
|
| 38 |
+
try x finally modify fun s => { s with map }
|
| 39 |
+
|
| 40 |
+
def replaceLet (decl : LetDecl) (fvarId : FVarId) : M Unit := do
|
| 41 |
+
eraseLetDecl decl
|
| 42 |
+
addFVarSubst decl.fvarId fvarId
|
| 43 |
+
|
| 44 |
+
def replaceFun (decl : FunDecl) (fvarId : FVarId) : M Unit := do
|
| 45 |
+
eraseFunDecl decl
|
| 46 |
+
addFVarSubst decl.fvarId fvarId
|
| 47 |
+
|
| 48 |
+
def hasNeverExtract (v : LetValue) : CompilerM Bool :=
|
| 49 |
+
match v with
|
| 50 |
+
| .const declName .. =>
|
| 51 |
+
return hasNeverExtractAttribute (← getEnv) declName
|
| 52 |
+
| .lit _ | .erased | .proj .. | .fvar .. =>
|
| 53 |
+
return false
|
| 54 |
+
|
| 55 |
+
partial def _root_.Lean.Compiler.LCNF.Code.cse (shouldElimFunDecls : Bool) (code : Code) : CompilerM Code :=
|
| 56 |
+
go code |>.run' {}
|
| 57 |
+
where
|
| 58 |
+
goFunDecl (decl : FunDecl) : M FunDecl := do
|
| 59 |
+
let type ← normExpr decl.type
|
| 60 |
+
let params ← normParams decl.params
|
| 61 |
+
let value ← withNewScope do go decl.value
|
| 62 |
+
decl.update type params value
|
| 63 |
+
|
| 64 |
+
go (code : Code) : M Code := do
|
| 65 |
+
match code with
|
| 66 |
+
| .let decl k =>
|
| 67 |
+
let decl ← normLetDecl decl
|
| 68 |
+
if (← hasNeverExtract decl.value) then
|
| 69 |
+
return code.updateLet! decl (← go k)
|
| 70 |
+
else
|
| 71 |
+
-- We only apply CSE to pure code
|
| 72 |
+
let key := decl.value.toExpr
|
| 73 |
+
match (← get).map.find? key with
|
| 74 |
+
| some fvarId =>
|
| 75 |
+
replaceLet decl fvarId
|
| 76 |
+
go k
|
| 77 |
+
| none =>
|
| 78 |
+
addEntry key decl.fvarId
|
| 79 |
+
return code.updateLet! decl (← go k)
|
| 80 |
+
| .fun decl k =>
|
| 81 |
+
let decl ← goFunDecl decl
|
| 82 |
+
if shouldElimFunDecls then
|
| 83 |
+
let value := decl.toExpr
|
| 84 |
+
match (← get).map.find? value with
|
| 85 |
+
| some fvarId' =>
|
| 86 |
+
replaceFun decl fvarId'
|
| 87 |
+
go k
|
| 88 |
+
| none =>
|
| 89 |
+
addEntry value decl.fvarId
|
| 90 |
+
return code.updateFun! decl (← go k)
|
| 91 |
+
else
|
| 92 |
+
return code.updateFun! decl (← go k)
|
| 93 |
+
| .jp decl k =>
|
| 94 |
+
let decl ← goFunDecl decl
|
| 95 |
+
/-
|
| 96 |
+
We currently don't eliminate common join points because we want to prevent
|
| 97 |
+
jumps to out-of-scope join points.
|
| 98 |
+
-/
|
| 99 |
+
return code.updateFun! decl (← go k)
|
| 100 |
+
| .cases c =>
|
| 101 |
+
withNormFVarResult (← normFVar c.discr) fun discr => do
|
| 102 |
+
let resultType ← normExpr c.resultType
|
| 103 |
+
let alts ← c.alts.mapMonoM fun alt => do
|
| 104 |
+
match alt with
|
| 105 |
+
| .alt _ ps k => withNewScope do
|
| 106 |
+
return alt.updateAlt! (← normParams ps) (← go k)
|
| 107 |
+
| .default k => withNewScope do return alt.updateCode (← go k)
|
| 108 |
+
return code.updateCases! resultType discr alts
|
| 109 |
+
| .return fvarId => withNormFVarResult (← normFVar fvarId) fun fvarId => return code.updateReturn! fvarId
|
| 110 |
+
| .jmp fvarId args => withNormFVarResult (← normFVar fvarId) fun fvarId => return code.updateJmp! fvarId (← normArgs args)
|
| 111 |
+
| .unreach .. => return code
|
| 112 |
+
|
| 113 |
+
end CSE
|
| 114 |
+
|
| 115 |
+
/--
|
| 116 |
+
Common sub-expression elimination
|
| 117 |
+
-/
|
| 118 |
+
def Decl.cse (shouldElimFunDecls : Bool) (decl : Decl) : CompilerM Decl := do
|
| 119 |
+
let value ← decl.value.mapCodeM (·.cse shouldElimFunDecls)
|
| 120 |
+
return { decl with value }
|
| 121 |
+
|
| 122 |
+
def cse (phase : Phase := .base) (shouldElimFunDecls := false) (occurrence := 0) : Pass :=
|
| 123 |
+
.mkPerDeclaration `cse (Decl.cse shouldElimFunDecls) phase occurrence
|
| 124 |
+
|
| 125 |
+
builtin_initialize
|
| 126 |
+
registerTraceClass `Compiler.cse (inherited := true)
|
| 127 |
+
|
| 128 |
+
end Lean.Compiler.LCNF
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/Check.lean
ADDED
|
@@ -0,0 +1,307 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2022 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Compiler.LCNF.InferType
|
| 8 |
+
import Lean.Compiler.LCNF.PrettyPrinter
|
| 9 |
+
import Lean.Compiler.LCNF.CompatibleTypes
|
| 10 |
+
|
| 11 |
+
namespace Lean.Compiler.LCNF
|
| 12 |
+
|
| 13 |
+
/-!
|
| 14 |
+
# Note: Type compatibility checking for LCNF
|
| 15 |
+
|
| 16 |
+
We used to have a type compatibility relation `≃` for LCNF types.
|
| 17 |
+
It treated erased types/values as wildcards. Examples:
|
| 18 |
+
- `List Nat ≃ List ◾`
|
| 19 |
+
- `(List ◾ → List ◾) ≃ (List Nat → List Bool)`
|
| 20 |
+
|
| 21 |
+
We used this relation to sanity check compiler passes, and detect
|
| 22 |
+
buggy transformations that broke type compatibility. For example,
|
| 23 |
+
given an application `f a`, we would check whether `a`s type as
|
| 24 |
+
compatible with the type expected by `f`.
|
| 25 |
+
|
| 26 |
+
However, the type compatibility relation is not transitive. Example:
|
| 27 |
+
- `List Nat ≃ List ◾`, `List ◾ ≃ List String`, but `List Nat` and `List String` are **not** compatible.
|
| 28 |
+
|
| 29 |
+
We tried address the issue above by adding casts, which required us
|
| 30 |
+
to then add `cast` elimination simplifications, and generated a significant overhead in
|
| 31 |
+
the code generator.
|
| 32 |
+
|
| 33 |
+
Here is an example of transformation that would require the insertion of a cast operation.
|
| 34 |
+
```
|
| 35 |
+
def foo (g : List A → List A) (a : List B) :=
|
| 36 |
+
fun f (x : List ◾) :=
|
| 37 |
+
let _x.1 := g x
|
| 38 |
+
...
|
| 39 |
+
let _x.2 := f a
|
| 40 |
+
...
|
| 41 |
+
```
|
| 42 |
+
The code above would not trigger any type compatibility issue, but
|
| 43 |
+
by inlining `f` without adding cast operations, we would get the
|
| 44 |
+
following type incorrect code.
|
| 45 |
+
```
|
| 46 |
+
def foo (g : List A → List A) (a : List B) :=
|
| 47 |
+
let _x.2 := g a -- Type error
|
| 48 |
+
...
|
| 49 |
+
```
|
| 50 |
+
|
| 51 |
+
We have considered using a reflexive and transitive subtype relation `≺`.
|
| 52 |
+
- `A ≺ A`
|
| 53 |
+
- `(Nat × Nat) ≺ (Nat × ◾) ≺ (◾ × ◾) ≺ ◾`
|
| 54 |
+
- `List Nat ≺ List ◾ ⊀ List String`
|
| 55 |
+
- `(List ◾ → List Nat) ≺ (List Bool → List ◾)`
|
| 56 |
+
Note that `A ≺ B` implies `A ≃ B`
|
| 57 |
+
|
| 58 |
+
The subtype relation has better properties, but also has problems.
|
| 59 |
+
First, when converting to LCNF we would have to add more casts. Example:
|
| 60 |
+
the function takes a `List ◾`, but the value has type `◾`.
|
| 61 |
+
Moreover, recall that `(List Nat → List Nat) ⊀ (◾ → ◾)` forcing us
|
| 62 |
+
to add many casts operations when moving to the mono phase where
|
| 63 |
+
we erase type parameters.
|
| 64 |
+
|
| 65 |
+
Recall that type compatibility and subtype relationships do not help with memory layout.
|
| 66 |
+
We have that `(UInt32 × UInt32) ≺ (◾ × ◾) ≺ ◾` but elements of these types have
|
| 67 |
+
different runtime representation.
|
| 68 |
+
|
| 69 |
+
Thus, we have decided to abandon the type compatibility checks and cast operations
|
| 70 |
+
in LCNF. The only drawback is that we lose the capability of catching simple bugs
|
| 71 |
+
at compiler passes.
|
| 72 |
+
|
| 73 |
+
In the future, we can try to add a sanity check flag that instructs the compiler to use
|
| 74 |
+
the subtype relation in sanity checks and add the necessary casts.
|
| 75 |
+
|
| 76 |
+
-/
|
| 77 |
+
|
| 78 |
+
namespace Check
|
| 79 |
+
open InferType
|
| 80 |
+
|
| 81 |
+
/-
|
| 82 |
+
Type and structural properties checker for LCNF expressions.
|
| 83 |
+
-/
|
| 84 |
+
|
| 85 |
+
structure Context where
|
| 86 |
+
/-- Join points that are in scope. -/
|
| 87 |
+
jps : FVarIdSet := {}
|
| 88 |
+
/-- Variables and local functions in scope -/
|
| 89 |
+
vars : FVarIdSet := {}
|
| 90 |
+
|
| 91 |
+
structure State where
|
| 92 |
+
/-- All free variables found -/
|
| 93 |
+
all : FVarIdHashSet := {}
|
| 94 |
+
|
| 95 |
+
abbrev CheckM := ReaderT Context $ StateRefT State InferTypeM
|
| 96 |
+
|
| 97 |
+
def checkTypes : CheckM Bool := do
|
| 98 |
+
return (← getConfig).checkTypes
|
| 99 |
+
|
| 100 |
+
def checkFVar (fvarId : FVarId) : CheckM Unit :=
|
| 101 |
+
unless (← read).vars.contains fvarId do
|
| 102 |
+
throwError "invalid out of scope free variable {← getBinderName fvarId}"
|
| 103 |
+
|
| 104 |
+
/-- Return true `f` is a constructor and `i` is less than its number of parameters. -/
|
| 105 |
+
def isCtorParam (f : Expr) (i : Nat) : CoreM Bool := do
|
| 106 |
+
let .const declName _ := f | return false
|
| 107 |
+
let .ctorInfo info ← getConstInfo declName | return false
|
| 108 |
+
return i < info.numParams
|
| 109 |
+
|
| 110 |
+
def checkAppArgs (f : Expr) (args : Array Arg) : CheckM Unit := do
|
| 111 |
+
let mut fType ← inferType f
|
| 112 |
+
let mut j := 0
|
| 113 |
+
for h : i in [:args.size] do
|
| 114 |
+
let arg := args[i]
|
| 115 |
+
if fType.isErased then
|
| 116 |
+
return ()
|
| 117 |
+
fType := fType.headBeta
|
| 118 |
+
let (d, b) ←
|
| 119 |
+
match fType with
|
| 120 |
+
| .forallE _ d b _ => pure (d, b)
|
| 121 |
+
| _ =>
|
| 122 |
+
fType := instantiateRevRangeArgs fType j i args |>.headBeta
|
| 123 |
+
match fType with
|
| 124 |
+
| .forallE _ d b _ => j := i; pure (d, b)
|
| 125 |
+
| _ => return ()
|
| 126 |
+
let expectedType := instantiateRevRangeArgs d j i args
|
| 127 |
+
if (← checkTypes) then
|
| 128 |
+
let argType ← arg.inferType
|
| 129 |
+
unless (← InferType.compatibleTypes argType expectedType) do
|
| 130 |
+
throwError "type mismatch at LCNF application{indentExpr (mkAppN f (args.map Arg.toExpr))}\nargument {arg.toExpr} has type{indentExpr argType}\nbut is expected to have type{indentExpr expectedType}"
|
| 131 |
+
unless (← pure (maybeTypeFormerType expectedType) <||> isErasedCompatible expectedType) do
|
| 132 |
+
match arg with
|
| 133 |
+
| .fvar fvarId => checkFVar fvarId
|
| 134 |
+
| .erased => pure ()
|
| 135 |
+
| .type _ =>
|
| 136 |
+
-- Constructor parameters that are not type formers are erased at phase .mono
|
| 137 |
+
unless (← getPhase) ≥ .mono && (← isCtorParam f i) do
|
| 138 |
+
throwError "invalid LCNF application{indentExpr (mkAppN f (args.map (·.toExpr)))}\nargument{indentExpr arg.toExpr}\nhas type{indentExpr expectedType}\nmust be a free variable"
|
| 139 |
+
fType := b
|
| 140 |
+
|
| 141 |
+
def checkLetValue (e : LetValue) : CheckM Unit := do
|
| 142 |
+
match e with
|
| 143 |
+
| .lit .. | .erased => pure ()
|
| 144 |
+
| .const declName us args => checkAppArgs (mkConst declName us) args
|
| 145 |
+
| .fvar fvarId args => checkFVar fvarId; checkAppArgs (.fvar fvarId) args
|
| 146 |
+
| .proj _ _ fvarId => checkFVar fvarId
|
| 147 |
+
|
| 148 |
+
def checkJpInScope (jp : FVarId) : CheckM Unit := do
|
| 149 |
+
unless (← read).jps.contains jp do
|
| 150 |
+
/-
|
| 151 |
+
We cannot jump to join points defined out of the scope of a local function declaration.
|
| 152 |
+
For example, the following is an invalid LCNF.
|
| 153 |
+
```
|
| 154 |
+
jp_1 := fun x => ... -- Some join point
|
| 155 |
+
let f := fun y => -- Local function declaration.
|
| 156 |
+
...
|
| 157 |
+
jp_1 _x.n -- jump to a join point that is not in the scope of `f`.
|
| 158 |
+
```
|
| 159 |
+
-/
|
| 160 |
+
throwError "invalid jump to out of scope join point `{mkFVar jp}`"
|
| 161 |
+
|
| 162 |
+
def checkParam (param : Param) : CheckM Unit := do
|
| 163 |
+
unless param == (← getParam param.fvarId) do
|
| 164 |
+
throwError "LCNF parameter mismatch at `{param.binderName}`, does not value in local context"
|
| 165 |
+
|
| 166 |
+
def checkParams (params : Array Param) : CheckM Unit :=
|
| 167 |
+
params.forM checkParam
|
| 168 |
+
|
| 169 |
+
def checkLetDecl (letDecl : LetDecl) : CheckM Unit := do
|
| 170 |
+
checkLetValue letDecl.value
|
| 171 |
+
if (← checkTypes) then
|
| 172 |
+
let valueType ← letDecl.value.inferType
|
| 173 |
+
unless (← InferType.compatibleTypes letDecl.type valueType) do
|
| 174 |
+
throwError "type mismatch at `{letDecl.binderName}`, value has type{indentExpr valueType}\nbut is expected to have type{indentExpr letDecl.type}"
|
| 175 |
+
unless letDecl == (← getLetDecl letDecl.fvarId) do
|
| 176 |
+
throwError "LCNF let declaration mismatch at `{letDecl.binderName}`, does not match value in local context"
|
| 177 |
+
|
| 178 |
+
def addFVarId (fvarId : FVarId) : CheckM Unit := do
|
| 179 |
+
if (← get).all.contains fvarId then
|
| 180 |
+
throwError "invalid LCNF, free variables are not unique `{fvarId.name}`"
|
| 181 |
+
modify fun s => { s with all := s.all.insert fvarId }
|
| 182 |
+
|
| 183 |
+
@[inline] def withFVarId (fvarId : FVarId) (x : CheckM α) : CheckM α := do
|
| 184 |
+
addFVarId fvarId
|
| 185 |
+
withReader (fun ctx => { ctx with vars := ctx.vars.insert fvarId }) x
|
| 186 |
+
|
| 187 |
+
@[inline] def withJp (fvarId : FVarId) (x : CheckM α) : CheckM α := do
|
| 188 |
+
addFVarId fvarId
|
| 189 |
+
withReader (fun ctx => { ctx with jps := ctx.jps.insert fvarId }) x
|
| 190 |
+
|
| 191 |
+
@[inline] def withParams (params : Array Param) (x : CheckM α) : CheckM α := do
|
| 192 |
+
params.forM (addFVarId ·.fvarId)
|
| 193 |
+
withReader (fun ctx => { ctx with vars := params.foldl (init := ctx.vars) fun vars p => vars.insert p.fvarId })
|
| 194 |
+
x
|
| 195 |
+
|
| 196 |
+
mutual
|
| 197 |
+
|
| 198 |
+
set_option linter.all false
|
| 199 |
+
|
| 200 |
+
partial def checkFunDeclCore (declName : Name) (params : Array Param) (type : Expr) (value : Code) : CheckM Unit := do
|
| 201 |
+
checkParams params
|
| 202 |
+
withParams params do
|
| 203 |
+
discard <| check value
|
| 204 |
+
if (← checkTypes) then
|
| 205 |
+
let valueType ← mkForallParams params (← value.inferType)
|
| 206 |
+
unless (← InferType.compatibleTypes type valueType) do
|
| 207 |
+
throwError "type mismatch at `{declName}`, value has type{indentExpr valueType}\nbut is expected to have type{indentExpr type}"
|
| 208 |
+
|
| 209 |
+
partial def checkFunDecl (funDecl : FunDecl) : CheckM Unit := do
|
| 210 |
+
checkFunDeclCore funDecl.binderName funDecl.params funDecl.type funDecl.value
|
| 211 |
+
let decl ← getFunDecl funDecl.fvarId
|
| 212 |
+
unless decl.binderName == funDecl.binderName do
|
| 213 |
+
throwError "LCNF local function declaration mismatch at `{funDecl.binderName}`, binder name in local context `{decl.binderName}`"
|
| 214 |
+
unless decl.type == funDecl.type do
|
| 215 |
+
throwError "LCNF local function declaration mismatch at `{funDecl.binderName}`, type in local context{indentExpr decl.type}\nexpected{indentExpr funDecl.type}"
|
| 216 |
+
unless (← getFunDecl funDecl.fvarId) == funDecl do
|
| 217 |
+
throwError "LCNF local function declaration mismatch at `{funDecl.binderName}`, declaration in local context does match"
|
| 218 |
+
|
| 219 |
+
partial def checkCases (c : Cases) : CheckM Unit := do
|
| 220 |
+
let mut ctorNames : NameSet := {}
|
| 221 |
+
let mut hasDefault := false
|
| 222 |
+
checkFVar c.discr
|
| 223 |
+
for alt in c.alts do
|
| 224 |
+
match alt with
|
| 225 |
+
| .default k => hasDefault := true; check k
|
| 226 |
+
| .alt ctorName params k =>
|
| 227 |
+
checkParams params
|
| 228 |
+
if ctorNames.contains ctorName then
|
| 229 |
+
throwError "invalid LCNF `cases`, alternative `{ctorName}` occurs more than once"
|
| 230 |
+
ctorNames := ctorNames.insert ctorName
|
| 231 |
+
let .ctorInfo val ← getConstInfo ctorName | throwError "invalid LCNF `cases`, `{ctorName}` is not a constructor name"
|
| 232 |
+
unless val.induct == c.typeName do
|
| 233 |
+
throwError "invalid LCNF `cases`, `{ctorName}` is not a constructor of `{c.typeName}`"
|
| 234 |
+
unless params.size == val.numFields do
|
| 235 |
+
throwError "invalid LCNF `cases`, `{ctorName}` has # {val.numFields} fields, but alternative has # {params.size} alternatives"
|
| 236 |
+
withParams params do check k
|
| 237 |
+
|
| 238 |
+
partial def check (code : Code) : CheckM Unit := do
|
| 239 |
+
match code with
|
| 240 |
+
| .let decl k => checkLetDecl decl; withFVarId decl.fvarId do check k
|
| 241 |
+
| .fun decl k =>
|
| 242 |
+
-- Remark: local function declarations should not jump to out of scope join points
|
| 243 |
+
withReader (fun ctx => { ctx with jps := {} }) do checkFunDecl decl
|
| 244 |
+
withFVarId decl.fvarId do check k
|
| 245 |
+
| .jp decl k => checkFunDecl decl; withJp decl.fvarId do check k
|
| 246 |
+
| .cases c => checkCases c
|
| 247 |
+
| .jmp fvarId args =>
|
| 248 |
+
checkJpInScope fvarId
|
| 249 |
+
let decl ← getFunDecl fvarId
|
| 250 |
+
unless decl.getArity == args.size do
|
| 251 |
+
throwError "invalid LCNF `goto`, join point {decl.binderName} has #{decl.getArity} parameters, but #{args.size} were provided"
|
| 252 |
+
checkAppArgs (.fvar fvarId) args
|
| 253 |
+
| .return fvarId => checkFVar fvarId
|
| 254 |
+
| .unreach .. => pure ()
|
| 255 |
+
|
| 256 |
+
end
|
| 257 |
+
|
| 258 |
+
def run (x : CheckM α) : CompilerM α :=
|
| 259 |
+
x |>.run {} |>.run' {} |>.run {}
|
| 260 |
+
|
| 261 |
+
end Check
|
| 262 |
+
|
| 263 |
+
def Decl.check (decl : Decl) : CompilerM Unit := do
|
| 264 |
+
Check.run do decl.value.forCodeM (Check.checkFunDeclCore decl.name decl.params decl.type)
|
| 265 |
+
|
| 266 |
+
/--
|
| 267 |
+
Check whether every local declaration in the local context is used in one of given `decls`.
|
| 268 |
+
-/
|
| 269 |
+
partial def checkDeadLocalDecls (decls : Array Decl) : CompilerM Unit := do
|
| 270 |
+
let (_, s) := visitDecls decls |>.run {}
|
| 271 |
+
let usesFVar (binderName : Name) (fvarId : FVarId) :=
|
| 272 |
+
unless s.contains fvarId do
|
| 273 |
+
throwError "LCNF local context contains unused local variable declaration `{binderName}`"
|
| 274 |
+
let lctx := (← get).lctx
|
| 275 |
+
lctx.params.forM fun fvarId decl => usesFVar decl.binderName fvarId
|
| 276 |
+
lctx.letDecls.forM fun fvarId decl => usesFVar decl.binderName fvarId
|
| 277 |
+
lctx.funDecls.forM fun fvarId decl => usesFVar decl.binderName fvarId
|
| 278 |
+
where
|
| 279 |
+
visitFVar (fvarId : FVarId) : StateM FVarIdHashSet Unit :=
|
| 280 |
+
modify (·.insert fvarId)
|
| 281 |
+
|
| 282 |
+
visitParam (param : Param) : StateM FVarIdHashSet Unit := do
|
| 283 |
+
visitFVar param.fvarId
|
| 284 |
+
|
| 285 |
+
visitParams (params : Array Param) : StateM FVarIdHashSet Unit := do
|
| 286 |
+
params.forM visitParam
|
| 287 |
+
|
| 288 |
+
visitCode (code : Code) : StateM FVarIdHashSet Unit := do
|
| 289 |
+
match code with
|
| 290 |
+
| .jmp .. | .return .. | .unreach .. => return ()
|
| 291 |
+
| .let decl k => visitFVar decl.fvarId; visitCode k
|
| 292 |
+
| .fun decl k | .jp decl k =>
|
| 293 |
+
visitFVar decl.fvarId; visitParams decl.params; visitCode decl.value
|
| 294 |
+
visitCode k
|
| 295 |
+
| .cases c => c.alts.forM fun alt => do
|
| 296 |
+
match alt with
|
| 297 |
+
| .default k => visitCode k
|
| 298 |
+
| .alt _ ps k => visitParams ps; visitCode k
|
| 299 |
+
|
| 300 |
+
visitDecl (decl : Decl) : StateM FVarIdHashSet Unit := do
|
| 301 |
+
visitParams decl.params
|
| 302 |
+
decl.value.forCodeM visitCode
|
| 303 |
+
|
| 304 |
+
visitDecls (decls : Array Decl) : StateM FVarIdHashSet Unit :=
|
| 305 |
+
decls.forM visitDecl
|
| 306 |
+
|
| 307 |
+
end Lean.Compiler.LCNF
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/Closure.lean
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2022 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Util.ForEachExprWhere
|
| 8 |
+
import Lean.Compiler.LCNF.CompilerM
|
| 9 |
+
|
| 10 |
+
namespace Lean.Compiler.LCNF
|
| 11 |
+
namespace Closure
|
| 12 |
+
|
| 13 |
+
/-!
|
| 14 |
+
# Dependency collector for code specialization and lambda lifting.
|
| 15 |
+
|
| 16 |
+
During code specialization and lambda lifting, we have code `C` containing free variables. These free variables
|
| 17 |
+
are in a scope, and we say we are computing `C`'s closure.
|
| 18 |
+
This module is used to compute the closure.
|
| 19 |
+
-/
|
| 20 |
+
|
| 21 |
+
structure Context where
|
| 22 |
+
/--
|
| 23 |
+
`inScope x` returns `true` if `x` is a variable that is not in `C`.
|
| 24 |
+
-/
|
| 25 |
+
inScope : FVarId → Bool
|
| 26 |
+
/--
|
| 27 |
+
If `abstract x` returns `true`, we convert `x` into a closure parameter. Otherwise,
|
| 28 |
+
we collect the dependencies in the `let`/`fun`-declaration too, and include the declaration in the closure.
|
| 29 |
+
Remark: the lambda lifting pass abstracts all `let`/`fun`-declarations.
|
| 30 |
+
-/
|
| 31 |
+
abstract : FVarId → Bool
|
| 32 |
+
/--
|
| 33 |
+
Indicates whether we are processing terms beneath a binder.
|
| 34 |
+
-/
|
| 35 |
+
isUnderBinder : Bool
|
| 36 |
+
|
| 37 |
+
/--
|
| 38 |
+
State for the `ClosureM` monad.
|
| 39 |
+
-/
|
| 40 |
+
structure State where
|
| 41 |
+
/--
|
| 42 |
+
Set of already visited free variables.
|
| 43 |
+
-/
|
| 44 |
+
visited : FVarIdHashSet := {}
|
| 45 |
+
/--
|
| 46 |
+
Free variables that must become new parameters of the code being specialized.
|
| 47 |
+
-/
|
| 48 |
+
params : Array Param := #[]
|
| 49 |
+
/--
|
| 50 |
+
Let-declarations and local function declarations that are going to be "copied" to the code
|
| 51 |
+
being processed. For example, when this module is used in the code specializer, the let-declarations
|
| 52 |
+
often contain the instance values. In the current specialization heuristic all let-declarations are ground values
|
| 53 |
+
(i.e., they do not contain free-variables).
|
| 54 |
+
However, local function declarations may contain free variables.
|
| 55 |
+
|
| 56 |
+
All customers of this module try to avoid work duplication. If a let-declaration is a ground value,
|
| 57 |
+
it most likely will be computed during compilation time, and work duplication is not an issue.
|
| 58 |
+
-/
|
| 59 |
+
decls : Array CodeDecl := #[]
|
| 60 |
+
|
| 61 |
+
/--
|
| 62 |
+
Monad for implementing the dependency collector.
|
| 63 |
+
-/
|
| 64 |
+
abbrev ClosureM := ReaderT Context $ StateRefT State CompilerM
|
| 65 |
+
|
| 66 |
+
/--
|
| 67 |
+
Mark a free variable as already visited.
|
| 68 |
+
We perform a topological sort over the dependencies.
|
| 69 |
+
-/
|
| 70 |
+
def markVisited (fvarId : FVarId) : ClosureM Unit :=
|
| 71 |
+
modify fun s => { s with visited := s.visited.insert fvarId }
|
| 72 |
+
|
| 73 |
+
mutual
|
| 74 |
+
/--
|
| 75 |
+
Collect dependencies in parameters. We need this because parameters may
|
| 76 |
+
contain other type parameters.
|
| 77 |
+
-/
|
| 78 |
+
partial def collectParams (params : Array Param) : ClosureM Unit :=
|
| 79 |
+
params.forM (collectType ·.type)
|
| 80 |
+
|
| 81 |
+
partial def collectArg (arg : Arg) : ClosureM Unit :=
|
| 82 |
+
match arg with
|
| 83 |
+
| .erased => return ()
|
| 84 |
+
| .type e => collectType e
|
| 85 |
+
| .fvar fvarId => collectFVar fvarId
|
| 86 |
+
|
| 87 |
+
partial def collectLetValue (e : LetValue) : ClosureM Unit := do
|
| 88 |
+
match e with
|
| 89 |
+
| .erased | .lit .. => return ()
|
| 90 |
+
| .proj _ _ fvarId => collectFVar fvarId
|
| 91 |
+
| .const _ _ args => args.forM collectArg
|
| 92 |
+
| .fvar fvarId args => collectFVar fvarId; args.forM collectArg
|
| 93 |
+
|
| 94 |
+
/--
|
| 95 |
+
Collect dependencies in the given code. We need this function to be able
|
| 96 |
+
to collect dependencies in a local function declaration.
|
| 97 |
+
-/
|
| 98 |
+
partial def collectCode (c : Code) : ClosureM Unit := do
|
| 99 |
+
match c with
|
| 100 |
+
| .let decl k =>
|
| 101 |
+
collectType decl.type
|
| 102 |
+
withReader (fun ctx => { ctx with isUnderBinder := ctx.isUnderBinder || decl.type.isForall })
|
| 103 |
+
do collectLetValue decl.value
|
| 104 |
+
collectCode k
|
| 105 |
+
| .fun decl k | .jp decl k => collectFunDecl decl; collectCode k
|
| 106 |
+
| .cases c =>
|
| 107 |
+
collectType c.resultType
|
| 108 |
+
collectFVar c.discr
|
| 109 |
+
c.alts.forM fun alt => do
|
| 110 |
+
match alt with
|
| 111 |
+
| .default k => collectCode k
|
| 112 |
+
| .alt _ ps k => collectParams ps; collectCode k
|
| 113 |
+
| .jmp _ args => args.forM collectArg
|
| 114 |
+
| .unreach type => collectType type
|
| 115 |
+
| .return fvarId => collectFVar fvarId
|
| 116 |
+
|
| 117 |
+
/-- Collect dependencies of a local function declaration. -/
|
| 118 |
+
partial def collectFunDecl (decl : FunDecl) : ClosureM Unit := do
|
| 119 |
+
collectType decl.type
|
| 120 |
+
collectParams decl.params
|
| 121 |
+
withReader (fun ctx => { ctx with isUnderBinder := true }) do
|
| 122 |
+
collectCode decl.value
|
| 123 |
+
|
| 124 |
+
/--
|
| 125 |
+
Process the given free variable.
|
| 126 |
+
If it has not already been visited and is in scope, we collect its dependencies.
|
| 127 |
+
-/
|
| 128 |
+
partial def collectFVar (fvarId : FVarId) : ClosureM Unit := do
|
| 129 |
+
unless (← get).visited.contains fvarId do
|
| 130 |
+
markVisited fvarId
|
| 131 |
+
let ctx ← read
|
| 132 |
+
if ctx.inScope fvarId then
|
| 133 |
+
/- We only collect the variables in the scope of the function application being specialized. -/
|
| 134 |
+
if let some funDecl ← findFunDecl? fvarId then
|
| 135 |
+
if ctx.isUnderBinder || ctx.abstract funDecl.fvarId then
|
| 136 |
+
modify fun s => { s with params := s.params.push <| { funDecl with borrow := false } }
|
| 137 |
+
else
|
| 138 |
+
collectFunDecl funDecl
|
| 139 |
+
modify fun s => { s with decls := s.decls.push <| .fun funDecl }
|
| 140 |
+
else if let some param ← findParam? fvarId then
|
| 141 |
+
collectType param.type
|
| 142 |
+
modify fun s => { s with params := s.params.push param }
|
| 143 |
+
else if let some letDecl ← findLetDecl? fvarId then
|
| 144 |
+
collectType letDecl.type
|
| 145 |
+
if ctx.isUnderBinder || ctx.abstract letDecl.fvarId then
|
| 146 |
+
modify fun s => { s with params := s.params.push <| { letDecl with borrow := false } }
|
| 147 |
+
else
|
| 148 |
+
collectLetValue letDecl.value
|
| 149 |
+
modify fun s => { s with decls := s.decls.push <| .let letDecl }
|
| 150 |
+
else
|
| 151 |
+
unreachable!
|
| 152 |
+
|
| 153 |
+
/-- Collect dependencies of the given expression. -/
|
| 154 |
+
partial def collectType (type : Expr) : ClosureM Unit := do
|
| 155 |
+
type.forEachWhere Expr.isFVar fun e => collectFVar e.fvarId!
|
| 156 |
+
|
| 157 |
+
end
|
| 158 |
+
|
| 159 |
+
def run (x : ClosureM α) (inScope : FVarId → Bool) (abstract : FVarId → Bool := fun _ => true) : CompilerM (α × Array Param × Array CodeDecl) := do
|
| 160 |
+
let (a, s) ← x { inScope, abstract, isUnderBinder := false } |>.run {}
|
| 161 |
+
-- If we've abstracted an fvar into a param, exclude its definition. Note that this still allows
|
| 162 |
+
-- for other decls the removed decl depends upon to be included, but they will be removed later
|
| 163 |
+
-- for having no users.
|
| 164 |
+
let mut paramFVars : FVarIdSet := {}
|
| 165 |
+
for param in s.params do
|
| 166 |
+
paramFVars := paramFVars.insert param.fvarId
|
| 167 |
+
let filteredDecls := s.decls.filter fun decl => !(paramFVars.contains decl.fvarId)
|
| 168 |
+
return (a, s.params, filteredDecls)
|
| 169 |
+
|
| 170 |
+
end Closure
|
| 171 |
+
|
| 172 |
+
end Lean.Compiler.LCNF
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/CompatibleTypes.lean
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2022 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Compiler.LCNF.InferType
|
| 8 |
+
|
| 9 |
+
namespace Lean.Compiler.LCNF
|
| 10 |
+
|
| 11 |
+
/-!
|
| 12 |
+
# Compatible Types
|
| 13 |
+
|
| 14 |
+
We used to type check LCNF after each compiler pass. However, we disable this capability because cast management was too costly.
|
| 15 |
+
The casts may be needed to ensure the result of each pass is still typeable.
|
| 16 |
+
However, these sanity checks are useful for catching silly mistakes.
|
| 17 |
+
Thus, we have added an "LCNF type linter". When turned on, this "linter" option instructs the compiler to perform compatibility type checking
|
| 18 |
+
in the LCNF code after some compiler passes.
|
| 19 |
+
Recall most casts are only needed in functions that make heavy use of dependent types.
|
| 20 |
+
We claim it is "defensible" to say this sanity checker is a linter. If the sanity checker fails, it means the user is "abusing" dependent types
|
| 21 |
+
and performance may suffer at runtime.
|
| 22 |
+
Here is an example of code that "abuses" dependent types:
|
| 23 |
+
```
|
| 24 |
+
def Tuple (α : Type u) : Nat → Type u
|
| 25 |
+
| 0 => PUnit
|
| 26 |
+
| 1 => α
|
| 27 |
+
| n+2 => α × Tuple α (n+1)
|
| 28 |
+
|
| 29 |
+
def mkConstTuple (a : α) : (n : Nat) → Tuple α n
|
| 30 |
+
| 0 => ⟨⟩
|
| 31 |
+
| 1 => a
|
| 32 |
+
| n+2 => (a, mkConstTuple a (n+1))
|
| 33 |
+
|
| 34 |
+
def Tuple.map (f : α → β) (xs : Tuple α n) : Tuple β n :=
|
| 35 |
+
match n with
|
| 36 |
+
| 0 => ⟨⟩
|
| 37 |
+
| 1 => f xs
|
| 38 |
+
| _+2 => match xs with
|
| 39 |
+
| (a, xs) => (f a, Tuple.map f xs)
|
| 40 |
+
```
|
| 41 |
+
-/
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
/--
|
| 45 |
+
Quick check for `compatibleTypes`. It is not monadic, but it is incomplete
|
| 46 |
+
because it does not eta-expand type formers. See comment at `compatibleTypes`.
|
| 47 |
+
Remark: if the result is `true`, then `a` and `b` are indeed compatible.
|
| 48 |
+
If it is `false`, we must use the full-check.
|
| 49 |
+
-/
|
| 50 |
+
partial def compatibleTypesQuick (a b : Expr) : Bool :=
|
| 51 |
+
if a.isErased || b.isErased then
|
| 52 |
+
true
|
| 53 |
+
else
|
| 54 |
+
let a' := a.headBeta
|
| 55 |
+
let b' := b.headBeta
|
| 56 |
+
if a != a' || b != b' then
|
| 57 |
+
compatibleTypesQuick a' b'
|
| 58 |
+
else if a == b then
|
| 59 |
+
true
|
| 60 |
+
else
|
| 61 |
+
match a, b with
|
| 62 |
+
-- Note that even after reducing to head-beta, we can still have `.app` terms. For example,
|
| 63 |
+
-- an inductive constructor application such as `List Int`
|
| 64 |
+
| .app f a, .app g b => compatibleTypesQuick f g && compatibleTypesQuick a b
|
| 65 |
+
| .forallE _ d₁ b₁ _, .forallE _ d₂ b₂ _ => compatibleTypesQuick d₁ d₂ && compatibleTypesQuick b₁ b₂
|
| 66 |
+
| .lam _ d₁ b₁ _, .lam _ d₂ b₂ _ => compatibleTypesQuick d₁ d₂ && compatibleTypesQuick b₁ b₂
|
| 67 |
+
| .sort u, .sort v => Level.isEquiv u v
|
| 68 |
+
| .const n us, .const m vs => n == m && List.isEqv us vs Level.isEquiv
|
| 69 |
+
| _, _ => false
|
| 70 |
+
|
| 71 |
+
/--
|
| 72 |
+
Complete check for `compatibleTypes`. It eta-expands type formers. See comment at `compatibleTypes`.
|
| 73 |
+
-/
|
| 74 |
+
partial def InferType.compatibleTypesFull (a b : Expr) : InferTypeM Bool := do
|
| 75 |
+
if a.isErased || b.isErased then
|
| 76 |
+
return true
|
| 77 |
+
else
|
| 78 |
+
let a' := a.headBeta
|
| 79 |
+
let b' := b.headBeta
|
| 80 |
+
if a != a' || b != b' then
|
| 81 |
+
compatibleTypesFull a' b'
|
| 82 |
+
else if a == b then
|
| 83 |
+
return true
|
| 84 |
+
else
|
| 85 |
+
match a, b with
|
| 86 |
+
-- Note that even after reducing to head-beta, we can still have `.app` terms. For example,
|
| 87 |
+
-- an inductive constructor application such as `List Int`
|
| 88 |
+
| .app f a, .app g b => compatibleTypesFull f g <&&> compatibleTypesFull a b
|
| 89 |
+
| .forallE n d₁ b₁ bi, .forallE _ d₂ b₂ _ =>
|
| 90 |
+
unless (← compatibleTypesFull d₁ d₂) do return false
|
| 91 |
+
withLocalDecl n d₁ bi fun x =>
|
| 92 |
+
compatibleTypesFull (b₁.instantiate1 x) (b₂.instantiate1 x)
|
| 93 |
+
| .lam n d₁ b₁ bi, .lam _ d₂ b₂ _ =>
|
| 94 |
+
unless (← compatibleTypesFull d₁ d₂) do return false
|
| 95 |
+
withLocalDecl n d₁ bi fun x =>
|
| 96 |
+
compatibleTypesFull (b₁.instantiate1 x) (b₂.instantiate1 x)
|
| 97 |
+
| .sort u, .sort v => return Level.isEquiv u v
|
| 98 |
+
| .const n us, .const m vs => return n == m && List.isEqv us vs Level.isEquiv
|
| 99 |
+
| .mdata _ e, _ => compatibleTypesFull e b
|
| 100 |
+
| _, .mdata _ e => compatibleTypesFull a e
|
| 101 |
+
| _, _ =>
|
| 102 |
+
if a.isLambda then
|
| 103 |
+
let some b ← etaExpand? b | return false
|
| 104 |
+
compatibleTypesFull a b
|
| 105 |
+
else if b.isLambda then
|
| 106 |
+
let some a ← etaExpand? a | return false
|
| 107 |
+
compatibleTypesFull a b
|
| 108 |
+
else
|
| 109 |
+
return false
|
| 110 |
+
where
|
| 111 |
+
etaExpand? (e : Expr) : InferTypeM (Option Expr) := do
|
| 112 |
+
match (← inferType e).headBeta with
|
| 113 |
+
| .forallE n d _ bi =>
|
| 114 |
+
/-
|
| 115 |
+
In principle, `.app e (.bvar 0)` may not be a valid LCNF type sub-expression
|
| 116 |
+
because `d` may not be a type former type, See remark `compatibleTypes` for
|
| 117 |
+
a justification why this is ok.
|
| 118 |
+
-/
|
| 119 |
+
return some (.lam n d (.app e (.bvar 0)) bi)
|
| 120 |
+
| _ => return none
|
| 121 |
+
|
| 122 |
+
/--
|
| 123 |
+
Return true if the LCNF types `a` and `b` are compatible.
|
| 124 |
+
Remark: `a` and `b` can be type formers (e.g., `List`, or `fun (α : Type) => Nat → Nat × α`)
|
| 125 |
+
Remark: We may need to eta-expand type formers to establish whether they are compatible or not.
|
| 126 |
+
For example, suppose we have
|
| 127 |
+
```
|
| 128 |
+
fun (x : B) => Id B ◾ ◾
|
| 129 |
+
Id B ◾
|
| 130 |
+
```
|
| 131 |
+
We must eta-expand `Id B ◾` to `fun (x : B) => Id B ◾ x`. Note that, we use `x` instead of `◾` to
|
| 132 |
+
make the implementation simpler and skip the check whether `B` is a type former type. However,
|
| 133 |
+
this simplification should not affect correctness since `◾` is compatible with everything.
|
| 134 |
+
Remark: see comment at `isErasedCompatible`.
|
| 135 |
+
Remark: because of "erasure confusion" see note above, we assume `◾` (aka `lcErasure`) is compatible with everything.
|
| 136 |
+
This is a simplification. We used to use `isErasedCompatible`, but this only address item 1.
|
| 137 |
+
For item 2, we would have to modify the `toLCNFType` function and make sure a type former is erased if the expected
|
| 138 |
+
type is not always a type former (see `S.mk` type and example in the note above).
|
| 139 |
+
-/
|
| 140 |
+
def InferType.compatibleTypes (a b : Expr) : InferTypeM Bool := do
|
| 141 |
+
if compatibleTypesQuick a b then
|
| 142 |
+
return true
|
| 143 |
+
else
|
| 144 |
+
compatibleTypesFull a b
|
| 145 |
+
|
| 146 |
+
end Lean.Compiler.LCNF
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/CompilerM.lean
ADDED
|
@@ -0,0 +1,504 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2022 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.CoreM
|
| 8 |
+
import Lean.Compiler.LCNF.Basic
|
| 9 |
+
import Lean.Compiler.LCNF.LCtx
|
| 10 |
+
import Lean.Compiler.LCNF.ConfigOptions
|
| 11 |
+
|
| 12 |
+
namespace Lean.Compiler.LCNF
|
| 13 |
+
/--
|
| 14 |
+
The pipeline phase a certain `Pass` is supposed to happen in.
|
| 15 |
+
-/
|
| 16 |
+
inductive Phase where
|
| 17 |
+
/-- Here we still carry most of the original type information, most
|
| 18 |
+
of the dependent portion is already (partially) erased though. -/
|
| 19 |
+
| base
|
| 20 |
+
/-- In this phase polymorphism has been eliminated. -/
|
| 21 |
+
| mono
|
| 22 |
+
deriving Inhabited
|
| 23 |
+
|
| 24 |
+
/--
|
| 25 |
+
The state managed by the `CompilerM` `Monad`.
|
| 26 |
+
-/
|
| 27 |
+
structure CompilerM.State where
|
| 28 |
+
/--
|
| 29 |
+
A `LocalContext` to store local declarations from let binders
|
| 30 |
+
and other constructs in as we move through `Expr`s.
|
| 31 |
+
-/
|
| 32 |
+
lctx : LCtx := {}
|
| 33 |
+
/-- Next auxiliary variable suffix -/
|
| 34 |
+
nextIdx : Nat := 1
|
| 35 |
+
deriving Inhabited
|
| 36 |
+
|
| 37 |
+
structure CompilerM.Context where
|
| 38 |
+
phase : Phase
|
| 39 |
+
config : ConfigOptions
|
| 40 |
+
deriving Inhabited
|
| 41 |
+
|
| 42 |
+
abbrev CompilerM := ReaderT CompilerM.Context $ StateRefT CompilerM.State CoreM
|
| 43 |
+
|
| 44 |
+
@[always_inline]
|
| 45 |
+
instance : Monad CompilerM := let i := inferInstanceAs (Monad CompilerM); { pure := i.pure, bind := i.bind }
|
| 46 |
+
|
| 47 |
+
@[inline] def withPhase (phase : Phase) (x : CompilerM α) : CompilerM α :=
|
| 48 |
+
withReader (fun ctx => { ctx with phase }) x
|
| 49 |
+
|
| 50 |
+
def getPhase : CompilerM Phase :=
|
| 51 |
+
return (← read).phase
|
| 52 |
+
|
| 53 |
+
def inBasePhase : CompilerM Bool :=
|
| 54 |
+
return (← getPhase) matches .base
|
| 55 |
+
|
| 56 |
+
instance : AddMessageContext CompilerM where
|
| 57 |
+
addMessageContext msgData := do
|
| 58 |
+
let env ← getEnv
|
| 59 |
+
let lctx := (← get).lctx.toLocalContext
|
| 60 |
+
let opts ← getOptions
|
| 61 |
+
return MessageData.withContext { env, lctx, opts, mctx := {} } msgData
|
| 62 |
+
|
| 63 |
+
def getType (fvarId : FVarId) : CompilerM Expr := do
|
| 64 |
+
let lctx := (← get).lctx
|
| 65 |
+
if let some decl := lctx.letDecls[fvarId]? then
|
| 66 |
+
return decl.type
|
| 67 |
+
else if let some decl := lctx.params[fvarId]? then
|
| 68 |
+
return decl.type
|
| 69 |
+
else if let some decl := lctx.funDecls[fvarId]? then
|
| 70 |
+
return decl.type
|
| 71 |
+
else
|
| 72 |
+
throwError "unknown free variable {fvarId.name}"
|
| 73 |
+
|
| 74 |
+
def getBinderName (fvarId : FVarId) : CompilerM Name := do
|
| 75 |
+
let lctx := (← get).lctx
|
| 76 |
+
if let some decl := lctx.letDecls[fvarId]? then
|
| 77 |
+
return decl.binderName
|
| 78 |
+
else if let some decl := lctx.params[fvarId]? then
|
| 79 |
+
return decl.binderName
|
| 80 |
+
else if let some decl := lctx.funDecls[fvarId]? then
|
| 81 |
+
return decl.binderName
|
| 82 |
+
else
|
| 83 |
+
throwError "unknown free variable {fvarId.name}"
|
| 84 |
+
|
| 85 |
+
def findParam? (fvarId : FVarId) : CompilerM (Option Param) :=
|
| 86 |
+
return (← get).lctx.params[fvarId]?
|
| 87 |
+
|
| 88 |
+
def findLetDecl? (fvarId : FVarId) : CompilerM (Option LetDecl) :=
|
| 89 |
+
return (← get).lctx.letDecls[fvarId]?
|
| 90 |
+
|
| 91 |
+
def findFunDecl? (fvarId : FVarId) : CompilerM (Option FunDecl) :=
|
| 92 |
+
return (← get).lctx.funDecls[fvarId]?
|
| 93 |
+
|
| 94 |
+
def findLetValue? (fvarId : FVarId) : CompilerM (Option LetValue) := do
|
| 95 |
+
let some { value, .. } ← findLetDecl? fvarId | return none
|
| 96 |
+
return some value
|
| 97 |
+
|
| 98 |
+
def isConstructorApp (fvarId : FVarId) : CompilerM Bool := do
|
| 99 |
+
let some (.const declName _ _) ← findLetValue? fvarId | return false
|
| 100 |
+
return (← getEnv).find? declName matches some (.ctorInfo ..)
|
| 101 |
+
|
| 102 |
+
def Arg.isConstructorApp (arg : Arg) : CompilerM Bool := do
|
| 103 |
+
let .fvar fvarId := arg | return false
|
| 104 |
+
LCNF.isConstructorApp fvarId
|
| 105 |
+
|
| 106 |
+
def getParam (fvarId : FVarId) : CompilerM Param := do
|
| 107 |
+
let some param ← findParam? fvarId | throwError "unknown parameter {fvarId.name}"
|
| 108 |
+
return param
|
| 109 |
+
|
| 110 |
+
def getLetDecl (fvarId : FVarId) : CompilerM LetDecl := do
|
| 111 |
+
let some decl ← findLetDecl? fvarId | throwError "unknown let-declaration {fvarId.name}"
|
| 112 |
+
return decl
|
| 113 |
+
|
| 114 |
+
def getFunDecl (fvarId : FVarId) : CompilerM FunDecl := do
|
| 115 |
+
let some decl ← findFunDecl? fvarId | throwError "unknown local function {fvarId.name}"
|
| 116 |
+
return decl
|
| 117 |
+
|
| 118 |
+
@[inline] def modifyLCtx (f : LCtx → LCtx) : CompilerM Unit := do
|
| 119 |
+
modify fun s => { s with lctx := f s.lctx }
|
| 120 |
+
|
| 121 |
+
def eraseLetDecl (decl : LetDecl) : CompilerM Unit := do
|
| 122 |
+
modifyLCtx fun lctx => lctx.eraseLetDecl decl
|
| 123 |
+
|
| 124 |
+
def eraseFunDecl (decl : FunDecl) (recursive := true) : CompilerM Unit := do
|
| 125 |
+
modifyLCtx fun lctx => lctx.eraseFunDecl decl recursive
|
| 126 |
+
|
| 127 |
+
def eraseCode (code : Code) : CompilerM Unit := do
|
| 128 |
+
modifyLCtx fun lctx => lctx.eraseCode code
|
| 129 |
+
|
| 130 |
+
def eraseParam (param : Param) : CompilerM Unit :=
|
| 131 |
+
modifyLCtx fun lctx => lctx.eraseParam param
|
| 132 |
+
|
| 133 |
+
def eraseParams (params : Array Param) : CompilerM Unit :=
|
| 134 |
+
modifyLCtx fun lctx => lctx.eraseParams params
|
| 135 |
+
|
| 136 |
+
def eraseCodeDecl (decl : CodeDecl) : CompilerM Unit := do
|
| 137 |
+
match decl with
|
| 138 |
+
| .let decl => eraseLetDecl decl
|
| 139 |
+
| .jp decl | .fun decl => eraseFunDecl decl
|
| 140 |
+
|
| 141 |
+
/--
|
| 142 |
+
Erase all free variables occurring in `decls` from the local context.
|
| 143 |
+
-/
|
| 144 |
+
def eraseCodeDecls (decls : Array CodeDecl) : CompilerM Unit := do
|
| 145 |
+
decls.forM fun decl => eraseCodeDecl decl
|
| 146 |
+
|
| 147 |
+
def eraseDecl (decl : Decl) : CompilerM Unit := do
|
| 148 |
+
eraseParams decl.params
|
| 149 |
+
decl.value.forCodeM eraseCode
|
| 150 |
+
|
| 151 |
+
abbrev Decl.erase (decl : Decl) : CompilerM Unit :=
|
| 152 |
+
eraseDecl decl
|
| 153 |
+
|
| 154 |
+
/--
|
| 155 |
+
A free variable substitution.
|
| 156 |
+
We use these substitutions when inlining definitions and "internalizing" LCNF code into `CompilerM`.
|
| 157 |
+
During the internalization process, we ensure all free variables in the LCNF code do not collide with existing ones
|
| 158 |
+
at the `CompilerM` local context.
|
| 159 |
+
Remark: in LCNF, (computationally relevant) data is in A-normal form, but this is not the case for types and type formers.
|
| 160 |
+
So, when inlining we often want to replace a free variable with a type or type former.
|
| 161 |
+
|
| 162 |
+
The substitution contains entries `fvarId ↦ e` s.t., `e` is a valid LCNF argument. That is,
|
| 163 |
+
it is a free variable, a type (or type former), or `lcErased`.
|
| 164 |
+
|
| 165 |
+
`Check.lean` contains a substitution validator.
|
| 166 |
+
-/
|
| 167 |
+
abbrev FVarSubst := Std.HashMap FVarId Arg
|
| 168 |
+
|
| 169 |
+
/--
|
| 170 |
+
Replace the free variables in `e` using the given substitution.
|
| 171 |
+
|
| 172 |
+
If `translator = true`, then we assume the free variables occurring in the range of the substitution are in another
|
| 173 |
+
local context. For example, `translator = true` during internalization where we are making sure all free variables
|
| 174 |
+
in a given expression are replaced with new ones that do not collide with the ones in the current local context.
|
| 175 |
+
|
| 176 |
+
If `translator = false`, we assume the substitution contains free variable replacements in the same local context,
|
| 177 |
+
and given entries such as `x₁ ↦ x₂`, `x₂ ↦ x₃`, ..., `xₙ₋₁ ↦ xₙ`, and the expression `f x₁ x₂`, we want the resulting
|
| 178 |
+
expression to be `f xₙ xₙ`. We use this setting, for example, in the simplifier.
|
| 179 |
+
-/
|
| 180 |
+
private partial def normExprImp (s : FVarSubst) (e : Expr) (translator : Bool) : Expr :=
|
| 181 |
+
go e
|
| 182 |
+
where
|
| 183 |
+
goApp (e : Expr) : Expr :=
|
| 184 |
+
match e with
|
| 185 |
+
| .app f a => e.updateApp! (goApp f) (go a)
|
| 186 |
+
| _ => go e
|
| 187 |
+
|
| 188 |
+
go (e : Expr) : Expr :=
|
| 189 |
+
if e.hasFVar then
|
| 190 |
+
match e with
|
| 191 |
+
| .fvar fvarId => match s[fvarId]? with
|
| 192 |
+
| some (.fvar fvarId') => if translator then .fvar fvarId' else go (.fvar fvarId')
|
| 193 |
+
| some (.type e) => if translator then e else go e
|
| 194 |
+
| some .erased => erasedExpr
|
| 195 |
+
| none => e
|
| 196 |
+
| .lit .. | .const .. | .sort .. | .mvar .. | .bvar .. => e
|
| 197 |
+
| .app f a => e.updateApp! (goApp f) (go a) |>.headBeta
|
| 198 |
+
| .mdata _ b => e.updateMData! (go b)
|
| 199 |
+
| .proj _ _ b => e.updateProj! (go b)
|
| 200 |
+
| .forallE _ d b _ => e.updateForallE! (go d) (go b)
|
| 201 |
+
| .lam _ d b _ => e.updateLambdaE! (go d) (go b)
|
| 202 |
+
| .letE .. => unreachable! -- Valid LCNF does not contain `let`-declarations
|
| 203 |
+
else
|
| 204 |
+
e
|
| 205 |
+
|
| 206 |
+
/--
|
| 207 |
+
Result type for `normFVar` and `normFVarImp`.
|
| 208 |
+
-/
|
| 209 |
+
inductive NormFVarResult where
|
| 210 |
+
| /-- New free variable. -/
|
| 211 |
+
fvar (fvarId : FVarId)
|
| 212 |
+
| /--
|
| 213 |
+
Free variable has been erased. This can happen when instantiating polymorphic code
|
| 214 |
+
with computationally irrelevant stuff. -/
|
| 215 |
+
erased
|
| 216 |
+
deriving Inhabited
|
| 217 |
+
|
| 218 |
+
/--
|
| 219 |
+
Normalize the given free variable.
|
| 220 |
+
See `normExprImp` for documentation on the `translator` parameter.
|
| 221 |
+
This function is meant to be used in contexts where the input free-variable is computationally relevant.
|
| 222 |
+
This function panics if the substitution is mapping `fvarId` to an expression that is not another free variable.
|
| 223 |
+
That is, it is not a type (or type former), nor `lcErased`. Recall that a valid `FVarSubst` contains only
|
| 224 |
+
expressions that are free variables, `lcErased`, or type formers.
|
| 225 |
+
-/
|
| 226 |
+
private partial def normFVarImp (s : FVarSubst) (fvarId : FVarId) (translator : Bool) : NormFVarResult :=
|
| 227 |
+
match s[fvarId]? with
|
| 228 |
+
| some (.fvar fvarId') =>
|
| 229 |
+
if translator then
|
| 230 |
+
.fvar fvarId'
|
| 231 |
+
else
|
| 232 |
+
normFVarImp s fvarId' translator
|
| 233 |
+
-- Types and type formers are only preserved as hints and
|
| 234 |
+
-- are erased in computationally relevant contexts.
|
| 235 |
+
| some .erased | some (.type _) => .erased
|
| 236 |
+
| none => .fvar fvarId
|
| 237 |
+
|
| 238 |
+
/--
|
| 239 |
+
Replace the free variables in `arg` using the given substitution.
|
| 240 |
+
|
| 241 |
+
See `normExprImp`
|
| 242 |
+
-/
|
| 243 |
+
private partial def normArgImp (s : FVarSubst) (arg : Arg) (translator : Bool) : Arg :=
|
| 244 |
+
match arg with
|
| 245 |
+
| .erased => arg
|
| 246 |
+
| .fvar fvarId =>
|
| 247 |
+
match s[fvarId]? with
|
| 248 |
+
| some (arg'@(.fvar _)) =>
|
| 249 |
+
if translator then arg' else normArgImp s arg' translator
|
| 250 |
+
| some (arg'@.erased) | some (arg'@(.type _)) => arg'
|
| 251 |
+
| none => arg
|
| 252 |
+
| .type e => arg.updateType! (normExprImp s e translator)
|
| 253 |
+
|
| 254 |
+
private def normArgsImp (s : FVarSubst) (args : Array Arg) (translator : Bool) : Array Arg :=
|
| 255 |
+
args.mapMono (normArgImp s · translator)
|
| 256 |
+
|
| 257 |
+
/--
|
| 258 |
+
Replace the free variables in `e` using the given substitution.
|
| 259 |
+
|
| 260 |
+
See `normExprImp`
|
| 261 |
+
-/
|
| 262 |
+
private partial def normLetValueImp (s : FVarSubst) (e : LetValue) (translator : Bool) : LetValue :=
|
| 263 |
+
match e with
|
| 264 |
+
| .erased | .lit .. => e
|
| 265 |
+
| .proj _ _ fvarId => match normFVarImp s fvarId translator with
|
| 266 |
+
| .fvar fvarId' => e.updateProj! fvarId'
|
| 267 |
+
| .erased => .erased
|
| 268 |
+
| .const _ _ args => e.updateArgs! (normArgsImp s args translator)
|
| 269 |
+
| .fvar fvarId args => match normFVarImp s fvarId translator with
|
| 270 |
+
| .fvar fvarId' => e.updateFVar! fvarId' (normArgsImp s args translator)
|
| 271 |
+
| .erased => .erased
|
| 272 |
+
|
| 273 |
+
/--
|
| 274 |
+
Interface for monads that have a free substitutions.
|
| 275 |
+
-/
|
| 276 |
+
class MonadFVarSubst (m : Type → Type) (translator : outParam Bool) where
|
| 277 |
+
getSubst : m FVarSubst
|
| 278 |
+
|
| 279 |
+
export MonadFVarSubst (getSubst)
|
| 280 |
+
|
| 281 |
+
instance (m n) [MonadLift m n] [MonadFVarSubst m t] : MonadFVarSubst n t where
|
| 282 |
+
getSubst := liftM (getSubst : m _)
|
| 283 |
+
|
| 284 |
+
class MonadFVarSubstState (m : Type → Type) where
|
| 285 |
+
modifySubst : (FVarSubst → FVarSubst) → m Unit
|
| 286 |
+
|
| 287 |
+
export MonadFVarSubstState (modifySubst)
|
| 288 |
+
|
| 289 |
+
instance (m n) [MonadLift m n] [MonadFVarSubstState m] : MonadFVarSubstState n where
|
| 290 |
+
modifySubst f := liftM (modifySubst f : m _)
|
| 291 |
+
|
| 292 |
+
/--
|
| 293 |
+
Add the substitution `fvarId ↦ e`, `e` must be a valid LCNF `Arg`.
|
| 294 |
+
|
| 295 |
+
See `Check.lean` for the free variable substitution checker.
|
| 296 |
+
-/
|
| 297 |
+
@[inline] def addSubst [MonadFVarSubstState m] (fvarId : FVarId) (arg : Arg) : m Unit :=
|
| 298 |
+
modifySubst fun s => s.insert fvarId arg
|
| 299 |
+
|
| 300 |
+
/--
|
| 301 |
+
Add the entry `fvarId ↦ fvarId'` to the free variable substitution.
|
| 302 |
+
-/
|
| 303 |
+
@[inline] def addFVarSubst [MonadFVarSubstState m] (fvarId : FVarId) (fvarId' : FVarId) : m Unit :=
|
| 304 |
+
modifySubst fun s => s.insert fvarId (.fvar fvarId')
|
| 305 |
+
|
| 306 |
+
@[inline, inherit_doc normFVarImp] def normFVar [MonadFVarSubst m t] [Monad m] (fvarId : FVarId) : m NormFVarResult :=
|
| 307 |
+
return normFVarImp (← getSubst) fvarId t
|
| 308 |
+
|
| 309 |
+
@[inline, inherit_doc normExprImp] def normExpr [MonadFVarSubst m t] [Monad m] (e : Expr) : m Expr :=
|
| 310 |
+
return normExprImp (← getSubst) e t
|
| 311 |
+
|
| 312 |
+
@[inline, inherit_doc normArgImp] def normArg [MonadFVarSubst m t] [Monad m] (arg : Arg) : m Arg :=
|
| 313 |
+
return normArgImp (← getSubst) arg t
|
| 314 |
+
|
| 315 |
+
@[inline, inherit_doc normLetValueImp] def normLetValue [MonadFVarSubst m t] [Monad m] (e : LetValue) : m LetValue :=
|
| 316 |
+
return normLetValueImp (← getSubst) e t
|
| 317 |
+
|
| 318 |
+
@[inherit_doc normExprImp]
|
| 319 |
+
abbrev normExprCore (s : FVarSubst) (e : Expr) (translator : Bool) : Expr :=
|
| 320 |
+
normExprImp s e translator
|
| 321 |
+
|
| 322 |
+
/--
|
| 323 |
+
Normalize the given arguments using the current substitution.
|
| 324 |
+
-/
|
| 325 |
+
def normArgs [MonadFVarSubst m t] [Monad m] (args : Array Arg) : m (Array Arg) :=
|
| 326 |
+
return normArgsImp (← getSubst) args t
|
| 327 |
+
|
| 328 |
+
def mkFreshBinderName (binderName := `_x): CompilerM Name := do
|
| 329 |
+
let declName := .num binderName (← get).nextIdx
|
| 330 |
+
modify fun s => { s with nextIdx := s.nextIdx + 1 }
|
| 331 |
+
return declName
|
| 332 |
+
|
| 333 |
+
def ensureNotAnonymous (binderName : Name) (baseName : Name) : CompilerM Name :=
|
| 334 |
+
if binderName.isAnonymous then
|
| 335 |
+
mkFreshBinderName baseName
|
| 336 |
+
else
|
| 337 |
+
return binderName
|
| 338 |
+
|
| 339 |
+
/-!
|
| 340 |
+
Helper functions for creating LCNF local declarations.
|
| 341 |
+
-/
|
| 342 |
+
|
| 343 |
+
def mkParam (binderName : Name) (type : Expr) (borrow : Bool) : CompilerM Param := do
|
| 344 |
+
let fvarId ← mkFreshFVarId
|
| 345 |
+
let binderName ← ensureNotAnonymous binderName `_y
|
| 346 |
+
let param := { fvarId, binderName, type, borrow }
|
| 347 |
+
modifyLCtx fun lctx => lctx.addParam param
|
| 348 |
+
return param
|
| 349 |
+
|
| 350 |
+
def mkLetDecl (binderName : Name) (type : Expr) (value : LetValue) : CompilerM LetDecl := do
|
| 351 |
+
let fvarId ← mkFreshFVarId
|
| 352 |
+
let binderName ← ensureNotAnonymous binderName `_x
|
| 353 |
+
let decl := { fvarId, binderName, type, value }
|
| 354 |
+
modifyLCtx fun lctx => lctx.addLetDecl decl
|
| 355 |
+
return decl
|
| 356 |
+
|
| 357 |
+
def mkFunDecl (binderName : Name) (type : Expr) (params : Array Param) (value : Code) : CompilerM FunDecl := do
|
| 358 |
+
let fvarId ← mkFreshFVarId
|
| 359 |
+
let binderName ← ensureNotAnonymous binderName `_f
|
| 360 |
+
let funDecl := { fvarId, binderName, type, params, value }
|
| 361 |
+
modifyLCtx fun lctx => lctx.addFunDecl funDecl
|
| 362 |
+
return funDecl
|
| 363 |
+
|
| 364 |
+
def mkLetDeclErased : CompilerM LetDecl := do
|
| 365 |
+
mkLetDecl (← mkFreshBinderName `_x) erasedExpr .erased
|
| 366 |
+
|
| 367 |
+
def mkReturnErased : CompilerM Code := do
|
| 368 |
+
let auxDecl ← mkLetDeclErased
|
| 369 |
+
return .let auxDecl (.return auxDecl.fvarId)
|
| 370 |
+
|
| 371 |
+
private unsafe def updateParamImp (p : Param) (type : Expr) : CompilerM Param := do
|
| 372 |
+
if ptrEq type p.type then
|
| 373 |
+
return p
|
| 374 |
+
else
|
| 375 |
+
let p := { p with type }
|
| 376 |
+
modifyLCtx fun lctx => lctx.addParam p
|
| 377 |
+
return p
|
| 378 |
+
|
| 379 |
+
@[implemented_by updateParamImp] opaque Param.update (p : Param) (type : Expr) : CompilerM Param
|
| 380 |
+
|
| 381 |
+
private unsafe def updateLetDeclImp (decl : LetDecl) (type : Expr) (value : LetValue) : CompilerM LetDecl := do
|
| 382 |
+
if ptrEq type decl.type && ptrEq value decl.value then
|
| 383 |
+
return decl
|
| 384 |
+
else
|
| 385 |
+
let decl := { decl with type, value }
|
| 386 |
+
modifyLCtx fun lctx => lctx.addLetDecl decl
|
| 387 |
+
return decl
|
| 388 |
+
|
| 389 |
+
@[implemented_by updateLetDeclImp] opaque LetDecl.update (decl : LetDecl) (type : Expr) (value : LetValue) : CompilerM LetDecl
|
| 390 |
+
|
| 391 |
+
def LetDecl.updateValue (decl : LetDecl) (value : LetValue) : CompilerM LetDecl :=
|
| 392 |
+
decl.update decl.type value
|
| 393 |
+
|
| 394 |
+
private unsafe def updateFunDeclImp (decl : FunDecl) (type : Expr) (params : Array Param) (value : Code) : CompilerM FunDecl := do
|
| 395 |
+
if ptrEq type decl.type && ptrEq params decl.params && ptrEq value decl.value then
|
| 396 |
+
return decl
|
| 397 |
+
else
|
| 398 |
+
let decl := { decl with type, params, value }
|
| 399 |
+
modifyLCtx fun lctx => lctx.addFunDecl decl
|
| 400 |
+
return decl
|
| 401 |
+
|
| 402 |
+
@[implemented_by updateFunDeclImp] opaque FunDecl.update (decl : FunDecl) (type : Expr) (params : Array Param) (value : Code) : CompilerM FunDecl
|
| 403 |
+
|
| 404 |
+
abbrev FunDecl.update' (decl : FunDecl) (type : Expr) (value : Code) : CompilerM FunDecl :=
|
| 405 |
+
decl.update type decl.params value
|
| 406 |
+
|
| 407 |
+
abbrev FunDecl.updateValue (decl : FunDecl) (value : Code) : CompilerM FunDecl :=
|
| 408 |
+
decl.update decl.type decl.params value
|
| 409 |
+
|
| 410 |
+
@[inline] def normParam [MonadLiftT CompilerM m] [Monad m] [MonadFVarSubst m t] (p : Param) : m Param := do
|
| 411 |
+
p.update (← normExpr p.type)
|
| 412 |
+
|
| 413 |
+
def normParams [MonadLiftT CompilerM m] [Monad m] [MonadFVarSubst m t] (ps : Array Param) : m (Array Param) :=
|
| 414 |
+
ps.mapMonoM normParam
|
| 415 |
+
|
| 416 |
+
def normLetDecl [MonadLiftT CompilerM m] [Monad m] [MonadFVarSubst m t] (decl : LetDecl) : m LetDecl := do
|
| 417 |
+
decl.update (← normExpr decl.type) (← normLetValue decl.value)
|
| 418 |
+
|
| 419 |
+
abbrev NormalizerM (_translator : Bool) := ReaderT FVarSubst CompilerM
|
| 420 |
+
|
| 421 |
+
instance : MonadFVarSubst (NormalizerM t) t where
|
| 422 |
+
getSubst := read
|
| 423 |
+
|
| 424 |
+
/--
|
| 425 |
+
If `result` is `.fvar fvarId`, then return `x fvarId`. Otherwise, it is `.erased`,
|
| 426 |
+
and method returns `let _x.i := .erased; return _x.i`.
|
| 427 |
+
-/
|
| 428 |
+
@[inline] def withNormFVarResult [MonadLiftT CompilerM m] [Monad m] (result : NormFVarResult) (x : FVarId → m Code) : m Code := do
|
| 429 |
+
match result with
|
| 430 |
+
| .fvar fvarId => x fvarId
|
| 431 |
+
| .erased => mkReturnErased
|
| 432 |
+
|
| 433 |
+
mutual
|
| 434 |
+
partial def normFunDeclImp (decl : FunDecl) : NormalizerM t FunDecl := do
|
| 435 |
+
let type ← normExpr decl.type
|
| 436 |
+
let params ← normParams decl.params
|
| 437 |
+
let value ← normCodeImp decl.value
|
| 438 |
+
decl.update type params value
|
| 439 |
+
|
| 440 |
+
partial def normCodeImp (code : Code) : NormalizerM t Code := do
|
| 441 |
+
match code with
|
| 442 |
+
| .let decl k => return code.updateLet! (← normLetDecl decl) (← normCodeImp k)
|
| 443 |
+
| .fun decl k | .jp decl k => return code.updateFun! (← normFunDeclImp decl) (← normCodeImp k)
|
| 444 |
+
| .return fvarId => withNormFVarResult (← normFVar fvarId) fun fvarId => return code.updateReturn! fvarId
|
| 445 |
+
| .jmp fvarId args => withNormFVarResult (← normFVar fvarId) fun fvarId => return code.updateJmp! fvarId (← normArgs args)
|
| 446 |
+
| .unreach type => return code.updateUnreach! (← normExpr type)
|
| 447 |
+
| .cases c =>
|
| 448 |
+
let resultType ← normExpr c.resultType
|
| 449 |
+
withNormFVarResult (← normFVar c.discr) fun discr => do
|
| 450 |
+
let alts ← c.alts.mapMonoM fun alt =>
|
| 451 |
+
match alt with
|
| 452 |
+
| .alt _ params k => return alt.updateAlt! (← normParams params) (← normCodeImp k)
|
| 453 |
+
| .default k => return alt.updateCode (← normCodeImp k)
|
| 454 |
+
return code.updateCases! resultType discr alts
|
| 455 |
+
end
|
| 456 |
+
|
| 457 |
+
@[inline] def normFunDecl [MonadLiftT CompilerM m] [Monad m] [MonadFVarSubst m t] (decl : FunDecl) : m FunDecl := do
|
| 458 |
+
normFunDeclImp (t := t) decl (← getSubst)
|
| 459 |
+
|
| 460 |
+
/-- Similar to `internalize`, but does not refresh `FVarId`s. -/
|
| 461 |
+
@[inline] def normCode [MonadLiftT CompilerM m] [Monad m] [MonadFVarSubst m t] (code : Code) : m Code := do
|
| 462 |
+
normCodeImp (t := t) code (← getSubst)
|
| 463 |
+
|
| 464 |
+
def replaceExprFVars (e : Expr) (s : FVarSubst) (translator : Bool) : CompilerM Expr :=
|
| 465 |
+
(normExpr e : NormalizerM translator Expr).run s
|
| 466 |
+
|
| 467 |
+
def replaceFVars (code : Code) (s : FVarSubst) (translator : Bool) : CompilerM Code :=
|
| 468 |
+
(normCode code : NormalizerM translator Code).run s
|
| 469 |
+
|
| 470 |
+
def mkFreshJpName : CompilerM Name := do
|
| 471 |
+
mkFreshBinderName `_jp
|
| 472 |
+
|
| 473 |
+
def mkAuxParam (type : Expr) (borrow := false) : CompilerM Param := do
|
| 474 |
+
mkParam (← mkFreshBinderName `_y) type borrow
|
| 475 |
+
|
| 476 |
+
def getConfig : CompilerM ConfigOptions :=
|
| 477 |
+
return (← read).config
|
| 478 |
+
|
| 479 |
+
def CompilerM.run (x : CompilerM α) (s : State := {}) (phase : Phase := .base) : CoreM α := do
|
| 480 |
+
x { phase, config := toConfigOptions (← getOptions) } |>.run' s
|
| 481 |
+
|
| 482 |
+
/-- Environment extension for local caching of key-value pairs, not persisted in .olean files. -/
|
| 483 |
+
structure CacheExtension (α β : Type) [BEq α] [Hashable α] extends EnvExtension (List α × PHashMap α β)
|
| 484 |
+
deriving Inhabited
|
| 485 |
+
|
| 486 |
+
namespace CacheExtension
|
| 487 |
+
|
| 488 |
+
def register [BEq α] [Hashable α] [Inhabited β] :
|
| 489 |
+
IO (CacheExtension α β) :=
|
| 490 |
+
CacheExtension.mk <$> registerEnvExtension (pure ([], {})) (asyncMode := .sync) -- compilation is non-parallel anyway
|
| 491 |
+
(replay? := some fun oldState newState _ s =>
|
| 492 |
+
let newEntries := newState.1.take (newState.1.length - oldState.1.length)
|
| 493 |
+
newEntries.foldl (init := s) fun s e =>
|
| 494 |
+
(e :: s.1, s.2.insert e (newState.2.find! e)))
|
| 495 |
+
|
| 496 |
+
def insert [BEq α] [Hashable α] [Inhabited β] (ext : CacheExtension α β) (a : α) (b : β) : CoreM Unit := do
|
| 497 |
+
modifyEnv (ext.modifyState · fun ⟨as, m⟩ => (a :: as, m.insert a b))
|
| 498 |
+
|
| 499 |
+
def find? [BEq α] [Hashable α] [Inhabited β] (ext : CacheExtension α β) (a : α) : CoreM (Option β) := do
|
| 500 |
+
return ext.toEnvExtension.getState (← getEnv) |>.2.find? a
|
| 501 |
+
|
| 502 |
+
end CacheExtension
|
| 503 |
+
|
| 504 |
+
end Lean.Compiler.LCNF
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/ConfigOptions.lean
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2022 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Data.Options
|
| 8 |
+
|
| 9 |
+
namespace Lean.Compiler.LCNF
|
| 10 |
+
|
| 11 |
+
/--
|
| 12 |
+
User controlled configuration options for the code generator.
|
| 13 |
+
-/
|
| 14 |
+
structure ConfigOptions where
|
| 15 |
+
/--
|
| 16 |
+
Any function declaration or join point with size `≤ smallThresold` is inlined
|
| 17 |
+
even if there are multiple occurrences.
|
| 18 |
+
-/
|
| 19 |
+
smallThreshold : Nat := 1
|
| 20 |
+
/--
|
| 21 |
+
Maximum number of times a recursive definition tagged with `[inline]` can be recursively inlined before generating an
|
| 22 |
+
error during compilation.
|
| 23 |
+
-/
|
| 24 |
+
maxRecInline : Nat := 1
|
| 25 |
+
/--
|
| 26 |
+
Maximum number of times a recursive definition tagged with `[inline_if_reduce]` can be recursively inlined
|
| 27 |
+
before generating an error during compilation.
|
| 28 |
+
-/
|
| 29 |
+
maxRecInlineIfReduce : Nat := 16
|
| 30 |
+
/--
|
| 31 |
+
Perform type compatibility checking after each compiler pass.
|
| 32 |
+
-/
|
| 33 |
+
checkTypes : Bool := false
|
| 34 |
+
deriving Inhabited
|
| 35 |
+
|
| 36 |
+
register_builtin_option compiler.small : Nat := {
|
| 37 |
+
defValue := 1
|
| 38 |
+
group := "compiler"
|
| 39 |
+
descr := "(compiler) function declarations with size `≤ small` is inlined even if there are multiple occurrences."
|
| 40 |
+
}
|
| 41 |
+
|
| 42 |
+
register_builtin_option compiler.maxRecInline : Nat := {
|
| 43 |
+
defValue := 1
|
| 44 |
+
group := "compiler"
|
| 45 |
+
descr := "(compiler) maximum number of times a recursive definition tagged with `[inline]` can be recursively inlined before generating an error during compilation."
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
register_builtin_option compiler.maxRecInlineIfReduce : Nat := {
|
| 49 |
+
defValue := 16
|
| 50 |
+
group := "compiler"
|
| 51 |
+
descr := "(compiler) maximum number of times a recursive definition tagged with `[inline_if_reduce]` can be recursively inlined before generating an error during compilation."
|
| 52 |
+
}
|
| 53 |
+
|
| 54 |
+
register_builtin_option compiler.checkTypes : Bool := {
|
| 55 |
+
defValue := false
|
| 56 |
+
group := "compiler"
|
| 57 |
+
descr := "(compiler) perform type compatibility checking after each compiler pass. Note this is not a complete check, and it is used only for debugging purposes. It fails in code that makes heavy use of dependent types."
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
def toConfigOptions (opts : Options) : ConfigOptions := {
|
| 61 |
+
smallThreshold := compiler.small.get opts
|
| 62 |
+
maxRecInline := compiler.maxRecInline.get opts
|
| 63 |
+
maxRecInlineIfReduce := compiler.maxRecInlineIfReduce.get opts
|
| 64 |
+
checkTypes := compiler.checkTypes.get opts
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
end Lean.Compiler.LCNF
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/DeclHash.lean
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2022 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Compiler.LCNF.Basic
|
| 8 |
+
|
| 9 |
+
namespace Lean.Compiler.LCNF
|
| 10 |
+
|
| 11 |
+
instance : Hashable Param where
|
| 12 |
+
hash p := mixHash (hash p.fvarId) (hash p.type)
|
| 13 |
+
|
| 14 |
+
def hashParams (ps : Array Param) : UInt64 :=
|
| 15 |
+
hash ps
|
| 16 |
+
|
| 17 |
+
mutual
|
| 18 |
+
partial def hashAlt (alt : Alt) : UInt64 :=
|
| 19 |
+
match alt with
|
| 20 |
+
| .alt ctorName ps k => mixHash (mixHash (hash ctorName) (hash ps)) (hashCode k)
|
| 21 |
+
| .default k => hashCode k
|
| 22 |
+
|
| 23 |
+
partial def hashAlts (alts : Array Alt) : UInt64 :=
|
| 24 |
+
alts.foldl (fun r a => mixHash r (hashAlt a)) 7
|
| 25 |
+
|
| 26 |
+
partial def hashCode (code : Code) : UInt64 :=
|
| 27 |
+
match code with
|
| 28 |
+
| .let decl k => mixHash (mixHash (hash decl.fvarId) (hash decl.type)) (mixHash (hash decl.value) (hashCode k))
|
| 29 |
+
| .fun decl k | .jp decl k =>
|
| 30 |
+
mixHash (mixHash (mixHash (hash decl.fvarId) (hash decl.type)) (mixHash (hashCode decl.value) (hashCode k))) (hash decl.params)
|
| 31 |
+
| .return fvarId => hash fvarId
|
| 32 |
+
| .unreach type => hash type
|
| 33 |
+
| .jmp fvarId args => mixHash (hash fvarId) (hash args)
|
| 34 |
+
| .cases c => mixHash (mixHash (hash c.discr) (hash c.resultType)) (hashAlts c.alts)
|
| 35 |
+
|
| 36 |
+
end
|
| 37 |
+
|
| 38 |
+
instance : Hashable Code where
|
| 39 |
+
hash c := hashCode c
|
| 40 |
+
|
| 41 |
+
deriving instance Hashable for DeclValue
|
| 42 |
+
deriving instance Hashable for Decl
|
| 43 |
+
|
| 44 |
+
end Lean.Compiler.LCNF
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/DependsOn.lean
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2022 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Compiler.LCNF.Basic
|
| 8 |
+
|
| 9 |
+
namespace Lean.Compiler.LCNF
|
| 10 |
+
|
| 11 |
+
private abbrev M := ReaderT FVarIdSet Id
|
| 12 |
+
|
| 13 |
+
private def fvarDepOn (fvarId : FVarId) : M Bool :=
|
| 14 |
+
return (← read).contains fvarId
|
| 15 |
+
|
| 16 |
+
private def typeDepOn (e : Expr) : M Bool := do
|
| 17 |
+
let s ← read
|
| 18 |
+
return e.hasAnyFVar fun fvarId => s.contains fvarId
|
| 19 |
+
|
| 20 |
+
private def argDepOn (a : Arg) : M Bool := do
|
| 21 |
+
match a with
|
| 22 |
+
| .erased => return false
|
| 23 |
+
| .fvar fvarId => fvarDepOn fvarId
|
| 24 |
+
| .type e => typeDepOn e
|
| 25 |
+
|
| 26 |
+
private def letValueDepOn (e : LetValue) : M Bool :=
|
| 27 |
+
match e with
|
| 28 |
+
| .erased | .lit .. => return false
|
| 29 |
+
| .proj _ _ fvarId => fvarDepOn fvarId
|
| 30 |
+
| .fvar fvarId args => fvarDepOn fvarId <||> args.anyM argDepOn
|
| 31 |
+
| .const _ _ args => args.anyM argDepOn
|
| 32 |
+
|
| 33 |
+
private def LetDecl.depOn (decl : LetDecl) : M Bool :=
|
| 34 |
+
typeDepOn decl.type <||> letValueDepOn decl.value
|
| 35 |
+
|
| 36 |
+
private partial def depOn (c : Code) : M Bool :=
|
| 37 |
+
match c with
|
| 38 |
+
| .let decl k => decl.depOn <||> depOn k
|
| 39 |
+
| .jp decl k | .fun decl k => typeDepOn decl.type <||> depOn decl.value <||> depOn k
|
| 40 |
+
| .cases c => typeDepOn c.resultType <||> fvarDepOn c.discr <||> c.alts.anyM fun alt => depOn alt.getCode
|
| 41 |
+
| .jmp fvarId args => fvarDepOn fvarId <||> args.anyM argDepOn
|
| 42 |
+
| .return fvarId => fvarDepOn fvarId
|
| 43 |
+
| .unreach _ => return false
|
| 44 |
+
|
| 45 |
+
abbrev LetDecl.dependsOn (decl : LetDecl) (s : FVarIdSet) : Bool :=
|
| 46 |
+
decl.depOn s
|
| 47 |
+
|
| 48 |
+
abbrev FunDecl.dependsOn (decl : FunDecl) (s : FVarIdSet) : Bool :=
|
| 49 |
+
typeDepOn decl.type s || depOn decl.value s
|
| 50 |
+
|
| 51 |
+
def CodeDecl.dependsOn (decl : CodeDecl) (s : FVarIdSet) : Bool :=
|
| 52 |
+
match decl with
|
| 53 |
+
| .let decl => decl.dependsOn s
|
| 54 |
+
| .jp decl | .fun decl => decl.dependsOn s
|
| 55 |
+
|
| 56 |
+
/--
|
| 57 |
+
Return `true` is `c` depends on a free variable in `s`.
|
| 58 |
+
-/
|
| 59 |
+
def Code.dependsOn (c : Code) (s : FVarIdSet) : Bool :=
|
| 60 |
+
depOn c s
|
| 61 |
+
|
| 62 |
+
end Lean.Compiler.LCNF
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/ElimDead.lean
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2022 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Compiler.LCNF.CompilerM
|
| 8 |
+
|
| 9 |
+
namespace Lean.Compiler.LCNF
|
| 10 |
+
|
| 11 |
+
abbrev UsedLocalDecls := FVarIdHashSet
|
| 12 |
+
|
| 13 |
+
/--
|
| 14 |
+
Collect set of (let) free variables in a LCNF value.
|
| 15 |
+
This code exploits the LCNF property that local declarations do not occur in types.
|
| 16 |
+
-/
|
| 17 |
+
def collectLocalDeclsType (s : UsedLocalDecls) (type : Expr) : UsedLocalDecls :=
|
| 18 |
+
go s type
|
| 19 |
+
where
|
| 20 |
+
go (s : UsedLocalDecls) (e : Expr) : UsedLocalDecls :=
|
| 21 |
+
match e with
|
| 22 |
+
| .forallE .. => s
|
| 23 |
+
| .lam _ _ b _ => go s b
|
| 24 |
+
| .app f a => go (go s a) f
|
| 25 |
+
| .fvar fvarId => s.insert fvarId
|
| 26 |
+
| .letE .. | .proj .. | .mdata .. => unreachable! -- Valid LCNF type does not contain this kind of expr
|
| 27 |
+
| _ => s
|
| 28 |
+
|
| 29 |
+
def collectLocalDeclsArg (s : UsedLocalDecls) (arg : Arg) : UsedLocalDecls :=
|
| 30 |
+
match arg with
|
| 31 |
+
| .erased => s
|
| 32 |
+
| .type e => collectLocalDeclsType s e
|
| 33 |
+
| .fvar fvarId => s.insert fvarId
|
| 34 |
+
|
| 35 |
+
def collectLocalDeclsArgs (s : UsedLocalDecls) (args : Array Arg) : UsedLocalDecls :=
|
| 36 |
+
args.foldl (init := s) collectLocalDeclsArg
|
| 37 |
+
|
| 38 |
+
def collectLocalDeclsLetValue (s : UsedLocalDecls) (e : LetValue) : UsedLocalDecls :=
|
| 39 |
+
match e with
|
| 40 |
+
| .erased | .lit .. => s
|
| 41 |
+
| .proj _ _ fvarId => s.insert fvarId
|
| 42 |
+
| .const _ _ args => collectLocalDeclsArgs s args
|
| 43 |
+
| .fvar fvarId args => collectLocalDeclsArgs (s.insert fvarId) args
|
| 44 |
+
|
| 45 |
+
namespace ElimDead
|
| 46 |
+
|
| 47 |
+
abbrev M := StateRefT UsedLocalDecls CompilerM
|
| 48 |
+
|
| 49 |
+
private abbrev collectArgM (arg : Arg) : M Unit :=
|
| 50 |
+
modify (collectLocalDeclsArg · arg)
|
| 51 |
+
|
| 52 |
+
private abbrev collectLetValueM (e : LetValue) : M Unit :=
|
| 53 |
+
modify (collectLocalDeclsLetValue · e)
|
| 54 |
+
|
| 55 |
+
private abbrev collectFVarM (fvarId : FVarId) : M Unit :=
|
| 56 |
+
modify (·.insert fvarId)
|
| 57 |
+
|
| 58 |
+
mutual
|
| 59 |
+
partial def visitFunDecl (funDecl : FunDecl) : M FunDecl := do
|
| 60 |
+
let value ← elimDead funDecl.value
|
| 61 |
+
funDecl.updateValue value
|
| 62 |
+
|
| 63 |
+
partial def elimDead (code : Code) : M Code := do
|
| 64 |
+
match code with
|
| 65 |
+
| .let decl k =>
|
| 66 |
+
let k ← elimDead k
|
| 67 |
+
if (← get).contains decl.fvarId then
|
| 68 |
+
/- Remark: we don't need to collect `decl.type` because LCNF local declarations do not occur in types. -/
|
| 69 |
+
collectLetValueM decl.value
|
| 70 |
+
return code.updateCont! k
|
| 71 |
+
else
|
| 72 |
+
eraseLetDecl decl
|
| 73 |
+
return k
|
| 74 |
+
| .fun decl k | .jp decl k =>
|
| 75 |
+
let k ← elimDead k
|
| 76 |
+
if (← get).contains decl.fvarId then
|
| 77 |
+
let decl ← visitFunDecl decl
|
| 78 |
+
return code.updateFun! decl k
|
| 79 |
+
else
|
| 80 |
+
eraseFunDecl decl
|
| 81 |
+
return k
|
| 82 |
+
| .cases c =>
|
| 83 |
+
let alts ← c.alts.mapMonoM fun alt => return alt.updateCode (← elimDead alt.getCode)
|
| 84 |
+
collectFVarM c.discr
|
| 85 |
+
return code.updateAlts! alts
|
| 86 |
+
| .return fvarId => collectFVarM fvarId; return code
|
| 87 |
+
| .jmp fvarId args => collectFVarM fvarId; args.forM collectArgM; return code
|
| 88 |
+
| .unreach .. => return code
|
| 89 |
+
|
| 90 |
+
end
|
| 91 |
+
|
| 92 |
+
end ElimDead
|
| 93 |
+
|
| 94 |
+
def Code.elimDead (code : Code) : CompilerM Code :=
|
| 95 |
+
ElimDead.elimDead code |>.run' {}
|
| 96 |
+
|
| 97 |
+
def Decl.elimDead (decl : Decl) : CompilerM Decl := do
|
| 98 |
+
return { decl with value := (← decl.value.mapCodeM Code.elimDead) }
|
| 99 |
+
|
| 100 |
+
end Lean.Compiler.LCNF
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/ElimDeadBranches.lean
ADDED
|
@@ -0,0 +1,630 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2022 Henrik Böving. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Henrik Böving
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Compiler.LCNF.CompilerM
|
| 8 |
+
import Lean.Compiler.LCNF.PassManager
|
| 9 |
+
import Lean.Compiler.LCNF.PhaseExt
|
| 10 |
+
import Lean.Compiler.LCNF.InferType
|
| 11 |
+
|
| 12 |
+
namespace Lean.Compiler.LCNF
|
| 13 |
+
|
| 14 |
+
namespace UnreachableBranches
|
| 15 |
+
|
| 16 |
+
/--
|
| 17 |
+
The abstract domain of the interpreter. Representing sets of values
|
| 18 |
+
of a certain type.
|
| 19 |
+
-/
|
| 20 |
+
inductive Value where
|
| 21 |
+
/--
|
| 22 |
+
Undefined, could be anything we have no information.
|
| 23 |
+
-/
|
| 24 |
+
| bot
|
| 25 |
+
/--
|
| 26 |
+
All values are possible.
|
| 27 |
+
-/
|
| 28 |
+
| top
|
| 29 |
+
/--
|
| 30 |
+
A certain constructor with a certain sets of parameters is possible.
|
| 31 |
+
-/
|
| 32 |
+
| ctor (i : Name) (vs : Array Value)
|
| 33 |
+
/--
|
| 34 |
+
A set of values are possible.
|
| 35 |
+
-/
|
| 36 |
+
| choice (vs : List Value)
|
| 37 |
+
deriving Inhabited, Repr
|
| 38 |
+
|
| 39 |
+
namespace Value
|
| 40 |
+
|
| 41 |
+
-- TODO: parameterize
|
| 42 |
+
def maxValueDepth := 8
|
| 43 |
+
|
| 44 |
+
protected partial def beq : Value → Value → Bool
|
| 45 |
+
| bot, bot => true
|
| 46 |
+
| top, top => true
|
| 47 |
+
| ctor i1 vs1 , ctor i2 vs2 =>
|
| 48 |
+
i1 == i2 && Array.isEqv vs1 vs2 Value.beq
|
| 49 |
+
| choice vs1 , choice vs2 =>
|
| 50 |
+
let isSubset as bs := as.all (fun a => bs.any fun b => Value.beq a b)
|
| 51 |
+
isSubset vs1 vs2 && isSubset vs2 vs1
|
| 52 |
+
| _, _ => false
|
| 53 |
+
|
| 54 |
+
instance : BEq Value := ⟨Value.beq⟩
|
| 55 |
+
|
| 56 |
+
mutual
|
| 57 |
+
|
| 58 |
+
/--
|
| 59 |
+
Fuse `v` into `vs`. That is do not only append but if we see that `v`
|
| 60 |
+
is a constructor that is already contained within `vs` try to detect
|
| 61 |
+
the difference between these values and merge them accordingly into a
|
| 62 |
+
choice node further down the tree.
|
| 63 |
+
-/
|
| 64 |
+
partial def addChoice (vs : List Value) (v : Value) : List Value :=
|
| 65 |
+
match vs, v with
|
| 66 |
+
| [], v => [v]
|
| 67 |
+
| v1@(ctor i1 _ ) :: cs, ctor i2 _ =>
|
| 68 |
+
if i1 == i2 then
|
| 69 |
+
(merge v1 v) :: cs
|
| 70 |
+
else
|
| 71 |
+
v1 :: addChoice cs v
|
| 72 |
+
| _, _ => panic! "invalid addChoice"
|
| 73 |
+
|
| 74 |
+
/--
|
| 75 |
+
Merge two values into one. `bot` is the neutral element, `top` the annihilator.
|
| 76 |
+
-/
|
| 77 |
+
partial def merge (v1 v2 : Value) : Value :=
|
| 78 |
+
match v1, v2 with
|
| 79 |
+
| bot, v | v, bot => v
|
| 80 |
+
| top, _ | _, top => top
|
| 81 |
+
| ctor i1 vs1, ctor i2 vs2 =>
|
| 82 |
+
if i1 == i2 then
|
| 83 |
+
ctor i1 (Array.zipWith merge vs1 vs2)
|
| 84 |
+
else
|
| 85 |
+
choice [v1, v2]
|
| 86 |
+
| choice vs1, choice vs2 =>
|
| 87 |
+
choice (vs1.foldl addChoice vs2)
|
| 88 |
+
| choice vs, v | v, choice vs =>
|
| 89 |
+
choice (addChoice vs v)
|
| 90 |
+
|
| 91 |
+
end
|
| 92 |
+
|
| 93 |
+
/--
|
| 94 |
+
Make sure constructors of recursive inductive datatypes can only occur once in each path.
|
| 95 |
+
Values at depth > `maxValueDepth` are also approximated at `top`.
|
| 96 |
+
We use this function to implement a simple widening operation for our abstract interpreter.
|
| 97 |
+
Recall the widening functions is used to ensure termination in abstract interpreters.
|
| 98 |
+
-/
|
| 99 |
+
partial def truncate (env : Environment) (v : Value) : Value :=
|
| 100 |
+
go v {} maxValueDepth
|
| 101 |
+
where
|
| 102 |
+
go (v : Value) (forbiddenTypes : NameSet) (remainingDepth : Nat) :=
|
| 103 |
+
match remainingDepth with
|
| 104 |
+
| 0 => top
|
| 105 |
+
| remainingDepth + 1 =>
|
| 106 |
+
match v with
|
| 107 |
+
| ctor i vs =>
|
| 108 |
+
let typeName := i.getPrefix
|
| 109 |
+
if forbiddenTypes.contains typeName then
|
| 110 |
+
top
|
| 111 |
+
else
|
| 112 |
+
let cont forbiddenTypes' :=
|
| 113 |
+
ctor i (vs.map (go · forbiddenTypes' remainingDepth))
|
| 114 |
+
match env.find? typeName with
|
| 115 |
+
| some (.inductInfo type) =>
|
| 116 |
+
if type.isRec then
|
| 117 |
+
cont <| forbiddenTypes.insert typeName
|
| 118 |
+
else
|
| 119 |
+
cont forbiddenTypes
|
| 120 |
+
| _ => cont forbiddenTypes
|
| 121 |
+
| choice vs =>
|
| 122 |
+
let vs := vs.map (go · forbiddenTypes remainingDepth)
|
| 123 |
+
if vs.elem top then
|
| 124 |
+
top
|
| 125 |
+
else
|
| 126 |
+
choice vs
|
| 127 |
+
| v => v
|
| 128 |
+
|
| 129 |
+
/-- Widening operator that guarantees termination in our abstract interpreter. -/
|
| 130 |
+
def widening (env : Environment) (v1 v2 : Value) : Value :=
|
| 131 |
+
truncate env (merge v1 v2)
|
| 132 |
+
|
| 133 |
+
/--
|
| 134 |
+
Check whether a certain constructor is part of a `Value` by name.
|
| 135 |
+
Note that both `top` and `bot` will always true here. For bot this is
|
| 136 |
+
because we have no information about the `Value` so just to be sure
|
| 137 |
+
we don't claim the absence of a certain constructor.
|
| 138 |
+
-/
|
| 139 |
+
partial def containsCtor : Value → Name → Bool
|
| 140 |
+
| .top .., _ => true
|
| 141 |
+
| .bot .., _ => true -- we don't know so better be safe than sorry
|
| 142 |
+
| .ctor i .., j => i == j
|
| 143 |
+
| .choice vs .., j => vs.any fun v => containsCtor v j
|
| 144 |
+
|
| 145 |
+
/--
|
| 146 |
+
Obtain the arguments of a certain constructor within the `Value`.
|
| 147 |
+
-/
|
| 148 |
+
partial def getCtorArgs : Value → Name → Option (Array Value)
|
| 149 |
+
| .ctor i args .., j => if i == j then some args else none
|
| 150 |
+
| .choice vs .., j => do
|
| 151 |
+
for variant in vs do
|
| 152 |
+
if let .ctor i args .. := variant then
|
| 153 |
+
if i == j then
|
| 154 |
+
return args
|
| 155 |
+
none
|
| 156 |
+
| _, _ => none
|
| 157 |
+
|
| 158 |
+
partial def ofNat (n : Nat) : Value :=
|
| 159 |
+
if n > maxValueDepth then
|
| 160 |
+
goBig n n
|
| 161 |
+
else
|
| 162 |
+
goSmall n
|
| 163 |
+
where
|
| 164 |
+
goBig (orig : Nat) (curr : Nat) : Value :=
|
| 165 |
+
if orig - curr == maxValueDepth then
|
| 166 |
+
.top
|
| 167 |
+
else
|
| 168 |
+
.ctor ``Nat.succ #[goBig orig (curr - 1)]
|
| 169 |
+
goSmall : Nat → Value
|
| 170 |
+
| 0 => .ctor ``Nat.zero #[]
|
| 171 |
+
| n + 1 => .ctor ``Nat.succ #[goSmall n]
|
| 172 |
+
|
| 173 |
+
def ofLCNFLit : LCNF.LitValue → Value
|
| 174 |
+
| .nat n => ofNat n
|
| 175 |
+
-- TODO: Make this work for other numeric literal types.
|
| 176 |
+
| .uint8 _ | .uint16 _ | .uint32 _ | .uint64 _ | .usize _ => .top
|
| 177 |
+
-- TODO: We could make this much more precise but the payoff is questionable
|
| 178 |
+
| .str .. => .top
|
| 179 |
+
|
| 180 |
+
partial def proj : Value → Nat → Value
|
| 181 |
+
| .ctor _ vs , i => vs.getD i bot
|
| 182 |
+
| .choice vs, i => vs.foldl (fun r v => merge r (proj v i)) bot
|
| 183 |
+
| v, _ => v
|
| 184 |
+
|
| 185 |
+
/--
|
| 186 |
+
We say that a `Value` is a literal iff it is only a tree of `Value.ctor`
|
| 187 |
+
nodes.
|
| 188 |
+
-/
|
| 189 |
+
partial def isLiteral : Value → Bool
|
| 190 |
+
| .ctor _ vs => vs.all isLiteral
|
| 191 |
+
| _ => false
|
| 192 |
+
|
| 193 |
+
/-
|
| 194 |
+
TODO: Add support for "Higher Order Literals", that is literals with
|
| 195 |
+
type parameters.
|
| 196 |
+
-/
|
| 197 |
+
/--
|
| 198 |
+
Attempt to turn a `Value` that is representing a literal into a set of
|
| 199 |
+
auxiliary declarations + the final `FVarId` of the declaration that
|
| 200 |
+
contains the actual literal. If it is not a literal return none.
|
| 201 |
+
-/
|
| 202 |
+
partial def getLiteral (v : Value) : CompilerM (Option ((Array CodeDecl) × FVarId)) := do
|
| 203 |
+
if isLiteral v then
|
| 204 |
+
let literal ← go v
|
| 205 |
+
return some literal
|
| 206 |
+
else
|
| 207 |
+
return none
|
| 208 |
+
where
|
| 209 |
+
go : Value → CompilerM ((Array CodeDecl) × FVarId)
|
| 210 |
+
| .ctor `Nat.zero #[] .. => do
|
| 211 |
+
let decl ← mkAuxLetDecl <| .lit <| .nat <| 0
|
| 212 |
+
return (#[.let decl], decl.fvarId)
|
| 213 |
+
| .ctor `Nat.succ #[val] .. => do
|
| 214 |
+
let val := getNatConstant val + 1
|
| 215 |
+
let decl ← mkAuxLetDecl <| .lit <| .nat <| val
|
| 216 |
+
return (#[.let decl], decl.fvarId)
|
| 217 |
+
| .ctor ctorName vs => do
|
| 218 |
+
let some (.ctorInfo ctorInfo) := (← getEnv).find? ctorName | unreachable!
|
| 219 |
+
let fields ← vs.mapM go
|
| 220 |
+
let flatten acc := fun (decls, var) => (acc.fst ++ decls, acc.snd.push <| .fvar var)
|
| 221 |
+
let (decls, args) :=
|
| 222 |
+
fields.foldl (init := (#[], Array.replicate ctorInfo.numParams .erased)) flatten
|
| 223 |
+
let letVal : LetValue := .const ctorName [] args
|
| 224 |
+
let letDecl ← mkAuxLetDecl letVal
|
| 225 |
+
return (decls.push <| .let letDecl, letDecl.fvarId)
|
| 226 |
+
| _ => unreachable!
|
| 227 |
+
|
| 228 |
+
getNatConstant : Value → Nat
|
| 229 |
+
| .ctor `Nat.zero #[] .. => 0
|
| 230 |
+
| .ctor `Nat.succ #[val] .. => getNatConstant val + 1
|
| 231 |
+
| _ => panic! "Not a well formed Nat constant Value"
|
| 232 |
+
|
| 233 |
+
end Value
|
| 234 |
+
|
| 235 |
+
/--
|
| 236 |
+
A map from function names to the `Value` that the abstract interpreter
|
| 237 |
+
produced for them.
|
| 238 |
+
-/
|
| 239 |
+
abbrev FunctionSummaries := PHashMap Name Value
|
| 240 |
+
|
| 241 |
+
private abbrev decLt (a b : Name × Value) : Bool :=
|
| 242 |
+
Name.quickLt a.fst b.fst
|
| 243 |
+
|
| 244 |
+
private abbrev findAtSorted? (entries : Array (Name × Value)) (fid : Name) : Option Value :=
|
| 245 |
+
entries.binSearch (fid, default) decLt |>.map Prod.snd
|
| 246 |
+
|
| 247 |
+
/--
|
| 248 |
+
Storing `FunctionSummaries` for all functions in a `.olean`.
|
| 249 |
+
-/
|
| 250 |
+
builtin_initialize functionSummariesExt : SimplePersistentEnvExtension (Name × Value) FunctionSummaries ←
|
| 251 |
+
registerSimplePersistentEnvExtension {
|
| 252 |
+
addImportedFn := fun _ => {}
|
| 253 |
+
addEntryFn := fun s ⟨e, n⟩ => s.insert e n
|
| 254 |
+
toArrayFn := fun s => s.toArray.qsort decLt
|
| 255 |
+
asyncMode := .sync -- compilation is non-parallel anyway
|
| 256 |
+
replay? := some <| SimplePersistentEnvExtension.replayOfFilter (!·.contains ·.1) (fun s ⟨e, n⟩ => s.insert e n)
|
| 257 |
+
}
|
| 258 |
+
|
| 259 |
+
/--
|
| 260 |
+
Add a `Value` for a function name.
|
| 261 |
+
-/
|
| 262 |
+
def addFunctionSummary (env : Environment) (fid : Name) (v : Value) : Environment :=
|
| 263 |
+
functionSummariesExt.addEntry (env.addExtraName fid) (fid, v)
|
| 264 |
+
|
| 265 |
+
/--
|
| 266 |
+
Obtain the `Value` for a function name if possible.
|
| 267 |
+
-/
|
| 268 |
+
def getFunctionSummary? (env : Environment) (fid : Name) : Option Value :=
|
| 269 |
+
match env.getModuleIdxFor? fid with
|
| 270 |
+
| some modIdx => findAtSorted? (functionSummariesExt.getModuleEntries env modIdx) fid
|
| 271 |
+
| none => functionSummariesExt.getState env |>.find? fid
|
| 272 |
+
|
| 273 |
+
/--
|
| 274 |
+
A map from variable identifiers to the `Value` produced by the abstract
|
| 275 |
+
interpreter for them.
|
| 276 |
+
-/
|
| 277 |
+
abbrev Assignment := Std.HashMap FVarId Value
|
| 278 |
+
|
| 279 |
+
/--
|
| 280 |
+
The context of `InterpM`.
|
| 281 |
+
-/
|
| 282 |
+
structure InterpContext where
|
| 283 |
+
/--
|
| 284 |
+
The list of `Decl`s we are operating on in `InterpM`. This can be
|
| 285 |
+
a single declaration or a mutual block of declarations where their
|
| 286 |
+
analysis might influence each other as we approach the fixpoint.
|
| 287 |
+
-/
|
| 288 |
+
decls : Array Decl
|
| 289 |
+
/--
|
| 290 |
+
The index of the function we are currently operating on in `decls.`
|
| 291 |
+
-/
|
| 292 |
+
currFnIdx : Nat := 0
|
| 293 |
+
|
| 294 |
+
structure InterpState where
|
| 295 |
+
/--
|
| 296 |
+
`Assignment`s of functions in the `InterpContext`.
|
| 297 |
+
-/
|
| 298 |
+
assignments : Array Assignment
|
| 299 |
+
/--
|
| 300 |
+
`Value`s of functions in the `InterpContext` use during computation of
|
| 301 |
+
the fixpoint. Afterwards they are stored into the `Environment`.
|
| 302 |
+
-/
|
| 303 |
+
funVals : PArray Value
|
| 304 |
+
|
| 305 |
+
/--
|
| 306 |
+
The monad which powers the abstract interpreter.
|
| 307 |
+
-/
|
| 308 |
+
abbrev InterpM := ReaderT InterpContext StateRefT InterpState CompilerM
|
| 309 |
+
|
| 310 |
+
/--
|
| 311 |
+
Get the variable `Assignment` of the current function.
|
| 312 |
+
-/
|
| 313 |
+
def getAssignment : InterpM Assignment := do
|
| 314 |
+
return (← get).assignments[(← read).currFnIdx]!
|
| 315 |
+
|
| 316 |
+
/--
|
| 317 |
+
Get the `Value` of a certain function in the current block by index.
|
| 318 |
+
-/
|
| 319 |
+
def getFunVal (funIdx : Nat) : InterpM Value := do
|
| 320 |
+
return (← get).funVals[funIdx]!
|
| 321 |
+
|
| 322 |
+
def findFunVal? (declName : Name) : InterpM (Option Value) := do
|
| 323 |
+
match (← read).decls.findIdx? (·.name == declName) with
|
| 324 |
+
| some idx => return some (← getFunVal idx)
|
| 325 |
+
| none => return none
|
| 326 |
+
|
| 327 |
+
/--
|
| 328 |
+
Run `f` on the variable `Assignment` of the current function.
|
| 329 |
+
-/
|
| 330 |
+
def modifyAssignment (f : Assignment → Assignment) : InterpM Unit := do
|
| 331 |
+
let ctx ← read
|
| 332 |
+
let currFnIdx := ctx.currFnIdx
|
| 333 |
+
modify fun s => { s with assignments := s.assignments.modify currFnIdx f }
|
| 334 |
+
|
| 335 |
+
/--
|
| 336 |
+
Obtain the `Value` associated with `var` from the context of `InterpM`.
|
| 337 |
+
If none is available return `Value.bot`.
|
| 338 |
+
-/
|
| 339 |
+
def findVarValue (var : FVarId) : InterpM Value := do
|
| 340 |
+
let assignment ← getAssignment
|
| 341 |
+
return assignment.getD var .bot
|
| 342 |
+
|
| 343 |
+
/--
|
| 344 |
+
Find the value of `arg` using the logic of `findVarValue`.
|
| 345 |
+
-/
|
| 346 |
+
def findArgValue (arg : Arg) : InterpM Value := do
|
| 347 |
+
match arg with
|
| 348 |
+
| .fvar fvarId => findVarValue fvarId
|
| 349 |
+
| _ => return .top
|
| 350 |
+
|
| 351 |
+
/--
|
| 352 |
+
Update the assignment of `var` by merging the current value with `newVal`.
|
| 353 |
+
-/
|
| 354 |
+
def updateVarAssignment (var : FVarId) (newVal : Value) : InterpM Unit := do
|
| 355 |
+
let val ← findVarValue var
|
| 356 |
+
let updatedVal := .merge val newVal
|
| 357 |
+
modifyAssignment (·.insert var updatedVal)
|
| 358 |
+
|
| 359 |
+
/--
|
| 360 |
+
Set the value of `var` to `bot`.
|
| 361 |
+
-/
|
| 362 |
+
def resetVarAssignment (var : FVarId) : InterpM Unit := do
|
| 363 |
+
modifyAssignment (·.insert var .bot)
|
| 364 |
+
|
| 365 |
+
/--
|
| 366 |
+
Widen the value of the current function by `v`.
|
| 367 |
+
-/
|
| 368 |
+
def updateCurrFnSummary (v : Value) : InterpM Unit := do
|
| 369 |
+
let ctx ← read
|
| 370 |
+
let env ← getEnv
|
| 371 |
+
let currFnIdx := ctx.currFnIdx
|
| 372 |
+
modify fun s => { s with funVals := s.funVals.modify currFnIdx (fun v' => .widening env v v') }
|
| 373 |
+
|
| 374 |
+
/--
|
| 375 |
+
Return true if the assignment of at least one parameter has been updated.
|
| 376 |
+
Furthermore if we see that `params.size != args.size` we know that this is
|
| 377 |
+
a partial application and set the values of the remaining parameters to
|
| 378 |
+
`top` since it is impossible to track what will happen with them from here on.
|
| 379 |
+
-/
|
| 380 |
+
def updateFunDeclParamsAssignment (params : Array Param) (args : Array Arg) : InterpM Bool := do
|
| 381 |
+
let mut ret := false
|
| 382 |
+
for param in params, arg in args do
|
| 383 |
+
let paramVal ← findVarValue param.fvarId
|
| 384 |
+
let argVal ← findArgValue arg
|
| 385 |
+
let newVal := .merge paramVal argVal
|
| 386 |
+
if newVal != paramVal then
|
| 387 |
+
modifyAssignment (·.insert param.fvarId newVal)
|
| 388 |
+
ret := true
|
| 389 |
+
/-
|
| 390 |
+
This is a partial application, we can not know for sure what remaining
|
| 391 |
+
arguments the local function is getting passed without a much more
|
| 392 |
+
sophisticated analysis. Hence we will set all of the non applied ones
|
| 393 |
+
to top.
|
| 394 |
+
-/
|
| 395 |
+
if params.size != args.size then
|
| 396 |
+
for param in params[args.size...*] do
|
| 397 |
+
ret := (← findVarValue param.fvarId) == .bot
|
| 398 |
+
updateVarAssignment param.fvarId .top
|
| 399 |
+
return ret
|
| 400 |
+
|
| 401 |
+
def updateFunDeclParamsTop (params : Array Param) : InterpM Bool := do
|
| 402 |
+
let mut ret := false
|
| 403 |
+
for param in params do
|
| 404 |
+
let paramVal ← findVarValue param.fvarId
|
| 405 |
+
let newVal := .top
|
| 406 |
+
if newVal != paramVal then
|
| 407 |
+
modifyAssignment (·.insert param.fvarId newVal)
|
| 408 |
+
ret := true
|
| 409 |
+
return ret
|
| 410 |
+
|
| 411 |
+
private partial def resetNestedFunDeclParams : Code → InterpM Unit
|
| 412 |
+
| .let _ k => resetNestedFunDeclParams k
|
| 413 |
+
| .jp decl k | .fun decl k => do
|
| 414 |
+
decl.params.forM (resetVarAssignment ·.fvarId)
|
| 415 |
+
/- We don't need to reset the parameters of decls
|
| 416 |
+
nested in this one since they will be reset if this decl is used. -/
|
| 417 |
+
resetNestedFunDeclParams k
|
| 418 |
+
| .cases cs =>
|
| 419 |
+
cs.alts.forM (resetNestedFunDeclParams ·.getCode)
|
| 420 |
+
| .return .. | .unreach .. | .jmp .. => return ()
|
| 421 |
+
|
| 422 |
+
/--
|
| 423 |
+
The actual abstract interpreter on a block of `Code`.
|
| 424 |
+
-/
|
| 425 |
+
partial def interpCode : Code → InterpM Unit
|
| 426 |
+
| .let decl k => do
|
| 427 |
+
let val ← interpLetValue decl.value
|
| 428 |
+
updateVarAssignment decl.fvarId val
|
| 429 |
+
if let .fvar fvarId args := decl.value then
|
| 430 |
+
if let some funDecl ← findFunDecl? fvarId then
|
| 431 |
+
-- TODO: unclear how much we should reset in the case of partial applications
|
| 432 |
+
interpFunCall funDecl args
|
| 433 |
+
interpCode k
|
| 434 |
+
| .jp decl k | .fun decl k => do
|
| 435 |
+
interpCode decl.value
|
| 436 |
+
interpCode k
|
| 437 |
+
| .jmp fn args => do
|
| 438 |
+
let jp ← getFunDecl fn
|
| 439 |
+
args.forM handleFunArg
|
| 440 |
+
interpFunCall jp args
|
| 441 |
+
| .cases cs => do
|
| 442 |
+
let discrVal ← findVarValue cs.discr
|
| 443 |
+
for alt in cs.alts do
|
| 444 |
+
match alt with
|
| 445 |
+
| .alt ctor params body =>
|
| 446 |
+
if let some argValues := discrVal.getCtorArgs ctor then
|
| 447 |
+
params.zip argValues |>.forM (fun (param, val) => updateVarAssignment param.fvarId val)
|
| 448 |
+
else
|
| 449 |
+
params.forM (updateVarAssignment ·.fvarId .top)
|
| 450 |
+
interpCode body
|
| 451 |
+
| .default body => interpCode body
|
| 452 |
+
| .return var => do
|
| 453 |
+
handleFunVar var
|
| 454 |
+
let val ← findVarValue var
|
| 455 |
+
updateCurrFnSummary val
|
| 456 |
+
| .unreach .. => return ()
|
| 457 |
+
where
|
| 458 |
+
/--
|
| 459 |
+
The abstract interpreter on a `LetValue`.
|
| 460 |
+
-/
|
| 461 |
+
interpLetValue (letVal : LetValue) : InterpM Value := do
|
| 462 |
+
match letVal with
|
| 463 |
+
| .lit val => return .ofLCNFLit val
|
| 464 |
+
| .proj _ idx struct => return (← findVarValue struct).proj idx
|
| 465 |
+
| .const declName _ args =>
|
| 466 |
+
let env ← getEnv
|
| 467 |
+
args.forM handleFunArg
|
| 468 |
+
match (← getDecl? declName) with
|
| 469 |
+
| some decl =>
|
| 470 |
+
if decl.getArity == args.size then
|
| 471 |
+
match getFunctionSummary? env declName with
|
| 472 |
+
| some v => return v
|
| 473 |
+
| none => return (← findFunVal? declName).getD .top
|
| 474 |
+
else
|
| 475 |
+
return .top
|
| 476 |
+
| none =>
|
| 477 |
+
let some (.ctorInfo info) := env.find? declName | return .top
|
| 478 |
+
let args := args[info.numParams...*].toArray
|
| 479 |
+
if info.numFields == args.size then
|
| 480 |
+
return .ctor declName (← args.mapM findArgValue)
|
| 481 |
+
else
|
| 482 |
+
return .top
|
| 483 |
+
| .fvar _ args =>
|
| 484 |
+
args.forM handleFunArg
|
| 485 |
+
/-
|
| 486 |
+
Since free variables in `LetValue`s cannot be of the form
|
| 487 |
+
`let x := y` after a simplifier pass we know they are in fact a
|
| 488 |
+
partially applied function, we cannot know anything about the result
|
| 489 |
+
of a partially applied function.
|
| 490 |
+
-/
|
| 491 |
+
return .top
|
| 492 |
+
| .erased => return .top
|
| 493 |
+
|
| 494 |
+
handleFunArg (arg : Arg) : InterpM Unit := do
|
| 495 |
+
if let .fvar fvarId := arg then
|
| 496 |
+
handleFunVar fvarId
|
| 497 |
+
|
| 498 |
+
/--
|
| 499 |
+
If we see a function being passed as an argument to a higher order
|
| 500 |
+
function we cannot know what arguments it will be passed further
|
| 501 |
+
down the line. Hence we set all of its arguments to `top` since anything
|
| 502 |
+
is possible.
|
| 503 |
+
-/
|
| 504 |
+
handleFunVar (var : FVarId) : InterpM Unit := do
|
| 505 |
+
if let some funDecl ← findFunDecl? var then
|
| 506 |
+
let updated ← updateFunDeclParamsTop funDecl.params
|
| 507 |
+
if updated then
|
| 508 |
+
/- We must reset the value of nested function declaration
|
| 509 |
+
parameters since they depend on `args` values. -/
|
| 510 |
+
resetNestedFunDeclParams funDecl.value
|
| 511 |
+
interpCode funDecl.value
|
| 512 |
+
|
| 513 |
+
interpFunCall (funDecl : FunDecl) (args : Array Arg) : InterpM Unit := do
|
| 514 |
+
let updated ← updateFunDeclParamsAssignment funDecl.params args
|
| 515 |
+
if updated then
|
| 516 |
+
/- We must reset the value of nested function declaration
|
| 517 |
+
parameters since they depend on `args` values. -/
|
| 518 |
+
resetNestedFunDeclParams funDecl.value
|
| 519 |
+
interpCode funDecl.value
|
| 520 |
+
|
| 521 |
+
|
| 522 |
+
/--
|
| 523 |
+
Rerun the abstract interpreter on all declarations except of the unsafe
|
| 524 |
+
ones. Return whether any `Value` got updated in the process.
|
| 525 |
+
-/
|
| 526 |
+
def inferStep : InterpM Bool := do
|
| 527 |
+
let ctx ← read
|
| 528 |
+
for h : idx in [0:ctx.decls.size] do
|
| 529 |
+
let decl := ctx.decls[idx]
|
| 530 |
+
if !decl.safe then
|
| 531 |
+
continue
|
| 532 |
+
|
| 533 |
+
let currentVal ← getFunVal idx
|
| 534 |
+
withReader (fun ctx => { ctx with currFnIdx := idx }) do
|
| 535 |
+
decl.params.forM fun p => updateVarAssignment p.fvarId .top
|
| 536 |
+
match decl.value with
|
| 537 |
+
| .code code .. => interpCode code
|
| 538 |
+
| .extern .. => updateCurrFnSummary .top
|
| 539 |
+
let newVal ← getFunVal idx
|
| 540 |
+
if currentVal != newVal then
|
| 541 |
+
return true
|
| 542 |
+
return false
|
| 543 |
+
|
| 544 |
+
/--
|
| 545 |
+
Run `inferStep` until it reaches a fix point.
|
| 546 |
+
-/
|
| 547 |
+
partial def inferMain : InterpM Unit := do
|
| 548 |
+
let ctx ← read
|
| 549 |
+
modify fun s => { s with assignments := ctx.decls.map fun _ => {} }
|
| 550 |
+
let modified ← inferStep
|
| 551 |
+
if modified then
|
| 552 |
+
inferMain
|
| 553 |
+
else
|
| 554 |
+
return ()
|
| 555 |
+
|
| 556 |
+
/--
|
| 557 |
+
Use the information produced by the abstract interpreter to:
|
| 558 |
+
- Eliminate branches that we know cannot be hit
|
| 559 |
+
- Eliminate values that we know have to be constants.
|
| 560 |
+
-/
|
| 561 |
+
partial def elimDead (assignment : Assignment) (decl : Decl) : CompilerM Decl := do
|
| 562 |
+
trace[Compiler.elimDeadBranches] s!"Eliminating {decl.name} with {repr (← assignment.toArray |>.mapM (fun (name, val) => do return (toString (← getBinderName name), val)))}"
|
| 563 |
+
return { decl with value := (← decl.value.mapCodeM go) }
|
| 564 |
+
where
|
| 565 |
+
go (code : Code) : CompilerM Code := do
|
| 566 |
+
match code with
|
| 567 |
+
| .let decl k =>
|
| 568 |
+
return code.updateLet! decl (← go k)
|
| 569 |
+
| .jp decl k | .fun decl k =>
|
| 570 |
+
return code.updateFun! (← decl.updateValue (← go decl.value)) (← go k)
|
| 571 |
+
| .cases cs =>
|
| 572 |
+
let discrVal := assignment.getD cs.discr .bot
|
| 573 |
+
let processAlt typ alt := do
|
| 574 |
+
match alt with
|
| 575 |
+
| .alt ctor args body =>
|
| 576 |
+
if discrVal.containsCtor ctor then
|
| 577 |
+
let filter param := do
|
| 578 |
+
if let some val := assignment[param.fvarId]? then
|
| 579 |
+
if let some literal ← val.getLiteral then
|
| 580 |
+
return some (param, literal)
|
| 581 |
+
return none
|
| 582 |
+
let constantInfos ← args.filterMapM filter
|
| 583 |
+
if constantInfos.size != 0 then
|
| 584 |
+
let folder := fun (body, subst) (param, decls, var) => do
|
| 585 |
+
return (attachCodeDecls decls body, subst.insert param.fvarId (.fvar var))
|
| 586 |
+
let (body, subst) ← constantInfos.foldlM (init := (← go body, {})) folder
|
| 587 |
+
let body ← replaceFVars body subst false
|
| 588 |
+
return alt.updateCode body
|
| 589 |
+
else
|
| 590 |
+
return alt.updateCode (← go body)
|
| 591 |
+
else
|
| 592 |
+
trace[Compiler.elimDeadBranches] s!"Threw away cases {← getBinderName cs.discr} branch {ctor}"
|
| 593 |
+
eraseCode alt.getCode
|
| 594 |
+
return alt.updateCode <| .unreach typ
|
| 595 |
+
| .default body => return alt.updateCode (← go body)
|
| 596 |
+
return code.updateCases! cs.resultType cs.discr (← cs.alts.mapM <| processAlt cs.resultType)
|
| 597 |
+
| .jmp .. | .return .. | .unreach .. => return code
|
| 598 |
+
|
| 599 |
+
end UnreachableBranches
|
| 600 |
+
|
| 601 |
+
open UnreachableBranches in
|
| 602 |
+
def Decl.elimDeadBranches (decls : Array Decl) : CompilerM (Array Decl) := do
|
| 603 |
+
let mut assignments := decls.map fun _ => {}
|
| 604 |
+
let initialVal i :=
|
| 605 |
+
/-
|
| 606 |
+
Non terminating functions are marked as unsafe, we don't want to run
|
| 607 |
+
any analysis on them since we cannot be sure they will ever return
|
| 608 |
+
the constructor that we inferred for them. For more information
|
| 609 |
+
refer to the docstring of `Decl.safe`.
|
| 610 |
+
-/
|
| 611 |
+
if decls[i]!.safe then .bot else .top
|
| 612 |
+
let mut funVals := decls.size.fold (init := .empty) fun i _ p => p.push (initialVal i)
|
| 613 |
+
let ctx := { decls }
|
| 614 |
+
let mut state := { assignments, funVals }
|
| 615 |
+
(_, state) ← inferMain |>.run ctx |>.run state
|
| 616 |
+
funVals := state.funVals
|
| 617 |
+
assignments := state.assignments
|
| 618 |
+
modifyEnv fun e =>
|
| 619 |
+
decls.size.fold (init := e) fun i _ env =>
|
| 620 |
+
addFunctionSummary env decls[i].name funVals[i]!
|
| 621 |
+
|
| 622 |
+
decls.mapIdxM fun i decl => if decl.safe then elimDead assignments[i]! decl else return decl
|
| 623 |
+
|
| 624 |
+
def elimDeadBranches : Pass :=
|
| 625 |
+
{ name := `elimDeadBranches, run := Decl.elimDeadBranches, phase := .mono }
|
| 626 |
+
|
| 627 |
+
builtin_initialize
|
| 628 |
+
registerTraceClass `Compiler.elimDeadBranches (inherited := true)
|
| 629 |
+
|
| 630 |
+
end Lean.Compiler.LCNF
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/ExtractClosed.lean
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2025 Lean FRO, LLC. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Cameron Zwarich
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Compiler.ClosedTermCache
|
| 8 |
+
import Lean.Compiler.NeverExtractAttr
|
| 9 |
+
import Lean.Compiler.LCNF.Basic
|
| 10 |
+
import Lean.Compiler.LCNF.InferType
|
| 11 |
+
import Lean.Compiler.LCNF.Internalize
|
| 12 |
+
import Lean.Compiler.LCNF.MonoTypes
|
| 13 |
+
import Lean.Compiler.LCNF.PassManager
|
| 14 |
+
import Lean.Compiler.LCNF.ToExpr
|
| 15 |
+
|
| 16 |
+
namespace Lean.Compiler.LCNF
|
| 17 |
+
namespace ExtractClosed
|
| 18 |
+
|
| 19 |
+
abbrev ExtractM := StateRefT (Array CodeDecl) CompilerM
|
| 20 |
+
|
| 21 |
+
mutual
|
| 22 |
+
|
| 23 |
+
partial def extractLetValue (v : LetValue) : ExtractM Unit := do
|
| 24 |
+
match v with
|
| 25 |
+
| .const _ _ args => args.forM extractArg
|
| 26 |
+
| .fvar fnVar args =>
|
| 27 |
+
extractFVar fnVar
|
| 28 |
+
args.forM extractArg
|
| 29 |
+
| .proj _ _ baseVar => extractFVar baseVar
|
| 30 |
+
| .lit _ | .erased => return ()
|
| 31 |
+
|
| 32 |
+
partial def extractArg (arg : Arg) : ExtractM Unit := do
|
| 33 |
+
match arg with
|
| 34 |
+
| .fvar fvarId => extractFVar fvarId
|
| 35 |
+
| .type _ | .erased => return ()
|
| 36 |
+
|
| 37 |
+
partial def extractFVar (fvarId : FVarId) : ExtractM Unit := do
|
| 38 |
+
if let some letDecl ← findLetDecl? fvarId then
|
| 39 |
+
modify fun decls => decls.push (.let letDecl)
|
| 40 |
+
extractLetValue letDecl.value
|
| 41 |
+
|
| 42 |
+
end
|
| 43 |
+
|
| 44 |
+
def isIrrelevantArg (arg : Arg) : Bool :=
|
| 45 |
+
match arg with
|
| 46 |
+
| .erased | .type _ => true
|
| 47 |
+
| .fvar _ => false
|
| 48 |
+
|
| 49 |
+
structure Context where
|
| 50 |
+
baseName : Name
|
| 51 |
+
sccDecls : Array Decl
|
| 52 |
+
|
| 53 |
+
structure State where
|
| 54 |
+
decls : Array Decl := {}
|
| 55 |
+
|
| 56 |
+
abbrev M := ReaderT Context $ StateRefT State CompilerM
|
| 57 |
+
|
| 58 |
+
mutual
|
| 59 |
+
|
| 60 |
+
partial def shouldExtractLetValue (isRoot : Bool) (v : LetValue) : M Bool := do
|
| 61 |
+
match v with
|
| 62 |
+
| .lit (.str _) => return true
|
| 63 |
+
| .lit (.nat v) =>
|
| 64 |
+
-- The old compiler's implementation used the runtime's `is_scalar` function, which
|
| 65 |
+
-- introduces a dependency on the architecture used by the compiler.
|
| 66 |
+
return !isRoot || v >= Nat.pow 2 63
|
| 67 |
+
| .lit _ | .erased => return !isRoot
|
| 68 |
+
| .const name _ args =>
|
| 69 |
+
if (← read).sccDecls.any (·.name == name) then
|
| 70 |
+
return false
|
| 71 |
+
if hasNeverExtractAttribute (← getEnv) name then
|
| 72 |
+
return false
|
| 73 |
+
if isRoot then
|
| 74 |
+
if let some constInfo := (← getEnv).find? name then
|
| 75 |
+
let shouldExtract := match constInfo with
|
| 76 |
+
| .defnInfo val => val.type.isForall
|
| 77 |
+
| .ctorInfo _ => !(args.all isIrrelevantArg)
|
| 78 |
+
| _ => true
|
| 79 |
+
if !shouldExtract then
|
| 80 |
+
return false
|
| 81 |
+
args.allM shouldExtractArg
|
| 82 |
+
| .fvar fnVar args => return (← shouldExtractFVar fnVar) && (← args.allM shouldExtractArg)
|
| 83 |
+
| .proj _ _ baseVar => shouldExtractFVar baseVar
|
| 84 |
+
|
| 85 |
+
partial def shouldExtractArg (arg : Arg) : M Bool := do
|
| 86 |
+
match arg with
|
| 87 |
+
| .fvar fvarId => shouldExtractFVar fvarId
|
| 88 |
+
| .type _ | .erased => return true
|
| 89 |
+
|
| 90 |
+
partial def shouldExtractFVar (fvarId : FVarId) : M Bool := do
|
| 91 |
+
if let some letDecl ← findLetDecl? fvarId then
|
| 92 |
+
shouldExtractLetValue false letDecl.value
|
| 93 |
+
else
|
| 94 |
+
return false
|
| 95 |
+
|
| 96 |
+
end
|
| 97 |
+
|
| 98 |
+
mutual
|
| 99 |
+
|
| 100 |
+
partial def visitCode (code : Code) : M Code := do
|
| 101 |
+
match code with
|
| 102 |
+
| .let decl k =>
|
| 103 |
+
if (← shouldExtractLetValue true decl.value) then
|
| 104 |
+
let ⟨_, decls⟩ ← extractLetValue decl.value |>.run {}
|
| 105 |
+
let decls := decls.reverse.push (.let decl)
|
| 106 |
+
let decls ← decls.mapM Internalize.internalizeCodeDecl |>.run' {}
|
| 107 |
+
let closedCode := attachCodeDecls decls (.return decls.back!.fvarId)
|
| 108 |
+
let closedExpr := closedCode.toExpr
|
| 109 |
+
let env ← getEnv
|
| 110 |
+
let name ← if let some closedTermName := getClosedTermName? env closedExpr then
|
| 111 |
+
eraseCode closedCode
|
| 112 |
+
pure closedTermName
|
| 113 |
+
else
|
| 114 |
+
let name := (← read).baseName ++ (`_closed).appendIndexAfter (← get).decls.size
|
| 115 |
+
cacheClosedTermName env closedExpr name |> setEnv
|
| 116 |
+
let decl := { name, levelParams := [], type := decl.type, params := #[],
|
| 117 |
+
value := .code closedCode, inlineAttr? := some .noinline }
|
| 118 |
+
decl.saveMono
|
| 119 |
+
modify fun s => { s with decls := s.decls.push decl }
|
| 120 |
+
pure name
|
| 121 |
+
let decl ← decl.updateValue (.const name [] #[])
|
| 122 |
+
return code.updateLet! decl (← visitCode k)
|
| 123 |
+
else
|
| 124 |
+
return code.updateLet! decl (← visitCode k)
|
| 125 |
+
| .fun decl k =>
|
| 126 |
+
let decl ← decl.updateValue (← visitCode decl.value)
|
| 127 |
+
return code.updateFun! decl (← visitCode k)
|
| 128 |
+
| .jp decl k =>
|
| 129 |
+
let decl ← decl.updateValue (← visitCode decl.value)
|
| 130 |
+
return code.updateFun! decl (← visitCode k)
|
| 131 |
+
| .cases cases =>
|
| 132 |
+
let alts ← cases.alts.mapM (fun alt => do return alt.updateCode (← visitCode alt.getCode))
|
| 133 |
+
return code.updateAlts! alts
|
| 134 |
+
| .jmp .. | .return _ | .unreach .. => return code
|
| 135 |
+
|
| 136 |
+
end
|
| 137 |
+
|
| 138 |
+
def visitDecl (decl : Decl) : M Decl := do
|
| 139 |
+
let value ← decl.value.mapCodeM visitCode
|
| 140 |
+
return { decl with value }
|
| 141 |
+
|
| 142 |
+
end ExtractClosed
|
| 143 |
+
|
| 144 |
+
partial def Decl.extractClosed (decl : Decl) (sccDecls : Array Decl) : CompilerM (Array Decl) := do
|
| 145 |
+
let ⟨decl, s⟩ ← ExtractClosed.visitDecl decl |>.run { baseName := decl.name, sccDecls } |>.run {}
|
| 146 |
+
return s.decls.push decl
|
| 147 |
+
|
| 148 |
+
def extractClosed : Pass where
|
| 149 |
+
phase := .mono
|
| 150 |
+
name := `extractClosed
|
| 151 |
+
run := fun decls => do
|
| 152 |
+
-- Reuse the option from the old compiler for now.
|
| 153 |
+
if (← getOptions).getBool `compiler.extract_closed true then
|
| 154 |
+
decls.foldlM (init := #[]) fun newDecls decl =>
|
| 155 |
+
return newDecls ++ (← decl.extractClosed decls)
|
| 156 |
+
else
|
| 157 |
+
return decls
|
| 158 |
+
|
| 159 |
+
builtin_initialize registerTraceClass `Compiler.extractClosed (inherited := true)
|
| 160 |
+
|
| 161 |
+
end Lean.Compiler.LCNF
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/FVarUtil.lean
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2022 Henrik Böving. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Henrik Böving
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Expr
|
| 8 |
+
import Lean.Compiler.LCNF.Basic
|
| 9 |
+
import Lean.Compiler.LCNF.CompilerM
|
| 10 |
+
|
| 11 |
+
namespace Lean.Compiler.LCNF
|
| 12 |
+
|
| 13 |
+
class TraverseFVar (α : Type) where
|
| 14 |
+
mapFVarM {m : Type → Type} [MonadLiftT CompilerM m] [Monad m] (f : FVarId → m FVarId) (val : α) : m α
|
| 15 |
+
forFVarM {m : Type → Type} [Monad m] (f : FVarId → m Unit) (val : α) : m Unit
|
| 16 |
+
|
| 17 |
+
export TraverseFVar (mapFVarM forFVarM)
|
| 18 |
+
|
| 19 |
+
partial def Expr.mapFVarM [MonadLiftT CompilerM m] [Monad m] (f : FVarId → m FVarId) (e : Expr) : m Expr := do
|
| 20 |
+
match e with
|
| 21 |
+
| .app fn arg => return e.updateApp! (← mapFVarM f fn) (← mapFVarM f arg)
|
| 22 |
+
| .fvar fvarId => return e.updateFVar! (← f fvarId)
|
| 23 |
+
| .lam _ ty body _ => return e.updateLambdaE! (← mapFVarM f ty) (← mapFVarM f body)
|
| 24 |
+
| .forallE _ ty body _ => return e.updateForallE! (← mapFVarM f ty) (← mapFVarM f body)
|
| 25 |
+
| .bvar .. | .sort .. => return e
|
| 26 |
+
| .mdata .. | .const .. | .lit .. => return e
|
| 27 |
+
| .letE .. | .proj .. | .mvar .. => unreachable! -- LCNF types do not have this kind of expr
|
| 28 |
+
|
| 29 |
+
partial def Expr.forFVarM [Monad m] (f : FVarId → m Unit) (e : Expr) : m Unit := do
|
| 30 |
+
match e with
|
| 31 |
+
| .app fn arg =>
|
| 32 |
+
forFVarM f fn
|
| 33 |
+
forFVarM f arg
|
| 34 |
+
| .fvar fvarId => f fvarId
|
| 35 |
+
| .lam _ ty body .. =>
|
| 36 |
+
forFVarM f ty
|
| 37 |
+
forFVarM f body
|
| 38 |
+
| .forallE _ ty body .. =>
|
| 39 |
+
forFVarM f ty
|
| 40 |
+
forFVarM f body
|
| 41 |
+
| .bvar .. | .sort .. => return
|
| 42 |
+
| .mdata .. | .const .. | .lit .. => return
|
| 43 |
+
| .mvar .. | .letE .. | .proj .. => unreachable! -- LCNF types do not have this kind of expr
|
| 44 |
+
|
| 45 |
+
instance : TraverseFVar Expr where
|
| 46 |
+
mapFVarM := Expr.mapFVarM
|
| 47 |
+
forFVarM := Expr.forFVarM
|
| 48 |
+
|
| 49 |
+
def Arg.mapFVarM [MonadLiftT CompilerM m] [Monad m] (f : FVarId → m FVarId) (arg : Arg) : m Arg := do
|
| 50 |
+
match arg with
|
| 51 |
+
| .erased => return .erased
|
| 52 |
+
| .type e => return arg.updateType! (← TraverseFVar.mapFVarM f e)
|
| 53 |
+
| .fvar fvarId => return arg.updateFVar! (← f fvarId)
|
| 54 |
+
|
| 55 |
+
def Arg.forFVarM [Monad m] (f : FVarId → m Unit) (arg : Arg) : m Unit := do
|
| 56 |
+
match arg with
|
| 57 |
+
| .erased => return ()
|
| 58 |
+
| .type e => TraverseFVar.forFVarM f e
|
| 59 |
+
| .fvar fvarId => f fvarId
|
| 60 |
+
|
| 61 |
+
instance : TraverseFVar Arg where
|
| 62 |
+
mapFVarM := Arg.mapFVarM
|
| 63 |
+
forFVarM := Arg.forFVarM
|
| 64 |
+
|
| 65 |
+
def LetValue.mapFVarM [MonadLiftT CompilerM m] [Monad m] (f : FVarId → m FVarId) (e : LetValue) : m LetValue := do
|
| 66 |
+
match e with
|
| 67 |
+
| .lit .. | .erased => return e
|
| 68 |
+
| .proj _ _ fvarId => return e.updateProj! (← f fvarId)
|
| 69 |
+
| .const _ _ args => return e.updateArgs! (← args.mapM (TraverseFVar.mapFVarM f))
|
| 70 |
+
| .fvar fvarId args => return e.updateFVar! (← f fvarId) (← args.mapM (TraverseFVar.mapFVarM f))
|
| 71 |
+
|
| 72 |
+
def LetValue.forFVarM [Monad m] (f : FVarId → m Unit) (e : LetValue) : m Unit := do
|
| 73 |
+
match e with
|
| 74 |
+
| .lit .. | .erased => return ()
|
| 75 |
+
| .proj _ _ fvarId => f fvarId
|
| 76 |
+
| .const _ _ args => args.forM (TraverseFVar.forFVarM f)
|
| 77 |
+
| .fvar fvarId args => f fvarId; args.forM (TraverseFVar.forFVarM f)
|
| 78 |
+
|
| 79 |
+
instance : TraverseFVar LetValue where
|
| 80 |
+
mapFVarM := LetValue.mapFVarM
|
| 81 |
+
forFVarM := LetValue.forFVarM
|
| 82 |
+
|
| 83 |
+
partial def LetDecl.mapFVarM [MonadLiftT CompilerM m] [Monad m] (f : FVarId → m FVarId) (decl : LetDecl) : m LetDecl := do
|
| 84 |
+
decl.update (← Expr.mapFVarM f decl.type) (← LetValue.mapFVarM f decl.value)
|
| 85 |
+
|
| 86 |
+
partial def LetDecl.forFVarM [Monad m] (f : FVarId → m Unit) (decl : LetDecl) : m Unit := do
|
| 87 |
+
Expr.forFVarM f decl.type
|
| 88 |
+
LetValue.forFVarM f decl.value
|
| 89 |
+
|
| 90 |
+
instance : TraverseFVar LetDecl where
|
| 91 |
+
mapFVarM := LetDecl.mapFVarM
|
| 92 |
+
forFVarM := LetDecl.forFVarM
|
| 93 |
+
|
| 94 |
+
partial def Param.mapFVarM [MonadLiftT CompilerM m] [Monad m] (f : FVarId → m FVarId) (param : Param) : m Param := do
|
| 95 |
+
param.update (← Expr.mapFVarM f param.type)
|
| 96 |
+
|
| 97 |
+
partial def Param.forFVarM [Monad m] (f : FVarId → m Unit) (param : Param) : m Unit := do
|
| 98 |
+
Expr.forFVarM f param.type
|
| 99 |
+
|
| 100 |
+
instance : TraverseFVar Param where
|
| 101 |
+
mapFVarM := Param.mapFVarM
|
| 102 |
+
forFVarM := Param.forFVarM
|
| 103 |
+
|
| 104 |
+
partial def Code.mapFVarM [MonadLiftT CompilerM m] [Monad m] (f : FVarId → m FVarId) (c : Code) : m Code := do
|
| 105 |
+
match c with
|
| 106 |
+
| .let decl k =>
|
| 107 |
+
let decl ← LetDecl.mapFVarM f decl
|
| 108 |
+
return Code.updateLet! c decl (← mapFVarM f k)
|
| 109 |
+
| .fun decl k =>
|
| 110 |
+
let params ← decl.params.mapM (Param.mapFVarM f)
|
| 111 |
+
let decl ← decl.update (← Expr.mapFVarM f decl.type) params (← mapFVarM f decl.value)
|
| 112 |
+
return Code.updateFun! c decl (← mapFVarM f k)
|
| 113 |
+
| .jp decl k =>
|
| 114 |
+
let params ← decl.params.mapM (Param.mapFVarM f)
|
| 115 |
+
let decl ← decl.update (← Expr.mapFVarM f decl.type) params (← mapFVarM f decl.value)
|
| 116 |
+
return Code.updateFun! c decl (← mapFVarM f k)
|
| 117 |
+
| .cases cs =>
|
| 118 |
+
return Code.updateCases! c (← Expr.mapFVarM f cs.resultType) (← f cs.discr) (← cs.alts.mapM (·.mapCodeM (mapFVarM f)))
|
| 119 |
+
| .jmp fn args =>
|
| 120 |
+
return Code.updateJmp! c (← f fn) (← args.mapM (Arg.mapFVarM f))
|
| 121 |
+
| .return var =>
|
| 122 |
+
return Code.updateReturn! c (← f var)
|
| 123 |
+
| .unreach typ =>
|
| 124 |
+
return Code.updateUnreach! c (← Expr.mapFVarM f typ)
|
| 125 |
+
|
| 126 |
+
partial def Code.forFVarM [Monad m] (f : FVarId → m Unit) (c : Code) : m Unit := do
|
| 127 |
+
match c with
|
| 128 |
+
| .let decl k =>
|
| 129 |
+
LetDecl.forFVarM f decl
|
| 130 |
+
forFVarM f k
|
| 131 |
+
| .fun decl k =>
|
| 132 |
+
decl.params.forM (Param.forFVarM f)
|
| 133 |
+
Expr.forFVarM f decl.type
|
| 134 |
+
forFVarM f decl.value
|
| 135 |
+
forFVarM f k
|
| 136 |
+
| .jp decl k =>
|
| 137 |
+
decl.params.forM (Param.forFVarM f)
|
| 138 |
+
Expr.forFVarM f decl.type
|
| 139 |
+
forFVarM f decl.value
|
| 140 |
+
forFVarM f k
|
| 141 |
+
| .cases cs =>
|
| 142 |
+
Expr.forFVarM f cs.resultType
|
| 143 |
+
f cs.discr
|
| 144 |
+
cs.alts.forM (·.forCodeM (forFVarM f))
|
| 145 |
+
| .jmp fn args =>
|
| 146 |
+
f fn
|
| 147 |
+
args.forM (Arg.forFVarM f)
|
| 148 |
+
| .return var => f var
|
| 149 |
+
| .unreach typ =>
|
| 150 |
+
Expr.forFVarM f typ
|
| 151 |
+
|
| 152 |
+
instance : TraverseFVar Code where
|
| 153 |
+
mapFVarM := Code.mapFVarM
|
| 154 |
+
forFVarM := Code.forFVarM
|
| 155 |
+
|
| 156 |
+
def FunDecl.mapFVarM [MonadLiftT CompilerM m] [Monad m] (f : FVarId → m FVarId) (decl : FunDecl) : m FunDecl := do
|
| 157 |
+
let params ← decl.params.mapM (Param.mapFVarM f)
|
| 158 |
+
decl.update (← Expr.mapFVarM f decl.type) params (← Code.mapFVarM f decl.value)
|
| 159 |
+
|
| 160 |
+
def FunDecl.forFVarM [Monad m] (f : FVarId → m Unit) (decl : FunDecl) : m Unit := do
|
| 161 |
+
decl.params.forM (Param.forFVarM f)
|
| 162 |
+
Expr.forFVarM f decl.type
|
| 163 |
+
Code.forFVarM f decl.value
|
| 164 |
+
|
| 165 |
+
instance : TraverseFVar FunDecl where
|
| 166 |
+
mapFVarM := FunDecl.mapFVarM
|
| 167 |
+
forFVarM := FunDecl.forFVarM
|
| 168 |
+
|
| 169 |
+
instance : TraverseFVar CodeDecl where
|
| 170 |
+
mapFVarM f decl := do
|
| 171 |
+
match decl with
|
| 172 |
+
| .fun decl => return .fun (← mapFVarM f decl)
|
| 173 |
+
| .jp decl => return .jp (← mapFVarM f decl)
|
| 174 |
+
| .let decl => return .let (← mapFVarM f decl)
|
| 175 |
+
forFVarM f decl :=
|
| 176 |
+
match decl with
|
| 177 |
+
| .fun decl => forFVarM f decl
|
| 178 |
+
| .jp decl => forFVarM f decl
|
| 179 |
+
| .let decl => forFVarM f decl
|
| 180 |
+
|
| 181 |
+
instance : TraverseFVar Alt where
|
| 182 |
+
mapFVarM f alt := do
|
| 183 |
+
match alt with
|
| 184 |
+
| .alt ctor params c =>
|
| 185 |
+
let params ← params.mapM (Param.mapFVarM f)
|
| 186 |
+
return .alt ctor params (← Code.mapFVarM f c)
|
| 187 |
+
| .default c => return .default (← Code.mapFVarM f c)
|
| 188 |
+
forFVarM f alt := do
|
| 189 |
+
match alt with
|
| 190 |
+
| .alt _ params c =>
|
| 191 |
+
params.forM (Param.forFVarM f)
|
| 192 |
+
Code.forFVarM f c
|
| 193 |
+
| .default c => Code.forFVarM f c
|
| 194 |
+
|
| 195 |
+
def anyFVarM [Monad m] [TraverseFVar α] (f : FVarId → m Bool) (x : α) : m Bool := do
|
| 196 |
+
return (← TraverseFVar.forFVarM go x |>.run) matches none
|
| 197 |
+
where
|
| 198 |
+
go (fvar : FVarId) : OptionT m Unit := do
|
| 199 |
+
if (← f fvar) then failure
|
| 200 |
+
|
| 201 |
+
def allFVarM [Monad m] [TraverseFVar α] (f : FVarId → m Bool) (x : α) : m Bool := do
|
| 202 |
+
return (← TraverseFVar.forFVarM go x |>.run) matches .some ..
|
| 203 |
+
where
|
| 204 |
+
go (fvar : FVarId) : OptionT m Unit := do
|
| 205 |
+
if !(← f fvar) then failure
|
| 206 |
+
|
| 207 |
+
def anyFVar [TraverseFVar α] (f : FVarId → Bool) (x : α) : Bool :=
|
| 208 |
+
Id.run <| anyFVarM (pure <| f ·) x
|
| 209 |
+
|
| 210 |
+
def allFVar [TraverseFVar α] (f : FVarId → Bool) (x : α) : Bool :=
|
| 211 |
+
Id.run <| allFVarM (pure <| f ·) x
|
| 212 |
+
|
| 213 |
+
end Lean.Compiler.LCNF
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/FixedParams.lean
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2022 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Compiler.LCNF.Basic
|
| 8 |
+
import Lean.Compiler.LCNF.Types
|
| 9 |
+
|
| 10 |
+
namespace Lean.Compiler.LCNF
|
| 11 |
+
namespace FixedParams
|
| 12 |
+
|
| 13 |
+
/-! # Fixed Parameter Static Analyzer -/
|
| 14 |
+
|
| 15 |
+
/-
|
| 16 |
+
If function is partially applied, we assume missing parameters are not fixed.
|
| 17 |
+
Note that, since LCNF is in A-normal form, if function is used as an argument, none of its parameters
|
| 18 |
+
will be considered fixed.
|
| 19 |
+
|
| 20 |
+
We assume functions that are not in mutual block do not invoke the function being
|
| 21 |
+
analyzed.
|
| 22 |
+
|
| 23 |
+
We track fixed arguments using "abstract values". They are just a "token" or ⊤.
|
| 24 |
+
We can view it as a form of constant propagation.
|
| 25 |
+
|
| 26 |
+
When analyzing a function call to another function in the same mutual block,
|
| 27 |
+
we visit its body after binding its parameters to "abstract values". We keep a cache
|
| 28 |
+
of the already visited pairs ("declName", "abstract values").
|
| 29 |
+
|
| 30 |
+
Whenever, we find a recursive call to the function being analyzed, we check whether
|
| 31 |
+
the arguments match the initial "abstract values".
|
| 32 |
+
|
| 33 |
+
We interrupt the analysis if all parameters of the function being analyzed have been
|
| 34 |
+
marked as not fixed.
|
| 35 |
+
-/
|
| 36 |
+
|
| 37 |
+
/-- Abstract value for the "fixed parameter" analysis. -/
|
| 38 |
+
inductive AbsValue where
|
| 39 |
+
| top
|
| 40 |
+
| erased
|
| 41 |
+
| val (i : Nat)
|
| 42 |
+
deriving Inhabited, BEq, Hashable
|
| 43 |
+
|
| 44 |
+
structure Context where
|
| 45 |
+
/-- Declaration in the same mutual block. -/
|
| 46 |
+
decls : Array Decl
|
| 47 |
+
/--
|
| 48 |
+
Function being analyzed. We check every recursive call to this function.
|
| 49 |
+
Remark: `main` is in `decls`.
|
| 50 |
+
-/
|
| 51 |
+
main : Decl
|
| 52 |
+
/--
|
| 53 |
+
The assignment maps free variable ids in the current code being analyzed to abstract values.
|
| 54 |
+
We only track the abstract value assigned to parameters.
|
| 55 |
+
-/
|
| 56 |
+
assignment : FVarIdMap AbsValue
|
| 57 |
+
|
| 58 |
+
structure State where
|
| 59 |
+
/--
|
| 60 |
+
Set of calls that have been already analyzed.
|
| 61 |
+
Recall that we assume that only functions in `decls` may have recursive calls to the function being analyzed (i.e., `main`).
|
| 62 |
+
Whenever there is function application `f a₁ ... aₙ`, where `f` is in `decls`, `f` is not `main`, and
|
| 63 |
+
we visit with the abstract values assigned to `aᵢ`, but first we record the visit here.
|
| 64 |
+
-/
|
| 65 |
+
visited : Std.HashSet (Name × Array AbsValue) := {}
|
| 66 |
+
/--
|
| 67 |
+
Bitmask containing the result, i.e., which parameters of `main` are fixed.
|
| 68 |
+
We initialize it with `true` everywhere.
|
| 69 |
+
-/
|
| 70 |
+
fixed : Array Bool
|
| 71 |
+
|
| 72 |
+
/-- Monad for the fixed parameter static analyzer. We use the unit-exception to interrupt the analysis. -/
|
| 73 |
+
abbrev FixParamM := ReaderT Context <| EStateM Unit State
|
| 74 |
+
|
| 75 |
+
/-- Stop the analysis and mark all parameters as non-fixed. -/
|
| 76 |
+
abbrev abort : FixParamM α := do
|
| 77 |
+
modify fun s => { s with fixed := s.fixed.map fun _ => false }
|
| 78 |
+
throw ()
|
| 79 |
+
|
| 80 |
+
def evalFVar (fvarId : FVarId) : FixParamM AbsValue := do
|
| 81 |
+
let some val := (← read).assignment.find? fvarId | return .top
|
| 82 |
+
return val
|
| 83 |
+
|
| 84 |
+
def evalArg (arg : Arg) : FixParamM AbsValue := do
|
| 85 |
+
match arg with
|
| 86 |
+
| .erased => return .erased
|
| 87 |
+
| .type (.fvar fvarId) => evalFVar fvarId
|
| 88 |
+
| .type _ => return .top
|
| 89 |
+
| .fvar fvarId => evalFVar fvarId
|
| 90 |
+
|
| 91 |
+
def inMutualBlock (declName : Name) : FixParamM Bool :=
|
| 92 |
+
return (← read).decls.any (·.name == declName)
|
| 93 |
+
|
| 94 |
+
def mkAssignment (decl : Decl) (values : Array AbsValue) : FVarIdMap AbsValue := Id.run do
|
| 95 |
+
let mut assignment := {}
|
| 96 |
+
for param in decl.params, value in values do
|
| 97 |
+
assignment := assignment.insert param.fvarId value
|
| 98 |
+
return assignment
|
| 99 |
+
|
| 100 |
+
mutual
|
| 101 |
+
|
| 102 |
+
partial def evalLetValue (e : LetValue) : FixParamM Unit := do
|
| 103 |
+
match e with
|
| 104 |
+
| .const declName _ args => evalApp declName args
|
| 105 |
+
| _ => return ()
|
| 106 |
+
|
| 107 |
+
partial def evalCode (code : Code) : FixParamM Unit := do
|
| 108 |
+
match code with
|
| 109 |
+
| .let decl k => evalLetValue decl.value; evalCode k
|
| 110 |
+
| .fun decl k | .jp decl k => evalCode decl.value; evalCode k
|
| 111 |
+
| .cases c => c.alts.forM fun alt => evalCode alt.getCode
|
| 112 |
+
| .unreach .. | .jmp .. | .return .. => return ()
|
| 113 |
+
|
| 114 |
+
partial def evalApp (declName : Name) (args : Array Arg) : FixParamM Unit := do
|
| 115 |
+
let main := (← read).main
|
| 116 |
+
if declName == main.name then
|
| 117 |
+
-- Recursive call to the function being analyzed
|
| 118 |
+
for h : i in [:main.params.size] do
|
| 119 |
+
if _h : i < args.size then
|
| 120 |
+
have : i < main.params.size := h.upper
|
| 121 |
+
let param := main.params[i]
|
| 122 |
+
let val ← evalArg args[i]
|
| 123 |
+
unless val == .val i || (val == .erased && param.type.isErased) do
|
| 124 |
+
-- Found non fixed argument
|
| 125 |
+
-- Remark: if the argument is erased and the type of the parameter is erased we assume it is a fixed "propositonal" parameter.
|
| 126 |
+
modify fun s => { s with fixed := s.fixed.set! i false }
|
| 127 |
+
else
|
| 128 |
+
-- Partial application mark argument as not fixed
|
| 129 |
+
modify fun s => { s with fixed := s.fixed.set! i false }
|
| 130 |
+
unless (← get).fixed.contains true do
|
| 131 |
+
throw () -- stop analysis, none of the arguments are fixed.
|
| 132 |
+
for decl in (← read).decls do
|
| 133 |
+
if declName == decl.name then
|
| 134 |
+
-- Call to another function in the same mutual block.
|
| 135 |
+
let mut values := #[]
|
| 136 |
+
for i in [:decl.params.size] do
|
| 137 |
+
if h : i < args.size then
|
| 138 |
+
values := values.push (← evalArg args[i])
|
| 139 |
+
else
|
| 140 |
+
values := values.push .top
|
| 141 |
+
let key := (declName, values)
|
| 142 |
+
unless (← get).visited.contains key do
|
| 143 |
+
modify fun s => { s with visited := s.visited.insert key }
|
| 144 |
+
decl.value.forCodeM fun c =>
|
| 145 |
+
let assignment := mkAssignment decl values
|
| 146 |
+
withReader (fun ctx => { ctx with assignment }) <| evalCode c
|
| 147 |
+
|
| 148 |
+
end
|
| 149 |
+
|
| 150 |
+
def mkInitialValues (numParams : Nat) : Array AbsValue := Id.run do
|
| 151 |
+
let mut values := #[]
|
| 152 |
+
for i in [:numParams] do
|
| 153 |
+
values := values.push <| .val i
|
| 154 |
+
return values
|
| 155 |
+
|
| 156 |
+
end FixedParams
|
| 157 |
+
open FixedParams
|
| 158 |
+
|
| 159 |
+
/--
|
| 160 |
+
Given the (potentially mutually) recursive declarations `decls`,
|
| 161 |
+
return a map from declaration name `decl.name` to a bit-mask `m` where `m[i]` is true
|
| 162 |
+
iff the `decl.params[i]` is a fixed argument. That is, it does not change in recursive
|
| 163 |
+
applications.
|
| 164 |
+
The function assumes that if a function `f` was declared in a mutual block, then `decls`
|
| 165 |
+
contains all (computationally relevant) functions in the mutual block.
|
| 166 |
+
-/
|
| 167 |
+
def mkFixedParamsMap (decls : Array Decl) : NameMap (Array Bool) := Id.run do
|
| 168 |
+
let mut result := {}
|
| 169 |
+
for decl in decls do
|
| 170 |
+
let values := mkInitialValues decl.params.size
|
| 171 |
+
let assignment := mkAssignment decl values
|
| 172 |
+
let fixed := Array.replicate decl.params.size true
|
| 173 |
+
match decl.value with
|
| 174 |
+
| .code c =>
|
| 175 |
+
match evalCode c |>.run { main := decl, decls, assignment } |>.run { fixed } with
|
| 176 |
+
| .ok _ s | .error _ s => result := result.insert decl.name s.fixed
|
| 177 |
+
| .extern .. =>
|
| 178 |
+
result := result.insert decl.name fixed
|
| 179 |
+
return result
|
| 180 |
+
|
| 181 |
+
end Lean.Compiler.LCNF
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/FloatLetIn.lean
ADDED
|
@@ -0,0 +1,310 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2022 Henrik Böving. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Henrik Böving
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Compiler.LCNF.CompilerM
|
| 8 |
+
import Lean.Compiler.LCNF.FVarUtil
|
| 9 |
+
import Lean.Compiler.LCNF.PassManager
|
| 10 |
+
import Lean.Compiler.LCNF.Types
|
| 11 |
+
|
| 12 |
+
namespace Lean.Compiler.LCNF
|
| 13 |
+
|
| 14 |
+
namespace FloatLetIn
|
| 15 |
+
|
| 16 |
+
/--
|
| 17 |
+
The decision of the float mechanism.
|
| 18 |
+
-/
|
| 19 |
+
inductive Decision where
|
| 20 |
+
|
|
| 21 |
+
/--
|
| 22 |
+
Push into the arm with name `name`.
|
| 23 |
+
-/
|
| 24 |
+
arm (name : Name)
|
| 25 |
+
| /--
|
| 26 |
+
Push into the default arm.
|
| 27 |
+
-/
|
| 28 |
+
default
|
| 29 |
+
|
|
| 30 |
+
/--
|
| 31 |
+
Dont move this declaration it is needed where it is right now.
|
| 32 |
+
-/
|
| 33 |
+
dont
|
| 34 |
+
|
|
| 35 |
+
/--
|
| 36 |
+
No decision has been made yet.
|
| 37 |
+
-/
|
| 38 |
+
unknown
|
| 39 |
+
deriving Hashable, BEq, Inhabited, Repr
|
| 40 |
+
|
| 41 |
+
def Decision.ofAlt : Alt → Decision
|
| 42 |
+
| .alt name _ _ => .arm name
|
| 43 |
+
| .default _ => .default
|
| 44 |
+
|
| 45 |
+
/--
|
| 46 |
+
The context for `BaseFloatM`.
|
| 47 |
+
-/
|
| 48 |
+
structure BaseFloatContext where
|
| 49 |
+
/--
|
| 50 |
+
All the declarations that were collected in the current LCNF basic
|
| 51 |
+
block up to the current statement (in reverse order for efficiency).
|
| 52 |
+
-/
|
| 53 |
+
decls : List CodeDecl := []
|
| 54 |
+
|
| 55 |
+
/--
|
| 56 |
+
The state for `FloatM`
|
| 57 |
+
-/
|
| 58 |
+
structure FloatState where
|
| 59 |
+
/--
|
| 60 |
+
A map from identifiers of declarations to their current decision.
|
| 61 |
+
-/
|
| 62 |
+
decision : Std.HashMap FVarId Decision
|
| 63 |
+
/--
|
| 64 |
+
A map from decisions (excluding `unknown`) to the declarations with
|
| 65 |
+
these decisions (in correct order). Basically:
|
| 66 |
+
- Which declarations do we not move
|
| 67 |
+
- Which declarations do we move into a certain arm
|
| 68 |
+
- Which declarations do we move into the default arm
|
| 69 |
+
-/
|
| 70 |
+
newArms : Std.HashMap Decision (List CodeDecl)
|
| 71 |
+
|
| 72 |
+
/--
|
| 73 |
+
Use to collect relevant declarations for the floating mechanism.
|
| 74 |
+
-/
|
| 75 |
+
abbrev BaseFloatM := ReaderT BaseFloatContext CompilerM
|
| 76 |
+
|
| 77 |
+
/--
|
| 78 |
+
Use to compute the actual floating.
|
| 79 |
+
-/
|
| 80 |
+
abbrev FloatM := StateRefT FloatState BaseFloatM
|
| 81 |
+
|
| 82 |
+
/--
|
| 83 |
+
Add `decl` to the list of declarations and run `x` with that updated context.
|
| 84 |
+
-/
|
| 85 |
+
def withNewCandidate (decl : CodeDecl) (x : BaseFloatM α) : BaseFloatM α :=
|
| 86 |
+
withReader (fun r => { r with decls := decl :: r.decls }) do
|
| 87 |
+
x
|
| 88 |
+
|
| 89 |
+
/--
|
| 90 |
+
Run `x` with an empty list of declarations.
|
| 91 |
+
-/
|
| 92 |
+
def withNewScope (x : BaseFloatM α) : BaseFloatM α := do
|
| 93 |
+
withReader (fun _ => {}) do
|
| 94 |
+
x
|
| 95 |
+
|
| 96 |
+
/--
|
| 97 |
+
Whether to ignore `decl` for the floating mechanism. We want to do this if:
|
| 98 |
+
- `decl`' is storing a typeclass instance
|
| 99 |
+
- `decl` is a projection from a variable that is storing a typeclass instance
|
| 100 |
+
-/
|
| 101 |
+
def ignore? (decl : LetDecl) : BaseFloatM Bool := do
|
| 102 |
+
if (← isArrowClass? decl.type).isSome then
|
| 103 |
+
return true
|
| 104 |
+
else if let .proj _ _ fvarId := decl.value then
|
| 105 |
+
return (← isArrowClass? (← getType fvarId)).isSome
|
| 106 |
+
else
|
| 107 |
+
return false
|
| 108 |
+
|
| 109 |
+
/--
|
| 110 |
+
Compute the initial decision for all declarations that `BaseFloatM` collected
|
| 111 |
+
up to this point, with respect to `cs`. The initial decisions are:
|
| 112 |
+
- `dont` if the declaration is detected by `ignore?`
|
| 113 |
+
- `dont` if the declaration is the discriminant of `cs` since we obviously need
|
| 114 |
+
the discriminant to be computed before the match.
|
| 115 |
+
- `dont` if we see the declaration being used in more than one cases arm
|
| 116 |
+
- `arm` or `default` if we see the declaration only being used in exactly one cases arm
|
| 117 |
+
- `unknown` otherwise
|
| 118 |
+
-/
|
| 119 |
+
def initialDecisions (cs : Cases) : BaseFloatM (Std.HashMap FVarId Decision) := do
|
| 120 |
+
let mut map := Std.HashMap.emptyWithCapacity (← read).decls.length
|
| 121 |
+
map ← (← read).decls.foldrM (init := map) fun val acc => do
|
| 122 |
+
if let .let decl := val then
|
| 123 |
+
if (← ignore? decl) then
|
| 124 |
+
return acc.insert decl.fvarId .dont
|
| 125 |
+
return acc.insert val.fvarId .unknown
|
| 126 |
+
|
| 127 |
+
if map.contains cs.discr then
|
| 128 |
+
map := map.insert cs.discr .dont
|
| 129 |
+
(_, map) ← goCases cs |>.run map
|
| 130 |
+
return map
|
| 131 |
+
where
|
| 132 |
+
goFVar (plannedDecision : Decision) (var : FVarId) : StateRefT (Std.HashMap FVarId Decision) BaseFloatM Unit := do
|
| 133 |
+
if let some decision := (← get)[var]? then
|
| 134 |
+
if decision == .unknown then
|
| 135 |
+
modify fun s => s.insert var plannedDecision
|
| 136 |
+
else if decision != plannedDecision then
|
| 137 |
+
modify fun s => s.insert var .dont
|
| 138 |
+
-- otherwise we already have the proper decision
|
| 139 |
+
|
| 140 |
+
goAlt (alt : Alt) : StateRefT (Std.HashMap FVarId Decision) BaseFloatM Unit :=
|
| 141 |
+
forFVarM (goFVar (.ofAlt alt)) alt
|
| 142 |
+
goCases (cs : Cases) : StateRefT (Std.HashMap FVarId Decision) BaseFloatM Unit :=
|
| 143 |
+
cs.alts.forM goAlt
|
| 144 |
+
|
| 145 |
+
/--
|
| 146 |
+
Compute the initial new arms. This will just set up a map from all arms of
|
| 147 |
+
`cs` to empty `Array`s, plus one additional entry for `dont`.
|
| 148 |
+
-/
|
| 149 |
+
def initialNewArms (cs : Cases) : Std.HashMap Decision (List CodeDecl) := Id.run do
|
| 150 |
+
let mut map := Std.HashMap.emptyWithCapacity (cs.alts.size + 1)
|
| 151 |
+
map := map.insert .dont []
|
| 152 |
+
cs.alts.foldr (init := map) fun val acc => acc.insert (.ofAlt val) []
|
| 153 |
+
|
| 154 |
+
/--
|
| 155 |
+
Will:
|
| 156 |
+
- put `decl` into the `dont` arm
|
| 157 |
+
- decide that any free variable that occurs in `decl` and is a declaration
|
| 158 |
+
of interest as not getting moved either.
|
| 159 |
+
```
|
| 160 |
+
let x := ...
|
| 161 |
+
let y := ...
|
| 162 |
+
let z := x + y
|
| 163 |
+
cases z with
|
| 164 |
+
| n => z * x
|
| 165 |
+
| m => z * y
|
| 166 |
+
```
|
| 167 |
+
Here `x` and `y` are originally marked as getting floated into `n` and `m`
|
| 168 |
+
respectively but since `z` can't be moved we don't want that to move `x` and `y`.
|
| 169 |
+
-/
|
| 170 |
+
def dontFloat (decl : CodeDecl) : FloatM Unit := do
|
| 171 |
+
forFVarM goFVar decl
|
| 172 |
+
modify fun s => { s with newArms := s.newArms.insert .dont (decl :: s.newArms[Decision.dont]!) }
|
| 173 |
+
where
|
| 174 |
+
goFVar (fvar : FVarId) : FloatM Unit := do
|
| 175 |
+
if (← get).decision.contains fvar then
|
| 176 |
+
modify fun s => { s with decision := s.decision.insert fvar .dont }
|
| 177 |
+
|
| 178 |
+
/--
|
| 179 |
+
Will:
|
| 180 |
+
- put `decl` into the arm it is marked to be moved into
|
| 181 |
+
- for any variables that might occur in `decl` and are of interest:
|
| 182 |
+
- if they are already meant to be floated into the same arm or not at all leave them untouched:
|
| 183 |
+
```
|
| 184 |
+
let x := ...
|
| 185 |
+
let y := x + z
|
| 186 |
+
cases z with
|
| 187 |
+
| n => x * y
|
| 188 |
+
| m => z
|
| 189 |
+
```
|
| 190 |
+
If we are at `y` `x` is already marked to be floated into `n` as well.
|
| 191 |
+
- if there hasn't be a decision yet, that is they are marked with `.unknown` we float
|
| 192 |
+
them into the same arm as the current value:
|
| 193 |
+
```
|
| 194 |
+
let x := ..
|
| 195 |
+
let y := x + 2
|
| 196 |
+
cases z with
|
| 197 |
+
| n => y
|
| 198 |
+
| m => z
|
| 199 |
+
```
|
| 200 |
+
Here `x` is initially marked as `.unknown` since it occurs in no branch, however
|
| 201 |
+
since we want to move `y` into the `n` branch we can also decide to move `x`
|
| 202 |
+
into the `n` branch. Note that this decision might be revoked later on in the case of:
|
| 203 |
+
```
|
| 204 |
+
let x := ..
|
| 205 |
+
let a := x + 1
|
| 206 |
+
let y := x + 2
|
| 207 |
+
cases z with
|
| 208 |
+
| n => y
|
| 209 |
+
| m => a
|
| 210 |
+
```
|
| 211 |
+
When we visit `a` `x` is now marked as getting moved into `n` but since it also occurs
|
| 212 |
+
in `a` which wants to be moved somewhere else we will instead decide to not move `x`
|
| 213 |
+
at all.
|
| 214 |
+
- if they are meant to be floated somewhere else decide that they won't get floated:
|
| 215 |
+
```
|
| 216 |
+
let x := ...
|
| 217 |
+
let y := x + z
|
| 218 |
+
cases z with
|
| 219 |
+
| n => y
|
| 220 |
+
| m => x
|
| 221 |
+
```
|
| 222 |
+
If we are at `y` `x` is still marked to be moved but we don't want that.
|
| 223 |
+
-/
|
| 224 |
+
def float (decl : CodeDecl) : FloatM Unit := do
|
| 225 |
+
let arm := (← get).decision[decl.fvarId]!
|
| 226 |
+
forFVarM (goFVar · arm) decl
|
| 227 |
+
modify fun s => { s with newArms := s.newArms.insert arm (decl :: s.newArms[arm]!) }
|
| 228 |
+
where
|
| 229 |
+
goFVar (fvar : FVarId) (arm : Decision) : FloatM Unit := do
|
| 230 |
+
let some decision := (← get).decision[fvar]? | return ()
|
| 231 |
+
if decision == .unknown then
|
| 232 |
+
modify fun s => { s with decision := s.decision.insert fvar arm }
|
| 233 |
+
else if decision != arm then
|
| 234 |
+
modify fun s => { s with decision := s.decision.insert fvar .dont }
|
| 235 |
+
|
| 236 |
+
/--
|
| 237 |
+
Iterate through `decl`, pushing local declarations that are only used in one
|
| 238 |
+
control flow arm into said arm in order to avoid useless computations.
|
| 239 |
+
-/
|
| 240 |
+
partial def floatLetIn (decl : Decl) : CompilerM Decl := do
|
| 241 |
+
let newValue ← decl.value.mapCodeM go |>.run {}
|
| 242 |
+
return { decl with value := newValue }
|
| 243 |
+
where
|
| 244 |
+
/--
|
| 245 |
+
Iterate through the collected declarations,
|
| 246 |
+
determining from the bottom up whether they (and the declarations they refer to)
|
| 247 |
+
should get moved down into the arms of the cases statement or not.
|
| 248 |
+
-/
|
| 249 |
+
goCases : FloatM Unit := do
|
| 250 |
+
for decl in (← read).decls do
|
| 251 |
+
let currentDecision := (← get).decision[decl.fvarId]!
|
| 252 |
+
if currentDecision == .unknown then
|
| 253 |
+
/-
|
| 254 |
+
If the decision is still unknown by now this means `decl` is
|
| 255 |
+
unused in its continuation and can hence be removed.
|
| 256 |
+
-/
|
| 257 |
+
eraseCodeDecl decl
|
| 258 |
+
else if currentDecision == .dont then
|
| 259 |
+
dontFloat decl
|
| 260 |
+
else
|
| 261 |
+
float decl
|
| 262 |
+
|
| 263 |
+
go (code : Code) : BaseFloatM Code := do
|
| 264 |
+
match code with
|
| 265 |
+
| .let decl k =>
|
| 266 |
+
withNewCandidate (.let decl) do
|
| 267 |
+
go k
|
| 268 |
+
| .jp decl k =>
|
| 269 |
+
let value ← withNewScope do
|
| 270 |
+
go decl.value
|
| 271 |
+
let decl ← decl.updateValue value
|
| 272 |
+
withNewCandidate (.jp decl) do
|
| 273 |
+
go k
|
| 274 |
+
| .fun decl k =>
|
| 275 |
+
let value ← withNewScope do
|
| 276 |
+
go decl.value
|
| 277 |
+
let decl ← decl.updateValue value
|
| 278 |
+
withNewCandidate (.fun decl) do
|
| 279 |
+
go k
|
| 280 |
+
| .cases cs =>
|
| 281 |
+
let base := {
|
| 282 |
+
decision := (← initialDecisions cs)
|
| 283 |
+
newArms := initialNewArms cs
|
| 284 |
+
}
|
| 285 |
+
let (_, res) ← goCases |>.run base
|
| 286 |
+
let remainders := res.newArms[Decision.dont]!
|
| 287 |
+
let newAlts ← cs.alts.mapM fun alt => do
|
| 288 |
+
let decision := Decision.ofAlt alt
|
| 289 |
+
let newCode := res.newArms[decision]!
|
| 290 |
+
trace[Compiler.floatLetIn] "Size of code that was pushed into arm: {repr decision} {newCode.length}"
|
| 291 |
+
let fused ← withNewScope do
|
| 292 |
+
go (attachCodeDecls newCode.toArray alt.getCode)
|
| 293 |
+
return alt.updateCode fused
|
| 294 |
+
let mut newCases := Code.updateCases! code cs.resultType cs.discr newAlts
|
| 295 |
+
return attachCodeDecls remainders.toArray newCases
|
| 296 |
+
| .jmp .. | .return .. | .unreach .. =>
|
| 297 |
+
return attachCodeDecls (← read).decls.toArray.reverse code
|
| 298 |
+
|
| 299 |
+
end FloatLetIn
|
| 300 |
+
|
| 301 |
+
def Decl.floatLetIn (decl : Decl) : CompilerM Decl := do
|
| 302 |
+
FloatLetIn.floatLetIn decl
|
| 303 |
+
|
| 304 |
+
def floatLetIn (phase := Phase.base) (occurrence := 0) : Pass :=
|
| 305 |
+
.mkPerDeclaration `floatLetIn Decl.floatLetIn phase occurrence
|
| 306 |
+
|
| 307 |
+
builtin_initialize
|
| 308 |
+
registerTraceClass `Compiler.floatLetIn (inherited := true)
|
| 309 |
+
|
| 310 |
+
end Lean.Compiler.LCNF
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/InferType.lean
ADDED
|
@@ -0,0 +1,348 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2022 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Compiler.LCNF.CompilerM
|
| 8 |
+
import Lean.Compiler.LCNF.Types
|
| 9 |
+
import Lean.Compiler.LCNF.PhaseExt
|
| 10 |
+
import Lean.Compiler.LCNF.OtherDecl
|
| 11 |
+
|
| 12 |
+
namespace Lean.Compiler.LCNF
|
| 13 |
+
/-! # Type inference for LCNF -/
|
| 14 |
+
|
| 15 |
+
/-
|
| 16 |
+
Note about **erasure confusion**.
|
| 17 |
+
|
| 18 |
+
1- After instantiating universe polymorphic code, we may have
|
| 19 |
+
some types that become propositions, and all propositions are erased.
|
| 20 |
+
|
| 21 |
+
For example, suppose we have
|
| 22 |
+
```
|
| 23 |
+
def f (α : Sort u) (x : α → α → Sort v) (a b : α) (h : x a b) ...
|
| 24 |
+
```
|
| 25 |
+
The LCNF type for this universe polymorphic declaration is
|
| 26 |
+
```
|
| 27 |
+
def f (α : Sort u) (x : α → α → Sort v) (a b : α) (h : x ◾ ◾) ...
|
| 28 |
+
```
|
| 29 |
+
Now, if we instantiate with `v` with the universe `0`, we have that `x ◾ ◾` is also a proposition
|
| 30 |
+
and should be erased.
|
| 31 |
+
|
| 32 |
+
2- We may also get "erasure confusion" when instantiating
|
| 33 |
+
polymorphic code with types and type formers. For example, suppose we have
|
| 34 |
+
```
|
| 35 |
+
structure S (α : Type u) (β : Type v) (f : α → β) where
|
| 36 |
+
a : α
|
| 37 |
+
b : β := f a
|
| 38 |
+
```
|
| 39 |
+
The LCNF type for `S.mk` is
|
| 40 |
+
```
|
| 41 |
+
S.mk : {α : Type u} → {β : Type v} → {f : α → β} → α → β → S α β ◾
|
| 42 |
+
```
|
| 43 |
+
Note that `f` was erased from the resulting type `S α β ◾` because it is
|
| 44 |
+
not a type former. Now, suppose we have the valid Lean declaration
|
| 45 |
+
```
|
| 46 |
+
def f : S Nat Type (fun _ => Nat) :=
|
| 47 |
+
S.mk 0 Nat
|
| 48 |
+
```
|
| 49 |
+
The LNCF type for the value `S.mk 0 Nat` is `S Nat Type ◾` (see `S.mk` type above),
|
| 50 |
+
but the expected type is `S Nat Type (fun x => Nat)`. `fun x => Nat` is not erased
|
| 51 |
+
here because it is a type former.
|
| 52 |
+
-/
|
| 53 |
+
|
| 54 |
+
namespace InferType
|
| 55 |
+
|
| 56 |
+
/-
|
| 57 |
+
Type inference algorithm for LCNF. Invoked by the LCNF type checker
|
| 58 |
+
to check correctness of LCNF IR.
|
| 59 |
+
-/
|
| 60 |
+
|
| 61 |
+
/--
|
| 62 |
+
We use a regular local context to store temporary local declarations
|
| 63 |
+
created during type inference.
|
| 64 |
+
-/
|
| 65 |
+
abbrev InferTypeM := ReaderT LocalContext CompilerM
|
| 66 |
+
|
| 67 |
+
def getBinderName (fvarId : FVarId) : InferTypeM Name := do
|
| 68 |
+
match (← read).find? fvarId with
|
| 69 |
+
| some localDecl => return localDecl.userName
|
| 70 |
+
| none => LCNF.getBinderName fvarId
|
| 71 |
+
|
| 72 |
+
def getType (fvarId : FVarId) : InferTypeM Expr := do
|
| 73 |
+
match (← read).find? fvarId with
|
| 74 |
+
| some localDecl => return localDecl.type
|
| 75 |
+
| none => LCNF.getType fvarId
|
| 76 |
+
|
| 77 |
+
def mkForallFVars (xs : Array Expr) (type : Expr) : InferTypeM Expr :=
|
| 78 |
+
let b := type.abstract xs
|
| 79 |
+
xs.size.foldRevM (init := b) fun i _ b => do
|
| 80 |
+
let x := xs[i]
|
| 81 |
+
let n ← InferType.getBinderName x.fvarId!
|
| 82 |
+
let ty ← InferType.getType x.fvarId!
|
| 83 |
+
let ty := ty.abstractRange i xs;
|
| 84 |
+
return .forallE n ty b .default
|
| 85 |
+
|
| 86 |
+
def mkForallParams (params : Array Param) (type : Expr) : InferTypeM Expr :=
|
| 87 |
+
let xs := params.map fun p => .fvar p.fvarId
|
| 88 |
+
mkForallFVars xs type |>.run {}
|
| 89 |
+
|
| 90 |
+
@[inline] def withLocalDecl (binderName : Name) (type : Expr) (binderInfo : BinderInfo) (k : Expr → InferTypeM α) : InferTypeM α := do
|
| 91 |
+
let fvarId ← mkFreshFVarId
|
| 92 |
+
withReader (fun lctx => lctx.mkLocalDecl fvarId binderName type binderInfo) do
|
| 93 |
+
k (.fvar fvarId)
|
| 94 |
+
|
| 95 |
+
def inferConstType (declName : Name) (us : List Level) : CompilerM Expr := do
|
| 96 |
+
if declName == ``lcErased then
|
| 97 |
+
return erasedExpr
|
| 98 |
+
else if let some decl ← getDecl? declName then
|
| 99 |
+
return decl.instantiateTypeLevelParams us
|
| 100 |
+
else
|
| 101 |
+
/- Declaration does not have code associated with it: constructor, inductive type, foreign function -/
|
| 102 |
+
getOtherDeclType declName us
|
| 103 |
+
|
| 104 |
+
def inferLitValueType (value : LitValue) : Expr :=
|
| 105 |
+
match value with
|
| 106 |
+
| .nat .. => mkConst ``Nat
|
| 107 |
+
| .str .. => mkConst ``String
|
| 108 |
+
| .uint8 .. => mkConst ``UInt8
|
| 109 |
+
| .uint16 .. => mkConst ``UInt16
|
| 110 |
+
| .uint32 .. => mkConst ``UInt32
|
| 111 |
+
| .uint64 .. => mkConst ``UInt64
|
| 112 |
+
| .usize .. => mkConst ``USize
|
| 113 |
+
|
| 114 |
+
mutual
|
| 115 |
+
partial def inferArgType (arg : Arg) : InferTypeM Expr :=
|
| 116 |
+
match arg with
|
| 117 |
+
| .erased => return erasedExpr
|
| 118 |
+
| .type e => inferType e
|
| 119 |
+
| .fvar fvarId => LCNF.getType fvarId
|
| 120 |
+
|
| 121 |
+
partial def inferType (e : Expr) : InferTypeM Expr :=
|
| 122 |
+
match e with
|
| 123 |
+
| .const c us => inferConstType c us
|
| 124 |
+
| .app .. => inferAppType e
|
| 125 |
+
| .fvar fvarId => InferType.getType fvarId
|
| 126 |
+
| .sort lvl => return .sort (mkLevelSucc lvl)
|
| 127 |
+
| .forallE .. => inferForallType e
|
| 128 |
+
| .lam .. => inferLambdaType e
|
| 129 |
+
| .letE .. | .mvar .. | .mdata .. | .lit .. | .bvar .. | .proj .. => unreachable!
|
| 130 |
+
|
| 131 |
+
partial def inferLetValueType (e : LetValue) : InferTypeM Expr := do
|
| 132 |
+
match e with
|
| 133 |
+
| .erased => return erasedExpr
|
| 134 |
+
| .lit v => return inferLitValueType v
|
| 135 |
+
| .proj structName idx fvarId => inferProjType structName idx fvarId
|
| 136 |
+
| .const declName us args => inferAppTypeCore (← inferConstType declName us) args
|
| 137 |
+
| .fvar fvarId args => inferAppTypeCore (← getType fvarId) args
|
| 138 |
+
|
| 139 |
+
partial def inferAppTypeCore (fType : Expr) (args : Array Arg) : InferTypeM Expr := do
|
| 140 |
+
let mut j := 0
|
| 141 |
+
let mut fType := fType
|
| 142 |
+
for i in [:args.size] do
|
| 143 |
+
fType := fType.headBeta
|
| 144 |
+
match fType with
|
| 145 |
+
| .forallE _ _ b _ => fType := b
|
| 146 |
+
| _ =>
|
| 147 |
+
fType := instantiateRevRangeArgs fType j i args |>.headBeta
|
| 148 |
+
match fType with
|
| 149 |
+
| .forallE _ _ b _ => j := i; fType := b
|
| 150 |
+
| _ => return anyExpr
|
| 151 |
+
return instantiateRevRangeArgs fType j args.size args |>.headBeta
|
| 152 |
+
|
| 153 |
+
partial def inferAppType (e : Expr) : InferTypeM Expr := do
|
| 154 |
+
let mut j := 0
|
| 155 |
+
let mut fType ← inferType e.getAppFn
|
| 156 |
+
let args := e.getAppArgs
|
| 157 |
+
for i in [:args.size] do
|
| 158 |
+
fType := fType.headBeta
|
| 159 |
+
match fType with
|
| 160 |
+
| .forallE _ _ b _ => fType := b
|
| 161 |
+
| _ =>
|
| 162 |
+
fType := fType.instantiateRevRange j i args |>.headBeta
|
| 163 |
+
match fType with
|
| 164 |
+
| .forallE _ _ b _ => j := i; fType := b
|
| 165 |
+
| _ => return anyExpr
|
| 166 |
+
return fType.instantiateRevRange j args.size args |>.headBeta
|
| 167 |
+
|
| 168 |
+
partial def inferProjType (structName : Name) (idx : Nat) (s : FVarId) : InferTypeM Expr := do
|
| 169 |
+
let failed {α} : Unit → InferTypeM α := fun _ =>
|
| 170 |
+
throwError "invalid projection{indentExpr (mkProj structName idx (mkFVar s))}"
|
| 171 |
+
let structType := (← getType s).headBeta
|
| 172 |
+
if structType.isErased then
|
| 173 |
+
/- TODO: after we erase universe variables, we can just extract a better type using just `structName` and `idx`. -/
|
| 174 |
+
return erasedExpr
|
| 175 |
+
else if structType.isAny then
|
| 176 |
+
return anyExpr
|
| 177 |
+
else
|
| 178 |
+
matchConstStructure structType.getAppFn failed fun structVal structLvls ctorVal =>
|
| 179 |
+
let structTypeArgs := structType.getAppArgs
|
| 180 |
+
if structVal.numParams + structVal.numIndices != structTypeArgs.size then
|
| 181 |
+
failed ()
|
| 182 |
+
else do
|
| 183 |
+
let mut ctorType ← inferAppType (mkAppN (mkConst ctorVal.name structLvls) structTypeArgs[*...structVal.numParams])
|
| 184 |
+
for _ in [:idx] do
|
| 185 |
+
match ctorType with
|
| 186 |
+
| .forallE _ _ body _ =>
|
| 187 |
+
if body.hasLooseBVars then
|
| 188 |
+
-- This can happen when one of the fields is a type or type former.
|
| 189 |
+
ctorType := body.instantiate1 anyExpr
|
| 190 |
+
else
|
| 191 |
+
ctorType := body
|
| 192 |
+
| _ =>
|
| 193 |
+
if ctorType.isErased then return erasedExpr
|
| 194 |
+
failed ()
|
| 195 |
+
match ctorType with
|
| 196 |
+
| .forallE _ d _ _ => return d
|
| 197 |
+
| _ =>
|
| 198 |
+
if ctorType.isErased then return erasedExpr
|
| 199 |
+
failed ()
|
| 200 |
+
|
| 201 |
+
partial def getLevel? (type : Expr) : InferTypeM (Option Level) := do
|
| 202 |
+
match (← inferType type) with
|
| 203 |
+
| .sort u => return some u
|
| 204 |
+
| _ => return none
|
| 205 |
+
|
| 206 |
+
partial def inferForallType (e : Expr) : InferTypeM Expr :=
|
| 207 |
+
go e #[]
|
| 208 |
+
where
|
| 209 |
+
go (e : Expr) (fvars : Array Expr) : InferTypeM Expr := do
|
| 210 |
+
match e with
|
| 211 |
+
| .forallE n d b bi =>
|
| 212 |
+
withLocalDecl n (d.instantiateRev fvars) bi fun fvar =>
|
| 213 |
+
go b (fvars.push fvar)
|
| 214 |
+
| _ =>
|
| 215 |
+
let e := e.instantiateRev fvars
|
| 216 |
+
let some u ← getLevel? e | return erasedExpr
|
| 217 |
+
let mut u := u
|
| 218 |
+
for x in fvars.reverse do
|
| 219 |
+
let xType ← inferType x
|
| 220 |
+
let some v ← getLevel? xType | return erasedExpr
|
| 221 |
+
u := mkLevelIMax' v u
|
| 222 |
+
return .sort u.normalize
|
| 223 |
+
|
| 224 |
+
partial def inferLambdaType (e : Expr) : InferTypeM Expr :=
|
| 225 |
+
go e #[] #[]
|
| 226 |
+
where
|
| 227 |
+
go (e : Expr) (fvars : Array Expr) (all : Array Expr) : InferTypeM Expr := do
|
| 228 |
+
match e with
|
| 229 |
+
| .lam n d b bi =>
|
| 230 |
+
withLocalDecl n (d.instantiateRev all) bi fun fvar => go b (fvars.push fvar) (all.push fvar)
|
| 231 |
+
| .letE n t _ b _ =>
|
| 232 |
+
withLocalDecl n (t.instantiateRev all) .default fun fvar => go b fvars (all.push fvar)
|
| 233 |
+
| e =>
|
| 234 |
+
let type ← inferType (e.instantiateRev all)
|
| 235 |
+
mkForallFVars fvars type
|
| 236 |
+
|
| 237 |
+
end
|
| 238 |
+
end InferType
|
| 239 |
+
|
| 240 |
+
def inferType (e : Expr) : CompilerM Expr :=
|
| 241 |
+
InferType.inferType e |>.run {}
|
| 242 |
+
|
| 243 |
+
def inferAppType (fnType : Expr) (args : Array Arg) : CompilerM Expr :=
|
| 244 |
+
InferType.inferAppTypeCore fnType args |>.run {}
|
| 245 |
+
|
| 246 |
+
def getLevel (type : Expr) : CompilerM Level := do
|
| 247 |
+
match (← inferType type) with
|
| 248 |
+
| .sort u => return u
|
| 249 |
+
| e => if e.isErased then return levelOne else throwError "type expected{indentExpr type}"
|
| 250 |
+
|
| 251 |
+
def Arg.inferType (arg : Arg) : CompilerM Expr :=
|
| 252 |
+
InferType.inferArgType arg |>.run {}
|
| 253 |
+
|
| 254 |
+
def LetValue.inferType (e : LetValue) : CompilerM Expr :=
|
| 255 |
+
InferType.inferLetValueType e |>.run {}
|
| 256 |
+
|
| 257 |
+
def Code.inferType (code : Code) : CompilerM Expr := do
|
| 258 |
+
match code with
|
| 259 |
+
| .let _ k | .fun _ k | .jp _ k => k.inferType
|
| 260 |
+
| .return fvarId => getType fvarId
|
| 261 |
+
| .jmp fvarId args => InferType.inferAppTypeCore (← getType fvarId) args |>.run {}
|
| 262 |
+
| .unreach type => return type
|
| 263 |
+
| .cases c => return c.resultType
|
| 264 |
+
|
| 265 |
+
def Code.inferParamType (params : Array Param) (code : Code) : CompilerM Expr := do
|
| 266 |
+
let type ← code.inferType
|
| 267 |
+
let xs := params.map fun p => .fvar p.fvarId
|
| 268 |
+
InferType.mkForallFVars xs type |>.run {}
|
| 269 |
+
|
| 270 |
+
def Alt.inferType (alt : Alt) : CompilerM Expr :=
|
| 271 |
+
alt.getCode.inferType
|
| 272 |
+
|
| 273 |
+
def mkAuxLetDecl (e : LetValue) (prefixName := `_x) : CompilerM LetDecl := do
|
| 274 |
+
mkLetDecl (← mkFreshBinderName prefixName) (← e.inferType) e
|
| 275 |
+
|
| 276 |
+
def mkForallParams (params : Array Param) (type : Expr) : CompilerM Expr :=
|
| 277 |
+
InferType.mkForallParams params type |>.run {}
|
| 278 |
+
|
| 279 |
+
def mkAuxFunDecl (params : Array Param) (code : Code) (prefixName := `_f) : CompilerM FunDecl := do
|
| 280 |
+
let type ← mkForallParams params (← code.inferType)
|
| 281 |
+
let binderName ← mkFreshBinderName prefixName
|
| 282 |
+
mkFunDecl binderName type params code
|
| 283 |
+
|
| 284 |
+
def mkAuxJpDecl (params : Array Param) (code : Code) (prefixName := `_jp) : CompilerM FunDecl := do
|
| 285 |
+
mkAuxFunDecl params code prefixName
|
| 286 |
+
|
| 287 |
+
def mkAuxJpDecl' (param : Param) (code : Code) (prefixName := `_jp) : CompilerM FunDecl := do
|
| 288 |
+
let params := #[param]
|
| 289 |
+
mkAuxFunDecl params code prefixName
|
| 290 |
+
|
| 291 |
+
def mkCasesResultType (alts : Array Alt) : CompilerM Expr := do
|
| 292 |
+
if alts.isEmpty then
|
| 293 |
+
throwError "`Code.bind` failed, empty `cases` found"
|
| 294 |
+
let mut resultType ← alts[0]!.inferType
|
| 295 |
+
for alt in alts[1...*] do
|
| 296 |
+
resultType := joinTypes resultType (← alt.inferType)
|
| 297 |
+
return resultType
|
| 298 |
+
|
| 299 |
+
/--
|
| 300 |
+
Return `true` if `type` should be erased. See item 1 in the note above where `x ◾ ◾` is
|
| 301 |
+
a proposition and should be erased when the universe level parameter is set to 0.
|
| 302 |
+
|
| 303 |
+
Remark: `predVars` is a bitmask that indicates whether de-bruijn variables are predicates or not.
|
| 304 |
+
That is, `#i` is a predicate if `predVars[predVars.size - i - 1] = true`
|
| 305 |
+
-/
|
| 306 |
+
partial def isErasedCompatible (type : Expr) (predVars : Array Bool := #[]): CompilerM Bool :=
|
| 307 |
+
go type predVars
|
| 308 |
+
where
|
| 309 |
+
go (type : Expr) (predVars : Array Bool) : CompilerM Bool := do
|
| 310 |
+
let type := type.headBeta
|
| 311 |
+
match type with
|
| 312 |
+
| .const .. => return type.isErased
|
| 313 |
+
| .sort .. => return false
|
| 314 |
+
| .mdata _ e => go e predVars
|
| 315 |
+
| .forallE _ t b _
|
| 316 |
+
| .lam _ t b _ => go b (predVars.push <| isPredicateType t)
|
| 317 |
+
| .app f _ => go f predVars
|
| 318 |
+
| .bvar idx => return predVars[predVars.size - idx - 1]!
|
| 319 |
+
| .fvar fvarId => return isPredicateType (← getType fvarId)
|
| 320 |
+
| .proj .. | .mvar .. | .letE .. | .lit .. => unreachable!
|
| 321 |
+
|
| 322 |
+
/--
|
| 323 |
+
Return `true` if the given LCNF are equivalent.
|
| 324 |
+
`List Nat` and `(fun x => List x) Nat` are both equivalent.
|
| 325 |
+
-/
|
| 326 |
+
partial def eqvTypes (a b : Expr) : Bool :=
|
| 327 |
+
if a == b then
|
| 328 |
+
true
|
| 329 |
+
else if a.isErased && b.isErased then
|
| 330 |
+
-- `◾ α` is equivalent to `◾`
|
| 331 |
+
true
|
| 332 |
+
else
|
| 333 |
+
let a' := a.headBeta
|
| 334 |
+
let b' := b.headBeta
|
| 335 |
+
if a != a' || b != b' then
|
| 336 |
+
eqvTypes a' b'
|
| 337 |
+
else
|
| 338 |
+
match a, b with
|
| 339 |
+
| .mdata _ a, b => eqvTypes a b
|
| 340 |
+
| a, .mdata _ b => eqvTypes a b
|
| 341 |
+
| .app f a, .app g b => eqvTypes f g && eqvTypes a b
|
| 342 |
+
| .forallE _ d₁ b₁ _, .forallE _ d₂ b₂ _ => eqvTypes d₁ d₂ && eqvTypes b₁ b₂
|
| 343 |
+
| .lam _ d₁ b₁ _, .lam _ d₂ b₂ _ => eqvTypes d₁ d₂ && eqvTypes b₁ b₂
|
| 344 |
+
| .sort u, .sort v => Level.isEquiv u v
|
| 345 |
+
| .const n us, .const m vs => n == m && List.isEqv us vs Level.isEquiv
|
| 346 |
+
| _, _ => false
|
| 347 |
+
|
| 348 |
+
end Lean.Compiler.LCNF
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/Internalize.lean
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2022 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Compiler.LCNF.Types
|
| 8 |
+
import Lean.Compiler.LCNF.Bind
|
| 9 |
+
import Lean.Compiler.LCNF.CompilerM
|
| 10 |
+
|
| 11 |
+
namespace Lean.Compiler.LCNF
|
| 12 |
+
|
| 13 |
+
private def refreshBinderName (binderName : Name) : CompilerM Name := do
|
| 14 |
+
match binderName with
|
| 15 |
+
| .num p _ =>
|
| 16 |
+
let r := .num p (← get).nextIdx
|
| 17 |
+
modify fun s => { s with nextIdx := s.nextIdx + 1 }
|
| 18 |
+
return r
|
| 19 |
+
| _ => return binderName
|
| 20 |
+
|
| 21 |
+
namespace Internalize
|
| 22 |
+
|
| 23 |
+
abbrev InternalizeM := StateRefT FVarSubst CompilerM
|
| 24 |
+
|
| 25 |
+
/--
|
| 26 |
+
The `InternalizeM` monad is a translator. It "translates" the free variables
|
| 27 |
+
in the input expressions and `Code`, into new fresh free variables in the
|
| 28 |
+
local context.
|
| 29 |
+
-/
|
| 30 |
+
instance : MonadFVarSubst InternalizeM true where
|
| 31 |
+
getSubst := get
|
| 32 |
+
|
| 33 |
+
instance : MonadFVarSubstState InternalizeM where
|
| 34 |
+
modifySubst := modify
|
| 35 |
+
|
| 36 |
+
private def mkNewFVarId (fvarId : FVarId) : InternalizeM FVarId := do
|
| 37 |
+
let fvarId' ← Lean.mkFreshFVarId
|
| 38 |
+
addFVarSubst fvarId fvarId'
|
| 39 |
+
return fvarId'
|
| 40 |
+
|
| 41 |
+
def internalizeParam (p : Param) : InternalizeM Param := do
|
| 42 |
+
let binderName ← refreshBinderName p.binderName
|
| 43 |
+
let type ← normExpr p.type
|
| 44 |
+
let fvarId ← mkNewFVarId p.fvarId
|
| 45 |
+
let p := { p with binderName, fvarId, type }
|
| 46 |
+
modifyLCtx fun lctx => lctx.addParam p
|
| 47 |
+
return p
|
| 48 |
+
|
| 49 |
+
def internalizeLetDecl (decl : LetDecl) : InternalizeM LetDecl := do
|
| 50 |
+
let binderName ← refreshBinderName decl.binderName
|
| 51 |
+
let type ← normExpr decl.type
|
| 52 |
+
let value ← normLetValue decl.value
|
| 53 |
+
let fvarId ← mkNewFVarId decl.fvarId
|
| 54 |
+
let decl := { decl with binderName, fvarId, type, value }
|
| 55 |
+
modifyLCtx fun lctx => lctx.addLetDecl decl
|
| 56 |
+
return decl
|
| 57 |
+
|
| 58 |
+
mutual
|
| 59 |
+
|
| 60 |
+
partial def internalizeFunDecl (decl : FunDecl) : InternalizeM FunDecl := do
|
| 61 |
+
let type ← normExpr decl.type
|
| 62 |
+
let binderName ← refreshBinderName decl.binderName
|
| 63 |
+
let params ← decl.params.mapM internalizeParam
|
| 64 |
+
let value ← internalizeCode decl.value
|
| 65 |
+
let fvarId ← mkNewFVarId decl.fvarId
|
| 66 |
+
let decl := { decl with binderName, fvarId, params, type, value }
|
| 67 |
+
modifyLCtx fun lctx => lctx.addFunDecl decl
|
| 68 |
+
return decl
|
| 69 |
+
|
| 70 |
+
partial def internalizeCode (code : Code) : InternalizeM Code := do
|
| 71 |
+
match code with
|
| 72 |
+
| .let decl k => return .let (← internalizeLetDecl decl) (← internalizeCode k)
|
| 73 |
+
| .fun decl k => return .fun (← internalizeFunDecl decl) (← internalizeCode k)
|
| 74 |
+
| .jp decl k => return .jp (← internalizeFunDecl decl) (← internalizeCode k)
|
| 75 |
+
| .return fvarId => withNormFVarResult (← normFVar fvarId) fun fvarId => return .return fvarId
|
| 76 |
+
| .jmp fvarId args => withNormFVarResult (← normFVar fvarId) fun fvarId => return .jmp fvarId (← args.mapM normArg)
|
| 77 |
+
| .unreach type => return .unreach (← normExpr type)
|
| 78 |
+
| .cases c =>
|
| 79 |
+
withNormFVarResult (← normFVar c.discr) fun discr => do
|
| 80 |
+
let resultType ← normExpr c.resultType
|
| 81 |
+
let internalizeAltCode (k : Code) : InternalizeM Code :=
|
| 82 |
+
internalizeCode k
|
| 83 |
+
let alts ← c.alts.mapM fun
|
| 84 |
+
| .alt ctorName params k => return .alt ctorName (← params.mapM internalizeParam) (← internalizeAltCode k)
|
| 85 |
+
| .default k => return .default (← internalizeAltCode k)
|
| 86 |
+
return .cases { c with discr, alts, resultType }
|
| 87 |
+
|
| 88 |
+
end
|
| 89 |
+
|
| 90 |
+
partial def internalizeCodeDecl (decl : CodeDecl) : InternalizeM CodeDecl := do
|
| 91 |
+
match decl with
|
| 92 |
+
| .let decl => return .let (← internalizeLetDecl decl)
|
| 93 |
+
| .fun decl => return .fun (← internalizeFunDecl decl)
|
| 94 |
+
| .jp decl => return .jp (← internalizeFunDecl decl)
|
| 95 |
+
|
| 96 |
+
end Internalize
|
| 97 |
+
|
| 98 |
+
/--
|
| 99 |
+
Refresh free variables ids in `code`, and store their declarations in the local context.
|
| 100 |
+
-/
|
| 101 |
+
partial def Code.internalize (code : Code) (s : FVarSubst := {}) : CompilerM Code :=
|
| 102 |
+
Internalize.internalizeCode code |>.run' s
|
| 103 |
+
|
| 104 |
+
open Internalize in
|
| 105 |
+
def Decl.internalize (decl : Decl) (s : FVarSubst := {}): CompilerM Decl :=
|
| 106 |
+
go decl |>.run' s
|
| 107 |
+
where
|
| 108 |
+
go (decl : Decl) : InternalizeM Decl := do
|
| 109 |
+
let type ← normExpr decl.type
|
| 110 |
+
let params ← decl.params.mapM internalizeParam
|
| 111 |
+
let value ← decl.value.mapCodeM internalizeCode
|
| 112 |
+
return { decl with type, params, value }
|
| 113 |
+
|
| 114 |
+
/--
|
| 115 |
+
Create a fresh local context and internalize the given decls.
|
| 116 |
+
-/
|
| 117 |
+
def cleanup (decl : Array Decl) : CompilerM (Array Decl) := do
|
| 118 |
+
modify fun _ => {}
|
| 119 |
+
decl.mapM fun decl => do
|
| 120 |
+
modify fun s => { s with nextIdx := 1 }
|
| 121 |
+
decl.internalize
|
| 122 |
+
|
| 123 |
+
def normalizeFVarIds (decl : Decl) : CoreM Decl := do
|
| 124 |
+
let ngenSaved ← getNGen
|
| 125 |
+
setNGen {}
|
| 126 |
+
try
|
| 127 |
+
CompilerM.run <| decl.internalize
|
| 128 |
+
finally
|
| 129 |
+
setNGen ngenSaved
|
| 130 |
+
|
| 131 |
+
end Lean.Compiler.LCNF
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/JoinPoints.lean
ADDED
|
@@ -0,0 +1,636 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2022 Henrik Böving. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Henrik Böving
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Compiler.LCNF.CompilerM
|
| 8 |
+
import Lean.Compiler.LCNF.PassManager
|
| 9 |
+
import Lean.Compiler.LCNF.PullFunDecls
|
| 10 |
+
import Lean.Compiler.LCNF.FVarUtil
|
| 11 |
+
import Lean.Compiler.LCNF.ScopeM
|
| 12 |
+
import Lean.Compiler.LCNF.InferType
|
| 13 |
+
|
| 14 |
+
namespace Lean.Compiler.LCNF
|
| 15 |
+
|
| 16 |
+
namespace JoinPointFinder
|
| 17 |
+
|
| 18 |
+
open ScopeM
|
| 19 |
+
|
| 20 |
+
/--
|
| 21 |
+
Info about a join point candidate (a `fun` declaration) during the find phase.
|
| 22 |
+
-/
|
| 23 |
+
structure CandidateInfo where
|
| 24 |
+
/--
|
| 25 |
+
The arity of the candidate
|
| 26 |
+
-/
|
| 27 |
+
arity : Nat
|
| 28 |
+
/--
|
| 29 |
+
The set of candidates that rely on this candidate to be a join point.
|
| 30 |
+
For a more detailed explanation see the documentation of `find`
|
| 31 |
+
-/
|
| 32 |
+
associated : Std.HashSet FVarId
|
| 33 |
+
deriving Inhabited
|
| 34 |
+
|
| 35 |
+
/--
|
| 36 |
+
The state for the join point candidate finder.
|
| 37 |
+
-/
|
| 38 |
+
structure FindState where
|
| 39 |
+
/--
|
| 40 |
+
All current join point candidates accessible by their `FVarId`.
|
| 41 |
+
-/
|
| 42 |
+
candidates : Std.HashMap FVarId CandidateInfo := ∅
|
| 43 |
+
/--
|
| 44 |
+
The `FVarId`s of all `fun` declarations that were declared within the
|
| 45 |
+
current `fun`.
|
| 46 |
+
-/
|
| 47 |
+
scope : Std.HashSet FVarId := ∅
|
| 48 |
+
|
| 49 |
+
abbrev ReplaceCtx := Std.HashMap FVarId Name
|
| 50 |
+
|
| 51 |
+
abbrev FindM := ReaderT (Option FVarId) StateRefT FindState ScopeM
|
| 52 |
+
abbrev ReplaceM := ReaderT ReplaceCtx CompilerM
|
| 53 |
+
|
| 54 |
+
/--
|
| 55 |
+
Attempt to find a join point candidate by its `FVarId`.
|
| 56 |
+
-/
|
| 57 |
+
private def findCandidate? (fvarId : FVarId) : FindM (Option CandidateInfo) := do
|
| 58 |
+
return (← get).candidates[fvarId]?
|
| 59 |
+
|
| 60 |
+
/--
|
| 61 |
+
Erase a join point candidate as well as all the ones that depend on it
|
| 62 |
+
by its `FVarId`, no error is thrown is the candidate does not exist.
|
| 63 |
+
-/
|
| 64 |
+
private partial def eraseCandidate (fvarId : FVarId) : FindM Unit := do
|
| 65 |
+
if let some info ← findCandidate? fvarId then
|
| 66 |
+
modify (fun state => { state with candidates := state.candidates.erase fvarId })
|
| 67 |
+
info.associated.forM eraseCandidate
|
| 68 |
+
|
| 69 |
+
/--
|
| 70 |
+
Combinator for modifying the candidates in `FindM`.
|
| 71 |
+
-/
|
| 72 |
+
private def modifyCandidates (f : Std.HashMap FVarId CandidateInfo → Std.HashMap FVarId CandidateInfo) : FindM Unit :=
|
| 73 |
+
modify (fun state => {state with candidates := f state.candidates })
|
| 74 |
+
|
| 75 |
+
/--
|
| 76 |
+
Remove all join point candidates contained in `a`.
|
| 77 |
+
-/
|
| 78 |
+
private partial def removeCandidatesInArg (a : Arg) : FindM Unit := do
|
| 79 |
+
forFVarM eraseCandidate a
|
| 80 |
+
|
| 81 |
+
/--
|
| 82 |
+
Remove all join point candidates contained in `a`.
|
| 83 |
+
-/
|
| 84 |
+
private partial def removeCandidatesInLetValue (e : LetValue) : FindM Unit := do
|
| 85 |
+
forFVarM eraseCandidate e
|
| 86 |
+
|
| 87 |
+
/--
|
| 88 |
+
Add a new join point candidate to the state.
|
| 89 |
+
-/
|
| 90 |
+
private def addCandidate (fvarId : FVarId) (arity : Nat) : FindM Unit := do
|
| 91 |
+
let cinfo := { arity, associated := ∅ }
|
| 92 |
+
modifyCandidates (fun cs => cs.insert fvarId cinfo )
|
| 93 |
+
|
| 94 |
+
/--
|
| 95 |
+
Add a new join point dependency from `src` to `dst`.
|
| 96 |
+
-/
|
| 97 |
+
private def addDependency (src : FVarId) (target : FVarId) : FindM Unit := do
|
| 98 |
+
if let some targetInfo ← findCandidate? target then
|
| 99 |
+
modifyCandidates (fun cs => cs.insert target { targetInfo with associated := targetInfo.associated.insert src })
|
| 100 |
+
else
|
| 101 |
+
eraseCandidate src
|
| 102 |
+
|
| 103 |
+
/--
|
| 104 |
+
Find all `fun` declarations that qualify as a join point, that is:
|
| 105 |
+
- are always fully applied
|
| 106 |
+
- are always called in tail position
|
| 107 |
+
|
| 108 |
+
Where a `fun` `f` is in tail position iff it is called as follows:
|
| 109 |
+
```
|
| 110 |
+
let res := f arg
|
| 111 |
+
res
|
| 112 |
+
```
|
| 113 |
+
The majority (if not all) tail calls will be brought into this form
|
| 114 |
+
by the simplifier pass.
|
| 115 |
+
|
| 116 |
+
Furthermore a `fun` disqualifies as a join point if turning it into a join
|
| 117 |
+
point would turn a call to it into an out of scope join point.
|
| 118 |
+
This can happen if we have something like:
|
| 119 |
+
```
|
| 120 |
+
def test (b : Bool) (x y : Nat) : Nat :=
|
| 121 |
+
fun myjp x => Nat.add x (Nat.add x x)
|
| 122 |
+
fun f y =>
|
| 123 |
+
let x := Nat.add y y
|
| 124 |
+
myjp x
|
| 125 |
+
fun f y =>
|
| 126 |
+
let x := Nat.mul y y
|
| 127 |
+
myjp x
|
| 128 |
+
cases b (f x) (g y)
|
| 129 |
+
```
|
| 130 |
+
`f` and `g` can be detected as a join point right away, however
|
| 131 |
+
`myjp` can only ever be detected as a join point after we have established
|
| 132 |
+
this. This is because otherwise the calls to `myjp` in `f` and `g` would
|
| 133 |
+
produce out of scope join point jumps.
|
| 134 |
+
-/
|
| 135 |
+
partial def find (decl : Decl) : CompilerM FindState := do
|
| 136 |
+
let (_, candidates) ← decl.value.forCodeM go |>.run none |>.run {} |>.run' {}
|
| 137 |
+
return candidates
|
| 138 |
+
where
|
| 139 |
+
go : Code → FindM Unit
|
| 140 |
+
| .let decl k => do
|
| 141 |
+
match k, decl.value with
|
| 142 |
+
| .return valId, .fvar fvarId args =>
|
| 143 |
+
args.forM removeCandidatesInArg
|
| 144 |
+
if let some candidateInfo ← findCandidate? fvarId then
|
| 145 |
+
-- Erase candidate that are not fully applied or applied outside of tail position
|
| 146 |
+
if valId != decl.fvarId || args.size != candidateInfo.arity then
|
| 147 |
+
eraseCandidate fvarId
|
| 148 |
+
-- Out of scope join point candidate handling
|
| 149 |
+
else if let some upperCandidate ← read then
|
| 150 |
+
if !(← isInScope fvarId) then
|
| 151 |
+
addDependency fvarId upperCandidate
|
| 152 |
+
else
|
| 153 |
+
eraseCandidate fvarId
|
| 154 |
+
| _, _ =>
|
| 155 |
+
removeCandidatesInLetValue decl.value
|
| 156 |
+
go k
|
| 157 |
+
| .fun decl k => do
|
| 158 |
+
withReader (fun _ => some decl.fvarId) do
|
| 159 |
+
withNewScope do
|
| 160 |
+
go decl.value
|
| 161 |
+
addCandidate decl.fvarId decl.getArity
|
| 162 |
+
addToScope decl.fvarId
|
| 163 |
+
go k
|
| 164 |
+
| .jp decl k => do
|
| 165 |
+
go decl.value
|
| 166 |
+
go k
|
| 167 |
+
| .jmp _ args => args.forM removeCandidatesInArg
|
| 168 |
+
| .return val => eraseCandidate val
|
| 169 |
+
| .cases c => do
|
| 170 |
+
eraseCandidate c.discr
|
| 171 |
+
c.alts.forM (·.forCodeM go)
|
| 172 |
+
| .unreach .. => return ()
|
| 173 |
+
|
| 174 |
+
/--
|
| 175 |
+
Replace all join point candidate `fun` declarations with `jp` ones
|
| 176 |
+
and all calls to them with `jmp`s.
|
| 177 |
+
-/
|
| 178 |
+
partial def replace (decl : Decl) (state : FindState) : CompilerM Decl := do
|
| 179 |
+
let mapper := fun acc cname _ => do return acc.insert cname (← mkFreshJpName)
|
| 180 |
+
let replaceCtx : ReplaceCtx ← state.candidates.foldM (init := ∅) mapper
|
| 181 |
+
let newValue ← decl.value.mapCodeM go |>.run replaceCtx
|
| 182 |
+
return { decl with value := newValue }
|
| 183 |
+
where
|
| 184 |
+
go (code : Code) : ReplaceM Code := do
|
| 185 |
+
match code with
|
| 186 |
+
| .let decl k =>
|
| 187 |
+
match k, decl.value with
|
| 188 |
+
| .return valId, .fvar fvarId args =>
|
| 189 |
+
if valId == decl.fvarId then
|
| 190 |
+
if (← read).contains fvarId then
|
| 191 |
+
eraseLetDecl decl
|
| 192 |
+
return .jmp fvarId args
|
| 193 |
+
else
|
| 194 |
+
return code
|
| 195 |
+
else
|
| 196 |
+
return code
|
| 197 |
+
| _, _ => return Code.updateLet! code decl (← go k)
|
| 198 |
+
| .fun decl k =>
|
| 199 |
+
if let some replacement := (← read)[decl.fvarId]? then
|
| 200 |
+
let newDecl := { decl with
|
| 201 |
+
binderName := replacement,
|
| 202 |
+
value := (← go decl.value)
|
| 203 |
+
}
|
| 204 |
+
modifyLCtx fun lctx => lctx.addFunDecl newDecl
|
| 205 |
+
return .jp newDecl (← go k)
|
| 206 |
+
else
|
| 207 |
+
let newDecl ← decl.updateValue (← go decl.value)
|
| 208 |
+
return Code.updateFun! code newDecl (← go k)
|
| 209 |
+
| .jp decl k =>
|
| 210 |
+
let newDecl ← decl.updateValue (← go decl.value)
|
| 211 |
+
return Code.updateFun! code newDecl (← go k)
|
| 212 |
+
| .cases cs =>
|
| 213 |
+
return Code.updateCases! code cs.resultType cs.discr (← cs.alts.mapM (·.mapCodeM go))
|
| 214 |
+
| .jmp .. | .return .. | .unreach .. =>
|
| 215 |
+
return code
|
| 216 |
+
|
| 217 |
+
end JoinPointFinder
|
| 218 |
+
|
| 219 |
+
namespace JoinPointContextExtender
|
| 220 |
+
|
| 221 |
+
open ScopeM
|
| 222 |
+
|
| 223 |
+
/--
|
| 224 |
+
The context managed by `ExtendM`.
|
| 225 |
+
-/
|
| 226 |
+
structure ExtendContext where
|
| 227 |
+
/--
|
| 228 |
+
The `FVarId` of the current join point if we are currently inside one.
|
| 229 |
+
-/
|
| 230 |
+
currentJp? : Option FVarId := none
|
| 231 |
+
/--
|
| 232 |
+
The list of valid candidates for extending the context. This will be
|
| 233 |
+
all `let` and `fun` declarations as well as all `jp` parameters up
|
| 234 |
+
until the last `fun` declaration in the tree.
|
| 235 |
+
-/
|
| 236 |
+
candidates : FVarIdSet := {}
|
| 237 |
+
|
| 238 |
+
/--
|
| 239 |
+
The state managed by `ExtendM`.
|
| 240 |
+
-/
|
| 241 |
+
structure ExtendState where
|
| 242 |
+
/--
|
| 243 |
+
A map from join point `FVarId`s to a respective map from free variables
|
| 244 |
+
to `Param`s. The free variables in this map are the once that the context
|
| 245 |
+
of said join point will be extended by passing in the respective parameter.
|
| 246 |
+
-/
|
| 247 |
+
fvarMap : Std.HashMap FVarId (Std.HashMap FVarId Param) := {}
|
| 248 |
+
|
| 249 |
+
/--
|
| 250 |
+
The monad for the `extendJoinPointContext` pass.
|
| 251 |
+
-/
|
| 252 |
+
abbrev ExtendM := ReaderT ExtendContext StateRefT ExtendState ScopeM
|
| 253 |
+
|
| 254 |
+
/--
|
| 255 |
+
Replace a free variable if necessary, that is:
|
| 256 |
+
- It is in the list of candidates
|
| 257 |
+
- We are currently within a join point (if we are within a function there
|
| 258 |
+
cannot be a need to replace them since we dont extend their context)
|
| 259 |
+
- Said join point actually has a replacement parameter registered.
|
| 260 |
+
otherwise just return `fvar`.
|
| 261 |
+
-/
|
| 262 |
+
def replaceFVar (fvar : FVarId) : ExtendM FVarId := do
|
| 263 |
+
if (← read).candidates.contains fvar then
|
| 264 |
+
if let some currentJp := (← read).currentJp? then
|
| 265 |
+
if let some replacement := (← get).fvarMap[currentJp]![fvar]? then
|
| 266 |
+
return replacement.fvarId
|
| 267 |
+
return fvar
|
| 268 |
+
|
| 269 |
+
/--
|
| 270 |
+
Add a new candidate to the current scope + to the list of candidates
|
| 271 |
+
if we are currently within a join point. Then execute `x`.
|
| 272 |
+
-/
|
| 273 |
+
def withNewCandidate (fvar : FVarId) (x : ExtendM α) : ExtendM α := do
|
| 274 |
+
addToScope fvar
|
| 275 |
+
if (← read).currentJp?.isSome then
|
| 276 |
+
withReader (fun ctx => { ctx with candidates := ctx.candidates.insert fvar }) do
|
| 277 |
+
x
|
| 278 |
+
else
|
| 279 |
+
x
|
| 280 |
+
|
| 281 |
+
/--
|
| 282 |
+
Same as `withNewCandidate` but with multiple `FVarId`s.
|
| 283 |
+
-/
|
| 284 |
+
def withNewCandidates (fvars : Array FVarId) (x : ExtendM α) : ExtendM α := do
|
| 285 |
+
if (← read).currentJp?.isSome then
|
| 286 |
+
let candidates := (← read).candidates
|
| 287 |
+
let folder (acc : FVarIdSet) (val : FVarId) := do
|
| 288 |
+
addToScope val
|
| 289 |
+
return acc.insert val
|
| 290 |
+
let newCandidates ← fvars.foldlM (init := candidates) folder
|
| 291 |
+
withReader (fun ctx => { ctx with candidates := newCandidates }) do
|
| 292 |
+
x
|
| 293 |
+
else
|
| 294 |
+
x
|
| 295 |
+
|
| 296 |
+
/--
|
| 297 |
+
Extend the context of the current join point (if we are within one)
|
| 298 |
+
by `fvar` if necessary.
|
| 299 |
+
This is necessary if:
|
| 300 |
+
- `fvar` is not in scope (that is, was declared outside of the current jp)
|
| 301 |
+
- we have not already extended the context by `fvar`
|
| 302 |
+
- the list of candidates contains `fvar`. This is because if we have something
|
| 303 |
+
like:
|
| 304 |
+
```
|
| 305 |
+
let x := ..
|
| 306 |
+
fun f a =>
|
| 307 |
+
jp j b =>
|
| 308 |
+
let y := x
|
| 309 |
+
y
|
| 310 |
+
```
|
| 311 |
+
There is no point in extending the context of `j` by `x` because we
|
| 312 |
+
cannot lift a join point outside of a local function declaration.
|
| 313 |
+
-/
|
| 314 |
+
def extendByIfNecessary (fvar : FVarId) : ExtendM Unit := do
|
| 315 |
+
if let some currentJp := (← read).currentJp? then
|
| 316 |
+
let mut translator := (← get).fvarMap[currentJp]!
|
| 317 |
+
let candidates := (← read).candidates
|
| 318 |
+
if !(← isInScope fvar) && !translator.contains fvar && candidates.contains fvar then
|
| 319 |
+
let typ ← getType fvar
|
| 320 |
+
let newParam ← mkAuxParam typ
|
| 321 |
+
translator := translator.insert fvar newParam
|
| 322 |
+
modify fun s => { s with fvarMap := s.fvarMap.insert currentJp translator }
|
| 323 |
+
|
| 324 |
+
/--
|
| 325 |
+
Merge the extended context of two join points if necessary. That is
|
| 326 |
+
if we have a structure such as:
|
| 327 |
+
```
|
| 328 |
+
jp j.1 ... =>
|
| 329 |
+
jp j.2 .. =>
|
| 330 |
+
...
|
| 331 |
+
...
|
| 332 |
+
```
|
| 333 |
+
And we are just done visiting `j.2` we want to extend the context of
|
| 334 |
+
`j.1` by all free variables that the context of `j.2` was extended by
|
| 335 |
+
as well because we need to drag these variables through at the call sites
|
| 336 |
+
of `j.2` in `j.1`.
|
| 337 |
+
-/
|
| 338 |
+
def mergeJpContextIfNecessary (jp : FVarId) : ExtendM Unit := do
|
| 339 |
+
if (← read).currentJp?.isSome then
|
| 340 |
+
let additionalArgs := (← get).fvarMap[jp]!.toArray
|
| 341 |
+
for (fvar, _) in additionalArgs do
|
| 342 |
+
extendByIfNecessary fvar
|
| 343 |
+
|
| 344 |
+
/--
|
| 345 |
+
We call this whenever we enter a new local function. It clears both the
|
| 346 |
+
current join point and the list of candidates since we can't lift join
|
| 347 |
+
points outside of functions as explained in `mergeJpContextIfNecessary`.
|
| 348 |
+
-/
|
| 349 |
+
def withNewFunScope (x : ExtendM α): ExtendM α := do
|
| 350 |
+
withReader (fun ctx => { ctx with currentJp? := none, candidates := {} }) do
|
| 351 |
+
withNewScope do
|
| 352 |
+
x
|
| 353 |
+
|
| 354 |
+
/--
|
| 355 |
+
We call this whenever we enter a new join point. It will set the current
|
| 356 |
+
join point and extend the list of candidates by all of the parameters of
|
| 357 |
+
the join point. This is so in the case of nested join points that refer
|
| 358 |
+
to parameters of the current one we extend the context of the nested
|
| 359 |
+
join points by said parameters.
|
| 360 |
+
-/
|
| 361 |
+
def withNewJpScope (decl : FunDecl) (x : ExtendM α): ExtendM α := do
|
| 362 |
+
withReader (fun ctx => { ctx with currentJp? := some decl.fvarId }) do
|
| 363 |
+
modify fun s => { s with fvarMap := s.fvarMap.insert decl.fvarId {} }
|
| 364 |
+
withNewScope do
|
| 365 |
+
withNewCandidates (decl.params.map (·.fvarId)) do
|
| 366 |
+
x
|
| 367 |
+
|
| 368 |
+
/--
|
| 369 |
+
We call this whenever we visit a new arm of a cases statement.
|
| 370 |
+
It will back up the current scope (since we are doing a case split
|
| 371 |
+
and want to continue with other arms afterwards) and add all of the
|
| 372 |
+
parameters of the match arm to the list of candidates.
|
| 373 |
+
-/
|
| 374 |
+
def withNewAltScope (alt : Alt) (x : ExtendM α) : ExtendM α := do
|
| 375 |
+
withBackTrackingScope do
|
| 376 |
+
withNewCandidates (alt.getParams.map (·.fvarId)) do
|
| 377 |
+
x
|
| 378 |
+
|
| 379 |
+
/--
|
| 380 |
+
Use all of the above functions to find free variables declared outside
|
| 381 |
+
of join points that said join points can be reasonaly extended by. Reasonable
|
| 382 |
+
meaning that in case the current join point is nested within a function
|
| 383 |
+
declaration we will not extend it by free variables declared before the
|
| 384 |
+
function declaration because we cannot lift join points outside of function
|
| 385 |
+
declarations.
|
| 386 |
+
|
| 387 |
+
All of this is done to eliminate dependencies of join points onto their
|
| 388 |
+
position within the code so we can pull them out as far as possible, hopefully
|
| 389 |
+
enabling new inlining possibilities in the next simplifier run.
|
| 390 |
+
-/
|
| 391 |
+
partial def extend (decl : Decl) : CompilerM Decl := do
|
| 392 |
+
let newValue ← decl.value.mapCodeM go |>.run {} |>.run' {} |>.run' {}
|
| 393 |
+
let decl := { decl with value := newValue }
|
| 394 |
+
decl.pullFunDecls
|
| 395 |
+
where
|
| 396 |
+
goFVar (fvar : FVarId) : ExtendM FVarId := do
|
| 397 |
+
extendByIfNecessary fvar
|
| 398 |
+
replaceFVar fvar
|
| 399 |
+
go (code : Code) : ExtendM Code := do
|
| 400 |
+
match code with
|
| 401 |
+
| .let decl k =>
|
| 402 |
+
let decl ← decl.updateValue (← mapFVarM goFVar decl.value)
|
| 403 |
+
withNewCandidate decl.fvarId do
|
| 404 |
+
return Code.updateLet! code decl (← go k)
|
| 405 |
+
| .jp decl k =>
|
| 406 |
+
let decl ← withNewJpScope decl do
|
| 407 |
+
let value ← go decl.value
|
| 408 |
+
let additionalParams := (← get).fvarMap[decl.fvarId]!.toArray |>.map Prod.snd
|
| 409 |
+
let newType := additionalParams.foldr (init := decl.type) (fun val acc => .forallE val.binderName val.type acc .default)
|
| 410 |
+
decl.update newType (additionalParams ++ decl.params) value
|
| 411 |
+
mergeJpContextIfNecessary decl.fvarId
|
| 412 |
+
withNewCandidate decl.fvarId do
|
| 413 |
+
return Code.updateFun! code decl (← go k)
|
| 414 |
+
| .fun decl k =>
|
| 415 |
+
let decl ← withNewFunScope do
|
| 416 |
+
decl.updateValue (← go decl.value)
|
| 417 |
+
withNewCandidate decl.fvarId do
|
| 418 |
+
return Code.updateFun! code decl (← go k)
|
| 419 |
+
| .cases cs =>
|
| 420 |
+
extendByIfNecessary cs.discr
|
| 421 |
+
let discr ← replaceFVar cs.discr
|
| 422 |
+
let visitor := fun alt => do
|
| 423 |
+
withNewAltScope alt do
|
| 424 |
+
alt.mapCodeM go
|
| 425 |
+
let alts ← cs.alts.mapM visitor
|
| 426 |
+
return Code.updateCases! code cs.resultType discr alts
|
| 427 |
+
| .jmp fn args =>
|
| 428 |
+
let mut newArgs ← args.mapM (mapFVarM goFVar)
|
| 429 |
+
let additionalArgs := (← get).fvarMap[fn]!.toArray |>.map Prod.fst
|
| 430 |
+
if let some _currentJp := (← read).currentJp? then
|
| 431 |
+
let f := fun arg => do
|
| 432 |
+
return .fvar (← goFVar arg)
|
| 433 |
+
newArgs := (←additionalArgs.mapM f) ++ newArgs
|
| 434 |
+
else
|
| 435 |
+
newArgs := (additionalArgs.map .fvar) ++ newArgs
|
| 436 |
+
return Code.updateJmp! code fn newArgs
|
| 437 |
+
| .return var =>
|
| 438 |
+
extendByIfNecessary var
|
| 439 |
+
return Code.updateReturn! code (← replaceFVar var)
|
| 440 |
+
| .unreach .. => return code
|
| 441 |
+
|
| 442 |
+
end JoinPointContextExtender
|
| 443 |
+
|
| 444 |
+
namespace JoinPointCommonArgs
|
| 445 |
+
|
| 446 |
+
/--
|
| 447 |
+
Context for `ReduceAnalysisM`.
|
| 448 |
+
-/
|
| 449 |
+
structure AnalysisCtx where
|
| 450 |
+
/--
|
| 451 |
+
The variables that are in scope at the time of the definition of
|
| 452 |
+
the join point.
|
| 453 |
+
-/
|
| 454 |
+
jpScopes : FVarIdMap FVarIdSet := {}
|
| 455 |
+
|
| 456 |
+
/--
|
| 457 |
+
State for `ReduceAnalysisM`.
|
| 458 |
+
-/
|
| 459 |
+
structure AnalysisState where
|
| 460 |
+
/--
|
| 461 |
+
A map, that for each join point id contains a map from all (so far)
|
| 462 |
+
duplicated argument ids to the respective duplicate value
|
| 463 |
+
-/
|
| 464 |
+
jpJmpArgs : FVarIdMap FVarSubst := {}
|
| 465 |
+
|
| 466 |
+
abbrev ReduceAnalysisM := ReaderT AnalysisCtx StateRefT AnalysisState ScopeM
|
| 467 |
+
abbrev ReduceActionM := ReaderT AnalysisState CompilerM
|
| 468 |
+
|
| 469 |
+
def isInJpScope (jp : FVarId) (var : FVarId) : ReduceAnalysisM Bool := do
|
| 470 |
+
return (← read).jpScopes.find! jp |>.contains var
|
| 471 |
+
|
| 472 |
+
open ScopeM
|
| 473 |
+
|
| 474 |
+
/--
|
| 475 |
+
Take a look at each join point and each of their call sites. If all
|
| 476 |
+
call sites of a join point have one or more arguments in common, for example:
|
| 477 |
+
```
|
| 478 |
+
jp _jp.1 a b c => ...
|
| 479 |
+
...
|
| 480 |
+
cases foo
|
| 481 |
+
| n1 => jmp _jp.1 d e f
|
| 482 |
+
| n2 => jmp _jp.1 g e h
|
| 483 |
+
```
|
| 484 |
+
We can get rid of the common argument in favour of inlining it directly
|
| 485 |
+
into the join point (in this case the `e`). This reduces the amount of
|
| 486 |
+
arguments we have to pass around drastically for example in `ReaderT` based
|
| 487 |
+
monad stacks.
|
| 488 |
+
|
| 489 |
+
Note 1: This transformation can in certain niche cases obtain better results.
|
| 490 |
+
For example:
|
| 491 |
+
```
|
| 492 |
+
jp foo a b => ..
|
| 493 |
+
let x := ...
|
| 494 |
+
cases discr
|
| 495 |
+
| n1 => jmp foo x y
|
| 496 |
+
| n2 => jmp foo x z
|
| 497 |
+
```
|
| 498 |
+
Here we will not collapse the `x` since it is defined after the join point `foo`
|
| 499 |
+
and thus not accessible for substitution yet. We could however reorder the code in
|
| 500 |
+
such a way that this is possible, this is currently not done since we observe
|
| 501 |
+
than in praxis most of the applications of this transformation can occur naturally
|
| 502 |
+
without reordering.
|
| 503 |
+
|
| 504 |
+
Note 2: This transformation is kind of the opposite of `JoinPointContextExtender`.
|
| 505 |
+
However we still benefit from the extender because in the `simp` run after it
|
| 506 |
+
we might be able to pull join point declarations further up in the hierarchy
|
| 507 |
+
of nested functions/join points which in turn might enable additional optimizations.
|
| 508 |
+
After we have performed all of these optimizations we can take away the
|
| 509 |
+
(remaining) common arguments and end up with nicely floated and optimized
|
| 510 |
+
code that has as little arguments as possible in the join points.
|
| 511 |
+
-/
|
| 512 |
+
partial def reduce (decl : Decl) : CompilerM Decl := do
|
| 513 |
+
let (_, analysis) ← decl.value.forCodeM goAnalyze |>.run {} |>.run {} |>.run' {}
|
| 514 |
+
let newValue ← decl.value.mapCodeM goReduce |>.run analysis
|
| 515 |
+
return { decl with value := newValue }
|
| 516 |
+
where
|
| 517 |
+
goAnalyzeFunDecl (fn : FunDecl) : ReduceAnalysisM Unit := do
|
| 518 |
+
withNewScope do
|
| 519 |
+
fn.params.forM (addToScope ·.fvarId)
|
| 520 |
+
goAnalyze fn.value
|
| 521 |
+
|
| 522 |
+
goAnalyze (code : Code) : ReduceAnalysisM Unit := do
|
| 523 |
+
match code with
|
| 524 |
+
| .let decl k =>
|
| 525 |
+
addToScope decl.fvarId
|
| 526 |
+
goAnalyze k
|
| 527 |
+
| .jp decl k =>
|
| 528 |
+
goAnalyzeFunDecl decl
|
| 529 |
+
let scope ← getScope
|
| 530 |
+
withReader (fun ctx => { ctx with jpScopes := ctx.jpScopes.insert decl.fvarId scope }) do
|
| 531 |
+
addToScope decl.fvarId
|
| 532 |
+
goAnalyze k
|
| 533 |
+
| .fun decl k =>
|
| 534 |
+
goAnalyzeFunDecl decl
|
| 535 |
+
addToScope decl.fvarId
|
| 536 |
+
goAnalyze k
|
| 537 |
+
| .cases cs =>
|
| 538 |
+
let visitor alt := do
|
| 539 |
+
withNewScope do
|
| 540 |
+
alt.getParams.forM (addToScope ·.fvarId)
|
| 541 |
+
goAnalyze alt.getCode
|
| 542 |
+
cs.alts.forM visitor
|
| 543 |
+
| .jmp fn args =>
|
| 544 |
+
let decl ← getFunDecl fn
|
| 545 |
+
if let some knownArgs := (← get).jpJmpArgs.find? fn then
|
| 546 |
+
let mut newArgs := knownArgs
|
| 547 |
+
for (param, arg) in decl.params.zip args do
|
| 548 |
+
if let some knownVal := newArgs[param.fvarId]? then
|
| 549 |
+
if arg != knownVal then
|
| 550 |
+
newArgs := newArgs.erase param.fvarId
|
| 551 |
+
modify fun s => { s with jpJmpArgs := s.jpJmpArgs.insert fn newArgs }
|
| 552 |
+
else
|
| 553 |
+
let folder := fun acc (param, arg) => do
|
| 554 |
+
if (← allFVarM (isInJpScope fn) arg) then
|
| 555 |
+
return acc.insert param.fvarId arg
|
| 556 |
+
else
|
| 557 |
+
return acc
|
| 558 |
+
let interestingArgs ← decl.params.zip args |>.foldlM (init := {}) folder
|
| 559 |
+
modify fun s => { s with jpJmpArgs := s.jpJmpArgs.insert fn interestingArgs }
|
| 560 |
+
| .return .. | .unreach .. => return ()
|
| 561 |
+
|
| 562 |
+
goReduce (code : Code) : ReduceActionM Code := do
|
| 563 |
+
match code with
|
| 564 |
+
| .jp decl k =>
|
| 565 |
+
if let some reducibleArgs := (← read).jpJmpArgs.find? decl.fvarId then
|
| 566 |
+
let filter param := do
|
| 567 |
+
let erasable := reducibleArgs.contains param.fvarId
|
| 568 |
+
if erasable then
|
| 569 |
+
eraseParam param
|
| 570 |
+
return !erasable
|
| 571 |
+
let newParams ← decl.params.filterM filter
|
| 572 |
+
let mut newValue ← goReduce decl.value
|
| 573 |
+
newValue ← replaceFVars newValue reducibleArgs false
|
| 574 |
+
let newType ←
|
| 575 |
+
if newParams.size != decl.params.size then
|
| 576 |
+
mkForallParams newParams (← newValue.inferType)
|
| 577 |
+
else
|
| 578 |
+
pure decl.type
|
| 579 |
+
let k ← goReduce k
|
| 580 |
+
let decl ← decl.update newType newParams newValue
|
| 581 |
+
return Code.updateFun! code decl k
|
| 582 |
+
else
|
| 583 |
+
return Code.updateFun! code decl (← goReduce k)
|
| 584 |
+
| .jmp fn args =>
|
| 585 |
+
let reducibleArgs := (← read).jpJmpArgs.find! fn
|
| 586 |
+
let decl ← getFunDecl fn
|
| 587 |
+
let newParams := decl.params.zip args
|
| 588 |
+
|>.filter (!reducibleArgs.contains ·.fst.fvarId)
|
| 589 |
+
|>.map Prod.snd
|
| 590 |
+
return Code.updateJmp! code fn newParams
|
| 591 |
+
| .let decl k =>
|
| 592 |
+
return Code.updateLet! code decl (← goReduce k)
|
| 593 |
+
| .fun decl k =>
|
| 594 |
+
let decl ← decl.updateValue (← goReduce decl.value)
|
| 595 |
+
return Code.updateFun! code decl (← goReduce k)
|
| 596 |
+
| .cases cs =>
|
| 597 |
+
let alts ← cs.alts.mapM (·.mapCodeM goReduce)
|
| 598 |
+
return Code.updateCases! code cs.resultType cs.discr alts
|
| 599 |
+
| .return .. | .unreach .. => return code
|
| 600 |
+
|
| 601 |
+
end JoinPointCommonArgs
|
| 602 |
+
|
| 603 |
+
/--
|
| 604 |
+
Find all `fun` declarations in `decl` that qualify as join points then replace
|
| 605 |
+
their definitions and call sites with `jp`/`jmp`.
|
| 606 |
+
-/
|
| 607 |
+
def Decl.findJoinPoints (decl : Decl) : CompilerM Decl := do
|
| 608 |
+
let findResult ← JoinPointFinder.find decl
|
| 609 |
+
trace[Compiler.findJoinPoints] "Found: {findResult.candidates.size} jp candidates"
|
| 610 |
+
JoinPointFinder.replace decl findResult
|
| 611 |
+
|
| 612 |
+
def findJoinPoints : Pass :=
|
| 613 |
+
.mkPerDeclaration `findJoinPoints Decl.findJoinPoints .base
|
| 614 |
+
|
| 615 |
+
builtin_initialize
|
| 616 |
+
registerTraceClass `Compiler.findJoinPoints (inherited := true)
|
| 617 |
+
|
| 618 |
+
def Decl.extendJoinPointContext (decl : Decl) : CompilerM Decl := do
|
| 619 |
+
JoinPointContextExtender.extend decl
|
| 620 |
+
|
| 621 |
+
def extendJoinPointContext (occurrence : Nat := 0) (phase := Phase.mono) (_h : phase ≠ .base := by simp): Pass :=
|
| 622 |
+
.mkPerDeclaration `extendJoinPointContext Decl.extendJoinPointContext phase (occurrence := occurrence)
|
| 623 |
+
|
| 624 |
+
builtin_initialize
|
| 625 |
+
registerTraceClass `Compiler.extendJoinPointContext (inherited := true)
|
| 626 |
+
|
| 627 |
+
def Decl.commonJoinPointArgs (decl : Decl) : CompilerM Decl := do
|
| 628 |
+
JoinPointCommonArgs.reduce decl
|
| 629 |
+
|
| 630 |
+
def commonJoinPointArgs : Pass :=
|
| 631 |
+
.mkPerDeclaration `commonJoinPointArgs Decl.commonJoinPointArgs .mono
|
| 632 |
+
|
| 633 |
+
builtin_initialize
|
| 634 |
+
registerTraceClass `Compiler.commonJoinPointArgs (inherited := true)
|
| 635 |
+
|
| 636 |
+
end Lean.Compiler.LCNF
|
backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/LambdaLifting.lean
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/-
|
| 2 |
+
Copyright (c) 2022 Microsoft Corporation. All rights reserved.
|
| 3 |
+
Released under Apache 2.0 license as described in the file LICENSE.
|
| 4 |
+
Authors: Leonardo de Moura
|
| 5 |
+
-/
|
| 6 |
+
prelude
|
| 7 |
+
import Lean.Meta.Instances
|
| 8 |
+
import Lean.Compiler.InlineAttrs
|
| 9 |
+
import Lean.Compiler.LCNF.Closure
|
| 10 |
+
import Lean.Compiler.LCNF.Types
|
| 11 |
+
import Lean.Compiler.LCNF.MonadScope
|
| 12 |
+
import Lean.Compiler.LCNF.Internalize
|
| 13 |
+
import Lean.Compiler.LCNF.Level
|
| 14 |
+
import Lean.Compiler.LCNF.AuxDeclCache
|
| 15 |
+
|
| 16 |
+
namespace Lean.Compiler.LCNF
|
| 17 |
+
namespace LambdaLifting
|
| 18 |
+
|
| 19 |
+
/-- Context for the `LiftM` monad. -/
|
| 20 |
+
structure Context where
|
| 21 |
+
/--
|
| 22 |
+
If `liftInstParamOnly` is `true`, then only local functions that take
|
| 23 |
+
local instances as parameters are lambda lifted.
|
| 24 |
+
-/
|
| 25 |
+
liftInstParamOnly : Bool := false
|
| 26 |
+
/-- Suffix for the new auxiliary declarations being created. -/
|
| 27 |
+
suffix : Name
|
| 28 |
+
/--
|
| 29 |
+
Declaration where lambda lifting is being applied.
|
| 30 |
+
We use it to provide the "base name" for auxiliary declarations and the flag `safe`.
|
| 31 |
+
-/
|
| 32 |
+
mainDecl : Decl
|
| 33 |
+
/--
|
| 34 |
+
If true, the lambda-lifted functions inherit the inline attribute from `mainDecl`.
|
| 35 |
+
We use this feature to implement `@[inline] instance ...` and `@[always_inline] instance ...`
|
| 36 |
+
-/
|
| 37 |
+
inheritInlineAttrs := false
|
| 38 |
+
/--
|
| 39 |
+
Only local functions with `size > minSize` are lambda lifted.
|
| 40 |
+
We use this feature to implement `@[inline] instance ...` and `@[always_inline] instance ...`
|
| 41 |
+
-/
|
| 42 |
+
minSize : Nat := 0
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
/-- State for the `LiftM` monad. -/
|
| 46 |
+
structure State where
|
| 47 |
+
/--
|
| 48 |
+
New auxiliary declarations
|
| 49 |
+
-/
|
| 50 |
+
decls : Array Decl := #[]
|
| 51 |
+
/--
|
| 52 |
+
Next index for generating auxiliary declaration name.
|
| 53 |
+
-/
|
| 54 |
+
nextIdx := 0
|
| 55 |
+
|
| 56 |
+
/-- Monad for applying lambda lifting. -/
|
| 57 |
+
abbrev LiftM := ReaderT Context (StateRefT State (ScopeT CompilerM))
|
| 58 |
+
|
| 59 |
+
/--
|
| 60 |
+
Return `true` if the given declaration takes a local instance as a parameter.
|
| 61 |
+
We lambda lift this kind of local function declaration before specialization.
|
| 62 |
+
-/
|
| 63 |
+
def hasInstParam (decl : FunDecl) : CompilerM Bool :=
|
| 64 |
+
decl.params.anyM fun param => return (← isArrowClass? param.type).isSome
|
| 65 |
+
|
| 66 |
+
/--
|
| 67 |
+
Return `true` if the given declaration should be lambda lifted.
|
| 68 |
+
-/
|
| 69 |
+
def shouldLift (decl : FunDecl) : LiftM Bool := do
|
| 70 |
+
let minSize := (← read).minSize
|
| 71 |
+
if decl.value.size < minSize then
|
| 72 |
+
return false
|
| 73 |
+
else if (← read).liftInstParamOnly then
|
| 74 |
+
hasInstParam decl
|
| 75 |
+
else
|
| 76 |
+
return true
|
| 77 |
+
|
| 78 |
+
partial def mkAuxDeclName : LiftM Name := do
|
| 79 |
+
let nextIdx ← modifyGet fun s => (s.nextIdx, { s with nextIdx := s.nextIdx + 1})
|
| 80 |
+
let nameNew := (← read).mainDecl.name ++ (← read).suffix.appendIndexAfter nextIdx
|
| 81 |
+
if (← getDecl? nameNew).isNone then return nameNew
|
| 82 |
+
mkAuxDeclName
|
| 83 |
+
|
| 84 |
+
open Internalize in
|
| 85 |
+
/--
|
| 86 |
+
Create a new auxiliary declaration. The array `closure` contains all free variables
|
| 87 |
+
occurring in `decl`.
|
| 88 |
+
-/
|
| 89 |
+
def mkAuxDecl (closure : Array Param) (decl : FunDecl) : LiftM LetDecl := do
|
| 90 |
+
let nameNew ← mkAuxDeclName
|
| 91 |
+
let inlineAttr? ← if (← read).inheritInlineAttrs then pure (← read).mainDecl.inlineAttr? else pure none
|
| 92 |
+
let auxDecl ← go nameNew (← read).mainDecl.safe inlineAttr? |>.run' {}
|
| 93 |
+
let us := auxDecl.levelParams.map mkLevelParam
|
| 94 |
+
let auxDeclName ← match (← cacheAuxDecl auxDecl) with
|
| 95 |
+
| .new =>
|
| 96 |
+
auxDecl.save
|
| 97 |
+
modify fun { decls, .. } => { decls := decls.push auxDecl }
|
| 98 |
+
pure auxDecl.name
|
| 99 |
+
| .alreadyCached declName =>
|
| 100 |
+
auxDecl.erase
|
| 101 |
+
pure declName
|
| 102 |
+
let value := .const auxDeclName us (closure.map (.fvar ·.fvarId))
|
| 103 |
+
/- We reuse `decl`s `fvarId` to avoid substitution -/
|
| 104 |
+
let declNew := { fvarId := decl.fvarId, binderName := decl.binderName, type := decl.type, value }
|
| 105 |
+
modifyLCtx fun lctx => lctx.addLetDecl declNew
|
| 106 |
+
eraseFunDecl decl
|
| 107 |
+
return declNew
|
| 108 |
+
where
|
| 109 |
+
go (nameNew : Name) (safe : Bool) (inlineAttr? : Option InlineAttributeKind) : InternalizeM Decl := do
|
| 110 |
+
let params := (← closure.mapM internalizeParam) ++ (← decl.params.mapM internalizeParam)
|
| 111 |
+
let code ← internalizeCode decl.value
|
| 112 |
+
let type ← code.inferType
|
| 113 |
+
let type ← mkForallParams params type
|
| 114 |
+
let value := .code code
|
| 115 |
+
let decl := { name := nameNew, levelParams := [], params, type, value, safe, inlineAttr?, recursive := false : Decl }
|
| 116 |
+
return decl.setLevelParams
|
| 117 |
+
|
| 118 |
+
mutual
|
| 119 |
+
partial def visitFunDecl (funDecl : FunDecl) : LiftM FunDecl := do
|
| 120 |
+
let value ← withParams funDecl.params <| visitCode funDecl.value
|
| 121 |
+
funDecl.update' funDecl.type value
|
| 122 |
+
|
| 123 |
+
partial def visitCode (code : Code) : LiftM Code := do
|
| 124 |
+
match code with
|
| 125 |
+
| .let decl k =>
|
| 126 |
+
let k ← withFVar decl.fvarId <| visitCode k
|
| 127 |
+
return code.updateLet! decl k
|
| 128 |
+
| .fun decl k =>
|
| 129 |
+
let decl ← visitFunDecl decl
|
| 130 |
+
if (← shouldLift decl) then
|
| 131 |
+
let scope ← getScope
|
| 132 |
+
let (_, params, _) ← Closure.run (inScope := scope.contains) <| Closure.collectFunDecl decl
|
| 133 |
+
let declNew ← mkAuxDecl params decl
|
| 134 |
+
let k ← withFVar declNew.fvarId <| visitCode k
|
| 135 |
+
return .let declNew k
|
| 136 |
+
else
|
| 137 |
+
let k ← withFVar decl.fvarId <| visitCode k
|
| 138 |
+
return code.updateFun! decl k
|
| 139 |
+
| .jp decl k =>
|
| 140 |
+
let decl ← visitFunDecl decl
|
| 141 |
+
let k ← withFVar decl.fvarId <| visitCode k
|
| 142 |
+
return code.updateFun! decl k
|
| 143 |
+
| .cases c =>
|
| 144 |
+
let alts ← c.alts.mapMonoM fun alt =>
|
| 145 |
+
match alt with
|
| 146 |
+
| .default k => return alt.updateCode (← visitCode k)
|
| 147 |
+
| .alt _ ps k => withParams ps do return alt.updateCode (← visitCode k)
|
| 148 |
+
return code.updateAlts! alts
|
| 149 |
+
| .unreach .. | .jmp .. | .return .. => return code
|
| 150 |
+
end
|
| 151 |
+
|
| 152 |
+
def main (decl : Decl) : LiftM Decl := do
|
| 153 |
+
let value ← withParams decl.params <| decl.value.mapCodeM visitCode
|
| 154 |
+
return { decl with value }
|
| 155 |
+
|
| 156 |
+
end LambdaLifting
|
| 157 |
+
|
| 158 |
+
partial def Decl.lambdaLifting (decl : Decl) (liftInstParamOnly : Bool) (suffix : Name) (inheritInlineAttrs := false) (minSize := 0) : CompilerM (Array Decl) := do
|
| 159 |
+
let (decl, s) ← LambdaLifting.main decl |>.run { mainDecl := decl, liftInstParamOnly, suffix, inheritInlineAttrs, minSize } |>.run {} |>.run {}
|
| 160 |
+
return s.decls.push decl
|
| 161 |
+
|
| 162 |
+
/--
|
| 163 |
+
Eliminate all local function declarations.
|
| 164 |
+
-/
|
| 165 |
+
def lambdaLifting : Pass where
|
| 166 |
+
phase := .mono
|
| 167 |
+
name := `lambdaLifting
|
| 168 |
+
run := fun decls => do
|
| 169 |
+
decls.foldlM (init := #[]) fun decls decl => return decls ++ (← decl.lambdaLifting false (suffix := `_lam))
|
| 170 |
+
|
| 171 |
+
/--
|
| 172 |
+
During eager lambda lifting, we lift
|
| 173 |
+
- All local function declarations from instances (motivation: make sure it is cheap to inline them later)
|
| 174 |
+
- Local function declarations that take local instances as parameters (motivation: ensure they are specialized)
|
| 175 |
+
-/
|
| 176 |
+
def eagerLambdaLifting : Pass where
|
| 177 |
+
phase := .base
|
| 178 |
+
name := `eagerLambdaLifting
|
| 179 |
+
run := fun decls => do
|
| 180 |
+
decls.foldlM (init := #[]) fun decls decl => do
|
| 181 |
+
if decl.inlineAttr || (← Meta.isInstance decl.name) then
|
| 182 |
+
return decls.push decl
|
| 183 |
+
else
|
| 184 |
+
return decls ++ (← decl.lambdaLifting (liftInstParamOnly := true) (suffix := `_elam))
|
| 185 |
+
|
| 186 |
+
builtin_initialize
|
| 187 |
+
registerTraceClass `Compiler.eagerLambdaLifting (inherited := true)
|
| 188 |
+
registerTraceClass `Compiler.lambdaLifting (inherited := true)
|
| 189 |
+
|
| 190 |
+
end Lean.Compiler.LCNF
|
external/alphageometry/.venv-ag/Lib/site-packages/fontTools/subset/__init__.py
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
external/alphageometry/.venv-ag/Lib/site-packages/fontTools/subset/__main__.py
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import sys
|
| 2 |
+
from fontTools.subset import main
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
if __name__ == "__main__":
|
| 6 |
+
sys.exit(main())
|
external/alphageometry/.venv-ag/Lib/site-packages/fontTools/subset/cff.py
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from fontTools.misc import psCharStrings
|
| 2 |
+
from fontTools import ttLib
|
| 3 |
+
from fontTools.pens.basePen import NullPen
|
| 4 |
+
from fontTools.misc.roundTools import otRound
|
| 5 |
+
from fontTools.misc.loggingTools import deprecateFunction
|
| 6 |
+
from fontTools.subset.util import _add_method, _uniq_sort
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
class _ClosureGlyphsT2Decompiler(psCharStrings.SimpleT2Decompiler):
|
| 10 |
+
def __init__(self, components, localSubrs, globalSubrs):
|
| 11 |
+
psCharStrings.SimpleT2Decompiler.__init__(self, localSubrs, globalSubrs)
|
| 12 |
+
self.components = components
|
| 13 |
+
|
| 14 |
+
def op_endchar(self, index):
|
| 15 |
+
args = self.popall()
|
| 16 |
+
if len(args) >= 4:
|
| 17 |
+
from fontTools.encodings.StandardEncoding import StandardEncoding
|
| 18 |
+
|
| 19 |
+
# endchar can do seac accent bulding; The T2 spec says it's deprecated,
|
| 20 |
+
# but recent software that shall remain nameless does output it.
|
| 21 |
+
adx, ady, bchar, achar = args[-4:]
|
| 22 |
+
baseGlyph = StandardEncoding[bchar]
|
| 23 |
+
accentGlyph = StandardEncoding[achar]
|
| 24 |
+
self.components.add(baseGlyph)
|
| 25 |
+
self.components.add(accentGlyph)
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
@_add_method(ttLib.getTableClass("CFF "))
|
| 29 |
+
def closure_glyphs(self, s):
|
| 30 |
+
cff = self.cff
|
| 31 |
+
assert len(cff) == 1
|
| 32 |
+
font = cff[cff.keys()[0]]
|
| 33 |
+
glyphSet = font.CharStrings
|
| 34 |
+
|
| 35 |
+
decompose = s.glyphs
|
| 36 |
+
while decompose:
|
| 37 |
+
components = set()
|
| 38 |
+
for g in decompose:
|
| 39 |
+
if g not in glyphSet:
|
| 40 |
+
continue
|
| 41 |
+
gl = glyphSet[g]
|
| 42 |
+
|
| 43 |
+
subrs = getattr(gl.private, "Subrs", [])
|
| 44 |
+
decompiler = _ClosureGlyphsT2Decompiler(components, subrs, gl.globalSubrs)
|
| 45 |
+
decompiler.execute(gl)
|
| 46 |
+
components -= s.glyphs
|
| 47 |
+
s.glyphs.update(components)
|
| 48 |
+
decompose = components
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
def _empty_charstring(font, glyphName, isCFF2, ignoreWidth=False):
|
| 52 |
+
c, fdSelectIndex = font.CharStrings.getItemAndSelector(glyphName)
|
| 53 |
+
if isCFF2 or ignoreWidth:
|
| 54 |
+
# CFF2 charstrings have no widths nor 'endchar' operators
|
| 55 |
+
c.setProgram([] if isCFF2 else ["endchar"])
|
| 56 |
+
else:
|
| 57 |
+
if hasattr(font, "FDArray") and font.FDArray is not None:
|
| 58 |
+
private = font.FDArray[fdSelectIndex].Private
|
| 59 |
+
else:
|
| 60 |
+
private = font.Private
|
| 61 |
+
dfltWdX = private.defaultWidthX
|
| 62 |
+
nmnlWdX = private.nominalWidthX
|
| 63 |
+
pen = NullPen()
|
| 64 |
+
c.draw(pen) # this will set the charstring's width
|
| 65 |
+
if c.width != dfltWdX:
|
| 66 |
+
c.program = [c.width - nmnlWdX, "endchar"]
|
| 67 |
+
else:
|
| 68 |
+
c.program = ["endchar"]
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
@_add_method(ttLib.getTableClass("CFF "))
|
| 72 |
+
def prune_pre_subset(self, font, options):
|
| 73 |
+
cff = self.cff
|
| 74 |
+
# CFF table must have one font only
|
| 75 |
+
cff.fontNames = cff.fontNames[:1]
|
| 76 |
+
|
| 77 |
+
if options.notdef_glyph and not options.notdef_outline:
|
| 78 |
+
isCFF2 = cff.major > 1
|
| 79 |
+
for fontname in cff.keys():
|
| 80 |
+
font = cff[fontname]
|
| 81 |
+
_empty_charstring(font, ".notdef", isCFF2=isCFF2)
|
| 82 |
+
|
| 83 |
+
# Clear useless Encoding
|
| 84 |
+
for fontname in cff.keys():
|
| 85 |
+
font = cff[fontname]
|
| 86 |
+
# https://github.com/fonttools/fonttools/issues/620
|
| 87 |
+
font.Encoding = "StandardEncoding"
|
| 88 |
+
|
| 89 |
+
return True # bool(cff.fontNames)
|
| 90 |
+
|
| 91 |
+
|
| 92 |
+
@_add_method(ttLib.getTableClass("CFF "))
|
| 93 |
+
def subset_glyphs(self, s):
|
| 94 |
+
cff = self.cff
|
| 95 |
+
for fontname in cff.keys():
|
| 96 |
+
font = cff[fontname]
|
| 97 |
+
cs = font.CharStrings
|
| 98 |
+
|
| 99 |
+
glyphs = s.glyphs.union(s.glyphs_emptied)
|
| 100 |
+
|
| 101 |
+
# Load all glyphs
|
| 102 |
+
for g in font.charset:
|
| 103 |
+
if g not in glyphs:
|
| 104 |
+
continue
|
| 105 |
+
c, _ = cs.getItemAndSelector(g)
|
| 106 |
+
|
| 107 |
+
if cs.charStringsAreIndexed:
|
| 108 |
+
indices = [i for i, g in enumerate(font.charset) if g in glyphs]
|
| 109 |
+
csi = cs.charStringsIndex
|
| 110 |
+
csi.items = [csi.items[i] for i in indices]
|
| 111 |
+
del csi.file, csi.offsets
|
| 112 |
+
if hasattr(font, "FDSelect"):
|
| 113 |
+
sel = font.FDSelect
|
| 114 |
+
sel.format = None
|
| 115 |
+
sel.gidArray = [sel.gidArray[i] for i in indices]
|
| 116 |
+
newCharStrings = {}
|
| 117 |
+
for indicesIdx, charsetIdx in enumerate(indices):
|
| 118 |
+
g = font.charset[charsetIdx]
|
| 119 |
+
if g in cs.charStrings:
|
| 120 |
+
newCharStrings[g] = indicesIdx
|
| 121 |
+
cs.charStrings = newCharStrings
|
| 122 |
+
else:
|
| 123 |
+
cs.charStrings = {g: v for g, v in cs.charStrings.items() if g in glyphs}
|
| 124 |
+
font.charset = [g for g in font.charset if g in glyphs]
|
| 125 |
+
font.numGlyphs = len(font.charset)
|
| 126 |
+
|
| 127 |
+
if s.options.retain_gids:
|
| 128 |
+
isCFF2 = cff.major > 1
|
| 129 |
+
for g in s.glyphs_emptied:
|
| 130 |
+
_empty_charstring(font, g, isCFF2=isCFF2, ignoreWidth=True)
|
| 131 |
+
|
| 132 |
+
return True # any(cff[fontname].numGlyphs for fontname in cff.keys())
|
| 133 |
+
|
| 134 |
+
|
| 135 |
+
@_add_method(ttLib.getTableClass("CFF "))
|
| 136 |
+
def prune_post_subset(self, ttfFont, options):
|
| 137 |
+
cff = self.cff
|
| 138 |
+
for fontname in cff.keys():
|
| 139 |
+
font = cff[fontname]
|
| 140 |
+
cs = font.CharStrings
|
| 141 |
+
|
| 142 |
+
# Drop unused FontDictionaries
|
| 143 |
+
if hasattr(font, "FDSelect"):
|
| 144 |
+
sel = font.FDSelect
|
| 145 |
+
indices = _uniq_sort(sel.gidArray)
|
| 146 |
+
sel.gidArray = [indices.index(ss) for ss in sel.gidArray]
|
| 147 |
+
arr = font.FDArray
|
| 148 |
+
arr.items = [arr[i] for i in indices]
|
| 149 |
+
del arr.file, arr.offsets
|
| 150 |
+
|
| 151 |
+
# Desubroutinize if asked for
|
| 152 |
+
if options.desubroutinize:
|
| 153 |
+
cff.desubroutinize()
|
| 154 |
+
|
| 155 |
+
# Drop hints if not needed
|
| 156 |
+
if not options.hinting:
|
| 157 |
+
self.remove_hints()
|
| 158 |
+
elif not options.desubroutinize:
|
| 159 |
+
self.remove_unused_subroutines()
|
| 160 |
+
return True
|
| 161 |
+
|
| 162 |
+
|
| 163 |
+
@deprecateFunction(
|
| 164 |
+
"use 'CFFFontSet.desubroutinize()' instead", category=DeprecationWarning
|
| 165 |
+
)
|
| 166 |
+
@_add_method(ttLib.getTableClass("CFF "))
|
| 167 |
+
def desubroutinize(self):
|
| 168 |
+
self.cff.desubroutinize()
|
| 169 |
+
|
| 170 |
+
|
| 171 |
+
@deprecateFunction(
|
| 172 |
+
"use 'CFFFontSet.remove_hints()' instead", category=DeprecationWarning
|
| 173 |
+
)
|
| 174 |
+
@_add_method(ttLib.getTableClass("CFF "))
|
| 175 |
+
def remove_hints(self):
|
| 176 |
+
self.cff.remove_hints()
|
| 177 |
+
|
| 178 |
+
|
| 179 |
+
@deprecateFunction(
|
| 180 |
+
"use 'CFFFontSet.remove_unused_subroutines' instead", category=DeprecationWarning
|
| 181 |
+
)
|
| 182 |
+
@_add_method(ttLib.getTableClass("CFF "))
|
| 183 |
+
def remove_unused_subroutines(self):
|
| 184 |
+
self.cff.remove_unused_subroutines()
|
external/alphageometry/.venv-ag/Lib/site-packages/fontTools/subset/svg.py
ADDED
|
@@ -0,0 +1,253 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import re
|
| 4 |
+
from functools import lru_cache
|
| 5 |
+
from itertools import chain, count
|
| 6 |
+
from typing import Dict, Iterable, Iterator, List, Optional, Set, Tuple
|
| 7 |
+
|
| 8 |
+
try:
|
| 9 |
+
from lxml import etree
|
| 10 |
+
except ImportError:
|
| 11 |
+
# lxml is required for subsetting SVG, but we prefer to delay the import error
|
| 12 |
+
# until subset_glyphs() is called (i.e. if font to subset has an 'SVG ' table)
|
| 13 |
+
etree = None
|
| 14 |
+
|
| 15 |
+
from fontTools import ttLib
|
| 16 |
+
from fontTools.subset.util import _add_method
|
| 17 |
+
from fontTools.ttLib.tables.S_V_G_ import SVGDocument
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
__all__ = ["subset_glyphs"]
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
GID_RE = re.compile(r"^glyph(\d+)$")
|
| 24 |
+
|
| 25 |
+
NAMESPACES = {
|
| 26 |
+
"svg": "http://www.w3.org/2000/svg",
|
| 27 |
+
"xlink": "http://www.w3.org/1999/xlink",
|
| 28 |
+
}
|
| 29 |
+
XLINK_HREF = f'{{{NAMESPACES["xlink"]}}}href'
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
# TODO(antrotype): Replace with functools.cache once we are 3.9+
|
| 33 |
+
@lru_cache(maxsize=None)
|
| 34 |
+
def xpath(path):
|
| 35 |
+
# compile XPath upfront, caching result to reuse on multiple elements
|
| 36 |
+
return etree.XPath(path, namespaces=NAMESPACES)
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
def group_elements_by_id(tree: etree.Element) -> Dict[str, etree.Element]:
|
| 40 |
+
# select all svg elements with 'id' attribute no matter where they are
|
| 41 |
+
# including the root element itself:
|
| 42 |
+
# https://github.com/fonttools/fonttools/issues/2548
|
| 43 |
+
return {el.attrib["id"]: el for el in xpath("//svg:*[@id]")(tree)}
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def parse_css_declarations(style_attr: str) -> Dict[str, str]:
|
| 47 |
+
# https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/style
|
| 48 |
+
# https://developer.mozilla.org/en-US/docs/Web/CSS/Syntax#css_declarations
|
| 49 |
+
result = {}
|
| 50 |
+
for declaration in style_attr.split(";"):
|
| 51 |
+
if declaration.count(":") == 1:
|
| 52 |
+
property_name, value = declaration.split(":")
|
| 53 |
+
property_name = property_name.strip()
|
| 54 |
+
result[property_name] = value.strip()
|
| 55 |
+
elif declaration.strip():
|
| 56 |
+
raise ValueError(f"Invalid CSS declaration syntax: {declaration}")
|
| 57 |
+
return result
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
def iter_referenced_ids(tree: etree.Element) -> Iterator[str]:
|
| 61 |
+
# Yield all the ids that can be reached via references from this element tree.
|
| 62 |
+
# We currently support xlink:href (as used by <use> and gradient templates),
|
| 63 |
+
# and local url(#...) links found in fill or clip-path attributes
|
| 64 |
+
# TODO(anthrotype): Check we aren't missing other supported kinds of reference
|
| 65 |
+
find_svg_elements_with_references = xpath(
|
| 66 |
+
".//svg:*[ "
|
| 67 |
+
"starts-with(@xlink:href, '#') "
|
| 68 |
+
"or starts-with(@fill, 'url(#') "
|
| 69 |
+
"or starts-with(@clip-path, 'url(#') "
|
| 70 |
+
"or contains(@style, ':url(#') "
|
| 71 |
+
"]",
|
| 72 |
+
)
|
| 73 |
+
for el in chain([tree], find_svg_elements_with_references(tree)):
|
| 74 |
+
ref_id = href_local_target(el)
|
| 75 |
+
if ref_id is not None:
|
| 76 |
+
yield ref_id
|
| 77 |
+
|
| 78 |
+
attrs = el.attrib
|
| 79 |
+
if "style" in attrs:
|
| 80 |
+
attrs = {**dict(attrs), **parse_css_declarations(el.attrib["style"])}
|
| 81 |
+
for attr in ("fill", "clip-path"):
|
| 82 |
+
if attr in attrs:
|
| 83 |
+
value = attrs[attr]
|
| 84 |
+
if value.startswith("url(#") and value.endswith(")"):
|
| 85 |
+
ref_id = value[5:-1]
|
| 86 |
+
assert ref_id
|
| 87 |
+
yield ref_id
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
def closure_element_ids(
|
| 91 |
+
elements: Dict[str, etree.Element], element_ids: Set[str]
|
| 92 |
+
) -> None:
|
| 93 |
+
# Expand the initial subset of element ids to include ids that can be reached
|
| 94 |
+
# via references from the initial set.
|
| 95 |
+
unvisited = element_ids
|
| 96 |
+
while unvisited:
|
| 97 |
+
referenced: Set[str] = set()
|
| 98 |
+
for el_id in unvisited:
|
| 99 |
+
if el_id not in elements:
|
| 100 |
+
# ignore dangling reference; not our job to validate svg
|
| 101 |
+
continue
|
| 102 |
+
referenced.update(iter_referenced_ids(elements[el_id]))
|
| 103 |
+
referenced -= element_ids
|
| 104 |
+
element_ids.update(referenced)
|
| 105 |
+
unvisited = referenced
|
| 106 |
+
|
| 107 |
+
|
| 108 |
+
def subset_elements(el: etree.Element, retained_ids: Set[str]) -> bool:
|
| 109 |
+
# Keep elements if their id is in the subset, or any of their children's id is.
|
| 110 |
+
# Drop elements whose id is not in the subset, and either have no children,
|
| 111 |
+
# or all their children are being dropped.
|
| 112 |
+
if el.attrib.get("id") in retained_ids:
|
| 113 |
+
# if id is in the set, don't recurse; keep whole subtree
|
| 114 |
+
return True
|
| 115 |
+
# recursively subset all the children; we use a list comprehension instead
|
| 116 |
+
# of a parentheses-less generator expression because we don't want any() to
|
| 117 |
+
# short-circuit, as our function has a side effect of dropping empty elements.
|
| 118 |
+
if any([subset_elements(e, retained_ids) for e in el]):
|
| 119 |
+
return True
|
| 120 |
+
assert len(el) == 0
|
| 121 |
+
parent = el.getparent()
|
| 122 |
+
if parent is not None:
|
| 123 |
+
parent.remove(el)
|
| 124 |
+
return False
|
| 125 |
+
|
| 126 |
+
|
| 127 |
+
def remap_glyph_ids(
|
| 128 |
+
svg: etree.Element, glyph_index_map: Dict[int, int]
|
| 129 |
+
) -> Dict[str, str]:
|
| 130 |
+
# Given {old_gid: new_gid} map, rename all elements containing id="glyph{gid}"
|
| 131 |
+
# special attributes
|
| 132 |
+
elements = group_elements_by_id(svg)
|
| 133 |
+
id_map = {}
|
| 134 |
+
for el_id, el in elements.items():
|
| 135 |
+
m = GID_RE.match(el_id)
|
| 136 |
+
if not m:
|
| 137 |
+
continue
|
| 138 |
+
old_index = int(m.group(1))
|
| 139 |
+
new_index = glyph_index_map.get(old_index)
|
| 140 |
+
if new_index is not None:
|
| 141 |
+
if old_index == new_index:
|
| 142 |
+
continue
|
| 143 |
+
new_id = f"glyph{new_index}"
|
| 144 |
+
else:
|
| 145 |
+
# If the old index is missing, the element correspond to a glyph that was
|
| 146 |
+
# excluded from the font's subset.
|
| 147 |
+
# We rename it to avoid clashes with the new GIDs or other element ids.
|
| 148 |
+
new_id = f".{el_id}"
|
| 149 |
+
n = count(1)
|
| 150 |
+
while new_id in elements:
|
| 151 |
+
new_id = f"{new_id}.{next(n)}"
|
| 152 |
+
|
| 153 |
+
id_map[el_id] = new_id
|
| 154 |
+
el.attrib["id"] = new_id
|
| 155 |
+
|
| 156 |
+
return id_map
|
| 157 |
+
|
| 158 |
+
|
| 159 |
+
def href_local_target(el: etree.Element) -> Optional[str]:
|
| 160 |
+
if XLINK_HREF in el.attrib:
|
| 161 |
+
href = el.attrib[XLINK_HREF]
|
| 162 |
+
if href.startswith("#") and len(href) > 1:
|
| 163 |
+
return href[1:] # drop the leading #
|
| 164 |
+
return None
|
| 165 |
+
|
| 166 |
+
|
| 167 |
+
def update_glyph_href_links(svg: etree.Element, id_map: Dict[str, str]) -> None:
|
| 168 |
+
# update all xlink:href="#glyph..." attributes to point to the new glyph ids
|
| 169 |
+
for el in xpath(".//svg:*[starts-with(@xlink:href, '#glyph')]")(svg):
|
| 170 |
+
old_id = href_local_target(el)
|
| 171 |
+
assert old_id is not None
|
| 172 |
+
if old_id in id_map:
|
| 173 |
+
new_id = id_map[old_id]
|
| 174 |
+
el.attrib[XLINK_HREF] = f"#{new_id}"
|
| 175 |
+
|
| 176 |
+
|
| 177 |
+
def ranges(ints: Iterable[int]) -> Iterator[Tuple[int, int]]:
|
| 178 |
+
# Yield sorted, non-overlapping (min, max) ranges of consecutive integers
|
| 179 |
+
sorted_ints = iter(sorted(set(ints)))
|
| 180 |
+
try:
|
| 181 |
+
start = end = next(sorted_ints)
|
| 182 |
+
except StopIteration:
|
| 183 |
+
return
|
| 184 |
+
for v in sorted_ints:
|
| 185 |
+
if v - 1 == end:
|
| 186 |
+
end = v
|
| 187 |
+
else:
|
| 188 |
+
yield (start, end)
|
| 189 |
+
start = end = v
|
| 190 |
+
yield (start, end)
|
| 191 |
+
|
| 192 |
+
|
| 193 |
+
@_add_method(ttLib.getTableClass("SVG "))
|
| 194 |
+
def subset_glyphs(self, s) -> bool:
|
| 195 |
+
if etree is None:
|
| 196 |
+
raise ImportError("No module named 'lxml', required to subset SVG")
|
| 197 |
+
|
| 198 |
+
# glyph names (before subsetting)
|
| 199 |
+
glyph_order: List[str] = s.orig_glyph_order
|
| 200 |
+
# map from glyph names to original glyph indices
|
| 201 |
+
rev_orig_glyph_map: Dict[str, int] = s.reverseOrigGlyphMap
|
| 202 |
+
# map from original to new glyph indices (after subsetting)
|
| 203 |
+
glyph_index_map: Dict[int, int] = s.glyph_index_map
|
| 204 |
+
|
| 205 |
+
new_docs: List[SVGDocument] = []
|
| 206 |
+
for doc in self.docList:
|
| 207 |
+
glyphs = {
|
| 208 |
+
glyph_order[i] for i in range(doc.startGlyphID, doc.endGlyphID + 1)
|
| 209 |
+
}.intersection(s.glyphs)
|
| 210 |
+
if not glyphs:
|
| 211 |
+
# no intersection: we can drop the whole record
|
| 212 |
+
continue
|
| 213 |
+
|
| 214 |
+
svg = etree.fromstring(
|
| 215 |
+
# encode because fromstring dislikes xml encoding decl if input is str.
|
| 216 |
+
# SVG xml encoding must be utf-8 as per OT spec.
|
| 217 |
+
doc.data.encode("utf-8"),
|
| 218 |
+
parser=etree.XMLParser(
|
| 219 |
+
# Disable libxml2 security restrictions to support very deep trees.
|
| 220 |
+
# Without this we would get an error like this:
|
| 221 |
+
# `lxml.etree.XMLSyntaxError: internal error: Huge input lookup`
|
| 222 |
+
# when parsing big fonts e.g. noto-emoji-picosvg.ttf.
|
| 223 |
+
huge_tree=True,
|
| 224 |
+
# ignore blank text as it's not meaningful in OT-SVG; it also prevents
|
| 225 |
+
# dangling tail text after removing an element when pretty_print=True
|
| 226 |
+
remove_blank_text=True,
|
| 227 |
+
# don't replace entities; we don't expect any in OT-SVG and they may
|
| 228 |
+
# be abused for XXE attacks
|
| 229 |
+
resolve_entities=False,
|
| 230 |
+
),
|
| 231 |
+
)
|
| 232 |
+
|
| 233 |
+
elements = group_elements_by_id(svg)
|
| 234 |
+
gids = {rev_orig_glyph_map[g] for g in glyphs}
|
| 235 |
+
element_ids = {f"glyph{i}" for i in gids}
|
| 236 |
+
closure_element_ids(elements, element_ids)
|
| 237 |
+
|
| 238 |
+
if not subset_elements(svg, element_ids):
|
| 239 |
+
continue
|
| 240 |
+
|
| 241 |
+
if not s.options.retain_gids:
|
| 242 |
+
id_map = remap_glyph_ids(svg, glyph_index_map)
|
| 243 |
+
update_glyph_href_links(svg, id_map)
|
| 244 |
+
|
| 245 |
+
new_doc = etree.tostring(svg, pretty_print=s.options.pretty_svg).decode("utf-8")
|
| 246 |
+
|
| 247 |
+
new_gids = (glyph_index_map[i] for i in gids)
|
| 248 |
+
for start, end in ranges(new_gids):
|
| 249 |
+
new_docs.append(SVGDocument(new_doc, start, end, doc.compressed))
|
| 250 |
+
|
| 251 |
+
self.docList = new_docs
|
| 252 |
+
|
| 253 |
+
return bool(self.docList)
|