diff --git a/lib/compiler/src/beam_bounds.erl b/lib/compiler/src/beam_bounds.erl index 6f3902596ea2..6ac514182164 100644 --- a/lib/compiler/src/beam_bounds.erl +++ b/lib/compiler/src/beam_bounds.erl @@ -46,7 +46,28 @@ bounds('bnot', R0) -> case R0 of - {A,B} -> + {A, B} when is_integer(A), is_integer(B), A =/= B -> + %% While it's easy to get an exact range, doing so can make certain + %% chains of operations slow to converge, e.g. + %% + %% f(0) -> -1; f(N) -> abs(bnot f(N)). + %% + %% Where the range increases by 1 every time we pass through, + %% making it more or less impossible to reach a fixpoint. + %% + %% We therefore widen the range a bit quicker to ensure that we + %% converge on 'any' within a reasonable time frame, hoping that + %% the range will still be tight enough in the cases where we + %% don't feed the result into itself. + case {abs(A) bsr ?NUM_BITS, abs(B) bsr ?NUM_BITS} of + {0, 0} -> + Min = min(-B - 1, -(B bsl 1) - 1), + Max = max(-A - 1, -(A bsl 1) - 1), + normalize({Min, Max}); + {_, _} -> + any + end; + {A, B} -> R = {inf_add(inf_neg(B), -1), inf_add(inf_neg(A), -1)}, normalize(R); _ -> diff --git a/lib/compiler/test/beam_bounds_SUITE.erl b/lib/compiler/test/beam_bounds_SUITE.erl index 93021cad0e31..e10f25c6886d 100644 --- a/lib/compiler/test/beam_bounds_SUITE.erl +++ b/lib/compiler/test/beam_bounds_SUITE.erl @@ -197,6 +197,8 @@ bnot_bounds(_Config) -> {'-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) -> @@ -211,6 +213,10 @@ bnot_bounds_1(R) -> 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}),