Simpler and more precise type derivation for APPEND/NCONC
[sbcl.git] / src / compiler / mips / call.lisp
index 8cb79e2..b10e80e 100644 (file)
@@ -35,7 +35,7 @@
       (make-restricted-tn *backend-t-primitive-type* register-arg-scn)))
 
 ;;; This is similar to MAKE-RETURN-PC-PASSING-LOCATION, but makes a
-;;; location to pass Old-FP in. This is (obviously) wired in the
+;;; location to pass OLD-FP in. This is (obviously) wired in the
 ;;; standard convention, but is totally unrestricted in non-standard
 ;;; conventions, since we can always fetch it off of the stack using
 ;;; the arg pointer.
@@ -53,6 +53,7 @@
    (make-wired-tn *fixnum-primitive-type*
                   control-stack-arg-scn
                   ocfp-save-offset)))
+
 (!def-vm-support-routine make-return-pc-save-location (env)
   (let ((ptype *backend-t-primitive-type*))
     (specify-save-tn
       (when nfp
         (inst addu val nfp (bytes-needed-for-non-descriptor-stack-frame))))))
 
+;;; Accessing a slot from an earlier stack frame is definite hackery.
+(define-vop (ancestor-frame-ref)
+  (:args (frame-pointer :scs (descriptor-reg))
+         (variable-home-tn :load-if nil))
+  (:results (value :scs (descriptor-reg any-reg)))
+  (:policy :fast-safe)
+  (:generator 4
+    (aver (sc-is variable-home-tn control-stack))
+    (loadw value frame-pointer (tn-offset variable-home-tn))))
+(define-vop (ancestor-frame-set)
+  (:args (frame-pointer :scs (descriptor-reg))
+         (value :scs (descriptor-reg any-reg)))
+  (:results (variable-home-tn :load-if nil))
+  (:policy :fast-safe)
+  (:generator 4
+    (aver (sc-is variable-home-tn control-stack))
+    (storew value frame-pointer (tn-offset variable-home-tn))))
 
 (define-vop (xep-allocate-frame)
   (:info start-lab copy-more-arg-follows)
   (:generator 1
     ;; Make sure the function is aligned, and drop a label pointing to this
     ;; function header.
-    (align n-lowtag-bits)
+    (emit-alignment n-lowtag-bits)
     (trace-table-entry trace-table-fun-prologue)
     (emit-label start-lab)
     ;; Allocate function header.
-    (inst fun-header-word)
+    (inst simple-fun-header-word)
     (dotimes (i (1- simple-fun-code-offset))
       (inst word 0))
     ;; The start of the actual code.
     ;; Compute CODE from the address of this entry point.
     (let ((entry-point (gen-label)))
       (emit-label entry-point)
-      (inst compute-code-from-fn code-tn lip-tn entry-point temp)
+      (inst compute-code-from-lip code-tn lip-tn entry-point temp)
       ;; ### We should also save it on the stack so that the garbage collector
       ;; won't forget about us if we call anyone else.
       )
 ;;; In the general case, we have to do three things:
 ;;;  -- Default unsupplied register values.  This need only be done when a
 ;;;     single value is returned, since register values are defaulted by the
-;;;     called in the non-single case.
+;;;     callee in the non-single case.
 ;;;  -- Default unsupplied stack values.  This needs to be done whenever there
 ;;;     are stack values.
 ;;;  -- Reset SP.  This must be done whenever other than 1 value is returned,
@@ -240,7 +258,7 @@ regs-defaulted
         ...
 
 defaulting-done
-        move sp ocfp                    ; Reset SP.
+        move csp ocfp                    ; Reset SP.
 <end of code>
 
 <elsewhere>
@@ -267,7 +285,7 @@ default-value-8
         ;; gets confused.
         (without-scheduling ()
           (note-this-location vop :single-value-return)
-          (inst move csp-tn ocfp-tn)
+          (move csp-tn ocfp-tn t)
           (inst nop))
         (when lra-label
           (inst compute-code-from-lra code-tn code-tn lra-label temp)))
@@ -284,7 +302,7 @@ default-value-8
               (inst addu temp nargs-tn (fixnumize (- register-arg-count)))
               (move csp-tn ocfp-tn t)))
 
-        ;; Do the single value calse.
+        ;; Do the single value case.
         (do ((i 1 (1+ i))
              (val (tn-ref-across values) (tn-ref-across val)))
             ((= i (min nvals register-arg-count)))
@@ -322,13 +340,15 @@ default-value-8
               (aver defaults)
               (assemble (*elsewhere*)
                 (emit-label default-stack-vals)
+                (trace-table-entry trace-table-fun-prologue)
                 (do ((remaining defaults (cdr remaining)))
                     ((null remaining))
                   (let ((def (car remaining)))
                     (emit-label (car def))
                     (when (null (cdr remaining))
                       (inst b defaulting-done))
-                    (store-stack-tn (cdr def) null-tn)))))))
+                    (store-stack-tn (cdr def) null-tn)))
+                (trace-table-entry trace-table-normal)))))
 
         (when lra-label
           (inst compute-code-from-lra code-tn code-tn lra-label temp))))
@@ -364,24 +384,26 @@ default-value-8
 
     (when lra-label
       (inst compute-code-from-lra code-tn code-tn lra-label temp))
-    (inst addu csp-tn csp-tn 4)
-    (storew (first register-arg-tns) csp-tn -1)
-    (inst addu start csp-tn -4)
+    (inst addu csp-tn csp-tn n-word-bytes)
+    (storew (first *register-arg-tns*) csp-tn -1)
+    (inst addu start csp-tn (- n-word-bytes))
     (inst li count (fixnumize 1))
 
     (emit-label done)
 
     (assemble (*elsewhere*)
+      (trace-table-entry trace-table-fun-prologue)
       (emit-label variable-values)
       (when lra-label
         (inst compute-code-from-lra code-tn code-tn lra-label temp))
-      (do ((arg register-arg-tns (rest arg))
+      (do ((arg *register-arg-tns* (rest arg))
            (i 0 (1+ i)))
           ((null arg))
         (storew (first arg) args i))
       (move start args)
       (inst b done)
-      (move count nargs t)))
+      (move count nargs t)
+      (trace-table-entry trace-table-normal)))
   (values))
 
 
@@ -400,6 +422,15 @@ default-value-8
               nvals)
   (:temporary (:scs (non-descriptor-reg)) temp))
 
+\f
+;;; This hook in the codegen pass lets us insert code before fall-thru entry
+;;; points, local-call entry points, and tail-call entry points.  The default
+;;; does nothing.
+(defun emit-block-header (start-label trampoline-label fall-thru-p alignp)
+  (declare (ignore fall-thru-p alignp))
+  (when trampoline-label
+    (emit-label trampoline-label))
+  (emit-label start-label))
 
 \f
 ;;;; Local call with unknown values convention return:
@@ -476,6 +507,7 @@ default-value-8
   (:ignore args save)
   (:vop-var vop)
   (:temporary (:sc control-stack :offset nfp-save-offset) nfp-save)
+  (:temporary (:scs (non-descriptor-reg)) temp)
   (:generator 20
     (let ((label (gen-label))
           (cur-nfp (current-nfp-tn vop)))
@@ -646,7 +678,8 @@ default-value-8
      (:vop-var vop)
      (:info ,@(unless (or variable (eq return :tail)) '(arg-locs))
             ,@(unless variable '(nargs))
-            ,@(when (eq return :fixed) '(nvals)))
+            ,@(when (eq return :fixed) '(nvals))
+            step-instrumenting)
 
      (:ignore ,@(unless (or variable (eq return :tail)) '(arg-locs))
               ,@(unless variable '(args)))
@@ -690,11 +723,13 @@ default-value-8
      ,@(when (eq return :fixed)
          '((:temporary (:scs (descriptor-reg) :from :eval) move-temp)))
 
+     (:temporary (:scs (descriptor-reg) :to :eval) stepping)
+
      ,@(unless (eq return :tail)
          '((:temporary (:scs (non-descriptor-reg)) temp)
            (:temporary (:sc control-stack :offset nfp-save-offset) nfp-save)))
 
-     (:temporary (:sc interior-reg :offset lip-offset) entry-point)
+     (:temporary (:sc interior-reg) entry-point)
 
      (:generator ,(+ (if named 5 0)
                      (if variable 19 1)
@@ -704,6 +739,7 @@ default-value-8
        (let* ((cur-nfp (current-nfp-tn vop))
               ,@(unless (eq return :tail)
                   '((lra-label (gen-label))))
+              (step-done-label (gen-label))
               (filler
                (remove nil
                        (list :load-nargs
@@ -769,7 +805,24 @@ default-value-8
                                          (move cfp-tn csp-tn)))
                                (trace-table-entry trace-table-call-site))))
                       ((nil)
-                       (inst nop))))))
+                       (inst nop)))))
+                (insert-step-instrumenting (callable-tn)
+                  ;; Conditionally insert a conditional trap:
+                  (when step-instrumenting
+                    (load-symbol-value stepping sb!impl::*stepping*)
+                    ;; If it's not NIL, trap.
+                    (inst beq stepping null-tn step-done-label)
+                    (inst nop)
+                    ;; CONTEXT-PC will be pointing here when the
+                    ;; interrupt is handled, not after the BREAK.
+                    (note-this-location vop :step-before-vop)
+                    ;; Construct a trap code with the low bits from
+                    ;; SINGLE-STEP-AROUND-TRAP and the high bits from
+                    ;; the register number of CALLABLE-TN.
+                    (inst break 0 (logior single-step-around-trap
+                                          (ash (reg-tn-encoding callable-tn)
+                                               5)))
+                    (emit-label step-done-label))))
 
            ,@(if named
                  `((sc-case name
@@ -783,6 +836,10 @@ default-value-8
                             (- (ash (tn-offset name) word-shift)
                                other-pointer-lowtag))
                       (do-next-filler)))
+                   ;; The step instrumenting must be done after
+                   ;; FUNCTION is loaded, but before ENTRY-POINT is
+                   ;; calculated.
+                   (insert-step-instrumenting name-pass)
                    (inst lw entry-point name-pass
                          (- (ash fdefn-raw-addr-slot word-shift)
                             other-pointer-lowtag))
@@ -802,6 +859,10 @@ default-value-8
                          (- (ash closure-fun-slot word-shift)
                             fun-pointer-lowtag))
                    (do-next-filler)
+                   ;; The step instrumenting must be done before
+                   ;; after FUNCTION is loaded, but before ENTRY-POINT
+                   ;; is calculated.
+                   (insert-step-instrumenting function)
                    (inst addu entry-point function
                          (- (ash simple-fun-code-offset word-shift)
                             fun-pointer-lowtag))))
@@ -942,19 +1003,26 @@ default-value-8
       (when cur-nfp
         (inst addu nsp-tn cur-nfp
               (bytes-needed-for-non-descriptor-stack-frame))))
-    ;; Establish the values pointer and values count.
-    (move val-ptr cfp-tn)
-    (inst li nargs (fixnumize nvals))
-    ;; restore the frame pointer and clear as much of the control
-    ;; stack as possible.
-    (move cfp-tn ocfp)
-    (inst addu csp-tn val-ptr (* nvals n-word-bytes))
-    ;; pre-default any argument register that need it.
-    (when (< nvals register-arg-count)
-      (dolist (reg (subseq (list a0 a1 a2 a3 a4 a5) nvals))
-        (move reg null-tn)))
-    ;; And away we go.
-    (lisp-return return-pc lip)
+    (cond ((= nvals 1)
+           ;; Clear the control stack, and restore the frame pointer.
+           (move csp-tn cfp-tn)
+           (move cfp-tn ocfp)
+           ;; Out of here.
+           (lisp-return return-pc lip :offset 2))
+          (t
+           ;; Establish the values pointer and values count.
+           (move val-ptr cfp-tn)
+           (inst li nargs (fixnumize nvals))
+           ;; restore the frame pointer and clear as much of the control
+           ;; stack as possible.
+           (move cfp-tn ocfp)
+           (inst addu csp-tn val-ptr (* nvals n-word-bytes))
+           ;; pre-default any argument register that need it.
+           (when (< nvals register-arg-count)
+             (dolist (reg (subseq (list a0 a1 a2 a3 a4 a5) nvals))
+               (move reg null-tn)))
+           ;; And away we go.
+           (lisp-return return-pc lip)))
     (trace-table-entry trace-table-normal)))
 
 ;;; Do unknown-values return of an arbitrary number of values (passed on the
@@ -1091,20 +1159,18 @@ default-value-8
           ;; Is this the last one?
           (inst beq count done)
           ;; Store it relative to the pointer saved at the start.
-          (storew (nth i register-arg-tns) result (- i fixed))
+          (storew (nth i *register-arg-tns*) result (- i fixed))
           ;; Decrement count.
           (inst subu count (fixnumize 1))))
       (emit-label done))))
 
 
-;;; More args are stored consequtively on the stack, starting immediately at
-;;; the context pointer.  The context pointer is not typed, so the lowtag is 0.
-;;;
+;;; More args are stored consecutively on the stack, starting
+;;; immediately at the context pointer.  The context pointer is not
+;;; typed, so the lowtag is 0.
 (define-full-reffer more-arg * 0 0 (descriptor-reg any-reg) * %more-arg)
 
-
 ;;; Turn more arg (context, count) into a list.
-;;;
 (define-vop (listify-rest-args)
   (:args (context-arg :target context :scs (descriptor-reg))
          (count-arg :target count :scs (any-reg)))
@@ -1116,24 +1182,31 @@ default-value-8
   (:results (result :scs (descriptor-reg)))
   (:translate %listify-rest-args)
   (:policy :safe)
+  (:node-var node)
   (:generator 20
-    (let ((enter (gen-label))
-          (loop (gen-label))
-          (done (gen-label)))
+    (let* ((enter (gen-label))
+           (loop (gen-label))
+           (done (gen-label))
+           (dx-p (node-stack-allocate-p node))
+           (alloc-area-tn (if dx-p csp-tn alloc-tn)))
       (move context context-arg)
       (move count count-arg)
       ;; Check to see if there are any arguments.
-      (inst beq count zero-tn done)
+      (inst beq count done)
       (move result null-tn t)
 
       ;; We need to do this atomically.
       (pseudo-atomic (pa-flag)
+        (when dx-p
+          (align-csp temp))
         ;; Allocate a cons (2 words) for each item.
-        (inst or result alloc-tn list-pointer-lowtag)
+        (inst srl result alloc-area-tn n-lowtag-bits)
+        (inst sll result n-lowtag-bits)
+        (inst or result list-pointer-lowtag)
         (move dst result)
         (inst sll temp count 1)
         (inst b enter)
-        (inst addu alloc-tn alloc-tn temp)
+        (inst addu alloc-area-tn temp)
 
         ;; Store the current cons in the cdr of the previous cons.
         (emit-label loop)
@@ -1143,11 +1216,11 @@ default-value-8
         (emit-label enter)
         ;; Grab one value.
         (loadw temp context)
-        (inst addu context context n-word-bytes)
+        (inst addu context n-word-bytes)
 
         ;; Dec count, and if != zero, go back for more.
         (inst addu count count (fixnumize -1))
-        (inst bne count zero-tn loop)
+        (inst bne count loop)
 
         ;; Store the value in the car (in delay slot)
         (storew temp dst 0 list-pointer-lowtag)
@@ -1196,7 +1269,7 @@ default-value-8
     (let ((err-lab
            (generate-error-code vop invalid-arg-count-error nargs)))
       (cond ((zerop count)
-             (inst bne nargs zero-tn err-lab)
+             (inst bne nargs err-lab)
              (inst nop))
             (t
              (inst li temp (fixnumize count))
@@ -1228,3 +1301,22 @@ default-value-8
   (frob unknown-key-arg-error unknown-key-arg-error
     sb!c::%unknown-key-arg-error key)
   (frob nil-fun-returned-error nil-fun-returned-error nil fun))
+
+;;; Single-stepping
+
+(define-vop (step-instrument-before-vop)
+  (:temporary (:scs (descriptor-reg)) stepping)
+  (:policy :fast-safe)
+  (:vop-var vop)
+  (:generator 3
+    (load-symbol-value stepping sb!impl::*stepping*)
+    ;; If it's not NIL, trap.
+    (inst beq stepping null-tn DONE)
+    (inst nop)
+    ;; CONTEXT-PC will be pointing here when the interrupt is handled,
+    ;; not after the BREAK.
+    (note-this-location vop :step-before-vop)
+    ;; CALLEE-REGISTER-OFFSET isn't needed for before-traps, so we
+    ;; can just use a bare SINGLE-STEP-BEFORE-TRAP as the code.
+    (inst break 0 single-step-before-trap)
+    DONE))