aboutsummaryrefslogtreecommitdiffstats
path: root/lib/compiler/src/beam_bsm.erl
diff options
context:
space:
mode:
authorBjörn Gustavsson <[email protected]>2016-10-06 06:01:11 +0200
committerBjörn Gustavsson <[email protected]>2016-10-06 07:10:37 +0200
commit67808ef46f8f429652ddebb81b0b5c3c603f8655 (patch)
treec156df806f0b7de28740af748283476ff3aa02b3 /lib/compiler/src/beam_bsm.erl
parent05be21eb0ad09fcdd63d955ed1d5b50ed34af925 (diff)
downloadotp-67808ef46f8f429652ddebb81b0b5c3c603f8655.tar.gz
otp-67808ef46f8f429652ddebb81b0b5c3c603f8655.tar.bz2
otp-67808ef46f8f429652ddebb81b0b5c3c603f8655.zip
beam_bsm: Eliminate unsafe optimization
The following code causes a compiler failure: first_after(Data, Offset) -> case byte_size(Data) > Offset of false -> {First, Rest} = {ok, ok}, ok; true -> <<_:Offset/binary, Rest/binary>> = Data, %% 'Rest' saved in y(0) before the call. {First, _} = match_first(Data, Rest), %% When beam_bsm sees the code, the following line %% which uses y(0) has been optimized away. {First, Rest} = {First, Rest}, First end. match_first(_, <<First:1/binary, Rest/binary>>) -> {First, Rest}. Here is the error message from beam_validator: t: function first_after/2+15: Internal consistency check failed - please report this bug. Instruction: {call,2,{f,7}} Error: {multiple_match_contexts,[{x,1},0]}: Basically, what happens is that at time of code generation, the variable 'Rest' is needed after the call to match_first/2 and is therefore saved in y(0). When beam_bsm (a late optimization pass) sees the code, the use of y(0) following the call to match_first/2 has been optimized away. beam_bsm therefore assumes that the delayed sub-binary creation is safe. (Actually, it is safe, but beam_validator does not realize it.) The bug was caused by two separate commits: e199e2471a reduced the number of special cases to handle in BEAM optimization passed by breaking apart the tail-recursive call instructions (call_only and call_last) into separate instructions. Unfortunately, the special handling for tail calls was lost, which resulted in worse code (i.e. the delayed sub-binary creation optimization could not be applied). e1aa422290 tried to compensate, but did so in a way that was not always safe. Teaching beam_validator that this kind of code is safe would be expensive. Instead, we will undo the damage caused by the two commits. Re-introduce the special handling of tail-recursive calls in beam_bsm that was lost in the first commit. (Effectively) revert the change in the second commit. ERL-268
Diffstat (limited to 'lib/compiler/src/beam_bsm.erl')
-rw-r--r--lib/compiler/src/beam_bsm.erl31
1 files changed, 21 insertions, 10 deletions
diff --git a/lib/compiler/src/beam_bsm.erl b/lib/compiler/src/beam_bsm.erl
index 286307a4be..ae1b34ba49 100644
--- a/lib/compiler/src/beam_bsm.erl
+++ b/lib/compiler/src/beam_bsm.erl
@@ -205,8 +205,15 @@ btb_reaches_match_1(Is, Regs, D) ->
btb_reaches_match_2([{block,Bl}|Is], Regs0, D) ->
Regs = btb_reaches_match_block(Bl, Regs0),
btb_reaches_match_1(Is, Regs, D);
-btb_reaches_match_2([{call,Arity,{f,Lbl}}|Is], Regs, D) ->
- btb_call(Arity, Lbl, Regs, Is, D);
+btb_reaches_match_2([{call,Arity,{f,Lbl}}|Is], Regs0, D) ->
+ case is_tail_call(Is) of
+ true ->
+ Regs1 = btb_kill_not_live(Arity, Regs0),
+ Regs = btb_kill_yregs(Regs1),
+ btb_tail_call(Lbl, Regs, D);
+ false ->
+ btb_call(Arity, Lbl, Regs0, Is, D)
+ end;
btb_reaches_match_2([{apply,Arity}|Is], Regs, D) ->
btb_call(Arity+2, apply, Regs, Is, D);
btb_reaches_match_2([{call_fun,Live}=I|Is], Regs, D) ->
@@ -360,6 +367,10 @@ btb_reaches_match_2([{line,_}|Is], Regs, D) ->
btb_reaches_match_2([I|_], Regs, _) ->
btb_error({btb_context_regs(Regs),I,not_handled}).
+is_tail_call([{deallocate,_}|_]) -> true;
+is_tail_call([return|_]) -> true;
+is_tail_call(_) -> false.
+
btb_call(Arity, Lbl, Regs0, Is, D0) ->
Regs = btb_kill_not_live(Arity, Regs0),
case btb_are_x_registers_empty(Regs) of
@@ -369,15 +380,15 @@ btb_call(Arity, Lbl, Regs0, Is, D0) ->
D = btb_tail_call(Lbl, Regs, D0),
%% No problem so far (the called function can handle a
- %% match context). Now we must make sure that the rest
- %% of this function following the call does not attempt
- %% to use the match context in case there is a copy
- %% tucked away in a y register.
+ %% match context). Now we must make sure that we don't
+ %% have any copies of the match context tucked away in an
+ %% y register.
RegList = btb_context_regs(Regs),
- YRegs = [R || {y,_}=R <- RegList],
- case btb_are_all_unused(YRegs, Is, D) of
- true -> D;
- false -> btb_error({multiple_uses,RegList})
+ case [R || {y,_}=R <- RegList] of
+ [] ->
+ D;
+ [_|_] ->
+ btb_error({multiple_uses,RegList})
end;
true ->
%% No match context in any x register. It could have been