Haskell Source
repa_process :: R.Stream k Int -> Int
repa_process s
= R.fold (+) 0 s + R.fold (*) 1 s
Actual well formed GHC Core code
.. or at least the parts that get printed with -dsuppress-all.
repa_process
repa_process =
\ @ k_aq6 arg_s2Rf ->
let { (# x1_s2R6, x1_acc_s2R5 #) ~ _ <- newByteArray# 8 realWorld# } in
let { __DEFAULT ~ x2_s2R8 <- writeIntArray# x1_acc_s2R5 0 0 x1_s2R6 } in
let { (# x3_s2Rd, x3_acc_s2Rc #) ~ _ <- newByteArray# 8 x2_s2R8 } in
let { __DEFAULT ~ x4_s2RS <- writeIntArray# x3_acc_s2Rc 0 1 x3_s2Rd } in
let { Stream ds1_s2Rs ds2_s2Rj ~ _ <- arg_s2Rf } in
let { Vector rb_s2Rz _ rb2_s2Ry ~ _ <- ds2_s2Rj `cast` ... } in
letrec {
go_s2RP
go_s2RP =
\ ix_s2Rr world_s2Ru ->
case >=# ix_s2Rr ds1_s2Rs of _ {
False ->
let { (# x7_s2RF, x0_s2RC #) ~ _ <- readIntArray# x1_acc_s2R5 0 world_s2Ru } in
let { __DEFAULT ~ sat_s2SV <- +# rb_s2Rz ix_s2Rr } in
let { __DEFAULT ~ wild3_s2RD <- indexIntArray# rb2_s2Ry sat_s2SV } in
let { __DEFAULT ~ sat_s2SU <- +# x0_s2RC wild3_s2RD } in
let { __DEFAULT ~ x8_s2RH <- writeIntArray# x1_acc_s2R5 0 sat_s2SU x7_s2RF } in
let { (# x9_s2RN, x1_s2RL #) ~ _ <- readIntArray# x3_acc_s2Rc 0 x8_s2RH } in
let { __DEFAULT ~ sat_s2ST <- *# x1_s2RL wild3_s2RD } in
let { __DEFAULT ~ x10_s2RR <- writeIntArray# x3_acc_s2Rc 0 sat_s2ST x9_s2RN } in
let { __DEFAULT ~ sat_s2SS <- +# ix_s2Rr 1 } in
go_s2RP sat_s2SS x10_s2RR;
True -> world_s2Ru
}; } in
let { __DEFAULT ~ x11_s2RU <- go_s2RP 0 x4_s2RS } in
let { (# x12_s2RY, x1_s2S2 #) ~ _ <- readIntArray# x1_acc_s2R5 0 x11_s2RU } in
let { (# _, x2_s2S3 #) ~ _ <- readIntArray# x3_acc_s2Rc 0 x12_s2RY } in
let { __DEFAULT ~ sat_s2SW <- +# x1_s2S2 x2_s2S3 } in I# sat_s2SW
Im using ByteArrays to fake imperative variables. The next job is to convert the arrays x1_acc_s2R5 and x3_acc_s2Rc into proper accumulation variables, and attach them to the go_s2RP loop.
x86 assembly code
This is just for the inner loop.
LBB11_2:
movq (%rcx), %rdi
addq %rdi, 16(%rdx)
imulq 16(%rsi), %rdi
movq %rdi, 16(%rsi)
addq $8, %rcx
incq %r14
cmpq %r14, %rax
jg LBB11_2
The extra memory operations are there because the LLVM optimiser doesn't know that the two ByteArrays I'm using to fake imperative variables don't alias. This should turn into optimal code once it uses pure accumulators.
Is there a standard algorithm for converting mutable variables to accumulation variables? The closest I can imagine would be to ensure a mutable variable does not escape to an outer scope, convert it to be the state of a state monad, augment all reads to the equivalent get, writes to the equivalent put, and augment all other functions that reference it to be in the state monad (and perhaps optimizing by storing all mutable variables in the same data type, unboxing where possible, and perhaps only using a reader monad if only reads occur, a form of writer monad if only writes occur).
ReplyDelete