; -*- Mode: Lisp; Package: CCL; -*-
;;;
;;;   Copyright (C) 2009 Clozure Associates
;;;   Copyright (C) 1994-2001 Digitool, Inc
;;;   This file is part of Clozure CL.  
;;;
;;;   Clozure CL is licensed under the terms of the Lisp Lesser GNU Public
;;;   License , known as the LLGPL and distributed with Clozure CL as the
;;;   file "LICENSE".  The LLGPL consists of a preamble and the LGPL,
;;;   which is distributed with Clozure CL as the file "LGPL".  Where these
;;;   conflict, the preamble takes precedence.  
;;;
;;;   Clozure CL is referenced in the preamble as the "LIBRARY."
;;;
;;;   The LLGPL is also available online at
;;;   http://opensource.franz.com/preamble.html


(in-package "CCL")
#+x8664-target
(progn

(defx86lapfunction %address-of ((arg arg_z))
  ;; %address-of a fixnum is a fixnum, just for spite.
  ;; %address-of anything else is the address of that thing as an integer.
  (testb ($ x8664::fixnummask) (%b arg))
  (je @done)
  (movq (% arg) (% imm0))
  (jmp-subprim .SPmakeu64)
  @done
  (single-value-return))

;;; "areas" are fixnum-tagged and, for the most part, so are their
;;; contents.

;;; The nilreg-relative global all-areas is a doubly-linked-list header
;;; that describes nothing.  Its successor describes the current/active
;;; dynamic heap.  Return a fixnum which "points to" that area, after
;;; ensuring that the "active" pointers associated with the current thread's
;;; stacks are correct.



(defx86lapfunction %normalize-areas ()
  (let ((address temp0)
        (temp temp1))

    ; update active pointer for tsp area.
    (movq (:rcontext x8664::tcr.ts-area) (% address))
    (movq (:rcontext x8664::tcr.save-tsp) (% temp))
    (movq (% temp) (@ x8664::area.active (% address)))
    
    ;; Update active pointer for vsp area.
    (movq (:rcontext x8664::tcr.vs-area) (% address))
    (movq (% rsp) (@ x8664::area.active (% address)))

    (ref-global all-areas arg_z)
    (movq (@ x8664::area.succ (% arg_z)) (% arg_z))

    (single-value-return)))

(defx86lapfunction %active-dynamic-area ()
  (ref-global all-areas arg_z)
  (movq (@ x8664::area.succ (% arg_z)) (% arg_z))
  (single-value-return))

  
(defx86lapfunction %object-in-stack-area-p ((object arg_y) (area arg_z))
  (movq (@ x8664::area.active (% area)) (% imm0))
  (movq (@ x8664::area.high (% area)) (% imm1))
  (rcmp (% object) (% imm0))
  (movq ($ nil) (% arg_z))
  (movq ($ t) (% imm0))
  (jb @done)
  (rcmp (% object) (% imm1))
  (cmovbq (% imm0) (% arg_z))
  @done
  (single-value-return))

(defx86lapfunction %object-in-heap-area-p ((object arg_y) (area arg_z))
  (rcmp (% object) (@ x8664::area.low (% area)))
  (setae (%b imm0))
  (rcmp (% object) (@ x8664::area.low (% area)))
  (setb (%b imm1))
  (andb (% imm1.b) (% imm0.b))
  (andl ($ x8664::t-offset) (%l imm0))
  (lea (@ (target-nil-value) (% imm0)) (% arg_z))
  (single-value-return))




(defx86lapfunction walk-static-area ((a arg_y) (f arg_z))
  (let ((fun save0)
        (obj save1)
        (limit save2))
    (save-simple-frame)
    (push (% fun))
    (push (% obj))
    (push (% limit))
    (movq (% f) (% fun))
    (movq (@ x8664::area.active (% a)) (% limit))
    (movq (@ x8664::area.low (% a)) (% obj))
    (jmp @test)
    @loop
    (movb (@ (% obj)) (% imm0.b))
    (andb ($ x8664::fulltagmask) (% imm0.b))
    (cmpb ($ x8664::fulltag-nodeheader-0) (% imm0.b))
    (je @misc)
    (cmpb ($ x8664::fulltag-nodeheader-1) (% imm0.b))
    (je @misc)
    (cmpb ($ x8664::fulltag-immheader-0) (% imm0.b))
    (je @misc)
    (cmpb ($ x8664::fulltag-immheader-2) (% imm0.b))
    (je @misc)
    (cmpb ($ x8664::fulltag-immheader-1) (% imm0.b))
    (jne @cons)
    @misc
    (lea (@ x8664::fulltag-misc (% obj)) (% obj))
    (movq (% obj) (% arg_z))
    (set-nargs 1)
    (:talign 4)
    (call (% fun))
    (recover-fn-from-rip)
    (getvheader obj imm1)
    (movb (% imm1.b) (% imm0.b))
    (andb ($ x8664::fulltagmask) (% imm0.b))
    (cmpb ($ x8664::fulltag-nodeheader-0) (% imm0.b))
    (je @64)
    (cmpb ($ x8664::fulltag-nodeheader-1) (% imm0.b))
    (je @64)
    (cmpb ($ x8664::ivector-class-64-bit) (% imm0.b))
    (jne @not64)
    @64
    (shrq ($ x8664::num-subtag-bits) (% imm1))
    (shlq ($ x8664::word-shift) (% imm1))
    (jmp @uvector-next)
    @not64
    (cmpb ($ x8664::ivector-class-32-bit) (% imm0.b))
    (jne @not32)
    (shrq ($ x8664::num-subtag-bits) (% imm1))
    (shlq ($ 2) (% imm1))
    (jmp @uvector-next)
    @not32
    (cmpb ($ (- x8664::subtag-bit-vector 256)) (% imm1.b))
    (jne @not-bit)
    (shrq ($ x8664::num-subtag-bits) (% imm1))
    (addq ($ 7) (% imm1))
    (shrq ($ 3) (% imm1))
    (jmp @uvector-next)
    @not-bit
    (cmpb ($ x8664::subtag-complex-double-float-vector) (% imm1.b))
    (jne @not-complex-double-float-vector)
    (shrq ($ x8664::num-subtag-bits) (% imm1))
    (shlq ($ x8664::fulltag-function) (% imm1))
    (jmp @uvector-next)
    @not-complex-double-float-vector
    (rcmpb (% imm1.b) ($ (- x8664::min-8-bit-ivector-subtag 256)))
    (jb @16)
    (shrq ($ x8664::num-subtag-bits) (% imm1))
    (jmp @uvector-next)
    @16
    (shrq ($ x8664::num-subtag-bits) (% imm1))
    (shlq ($ 1) (% imm1))
    (jmp @uvector-next)
    @cons
    (addq ($ x8664::fulltag-cons) (% obj))
    (movq (% obj) (% arg_z))
    (set-nargs 1)
    (:talign 4)
    (call (% fun))
    (recover-fn-from-rip)
    (addq ($ (- x8664::cons.size x8664::fulltag-cons)) (% obj))
    (jmp @test)
    ;; size of OBJ in bytes (without header or alignment padding)
    ;; in imm1.
    @uvector-next
    (addq ($ (+ x8664::node-size (1- x8664::dnode-size))) (% imm1))
    (andb ($ (lognot (1- x8664::dnode-size))) (% imm1.b))
    (lea (@ (- x8664::fulltag-misc) (% obj) (% imm1)) (% obj))
    @test
    (cmpq (% limit) (% obj))
    (jb @loop)
    (pop (% limit))
    (pop (% obj))
    (pop (% fun))
    (movl ($ (target-nil-value)) (% arg_z.l))
    (restore-simple-frame)
    (single-value-return)))



;;; This walks the active "dynamic" area.  Objects might be moving around
;;; while we're doing this, so we have to be a lot more careful than we 
;;; are when walking a static area.
;;; There are a couple of approaches to termination:
;;;  a) Allocate a "sentinel" cons, and terminate when we run into it.
;;;  b) Check the area limit (which is changing if we're consing) and
;;;     terminate when we hit it.
;;; (b) loses if the function conses.  (a) conses.  I can't think of anything
;;; better than (a).
;;; This, of course, assumes that any GC we're doing does in-place compaction
;;; (or at least preserves the relative order of objects in the heap.)

(defx86lapfunction %walk-dynamic-area ((a arg_y) (f arg_z))
  (let ((fun save0)
        (obj save1)
        (limit save2))
    (save-simple-frame)
    (push (% fun))
    (push (% obj))
    (push (% limit))
    (movq (% f) (% fun))
    (ref-global tenured-area a)
    (movq (@ x8664::area.low (% a)) (% obj))
    (subq ($ (- x8664::cons.size x8664::fulltag-cons))
          (:rcontext x8664::tcr.save-allocptr))
    (movq (:rcontext x8664::tcr.save-allocptr) (% allocptr))
    (cmpq (:rcontext x8664::tcr.save-allocbase) (% allocptr))
    (ja @ok)
    (uuo-alloc)
    @ok
    (andb ($ (lognot x8664::fulltagmask))
          (:rcontext x8664::tcr.save-allocptr))
    (movq (% allocptr) (% limit))
    (jmp @test)
    @loop
    (movb (@ (% obj)) (% imm0.b))
    (andb ($ x8664::fulltagmask) (% imm0.b))
    (cmpb ($ x8664::fulltag-nodeheader-0) (% imm0.b))
    (je @misc)
    (cmpb ($ x8664::fulltag-nodeheader-1) (% imm0.b))
    (je @misc)
    (cmpb ($ x8664::fulltag-immheader-0) (% imm0.b))
    (je @misc)
    (cmpb ($ x8664::fulltag-immheader-2) (% imm0.b))
    (je @misc)
    (cmpb ($ x8664::fulltag-immheader-1) (% imm0.b))
    (jne @cons)
    @misc
    (lea (@ x8664::fulltag-misc (% obj)) (% obj))
    (movq (% obj) (% arg_z))
    (set-nargs 1)
    (:talign 4)
    (call (% fun))
    (recover-fn-from-rip)
    (getvheader obj imm1)
    (movb (% imm1.b) (% imm0.b))
    (andb ($ x8664::fulltagmask) (% imm0.b))
    (cmpb ($ x8664::fulltag-nodeheader-0) (% imm0.b))
    (je @64)
    (cmpb ($ x8664::fulltag-nodeheader-1) (% imm0.b))
    (je @64)
    (cmpb ($ x8664::ivector-class-64-bit) (% imm0.b))
    (jne @not64)
    @64
    (shrq ($ x8664::num-subtag-bits) (% imm1))
    (shlq ($ x8664::word-shift) (% imm1))
    (jmp @uvector-next)
    @not64
    (cmpb ($ x8664::ivector-class-32-bit) (% imm0.b))
    (jne @not32)
    (shrq ($ x8664::num-subtag-bits) (% imm1))
    (shlq ($ 2) (% imm1))
    (jmp @uvector-next)
    @not32
    (cmpb ($ (- x8664::subtag-bit-vector 256)) (% imm1.b))
    (jne @not-bit)
    (shrq ($ x8664::num-subtag-bits) (% imm1))
    (addq ($ 7) (% imm1))
    (shrq ($ 3) (% imm1))
    (jmp @uvector-next)
    @not-bit
    (cmpb ($ x8664::subtag-complex-double-float-vector) (% imm1.b))
    (jne @not-complex-double-float-vector)
    (shrq ($ x8664::num-subtag-bits) (% imm1))
    (shlq ($ 4) (% imm1))
    (jmp @uvector-next)
    @not-complex-double-float-vector
    (rcmpb (% imm1.b) ($ (- x8664::min-8-bit-ivector-subtag 256)))
    (jb @16)
    (shrq ($ x8664::num-subtag-bits) (% imm1))
    (jmp @uvector-next)
    @16
    (shrq ($ x8664::num-subtag-bits) (% imm1))
    (shlq ($ 1) (% imm1))
    (jmp @uvector-next)
    @cons
    (addq ($ x8664::fulltag-cons) (% obj))
    (cmpq (% obj) (% limit))
    (movq (% obj) (% arg_z))
    (je @done)
    (set-nargs 1)
    (:talign 4)
    (call (% fun))
    (recover-fn-from-rip)
    (addq ($ (- x8664::cons.size x8664::fulltag-cons)) (% obj))
    (jmp @test)
    ;; size of OBJ in bytes (without header or alignment padding)
    ;; in imm1.
    @uvector-next
    (addq ($ (+ x8664::node-size (1- x8664::dnode-size))) (% imm1))
    (andb ($ (lognot (1- x8664::dnode-size))) (% imm1.b))
    (lea (@ (- x8664::fulltag-misc) (% obj) (% imm1)) (% obj))
    @test
    (cmpq (% limit) (% obj))
    (jb @loop)
    @done
    (pop (% limit))
    (pop (% obj))
    (pop (% fun))
    (movl ($ (target-nil-value)) (% arg_z.l))
    (restore-simple-frame)
    (single-value-return)))

(defun walk-dynamic-area (area func)
  (with-other-threads-suspended
      (%walk-dynamic-area area func)))



(defx86lapfunction %class-of-instance ((i arg_z))
  (svref i instance.class-wrapper arg_z)
  (svref arg_z %wrapper-class arg_z)
  (single-value-return))

(defx86lapfunction class-of ((x arg_z))
  (check-nargs 1)
  (movw ($ (logior (ash 1 x8664::tag-list)
                   (ash 1 x8664::tag-imm-1)))
        (%w imm1))
  (extract-lisptag x imm0)
  (btw (% imm0.w) (% imm1.w))
  (cmovbl (% arg_z.l) (% imm0.l))
  (movq (@ '*class-table* (% fn)) (% temp1))
  (cmpb ($ x8664::tag-misc) (% imm0.b))
  (jne @have-tag)
  (extract-subtag x imm0)
  @have-tag
  (movq (@ x8664::symbol.vcell (% temp1)) (% temp1))
  (movzbl (% imm0.b) (% imm0.l))
  (movq (@ x8664::misc-data-offset (% temp1) (% imm0) 8) (% temp0))
  (cmpb ($ x8664::fulltag-nil) (%b temp0))
  (je @bad)
  (extract-fulltag temp0 imm0)
  (cmpb ($ x8664::fulltag-function) (%b imm0))
  (jne @ret)
  (set-nargs 1)
  (jmp (% temp0))
  @bad
  (load-constant no-class-error fname)
  (set-nargs 1)
  (jmp  (@ x8664::symbol.fcell (% fname)))
  @ret
  (movq (% temp0) (% arg_z))  ; return frob from table
  (single-value-return))

(defx86lapfunction full-gccount ()
  (ref-global tenured-area arg_z)
  (testq (% arg_z) (% arg_z))
  (cmoveq (@ (+ (target-nil-value) (x8664::%kernel-global 'gc-count))) (% arg_z))
  (cmovneq (@ x8664::area.gc-count (% arg_z)) (% arg_z))
  (single-value-return))


(defx86lapfunction gc ()
  (check-nargs 0)
  (movq ($ arch::gc-trap-function-gc) (% imm0))
  (uuo-gc-trap)
  (movq ($ nil) (% arg_z))
  (single-value-return))


(defx86lapfunction egc ((arg arg_z))
  "Enable the EGC if arg is non-nil, disables the EGC otherwise. Return
the previous enabled status. Although this function is thread-safe (in
the sense that calls to it are serialized), it doesn't make a whole lot
of sense to be turning the EGC on and off from multiple threads ..."
  (check-nargs 1)
  (clrq imm1)
  (cmp-reg-to-nil arg)
  (setne (% imm1.b))
  (movq ($ arch::gc-trap-function-egc-control) (% imm0))
  (uuo-gc-trap)
  (single-value-return))




(defx86lapfunction %configure-egc ((e0size arg_x)
				   (e1size arg_y)
				   (e2size arg_z))
  (check-nargs 3)
  (movq ($ arch::gc-trap-function-configure-egc) (% imm0))
  (uuo-gc-trap)
  (single-value-return))

(defx86lapfunction purify ()
  (check-nargs 0)
  (movq ($ arch::gc-trap-function-purify) (% imm0))
  (uuo-gc-trap)
  (movq ($ nil) (% arg_z))
  (single-value-return))


(defx86lapfunction impurify ()
  (check-nargs 0)
  (movq ($ arch::gc-trap-function-impurify) (% imm0))
  (uuo-gc-trap)
  (movq ($ nil) (% arg_z))
  (single-value-return))


(defx86lapfunction lisp-heap-gc-threshold ()
  "Return the value of the kernel variable that specifies the amount
of free space to leave in the heap after full GC."
  (check-nargs 0)
  (movq ($ arch::gc-trap-function-get-lisp-heap-threshold) (% imm0))
  (uuo-gc-trap)
  #+x8632-target
  (jmp-subprim .SPmakeu32)
  #+x8664-target
  (jmp-subprim .SPmakeu64))

(defx86lapfunction set-lisp-heap-gc-threshold ((new arg_z))
  "Set the value of the kernel variable that specifies the amount of free
space to leave in the heap after full GC to new-value, which should be a
non-negative fixnum. Returns the value of that kernel variable (which may
be somewhat larger than what was specified)."
  (check-nargs 1)
  (save-simple-frame)
  (call-subprim .SPgetu64)
  (movq (% imm0) (% imm1))
  (movq ($ arch::gc-trap-function-set-lisp-heap-threshold) (% imm0))
  (uuo-gc-trap)
  (restore-simple-frame)
  (jmp-subprim .SPmakeu64))


(defx86lapfunction use-lisp-heap-gc-threshold ()
  "Try to grow or shrink lisp's heap space, so that the free space is (approximately) equal to the current heap threshold. Return NIL"
  (check-nargs 0) 
  (movq ($ arch::gc-trap-function-use-lisp-heap-threshold) (% imm0))
  (uuo-gc-trap)
  (movl ($ (target-nil-value)) (%l arg_z))
  (single-value-return))


(defx86lapfunction %watch ((thing arg_z))
  (check-nargs 1)
  (movl ($ arch::watch-trap-function-watch) (%l imm0))
  (uuo-watch-trap)
  (single-value-return))

(defx86lapfunction %unwatch ((watched arg_y) (new arg_z))
  (check-nargs 2)
  (movl ($ arch::watch-trap-function-unwatch) (%l imm0))
  (uuo-watch-trap)
  (single-value-return))

(defx86lapfunction %allocate-list ((initial-element arg_y) (nconses arg_z))
  (check-nargs 2)
  (save-simple-frame)
  (ud2a)
  (:byte 10)
  (push (% arg_z))
  (push (% allocptr))
  (set-nargs 2)
  (jmp-subprim .SPnvalret))


(defx86lapfunction %ensure-static-conses ()
  (check-nargs 0)
  (movl ($ arch::gc-trap-function-ensure-static-conses) (% imm0.l))
  (uuo-gc-trap)
  (movl ($ (target-nil-value)) (% arg_z.l))
  (single-value-return))

(defx86lapfunction set-gc-notification-threshold ((threshold arg_z))
  "Set the value of the kernel variable that can be used to trigger
GC notifications."
  (check-nargs 1)
  (save-simple-frame)
  (call-subprim .SPgetu64)
  (movq (% imm0) (% imm1))
  (movq ($ arch::gc-trap-function-set-gc-notification-threshold) (% imm0))
  (uuo-gc-trap)
  (restore-simple-frame)
  (jmp-subprim .SPmakeu64))

(defx86lapfunction get-gc-notification-threshold ()
  "Get the value of the kernel variable that can be used to trigger
GC notifications."
  (check-nargs 0)
  (movq ($ arch::gc-trap-function-set-gc-notification-threshold) (% imm0))
  (uuo-gc-trap)
  (jmp-subprim .SPmakeu64))
  

(defx86lapfunction allow-heap-allocation ((arg_arg_z))
  "If ARG is true, signal an ALLOCATION-DISABLED condition on attempts
at heap allocation."
  (check-nargs 1)
  (cmpq ($ (target-nil-value)) (%q arg_z))
  (setne (%b imm1))
  (andl ($ 1) (%l imm1))
  (movl ($ arch::gc-trap-function-allocation-control) (%l imm0))
  (uuo-gc-trap)
  (single-value-return))

(defx86lapfunction heap-allocation-allowed-p ()
  (check-nargs 0)
  (movl ($ arch::gc-trap-function-allocation-control) (%l imm0))
  (movl ($ 2) (%l imm1))                ;query
  (uuo-gc-trap)
  (single-value-return))

;;; offset is a fixnum, one of the x8664::kernel-import-xxx constants.
;;; Returns that kernel import, a fixnum.
(defx86lapfunction %kernel-import ((offset arg_z))
  (ref-global kernel-imports imm0)
  (unbox-fixnum arg_z imm1)
  (movq (@ (% imm0) (% imm1)) (% imm0))
  (box-fixnum imm0 arg_z)
  (single-value-return))

(defx86lapfunction %get-unboxed-ptr ((macptr arg_z))
  (macptr-ptr arg_z imm0)
  (movq (@ (% imm0)) (% arg_z))
  (single-value-return))


(defx86lapfunction %revive-macptr ((p arg_z))
  (movb ($ x8664::subtag-macptr) (@ x8664::misc-subtag-offset (% p)))
  (single-value-return))

(defx86lapfunction %macptr-type ((p arg_z))
  (check-nargs 1)
  (trap-unless-typecode= p x8664::subtag-macptr)
  (svref p x8664::macptr.type-cell imm0)
  (box-fixnum imm0 arg_z)
  (single-value-return))
  
(defx86lapfunction %macptr-domain ((p arg_z))
  (check-nargs 1)
  (trap-unless-typecode= p x8664::subtag-macptr)
  (svref p x8664::macptr.domain-cell imm0)
  (box-fixnum imm0 arg_z)
  (single-value-return))

(defx86lapfunction %set-macptr-type ((p arg_y) (new arg_z))
  (check-nargs 2)
  (unbox-fixnum new imm1)
  (trap-unless-typecode= p x8664::subtag-macptr)
  (svset p x8664::macptr.type-cell imm1)
  (single-value-return))

(defx86lapfunction %set-macptr-domain ((p arg_y) (new arg_z))
  (check-nargs 2)
  (unbox-fixnum new imm1)
  (trap-unless-typecode= p x8664::subtag-macptr)
  (svset p x8664::macptr.domain-cell imm1)
  (single-value-return))

(defx86lapfunction true ()
  (pop (% ra0))
  (subq ($ '3) (% nargs.q))
  (leaq (@ '2 (% rsp) (% nargs.q)) (% imm0))
  (cmovaq (% imm0) (% rsp))
  (movl ($ (target-t-value)) (%l arg_z))
  (push (% ra0))
  (single-value-return))

(defx86lapfunction false ()
  (pop (% ra0))
  (subq ($ '3) (% nargs.q))
  (leaq (@ '2 (% rsp) (% nargs.q)) (% imm0))
  (cmovaq (% imm0) (% rsp))
  (movl ($ (target-nil-value)) (%l arg_z))
  (push (% ra0))
  (single-value-return))

(defx86lapfunction constant-ref ()
  (pop (% ra0))
  (subq ($ '3) (% nargs.q))
  (leaq (@ '2 (% rsp) (% nargs.q)) (% imm0))
  (cmovaq (% imm0) (% rsp))
  (movq (@ 'constant (% fn)) (% arg_z))
  (push (% ra0))
  (single-value-return))  


;;; end
) ; #+x8664-target
