;;; ;;; frame-return-guts.lisp ;;; ;;; Guts of frame-return for SBCL/x86. ;;; (in-package :sb-c) ;; %frame-return needs the same sort of special-casing in the ;; compiler as %throw does. ir1-optimize-mv-combination in ir1opt.lisp ;; and ltn-analyze-mv-call in ltn.lisp. And don't forget the defknown. ;; Also needs a patch in ir2-convert-block in ir2tran.lisp. (defknown %frame-return (t t &rest t) nil) ; This is MV-called. ;;; Do stuff to notice a change to a MV combination node. There are ;;; two main branches here: ;;; -- If the call is local, then it is already a MV let, or should ;;; become one. Note that although all :LOCAL MV calls must eventually ;;; be converted to :MV-LETs, there can be a window when the call ;;; is local, but has not been LET converted yet. This is because ;;; the entry-point lambdas may have stray references (in other ;;; entry points) that have not been deleted yet. ;;; -- The call is full. This case is somewhat similar to the non-MV ;;; combination optimization: we propagate return type information and ;;; notice non-returning calls. We also have an optimization ;;; which tries to convert MV-CALLs into MV-binds. (defun ir1-optimize-mv-combination (node) (ecase (basic-combination-kind node) (:local (let ((fun-lvar (basic-combination-fun node))) (when (lvar-reoptimize fun-lvar) (setf (lvar-reoptimize fun-lvar) nil) (maybe-let-convert (combination-lambda node)))) (setf (lvar-reoptimize (first (basic-combination-args node))) nil) (when (eq (functional-kind (combination-lambda node)) :mv-let) (unless (convert-mv-bind-to-let node) (ir1-optimize-mv-bind node)))) (:full (let* ((fun (basic-combination-fun node)) (fun-changed (lvar-reoptimize fun)) (args (basic-combination-args node))) (when fun-changed (setf (lvar-reoptimize fun) nil) (let ((type (lvar-type fun))) (when (fun-type-p type) (derive-node-type node (fun-type-returns type)))) (maybe-terminate-block node nil) (let ((use (lvar-uses fun))) (when (and (ref-p use) (functional-p (ref-leaf use))) (convert-call-if-possible use node) (when (eq (basic-combination-kind node) :local) (maybe-let-convert (ref-leaf use)))))) (unless (or (eq (basic-combination-kind node) :local) (eq (lvar-fun-name fun) '%throw) (eq (lvar-fun-name fun) '%frame-return)) (ir1-optimize-mv-call node)) (dolist (arg args) (setf (lvar-reoptimize arg) nil)))) (:error)) (values)) ;;; We force all the argument lvars to use the unknown values ;;; convention. The lvars are annotated in reverse order, since the ;;; last argument is on top, thus must be popped first. We disallow ;;; delayed evaluation of the function lvar to simplify IR2 conversion ;;; of MV call. ;;; ;;; We could be cleverer when we know the number of values returned by ;;; the lvars, but optimizations of MV call are probably unworthwhile. ;;; ;;; We are also responsible for handling THROW, which is represented ;;; in IR1 as an MV call to the %THROW funny function. We annotate the ;;; tag lvar for a single value and the values lvar for unknown ;;; values. (defun ltn-analyze-mv-call (call) (declare (type mv-combination call)) (let ((fun (basic-combination-fun call)) (args (basic-combination-args call))) (cond ((eq (lvar-fun-name fun) '%throw) (setf (basic-combination-info call) :funny) (annotate-ordinary-lvar (first args)) (annotate-unknown-values-lvar (second args)) (setf (node-tail-p call) nil)) ((eq (lvar-fun-name fun) '%frame-return) (setf (basic-combination-info call) :funny) (annotate-ordinary-lvar (first args)) (annotate-ordinary-lvar (second args)) (annotate-unknown-values-lvar (third args)) (setf (node-tail-p call) nil)) (t (setf (basic-combination-info call) :full) (annotate-fun-lvar (basic-combination-fun call) nil) (dolist (arg (reverse args)) (annotate-unknown-values-lvar arg)) (flush-full-call-tail-transfer call)))) (values)) (in-package :sb-vm) ;;; Compute the address of the catch block from its TN, then store into the ;;; block the current Fp, Env, Unwind-Protect, and the entry PC. (define-vop (return-from-frame) (:args (ofp :scs #+nil (sap-reg) (#+nil any-reg descriptor-reg)) (uwp :scs #+nil (sap-reg) (#+nil any-reg descriptor-reg)) (tn) (start :scs (any-reg descriptor-reg) :target ebx) (count :scs (any-reg descriptor-reg) :target ecx)) (:arg-types system-area-pointer system-area-pointer t t t) (:temporary (:sc unsigned-reg :offset ebx-offset :from (:argument 3)) ebx) (:temporary (:sc unsigned-reg :offset ecx-offset :from (:argument 4)) ecx) (:temporary (:sc sap-reg) temp) (:temporary (:sc unsigned-reg :offset eax-offset) block) (:generator 22 ;; Set up magic catch / UWP block. (inst lea block (catch-block-ea tn)) (loadw temp uwp sap-pointer-slot other-pointer-lowtag) (storew temp block unwind-block-current-uwp-slot) (loadw temp ofp sap-pointer-slot other-pointer-lowtag) (storew temp block unwind-block-current-cont-slot) (storew (make-fixup nil :code-object entry-label) block catch-block-entry-pc-slot) ;; Fix up args. (move ebx start) (move ecx count) ;; Run any required UWPs. (inst jmp (make-fixup 'unwind :assembly-routine)) ENTRY-LABEL ;; Now just inline the core bit of return-multiple. ;; We get here with start and count in EBX and ECX, ;; and EBP already points to the frame we want to ;; return from. We need to set EBX to the value of ;; EBP, ESI and ECX to start and count, and EAX to ;; the return value from the frame, then jump to ;; the assem-routine. (inst mov esi-tn ebx-tn) (inst mov ebx-tn ebp-tn) (inst mov eax-tn (make-ea :dword :base ebp-tn :disp (- (* n-word-bytes (1+ return-pc-save-offset))))) (inst mov ebp-tn (make-ea :dword :base ebp-tn :disp (- (* n-word-bytes (1+ ocfp-save-offset))))) (inst jmp (make-fixup 'return-multiple :assembly-routine)))) (in-package :sb-c) ;;; We have to do a spurious move of no values to the result lvar so ;;; that lifetime analysis won't get confused. (defun ir2-convert-frame-return (node block) (declare (type mv-combination node) (type ir2-block block)) (let* ((args (basic-combination-args node)) (block-tn (physenv-live-tn (make-normal-tn (primitive-type-or-lose 'catch-block)) (node-physenv node)))) ;; First two args are SAPs for the frame pointer and a UWP-block. ;; Third arg is the values to return. ;(check-catch-tag-type (first args)) (vop* sb-vm::return-from-frame node block ((lvar-tn node block (first args)) (lvar-tn node block (second args)) block-tn (reference-tn-list (ir2-lvar-locs (lvar-info (third args))) nil)) (nil))) (move-lvar-result node block () (node-lvar node)) (values)) ;;; Convert the code in a block into VOPs. (defun ir2-convert-block (block) (declare (type cblock block)) (let ((2block (block-info block))) (do-nodes (node lvar block) (etypecase node (ref (when lvar (let ((2lvar (lvar-info lvar))) ;; function REF in a local call is not annotated (when (and 2lvar (not (eq (ir2-lvar-kind 2lvar) :delayed))) (ir2-convert-ref node 2block))))) (combination (let ((kind (basic-combination-kind node))) (ecase kind (:local (ir2-convert-local-call node 2block)) (:full (ir2-convert-full-call node 2block)) (:known (let* ((info (basic-combination-fun-info node)) (fun (fun-info-ir2-convert info))) (cond (fun (funcall fun node 2block)) ((eq (basic-combination-info node) :full) (ir2-convert-full-call node 2block)) (t (ir2-convert-template node 2block)))))))) (cif (when (lvar-info (if-test node)) (ir2-convert-if node 2block))) (bind (let ((fun (bind-lambda node))) (when (eq (lambda-home fun) fun) (ir2-convert-bind node 2block)))) (creturn (ir2-convert-return node 2block)) (cset (ir2-convert-set node 2block)) (cast (ir2-convert-cast node 2block)) (mv-combination (cond ((eq (basic-combination-kind node) :local) (ir2-convert-mv-bind node 2block)) ((eq (lvar-fun-name (basic-combination-fun node)) '%throw) (ir2-convert-throw node 2block)) ((eq (lvar-fun-name (basic-combination-fun node)) '%frame-return) (ir2-convert-frame-return node 2block)) (t (ir2-convert-mv-call node 2block)))) (exit (when (exit-entry node) (ir2-convert-exit node 2block))) (entry (ir2-convert-entry node 2block))))) (finish-ir2-block block) (values)) ;;; EOF