This pass expands pairs of reset-reuse instructions into explicit hot and cold paths. We do this on the LCNF level rather than letting the backend do it because we can apply domain specific optimizations at this point in time.
Whenever we encounter a let token := reset nfields orig; k, we create code of the shape (not
showing reference counting instructions):
jp resetjp token isShared :=
k
cases isShared orig with
| false -> jmp resetjp orig true
| true -> jmp resetjp box(0) false
Then within the join point body k we turn dec instructions on token into del and expand
let final := reuse token arg; k' into another join point:
jp reusejp final :=
k'
cases isShared with
| false -> jmp reusejp token
| true ->
let x := alloc args
jmp reusejp x
In addition to this we perform optimizations specific to the hot path for both the resetjp and
reusejp. For the former, we will frequently encounter the pattern:
let x_0 = proj[0] orig
inc x_0
...
let x_i = proj[i] orig
inc x_i
let token := reset nfields orig
On the hot path we do not free orig, thus there is no need to increment the reference counts of
the projections because the reference coming from orig will keep all of the projections alive
naturally (a form of "dynamic derived borrows" if you wish). On the cold path the reference counts
still have to happen though.
For resetjp we frequently encounter the pattern:
let final := reuse token args
set final[0] := x0
...
set final[i] := xi
On the hot path we know that token and orig refer to the same value. Thus, if we can detect that
one of the xi is of the shape let xi := proj[i] orig, we can omit the store on the hot path.
Just like with reusejp on the cold path we have to perform all the stores.