d6d0eb68606133a8ce45723099f535ddeeda5133
linguaggi/s02/20260224.md
| ... | ... | @@ -105,7 +105,7 @@ Il ciclo di vita di un RdA si divide in fasi ben precise gestite dal chiamante e |
| 105 | 105 | 5. Deallocazione dello spazio sulla pila (si arretra il Puntatore al Top). |
| 106 | 106 | 6. Ripristino del Contatore di Programma all'indirizzo di ritorno salvato. |
| 107 | 107 | |
| 108 | -### 4. Esempio di Ricorsione: Il Fattoriale |
|
| 108 | +### Esempio di Ricorsione: Il Fattoriale |
|
| 109 | 109 | |
| 110 | 110 | Prendiamo come esempio la funzione fattoriale: `fact(n) { if (n<=1) return 1; else return n * fact(n-1); }` |
| 111 | 111 |  |
| ... | ... | @@ -197,33 +197,38 @@ Consideriamo il seguente pseudo-codice: |
| 197 | 197 | **Come si comporta in memoria?** |
| 198 | 198 | Il codice della funzione `foo` deve accedere sempre alla stessa variabile `x` (quella dichiarata globalmente a valore 10, memorizzata nel Record di Attivazione - RdA - del main). |
| 199 | 199 | |
| 200 | + |
|
| 201 | + |
|
| 200 | 202 | - Anche se chiamiamo `foo` dall'interno di `fie` (dove esiste una x locale a 0), lo scope statico impone che `foo` "veda" solo l'ambiente in cui è stata definita (il main). |
| 201 | 203 | - Il meccanismo: A run-time, in cima alla pila c'è l'RdA di `foo` (il Puntatore SP guarda lì). Per trovare la x giusta, il sistema non la cerca semplicemente "indietro" nella pila dinamica, ma usa i puntatori di scope per determinare prima qual è l'RdA corretto a cui appartiene quella variabile (in questo caso l'RdA del main). Una volta trovato l'RdA giusto, accede a x tramite l'offset calcolato rispetto a *quell'*RdA, ignorando totalmente la x locale di fie. |
| 202 | 204 | |
| 203 | - |
|
| 204 | 205 | x tramite offset relativo a tale RdA (e non relativo a SP) |
| 205 | 206 | |
| 206 | -Record di attivazione per scoping statico |
|
| 207 | +### Link Dinamico vs Link Statico |
|
| 208 | + |
|
| 207 | 209 |  |
| 208 | -Link dinamico: |
|
| 209 | -– puntatore all’RdA precedente sulla pila (RdA del chiamante) |
|
| 210 | -• Link statico: |
|
| 211 | -– puntatore all’RdA del blocco che contiene immediatamente il testo del blocco in esecuzione |
|
| 212 | -• Osserva: |
|
| 213 | -– link dinamico dipende dalla sequenza di esecuzione del programma |
|
| 214 | -– link statico dipende dall’annidamento statico (nel testo) delle dichiarazioni delle procedure |
|
| 215 | - |
|
| 216 | -Catena Statica: esempio |
|
| 210 | +Nel Record di Attivazione (RdA), quando si usa lo scope statico, sono presenti due puntatori fondamentali che servono a scopi diversi: |
|
| 211 | + |
|
| 212 | +- **Link Dinamico:** Punta all'RdA della procedura _chiamante_ (quella precedente sulla pila). Dipende esclusivamente dalla **sequenza di esecuzione** del programma a run-time. |
|
| 213 | +- **Link Statico:** Punta all'RdA del blocco che **contiene testualmente** la procedura in esecuzione (il blocco "padre" nel codice sorgente). Dipende esclusivamente dall'**annidamento statico** del codice scritto dal programmatore. |
|
| 214 | + |
|
| 215 | +### Esempio |
|
| 216 | + |
|
| 217 | 217 | Sequenza di chiamate a run time A, B, C, D, E, C |
| 218 | 218 |  |
| 219 | -le linee tratteggiate sono link statici |
|
| 220 | - |
|
| 221 | 219 |  |
| 222 | -Se un sottoprogramma è annidato a livello k, allora la catena è lunga k |
|
| 220 | +**Risoluzione delle Variabili** (Come trovare la 'x' giusta) |
|
| 221 | +Se un sottoprogramma (es. $E$) è annidato a livello $k$, la sua catena statica è lunga $k$. |
|
| 222 | +Quando il programma deve accedere a una variabile _non locale_ $x$: |
|
| 223 | + |
|
| 224 | +1. **Cosa fa il Compilatore:** Determina a livello statico a quale blocco appartiene la variabile (es. "la $x$ si trova 2 livelli più in alto"). Associa al nome della variabile un indice $h$: |
|
| 225 | + - $h=0$: Variabile locale (trovata nell'RdA corrente). |
|
| 226 | + - $h>0$: Variabile non locale definita $h$ blocchi sopra. Il compilatore dice al programma _quanti passi_ fare, ma non conosce l'indirizzo esatto in memoria. |
|
| 227 | +2. **Cosa succede a Run-time:** Il programma usa il Link Statico per "risalire" di $h$ RdA. Una volta raggiunto l'RdA corretto (es. risalendo dal blocco $E$ al blocco $C$, e poi al blocco $A$), utilizza un offset per accedere fisicamente alla variabile. |
|
| 223 | 228 | |
| 224 | -se sono in e e sto cercando una var x non locale allora vado in c e poi vado in a |
|
| 225 | -questo grazie ai link statici (questi puntatori sono determinati a runtime) |
|
| 226 | -Esempio |
|
| 229 | +> **Nota**: Se un sottoprogramma è annidato a livello k, allora la catena è lunga k |
|
| 230 | + |
|
| 231 | +_Esempio Pratico:_ Se la funzione $A$ è definita nel `main`, il suo link statico punterà sempre all'RdA del `main`. Se $A$ modifica $x$, modificherà sempre la $x$ del `main`, indipendentemente da chi ha chiamato $A$ a run-time. |
|
| 227 | 232 | |
| 228 | 233 | ``` |
| 229 | 234 | {int x; |
| ... | ... | @@ -242,64 +247,32 @@ B(); |
| 242 | 247 | } |
| 243 | 248 | ``` |
| 244 | 249 | |
| 245 | -Struttura main con dentro a e b e b con dentro c |
|
| 246 | - |
|
| 247 | 250 |  |
| 248 | -la x che viene modificata e' sempre quella del main visto che viene modificata da A e il puntatore di catena statica di A punta al main. Le altre x non vengono modificate da A. |
|
| 249 | -C modifica la propria x (visto che la dichiara ed e'quindi locale) |
|
| 250 | - |
|
| 251 | -il compilatore dice di risalire di 1 il record di attivazione. A lo fa e trova a x del main (con un offset) |
|
| 252 | - |
|
| 253 | -se in C avessi la variabile pippo chee e' definita nel main il compilatore mi direbbe che pippo e' definito in main (2 livelli sopra) e quindi quando devo manipolarla o chiamarla salgo due livelli e ne uso il valore |
|
| 251 | +(non si capisce molto) |
|
| 254 | 252 | |
| 255 | -Il compilatore sa dove sono dichiarate le variabili ma non sa la loro posizione a runtime (quindi ci dice solo quanto andare in su ma il dove va deciso a runtime grazie alla catena statica) |
|
| 253 | +- la x che viene modificata e' sempre quella del main visto che viene modificata da A e il puntatore di catena statica di A punta al main. Le altre x non vengono modificate da A. |
|
| 254 | +- C modifica la propria x (visto che la dichiara ed e'quindi locale) |
|
| 256 | 255 | |
| 257 | -Dal punto di vista del supporto a run time |
|
| 258 | -Come viene determinato il link statico del chiamato? |
|
| 256 | +Quando una procedura $Ch$ (chiamante) invoca una procedura $P$ (chiamato), **è il chiamante che deve determinare e passare il Link Statico al chiamato**. |
|
| 257 | +Affinché $Ch$ possa chiamare $P$, $P$ deve essere nel suo scope visibile. Ci sono due casi, basati sulla distanza di annidamento ($k$) tra $Ch$ e $P$: |
|
| 259 | 258 | |
| 260 | -es sopra |
|
| 261 | -sono nel main, chiamo B devo inizializzre il puntatore di catena statica di B, so che B e' inizializzato dentro al main quindi inizializzo il suo puntatore al main (me) |
|
| 262 | -Ora B chiama A, come posso inizializzare il suo puntatore? B sa che A e' allo stesso livello di annidamento di A e quindi gli basta risalire di (0) livelli e passare il puntatore a A. |
|
| 263 | -In generale o X e' inizializzato dentro a A (e quindi il suo indirizzo e' A) oppure Si calcola livello di annidamento di A - X e si risalgono i A-X livelli e si assegna l'indirizzo a X |
|
| 259 | +- **Caso 1 ($k=0$):** $P$ è definito _immediatamente dentro_ $Ch$. |
|
| 260 | + - _Soluzione:_ $Ch$ passa a $P$ semplicemente il proprio Stack Pointer (SP). L'RdA di $Ch$ diventa il Link Statico di $P$. |
|
| 261 | +- **Caso 2 ($k>0$):** $P$ è definito in un blocco $k$ passi "fuori" da $Ch$ (cioè $Ch$ e $P$ condividono un blocco padre o nonno comune). |
|
| 262 | + - _Soluzione:_ $Ch$ risale la **propria** catena statica di $k$ passi per trovare l'RdA del blocco in cui è definito $P$, e passa quel puntatore come Link Statico a $P$. |
|
| 264 | 263 | |
| 265 | -e'il chiamante a determinare il link statico del chiamato |
|
| 266 | -Info a disposizione del chiamante: |
|
| 264 | +_Nota di Visibilità:_ Un blocco non può chiamare un altro blocco "fratello" se non lo "vede" (es. se $B$ e $C$ sono definiti dentro $A$, e $D$ è definito dentro $C$, il blocco $B$ non può chiamare $D$ direttamente). |
|
| 267 | 265 | |
| 268 | -- annidamento statico dei blocchi (determinata dal compilatore) |
|
| 269 | -- proprio RdA |
|
| 270 | - |
|
| 271 | -Come determinare il puntatore di Catena statica (CS) |
|
| 272 | -il chiamante Ch conosce l'annidamento dei blocchi: |
|
| 273 | - |
|
| 274 | -- quando Ch chiama P sa se la definizione di P e': |
|
| 275 | - - immediatamente inclusain Ch (k=0) |
|
| 276 | - - in un blocco k passi fuori da Ch |
|
| 266 | + |
|
| 277 | 267 | |
| 278 | -– nessun altro caso possibile: |
|
| 279 | -• perché P deve essere in scope! |
|
| 280 | -– nel caso a destra: |
|
| 281 | -• chiamate: A, B, C, D, E, C |
|
| 282 | -– con i dati di catena statica: |
|
| 283 | -• A; (B,0); (C,1); (D,0); (E,1); (C,2) |
|
| 268 | +### Ripartizione dei Compiti e Costi Computazionali |
|
| 284 | 269 | |
| 285 | -Se k=0: |
|
| 286 | -– Ch passa a P il proprio SP |
|
| 287 | -•Se k>0: |
|
| 288 | -– Ch risale la propria catena statica di k passi e passa il puntatore così determinato |
|
| 289 | - |
|
| 270 | +L'implementazione dello scope statico divide il lavoro e introduce dei costi a run-time: |
|
| 290 | 271 | |
| 291 | -> Nota: Se B chiamasse D non potrebbe farlo perche' non lo puo' vedere. |
|
| 292 | - |
|
| 293 | -Ripartizione dei compiti |
|
| 294 | -Compilatore: |
|
| 295 | - |
|
| 296 | -- associa l'informazione k ad ogni chiamata |
|
| 297 | -- associa ad ogni nome un indice h: |
|
| 298 | - - h=0: nome locale |
|
| 299 | - - h diverso da 0: nome non locale definito h blocchi sopra |
|
| 300 | -- sequenza chiamata/prologo |
|
| 301 | - - risale la catena statica |
|
| 302 | - - inizializza il puntatore di catena statica |
|
| 303 | -- Costi: |
|
| 304 | - - per ogni chiamata: k passi di catena statica |
|
| 305 | - - ad ogni accesso ad una variabile non locale:(h passi di catena statica in piu rispetto all'accesso ad un locale) |
|
| 272 | +- **Compilatore:** \* Associa la distanza di chiamata ($k$) ad ogni invocazione di funzione. |
|
| 273 | + - Associa la distanza di annidamento ($h$) ad ogni accesso a variabile. |
|
| 274 | +- **Sequenza di chiamata / Prologo (Run-time):** |
|
| 275 | + - Risale materialmente la catena statica e inizializza il puntatore per il nuovo RdA. |
|
| 276 | +- **Costi a Run-time:** |
|
| 277 | + - **Costo di chiamata:** Per ogni chiamata a funzione, bisogna fare $k$ "salti" sui puntatori della catena statica. |
|
| 278 | + - **Costo di accesso a variabile non locale:** Per ogni lettura/scrittura, bisogna fare $h$ "salti" nella catena statica (più costoso rispetto all'accesso di una variabile locale, che richiede 0 salti).Compilatore: |