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)
32 (:temp temp unsigned-reg r8-offset)
33 (:temp loop-index unsigned-reg r9-offset))
35 ;; Pick off the cases where everything fits in register args.
36 (inst jrcxz ZERO-VALUES)
37 (inst cmp ecx (fixnumize 1))
38 (inst jmp :e ONE-VALUE)
39 (inst cmp ecx (fixnumize 2))
40 (inst jmp :e TWO-VALUES)
41 (inst cmp ecx (fixnumize 3))
42 (inst jmp :e THREE-VALUES)
44 ;; As per the calling convention EBX is expected to point at the SP
45 ;; before the stack frame.
46 (inst lea ebx (make-ea :qword :base rbp-tn
47 :disp (* sp->fp-offset n-word-bytes)))
49 ;; Save the count, the return address and restore the frame pointer,
50 ;; because the loop is going to destroy them.
52 (inst mov eax (make-ea :qword :base rbp-tn
53 :disp (frame-byte-offset return-pc-save-offset)))
54 (inst mov rbp-tn (make-ea :qword :base rbp-tn
55 :disp (frame-byte-offset ocfp-save-offset)))
56 ;; Blit the values down the stack. Note: there might be overlap, so
57 ;; we have to be careful not to clobber values before we've read
58 ;; them. Because the stack builds down, we are copying to a larger
59 ;; address. Therefore, we need to iterate from larger addresses to
63 (inst sub loop-index n-word-bytes)
65 (make-ea :qword :base esi
68 (make-ea :qword :base ebx
72 (inst sub edx (fixnumize 1))
75 ;; Set the stack top to the last result.
76 (inst lea rsp-tn (make-ea :qword :base ebx :index loop-index))
78 ;; Load the register args.
88 ;; Handle the register arg cases.
90 (inst lea ebx (make-ea :qword :base rbp-tn
91 :disp (* sp->fp-offset n-word-bytes)))
92 (inst mov edx nil-value)
95 (inst mov rsp-tn rbp-tn)
100 ;; Note: we can get this, because the return-multiple vop doesn't
101 ;; check for this case when size > speed.
104 (inst mov rsp-tn rbp-tn)
110 (inst lea ebx (make-ea :qword :base rbp-tn
111 :disp (* sp->fp-offset n-word-bytes)))
114 (inst mov esi nil-value)
115 (inst mov rsp-tn rbp-tn)
121 (inst lea ebx (make-ea :qword :base rbp-tn
122 :disp (* sp->fp-offset n-word-bytes)))
126 (inst mov rsp-tn rbp-tn)
131 ;;;; TAIL-CALL-VARIABLE
133 ;;; For tail-call-variable, we have to copy the arguments from the end
134 ;;; of our stack frame (were args are produced) to the start of our
135 ;;; stack frame (were args are expected).
137 ;;; We take the function to call in EAX and a pointer to the arguments in
138 ;;; ESI. EBP says the same over the jump, and the old frame pointer is
139 ;;; still saved in the first stack slot. The return-pc is saved in
140 ;;; the second stack slot, so we have to push it to make it look like
141 ;;; we actually called. We also have to compute ECX from the difference
142 ;;; between ESI and the stack top.
143 #+sb-assembling ;; No vop for this one either.
144 (define-assembly-routine
146 (:return-style :none))
148 ((:temp eax unsigned-reg rax-offset)
149 (:temp ebx unsigned-reg rbx-offset)
150 (:temp ecx unsigned-reg rcx-offset)
151 (:temp edx unsigned-reg rdx-offset)
152 (:temp edi unsigned-reg rdi-offset)
153 (:temp esi unsigned-reg rsi-offset))
155 ;; Calculate NARGS (as a fixnum)
157 (inst sub ecx rsp-tn)
158 #!-#.(cl:if (cl:= sb!vm:word-shift sb!vm:n-fixnum-tag-bits) '(and) '(or))
159 (inst shr ecx (- word-shift n-fixnum-tag-bits))
161 ;; Check for all the args fitting the registers.
162 (inst cmp ecx (fixnumize register-arg-count))
163 (inst jmp :le REGISTER-ARGS)
165 ;; Save the OLD-FP and RETURN-PC because the blit is going to trash
166 ;; those stack locations. Save the ECX, because the loop is going to
168 (pushw rbp-tn (frame-word-offset ocfp-save-offset))
169 (loadw ebx rbp-tn (frame-word-offset return-pc-save-offset))
172 ;; Do the blit. Because we are coping from smaller addresses to
173 ;; larger addresses, we have to start at the largest pair and work
175 (inst shr ecx n-fixnum-tag-bits)
176 (inst std) ; count down
177 (inst lea edi (make-ea :qword :base rbp-tn :disp (frame-byte-offset 0)))
178 (inst sub esi n-word-bytes)
183 ;; Load the register arguments carefully.
184 (loadw edx rbp-tn (frame-word-offset ocfp-save-offset))
186 ;; Restore OLD-FP and ECX.
189 (popw rbp-tn (frame-word-offset ocfp-save-offset))
191 ;; Blow off the stack above the arguments.
192 (inst lea rsp-tn (make-ea :qword :base edi :disp n-word-bytes))
194 ;; remaining register args
196 (loadw edx rbp-tn (frame-word-offset 0))
197 (loadw esi rbp-tn (frame-word-offset 2))
199 ;; Push the (saved) return-pc so it looks like we just called.
202 ;; And jump into the function.
204 (make-ea :byte :base eax
205 :disp (- (* closure-fun-slot n-word-bytes)
206 fun-pointer-lowtag)))
208 ;; All the arguments fit in registers, so load them.
214 ;; Clear most of the stack.
216 (make-ea :qword :base rbp-tn :disp (* (- sp->fp-offset 3) n-word-bytes)))
218 ;; Push the return-pc so it looks like we just called.
219 (pushw rbp-tn (frame-word-offset return-pc-save-offset))
222 (inst jmp (make-ea :byte :base eax
223 :disp (- (* closure-fun-slot n-word-bytes)
224 fun-pointer-lowtag))))
226 (define-assembly-routine (throw
227 (:return-style :raw))
228 ((:arg target (descriptor-reg any-reg) rdx-offset)
229 (:arg start any-reg rbx-offset)
230 (:arg count any-reg rcx-offset)
231 (:temp catch any-reg rax-offset))
233 (declare (ignore start count))
235 (load-tl-symbol-value catch *current-catch-block*)
239 (let ((error (gen-label)))
240 (assemble (*elsewhere*)
243 ;; Fake up a stack frame so that backtraces come out right.
245 (inst mov rbp-tn rsp-tn)
247 (emit-error-break nil error-trap
248 (error-number-or-lose 'unseen-throw-tag-error)
250 (inst test catch catch) ; check for NULL pointer
253 (inst cmp target (make-ea-for-object-slot catch catch-block-tag-slot 0))
256 (loadw catch catch catch-block-previous-catch-slot)
261 ;; Here EAX points to catch block containing symbol pointed to by EDX.
262 (inst jmp (make-fixup 'unwind :assembly-routine)))
264 ;;;; non-local exit noise
266 (define-assembly-routine (unwind
267 (:return-style :none)
268 (:translate %continue-unwind)
269 (:policy :fast-safe))
270 ((:arg block (any-reg descriptor-reg) rax-offset)
271 (:arg start (any-reg descriptor-reg) rbx-offset)
272 (:arg count (any-reg descriptor-reg) rcx-offset)
273 (:temp uwp unsigned-reg rsi-offset))
274 (declare (ignore start count))
276 (let ((error (generate-error-code nil 'invalid-unwind-error)))
277 (inst test block block) ; check for NULL pointer
280 (load-tl-symbol-value uwp *current-unwind-protect-block*)
282 ;; Does *CURRENT-UNWIND-PROTECT-BLOCK* match the value stored in
283 ;; argument's CURRENT-UWP-SLOT?
285 (make-ea-for-object-slot block unwind-block-current-uwp-slot 0))
286 ;; If a match, return to context in arg block.
287 (inst jmp :e DO-EXIT)
289 ;; Not a match - return to *CURRENT-UNWIND-PROTECT-BLOCK* context.
290 ;; Important! Must save (and return) the arg 'block' for later use!!
293 ;; Set next unwind protect context.
294 (loadw uwp uwp unwind-block-current-uwp-slot)
295 ;; we're about to reload ebp anyway, so let's borrow it here as a
296 ;; temporary. Hope this works
297 (store-tl-symbol-value uwp *current-unwind-protect-block* rbp-tn)
301 (loadw rbp-tn block unwind-block-current-cont-slot)
303 ;; Uwp-entry expects some things in known locations so that they can
304 ;; be saved on the stack: the block in edx-tn, start in ebx-tn, and
307 (inst jmp (make-ea :byte :base block
308 :disp (* unwind-block-entry-pc-slot n-word-bytes))))