Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 50 additions & 5 deletions lib/compiler/src/beam_bounds.erl
Original file line number Diff line number Diff line change
Expand Up @@ -147,16 +147,38 @@ bounds('rem', R1, R2) ->
rem_bounds(R1, R2);
bounds('band', 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,
{{A,B}, {C,D}} when is_integer(A), A >= 0, A bsr ?NUM_BITS =:= 0,
is_integer(C), C >= 0, C bsr ?NUM_BITS =:= 0,
is_integer(B), is_integer(D) ->
Min = min_band(A, B, C, D),
Max = max_band(A, B, C, D),
{Min,Max};
{_, {C,D}} when is_integer(C), C >= 0 ->
{0,D};
{{A,B}, {C,D}} when is_integer(A), A >= 0,
is_integer(C), C >= 0 ->
%% Both non-negative, but some bounds exceed ?NUM_BITS.
%% x band y =< min(x, y).
{0, cap(inf_min(B, D))};
{{_,B}, {_,D}} when is_integer(B), B < 0,
is_integer(D), D < 0 ->
%% Both strictly negative. Result is always <= min(B, D).
%% When min(B,D) exceeds NUM_BITS, widen to -1 (least
%% negative), not '-inf'.
Max = case inf_min(B, D) of
M when is_integer(M), abs(M) bsr ?NUM_BITS =/= 0 -> -1;
M -> M
end,
{'-inf', Max};
{{A,B}, _} when is_integer(A), A >= 0 ->
{0,B};
%% First non-negative. Result is non-negative, bounded by the first operand.
{0, cap(B)};
{_, {C,D}} when is_integer(C), C >= 0 ->
%% Second non-negative. Result is non-negative, bounded by the second operand.
{0, cap(D)};
{{_,B}, {_,D}} when is_integer(B), B >= 0,
is_integer(D), D >= 0 ->
%% Both ranges straddle zero with non-negative upper
%% bounds. The upper bound of band cannot exceed max(B, D).
{'-inf', cap(inf_max(B, D))};
{_, _} ->
any
end;
Expand All @@ -169,6 +191,14 @@ bounds('bor', R1, R2) ->
Min = min_bor(A, B, C, D),
Max = max_bor(A, B, C, D),
normalize({Min,Max});
{{A,_}, {C,_}} when is_integer(A), A >= 0,
is_integer(C), C >= 0 ->
%% Both non-negative, but some bounds exceed ?NUM_BITS.
{0, '+inf'};
{{_,B}, {_,D}} when is_integer(B), B =< 0,
is_integer(D), D =< 0 ->
%% Both non-positive, but some bounds exceed ?NUM_BITS.
{'-inf', 0};
{_, _} ->
any
end;
Expand All @@ -179,6 +209,15 @@ bounds('bxor', R1, R2) ->
is_integer(B), is_integer(D) ->
Max = max_bxor(A, B, C, D),
{0,Max};
{{A,_}, {C,_}} when is_integer(A), A >= 0,
is_integer(C), C >= 0 ->
%% Both non-negative, but some bounds exceed ?NUM_BITS.
{0, '+inf'};
{{A,B}, {C,D}} when (is_integer(B) andalso B < 0 andalso is_integer(C) andalso C >= 0) orelse
(is_integer(A) andalso A >= 0 andalso is_integer(D) andalso D < 0) ->
%% One strictly negative, other non-negative.
%% XOR of different sign bits always yields negative.
{'-inf', -1};
{_, _} ->
any
end;
Expand Down Expand Up @@ -548,6 +587,12 @@ inf_neg(N) -> -N.
inf_add(Int, N) when is_integer(Int) -> Int + N;
inf_add(Inf, _N) -> Inf.

%% Cap a value to '+inf' or '-inf' if it exceeds ?NUM_BITS.
cap(A) when A =:= '-inf'; A =:= '+inf' -> A;
cap(N) when is_integer(N), N >= 0, N bsr ?NUM_BITS =/= 0 -> '+inf';
cap(N) when is_integer(N), N < 0, (-N) bsr ?NUM_BITS =/= 0 -> '-inf';
cap(N) -> N.

inf_bsr('-inf', _S) ->
'-inf';
inf_bsr('+inf', _S) ->
Expand Down
73 changes: 72 additions & 1 deletion lib/compiler/test/beam_bounds_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -191,8 +191,43 @@ band_bounds(_Config) ->
{0,42} = beam_bounds:bounds('band', {0,42}, any),
any = beam_bounds:bounds('band', {-1,1}, any),
any = beam_bounds:bounds('band', any, {-10,0}),
any = beam_bounds:bounds('band', {-10,0}, {-1,10}),
any = beam_bounds:bounds('band', {-20,-10}, {-1,10}),
{'-inf',10} = beam_bounds:bounds('band', {-10,0}, {-1,10}),

%% band upper bound should be min(B, D), not just D or B.
%% x band y =< min(x, y) when both non-negative.
{0,5} = beam_bounds:bounds('band', {0,5}, {0,'+inf'}),
{0,5} = beam_bounds:bounds('band', {0,'+inf'}, {0,5}),
{0,10} = beam_bounds:bounds('band', {0,10}, {0,20}),

%% One strictly non-negative, one strictly negative.
%% Result is bounded by [0, PositiveMax].
{0,10} = beam_bounds:bounds('band', {0,10}, {-20,-5}),
{0,10} = beam_bounds:bounds('band', {-20,-5}, {0,10}),

%% Both strictly negative.
%% Lower bound drops to '-inf', upper bound is min(B, D).
{'-inf',-10} = beam_bounds:bounds('band', {-20,-10}, {-15,-5}),
{'-inf',-10} = beam_bounds:bounds('band', {-15,-5}, {-20,-10}),

%% Straddling zero.
%% Lower bound fails open. Upper bound is max of the positive bounds.
{'-inf',10} = beam_bounds:bounds('band', {-5,10}, {-20,5}),
{'-inf',20} = beam_bounds:bounds('band', {-10,20}, {-10,20}),

%% Large bounds exceeding ?NUM_BITS.
%% min(Big, +inf) = Big, but Big exceeds ?NUM_BITS so widens to +inf.
Big = 1 bsl 512,
{0,'+inf'} = beam_bounds:bounds('band', {0,Big}, {0,'+inf'}),
{0,'+inf'} = beam_bounds:bounds('band', {0,'+inf'}, {0,Big}),
{'-inf',-1} = beam_bounds:bounds('band', {-Big,-1}, {-Big,-1}),
{'-inf',-1} = beam_bounds:bounds('band', {-Big,-Big-1}, {-Big,-Big}),
{0,'+inf'} = beam_bounds:bounds('band', {0, Big}, {-Big,Big}),
{0,'+inf'} = beam_bounds:bounds('band', {-Big, -Big}, {0, Big}),

%% Large negative operand, small positive operand.
%% The small positive operand perfectly constrains the result.
{0,5} = beam_bounds:bounds('band', {0,5}, {-Big,-1}),

ok.

Expand All @@ -212,6 +247,20 @@ bor_bounds(_Config) ->
{16,'+inf'} = beam_bounds:bounds('bor', {0,8}, {16,'+inf'}),
{16,'+inf'} = beam_bounds:bounds('bor', {3,'+inf'}, {16,'+inf'}),

%% Both non-negative, some bounds exceeding ?NUM_BITS.
%% Bounds that exceed ?NUM_BITS are widened to +inf/-inf.
Big = 1 bsl 512,
{0,'+inf'} = beam_bounds:bounds('bor', {0,'+inf'}, {Big,Big}),
{0,'+inf'} = beam_bounds:bounds('bor', {0,Big}, {0,'+inf'}),
{0,'+inf'} = beam_bounds:bounds('bor', {0,Big}, {Big,Big}),
{0,'+inf'} = beam_bounds:bounds('bor', {Big,Big}, {Big,Big}),

%% Both non-positive, some bounds exceeding ?NUM_BITS.
{'-inf',0} = beam_bounds:bounds('bor', {'-inf', 0}, {-Big,-Big}),
{'-inf',0} = beam_bounds:bounds('bor', {-Big,0}, {'-inf',0}),
{'-inf',0} = beam_bounds:bounds('bor', {-Big,0}, {-Big,0}),
{'-inf',0} = beam_bounds:bounds('bor', {-Big,-Big}, {-Big,0}),

ok.

bxor_bounds(_Config) ->
Expand All @@ -220,6 +269,28 @@ bxor_bounds(_Config) ->
any = beam_bounds:bounds('bxor', {-10,0}, {-1,10}),
any = beam_bounds:bounds('bxor', {-20,-10}, {-1,10}),

%% Both non-negative, at least one infinite upper bound,
%% with finite bounds exceeding ?NUM_BITS.
Big = 1 bsl 512,
{0,'+inf'} = beam_bounds:bounds('bxor', {0,'+inf'}, {Big,Big}),
{0,'+inf'} = beam_bounds:bounds('bxor', {0,Big}, {0,'+inf'}),

%% Both non-negative, both finite, exceeding ?NUM_BITS.
{0,'+inf'} = beam_bounds:bounds('bxor', {0,Big}, {Big,Big}),
{0,'+inf'} = beam_bounds:bounds('bxor', {Big,Big}, {Big,Big}),

%% One strictly negative, one strictly non-negative.
%% The result is guaranteed to be negative, so the upper bound is -1.
{'-inf',-1} = beam_bounds:bounds('bxor', {-Big,-1}, {0,Big}),
{'-inf',-1} = beam_bounds:bounds('bxor', {0,Big}, {-Big,-1}),

%% One strictly negative, one strictly non-negative (with infinite inputs).
{'-inf',-1} = beam_bounds:bounds('bxor', {'-inf',-1}, {0,'+inf'}),
{'-inf',-1} = beam_bounds:bounds('bxor', {0,'+inf'}, {'-inf',-1}),

%% Both finite constants exceeding ?NUM_BITS, signs mismatched.
{'-inf',-1} = beam_bounds:bounds('bxor', {-Big,-Big}, {Big,Big}),

ok.

bnot_bounds(_Config) ->
Expand Down
Loading