mirror of
git://git.code.sf.net/p/sbcl/sbcl
synced 2026-09-10 07:26:40 -04:00
New yieldpoints for SBCL25 dev meeting
Add "--with-yieldpoints --with-ultrafutex" to test-drive this patch.
This commit is contained in:
parent
c111da51e1
commit
a1fcc8b129
|
|
@ -146,7 +146,7 @@
|
||||||
;; to receive an interrupt causing it to do a slow operation between
|
;; to receive an interrupt causing it to do a slow operation between
|
||||||
;; acquisition and release of the spinlock. Preventing GC is irrelevant,
|
;; acquisition and release of the spinlock. Preventing GC is irrelevant,
|
||||||
;; but would not be if we recycled tls indices of garbage symbols.
|
;; but would not be if we recycled tls indices of garbage symbols.
|
||||||
(pseudo-atomic ()
|
(pseudo-atomic () ; allocating a TLS index
|
||||||
RETRY
|
RETRY
|
||||||
(inst bts :qword :lock free-tls-index-ea lock-bit)
|
(inst bts :qword :lock free-tls-index-ea lock-bit)
|
||||||
(inst jmp :nc got-tls-index-lock)
|
(inst jmp :nc got-tls-index-lock)
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@
|
||||||
(let ((header (logior (ash 1 n-widetag-bits) bignum-widetag))
|
(let ((header (logior (ash 1 n-widetag-bits) bignum-widetag))
|
||||||
(nbytes #+bignum-assertions 32 #-bignum-assertions 16))
|
(nbytes #+bignum-assertions 32 #-bignum-assertions 16))
|
||||||
(instrument-alloc bignum-widetag nbytes nil alloc-tn)
|
(instrument-alloc bignum-widetag nbytes nil alloc-tn)
|
||||||
(pseudo-atomic ()
|
(with-allocator ()
|
||||||
(allocation bignum-widetag nbytes 0 alloc-tn nil nil nil)
|
(allocation bignum-widetag nbytes 0 alloc-tn nil nil nil)
|
||||||
(storew* header alloc-tn 0 0 t)
|
(storew* header alloc-tn 0 0 t)
|
||||||
(storew source alloc-tn bignum-digits-offset 0)
|
(storew source alloc-tn bignum-digits-offset 0)
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@
|
||||||
(:temp temp unsigned-reg r8-offset)
|
(:temp temp unsigned-reg r8-offset)
|
||||||
(:temp loop-index unsigned-reg r9-offset))
|
(:temp loop-index unsigned-reg r9-offset))
|
||||||
|
|
||||||
|
(emit-safepoint)
|
||||||
;; Pick off the cases where everything fits in register args.
|
;; Pick off the cases where everything fits in register args.
|
||||||
(inst cmp :dword rcx (fixnumize 1))
|
(inst cmp :dword rcx (fixnumize 1))
|
||||||
(inst jmp :e ONE-VALUE)
|
(inst jmp :e ONE-VALUE)
|
||||||
|
|
@ -313,6 +314,7 @@
|
||||||
(:temp value unsigned-reg r10-offset)
|
(:temp value unsigned-reg r10-offset)
|
||||||
(:temp bsp-temp unsigned-reg r11-offset)
|
(:temp bsp-temp unsigned-reg r11-offset)
|
||||||
(:temp zero complex-double-reg float0-offset))
|
(:temp zero complex-double-reg float0-offset))
|
||||||
|
(emit-safepoint)
|
||||||
AGAIN
|
AGAIN
|
||||||
(let ((error (generate-error-code nil 'invalid-unwind-error)))
|
(let ((error (generate-error-code nil 'invalid-unwind-error)))
|
||||||
(inst test block block) ; check for NULL pointer
|
(inst test block block) ; check for NULL pointer
|
||||||
|
|
@ -404,6 +406,7 @@
|
||||||
(:temp null unsigned-reg r8-offset)
|
(:temp null unsigned-reg r8-offset)
|
||||||
(:temp temp unsigned-reg r9-offset)
|
(:temp temp unsigned-reg r9-offset)
|
||||||
(:temp return unsigned-reg r10-offset))
|
(:temp return unsigned-reg r10-offset))
|
||||||
|
(emit-safepoint)
|
||||||
(flet ((check (label)
|
(flet ((check (label)
|
||||||
(assemble ()
|
(assemble ()
|
||||||
(%test-lowtag list temp skip nil list-pointer-lowtag)
|
(%test-lowtag list temp skip nil list-pointer-lowtag)
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,7 @@
|
||||||
(define-assembly-routine (switch-to-arena (:return-style :raw)) ()
|
(define-assembly-routine (switch-to-arena (:return-style :raw)) ()
|
||||||
;; RSI and RDI are vop temps, so don't bother preserving them
|
;; RSI and RDI are vop temps, so don't bother preserving them
|
||||||
(with-registers-preserved (c :except (rsi rdi))
|
(with-registers-preserved (c :except (rsi rdi))
|
||||||
(pseudo-atomic ()
|
(pseudo-atomic () ; arena switcher
|
||||||
#-system-tlabs (inst break halt-trap)
|
#-system-tlabs (inst break halt-trap)
|
||||||
#+system-tlabs (call-c "switch_to_arena" #+win32 rdi-tn #+win32 rsi-tn))))
|
#+system-tlabs (call-c "switch_to_arena" #+win32 rdi-tn #+win32 rsi-tn))))
|
||||||
|
|
||||||
|
|
@ -103,30 +103,35 @@
|
||||||
,vars ,@code)))))
|
,vars ,@code)))))
|
||||||
|
|
||||||
(def-routine-pair (alloc-tramp) ()
|
(def-routine-pair (alloc-tramp) ()
|
||||||
(with-registers-preserved (c)
|
;(emit-safepoint)
|
||||||
|
(with-registers-preserved (#+yieldpoints lisp)
|
||||||
(call-c "alloc" (ea 16 rbp-tn) system-tlab-p)
|
(call-c "alloc" (ea 16 rbp-tn) system-tlab-p)
|
||||||
(inst mov (ea 16 rbp-tn) rax-tn))) ; result onto stack
|
(inst mov (ea 16 rbp-tn) rax-tn))) ; result onto stack
|
||||||
|
|
||||||
(def-routine-pair (list-alloc-tramp) () ; CONS, ACONS, LIST, LIST*
|
(def-routine-pair (list-alloc-tramp) () ; CONS, ACONS, LIST, LIST*
|
||||||
(with-registers-preserved (c)
|
;(emit-safepoint)
|
||||||
|
(with-registers-preserved (#+yieldpoints lisp)
|
||||||
(call-c "alloc_list" (ea 16 rbp-tn) system-tlab-p)
|
(call-c "alloc_list" (ea 16 rbp-tn) system-tlab-p)
|
||||||
(inst mov (ea 16 rbp-tn) rax-tn))) ; result onto stack
|
(inst mov (ea 16 rbp-tn) rax-tn))) ; result onto stack
|
||||||
|
|
||||||
(def-routine-pair (listify-&rest (:return-style :none)) ()
|
(def-routine-pair (listify-&rest (:return-style :none)) ()
|
||||||
(with-registers-preserved (c)
|
;(emit-safepoint)
|
||||||
|
(with-registers-preserved (#+yieldpoints lisp)
|
||||||
(call-c "listify_rest_arg" (ea 16 rbp-tn) (ea 24 rbp-tn) system-tlab-p)
|
(call-c "listify_rest_arg" (ea 16 rbp-tn) (ea 24 rbp-tn) system-tlab-p)
|
||||||
(inst mov (ea 24 rbp-tn) rax-tn)) ; result
|
(inst mov (ea 24 rbp-tn) rax-tn)) ; result
|
||||||
(inst ret 8)) ; pop one argument; the unpopped word now holds the result
|
(inst ret 8)) ; pop one argument; the unpopped word now holds the result
|
||||||
|
|
||||||
(def-routine-pair (make-list (:return-style :none)) ()
|
(def-routine-pair (make-list (:return-style :none)) ()
|
||||||
(with-registers-preserved (c)
|
;(emit-safepoint)
|
||||||
|
(with-registers-preserved (#+yieldpoints lisp)
|
||||||
(call-c "make_list" (ea 16 rbp-tn) (ea 24 rbp-tn) system-tlab-p)
|
(call-c "make_list" (ea 16 rbp-tn) (ea 24 rbp-tn) system-tlab-p)
|
||||||
(inst mov (ea 24 rbp-tn) rax-tn)) ; result
|
(inst mov (ea 24 rbp-tn) rax-tn)) ; result
|
||||||
(inst ret 8)) ; pop one argument; the unpopped word now holds the result
|
(inst ret 8)) ; pop one argument; the unpopped word now holds the result
|
||||||
)
|
)
|
||||||
|
|
||||||
(define-assembly-routine (alloc-funinstance) ()
|
(define-assembly-routine (alloc-funinstance) ()
|
||||||
(with-registers-preserved (c)
|
;(emit-safepoint)
|
||||||
|
(with-registers-preserved (#+yieldpoints lisp)
|
||||||
(call-c "alloc_funinstance" (ea 16 rbp-tn))
|
(call-c "alloc_funinstance" (ea 16 rbp-tn))
|
||||||
(inst mov (ea 16 rbp-tn) rax-tn)))
|
(inst mov (ea 16 rbp-tn) rax-tn)))
|
||||||
|
|
||||||
|
|
@ -135,13 +140,13 @@
|
||||||
(define-assembly-routine (enable-alloc-counter) ()
|
(define-assembly-routine (enable-alloc-counter) ()
|
||||||
(with-registers-preserved (c)
|
(with-registers-preserved (c)
|
||||||
#+sb-thread
|
#+sb-thread
|
||||||
(pseudo-atomic ()
|
(pseudo-atomic () ; (not sure why this is pseudo-atomic)
|
||||||
(call-c "allocation_tracker_counted" (* (ea 8 rbp-tn))))))
|
(call-c "allocation_tracker_counted" (* (ea 8 rbp-tn))))))
|
||||||
|
|
||||||
(define-assembly-routine (enable-sized-alloc-counter) ()
|
(define-assembly-routine (enable-sized-alloc-counter) ()
|
||||||
(with-registers-preserved (c)
|
(with-registers-preserved (c)
|
||||||
#+sb-thread
|
#+sb-thread
|
||||||
(pseudo-atomic ()
|
(pseudo-atomic () ; (not sure why this is pseudo-atomic)
|
||||||
(call-c "allocation_tracker_sized" (* (ea 8 rbp-tn))))))
|
(call-c "allocation_tracker_sized" (* (ea 8 rbp-tn))))))
|
||||||
|
|
||||||
#+win32
|
#+win32
|
||||||
|
|
@ -243,7 +248,7 @@
|
||||||
(rax rax-tn)
|
(rax rax-tn)
|
||||||
(rdx rdx-tn)
|
(rdx rdx-tn)
|
||||||
(rdi rdi-tn))
|
(rdi rdi-tn))
|
||||||
(pseudo-atomic ()
|
(pseudo-atomic () ; setting a code header slot
|
||||||
#+immobile-space
|
#+immobile-space
|
||||||
(progn
|
(progn
|
||||||
#-sb-thread
|
#-sb-thread
|
||||||
|
|
@ -291,4 +296,24 @@
|
||||||
(inst call (make-fixup 'mutex-wake-waiter :assembly-routine))
|
(inst call (make-fixup 'mutex-wake-waiter :assembly-routine))
|
||||||
uncontested
|
uncontested
|
||||||
(inst pop rax-tn))
|
(inst pop rax-tn))
|
||||||
|
|
||||||
|
(define-assembly-routine (mutex-unlock-if-held (:return-style :raw)) ()
|
||||||
|
;; There are no registers reserved for this asm routine
|
||||||
|
(inst push rax-tn)
|
||||||
|
(inst mov rax-tn (thread-tls-ea (load-time-tls-offset '*current-mutex*)))
|
||||||
|
(inst cmp (mutex-slot rax-tn %owner) thread-tn)
|
||||||
|
(inst jmp :ne uncontested) ; "not mine"
|
||||||
|
(inst mov :qword (mutex-slot rax-tn %owner) 0)
|
||||||
|
(inst dec :lock :byte (mutex-slot rax-tn state))
|
||||||
|
(inst jmp :z uncontested) ; if ZF then previous value was 1, no waiters
|
||||||
|
(inst call (make-fixup 'mutex-wake-waiter :assembly-routine))
|
||||||
|
uncontested
|
||||||
|
(inst pop rax-tn))
|
||||||
) ; end PROGN
|
) ; end PROGN
|
||||||
|
|
||||||
|
#+yieldpoints
|
||||||
|
(define-assembly-routine (handle-deferred-signal) ()
|
||||||
|
(inst pushf)
|
||||||
|
(with-registers-preserved (lisp)
|
||||||
|
(call-static-fun 'sb-unix::handle-deferred-signal 0))
|
||||||
|
(inst popf))
|
||||||
|
|
|
||||||
|
|
@ -427,7 +427,8 @@ Examples:
|
||||||
;;; Nested invocations (from a GC forced by a finalizer) are not ok.
|
;;; Nested invocations (from a GC forced by a finalizer) are not ok.
|
||||||
;;; See the trace at the bottom of this file.
|
;;; See the trace at the bottom of this file.
|
||||||
(define-load-time-global *bg-compiler-function* nil)
|
(define-load-time-global *bg-compiler-function* nil)
|
||||||
(defun run-pending-finalizers (&aux (system-finalizer-scratchpad (list 0)))
|
(defun run-pending-finalizers (&aux (system-finalizer-scratchpad (list 0))
|
||||||
|
(n-ran 0))
|
||||||
(declare (dynamic-extent system-finalizer-scratchpad))
|
(declare (dynamic-extent system-finalizer-scratchpad))
|
||||||
(finalizers-rehash)
|
(finalizers-rehash)
|
||||||
(loop
|
(loop
|
||||||
|
|
@ -446,9 +447,16 @@ Examples:
|
||||||
(sb-vm::immobile-code-dealloc-1 system-finalizer-scratchpad))
|
(sb-vm::immobile-code-dealloc-1 system-finalizer-scratchpad))
|
||||||
(ran-a-user-finalizer ; Try to run 1 user finalizer
|
(ran-a-user-finalizer ; Try to run 1 user finalizer
|
||||||
(run-user-finalizer)))
|
(run-user-finalizer)))
|
||||||
|
(incf n-ran (+ (if ran-a-system-finalizer 1 0)
|
||||||
|
(if ran-a-user-finalizer 1 0)))
|
||||||
;; Did this iteration do anything at all?
|
;; Did this iteration do anything at all?
|
||||||
(unless (or ran-bg-compile ran-a-system-finalizer ran-a-user-finalizer)
|
(unless (or ran-bg-compile ran-a-system-finalizer ran-a-user-finalizer)
|
||||||
(return)))))
|
(return))))
|
||||||
|
#+nil
|
||||||
|
(alien-funcall (extern-alien "printf" (function void system-area-pointer unsigned))
|
||||||
|
(vector-sap #.(format nil "Finalizer ran %d things~%"))
|
||||||
|
n-ran)
|
||||||
|
)
|
||||||
|
|
||||||
(define-load-time-global *finalizer-thread* nil)
|
(define-load-time-global *finalizer-thread* nil)
|
||||||
(declaim (type (or sb-thread:thread (eql :start) null) *finalizer-thread*))
|
(declaim (type (or sb-thread:thread (eql :start) null) *finalizer-thread*))
|
||||||
|
|
@ -475,6 +483,9 @@ Examples:
|
||||||
(sb-thread::make-system-thread
|
(sb-thread::make-system-thread
|
||||||
"finalizer"
|
"finalizer"
|
||||||
(lambda ()
|
(lambda ()
|
||||||
|
;; This lambda is in the lexical scope of (INSERT-SAFEPOINT 0) due to
|
||||||
|
;; the system mutex. But the finalizer-thread-wait must have a yieldpoint.
|
||||||
|
#+yieldpoints (declare (optimize (sb-c::insert-safepoints 1)))
|
||||||
(setf *finalizer-thread* sb-thread:*current-thread*)
|
(setf *finalizer-thread* sb-thread:*current-thread*)
|
||||||
(loop (run-pending-finalizers)
|
(loop (run-pending-finalizers)
|
||||||
(alien-funcall (extern-alien "finalizer_thread_wait" (function void)))
|
(alien-funcall (extern-alien "finalizer_thread_wait" (function void)))
|
||||||
|
|
|
||||||
|
|
@ -283,6 +283,9 @@ run in any thread.")
|
||||||
(call-hooks "after-GC" *after-gc-hooks* :on-error :warn))))
|
(call-hooks "after-GC" *after-gc-hooks* :on-error :warn))))
|
||||||
nil)
|
nil)
|
||||||
|
|
||||||
|
(with-alien ((request-garbage-collection (function void int) :extern)
|
||||||
|
(gc-inhibitor-control (function void int) :extern))
|
||||||
|
|
||||||
;;; This is the user-advertised garbage collection function.
|
;;; This is the user-advertised garbage collection function.
|
||||||
(defun gc (&key (full nil) (gen 0) &allow-other-keys)
|
(defun gc (&key (full nil) (gen 0) &allow-other-keys)
|
||||||
"Initiate a garbage collection.
|
"Initiate a garbage collection.
|
||||||
|
|
@ -292,9 +295,35 @@ trigger a collection of one or more older generations as well. If FULL
|
||||||
is true, all generations are collected. If GEN is provided, it can be
|
is true, all generations are collected. If GEN is provided, it can be
|
||||||
used to specify the oldest generation guaranteed to be collected."
|
used to specify the oldest generation guaranteed to be collected."
|
||||||
(let ((gen (if full sb-vm:+pseudo-static-generation+ gen)))
|
(let ((gen (if full sb-vm:+pseudo-static-generation+ gen)))
|
||||||
|
#+yieldpoints
|
||||||
|
(let ((inhibit *gc-inhibit*))
|
||||||
|
(if inhibit
|
||||||
|
(setq *gc-inhibit* (max inhibit gen))
|
||||||
|
(alien-funcall request-garbage-collection gen)))
|
||||||
|
#-yieldpoints
|
||||||
(when (eq t (sub-gc gen))
|
(when (eq t (sub-gc gen))
|
||||||
(post-gc))))
|
(post-gc))))
|
||||||
|
|
||||||
|
(defun call-with-gc-disabled (thunk)
|
||||||
|
(if *gc-inhibit*
|
||||||
|
(funcall thunk)
|
||||||
|
;; If GC was was deferred, NLX through this will NOT gc but normal exit will.
|
||||||
|
;; (And if NLX, what the f*** are you doing anyway? Inhibiting GC is for
|
||||||
|
;; small code fragments, not massive swaths of application logic)
|
||||||
|
(let (gen)
|
||||||
|
(multiple-value-prog1
|
||||||
|
(let ((*gc-inhibit* -1))
|
||||||
|
(unwind-protect
|
||||||
|
(multiple-value-prog1
|
||||||
|
(progn (alien-funcall gc-inhibitor-control 1)
|
||||||
|
;(format t "~&doing a without-gcing thunk~%")
|
||||||
|
(funcall thunk))
|
||||||
|
(setq gen *gc-inhibit*))
|
||||||
|
(alien-funcall gc-inhibitor-control 0)))
|
||||||
|
(when (>= gen 0)
|
||||||
|
(alien-funcall request-garbage-collection gen))))))
|
||||||
|
) ; end WITH-ALIEN
|
||||||
|
|
||||||
(define-alien-routine scrub-control-stack void)
|
(define-alien-routine scrub-control-stack void)
|
||||||
|
|
||||||
(defglobal sb-unicode::*name->char-buffers* nil)
|
(defglobal sb-unicode::*name->char-buffers* nil)
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,8 @@ maintained."
|
||||||
(with-unique-names (without-gcing-body)
|
(with-unique-names (without-gcing-body)
|
||||||
`(dx-flet ((,without-gcing-body ()
|
`(dx-flet ((,without-gcing-body ()
|
||||||
,@body))
|
,@body))
|
||||||
|
#+yieldpoints (sb-kernel::call-with-gc-disabled #',without-gcing-body)
|
||||||
|
#-yieldpoints
|
||||||
(if *gc-inhibit*
|
(if *gc-inhibit*
|
||||||
(,without-gcing-body)
|
(,without-gcing-body)
|
||||||
;; We need to disable interrupts before disabling GC, so
|
;; We need to disable interrupts before disabling GC, so
|
||||||
|
|
@ -258,7 +260,9 @@ maintained."
|
||||||
;;; System mutexes will always be our own mutex-based-on-futex (if available).
|
;;; System mutexes will always be our own mutex-based-on-futex (if available).
|
||||||
(defmacro with-system-mutex ((mutex &key without-gcing allow-with-interrupts)
|
(defmacro with-system-mutex ((mutex &key without-gcing allow-with-interrupts)
|
||||||
&body body)
|
&body body)
|
||||||
`(dx-flet ((with-system-mutex-thunk () ,@body))
|
`(dx-flet ((with-system-mutex-thunk ()
|
||||||
|
#+yieldpoints (declare (optimize (sb-c::insert-safepoints 0)))
|
||||||
|
,@body))
|
||||||
(,(cond (without-gcing
|
(,(cond (without-gcing
|
||||||
'call-with-system-mutex/without-gcing)
|
'call-with-system-mutex/without-gcing)
|
||||||
(allow-with-interrupts
|
(allow-with-interrupts
|
||||||
|
|
@ -269,5 +273,7 @@ maintained."
|
||||||
,mutex)))
|
,mutex)))
|
||||||
|
|
||||||
(defmacro with-recursive-system-lock ((lock) &body body)
|
(defmacro with-recursive-system-lock ((lock) &body body)
|
||||||
`(dx-flet ((recursive-system-lock-thunk () ,@body))
|
`(dx-flet ((recursive-system-lock-thunk ()
|
||||||
|
#+yieldpoints (declare (optimize (sb-c::insert-safepoints 0)))
|
||||||
|
,@body))
|
||||||
(call-with-recursive-system-lock #'recursive-system-lock-thunk ,lock)))
|
(call-with-recursive-system-lock #'recursive-system-lock-thunk ,lock)))
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@
|
||||||
;;; doing things the SBCL way and moving this kind of C-level work
|
;;; doing things the SBCL way and moving this kind of C-level work
|
||||||
;;; down to C wrapper functions.)
|
;;; down to C wrapper functions.)
|
||||||
|
|
||||||
#-sb-safepoint
|
#-(or sb-safepoint yieldpoints)
|
||||||
(defun unblock-stop-for-gc-signal ()
|
(defun unblock-stop-for-gc-signal ()
|
||||||
(with-alien ((%unblock (function void) :extern "unblock_gc_stop_signal"))
|
(with-alien ((%unblock (function void) :extern "unblock_gc_stop_signal"))
|
||||||
(alien-funcall %unblock)
|
(alien-funcall %unblock)
|
||||||
|
|
@ -81,13 +81,13 @@
|
||||||
;; are opaque. We use our own explicit translation of 0 and 1 to them
|
;; are opaque. We use our own explicit translation of 0 and 1 to them
|
||||||
;; in the C install_handler() argument passing convention.
|
;; in the C install_handler() argument passing convention.
|
||||||
(with-alien ((%sigaction (function void int unsigned) :extern "install_handler"))
|
(with-alien ((%sigaction (function void int unsigned) :extern "install_handler"))
|
||||||
#+sb-safepoint
|
#+(or sb-safepoint yieldpoints)
|
||||||
(alien-funcall %sigaction signal
|
(alien-funcall %sigaction signal
|
||||||
(case handler
|
(case handler
|
||||||
(:default 0)
|
(:default 0)
|
||||||
(:ignore 1)
|
(:ignore 1)
|
||||||
(t (sb-kernel:get-lisp-obj-address handler))))
|
(t (sb-kernel:get-lisp-obj-address handler))))
|
||||||
#-sb-safepoint
|
#-(or sb-safepoint yieldpoints)
|
||||||
(flet ((run-handler (signo info-sap context-sap)
|
(flet ((run-handler (signo info-sap context-sap)
|
||||||
#-(or c-stack-is-control-stack sb-safepoint) ;; able to do that in interrupt_handle_now()
|
#-(or c-stack-is-control-stack sb-safepoint) ;; able to do that in interrupt_handle_now()
|
||||||
(unblock-stop-for-gc-signal)
|
(unblock-stop-for-gc-signal)
|
||||||
|
|
@ -146,7 +146,9 @@
|
||||||
(signal int)
|
(signal int)
|
||||||
;; Then enter the debugger like BREAK.
|
;; Then enter the debugger like BREAK.
|
||||||
(%break 'sigint int))))))
|
(%break 'sigint int))))))
|
||||||
#+sb-safepoint
|
;; The comment below is confusing AF but the behavior of the
|
||||||
|
;; #+sb-safepoint code is correct for #+yieldpoints as well.
|
||||||
|
#+(or sb-safepoint yieldpoints)
|
||||||
(let ((target (sb-thread::foreground-thread)))
|
(let ((target (sb-thread::foreground-thread)))
|
||||||
;; Note that INTERRUPT-THREAD on *CURRENT-THREAD* doesn't actually
|
;; Note that INTERRUPT-THREAD on *CURRENT-THREAD* doesn't actually
|
||||||
;; interrupt right away, because deferrables are blocked. Rather,
|
;; interrupt right away, because deferrables are blocked. Rather,
|
||||||
|
|
@ -161,7 +163,7 @@
|
||||||
(if (eq target sb-thread:*current-thread*)
|
(if (eq target sb-thread:*current-thread*)
|
||||||
(interrupt-it)
|
(interrupt-it)
|
||||||
(sb-thread:interrupt-thread target #'interrupt-it)))
|
(sb-thread:interrupt-thread target #'interrupt-it)))
|
||||||
#-sb-safepoint
|
#-(or sb-safepoint yieldpoints)
|
||||||
(sb-thread:interrupt-thread (sb-thread::foreground-thread)
|
(sb-thread:interrupt-thread (sb-thread::foreground-thread)
|
||||||
#'interrupt-it)))
|
#'interrupt-it)))
|
||||||
|
|
||||||
|
|
@ -250,3 +252,24 @@
|
||||||
(ash num sb-vm:word-shift))))
|
(ash num sb-vm:word-shift))))
|
||||||
(when (functionp fun)
|
(when (functionp fun)
|
||||||
(funcall fun num nil nil)))))))))
|
(funcall fun num nil nil)))))))))
|
||||||
|
|
||||||
|
#+yieldpoints
|
||||||
|
(with-alien ((show-interrupt-data (function void) :extern)
|
||||||
|
(getcontext (function int system-area-pointer) :extern)
|
||||||
|
(get-pending-signal-number (function int) :extern)
|
||||||
|
(lisp-sig-handlers (array unsigned 32) :extern)
|
||||||
|
(flush-pending-signal-and-restore (function void) :extern))
|
||||||
|
(export 'showint)
|
||||||
|
(defun showint ()
|
||||||
|
(alien-funcall show-interrupt-data))
|
||||||
|
(defun handle-deferred-signal ()
|
||||||
|
(alien-funcall show-interrupt-data)
|
||||||
|
(let* ((c-context (make-array sb-unix::sizeof-ucontext_t :element-type '(unsigned-byte 8)))
|
||||||
|
(sig (alien-funcall get-pending-signal-number))
|
||||||
|
(fun (sb-kernel:%make-lisp-obj (deref lisp-sig-handlers sig))))
|
||||||
|
(aver (functionp fun))
|
||||||
|
(with-pinned-objects (c-context)
|
||||||
|
(let ((context-sap (vector-sap c-context)))
|
||||||
|
(alien-funcall getcontext context-sap)
|
||||||
|
(funcall fun sig (sb-sys:int-sap 0) context-sap)
|
||||||
|
(alien-funcall flush-pending-signal-and-restore))))))
|
||||||
|
|
|
||||||
|
|
@ -510,11 +510,11 @@ See also: RETURN-FROM-THREAD and SB-EXT:EXIT."
|
||||||
|
|
||||||
(define-alien-routine "futex_wake" int (word-addr unsigned) (n unsigned-long))
|
(define-alien-routine "futex_wake" int (word-addr unsigned) (n unsigned-long))
|
||||||
|
|
||||||
(defun futex-wait (word-addr oldval to-sec to-usec)
|
(with-alien ((%wait (function int unsigned (unsigned 32) long unsigned-long)
|
||||||
(with-alien ((%wait (function int unsigned
|
:extern "futex_wait"))
|
||||||
(unsigned 32)
|
(defun fast-futex-wait (word-addr oldval to-sec to-usec)
|
||||||
long unsigned-long)
|
(alien-funcall %wait word-addr oldval to-sec to-usec))
|
||||||
:extern "futex_wait"))
|
(defun futex-wait (word-addr oldval to-sec to-usec)
|
||||||
(with-interrupts
|
(with-interrupts
|
||||||
(alien-funcall %wait word-addr oldval to-sec to-usec))))))
|
(alien-funcall %wait word-addr oldval to-sec to-usec))))))
|
||||||
|
|
||||||
|
|
@ -820,13 +820,6 @@ returns NIL each time."
|
||||||
|
|
||||||
#+ultrafutex
|
#+ultrafutex
|
||||||
(progn
|
(progn
|
||||||
(declaim (inline fast-futex-wait))
|
|
||||||
(defun fast-futex-wait (word-addr oldval to-sec to-usec)
|
|
||||||
(with-alien ((%wait (function int unsigned
|
|
||||||
#+freebsd unsigned #-freebsd (unsigned 32)
|
|
||||||
long unsigned-long)
|
|
||||||
:extern "futex_wait"))
|
|
||||||
(alien-funcall %wait word-addr oldval to-sec to-usec)))
|
|
||||||
(declaim (sb-ext:maybe-inline %wait-for-mutex-algorithm-3))
|
(declaim (sb-ext:maybe-inline %wait-for-mutex-algorithm-3))
|
||||||
(defun %wait-for-mutex-algorithm-3 (mutex)
|
(defun %wait-for-mutex-algorithm-3 (mutex)
|
||||||
#+nil ; in case I want to count calls to this function
|
#+nil ; in case I want to count calls to this function
|
||||||
|
|
@ -845,7 +838,12 @@ returns NIL each time."
|
||||||
(loop while (/= c 0)
|
(loop while (/= c 0)
|
||||||
do (with-pinned-objects (mutex)
|
do (with-pinned-objects (mutex)
|
||||||
(fast-futex-wait (mutex-state-address mutex) 2 -1 0))
|
(fast-futex-wait (mutex-state-address mutex) 2 -1 0))
|
||||||
(setq c (%raw-instance-xchg/word mutex (get-dsd-index mutex state) 2))))))))
|
(setq c (%raw-instance-xchg/word mutex (get-dsd-index mutex state) 2)))))))
|
||||||
|
(defun wait-for-mutex-algorithm-3 (mutex)
|
||||||
|
;(declare (inline %wait-for-mutex-algorithm-3))
|
||||||
|
(let ((mutex (sb-ext:truly-the mutex mutex)))
|
||||||
|
(%wait-for-mutex-algorithm-3 mutex)
|
||||||
|
(setf (mutex-%owner mutex) (current-vmthread-id)))))
|
||||||
|
|
||||||
#+mutex-benchmarks
|
#+mutex-benchmarks
|
||||||
(symbol-macrolet ((val (mutex-state mutex)))
|
(symbol-macrolet ((val (mutex-state mutex)))
|
||||||
|
|
@ -879,7 +877,7 @@ returns NIL each time."
|
||||||
;; Code size is a little less. More improvement comes from doing the
|
;; Code size is a little less. More improvement comes from doing the
|
||||||
;; partial-inline algorithms which perform one CAS without a function call.
|
;; partial-inline algorithms which perform one CAS without a function call.
|
||||||
(defun wait-for-mutex-algorithm-3 (mutex)
|
(defun wait-for-mutex-algorithm-3 (mutex)
|
||||||
(declare (inline %wait-for-mutex-algorithm-3))
|
;(declare (inline %wait-for-mutex-algorithm-3))
|
||||||
(let ((mutex (sb-ext:truly-the mutex mutex)))
|
(let ((mutex (sb-ext:truly-the mutex mutex)))
|
||||||
(%wait-for-mutex-algorithm-3 mutex)
|
(%wait-for-mutex-algorithm-3 mutex)
|
||||||
(setf (mutex-%owner mutex) (current-vmthread-id))))
|
(setf (mutex-%owner mutex) (current-vmthread-id))))
|
||||||
|
|
@ -1268,14 +1266,10 @@ associated data:
|
||||||
|
|
||||||
IMPORTANT: The same mutex that is used in the corresponding CONDITION-WAIT
|
IMPORTANT: The same mutex that is used in the corresponding CONDITION-WAIT
|
||||||
must be held by this thread during this call."
|
must be held by this thread during this call."
|
||||||
#-sb-thread
|
(declare (ignorable queue n))
|
||||||
(declare (ignore queue n))
|
#-sb-thread (error "Not supported in unithread builds.")
|
||||||
#-sb-thread
|
#+sb-futex ; implies sb-thread per feature-compatibility-tests
|
||||||
(error "Not supported in unithread builds.")
|
(progn
|
||||||
#+sb-thread
|
|
||||||
(cond
|
|
||||||
#+sb-futex
|
|
||||||
(t
|
|
||||||
;; No problem if >1 thread notifies during the comment in condition-wait:
|
;; No problem if >1 thread notifies during the comment in condition-wait:
|
||||||
;; as long as the value in queue-data isn't the waiting thread's id, it
|
;; as long as the value in queue-data isn't the waiting thread's id, it
|
||||||
;; matters not what it is. We rely on kernel thread ID being nonzero.
|
;; matters not what it is. We rely on kernel thread ID being nonzero.
|
||||||
|
|
@ -1288,10 +1282,9 @@ must be held by this thread during this call."
|
||||||
(with-pinned-objects (queue)
|
(with-pinned-objects (queue)
|
||||||
(futex-wake (waitqueue-token-address queue) n))
|
(futex-wake (waitqueue-token-address queue) n))
|
||||||
nil)
|
nil)
|
||||||
#-sb-futex
|
#+(and sb-thread (not sb-futex))
|
||||||
(t
|
(with-cas-lock ((waitqueue-%owner queue))
|
||||||
(with-cas-lock ((waitqueue-%owner queue))
|
(%waitqueue-wakeup queue n)))
|
||||||
(%waitqueue-wakeup queue n)))))
|
|
||||||
|
|
||||||
|
|
||||||
(declaim (ftype (sfunction (waitqueue) null) condition-broadcast))
|
(declaim (ftype (sfunction (waitqueue) null) condition-broadcast))
|
||||||
|
|
@ -1885,6 +1878,10 @@ session."
|
||||||
(prot "protect_alien_stack_guard_page")))
|
(prot "protect_alien_stack_guard_page")))
|
||||||
(unless (= (sap-int thread-sap) 0) thread-sap))))
|
(unless (= (sap-int thread-sap) 0) thread-sap))))
|
||||||
|
|
||||||
|
(sb-ext:defglobal *lisp-yps-executed* 0)
|
||||||
|
(sb-ext:defglobal *c-yps-executed* 0)
|
||||||
|
(declaim (fixnum *lisp-yps-executed* *c-yps-executed*))
|
||||||
|
|
||||||
;;; Remove thread from its session, if it has one, and from *all-threads*.
|
;;; Remove thread from its session, if it has one, and from *all-threads*.
|
||||||
;;; Also clobber the pointer to the primitive thread
|
;;; Also clobber the pointer to the primitive thread
|
||||||
;;; which makes THREAD-ALIVE-P return false hereafter.
|
;;; which makes THREAD-ALIVE-P return false hereafter.
|
||||||
|
|
@ -1925,7 +1922,9 @@ session."
|
||||||
(setf (sap-ref-8 (current-thread-sap) ; state_word.sprof_enable
|
(setf (sap-ref-8 (current-thread-sap) ; state_word.sprof_enable
|
||||||
(1+ (ash sb-vm:thread-state-word-slot sb-vm:word-shift)))
|
(1+ (ash sb-vm:thread-state-word-slot sb-vm:word-shift)))
|
||||||
0)
|
0)
|
||||||
;; Take ownership of our statistical profiling data and transfer the results to
|
(sb-ext:atomic-incf *lisp-yps-executed* (sb-sys:sap-int (sb-vm::current-thread-offset-sap sb-vm::thread-lisp-yps-executed-slot)))
|
||||||
|
(sb-ext:atomic-incf *c-yps-executed* (sb-sys:sap-int (sb-vm::current-thread-offset-sap sb-vm::thread-c-yps-executed-slot)))
|
||||||
|
;; take ownership of our statistical profiling data and transfer the results to
|
||||||
;; the global pool. This doesn't need to synchronize with the signal handler,
|
;; the global pool. This doesn't need to synchronize with the signal handler,
|
||||||
;; which is effectively disabled now, but does synchronize via the interruptions
|
;; which is effectively disabled now, but does synchronize via the interruptions
|
||||||
;; mutex with any other thread trying to read this thread's data.
|
;; mutex with any other thread trying to read this thread's data.
|
||||||
|
|
@ -2062,6 +2061,7 @@ session."
|
||||||
0)
|
0)
|
||||||
) ; end PROGN for #+sb-thread
|
) ; end PROGN for #+sb-thread
|
||||||
|
|
||||||
|
(sb-ext:defglobal *anon-thread-name-generator* 0)
|
||||||
(defun make-thread (function &key name arguments)
|
(defun make-thread (function &key name arguments)
|
||||||
"Create a new thread of NAME that runs FUNCTION with the argument
|
"Create a new thread of NAME that runs FUNCTION with the argument
|
||||||
list designator provided (defaults to no argument). Thread exits when
|
list designator provided (defaults to no argument). Thread exits when
|
||||||
|
|
@ -2075,7 +2075,10 @@ See also: RETURN-FROM-THREAD, ABORT-THREAD."
|
||||||
#-sb-thread (declare (ignore function name arguments))
|
#-sb-thread (declare (ignore function name arguments))
|
||||||
#-sb-thread (error "Not supported in unithread builds.")
|
#-sb-thread (error "Not supported in unithread builds.")
|
||||||
#+sb-thread
|
#+sb-thread
|
||||||
(let ((name (when name (possibly-base-stringize name))))
|
(let ((name (if name
|
||||||
|
(possibly-base-stringize name)
|
||||||
|
(with-system-mutex (*make-thread-lock*)
|
||||||
|
(format nil "Thr~D" (incf *anon-thread-name-generator*))))))
|
||||||
(assert (or (atom arguments) (null (cdr (last arguments))))
|
(assert (or (atom arguments) (null (cdr (last arguments))))
|
||||||
(arguments)
|
(arguments)
|
||||||
"Argument passed to ~S, ~S, is an improper list."
|
"Argument passed to ~S, ~S, is an improper list."
|
||||||
|
|
@ -2399,6 +2402,11 @@ Short version: be careful out there."
|
||||||
;; the behavior is undefined."
|
;; the behavior is undefined."
|
||||||
;; so we use the death lock to keep the thread alive, unless it already isn't.
|
;; so we use the death lock to keep the thread alive, unless it already isn't.
|
||||||
;;
|
;;
|
||||||
|
(let ((str (let ((*print-pretty* nil))
|
||||||
|
(format nil "~s interrupts ~S with ~s~%"
|
||||||
|
(thread-name *current-thread*) (thread-name thread) function))))
|
||||||
|
(with-pinned-objects (str)
|
||||||
|
(sb-unix:unix-write 2 str 0 (length str))))
|
||||||
(when (with-deathlok (thread c-thread)
|
(when (with-deathlok (thread c-thread)
|
||||||
;; Return T if couldn't interrupt.
|
;; Return T if couldn't interrupt.
|
||||||
(cond ((eql c-thread 0) t)
|
(cond ((eql c-thread 0) t)
|
||||||
|
|
@ -2647,14 +2655,12 @@ mechanism for inter-thread communication."
|
||||||
|
|
||||||
;;;; Stepping
|
;;;; Stepping
|
||||||
|
|
||||||
(defun thread-stepping ()
|
#+sb-thread
|
||||||
(sap-ref-lispobj (current-thread-sap)
|
(macrolet ((access-it ()
|
||||||
(* sb-vm::thread-stepping-slot sb-vm:n-word-bytes)))
|
`(sap-ref-8 (current-thread-sap)
|
||||||
|
(* sb-vm::thread-stepping-slot sb-vm:n-word-bytes))))
|
||||||
(defun (setf thread-stepping) (value)
|
(defun thread-stepping () (access-it))
|
||||||
(setf (sap-ref-lispobj (current-thread-sap)
|
(defun (setf thread-stepping) (value) (setf (access-it) value)))
|
||||||
(* sb-vm::thread-stepping-slot sb-vm:n-word-bytes))
|
|
||||||
value))
|
|
||||||
|
|
||||||
;;;; Diagnostic tool
|
;;;; Diagnostic tool
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@
|
||||||
"Semaphore type. The fact that a SEMAPHORE is a STRUCTURE-OBJECT
|
"Semaphore type. The fact that a SEMAPHORE is a STRUCTURE-OBJECT
|
||||||
should be considered an implementation detail, and may change in the
|
should be considered an implementation detail, and may change in the
|
||||||
future."
|
future."
|
||||||
(%count 0 :type (integer 0))
|
(%count 0 :type (and (integer 0) fixnum))
|
||||||
(waitcount 0 :type sb-vm:word)
|
(waitcount 0 :type sb-vm:word)
|
||||||
(mutex nil :read-only t :type mutex)
|
(mutex nil :read-only t :type mutex)
|
||||||
(queue nil :read-only t :type waitqueue))
|
(queue nil :read-only t :type waitqueue))
|
||||||
|
|
|
||||||
|
|
@ -174,7 +174,7 @@ HOLDING-MUTEX-P."
|
||||||
;; Well if either HOLDING-MUTEX-P *or* if the OWNER is 0, there's no chance that
|
;; Well if either HOLDING-MUTEX-P *or* if the OWNER is 0, there's no chance that
|
||||||
;; a different thread owns it.
|
;; a different thread owns it.
|
||||||
(unless (sb-vm::quick-try-mutex m)
|
(unless (sb-vm::quick-try-mutex m)
|
||||||
(%wait-for-mutex-algorithm-3 m))
|
(wait-for-mutex-algorithm-3 m))
|
||||||
;; unbinding the special = releasing the mutex
|
;; unbinding the special = releasing the mutex
|
||||||
(let ((sb-vm::*current-mutex* m))
|
(let ((sb-vm::*current-mutex* m))
|
||||||
(setf (mutex-%owner m) (current-vmthread-id))
|
(setf (mutex-%owner m) (current-vmthread-id))
|
||||||
|
|
@ -268,11 +268,11 @@ held mutex, WITH-RECURSIVE-LOCK allows recursive lock attempts to succeed."
|
||||||
(t
|
(t
|
||||||
`(fast-call-with-recursive-lock #'with-recursive-lock-thunk ,mutex)))))
|
`(fast-call-with-recursive-lock #'with-recursive-lock-thunk ,mutex)))))
|
||||||
|
|
||||||
|
;#-ultrafutex
|
||||||
(macrolet ((def (name &optional variant)
|
(macrolet ((def (name &optional variant)
|
||||||
`(defun ,(if variant (symbolicate name "/" variant) name)
|
`(defun ,(if variant (symbolicate name "/" variant) name)
|
||||||
(function mutex)
|
(function mutex)
|
||||||
(declare (function function))
|
(declare (function function) (dynamic-extent function))
|
||||||
(declare (dynamic-extent function))
|
|
||||||
(flet ((%call-with-system-mutex ()
|
(flet ((%call-with-system-mutex ()
|
||||||
(let (got-it)
|
(let (got-it)
|
||||||
(unwind-protect
|
(unwind-protect
|
||||||
|
|
@ -363,16 +363,16 @@ held mutex, WITH-RECURSIVE-LOCK allows recursive lock attempts to succeed."
|
||||||
(funcall function)
|
(funcall function)
|
||||||
(with-ultrafutex (mutex) (funcall function)))))
|
(with-ultrafutex (mutex) (funcall function)))))
|
||||||
|
|
||||||
(defun call-with-recursive-system-lock (function lock)
|
(defun call-with-recursive-system-lock (function mutex)
|
||||||
(declare (function function))
|
(declare (function function))
|
||||||
(declare (dynamic-extent function))
|
(declare (dynamic-extent function))
|
||||||
(without-interrupts
|
(without-interrupts
|
||||||
(let ((had-it (holding-mutex-p lock))
|
(let ((had-it (holding-mutex-p mutex))
|
||||||
(got-it nil))
|
(got-it nil))
|
||||||
(unwind-protect
|
(unwind-protect
|
||||||
(when (or had-it (setf got-it (grab-mutex lock)))
|
(when (or had-it (setf got-it (grab-mutex mutex)))
|
||||||
(funcall function))
|
(funcall function))
|
||||||
(when got-it
|
(when got-it
|
||||||
(release-mutex lock)))))))
|
(release-mutex mutex)))))))
|
||||||
|
|
||||||
(sb-ext:define-load-time-global *make-thread-lock* nil)
|
(sb-ext:define-load-time-global *make-thread-lock* nil)
|
||||||
|
|
|
||||||
|
|
@ -233,6 +233,9 @@
|
||||||
internal-time-units-per-second)))
|
internal-time-units-per-second)))
|
||||||
(format t " (~5,3f sec)~%" elapsed)
|
(format t " (~5,3f sec)~%" elapsed)
|
||||||
(incf total-time elapsed)))
|
(incf total-time elapsed)))
|
||||||
|
(format t "~&Total yieldpoints: ~D, deleted: ~D ~%"
|
||||||
|
sb-x86-64-asm::*original-total-n-yieldpoints*
|
||||||
|
sb-x86-64-asm::*total-n-yieldpoints-deleted*)
|
||||||
;(sb-kernel::show-ctype-ctor-cache-metrics)
|
;(sb-kernel::show-ctype-ctor-cache-metrics)
|
||||||
(when sb-impl::*profile-hash-cache*
|
(when sb-impl::*profile-hash-cache*
|
||||||
;; avoid "make-host-2 stopped due to unexpected STYLE-WARNING raised from the host."
|
;; avoid "make-host-2 stopped due to unexpected STYLE-WARNING raised from the host."
|
||||||
|
|
|
||||||
|
|
@ -1935,7 +1935,7 @@ is a good idea, but see SB-SYS re. blurring of boundaries.")
|
||||||
"%VECTOR-RAW-BITS"
|
"%VECTOR-RAW-BITS"
|
||||||
"%SCALB" "%SCALBN"
|
"%SCALB" "%SCALBN"
|
||||||
"%RAW-INSTANCE-ATOMIC-INCF/WORD"
|
"%RAW-INSTANCE-ATOMIC-INCF/WORD"
|
||||||
"%RAW-INSTANCE-CAS/WORD" "%RAW-INSTANCE-XCHG/WORD"
|
"%RAW-INSTANCE-CAS/WORD" "%RAW-INSTANCE-XCHG/BYTE" "%RAW-INSTANCE-XCHG/WORD"
|
||||||
"%RAW-INSTANCE-REF/WORD" "%RAW-INSTANCE-SET/WORD"
|
"%RAW-INSTANCE-REF/WORD" "%RAW-INSTANCE-SET/WORD"
|
||||||
"%RAW-INSTANCE-CAS/SIGNED-WORD"
|
"%RAW-INSTANCE-CAS/SIGNED-WORD"
|
||||||
"%RAW-INSTANCE-REF/SIGNED-WORD" "%RAW-INSTANCE-SET/SIGNED-WORD"
|
"%RAW-INSTANCE-REF/SIGNED-WORD" "%RAW-INSTANCE-SET/SIGNED-WORD"
|
||||||
|
|
|
||||||
|
|
@ -327,6 +327,9 @@
|
||||||
(push :salted-symbol-hash sb-xc:*features*))
|
(push :salted-symbol-hash sb-xc:*features*))
|
||||||
(when (target-featurep '(:and :sb-thread (:or (:and :darwin (:not (:or :ppc :x86))) :openbsd)))
|
(when (target-featurep '(:and :sb-thread (:or (:and :darwin (:not (:or :ppc :x86))) :openbsd)))
|
||||||
(push :os-thread-stack sb-xc:*features*))
|
(push :os-thread-stack sb-xc:*features*))
|
||||||
|
(when (target-featurep '(:and (:not :sb-safepoint) (:not :yieldpoints)))
|
||||||
|
;; signal-based stop-the-world (_other_ than memory-fault on a trap page)
|
||||||
|
(push :gc-stw-signal sb-xc:*features*))
|
||||||
(when (target-featurep '(:and :x86 :int4-breakpoints))
|
(when (target-featurep '(:and :x86 :int4-breakpoints))
|
||||||
;; 0xCE is a perfectly good 32-bit instruction,
|
;; 0xCE is a perfectly good 32-bit instruction,
|
||||||
;; unlike on x86-64 where it is illegal. It's therefore
|
;; unlike on x86-64 where it is illegal. It's therefore
|
||||||
|
|
|
||||||
|
|
@ -173,6 +173,7 @@ sb-kernel::
|
||||||
(sb-c:insert-step-conditions 0)
|
(sb-c:insert-step-conditions 0)
|
||||||
(sb-c:alien-funcall-saves-fp-and-pc #+x86 3 #-x86 0)))
|
(sb-c:alien-funcall-saves-fp-and-pc #+x86 3 #-x86 0)))
|
||||||
(compile-file (concatenate 'string *sbclroot* stem)
|
(compile-file (concatenate 'string *sbclroot* stem)
|
||||||
|
;:trace-file t :print t
|
||||||
:output-file output)))
|
:output-file output)))
|
||||||
((nil) output))
|
((nil) output))
|
||||||
(cond ((not output-truename)
|
(cond ((not output-truename)
|
||||||
|
|
|
||||||
|
|
@ -292,10 +292,10 @@
|
||||||
(declaim (freeze-type stmt))
|
(declaim (freeze-type stmt))
|
||||||
(defmethod print-object ((stmt stmt) stream)
|
(defmethod print-object ((stmt stmt) stream)
|
||||||
(print-unreadable-object (stmt stream :type t :identity t)
|
(print-unreadable-object (stmt stream :type t :identity t)
|
||||||
(awhen (stmt-labels stmt)
|
(format t stream "~@[~A ~]~A ~:S"
|
||||||
(princ it stream)
|
(stmt-labels stmt)
|
||||||
(write-char #\space stream))
|
(stmt-mnemonic stmt)
|
||||||
(princ (stmt-mnemonic stmt) stream)))
|
(stmt-operands stmt))))
|
||||||
|
|
||||||
;;; A section is just a doubly-linked list of statements with a head and
|
;;; A section is just a doubly-linked list of statements with a head and
|
||||||
;;; tail pointer to allow insertion anywhere,
|
;;; tail pointer to allow insertion anywhere,
|
||||||
|
|
@ -1394,9 +1394,9 @@
|
||||||
(defun extract-prefix-keywords (x) x)
|
(defun extract-prefix-keywords (x) x)
|
||||||
(defun decode-prefix (args) args))
|
(defun decode-prefix (args) args))
|
||||||
|
|
||||||
(defun dump-symbolic-asm (section stream &aux last-vop all-labels (n 0))
|
(defun dump-symbolic-asm (start stream &aux last-vop all-labels (n 0))
|
||||||
(format stream "~2&Assembler input:~%")
|
(format stream "~2&Assembler input:~%")
|
||||||
(do ((statement (stmt-next (section-start section)) (stmt-next statement))
|
(do ((statement start (stmt-next statement))
|
||||||
(*print-pretty* nil))
|
(*print-pretty* nil))
|
||||||
((null statement))
|
((null statement))
|
||||||
(incf n)
|
(incf n)
|
||||||
|
|
@ -2002,7 +2002,8 @@
|
||||||
(entry (list opcodes1 opcodes2 applicator index name)))
|
(entry (list opcodes1 opcodes2 applicator index name)))
|
||||||
(push entry *asm-pattern-matchers*)))))
|
(push entry *asm-pattern-matchers*)))))
|
||||||
|
|
||||||
(defun combine-instructions (section)
|
(defun combine-instructions (code-section elsewhere-section)
|
||||||
|
(declare (ignorable elsewhere-section))
|
||||||
;; Triply nested loop:
|
;; Triply nested loop:
|
||||||
;; - repeatedly scan until no further changes
|
;; - repeatedly scan until no further changes
|
||||||
;; - looking for a pattern that starts at each instruction
|
;; - looking for a pattern that starts at each instruction
|
||||||
|
|
@ -2013,7 +2014,7 @@
|
||||||
(loop
|
(loop
|
||||||
(let* ((any-changes)
|
(let* ((any-changes)
|
||||||
(stmt nil)
|
(stmt nil)
|
||||||
(next (section-start section)))
|
(next (section-start code-section)))
|
||||||
(loop
|
(loop
|
||||||
(setq stmt next next (stmt-next stmt))
|
(setq stmt next next (stmt-next stmt))
|
||||||
(unless next (return))
|
(unless next (return))
|
||||||
|
|
@ -2046,13 +2047,19 @@
|
||||||
(unless any-changes (return))))
|
(unless any-changes (return))))
|
||||||
#+x86-64
|
#+x86-64
|
||||||
;; Build the label -> stmt map
|
;; Build the label -> stmt map
|
||||||
(let ((label->stmt (make-hash-table)))
|
(let ((label->stmt (make-hash-table))
|
||||||
(do ((stmt (section-start section) (stmt-next stmt)))
|
(elsewhere-labels (alloc-xset)))
|
||||||
((null stmt))
|
(dolist (section `((nil . ,code-section)
|
||||||
(dolist (label (ensure-list (stmt-labels stmt)))
|
(t . ,elsewhere-section)))
|
||||||
(aver (not (gethash label label->stmt)))
|
(do ((stmt (section-start (cdr section)) (stmt-next stmt)))
|
||||||
(setf (gethash label label->stmt) stmt)))
|
((null stmt))
|
||||||
(perform-jump-to-jump-elimination (section-start section) label->stmt)))
|
(dolist (label (ensure-list (stmt-labels stmt)))
|
||||||
|
(aver (not (gethash label label->stmt)))
|
||||||
|
(when (car section) ; is "elsewhere"
|
||||||
|
(add-to-xset label elsewhere-labels))
|
||||||
|
(setf (gethash label label->stmt) stmt))))
|
||||||
|
(perform-jump-to-jump-elimination (section-start code-section)
|
||||||
|
label->stmt elsewhere-labels)))
|
||||||
|
|
||||||
;; Remove macros that users should not invoke
|
;; Remove macros that users should not invoke
|
||||||
(push '("SB-ASSEM" define-instruction define-instruction-macro)
|
(push '("SB-ASSEM" define-instruction define-instruction-macro)
|
||||||
|
|
|
||||||
|
|
@ -308,7 +308,8 @@
|
||||||
|
|
||||||
(when *do-instcombine-pass*
|
(when *do-instcombine-pass*
|
||||||
#+(or arm64 x86-64)
|
#+(or arm64 x86-64)
|
||||||
(sb-assem::combine-instructions (asmstream-code-section asmstream)))
|
(sb-assem::combine-instructions (asmstream-code-section asmstream)
|
||||||
|
(asmstream-elsewhere-section asmstream)))
|
||||||
|
|
||||||
(emit (asmstream-data-section asmstream)
|
(emit (asmstream-data-section asmstream)
|
||||||
(sb-assem::asmstream-data-origin-label asmstream))
|
(sb-assem::asmstream-data-origin-label asmstream))
|
||||||
|
|
|
||||||
|
|
@ -2226,7 +2226,7 @@
|
||||||
(defknown sb-vm:%write-barrier () (values) ())
|
(defknown sb-vm:%write-barrier () (values) ())
|
||||||
(defknown sb-vm:%data-dependency-barrier () (values) ())
|
(defknown sb-vm:%data-dependency-barrier () (values) ())
|
||||||
|
|
||||||
#+sb-safepoint
|
#+(or sb-safepoint yieldpoints)
|
||||||
;;; Note: This known function does not have an out-of-line definition;
|
;;; Note: This known function does not have an out-of-line definition;
|
||||||
;;; and if such a definition were needed, it would not need to "call"
|
;;; and if such a definition were needed, it would not need to "call"
|
||||||
;;; itself inline, but could be a no-op, because the compiler inserts a
|
;;; itself inline, but could be a no-op, because the compiler inserts a
|
||||||
|
|
|
||||||
|
|
@ -3044,6 +3044,9 @@ Legal values for OFFSET are -4, -8, -12, ..."
|
||||||
(dolist (target-feature-name (sort (mapcar #'c-symbol-name sb-xc:*features*)
|
(dolist (target-feature-name (sort (mapcar #'c-symbol-name sb-xc:*features*)
|
||||||
#'string<))
|
#'string<))
|
||||||
(format t "#define LISP_FEATURE_~A~%" target-feature-name))
|
(format t "#define LISP_FEATURE_~A~%" target-feature-name))
|
||||||
|
;; A random one derived from sb-xc:*features* but not spelled in the usual way,
|
||||||
|
;; and to easy to accidentaly fail to define if used in multiple files.
|
||||||
|
#+(and sb-thread gc-stw-signal) (format t "#define THREADS_USING_GCSIGNAL~%")
|
||||||
(terpri)
|
(terpri)
|
||||||
;; and miscellaneous constants
|
;; and miscellaneous constants
|
||||||
(format t "#define SBCL_TARGET_ARCHITECTURE_STRING ~S~%"
|
(format t "#define SBCL_TARGET_ARCHITECTURE_STRING ~S~%"
|
||||||
|
|
@ -3376,6 +3379,7 @@ lispobj symbol_package(struct symbol*);~%" (genesis-header-prefix))
|
||||||
;; but it gets cast to HANDLE upon use.
|
;; but it gets cast to HANDLE upon use.
|
||||||
#+(and unix sb-thread) (format t "#include <pthread.h>~%")
|
#+(and unix sb-thread) (format t "#include <pthread.h>~%")
|
||||||
(format t "#include ~S
|
(format t "#include ~S
|
||||||
|
#include ~S
|
||||||
|
|
||||||
#define N_HISTOGRAM_BINS_LARGE 32
|
#define N_HISTOGRAM_BINS_LARGE 32
|
||||||
#define N_HISTOGRAM_BINS_SMALL 32
|
#define N_HISTOGRAM_BINS_SMALL 32
|
||||||
|
|
@ -3395,6 +3399,7 @@ struct thread_state_word {
|
||||||
;; autogenerated files can use full paths to other inclusions
|
;; autogenerated files can use full paths to other inclusions
|
||||||
;; (in case your build system disfavors use of -I compiler options)
|
;; (in case your build system disfavors use of -I compiler options)
|
||||||
(namestring (merge-pathnames "gencgc-alloc-region.h" (lispobj-dot-h)))
|
(namestring (merge-pathnames "gencgc-alloc-region.h" (lispobj-dot-h)))
|
||||||
|
(namestring (merge-pathnames "tiny-lock.h" (lispobj-dot-h)))
|
||||||
#+64-bit " char padding[4];" #-64-bit ""))
|
#+64-bit " char padding[4];" #-64-bit ""))
|
||||||
|
|
||||||
(defun write-weak-pointer-manipulators ()
|
(defun write-weak-pointer-manipulators ()
|
||||||
|
|
|
||||||
|
|
@ -553,7 +553,7 @@ during backtrace.
|
||||||
;; of a symbol is initialized to zero
|
;; of a symbol is initialized to zero
|
||||||
(no-tls-value-marker)
|
(no-tls-value-marker)
|
||||||
|
|
||||||
(stepping)
|
(stepping :c-type "struct {int step; int suspend;}")
|
||||||
|
|
||||||
;; Keep this first bunch of slots from binding-stack-pointer through alloc-region
|
;; Keep this first bunch of slots from binding-stack-pointer through alloc-region
|
||||||
;; near the beginning of the structure so that x86[-64] assembly code
|
;; near the beginning of the structure so that x86[-64] assembly code
|
||||||
|
|
@ -616,6 +616,9 @@ during backtrace.
|
||||||
;; a struct containing {starting, running, suspended, dead}
|
;; a struct containing {starting, running, suspended, dead}
|
||||||
;; and some other state fields.
|
;; and some other state fields.
|
||||||
(state-word :c-type "struct thread_state_word")
|
(state-word :c-type "struct thread_state_word")
|
||||||
|
(yieldpoint-spinlock :c-type "lock_t")
|
||||||
|
(yieldpoint-page-access)
|
||||||
|
(interrupt-reason)
|
||||||
;; Statistical CPU profiler data recording buffer
|
;; Statistical CPU profiler data recording buffer
|
||||||
(sprof-data)
|
(sprof-data)
|
||||||
;;
|
;;
|
||||||
|
|
@ -647,6 +650,13 @@ during backtrace.
|
||||||
(symbol-tlab :c-type "struct alloc_region" :length 3)
|
(symbol-tlab :c-type "struct alloc_region" :length 3)
|
||||||
(sys-mixed-tlab :c-type "struct alloc_region" :length 3)
|
(sys-mixed-tlab :c-type "struct alloc_region" :length 3)
|
||||||
(sys-cons-tlab :c-type "struct alloc_region" :length 3)
|
(sys-cons-tlab :c-type "struct alloc_region" :length 3)
|
||||||
|
(spill-cons-tlab-end)
|
||||||
|
(spill-mixed-tlab-end)
|
||||||
|
(spill-sys-cons-tlab-end)
|
||||||
|
(spill-sys-mixed-tlab-end)
|
||||||
|
(lisp-yps-executed)
|
||||||
|
(c-yps-executed)
|
||||||
|
;;(spill-boxed-tlab-end)
|
||||||
;; allocation instrumenting
|
;; allocation instrumenting
|
||||||
(tot-bytes-alloc-boxed)
|
(tot-bytes-alloc-boxed)
|
||||||
(tot-bytes-alloc-unboxed)
|
(tot-bytes-alloc-unboxed)
|
||||||
|
|
|
||||||
|
|
@ -251,13 +251,14 @@
|
||||||
;; the C function is in a dynamic shared object or not)
|
;; the C function is in a dynamic shared object or not)
|
||||||
+required-foreign-symbols+
|
+required-foreign-symbols+
|
||||||
|
|
||||||
|
;; arbitrary object that changes after each GC
|
||||||
|
sb-kernel::*gc-epoch*
|
||||||
|
|
||||||
;;; The following symbols aren't strictly required to be static
|
;;; The following symbols aren't strictly required to be static
|
||||||
;;; - they are not accessed from C - but we make them static in order
|
;;; - they are not accessed from C - but we make them static in order
|
||||||
;;; to (perhaps) micro-optimize access in Lisp.
|
;;; to (perhaps) micro-optimize access in Lisp.
|
||||||
;;; However there is no efficiency gain if we have #+immobile-space.
|
;;; However there is no efficiency gain if we have #+immobile-space.
|
||||||
#-immobile-space ,@'(
|
#-immobile-space ,@'(
|
||||||
;; arbitrary object that changes after each GC
|
|
||||||
sb-kernel::*gc-epoch*
|
|
||||||
;; Dispatch tables for generic array access
|
;; Dispatch tables for generic array access
|
||||||
%%data-vector-reffers%%
|
%%data-vector-reffers%%
|
||||||
%%data-vector-reffers/check-bounds%%
|
%%data-vector-reffers/check-bounds%%
|
||||||
|
|
@ -366,10 +367,10 @@
|
||||||
(defconstant thread-header-slots
|
(defconstant thread-header-slots
|
||||||
;; This seems to need to be an even number.
|
;; This seems to need to be an even number.
|
||||||
;; I'm not sure what the constraint on that stems from.
|
;; I'm not sure what the constraint on that stems from.
|
||||||
#+(and x86-64 sb-safepoint) 14 ; the safepoint trap page is at word index -15
|
#+x86-64 (or #+(or sb-safepoint yieldpoints) 14 ; the safepoint trap page is at word index -15
|
||||||
#+(and x86-64 (not sb-safepoint)) 16
|
16)
|
||||||
#+(and (not x86-64) immobile-space) 14 ; the safepoint trap page is at word index -15
|
#-x86-64 (or #+immobile-space 14 ; the safepoint trap page is at word index -15
|
||||||
#+(and (not x86-64) (not immobile-space)) 0)
|
0))
|
||||||
|
|
||||||
(progn
|
(progn
|
||||||
(defconstant +highest-normal-generation+ 5)
|
(defconstant +highest-normal-generation+ 5)
|
||||||
|
|
|
||||||
|
|
@ -529,7 +529,7 @@
|
||||||
;; occurs at the end. In that case, we can not prevent stop-for-GC
|
;; occurs at the end. In that case, we can not prevent stop-for-GC
|
||||||
;; from occurring in the C code, because foreign code is allowed
|
;; from occurring in the C code, because foreign code is allowed
|
||||||
;; to run during GC; it just can't go back into Lisp until GC is over.
|
;; to run during GC; it just can't go back into Lisp until GC is over.
|
||||||
#-sb-safepoint
|
#-(or sb-safepoint yieldpoints)
|
||||||
(loop for e = (sb-c::node-lexenv (sb-c::vop-node vop))
|
(loop for e = (sb-c::node-lexenv (sb-c::vop-node vop))
|
||||||
then (sb-c::lexenv-parent e)
|
then (sb-c::lexenv-parent e)
|
||||||
while e
|
while e
|
||||||
|
|
|
||||||
|
|
@ -241,6 +241,7 @@
|
||||||
#+(or arm64 riscv x86 x86-64)
|
#+(or arm64 riscv x86 x86-64)
|
||||||
(defknown %raw-instance-cas/signed-word (instance index sb-vm:signed-word sb-vm:signed-word)
|
(defknown %raw-instance-cas/signed-word (instance index sb-vm:signed-word sb-vm:signed-word)
|
||||||
sb-vm:signed-word ())
|
sb-vm:signed-word ())
|
||||||
|
(defknown %raw-instance-xchg/byte (instance index (unsigned-byte 8)) (unsigned-byte 8) ())
|
||||||
(defknown %raw-instance-xchg/word (instance index sb-vm:word) sb-vm:word ())
|
(defknown %raw-instance-xchg/word (instance index sb-vm:word) sb-vm:word ())
|
||||||
|
|
||||||
(macrolet ((define-raw-slot-defknowns ()
|
(macrolet ((define-raw-slot-defknowns ()
|
||||||
|
|
|
||||||
|
|
@ -1230,7 +1230,7 @@
|
||||||
;; but the slot setters don't all have to be updated to understand how to
|
;; but the slot setters don't all have to be updated to understand how to
|
||||||
;; terminate the pseudo-atomic sequence. It's a separate vop to do that.
|
;; terminate the pseudo-atomic sequence. It's a separate vop to do that.
|
||||||
(emit-and-insert-vop (vop-node last) (vop-block last)
|
(emit-and-insert-vop (vop-node last) (vop-block last)
|
||||||
(template-or-lose 'end-pseudo-atomic)
|
(template-or-lose 'end-allocation)
|
||||||
nil nil (vop-next last)))
|
nil nil (vop-next last)))
|
||||||
(process-closure-inits (vop)
|
(process-closure-inits (vop)
|
||||||
(let* ((result-ref (vop-results vop))
|
(let* ((result-ref (vop-results vop))
|
||||||
|
|
|
||||||
|
|
@ -1635,7 +1635,7 @@
|
||||||
(let ((lab (gen-label)))
|
(let ((lab (gen-label)))
|
||||||
(setf (ir2-environment-environment-start env) lab)
|
(setf (ir2-environment-environment-start env) lab)
|
||||||
(vop note-environment-start node block lab)
|
(vop note-environment-start node block lab)
|
||||||
#+sb-safepoint
|
#+(or sb-safepoint yieldpoints)
|
||||||
(when (policy fun (/= insert-safepoints 0))
|
(when (policy fun (/= insert-safepoints 0))
|
||||||
(vop sb-vm::insert-safepoint node block))))
|
(vop sb-vm::insert-safepoint node block))))
|
||||||
|
|
||||||
|
|
@ -2362,7 +2362,7 @@
|
||||||
2block
|
2block
|
||||||
*dynamic-counts-tn*
|
*dynamic-counts-tn*
|
||||||
num))))
|
num))))
|
||||||
#+sb-safepoint
|
#+(or sb-safepoint yieldpoints)
|
||||||
(let ((first-node (block-start-node block)))
|
(let ((first-node (block-start-node block)))
|
||||||
(unless (or (and (bind-p first-node)
|
(unless (or (and (bind-p first-node)
|
||||||
;; Bind-nodes already have safepoints
|
;; Bind-nodes already have safepoints
|
||||||
|
|
|
||||||
|
|
@ -142,7 +142,6 @@ debugger.")
|
||||||
1
|
1
|
||||||
("no" "no" "yes" "yes"))
|
("no" "no" "yes" "yes"))
|
||||||
|
|
||||||
#+sb-safepoint
|
|
||||||
(define-optimization-quality insert-safepoints
|
(define-optimization-quality insert-safepoints
|
||||||
1
|
1
|
||||||
("no" "yes" "yes" "yes")
|
("no" "yes" "yes" "yes")
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@
|
||||||
(:generator 0
|
(:generator 0
|
||||||
(emit-label the-label)))
|
(emit-label the-label)))
|
||||||
|
|
||||||
#+sb-safepoint
|
#+(or sb-safepoint yieldpoints)
|
||||||
(define-vop (sb-vm::insert-safepoint)
|
(define-vop (sb-vm::insert-safepoint)
|
||||||
(:policy :fast-safe)
|
(:policy :fast-safe)
|
||||||
(:translate sb-kernel::gc-safepoint)
|
(:translate sb-kernel::gc-safepoint)
|
||||||
|
|
|
||||||
|
|
@ -185,8 +185,9 @@
|
||||||
;;; the allocator to use cons_tlab.
|
;;; the allocator to use cons_tlab.
|
||||||
(defconstant +cons-primtype+ list-pointer-lowtag)
|
(defconstant +cons-primtype+ list-pointer-lowtag)
|
||||||
|
|
||||||
(define-vop (sb-c::end-pseudo-atomic)
|
(define-vop (sb-c::end-allocation)
|
||||||
(:generator 1 (emit-end-pseudo-atomic)))
|
(:generator 1
|
||||||
|
#-sb-safepoint (emit-end-pseudo-atomic)))
|
||||||
|
|
||||||
;;; Emit code to allocate an object with a size in bytes given by
|
;;; Emit code to allocate an object with a size in bytes given by
|
||||||
;;; SIZE into ALLOC-TN. The size may be an integer of a TN.
|
;;; SIZE into ALLOC-TN. The size may be an integer of a TN.
|
||||||
|
|
@ -249,7 +250,7 @@
|
||||||
;; alloc-tn <- old free ptr and temp <- new free ptr
|
;; alloc-tn <- old free ptr and temp <- new free ptr
|
||||||
(inst xadd temp alloc-tn)
|
(inst xadd temp alloc-tn)
|
||||||
(inst cmp temp end-addr)
|
(inst cmp temp end-addr)
|
||||||
(inst jmp :a NOT-INLINE)
|
(note-yieldpoint (inst jmp :a NOT-INLINE))
|
||||||
(inst mov free-pointer temp)
|
(inst mov free-pointer temp)
|
||||||
(emit-label DONE)
|
(emit-label DONE)
|
||||||
(when (/= lowtag 0) (inst or :byte alloc-tn lowtag))
|
(when (/= lowtag 0) (inst or :byte alloc-tn lowtag))
|
||||||
|
|
@ -268,14 +269,14 @@
|
||||||
(when (tn-p size) (aver (not (location= size temp))))
|
(when (tn-p size) (aver (not (location= size temp))))
|
||||||
(inst lea temp (ea size alloc-tn))
|
(inst lea temp (ea size alloc-tn))
|
||||||
(inst cmp temp end-addr)
|
(inst cmp temp end-addr)
|
||||||
(inst jmp :a NOT-INLINE)
|
(note-yieldpoint (inst jmp :a NOT-INLINE))
|
||||||
(inst mov free-pointer temp)
|
(inst mov free-pointer temp)
|
||||||
(emit-label DONE)
|
(emit-label DONE)
|
||||||
(when (/= lowtag 0) (inst or :byte alloc-tn lowtag)))
|
(when (/= lowtag 0) (inst or :byte alloc-tn lowtag)))
|
||||||
(t
|
(t
|
||||||
(inst add alloc-tn size)
|
(inst add alloc-tn size)
|
||||||
(inst cmp alloc-tn end-addr)
|
(inst cmp alloc-tn end-addr)
|
||||||
(inst jmp :a NOT-INLINE)
|
(note-yieldpoint (inst jmp :a NOT-INLINE))
|
||||||
(inst mov free-pointer alloc-tn)
|
(inst mov free-pointer alloc-tn)
|
||||||
(cond ((tn-p size)
|
(cond ((tn-p size)
|
||||||
(inst sub alloc-tn size)
|
(inst sub alloc-tn size)
|
||||||
|
|
@ -310,7 +311,7 @@
|
||||||
(instrument-alloc widetag bytes node (cons result-tn (ensure-list alloc-temps)) thread-temp)
|
(instrument-alloc widetag bytes node (cons result-tn (ensure-list alloc-temps)) thread-temp)
|
||||||
(let ((header (compute-object-header nwords widetag))
|
(let ((header (compute-object-header nwords widetag))
|
||||||
(alloc-temp (if (listp alloc-temps) (car alloc-temps) alloc-temps)))
|
(alloc-temp (if (listp alloc-temps) (car alloc-temps) alloc-temps)))
|
||||||
(pseudo-atomic ()
|
(with-allocator ()
|
||||||
(cond (alloc-temp
|
(cond (alloc-temp
|
||||||
(allocation widetag bytes 0 result-tn node alloc-temp thread-temp)
|
(allocation widetag bytes 0 result-tn node alloc-temp thread-temp)
|
||||||
(storew* header result-tn 0 0 t)
|
(storew* header result-tn 0 0 t)
|
||||||
|
|
@ -413,7 +414,7 @@
|
||||||
(zeroed #+mark-region-gc t)
|
(zeroed #+mark-region-gc t)
|
||||||
(prev-constant temp)) ;; a non-eq initial value
|
(prev-constant temp)) ;; a non-eq initial value
|
||||||
(instrument-alloc +cons-primtype+ nbytes node (list temp alloc) thread-tn)
|
(instrument-alloc +cons-primtype+ nbytes node (list temp alloc) thread-tn)
|
||||||
(pseudo-atomic (:thread-tn thread-tn)
|
(with-allocator (:thread-tn thread-tn)
|
||||||
(allocation +cons-primtype+ nbytes 0 alloc node temp thread-tn)
|
(allocation +cons-primtype+ nbytes 0 alloc node temp thread-tn)
|
||||||
(store-slot car alloc cons-car-slot 0)
|
(store-slot car alloc cons-car-slot 0)
|
||||||
(store-slot cdr alloc cons-cdr-slot 0)
|
(store-slot cdr alloc cons-cdr-slot 0)
|
||||||
|
|
@ -437,7 +438,7 @@
|
||||||
(zeroed #+mark-region-gc t)
|
(zeroed #+mark-region-gc t)
|
||||||
(prev-constant temp))
|
(prev-constant temp))
|
||||||
(instrument-alloc +cons-primtype+ nbytes node (list temp alloc) thread-tn)
|
(instrument-alloc +cons-primtype+ nbytes node (list temp alloc) thread-tn)
|
||||||
(pseudo-atomic (:thread-tn thread-tn)
|
(with-allocator (:thread-tn thread-tn)
|
||||||
(allocation +cons-primtype+ nbytes 0 alloc node temp thread-tn)
|
(allocation +cons-primtype+ nbytes 0 alloc node temp thread-tn)
|
||||||
(store-slot tail alloc cons-cdr-slot 0)
|
(store-slot tail alloc cons-cdr-slot 0)
|
||||||
(inst lea temp (ea (+ 16 list-pointer-lowtag) alloc))
|
(inst lea temp (ea (+ 16 list-pointer-lowtag) alloc))
|
||||||
|
|
@ -482,7 +483,7 @@
|
||||||
(zeroed #+mark-region-gc t)
|
(zeroed #+mark-region-gc t)
|
||||||
(prev-constant temp))
|
(prev-constant temp))
|
||||||
(instrument-alloc +cons-primtype+ nbytes node (list temp alloc) thread-tn)
|
(instrument-alloc +cons-primtype+ nbytes node (list temp alloc) thread-tn)
|
||||||
(pseudo-atomic (:thread-tn thread-tn)
|
(with-allocator (:thread-tn thread-tn)
|
||||||
(allocation +cons-primtype+ nbytes 0 alloc node temp thread-tn)
|
(allocation +cons-primtype+ nbytes 0 alloc node temp thread-tn)
|
||||||
(store-slot car alloc cons-car-slot 0)
|
(store-slot car alloc cons-car-slot 0)
|
||||||
(store-slot cadr alloc (+ 2 cons-car-slot) 0)
|
(store-slot cadr alloc (+ 2 cons-car-slot) 0)
|
||||||
|
|
@ -509,7 +510,7 @@
|
||||||
(prev-constant temp))
|
(prev-constant temp))
|
||||||
(unless stack-allocate-p
|
(unless stack-allocate-p
|
||||||
(instrument-alloc +cons-primtype+ size node (list ptr temp) thread-tn))
|
(instrument-alloc +cons-primtype+ size node (list ptr temp) thread-tn))
|
||||||
(pseudo-atomic (:elide-if stack-allocate-p :thread-tn thread-tn)
|
(with-allocator (:elide-if stack-allocate-p :thread-tn thread-tn)
|
||||||
(if stack-allocate-p
|
(if stack-allocate-p
|
||||||
(stack-allocation size list-pointer-lowtag res)
|
(stack-allocation size list-pointer-lowtag res)
|
||||||
(allocation +cons-primtype+ size list-pointer-lowtag res node temp thread-tn))
|
(allocation +cons-primtype+ size list-pointer-lowtag res node temp thread-tn))
|
||||||
|
|
@ -676,7 +677,7 @@
|
||||||
;; It would be possible to do this and the array proper
|
;; It would be possible to do this and the array proper
|
||||||
;; in a single pseudo-atomic section, but I don't care to do that.
|
;; in a single pseudo-atomic section, but I don't care to do that.
|
||||||
(let ((nbytes (calc-shadow-bits-size result)))
|
(let ((nbytes (calc-shadow-bits-size result)))
|
||||||
(pseudo-atomic ()
|
(with-allocator ()
|
||||||
;; Allocate the bits into RESULT
|
;; Allocate the bits into RESULT
|
||||||
(allocation simple-bit-vector-widetag nbytes 0 result node temp nil)
|
(allocation simple-bit-vector-widetag nbytes 0 result node temp nil)
|
||||||
(inst mov :byte (ea result) simple-bit-vector-widetag)
|
(inst mov :byte (ea result) simple-bit-vector-widetag)
|
||||||
|
|
@ -715,7 +716,7 @@
|
||||||
(t 'unboxed-array))
|
(t 'unboxed-array))
|
||||||
type)
|
type)
|
||||||
size-tn node instrumentation-temp thread-tn)
|
size-tn node instrumentation-temp thread-tn)
|
||||||
(pseudo-atomic (:thread-tn thread-tn)
|
(with-allocator (:thread-tn thread-tn)
|
||||||
(allocation type size-tn 0 result node alloc-temp thread-tn)
|
(allocation type size-tn 0 result node alloc-temp thread-tn)
|
||||||
(put-header result 0 type length t alloc-temp)
|
(put-header result 0 type length t alloc-temp)
|
||||||
(inst or :byte result other-pointer-lowtag)))
|
(inst or :byte result other-pointer-lowtag)))
|
||||||
|
|
@ -901,7 +902,7 @@
|
||||||
(loop (gen-label))
|
(loop (gen-label))
|
||||||
(leave-pa (gen-label)))
|
(leave-pa (gen-label)))
|
||||||
(instrument-alloc +cons-primtype+ size node (list next limit) thread-tn)
|
(instrument-alloc +cons-primtype+ size node (list next limit) thread-tn)
|
||||||
(pseudo-atomic (:thread-tn thread-tn)
|
(with-allocator (:thread-tn thread-tn)
|
||||||
(allocation +cons-primtype+ size list-pointer-lowtag result node limit thread-tn
|
(allocation +cons-primtype+ size list-pointer-lowtag result node limit thread-tn
|
||||||
:overflow
|
:overflow
|
||||||
(lambda ()
|
(lambda ()
|
||||||
|
|
@ -957,10 +958,11 @@
|
||||||
(header (logior (ash (1- words) n-widetag-bits) closure-widetag))
|
(header (logior (ash (1- words) n-widetag-bits) closure-widetag))
|
||||||
(remain-pseudo-atomic
|
(remain-pseudo-atomic
|
||||||
(eq (car (last (vop-codegen-info vop))) :pseudo-atomic)))
|
(eq (car (last (vop-codegen-info vop))) :pseudo-atomic)))
|
||||||
|
(declare (ignorable remain-pseudo-atomic))
|
||||||
(unless stack-allocate-p
|
(unless stack-allocate-p
|
||||||
(instrument-alloc closure-widetag bytes node (list result temp) thread-tn))
|
(instrument-alloc closure-widetag bytes node (list result temp) thread-tn))
|
||||||
(pseudo-atomic (:default-exit (not remain-pseudo-atomic)
|
(with-allocator (:default-exit (not remain-pseudo-atomic)
|
||||||
:elide-if stack-allocate-p :thread-tn thread-tn)
|
:elide-if stack-allocate-p :thread-tn thread-tn)
|
||||||
(if stack-allocate-p
|
(if stack-allocate-p
|
||||||
(stack-allocation bytes fun-pointer-lowtag result stack-allocate-p)
|
(stack-allocation bytes fun-pointer-lowtag result stack-allocate-p)
|
||||||
(allocation closure-widetag bytes fun-pointer-lowtag result node temp thread-tn))
|
(allocation closure-widetag bytes fun-pointer-lowtag result node temp thread-tn))
|
||||||
|
|
@ -1030,13 +1032,14 @@
|
||||||
&aux (bytes (pad-data-block words))
|
&aux (bytes (pad-data-block words))
|
||||||
(remain-pseudo-atomic
|
(remain-pseudo-atomic
|
||||||
(eq (car (last (vop-codegen-info vop))) :pseudo-atomic)))
|
(eq (car (last (vop-codegen-info vop))) :pseudo-atomic)))
|
||||||
|
(declare (ignorable remain-pseudo-atomic))
|
||||||
#+bignum-assertions
|
#+bignum-assertions
|
||||||
(when (eq type bignum-widetag) (setq bytes (* bytes 2))) ; use 2x the space
|
(when (eq type bignum-widetag) (setq bytes (* bytes 2))) ; use 2x the space
|
||||||
(progn name) ; possibly not used
|
(progn name) ; possibly not used
|
||||||
(unless stack-allocate-p
|
(unless stack-allocate-p
|
||||||
(instrument-alloc type bytes node (list result alloc-temp) thread-tn))
|
(instrument-alloc type bytes node (list result alloc-temp) thread-tn))
|
||||||
(pseudo-atomic (:default-exit (not remain-pseudo-atomic)
|
(with-allocator (:default-exit (not remain-pseudo-atomic)
|
||||||
:elide-if stack-allocate-p :thread-tn thread-tn)
|
:elide-if stack-allocate-p :thread-tn thread-tn)
|
||||||
;; If storing a header word, defer ORing in the lowtag until after
|
;; If storing a header word, defer ORing in the lowtag until after
|
||||||
;; the header is written so that displacement can be 0.
|
;; the header is written so that displacement can be 0.
|
||||||
(cond (stack-allocate-p
|
(cond (stack-allocate-p
|
||||||
|
|
@ -1106,6 +1109,7 @@
|
||||||
;; But it seems we never need this! (so is it FIXME or isn't it?)
|
;; But it seems we never need this! (so is it FIXME or isn't it?)
|
||||||
(error "can't %MAKE-FUNCALLABLE-INSTANCE of unknown length"))
|
(error "can't %MAKE-FUNCALLABLE-INSTANCE of unknown length"))
|
||||||
(let ((remain-pseudo-atomic (eq (car (last (vop-codegen-info vop))) :pseudo-atomic)))
|
(let ((remain-pseudo-atomic (eq (car (last (vop-codegen-info vop))) :pseudo-atomic)))
|
||||||
|
(declare (ignorable remain-pseudo-atomic))
|
||||||
;; With the exception of bignums, these objects have effectively
|
;; With the exception of bignums, these objects have effectively
|
||||||
;; 32-bit headers because the high 4 byes contain a layout pointer.
|
;; 32-bit headers because the high 4 byes contain a layout pointer.
|
||||||
(let ((operand-size (if (= type bignum-widetag) :qword :dword)))
|
(let ((operand-size (if (= type bignum-widetag) :qword :dword)))
|
||||||
|
|
@ -1127,8 +1131,8 @@
|
||||||
;; because it might be in the same physical reg as BYTES.
|
;; because it might be in the same physical reg as BYTES.
|
||||||
;; Yup, the lifetime specs in this vop are pretty confusing.
|
;; Yup, the lifetime specs in this vop are pretty confusing.
|
||||||
(instrument-alloc type bytes node alloc-temp thread-tn)
|
(instrument-alloc type bytes node alloc-temp thread-tn)
|
||||||
(pseudo-atomic (:default-exit (not remain-pseudo-atomic)
|
(with-allocator (:default-exit (not remain-pseudo-atomic)
|
||||||
:thread-tn thread-tn)
|
:thread-tn thread-tn)
|
||||||
(allocation type bytes lowtag result node alloc-temp thread-tn)
|
(allocation type bytes lowtag result node alloc-temp thread-tn)
|
||||||
(storew header result 0 lowtag)))))))
|
(storew header result 0 lowtag)))))))
|
||||||
|
|
||||||
|
|
@ -1149,7 +1153,7 @@
|
||||||
(move c-arg-1 total-words)
|
(move c-arg-1 total-words)
|
||||||
(move c-arg-2 boxed-words)
|
(move c-arg-2 boxed-words)
|
||||||
(with-registers-preserved (c :except #-win32 rdi #+win32 rcx :frame-reg r15)
|
(with-registers-preserved (c :except #-win32 rdi #+win32 rcx :frame-reg r15)
|
||||||
(pseudo-atomic () (call-c "alloc_code_object"))
|
(with-allocator () (call-c "alloc_code_object"))
|
||||||
(move c-arg-1 rax-tn))
|
(move c-arg-1 rax-tn))
|
||||||
(move res c-arg-1)))
|
(move res c-arg-1)))
|
||||||
|
|
||||||
|
|
@ -1178,7 +1182,7 @@
|
||||||
;; RSP needn't be restored because the allocators all return immediately
|
;; RSP needn't be restored because the allocators all return immediately
|
||||||
;; which has that effect
|
;; which has that effect
|
||||||
(inst and rsp-tn -16)
|
(inst and rsp-tn -16)
|
||||||
(pseudo-atomic ()
|
(with-allocator ()
|
||||||
(call-c (c-fun "alloc_immobile_fixedobj"))
|
(call-c (c-fun "alloc_immobile_fixedobj"))
|
||||||
(move result rax))))
|
(move result rax))))
|
||||||
|
|
||||||
|
|
@ -1215,7 +1219,7 @@
|
||||||
;; because it is no longer a page of symbols but rather a free page.
|
;; because it is no longer a page of symbols but rather a free page.
|
||||||
;; There is no way to inform GC that we are currently looking at a page
|
;; There is no way to inform GC that we are currently looking at a page
|
||||||
;; in anticipation of allocating to it.
|
;; in anticipation of allocating to it.
|
||||||
(pseudo-atomic ()
|
(with-allocator ()
|
||||||
(inst mov :dword rax (ea 4 rax)) ; rax := fixedobj_page_hint[1] (sizeclass=SYMBOL)
|
(inst mov :dword rax (ea 4 rax)) ; rax := fixedobj_page_hint[1] (sizeclass=SYMBOL)
|
||||||
(inst test :dword rax rax)
|
(inst test :dword rax rax)
|
||||||
(inst jmp :z FAIL) ; fail if hint page is 0
|
(inst jmp :z FAIL) ; fail if hint page is 0
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,7 @@
|
||||||
(inst or :dword header type)
|
(inst or :dword header type)
|
||||||
(inst shr :dword header n-fixnum-tag-bits)
|
(inst shr :dword header n-fixnum-tag-bits)
|
||||||
(instrument-alloc nil bytes node temp thread-tn)
|
(instrument-alloc nil bytes node temp thread-tn)
|
||||||
(pseudo-atomic (:thread-tn thread-tn)
|
(with-allocator (:thread-tn thread-tn)
|
||||||
(allocation type bytes 0 result node temp thread-tn)
|
(allocation type bytes 0 result node temp thread-tn)
|
||||||
(storew header result 0 0)
|
(storew header result 0 0)
|
||||||
(inst or :byte result other-pointer-lowtag))))
|
(inst or :byte result other-pointer-lowtag))))
|
||||||
|
|
|
||||||
|
|
@ -264,17 +264,14 @@
|
||||||
(inst mov res (thread-slot-ea thread-alien-linkage-table-base-slot))
|
(inst mov res (thread-slot-ea thread-alien-linkage-table-base-slot))
|
||||||
(inst mov res (ea (make-fixup foreign-symbol :alien-data-linkage-index) res))))))
|
(inst mov res (ea (make-fixup foreign-symbol :alien-data-linkage-index) res))))))
|
||||||
|
|
||||||
#+sb-safepoint
|
|
||||||
(defconstant thread-saved-csp-offset (- (1+ sb-vm::thread-header-slots)))
|
|
||||||
|
|
||||||
(eval-when (#-sb-xc :compile-toplevel :load-toplevel :execute)
|
(eval-when (#-sb-xc :compile-toplevel :load-toplevel :execute)
|
||||||
(defun destroyed-c-registers ()
|
(defun destroyed-c-registers ()
|
||||||
;; Safepoints do not save interrupt contexts to be scanned during
|
;; Safepoints do not save interrupt contexts to be scanned during
|
||||||
;; GCing, it only looks at the stack, so if a register isn't
|
;; GCing, it only looks at the stack, so if a register isn't
|
||||||
;; spilled it won't be visible to the GC.
|
;; spilled it won't be visible to the GC.
|
||||||
#+sb-safepoint
|
#+(or sb-safepoint yieldpoints)
|
||||||
'((:save-p t))
|
'((:save-p t))
|
||||||
#-sb-safepoint
|
#-(or sb-safepoint yieldpoints)
|
||||||
(let ((gprs (list '#:rcx '#:rdx #-win32 '#:rsi #-win32 '#:rdi
|
(let ((gprs (list '#:rcx '#:rdx #-win32 '#:rsi #-win32 '#:rdi
|
||||||
'#:r8 '#:r9 '#:r10 '#:r11))
|
'#:r8 '#:r9 '#:r10 '#:r11))
|
||||||
(vars))
|
(vars))
|
||||||
|
|
@ -389,8 +386,8 @@
|
||||||
;; the UNDEFINED-ALIEN-TRAMP lisp asm routine to recognize the various shapes
|
;; the UNDEFINED-ALIEN-TRAMP lisp asm routine to recognize the various shapes
|
||||||
;; this instruction sequence can take.
|
;; this instruction sequence can take.
|
||||||
#-win32
|
#-win32
|
||||||
(pseudo-atomic (:elide-if (not (call-out-pseudo-atomic-p vop)))
|
(let ((operand
|
||||||
(inst call (if (tn-p fun)
|
(if (tn-p fun)
|
||||||
fun
|
fun
|
||||||
#-immobile-space (ea (make-fixup fun :foreign 8))
|
#-immobile-space (ea (make-fixup fun :foreign 8))
|
||||||
#+immobile-space
|
#+immobile-space
|
||||||
|
|
@ -401,6 +398,24 @@
|
||||||
;; spilled by Lisp because a C function has to save it if used)
|
;; spilled by Lisp because a C function has to save it if used)
|
||||||
(inst mov r10-tn (thread-slot-ea thread-alien-linkage-table-base-slot))
|
(inst mov r10-tn (thread-slot-ea thread-alien-linkage-table-base-slot))
|
||||||
(ea (make-fixup fun :alien-code-linkage-index 8) r10-tn))))))
|
(ea (make-fixup fun :alien-code-linkage-index 8) r10-tn))))))
|
||||||
|
(cond ((or (call-out-pseudo-atomic-p vop)
|
||||||
|
(policy (sb-c::vop-node vop) (= sb-c:insert-safepoints 0)))
|
||||||
|
#+yieldpoints (inst call operand)
|
||||||
|
#-yieldpoints (pseudo-atomic () (inst call operand)))
|
||||||
|
#+yieldpoints
|
||||||
|
(t
|
||||||
|
;; GC can run without pausing this thread so long as the thread indicates that
|
||||||
|
;; it is in foreign code by the saved-csp being nonzero. The PC of the call site
|
||||||
|
;; can be read from the word below the stack pointer that is saved here,
|
||||||
|
;; which is important if that PC needs to pin a Lisp function.
|
||||||
|
;; Possibly TODO: the pre/post-call instructions could be incorporated into the
|
||||||
|
;; linkage table entry.
|
||||||
|
(inst yieldpoint :call-out)
|
||||||
|
(inst call operand)
|
||||||
|
(inst yieldpoint :call-out-done))
|
||||||
|
#-yieldpoints
|
||||||
|
(t
|
||||||
|
(inst call operand))))
|
||||||
|
|
||||||
;; On win64, we don't support immobile space (yet) and calls go through one of
|
;; On win64, we don't support immobile space (yet) and calls go through one of
|
||||||
;; the thunks defined in set_up_win64_seh_data(). If the linkage table is
|
;; the thunks defined in set_up_win64_seh_data(). If the linkage table is
|
||||||
|
|
|
||||||
|
|
@ -887,7 +887,8 @@
|
||||||
(when (static-fdefn-offset name)
|
(when (static-fdefn-offset name)
|
||||||
(let ((fixup (make-fixup name :static-call)))
|
(let ((fixup (make-fixup name :static-call)))
|
||||||
(return-from emit-direct-call
|
(return-from emit-direct-call
|
||||||
(inst* instruction (if (sb-c::code-immobile-p node) fixup (ea fixup))))))
|
(note-yieldpoint (inst* instruction
|
||||||
|
(if (sb-c::code-immobile-p node) fixup (ea fixup)))))))
|
||||||
(let* ((fixup (make-fixup name :fdefn-call))
|
(let* ((fixup (make-fixup name :fdefn-call))
|
||||||
(target
|
(target
|
||||||
(if (and (sb-c::code-immobile-p node)
|
(if (and (sb-c::code-immobile-p node)
|
||||||
|
|
@ -902,7 +903,7 @@
|
||||||
;; RAX will get loaded regardless.
|
;; RAX will get loaded regardless.
|
||||||
(inst mov rax-tn fixup)
|
(inst mov rax-tn fixup)
|
||||||
rax-tn))))
|
rax-tn))))
|
||||||
(inst* instruction target)))
|
(note-yieldpoint (inst* instruction target))))
|
||||||
|
|
||||||
;;; Invoke the function-designator FUN.
|
;;; Invoke the function-designator FUN.
|
||||||
(defun tail-call-unnamed (fun type vop)
|
(defun tail-call-unnamed (fun type vop)
|
||||||
|
|
@ -1341,7 +1342,7 @@
|
||||||
(inst jmp :z done)))
|
(inst jmp :z done)))
|
||||||
(unless (node-stack-allocate-p node)
|
(unless (node-stack-allocate-p node)
|
||||||
(instrument-alloc +cons-primtype+ rcx node (list value dst) thread-tn))
|
(instrument-alloc +cons-primtype+ rcx node (list value dst) thread-tn))
|
||||||
(pseudo-atomic (:elide-if (node-stack-allocate-p node) :thread-tn thread-tn)
|
(with-allocator (:elide-if (node-stack-allocate-p node) :thread-tn thread-tn)
|
||||||
;; Produce an untagged pointer into DST
|
;; Produce an untagged pointer into DST
|
||||||
(if (node-stack-allocate-p node)
|
(if (node-stack-allocate-p node)
|
||||||
(stack-allocation rcx 0 dst)
|
(stack-allocation rcx 0 dst)
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@
|
||||||
;; But funcallable-instances are on PAGE_TYPE_CODE, and code pages do not use
|
;; But funcallable-instances are on PAGE_TYPE_CODE, and code pages do not use
|
||||||
;; MMU-based protection regardless of this feature.
|
;; MMU-based protection regardless of this feature.
|
||||||
;; So we have to alter the card mark differently.
|
;; So we have to alter the card mark differently.
|
||||||
(pseudo-atomic ()
|
(pseudo-atomic () ; setting funinstance-fun
|
||||||
(emit-code-page-gengc-barrier object val-temp)
|
(emit-code-page-gengc-barrier object val-temp)
|
||||||
(emit-store (object-slot-ea object offset lowtag) value val-temp)))
|
(emit-store (object-slot-ea object offset lowtag) value val-temp)))
|
||||||
(t
|
(t
|
||||||
|
|
@ -507,7 +507,7 @@
|
||||||
;; refer to doc/internals-notes/fdefn-gc-safety
|
;; refer to doc/internals-notes/fdefn-gc-safety
|
||||||
;; No barrier here, because fdefns in immobile space rely on the SIGSEGV signal
|
;; No barrier here, because fdefns in immobile space rely on the SIGSEGV signal
|
||||||
;; to manage the card marks.
|
;; to manage the card marks.
|
||||||
(pseudo-atomic ()
|
(pseudo-atomic () ; setting fdefn-fun
|
||||||
(storew function fdefn fdefn-fun-slot other-pointer-lowtag)
|
(storew function fdefn fdefn-fun-slot other-pointer-lowtag)
|
||||||
(storew raw-word fdefn fdefn-raw-addr-slot other-pointer-lowtag)
|
(storew raw-word fdefn fdefn-raw-addr-slot other-pointer-lowtag)
|
||||||
;; Ensure that the header contains a JMP instruction, not INT3.
|
;; Ensure that the header contains a JMP instruction, not INT3.
|
||||||
|
|
@ -627,13 +627,7 @@
|
||||||
do
|
do
|
||||||
#+ultrafutex
|
#+ultrafutex
|
||||||
(when (eq symbol '*current-mutex*)
|
(when (eq symbol '*current-mutex*)
|
||||||
(let ((uncontested (gen-label)))
|
(invoke-asm-routine 'call 'mutex-unlock-if-held vop))
|
||||||
(inst mov temp tls-cell) ; load the current value
|
|
||||||
(inst mov :qword (mutex-slot temp %owner) 0)
|
|
||||||
(inst dec :lock :byte (mutex-slot temp state))
|
|
||||||
(inst jmp :z uncontested) ; if ZF then previous value was 1, no waiters
|
|
||||||
(invoke-asm-routine 'call 'mutex-wake-waiter vop)
|
|
||||||
(emit-label uncontested)))
|
|
||||||
|
|
||||||
(inst sub bsp (* binding-size n-word-bytes))
|
(inst sub bsp (* binding-size n-word-bytes))
|
||||||
|
|
||||||
|
|
@ -680,7 +674,7 @@
|
||||||
(let ((notmutex (gen-label)))
|
(let ((notmutex (gen-label)))
|
||||||
(inst cmp :dword symbol (make-fixup '*current-mutex* :symbol-tls-index))
|
(inst cmp :dword symbol (make-fixup '*current-mutex* :symbol-tls-index))
|
||||||
(inst jmp :ne notmutex)
|
(inst jmp :ne notmutex)
|
||||||
(inst call (ea (make-fixup 'mutex-unlock :assembly-routine*)))
|
(inst call (ea (make-fixup 'mutex-unlock-if-held :assembly-routine*)))
|
||||||
(emit-label notmutex))
|
(emit-label notmutex))
|
||||||
(inst test :dword symbol symbol))
|
(inst test :dword symbol symbol))
|
||||||
#-sb-thread
|
#-sb-thread
|
||||||
|
|
@ -744,7 +738,7 @@
|
||||||
(:generator 4
|
(:generator 4
|
||||||
(let ((ea (ea (- (* funcallable-instance-info-offset n-word-bytes) fun-pointer-lowtag)
|
(let ((ea (ea (- (* funcallable-instance-info-offset n-word-bytes) fun-pointer-lowtag)
|
||||||
object index (index-scale n-word-bytes index))))
|
object index (index-scale n-word-bytes index))))
|
||||||
(pseudo-atomic ()
|
(pseudo-atomic () ; setting funinstance-info
|
||||||
(emit-code-page-gengc-barrier object val-temp)
|
(emit-code-page-gengc-barrier object val-temp)
|
||||||
(emit-store ea value val-temp))))))
|
(emit-store ea value val-temp))))))
|
||||||
|
|
||||||
|
|
@ -895,9 +889,11 @@
|
||||||
instance-slots-offset instance-pointer-lowtag
|
instance-slots-offset instance-pointer-lowtag
|
||||||
(signed-reg) signed-num %raw-instance-cas/signed-word)
|
(signed-reg) signed-num %raw-instance-cas/signed-word)
|
||||||
|
|
||||||
(define-vop ()
|
(define-vop (%raw-instance-xchg/word)
|
||||||
(:translate %raw-instance-xchg/word)
|
(:translate %raw-instance-xchg/word)
|
||||||
(:policy :fast-safe)
|
(:policy :fast-safe)
|
||||||
|
(:variant-vars operand-size)
|
||||||
|
(:variant :qword)
|
||||||
(:args (instance :scs (descriptor-reg))
|
(:args (instance :scs (descriptor-reg))
|
||||||
(newval :scs (unsigned-reg immediate constant) :target result))
|
(newval :scs (unsigned-reg immediate constant) :target result))
|
||||||
(:info index)
|
(:info index)
|
||||||
|
|
@ -905,6 +901,7 @@
|
||||||
(:results (result :scs (unsigned-reg)))
|
(:results (result :scs (unsigned-reg)))
|
||||||
(:result-types unsigned-num)
|
(:result-types unsigned-num)
|
||||||
(:temporary (:sc unsigned-reg) temp)
|
(:temporary (:sc unsigned-reg) temp)
|
||||||
|
(:vop-var vop)
|
||||||
(:generator 3
|
(:generator 3
|
||||||
;; Use RESULT as the source of the exchange, unless doing so
|
;; Use RESULT as the source of the exchange, unless doing so
|
||||||
;; would clobber NEWVAL
|
;; would clobber NEWVAL
|
||||||
|
|
@ -912,11 +909,15 @@
|
||||||
(if (sc-is newval immediate)
|
(if (sc-is newval immediate)
|
||||||
(inst mov source (constantize (tn-value newval)))
|
(inst mov source (constantize (tn-value newval)))
|
||||||
(move source newval))
|
(move source newval))
|
||||||
(inst xchg (ea (- (ash (+ instance-slots-offset index) word-shift)
|
(inst xchg operand-size
|
||||||
instance-pointer-lowtag) instance)
|
(ea (- (ash (+ instance-slots-offset index) word-shift) instance-pointer-lowtag)
|
||||||
|
instance)
|
||||||
source)
|
source)
|
||||||
(unless (eq source result)
|
(unless (eq source result)
|
||||||
(move result temp)))))
|
(move result temp)))))
|
||||||
|
(define-vop (%raw-instance-xchg/byte %raw-instance-xchg/word)
|
||||||
|
(:translate %raw-instance-xchg/byte)
|
||||||
|
(:variant :byte))
|
||||||
|
|
||||||
;;;; code object frobbing
|
;;;; code object frobbing
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@
|
||||||
#+sb-simd-pack-256
|
#+sb-simd-pack-256
|
||||||
(import '(sb-vm::int-avx2-reg sb-vm::double-avx2-reg sb-vm::single-avx2-reg))
|
(import '(sb-vm::int-avx2-reg sb-vm::double-avx2-reg sb-vm::single-avx2-reg))
|
||||||
(import '(sb-vm::tn-byte-offset sb-vm::tn-reg sb-vm::reg-name
|
(import '(sb-vm::tn-byte-offset sb-vm::tn-reg sb-vm::reg-name
|
||||||
sb-vm::frame-byte-offset sb-vm::rip-tn sb-vm::rbp-tn
|
sb-vm::frame-byte-offset sb-vm::rip-tn sb-vm::rbp-tn sb-vm::rsp-tn
|
||||||
sb-vm::gpr-tn-p sb-vm::stack-tn-p sb-c::tn-reads sb-c::tn-writes
|
sb-vm::gpr-tn-p sb-vm::stack-tn-p sb-c::tn-reads sb-c::tn-writes
|
||||||
sb-vm::ymm-reg
|
sb-vm::ymm-reg
|
||||||
sb-vm::registers sb-vm::float-registers sb-vm::stack))) ; SB names
|
sb-vm::registers sb-vm::float-registers sb-vm::stack))) ; SB names
|
||||||
|
|
@ -1200,7 +1200,7 @@
|
||||||
(let ((ss (1- (integer-length scale)))
|
(let ((ss (1- (integer-length scale)))
|
||||||
(index (if (null index)
|
(index (if (null index)
|
||||||
#b100
|
#b100
|
||||||
(if (location= index sb-vm::rsp-tn)
|
(if (location= index rsp-tn)
|
||||||
(error "can't index off of RSP")
|
(error "can't index off of RSP")
|
||||||
(reg-encoding (if xmm-index
|
(reg-encoding (if xmm-index
|
||||||
(get-fpr :xmm (tn-offset index))
|
(get-fpr :xmm (tn-offset index))
|
||||||
|
|
@ -2356,6 +2356,32 @@
|
||||||
(:emitter
|
(:emitter
|
||||||
(emit-byte segment byte)))
|
(emit-byte segment byte)))
|
||||||
|
|
||||||
|
#+nil
|
||||||
|
(defun sb-c::emit-patchable-yieldpoint-nop (name) ; 4-byte NOP
|
||||||
|
(cond ((eql (string/= "TWO-WORD-BIGNUM" name) 15)
|
||||||
|
;; these routines need the carry flag so we don't want
|
||||||
|
;; to mess it up by doing a TEST instruction.
|
||||||
|
;; I guess we could emit the 6-byte MOV yieldpoint.
|
||||||
|
;; For the time being, I'm doing the yieldpoint at the end,
|
||||||
|
;; inside the routine, and not overwriting with a NOP.
|
||||||
|
)
|
||||||
|
((member name '(sb-vm::enable-alloc-counter ; not sure about this or the next
|
||||||
|
sb-vm::enable-sized-alloc-counter
|
||||||
|
sb-vm::undefined-tramp ; don't need one here
|
||||||
|
sb-vm::undefined-alien-tramp ; or here
|
||||||
|
sb-vm::handle-deferred-signal ; don't want
|
||||||
|
sb-vm::tail-call-variable ; don't need - calls a function
|
||||||
|
sb-vm::tail-call-callable-variable ; "
|
||||||
|
sb-vm::call-symbol ; "
|
||||||
|
sb-kernel:update-object-layout ; "
|
||||||
|
sb-impl::install-hash-table-lock ; "
|
||||||
|
sb-vm::fpr-save ; internal use only for other ASM routines
|
||||||
|
sb-vm::fpr-restore)) ; "
|
||||||
|
nil)
|
||||||
|
(t ; otherwise emit a 4-byte NOP
|
||||||
|
(dolist (b '(#x0f #x1f #x40 #x00))
|
||||||
|
(inst byte b)))))
|
||||||
|
|
||||||
;;; Compute the distance backward to the base address of the code component
|
;;; Compute the distance backward to the base address of the code component
|
||||||
;;; containing this simple-fun header, measured in words.
|
;;; containing this simple-fun header, measured in words.
|
||||||
(defun emit-header-data (segment type)
|
(defun emit-header-data (segment type)
|
||||||
|
|
@ -3587,6 +3613,138 @@
|
||||||
(delete-stmt next)
|
(delete-stmt next)
|
||||||
stmt)))
|
stmt)))
|
||||||
|
|
||||||
|
(defconstant-eqx yieldpoint-asm-routines
|
||||||
|
'(sb-c:return-multiple
|
||||||
|
sb-c:tail-call-variable
|
||||||
|
sb-vm::tail-call-callable-variable
|
||||||
|
sb-vm::call-symbol
|
||||||
|
throw sb-c:unwind
|
||||||
|
sb-kernel:update-object-layout
|
||||||
|
sb-vm::return-values-list)
|
||||||
|
#'equal)
|
||||||
|
|
||||||
|
#+nil
|
||||||
|
(defun copy-asm-statements (start)
|
||||||
|
(let* ((new-head (copy-structure start))
|
||||||
|
(new-prev new-head)
|
||||||
|
(stmt start))
|
||||||
|
(loop
|
||||||
|
(let ((next (stmt-next stmt)))
|
||||||
|
(when (null next) (return new-head))
|
||||||
|
(let ((copy-of-next (copy-structure next)))
|
||||||
|
(setf (stmt-next new-prev) copy-of-next
|
||||||
|
(stmt-prev copy-of-next) new-prev)
|
||||||
|
(setq new-prev copy-of-next
|
||||||
|
stmt next))))))
|
||||||
|
|
||||||
|
(defvar *original-total-n-yieldpoints* 0)
|
||||||
|
(defvar *total-n-yieldpoints-deleted* 0)
|
||||||
|
(defun delete-redundant-yieldpoints (start label->stmt-map elsewhere-labels &aux (n-deleted 0) original)
|
||||||
|
(declare (ignorable original))
|
||||||
|
#+nil (setq original (copy-asm-statements start))
|
||||||
|
;; Given straight-line code: POINT1 .. anything .. POINT2
|
||||||
|
;; then POINT1 is redundant because POINT2 is eventually hit.
|
||||||
|
(labels ((yieldpoint-inst-p (x) (eq (stmt-mnemonic x) 'yieldpoint))
|
||||||
|
(straight-line-to-yieldpoint-p (from follow-branches)
|
||||||
|
;; Return true if there is no control flow to the next yieldpoint starting at FROM,
|
||||||
|
;; i.e. if it is certain that a yieldpoint is hit; NIL otherwise.
|
||||||
|
(do ((s from (stmt-next s)))
|
||||||
|
((null s) nil)
|
||||||
|
;;(format t "~&Consider s-l-to-yp ~s ~S~%" s)
|
||||||
|
(when (or (yieldpoint-inst-p s)
|
||||||
|
;; CALL and unconditional JMP can be annotated as yieldpoints
|
||||||
|
;; and this shortcut is easier than deducing based on the operands
|
||||||
|
(eq (stmt-plist s) :yieldpoint)
|
||||||
|
;; BREAK invokes a signal handler, and signal handlers yield
|
||||||
|
(eq (stmt-mnemonic s) 'break))
|
||||||
|
(return t))
|
||||||
|
(when (eq (stmt-mnemonic s) 'ret)
|
||||||
|
(return nil))
|
||||||
|
(when (member (stmt-mnemonic s) '(jmp call))
|
||||||
|
(case (categorize-branch (car (last (stmt-operands s)))
|
||||||
|
(keywordp (car (stmt-operands s))) ; conditional
|
||||||
|
s
|
||||||
|
follow-branches)
|
||||||
|
(:yieldpoint (return t)) ; consider the jmp/call a yieldpoint
|
||||||
|
(:ignore ; just continue looking for a yieldpoint
|
||||||
|
;(format t "~A ~S = :IGNORE~%" (stmt-mnemonic s) (stmt-operands s))
|
||||||
|
)
|
||||||
|
(t
|
||||||
|
;(format t "~A ~S can't ignore~%" (stmt-mnemonic s) (stmt-operands s))
|
||||||
|
(return nil)))))) ; no good
|
||||||
|
(categorize-branch (target conditional stmt follow-branches)
|
||||||
|
;; JMP and CALL are tricky:
|
||||||
|
;; - CALL to any Lisp function is a yieldpoint per se, as every
|
||||||
|
;; function can be assumed to yield in its prologue.
|
||||||
|
;; - foreign call is preceded by a yieldpoint.
|
||||||
|
;; JMP depends on a few things:
|
||||||
|
;; - JMP to a lisp function is a tail-call. The called function will
|
||||||
|
;; commence with a yieldpoint. This goes for named or un-named call.
|
||||||
|
;; - JMP to a BREAK is not a yieldpoint per se, unless it's unconditional.
|
||||||
|
; If conditional it does not disrupt straight-line code.
|
||||||
|
;; CALL to assembly routine is generally considered straight-line code
|
||||||
|
;; though a few routines can be treated as yieldpoints.
|
||||||
|
(when (label-p target)
|
||||||
|
(when (and conditional (xset-member-p target elsewhere-labels))
|
||||||
|
;; Branching to the elsewhere segment is like straight-line code
|
||||||
|
;; because the exceptional path contains a yieldpoint.
|
||||||
|
(return-from categorize-branch :ignore))
|
||||||
|
(return-from categorize-branch
|
||||||
|
(let ((target (gethash target label->stmt-map)))
|
||||||
|
(cond ((and follow-branches conditional
|
||||||
|
;; If, no matter which way you branch, it is straight-line to a yieldpoint,
|
||||||
|
;; then this branch is as good as a yieldpoint.
|
||||||
|
(straight-line-to-yieldpoint-p (stmt-next stmt) nil)
|
||||||
|
(straight-line-to-yieldpoint-p target nil))
|
||||||
|
:yieldpoint)
|
||||||
|
((and follow-branches (not conditional)) ; maybe this does nothing now?
|
||||||
|
(straight-line-to-yieldpoint-p target nil))
|
||||||
|
(t ; also this, is it helpful? Branching to ELSEWHERE was already handled,
|
||||||
|
;; but sometimes we have error breaks in non-elsewhere
|
||||||
|
(do ((stmt target (stmt-next stmt))) ; quick check
|
||||||
|
(nil)
|
||||||
|
(aver stmt)
|
||||||
|
(when (eq (stmt-mnemonic stmt) 'break)
|
||||||
|
(return (if conditional :ignore :yieldpoint)))
|
||||||
|
(unless (eq (stmt-mnemonic stmt) 'mov)
|
||||||
|
(return nil))))))))
|
||||||
|
(let ((fixup (cond ((and (fixup-p target)
|
||||||
|
(eq (fixup-flavor target) :assembly-routine))
|
||||||
|
target)
|
||||||
|
((and (ea-p target)
|
||||||
|
(fixup-p (ea-disp target))
|
||||||
|
(eq (fixup-flavor (ea-disp target)) :assembly-routine*))
|
||||||
|
(ea-disp target)))))
|
||||||
|
(when fixup
|
||||||
|
(return-from categorize-branch
|
||||||
|
(if (member (fixup-name fixup) yieldpoint-asm-routines) :yieldpoint :ignore))))
|
||||||
|
(when (and (ea-p target)
|
||||||
|
(ea-base target)
|
||||||
|
(eql (ea-disp target) (- sb-vm:n-word-bytes sb-vm:fun-pointer-lowtag))
|
||||||
|
(location= (ea-base target) sb-vm::rax-tn))
|
||||||
|
(return-from categorize-branch :yieldpoint))))
|
||||||
|
(let ((stmt start))
|
||||||
|
(loop
|
||||||
|
(when (null stmt) (return))
|
||||||
|
(let ((next (stmt-next stmt)))
|
||||||
|
;; A call-out yieldpoint can't be deleted, but a call-out can count as
|
||||||
|
;; the subsequent yieldpoint for a preceding Lisp yieldpoint.
|
||||||
|
(when (and (yieldpoint-inst-p stmt) (eq (car (stmt-operands stmt)) :lisp))
|
||||||
|
(incf *original-total-n-yieldpoints*)
|
||||||
|
(when (straight-line-to-yieldpoint-p next t)
|
||||||
|
(incf n-deleted)
|
||||||
|
(awhen (stmt-labels stmt) (add-stmt-labels next it))
|
||||||
|
(delete-stmt stmt)))
|
||||||
|
(setq stmt next)))))
|
||||||
|
#+nil
|
||||||
|
(when (plusp n-deleted)
|
||||||
|
(with-open-file (f "/tmp/asmdump.txt" :direction :output :if-exists :append :if-does-not-exist :create)
|
||||||
|
(format f "~&Deleted ~D yieldpoints. Original code:~%" n-deleted)
|
||||||
|
(sb-assem::dump-symbolic-asm original f)
|
||||||
|
(terpri f)
|
||||||
|
(sb-assem::dump-symbolic-asm start f)))
|
||||||
|
(incf *total-n-yieldpoints-deleted* n-deleted))
|
||||||
|
|
||||||
;;; Return :TAKEN if taking the conditional branch COND1 implies that COND2's
|
;;; Return :TAKEN if taking the conditional branch COND1 implies that COND2's
|
||||||
;;; branch will be taken, or :NOT-TAKEN if COND2 will fallthrough,
|
;;; branch will be taken, or :NOT-TAKEN if COND2 will fallthrough,
|
||||||
;;; or NIL it can't be determined.
|
;;; or NIL it can't be determined.
|
||||||
|
|
@ -3609,10 +3767,7 @@
|
||||||
((conditions :a :ne) :taken) ; above to not-equal
|
((conditions :a :ne) :taken) ; above to not-equal
|
||||||
(t nil))))
|
(t nil))))
|
||||||
|
|
||||||
;;; Possible enhancement: it should be possible to eliminate more jumps-to-jumps
|
(defun sb-assem::perform-jump-to-jump-elimination (starting-stmt label->stmt-map elsewhere-labels)
|
||||||
;;; by knowing something about implication of one condition upon another, e.g.
|
|
||||||
;;; either JC or JZ jumping to JBE would take the second jump, since JBE is (CF=1 or ZF=1).
|
|
||||||
(defun sb-assem::perform-jump-to-jump-elimination (starting-stmt label->stmt-map)
|
|
||||||
(flet ((jmp-cond (stmt)
|
(flet ((jmp-cond (stmt)
|
||||||
(if (cdr (stmt-operands stmt))
|
(if (cdr (stmt-operands stmt))
|
||||||
(encoded-condition (car (stmt-operands stmt)))
|
(encoded-condition (car (stmt-operands stmt)))
|
||||||
|
|
@ -3639,4 +3794,5 @@
|
||||||
(let ((label (gen-label))) ; maake a new label
|
(let ((label (gen-label))) ; maake a new label
|
||||||
(setf (gethash label label->stmt-map) fallthrough)
|
(setf (gethash label label->stmt-map) fallthrough)
|
||||||
(add-stmt-labels fallthrough label)))))
|
(add-stmt-labels fallthrough label)))))
|
||||||
(setf (car (last (stmt-operands stmt))) label)))))))))
|
(setf (car (last (stmt-operands stmt))) label))))))))
|
||||||
|
(delete-redundant-yieldpoints starting-stmt label->stmt-map elsewhere-labels))
|
||||||
|
|
|
||||||
|
|
@ -195,14 +195,64 @@
|
||||||
(defmacro %clear-pseudo-atomic ()
|
(defmacro %clear-pseudo-atomic ()
|
||||||
'(inst mov :qword (thread-slot-ea thread-pseudo-atomic-bits-slot) 0))
|
'(inst mov :qword (thread-slot-ea thread-pseudo-atomic-bits-slot) 0))
|
||||||
|
|
||||||
#+sb-safepoint
|
;;; This is sort of a macro instruction but also not. Macro instructions
|
||||||
|
;;; don't know the instruction that was emitted by the vop because expansion
|
||||||
|
;;; occurs early. But we need to test for yieldpoint instructions
|
||||||
|
;;; in a post-processing step.
|
||||||
|
;;; I can't remember why it's not in "insts"
|
||||||
|
(sb-assem:define-instruction yieldpoint (segment kind)
|
||||||
|
(:emitter
|
||||||
|
(assemble (segment)
|
||||||
|
(let ((ea (thread-slot-ea thread-saved-csp-offset)))
|
||||||
|
(ecase kind
|
||||||
|
(:lisp
|
||||||
|
(inst vmovntdqa float15-tn ea)
|
||||||
|
;(inst prefetch :nta ea)
|
||||||
|
; (inst pushf)
|
||||||
|
; (inst inc :dword (thread-slot-ea thread-lisp-yps-executed-slot))
|
||||||
|
; (inst popf)
|
||||||
|
; (inst test :byte ea thread-tn)
|
||||||
|
;(inst test :dword (ea (+ (ash thread-stepping-slot word-shift) 4) thread-tn) thread-tn)
|
||||||
|
;(inst byte #x74) (inst byte 1)
|
||||||
|
;(inst icebp)
|
||||||
|
)
|
||||||
|
(:call-out
|
||||||
|
;(inst inc :qword (thread-slot-ea thread-c-yps-executed-slot))
|
||||||
|
(inst mov ea rsp-tn))
|
||||||
|
(:call-out-done
|
||||||
|
(inst xor ea rsp-tn)))))))
|
||||||
|
|
||||||
|
(defun note-yieldpoint (stmt) (setf (stmt-plist stmt) :yieldpoint))
|
||||||
|
|
||||||
|
#+nil
|
||||||
|
(defun sb-c::emit-patchable-yieldpoint-nop (name) ; 4-byte NOP
|
||||||
|
(cond ((eql (string/= "TWO-WORD-BIGNUM" name) 15)
|
||||||
|
;; these routines need the carry flag so we don't want
|
||||||
|
;; to mess it up by doing a TEST instruction.
|
||||||
|
;; I guess we could emit the 6-byte MOV yieldpoint.
|
||||||
|
;; For the time being, I'm doing the yieldpoint at the end,
|
||||||
|
;; inside the routine, and not overwriting with a NOP.
|
||||||
|
)
|
||||||
|
((member name '(sb-vm::enable-alloc-counter ; not sure about this or the next
|
||||||
|
sb-vm::enable-sized-alloc-counter
|
||||||
|
sb-vm::undefined-tramp ; don't need one here
|
||||||
|
sb-vm::undefined-alien-tramp ; or here
|
||||||
|
sb-vm::handle-deferred-signal ; don't want
|
||||||
|
sb-vm::tail-call-variable ; don't need - calls a function
|
||||||
|
sb-vm::tail-call-callable-variable ; "
|
||||||
|
sb-vm::call-symbol ; "
|
||||||
|
sb-kernel:update-object-layout ; "
|
||||||
|
sb-impl::install-hash-table-lock ; "
|
||||||
|
sb-vm::fpr-save ; internal use only for other ASM routines
|
||||||
|
sb-vm::fpr-restore)) ; "
|
||||||
|
nil)
|
||||||
|
(t ; otherwise emit a 4-byte NOP
|
||||||
|
(dolist (b '(#x0f #x1f #x40 #x00))
|
||||||
|
(inst byte b)))))
|
||||||
|
|
||||||
(defun emit-safepoint ()
|
(defun emit-safepoint ()
|
||||||
;; FIXME: need to get the node and policy to decide not to emit this safepoint.
|
#+yieldpoints (inst yieldpoint :lisp)
|
||||||
;; Also, it would be good to emit only the last of consecutive safepoints in
|
#+sb-safepoint (inst test :byte rax-tn (ea -8 gc-card-table-reg-tn)))
|
||||||
;; straight-line code, e.g. (LIST (LIST X Y) (LIST Z W)) should emit 1 safepoint
|
|
||||||
;; not 3, even if we consider it 3 separate pointer bumps.
|
|
||||||
;; (Ideally we'd only do 1 pointer bump, but that's a separate issue)
|
|
||||||
(inst test :byte rax-tn (ea -8 gc-card-table-reg-tn)))
|
|
||||||
|
|
||||||
(macrolet ((pa-bits-ea ()
|
(macrolet ((pa-bits-ea ()
|
||||||
#+sb-thread `(thread-slot-ea
|
#+sb-thread `(thread-slot-ea
|
||||||
|
|
@ -216,10 +266,10 @@
|
||||||
#+(and sb-thread (not gs-seg)) 'thread-tn
|
#+(and sb-thread (not gs-seg)) 'thread-tn
|
||||||
#-(and sb-thread (not gs-seg)) 'rbp-tn))
|
#-(and sb-thread (not gs-seg)) 'rbp-tn))
|
||||||
(defun emit-begin-pseudo-atomic ()
|
(defun emit-begin-pseudo-atomic ()
|
||||||
#-sb-safepoint (inst mov (pa-bits-ea) (nonzero-bits)))
|
#-(or sb-safepoint yieldpoints) (inst mov (pa-bits-ea) (nonzero-bits)))
|
||||||
(defun emit-end-pseudo-atomic ()
|
(defun emit-end-pseudo-atomic ()
|
||||||
#+sb-safepoint (emit-safepoint)
|
#+sb-safepoint (emit-safepoint)
|
||||||
#-sb-safepoint
|
#-(or sb-safepoint yieldpoints)
|
||||||
(assemble ()
|
(assemble ()
|
||||||
(inst xor (pa-bits-ea) (nonzero-bits))
|
(inst xor (pa-bits-ea) (nonzero-bits))
|
||||||
(inst jmp :z OUT)
|
(inst jmp :z OUT)
|
||||||
|
|
@ -232,6 +282,11 @@
|
||||||
;;; This macro is purposely unhygienic with respect to THREAD-TN,
|
;;; This macro is purposely unhygienic with respect to THREAD-TN,
|
||||||
;;; which is either a global symbol macro, or a LET-bound variable,
|
;;; which is either a global symbol macro, or a LET-bound variable,
|
||||||
;;; depending on #+gs-seg.
|
;;; depending on #+gs-seg.
|
||||||
|
(defmacro with-allocator (options &body body)
|
||||||
|
(declare (ignorable options))
|
||||||
|
#+(or sb-safepoint yieldpoints) `(assemble () ,@body)
|
||||||
|
#-(or sb-safepoint yieldpoints) `(pseudo-atomic ,options ,@body))
|
||||||
|
|
||||||
(defmacro pseudo-atomic ((&key ((:thread-tn thread)) elide-if (default-exit t))
|
(defmacro pseudo-atomic ((&key ((:thread-tn thread)) elide-if (default-exit t))
|
||||||
&body forms)
|
&body forms)
|
||||||
(declare (ignorable thread))
|
(declare (ignorable thread))
|
||||||
|
|
|
||||||
|
|
@ -160,7 +160,9 @@
|
||||||
#'equalp)
|
#'equalp)
|
||||||
|
|
||||||
(defconstant-eqx +static-fdefns+
|
(defconstant-eqx +static-fdefns+
|
||||||
`#(sb-impl::install-hash-table-lock update-object-layout
|
`#(sb-impl::install-hash-table-lock
|
||||||
|
update-object-layout
|
||||||
|
#+yieldpoints sb-unix::handle-deferred-signal
|
||||||
,@common-static-fdefns)
|
,@common-static-fdefns)
|
||||||
#'equalp)
|
#'equalp)
|
||||||
|
|
||||||
|
|
@ -197,3 +199,6 @@
|
||||||
;; The 'disp' field will aready be correct.
|
;; The 'disp' field will aready be correct.
|
||||||
(logior (ash undefined-function-trap 8)
|
(logior (ash undefined-function-trap 8)
|
||||||
(+ #x100000 (or #+int4-breakpoints #xCE #xCC))))
|
(+ #x100000 (or #+int4-breakpoints #xCE #xCC))))
|
||||||
|
|
||||||
|
#+yieldpoints
|
||||||
|
(defconstant sb-vm::thread-saved-csp-offset (- (+ 2 sb-vm::thread-header-slots)))
|
||||||
|
|
|
||||||
|
|
@ -540,4 +540,23 @@ number of CPU cycles elapsed as secondary value. EXPERIMENTAL."
|
||||||
(:generator 1
|
(:generator 1
|
||||||
(inst mov :byte new 1)
|
(inst mov :byte new 1)
|
||||||
(zeroize old)
|
(zeroize old)
|
||||||
(inst cmpxchg :lock :byte (mutex-slot m state) new)))
|
(inst cmpxchg :lock :byte (mutex-slot m state) new)
|
||||||
|
(inst jmp :nz NOPE)
|
||||||
|
(inst mov (mutex-slot m %owner) thread-tn)
|
||||||
|
NOPE))
|
||||||
|
|
||||||
|
(eval-when (:compile-toplevel :load-toplevel)
|
||||||
|
(define-vop (messup-all-regs)
|
||||||
|
(:generator 1
|
||||||
|
(inst mov rax-tn #xaaaaaaaa)
|
||||||
|
(inst mov rcx-tn #xcccccccc)
|
||||||
|
(inst mov rdx-tn #xdddddddd)
|
||||||
|
(inst mov rdi-tn #xd1d1d1d1)
|
||||||
|
(inst mov rsi-tn #x31313131b00f0000)
|
||||||
|
(inst mov r8-tn #x8888888888888888)
|
||||||
|
(inst mov r9-tn #x9999999999999999)
|
||||||
|
(inst mov r10-tn #xc0c0c0c0c0c0c0c0)
|
||||||
|
(inst movq float0-tn r10-tn)
|
||||||
|
(inst inc r10-tn)
|
||||||
|
(inst movq float1-tn r10-tn)
|
||||||
|
)))
|
||||||
|
|
|
||||||
|
|
@ -436,6 +436,11 @@
|
||||||
;; Always try to add an end-of-line comment about the EA.
|
;; Always try to add an end-of-line comment about the EA.
|
||||||
;; Assembler routines were already handled above (not really sure why)
|
;; Assembler routines were already handled above (not really sure why)
|
||||||
;; so now we have to figure out everything else.
|
;; so now we have to figure out everything else.
|
||||||
|
#+yieldpoints
|
||||||
|
(when (and (eql (machine-ea-base value) sb-vm::thread-reg)
|
||||||
|
(eql (machine-ea-disp value) (ash sb-vm::thread-saved-csp-offset sb-vm:word-shift)))
|
||||||
|
(return-from print-mem-ref (note "yieldpoint" dstate)))
|
||||||
|
|
||||||
#+sb-safepoint
|
#+sb-safepoint
|
||||||
(when (and (eql (machine-ea-base value) sb-vm::card-table-reg)
|
(when (and (eql (machine-ea-base value) sb-vm::card-table-reg)
|
||||||
(eql (machine-ea-disp value) -8))
|
(eql (machine-ea-disp value) -8))
|
||||||
|
|
|
||||||
|
|
@ -154,7 +154,7 @@
|
||||||
(defreg float14 14 :float)
|
(defreg float14 14 :float)
|
||||||
(defreg float15 15 :float)
|
(defreg float15 15 :float)
|
||||||
(defregset *float-regs* float0 float1 float2 float3 float4 float5 float6 float7
|
(defregset *float-regs* float0 float1 float2 float3 float4 float5 float6 float7
|
||||||
float8 float9 float10 float11 float12 float13 float14 float15)
|
float8 float9 float10 float11 float12 float13 float14 #|float15|#)
|
||||||
|
|
||||||
;; registers used to pass arguments
|
;; registers used to pass arguments
|
||||||
;;
|
;;
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,7 @@ COMMON_SRC = alloc.c arena.c backtrace.c breakpoint.c coalesce.c \
|
||||||
perfecthash.c print.c \
|
perfecthash.c print.c \
|
||||||
regnames.c runtime.c safepoint.c save.c \
|
regnames.c runtime.c safepoint.c save.c \
|
||||||
sc-offset.c search.c stringspace.c thread.c time.c \
|
sc-offset.c search.c stringspace.c thread.c time.c \
|
||||||
validate.c var-io.c vars.c wrap.c
|
validate.c var-io.c vars.c wrap.c yieldpoints.c
|
||||||
|
|
||||||
ifndef LISP_FEATURE_WIN32
|
ifndef LISP_FEATURE_WIN32
|
||||||
COMMON_SRC += run-program.c sprof.c
|
COMMON_SRC += run-program.c sprof.c
|
||||||
|
|
@ -99,7 +99,7 @@ endif
|
||||||
|
|
||||||
targets: $(TARGET) $(OBJTARGET) $(LDB_TARGET) sbcl.mk
|
targets: $(TARGET) $(OBJTARGET) $(LDB_TARGET) sbcl.mk
|
||||||
|
|
||||||
LDB_OBJS = $(filter-out main.o interr.o runtime.o monitor.o thread.o safepoint.o,$(OBJS))
|
LDB_OBJS = $(filter-out main.o interr.o runtime.o monitor.o thread.o safepoint.o yieldpoints.o,$(OBJS))
|
||||||
ldb: $(LIBSBCL)
|
ldb: $(LIBSBCL)
|
||||||
$(CC) -g $(CPPFLAGS) $(CFLAGS) -DSTANDALONE_LDB -c -o standalone-monitor.o monitor.c
|
$(CC) -g $(CPPFLAGS) $(CFLAGS) -DSTANDALONE_LDB -c -o standalone-monitor.o monitor.c
|
||||||
$(CC) -g $(CPPFLAGS) $(CFLAGS) -DSTANDALONE_LDB -c -o standalone-interr.o interr.c
|
$(CC) -g $(CPPFLAGS) $(CFLAGS) -DSTANDALONE_LDB -c -o standalone-interr.o interr.c
|
||||||
|
|
|
||||||
|
|
@ -213,11 +213,25 @@ void gc_close_thread_regions(__attribute__((unused)) struct thread* th,
|
||||||
{ main_thread_cons_region, PAGE_TYPE_CONS },
|
{ main_thread_cons_region, PAGE_TYPE_CONS },
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
#if 0
|
||||||
|
fprintf(stderr, "about to close region for %ps:\n", th);
|
||||||
|
int i;
|
||||||
|
for (i=0;i<4;++i) {
|
||||||
|
struct alloc_region* r = argv[i].r;
|
||||||
|
fprintf(stderr, " %p:%p (free=%p)\n", r->start_addr, r->end_addr, r->free_pointer);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
sync_close_regions(0, locking, argv, N_THREAD_TLABS(argv));
|
sync_close_regions(0, locking, argv, N_THREAD_TLABS(argv));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extern lispobj* lisp_alloc(int, struct alloc_region *, sword_t,
|
extern lispobj* lisp_alloc(int, struct alloc_region *, sword_t,
|
||||||
int, struct thread *);
|
int, struct thread *);
|
||||||
|
extern void maybe_suspend_for_gc(struct thread*);
|
||||||
|
extern struct alloc_region alloc_cooperate_before(struct alloc_region*, struct thread*);
|
||||||
|
// argument order is like memcpy() - to, from
|
||||||
|
extern void alloc_cooperate_after(struct alloc_region*, struct alloc_region*,
|
||||||
|
struct thread*);
|
||||||
|
|
||||||
/* alloc() and alloc_list() are external interfaces for memory allocation.
|
/* alloc() and alloc_list() are external interfaces for memory allocation.
|
||||||
* They allocate to generation 0 and are not called from within the garbage
|
* They allocate to generation 0 and are not called from within the garbage
|
||||||
|
|
@ -227,14 +241,21 @@ extern lispobj* lisp_alloc(int, struct alloc_region *, sword_t,
|
||||||
|
|
||||||
#if defined(LISP_FEATURE_SYSTEM_TLABS) || defined(LISP_FEATURE_X86_64)
|
#if defined(LISP_FEATURE_SYSTEM_TLABS) || defined(LISP_FEATURE_X86_64)
|
||||||
|
|
||||||
// The asm routines have been modified so that alloc() and alloc_list()
|
/* The asm routines have been modified so that alloc() and alloc_list()
|
||||||
// each receive the size an a single-bit flag affecting locality of the result.
|
* each receive the size an a single-bit flag affecting locality of the result.
|
||||||
|
* Note that we make a copy by value of the TLAB which avoids having GC steal
|
||||||
|
* it while in lisp_alloc. cooperate_with_gc contains the logic to synchronize
|
||||||
|
* with GC. */
|
||||||
#define DEFINE_LISP_ENTRYPOINT(name, largep, TLAB, page_type) \
|
#define DEFINE_LISP_ENTRYPOINT(name, largep, TLAB, page_type) \
|
||||||
NO_SANITIZE_MEMORY lispobj *name(sword_t nbytes, int sys) { \
|
NO_SANITIZE_MEMORY lispobj *name(sword_t nbytes, int sys) { \
|
||||||
struct thread *self = get_sb_vm_thread(); \
|
struct thread *self = get_sb_vm_thread(); \
|
||||||
return lisp_alloc(largep | sys, \
|
struct alloc_region* tlab \
|
||||||
sys ? &self->sys_##TLAB##_tlab : THREAD_ALLOC_REGION(self,TLAB), \
|
= sys ? &self->sys_##TLAB##_tlab : THREAD_ALLOC_REGION(self,TLAB); \
|
||||||
nbytes, page_type, self); }
|
struct alloc_region region = alloc_cooperate_before(tlab, self); \
|
||||||
|
lispobj* res = lisp_alloc(largep | sys, ®ion, nbytes, page_type, self); \
|
||||||
|
alloc_cooperate_after(tlab, ®ion, self); \
|
||||||
|
return res; \
|
||||||
|
}
|
||||||
|
|
||||||
DEFINE_LISP_ENTRYPOINT(alloc, (nbytes >= LARGE_OBJECT_SIZE), mixed, PAGE_TYPE_MIXED)
|
DEFINE_LISP_ENTRYPOINT(alloc, (nbytes >= LARGE_OBJECT_SIZE), mixed, PAGE_TYPE_MIXED)
|
||||||
DEFINE_LISP_ENTRYPOINT(alloc_list, 0, cons, PAGE_TYPE_CONS)
|
DEFINE_LISP_ENTRYPOINT(alloc_list, 0, cons, PAGE_TYPE_CONS)
|
||||||
|
|
@ -262,6 +283,7 @@ DEFINE_LISP_ENTRYPOINT(alloc_list, 0, mixed, PAGE_TYPE_MIXED)
|
||||||
lispobj alloc_code_object(unsigned total_words, unsigned boxed)
|
lispobj alloc_code_object(unsigned total_words, unsigned boxed)
|
||||||
{
|
{
|
||||||
struct thread *th = get_sb_vm_thread();
|
struct thread *th = get_sb_vm_thread();
|
||||||
|
maybe_suspend_for_gc(th);
|
||||||
// x86-64 uses pseudo-atomic. Others should too, but instead use WITHOUT-GCING
|
// x86-64 uses pseudo-atomic. Others should too, but instead use WITHOUT-GCING
|
||||||
#ifndef LISP_FEATURE_X86_64
|
#ifndef LISP_FEATURE_X86_64
|
||||||
if (read_TLS(GC_INHIBIT, th) == NIL)
|
if (read_TLS(GC_INHIBIT, th) == NIL)
|
||||||
|
|
@ -297,9 +319,10 @@ lispobj alloc_code_object(unsigned total_words, unsigned boxed)
|
||||||
|
|
||||||
#ifdef LISP_FEATURE_SYSTEM_TLABS
|
#ifdef LISP_FEATURE_SYSTEM_TLABS
|
||||||
#define PREPARE_LIST_ALLOCATION() \
|
#define PREPARE_LIST_ALLOCATION() \
|
||||||
struct alloc_region *region = sys ? &self->sys_cons_tlab : &self->cons_tlab; \
|
struct alloc_region* tlab = sys ? &self->sys_cons_tlab : &self->cons_tlab; \
|
||||||
|
struct alloc_region region = alloc_cooperate_before(tlab, self); \
|
||||||
int partial_request = (self->arena && !sys) ? \
|
int partial_request = (self->arena && !sys) ? \
|
||||||
nbytes : (char*)region->end_addr - (char*)region->free_pointer; \
|
nbytes : (char*)region.end_addr - (char*)region.free_pointer; \
|
||||||
gc_assert(nbytes >= (sword_t)partial_request); \
|
gc_assert(nbytes >= (sword_t)partial_request); \
|
||||||
if (partial_request == 0) partial_request = CONS_PAGE_USABLE_BYTES
|
if (partial_request == 0) partial_request = CONS_PAGE_USABLE_BYTES
|
||||||
#else /* no system tlabs */
|
#else /* no system tlabs */
|
||||||
|
|
@ -314,6 +337,7 @@ lispobj alloc_code_object(unsigned total_words, unsigned boxed)
|
||||||
NO_SANITIZE_MEMORY lispobj alloc_funinstance(sword_t nbytes)
|
NO_SANITIZE_MEMORY lispobj alloc_funinstance(sword_t nbytes)
|
||||||
{
|
{
|
||||||
struct thread *th = get_sb_vm_thread();
|
struct thread *th = get_sb_vm_thread();
|
||||||
|
maybe_suspend_for_gc(th);
|
||||||
__attribute__((unused)) int result = mutex_acquire(&code_allocator_lock);
|
__attribute__((unused)) int result = mutex_acquire(&code_allocator_lock);
|
||||||
gc_assert(result);
|
gc_assert(result);
|
||||||
void* mem = lisp_alloc(0, code_region, nbytes, PAGE_TYPE_CODE, th);
|
void* mem = lisp_alloc(0, code_region, nbytes, PAGE_TYPE_CODE, th);
|
||||||
|
|
@ -334,7 +358,7 @@ make_list(lispobj element, sword_t nbytes, int sys) {
|
||||||
lispobj result, *tail = &result;
|
lispobj result, *tail = &result;
|
||||||
do {
|
do {
|
||||||
if (nbytes < partial_request) partial_request = nbytes;
|
if (nbytes < partial_request) partial_request = nbytes;
|
||||||
struct cons* c = (void*)lisp_alloc(sys, region, partial_request, PAGE_TYPE_CONS, self);
|
struct cons* c = (void*)lisp_alloc(sys, ®ion, partial_request, PAGE_TYPE_CONS, self);
|
||||||
*tail = make_lispobj((void*)c, LIST_POINTER_LOWTAG);
|
*tail = make_lispobj((void*)c, LIST_POINTER_LOWTAG);
|
||||||
int ncells = partial_request >> (1+WORD_SHIFT);
|
int ncells = partial_request >> (1+WORD_SHIFT);
|
||||||
nbytes -= N_WORD_BYTES * 2 * ncells;
|
nbytes -= N_WORD_BYTES * 2 * ncells;
|
||||||
|
|
@ -347,6 +371,7 @@ make_list(lispobj element, sword_t nbytes, int sys) {
|
||||||
partial_request = CONS_PAGE_USABLE_BYTES;
|
partial_request = CONS_PAGE_USABLE_BYTES;
|
||||||
} while (nbytes);
|
} while (nbytes);
|
||||||
*tail = NIL;
|
*tail = NIL;
|
||||||
|
alloc_cooperate_after(tlab, ®ion, self);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -361,7 +386,7 @@ listify_rest_arg(lispobj* context, sword_t nbytes, int sys) {
|
||||||
lispobj result, *tail = &result;
|
lispobj result, *tail = &result;
|
||||||
do {
|
do {
|
||||||
if (nbytes < partial_request) partial_request = nbytes;
|
if (nbytes < partial_request) partial_request = nbytes;
|
||||||
struct cons* c = (void*)lisp_alloc(sys, region, partial_request, PAGE_TYPE_CONS, self);
|
struct cons* c = (void*)lisp_alloc(sys, ®ion, partial_request, PAGE_TYPE_CONS, self);
|
||||||
*tail = make_lispobj((void*)c, LIST_POINTER_LOWTAG);
|
*tail = make_lispobj((void*)c, LIST_POINTER_LOWTAG);
|
||||||
int ncells = partial_request >> (1+WORD_SHIFT);
|
int ncells = partial_request >> (1+WORD_SHIFT);
|
||||||
nbytes -= N_WORD_BYTES * 2 * ncells;
|
nbytes -= N_WORD_BYTES * 2 * ncells;
|
||||||
|
|
@ -374,6 +399,7 @@ listify_rest_arg(lispobj* context, sword_t nbytes, int sys) {
|
||||||
partial_request = CONS_PAGE_USABLE_BYTES;
|
partial_request = CONS_PAGE_USABLE_BYTES;
|
||||||
} while (nbytes);
|
} while (nbytes);
|
||||||
*tail = NIL;
|
*tail = NIL;
|
||||||
|
alloc_cooperate_after(tlab, ®ion, self);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
|
|
||||||
|
|
@ -113,7 +113,7 @@ void handle_breakpoint(os_context_t *context)
|
||||||
|
|
||||||
fake_foreign_function_call(context);
|
fake_foreign_function_call(context);
|
||||||
|
|
||||||
#ifndef LISP_FEATURE_SB_SAFEPOINT
|
#ifdef USE_GC_STOP_SIGNAL
|
||||||
unblock_gc_stop_signal();
|
unblock_gc_stop_signal();
|
||||||
#endif
|
#endif
|
||||||
code = find_code(context);
|
code = find_code(context);
|
||||||
|
|
@ -140,7 +140,7 @@ void *handle_fun_end_breakpoint(os_context_t *context)
|
||||||
|
|
||||||
fake_foreign_function_call(context);
|
fake_foreign_function_call(context);
|
||||||
|
|
||||||
#ifndef LISP_FEATURE_SB_SAFEPOINT
|
#ifdef USE_GC_STOP_SIGNAL
|
||||||
unblock_gc_stop_signal();
|
unblock_gc_stop_signal();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1415,7 +1415,7 @@ bool test_weak_triggers(bool (*predicate)(lispobj), void (*mark)(lispobj))
|
||||||
}
|
}
|
||||||
|
|
||||||
int finalizer_thread_runflag = 1;
|
int finalizer_thread_runflag = 1;
|
||||||
#ifdef LISP_FEATURE_SB_THREAD
|
#if defined LISP_FEATURE_SB_THREAD && !defined LISP_FEATURE_YIELDPOINTS
|
||||||
|
|
||||||
#ifdef LISP_FEATURE_WIN32
|
#ifdef LISP_FEATURE_WIN32
|
||||||
CRITICAL_SECTION finalizer_mutex;
|
CRITICAL_SECTION finalizer_mutex;
|
||||||
|
|
@ -2195,7 +2195,7 @@ bool maybe_gc(os_context_t *context)
|
||||||
* A kludgy alternative is to propagate the sigmask change to the
|
* A kludgy alternative is to propagate the sigmask change to the
|
||||||
* outer context.
|
* outer context.
|
||||||
*/
|
*/
|
||||||
#ifndef LISP_FEATURE_SB_SAFEPOINT
|
#ifdef USE_GC_STOP_SIGNAL
|
||||||
check_gc_signals_unblocked_or_lose(os_context_sigmask_addr(context));
|
check_gc_signals_unblocked_or_lose(os_context_sigmask_addr(context));
|
||||||
unblock_gc_stop_signal();
|
unblock_gc_stop_signal();
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -2233,7 +2233,7 @@ bool maybe_gc(os_context_t *context)
|
||||||
* post-GC code. Except that we do it while the interrupt context
|
* post-GC code. Except that we do it while the interrupt context
|
||||||
* is still on the stack */
|
* is still on the stack */
|
||||||
thread_sigmask(SIG_SETMASK, context_sigmask, 0);
|
thread_sigmask(SIG_SETMASK, context_sigmask, 0);
|
||||||
#ifndef LISP_FEATURE_SB_SAFEPOINT
|
#ifdef USE_GC_STOP_SIGNAL
|
||||||
check_gc_signals_unblocked_or_lose(0);
|
check_gc_signals_unblocked_or_lose(0);
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -23,10 +23,6 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#if defined LISP_FEATURE_SB_THREAD && !defined LISP_FEATURE_SB_SAFEPOINT
|
|
||||||
# define THREADS_USING_GCSIGNAL 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined LISP_FEATURE_GENERATIONAL && !defined LISP_FEATURE_C_STACK_IS_CONTROL_STACK
|
#if defined LISP_FEATURE_GENERATIONAL && !defined LISP_FEATURE_C_STACK_IS_CONTROL_STACK
|
||||||
# define GENCGC_IS_PRECISE 1
|
# define GENCGC_IS_PRECISE 1
|
||||||
#else
|
#else
|
||||||
|
|
|
||||||
|
|
@ -15,12 +15,13 @@ struct alloc_region {
|
||||||
void *start_addr;
|
void *start_addr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define REGION_END_EMPTY_VALUE (void*)0x1000
|
||||||
static inline void gc_set_region_empty(struct alloc_region *region)
|
static inline void gc_set_region_empty(struct alloc_region *region)
|
||||||
{
|
{
|
||||||
/* Free-pointer has to be not equal to 0 because it's undefined behavior
|
/* Free-pointer has to be not equal to 0 because it's undefined behavior
|
||||||
* to add any value whatsoever to the null pointer.
|
* to add any value whatsoever to the null pointer.
|
||||||
* Annoying, isn't it. http://c-faq.com/null/machexamp.html */
|
* Annoying, isn't it. http://c-faq.com/null/machexamp.html */
|
||||||
region->free_pointer = region->end_addr = (void*)0x1000;
|
region->free_pointer = region->end_addr = REGION_END_EMPTY_VALUE;
|
||||||
/* Start 0 is the indicator of closed-ness. */
|
/* Start 0 is the indicator of closed-ness. */
|
||||||
region->start_addr = 0;
|
region->start_addr = 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -324,22 +324,24 @@ static page_index_t
|
||||||
get_alloc_start_page(unsigned int page_type)
|
get_alloc_start_page(unsigned int page_type)
|
||||||
{
|
{
|
||||||
if (page_type > 7) lose("bad page_type: %d", page_type);
|
if (page_type > 7) lose("bad page_type: %d", page_type);
|
||||||
struct thread* th = get_sb_vm_thread();
|
|
||||||
page_index_t global_start = alloc_start_pages[page_type];
|
page_index_t global_start = alloc_start_pages[page_type];
|
||||||
page_index_t hint;
|
struct thread* th = get_sb_vm_thread();
|
||||||
switch (page_type) {
|
if (th) {
|
||||||
case PAGE_TYPE_MIXED:
|
page_index_t hint;
|
||||||
if ((hint = thread_extra_data(th)->mixed_page_hint) > 0 && hint <= global_start) {
|
switch (page_type) {
|
||||||
thread_extra_data(th)->mixed_page_hint = - 1;
|
case PAGE_TYPE_MIXED:
|
||||||
return hint;
|
if ((hint = thread_extra_data(th)->mixed_page_hint) > 0 && hint <= global_start) {
|
||||||
|
thread_extra_data(th)->mixed_page_hint = - 1;
|
||||||
|
return hint;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PAGE_TYPE_CONS:
|
||||||
|
if ((hint = thread_extra_data(th)->cons_page_hint) > 0 && hint <= global_start) {
|
||||||
|
thread_extra_data(th)->cons_page_hint = - 1;
|
||||||
|
return hint;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
case PAGE_TYPE_CONS:
|
|
||||||
if ((hint = thread_extra_data(th)->cons_page_hint) > 0 && hint <= global_start) {
|
|
||||||
thread_extra_data(th)->cons_page_hint = - 1;
|
|
||||||
return hint;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
return global_start;
|
return global_start;
|
||||||
}
|
}
|
||||||
|
|
@ -3175,6 +3177,7 @@ conservative_stack_scan(struct thread* th,
|
||||||
# ifndef LISP_FEATURE_WIN32
|
# ifndef LISP_FEATURE_WIN32
|
||||||
if (th != get_sb_vm_thread()) {
|
if (th != get_sb_vm_thread()) {
|
||||||
int k = fixnum_value(read_TLS(FREE_INTERRUPT_CONTEXT_INDEX,th));
|
int k = fixnum_value(read_TLS(FREE_INTERRUPT_CONTEXT_INDEX,th));
|
||||||
|
//fprintf(stderr, "scanning context, thread %p index %d\n", th, k);
|
||||||
while (k > 0) {
|
while (k > 0) {
|
||||||
os_context_t* context = nth_interrupt_context(--k, th);
|
os_context_t* context = nth_interrupt_context(--k, th);
|
||||||
if (context)
|
if (context)
|
||||||
|
|
@ -3184,10 +3187,11 @@ conservative_stack_scan(struct thread* th,
|
||||||
# endif
|
# endif
|
||||||
# elif defined(LISP_FEATURE_SB_THREAD)
|
# elif defined(LISP_FEATURE_SB_THREAD)
|
||||||
int i;
|
int i;
|
||||||
/* fprintf(stderr, "Thread %p, ici=%d stack[%p:%p] (%dw)",
|
if (th->control_stack_pointer != 0) esp = th->control_stack_pointer;
|
||||||
th, fixnum_value(read_TLS(FREE_INTERRUPT_CONTEXT_INDEX,th)),
|
/* fprintf(stderr, "Thread %p, ici=%d stack[%p:%p] (%dw)\n",
|
||||||
|
th, (int)fixnum_value(read_TLS(FREE_INTERRUPT_CONTEXT_INDEX,th)),
|
||||||
th->control_stack_start, th->control_stack_end,
|
th->control_stack_start, th->control_stack_end,
|
||||||
th->control_stack_end - th->control_stack_start); */
|
(int)(th->control_stack_end - th->control_stack_start)); */
|
||||||
for (i = fixnum_value(read_TLS(FREE_INTERRUPT_CONTEXT_INDEX,th))-1; i>=0; i--) {
|
for (i = fixnum_value(read_TLS(FREE_INTERRUPT_CONTEXT_INDEX,th))-1; i>=0; i--) {
|
||||||
os_context_t *c = nth_interrupt_context(i, th);
|
os_context_t *c = nth_interrupt_context(i, th);
|
||||||
visit_context_registers(context_method, c, (void*)1);
|
visit_context_registers(context_method, c, (void*)1);
|
||||||
|
|
@ -3981,6 +3985,13 @@ collect_garbage(generation_index_t last_gen)
|
||||||
|
|
||||||
large_allocation = 0;
|
large_allocation = 0;
|
||||||
finish:
|
finish:
|
||||||
|
{
|
||||||
|
struct cons* cons = gc_general_alloc(cons_region, 2*N_WORD_BYTES, PAGE_TYPE_CONS);
|
||||||
|
cons->car = cons->cdr = 0;
|
||||||
|
SYMBOL(GC_EPOCH)->value = make_lispobj(cons, LIST_POINTER_LOWTAG);
|
||||||
|
ensure_region_closed(cons_region, PAGE_TYPE_CONS);
|
||||||
|
}
|
||||||
|
|
||||||
write_protect_immobile_space();
|
write_protect_immobile_space();
|
||||||
gc_active_p = 0;
|
gc_active_p = 0;
|
||||||
|
|
||||||
|
|
@ -4020,6 +4031,10 @@ gc_init(void)
|
||||||
32 /* logical bin count */, 0 /* default range */);
|
32 /* logical bin count */, 0 /* default range */);
|
||||||
#ifdef LISP_FEATURE_WIN32
|
#ifdef LISP_FEATURE_WIN32
|
||||||
InitializeCriticalSection(&free_pages_lock);
|
InitializeCriticalSection(&free_pages_lock);
|
||||||
|
#endif
|
||||||
|
extern void safepoint_init(void);
|
||||||
|
#if defined LISP_FEATURE_SB_SAFEPOINT || defined LISP_FEATURE_YIELDPOINTS
|
||||||
|
safepoint_init();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -4104,6 +4119,10 @@ lisp_alloc(int flags, struct alloc_region *region, sword_t nbytes,
|
||||||
* should GC in the near future
|
* should GC in the near future
|
||||||
*/
|
*/
|
||||||
if (auto_gc_trigger && (bytes_allocated+trigger_bytes > auto_gc_trigger)) {
|
if (auto_gc_trigger && (bytes_allocated+trigger_bytes > auto_gc_trigger)) {
|
||||||
|
#ifdef LISP_FEATURE_YIELDPOINTS
|
||||||
|
extern void request_garbage_collection(int);
|
||||||
|
request_garbage_collection(-1);
|
||||||
|
#else
|
||||||
/* Don't flood the system with interrupts if the need to gc is
|
/* Don't flood the system with interrupts if the need to gc is
|
||||||
* already noted. This can happen for example when SUB-GC
|
* already noted. This can happen for example when SUB-GC
|
||||||
* allocates or after a gc triggered in a WITHOUT-GCING. */
|
* allocates or after a gc triggered in a WITHOUT-GCING. */
|
||||||
|
|
@ -4125,6 +4144,7 @@ lisp_alloc(int flags, struct alloc_region *region, sword_t nbytes,
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/* For the architectures which do NOT use a trap instruction for allocation,
|
/* For the architectures which do NOT use a trap instruction for allocation,
|
||||||
|
|
@ -4792,8 +4812,10 @@ static int verify_range(lispobj* start, lispobj* end, struct verify_state* state
|
||||||
#endif
|
#endif
|
||||||
if (widetag != FILLER_WIDETAG && pg >= 0) {
|
if (widetag != FILLER_WIDETAG && pg >= 0) {
|
||||||
// Assert proper page type
|
// Assert proper page type
|
||||||
if (state->object_header) // is not a cons
|
if (state->object_header) { // is not a cons
|
||||||
gc_assert(page_table[pg].type != PAGE_TYPE_CONS);
|
if (page_table[pg].type == PAGE_TYPE_CONS)
|
||||||
|
lose("headered object @ %p on cons page", state->object_addr);
|
||||||
|
}
|
||||||
#ifdef LISP_FEATURE_USE_CONS_REGION
|
#ifdef LISP_FEATURE_USE_CONS_REGION
|
||||||
else if (page_table[pg].type != PAGE_TYPE_CONS) {
|
else if (page_table[pg].type != PAGE_TYPE_CONS) {
|
||||||
if (is_cons_half(where[0]))
|
if (is_cons_half(where[0]))
|
||||||
|
|
|
||||||
|
|
@ -1096,6 +1096,7 @@ sweep_text_pages(int raise)
|
||||||
if (freelist_tail) {
|
if (freelist_tail) {
|
||||||
freelist_tail[1] = codeblob_freelist;
|
freelist_tail[1] = codeblob_freelist;
|
||||||
codeblob_freelist = (lispobj)freelist;
|
codeblob_freelist = (lispobj)freelist;
|
||||||
|
// fprintf(stderr, "New items in codeblob freelist\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -123,7 +123,6 @@ int internal_errors_enabled = 0; // read in cold-init
|
||||||
// SIGRTMAX is not usable in an array size declaration because it might be
|
// SIGRTMAX is not usable in an array size declaration because it might be
|
||||||
// a variable expression, so use NSIG which is at least as large as SIGRTMAX.
|
// a variable expression, so use NSIG which is at least as large as SIGRTMAX.
|
||||||
#ifndef LISP_FEATURE_WIN32
|
#ifndef LISP_FEATURE_WIN32
|
||||||
static
|
|
||||||
void (*interrupt_low_level_handlers[NSIG]) (int, siginfo_t*, os_context_t*);
|
void (*interrupt_low_level_handlers[NSIG]) (int, siginfo_t*, os_context_t*);
|
||||||
struct sigaction old_ll_sigactions[NSIG];
|
struct sigaction old_ll_sigactions[NSIG];
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -420,7 +419,11 @@ sigaddset_blockable(sigset_t *sigset)
|
||||||
{
|
{
|
||||||
#ifdef LISP_FEATURE_SB_SAFEPOINT
|
#ifdef LISP_FEATURE_SB_SAFEPOINT
|
||||||
sigaddset_async(sigset);
|
sigaddset_async(sigset);
|
||||||
#else
|
#endif
|
||||||
|
#ifdef LISP_FEATURE_YIELDPOINTS
|
||||||
|
sigaddset_deferrable(sigset);
|
||||||
|
#endif
|
||||||
|
#ifdef LISP_FEATURE_GC_STW_SIGNAL
|
||||||
sigaddset_deferrable(sigset);
|
sigaddset_deferrable(sigset);
|
||||||
sigaddset_gc(sigset);
|
sigaddset_gc(sigset);
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -578,7 +581,7 @@ static void assert_blockables_blocked()
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef LISP_FEATURE_SB_SAFEPOINT
|
#ifdef LISP_FEATURE_GC_STW_SIGNAL
|
||||||
void
|
void
|
||||||
check_gc_signals_unblocked_or_lose(sigset_t *sigset)
|
check_gc_signals_unblocked_or_lose(sigset_t *sigset)
|
||||||
{
|
{
|
||||||
|
|
@ -614,7 +617,7 @@ unblock_deferrable_signals(sigset_t *where)
|
||||||
{
|
{
|
||||||
if (interrupt_handler_pending_p())
|
if (interrupt_handler_pending_p())
|
||||||
lose("unblock_deferrable_signals: losing proposition");
|
lose("unblock_deferrable_signals: losing proposition");
|
||||||
#ifndef LISP_FEATURE_SB_SAFEPOINT
|
#ifdef LISP_FEATURE_GC_STW_SIGNAL
|
||||||
// If 'where' is null, check_gc_signals_unblocked_or_lose() will
|
// If 'where' is null, check_gc_signals_unblocked_or_lose() will
|
||||||
// fetch the current signal mask (from the OS) and check that.
|
// fetch the current signal mask (from the OS) and check that.
|
||||||
check_gc_signals_unblocked_or_lose(where);
|
check_gc_signals_unblocked_or_lose(where);
|
||||||
|
|
@ -636,7 +639,7 @@ unblock_deferrable_signals(sigset_t *where)
|
||||||
thread_sigmask(SIG_UNBLOCK, sigset, 0);
|
thread_sigmask(SIG_UNBLOCK, sigset, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef LISP_FEATURE_SB_SAFEPOINT
|
#ifdef LISP_FEATURE_GC_STW_SIGNAL
|
||||||
// This function previously had an #ifdef guard precluding doing anything for
|
// This function previously had an #ifdef guard precluding doing anything for
|
||||||
// win32, which was redundant because SB_SAFEPOINT is always defined for win32.
|
// win32, which was redundant because SB_SAFEPOINT is always defined for win32.
|
||||||
void unblock_gc_stop_signal(void) {
|
void unblock_gc_stop_signal(void) {
|
||||||
|
|
@ -648,7 +651,7 @@ void
|
||||||
unblock_signals_in_context_and_maybe_warn(os_context_t *context)
|
unblock_signals_in_context_and_maybe_warn(os_context_t *context)
|
||||||
{
|
{
|
||||||
sigset_t *sigset = os_context_sigmask_addr(context);
|
sigset_t *sigset = os_context_sigmask_addr(context);
|
||||||
#ifndef LISP_FEATURE_SB_SAFEPOINT
|
#ifdef LISP_FEATURE_GC_STW_SIGNAL
|
||||||
if (sigismember(sigset, SIG_STOP_FOR_GC)) {
|
if (sigismember(sigset, SIG_STOP_FOR_GC)) {
|
||||||
corruption_warning_and_maybe_lose(
|
corruption_warning_and_maybe_lose(
|
||||||
"Enabling blocked gc signals to allow returning to Lisp without risking\n\
|
"Enabling blocked gc signals to allow returning to Lisp without risking\n\
|
||||||
|
|
@ -672,7 +675,7 @@ they are not safe to interrupt at all, this is a pretty severe occurrence.\n");
|
||||||
* The purpose is to avoid losing the pending gc signal if a
|
* The purpose is to avoid losing the pending gc signal if a
|
||||||
* deferrable interrupt async unwinds between clearing the pseudo
|
* deferrable interrupt async unwinds between clearing the pseudo
|
||||||
* atomic and trapping to GC.*/
|
* atomic and trapping to GC.*/
|
||||||
#ifndef LISP_FEATURE_SB_SAFEPOINT
|
#ifdef LISP_FEATURE_GC_STW_SIGNAL
|
||||||
void maybe_save_gc_mask_and_block_deferrables(os_context_t *context)
|
void maybe_save_gc_mask_and_block_deferrables(os_context_t *context)
|
||||||
{
|
{
|
||||||
struct thread *thread = get_sb_vm_thread();
|
struct thread *thread = get_sb_vm_thread();
|
||||||
|
|
@ -718,6 +721,7 @@ void maybe_save_gc_mask_and_block_deferrables(os_context_t *context)
|
||||||
/* Are we leaving WITH-GCING and already running with interrupts
|
/* Are we leaving WITH-GCING and already running with interrupts
|
||||||
* enabled, without the protection of *GC-INHIBIT* T and there is gc
|
* enabled, without the protection of *GC-INHIBIT* T and there is gc
|
||||||
* (or stop for gc) pending, but we haven't trapped yet? */
|
* (or stop for gc) pending, but we haven't trapped yet? */
|
||||||
|
#ifndef LISP_FEATURE_YIELDPOINTS
|
||||||
int
|
int
|
||||||
in_leaving_without_gcing_race_p(struct thread __attribute__((unused)) *thread)
|
in_leaving_without_gcing_race_p(struct thread __attribute__((unused)) *thread)
|
||||||
{
|
{
|
||||||
|
|
@ -749,7 +753,7 @@ check_interrupt_context_or_lose(os_context_t *context)
|
||||||
int pseudo_atomic_interrupted = get_pseudo_atomic_interrupted(thread);
|
int pseudo_atomic_interrupted = get_pseudo_atomic_interrupted(thread);
|
||||||
int in_race_p = in_leaving_without_gcing_race_p(thread);
|
int in_race_p = in_leaving_without_gcing_race_p(thread);
|
||||||
int safepoint_active = 0;
|
int safepoint_active = 0;
|
||||||
#if defined(LISP_FEATURE_SB_SAFEPOINT)
|
#ifdef LISP_FEATURE_SB_SAFEPOINT
|
||||||
/* Don't try to take the gc state lock if there's a chance that
|
/* Don't try to take the gc state lock if there's a chance that
|
||||||
* we're already holding it (thread_register_gc_trigger() is
|
* we're already holding it (thread_register_gc_trigger() is
|
||||||
* called from PA, gc_stop_the_world() and gc_start_the_world()
|
* called from PA, gc_stop_the_world() and gc_start_the_world()
|
||||||
|
|
@ -800,13 +804,14 @@ check_interrupt_context_or_lose(os_context_t *context)
|
||||||
check_deferrables_blocked_or_lose(sigset);
|
check_deferrables_blocked_or_lose(sigset);
|
||||||
else {
|
else {
|
||||||
check_deferrables_unblocked_or_lose(sigset);
|
check_deferrables_unblocked_or_lose(sigset);
|
||||||
#ifndef LISP_FEATURE_SB_SAFEPOINT
|
#ifdef LISP_FEATURE_GC_STW_SIGNAL
|
||||||
/* If deferrables are unblocked then we are open to signals
|
/* If deferrables are unblocked then we are open to signals
|
||||||
* that run lisp code. */
|
* that run lisp code. */
|
||||||
check_gc_signals_unblocked_or_lose(sigset);
|
check_gc_signals_unblocked_or_lose(sigset);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* utility routines used by various signal handlers
|
* utility routines used by various signal handlers
|
||||||
|
|
@ -1056,7 +1061,7 @@ interrupt_handle_pending(os_context_t *context)
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_blockables_blocked();
|
assert_blockables_blocked();
|
||||||
#ifndef LISP_FEATURE_SB_SAFEPOINT
|
#ifdef LISP_FEATURE_GC_STW_SIGNAL
|
||||||
/*
|
/*
|
||||||
* (On safepoint builds, there is no gc_blocked_deferrables nor
|
* (On safepoint builds, there is no gc_blocked_deferrables nor
|
||||||
* SIG_STOP_FOR_GC.)
|
* SIG_STOP_FOR_GC.)
|
||||||
|
|
@ -1097,7 +1102,7 @@ interrupt_handle_pending(os_context_t *context)
|
||||||
thread_in_lisp_raised(context);
|
thread_in_lisp_raised(context);
|
||||||
undo_fake_foreign_function_call(context);
|
undo_fake_foreign_function_call(context);
|
||||||
}
|
}
|
||||||
#elif defined(LISP_FEATURE_SB_THREAD)
|
#elif defined THREADS_USING_GCSIGNAL
|
||||||
if (read_TLS(STOP_FOR_GC_PENDING,thread) != NIL) {
|
if (read_TLS(STOP_FOR_GC_PENDING,thread) != NIL) {
|
||||||
/* STOP_FOR_GC_PENDING and GC_PENDING are cleared by
|
/* STOP_FOR_GC_PENDING and GC_PENDING are cleared by
|
||||||
* the signal handler if it actually stops us. */
|
* the signal handler if it actually stops us. */
|
||||||
|
|
@ -1234,7 +1239,7 @@ interrupt_handle_now(int signal, siginfo_t *info, os_context_t *context)
|
||||||
* be available; should we copy it or was nobody using it anyway?)
|
* be available; should we copy it or was nobody using it anyway?)
|
||||||
* then we should convert this to return-elsewhere */
|
* then we should convert this to return-elsewhere */
|
||||||
|
|
||||||
#if !defined(LISP_FEATURE_SB_SAFEPOINT) && defined(LISP_FEATURE_C_STACK_IS_CONTROL_STACK)
|
#if defined LISP_FEATURE_GC_STW_SIGNAL && defined LISP_FEATURE_C_STACK_IS_CONTROL_STACK
|
||||||
/* Leave deferrable signals blocked, the handler itself will
|
/* Leave deferrable signals blocked, the handler itself will
|
||||||
* allow signals again when it sees fit. */
|
* allow signals again when it sees fit. */
|
||||||
/* handler.lisp will hide from the GC, will be enabled in the handler itself.
|
/* handler.lisp will hide from the GC, will be enabled in the handler itself.
|
||||||
|
|
@ -1278,8 +1283,8 @@ run_deferred_handler(struct interrupt_data *data, os_context_t *context)
|
||||||
(*pending_handler)(data->pending_signal,&(data->pending_info), context);
|
(*pending_handler)(data->pending_signal,&(data->pending_info), context);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef LISP_FEATURE_WIN32
|
#ifdef LISP_FEATURE_UNIX
|
||||||
static void
|
void
|
||||||
store_signal_data_for_later (struct interrupt_data *data, void *handler,
|
store_signal_data_for_later (struct interrupt_data *data, void *handler,
|
||||||
int signal,
|
int signal,
|
||||||
siginfo_t *info, os_context_t *context)
|
siginfo_t *info, os_context_t *context)
|
||||||
|
|
@ -1301,6 +1306,9 @@ store_signal_data_for_later (struct interrupt_data *data, void *handler,
|
||||||
sigcopyset(&data->pending_mask, os_context_sigmask_addr(context));
|
sigcopyset(&data->pending_mask, os_context_sigmask_addr(context));
|
||||||
sigaddset_deferrable(os_context_sigmask_addr(context));
|
sigaddset_deferrable(os_context_sigmask_addr(context));
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined LISP_FEATURE_WIN32 && !defined LISP_FEATURE_YIELDPOINTS
|
||||||
|
|
||||||
/* What's going on ?
|
/* What's going on ?
|
||||||
*
|
*
|
||||||
|
|
@ -1528,7 +1536,7 @@ arrange_return_to_c_function(os_context_t *context,
|
||||||
call_into_lisp_lookalike funptr,
|
call_into_lisp_lookalike funptr,
|
||||||
lispobj function)
|
lispobj function)
|
||||||
{
|
{
|
||||||
#ifndef LISP_FEATURE_SB_SAFEPOINT
|
#ifdef LISP_FEATURE_GC_STOP_SIGNAL
|
||||||
check_gc_signals_unblocked_or_lose(os_context_sigmask_addr(context));
|
check_gc_signals_unblocked_or_lose(os_context_sigmask_addr(context));
|
||||||
#endif
|
#endif
|
||||||
#if !(defined(LISP_FEATURE_X86) || defined(LISP_FEATURE_X86_64))
|
#if !(defined(LISP_FEATURE_X86) || defined(LISP_FEATURE_X86_64))
|
||||||
|
|
@ -1968,6 +1976,7 @@ ll_install_handler (int signal, interrupt_handler_t handler)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef LISP_FEATURE_YIELDPOINTS
|
||||||
extern void sigprof_handler(int, siginfo_t*, void*);
|
extern void sigprof_handler(int, siginfo_t*, void*);
|
||||||
|
|
||||||
/* This is called from Lisp. */
|
/* This is called from Lisp. */
|
||||||
|
|
@ -2018,6 +2027,7 @@ void install_handler(int signal, lispobj handler)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/* This must not go through lisp as it's allowed anytime, even when on
|
/* This must not go through lisp as it's allowed anytime, even when on
|
||||||
* the altstack. */
|
* the altstack. */
|
||||||
|
|
|
||||||
|
|
@ -76,11 +76,13 @@ struct interrupt_data {
|
||||||
int pending_signal;
|
int pending_signal;
|
||||||
siginfo_t pending_info;
|
siginfo_t pending_info;
|
||||||
sigset_t pending_mask;
|
sigset_t pending_mask;
|
||||||
|
#ifndef LISP_FEATURE_YIELDPOINTS
|
||||||
/* Was pending mask saved for gc request? True if GC_PENDING or
|
/* Was pending mask saved for gc request? True if GC_PENDING or
|
||||||
* SIG_STOP_FOR_GC happened in a pseudo atomic with GC_INHIBIT NIL
|
* SIG_STOP_FOR_GC happened in a pseudo atomic with GC_INHIBIT NIL
|
||||||
* and with no pending handler. Both deferrable interrupt handlers
|
* and with no pending handler. Both deferrable interrupt handlers
|
||||||
* and gc are careful not to clobber each other's pending_mask. */
|
* and gc are careful not to clobber each other's pending_mask. */
|
||||||
bool gc_blocked_deferrables;
|
bool gc_blocked_deferrables;
|
||||||
|
#endif
|
||||||
#if defined LISP_FEATURE_MIPS || defined LISP_FEATURE_PPC \
|
#if defined LISP_FEATURE_MIPS || defined LISP_FEATURE_PPC \
|
||||||
|| defined LISP_FEATURE_PPC64 || defined LISP_FEATURE_SPARC
|
|| defined LISP_FEATURE_PPC64 || defined LISP_FEATURE_SPARC
|
||||||
#define HAVE_ALLOCATION_TRAP_CONTEXT 1
|
#define HAVE_ALLOCATION_TRAP_CONTEXT 1
|
||||||
|
|
|
||||||
|
|
@ -380,7 +380,7 @@ sigsegv_handler(int signal, siginfo_t *info, os_context_t *context)
|
||||||
{
|
{
|
||||||
os_vm_address_t addr = arch_get_bad_addr(signal, info, context);
|
os_vm_address_t addr = arch_get_bad_addr(signal, info, context);
|
||||||
|
|
||||||
#ifdef LISP_FEATURE_SB_SAFEPOINT
|
#if defined LISP_FEATURE_YIELDPOINTS || LISP_FEATURE_SB_SAFEPOINT
|
||||||
if (handle_safepoint_violation(context, addr)) return;
|
if (handle_safepoint_violation(context, addr)) return;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
@ -398,7 +398,7 @@ sigsegv_handler(int signal, siginfo_t *info, os_context_t *context)
|
||||||
void
|
void
|
||||||
os_install_interrupt_handlers(void)
|
os_install_interrupt_handlers(void)
|
||||||
{
|
{
|
||||||
if (INSTALL_SIG_MEMORY_FAULT_HANDLER) {
|
if (1) { // INSTALL_SIG_MEMORY_FAULT_HANDLER) {
|
||||||
ll_install_handler(SIG_MEMORY_FAULT, sigsegv_handler);
|
ll_install_handler(SIG_MEMORY_FAULT, sigsegv_handler);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1081,6 +1081,25 @@ ldb_monitor(void)
|
||||||
#ifdef STANDALONE_LDB
|
#ifdef STANDALONE_LDB
|
||||||
void gc_stop_the_world() { } // do nothing
|
void gc_stop_the_world() { } // do nothing
|
||||||
void gc_start_the_world() { } // do nothing
|
void gc_start_the_world() { } // do nothing
|
||||||
|
void defer_until_yieldpoint() { }
|
||||||
|
void sigtrap_handler() { }
|
||||||
|
void set_gc_pending() { }
|
||||||
|
void safepoint_init() { }
|
||||||
|
void request_garbage_collection(int) { }
|
||||||
|
void cooperate_with_gc(struct thread* th) { lose("cooperate_with_gc %p ?", th); }
|
||||||
|
int handle_safepoint_violation(__attribute__((unused)) os_context_t *context,
|
||||||
|
__attribute__((unused)) os_vm_address_t addr) { return 0; }
|
||||||
|
struct alloc_region alloc_cooperate_before(__attribute__((unused)) struct alloc_region*,
|
||||||
|
__attribute__((unused)) struct thread*) {
|
||||||
|
struct alloc_region r;
|
||||||
|
memset(&r, 0, sizeof r);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
void alloc_cooperate_after(__attribute__((unused)) struct alloc_region*dst,
|
||||||
|
__attribute__((unused)) struct alloc_region*src,
|
||||||
|
__attribute__((unused)) struct thread* th) { }
|
||||||
|
void maybe_suspend_for_gc(__attribute__((unused)) struct thread* th) { }
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <setjmp.h>
|
#include <setjmp.h>
|
||||||
#include "core.h"
|
#include "core.h"
|
||||||
|
|
|
||||||
|
|
@ -795,7 +795,7 @@ initialize_lisp(int argc, char *argv[], char *envp[])
|
||||||
os_install_interrupt_handlers();
|
os_install_interrupt_handlers();
|
||||||
# ifdef LISP_FEATURE_SB_SAFEPOINT
|
# ifdef LISP_FEATURE_SB_SAFEPOINT
|
||||||
ll_install_handler(SIGURG, thruption_handler);
|
ll_install_handler(SIGURG, thruption_handler);
|
||||||
# elif defined LISP_FEATURE_SB_THREAD
|
# elif defined THREADS_USING_GCSIGNAL
|
||||||
ll_install_handler(SIG_STOP_FOR_GC, sig_stop_for_gc_handler);
|
ll_install_handler(SIG_STOP_FOR_GC, sig_stop_for_gc_handler);
|
||||||
# endif
|
# endif
|
||||||
#else
|
#else
|
||||||
|
|
|
||||||
|
|
@ -74,8 +74,18 @@ pthread_key_t current_thread = 0;
|
||||||
CRITICAL_SECTION all_threads_lock;
|
CRITICAL_SECTION all_threads_lock;
|
||||||
static CRITICAL_SECTION recyclebin_lock;
|
static CRITICAL_SECTION recyclebin_lock;
|
||||||
static CRITICAL_SECTION in_gc_lock;
|
static CRITICAL_SECTION in_gc_lock;
|
||||||
|
#else
|
||||||
|
#ifdef LISP_FEATURE_YIELDPOINTS
|
||||||
|
pthread_rwlock_t all_threads_lock = PTHREAD_RWLOCK_INITIALIZER;
|
||||||
|
// the functions return 0 on success, but we want boolean true
|
||||||
|
#define GRAB_ALL_THREADS_LOCK() !pthread_rwlock_wrlock(&all_threads_lock)
|
||||||
|
#define RELEASE_ALL_THREADS_LOCK() !pthread_rwlock_unlock(&all_threads_lock)
|
||||||
|
|
||||||
#else
|
#else
|
||||||
pthread_mutex_t all_threads_lock = PTHREAD_MUTEX_INITIALIZER;
|
pthread_mutex_t all_threads_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
#define GRAB_ALL_THREADS_LOCK() mutex_acquire(&all_threads_lock)
|
||||||
|
#define RELEASE_ALL_THREADS_LOCK() mutex_release(&all_threads_lock)
|
||||||
|
#endif
|
||||||
static pthread_mutex_t recyclebin_lock = PTHREAD_MUTEX_INITIALIZER;
|
static pthread_mutex_t recyclebin_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||||
static pthread_mutex_t in_gc_lock = PTHREAD_MUTEX_INITIALIZER;
|
static pthread_mutex_t in_gc_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -121,7 +131,7 @@ char* vm_thread_name(struct thread* th)
|
||||||
#define get_thread_state(thread) \
|
#define get_thread_state(thread) \
|
||||||
(int)__sync_val_compare_and_swap(&thread->state_word.state, -1, -1)
|
(int)__sync_val_compare_and_swap(&thread->state_word.state, -1, -1)
|
||||||
|
|
||||||
#ifndef LISP_FEATURE_SB_SAFEPOINT
|
#ifdef LISP_FEATURE_GC_STW_SIGNAL
|
||||||
|
|
||||||
void
|
void
|
||||||
set_thread_state(struct thread *thread,
|
set_thread_state(struct thread *thread,
|
||||||
|
|
@ -445,10 +455,10 @@ init_new_thread(struct thread *th,
|
||||||
#ifdef LISP_FEATURE_SB_SAFEPOINT
|
#ifdef LISP_FEATURE_SB_SAFEPOINT
|
||||||
csp_around_foreign_call(th) = (lispobj)scribble;
|
csp_around_foreign_call(th) = (lispobj)scribble;
|
||||||
#endif
|
#endif
|
||||||
__attribute__((unused)) int lock_ret = mutex_acquire(&all_threads_lock);
|
__attribute__((unused)) int lock_ret = GRAB_ALL_THREADS_LOCK();
|
||||||
gc_assert(lock_ret);
|
gc_assert(lock_ret);
|
||||||
link_thread(th);
|
link_thread(th);
|
||||||
ignore_value(mutex_release(&all_threads_lock));
|
ignore_value(RELEASE_ALL_THREADS_LOCK());
|
||||||
|
|
||||||
/* Kludge: Changed the order of some steps between the safepoint/
|
/* Kludge: Changed the order of some steps between the safepoint/
|
||||||
* non-safepoint versions of this code. Can we unify this more?
|
* non-safepoint versions of this code. Can we unify this more?
|
||||||
|
|
@ -469,7 +479,8 @@ unregister_thread(struct thread *th,
|
||||||
gc_close_thread_regions(th, LOCK_PAGE_TABLE|CONSUME_REMAINDER);
|
gc_close_thread_regions(th, LOCK_PAGE_TABLE|CONSUME_REMAINDER);
|
||||||
#ifdef LISP_FEATURE_SB_SAFEPOINT
|
#ifdef LISP_FEATURE_SB_SAFEPOINT
|
||||||
pop_gcing_safety(&scribble->safety);
|
pop_gcing_safety(&scribble->safety);
|
||||||
#else
|
#endif
|
||||||
|
#ifdef LISP_FEATURE_GC_STW_SIGNAL
|
||||||
/* This state change serves to "acknowledge" any stop-the-world
|
/* This state change serves to "acknowledge" any stop-the-world
|
||||||
* signal received while the STOP_FOR_GC signal is blocked */
|
* signal received while the STOP_FOR_GC signal is blocked */
|
||||||
set_thread_state(th, STATE_DEAD, 1);
|
set_thread_state(th, STATE_DEAD, 1);
|
||||||
|
|
@ -478,10 +489,10 @@ unregister_thread(struct thread *th,
|
||||||
* thread, but since we are either exiting lisp code as a lisp
|
* thread, but since we are either exiting lisp code as a lisp
|
||||||
* thread that is dying, or exiting lisp code to return to
|
* thread that is dying, or exiting lisp code to return to
|
||||||
* former status as a C thread, it won't wait long. */
|
* former status as a C thread, it won't wait long. */
|
||||||
__attribute__((unused)) int lock_ret = mutex_acquire(&all_threads_lock);
|
__attribute__((unused)) int lock_ret = GRAB_ALL_THREADS_LOCK();
|
||||||
gc_assert(lock_ret);
|
gc_assert(lock_ret);
|
||||||
unlink_thread(th);
|
unlink_thread(th);
|
||||||
lock_ret = mutex_release(&all_threads_lock);
|
lock_ret = RELEASE_ALL_THREADS_LOCK();
|
||||||
gc_assert(lock_ret);
|
gc_assert(lock_ret);
|
||||||
|
|
||||||
arch_os_thread_cleanup(th);
|
arch_os_thread_cleanup(th);
|
||||||
|
|
@ -490,7 +501,7 @@ unregister_thread(struct thread *th,
|
||||||
#ifdef LISP_FEATURE_UNIX
|
#ifdef LISP_FEATURE_UNIX
|
||||||
os_sem_destroy(&semaphores->sprof_sem);
|
os_sem_destroy(&semaphores->sprof_sem);
|
||||||
#endif
|
#endif
|
||||||
#ifndef LISP_FEATURE_SB_SAFEPOINT
|
#ifdef LISP_FEATURE_GC_STW_SIGNAL
|
||||||
os_sem_destroy(&semaphores->state_sem);
|
os_sem_destroy(&semaphores->state_sem);
|
||||||
os_sem_destroy(&semaphores->state_not_running_sem);
|
os_sem_destroy(&semaphores->state_not_running_sem);
|
||||||
os_sem_destroy(&semaphores->state_not_stopped_sem);
|
os_sem_destroy(&semaphores->state_not_stopped_sem);
|
||||||
|
|
@ -577,7 +588,7 @@ void* new_thread_trampoline(void* arg)
|
||||||
// strictly below the computed th->control_stack_end. So make sure the value we pick
|
// strictly below the computed th->control_stack_end. So make sure the value we pick
|
||||||
// is strictly above any value of SP that the interrupt context could have.
|
// is strictly above any value of SP that the interrupt context could have.
|
||||||
#if defined LISP_FEATURE_C_STACK_IS_CONTROL_STACK && !defined ADDRESS_SANITIZER \
|
#if defined LISP_FEATURE_C_STACK_IS_CONTROL_STACK && !defined ADDRESS_SANITIZER \
|
||||||
&& !defined LISP_FEATURE_SB_SAFEPOINT
|
&& defined LISP_FEATURE_UNIX
|
||||||
th->control_stack_end = (lispobj*)&arg + 1;
|
th->control_stack_end = (lispobj*)&arg + 1;
|
||||||
#endif
|
#endif
|
||||||
th->os_kernel_tid = get_nonzero_tid();
|
th->os_kernel_tid = get_nonzero_tid();
|
||||||
|
|
@ -681,7 +692,7 @@ static void attach_os_thread(init_thread_data *scribble)
|
||||||
void* recycled_memory = get_recyclebin_item();
|
void* recycled_memory = get_recyclebin_item();
|
||||||
struct thread *th = alloc_thread_struct(recycled_memory);
|
struct thread *th = alloc_thread_struct(recycled_memory);
|
||||||
|
|
||||||
#ifndef LISP_FEATURE_SB_SAFEPOINT
|
#ifdef LISP_FEATURE_GC_STW_SIGNAL
|
||||||
/* new-lisp-thread-trampoline doesn't like when the GC signal is blocked */
|
/* new-lisp-thread-trampoline doesn't like when the GC signal is blocked */
|
||||||
/* FIXME: could be done using a single call to pthread_sigmask
|
/* FIXME: could be done using a single call to pthread_sigmask
|
||||||
together with blocking the deferrable signals above. */
|
together with blocking the deferrable signals above. */
|
||||||
|
|
@ -764,7 +775,7 @@ static void detach_os_thread(init_thread_data *scribble)
|
||||||
* - but STOP_FOR_GC is pending because it was in the blocked set.
|
* - but STOP_FOR_GC is pending because it was in the blocked set.
|
||||||
* Bad things happen unless we clear the pending GC signal.
|
* Bad things happen unless we clear the pending GC signal.
|
||||||
*/
|
*/
|
||||||
#if !defined LISP_FEATURE_SB_SAFEPOINT
|
#ifdef LISP_FEATURE_GC_STW_SIGNAL
|
||||||
sigset_t pending;
|
sigset_t pending;
|
||||||
sigpending(&pending);
|
sigpending(&pending);
|
||||||
if (sigismember(&pending, SIG_STOP_FOR_GC)) {
|
if (sigismember(&pending, SIG_STOP_FOR_GC)) {
|
||||||
|
|
@ -863,8 +874,8 @@ callback_wrapper_trampoline(
|
||||||
* / ___ aligned_spaces
|
* / ___ aligned_spaces
|
||||||
* / /
|
* / /
|
||||||
* (0) (1) (2) (3) (4) (5) (6)
|
* (0) (1) (2) (3) (4) (5) (6)
|
||||||
* | | CONTROL | BINDING | ALIEN | CSP | thread | |
|
* | | CONTROL | BINDING | ALIEN | Trap | thread | |
|
||||||
* | | STACK | STACK | STACK | PAGE | structure | altstack |
|
* | | STACK | STACK | STACK | page | structure | altstack |
|
||||||
* |...|------------------------------------------------------------|
|
* |...|------------------------------------------------------------|
|
||||||
* 2MiB 1MiB 1MiB (*) (**)
|
* 2MiB 1MiB 1MiB (*) (**)
|
||||||
*
|
*
|
||||||
|
|
@ -917,17 +928,17 @@ alloc_thread_struct(void* spaces) {
|
||||||
/* Aligning up is safe as THREAD_STRUCT_SIZE has
|
/* Aligning up is safe as THREAD_STRUCT_SIZE has
|
||||||
* THREAD_ALIGNMENT_BYTES padding. */
|
* THREAD_ALIGNMENT_BYTES padding. */
|
||||||
char *aligned_spaces = PTR_ALIGN_UP(spaces, THREAD_ALIGNMENT_BYTES);
|
char *aligned_spaces = PTR_ALIGN_UP(spaces, THREAD_ALIGNMENT_BYTES);
|
||||||
char* csp_page = aligned_spaces + thread_control_stack_size +
|
char* yieldpoint_page = aligned_spaces + thread_control_stack_size +
|
||||||
BINDING_STACK_SIZE + ALIEN_STACK_SIZE;
|
BINDING_STACK_SIZE + ALIEN_STACK_SIZE;
|
||||||
|
|
||||||
// Refer to the ASCII art in the block comment above
|
// Refer to the ASCII art in the block comment above
|
||||||
struct thread *th = (void*)(csp_page + THREAD_CSP_PAGE_SIZE
|
struct thread *th = (void*)(yieldpoint_page + THREAD_YIELDPOINT_PAGE_SIZE
|
||||||
+ THREAD_HEADER_SLOTS*N_WORD_BYTES);
|
+ THREAD_HEADER_SLOTS*N_WORD_BYTES);
|
||||||
|
|
||||||
#ifdef LISP_FEATURE_SB_SAFEPOINT
|
#if defined LISP_FEATURE_SB_SAFEPOINT || defined LISP_FEATURE_YIELDPOINTS
|
||||||
// Out of caution I'm supposing that the last thread to use this memory
|
// Out of caution I'm supposing that the last thread to use this memory
|
||||||
// might have left this page as read-only. Could it? I have no idea.
|
// might have left this page as read-only. Could it? I have no idea.
|
||||||
os_protect(csp_page, THREAD_CSP_PAGE_SIZE, OS_VM_PROT_READ|OS_VM_PROT_WRITE);
|
os_protect(yieldpoint_page, THREAD_YIELDPOINT_PAGE_SIZE, OS_VM_PROT_READ|OS_VM_PROT_WRITE);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef LISP_FEATURE_SB_THREAD
|
#ifdef LISP_FEATURE_SB_THREAD
|
||||||
|
|
@ -1071,11 +1082,7 @@ alloc_thread_struct(void* spaces) {
|
||||||
access_control_frame_pointer(th)=0;
|
access_control_frame_pointer(th)=0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
thread_interrupt_data(th).pending_handler = 0;
|
memset(&thread_interrupt_data(th), 0, sizeof (struct interrupt_data));
|
||||||
thread_interrupt_data(th).gc_blocked_deferrables = 0;
|
|
||||||
#if HAVE_ALLOCATION_TRAP_CONTEXT
|
|
||||||
thread_interrupt_data(th).allocation_trap_context = 0;
|
|
||||||
#endif
|
|
||||||
#if defined LISP_FEATURE_PPC64
|
#if defined LISP_FEATURE_PPC64
|
||||||
/* Storing a 0 into code coverage mark bytes or GC card mark bytes
|
/* Storing a 0 into code coverage mark bytes or GC card mark bytes
|
||||||
* can be done from the low byte of the thread base register.
|
* can be done from the low byte of the thread base register.
|
||||||
|
|
@ -1103,7 +1110,7 @@ alloc_thread_struct(void* spaces) {
|
||||||
thread_private_events(th,i) = CreateEvent(NULL,FALSE,FALSE,NULL);
|
thread_private_events(th,i) = CreateEvent(NULL,FALSE,FALSE,NULL);
|
||||||
thread_extra_data(th)->synchronous_io_handle_and_flag = 0;
|
thread_extra_data(th)->synchronous_io_handle_and_flag = 0;
|
||||||
#endif
|
#endif
|
||||||
th->stepping = 0;
|
memset(&th->stepping, 0, N_WORD_BYTES);
|
||||||
th->card_table = (lispobj)gc_card_mark;
|
th->card_table = (lispobj)gc_card_mark;
|
||||||
return th;
|
return th;
|
||||||
}
|
}
|
||||||
|
|
@ -1161,7 +1168,7 @@ void thread_accrue_stw_time(struct thread* th, struct timespec* begin)
|
||||||
/*
|
/*
|
||||||
* (With SB-SAFEPOINT, see the definitions in safepoint.c instead.)
|
* (With SB-SAFEPOINT, see the definitions in safepoint.c instead.)
|
||||||
*/
|
*/
|
||||||
#if !defined LISP_FEATURE_SB_SAFEPOINT && !defined STANDALONE_LDB
|
#if defined LISP_FEATURE_GC_STW_SIGNAL && !defined STANDALONE_LDB
|
||||||
|
|
||||||
/* To avoid deadlocks when gc stops the world all clients of each
|
/* To avoid deadlocks when gc stops the world all clients of each
|
||||||
* mutex must enable or disable SIG_STOP_FOR_GC for the duration of
|
* mutex must enable or disable SIG_STOP_FOR_GC for the duration of
|
||||||
|
|
|
||||||
|
|
@ -24,12 +24,14 @@ void set_thread_state(struct thread *thread, char state, bool);
|
||||||
int thread_wait_until_not(int state, struct thread *thread);
|
int thread_wait_until_not(int state, struct thread *thread);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined LISP_FEATURE_YIELDPOINTS || defined LISP_FEATURE_SB_SAFEPOINT
|
||||||
|
int handle_safepoint_violation(os_context_t *context, os_vm_address_t addr);
|
||||||
|
#endif
|
||||||
#if defined(LISP_FEATURE_SB_SAFEPOINT)
|
#if defined(LISP_FEATURE_SB_SAFEPOINT)
|
||||||
struct gcing_safety {
|
struct gcing_safety {
|
||||||
lispobj csp_around_foreign_call;
|
lispobj csp_around_foreign_call;
|
||||||
};
|
};
|
||||||
|
|
||||||
int handle_safepoint_violation(os_context_t *context, os_vm_address_t addr);
|
|
||||||
void* os_get_csp(struct thread* th);
|
void* os_get_csp(struct thread* th);
|
||||||
void assert_on_stack(struct thread *th, void *esp);
|
void assert_on_stack(struct thread *th, void *esp);
|
||||||
#endif /* defined(LISP_FEATURE_SB_SAFEPOINT) */
|
#endif /* defined(LISP_FEATURE_SB_SAFEPOINT) */
|
||||||
|
|
@ -154,10 +156,10 @@ extern pthread_key_t current_thread;
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef LISP_FEATURE_SB_SAFEPOINT
|
#if defined LISP_FEATURE_SB_SAFEPOINT || defined LISP_FEATURE_YIELDPOINTS
|
||||||
# define THREAD_CSP_PAGE_SIZE 0
|
# define THREAD_YIELDPOINT_PAGE_SIZE os_reported_page_size
|
||||||
#else
|
#else
|
||||||
# define THREAD_CSP_PAGE_SIZE os_reported_page_size
|
# define THREAD_YIELDPOINT_PAGE_SIZE 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef LISP_FEATURE_WIN32
|
#ifdef LISP_FEATURE_WIN32
|
||||||
|
|
@ -171,7 +173,7 @@ extern pthread_key_t current_thread;
|
||||||
#define THREAD_STRUCT_SIZE \
|
#define THREAD_STRUCT_SIZE \
|
||||||
(THREAD_ALIGNMENT_BYTES + \
|
(THREAD_ALIGNMENT_BYTES + \
|
||||||
thread_control_stack_size + BINDING_STACK_SIZE + ALIEN_STACK_SIZE + \
|
thread_control_stack_size + BINDING_STACK_SIZE + ALIEN_STACK_SIZE + \
|
||||||
THREAD_CSP_PAGE_SIZE + \
|
THREAD_YIELDPOINT_PAGE_SIZE + \
|
||||||
(THREAD_HEADER_SLOTS*N_WORD_BYTES) + dynamic_values_bytes + \
|
(THREAD_HEADER_SLOTS*N_WORD_BYTES) + dynamic_values_bytes + \
|
||||||
sizeof (struct extra_thread_data) + ALT_STACK_SIZE)
|
sizeof (struct extra_thread_data) + ALT_STACK_SIZE)
|
||||||
|
|
||||||
|
|
@ -330,7 +332,11 @@ extern void create_main_lisp_thread(lispobj);
|
||||||
#ifdef LISP_FEATURE_WIN32
|
#ifdef LISP_FEATURE_WIN32
|
||||||
extern CRITICAL_SECTION all_threads_lock;
|
extern CRITICAL_SECTION all_threads_lock;
|
||||||
#elif defined LISP_FEATURE_SB_THREAD
|
#elif defined LISP_FEATURE_SB_THREAD
|
||||||
|
# ifdef LISP_FEATURE_YIELDPOINTS
|
||||||
|
extern pthread_rwlock_t all_threads_lock;
|
||||||
|
# else
|
||||||
extern pthread_mutex_t all_threads_lock;
|
extern pthread_mutex_t all_threads_lock;
|
||||||
|
# endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef LISP_FEATURE_SB_THREAD
|
#ifndef LISP_FEATURE_SB_THREAD
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ static void release_lock(lock_t *l) { gc_assert(!pthread_mutex_unlock(l)); }
|
||||||
struct lock { _Atomic(int) grabbed; };
|
struct lock { _Atomic(int) grabbed; };
|
||||||
typedef struct lock lock_t;
|
typedef struct lock lock_t;
|
||||||
|
|
||||||
static void acquire_lock(lock_t *l) {
|
static void __attribute__((unused)) acquire_lock(lock_t *l) {
|
||||||
int expected = 0, cycles = 0;
|
int expected = 0, cycles = 0;
|
||||||
/* atomic_compare_exchange_strong kindly clobbers expected for us,
|
/* atomic_compare_exchange_strong kindly clobbers expected for us,
|
||||||
* when CAS fails. */
|
* when CAS fails. */
|
||||||
|
|
@ -26,7 +26,11 @@ static void acquire_lock(lock_t *l) {
|
||||||
if (cycles++ > 1000) sched_yield();
|
if (cycles++ > 1000) sched_yield();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
static void release_lock(lock_t *l) {
|
static int __attribute__((unused)) try_acquire_lock(lock_t *l) {
|
||||||
|
int expected = 0;
|
||||||
|
return atomic_compare_exchange_strong(&l->grabbed, &expected, 1);
|
||||||
|
}
|
||||||
|
static void __attribute__((unused)) release_lock(lock_t *l) {
|
||||||
atomic_store(&l->grabbed, 0);
|
atomic_store(&l->grabbed, 0);
|
||||||
}
|
}
|
||||||
#define LOCK_INITIALIZER { 0 }
|
#define LOCK_INITIALIZER { 0 }
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "genesis/sbcl.h"
|
#include "genesis/sbcl.h"
|
||||||
|
#include "genesis/hash-table.h"
|
||||||
#include "runtime.h"
|
#include "runtime.h"
|
||||||
#include "globals.h"
|
#include "globals.h"
|
||||||
#include "validate.h"
|
#include "validate.h"
|
||||||
|
|
@ -364,7 +365,7 @@ restore_breakpoint_from_single_step(os_context_t * context)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
static void
|
||||||
sigtrap_handler(int __attribute__((unused)) signal,
|
sigtrap_handler(int __attribute__((unused)) signal,
|
||||||
siginfo_t __attribute__((unused)) *info,
|
siginfo_t __attribute__((unused)) *info,
|
||||||
os_context_t *context)
|
os_context_t *context)
|
||||||
|
|
@ -501,6 +502,7 @@ arch_install_interrupt_handlers()
|
||||||
* CL way, I hope there will at least be a comment to explain
|
* CL way, I hope there will at least be a comment to explain
|
||||||
* why.. -- WHN 2001-06-07 */
|
* why.. -- WHN 2001-06-07 */
|
||||||
#ifndef LISP_FEATURE_WIN32
|
#ifndef LISP_FEATURE_WIN32
|
||||||
|
extern void sigtrap_handler();
|
||||||
ll_install_handler(SIGILL , sigill_handler);
|
ll_install_handler(SIGILL , sigill_handler);
|
||||||
ll_install_handler(SIGTRAP, sigtrap_handler);
|
ll_install_handler(SIGTRAP, sigtrap_handler);
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -787,3 +789,36 @@ lispobj call_into_lisp_first_time(lispobj fun, lispobj *args, int nargs) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "x86-arch-shared.inc"
|
#include "x86-arch-shared.inc"
|
||||||
|
|
||||||
|
void yieldpoint_patch_asm_routines(int flag)
|
||||||
|
{
|
||||||
|
struct code* code = (struct code*)asm_routines_start;
|
||||||
|
#ifdef LISP_FEATURE_DARWIN_JIT
|
||||||
|
lispobj ht = CONS(code->debug_info)->car;
|
||||||
|
#else
|
||||||
|
lispobj ht = code->debug_info;
|
||||||
|
#endif
|
||||||
|
struct vector* table =
|
||||||
|
VECTOR(((struct hash_table*)native_pointer(ht))->pairs);
|
||||||
|
int i;
|
||||||
|
uint32_t nop = 0x00401F0F; // really [0F 1F 40 00]
|
||||||
|
uint32_t yp = 0x806D8545; // really [45 85 6D 80]
|
||||||
|
uint32_t old = nop;
|
||||||
|
uint32_t new = yp;
|
||||||
|
if (!flag) { // deinstall them
|
||||||
|
old = yp;
|
||||||
|
new = nop;
|
||||||
|
}
|
||||||
|
for (i=2 ; i < vector_len(table) ; i += 2) {
|
||||||
|
lispobj value = table->data[i+1];
|
||||||
|
if (listp(value)) {
|
||||||
|
// value = (start-address . (end-address . index))
|
||||||
|
char* entrypoint
|
||||||
|
= code_text_start(code) + fixnum_value(CONS(value)->car);
|
||||||
|
uint32_t* pdword = (uint32_t*)entrypoint;
|
||||||
|
if (*pdword == old)
|
||||||
|
__sync_bool_compare_and_swap(pdword, old, new);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -460,3 +460,12 @@ ascs_check_loop:
|
||||||
ascs_finished:
|
ascs_finished:
|
||||||
ret
|
ret
|
||||||
SIZE(GNAME(arch_scrub_control_stack))
|
SIZE(GNAME(arch_scrub_control_stack))
|
||||||
|
|
||||||
|
.global GNAME(foo)
|
||||||
|
GNAME(foo): .byte 0x66, 0x45, 0x0f, 0x38, 0x2a, 0x7d, 0x80
|
||||||
|
.byte 0x45, 0x0f, 0x38, 0x2a, 0x7d, 0x80
|
||||||
|
|
||||||
|
.globl GNAME(bar)
|
||||||
|
GNAME(bar):
|
||||||
|
vmovntdqa -128(%r13), %xmm15
|
||||||
|
vmovntdqa -128(%r13), %ymm15
|
||||||
|
|
|
||||||
760
src/runtime/yieldpoints.c
Normal file
760
src/runtime/yieldpoints.c
Normal file
|
|
@ -0,0 +1,760 @@
|
||||||
|
#define _GNU_SOURCE // for pthread_setname_np
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <limits.h> // INT_MAX
|
||||||
|
#include <errno.h>
|
||||||
|
#include "thread.h"
|
||||||
|
#include "interr.h"
|
||||||
|
#include "gc-assert.h"
|
||||||
|
#include "core.h"
|
||||||
|
#include "atomiclog.inc"
|
||||||
|
|
||||||
|
typedef enum { SUSPEND=1, ASYNC_SIGNAL=2 } yield_reason;
|
||||||
|
|
||||||
|
static int debug = 0;
|
||||||
|
|
||||||
|
typedef enum { UNKNOWN, IN_LISP, LISP_TO_C, C_TO_LISP } yieldpoint_kind;
|
||||||
|
int yp_kind_hit[4];
|
||||||
|
|
||||||
|
static __thread char threadname[20];
|
||||||
|
char *pthreadname(struct thread* th) {
|
||||||
|
pthread_getname_np(th->os_thread, threadname, 20);
|
||||||
|
//sprintf(threadname, "%p", th);
|
||||||
|
return threadname;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *thread_yieldpoint_trap_page_base(struct thread*th ) {
|
||||||
|
return (char*)th - THREAD_HEADER_SLOTS*N_WORD_BYTES - THREAD_YIELDPOINT_PAGE_SIZE;
|
||||||
|
}
|
||||||
|
typedef enum { READ_WRITE, READ_ONLY, NO_ACCESS } yieldpoint_page_acc;
|
||||||
|
void yieldpoint_trap_toggle(struct thread* th,
|
||||||
|
yieldpoint_page_acc access)
|
||||||
|
{
|
||||||
|
int prot_mode[3] = { OS_VM_PROT_ALL, PROT_READ, PROT_NONE };
|
||||||
|
char* page_base = thread_yieldpoint_trap_page_base(th);
|
||||||
|
// fprintf(stderr, "YP page protect: %d @ %p\n", THREAD_YIELDPOINT_PAGE_SIZE, page_base);
|
||||||
|
mprotect(page_base, THREAD_YIELDPOINT_PAGE_SIZE, prot_mode[access]);
|
||||||
|
th->yieldpoint_page_access = access;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define INITIATE_GC 0x100
|
||||||
|
#define GC_CONTROLLER_QUIT 0x200
|
||||||
|
|
||||||
|
extern pthread_rwlock_t all_threads_lock;
|
||||||
|
pthread_mutex_t gc_condition_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
pthread_cond_t gc_controller_cond = PTHREAD_COND_INITIALIZER;
|
||||||
|
pthread_cond_t gc_completion_cond = PTHREAD_COND_INITIALIZER;
|
||||||
|
pthread_t gc_controller;
|
||||||
|
pthread_t rdb_thread;
|
||||||
|
int gc_notification_reason;
|
||||||
|
os_sem_t gc_sync_sem;
|
||||||
|
long gc_cycle_count;
|
||||||
|
|
||||||
|
static lispobj* yieldpoint_trap_addr(struct thread* th) {
|
||||||
|
return (lispobj*)((char*)th - 128);
|
||||||
|
}
|
||||||
|
static lispobj get_stackptr_at_foreign_call(struct thread* th) {
|
||||||
|
return *yieldpoint_trap_addr(th);
|
||||||
|
}
|
||||||
|
static void clear_stackptr_at_foreign_call(struct thread* th) {
|
||||||
|
*yieldpoint_trap_addr(th) = 0;
|
||||||
|
}
|
||||||
|
void gc_inhibitor_control(int flag) {
|
||||||
|
if (flag)
|
||||||
|
pthread_rwlock_rdlock(&all_threads_lock);
|
||||||
|
else
|
||||||
|
pthread_rwlock_unlock(&all_threads_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern int futex_wait(int*, int, long, unsigned long);
|
||||||
|
extern int futex_wake(int*, int);
|
||||||
|
// FIXME: uint32_t as per https://man7.org/linux/man-pages/man2/futex.2.html
|
||||||
|
// but our os.c files all use 'int'
|
||||||
|
static int32_t world_is_stopped;
|
||||||
|
|
||||||
|
static void force_tlab_slowpath(struct alloc_region* r, lispobj* spill)
|
||||||
|
{
|
||||||
|
void* real_end = __sync_fetch_and_or(&r->end_addr, 0);
|
||||||
|
/* By stuffing in 0 for 'end' the thread's allocator will fail its next comparison
|
||||||
|
* against the TLAB limit, reverting to the slow path allocator, at which point it
|
||||||
|
* observes the stop request. The only synchronization needed with the mutator
|
||||||
|
* is to ensure we've got an up-to-date copy of the actual end before clobbering it */
|
||||||
|
while (1) {
|
||||||
|
*spill = (lispobj)real_end;
|
||||||
|
if (real_end == REGION_END_EMPTY_VALUE) break;
|
||||||
|
void* oldval = __sync_val_compare_and_swap(&r->end_addr, real_end, 0);
|
||||||
|
if (oldval == real_end) break;
|
||||||
|
real_end = oldval;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static void restore_tlab(struct alloc_region* r, lispobj* spill)
|
||||||
|
{
|
||||||
|
if (r->end_addr == 0) {
|
||||||
|
lispobj actual_end = *spill;
|
||||||
|
gc_assert(actual_end != 0);
|
||||||
|
r->end_addr = (void*)actual_end;
|
||||||
|
}
|
||||||
|
*spill = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define TELNET_SERVER 1
|
||||||
|
#if TELNET_SERVER
|
||||||
|
#define MAXTHREADS 50
|
||||||
|
static struct {
|
||||||
|
struct thread* th;
|
||||||
|
char in_foreign_code;
|
||||||
|
char gc_waiting_on_spinlock;
|
||||||
|
//char did_sem_post;
|
||||||
|
} known_threads[MAXTHREADS];
|
||||||
|
static int n_known_threads;
|
||||||
|
static int sem_waits;
|
||||||
|
static int get_n_currently_waiting_on() { return sem_waits; }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Acquire the locks in this order:
|
||||||
|
// condition_lock, mutators lock, per-thread spinlock (one at a time)
|
||||||
|
void gc_stop_the_world() {
|
||||||
|
event0("begin stw");
|
||||||
|
struct thread* me = get_sb_vm_thread();
|
||||||
|
struct thread* th;
|
||||||
|
/* The global "stopped" flag is set *before* doing anything else
|
||||||
|
* because as we're arming the trap for each thread in turn, any thread
|
||||||
|
* that hits a C-to-Lisp yieldpoint should block at the futex_wait call
|
||||||
|
* as though the world is already stopped */
|
||||||
|
world_is_stopped = 1;
|
||||||
|
sem_waits = 0;
|
||||||
|
// int sem_waits = 0;
|
||||||
|
// TODO: for all threads _except_ the finalizer, stop them; then ask the finalizer
|
||||||
|
// thread to run the pre-GC actions (clobber the hash-caches, etc) and stop itself.
|
||||||
|
for_each_thread(th) {
|
||||||
|
#if TELNET_SERVER
|
||||||
|
int ti = n_known_threads++;
|
||||||
|
if (ti > MAXTHREADS) lose("Too many cooks");
|
||||||
|
known_threads[ti].th = th;
|
||||||
|
#endif
|
||||||
|
// Thread can't change its state to DEAD without acquiring the rwlock for reading
|
||||||
|
// Therefore if non-dead, it will participate in stop-for-gc by waiting for a
|
||||||
|
// stop-the-world trap, and it will respond with sem_post.
|
||||||
|
if (th == me) continue;
|
||||||
|
//
|
||||||
|
known_threads[ti].gc_waiting_on_spinlock = 1;
|
||||||
|
acquire_lock(&th->yieldpoint_spinlock);
|
||||||
|
if (th->state_word.state == STATE_DEAD) {
|
||||||
|
known_threads[ti].gc_waiting_on_spinlock = 0;
|
||||||
|
release_lock(&th->yieldpoint_spinlock);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// th->stepping.suspend = -1;
|
||||||
|
if (debug) fprintf(stderr, "GC: will stop thread %p (%s)\n", th, pthreadname(th));
|
||||||
|
__sync_fetch_and_or(&th->interrupt_reason, SUSPEND);
|
||||||
|
force_tlab_slowpath(&th->cons_tlab, &th->spill_cons_tlab_end);
|
||||||
|
force_tlab_slowpath(&th->sys_cons_tlab, &th->spill_sys_cons_tlab_end);
|
||||||
|
force_tlab_slowpath(&th->mixed_tlab, &th->spill_mixed_tlab_end);
|
||||||
|
force_tlab_slowpath(&th->sys_mixed_tlab, &th->spill_sys_mixed_tlab_end);
|
||||||
|
yieldpoint_trap_toggle(th, READ_ONLY);
|
||||||
|
lispobj stackptr = get_stackptr_at_foreign_call(th);
|
||||||
|
yieldpoint_trap_toggle(th, NO_ACCESS);
|
||||||
|
release_lock(&th->yieldpoint_spinlock);
|
||||||
|
known_threads[ti].gc_waiting_on_spinlock = 0;
|
||||||
|
event2("stopping %p, sp=%lx", th, stackptr);
|
||||||
|
if (stackptr) {
|
||||||
|
known_threads[ti].in_foreign_code = 1;
|
||||||
|
/* Assume that the next instruction after a Lisp-to-C yieldpoint
|
||||||
|
* is a call into C which pushes the return address on the stack.
|
||||||
|
* This gets us the return PC "for free" relative to the #+sb-safepoint
|
||||||
|
* technique which uses LEA [RIP+n] to compute and store the PC. */
|
||||||
|
if ((lispobj*)stackptr - 1 >= th->control_stack_start)
|
||||||
|
stackptr -= N_WORD_BYTES;
|
||||||
|
gc_assert(stackptr >= (lispobj)th->control_stack_start &&
|
||||||
|
stackptr < (lispobj)th->control_stack_end);
|
||||||
|
th->control_stack_pointer = (lispobj*)stackptr;
|
||||||
|
if (debug)
|
||||||
|
fprintf(stderr, "thread %p stackptr = %p = end-%dw\n",
|
||||||
|
th, th->control_stack_pointer,
|
||||||
|
(int)(th->control_stack_end - th->control_stack_pointer));
|
||||||
|
}
|
||||||
|
if (stackptr == 0) ++sem_waits; // the thread is in Lisp
|
||||||
|
}
|
||||||
|
if (debug) fprintf(stderr, "GC: sem_wait for %d\n", sem_waits);
|
||||||
|
event1("waiting on %d threads", sem_waits);
|
||||||
|
// Wait for all threads executing Lisp (i.e. not foreign code) to stop
|
||||||
|
while (sem_waits--) sem_wait(&gc_sync_sem);
|
||||||
|
// Assert that every threads' stack pointer is known
|
||||||
|
for_each_thread(th) {
|
||||||
|
if (th->control_stack_pointer != 0 ||
|
||||||
|
read_TLS(FREE_INTERRUPT_CONTEXT_INDEX, th)) {
|
||||||
|
} else {
|
||||||
|
lose("Thread %p context is unknown", th);
|
||||||
|
}
|
||||||
|
restore_tlab(&th->cons_tlab, &th->spill_cons_tlab_end);
|
||||||
|
restore_tlab(&th->sys_cons_tlab, &th->spill_sys_cons_tlab_end);
|
||||||
|
restore_tlab(&th->mixed_tlab, &th->spill_mixed_tlab_end);
|
||||||
|
restore_tlab(&th->sys_mixed_tlab, &th->spill_sys_mixed_tlab_end);
|
||||||
|
}
|
||||||
|
n_known_threads = 0;
|
||||||
|
memset(known_threads, 0, sizeof known_threads);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gc_start_the_world() {
|
||||||
|
struct thread* th;
|
||||||
|
for_each_thread(th) {
|
||||||
|
th->control_stack_pointer = 0;
|
||||||
|
__sync_fetch_and_and(&th->interrupt_reason, ~SUSPEND);
|
||||||
|
//th->stepping.suspend = 0;
|
||||||
|
}
|
||||||
|
world_is_stopped = 0;
|
||||||
|
futex_wake(&world_is_stopped, INT_MAX);
|
||||||
|
if (debug) fprintf(stderr, "GC: world restarted\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void show_yp_kind_hit() {
|
||||||
|
fprintf(stderr, "YP hit: %d %d %d %d\n", yp_kind_hit[0], yp_kind_hit[1], yp_kind_hit[2], yp_kind_hit[3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Finalizer thread should wake on any of the following events:
|
||||||
|
- finalizer item queued
|
||||||
|
- interrupt-thread action
|
||||||
|
- other deferred async signal
|
||||||
|
- post-gc hooks
|
||||||
|
The first 3 are working, the last is not. I need a way to tell
|
||||||
|
it to run the hooks */
|
||||||
|
|
||||||
|
extern int finalizer_thread_runflag;
|
||||||
|
void finalizer_thread_wait () {
|
||||||
|
/* pthread_cond_wait can never return EINTR which is what made
|
||||||
|
* SB-THREAD:INTERRUPT-THREAD unreliable on the finalizer.
|
||||||
|
* Direct use of a futex gives nicer behavior */
|
||||||
|
int c = finalizer_thread_runflag;
|
||||||
|
if (c) futex_wait(&finalizer_thread_runflag, c, -1, 0);
|
||||||
|
|
||||||
|
}
|
||||||
|
void finalizer_thread_wake () {
|
||||||
|
futex_wake(&finalizer_thread_runflag, 1);
|
||||||
|
}
|
||||||
|
void finalizer_thread_stop () {
|
||||||
|
finalizer_thread_runflag = 0;
|
||||||
|
futex_wake(&finalizer_thread_runflag, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern void yieldpoint_patch_asm_routines(int);
|
||||||
|
void* collector_main(__attribute__((unused)) void* dummy)
|
||||||
|
{
|
||||||
|
#ifdef LISP_FEATURE_LINUX
|
||||||
|
pthread_setname_np(pthread_self(), "collectorMain");
|
||||||
|
#endif
|
||||||
|
pthread_mutex_lock(&gc_condition_lock);
|
||||||
|
for (;;) {
|
||||||
|
pthread_cond_wait(&gc_controller_cond, &gc_condition_lock);
|
||||||
|
int reason = gc_notification_reason;
|
||||||
|
if (reason & GC_CONTROLLER_QUIT) break;
|
||||||
|
if (!(gc_notification_reason & INITIATE_GC)) continue; // sleep some more
|
||||||
|
if (debug) fprintf(stderr, "GC main wants to run\n");
|
||||||
|
/* One or more threads that we need to try to suspend might each be
|
||||||
|
* trying to acquire the lock to signal a GC request, so never hold
|
||||||
|
* the condition lock while trying to initiat a collection */
|
||||||
|
pthread_mutex_unlock(&gc_condition_lock);
|
||||||
|
/* No thread can die nor be created; and no thread can inhibit GC. */
|
||||||
|
pthread_rwlock_wrlock(&all_threads_lock);
|
||||||
|
// Patching is needed in order for threads to reach a yieldpoint quicker,
|
||||||
|
// since asm routines have NOPs where yieldpoints should be.
|
||||||
|
//yieldpoint_patch_asm_routines(1);
|
||||||
|
gc_stop_the_world();
|
||||||
|
int generation = gc_notification_reason & 7;
|
||||||
|
extern void collect_garbage(generation_index_t);
|
||||||
|
//show_yp_kind_hit();
|
||||||
|
collect_garbage(generation);
|
||||||
|
gc_notification_reason = 0;
|
||||||
|
++gc_cycle_count;
|
||||||
|
// TODO: enqueue a finalizer thread action to run post-GC hooks
|
||||||
|
gc_start_the_world();
|
||||||
|
pthread_rwlock_unlock(&all_threads_lock);
|
||||||
|
finalizer_thread_wake();
|
||||||
|
pthread_mutex_lock(&gc_condition_lock);
|
||||||
|
// Manually requested GC should wait on this condition
|
||||||
|
pthread_cond_broadcast(&gc_completion_cond);
|
||||||
|
extern void empty_thread_recyclebin();
|
||||||
|
empty_thread_recyclebin();
|
||||||
|
// can be un-patched at any time
|
||||||
|
//yieldpoint_patch_asm_routines(0);
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&gc_condition_lock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if TELNET_SERVER
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
int listener;
|
||||||
|
void* debugging_service_main(__attribute__((unused)) void* dummy)
|
||||||
|
{
|
||||||
|
#if 0
|
||||||
|
{sigset_t mask;
|
||||||
|
pthread_sigmask(SIG_SETMASK, 0, &mask);
|
||||||
|
char buf[100]; sigset_tostring(&mask, buf, sizeof buf);
|
||||||
|
printf("service thread has mask %s\n", buf);
|
||||||
|
sigset_tostring(&deferrable_sigset, buf, sizeof buf);
|
||||||
|
printf("deferrable is %s\n", buf);
|
||||||
|
sigset_tostring(&blockable_sigset, buf, sizeof buf);
|
||||||
|
printf("blockable is %s\n", buf);}
|
||||||
|
#endif
|
||||||
|
struct sockaddr_in sin;
|
||||||
|
memset(&sin, 0, sizeof sin);
|
||||||
|
sin.sin_addr.s_addr = INADDR_ANY;
|
||||||
|
sin.sin_port = 0;
|
||||||
|
listener = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
if (bind(listener, &sin, sizeof sin)) {perror("bind");exit(1);}
|
||||||
|
if (listen(listener, 5)) {perror("listen");exit(1);}
|
||||||
|
socklen_t addrlen = sizeof sin;
|
||||||
|
if (getsockname(listener, &sin, &addrlen)) {perror("getsockname");exit(1);}
|
||||||
|
char buf[64];
|
||||||
|
int n = snprintf(buf, sizeof buf, "Pid %d listening on port %d\n", getpid(), ntohs(sin.sin_port));
|
||||||
|
write(2, buf, n);
|
||||||
|
int peer;
|
||||||
|
FILE* stream = 0;
|
||||||
|
while (1) {
|
||||||
|
peer = accept(listener, &sin, &addrlen);
|
||||||
|
if (peer < 0) lose("wtf?");
|
||||||
|
stream = fdopen(peer, "w");
|
||||||
|
setlinebuf(stream);
|
||||||
|
fprintf(stream, "nthreads=%d waiting_on=%d\n", n_known_threads, get_n_currently_waiting_on());
|
||||||
|
fprintf(stream, " pthread vm_thread tid State Lip/C spinlock_wait\n");
|
||||||
|
{int i;for (i=0;i<n_known_threads;++i) {
|
||||||
|
struct thread* th = known_threads[i].th;
|
||||||
|
if (!th) {
|
||||||
|
fprintf(stream, "%d: bad\n", i);
|
||||||
|
} else {
|
||||||
|
int sw = th->state_word.state;
|
||||||
|
lispobj mutex = read_TLS(CURRENT_MUTEX, th);
|
||||||
|
fprintf(stream, "%d: %lx %p %lx %s %s %d ",
|
||||||
|
i, th->os_thread, th, th->os_kernel_tid,
|
||||||
|
(sw==STATE_DEAD?"DEAD":sw==STATE_RUNNING?"RUN ":sw==STATE_STOPPED?"STOP":"????"),
|
||||||
|
known_threads[i].in_foreign_code?"C ":"Lisp",
|
||||||
|
known_threads[i].gc_waiting_on_spinlock);
|
||||||
|
if (mutex != 0) {
|
||||||
|
struct lispmutex* m = (void*)native_pointer(mutex);
|
||||||
|
lispobj name = m->name;
|
||||||
|
fprintf(stderr, "mutex=%lx (state=%lx owner=%lx name=%s)",
|
||||||
|
mutex, m->uw_state, m->_owner,
|
||||||
|
(other_pointer_p(name) && simple_base_string_p(name)) ? (char*)(name+1) : "?");
|
||||||
|
}
|
||||||
|
putc('\n', stderr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (1) {
|
||||||
|
char line[80];
|
||||||
|
write(peer, "> ", 2);
|
||||||
|
int n = read(peer, line, sizeof line);
|
||||||
|
if (n<=0){ fclose(stream); break; }
|
||||||
|
printf("got n=%d command [%s]\n", n, line);
|
||||||
|
char cmd = line[0];
|
||||||
|
if (cmd == 'q') break;
|
||||||
|
write(peer, "m'kay\n",6);
|
||||||
|
}
|
||||||
|
printf("remote debug service connected\n");
|
||||||
|
close(peer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void safepoint_init() { // backward-compatible function name
|
||||||
|
os_sem_init(&gc_sync_sem, 0);
|
||||||
|
sigset_t oldmask;
|
||||||
|
// Don't want SIGCHLD, SIGARLM, etc
|
||||||
|
pthread_sigmask(SIG_BLOCK, &blockable_sigset, &oldmask);
|
||||||
|
pthread_create(&gc_controller, NULL, collector_main, 0);
|
||||||
|
if (debug) fprintf(stderr, "GC controller thread is %p\n", (void*)gc_controller);
|
||||||
|
#ifdef TELNET_SERVER
|
||||||
|
pthread_create(&rdb_thread, NULL, debugging_service_main, 0);
|
||||||
|
pthread_setname_np(rdb_thread, "debug");
|
||||||
|
#endif
|
||||||
|
pthread_sigmask(SIG_SETMASK, &oldmask, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void request_garbage_collection(int generation)
|
||||||
|
{
|
||||||
|
if (generation == -1) { // auto-triggered request
|
||||||
|
// a read barrier is not important here
|
||||||
|
if (gc_notification_reason) return; // request already pending
|
||||||
|
pthread_mutex_lock(&gc_condition_lock);
|
||||||
|
gc_notification_reason |= INITIATE_GC;
|
||||||
|
pthread_cond_broadcast(&gc_controller_cond);
|
||||||
|
pthread_mutex_unlock(&gc_condition_lock);
|
||||||
|
} else {
|
||||||
|
pthread_mutex_lock(&gc_condition_lock);
|
||||||
|
/* Take the higher of competing args to SB-EXT:GC if two user threads
|
||||||
|
* nearly simultaneously invoke it */
|
||||||
|
int pending = gc_notification_reason & 7;
|
||||||
|
if (pending > generation) generation = pending;
|
||||||
|
gc_notification_reason = INITIATE_GC | generation;
|
||||||
|
long starting_count = gc_cycle_count;
|
||||||
|
/* The Lisp side will deal with the case of explicitly requesting a collection
|
||||||
|
* while inside without-gcing. That's kinda stupid and it's unclear why we would
|
||||||
|
* want to support that, other than regression tests assert that it works */
|
||||||
|
pthread_cond_broadcast(&gc_controller_cond);
|
||||||
|
while (gc_cycle_count == starting_count)
|
||||||
|
pthread_cond_wait(&gc_completion_cond, &gc_condition_lock);
|
||||||
|
pthread_mutex_unlock(&gc_condition_lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern void
|
||||||
|
store_signal_data_for_later (struct interrupt_data *data, void *handler,
|
||||||
|
int signal,
|
||||||
|
siginfo_t *info, os_context_t *context);
|
||||||
|
|
||||||
|
/* Unlike maybe_now_maybe_later, this handler never handles a signal right away
|
||||||
|
* but instead always defers it to a yieldpoint.
|
||||||
|
* It would be neat if we could figure out which Lisp thread is in the foreground
|
||||||
|
* so SIGINT could be directed correctly the first time instead of having to
|
||||||
|
* potentially re-signal it in Lisp */
|
||||||
|
void defer_to_yieldpoint(int sig, siginfo_t* info, void* context)
|
||||||
|
{
|
||||||
|
int _saved_errno = errno;
|
||||||
|
struct thread* th = get_sb_vm_thread();
|
||||||
|
store_signal_data_for_later(&thread_interrupt_data(th), (void*)1,
|
||||||
|
sig, info, context);
|
||||||
|
lispobj enabled = read_TLS(INTERRUPTS_ENABLED, th);
|
||||||
|
#if 1
|
||||||
|
{
|
||||||
|
char b[100];
|
||||||
|
int n = snprintf(b, sizeof b, "%s: Deferring: %s (pc=%lx) %s\n",
|
||||||
|
pthreadname(th), strsignal(sig),
|
||||||
|
os_context_pc(context),
|
||||||
|
enabled==NIL?"DISABLED":enabled==LISP_T?"ENABLED":"?");
|
||||||
|
|
||||||
|
write(2,b,n);
|
||||||
|
//libunwind_backtrace(th, context);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (enabled == NIL) {
|
||||||
|
write_TLS(INTERRUPTS_ENABLED, 0, th); // 0 = "signal deferred"
|
||||||
|
/* WITHOUT-INTERRUPTS will check whether a signal got deferred,
|
||||||
|
* and will explicitly handle it without a yieldpoint trap */
|
||||||
|
} else if (enabled == LISP_T) {
|
||||||
|
/* If the lock is already taken, then either the GC controller is frobbing it,
|
||||||
|
* or this thread itself is in alloc_cooperate. In either case we don't need
|
||||||
|
* to arm the trap. The worst that happens is a deferred interrupt
|
||||||
|
* gets handled a little later */
|
||||||
|
__sync_fetch_and_or(&th->interrupt_reason, ASYNC_SIGNAL);
|
||||||
|
if (try_acquire_lock(&th->yieldpoint_spinlock)) {
|
||||||
|
yieldpoint_trap_toggle(th, NO_ACCESS);
|
||||||
|
release_lock(&th->yieldpoint_spinlock);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
lose("Received async interrupt with enable neither T nor NIL");
|
||||||
|
}
|
||||||
|
errno = _saved_errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void publish_context(struct thread* thread, os_context_t* context)
|
||||||
|
{
|
||||||
|
/* Do dynamic binding of the active interrupt context index
|
||||||
|
* and save the context in the context array. */
|
||||||
|
int context_index =
|
||||||
|
fixnum_value(read_TLS(FREE_INTERRUPT_CONTEXT_INDEX, thread));
|
||||||
|
if (context_index >= MAX_INTERRUPTS)
|
||||||
|
lose("maximum interrupt nesting depth (%d) exceeded", MAX_INTERRUPTS);
|
||||||
|
|
||||||
|
bind_variable(FREE_INTERRUPT_CONTEXT_INDEX, make_fixnum(context_index + 1), thread);
|
||||||
|
nth_interrupt_context(context_index, thread) = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void unpublish_context(struct thread* thread)
|
||||||
|
{
|
||||||
|
// Never leave stale pointers in the signal context array
|
||||||
|
int ici = fixnum_value(read_TLS(FREE_INTERRUPT_CONTEXT_INDEX,thread));
|
||||||
|
nth_interrupt_context(ici - 1, thread) = NULL;
|
||||||
|
/* Undo dynamic binding of FREE_INTERRUPT_CONTEXT_INDEX */
|
||||||
|
unbind(thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
void sigtrap_handler(int __attribute__((unused)) signal,
|
||||||
|
siginfo_t __attribute__((unused)) *info,
|
||||||
|
os_context_t *context)
|
||||||
|
{
|
||||||
|
struct thread*th = get_sb_vm_thread();
|
||||||
|
if (!th->stepping.suspend) lose("Why are you closed?");
|
||||||
|
publish_context(th, context);
|
||||||
|
sem_post(&gc_sync_sem);
|
||||||
|
if (debug) {
|
||||||
|
char buf[100];
|
||||||
|
int n = snprintf(buf, sizeof buf, "%s: posted\n", pthreadname(th));
|
||||||
|
write(2, buf, n);
|
||||||
|
}
|
||||||
|
do { futex_wait(&world_is_stopped, 1, -1, 0); } while (world_is_stopped);
|
||||||
|
if (debug) fprintf(stderr, "%s: back from futex_wait\n", pthreadname(th));
|
||||||
|
unpublish_context(th);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void suspend_for_gc(struct thread* th)
|
||||||
|
{
|
||||||
|
if (debug)fprintf(stderr, "%s: need to suspend in allocator\n", pthreadname(th));
|
||||||
|
__sync_fetch_and_add(&yp_kind_hit[0], 1);
|
||||||
|
ucontext_t context; // don't really need this whole context, but that's fine
|
||||||
|
getcontext(&context);
|
||||||
|
publish_context(th, &context);
|
||||||
|
th->state_word.state = STATE_STOPPED;
|
||||||
|
sem_post(&gc_sync_sem);
|
||||||
|
do { futex_wait(&world_is_stopped, 1, -1, 0); } while (world_is_stopped);
|
||||||
|
th->state_word.state = STATE_RUNNING;
|
||||||
|
if (debug) fprintf(stderr, "%s: back from futex_wait in allocator\n", pthreadname(th));
|
||||||
|
unpublish_context(th);
|
||||||
|
}
|
||||||
|
|
||||||
|
void maybe_suspend_for_gc(struct thread* th)
|
||||||
|
{
|
||||||
|
if (th->interrupt_reason & SUSPEND) suspend_for_gc(th);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return a copy of the thread's region. We operate on the copy so that it is not
|
||||||
|
* succeptible to having the 'end' slot clobbered while in the call sequence of lisp_alloc. */
|
||||||
|
struct alloc_region alloc_cooperate_before(struct alloc_region* r, struct thread* th)
|
||||||
|
{
|
||||||
|
struct alloc_region result;
|
||||||
|
while (1) {
|
||||||
|
// acquire_lock(&th->yieldpoint_spinlock);
|
||||||
|
result = *r;
|
||||||
|
// release_lock(&th->yieldpoint_spinlock);
|
||||||
|
if (result.end_addr) return result;
|
||||||
|
gc_assert(th->interrupt_reason & SUSPEND);
|
||||||
|
suspend_for_gc(th);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static lispobj* region_end_spill_slot(struct thread* th, struct alloc_region* r)
|
||||||
|
{
|
||||||
|
if (r == &th->cons_tlab) return &th->spill_cons_tlab_end;
|
||||||
|
if (r == &th->sys_cons_tlab) return &th->spill_sys_cons_tlab_end;
|
||||||
|
if (r == &th->mixed_tlab) return &th->spill_mixed_tlab_end;
|
||||||
|
if (r == &th->sys_mixed_tlab) return &th->spill_sys_mixed_tlab_end;
|
||||||
|
lose("Unknown TLAB %p for thread %p", r, th);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Restore the thread's TLAB from the shadow copy. If GC already forced the region
|
||||||
|
* into a slow path fallback, we have replicate that effect. It won't work to yield
|
||||||
|
* now because we'll lose the object whose space was just allocated but whose bits
|
||||||
|
* were not written */
|
||||||
|
void alloc_cooperate_after(struct alloc_region* dst, struct alloc_region* src,
|
||||||
|
struct thread* th)
|
||||||
|
{
|
||||||
|
acquire_lock(&th->yieldpoint_spinlock);
|
||||||
|
bool yield = !dst->end_addr;
|
||||||
|
*dst = *src;
|
||||||
|
lispobj* spill_slot = region_end_spill_slot(th, dst);
|
||||||
|
if (yield)
|
||||||
|
force_tlab_slowpath(dst, spill_slot);
|
||||||
|
else
|
||||||
|
gc_assert(*spill_slot == 0);
|
||||||
|
release_lock(&th->yieldpoint_spinlock);
|
||||||
|
#if 0
|
||||||
|
if (yield) //start_addr != src->start_addr || dst->end_addr != src->end_addr)
|
||||||
|
fprintf(stderr, "cooperate_after detected detected change of region: TLAB=%p:%p (free=%p) shadow=%p:%p (free=%p) yield=%d\n",
|
||||||
|
dst->start_addr, dst->end_addr, dst->free_pointer,
|
||||||
|
src->start_addr, src->end_addr, src->free_pointer, yield);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef LISP_FEATURE_X86_64
|
||||||
|
static yieldpoint_kind infer_kind(unsigned char* pc) {
|
||||||
|
if (pc[0] == 0x45 && pc[1] == 0x84) return IN_LISP; // TEST BYTE
|
||||||
|
if (pc[0] == 0x45 && pc[1] == 0x85) return IN_LISP; // TEST DWORD
|
||||||
|
if (pc[0] == 0x66 && pc[1] == 0x45) return IN_LISP; // MOVNTDQA
|
||||||
|
if (pc[0] == 0xC4 && pc[1] == 0x42) return IN_LISP; // VMOVNTDQA
|
||||||
|
if (pc[0] == 0x49 && pc[1] == 0x89) return LISP_TO_C; // MOV mem, RSP
|
||||||
|
if (pc[0] == 0x49 && pc[1] == 0x31) return C_TO_LISP; // XOR mem, RSP
|
||||||
|
return UNKNOWN;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void yieldpoint_trap(struct thread* th, os_context_t *context)
|
||||||
|
{
|
||||||
|
unsigned char* pc = (void*)os_context_pc(context);
|
||||||
|
yieldpoint_kind kind = infer_kind(pc);
|
||||||
|
if (debug) {
|
||||||
|
char buf[100];
|
||||||
|
int n = snprintf(buf, sizeof buf, "%s: YP trap %d %d pc=%p\n",
|
||||||
|
pthreadname(th), (int)th->interrupt_reason, kind, pc);
|
||||||
|
write(2, buf, n);
|
||||||
|
sb_dump_mcontext("foo", context);
|
||||||
|
}
|
||||||
|
event3("YP trap %x %d %p", th->interrupt_reason, kind, pc);
|
||||||
|
if (kind == UNKNOWN) lose("unrecognized yieldpoint @ %p", pc);
|
||||||
|
__sync_fetch_and_add(&yp_kind_hit[kind], 1);
|
||||||
|
while (1) {
|
||||||
|
int reason = th->interrupt_reason;
|
||||||
|
if (!reason) {
|
||||||
|
/* Disarm the trap but only if a double-check of the trap reason
|
||||||
|
* confirms that it need not be armed. A yieldpoint trap can't happen
|
||||||
|
* in alloc_cooperate, so this can't deadlock with itself */
|
||||||
|
acquire_lock(&th->yieldpoint_spinlock);
|
||||||
|
// FIXME: atomic load probably needed here? especially for non-x86
|
||||||
|
reason = th->interrupt_reason;
|
||||||
|
if (!reason) yieldpoint_trap_toggle(th, READ_WRITE);
|
||||||
|
release_lock(&th->yieldpoint_spinlock);
|
||||||
|
if (!reason) return;
|
||||||
|
}
|
||||||
|
/* Always cooperate with GC before doing anything else. */
|
||||||
|
if (reason & SUSPEND) {
|
||||||
|
int context_published = 0;
|
||||||
|
th->state_word.state = STATE_STOPPED;
|
||||||
|
if (kind == C_TO_LISP) {
|
||||||
|
/* Trapped trying to get back to Lisp from a C call. Do not sem_post,
|
||||||
|
* just wait until world isn't stopped. */
|
||||||
|
} else {
|
||||||
|
publish_context(th, context);
|
||||||
|
context_published = 1;
|
||||||
|
sem_post(&gc_sync_sem);
|
||||||
|
if (debug) {
|
||||||
|
char buf[100];
|
||||||
|
int n = snprintf(buf, sizeof buf, "%s: posted\n", pthreadname(th));
|
||||||
|
write(2, buf, n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
do { futex_wait(&world_is_stopped, 1, -1, 0); } while (world_is_stopped);
|
||||||
|
th->state_word.state = STATE_RUNNING;
|
||||||
|
if (debug) fprintf(stderr, "%s: back from futex_wait\n", pthreadname(th));
|
||||||
|
if (context_published) unpublish_context(th);
|
||||||
|
reason &= ~SUSPEND;
|
||||||
|
}
|
||||||
|
if (!reason) continue; // try to return now
|
||||||
|
gc_assert(reason & ASYNC_SIGNAL);
|
||||||
|
// Make the yieldpoint instruction act like it was a CALL to a Lisp asm routine
|
||||||
|
static char* asm_trampoline;
|
||||||
|
if (!asm_trampoline) { // Lookup and/or memoize the routine to call
|
||||||
|
asm_trampoline = get_asm_routine_by_name("HANDLE-DEFERRED-SIGNAL", 0);
|
||||||
|
gc_assert(asm_trampoline);
|
||||||
|
if (debug) fprintf(stderr, "YP asm tramp is %p\n", asm_trampoline);
|
||||||
|
}
|
||||||
|
/* Disarm the trap, ensuring that we're "in Lisp" regardless of the kind of yieldpoint,
|
||||||
|
* because we don't want to handle async signals from C. (The Lisp signal handler
|
||||||
|
* is ordinary Lisp code that can cons, triggering a garbage collection)
|
||||||
|
* But also consider the following theoretically possible sequence:
|
||||||
|
* GC is initiated, arms this thread's trap, completes, is immediately re-initiated
|
||||||
|
* and arms this thread's trap. If we unilaterally disarm it, then we will miss the
|
||||||
|
* second stop request that arrived in rapid-fire succession */
|
||||||
|
acquire_lock(&th->yieldpoint_spinlock);
|
||||||
|
if (th->interrupt_reason & SUSPEND) { // dang, not again?
|
||||||
|
release_lock(&th->yieldpoint_spinlock);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
yieldpoint_trap_toggle(th, READ_WRITE);
|
||||||
|
clear_stackptr_at_foreign_call(th);
|
||||||
|
release_lock(&th->yieldpoint_spinlock);
|
||||||
|
os_context_register_t return_to = (os_context_register_t)pc;
|
||||||
|
/* If returning from foreign code (at a C-to-Lisp yieldpoint), step beyond the
|
||||||
|
* yieldpoint. Having cleared the stack-pointer slot in the thread structure,
|
||||||
|
* the collector understands that this thread is effectively in Lisp
|
||||||
|
* regardless of the kind of yieldpoint trap */
|
||||||
|
if (kind == C_TO_LISP) return_to += 4; // size of the XOR instruction
|
||||||
|
/* Simulate a CALL that returns to the yieldpoint (or one instruction after).
|
||||||
|
* God forbid this store hits the stack guard page.
|
||||||
|
* Maybe just don't handle the async signal in that case? */
|
||||||
|
lispobj* sp = (lispobj*)*os_context_sp_addr(context);
|
||||||
|
--sp;
|
||||||
|
fprintf(stderr, "YP trap: delivering signal to thread %p, pushing old PC %lx\n", th, return_to);
|
||||||
|
*sp = return_to;
|
||||||
|
*os_context_sp_addr(context) = (os_context_register_t)sp;
|
||||||
|
/* If yet another GC stop request arrives, the next yieldpoint will be in the
|
||||||
|
* prologue of the Lisp function that handles the deferred signal */
|
||||||
|
OS_CONTEXT_PC(context) = (os_context_register_t)asm_trampoline;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int handle_safepoint_violation(os_context_t *context, os_vm_address_t fault_address) {
|
||||||
|
struct thread*th = get_sb_vm_thread();
|
||||||
|
if (th && yieldpoint_trap_addr(th) == (lispobj*)fault_address) {
|
||||||
|
yieldpoint_trap(th, context);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int show_interrupt_data() {
|
||||||
|
struct thread*th = get_sb_vm_thread();
|
||||||
|
struct interrupt_data *data = &thread_interrupt_data(th);
|
||||||
|
siginfo_t *inf = &data->pending_info;
|
||||||
|
char string1[100], string2[100];
|
||||||
|
sigset_t oldmask;
|
||||||
|
int result = data->pending_signal;
|
||||||
|
pthread_sigmask(SIG_SETMASK, 0, &oldmask);
|
||||||
|
sigset_tostring(&oldmask, string1, 100);
|
||||||
|
sigset_tostring(&data->pending_mask, string2, 100);
|
||||||
|
printf("sig=%d no=%d errno=%d code=%d pid=%d uid=%d st=%d cur_mask=%s pend_mask=%s\n",
|
||||||
|
data->pending_signal, inf->si_signo, inf->si_errno, inf->si_code, inf->si_pid, inf->si_uid,
|
||||||
|
inf->si_status, string1, string2);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
int get_pending_signal_number() {
|
||||||
|
struct thread*th = get_sb_vm_thread();
|
||||||
|
return thread_interrupt_data(th).pending_signal;
|
||||||
|
}
|
||||||
|
void flush_pending_signal_and_restore() {
|
||||||
|
struct thread*th = get_sb_vm_thread();
|
||||||
|
struct interrupt_data *data = &thread_interrupt_data(th);
|
||||||
|
//siginfo_t *inf = &data->pending_info;
|
||||||
|
data->pending_handler = 0;
|
||||||
|
data->pending_signal = 0;
|
||||||
|
__sync_fetch_and_and(&th->interrupt_reason, ~ASYNC_SIGNAL);
|
||||||
|
pthread_sigmask(SIG_SETMASK, &data->pending_mask, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern void interrupt_handle_now_handler(int, siginfo_t*, void*);
|
||||||
|
extern void sigprof_handler(int, siginfo_t*, void*);
|
||||||
|
extern void (*interrupt_low_level_handlers[NSIG]) (int, siginfo_t*, os_context_t*);
|
||||||
|
|
||||||
|
/* This is called from Lisp. */
|
||||||
|
void install_handler(int signal, lispobj handler)
|
||||||
|
{
|
||||||
|
struct sigaction sa;
|
||||||
|
memset(&sa, 0, sizeof sa);
|
||||||
|
|
||||||
|
printf("HL handler %d (%s)\n", signal, strsignal(signal));
|
||||||
|
if (interrupt_low_level_handlers[signal]) {
|
||||||
|
// When there's a low-level handler, we must leave it alone.
|
||||||
|
// Give it the lisp function to call if it decides to forward a signal.
|
||||||
|
// SIG_IGN and SIG_DFL don't always do what you think in such case.
|
||||||
|
lisp_sig_handlers[signal] = functionp(handler) ? handler : 0;
|
||||||
|
} else if (signal == SIGPROF) {
|
||||||
|
if (handler) sa.sa_sigaction = sigprof_handler;
|
||||||
|
else sa.sa_handler = SIG_DFL;
|
||||||
|
// The handler is signal-safe, but because it uses component_ptr_from_pc(),
|
||||||
|
// it must block GC, lest crashes occur from dereferencing wild pointers.
|
||||||
|
// FIXME: skip collect a profiling sample if GC is in progress which can happen
|
||||||
|
// if we transited through a lisp-to-c yieldpoint
|
||||||
|
sa.sa_mask = blockable_sigset;
|
||||||
|
sa.sa_flags = SA_SIGINFO | SA_RESTART;
|
||||||
|
sigaction(signal, &sa, NULL);
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
// Our "abstract" values for SIG_DFL and SIG_IGN are 0 and 1
|
||||||
|
// respectively which are probably the real values from signal.h
|
||||||
|
// but this way way don't need to put them in grovel-headers.c
|
||||||
|
if (handler==0 || handler==1) {
|
||||||
|
sa.sa_handler = handler ? SIG_IGN : SIG_DFL;
|
||||||
|
// assign the OS level action before clearing the lisp function.
|
||||||
|
// (If a signal were to be delivered to the C trampoline when the lisp
|
||||||
|
// function is NIL, we'd get the effect of :IGNORE regardless
|
||||||
|
// of what the default action should be)
|
||||||
|
sigaction(signal, &sa, NULL);
|
||||||
|
lisp_sig_handlers[signal] = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sa.sa_flags = SA_SIGINFO;
|
||||||
|
if (signal != SIGURG) sa.sa_flags |= SA_RESTART;
|
||||||
|
if (sigismember(&deferrable_sigset, signal)) {
|
||||||
|
sa.sa_sigaction = defer_to_yieldpoint;
|
||||||
|
} else {
|
||||||
|
sa.sa_sigaction = interrupt_handle_now_handler;
|
||||||
|
sa.sa_flags |= SA_NODEFER;
|
||||||
|
}
|
||||||
|
sa.sa_mask = blockable_sigset;
|
||||||
|
// ensure the C handler sees a lisp function before doing sigaction()
|
||||||
|
lisp_sig_handlers[signal] = handler;
|
||||||
|
sigaction(signal, &sa, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -224,6 +224,9 @@
|
||||||
sb-c::*compile-file-elapsed-time*
|
sb-c::*compile-file-elapsed-time*
|
||||||
sb-c::*phash-lambda-cache*
|
sb-c::*phash-lambda-cache*
|
||||||
,(maybe "SB-IMPL" "*RUN-GC-HOOKS*")
|
,(maybe "SB-IMPL" "*RUN-GC-HOOKS*")
|
||||||
|
sb-x86-64-asm::*total-n-yieldpoints-deleted*
|
||||||
|
sb-x86-64-asm::*original-total-n-yieldpoints*
|
||||||
|
sb-thread::*anon-thread-name-generator*
|
||||||
sb-impl::**finalizer-store**
|
sb-impl::**finalizer-store**
|
||||||
sb-impl::*finalizer-rehashlist*
|
sb-impl::*finalizer-rehashlist*
|
||||||
sb-impl::*finalizers-triggered*
|
sb-impl::*finalizers-triggered*
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,9 @@
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
#ifdef LISP_FEATURE_YIELDPOINTS
|
||||||
|
#include <ucontext.h> // probably already included via signal.h
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef LISP_FEATURE_BSD
|
#ifdef LISP_FEATURE_BSD
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
|
|
@ -280,6 +283,9 @@ main(int argc, char __attribute__((unused)) *argv[])
|
||||||
|
|
||||||
printf(";;; signals\n");
|
printf(";;; signals\n");
|
||||||
defconstant("sizeof-sigset_t", sizeof (sigset_t));
|
defconstant("sizeof-sigset_t", sizeof (sigset_t));
|
||||||
|
#ifdef LISP_FEATURE_YIELDPOINTS
|
||||||
|
defconstant("sizeof-ucontext_t", sizeof (ucontext_t));
|
||||||
|
#endif
|
||||||
defconstant("sig_block", SIG_BLOCK);
|
defconstant("sig_block", SIG_BLOCK);
|
||||||
defconstant("sig_unblock", SIG_UNBLOCK);
|
defconstant("sig_unblock", SIG_UNBLOCK);
|
||||||
defconstant("sig_setmask", SIG_SETMASK);
|
defconstant("sig_setmask", SIG_SETMASK);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue