Haskell llvm-general JIT:即时调用 C 函数。 Stephen Diehl 的教程

Haskell llvm-general JIT: calling a C function on the fly. Stephen Diehl's tutorial

我在 Linux Mint 盒子上关注 Stephen Diehl's excellent LLVM Haskell tutorial(Linux Mint 17 Qiana,GHC 7.8.4,llvm 3.4)。

我克隆了项目的 github 存储库,并且能够使用包含的 Makefile.

构建每一章的示例

教程第 4 章介绍了 JIT 编译器:

import qualified LLVM.General.ExecutionEngine as EE

jit :: Context -> (EE.MCJIT -> IO a) -> IO a
jit c = EE.withMCJIT c optlevel model ptrelim fastins
  where
    optlevel = Just 2  -- optimization level
    model    = Nothing -- code model ( Default )
    ptrelim  = Nothing -- frame pointer elimination
    fastins  = Nothing -- fast instruction selection

runJIT :: AST.Module -> IO (Either String ())
runJIT mod = do
    ...
    jit context $ \executionEngine ->
        ...
        EE.withModuleInEngine executionEngine m $ \ee -> do
          mainfn <- EE.getFunction ee (AST.Name "main")
          case mainfn of
            Just fn -> do
              res <- run fn
              putStrLn $ "Evaluated to: " ++ show res
            Nothing -> return ()

然后教程通过编写C代码来实现操作来扩展语言

/* cbits
$ gcc -fPIC -shared cbits.c -o cbits.so
$ clang -fPIC -shared cbits.c -o cbits.so
*/

#include "stdio.h"

// putchard - putchar that takes a double and returns 0.
double putchard(double X) {
  putchar((char)X);
  fflush(stdout);
  return 0;
}

makefile 通过运行构建项目:

gcc -fPIC -shared src/chapter4/cbits.c -o src/chapter4/cbits.so
ghc -no-user-package-db -package-db .cabal-sandbox/*-packages.conf.d src/chapter4/cbits.so --make src/chapter4/*.hs -o chapter4

但是当我尝试调用 putchard() 时出现错误:

LLVM ERROR: Program used external function 'putchard' which could not be resolved!

我是不是遗漏了什么?

我看到有人对本教程的原始 C++ 版本有类似的问题。他们通常通过向 gcc 构建命令 (-rdynamic) 添加一个标志来解决它,这应该使链接器将所有符号添加到动态符号 table,而不仅仅是使用的符号。我怀疑 ghc 正在从 executable 文件中剥离 putchard()

当我在 OS X 上执行完全相同的步骤时,一切正常,我可以毫无问题地调用 putchard()

发生了什么事?

我刚刚在 Centos 7 上尝试 运行 这个项目,它成功了。我的 Mint 机器一定有问题。

也许 GHC 在链接和删除符号时有点过分热心?能否在Main.hs中使用FFI手动添加引用,然后重新编译。

{-# LANGUAGE ForeignFunctionInterface #-}

import Foreign.C.Types

foreign import ccall safe "putchard" putchard
    :: CDouble -> CDouble