4

我正在尝试开始使用 haskell-llvm 绑定,但遇到了我不太理解的编译错误。

代码:

module ModuleMaker where

import LLVM.Core
import LLVM.FFI.Core
import Data.Int

main :: IO ()
main = do
  m <- newNamedModule "test"

  fns <- defineModule m buildMod

  writeBitcodeToFile "ModuleMaker.bc" m

  return ()

buildMod :: CodeGenModule (Function (IO Int32))
buildMod = do 
  main <- createNamedFunction ExternalLinkage "main" $ do
    addResult <- iadd (2::Int32) (3::Int32)
    ret addResult

  return main

这导致这两个错误:

ModuleMaker.hs:20:18:
    No instance for (ABinOp Int32 Int32 (v0 c0))
      arising from a use of `iadd'
    Possible fix:
      add an instance declaration for (ABinOp Int32 Int32 (v0 c0))
    In a stmt of a 'do' block:
      addResult <- iadd (2 :: Int32) (3 :: Int32)
    In the second argument of `($)', namely
      `do { addResult <- iadd (2 :: Int32) (3 :: Int32);
            ret addResult }'
    In a stmt of a 'do' block:
      main <- createNamedFunction ExternalLinkage "main"
              $ do { addResult <- iadd (2 :: Int32) (3 :: Int32);
                     ret addResult }

ModuleMaker.hs:21:5:
    No instance for (Ret (v0 c0) Int32) arising from a use of `ret'
    The type variables `v0', `c0' are ambiguous
    Possible fix: add a type signature that fixes these type variable(s)
    Note: there is a potential instance available:
      instance [overlap ok] Ret (LLVM.Core.Value a) a
        -- Defined in `llvm-3.2.0.2:LLVM.Core.Instructions'
    Possible fix: add an instance declaration for (Ret (v0 c0) Int32)
    In a stmt of a 'do' block: ret addResult
    In the second argument of `($)', namely
      `do { addResult <- iadd (2 :: Int32) (3 :: Int32);
            ret addResult }'
    In a stmt of a 'do' block:
      main <- createNamedFunction ExternalLinkage "main"
              $ do { addResult <- iadd (2 :: Int32) (3 :: Int32);
                     ret addResult }

在第一个错误中,我看到那(ABinOp Int32 Int32iadd专门用于 的指令Int32,但我不明白(v0 c0)它来自哪里或它应该是什么值。我见过的 haskell-llvm 示例似乎没有提供任何进一步的论据,add所以我有点困惑......

我看到的第二个错误与第一个错误有关(因为v0andc0变量对吗?)。我猜测修复第一个错误将修复第二个错误。我做错了什么导致这些错误?

4

3 回答 3

3

查看源代码,我们有iadd :: (IsInteger c, ABinOp a b (v c)) => a -> b -> CodeGenFunction r (v c). 然后, 的前两个参数iadd必须是类型类的成员ABinOp——但你已经知道了,GHC 告诉你。

我认为这(v0 c0)不是问题,我怀疑。看源码,ABinOp定义为class ABinOp a b c | a b -> c where。最后一个类型参数,c由给定任何两个输入参数的类型检查器自动确定(它们的类型将根据您使用ABinOp约束提供给函数的参数类型推断出来)。这是由于类声明中的功能依赖。我还怀疑第二个错误与第一个错误直接相关。

现在来解决问题。GHC声称不存在前两个类型参数为ABinOp的实例Int32;事实上,从我看到的源代码来看,GHC 是完全正确的。ABinOp我看到的唯一实例是:

instance ABinOp (Value a) (Value a) (Value a) where
    abinop _ op (Value a1) (Value a2) = buildBinOp op a1 a2

instance ABinOp (ConstValue a) (Value a) (Value a) where
    abinop _ op (ConstValue a1) (Value a2) = buildBinOp op a1 a2

instance ABinOp (Value a) (ConstValue a) (Value a) where
    abinop _ op (Value a1) (ConstValue a2) = buildBinOp op a1 a2

instance ABinOp (ConstValue a) (ConstValue a) (ConstValue a) where
    abinop cop _ (ConstValue a1) (ConstValue a2) =
        return $ ConstValue $ cop a1 a2

instance (IsConst a) => ABinOp (Value a) a (Value a) where
    abinop cop op a1 a2 = abinop cop op a1 (constOf a2)

instance (IsConst a) => ABinOp a (Value a) (Value a) where
    abinop cop op a1 a2 = abinop cop op (constOf a1) a2

自然,您的使用iadd必须匹配其中之一(除非某处隐藏更多)。如您所见,Int32不在其中任何一个中。好吧,让我们看一下Valueand ConstValue

newtype Value a = Value { unValue :: FFI.ValueRef }
    deriving (Show, Typeable)

newtype ConstValue a = ConstValue { unConstValue :: FFI.ValueRef }
    deriving (Show, Typeable)

深入探究FFI(的别名LLVM.FFI.Core),我们发现:

data Value
    deriving (Typeable)
type ValueRef = Ptr Value

现在我们可以假设iadd需要类型Value Ptr或的争论ConstValue Ptr。我不知道它们之间的区别。

现在就个人而言,我对 Haskell 知之甚少Ptr。编辑:鉴于这一事实,我不能说如何正确实例化 a Ptr,但我下面的答案可以。以上所有内容仍然成立。

于 2013-06-05T02:47:18.580 回答
3

user2407038 提出了关于 ABinOp 实例的重要观点。最终,问题是iadd至少需要一个ConstValue aor Value a,但给定了两个Int32s。修复方法是:

createNamedFunction ExternalLinkage "main" $ do
  addResult <- iadd (valueOf (5 :: Int32)) (5 :: Int32)
  ret addResult

请注意,只有一个iadd参数必须是值,另一个可以是直Int32的,因为ABinop (Value a) a (Value a)实例。

于 2013-06-05T03:36:29.863 回答
0

llvm 包试图使 API 尽可能接近 LLVM LL 文件。因此,它尝试使用 FlexibleInstances 将所有可能的参数类型编码到一个名为“iadd”的函数中。因此,您必须在任何地方添加类型注释。与此相反,我编写了 llvm-extra 包,其中“添加”总是添加“值”类型。简单得多,即使是 Haskell 98,也几乎不需要类型注释,如果需要,您总是可以轻松地将 ConstValue 转换为 Value。

于 2016-08-02T07:57:22.117 回答