update - Uso de la API de GHC para compilar fuentes de Haskell en CORE y CORE en binario
peta haskell en español (2)
Generar una representación textual del núcleo no es un problema aquí, porque se puede hacer de múltiples maneras. Puede usar el -fext-core
para generar archivos .hcr
y trabajar con ellos, por ejemplo, extcore . También hay otros paquetes que pueden volcar el núcleo, como ghc-core o ghc-core-html .
El principal problema aquí es cargar ghc-core en ghc
. Por lo que sé, fue compatible pero ahora no, porque había poco interés en usarlo y se quedó obsoleto con el tiempo.
Lo mejor que podemos intentar aquí es profundizar más en los internos de ghc
, encontrar lugares donde se usa ghc-core e intentar modificarlo allí. Tal vez también podamos intentar crear un complemento de ghc y modificar el núcleo con él.
La idea
¡Hola! Quiero crear un programa, que generará Haskell Core y usará la API de GHC para compilarlo en un ejecutable. Pero antes de hacerlo, quiero construir un ejemplo muy básico que muestre cómo podemos simplemente compilar las fuentes de Haskell en CORE y luego en el archivo binario.
El problema
He leído mucha documentación y he probado muchos métodos de GHC Api, pero por ahora sin éxito. Comencé con la introducción oficial de GHC Api y compilé exitosamente los ejemplos. Los ejemplos muestran el uso de las siguientes funciones: parseModule
, typecheckModule
, desugarModule
, getNamesInScope
y getModuleGraph
pero no cubre el paso de compilación final. Por otro lado, hay algunas funciones en la api, cuyos nombres parecen relacionados con el problema, como HscMain. {HscCompileOneShot, hscCompileBatch} o GHC. {CompileToCoreModule, compileCoreToObj} . Intenté usarlos, pero recibo errores de tiempo de ejecución, como en este ejemplo:
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 ()
que se puede compilar con ghc -package ghc Main.hs
y que produce el siguiente error durante el tiempo de ejecución:
Main: panic! (the ''impossible'' happened)
(GHC version 7.8.3 for x86_64-unknown-linux):
expectJust mkStubPaths
que por supuesto puede ser el resultado de un uso incorrecto de la API, en particular, debido a la línea compileCoreToObj False coreMod "foo" "bar"
, donde la cadena es solo aleatoria, porque la documentación no dice mucho sobre ellos. Si observamos las fuentes, parece que el primero es el nombre de salida y el segundo es "extCore_filename", sea lo que sea.
Otra cosa preocupante es el comentario en la documentación al lado de la función compileCoreToObj
:
[...] Esto solo se ha probado hasta ahora con un único módulo autónomo.
Pero espero que no introduzca más problemas.
La pregunta
¿Cuál es la mejor manera posible de crear esta solución? ¿Cómo podemos crear un ejemplo de trabajo mínimo, que cargará las fuentes de haskell, las compilará en el CORE y luego compilará el núcleo al ejecutable final (utilizando la API de GHC)? El paso intermedio es necesario para su posterior reemplazo por CORE personalizado.
Como una pregunta adicional, ¿es posible actualmente proporcionar a GHC archivos centrales externos o esta función aún no está implementada y tendré que construir el Core manualmente, usando GHC.Api (relacionado con: Compilación a GHC Core )
Actualizar
Finalmente pude crear un pequeño ejemplo que permite cargar un módulo y compilarlo en archivos .hi
y .o
. Esta no es una solución para el problema, ya que no me permite reemplazar el CORE y aún no vincula los archivos de objetos a los ejecutables:
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 ()
Respuesta corta: una vez que tenga que objetar el archivo, use el compilador c de su elección para compilar un código auxiliar principal y vincularlo a un ejecutable.
Si tiene el archivo de objeto, entonces los últimos pasos que haría GHC se realizan en el enlazador y en un compilador de C. Por ejemplo, al establecer la marca -verbose y los archivos -keep-tmp para un simple hello_world, los últimos tres pasos para mí, después de construir los objetos, fueron:
''/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__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''
Mirar esos dos primeros archivos revela que el archivo c es simplemente:
#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);
}
No parece que eso deba cambiar mucho de un proyecto a otro.
El archivo de ensamblaje es:
.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__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,[],[])"
Bueno, eso es un poco peor, pero parece que esas son una lista de indicadores de vinculador con algunos errores que se pasaron a GHC al final. No estoy seguro de cuáles son todas las cosas que el enlazador no está definiendo, y mirar las banderas del enlazador será tu mayor tarea. ¿Tendrás que modificar esas banderas? Tal vez, y tal vez solo si las dependencias cambian.