Подтвердить что ты не робот

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

Идея

Здравствуйте! Я хочу создать программу, которая будет генерировать Haskell Core и будет использовать API GHC для дальнейшей компиляции в исполняемый файл. Но прежде чем я это сделаю, я хочу построить очень простой пример, показывая, как мы можем просто скомпилировать источники 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 и затем компилировать ядро ​​в окончательный исполняемый файл (используя API GHC). Промежуточный шаг необходим для дальнейшей замены пользовательским CORE.

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

Ответ 1

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

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

Самое лучшее, что мы можем попробовать здесь, - это больше узнать о внутренних элементах ghc, найти места, где используется ghc-core, и попытаться изменить его там. Возможно, мы также попробуем создать ghc plugin и изменить его с помощью ядра.

Ответ 2

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

Если у вас есть объектный файл, то последние шаги, которые GHC сделает, выполняются в компоновщике и компиляторе C. Например, установив флаг -verbose и -keep-tmp файлы для простого 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 в конце. Я не уверен, что все, что линкер не определяет, и глядя на флаги компоновщика, станет вашей самой большой домашней работой. Вам придется изменить эти флаги? Возможно, и, возможно, только в том случае, если зависимости меняются.