I want to be able to call LLVM code from Haskell without the overhead of a full function call. For example:
-- Main.hs --
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE UnboxedTuples #-}
{-# LANGUAGE GHCForeignImportPrim #-}
{-# LANGUAGE ForeignFunctionInterface #-}
{-# LANGUAGE UnliftedFFITypes #-}
{-# LANGUAGE BangPatterns #-}
import GHC.Exts(Word(W#))
import GHC.Prim(Word#)
foreign import ccall llvminc :: Word# -> Word#
main = do
line1 <- getLine
let !(W# x1) = read line1
let !r1 = llvminc x1
print (W# r1)
-- funcs.ll --
define fastcc i64 @llvminc(i64 inreg %x) {
%r = add i64 %x, 1
ret i64 %r
}
I can compile and link this to produce a functioning executable by running:
ghc -O2 -fllvm Main.hs funcs.ll
Indeed, even removing -fllvm
still results in a functioning executable, e.g.
ghc -O2 Main.hs funcs.ll
Which leads me to strongly suspect GHC is linking these files separately in both cases using ordinary C linkage.
Indeed, when I investigate the intermediate output using:
ghc -O2 -fllvm -keep-s-files Main.hs funcs.ll
I see the following in Main.s:
callq suspendThread
movq %rax, %rbp
movq %rbx, %rdi
callq llvminc
movq %rax, %rbx
movq %rbp, %rdi
callq resumeThread
Which again suggests that GHC is just asking LLVM to compile the files separately and it then just sending the results to the system linker, which wouldn't inline the calls.
Instead, I'd like GHC to send the initial LLVM files (both from GHC and user specified) to the llvm-link, which unlike a system linker, simply combines multiple LLVM bitcode files into one LLVM bitcode file. It would be best if this result was then compiled to native code object file and sent to the system linker, instead of multiple object files being sent to the system linker.
Indeed, when I tried this manually, assembling the .ll
human readable files to LLVM .bc
bitcode, llvm-link
ing the resulting bitcode, then disassembling the bitcode like so:
llvm-as Main.ll
llvm-as funcs.ll
llvm-link funcs.bc Main.bc -o combined.bc
llvm-dis combined.bc
I found the following in the resulting LLVM code
%ln59M = add i64 %ln59L, 1
directly after the call to read, with no "call" or return. The actual function is still in the LLVM, but is uncalled.
So I tried instructing GHC to link with the LLVM linker by adding -pgml llvm-link
to the command line, but this failed spectacularly, with llvm-link
throwing back many errors about unknown options. This is unsurprising, as llvm-link
isn't a real linker, it just combines LLVM files.
So, is there anyway to get GHC to send all it's intermediate LLVM files through the LLVM linker to enable intermodule optimisations at the LLVM level?
Have you tried these two things?
1 - Use the alwaysinline function attribute: http://llvm.org/docs/LangRef.html#function-attributes
2 - Using GHC calling convention, (cc 10 instead of fastcc): http://llvm.org/docs/LangRef.html#calling-conventions
define cc 10 i64 @llvminc(i64 inreg %x) alwaysinline {
%r = add i64 %x, 1
ret i64 %r
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With