Ultimamente ho fatto molte domande qui sulle VM. Eccone un altro:
Comprendo che spesso le VM basate su stack utilizzano solo uno stack, lo stack delle chiamate, per tutto. Per esempio. è anche usato per la valutazione delle espressioni aritmetiche.
Quello che non capisco è, in che modo questo non complica molto le cose? Mostrerò cosa intendo con un esempio.
Considerare il seguente programma psuedocode:
func main:
funcA()
func funcA:
2 + 4 * 8
Questo sarebbe compilato al seguente codice bytec:
main:
call funcA
end
funcA:
push 2
push 4
push 8
mult
add
end
(in questo bytecode, il programma parte da main:
. call
spinge il contatore del programma sullo stack e salta all'etichetta specificata. Quando viene raggiunto un end
, la parte superiore della pila - si presume che sia un numero di linea - viene spuntato e saltiamo lì.)
Quindi vediamo cosa succede qui:
In call funcA
, spingiamo il contatore del programma (cioè il numero di riga successivo) nello stack. Quindi saltiamo su funcA
.
In funcA
viene eseguito un calcolo. Dopo il calcolo, il numero 34
viene lasciato all'inizio dello stack.
Quando raggiungiamo end
, facciamo il punto in cima allo stack, assumendo che sia il numero di riga a cui dovremmo tornare
Ma non lo è, il numero della linea da cui tornare è sepolto sotto. Come dovrebbe sapere end
di questo?
Per evitare tutto questo casino, possiamo semplicemente avere uno stack di dati e uno stack di chiamate separati, e non mescolare i due.
Quindi la mia domanda è: perché alcune VM (come la JVM) usano uno stack per tutto, e quando lo fanno: come gestiscono situazioni come quella descritta sopra?