mirror of
https://github.com/open-goal/jak-project.git
synced 2024-11-24 06:39:51 +00:00
Implement gkernel
: Part 2 (#155)
* update * small fixes * deactivate * simple kernel test
This commit is contained in:
parent
d86964985a
commit
e05f3ceefc
@ -190,6 +190,7 @@
|
||||
(declare-type stack-frame basic)
|
||||
(declare-type cpu-thread basic)
|
||||
(declare-type state basic)
|
||||
(declare-type dead-pool basic)
|
||||
|
||||
;; gkernel-h
|
||||
(deftype thread (basic)
|
||||
@ -246,8 +247,8 @@
|
||||
|
||||
(:methods
|
||||
(new ((allocation symbol) (type-to-make type) (name basic)) _type_ 0)
|
||||
(activate ((obj _type_) (dest process-tree) (name basic) (stack-top pointer)) basic 9)
|
||||
(deactivate ((obj _type_)) basic 10)
|
||||
(activate ((obj _type_) (dest process-tree) (name basic) (stack-top pointer)) process-tree 9)
|
||||
(deactivate ((obj _type_)) none 10)
|
||||
(dummy-method-11 () none 11)
|
||||
(run-logic? ((obj _type_)) symbol 12)
|
||||
(dummy-method () none 13)
|
||||
@ -260,7 +261,7 @@
|
||||
|
||||
;; gkernel
|
||||
(deftype process (process-tree)
|
||||
((pool basic :offset-assert #x20)
|
||||
((pool dead-pool :offset-assert #x20)
|
||||
(status basic :offset-assert #x24)
|
||||
(pid int32 :offset-assert #x28)
|
||||
(main-thread cpu-thread :offset-assert #x2c)
|
||||
@ -280,10 +281,11 @@
|
||||
(stack uint8 :dynamic :offset-assert #x70)
|
||||
)
|
||||
|
||||
|
||||
(:methods
|
||||
(new ((allocation symbol) (type-to-make type) (name basic) (stack-size int)) _type_ 0)
|
||||
(activate ((obj process) (dest process-tree) (name basic) (stack-top pointer)) basic 9)
|
||||
(deactivate ((obj process)) basic 10)
|
||||
(activate ((obj _type_) (dest process-tree) (name basic) (stack-top pointer)) process-tree 9)
|
||||
(deactivate ((obj process)) none 10)
|
||||
(dummy-method-11 () none 11)
|
||||
(run-logic? ((obj process)) symbol 12)
|
||||
(dummy-method () none 13)
|
||||
@ -384,8 +386,12 @@
|
||||
)
|
||||
|
||||
(deftype protect-frame (stack-frame)
|
||||
((exit function :offset-assert 12)
|
||||
((exit (function object) :offset-assert 12)
|
||||
)
|
||||
(:methods
|
||||
(new ((allocation symbol) (type-to-make type) (func (function object))) protect-frame)
|
||||
)
|
||||
|
||||
:method-count-assert 9
|
||||
:size-assert #x10
|
||||
:flag-assert #x900000010
|
||||
@ -483,73 +489,55 @@
|
||||
(define-extern kernel-dispatcher (function (function object)))
|
||||
(define-extern inspect-process-tree (function process-tree int int symbol process-tree))
|
||||
|
||||
(define-extern throw-dispatch (function catch-frame object none))
|
||||
(define-extern run-function-in-process (function process function object object object object object object object))
|
||||
(define-extern throw (function symbol object int))
|
||||
(define-extern set-to-run (function cpu-thread function object object object object object object pointer))
|
||||
(define-extern set-to-run-bootstrap (function none))
|
||||
(define-extern entity-deactivate-handler (function process object none))
|
||||
(define-extern process-disconnect (function object none))
|
||||
|
||||
;;(define-extern stack-frame object) ;; unknown type
|
||||
(define-extern stack-frame type)
|
||||
(define-extern state type)
|
||||
;;(define-extern dead-pool-heap-rec object) ;; unknown type
|
||||
;;(define-extern dead-pool object) ;; unknown type
|
||||
;;(define-extern catch-frame object) ;; unknown type
|
||||
;;(define-extern thread object) ;; unknown type
|
||||
;;(define-extern handle object) ;; unknown type
|
||||
;;(define-extern cpu-thread object) ;; unknown type
|
||||
;;(define-extern dead-pool-heap object) ;; unknown type
|
||||
(define-extern dead-pool-heap-rec type)
|
||||
(define-extern dead-pool type)
|
||||
(define-extern catch-frame type)
|
||||
(define-extern thread type)
|
||||
(define-extern handle type)
|
||||
(define-extern cpu-thread type)
|
||||
(define-extern dead-pool-heap type)
|
||||
(define-extern kernel-context type)
|
||||
;;(define-extern protect-frame object) ;; unknown type
|
||||
;;(define-extern event-message-block object) ;; unknown type
|
||||
;;(define-extern process-tree object) ;; unknown type
|
||||
;;(define-extern *listener-process* object) ;; unknown type
|
||||
;;(define-extern *entity-pool* object) ;; unknown type
|
||||
;;(define-extern *default-pool* object) ;; unknown type
|
||||
;;(define-extern malloc object) ;; unknown type
|
||||
;;(define-extern ready object) ;; unknown type
|
||||
;;(define-extern *camera-pool* object) ;; unknown type
|
||||
(define-extern throw-dispatch function)
|
||||
;;(define-extern game object) ;; unknown type
|
||||
(define-extern run-function-in-process function)
|
||||
(define-extern throw function)
|
||||
;;(define-extern *nk-dead-pool* object) ;; unknown type
|
||||
(define-extern protect-frame type)
|
||||
(define-extern event-message-block type)
|
||||
(define-extern process-tree type)
|
||||
(define-extern *listener-process* process)
|
||||
(define-extern *entity-pool* process-tree)
|
||||
(define-extern *default-pool* process-tree)
|
||||
(define-extern malloc (function symbol int pointer)) ;; from kernel-defs.gc
|
||||
(define-extern *camera-pool* process-tree)
|
||||
|
||||
(define-extern *nk-dead-pool* dead-pool-heap)
|
||||
(define-extern change-to-last-brother function)
|
||||
;;(define-extern *pickup-dead-pool* object) ;; unknown type
|
||||
;;(define-extern *camera-master-dead-pool* object) ;; unknown type
|
||||
(define-extern set-to-run function)
|
||||
;;(define-extern default-pool object) ;; unknown type
|
||||
;;(define-extern listener object) ;; unknown type
|
||||
;;(define-extern target-pool object) ;; unknown type
|
||||
(define-extern set-to-run-bootstrap function)
|
||||
;;(define-extern *camera-dead-pool* object) ;; unknown type
|
||||
;;(define-extern process object) ;; unknown type
|
||||
(define-extern previous-brother function)
|
||||
;;(define-extern dead-state object) ;; unknown type
|
||||
;;(define-extern debug object) ;; unknown type
|
||||
;;(define-extern *16k-dead-pool* object) ;; unknown type
|
||||
(define-extern entity-deactivate-handler function)
|
||||
;;(define-extern *target-pool* object) ;; unknown type
|
||||
;;(define-extern entity-pool object) ;; unknown type
|
||||
;;(define-extern main object) ;; unknown type
|
||||
(define-extern change-brother function)
|
||||
;;(define-extern *active-pool* object) ;; unknown type
|
||||
;;(define-extern display-pool object) ;; unknown type
|
||||
;;(define-extern *target-dead-pool* object) ;; unknown type
|
||||
;;(define-extern *4k-dead-pool* object) ;; unknown type
|
||||
;;(define-extern *default-dead-pool* object) ;; unknown type
|
||||
;;(define-extern *8k-dead-pool* object) ;; unknown type
|
||||
;;(define-extern *display-pool* object) ;; unknown type
|
||||
;;(define-extern active-pool object) ;; unknown type
|
||||
;;(define-extern *dead-pool-list* object) ;; unknown type
|
||||
;;(define-extern camera-pool object) ;; unknown type
|
||||
;;(define-extern suspended object) ;; unknown type
|
||||
;;(define-extern dgo-load object) ;; unknown type
|
||||
;;(define-extern initialize-dead object) ;; unknown type
|
||||
;;(define-extern #f object) ;; unknown type
|
||||
;;(define-extern waiting-to-run object) ;; unknown type
|
||||
(define-extern *pickup-dead-pool* dead-pool)
|
||||
(define-extern *camera-master-dead-pool* dead-pool)
|
||||
(define-extern *camera-dead-pool* dead-pool)
|
||||
(define-extern process type)
|
||||
(define-extern dead-state state)
|
||||
(define-extern *16k-dead-pool* dead-pool)
|
||||
(define-extern *target-pool* process-tree)
|
||||
(define-extern *active-pool* process-tree)
|
||||
(define-extern *target-dead-pool* dead-pool)
|
||||
(define-extern *4k-dead-pool* dead-pool)
|
||||
(define-extern *default-dead-pool* dead-pool)
|
||||
(define-extern *8k-dead-pool* dead-pool)
|
||||
(define-extern *display-pool* process-tree)
|
||||
|
||||
(define-extern #f symbol)
|
||||
;;(define-extern *stdcon0* object) ;; unknown type
|
||||
;;(define-extern initialize-go object) ;; unknown type
|
||||
;;(define-extern trans object) ;; unknown type
|
||||
;;(define-extern code object) ;; unknown type
|
||||
;;(define-extern *listener-function* object) ;; unknown type
|
||||
;;(define-extern *stdcon1* object) ;; unknown type
|
||||
;;(define-extern initialize object) ;; unknown type
|
||||
(define-extern process-disconnect function)
|
||||
|
||||
;;(define-extern *debug-draw-pauseable* object) ;; unknown type
|
||||
;;(define-extern _empty_ object) ;; unknown type
|
||||
;;(define-extern running object) ;; unknown type
|
||||
@ -557,6 +545,11 @@
|
||||
;;(define-extern post object) ;; unknown type
|
||||
;;(define-extern dead object) ;; unknown type
|
||||
|
||||
;; todo
|
||||
(define-extern previous-brother function)
|
||||
(define-extern change-brother function)
|
||||
;;(define-extern *dead-pool-list* object) ;; unknown type
|
||||
|
||||
;; pskernel
|
||||
(deftype lowmemmap (structure)
|
||||
((irq-info-stack uint32 :offset-assert 0)
|
||||
|
@ -79,4 +79,5 @@
|
||||
- Fixed a bug where early returns out of methods would not change the return type of the method.
|
||||
- Fixed a bug where the return instruction was still emitted and the overridden return type of `asm-func` was ignored for methods
|
||||
- Rearranged function stack frames so spilled register variable slots come after stack structures.
|
||||
- Added `stack` allocated and constructed basic/structure types.
|
||||
- Added `stack` allocated and constructed basic/structure types.
|
||||
- Fixed a bug where functions with exactly 8 parameters created a compiler error.
|
@ -1,13 +1,16 @@
|
||||
(defglobalconstant all-kernel-goal-files
|
||||
("goal_src/kernel/gcommon.gc"
|
||||
"goal_src/kernel/gstring-h.gc"
|
||||
"goal_src/kernel/gkernel-h.gc"
|
||||
"goal_src/kernel/gkernel.gc"
|
||||
"goal_src/kernel/pskernel.gc"
|
||||
"goal_src/kernel/gstring.gc"
|
||||
"goal_src/kernel/dgo-h.gc"
|
||||
"goal_src/kernel/gstate.gc")
|
||||
)
|
||||
|
||||
(defglobalconstant all-goal-files
|
||||
(
|
||||
"goal_src/kernel/gcommon.gc"
|
||||
"goal_src/kernel/gstring-h.gc"
|
||||
"goal_src/kernel/gkernel-h.gc"
|
||||
"goal_src/kernel/gkernel.gc"
|
||||
"goal_src/kernel/pskernel.gc"
|
||||
"goal_src/kernel/gstring.gc"
|
||||
"goal_src/kernel/dgo-h.gc"
|
||||
"goal_src/kernel/gstate.gc"
|
||||
"goal_src/engine/util/types-h.gc"
|
||||
"goal_src/engine/ps2/vu1-macros.gc"
|
||||
"goal_src/engine/math/math.gc"
|
||||
|
@ -1002,17 +1002,6 @@
|
||||
("launcherdoor.o" "launcherdoor")
|
||||
)
|
||||
|
||||
("KERNEL.CGO"
|
||||
("gcommon.o" "gcommon")
|
||||
("gstring-h.o" "gstring-h")
|
||||
("gkernel-h.o" "gkernel-h")
|
||||
("gkernel.o" "gkernel")
|
||||
("pskernel.o" "pskernel")
|
||||
("gstring.o" "gstring")
|
||||
("dgo-h.o" "dgo-h")
|
||||
("gstate.o" "gstate")
|
||||
)
|
||||
|
||||
("L1.CGO"
|
||||
("rigid-body-h.o" "rigid-body-h")
|
||||
("water-anim.o" "water-anim")
|
10
goal_src/build/kernel_dgos.txt
Normal file
10
goal_src/build/kernel_dgos.txt
Normal file
@ -0,0 +1,10 @@
|
||||
("KERNEL.CGO"
|
||||
("gcommon.o" "gcommon")
|
||||
("gstring-h.o" "gstring-h")
|
||||
("gkernel-h.o" "gkernel-h")
|
||||
("gkernel.o" "gkernel")
|
||||
("pskernel.o" "pskernel")
|
||||
("gstring.o" "gstring")
|
||||
("dgo-h.o" "dgo-h")
|
||||
("gstate.o" "gstate")
|
||||
)
|
@ -32,10 +32,19 @@
|
||||
`(asm-file ,file :color :write)
|
||||
)
|
||||
|
||||
|
||||
(defmacro build-kernel ()
|
||||
`(begin
|
||||
,@(apply make-build-command all-kernel-goal-files)
|
||||
(build-dgos "goal_src/build/kernel_dgos.txt")
|
||||
)
|
||||
)
|
||||
|
||||
(defmacro build-game ()
|
||||
`(begin
|
||||
(build-kernel)
|
||||
,@(apply make-build-command all-goal-files)
|
||||
(build-dgos "goal_src/build/dgos.txt")
|
||||
(build-dgos "goal_src/build/game_dgos.txt")
|
||||
)
|
||||
)
|
||||
|
||||
@ -62,7 +71,7 @@
|
||||
`(:exit)
|
||||
)
|
||||
|
||||
(defmacro db ()
|
||||
(defmacro dbc ()
|
||||
`(begin
|
||||
(set-config! print-ir #t)
|
||||
(set-config! print-regalloc #t)
|
||||
|
@ -147,6 +147,7 @@
|
||||
(declare-type stack-frame basic)
|
||||
(declare-type state basic)
|
||||
(declare-type cpu-thread basic)
|
||||
(declare-type dead-pool basic)
|
||||
|
||||
; DANGER - this type is created in kscheme.cpp. It has room for 12 methods and size 0x28 bytes.
|
||||
(deftype thread (basic)
|
||||
@ -207,8 +208,8 @@
|
||||
|
||||
(:methods
|
||||
(new ((allocation symbol) (type-to-make type) (name basic)) _type_ 0)
|
||||
(activate ((obj _type_) (dest process-tree) (name basic) (stack-top pointer)) basic 9)
|
||||
(deactivate ((obj _type_)) basic 10)
|
||||
(activate ((obj _type_) (dest process-tree) (name basic) (stack-top pointer)) process-tree 9)
|
||||
(deactivate ((obj _type_)) none 10)
|
||||
(dummy-method-11 () none 11)
|
||||
(run-logic? ((obj _type_)) symbol 12)
|
||||
(dummy-method () none 13)
|
||||
@ -222,7 +223,7 @@
|
||||
|
||||
;; A GOAL process. A GOAL process contains memory and a suspendable main-thread.
|
||||
(deftype process (process-tree)
|
||||
((pool basic :offset-assert #x20)
|
||||
((pool dead-pool :offset-assert #x20)
|
||||
(status basic :offset-assert #x24)
|
||||
(pid int32 :offset-assert #x28)
|
||||
(main-thread cpu-thread :offset-assert #x2c)
|
||||
@ -244,8 +245,8 @@
|
||||
|
||||
(:methods
|
||||
(new ((allocation symbol) (type-to-make type) (name basic) (stack-size int)) _type_ 0)
|
||||
(activate ((obj process) (dest process-tree) (name basic) (stack-top pointer)) basic 9)
|
||||
(deactivate ((obj process)) basic 10)
|
||||
(activate ((obj _type_) (dest process-tree) (name basic) (stack-top pointer)) process-tree 9)
|
||||
(deactivate ((obj process)) none 10)
|
||||
(dummy-method-11 () none 11)
|
||||
(run-logic? ((obj process)) symbol 12)
|
||||
(dummy-method () none 13)
|
||||
@ -361,8 +362,11 @@
|
||||
|
||||
;; A protect frame is a frame which has a cleanup function called on exit.
|
||||
(deftype protect-frame (stack-frame)
|
||||
((exit function :offset-assert 12)) ;; function to call to clean up
|
||||
((exit (function object) :offset-assert 12)) ;; function to call to clean up
|
||||
|
||||
(:methods
|
||||
(new ((allocation symbol) (type-to-make type) (func (function object))) protect-frame)
|
||||
)
|
||||
:size-assert 16
|
||||
:method-count-assert 9
|
||||
:flag-assert #x900000010
|
||||
@ -483,4 +487,13 @@
|
||||
|
||||
(defmacro process-mask-set! (mask enum-value)
|
||||
`(set! ,mask (logior ,mask (process-mask ,enum-value)))
|
||||
)
|
||||
|
||||
(defmacro suspend ()
|
||||
`(rlet ((pp :reg r13))
|
||||
(.push pp)
|
||||
(set! pp (-> (the process pp) top-thread))
|
||||
((-> (the cpu-thread pp) suspend-hook) (the cpu-thread 0))
|
||||
(.pop pp)
|
||||
)
|
||||
)
|
@ -44,6 +44,8 @@
|
||||
;; Objects on a dynamic process heap may be relocated.
|
||||
;; They should provide their own relocate method to do any fixups
|
||||
;; for any references.
|
||||
|
||||
;; Note - the actual relocation method of process is in relocate.gc.
|
||||
(defmethod relocate object ((this object) (offset int))
|
||||
this
|
||||
)
|
||||
@ -93,7 +95,10 @@
|
||||
)
|
||||
|
||||
;; the main stack for running GOAL code!
|
||||
;; all user code (that I know of) runs using *dram-stack*
|
||||
(define *dram-stack* (new 'global 'array 'uint8 DPROCESS_STACK_SIZE))
|
||||
;; note - this name is a bit confusing. The kernel-dram-stack is not the stack that the kernel runs in.
|
||||
;; I think it refers to the fact that it's _not_ the scratchpad stack (which wasn't used anyway)
|
||||
(defconstant *kernel-dram-stack* (&+ *dram-stack* DPROCESS_STACK_SIZE))
|
||||
|
||||
;; I don't think this stack is used, but I'm not sure.
|
||||
@ -114,14 +119,19 @@
|
||||
; A "temporary thread" cannot suspend and resume, but a "main thread" can.
|
||||
; The currently executing thread of a process is the "top-thread".
|
||||
|
||||
; Some GOAL threads also have the ability to "back up" their stack, while others are "temporary".
|
||||
; The main thread of a process can "back up" it's stack, and all others are temporary.
|
||||
; Threads that suspend do so by saving their saved registers and their stack.
|
||||
; All threads run on a single large stack and have small "backup" stacks that are much smaller than the main stack.
|
||||
; as a result, suspending can fail if you are using more stack than the size of your backup stack.
|
||||
; This "backup stack" can be different sizes for different threads and makes the thread type dynamic.
|
||||
; The main thread is stored on the process heap, as they need the same lifetime as the process.
|
||||
; The temporary threads are stored on the stack. There can be only one temporary thread at a time.
|
||||
|
||||
; All threads are actually cpu-threads. It's not clear why there are two separate types.
|
||||
; Perhaps the thread was the public interface and cpu-thread is internal to the kernel?
|
||||
|
||||
(defmethod delete thread ((obj thread))
|
||||
"Clean up a thread. This assumes it's the top-thread of the process and restores the previous top thread."
|
||||
"Clean up a temporary thread after it is done being used.
|
||||
This assumes it's the top-thread of the process and restores the previous top thread."
|
||||
(when (eq? obj (-> obj process main-thread))
|
||||
;; We have attempted to delete the main thread, which is bad.
|
||||
(break)
|
||||
@ -139,7 +149,7 @@
|
||||
|
||||
(defmethod stack-size-set! thread ((this thread) (stack-size int))
|
||||
"Set the backup stack size of a thread. This should only be done on the main-thread.
|
||||
This should be done immediately after allocating the main-thread"
|
||||
This should be done immediately after allocating the main-thread."
|
||||
|
||||
(let ((proc (-> this process)))
|
||||
(cond
|
||||
@ -167,7 +177,8 @@
|
||||
)
|
||||
|
||||
(defmethod new cpu-thread ((allocation symbol) (type-to-make type) (parent-process process) (name symbol) (stack-size int) (stack-top pointer))
|
||||
"Create a new CPU thread. Will allocate the main thread if none exists, otherwise a temp thread.
|
||||
"Create a new CPU thread. If there is no main thread, it will allocate the main thread on the process.
|
||||
If there is already a main thread, it will allocate a temporary thread on the given stack.
|
||||
Sets the thread as the top-thread of the process
|
||||
This is a special new method which ignores the allocation symbol.
|
||||
The stack-top is for the execution stack.
|
||||
@ -178,7 +189,7 @@
|
||||
((-> parent-process top-thread)
|
||||
;; we're allocating a temporary thread, the main thread already exists.
|
||||
;; we can stash the cpu-thread structure at the bottom of the stack.
|
||||
;; we assume the smaller PROCESS_STACK_SIZE
|
||||
;; we use the smaller PROCESS_STACK_SIZE, which is only half the size of the real stack.
|
||||
(the cpu-thread (&+ stack-top
|
||||
(- PROCESS_STACK_SIZE)
|
||||
*gtype-basic-offset*
|
||||
@ -488,7 +499,7 @@
|
||||
;; Context Suspend And Resume - Kernel
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;; the following functions are used for going from the kernel to temporary threads and back.
|
||||
;; the following functions are used for going from the kernel to threads and back.
|
||||
;; saved registers: rbx, rbp, r10, r11, r12
|
||||
|
||||
;; DANGER - THE KERNEL DOES NOT SAVE ITS FLOATING POINT CONTEXT!!!!
|
||||
@ -499,8 +510,10 @@
|
||||
|
||||
(defun return-from-thread ()
|
||||
"Context switch to the saved kernel context now.
|
||||
This is intended to be jumped to with the ret instruction
|
||||
at the end of a normal function, so this should preserve rax."
|
||||
This is intended to be jumped to with the ret instruction (return trampoline)
|
||||
at the end of a normal function, so this should preserve rax.
|
||||
To make sure this happens, all ops should be asm ops and we should have no
|
||||
GOAL expressions."
|
||||
(declare (asm-func none)
|
||||
;(print-asm)
|
||||
)
|
||||
@ -512,19 +525,20 @@
|
||||
(s3 :reg r11 :type uint)
|
||||
(s4 :reg r12 :type uint)
|
||||
)
|
||||
;; get the kernel stack pointer as a GOAL pointer
|
||||
;; get the kernel stack pointer as a GOAL pointer (won't use a temp reg)
|
||||
(.load-sym :sext #f sp *kernel-sp*)
|
||||
;; convert it back to a real pointer
|
||||
(.add sp off)
|
||||
|
||||
;; restore saved registers...
|
||||
;; without coloring system because this is "cheating".
|
||||
;; without coloring system because this is "cheating" and modifying saved registers without backing up.
|
||||
(.pop :color #f s4)
|
||||
(.pop :color #f s3)
|
||||
(.pop :color #f s2)
|
||||
(.pop :color #f s1)
|
||||
(.pop :color #f s0)
|
||||
;; return to the kernel function that called the user code
|
||||
;; rax should still contain the return value.
|
||||
(.ret)
|
||||
)
|
||||
)
|
||||
@ -545,7 +559,7 @@
|
||||
(s4 :reg r12 :type uint)
|
||||
)
|
||||
|
||||
;; first call the deactivate method.
|
||||
;; first call the deactivate method. (todo - is the stack properly aligned for this?)
|
||||
(deactivate pp)
|
||||
;; get the kernel stack pointer as a GOAL pointer
|
||||
(.load-sym :sext #f sp *kernel-sp*)
|
||||
@ -567,7 +581,9 @@
|
||||
(defun reset-and-call ((obj thread) (func function))
|
||||
"Make the given thread the top thread, reset the stack, and call the function.
|
||||
Sets up a return trampoline so when the function returns it will return to the
|
||||
kernel context."
|
||||
kernel context. Will NOT deactivate on return, so this is intended for temporary threads.
|
||||
NOTE: this should only be done from the kernel, running on the
|
||||
kernel's stack."
|
||||
(declare (asm-func object)
|
||||
;(print-asm)
|
||||
)
|
||||
@ -606,10 +622,9 @@
|
||||
(.add sp off)
|
||||
|
||||
;; push the return trampoline to the stack for the user code to return to
|
||||
;(.push 0) ;; for 16-byte stack alignment.
|
||||
(set! temp (the uint return-from-thread))
|
||||
(.add temp off)
|
||||
(.push temp)
|
||||
(.push temp) ;; stack now 16 + 8 aligned
|
||||
;; and call the function!
|
||||
(.add func off)
|
||||
(.jr func)
|
||||
@ -621,7 +636,7 @@
|
||||
;; Context Suspend And Resume - Thread
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;; these are for resuming and suspending a thread.
|
||||
;; these are for resuming and suspending main threads.
|
||||
|
||||
(defmethod thread-suspend cpu-thread ((unused cpu-thread))
|
||||
"Suspend the thread and return to the kernel."
|
||||
@ -643,7 +658,7 @@
|
||||
(s3 :reg r11 :type uint)
|
||||
(s4 :reg r12 :type uint))
|
||||
|
||||
;; get the return address pushed by "call"
|
||||
;; get the return address pushed by "call" in the suspend.
|
||||
(.pop temp)
|
||||
;; convert to a GOAL address
|
||||
(.sub temp off)
|
||||
@ -711,6 +726,9 @@
|
||||
|
||||
|
||||
(defmethod thread-resume cpu-thread ((thread-to-resume cpu-thread))
|
||||
"Resume a suspended thread. Call this from the kernel only.
|
||||
This is also used to start a thread initialized with set-to-run.
|
||||
As a result of MIPS/x86 differences, there is a hack for this."
|
||||
(declare (asm-func none)
|
||||
;(print-asm)
|
||||
)
|
||||
@ -723,7 +741,10 @@
|
||||
(s1 :reg rbp :type uint)
|
||||
(s2 :reg r10 :type uint)
|
||||
(s3 :reg r11 :type uint)
|
||||
(s4 :reg r12 :type uint))
|
||||
(s4 :reg r12 :type uint)
|
||||
(a4 :reg r8 :type uint)
|
||||
(a5 :reg r9 :type uint)
|
||||
)
|
||||
|
||||
;; save the current kernel regs
|
||||
(.push :color #f s0)
|
||||
@ -740,10 +761,10 @@
|
||||
;; temp, stash thread in process-pointer
|
||||
(set! obj thread-to-resume)
|
||||
|
||||
;; set stack pointer for the thread.
|
||||
;; set stack pointer for the thread. leave it as a GOAL pointer for now..
|
||||
(set! sp (the uint (-> obj sp)))
|
||||
|
||||
;; restore the stack.
|
||||
;; restore the stack (sp is a GOAL pointer)
|
||||
(let ((cur (the (pointer uint64) (-> obj stack-top)))
|
||||
(restore (&+ (the (pointer uint64) (-> obj stack)) (-> obj stack-size)))
|
||||
)
|
||||
@ -754,7 +775,7 @@
|
||||
)
|
||||
)
|
||||
|
||||
;; offset sp after we're done looking at it.
|
||||
;; offset sp after we're done using it as a GOAL pointer.
|
||||
(.add sp off)
|
||||
|
||||
;; setup process
|
||||
@ -772,12 +793,30 @@
|
||||
(.mov :color #f s3 temp)
|
||||
(set! temp (-> obj rreg 4))
|
||||
(.mov :color #f s4 temp)
|
||||
|
||||
;; todo restore fpr.
|
||||
|
||||
;; hack for set-to-run-bootstrap. The set-to-run-bootstrap in MIPS
|
||||
;; expects to receive 7 values from the cpu thread's rregs.
|
||||
;; usually rreg holds saved registers, but on the first resume after
|
||||
;; a set-to-run, they hold arguments, and set-to-run-bootstrap copies them.
|
||||
|
||||
;; We only have 5 saved regs, so we need to cheat and directly pass
|
||||
;; two values in other registers
|
||||
;; so we load the a4/a5 argument registers with rreg 5 and rreg 6
|
||||
;; In the case where we are doing a normal resume, the
|
||||
;; compiler should assume that these registers are overwritten anyway.
|
||||
(set! temp (-> obj rreg 5))
|
||||
(.mov a4 temp)
|
||||
(set! temp (-> obj rreg 6))
|
||||
(.mov a5 temp)
|
||||
|
||||
;; get the resume address
|
||||
(set! temp (the uint (-> obj pc)))
|
||||
(.add temp off)
|
||||
|
||||
;; setup the process
|
||||
(set! obj (the cpu-thread (-> obj process)))
|
||||
;; resume!
|
||||
(.jr temp)
|
||||
)
|
||||
(none)
|
||||
@ -1576,7 +1615,7 @@
|
||||
(execute-process-tree
|
||||
*active-pool*
|
||||
(lambda ((obj process))
|
||||
(format 0 "Call to dispatcher lambda!~%")
|
||||
;(format 0 "Call to dispatcher lambda!~%")
|
||||
(let ((context *kernel-context*))
|
||||
|
||||
(cond
|
||||
@ -1707,6 +1746,14 @@
|
||||
)
|
||||
)
|
||||
|
||||
(defmacro set-u64-from-u128! (dst src)
|
||||
`(set! ,dst (-> (the (pointer uint64) (& ,src))))
|
||||
)
|
||||
|
||||
(defmacro the-super-u64-fucntion (func)
|
||||
`(the-as (function uint uint uint uint uint uint object) ,func)
|
||||
)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Stack Frame Stuff (TODO)
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
@ -1715,94 +1762,181 @@
|
||||
;; The catch frames are managed per process (you can't throw to a frame outside your process)
|
||||
;; But otherwise it is fully dynamic.
|
||||
|
||||
; (defmethod new catch-frame ((allocation symbol) (type-to-make type) (name symbol) (func function) (param-block (pointer uint64)))
|
||||
; "Run func in a catch frame with the given 8 parameters.
|
||||
; The return value is the result of the function.
|
||||
; The allocation must be an address"
|
||||
; (declare (asm-func object)
|
||||
; (print-asm)
|
||||
; )
|
||||
(defmethod new catch-frame ((allocation symbol) (type-to-make type) (name symbol) (func function) (param-block (pointer uint64)))
|
||||
"Run func in a catch frame with the given 8 parameters.
|
||||
The return value is the result of the function.
|
||||
The allocation must be an address.
|
||||
Unlike the original, this only works on the first six parameters, but I think this doesn't matter."
|
||||
(declare (asm-func object)
|
||||
;(print-asm)
|
||||
(allow-saved-regs) ;; very dangerous!
|
||||
)
|
||||
|
||||
; (rlet ((pp :reg r13 :type process)
|
||||
; (temp :reg rax :type uint)
|
||||
; (off :reg r15 :type uint)
|
||||
; (sp :reg rsp :type uint)
|
||||
; (s0 :reg rbx :type uint)
|
||||
; (s1 :reg rbp :type uint)
|
||||
; (s2 :reg r10 :type uint)
|
||||
; (s3 :reg r11 :type uint)
|
||||
; (s4 :reg r12 :type uint)
|
||||
; (a0 :reg rdi :type uint)
|
||||
; (a1 :reg rsi :type uint)
|
||||
; (a2 :reg rdx :type uint)
|
||||
; (a3 :reg rcx :type uint)
|
||||
; (a4 :reg r8 :type uint)
|
||||
; (a5 :reg r9 :type uint)
|
||||
; (a6 :reg r10 :type uint)
|
||||
; (a7 :reg r11 :type uint)
|
||||
; )
|
||||
(rlet ((pp :reg r13 :type process)
|
||||
(temp :reg rax :type uint)
|
||||
(off :reg r15 :type uint)
|
||||
(sp :reg rsp :type uint)
|
||||
(s0 :reg rbx :type uint)
|
||||
(s1 :reg rbp :type uint)
|
||||
(s2 :reg r10 :type (pointer uint64))
|
||||
(s3 :reg r11 :type uint)
|
||||
(s4 :reg r12 :type uint)
|
||||
)
|
||||
|
||||
; ;; we treat the allocation as an address.
|
||||
; (let ((obj (the catch-frame (&+ allocation *gtype-basic-offset*))))
|
||||
; ;; setup catch frame
|
||||
; (set! (-> obj type) type-to-make)
|
||||
; (set! (-> obj name) name)
|
||||
; ;; get the return address
|
||||
; (.pop temp)
|
||||
; (.push temp)
|
||||
; ;; make it a GOAL address so it fits in 32 bitys
|
||||
; (.sub temp off)
|
||||
; ;; store it
|
||||
; (set! (-> obj ra) (the int temp))
|
||||
;; we treat the allocation as an address.
|
||||
(let ((obj (the catch-frame (&+ allocation *gtype-basic-offset*))))
|
||||
;; setup catch frame
|
||||
(set! (-> obj type) type-to-make)
|
||||
(set! (-> obj name) name)
|
||||
;; get the return address (the compiler won't touch the stack because we're an asm-func)
|
||||
(.pop temp)
|
||||
(.push temp)
|
||||
;; make it a GOAL address so it fits in 32 bitys
|
||||
(.sub temp off)
|
||||
;; store it
|
||||
(set! (-> obj ra) (the int temp))
|
||||
|
||||
; ;; todo, do we need a stack offset here?
|
||||
; (set! temp sp)
|
||||
; (.sub temp off)
|
||||
; (set! (-> obj sp) (the int sp))
|
||||
;; todo, do we need a stack offset here?
|
||||
;; remember the stack pointer
|
||||
(set! temp sp)
|
||||
(.sub temp off)
|
||||
(set! (-> obj sp) (the int sp))
|
||||
|
||||
; ;; back up registers
|
||||
; (.mov :color #f temp s0)
|
||||
; (set-u128-as-u64! (-> obj rreg 0) temp)
|
||||
; (.mov :color #f temp s1)
|
||||
; (set-u128-as-u64! (-> obj rreg 1) temp)
|
||||
; (.mov :color #f temp s2)
|
||||
; (set-u128-as-u64! (-> obj rreg 2) temp)
|
||||
; (.mov :color #f temp s3)
|
||||
; (set-u128-as-u64! (-> obj rreg 3) temp)
|
||||
; (.mov :color #f temp s4)
|
||||
; (set-u128-as-u64! (-> obj rreg 4) temp)
|
||||
; ;; todo save fprs
|
||||
;; back up registers we care about
|
||||
(.mov :color #f temp s0)
|
||||
(set-u128-as-u64! (-> obj rreg 0) temp)
|
||||
(.mov :color #f temp s1)
|
||||
(set-u128-as-u64! (-> obj rreg 1) temp)
|
||||
(.mov :color #f temp s2)
|
||||
(set-u128-as-u64! (-> obj rreg 2) temp)
|
||||
(.mov :color #f temp s3)
|
||||
(set-u128-as-u64! (-> obj rreg 3) temp)
|
||||
(.mov :color #f temp s4)
|
||||
(set-u128-as-u64! (-> obj rreg 4) temp)
|
||||
;; todo save fprs
|
||||
|
||||
; ;; push this stack frame
|
||||
; (set! (-> obj next) (-> pp stack-frame-top))
|
||||
; (set! (-> pp stack-frame-top) obj)
|
||||
;; push this stack frame
|
||||
(set! (-> obj next) (-> pp stack-frame-top))
|
||||
(set! (-> pp stack-frame-top) obj)
|
||||
|
||||
; (let ((ret ((the-super-u64-fucntion func)
|
||||
; ;(-> param-block 0)
|
||||
; (-> param-block)
|
||||
; ;(-> param-block 1)
|
||||
; (-> (&+ param-block 8))
|
||||
; (-> (&+ param-block 16))
|
||||
; (-> (&+ param-block 24))
|
||||
; ;(-> (&+ param-block 32))
|
||||
; ; (-> param-block 5)
|
||||
; ))
|
||||
; )
|
||||
;; help coloring, it isn't smart enough to realize it's "safe" to use these registers.
|
||||
(.push :color #f s3)
|
||||
(.push :color #f s2)
|
||||
(set! s3 (the uint func))
|
||||
(set! s2 param-block)
|
||||
|
||||
;; todo - are we aligned correctly here?
|
||||
(let ((ret ((the-super-u64-fucntion s3)
|
||||
(-> s2 0)
|
||||
(-> s2 1)
|
||||
(-> s2 2)
|
||||
(-> s2 3)
|
||||
(-> s2 4)
|
||||
(-> s2 5)
|
||||
))
|
||||
)
|
||||
|
||||
; ; (set! (-> pp stack-frame-top) (-> pp stack-frame-top next))
|
||||
; )
|
||||
; )
|
||||
; )
|
||||
; ;; the code in here may throw at any point in time, without properly resetting saved registers.
|
||||
; ;; so we should save them ourself.
|
||||
(.pop :color #f s2)
|
||||
(.pop :color #f s3)
|
||||
(set! (-> pp stack-frame-top) (-> pp stack-frame-top next))
|
||||
(.ret)
|
||||
(the object ret)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
(defun throw-dispatch ((obj catch-frame) value)
|
||||
"Throw the given value to the catch frame.
|
||||
Only can throw a 64-bit value. The original could throw 128 bits."
|
||||
(declare (asm-func none)
|
||||
;(print-asm)
|
||||
)
|
||||
|
||||
; (the object #f)
|
||||
; )
|
||||
(rlet ((pp :reg r13 :type process)
|
||||
(temp :reg rax :type uint)
|
||||
(off :reg r15 :type uint)
|
||||
(sp :reg rsp :type uint)
|
||||
(s0 :reg rbx :type uint)
|
||||
(s1 :reg rbp :type uint)
|
||||
(s2 :reg r10 :type (pointer uint64))
|
||||
(s3 :reg r11 :type uint)
|
||||
(s4 :reg r12 :type uint)
|
||||
)
|
||||
|
||||
;; pop everything we threw past
|
||||
(set! (-> pp stack-frame-top) (-> obj next))
|
||||
|
||||
;; restore regs we care about.
|
||||
(set-u64-from-u128! temp (-> obj rreg 0))
|
||||
(.mov :color #f s0 temp)
|
||||
(set-u64-from-u128! temp (-> obj rreg 1))
|
||||
(.mov :color #f s1 temp)
|
||||
(set-u64-from-u128! temp (-> obj rreg 2))
|
||||
(.mov :color #f s2 temp)
|
||||
(set-u64-from-u128! temp (-> obj rreg 3))
|
||||
(.mov :color #f s3 temp)
|
||||
(set-u64-from-u128! temp (-> obj rreg 4))
|
||||
(.mov :color #f s4 temp)
|
||||
;; todo fpr
|
||||
|
||||
;; set stack pointer
|
||||
(set! sp (the uint (-> obj sp)))
|
||||
(.add sp off)
|
||||
|
||||
;; overwrite our return address
|
||||
(.pop temp)
|
||||
(set! temp (the uint (-> obj ra)))
|
||||
(.add temp off)
|
||||
(.push temp)
|
||||
|
||||
;; load the return register
|
||||
(.mov temp value)
|
||||
(.ret)
|
||||
)
|
||||
)
|
||||
|
||||
(defun throw ((name symbol) value)
|
||||
"Dynamic throw."
|
||||
(rlet ((pp :reg r13 :type process))
|
||||
(let ((cur (-> pp stack-frame-top)))
|
||||
(while cur
|
||||
(when (and (eq? (-> cur name) name) (eq? (-> cur type) catch-frame))
|
||||
;; match!
|
||||
(throw-dispatch (the catch-frame cur) value)
|
||||
)
|
||||
|
||||
(if (eq? (-> cur type) protect-frame)
|
||||
;; call the cleanup function
|
||||
((-> (the protect-frame cur) exit))
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
(format 0 "ERROR: throw could not find tag ~A~%" name)
|
||||
(break)
|
||||
)
|
||||
|
||||
(defmethod new protect-frame ((allocation symbol) (type-to-make type) (func (function object)))
|
||||
(let ((obj (the protect-frame (&+ allocation *gtype-basic-offset*))))
|
||||
(set! (-> obj type) type-to-make)
|
||||
(set! (-> obj name) 'protect-frame)
|
||||
(set! (-> obj exit) func)
|
||||
|
||||
(rlet ((pp :reg r13 :type process))
|
||||
(set! (-> obj next) (-> pp stack-frame-top))
|
||||
(set! (-> pp stack-frame-top) obj)
|
||||
)
|
||||
obj
|
||||
)
|
||||
)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Tree Stuff
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;; todo previous-brother
|
||||
|
||||
(defun change-parent ((obj process-tree) (new-parent process-tree))
|
||||
"Make obj a child of new-parent"
|
||||
(let ((parent (-> obj parent)))
|
||||
@ -1834,6 +1968,298 @@
|
||||
)
|
||||
)
|
||||
|
||||
;; todo change-brother
|
||||
;; todo change-to-last-brother
|
||||
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Process Control
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defmethod activate process ((obj process) (dest process-tree) (name basic) (stack-top pointer))
|
||||
"Activate a process! Put it on the given active tree and set up the main thread."
|
||||
(set! (-> obj mask) (logand (-> dest mask) PROCESS_CLEAR_MASK))
|
||||
(set! (-> obj status) 'ready)
|
||||
(let ((pid (-> *kernel-context* next-pid)))
|
||||
(set! (-> obj pid) pid)
|
||||
(set! (-> *kernel-context* next-pid) (+ 1 pid)))
|
||||
(set! (-> obj top-thread) #f)
|
||||
(set! (-> obj main-thread) #f)
|
||||
(set! (-> obj name) name)
|
||||
(set! (-> obj heap-base) (set! (-> obj heap-cur) (&+ (-> obj stack) (-> obj type heap-base))))
|
||||
(set! (-> obj stack-frame-top) #f)
|
||||
(mem-set32! (-> obj stack) (the int (/ (-> obj type heap-base) 4)) 0)
|
||||
|
||||
(set! (-> obj trans-hook) #f)
|
||||
(set! (-> obj post-hook) #f)
|
||||
(set! (-> obj event-hook) #f)
|
||||
(set! (-> obj state) #f)
|
||||
(set! (-> obj next-state) #f)
|
||||
(if (process-mask? (-> dest mask) process-tree)
|
||||
(set! (-> obj entity) #f)
|
||||
(set! (-> obj entity) (-> (the process dest) entity))
|
||||
)
|
||||
|
||||
(set! (-> obj connection-list next1) #f)
|
||||
(set! (-> obj connection-list prev1) #f)
|
||||
|
||||
;; todo global -> process
|
||||
(let ((thread (new 'global 'cpu-thread obj 'code PROCESS_STACK_SAVE_SIZE stack-top)))
|
||||
(set! (-> obj main-thread) thread)
|
||||
)
|
||||
(change-parent obj dest)
|
||||
)
|
||||
|
||||
(defun run-function-in-process ((obj process) (func function) a0 a1 a2 a3 a4 a5)
|
||||
"Switch to the given process and run the function. This is used to initialize a process.
|
||||
The function will run until it attempts to change state. At the first attempt to change state,
|
||||
this function will return. The idea is that you use this when you want to initialize a process NOW.
|
||||
This will then return the value of the function you called!"
|
||||
|
||||
(rlet ((pp :reg r13 :type process))
|
||||
|
||||
(let ((param-array (new 'stack 'array 'uint64 6)))
|
||||
;; copy params to the stack.
|
||||
(set! (-> param-array 0) (the uint64 a0))
|
||||
(set! (-> param-array 1) (the uint64 a1))
|
||||
(set! (-> param-array 2) (the uint64 a2))
|
||||
(set! (-> param-array 3) (the uint64 a3))
|
||||
(set! (-> param-array 4) (the uint64 a4))
|
||||
(set! (-> param-array 5) (the uint64 a5))
|
||||
|
||||
(let* ((old-pp pp)
|
||||
(func-val (begin
|
||||
;; set the process
|
||||
(set! pp obj)
|
||||
;; set us as initializing
|
||||
(set! (-> pp status) 'initialize)
|
||||
;; run!
|
||||
(the object (new 'stack 'catch-frame 'initialize func param-array))
|
||||
)))
|
||||
;; the function returned, either through a throw or through actually returning.
|
||||
;; the status will give us a clue of what happened.
|
||||
(cond
|
||||
((= (-> pp status) 'initialize)
|
||||
;; we returned and didn't change status.
|
||||
(set! (-> pp status) 'initialize-dead)
|
||||
;; this means we died, and we should be deactivated.
|
||||
(deactivate pp)
|
||||
)
|
||||
((= (-> pp status) 'initalize-go)
|
||||
;; we returned with a (suspend) or (go) ? not sure
|
||||
;; either way, we're ready for next time!
|
||||
(set! (-> pp status) 'waiting-to-run)
|
||||
(when (eq? (-> pp pool type) dead-pool-heap)
|
||||
;; we can shrink the heap now.
|
||||
(shrink-heap (the dead-pool-heap (-> pp pool)) pp)
|
||||
)
|
||||
)
|
||||
(else
|
||||
(format 0 "GOT UNKNOWN INIT: ~A~%" (-> pp status))
|
||||
)
|
||||
)
|
||||
;; restore the old pp
|
||||
(set! pp old-pp)
|
||||
func-val
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
(defun set-to-run-bootstrap ()
|
||||
"This function is a clever hack.
|
||||
To reset a thread to running a new function, we stash the arguments as saved registers.
|
||||
These are then restored by thread-resume on the next run of the kernel.
|
||||
This stub remaps these saved registers to argument registers.
|
||||
It also creates a return trampoline to return-from-thread-dead"
|
||||
(declare (asm-func none)
|
||||
;(print-asm)
|
||||
)
|
||||
|
||||
(rlet ((s0 :reg rbx :type uint)
|
||||
(s1 :reg rbp :type uint)
|
||||
(s2 :reg r10 :type uint)
|
||||
(s3 :reg r11 :type uint)
|
||||
(s4 :reg r12 :type uint)
|
||||
(a0 :reg rdi :type uint) ; ok
|
||||
(a1 :reg rsi :type uint) ; ok
|
||||
(a2 :reg rdx :type uint) ; ok
|
||||
(a3 :reg rcx :type uint) ; ok
|
||||
(off :reg r15 :type uint)
|
||||
(temp :reg rax)
|
||||
)
|
||||
|
||||
|
||||
(.mov temp return-from-thread-dead)
|
||||
(.add temp off)
|
||||
(.push temp)
|
||||
|
||||
;; stack is 16 + 8 aligned now
|
||||
|
||||
(.mov :color #f a0 s1)
|
||||
(.mov :color #f a1 s2)
|
||||
(.mov :color #f a2 s3)
|
||||
(.mov :color #f a3 s4)
|
||||
|
||||
(.add :color #f s0 off)
|
||||
(.jr :color #f s0)
|
||||
|
||||
)
|
||||
|
||||
)
|
||||
|
||||
|
||||
(defun set-to-run ((thread cpu-thread) (func function) a0 a1 a2 a3 a4 a5)
|
||||
"Set the given thread to call the given function with the given arguments next time it resumes.
|
||||
Only for main threads.
|
||||
Once the function returns, the process deactivates."
|
||||
(let ((proc (-> thread process)))
|
||||
(set! (-> proc status) 'waiting-to-run)
|
||||
|
||||
;; we store arguments and the function to call in saved registers
|
||||
(set! (-> thread rreg 0) (the uint func))
|
||||
(set! (-> thread rreg 1) (the uint a0))
|
||||
(set! (-> thread rreg 2) (the uint a1))
|
||||
(set! (-> thread rreg 3) (the uint a2))
|
||||
(set! (-> thread rreg 4) (the uint a3))
|
||||
(set! (-> thread rreg 5) (the uint a4))
|
||||
(set! (-> thread rreg 6) (the uint a5))
|
||||
|
||||
;; and have the thread first call set-to-run-bootstrap, which will properly call
|
||||
;; the function with the arguments and install a return trampoline for
|
||||
;; deactivating and returning to the kernel on return.
|
||||
(set! (-> thread pc) (the pointer set-to-run-bootstrap))
|
||||
;; reset sp.
|
||||
(set! (-> thread sp) (-> thread stack-top))
|
||||
)
|
||||
)
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Process Deactivation
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defmethod deactivate process-tree ((obj process-tree))
|
||||
;; todo
|
||||
(format 0 "CALL TO DEACTIVATE~%")
|
||||
(none)
|
||||
)
|
||||
|
||||
;; todo defstate
|
||||
(define dead-state
|
||||
(new 'static 'state
|
||||
:name #f
|
||||
:next #f
|
||||
:exit #f
|
||||
:code #f
|
||||
:trans #f
|
||||
:post #f
|
||||
:enter #f
|
||||
:event #f))
|
||||
|
||||
(set! (-> dead-state code) nothing)
|
||||
|
||||
|
||||
;; hack
|
||||
(define-extern entity-deactivate-handler (function process object none))
|
||||
(define-extern process-disconnect (function object none))
|
||||
|
||||
(defmethod deactivate process ((obj process))
|
||||
"Deactivate a process. This returns the process to the dead pool
|
||||
it came from. You can use this on your own process to kill yourself
|
||||
and immediately return to the kernel.
|
||||
You can also use this during initialization to kill yourself and return
|
||||
to the process that initialzed you.
|
||||
All protects/states will be cleaned up, with pp set correctly for the process.
|
||||
But you might not have the stack of your main thread, so don't reference stack
|
||||
vars from within your exit handlers."
|
||||
|
||||
;; don't do anything if we already died.
|
||||
(unless (eq? (-> obj status) 'dead)
|
||||
(set! (-> obj next-state) dead-state)
|
||||
|
||||
;; call entity handler
|
||||
(when (-> obj entity)
|
||||
(entity-deactivate-handler obj (-> obj entity))
|
||||
)
|
||||
|
||||
;; clean up stack frames the process is in.
|
||||
;; first, set pp so the cleanup code thinks its running in the right process.
|
||||
(rlet ((pp :reg r13 :type process))
|
||||
(let ((old-pp pp))
|
||||
(set! pp obj)
|
||||
(let ((cur (-> pp stack-frame-top)))
|
||||
(while cur
|
||||
(when (or
|
||||
(= (-> cur type) protect-frame)
|
||||
(= (-> cur type) state)
|
||||
)
|
||||
;; we're a state or protect-frame, we can exit.
|
||||
((-> (the protect-frame cur) exit))
|
||||
)
|
||||
(set! cur (-> cur next))
|
||||
)
|
||||
)
|
||||
(set! pp old-pp)
|
||||
)
|
||||
)
|
||||
|
||||
;; hack - if this isn't defined yet, don't try it.
|
||||
(if (!= 0 (the uint process-disconnect))
|
||||
(process-disconnect obj)
|
||||
)
|
||||
|
||||
;; kill our child and their brothers
|
||||
(let ((bro (-> obj child)))
|
||||
(while bro
|
||||
(let ((temp (-> (-> bro) brother)))
|
||||
(deactivate (-> bro))
|
||||
(set! bro temp)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
;; return ourself to the pool
|
||||
(return-process (-> obj pool) obj)
|
||||
(set! (-> obj state) #f)
|
||||
(set! (-> obj next-state) #f)
|
||||
(set! (-> obj entity) #f)
|
||||
(set! (-> obj pid) 0)
|
||||
|
||||
;; deal with getting out of here.
|
||||
(cond
|
||||
;; first case - we deactivated the running process
|
||||
;; (note, we don't check against pp because run-function-in-process
|
||||
;; will change pp for running initializations.)
|
||||
((eq? obj (-> *kernel-context* current-process))
|
||||
;; go straight to dead.
|
||||
(set! (-> obj status) 'dead)
|
||||
;; and return (with no deactivate)
|
||||
(let ((temp (the uint return-from-thread)))
|
||||
(rlet ((off :reg r15 :type uint))
|
||||
(+! temp off)
|
||||
(.push temp)
|
||||
(.ret)
|
||||
)
|
||||
)
|
||||
)
|
||||
;; second case - we deactivated while initializing.
|
||||
((eq? (-> obj status) 'initialize)
|
||||
;; added this
|
||||
|
||||
; (if (!= pp obj)
|
||||
; (format 0 "ERROR: deactivated a non-current initializing process!")
|
||||
; (break)
|
||||
; )
|
||||
(set! (-> obj status) 'dead)
|
||||
(throw 'initalize #f)
|
||||
)
|
||||
)
|
||||
(set! (-> obj status) 'dead)
|
||||
)
|
||||
(none)
|
||||
)
|
||||
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Process Globals
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
@ -1869,9 +2295,25 @@
|
||||
|
||||
;; todo dead pool list
|
||||
|
||||
|
||||
;; main active pool
|
||||
(define *active-pool* (new 'global 'process-tree 'active-pool))
|
||||
|
||||
;; other active pools
|
||||
(change-parent (define *display-pool* (new 'global 'process-tree 'display-pool)) *active-pool*)
|
||||
|
||||
(change-parent (define *camera-pool* (new 'global 'process-tree 'camera-pool)) *active-pool*)
|
||||
(set! (-> *camera-pool* mask) (process-mask pause menu progress camera process-tree))
|
||||
|
||||
(change-parent (define *target-pool* (new 'global 'process-tree 'target-pool)) *active-pool*)
|
||||
(set! (-> *target-pool* mask) (process-mask pause menu progress process-tree))
|
||||
|
||||
(change-parent (define *entity-pool* (new 'global 'process-tree 'entity-pool)) *active-pool*)
|
||||
(set! (-> *entity-pool* mask) (process-mask pause menu progress entity process-tree))
|
||||
|
||||
(change-parent (define *default-pool* (new 'global 'process-tree 'default-pool)) *active-pool*)
|
||||
(set! (-> *default-pool* mask) (process-mask pause menu progress process-tree))
|
||||
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Temp Hacks
|
||||
|
@ -204,6 +204,23 @@ bool Compiler::codegen_and_disassemble_object_file(FileEnv* env,
|
||||
return ok;
|
||||
}
|
||||
|
||||
void Compiler::compile_and_send_from_string(const std::string& source_code) {
|
||||
if (!connect_to_target()) {
|
||||
throw std::runtime_error(
|
||||
"Compiler failed to connect to target for compile_and_send_from_string.");
|
||||
}
|
||||
|
||||
auto code = m_goos.reader.read_from_string(source_code);
|
||||
auto compiled = compile_object_file("test-code", code, true);
|
||||
assert(!compiled->is_empty());
|
||||
color_object_file(compiled);
|
||||
auto data = codegen_object_file(compiled);
|
||||
m_listener.send_code(data);
|
||||
if (!m_listener.most_recent_send_was_acked()) {
|
||||
print_compiler_warning("Runtime is not responding after sending test code. Did it crash?\n");
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> Compiler::run_test_from_file(const std::string& source_code) {
|
||||
try {
|
||||
if (!connect_to_target()) {
|
||||
|
@ -35,6 +35,7 @@ class Compiler {
|
||||
std::vector<std::string> run_test_from_string(const std::string& src,
|
||||
const std::string& obj_name = "*listener*");
|
||||
std::vector<std::string> run_test_no_load(const std::string& source_code);
|
||||
void compile_and_send_from_string(const std::string& source_code);
|
||||
void run_front_end_on_string(const std::string& src);
|
||||
void shutdown_target();
|
||||
void enable_throw_on_redefines() { m_throw_on_define_extern_redefinition = true; }
|
||||
|
@ -141,7 +141,7 @@ Val* Compiler::compile_lambda(const goos::Object& form, const goos::Object& rest
|
||||
new_func_env->set_segment(segment);
|
||||
|
||||
// set up arguments
|
||||
if (lambda.params.size() >= 8) {
|
||||
if (lambda.params.size() > 8) {
|
||||
throw_compiler_error(form,
|
||||
"Cannot generate an x86-64 function for a lambda with {} parameters. "
|
||||
"The current limit is 8.",
|
||||
|
@ -304,6 +304,16 @@ std::vector<std::string> Listener::stop_recording_messages() {
|
||||
return result;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Get the number of messages recorded so far.
|
||||
*/
|
||||
int Listener::get_received_message_count() {
|
||||
rcv_mtx.lock();
|
||||
auto result = message_record.size();
|
||||
rcv_mtx.unlock();
|
||||
return result;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Send a "CODE" message for the target to execute as the Listener Function.
|
||||
* Returns once the target acks the code.
|
||||
|
@ -30,6 +30,7 @@ class Listener {
|
||||
const std::string& ip = "127.0.0.1",
|
||||
int port = DECI2_PORT);
|
||||
void record_messages(ListenerMessageKind kind);
|
||||
int get_received_message_count();
|
||||
std::vector<std::string> stop_recording_messages();
|
||||
bool is_connected() const;
|
||||
void send_reset(bool shutdown);
|
||||
|
@ -895,4 +895,4 @@ bool run_allocator(RegAllocCache* cache, const AllocationInput& in, int debug_tr
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -221,6 +221,14 @@ struct LiveInfo {
|
||||
return assignment.at(id - min);
|
||||
}
|
||||
|
||||
int size() const { return 1 + max - min; }
|
||||
|
||||
bool overlaps(const LiveInfo& other) const {
|
||||
auto overlap_min = std::max(min, other.min);
|
||||
auto overlap_max = std::min(max, other.max);
|
||||
return overlap_min <= overlap_max;
|
||||
}
|
||||
|
||||
std::string print_assignment();
|
||||
};
|
||||
#endif // JAK_ALLOCATE_COMMON_H
|
||||
|
@ -16,6 +16,7 @@ add_executable(goalc-test
|
||||
test_common_util.cpp
|
||||
test_pretty_print.cpp
|
||||
test_zydis.cpp
|
||||
goalc/test_goal_kernel.cpp
|
||||
${GOALC_TEST_FRAMEWORK_SOURCES}
|
||||
${GOALC_TEST_CASES})
|
||||
|
||||
|
64
test/goalc/source_templates/kernel/kernel-test.gc
Normal file
64
test/goalc/source_templates/kernel/kernel-test.gc
Normal file
@ -0,0 +1,64 @@
|
||||
(defun deactivate-myself ()
|
||||
"Deactivate the current process. A workaround because rlet isn't working well."
|
||||
(rlet ((pp :reg r13 :type process))
|
||||
(deactivate pp)
|
||||
)
|
||||
)
|
||||
|
||||
(defun target-function ((a0 uint) (a1 uint) (a2 uint) (a3 uint) (a4 uint) (a5 uint))
|
||||
(format #t "TARGET FUNCTION ~D ~D ~D~%" a0 a1 a2)
|
||||
(format #t "~D ~D ~D~%" a3 a4 a5)
|
||||
|
||||
(let ((stack-arr (new 'stack 'array 'uint8 12)))
|
||||
(format #t "Stack Alignemnt ~D/16~%" (logand 15 (the uint stack-arr)))
|
||||
)
|
||||
|
||||
(dotimes (i 10)
|
||||
(format #t "proc1: ~D~%" i)
|
||||
(when (> i 4)
|
||||
(format #t "DEACTIVATE PROC 1~%")
|
||||
(deactivate-myself)
|
||||
)
|
||||
(suspend)
|
||||
)
|
||||
)
|
||||
|
||||
(define-extern recurse (function int (pointer int32) int))
|
||||
(defun recurse ((i int) (ptr (pointer int32)))
|
||||
(if (> i 0)
|
||||
(recurse (- i 1) ptr)
|
||||
(suspend)
|
||||
)
|
||||
(set! (-> ptr) (+ (-> ptr) 1))
|
||||
1
|
||||
)
|
||||
|
||||
(defun target-function-2 ()
|
||||
(let ((stack-var (new 'stack 'array 'int32 1)))
|
||||
(set! (-> stack-var) 0)
|
||||
(countdown (i 10)
|
||||
(format #t "proc2: ~D~%" (-> stack-var))
|
||||
(recurse 5 stack-var)
|
||||
)
|
||||
)
|
||||
|
||||
)
|
||||
|
||||
(defun kernel-test ()
|
||||
(define test-process (get-process *nk-dead-pool* process 1024))
|
||||
|
||||
(activate test-process *active-pool* 'test-proc *kernel-dram-stack*)
|
||||
|
||||
|
||||
(set-to-run (-> test-process main-thread)
|
||||
target-function
|
||||
1 2 3 4 5 6
|
||||
)
|
||||
|
||||
(define test-process-2 (get-process *nk-dead-pool* process 1024))
|
||||
(activate test-process-2 *active-pool* 'test-2 *kernel-dram-stack*)
|
||||
(set-to-run (-> test-process-2 main-thread)
|
||||
target-function-2
|
||||
0 0 0 0 0 0)
|
||||
0
|
||||
)
|
89
test/goalc/test_goal_kernel.cpp
Normal file
89
test/goalc/test_goal_kernel.cpp
Normal file
@ -0,0 +1,89 @@
|
||||
#include <thread>
|
||||
#include "goalc/compiler/Compiler.h"
|
||||
#include "test/goalc/framework/test_runner.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
class KernelTest : public testing::Test {
|
||||
public:
|
||||
static void SetUpTestSuite() {
|
||||
printf("Building kernel...\n");
|
||||
try {
|
||||
// a macro in goal-lib.gc
|
||||
compiler.run_front_end_on_string("(build-kernel)");
|
||||
} catch (std::exception& e) {
|
||||
fprintf(stderr, "caught exception %s\n", e.what());
|
||||
EXPECT_TRUE(false);
|
||||
}
|
||||
|
||||
printf("Starting GOAL Kernel...\n");
|
||||
runtime_thread = std::thread(GoalTest::runtime_with_kernel);
|
||||
runner.c = &compiler;
|
||||
}
|
||||
|
||||
static void TearDownTestSuite() {
|
||||
// send message to shutdown
|
||||
compiler.shutdown_target();
|
||||
// wait for shutdown.
|
||||
runtime_thread.join();
|
||||
}
|
||||
|
||||
void SetUp() {}
|
||||
|
||||
void TearDown() {}
|
||||
|
||||
static std::thread runtime_thread;
|
||||
static Compiler compiler;
|
||||
static GoalTest::CompilerTestRunner runner;
|
||||
};
|
||||
|
||||
std::thread KernelTest::runtime_thread;
|
||||
Compiler KernelTest::compiler;
|
||||
GoalTest::CompilerTestRunner KernelTest::runner;
|
||||
|
||||
TEST_F(KernelTest, Basic) {
|
||||
// first, let's load the kernel test code
|
||||
runner.c->run_test_from_string("(ml \"test/goalc/source_templates/kernel/kernel-test.gc\")");
|
||||
auto& listener = runner.c->listener();
|
||||
|
||||
// record all print messages
|
||||
listener.record_messages(ListenerMessageKind::MSG_PRINT);
|
||||
|
||||
// run the test.
|
||||
runner.c->compile_and_send_from_string("(kernel-test)");
|
||||
|
||||
// kinda hacky, but wait until the kernel runs and sends all the messages
|
||||
while (listener.get_received_message_count() < 10) {
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(1000));
|
||||
}
|
||||
|
||||
auto messages = listener.stop_recording_messages();
|
||||
std::string result;
|
||||
for (auto& m : messages) {
|
||||
result += m;
|
||||
}
|
||||
|
||||
std::string expected =
|
||||
"0\n"
|
||||
"proc2: 0\n"
|
||||
"TARGET FUNCTION 1 2 3\n"
|
||||
"4 5 6\n"
|
||||
"Stack Alignemnt 0/16\n"
|
||||
"proc1: 0\n"
|
||||
"proc2: 6\n"
|
||||
"proc1: 1\n"
|
||||
"proc2: 12\n"
|
||||
"proc1: 2\n"
|
||||
"proc2: 18\n"
|
||||
"proc1: 3\n"
|
||||
"proc2: 24\n"
|
||||
"proc1: 4\n"
|
||||
"proc2: 30\n"
|
||||
"proc1: 5\n"
|
||||
"DEACTIVATE PROC 1\n"
|
||||
"proc2: 36\n"
|
||||
"proc2: 42\n"
|
||||
"proc2: 48\n"
|
||||
"proc2: 54\n";
|
||||
|
||||
EXPECT_EQ(expected, result);
|
||||
}
|
Loading…
Reference in New Issue
Block a user