6
votes

I am trying to get started with the LLVM binding for Haskell. A great place to start is Hello World.

The following is from a blog by the author of the binding.

bldGreet :: CodeGenModule (Function (IO ()))
bldGreet = do
    puts <- newNamedFunction ExternalLinkage "puts" :: TFunction (Ptr Word8 -> IO Word32)
    greetz <- createStringNul "Hello, World!"
    func <- createFunction ExternalLinkage $ do
      tmp <- getElementPtr greetz (0::Word32, (0::Word32, ()))
      call puts tmp -- Throw away return value.
      ret ()
    return func

It does not compile.
Instead I get "Ambiguous type variable n0' in the constraint: (type-level-0.2.4:Data.TypeLevel.Num.Sets.NatI n0) arising from a use ofgetElementPtr0' Probable fix: add a type signature that fixes these type variable(s)"

Here is a variation that does work

llvmModule :: TFunction (IO Word32)
llvmModule = 
    withStringNul "Hello world!" $ \s -> do 
    puts <- newNamedFunction ExternalLinkage "puts" :: TFunction (Ptr Word8 -> IO Word32)
    main <- newNamedFunction ExternalLinkage "main" :: TFunction (IO Word32)
    defineFunction main $ do
      tmp <- getElementPtr0 s (0::Word32, ())
      _ <- call puts tmp
      ret (0::Word32)
    return main

The first seems more natural. The question I have is what is the ambiguity in the first, and how do I fix it. The second question I have is why is the second not ambiguous.

2
could it be a typo and forgot the 0? getElementPtr0<- ? I am just guessing here.Gert Cuykens
No. There are two different functions. getElementPtr0 uses a default, where getElementPtr does not -- hence the extra argument. Also, the error is, I am pretty sure, a typeclass thing. It(GHC) wants me to tell it something more specific about the typeclass. I just have not figured out what it wants.Jonathan Gallagher
Maybe hpaste.org the complete thing so the pro's can fiddle with it :)Gert Cuykens

2 Answers

1
votes

Okay. So I solved the problem. It is indeed a typeclass thing. And it has only confused me more. However, I do have an answer to the solution. But do feel free to help my understanding out. First, some digging. The function createStringNul has type

createString :: String -> TGlobal (Array n Word8)

Fine. The problem the compiler was having is that the "n" in the Array type is ambiguous. It could literally be anything in the world. Lookup, Array, and you see

newtype Array n a

Now it is not so obvious, but upon a little digging, in particular wrt the call to getElementPtr, one finds that the n, really should be a Nat n, which is a type-level way to fix the size of the array. Now, the definition of Array n a, does not actually care it is really just a type synonym for [a]. So you can use D0, or D9 or whatever you want from the Data.TypeLevel.Num.Reps package. Fixing the size of the array, while a good idea was not actually taken into consideration by this function. But anyways, changing greetz <- createStringNul "Hello, World!" to greetz <- createStringNul "Hello, World!" :: TGlobal (Array D0 Word8) works.

Here is the interesting part... I did not expect it to work. D0 is supposed to be 0, so I did not understand why it was allowing me to store so many characters in a 0 size "Array" However, if you look at the source code, it is immediately clear that the type restriction is not actually payed attention to.

Fine, whatever, upon compiling one realizes that the createStringNul is deprecated, and withStringNul is preferred instead. Except I don't completely understand how withStringNul's types work.

1
votes

createStringNul is marked as deprecated in the current version of llvm (3.0.1.0). Use withStringNul:

import Data.Word
import LLVM.Core

bldGreet :: CodeGenModule (Function (IO ()))
bldGreet = do
    puts <- newNamedFunction ExternalLinkage "puts" :: TFunction (Ptr Word8 -> IO Word32)
    func <- withStringNul "Hello, World!" $ \greetz ->
      createFunction ExternalLinkage $ do
        tmp <- getElementPtr greetz (0::Word32, (0::Word32, ()))
        _ <- call puts tmp -- Throw away return value.
        ret ()
    return func

As to what causes the error in the first example, it's related to the fact that withStringNul has a more informative type: withStringNul :: String -> (forall n . Nat n => Global (Array n Word8) -> a) -> a -- cf. to createStringNul :: String -> TGlobal (Array n Word8). The function argument of withStringNul has a higher-rank type - it means that the function works for all n where n is a natural number.

If you really want to use createStringNul, you can make the first example compile by adding an explicit type signature for greetz:

{-# LANGUAGE TypeOperators #-}
module Test
       where

import Data.Word
import Data.TypeLevel.Num.Reps
import LLVM.Core

bldGreet :: CodeGenModule (Function (IO ()))
bldGreet = do
    puts <- newNamedFunction ExternalLinkage "puts" :: TFunction (Ptr Word8 -> IO Word32)
    greetz <- createStringNul "Hello, World!"
    func <- createFunction ExternalLinkage $ do
      tmp <- getElementPtr (greetz :: Global (Array (D1 :* D3) Word8)) (0::Word32, (0::Word32, ()))
      call puts tmp -- Throw away return value.
      ret ()
    return func

The :* type constructor comes from the type-level package and is used to construct type-level numbers. D1 :* D3 means that the array has size 13.