diff options
Diffstat (limited to 'lib/compiler/src/beam_receive.erl')
-rw-r--r-- | lib/compiler/src/beam_receive.erl | 416 |
1 files changed, 0 insertions, 416 deletions
diff --git a/lib/compiler/src/beam_receive.erl b/lib/compiler/src/beam_receive.erl deleted file mode 100644 index ddbe67605a..0000000000 --- a/lib/compiler/src/beam_receive.erl +++ /dev/null @@ -1,416 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2010-2018. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%% -%% %CopyrightEnd% -%% - --module(beam_receive). --export([module/2]). --import(lists, [foldl/3,reverse/1,reverse/2]). - -%%% -%%% In code such as: -%%% -%%% Ref = make_ref(), %Or erlang:monitor(process, Pid) -%%% . -%%% . -%%% . -%%% receive -%%% {Ref,Reply} -> Reply -%%% end. -%%% -%%% we know that none of the messages that exist in the message queue -%%% before the call to make_ref/0 can be matched out in the receive -%%% statement. Therefore we can avoid going through the entire message -%%% queue if we introduce two new instructions (here written as -%%% BIFs in pseudo-Erlang): -%%% -%%% recv_mark(SomeUniqInteger), -%%% Ref = make_ref(), -%%% . -%%% . -%%% . -%%% recv_set(SomeUniqInteger), -%%% receive -%%% {Ref,Reply} -> Reply -%%% end. -%%% -%%% The recv_mark/1 instruction will save the current position and -%%% SomeUniqInteger in the process context. The recv_set -%%% instruction will verify that SomeUniqInteger is still stored -%%% in the process context. If it is, it will set the current pointer -%%% for the message queue (the next message to be read out) to the -%%% position that was saved by recv_mark/1. -%%% -%%% The remove_message instruction must be modified to invalidate -%%% the information stored by the previous recv_mark/1, in case there -%%% is another receive executed between the calls to recv_mark/1 and -%%% recv_set/1. -%%% -%%% We use a reference to a label (i.e. a position in the loaded code) -%%% as the SomeUniqInteger. -%%% - --spec module(beam_utils:module_code(), [compile:option()]) -> - {'ok',beam_utils:module_code()}. - -module({Mod,Exp,Attr,Fs0,Lc}, _Opts) -> - Fs = [function(F) || F <- Fs0], - Code = {Mod,Exp,Attr,Fs,Lc}, - {ok,Code}. - -%%% -%%% Local functions. -%%% - -function({function,Name,Arity,Entry,Is}) -> - try - D = beam_utils:index_labels(Is), - {function,Name,Arity,Entry,opt(Is, D, [])} - catch - Class:Error:Stack -> - io:fwrite("Function: ~w/~w\n", [Name,Arity]), - erlang:raise(Class, Error, Stack) - end. - -opt([{call_ext,A,{extfunc,erlang,spawn_monitor,A}}=I0|Is0], D, Acc) - when A =:= 1; A =:= 3 -> - case ref_in_tuple(Is0) of - no -> - opt(Is0, D, [I0|Acc]); - {yes,Regs,Is1,MatchReversed} -> - %% The call creates a brand new reference. Now - %% search for a receive statement in the same - %% function that will match against the reference. - case opt_recv(Is1, Regs, D) of - no -> - opt(Is0, D, [I0|Acc]); - {yes,Is,Lbl} -> - opt(Is, D, MatchReversed++[I0,{recv_mark,{f,Lbl}}|Acc]) - end - end; -opt([{call_ext,Arity,{extfunc,erlang,Name,Arity}}=I|Is0], D, Acc) -> - case creates_new_ref(Name, Arity) of - true -> - %% The call creates a brand new reference. Now - %% search for a receive statement in the same - %% function that will match against the reference. - case opt_recv(Is0, regs_init_x0(), D) of - no -> - opt(Is0, D, [I|Acc]); - {yes,Is,Lbl} -> - opt(Is, D, [I,{recv_mark,{f,Lbl}}|Acc]) - end; - false -> - opt(Is0, D, [I|Acc]) - end; -opt([I|Is], D, Acc) -> - opt(Is, D, [I|Acc]); -opt([], _, Acc) -> - reverse(Acc). - -ref_in_tuple([{test,is_tuple,_,[{x,0}]}=I1, - {test,test_arity,_,[{x,0},2]}=I2, - {block,[{set,[_],[{x,0}],{get_tuple_element,0}}, - {set,[Dst],[{x,0}],{get_tuple_element,1}}|Bl]}=I3|Is]) -> - ref_in_tuple_1(Bl, Dst, Is, [I3,I2,I1]); -ref_in_tuple([{test,is_tuple,_,[{x,0}]}=I1, - {test,test_arity,_,[{x,0},2]}=I2, - {block,[{set,[Dst],[{x,0}],{get_tuple_element,1}}|Bl]}=I3|Is]) -> - ref_in_tuple_1(Bl, Dst, Is, [I3,I2,I1]); -ref_in_tuple(_) -> no. - -ref_in_tuple_1(Bl, Dst, Is, MatchReversed) -> - Regs0 = regs_init_singleton(Dst), - Regs = opt_update_regs_bl(Bl, Regs0), - {yes,Regs,Is,MatchReversed}. - -%% creates_new_ref(Name, Arity) -> true|false. -%% Return 'true' if the BIF Name/Arity will create a new reference. -creates_new_ref(monitor, 2) -> true; -creates_new_ref(make_ref, 0) -> true; -creates_new_ref(_, _) -> false. - -%% opt_recv([Instruction], Regs, LabelIndex) -> no|{yes,[Instruction]} -%% Search for a receive statement that will only retrieve messages -%% that contain the newly created reference (which is currently in {x,0}). -opt_recv(Is, Regs, D) -> - L = gb_sets:empty(), - opt_recv(Is, D, Regs, L, []). - -opt_recv([{label,L}=Lbl,{loop_rec,{f,Fail},_}=Loop|Is], D, R0, _, Acc) -> - R = regs_kill_not_live(0, R0), - case regs_empty(R) of - false -> - %% We now have the new reference in Y registers - %% and the current instruction is the beginning of a - %% receive statement. We must now verify that only messages - %% that contain the reference will be matched. - case opt_ref_used(Is, R, Fail, D) of - false -> - no; - true -> - RecvSet = {recv_set,{f,L}}, - {yes,reverse(Acc, [RecvSet,Lbl,Loop|Is]),L} - end; - true -> - no - end; -opt_recv([I|Is], D, R0, L0, Acc) -> - {R,L} = opt_update_regs(I, R0, L0), - case regs_empty(R) of - true -> - %% The reference is no longer alive. There is no - %% point in continuing the search. - no; - false -> - opt_recv(Is, D, R, L, [I|Acc]) - end; -opt_recv([], _, _, _, _) -> no. - -opt_update_regs({block,Bl}, R, L) -> - {opt_update_regs_bl(Bl, R),L}; -opt_update_regs({call,_,_}, R, L) -> - {regs_kill_not_live(0, R),L}; -opt_update_regs({call_ext,_,_}, R, L) -> - {regs_kill_not_live(0, R),L}; -opt_update_regs({call_fun,_}, R, L) -> - {regs_kill_not_live(0, R),L}; -opt_update_regs({kill,Y}, R, L) -> - {regs_kill([Y], R),L}; -opt_update_regs({'catch',_,{f,Lbl}}, R, L) -> - {R,gb_sets:add(Lbl, L)}; -opt_update_regs({catch_end,_}, R, L) -> - {R,L}; -opt_update_regs({label,Lbl}, R, L) -> - case gb_sets:is_member(Lbl, L) of - false -> - %% We can't allow arbitrary labels (since the receive - %% could be entered without first creating the reference). - {regs_init(),L}; - true -> - %% A catch label for a previously seen catch instruction is OK. - {R,L} - end; -opt_update_regs({'try',_,{f,Lbl}}, R, L) -> - {R,gb_sets:add(Lbl, L)}; -opt_update_regs({try_end,_}, R, L) -> - {R,L}; -opt_update_regs({line,_}, R, L) -> - {R,L}; -opt_update_regs(_I, _R, L) -> - %% Unrecognized instruction. Abort the search. - {regs_init(),L}. - -opt_update_regs_bl([{set,Ds,_,{alloc,Live,_}}|Is], Regs0) -> - Regs1 = regs_kill_not_live(Live, Regs0), - Regs = regs_kill(Ds, Regs1), - opt_update_regs_bl(Is, Regs); -opt_update_regs_bl([{set,[Dst]=Ds,[Src],move}|Is], Regs0) -> - Regs1 = regs_kill(Ds, Regs0), - Regs = case regs_is_member(Src, Regs1) of - false -> Regs1; - true -> regs_add(Dst, Regs1) - end, - opt_update_regs_bl(Is, Regs); -opt_update_regs_bl([{set,Ds,_,_}|Is], Regs0) -> - Regs = regs_kill(Ds, Regs0), - opt_update_regs_bl(Is, Regs); -opt_update_regs_bl([], Regs) -> Regs. - -%% opt_ref_used([Instruction], RefRegs, FailLabel, LabelIndex) -> true|false -%% Return 'true' if it is certain that only messages that contain the same -%% reference as in RefRegs can be matched out. Otherwise return 'false'. -%% -%% Basically, we follow all possible paths through the receive statement. -%% If all paths are safe, we return 'true'. -%% -%% A branch to FailLabel is safe, because it exits the receive statement -%% and no further message may be matched out. -%% -%% If a path hits an comparision between RefRegs and part of the message, -%% that path is safe (any messages that may be matched further down the -%% path is guaranteed to contain the reference). -%% -%% Otherwise, if we hit a 'remove_message' instruction, we give up -%% and return 'false' (the optimization is definitely unsafe). If -%% we hit an unrecognized instruction, we also give up and return -%% 'false' (the optimization may be unsafe). - -opt_ref_used(Is, RefRegs, Fail, D) -> - Done = gb_sets:singleton(Fail), - Regs = regs_init_x0(), - try - _ = opt_ref_used_1(Is, RefRegs, D, Done, Regs), - true - catch - throw:not_used -> - false - end. - -%% This functions only returns if all paths through the receive -%% statement are safe, and throws an 'not_used' term otherwise. -opt_ref_used_1([{block,Bl}|Is], RefRegs, D, Done, Regs0) -> - Regs = opt_ref_used_bl(Bl, Regs0), - opt_ref_used_1(Is, RefRegs, D, Done, Regs); -opt_ref_used_1([{test,is_eq_exact,{f,Fail},Args}|Is], - RefRegs, D, Done0, Regs) -> - Done = opt_ref_used_at(Fail, RefRegs, D, Done0, Regs), - case is_ref_msg_comparison(Args, RefRegs, Regs) of - false -> - opt_ref_used_1(Is, RefRegs, D, Done, Regs); - true -> - %% The instructions that follow (Is) can only be executed - %% if the message contains the same reference as in RefRegs. - Done - end; -opt_ref_used_1([{test,is_ne_exact,{f,Fail},Args}|Is], - RefRegs, D, Done0, Regs) -> - Done = opt_ref_used_1(Is, RefRegs, D, Done0, Regs), - case is_ref_msg_comparison(Args, RefRegs, Regs) of - false -> - opt_ref_used_at(Fail, RefRegs, D, Done, Regs); - true -> - Done - end; -opt_ref_used_1([{test,_,{f,Fail},_}|Is], RefRegs, D, Done0, Regs) -> - Done = opt_ref_used_at(Fail, RefRegs, D, Done0, Regs), - opt_ref_used_1(Is, RefRegs, D, Done, Regs); -opt_ref_used_1([{select,_,_,{f,Fail},List}|_], RefRegs, D, Done, Regs) -> - Lbls = [F || {f,F} <- List] ++ [Fail], - opt_ref_used_in_all(Lbls, RefRegs, D, Done, Regs); -opt_ref_used_1([{label,Lbl}|Is], RefRegs, D, Done, Regs) -> - case gb_sets:is_member(Lbl, Done) of - true -> Done; - false -> opt_ref_used_1(Is, RefRegs, D, Done, Regs) - end; -opt_ref_used_1([{loop_rec_end,_}|_], _, _, Done, _) -> - Done; -opt_ref_used_1([_I|_], _RefReg, _D, _Done, _Regs) -> - %% The optimization may be unsafe. - throw(not_used). - -%% is_ref_msg_comparison(Args, RefRegs, RegisterSet) -> true|false. -%% Return 'true' if Args denotes a comparison between the -%% reference and message or part of the message. -is_ref_msg_comparison([R1,R2], RefRegs, Regs) -> - (regs_is_member(R2, RefRegs) andalso regs_is_member(R1, Regs)) orelse - (regs_is_member(R1, RefRegs) andalso regs_is_member(R2, Regs)). - -opt_ref_used_in_all([L|Ls], RefRegs, D, Done0, Regs) -> - Done = opt_ref_used_at(L, RefRegs, D, Done0, Regs), - opt_ref_used_in_all(Ls, RefRegs, D, Done, Regs); -opt_ref_used_in_all([], _, _, Done, _) -> Done. - -opt_ref_used_at(Fail, RefRegs, D, Done0, Regs) -> - case gb_sets:is_member(Fail, Done0) of - true -> - Done0; - false -> - Is = beam_utils:code_at(Fail, D), - Done = opt_ref_used_1(Is, RefRegs, D, Done0, Regs), - gb_sets:add(Fail, Done) - end. - -opt_ref_used_bl([{set,[],[],remove_message}|_], _) -> - %% We have proved that a message that does not depend on the - %% reference can be matched out. - throw(not_used); -opt_ref_used_bl([{set,Ds,Ss,_}|Is], Regs0) -> - case regs_all_members(Ss, Regs0) of - false -> - %% The destination registers may be assigned values that - %% are not dependent on the message being matched. - Regs = regs_kill(Ds, Regs0), - opt_ref_used_bl(Is, Regs); - true -> - %% All the sources depend on the message directly or - %% indirectly. - Regs = regs_add_list(Ds, Regs0), - opt_ref_used_bl(Is, Regs) - end; -opt_ref_used_bl([], Regs) -> Regs. - -%%% -%%% Functions for keeping track of a set of registers. -%%% - -%% regs_init() -> RegisterSet -%% Return an empty set of registers. - -regs_init() -> - {0,0}. - -%% regs_init_singleton(Register) -> RegisterSet -%% Return a set that only contains one register. - -regs_init_singleton(Reg) -> - regs_add(Reg, regs_init()). - -%% regs_init_x0() -> RegisterSet -%% Return a set that only contains the {x,0} register. - -regs_init_x0() -> - {1 bsl 0,0}. - -%% regs_empty(Register) -> true|false -%% Test whether the register set is empty. - -regs_empty(R) -> - R =:= {0,0}. - -%% regs_kill_not_live(Live, RegisterSet) -> RegisterSet' -%% Kill all registers indicated not live by Live. - -regs_kill_not_live(Live, {Xregs,Yregs}) -> - {Xregs band ((1 bsl Live)-1),Yregs}. - -%% regs_kill([Register], RegisterSet) -> RegisterSet' -%% Kill all registers mentioned in the list of registers. - -regs_kill([{x,N}|Rs], {Xregs,Yregs}) -> - regs_kill(Rs, {Xregs band (bnot (1 bsl N)),Yregs}); -regs_kill([{y,N}|Rs], {Xregs,Yregs}) -> - regs_kill(Rs, {Xregs,Yregs band (bnot (1 bsl N))}); -regs_kill([{fr,_}|Rs], Regs) -> - regs_kill(Rs, Regs); -regs_kill([], Regs) -> Regs. - -regs_add_list(List, Regs) -> - foldl(fun(R, A) -> regs_add(R, A) end, Regs, List). - -%% regs_add(Register, RegisterSet) -> RegisterSet' -%% Add a new register to the set of registers. - -regs_add({x,N}, {Xregs,Yregs}) -> - {Xregs bor (1 bsl N),Yregs}; -regs_add({y,N}, {Xregs,Yregs}) -> - {Xregs,Yregs bor (1 bsl N)}. - -%% regs_all_members([Register], RegisterSet) -> true|false -%% Test whether all of the registers are part of the register set. - -regs_all_members([R|Rs], Regs) -> - regs_is_member(R, Regs) andalso regs_all_members(Rs, Regs); -regs_all_members([], _) -> true. - -%% regs_is_member(Register, RegisterSet) -> true|false -%% Test whether Register is part of the register set. - -regs_is_member({x,N}, {Regs,_}) -> Regs band (1 bsl N) =/= 0; -regs_is_member({y,N}, {_,Regs}) -> Regs band (1 bsl N) =/= 0; -regs_is_member(_, _) -> false. |