📄 ch18_02.htm
字号:
<p>During this pass, the parse tree is built up by the <em class="emphasis">yacc</em>(1) parserusing the tokens it's fed from the underlying lexer (which could beconsidered another logical pass in its own right). Bottom-up justmeans that the parser knows about the leaves of the tree before itknows about its branches and root. It really does figure things outfrom bottom to top in <a href="ch18_02.htm#perl3-opcode-order">Figure 18-2</a>, since we drew the root at thetop, in the idiosyncratic fashion of computer scientists. (And linguists.)</p><p>As each opcode node is constructed, per-opcode sanity checks verifycorrect semantics, such as the correct number and types of arguments usedto call built-in functions. As each subsection of the tree takesshape, the optimizer considers what transformations it can apply to theentire subtree now beneath it. For instance, once it knows that a listof values is being fed to a function that takes a specific number ofarguments, it can throw away the opcode that records the number ofarguments for functions that take a varying number of arguments. Amore important optimization, known as <em class="emphasis">constant folding</em>, is described later in this section.</p><p>This pass also constructs the node visitation order used laterfor execution, which is a really neat trick because the first place tovisit is almost never the top node. The compiler makesa temporary loop of opcodes, with the top node pointing to the first opcode to visit. When the top-level opcode is incorporated into something bigger, that loop of opcodes is broken, only to make a bigger loop with the newtop node. Eventually the loop is broken for good when the start opcodegets poked into some other structure such as a subroutine descriptor.The subroutine caller can still find that first opcode despite itsbeing way down at the bottom of the tree, as it is in<a href="ch18_02.htm#perl3-opcode-order">Figure 18-2</a>. There's no need for the interpreter to recurse back down theparse tree to figure out where to start.</p></dd><dt><b>Pass 2: Top-Down Optimizer</b></dt><dd><p>A person reading a snippet of Perl code (or of English code,for that matter) cannot determine the context without examining thesurrounding lexical elements. Sometimes you can't decide what's really going on until you have more information. Don't feelbad, though, because you're not alone: neither can the compiler. In thispass, the compiler descends back down the subtree it's just built toapply local optimizations, the most notable of which is <em class="emphasis">context propagation.</em> The compiler marks subjacent nodes with the appropriate contexts (void, scalar, list, reference, or lvalue) imposed by the current node. Unwanted opcodes are nulled out but not deleted, because it's now too late to reconstruct the execution order. We'll rely on the third pass to remove them from the provisional execution order determined by the first pass.</p></dd><dt><b>Pass 3: Peephole Optimizer</b></dt><dd><p>Certain units of code have their own storage space in which they keeplexically scoped variables. (Such a space is called a <em class="emphasis">scratchpad</em> inPerl lingo.) These units include <tt class="literal">eval</tt><em class="replaceable">STRING</em>s, subroutines,and entire files. More importantly from the standpoint of theoptimizer, they each have their own entry point, which means that whilewe know the execution order from here on, we can't know what happenedbefore, because the construct could have been called from anywhere. Sowhen one of these units is done being parsed, Perl runs a peephole optimizer onthat code. Unlike the previous two passes, which walked the branchstructure of the parse tree, this pass traverses the code in linearexecution order, since this is basically the last opportunity to do sobefore we cut the opcode list off from the parser. Most optimizationswere already performed in the first two passes, but some can't be.</p><p>Assorted late-term optimizations happen here, including stitchingtogether the final execution order by skipping over nulled out opcodes,and recognizing when various opcode juxtapositions can be reduced tosomething simpler. The recognition of chained string concatenations is one important optimization, since you'd really like to avoid copyinga string back and forth each time you add a little bit to the end. Thispass doesn't just optimize; it also does a great deal of"real" work: trapping barewords, generating warnings on questionableconstructs, checking for code unlikely to be reached, resolvingpseudohash keys, and looking for subroutines called before their prototypeshad been compiled.</p></dd><dt><b>Pass 4: Code Generation</b></dt><dd><p>This pass is optional; it isn't used in the normal scheme of things.But if any of the three codegenerators--<tt class="literal">B::Bytecode</tt>,<tt class="literal">B::C</tt>, and <tt class="literal">B::CC</tt>--areinvoked, the parse tree is accessed one final time. The codegenerators emit either serialized Perl bytecodes used to reconstructthe parse tree later or literal C code representing the state ofthe compile-time parse tree.</p><p>Generation of C code comes in two different flavors.<tt class="literal">B::C</tt> simply reconstructs the parse tree and runs itusing the usual <tt class="literal">runops()</tt> loop that Perl itself usesduring execution. <tt class="literal">B::CC</tt> produces a linearized andoptimized C equivalent of the run-time code path (which resembles agiant jump table) and executes that instead.</p></dd></dl><p>During compilation, Perl optimizes your code in many, many ways. Itrearranges code to make it more efficient at execution time. Itdeletes code that can never be reached during execution, like an<tt class="literal">if (0)</tt> block, or the <tt class="literal">elsif</tt>s andthe <tt class="literal">else</tt> in an <tt class="literal">if (1)</tt> block. Ifyou use lexically typed variables declared with <tt class="literal">my ClassName$var</tt> or <tt class="literal">our ClassName $var</tt>, and the<tt class="literal">ClassName</tt> package was set up with the <tt class="literal">usefields</tt> pragma, accesses to constant fields from theunderlying pseudohash are typo-checked at compile time and convertedinto array accesses instead. If you supply the<tt class="literal">sort</tt> operator with a simple enough comparisonroutine, such as <tt class="literal">{$a <=> $b}</tt> or <tt class="literal">{$bcmp $a}</tt>, this is replaced by a call to compiled C code.</p><p>Perl's most dramatic optimization is probably the way it resolvesconstant expressions as soon as possible. For example, consider theparse tree shown in <a href="ch18_02.htm#perl3-opcode-order">Figure 18-2</a>. If nodes 1 and 2 had both beenliterals or constant functions, nodes 1 through 4 would have beenreplaced by the result of that computation, something like<a href="ch18_02.htm#perl3-constant-folding">Figure 18-3</a>.</p><a name="perl3-constant-folding"></a><div class="figure"></div><h4 class="objtitle">Figure 18.3. Constant folding</h4><p>This is called <em class="emphasis">constant folding</em>. Constant foldingisn't limited to simple cases such as turning <tt class="literal">2**10</tt>into <tt class="literal">1024</tt> at compile time. It also resolvesfunction calls--both built-ins and user-declared subroutines thatmeet the criteria from the section <a href="ch06_04.htm#ch06-sect-icf">Section 18.4.1, "Inlining Constant Functions"</a> in<a href="ch06_01.htm">Chapter 6, "Subroutines"</a>. Reminiscent of FORTRANcompilers' notorious knowledge of their own intrinsic functions, Perlalso knows which of its own built-ins to call during compilation.That's why if you try to take the <tt class="literal">log</tt> of<tt class="literal">0.0</tt> or the <tt class="literal">sqrt</tt> of a negativeconstant, you'll incur a compilation error, not a run-time error, andthe interpreter is never run at all.<a href="#FOOTNOTE-4">[4]</a></p><blockquote class="footnote"><a name="FOOTNOTE-4"></a><p>[4] Actually, we'reoversimplifying here. The interpreter does get run, because that's howthe constant folder is implemented. But it is run immediately atcompile time, similar to how <tt class="literal">BEGIN</tt> blocks areexecuted.</p></blockquote><p>Even arbitrarily complicated expressions are resolved early, sometimestriggering the deletion of complete blocks such as the one here:<blockquote><pre class="programlisting">if (2 * sin(1)/cos(1) < 3 && somefn()) { whatever() }</pre></blockquote>No code is generated for what can never be evaluated. Because thefirst part is always false, neither <tt class="literal">somefn</tt> nor<tt class="literal">whatever</tt> can ever be called. (So don't expect to<tt class="literal">goto</tt> labels inside that block, because it won'teven exist at run time.) If <tt class="literal">somefn</tt> were aninlinable constant function, then even switching the evaluation orderlike this:<blockquote><pre class="programlisting">if (somefn() && 2 * sin(1)/cos(1) < 3)) { whatever() }</pre></blockquote>wouldn't change the outcome, since the entire expression still resolvesat compile time. If <tt class="literal">whatever</tt> were inlinable, itwouldn't be called at run time, nor even during compilation; its valuewould just be inserted as though it were a literal constant. Youwould then incur a warning about a "Useless use of a constant in voidcontext". This might surprise you if you didn't realize it was aconstant. However, if <tt class="literal">whatever</tt> were the laststatement evaluated in a function called in a nonvoid context (asdetermined by the optimizer), you wouldn't see the warning.</p><p>You can see the final result of the constructed parse tree after alloptimization stages with <em class="emphasis">perl -Dx</em>. (The<tt class="userinput"><b>-D</b></tt> switch requires a special, debugging-enabledbuild of Perl). Also see the section on <tt class="literal">B::Deparse</tt>described below.</p><p>All in all, the Perl compiler works hard (but not <em class="emphasis">too</em> hard) tooptimize code so that, come run time, overall execution is sped up.It's about time to get your program running, so let's do that now.</p><a name="INDEX-3253"></a><!-- BOTTOM NAV BAR --><hr width="515" align="left"><div class="navbar"><table width="515" border="0"><tr><td align="left" valign="top" width="172"><a href="ch18_01.htm"><img src="../gifs/txtpreva.gif" alt="Previous" border="0"></a></td><td align="center" valign="top" width="171"><a href="index.htm"><img src="../gifs/txthome.gif" alt="Home" border="0"></a></td><td align="right" valign="top" width="172"><a href="ch18_03.htm"><img src="../gifs/txtnexta.gif" alt="Next" border="0"></a></td></tr><tr><td align="left" valign="top" width="172">18.1. The Life Cycle of a Perl Program</td><td align="center" valign="top" width="171"><a href="index/index.htm"><img src="../gifs/index.gif" alt="Book Index" border="0"></a></td><td align="right" valign="top" width="172">18.3. Executing Your Code</td></tr></table></div><hr width="515" align="left"><!-- LIBRARY NAV BAR --><img src="../gifs/smnavbar.gif" usemap="#library-map" border="0" alt="Library Navigation Links"><p><font size="-1"><a href="copyrght.htm">Copyright © 2001</a> O'Reilly & Associates. All rights reserved.</font></p><map name="library-map"> <area shape="rect" coords="2,-1,79,99" href="../index.htm"><area shape="rect" coords="84,1,157,108" href="../perlnut/index.htm"><area shape="rect" coords="162,2,248,125" href="../prog/index.htm"><area shape="rect" coords="253,2,326,130" href="../advprog/index.htm"><area shape="rect" coords="332,1,407,112" href="../cookbook/index.htm"><area shape="rect" coords="414,2,523,103" href="../sysadmin/index.htm"></map><!-- END OF BODY --></body></html>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -