aboutsummaryrefslogtreecommitdiffstats
path: root/lib/hipe/icode/hipe_icode_primops.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/hipe/icode/hipe_icode_primops.erl')
-rw-r--r--lib/hipe/icode/hipe_icode_primops.erl963
1 files changed, 963 insertions, 0 deletions
diff --git a/lib/hipe/icode/hipe_icode_primops.erl b/lib/hipe/icode/hipe_icode_primops.erl
new file mode 100644
index 0000000000..b0fe7eb708
--- /dev/null
+++ b/lib/hipe/icode/hipe_icode_primops.erl
@@ -0,0 +1,963 @@
+%% -*- erlang-indent-level: 2 -*-
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2001-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Copyright (c) 2001 by Erik Johansson. All Rights Reserved
+%% ====================================================================
+%% Filename : hipe_icode_primops.erl
+%% Module : hipe_icode_primops
+%% Purpose :
+%% Notes :
+%% History : * 2001-06-13 Erik Johansson ([email protected]):
+%% Created.
+%%
+%% $Id$
+%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-module(hipe_icode_primops).
+
+-export([is_safe/1, fails/1, pp/2, type/1, type/2, arg_types/1]).
+
+-include("hipe_icode.hrl").
+-include("hipe_icode_primops.hrl").
+
+%%---------------------------------------------------------------------
+
+%% Note that 'unsafe_...' operations are generally "safe", i.e., it is
+%% typically unsafe to use them unless you have extra information about
+%% the call (e.g., if the types are known). However, if they have been
+%% correctly introduced in the code, most of them are also OK to remove
+%% if the result is not used.
+
+-spec is_safe(icode_primop()) -> boolean().
+
+is_safe('+') -> false;
+is_safe('/') -> false;
+is_safe('*') -> false;
+is_safe('-') -> false;
+is_safe('bsr') -> false;
+is_safe('bsl') -> false;
+is_safe('band') -> false;
+is_safe('bor') -> false;
+is_safe('bxor') -> false;
+is_safe('bnot') -> false;
+is_safe('div') -> false;
+is_safe('rem') -> false;
+is_safe(call_fun) -> false;
+is_safe(check_get_msg) -> false;
+is_safe(clear_timeout) -> false;
+is_safe(cons) -> true;
+%% is_safe(conv_to_float) -> false;
+is_safe(extra_unsafe_add) -> true;
+is_safe(extra_unsafe_sub) -> true;
+is_safe(fcheckerror) -> false;
+is_safe(fclearerror) -> false;
+is_safe(fp_add) -> false;
+is_safe(fp_div) -> false;
+is_safe(fp_mul) -> false;
+is_safe(fp_sub) -> false;
+is_safe(mktuple) -> true;
+is_safe(next_msg) -> false;
+is_safe(redtest) -> false;
+is_safe(select_msg) -> false;
+is_safe(self) -> true;
+is_safe(set_timeout) -> false;
+is_safe(suspend_msg) -> false;
+is_safe(unsafe_add) -> true;
+is_safe(unsafe_band) -> true;
+is_safe(unsafe_bnot) -> true;
+is_safe(unsafe_bor) -> true;
+is_safe(unsafe_bsl) -> true;
+is_safe(unsafe_bsr) -> true;
+is_safe(unsafe_bxor) -> true;
+is_safe(unsafe_hd) -> true;
+is_safe(unsafe_sub) -> true;
+is_safe(unsafe_tag_float) -> true;
+is_safe(unsafe_tl) -> true;
+is_safe(unsafe_untag_float) -> true;
+is_safe(#apply_N{}) -> false;
+is_safe(#closure_element{}) -> true;
+is_safe(#element{}) -> false;
+%% is_safe(#gc_test{}) -> ???
+is_safe({hipe_bs_primop, {bs_start_match, _}}) -> false;
+is_safe({hipe_bs_primop, {{bs_start_match, bitstr}, _}}) -> true;
+is_safe({hipe_bs_primop, {{bs_start_match, ok_matchstate}, _}}) -> false;
+is_safe({hipe_bs_primop, {bs_get_binary, _, _}}) -> false;
+is_safe({hipe_bs_primop, {bs_get_binary_all, _, _}}) -> false;
+is_safe({hipe_bs_primop, {bs_get_binary_all_2, _, _}}) -> false;
+is_safe({hipe_bs_primop, {bs_get_integer, _, _}}) -> false;
+is_safe({hipe_bs_primop, {bs_get_float, _, _}}) -> false;
+is_safe({hipe_bs_primop, {bs_skip_bits, _}}) -> false;
+is_safe({hipe_bs_primop, {bs_skip_bits_all, _, _}}) -> false;
+is_safe({hipe_bs_primop, {bs_test_tail, _}}) -> false;
+is_safe({hipe_bs_primop, {bs_restore, _}}) -> true;
+is_safe({hipe_bs_primop, {bs_save, _}}) -> true;
+is_safe({hipe_bs_primop, {bs_add, _}}) -> false;
+is_safe({hipe_bs_primop, {bs_add, _, _}}) -> false;
+is_safe({hipe_bs_primop, bs_bits_to_bytes}) -> false;
+is_safe({hipe_bs_primop, bs_bits_to_bytes2}) -> false;
+is_safe({hipe_bs_primop, {bs_init, _}}) -> false;
+is_safe({hipe_bs_primop, {bs_init, _, _}}) -> false;
+is_safe({hipe_bs_primop, {bs_init_bits, _}}) -> false;
+is_safe({hipe_bs_primop, {bs_init_bits, _, _}}) -> false;
+is_safe({hipe_bs_primop, {bs_put_binary, _, _}}) -> false;
+is_safe({hipe_bs_primop, {bs_put_binary_all, _}}) -> false;
+is_safe({hipe_bs_primop, {bs_put_float, _, _, _}}) -> false;
+is_safe({hipe_bs_primop, {bs_put_integer, _, _, _}}) -> false;
+is_safe({hipe_bs_primop, {bs_put_string, _, _}}) -> false;
+is_safe({hipe_bs_primop, bs_put_utf8}) -> false;
+is_safe({hipe_bs_primop, bs_utf8_size}) -> true;
+is_safe({hipe_bs_primop, bs_get_utf8}) -> false;
+is_safe({hipe_bs_primop, bs_utf16_size}) -> true;
+is_safe({hipe_bs_primop, {bs_put_utf16, _}}) -> false;
+is_safe({hipe_bs_primop, {bs_get_utf16, _}}) -> false;
+is_safe({hipe_bs_primop, bs_validate_unicode}) -> false;
+is_safe({hipe_bs_primop, bs_validate_unicode_retract}) -> false;
+is_safe({hipe_bs_primop, {unsafe_bs_put_integer, _, _, _}}) -> false;
+is_safe({hipe_bs_primop, bs_final}) -> true;
+is_safe({hipe_bs_primop, bs_context_to_binary}) -> true;
+is_safe({hipe_bs_primop, {bs_test_unit, _}}) -> false;
+is_safe({hipe_bs_primop, {bs_match_string, _, _}}) -> false;
+is_safe({hipe_bs_primop, {bs_append, _, _, _, _}}) -> false;
+is_safe({hipe_bs_primop, {bs_private_append, _, _}}) -> false;
+is_safe({hipe_bs_primop, bs_init_writable}) -> true;
+is_safe(#mkfun{}) -> true;
+is_safe(#unsafe_element{}) -> true;
+is_safe(#unsafe_update_element{}) -> true.
+
+
+-spec fails(icode_funcall()) -> boolean().
+
+fails('+') -> true;
+fails('-') -> true;
+fails('*') -> true;
+fails('/') -> true;
+fails('bnot') -> true;
+fails('band') -> true;
+fails('bor') -> true;
+fails('bsl') -> true;
+fails('bsr') -> true;
+fails('bxor') -> true;
+fails('div') -> true;
+fails('rem') -> true;
+fails(call_fun) -> true;
+fails(check_get_msg) -> true;
+fails(clear_timeout) -> false;
+fails(cons) -> false;
+fails(conv_to_float) -> true;
+fails(extra_unsafe_add) -> false;
+fails(extra_unsafe_sub) -> false;
+fails(fcheckerror) -> true;
+fails(fclearerror) -> false;
+fails(fp_add) -> false;
+fails(fp_div) -> false;
+fails(fp_mul) -> false;
+fails(fp_sub) -> false;
+fails(mktuple) -> false;
+fails(next_msg) -> false;
+fails(redtest) -> false;
+fails(select_msg) -> false;
+fails(self) -> false;
+fails(set_timeout) -> true;
+fails(suspend_msg) -> false;
+fails(unsafe_untag_float) -> false;
+fails(unsafe_tag_float) -> false;
+fails(unsafe_add) -> false;
+fails(unsafe_band) -> false;
+fails(unsafe_bnot) -> false;
+fails(unsafe_bor) -> false;
+fails(unsafe_bsl) -> false;
+fails(unsafe_bsr) -> false;
+fails(unsafe_bxor) -> false;
+fails(unsafe_hd) -> false;
+fails(unsafe_sub) -> false;
+%% fails(unsafe_tag_float) -> false;
+fails(unsafe_tl) -> false;
+%% fails(unsafe_untag_float) -> false;
+fails(#apply_N{}) -> true;
+fails(#closure_element{}) -> false;
+fails(#element{}) -> true;
+%% fails(#gc_test{}) -> ???
+fails({hipe_bs_primop, {bs_start_match, _}}) -> true;
+fails({hipe_bs_primop, {{bs_start_match, bitstr}, _}}) -> true;
+fails({hipe_bs_primop, {{bs_start_match, ok_matchstate}, _}}) -> false;
+fails({hipe_bs_primop, {bs_get_binary, _, _}}) -> true;
+fails({hipe_bs_primop, {bs_get_binary_all, _, _}}) -> true;
+fails({hipe_bs_primop, {bs_get_binary_all_2, _, _}}) -> true;
+fails({hipe_bs_primop, {bs_get_integer, _, _}}) -> true;
+fails({hipe_bs_primop, {bs_get_float, _, _}}) -> true;
+fails({hipe_bs_primop, {bs_skip_bits, _}}) -> true;
+fails({hipe_bs_primop, {bs_skip_bits_all, _, _}}) -> true;
+fails({hipe_bs_primop, {bs_test_tail, _}}) -> true;
+fails({hipe_bs_primop, {bs_restore, _}}) -> false;
+fails({hipe_bs_primop, {bs_save, _}}) -> false;
+fails({hipe_bs_primop, bs_context_to_binary}) -> false;
+fails({hipe_bs_primop, {bs_test_unit, _}}) -> true;
+fails({hipe_bs_primop, {bs_match_string, _, _}}) -> true;
+fails({hipe_bs_primop, {bs_add, _}}) -> true;
+fails({hipe_bs_primop, {bs_add, _, _}}) -> true;
+fails({hipe_bs_primop, bs_bits_to_bytes}) -> true;
+fails({hipe_bs_primop, bs_bits_to_bytes2}) -> true;
+fails({hipe_bs_primop, {bs_init, _}}) -> true;
+fails({hipe_bs_primop, {bs_init, _, _}}) -> true;
+fails({hipe_bs_primop, {bs_init_bits, _}}) -> true;
+fails({hipe_bs_primop, {bs_init_bits, _, _}}) -> true;
+fails({hipe_bs_primop, {bs_put_binary, _, _}}) -> true;
+fails({hipe_bs_primop, {bs_put_binary_all, _}}) -> true;
+fails({hipe_bs_primop, {bs_put_float, _, _, _}}) -> true;
+fails({hipe_bs_primop, {bs_put_integer, _, _, _}}) -> true;
+fails({hipe_bs_primop, {bs_put_string, _, _}}) -> true;
+fails({hipe_bs_primop, bs_put_utf8}) -> true;
+fails({hipe_bs_primop, bs_utf8_size}) -> false;
+fails({hipe_bs_primop, bs_get_utf8}) -> true;
+fails({hipe_bs_primop, bs_utf16_size}) -> false;
+fails({hipe_bs_primop, {bs_put_utf16, _}}) -> true;
+fails({hipe_bs_primop, {bs_get_utf16, _}}) -> true;
+fails({hipe_bs_primop, bs_validate_unicode}) -> true;
+fails({hipe_bs_primop, bs_validate_unicode_retract}) -> true;
+fails({hipe_bs_primop, {unsafe_bs_put_integer, _, _, _}}) -> true;
+fails({hipe_bs_primop, bs_final}) -> false;
+fails({hipe_bs_primop, {bs_append, _, _, _, _}}) -> true;
+fails({hipe_bs_primop, {bs_private_append, _, _}}) -> true;
+fails({hipe_bs_primop, bs_init_writable}) -> true;
+fails(#mkfun{}) -> false;
+fails(#unsafe_element{}) -> false;
+fails(#unsafe_update_element{}) -> false;
+%% Apparently, we are calling fails/1 for all MFAs which are compiled.
+%% This is weird and we should restructure the compiler to avoid
+%% calling fails/1 for things that are not primops.
+fails({M, F, A}) when is_atom(M), is_atom(F), is_integer(A), 0 =< A, A =< 255 ->
+ %% Yes, we should move this.
+ not erl_bifs:is_safe(M, F, A).
+
+%%=====================================================================
+%% Pretty printing
+%%=====================================================================
+
+-spec pp(io:device(), icode_primop()) -> 'ok'.
+
+pp(Dev, Op) ->
+ case Op of
+ #apply_N{arity = N} ->
+ io:format(Dev, "apply_N<~w>/", [N]);
+ #closure_element{n = N} ->
+ io:format(Dev, "closure_element<~w>", [N]);
+ #element{} ->
+ io:format(Dev, "element", []);
+ #gc_test{need = N} ->
+ io:format(Dev, "gc_test<~w>", [N]);
+ {hipe_bs_primop, BsOp} ->
+ case BsOp of
+ {bs_put_binary_all, Flags} ->
+ io:format(Dev, "bs_put_binary_all<~w>", [Flags]);
+ {bs_put_binary, Size} ->
+ io:format(Dev, "bs_put_binary<~w>", [Size]);
+ {bs_put_binary, Flags, Size} ->
+ io:format(Dev, "bs_put_binary<~w, ~w>", [Flags, Size]);
+ {bs_put_float, Flags, Size, _ConstInfo} ->
+ io:format(Dev, "bs_put_float<~w, ~w>", [Flags, Size]);
+ {bs_put_string, String, SizeInBytes} ->
+ io:format(Dev, "bs_put_string<~w, ~w>", [String, SizeInBytes]);
+ {bs_put_integer, Bits, Flags, _ConstInfo} ->
+ io:format(Dev, "bs_put_integer<~w, ~w>", [Bits, Flags]);
+ {unsafe_bs_put_integer, Bits, Flags, _ConstInfo} ->
+ io:format(Dev, "unsafe_bs_put_integer<~w, ~w>", [Bits, Flags]);
+ {bs_skip_bits_all, Unit, Flags} ->
+ io:format(Dev, "bs_skip_bits_all<~w,~w>", [Unit, Flags]);
+ {bs_skip_bits, Unit} ->
+ io:format(Dev, "bs_skip_bits<~w>", [Unit]);
+ {bs_start_match, Max} ->
+ io:format(Dev, "bs_start_match<~w>", [Max]);
+ {{bs_start_match, Type}, Max} ->
+ io:format(Dev, "bs_start_match<~w,~w>", [Type,Max]);
+ {bs_match_string, String, SizeInBytes} ->
+ io:format(Dev, "bs_match_string<~w, ~w>", [String, SizeInBytes]);
+ {bs_get_integer, Size, Flags} ->
+ io:format(Dev, "bs_get_integer<~w, ~w>", [Size, Flags]);
+ {bs_get_float, Size, Flags} ->
+ io:format(Dev, "bs_get_float<~w, ~w>", [Size, Flags]);
+ {bs_get_binary, Size, Flags} ->
+ io:format(Dev, "bs_get_binary<~w, ~w>", [Size, Flags]);
+ {bs_get_binary_all, Unit, Flags} ->
+ io:format(Dev, "bs_get_binary_all<~w,~w>", [Unit, Flags]);
+ {bs_get_binary_all_2, Unit, Flags} ->
+ io:format(Dev, "bs_get_binary_all<~w,~w>", [Unit, Flags]);
+ {bs_test_tail, NumBits} ->
+ io:format(Dev, "bs_test_tail<~w>", [NumBits]);
+ {bs_test_unit, Unit} ->
+ io:format(Dev, "bs_test_unit<~w>", [Unit]);
+ bs_context_to_binary ->
+ io:format(Dev, "bs_context_to_binary", []);
+ {bs_restore, Index} ->
+ io:format(Dev, "bs_restore<~w>", [Index]);
+ {bs_save, Index} ->
+ io:format(Dev, "bs_save<~w>", [Index]);
+ {bs_init, Size, Flags} ->
+ io:format(Dev, "bs_init<~w, ~w>", [Size, Flags]);
+ {bs_init,Flags} ->
+ io:format(Dev, "bs_init<~w>", [Flags]);
+ {bs_init_bits, Size, Flags} ->
+ io:format(Dev, "bs_init_bits<~w, ~w>", [Size, Flags]);
+ {bs_init_bits, Flags} ->
+ io:format(Dev, "bs_init_bits<~w>", [Flags]);
+ {bs_add, Unit} ->
+ io:format(Dev, "bs_add<~w>", [Unit]);
+ {bs_add, Const, Unit} ->
+ io:format(Dev, "bs_add<~w, ~w>", [Const, Unit]);
+ {bs_append, X, Y, Z, W} ->
+ io:format(Dev, "bs_append<~w, ~w, ~w, ~w>", [X, Y, Z, W]);
+ {bs_private_append, U, Flags} ->
+ io:format(Dev, "bs_private_append<~w, ~w>", [U, Flags]);
+ bs_bits_to_bytes ->
+ io:format(Dev, "bs_bits_to_bytes", []);
+ bs_bits_to_bytes2 ->
+ io:format(Dev, "bs_bits_to_bytes2", []);
+ bs_utf8_size ->
+ io:format(Dev, "bs_utf8_size", []);
+ bs_put_utf8 ->
+ io:format(Dev, "bs_put_utf8", []);
+ bs_get_utf8 ->
+ io:format(Dev, "bs_get_utf8", []);
+ bs_utf16_size ->
+ io:format(Dev, "bs_utf16_size", []);
+ {bs_put_utf16, Flags} ->
+ io:format(Dev, "bs_put_utf16<~w>", [Flags]);
+ {bs_get_utf16, Flags} ->
+ io:format(Dev, "bs_get_utf16<~w>", [Flags]);
+ bs_validate_unicode ->
+ io:format(Dev, "bs_validate_unicode", []);
+ bs_validate_unicode_retract ->
+ io:format(Dev, "bs_validate_unicode_retract", []);
+ bs_final ->
+ io:format(Dev, "bs_final", []);
+ bs_final2 ->
+ io:format(Dev, "bs_final2", []);
+ bs_init_writable ->
+ io:format(Dev, "bs_init_writable", [])
+ end;
+ #mkfun{mfa = {Mod, Fun, Arity}, magic_num = Unique, index = I} ->
+ io:format(Dev, "mkfun<~w,~w,~w,~w,~w>", [Mod, Fun, Arity, Unique, I]);
+ #unsafe_element{index = N} ->
+ io:format(Dev, "unsafe_element<~w>", [N]);
+ #unsafe_update_element{index = N} ->
+ io:format(Dev, "unsafe_update_element<~w>", [N]);
+ Fun when is_atom(Fun) ->
+ io:format(Dev, "~w", [Fun])
+ end.
+
+%%=====================================================================
+%% Type handling
+%%=====================================================================
+
+-spec type(icode_funcall(), [erl_types:erl_type()]) -> erl_types:erl_type().
+
+type(Primop, Args) ->
+ case Primop of
+%%% -----------------------------------------------------
+%%% Arithops
+ '+' ->
+ erl_bif_types:type(erlang, '+', 2, Args);
+ '-' ->
+ erl_bif_types:type(erlang, '-', 2, Args);
+ '*' ->
+ erl_bif_types:type(erlang, '*', 2, Args);
+ '/' ->
+ erl_bif_types:type(erlang, '/', 2, Args);
+ 'band' ->
+ erl_bif_types:type(erlang, 'band', 2, Args);
+ 'bnot' ->
+ erl_bif_types:type(erlang, 'bnot', 1, Args);
+ 'bor' ->
+ erl_bif_types:type(erlang, 'bor', 2, Args);
+ 'bxor' ->
+ erl_bif_types:type(erlang, 'bxor', 2, Args);
+ 'bsl' ->
+ erl_bif_types:type(erlang, 'bsl', 2, Args);
+ 'bsr' ->
+ erl_bif_types:type(erlang, 'bsr', 2, Args);
+ 'div' ->
+ erl_bif_types:type(erlang, 'div', 2, Args);
+ 'rem' ->
+ erl_bif_types:type(erlang, 'rem', 2, Args);
+ extra_unsafe_add ->
+ erl_bif_types:type(erlang, '+', 2, Args);
+ unsafe_add ->
+ erl_bif_types:type(erlang, '+', 2, Args);
+ unsafe_bnot ->
+ erl_bif_types:type(erlang, 'bnot', 1, Args);
+ unsafe_bor ->
+ erl_bif_types:type(erlang, 'bor', 2, Args);
+ unsafe_band ->
+ erl_bif_types:type(erlang, 'band', 2, Args);
+ unsafe_bxor ->
+ erl_bif_types:type(erlang, 'bxor', 2, Args);
+ unsafe_sub ->
+ erl_bif_types:type(erlang, '-', 2, Args);
+%%% -----------------------------------------------------
+%%% Lists
+ cons ->
+ [HeadType, TailType] = Args,
+ erl_types:t_cons(HeadType, TailType);
+ unsafe_hd ->
+ [Type] = Args,
+ case erl_types:t_is_cons(Type) of
+ true -> erl_types:t_cons_hd(Type);
+ false -> erl_types:t_none()
+ end;
+ unsafe_tl ->
+ [Type] = Args,
+ case erl_types:t_is_cons(Type) of
+ true -> erl_types:t_cons_tl(Type);
+ false -> erl_types:t_none()
+ end;
+%%% -----------------------------------------------------
+%%% Tuples
+ mktuple ->
+ erl_types:t_tuple(Args);
+ #element{} ->
+ erl_bif_types:type(erlang, element, 2, Args);
+ #unsafe_element{index = N} ->
+ [Type] = Args,
+ case erl_types:t_is_tuple(Type) of
+ false ->
+ erl_types:t_none();
+ true ->
+ Index = erl_types:t_from_term(N),
+ erl_bif_types:type(erlang, element, 2, [Index|Args])
+ end;
+ #unsafe_update_element{index = N} ->
+ %% Same, same
+ erl_bif_types:type(erlang, setelement, 3, [erl_types:t_integer(N)|Args]);
+%%% -----------------------------------------------------
+%%% Floats
+ fclearerror ->
+ erl_types:t_any();
+ fcheckerror ->
+ erl_types:t_any();
+ unsafe_tag_float ->
+ erl_types:t_float();
+ %% These might look surprising, but the return is an untagged
+ %% float and we have no type for untagged values.
+ conv_to_float ->
+ erl_types:t_any();
+ unsafe_untag_float ->
+ erl_types:t_any();
+ fp_add ->
+ erl_types:t_any();
+ fp_sub ->
+ erl_types:t_any();
+ fp_mul ->
+ erl_types:t_any();
+ fp_div ->
+ erl_types:t_any();
+ fnegate ->
+ erl_types:t_any();
+%%% -----------------------------------------------------
+%%%
+ {hipe_bs_primop, {bs_start_match, Max}} ->
+ [Type] = Args,
+ Init =
+ erl_types:t_sup(
+ erl_types:t_matchstate_present(Type),
+ erl_types:t_inf(erl_types:t_bitstr(1, 0), Type)),
+ case erl_types:t_is_none(Init) of
+ true ->
+ erl_types:t_none();
+ false ->
+ erl_types:t_matchstate(Init, Max)
+ end;
+ {hipe_bs_primop, {{bs_start_match, _}, Max}} ->
+ [Type] = Args,
+ Init =
+ erl_types:t_sup(
+ erl_types:t_matchstate_present(Type),
+ erl_types:t_inf(erl_types:t_bitstr(1, 0), Type)),
+ case erl_types:t_is_none(Init) of
+ true ->
+ erl_types:t_none();
+ false ->
+ erl_types:t_matchstate(Init, Max)
+ end;
+ {hipe_bs_primop, {bs_get_integer, Size, Flags}} ->
+ Signed = Flags band 4,
+ [MatchState|RestArgs] = Args,
+ BinType = erl_types:t_matchstate_present(MatchState),
+ case RestArgs of
+ [] ->
+ NewBinType = match_bin(erl_types:t_bitstr(0, Size), BinType),
+ NewMatchState =
+ erl_types:t_matchstate_update_present(NewBinType, MatchState),
+ if Signed =:= 0 ->
+ erl_types:t_product([erl_types:t_from_range(0, 1 bsl Size - 1),
+ NewMatchState]);
+ Signed =:= 4 ->
+ erl_types:t_product([erl_types:t_from_range(- (1 bsl (Size-1)),
+ (1 bsl (Size-1)) - 1),
+ NewMatchState])
+ end;
+ [_Arg] ->
+ NewBinType = match_bin(erl_types:t_bitstr(Size, 0), BinType),
+ NewMatchState =
+ erl_types:t_matchstate_update_present(NewBinType, MatchState),
+ erl_types:t_product([erl_types:t_integer(), NewMatchState])
+ end;
+ {hipe_bs_primop, {bs_get_float, Size, _Flags}} ->
+ [MatchState|RestArgs] = Args,
+ BinType = erl_types:t_matchstate_present(MatchState),
+ NewBinType =
+ case RestArgs of
+ [] ->
+ match_bin(erl_types:t_bitstr(0,Size),BinType);
+ [_Arg] ->
+ erl_types:t_sup(match_bin(erl_types:t_bitstr(0, 32), BinType),
+ match_bin(erl_types:t_bitstr(0, 64), BinType))
+ end,
+ NewMatchState = erl_types:t_matchstate_update_present(NewBinType, MatchState),
+ erl_types:t_product([erl_types:t_float(), NewMatchState]);
+ {hipe_bs_primop, {bs_get_binary, Size, _Flags}} ->
+ [MatchState|RestArgs] = Args,
+ BinType = erl_types:t_matchstate_present(MatchState),
+ case RestArgs of
+ [] ->
+ NewBinType = match_bin(erl_types:t_bitstr(0, Size), BinType),
+ NewMatchState = erl_types:t_matchstate_update_present(NewBinType, MatchState),
+ erl_types:t_product([erl_types:t_bitstr(0,Size), NewMatchState]);
+ [ArgType] ->
+ Posint = erl_types:t_inf(erl_types:t_non_neg_integer(), ArgType),
+ case erl_types:t_is_none(Posint) of
+ true ->
+ erl_types:t_product([erl_types:t_none(),
+ erl_types:t_matchstate_update_present(
+ erl_types:t_none(),
+ MatchState)]);
+ false ->
+ OutBinType =
+ erl_types:t_bitstr(Size,erl_types:number_min(Posint)*Size),
+ NewBinType = match_bin(OutBinType,BinType),
+ NewMatchState = erl_types:t_matchstate_update_present(NewBinType, MatchState),
+ erl_types:t_product([OutBinType, NewMatchState])
+ end
+ end;
+ {hipe_bs_primop, {bs_get_binary_all, Unit, _Flags}} ->
+ [MatchState] = Args,
+ BinType = erl_types:t_matchstate_present(MatchState),
+ erl_types:t_inf(BinType, erl_types:t_bitstr(Unit, 0));
+ {hipe_bs_primop, {bs_get_binary_all_2, Unit, _Flags}} ->
+ [MatchState] = Args,
+ BinType = erl_types:t_matchstate_present(MatchState),
+ erl_types:t_product(
+ [erl_types:t_inf(BinType,erl_types:t_bitstr(Unit, 0)),
+ erl_types:t_matchstate_update_present(
+ erl_types:t_bitstr(0, 0), MatchState)]);
+ {hipe_bs_primop, {bs_skip_bits_all, _Unit, _Flags}} ->
+ [MatchState] = Args,
+ erl_types:t_matchstate_update_present(erl_types:t_bitstr(0,0),MatchState);
+ {hipe_bs_primop, {bs_skip_bits, Size}} ->
+ [MatchState|RestArgs] = Args,
+ BinType = erl_types:t_matchstate_present(MatchState),
+ NewBinType =
+ case RestArgs of
+ [] ->
+ match_bin(erl_types:t_bitstr(0, Size), BinType);
+ [_Arg] ->
+ match_bin(erl_types:t_bitstr(Size, 0), BinType)
+ end,
+ erl_types:t_matchstate_update_present(NewBinType, MatchState);
+ {hipe_bs_primop, {bs_save, Slot}} ->
+ [MatchState] = Args,
+ BinType = erl_types:t_matchstate_present(MatchState),
+ erl_types:t_matchstate_update_slot(BinType, MatchState, Slot);
+ {hipe_bs_primop, {bs_restore, Slot}} ->
+ [MatchState] = Args,
+ BinType = erl_types:t_matchstate_slot(MatchState, Slot),
+ erl_types:t_matchstate_update_present(BinType, MatchState);
+ {hipe_bs_primop, bs_context_to_binary} ->
+ [Type] = Args,
+ erl_types:t_sup(
+ erl_types:t_subtract(Type, erl_types:t_matchstate()),
+ erl_types:t_matchstate_slot(
+ erl_types:t_inf(Type, erl_types:t_matchstate()), 0));
+ {hipe_bs_primop, {bs_match_string,_,Bytes}} ->
+ [MatchState] = Args,
+ BinType = erl_types:t_matchstate_present(MatchState),
+ NewBinType = match_bin(erl_types:t_bitstr(0, Bytes*8), BinType),
+ erl_types:t_matchstate_update_present(NewBinType, MatchState);
+ {hipe_bs_primop, {bs_test_unit,Unit}} ->
+ [MatchState] = Args,
+ BinType = erl_types:t_matchstate_present(MatchState),
+ NewBinType = erl_types:t_inf(erl_types:t_bitstr(Unit, 0), BinType),
+ erl_types:t_matchstate_update_present(NewBinType, MatchState);
+ {hipe_bs_primop, {bs_add, _, _}} ->
+ erl_types:t_integer();
+ {hipe_bs_primop, {bs_add, _}} ->
+ erl_types:t_integer();
+ {hipe_bs_primop, bs_bits_to_bytes} ->
+ erl_types:t_integer();
+ {hipe_bs_primop, bs_bits_to_bytes2} ->
+ erl_types:t_integer();
+ {hipe_bs_primop, {Name, Size, _Flags, _ConstInfo}}
+ when Name =:= bs_put_integer;
+ Name =:= bs_put_float ->
+ case Args of
+ [_SrcType, _Base, Type] ->
+ erl_types:t_bitstr_concat(Type, erl_types:t_bitstr(0, Size));
+ [_SrcType,_BitsType, _Base, Type] ->
+ erl_types:t_bitstr_concat(Type, erl_types:t_bitstr(Size, 0))
+ end;
+ {hipe_bs_primop, {bs_put_binary, Size, _Flags}} ->
+ case Args of
+ [_SrcType, _Base, Type] ->
+ erl_types:t_bitstr_concat(Type, erl_types:t_bitstr(0, Size));
+ [_SrcType, _BitsType, _Base, Type] ->
+ erl_types:t_bitstr_concat(Type, erl_types:t_bitstr(Size, 0))
+ end;
+ {hipe_bs_primop, {bs_put_binary_all, _Flags}} ->
+ [SrcType, _Base, Type] = Args,
+ erl_types:t_bitstr_concat(SrcType,Type);
+ {hipe_bs_primop, {bs_put_string, _, Size}} ->
+ [_Base, Type] = Args,
+ erl_types:t_bitstr_concat(Type, erl_types:t_bitstr(0, 8*Size));
+ {hipe_bs_primop, bs_utf8_size} ->
+ [_Arg] = Args,
+ erl_types:t_from_range(1, 4);
+ {hipe_bs_primop, bs_utf16_size} ->
+ [_Arg] = Args,
+ erl_types:t_from_range(2, 4); % XXX: really 2 | 4
+ {hipe_bs_primop, bs_final} ->
+ [_Base, Type] = Args,
+ Type;
+ {hipe_bs_primop, {bs_init, Size, _Flags}} ->
+ erl_types:t_product(
+ [erl_types:t_bitstr(0, Size*8),
+ erl_types:t_any(),
+ erl_types:t_bitstr(0, 0)]);
+ {hipe_bs_primop, {bs_init, _Flags}} ->
+ erl_types:t_product(
+ [erl_types:t_binary(),
+ erl_types:t_any(),
+ erl_types:t_bitstr(0, 0)]);
+ {hipe_bs_primop, {bs_init_bits, Size, _Flags}} ->
+ erl_types:t_product(
+ [erl_types:t_bitstr(0, Size),
+ erl_types:t_any(),
+ erl_types:t_bitstr(0, 0)]);
+ {hipe_bs_primop, {bs_init_bits, _Flags}} ->
+ erl_types:t_product(
+ [erl_types:t_bitstr(),
+ erl_types:t_any(),
+ erl_types:t_bitstr(0, 0)]);
+ {hipe_bs_primop, {bs_private_append, _U, _Flags}} ->
+ erl_types:t_product(
+ [erl_types:t_bitstr(),
+ erl_types:t_any(),
+ erl_types:t_bitstr()]);
+ {hipe_bs_primop, {bs_append, _W, _R, _U, _Flags}} ->
+ erl_types:t_product(
+ [erl_types:t_bitstr(),
+ erl_types:t_any(),
+ erl_types:t_bitstr()]);
+ {hipe_bs_primop, bs_init_writable} ->
+ erl_types:t_bitstr(0, 0);
+ {hipe_bs_primop, _BsOp} ->
+ erl_types:t_any();
+%%% -----------------------------------------------------
+%%% Funs
+ #mkfun{mfa = {_M, _F, A}} ->
+ %% Note that the arity includes the bound variables in args
+ erl_types:t_fun(A - length(Args), erl_types:t_any());
+ #apply_N{} ->
+ erl_types:t_any();
+ Op when Op =:= call_fun orelse Op =:= enter_fun ->
+ [Fun0|TailArgs0] = lists:reverse(Args),
+ TailArgs = lists:reverse(TailArgs0),
+ Fun = erl_types:t_inf(erl_types:t_fun(), Fun0),
+ case erl_types:t_is_fun(Fun) of
+ true ->
+ case erl_types:t_fun_args(Fun) of
+ unknown ->
+ erl_types:t_any();
+ FunArgs ->
+ case check_fun_args(FunArgs, TailArgs) of
+ ok ->
+ erl_types:t_fun_range(Fun);
+ error ->
+ erl_types:t_none()
+ end
+ end;
+ false ->
+ erl_types:t_none()
+ end;
+%%% -----------------------------------------------------
+%%% Communication
+ check_get_msg ->
+ erl_types:t_any();
+ clear_timeout ->
+ erl_types:t_any();
+ next_msg ->
+ erl_types:t_any();
+ select_msg ->
+ erl_types:t_any();
+ set_timeout ->
+ erl_types:t_any();
+ suspend_msg ->
+ erl_types:t_any();
+%%% -----------------------------------------------------
+%%% Other
+ #closure_element{} ->
+ erl_types:t_any();
+ redtest ->
+ erl_types:t_any();
+ {M, F, A} ->
+ erl_bif_types:type(M, F, A, Args)
+ end.
+
+
+-spec type(icode_funcall()) -> erl_types:erl_type().
+
+type(Primop) ->
+ case Primop of
+%%% -----------------------------------------------------
+%%% Arithops
+ 'bnot' ->
+ erl_bif_types:type(erlang, 'bnot', 1);
+ '+' ->
+ erl_bif_types:type(erlang, '+', 2);
+ '-' ->
+ erl_bif_types:type(erlang, '-', 2);
+ '*' ->
+ erl_bif_types:type(erlang, '*', 2);
+ '/' ->
+ erl_bif_types:type(erlang, '/', 2);
+ 'div' ->
+ erl_bif_types:type(erlang, 'div', 2);
+ 'rem' ->
+ erl_bif_types:type(erlang, 'rem', 2);
+ 'band' ->
+ erl_bif_types:type(erlang, 'band', 2);
+ 'bor' ->
+ erl_bif_types:type(erlang, 'bor', 2);
+ 'bxor' ->
+ erl_bif_types:type(erlang, 'bxor', 2);
+ 'bsr' ->
+ erl_bif_types:type(erlang, 'bsr', 2);
+ 'bsl' ->
+ erl_bif_types:type(erlang, 'bsl', 2);
+ unsafe_add ->
+ erl_bif_types:type(erlang, '+', 2);
+ extra_unsafe_add ->
+ erl_bif_types:type(erlang, '+', 2);
+ unsafe_sub ->
+ erl_bif_types:type(erlang, '-', 2);
+ unsafe_bor ->
+ erl_bif_types:type(erlang, 'bor', 2);
+ unsafe_band ->
+ erl_bif_types:type(erlang, 'band', 2);
+ unsafe_bxor ->
+ erl_bif_types:type(erlang, 'bxor', 2);
+%%% -----------------------------------------------------
+%%% Lists
+ cons ->
+ erl_types:t_cons();
+ unsafe_hd ->
+ erl_bif_types:type(erlang, hd, 1);
+ unsafe_tl ->
+ erl_bif_types:type(erlang, tl, 1);
+%%% -----------------------------------------------------
+%%% Tuples
+ mktuple ->
+ erl_types:t_tuple();
+ #element{} ->
+ erl_bif_types:type(erlang, element, 2);
+ #unsafe_element{} ->
+ erl_bif_types:type(erlang, element, 2);
+ #unsafe_update_element{} ->
+ erl_bif_types:type(erlang, setelement, 3);
+%%% -----------------------------------------------------
+%%% Floats
+ fclearerror ->
+ erl_types:t_any();
+ fcheckerror ->
+ erl_types:t_any();
+ unsafe_tag_float ->
+ erl_types:t_float();
+ %% These might look surprising, but the return is an untagged
+ %% float and we have no type for untagged values.
+ conv_to_float ->
+ erl_types:t_any();
+ unsafe_untag_float ->
+ erl_types:t_any();
+ fp_add ->
+ erl_types:t_any();
+ fp_sub ->
+ erl_types:t_any();
+ fp_mul ->
+ erl_types:t_any();
+ fp_div ->
+ erl_types:t_any();
+ fnegate ->
+ erl_types:t_any();
+%%% -----------------------------------------------------
+%%% Binaries
+ {hipe_bs_primop, bs_get_utf8} ->
+ erl_types:t_product([erl_types:t_integer(), erl_types:t_matchstate()]);
+ {hipe_bs_primop, {bs_get_utf16, _Flags}} ->
+ erl_types:t_product([erl_types:t_integer(), erl_types:t_matchstate()]);
+ {hipe_bs_primop, {bs_get_integer, _Size, _Flags}} ->
+ erl_types:t_product([erl_types:t_integer(), erl_types:t_matchstate()]);
+ {hipe_bs_primop, {bs_get_float, _, _}} ->
+ erl_types:t_product([erl_types:t_float(), erl_types:t_matchstate()]);
+ {hipe_bs_primop, {bs_get_binary, _, _}} ->
+ erl_types:t_product([erl_types:t_bitstr(), erl_types:t_matchstate()]);
+ {hipe_bs_primop, {bs_get_binary_all, _, _}} ->
+ erl_types:t_bitstr();
+ {hipe_bs_primop, {bs_get_binary_all_2, _, _}} ->
+ erl_types:t_product([erl_types:t_bitstr(), erl_types:t_matchstate()]);
+ {hipe_bs_primop, bs_final} ->
+ erl_types:t_bitstr();
+ {hipe_bs_primop, {bs_init, _, _}} ->
+ erl_types:t_product([erl_types:t_binary(), erl_types:t_bitstr(),
+ erl_types:t_bitstr()]);
+ {hipe_bs_primop, {bs_init, _}} ->
+ erl_types:t_product([erl_types:t_binary(), erl_types:t_bitstr(),
+ erl_types:t_bitstr()]);
+ {hipe_bs_primop, {bs_init_bits, Size, _}} ->
+ erl_types:t_product([erl_types:t_bitstr(0, Size), erl_types:t_bitstr(),
+ erl_types:t_bitstr()]);
+ {hipe_bs_primop, {bs_init_bits, _}} ->
+ erl_types:t_product([erl_types:t_bitstr(), erl_types:t_bitstr(),
+ erl_types:t_bitstr()]);
+ {hipe_bs_primop, {bs_add, _, _}} ->
+ erl_types:t_integer();
+ {hipe_bs_primop, {bs_add, _}} ->
+ erl_types:t_integer();
+ {hipe_bs_primop, bs_bits_to_bytes} ->
+ erl_types:t_integer();
+ {hipe_bs_primop, bs_bits_to_bytes2} ->
+ erl_types:t_integer();
+ {hipe_bs_primop, {bs_private_append, _U, _Flags}} ->
+ erl_types:t_product(
+ [erl_types:t_bitstr(),
+ erl_types:t_any(),
+ erl_types:t_bitstr()]);
+ {hipe_bs_primop, {bs_append, _W, _R, _U, _Flags}} ->
+ erl_types:t_product(
+ [erl_types:t_bitstr(),
+ erl_types:t_any(),
+ erl_types:t_bitstr()]);
+ {hipe_bs_primop, bs_init_writable} ->
+ erl_types:t_bitstr();
+ {hipe_bs_primop, _BsOp} ->
+ erl_types:t_any();
+%%% -----------------------------------------------------
+%%% Funs
+ #mkfun{} ->
+ %% Note that the arity includes the bound variables in args
+ erl_types:t_fun();
+ #apply_N{} ->
+ erl_types:t_any();
+ call_fun ->
+ erl_types:t_any();
+ enter_fun ->
+ erl_types:t_any();
+%%% -----------------------------------------------------
+%%% Communication
+ check_get_msg ->
+ erl_types:t_any();
+ clear_timeout ->
+ erl_types:t_any();
+ next_msg ->
+ erl_types:t_any();
+ select_msg ->
+ erl_types:t_any();
+ set_timeout ->
+ erl_types:t_any();
+ suspend_msg ->
+ erl_types:t_any();
+%%% -----------------------------------------------------
+%%% Other
+ #closure_element{} ->
+ erl_types:t_any();
+ redtest ->
+ erl_types:t_any();
+ {M, F, A} ->
+ erl_bif_types:type(M, F, A)
+ end.
+
+
+%% =====================================================================
+%% @doc
+%% function arg_types returns a list of the demanded argument types for
+%% a bif to succeed.
+
+-spec arg_types(icode_funcall()) -> [erl_types:erl_type()] | 'unknown'.
+
+arg_types(Primop) ->
+ case Primop of
+ {M, F, A} ->
+ erl_bif_types:arg_types(M, F, A);
+ #element{} ->
+ [erl_types:t_pos_fixnum(), erl_types:t_tuple()];
+ '+' ->
+ erl_bif_types:arg_types(erlang, '+', 2);
+ '-' ->
+ erl_bif_types:arg_types(erlang, '-', 2);
+ '*' ->
+ erl_bif_types:arg_types(erlang, '*', 2);
+ '/' ->
+ erl_bif_types:arg_types(erlang, '/', 2);
+ 'band' ->
+ erl_bif_types:arg_types(erlang, 'band', 2);
+ 'bnot' ->
+ erl_bif_types:arg_types(erlang, 'bnot', 1);
+ 'bor' ->
+ erl_bif_types:arg_types(erlang, 'bor', 2);
+ 'bxor' ->
+ erl_bif_types:arg_types(erlang, 'bxor', 2);
+ 'bsl' ->
+ erl_bif_types:arg_types(erlang, 'bsl', 2);
+ 'bsr' ->
+ erl_bif_types:arg_types(erlang, 'bsr', 2);
+ 'div' ->
+ erl_bif_types:arg_types(erlang, 'div', 2);
+ 'rem' ->
+ erl_bif_types:arg_types(erlang, 'rem', 2);
+ _ ->
+ unknown % safe approximation for all primops.
+ end.
+
+%%=====================================================================
+%% Auxiliary functions
+%%=====================================================================
+
+check_fun_args([T1|Left1], [T2|Left2]) ->
+ Inf = erl_types:t_inf(T1, T2),
+ case erl_types:t_inf(Inf, T2) of
+ Inf ->
+ check_fun_args(Left1, Left2);
+ _ ->
+ error
+ end;
+check_fun_args([], []) ->
+ ok;
+check_fun_args(_, _) ->
+ error.
+
+match_bin(Pattern, Match) ->
+ erl_types:t_bitstr_match(Pattern, Match).