%%% -*- erlang-indent-level: 2 -*- %%% %%% %CopyrightBegin% %%% %%% Copyright Ericsson AB 2001-2009. All Rights Reserved. %%% %%% The contents of this file are subject to the Erlang Public License, %%% Version 1.1, (the "License"); you may not use this file except in %%% compliance with the License. You should have received a copy of the %%% Erlang Public License along with this software. If not, it can be %%% retrieved online at http://www.erlang.org/. %%% %%% Software distributed under the License is distributed on an "AS IS" %%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See %%% the License for the specific language governing rights and limitations %%% under the License. %%% %%% %CopyrightEnd% %%% %%% %%% Translate 3-address RTL code to 2-address pseudo-x86 code. -ifdef(HIPE_AMD64). -define(HIPE_RTL_TO_X86, hipe_rtl_to_amd64). -define(HIPE_X86_REGISTERS, hipe_amd64_registers). -define(ECX, rcx). -define(EAX, rax). -else. -define(HIPE_RTL_TO_X86, hipe_rtl_to_x86). -define(HIPE_X86_REGISTERS, hipe_x86_registers). -define(ECX, ecx). -define(EAX, eax). -endif. -module(?HIPE_RTL_TO_X86). -export([translate/1]). -include("../rtl/hipe_rtl.hrl"). translate(RTL) -> % RTL function -> x86 defun hipe_gensym:init(x86), hipe_gensym:set_var(x86, ?HIPE_X86_REGISTERS:first_virtual()), hipe_gensym:set_label(x86, hipe_gensym:get_label(rtl)), Map0 = vmap_empty(), {Formals, Map1} = conv_formals(hipe_rtl:rtl_params(RTL), Map0), OldData = hipe_rtl:rtl_data(RTL), {Code0, NewData} = conv_insn_list(hipe_rtl:rtl_code(RTL), Map1, OldData), {RegFormals,_} = split_args(Formals), Code = case RegFormals of [] -> Code0; _ -> [hipe_x86:mk_label(hipe_gensym:get_next_label(x86)) | move_formals(RegFormals, Code0)] end, IsClosure = hipe_rtl:rtl_is_closure(RTL), IsLeaf = hipe_rtl:rtl_is_leaf(RTL), hipe_x86:mk_defun(hipe_rtl:rtl_fun(RTL), Formals, IsClosure, IsLeaf, Code, NewData, [], []). conv_insn_list([H|T], Map, Data) -> {NewH, NewMap, NewData1} = conv_insn(H, Map, Data), %% io:format("~w \n ==>\n ~w\n- - - - - - - - -\n",[H,NewH]), {NewT, NewData2} = conv_insn_list(T, NewMap, NewData1), {NewH ++ NewT, NewData2}; conv_insn_list([], _, Data) -> {[], Data}. conv_insn(I, Map, Data) -> case I of #alu{} -> %% dst = src1 binop src2 BinOp = conv_binop(hipe_rtl:alu_op(I)), {Dst, Map0} = conv_dst(hipe_rtl:alu_dst(I), Map), {FixSrc1, Src1, Map1} = conv_src(hipe_rtl:alu_src1(I), Map0), {FixSrc2, Src2, Map2} = conv_src(hipe_rtl:alu_src2(I), Map1), I2 = case hipe_rtl:is_shift_op(hipe_rtl:alu_op(I)) of true -> conv_shift(Dst, Src1, BinOp, Src2); false -> conv_alu(Dst, Src1, BinOp, Src2, []) end, {FixSrc1++FixSrc2++I2, Map2, Data}; #alub{} -> %% dst = src1 op src2; if COND goto label BinOp = conv_binop(hipe_rtl:alub_op(I)), {Dst, Map0} = conv_dst(hipe_rtl:alub_dst(I), Map), {FixSrc1, Src1, Map1} = conv_src(hipe_rtl:alub_src1(I), Map0), {FixSrc2, Src2, Map2} = conv_src(hipe_rtl:alub_src2(I), Map1), Cc = conv_cond(hipe_rtl:alub_cond(I)), I1 = [hipe_x86:mk_pseudo_jcc(Cc, hipe_rtl:alub_true_label(I), hipe_rtl:alub_false_label(I), hipe_rtl:alub_pred(I))], I2 = conv_alu(Dst, Src1, BinOp, Src2, I1), {FixSrc1++FixSrc2++I2, Map2, Data}; #branch{} -> %% = src1 - src2; if COND goto label {FixSrc1, Src1, Map0} = conv_src(hipe_rtl:branch_src1(I), Map), {FixSrc2, Src2, Map1} = conv_src(hipe_rtl:branch_src2(I), Map0), Cc = conv_cond(hipe_rtl:branch_cond(I)), I2 = conv_branch(Src1, Cc, Src2, hipe_rtl:branch_true_label(I), hipe_rtl:branch_false_label(I), hipe_rtl:branch_pred(I)), {FixSrc1++FixSrc2++I2, Map1, Data}; #call{} -> %% push %% ... %% push %% eax := call ; if exn goto else goto Next %% Next: %% := eax %% goto {FixArgs, Args, Map0} = conv_src_list(hipe_rtl:call_arglist(I), Map), {Dsts, Map1} = conv_dst_list(hipe_rtl:call_dstlist(I), Map0), {Fun, Map2} = conv_fun(hipe_rtl:call_fun(I), Map1), I2 = conv_call(Dsts, Fun, Args, hipe_rtl:call_continuation(I), hipe_rtl:call_fail(I), hipe_rtl:call_type(I)), %% XXX Fixme: this ++ is probably inefficient. {FixArgs++I2, Map2, Data}; #comment{} -> I2 = [hipe_x86:mk_comment(hipe_rtl:comment_text(I))], {I2, Map, Data}; #enter{} -> {FixArgs, Args, Map0} = conv_src_list(hipe_rtl:enter_arglist(I), Map), {Fun, Map1} = conv_fun(hipe_rtl:enter_fun(I), Map0), I2 = conv_tailcall(Fun, Args, hipe_rtl:enter_type(I)), {FixArgs++I2, Map1, Data}; #goto{} -> I2 = [hipe_x86:mk_jmp_label(hipe_rtl:goto_label(I))], {I2, Map, Data}; #label{} -> I2 = [hipe_x86:mk_label(hipe_rtl:label_name(I))], {I2, Map, Data}; #load{} -> {Dst, Map0} = conv_dst(hipe_rtl:load_dst(I), Map), {FixSrc, Src, Map1} = conv_src(hipe_rtl:load_src(I), Map0), {FixOff, Off, Map2} = conv_src(hipe_rtl:load_offset(I), Map1), I2 = case {hipe_rtl:load_size(I), hipe_rtl:load_sign(I)} of {byte, signed} -> [hipe_x86:mk_movsx(hipe_x86:mk_mem(Src, Off, 'byte'), Dst)]; {byte, unsigned} -> [hipe_x86:mk_movzx(hipe_x86:mk_mem(Src, Off, 'byte'), Dst)]; {int16, signed} -> [hipe_x86:mk_movsx(hipe_x86:mk_mem(Src, Off, 'int16'), Dst)]; {int16, unsigned} -> [hipe_x86:mk_movzx(hipe_x86:mk_mem(Src, Off, 'int16'), Dst)]; {LoadSize, LoadSign} -> mk_load(LoadSize, LoadSign, Src, Off, Dst) end, {FixSrc++FixOff++I2, Map2, Data}; #load_address{} -> {Dst, Map0} = conv_dst(hipe_rtl:load_address_dst(I), Map), Addr = hipe_rtl:load_address_addr(I), Type = hipe_rtl:load_address_type(I), Src = hipe_x86:mk_imm_from_addr(Addr, Type), I2 = mk_load_address(Type, Src, Dst), {I2, Map0, Data}; #load_atom{} -> {Dst, Map0} = conv_dst(hipe_rtl:load_atom_dst(I), Map), Src = hipe_x86:mk_imm_from_atom(hipe_rtl:load_atom_atom(I)), I2 = [hipe_x86:mk_move(Src, Dst)], {I2, Map0, Data}; #move{} -> {Dst, Map0} = conv_dst(hipe_rtl:move_dst(I), Map), {FixSrc, Src, Map1} = conv_src(hipe_rtl:move_src(I), Map0), I2 = [hipe_x86:mk_move(Src, Dst)], {FixSrc++I2, Map1, Data}; #return{} -> {FixArgs, Args, Map0} = conv_src_list(hipe_rtl:return_varlist(I), Map), %% frame will fill in npop later, hence the "mk_ret(-1)" I2 = move_retvals(Args, [hipe_x86:mk_ret(-1)]), {FixArgs++I2, Map0, Data}; #store{} -> {Ptr, Map0} = conv_dst(hipe_rtl:store_base(I), Map), {FixSrc, Src, Map1} = conv_src(hipe_rtl:store_src(I), Map0), {FixOff, Off, Map2} = conv_src(hipe_rtl:store_offset(I), Map1), I2 = mk_store(hipe_rtl:store_size(I), Src, Ptr, Off), {FixSrc++FixOff++I2, Map2, Data}; #switch{} -> % this one also updates Data :-( %% from hipe_rtl2sparc, but we use a hairy addressing mode %% instead of doing the arithmetic manually Labels = hipe_rtl:switch_labels(I), LMap = [{label,L} || L <- Labels], {NewData, JTabLab} = case hipe_rtl:switch_sort_order(I) of [] -> hipe_consttab:insert_block(Data, word, LMap); SortOrder -> hipe_consttab:insert_sorted_block( Data, word, LMap, SortOrder) end, %% no immediates allowed here {Index, Map1} = conv_dst(hipe_rtl:switch_src(I), Map), I2 = mk_jmp_switch(Index, JTabLab, Labels), {I2, Map1, NewData}; #fload{} -> {Dst, Map0} = conv_dst(hipe_rtl:fload_dst(I), Map), {[], Src, Map1} = conv_src(hipe_rtl:fload_src(I), Map0), {[], Off, Map2} = conv_src(hipe_rtl:fload_offset(I), Map1), I2 = [hipe_x86:mk_fmove(hipe_x86:mk_mem(Src, Off, 'double'),Dst)], {I2, Map2, Data}; #fstore{} -> {Dst, Map0} = conv_dst(hipe_rtl:fstore_base(I), Map), {[], Src, Map1} = conv_src(hipe_rtl:fstore_src(I), Map0), {[], Off, Map2} = conv_src(hipe_rtl:fstore_offset(I), Map1), I2 = [hipe_x86:mk_fmove(Src, hipe_x86:mk_mem(Dst, Off, 'double'))], {I2, Map2, Data}; #fp{} -> {Dst, Map0} = conv_dst(hipe_rtl:fp_dst(I), Map), {[], Src1, Map1} = conv_src(hipe_rtl:fp_src1(I), Map0), {[], Src2, Map2} = conv_src(hipe_rtl:fp_src2(I), Map1), FpBinOp = conv_fp_binop(hipe_rtl:fp_op(I)), I2 = conv_fp_binary(Dst, Src1, FpBinOp, Src2), {I2, Map2, Data}; #fp_unop{} -> {Dst, Map0} = conv_dst(hipe_rtl:fp_unop_dst(I), Map), {[], Src, Map1} = conv_src(hipe_rtl:fp_unop_src(I), Map0), FpUnOp = conv_fp_unop(hipe_rtl:fp_unop_op(I)), I2 = conv_fp_unary(Dst, Src, FpUnOp), {I2, Map1, Data}; #fmove{} -> {Dst, Map0} = conv_dst(hipe_rtl:fmove_dst(I), Map), {[], Src, Map1} = conv_src(hipe_rtl:fmove_src(I), Map0), I2 = [hipe_x86:mk_fmove(Src, Dst)], {I2, Map1, Data}; #fconv{} -> {Dst, Map0} = conv_dst(hipe_rtl:fconv_dst(I), Map), {[], Src, Map1} = conv_src(hipe_rtl:fconv_src(I), Map0), I2 = [hipe_x86:mk_fmove(Src, Dst)], {I2, Map1, Data}; X -> %% gctest?? %% jmp, jmp_link, jsr, esr, multimove, %% stackneed, pop_frame, restore_frame, save_frame throw({?MODULE, {"unknown RTL instruction", X}}) end. %%% Finalise the conversion of a 3-address ALU operation, taking %%% care to not introduce more temps and moves than necessary. conv_alu(Dst, Src1, 'imul', Src2, Tail) -> mk_imul(Src1, Src2, Dst, Tail); conv_alu(Dst, Src1, BinOp, Src2, Tail) -> case same_opnd(Dst, Src1) of true -> % x = x op y [hipe_x86:mk_alu(BinOp, Src2, Dst) | Tail]; % x op= y false -> % z = x op y, where z != x case same_opnd(Dst, Src2) of false -> % z = x op y, where z != x && z != y [hipe_x86:mk_move(Src1, Dst), % z = x hipe_x86:mk_alu(BinOp, Src2, Dst) | Tail]; % z op= y true -> % y = x op y, where y != x case binop_commutes(BinOp) of true -> % y = y op x [hipe_x86:mk_alu(BinOp, Src1, Dst) | Tail]; % y op= x false -> % y = x op y, where op doesn't commute Tmp = clone_dst(Dst), [hipe_x86:mk_move(Src1, Tmp), % t = x hipe_x86:mk_alu(BinOp, Src2, Tmp), % t op= y hipe_x86:mk_move(Tmp, Dst) | Tail] % y = t end end end. mk_imul(Src1, Src2, Dst, Tail) -> case hipe_x86:is_imm(Src1) of true -> case hipe_x86:is_imm(Src2) of true -> mk_imul_iit(Src1, Src2, Dst, Tail); _ -> mk_imul_itt(Src1, Src2, Dst, Tail) end; _ -> case hipe_x86:is_imm(Src2) of true -> mk_imul_itt(Src2, Src1, Dst, Tail); _ -> mk_imul_ttt(Src1, Src2, Dst, Tail) end end. mk_imul_iit(Src1, Src2, Dst, Tail) -> io:format("~w: RTL mul with two immediates\n", [?MODULE]), Tmp2 = new_untagged_temp(), [hipe_x86:mk_move(Src2, Tmp2) | mk_imul_itt(Src1, Tmp2, Dst, Tail)]. mk_imul_itt(Src1, Src2, Dst, Tail) -> [hipe_x86:mk_imul(Src1, Src2, Dst) | Tail]. mk_imul_ttt(Src1, Src2, Dst, Tail) -> case same_opnd(Dst, Src1) of true -> [hipe_x86:mk_imul([], Src2, Dst) | Tail]; false -> case same_opnd(Dst, Src2) of true -> [hipe_x86:mk_imul([], Src1, Dst) | Tail]; false -> [hipe_x86:mk_move(Src1, Dst), hipe_x86:mk_imul([], Src2, Dst) | Tail] end end. conv_shift(Dst, Src1, BinOp, Src2) -> {NewSrc2,I1} = case hipe_x86:is_imm(Src2) of true -> {Src2, []}; false -> NewSrc = hipe_x86:mk_temp(?HIPE_X86_REGISTERS:?ECX(), 'untagged'), {NewSrc, [hipe_x86:mk_move(Src2, NewSrc)]} end, I2 = case same_opnd(Dst, Src1) of true -> % x = x op y [hipe_x86:mk_shift(BinOp, NewSrc2, Dst)]; % x op= y false -> % z = x op y, where z != x case same_opnd(Dst, Src2) of false -> % z = x op y, where z != x && z != y [hipe_x86:mk_move(Src1, Dst), % z = x hipe_x86:mk_shift(BinOp, NewSrc2, Dst)];% z op= y true -> % y = x op y, no shift op commutes Tmp = clone_dst(Dst), [hipe_x86:mk_move(Src1, Tmp), % t = x hipe_x86:mk_shift(BinOp, NewSrc2, Tmp), % t op= y hipe_x86:mk_move(Tmp, Dst)] % y = t end end, I1 ++ I2. %%% Finalise the conversion of a conditional branch operation, taking %%% care to not introduce more temps and moves than necessary. conv_branch(Src1, Cc, Src2, TrueLab, FalseLab, Pred) -> case hipe_x86:is_imm(Src1) of false -> mk_branch(Src1, Cc, Src2, TrueLab, FalseLab, Pred); true -> case hipe_x86:is_imm(Src2) of false -> NewCc = commute_cc(Cc), mk_branch(Src2, NewCc, Src1, TrueLab, FalseLab, Pred); true -> %% two immediates, let the optimiser clean it up Tmp = new_untagged_temp(), [hipe_x86:mk_move(Src1, Tmp) | mk_branch(Tmp, Cc, Src2, TrueLab, FalseLab, Pred)] end end. mk_branch(Src1, Cc, Src2, TrueLab, FalseLab, Pred) -> %% PRE: not(is_imm(Src1)) [hipe_x86:mk_cmp(Src2, Src1), hipe_x86:mk_pseudo_jcc(Cc, TrueLab, FalseLab, Pred)]. %%% Convert an RTL ALU or ALUB binary operator. conv_binop(BinOp) -> case BinOp of 'add' -> 'add'; 'sub' -> 'sub'; 'or' -> 'or'; 'and' -> 'and'; 'xor' -> 'xor'; 'sll' -> 'shl'; 'srl' -> 'shr'; 'sra' -> 'sar'; 'mul' -> 'imul'; %% andnot ??? _ -> exit({?MODULE, {"unknown binop", BinOp}}) end. binop_commutes(BinOp) -> case BinOp of 'add' -> true; 'or' -> true; 'and' -> true; 'xor' -> true; _ -> false end. %%% Convert an RTL conditional operator. conv_cond(Cond) -> case Cond of eq -> 'e'; ne -> 'ne'; gt -> 'g'; gtu -> 'a'; ge -> 'ge'; geu -> 'ae'; lt -> 'l'; ltu -> 'b'; le -> 'le'; leu -> 'be'; overflow -> 'o'; not_overflow -> 'no'; _ -> exit({?MODULE, {"unknown rtl cond", Cond}}) end. commute_cc(Cc) -> % if x Cc y, then y commute_cc(Cc) x case Cc of 'e' -> 'e'; % ==, == 'ne' -> 'ne'; % !=, != 'g' -> 'l'; % >, < 'a' -> 'b'; % >u, 'le'; % >=, <= 'ae' -> 'be'; % >=u, <=u 'l' -> 'g'; % <, > 'b' -> 'a'; % u 'le' -> 'ge'; % <=, >= 'be' -> 'ae'; % <=u, >=u %% overflow/not_overflow: n/a _ -> exit({?MODULE, {"unknown cc", Cc}}) end. %%% Test if Dst and Src are the same operand. same_opnd(Dst, Src) -> Dst =:= Src. %%% Finalise the conversion of a tailcall instruction. conv_tailcall(Fun, Args, Linkage) -> Arity = length(Args), {RegArgs,StkArgs} = split_args(Args), move_actuals(RegArgs, [hipe_x86:mk_pseudo_tailcall_prepare(), hipe_x86:mk_pseudo_tailcall(Fun, Arity, StkArgs, Linkage)]). split_args(Args) -> split_args(0, ?HIPE_X86_REGISTERS:nr_args(), Args, []). split_args(I, N, [Arg|Args], RegArgs) when I < N -> Reg = ?HIPE_X86_REGISTERS:arg(I), Temp = hipe_x86:mk_temp(Reg, 'tagged'), split_args(I+1, N, Args, [{Arg,Temp}|RegArgs]); split_args(_, _, StkArgs, RegArgs) -> {RegArgs, StkArgs}. move_actuals([], Rest) -> Rest; move_actuals([{Src,Dst}|Actuals], Rest) -> move_actuals(Actuals, [hipe_x86:mk_move(Src, Dst) | Rest]). move_formals([], Rest) -> Rest; move_formals([{Dst,Src}|Formals], Rest) -> move_formals(Formals, [hipe_x86:mk_move(Src, Dst) | Rest]). %%% Finalise the conversion of a call instruction. conv_call(Dsts, Fun, Args, ContLab, ExnLab, Linkage) -> case hipe_x86:is_prim(Fun) of true -> conv_primop_call(Dsts, Fun, Args, ContLab, ExnLab, Linkage); false -> conv_general_call(Dsts, Fun, Args, ContLab, ExnLab, Linkage) end. conv_primop_call(Dsts, Prim, Args, ContLab, ExnLab, Linkage) -> case hipe_x86:prim_prim(Prim) of 'fwait' -> conv_fwait_call(Dsts, Args, ContLab, ExnLab, Linkage); _ -> conv_general_call(Dsts, Prim, Args, ContLab, ExnLab, Linkage) end. conv_fwait_call([], [], [], [], not_remote) -> [hipe_x86:mk_fp_unop('fwait', [])]. conv_general_call(Dsts, Fun, Args, ContLab, ExnLab, Linkage) -> %% The backend does not support pseudo_calls without a %% continuation label, so we make sure each call has one. {RealContLab, Tail} = case do_call_results(Dsts) of [] -> %% Avoid consing up a dummy basic block if the moves list %% is empty, as is typical for calls to suspend/0. %% This should be subsumed by a general "optimise the CFG" %% module, and could probably be removed. case ContLab of [] -> NewContLab = hipe_gensym:get_next_label(x86), {NewContLab, [hipe_x86:mk_label(NewContLab)]}; _ -> {ContLab, []} end; Moves -> %% Change the call to continue at a new basic block. %% In this block move the result registers to the Dsts, %% then continue at the call's original continuation. %% %% This should be fixed to propagate "fallthrough calls" %% When the rest of the backend supports them. NewContLab = hipe_gensym:get_next_label(x86), case ContLab of [] -> %% This is just a fallthrough %% No jump back after the moves. {NewContLab, [hipe_x86:mk_label(NewContLab) | Moves]}; _ -> %% The call has a continuation %% jump to it. {NewContLab, [hipe_x86:mk_label(NewContLab) | Moves ++ [hipe_x86:mk_jmp_label(ContLab)]]} end end, SDesc = hipe_x86:mk_sdesc(ExnLab, 0, length(Args), {}), CallInsn = hipe_x86:mk_pseudo_call(Fun, SDesc, RealContLab, Linkage), {RegArgs,StkArgs} = split_args(Args), do_push_args(StkArgs, move_actuals(RegArgs, [CallInsn | Tail])). do_push_args([Arg|Args], Tail) -> [hipe_x86:mk_push(Arg) | do_push_args(Args, Tail)]; do_push_args([], Tail) -> Tail. %%% Move return values from the return value registers. do_call_results(DstList) -> do_call_results(DstList, 0, []). do_call_results([Dst|DstList], I, Rest) -> Src = hipe_x86:mk_temp(?HIPE_X86_REGISTERS:ret(I), 'tagged'), Move = hipe_x86:mk_move(Src, Dst), do_call_results(DstList, I+1, [Move|Rest]); do_call_results([], _, Insns) -> Insns. %%% Move return values to the return value registers. move_retvals(SrcLst, Rest) -> move_retvals(SrcLst, 0, Rest). move_retvals([Src|SrcLst], I, Rest) -> Dst = hipe_x86:mk_temp(?HIPE_X86_REGISTERS:ret(I), 'tagged'), Move = hipe_x86:mk_move(Src, Dst), move_retvals(SrcLst, I+1, [Move|Rest]); move_retvals([], _, Insns) -> Insns. %%% Convert a 'fun' operand (MFA, prim, or temp) conv_fun(Fun, Map) -> case hipe_rtl:is_var(Fun) of true -> conv_dst(Fun, Map); false -> case hipe_rtl:is_reg(Fun) of true -> conv_dst(Fun, Map); false -> case Fun of Prim when is_atom(Prim) -> {hipe_x86:mk_prim(Prim), Map}; {M,F,A} when is_atom(M), is_atom(F), is_integer(A) -> {hipe_x86:mk_mfa(M,F,A), Map}; _ -> exit({?MODULE,conv_fun,Fun}) end end end. %%% Convert an RTL source operand (imm/var/reg). conv_src(Opnd, Map) -> case hipe_rtl:is_imm(Opnd) of true -> conv_imm(Opnd, Map); false -> {NewOpnd,NewMap} = conv_dst(Opnd, Map), {[], NewOpnd, NewMap} end. -ifdef(HIPE_AMD64). conv_imm(Opnd, Map) -> ImmVal = hipe_rtl:imm_value(Opnd), case is_imm64(ImmVal) of true -> Temp = hipe_x86:mk_new_temp('untagged'), {[hipe_x86:mk_move64(hipe_x86:mk_imm(ImmVal), Temp)], Temp, Map}; false -> {[], hipe_x86:mk_imm(ImmVal), Map} end. is_imm64(Value) when is_integer(Value) -> (Value < -(1 bsl (32 - 1))) or (Value > (1 bsl (32 - 1)) - 1); is_imm64({_,atom}) -> false; % Atoms are 32 bits. is_imm64({_,c_const}) -> false; % c_consts are 32 bits. is_imm64({_,_}) -> true . % Other relocs are 64 bits. -else. conv_imm(Opnd, Map) -> {[], hipe_x86:mk_imm(hipe_rtl:imm_value(Opnd)), Map}. -endif. conv_src_list([O|Os], Map) -> {NewInstr, V, Map1} = conv_src(O, Map), {Instrs, Vs, Map2} = conv_src_list(Os, Map1), {Instrs++NewInstr, [V|Vs], Map2}; conv_src_list([], Map) -> {[], [], Map}. %%% Convert an RTL destination operand (var/reg). conv_dst(Opnd, Map) -> {Name, Type} = case hipe_rtl:is_var(Opnd) of true -> {hipe_rtl:var_index(Opnd), 'tagged'}; false -> case hipe_rtl:is_fpreg(Opnd) of true -> {hipe_rtl:fpreg_index(Opnd), 'double'}; false -> {hipe_rtl:reg_index(Opnd), 'untagged'} end end, case ?HIPE_X86_REGISTERS:is_precoloured(Name) of true -> case ?HIPE_X86_REGISTERS:proc_offset(Name) of false -> {hipe_x86:mk_temp(Name, Type), Map}; Offset -> Preg = ?HIPE_X86_REGISTERS:proc_pointer(), Pbase = hipe_x86:mk_temp(Preg, 'untagged'), Poff = hipe_x86:mk_imm(Offset), {hipe_x86:mk_mem(Pbase, Poff, Type), Map} end; false -> case vmap_lookup(Map, Opnd) of {value, NewTemp} -> {NewTemp, Map}; _ -> NewTemp = hipe_x86:mk_new_temp(Type), {NewTemp, vmap_bind(Map, Opnd, NewTemp)} end end. conv_dst_list([O|Os], Map) -> {Dst, Map1} = conv_dst(O, Map), {Dsts, Map2} = conv_dst_list(Os, Map1), {[Dst|Dsts], Map2}; conv_dst_list([], Map) -> {[], Map}. conv_formals(Os, Map) -> conv_formals(?HIPE_X86_REGISTERS:nr_args(), Os, Map, []). conv_formals(N, [O|Os], Map, Res) -> Type = case hipe_rtl:is_var(O) of true -> 'tagged'; false ->'untagged' end, Dst = if N > 0 -> hipe_x86:mk_new_temp(Type); % allocatable true -> hipe_x86:mk_new_nonallocatable_temp(Type) end, Map1 = vmap_bind(Map, O, Dst), conv_formals(N-1, Os, Map1, [Dst|Res]); conv_formals(_, [], Map, Res) -> {lists:reverse(Res), Map}. %%% typeof_src -- what's src's type? typeof_src(Src) -> case hipe_x86:is_imm(Src) of true -> 'untagged'; _ -> typeof_dst(Src) end. %%% typeof_dst -- what's dst's type? typeof_dst(Dst) -> case hipe_x86:is_temp(Dst) of true -> hipe_x86:temp_type(Dst); _ -> hipe_x86:mem_type(Dst) end. %%% clone_dst -- conjure up a scratch reg with same type as dst clone_dst(Dst) -> hipe_x86:mk_new_temp(typeof_dst(Dst)). %%% new_untagged_temp -- conjure up an untagged scratch reg new_untagged_temp() -> hipe_x86:mk_new_temp('untagged'). %%% Map from RTL var/reg operands to x86 temps. vmap_empty() -> gb_trees:empty(). vmap_lookup(Map, Key) -> gb_trees:lookup(Key, Map). vmap_bind(Map, Key, Val) -> gb_trees:insert(Key, Val, Map). %%% Finalise the conversion of a 2-address FP operation. conv_fp_unary(Dst, Src, FpUnOp) -> case same_opnd(Dst, Src) of true -> [hipe_x86:mk_fp_unop(FpUnOp, Dst)]; _ -> [hipe_x86:mk_fmove(Src, Dst), hipe_x86:mk_fp_unop(FpUnOp, Dst)] end. conv_fp_unop(RtlFpUnOp) -> case RtlFpUnOp of 'fchs' -> 'fchs' end. %%% Finalise the conversion of a 3-address FP operation. conv_fp_binary(Dst, Src1, FpBinOp, Src2) -> case same_opnd(Dst, Src1) of true -> % x = x op y [hipe_x86:mk_fp_binop(FpBinOp, Src2, Dst)]; % x op= y false -> % z = x op y, where z != x case same_opnd(Dst, Src2) of false -> % z = x op y, where z != x && z != y [hipe_x86:mk_fmove(Src1, Dst), % z = x hipe_x86:mk_fp_binop(FpBinOp, Src2, Dst)]; % z op= y true -> % y = x op y, where y != x case fp_binop_commutes(FpBinOp) of true -> % y = y op x [hipe_x86:mk_fp_binop(FpBinOp, Src1, Dst)]; % y op= x false -> % y = x op y, where op doesn't commute RevFpBinOp = reverse_fp_binop(FpBinOp), [hipe_x86:mk_fp_binop(RevFpBinOp, Src1, Dst)] end end end. %%% Convert an RTL FP binary operator. conv_fp_binop(RtlFpBinOp) -> case RtlFpBinOp of 'fadd' -> 'fadd'; 'fdiv' -> 'fdiv'; 'fmul' -> 'fmul'; 'fsub' -> 'fsub' end. fp_binop_commutes(FpBinOp) -> case FpBinOp of 'fadd' -> true; 'fmul' -> true; _ -> false end. reverse_fp_binop(FpBinOp) -> case FpBinOp of 'fsub' -> 'fsubr'; 'fdiv' -> 'fdivr' end. %%% Create a jmp_switch instruction. -ifdef(HIPE_AMD64). mk_jmp_switch(Index, JTabLab, Labels) -> JTabReg = hipe_x86:mk_new_temp('untagged'), JTabImm = hipe_x86:mk_imm_from_addr(JTabLab, constant), [hipe_x86:mk_move64(JTabImm, JTabReg), hipe_x86:mk_jmp_switch(Index, JTabReg, Labels)]. -else. mk_jmp_switch(Index, JTabLab, Labels) -> %% this is equivalent to "jmp *JTabLab(,Index,4)" %% ("r = Index; r *= 4; r += &JTab; jmp *r" isn't as nice) [hipe_x86:mk_jmp_switch(Index, JTabLab, Labels)]. -endif. %%% Finalise the translation of a load_address instruction. -ifdef(HIPE_AMD64). mk_load_address(Type, Src, Dst) -> case Type of c_const -> % 32 bits [hipe_x86:mk_move(Src, Dst)]; _ -> [hipe_x86:mk_move64(Src, Dst)] end. -else. mk_load_address(_Type, Src, Dst) -> [hipe_x86:mk_move(Src, Dst)]. -endif. %%% Translate 32-bit and larger loads. -ifdef(HIPE_AMD64). mk_load(LoadSize, LoadSign, Src, Off, Dst) -> case {LoadSize, LoadSign} of {int32, signed} -> [hipe_x86:mk_movsx(hipe_x86:mk_mem(Src, Off, 'int32'), Dst)]; {int32, unsigned} -> %% The processor zero-extends for us. No need for 'movzx'. [hipe_x86:mk_move(hipe_x86:mk_mem(Src, Off, 'int32'), Dst)]; {_, _} -> mk_load_word(Src, Off, Dst) end. -else. mk_load(_LoadSize, _LoadSign, Src, Off, Dst) -> mk_load_word(Src, Off, Dst). -endif. mk_load_word(Src, Off, Dst) -> Type = typeof_dst(Dst), [hipe_x86:mk_move(hipe_x86:mk_mem(Src, Off, Type), Dst)]. %%% Finalise the translation of a store instruction. -ifdef(HIPE_AMD64). mk_store(RtlStoreSize, Src, Ptr, Off) -> Type = case RtlStoreSize of word -> typeof_src(Src); OtherType -> OtherType end, [hipe_x86:mk_move(Src, hipe_x86:mk_mem(Ptr, Off, Type))]. -else. mk_store(RtlStoreSize, Src, Ptr, Off) -> case RtlStoreSize of word -> Type = typeof_src(Src), [hipe_x86:mk_move(Src, hipe_x86:mk_mem(Ptr, Off, Type))]; int32 -> Type = typeof_src(Src), [hipe_x86:mk_move(Src, hipe_x86:mk_mem(Ptr, Off, Type))]; int16 -> Type = 'int16', [hipe_x86:mk_move(Src, hipe_x86:mk_mem(Ptr, Off, Type))]; byte -> Type = 'byte', {NewSrc, I1} = conv_small_store(Src), I1 ++ [hipe_x86:mk_move(NewSrc, hipe_x86:mk_mem(Ptr, Off, Type))] end. conv_small_store(Src) -> case hipe_x86:is_imm(Src) of true -> {Src, []}; false -> NewSrc = hipe_x86:mk_temp(hipe_x86_registers:eax(), 'untagged'), {NewSrc, [hipe_x86:mk_move(Src, NewSrc)]} end. -endif.