%% -*- erlang-indent-level: 2 -*- %% %% 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. %% %%======================================================================= %% File : hipe_beam_to_icode.erl %% Author : Kostis Sagonas %% Description : Translates symbolic BEAM code to Icode %%======================================================================= %% @doc %% This file translates symbolic BEAM code to Icode which is HiPE's %% intermediate code representation. Either the code of an entire %% module, or the code of a specified function can be translated. %% @end %%======================================================================= -module(hipe_beam_to_icode). -export([module/2]). %%----------------------------------------------------------------------- %% Uncomment the following lines to turn on debugging for this module %% or comment them to it turn off. Debug-level 6 inserts a print in %% each compiled function. %% %%-ifndef(DEBUG). %%-define(DEBUG,6). %% Choose one of two tracing methods %%-define(DEBUG_BIF_CALL_TRACE,true). %%-define(IO_FORMAT_CALL_TRACE,true). %%-endif. -include("../main/hipe.hrl"). -include("hipe_icode.hrl"). -include("hipe_icode_primops.hrl"). -include("../../compiler/src/beam_disasm.hrl"). -define(no_debug_msg(Str,Xs),ok). %%-define(no_debug_msg(Str,Xs),msg(Str,Xs)). -ifdef(DEBUG_BIF_CALL_TRACE). %% Use BIF hipe_bifs_debug_native_called_2 to trace function calls mk_debug_calltrace({_M,_F,A}=MFA, Env, Code) -> MFAVar = mk_var(new), Ignore = mk_var(new), MkMfa = hipe_icode:mk_move(MFAVar,hipe_icode:mk_const(MFA)), Args = [mk_var({x,I-1}) || I <- lists:seq(1,A)], ArgTup = mk_var(new), MkArgTup = hipe_icode:mk_primop([ArgTup], mktuple, Args), Call = hipe_icode:mk_primop([Ignore], debug_native_called, [MFAVar,ArgTup]), {[MkMfa,MkArgTup,Call | Code], Env}. -endif. -ifdef(IO_FORMAT_CALL_TRACE). %% Use io:format to trace function calls mk_debug_calltrace(MFA, Env, Code) -> case MFA of {io,_,_} -> %% We do not want to loop infinitely if we are compiling %% the module io. {Code,Env}; {M,F,A} -> MFAVar = mk_var(new), StringVar = mk_var(new), Ignore = mk_var(new), MkMfa = hipe_icode:mk_move(MFAVar,hipe_icode:mk_const([MFA])), MkString = hipe_icode:mk_move(StringVar, hipe_icode:mk_const( atom_to_list(M) ++ ":" ++ atom_to_list(F) ++"/"++ integer_to_list(A) ++ " Native enter fun ~w\n")), Call = hipe_icode:mk_call([Ignore],io,format,[StringVar,MFAVar],remote), {[MkMfa,MkString,Call | Code], Env} end. -endif. %%----------------------------------------------------------------------- %% Types %%----------------------------------------------------------------------- -type hipe_beam_to_icode_ret() :: [{mfa(),#icode{}}]. %%----------------------------------------------------------------------- %% Internal data structures %%----------------------------------------------------------------------- -record(beam_const, {value :: simple_const()}). % defined in hipe_icode.hrl -record(closure_info, {mfa :: mfa(), arity :: arity(), fv_arity :: arity()}). -record(environment, {mfa :: mfa(), entry :: non_neg_integer()}). %%----------------------------------------------------------------------- %% @doc %% Translates the code of a whole module into Icode. %% Returns a tuple whose first argument is a list of {{M,F,A}, ICode} %% pairs, and its second argument is the list of HiPE compiler options. %% @end %%----------------------------------------------------------------------- -spec module([#function{}], comp_options()) -> hipe_beam_to_icode_ret(). module(BeamFuns, Options) -> BeamCode0 = [beam_disasm:function__code(F) || F <- BeamFuns], {ModCode, ClosureInfo} = preprocess_code(BeamCode0), pp_beam(ModCode, Options), [trans_beam_function_chunk(FunCode, ClosureInfo) || FunCode <- ModCode]. trans_beam_function_chunk(FunBeamCode, ClosureInfo) -> {M,F,A} = MFA = find_mfa(FunBeamCode), Icode = trans_mfa_code(M,F,A, FunBeamCode, ClosureInfo), {MFA,Icode}. %%----------------------------------------------------------------------- %% The main translation function. %%----------------------------------------------------------------------- trans_mfa_code(M,F,A, FunBeamCode, ClosureInfo) -> ?no_debug_msg("disassembling: {~p,~p,~p} ...", [M,F,A]), hipe_gensym:init(icode), %% Extract the function arguments FunArgs = extract_fun_args(A), %% Record the function arguments FunLbl = mk_label(new), Env1 = env__mk_env(M, F, A, hipe_icode:label_name(FunLbl)), Code1 = lists:flatten(trans_fun(FunBeamCode,Env1)), Code2 = fix_fallthroughs(fix_catches(Code1)), MFA = {M,F,A}, %% Debug code ?IF_DEBUG_LEVEL(5, {Code3,_Env3} = mk_debug_calltrace(MFA, Env1, Code2), {Code3,_Env3} = {Code2,Env1}), %% For stack optimization IsClosure = get_closure_info(MFA, ClosureInfo) =/= not_a_closure, Leafness = leafness(Code3, IsClosure), IsLeaf = is_leaf_code(Leafness), Code4 = [FunLbl | case needs_redtest(Leafness) of false -> Code3; true -> [mk_redtest()|Code3] end], Code5 = hipe_icode:mk_icode(MFA, FunArgs, IsClosure, IsLeaf, remove_dead_code(Code4), hipe_gensym:var_range(icode), hipe_gensym:label_range(icode)), Icode = %% If this function is the code for a closure ... case get_closure_info(MFA, ClosureInfo) of not_a_closure -> Code5; CI -> %% ... then patch the code to %% get the free_vars from the closure patch_closure_entry(Code5, CI) end, ?no_debug_msg("ok~n", []), Icode. mk_redtest() -> hipe_icode:mk_primop([], redtest, []). leafness(Is, IsClosure) -> % -> true, selfrec, closure, or false leafness(Is, IsClosure, true). leafness([], _IsClosure, Leafness) -> Leafness; leafness([I|Is], IsClosure, Leafness) -> case I of #icode_comment{} -> %% BEAM self-tailcalls become gotos, but they leave %% a trace behind in comments. Check those to ensure %% that the computed leafness is correct. Needed to %% prevent redtest elimination in those cases. NewLeafness = case hipe_icode:comment_text(I) of 'tail_recursive' -> selfrec; % call_last to selfrec 'self_tail_recursive' -> selfrec; % call_only to selfrec _ -> Leafness end, leafness(Is, IsClosure, NewLeafness); #icode_call{} -> case hipe_icode:call_type(I) of 'primop' -> case hipe_icode:call_fun(I) of call_fun -> false; % Calls closure enter_fun -> false; % Calls closure #apply_N{} -> false; _ -> leafness(Is, IsClosure, Leafness) % Other primop calls are ok end; T when T =:= 'local' orelse T =:= 'remote' -> {M,F,A} = hipe_icode:call_fun(I), case erlang:is_builtin(M, F, A) of true -> leafness(Is, IsClosure, Leafness); false -> false end end; #icode_enter{} -> case hipe_icode:enter_type(I) of 'primop' -> case hipe_icode:enter_fun(I) of enter_fun -> false; #apply_N{} -> false; _ -> %% All primops should be ok except those excluded above, %% except we don't actually tailcall them... io:format("leafness: unexpected enter to primop ~w\n", [I]), true end; T when T =:= 'local' orelse T =:= 'remote' -> {M,F,A} = hipe_icode:enter_fun(I), case erlang:is_builtin(M, F, A) of true -> leafness(Is, IsClosure, Leafness); _ when IsClosure -> leafness(Is, IsClosure, closure); _ -> false end end; _ -> leafness(Is, IsClosure, Leafness) end. %% XXX: this old stuff is passed around but essentially unused is_leaf_code(Leafness) -> case Leafness of true -> true; selfrec -> true; closure -> false; false -> false end. needs_redtest(Leafness) -> case Leafness of true -> false; %% A "leaf" closure may contain tailcalls to non-closures in addition to %% what other leaves may contain. Omitting the redtest is useful to generate %% shorter code for closures generated by (fun F/A), and is safe since %% control flow cannot return to a "leaf" closure again without a reduction %% being consumed. This is true since no function that can call a closure %% will ever have its redtest omitted. closure -> false; selfrec -> true; false -> true end. %%----------------------------------------------------------------------- %% The main translation switch. %%----------------------------------------------------------------------- %%--- label & func_info combo --- trans_fun([{label,_}=F,{func_info,_,_,_}=FI|Instructions], Env) -> %% Handle old code without a line instruction. trans_fun([F,{line,[]},FI|Instructions], Env); trans_fun([{label,B},{label,_}, {func_info,M,F,A},{label,L}|Instructions], Env) -> trans_fun([{label,B},{func_info,M,F,A},{label,L}|Instructions], Env); trans_fun([{label,B}, {line,_}, {func_info,{atom,_M},{atom,_F},_A}, {label,L}|Instructions], Env) -> %% Emit code to handle function_clause errors. The BEAM test instructions %% branch to this label if they fail during function clause selection. %% Obviously, we must goto past this error point on normal entry. Begin = mk_label(B), V = mk_var(new), EntryPt = mk_label(L), Goto = hipe_icode:mk_goto(hipe_icode:label_name(EntryPt)), Mov = hipe_icode:mk_move(V, hipe_icode:mk_const(function_clause)), Fail = hipe_icode:mk_fail([V],error), [Goto, Begin, Mov, Fail, EntryPt | trans_fun(Instructions, Env)]; %%--- label --- trans_fun([{label,L1},{label,L2}|Instructions], Env) -> %% Old BEAM code can have two consecutive labels. Lab1 = mk_label(L1), Lab2 = mk_label(L2), Goto = hipe_icode:mk_goto(map_label(L2)), [Lab1, Goto, Lab2 | trans_fun(Instructions, Env)]; trans_fun([{label,L}|Instructions], Env) -> [mk_label(L) | trans_fun(Instructions, Env)]; %%--- int_code_end --- SHOULD NEVER OCCUR HERE %%--- call --- trans_fun([{call,_N,{_M,_F,A}=MFA}|Instructions], Env) -> Args = extract_fun_args(A), Dst = [mk_var({r,0})], I = trans_call(MFA, Dst, Args, local), [I | trans_fun(Instructions, Env)]; %%--- call_last --- %% Differs from call_only in that it deallocates the environment trans_fun([{call_last,_N,{_M,_F,A}=MFA,_}|Instructions], Env) -> %% IS IT OK TO IGNORE LAST ARG ?? ?no_debug_msg(" translating call_last: ~p ...~n", [Env]), case env__get_mfa(Env) of MFA -> %% Does this case really happen, or is it covered by call_only? Entry = env__get_entry(Env), [hipe_icode:mk_comment('tail_recursive'), % needed by leafness/2 hipe_icode:mk_goto(Entry) | trans_fun(Instructions,Env)]; _ -> Args = extract_fun_args(A), I = trans_enter(MFA, Args, local), [I | trans_fun(Instructions, Env)] end; %%--- call_only --- %% Used when the body contains only one call in which case %% an environment is not needed/created. trans_fun([{call_only,_N,{_M,_F,A}=MFA}|Instructions], Env) -> ?no_debug_msg(" translating call_only: ~p ...~n", [Env]), case env__get_mfa(Env) of MFA -> Entry = env__get_entry(Env), [hipe_icode:mk_comment('self_tail_recursive'), % needed by leafness/2 hipe_icode:mk_goto(Entry) | trans_fun(Instructions,Env)]; _ -> Args = extract_fun_args(A), I = trans_enter(MFA,Args,local), [I | trans_fun(Instructions,Env)] end; %%--- call_ext --- trans_fun([{call_ext,_N,{extfunc,M,F,A}}|Instructions], Env) -> Args = extract_fun_args(A), Dst = [mk_var({r,0})], I = trans_call({M,F,A},Dst,Args,remote), [hipe_icode:mk_comment('call_ext'),I | trans_fun(Instructions,Env)]; %%--- call_ext_last --- trans_fun([{call_ext_last,_N,{extfunc,M,F,A},_}|Instructions], Env) -> %% IS IT OK TO IGNORE LAST ARG ?? Args = extract_fun_args(A), %% Dst = [mk_var({r,0})], I = trans_enter({M,F,A},Args,remote), [hipe_icode:mk_comment('call_ext_last'), I | trans_fun(Instructions,Env)]; %%--- bif0 --- trans_fun([{bif,BifName,nofail,[],Reg}|Instructions], Env) -> BifInst = trans_bif0(BifName,Reg), [BifInst|trans_fun(Instructions,Env)]; %%--- bif1 --- trans_fun([{bif,BifName,{f,Lbl},[_] = Args,Reg}|Instructions], Env) -> {BifInsts,Env1} = trans_bif(1,BifName,Lbl,Args,Reg,Env), BifInsts ++ trans_fun(Instructions,Env1); %%--- bif2 --- trans_fun([{bif,BifName,{f,Lbl},[_,_] = Args,Reg}|Instructions], Env) -> {BifInsts,Env1} = trans_bif(2,BifName,Lbl,Args,Reg,Env), BifInsts ++ trans_fun(Instructions,Env1); %%--- bif3 --- trans_fun([{bif,BifName,{f,Lbl},[_,_,_] = Args,Reg}|Instructions], Env) -> {BifInsts,Env1} = trans_bif(3,BifName,Lbl,Args,Reg,Env), BifInsts ++ trans_fun(Instructions,Env1); %%--- allocate trans_fun([{allocate,StackSlots,_}|Instructions], Env) -> trans_allocate(StackSlots) ++ trans_fun(Instructions,Env); %%--- allocate_heap trans_fun([{allocate_heap,StackSlots,_,_}|Instructions], Env) -> trans_allocate(StackSlots) ++ trans_fun(Instructions,Env); %%--- allocate_zero trans_fun([{allocate_zero,StackSlots,_}|Instructions], Env) -> trans_allocate(StackSlots) ++ trans_fun(Instructions,Env); %%--- allocate_heap_zero trans_fun([{allocate_heap_zero,StackSlots,_,_}|Instructions], Env) -> trans_allocate(StackSlots) ++ trans_fun(Instructions,Env); %%--- test_heap --- IGNORED ON PURPOSE trans_fun([{test_heap,_,_}|Instructions], Env) -> trans_fun(Instructions,Env); %%--- init --- IGNORED - CORRECT?? trans_fun([{init,_}|Instructions], Env) -> trans_fun(Instructions,Env); %%--- deallocate --- IGNORED ON PURPOSE trans_fun([{deallocate,_}|Instructions], Env) -> trans_fun(Instructions,Env); %%--- return --- trans_fun([return|Instructions], Env) -> [hipe_icode:mk_return([mk_var({r,0})]) | trans_fun(Instructions,Env)]; %%--- send --- trans_fun([send|Instructions], Env) -> I = hipe_icode:mk_call([mk_var({r,0})], erlang, send, [mk_var({x,0}),mk_var({x,1})], remote), [I | trans_fun(Instructions,Env)]; %%--- remove_message --- trans_fun([remove_message|Instructions], Env) -> [hipe_icode:mk_primop([],select_msg,[]) | trans_fun(Instructions,Env)]; %%--- timeout --- trans_fun([timeout|Instructions], Env) -> [hipe_icode:mk_primop([],clear_timeout,[]) | trans_fun(Instructions,Env)]; %%--- loop_rec --- trans_fun([{loop_rec,{_,Lbl},Reg}|Instructions], Env) -> {Movs,[Temp],Env1} = get_constants_in_temps([Reg],Env), GotitLbl = mk_label(new), ChkGetMsg = hipe_icode:mk_primop([Temp],check_get_msg,[], hipe_icode:label_name(GotitLbl), map_label(Lbl)), Movs ++ [ChkGetMsg, GotitLbl | trans_fun(Instructions,Env1)]; %%--- loop_rec_end --- trans_fun([{loop_rec_end,{_,Lbl}}|Instructions], Env) -> Loop = hipe_icode:mk_goto(map_label(Lbl)), [hipe_icode:mk_primop([],next_msg,[]), Loop | trans_fun(Instructions,Env)]; %%--- wait --- trans_fun([{wait,{_,Lbl}}|Instructions], Env) -> Susp = hipe_icode:mk_primop([],suspend_msg,[]), Loop = hipe_icode:mk_goto(map_label(Lbl)), [Susp, Loop | trans_fun(Instructions,Env)]; %%--- wait_timeout --- trans_fun([{wait_timeout,{_,Lbl},Reg}|Instructions], Env) -> {Movs,[_]=Temps,Env1} = get_constants_in_temps([Reg],Env), SetTmout = hipe_icode:mk_primop([],set_timeout,Temps), DoneLbl = mk_label(new), SuspTmout = hipe_icode:mk_if(suspend_msg_timeout,[], map_label(Lbl),hipe_icode:label_name(DoneLbl)), Movs ++ [SetTmout, SuspTmout, DoneLbl | trans_fun(Instructions,Env1)]; %%--- recv_mark/1 & recv_set/1 --- trans_fun([{recv_mark,{f,_}}|Instructions], Env) -> Mark = hipe_icode:mk_primop([],recv_mark,[]), [Mark | trans_fun(Instructions,Env)]; trans_fun([{recv_set,{f,_}}|Instructions], Env) -> Set = hipe_icode:mk_primop([],recv_set,[]), [Set | trans_fun(Instructions,Env)]; %%-------------------------------------------------------------------- %%--- Translation of arithmetics {bif,ArithOp, ...} --- %%-------------------------------------------------------------------- trans_fun([{arithbif,ArithOp,{f,L},SrcRs,DstR}|Instructions], Env) -> {ICode,NewEnv} = trans_arith(ArithOp,SrcRs,DstR,L,Env), ICode ++ trans_fun(Instructions,NewEnv); %%-------------------------------------------------------------------- %%--- Translation of arithmetic tests {test,is_ARITHTEST, ...} --- %%-------------------------------------------------------------------- %%--- is_lt --- trans_fun([{test,is_lt,{f,Lbl},[Arg1,Arg2]}|Instructions], Env) -> {ICode,Env1} = trans_test_guard('<',Lbl,Arg1,Arg2,Env), ICode ++ trans_fun(Instructions,Env1); %%--- is_ge --- trans_fun([{test,is_ge,{f,Lbl},[Arg1,Arg2]}|Instructions], Env) -> {ICode,Env1} = trans_test_guard('>=',Lbl,Arg1,Arg2,Env), ICode ++ trans_fun(Instructions,Env1); %%--- is_eq --- trans_fun([{test,is_eq,{f,Lbl},[Arg1,Arg2]}|Instructions], Env) -> {ICode,Env1} = trans_is_eq(Lbl,Arg1,Arg2,Env), ICode ++ trans_fun(Instructions,Env1); %%--- is_ne --- trans_fun([{test,is_ne,{f,Lbl},[Arg1,Arg2]}|Instructions], Env) -> {ICode,Env1} = trans_is_ne(Lbl,Arg1,Arg2,Env), ICode ++ trans_fun(Instructions,Env1); %%--- is_eq_exact --- trans_fun([{test,is_eq_exact,{f,Lbl},[Arg1,Arg2]}|Instructions], Env) -> {ICode,Env1} = trans_is_eq_exact(Lbl,Arg1,Arg2,Env), ICode ++ trans_fun(Instructions,Env1); %%--- is_ne_exact --- trans_fun([{test,is_ne_exact,{f,Lbl},[Arg1,Arg2]}|Instructions], Env) -> {ICode,Env1} = trans_is_ne_exact(Lbl,Arg1,Arg2,Env), ICode ++ trans_fun(Instructions,Env1); %%-------------------------------------------------------------------- %%--- Translation of type tests {test,is_TYPE, ...} --- %%-------------------------------------------------------------------- %%--- is_integer --- trans_fun([{test,is_integer,{f,Lbl},[Arg]}|Instructions], Env) -> {Code,Env1} = trans_type_test(integer,Lbl,Arg,Env), [Code | trans_fun(Instructions,Env1)]; %%--- is_float --- trans_fun([{test,is_float,{f,Lbl},[Arg]}|Instructions], Env) -> {Code,Env1} = trans_type_test(float,Lbl,Arg,Env), [Code | trans_fun(Instructions,Env1)]; %%--- is_number --- trans_fun([{test,is_number,{f,Lbl},[Arg]}|Instructions], Env) -> {Code,Env1} = trans_type_test(number,Lbl,Arg,Env), [Code | trans_fun(Instructions,Env1)]; %%--- is_atom --- trans_fun([{test,is_atom,{f,Lbl},[Arg]}|Instructions], Env) -> {Code,Env1} = trans_type_test(atom,Lbl,Arg,Env), [Code | trans_fun(Instructions,Env1)]; %%--- is_pid --- trans_fun([{test,is_pid,{f,Lbl},[Arg]}|Instructions], Env) -> {Code,Env1} = trans_type_test(pid,Lbl,Arg,Env), [Code | trans_fun(Instructions,Env1)]; %%--- is_ref --- trans_fun([{test,is_reference,{f,Lbl},[Arg]}|Instructions], Env) -> {Code,Env1} = trans_type_test(reference,Lbl,Arg,Env), [Code | trans_fun(Instructions,Env1)]; %%--- is_port --- trans_fun([{test,is_port,{f,Lbl},[Arg]}|Instructions], Env) -> {Code,Env1} = trans_type_test(port,Lbl,Arg,Env), [Code | trans_fun(Instructions,Env1)]; %%--- is_nil --- trans_fun([{test,is_nil,{f,Lbl},[Arg]}|Instructions], Env) -> {Code,Env1} = trans_type_test(nil,Lbl,Arg,Env), [Code | trans_fun(Instructions,Env1)]; %%--- is_binary --- trans_fun([{test,is_binary,{f,Lbl},[Arg]}|Instructions], Env) -> {Code,Env1} = trans_type_test(binary,Lbl,Arg,Env), [Code | trans_fun(Instructions,Env1)]; %%--- is_list --- trans_fun([{test,is_list,{f,Lbl},[Arg]}|Instructions], Env) -> {Code,Env1} = trans_type_test(list,Lbl,Arg,Env), [Code | trans_fun(Instructions,Env1)]; %%--- is_nonempty_list --- trans_fun([{test,is_nonempty_list,{f,Lbl},[Arg]}|Instructions], Env) -> {Code,Env1} = trans_type_test(cons,Lbl,Arg,Env), [Code | trans_fun(Instructions,Env1)]; %%--- is_tuple --- trans_fun([{test,is_tuple,{f,_Lbl}=FLbl,[Xreg]}, {test,test_arity,FLbl,[Xreg,_]=Args}|Instructions], Env) -> trans_fun([{test,test_arity,FLbl,Args}|Instructions],Env); trans_fun([{test,is_tuple,{_,Lbl},[Arg]}|Instructions], Env) -> {Code,Env1} = trans_type_test(tuple,Lbl,Arg,Env), [Code | trans_fun(Instructions,Env1)]; %%--- test_arity --- trans_fun([{test,test_arity,{f,Lbl},[Reg,N]}|Instructions], Env) -> True = mk_label(new), I = hipe_icode:mk_type([trans_arg(Reg)],{tuple,N}, hipe_icode:label_name(True),map_label(Lbl)), [I,True | trans_fun(Instructions,Env)]; %%--- test_is_tagged_tuple --- trans_fun([{test,is_tagged_tuple,{f,Lbl},[Reg,N,Atom]}|Instructions], Env) -> TrueArity = mk_label(new), IArity = hipe_icode:mk_type([trans_arg(Reg)],{tuple,N}, hipe_icode:label_name(TrueArity),map_label(Lbl)), Var = hipe_icode:mk_new_var(), IGet = hipe_icode:mk_primop([Var], #unsafe_element{index=1}, [trans_arg(Reg)]), TrueAtom = mk_label(new), IEQ = hipe_icode:mk_type([Var], Atom, hipe_icode:label_name(TrueAtom), map_label(Lbl)), [IArity,TrueArity,IGet,IEQ,TrueAtom | trans_fun(Instructions,Env)]; %%--- is_map --- trans_fun([{test,is_map,{f,Lbl},[Arg]}|Instructions], Env) -> {Code,Env1} = trans_type_test(map,Lbl,Arg,Env), [Code | trans_fun(Instructions,Env1)]; %%-------------------------------------------------------------------- %%--- select_val --- trans_fun([{select_val,Reg,{f,Lbl},{list,Cases}}|Instructions], Env) -> {SwVar,CasePairs} = trans_select_stuff(Reg,Cases), Len = length(CasePairs), I = hipe_icode:mk_switch_val(SwVar,map_label(Lbl),Len,CasePairs), ?no_debug_msg("switch_val instr is ~p~n",[I]), [I | trans_fun(Instructions,Env)]; %%--- select_tuple_arity --- trans_fun([{select_tuple_arity,Reg,{f,Lbl},{list,Cases}}|Instructions],Env) -> {SwVar,CasePairs} = trans_select_stuff(Reg,Cases), Len = length(CasePairs), I = hipe_icode:mk_switch_tuple_arity(SwVar,map_label(Lbl),Len,CasePairs), ?no_debug_msg("switch_tuple_arity instr is ~p~n",[I]), [I | trans_fun(Instructions,Env)]; %%--- jump --- trans_fun([{jump,{_,L}}|Instructions], Env) -> Label = mk_label(L), I = hipe_icode:mk_goto(hipe_icode:label_name(Label)), [I | trans_fun(Instructions,Env)]; %%--- move --- trans_fun([{move,Src,Dst}|Instructions], Env) -> Dst1 = mk_var(Dst), Src1 = trans_arg(Src), [hipe_icode:mk_move(Dst1,Src1) | trans_fun(Instructions,Env)]; %%--- catch --- ITS PROCESSING IS POSTPONED trans_fun([{'catch',N,{_,EndLabel}}|Instructions], Env) -> NewContLbl = mk_label(new), [{'catch',N,EndLabel},NewContLbl | trans_fun(Instructions,Env)]; %%--- catch_end --- ITS PROCESSING IS POSTPONED trans_fun([{catch_end,_N}=I|Instructions], Env) -> [I | trans_fun(Instructions,Env)]; %%--- try --- ITS PROCESSING IS POSTPONED trans_fun([{'try',N,{_,EndLabel}}|Instructions], Env) -> NewContLbl = mk_label(new), [{'try',N,EndLabel},NewContLbl | trans_fun(Instructions,Env)]; %%--- try_end --- trans_fun([{try_end,_N}|Instructions], Env) -> [hipe_icode:mk_end_try() | trans_fun(Instructions,Env)]; %%--- try_case --- ITS PROCESSING IS POSTPONED trans_fun([{try_case,_N}=I|Instructions], Env) -> [I | trans_fun(Instructions,Env)]; %%--- try_case_end --- trans_fun([{try_case_end,Arg}|Instructions], Env) -> BadArg = trans_arg(Arg), ErrVar = mk_var(new), Vs = [mk_var(new)], Atom = hipe_icode:mk_move(ErrVar,hipe_icode:mk_const(try_clause)), Tuple = hipe_icode:mk_primop(Vs,mktuple,[ErrVar,BadArg]), Fail = hipe_icode:mk_fail(Vs,error), [Atom,Tuple,Fail | trans_fun(Instructions,Env)]; %%--- raise --- trans_fun([{raise,{f,0},[Reg1,Reg2],{x,0}}|Instructions], Env) -> V1 = trans_arg(Reg1), V2 = trans_arg(Reg2), Fail = hipe_icode:mk_fail([V1,V2],rethrow), [Fail | trans_fun(Instructions,Env)]; %%--- get_list --- trans_fun([{get_list,List,Head,Tail}|Instructions], Env) -> TransList = [trans_arg(List)], I1 = hipe_icode:mk_primop([mk_var(Head)],unsafe_hd,TransList), I2 = hipe_icode:mk_primop([mk_var(Tail)],unsafe_tl,TransList), %% Handle the cases where the dest overwrites the src!! if Head =/= List -> [I1, I2 | trans_fun(Instructions,Env)]; Tail =/= List -> [I2, I1 | trans_fun(Instructions,Env)]; true -> %% XXX: We should take care of this case!!!!! ?error_msg("hd and tl regs identical in get_list~n",[]), erlang:error(not_handled) end; %%--- get_hd --- trans_fun([{get_hd,List,Head}|Instructions], Env) -> TransList = [trans_arg(List)], I = hipe_icode:mk_primop([mk_var(Head)],unsafe_hd,TransList), [I | trans_fun(Instructions,Env)]; %%--- get_tl --- trans_fun([{get_tl,List,Tail}|Instructions], Env) -> TransList = [trans_arg(List)], I = hipe_icode:mk_primop([mk_var(Tail)],unsafe_tl,TransList), [I | trans_fun(Instructions,Env)]; %%--- get_tuple_element --- trans_fun([{get_tuple_element,Xreg,Index,Dst}|Instructions], Env) -> I = hipe_icode:mk_primop([mk_var(Dst)], #unsafe_element{index=Index+1}, [trans_arg(Xreg)]), [I | trans_fun(Instructions,Env)]; %%--- set_tuple_element --- trans_fun([{set_tuple_element,Elem,Tuple,Index}|Instructions], Env) -> Elem1 = trans_arg(Elem), I = hipe_icode:mk_primop([mk_var(Tuple)], #unsafe_update_element{index=Index+1}, [mk_var(Tuple),Elem1]), [I | trans_fun(Instructions,Env)]; %%--- put_string --- trans_fun([{put_string,_Len,String,Dst}|Instructions], Env) -> Mov = hipe_icode:mk_move(mk_var(Dst),trans_const(String)), [Mov | trans_fun(Instructions,Env)]; %%--- put_list --- trans_fun([{put_list,Car,Cdr,Dest}|Instructions], Env) -> {M1,V1,Env2} = mk_move_and_var(Car,Env), {M2,V2,Env3} = mk_move_and_var(Cdr,Env2), D = mk_var(Dest), M1 ++ M2 ++ [hipe_icode:mk_primop([D],cons,[V1,V2]) | trans_fun(Instructions,Env3)]; %%--- put_tuple --- trans_fun([{put_tuple,_Size,Reg}|Instructions], Env) -> {Moves,Instructions2,Vars,Env2} = trans_puts(Instructions,Env), Dest = [mk_var(Reg)], Src = lists:reverse(Vars), Primop = hipe_icode:mk_primop(Dest,mktuple,Src), Moves ++ [Primop | trans_fun(Instructions2,Env2)]; %%--- put --- SHOULD NOT REALLY EXIST HERE; put INSTRUCTIONS ARE HANDLED ABOVE. %%--- badmatch --- trans_fun([{badmatch,Arg}|Instructions], Env) -> BadVar = trans_arg(Arg), ErrVar = mk_var(new), Vs = [mk_var(new)], Atom = hipe_icode:mk_move(ErrVar,hipe_icode:mk_const(badmatch)), Tuple = hipe_icode:mk_primop(Vs,mktuple,[ErrVar,BadVar]), Fail = hipe_icode:mk_fail(Vs,error), [Atom,Tuple,Fail | trans_fun(Instructions,Env)]; %%--- if_end --- trans_fun([if_end|Instructions], Env) -> V = mk_var(new), Mov = hipe_icode:mk_move(V,hipe_icode:mk_const(if_clause)), Fail = hipe_icode:mk_fail([V],error), [Mov,Fail | trans_fun(Instructions, Env)]; %%--- case_end --- trans_fun([{case_end,Arg}|Instructions], Env) -> BadArg = trans_arg(Arg), ErrVar = mk_var(new), Vs = [mk_var(new)], Atom = hipe_icode:mk_move(ErrVar,hipe_icode:mk_const(case_clause)), Tuple = hipe_icode:mk_primop(Vs,mktuple,[ErrVar,BadArg]), Fail = hipe_icode:mk_fail(Vs,error), [Atom,Tuple,Fail | trans_fun(Instructions,Env)]; %%--- enter_fun --- trans_fun([{call_fun,N},{deallocate,_},return|Instructions], Env) -> Args = extract_fun_args(N+1), %% +1 is for the fun itself [hipe_icode:mk_comment('enter_fun'), hipe_icode:mk_enter_primop(enter_fun,Args) | trans_fun(Instructions,Env)]; %%--- call_fun --- trans_fun([{call_fun,N}|Instructions], Env) -> Args = extract_fun_args(N+1), %% +1 is for the fun itself Dst = [mk_var({r,0})], [hipe_icode:mk_comment('call_fun'), hipe_icode:mk_primop(Dst,call_fun,Args) | trans_fun(Instructions,Env)]; %%--- patched_make_fun --- make_fun/make_fun2 after fixes trans_fun([{patched_make_fun,MFA,Magic,FreeVarNum,Index}|Instructions], Env) -> Args = extract_fun_args(FreeVarNum), Dst = [mk_var({r,0})], Fun = hipe_icode:mk_primop(Dst, #mkfun{mfa=MFA,magic_num=Magic,index=Index}, Args), ?no_debug_msg("mkfun translates to: ~p~n",[Fun]), [Fun | trans_fun(Instructions,Env)]; %%--- is_function --- trans_fun([{test,is_function,{f,Lbl},[Arg]}|Instructions], Env) -> {Code,Env1} = trans_type_test(function,Lbl,Arg,Env), [Code | trans_fun(Instructions,Env1)]; %%--- call_ext_only --- trans_fun([{call_ext_only,_N,{extfunc,M,F,A}}|Instructions], Env) -> Args = extract_fun_args(A), I = trans_enter({M,F,A}, Args, remote), [hipe_icode:mk_comment('call_ext_only'), I | trans_fun(Instructions,Env)]; %%-------------------------------------------------------------------- %%--- Translation of binary instructions --- %%-------------------------------------------------------------------- %% This code uses a somewhat unorthodox translation: %% Since we do not want non-erlang values as arguments to Icode %% instructions some compile time constants are coded into the %% name of the function (or rather the primop). %% TODO: Make sure all cases of argument types are covered. %%-------------------------------------------------------------------- trans_fun([{test,bs_start_match2,{f,Lbl},[X,_Live,Max,Ms]}|Instructions], Env) -> Bin = trans_arg(X), MsVar = mk_var(Ms), trans_op_call({hipe_bs_primop, {bs_start_match, Max}}, Lbl, [Bin], [MsVar], Env, Instructions); trans_fun([{test,bs_get_float2,{f,Lbl},[Ms,_Live,Size,Unit,{field_flags,Flags0},X]}| Instructions], Env) -> Dst = mk_var(X), MsVar = mk_var(Ms), Flags = resolve_native_endianess(Flags0), {Name, Args} = case Size of {integer, NoBits} when is_integer(NoBits), NoBits >= 0 -> {{bs_get_float,NoBits*Unit,Flags}, [MsVar]}; {integer, NoBits} when is_integer(NoBits), NoBits < 0 -> ?EXIT({bad_bs_size_constant,Size}); BitReg -> Bits = mk_var(BitReg), {{bs_get_float,Unit,Flags}, [MsVar,Bits]} end, trans_op_call({hipe_bs_primop,Name}, Lbl, Args, [Dst,MsVar], Env, Instructions); trans_fun([{test,bs_get_integer2,{f,Lbl},[Ms,_Live,Size,Unit,{field_flags,Flags0},X]}| Instructions], Env) -> Dst = mk_var(X), MsVar = mk_var(Ms), Flags = resolve_native_endianess(Flags0), {Name, Args} = case Size of {integer,NoBits} when is_integer(NoBits), NoBits >= 0 -> {{bs_get_integer,NoBits*Unit,Flags}, [MsVar]}; {integer,NoBits} when is_integer(NoBits), NoBits < 0 -> ?EXIT({bad_bs_size_constant,Size}); BitReg -> Bits = mk_var(BitReg), {{bs_get_integer,Unit,Flags}, [MsVar,Bits]} end, trans_op_call({hipe_bs_primop,Name}, Lbl, Args, [Dst,MsVar], Env, Instructions); trans_fun([{test,bs_get_binary2,{f,Lbl},[Ms,_Live,Size,Unit,{field_flags,Flags},X]}| Instructions], Env) -> MsVar = mk_var(Ms), {Name, Args, Dsts} = case Size of {atom, all} -> %% put all bits if Ms =:= X -> {{bs_get_binary_all,Unit,Flags},[MsVar],[mk_var(X)]}; true -> {{bs_get_binary_all_2,Unit,Flags},[MsVar],[mk_var(X),MsVar]} end; {integer, NoBits} when is_integer(NoBits), NoBits >= 0 -> {{bs_get_binary,NoBits*Unit,Flags}, [MsVar], [mk_var(X),MsVar]};%% Create a N*Unit bits subbinary {integer, NoBits} when is_integer(NoBits), NoBits < 0 -> ?EXIT({bad_bs_size_constant,Size}); BitReg -> % Use a number of bits only known at runtime. Bits = mk_var(BitReg), {{bs_get_binary,Unit,Flags}, [MsVar,Bits], [mk_var(X),MsVar]} end, trans_op_call({hipe_bs_primop,Name}, Lbl, Args, Dsts, Env, Instructions); trans_fun([{test,bs_skip_bits2,{f,Lbl},[Ms,Size,NumBits,{field_flags,Flags}]}| Instructions], Env) -> %% the current match buffer MsVar = mk_var(Ms), {Name, Args} = case Size of {atom, all} -> %% Skip all bits {{bs_skip_bits_all,NumBits,Flags},[MsVar]}; {integer, BitSize} when is_integer(BitSize), BitSize >= 0-> %% Skip N bits {{bs_skip_bits,BitSize*NumBits}, [MsVar]}; {integer, BitSize} when is_integer(BitSize), BitSize < 0 -> ?EXIT({bad_bs_size_constant,Size}); X -> % Skip a number of bits only known at runtime. Src = mk_var(X), {{bs_skip_bits,NumBits},[MsVar,Src]} end, trans_op_call({hipe_bs_primop,Name}, Lbl, Args, [MsVar], Env, Instructions); trans_fun([{test,bs_test_unit,{f,Lbl},[Ms,Unit]}| Instructions], Env) -> %% the current match buffer MsVar = mk_var(Ms), trans_op_call({hipe_bs_primop,{bs_test_unit,Unit}}, Lbl, [MsVar], [], Env, Instructions); trans_fun([{test,bs_match_string,{f,Lbl},[Ms,BitSize,Bin]}| Instructions], Env) -> %% the current match buffer MsVar = mk_var(Ms), Primop = {hipe_bs_primop, {bs_match_string, Bin, BitSize}}, trans_op_call(Primop, Lbl, [MsVar], [MsVar], Env, Instructions); trans_fun([{bs_context_to_binary,Var}|Instructions], Env) -> %% the current match buffer IVars = [trans_arg(Var)], [hipe_icode:mk_primop(IVars,{hipe_bs_primop,bs_context_to_binary},IVars)| trans_fun(Instructions, Env)]; trans_fun([{bs_append,{f,Lbl},Size,W,R,U,Binary,{field_flags,F},Dst}| Instructions], Env) -> %% the current match buffer SizeArg = trans_arg(Size), BinArg = trans_arg(Binary), IcodeDst = mk_var(Dst), Offset = mk_var(reg_gcsafe), Base = mk_var(reg), trans_bin_call({hipe_bs_primop,{bs_append,W,R,U,F}},Lbl,[SizeArg,BinArg], [IcodeDst,Base,Offset], Base, Offset, Env, Instructions); trans_fun([{bs_private_append,{f,Lbl},Size,U,Binary,{field_flags,F},Dst}| Instructions], Env) -> %% the current match buffer SizeArg = trans_arg(Size), BinArg = trans_arg(Binary), IcodeDst = mk_var(Dst), Offset = mk_var(reg_gcsafe), Base = mk_var(reg), trans_bin_call({hipe_bs_primop,{bs_private_append,U,F}}, Lbl,[SizeArg,BinArg], [IcodeDst,Base,Offset], Base, Offset, Env, Instructions); trans_fun([bs_init_writable|Instructions], Env) -> Vars = [mk_var({x,0})], %{x,0} is implict arg and dst [hipe_icode:mk_primop(Vars,{hipe_bs_primop,bs_init_writable},Vars), trans_fun(Instructions, Env)]; trans_fun([{bs_save2,Ms,IndexName}|Instructions], Env) -> Index = case IndexName of {atom, start} -> 0; _ -> IndexName+1 end, MsVars = [mk_var(Ms)], [hipe_icode:mk_primop(MsVars,{hipe_bs_primop,{bs_save,Index}},MsVars) | trans_fun(Instructions, Env)]; trans_fun([{bs_restore2,Ms,IndexName}|Instructions], Env) -> Index = case IndexName of {atom, start} -> 0; _ -> IndexName+1 end, MsVars = [mk_var(Ms)], [hipe_icode:mk_primop(MsVars,{hipe_bs_primop,{bs_restore,Index}},MsVars) | trans_fun(Instructions, Env)]; trans_fun([{test,bs_test_tail2,{f,Lbl},[Ms,Numbits]}| Instructions], Env) -> MsVar = mk_var(Ms), trans_op_call({hipe_bs_primop,{bs_test_tail,Numbits}}, Lbl, [MsVar], [], Env, Instructions); %%-------------------------------------------------------------------- %% bit syntax instructions added in February 2004 (R10B). %%-------------------------------------------------------------------- trans_fun([{bs_init2,{f,Lbl},Size,_Words,_LiveRegs,{field_flags,Flags0},X}| Instructions], Env) -> Dst = mk_var(X), Flags = resolve_native_endianess(Flags0), Offset = mk_var(reg_gcsafe), Base = mk_var(reg), {Name, Args} = case Size of NoBytes when is_integer(NoBytes) -> {{bs_init, Size, Flags}, []}; BitReg -> Bits = mk_var(BitReg), {{bs_init, Flags}, [Bits]} end, trans_bin_call({hipe_bs_primop,Name}, Lbl, Args, [Dst, Base, Offset], Base, Offset, Env, Instructions); trans_fun([{bs_init_bits,{f,Lbl},Size,_Words,_LiveRegs,{field_flags,Flags0},X}| Instructions], Env) -> Dst = mk_var(X), Flags = resolve_native_endianess(Flags0), Offset = mk_var(reg_gcsafe), Base = mk_var(reg), {Name, Args} = case Size of NoBits when is_integer(NoBits) -> {{bs_init_bits, NoBits, Flags}, []}; BitReg -> Bits = mk_var(BitReg), {{bs_init_bits, Flags}, [Bits]} end, trans_bin_call({hipe_bs_primop,Name}, Lbl, Args, [Dst, Base, Offset], Base, Offset, Env, Instructions); trans_fun([{bs_add, {f,Lbl}, [Old,New,Unit], Res}|Instructions], Env) -> Dst = mk_var(Res), Temp = mk_var(new), {FailLblName, FailCode} = if Lbl =:= 0 -> FailLbl = mk_label(new), {hipe_icode:label_name(FailLbl), [FailLbl, hipe_icode:mk_fail([hipe_icode:mk_const(badarg)], error)]}; true -> {map_label(Lbl), []} end, MultIs = case {New,Unit} of {{integer, NewInt}, _} -> [hipe_icode:mk_move(Temp, hipe_icode:mk_const(NewInt*Unit))]; {_, 1} -> NewVar = mk_var(New), [hipe_icode:mk_move(Temp, NewVar)]; _ -> NewVar = mk_var(New), Succ = mk_label(new), [hipe_icode:mk_primop([Temp], '*', [NewVar, hipe_icode:mk_const(Unit)], hipe_icode:label_name(Succ), FailLblName), Succ] end, Succ2 = mk_label(new), IsPos = [hipe_icode:mk_if('>=', [Temp, hipe_icode:mk_const(0)], hipe_icode:label_name(Succ2), FailLblName)] ++ FailCode ++ [Succ2], AddRhs = case Old of {integer,OldInt} -> hipe_icode:mk_const(OldInt); _ -> mk_var(Old) end, Succ3 = mk_label(new), AddI = hipe_icode:mk_primop([Dst], '+', [Temp, AddRhs], hipe_icode:label_name(Succ3), FailLblName), MultIs ++ IsPos ++ [AddI,Succ3|trans_fun(Instructions, Env)]; %%-------------------------------------------------------------------- %% Bit syntax instructions added in R12B-5 (Fall 2008) %%-------------------------------------------------------------------- trans_fun([{bs_utf8_size,{f,Lbl},A2,A3}|Instructions], Env) -> Bin = trans_arg(A2), Dst = mk_var(A3), trans_op_call({hipe_bs_primop, bs_utf8_size}, Lbl, [Bin], [Dst], Env, Instructions); trans_fun([{test,bs_get_utf8,{f,Lbl},[Ms,_Live,{field_flags,_Flags},X]} | Instructions], Env) -> trans_bs_get_or_skip_utf8(Lbl, Ms, X, Instructions, Env); trans_fun([{test,bs_skip_utf8,{f,Lbl},[Ms,_Live,{field_flags,_Flags}]} | Instructions], Env) -> trans_bs_get_or_skip_utf8(Lbl, Ms, 'new', Instructions, Env); trans_fun([{bs_utf16_size,{f,Lbl},A2,A3}|Instructions], Env) -> Bin = trans_arg(A2), Dst = mk_var(A3), trans_op_call({hipe_bs_primop, bs_utf16_size}, Lbl, [Bin], [Dst], Env, Instructions); trans_fun([{test,bs_get_utf16,{f,Lbl},[Ms,_Live,{field_flags,Flags0},X]} | Instructions], Env) -> trans_bs_get_or_skip_utf16(Lbl, Ms, Flags0, X, Instructions, Env); trans_fun([{test,bs_skip_utf16,{f,Lbl},[Ms,_Live,{field_flags,Flags0}]} | Instructions], Env) -> trans_bs_get_or_skip_utf16(Lbl, Ms, Flags0, 'new', Instructions, Env); trans_fun([{test,bs_get_utf32,{f,Lbl},[Ms,_Live,{field_flags,Flags0},X]} | Instructions], Env) -> trans_bs_get_or_skip_utf32(Lbl, Ms, Flags0, X, Instructions, Env); trans_fun([{test,bs_skip_utf32,{f,Lbl},[Ms,_Live,{field_flags,Flags0}]} | Instructions], Env) -> trans_bs_get_or_skip_utf32(Lbl, Ms, Flags0, 'new', Instructions, Env); %%-------------------------------------------------------------------- %%--- Translation of floating point instructions --- %%-------------------------------------------------------------------- %%--- fclearerror --- trans_fun([fclearerror|Instructions], Env) -> case get(hipe_inline_fp) of true -> [hipe_icode:mk_primop([], fclearerror, []) | trans_fun(Instructions,Env)]; _ -> trans_fun(Instructions,Env) end; %%--- fcheckerror --- trans_fun([{fcheckerror,{_,Fail}}|Instructions], Env) -> case get(hipe_inline_fp) of true -> ContLbl = mk_label(new), case Fail of 0 -> [hipe_icode:mk_primop([], fcheckerror, [], hipe_icode:label_name(ContLbl), []), ContLbl | trans_fun(Instructions,Env)]; _ -> %% Can this happen? {Guard,Env1} = make_guard([], fcheckerror, [], hipe_icode:label_name(ContLbl), map_label(Fail), Env), [Guard, ContLbl | trans_fun(Instructions,Env1)] end; _ -> trans_fun(Instructions, Env) end; %%--- fmove --- trans_fun([{fmove,Src,Dst}|Instructions], Env) -> case get(hipe_inline_fp) of true -> Dst1 = mk_var(Dst), Src1 = trans_arg(Src), case{hipe_icode:is_fvar(Dst1), hipe_icode:is_fvar(Src1)} of {true, true} -> %% fvar := fvar [hipe_icode:mk_move(Dst1,Src1) | trans_fun(Instructions,Env)]; {false, true} -> %% var := fvar [hipe_icode:mk_primop([Dst1], unsafe_tag_float, [Src1]) | trans_fun(Instructions,Env)]; {true, false} -> %% fvar := var or fvar := constant [hipe_icode:mk_primop([Dst1], unsafe_untag_float, [Src1]) | trans_fun(Instructions,Env)] end; _ -> trans_fun([{move,Src,Dst}|Instructions], Env) end; %%--- fconv --- trans_fun([{fconv,Eterm,FReg}|Instructions], Env) -> case get(hipe_inline_fp) of true -> Src = trans_arg(Eterm), ContLbl = mk_label(new), Dst = mk_var(FReg), [hipe_icode:mk_primop([Dst], conv_to_float, [Src], hipe_icode:label_name(ContLbl), []), ContLbl| trans_fun(Instructions, Env)]; _ -> trans_fun([{fmove,Eterm,FReg}|Instructions], Env) end; %%--- fadd --- trans_fun([{arithfbif,fadd,Lab,SrcRs,DstR}|Instructions], Env) -> case get(hipe_inline_fp) of true -> trans_fun([{arithbif,fp_add,Lab,SrcRs,DstR}|Instructions], Env); _ -> trans_fun([{arithbif,'+',Lab,SrcRs,DstR}|Instructions], Env) end; %%--- fsub --- trans_fun([{arithfbif,fsub,Lab,SrcRs,DstR}|Instructions], Env) -> case get(hipe_inline_fp) of true -> trans_fun([{arithbif,fp_sub,Lab,SrcRs,DstR}|Instructions], Env); _ -> trans_fun([{arithbif,'-',Lab,SrcRs,DstR}|Instructions], Env) end; %%--- fmult --- trans_fun([{arithfbif,fmul,Lab,SrcRs,DstR}|Instructions], Env) -> case get(hipe_inline_fp) of true -> trans_fun([{arithbif,fp_mul,Lab,SrcRs,DstR}|Instructions], Env); _ -> trans_fun([{arithbif,'*',Lab,SrcRs,DstR}|Instructions], Env) end; %%--- fdiv --- trans_fun([{arithfbif,fdiv,Lab,SrcRs,DstR}|Instructions], Env) -> case get(hipe_inline_fp) of true -> trans_fun([{arithbif,fp_div,Lab,SrcRs,DstR}|Instructions], Env); _ -> trans_fun([{arithbif,'/',Lab,SrcRs,DstR}|Instructions], Env) end; %%--- fnegate --- trans_fun([{arithfbif,fnegate,Lab,[SrcR],DestR}|Instructions], Env) -> case get(hipe_inline_fp) of true -> Src = trans_arg(SrcR), Dst = mk_var(DestR), [hipe_icode:mk_primop([Dst], fnegate, [Src])| trans_fun(Instructions,Env)]; _ -> trans_fun([{arithbif,'-',Lab,[{float,0.0},SrcR],DestR}|Instructions], Env) end; %%-------------------------------------------------------------------- %% apply instructions added in April 2004 (R10B). %%-------------------------------------------------------------------- trans_fun([{apply,Arity}|Instructions], Env) -> BeamArgs = extract_fun_args(Arity+2), %% +2 is for M and F {Args,[M,F]} = lists:split(Arity,BeamArgs), Dst = [mk_var({r,0})], [hipe_icode:mk_comment('apply'), hipe_icode:mk_primop(Dst, #apply_N{arity=Arity}, [M,F|Args]) | trans_fun(Instructions,Env)]; trans_fun([{apply_last,Arity,_N}|Instructions], Env) -> % N is StackAdjustment? BeamArgs = extract_fun_args(Arity+2), %% +2 is for M and F {Args,[M,F]} = lists:split(Arity,BeamArgs), [hipe_icode:mk_comment('apply_last'), hipe_icode:mk_enter_primop(#apply_N{arity=Arity}, [M,F|Args]) | trans_fun(Instructions,Env)]; %%-------------------------------------------------------------------- %% test for boolean added in April 2004 (R10B). %%-------------------------------------------------------------------- %%--- is_boolean --- trans_fun([{test,is_boolean,{f,Lbl},[Arg]}|Instructions], Env) -> {Code,Env1} = trans_type_test(boolean,Lbl,Arg,Env), [Code | trans_fun(Instructions,Env1)]; %%-------------------------------------------------------------------- %% test for function with specific arity added in June 2005 (R11). %%-------------------------------------------------------------------- %%--- is_function2 --- trans_fun([{test,is_function2,{f,Lbl},[Arg,Arity]}|Instructions], Env) -> {Code,Env1} = trans_type_test2(function2,Lbl,Arg,Arity,Env), [Code | trans_fun(Instructions,Env1)]; %%-------------------------------------------------------------------- %% garbage collecting BIFs added in January 2006 (R11B). %%-------------------------------------------------------------------- trans_fun([{gc_bif,'-',Fail,_Live,[SrcR],DstR}|Instructions], Env) -> %% Unary minus. Change this to binary minus. trans_fun([{arithbif,'-',Fail,[{integer,0},SrcR],DstR}|Instructions], Env); trans_fun([{gc_bif,'+',Fail,_Live,[SrcR],DstR}|Instructions], Env) -> %% Unary plus. Change this to a bif call. trans_fun([{bif,'+',Fail,[SrcR],DstR}|Instructions], Env); trans_fun([{gc_bif,Name,Fail,_Live,SrcRs,DstR}|Instructions], Env) -> case erl_internal:guard_bif(Name, length(SrcRs)) of false -> %% Arithmetic instruction. trans_fun([{arithbif,Name,Fail,SrcRs,DstR}|Instructions], Env); true -> %% A guard BIF. trans_fun([{bif,Name,Fail,SrcRs,DstR}|Instructions], Env) end; %%-------------------------------------------------------------------- %% test for bitstream added in July 2007 (R12). %%-------------------------------------------------------------------- %%--- is_bitstr --- trans_fun([{test,is_bitstr,{f,Lbl},[Arg]}|Instructions], Env) -> {Code,Env1} = trans_type_test(bitstr, Lbl, Arg, Env), [Code | trans_fun(Instructions, Env1)]; %%-------------------------------------------------------------------- %% stack triming instruction added in October 2007 (R12). %%-------------------------------------------------------------------- trans_fun([{trim,N,NY}|Instructions], Env) -> %% trim away N registers leaving NY registers Moves = trans_trim(N, NY), Moves ++ trans_fun(Instructions, Env); %%-------------------------------------------------------------------- %% line instruction added in Fall 2012 (R15). %%-------------------------------------------------------------------- trans_fun([{line,_}|Instructions], Env) -> trans_fun(Instructions,Env); %%-------------------------------------------------------------------- %% Map instructions added in Spring 2014 (17.0). %%-------------------------------------------------------------------- trans_fun([{test,has_map_fields,{f,Lbl},Map,{list,Keys}}|Instructions], Env) -> {MapMove, MapVar, Env1} = mk_move_and_var(Map, Env), %% We assume that hipe_icode:mk_call has no side-effects, and reuse %% the help function of get_map_elements below, discarding the value %% assignment instruction list. {TestInstructions, _GetInstructions, Env2} = trans_map_query(MapVar, map_label(Lbl), Env1, lists:flatten([[K, {r, 0}] || K <- Keys])), [MapMove, TestInstructions | trans_fun(Instructions, Env2)]; trans_fun([{get_map_elements,{f,Lbl},Map,{list,KVPs}}|Instructions], Env) -> {MapMove, MapVar, Env1} = mk_move_and_var(Map, Env), {TestInstructions, GetInstructions, Env2} = trans_map_query(MapVar, map_label(Lbl), Env1, KVPs), [MapMove, TestInstructions, GetInstructions | trans_fun(Instructions, Env2)]; %%--- put_map_assoc --- trans_fun([{put_map_assoc,{f,Lbl},Map,Dst,_N,{list,Pairs}}|Instructions], Env) -> {MapMove, MapVar, Env1} = mk_move_and_var(Map, Env), TempMapVar = mk_var(new), TempMapMove = hipe_icode:mk_move(TempMapVar, MapVar), {PutInstructions, Env2} = case Lbl > 0 of true -> gen_put_map_instrs(exists, assoc, TempMapVar, Dst, Lbl, Pairs, Env1); false -> gen_put_map_instrs(new, assoc, TempMapVar, Dst, new, Pairs, Env1) end, [MapMove, TempMapMove, PutInstructions | trans_fun(Instructions, Env2)]; %%--- put_map_exact --- trans_fun([{put_map_exact,{f,Lbl},Map,Dst,_N,{list,Pairs}}|Instructions], Env) -> {MapMove, MapVar, Env1} = mk_move_and_var(Map, Env), TempMapVar = mk_var(new), TempMapMove = hipe_icode:mk_move(TempMapVar, MapVar), {PutInstructions, Env2} = case Lbl > 0 of true -> gen_put_map_instrs(exists, exact, TempMapVar, Dst, Lbl, Pairs, Env1); false -> gen_put_map_instrs(new, exact, TempMapVar, Dst, new, Pairs, Env1) end, [MapMove, TempMapMove, PutInstructions | trans_fun(Instructions, Env2)]; %%--- build_stacktrace --- trans_fun([build_stacktrace|Instructions], Env) -> Vars = [mk_var({x,0})], %{x,0} is implict arg and dst [hipe_icode:mk_primop(Vars,build_stacktrace,Vars), trans_fun(Instructions, Env)]; %%--- raw_raise --- trans_fun([raw_raise|Instructions], Env) -> Vars = [mk_var({x,0}),mk_var({x,1}),mk_var({x,2})], Dst = [mk_var({x,0})], [hipe_icode:mk_primop(Dst,raw_raise,Vars) | trans_fun(Instructions, Env)]; %%-------------------------------------------------------------------- %%--- ERROR HANDLING --- %%-------------------------------------------------------------------- trans_fun([X|_], _) -> ?EXIT({'trans_fun/2',X}); trans_fun([], _) -> []. %%-------------------------------------------------------------------- %% trans_call and trans_enter generate correct Icode calls/tail-calls, %% recognizing explicit fails. %%-------------------------------------------------------------------- trans_call(MFA={M,F,_A}, Dst, Args, Type) -> handle_fail(MFA, Args, fun () -> hipe_icode:mk_call(Dst,M,F,Args,Type) end). trans_enter(MFA={M,F,_A}, Args, Type) -> handle_fail(MFA, Args, fun () -> hipe_icode:mk_enter(M,F,Args,Type) end). handle_fail(MFA, Args, F) -> case MFA of {erlang,exit,1} -> hipe_icode:mk_fail(Args,exit); {erlang,throw,1} -> hipe_icode:mk_fail(Args,throw); {erlang,fault,1} -> hipe_icode:mk_fail(Args,error); {erlang,fault,2} -> hipe_icode:mk_fail(Args,error); {erlang,error,1} -> hipe_icode:mk_fail(Args,error); {erlang,error,2} -> hipe_icode:mk_fail(Args,error); _ -> F() end. %%----------------------------------------------------------------------- %% trans_bif0(BifName, DestReg) %% trans_bif(Arity, BifName, FailLab, Args, DestReg, Environment) %%----------------------------------------------------------------------- trans_bif0(BifName, DestReg) -> ?no_debug_msg(" found BIF0: ~p() ...~n", [BifName]), BifRes = mk_var(DestReg), hipe_icode:mk_call([BifRes],erlang,BifName,[],remote). trans_bif(Arity, BifName, Lbl, Args, DestReg, Env) -> ?no_debug_msg(" found BIF: ~p(~p) ...~n", [BifName,Args]), BifRes = mk_var(DestReg), {Movs, SrcVars, Env1} = get_constants_in_temps(Args,Env), case Lbl of 0 -> % Bif is not in a guard I = hipe_icode:mk_call([BifRes],erlang,BifName,SrcVars,remote), {Movs ++ [I], Env1}; _ -> % Bif occurs in a guard - fail silently to Lbl {GuardI,Env2} = make_fallthrough_guard([BifRes],{erlang,BifName,Arity},SrcVars, map_label(Lbl),Env1), {[Movs,GuardI], Env2} end. trans_op_call(Name, Lbl, Args, Dests, Env, Instructions) -> {Code, Env1} = trans_one_op_call(Name, Lbl, Args, Dests, Env), [Code|trans_fun(Instructions, Env1)]. trans_one_op_call(Name, Lbl, Args, Dests, Env) -> case Lbl of 0 -> % Op is not in a guard I = hipe_icode:mk_primop(Dests, Name, Args), {[I], Env}; _ -> % op occurs in a guard - fail silently to Lbl make_fallthrough_guard(Dests, Name, Args, map_label(Lbl), Env) end. %%----------------------------------------------------------------------- %% trans_bin_call %%----------------------------------------------------------------------- trans_bin_call(Name, Lbl, Args, Dests, Base, Offset, Env, Instructions) -> {Code, Env1} = case Lbl of 0 -> % Op is not in a guard I = hipe_icode:mk_primop(Dests, Name, Args), {[I], Env}; _ -> % op occurs in a guard - fail silently to Lbl make_fallthrough_guard(Dests, Name, Args, map_label(Lbl), Env) end, [Code|trans_bin(Instructions, Base, Offset, Env1)]. %% Translate instructions for building binaries separately to give %% them an appropriate state trans_bin([{bs_put_float,{f,Lbl},Size,Unit,{field_flags,Flags0},Source}| Instructions], Base, Offset, Env) -> Flags = resolve_native_endianess(Flags0), %% Get source {Src,SourceInstrs,ConstInfo} = case is_var(Source) of true -> {mk_var(Source),[], var}; false -> case Source of {float, X} when is_float(X) -> C = trans_const(Source), SrcVar = mk_var(new), I = hipe_icode:mk_move(SrcVar, C), {SrcVar,[I],pass}; _ -> C = trans_const(Source), SrcVar = mk_var(new), I = hipe_icode:mk_move(SrcVar, C), {SrcVar,[I],fail} end end, %% Get type of put_float {Name,Args,Env2} = case Size of {integer,NoBits} when is_integer(NoBits), NoBits >= 0 -> %% Create a N*Unit bits float {{bs_put_float, NoBits*Unit, Flags, ConstInfo}, [Src, Base, Offset], Env}; {integer,NoBits} when is_integer(NoBits), NoBits < 0 -> ?EXIT({bad_bs_size_constant,Size}); BitReg -> % Use a number of bits only known at runtime. Bits = mk_var(BitReg), {{bs_put_float, Unit, Flags, ConstInfo}, [Src,Bits,Base,Offset], Env} end, %% Generate code for calling the bs-op. SourceInstrs ++ trans_bin_call({hipe_bs_primop,Name}, Lbl, Args, [Offset], Base, Offset, Env2, Instructions); trans_bin([{bs_put_binary,{f,Lbl},Size,Unit,{field_flags,Flags},Source}| Instructions], Base, Offset, Env) -> %% Get the source of the binary. Src = trans_arg(Source), %% Get type of put_binary {Name, Args, Env2} = case Size of {atom,all} -> %% put all bits {{bs_put_binary_all, Unit, Flags}, [Src,Base,Offset], Env}; {integer,NoBits} when is_integer(NoBits), NoBits >= 0 -> %% Create a N*Unit bits subbinary {{bs_put_binary, NoBits*Unit, Flags}, [Src,Base,Offset], Env}; {integer,NoBits} when is_integer(NoBits), NoBits < 0 -> ?EXIT({bad_bs_size_constant,Size}); BitReg -> % Use a number of bits only known at runtime. Bits = mk_var(BitReg), {{bs_put_binary, Unit, Flags}, [Src, Bits,Base,Offset], Env} end, %% Generate code for calling the bs-op. trans_bin_call({hipe_bs_primop, Name}, Lbl, Args, [Offset], Base, Offset, Env2, Instructions); %%--- bs_put_string --- trans_bin([{bs_put_string,SizeInBytes,{string,String}}|Instructions], Base, Offset, Env) -> [hipe_icode:mk_primop([Offset], {hipe_bs_primop,{bs_put_string, String, SizeInBytes}}, [Base, Offset]) | trans_bin(Instructions, Base, Offset, Env)]; trans_bin([{bs_put_integer,{f,Lbl},Size,Unit,{field_flags,Flags0},Source}| Instructions], Base, Offset, Env) -> Flags = resolve_native_endianess(Flags0), %% Get size-type %% Get the source of the binary. {Src, SrcInstrs, ConstInfo} = case is_var(Source) of true -> {mk_var(Source), [], var}; false -> case Source of {integer, X} when is_integer(X) -> C = trans_const(Source), SrcVar = mk_var(new), I = hipe_icode:mk_move(SrcVar, C), {SrcVar,[I], pass}; _ -> C = trans_const(Source), SrcVar = mk_var(new), I = hipe_icode:mk_move(SrcVar, C), {SrcVar,[I], fail} end end, {Name, Args, Env2} = case is_var(Size) of true -> SVar = mk_var(Size), {{bs_put_integer,Unit,Flags,ConstInfo}, [SVar, Base, Offset], Env}; false -> case Size of {integer, NoBits} when NoBits >= 0 -> {{bs_put_integer,NoBits*Unit,Flags,ConstInfo}, [Base, Offset], Env}; _ -> ?EXIT({bad_bs_size_constant,Size}) end end, SrcInstrs ++ trans_bin_call({hipe_bs_primop, Name}, Lbl, [Src|Args], [Offset], Base, Offset, Env2, Instructions); %%---------------------------------------------------------------- %% binary construction instructions added in Fall 2008 (R12B-5). %%---------------------------------------------------------------- trans_bin([{bs_put_utf8,{f,Lbl},_FF,A3}|Instructions], Base, Offset, Env) -> Src = trans_arg(A3), Args = [Src, Base, Offset], trans_bin_call({hipe_bs_primop, bs_put_utf8}, Lbl, Args, [Offset], Base, Offset, Env, Instructions); trans_bin([{bs_put_utf16,{f,Lbl},{field_flags,Flags0},A3}|Instructions], Base, Offset, Env) -> Src = trans_arg(A3), Args = [Src, Base, Offset], Flags = resolve_native_endianess(Flags0), Name = {bs_put_utf16, Flags}, trans_bin_call({hipe_bs_primop, Name}, Lbl, Args, [Offset], Base, Offset, Env, Instructions); trans_bin([{bs_put_utf32,F={f,Lbl},FF={field_flags,_Flags0},A3}|Instructions], Base, Offset, Env) -> Src = trans_arg(A3), trans_bin_call({hipe_bs_primop,bs_validate_unicode}, Lbl, [Src], [], Base, Offset, Env, [{bs_put_integer,F,{integer,32},1,FF,A3} | Instructions]); %%---------------------------------------------------------------- %% Base cases for the end of a binary construction sequence. %%---------------------------------------------------------------- trans_bin([{bs_final2,Src,Dst}|Instructions], _Base, Offset, Env) -> [hipe_icode:mk_primop([mk_var(Dst)], {hipe_bs_primop, bs_final}, [trans_arg(Src),Offset]) |trans_fun(Instructions, Env)]; trans_bin(Instructions, _Base, _Offset, Env) -> trans_fun(Instructions, Env). %% this translates bs_get_utf8 and bs_skip_utf8 (get with new unused dst) trans_bs_get_or_skip_utf8(Lbl, Ms, X, Instructions, Env) -> Dst = mk_var(X), MsVar = mk_var(Ms), trans_op_call({hipe_bs_primop,bs_get_utf8}, Lbl, [MsVar], [Dst,MsVar], Env, Instructions). %% this translates bs_get_utf16 and bs_skip_utf16 (get with new unused dst) trans_bs_get_or_skip_utf16(Lbl, Ms, Flags0, X, Instructions, Env) -> Dst = mk_var(X), MsVar = mk_var(Ms), Flags = resolve_native_endianess(Flags0), Name = {bs_get_utf16,Flags}, trans_op_call({hipe_bs_primop,Name}, Lbl, [MsVar], [Dst,MsVar], Env, Instructions). %% this translates bs_get_utf32 and bs_skip_utf32 (get with new unused dst) trans_bs_get_or_skip_utf32(Lbl, Ms, Flags0, X, Instructions, Env) -> Dst = mk_var(X), MsVar = mk_var(Ms), Flags = resolve_native_endianess(Flags0), {I1,Env1} = trans_one_op_call({hipe_bs_primop,{bs_get_integer,32,Flags}}, Lbl, [MsVar], [Dst,MsVar], Env), I1 ++ trans_op_call({hipe_bs_primop,bs_validate_unicode_retract}, Lbl, [Dst,MsVar], [MsVar], Env1, Instructions). %%----------------------------------------------------------------------- %% trans_arith(Op, SrcVars, Des, Lab, Env) -> {Icode, NewEnv} %% A failure label of type {f,0} means in a body. %% A failure label of type {f,L} where L>0 means in a guard. %% Within a guard a failure should branch to the next guard and %% not trigger an exception!! %% Handles body arithmetic with Icode primops! %% Handles guard arithmetic with Icode guardops! %%----------------------------------------------------------------------- trans_arith(Op, SrcRs, DstR, Lbl, Env) -> {Movs,SrcVars,Env1} = get_constants_in_temps(SrcRs,Env), DstVar = mk_var(DstR), %%io:format("~w:trans_arith()\n ~w := ~w ~w\n", %% [?MODULE,DstVar,SrcVars,Op]), case Lbl of 0 -> % Body arithmetic Primop = hipe_icode:mk_primop([DstVar], arith_op_name(Op), SrcVars), {Movs++[Primop], Env1}; _ -> % Guard arithmetic {Guard,Env2} = make_fallthrough_guard([DstVar], arith_op_name(Op), SrcVars, map_label(Lbl), Env1), {[Movs,Guard], Env2} end. %% Prevent arbitrary names from leaking into Icode from BEAM. arith_op_name('+') -> '+'; arith_op_name('-') -> '-'; arith_op_name('*') -> '*'; arith_op_name('/') -> '/'; arith_op_name('div') -> 'div'; arith_op_name('fp_add') -> 'fp_add'; arith_op_name('fp_sub') -> 'fp_sub'; arith_op_name('fp_mul') -> 'fp_mul'; arith_op_name('fp_div') -> 'fp_div'; arith_op_name('rem') -> 'rem'; arith_op_name('bsl') -> 'bsl'; arith_op_name('bsr') -> 'bsr'; arith_op_name('band') -> 'band'; arith_op_name('bor') -> 'bor'; arith_op_name('bxor') -> 'bxor'; arith_op_name('bnot') -> 'bnot'. %%----------------------------------------------------------------------- %%----------------------------------------------------------------------- trans_test_guard(TestOp,F,Arg1,Arg2,Env) -> {Movs,Vars,Env1} = get_constants_in_temps([Arg1,Arg2],Env), True = mk_label(new), I = hipe_icode:mk_if(TestOp,Vars,hipe_icode:label_name(True),map_label(F)), {[Movs,I,True], Env1}. %%----------------------------------------------------------------------- %%----------------------------------------------------------------------- make_fallthrough_guard(DstVar,GuardOp,Args,FailLName,Env) -> ContL = mk_label(new), ContLName = hipe_icode:label_name(ContL), {Instrs, NewDsts} = clone_dsts(DstVar), {Guard,Env1} = make_guard(NewDsts,GuardOp,Args,ContLName,FailLName,Env), {[Guard,ContL]++Instrs,Env1}. %% Make sure DstVar gets initialised to a dummy value after a fail: %make_guard(Dests,{hipe_bs_primop,Primop},Args,ContLName,FailLName,Env) -> % {[hipe_icode:mk_guardop(Dests,{hipe_bs_primop,Primop},Args,ContLName,FailLName)], % Env}; make_guard(Dests=[_|_],GuardOp,Args,ContLName,FailLName,Env) -> TmpFailL = mk_label(new), TmpFailLName = hipe_icode:label_name(TmpFailL), GuardOpIns = hipe_icode:mk_guardop(Dests,GuardOp,Args, ContLName,TmpFailLName), FailCode = [TmpFailL, nillify_all(Dests), hipe_icode:mk_goto(FailLName)], {[GuardOpIns|FailCode], Env}; %% A guard that does not return anything: make_guard([],GuardOp,Args,ContLName,FailLName,Env) -> {[hipe_icode:mk_guardop([],GuardOp,Args,ContLName,FailLName)], Env}. nillify_all([Var|Vars]) -> [hipe_icode:mk_move(Var,hipe_icode:mk_const([]))|nillify_all(Vars)]; nillify_all([]) -> []. clone_dsts(Dests) -> clone_dsts(Dests, [],[]). clone_dsts([Dest|Dests], Instrs, NewDests) -> {I,ND} = clone_dst(Dest), clone_dsts(Dests, [I|Instrs], [ND|NewDests]); clone_dsts([], Instrs, NewDests) -> {lists:reverse(Instrs), lists:reverse(NewDests)}. clone_dst(Dest) -> New = case hipe_icode:is_reg(Dest) of true -> case hipe_icode:reg_is_gcsafe(Dest) of true -> mk_var(reg_gcsafe); false -> mk_var(reg) end; false -> true = hipe_icode:is_var(Dest), mk_var(new) end, {hipe_icode:mk_move(Dest, New), New}. %%----------------------------------------------------------------------- %% trans_type_test(Test, Lbl, Arg, Env) -> {Icode, NewEnv} %% Handles all unary type tests like is_integer etc. %%----------------------------------------------------------------------- trans_type_test(Test, Lbl, Arg, Env) -> True = mk_label(new), {Move,Var,Env1} = mk_move_and_var(Arg,Env), I = hipe_icode:mk_type([Var], Test, hipe_icode:label_name(True), map_label(Lbl)), {[Move,I,True],Env1}. %% %% This handles binary type tests. Currently, the only such is the %% is_function/2 BIF. %% trans_type_test2(function2, Lbl, Arg, Arity, Env) -> True = mk_label(new), {Move1,Var1,Env1} = mk_move_and_var(Arg, Env), {Move2,Var2,Env2} = mk_move_and_var(Arity, Env1), I = hipe_icode:mk_type([Var1,Var2], function2, hipe_icode:label_name(True), map_label(Lbl)), {[Move1,Move2,I,True],Env2}. %% %% Handles the get_map_elements instruction and the has_map_fields %% test instruction. %% trans_map_query(_MapVar, _FailLabel, Env, []) -> {[], [], Env}; trans_map_query(MapVar, FailLabel, Env, [Key,Val|KVPs]) -> {Move,KeyVar,Env1} = mk_move_and_var(Key,Env), PassLabel = mk_label(new), BoolVar = hipe_icode:mk_new_var(), ValVar = mk_var(Val), IsKeyCall = hipe_icode:mk_call([BoolVar], maps, is_key, [KeyVar, MapVar], remote), TrueTest = hipe_icode:mk_if('=:=', [BoolVar, hipe_icode:mk_const(true)], hipe_icode:label_name(PassLabel), FailLabel), GetCall = hipe_icode:mk_call([ValVar], maps, get, [KeyVar, MapVar], remote), {TestList, GetList, Env2} = trans_map_query(MapVar, FailLabel, Env1, KVPs), {[Move, IsKeyCall, TrueTest, PassLabel|TestList], [GetCall|GetList], Env2}. %% %% Generates a fail label if necessary when translating put_map_* instructions. %% gen_put_map_instrs(exists, Op, TempMapVar, Dst, FailLbl, Pairs, Env) -> TrueLabel = mk_label(new), IsMapCode = hipe_icode:mk_type([TempMapVar], map, hipe_icode:label_name(TrueLabel), map_label(FailLbl)), DstMapVar = mk_var(Dst), {ReturnLbl, PutInstructions, Env1} = case Op of assoc -> trans_put_map_assoc(TempMapVar, DstMapVar, Pairs, Env, []); exact -> trans_put_map_exact(TempMapVar, DstMapVar, map_label(FailLbl), Pairs, Env, []) end, {[IsMapCode, TrueLabel, PutInstructions, ReturnLbl], Env1}; gen_put_map_instrs(new, Op, TempMapVar, Dst, new, Pairs, Env) -> FailLbl = mk_label(new), DstMapVar = mk_var(Dst), {ReturnLbl, PutInstructions, Env1} = case Op of assoc -> trans_put_map_assoc(TempMapVar, DstMapVar, Pairs, Env, []); exact -> trans_put_map_exact(TempMapVar, DstMapVar, none, Pairs, Env, []) end, Fail = hipe_icode:mk_fail([hipe_icode:mk_const(badarg)], error), {[PutInstructions, FailLbl, Fail, ReturnLbl], Env1}. %%----------------------------------------------------------------------- %% This function generates the instructions needed to insert several %% (Key, Value) pairs into an existing map, each recursive call inserts %% one (Key, Value) pair. %%----------------------------------------------------------------------- trans_put_map_assoc(MapVar, DestMapVar, [], Env, Acc) -> MoveToReturnVar = hipe_icode:mk_move(DestMapVar, MapVar), ReturnLbl = mk_label(new), GotoReturn = hipe_icode:mk_goto(hipe_icode:label_name(ReturnLbl)), {ReturnLbl, lists:reverse([GotoReturn, MoveToReturnVar | Acc]), Env}; trans_put_map_assoc(MapVar, DestMapVar, [Key, Value | Rest], Env, Acc) -> {MoveKey, KeyVar, Env1} = mk_move_and_var(Key, Env), {MoveVal, ValVar, Env2} = mk_move_and_var(Value, Env1), BifCall = hipe_icode:mk_call([MapVar], maps, put, [KeyVar, ValVar, MapVar], remote), trans_put_map_assoc(MapVar, DestMapVar, Rest, Env2, [BifCall, MoveVal, MoveKey | Acc]). %%----------------------------------------------------------------------- %% This function generates the instructions needed to update several %% (Key, Value) pairs in an existing map, each recursive call inserts %% one (Key, Value) pair. %%----------------------------------------------------------------------- trans_put_map_exact(MapVar, DestMapVar, _FLbl, [], Env, Acc) -> MoveToReturnVar = hipe_icode:mk_move(DestMapVar, MapVar), ReturnLbl = mk_label(new), GotoReturn = hipe_icode:mk_goto(hipe_icode:label_name(ReturnLbl)), {ReturnLbl, lists:reverse([GotoReturn, MoveToReturnVar | Acc]), Env}; trans_put_map_exact(MapVar, DestMapVar, none, [Key, Value | Rest], Env, Acc) -> {MoveKey, KeyVar, Env1} = mk_move_and_var(Key, Env), {MoveVal, ValVar, Env2} = mk_move_and_var(Value, Env1), BifCallPut = hipe_icode:mk_call([MapVar], maps, update, [KeyVar, ValVar, MapVar], remote), Acc1 = [BifCallPut, MoveVal, MoveKey | Acc], trans_put_map_exact(MapVar, DestMapVar, none, Rest, Env2, Acc1); trans_put_map_exact(MapVar, DestMapVar, FLbl, [Key, Value | Rest], Env, Acc) -> SuccLbl = mk_label(new), {MoveKey, KeyVar, Env1} = mk_move_and_var(Key, Env), {MoveVal, ValVar, Env2} = mk_move_and_var(Value, Env1), IsKey = hipe_icode:mk_new_var(), BifCallIsKey = hipe_icode:mk_call([IsKey], maps, is_key, [KeyVar, MapVar], remote), IsKeyTest = hipe_icode:mk_if('=:=', [IsKey, hipe_icode:mk_const(true)], hipe_icode:label_name(SuccLbl), FLbl), BifCallPut = hipe_icode:mk_call([MapVar], maps, put, [KeyVar, ValVar, MapVar], remote), Acc1 = [BifCallPut, SuccLbl, IsKeyTest, BifCallIsKey, MoveVal, MoveKey | Acc], trans_put_map_exact(MapVar, DestMapVar, FLbl, Rest, Env2, Acc1). %%----------------------------------------------------------------------- %% trans_puts(Code, Environment) -> %% {Movs, Code, Vars, NewEnv} %%----------------------------------------------------------------------- trans_puts(Code, Env) -> trans_puts(Code, [], [], Env). trans_puts([{put,X}|Code], Vars, Moves, Env) -> case type(X) of var -> Var = mk_var(X), trans_puts(Code, [Var|Vars], Moves, Env); #beam_const{value=C} -> Var = mk_var(new), Move = hipe_icode:mk_move(Var, hipe_icode:mk_const(C)), trans_puts(Code, [Var|Vars], [Move|Moves], Env) end; trans_puts(Code, Vars, Moves, Env) -> %% No more put operations {Moves, Code, Vars, Env}. %%----------------------------------------------------------------------- %% The code for this instruction is a bit large because we are treating %% different cases differently. We want to use the icode `type' %% instruction when it is applicable to take care of match expressions. %%----------------------------------------------------------------------- trans_is_eq_exact(Lbl, Arg1, Arg2, Env) -> case {is_var(Arg1),is_var(Arg2)} of {true,true} -> True = mk_label(new), I = hipe_icode:mk_if('=:=', [mk_var(Arg1),mk_var(Arg2)], hipe_icode:label_name(True), map_label(Lbl)), {[I,True], Env}; {true,false} -> %% right argument is a constant -- use type()! trans_is_eq_exact_var_const(Lbl, Arg1, Arg2, Env); {false,true} -> %% mirror of the case above; swap args trans_is_eq_exact_var_const(Lbl, Arg2, Arg1, Env); {false,false} -> %% both arguments are constants !!! case Arg1 =:= Arg2 of true -> {[], Env}; false -> Never = mk_label(new), I = hipe_icode:mk_goto(map_label(Lbl)), {[I,Never], Env} end end. trans_is_eq_exact_var_const(Lbl, Arg1, Arg2, Env) -> % var =:= const True = mk_label(new), NewArg1 = mk_var(Arg1), TrueLabName = hipe_icode:label_name(True), FalseLabName = map_label(Lbl), I = case Arg2 of {float,Float} -> hipe_icode:mk_if('=:=', [NewArg1, hipe_icode:mk_const(Float)], TrueLabName, FalseLabName); {literal,Literal} -> hipe_icode:mk_if('=:=', [NewArg1, hipe_icode:mk_const(Literal)], TrueLabName, FalseLabName); _ -> hipe_icode:mk_type([NewArg1], Arg2, TrueLabName, FalseLabName) end, {[I,True], Env}. %%----------------------------------------------------------------------- %% ... and this is analogous to the above %%----------------------------------------------------------------------- trans_is_ne_exact(Lbl, Arg1, Arg2, Env) -> case {is_var(Arg1),is_var(Arg2)} of {true,true} -> True = mk_label(new), I = hipe_icode:mk_if('=/=', [mk_var(Arg1),mk_var(Arg2)], hipe_icode:label_name(True), map_label(Lbl)), {[I,True], Env}; {true,false} -> %% right argument is a constant -- use type()! trans_is_ne_exact_var_const(Lbl, Arg1, Arg2, Env); {false,true} -> %% mirror of the case above; swap args trans_is_ne_exact_var_const(Lbl, Arg2, Arg1, Env); {false,false} -> %% both arguments are constants !!! case Arg1 =/= Arg2 of true -> {[], Env}; false -> Never = mk_label(new), I = hipe_icode:mk_goto(map_label(Lbl)), {[I,Never], Env} end end. trans_is_ne_exact_var_const(Lbl, Arg1, Arg2, Env) -> % var =/= const True = mk_label(new), NewArg1 = mk_var(Arg1), TrueLabName = hipe_icode:label_name(True), FalseLabName = map_label(Lbl), I = case Arg2 of {float,Float} -> hipe_icode:mk_if('=/=', [NewArg1, hipe_icode:mk_const(Float)], TrueLabName, FalseLabName); {literal,Literal} -> hipe_icode:mk_if('=/=', [NewArg1, hipe_icode:mk_const(Literal)], TrueLabName, FalseLabName); _ -> hipe_icode:mk_type([NewArg1], Arg2, FalseLabName, TrueLabName) end, {[I,True], Env}. %%----------------------------------------------------------------------- %% Try to do a relatively straightforward optimization: if equality with %% an atom is used, then convert this test to use of exact equality test %% with the same atom (which in turn will be translated to a `type' test %% instruction by the code of trans_is_eq_exact_var_const/4 above). %%----------------------------------------------------------------------- trans_is_eq(Lbl, Arg1, Arg2, Env) -> case {is_var(Arg1),is_var(Arg2)} of {true,true} -> %% not much can be done in this case trans_test_guard('==', Lbl, Arg1, Arg2, Env); {true,false} -> %% optimize this case, if possible case Arg2 of {atom,_SomeAtom} -> trans_is_eq_exact_var_const(Lbl, Arg1, Arg2, Env); _ -> trans_test_guard('==', Lbl, Arg1, Arg2, Env) end; {false,true} -> %% probably happens rarely; hence the recursive call trans_is_eq(Lbl, Arg2, Arg1, Env); {false,false} -> %% both arguments are constants !!! case Arg1 == Arg2 of true -> {[], Env}; false -> Never = mk_label(new), I = hipe_icode:mk_goto(map_label(Lbl)), {[I,Never], Env} end end. %%----------------------------------------------------------------------- %% ... and this is analogous to the above %%----------------------------------------------------------------------- trans_is_ne(Lbl, Arg1, Arg2, Env) -> case {is_var(Arg1),is_var(Arg2)} of {true,true} -> %% not much can be done in this case trans_test_guard('/=', Lbl, Arg1, Arg2, Env); {true,false} -> %% optimize this case, if possible case Arg2 of {atom,_SomeAtom} -> trans_is_ne_exact_var_const(Lbl, Arg1, Arg2, Env); _ -> trans_test_guard('/=', Lbl, Arg1, Arg2, Env) end; {false,true} -> %% probably happens rarely; hence the recursive call trans_is_ne(Lbl, Arg2, Arg1, Env); {false,false} -> %% both arguments are constants !!! case Arg1 /= Arg2 of true -> {[], Env}; false -> Never = mk_label(new), I = hipe_icode:mk_goto(map_label(Lbl)), {[I,Never], Env} end end. %%----------------------------------------------------------------------- %% Translates an allocate instruction into a sequence of initializations %%----------------------------------------------------------------------- trans_allocate(N) -> trans_allocate(N, []). trans_allocate(0, Acc) -> Acc; trans_allocate(N, Acc) -> Move = hipe_icode:mk_move(mk_var({y,N-1}), hipe_icode:mk_const('dummy_value')), trans_allocate(N-1, [Move|Acc]). %%----------------------------------------------------------------------- %% Translates a trim instruction into a sequence of moves %%----------------------------------------------------------------------- trans_trim(N, NY) -> lists:reverse(trans_trim(N, NY, 0, [])). trans_trim(_, 0, _, Acc) -> Acc; trans_trim(N, NY, Y, Acc) -> Move = hipe_icode:mk_move(mk_var({y,Y}), mk_var({y,N})), trans_trim(N+1, NY-1, Y+1, [Move|Acc]). %%----------------------------------------------------------------------- %%----------------------------------------------------------------------- mk_move_and_var(Var, Env) -> case type(Var) of var -> V = mk_var(Var), {[], V, Env}; #beam_const{value=C} -> V = mk_var(new), {[hipe_icode:mk_move(V,hipe_icode:mk_const(C))], V, Env} end. %%----------------------------------------------------------------------- %% Find names of closures and number of free vars. %%----------------------------------------------------------------------- closure_info_mfa(#closure_info{mfa=MFA}) -> MFA. closure_info_arity(#closure_info{arity=Arity}) -> Arity. %% closure_info_fv_arity(#closure_info{fv_arity=Arity}) -> Arity. find_closure_info(Code) -> mod_find_closure_info(Code, []). mod_find_closure_info([FunCode|Fs], CI) -> mod_find_closure_info(Fs, find_closure_info(FunCode, CI)); mod_find_closure_info([], CI) -> CI. find_closure_info([{patched_make_fun,MFA={_M,_F,A},_Magic,FreeVarNum,_Index}|BeamCode], ClosureInfo) -> NewClosure = %% A-FreeVarNum+1 (The real arity + 1 for the closure) #closure_info{mfa=MFA, arity=A-FreeVarNum+1, fv_arity=FreeVarNum}, find_closure_info(BeamCode, [NewClosure|ClosureInfo]); find_closure_info([_Inst|BeamCode], ClosureInfo) -> find_closure_info(BeamCode, ClosureInfo); find_closure_info([], ClosureInfo) -> ClosureInfo. %%----------------------------------------------------------------------- %% Is closure %%----------------------------------------------------------------------- get_closure_info(MFA, [CI|Rest]) -> case closure_info_mfa(CI) of MFA -> CI; _ -> get_closure_info(MFA, Rest) end; get_closure_info(_, []) -> not_a_closure. %%----------------------------------------------------------------------- %% Patch closure entry. %%----------------------------------------------------------------------- %% NOTE: this changes the number of parameters in the ICode function, %% but does *not* change the arity in the function name. Thus, all %% closure-functions have the exact same names in Beam and in native %% code, although they have different calling conventions. patch_closure_entry(Icode, ClosureInfo)-> Arity = closure_info_arity(ClosureInfo), %% ?msg("Arity ~w\n",[Arity]), {Args, Closure, FreeVars} = split_params(Arity, hipe_icode:icode_params(Icode), []), [Start|_] = hipe_icode:icode_code(Icode), {_LMin, LMax} = hipe_icode:icode_label_range(Icode), hipe_gensym:set_label(icode,LMax+1), {_VMin, VMax} = hipe_icode:icode_var_range(Icode), hipe_gensym:set_var(icode,VMax+1), MoveCode = gen_get_free_vars(FreeVars, Closure, hipe_icode:label_name(Start)), Icode1 = hipe_icode:icode_code_update(Icode, MoveCode ++ hipe_icode:icode_code(Icode)), Icode2 = hipe_icode:icode_params_update(Icode1, Args), %% Arity - 1 since the original arity did not have the closure argument. Icode3 = hipe_icode:icode_closure_arity_update(Icode2, Arity-1), Icode3. %%----------------------------------------------------------------------- gen_get_free_vars(Vars, Closure, StartName) -> [hipe_icode:mk_new_label()] ++ get_free_vars(Vars, Closure, 1, []) ++ [hipe_icode:mk_goto(StartName)]. get_free_vars([V|Vs], Closure, No, MoveCode) -> %% TempV = hipe_icode:mk_new_var(), get_free_vars(Vs, Closure, No+1, [%% hipe_icode:mk_move(TempV,hipe_icode:mk_const(No)), hipe_icode:mk_primop([V], #closure_element{n=No}, [Closure]) |MoveCode]); get_free_vars([],_,_,MoveCode) -> MoveCode. %%----------------------------------------------------------------------- split_params(1, [Closure|_OrgArgs] = Params, Args) -> {lists:reverse([Closure|Args]), Closure, Params}; split_params(1, [], Args) -> Closure = hipe_icode:mk_new_var(), {lists:reverse([Closure|Args]), Closure, []}; split_params(N, [ArgN|OrgArgs], Args) -> split_params(N-1, OrgArgs, [ArgN|Args]). %%----------------------------------------------------------------------- preprocess_code(ModuleCode) -> PatchedCode = patch_R7_funs(ModuleCode), ClosureInfo = find_closure_info(PatchedCode), {PatchedCode, ClosureInfo}. %%----------------------------------------------------------------------- %% Patches the "make_fun" BEAM instructions of R7 so that they also %% contain the index that the BEAM loader generates for funs. %% %% The index starts from 0 and is incremented by 1 for each make_fun %% instruction encountered. %% %% Retained only for compatibility with BEAM code prior to R8. %% %% Temporarily, it also rewrites R8-PRE-RELEASE "make_fun2" %% instructions, since their embedded indices don't work. %%----------------------------------------------------------------------- patch_R7_funs(ModuleCode) -> patch_make_funs(ModuleCode, 0). patch_make_funs([FunCode0|Fs], FunIndex0) -> {PatchedFunCode,FunIndex} = patch_make_funs(FunCode0, FunIndex0, []), [PatchedFunCode|patch_make_funs(Fs, FunIndex)]; patch_make_funs([], _) -> []. patch_make_funs([{make_fun,MFA,Magic,FreeVarNum}|Is], FunIndex, Acc) -> Patched = {patched_make_fun,MFA,Magic,FreeVarNum,FunIndex}, patch_make_funs(Is, FunIndex+1, [Patched|Acc]); patch_make_funs([{make_fun2,MFA,_BogusIndex,Magic,FreeVarNum}|Is], FunIndex, Acc) -> Patched = {patched_make_fun,MFA,Magic,FreeVarNum,FunIndex}, patch_make_funs(Is, FunIndex+1, [Patched|Acc]); patch_make_funs([I|Is], FunIndex, Acc) -> patch_make_funs(Is, FunIndex, [I|Acc]); patch_make_funs([], FunIndex, Acc) -> {lists:reverse(Acc),FunIndex}. %%----------------------------------------------------------------------- find_mfa([{label,_}|Code]) -> find_mfa(Code); find_mfa([{line,_}|Code]) -> find_mfa(Code); find_mfa([{func_info,{atom,M},{atom,F},A}|_]) when is_atom(M), is_atom(F), is_integer(A), 0 =< A, A =< 255 -> {M, F, A}. %%----------------------------------------------------------------------- %% Takes a list of arguments and returns the constants of them into %% fresh temporaries. Return a triple consisting of a list of move %% instructions, a list of proper icode arguments and the new environment. %%----------------------------------------------------------------------- get_constants_in_temps(Args, Env) -> get_constants_in_temps(Args, [], [], Env). get_constants_in_temps([Arg|Args], Instrs, Temps, Env) -> case get_constant_in_temp(Arg, Env) of {none,ArgVar,Env1} -> get_constants_in_temps(Args, Instrs, [ArgVar|Temps], Env1); {Instr,Temp,Env1} -> get_constants_in_temps(Args, [Instr|Instrs], [Temp|Temps], Env1) end; get_constants_in_temps([], Instrs, Temps, Env) -> {lists:reverse(Instrs), lists:reverse(Temps), Env}. %% If Arg is a constant then put Arg in a fresh temp! get_constant_in_temp(Arg, Env) -> case is_var(Arg) of true -> % Convert into Icode variable format before return {none, mk_var(Arg), Env}; false -> % Create a new temp and move the constant into it Temp = mk_var(new), Const = trans_const(Arg), {hipe_icode:mk_move(Temp, Const), Temp, Env} end. %%----------------------------------------------------------------------- %% Makes a list of function arguments. %%----------------------------------------------------------------------- extract_fun_args(A) -> lists:reverse(extract_fun_args1(A)). extract_fun_args1(0) -> []; extract_fun_args1(1) -> [mk_var({r,0})]; extract_fun_args1(N) -> [mk_var({x,N-1}) | extract_fun_args1(N-1)]. %%----------------------------------------------------------------------- %% Auxiliary translation for arguments of select_val & select_tuple_arity %%----------------------------------------------------------------------- trans_select_stuff(Reg, CaseList) -> SwVar = case is_var(Reg) of true -> mk_var(Reg); false -> trans_const(Reg) end, CasePairs = trans_case_list(CaseList), {SwVar,CasePairs}. trans_case_list([Symbol,{f,Lbl}|L]) -> [{trans_const(Symbol),map_label(Lbl)} | trans_case_list(L)]; trans_case_list([]) -> []. %%----------------------------------------------------------------------- %% Makes an Icode argument from a BEAM argument. %%----------------------------------------------------------------------- trans_arg(Arg) -> case is_var(Arg) of true -> mk_var(Arg); false -> trans_const(Arg) end. %%----------------------------------------------------------------------- %% Makes an Icode constant from a BEAM constant. %%----------------------------------------------------------------------- trans_const(Const) -> case Const of {atom,Atom} when is_atom(Atom) -> hipe_icode:mk_const(Atom); {integer,N} when is_integer(N) -> hipe_icode:mk_const(N); {float,Float} when is_float(Float) -> hipe_icode:mk_const(Float); {string,String} -> hipe_icode:mk_const(String); {literal,Literal} -> hipe_icode:mk_const(Literal); nil -> hipe_icode:mk_const([]); Int when is_integer(Int) -> hipe_icode:mk_const(Int) end. %%----------------------------------------------------------------------- %% Make an icode variable of proper type %% (Variables mod 5) =:= 0 are X regs %% (Variables mod 5) =:= 1 are Y regs %% (Variables mod 5) =:= 2 are FR regs %% (Variables mod 5) =:= 3 are new temporaries %% (Variables mod 5) =:= 4 are new register temporaries %% Tell hipe_gensym to update its state for each new thing created!! %%----------------------------------------------------------------------- mk_var({r,0}) -> hipe_icode:mk_var(0); mk_var({x,R}) when is_integer(R) -> V = 5*R, hipe_gensym:update_vrange(icode,V), hipe_icode:mk_var(V); mk_var({y,R}) when is_integer(R) -> V = (5*R)+1, hipe_gensym:update_vrange(icode,V), hipe_icode:mk_var(V); mk_var({fr,R}) when is_integer(R) -> V = (5*R)+2, hipe_gensym:update_vrange(icode,V), case get(hipe_inline_fp) of true -> hipe_icode:mk_fvar(V); _ -> hipe_icode:mk_var(V) end; mk_var(new) -> T = hipe_gensym:new_var(icode), V = (5*T)+3, hipe_gensym:update_vrange(icode,V), hipe_icode:mk_var(V); mk_var(reg) -> T = hipe_gensym:new_var(icode), V = (5*T)+4, hipe_gensym:update_vrange(icode,V), hipe_icode:mk_reg(V); mk_var(reg_gcsafe) -> T = hipe_gensym:new_var(icode), V = (5*T)+4, % same namespace as 'reg' hipe_gensym:update_vrange(icode,V), hipe_icode:mk_reg_gcsafe(V). %%----------------------------------------------------------------------- %% Make an icode label of proper type %% (Labels mod 2) =:= 0 are actually occuring in the BEAM code %% (Labels mod 2) =:= 1 are new labels generated by the translation %%----------------------------------------------------------------------- mk_label(L) when is_integer(L) -> LL = 2 * L, hipe_gensym:update_lblrange(icode, LL), hipe_icode:mk_label(LL); mk_label(new) -> L = hipe_gensym:new_label(icode), LL = (2 * L) + 1, hipe_gensym:update_lblrange(icode, LL), hipe_icode:mk_label(LL). %% Maps from the BEAM's labelling scheme to our labelling scheme. %% See mk_label to understand how it works. map_label(L) -> L bsl 1. % faster and more type-friendly version of 2 * L %%----------------------------------------------------------------------- %% Returns the type of the given variables. %%----------------------------------------------------------------------- type({x,_}) -> var; type({y,_}) -> var; type({fr,_}) -> var; type({atom,A}) when is_atom(A) -> #beam_const{value=A}; type(nil) -> #beam_const{value=[]}; type({integer,X}) when is_integer(X) -> #beam_const{value=X}; type({float,X}) when is_float(X) -> #beam_const{value=X}; type({literal,X}) -> #beam_const{value=X}. %%----------------------------------------------------------------------- %% Returns true iff the argument is a variable. %%----------------------------------------------------------------------- is_var({x,_}) -> true; is_var({y,_}) -> true; is_var({fr,_}) -> true; is_var({atom,A}) when is_atom(A) -> false; is_var(nil) -> false; is_var({integer,N}) when is_integer(N) -> false; is_var({float,F}) when is_float(F) -> false; is_var({literal,_Literal}) -> false. %%----------------------------------------------------------------------- %% Fixes the code for catches by adding some code. %%----------------------------------------------------------------------- fix_catches(Code) -> fix_catches(Code, gb_trees:empty()). %% We need to handle merged catch blocks, that is multiple 'catch' with %% only one 'catch_end', or multiple 'try' with one 'try_case'. (Catch %% and try can never be merged.) All occurrences of 'catch' or 'try' %% with a particular fail-to label are assumed to only occur before the %% corresponding 'catch_end'/'try_end' in the Beam code. fix_catches([{'catch',N,Lbl},ContLbl|Code], HandledCatchLbls) -> fix_catch('catch',Lbl,ContLbl,Code,HandledCatchLbls,{catch_end,N}); fix_catches([{'try',N,Lbl},ContLbl|Code], HandledCatchLbls) -> fix_catch('try',Lbl,ContLbl,Code,HandledCatchLbls,{try_case,N}); fix_catches([Instr|Code], HandledCatchLbls) -> [Instr|fix_catches(Code, HandledCatchLbls)]; fix_catches([], _HandledCatchLbls) -> []. fix_catch(Type, Lbl, ContLbl, Code, HandledCatchLbls, Instr) -> TLbl = {Type, Lbl}, case gb_trees:lookup(TLbl, HandledCatchLbls) of {value, Catch} when is_integer(Catch) -> NewCode = fix_catches(Code, HandledCatchLbls), Cont = hipe_icode:label_name(ContLbl), [hipe_icode:mk_begin_try(Catch,Cont),ContLbl | NewCode]; none -> OldCatch = map_label(Lbl), OldCatchLbl = hipe_icode:mk_label(OldCatch), {CodeToCatch,RestOfCode} = split_code(Code,OldCatchLbl,Instr), NewCatchLbl = mk_label(new), NewCatch = hipe_icode:label_name(NewCatchLbl), %% The rest of the code cannot contain catches with the same label. RestOfCode1 = fix_catches(RestOfCode, HandledCatchLbls), %% The catched code *can* contain more catches with the same label. NewHandledCatchLbls = gb_trees:insert(TLbl, NewCatch, HandledCatchLbls), CatchedCode = fix_catches(CodeToCatch, NewHandledCatchLbls), %% The variables which will get the tag, value, and trace. Vars = [mk_var({r,0}), mk_var({x,1}), mk_var({x,2})], Cont = hipe_icode:label_name(ContLbl), [hipe_icode:mk_begin_try(NewCatch,Cont), ContLbl] ++ CatchedCode ++ [mk_label(new), % dummy label before the goto hipe_icode:mk_goto(OldCatch), % normal execution path NewCatchLbl, % exception handing enters here hipe_icode:mk_begin_handler(Vars)] ++ catch_handler(Type, Vars, OldCatchLbl) ++ RestOfCode1 % back to normal execution end. catch_handler('try', _Vars, OldCatchLbl) -> %% A try just falls through to the old fail-to label which marked the %% start of the try_case block. All variables are set up as expected. [OldCatchLbl]; catch_handler('catch', [TagVar,ValueVar,TraceVar], OldCatchLbl) -> %% This basically implements a catch as a try-expression. We must jump %% to the given end label afterwards so we don't pass through both the %% begin_handler and the end_try. ContLbl = mk_label(new), Cont = hipe_icode:label_name(ContLbl), ThrowLbl = mk_label(new), NoThrowLbl = mk_label(new), ExitLbl = mk_label(new), ErrorLbl = mk_label(new), Dst = mk_var({r,0}), [hipe_icode:mk_if('=:=', [TagVar, hipe_icode:mk_const('throw')], hipe_icode:label_name(ThrowLbl), hipe_icode:label_name(NoThrowLbl)), ThrowLbl, hipe_icode:mk_move(Dst, ValueVar), hipe_icode:mk_goto(Cont), NoThrowLbl, hipe_icode:mk_if('=:=', [TagVar, hipe_icode:mk_const('exit')], hipe_icode:label_name(ExitLbl), hipe_icode:label_name(ErrorLbl)), ExitLbl, hipe_icode:mk_primop([Dst],mktuple,[hipe_icode:mk_const('EXIT'), ValueVar]), hipe_icode:mk_goto(Cont), ErrorLbl, %% We use the trace variable to hold the symbolic trace. Its previous %% value is just that in p->ftrace, so get_stacktrace() works fine. hipe_icode:mk_call([TraceVar],erlang,get_stacktrace,[],remote), hipe_icode:mk_primop([ValueVar],mktuple, [ValueVar, TraceVar]), hipe_icode:mk_goto(hipe_icode:label_name(ExitLbl)), OldCatchLbl, % normal execution paths must go through end_try hipe_icode:mk_end_try(), hipe_icode:mk_goto(Cont), ContLbl]. %% Note that it is the fail-to label that is the important thing, but %% for 'catch' we want to make sure that the label is followed by the %% 'catch_end' instruction - if it is not, we might have a real problem. %% Checking that a 'try' label is followed by 'try_case' is not as %% important, but we get that as a bonus. split_code([First|Code], Label, Instr) -> split_code(Code, Label, Instr, First, []). split_code([Instr|Code], Label, Instr, Prev, As) when Prev =:= Label -> split_code_final(Code, As); % drop both label and instruction split_code([{icode_end_try}|_]=Code, Label, {try_case,_}, Prev, As) when Prev =:= Label -> %% The try_case has been replaced with try_end as an optimization. %% Keep this instruction, since it might be the only try_end instruction %% for this try/catch block. split_code_final(Code, As); % drop label split_code([Other|_Code], Label, Instr, Prev, _As) when Prev =:= Label -> ?EXIT({missing_instr_after_label, Label, Instr, [Other, Prev | _As]}); split_code([Other|Code], Label, Instr, Prev, As) -> split_code(Code, Label, Instr, Other, [Prev|As]); split_code([], _Label, _Instr, Prev, As) -> split_code_final([], [Prev|As]). split_code_final(Code, As) -> {lists:reverse(As), Code}. %%----------------------------------------------------------------------- %% Fixes fallthroughs %%----------------------------------------------------------------------- fix_fallthroughs([]) -> []; fix_fallthroughs([I|Is]) -> fix_fallthroughs(Is, I, []). fix_fallthroughs([I1|Is], I0, Acc) -> case hipe_icode:is_label(I1) of false -> fix_fallthroughs(Is, I1, [I0 | Acc]); true -> case hipe_icode:is_branch(I0) of true -> fix_fallthroughs(Is, I1, [I0 | Acc]); false -> %% non-branch before label - insert a goto Goto = hipe_icode:mk_goto(hipe_icode:label_name(I1)), fix_fallthroughs(Is, I1, [Goto, I0 | Acc]) end end; fix_fallthroughs([], I, Acc) -> lists:reverse([I | Acc]). %%----------------------------------------------------------------------- %% Removes the code between a fail instruction and the closest following %% label. %%----------------------------------------------------------------------- -spec remove_dead_code(icode_instrs()) -> icode_instrs(). remove_dead_code([I|Is]) -> case I of #icode_fail{} -> [I|remove_dead_code(skip_to_label(Is))]; _ -> [I|remove_dead_code(Is)] end; remove_dead_code([]) -> []. %% returns the instructions from the closest label -spec skip_to_label(icode_instrs()) -> icode_instrs(). skip_to_label([I|Is] = Instrs) -> case I of #icode_label{} -> Instrs; _ -> skip_to_label(Is) end; skip_to_label([]) -> []. %%----------------------------------------------------------------------- %% This needs to be extended in case new architectures are added. %%----------------------------------------------------------------------- resolve_native_endianess(Flags) -> case {Flags band 16#10, hipe_rtl_arch:endianess()} of {16#10, big} -> Flags band 5; {16#10, little} -> (Flags bor 2) band 7; _ -> Flags band 7 end. %%----------------------------------------------------------------------- %% Potentially useful for debugging. %%----------------------------------------------------------------------- pp_beam(BeamCode, Options) -> case proplists:get_value(pp_beam, Options) of true -> pp(BeamCode); {file,FileName} -> {ok,File} = file:open(FileName, [write]), pp(File, BeamCode); _ -> %% includes "false" case ok end. pp(Code) -> pp(standard_io, Code). pp(Stream, []) -> case Stream of %% I am not sure whether this is necessary standard_io -> ok; _ -> ok = file:close(Stream) end; pp(Stream, [FunCode|FunCodes]) -> pp_mfa(Stream, FunCode), put_nl(Stream), pp(Stream, FunCodes). pp_mfa(Stream, FunCode) -> lists:foreach(fun(Instr) -> print_instr(Stream, Instr) end, FunCode). print_instr(Stream, {label,Lbl}) -> io:format(Stream, " label ~p:\n", [Lbl]); print_instr(Stream, Op) -> io:format(Stream, " ~p\n", [Op]). put_nl(Stream) -> io:format(Stream, "\n", []). %%----------------------------------------------------------------------- %% Handling of environments -- used to process local tail calls. %%----------------------------------------------------------------------- %% Construct an environment env__mk_env(M, F, A, Entry) -> #environment{mfa={M,F,A}, entry=Entry}. %% Get current MFA env__get_mfa(#environment{mfa=MFA}) -> MFA. %% Get entry point of the current function env__get_entry(#environment{entry=EP}) -> EP. %%-----------------------------------------------------------------------