2015-01-21 13 views
19

L'ideaUtilizzando GHC API per compilare sorgenti Haskell a Core e Core a binario

Ciao! Voglio creare un programma, che genererà Haskell Core e userà GHC API per compilarlo ulteriormente in un eseguibile. Ma prima di farlo, voglio costruire un esempio molto semplice, mostrando come possiamo semplicemente compilare i sorgenti di Haskell in CORE e poi nel file binario.

Il problema

Ho letto un sacco di documentazione e provato molti metodi da GHC Api, ma per ora senza successo. Ho iniziato con Official GHC Api introduction e ho compilato correttamente gli esempi. Gli esempi mostrano l'utilizzo delle seguenti funzioni: parseModule, typecheckModule, desugarModule, getNamesInScope e getModuleGraph ma non copre la fase di compilazione finale. D'altra parte, ci sono alcune funzioni nell'API, i cui nomi sembrano correlati al problema, ad esempio HscMain.{hscCompileOneShot, hscCompileBatch} o GHC.{compileToCoreModule, compileCoreToObj}. Ho cercato di usarle, ma si verificano errori di esecuzione, come in questo esempio:

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() 

che può essere compilato con ghc -package ghc Main.hs e che provoca il seguente errore durante il runtime:

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

che naturalmente può essere il risultato di un utilizzo API errato, in particolare, a causa della riga compileCoreToObj False coreMod "foo" "bar", dove la stringa è solo casuale, perché la documentazione non dice molto su di essi. Se esaminiamo le fonti, sembra che il primo sia il nome dell'output e il secondo sia "extCore_filename", qualunque esso sia.

Un'altra cosa preoccupante è il commento nella documentazione accanto alla funzione compileCoreToObj:

[...] Questo è solo finora stato testato con un singolo modulo autonomo.

Ma spero che non introduca ulteriori problemi.

La domanda

Qual è il miglior modo possibile per creare questa soluzione? Come possiamo creare un esempio di lavoro minimale, che caricherà i sorgenti di haskell, li compilerà nel CORE e quindi compilerà il core per l'eseguibile finale (usando l'API GHC). Il passaggio intermedio è necessario per un'ulteriore sostituzione da parte del CORE personalizzato.

Come un lato-domanda - è attualmente possibile fornire GHC con file principali esterni o questa funzione non è ancora implementata e dovrà costruire Core manualmente, utilizzando GHC.Api (relative a: Compiling to GHC Core)

Aggiornamento

sono stato finalmente in grado di creare un piccolo esempio che permette il caricamento di un modulo e compilarlo per .hi e .o file.Questa non è una soluzione per il problema, perché non mi permette di sostituire il centro e non collegare i file oggetto in eseguibili ancora:

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() 
+0

Gli sviluppatori della rete [mailing list GHC] (https://www.haskell.org/mailman/listinfo/ghc-devs) sono probabilmente quelli con le risposte che stai cercando. – gxtaillon

+1

Credo che ci sia stato recentemente un lavoro su come consentire ai plugin di effettuare passaggi di trasformazione core-to-core all'interno della pipeline di GHC; non esattamente quello che vuoi però. Forse potresti spiegare brevemente perché vuoi fare questo preciso flusso di lavoro? –

+1

@ChristianConkle Non voglio il plugin 'CORE -> CORE'. Sto creando il mio linguaggio personalizzato e voglio compilarlo al core e quindi utilizzare la pipeline di GHC. Scusa per non essere chiaro. –

risposta

-2

Risposta breve: una volta che avete per oggetto file si utilizza il c compilatore di tua scelta per compilare uno stub principale e collegarlo a un eseguibile.

Se avete il file oggetto, poi gli ultimi passi che GHC avrebbe fatto sono fatto in linker e un compilatore C. Ad esempio, impostando il flag -verbose e -Tenere-TMP-files per un semplice hello_world, gli ultimi tre passaggi per me, dopo aver costruito gli oggetti, sono stati:

'/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' 

Guardando quelle prime due file rivela che il file c è solo:

#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); 
} 

Non sembra che dovrebbe cambiare molto da un progetto all'altro.

Il file assembly è:

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

Beh, questo è un po 'peggio, ma sembra che questi sono un elenco di bandiere linker con un po' burocratese che è stato passato in GHC alla fine. Non sono sicuro che cosa sia il tutte le cose che il linker non definisce, e guardare le bandiere del linker sarà il tuo compito più importante. Dovrai modificare queste bandiere? Forse, e forse solo se cambiano le dipendenze.

+0

Per quanto riguarda l'altra parte della tua domanda, sostituendo il CORE con un altro, probabilmente sei sfortunato, e non sono nemmeno abbastanza sicuro di quale sia il tuo obiettivo. – Llamadonica

+0

hmm, ok, forse questo è il modo per andare a collegare manualmente il file oggetto, ma spero che GHC abbia alcune routine per farlo per noi. Voglio dire - a volte questo potrebbe cambiare e sostenerlo sarebbe molto più facile se fosse gestito da ghc. @Llamadonica Voglio compilare la mia lingua personalizzata su CORE e quindi procedere con la normale pipeline di GHC. –

5

Generazione rappresentazione testuale del nucleo non è un problema, perché può essere fatto in diversi modi. Puoi utilizzare il flag -fext-core per generare i file .hcr e lavorare con loro utilizzando ad es. extcore. Esistono anche altri pacchetti che possono eseguire il dump di core, ad esempio ghc-core o ghc-core-html.

Il problema principale qui, sta caricando ghc-core in ghc. Per quanto ne so, è stato supportato, ma ora non lo è, perché c'era scarso interesse ad usarlo e divenne obsoleto nel tempo.

La cosa migliore che possiamo provare qui è scavare più in interni ghc, trovare luoghi in cui viene utilizzato ghc-core e provare a modificarlo lì. Forse possiamo anche provare a creare un ghc plugin e modificare il core con esso.

+0

Non mi interessa creare il core testuale, perché voglio generarlo è AST. Il caricamento del core esterno su GHC potrebbe essere una soluzione, ma come hai scritto non viene più mantenuto - la stessa risposta che ho ricevuto sulla mailing list.Sono ancora molto interessato a ottenere la soluzione "reale", ma per ora farò cadere la taglia qui, perché il tempo è finito :( –