Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions erts/emulator/beam/bif.tab
Original file line number Diff line number Diff line change
Expand Up @@ -834,3 +834,8 @@ bif erl_debugger:peek_xreg/3
#
bif erts_debug:unaligned_bitstring/2
bif re:import/1

#
# New in 29.
#
ubif erlang:is_integer/3
2 changes: 2 additions & 0 deletions erts/emulator/beam/emu/ops.tab
Original file line number Diff line number Diff line change
Expand Up @@ -1001,6 +1001,8 @@ bif1 Fail=f Bif S1 Dst => i_bif1 S1 Fail Bif Dst
bif2 p Bif S1 S2 Dst => i_bif2_body S2 S1 Bif Dst
bif2 Fail=f Bif S1 S2 Dst => i_bif2 S2 S1 Fail Bif Dst

bif3 p Bif S1 S2 S3 Dst => i_bif3_body S3 S2 S1 Bif Dst

i_get_hash c W d
i_get s d

Expand Down
14 changes: 14 additions & 0 deletions erts/emulator/beam/erl_bif_guard.c
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,20 @@ BIF_RETTYPE size_1(BIF_ALIST_1)
BIF_ERROR(BIF_P, BADARG);
}

BIF_RETTYPE is_integer_3(BIF_ALIST_3)
{
if(is_not_integer(BIF_ARG_2) ||
is_not_integer(BIF_ARG_3)) {
BIF_ERROR(BIF_P, BADARG);
}
if(is_not_integer(BIF_ARG_1)) {
BIF_RET(am_false);
}

BIF_RET((CMP_LE(BIF_ARG_2, BIF_ARG_1) && CMP_LE(BIF_ARG_1, BIF_ARG_3)) ?
am_true : am_false);
}

/**********************************************************************/
/* returns the bitsize of a bitstring */

Expand Down
6 changes: 6 additions & 0 deletions erts/emulator/beam/erl_db_util.c
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,12 @@ static DMCGuardBif guard_tab[] =
1,
DBIF_ALL
},
{
am_is_integer,
&is_integer_3,
3,
DBIF_ALL
},
{
am_is_list,
&is_list_1,
Expand Down
1 change: 1 addition & 0 deletions erts/emulator/beam/jit/arm/ops.tab
Original file line number Diff line number Diff line change
Expand Up @@ -814,6 +814,7 @@ bif2 _Fail Bif S1 S2 Dst | never_fails(Bif) => nofail_bif2 S1 S2 Bif Dst

bif1 Fail Bif S1 Dst => i_bif1 S1 Fail Bif Dst
bif2 Fail Bif S1 S2 Dst => i_bif2 S1 S2 Fail Bif Dst
bif3 Fail Bif S1 S2 S3 Dst => i_bif3 S1 S2 S3 Fail Bif Dst

nofail_bif2 S1=d S2 Bif Dst | is_eq_exact_bif(Bif) => bif_is_eq_exact S1 S2 Dst
nofail_bif2 S1=d S2 Bif Dst | is_ne_exact_bif(Bif) => bif_is_ne_exact S1 S2 Dst
Expand Down
1 change: 1 addition & 0 deletions erts/emulator/beam/jit/x86/ops.tab
Original file line number Diff line number Diff line change
Expand Up @@ -745,6 +745,7 @@ bif2 _Fail Bif S1 S2 Dst | never_fails(Bif) => nofail_bif2 S1 S2 Bif Dst

bif1 Fail Bif S1 Dst => i_bif1 S1 Fail Bif Dst
bif2 Fail Bif S1 S2 Dst => i_bif2 S1 S2 Fail Bif Dst
bif3 Fail Bif S1 S2 S3 Dst => i_bif3 S1 S2 S3 Fail Bif Dst

nofail_bif2 S1=d S2 Bif Dst | is_eq_exact_bif(Bif) => bif_is_eq_exact S1 S2 Dst
nofail_bif2 S1=d S2 Bif Dst | is_ne_exact_bif(Bif) => bif_is_ne_exact S1 S2 Dst
Expand Down
71 changes: 69 additions & 2 deletions erts/emulator/test/bif_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

-include_lib("common_test/include/ct.hrl").
-include_lib("kernel/include/file.hrl").
-include_lib("stdlib/include/assert.hrl").

-export([all/0, suite/0, init_per_testcase/2, end_per_testcase/2]).

Expand All @@ -46,7 +47,7 @@
test_length/1,
fixed_apply_badarg/1,
external_fun_apply3/1,
node_1/1,doctests/1]).
node_1/1,doctests/1,is_integer_3_test/1]).

suite() ->
[{ct_hooks,[ts_install_cth]},
Expand All @@ -64,7 +65,7 @@ all() ->
is_process_alive, is_process_alive_signal_from,
process_info_blast, os_env_case_sensitivity,
verify_middle_queue_save, test_length,fixed_apply_badarg,
external_fun_apply3, node_1, doctests].
external_fun_apply3, node_1, doctests, is_integer_3_test].

init_per_testcase(guard_bifs_in_erl_bif_types, Config) when is_list(Config) ->
skip_missing_erl_bif_types(Config);
Expand Down Expand Up @@ -1780,6 +1781,72 @@ node_error(E0) ->
doctests(_Config) ->
shell_docs:test(erlang, []).

is_integer_3_test(_Config) ->
_ = [is_between_ten(X) || X <- lists:seq(-2, 12)],

false = is_between_ten(0),
true = is_between_ten(1),
true = is_between_ten(10),
false = is_between_ten(11),

false = is_between_ten(a),
false = is_between_ten(5.0),
false = is_between_ten(-7.0),
false = is_between_ten([1]),

_ = [begin
is_between_negative(X),
false = is_between_negative(-X)
end || X <- lists:seq(-100, -70)],

_ = [is_between_mixed(X) || X <- lists:seq(-10, 10)],

_ = [begin
is_between_bignum(X),
false = is_between_bignum(-X),
false = is_between_bignum(X - (1 bsl 64))
end || X <- lists:seq((1 bsl 64) - 3, (1 bsl 64) + 10)],

is_between_badarg(2, 1.5, 10.0),
is_between_badarg(2, 10.0, 1.5),
is_between_badarg(2, 1.5, 10),
is_between_badarg(2, 1, 10.0),
is_between_badarg(2, lower, upper),

ok.

-define(IS_BETWEEN_TEST(Name, LB, UB),
Name(X0) ->
F = id(is_integer),
Lower0 = LB,
Upper0 = UB,
Lower = id(Lower0),
Upper = id(Upper0),

X1 = id(X0),
Result = is_integer(X1, Lower0, Upper0),
Result = is_integer(X1, Lower, Upper),
Result = apply(erlang, F, id([X1, Lower, Upper])),
Result = erlang:F(X1, Lower, Upper),

false = is_integer(id(X1), Upper, Lower),

X = id(X1),
Result = is_integer(X) andalso Lower =< X andalso X =< Upper,
Result).

?IS_BETWEEN_TEST(is_between_ten, 1, 10).
?IS_BETWEEN_TEST(is_between_negative, -89, -77).
?IS_BETWEEN_TEST(is_between_mixed, -7, 7).
?IS_BETWEEN_TEST(is_between_bignum, 1 bsl 64, (1 bsl 64) + 7).

is_between_badarg(X, A, B) ->
F = id(is_integer),

?assertError(badarg, is_integer(id(X), id(A), id(B))),
?assertError(badarg, erlang:F(X, A, B)),
?assertError(badarg, apply(erlang, F, id([X, A, B]))).

%% helpers

wait_until(Fun) ->
Expand Down
1 change: 1 addition & 0 deletions erts/emulator/test/exception_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -940,6 +940,7 @@ error_info(_Config) ->
{is_builtin, [1, 2, a]},
{is_function, [abc, bad_arity]},
{is_function, [abc, -1]},
{is_integer, [5, a, b]},
{is_map_key, [key, not_map]},
{is_process_alive, [abc]},
{is_record, [not_tuple,42]},
Expand Down
Binary file modified erts/preloaded/ebin/erlang.beam
Binary file not shown.
Binary file modified erts/preloaded/ebin/init.beam
Binary file not shown.
27 changes: 26 additions & 1 deletion erts/preloaded/src/erlang.erl
Original file line number Diff line number Diff line change
Expand Up @@ -484,7 +484,7 @@ A list of binaries. This datatype is useful to use together with

-export([abs/1, append/2, element/2, get_module_info/2, hd/1,
is_atom/1, is_binary/1, is_bitstring/1, is_boolean/1,
is_float/1, is_function/1, is_function/2, is_integer/1,
is_float/1, is_function/1, is_function/2, is_integer/1, is_integer/3,
is_list/1, is_map/1, is_number/1, is_pid/1, is_port/1, is_record/2,
is_record/3, is_reference/1, is_tuple/1, load_module/2,
load_nif/2, localtime_to_universaltime/2, make_fun/3,
Expand Down Expand Up @@ -7302,6 +7302,31 @@ false
is_tuple(_Term) ->
erlang:nif_error(undefined).

%% Shadowed by erl_bif_types: erlang:is_integer/3
-doc """
Returns `true` if `Term`, `LB`, and `UB` all evaluate to integers, and `Term`
is between `LB` and `UB` inclusive; otherwise, returns `false`.

## Examples

```erlang
1> is_integer(15, 0, 1024).
true
2> is_integer(-1, 0, 1).
false
```

Failure: `badarg` if `LB` or `UB` does not evaluate to an integer.
""".
-doc #{ category => terms }.
-spec is_integer(Term, LB, UB) -> boolean() when
Term :: integer(),
LB :: integer(),
UB :: integer().
is_integer(_Term, _LB, _UB) ->
erlang:nif_error(undefined).


-doc """
Loads `Module` described by the object code contained within `Binary`.

Expand Down
3 changes: 2 additions & 1 deletion lib/compiler/src/beam_asm.erl
Original file line number Diff line number Diff line change
Expand Up @@ -508,7 +508,8 @@ bif_type(fsub, 2) -> {op,fsub};
bif_type(fmul, 2) -> {op,fmul};
bif_type(fdiv, 2) -> {op,fdiv};
bif_type(_, 1) -> bif1;
bif_type(_, 2) -> bif2.
bif_type(_, 2) -> bif2;
bif_type(_, 3) -> bif3.

make_op({'%',_}, Dict) ->
{[],Dict};
Expand Down
42 changes: 28 additions & 14 deletions lib/compiler/src/beam_call_types.erl
Original file line number Diff line number Diff line change
Expand Up @@ -100,14 +100,15 @@ will_succeed(erlang, 'bsl'=Op, [LHS, RHS]=Args) ->
will_succeed(erlang, '++', [LHS, _RHS]) ->
succeeds_if_type(LHS, proper_list());
will_succeed(erlang, '--', [_, _] = Args) ->
succeeds_if_types(Args, proper_list());
succeeds_if_types(Args, [proper_list(), proper_list()]);
will_succeed(erlang, BoolOp, [_, _] = Args) when BoolOp =:= 'and';
BoolOp =:= 'or' ->
succeeds_if_types(Args, beam_types:make_boolean());
Bool = beam_types:make_boolean(),
succeeds_if_types(Args, [Bool, Bool]);
will_succeed(erlang, Op, [_, _] = Args) when Op =:= 'band';
Op =:= 'bor';
Op =:= 'bxor' ->
succeeds_if_types(Args, #t_integer{});
succeeds_if_types(Args, [#t_integer{}, #t_integer{}]);
will_succeed(erlang, bit_size, [Arg]) ->
succeeds_if_type(Arg, #t_bs_matchable{});
will_succeed(erlang, byte_size, [Arg]) ->
Expand Down Expand Up @@ -144,13 +145,18 @@ will_succeed(erlang, map_size, [Arg]) ->
will_succeed(erlang, node, [Arg]) ->
succeeds_if_type(Arg, identifier);
will_succeed(erlang, 'and', [_, _]=Args) ->
succeeds_if_types(Args, beam_types:make_boolean());
Bool = beam_types:make_boolean(),
succeeds_if_types(Args, [Bool, Bool]);
will_succeed(erlang, 'not', [Arg]) ->
succeeds_if_type(Arg, beam_types:make_boolean());
will_succeed(erlang, 'or', [_, _]=Args) ->
succeeds_if_types(Args, beam_types:make_boolean());
Bool = beam_types:make_boolean(),
succeeds_if_types(Args, [Bool, Bool]);
will_succeed(erlang, 'xor', [_, _]=Args) ->
succeeds_if_types(Args, beam_types:make_boolean());
Bool = beam_types:make_boolean(),
succeeds_if_types(Args, [Bool, Bool]);
will_succeed(erlang, 'is_integer', [_, _, _]=Args) ->
succeeds_if_types(Args, [any, #t_integer{}, #t_integer{}]);
will_succeed(erlang, setelement, [Pos, Tuple0, _Value]=Args) ->
PosRange = #t_integer{elements={1,?MAX_TUPLE_SIZE}},
case {meet(Pos, PosRange), meet(Tuple0, #t_tuple{size=1})} of
Expand Down Expand Up @@ -231,14 +237,20 @@ fails_on_conflict_1([ArgType | Args], [Required | Types]) ->
fails_on_conflict_1([], []) ->
'maybe'.

succeeds_if_types([LHS, RHS], Required) ->
case {succeeds_if_type(LHS, Required),
succeeds_if_type(RHS, Required)} of
{yes, yes} -> yes;
{no, _} -> no;
{_, no} -> no;
{_, _} -> 'maybe'
end.
succeeds_if_types(Ts, Rs) ->
succeeds_if_types_1(Ts, Rs, yes).

succeeds_if_types_1([T | Ts], [R | Rs], Acc) ->
case succeeds_if_type(T, R) of
yes when Acc =:= yes ->
succeeds_if_types_1(Ts, Rs, Acc);
no ->
no;
_ ->
succeeds_if_types_1(Ts, Rs, 'maybe')
end;
succeeds_if_types_1([], [], Acc) ->
Acc.

succeeds_if_type(ArgType, Required) ->
case meet(ArgType, Required) of
Expand Down Expand Up @@ -396,6 +408,8 @@ types(erlang, is_function, [Type]) ->
sub_unsafe_type_test(Type, #t_fun{});
types(erlang, is_integer, [Type]) ->
sub_unsafe_type_test(Type, #t_integer{});
types(erlang, is_integer, [_Term, _LB, _UB]) ->
sub_unsafe(beam_types:make_boolean(), [any, #t_integer{}, #t_integer{}]);
types(erlang, is_list, [Type]) ->
sub_unsafe_type_test(Type, #t_list{});
types(erlang, is_map, [Type]) ->
Expand Down
4 changes: 4 additions & 0 deletions lib/compiler/src/beam_disasm.erl
Original file line number Diff line number Diff line change
Expand Up @@ -820,6 +820,10 @@ resolve_inst({bif2,Args},Imports,_,_) ->
[F,Bif,A1,A2,Reg] = resolve_args(Args),
{extfunc,_Mod,BifName,_Arity} = lookup(Bif+1,Imports),
{bif,BifName,F,[A1,A2],Reg};
resolve_inst({bif3,Args},Imports,_,_) ->
[F,Bif,A1,A2,A3,Reg] = resolve_args(Args),
{extfunc,_Mod,BifName,_Arity} = lookup(Bif+1,Imports),
{bif,BifName,F,[A1,A2,A3],Reg};
resolve_inst({allocate,[{u,X0},{u,X1}]},_,_,_) ->
{allocate,X0,X1};
resolve_inst({allocate_heap,[{u,X0},{u,X1},{u,X2}]},_,_,_) ->
Expand Down
1 change: 1 addition & 0 deletions lib/compiler/src/beam_ssa_codegen.erl
Original file line number Diff line number Diff line change
Expand Up @@ -2792,6 +2792,7 @@ is_gc_bif(node, [_]) -> false;
is_gc_bif(element, [_,_]) -> false;
is_gc_bif(get, [_]) -> false;
is_gc_bif(is_map_key, [_,_]) -> false;
is_gc_bif(is_integer, [_,_,_]) -> false;
is_gc_bif(map_get, [_,_]) -> false;
is_gc_bif(tuple_size, [_]) -> false;
is_gc_bif(Bif, Args) ->
Expand Down
51 changes: 50 additions & 1 deletion lib/compiler/src/beam_ssa_opt.erl
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,7 @@ module_passes(Opts) ->
%% are repeated as required.
repeated_passes(Opts) ->
Ps = [?PASS(ssa_opt_live),
?PASS(ssa_opt_is_between),
?PASS(ssa_opt_ne),
?PASS(ssa_opt_bs_create_bin),
?PASS(ssa_opt_dead),
Expand Down Expand Up @@ -549,7 +550,8 @@ merge_tuple_update_1([], Tuple) ->
%%%

ssa_opt_split_blocks({#opt_st{ssa=Blocks0,cnt=Count0}=St, FuncDb}) ->
P = fun(#b_set{op={bif,element}}) -> true;
P = fun(#b_set{op={bif,is_integer},args=[_,_,_]}) -> true;
(#b_set{op={bif,element}}) -> true;
(#b_set{op=call}) -> true;
(#b_set{op=bs_init_writable}) -> true;
(#b_set{op=make_fun}) -> true;
Expand All @@ -559,6 +561,53 @@ ssa_opt_split_blocks({#opt_st{ssa=Blocks0,cnt=Count0}=St, FuncDb}) ->
{Blocks,Count} = beam_ssa:split_blocks_before(RPO, P, Blocks0, Count0),
{St#opt_st{ssa=Blocks,cnt=Count}, FuncDb}.

%%%
%%% BIF is_integer/3 tests whether a number is between a given range.
%%% When the range is constant, rewrite it into 3 BIFs: is_integer/1 and two
%%% =<'s to enable later optimization.
%%%
ssa_opt_is_between({#opt_st{ssa=Blocks0,cnt=Count0}=St, FuncDb}) ->
{Blocks1, Count1} = ssa_opt_is_between_1(Blocks0, Count0),
{St#opt_st{ssa=Blocks1,cnt=Count1}, FuncDb}.

ssa_opt_is_between_1([{L,#b_blk{}=B}=Blk0|Ls0], Count0) ->
case B of
#b_blk{is=[#b_set{op={bif,is_integer},dst=Bool1,
args=[_,#b_literal{val=Min},
#b_literal{val=Max}]}],
last=#b_br{bool=Bool1}}=Blk when is_integer(Min),
is_integer(Max),
Min =< Max ->
{Blk1, Count1} = is_between_rewrite(Count0, L, Blk),
{Ls1, Count2} = ssa_opt_is_between_1(Ls0, Count1),
{Blk1++Ls1, Count2};
#b_blk{} ->
{Ls1, Count1} = ssa_opt_is_between_1(Ls0, Count0),
{[Blk0|Ls1], Count1}
end;
ssa_opt_is_between_1([], Count0) ->
{[], Count0}.

is_between_rewrite(Count0, L, Blk0) ->
LowerL = Count0,
UpperL = Count0 + 1,
LowerBool = #b_var{name=Count0},
UpperBool = #b_var{name=Count0 + 1},
Count = Count0 + 2,
#b_blk{is=[#b_set{dst=Bool1,args=[Term,LB,UB]}|_],
last=#b_br{fail=Fail}=Br0} = Blk0,
Blk1 = Blk0#b_blk{is=[#b_set{op={bif,is_integer},dst=Bool1,
args=[Term]}],
last=#b_br{bool=Bool1,succ=LowerL,fail=Fail}},
BlkLower = #b_blk{is=[#b_set{op={bif,'=<'},dst=LowerBool,
args=[LB,Term]}],
last=#b_br{bool=LowerBool,succ=UpperL,fail=Fail}},
BlkUpper = #b_blk{is=[#b_set{op={bif,'=<'},dst=UpperBool,
args=[Term,UB]}],
last=Br0#b_br{bool=UpperBool}},
Blocks = [{L, Blk1}, {LowerL, BlkLower}, {UpperL, BlkUpper}],
{Blocks, Count}.

%%%
%%% Coalesce phi nodes.
%%%
Expand Down
Loading
Loading