Better calls to static functions on x86-64.
[sbcl.git] / src / compiler / x86-64 / macros.lisp
index e150e7e..8088d43 100644 (file)
@@ -1,4 +1,4 @@
-;;;; a bunch of handy macros for the x86
+;;;; a bunch of handy macros for x86-64
 
 ;;;; This software is part of the SBCL system. See the README file for
 ;;;; more information.
 
 ;;;; instruction-like macros
 
-(defmacro move (dst src)
+;;; This used to be a macro (and still is on the other platforms) but
+;;; the support for SC-dependent move instructions needed here makes
+;;; that expand into so large an expression that the resulting code
+;;; bloat is not justifiable.
+(defun move (dst src)
   #!+sb-doc
   "Move SRC into DST unless they are location=."
-  (once-only ((n-dst dst)
-              (n-src src))
-    `(unless (location= ,n-dst ,n-src)
-       (sc-case ,n-dst
-         (single-reg
-          (inst movss ,n-dst ,n-src))
-         (double-reg
-          (inst movsd ,n-dst ,n-src))
-         (t
-          (inst mov ,n-dst ,n-src))))))
+  (unless (location= dst src)
+    (sc-case dst
+      ((single-reg complex-single-reg)
+       (aver (xmm-register-p src))
+       (inst movaps dst src))
+      ((double-reg complex-double-reg)
+       (aver (xmm-register-p src))
+       (inst movapd dst src))
+      #!+sb-simd-pack
+      ((int-sse-reg sse-reg)
+       (aver (xmm-register-p src))
+       (inst movdqa dst src))
+      #!+sb-simd-pack
+      ((single-sse-reg double-sse-reg)
+       (aver (xmm-register-p src))
+       (inst movaps dst src))
+      (t
+       (inst mov dst src)))))
 
 (defmacro make-ea-for-object-slot (ptr slot lowtag)
   `(make-ea :qword :base ,ptr :disp (- (* ,slot n-word-bytes) ,lowtag)))
   (once-only ((value value))
     `(cond ((and (integerp ,value)
                  (not (typep ,value '(signed-byte 32))))
-            (multiple-value-bind (lo hi) (dwords-for-quad ,value)
-              (inst mov (make-ea-for-object-slot-half
-                         ,ptr ,slot ,lowtag) lo)
-              (inst mov (make-ea-for-object-slot-half
-                         ,ptr (+ ,slot 1/2) ,lowtag) hi)))
+            (inst mov temp-reg-tn ,value)
+            (inst mov (make-ea-for-object-slot ,ptr ,slot ,lowtag) temp-reg-tn))
            (t
             (inst mov (make-ea-for-object-slot ,ptr ,slot ,lowtag) ,value)))))
 
 
 (defmacro popw (ptr &optional (slot 0) (lowtag 0))
   `(inst pop (make-ea-for-object-slot ,ptr ,slot ,lowtag)))
+
+(defun call-indirect (offset)
+  (let ((ea (make-ea :qword :disp offset)))
+   (cond ((immediate32-p offset)
+          (inst call ea))
+         (t
+          (inst mov temp-reg-tn ea)
+          (inst call temp-reg-tn)))))
 \f
 ;;;; macros to generate useful values
 
 (defmacro load-binding-stack-pointer (reg)
   #!+sb-thread
   `(inst mov ,reg (make-ea :qword :base thread-base-tn
-                   :disp (* 8 thread-binding-stack-pointer-slot)))
+                   :disp (* n-word-bytes thread-binding-stack-pointer-slot)))
   #!-sb-thread
   `(load-symbol-value ,reg *binding-stack-pointer*))
 
 (defmacro store-binding-stack-pointer (reg)
   #!+sb-thread
   `(inst mov (make-ea :qword :base thread-base-tn
-              :disp (* 8 thread-binding-stack-pointer-slot))
+              :disp (* n-word-bytes thread-binding-stack-pointer-slot))
     ,reg)
   #!-sb-thread
   `(store-symbol-value ,reg *binding-stack-pointer*))
               (n-offset offset))
     (ecase *backend-byte-order*
       (:little-endian
-       `(inst mov ,n-target
+       `(inst movzx ,n-target
               (make-ea :byte :base ,n-source :disp ,n-offset)))
       (:big-endian
-       `(inst mov ,n-target
+       `(inst movzx ,n-target
               (make-ea :byte :base ,n-source
                              :disp (+ ,n-offset (1- n-word-bytes))))))))
 \f
 ;;; node-var then it is used to make an appropriate speed vs size
 ;;; decision.
 
-(defun allocation-dynamic-extent (alloc-tn size)
+(defun allocation-dynamic-extent (alloc-tn size lowtag)
   (inst sub rsp-tn size)
   ;; see comment in x86/macros.lisp implementation of this
   (inst and rsp-tn #.(lognot lowtag-mask))
   (aver (not (location= alloc-tn rsp-tn)))
-  (inst mov alloc-tn rsp-tn)
+  (inst lea alloc-tn (make-ea :byte :base rsp-tn :disp lowtag))
   (values))
 
 ;;; This macro should only be used inside a pseudo-atomic section,
 ;;; which should also cover subsequent initialization of the
 ;;; object.
-(defun allocation-tramp (alloc-tn size &optional ignored)
-  (declare (ignore ignored))
+(defun allocation-tramp (alloc-tn size lowtag)
   (inst push size)
   (inst lea temp-reg-tn (make-ea :qword
                             :disp (make-fixup "alloc_tramp" :foreign)))
   (inst call temp-reg-tn)
   (inst pop alloc-tn)
+  (when lowtag
+    (inst lea alloc-tn (make-ea :byte :base alloc-tn :disp lowtag)))
   (values))
 
-(defun allocation (alloc-tn size &optional ignored dynamic-extent)
+(defun allocation (alloc-tn size &optional ignored dynamic-extent lowtag)
   (declare (ignore ignored))
   (when dynamic-extent
-    (allocation-dynamic-extent alloc-tn size)
+    (allocation-dynamic-extent alloc-tn size lowtag)
     (return-from allocation (values)))
   (let ((NOT-INLINE (gen-label))
         (DONE (gen-label))
                   :scale 1 :disp
                   (make-fixup "boxed_region" :foreign 8))))
     (cond (in-elsewhere
-           (allocation-tramp alloc-tn size))
+           (allocation-tramp alloc-tn size lowtag))
           (t
            (inst mov temp-reg-tn free-pointer)
            (if (tn-p size)
            (inst cmp end-addr alloc-tn)
            (inst jmp :be NOT-INLINE)
            (inst mov free-pointer alloc-tn)
-           (inst mov alloc-tn temp-reg-tn)
+           (if lowtag
+               (inst lea alloc-tn (make-ea :byte :base temp-reg-tn :disp lowtag))
+               (inst mov alloc-tn temp-reg-tn))
            (emit-label DONE)
            (assemble (*elsewhere*)
              (emit-label NOT-INLINE)
              (cond ((numberp size)
-                    (allocation-tramp alloc-tn size))
+                    (allocation-tramp alloc-tn size lowtag))
                    (t
                     (inst sub alloc-tn free-pointer)
-                    (allocation-tramp alloc-tn alloc-tn)))
-             (inst jmp DONE))
-           (values)))))
-
-#+nil
-(defun allocation (alloc-tn size &optional ignored)
-  (declare (ignore ignored))
-  (inst push size)
-  (inst lea temp-reg-tn (make-ea :qword
-                            :disp (make-fixup "alloc_tramp" :foreign)))
-  (inst call temp-reg-tn)
-  (inst pop alloc-tn)
-  (values))
+                    (allocation-tramp alloc-tn alloc-tn lowtag)))
+             (inst jmp DONE))))
+    (values)))
 
 ;;; Allocate an other-pointer object of fixed SIZE with a single word
 ;;; header having the specified WIDETAG value. The result is placed in
     (bug "empty &body in WITH-FIXED-ALLOCATION"))
   (once-only ((result-tn result-tn) (size size) (stack-allocate-p stack-allocate-p))
     `(maybe-pseudo-atomic ,stack-allocate-p
-      (allocation ,result-tn (pad-data-block ,size) ,inline ,stack-allocate-p)
+      (allocation ,result-tn (pad-data-block ,size) ,inline ,stack-allocate-p
+                  other-pointer-lowtag)
       (storew (logior (ash (1- ,size) n-widetag-bits) ,widetag)
-              ,result-tn)
-      (inst lea ,result-tn
-            (make-ea :qword :base ,result-tn :disp other-pointer-lowtag))
+              ,result-tn 0 other-pointer-lowtag)
       ,@forms)))
 \f
 ;;;; error code
-(eval-when (#-sb-xc :compile-toplevel :load-toplevel :execute)
-  (defun emit-error-break (vop kind code values)
-    (let ((vector (gensym)))
-      `((progn
-          #!-darwin (inst int 3)                  ; i386 breakpoint instruction
-          ;; On Darwin, we need to use #x0b0f instead of int3 in order
-          ;; to generate a SIGILL instead of a SIGTRAP as darwin/x86
-          ;; doesn't seem to be reliably firing SIGTRAP
-          ;; handlers. Hopefully this will be fixed by Apple at a
-          ;; later date. Do the same on x86-64 as we do on x86 until this gets
-          ;; sorted out.
-          #!+darwin (inst word #x0b0f))
-
-        ;; The return PC points here; note the location for the debugger.
-        (let ((vop ,vop))
-          (when vop
-                (note-this-location vop :internal-error)))
-        (inst byte ,kind)                       ; eg trap_Xyyy
-        (with-adjustable-vector (,vector)       ; interr arguments
-          (write-var-integer (error-number-or-lose ',code) ,vector)
-          ,@(mapcar (lambda (tn)
-                      `(let ((tn ,tn))
-                         ;; classic CMU CL comment:
-                         ;;   zzzzz jrd here. tn-offset is zero for constant
-                         ;;   tns.
-                         (write-var-integer (make-sc-offset (sc-number
-                                                             (tn-sc tn))
-                                                            (or (tn-offset tn)
-                                                                0))
-                                            ,vector)))
-                    values)
-          (inst byte (length ,vector))
-          (dotimes (i (length ,vector))
-            (inst byte (aref ,vector i))))))))
-
-(defmacro error-call (vop error-code &rest values)
+(defun emit-error-break (vop kind code values)
+  (assemble ()
+    #!-ud2-breakpoints
+    (inst int 3)                  ; i386 breakpoint instruction
+    ;; On Darwin, we need to use #x0b0f instead of int3 in order
+    ;; to generate a SIGILL instead of a SIGTRAP as darwin/x86
+    ;; doesn't seem to be reliably firing SIGTRAP
+    ;; handlers. Hopefully this will be fixed by Apple at a
+    ;; later date. Do the same on x86-64 as we do on x86 until this gets
+    ;; sorted out.
+    #!+ud2-breakpoints
+    (inst word #x0b0f)
+    ;; The return PC points here; note the location for the debugger.
+    (when vop
+      (note-this-location vop :internal-error))
+    (inst byte kind)                       ; eg trap_Xyyy
+    (with-adjustable-vector (vector)       ; interr arguments
+      (write-var-integer code vector)
+      (dolist (tn values)
+        ;; classic CMU CL comment:
+        ;;   zzzzz jrd here. tn-offset is zero for constant
+        ;;   tns.
+        (write-var-integer (make-sc-offset (sc-number (tn-sc tn))
+                                           (or (tn-offset tn) 0))
+                           vector))
+      (inst byte (length vector))
+      (dotimes (i (length vector))
+        (inst byte (aref vector i))))))
+
+(defun error-call (vop error-code &rest values)
   #!+sb-doc
   "Cause an error. ERROR-CODE is the error to cause."
-  (cons 'progn
-        (emit-error-break vop error-trap error-code values)))
+  (emit-error-break vop error-trap (error-number-or-lose error-code) values))
 
-(defmacro generate-error-code (vop error-code &rest values)
+(defun generate-error-code (vop error-code &rest values)
   #!+sb-doc
   "Generate-Error-Code Error-code Value*
   Emit code for an error with the specified Error-Code and context Values."
-  `(assemble (*elsewhere*)
-     (let ((start-lab (gen-label)))
-       (emit-label start-lab)
-       (error-call ,vop ,error-code ,@values)
+  (assemble (*elsewhere*)
+    (let ((start-lab (gen-label)))
+      (emit-label start-lab)
+      (emit-error-break vop error-trap (error-number-or-lose error-code) values)
        start-lab)))
 
 \f
 ;;; place and there's no logical single place to attach documentation.
 ;;; grep (mostly in src/runtime) is your friend
 
-;;; FIXME: THIS NAME IS BACKWARDS!
-(defmacro maybe-pseudo-atomic (really-p &body body)
-  `(if ,really-p
+(defmacro maybe-pseudo-atomic (not-really-p &body body)
+  `(if ,not-really-p
        (progn ,@body)
        (pseudo-atomic ,@body)))
 
+;;; Unsafely clear pa flags so that the image can properly lose in a
+;;; pa section.
+#!+sb-thread
+(defmacro %clear-pseudo-atomic ()
+  '(inst mov (make-ea :qword :base thread-base-tn
+              :disp (* n-word-bytes thread-pseudo-atomic-bits-slot))
+    0))
+
+#!+sb-safepoint
+(defun emit-safepoint ()
+  (inst test al-tn (make-ea :byte :disp sb!vm::gc-safepoint-page-addr)))
+
 #!+sb-thread
 (defmacro pseudo-atomic (&rest forms)
+  #!+sb-safepoint-strictly
+  `(progn ,@forms (emit-safepoint))
+  #!-sb-safepoint-strictly
   (with-unique-names (label)
     `(let ((,label (gen-label)))
-      (inst or (make-ea :byte
-                 :base thread-base-tn
-                 :disp (* 8 thread-pseudo-atomic-bits-slot))
-            (fixnumize 1))
-      ,@forms
-      (inst xor (make-ea :byte
-                 :base thread-base-tn
-                 :disp (* 8 thread-pseudo-atomic-bits-slot))
-            (fixnumize 1))
-      (inst jmp :z ,label)
-      ;; if PAI was set, interrupts were disabled at the same
-      ;; time using the process signal mask.
-      (inst break pending-interrupt-trap)
-      (emit-label ,label))))
+       (inst mov (make-ea :qword
+                          :base thread-base-tn
+                          :disp (* n-word-bytes thread-pseudo-atomic-bits-slot))
+             rbp-tn)
+       ,@forms
+       (inst xor (make-ea :qword
+                          :base thread-base-tn
+                          :disp (* n-word-bytes thread-pseudo-atomic-bits-slot))
+             rbp-tn)
+       (inst jmp :z ,label)
+       ;; if PAI was set, interrupts were disabled at the same time
+       ;; using the process signal mask.
+       (inst break pending-interrupt-trap)
+       (emit-label ,label)
+       #!+sb-safepoint
+       ;; In this case, when allocation thinks a GC should be done, it
+       ;; does not mark PA as interrupted, but schedules a safepoint
+       ;; trap instead.  Let's take the opportunity to trigger that
+       ;; safepoint right now.
+       (emit-safepoint))))
 
 
 #!-sb-thread
 (defmacro pseudo-atomic (&rest forms)
   (with-unique-names (label)
     `(let ((,label (gen-label)))
-      ;; FIXME: The MAKE-EA noise should become a MACROLET macro or
-      ;; something. (perhaps SVLB, for static variable low byte)
-      (inst or (make-ea :byte :disp (+ nil-value
-                                       (static-symbol-offset
-                                        '*pseudo-atomic-bits*)
-                                       (ash symbol-value-slot word-shift)
-                                       (- other-pointer-lowtag)))
-            (fixnumize 1))
-      ,@forms
-      (inst xor (make-ea :byte :disp (+ nil-value
-                                        (static-symbol-offset
-                                         '*pseudo-atomic-bits*)
-                                        (ash symbol-value-slot word-shift)
-                                        (- other-pointer-lowtag)))
-            (fixnumize 1))
-      (inst jmp :z ,label)
-      ;; if PAI was set, interrupts were disabled at the same time
-      ;; using the process signal mask.
-      (inst break pending-interrupt-trap)
-      (emit-label ,label))))
-
-
+       ;; FIXME: The MAKE-EA noise should become a MACROLET macro or
+       ;; something. (perhaps SVLB, for static variable low byte)
+       (inst mov (make-ea :qword :disp (+ nil-value
+                                          (static-symbol-offset
+                                           '*pseudo-atomic-bits*)
+                                          (ash symbol-value-slot word-shift)
+                                          (- other-pointer-lowtag)))
+             rbp-tn)
+       ,@forms
+       (inst xor (make-ea :qword :disp (+ nil-value
+                                          (static-symbol-offset
+                                           '*pseudo-atomic-bits*)
+                                          (ash symbol-value-slot word-shift)
+                                          (- other-pointer-lowtag)))
+             rbp-tn)
+       (inst jmp :z ,label)
+       ;; if PAI was set, interrupts were disabled at the same time
+       ;; using the process signal mask.
+       (inst break pending-interrupt-trap)
+       (emit-label ,label))))
 \f
 ;;;; indexed references
 
        (:result-types ,el-type)
        (:generator 5
          (move rax old-value)
-         #!+sb-thread
-         (inst lock)
          (inst cmpxchg (make-ea :qword :base object :index index
+                                :scale (ash 1 (- word-shift n-fixnum-tag-bits))
                                 :disp (- (* ,offset n-word-bytes) ,lowtag))
-               new-value)
+               new-value :lock)
          (move value rax)))))
 
 (defmacro define-full-reffer (name type offset lowtag scs el-type &optional translate)
        (:result-types ,el-type)
        (:generator 3                    ; pw was 5
          (inst mov value (make-ea :qword :base object :index index
+                                  :scale (ash 1 (- word-shift n-fixnum-tag-bits))
                                   :disp (- (* ,offset n-word-bytes)
                                            ,lowtag)))))
      (define-vop (,(symbolicate name "-C"))
        (:result-types ,el-type)
        (:generator 3                    ; pw was 5
          (inst mov value (make-ea :qword :base object :index index
+                                  :scale (ash 1 (- word-shift n-fixnum-tag-bits))
                                   :disp (- (* (+ ,offset offset) n-word-bytes)
                                            ,lowtag)))))
      (define-vop (,(symbolicate name "-C"))
        (:result-types ,el-type)
        (:generator 4                    ; was 5
          (inst mov (make-ea :qword :base object :index index
+                            :scale (ash 1 (- word-shift n-fixnum-tag-bits))
                             :disp (- (* ,offset n-word-bytes) ,lowtag))
                value)
          (move result value)))
        (:result-types ,el-type)
        (:generator 4                    ; was 5
          (inst mov (make-ea :qword :base object :index index
+                            :scale (ash 1 (- word-shift n-fixnum-tag-bits))
                             :disp (- (* (+ ,offset offset) n-word-bytes) ,lowtag))
                value)
          (move result value)))
@@ -537,10 +560,11 @@ Useful for e.g. foreign calls where another thread may trigger
 collection."
   (if objects
       (let ((pins (make-gensym-list (length objects)))
-            (wpo (block-gensym "WPO")))
+            (wpo (sb!xc:gensym "WITH-PINNED-OBJECTS-THUNK")))
         ;; BODY is stuffed in a function to preserve the lexical
         ;; environment.
         `(flet ((,wpo () (progn ,@body)))
+           (declare (muffle-conditions compiler-note))
            ;; PINS are dx-allocated in case the compiler for some
            ;; unfathomable reason decides to allocate value-cells
            ;; for them -- since we have DX value-cells on x86oid