Signal floating-point-overflow from bignum-to-float.
[sbcl.git] / tests / gc.impure-cload.lisp
blob57cbf9c33a23b53c36b58f1abc3ad6e4fc537c52
1 ;;;; more gc tests
3 ;;;; This software is part of the SBCL system. See the README file for
4 ;;;; more information.
5 ;;;;
6 ;;;; While most of SBCL is derived from the CMU CL system, the test
7 ;;;; files (like this one) were written from scratch after the fork
8 ;;;; from CMU CL.
9 ;;;
10 ;;;; This software is in the public domain and is provided with
11 ;;;; absoluely no warranty. See the COPYING and CREDITS files for
12 ;;;; more information.
15 (defconstant min-code-header-bytes
16 (let ((min-header-words (* 2 (ceiling sb-vm:code-constants-offset 2))))
17 (* min-header-words sb-vm:n-word-bytes)))
19 ;;; A newly opened region must not start on a page that has 0 bytes available.
20 ;;; The effect of that was to cause start_addr to be the next page's address,
21 ;;; where the OPEN_REGION_PAGE_FLAG was already set on each page in the region
22 ;;; including the one that was completely full. This caused a failure when
23 ;;; closing the region because find_page_index(start_addr) was not the *first*
24 ;;; page on which the open flag should be removed.
25 ;;; Strangely, the assertion that caught this was far removed from the
26 ;;; point of failure, in conservative_root_p()
27 #+gencgc
28 (with-test (:name :gc-region-pickup :skipped-on (not (or :x86 :x86-64)))
29 (flet ((allocate-code-bytes (nbytes)
30 ;; Make a code component occupying exactly NBYTES bytes in total.
31 (assert (zerop (mod nbytes (* 2 sb-vm:n-word-bytes))))
32 (assert (>= nbytes min-code-header-bytes))
33 (sb-c::allocate-code-object nil 0 (- nbytes min-code-header-bytes)))
34 (get-code-region (a)
35 (declare (type (simple-array sb-ext:word (4)) a))
36 ;; Return array of 4: free-ptr, end-addr, last-page, start-addr
37 (dotimes (i 4 a)
38 (setf (aref a i)
39 (deref (extern-alien "gc_alloc_region" (array unsigned 12))
40 ;; code region is the third region in the GC global
41 ;; regions. Each region is described by 4 words.
42 (+ 8 i))))))
43 (let ((a (make-array 4 :element-type 'sb-ext:word))
44 (code-size
45 #+64-bit 10240
46 #-64-bit (- (* 4 sb-vm:gencgc-card-bytes) (* 2 sb-vm:n-word-bytes)))
47 (saved-region-start)
48 (saved-region-end))
49 (symbol-macrolet ((free-ptr (aref a 0))
50 (end-addr (aref a 1))
51 (last-page (aref a 2))
52 (start-addr (aref a 3)))
53 (gc) ; This will leave the code region in a closed state
54 (get-code-region a)
55 (assert (= free-ptr end-addr))
56 ;; Allocate a teency amount to start a new region
57 (sb-c::allocate-code-object nil 0 0)
58 (get-code-region a)
59 (setq saved-region-start start-addr
60 saved-region-end end-addr)
61 (multiple-value-bind (n-chunks remainder)
62 (floor (- end-addr free-ptr) code-size)
63 ;; (Maybe) use up a few bytes more so that the larger objects
64 ;; exactly consume the entirety of the region.
65 (when (plusp remainder)
66 (allocate-code-bytes (max remainder min-code-header-bytes))
67 (get-code-region a)
68 (multiple-value-setq (n-chunks remainder)
69 (floor (- end-addr free-ptr) code-size)))
70 (when (plusp remainder)
71 ;; This happens only if the MAX expression above bumped the
72 ;; remainder up, so now there is a different remainder.
73 (allocate-code-bytes remainder))
74 (dotimes (i (1- n-chunks))
75 (allocate-code-bytes code-size))
76 ;; Now make two more objects, one consuming almost the entirety
77 ;; of the region, and one touching just the final page.
78 (allocate-code-bytes (- code-size 128))
79 (let ((c (allocate-code-bytes 128)))
80 (get-code-region a)
81 ;; The region should be the same region we started with,
82 ;; not a new one.
83 (assert (= start-addr saved-region-start))
84 (assert (= end-addr saved-region-end))
85 ;; It should be totally full
86 (assert (= free-ptr end-addr))
87 ;; Create an object to open a new region where the last one
88 ;; ended. It should start on the next completely empty page,
89 ;; not the prior totally full page.
90 (allocate-code-bytes 128)
91 ;; This GC failed
92 (gc)
93 ;; Return C (so that it has to be kept live on the stack).
94 c))))))
96 ;;; This test pertains only to the compact-instance-header feature.
97 #-compact-instance-header (exit :code 104)
99 ;;; Everything from here down to the WITH-TEST is the setup to try
100 ;;; to hit "implausible layout" in verify_gc() which would occur
101 ;;; prior to the associated fix in update_page_write_prot().
102 ;;; GC has to use care if the sole pointer to a layout is the header
103 ;;; of an obsolete instance, so not to miss any old -> young pointers
104 ;;; where the layout pointer is in the high half of the header word.
105 (setf (extern-alien "verify_gens" char) 0)
107 (defstruct foo)
109 (defun change-foo-layout (myfoo)
110 ;; Slam a new layout (a copy of the existing one) into a FOO
111 ;; The new layout will be *younger* than the FOO itself,
112 ;; which is exactly the situation that tickled the GC bug.
113 (flet ((copy-layout (layout)
114 ;; don't just COPY-STRUCTURE - that would place it in dynamic space
115 (let ((new-layout
116 (sb-kernel:make-layout
117 :classoid (sb-kernel:layout-classoid layout))))
118 (sb-kernel:%byte-blt
119 (sb-sys:int-sap
120 (- (sb-kernel:get-lisp-obj-address layout)
121 sb-vm:instance-pointer-lowtag))
122 sb-vm:n-word-bytes ; do not copy the header!
123 (sb-sys:int-sap
124 (- (sb-kernel:get-lisp-obj-address new-layout)
125 sb-vm:instance-pointer-lowtag))
126 sb-vm:n-word-bytes ; correspondingly with above
127 (* (1+ (sb-kernel:%instance-length layout))
128 sb-vm:n-word-bytes))
129 new-layout)))
130 (setf (sb-kernel:%instance-layout myfoo)
131 (copy-layout (sb-kernel:%instance-layout myfoo)))
132 nil))
134 (defconstant n-conses-per-page
135 (/ sb-vm:gencgc-card-bytes (* 2 sb-vm:n-word-bytes)))
137 (defparameter *junk*
138 (make-array (1+ (* 2 n-conses-per-page))))
140 ;;; In order to get a FOO on a page by itself, we pad the page
141 ;;; with conses of immediate values, before and after.
142 ;;; So it doesn't matter where the FOO is, but there won't be
143 ;;; other objects with pointers in them (the conses don't have pointers).
144 ;;; And we point at all those conses from a single vector
145 ;;; so that scavenging is forced to linearize them onto one page,
146 ;;; along with the FOO, after discovering that the vector is live.
147 (defun filljunk (n)
148 (let ((j -1))
149 (dotimes (i n)
150 (setf (svref *junk* (incf j)) (cons 1 2)))
151 (setf (svref *junk* (incf j)) (make-foo))
152 (dotimes (i n)
153 (setf (svref *junk* (incf j)) (cons 1 2)))))
155 (filljunk n-conses-per-page)
157 ;; Promote *junk* and all its referenced objects into generation 1
158 ;; With luck, they should be in order, and the page on which the FOO
159 ;; resides should have some leading and trailing conses.
160 ;; (The conses ensure that nothing else is on the page impeding
161 ;; validity of the test)
162 (gc :gen 1)
163 (assert (= (sb-kernel:generation-of *junk*) 1))
165 ;;; This test is very contrived, but this bug was observed in real life,
166 ;;; having something to do with SB-PCL::CHECK-WRAPPER-VALIDITY.
167 (with-test (:name :gc-anonymous-layout)
168 ;;; The page on which the FOO instance resides should be WPed
169 ;;; and should have nothing else but conses on it.
170 (let* ((foo (svref *junk* n-conses-per-page))
171 (page (sb-vm::find-page-index (sb-kernel:get-lisp-obj-address foo)))
172 (gen (slot (deref sb-vm::page-table page) 'sb-vm::gen))
173 (flags (slot (deref sb-vm::page-table page) 'sb-vm::flags))
174 (wp (logbitp 3 flags))
175 (page-addr (+ sb-vm:dynamic-space-start
176 (* sb-vm:gencgc-card-bytes page)))
177 (aok t))
178 (declare (ignorable gen wp))
179 ; (format t "~&page ~d: gen=~d flags=~b (wp=~a)~%" page gen flags wp))
180 ;; Check that the page holding the FOO has those conses and nothing else
181 (sb-vm::map-objects-in-range
182 (lambda (obj type size)
183 (declare (ignore type size))
184 (unless (typep obj '(or (cons (eql 1) (eql 2)) foo))
185 (setq aok nil)))
186 (sb-kernel:%make-lisp-obj page-addr)
187 (sb-kernel:%make-lisp-obj (+ page-addr sb-vm:gencgc-card-bytes)))
188 (assert aok) ; page should have nothing but a foo and the conses
190 (change-foo-layout foo)
192 ;; Assert that we didn't accidentally copy the header word of the layout,
193 ;; which would place it in generation 1 (and probably break other parts of GC)
194 (assert (= (sb-kernel:generation-of (sb-kernel:%instance-layout foo)) 0))
196 ;; And the page with FOO must have gotten touched
197 (assert (not (logbitp 3 (slot (deref sb-vm::page-table page) 'sb-vm::flags))))
199 ;; It requires *two* GCs, not one, to cause this bug.
200 ;; The first GC sees that the page with the FOO on it was touched,
201 ;; and so GC is required to scavenge the whole page.
202 ;; This scavenge pass enlivens the wonky layout that we created,
203 ;; but it INCORRECTLY would re-protect the page, because there did
204 ;; not seem to be any pointer to younger. (The conses are immediates,
205 ;; the FOO has no slots, and its compact header was opaque)
206 (gc)
207 #+nil
208 (format t "~&page ~d: wp=~a~%"
209 page
210 (logbitp 3 (slot (deref sb-vm::page-table page) 'sb-vm::flags)))
212 ;; This GC would fail in the verify step because it trashes the apparently
213 ;; orphaned layout, which actually does have a referer.
214 (gc)))