3e20c42f8b630eea0b567aa42643e136263b84a6
[sbcl.git] / src / compiler / x86-64 / c-call.lisp
1 ;;;; the VOPs and other necessary machine specific support
2 ;;;; routines for call-out to C
3
4 ;;;; This software is part of the SBCL system. See the README file for
5 ;;;; more information.
6 ;;;;
7 ;;;; This software is derived from the CMU CL system, which was
8 ;;;; written at Carnegie Mellon University and released into the
9 ;;;; public domain. The software is in the public domain and is
10 ;;;; provided with absolutely no warranty. See the COPYING and CREDITS
11 ;;;; files for more information.
12
13 (in-package "SB!VM")
14
15 ;; The MOVE-ARG vop is going to store args on the stack for
16 ;; call-out. These tn's will be used for that. move-arg is normally
17 ;; used for things going down the stack but C wants to have args
18 ;; indexed in the positive direction.
19
20 (defun my-make-wired-tn (prim-type-name sc-name offset)
21   (make-wired-tn (primitive-type-or-lose prim-type-name)
22                  (sc-number-or-lose sc-name)
23                  offset))
24
25 (defstruct (arg-state (:copier nil))
26   (register-args 0)
27   (xmm-args 0)
28   (stack-frame-size 0))
29
30 (defun int-arg (state prim-type reg-sc stack-sc)
31   (let ((reg-args (arg-state-register-args state)))
32     (cond ((< reg-args 6)
33            (setf (arg-state-register-args state) (1+ reg-args))
34            (my-make-wired-tn prim-type reg-sc
35                              (nth reg-args *c-call-register-arg-offsets*)))
36           (t
37            (let ((frame-size (arg-state-stack-frame-size state)))
38              (setf (arg-state-stack-frame-size state) (1+ frame-size))
39              (my-make-wired-tn prim-type stack-sc frame-size))))))
40
41 (define-alien-type-method (integer :arg-tn) (type state)
42   (if (alien-integer-type-signed type)
43       (int-arg state 'signed-byte-64 'signed-reg 'signed-stack)
44       (int-arg state 'unsigned-byte-64 'unsigned-reg 'unsigned-stack)))
45
46 (define-alien-type-method (system-area-pointer :arg-tn) (type state)
47   (declare (ignore type))
48   (int-arg state 'system-area-pointer 'sap-reg 'sap-stack))
49
50 (defun float-arg (state prim-type reg-sc stack-sc)
51   (let ((xmm-args (arg-state-xmm-args state)))
52     (cond ((< xmm-args 8)
53            (setf (arg-state-xmm-args state) (1+ xmm-args))
54            (my-make-wired-tn prim-type reg-sc
55                              (nth xmm-args *float-regs*)))
56           (t
57            (let ((frame-size (arg-state-stack-frame-size state)))
58              (setf (arg-state-stack-frame-size state) (1+ frame-size))
59              (my-make-wired-tn prim-type stack-sc frame-size))))))
60
61 (define-alien-type-method (double-float :arg-tn) (type state)
62   (declare (ignore type))
63   (float-arg state 'double-float 'double-reg 'double-stack))
64
65 (define-alien-type-method (single-float :arg-tn) (type state)
66   (declare (ignore type))
67   (float-arg state 'single-float 'single-reg 'single-stack))
68
69 (defstruct (result-state (:copier nil))
70   (num-results 0))
71
72 (defun result-reg-offset (slot)
73   (ecase slot
74     (0 eax-offset)
75     (1 edx-offset)))
76
77 (define-alien-type-method (integer :result-tn) (type state)
78   (let ((num-results (result-state-num-results state)))
79     (setf (result-state-num-results state) (1+ num-results))
80     (multiple-value-bind (ptype reg-sc)
81         (if (alien-integer-type-signed type)
82             (values 'signed-byte-64 'signed-reg)
83             (values 'unsigned-byte-64 'unsigned-reg))
84       (my-make-wired-tn ptype reg-sc (result-reg-offset num-results)))))
85
86 (define-alien-type-method (integer :naturalize-gen) (type alien)
87   (if (and (alien-integer-type-signed type)
88            (<= (alien-type-bits type) 32))
89       `(sign-extend ,alien ,(alien-type-bits type))
90       alien))
91
92 (define-alien-type-method (system-area-pointer :result-tn) (type state)
93   (declare (ignore type))
94   (let ((num-results (result-state-num-results state)))
95     (setf (result-state-num-results state) (1+ num-results))
96     (my-make-wired-tn 'system-area-pointer 'sap-reg
97                       (result-reg-offset num-results))))
98
99 (define-alien-type-method (double-float :result-tn) (type state)
100   (declare (ignore type))
101   (let ((num-results (result-state-num-results state)))
102     (setf (result-state-num-results state) (1+ num-results))
103     (my-make-wired-tn 'double-float 'double-reg num-results)))
104
105 (define-alien-type-method (single-float :result-tn) (type state)
106   (declare (ignore type))
107   (let ((num-results (result-state-num-results state)))
108     (setf (result-state-num-results state) (1+ num-results))
109     (my-make-wired-tn 'single-float 'single-reg num-results)))
110
111 (define-alien-type-method (values :result-tn) (type state)
112   (let ((values (alien-values-type-values type)))
113     (when (> (length values) 2)
114       (error "Too many result values from c-call."))
115     (mapcar (lambda (type)
116               (invoke-alien-type-method :result-tn type state))
117             values)))
118
119 (!def-vm-support-routine make-call-out-tns (type)
120   (let ((arg-state (make-arg-state)))
121     (collect ((arg-tns))
122       (dolist (arg-type (alien-fun-type-arg-types type))
123         (arg-tns (invoke-alien-type-method :arg-tn arg-type arg-state)))
124       (values (my-make-wired-tn 'positive-fixnum 'any-reg esp-offset)
125               (* (arg-state-stack-frame-size arg-state) n-word-bytes)
126               (arg-tns)
127               (invoke-alien-type-method :result-tn
128                                         (alien-fun-type-result-type type)
129                                         (make-result-state))))))
130
131
132 (deftransform %alien-funcall ((function type &rest args) * * :node node)
133   (aver (sb!c::constant-lvar-p type))
134   (let* ((type (sb!c::lvar-value type))
135          (env (sb!c::node-lexenv node))
136          (arg-types (alien-fun-type-arg-types type))
137          (result-type (alien-fun-type-result-type type)))
138     (aver (= (length arg-types) (length args)))
139     (if (or (some #'(lambda (type)
140                       (and (alien-integer-type-p type)
141                            (> (sb!alien::alien-integer-type-bits type) 64)))
142                   arg-types)
143             (and (alien-integer-type-p result-type)
144                  (> (sb!alien::alien-integer-type-bits result-type) 64)))
145         (collect ((new-args) (lambda-vars) (new-arg-types))
146           (dolist (type arg-types)
147             (let ((arg (gensym)))
148               (lambda-vars arg)
149               (cond ((and (alien-integer-type-p type)
150                           (> (sb!alien::alien-integer-type-bits type) 64))
151                      ;; CLH: FIXME! This should really be
152                      ;; #xffffffffffffffff. nyef says: "Passing
153                      ;; 128-bit integers to ALIEN functions on x86-64
154                      ;; believed to be broken."
155                      (new-args `(logand ,arg #xffffffff))
156                      (new-args `(ash ,arg -64))
157                      (new-arg-types (parse-alien-type '(unsigned 64) env))
158                      (if (alien-integer-type-signed type)
159                          (new-arg-types (parse-alien-type '(signed 64) env))
160                          (new-arg-types (parse-alien-type '(unsigned 64) env))))
161                     (t
162                      (new-args arg)
163                      (new-arg-types type)))))
164           (cond ((and (alien-integer-type-p result-type)
165                       (> (sb!alien::alien-integer-type-bits result-type) 64))
166                  (let ((new-result-type
167                         (let ((sb!alien::*values-type-okay* t))
168                           (parse-alien-type
169                            (if (alien-integer-type-signed result-type)
170                                '(values (unsigned 64) (signed 64))
171                                '(values (unsigned 64) (unsigned 64)))
172                            env))))
173                    `(lambda (function type ,@(lambda-vars))
174                       (declare (ignore type))
175                       (multiple-value-bind (low high)
176                           (%alien-funcall function
177                                           ',(make-alien-fun-type
178                                              :arg-types (new-arg-types)
179                                              :result-type new-result-type)
180                                           ,@(new-args))
181                         (logior low (ash high 64))))))
182                 (t
183                  `(lambda (function type ,@(lambda-vars))
184                     (declare (ignore type))
185                     (%alien-funcall function
186                                     ',(make-alien-fun-type
187                                        :arg-types (new-arg-types)
188                                        :result-type result-type)
189                                     ,@(new-args))))))
190         (sb!c::give-up-ir1-transform))))
191
192 ;;; The ABI is vague about how signed sub-word integer return values
193 ;;; are handled, but since gcc versions >=4.3 no longer do sign
194 ;;; extension in the callee, we need to do it in the caller.
195 (defknown sign-extend ((signed-byte 32) t) fixnum
196     (foldable flushable movable))
197
198 (define-vop (sign-extend)
199   (:translate sign-extend)
200   (:policy :fast-safe)
201   (:args (val :scs (signed-reg)))
202   (:arg-types fixnum (:constant fixnum))
203   (:info size)
204   (:results (res :scs (signed-reg)))
205   (:result-types fixnum)
206   (:generator 1
207    (inst movsxd res
208          (make-random-tn :kind :normal
209                          :sc (sc-or-lose (ecase size
210                                            (8 'byte-reg)
211                                            (16 'word-reg)
212                                            (32 'dword-reg)))
213                          :offset (tn-offset val)))))
214
215 #-sb-xc-host
216 (defun sign-extend (x size)
217   (declare (type fixnum x))
218   (ecase size
219     (8 (sign-extend x size))
220     (16 (sign-extend x size))
221     (32 (sign-extend x size))))
222
223 #+sb-xc-host
224 (defun sign-extend (x size)
225   (if (logbitp (1- size) x)
226       (dpb x (byte size 0) -1)
227       x))
228
229 (define-vop (foreign-symbol-sap)
230   (:translate foreign-symbol-sap)
231   (:policy :fast-safe)
232   (:args)
233   (:arg-types (:constant simple-string))
234   (:info foreign-symbol)
235   (:results (res :scs (sap-reg)))
236   (:result-types system-area-pointer)
237   (:generator 2
238    (inst lea res (make-fixup foreign-symbol :foreign))))
239
240 #!+linkage-table
241 (define-vop (foreign-symbol-dataref-sap)
242   (:translate foreign-symbol-dataref-sap)
243   (:policy :fast-safe)
244   (:args)
245   (:arg-types (:constant simple-string))
246   (:info foreign-symbol)
247   (:results (res :scs (sap-reg)))
248   (:result-types system-area-pointer)
249   (:generator 2
250    (inst mov res (make-fixup foreign-symbol :foreign-dataref))))
251
252 (define-vop (call-out)
253   (:args (function :scs (sap-reg))
254          (args :more t))
255   (:results (results :more t))
256   (:temporary (:sc unsigned-reg :offset rax-offset :to :result) rax)
257   (:ignore results)
258   (:vop-var vop)
259   (:save-p t)
260   (:generator 0
261     ;; ABI: Direction flag must be clear on function entry. -- JES, 2006-01-20
262     (inst cld)
263     ;; ABI: AL contains amount of arguments passed in XMM registers
264     ;; for vararg calls.
265     (move-immediate rax
266                     (loop for tn-ref = args then (tn-ref-across tn-ref)
267                        while tn-ref
268                        count (eq (sb-name (sc-sb (tn-sc (tn-ref-tn tn-ref))))
269                                  'float-registers)))
270     (inst call function)
271     ;; To give the debugger a clue. XX not really internal-error?
272     (note-this-location vop :internal-error)))
273
274 (define-vop (alloc-number-stack-space)
275   (:info amount)
276   (:results (result :scs (sap-reg any-reg)))
277   (:result-types system-area-pointer)
278   (:generator 0
279     (aver (location= result rsp-tn))
280     (unless (zerop amount)
281       (let ((delta (logandc2 (+ amount 7) 7)))
282         (inst sub rsp-tn delta)))
283     ;; C stack must be 16 byte aligned
284     (inst and rsp-tn -16)
285     (move result rsp-tn)))
286
287 (define-vop (dealloc-number-stack-space)
288   (:info amount)
289   (:generator 0
290     (unless (zerop amount)
291       (let ((delta (logandc2 (+ amount 7) 7)))
292         (inst add rsp-tn delta)))))
293
294 (define-vop (alloc-alien-stack-space)
295   (:info amount)
296   #!+sb-thread (:temporary (:sc unsigned-reg) temp)
297   (:results (result :scs (sap-reg any-reg)))
298   (:result-types system-area-pointer)
299   #!+sb-thread
300   (:generator 0
301     (aver (not (location= result rsp-tn)))
302     (unless (zerop amount)
303       (let ((delta (logandc2 (+ amount 7) 7)))
304         (inst mov temp
305               (make-ea :qword
306                        :disp (+ nil-value
307                                 (static-symbol-offset '*alien-stack*)
308                                 (ash symbol-tls-index-slot word-shift)
309                                 (- other-pointer-lowtag))))
310         (inst sub (make-ea :qword :base thread-base-tn
311                            :scale 1 :index temp) delta)))
312     (load-tl-symbol-value result *alien-stack*))
313   #!-sb-thread
314   (:generator 0
315     (aver (not (location= result rsp-tn)))
316     (unless (zerop amount)
317       (let ((delta (logandc2 (+ amount 7) 7)))
318         (inst sub (make-ea :qword
319                            :disp (+ nil-value
320                                     (static-symbol-offset '*alien-stack*)
321                                     (ash symbol-value-slot word-shift)
322                                     (- other-pointer-lowtag)))
323               delta)))
324     (load-symbol-value result *alien-stack*)))
325
326 (define-vop (dealloc-alien-stack-space)
327   (:info amount)
328   #!+sb-thread (:temporary (:sc unsigned-reg) temp)
329   #!+sb-thread
330   (:generator 0
331     (unless (zerop amount)
332       (let ((delta (logandc2 (+ amount 7) 7)))
333         (inst mov temp
334               (make-ea :qword
335                        :disp (+ nil-value
336                                 (static-symbol-offset '*alien-stack*)
337                                 (ash symbol-tls-index-slot word-shift)
338                                 (- other-pointer-lowtag))))
339         (inst add (make-ea :qword :base thread-base-tn :scale 1 :index temp)
340               delta))))
341   #!-sb-thread
342   (:generator 0
343     (unless (zerop amount)
344       (let ((delta (logandc2 (+ amount 7) 7)))
345         (inst add (make-ea :qword
346                            :disp (+ nil-value
347                                     (static-symbol-offset '*alien-stack*)
348                                     (ash symbol-value-slot word-shift)
349                                     (- other-pointer-lowtag)))
350               delta)))))
351
352 ;;; not strictly part of the c-call convention, but needed for the
353 ;;; WITH-PINNED-OBJECTS macro used for "locking down" lisp objects so
354 ;;; that GC won't move them while foreign functions go to work.
355 (define-vop (touch-object)
356   (:translate touch-object)
357   (:args (object))
358   (:ignore object)
359   (:policy :fast-safe)
360   (:arg-types t)
361   (:generator 0))
362
363 ;;; Callbacks
364
365 #-sb-xc-host
366 (defun alien-callback-accessor-form (type sp offset)
367   `(deref (sap-alien (sap+ ,sp ,offset) (* ,type))))
368
369 #-sb-xc-host
370 (defun alien-callback-assembler-wrapper (index result-type argument-types)
371   (labels ((make-tn-maker (sc-name)
372              (lambda (offset)
373                (make-random-tn :kind :normal
374                                :sc (sc-or-lose sc-name)
375                                :offset offset)))
376            (out-of-registers-error ()
377              (error "Too many arguments in callback")))
378     (let* ((segment (make-segment))
379            (rax rax-tn)
380            (rcx rcx-tn)
381            (rdi rdi-tn)
382            (rsi rsi-tn)
383            (rdx rdx-tn)
384            (rbp rbp-tn)
385            (rsp rsp-tn)
386            (xmm0 float0-tn)
387            ([rsp] (make-ea :qword :base rsp :disp 0))
388            ;; How many arguments have been copied
389            (arg-count 0)
390            ;; How many arguments have been copied from the stack
391            (stack-argument-count 0)
392            (gprs (mapcar (make-tn-maker 'any-reg) *c-call-register-arg-offsets*))
393            (fprs (mapcar (make-tn-maker 'double-reg)
394                          ;; Only 8 first XMM registers are used for
395                          ;; passing arguments
396                          (subseq *float-regs* 0 8))))
397       (assemble (segment)
398         ;; Make room on the stack for arguments.
399         (inst sub rsp (* n-word-bytes (length argument-types)))
400         ;; Copy arguments from registers to stack
401         (dolist (type argument-types)
402           (let ((integerp (not (alien-float-type-p type)))
403                 ;; A TN pointing to the stack location where the
404                 ;; current argument should be stored for the purposes
405                 ;; of ENTER-ALIEN-CALLBACK.
406                 (target-tn (make-ea :qword :base rsp
407                                    :disp (* arg-count
408                                             n-word-bytes)))
409                 ;; A TN pointing to the stack location that contains
410                 ;; the next argument passed on the stack.
411                 (stack-arg-tn (make-ea :qword :base rsp
412                                        :disp (* (+ 1
413                                                    (length argument-types)
414                                                    stack-argument-count)
415                                                 n-word-bytes))))
416             (incf arg-count)
417             (cond (integerp
418                    (let ((gpr (pop gprs)))
419                      ;; Argument not in register, copy it from the old
420                      ;; stack location to a temporary register.
421                      (unless gpr
422                        (incf stack-argument-count)
423                        (setf gpr temp-reg-tn)
424                        (inst mov gpr stack-arg-tn))
425                      ;; Copy from either argument register or temporary
426                      ;; register to target.
427                      (inst mov target-tn gpr)))
428                   ((or (alien-single-float-type-p type)
429                        (alien-double-float-type-p type))
430                    (let ((fpr (pop fprs)))
431                      (cond (fpr
432                             ;; Copy from float register to target location.
433                             (inst movq target-tn fpr))
434                            (t
435                             ;; Not in float register. Copy from stack to
436                             ;; temporary (general purpose) register, and
437                             ;; from there to the target location.
438                             (incf stack-argument-count)
439                             (inst mov temp-reg-tn stack-arg-tn)
440                             (inst mov target-tn temp-reg-tn)))))
441                   (t
442                    (bug "Unknown alien floating point type: ~S" type)))))
443
444         ;; arg0 to FUNCALL3 (function)
445         ;;
446         ;; Indirect the access to ENTER-ALIEN-CALLBACK through
447         ;; the symbol-value slot of SB-ALIEN::*ENTER-ALIEN-CALLBACK*
448         ;; to ensure it'll work even if the GC moves ENTER-ALIEN-CALLBACK.
449         ;; Skip any SB-THREAD TLS magic, since we don't expect anyone
450         ;; to rebind the variable. -- JES, 2006-01-01
451         (inst mov rdi (+ nil-value (static-symbol-offset
452                                     'sb!alien::*enter-alien-callback*)))
453         (loadw rdi rdi symbol-value-slot other-pointer-lowtag)
454         ;; arg0 to ENTER-ALIEN-CALLBACK (trampoline index)
455         (inst mov rsi (fixnumize index))
456         ;; arg1 to ENTER-ALIEN-CALLBACK (pointer to argument vector)
457         (inst mov rdx rsp)
458         ;; add room on stack for return value
459         (inst sub rsp 8)
460         ;; arg2 to ENTER-ALIEN-CALLBACK (pointer to return value)
461         (inst mov rcx rsp)
462
463         ;; Make new frame
464         (inst push rbp)
465         (inst mov  rbp rsp)
466
467         ;; Call
468         (inst mov  rax (foreign-symbol-address "funcall3"))
469         (inst call rax)
470
471         ;; Back! Restore frame
472         (inst mov rsp rbp)
473         (inst pop rbp)
474
475         ;; Result now on top of stack, put it in the right register
476         (cond
477           ((or (alien-integer-type-p result-type)
478                (alien-pointer-type-p result-type)
479                (alien-type-= #.(parse-alien-type 'system-area-pointer nil)
480                              result-type))
481            (inst mov rax [rsp]))
482           ((or (alien-single-float-type-p result-type)
483                (alien-double-float-type-p result-type))
484            (inst movq xmm0 [rsp]))
485           ((alien-void-type-p result-type))
486           (t
487            (error "unrecognized alien type: ~A" result-type)))
488
489         ;; Pop the arguments and the return value from the stack to get
490         ;; the return address at top of stack.
491         (inst add rsp (* (1+ (length argument-types)) n-word-bytes))
492         ;; Return
493         (inst ret))
494       (finalize-segment segment)
495       ;; Now that the segment is done, convert it to a static
496       ;; vector we can point foreign code to.
497       (let ((buffer (sb!assem::segment-buffer segment)))
498         (make-static-vector (length buffer)
499                             :element-type '(unsigned-byte 8)
500                             :initial-contents buffer)))))