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操作码来自语法分析器。它们都不会被执行并且不会带来性能损失。
使用 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操作码来自语法分析器。它们都不会被执行并且不会带来性能损失。