Skip to content

Commit

Permalink
Merge pull request #7563 from bjorng/bjorn/compiler/types
Browse files Browse the repository at this point in the history
Enhance type analysis
  • Loading branch information
bjorng authored Aug 17, 2023
2 parents b5e045c + c5979b0 commit 0a5b4ad
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 18 deletions.
56 changes: 44 additions & 12 deletions lib/compiler/src/beam_bounds.erl
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,9 @@ bounds('*', R1, R2) ->
Min = lists:min(All),
Max = lists:max(All),
normalize({Min,Max});
{{A,'+inf'}, {C,'+inf'}} when abs(A) bsr ?NUM_BITS =:= 0, A >= 0,
abs(C) bsr ?NUM_BITS =:= 0, C >= 0 ->
{A*C,'+inf'};
{{A,'+inf'}, {C,D}} when abs(A) bsr ?NUM_BITS =:= 0,
abs(C) bsr ?NUM_BITS =:= 0,
abs(D) bsr ?NUM_BITS =:= 0,
Expand Down Expand Up @@ -156,12 +159,13 @@ bounds('band', R1, R2) ->
end;
bounds('bor', R1, R2) ->
case {R1,R2} of
{{A,B}, {C,D}} when A bsr ?NUM_BITS =:= 0, A >= 0,
C bsr ?NUM_BITS =:= 0, C >= 0,
is_integer(B), is_integer(D) ->
{{A,B}, {C,D}} when A =:= '-inf' orelse abs(A) bsr ?NUM_BITS =:= 0,
C =:= '-inf' orelse abs(C) bsr ?NUM_BITS =:= 0,
B =:= '+inf' orelse abs(B) bsr ?NUM_BITS =:= 0,
D =:= '+inf' orelse abs(D) bsr ?NUM_BITS =:= 0 ->
Min = min_bor(A, B, C, D),
Max = max_bor(A, B, C, D),
{Min,Max};
normalize({Min,Max});
{_, _} ->
any
end;
Expand All @@ -186,8 +190,8 @@ bounds('bsr', R1, R2) ->
end;
bounds('bsl', R1, R2) ->
case {R1,R2} of
{{A,B}, {C,D}} when abs(A) bsr ?NUM_BITS =:= 0,
abs(B) bsr ?NUM_BITS =:= 0 ->
{{A,B}, {C,D}} when A =:= '-inf' orelse abs(A) bsr ?NUM_BITS =:= 0,
B =:= '+inf' orelse abs(B) bsr ?NUM_BITS =:= 0 ->
Min = inf_min(inf_bsl(A, C), inf_bsl(A, D)),
Max = inf_max(inf_bsl(B, C), inf_bsl(B, D)),
normalize({Min,Max});
Expand Down Expand Up @@ -299,6 +303,10 @@ div_bounds({'-inf',B}, {C,D}) when is_integer(C), C > 0, is_integer(D) ->
Min = '-inf',
Max = max(B div C, B div D),
normalize({Min,Max});
div_bounds({A,B}, _) when is_integer(A), is_integer(B) ->
Max = max(abs(A), abs(B)),
Min = -Max,
{Min,Max};
div_bounds(_, _) ->
any.

Expand All @@ -315,6 +323,12 @@ rem_bounds(_, {C,D}) when is_integer(C), is_integer(D),
Max = max(abs(C), abs(D)) - 1,
Min = -Max,
normalize({Min,Max});
rem_bounds({A,B}, _) ->
%% The sign of the remainder is the same as the sign of the
%% left-hand side operand; it does not depend on the sign of the
%% right-hand side operand. Therefore, the range of the remainder
%% is the same as the range of the left-hand side operand.
{A,B};
rem_bounds(_, _) ->
any.

Expand Down Expand Up @@ -369,8 +383,13 @@ max_band(A, B, C, D, M) ->
end.

min_bor(A, B, C, D) ->
M = 1 bsl upper_bit(A bxor C),
min_bor(A, B, C, D, M).
case inf_lt(inf_min(A, C), 0) of
true ->
'-inf';
false ->
M = 1 bsl upper_bit(A bxor C),
min_bor(A, B, C, D, M)
end.

min_bor(A, _B, C, _D, 0) ->
A bor C;
Expand All @@ -394,10 +413,23 @@ min_bor(A, B, C, D, M) ->
min_bor(A, B, C, D, M bsr 1)
end.

max_bor(A, B, C, D) ->
Intersection = B band D,
M = 1 bsl upper_bit(Intersection),
max_bor(Intersection, A, B, C, D, M).
max_bor(A0, B, C0, D) ->
A = inf_max(A0, 0),
C = inf_max(C0, 0),
case inf_max(B, D) of
'+inf' ->
'+inf';
Max when Max < 0 ->
%% Both B and D are negative. The intersection would be
%% infinite.
-1;
_ ->
%% At least one of B and D are positive. The intersection
%% has a finite size.
Intersection = B band D,
M = 1 bsl upper_bit(Intersection),
max_bor(Intersection, A, B, C, D, M)
end.

max_bor(_Intersection, _A, B, _C, D, 0) ->
B bor D;
Expand Down
72 changes: 72 additions & 0 deletions lib/compiler/src/beam_call_types.erl
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,24 @@ types(erlang, Op, [LHS, RHS]) when Op =:= '+'; Op =:= '-' ->
mixed_arith_types([LHS, RHS])
end;

types(erlang, '*', [LHS, RHS]) ->
case get_range(LHS, RHS, #t_number{}) of
{Type, {A,B}, {C,D}} ->
case beam_bounds:bounds('*', {A,B}, {C,D}) of
{Min,_Max} when is_integer(Min), Min >= 0 ->
R = {Min,'+inf'},
RetType = case Type of
integer -> #t_integer{elements=R};
number -> #t_number{elements=R}
end,
sub_unsafe(RetType, [#t_number{}, #t_number{}]);
_ ->
mixed_arith_types([LHS, RHS])
end;
_ ->
mixed_arith_types([LHS, RHS])
end;

types(erlang, abs, [Type]) ->
case meet(Type, #t_number{}) of
#t_float{} ->
Expand Down Expand Up @@ -562,6 +580,50 @@ types(erlang, 'spawn_monitor', [_, _, _]) ->
types(erlang, 'spawn_request', [_ | _]=Args) when length(Args) =< 5 ->
sub_unsafe(reference, [any || _ <- Args]);

%% Conversion functions.
types(erlang, atom_to_binary, [_]) ->
sub_safe(binary(), [#t_atom{}]);
types(erlang, binary_to_integer, [_]) ->
sub_unsafe(#t_integer{}, [binary()]);
types(erlang, binary_to_list, [_]) ->
sub_safe(proper_list(), [binary()]);
types(erlang, integer_to_list, [_]) ->
sub_safe(proper_cons(), [#t_integer{}]);
types(erlang, list_to_atom, [_]) ->
sub_unsafe(#t_atom{}, [#t_list{}]);
types(erlang, list_to_tuple, [Arg]) ->
Sz = case meet(Arg, #t_list{}) of
#t_cons{} -> 1;
_ -> 0
end,
sub_unsafe(#t_tuple{size=Sz}, [#t_list{}]);
types(erlang, term_to_binary, [_]) ->
sub_unsafe(binary(), [any]);
types(erlang, tuple_to_list, [Arg]) ->
T = case meet(Arg, #t_tuple{}) of
#t_tuple{size=Sz} when Sz >= 1 ->
proper_cons();
_ ->
proper_list()
end,
sub_safe(T, [#t_tuple{}]);

%% Misc functions returning integers.
types(erlang, convert_time_unit, [_, _, _]) ->
sub_unsafe(#t_integer{}, [any, any, any]);
types(erlang, monotonic_time, []) ->
sub_unsafe(#t_integer{}, []);
types(erlang, phash2, [_]) ->
R = {0, (1 bsl 27) - 1},
sub_unsafe(#t_integer{elements=R}, [any]);
types(erlang, phash2, [_, _]) ->
R = {0, (1 bsl 32) - 1},
sub_unsafe(#t_integer{elements=R}, [any, any]);
types(erlang, unique_integer, []) ->
sub_unsafe(#t_integer{}, []);
types(erlang, unique_integer, [_]) ->
sub_unsafe(#t_integer{}, [any]);

%% Misc ops.
types(erlang, 'binary_part', [_, _]) ->
PosLen = make_two_tuple(#t_integer{}, #t_integer{}),
Expand Down Expand Up @@ -590,6 +652,13 @@ types(erlang, self, []) ->
types(erlang, 'size', [_]) ->
ArgType = join(#t_tuple{}, #t_bitstring{}),
sub_unsafe(#t_integer{}, [ArgType]);
types(erlang, split_binary, [_, _]) ->
%% Note that, contrary to the documentation at the time of writing,
%% split_binary/2 accepts a bitstring and that it can return a
%% bitstring in the second element of the result tuple.
Binary = binary(),
T = make_two_tuple(Binary, #t_bitstring{}),
sub_unsafe(T, [#t_bitstring{}, #t_integer{}]);

%% Tuple element ops
types(erlang, element, [Pos, Tuple0]) ->
Expand Down Expand Up @@ -1384,6 +1453,9 @@ proper_list() ->
proper_list(ElementType) ->
#t_list{type=ElementType,terminator=nil}.

binary() ->
#t_bitstring{size_unit=8}.

%% Constructs a new list type based on another, optionally keeping the same
%% length and/or making it proper.
-spec copy_list(List, Length, Proper) -> type() when
Expand Down
42 changes: 36 additions & 6 deletions lib/compiler/test/beam_bounds_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,13 @@ division_bounds(_Config) ->
any = beam_bounds:bounds('div', {10,'+inf'}, {0,0}),
{'EXIT', {badarith, _}} = catch division_bounds_1([], ok),

{-10,10} = beam_bounds:bounds('div', {0,10}, any),
{-50,50} = beam_bounds:bounds('div', {-50,-15}, {-10,'+inf'}),
{-20,20} = beam_bounds:bounds('div', {-20,10}, any),
{-7,7} = beam_bounds:bounds('div', {-5,7}, {'-inf',-1}),
any = beam_bounds:bounds('div', {'-inf',10}, any),
any = beam_bounds:bounds('div', {0,'+inf'}, any),

ok.

%% GH-6604: Division by zero could cause type analysis to hang forever as
Expand All @@ -147,11 +154,14 @@ rem_bounds(_Config) ->
{-7,7} = beam_bounds:bounds('rem', {'-inf',10}, {1,8}),
{0,7} = beam_bounds:bounds('rem', {10,'+inf'}, {1,8}),

any = beam_bounds:bounds('rem', {1,10}, {'-inf',10}),
any = beam_bounds:bounds('rem', {1,10}, {10,'+inf'}),
{1,10} = beam_bounds:bounds('rem', {1,10}, {'-inf',10}),
{20,'+inf'} = beam_bounds:bounds('rem', {20,'+inf'}, {10,'+inf'}),
{'-inf',10} = beam_bounds:bounds('rem', {'-inf',10}, any),

any = beam_bounds:bounds('rem', {-10,10}, {'-inf',10}),
any = beam_bounds:bounds('rem', {-10,10}, {10,'+inf'}),
{-11,10} = beam_bounds:bounds('rem', {-11,10}, {'-inf',89}),
{-11,10} = beam_bounds:bounds('rem', {-11,10}, {7,'+inf'}),
{-11,10} = beam_bounds:bounds('rem', {-11,10}, {'-inf',113}),
{-11,10} = beam_bounds:bounds('rem', {-11,10}, {55,'+inf'}),

ok.

Expand All @@ -171,8 +181,18 @@ band_bounds(_Config) ->
bor_bounds(_Config) ->
test_commutative('bor'),

any = beam_bounds:bounds('bor', {-10,0},{-1,10}),
any = beam_bounds:bounds('bor', {-20,-10}, {-1,10}),
{'-inf',15} = beam_bounds:bounds('bor', {-10,7},{3,10}),
{'-inf',11} = beam_bounds:bounds('bor', {-10,1},{-1,10}),
{'-inf',-1} = beam_bounds:bounds('bor', {-20,-10}, {-2,10}),

{'-inf',15} = beam_bounds:bounds('bor', {'-inf',10}, {3,5}),
{'-inf',-1} = beam_bounds:bounds('bor', {-20,-10}, {-100,-50}),

any = beam_bounds:bounds('bor', {-20,-10}, {-2,'+inf'}),
any = beam_bounds:bounds('bor', {-20,'+inf'}, {-7,-3}),

{16,'+inf'} = beam_bounds:bounds('bor', {0,8}, {16,'+inf'}),
{16,'+inf'} = beam_bounds:bounds('bor', {3,'+inf'}, {16,'+inf'}),

ok.

Expand Down Expand Up @@ -257,8 +277,13 @@ bsl_bounds(_Config) ->

{2,'+inf'} = beam_bounds:bounds('bsl', {1,10}, {1,10_000}),
{0,'+inf'} = beam_bounds:bounds('bsl', {1,10}, {-10,10_000}),
{'-inf',-20} = beam_bounds:bounds('bsl', {-30,-10}, {1,10_000}),
{'-inf',-2} = beam_bounds:bounds('bsl', {-9,-1}, {1,10_000}),
any = beam_bounds:bounds('bsl', {-7,10}, {1,10_000}),

{0,'+inf'} = beam_bounds:bounds('bsl', {0,'+inf'}, {0,'+inf'}),
{20,'+inf'} = beam_bounds:bounds('bsl', {20,30}, {0,'+inf'}),

any = beam_bounds:bounds('bsl', {-10,100}, {0,'+inf'}),
any = beam_bounds:bounds('bsl', {-10,100}, {1,'+inf'}),
any = beam_bounds:bounds('bsl', {-10,100}, {-1,'+inf'}),
Expand All @@ -271,6 +296,11 @@ bsl_bounds(_Config) ->
{'-inf',-1} = beam_bounds:bounds('bsl', {-10,-1}, {500,1024}),
{0,'+inf'} = beam_bounds:bounds('bsl', {1,10}, {500,1024}),

{'-inf',-40} = beam_bounds:bounds('bsl', {'-inf',-10}, {2,64}),
{'-inf',224} = beam_bounds:bounds('bsl', {'-inf',7}, {3,5}),

any = beam_bounds:bounds('bsl', {'-inf',7}, {3,'+inf'}),

ok.

lt_bounds(_Config) ->
Expand Down

0 comments on commit 0a5b4ad

Please sign in to comment.