查看源代码 BEAM SSA 的结构和格式不变性
异常处理
将 try
-catch
表达式转换为 BEAM SSA 具有以下结构
@tag = new_try_tag `try`
br @tag, ^protected_block0, ^landing_pad_block
protected_block0:
@success0 = ... % Something that could raise an exception
br @success0, ^protected_block1, ^landing_pad_block
...
protected_blockN:
% The end of the protected code
@ignored0 = kill_try_tag @tag
br ^after_try_catch
landing_pad_block:
@aggregate = landingpad try, @tag
@class = extract @aggregate, `0` % The error class
@reason = extract @aggregate, `1` % The reason
@stk = extract @aggregate, `2` % The stack trace
@ignored1 = kill_try_tag @tag
%% Pattern matching on @class, @reason, and @stk is done here
%% to send control to the appropriate catch clause
br ^after_try_catch
after_try_catch:
% Normal execution continues
SSA 必须保持以下不变性
- 所有可能在受保护的代码块中导致异常的代码,都必须具有到落地代码块的显式控制流边。如果除了包含
new_try_tag
的代码块外,没有其他边指向落地代码块,编译器将删除冗余的异常处理程序。 - 必须按照类、原因和堆栈跟踪的顺序从
landingpad
指令的结果中提取它们。允许省略未使用的元素提取。 - 落地代码块和最终受保护的代码块都必须以
kill_try_tag
指令结束。尝试在最后一个受保护的代码块和落地代码块之间共享kill_try_tag
尾声是不太可能成功的。
将旧式 catch
表达式转换为 BEAM SSA 具有以下结构
@tag = new_try_tag `try`
br @tag, ^protected_block0, ^landing_pad_block
protected_block0:
@success0 = ... % Something that could raise an exception
br @success0, ^protected_block1, ^landing_pad_block
...
protected_blockN:
% The end of the protected code
@successful_result = .... % The result of a successful computation
br ^common_end_of_catch
landing_pad_block:
@aggregate = landingpad catch, @tag
@catched_val = extract @ssa_agg, `0`
br ^common_end_of_catch
common_end_of_catch:
@tmp = phi { @catched_val, ^landing_pad_block },
{ @successful_result, ^protected_blockN }
@result_of_catch_expr = catch_end @tag, @tmp
与 try
-catch
表达式一样,所有可能在受保护的代码块中导致异常的代码,都必须具有到落地代码块的显式控制流边。
重新抛出异常
典型的用户编写的 try
-catch
表达式会捕获所有可能的异常类和原因的子集,并将未处理的异常留给调用堆栈中更上一层的处理程序。使用 resume
指令重新抛出异常。resume
必须在程序流中的 kill_try_tag
指令之后。例如,如果 异常处理部分中的示例 仅处理用户 throws
,则相关的代码块如下所示
landing_pad_block:
@aggregate = landingpad `try`, @tag
@class = extract @aggregate, `0` % The error class
@reason = extract @aggregate, `1` % The reason
@stk = extract @aggregate, `2` % The stack trace
@ignored1 = kill_try_tag @tag
@is_throw = bif:'=:=' @class, `throw`
br @is_throw ^first_block_of_throw_handler, ^reissue
first_block_of_throw_handler:
%% Handle the user-defined throw
reissue:
@tmp = resume @stk, @reason
ret @tmp
函数调用
除非应用以下例外情况之一,否则不在尾调用位置的所有函数调用之后都必须跟随 succeeded:body 指令
可以静态证明该函数调用总是会失败。
该函数调用是到
erlang
模块的,并且可以静态证明总是会成功或失败。
变量命名
BEAM SSA 中的变量名要么是原子,要么是非负整数
atom() | non_neg_integer()
为了生成新的、未使用的变量名,所有编译器转换都维护一个计数器,即 b_function
和 opt_st
记录中的 cnt
字段,每次创建新的变量或标签时,该计数器都会递增。在以下描述中,cnt
字段的值称为 Cnt
。Cnt
值保证永远不会与先前定义的变量名冲突。因此,Cnt
的值可以直接用作 SSA 传递中的变量名。
请注意,在 Erlang/OTP 27 之前,规则更加复杂,因为 Cnt
值可能会与其他变量冲突。