if ($a && $b) { exit() } do { exit() } if ($a && $b)
我相信这些应该是同一个东西,但是最上面的一个创建了一个null vK操作码,
<1> null vK*/1 ->-
null vK的意义是什么?它有什么作用?
$perl -MO=Concise -e'if ($a && $b) { exit() }' 8 <@> leave[1 ref] vKP/REFC ->(end) 1 <0> enter ->2 2 <;> nextstate(main 1 -e:1) v:{ ->3 - <1> null vK/1 ->8 6 <|> and(other->7) vK/1 ->8 - <1> null sK/1 ->6 4 <|> and(other->5) sK/1 ->8 - <1> ex-rv2sv sK/1 ->4 3 <#> gvsv[*a] s ->4 - <1> ex-rv2sv sK/1 ->- 5 <#> gvsv[*b] s ->6 - <@> scope vK ->- - <;> ex-nextstate(main 3 -e:1) v ->7 7 <0> exit v* ->8 -e Syntax OK
以下是经文,
$perl -MO=Concise -e'do { exit() } if ($a && $b)' 8 <@> leave[1 ref] vKP/REFC ->(end) 1 <0> enter ->2 2 <;> nextstate(main 1 -e:1) v:{ ->3 - <1> null vK/1 ->8 6 <|> and(other->7) vK/1 ->8 - <1> null sKP/1 ->6 4 <|> and(other->5) sK/1 ->8 - <1> ex-rv2sv sK/1 ->4 3 <#> gvsv[*a] s ->4 - <1> ex-rv2sv sK/1 ->- 5 <#> gvsv[*b] s ->6 - <1> null vK*/1 ->- - <@> scope vK ->- - <;> ex-nextstate(main 2 -e:1) v ->7 7 <0> exit v* ->8
解决方法
也就是说,B :: Concise输出中的null vK …或null sK …操作码并不意味着某些操作已经被优化掉了. B :: Concise上的perldoc清楚地表明这种优化由输出中的ex表示:
Nullops appear as “ex-opname”,where opname is an op that has been
optimized away by perl. They’re displayed with a sequence-number of ‘-‘,
because they are not executed (they don’t appear in prevIoUs example),
they’re printed here because they reflect the parse.
例如:
> perl -MO=Concise -e "$a" 4 <@> leave[1 ref] vKP/REFC ->(end) 1 <0> enter ->2 2 <;> nextstate(main 1 -e:1) v:{ ->3 - **<1> ex-rv2sv vK/1 ->4** 3 <#> gvsv[*a] s ->4
那么这些空值是什么呢?
它们是来自Perl用于解析代码的yacc语法的真正空值,并且它们从一开始就不打算执行.
在你的情况下,多余的null vk直接来自以下的BLOCK语法规则(perly.y):
termdo : DO term %prec UNIOP /* do $filename */ { $$= dofile($2,$1);} | DO block %prec '(' /* do { code */ { $$= newUNOP(OP_NULL,OPf_SPECIAL,op_scope($2));} ;
我们可以在这里看到它:
>perl -MO=Concise -e "do{}" 4 <@> leave[1 ref] vKP/REFC ->(end) 1 <0> enter ->2 2 <;> nextstate(main 2 -e:1) v:{ ->3 - <1> null vK*/1 ->4 - <@> scope vK ->- 3 <0> stub v ->4
在其他情况下,空值来自yacc操作.
显然,这些空值用于帮助管理操作树,因为它们永远不会执行,我认为Perl开发人员不会为他们的存在而烦恼.
以下是解析布尔表达式产生的null操作示例:
>perl -MO=Concise -e "$a||$b" 6 <@> leave[1 ref] vKP/REFC ->(end) 1 <0> enter ->2 2 <;> nextstate(main 1 -e:1) v:{ ->3 - <1> null vK/1 ->6 4 <|> or(other->5) vK/1 ->6 - <1> ex-rv2sv sK/1 ->4 3 <#> gvsv[*a] s ->4 - <1> ex-rv2sv vK/1 ->- 5 <#> gvsv[*b] s ->6
为什么这里为空?另一个片段有助于说清楚:
>perl -MO=Concise -e "!$a&&!$b" 7 <@> leave[1 ref] vKP/REFC ->(end) 1 <0> enter ->2 2 <;> nextstate(main 1 -e:1) v:{ ->3 6 <1> not vK/1 ->7 4 <|> or(other->5) sK/1 ->6 - <1> ex-not sK/1 ->4 - <1> ex-rv2sv sK/1 ->- 3 <#> gvsv[*a] s ->4 - <1> ex-not sK/1 ->6 - <1> ex-rv2sv sK/1 ->- 5 <#> gvsv[*b] s ->6
似乎null vK已经不是vK.
仔细观察,我们可以看到Perl优化了!$a&&!$b进入!($a || $b)而没有(!)取代null.
事实证明,Perl总是为逻辑表达式保留父操作码,如果表达式可以用外部not来简化
Perl不会放入父操作码,否则返回null.
总结一下:由B :: Concise输出中指示的NULL操作码由优化器生成,而由null指示的NULL操作码来自语法解析器.它们都不会被执行,也不会带来任何性能损失.