Search code examples
haskelllinkerghcnvcc

Using GHC with NVCC


As an alternative to accelerate, I'm trying to call CUDA code over Haskell's FFI.

Here's a simple program that fails to compile:

cuda_code.cu:

void cuda_init() {
    cudaFree (0);
    cudaThreadSynchronize ();
}

Test.hs:

foreign import ccall unsafe "cuda_init" cuda_init :: IO ()
main = cuda_init

I compiled with

$> nvcc -c -o cuda_code.o cuda_code.cu
$> ghc Test cuda_code.o

and got several linking errors (undefined reference to cudaFree, etc). This isn't terribly surprising, and the obvious solution (to me) is to link with NVCC using -pgml nvcc. (This worked when I was using Intel CILK+ in my C code: I simply changed the linker to ICC, and everything worked just fine.)

Howver, using NVCC to link results in the linking error:

ghc Test -pgml nvcc cuda_code.o
[1 of 1] Compiling Main             ( Test.hs, Test.o )
Linking Test ...
nvcc fatal   : Unknown option 'u'

Running

strace -v -f -e execve ghc Test -pgml nvcc cuda_code.o

(is there an easier way?) I discovered ghc is calling nvcc with

nvcc ... -L~/ghc... -L... -l... -l... -u ghczmprim_GHC... -u ghc...

I assume the -u options are intended for linking gcc (and apparently icc) with undefined symbols, something nvcc clearly doesn't like.

I have no knowledge about how GHC links files. Thoughts on how I can get GHC to link to my CUDA code?

--------EDIT-----------------

Someone suggested that I try linking with GCC (as usual), but pass in the necessary linker options to gcc so that it can link to the CUDA libraries. If anyone knows what these might be, this would probably work!


Solution

  • I figured out how to make this work.

    cudaTest.cu:

    // the `extern "C"` is important! It tells nvcc to not 
    // mangle the name, since nvcc assumes C++ code by default
    extern "C" 
    void cudafunc() {
      cudaFree(0);
      cudaThreadSynchronize();
    }
    

    Test.hs

    foreign import ccall unsafe "cudafunc" cudaFunc :: IO ()
    main = cudaFunc
    

    Compile with:

    >nvcc -c -o cudaTest.o cudaTest.cu
    >ghc --make Test.hs -o Test cudaTest.o -optl-lcudart
    

    I also tried giving GHC the option -pgmc g++ and removing the extern "C" (which I expected to work), but got compile errors in some CUDA header files. There's probably some easy way to fix this so that you don't need to explicitly tag every function with extern "C".