ecl/src/cmp/cmpfun.lsp
Marius Gerbershagen f099a9082a cmp: move proclamations from system-properties into a compiler specific storage
This allows us to switch out the proclamations when cross-compiling so
that target specific functions are declared correctly.

This was a problem for cross compilation with mismatching thread
support, so we can now allow that.
2025-11-22 16:25:42 +01:00

177 lines
6.5 KiB
Common Lisp

;;;;
;;;; Copyright (c) 1984, Taiichi Yuasa and Masami Hagiya
;;;; Copyright (c) 1990, Giuseppe Attardi
;;;; Copyright (c) 2010, Juan Jose Garcia-Ripoll
;;;; Copyright (c) 2021, Daniel Kochmański
;;;;
;;;; See file 'LICENSE' for the copyright details.
;;;;
(in-package #:compiler)
(defun register-function (fun)
(push fun *functions*))
(defun child-function-p (presumed-parent fun)
(declare (optimize speed))
(loop for real-parent = (fun-parent fun)
while real-parent
do (if (eq real-parent presumed-parent)
(return t)
(setf fun real-parent))))
(defun compute-closure-type (fun)
(declare (si::c-local))
(let ((lexical-closure-p nil))
;; it will have a full closure if it refers external non-global variables
(dolist (var (fun-referenced-vars fun))
(cond ((global-var-p var))
;; ...across CB
((ref-ref-ccb var)
(return-from compute-closure-type 'CLOSURE))
(t
(setf lexical-closure-p t))))
;; ...or if it directly calls a function
(dolist (f (fun-referenced-funs fun))
(unless (child-function-p fun f)
;; .. which has a full closure
(case (fun-closure f)
(CLOSURE (return-from compute-closure-type 'CLOSURE))
(LEXICAL (setf lexical-closure-p t)))))
;; ...or the function itself is referred across CB
(when lexical-closure-p
(if (or (fun-ref-ccb fun)
(and (fun-var fun)
(plusp (var-ref (fun-var fun)))))
'CLOSURE
'LEXICAL))))
(defun update-fun-closure-type-many (function-list)
(do ((finish nil t)
(recompute nil))
(finish
recompute)
(dolist (f function-list)
(when (update-fun-closure-type f)
(setf recompute t finish nil)))))
(defun prepend-new (l1 l2)
(loop for f in l1
do (pushnew f l2))
l2)
(defun update-fun-closure-type (fun)
(let ((old-type (fun-closure fun)))
(when (eq old-type 'closure)
(return-from update-fun-closure-type nil))
;; This recursive algorithm is guaranteed to stop when functions
;; do not change.
(let ((new-type (compute-closure-type fun))
to-be-updated)
;; Same type
(when (eq new-type old-type)
(return-from update-fun-closure-type nil))
(when (fun-global fun)
(cmpnote "Function ~A is global but is closed over some variables.~%~{~A ~}"
(fun-name fun) (mapcar #'var-name (fun-referenced-vars fun))))
(setf to-be-updated (append (fun-child-funs fun) (fun-referencing-funs fun)))
(setf (fun-closure fun) new-type)
;; All external, non-global variables become of type closure
(when (eq new-type 'CLOSURE)
(dolist (var (fun-referenced-vars fun))
(unless (or (global-var-p var)
(eq (var-kind var) new-type))
(setf (var-ref-clb var) nil
(var-ref-ccb var) t
(var-kind var) 'CLOSURE
to-be-updated
(prepend-new (var-functions-reading var)
(prepend-new (var-functions-setting var)
to-be-updated)))))
(dolist (f (fun-referenced-funs fun))
(setf (fun-ref-ccb f) t)))
;; If the status of some of the children changes, we have
;; to recompute the closure type.
(when (update-fun-closure-type-many to-be-updated)
(update-fun-closure-type fun))
t)))
(defun local-function-ref (fname &optional build-object)
(multiple-value-bind (fun cfb unw)
(cmp-env-search-function fname)
(declare (ignore unw))
(when fun
(when (functionp fun)
(when build-object
;; Macro definition appears in #'.... This should not happen.
(cmperr "The name of a macro ~A was found in special form FUNCTION." fname))
(return-from local-function-ref nil))
(incf (fun-ref fun))
(if build-object
(setf (fun-ref-ccb fun) t)
(let ((caller *current-function*))
(when (and caller
(not (member fun (fun-referenced-funs caller) :test #'eq)))
(push fun (fun-referenced-funs caller))
(push caller (fun-referencing-funs fun)))))
;; we introduce a variable to hold the funob
(let ((var (fun-var fun)))
(when (and cfb build-object)
(setf (var-ref-clb var) t)
(when (not (eq (var-kind var) 'CLOSURE))
(setf (var-kind var) 'LEXICAL)))))
fun))
(defun fun-needs-narg (fun)
(not (fun-fixed-narg fun)))
(defun fun-fixed-narg (fun)
"Returns true if the function has a fixed number of arguments and it is not a closure.
The function thus belongs to the type of functions that ecl_make_cfun accepts."
(let (narg)
(and (not (eq (fun-closure fun) 'CLOSURE))
(= (fun-minarg fun) (setf narg (fun-maxarg fun)))
(<= narg si:c-arguments-limit)
narg)))
(defun add-to-fun-referenced-vars (fun var-list)
(loop with new-vars = (fun-referenced-vars fun)
with locals = (fun-local-vars fun)
with change = nil
for v in var-list
when (and (not (member v locals :test #'eq))
(not (member v new-vars :test #'eq)))
do (setf change t new-vars (cons v new-vars))
finally (when change
(setf (fun-referenced-vars fun) new-vars)
(return t))))
(defun add-to-fun-referenced-funs (fun fun-list)
(loop with new-funs = (fun-referenced-funs fun)
with change = nil
for f in fun-list
when (and (not (eq fun f))
(not (member f new-funs :test #'eq))
(not (child-function-p fun f)))
do (setf change t
new-funs (cons f new-funs)
(fun-referencing-funs f) (cons fun (fun-referencing-funs f)))
finally (when change
(setf (fun-referenced-funs fun) new-funs)
(return t))))
;;; searches for a (FUNCTION-BLOCK-NAME ...) declaration
(defun function-block-name-declaration (declarations)
(loop for i in declarations
do (when (and (consp i) (eql (car i) 'si:function-block-name))
(let ((name (second i))
(rest (cddr i)))
(unless (and (symbolp name) (null rest))
(cmperr "Invalid ~s declaration:~%~s" 'si:function-block-name i))
(return name)))))
(defun exported-fname (name)
(let (cname)
(if (and (symbolp name) (setf cname (si:get-sysprop name 'Lfun)))
(values cname t)
(values (next-cfun "L~D~A" name) nil))))