diff --git a/basis/cpu/x86/x86.factor b/basis/cpu/x86/x86.factor index 5ae9e1c489..e12cec9738 100644 --- a/basis/cpu/x86/x86.factor +++ b/basis/cpu/x86/x86.factor @@ -61,8 +61,12 @@ M: x86 stack-frame-size ( stack-frame -- i ) M: x86 %call ( word -- ) 0 CALL rc-relative rel-word-pic ; +: xt-tail-pic-offset ( -- n ) + #! See the comment in vm/cpu-x86.hpp + cell 4 + 1 + ; inline + M: x86 %jump ( word -- ) - pic-tail-reg 0 MOV 2 cells 1 + rc-absolute-cell rel-here + pic-tail-reg 0 MOV xt-tail-pic-offset rc-absolute-cell rel-here 0 JMP rc-relative rel-word-pic-tail ; M: x86 %jump-label ( label -- ) 0 JMP rc-relative label-fixup ; diff --git a/vm/cpu-x86.hpp b/vm/cpu-x86.hpp index 71a85b4e82..e5852f9ad9 100755 --- a/vm/cpu-x86.hpp +++ b/vm/cpu-x86.hpp @@ -7,7 +7,15 @@ namespace factor inline static void flush_icache(cell start, cell len) {} -static const fixnum xt_tail_pic_offset = 2 * sizeof(cell) + 1; +/* In the instruction sequence: + + MOV EBX,... + JMP blah + + the offset from the immediate operand to MOV to the instruction after + the jump is a cell for the immediate operand, 4 bytes for the JMP + destination, and one byte for the JMP opcode. */ +static const fixnum xt_tail_pic_offset = sizeof(cell) + 4 + 1; static const unsigned char call_opcode = 0xe8; static const unsigned char jmp_opcode = 0xe9; diff --git a/vm/quotations.cpp b/vm/quotations.cpp index 32e5e37a79..b049f528e4 100755 --- a/vm/quotations.cpp +++ b/vm/quotations.cpp @@ -152,7 +152,14 @@ void quotation_jit::iterate_quotation() { if(stack_frame) emit(userenv[JIT_EPILOG]); tail_call = true; - /* Inline cache misses are special-cased */ + /* Inline cache misses are special-cased. + The calling convention for tail + calls stores the address of the next + instruction in a register. However, + PIC miss stubs themselves tail-call + the inline cache miss primitive, and + we don't want to clobber the saved + address. */ if(obj.value() == userenv[PIC_MISS_WORD] || obj.value() == userenv[PIC_MISS_TAIL_WORD]) {