查看源代码 BEAM SSA 检查
在开发基于 BEAM SSA 的优化时,通常很难检查各种转换是否达到了预期效果。例如,除非转换产生崩溃的代码,否则很难检测到转换是否损坏。同样,错过优化机会也很难检测到。
为了简化在 BEAM SSA 上创建测试,编译器具有一个内部模式,在该模式下,它解析并检查对生成的 BEAM SSA 代码的结构和内容的断言。这是对 SSA 检查器功能的语法和语义的简短介绍。
语法
SSA 检查以注释的形式嵌入到源代码中,注释以 %ssa%
、%%ssa%
或 %%%ssa%
开头。这是对语法的简短介绍,有关完整语法,请参阅 erl_parse.yrl
中的 ssa_check_when_clause
产生式。
SSA 检查可以放置在任何 Erlang 函数内部,例如
t0() ->
%ssa% () when post_ssa_opt ->
%ssa% ret(#{}).
#{}.
将检查 t0/0
是否返回字面量 #{}
。如果我们想检查一个函数是否返回其第一个形式参数,我们可以写
t1(A, _B) ->
%ssa% (X, _) when post_ssa_opt ->
%ssa% ret(X).
A.
请注意我们如何使用 X
来匹配第一个形式参数。为 SSA 检查设置我们自己的形式参数的原因是,我们不想在 Erlang 级别引入新的标识符来支持 SSA 级别的检查。考虑一下如果 t1/2
被定义为 t1([A|As], B)
,我们将不得不为聚合值 [A|As]
引入一个新的标识符。
SSA 检查子句的完整语法为
<expected-result>? (<formals>) when <pipeline-location> -> <checks> '.'
其中 <expected-result>
可以是 pass
(检查必须成功)、fail
和 xfail
(检查必须失败)之一。省略 <expected-result>
被解析为隐式的 pass
。
<formals>
是一个以逗号分隔的变量列表。
<pipeline-location>
指定在编译器管道中的哪个位置运行检查。目前,<pipeline-location>
唯一支持的值是 post_ssa_opt
,它在 ssa_opt
传递后运行检查。
<checks>
是一个以逗号分隔的与 BEAM SSA 代码匹配的列表。对于非流控制操作,语法为
<variable> = <operation> ( <arguments> ) <annotation>?
其中 <operation>
是内部 SSA 表示中的 #b_set.op
字段。BIF 被写成 bif:<atom>
。
<arguments>
是一个以逗号分隔的变量或字面量列表。
对于流控制操作和标签,语法如下
br(<bool>, <true-label>, <false-label>)
switch(<value>, <fail-label>, [{<label>,<value>},...])
ret(<value>)
label <value>
其中 <value>
是一个字面量或一个变量。
检查还可以包含对操作注释的断言。该断言被写成一个类似于映射的模式,紧跟在参数列表之后,例如
t0() ->
%ssa% () when post_ssa_opt ->
%ssa% _ = call(fun return_int/0) { result_type => {t_integer,{17,17}},
%ssa% location => {_,32} },
%ssa% _ = call(fun return_tuple/0) {
%ssa% result_type => {t_tuple,2,true,#{1 => {t_integer,{1,1}},
%ssa% 2 => {t_integer,{2,2}}}}
%ssa% }.
X = return_int(),
Y = return_tuple(),
{X, Y}.
语义
当将 SSA 断言与函数的 BEAM SSA 进行匹配时,模式会按顺序应用。如果当前模式不匹配,则检查器会尝试下一个指令。如果检查器到达 SSA 表示的末尾,而没有匹配所有模式,则认为检查失败,否则认为断言成功。当模式与 SSA 操作匹配时,会考虑已经绑定的变量的值,并且如果模式匹配,则在成功匹配的模式中引入的自由变量将绑定到它们在匹配操作中具有的值。
编译器集成
BEAM SSA 检查器通过编译器选项 {check_ssa,post_ssa_opt}
启用。启用后,失败的 SSA 断言将使用与普通编译错误相同的机制报告。
限制
- 未绑定的变量不允许在映射字面量的键部分或注释断言中使用。