Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Running GHC's LLVM output through the LLVM bitcode linker first

Tags:

haskell

llvm

ghc

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-linking 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?

like image 798
Clinton Avatar asked Nov 23 '15 02:11

Clinton


1 Answers

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
}
like image 109
mentatkgs Avatar answered Oct 26 '22 00:10

mentatkgs