Figura 1: La struttura di un compilatore

Documenti analoghi
Architettura dei calcolatori e sistemi operativi. Sottoprogrammi e MIPS. Espressioni algebriche. Capitolo 2 P&H

Calcolatori Elettronici A a.a. 2008/2009

Lezione4: MIPS e Istruzioni (1 Parte)

Macchine Astratte. Luca Abeni. February 22, 2017

Sottoprogrammi in linguaggio assembly del Motorola 68000

Argomenti Avanzati.! I puntatori! Stack! Visibilità delle Variabili

Ottenere una modifica del parametro attuale

Linguaggio C: le funzioni. Visibilità variabili e passaggio parametri

Allocazione Dinamica della Memoria

Calcolatori Elettronici Lezione A4 Programmazione a Moduli

FRAMEWORK PER L'ESECUZIONE DI APPLICAZIONI ASSEMBLY SUL MODELLO VERILOG DI UN SISTEMA DI ELABORAZIONE BASATO SUL PROCESSORE emips-sc

Il set istruzioni di MIPS Modalità di indirizzamento. Proff. A. Borghese, F. Pedersini

Organizzazione di un SO monolitico

Linguaggio Assembly e linguaggio macchina

Università degli Studi di Cassino e del Lazio Meridionale Corso di Fondamenti di Informatica Allocazione dinamica di memoria

AXO - Architettura dei Calcolatori e Sistema Operativo. organizzazione strutturata dei calcolatori

Linguaggio Macchina. Linguaggio Macchina. Linguaggio Macchina. Linguaggio Macchina ADD A,B ISTRUZIONE SUCCESSIVA

Funzioni, Stack e Visibilità delle Variabili in C

Compilatori e Livelli di Compilazione

Lecture 2: Prime Istruzioni

Architettura di una CPU

Istruzioni macchina. Dove sono gli operandi? Ciclo della CPU. Elementi di un istruzione macchina. Rappresentazione delle istruzioni

cap.6 del testo a cosa servono i tipi nei linguaggi di programmazione cos è un linguaggio type safe

Gestione della Memoria

Scope delle variabili e passaggio parametri. Danilo Ardagna Politecnico di Milano

GESTIONE DELLA MEMORIA CENTRALE 6.1 D. - UNICAL

Linguaggio Assembly e linguaggio macchina

Allocazione Dinamica della Memoria

I THREAD O PROCESSI LEGGERI

AMBIENTI DI PROGRAMMAZIONE

Introduzione al C. Tipi derivati

Corso di Fondamenti di Informatica

Macchine astratte, linguaggi, interpretazione, compilazione

ARCHITETTURA DI UN SISTEMA DI ELABORAZIONE

FONDAMENTI DI INFORMATICA Lezione n. 11

Dal sorgente all eseguibile I programmi Assembly. Prof. Alberto Borghese Dipartimento di Scienze dell Informazione

Architettura hardware

Tipi di dato personalizzati Array di struct. Tipi di dato utente. Laboratorio di Programmazione I. Corso di Laurea in Informatica A.A.

Il linguaggio assembly

Richiami sull architettura del processore MIPS a 32 bit

Architettura di un calcolatore e linguaggio macchina. Primo modulo Tecniche della programmazione

Il processore Pentium

Programmazione (imperativa)

Architettura dei calcolatori e sistemi operativi. M2 Organizzazione della memoria virtuale Struttura dello spazio virtuale kernel e utente

Fasi di un Compilatore

! Per quanto sappiamo finora, in C le variabili sono sempre definite staticamente

Università degli Studi di Roma La Sapienza

Pipeline nel Mondo Reale

Informatica Generale 07 - Sistemi Operativi:Gestione dei processi

Allocazione dinamica della memoria

Università di Roma Tor Vergata Corso di Laurea triennale in Informatica Sistemi operativi e reti A.A Pietro Frasca.

Il Modello di un Compilatore. La costruzione di un compilatore per un particolare linguaggio di programmazione e' abbastanza complessa.

Architettura dei sistemi di elaborazione: La CPU: Architettura (parte1)

Indici multilivello dinamici (B-alberi e B + -alberi) Alberi di ricerca - 1. Un esempio. Alberi di ricerca - 3. Alberi di ricerca - 2

Modi di esecuzione user / kernel

Gestione della memoria

Il Ciclo Fetch-Decode-Execute. C Nyssen/Aberdeen College 2003

Accesso a memoria. Accesso a memoria. Accesso a memoria. Modalità di indirizzamento. Lezione 5 e 6. Architettura degli Elaboratori A.

Sistemi Operativi 1. Mattia Monga. 11 marzo Dip. di Informatica e Comunicazione Università degli Studi di Milano, Italia

Struttura CPU. Struttura e Funzione del Processore. Capitolo 12. Compiti CPU:

Gli indirizzi occupano spazio! Gli indirizzi occupano spazio! Corso di Architettura degli Elaboratori. Indirizzamento immediato

Architettura del processore. Modello di calcolatore. Caratteristiche del processore. Caratteristiche del processore. Fondamenti di Informatica

Linguaggi di Programmazione

Fasi (MIPS) Dove NPC è un registro temporaneo PC (program counter) è il registro IP (instruction pointer)

Introduzione ai puntatori in C Definizione

Laboratorio di Architettura lezione 5. Massimo Marchiori W3C/MIT/UNIVE

Calcolatori Elettronici Parte VIII: linguaggi assemblativi

PROGRAMMAZIONE AVANZATA JAVA E C

Sistema Operativo (Software di base)

Primi passi col linguaggio C

Introduzione all'architettura dei Calcolatori

TEORIA DEI SISTEMI OPERATIVI. Sistemi monoprogrammatie multiprogrammati

La gestione della memoria dinamica Heap

La CPU e la Memoria. Sistemi e Tecnologie Informatiche 1. Struttura del computer. Sistemi e Tecnologie Informatiche 2

LINGUAGGI DI ALTO LIVELLO

Il calcolatore. È un sistema complesso costituito da un numero elevato di componenti. è strutturato in forma gerarchica

Esercitazioni di Informatica 3

Gestione dinamica della memoria

Periferiche CPU. Misure e Sistemi Microelettronici Sistemi 6-1 SREG. Data Bus Address Bus Control Bus

Linguaggi di Programmazione

Il linguaggio C. Notate che...

Dove siamo. Gestione della Memoria (1) Dove siamo. Dove siamo. Operating Systems and Distributed Systems. Operating Systems and Distributed Systems

Parte V. Il Livello delle Istruzioni Macchina

Java. Java: compilatore e interprete. Programma Java. Java Virtual Machine. Token. Alfabeto

Algoritmo. La programmazione. Algoritmo. Programmare. Procedimento di risoluzione di un problema

Lezione 21 e 22. Valentina Ciriani ( ) Laboratorio di programmazione. Laboratorio di programmazione. Lezione 21 e 22

Modi di indirizzamento del processore MC68000 (parte prima)

Laboratorio di Architettura degli Elaboratori

Architettura dei computer

Java. Java : compilatore e interprete. Programma Java. Java Virtual Machine. Token. Alfabeto

7 - Programmazione procedurale: Dichiarazione e chiamata di metodi ausiliari

Strutture Dati. Elisa Marengo. Università degli Studi di Torino Dipartimento di Informatica. Elisa Marengo (UNITO) Strutture Dati 1 / 16

Architetture dei Calcolatori

ARCHITETTURA DEI MICROPROCESSORI INTEL 8086/8088

Fondamenti di Informatica T. Linguaggio C: i puntatori

Corso di Fondamenti di Informatica. Puntatori e Allocazione Dinamica

Programma del corso. Elementi di Programmazione. Introduzione agli algoritmi. Rappresentazione delle Informazioni. Architettura del calcolatore

L Allocazione Dinamica della Memoria

Famiglia dei processori INTEL

Array. Parte 7. Domenico Daniele Bloisi. Corso di Fondamenti di Informatica Ingegneria delle Comunicazioni BCOR Ingegneria Elettronica BELR

Transcript:

Parte I Il backend Figura 1: La struttura di un compilatore Il backend è la parte di un compilatore che si occupa di ottimizzare il programma che si sta compilando (mentre è sotto forma di linguaggio intermedio) e successivamente di tradurlo in codice macchina per una specica architettura. 1 Linguaggi intermedi Esistono diversi tipi di linguaggi intermedi: istruzioni a 3 indirizzi (quadruples) Polish notation Abstract Syntax Tree (AST) 1 Macchina virtuale a pila (ne sono esempi i bytecode Java e.net) 1.1 Caratteristiche Il linguaggio intermedio è neutrale rispetto all'architettura, per ragioni di portabilità e essibilità. Per questo, le variabili non hanno indirizzo di memoria, ma solo una rappresentazione simbolica. Gli operatori sono simili a quelli del linguaggio sorgente (verranno precisati in seguito, perchè possono dipendere dalla specica ALU). Vengono già distinte le operazioni in base al tipo (interi, virgola mobile, ecc). In tal modo il backend le può mappare più facilmente sulle istruzioni macchina. 2 Caratteristiche hardware che inuenzano il backend Memory hierarchy: registri dynamic memory allocation memoria garbage collection cache 1 livello 2 livello In presenza di molti registri migliorano le prestazioni, ma si complica il backend che deve determinarne l'assegnamento e la gestione. Quali istruzioni macchina abbiamo? RISC (Reduced Instruction Set Computer) CISC (Complex Instruction Set Computer) 1 Il linguaggio intermedio usato nel libro Modern Compiler Implementation in Java di A. W. Appel 1

I processori RISC sono molto più diusi. L'architettura Pentium è intermedia: un RISC abbondante. Dove mettere il codice? Code layout Pipelined CPU La pipeline della CPU serve a far eseguire contemporaneamente più istruzioni in fasi diverse. È utile per istruzioni consecutive. Fallisce in presenza di salti. Il BE deve schedulare le istruzioni in modo da ridurre gli stalli del pipeline. Instruction level parallelism Very Long Instruction Word (VLIW) Superscalar Il compilatore deve decidere quali istruzioni possono esser eseguite contemporaneamente tramite analisi dataow delle dipendenze. Predicative execution Vengono eseguite istruzioni che non si sa se serviranno (ad es: entrambi i rami di un'if) se sono disponibili parti inutilizzate di processore. Spreco energia ma aumento le prestazioni. Mutithreading Presenza di più Program Counter. Permette di eseguire in parallelo più programmi purchè non interferiscano nell'uso di risorse Multi-processor (o multi-core) La parallelizzazione è attualmente eseguita principalmente dal programmatore. Si studiano metodi per automatizzarla. Parte II Record di attivazione Se una funzione presenta delle variabili locali o dei parametri, è necessario allocare dello spazio in memoria per queste variabili e deallocarlo al termine della funzione stessa. Ogni funzione può essere richiamata più volte ricorsivamente, quindi anche lo spazio per le variabili locali deve essere, in tal caso, allocato più volte. Siccome una funzione ha termine solo quando tutte le funzioni da lei chiamate hanno già avuto termine, si osserva che le chiamate a funzione hanno un comportamento di tipo LIFO. Ciò signica che possiamo usare una pila per conservare questi dati. In realtà, in presenza di un linguaggio che supporti sia le funzioni annidate (nested functions) sia l'assegnazione di funzioni alle variabili (function-valued variables), potrebbe essere necessario mantenere alcune variabili locali dopo il termine della funzione, quindi non si può usare una struttura a stack. Ciò avviene in linguaggi come ML o Scheme (che qui non verranno trattati oltre). La presenza di una sola delle due caratteristiche (Pascal, Java, C) non comporta questo problema. 3 Stack frames La struttura dati che viene utilizzata si comporta, in generale, come una pila, con accesso solo all'elemento superiore e istruzioni di push e pop. Abbiamo tuttavia alcune esigenze speciche della compilazione: le variabili locali sono inserite sulla pila a gruppi (all'ingresso di una funzione) sono eliminate dalla pila a gruppi (all'uscita dalla funzione) non sempre le variabili inserite sulla pila sono inizializzate vogliamo poter accedere anche a valori che si trovano anche all'interno della pila stessa 2

Figura 2: Uno stack frame con i relativi campi le variabili locali i parametri di ingresso della funzione variabili non locali, o globali Per avere queste funzionalità, usiamo come pila (crescente verso il basso, ogni volta che si alloca spazio per l'esecuzione di una nuova funzione) un grande array, con gli indirizzi decrescenti verso il basso. Memorizziamo inoltre due puntatori: stack pointer (SP) indica la cima della pila. Tutto ciò che sta al di là di esso (verso indirizzi più bassi) è inutile (garbage). Tutto le posizioni di memoria precedenti sono allocate. frame pointer (FP) indica l'inizio dell'area dello stack dedicata alla funzione corrente. Al momento di una chiamata di funzione, FP assume il valore del vecchio SP. Se il frame ha dimensione ssa, FP è un valore virtuale, ottenuto da SP+<dimensione stack frame>. FP SP SP SP+<dimensione stack frame> Soprattutto, invece, nel caso in cui la dimensione non è ssa, FP è utile perchè le posizioni relative ad esso possono essere determinate tramite un oset in modo immediato. Non si può fare lo stesso con SP perchè il suo valore è noto solo molto più tardi, quando tutti i campi sono stati deniti. Tutto ciò che è compreso tra frame pointer e stack pointer è il record di attivazione (o stack frame, o activation record) della funzione corrente. L'ordine dei dati contenuti nello stack frame non è necessariamente sso, ma per ragioni di compatibilità è generalmente denito dal manuale di riferimento dell'architettura. Il primo stack frame (quello con indirizzi più alti, cioè sul fondo della pila), è relativo al main del programma. Al suo interno (o vicino a lui) sono presenti le varibili globali. Le dimensioni di uno stack frame sono dim. variabili dichiarate (nella funzione, e nei blocchi contenuti)+ dim. parametri+ altri dati. 3.1 Descrizione dei campi Incoming arguments Parametri d'ingresso della funzione. Strettamente parlando, fanno parte del frame precedente. Variabili locali spazio dedicato alle variabili locali dichiarate all'interno della funzione. 3

Return address indirizzo (nell'instruction segment) della prima istruzione che dovrà essere eseguita al termine dell'esecuzione di questa funzione. Se il chiamante ha le istruzioni: a-1: istr. a: call F a+1: istr. allora return address dello stack frame di F contiene l'indirizzo a + 1. L'indirizzo è inserito dall'istruzione call, alla creazione dello stack frame. Nelle architetture moderne, spesso è salvato in un registro. In tal modo, solo le procedure non foglia dovranno scriverlo in memoria. Temporaries variabili temporanee usate dal compilatore per memorizzare sottoespressioni già calcolate ma non ancora utilizzate. Saved registers Area utilizzata per salvare il contenuto dei registri che dovrà essere utilizzato in seguito quando è necessario sovrascriverli. Outgoing arguments Spazio riservato alla scrittura dei parametri di ingresso delle funzioni chiamate dalla funzione corrente. Static link Viene usato nei linguaggi con struttura a blocchi, per far sì che si possa accedere alle variabili non locali ma visibili. Punta alla base del frame dell'attivazione corrente (avvenuta più di recente) della funzione che racchiude staticamente (nel codice sorgente) la funzione attuale f. Alternativamente, esistono altri modi per rendere accessibili le variabili non locali: Lambda lifting: le variabili non locali accessibili da una funzione vengono automaticamente incluse tra i parametri della funzione stessa. Si mantiene un array globale che contiene, a ogni posizione i, il puntatore al frame della più recente funzione eseguita che abbia livello di annidamento statico i. Dynamic link puntatore al Frame Pointer della funzione chiamante. Viene utilizzato al termine di una funzione, quando il suo stack frame deve essere deallocato. Viene anche utilizzato per determinare la visilibilità delle funzioni in linguaggi con scope dinamico. 4 Assegnazione dei registri Le architetture moderne contengono molti registri (almeno 32) quindi, per ragioni di prestazioni, alcune variabili e valori intermedi non vengono scritte in memoria, ma vengono lasciate nei registri. Se è necessario sovrascrivere il contenuto di un registro e riutilizzarlo in seguito, la politica di salvataggio è denita dalle convenzioni dell'architettura, che dividono i registri in: caller-saved Il chiamante li deve salvare prima di eseguire una chiamata a funzione, se ritiene di aver ancora bisogno di ciò che contengono in seguito. Generalmente vengono usati per memorizzare i dati non più utili dopo una chiamata. In tal modo si risparmiano una store e una load. callee-saved si ha la certezza che (a cura della funzione chiamata) i valori contenuti in questi registri, se sovrascritti, verrano ripristinati al termine della funzione, mantenendo il valore che avevano al momento della chiamata. Utili per i valori che dovranno essere disponibili successivamente alla chiamata. Generalmente si può determinare tramite l'analisi di usso quali a quali registri assegnare una determinata variabile, a seconda se dovrà ancora essere usata o meno. 4

4.1 Registri per il passaggio di parametri Molte funzioni hanno al massimo da 4 a 6 parametri. Perciò un tal numero di registri viene riservato per il passaggio di parametri. In alcuni linguaggi, i parametri possono essere acceduti per indirizzo, e devono essere adiacenti. I registri non hanno indirizzo. Perciò si riserva comunque spazio sullo stack, ed è la funzione chiamata a riempirlo, solo se eettivamente necessario. In tal modo, comunque, si risparmia il tempo necessario al caricamento e scaricamento dalla memoria. Se una funzione ne chiama un'altra, i suoi parametri possono dover sovrascrivere quelli della chiamante, contenuti nei registri, salvandone quindi il contenuto precedente nello stack. Si ha comunque un risparmio di tempo rispetto all'uso della sola memoria perchè: Alcune procedure, le procedure foglia (leaf procedure), non ne chiamano altre. Quindi i loro parametri possono stare nei registri senza che debbano essere salvati in memoria. Alcuni compilatori ottimizzanti utilizzano la interprocedural register allocation Se una procedura non ha più bisogno di usare i propri parametri quando chiama una sottoprocedura, la sottoprocedura può sovrascrivere i registri senza salvarli (e lo può scoprire tramite analisi statica del codice). Alcune architetture hanno register windows, che permettono di allocare un insieme di registri senza traco in memoria. 4.2 Salvataggio in memoria Ogni volta che è possibile, i registri vengono sfruttatti per conservare le variabili. I valori vengono scritti in memoria per una delle seguenti ragioni: La variabile sfugge (escapes): La variabile verrà passata per riferimento, quindi ha bisogno di un indirizzo in memoria La variabile è acceduta da una funzione innestata in quella corrente Il valore è troppo grande per un singolo registro Il registro che contiene la variabile deve essere riutilizzato perchè ha una funzione specica (es: passaggio parametri) Ci sono troppe variabili per usare solo i registri. 5 Chiamate di funzioni function f() g(); function g() Caller Funzione chiamante: f() Callee Funzione chiamata: g() 5.1 Nested procedures Si parla di procedure annidate (nested procedures) quando è possibile denire una funzione all'interno dell'altra. Dato un insieme di procedure innestate, è possibile denire una albero di annidamento statico (Static Nesting Tree), che determina, a livello di codice sorgente, quale procedura contiene al suo interno quale. In linguaggi con scope statico, è possibile determinare le variabili visibili da una determinata funzione risalendo da essa alla radice dell'albero SNT. 5

Algorithm 1 Dichiarazione di funzioni annidate main() var a; function f() var b; function g() var c; function k() g(); Figura 3: Lo Static Nesting Tree relativo all'algoritmo 1 6