Skip to content

Commit

Permalink
Enhance range analysis for the bor operator
Browse files Browse the repository at this point in the history
Teach beam_bounds:bounds/3 to calculate a range for the `bor` operator
for negative or partly unbounded arguments. For example:

    bor0(A) when is_integer(A), -10 =< A, A =< 10 ->
        %% Range for A is -10..10
        A bor 1.                % Range is '-inf'..11

    bor1(A, B) when is_integer(A), A < 0, is_integer(B), B < 0 ->
        %% Range for A and B is '-inf'..-1
        A bor B.                % Range is '-inf'..-1

    bor2(A, B) when is_integer(A), A >= 1, is_integer(B), B >= 10 ->
        %% Range for A is 1..'+inf'; range for B is 10..'+inf'
        A bor B.                % Range is 10..'+inf'
  • Loading branch information
bjorng committed Aug 15, 2023
1 parent a71c2c6 commit 7e33bac
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 12 deletions.
39 changes: 29 additions & 10 deletions lib/compiler/src/beam_bounds.erl
Original file line number Diff line number Diff line change
Expand Up @@ -156,12 +156,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 Down Expand Up @@ -379,8 +380,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 @@ -404,10 +410,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
14 changes: 12 additions & 2 deletions lib/compiler/test/beam_bounds_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -181,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

0 comments on commit 7e33bac

Please sign in to comment.