使用GHC API将Haskell源代码编译成CORE,再将CORE编译成二进制文件。

20

想法

你好!我想创建一个程序,它将生成Haskell Core并使用GHC API将其进一步编译为可执行文件。但在此之前,我想构建一个非常基本的示例,展示如何将Haskell源代码编译成CORE,然后再编译成二进制文件。

问题

我已经阅读了很多文档,并尝试了许多来自GHC API的方法,但目前还没有成功。我从官方GHC API介绍开始,并成功编译了示例。这些示例展示了以下函数的使用:parseModuletypecheckModuledesugarModulegetNamesInScopegetModuleGraph,但并未涵盖最终的编译步骤。另一方面,API中有一些函数的名称看起来与问题相关,比如HscMain.{hscCompileOneShot, hscCompileBatch}GHC.{compileToCoreModule, compileCoreToObj}。我尝试使用它们,但会出现运行时错误,就像这个例子中一样:

import GHC
import GHC.Paths ( libdir )
import DynFlags
targetFile = "Test.hs"

main :: IO ()
main = do
   res <- example
   return ()

example = 
    defaultErrorHandler defaultFatalMessager defaultFlushOut $ do
      runGhc (Just libdir) $ do
        dflags <- getSessionDynFlags
        let dflags' = foldl xopt_set dflags
                            [Opt_Cpp, Opt_ImplicitPrelude, Opt_MagicHash]
        setSessionDynFlags dflags'
        coreMod <- compileToCoreModule targetFile
        compileCoreToObj False coreMod "foo" "bar"
        return () 

可以使用ghc -package ghc Main.hs编译,但在运行时会产生以下错误:

Main: panic! (the 'impossible' happened)
  (GHC version 7.8.3 for x86_64-unknown-linux):
    expectJust mkStubPaths

当然,这可能是错误的API使用的结果,特别是因为行 compileCoreToObj False coreMod "foo" "bar",其中字符串只是随机的,因为文档没有提供太多信息。如果我们查看源代码,似乎第一个是输出名称,第二个是“extCore_filename”,无论它是什么。

另一个令人担忧的事情是文档中对compileCoreToObj函数旁边的注释:

[...] 迄今为止,只测试了一个独立模块。

但我希望它不会引入任何进一步的问题。

问题

创建这个解决方案的最佳方法是什么?我们如何创建一个最小的工作示例,加载Haskell源代码,将其编译成CORE,然后将CORE编译成最终可执行文件(使用GHC API)。中间步骤需要进一步用自定义CORE替换。

作为一个副问题 - 目前是否可以向GHC提供外部core文件,或者这个功能尚未实现,我将不得不手动构建Core,使用GHC.Api(相关: Compiling to GHC Core

更新

我最终能够创建一个小例子,允许加载模块并将其编译为.hi.o文件。这不是问题的解决方案,因为它不允许我替换CORE,并且尚未将目标文件链接到可执行文件中:

import GHC
import GHC.Paths ( libdir )
import DynFlags
import Linker
import Module
targetFile = "Test.hs"

main :: IO ()
main = do
   res <- example
   return ()

example = 
    defaultErrorHandler defaultFatalMessager defaultFlushOut $ do
      runGhc (Just libdir) $ do
        dflags <- getSessionDynFlags
        let dflags2 = dflags { ghcLink   = LinkBinary
                             , hscTarget = HscAsm
                             }
        let dflags' = foldl xopt_set dflags2
                            [Opt_Cpp, Opt_ImplicitPrelude, Opt_MagicHash]
        setSessionDynFlags dflags'
        setTargets =<< sequence [guessTarget "Test.hs" Nothing]

        load LoadAllTargets
        return ()

ghc邮件列表的开发人员可能是您正在寻找答案的人。 - gxtaillon
1
我相信最近已经有关于允许插件在GHC管道内进行核心到核心的转换处理方面的工作,但这并不完全是您想要的。也许您可以简要解释一下为什么需要执行这个精确的工作流程? - Christian Conkle
1
@ChristianConkle 我不想要 CORE -> CORE 插件。我正在创建自定义语言,并希望将其编译到核心,然后使用 GHC 管道。抱歉表述不清楚。 - Wojciech Danilo
2个回答

5
生成核心的文本表示在这里并不是问题,因为它可以用多种方式制作。您可以使用 -fext-core 标志生成 .hcr 文件,并使用例如 extcore 来处理它们。还有其他可以转储核心的软件包,例如 ghc-coreghc-core-html
主要问题在于将 ghc-core 加载到 ghc 中。据我所知,它曾经得到支持,但现在已不再支持,因为人们对其使用的兴趣很低,并且随着时间的推移它变得过时了。
我们可以尝试的最好的方法是深入了解 ghc 的内部结构,找到使用 ghc-core 的地方,并在那里进行修改。也许我们还可以尝试创建一个 ghc 插件 并用它修改核心。

我对创建文本核心不感兴趣,因为我想生成它的AST。将外部核心加载到GHC可能是一个解决方案,但正如你所写的那样,它已经不再维护了 - 这也是我在邮件列表上得到的答案。我仍然非常有兴趣找到“真正”的解决方案,但现在我只能放弃这个赏金,因为时间已经结束 :( - Wojciech Danilo

-2
简短回答:一旦你有了目标文件,你可以使用你选择的C编译器编译一个主存根并将其链接成可执行文件。
如果你有目标文件,那么 GHC 最后要做的步骤都是在链接器和 C 编译器中完成的。例如,对于一个简单的 hello_world,通过设置 -verbose 标志和 -keep-tmp-files,我在构建对象之后的最后三个步骤是:
'/usr/bin/gcc' '-fno-stack-protector' '-Wl,--hash-size=31' '-Wl,--reduce-memory-overheads' '-c' '/tmp/ghc29076_0/ghc29076_0.c' '-o' '/tmp/ghc29076_0/ghc29076_0.o' '-DTABLES_NEXT_TO_CODE' '-I/usr/lib/ghc/include'
*** C Compiler:
'/usr/bin/gcc' '-fno-stack-protector' '-Wl,--hash-size=31' '-Wl,--reduce-memory-overheads' '-c' '/tmp/ghc29076_0/ghc29076_0.s' '-o' '/tmp/ghc29076_0/ghc29076_1.o' '-DTABLES_NEXT_TO_CODE' '-I/usr/lib/ghc/include'
*** Linker:
'/usr/bin/gcc' '-fno-stack-protector' '-Wl,--hash-size=31' '-Wl,--reduce-memory-overheads' '-o' 'hello' 'hello.o' '-L/usr/lib/ghc/base-4.6.0.1' '-L/usr/lib/ghc/integer-gmp-0.5.0.0' '-L/usr/lib/ghc/ghc-prim-0.3.0.0' '-L/usr/lib/ghc' '/tmp/ghc29076_0/ghc29076_0.o' '/tmp/ghc29076_0/ghc29076_1.o' '-lHSbase-4.6.0.1' '-lHSinteger-gmp-0.5.0.0' '-lgmp' '-lHSghc-prim-0.3.0.0' '-lHSrts' '-lffi' '-lm' '-lrt' '-ldl' '-u' 'ghczmprim_GHCziTypes_Izh_static_info' '-u' 'ghczmprim_GHCziTypes_Czh_static_info' '-u' 'ghczmprim_GHCziTypes_Fzh_static_info' '-u' 'ghczmprim_GHCziTypes_Dzh_static_info' '-u' 'base_GHCziPtr_Ptr_static_info' '-u' 'ghczmprim_GHCziTypes_Wzh_static_info' '-u' 'base_GHCziInt_I8zh_static_info' '-u' 'base_GHCziInt_I16zh_static_info' '-u' 'base_GHCziInt_I32zh_static_info' '-u' 'base_GHCziInt_I64zh_static_info' '-u' 'base_GHCziWord_W8zh_static_info' '-u' 'base_GHCziWord_W16zh_static_info' '-u' 'base_GHCziWord_W32zh_static_info' '-u' 'base_GHCziWord_W64zh_static_info' '-u' 'base_GHCziStable_StablePtr_static_info' '-u' 'ghczmprim_GHCziTypes_Izh_con_info' '-u' 'ghczmprim_GHCziTypes_Czh_con_info' '-u' 'ghczmprim_GHCziTypes_Fzh_con_info' '-u' 'ghczmprim_GHCziTypes_Dzh_con_info' '-u' 'base_GHCziPtr_Ptr_con_info' '-u' 'base_GHCziPtr_FunPtr_con_info' '-u' 'base_GHCziStable_StablePtr_con_info' '-u' 'ghczmprim_GHCziTypes_False_closure' '-u' 'ghczmprim_GHCziTypes_True_closure' '-u' 'base_GHCziPack_unpackCString_closure' '-u' 'base_GHCziIOziException_stackOverflow_closure' '-u' 'base_GHCziIOziException_heapOverflow_closure' '-u' 'base_ControlziExceptionziBase_nonTermination_closure' '-u' 'base_GHCziIOziException_blockedIndefinitelyOnMVar_closure' '-u' 'base_GHCziIOziException_blockedIndefinitelyOnSTM_closure' '-u' 'base_ControlziExceptionziBase_nestedAtomically_closure' '-u' 'base_GHCziWeak_runFinalizzerBatch_closure' '-u' 'base_GHCziTopHandler_flushStdHandles_closure' '-u' 'base_GHCziTopHandler_runIO_closure' '-u' 'base_GHCziTopHandler_runNonIO_closure' '-u' 'base_GHCziConcziIO_ensureIOManagerIsRunning_closure' '-u' 'base_GHCziConcziSync_runSparks_closure' '-u' 'base_GHCziConcziSignal_runHandlers_closure'

看一下那前两个文件,就会发现 c 文件只是:

#include "Rts.h"
extern StgClosure ZCMain_main_closure;
int main(int argc, char *argv[])
{
    RtsConfig __conf = defaultRtsConfig;
    __conf.rts_opts_enabled = RtsOptsSafeOnly;
   return hs_main(argc, argv, &ZCMain_main_closure,__conf);
}

看起来这似乎不应该在项目之间有太大的变化。

汇编文件是:

 .section .debug-ghc-link-info,"",@note
 .ascii "([\"-lHSbase-4.6.0.1\",\"-lHSinteger-gmp-0.5.0.0\",\"-lgmp\",\"-lHSghc-prim-0.3.0.0\",\"-lHSrts\",\"-lffi\    ",\"-lm\",\"-lrt\",\"-ldl\",\"-u\",\"ghczmprim_GHCziTypes_Izh_static_info\",\"-u\",\"ghczmprim_GHCziTypes_Czh_static    _info\",\"-u\",\"ghczmprim_GHCziTypes_Fzh_static_info\",\"-u\",\"ghczmprim_GHCziTypes_Dzh_static_info\",\"-u\",\"bas    e_GHCziPtr_Ptr_static_info\",\"-u\",\"ghczmprim_GHCziTypes_Wzh_static_info\",\"-u\",\"base_GHCziInt_I8zh_static_info    \",\"-u\",\"base_GHCziInt_I16zh_static_info\",\"-u\",\"base_GHCziInt_I32zh_static_info\",\"-u\",\"base_GHCziInt_I64z    h_static_info\",\"-u\",\"base_GHCziWord_W8zh_static_info\",\"-u\",\"base_GHCziWord_W16zh_static_info\",\"-u\",\"base    _GHCziWord_W32zh_static_info\",\"-u\",\"base_GHCziWord_W64zh_static_info\",\"-u\",\"base_GHCziStable_StablePtr_stati    c_info\",\"-u\",\"ghczmprim_GHCziTypes_Izh_con_info\",\"-u\",\"ghczmprim_GHCziTypes_Czh_con_info\",\"-u\",\"ghczmpri    m_GHCziTypes_Fzh_con_info\",\"-u\",\"ghczmprim_GHCziTypes_Dzh_con_info\",\"-u\",\"base_GHCziPtr_Ptr_con_info\",\"-u\    ",\"base_GHCziPtr_FunPtr_con_info\",\"-u\",\"base_GHCziStable_StablePtr_con_info\",\"-u\",\"ghczmprim_GHCziTypes_Fal    se_closure\",\"-u\",\"ghczmprim_GHCziTypes_True_closure\",\"-u\",\"base_GHCziPack_unpackCString_closure\",\"-u\",\"b    ase_GHCziIOziException_stackOverflow_closure\",\"-u\",\"base_GHCziIOziException_heapOverflow_closure\",\"-u\",\"base    _ControlziExceptionziBase_nonTermination_closure\",\"-u\",\"base_GHCziIOziException_blockedIndefinitelyOnMVar_closur    e\",\"-u\",\"base_GHCziIOziException_blockedIndefinitelyOnSTM_closure\",\"-u\",\"base_ControlziExceptionziBase_neste    dAtomically_closure\",\"-u\",\"base_GHCziWeak_runFinalizzerBatch_closure\",\"-u\",\"base_GHCziTopHandler_flushStdHan    dles_closure\",\"-u\",\"base_GHCziTopHandler_runIO_closure\",\"-u\",\"base_GHCziTopHandler_runNonIO_closure\",\"-u\"    ,\"base_GHCziConcziIO_ensureIOManagerIsRunning_closure\",\"-u\",\"base_GHCziConcziSync_runSparks_closure\",\"-u\",\"    base_GHCziConcziSignal_runHandlers_closure\"],[],Nothing,RtsOptsSafeOnly,False,[],[])"

嗯,这有点糟糕,但看起来那些是一些链接标志列表,以及在末尾传递给 GHC 的一些胡言乱语。我不确定连接器正在取消定义的所有内容,查看连接器标志将是您最大的功课。您是否需要修改这些标志?也许只有在依赖项发生更改时才需要。


至于你问题的另一部分,想要用另一个CORE替换它,你可能会很难实现,而且我甚至不确定你的目标是什么。 - Llamadonica
嗯,好的,也许手动链接对象文件是可行的方式,但我希望 GHC 有一些例程可以为我们完成这个过程。我的意思是 - 有时候这可能会改变,如果由 GHC 处理,支持它会更容易。@Llamadonica 我想将我的自定义语言编译成 CORE,然后继续使用正常的 GHC 流水线。 - Wojciech Danilo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接