Cos'è un compilatore?

Donna che lavora da casa al computer

Autori

Josh Schneider

Staff Writer

IBM Think

Ian Smalley

Staff Editor

IBM Think

Cos'è un compilatore?

Un compilatore è un tipo di programma per computer che converte il codice da un linguaggio di programmazione (il linguaggio di origine) in un altro linguaggio di programmazione (il linguaggio di destinazione).

I compilatori sono utilizzati per trasformare il codice sorgente di alto livello in codice target di basso livello (come linguaggio assembly, codice oggetto o codice macchina) preservando la funzionalità del programma.

I compilatori, uno strumento critico per la programmazione informatica moderna e pratica, consentono ai programmatori di lavorare con codice di alto livello leggibile dall'uomo e quindi di convertire il loro codice sorgente in codice target eseguibile. I compilatori aiutano anche gli sviluppatori di software a creare programmi eseguibili efficienti con maggiore sicurezza, stabilità e portabilità. Questo perché i compilatori aiutano a identificare e risolvere gli errori, creando così applicazioni eseguibili portatili. 

Sebbene tutti i compilatori convertano il codice di alto livello in codice eseguibile di basso livello, per i diversi linguaggi di programmazione e le diverse applicazioni si utilizzano diversi tipi di compilatori. Ad esempio, un compilatore per diverse architetture viene utilizzato per produrre codice per un tipo di CPU o sistema operativo diverso da quello su cui è in esecuzione.

Quando il compilatore ideale non è disponibile o non è ancora stato creato, viene utilizzato un compilatore di bootstrapping temporaneo per compilare un compilatore più permanente che è meglio ottimizzato per la compilazione di qualsiasi linguaggio di programmazione specifico.

Un breve elenco di altri software correlati include:

  • I decompilatori funzionano come compilatori inversi e convertono il codice di basso livello in linguaggi di alto livello.
  • I compilatori da sorgente a sorgente (o transpiler) convertono il codice di alto livello in altri linguaggi di alto livello.
  • I language rewriter convertono le espressioni di codice formale in forme diverse senza modificare il linguaggio.
  • I compiler-compiler sono utilizzati per realizzare compilatori generici e riutilizzabili o componenti di compilatori che possono essere incorporati in scopi più specifici legati a un progetto.  

Le ultime notizie nel campo della tecnologia, supportate dalle analisi degli esperti

Resta al passo con le tendenze più importanti e interessanti del settore relative ad AI, automazione, dati e oltre con la newsletter Think. Leggi l' Informativa sulla privacy IBM.

Grazie per aver effettuato l'iscrizione!

L'abbonamento sarà fornito in lingua inglese. Troverai un link per annullare l'iscrizione in tutte le newsletter. Puoi gestire i tuoi abbonamenti o annullarli qui. Per ulteriori informazioni, consulta l'Informativa sulla privacy IBM.

Come funzionano i compilatori

In pratica, usare un compilatore può essere semplice come inserire un comando in una riga di comando in qualsiasi sistema Linux (o equivalente), specificando il file eseguibile del compilatore e i file sorgente da compilare. Questo comando indica al sistema di elaborare il codice sorgente, compilandolo in un codice macchina di destinazione e ottenendo i file oggetto necessari per produrre un programma eseguibile. 

Compilatori open-source come GNU Compiler Collection (GCC), una robusta raccolta di compilatori C comunemente utilizzata per compilare il codice C in programmi C, o l'alternativa Clang, sono disponibili su repository come GitHub. Altri compilatori possono essere installati liberamente o acquistati da una vasta gamma di distributori. Possono anche essere integrati nei più diffusi ambienti di sviluppo integrati (IDE) che raggruppano varie utility per lo sviluppo di software, tra cui editor di testo, documentazione API e strumenti di debug.     

Indipendentemente dal compilatore specifico utilizzato, il processo di compilazione del codice comporta il passaggio del codice sorgente attraverso vari livelli di analisi, ottimizzazione e infine generazione del codice. Il codice sorgente passa attraverso i diversi livelli analitici in modo sequenziale e viene valutato in ogni fase del processo.

Se il compilatore riconosce qualche problema con il codice sorgente, potrebbe restituire un messaggio di errore, invitando gli sviluppatori a risolvere gli errori identificati prima di procedere con la compilazione del resto del codice. In genere, i compilatori procedono attraverso le seguenti fasi:

  1. Analisi lessicale: la prima fase della compilazione passa il codice sorgente attraverso il lexer del compilatore, un programma che trasforma i caratteri in unità linguistiche significative, come parole chiave, identificatori e operatori noti. Queste unità sono conosciute collettivamente come token. Questa fase prepara essenzialmente il codice sorgente per la fase successiva, convertendo gli elementi significativi e importanti del codice sorgente in token con cui il compilatore può lavorare. 
  2. Analisi della sintassi: la seconda fase del processo di compilazione invia i token dal lesser al parser del compilatore. Un parser è un programma che verifica la presenza di errori sintattici nel codice e garantisce che il codice sorgente segua correttamente le regole del linguaggio sorgente. Se il parser non rileva errori durante l'analisi, genera una rappresentazione astratta della struttura complessiva del codice chiamata Abstract Syntax Tree (AST).
  3. Analisi semantica: dopo la verifica della sintassi del codice, un compilatore esegue un'analisi semantica sul codice analizzato per dedurre la funzione prevista del codice sorgente. In questa fase, il compilatore esegue dei controlli per rilevare gli errori logici, come variabili non dichiarate o uso non corretto degli operatori.
  4. Ottimizzazione: sebbene non sia necessariamente indispensabile per produrre codice funzionante, l'ottimizzazione è un passaggio opzionale comune a molti compilatori per migliorare le prestazioni complessive del codice compilato. L'ottimizzazione può identificare e rimuovere il codice non necessario e portare a programmi più veloci, efficienti e stabili, oltre ad abbreviare il processo di debug finale. 
  5. Generazione del codice: nella fase finale del processo, il compilatore converte l'AST in codice leggibile dalla macchina. L'output finale della generazione del codice è un codice in linguaggio assembly che può essere convertito in codice binario ed eseguito dal sistema informatico. 
AI Academy

Prepararsi all'AI con l'hybrid cloud

Condotto dai migliori leader di pensiero di IBM, il programma di studi è stato progettato per aiutare i dirigenti aziendali ad acquisire le conoscenze necessarie per dare priorità agli investimenti in AI che possono favorire la crescita.

Struttura del compilatore a tre fasi

Alcuni compilatori potrebbero non aderire strettamente alla struttura descritta in precedenza. Tuttavia, mentre alcuni compilatori possono contenere più o meno passaggi, tutte le fasi della compilazione possono essere attribuite a una di queste tre fasi: un front-end, un middle-end e un back-end.

Questa struttura in tre fasi consente ai compilatori di adottare un approccio modulare. Consente di combinare più front-end per linguaggi diverse con backend per CPU diverse, il tutto condividendo le funzionalità dei vari middle-end applicabili.

Le tre fasi di un compilatore comportano la seguente distribuzione:

  1. Front-end: il front-end di un compilatore comprende aspetti di analisi lessicale, analisi sintattica e analisi semantica. Questa fase verifica la sintassi e la semantica secondo le regole del linguaggio di origine e può identificare e individuare gli errori nel codice sorgente. Supponendo che non vengano trovati errori, il front-end del compilatore converte il codice sorgente in una rappresentazione intermedia (IR), ovvero una conversione temporanea di livello inferiore del codice sorgente, per il middle-end. 
  2. Middle-end: la fase intermedia di un compilatore esegue varie ottimizzazioni del codice sull'IR, indipendentemente dall'architettura della CPU a cui si rivolge il processo di compilazione complessivo. Eseguendo le ottimizzazioni sul codice sorgente indipendentemente dal codice macchina di destinazione, il compilatore può applicare ottimizzazioni generalizzate in grado di migliorare le prestazioni del codice in diverse versioni. Questi miglioramenti possono essere apportati indipendentemente dal linguaggio specifico supportato o dall'architettura hardware. 
  3. Back-end: la fase di back-end utilizza l'output della fase intermedia e potrebbe eseguire altre ottimizzazioni e conversioni specifiche della CPU. In questa fase finale del processo di compilazione, il compilatore genera un codice assembly dipendente dal target, incluse le allocazioni dei registri e la pianificazione delle istruzioni. La fase di back-end produce in genere codice macchina specializzato per i sistemi operativi e l'hardware di destinazione. 

Benefici dell'utilizzo di un compilatore

Sebbene i compilatori non siano esplicitamente necessari per produrre codice funzionante, l'ampia varietà e complessità dei linguaggi di codifica e degli ambienti macchina rendono i compilatori una necessità pratica per la creazione di software eseguibile. Questi sono i quattro principali benefici dell'utilizzo dei compilatori software.

Facilitare la codifica del linguaggio di alto livello

I linguaggi di programmazione di alto livello utilizzano sintassi e parole chiave più vicine ai linguaggi parlati, il che li rende molto più facili da usare per gli sviluppatori. I compilatori convertono questo codice leggibile dall'uomo nel codice macchina più complesso necessario per eseguire applicazioni software ottimizzate.

Alcuni esempi di linguaggi di alto livello includono i seguenti linguaggi:

  • Python (utilizzato per lo sviluppo web, data science e altri)
  • Java™ (utilizzato per lo sviluppo di Android, applicazioni aziendali e altro)
  • C++ (utilizzato per lo sviluppo di giochi, sistemi operativi e altro)
  • JavaScript (utilizzato per lo sviluppo web dinamico e interattivo)
  • PHP (utilizzato per lo scripting lato server nello sviluppo web)
  • C# (utilizzato per applicazioni Windows, sviluppo di giochi con motore Unity)
Ridure le ripetizioni

I compilatori consentono di migliorare l'efficienza convertendo il codice di alto livello in codice macchina eseguibile. L'output del compilatore viene memorizzato tramite un file con estensione .exe che viene poi eseguito direttamente da un computer. Grazie al compilatore, la scrittura di un programma eseguibile diventa un compito da svolgere una sola volta.

Una volta completato, il codice compilato può essere eseguito tutte le volte che è necessario. Questo processo aiuta generalmente i programmi a funzionare in modo più rapido ed efficiente, poiché determinate applicazioni o parti di applicazioni possono essere eseguite separatamente dalle attività di runtime del software.

Migliorare la portabilità

Non tutti i sistemi possono eseguire tutti i tipi di codice di programmazione. I compilatori vengono utilizzati per convertire i tipi di codice preferiti dagli sviluppatori per l'utilizzo nei tipi di codice necessari per il funzionamento dei sistemi. In questo modo, i compilatori migliorano la portabilità del programma convertendo il software in un'ampia varietà di linguaggi compatibili che possono essere facilmente memorizzati, trasferiti ed eseguiti in vari sistemi operativi e architetture hardware.

Promuovere l'ottimizzazione generale

Durante il processo di compilazione, i compilatori possono essere utilizzati per identificare e risolvere errori e difetti del software, ottenendo programmi più stabili e meglio ottimizzati. I compilatori possono inoltre contribuire a migliorare la sicurezza del software prevenendo errori legati alla memoria, come gli overflow del buffer, e generando avvisi se vengono rilevati potenziali problemi di memoria. 

Compilatori e interpreti a confronto

Mentre i compilatori vengono utilizzati per convertire il codice sorgente in codice macchina eseguibile, gli interpreti sono un altro tipo di programma in grado di fornire funzionalità simili, sebbene attraverso un meccanismo diverso.

Invece di convertire il codice sorgente, gli interpreti eseguono direttamente il codice sorgente o utilizzano un codice intermedio noto come bytecode, una rappresentazione di basso livello, indipendente dalla piattaforma, del codice sorgente. Il bytecode funge da intermediario tra il codice sorgente leggibile dall'uomo e il codice macchina, progettato per l'esecuzione da una macchina virtuale (VM) anziché direttamente sull'hardware di un computer. 

In teoria, qualsiasi linguaggio di programmazione può essere eseguito sia con un compilatore che con un interprete. Tuttavia, i singoli linguaggi di programmazione tendono a essere più adatti alla compilazione o all'interpretazione.

Nella pratica, la distinzione tra i linguaggi dei compilatori e i linguaggi degli interpreti può talvolta confondere, proprio come la distinzione tra i compilatori e gli interpreti stessi, in quanto entrambi i tipi di programmi possono avere caratteristiche che si sovrappongono. Mentre alcuni linguaggi sono più comunemente compilati e altri più comunemente interpretati, è possibile scrivere un compilatore per un linguaggio comunemente interpretato e viceversa.

I linguaggi di alto livello vengono in genere creati pensando a un tipo di conversione, compilazione o interpretazione, ma questi sono più suggerimenti che limitazioni rigorose. Ad esempio, BASIC viene spesso definito un linguaggio interpretato e C un linguaggio compilato, tuttavia esistono compilatori per BASIC così come esistono interpreti per C. 

La differenza principale tra interpreti e compilatori sta nelle tempistiche e nell'ottimizzazione. Entrambi i tipi di programmi cercano di convertire il codice sorgente in un codice target che sia prima funzionale e poi ottimizzato.

A seconda dell'ambiente operativo, il codice compilato o interpretato potrebbe essere più adatto per essere eseguito in modo efficiente, tenendo conto delle funzionalità hardware, della memoria e della capacità di storage. A seconda dei vincoli di un programma, di un'applicazione e di un hardware specifici, la compilazione, l'interpretazione o una combinazione di entrambi potrebbero dare i risultati migliori. 

Per questo motivo, l'interpretazione non può sostituirsi completamente alla compilazione, ma può spostare i compiti di compilazione in secondo piano attraverso un processo di conversione graduale. I compilatori utilizzano una strategia di conversione anticipata (AOT) che converte interamente il codice sorgente in codice di destinazione prima ancora di creare un file eseguibile.

Gli interpreti eseguono invece il codice direttamente come richiesto da un'applicazione o utilizzano il bytecode come intermediario per generare il codice sorgente eseguibile della macchina virtuale. In questo modo, gli interpreti potrebbero fornire una certa accelerazione o flessibilità, ma a un certo punto è necessario fornire una serie di istruzioni macchina eseguite direttamente verso la fine dello stack di esecuzione.

In alcuni casi, quando l'efficienza leggera è una priorità, gli interpreti speciali possono essere preferibili ai compilatori per la capacità di eseguire la conversione JIT (Just-In-Time). La JIT è una strategia che compila parti di codice sorgente in codice di destinazione in un buffer di memoria per l'esecuzione immediata. L'interpretazione JIT compila il codice on demand, combinando l'efficienza di compilazione una tantum di un compilatore tradizionale con la flessibilità di eseguire ripetutamente il codice, spesso più velocemente rispetto agli interpreti di bytecode standard.

Tuttavia, con l'aumento delle tendenze moderne verso la compilazione JIT e l'interpretazione del bytecode dipendente dalla situazione, molti compilatori sono stati progettati per offrire sia caratteristiche di compilazione che di interpretazione. Questa sovrapposizione confonde ulteriormente i confini tra queste due categorie. 

Soluzioni correlate
IBM Cloud Infrastructure Center 

IBM Cloud Infrastructure Center è una piattaforma software compatibile con OpenStack per gestire l'infrastruttura di cloud privati su IBM zSystems e IBM LinuxONE.

Esplora Cloud Infrastructure Center
Soluzioni per l'infrastruttura IT

Scopri i server, lo storage e il software progettati per l'hybrid cloud e la strategia AI della tua azienda.

Scopri le soluzioni per le infrastrutture IT
Soluzioni per l'infrastruttura cloud

Trova la soluzione di infrastruttura cloud adatta alle esigenze della tua azienda e scala le risorse on-demand.

Soluzioni cloud
Fai il passo successivo

Trasforma la sua infrastruttura aziendale con l'hybrid cloud e le soluzioni pensate per l'AI di IBM. Scopri i server, lo storage e i software progettati per proteggere, scalare e modernizzare la tua azienda o ascolta i pareri degli esperti per migliorare la tua strategia di AI generativa.

Scopri le soluzioni per le infrastrutture IT Scarica l'ebook