diff --git a/implementation/compilation.lisp b/implementation/compilation.lisp index 7b6d768..3f8fa82 100644 --- a/implementation/compilation.lisp +++ b/implementation/compilation.lisp @@ -5,7 +5,7 @@ (defvar asm-fixnum-size 32) (defvar asm-max-fixnum (expt 2 asm-fixnum-size)) (defun type-number (type) - (position type '(fixnum bignum symbol string cons nil))) + (position type '(placeholder fixnum bignum symbol string cons nil))) (defvar label-ctr 0) (defmacro fasm (&rest stuff) diff --git a/implementation/squash-lisp.lisp b/implementation/squash-lisp.lisp index 958aa3c..0b70973 100644 --- a/implementation/squash-lisp.lisp +++ b/implementation/squash-lisp.lisp @@ -1,5 +1,10 @@ (require 'mini-meval "implementation/mini-meval") +;; TODO !!! +;; TODO !!! Utiliser une pile descendante (donc adapter les calculs pour unwind), sinon on n'aura pas la compatibilité x86 +;; TODO !!! + + ;; lisp2li simpliste pour le compilateur. On fusionnera les deux plus tard. (defun simple-splice-up-tagbody (body) @@ -43,7 +48,7 @@ Donc uniquement des adresses de portions de code généré par le compilateur, p 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) +(unwind-catch object body [catch-code]?) Elle est compilée ainsi : @@ -52,10 +57,10 @@ push @catch-code push r0 push @marker-unwind-destination [compile body] -jmp @after-catch-code -@catch-code -[compile catch-code] -@after-catch-code +jmp @after-catch-code ;; seulement si catch-code est présent +@catch-code ;; seulement si catch-code est présent +[compile catch-code] ;; seulement si catch-code est présent +@after-catch-code ;; seulement si catch-code est présent De plus, un (unwind-protect body protect-code) est compilé ainsi : push @protect-code @@ -67,10 +72,23 @@ jmp @after-protect-code jmp @start-unwind @after-protect-code +(half-unwind object post-unwind-code) est compilé ainsi : +jsr @find-unwind-destination +push @post-unwind-code ;; On "push" after-unwind-code comme cible au lieu de l'ancienne. +add 2 sp ;; On "re-push" l'objet et le marqueur, mettre 1 si on n'a pas de marqueur. +mov sp @singleton-unwind-destination +mov r1 sp ;; On remonte en haut de la pile +jmp @start-unwind +@post-unwind-code +[compile post-unwind-code] ;; DOIT contenir un jump ! +halt ;; Sinon, on quite "brutalement" + Et enfin, (unwind object) est compilé ainsi : [compile object] push r0 jsr @find-unwind-destination +mov sp @singleton-unwind-destination +mov r1 sp ;; On remonte en haut de la pile jmp @start-unwind Et une fonction (lambda nb-let-vars body) est compilée ainsi @@ -93,8 +111,8 @@ pop bp Les "fonctions" find-unwind-destination et start-unwind : @singleton-unwind-destination -db 0 -db 0 +db 0 ;; 0 = (type-number placeholder) +db 0 ;; Toujours au moins deux octets. @marker-unwind-destination db 0 db 0 @@ -118,14 +136,12 @@ 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. +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 +jeq @fud-loop ret @fud-skip-frame @@ -137,10 +153,6 @@ jmp @fud-loop ;; 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 @@ -165,25 +177,27 @@ pop r0 jmp *r0 On a donc une pile de cette forme (les vieilles données sont en haut) : -** [stack-frame] +** [old bp] ;; adresse = 987 == BP == [variable foo] [variable bar] [variable ...] -** [unwind-protect] + [begin-frame à l'adresse 987] +** [end-frame] [protect-code à l'adresse 1234] -** [unwind-catch] - [objet] - [code unwind-catch à l'adresse 1111] ** [unwind-protect] + [code unwind-catch à l'adresse 1111] + [objet] +** [unwind-catch] [protect-code à l'adresse 2222] ** [unwind-protect] [protect-code à l'adresse 3333] -** [unwind-catch] +** [unwind-protect] [objet] [code unwind-catch à l'adresse 4444] -** [unwind-protect] +** [unwind-catch] [protect-code à l'adresse 5555] +** [unwind-protect] == SP == |# @@ -247,14 +261,16 @@ On a donc une pile de cette forme (les vieilles données sont en haut) : ;; Lorsqu'on rentre dans un block, on met sur la pile un marqueur spécial avec un pointeur vers un objet créé à l'exécution. ((block :block-name $$ :body _*) (let ((retval-sym (make-symbol "RETVAL")) - (end-sym (make-symbol "END-BLOCK"))) + (block-id-sym (make-symbol "BLOCK-ID"))) (squash-lisp - `(let ((,retval-sym nil)) - (tagbody - (progn ,@body) - ,end-sym) + `(let ((,retval-sym nil) + ;; Il y a un peu de redondance, car block-id-sym + ;; stocké dans le let et dans le unwind-catch + (,block-id-sym (cons nil nil))) + (unwind-catch ,block-id-sym + (progn ,@body)) ,retval-sym) - (push-local etat block-name 'squash-block-catch (cons end-sym retval-sym))))) + (push-local etat block-name 'squash-block-catch (cons block-id-sym retval-sym))))) ;; Les return-from qui sont accessibles lexicalement sont remplacés par un (unwind ) ;; Unwind remonte la pile jusqu'à trouver le marqueur spécial, tout en exécutant les unwind-protect éventuels. @@ -263,7 +279,8 @@ On a donc une pile de cette forme (les vieilles données sont en haut) : ((return-from :block-name $$ :value _) (let ((association (assoc-etat block-name 'squash-block-catch etat))) (unless association (error "Squash-Lisp : Can't return from block ~w, it is inexistant or not lexically apparent." block-name)) - (squash-lisp `(progn (setq ,(cddr association) value) (go ,(cadr association)))))) + (squash-lisp `(progn (setq ,(cddr association) value) + (unwind ,(cadr association)))))) ;; Le traitement de tagbody/go est similaire pour sortir d'un tag, puis on jmp directement sur le tag de destination (vu qu'il est au même niveau). @@ -278,28 +295,68 @@ On a donc une pile de cette forme (les vieilles données sont en haut) : (setq unique-label-sym (make-symbol (format nil "~a" (car zone)))) (setq new-etat (push-local new-etat (car zone) 'squash-tagbody-catch (cons unwind-catch-marker-sym unique-label-sym))) (setf (car zone) unique-label-sym)) - ;; Définition de (unwind-catch name &rest body) : - ;; `(let ((,name (make-unwind-marker))) ,body) - `(let ((,unwind-catch-marker-sym (make-unwind-marker))) - (unwind-catch ,unwind-catch-marker-sym - ,@(progn (dolist (zone spliced-body) - (setq the-body ,@) - (push `(tagbody-label (car zone)) res) - (push (squash-lisp `(progn (cdr zone)) new-etat) res)) - `(simple-tagbody ,@(cdr (reverse res))))))))) ;; cdr pour zapper le tout premier (tagbody-label) + (squash-lisp + `(let ((,tagbody-id-sym (cons nil nil))) + (unwind-catch ,tagbody-id-sym + (progn + ,@(progn (dolist (zone spliced-body) + (push `(jump-label (car zone)) res) + (push `(progn (cdr zone)) res)) + ;; (cdr (reverse …)) pour zapper le tout premier (jump-label …) + (cdr (reverse res))))) + nil) + new-etat)))) ((go :target $$) (let ((association (assoc-etat target 'squash-tagbody-catch etat))) (unless association (error "Squash-Lisp : Can't go to label ~w, it is inexistant or not lexically apparent." target)) - `(progn (unwind ,(cadr association)) (simple-go ,(cddr association))))) + (squash-lisp `(progn (half-unwind ,(cadr association) + (jump ,(cddr association))))))) ;; Le traitement de catch/throw est similaire, sauf que le pointeur est simplement un pointeur vers l'objet utilisé pour le catch / throw. - ((catch :tag tag :body _*) + ((catch :tag _ :body _*) + (squash-lisp + ;; TODO : ajouter une variable globale singleton-catch-retval + `(unwind-catch ,tag (progn ,@body) singleton-catch-retval))) + + ((throw :tag _ :result _) + (squash-lisp + `(progn (setq singleton-catch-retval value) + (unwind ,tag (progn ,@body))))) + + ;; Simplification du unwind-protect + ((unwind-protect :body _ :a-cleanup _ :other-cleanups _+) + `(unwind-protect ,(squash-lisp body) + (progn ,(squash-lisp a-cleanup) ,@(squash-lisp other-cleanups)))) + + ((unwind-protect :body _ :a-cleanup _) + `(unwind-protect ,(squash-lisp body) + ,(squash-lisp a-cleanup))) + ((unwind-catch :object _ :body _ :catch-code _?) + (if catch-code + `(unwind-catch ,(squash-lisp object) + ,(squash-lisp body) + ,(squash-lisp (car catch-code))) + `(unwind-catch ,(squash-lisp object) + ,(squash-lisp body)))) + + ((unwind :object _) + `(unwind ,(squash-lisp object))) + + ((half-unwind :object _ :post-unwind-code _) + `(half-unwind ,(squash-lisp object) ,(squash-lisp post-unwind-code))) + + ((jump-label :name _) + expr) + + ((jump :dest _) + expr) ;; Les constantes sont renvoyées telles qu'elles ((? or numberp stringp) expr) + ;; TODO : nil et t devraient être des defconst (nil nil)))