You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
which has correctly inferred range of arguments 48..57 - 48. However the resulting value is inferred as -inf..9, rather than 0..9. This prevents the JIT from emitting optimised operations for small integers. This can be seen in:
This is not a bug, but a limitation of the current value range analysis. We will not attempt to improve the value range analysis in Erlang/OTP 27.
Determining ranges is done in two passes. First in the global type analysis pass (meaning looking at all functions in a module), and then in a later local pass that propagates ranges through sequential code in a single function.
When the global pass sees the subtraction of 48 from range 48..57, the conservative safe result is -inf..9. The reason is that the calculation could be called in a loop. If called in a loop, the range 0..9 would not be safe, because the next time through the loop, the value range analysis could be asked to calculate (for instance) -48..9 - 48, and the next time -96..9 - 48 and so on. After an infinite number of iterations, we would end up with the range -inf..9.
The local value range analysis pass is less conservative and will correctly deduce that the range of the difference is 0..9, and if there had been any calculations directly following it in the same function, that range would have been propagated to them. However, the range cannot be propagated to the caller.
No promises, but a possible goal for the compiler in Erlang/OTP 28 could be to improve the ranges in the following example:
-module(ranges).
-export([fact/1, num_odd/1]).
fact(N) when is_integer(N), 0 =< N, N < 10_000 ->
fact(N, 1).
%% With a more sophisticated value range analysis, the range of N
%% could be determined to be 0..9999 instead of '-inf'..9999.
fact(0, P) -> P;
fact(N, P) -> fact(N - 1, P * N).
num_odd(L) ->
num_odd(L, 0).
%% Assuming that a list can contain no more than 288230376151711743
%% elements, the range of N would be 0..288230376151711743 instead of
%% the current 0..'+inf'.
num_odd([H|T], N) ->
case H rem 2 of
0 -> num_odd(T, N);
1 -> num_odd(T, N + 1)
end;
num_odd([], N) ->
N.
Describe the bug
The compiler fails to properly optimise integer subtraction by miscalculating the possible value range.
To Reproduce
Given the following code:
compiling with
erlc +to_asm test.erl
produces an assembly file with following:The important part is the subtraction call
which has correctly inferred range of arguments
48..57 - 48
. However the resulting value is inferred as-inf..9
, rather than0..9
. This prevents the JIT from emitting optimised operations for small integers. This can be seen in:and later
Expected behavior
The emitted operations should be optimised to operate on small integers exclusively. In particular I'd expect to see:
and later
Affected versions
Latest master, and OTP 26.2.1
The text was updated successfully, but these errors were encountered: