diff options
Diffstat (limited to 'lib/compiler/test/beam_bounds_SUITE.erl')
-rw-r--r-- | lib/compiler/test/beam_bounds_SUITE.erl | 342 |
1 files changed, 319 insertions, 23 deletions
diff --git a/lib/compiler/test/beam_bounds_SUITE.erl b/lib/compiler/test/beam_bounds_SUITE.erl index 42e7f2bc27..e10f25c688 100644 --- a/lib/compiler/test/beam_bounds_SUITE.erl +++ b/lib/compiler/test/beam_bounds_SUITE.erl @@ -25,7 +25,12 @@ multiplication_bounds/1, division_bounds/1, rem_bounds/1, band_bounds/1, bor_bounds/1, bxor_bounds/1, bsr_bounds/1, bsl_bounds/1, - lt_bounds/1, le_bounds/1, gt_bounds/1, ge_bounds/1]). + bnot_bounds/1, + lt_bounds/1, le_bounds/1, gt_bounds/1, ge_bounds/1, + min_bounds/1, max_bounds/1, + abs_bounds/1, + infer_lt_gt_bounds/1, + redundant_masking/1]). suite() -> [{ct_hooks,[ts_install_cth]}]. @@ -42,12 +47,18 @@ groups() -> band_bounds, bor_bounds, bxor_bounds, + bnot_bounds, bsr_bounds, bsl_bounds, lt_bounds, le_bounds, gt_bounds, - ge_bounds]}]. + ge_bounds, + min_bounds, + max_bounds, + abs_bounds, + infer_lt_gt_bounds, + redundant_masking]}]. init_per_suite(Config) -> test_lib:recompile(?MODULE), @@ -63,17 +74,59 @@ end_per_group(_GroupName, Config) -> Config. addition_bounds(_Config) -> - test_commutative('+', {-12,12}). + test_commutative('+', {-12,12}), + + {'-inf',-15} = beam_bounds:bounds('+', {'-inf',-20}, {2,5}), + {'-inf',55} = beam_bounds:bounds('+', {'-inf',50}, {'-inf',5}), + {'-inf',110} = beam_bounds:bounds('+', {1,10}, {'-inf',100}), + any = beam_bounds:bounds('+', {1,'+inf'}, {'-inf',100}), + + {-8,'+inf'} = beam_bounds:bounds('+', {2,'+inf'}, {-10,20}), + {6,'+inf'} = beam_bounds:bounds('+', {1,10}, {5,'+inf'}), + {9,'+inf'} = beam_bounds:bounds('+', {2,'+inf'}, {7,'+inf'}), + + ok. subtraction_bounds(_Config) -> - test_noncommutative('-', {-12,12}). + test_noncommutative('-', {-12,12}), + + {'-inf',18} = beam_bounds:bounds('-', {'-inf',20}, {2,9}), + any = beam_bounds:bounds('-', {'-inf',20}, {'-inf',17}), + {-99,'+inf'} = beam_bounds:bounds('-', {1,10}, {'-inf',100}), + {-93,'+inf'} = beam_bounds:bounds('-', {7,'+inf'}, {'-inf',100}), + + {-18,'+inf'} = beam_bounds:bounds('-', {2,'+inf'}, {-10,20}), + {'-inf',6} = beam_bounds:bounds('-', {1,11}, {5,'+inf'}), + any = beam_bounds:bounds('-', {2,'+inf'}, {7,'+inf'}), + + ok. multiplication_bounds(_Config) -> - test_commutative('*', {-12,12}). + test_commutative('*', {-12,12}), + + {'-inf',-40} = beam_bounds:bounds('*', {'-inf',-20}, {2,5}), + {'-inf',1000} = beam_bounds:bounds('*', {'-inf',100}, {1,10}), + any = beam_bounds:bounds('*', {'-inf',100}, {-10,10}), + + {-100,'+inf'} = beam_bounds:bounds('*', {-10,'+inf'}, {1,10}), + {7,'+inf'} = beam_bounds:bounds('*', {7,'+inf'}, {1,10}), + any = beam_bounds:bounds('*', {-10,'+inf'}, {-5,5}), + + {'-inf',1000} = beam_bounds:bounds('*', {1,10}, {'-inf',100}), + {-100,'+inf'} = beam_bounds:bounds('*', {1,10}, {-10,'+inf'}), + + ok. division_bounds(_Config) -> test_noncommutative('div', {-12,12}), + {'-inf',-5} = beam_bounds:bounds('div', {'-inf',-20}, {2,4}), + {'-inf',50} = beam_bounds:bounds('div', {'-inf',100}, {2,4}), + + {-5,'+inf'} = beam_bounds:bounds('div', {-10,'+inf'}, {2,4}), + {2,'+inf'} = beam_bounds:bounds('div', {10,'+inf'}, {2,4}), + + any = beam_bounds:bounds('div', {10,'+inf'}, {0,0}), {'EXIT', {badarith, _}} = catch division_bounds_1([], ok), ok. @@ -88,8 +141,17 @@ division_bounds_1(_, _) -> rem_bounds(_Config) -> test_noncommutative('rem', {-12,12}), - {-7,7} = beam_bounds:'rem'(any, {1,8}), - {-11,11} = beam_bounds:'rem'(any, {-12,8}), + {-7,7} = beam_bounds:bounds('rem', any, {1,8}), + {-11,11} = beam_bounds:bounds('rem', any, {-12,8}), + + {-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'}), + + any = beam_bounds:bounds('rem', {-10,10}, {'-inf',10}), + any = beam_bounds:bounds('rem', {-10,10}, {10,'+inf'}), ok. @@ -97,36 +159,99 @@ band_bounds(_Config) -> test_commutative('band'), %% Coverage. - {0,17} = beam_bounds:'band'(any, {7,17}), - {0,42} = beam_bounds:'band'({0,42}, any), - any = beam_bounds:'band'({-1,1}, any), - any = beam_bounds:'band'(any, {-10,0}), - any = beam_bounds:'band'({-10,0},{-1,10}), - any = beam_bounds:'band'({-20,-10},{-1,10}), + {0,17} = beam_bounds:bounds('band', any, {7,17}), + {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}), ok. bor_bounds(_Config) -> test_commutative('bor'), - any = beam_bounds:'bor'({-10,0},{-1,10}), - any = beam_bounds:'bor'({-20,-10},{-1,10}), + any = beam_bounds:bounds('bor', {-10,0},{-1,10}), + any = beam_bounds:bounds('bor', {-20,-10}, {-1,10}), ok. bxor_bounds(_Config) -> test_commutative('bxor'), - any = beam_bounds:'bxor'({-10,0},{-1,10}), - any = beam_bounds:'bxor'({-20,-10},{-1,10}), + any = beam_bounds:bounds('bxor', {-10,0}, {-1,10}), + any = beam_bounds:bounds('bxor', {-20,-10}, {-1,10}), ok. +bnot_bounds(_Config) -> + Min = -7, + Max = 7, + Seq = lists:seq(Min, Max), + _ = [bnot_bounds_1({A,B}) || + A <- Seq, + B <- lists:nthtail(A-Min, Seq)], + + {-43,'+inf'} = beam_bounds:bounds('bnot', {'-inf',42}), + {99,'+inf'} = beam_bounds:bounds('bnot', {'-inf',-100}), + {'-inf',-8} = beam_bounds:bounds('bnot', {7,'+inf'}), + {'-inf',9} = beam_bounds:bounds('bnot', {-10,'+inf'}), + + -1 = bnot_bounds_2(0), + + ok. + +bnot_bounds_1(R) -> + {HighestMin,LowestMax} = min_max_unary_op('bnot', R), + {Min,Max} = beam_bounds:bounds('bnot', R), + if + Min =< HighestMin, LowestMax =< Max -> + ok; + true -> + io:format("bnot(~p) evaluates to ~p; should be ~p\n", + [R,{Min,Max},{HighestMin,LowestMax}]), + ct:fail(bad_min_or_max) + end. + +%% GH-7145: 'bnot' converged too slowly, effectively hanging the compiler. +bnot_bounds_2(0) -> -1; +bnot_bounds_2(N) -> abs(bnot bnot_bounds_2(N)). + bsr_bounds(_Config) -> - test_noncommutative('bsr', {-12,12}, {0,7}). + test_noncommutative('bsr', {-12,12}, {0,7}), + + {0,10} = beam_bounds:bounds('bsr', {0,10}, {0,'+inf'}), + {0,2} = beam_bounds:bounds('bsr', {0,10}, {2,'+inf'}), + + {-1,10} = beam_bounds:bounds('bsr', {-1,10}, {0,'+inf'}), + {-100,900} = beam_bounds:bounds('bsr', {-100,900}, {0,'+inf'}), + {-50,450} = beam_bounds:bounds('bsr', {-100,900}, {1,'+inf'}), + + {'-inf',16} = beam_bounds:bounds('bsr', {'-inf',32}, {1,10}), + {-5,'+inf'} = beam_bounds:bounds('bsr', {-10,'+inf'}, {1,10}), + + ok. bsl_bounds(_Config) -> - test_noncommutative('bsl', {-12,12}, {0,7}). + test_noncommutative('bsl', {-12,12}, {-7,7}), + + {2,'+inf'} = beam_bounds:bounds('bsl', {1,10}, {1,10_000}), + {0,'+inf'} = beam_bounds:bounds('bsl', {1,10}, {-10,10_000}), + any = beam_bounds:bounds('bsl', {-7,10}, {1,10_000}), + + 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'}), + + {0,10} = beam_bounds:bounds('bsl', {1,10}, {'-inf',0}), + {0,20} = beam_bounds:bounds('bsl', {1,10}, {'-inf',1}), + {-7,10} = beam_bounds:bounds('bsl', {-7,10}, {'-inf',0}), + {-28,40} = beam_bounds:bounds('bsl', {-7,10}, {'-inf',2}), + + {'-inf',-1} = beam_bounds:bounds('bsl', {-10,-1}, {500,1024}), + {0,'+inf'} = beam_bounds:bounds('bsl', {1,10}, {500,1024}), + + ok. lt_bounds(_Config) -> test_relop('<'). @@ -140,8 +265,98 @@ gt_bounds(_Config) -> ge_bounds(_Config) -> test_relop('>='). +min_bounds(_Config) -> + test_commutative(min, {-12,12}), + + {'-inf',-10} = min_bounds({'-inf',-10}, {1,100}), + {'-inf',1} = min_bounds({'-inf',1}, {1,100}), + {'-inf',50} = min_bounds({'-inf',50}, {1,100}), + {'-inf',100} = min_bounds({'-inf',500}, {1,100}), + + {'-inf',-10} = min_bounds({'-inf',-10}, {1,'+inf'}), + {'-inf',1} = min_bounds({'-inf',1}, {1,'+inf'}), + {'-inf',700} = min_bounds({'-inf',700}, {1,'+inf'}), + + {1,99} = min_bounds({1,99}, {100,'+inf'}), + {1,100} = min_bounds({1,100}, {100,'+inf'}), + {100,200} = min_bounds({150,200}, {100,'+inf'}), + + ok. + +min_bounds(R1, R2) -> + Result = beam_bounds:bounds(min, R1, R2), + Result = beam_bounds:bounds(min, R2, R1). + +max_bounds(_Config) -> + test_commutative(max, {-12,12}), + + {1,100} = max_bounds({'-inf',-10}, {1,100}), + {1,100} = max_bounds({'-inf',1}, {1,100}), + {1,100} = max_bounds({'-inf',50}, {1,100}), + {1,500} = max_bounds({'-inf',500}, {1,100}), + + {1,'+inf'} = max_bounds({'-inf',-10}, {1,'+inf'}), + {1,'+inf'} = max_bounds({'-inf',1}, {1,'+inf'}), + {1,'+inf'} = max_bounds({'-inf',700}, {1,'+inf'}), + + {100,'+inf'} = max_bounds({1,99}, {100,'+inf'}), + {100,'+inf'} = max_bounds({1,100}, {100,'+inf'}), + {150,'+inf'} = max_bounds({150,200}, {100,'+inf'}), + + ok. + +max_bounds(R1, R2) -> + Result = beam_bounds:bounds(max, R1, R2), + Result = beam_bounds:bounds(max, R2, R1). + +abs_bounds(_Config) -> + Min = -7, + Max = 7, + Seq = lists:seq(Min, Max), + _ = [abs_bounds_1({A,B}) || + A <- Seq, + B <- lists:nthtail(A-Min, Seq)], + ok. + +abs_bounds_1(R) -> + {HighestMin,LowestMax} = min_max_unary_op('abs', R), + {Min,Max} = beam_bounds:bounds(abs, R), + if + Min =< HighestMin, LowestMax =< Max -> + ok; + true -> + io:format("~p(~p) evaluates to ~p; should be ~p\n", + [bif_abs,R,{Min,Max},{HighestMin,LowestMax}]), + ct:fail(bad_min_or_max) + end. + +infer_lt_gt_bounds(_Config) -> + {{'-inf',-1}, {'-inf',0}} = infer_lt_gt({'-inf',0}, {'-inf',0}), + {{'-inf',1}, {'-inf',2}} = infer_lt_gt({'-inf',1}, {'-inf',2}), + {{'-inf',-2}, {'-inf',-1}} = infer_lt_gt({'-inf',1}, {'-inf',-1}), + {{'-inf',2}, {1,3}} = infer_lt_gt({'-inf',2}, {1,3}), + + any = infer_lt_gt({'-inf',2}, {3,10}), + any = infer_lt_gt({'-inf',2}, {3,'+inf'}), + + {{0,10}, {1,84}} = infer_lt_gt({0,10}, {'-inf',84}), + {{0,83}, {1,84}} = infer_lt_gt({0,'+inf'}, {'-inf',84}), + + {{0,'+inf'}, {42, '+inf'}} = infer_lt_gt({0,'+inf'}, {42, '+inf'}), + {{100,'+inf'}, {101, '+inf'}} = infer_lt_gt({100,'+inf'}, {42, '+inf'}), + + ok. + %%% Utilities +infer_lt_gt(R1, R2) -> + case beam_bounds:infer_relop_types('>', R2, R1) of + {Rb,Ra} -> + {Ra,Rb} = beam_bounds:infer_relop_types('<', R1, R2); + any -> + any = beam_bounds:infer_relop_types('<', R1, R2) + end. + test_commutative(Op) -> test_commutative(Op, {0,32}). @@ -157,8 +372,8 @@ test_commutative(Op, {Min,Max}) -> test_commutative_1(Op, R1, R2) -> {HighestMin,LowestMax} = min_max_op(Op, R1, R2), - {Min,Max} = beam_bounds:Op(R1, R2), - {Min,Max} = beam_bounds:Op(R2, R1), + {Min,Max} = beam_bounds:bounds(Op, R1, R2), + {Min,Max} = beam_bounds:bounds(Op, R2, R1), if Min =< HighestMin, LowestMax =< Max -> ok; @@ -167,6 +382,7 @@ test_commutative_1(Op, R1, R2) -> [Op,R1,R2,{Min,Max},{HighestMin,LowestMax}]), ct:fail(bad_min_or_max) end. + test_noncommutative(Op, Range) -> test_noncommutative(Op, Range, Range). @@ -182,7 +398,7 @@ test_noncommutative(Op, {Min1,Max1}, {Min2,Max2}) -> test_noncommutative_1(Op, R1, R2) -> {HighestMin,LowestMax} = min_max_op(Op, R1, R2), - case beam_bounds:Op(R1, R2) of + case beam_bounds:bounds(Op, R1, R2) of any -> case {Op,R2} of {'div',{0,0}} -> ok; @@ -218,6 +434,20 @@ min_max_op_2(Op, A, C, D, MinMax) when C =< D -> min_max_op_2(_Op, _, _, _, MinMax) -> MinMax. +min_max_unary_op(Op, {A,B}) -> + min_max_unary_op_1(Op, A, B, {infinity,-(1 bsl 24)}). + +min_max_unary_op_1(Op, A, B, {Min,Max}) when A =< B -> + Val = erlang:Op(A), + if + Min =< Val, Val =< Max -> + min_max_unary_op_1(Op, A + 1, B, {Min,Max}); + true -> + min_max_unary_op_1(Op, A + 1, B, {min(Min, Val),max(Max, Val)}) + end; +min_max_unary_op_1(_Op, _, _, MinMax) -> + MinMax. + test_relop(Op) -> Max = 15, Seq = lists:seq(0, Max), @@ -232,13 +462,45 @@ test_relop_1(Op, R1, R2) -> Bool = rel_op(Op, R1, R2), case beam_bounds:relop(Op, R1, R2) of Bool -> - ok; + test_infer_relop(Bool, Op, R1, R2); Wrong -> io:format("~p(~p, ~p) evaluates to ~p; should be ~p\n", [Op,R1,R2,Wrong,Bool]), ct:fail(bad_bool_result) end. +test_infer_relop(true, Op, R1, R2) -> + any = beam_bounds:infer_relop_types(Op, R1, R2); +test_infer_relop(false, Op, R1, R2) -> + none = beam_bounds:infer_relop_types(Op, R1, R2); +test_infer_relop('maybe', Op, {A0,B0}=R1, {C0,D0}=R2) -> + {{A,B},{C,D}} = beam_bounds:infer_relop_types(Op, R1, R2), + if + A =< B, C =< D, A0 =< A, B0 >= B, C0 =< C, D0 >= D -> + ok; + true -> + io:format("~p ~p infers as ~p ~p\n", + [R1,R2,{A,B},{C,D}]), + ct:fail(ranges_grew) + end, + _ = [begin + case in_range(X, {A,B}) andalso in_range(Y, {C,D}) of + true -> + ok; + false -> + io:format("X = ~p; Y = ~p\n", [X,Y]), + io:format("~p ~p infers as ~p ~p\n", + [R1,R2,{A,B},{C,D}]), + ct:fail(bad_inference) + end + end || X <- lists:seq(A0, B0), + Y <- lists:seq(C0, D0), + erlang:Op(X, Y)], + ok. + +in_range(Int, {A,B}) -> + A =< Int andalso Int =< B. + rel_op(Op, {A,B}, {C,D}) -> rel_op_1(Op, A, B, C, D, none). @@ -258,3 +520,37 @@ rel_op_2(Op, A, C, D, BoolResult0) when C =< D -> rel_op_2(Op, A, C + 1, D, BoolResult); rel_op_2(_Op, _, _, _, BoolResult) -> BoolResult. + +redundant_masking(_Config) -> + Min = -7, + Max = 15, + Seq = lists:seq(Min, Max), + _ = [test_redundant_masking({A,B}, M) || + A <- Seq, + B <- lists:nthtail(A-Min, Seq), + M <- Seq], + + false = beam_bounds:is_masking_redundant({'-inf',10}, 16#ff), + false = beam_bounds:is_masking_redundant({0,'+inf'}, 16#ff), + ok. + +test_redundant_masking({A,B}=R, M) -> + ShouldBe = test_redundant_masking(A, B, M), + case beam_bounds:is_masking_redundant(R, M) of + ShouldBe -> + ok; + false when M band (M + 1) =/= 0 -> + %% M + 1 is not a power of two. + ok; + false when A =:= B -> + ok; + Unexpected -> + io:format("beam_bounds:is_masking_redundant(~p, ~p) " + "evaluates to ~p; should be ~p\n", + [R,M,Unexpected,ShouldBe]), + ct:fail(bad_boolean) + end. + +test_redundant_masking(A, B, M) when A =< B -> + A band M =:= A andalso test_redundant_masking(A + 1, B, M); +test_redundant_masking(_, _, _) -> true. |