Come evitare di generare codice prolisso durante la compilazione da una lingua di livello superiore ad una inferiore?

3

Mi scuso in anticipo per il vago titolo. Non volevo renderlo eccessivamente prolisso, quindi permettimi di spiegarlo più approfonditamente qui sotto:

Attualmente sto sviluppando un linguaggio strong, tipizzato staticamente, che compila fino al codice C ++ (11). Utilizzando le risposte da come la raccolta di dati inutili funziona in lingue che sono compilate in modo nativo? come guida, ho sviluppato un semplice sistema di runtime (scritto in C ++ ovviamente) per il mio linguaggio che consiste essenzialmente in un garbage collector.

Il modo in cui ho progettato il sistema GC è che ho creato per la prima volta un oggetto base che tutti gli altri oggetti che rappresentano specifici tipi di dati nella mia lingua (che attualmente includono solo int egers e bool eans) erediterebbero da. Il GC stesso utilizza un algoritmo di conteggio dei riferimenti di base, che accede al campo che contiene il numero di riferimenti forniti da ciascun oggetto dalla classe base sopra menzionata.

Lo sviluppo del sistema di runtime è andato benissimo. Tuttavia, ho appena realizzato un po 'di problemi. Perché devo avvolgere tutti i tipi di dati dalla mia lingua nei loro rispettivi oggetti, questo rende il codice C ++ che ho generato estremamente prolisso e goffo per qualcosa di più grande delle semplici espressioni. Ad esempio, supponiamo di avere l'espressione 1 + 2 * (4 - 5) - 6 / (7 + 8) . Questo sarebbe più o meno transpilato al seguente codice C ++ dal mio compilatore:

*new Integer(1) + *new Integer(2) * (*new Integer(4) - *new Integer(5)) - *new Integer(6) / (*new Integer(7) - *new Integer(8));

Come si può chiaramente vedere, il codice C ++ che è stato generato è estremamente prolisso rispetto all'espressione originale scritta nella mia lingua. Potresti immaginare quanto peggio sarebbe cercare espressioni ancora più complesse.

La mia domanda è: come dovrebbe essere affrontato un problema come questo? Questo è semplicemente un problema creato dalla mia inesperienza nella creazione di un sistema runtime, o è qualcosa che normalmente si verifica durante la compilazione? Ovviamente questo non è un "problema" nel senso che mi sta impedendo di continuare a sviluppare il mio compilatore, ma dal momento che voglio che il mio compilatore generi codice read-able C ++, questo dovrebbe essere qualcosa che è risolto.

Una soluzione che ho pensato a questo problema è utilizzare un metodo come codice indirizzo tre per rompere grandi espressioni in parti gestibili, ma prima di implementarlo vorrei capire se questo problema ha una soluzione migliore.

    
posta Christian Dean 04.01.2018 - 22:55
fonte

2 risposte

9

How should a problem like this be dealt with?

In generale, non dovrebbe. Ti interessa quanto sia leggibile l'assemblaggio generato dal tuo compilatore C ++?

E lo fai assicurandoti che la tua lingua supporti abbastanza (anche se è un passaggio alla lingua di livello inferiore) che nessuno guarda il codice compilato.

Le ottimizzazioni possono aiutare (ma rallentare il compilatore). Il codice di indirizzo tre può aiutare un po 'l'ottimizzazione, ma è solo mescolare le sedie a sdraio per quanto riguarda il codice generato. Alcuni nomi di formattazione e intelligenza possono aiutare, ma non modificare in modo significativo la struttura del codice generato.

Ma alla fine, se la tua lingua di partenza è più espressiva della lingua di destinazione, il codice generato sarà più disordinato poiché hai bisogno di più espressioni per rappresentare le stesse informazioni.

    
risposta data 05.01.2018 - 03:40
fonte
5

Non ho mai visto un sistema di generazione del codice che producesse codice leggibile. Tutti i risultati che ho visto erano brutti e prolissi. Quindi saresti una specie di pioniere; -).

Ma oltre alla leggibilità, vorrei suggerire una modifica che potrebbe migliorare l'efficienza.

Nel codice generato, crei molti nuovi oggetti per i valori interi letterali e lo ripeti ogni volta che esegui quell'espressione.

Che ne pensi di introdurre un pool letterale ? Poi ci sono tutti gli interi e gli oggetti booleani corrispondenti a tutti i valori trovati letteralmente nel codice della tua lingua sorgente. Quindi l'espressione C ++ può fare riferimento a questi oggetti come costanti preesistenti (come INT_1 ) invece di creare nuovi numeri interi.

Poiché i valori di quel pool devono essere disponibili per l'intera durata del programma, puoi e dovresti escluderli dalla procedura di Garbage Collection.

E forse puoi fare un ulteriore passo: usa primitive C ++

Non posso dire dallo snippet se gli operatori numerici che usi sono ancora quelli base di C ++ o quelli sovraccarichi delle tue classi. Se sono quelli di base, l'espressione C ++ potrebbe essere semplicemente 1 + 2 * (4 - 5) - 6 / (7 + 8) . Solo quando memorizzi quel risultato avresti bisogno di avvolgerlo in un numero intero, ad es. come new Integer(1 + 2 * (4 - 5) - 6 / (7 + 8)) , o sovraccaricando l'operatore di assegnazione.

Per ulteriori informazioni su questo approccio, dai un'occhiata ad es. Java boxing e unboxing di tipi primitivi.

    
risposta data 05.01.2018 - 10:59
fonte