ZAIDX11 commited on
Commit
c8bbc42
·
verified ·
1 Parent(s): 542fab4

Add files using upload-large-folder tool

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +2 -0
  2. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/Boxing.lean +346 -0
  3. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/Checker.lean +192 -0
  4. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/CompilerM.lean +239 -0
  5. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/ElimDeadBranches.lean +351 -0
  6. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/ElimDeadVars.lean +48 -0
  7. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/EmitC.lean +775 -0
  8. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/EmitLLVM.lean +1657 -0
  9. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/EmitUtil.lean +86 -0
  10. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/ExpandResetReuse.lean +281 -0
  11. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/Format.lean +143 -0
  12. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/FreeVars.lean +233 -0
  13. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/LLVMBindings.lean +470 -0
  14. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/LiveVars.lean +170 -0
  15. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/NormIds.lean +177 -0
  16. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/PushProj.lean +57 -0
  17. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/RC.lean +285 -0
  18. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/ResetReuse.lean +246 -0
  19. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/SimpCase.lean +78 -0
  20. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/Sorry.lean +80 -0
  21. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/ToIR.lean +350 -0
  22. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/ToIRType.lean +126 -0
  23. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/IR/UnboxResult.lean +30 -0
  24. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/AlphaEqv.lean +137 -0
  25. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/AuxDeclCache.lean +29 -0
  26. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/BaseTypes.lean +29 -0
  27. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/Basic.lean +755 -0
  28. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/Bind.lean +132 -0
  29. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/CSE.lean +128 -0
  30. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/Check.lean +307 -0
  31. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/Closure.lean +172 -0
  32. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/CompatibleTypes.lean +146 -0
  33. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/CompilerM.lean +504 -0
  34. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/ConfigOptions.lean +67 -0
  35. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/DeclHash.lean +44 -0
  36. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/DependsOn.lean +62 -0
  37. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/ElimDead.lean +100 -0
  38. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/ElimDeadBranches.lean +630 -0
  39. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/ExtractClosed.lean +161 -0
  40. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/FVarUtil.lean +213 -0
  41. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/FixedParams.lean +181 -0
  42. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/FloatLetIn.lean +310 -0
  43. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/InferType.lean +348 -0
  44. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/Internalize.lean +131 -0
  45. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/JoinPoints.lean +636 -0
  46. backend/core/leanprover--lean4---v4.22.0/src/lean/Lean/Compiler/LCNF/LambdaLifting.lean +190 -0
  47. external/alphageometry/.venv-ag/Lib/site-packages/fontTools/subset/__init__.py +0 -0
  48. external/alphageometry/.venv-ag/Lib/site-packages/fontTools/subset/__main__.py +6 -0
  49. external/alphageometry/.venv-ag/Lib/site-packages/fontTools/subset/cff.py +184 -0
  50. 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)