From a185892f7acc5d1d51b2f167b8304edf2377e9bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Fri, 8 Feb 2019 13:07:03 +0100 Subject: beam_validator: Misc cosmetic refactoring --- lib/compiler/src/beam_validator.erl | 59 +++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 28 deletions(-) diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl index 3b197f7bae..c659b502bb 100644 --- a/lib/compiler/src/beam_validator.erl +++ b/lib/compiler/src/beam_validator.erl @@ -28,7 +28,7 @@ -export([module/2, format_error/1]). -export([type_anno/1, type_anno/2, type_anno/4]). --import(lists, [any/2,dropwhile/2,foldl/3,map/2,foreach/2,reverse/1]). +-import(lists, [any/2,dropwhile/2,foldl/3,map/2,reverse/1]). %% To be called by the compiler. @@ -372,7 +372,7 @@ valfun_1({put_tuple2,Dst,{list,Elements}}, Vst0) -> Type = get_term_type(Val, Vst0), Es = set_element_type(Index, Type, Es0), {Es, Index + 1} - end, {#{}, 1}, Elements), + end, {#{}, 1}, Elements), Type = {tuple,Size,Es}, create_term(Type, Dst, Vst); valfun_1({put_tuple,Sz,Dst}, Vst0) when is_integer(Sz) -> @@ -542,16 +542,16 @@ valfun_2(_, _) -> valfun_3({fconv,Src,{fr,_}=Dst}, Vst) -> assert_term(Src, Vst), set_freg(Dst, Vst); -valfun_3({bif,fadd,_,[_,_]=Src,Dst}, Vst) -> - float_op(Src, Dst, Vst); -valfun_3({bif,fdiv,_,[_,_]=Src,Dst}, Vst) -> - float_op(Src, Dst, Vst); -valfun_3({bif,fmul,_,[_,_]=Src,Dst}, Vst) -> - float_op(Src, Dst, Vst); -valfun_3({bif,fnegate,_,[_]=Src,Dst}, Vst) -> - float_op(Src, Dst, Vst); -valfun_3({bif,fsub,_,[_,_]=Src,Dst}, Vst) -> - float_op(Src, Dst, Vst); +valfun_3({bif,fadd,_,[_,_]=Ss,Dst}, Vst) -> + float_op(Ss, Dst, Vst); +valfun_3({bif,fdiv,_,[_,_]=Ss,Dst}, Vst) -> + float_op(Ss, Dst, Vst); +valfun_3({bif,fmul,_,[_,_]=Ss,Dst}, Vst) -> + float_op(Ss, Dst, Vst); +valfun_3({bif,fnegate,_,[_]=Ss,Dst}, Vst) -> + float_op(Ss, Dst, Vst); +valfun_3({bif,fsub,_,[_,_]=Ss,Dst}, Vst) -> + float_op(Ss, Dst, Vst); valfun_3(fclearerror, Vst) -> case get_fls(Vst) of undefined -> ok; @@ -697,12 +697,11 @@ valfun_4({select_val,Src,{f,Fail},{list,Choices}}, Vst0) -> assert_choices(Choices), Vst = branch_state(Fail, Vst0), kill_state(select_val_branches(Src, Choices, Vst)); -valfun_4({select_tuple_arity,Tuple,{f,Fail},{list,Choices}}, Vst) -> - assert_type(tuple, Tuple, Vst), +valfun_4({select_tuple_arity,Tuple,{f,Fail},{list,Choices}}, Vst0) -> + assert_type(tuple, Tuple, Vst0), assert_arities(Choices), - TupleType = get_durable_term_type(Tuple, Vst), - kill_state(branch_arities(Choices, Tuple, TupleType, - branch_state(Fail, Vst))); + Vst = branch_state(Fail, Vst0), + kill_state(branch_arities(Choices, Tuple, Vst)); %% New bit syntax matching instructions. valfun_4({test,bs_start_match3,{f,Fail},Live,[Src],Dst}, Vst) -> @@ -783,9 +782,10 @@ valfun_4({test,is_map,{f,Lbl},[Src]}, Vst) -> assert_term(Src, Vst), kill_state(Vst) end; -valfun_4({test,test_arity,{f,Lbl},[Tuple,Sz]}, Vst) when is_integer(Sz) -> - assert_type(tuple, Tuple, Vst), - update_type(fun meet/2, {tuple,Sz,#{}}, Tuple, branch_state(Lbl, Vst)); +valfun_4({test,test_arity,{f,Lbl},[Tuple,Sz]}, Vst0) when is_integer(Sz) -> + assert_type(tuple, Tuple, Vst0), + Vst = branch_state(Lbl, Vst0), + update_type(fun meet/2, {tuple,Sz,#{}}, Tuple, Vst); valfun_4({test,is_tagged_tuple,{f,Lbl},[Src,Sz,Atom]}, Vst0) -> assert_term(Src, Vst0), Vst = branch_state(Lbl, Vst0), @@ -926,7 +926,7 @@ verify_put_map(Fail, Src, Dst, Live, List, Vst0) -> assert_type(map, Src, Vst0), verify_live(Live, Vst0), verify_y_init(Vst0), - foreach(fun (Term) -> assert_not_fragile(Term, Vst0) end, List), + [assert_not_fragile(Term, Vst0) || Term <- List], Vst1 = heap_alloc(0, Vst0), Vst2 = branch_state(Fail, Vst1), Vst = prune_x_regs(Live, Vst2), @@ -1036,12 +1036,10 @@ verify_call_args_1(N, Vst) -> verify_call_args_1(X, Vst). verify_local_call(Lbl, Live, Vst) -> - F = fun({R, Type}) -> - verify_arg_type(Lbl, R, Type, Vst) - end, TRegs = typed_call_regs(Live, Vst), + [verify_arg_type(Lbl, R, Type, Vst) || {R, Type} <- TRegs], verify_no_ms_aliases(TRegs), - foreach(F, TRegs). + ok. typed_call_regs(0, _Vst) -> []; @@ -1190,8 +1188,8 @@ assert_arities(_) -> error(bad_tuple_arity_list). %%% fmove Src {fr,_} %% Move INTO floating point register. %%% -float_op(Src, Dst, Vst0) -> - foreach (fun(S) -> assert_freg_set(S, Vst0) end, Src), +float_op(Ss, Dst, Vst0) -> + [assert_freg_set(S, Vst0) || S <- Ss], assert_fls(cleared, Vst0), Vst = set_fls(cleared, Vst0), set_freg(Dst, Vst). @@ -1779,7 +1777,8 @@ get_tuple_size({integer,Sz}) -> Sz; get_tuple_size(_) -> 0. validate_src(Ss, Vst) when is_list(Ss) -> - foreach(fun(S) -> get_term_type(S, Vst) end, Ss). + [assert_term(S, Vst) || S <- Ss], + ok. %% get_durable_term_type(Src, ValidatorState) -> Type %% Get the type of the source Src. The returned type Type will be @@ -1865,6 +1864,10 @@ value_to_type(T) when is_tuple(T) -> {tuple, tuple_size(T), Es}; value_to_type(L) -> {literal, L}. +branch_arities(List, Tuple, Vst) -> + Type = get_durable_term_type(Tuple, Vst), + branch_arities(List, Tuple, Type, Vst). + branch_arities([Sz,{f,L}|T], Tuple, {tuple,[_],Es0}=Type0, Vst0) when is_integer(Sz) -> %% Filter out element types that are no longer valid. Es = maps:filter(fun(Index, _Type) -> Index =< Sz end, Es0), -- cgit v1.2.3 From 8fddd069a2b076d0b3d28673e50d3fd6f7a9f0f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Fri, 8 Feb 2019 14:13:48 +0100 Subject: beam_validator: Remove pointless fragility propagation set_aliased_type already preserves fragility when updating the type of a fragile register. --- lib/compiler/src/beam_validator.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl index c659b502bb..1aa05a8495 100644 --- a/lib/compiler/src/beam_validator.erl +++ b/lib/compiler/src/beam_validator.erl @@ -1393,7 +1393,7 @@ update_type(Merge, Type0, Reg, Vst) -> none -> Type0; T -> T end, - set_aliased_type(propagate_fragility(Type, [Reg], Vst), Reg, Vst). + set_aliased_type(Type, Reg, Vst). update_ne_types(LHS, RHS, Vst) -> T1 = get_durable_term_type(LHS, Vst), -- cgit v1.2.3 From ccd9450ab0e04744bcd7fa448c810ab3f1dfc946 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Thu, 7 Feb 2019 12:21:40 +0100 Subject: beam_validator: Simplify update_ne/eq_types --- lib/compiler/src/beam_validator.erl | 38 ++++++++++++------------------------- 1 file changed, 12 insertions(+), 26 deletions(-) diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl index 1aa05a8495..1d9149e607 100644 --- a/lib/compiler/src/beam_validator.erl +++ b/lib/compiler/src/beam_validator.erl @@ -796,11 +796,9 @@ valfun_4({test,has_map_fields,{f,Lbl},Src,{list,List}}, Vst) -> branch_state(Lbl, Vst); valfun_4({test,is_eq_exact,{f,Lbl},[Src,Val]=Ss}, Vst0) -> validate_src(Ss, Vst0), - Infer = infer_types(Src, Vst0), - Vst1 = Infer(Val, Vst0), - Vst2 = update_ne_types(Src, Val, Vst1), - Vst3 = branch_state(Lbl, Vst2), - Vst = Vst3#vst{current=Vst1#vst.current}, + Vst1 = update_ne_types(Src, Val, Vst0), + Vst2 = branch_state(Lbl, Vst1), + Vst = Vst2#vst{current=Vst0#vst.current}, update_eq_types(Src, Val, Vst); valfun_4({test,is_ne_exact,{f,Lbl},[Src,Val]=Ss}, Vst0) -> validate_src(Ss, Vst0), @@ -1396,29 +1394,17 @@ update_type(Merge, Type0, Reg, Vst) -> set_aliased_type(Type, Reg, Vst). update_ne_types(LHS, RHS, Vst) -> - T1 = get_durable_term_type(LHS, Vst), - T2 = get_durable_term_type(RHS, Vst), - Type = propagate_fragility(subtract(T1, T2), [LHS], Vst), - set_aliased_type(Type, LHS, Vst). + update_type(fun subtract/2, get_durable_term_type(RHS, Vst), LHS, Vst). update_eq_types(LHS, RHS, Vst0) -> - T1 = get_durable_term_type(LHS, Vst0), - T2 = get_durable_term_type(RHS, Vst0), - Meet = meet(T1, T2), - Vst = case T1 =/= Meet of - true -> - LType = propagate_fragility(Meet, [LHS], Vst0), - set_aliased_type(LType, LHS, Vst0); - false -> - Vst0 - end, - case T2 =/= Meet of - true -> - RType = propagate_fragility(Meet, [RHS], Vst0), - set_aliased_type(RType, RHS, Vst); - false -> - Vst - end. + Infer = infer_types(LHS, Vst0), + Vst1 = Infer(RHS, Vst0), + + T1 = get_durable_term_type(LHS, Vst1), + T2 = get_durable_term_type(RHS, Vst1), + + Vst = update_type(fun meet/2, T2, LHS, Vst1), + update_type(fun meet/2, T1, RHS, Vst). %% Helper functions for the above. -- cgit v1.2.3 From 70aba8f1b77ebb40c9ea39cbcb5f78177c5c1283 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Thu, 7 Feb 2019 08:46:12 +0100 Subject: beam_validator: fconv means we have a number --- lib/compiler/src/beam_validator.erl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl index 1d9149e607..a336cf7680 100644 --- a/lib/compiler/src/beam_validator.erl +++ b/lib/compiler/src/beam_validator.erl @@ -539,8 +539,9 @@ valfun_2(_, _) -> %% Handle the remaining floating point instructions here. %% Floating point. -valfun_3({fconv,Src,{fr,_}=Dst}, Vst) -> - assert_term(Src, Vst), +valfun_3({fconv,Src,{fr,_}=Dst}, Vst0) -> + assert_term(Src, Vst0), + Vst = update_type(fun meet/2, number, Src, Vst0), set_freg(Dst, Vst); valfun_3({bif,fadd,_,[_,_]=Ss,Dst}, Vst) -> float_op(Ss, Dst, Vst); -- cgit v1.2.3 From 9fc7477cb5452f7ca745f03fe10fbd895e9fff6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Fri, 8 Feb 2019 11:35:43 +0100 Subject: beam_validator: Explain why verify_get_map wipes dst registers --- lib/compiler/src/beam_validator.erl | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl index a336cf7680..3713d2c573 100644 --- a/lib/compiler/src/beam_validator.erl +++ b/lib/compiler/src/beam_validator.erl @@ -896,12 +896,20 @@ valfun_4(_, _) -> verify_get_map(Fail, Src, List, Vst0) -> assert_not_literal(Src), %OTP 22. assert_type(map, Src, Vst0), - Vst1 = foldl(fun(D, Vsti) -> - case is_reg_defined(D,Vsti) of - true -> create_term(term, D, Vsti); + + %% get_map_elements may leave its destinations in an inconsistent state + %% when the fail label is taken. Consider the following: + %% + %% {get_map_elements,{f,7},{x,1},{list,[{atom,a},{x,1},{atom,b},{x,2}]}}. + %% + %% If 'a' exists but not 'b', {x,1} is overwritten when we jump to {f,7}. + Vst1 = foldl(fun(Dst, Vsti) -> + case is_reg_defined(Dst,Vsti) of + true -> extract_term(term, [Src], Dst, Vsti); false -> Vsti end end, Vst0, extract_map_vals(List)), + Vst2 = branch_state(Fail, Vst1), Keys = extract_map_keys(List), assert_unique_map_keys(Keys), -- cgit v1.2.3 From bd6837de0be42ce8be68bef1e8d027654c81a2b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Fri, 8 Feb 2019 12:55:36 +0100 Subject: beam_validator: Simplify complex branches Branches where the state is altered on both success and failure are hard to follow and require juggling the current state, so this commit adds a helper function to make it easier. --- lib/compiler/src/beam_validator.erl | 124 ++++++++++++++++++++++-------------- 1 file changed, 77 insertions(+), 47 deletions(-) diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl index 3713d2c573..6816c8a372 100644 --- a/lib/compiler/src/beam_validator.erl +++ b/lib/compiler/src/beam_validator.erl @@ -795,18 +795,24 @@ valfun_4({test,has_map_fields,{f,Lbl},Src,{list,List}}, Vst) -> assert_type(map, Src, Vst), assert_unique_map_keys(List), branch_state(Lbl, Vst); -valfun_4({test,is_eq_exact,{f,Lbl},[Src,Val]=Ss}, Vst0) -> - validate_src(Ss, Vst0), - Vst1 = update_ne_types(Src, Val, Vst0), - Vst2 = branch_state(Lbl, Vst1), - Vst = Vst2#vst{current=Vst0#vst.current}, - update_eq_types(Src, Val, Vst); -valfun_4({test,is_ne_exact,{f,Lbl},[Src,Val]=Ss}, Vst0) -> - validate_src(Ss, Vst0), - Vst1 = update_eq_types(Src, Val, Vst0), - Vst2 = branch_state(Lbl, Vst1), - Vst = Vst2#vst{current=Vst0#vst.current}, - update_ne_types(Src, Val, Vst); +valfun_4({test,is_eq_exact,{f,Lbl},[Src,Val]=Ss}, Vst) -> + validate_src(Ss, Vst), + complex_test(Lbl, + fun(FailVst) -> + update_ne_types(Src, Val, FailVst) + end, + fun(SuccVst) -> + update_eq_types(Src, Val, SuccVst) + end, Vst); +valfun_4({test,is_ne_exact,{f,Lbl},[Src,Val]=Ss}, Vst) -> + validate_src(Ss, Vst), + complex_test(Lbl, + fun(FailVst) -> + update_eq_types(Src, Val, FailVst) + end, + fun(SuccVst) -> + update_ne_types(Src, Val, SuccVst) + end, Vst); valfun_4({test,_Op,{f,Lbl},Src}, Vst) -> validate_src(Src, Vst), branch_state(Lbl, Vst); @@ -897,37 +903,43 @@ verify_get_map(Fail, Src, List, Vst0) -> assert_not_literal(Src), %OTP 22. assert_type(map, Src, Vst0), - %% get_map_elements may leave its destinations in an inconsistent state - %% when the fail label is taken. Consider the following: - %% - %% {get_map_elements,{f,7},{x,1},{list,[{atom,a},{x,1},{atom,b},{x,2}]}}. - %% - %% If 'a' exists but not 'b', {x,1} is overwritten when we jump to {f,7}. - Vst1 = foldl(fun(Dst, Vsti) -> - case is_reg_defined(Dst,Vsti) of - true -> extract_term(term, [Src], Dst, Vsti); - false -> Vsti - end - end, Vst0, extract_map_vals(List)), - - Vst2 = branch_state(Fail, Vst1), - Keys = extract_map_keys(List), - assert_unique_map_keys(Keys), - verify_get_map_pair(List, Src, Vst0, Vst2). - -extract_map_vals([_Key,Val|T]) -> - [Val|extract_map_vals(T)]; -extract_map_vals([]) -> []. + complex_test(Fail, + fun(FailVst) -> + clobber_map_vals(List, Src, FailVst) + end, + fun(SuccVst) -> + Keys = extract_map_keys(List), + assert_unique_map_keys(Keys), + extract_map_vals(List, Src, SuccVst, SuccVst) + end, Vst0). + +%% get_map_elements may leave its destinations in an inconsistent state when +%% the fail label is taken. Consider the following: +%% +%% {get_map_elements,{f,7},{x,1},{list,[{atom,a},{x,1},{atom,b},{x,2}]}}. +%% +%% If 'a' exists but not 'b', {x,1} is overwritten when we jump to {f,7}. +clobber_map_vals([_Key,Dst|T], Map, Vst0) -> + case is_reg_defined(Dst, Vst0) of + true -> + Vst = extract_term(term, [Map], Dst, Vst0), + clobber_map_vals(T, Map, Vst); + false -> + clobber_map_vals(T, Map, Vst0) + end; +clobber_map_vals([], _Map, Vst) -> + Vst. extract_map_keys([Key,_Val|T]) -> [Key|extract_map_keys(T)]; extract_map_keys([]) -> []. -verify_get_map_pair([Src,Dst|Vs], Map, Vst0, Vsti0) -> +extract_map_vals([Src,Dst|Vs], Map, Vst0, Vsti0) -> assert_term(Src, Vst0), Vsti = extract_term(term, [Map], Dst, Vsti0), - verify_get_map_pair(Vs, Map, Vst0, Vsti); -verify_get_map_pair([], _Map, _Vst0, Vst) -> Vst. + extract_map_vals(Vs, Map, Vst0, Vsti); +extract_map_vals([], _Map, _Vst0, Vst) -> + Vst. verify_put_map(Fail, Src, Dst, Live, List, Vst0) -> assert_type(map, Src, Vst0), @@ -945,17 +957,21 @@ verify_put_map(Fail, Src, Dst, Live, List, Vst0) -> %% Common code for validating bs_start_match* instructions. %% -validate_bs_start_match(Fail, Live, Type, Src, Dst, Vst0) -> - verify_live(Live, Vst0), - verify_y_init(Vst0), +validate_bs_start_match(Fail, Live, Type, Src, Dst, Vst) -> + verify_live(Live, Vst), + verify_y_init(Vst), %% #ms{} can represent either a match context or a term, so we have to mark %% the source as a term if it fails, and retain the incoming type if it %% succeeds (match context or not). - Vst1 = set_aliased_type(term, Src, Vst0), - Vst2 = prune_x_regs(Live, Vst1), - Vst3 = branch_state(Fail, Vst2), - extract_term(Type, [Src], Dst, Vst3, Vst0). + complex_test(Fail, + fun(FailVst) -> + set_aliased_type(term, Src, FailVst) + end, + fun(SuccVst0) -> + SuccVst = prune_x_regs(Live, SuccVst0), + extract_term(Type, [Src], Dst, SuccVst, Vst) + end, Vst). %% %% Common code for validating bs_get* instructions. @@ -1377,11 +1393,25 @@ extract_term(Type0, Ss, Dst, Vst, OrigVst) -> Type = propagate_fragility(Type0, Ss, OrigVst), set_type_reg(Type, Dst, Vst). +%% Helper functions for tests that alter state on both the success and fail +%% branches, keeping the states from tainting each other. +complex_test(Fail, FailFun, SuccFun, Vst0) -> + #vst{current=St0} = Vst0, + Vst1 = FailFun(Vst0), + Vst2 = branch_state(Fail, Vst1), + Vst = Vst2#vst{current=St0}, + SuccFun(Vst). + %% Helper function for simple "is_type" tests. -type_test(Fail, Type, Reg, Vst0) -> - assert_term(Reg, Vst0), - Vst = branch_state(Fail, update_type(fun subtract/2, Type, Reg, Vst0)), - update_type(fun meet/2, Type, Reg, Vst). +type_test(Fail, Type, Reg, Vst) -> + assert_term(Reg, Vst), + complex_test(Fail, + fun(FailVst) -> + update_type(fun subtract/2, Type, Reg, FailVst) + end, + fun(SuccVst) -> + update_type(fun meet/2, Type, Reg, SuccVst) + end, Vst). %% This is used when linear code finds out more and more information about a %% type, so that the type gets more specialized. -- cgit v1.2.3 From 0610566c58fdebb8f2fa2ffe5196278fc6052d10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Fri, 8 Feb 2019 14:37:56 +0100 Subject: beam_validator: type_test in BIFs that only fail on invalid types --- lib/compiler/src/beam_validator.erl | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl index 6816c8a372..984d668532 100644 --- a/lib/compiler/src/beam_validator.erl +++ b/lib/compiler/src/beam_validator.erl @@ -604,8 +604,7 @@ valfun_4({make_fun2,_,_,_,Live}, Vst) -> call(make_fun, Live, Vst); %% Other BIFs valfun_4({bif,tuple_size,{f,Fail},[Tuple],Dst}=I, Vst0) -> - Vst1 = branch_state(Fail, Vst0), - Vst = update_type(fun meet/2, {tuple,[0],#{}}, Tuple, Vst1), + Vst = type_test(Fail, {tuple,[0],#{}}, Tuple, Vst0), set_type_reg_expr({integer,[]}, I, Dst, Vst); valfun_4({bif,element,{f,Fail},[Pos,Tuple],Dst}, Vst0) -> PosType = get_durable_term_type(Pos, Vst0), @@ -635,8 +634,7 @@ valfun_4({bif,is_map_key,{f,Fail},[_Key,Map]=Ss,Dst}, Vst0) -> valfun_4({bif,Op,{f,Fail},[Cons]=Ss,Dst}, Vst0) when Op =:= hd; Op =:= tl -> validate_src(Ss, Vst0), - Vst1 = branch_state(Fail, Vst0), - Vst = update_type(fun meet/2, cons, Cons, Vst1), + Vst = type_test(Fail, cons, Cons, Vst0), Type = bif_type(Op, Ss, Vst), extract_term(Type, Ss, Dst, Vst); valfun_4({bif,Op,{f,Fail},Ss,Dst}, Vst0) -> -- cgit v1.2.3 From 6ae2fa3f49e7f4a3cd228dce072065bdc516d62a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Fri, 8 Feb 2019 15:15:54 +0100 Subject: beam_validator: Infer BIF argument types If we know that a BIF will badarg unless its arguments have certain types, we can infer that we have at least those types on success. Note that we can't do this in the general case as the BIF could fail for reasons other than bad arguments. --- lib/compiler/src/beam_validator.erl | 59 ++++++++++++++++++++++++++++++++----- 1 file changed, 52 insertions(+), 7 deletions(-) diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl index 984d668532..4f5818ae9f 100644 --- a/lib/compiler/src/beam_validator.erl +++ b/lib/compiler/src/beam_validator.erl @@ -28,7 +28,7 @@ -export([module/2, format_error/1]). -export([type_anno/1, type_anno/2, type_anno/4]). --import(lists, [any/2,dropwhile/2,foldl/3,map/2,reverse/1]). +-import(lists, [any/2,dropwhile/2,foldl/3,map/2,reverse/1,zip/2]). %% To be called by the compiler. @@ -639,7 +639,15 @@ valfun_4({bif,Op,{f,Fail},[Cons]=Ss,Dst}, Vst0) extract_term(Type, Ss, Dst, Vst); valfun_4({bif,Op,{f,Fail},Ss,Dst}, Vst0) -> validate_src(Ss, Vst0), - Vst = branch_state(Fail, Vst0), + Vst1 = branch_state(Fail, Vst0), + + %% Infer argument types. Note that we can't type_test in the general case + %% as the BIF could fail for reasons other than bad arguments. + ArgTypes = bif_arg_types(Op, Ss), + Vst = foldl(fun({Arg, T}, Vsti) -> + update_type(fun meet/2, T, Arg, Vsti) + end, Vst1, zip(Ss, ArgTypes)), + Type = bif_type(Op, Ss, Vst), extract_term(Type, Ss, Dst, Vst); valfun_4({gc_bif,Op,{f,Fail},Live,Ss,Dst}, #vst{current=St0}=Vst0) -> @@ -649,11 +657,12 @@ valfun_4({gc_bif,Op,{f,Fail},Live,Ss,Dst}, #vst{current=St0}=Vst0) -> St = kill_heap_allocation(St0), Vst1 = Vst0#vst{current=St}, Vst2 = branch_state(Fail, Vst1), - Vst3 = case Op of - length -> update_type(fun meet/2, list, hd(Ss), Vst2); - map_size -> update_type(fun meet/2, map, hd(Ss), Vst2); - _ -> Vst2 - end, + + ArgTypes = bif_arg_types(Op, Ss), + Vst3 = foldl(fun({Arg, T}, Vsti) -> + update_type(fun meet/2, T, Arg, Vsti) + end, Vst2, zip(Ss, ArgTypes)), + Type = bif_type(Op, Ss, Vst3), Vst = prune_x_regs(Live, Vst3), extract_term(Type, Ss, Dst, Vst, Vst0); @@ -2187,6 +2196,42 @@ propagate_fragility(Type, Ss, Vst) -> false -> Type end. +%% Generic +bif_arg_types(tuple_size, [_]) -> [{tuple,[0],#{}}]; +bif_arg_types(map_size, [_]) -> [map]; +bif_arg_types(length, [_]) -> [list]; +bif_arg_types(hd, [_]) -> [cons]; +bif_arg_types(tl, [_]) -> [cons]; +%% Boolean +bif_arg_types('not', [_]) -> [bool]; +bif_arg_types('and', [_,_]) -> [bool, bool]; +bif_arg_types('or', [_,_]) -> [bool, bool]; +bif_arg_types('xor', [_,_]) -> [bool, bool]; +%% Binary +bif_arg_types('byte_size', [_]) -> [binary]; +bif_arg_types('bit_size', [_]) -> [binary]; +%% Numerical +bif_arg_types('-', [_]) -> [number]; +bif_arg_types('+', [_]) -> [number]; +bif_arg_types('*', [_,_]) -> [number, number]; +bif_arg_types('/', [_,_]) -> [number, number]; +bif_arg_types(ceil, [_]) -> [number]; +bif_arg_types(floor, [_]) -> [number]; +bif_arg_types(trunc, [_]) -> [number]; +bif_arg_types(round, [_]) -> [number]; +%% Integer-specific +bif_arg_types('div', [_,_]) -> [{integer,[]}, {integer,[]}]; +bif_arg_types('rem', [_,_]) -> [{integer,[]}, {integer,[]}]; +bif_arg_types('band', [_,_]) -> [{integer,[]}, {integer,[]}]; +bif_arg_types('bor', [_,_]) -> [{integer,[]}, {integer,[]}]; +bif_arg_types('bxor', [_,_]) -> [{integer,[]}, {integer,[]}]; +bif_arg_types('bnot', [_]) -> [{integer,[]}]; +bif_arg_types('bsl', [_,_]) -> [{integer,[]}, {integer,[]}]; +bif_arg_types('bsr', [_,_]) -> [{integer,[]}, {integer,[]}]; +%% Unsafe type tests that may fail if an argument doesn't have the right type. +bif_arg_types(is_function, [_,_]) -> [term, {integer,[]}]; +bif_arg_types(_, Args) -> [term || _Arg <- Args]. + bif_type('-', Src, Vst) -> arith_type(Src, Vst); bif_type('+', Src, Vst) -> -- cgit v1.2.3 From 8e337c3e25c11dd0a01a99f11e98aa99d9af3451 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Fri, 8 Feb 2019 15:46:05 +0100 Subject: beam_validator: Infer types from result of all type test BIFs The compiler will usually optimize these into test instructions, but they'll nevertheless pop up in some cases. --- lib/compiler/src/beam_validator.erl | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl index 4f5818ae9f..aa7b190670 100644 --- a/lib/compiler/src/beam_validator.erl +++ b/lib/compiler/src/beam_validator.erl @@ -1350,10 +1350,6 @@ select_val_branches_1([], _, _, Vst) -> Vst. infer_types(Src, Vst) -> case get_def(Src, Vst) of - {bif,is_map,{f,_},[Map],_} -> - fun({atom,true}, S) -> update_type(fun meet/2, map, Map, S); - (_, S) -> S - end; {bif,tuple_size,{f,_},[Tuple],_} -> fun({integer,Arity}, S) -> update_type(fun meet/2, {tuple,Arity,#{}}, Tuple, S); @@ -1365,10 +1361,37 @@ infer_types(Src, Vst) -> Infer(Val, S); (_, S) -> S end; + {bif,is_atom,{f,_},[Src],_} -> + infer_type_test_bif({atom,[]}, Src); + {bif,is_boolean,{f,_},[Src],_} -> + infer_type_test_bif(bool, Src); + {bif,is_binary,{f,_},[Src],_} -> + infer_type_test_bif(binary, Src); + {bif,is_bitstring,{f,_},[Src],_} -> + infer_type_test_bif(binary, Src); + {bif,is_float,{f,_},[Src],_} -> + infer_type_test_bif(float, Src); + {bif,is_integer,{f,_},[Src],_} -> + infer_type_test_bif({integer,{}}, Src); + {bif,is_list,{f,_},[Src],_} -> + infer_type_test_bif(list, Src); + {bif,is_map,{f,_},[Src],_} -> + infer_type_test_bif(map, Src); + {bif,is_number,{f,_},[Src],_} -> + infer_type_test_bif(number, Src); + {bif,is_tuple,{f,_},[Src],_} -> + infer_type_test_bif({tuple,[],#{}}, Src); _ -> fun(_, S) -> S end end. +infer_type_test_bif(Type, Src) -> + fun({atom,true}, S) -> + update_type(fun meet/2, Type, Src, S); + (_, S) -> + S + end. + %%% %%% Keeping track of types. %%% -- cgit v1.2.3 From ed1eb246a7573d8cfb440e08644e035aad3cbe44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Mon, 18 Feb 2019 15:42:04 +0100 Subject: compiler: Allow disabling SSA passes in -compile() directives This fixes compiling against older OTP versions with the +r?VSN options, which often expand to disabling certain SSA passes to avoid new instructions. --- lib/compiler/src/compile.erl | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/lib/compiler/src/compile.erl b/lib/compiler/src/compile.erl index 53d3cec2d7..c281af57a1 100644 --- a/lib/compiler/src/compile.erl +++ b/lib/compiler/src/compile.erl @@ -827,20 +827,21 @@ kernel_passes() -> {pass,beam_kernel_to_ssa}, {iff,dssa,{listing,"ssa"}}, {iff,ssalint,{pass,beam_ssa_lint}}, - {unless,no_share_opt,{pass,beam_ssa_share}}, - {iff,dssashare,{listing,"ssashare"}}, - {iff,ssalint,{pass,beam_ssa_lint}}, - {unless,no_bsm_opt,{pass,beam_ssa_bsm}}, - {iff,dssabsm,{listing,"ssabsm"}}, - {iff,ssalint,{pass,beam_ssa_lint}}, - {unless,no_fun_opt,{pass,beam_ssa_funs}}, - {iff,dssafuns,{listing,"ssafuns"}}, - {iff,ssalint,{pass,beam_ssa_lint}}, - {unless,no_ssa_opt,{pass,beam_ssa_opt}}, - {iff,dssaopt,{listing,"ssaopt"}}, - {iff,ssalint,{pass,beam_ssa_lint}}, - {unless,no_recv_opt,{pass,beam_ssa_recv}}, - {iff,drecv,{listing,"recv"}}, + {delay, + [{unless,no_share_opt,{pass,beam_ssa_share}}, + {iff,dssashare,{listing,"ssashare"}}, + {iff,ssalint,{pass,beam_ssa_lint}}, + {unless,no_bsm_opt,{pass,beam_ssa_bsm}}, + {iff,dssabsm,{listing,"ssabsm"}}, + {iff,ssalint,{pass,beam_ssa_lint}}, + {unless,no_fun_opt,{pass,beam_ssa_funs}}, + {iff,dssafuns,{listing,"ssafuns"}}, + {iff,ssalint,{pass,beam_ssa_lint}}, + {unless,no_ssa_opt,{pass,beam_ssa_opt}}, + {iff,dssaopt,{listing,"ssaopt"}}, + {iff,ssalint,{pass,beam_ssa_lint}}, + {unless,no_recv_opt,{pass,beam_ssa_recv}}, + {iff,drecv,{listing,"recv"}}]}, {pass,beam_ssa_pre_codegen}, {iff,dprecg,{listing,"precodegen"}}, {iff,ssalint,{pass,beam_ssa_lint}}, -- cgit v1.2.3