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 n-fixnum-tag-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)
154 #!-#.(cl:if (cl:= sb!vm:word-shift sb!vm:n-fixnum-tag-bits) '(and) '(or))
155 (inst shr ecx (- word-shift n-fixnum-tag-bits))
157 ;; Check for all the args fitting the registers.
158 (inst cmp ecx (fixnumize register-arg-count))
159 (inst jmp :le REGISTER-ARGS)
161 ;; Save the OLD-FP and RETURN-PC because the blit is going to trash
162 ;; those stack locations. Save the ECX, because the loop is going to
164 (pushw rbp-tn (frame-word-offset ocfp-save-offset))
165 (loadw ebx rbp-tn (frame-word-offset return-pc-save-offset))
168 ;; Do the blit. Because we are coping from smaller addresses to
169 ;; larger addresses, we have to start at the largest pair and work
171 (inst shr ecx n-fixnum-tag-bits)
172 (inst std) ; count down
173 (inst lea edi (make-ea :qword :base rbp-tn :disp (frame-byte-offset 0)))
174 (inst sub esi n-word-bytes)
179 ;; Load the register arguments carefully.
180 (loadw edx rbp-tn (frame-word-offset ocfp-save-offset))
182 ;; Restore OLD-FP and ECX.
185 (popw rbp-tn (frame-word-offset ocfp-save-offset))
187 ;; Blow off the stack above the arguments.
188 (inst lea rsp-tn (make-ea :qword :base edi :disp n-word-bytes))
190 ;; remaining register args
192 (loadw edx rbp-tn (frame-word-offset 0))
193 (loadw esi rbp-tn (frame-word-offset 2))
195 ;; Push the (saved) return-pc so it looks like we just called.
198 ;; And jump into the function.
200 (make-ea :byte :base eax
201 :disp (- (* closure-fun-slot n-word-bytes)
202 fun-pointer-lowtag)))
204 ;; All the arguments fit in registers, so load them.
210 ;; Clear most of the stack.
212 (make-ea :qword :base rbp-tn :disp (* (- sp->fp-offset 3) n-word-bytes)))
214 ;; Push the return-pc so it looks like we just called.
215 (pushw rbp-tn (frame-word-offset return-pc-save-offset))
218 (inst jmp (make-ea :byte :base eax
219 :disp (- (* closure-fun-slot n-word-bytes)
220 fun-pointer-lowtag))))
222 (define-assembly-routine (throw
223 (:return-style :raw))
224 ((:arg target (descriptor-reg any-reg) rdx-offset)
225 (:arg start any-reg rbx-offset)
226 (:arg count any-reg rcx-offset)
227 (:temp catch any-reg rax-offset))
229 (declare (ignore start count))
231 (load-tl-symbol-value catch *current-catch-block*)
235 (let ((error (gen-label)))
236 (assemble (*elsewhere*)
239 ;; Fake up a stack frame so that backtraces come out right.
241 (inst mov rbp-tn rsp-tn)
243 (emit-error-break nil error-trap
244 (error-number-or-lose 'unseen-throw-tag-error)
246 (inst test catch catch) ; check for NULL pointer
249 (inst cmp target (make-ea-for-object-slot catch catch-block-tag-slot 0))
252 (loadw catch catch catch-block-previous-catch-slot)
257 ;; Here EAX points to catch block containing symbol pointed to by EDX.
258 (inst jmp (make-fixup 'unwind :assembly-routine)))
260 ;;;; non-local exit noise
262 (define-assembly-routine (unwind
263 (:return-style :none)
264 (:translate %continue-unwind)
265 (:policy :fast-safe))
266 ((:arg block (any-reg descriptor-reg) rax-offset)
267 (:arg start (any-reg descriptor-reg) rbx-offset)
268 (:arg count (any-reg descriptor-reg) rcx-offset)
269 (:temp uwp unsigned-reg rsi-offset))
270 (declare (ignore start count))
272 (let ((error (generate-error-code nil 'invalid-unwind-error)))
273 (inst test block block) ; check for NULL pointer
276 (load-tl-symbol-value uwp *current-unwind-protect-block*)
278 ;; Does *CURRENT-UNWIND-PROTECT-BLOCK* match the value stored in
279 ;; argument's CURRENT-UWP-SLOT?
281 (make-ea-for-object-slot block unwind-block-current-uwp-slot 0))
282 ;; If a match, return to context in arg block.
283 (inst jmp :e DO-EXIT)
285 ;; Not a match - return to *CURRENT-UNWIND-PROTECT-BLOCK* context.
286 ;; Important! Must save (and return) the arg 'block' for later use!!
289 ;; Set next unwind protect context.
290 (loadw uwp uwp unwind-block-current-uwp-slot)
291 ;; we're about to reload ebp anyway, so let's borrow it here as a
292 ;; temporary. Hope this works
293 (store-tl-symbol-value uwp *current-unwind-protect-block* rbp-tn)
297 (loadw rbp-tn block unwind-block-current-cont-slot)
299 ;; Uwp-entry expects some things in known locations so that they can
300 ;; be saved on the stack: the block in edx-tn, start in ebx-tn, and
303 (inst jmp (make-ea :byte :base block
304 :disp (* unwind-block-entry-pc-slot n-word-bytes))))