%% %CopyrightBegin% %% %% Copyright Ericsson AB 2014-2017. 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% %% %% Rewrite the instruction stream on tagged tuple tests. %% Tagged tuples means a tuple of any arity with an atom as its %% first element, such as records and error tuples. %% %% From: %% ... %% {test,is_tuple,Fail,[Src]}. %% {test,test_arity,Fail,[Src,Sz]}. %% ... %% {get_tuple_element,Src,0,Dst}. %% ... %% {test,is_eq_exact,Fail,[Dst,Atom]}. %% ... %% To: %% ... %% {test,is_tagged_tuple,Fail,[Src,Sz,Atom]}. %% ... %% -module(beam_record). -export([module/2]). -import(lists, [reverse/1,reverse/2]). -spec module(beam_utils:module_code(), [compile:option()]) -> {'ok',beam_utils:module_code()}. module({Mod,Exp,Attr,Fs0,Lc}, _Opt) -> Fs = [function(F) || F <- Fs0], {ok,{Mod,Exp,Attr,Fs,Lc}}. function({function,Name,Arity,CLabel,Is0}) -> try Is1 = beam_utils:anno_defs(Is0), Idx = beam_utils:index_labels(Is1), Is = rewrite(reverse(Is1), Idx), {function,Name,Arity,CLabel,Is} catch Class:Error:Stack -> io:fwrite("Function: ~w/~w\n", [Name,Arity]), erlang:raise(Class, Error, Stack) end. rewrite(Is, Idx) -> rewrite(Is, Idx, 0, []). rewrite([{test,test_arity,Fail,[Src,N]}=TA, {test,is_tuple,Fail,[Src]}=TT|Is], Idx, Def, Acc0) -> case is_tagged_tuple(Acc0, Def, Fail, Src, Idx) of no -> rewrite(Is, Idx, 0, [TT,TA|Acc0]); {yes,Atom,Acc} -> I = {test,is_tagged_tuple,Fail,[Src,N,Atom]}, rewrite(Is, Idx, Def, [I|Acc]) end; rewrite([{block,[{'%anno',{def,Def}}|Bl]}|Is], Idx, _Def, Acc) -> rewrite(Is, Idx, Def, [{block,Bl}|Acc]); rewrite([{label,L}=I|Is], Idx0, Def, Acc) -> Idx = beam_utils:index_label(L, Acc, Idx0), rewrite(Is, Idx, Def, [I|Acc]); rewrite([I|Is], Idx, Def, Acc) -> rewrite(Is, Idx, Def, [I|Acc]); rewrite([], _, _, Acc) -> Acc. is_tagged_tuple([{block,Bl}, {test,is_eq_exact,Fail,[Dst,{atom,_}=Atom]}|Is], Def, Fail, Src, Idx) -> case is_tagged_tuple_1(Bl, Is, Fail, Src, Dst, Idx, Def, []) of no -> no; {yes,[]} -> {yes,Atom,Is}; {yes,[_|_]=Block} -> {yes,Atom,[{block,Block}|Is]} end; is_tagged_tuple(_, _, _, _, _) -> no. is_tagged_tuple_1([{set,[Dst],[Src],{get_tuple_element,0}}=I|Bl], Is, Fail, Src, Dst, Idx, Def, Acc) -> %% Check usage of Dst to find out whether the get_tuple_element %% is needed. case usage(Dst, Is, Fail, Idx) of killed -> %% Safe to remove the get_tuple_element instruction. {yes,reverse(Acc, Bl)}; used -> %% Actively used. Must keep instruction. {yes,reverse(Acc, [I|Bl])}; not_used -> %% Not actually used (but must be initialized). case is_defined(Dst, Def) of false -> %% Dst must be initialized, but the %% actual value does not matter. Kill = {set,[Dst],[nil],move}, {yes,reverse(Acc, [Kill|Bl])}; true -> %% The register is previously initialized. %% We can remove the instruction. {yes,reverse(Acc, Bl)} end end; is_tagged_tuple_1([I|Bl], Is, Fail, Src, Dst, Idx, Def, Acc) -> is_tagged_tuple_1(Bl, Is, Fail, Src, Dst, Idx, Def, [I|Acc]); is_tagged_tuple_1(_, _, _, _, _, _, _, _) -> no. usage(Dst, Is, Fail, Idx) -> beam_utils:usage(Dst, [{test,is_number,Fail,[nil]}|Is], Idx). is_defined({x,X}, Def) -> (Def bsr X) band 1 =:= 1.