From 904dfa9fd76b1dda1f8565219f3fe6c386f8aa6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 24 Apr 2019 07:22:08 +0200 Subject: Optimize is_function/{1,2} calls Remove is_function/1,2 tests if that are known to never fail. --- lib/compiler/src/beam_ssa_type.erl | 78 ++++++++++++++++++++++++++++++++------ 1 file changed, 67 insertions(+), 11 deletions(-) (limited to 'lib/compiler/src/beam_ssa_type.erl') diff --git a/lib/compiler/src/beam_ssa_type.erl b/lib/compiler/src/beam_ssa_type.erl index 82371f4ba3..15f646fcb7 100644 --- a/lib/compiler/src/beam_ssa_type.erl +++ b/lib/compiler/src/beam_ssa_type.erl @@ -41,8 +41,9 @@ %% Records that represent type information. -record(t_atom, {elements=any :: 'any' | [atom()]}). --record(t_integer, {elements=any :: 'any' | {integer(),integer()}}). -record(t_bs_match, {type :: type()}). +-record(t_fun, {arity=any :: arity() | 'any'}). +-record(t_integer, {elements=any :: 'any' | {integer(),integer()}}). -record(t_tuple, {size=0 :: integer(), exact=false :: boolean(), %% Known element types (1-based index), unknown elements are @@ -50,8 +51,9 @@ elements=#{} :: #{ non_neg_integer() => type() }}). -type type() :: 'any' | 'none' | - #t_atom{} | #t_integer{} | #t_bs_match{} | #t_tuple{} | - {'binary',pos_integer()} | 'cons' | 'float' | 'list' | 'map' | 'nil' | 'number'. + #t_atom{} | #t_bs_match{} | #t_fun{} | #t_integer{} | #t_tuple{} | + {'binary',pos_integer()} | 'cons' | 'float' | + 'list' | 'map' | 'nil' | 'number'. -type type_db() :: #{beam_ssa:var_name():=type()}. -spec opt_start(Linear, Args, Anno, FuncDb) -> {Linear, FuncDb} when @@ -157,21 +159,29 @@ opt_finish_1([Arg | Args], [TypeMap | TypeMaps], ParamInfo) map_size(TypeMap) =:= 0 -> opt_finish_1(Args, TypeMaps, ParamInfo); opt_finish_1([Arg | Args], [TypeMap | TypeMaps], ParamInfo0) -> - case join(maps:values(TypeMap)) of + JoinedType0 = verified_type(join(maps:values(TypeMap))), + case validator_anno(JoinedType0) of any -> opt_finish_1(Args, TypeMaps, ParamInfo0); JoinedType -> - JoinedType = verified_type(JoinedType), - ParamInfo = ParamInfo0#{ Arg => validator_anno(JoinedType) }, + ParamInfo = ParamInfo0#{ Arg => JoinedType }, opt_finish_1(Args, TypeMaps, ParamInfo) end; opt_finish_1([], [], ParamInfo) -> ParamInfo. +validator_anno(any) -> + any; +validator_anno(#t_fun{}) -> + %% There is no need make funs visible to beam_validator. + any; validator_anno(#t_tuple{size=Size,exact=Exact,elements=Elements0}) -> - Elements = maps:fold(fun(Index, Type, Acc) -> + Elements = maps:fold(fun(Index, Type0, Acc) -> Key = beam_validator:type_anno(integer, Index), - Acc#{ Key => validator_anno(Type) } + case validator_anno(Type0) of + any -> Acc; + Type -> Acc#{Key=>Type} + end end, #{}, Elements0), beam_validator:type_anno(tuple, Size, Exact, Elements); validator_anno(#t_integer{elements={Same,Same}}) -> @@ -413,6 +423,11 @@ simplify_remote_call(Mod, Name, Args0, I) -> end end. +opt_call(#b_set{dst=Dst,args=[#b_var{}=Fun|Args]}=I, _D, Ts0, Ds0, Fdb) -> + Type = #t_fun{arity=length(Args)}, + Ts = Ts0#{ Fun => Type, Dst => any }, + Ds = Ds0#{ Dst => I }, + {Ts, Ds, Fdb, I}; opt_call(#b_set{dst=Dst,args=[#b_local{}=Callee|Args]}=I0, D, Ts0, Ds0, Fdb0) -> {Ts, Ds, I} = opt_local_call(I0, Ts0, Ds0, Fdb0), case Fdb0 of @@ -440,9 +455,15 @@ opt_local_call(#b_set{dst=Dst,args=[Id|_]}=I0, Ts0, Ds0, Fdb) -> #{} -> any end, I = case Type of - any -> I0; - none -> I0; - _ -> beam_ssa:add_anno(result_type, validator_anno(Type), I0) + none -> + I0; + _ -> + case validator_anno(Type) of + any -> + I0; + ValidatorType -> + beam_ssa:add_anno(result_type, ValidatorType, I0) + end end, Ts = Ts0#{ Dst => Type }, Ds = Ds0#{ Dst => I }, @@ -519,6 +540,18 @@ simplify(#b_set{op={bif,tuple_size},args=[Term]}=I, Ts) -> _ -> I end; +simplify(#b_set{op={bif,is_function},args=[Fun,#b_literal{val=Arity}]}=I, Ts) + when is_integer(Arity), Arity >= 0 -> + case get_type(Fun, Ts) of + #t_fun{arity=any} -> + I; + #t_fun{arity=Arity} -> + #b_literal{val=true}; + any -> + I; + _ -> + #b_literal{val=false} + end; simplify(#b_set{op={bif,Op0},args=Args}=I, Ts) when Op0 =:= '=='; Op0 =:= '/=' -> Types = get_types(Args, Ts), EqEq0 = case {meet(Types),join(Types)} of @@ -913,6 +946,13 @@ type(bs_get_tail, _Args, _Ts, _Ds) -> type(call, [#b_remote{mod=#b_literal{val=Mod}, name=#b_literal{val=Name}}|Args], Ts, _Ds) -> case {Mod,Name,Args} of + {erlang,make_fun,[_,_,Arity0]} -> + case Arity0 of + #b_literal{val=Arity} when is_integer(Arity), Arity >= 0 -> + #t_fun{arity=Arity}; + _ -> + #t_fun{} + end; {erlang,setelement,[Pos,Tuple,Arg]} -> case {get_type(Pos, Ts),get_type(Tuple, Ts)} of {#t_integer{elements={Index,Index}}, @@ -981,6 +1021,8 @@ type(is_nonempty_list, [_], _Ts, _Ds) -> t_boolean(); type(is_tagged_tuple, [_,#b_literal{},#b_literal{}], _Ts, _Ds) -> t_boolean(); +type(make_fun, [#b_local{arity=TotalArity}|Env], _Ts, _Ds) -> + #t_fun{arity=TotalArity-length(Env)}; type(put_map, _Args, _Ts, _Ds) -> map; type(put_list, _Args, _Ts, _Ds) -> @@ -1162,6 +1204,11 @@ will_succeed(is_float, Type) -> number -> maybe; _ -> no end; +will_succeed(is_function, Type) -> + case Type of + #t_fun{} -> yes; + _ -> no + end; will_succeed(is_integer, Type) -> case Type of #t_integer{} -> yes; @@ -1401,6 +1448,9 @@ get_type(#b_literal{val=Val}, _Ts) -> t_atom(Val); is_float(Val) -> float; + is_function(Val) -> + {arity,Arity} = erlang:fun_info(Val, arity), + #t_fun{arity=Arity}; is_integer(Val) -> t_integer(Val); is_list(Val), Val =/= [] -> @@ -1790,6 +1840,7 @@ join(#t_atom{elements=any}=T, #t_atom{elements=[_|_]}) -> T; join(#t_atom{elements=[_|_]}, #t_atom{elements=any}=T) -> T; join({binary,U1}, {binary,U2}) -> {binary,gcd(U1, U2)}; +join(#t_fun{}, #t_fun{}) -> #t_fun{}; join(#t_integer{}, #t_integer{}) -> t_integer(); join(list, cons) -> list; join(cons, list) -> list; @@ -1907,6 +1958,10 @@ meet(#t_atom{elements=[_|_]}=T, #t_atom{elements=any}) -> T; meet(#t_atom{elements=any}, #t_atom{elements=[_|_]}=T) -> T; +meet(#t_fun{arity=any}, #t_fun{}=T) -> + T; +meet(#t_fun{}=T, #t_fun{arity=any}) -> + T; meet(#t_integer{elements={_,_}}=T, #t_integer{elements=any}) -> T; meet(#t_integer{elements=any}, #t_integer{elements={_,_}}=T) -> @@ -1996,6 +2051,7 @@ verified_type(none=T) -> T; verified_type(#t_atom{elements=any}=T) -> T; verified_type(#t_atom{elements=[_|_]}=T) -> T; verified_type({binary,U}=T) when is_integer(U) -> T; +verified_type(#t_fun{arity=Arity}=T) when Arity =:= any; is_integer(Arity) -> T; verified_type(#t_integer{elements=any}=T) -> T; verified_type(#t_integer{elements={Min,Max}}=T) when is_integer(Min), is_integer(Max) -> T; -- cgit v1.2.3