; .EnTete "CXCP" "Pre'compilateur Ceyx" "cxcp"
; .Chapitre C "LE PRECOMPILATEUR CEYX"
; .Auteur "Bertrand Serlet"
; .INRIA
; .Section "Objectifs"
; Afin d'obtenir une efficacite' maximale lors de l'exe'cution,
; il est pre'fe'rable, lorsque le programme Ceyx est au point,
; de compiler \fIinline\fR et \fIopen\fR les fonctions d'acce`s aux champs.
; On e'vite aussi en outre de cette fac/on de payer le prix des ve'rifications
; dynamiques de type.
; Une autre possibilite' offerte par ce compilateur est l'expansion
; en macro, le temps de la compilation,
; en prote'geant e'ventuellement les variables d'une double e'valuation.
; Sur demande, ce pre'compilateur compile aussi les SEND d'une fac/on
; meilleure (en temps), au prix d'une expansion de la taille du code compile'.
; Enfin, ce pre'compilateur e'tant conc/u comme une couche au dessus
; du compilateur Le←Lisp de J. Chailloux, il e'vite le me'lange des
; diffe'rents niveaux de compilation.
; Plusieurs macros (mapc, mapcar ...), pourront - si Je'ro↑me le veut -
; repasser dans le compilateur. C'est aussi la porte ouverte a`
; un type-checker a` la ML, et a` des optimisations de haut niveaux plus
; fantaisistes ...
; Des possibilites restent a` ajouter (attribut memo sur une fonction,
; execute and compile ...)
; .Section "Appel du pre'compilateur"
; .SSection "Compilation d'une, plusieurs, ou toutes les fonctions"
; Syntaxe:
; .br
; - (CXCP fn [ind1 [ind2 [ind3]]]) ; une fonction
; .br
; - (CXCP (fn1 fn2 ... fnk) [ind1 [ind2 [ind3]]]) ; plusieurs fonctions
; .br
; - (CXCP T [ind2]) ou (CXCP) ; toutes les fonctions
; Les indicateurs optionnels ind1, ind2, ind3 ont le me↑me sens
; que pour la fonction compiler du compilateur Le←Lisp.
(df cxcp args
(let ((arg1 (car args)) (rest (cdr args)))
(when (or (null arg1) (eq arg1 t))
(setq arg1 (maploblist '#:cxcp:compilable)
rest (cons t rest)))
(when (and arg1 (symbolp arg1))
(setq arg1 (list arg1)))
(when arg1
(#:cxcp:compiler arg1 (car rest) (cadr rest) (caddr rest)))
(when (or (null args) (eq (car args) t))
; il reste a compiler les fonctions expansees in-line
(compile-all-in-core))))
; .SSection "Optimisation des SEND: execute-and-compile"
; Afin d'optimiser les SEND, il faut d'abord les faire tourner sur
; un peu de code.
; Syntaxe: (ECXCP <exp> <arg>1 ... <arg>n)
; .br
; ou` <exp> est une expression a e'valuer, et <arg>i les me↑mes arguments
; que pour CXCP
; La variable #:cxcp:ecxcp indique le nombre de clauses a partir duquel
; on ne veut plus faire de selectq
(defvar #:cxcp:ecxcp 5)
(df ecxcp (exp . args)
(let ((oldtype (typefn 'send)) (oldval (valfn 'send))
(#:system:redef-flag t))
; On rede'finit le send
(setfn '#:cxcp:send oldtype oldval)
(dmd send (sem arg1 . args)
(if (and (consp sem) (eq (car sem) 'quote))
`(#:cxcp:sendcase '(()) ,sem ,arg1 ,.args)
`(#:cxcp:send ,sem ,arg1 ,.args)))
(de #:cxcp:sendcase (ltypes sem . args)
(let ((type (tcar (car args))))
; on met le nouveau type en te↑te de liste
(rplacd ltypes (cons type
(if (memq type (cdr ltypes))
(delq type (cdr ltypes))
(cdr ltypes))))
(apply (getfn type sem '||) args)))
; on evalue
(eval exp)
; on remet le send initial, et on redefinit #:cxcp:sendcase
(setfn 'send oldtype oldval)
(dmd #:cxcp:sendcase (ltypes sem arg1 . args)
(cond
((> (length (cadr ltypes)) #:cxcp:ecxcp)
; ca expanserait trop, on fait juste un send
`(send ,sem ,arg1 ,@args))
((#:cxcp:danger arg1)
(let ((gen (gensym)))
`(let ((,gen ,arg1))
(#:cxcp:sendcase ,ltypes ,sem ,gen ,.args))))
(t
(let ((al)) ; aliste (fonction . types)
(mapc (lambda (type)
(let ((x (getfn type (cadr sem) '||)) (y))
(setq y (assq x al))
(if y (rplacd y (nconc1 (cdr y) type))
(setq al (nconc1 al
(cons x (list type)))))))
(cdadr ltypes))
`(selectq (car ,arg1)
,@(mapcar (lambda (paire)
(list (cdr paire)
(mcons (car paire) arg1 args)))
al)
(t (send ,sem ,arg1 ,.args)))))))
(prog1 (eval `(cxcp ,.args))
(dmd #:cxcp:sendcase (ltypes sem arg1 . args)
`(send ,sem ,arg1 ,.args)))))
(de #:cxcp:compilable (x)
(and (null (getprop x 'dont-compile))
(memq (typefn x)
'(expr fexpr macro dmacro))))
; Pour la mise au point
(defvar #:cxcp:debug ())
; .SSection "Compilation d'un ou plusieurs packages Ceyx"
; Syntaxe:
; .br
; - (CXCP-PACKAGE pack [ind1 [ind2 [ind3]]]) ; un package
; .br
; - (CXCP-PACKAGE (pack1 ... packn) [ind1 [ind2 [ind3]]]) ; plusieurs packages
; Toutes les fonctions du(des) package(s) et des sous-packages, sont
; compilees.
(df cxcp-package (arg1 . rest)
(when (symbolp arg1) (setq arg1 (list arg1)))
(let ((lpacks))
(foreach x arg1 (newl lpacks (mlink x)))
(#:cxcp:compiler
(maploblist (lambda (x) (and (#:cxcp:compilable x)
(#:cxcp:in-packs x lpacks))))
(car rest) (cadr rest) (caddr rest))))
(de #:cxcp:in-packs (x lpacks)
(and (neq x '||)
(or (memq x lpacks) (#:cxcp:in-packs (packagecell x) lpacks))))
; .SSection "De'claration de l'expansion inline"
; L'expansion inline a` la demande pour une fonction
; est une primitive appartenant pluto↑t au compilateur
; lui me↑me, mais inclus ici pour plus de facilite'.
; Syntaxe:
; (CXCP-INLINE fn1 ... fnk) , fni est soit un nom de fonction,
; soit une liste dont le premier e'le'ment est le nom de la fonction
; et les suivants les noms des variables a` prote'ger en
; double e'valuation.
; ATTENTION: Aucune ve'rification n'est faite sur la validite'
; de l'expansion inline.
; Des cas triviaux ou` l'expansion inline simple n'est pas
; suffisante sont:
; .br
; - les cas de double e'valuation
; .br
; - les cas d'appels avec effets de bords.
; .br
; - les cas d'appel de FSUBR.
; .br
; Un exemple typique est la fonction xcons:
; .DebLL
; (de xcons (a b) (cons b a))
; .FinLL
; Pour cette fonction, l'expansion inline est fausse si l'ordre des effets
; de bord des arguments d'appel importe.
; Un autre cas difficile:
; .DebLL
; (de foo () (omakeq foo foo))
; .FinLL
; qui devrait generer:
; .DebLL
; (dmd foo () `(omakeq foo ,foo))
; .FinLL
; Ce cas difficile est en fait re'solu par l'application
; du macro-expanseur avant tout traitement.
; Un misfeature de l'expanseur, dans le cas ou il doit proteger en double
; e'valuation certaines variables, est de ge'ne'rer du code incorrect
; pour les expr ayant des argument de la forme (a . b)
(df cxcp-inline args
(foreach arg args
(if (symbolp arg)
({Ceyx}:cxcp-inline arg ())
({Ceyx}:cxcp-inline (car arg) (cdr arg)))))
; On stocke sur la plist de la fonction, la liste des
; variables a` prote'ger, pre'ce'de'e du nom de la fonction.
; .Section "Code de la fonction interne de compilation"
; .SSection "Utilitaires"
; L'ite'ration pour l'e'le'memt "x" de'crivant la liste "liste".
(defmacro foreach (x liste . body)
`(let ((,x) (*foreach* ,liste))
(while *foreach* (setq ,x (nextl *foreach*)) ,.body)))
; .SSection "Lancement de la compilation"
; La compilation d'une liste de fonctions:
(de #:cxcp:compiler (l ind1 ind2 ind3)
(let ((oldval (valfn 'send)) (oldtype (typefn 'send)))
; On enle`ve toutes les fonctions de l qui sont dans les fonctions
; d'acce`s ou a` macroizer.
(foreach pair {Ceyx}:cxcp-access (delq (car pair) l))
(foreach pair {Ceyx}:cxcp-inline (delq (car pair) l))
; On ge'ne`re les fonctions d'acce`s
(mapc
(lambda (pair) (#:cxcp:make-access-macro (car pair) (cdr pair)))
{Ceyx}:cxcp-access)
; On ge'ne`re les fonctions macroize'es
(mapc (lambda (pair)
(when (eq (typefn (car pair)) 'expr)
(#:cxcp:make-inline-macro (car pair) (cdr pair))))
{Ceyx}:cxcp-inline)
; On compile pour de bon
(protect
(mapc (lambda (f) (compiler f ind1 ind2 ind3)) l)
; On enle`ve les macros (seulement si le flag de debug est a ())
(unless #:cxcp:debug
(mapc (lambda (x) (remob (symbol x 'macro-open)))
(or (mapcar 'car {Ceyx}:cxcp-access)
(mapcar 'car {Ceyx}:cxcp-inline)))))))
; .SSection "Ge'ne'ration des fonctions d'acce`s"
(de #:cxcp:make-access-macro (name access)
(make-macro-open name
`((inst . val)
(if (consp val)
,(#:cxcp:make-put-function access 'inst 'val)
,(#:cxcp:make-get-function access 'inst)))))
(de #:cxcp:make-get-function (access exp)
(cond
((null access) exp)
((numberp (car access))
(#:cxcp:make-get-function (cdr access)
`(list 'vref ,exp ,(car access))))
((eq (car access) 'car)
(#:cxcp:make-get-function (cdr access)
`(list 'car ,exp)))
((eq (car access) 'cdr)
(#:cxcp:make-get-function (cdr access)
`(list 'cdr ,exp)))
((eq (car access) 'tcar)
(#:cxcp:make-get-function (cdr access)
`(list 'tcar ,exp)))
((eq (car access) 'tcdr)
(#:cxcp:make-get-function (cdr access)
`(list 'tcdr ,exp)))
(t (syserror '#:cxcp:make-get-function "Bad access list" access))))
(de #:cxcp:make-put-function (access exp val)
(unless access (syserror 'cxcp "access list too short" access))
(setq exp (cdr (#:cxcp:make-get-function access exp))
acces (cadar exp) exp (cdr exp))
`(mcons ',(cassq acces '((car . rplaca) (cdr . rplacd)
(tcar . trplaca) (tcdr . trplacd)
(vref . vset)))
,@exp ,val))
; .SSection "Ge'ne'ration de l'expansion inline"
(de #:cxcp:make-inline-macro (name protect)
(let ((body (valfn name)) (larg) (exp))
(setq larg (nextl body))
(ifn (equal larg (flat larg))
(print "**** cxcp-inline : Desole, je ne peux pas expanser : "
name)
(setq exp (#:cxcp:inliner-aux
(if (cdr body) (cons 'progn body) (car body))
larg))
(foreach x protect
(setq exp `(if (#:cxcp:danger ,x)
(list 'let (list (list ',x ,x))
(cons ',name
,(#:cxcp:inline-copy-args larg x)))
,exp)))
(make-macro-open name `(,larg ,exp)))))
(de #:cxcp:inline-copy-args (l x)
(cond
((null l) ())
((eq l x) (kwote x))
((symbolp l) l)
(t `(cons ,(#:cxcp:inline-copy-args (car l) x)
,(#:cxcp:inline-copy-args (cdr l) x)))))
; L'expansion pour une liste de S-expr.
; retourne une liste de sexpr.
(de #:cxcp:inliner-aux-body (lbody larg)
(mapcar (lambda (x) (#:cxcp:inliner-aux x larg)) lbody))
(de #:cxcp:inliner-aux (body larg)
(setq body (#:compiler:macroexpand body))
(cond
((null body) ())
((atomp body) (if (memq body larg) body (kwote body)))
; la FSUBR la plus triviale !
((eq (car body) 'quote) (kwote body))
((memq (car body) '(lambda mlambda flambda))
; La portee syntaxique est delicate, ici !
(mcons 'list (kwote (car body)) (kwote (cadr body))
(#:cxcp:inliner-aux-body (cddr body)
(#:cxcp:inliner-delq larg (cadr body)))))
((consp (car body))
; Attention, ca peut etre faux!
(unless (memq (caar body) '(lambda mlambda flambda))
(print "; **** Inline expansion for " name
" can be wrong in " body))
(cons 'list (#:cxcp:inliner-aux-body body larg)))
(t (mcons 'list (kwote (car body))
(#:cxcp:inliner-aux-body (cdr body) larg)))))
(de #:cxcp:inliner-delq (larg lvar)
(cond
((null lvar) larg)
((symbolp lvar) (delq lvar larg))
(t (#:cxcp:inliner-delq
(#:cxcp:inliner-delq larg (car lvar)) (cdr lvar)))))
(de #:cxcp:danger (x)
(setq x (#:compiler:macroexpand x))
(cond
((atomp x) ())
((eq (car x) 'quote) ())
((memq (car x) '($car $cdr $vref $logand $logor $logxor $add $add1
$minus $sub $sub1 and eq or = <> equal not))
(any '#:cxcp:danger (cdr x)))
(t t)))