Notes sur l'unwind.

This commit is contained in:
Georges Dupéron 2010-11-30 23:48:35 +01:00
parent a3876957b8
commit d3b9a52bf6

View File

@ -33,6 +33,161 @@
;; (defmatch squash-lisp (:name _ :params _*) `(:call ,name ,@(mapcar #'squash-lisp params)))
;; (defmatch squash-lisp (:x . _) (error "Squash-Lisp ne sait pas gérer : ~w" x))
#|
Notes sur l'implémentation d'unwind.
Tous les lets qui aparaissent dans un appel de fonction sont regrouppés en un seul. Donc à un appel de fonction correspond un "gros" segment de pile.
Après la mise en place de ce segment de pile, le code est exécuté.
TODO / NOTE pour la suite : on peut se passer des marker-* si on peut s'assurer qu'entre un end-frame et le begin-frame qui suit, il n'y a QUE des unwind-protect, unwind-catch etc.
Donc uniquement des adresses de portions de code généré par le compilateur, pas de "vrais" objets, donc on est sûr qu'il n'y aura pas de confusion entre un "vrai" objet
et une cible mise en place par unwind-catch.
Lorsqu'on rencontre une structure de contrôle comme la suivante :
(unwind-catch object body catch-code)
Elle est compilée ainsi :
push @catch-code
[compile object]
push r0
push @marker-unwind-destination
[compile body]
jmp @after-catch-code
@catch-code
[compile catch-code]
@after-catch-code
De plus, un (unwind-protect body protect-code) est compilé ainsi :
push @protect-code
push @marker-unwind-protect
[compile body]
jmp @after-protect-code
@protect-code
[compile protect-code]
jmp @start-unwind
@after-protect-code
Et enfin, (unwind object) est compilé ainsi :
[compile object]
push r0
jsr @find-unwind-destination
jmp @start-unwind
Et une fonction (lambda nb-let-vars body) est compilée ainsi
<jsr @function> => push ip; jmp @function
@function
mov sp r0 ;; begin-frame : Va avec le marker-end-frame
push bp
mov sp, bp ;; sp -> bp
add sp, [nb-let-vars]
push r0 ;; Permet à unwind de sauter directement jusqu'au begin-frame.
push @marker-end-frame ;; On peut l'ommetre pour accélérer les appels de fonction et/ou
;; quand il n'y a pas de marker-unwind-* à la suite.
;; IMPORTANT : effacer tout le segment de pile _SI_ on n'utilise pas begin/end-frame
;; (car sinon il peut y avoir des destinations / protect d'unwind qui traînent encore).
[body]
sub sp, [nb-let-vars]
pop bp
Les "fonctions" find-unwind-destination et start-unwind :
@singleton-unwind-destination
db 0
db 0
@marker-unwind-destination
db 0
db 0
@marker-unwind-protect
db 0
db 0
@marker-end-frame
db 0
db 0
;; fud == find-unwind-destination
@find-unwind-destination
mov sp r1
@fud-loop
cmp sp 3 ;; Ne pas passer en-dessous de 0.
jpe @unwind-not-found-error ;; Ne pas passer en-dessous de 0.
pop r2
cmp r2 @marker-end-frame
jeq @fud-skip-frame
cmp r2 @marker-unwind-destination
jne @fud-loop
pop r2
cmp r2 r0
pop r2 ;; Récupérer l'adresse de retour
mov @nil *sp ;; écraser l'adresse de retour avec @nil pour désactiver la cible.
jne @fud-loop
;; fud-found
cmp r2 @nil ;; Cible désactivée ?
jeq @unwind-inbetween-error
mov sp @singleton-unwind-destination
mov r1 sp ;; On remonte en haut de la pile
ret
@fud-skip-frame
pop r2
mov r2 sp
jmp @fud-loop
@unwind-not-found-error
;; error : cant unwind to this object, the return point doesn't exist anymore.
halt
@unwind-not-anymore-error
;; error : cant unwind to this object, the return point has already been passed by a previous unwind.
halt
@start-unwind
;; TODO
;; su == start-unwind
@su-loop
cmp sp @singleton-unwind-destination
jeq @su-stop
pop r0
cmp r0 @marker-end-frame
jeq @fud-skip-frame
cmp r0 @marker-unwind-protect
jne @su-loop
pop r0
jmp *r0
@su-skip-frame
pop r0
mov r0 sp
jmp @su-loop
@su-stop
pop r0
jmp *r0
On a donc une pile de cette forme (les vieilles données sont en haut) :
** [stack-frame]
== BP ==
[variable foo]
[variable bar]
[variable ...]
** [unwind-protect]
[protect-code à l'adresse 1234]
** [unwind-catch]
[objet]
[code unwind-catch à l'adresse 1111]
** [unwind-protect]
[protect-code à l'adresse 2222]
** [unwind-protect]
[protect-code à l'adresse 3333]
** [unwind-catch]
[objet]
[code unwind-catch à l'adresse 4444]
** [unwind-protect]
[protect-code à l'adresse 5555]
== SP ==
|#
(defun squash-lisp (expr &optional (at-toplevel t) (etat (list nil nil nil)))
(cond-match
expr