<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Parrot - Parrot JIT (i386/gcc)</title> <link rel="stylesheet" type="text/css" href="../../../resources/parrot.css" media="all"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> </head> <body> <div id="wrapper"> <div id="header"> <a href="http://www.parrot.org"> <img border=0 src="../../../resources/parrot_logo.png" id="logo" alt="parrot"> </a> </div> <!-- "header" --> <div id="divider"></div> <div id="mainbody"> <div id="breadcrumb"> <a href="../../../html/index.html">Home</a> » <a href="../../../html/developer.html">Developer Documentation</a> » Parrot JIT (i386/gcc) </div> <h1><a name="NAME" >NAME</a></h1> <p>docs/dev/jit_i386.pod - Parrot JIT (i386/gcc)</p> <h1><a name="ABSTRACT" >ABSTRACT</a></h1> <p>This PDD describes the i386 gcc JIT implementation.</p> <h1><a name="DESCRIPTION" >DESCRIPTION</a></h1> <p>JIT i386/gcc is a combination of unrolled assembly instructions and the Computed Goto Predereferenced (CGP) run loop. For branch instructions the function implementation in the standard core is called.</p> <p>Another difference of JIT/i386 is that most vtable functions are JITed instructions which use register mappings.</p> <p>For a better understanding of the control flow between these basically 3 run loop cores, an example shows the gory details.</p> <h1><a name="EXAMPLE" >EXAMPLE</a></h1> <p>Given the following PASM program, the righthand three columns show where each opcode gets executed:</p> <pre> PASM JIT ops Normal CGP ops</pre> <pre> set I0, 10 # set_i_ic print I0 # (call) print_i print "\n" # print_sc new P1, 'ResizableIntegerArray' local_branch P0, inc # (call) local_branch_p_ic cpu_ret end # (jmp) HALT end (ret) # end (ret) inc: inc I0 # inc_i new P0, 'String' # new_p_sc set P0, I0 # set_p_i print P0 # (call) print_p print "\n" # print_sc local_return P1 # (call) local_return_p cpu_ret </pre> <h2><a name="Startup_sequence" >Startup sequence</a></h2> <p>In <b>runops_jit</b> a prederefed copy of the opcode stream is built by <b>init_prederef</b>. Then <b>build_asm</b> generates the assembler code sequence as usual. This generated code (shown as <b>runops_jit</b> in <b>ddd</b>) is then executed.</p> <p>Generate minimal stack frame, save %ebx</p> <pre> 0x812c510 <jit_func>: push %ebp 0x812c511 <jit_func+1>: mov %esp,%ebp 0x812c513 <jit_func+3>: push %ebx</pre> <p>Get the program counter to %ebx</p> <pre> 0x812c514 <jit_func+4>: mov 0xc(%ebp),%ebx</pre> <p>Push <b>interpreter</b> and <b>(opcode_t*) 1</b> and call <b>cgp_core</b></p> <pre> 0x812c517 <jit_func+7>: push $0x8113db8 0x812c51c <jit_func+12>: push $0x1 0x812c521 <jit_func+17>: mov $0x1,%eax 0x812c526 <jit_func+22>: call 0x80b5830 <cgp_core></pre> <p>In <b>cgp_core</b> all callee saved registers are saved.</p> <pre> 0x80b5830 <cgp_core>: push %ebp 0x80b5831 <cgp_core+1>: mov %esp,%ebp 0x80b5833 <cgp_core+3>: sub $0xdc,%esp 0x80b5839 <cgp_core+9>: lea 0x8(%ebp),%eax 0x80b583c <cgp_core+12>: push %edi 0x80b583d <cgp_core+13>: push %esi 0x80b583e <cgp_core+14>: push %ebx</pre> <p>In <b>%eax</b> the init flag is set to <b>-1</b></p> <pre> 0x80b583f <cgp_core+15>: mov %eax,0xfffffff</pre> <p>The parameter <b>*cur_op</b> (the program counter) is put into <b>%esi</b> and ...</p> <pre> 0x80b5842 <cgp_core+18>: mov 0x8(%ebp),%esi 0x80b5845 <cgp_core+21>: test %esi,%esi 0x80b5847 <cgp_core+23>: jne 0x80b5853 <cgp_core+35> 0x80b5849 <cgp_core+25>: mov $0x810ca60,%eax 0x80b584e <cgp_core+30>: jmp 0x80bb470 <cgp_core+23616></pre> <p>... compared to <b>1</b></p> <pre> 0x80b5853 <cgp_core+35>: cmp $0x1,%esi 0x80b5856 <cgp_core+38>: jne 0x80b5860 <cgp_core+48></pre> <p>If true, the program jumps to the return address of above function call, i.e. it jumps back again to JIT code.</p> <pre> 0x80b5858 <cgp_core+40>: jmp *0x4(%ebp)</pre> <p>Back again in JIT code, the init flag is checked</p> <pre> 0x812c52b <jit_func+27>: test %eax,%eax 0x812c52d <jit_func+29>: jne 0x812c536 <jit_func+38></pre> <p>... and if zero, the function would be left.</p> <pre> [ 0x812c52f <jit_func+31>: pop %ebx ] [ 0x812c531 <jit_func+33>: mov %ebp,%esp ] [ 0x812c533 <jit_func+35>: pop %ebp ] [ 0x812c535 <jit_func+37>: ret ]</pre> <p>When coming from the init sequence, program flow continues by checking the <b>resume_offset</b> and jumping to the desired instruction</p> <pre> 0x812c536 <jit_func+38>: mov %ebx,%eax 0x812c538 <jit_func+40>: sub $0x400140c0,%eax 0x812c53e <jit_func+46>: mov $0x812c4a8,%edx 0x812c543 <jit_func+51>: jmp *(%edx,%eax,1)</pre> <p><b>set I0, 10</b> and save_registers</p> <pre> 0x812c546 <jit_func+54>: mov $0xa,%ebx 0x812c54b <jit_func+59>: mov %ebx,0x8113db8</pre> <p>Now non-JITed code follows -- get the address from the prederefed op_func_table and call it:</p> <pre> 0x812c551 <jit_func+65>: mov $0x812ac0c,%esi 0x812c556 <jit_func+70>: call *(%esi) inline op print(in INT) { printf(INTVAL_FMT, (INTVAL)$1); goto NEXT(); }</pre> <p>where the <b>goto NEXT()</b> is a simple:</p> <pre> 0x80b5b49 <cgp_core+793>: jmp *(%esi) op print(in STR) { ... goto NEXT(); }</pre> <p>As the last instruction of the non-JITed code sequence is a branch, this is not executed in CGP, but the opcode:</p> <pre> inline op cpu_ret() { #ifdef __GNUC__ # ifdef I386 asm("ret")</pre> <p>is executed. This opcode is patched into the prederefed code stream by Parrot_jit_normal_op at the end of a non-JITed code sequence. This returns to JIT code again, where the next instruction gets called as a function in the standard core ...</p> <pre> 0x812c558 <jit_func+72>: push $0x8113db8 0x812c55d <jit_func+77>: push $0x400140dc 0x812c562 <jit_func+82>: call 0x805be60 <Parrot_bsr_ic> 0x812c567 <jit_func+87>: add $0x8,%esp</pre> <p>... and from the return result in <b>%eax</b>, the new code position in JIT is calculated and gets jumped to:</p> <pre> 0x812c56a <jit_func+90>: sub $0x400140c0,%eax 0x812c570 <jit_func+96>: mov $0x812c4a8,%edx 0x812c575 <jit_func+101>: jmp *(%edx,%eax,1)</pre> <p>Now in the subroutine <b>inc</b>:</p> <pre> 0x812c580 <jit_func+112>: mov 0x8113db8,%ebx 0x812c586 <jit_func+118>: inc %ebx</pre> <p>Save register and arguments and call <b>pmc_new_noinit</b>:</p> <pre> 0x812c587 <jit_func+119>: push %edx 0x812c588 <jit_func+120>: push $0x11 0x812c58d <jit_func+125>: push $0x8113db8 0x812c592 <jit_func+130>: call 0x806fc60 <pmc_new_noinit></pre> <p>put the PMC* into Parrot's register:</p> <pre> 0x812c597 <jit_func+135>: mov %eax,0x8113fb8</pre> <p>and prepare arguments for a VTABLE call:</p> <pre> 0x812c59d <jit_func+141>: push %eax 0x812c59e <jit_func+142>: push $0x8113db8 0x812c5a3 <jit_func+147>: mov 0x10(%eax),%eax 0x812c5a6 <jit_func+150>: call *0x18(%eax) 0x812c5a9 <jit_func+153>: add $0x10,%esp 0x812c5ac <jit_func+156>: pop %edx</pre> <p>and another one:</p> <pre> 0x812c5ae <jit_func+158>: push %edx</pre> <p>Here, with the mapped register in <b>%ebx</b>, push <b>I0</b>, the PMC and the interpreter:</p> <pre> 0x812c5af <jit_func+159>: push %ebx 0x812c5b0 <jit_func+160>: mov 0x8113fb8,%eax 0x812c5b6 <jit_func+166>: push %eax 0x812c5b7 <jit_func+167>: push $0x8113db8</pre> <p>and call the vtable:</p> <pre> 0x812c5bc <jit_func+172>: mov 0x10(%eax),%eax 0x812c5bf <jit_func+175>: call *0xdc(%eax) 0x812c5c5 <jit_func+181>: add $0xc,%esp 0x812c5c8 <jit_func+184>: pop %edx</pre> <p>As this ends the JITed section, used registers are saved back to Parrot's register:</p> <pre> 0x812c5ca <jit_func+186>: mov %ebx,0x8113db8</pre> <p>and again the code in <b>cgp_core</b> gets called:</p> <pre> 0x812c5d0 <jit_func+192>: mov $0x812ac48,%esi 0x812c5d5 <jit_func+197>: call *(%esi)</pre> <p>which after executing the <b>print</b> returns back here in JIT, where the <b>ret</b> is called:</p> <pre> 0x812c5d7 <jit_func+199>: push $0x8113db8 0x812c5dc <jit_func+204>: push $0x40014118 0x812c5e1 <jit_func+209>: call 0x805d5e0 <Parrot_ret> 0x812c5e6 <jit_func+214>: add $0x8,%esp</pre> <p>From the returned PC a JIT address is calculated, which gets executed:</p> <pre> 0x812c5e9 <jit_func+217>: sub $0x400140c0,%eax 0x812c5ef <jit_func+223>: mov $0x812c4a8,%edx 0x812c5f4 <jit_func+228>: jmp *(%edx,%eax,1)</pre> <p>Now at the <b>end</b> opcode, the CGP code for HALT() gets jumped to:</p> <pre> 0x812c578 <jit_func+104>: mov $0x80b5877,%esi 0x812c57d <jit_func+109>: jmp *%esi</pre> <p>which is:</p> <pre> inline op end() { HALT(); }</pre> <p>or, set return result:</p> <pre> 0x80b8b6f <cgp_core+13119>: xor %eax,%eax ...</pre> <p>and clean up stack frame and ret:</p> <pre> 0x80bb470 <cgp_core+23616>: lea 0xffffff18(%ebp),%esp 0x80bb476 <cgp_core+23622>: pop %ebx 0x80bb477 <cgp_core+23623>: pop %esi 0x80bb478 <cgp_core+23624>: pop %edi 0x80bb479 <cgp_core+23625>: mov %ebp,%esp 0x80bb47b <cgp_core+23627>: pop %ebp 0x80bb47c <cgp_core+23628>: ret</pre> <p>This returns after the position where <b>cgp_core</b> was called during the init sequence, but now the return value <b>%eax</b> is zero and the..</p> <pre> 0x812c52b <jit_func+27>: test %eax,%eax 0x812c52d <jit_func+29>: jne 0x812c536 <jit_func+38> 0x812c52f <jit_func+31>: pop %ebx 0x812c531 <jit_func+33>: mov %ebp,%esp 0x812c533 <jit_func+35>: pop %ebp 0x812c535 <jit_func+37>: ret</pre> <p>... whole story ends here, we are back again in <b>runops_jit</b>.</p> <p>So this is rather simple once it gets going.</p> <h1><a name="BUGS" >BUGS</a></h1> <p>The floating point registers do not get saved to Parrot before vtable calls. This assumes that external routines preserve the FP stack pointer and don't use more the 4 floating point registers at once.</p> <h1><a name="AUTHOR" >AUTHOR</a></h1> <p>Leopold Toetsch <code>lt@toetsch.at</code></p> <h1><a name="VERSION" >VERSION</a></h1> <h2><a name="CURRENT" >CURRENT</a></h2> <p>14.02.2003 by Leopold Toetsch</p> </div> <!-- "mainbody" --> <div id="divider"></div> <div id="footer"> Copyright © 2002-2009, Parrot Foundation. </div> </div> <!-- "wrapper" --> </body> </html>