Использование GHC API для компиляции исходников Haskell в CORE и CORE в двоичный файл

Идея

Здравствуйте! Я хочу создать программу, которая будет генерировать ядро ​​Haskell и использовать GHC API для дальнейшей компиляции в исполняемый файл. Но прежде чем я сделаю это, я хочу построить очень простой пример, показывающий, как мы можем просто скомпилировать исходники Haskell в CORE, а затем в двоичный файл.

Эта проблема

Я прочитал много документации и попробовал много методов от GHC Api, но пока безуспешно. Я начал с официального ознакомления с GHC Api и успешно скомпилировал примеры. В примерах показано использование следующих функций: parseModule, typecheckModule, desugarModule, getNamesInScope а также getModuleGraph но не охватывает последний этап компиляции. С другой стороны, в 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, а затем компилировать ядро ​​в конечный исполняемый файл (используя GHC API). Промежуточный шаг необходим для дальнейшей замены на пользовательский CORE.

В качестве дополнительного вопроса - возможно ли в настоящее время предоставить GHC внешние файлы ядра, или эта функция еще не реализована, и мне придется создавать ядро ​​вручную, используя GHC.Api (связано с: Компиляция в 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 ()

2 ответа

Генерация текстового представления ядра здесь не проблема, потому что это можно сделать несколькими способами. Ты можешь использовать -fext-core флаг для генерации .hcr файлы и работать с ними, например, с помощью extcore. Существуют также другие пакеты, которые могут создавать дамп ядра, например, ghc-core или ghc-core-html.

Основной проблемой здесь является загрузка ghc-core в ghc, Насколько я знаю, это было поддержано, но сейчас нет, потому что было мало интереса к его использованию, и со временем оно устарело.

Лучшее, что мы можем попробовать здесь, это копать больше в ghc внутренности, найдите места, где используется ghc-core, и попробуйте изменить его там. Может быть, мы также можем попробовать создать плагин GHC и изменить ядро ​​с ним.

Краткий ответ: как только вам понадобится объектный файл, вы используете компилятор c по вашему выбору для компиляции главной заглушки и ссылки на исполняемый файл.

Если у вас есть объектный файл, то последние шаги, которые должен выполнить GHC, выполняются в компоновщике и компиляторе C. Например, установив флаг -verbose и -keep-tmp-files для простого hello_world, последние три шага для меня после создания объектов были следующими:

'/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,[],[])"

Что ж, это немного хуже, но похоже, что это список флагов компоновщика с каким-то gobbledygook, который был передан в GHC в конце. Я не уверен, что все то, что компоновщик не определил, и просмотр флагов компоновщика будет вашей самой большой домашней работой. Вам придется изменить эти флаги? Возможно, а может быть, только если зависимости изменятся.

Другие вопросы по тегам