;;; -*- 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")

#+ppc32-target
(defppclapfunction %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.
  (clrlwi. imm0 arg (- 32 ppc32::nlisptagbits))
  (beqlr cr0)
  (mr imm0 arg_z)
  ;; set cr0_eq if result fits in a fixnum
  (clrrwi. imm1 imm0 (- ppc32::least-significant-bit ppc32::nfixnumtagbits))
  (box-fixnum arg_z imm0)               ; assume it did
  (beqlr+ cr0)                          ; else arg_z tagged ok, but missing bits
  (ba .SPmakeu32)         ; put all bits in bignum.
)

#+ppc64-target
(defppclapfunction %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.
  (clrldi. imm0 arg (- 64 ppc64::nlisptagbits))
  (beqlr cr0)
  (mr imm0 arg_z)
  ;; set cr0_eq if result fits in a fixnum
  (clrrdi. imm1 imm0 (- ppc64::least-significant-bit ppc64::nfixnumtagbits))
  (box-fixnum arg_z imm0)               ; assume it did
  (beqlr+ cr0)                          ; else arg_z tagged ok, but missing bits
  (ba .SPmakeu64)         ; put all bits in bignum.
)

;;; "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.



(defppclapfunction %normalize-areas ()
  (let ((address imm0)
        (temp imm2))

    ; update active pointer for tsp area.
    (ldr address target::tcr.ts-area target::rcontext)
    (str tsp target::area.active address)
    
    ;; Update active pointer for vsp area.
    (ldr address target::tcr.vs-area target::rcontext)
    (str vsp target::area.active address)
    
    ; Update active pointer for SP area
    (ldr arg_z target::tcr.cs-area target::rcontext)
    (str sp target::area.active arg_z)


    (ref-global arg_z all-areas)
    (ldr arg_z target::area.succ arg_z)

    (blr)))

(defppclapfunction %active-dynamic-area ()
  (ref-global arg_z all-areas)
  (ldr arg_z target::area.succ arg_z)
  (blr))

  
(defppclapfunction %object-in-stack-area-p ((object arg_y) (area arg_z))
  (ldr imm0 target::area.active area)
  (cmplr cr0 object imm0)
  (ldr imm1 target::area.high area)
  (cmplr cr1 object imm1)
  (li arg_z nil)
  (bltlr cr0)
  (bgelr cr1)
  (la arg_z target::t-offset arg_z)
  (blr))

(defppclapfunction %object-in-heap-area-p ((object arg_y) (area arg_z))
  (ldr imm0 target::area.low area)
  (cmplr cr0 object imm0)
  (ldr imm1 target::area.active area)
  (cmplr cr1 object imm1)
  (li arg_z nil)
  (bltlr cr0)
  (bgelr cr1)
  (la arg_z target::t-offset arg_z)
  (blr))


#+ppc32-target
(defppclapfunction walk-static-area ((a arg_y) (f arg_z))
  (let ((fun save0)
        (obj save1)
        (limit save2)
        (header imm0)
        (tag imm1)
        (subtag imm2)
        (bytes imm3)
        (elements imm0))
    (save-lisp-context)
    (:regsave limit 0)
    (vpush fun)
    (vpush obj)
    (vpush limit)
    (mr fun f)
    (lwz limit ppc32::area.active a)
    (lwz obj ppc32::area.low a)
    (b @test)
    @loop
    (lwz header 0 obj)
    (extract-fulltag tag header)
    (cmpwi cr0 tag ppc32::fulltag-immheader)
    (cmpwi cr1 tag ppc32::fulltag-nodeheader)
    (beq cr0 @misc)
    (beq cr1 @misc)
    (la arg_z ppc32::fulltag-cons obj)
    (set-nargs 1)
    (mr temp0 fun)
    (bla .SPFuncall)
    (la obj ppc32::cons.size obj)
    (b @test)
    @misc
    (la arg_z ppc32::fulltag-misc obj)
    (set-nargs 1)
    (mr temp0 fun)
    (bla .SPFuncall)
    (lwz header 0 obj)
    (extract-fulltag tag header)
    (cmpwi cr1 tag ppc32::fulltag-nodeheader)
    (clrlwi subtag header (- 32 ppc32::num-subtag-bits))
    (cmpwi cr2 subtag ppc32::max-32-bit-ivector-subtag)
    (cmpwi cr3 subtag ppc32::max-8-bit-ivector-subtag)
    (cmpwi cr4 subtag ppc32::max-16-bit-ivector-subtag)
    (cmpwi cr5 subtag ppc32::subtag-double-float-vector)
    (header-size elements header)
    (slwi bytes elements 2)
    (beq cr1 @bump)
    (ble cr2 @bump)
    (mr bytes elements)
    (ble cr3 @bump)
    (slwi bytes elements 1)
    (ble cr4 @bump)
    (slwi bytes elements 3)
    (beq cr5 @bump)
    (la elements 7 elements)
    (srwi bytes elements 3)
    @bump
    (la bytes (+ 4 7) bytes)
    (clrrwi bytes bytes 3)
    (add obj obj bytes)
    @test
    (cmplw :cr0 obj limit)
    (blt cr0 @loop)
    (vpop limit)
    (vpop obj)
    (vpop fun)
    (restore-full-lisp-context)
    (blr)))

#+ppc64-target
(defppclapfunction walk-static-area ((a arg_y) (f arg_z))
  (let ((fun save0)
        (obj save1)
        (limit save2)
        (header imm0)
        (tag imm1)
        (subtag imm2)
        (bytes imm3)
        (elements imm0))
    (save-lisp-context)
    (:regsave limit 0)
    (vpush fun)
    (vpush obj)
    (vpush limit)
    (mr fun f)
    (ld limit ppc64::area.active a)
    (ld obj ppc64::area.low a)
    (b @test)
    @loop
    (ld header 0 obj)
    (extract-lowtag tag header)
    (cmpri cr0 tag ppc64::lowtag-immheader)
    (cmpri cr1 tag ppc64::lowtag-nodeheader)
    (beq cr0 @misc)
    (beq cr1 @misc)
    (la arg_z ppc64::fulltag-cons obj)
    (set-nargs 1)
    (mr temp0 fun)
    (bla .SPFuncall)
    (la obj ppc64::cons.size obj)
    (b @test)
    @misc
    (la arg_z ppc64::fulltag-misc obj)
    (set-nargs 1)
    (mr temp0 fun)
    (bla .SPFuncall)
    (ldr header 0 obj)
    (extract-lowtag tag header)
    (extract-fulltag subtag header)
    (cmpri cr1 tag ppc64::lowtag-nodeheader)
    (extract-lowbyte tag header)
    (cmpri cr2 subtag ppc64::ivector-class-64-bit)
    (cmpri cr3 subtag ppc64::ivector-class-8-bit)
    (cmpri cr4 subtag ppc64::ivector-class-32-bit)
    (cmpri cr5 tag ppc64::subtag-bit-vector)
    (header-size elements header)
    (sldi bytes elements 3)
    (beq cr1 @bump)
    (beq cr2 @bump)
    (mr bytes elements)
    (beq cr3 @bump)
    (sldi bytes elements 2)
    (beq cr4 @bump)
    (sldi bytes elements 1)
    (bne cr5 @bump)
    (la elements 7 elements)
    (srdi bytes elements 3)
    @bump
    (la bytes (+ 8 15) bytes)
    (clrrdi bytes bytes 4)
    (add obj obj bytes)
    @test
    (cmpld :cr0 obj limit)
    (blt cr0 @loop)
    (vpop limit)
    (vpop obj)
    (vpop fun)
    (restore-full-lisp-context)
    (blr)))

;;; 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's the vague notion that we can't take an interrupt when
;;; "initptr" doesn't equal "freeptr", though what kind of hooks into a
;;; preemptive scheduler we'd need to enforce this is unclear.  We use
;;; initptr as an untagged pointer here (and set it to freeptr when we've
;;; got a tagged pointer to the current object.)
;;; 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.)

#+ppc32-target
(defppclapfunction %walk-dynamic-area ((a arg_y) (f arg_z))
  (let ((fun save0)
        (obj save1)
        (sentinel save2)
        (header imm0)
        (tag imm1)
        (subtag imm2)
        (bytes imm3)
        (elements imm4))
    (save-lisp-context)
    (:regsave sentinel 0)
    (vpush fun)
    (vpush obj)
    (vpush sentinel)
    (ref-global imm0 tenured-area)
    (cmpwi cr0 imm0 0)
    (li allocbase #xfff8)
    (la allocptr (- ppc32::fulltag-cons ppc32::cons.size) allocptr)
    (twllt allocptr allocbase)
    (mr sentinel allocptr)
    (clrrwi allocptr allocptr ppc32::ntagbits)
    (mr fun f)
    (if :ne
      (mr a imm0))    
    (lwz imm5 ppc32::area.low a)
    @loop
    (lwz header 0 imm5)
    (extract-fulltag tag header)
    (cmpwi cr0 tag ppc32::fulltag-immheader)
    (cmpwi cr1 tag ppc32::fulltag-nodeheader)
    (beq cr0 @misc)
    (beq cr1 @misc)
    (la obj ppc32::fulltag-cons imm5)
    (cmpw cr0 obj sentinel)
    (mr arg_z obj)
    (set-nargs 1)
    (mr temp0 fun)
    (beq cr0 @done)
    (bla .SPfuncall)
    (la imm5 (- ppc32::cons.size ppc32::fulltag-cons) obj)
    (b @loop)
    @misc
    (la obj ppc32::fulltag-misc imm5)
    (mr arg_z obj)
    (set-nargs 1)
    (mr temp0 fun)
    (bla .SPFuncall)
    (getvheader header obj)
    (extract-fulltag tag header)
    (cmpwi cr1 tag ppc32::fulltag-nodeheader)
    (cmpwi cr7 tag ppc32::fulltag-immheader)
    (clrlwi subtag header (- 32 ppc32::num-subtag-bits))
    (cmpwi cr2 subtag ppc32::max-32-bit-ivector-subtag)
    (cmpwi cr3 subtag ppc32::max-8-bit-ivector-subtag)
    (cmpwi cr4 subtag ppc32::max-16-bit-ivector-subtag)
    (cmpwi cr5 subtag ppc32::subtag-double-float-vector)
    (header-size elements header)
    (slwi bytes elements 2)
    (beq cr1 @bump)
    (if (:cr7 :ne)
      (twle 0 0))
    (ble cr2 @bump)
    (mr bytes elements)
    (ble cr3 @bump)
    (slwi bytes elements 1)
    (ble cr4 @bump)
    (slwi bytes elements 3)
    (beq cr5 @bump)
    (la elements 7 elements)
    (srwi bytes elements 3)
    @bump
    (la bytes (+ 4 7) bytes)
    (clrrwi bytes bytes 3)
    (subi imm5 obj ppc32::fulltag-misc)
    (add imm5 imm5 bytes)
    (cmpw cr0 imm5  sentinel)
    (blt cr0 @loop)
    (uuo_interr 0 0)
    (b @loop)
    @done
    (li arg_z nil)
    (vpop sentinel)
    (vpop obj)
    (vpop fun)
    (restore-full-lisp-context)
    (blr)))

#+ppc64-target
(defppclapfunction %walk-dynamic-area ((a arg_y) (f arg_z))
  (let ((fun save0)
        (obj save1)
        (sentinel save2)
        (header imm0)
        (tag imm1)
        (subtag imm2)
        (bytes imm3)
        (elements imm4))
    (save-lisp-context)
    (:regsave sentinel 0)
    (vpush fun)
    (vpush obj)
    (vpush sentinel)
    (ref-global imm0 tenured-area)
    (cmpdi cr0 imm0 0)
    (lwi allocbase #x8000)
    (sldi allocbase allocbase 32)
    (subi allocbase allocbase 16)
    (la allocptr (- ppc64::fulltag-cons ppc64::cons.size) allocptr)
    (tdlt allocptr allocbase)
    (mr sentinel allocptr)
    (clrrdi allocptr allocptr ppc64::ntagbits)
    (mr fun f)
    (if :ne
      (mr a imm0))    
    (ld imm5 ppc64::area.low a)
    @loop
    (ld header 0 imm5)
    (extract-lowtag tag header)
    (cmpdi cr0 tag ppc64::lowtag-immheader)
    (cmpdi cr1 tag ppc64::lowtag-nodeheader)
    (beq cr0 @misc)
    (beq cr1 @misc)
    (la obj ppc64::fulltag-cons imm5)
    (cmpd cr0 obj sentinel)
    (mr arg_z obj)
    (set-nargs 1)
    (mr temp0 fun)
    (beq cr0 @done)
    (bla .SPfuncall)
    (la imm5 (- ppc64::cons.size ppc64::fulltag-cons) obj)
    (b @loop)
    @misc
    (la obj ppc64::fulltag-misc imm5)
    (mr arg_z obj)
    (set-nargs 1)
    (mr temp0 fun)
    (bla .SPFuncall)
    (getvheader header obj)
    (extract-lowtag tag header)    
    (extract-fulltag subtag header)
    (cmpdi cr1 tag ppc64::lowtag-nodeheader)
    (extract-lowbyte tag header)
    (cmpri cr2 subtag ppc64::ivector-class-64-bit)
    (cmpri cr3 subtag ppc64::ivector-class-8-bit)
    (cmpri cr4 subtag ppc64::ivector-class-32-bit)
    (cmpri cr5 tag ppc64::subtag-bit-vector)
    (header-size elements header)
    (sldi bytes elements 3)
    (beq cr1 @bump)
    (beq cr2 @bump)
    (mr bytes elements)
    (beq cr3 @bump)
    (sldi bytes elements 2)
    (beq cr4 @bump)
    (sldi bytes elements 1)
    (bne cr5 @bump)
    (la elements 7 elements)
    (srdi bytes elements 3)
    @bump
    (la bytes (+ 8 15) bytes)
    (clrrdi bytes bytes 4)
    (subi imm5 obj ppc64::fulltag-misc)
    (add imm5 imm5 bytes)
    (b @loop)
    @done
    (li arg_z nil)
    (vpop sentinel)
    (vpop obj)
    (vpop fun)
    (restore-full-lisp-context)
    (blr)))

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



(defppclapfunction %class-of-instance ((i arg_z))
  (svref arg_z instance.class-wrapper i)
  (svref arg_z %wrapper-class arg_z)
  (blr))

(defppclapfunction class-of ((x arg_z))
  (check-nargs 1)
  (extract-fulltag imm0 x)
  (cmpri imm0 target::fulltag-misc)
  (beq @misc)
  (extract-lowbyte imm0 x)
  (b @done)
  @misc
  (extract-subtag imm0 x)
  @done
  (slri imm0 imm0 target::word-shift)
  (ldr temp1 '*class-table* nfn)
  (addi imm0 imm0 target::misc-data-offset)
  (ldr temp1 target::symbol.vcell temp1)
  (ldrx temp0 temp1 imm0) ; get entry from table
  (cmpri cr0 temp0 nil)
  (beq @bad)
  ;; functionp?
  (extract-typecode imm1 temp0)
  (cmpri imm1 target::subtag-function)
  (bne @ret)  ; not function - return entry
  ;; else jump to the fn
  (mr nfn temp0)
  (ldr temp0 target::misc-data-offset temp0)
  (SET-NARGS 1)
  (mtctr temp0)
  (bctr)
  @bad
  (ldr fname 'no-class-error nfn)
  (ba .spjmpsym)
  @ret
  (mr arg_z temp0)  ; return frob from table
  (blr))

(defppclapfunction full-gccount ()
  (ref-global arg_z tenured-area)
  (cmpri cr0 arg_z 0)
  (if :eq
    (ref-global arg_z gc-count)
    (ldr arg_z target::area.gc-count arg_z))
  (blr))


(defppclapfunction gc ()
  (check-nargs 0)
  (li imm0 arch::gc-trap-function-gc)
  (trlgei allocptr 0)
  (li arg_z (target-nil-value))
  (blr))


(defppclapfunction 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)
  (subi imm1 arg nil)
  (li imm0 arch::gc-trap-function-egc-control)
  (trlgei allocptr 0)
  (blr))



(defppclapfunction %configure-egc ((e0size arg_x)
				   (e1size arg_y)
				   (e2size arg_z))
  (check-nargs 3)
  (li imm0 arch::gc-trap-function-configure-egc)
  (trlgei allocptr 0)
  (blr))

(defppclapfunction purify ()
  (li imm0 arch::gc-trap-function-purify)
  (trlgei allocptr 0)
  (li arg_z nil)
  (blr))


(defppclapfunction impurify ()
  (li imm0 arch::gc-trap-function-impurify)
  (trlgei allocptr 0)
  (li arg_z nil)
  (blr))

(defppclapfunction 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)
  (li imm0 arch::gc-trap-function-get-lisp-heap-threshold)
  (trlgei allocptr 0)
  #+ppc32-target
  (ba .SPmakeu32)
  #+ppc64-target
  (ba .SPmakeu64))

(defppclapfunction 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)
  (mflr loc-pc)
  #+ppc32-target
  (bla .SPgetu32)
  #+ppc64-target
  (bla .SPgetu64)
  (mtlr loc-pc)
  (mr imm1 imm0)
  (li imm0 arch::gc-trap-function-set-lisp-heap-threshold)
  (trlgei allocptr 0)
  #+ppc32-target
  (ba .SPmakeu32)
  #+ppc64-target
  (ba .SPmakeu64))


(defppclapfunction 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) 
  (li imm0 arch::gc-trap-function-use-lisp-heap-threshold)
  (trlgei allocptr 0)
  (li arg_z nil)
  (blr))


(defppclapfunction freeze ()
  "Do a full GC, then consider all heap-allocated objects which survive to be non-relocatable."
  (check-nargs 0)
  (li imm0 arch::gc-trap-function-freeze)
  (trlgei allocptr 0)
  #+64-bit-target
  (ba .SPmakeu64)
  #+32-bit-target
  (ba .SPmakeu32))

(defppclapfunction flash-freeze ()
  "Like FREEZE, but don't GC first."
  (check-nargs 0)
  (li imm0 arch::gc-trap-function-flash-freeze)
  (trlgei allocptr 0)
  #+64-bit-target
  (ba .SPmakeu64)
  #+32-bit-target
  (ba .SPmakeu32))

(defun %watch (uvector)
  (declare (ignore uvector))
  (error "watching objects not supported on PPC yet"))

(defun %unwatch (watched new)
  (declare (ignore watched new))
  (error "watching objects not supported on PPC yet"))

;;; Make a list.  This can be faster than doing so by doing CONS
;;; repeatedly, since the latter strategy might triger the GC several
;;; times if N is large.
(defppclapfunction %allocate-list ((initial-element arg_y) (nconses arg_z))
  (check-nargs 2)
  (save-lisp-context)
  (uuo_interr arch::error-allocate-list rzero)
  (vpush arg_z)
  (vpush arg_y)
  (set-nargs 2)
  (ba .SPnvalret))
  
(defppclapfunction %ensure-static-conses ()
  (check-nargs 0)
  (li imm0 arch::gc-trap-function-ensure-static-conses)
  (trlgei allocptr 0)
  (li arg_z nil)
  (blr))

;;; offset is a fixnum, one of the target::kernel-import-xxx constants.
;;; Returns that kernel import, a fixnum.
(defppclapfunction %kernel-import ((offset arg_z))
  (ref-global imm0 kernel-imports)
  (unbox-fixnum imm1 arg_z)
  (ldrx arg_z imm0 imm1)
  (blr))

(defppclapfunction %get-unboxed-ptr ((macptr arg_z))
  (macptr-ptr imm0 arg_z)
  (ldr arg_z 0 imm0)
  (blr))


(defppclapfunction %revive-macptr ((p arg_z))
  (li imm0 target::subtag-macptr)
  (stb imm0 target::misc-subtag-offset p)
  (blr))

(defppclapfunction %macptr-type ((p arg_z))
  (check-nargs 1)
  (trap-unless-typecode= p target::subtag-macptr)
  (svref imm0 target::macptr.type-cell p)
  (box-fixnum arg_z imm0)
  (blr))
  
(defppclapfunction %macptr-domain ((p arg_z))
  (check-nargs 1)
  (trap-unless-typecode= p target::subtag-macptr)
  (svref imm0 target::macptr.domain-cell p)
  (box-fixnum arg_z imm0)
  (blr))

(defppclapfunction %set-macptr-type ((p arg_y) (new arg_z))
  (check-nargs 2)
  (unbox-fixnum imm1 new)
  (trap-unless-typecode= p target::subtag-macptr)
  (svset imm1 target::macptr.type-cell p)
  (blr))

(defppclapfunction %set-macptr-domain ((p arg_y) (new arg_z))
  (check-nargs 2)
  (unbox-fixnum imm1 new)
  (trap-unless-typecode= p target::subtag-macptr)
  (svset imm1 target::macptr.domain-cell p)
  (blr))

(defppclapfunction true ()
  (cmplri nargs '3)
  (li arg_z t)
  (blelr)
  (subi imm0 nargs '3)
  (add vsp vsp imm0)
  (blr))

(defppclapfunction false ()
  (cmplri nargs '3)
  (li arg_z nil)
  (blelr)
  (subi imm0 nargs '3)
  (add vsp vsp imm0)
  (blr))

(defppclapfunction constant-ref ()
  (cmplri nargs '3)
  (ldr arg_z 'constant nfn)
  (blelr)
  (subi imm0 nargs '3)
  (add vsp vsp imm0)
  (blr))

(lfun-bits #'true #.(encode-lambda-list '(&lap &rest ignore)))
(lfun-bits #'false #.(encode-lambda-list '(&lap &rest ignore)))

;;; end
