⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 call.lisp

📁 开源跨平台Lisp编译器
💻 LISP
📖 第 1 页 / 共 4 页
字号:
               '((:results (values :more t))))               (:save-p ,(if (eq return :tail) :compute-only t))               ,@(unless (or (eq return :tail) variable)               '((:move-args :full-call)))               (:vop-var vop)               (:info               ,@(unless (or variable (eq return :tail)) '(arg-locs))               ,@(unless variable '(nargs))               ,@(when (eq return :fixed) '(nvals))               step-instrumenting)               (:ignore               ,@(unless (or variable (eq return :tail)) '(arg-locs))               ,@(unless variable '(args)))               ;; We pass either the fdefn object (for named call) or               ;; the actual function object (for unnamed call) in               ;; RAX. With named call, closure-tramp will replace it               ;; with the real function and invoke the real function               ;; for closures. Non-closures do not need this value,               ;; so don't care what shows up in it.               (:temporary               (:sc descriptor-reg                    :offset rax-offset                    :from (:argument 0)                    :to :eval)               rax)               ;; We pass the number of arguments in RCX.               (:temporary (:sc unsigned-reg :offset rcx-offset :to :eval) rcx)               ;; With variable call, we have to load the               ;; register-args out of the (new) stack frame before               ;; doing the call. Therefore, we have to tell the               ;; lifetime stuff that we need to use them.               ,@(when variable                   (mapcar (lambda (name offset)                             `(:temporary (:sc descriptor-reg                                               :offset ,offset                                               :from (:argument 0)                                               :to :eval)                                          ,name))                           *register-arg-names* *register-arg-offsets*))               ,@(when (eq return :tail)                   '((:temporary (:sc unsigned-reg                                      :from (:argument 1)                                      :to (:argument 2))                                 old-fp-tmp)))               (:generator ,(+ (if named 5 0)                               (if variable 19 1)                               (if (eq return :tail) 0 10)                               15                               (if (eq return :unknown) 25 0))               (trace-table-entry trace-table-call-site)               ;; This has to be done before the frame pointer is               ;; changed! RAX stores the 'lexical environment' needed               ;; for closures.               (move rax fun)               ,@(if variable                     ;; For variable call, compute the number of                     ;; arguments and move some of the arguments to                     ;; registers.                     (collect ((noise))                              ;; Compute the number of arguments.                              (noise '(inst mov rcx new-fp))                              (noise '(inst sub rcx rsp-tn))                              ;; Move the necessary args to registers,                              ;; this moves them all even if they are                              ;; not all needed.                              (loop                               for name in *register-arg-names*                               for index downfrom -1                               do (noise `(loadw ,name new-fp ,index)))                              (noise))                   '((if (zerop nargs)                         (zeroize rcx)                       (inst mov rcx (fixnumize nargs)))))               ,@(cond ((eq return :tail)                        '(;; Python has figured out what frame we should                          ;; return to so might as well use that clue.                          ;; This seems really important to the                          ;; implementation of things like                          ;; (without-interrupts ...)                          ;;                          ;; dtc; Could be doing a tail call from a                          ;; known-local-call etc in which the old-fp                          ;; or ret-pc are in regs or in non-standard                          ;; places. If the passing location were                          ;; wired to the stack in standard locations                          ;; then these moves will be un-necessary;                          ;; this is probably best for the x86.                          (sc-case old-fp                                   ((control-stack)                                    (unless (= ocfp-save-offset                                               (tn-offset old-fp))                                      ;; FIXME: FORMAT T for stale                                      ;; diagnostic output (several of                                      ;; them around here), ick                                      (format t "** tail-call old-fp not S0~%")                                      (move old-fp-tmp old-fp)                                      (storew old-fp-tmp                                              rbp-tn                                              (- (1+ ocfp-save-offset)))))                                   ((any-reg descriptor-reg)                                    (format t "** tail-call old-fp in reg not S0~%")                                    (storew old-fp                                            rbp-tn                                            (- (1+ ocfp-save-offset)))))                          ;; For tail call, we have to push the                          ;; return-pc so that it looks like we CALLed                          ;; drspite the fact that we are going to JMP.                          (inst push return-pc)                          ))                       (t                        ;; For non-tail call, we have to save our                        ;; frame pointer and install the new frame                        ;; pointer. We can't load stack tns after this                        ;; point.                        `(;; Python doesn't seem to allocate a frame                          ;; here which doesn't leave room for the                          ;; ofp/ret stuff.                          ;; The variable args are on the stack and                          ;; become the frame, but there may be <3                          ;; args and 3 stack slots are assumed                          ;; allocate on the call. So need to ensure                          ;; there are at least 3 slots. This hack                          ;; just adds 3 more.                          ,(if variable                               '(inst sub rsp-tn (fixnumize 3)))                          ;; Save the fp                          (storew rbp-tn new-fp (- (1+ ocfp-save-offset)))                          (move rbp-tn new-fp) ; NB - now on new stack frame.                          )))               (when step-instrumenting                 (emit-single-step-test)                 (inst jmp :eq DONE)                 (inst break single-step-around-trap))               DONE               (note-this-location vop :call-site)               (inst ,(if (eq return :tail) 'jmp 'call)                     (make-ea :qword :base rax                              :disp ,(if named                                         '(- (* fdefn-raw-addr-slot                                                n-word-bytes)                                             other-pointer-lowtag)                                       '(- (* closure-fun-slot n-word-bytes)                                           fun-pointer-lowtag))))               ,@(ecase return                   (:fixed                    '((default-unknown-values vop values nvals)))                   (:unknown                    '((note-this-location vop :unknown-return)                      (receive-unknown-values values-start nvals start count)))                   (:tail))               (trace-table-entry trace-table-normal)))))  (define-full-call call nil :fixed nil)  (define-full-call call-named t  :fixed nil)  (define-full-call multiple-call nil :unknown nil)  (define-full-call multiple-call-named t :unknown nil)  (define-full-call tail-call nil :tail nil)  (define-full-call tail-call-named t :tail nil)  (define-full-call call-variable nil :fixed t)  (define-full-call multiple-call-variable nil :unknown t));;; This is defined separately, since it needs special code that BLT's;;; the arguments down. All the real work is done in the assembly;;; routine. We just set things up so that it can find what it needs.(define-vop (tail-call-variable)  (:args (args :scs (any-reg control-stack) :target rsi)         (function :scs (descriptor-reg control-stack) :target rax)         (old-fp)         (ret-addr))  (:temporary (:sc unsigned-reg :offset rsi-offset :from (:argument 0)) rsi)  (:temporary (:sc unsigned-reg :offset rax-offset :from (:argument 1)) rax)  (:temporary (:sc unsigned-reg) call-target);  (:ignore ret-addr old-fp)  (:generator 75    ;; Move these into the passing locations if they are not already there.    (move rsi args)    (move rax function)    ;; The following assumes that the return-pc and old-fp are on the    ;; stack in their standard save locations - Check this.    (unless (and (sc-is old-fp control-stack)                 (= (tn-offset old-fp) ocfp-save-offset))            (error "tail-call-variable: ocfp not on stack in standard save location?"))    (unless (and (sc-is ret-addr sap-stack)                 (= (tn-offset ret-addr) return-pc-save-offset))            (error "tail-call-variable: ret-addr not on stack in standard save location?"))    (inst lea call-target          (make-ea :qword                   :disp (make-fixup 'tail-call-variable :assembly-routine)))    ;; And jump to the assembly routine.    (inst jmp call-target)));;;; unknown values return;;; Return a single-value using the Unknown-Values convention. Specifically,;;; we jump to clear the stack and jump to return-pc+3.;;;;;; We require old-fp to be in a register, because we want to reset RSP before;;; restoring RBP. If old-fp were still on the stack, it could get clobbered;;; by a signal.;;;;;; pfw--get wired-tn conflicts sometimes if register sc specd for args;;; having problems targeting args to regs -- using temps instead.(define-vop (return-single)  (:args (old-fp)         (return-pc)         (value))  (:ignore value)  (:generator 6    (trace-table-entry trace-table-fun-epilogue)    ;; Code structure lifted from known-return.    (sc-case return-pc      ((sap-reg)       ;; return PC in register for some reason (local call?)       ;; we jmp to the return pc after fixing the stack and frame.       (sc-case old-fp         ((control-stack)          ;; ofp on stack must be in slot 0 (the traditional storage place).          ;; Drop the stack above it and pop it off.          (cond ((zerop (tn-offset old-fp))                 (inst lea rsp-tn (make-ea :dword :base rbp-tn                                           :disp (- (* (1+ ocfp-save-offset)                                                       n-word-bytes))))                 (inst pop rbp-tn))                (t                 ;; Should this ever happen, we do the same as above, but                 ;; using (tn-offset old-fp) instead of ocfp-save-offset                 ;; (which is 0 anyway, see src/compiler/x86/vm.lisp) and                 ;; then lea rsp again against itself with a displacement                 ;; of (* (tn-offset old-fp) n-word-bytes) to clear the                 ;; rest of the stack.                 (cerror "Continue anyway"                         "VOP return-single doesn't work if old-fp (in slot ~S) is not in slot 0" (tn-offset old-fp)))))         ((any-reg descriptor-reg)          ;; ofp in reg, drop the stack and load the real fp.          (move rsp-tn rbp-tn)          (move rbp-tn old-fp)))       ;; Set single-value-return flag       (inst clc)       ;; And return       (inst jmp return-pc))      ((sap-stack)       ;; Note that this will only work right if, when old-fp is on       ;; the stack, it has a lower tn-offset than return-pc. One of       ;; the comments in known-return indicate that this is the case       ;; (in that it will be in its save location), but we may wish       ;; to assert that (in either the weaker or stronger forms).       ;; Should this ever not be the case, we should load old-fp       ;; into a temp reg while we fix the stack.       ;; Drop stack above return-pc       (inst lea rsp-tn (make-ea :dword :base rbp-tn                                 :disp (- (* (1+ (tn-offset return-pc))                                             n-word-bytes))))       ;; Set single-value return flag       (inst clc)       ;; Restore the old frame pointer       (move rbp-tn old-fp)       ;; And return, dropping the rest of the stack as we go.       (inst ret (* (tn-offset return-pc) n-word-bytes))))));;; Do unknown-values return of a fixed (other than 1) number of;;; values. The VALUES are required to be set up in the standard;;; passing locations. NVALS is the number of values returned.;;;;;; Basically, we just load RCX with the number of values returned and;;; RBX with a pointer to the values, set RSP to point to the end of;;; the values, and jump directly to return-pc.(define-vop (return)  (:args (old-fp)         (return-pc :to (:eval 1))         (values :more t))  (:ignore values)  (:info nvals)  ;; In the case of other than one value, we need these registers to  ;; tell the caller where they are and how many there are.  (:temporary (:sc unsigned-reg :offset rbx-offset) rbx)  (:temporary (:sc unsigned-reg :offset rcx-offset) rcx)  ;; We need to stretch the lifetime of return-pc past the argument  ;; registers so that we can default the argument registers without  ;; trashing return-pc.  (:temporary (:sc unsigned-reg :offset (first *register-arg-offsets*)                   :from :eval) a0)  (:temporary (:sc unsigned-reg :offset (second *register-arg-offsets*)                   :from :eval) a1)  (:temporary (:sc unsigned-reg :offset (third *register-arg-offsets*)                   :from :eval) a2)  (:generator 6    (trace-table-entry trace-table-fun-epilogue)    ;; Establish the values pointer and values count.    (move rbx rbp-tn)    (if (zerop nvals)        (zeroize rcx) ; smaller      (inst mov rcx (fixnumize nvals)))    ;; Restore the frame pointer.    (move rbp-tn old-fp)    ;; Clear as much of the stack as possible, but not past the return    ;; address.    (inst lea rsp-tn (make-ea :qword :base rbx                              :disp (- (* (max nvals 2) n-word-bytes))))    ;; Pre-default any argument register that need it.    (when (< nvals register-arg-count)      (let* ((arg-tns (nthcdr nvals (list a0 a1 a2)))             (first (first arg-tns)))        (inst mov first nil-value)        (dolist (tn (cdr arg-tns))          (inst mov tn first))))    ;; Set the multiple value return flag.    (inst stc)    ;; And away we go. Except that return-pc is still on the    ;; stack and we've changed the stack pointer. So we have to    ;; tell it to index off of RBX instead of RBP.    (cond ((zerop nvals)           ;; Return popping the return address and the OCFP.           (inst ret n-word-bytes))          ((= nvals 1)           ;; Return popping the return, leaving 1 slot. Can this           ;; happen, or is a single value return handled elsewhere?           (inst ret))          (t           (inst jmp (make-ea :qword :base rbx                              :disp (- (* (1+ (tn-offset return-pc))                                          n-word-bytes))))))    (trace-table-entry trace-table-normal)));;; Do unknown-values return of an arbitrary number of values (passed;;; on the stack.) We check for the common case of a single return;;; value, and do that inline using the normal single value return;;; convention. Otherwise, we branch off to code that calls an;;; assembly-routine.;;;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -