haskell - 无法在 LLVM OrcJIT 中跨模块访问符号

标签 haskell compiler-construction llvm jit llvm-ir

我正在使用 haskell、LLVM-hs 和 OrcJIT 编写 JIT 编译器。这是我编译模块的主文件,将它们添加到 JIT 并获取并运行内部主要功能:

main :: IO ()
main =
    withContext $ \ctx ->
        withExecutionSession $ \es ->
            withHostTargetMachine Reloc.PIC CodeModel.Default CodeGenOpt.None $ \tm ->
                withSymbolResolver es myResolver $ \psr ->
                    withObjectLinkingLayer es (\_ -> return psr) $ \oll ->
                        withIRCompileLayer oll tm $ \ircl -> do
                            loadLibraryPermanently Nothing
                            repl ctx es tm ircl

    where
        myResolver :: SymbolResolver
        myResolver = SymbolResolver $ \mangled -> do
            ptr <- getSymbolAddressInProcess mangled
            return $ Right $ JITSymbol
                { jitSymbolAddress = ptr 
                , jitSymbolFlags   = defaultJITSymbolFlags { jitSymbolExported = True }
                }


repl :: Context -> ExecutionSession -> TargetMachine -> IRCompileLayer ObjectLinkingLayer ->  IO ()
repl ctx es tm cl = runInputT defaultSettings (loop C.initCmpState)
    where
        loop :: C.CmpState -> InputT IO ()
        loop state =
            getInputLine "% " >>= \minput -> case minput of
                Nothing    -> return ()
                Just "q"   -> return ()
                Just input -> liftIO (process state input) >>= loop

        process :: C.CmpState -> String -> IO C.CmpState
        process state source =
            case L.alexScanner source of
                Left  errStr -> putStrLn errStr >> return state
                Right tokens -> case (P.parseTokens tokens) 0 of
                    P.ParseOk ast ->
                        let (res, state') = C.codeGen state (head ast) in
                        case res of
                            Left err -> putStrLn (show err) >> return state
                            Right () -> runDefinition (state' { C.externs = C.externs state }) >> return state'
                                { C.globals      = Map.empty
                                , C.instructions = []
                                }

        runDefinition :: C.CmpState -> IO ()
        runDefinition state = do
            let globals = Map.elems (C.globals state)
            let externs = Map.elems (C.externs state)
            let instructions = reverse (C.instructions state)

            let mainName = mkBSS "main.0"
            let mainFn = GlobalDefinition $ functionDefaults
                { returnType  = void
                , name        = Name mainName
                , basicBlocks = [BasicBlock (mkName "entry") instructions (Do $ Ret Nothing [])]
                }

            case instructions of
                [] -> do
                    let astmod = defaultModule
                        { moduleDefinitions = externs ++ globals 
                        }
                    M.withModuleFromAST ctx astmod $ \mod -> do
                        BS.putStrLn =<< M.moduleLLVMAssembly mod
                        withModuleKey es $ \modKey ->
                            addModule cl modKey mod
                x -> do
                    let astmod = defaultModule
                        { moduleDefinitions = externs ++ globals ++ [mainFn]
                        }
                    M.withModuleFromAST ctx astmod $ \mod -> do
                        BS.putStrLn =<< M.moduleLLVMAssembly mod
                        withModuleKey es $ \modKey ->
                            withModule cl modKey mod $ do
                                res <- (\mangled -> findSymbol cl mangled False) =<< mangleSymbol cl mainName
                                case res of
                                    Left _ -> putStrLn ("Couldn't find: " ++ show mainName)
                                    Right (JITSymbol fn _)-> do
                                        run $ castPtrToFunPtr (wordPtrToPtr fn)

独立的模块,例如这个打印语句,可以正确运行。具有主要功能的模块在执行后从 JIT 中删除:

print(234);

; ModuleID = '<string>'
source_filename = "<string>"

@0 = constant [4 x i8] c"%d\0A\00"

declare i32 @printf(i8*, ...)

define void @main.0() {
entry:
  %0 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i32 0, i32 0), i32 234)
  ret void
}

234

将 4 分配给符号 'x' 会产生一个带有全局变量的模块,该模块不会从 JIT 中删除:

x := 4;

; ModuleID = '<string>'
source_filename = "<string>"

@x = global i32 4

但是尝试在下一条语句中打印 'x' 会导致 main 函数的查找失败:

print(x);

; ModuleID = '<string>'
source_filename = "<string>"

@x = external global i32
@0 = constant [4 x i8] c"%d\0A\00"

declare i32 @printf(i8*, ...)

define void @main.0() {
entry:
  %0 = load i32, i32* @x
  %1 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @0, i32 0, i32 0), i32 %0)
  ret void
}

Couldn't find: "main.0"

跨模块访问符号时出现问题。

我尝试过的事情:

  • 访问函数而不是变量
  • 更改我的符号解析器以使用“findSymbol”而不是 llvm-hs-examples 存储库中的“getSymbolAddressInProcess”。这会阻止任何模块运行。
  • 下载 llvm-hs-examples 存储库并运行“orc”示例。 这也导致了符号错误!
  • 在新的 Linux 安装上重新下载 haskell 工具链和 llvm/llvm-hs (9.0.1)。

如有任何帮助,我将不胜感激!

最佳答案

解决了!我对符号解析器感到困惑。它不用于在使用“findSymbol”时检索符号,而是用于 JIT 的编译和链接阶段。 'getSymbolAddressInProcess' 将仅搜索主机进程中的符号(例如 printf),而不是 JIT 中定义的符号(例如 x)。

为了在 JIT 中使用从另一个模块检索外部符号“x”并从主机进程检索“printf”的模块,必须添加一个符号解析器,它在 JIT 编译层和主机进程中搜索符号:

myResolver :: IRCompileLayer ObjectLinkingLayer -> SymbolResolver
myResolver ircl = SymbolResolver $ \mangled -> do
    symbol <- findSymbol ircl mangled False
    case symbol of
        Right _ -> return symbol
        Left _ -> do
            ptr <- getSymbolAddressInProcess mangled
            return $ Right $ JITSymbol
                { jitSymbolAddress = ptr 
                , jitSymbolFlags   = defaultJITSymbolFlags { jitSymbolExported = True }
                }

关于haskell - 无法在 LLVM OrcJIT 中跨模块访问符号,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61110265/

相关文章:

haskell - 在 `index-state` 文件中提及 `cabal.project` 的好处

c# - foreach 和泛型编译器问题

clang - 获取llvm中局部变量的实际值

ios - Apple 的 LLVM 中的优化错误,还是代码中的错误?

haskell - 这是 Haskell 类型推理的实际应用,还是其他什么?

Haskell 中带列表的字符串替换运算符

compiler-construction - 基于 LLVM 的编译器的语言

haskell - 在 Haskell 编译器中遍历类型抽象语法树

llvm - 从 Julia 脚本生成可运行的 LLVM IR?

haskell - 在 Haskell 中使用包装器的混合类型列表