+;;; Callbacks
+
+#-sb-xc-host
+(defun alien-callback-accessor-form (type sp offset)
+ `(deref (sap-alien (sap+ ,sp ,offset) (* ,type))))
+
+#-sb-xc-host
+(defun alien-callback-assembler-wrapper (index result-type argument-types)
+ (labels ((make-tn-maker (sc-name)
+ (lambda (offset)
+ (make-random-tn :kind :normal
+ :sc (sc-or-lose sc-name)
+ :offset offset)))
+ (out-of-registers-error ()
+ (error "Too many arguments in callback")))
+ (let* ((segment (make-segment))
+ (rax rax-tn)
+ (rcx rcx-tn)
+ (rdi rdi-tn)
+ (rsi rsi-tn)
+ (rdx rdx-tn)
+ (rbp rbp-tn)
+ (rsp rsp-tn)
+ (xmm0 float0-tn)
+ ([rsp] (make-ea :qword :base rsp :disp 0))
+ ;; How many arguments have been copied
+ (arg-count 0)
+ ;; How many arguments have been copied from the stack
+ (stack-argument-count 0)
+ (gprs (mapcar (make-tn-maker 'any-reg) *c-call-register-arg-offsets*))
+ (fprs (mapcar (make-tn-maker 'double-reg)
+ ;; Only 8 first XMM registers are used for
+ ;; passing arguments
+ (subseq *float-regs* 0 8))))
+ (assemble (segment)
+ ;; Make room on the stack for arguments.
+ (inst sub rsp (* n-word-bytes (length argument-types)))
+ ;; Copy arguments from registers to stack
+ (dolist (type argument-types)
+ (let ((integerp (not (alien-float-type-p type)))
+ ;; A TN pointing to the stack location where the
+ ;; current argument should be stored for the purposes
+ ;; of ENTER-ALIEN-CALLBACK.
+ (target-tn (make-ea :qword :base rsp
+ :disp (* arg-count
+ n-word-bytes)))
+ ;; A TN pointing to the stack location that contains
+ ;; the next argument passed on the stack.
+ (stack-arg-tn (make-ea :qword :base rsp
+ :disp (* (+ 1
+ (length argument-types)
+ stack-argument-count)
+ n-word-bytes))))
+ (incf arg-count)
+ (cond (integerp
+ (let ((gpr (pop gprs)))
+ ;; Argument not in register, copy it from the old
+ ;; stack location to a temporary register.
+ (unless gpr
+ (incf stack-argument-count)
+ (setf gpr temp-reg-tn)
+ (inst mov gpr stack-arg-tn))
+ ;; Copy from either argument register or temporary
+ ;; register to target.
+ (inst mov target-tn gpr)))
+ ((or (alien-single-float-type-p type)
+ (alien-double-float-type-p type))
+ (let ((fpr (pop fprs)))
+ (cond (fpr
+ ;; Copy from float register to target location.
+ (inst movq target-tn fpr))
+ (t
+ ;; Not in float register. Copy from stack to
+ ;; temporary (general purpose) register, and
+ ;; from there to the target location.
+ (incf stack-argument-count)
+ (inst mov temp-reg-tn stack-arg-tn)
+ (inst mov target-tn temp-reg-tn)))))
+ (t
+ (bug "Unknown alien floating point type: ~S" type)))))
+
+ ;; arg0 to FUNCALL3 (function)
+ ;;
+ ;; Indirect the access to ENTER-ALIEN-CALLBACK through
+ ;; the symbol-value slot of SB-ALIEN::*ENTER-ALIEN-CALLBACK*
+ ;; to ensure it'll work even if the GC moves ENTER-ALIEN-CALLBACK.
+ ;; Skip any SB-THREAD TLS magic, since we don't expect anyone
+ ;; to rebind the variable. -- JES, 2006-01-01
+ (inst mov rdi (+ nil-value (static-symbol-offset
+ 'sb!alien::*enter-alien-callback*)))
+ (loadw rdi rdi symbol-value-slot other-pointer-lowtag)
+ ;; arg0 to ENTER-ALIEN-CALLBACK (trampoline index)
+ (inst mov rsi (fixnumize index))
+ ;; arg1 to ENTER-ALIEN-CALLBACK (pointer to argument vector)
+ (inst mov rdx rsp)
+ ;; add room on stack for return value
+ (inst sub rsp 8)
+ ;; arg2 to ENTER-ALIEN-CALLBACK (pointer to return value)
+ (inst mov rcx rsp)
+
+ ;; Make new frame
+ (inst push rbp)
+ (inst mov rbp rsp)
+
+ ;; Call
+ (inst mov rax (foreign-symbol-address "funcall3"))
+ (inst call rax)
+
+ ;; Back! Restore frame
+ (inst mov rsp rbp)
+ (inst pop rbp)
+
+ ;; Result now on top of stack, put it in the right register
+ (cond
+ ((or (alien-integer-type-p result-type)
+ (alien-pointer-type-p result-type)
+ (alien-type-= #.(parse-alien-type 'system-area-pointer nil)
+ result-type))
+ (inst mov rax [rsp]))
+ ((or (alien-single-float-type-p result-type)
+ (alien-double-float-type-p result-type))
+ (inst movq xmm0 [rsp]))
+ ((alien-void-type-p result-type))
+ (t
+ (error "unrecognized alien type: ~A" result-type)))
+
+ ;; Pop the arguments and the return value from the stack to get
+ ;; the return address at top of stack.
+ (inst add rsp (* (1+ (length argument-types)) n-word-bytes))
+ ;; Return
+ (inst ret))
+ (finalize-segment segment)
+ ;; Now that the segment is done, convert it to a static
+ ;; vector we can point foreign code to.
+ (let ((buffer (sb!assem::segment-buffer segment)))
+ (make-static-vector (length buffer)
+ :element-type '(unsigned-byte 8)
+ :initial-contents buffer)))))