# inline-asm
[![Build Status][travis-badge]][travis]
_When inline C is too safe_.
Did you try `inline-c`, but it's not enough? You need more? Nothing seems to satisfy?
`inline-asm` to the rescue!
And, since the inline assembly is just a usual Haskell value (even if manipulated at compile-time),
there's a lot of pretty cool stuff one can do, like, for instance, explicit compile-time loop unrolling.
## Examples
Swapping two `Int`s and also incrementing one of them by two:
```haskell
defineAsmFun "swap2p1"
[asmTy| (a : Int) (b : Int) | (_ : Int) (_ : Int)]
[asm|
xchg {a}, {b}
add $2, {b}
|]
```
This provides a function `swap2p1 :: Int -> Int -> (Int, Int)` that, well, swaps two `Int`s.
Note that the resulting function is pure, and the `{a}`, `{b}` antiquoters.
Getting the last character of a `ByteString`, or a default character if it's empty:
```haskell
defineAsmFun "lastChar"
[asmTy| (bs : ByteString) (def : Word) | (w : Word) |]
[asm|
test {bs:len}, {bs:len}
jz is_zero
movzbq -1({bs:ptr},{bs:len}), {w}
RET_HASK
is_zero:
mov {def}, {w}
|]
```
This provides a function `lastChar :: ByteString -> Word -> Word`.
Note the special `{bs:ptr}` and `{bs:len}` antiquoters, as well as `RET_HASK` command to return early.
SIMD-accelerated character occurrences count in a string:
```haskell
defineAsmFun "countCharSSE42"
[asmTy| (ch : Word8) (ptr : Ptr Word8) (len : Int) | (cnt : Int) |] $
unroll "i" [12..15]
[asm|
push %r{i}|] <> [asm|
vmovd {ch}, %xmm15
vpxor %xmm0, %xmm0, %xmm0
vpshufb %xmm0, %xmm15, %xmm15
shr $7, {len}
mov $16, %eax
mov $16, %edx
xor {cnt}, {cnt}
{move ptr rdi}
loop: |] <> unrolls "i" [1..8] [
[asm|
vmovdqa {(i - 1) * 0x10}({ptr}), %xmm{i}
|], [asm|
vpcmpestrm $10, %xmm15, %xmm{i}
vmovdqa %xmm0, %xmm{i}
|], [asm|
vmovq %xmm{i}, %r{i + 7}
|], [asm|
popcnt %r{i + 7}, %r{i + 7}
|], [asm|
add %r{i + 7}, {cnt}
|]
] <>
[asm|
add $128, {ptr}
dec {len}
jnz loop|] <> unroll "i" [15,14..12] [asm|
pop %r{i} |]
```
This provides a function `countCharSSE42 :: Word8 -> Ptr Word8 -> Int -> Int`.
Note the `unroll`/`unrolls` Haskell function for compile-time code generation and loop unrolling
with arithmetic expressions in the templates.
Impure computation depending on some external state, like reading the CPU's time stamp counter:
```haskell
defineAsmFunM "rdtsc"
[asmTy| | (out : Word64) |]
[asm|
rdtsc
mov %rdx, {out}
shl $32, {out}
add %rax, {out}
|]
```
This provides a function `rdtsc :: PrimMonad m => m Word64` which can be used in `ST` or `IO` contexts.
Note the **M** letter in `defineAsmFunM`.
## Basic usage
The entry point is the `defineAsmFun` function from `Language.Asm.Inline`
as well as the `asm` and `asmTy` quasiquoters from `Language.Asm.Inline.QQ`.
First, enable some extensions required for Template Haskell and for the unlifted FFI marshalling stuff:
```haskell
{-# LANGUAGE TemplateHaskell, QuasiQuotes #-}
{-# LANGUAGE GHCForeignImportPrim, UnliftedFFITypes, UnboxedTuples #-}
```
then one can just do
```haskell
defineAsmFun "funName"
[asmTy| (someInt : Int) (somePtr : Ptr Word) (someStr : BS.ByteString) | (len : Int) (count : Int) |]
[asm|
; your asm code follows
|]
```
to define a function `funName` of the type `Int -> Ptr Word -> BS.ByteString -> (Int, Int)`.
### Antiquotation
Good news: it's not necessary to memorize the GHC calling convention
to access the arguments and the output values slots!
Instead, one can use the `{someInt}` syntax to refer to the argument or the return slot named `someInt`.
**NB**: despite that, be careful to not accidentally overwrite an input parameter for now
by picking a wrong register for temporary computations.
We might introduce some syntax to pick unused registers in a future version, but for now care must be taken.
`ByteString` parameters are supported, but, being composite objects, they are a bit special:
an input parameter of type `ByteString` actually takes two registers:
one for the address of the string, and one for its length.
Sometimes it might be handy to reassociate an input parameter with another register.
For this, the `{move argName newReg}` antiquoter can be used (for instance, `{move someInt rdi}`).
This will both update the mapping from argument names to register names
as well as issue an assembly `mov` command.
In case you need to return early to the Haskell-land, just write `RET_HASK`,
which gets substituted by the actual command to return to Haskell.
### Explicit loop unrolling
The `asm` quasiquoter basically produces a string,
so it can be manipulated at compile-time with arbitrary Haskell functions.
In particular, a string template can be replicated at compile time.
The `unroll` function unrolls a single `asm` code block,
calculating arithmetic expressions involving the unroll variable, so
```haskell
unroll "i" [1..8] [asm|vmovdqa {(i - 1) * 0x10}({ptr}), %xmm{i}|]
```
is equivalent to
```assembly
vmovdqa 0x0({ptr}), %xmm1
vmovdqa 0x10({ptr}), %xmm2
vmovdqa 0x20({ptr}), %xmm3
vmovdqa 0x30({ptr}), %xmm4
vmovdqa 0x40({ptr}), %xmm5
vmovdqa 0x50({ptr}), %xmm6
vmovdqa 0x60({ptr}), %xmm7
vmovdqa 0x70({ptr}), %xmm8
```
`unrolls` works analogously, but it takes a list of `asm` code blocks (instead of a single block),
unrolls each of them and then concatenates the results. Equationally,
```haskell
unrolls var ints codes = foldMap (unroll var ints) codes
```
The `countCharSSE42` function above might be a pretty good example.
### Impure functions
Most of the functions above are actually pure:
they return the same result given the same parameters and have no side effects.
Perhaps unsurprisingly, this is not always the case.
Let's consider the `rdtsc` example again and assume we've written
```haskell
defineAsmFun "rdtsc"
[asmTy| (_ : Unit) | (out : Word64) |]
[asm|
rdtsc
mov %rdx, {out}
shl $32, {out}
add %rax, {out}
|]
```
(BTW we need a dummy `Unit` input here
since otherwise the type of the generated imported Assembly function would be just `Word64#`,
which is not a function but a value, and thus disallowed by GHC).
How do we use this function to measure something?
We'd probably write something like
```haskell
measure = do
let r1 = rdtsc Unit
runLongComputation
let r2 = rdtsc Unit
print $ r2 - r1
```
The problem is that the compiler is very keen on transforming this into
```haskell
measure = do
let r1 = rdtsc Unit
runLongComputation
let r2 = r1
print $ r2 - r1
```
so every computation is executed instantly according to our measurements,
and no amount of `{-# NOINLINE #-}` and the likes will fix it.
The only fix is to actually thread through the `State#` token,
which is what happens when we use the monadic function `defineAsmFunM`:
```haskell
defineAsmFunM "rdtsc"
[asmTy| | (out : Word64) |]
[asm|
rdtsc
mov %rdx, {out}
shl $32, {out}
add %rax, {out}
|]
```
In this case, the generated Assembly function would be imported with the type
`forall s. State# s -> (# State# s, Word64# #)`,
and what happens to `State# s` is completely opaque to the compiler, so it can no longer "optimize" this,
and the following works as expected:
```haskell
measure = do
r1 <- rdtsc
runLongComputation
r2 <- rdtsc
print $ r2 - r1
```
By the way, now that there's the state token parameter, we no longer need a dummy `Unit`.
## Safety and notes
* Firstly, all of this is utterly unsafe.
* While this package provides some shortcuts,
understanding the calling convention (in particular, which registers are actually used) is important.
The best source of truth is, of course, GHC's [source code](https://github.com/ghc/ghc/blob/HEAD/rts/include/stg/MachRegs.h).
* Each function is compiled in its own `.S` file,
so one can freely pick arbitrary naming for the labels and so on,
but, on the other hand, one cannot access labels in other functions.
This can be remedied somewhat easily — consider throwing up an issue if that's actually desired.
* Finally, all of this is utterly unsafe.
[travis]: <https://travis-ci.org/0xd34df00d/inline-asm>
[travis-badge]: <https://travis-ci.org/0xd34df00d/inline-asm.svg?branch=master>