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