1.0.18.2: more conservative interval artihmetic
[sbcl.git] / src / code / defstruct.lisp
index 338c09a..c757550 100644 (file)
            (error "Class is not a structure class: ~S" name))
           (t res))))
 
+(defun compiler-layout-ready-p (name)
+  (let ((layout (info :type :compiler-layout name)))
+    (and layout (typep (layout-info layout) 'defstruct-description))))
+
+(sb!xc:defmacro %make-structure-instance-macro (dd slot-specs &rest slot-vars)
+  `(truly-the ,(dd-name dd)
+              ,(if (compiler-layout-ready-p (dd-name dd))
+                   `(%make-structure-instance ,dd ,slot-specs ,@slot-vars)
+                   ;; Non-toplevel defstructs don't have a layout at compile time,
+                   ;; so we need to construct the actual function at runtime -- but
+                   ;; we cache it at the call site, so that we don't perform quite
+                   ;; so horribly.
+                   `(let* ((cell (load-time-value (list nil)))
+                           (fun (car cell)))
+                      (if (functionp fun)
+                          (funcall fun ,@slot-vars)
+                          (funcall (setf (car cell)
+                                         (%make-structure-instance-allocator ,dd ,slot-specs))
+                                   ,@slot-vars))))))
+
+(declaim (ftype (sfunction (defstruct-description list) function)
+                %Make-structure-instance-allocator))
+(defun %make-structure-instance-allocator (dd slot-specs)
+  (let ((vars (make-gensym-list (length slot-specs))))
+    (values (compile nil `(lambda (,@vars)
+                            (%make-structure-instance-macro ,dd ',slot-specs ,@vars))))))
+
 ;;; Delay looking for compiler-layout until the constructor is being
 ;;; compiled, since it doesn't exist until after the EVAL-WHEN
 ;;; (COMPILE) stuff is compiled. (Or, in the oddball case when
                   (error "Class is not a structure class: ~S" ',name))
                 ,layout))))))
 
-;;; Get layout right away.
-(sb!xc:defmacro compile-time-find-layout (name)
-  (find-layout name))
-
 ;;; re. %DELAYED-GET-COMPILER-LAYOUT and COMPILE-TIME-FIND-LAYOUT, above..
 ;;;
 ;;; FIXME: Perhaps both should be defined with DEFMACRO-MUNDANELY?
     (raw-type (missing-arg) :type (or symbol cons) :read-only t)
     ;; What operator is used to access a slot of this type?
     (accessor-name (missing-arg) :type symbol :read-only t)
+    (init-vop (missing-arg) :type symbol :read-only t)
     ;; How many words are each value of this type?
     (n-words (missing-arg) :type (and index (integer 1)) :read-only t)
     ;; Necessary alignment in units of words.  Note that instances
       (list
        (make-raw-slot-data :raw-type 'sb!vm:word
                            :accessor-name '%raw-instance-ref/word
+                           :init-vop 'sb!vm::raw-instance-init/word
                            :n-words 1)
        (make-raw-slot-data :raw-type 'single-float
                            :accessor-name '%raw-instance-ref/single
+                           :init-vop 'sb!vm::raw-instance-init/single
                            ;; KLUDGE: On 64 bit architectures, we
                            ;; could pack two SINGLE-FLOATs into the
                            ;; same word if raw slots were indexed
                            :n-words 1)
        (make-raw-slot-data :raw-type 'double-float
                            :accessor-name '%raw-instance-ref/double
+                           :init-vop 'sb!vm::raw-instance-init/double
                            :alignment double-float-alignment
                            :n-words (/ 8 sb!vm:n-word-bytes))
        (make-raw-slot-data :raw-type 'complex-single-float
                            :accessor-name '%raw-instance-ref/complex-single
+                           :init-vop 'sb!vm::raw-instance-init/complex-single
                            :n-words (/ 8 sb!vm:n-word-bytes))
        (make-raw-slot-data :raw-type 'complex-double-float
                            :accessor-name '%raw-instance-ref/complex-double
+                           :init-vop 'sb!vm::raw-instance-init/complex-double
                            :alignment double-float-alignment
                            :n-words (/ 16 sb!vm:n-word-bytes))
        #!+long-float
        (make-raw-slot-data :raw-type long-float
                            :accessor-name '%raw-instance-ref/long
+                           :init-vop 'sb!vm::raw-instance-init/long
                            :n-words #!+x86 3 #!+sparc 4)
        #!+long-float
        (make-raw-slot-data :raw-type complex-long-float
                            :accessor-name '%raw-instance-ref/complex-long
+                           :init-vop 'sb!vm::raw-instance-init/complex-long
                            :n-words #!+x86 6 #!+sparc 8)))))
 \f
 ;;;; the legendary DEFSTRUCT macro itself (both CL:DEFSTRUCT and its
           (let ((inherited (accessor-inherited-data name defstruct)))
             (cond
               ((not inherited)
-               (stuff `(declaim (inline ,name (setf ,name))))
+               (stuff `(declaim (inline ,name ,@(unless (dsd-read-only slot)
+                                                        `((setf ,name))))))
                ;; FIXME: The arguments in the next two DEFUNs should
                ;; be gensyms. (Otherwise e.g. if NEW-VALUE happened to
                ;; be the name of a special variable, things could get
            (when (and (classoid-subclasses classoid)
                       (not (eq layout old-layout)))
              (collect ((subs))
-                      (dohash (classoid layout (classoid-subclasses classoid))
-                        (declare (ignore layout))
-                        (undefine-structure classoid)
-                        (subs (classoid-proper-name classoid)))
-                      (when (subs)
-                        (warn "removing old subclasses of ~S:~%  ~S"
-                              (classoid-name classoid)
-                              (subs))))))
+               (dohash ((classoid layout) (classoid-subclasses classoid)
+                        :locked t)
+                 (declare (ignore layout))
+                 (undefine-structure classoid)
+                 (subs (classoid-proper-name classoid)))
+               (when (subs)
+                 (warn "removing old subclasses of ~S:~%  ~S"
+                       (classoid-name classoid)
+                       (subs))))))
           (t
            (unless (eq (classoid-layout classoid) layout)
              (register-layout layout :invalidate nil))
                         :destruct-layout old-layout))))
   (values))
 
+(declaim (inline dd-layout-length))
+(defun dd-layout-length (dd)
+  (+ (dd-length dd) (dd-raw-length dd)))
+
+(declaim (ftype (sfunction (defstruct-description) index) dd-instance-length))
+(defun dd-instance-length (dd)
+  ;; Make sure the object ends at a two-word boundary.  Note that this does
+  ;; not affect the amount of memory used, since the allocator would add the
+  ;; same padding anyway.  However, raw slots are indexed from the length of
+  ;; the object as indicated in the header, so the pad word needs to be
+  ;; included in that length to guarantee proper alignment of raw double float
+  ;; slots, necessary for (at least) the SPARC backend.
+  (let ((layout-length (dd-layout-length dd)))
+    (declare (index layout-length))
+    (+ layout-length (mod (1+ layout-length) 2))))
+
 ;;; This is called when we are about to define a structure class. It
 ;;; returns a (possibly new) class object and the layout which should
 ;;; be used for the new definition (may be the current layout, and
     (let ((new-layout (make-layout :classoid class
                                    :inherits inherits
                                    :depthoid (length inherits)
-                                   :length (+ (dd-length info)
-                                              (dd-raw-length info))
+                                   :length (dd-layout-length info)
                                    :n-untagged-slots (dd-raw-length info)
                                    :info info))
           (old-layout (or compiler-layout old-layout)))
     (loop for dsd in (dd-slots dd) and val in values do
       (setf (elt vals (dsd-index dsd))
             (if (eq val '.do-not-initialize-slot.) 0 val)))
-
     `(defun ,cons-name ,arglist
        (declare ,@(mapcar (lambda (var type) `(type ,type ,var)) vars types))
        (list ,@vals))))
 (defun create-structure-constructor (dd cons-name arglist vars types values)
-  (let* ((instance (gensym "INSTANCE")))
+  ;; The difference between the two implementations here is that on all
+  ;; platforms we don't have the appropriate RAW-INSTANCE-INIT VOPS, which
+  ;; must be able to deal with immediate values as well -- unlike
+  ;; RAW-INSTANCE-SET VOPs, which never end up seeing immediate values. With
+  ;; some additional cleverness we might manage without them and just a single
+  ;; implementation here, though -- figure out a way to ensure that on those
+  ;; platforms we always still get a non-immediate TN in every case...
+  ;;
+  ;; Until someone does that, this means that instances with raw slots can be
+  ;; DX allocated only on platforms with those additional VOPs.
+  #!+raw-instance-init-vops
+  (let* ((slot-values nil)
+         (slot-specs
+          (mapcan (lambda (dsd value)
+                    (unless (eq value '.do-not-initialize-slot.)
+                      (push value slot-values)
+                      (list (list* :slot (dsd-raw-type dsd) (dsd-index dsd)))))
+                  (dd-slots dd)
+                  values)))
     `(defun ,cons-name ,arglist
-       (declare ,@(mapcar (lambda (var type) `(type ,type ,var))
-                          vars types))
-       (let ((,instance (truly-the ,(dd-name dd)
-                          (%make-instance-with-layout
-                           (%delayed-get-compiler-layout ,(dd-name dd))))))
-         ,@(mapcar (lambda (dsd value)
-                     ;; (Note that we can't in general use the
-                     ;; ordinary named slot setter function here
-                     ;; because the slot might be :READ-ONLY, so we
-                     ;; whip up new LAMBDA representations of slot
-                     ;; setters for the occasion.)
-                     (unless (eq value '.do-not-initialize-slot.)
-                       `(,(slot-setter-lambda-form dd dsd) ,value ,instance)))
-                   (dd-slots dd)
-                   values)
-         ,instance))))
+       (declare ,@(mapcar (lambda (var type) `(type ,type ,var)) vars types))
+       (%make-structure-instance-macro ,dd ',slot-specs ,@(reverse slot-values))))
+  #!-raw-instance-init-vops
+  (let ((instance (gensym "INSTANCE")) slot-values slot-specs raw-slots raw-values)
+    (mapc (lambda (dsd value)
+            (unless (eq value '.do-not-initialize-slot.)
+              (let ((raw-type (dsd-raw-type dsd)))
+                (cond ((eq t raw-type)
+                       (push value slot-values)
+                       (push (list* :slot raw-type (dsd-index dsd)) slot-specs))
+                      (t
+                       (push value raw-values)
+                       (push dsd raw-slots))))))
+          (dd-slots dd)
+          values)
+    `(defun ,cons-name ,arglist
+       (declare ,@(mapcar (lambda (var type) `(type ,type ,var)) vars types))
+       ,(if raw-slots
+            `(let ((,instance (%make-structure-instance-macro ,dd ',slot-specs ,@slot-values)))
+              ,@(mapcar (lambda (dsd value)
+                          ;; (Note that we can't in general use the
+                          ;; ordinary named slot setter function here
+                          ;; because the slot might be :READ-ONLY, so we
+                          ;; whip up new LAMBDA representations of slot
+                          ;; setters for the occasion.)
+                          `(,(slot-setter-lambda-form dd dsd) ,value ,instance))
+                        raw-slots
+                        raw-values)
+              ,instance)
+            `(%make-structure-instance-macro ,dd ',slot-specs ,@slot-values)))))
 
 ;;; Create a default (non-BOA) keyword constructor.
 (defun create-keyword-constructor (defstruct creator)
     (multiple-value-bind (raw-maker-form raw-reffer-operator)
         (ecase dd-type
           (structure
-           (values `(let ((,object-gensym (%make-instance ,dd-length)))
-                      (setf (%instance-layout ,object-gensym)
-                            ,delayed-layout-form)
-                      ,object-gensym)
+           (values `(%make-structure-instance-macro ,dd nil)
                    '%instance-ref))
           (funcallable-structure
            (values `(let ((,object-gensym