This weekend I improved my prototype compiler which compiles JS to x86, using the global property jump optimization. The compiler is rather minimal and handles just enough to compile fib and similar simple code.
This prototype is mainly useful to explore various implementation approaches quickly, without the burden of the complete Tachyon compiler. It allowed me to test the performance of the global property jump optimization I proposed. Two versions were tested:
proto1 = optimization of global property calls keeping
the argument count setup and check in all cases
proto2 = optimization of global property calls without
the argument count setup and check when the number
of parameters matches the number of arguments
The results are very good. On fib(40), proto2 generate faster code than V8 (1.5x) and SpiderMonkey (2.3x), and Tachyon (18x). There is a 1.2x difference between proto1 and proto2 (in other words, there is a 20% overhead to setup and check argument counts). Here are the detailed running times:
relative
time to proto2
Tachyon 29.613 (18.3)
SM 3.700 ( 2.3)
V8 2.397 ( 1.5)
proto1 2.000 ( 1.2)
proto2 1.614 ( 1.0)
Please examine the x86 code generated by proto2. Note that it performs all the expected dynamic type checks, and also the stack overflow checks. The optimization in proto2 will replace the code of the global property jump:
call jump_global_prop_handler # perform jump to global property
.long 3 # fib
.byte 1,0 # number of arguments and filler
with the direct jump:
jmp fib_7
The details are in the attached file. If you have any questions please let me know.
Here's a question for Maxime and Erick... can you estimate the work needed to get Tachyon to generate similar x86 code?
Marc
# notes:
#
# %ecx = context register
# %eax = argument 1 of function and return value of function
# %edi = return address
# %ebp = stack pointer
#
# function fib(x)
# {
# if (x < 2)
# return x;
# else
# return fib(x-1) + fib(x-2);
# }
fib_7: # main entry point of "fib"
subb $1,%cl # check that arg_count = 1
jne _13
_14:
testb $3,%al # check that x is a fixnum
jne _17
cmpl $8,%eax # compare it to 2
jge else_16
then_15:
jmp *%edi # return x (which is already in %eax) when x<2
_13:
addb $1,%cl # handle arg_count != param_count
pushl %ecx
xorb %cl,%cl
call *16(%ecx)
jmp _14
_17:
pushl %eax # handle generic "x < 2"
pushl $8
call *28(%ecx)
jl then_15
jmp else_16
else_16:
movl %eax,-4(%ebp) # save x for later
testb $3,%al # check that x is a fixnum
jne _19
subl $8,%eax # "x - 2"
jo _20 # check for overflow
_21: # no overflow
movl %edi,-8(%ebp) # save return address on stack
addl $-8,%ebp # allocate frame on stack
cmpl %ebp,(%ecx) # check for stack overflow
ja _22
_23: # jump to global property "fib"
movl $_18,%edi # setup return address
movl 4(%ecx),%esi # setup "this" argument (= global object)
call jump_global_prop_handler # perform jump to global property
.long 3 # fib
.byte 1,0 # number of arguments and filler
_20:
addl $8,%eax # undo subtraction that overflowed
_19:
pushl %eax # handle generic "x - 2"
pushl $8
call *28(%ecx)
jmp _21
_22:
call *12(%ecx) # handle stack overflow
jmp _23
ALIGN_RET_POINT
FRAME_DESCR
.long RET_POINT_TYPE
RET_POINT_HEADER_END
_18: # return point of call fib(x-2)
movl 4(%ebp),%ebx # get x
testb $3,%bl # check that x is a fixnum
jne _25
subl $4,%ebx # "x - 1"
jo _26 # check for overflow
_27:
movl %eax,-4(%ebp) # save result of call fib(x-2)
movl %ebx,%eax # prepare argument of call fib(x-1)
addl $-4,%ebp # grow frame on stack
cmpl %ebp,(%ecx) # check for stack overflow
ja _28
_29: # jump to global property "fib"
movl $_24,%edi # setup return address
movl 4(%ecx),%esi # setup "this" argument (= global object)
call jump_global_prop_handler # perform jump to global property
.long 3 # fib
.byte 1,0 # number of arguments and filler
_26:
addl $4,%ebx # undo subtraction that overflowed
_25:
pushl %ebx # handle generic "x - 1"
pushl $4
call *28(%ecx)
jmp _27
_28:
call *12(%ecx) # handle stack overflow
jmp _29
ALIGN_RET_POINT
FRAME_DESCR
.long RET_POINT_TYPE
RET_POINT_HEADER_END
_24: # return point of call fib(x-1)
testb $3,%al # check that result of call fib(x-1) is a fixnum
jne _30
testb $3,(%ebp) # check that result of call fib(x-2) is a fixnum
jne _30
addl (%ebp),%eax # add result of calls fib(x-1) and fib(x-2)
jo _31 # check for overflow
_32:
addl $12,%ebp # remove stack frame
jmp *-8(%ebp) # jump to return address
_31:
subl (%ebp),%eax # undo addition that overflowed
_30:
pushl %eax # handle generic "fib(x-1) + fib(x-2)"
pushl -12(%ebp)
call *28(%ecx)
jmp _32