From 3dd5ab9473f888ceb8f2ae6d71f51cd92de83f5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Fri, 28 Jun 2019 08:25:00 +0200 Subject: beam_ssa_opt: Do not apply tuple_size optimization outside guards Rewriting `tuple_size` to `is_tuple` + `tuple_size` will cause it not to throw an exception, either crashing the compiler or the emulator when the code runs. --- lib/compiler/src/beam_ssa_opt.erl | 66 +++++++++++++++++++++++---------------- lib/compiler/test/match_SUITE.erl | 21 +++++++++++-- 2 files changed, 58 insertions(+), 29 deletions(-) diff --git a/lib/compiler/src/beam_ssa_opt.erl b/lib/compiler/src/beam_ssa_opt.erl index 90c0d3cf16..229edc6a1d 100644 --- a/lib/compiler/src/beam_ssa_opt.erl +++ b/lib/compiler/src/beam_ssa_opt.erl @@ -904,8 +904,7 @@ cse_suitable(#b_set{}) -> false. }). ssa_opt_float({#st{ssa=Linear0,cnt=Count0}=St, FuncDb}) -> - NonGuards0 = float_non_guards(Linear0), - NonGuards = gb_sets:from_list(NonGuards0), + NonGuards = non_guards(Linear0), Blocks = maps:from_list(Linear0), Fs = #fs{non_guards=NonGuards,bs=Blocks}, {Linear,Count} = float_opt(Linear0, Count0, Fs), @@ -916,15 +915,6 @@ float_blk_is_in_guard(#b_blk{last=#b_br{fail=F}}, #fs{non_guards=NonGuards}) -> float_blk_is_in_guard(#b_blk{}, #fs{}) -> false. -float_non_guards([{L,#b_blk{is=Is}}|Bs]) -> - case Is of - [#b_set{op=landingpad}|_] -> - [L|float_non_guards(Bs)]; - _ -> - float_non_guards(Bs) - end; -float_non_guards([]) -> [?BADARG_BLOCK]. - float_opt([{L,Blk}|Bs0], Count0, Fs) -> case float_blk_is_in_guard(Blk, Fs) of true -> @@ -1774,35 +1764,44 @@ opt_bs_put_split_int_1(Int, L, R) -> %%% ssa_opt_tuple_size({#st{ssa=Linear0,cnt=Count0}=St, FuncDb}) -> - {Linear,Count} = opt_tup_size(Linear0, Count0, []), + %% This optimization is only safe in guards, as prefixing tuple_size with + %% an is_tuple check prevents it from throwing an exception. + NonGuards = non_guards(Linear0), + {Linear,Count} = opt_tup_size(Linear0, NonGuards, Count0, []), {St#st{ssa=Linear,cnt=Count}, FuncDb}. -opt_tup_size([{L,#b_blk{is=Is,last=Last}=Blk}|Bs], Count0, Acc0) -> +opt_tup_size([{L,#b_blk{is=Is,last=Last}=Blk}|Bs], NonGuards, Count0, Acc0) -> case {Is,Last} of {[#b_set{op={bif,'=:='},dst=Bool,args=[#b_var{}=Tup,#b_literal{val=Arity}]}], #b_br{bool=Bool}} when is_integer(Arity), Arity >= 0 -> - {Acc,Count} = opt_tup_size_1(Tup, L, Count0, Acc0), - opt_tup_size(Bs, Count, [{L,Blk}|Acc]); + {Acc,Count} = opt_tup_size_1(Tup, L, NonGuards, Count0, Acc0), + opt_tup_size(Bs, NonGuards, Count, [{L,Blk}|Acc]); {_,_} -> - opt_tup_size(Bs, Count0, [{L,Blk}|Acc0]) + opt_tup_size(Bs, NonGuards, Count0, [{L,Blk}|Acc0]) end; -opt_tup_size([], Count, Acc) -> +opt_tup_size([], _NonGuards, Count, Acc) -> {reverse(Acc),Count}. -opt_tup_size_1(Size, EqL, Count0, [{L,Blk0}|Acc]) -> - case Blk0 of - #b_blk{is=Is0,last=#b_br{bool=Bool,succ=EqL,fail=Fail}} -> - case opt_tup_size_is(Is0, Bool, Size, []) of - none -> +opt_tup_size_1(Size, EqL, NonGuards, Count0, [{L,Blk0}|Acc]) -> + #b_blk{is=Is0,last=Last} = Blk0, + case Last of + #b_br{bool=Bool,succ=EqL,fail=Fail} -> + case gb_sets:is_member(Fail, NonGuards) of + true -> {[{L,Blk0}|Acc],Count0}; - {PreIs,TupleSizeIs,Tuple} -> - opt_tup_size_2(PreIs, TupleSizeIs, L, EqL, - Tuple, Fail, Count0, Acc) + false -> + case opt_tup_size_is(Is0, Bool, Size, []) of + none -> + {[{L,Blk0}|Acc],Count0}; + {PreIs,TupleSizeIs,Tuple} -> + opt_tup_size_2(PreIs, TupleSizeIs, L, EqL, + Tuple, Fail, Count0, Acc) + end end; - #b_blk{} -> + _ -> {[{L,Blk0}|Acc],Count0} end; -opt_tup_size_1(_, _, Count, Acc) -> +opt_tup_size_1(_, _, _, Count, Acc) -> {Acc,Count}. opt_tup_size_2(PreIs, TupleSizeIs, PreL, EqL, Tuple, Fail, Count0, Acc) -> @@ -2241,6 +2240,19 @@ gcd(A, B) -> X -> gcd(B, X) end. +non_guards(Linear) -> + gb_sets:from_list(non_guards_1(Linear)). + +non_guards_1([{L,#b_blk{is=Is}}|Bs]) -> + case Is of + [#b_set{op=landingpad}|_] -> + [L | non_guards_1(Bs)]; + _ -> + non_guards_1(Bs) + end; +non_guards_1([]) -> + [?BADARG_BLOCK]. + rel2fam(S0) -> S1 = sofs:relation(S0), S = sofs:rel2fam(S1), diff --git a/lib/compiler/test/match_SUITE.erl b/lib/compiler/test/match_SUITE.erl index aac9de278d..bc74ec4984 100644 --- a/lib/compiler/test/match_SUITE.erl +++ b/lib/compiler/test/match_SUITE.erl @@ -25,7 +25,8 @@ match_in_call/1,untuplify/1,shortcut_boolean/1,letify_guard/1, selectify/1,deselectify/1,underscore/1,match_map/1,map_vars_used/1, coverage/1,grab_bag/1,literal_binary/1, - unary_op/1,eq_types/1,match_after_return/1,match_right_tuple/1]). + unary_op/1,eq_types/1,match_after_return/1,match_right_tuple/1, + tuple_size_in_try/1]). -include_lib("common_test/include/ct.hrl"). @@ -41,7 +42,8 @@ groups() -> shortcut_boolean,letify_guard,selectify,deselectify, underscore,match_map,map_vars_used,coverage, grab_bag,literal_binary,unary_op,eq_types, - match_after_return,match_right_tuple]}]. + match_after_return,match_right_tuple, + tuple_size_in_try]}]. init_per_suite(Config) -> @@ -922,4 +924,19 @@ match_right_tuple_1(T) -> force_succ_regs(_A, B) -> B. +tuple_size_in_try(Config) when is_list(Config) -> + %% The tuple_size optimization was applied outside of guards, causing + %% either the emulator or compiler to crash. + ok = tsit(gurka), + ok = tsit(gaffel). + +tsit(A) -> + try + id(ignored), + 1 = tuple_size(A), + error + catch + _:_ -> ok + end. + id(I) -> I. -- cgit v1.2.3