From df669298ce191a808e2b92876df51ea25d0fd7c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Lindqvist?= Date: Thu, 13 Aug 2015 04:59:14 +0200 Subject: [PATCH] VM: split dispatch_signal_handler into two methods, one for resumable and one for non-resumable signals --- vm/cpu-x86.cpp | 135 ++++++++++++++++++++++++++++++------------------- vm/vm.hpp | 6 +++ 2 files changed, 90 insertions(+), 51 deletions(-) diff --git a/vm/cpu-x86.cpp b/vm/cpu-x86.cpp index 9696b52ebe..e4f5323869 100644 --- a/vm/cpu-x86.cpp +++ b/vm/cpu-x86.cpp @@ -2,60 +2,93 @@ namespace factor { +void factor_vm::dispatch_non_resumable_signal(cell* sp, cell* pc, + cell handler, + cell limit) { + + /* Fault came from the VM or foreign code. We don't try to fix the + call stack from *sp and instead use the last saved "good value" + which we get from ctx->callstack_top. Then launch the handler + without going through the resumable subprimitive. */ + signal_resumable = false; + + cell frame_top = ctx->callstack_top; + + if (frame_top < ctx->callstack_seg->start) { + /* The saved callstack pointer is outside the callstack + segment. That means that we need to carefully cut off one frame + first which hopefully should put the pointer within the + callstack's bounds. */ + code_block *block = code->code_block_for_address(*pc); + cell frame_size = block->stack_frame_size_for_address(*pc); + frame_top += frame_size; + } + + /* Cut the callstack down to the shallowest Factor stack + frame that leaves room for the signal handler to do its thing, + and launch the handler without going through the resumable + subprimitive. */ + FACTOR_ASSERT(ctx->callstack_seg->start <= frame_top); + while (frame_top < ctx->callstack_bottom && frame_top < limit) { + frame_top = code->frame_predecessor(frame_top); + } + ctx->callstack_top = frame_top; + *sp = frame_top; + *pc = handler; +} + +void factor_vm::dispatch_resumable_signal(cell* sp, cell* pc, cell handler) { + + signal_resumable = true; + + /* Fault came from Factor, and we've got a good callstack. Route the + signal handler through the resumable signal handler + subprimitive. */ + + cell offset = *sp % 16; + + signal_handler_addr = 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. */ + /* On architectures with a link register we would have to check for + leafness by matching the PC to a word. We should also use + FRAME_RETURN_ADDRESS instead of assuming the stack pointer is the + right place to put the resume address. */ + cell word_idx = 0; + if (offset == 0) { + word_idx = SIGNAL_HANDLER_WORD; + + cell newsp = *sp - sizeof(cell); + *sp = newsp; + *(cell*)newsp = *pc; + + } else if (offset == 16 - sizeof(cell)) { + word_idx = LEAF_SIGNAL_HANDLER_WORD; + /* Make a fake frame for the leaf procedure */ + FACTOR_ASSERT(code->code_block_for_address(*pc) != NULL); + + cell newsp = *sp - LEAF_FRAME_SIZE; + *sp = newsp; + *(cell*)newsp = *pc; + + } else { + FACTOR_ASSERT(false); + } + tagged handler_word = tagged(special_objects[word_idx]); + *pc = (cell)handler_word->entry_point; +} + void factor_vm::dispatch_signal_handler(cell* sp, cell* pc, cell handler) { - cell callstack_limit = ctx->callstack_seg->start + stack_reserved; - if (!code->seg->in_segment_p(*pc) || *sp < callstack_limit) { - /* Fault came from the VM, foreign code, a callstack overflow, or - we don't have enough callstack room to try the resumable - handler. Cut the callstack down to the shallowest Factor stack - frame that leaves room for the signal handler to do its thing, - and launch the handler without going through the resumable - subprimitive. */ - signal_resumable = false; - - cell frame_top = ctx->callstack_top; - - while (frame_top < ctx->callstack_bottom && frame_top < callstack_limit) { - frame_top = code->frame_predecessor(frame_top); - } - - *sp = frame_top; - ctx->callstack_top = frame_top; - *pc = handler; + bool in_code_seg = code->seg->in_segment_p(*pc); + cell cs_limit = ctx->callstack_seg->start + stack_reserved; + bool resumable_p = in_code_seg && *sp >= cs_limit; + if (resumable_p) { + dispatch_resumable_signal(sp, pc, handler); } else { - signal_resumable = true; - /* Fault came from Factor, and we've got a good callstack. Route - the signal handler through the resumable signal handler subprimitive. */ - cell offset = *sp % 16; - - signal_handler_addr = handler; - tagged handler_word = - tagged(special_objects[SIGNAL_HANDLER_WORD]); - - /* 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. */ - /* On architectures with a link register we would have to check for leafness - by matching the PC to a word. We should also use FRAME_RETURN_ADDRESS - instead of assuming the stack pointer is the right place to put the - resume address. */ - if (offset == 0) { - cell newsp = *sp - sizeof(cell); - *sp = newsp; - *(cell*)newsp = *pc; - } else if (offset == 16 - sizeof(cell)) { - /* Make a fake frame for the leaf procedure */ - FACTOR_ASSERT(code->code_block_for_address(*pc) != NULL); - - cell newsp = *sp - LEAF_FRAME_SIZE; - *(cell*)newsp = *pc; - *sp = newsp; - handler_word = tagged(special_objects[LEAF_SIGNAL_HANDLER_WORD]); - } else - FACTOR_ASSERT(false); - - *pc = (cell)handler_word->entry_point; + dispatch_non_resumable_signal(sp, pc, handler, cs_limit); } /* Poking with the stack pointer, which the above code does, means diff --git a/vm/vm.hpp b/vm/vm.hpp index 0e1a6e404a..218d941705 100644 --- a/vm/vm.hpp +++ b/vm/vm.hpp @@ -641,6 +641,12 @@ struct factor_vm { // cpu-* void dispatch_signal_handler(cell* sp, cell* pc, cell newpc); +#if defined(FACTOR_X86) || defined(FACTOR_64) + void dispatch_non_resumable_signal(cell* sp, cell* pc, + cell handler, + cell limit); + void dispatch_resumable_signal(cell* sp, cell* pc, cell handler); +#endif // alien char* pinned_alien_offset(cell obj);