1 ;;;; the machine specific support routines needed by the file assembler
3 ;;;; This software is part of the SBCL system. See the README file for
6 ;;;; This software is derived from the CMU CL system, which was
7 ;;;; written at Carnegie Mellon University and released into the
8 ;;;; public domain. The software is in the public domain and is
9 ;;;; provided with absolutely no warranty. See the COPYING and CREDITS
10 ;;;; files for more information.
16 ;;; For RETURN-MULTIPLE, we have to move the results from the end of
17 ;;; the frame for the function that is returning to the end of the
18 ;;; frame for the function being returned to.
20 #+sb-assembling ;; We don't want a vop for this one.
21 (define-assembly-routine
22 (return-multiple (:return-style :none))
23 (;; These are really arguments.
24 (:temp ecx unsigned-reg rcx-offset)
25 (:temp esi unsigned-reg rsi-offset)
27 ;; These we need as temporaries.
28 (:temp eax unsigned-reg rax-offset)
29 (:temp ebx unsigned-reg rbx-offset)
30 (:temp edx unsigned-reg rdx-offset)
31 (:temp edi unsigned-reg rdi-offset))
33 ;; Pick off the cases where everything fits in register args.
34 (inst jrcxz ZERO-VALUES)
35 (inst cmp ecx (fixnumize 1))
36 (inst jmp :e ONE-VALUE)
37 (inst cmp ecx (fixnumize 2))
38 (inst jmp :e TWO-VALUES)
39 (inst cmp ecx (fixnumize 3))
40 (inst jmp :e THREE-VALUES)
42 ;; As per the calling convention EBX is expected to point at the SP
43 ;; before the stack frame.
44 (inst lea ebx (make-ea :qword :base rbp-tn
45 :disp (* sp->fp-offset n-word-bytes)))
47 ;; Save the count, the return address and restore the frame pointer,
48 ;; because the loop is going to destroy them.
50 (inst mov eax (make-ea :qword :base rbp-tn
51 :disp (frame-byte-offset return-pc-save-offset)))
52 (inst mov rbp-tn (make-ea :qword :base rbp-tn
53 :disp (frame-byte-offset ocfp-save-offset)))
54 ;; Blit the values down the stack. Note: there might be overlap, so
55 ;; we have to be careful not to clobber values before we've read
56 ;; them. Because the stack builds down, we are copying to a larger
57 ;; address. Therefore, we need to iterate from larger addresses to
58 ;; smaller addresses. pfw-this says copy ecx words from esi to edi
60 (inst shr ecx (1- n-lowtag-bits))
61 (inst std) ; count down
62 (inst sub esi n-word-bytes)
63 (inst lea edi (make-ea :qword :base ebx :disp (- n-word-bytes)))
71 ;; Set the stack top to the last result.
72 (inst lea rsp-tn (make-ea :qword :base edi :disp n-word-bytes))
74 ;; Load the register args.
84 ;; Handle the register arg cases.
86 (inst lea ebx (make-ea :qword :base rbp-tn
87 :disp (* sp->fp-offset n-word-bytes)))
88 (inst mov edx nil-value)
91 (inst mov rsp-tn rbp-tn)
96 ;; Note: we can get this, because the return-multiple vop doesn't
97 ;; check for this case when size > speed.
100 (inst mov rsp-tn rbp-tn)
106 (inst lea ebx (make-ea :qword :base rbp-tn
107 :disp (* sp->fp-offset n-word-bytes)))
110 (inst mov esi nil-value)
111 (inst mov rsp-tn rbp-tn)
117 (inst lea ebx (make-ea :qword :base rbp-tn
118 :disp (* sp->fp-offset n-word-bytes)))
122 (inst mov rsp-tn rbp-tn)
127 ;;;; TAIL-CALL-VARIABLE
129 ;;; For tail-call-variable, we have to copy the arguments from the end
130 ;;; of our stack frame (were args are produced) to the start of our
131 ;;; stack frame (were args are expected).
133 ;;; We take the function to call in EAX and a pointer to the arguments in
134 ;;; ESI. EBP says the same over the jump, and the old frame pointer is
135 ;;; still saved in the first stack slot. The return-pc is saved in
136 ;;; the second stack slot, so we have to push it to make it look like
137 ;;; we actually called. We also have to compute ECX from the difference
138 ;;; between ESI and the stack top.
139 #+sb-assembling ;; No vop for this one either.
140 (define-assembly-routine
142 (:return-style :none))
144 ((:temp eax unsigned-reg rax-offset)
145 (:temp ebx unsigned-reg rbx-offset)
146 (:temp ecx unsigned-reg rcx-offset)
147 (:temp edx unsigned-reg rdx-offset)
148 (:temp edi unsigned-reg rdi-offset)
149 (:temp esi unsigned-reg rsi-offset))
151 ;; Calculate NARGS (as a fixnum)
153 (inst sub ecx rsp-tn)
155 ;; Check for all the args fitting the registers.
156 (inst cmp ecx (fixnumize 3))
157 (inst jmp :le REGISTER-ARGS)
159 ;; Save the OLD-FP and RETURN-PC because the blit is going to trash
160 ;; those stack locations. Save the ECX, because the loop is going to
162 (pushw rbp-tn (frame-word-offset ocfp-save-offset))
163 (loadw ebx rbp-tn (frame-word-offset return-pc-save-offset))
166 ;; Do the blit. Because we are coping from smaller addresses to
167 ;; larger addresses, we have to start at the largest pair and work
169 (inst shr ecx (1- n-lowtag-bits))
170 (inst std) ; count down
171 (inst lea edi (make-ea :qword :base rbp-tn :disp (frame-byte-offset 0)))
172 (inst sub esi (fixnumize 1))
177 ;; Load the register arguments carefully.
178 (loadw edx rbp-tn (frame-word-offset ocfp-save-offset))
180 ;; Restore OLD-FP and ECX.
183 (popw rbp-tn (frame-word-offset ocfp-save-offset))
185 ;; Blow off the stack above the arguments.
186 (inst lea rsp-tn (make-ea :qword :base edi :disp n-word-bytes))
188 ;; remaining register args
190 (loadw edx rbp-tn (frame-word-offset 0))
191 (loadw esi rbp-tn (frame-word-offset 2))
193 ;; Push the (saved) return-pc so it looks like we just called.
196 ;; And jump into the function.
198 (make-ea :byte :base eax
199 :disp (- (* closure-fun-slot n-word-bytes)
200 fun-pointer-lowtag)))
202 ;; All the arguments fit in registers, so load them.
208 ;; Clear most of the stack.
210 (make-ea :qword :base rbp-tn :disp (* (- sp->fp-offset 3) n-word-bytes)))
212 ;; Push the return-pc so it looks like we just called.
213 (pushw rbp-tn (frame-word-offset return-pc-save-offset))
216 (inst jmp (make-ea :byte :base eax
217 :disp (- (* closure-fun-slot n-word-bytes)
218 fun-pointer-lowtag))))
220 (define-assembly-routine (throw
221 (:return-style :none))
222 ((:arg target (descriptor-reg any-reg) rdx-offset)
223 (:arg start any-reg rbx-offset)
224 (:arg count any-reg rcx-offset)
225 (:temp catch any-reg rax-offset))
227 (declare (ignore start count))
229 (load-tl-symbol-value catch *current-catch-block*)
233 (let ((error (generate-error-code nil 'unseen-throw-tag-error target)))
234 (inst or catch catch) ; check for NULL pointer
237 (inst cmp target (make-ea-for-object-slot catch catch-block-tag-slot 0))
240 (loadw catch catch catch-block-previous-catch-slot)
245 ;; Here EAX points to catch block containing symbol pointed to by EDX.
246 (inst jmp (make-fixup 'unwind :assembly-routine)))
248 ;;;; non-local exit noise
250 (define-assembly-routine (unwind
251 (:return-style :none)
252 (:translate %continue-unwind)
253 (:policy :fast-safe))
254 ((:arg block (any-reg descriptor-reg) rax-offset)
255 (:arg start (any-reg descriptor-reg) rbx-offset)
256 (:arg count (any-reg descriptor-reg) rcx-offset)
257 (:temp uwp unsigned-reg rsi-offset))
258 (declare (ignore start count))
260 (let ((error (generate-error-code nil 'invalid-unwind-error)))
261 (inst or block block) ; check for NULL pointer
264 (load-tl-symbol-value uwp *current-unwind-protect-block*)
266 ;; Does *CURRENT-UNWIND-PROTECT-BLOCK* match the value stored in
267 ;; argument's CURRENT-UWP-SLOT?
269 (make-ea-for-object-slot block unwind-block-current-uwp-slot 0))
270 ;; If a match, return to context in arg block.
271 (inst jmp :e DO-EXIT)
273 ;; Not a match - return to *CURRENT-UNWIND-PROTECT-BLOCK* context.
274 ;; Important! Must save (and return) the arg 'block' for later use!!
277 ;; Set next unwind protect context.
278 (loadw uwp uwp unwind-block-current-uwp-slot)
279 ;; we're about to reload ebp anyway, so let's borrow it here as a
280 ;; temporary. Hope this works
281 (store-tl-symbol-value uwp *current-unwind-protect-block* rbp-tn)
285 (loadw rbp-tn block unwind-block-current-cont-slot)
287 ;; Uwp-entry expects some things in known locations so that they can
288 ;; be saved on the stack: the block in edx-tn, start in ebx-tn, and
291 (inst jmp (make-ea :byte :base block
292 :disp (* unwind-block-entry-pc-slot n-word-bytes))))