We also need to save C ABI volatile registers before calling the signal handler in order to be able to reliably resume. Add signal-handler and leaf-signal-handler subprimitives to preserve volatile registers before invoking the signal handler C function.
CONSTANT: data-base 1024
-CONSTANT: special-objects-size 70
+CONSTANT: special-objects-size 80
CONSTANT: header-size 10
SPECIAL-OBJECT: unwind-native-frames-word 44
SPECIAL-OBJECT: fpu-state-word 45
SPECIAL-OBJECT: set-fpu-state-word 46
+SPECIAL-OBJECT: signal-handler-word 47
+SPECIAL-OBJECT: leaf-signal-handler-word 48
-SPECIAL-OBJECT: callback-stub 48
+SPECIAL-OBJECT: callback-stub 50
! PIC stubs
-SPECIAL-OBJECT: pic-load 49
-SPECIAL-OBJECT: pic-tag 50
-SPECIAL-OBJECT: pic-tuple 51
-SPECIAL-OBJECT: pic-check-tag 52
-SPECIAL-OBJECT: pic-check-tuple 53
-SPECIAL-OBJECT: pic-hit 54
-SPECIAL-OBJECT: pic-miss-word 55
-SPECIAL-OBJECT: pic-miss-tail-word 56
+SPECIAL-OBJECT: pic-load 51
+SPECIAL-OBJECT: pic-tag 52
+SPECIAL-OBJECT: pic-tuple 53
+SPECIAL-OBJECT: pic-check-tag 54
+SPECIAL-OBJECT: pic-check-tuple 55
+SPECIAL-OBJECT: pic-hit 56
+SPECIAL-OBJECT: pic-miss-word 57
+SPECIAL-OBJECT: pic-miss-tail-word 58
! Megamorphic dispatch
-SPECIAL-OBJECT: mega-lookup 57
-SPECIAL-OBJECT: mega-lookup-word 58
-SPECIAL-OBJECT: mega-miss-word 59
+SPECIAL-OBJECT: mega-lookup 59
+SPECIAL-OBJECT: mega-lookup-word 60
+SPECIAL-OBJECT: mega-miss-word 61
! Default definition for undefined words
-SPECIAL-OBJECT: undefined-quot 60
+SPECIAL-OBJECT: undefined-quot 62
: special-object-offset ( symbol -- n )
special-objects get at header-size + ;
\ unwind-native-frames unwind-native-frames-word set
\ fpu-state fpu-state-word set
\ set-fpu-state set-fpu-state-word set
+ \ signal-handler signal-handler-word set
+ \ leaf-signal-handler leaf-signal-handler-word set
undefined-def undefined-quot set ;
: emit-special-objects ( -- )
[
! load entry point
RAX 0 MOV rc-absolute-cell rel-this
- ! store entry point
- RSP bootstrap-cell 2 * neg [+] RAX MOV
- ! store stack frame size
- RSP bootstrap-cell neg [+] stack-frame-size MOV
! alignment
RSP stack-frame-size bootstrap-cell - SUB
+ ! store entry point
+ RSP stack-frame-size bootstrap-cell 3 * - [+] RAX MOV
+ ! store stack frame size
+ RSP stack-frame-size bootstrap-cell 2 * - [+] stack-frame-size MOV
] jit-prolog jit-define
[
"end_callback" jit-call
] \ c-to-factor define-sub-primitive
+USE: locals
+
+: jit-save-volatile-regs ( -- )
+ ! do we also need to save XMM?
+ RSP volatile-regs length bootstrap-cell * SUB
+ volatile-regs
+ [| r i | RSP i bootstrap-cell * [+] r MOV ] each-index ;
+
+:: jit-restore-volatile-regs ( additional-pop -- )
+ volatile-regs
+ [| r i | r RSP i bootstrap-cell * [+] MOV ] each-index
+ RSP volatile-regs length bootstrap-cell * additional-pop + ADD ;
+
+[
+ ! Stack at this point has the signal handler pointer followed by
+ ! the return address back into normal execution, then the 24 bytes
+ ! of stack frame + alignment inserted by the prolog.
+ ! After registers are saved, the stack looks like:
+ ! RSP saved volatile regs (`volatile-regs length` cells)
+ ! + subprimitive stack frame alignment (3 cells)
+ ! . signal handler address (1 cell)
+ ! . resume address (1 cell)
+ jit-save-volatile-regs
+ jit-save-context
+ RAX RSP volatile-regs length 3 + bootstrap-cell * [+] MOV
+ RAX CALL
+ bootstrap-cell jit-restore-volatile-regs
+] \ signal-handler define-sub-primitive
+
+! :: jit-push-leaf-stack-frame ( -- )
+! ;
+!
+! :: jit-pop-leaf-stack-frame ( -- )
+! ;
+!
+! [
+! ! Stack at this point has the signal handler pointer followed by
+! ! the word pointer and the return address back into normal execution,
+! ! then the 24 bytes of stack frame + alignment inserted by the prolog
+! ! After registers are saved and the leaf stack frame is constructed,
+! ! the stack looks like:
+! ! RSP fake leaf stack frame (4 cells)
+! ! + saved volatile regs (`volatile-regs length` cells)
+! ! . subprimitive stack frame alignment (3 cells)
+! ! . leaf word (1 cell)
+! ! . signal handler address (1 cell)
+! ! resume address (1 cell)
+! jit-save-volatile-regs
+! jit-push-leaf-stack-frame
+! jit-save-context
+! "memory_signal_handler_impl" jit-call
+! jit-pop-leaf-stack-frame
+! bootstrap-cell jit-restore-volatile-regs
+! ] \ leaf-signal-handler define-sub-primitive
+
[
arg1 ds-reg [] MOV
ds-reg bootstrap-cell SUB
: stack-frame-size ( -- n ) 4 bootstrap-cells ;
: nv-regs ( -- seq ) { RBX R12 R13 R14 R15 } ;
+: volatile-regs ( -- seq ) { RAX RCX RDX RSI RDI R8 R9 R10 R11 } ;
: arg1 ( -- reg ) RDI ;
: arg2 ( -- reg ) RSI ;
: arg3 ( -- reg ) RDX ;
: stack-frame-size ( -- n ) 8 bootstrap-cells ;
: nv-regs ( -- seq ) { RBX RSI RDI R12 R13 R14 R15 } ;
+: volatile-regs ( -- seq ) { RAX RCX RDX R8 R9 R10 R11 } ;
: arg1 ( -- reg ) RCX ;
: arg2 ( -- reg ) RDX ;
: arg3 ( -- reg ) R8 ;
: HWNT ( -- ) HEX: 2e , ; ! Hint branch Weakly Not Taken
: HST ( -- ) HEX: 3e , ; ! Hint branch Strongly Taken
+
+! interrupt instructions
+
+: INT ( n -- ) dup 3 = [ drop HEX: cc , ] [ HEX: cd , 1, ] if ;
sleep-entry ;
: self ( -- thread )
- 63 special-object { thread } declare ; inline
+ 65 special-object { thread } declare ; inline
: thread-continuation ( thread -- continuation )
context>> check-box value>> continuation-for ;
[ tnamespace ] dip change-at ; inline
: threads ( -- assoc )
- 64 special-object { hashtable } declare ; inline
+ 66 special-object { hashtable } declare ; inline
: thread-registered? ( thread -- ? )
id>> threads key? ;
: unregister-thread ( thread -- )
id>> threads delete-at ;
-: set-self ( thread -- ) 63 set-special-object ; inline
+: set-self ( thread -- ) 65 set-special-object ; inline
PRIVATE>
: run-queue ( -- dlist )
- 65 special-object { dlist } declare ; inline
+ 67 special-object { dlist } declare ; inline
: sleep-queue ( -- heap )
- 66 special-object { min-heap } declare ; inline
+ 68 special-object { min-heap } declare ; inline
: waiting-callbacks ( -- assoc )
- 68 special-object { hashtable } declare ; inline
+ 70 special-object { hashtable } declare ; inline
: new-thread ( quot name class -- thread )
new
<PRIVATE
: init-thread-state ( -- )
- H{ } clone 64 set-special-object
- <dlist> 65 set-special-object
- <min-heap> 66 set-special-object
- H{ } clone 68 set-special-object ;
+ H{ } clone 66 set-special-object
+ <dlist> 67 set-special-object
+ <min-heap> 68 set-special-object
+ H{ } clone 70 set-special-object ;
: init-initial-thread ( -- )
[ ] "Initial" <thread>
{ nursery zone }
{ cards-offset cell }
{ decks-offset cell }
-{ special-objects cell[70] } ;
+{ special-objects cell[80] } ;
: vm-field-offset ( field -- offset ) vm offset-of ; inline
[
8 special-object utf8 alien>string string>cpu \ cpu set-global
9 special-object utf8 alien>string string>os \ os set-global
- 67 special-object utf8 alien>string \ vm-compiler set-global
+ 69 special-object utf8 alien>string \ vm-compiler set-global
] "alien.strings" add-startup-hook
{ "(call)" "kernel.private" ( quot -- ) }
{ "fpu-state" "kernel.private" ( -- ) }
{ "set-fpu-state" "kernel.private" ( -- ) }
+ { "signal-handler" "kernel.private" ( -- ) }
+ { "leaf-signal-handler" "kernel.private" ( -- ) }
{ "unwind-native-frames" "kernel.private" ( -- ) }
{ "set-callstack" "kernel.private" ( callstack -- * ) }
{ "lazy-jit-compile" "kernel.private" ( -- ) }
! Incremented each time stack effects potentially changed, used
! by compiler.tree.propagation.call-effect for call( and execute(
! inline caching
-: effect-counter ( -- n ) 47 special-object ; inline
+: effect-counter ( -- n ) 49 special-object ; inline
GENERIC: always-bump-effect-counter? ( defspec -- ? )
: bump-effect-counter ( -- )
bump-effect-counter? [
- 47 special-object 0 or
+ 49 special-object 0 or
1 +
- 47 set-special-object
+ 49 set-special-object
] when ;
: notify-observers ( -- )
catchstack* [
in-callback?
[ callback-error-hook get-global call( error -- * ) ]
- [ 63 special-object error-in-thread ]
+ [ 65 special-object error-in-thread ]
if
] [ pop continue-with ] if-empty ;
init-catchstack
! VM calls on error
[
- ! 63 = self
- 63 special-object error-thread set-global
+ ! 65 = self
+ 65 special-object error-thread set-global
continuation error-continuation set-global
[ original-error set-global ] [ rethrow ] bi
] 5 set-special-object
: stdin-handle ( -- alien ) 11 special-object ;
: stdout-handle ( -- alien ) 12 special-object ;
-: stderr-handle ( -- alien ) 61 special-object ;
+: stderr-handle ( -- alien ) 63 special-object ;
: init-c-stdio ( -- )
stdin-handle <c-reader>
return stack;
}
-void factor_vm::dispatch_signal_handler(cell *sp, cell *pc, cell newpc)
+void factor_vm::dispatch_signal_handler(cell *sp, cell *pc, cell handler)
{
/* True stack frames are always 16-byte aligned. Leaf procedures
that don't create a stack frame will be out of alignment by sizeof(cell)
bytes. */
+ /* XXX horribly x86-centric */
+
cell offset = *sp % 16;
- if (offset == 0) {
+
+ tagged<word> handler_word = tagged<word>(special_objects[SIGNAL_HANDLER_WORD]);
+ if (offset == 0)
+ {
signal_from_leaf = false;
- cell newsp = *sp - sizeof(cell);
- *sp = newsp;
- *(cell*)newsp = *pc;
- *pc = newpc;
- ctx->callstack_top = (stack_frame*)newsp;
- } else if (offset == 16 - sizeof(cell)) {
- dispatch_signal_handler_from_leaf(sp, pc, newpc);
- } else {
+ }
+ else if (offset == 16 - sizeof(cell))
+ {
+ signal_from_leaf = true;
+ handler_word = tagged<word>(special_objects[LEAF_SIGNAL_HANDLER_WORD]);
+ }
+ else
+ {
fatal_error("Invalid stack frame during signal handler", *sp);
}
-}
-void factor_vm::dispatch_signal_handler_from_leaf(cell *sp, cell *pc, cell newpc)
-{
- /* We should try to conjure a stack frame here, but we may need to deal
- with callstack overflows or the GC moving code around.
- For now leave the stack untouched so the signal handler returns into
- the parent procedure. This will cause things to blow up if the stack
- is left unbalanced. */
- signal_from_leaf = true;
- *pc = newpc;
+ /* Push the original PC as a return address and the C handler function
+ * pointer as an argument to the signal handler stub. */
+ cell newsp = *sp - 2*sizeof(cell);
+ *sp = newsp;
+ *(cell*)(newsp + sizeof(cell)) = *pc;
+ *(cell*)newsp = handler;
+ *pc = (cell)handler_word->code->entry_point();
}
/* We ignore the two topmost frames, the 'callstack' primitive
namespace factor
{
-static const cell special_object_count = 70;
+static const cell special_object_count = 80;
enum special_object {
OBJ_WALKER_HOOK = 3, /* non-local exit hook, used by library only */
UNWIND_NATIVE_FRAMES_WORD,
GET_FPU_STATE_WORD,
SET_FPU_STATE_WORD,
+ SIGNAL_HANDLER_WORD,
+ LEAF_SIGNAL_HANDLER_WORD,
/* Incremented on every modify-code-heap call; invalidates call( inline
caching */
- REDEFINITION_COUNTER = 47,
+ REDEFINITION_COUNTER = 49,
/* Callback stub generation in callbacks.c */
- CALLBACK_STUB = 48,
+ CALLBACK_STUB = 50,
/* Polymorphic inline cache generation in inline_cache.c */
- PIC_LOAD = 49,
+ PIC_LOAD = 51,
PIC_TAG,
PIC_TUPLE,
PIC_CHECK_TAG,
PIC_MISS_TAIL_WORD,
/* Megamorphic cache generation in dispatch.c */
- MEGA_LOOKUP = 57,
+ MEGA_LOOKUP = 59,
MEGA_LOOKUP_WORD,
MEGA_MISS_WORD,
- OBJ_UNDEFINED = 60, /* default quotation for undefined words */
+ OBJ_UNDEFINED = 62, /* default quotation for undefined words */
- OBJ_STDERR = 61, /* stderr FILE* handle */
+ OBJ_STDERR = 63, /* stderr FILE* handle */
- OBJ_STAGE2 = 62, /* have we bootstrapped? */
+ OBJ_STAGE2 = 64, /* have we bootstrapped? */
- OBJ_CURRENT_THREAD = 63,
+ OBJ_CURRENT_THREAD = 65,
- OBJ_THREADS = 64,
- OBJ_RUN_QUEUE = 65,
- OBJ_SLEEP_QUEUE = 66,
+ OBJ_THREADS = 66,
+ OBJ_RUN_QUEUE = 67,
+ OBJ_SLEEP_QUEUE = 68,
- OBJ_VM_COMPILER = 67, /* version string of the compiler we were built with */
+ OBJ_VM_COMPILER = 69, /* version string of the compiler we were built with */
- OBJ_WAITING_CALLBACKS = 68,
+ OBJ_WAITING_CALLBACKS = 70,
};
/* save-image-and-exit discards special objects that are filled in on startup