Simplify (and robustify) regular PACKing
[sbcl.git] / src / compiler / aliencomp.lisp
index c7e8f3d..be71829 100644 (file)
   (flushable movable))
 (defknown deport (alien alien-type) t
   (flushable movable))
-(defknown extract-alien-value (system-area-pointer unsigned-byte alien-type) t
+(defknown deport-alloc (alien alien-type) t
+  (flushable movable))
+(defknown %alien-value (system-area-pointer unsigned-byte alien-type) t
   (flushable))
-(defknown deposit-alien-value (system-area-pointer unsigned-byte alien-type t) t
+(defknown (setf %alien-value) (t system-area-pointer unsigned-byte alien-type) t
   ())
 
 (defknown alien-funcall (alien-value &rest *) *
   (any recursive))
-#!+win32
-(defknown alien-funcall-stdcall (alien-value &rest *) *
-  (any recursive))
 \f
 ;;;; cosmetic transforms
 
 (deftransform slot ((alien slot) * * :important t)
   (multiple-value-bind (slot-offset slot-type)
       (find-slot-offset-and-type alien slot)
-    `(extract-alien-value (alien-sap alien)
-                          ,slot-offset
-                          ',slot-type)))
+    `(%alien-value (alien-sap alien)
+                   ,slot-offset
+                   ',slot-type)))
 
 #+nil ;; ### But what about coercions?
 (defoptimizer (%set-slot derive-type) ((alien slot value))
 (deftransform %set-slot ((alien slot value) * * :important t)
   (multiple-value-bind (slot-offset slot-type)
       (find-slot-offset-and-type alien slot)
-    `(deposit-alien-value (alien-sap alien)
-                          ,slot-offset
-                          ',slot-type
-                          value)))
+    `(setf (%alien-value (alien-sap alien)
+                         ,slot-offset
+                         ',slot-type)
+           value)))
 
 (defoptimizer (%slot-addr derive-type) ((alien slot))
   (block nil
          (abort-ir1-transform "too many indices for pointer deref: ~W"
                               (length indices)))
        (let ((element-type (alien-pointer-type-to alien-type)))
+         (unless element-type
+           (give-up-ir1-transform "unable to open code deref of wild pointer type"))
          (if indices
              (let ((bits (alien-type-bits element-type))
                    (alignment (alien-type-alignment element-type)))
   (multiple-value-bind (indices-args offset-expr element-type)
       (compute-deref-guts alien indices)
     `(lambda (alien ,@indices-args)
-       (extract-alien-value (alien-sap alien)
-                            ,offset-expr
-                            ',element-type))))
+       (%alien-value (alien-sap alien)
+                     ,offset-expr
+                     ',element-type))))
 
 #+nil ;; ### Again, the value might be coerced.
 (defoptimizer (%set-deref derive-type) ((alien value &rest noise))
   (multiple-value-bind (indices-args offset-expr element-type)
       (compute-deref-guts alien indices)
     `(lambda (alien value ,@indices-args)
-       (deposit-alien-value (alien-sap alien)
-                            ,offset-expr
-                            ',element-type
-                            value))))
+       (setf (%alien-value (alien-sap alien)
+                           ,offset-expr
+                           ',element-type)
+             value))))
 
 (defoptimizer (%deref-addr derive-type) ((alien &rest noise))
   (declare (ignore noise))
         (return (make-alien-type-type type))))
     *wild-type*))
 
-(deftransform %heap-alien ((info) * * :important t)
+(deftransform %heap-alien ((info) ((constant-arg heap-alien-info)) * :important t)
   (multiple-value-bind (sap type) (heap-alien-sap-and-type info)
-    `(extract-alien-value ,sap 0 ',type)))
+    `(%alien-value ,sap 0 ',type)))
 
 #+nil ;; ### Again, deposit value might change the type.
 (defoptimizer (%set-heap-alien derive-type) ((info value))
 
 (deftransform %set-heap-alien ((info value) (heap-alien-info *) * :important t)
   (multiple-value-bind (sap type) (heap-alien-sap-and-type info)
-    `(deposit-alien-value ,sap 0 ',type value)))
+    `(setf (%alien-value ,sap 0 ',type) value)))
 
 (defoptimizer (%heap-alien-addr derive-type) ((info))
   (block nil
 (deftransform %heap-alien-addr ((info) * * :important t)
   (multiple-value-bind (sap type) (heap-alien-sap-and-type info)
     (/noshow "in DEFTRANSFORM %HEAP-ALIEN-ADDR, creating %SAP-ALIEN")
-    `(%sap-alien ,sap ',type)))
+    `(%sap-alien ,sap ',(make-alien-pointer-type :to type))))
+
 \f
 ;;;; support for local (stack or register) aliens
 
-(deftransform make-local-alien ((info) * * :important t)
+(defun alien-info-constant-or-abort (info)
   (unless (constant-lvar-p info)
-    (abort-ir1-transform "Local alien info isn't constant?"))
+    (abort-ir1-transform "Local alien info isn't constant?")))
+
+(deftransform make-local-alien ((info) * * :important t)
+  (alien-info-constant-or-abort info)
   (let* ((info (lvar-value info))
          (alien-type (local-alien-info-type info))
          (bits (alien-type-bits alien-type)))
     (/noshow (local-alien-info-force-to-memory-p info))
     (/noshow alien-type (unparse-alien-type alien-type) (alien-type-bits alien-type))
     (if (local-alien-info-force-to-memory-p info)
-      #!+(or x86 x86-64) `(truly-the system-area-pointer
-                         (%primitive alloc-alien-stack-space
-                                     ,(ceiling (alien-type-bits alien-type)
-                                               sb!vm:n-byte-bits)))
-      #!-(or x86 x86-64) `(truly-the system-area-pointer
-                         (%primitive alloc-number-stack-space
-                                     ,(ceiling (alien-type-bits alien-type)
-                                               sb!vm:n-byte-bits)))
-      (let* ((alien-rep-type-spec (compute-alien-rep-type alien-type))
-             (alien-rep-type (specifier-type alien-rep-type-spec)))
-        (cond ((csubtypep (specifier-type 'system-area-pointer)
-                          alien-rep-type)
-               '(int-sap 0))
-              ((ctypep 0 alien-rep-type) 0)
-              ((ctypep 0.0f0 alien-rep-type) 0.0f0)
-              ((ctypep 0.0d0 alien-rep-type) 0.0d0)
-              (t
-               (compiler-error
-                "Aliens of type ~S cannot be represented immediately."
-                (unparse-alien-type alien-type))))))))
+        #!+(or x86 x86-64)
+        `(%primitive alloc-alien-stack-space
+                     ,(ceiling (alien-type-bits alien-type)
+                               sb!vm:n-byte-bits))
+        #!-(or x86 x86-64)
+        `(%primitive alloc-number-stack-space
+                     ,(ceiling (alien-type-bits alien-type)
+                               sb!vm:n-byte-bits))
+        (let* ((alien-rep-type-spec (compute-alien-rep-type alien-type))
+               (alien-rep-type (specifier-type alien-rep-type-spec)))
+          (cond ((csubtypep (specifier-type 'system-area-pointer)
+                            alien-rep-type)
+                 '(int-sap 0))
+                ((ctypep 0 alien-rep-type) 0)
+                ((ctypep 0.0f0 alien-rep-type) 0.0f0)
+                ((ctypep 0.0d0 alien-rep-type) 0.0d0)
+                (t
+                 (compiler-error
+                  "Aliens of type ~S cannot be represented immediately."
+                  (unparse-alien-type alien-type))))))))
 
 (deftransform note-local-alien-type ((info var) * * :important t)
-  ;; FIXME: This test and error occur about a zillion times. They
-  ;; could be factored into a function.
-  (unless (constant-lvar-p info)
-    (abort-ir1-transform "Local alien info isn't constant?"))
+  (alien-info-constant-or-abort info)
   (let ((info (lvar-value info)))
     (/noshow "in DEFTRANSFORM NOTE-LOCAL-ALIEN-TYPE" info)
     (/noshow (local-alien-info-force-to-memory-p info))
   nil)
 
 (deftransform local-alien ((info var) * * :important t)
-  (unless (constant-lvar-p info)
-    (abort-ir1-transform "Local alien info isn't constant?"))
+  (alien-info-constant-or-abort info)
   (let* ((info (lvar-value info))
          (alien-type (local-alien-info-type info)))
     (/noshow "in DEFTRANSFORM LOCAL-ALIEN" info alien-type)
     (/noshow (local-alien-info-force-to-memory-p info))
     (if (local-alien-info-force-to-memory-p info)
-        `(extract-alien-value var 0 ',alien-type)
+        `(%alien-value var 0 ',alien-type)
         `(naturalize var ',alien-type))))
 
 (deftransform %local-alien-forced-to-memory-p ((info) * * :important t)
-  (unless (constant-lvar-p info)
-    (abort-ir1-transform "Local alien info isn't constant?"))
+  (alien-info-constant-or-abort info)
   (let ((info (lvar-value info)))
     (local-alien-info-force-to-memory-p info)))
 
 (deftransform %set-local-alien ((info var value) * * :important t)
-  (unless (constant-lvar-p info)
-    (abort-ir1-transform "Local alien info isn't constant?"))
+  (alien-info-constant-or-abort info)
   (let* ((info (lvar-value info))
          (alien-type (local-alien-info-type info)))
     (if (local-alien-info-force-to-memory-p info)
-        `(deposit-alien-value var 0 ',alien-type value)
+        `(setf (%alien-value var 0 ',alien-type) value)
         '(error "This should be eliminated as dead code."))))
 
 (defoptimizer (%local-alien-addr derive-type) ((info var))
       *wild-type*))
 
 (deftransform %local-alien-addr ((info var) * * :important t)
-  (unless (constant-lvar-p info)
-    (abort-ir1-transform "Local alien info isn't constant?"))
+  (alien-info-constant-or-abort info)
   (let* ((info (lvar-value info))
          (alien-type (local-alien-info-type info)))
     (/noshow "in DEFTRANSFORM %LOCAL-ALIEN-ADDR, creating %SAP-ALIEN")
         (error "This shouldn't happen."))))
 
 (deftransform dispose-local-alien ((info var) * * :important t)
-  (unless (constant-lvar-p info)
-    (abort-ir1-transform "Local alien info isn't constant?"))
+  (alien-info-constant-or-abort info)
   (let* ((info (lvar-value info))
          (alien-type (local-alien-info-type info)))
     (if (local-alien-info-force-to-memory-p info)
   (let ((alien-node (lvar-uses alien)))
     (typecase alien-node
       (combination
-       (extract-fun-args alien '%sap-alien 2)
+       (splice-fun-args alien '%sap-alien 2)
        '(lambda (sap type)
           (declare (ignore type))
           sap))
     (%computed-lambda #'compute-naturalize-lambda type))
   (deftransform deport ((alien type) * * :important t)
     (%computed-lambda #'compute-deport-lambda type))
-  (deftransform extract-alien-value ((sap offset type) * * :important t)
+  (deftransform deport-alloc ((alien type) * * :important t)
+    (%computed-lambda #'compute-deport-alloc-lambda type))
+  (deftransform %alien-value ((sap offset type) * * :important t)
     (%computed-lambda #'compute-extract-lambda type))
-  (deftransform deposit-alien-value ((sap offset type value) * * :important t)
+  (deftransform (setf %alien-value) ((value sap offset type) * * :important t)
     (%computed-lambda #'compute-deposit-lambda type)))
 \f
 ;;;; a hack to clean up divisions
          (count-low-order-zeros (lvar-uses thing))))
     (combination
      (case (let ((name (lvar-fun-name (combination-fun thing))))
-             (or (modular-version-info name :unsigned) name))
+             (or (modular-version-info name :untagged nil) name))
        ((+ -)
         (let ((min most-positive-fixnum)
               (itype (specifier-type 'integer)))
       (give-up-ir1-transform))
     (let ((inside-fun-name (lvar-fun-name (combination-fun value-node))))
       (multiple-value-bind (prototype width)
-          (modular-version-info inside-fun-name :unsigned)
+          (modular-version-info inside-fun-name :untagged nil)
         (unless (eq (or prototype inside-fun-name) 'ash)
           (give-up-ir1-transform))
         (when (and width (not (constant-lvar-p amount)))
             (unless (and (constant-lvar-p inside-amount)
                          (not (minusp (lvar-value inside-amount))))
               (give-up-ir1-transform)))
-          (extract-fun-args value inside-fun-name 2)
+          (splice-fun-args value inside-fun-name 2)
           (if width
               `(lambda (value amount1 amount2)
                  (logand (ash value (+ amount1 amount2))
     `(lambda (function ,@names)
        (alien-funcall (deref function) ,@names))))
 
-(deftransform alien-funcall ((function &rest args) * * :important t)
+(deftransform alien-funcall ((function &rest args) * * :node node :important t)
   (let ((type (lvar-type function)))
     (unless (alien-type-type-p type)
       (give-up-ir1-transform "can't tell function type at compile time"))
             (let ((param (gensym)))
               (params param)
               (deports `(deport ,param ',arg-type))))
+          ;; Build BODY from the inside out.
           (let ((return-type (alien-fun-type-result-type alien-type))
+                ;; Innermost, we DEPORT the parameters (e.g. by taking SAPs
+                ;; to them) and do the call.
                 (body `(%alien-funcall (deport function ',alien-type)
                                        ',alien-type
                                        ,@(deports))))
+            ;; Wrap that in a WITH-PINNED-OBJECTS to ensure the values
+            ;; the SAPs are taken for won't be moved by the GC. (If
+            ;; needed: some alien types won't need it).
+            (setf body `(maybe-with-pinned-objects ,(params) ,arg-types
+                          ,body))
+            ;; Around that handle any memory allocation that's needed.
+            ;; Mostly the DEPORT-ALLOC alien-type-methods are just an
+            ;; identity operation, but for example for deporting a
+            ;; Unicode string we need to convert the string into an
+            ;; octet array. This step needs to be done before the pinning
+            ;; to ensure we pin the right objects, so it can't be combined
+            ;; with the deporting.
+            ;; -- JES 2006-03-16
+            (loop for param in (params)
+                  for arg-type in arg-types
+                  do (setf body
+                           `(let ((,param (deport-alloc ,param ',arg-type)))
+                              ,body)))
             (if (alien-values-type-p return-type)
                 (collect ((temps) (results))
                   (dolist (type (alien-values-type-values return-type))
                         `(multiple-value-bind ,(temps) ,body
                            (values ,@(results)))))
                 (setf body `(naturalize ,body ',return-type)))
+            ;; Remember this frame to make sure that we can get back
+            ;; to it later regardless of how the foreign stack looks
+            ;; like.
+            #!+:c-stack-is-control-stack
+            (when (policy node (= 3 alien-funcall-saves-fp-and-pc))
+              (setf body `(invoke-with-saved-fp-and-pc (lambda () ,body))))
             (/noshow "returning from DEFTRANSFORM ALIEN-FUNCALL" (params) body)
             `(lambda (function ,@(params))
+               (declare (optimize (let-conversion 3)))
                ,body)))))))
 
 (defoptimizer (%alien-funcall derive-type) ((function type &rest args))
       (error "Something is broken."))
     (values-specifier-type
      (compute-alien-rep-type
-      (alien-fun-type-result-type type)))))
+      (alien-fun-type-result-type type)
+      :result))))
 
 (defoptimizer (%alien-funcall ltn-annotate)
               ((function type &rest args) node ltn-policy)
   (dolist (arg args)
     (annotate-ordinary-lvar arg)))
 
+;;; We support both the stdcall and cdecl calling conventions on win32 by
+;;; resetting ESP after the foreign function returns. This way it works
+;;; correctly whether the party that is supposed to pop arguments from
+;;; the stack is the caller (cdecl) or the callee (stdcall).
 (defoptimizer (%alien-funcall ir2-convert)
               ((function type &rest args) call block)
   (let ((type (if (constant-lvar-p type)
                   (lvar-value type)
                   (error "Something is broken.")))
         (lvar (node-lvar call))
-        (args args))
+        (args args)
+        #!+x86
+        (stack-pointer (make-stack-pointer-tn)))
     (multiple-value-bind (nsp stack-frame-size arg-tns result-tns)
         (make-call-out-tns type)
+      #!+x86
+      (progn
+        (vop set-fpu-word-for-c call block)
+        (vop current-stack-pointer call block stack-pointer))
       (vop alloc-number-stack-space call block stack-frame-size nsp)
       (dolist (tn arg-tns)
-        (let* ((arg (pop args))
-               (sc (tn-sc tn))
+        ;; On PPC, TN might be a list. This is used to indicate
+        ;; something special needs to happen. See below.
+        ;;
+        ;; FIXME: We should implement something better than this.
+        (let* ((first-tn (if (listp tn) (car tn) tn))
+               (arg (pop args))
+               (sc (tn-sc first-tn))
                (scn (sc-number sc))
-               #!-(or x86 x86-64) (temp-tn (make-representation-tn (tn-primitive-type tn)
-                                                       scn))
+               #!-(or x86 x86-64) (temp-tn (make-representation-tn
+                                            (tn-primitive-type first-tn) scn))
                (move-arg-vops (svref (sc-move-arg-vops sc) scn)))
           (aver arg)
           (unless (= (length move-arg-vops) 1)
             (error "no unique move-arg-vop for moves in SC ~S" (sc-name sc)))
           #!+(or x86 x86-64) (emit-move-arg-template call
-                                         block
-                                         (first move-arg-vops)
-                                         (lvar-tn call block arg)
-                                         nsp
-                                         tn)
+                                                     block
+                                                     (first move-arg-vops)
+                                                     (lvar-tn call block arg)
+                                                     nsp
+                                                     first-tn)
           #!-(or x86 x86-64) (progn
-                   (emit-move call
-                              block
-                              (lvar-tn call block arg)
-                              temp-tn)
-                   (emit-move-arg-template call
-                                           block
-                                           (first move-arg-vops)
-                                           temp-tn
-                                           nsp
-                                           tn))))
+                               (emit-move call
+                                          block
+                                          (lvar-tn call block arg)
+                                          temp-tn)
+                               (emit-move-arg-template call
+                                                       block
+                                                       (first move-arg-vops)
+                                                       temp-tn
+                                                       nsp
+                                                       first-tn))
+          #!+(and ppc darwin)
+          (when (listp tn)
+            ;; This means that we have a float arg that we need to
+            ;; also copy to some int regs. The list contains the TN
+            ;; for the float as well as the TNs to use for the int
+            ;; arg.
+            (destructuring-bind (float-tn i1-tn &optional i2-tn)
+                tn
+              (if i2-tn
+                  (vop sb!vm::move-double-to-int-arg call block
+                       float-tn i1-tn i2-tn)
+                  (vop sb!vm::move-single-to-int-arg call block
+                       float-tn i1-tn))))))
       (aver (null args))
       (unless (listp result-tns)
         (setf result-tns (list result-tns)))
-      (vop* call-out call block
-            ((lvar-tn call block function)
-             (reference-tn-list arg-tns nil))
-            ((reference-tn-list result-tns t)))
+      (let ((arg-tns (flatten-list arg-tns)))
+        (vop* call-out call block
+              ((lvar-tn call block function)
+               (reference-tn-list arg-tns nil))
+              ((reference-tn-list result-tns t))))
+      #!-x86
       (vop dealloc-number-stack-space call block stack-frame-size)
-      (move-lvar-result call block result-tns lvar))))
-\f
-;;;; ALIEN-FUNCALL-STDCALL support
-
-#!+win32
-(deftransform alien-funcall-stdcall ((function &rest args)
-                             ((alien (* t)) &rest *) *
-                             :important t)
-  (let ((names (make-gensym-list (length args))))
-    (/noshow "entering first DEFTRANSFORM ALIEN-FUNCALL-STDCALL" function args)
-    `(lambda (function ,@names)
-       (alien-funcall-stdcall (deref function) ,@names))))
-
-#!+win32
-(deftransform alien-funcall-stdcall ((function &rest args) * * :important t)
-  (let ((type (lvar-type function)))
-    (unless (alien-type-type-p type)
-      (give-up-ir1-transform "can't tell function type at compile time"))
-    (/noshow "entering second DEFTRANSFORM ALIEN-FUNCALL-STDCALL" function)
-    (let ((alien-type (alien-type-type-alien-type type)))
-      (unless (alien-fun-type-p alien-type)
-        (give-up-ir1-transform))
-      (let ((arg-types (alien-fun-type-arg-types alien-type)))
-        (unless (= (length args) (length arg-types))
-          (abort-ir1-transform
-           "wrong number of arguments; expected ~W, got ~W"
-           (length arg-types)
-           (length args)))
-        (collect ((params) (deports))
-          (dolist (arg-type arg-types)
-            (let ((param (gensym)))
-              (params param)
-              (deports `(deport ,param ',arg-type))))
-          (let ((return-type (alien-fun-type-result-type alien-type))
-                (body `(%alien-funcall-stdcall (deport function ',alien-type)
-                                       ',alien-type
-                                       ,@(deports))))
-            (if (alien-values-type-p return-type)
-                (collect ((temps) (results))
-                  (dolist (type (alien-values-type-values return-type))
-                    (let ((temp (gensym)))
-                      (temps temp)
-                      (results `(naturalize ,temp ',type))))
-                  (setf body
-                        `(multiple-value-bind ,(temps) ,body
-                           (values ,@(results)))))
-                (setf body `(naturalize ,body ',return-type)))
-            (/noshow "returning from DEFTRANSFORM ALIEN-FUNCALL-STDCALL" (params) body)
-            `(lambda (function ,@(params))
-               ,body)))))))
-
-#!+win32
-(defoptimizer (%alien-funcall-stdcall derive-type) ((function type &rest args))
-  (declare (ignore function args))
-  (unless (constant-lvar-p type)
-    (error "Something is broken."))
-  (let ((type (lvar-value type)))
-    (unless (alien-fun-type-p type)
-      (error "Something is broken."))
-    (values-specifier-type
-     (compute-alien-rep-type
-      (alien-fun-type-result-type type)))))
-
-#!+win32
-(defoptimizer (%alien-funcall-stdcall ltn-annotate)
-              ((function type &rest args) node ltn-policy)
-  (setf (basic-combination-info node) :funny)
-  (setf (node-tail-p node) nil)
-  (annotate-ordinary-lvar function)
-  (dolist (arg args)
-    (annotate-ordinary-lvar arg)))
-
-#!+win32
-(defoptimizer (%alien-funcall-stdcall ir2-convert)
-              ((function type &rest args) call block)
-  (let ((type (if (constant-lvar-p type)
-                  (lvar-value type)
-                  (error "Something is broken.")))
-        (lvar (node-lvar call))
-        (args args))
-    (multiple-value-bind (nsp stack-frame-size arg-tns result-tns)
-        (make-call-out-tns type)
-      (vop alloc-number-stack-space call block stack-frame-size nsp)
-      (dolist (tn arg-tns)
-        (let* ((arg (pop args))
-               (sc (tn-sc tn))
-               (scn (sc-number sc))
-               #!-x86 (temp-tn (make-representation-tn (tn-primitive-type tn)
-                                                       scn))
-               (move-arg-vops (svref (sc-move-arg-vops sc) scn)))
-          (aver arg)
-          (unless (= (length move-arg-vops) 1)
-            (error "no unique move-arg-vop for moves in SC ~S" (sc-name sc)))
-          #!+x86 (emit-move-arg-template call
-                                         block
-                                         (first move-arg-vops)
-                                         (lvar-tn call block arg)
-                                         nsp
-                                         tn)
-          #!-x86 (progn
-                   (emit-move call
-                              block
-                              (lvar-tn call block arg)
-                              temp-tn)
-                   (emit-move-arg-template call
-                                           block
-                                           (first move-arg-vops)
-                                           temp-tn
-                                           nsp
-                                           tn))))
-      (aver (null args))
-      (unless (listp result-tns)
-        (setf result-tns (list result-tns)))
-      (vop* call-out call block
-            ((lvar-tn call block function)
-             (reference-tn-list arg-tns nil))
-            ((reference-tn-list result-tns t)))
-      ;; This is the stdcall magic: Callee clears args.
-      #+nil (vop dealloc-number-stack-space call block stack-frame-size)
+      #!+x86
+      (progn
+        (vop reset-stack-pointer call block stack-pointer)
+        (vop set-fpu-word-for-lisp call block))
       (move-lvar-result call block result-tns lvar))))