+ ;;; On linux only use 8 bytes for LR and Back chain. JRXR
+ ;;; 2006/11/10.
+ #!-darwin
+ (defconstant n-foreign-linkage-area-bytes 8)
+
+ ;;; Returns a vector in static space containing machine code for the
+ ;;; callback wrapper. Linux version. JRXR. 2006/11/13
+ #!-darwin
+ (defun alien-callback-assembler-wrapper (index result-type argument-types)
+ (flet ((make-gpr (n)
+ (make-random-tn :kind :normal :sc (sc-or-lose 'any-reg) :offset n))
+ (make-fpr (n)
+ (make-random-tn :kind :normal :sc (sc-or-lose
+ 'double-reg) :offset
+ n)))
+ (let* ((segment (make-segment)))
+ (assemble (segment)
+ ;; Copy args from registers or stack to new position
+ ;; on stack.
+ (let* (
+ ;; Argument store.
+ (arg-store-size
+ (* n-word-bytes
+ (apply '+
+ (mapcar (lambda (type)
+ (ceiling (alien-type-bits type)
+ n-word-bits))
+ argument-types ))))
+ ;; Return area allocation.
+ (n-return-area-words
+ (ceiling (or (alien-type-bits result-type) 0) n-word-bits))
+ (n-return-area-bytes (* n-return-area-words
+ n-word-bytes))
+ ;; FIXME: magic constant, and probably n-args-bytes
+ ;; JRXR: What's this for? Copied from Darwin.
+ (args-size (* 3 n-word-bytes))
+ (frame-size (logandc2
+ (+ arg-store-size
+ n-return-area-bytes
+ args-size
+ SB!VM::NUMBER-STACK-DISPLACEMENT
+ +stack-alignment-bytes+)
+ +stack-alignment-bytes+))
+ (return-area-pos (- frame-size
+ SB!VM::NUMBER-STACK-DISPLACEMENT
+ args-size))
+ (arg-store-pos (- return-area-pos
+ n-return-area-bytes))
+ (stack-pointer (make-gpr 1))
+ (r0 (make-gpr 0))
+ (f0 (make-fpr 0))
+ (in-words-processed 0)
+ (out-words-processed 0)
+ (gprs (mapcar #'make-gpr '(3 4 5 6 7 8 9 10)))
+ (fprs (mapcar #'make-fpr
+ '(1 2 3 4 5 6 7 8))) )
+ ;; Setup useful functions and then copy all args.
+ (flet ((load-address-into (reg addr)
+ (let ((high (ldb (byte 16 16) addr))
+ (low (ldb (byte 16 0) addr)))
+ (inst lis reg high)
+ (inst ori reg reg low)))
+ (save-arg (type words)
+ (let ((integerp (not (alien-float-type-p type)))
+ (in-offset (+ (* in-words-processed n-word-bytes)
+ n-foreign-linkage-area-bytes))
+ (out-offset (- (* out-words-processed n-word-bytes)
+ arg-store-pos)))
+ (cond (integerp
+ (if (and
+ ;; Only upto long longs are passed
+ ;; in registers.
+ (<= words 2)
+ ;; And needs space for whole arg,
+ ;; including alignment.
+ (<= (+ words
+ (rem (length gprs) words))
+ (length gprs)))
+ (progn
+ (if (/= 0
+ (rem (length gprs) words))
+ (pop gprs))
+ (dotimes (k words)
+ (let ((gpr (pop gprs)))
+ (inst stw gpr stack-pointer
+ out-offset))
+ (incf out-words-processed)
+ (incf out-offset n-word-bytes)))
+ (progn
+ ;; First ensure alignment.
+ ;; FIXME! If passing structures
+ ;; becomes allowable, then this is
+ ;; broken.
+ (if (/= 0
+ (rem in-words-processed
+ words))
+ (progn
+ (incf in-words-processed)
+ (incf in-offset
+ n-word-bytes)))
+ (dotimes (k words)
+ ;; Copy from memory to memory.
+ (inst lwz r0 stack-pointer
+ in-offset)
+ (inst stw r0 stack-pointer
+ out-offset)
+ (incf out-words-processed)
+ (incf out-offset n-word-bytes)
+ (incf in-words-processed)
+ (incf in-offset n-word-bytes)))))
+ ;; The handling of floats is a little ugly
+ ;; because we hard-code the number of words
+ ;; for single- and double-floats.
+ ((alien-single-float-type-p type)
+ (let ((fpr (pop fprs)))
+ (if fpr
+ (inst stfs fpr stack-pointer out-offset)
+ (progn
+ ;; The ABI says that floats
+ ;; stored on the stack are
+ ;; promoted to doubles. gcc
+ ;; stores them as floats.
+ ;; Follow gcc here.
+ ;; => no alignment needed either.
+ (inst lfs f0
+ stack-pointer in-offset)
+ (inst stfs f0
+ stack-pointer out-offset)
+ (incf in-words-processed))))
+ (incf out-words-processed))
+ ((alien-double-float-type-p type)
+ (let ((fpr (pop fprs)))
+ (if fpr
+ (inst stfd fpr stack-pointer out-offset)
+ (progn
+ ;; Ensure alignment.
+ (if (oddp in-words-processed)
+ (progn
+ (incf in-words-processed)
+ (incf in-offset n-word-bytes)))
+ (inst lfd f0
+ stack-pointer in-offset)
+ (inst stfd f0
+ stack-pointer out-offset)
+ (incf in-words-processed 2))))
+ (incf out-words-processed 2))
+ (t
+ (bug "Unknown alien floating point type: ~S" type))))))
+ (mapc #'save-arg
+ argument-types
+ (mapcar (lambda (arg)
+ (ceiling (alien-type-bits arg) n-word-bits))
+ argument-types))
+
+ ;; Arranged the args, allocated the return area. Now
+ ;; actuall call funcall3: funcall3 (call-alien-function,
+ ;; index, args, return-area)
+
+ (destructuring-bind (arg1 arg2 arg3 arg4)
+ (mapcar #'make-gpr '(3 4 5 6))
+ (load-address-into arg1 (+ nil-value (static-symbol-offset
+ 'sb!alien::*enter-alien-callback*)))
+ (loadw arg1 arg1 symbol-value-slot other-pointer-lowtag)
+ (inst li arg2 (fixnumize index))
+ (inst addi arg3 stack-pointer (- arg-store-pos))
+ (inst addi arg4 stack-pointer (- return-area-pos)))
+
+ ;; Setup everything. Now save sp, setup the frame.
+ (inst mflr r0)
+ (inst stw r0 stack-pointer (* 2 n-word-bytes)) ; FIXME: magic
+ ; constant, copied from Darwin.
+ (inst stwu stack-pointer stack-pointer (- frame-size))
+
+ ;; And make the call.
+ (load-address-into r0 (foreign-symbol-address "funcall3"))
+ (inst mtlr r0)
+ (inst blrl)
+
+ ;; We're back! Restore sp and lr, load the
+ ;; return value from just under sp, and return.
+ (inst lwz stack-pointer stack-pointer 0)
+ (inst lwz r0 stack-pointer (* 2 n-word-bytes))
+ (inst mtlr r0)
+ (cond
+ ((sb!alien::alien-single-float-type-p result-type)
+ (let ((f1 (make-fpr 1)))
+ (inst lfs f1 stack-pointer (- return-area-pos))))
+ ((sb!alien::alien-double-float-type-p result-type)
+ (let ((f1 (make-fpr 1)))
+ (inst lfd f1 stack-pointer (- return-area-pos))))
+ ((sb!alien::alien-void-type-p result-type)
+ ;; Nothing to do
+ )
+ (t
+ (loop with gprs = (mapcar #'make-gpr '(3 4))
+ repeat n-return-area-words
+ for gpr = (pop gprs)
+ for offset from (- return-area-pos)
+ by n-word-bytes
+ do
+ (unless gpr
+ (bug "Out of return registers in alien-callback trampoline."))
+ (inst lwz gpr stack-pointer offset))))
+ (inst blr))))
+ (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))
+ (vector (make-static-vector (length buffer)
+ :element-type '(unsigned-byte 8)
+ :initial-contents buffer))
+ (sap (sb!sys:vector-sap vector)))
+ (sb!alien:alien-funcall
+ (sb!alien:extern-alien "ppc_flush_icache"
+ (function void
+ system-area-pointer
+ unsigned-long))
+ sap (length buffer))
+ vector))))
+