summaryrefslogtreecommitdiff
path: root/lib/hipe/icode/hipe_beam_to_icode.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/hipe/icode/hipe_beam_to_icode.erl')
-rw-r--r--lib/hipe/icode/hipe_beam_to_icode.erl2494
1 files changed, 0 insertions, 2494 deletions
diff --git a/lib/hipe/icode/hipe_beam_to_icode.erl b/lib/hipe/icode/hipe_beam_to_icode.erl
deleted file mode 100644
index 97d50eb472..0000000000
--- a/lib/hipe/icode/hipe_beam_to_icode.erl
+++ /dev/null
@@ -1,2494 +0,0 @@
-%% -*- 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.
-%%--- put_tuple2 ---
-trans_fun([{put_tuple2,Reg,{list,Elements}}|Instructions], Env) ->
- Dest = [mk_var(Reg)],
- {Moves,Vars,Env2} = trans_elements(Elements, [], [], Env),
- Src = lists:reverse(Vars),
- Primop = hipe_icode:mk_primop(Dest, mktuple, Src),
- Moves ++ [Primop | trans_fun(Instructions, Env2)];
-%%--- 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)];
-%%--- make_fun2 ---
-trans_fun([{make_fun2,MFA,Index,Magic,FreeVarNum}|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) ->
- KVPs1 = overwrite_map_last(Map, KVPs),
- {MapMove, MapVar, Env1} = mk_move_and_var(Map, Env),
- {TestInstructions, GetInstructions, Env2} =
- trans_map_query(MapVar, map_label(Lbl), Env1, KVPs1),
- [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)];
-%%--------------------------------------------------------------------
-%% New binary matching added in OTP 22.
-%%--------------------------------------------------------------------
-%%--- bs_get_tail ---
-trans_fun([{bs_get_tail=Name,_,_,_}|_Instructions], _Env) ->
- nyi(Name);
-%%--- bs_start_match3 ---
-trans_fun([{bs_start_match3=Name,_,_,_,_}|_Instructions], _Env) ->
- nyi(Name);
-%%--- bs_get_position ---
-trans_fun([{bs_get_position=Name,_,_,_}|_Instructions], _Env) ->
- nyi(Name);
-%%--- bs_set_position ---
-trans_fun([{bs_set_position=Name,_,_}|_Instructions], _Env) ->
- nyi(Name);
-%%--------------------------------------------------------------------
-%% New instructions added in OTP 23.
-%%--------------------------------------------------------------------
-%%--- swap ---
-trans_fun([{swap,Reg1,Reg2}|Instructions], Env) ->
- Var1 = mk_var(Reg1),
- Var2 = mk_var(Reg2),
- Temp = mk_var(new),
- [hipe_icode:mk_move(Temp, Var1),
- hipe_icode:mk_move(Var1, Var2),
- hipe_icode:mk_move(Var2, Temp) | trans_fun(Instructions, Env)];
-%%--------------------------------------------------------------------
-%%--- ERROR HANDLING ---
-%%--------------------------------------------------------------------
-trans_fun([X|_], _) ->
- ?EXIT({'trans_fun/2',X});
-trans_fun([], _) ->
- [].
-
-nyi(Name) ->
- throw({unimplemented_instruction,Name}).
-
-%%--------------------------------------------------------------------
-%% 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}.
-
-
-%%
-%% Makes sure that if a get_map_elements instruction will overwrite
-%% the map source, it will be done last.
-%%
-overwrite_map_last(Map, KVPs) ->
- overwrite_map_last2(Map, KVPs, []).
-
-overwrite_map_last2(Map, [Key,Map|KVPs], _Last) ->
- overwrite_map_last2(Map, KVPs, [Key,Map]);
-overwrite_map_last2(Map, [Key,Val|KVPs], Last) ->
- [Key,Val|overwrite_map_last2(Map, KVPs, Last)];
-overwrite_map_last2(_Map, [], Last) ->
- Last.
-
-%%
-%% 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}.
-
-trans_elements([X|Code], Vars, Moves, Env) ->
- case type(X) of
- var ->
- Var = mk_var(X),
- trans_elements(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_elements(Code, [Var|Vars], [Move|Moves], Env)
- end;
-trans_elements([], Vars, Moves, Env) ->
- {Moves, 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([{make_fun2,{_M,_F,A}=MFA,_Index,_Magic,FreeVarNum}|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) ->
- ClosureInfo = find_closure_info(ModuleCode),
- {ModuleCode, ClosureInfo}.
-
-%%-----------------------------------------------------------------------
-
-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) ->
- nyi(unsafe_catch);
- 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.
- hipe_icode:mk_primop([TraceVar],build_stacktrace,[TraceVar]),
- 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.
-
-%%-----------------------------------------------------------------------