File: .lake/packages/lean4/src/lean/Lean/Compiler/LCNF/JoinPoints.lean

full_name: Lean.Compiler.LCNF.JoinPointFinder.CandidateInfo.arity
code: /--
Info about a join point candidate (a `fun` declaration) during the find phase.
-/
structure CandidateInfo where
  /--
  The arity of the candidate
  -/
  arity : Nat
  /--
  The set of candidates that rely on this candidate to be a join point.
  For a more detailed explanation see the documentation of `find`
  -/
  associated : HashSet FVarId
  deriving Inhabited
goal: Nat

full_name: Lean.Compiler.LCNF.JoinPointFinder.CandidateInfo
code: /--
Info about a join point candidate (a `fun` declaration) during the find phase.
-/
structure CandidateInfo where
  /--
  The arity of the candidate
  -/
  arity : Nat
  /--
  The set of candidates that rely on this candidate to be a join point.
  For a more detailed explanation see the documentation of `find`
  -/
  associated : HashSet FVarId
  deriving Inhabited
goal: Type

full_name: Lean.Compiler.LCNF.JoinPointFinder.CandidateInfo.associated
code: /--
Info about a join point candidate (a `fun` declaration) during the find phase.
-/
structure CandidateInfo where
  /--
  The arity of the candidate
  -/
  arity : Nat
  /--
  The set of candidates that rely on this candidate to be a join point.
  For a more detailed explanation see the documentation of `find`
  -/
  associated : HashSet FVarId
  deriving Inhabited
goal: Lean.HashSet Lean.FVarId

full_name: Lean.Compiler.LCNF.JoinPointFinder.instInhabitedCandidateInfo
code: /--
Info about a join point candidate (a `fun` declaration) during the find phase.
-/
structure CandidateInfo where
  /--
  The arity of the candidate
  -/
  arity : Nat
  /--
  The set of candidates that rely on this candidate to be a join point.
  For a more detailed explanation see the documentation of `find`
  -/
  associated : HashSet FVarId
  deriving Inhabited
goal: Inhabited Lean.Compiler.LCNF.JoinPointFinder.CandidateInfo

full_name: Lean.Compiler.LCNF.JoinPointFinder.FindState.scope
code: /--
The state for the join point candidate finder.
-/
structure FindState where
  /--
  All current join point candidates accessible by their `FVarId`.
  -/
  candidates : HashMap FVarId CandidateInfo := .empty
  /--
  The `FVarId`s of all `fun` declarations that were declared within the
  current `fun`.
  -/
  scope : HashSet FVarId := .empty
goal: Lean.HashSet Lean.FVarId

full_name: Lean.Compiler.LCNF.JoinPointFinder.FindState.candidates
code: /--
The state for the join point candidate finder.
-/
structure FindState where
  /--
  All current join point candidates accessible by their `FVarId`.
  -/
  candidates : HashMap FVarId CandidateInfo := .empty
  /--
  The `FVarId`s of all `fun` declarations that were declared within the
  current `fun`.
  -/
  scope : HashSet FVarId := .empty
goal: Lean.HashMap Lean.FVarId Lean.Compiler.LCNF.JoinPointFinder.CandidateInfo

full_name: Lean.Compiler.LCNF.JoinPointFinder.FindState
code: /--
The state for the join point candidate finder.
-/
structure FindState where
  /--
  All current join point candidates accessible by their `FVarId`.
  -/
  candidates : HashMap FVarId CandidateInfo := .empty
  /--
  The `FVarId`s of all `fun` declarations that were declared within the
  current `fun`.
  -/
  scope : HashSet FVarId := .empty
goal: Type

full_name: Lean.Compiler.LCNF.JoinPointFinder.ReplaceCtx
code: abbrev ReplaceCtx 
goal: Type

full_name: Lean.Compiler.LCNF.JoinPointFinder.FindM
code: abbrev FindM 
goal: Type

full_name: Lean.Compiler.LCNF.JoinPointFinder.ReplaceM
code: abbrev ReplaceM 
goal: Type

full_name: _private.«.lake».packages.lean4.src.lean.Lean.Compiler.LCNF.JoinPoints.0.Lean.Compiler.LCNF.JoinPointFinder.findCandidate?
code: /--
Attempt to find a join point candidate by its `FVarId`.
-/
private def findCandidate? (fvarId : FVarId) : FindM (Option CandidateInfo) 
goal: Lean.Compiler.LCNF.JoinPointFinder.FindM (Option Lean.Compiler.LCNF.JoinPointFinder.CandidateInfo)

full_name: _private.«.lake».packages.lean4.src.lean.Lean.Compiler.LCNF.JoinPoints.0.Lean.Compiler.LCNF.JoinPointFinder.eraseCandidate
code: /--
Erase a join point candidate as well as all the ones that depend on it
by its `FVarId`, no error is thrown is the candidate does not exist.
-/
private partial def eraseCandidate (fvarId : FVarId) : FindM Unit 
goal: Lean.Compiler.LCNF.JoinPointFinder.FindM Unit

full_name: _private.«.lake».packages.lean4.src.lean.Lean.Compiler.LCNF.JoinPoints.0.Lean.Compiler.LCNF.JoinPointFinder.modifyCandidates
code: /--
Combinator for modifying the candidates in `FindM`.
-/
private def modifyCandidates (f : HashMap FVarId CandidateInfo → HashMap FVarId CandidateInfo) : FindM Unit 
goal: Lean.Compiler.LCNF.JoinPointFinder.FindM Unit

full_name: _private.«.lake».packages.lean4.src.lean.Lean.Compiler.LCNF.JoinPoints.0.Lean.Compiler.LCNF.JoinPointFinder.removeCandidatesInArg
code: /--
Remove all join point candidates contained in `a`.
-/
private partial def removeCandidatesInArg (a : Arg) : FindM Unit 
goal: Lean.Compiler.LCNF.JoinPointFinder.FindM Unit

full_name: _private.«.lake».packages.lean4.src.lean.Lean.Compiler.LCNF.JoinPoints.0.Lean.Compiler.LCNF.JoinPointFinder.removeCandidatesInLetValue
code: /--
Remove all join point candidates contained in `a`.
-/
private partial def removeCandidatesInLetValue (e : LetValue) : FindM Unit 
goal: Lean.Compiler.LCNF.JoinPointFinder.FindM Unit

full_name: _private.«.lake».packages.lean4.src.lean.Lean.Compiler.LCNF.JoinPoints.0.Lean.Compiler.LCNF.JoinPointFinder.addCandidate
code: /--
Add a new join point candidate to the state.
-/
private def addCandidate (fvarId : FVarId) (arity : Nat) : FindM Unit 
goal: Lean.Compiler.LCNF.JoinPointFinder.FindM Unit

full_name: _private.«.lake».packages.lean4.src.lean.Lean.Compiler.LCNF.JoinPoints.0.Lean.Compiler.LCNF.JoinPointFinder.addDependency
code: /--
Add a new join point dependency from `src` to `dst`.
-/
private def addDependency (src : FVarId) (target : FVarId) : FindM Unit 
goal: Lean.Compiler.LCNF.JoinPointFinder.FindM Unit

full_name: Lean.Compiler.LCNF.JoinPointFinder.find
code: /--
Find all `fun` declarations that qualify as a join point, that is:
- are always fully applied
- are always called in tail position

Where a `fun` `f` is in tail position iff it is called as follows:
```
let res := f arg
res
```
The majority (if not all) tail calls will be brought into this form
by the simplifier pass.

Furthermore a `fun` disqualifies as a join point if turning it into a join
point would turn a call to it into an out of scope join point.
This can happen if we have something like:
```
def test (b : Bool) (x y : Nat) : Nat :=
  fun myjp x => Nat.add x (Nat.add x x)
  fun f y =>
    let x := Nat.add y y
    myjp x
  fun f y =>
    let x := Nat.mul y y
    myjp x
  cases b (f x) (g y)
```
`f` and `g` can be detected as a join point right away, however
`myjp` can only ever be detected as a join point after we have established
this. This is because otherwise the calls to `myjp` in `f` and `g` would
produce out of scope join point jumps.
-/
partial def find (decl : Decl) : CompilerM FindState 
goal: Lean.Compiler.LCNF.CompilerM Lean.Compiler.LCNF.JoinPointFinder.FindState

full_name: Lean.Compiler.LCNF.JoinPointFinder.find.go
code: /--
Find all `fun` declarations that qualify as a join point, that is:
- are always fully applied
- are always called in tail position

Where a `fun` `f` is in tail position iff it is called as follows:
```
let res := f arg
res
```
The majority (if not all) tail calls will be brought into this form
by the simplifier pass.

Furthermore a `fun` disqualifies as a join point if turning it into a join
point would turn a call to it into an out of scope join point.
This can happen if we have something like:
```
def test (b : Bool) (x y : Nat) : Nat :=
  fun myjp x => Nat.add x (Nat.add x x)
  fun f y =>
    let x := Nat.add y y
    myjp x
  fun f y =>
    let x := Nat.mul y y
    myjp x
  cases b (f x) (g y)
```
`f` and `g` can be detected as a join point right away, however
`myjp` can only ever be detected as a join point after we have established
this. This is because otherwise the calls to `myjp` in `f` and `g` would
produce out of scope join point jumps.
-/
partial def find (decl : Decl) : CompilerM FindState 
goal: Lean.Compiler.LCNF.Code → Lean.Compiler.LCNF.JoinPointFinder.FindM Unit

full_name: Lean.Compiler.LCNF.JoinPointFinder.replace.go
code: /--
Replace all join point candidate `fun` declarations with `jp` ones
and all calls to them with `jmp`s.
-/
partial def replace (decl : Decl) (state : FindState) : CompilerM Decl 
goal: Lean.Compiler.LCNF.JoinPointFinder.ReplaceM Lean.Compiler.LCNF.Code

full_name: Lean.Compiler.LCNF.JoinPointFinder.replace
code: /--
Replace all join point candidate `fun` declarations with `jp` ones
and all calls to them with `jmp`s.
-/
partial def replace (decl : Decl) (state : FindState) : CompilerM Decl 
goal: Lean.Compiler.LCNF.CompilerM Lean.Compiler.LCNF.Decl

full_name: Lean.Compiler.LCNF.JoinPointContextExtender.ExtendContext.currentJp?
code: /--
The context managed by `ExtendM`.
-/
structure ExtendContext where
  /--
  The `FVarId` of the current join point if we are currently inside one.
  -/
  currentJp? : Option FVarId := none
  /--
  The list of valid candidates for extending the context. This will be
  all `let` and `fun` declarations as well as all `jp` parameters up
  until the last `fun` declaration in the tree.
  -/
  candidates : FVarIdSet := {}
goal: Option Lean.FVarId

full_name: Lean.Compiler.LCNF.JoinPointContextExtender.ExtendContext.candidates
code: /--
The context managed by `ExtendM`.
-/
structure ExtendContext where
  /--
  The `FVarId` of the current join point if we are currently inside one.
  -/
  currentJp? : Option FVarId := none
  /--
  The list of valid candidates for extending the context. This will be
  all `let` and `fun` declarations as well as all `jp` parameters up
  until the last `fun` declaration in the tree.
  -/
  candidates : FVarIdSet := {}
goal: Lean.FVarIdSet

full_name: Lean.Compiler.LCNF.JoinPointContextExtender.ExtendContext
code: /--
The context managed by `ExtendM`.
-/
structure ExtendContext where
  /--
  The `FVarId` of the current join point if we are currently inside one.
  -/
  currentJp? : Option FVarId := none
  /--
  The list of valid candidates for extending the context. This will be
  all `let` and `fun` declarations as well as all `jp` parameters up
  until the last `fun` declaration in the tree.
  -/
  candidates : FVarIdSet := {}
goal: Type

full_name: Lean.Compiler.LCNF.JoinPointContextExtender.ExtendState.fvarMap
code: /--
The state managed by `ExtendM`.
-/
structure ExtendState where
  /--
  A map from join point `FVarId`s to a respective map from free variables
  to `Param`s. The free variables in this map are the once that the context
  of said join point will be extended by by passing in the respective parameter.
  -/
  fvarMap : HashMap FVarId (HashMap FVarId Param) := {}
goal: Lean.HashMap Lean.FVarId (Lean.HashMap Lean.FVarId Lean.Compiler.LCNF.Param)

full_name: Lean.Compiler.LCNF.JoinPointContextExtender.ExtendState
code: /--
The state managed by `ExtendM`.
-/
structure ExtendState where
  /--
  A map from join point `FVarId`s to a respective map from free variables
  to `Param`s. The free variables in this map are the once that the context
  of said join point will be extended by by passing in the respective parameter.
  -/
  fvarMap : HashMap FVarId (HashMap FVarId Param) := {}
goal: Type

full_name: Lean.Compiler.LCNF.JoinPointContextExtender.ExtendM
code: /--
The monad for the `extendJoinPointContext` pass.
-/
abbrev ExtendM 
goal: Type

full_name: Lean.Compiler.LCNF.JoinPointContextExtender.replaceFVar
code: /--
Replace a free variable if necessary, that is:
- It is in the list of candidates
- We are currently within a join point (if we are within a function there
  cannot be a need to replace them since we dont extend their context)
- Said join point actually has a replacement parameter registered.
otherwise just return `fvar`.
-/
def replaceFVar (fvar : FVarId) : ExtendM FVarId 
goal: Lean.Compiler.LCNF.JoinPointContextExtender.ExtendM Lean.FVarId

full_name: Lean.Compiler.LCNF.JoinPointContextExtender.withNewCandidate
code: /--
Add a new candidate to the current scope + to the list of candidates
if we are currently within a join point. Then execute `x`.
-/
def withNewCandidate (fvar : FVarId) (x : ExtendM α) : ExtendM α 
goal: Lean.Compiler.LCNF.JoinPointContextExtender.ExtendM α

full_name: Lean.Compiler.LCNF.JoinPointContextExtender.withNewCandidates
code: /--
Same as `withNewCandidate` but with multiple `FVarId`s.
-/
def withNewCandidates (fvars : Array FVarId) (x : ExtendM α) : ExtendM α 
goal: Lean.Compiler.LCNF.JoinPointContextExtender.ExtendM α

full_name: Lean.Compiler.LCNF.JoinPointContextExtender.extendByIfNecessary
code: /--
Extend the context of the current join point (if we are within one)
by `fvar` if necessary.
This is necessary if:
- `fvar` is not in scope (that is, was declared outside of the current jp)
- we have not already extended the context by `fvar`
- the list of candidates contains `fvar`. This is because if we have something
  like:
  ```
  let x := ..
  fun f a =>
    jp j b =>
      let y := x
      y
  ```
  There is no point in extending the context of `j` by `x` because we
  cannot lift a join point outside of a local function declaration.
-/
def extendByIfNecessary (fvar : FVarId) : ExtendM Unit 
goal: Lean.Compiler.LCNF.JoinPointContextExtender.ExtendM Unit

full_name: Lean.Compiler.LCNF.JoinPointContextExtender.mergeJpContextIfNecessary
code: /--
Merge the extended context of two join points if necessary. That is
if we have a structure such as:
```
jp j.1 ... =>
  jp j.2 .. =>
    ...
  ...
```
And we are just done visiting `j.2` we want to extend the context of
`j.1` by all free variables that the context of `j.2` was extended by
as well because we need to drag these variables through at the call sites
of `j.2` in `j.1`.
-/
def mergeJpContextIfNecessary (jp : FVarId) : ExtendM Unit 
goal: Lean.Compiler.LCNF.JoinPointContextExtender.ExtendM Unit

full_name: Lean.Compiler.LCNF.JoinPointContextExtender.withNewFunScope
code: /--
We call this whenever we enter a new local function. It clears both the
current join point and the list of candidates since we can't lift join
points outside of functions as explained in `mergeJpContextIfNecessary`.
-/
def withNewFunScope (x : ExtendM α): ExtendM α 
goal: Lean.Compiler.LCNF.JoinPointContextExtender.ExtendM α

full_name: Lean.Compiler.LCNF.JoinPointContextExtender.withNewJpScope
code: /--
We call this whenever we enter a new join point. It will set the current
join point and extend the list of candidates by all of the parameters of
the join point. This is so in the case of nested join points that refer
to parameters of the current one we extend the context of the nested
join points by said parameters.
-/
def withNewJpScope (decl : FunDecl) (x : ExtendM α): ExtendM α 
goal: Lean.Compiler.LCNF.JoinPointContextExtender.ExtendM α

full_name: Lean.Compiler.LCNF.JoinPointContextExtender.withNewAltScope
code: /--
We call this whenever we visit a new arm of a cases statement.
It will back up the current scope (since we are doing a case split
and want to continue with other arms afterwards) and add all of the
parameters of the match arm to the list of candidates.
-/
def withNewAltScope (alt : Alt) (x : ExtendM α) : ExtendM α 
goal: Lean.Compiler.LCNF.JoinPointContextExtender.ExtendM α

full_name: Lean.Compiler.LCNF.JoinPointContextExtender.extend
code: /--
Use all of the above functions to find free variables declared outside
of join points that said join points can be reasonaly extended by. Reasonable
meaning that in case the current join point is nested within a function
declaration we will not extend it by free variables declared before the
function declaration because we cannot lift join points outside of function
declarations.

All of this is done to eliminate dependencies of join points onto their
position within the code so we can pull them out as far as possible, hopefully
enabling new inlining possibilities in the next simplifier run.
-/
partial def extend (decl : Decl) : CompilerM Decl 
goal: Lean.Compiler.LCNF.CompilerM Lean.Compiler.LCNF.Decl

full_name: Lean.Compiler.LCNF.JoinPointContextExtender.extend.go
code: /--
Use all of the above functions to find free variables declared outside
of join points that said join points can be reasonaly extended by. Reasonable
meaning that in case the current join point is nested within a function
declaration we will not extend it by free variables declared before the
function declaration because we cannot lift join points outside of function
declarations.

All of this is done to eliminate dependencies of join points onto their
position within the code so we can pull them out as far as possible, hopefully
enabling new inlining possibilities in the next simplifier run.
-/
partial def extend (decl : Decl) : CompilerM Decl 
goal: Lean.Compiler.LCNF.JoinPointContextExtender.ExtendM Lean.Compiler.LCNF.Code

full_name: Lean.Compiler.LCNF.JoinPointContextExtender.extend.goFVar
code: /--
Use all of the above functions to find free variables declared outside
of join points that said join points can be reasonaly extended by. Reasonable
meaning that in case the current join point is nested within a function
declaration we will not extend it by free variables declared before the
function declaration because we cannot lift join points outside of function
declarations.

All of this is done to eliminate dependencies of join points onto their
position within the code so we can pull them out as far as possible, hopefully
enabling new inlining possibilities in the next simplifier run.
-/
partial def extend (decl : Decl) : CompilerM Decl 
goal: Lean.Compiler.LCNF.JoinPointContextExtender.ExtendM Lean.FVarId

full_name: Lean.Compiler.LCNF.JoinPointCommonArgs.AnalysisCtx.jpScopes
code: /--
Context for `ReduceAnalysisM`.
-/
structure AnalysisCtx where
  /--
  The variables that are in scope at the time of the definition of
  the join point.
  -/
  jpScopes : FVarIdMap FVarIdSet := {}
goal: Lean.FVarIdMap Lean.FVarIdSet

full_name: Lean.Compiler.LCNF.JoinPointCommonArgs.AnalysisCtx
code: /--
Context for `ReduceAnalysisM`.
-/
structure AnalysisCtx where
  /--
  The variables that are in scope at the time of the definition of
  the join point.
  -/
  jpScopes : FVarIdMap FVarIdSet := {}
goal: Type

full_name: Lean.Compiler.LCNF.JoinPointCommonArgs.AnalysisState
code: /--
State for `ReduceAnalysisM`.
-/
structure AnalysisState where
  /--
  A map, that for each join point id contains a map from all (so far)
  duplicated argument ids to the respective duplicate value
  -/
  jpJmpArgs : FVarIdMap FVarSubst := {}
goal: Type

full_name: Lean.Compiler.LCNF.JoinPointCommonArgs.AnalysisState.jpJmpArgs
code: /--
State for `ReduceAnalysisM`.
-/
structure AnalysisState where
  /--
  A map, that for each join point id contains a map from all (so far)
  duplicated argument ids to the respective duplicate value
  -/
  jpJmpArgs : FVarIdMap FVarSubst := {}
goal: Lean.FVarIdMap Lean.Compiler.LCNF.FVarSubst

full_name: Lean.Compiler.LCNF.JoinPointCommonArgs.ReduceAnalysisM
code: abbrev ReduceAnalysisM 
goal: Type

full_name: Lean.Compiler.LCNF.JoinPointCommonArgs.ReduceActionM
code: abbrev ReduceActionM 
goal: Type

full_name: Lean.Compiler.LCNF.JoinPointCommonArgs.isInJpScope
code: def isInJpScope (jp : FVarId) (var : FVarId) : ReduceAnalysisM Bool 
goal: Lean.Compiler.LCNF.JoinPointCommonArgs.ReduceAnalysisM Bool

full_name: Lean.Compiler.LCNF.JoinPointCommonArgs.reduce.goAnalyze
code: /--
Take a look at each join point and each of their call sites. If all
call sites of a join point have one or more arguments in common, for example:
```
jp _jp.1 a b c => ...
...
cases foo
| n1 => jmp _jp.1 d e f
| n2 => jmp _jp.1 g e h
```
We can get rid of the common argument in favour of inlining it directly
into the join point (in this case the `e`). This reduces the amount of
arguments we have to pass around drastically for example in `ReaderT` based
monad stacks.

Note 1: This transformation can in certain niche cases obtain better results.
For example:
```
jp foo a b => ..
let x := ...
cases discr
| n1 => jmp foo x y
| n2 => jmp foo x z
```
Here we will not collapse the `x` since it is defined after the join point `foo`
and thus not accessible for substitution yet. We could however reorder the code in
such a way that this is possible, this is currently not done since we observe
than in praxis most of the applications of this transformation can occur naturally
without reordering.

Note 2: This transformation is kind of the opposite of `JoinPointContextExtender`.
However we still benefit from the extender because in the `simp` run after it
we might be able to pull join point declarations further up in the hierarchy
of nested functions/join points which in turn might enable additional optimizations.
After we have performed all of these optimizations we can take away the
(remaining) common arguments and end up with nicely floated and optimized
code that has as little arguments as possible in the join points.
-/
partial def reduce (decl : Decl) : CompilerM Decl 
goal: Lean.Compiler.LCNF.JoinPointCommonArgs.ReduceAnalysisM Unit

full_name: Lean.Compiler.LCNF.JoinPointCommonArgs.reduce.goReduce
code: /--
Take a look at each join point and each of their call sites. If all
call sites of a join point have one or more arguments in common, for example:
```
jp _jp.1 a b c => ...
...
cases foo
| n1 => jmp _jp.1 d e f
| n2 => jmp _jp.1 g e h
```
We can get rid of the common argument in favour of inlining it directly
into the join point (in this case the `e`). This reduces the amount of
arguments we have to pass around drastically for example in `ReaderT` based
monad stacks.

Note 1: This transformation can in certain niche cases obtain better results.
For example:
```
jp foo a b => ..
let x := ...
cases discr
| n1 => jmp foo x y
| n2 => jmp foo x z
```
Here we will not collapse the `x` since it is defined after the join point `foo`
and thus not accessible for substitution yet. We could however reorder the code in
such a way that this is possible, this is currently not done since we observe
than in praxis most of the applications of this transformation can occur naturally
without reordering.

Note 2: This transformation is kind of the opposite of `JoinPointContextExtender`.
However we still benefit from the extender because in the `simp` run after it
we might be able to pull join point declarations further up in the hierarchy
of nested functions/join points which in turn might enable additional optimizations.
After we have performed all of these optimizations we can take away the
(remaining) common arguments and end up with nicely floated and optimized
code that has as little arguments as possible in the join points.
-/
partial def reduce (decl : Decl) : CompilerM Decl 
goal: Lean.Compiler.LCNF.JoinPointCommonArgs.ReduceActionM Lean.Compiler.LCNF.Code

full_name: Lean.Compiler.LCNF.JoinPointCommonArgs.reduce.goAnalyzeFunDecl
code: /--
Take a look at each join point and each of their call sites. If all
call sites of a join point have one or more arguments in common, for example:
```
jp _jp.1 a b c => ...
...
cases foo
| n1 => jmp _jp.1 d e f
| n2 => jmp _jp.1 g e h
```
We can get rid of the common argument in favour of inlining it directly
into the join point (in this case the `e`). This reduces the amount of
arguments we have to pass around drastically for example in `ReaderT` based
monad stacks.

Note 1: This transformation can in certain niche cases obtain better results.
For example:
```
jp foo a b => ..
let x := ...
cases discr
| n1 => jmp foo x y
| n2 => jmp foo x z
```
Here we will not collapse the `x` since it is defined after the join point `foo`
and thus not accessible for substitution yet. We could however reorder the code in
such a way that this is possible, this is currently not done since we observe
than in praxis most of the applications of this transformation can occur naturally
without reordering.

Note 2: This transformation is kind of the opposite of `JoinPointContextExtender`.
However we still benefit from the extender because in the `simp` run after it
we might be able to pull join point declarations further up in the hierarchy
of nested functions/join points which in turn might enable additional optimizations.
After we have performed all of these optimizations we can take away the
(remaining) common arguments and end up with nicely floated and optimized
code that has as little arguments as possible in the join points.
-/
partial def reduce (decl : Decl) : CompilerM Decl 
goal: Lean.Compiler.LCNF.JoinPointCommonArgs.ReduceAnalysisM Unit

full_name: Lean.Compiler.LCNF.JoinPointCommonArgs.reduce
code: /--
Take a look at each join point and each of their call sites. If all
call sites of a join point have one or more arguments in common, for example:
```
jp _jp.1 a b c => ...
...
cases foo
| n1 => jmp _jp.1 d e f
| n2 => jmp _jp.1 g e h
```
We can get rid of the common argument in favour of inlining it directly
into the join point (in this case the `e`). This reduces the amount of
arguments we have to pass around drastically for example in `ReaderT` based
monad stacks.

Note 1: This transformation can in certain niche cases obtain better results.
For example:
```
jp foo a b => ..
let x := ...
cases discr
| n1 => jmp foo x y
| n2 => jmp foo x z
```
Here we will not collapse the `x` since it is defined after the join point `foo`
and thus not accessible for substitution yet. We could however reorder the code in
such a way that this is possible, this is currently not done since we observe
than in praxis most of the applications of this transformation can occur naturally
without reordering.

Note 2: This transformation is kind of the opposite of `JoinPointContextExtender`.
However we still benefit from the extender because in the `simp` run after it
we might be able to pull join point declarations further up in the hierarchy
of nested functions/join points which in turn might enable additional optimizations.
After we have performed all of these optimizations we can take away the
(remaining) common arguments and end up with nicely floated and optimized
code that has as little arguments as possible in the join points.
-/
partial def reduce (decl : Decl) : CompilerM Decl 
goal: Lean.Compiler.LCNF.CompilerM Lean.Compiler.LCNF.Decl

full_name: Lean.Compiler.LCNF.Decl.findJoinPoints
code: /--
Find all `fun` declarations in `decl` that qualify as join points then replace
their definitions and call sites with `jp`/`jmp`.
-/
def Decl.findJoinPoints (decl : Decl) : CompilerM Decl 
goal: Lean.Compiler.LCNF.CompilerM Lean.Compiler.LCNF.Decl

full_name: Lean.Compiler.LCNF.findJoinPoints
code: def findJoinPoints : Pass 
goal: Lean.Compiler.LCNF.Pass

full_name: Lean.Compiler.LCNF.initFn._@..lake.packages.lean4.src.lean.Lean.Compiler.LCNF.JoinPoints._hyg.4946
code: builtin_initialize
  registerTraceClass `Compiler.findJoinPoints (inherited := true)
goal: IO Unit

full_name: Lean.Compiler.LCNF.Decl.extendJoinPointContext
code: def Decl.extendJoinPointContext (decl : Decl) : CompilerM Decl 
goal: Lean.Compiler.LCNF.CompilerM Lean.Compiler.LCNF.Decl

full_name: Lean.Compiler.LCNF.extendJoinPointContext
code: def extendJoinPointContext (occurrence : Nat := 0) (phase := Phase.mono) (_h : phase ≠ .base := by simp): Pass 
goal: Lean.Compiler.LCNF.Pass

full_name: Lean.Compiler.LCNF.initFn._@..lake.packages.lean4.src.lean.Lean.Compiler.LCNF.JoinPoints._hyg.5041
code: builtin_initialize
  registerTraceClass `Compiler.extendJoinPointContext (inherited := true)
goal: IO Unit

full_name: Lean.Compiler.LCNF.Decl.commonJoinPointArgs
code: def Decl.commonJoinPointArgs (decl : Decl) : CompilerM Decl 
goal: Lean.Compiler.LCNF.CompilerM Lean.Compiler.LCNF.Decl

full_name: Lean.Compiler.LCNF.commonJoinPointArgs
code: def commonJoinPointArgs : Pass 
goal: Lean.Compiler.LCNF.Pass

full_name: Lean.Compiler.LCNF.initFn._@..lake.packages.lean4.src.lean.Lean.Compiler.LCNF.JoinPoints._hyg.5121
code: builtin_initialize
  registerTraceClass `Compiler.commonJoinPointArgs (inherited := true)
goal: IO Unit