Again co-developed with @bollu.
Based on top of: #3225
While hunting down the performance discrepancy on qsort.lean between C
and LLVM we noticed there was a single, trivially optimizeable, alloca
(LLVM's stack memory allocation instruction) that had load/stores in the
hot code path. We then found:
https://groups.google.com/g/llvm-dev/c/e90HiFcFF7Y.
TLDR: `mem2reg`, the pass responsible for getting rid of allocas if
possible, only triggers on an alloca if it is in the first BB. The
allocas of the current implementation get put right at the location
where they are needed -> they are ignored by mem2reg.
Thus we decided to add functionality that allows us to push all allocas
up into the first BB.
We initially wanted to write `buildPrologueAlloca` in a `withReader`
style so:
1. get the current position of the builder
2. jump to first BB and do the thing
3. revert position to the original
However the LLVM C API does not expose an option to obtain the current
position of an IR builder. Thus we ended up at the current
implementation which resets the builder position to the end of the BB
that the function was called from. This is valid because we never
operate anywhere but the end of the current BB in the LLVM emitter.
The numbers on the qsort benchmark got improved by the change as
expected, however we are not fully there yet:
```
C:
Benchmark 1: ./qsort.lean.out 400
Time (mean ± σ): 2.005 s ± 0.013 s [User: 1.996 s, System: 0.003 s]
Range (min … max): 1.993 s … 2.036 s 10 runs
LLVM before aligning the types
Benchmark 1: ./qsort.lean.out 400
Time (mean ± σ): 2.151 s ± 0.007 s [User: 2.146 s, System: 0.001 s]
Range (min … max): 2.142 s … 2.161 s 10 runs
LLVM after aligning the types
Benchmark 1: ./qsort.lean.out 400
Time (mean ± σ): 2.073 s ± 0.011 s [User: 2.067 s, System: 0.002 s]
Range (min … max): 2.060 s … 2.097 s 10 runs
LLVM after this
Benchmark 1: ./qsort.lean.out 400
Time (mean ± σ): 2.038 s ± 0.009 s [User: 2.032 s, System: 0.001 s]
Range (min … max): 2.027 s … 2.052 s 10 runs
```
Note: If you wish to merge this PR independently from its predecessor,
there is no technical dependency between the two, I'm merely stacking
them so we can see the performance impacts of each more clearly.