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

📄 call.lisp

📁 开源跨平台Lisp编译器
💻 LISP
📖 第 1 页 / 共 4 页
字号:
;;;; function call for the x86 VM;;;; 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!VM");;;; interfaces to IR2 conversion;;; Return a wired TN describing the N'th full call argument passing;;; location.(!def-vm-support-routine standard-arg-location (n)  (declare (type unsigned-byte n))  (if (< n register-arg-count)      (make-wired-tn *backend-t-primitive-type* descriptor-reg-sc-number                     (nth n *register-arg-offsets*))      (make-wired-tn *backend-t-primitive-type* control-stack-sc-number n)));;; Make a passing location TN for a local call return PC.;;;;;; Always wire the return PC location to the stack in its standard;;; location.(!def-vm-support-routine make-return-pc-passing-location (standard)  (declare (ignore standard))  (make-wired-tn (primitive-type-or-lose 'system-area-pointer)                 sap-stack-sc-number return-pc-save-offset));;; This is similar to MAKE-RETURN-PC-PASSING-LOCATION, but makes a;;; location to pass OLD-FP in.;;;;;; This is wired in both the standard and the local-call conventions,;;; because we want to be able to assume it's always there. Besides,;;; the x86 doesn't have enough registers to really make it profitable;;; to pass it in a register.(!def-vm-support-routine make-old-fp-passing-location (standard)  (declare (ignore standard))  (make-wired-tn *fixnum-primitive-type* control-stack-sc-number                 ocfp-save-offset));;; Make the TNs used to hold OLD-FP and RETURN-PC within the current;;; function. We treat these specially so that the debugger can find;;; them at a known location.;;;;;; Without using a save-tn - which does not make much sense if it is;;; wired to the stack?(!def-vm-support-routine make-old-fp-save-location (physenv)  (physenv-debug-live-tn (make-wired-tn *fixnum-primitive-type*                                        control-stack-sc-number                                        ocfp-save-offset)                         physenv))(!def-vm-support-routine make-return-pc-save-location (physenv)  (physenv-debug-live-tn   (make-wired-tn (primitive-type-or-lose 'system-area-pointer)                  sap-stack-sc-number return-pc-save-offset)   physenv));;; Make a TN for the standard argument count passing location. We only;;; need to make the standard location, since a count is never passed when we;;; are using non-standard conventions.(!def-vm-support-routine make-arg-count-location ()  (make-wired-tn *fixnum-primitive-type* any-reg-sc-number rcx-offset));;; Make a TN to hold the number-stack frame pointer. This is allocated;;; once per component, and is component-live.(!def-vm-support-routine make-nfp-tn ()  (make-restricted-tn *fixnum-primitive-type* ignore-me-sc-number))(!def-vm-support-routine make-stack-pointer-tn ()  (make-normal-tn *fixnum-primitive-type*))(!def-vm-support-routine make-number-stack-pointer-tn ()  (make-restricted-tn *fixnum-primitive-type* ignore-me-sc-number));;; Return a list of TNs that can be used to represent an unknown-values;;; continuation within a function.(!def-vm-support-routine make-unknown-values-locations ()  (list (make-stack-pointer-tn)        (make-normal-tn *fixnum-primitive-type*)));;; This function is called by the ENTRY-ANALYZE phase, allowing;;; VM-dependent initialization of the IR2-COMPONENT structure. We;;; push placeholder entries in the CONSTANTS to leave room for;;; additional noise in the code object header.(!def-vm-support-routine select-component-format (component)  (declare (type component component))  ;; The 1+ here is because for the x86 the first constant is a  ;; pointer to a list of fixups, or NIL if the code object has none.  ;; (If I understand correctly, the fixups are needed at GC copy  ;; time because the X86 code isn't relocatable.)  ;;  ;; KLUDGE: It'd be cleaner to have the fixups entry be a named  ;; element of the CODE (aka component) primitive object. However,  ;; it's currently a large, tricky, error-prone chore to change  ;; the layout of any primitive object, so for the foreseeable future  ;; we'll just live with this ugliness. -- WHN 2002-01-02  (dotimes (i (1+ code-constants-offset))    (vector-push-extend nil                        (ir2-component-constants (component-info component))))  (values));;;; frame hackery;;; This is used for setting up the Old-FP in local call.(define-vop (current-fp)  (:results (val :scs (any-reg control-stack)))  (:generator 1    (move val rbp-tn)));;; We don't have a separate NFP, so we don't need to do anything here.(define-vop (compute-old-nfp)  (:results (val))  (:ignore val)  (:generator 1    nil))(define-vop (xep-allocate-frame)  (:info start-lab copy-more-arg-follows)  (:vop-var vop)  (:generator 1    (emit-alignment n-lowtag-bits)    (trace-table-entry trace-table-fun-prologue)    (emit-label start-lab)    ;; Skip space for the function header.    (inst simple-fun-header-word)    (dotimes (i (* n-word-bytes (1- simple-fun-code-offset)))      (inst byte 0))    ;; The start of the actual code.    ;; Save the return-pc.    (popw rbp-tn (- (1+ return-pc-save-offset)))    ;; If copy-more-arg follows it will allocate the correct stack    ;; size. The stack is not allocated first here as this may expose    ;; args on the stack if they take up more space than the frame!    (unless copy-more-arg-follows      ;; The args fit within the frame so just allocate the frame.      (inst lea rsp-tn            (make-ea :qword :base rbp-tn                     :disp (- (* n-word-bytes                                 (max 3 (sb-allocated-size 'stack)))))))    (trace-table-entry trace-table-normal)));;; This is emitted directly before either a known-call-local, call-local,;;; or a multiple-call-local. All it does is allocate stack space for the;;; callee (who has the same size stack as us).(define-vop (allocate-frame)  (:results (res :scs (any-reg control-stack))            (nfp))  (:info callee)  (:ignore nfp callee)  (:generator 2    (move res rsp-tn)    (inst sub rsp-tn (* n-word-bytes (sb-allocated-size 'stack)))));;; Allocate a partial frame for passing stack arguments in a full;;; call. NARGS is the number of arguments passed. We allocate at;;; least 3 slots, because the XEP noise is going to want to use them;;; before it can extend the stack.(define-vop (allocate-full-call-frame)  (:info nargs)  (:results (res :scs (any-reg control-stack)))  (:generator 2    (move res rsp-tn)    (inst sub rsp-tn (* (max nargs 3) n-word-bytes))));;; Emit code needed at the return-point from an unknown-values call;;; for a fixed number of values. Values is the head of the TN-REF;;; list for the locations that the values are to be received into.;;; Nvals is the number of values that are to be received (should;;; equal the length of Values).;;;;;; MOVE-TEMP is a DESCRIPTOR-REG TN used as a temporary.;;;;;; This code exploits the fact that in the unknown-values convention,;;; a single value return returns at the return PC + 2, whereas a;;; return of other than one value returns directly at the return PC.;;;;;; If 0 or 1 values are expected, then we just emit an instruction to;;; reset the SP (which will only be executed when other than 1 value;;; is returned.);;;;;; In the general case we have to do three things:;;;  -- Default unsupplied register values. This need only be done;;;     when a single value is returned, since register values are;;;     defaulted by the called in the non-single case.;;;  -- Default unsupplied stack values. This needs to be done whenever;;;     there are stack values.;;;  -- Reset SP. This must be done whenever other than 1 value is;;;     returned, regardless of the number of values desired.(defun default-unknown-values (vop values nvals)  (declare (type (or tn-ref null) values)           (type unsigned-byte nvals))  (cond   ((<= nvals 1)    (note-this-location vop :single-value-return)    (inst cmov :c rsp-tn rbx-tn))   ((<= nvals register-arg-count)    (let ((regs-defaulted (gen-label)))      (note-this-location vop :unknown-return)      (inst jmp :c regs-defaulted)      ;; Default the unsupplied registers.      (let* ((2nd-tn-ref (tn-ref-across values))             (2nd-tn (tn-ref-tn 2nd-tn-ref)))        (inst mov 2nd-tn nil-value)        (when (> nvals 2)          (loop            for tn-ref = (tn-ref-across 2nd-tn-ref)            then (tn-ref-across tn-ref)            for count from 2 below register-arg-count            do (inst mov (tn-ref-tn tn-ref) 2nd-tn))))      (inst mov rbx-tn rsp-tn)      (emit-label regs-defaulted)      (inst mov rsp-tn rbx-tn)))   ((<= nvals 7)    ;; The number of bytes depends on the relative jump instructions.    ;; Best case is 31+(n-3)*14, worst case is 35+(n-3)*18. For    ;; NVALS=6 that is 73/89 bytes, and for NVALS=7 that is 87/107    ;; bytes which is likely better than using the blt below.    (let ((regs-defaulted (gen-label))          (defaulting-done (gen-label))          (default-stack-slots (gen-label)))      (note-this-location vop :unknown-return)      ;; Branch off to the MV case.      (inst jmp :c regs-defaulted)      ;; Do the single value case.      ;; Default the register args      (inst mov rax-tn nil-value)      (do ((i 1 (1+ i))           (val (tn-ref-across values) (tn-ref-across val)))          ((= i (min nvals register-arg-count)))        (inst mov (tn-ref-tn val) rax-tn))      ;; Fake other registers so it looks like we returned with all the      ;; registers filled in.      (move rbx-tn rsp-tn)      (inst push rdx-tn)      (inst jmp default-stack-slots)      (emit-label regs-defaulted)      (inst mov rax-tn nil-value)      (storew rdx-tn rbx-tn -1)      (collect ((defaults))        (do ((i register-arg-count (1+ i))             (val (do ((i 0 (1+ i))                       (val values (tn-ref-across val)))                      ((= i register-arg-count) val))                  (tn-ref-across val)))            ((null val))          (let ((default-lab (gen-label))                (tn (tn-ref-tn val)))            (defaults (cons default-lab tn))            (inst cmp rcx-tn (fixnumize i))            (inst jmp :be default-lab)            (loadw rdx-tn rbx-tn (- (1+ i)))            (inst mov tn rdx-tn)))        (emit-label defaulting-done)        (loadw rdx-tn rbx-tn -1)        (move rsp-tn rbx-tn)        (let ((defaults (defaults)))          (when defaults            (assemble (*elsewhere*)              (trace-table-entry trace-table-fun-prologue)              (emit-label default-stack-slots)              (dolist (default defaults)                (emit-label (car default))                (inst mov (cdr default) rax-tn))              (inst jmp defaulting-done)              (trace-table-entry trace-table-normal)))))))   (t    (let ((regs-defaulted (gen-label))          (restore-edi (gen-label))          (no-stack-args (gen-label))          (default-stack-vals (gen-label))          (count-okay (gen-label)))      (note-this-location vop :unknown-return)      ;; Branch off to the MV case.      (inst jmp :c regs-defaulted)      ;; Default the register args, and set up the stack as if we      ;; entered the MV return point.      (inst mov rbx-tn rsp-tn)      (inst push rdx-tn)      (inst mov rdi-tn nil-value)      (inst push rdi-tn)      (inst mov rsi-tn rdi-tn)      ;; Compute a pointer to where to put the [defaulted] stack values.      (emit-label no-stack-args)      (inst lea rdi-tn            (make-ea :qword :base rbp-tn                     :disp (* (- (1+ register-arg-count)) n-word-bytes)))      ;; Load RAX with NIL so we can quickly store it, and set up      ;; stuff for the loop.      (inst mov rax-tn nil-value)      (inst std)      (inst mov rcx-tn (- nvals register-arg-count))      ;; Jump into the default loop.      (inst jmp default-stack-vals)      ;; The regs are defaulted. We need to copy any stack arguments,      ;; and then default the remaining stack arguments.      (emit-label regs-defaulted)      ;; Save EDI.      (storew rdi-tn rbx-tn (- (1+ 1)))      ;; Compute the number of stack arguments, and if it's zero or      ;; less, don't copy any stack arguments.      (inst sub rcx-tn (fixnumize register-arg-count))      (inst jmp :le no-stack-args)      ;; Throw away any unwanted args.      (inst cmp rcx-tn (fixnumize (- nvals register-arg-count)))      (inst jmp :be count-okay)      (inst mov rcx-tn (fixnumize (- nvals register-arg-count)))      (emit-label count-okay)      ;; Save the number of stack values.      (inst mov rax-tn rcx-tn)      ;; Compute a pointer to where the stack args go.      (inst lea rdi-tn            (make-ea :qword :base rbp-tn                     :disp (* (- (1+ register-arg-count)) n-word-bytes)))      ;; Save ESI, and compute a pointer to where the args come from.      (storew rsi-tn rbx-tn (- (1+ 2)))      (inst lea rsi-tn            (make-ea :qword :base rbx-tn                     :disp (* (- (1+ register-arg-count)) n-word-bytes)))      ;; Do the copy.      (inst shr rcx-tn word-shift)              ; make word count      (inst std)      (inst rep)      (inst movs :qword)      ;; Restore RSI.      (loadw rsi-tn rbx-tn (- (1+ 2)))      ;; Now we have to default the remaining args. Find out how many.      (inst sub rax-tn (fixnumize (- nvals register-arg-count)))      (inst neg rax-tn)      ;; If none, then just blow out of here.      (inst jmp :le restore-edi)      (inst mov rcx-tn rax-tn)      (inst shr rcx-tn word-shift)      ; word count      ;; Load RAX with NIL for fast storing.      (inst mov rax-tn nil-value)      ;; Do the store.      (emit-label default-stack-vals)      (inst rep)      (inst stos rax-tn)      ;; Restore EDI, and reset the stack.      (emit-label restore-edi)      (loadw rdi-tn rbx-tn (- (1+ 1)))

⌨️ 快捷键说明

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