Perl 中的“null vK”是什么?

What is `null vK` in Perl?

使用 Perl,我有两个相似的语法,

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

行首的“-”表示op不会被执行,也可以用perl -MO=Concise,-exec.

看出

也就是说,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 直接来自以下 do BLOCK 语法规则 (perly.y):

termdo  :       DO term %prec UNIOP                     /* do $filename */
            { $$ = dofile(, );}
    |   DO block    %prec '('               /* do { code */
            { $$ = newUNOP(OP_NULL, OPf_SPECIAL, op_scope());}
        ;

我们可以在这里看到:

>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 操作。 显然,这些空值用于帮助管理 op-tree,并且由于它们从未被执行,我认为 Perl 开发人员不会为它们的存在而烦恼。

这是一个由布尔表达式解析产生的空操作的例子:

>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变成了not vK。 仔细观察,我们可以看到 Perl 将 !$a&&!$b 优化为 !($a||$b)not(!) 取代了 null。 事实证明,Perl 总是为逻辑表达式保留一个父操作码,如果一个表达式可以用外部 not 简化 Perl 将 not 放入父操作码,否则 null

总结一下:B::Concise输出中ex-指示的NULL操作码由优化器生成,null指示的NULL操作码来自语法分析器。它们都不会被执行并且不会带来性能损失。