;;;; This file contains code which does the translation from Lisp code ;;;; to the first intermediate representation (IR1). ;;;; This software is part of the SBCL system. See the README file for ;;;; more information. ;;;; ;;;; This software is derived from the CMU CL system, which was ;;;; written at Carnegie Mellon University and released into the ;;;; public domain. The software is in the public domain and is ;;;; provided with absolutely no warranty. See the COPYING and CREDITS ;;;; files for more information. (in-package "SB!C") (declaim (special *compiler-error-bailout*)) ;;; *SOURCE-PATHS* is a hashtable from source code forms to the path ;;; taken through the source to reach the form. This provides a way to ;;; keep track of the location of original source forms, even when ;;; macroexpansions and other arbitary permutations of the code ;;; happen. This table is initialized by calling Find-Source-Paths on ;;; the original source. (declaim (hash-table *source-paths*)) (defvar *source-paths*) ;;; *CURRENT-COMPONENT* is the Component structure which we link ;;; blocks into as we generate them. This just serves to glue the ;;; emitted blocks together until local call analysis and flow graph ;;; canonicalization figure out what is really going on. We need to ;;; keep track of all the blocks generated so that we can delete them ;;; if they turn out to be unreachable. ;;; ;;; FIXME: It's confusing having one variable named *CURRENT-COMPONENT* ;;; and another named *COMPONENT-BEING-COMPILED*. (In CMU CL they ;;; were called *CURRENT-COMPONENT* and *COMPILE-COMPONENT* respectively, ;;; which also confusing.) (declaim (type (or component null) *current-component*)) (defvar *current-component*) ;;; *CURRENT-PATH* is the source path of the form we are currently ;;; translating. See NODE-SOURCE-PATH in the NODE structure. (declaim (list *current-path*)) (defvar *current-path* nil) ;;; *CONVERTING-FOR-INTERPRETER* is true when we are creating IR1 to ;;; be interpreted rather than compiled. This inhibits source ;;; tranformations and stuff. (defvar *converting-for-interpreter* nil) ;;; FIXME: Rename to *IR1-FOR-INTERPRETER-NOT-COMPILER-P*. ;;; FIXME: This nastiness was one of my original motivations to start ;;; hacking CMU CL. The non-ANSI behavior can be useful, but it should ;;; be made not the default, and perhaps should be controlled by ;;; DECLAIM instead of a variable like this. And whether or not this ;;; kind of checking is on, declarations should be assertions to the ;;; extent practical, and code which can't be compiled efficiently ;;; while adhering to that principle should give warnings. (defvar *derive-function-types* t #!+sb-doc "(Caution: Soon, this might change its semantics somewhat, or even go away.) If true, argument and result type information derived from compilation of DEFUNs is used when compiling calls to that function. If false, only information from FTYPE proclamations will be used.") ;;;; namespace management utilities ;;; Return a GLOBAL-VAR structure usable for referencing the global ;;; function NAME. (defun find-free-really-function (name) (unless (info :function :kind name) (setf (info :function :kind name) :function) (setf (info :function :where-from name) :assumed)) (let ((where (info :function :where-from name))) (when (eq where :assumed) (note-undefined-reference name :function)) (make-global-var :kind :global-function :name name :type (if (or *derive-function-types* (eq where :declared)) (info :function :type name) (specifier-type 'function)) :where-from where))) ;;; Return a SLOT-ACCESSOR structure usable for referencing the slot ;;; accessor NAME. CLASS is the structure class. (defun find-structure-slot-accessor (class name) (declare (type sb!xc:class class)) (let* ((info (layout-info (or (info :type :compiler-layout (sb!xc:class-name class)) (class-layout class)))) (accessor (if (listp name) (cadr name) name)) (slot (find accessor (dd-slots info) :key #'sb!kernel:dsd-accessor)) (type (dd-name info)) (slot-type (dsd-type slot))) (assert slot () "Can't find slot ~S." type) (make-slot-accessor :name name :type (specifier-type (if (listp name) `(function (,slot-type ,type) ,slot-type) `(function (,type) ,slot-type))) :for class :slot slot))) ;;; If NAME is already entered in *FREE-FUNCTIONS*, then return the ;;; value. Otherwise, make a new GLOBAL-VAR using information from the ;;; global environment and enter it in *FREE-FUNCTIONS*. If NAME names ;;; a macro or special form, then we error out using the supplied ;;; context which indicates what we were trying to do that demanded a ;;; function. (defun find-free-function (name context) (declare (string context)) (declare (values global-var)) (or (gethash name *free-functions*) (ecase (info :function :kind name) ;; FIXME: The :MACRO and :SPECIAL-FORM cases could be merged. (:macro (compiler-error "The macro name ~S was found ~A." name context)) (:special-form (compiler-error "The special form name ~S was found ~A." name context)) ((:function nil) (check-function-name name) (note-if-setf-function-and-macro name) (let ((expansion (info :function :inline-expansion name)) (inlinep (info :function :inlinep name))) (setf (gethash name *free-functions*) (if (or expansion inlinep) (make-defined-function :name name :inline-expansion expansion :inlinep inlinep :where-from (info :function :where-from name) :type (info :function :type name)) (let ((info (info :function :accessor-for name))) (etypecase info (null (find-free-really-function name)) (sb!xc:structure-class (find-structure-slot-accessor info name)) (sb!xc:class (if (typep (layout-info (info :type :compiler-layout (sb!xc:class-name info))) 'defstruct-description) (find-structure-slot-accessor info name) (find-free-really-function name)))))))))))) ;;; Return the LEAF structure for the lexically apparent function ;;; definition of NAME. (declaim (ftype (function (t string) leaf) find-lexically-apparent-function)) (defun find-lexically-apparent-function (name context) (let ((var (lexenv-find name functions :test #'equal))) (cond (var (unless (leaf-p var) (assert (and (consp var) (eq (car var) 'macro))) (compiler-error "found macro name ~S ~A" name context)) var) (t (find-free-function name context))))) ;;; Return the LEAF node for a global variable reference to NAME. If ;;; NAME is already entered in *FREE-VARIABLES*, then we just return ;;; the corresponding value. Otherwise, we make a new leaf using ;;; information from the global environment and enter it in ;;; *FREE-VARIABLES*. If the variable is unknown, then we emit a ;;; warning. (defun find-free-variable (name) (declare (values (or leaf heap-alien-info))) (unless (symbolp name) (compiler-error "Variable name is not a symbol: ~S." name)) (or (gethash name *free-variables*) (let ((kind (info :variable :kind name)) (type (info :variable :type name)) (where-from (info :variable :where-from name))) (when (and (eq where-from :assumed) (eq kind :global)) (note-undefined-reference name :variable)) (setf (gethash name *free-variables*) (if (eq kind :alien) (info :variable :alien-info name) (multiple-value-bind (val valp) (info :variable :constant-value name) (if (and (eq kind :constant) valp) (make-constant :value val :name name :type (ctype-of val) :where-from where-from) (make-global-var :kind kind :name name :type type :where-from where-from)))))))) ;;; Grovel over CONSTANT checking for any sub-parts that need to be ;;; processed with MAKE-LOAD-FORM. We have to be careful, because ;;; CONSTANT might be circular. We also check that the constant (and ;;; any subparts) are dumpable at all. (eval-when (:compile-toplevel :load-toplevel :execute) ;; The EVAL-WHEN is necessary for #.(1+ LIST-TO-HASH-TABLE-THRESHOLD) ;; below. -- AL 20010227 (defconstant list-to-hash-table-threshold 32)) (defun maybe-emit-make-load-forms (constant) (let ((things-processed nil) (count 0)) ;; FIXME: Does this LIST-or-HASH-TABLE messiness give much benefit? (declare (type (or list hash-table) things-processed) (type (integer 0 #.(1+ list-to-hash-table-threshold)) count) (inline member)) (labels ((grovel (value) ;; Unless VALUE is an object which which obviously ;; can't contain other objects (unless (typep value '(or #-sb-xc-host unboxed-array symbol number character string)) (etypecase things-processed (list (when (member value things-processed :test #'eq) (return-from grovel nil)) (push value things-processed) (incf count) (when (> count list-to-hash-table-threshold) (let ((things things-processed)) (setf things-processed (make-hash-table :test 'eq)) (dolist (thing things) (setf (gethash thing things-processed) t))))) (hash-table (when (gethash value things-processed) (return-from grovel nil)) (setf (gethash value things-processed) t))) (typecase value (cons (grovel (car value)) (grovel (cdr value))) (simple-vector (dotimes (i (length value)) (grovel (svref value i)))) ((vector t) (dotimes (i (length value)) (grovel (aref value i)))) ((simple-array t) ;; Even though the (ARRAY T) branch does the exact ;; same thing as this branch we do this separately ;; so that the compiler can use faster versions of ;; array-total-size and row-major-aref. (dotimes (i (array-total-size value)) (grovel (row-major-aref value i)))) ((array t) (dotimes (i (array-total-size value)) (grovel (row-major-aref value i)))) (;; In the target SBCL, we can dump any instance, ;; but in the cross-compilation host, ;; %INSTANCE-FOO functions don't work on general ;; instances, only on STRUCTURE!OBJECTs. #+sb-xc-host structure!object #-sb-xc-host instance (when (emit-make-load-form value) (dotimes (i (%instance-length value)) (grovel (%instance-ref value i))))) (t (compiler-error "Objects of type ~S can't be dumped into fasl files." (type-of value))))))) (grovel constant))) (values)) ;;;; some flow-graph hacking utilities ;;; This function sets up the back link between the node and the ;;; continuation which continues at it. #!-sb-fluid (declaim (inline prev-link)) (defun prev-link (node cont) (declare (type node node) (type continuation cont)) (assert (not (continuation-next cont))) (setf (continuation-next cont) node) (setf (node-prev node) cont)) ;;; This function is used to set the continuation for a node, and thus ;;; determine what receives the value and what is evaluated next. If ;;; the continuation has no block, then we make it be in the block ;;; that the node is in. If the continuation heads its block, we end ;;; our block and link it to that block. If the continuation is not ;;; currently used, then we set the derived-type for the continuation ;;; to that of the node, so that a little type propagation gets done. ;;; ;;; We also deal with a bit of THE's semantics here: we weaken the ;;; assertion on CONT to be no stronger than the assertion on CONT in ;;; our scope. See the IR1-CONVERT method for THE. #!-sb-fluid (declaim (inline use-continuation)) (defun use-continuation (node cont) (declare (type node node) (type continuation cont)) (let ((node-block (continuation-block (node-prev node)))) (case (continuation-kind cont) (:unused (setf (continuation-block cont) node-block) (setf (continuation-kind cont) :inside-block) (setf (continuation-use cont) node) (setf (node-cont node) cont)) (t (%use-continuation node cont))))) (defun %use-continuation (node cont) (declare (type node node) (type continuation cont) (inline member)) (let ((block (continuation-block cont)) (node-block (continuation-block (node-prev node)))) (assert (eq (continuation-kind cont) :block-start)) (assert (not (block-last node-block)) () "~S has already ended." node-block) (setf (block-last node-block) node) (assert (null (block-succ node-block)) () "~S already has successors." node-block) (setf (block-succ node-block) (list block)) (assert (not (member node-block (block-pred block) :test #'eq)) () "~S is already a predecessor of ~S." node-block block) (push node-block (block-pred block)) (add-continuation-use node cont) (unless (eq (continuation-asserted-type cont) *wild-type*) (let ((new (values-type-union (continuation-asserted-type cont) (or (lexenv-find cont type-restrictions) *wild-type*)))) (when (type/= new (continuation-asserted-type cont)) (setf (continuation-asserted-type cont) new) (reoptimize-continuation cont)))))) ;;;; exported functions ;;; This function takes a form and the top-level form number for that ;;; form, and returns a lambda representing the translation of that ;;; form in the current global environment. The lambda is top-level ;;; lambda that can be called to cause evaluation of the forms. This ;;; lambda is in the initial component. If FOR-VALUE is T, then the ;;; value of the form is returned from the function, otherwise NIL is ;;; returned. ;;; ;;; This function may have arbitrary effects on the global environment ;;; due to processing of PROCLAIMs and EVAL-WHENs. All syntax error ;;; checking is done, with erroneous forms being replaced by a proxy ;;; which signals an error if it is evaluated. Warnings about possibly ;;; inconsistent or illegal changes to the global environment will ;;; also be given. ;;; ;;; We make the initial component and convert the form in a PROGN (and ;;; an optional NIL tacked on the end.) We then return the lambda. We ;;; bind all of our state variables here, rather than relying on the ;;; global value (if any) so that IR1 conversion will be reentrant. ;;; This is necessary for EVAL-WHEN processing, etc. ;;; ;;; The hashtables used to hold global namespace info must be ;;; reallocated elsewhere. Note also that *LEXENV* is not bound, so ;;; that local macro definitions can be introduced by enclosing code. (defun ir1-top-level (form path for-value) (declare (list path)) (let* ((*current-path* path) (component (make-empty-component)) (*current-component* component)) (setf (component-name component) "initial component") (setf (component-kind component) :initial) (let* ((forms (if for-value `(,form) `(,form nil))) (res (ir1-convert-lambda-body forms ()))) (setf (leaf-name res) "top-level form") (setf (functional-entry-function res) res) (setf (functional-arg-documentation res) ()) (setf (functional-kind res) :top-level) res))) ;;; *CURRENT-FORM-NUMBER* is used in FIND-SOURCE-PATHS to compute the ;;; form number to associate with a source path. This should be bound ;;; to 0 around the processing of each truly top-level form. (declaim (type index *current-form-number*)) (defvar *current-form-number*) ;;; This function is called on freshly read forms to record the ;;; initial location of each form (and subform.) Form is the form to ;;; find the paths in, and TLF-Num is the top-level form number of the ;;; truly top-level form. ;;; ;;; This gets a bit interesting when the source code is circular. This ;;; can (reasonably?) happen in the case of circular list constants. (defun find-source-paths (form tlf-num) (declare (type index tlf-num)) (let ((*current-form-number* 0)) (sub-find-source-paths form (list tlf-num))) (values)) (defun sub-find-source-paths (form path) (unless (gethash form *source-paths*) (setf (gethash form *source-paths*) (list* 'original-source-start *current-form-number* path)) (incf *current-form-number*) (let ((pos 0) (subform form) (trail form)) (declare (fixnum pos)) (macrolet ((frob () '(progn (when (atom subform) (return)) (let ((fm (car subform))) (when (consp fm) (sub-find-source-paths fm (cons pos path))) (incf pos)) (setq subform (cdr subform)) (when (eq subform trail) (return))))) (loop (frob) (frob) (setq trail (cdr trail))))))) ;;;; IR1-CONVERT, macroexpansion and special form dispatching (macrolet (;; Bind *COMPILER-ERROR-BAILOUT* to a function that throws ;; out of the body and converts a proxy form instead. (ir1-error-bailout ((start cont form &optional (proxy ``(error "execution of a form compiled with errors:~% ~S" ',,form))) &body body) (let ((skip (gensym "SKIP"))) `(block ,skip (catch 'ir1-error-abort (let ((*compiler-error-bailout* #'(lambda () (throw 'ir1-error-abort nil)))) ,@body (return-from ,skip nil))) (ir1-convert ,start ,cont ,proxy))))) ;; Translate FORM into IR1. The code is inserted as the NEXT of the ;; continuation START. CONT is the continuation which receives the ;; value of the FORM to be translated. The translators call this ;; function recursively to translate their subnodes. ;; ;; As a special hack to make life easier in the compiler, a LEAF ;; IR1-converts into a reference to that LEAF structure. This allows ;; the creation using backquote of forms that contain leaf ;; references, without having to introduce dummy names into the ;; namespace. (declaim (ftype (function (continuation continuation t) (values)) ir1-convert)) (defun ir1-convert (start cont form) (ir1-error-bailout (start cont form) (let ((*current-path* (or (gethash form *source-paths*) (cons form *current-path*)))) (if (atom form) (cond ((and (symbolp form) (not (keywordp form))) (ir1-convert-variable start cont form)) ((leaf-p form) (reference-leaf start cont form)) (t (reference-constant start cont form))) (let ((fun (car form))) (cond ((symbolp fun) (let ((lexical-def (lexenv-find fun functions))) (typecase lexical-def (null (ir1-convert-global-functoid start cont form)) (functional (ir1-convert-local-combination start cont form lexical-def)) (global-var (ir1-convert-srctran start cont lexical-def form)) (t (assert (and (consp lexical-def) (eq (car lexical-def) 'macro))) (ir1-convert start cont (careful-expand-macro (cdr lexical-def) form)))))) ((or (atom fun) (not (eq (car fun) 'lambda))) (compiler-error "illegal function call")) (t (ir1-convert-combination start cont form (ir1-convert-lambda fun)))))))) (values)) ;; Generate a reference to a manifest constant, creating a new leaf ;; if necessary. If we are producing a fasl-file, make sure that ;; MAKE-LOAD-FORM gets used on any parts of the constant that it ;; needs to be. (defun reference-constant (start cont value) (declare (type continuation start cont) (inline find-constant)) (ir1-error-bailout (start cont value '(error "attempt to reference undumpable constant")) (when (producing-fasl-file) (maybe-emit-make-load-forms value)) (let* ((leaf (find-constant value)) (res (make-ref (leaf-type leaf) leaf))) (push res (leaf-refs leaf)) (prev-link res start) (use-continuation res cont))) (values))) ;;; Add Fun to the COMPONENT-REANALYZE-FUNCTIONS. Fun is returned. (defun maybe-reanalyze-function (fun) (declare (type functional fun)) (when (typep fun '(or optional-dispatch clambda)) (pushnew fun (component-reanalyze-functions *current-component*))) fun) ;;; Generate a Ref node for LEAF, frobbing the LEAF structure as ;;; needed. If LEAF represents a defined function which has already ;;; been converted, and is not :NOTINLINE, then reference the ;;; functional instead. (defun reference-leaf (start cont leaf) (declare (type continuation start cont) (type leaf leaf)) (let* ((leaf (or (and (defined-function-p leaf) (not (eq (defined-function-inlinep leaf) :notinline)) (let ((fun (defined-function-functional leaf))) (when (and fun (not (functional-kind fun))) (maybe-reanalyze-function fun)))) leaf)) (res (make-ref (or (lexenv-find leaf type-restrictions) (leaf-type leaf)) leaf))) (push res (leaf-refs leaf)) (setf (leaf-ever-used leaf) t) (prev-link res start) (use-continuation res cont))) ;;; Convert a reference to a symbolic constant or variable. If the ;;; symbol is entered in the LEXENV-VARIABLES we use that definition, ;;; otherwise we find the current global definition. This is also ;;; where we pick off symbol macro and Alien variable references. (defun ir1-convert-variable (start cont name) (declare (type continuation start cont) (symbol name)) (let ((var (or (lexenv-find name variables) (find-free-variable name)))) (etypecase var (leaf (when (and (lambda-var-p var) (lambda-var-ignorep var)) ;; (ANSI's specification for the IGNORE declaration requires ;; that this be a STYLE-WARNING, not a full WARNING.) (compiler-style-warning "reading an ignored variable: ~S" name)) (reference-leaf start cont var)) (cons (assert (eq (car var) 'MACRO)) (ir1-convert start cont (cdr var))) (heap-alien-info (ir1-convert start cont `(%heap-alien ',var))))) (values)) ;;; Convert anything that looks like a special form, global function ;;; or macro call. (defun ir1-convert-global-functoid (start cont form) (declare (type continuation start cont) (list form)) (let* ((fun (first form)) (translator (info :function :ir1-convert fun)) (cmacro (info :function :compiler-macro-function fun))) (cond (translator (funcall translator start cont form)) ((and cmacro (not *converting-for-interpreter*) (not (eq (info :function :inlinep fun) :notinline))) (let ((res (careful-expand-macro cmacro form))) (if (eq res form) (ir1-convert-global-functoid-no-cmacro start cont form fun) (ir1-convert start cont res)))) (t (ir1-convert-global-functoid-no-cmacro start cont form fun))))) ;;; Handle the case of where the call was not a compiler macro, or was a ;;; compiler macro and passed. (defun ir1-convert-global-functoid-no-cmacro (start cont form fun) (declare (type continuation start cont) (list form)) ;; FIXME: Couldn't all the INFO calls here be converted into ;; standard CL functions, like MACRO-FUNCTION or something? ;; And what happens with lexically-defined (MACROLET) macros ;; here, anyway? (ecase (info :function :kind fun) (:macro (ir1-convert start cont (careful-expand-macro (info :function :macro-function fun) form))) ((nil :function) (ir1-convert-srctran start cont (find-free-function fun "Eh?") form)))) (defun muffle-warning-or-die () (muffle-warning) (error "internal error -- no MUFFLE-WARNING restart")) ;;; Trap errors during the macroexpansion. (defun careful-expand-macro (fun form) (handler-bind (;; When cross-compiling, we can get style warnings ;; about e.g. undefined functions. An unhandled ;; CL:STYLE-WARNING (as opposed to a ;; SB!C::COMPILER-NOTE) would cause FAILURE-P to be ;; set on the return from #'SB!XC:COMPILE-FILE, which ;; would falsely indicate an error sufficiently ;; serious that we should stop the build process. To ;; avoid this, we translate CL:STYLE-WARNING ;; conditions from the host Common Lisp into ;; cross-compiler SB!C::COMPILER-NOTE calls. (It ;; might be cleaner to just make Python use ;; CL:STYLE-WARNING internally, so that the ;; significance of any host Common Lisp ;; CL:STYLE-WARNINGs is understood automatically. But ;; for now I'm not motivated to do this. -- WHN ;; 19990412) (style-warning (lambda (c) (compiler-note "(during macroexpansion)~%~A" c) (muffle-warning-or-die))) ;; KLUDGE: CMU CL in its wisdom (version 2.4.6 for ;; Debian Linux, anyway) raises a CL:WARNING ;; condition (not a CL:STYLE-WARNING) for undefined ;; symbols when converting interpreted functions, ;; causing COMPILE-FILE to think the file has a real ;; problem, causing COMPILE-FILE to return FAILURE-P ;; set (not just WARNINGS-P set). Since undefined ;; symbol warnings are often harmless forward ;; references, and since it'd be inordinately painful ;; to try to eliminate all such forward references, ;; these warnings are basically unavoidable. Thus, we ;; need to coerce the system to work through them, ;; and this code does so, by crudely suppressing all ;; warnings in cross-compilation macroexpansion. -- ;; WHN 19990412 #+cmu (warning (lambda (c) (compiler-note "(during macroexpansion)~%~ ~A~%~ (KLUDGE: That was a non-STYLE WARNING.~%~ Ordinarily that would cause compilation to~%~ fail. However, since we're running under~%~ CMU CL, and since CMU CL emits non-STYLE~%~ warnings for safe, hard-to-fix things (e.g.~%~ references to not-yet-defined functions)~%~ we're going to have to ignore it and proceed~%~ anyway. Hopefully we're not ignoring anything~%~ horrible here..)~%" c) (muffle-warning-or-die))) (error (lambda (c) (compiler-error "(during macroexpansion)~%~A" c)))) (funcall sb!xc:*macroexpand-hook* fun form *lexenv*))) ;;;; conversion utilities ;;; Convert a bunch of forms, discarding all the values except the ;;; last. If there aren't any forms, then translate a NIL. (declaim (ftype (function (continuation continuation list) (values)) ir1-convert-progn-body)) (defun ir1-convert-progn-body (start cont body) (if (endp body) (reference-constant start cont nil) (let ((this-start start) (forms body)) (loop (let ((form (car forms))) (when (endp (cdr forms)) (ir1-convert this-start cont form) (return)) (let ((this-cont (make-continuation))) (ir1-convert this-start this-cont form) (setq this-start this-cont forms (cdr forms))))))) (values)) ;;;; converting combinations ;;; Convert a function call where the function (Fun) is a Leaf. We ;;; return the Combination node so that we can poke at it if we want to. (declaim (ftype (function (continuation continuation list leaf) combination) ir1-convert-combination)) (defun ir1-convert-combination (start cont form fun) (let ((fun-cont (make-continuation))) (reference-leaf start fun-cont fun) (ir1-convert-combination-args fun-cont cont (cdr form)))) ;;; Convert the arguments to a call and make the Combination node. Fun-Cont ;;; is the continuation which yields the function to call. Form is the source ;;; for the call. Args is the list of arguments for the call, which defaults ;;; to the cdr of source. We return the Combination node. (defun ir1-convert-combination-args (fun-cont cont args) (declare (type continuation fun-cont cont) (list args)) (let ((node (make-combination fun-cont))) (setf (continuation-dest fun-cont) node) (assert-continuation-type fun-cont (specifier-type '(or function symbol))) (collect ((arg-conts)) (let ((this-start fun-cont)) (dolist (arg args) (let ((this-cont (make-continuation node))) (ir1-convert this-start this-cont arg) (setq this-start this-cont) (arg-conts this-cont))) (prev-link node this-start) (use-continuation node cont) (setf (combination-args node) (arg-conts)))) node)) ;;; Convert a call to a global function. If not :NOTINLINE, then we do ;;; source transforms and try out any inline expansion. If there is no ;;; expansion, but is :INLINE, then give an efficiency note (unless a known ;;; function which will quite possibly be open-coded.) Next, we go to ;;; ok-combination conversion. (defun ir1-convert-srctran (start cont var form) (declare (type continuation start cont) (type global-var var)) (let ((inlinep (when (defined-function-p var) (defined-function-inlinep var)))) (cond ((eq inlinep :notinline) (ir1-convert-combination start cont form var)) (*converting-for-interpreter* (ir1-convert-combination-checking-type start cont form var)) (t (let ((transform (info :function :source-transform (leaf-name var)))) (cond (transform (multiple-value-bind (result pass) (funcall transform form) (if pass (ir1-convert-maybe-predicate start cont form var) (ir1-convert start cont result)))) (t (ir1-convert-maybe-predicate start cont form var)))))))) ;;; If the function has the Predicate attribute, and the CONT's DEST isn't ;;; an IF, then we convert (IF