6

我正在尝试开始使用 Haskell 的 LLVM 绑定。一个很好的起点是Hello World。

以下来自绑定作者的博客。

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

它不编译。
相反,我得到“不明确的类型变量n0' in the constraint: (type-level-0.2.4:Data.TypeLevel.Num.Sets.NatI n0) arising from a use ofgetElementPtr0' 可能的修复:添加修复这些类型变量的类型签名”

这是一个有效的变体

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

第一个似乎更自然。我的问题是第一个歧义是什么,我该如何解决。我的第二个问题是为什么第二个不是模棱两可的。

4

2 回答 2

1

好的。所以我解决了这个问题。这确实是一个类型类的东西。它只会让我更加困惑。但是,我确实有解决方案的答案。但请随时帮助我理解。首先,一些挖掘。函数 createStringNul 具有类型

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

美好的。编译器遇到的问题是 Array 类型中的“n”不明确。它实际上可以是世界上的任何东西。查找,数组,你会看到

newtype Array n a

现在它不是那么明显,但是经过一点挖掘,特别是对 getElementPtr 的调用,我们发现 n,实际上应该是一个 Nat n,这是一种固定数组大小的类型级方法。现在,Array na 的定义实际上并不关心它实际上只是 [a] 的类型同义词。因此,您可以使用 D0、D9 或 Data.TypeLevel.Num.Reps 包中的任何您想要的内容。固定数组的大小,而这个函数实际上并没有考虑到一个好主意。但无论如何,改变 greetz <- createStringNul "Hello, World!" 到 greetz <- createStringNul "你好,世界!" :: TGlobal (Array D0 Word8) 有效。

这是有趣的部分......我没想到它会起作用。D0 应该是 0,所以我不明白为什么它允许我在一个 0 大小的“数组”中存储这么多字符 但是,如果你查看源代码,就会立即清楚实际上并不是类型限制重视。

好吧,无论如何,在编译时意识到 createStringNul 已被弃用,而 withStringNul 是首选。除了我不完全理解 withStringNul 的类型是如何工作的。

于 2013-04-13T19:02:48.150 回答
1

createStringNul llvm在(3.0.1.0)的当前版本中被标记为已弃用。使用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

withStringNul至于第一个示例中导致错误的原因,这与具有更多信息类型的事实有关: withStringNul :: String -> (forall n . Nat n => Global (Array n Word8) -> a) -> a--cf。到createStringNul :: String -> TGlobal (Array n Word8). 的函数参数withStringNul具有更高等级的类型- 这意味着该函数适用于所有n自然n数。

如果你真的想使用createStringNul,你可以通过添加显式类型签名来编译第一个示例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

:*类型构造函数来自类型级别包,用于构造类型级别的数字。D1 :* D3表示数组的大小为 13。

于 2013-04-13T19:05:20.563 回答