Skip to content

Commit ec77c9e

Browse files
committed
beam_bounds: Simplify bounds computation for arithmetic operations
1 parent 6520b99 commit ec77c9e

2 files changed

Lines changed: 130 additions & 91 deletions

File tree

lib/compiler/src/beam_bounds.erl

Lines changed: 61 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@
4242
-type bool_result() :: 'true' | 'false' | 'maybe'.
4343
-type op() :: atom().
4444

45+
-import(lists, [foldl/3]).
46+
4547
%% Maximum size of integers in bits to keep ranges for.
4648
-define(NUM_BITS, 128).
4749

@@ -52,95 +54,51 @@ bounds('bnot', R0) ->
5254
R = {inf_add(inf_neg(B), -1), inf_add(inf_neg(A), -1)},
5355
normalize(R);
5456
bounds(abs, R) ->
55-
case R of
56-
{A,B} when is_integer(A), is_integer(B) ->
57-
Min = 0,
58-
Max = max(abs(A), abs(B)),
59-
{Min,Max};
60-
_ ->
61-
{0,'+inf'}
62-
end.
57+
[A,B] = canonical_arg(R),
58+
AbsA = inf_abs(A),
59+
AbsB = inf_abs(B),
60+
Min = case {inf_sign(A),inf_sign(B)} of
61+
{'-','+'} -> 0;
62+
{_,_} -> inf_min(AbsA, AbsB)
63+
end,
64+
Max = inf_max(AbsA, AbsB),
65+
normalize({Min,Max}).
6366

6467
-spec bounds(op(), range(), range()) -> range_result().
6568

6669
bounds('+', R1, R2) ->
67-
case {R1,R2} of
68-
{{A,B}, {C,D}} when abs(A) bsr ?NUM_BITS =:= 0,
69-
abs(B) bsr ?NUM_BITS =:= 0,
70-
abs(C) bsr ?NUM_BITS =:= 0,
71-
abs(D) bsr ?NUM_BITS =:= 0 ->
72-
normalize({A+C,B+D});
73-
{{'-inf',B}, {_C,D}} when abs(B) bsr ?NUM_BITS =:= 0,
74-
abs(D) bsr ?NUM_BITS =:= 0 ->
75-
normalize({'-inf',B+D});
76-
{{_A,B}, {'-inf',D}} when abs(B) bsr ?NUM_BITS =:= 0,
77-
abs(D) bsr ?NUM_BITS =:= 0 ->
78-
normalize({'-inf',B+D});
79-
{{A,'+inf'}, {C,_D}} when abs(A) bsr ?NUM_BITS =:= 0,
80-
abs(C) bsr ?NUM_BITS =:= 0 ->
81-
normalize({A+C,'+inf'});
82-
{{A,_B}, {C,'+inf'}} when abs(A) bsr ?NUM_BITS =:= 0,
83-
abs(C) bsr ?NUM_BITS =:= 0 ->
84-
normalize({A+C,'+inf'});
85-
{_, _} ->
86-
any
87-
end;
70+
[A,B,C,D] = canonical_args(R1, R2),
71+
normalize({inf_add(A, C), inf_add(B, D)});
8872
bounds('-', R1, R2) ->
89-
case {R1,R2} of
90-
{{A,B}, {C,D}} when abs(A) bsr ?NUM_BITS =:= 0,
91-
abs(B) bsr ?NUM_BITS =:= 0,
92-
abs(C) bsr ?NUM_BITS =:= 0,
93-
abs(D) bsr ?NUM_BITS =:= 0 ->
73+
case canonical_args(R1, R2) of
74+
[A,B,C,D] when is_integer(A),
75+
is_integer(B),
76+
is_integer(C),
77+
is_integer(D) ->
9478
normalize({A-D,B-C});
95-
{{A,'+inf'}, {_C,D}} when abs(A) bsr ?NUM_BITS =:= 0,
96-
abs(D) bsr ?NUM_BITS =:= 0 ->
79+
[A,'+inf',_C,D] when is_integer(A), is_integer(D) ->
9780
normalize({A-D,'+inf'});
98-
{{_A,B}, {C,'+inf'}} when abs(B) bsr ?NUM_BITS =:= 0,
99-
abs(C) bsr ?NUM_BITS =:= 0 ->
81+
[_A,B,C,'+inf'] when is_integer(B), is_integer(C) ->
10082
normalize({'-inf',B-C});
101-
{{'-inf',B}, {C,_D}} when abs(B) bsr ?NUM_BITS =:= 0,
102-
abs(C) bsr ?NUM_BITS =:= 0 ->
83+
['-inf',B,C,_D] when is_integer(B), is_integer(C) ->
10384
normalize({'-inf',B-C});
104-
{{A,_B}, {'-inf',D}} when abs(A) bsr ?NUM_BITS =:= 0,
105-
abs(D) bsr ?NUM_BITS =:= 0 ->
85+
[A,_B,'-inf',D] when is_integer(A), is_integer(D) ->
10686
normalize({A-D,'+inf'});
107-
{_, _} ->
87+
[_,_,_,_] ->
10888
any
10989
end;
11090
bounds('*', R1, R2) ->
111-
case {R1,R2} of
112-
{{A,B}, {C,D}} when abs(A) bsr ?NUM_BITS =:= 0,
113-
abs(B) bsr ?NUM_BITS =:= 0,
114-
abs(C) bsr ?NUM_BITS =:= 0,
115-
abs(D) bsr ?NUM_BITS =:= 0 ->
116-
All = [X * Y || X <- [A,B], Y <- [C,D]],
117-
Min = lists:min(All),
118-
Max = lists:max(All),
119-
normalize({Min,Max});
120-
{{A,'+inf'}, {C,'+inf'}} when abs(A) bsr ?NUM_BITS =:= 0, A >= 0,
121-
abs(C) bsr ?NUM_BITS =:= 0, C >= 0 ->
122-
{A*C,'+inf'};
123-
{{A,'+inf'}, {C,D}} when abs(A) bsr ?NUM_BITS =:= 0,
124-
abs(C) bsr ?NUM_BITS =:= 0,
125-
abs(D) bsr ?NUM_BITS =:= 0,
126-
C >= 0 ->
127-
{min(A*C, A*D),'+inf'};
128-
{{'-inf',B}, {C,D}} when abs(B) bsr ?NUM_BITS =:= 0,
129-
abs(C) bsr ?NUM_BITS =:= 0,
130-
abs(D) bsr ?NUM_BITS =:= 0,
131-
C >= 0 ->
132-
{'-inf',max(B*C, B*D)};
133-
{{A,B}, {'-inf',_}} when is_integer(A), is_integer(B) ->
134-
bounds('*', R2, R1);
135-
{{A,B}, {_,'+inf'}} when is_integer(A), is_integer(B) ->
136-
bounds('*', R2, R1);
137-
{_, _} ->
138-
any
139-
end;
91+
[A,B,C,D] = canonical_args(R1, R2),
92+
All = [inf_mul(X, Y) || X <- [A,B], Y <- [C,D]],
93+
Min = foldl(fun inf_min/2, '+inf', All),
94+
Max = foldl(fun inf_max/2, '-inf', All),
95+
normalize({Min,Max});
14096
bounds('div', R1, R2) ->
141-
div_bounds(R1, R2);
97+
[A,B,C,D] = canonical_args(R1, R2),
98+
div_bounds({A,B}, {C,D});
14299
bounds('rem', R1, R2) ->
143-
rem_bounds(R1, R2);
100+
[A,B,C,D] = canonical_args(R1, R2),
101+
rem_bounds({A,B}, {C,D});
144102
bounds('band', R1, R2) ->
145103
[A,B,C,D] = canonical_args(R1, R2),
146104
normalize(min_max_band(A, B, C, D));
@@ -255,13 +213,15 @@ div_bounds({A,B}, {C,D}) when is_integer(A), is_integer(B),
255213
Min = lists:min(All),
256214
Max = lists:max(All),
257215
normalize({Min,Max});
258-
div_bounds({A,'+inf'}, {C,D}) when is_integer(C), C > 0, is_integer(D) ->
259-
Min = min(A div C, A div D),
216+
div_bounds({A,'+inf'}, {C,D}) when is_integer(A),
217+
is_integer(C), C > 0 ->
218+
Min = min(A div C, inf_div(A, D)),
260219
Max = '+inf',
261220
normalize({Min,Max});
262-
div_bounds({'-inf',B}, {C,D}) when is_integer(C), C > 0, is_integer(D) ->
221+
div_bounds({'-inf',B}, {C,D}) when is_integer(B),
222+
is_integer(C), C > 0 ->
263223
Min = '-inf',
264-
Max = max(B div C, B div D),
224+
Max = max(B div C, inf_div(B, D)),
265225
normalize({Min,Max});
266226
div_bounds({A,B}, _) when is_integer(A), is_integer(B) ->
267227
Max = max(abs(A), abs(B)),
@@ -291,9 +251,7 @@ rem_bounds({A,B}, _) ->
291251
%% include zero.
292252
Min = inf_min(0, A),
293253
Max = inf_max(0, B),
294-
normalize({Min,Max});
295-
rem_bounds(_, _) ->
296-
any.
254+
normalize({Min,Max}).
297255

298256
-if(false).
299257
min_max_band(A, B, C, D) ->
@@ -737,8 +695,28 @@ inf_neg('-inf') -> '+inf';
737695
inf_neg('+inf') -> '-inf';
738696
inf_neg(N) -> -N.
739697

740-
inf_add(Int, N) when is_integer(Int) -> Int + N;
741-
inf_add(Inf, _N) -> Inf.
698+
inf_add(A, B) when is_integer(A), is_integer(B) -> A + B;
699+
inf_add(Inf, Inf) when is_atom(Inf) -> Inf;
700+
inf_add(Inf, _) when is_atom(Inf) -> Inf;
701+
inf_add(_, Inf) when is_atom(Inf) -> Inf.
702+
703+
inf_mul(A, B) when is_integer(A), is_integer(B) ->
704+
A * B;
705+
inf_mul(A, B) ->
706+
case {inf_sign(A),inf_sign(B)} of
707+
{'-','-'} -> '+inf';
708+
{'+','+'} -> '+inf';
709+
{_,_} -> '-inf'
710+
end.
711+
712+
inf_div(A, B) when is_integer(A), is_integer(B) ->
713+
A div B;
714+
inf_div(A, '+inf') when is_integer(A) ->
715+
0.
716+
717+
inf_abs('-inf') -> '+inf';
718+
inf_abs('+inf') -> '+inf';
719+
inf_abs(N) when is_integer(N) -> abs(N).
742720

743721
inf_bsr('-inf', _S) ->
744722
'-inf';

lib/compiler/test/beam_bounds_SUITE.erl

Lines changed: 69 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -93,17 +93,22 @@ end_per_testcase(Case, Config) when is_atom(Case), is_list(Config) ->
9393
addition_bounds(_Config) ->
9494
test_commutative('+', {-12,12}),
9595

96-
{'-inf',-15} = beam_bounds:bounds('+', {'-inf',-20}, {2,5}),
97-
{'-inf',55} = beam_bounds:bounds('+', {'-inf',50}, {'-inf',5}),
98-
{'-inf',110} = beam_bounds:bounds('+', {1,10}, {'-inf',100}),
99-
any = beam_bounds:bounds('+', {1,'+inf'}, {'-inf',100}),
96+
{'-inf',-15} = do_add({'-inf',-20}, {2,5}),
97+
{'-inf',55} = do_add({'-inf',50}, {'-inf',5}),
98+
{'-inf',110} = do_add({1,10}, {'-inf',100}),
99+
any = do_add({1,'+inf'}, {'-inf',100}),
100100

101-
{-8,'+inf'} = beam_bounds:bounds('+', {2,'+inf'}, {-10,20}),
102-
{6,'+inf'} = beam_bounds:bounds('+', {1,10}, {5,'+inf'}),
103-
{9,'+inf'} = beam_bounds:bounds('+', {2,'+inf'}, {7,'+inf'}),
101+
{-8,'+inf'} = do_add({2,'+inf'}, {-10,20}),
102+
{6,'+inf'} = do_add({1,10}, {5,'+inf'}),
103+
{9,'+inf'} = do_add({2,'+inf'}, {7,'+inf'}),
104104

105105
ok.
106106

107+
do_add(A, B) ->
108+
Res = beam_bounds:bounds('+', A, B),
109+
Res = beam_bounds:bounds('+', B, A),
110+
Res.
111+
107112
subtraction_bounds(_Config) ->
108113
test_noncommutative('-', {-12,12}),
109114

@@ -155,6 +160,28 @@ division_bounds(_Config) ->
155160

156161
any = beam_bounds:bounds('div', {'-inf',10}, any),
157162
any = beam_bounds:bounds('div', {0,'+inf'}, any),
163+
any = beam_bounds:bounds('div', any, {'-inf',10}),
164+
any = beam_bounds:bounds('div', any, {0,'+inf'}),
165+
166+
{-32,32} = beam_bounds:bounds('div', {16,32}, any),
167+
any = beam_bounds:bounds('div', any, {7,13}),
168+
169+
Big = 1 bsl 512,
170+
{-100,100} = beam_bounds:bounds('div', {0,100}, {1,Big}),
171+
{-100,100} = beam_bounds:bounds('div', {0,100}, {-Big,10}),
172+
173+
{-100,'+inf'} = beam_bounds:bounds('div', {-100,'+inf'}, {1,Big}),
174+
{-25,'+inf'} = beam_bounds:bounds('div', {-100,'+inf'}, {4,Big}),
175+
{'-inf',0} = beam_bounds:bounds('div', {'-inf',-5}, {1,Big}),
176+
{'-inf',5} = beam_bounds:bounds('div', {'-inf',5}, {1,Big}),
177+
{0,'+inf'} = beam_bounds:bounds('div', {5,'+inf'}, {1,Big}),
178+
179+
{'-inf',0} = beam_bounds:bounds('div', {-Big,-1}, {5,10}),
180+
{'-inf',-2} = beam_bounds:bounds('div', {-Big,-20}, {5,10}),
181+
{'-inf',0} = beam_bounds:bounds('div', {-Big,-1}, {7,Big}),
182+
{'-inf',8} = beam_bounds:bounds('div', {-Big,17}, {2,Big}),
183+
{2,'+inf'} = beam_bounds:bounds('div', {20,Big}, {5,10}),
184+
{0,'+inf'} = beam_bounds:bounds('div', {1,Big}, {1,Big}),
158185

159186
ok.
160187

@@ -194,6 +221,19 @@ rem_bounds(_Config) ->
194221
{-7,0} = beam_bounds:bounds('rem', {-7,-7}, any),
195222
{-6,0} = beam_bounds:bounds('rem', {-6,-4}, any),
196223

224+
{0,'+inf'} = beam_bounds:bounds('rem', {0,'+inf'}, any),
225+
{0,'+inf'} = beam_bounds:bounds('rem', {7,'+inf'}, any),
226+
{'-inf',0} = beam_bounds:bounds('rem', {'-inf',-5}, any),
227+
{'-inf',11} = beam_bounds:bounds('rem', {'-inf',11}, any),
228+
229+
any = beam_bounds:bounds('rem', any, {'-inf',-7}),
230+
any = beam_bounds:bounds('rem', any, {'-inf',0}),
231+
any = beam_bounds:bounds('rem', any, {'-inf',1}),
232+
any = beam_bounds:bounds('rem', any, {'-inf',5}),
233+
any = beam_bounds:bounds('rem', any, {0,'+inf'}),
234+
any = beam_bounds:bounds('rem', any, {1,'+inf'}),
235+
any = beam_bounds:bounds('rem', any, {100,'+inf'}),
236+
197237
ok.
198238

199239
band_bounds(_Config) ->
@@ -603,13 +643,34 @@ abs_bounds(_Config) ->
603643
_ = [abs_bounds_1({A,B}) ||
604644
A <- Seq,
605645
B <- lists:nthtail(A-Min, Seq)],
646+
647+
Big = 1 bsl 512,
648+
649+
{1,'+inf'} = do_abs({'-inf',-1}),
650+
{10,'+inf'} = do_abs({'-inf',-10}),
651+
652+
{0,'+inf'} = do_abs({'-inf',0}),
653+
{0,'+inf'} = do_abs({'-inf',1}),
654+
{0,'+inf'} = do_abs({'-inf',10}),
655+
{0,'+inf'} = do_abs(any),
656+
{1,'+inf'} = do_abs({-Big,-Big}),
657+
658+
{0,'+inf'} = do_abs({-5,'+inf'}),
659+
{0,'+inf'} = do_abs({0,'+inf'}),
660+
{1,'+inf'} = do_abs({1,'+inf'}),
661+
{17,'+inf'} = do_abs({17,'+inf'}),
662+
{0,'+inf'} = do_abs({Big,Big}),
663+
606664
ok.
607665

666+
do_abs(R) ->
667+
beam_bounds:bounds('abs', R).
668+
608669
abs_bounds_1(R) ->
609670
{HighestMin,LowestMax} = min_max_unary_op('abs', R),
610671
{Min,Max} = beam_bounds:bounds(abs, R),
611672
if
612-
Min =< HighestMin, LowestMax =< Max ->
673+
Min =:= HighestMin, LowestMax =:= Max ->
613674
ok;
614675
true ->
615676
io:format("~p(~p) evaluates to ~p; should be ~p\n",

0 commit comments

Comments
 (0)