Initial revision
[sbcl.git] / src / assembly / x86 / assem-rtns.lisp
1 ;;;; the machine specific support routines needed by the file assembler
2
3 ;;;; This software is part of the SBCL system. See the README file for
4 ;;;; more information.
5 ;;;;
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.
11
12 (in-package "SB!VM")
13
14 (file-comment
15  "$Header$")
16 \f
17 ;;;; RETURN-MULTIPLE
18
19 ;;; For RETURN-MULTIPLE, we have to move the results from the end of
20 ;;; the frame for the function that is returning to the end of the
21 ;;; frame for the function being returned to.
22
23 #+sb-assembling ;; We don't want a vop for this one.
24 (define-assembly-routine
25     (return-multiple (:return-style :none))
26     (;; These four are really arguments.
27      (:temp eax unsigned-reg eax-offset)
28      (:temp ebx unsigned-reg ebx-offset)
29      (:temp ecx unsigned-reg ecx-offset)
30      (:temp esi unsigned-reg esi-offset)
31
32      ;; These we need as temporaries.
33      (:temp edx unsigned-reg edx-offset)
34      (:temp edi unsigned-reg edi-offset))
35
36   ;; Pick off the cases where everything fits in register args.
37   (inst jecxz zero-values)
38   (inst cmp ecx (fixnumize 1))
39   (inst jmp :e one-value)
40   (inst cmp ecx (fixnumize 2))
41   (inst jmp :e two-values)
42   (inst cmp ecx (fixnumize 3))
43   (inst jmp :e three-values)
44
45   ;; Save the count, because the loop is going to destroy it.
46   (inst mov edx ecx)
47
48   ;; Blit the values down the stack. Note: there might be overlap, so we have
49   ;; to be careful not to clobber values before we've read them. Because the
50   ;; stack builds down, we are coping to a larger address. Therefore, we need
51   ;; to iterate from larger addresses to smaller addresses.
52   ;; pfw-this says copy ecx words from esi to edi counting down.
53   (inst shr ecx 2)                      ; fixnum to raw word count
54   (inst std)                            ; count down
55   (inst sub esi 4)                      ; ?
56   (inst lea edi (make-ea :dword :base ebx :disp (- word-bytes)))
57   (inst rep)
58   (inst movs :dword)
59
60   ;; Restore the count.
61   (inst mov ecx edx)
62
63   ;; Set the stack top to the last result.
64   (inst lea esp-tn (make-ea :dword :base edi :disp word-bytes))
65
66   ;; Load the register args.
67   (loadw edx ebx -1)
68   (loadw edi ebx -2)
69   (loadw esi ebx -3)
70
71   ;; And back we go.
72   (inst jmp eax)
73
74   ;; Handle the register arg cases.
75   ZERO-VALUES
76   (move esp-tn ebx)
77   (inst mov edx *nil-value*)
78   (inst mov edi edx)
79   (inst mov esi edx)
80   (inst jmp eax)
81
82   ONE-VALUE ; Note: we can get this, because the return-multiple vop
83             ; doesn't check for this case when size > speed.
84   (loadw edx esi -1)
85   (inst mov esp-tn ebx)
86   (inst add eax 2)
87   (inst jmp eax)
88
89   TWO-VALUES
90   (loadw edx esi -1)
91   (loadw edi esi -2)
92   (inst mov esi *nil-value*)
93   (inst lea esp-tn (make-ea :dword :base ebx :disp (* -2 word-bytes)))
94   (inst jmp eax)
95
96   THREE-VALUES
97   (loadw edx esi -1)
98   (loadw edi esi -2)
99   (loadw esi esi -3)
100   (inst lea esp-tn (make-ea :dword :base ebx :disp (* -3 word-bytes)))
101   (inst jmp eax))
102 \f
103 ;;;; TAIL-CALL-VARIABLE
104
105 ;;; For tail-call-variable, we have to copy the arguments from the end of our
106 ;;; stack frame (were args are produced) to the start of our stack frame
107 ;;; (were args are expected).
108 ;;;
109 ;;; We take the function to call in EAX and a pointer to the arguments in
110 ;;; ESI. EBP says the same over the jump, and the old frame pointer is
111 ;;; still saved in the first stack slot. The return-pc is saved in
112 ;;; the second stack slot, so we have to push it to make it look like
113 ;;; we actually called. We also have to compute ECX from the difference
114 ;;; between ESI and the stack top.
115 #+sb-assembling ;; No vop for this one either.
116 (define-assembly-routine
117     (tail-call-variable
118      (:return-style :none))
119
120     ((:temp eax unsigned-reg eax-offset)
121      (:temp ebx unsigned-reg ebx-offset)
122      (:temp ecx unsigned-reg ecx-offset)
123      (:temp edx unsigned-reg edx-offset)
124      (:temp edi unsigned-reg edi-offset)
125      (:temp esi unsigned-reg esi-offset))
126
127   ;; Calculate NARGS (as a fixnum)
128   (move ecx esi)
129   (inst sub ecx esp-tn)
130
131   ;; Check for all the args fitting the the registers.
132   (inst cmp ecx (fixnumize 3))
133   (inst jmp :le REGISTER-ARGS)
134
135   ;; Save the OLD-FP and RETURN-PC because the blit it going to trash
136   ;; those stack locations. Save the ECX, because the loop is going
137   ;; to trash it.
138   (pushw ebp-tn -1)
139   (loadw ebx ebp-tn -2)
140   (inst push ecx)
141
142   ;; Do the blit. Because we are coping from smaller addresses to larger
143   ;; addresses, we have to start at the largest pair and work our way down.
144   (inst shr ecx 2)                      ; fixnum to raw words
145   (inst std)                            ; count down
146   (inst lea edi (make-ea :dword :base ebp-tn :disp (- word-bytes)))
147   (inst sub esi (fixnumize 1))
148   (inst rep)
149   (inst movs :dword)
150
151   ;; Load the register arguments carefully.
152   (loadw edx ebp-tn -1)
153
154   ;; Restore OLD-FP and ECX.
155   (inst pop ecx)
156   (popw ebp-tn -1)                      ; overwrites a0
157
158   ;; Blow off the stack above the arguments.
159   (inst lea esp-tn (make-ea :dword :base edi :disp word-bytes))
160
161   ;; remaining register args
162   (loadw edi ebp-tn -2)
163   (loadw esi ebp-tn -3)
164
165   ;; Push the (saved) return-pc so it looks like we just called.
166   (inst push ebx)
167
168   ;; And jump into the function.
169     (inst jmp
170           (make-ea :byte :base eax
171                    :disp (- (* closure-function-slot word-bytes)
172                             function-pointer-type)))
173
174   ;; All the arguments fit in registers, so load them.
175   REGISTER-ARGS
176   (loadw edx esi -1)
177   (loadw edi esi -2)
178   (loadw esi esi -3)
179
180   ;; Clear most of the stack.
181   (inst lea esp-tn
182         (make-ea :dword :base ebp-tn :disp (* -3 word-bytes)))
183
184   ;; Push the return-pc so it looks like we just called.
185   (pushw ebp-tn -2)
186
187   ;; And away we go.
188   (inst jmp (make-ea :byte :base eax
189                      :disp (- (* closure-function-slot word-bytes)
190                               function-pointer-type))))
191 \f
192 (define-assembly-routine (throw
193                           (:return-style :none))
194                          ((:arg target (descriptor-reg any-reg) edx-offset)
195                           (:arg start any-reg ebx-offset)
196                           (:arg count any-reg ecx-offset)
197                           (:temp catch any-reg eax-offset))
198
199   (declare (ignore start count))
200
201   (load-symbol-value catch sb!impl::*current-catch-block*)
202
203   LOOP
204
205   (let ((error (generate-error-code nil unseen-throw-tag-error target)))
206     (inst or catch catch)               ; check for NULL pointer
207     (inst jmp :z error))
208
209   (inst cmp target (make-ea-for-object-slot catch catch-block-tag-slot 0))
210   (inst jmp :e exit)
211
212   (loadw catch catch catch-block-previous-catch-slot)
213   (inst jmp loop)
214
215   EXIT
216
217   ;; Hear EAX points to catch block containing symbol pointed to by EDX.
218   (inst jmp (make-fixup 'unwind :assembly-routine)))
219
220 ;;;; non-local exit noise
221
222 (define-assembly-routine (unwind
223                           (:return-style :none)
224                           (:translate %continue-unwind)
225                           (:policy :fast-safe))
226                          ((:arg block (any-reg descriptor-reg) eax-offset)
227                           (:arg start (any-reg descriptor-reg) ebx-offset)
228                           (:arg count (any-reg descriptor-reg) ecx-offset)
229                           (:temp uwp unsigned-reg esi-offset))
230   (declare (ignore start count))
231
232   (let ((error (generate-error-code nil invalid-unwind-error)))
233     (inst or block block)               ; check for NULL pointer
234     (inst jmp :z error))
235
236   (load-symbol-value uwp sb!impl::*current-unwind-protect-block*)
237
238   ;; Does *cuwpb* match value stored in argument cuwp slot?
239   (inst cmp uwp
240         (make-ea-for-object-slot block unwind-block-current-uwp-slot 0))
241   ;; If a match, return to context in arg block.
242   (inst jmp :e do-exit)
243
244   ;; Not a match - return to *current-unwind-protect-block* context.
245   ;; Important! Must save (and return) the arg 'block' for later use!!
246   (move edx-tn block)
247   (move block uwp)
248   ;; Set next unwind protect context.
249   (loadw uwp uwp unwind-block-current-uwp-slot)
250   (store-symbol-value uwp sb!impl::*current-unwind-protect-block*)
251
252   DO-EXIT
253
254   (loadw ebp-tn block unwind-block-current-cont-slot)
255
256   ;; Uwp-entry expects some things in known locations so that they can
257   ;; be saved on the stack: the block in edx-tn; start in ebx-tn; and
258   ;; count in ecx-tn
259
260   (inst jmp (make-ea :byte :base block
261                      :disp (* unwind-block-entry-pc-slot word-bytes))))