: callstack-top-offset ( -- n ) 2 \ callstack type-number slot-offset ; inline
: vm-context-offset ( -- n ) 0 bootstrap-cells ; inline
: vm-spare-context-offset ( -- n ) 1 bootstrap-cells ; inline
+: vm-signal-handler-addr-offset ( -- n ) 8 bootstrap-cells ; inline
: context-callstack-top-offset ( -- n ) 0 bootstrap-cells ; inline
: context-callstack-bottom-offset ( -- n ) 1 bootstrap-cells ; inline
: context-datastack-offset ( -- n ) 2 bootstrap-cells ; inline
USE: locals
-: jit-save-volatile-regs ( -- )
+:: jit-save-volatile-regs ( -- save-size )
! do we also need to save XMM?
- RSP volatile-regs length bootstrap-cell * SUB
+ volatile-regs length bootstrap-cell * 16 align :> save-size
+ RSP 2 bootstrap-cells [+] save-size ADD ! bump up stack frame size
+ RSP save-size SUB
volatile-regs
- [| r i | RSP i bootstrap-cell * [+] r MOV ] each-index ;
+ [| r i | RSP i bootstrap-cell * [+] r MOV ] each-index
+ save-size ;
-:: jit-restore-volatile-regs ( additional-pop -- )
+:: jit-restore-volatile-regs ( save-size -- )
volatile-regs
[| r i | r RSP i bootstrap-cell * [+] MOV ] each-index
- RSP volatile-regs length bootstrap-cell * additional-pop + ADD ;
+ RSP save-size 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-volatile-regs :> save-size
jit-save-context
- RAX RSP volatile-regs length 3 + bootstrap-cell * [+] MOV
+ RAX vm-reg vm-signal-handler-addr-offset [+] MOV
RAX CALL
- bootstrap-cell jit-restore-volatile-regs
+ save-size 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
+[| |
+ jit-save-volatile-regs :> save-size
+ jit-save-context
+ RAX vm-reg vm-signal-handler-addr-offset [+] MOV
+ RAX CALL
+ ! Stack at this point has a fake stack frame set up to represent the
+ ! leaf procedure we interrupted. We must tear down that frame in
+ ! addition to our own before resuming.
+ ! Grab our resume address and place it just underneath the leaf proc's
+ ! return address, since we can't touch any registers once they've been
+ ! restored. If we got this far there should be no faults here and we
+ ! can get away with corrupting the stack frame.
+ RAX RSP save-size 3 bootstrap-cells + [+] MOV
+ RSP save-size 6 bootstrap-cells + [+] RAX MOV
+
+ ! Popping 3 extra cells here plus the 3 cells the epilogue pops leaves
+ ! the resume address at the top of the stack for when the epilogue RETs.
+ save-size 3 bootstrap-cells + jit-restore-volatile-regs
+] \ leaf-signal-handler define-sub-primitive
[
arg1 ds-reg [] MOV
{ nursery zone }
{ cards-offset cell }
{ decks-offset cell }
+{ signal-handler-addr cell }
{ special-objects cell[80] } ;
: vm-field-offset ( field -- offset ) vm offset-of ; inline
return stack;
}
+struct word_finder {
+ cell address;
+ cell found_word;
+
+ word_finder(cell address) : address(address), found_word(0) {}
+
+ // XXX keep a map of word names in the code heap so we don't need this
+ void operator()(object *obj)
+ {
+ if (obj->type() == WORD_TYPE)
+ {
+ word *w = static_cast<word*>(obj);
+ if ((cell)w->code->entry_point() <= address
+ && address - (cell)w->code->entry_point() < w->code->size()) {
+ assert(found_word == 0);
+ found_word = (cell)w->code->entry_point();
+ }
+ }
+ }
+};
+
+static cell find_word_for_address(factor_vm *vm, cell pc)
+{
+ word_finder finder(pc);
+ vm->each_object(finder);
+ assert(finder.found_word != 0);
+ return finder.found_word;
+}
+
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 */
+ /* XXX check if exception came from C code */
+ /* XXX handle callstack overflow */
cell offset = *sp % 16;
+ signal_handler_addr = handler;
tagged<word> handler_word = tagged<word>(special_objects[SIGNAL_HANDLER_WORD]);
if (offset == 0)
{
+ // should use FRAME_RETURN_ADDRESS here to be platform-agnostic
signal_from_leaf = false;
+ cell newsp = *sp - sizeof(cell);
+ *sp = newsp;
+ *(cell*)newsp = *pc;
}
+ // should check the PC since leaf procs on RISC architectures won't touch the
+ // stack at all
else if (offset == 16 - sizeof(cell))
{
signal_from_leaf = true;
+
+ // Make a fake frame for the leaf procedure
+ cell leaf_word = find_word_for_address(this, *pc);
+
+ cell newsp = *sp + 4 * sizeof(cell); // XXX platform-appropriate stack size
+ *(cell*)(newsp + 3*sizeof(cell)) = 4*sizeof(cell);
+ *(cell*)(newsp + 2*sizeof(cell)) = leaf_word;
+ *(cell*) newsp = *pc;
+ *sp = newsp;
handler_word = tagged<word>(special_objects[LEAF_SIGNAL_HANDLER_WORD]);
}
else
fatal_error("Invalid stack frame during signal handler", *sp);
}
- /* 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();
}
struct factor_vm
{
- // First 5 fields accessed directly by compiler. See basis/vm/vm.factor
+ //
+ // vvvvvv
+ // THESE FIELDS ARE ACCESSED DIRECTLY FROM FACTOR. See:
+ // basis/vm/vm.factor
+ // basis/compiler/constants/constants.factor
/* Current context */
context *ctx;
cell cards_offset;
cell decks_offset;
+ /* cdecl signal handler address, used by signal handler subprimitives */
+ cell signal_handler_addr;
+
/* Various special objects, accessed by special-object and
set-special-object primitives */
cell special_objects[special_object_count];
+ // THESE FIELDS ARE ACCESSED DIRECTLY FROM FACTOR.
+ // ^^^^^^
+ //
+
/* Data stack and retain stack sizes */
cell datastack_size, retainstack_size, callstack_size;