Perché alcuni compilatori generano un codice macchina diretto?

4

Stavo seguendo questo corso: CMU 18-447, Computer Architecture presso Carnegie Mellon per spazzare le mie conoscenze e concetti.

Dicono che la maggior parte dei dettagli e delle implementazioni a livello macchina sono gestite a livello di Instruction Set Architecture (ISA) e sono astratte a quel livello.

Alcuni processori Intel hanno persino livelli di traduzione a livello di hardware che accettano l'ISA di livello frontale esposto al programmatore e li traducono più vicino alla macchina.

Dato che tale potere è fornito dall'ISA / Processore stesso, perché i compilatori generano il codice macchina diretto, o è solo una scatola nera e internamente utilizza gli assemblatori per convertirli in codice macchina diretto?

Ho sentito che JVM prende il codice byte e li traduce direttamente in codice macchina (exe). È vero o è la mia comprensione sbagliata qui?

    
posta Greedy Coder 17.12.2013 - 10:43
fonte

4 risposte

8

Trovo abbastanza chiaro questa definizione :

The Instruction Set Architecture (ISA) is the part of the processor that is visible to the programmer or compiler writer. The ISA serves as the boundary between software and hardware.

L'ISA fornisce un'astrazione della microarchitettura , che è l'implementazione delle istruzioni impostate dal processore.

Quindi quando si dice che il compilatore produce codice macchina, produce istruzioni per il set di istruzioni configurato, non l'effettivo processore, quindi qualsiasi processore che implementa quel set di istruzioni può eseguire quel codice macchina.

Una macchina virtuale come JVM o CLR esegue il codice che è stato compilato in bytecode specifico per l'architettura della macchina virtuale, codice bytecode Java e CIL in questo caso.

Il runtime di tale macchina virtuale, che a sua volta è costruito per essere eseguito su una specifica architettura del processore e sistema operativo, traduce il bytecode nel codice macchina e nelle chiamate API per la piattaforma su cui viene eseguito, e in genere lo fa just-in-time .

    
risposta data 17.12.2013 - 11:08
fonte
5

Some Intel processors even have hardware level translation layers that take the front level ISA that is exposed to the programmer and translate them further closer to the machine.

Tale concetto è chiamato microprogrammazione ed è stato in circolazione in teoria da Charles Babbage e in attuazione dagli anni '60. Alcuni processori non sono affatto microprogrammati e usano l'hardware per eseguire le istruzioni, altri sono interamente microprogrammati e alcuni adottano un approccio ibrido a seconda dell'istruzione che viene eseguita.

Given such power is provided by the ISA/Processor itself...

Quella potenza esiste nel processore ma non fornita . L'API per un processore è il suo set di istruzioni. La microprogrammazione, se presente, è un dettaglio di implementazione di cui non dovresti sapere né preoccuparti.

... why do compilers generate direct machine code, or is it just a black box and internally it uses assemblers to convert them into direct machine code?

Sono sicuro che ci sono alcuni compilatori che generano direttamente il codice oggetto, ma la maggior parte genera assemblaggi e poi li assemblano dietro le quinte. Assembly è solo una longhandable leggibile dall'uomo per il codice oggetto corrispondente che è molto più facile da generare e da usare per il debug o semplicemente capire cosa produce il compilatore. Le probabilità sono molto buone che se stai scrivendo un compilatore per un bersaglio, avrai già un assemblatore perfettamente funzionante (o un assemblatore incrociato) che è preferibile utilizzare per reinventare quella ruota da solo.

I hear that JVM takes byte code and translate them directly to machine code(exe).Is this true or is my understanding wrong here?

Le prime versioni della JVM erano essenzialmente emulatori che esaminavano ciascuna istruzione bytecode ed eseguivano le operazioni necessarie. Questo è molto simile a ciò che fa il microcodice, appena implementato in un software su un computer generico. I primi JVM erano anche molto lenti, il che ha portato all'uso di cose come la compilazione just-in-time di bytecode in istruzioni native.

Nessuna di queste cose, però, perché lingua franca per Java è bytecode Java. Questo non è diverso dal set di istruzioni su una CPU reale in quanto non ti interessa come è implementato sotto le copertine. Esistono, infatti, un certo numero di CPU chiamate "processori Java" che possono eseguire direttamente bytecode Java, una variante di ARM9 essendo una di queste. Il software Java non saprà o non si preoccuperà se viene eseguito su un processore Java o JVM.

    
risposta data 17.12.2013 - 15:15
fonte
2

I compilatori possono generare codice assembly, codice oggetto collegabile o codice macchina pronto all'uso; Ho lavorato con esempi di tutti e tre i tipi. Sebbene i compilatori che emettono codice assembly possano essere più adattabili a piattaforme diverse rispetto a quelli che generano il codice oggetto, l'output del codice assembly è spesso più lento dell'output del codice macchina e l'esecuzione di un programma richiederà l'ulteriore passaggio del codice assembly. In realtà non è molto più difficile per un compilatore generare codice macchina che generare codice assembly; in alcuni casi potrebbe essere più semplice.

Per quanto riguarda la generazione di codice collegabile o pronto per l'esecuzione, è spesso più veloce e più facile per un compilatore produrre quest'ultimo; l'unico vantaggio di produrre il primo è che la costruzione di un programma richiede la ricompilazione solo delle parti che sono state modificate e quindi il collegamento di tutto, invece di dover alimentare tutto attraverso il compilatore. Se i progetti saranno generalmente di grandi dimensioni e la porzione che cambia in una piccola build, quindi essere in grado di compilare in modo selettivo le parti del codice che sono state modificate potrebbe essere una "vittoria". Se, tuttavia, si desidera ricostruire la maggior parte del programma su ogni build, la produzione di un codice macchina eseguibile potrebbe essere più veloce rispetto a uno stage intermedio.

Un ulteriore vantaggio di produrre codice macchina eseguibile è che un compilatore può essere in grado di sfruttare le cose che conosce sulle posizioni degli oggetti in modi che non sarebbero possibili quando si genera codice rilocabile. Ad esempio, se foo e bar sono dichiarati in moduli separati, un'istruzione foo=bar; richiederebbe qualcosa del tipo:

    ldr r0,[pc+(_addr_of_bar-*)]
    ldr r1,[pc+(_addr_of_foo-*)]
    ldr r2,[r0]
    str r2,[r1]
 ...somewhere within 2K of the above:
 _addr_of_bar: dw _bar
 _addr_of_foo: dw _foo

Se le variabili foo e bar si verificano in corrispondenza di indirizzi noti che si trovano entro 4K l'uno dall'altro [ad es. 1600 byte a parte], il codice potrebbe essere semplificato a qualcosa di simile:

    ldr r0,[pc+(_addr_of_foo_bar-*)]
    ldr r1,[r0+600]
    str r1,[r0-1000]
 ...somewhere within 2K of the above:
 _addr_of_foo_bar: dw _foo+1000

Tali ottimizzazioni sono possibili solo se il compilatore sa come saranno posizionate le cose.

    
risposta data 25.12.2013 - 00:42
fonte
1

Si noti che alcuni compilatori stanno effettivamente generando direttamente il codice macchina, e alcuni di essi stanno persino generando codice macchina nello stesso processo che sta eseguendo il compilatore (un tipico esempio di quest'ultimo caso è SBCL implementazione di Common Lisp: ogni volta che si digita in modo interattivo un'espressione nel suo REPL , viene tradotto in codice macchina quindi eseguito).

Altri compilatori stanno generando qualche altra forma di codice. Potrebbero essere file di oggetti (contenenti in particolare un mix di codice macchina con direttive di ricollocazione per il linker), codice assemblatore , ecc ... Non è insolito per un compilatore di emette il codice C (che è ulteriormente compilato da qualche compilatore esistente C).

La terminologia " codice oggetto " potrebbe essere ambigua: la uso per la forma di output di qualsiasi compilatore (quindi C ++ il codice potrebbe essere il codice oggetto per me, in un compilatore che traduce in C ++). Alcune persone limitano il "codice oggetto" ai file oggetto (ad esempio file ELF con informazioni sulla rilocazione).

Maggiori informazioni sulla compilazione JIT (considera l'utilizzo di alcune librerie JIT come asmjit , libjit , LLVM , GCCJIT , ...) e optimizing compilers .

La cosa interessante oggi è che i computer sono abbastanza veloci oggi per rendere ragionevole il seguente scenario: si codifica un compilatore in C e un REPL interattivo che legge alcune espressioni, compilandolo al codice C (spesso alcune centinaia di righe) , genera una compilazione di quel codice C generato in un plugin, quindi finalmente carica dinamicamente che ha generato il plugin (usando dlopen ...) è abbastanza veloce da essere accettabile dal tuo utente.

Si noti inoltre che l'ottimizzazione è sempre più richiesta (per trarre vantaggio dai processori correnti) e che è molto più costoso dell'emettere C -or assembler-code e di eseguire il tranlating. (Se generi codice C, sfrutti le capacità di ottimizzazione del tuo compilatore C).

    
risposta data 14.04.2016 - 11:00
fonte

Leggi altre domande sui tag