aboutsummaryrefslogtreecommitdiffstats
path: root/lib/hipe/sparc/hipe_rtl_to_sparc.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/hipe/sparc/hipe_rtl_to_sparc.erl')
-rw-r--r--lib/hipe/sparc/hipe_rtl_to_sparc.erl972
1 files changed, 972 insertions, 0 deletions
diff --git a/lib/hipe/sparc/hipe_rtl_to_sparc.erl b/lib/hipe/sparc/hipe_rtl_to_sparc.erl
new file mode 100644
index 0000000000..df5e2b0077
--- /dev/null
+++ b/lib/hipe/sparc/hipe_rtl_to_sparc.erl
@@ -0,0 +1,972 @@
+%% -*- erlang-indent-level: 2 -*-
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2005-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%
+%%
+
+-module(hipe_rtl_to_sparc).
+-export([translate/1]).
+
+-include("../rtl/hipe_rtl.hrl").
+
+translate(RTL) ->
+ hipe_gensym:init(sparc),
+ hipe_gensym:set_var(sparc, hipe_sparc_registers:first_virtual()),
+ hipe_gensym:set_label(sparc, 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_sparc:mk_label(hipe_gensym:get_next_label(sparc)) |
+ move_formals(RegFormals, Code0)]
+ end,
+ IsClosure = hipe_rtl:rtl_is_closure(RTL),
+ IsLeaf = hipe_rtl:rtl_is_leaf(RTL),
+ hipe_sparc: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{} -> conv_alu(I, Map, Data);
+ #alub{} -> conv_alub(I, Map, Data);
+ #branch{} -> conv_branch(I, Map, Data);
+ #call{} -> conv_call(I, Map, Data);
+ #comment{} -> conv_comment(I, Map, Data);
+ #enter{} -> conv_enter(I, Map, Data);
+ #goto{} -> conv_goto(I, Map, Data);
+ #label{} -> conv_label(I, Map, Data);
+ #load{} -> conv_load(I, Map, Data);
+ #load_address{} -> conv_load_address(I, Map, Data);
+ #load_atom{} -> conv_load_atom(I, Map, Data);
+ #move{} -> conv_move(I, Map, Data);
+ #return{} -> conv_return(I, Map, Data);
+ #store{} -> conv_store(I, Map, Data);
+ #switch{} -> conv_switch(I, Map, Data); % XXX: only switch uses/updates Data
+ #fconv{} -> conv_fconv(I, Map, Data);
+ #fmove{} -> conv_fmove(I, Map, Data);
+ #fload{} -> conv_fload(I, Map, Data);
+ #fstore{} -> conv_fstore(I, Map, Data);
+ #fp{} -> conv_fp_binary(I, Map, Data);
+ #fp_unop{} -> conv_fp_unary(I, Map, Data);
+ _ -> exit({?MODULE,conv_insn,I})
+ end.
+
+conv_fconv(I, Map, Data) ->
+ %% Dst := (double)Src, where Dst is FP reg and Src is int reg
+ {Src, Map1} = conv_src(hipe_rtl:fconv_src(I), Map), % exclude imm src
+ {Dst, Map2} = conv_fpreg(hipe_rtl:fconv_dst(I), Map1),
+ I2 = mk_fconv(Src, Dst),
+ {I2, Map2, Data}.
+
+mk_fconv(Src, Dst) ->
+ CSP = hipe_sparc:mk_temp(14, 'untagged'), % o6
+ Disp = hipe_sparc:mk_simm13(100),
+ [hipe_sparc:mk_store('stw', Src, CSP, Disp),
+ hipe_sparc:mk_pseudo_fload(CSP, Disp, Dst, true),
+ hipe_sparc:mk_fp_unary('fitod', Dst, Dst)].
+
+conv_fmove(I, Map, Data) ->
+ %% Dst := Src, where both Dst and Src are FP regs
+ {Src, Map1} = conv_fpreg(hipe_rtl:fmove_src(I), Map),
+ {Dst, Map2} = conv_fpreg(hipe_rtl:fmove_dst(I), Map1),
+ I2 = mk_fmove(Src, Dst),
+ {I2, Map2, Data}.
+
+mk_fmove(Src, Dst) ->
+ [hipe_sparc:mk_pseudo_fmove(Src, Dst)].
+
+conv_fload(I, Map, Data) ->
+ %% Dst := MEM[Base+Off], where Dst is FP reg
+ {Base1, Map1} = conv_src(hipe_rtl:fload_src(I), Map),
+ {Base2, Map2} = conv_src(hipe_rtl:fload_offset(I), Map1),
+ {Dst, Map3} = conv_fpreg(hipe_rtl:fload_dst(I), Map2),
+ I2 = mk_fload(Base1, Base2, Dst),
+ {I2, Map3, Data}.
+
+mk_fload(Base1, Base2, Dst) ->
+ case hipe_sparc:is_temp(Base1) of
+ true ->
+ case hipe_sparc:is_temp(Base2) of
+ true ->
+ mk_fload_rr(Base1, Base2, Dst);
+ _ ->
+ mk_fload_ri(Base1, Base2, Dst)
+ end;
+ _ ->
+ case hipe_sparc:is_temp(Base2) of
+ true ->
+ mk_fload_ri(Base2, Base1, Dst);
+ _ ->
+ mk_fload_ii(Base1, Base2, Dst)
+ end
+ end.
+
+mk_fload_rr(Base1, Base2, Dst) ->
+ Tmp = new_untagged_temp(),
+ Disp = hipe_sparc:mk_simm13(0),
+ [hipe_sparc:mk_alu('add', Base1, Base2, Tmp),
+ hipe_sparc:mk_pseudo_fload(Tmp, Disp, Dst, false)].
+
+mk_fload_ii(Base1, Base2, Dst) ->
+ io:format("~w: RTL fload with two immediates\n", [?MODULE]),
+ Tmp = new_untagged_temp(),
+ mk_set(Base1, Tmp,
+ mk_fload_ri(Tmp, Base2, Dst)).
+
+mk_fload_ri(Base, Disp, Dst) ->
+ hipe_sparc:mk_fload(Base, Disp, Dst, 'new').
+
+conv_fstore(I, Map, Data) ->
+ %% MEM[Base+Off] := Src, where Src is FP reg
+ {Base1, Map1} = conv_dst(hipe_rtl:fstore_base(I), Map),
+ {Base2, Map2} = conv_src(hipe_rtl:fstore_offset(I), Map1),
+ {Src, Map3} = conv_fpreg(hipe_rtl:fstore_src(I), Map2),
+ I2 = mk_fstore(Src, Base1, Base2),
+ {I2, Map3, Data}.
+
+mk_fstore(Src, Base1, Base2) ->
+ case hipe_sparc:is_temp(Base2) of
+ true ->
+ mk_fstore_rr(Src, Base1, Base2);
+ _ ->
+ mk_fstore_ri(Src, Base1, Base2)
+ end.
+
+mk_fstore_rr(Src, Base1, Base2) ->
+ Tmp = new_untagged_temp(),
+ Disp = hipe_sparc:mk_simm13(0),
+ [hipe_sparc:mk_alu('add', Base1, Base2, Tmp),
+ hipe_sparc:mk_pseudo_fstore(Src, Tmp, Disp)].
+
+mk_fstore_ri(Src, Base, Disp) ->
+ hipe_sparc:mk_fstore(Src, Base, Disp, 'new').
+
+conv_fp_binary(I, Map, Data) ->
+ {Src1, Map1} = conv_fpreg(hipe_rtl:fp_src1(I), Map),
+ {Src2, Map2} = conv_fpreg(hipe_rtl:fp_src2(I), Map1),
+ {Dst, Map3} = conv_fpreg(hipe_rtl:fp_dst(I), Map2),
+ RtlFpOp = hipe_rtl:fp_op(I),
+ I2 = mk_fp_binary(RtlFpOp, Src1, Src2, Dst),
+ {I2, Map3, Data}.
+
+mk_fp_binary(RtlFpOp, Src1, Src2, Dst) ->
+ FpBinOp =
+ case RtlFpOp of
+ 'fadd' -> 'faddd';
+ 'fdiv' -> 'fdivd';
+ 'fmul' -> 'fmuld';
+ 'fsub' -> 'fsubd'
+ end,
+ [hipe_sparc:mk_fp_binary(FpBinOp, Src1, Src2, Dst)].
+
+conv_fp_unary(I, Map, Data) ->
+ {Src, Map1} = conv_fpreg(hipe_rtl:fp_unop_src(I), Map),
+ {Dst, Map2} = conv_fpreg(hipe_rtl:fp_unop_dst(I), Map1),
+ RtlFpUnOp = hipe_rtl:fp_unop_op(I),
+ I2 = mk_fp_unary(RtlFpUnOp, Src, Dst),
+ {I2, Map2, Data}.
+
+mk_fp_unary(RtlFpUnOp, Src, Dst) ->
+ FpUnOp =
+ case RtlFpUnOp of
+ 'fchs' -> 'fnegd'
+ end,
+ [hipe_sparc:mk_fp_unary(FpUnOp, Src, Dst)].
+
+conv_alu(I, Map, Data) ->
+ %% dst = src1 aluop src2
+ {Dst, Map0} = conv_dst(hipe_rtl:alu_dst(I), Map),
+ {Src1, Map1} = conv_src(hipe_rtl:alu_src1(I), Map0),
+ {Src2, Map2} = conv_src(hipe_rtl:alu_src2(I), Map1),
+ AluOp = conv_aluop(hipe_rtl:alu_op(I)),
+ {I2, _DidCommute} = mk_alu(AluOp, Src1, Src2, Dst),
+ {I2, Map2, Data}.
+
+mk_alu(XAluOp, Src1, Src2, Dst) ->
+ case hipe_sparc:is_temp(Src1) of
+ true ->
+ case hipe_sparc:is_temp(Src2) of
+ true ->
+ {mk_alu_rs(XAluOp, Src1, Src2, Dst),
+ false};
+ _ ->
+ {mk_alu_ri(XAluOp, Src1, Src2, Dst),
+ false}
+ end;
+ _ ->
+ case hipe_sparc:is_temp(Src2) of
+ true ->
+ mk_alu_ir(XAluOp, Src1, Src2, Dst);
+ _ ->
+ {mk_alu_ii(XAluOp, Src1, Src2, Dst),
+ false}
+ end
+ end.
+
+mk_alu_ii(XAluOp, Src1, Src2, Dst) ->
+ io:format("~w: ALU with two immediates (~w ~w ~w ~w)\n",
+ [?MODULE, XAluOp, Src1, Src2, Dst]),
+ Tmp = new_untagged_temp(),
+ mk_set(Src1, Tmp,
+ mk_alu_ri(XAluOp, Tmp, Src2, Dst)).
+
+mk_alu_ir(XAluOp, Src1, Src2, Dst) ->
+ case xaluop_commutes(XAluOp) of
+ true ->
+ {mk_alu_ri(XAluOp, Src2, Src1, Dst),
+ true};
+ _ ->
+ Tmp = new_untagged_temp(),
+ {mk_set(Src1, Tmp,
+ mk_alu_rs(XAluOp, Tmp, Src2, Dst)),
+ false}
+ end.
+
+mk_alu_ri(XAluOp, Src1, Src2, Dst) ->
+ case xaluop_is_shift(XAluOp) of
+ true ->
+ mk_shift_ri(XAluOp, Src1, Src2, Dst);
+ false ->
+ mk_arith_ri(XAluOp, Src1, Src2, Dst)
+ end.
+
+mk_shift_ri(XShiftOp, Src1, Src2, Dst) when is_integer(Src2) ->
+ if Src2 >= 0, Src2 < 32 -> % XXX: sparc64: < 64
+ mk_alu_rs(XShiftOp, Src1, hipe_sparc:mk_uimm5(Src2), Dst);
+ true ->
+ exit({?MODULE,mk_shift_ri,Src2}) % excessive shifts are errors
+ end.
+
+mk_arith_ri(XAluOp, Src1, Src2, Dst) when is_integer(Src2) ->
+ if -4096 =< Src2, Src2 < 4096 ->
+ mk_alu_rs(XAluOp, Src1, hipe_sparc:mk_simm13(Src2), Dst);
+ true ->
+ Tmp = new_untagged_temp(),
+ mk_set(Src2, Tmp,
+ mk_alu_rs(XAluOp, Src1, Tmp, Dst))
+ end.
+
+mk_alu_rs(XAluOp, Src1, Src2, Dst) ->
+ [hipe_sparc:mk_alu(xaluop_normalise(XAluOp), Src1, Src2, Dst)].
+
+conv_alub(I, Map, Data) ->
+ %% dst = src1 aluop src2; if COND goto label
+ {Dst, Map0} = conv_dst(hipe_rtl:alub_dst(I), Map),
+ {Src1, Map1} = conv_src(hipe_rtl:alub_src1(I), Map0),
+ {Src2, Map2} = conv_src(hipe_rtl:alub_src2(I), Map1),
+ Cond = conv_cond(hipe_rtl:alub_cond(I)),
+ RtlAlubOp = hipe_rtl:alub_op(I),
+ I2 =
+ case RtlAlubOp of
+ 'mul' ->
+ %% To check for overflow in 32x32->32 multiplication:
+ %% smul Src1,Src2,Dst % Dst is lo32(Res), %y is %hi32(Res)
+ %% rd %y,TmpHi
+ %% sra Dst,31,TmpSign % fill TmpSign with sign of Dst
+ %% subcc TmpSign,TmpHi,%g0
+ %% [bne OverflowLabel]
+ NewCond =
+ case Cond of
+ vs -> ne;
+ vc -> eq
+ end,
+ TmpHi = hipe_sparc:mk_new_temp('untagged'),
+ TmpSign = hipe_sparc:mk_new_temp('untagged'),
+ G0 = hipe_sparc:mk_g0(),
+ {I1, _DidCommute} = mk_alu('smul', Src1, Src2, Dst),
+ I1 ++
+ [hipe_sparc:mk_rdy(TmpHi),
+ hipe_sparc:mk_alu('sra', Dst, hipe_sparc:mk_uimm5(31), TmpSign) |
+ conv_alub2(G0, TmpSign, 'sub', NewCond, TmpHi, I)];
+ _ ->
+ conv_alub2(Dst, Src1, RtlAlubOp, Cond, Src2, I)
+ end,
+ {I2, Map2, Data}.
+
+-ifdef(notdef). % XXX: only for sparc64, alas
+conv_alub2(Dst, Src1, RtlAlubOp, Cond, Src2, I) ->
+ case conv_cond_rcond(Cond) of
+ [] ->
+ conv_alub_bp(Dst, Src1, RtlAlubOp, Cond, Src2, I);
+ RCond ->
+ conv_alub_br(Dst, Src1, RtlAlubOp, RCond, Src2, I)
+ end.
+
+conv_alub_br(Dst, Src1, RtlAlubOp, RCond, Src2, I) ->
+ TrueLab = hipe_rtl:alub_true_label(I),
+ FalseLab = hipe_rtl:alub_false_label(I),
+ Pred = hipe_rtl:alub_pred(I),
+ %% "Dst = Src1 AluOp Src2; if COND" becomes
+ %% "Dst = Src1 AluOp Src2; if-COND(Dst)"
+ {I2, _DidCommute} = mk_alu(conv_alubop_nocc(RtlAlubOp), Src1, Src2, Dst),
+ I2 ++ mk_pseudo_br(RCond, Dst, TrueLab, FalseLab, Pred).
+
+conv_cond_rcond(Cond) ->
+ case Cond of
+ 'e' -> 'z';
+ 'ne' -> 'nz';
+ 'g' -> 'gz';
+ 'ge' -> 'gez';
+ 'l' -> 'lz';
+ 'le' -> 'lez';
+ _ -> [] % vs, vc, gu, geu, lu, leu
+ end.
+
+conv_alubop_nocc(RtlAlubOp) ->
+ case RtlAlubOp of
+ 'add' -> 'add';
+ 'sub' -> 'sub';
+ %% mul: handled elsewhere
+ 'or' -> 'or';
+ 'and' -> 'and';
+ 'xor' -> 'xor'
+ %% no shift ops
+ end.
+
+mk_pseudo_br(RCond, Dst, TrueLab, FalseLab, Pred) ->
+ [hipe_sparc:mk_pseudo_br(RCond, Dst, TrueLab, FalseLab, Pred)].
+-else.
+conv_alub2(Dst, Src1, RtlAlubOp, Cond, Src2, I) ->
+ conv_alub_bp(Dst, Src1, RtlAlubOp, Cond, Src2, I).
+-endif.
+
+conv_alub_bp(Dst, Src1, RtlAlubOp, Cond, Src2, I) ->
+ TrueLab = hipe_rtl:alub_true_label(I),
+ FalseLab = hipe_rtl:alub_false_label(I),
+ Pred = hipe_rtl:alub_pred(I),
+ %% "Dst = Src1 AluOp Src2; if COND" becomes
+ %% "Dst = Src1 AluOpCC Src22; if-COND(CC)"
+ {I2, _DidCommute} = mk_alu(conv_alubop_cc(RtlAlubOp), Src1, Src2, Dst),
+ I2 ++ mk_pseudo_bp(Cond, TrueLab, FalseLab, Pred).
+
+conv_alubop_cc(RtlAlubOp) ->
+ case RtlAlubOp of
+ 'add' -> 'addcc';
+ 'sub' -> 'subcc';
+ %% mul: handled elsewhere
+ 'or' -> 'orcc';
+ 'and' -> 'andcc';
+ 'xor' -> 'xorcc'
+ %% no shift ops
+ end.
+
+conv_branch(I, Map, Data) ->
+ %% <unused> = src1 - src2; if COND goto label
+ {Src1, Map0} = conv_src(hipe_rtl:branch_src1(I), Map),
+ {Src2, Map1} = conv_src(hipe_rtl:branch_src2(I), Map0),
+ Cond = conv_cond(hipe_rtl:branch_cond(I)),
+ I2 = conv_branch2(Src1, Cond, Src2, I),
+ {I2, Map1, Data}.
+
+-ifdef(notdef). % XXX: only for sparc64, alas
+conv_branch2(Src1, Cond, Src2, I) ->
+ case conv_cond_rcond(Cond) of
+ [] ->
+ conv_branch_bp(Src1, Cond, Src2, I);
+ RCond ->
+ conv_branch_br(Src1, RCond, Src2, I)
+ end.
+
+conv_branch_br(Src1, RCond, Src2, I) ->
+ TrueLab = hipe_rtl:branch_true_label(I),
+ FalseLab = hipe_rtl:branch_false_label(I),
+ Pred = hipe_rtl:branch_pred(I),
+ %% "if src1-COND-src2" becomes
+ %% "sub src1,src2,tmp; if-COND(tmp)"
+ Dst = hipe_sparc:mk_new_temp('untagged'),
+ XAluOp = 'cmp', % == a sub that commutes
+ {I1, DidCommute} = mk_alu(XAluOp, Src1, Src2, Dst),
+ NewRCond =
+ case DidCommute of
+ true -> commute_rcond(RCond);
+ false -> RCond
+ end,
+ I1 ++ mk_pseudo_br(NewRCond, Dst, TrueLab, FalseLab, Pred).
+
+commute_rcond(RCond) -> % if x RCond y, then y commute_rcond(RCond) x
+ case RCond of
+ 'z' -> 'z'; % ==, ==
+ 'nz' -> 'nz'; % !=, !=
+ 'gz' -> 'lz'; % >, <
+ 'gez' -> 'lez'; % >=, <=
+ 'lz' -> 'gz'; % <, >
+ 'lez' -> 'gez' % <=, >=
+ end.
+-else.
+conv_branch2(Src1, Cond, Src2, I) ->
+ conv_branch_bp(Src1, Cond, Src2, I).
+-endif.
+
+conv_branch_bp(Src1, Cond, Src2, I) ->
+ TrueLab = hipe_rtl:branch_true_label(I),
+ FalseLab = hipe_rtl:branch_false_label(I),
+ Pred = hipe_rtl:branch_pred(I),
+ %% "if src1-COND-src2" becomes
+ %% "subcc src1,src2,%g0; if-COND(CC)"
+ Dst = hipe_sparc:mk_g0(),
+ XAluOp = 'cmpcc', % == a subcc that commutes
+ {I1, DidCommute} = mk_alu(XAluOp, Src1, Src2, Dst),
+ NewCond =
+ case DidCommute of
+ true -> commute_cond(Cond);
+ false -> Cond
+ end,
+ I1 ++ mk_pseudo_bp(NewCond, TrueLab, FalseLab, Pred).
+
+conv_call(I, Map, Data) ->
+ {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),
+ ContLab = hipe_rtl:call_continuation(I),
+ ExnLab = hipe_rtl:call_fail(I),
+ Linkage = hipe_rtl:call_type(I),
+ I2 = mk_call(Dsts, Fun, Args, ContLab, ExnLab, Linkage),
+ {I2, Map2, Data}.
+
+mk_call(Dsts, Fun, Args, ContLab, ExnLab, Linkage) ->
+ case hipe_sparc:is_prim(Fun) of
+ true ->
+ mk_primop_call(Dsts, Fun, Args, ContLab, ExnLab, Linkage);
+ false ->
+ mk_general_call(Dsts, Fun, Args, ContLab, ExnLab, Linkage)
+ end.
+
+mk_primop_call(Dsts, Prim, Args, ContLab, ExnLab, Linkage) ->
+ case hipe_sparc:prim_prim(Prim) of
+ %% no SPARC-specific primops defined yet
+ _ ->
+ mk_general_call(Dsts, Prim, Args, ContLab, ExnLab, Linkage)
+ end.
+
+mk_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 mk_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(sparc),
+ {NewContLab, [hipe_sparc: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.
+ NewContLab = hipe_gensym:get_next_label(sparc),
+ case ContLab of
+ [] ->
+ %% This is just a fallthrough
+ %% No jump back after the moves.
+ {NewContLab,
+ [hipe_sparc:mk_label(NewContLab) |
+ Moves]};
+ _ ->
+ %% The call has a continuation. Jump to it.
+ {NewContLab,
+ [hipe_sparc:mk_label(NewContLab) |
+ Moves ++
+ [hipe_sparc:mk_b_label(ContLab)]]}
+ end
+ end,
+ SDesc = hipe_sparc:mk_sdesc(ExnLab, 0, length(Args), {}),
+ CallInsn = hipe_sparc:mk_pseudo_call(Fun, SDesc, RealContLab, Linkage),
+ {RegArgs,StkArgs} = split_args(Args),
+ mk_push_args(StkArgs, move_actuals(RegArgs, [CallInsn | Tail])).
+
+mk_call_results(Dsts) ->
+ case Dsts of
+ [] -> [];
+ [Dst] ->
+ RV = hipe_sparc:mk_rv(),
+ [hipe_sparc:mk_pseudo_move(RV, Dst)]
+ end.
+
+mk_push_args(StkArgs, Tail) ->
+ case length(StkArgs) of
+ 0 ->
+ Tail;
+ NrStkArgs ->
+ [hipe_sparc:mk_pseudo_call_prepare(NrStkArgs) |
+ mk_store_args(StkArgs, NrStkArgs * word_size(), Tail)]
+ end.
+
+mk_store_args([Arg|Args], PrevOffset, Tail) ->
+ Offset = PrevOffset - word_size(),
+ {Src,FixSrc} =
+ case hipe_sparc:is_temp(Arg) of
+ true ->
+ {Arg, []};
+ _ ->
+ Tmp = new_tagged_temp(),
+ {Tmp, mk_set(Arg, Tmp)}
+ end,
+ %% XXX: sparc64: stx
+ Store = hipe_sparc:mk_store('stw', Src, hipe_sparc:mk_sp(), hipe_sparc:mk_simm13(Offset)),
+ mk_store_args(Args, Offset, FixSrc ++ [Store | Tail]);
+mk_store_args([], _, Tail) ->
+ Tail.
+
+conv_comment(I, Map, Data) ->
+ I2 = [hipe_sparc:mk_comment(hipe_rtl:comment_text(I))],
+ {I2, Map, Data}.
+
+conv_enter(I, Map, Data) ->
+ {Args, Map0} = conv_src_list(hipe_rtl:enter_arglist(I), Map),
+ {Fun, Map1} = conv_fun(hipe_rtl:enter_fun(I), Map0),
+ I2 = mk_enter(Fun, Args, hipe_rtl:enter_type(I)),
+ {I2, Map1, Data}.
+
+mk_enter(Fun, Args, Linkage) ->
+ Arity = length(Args),
+ {RegArgs,StkArgs} = split_args(Args),
+ move_actuals(RegArgs,
+ [hipe_sparc:mk_pseudo_tailcall_prepare(),
+ hipe_sparc:mk_pseudo_tailcall(Fun, Arity, StkArgs, Linkage)]).
+
+conv_goto(I, Map, Data) ->
+ I2 = [hipe_sparc:mk_b_label(hipe_rtl:goto_label(I))],
+ {I2, Map, Data}.
+
+conv_label(I, Map, Data) ->
+ I2 = [hipe_sparc:mk_label(hipe_rtl:label_name(I))],
+ {I2, Map, Data}.
+
+conv_load(I, Map, Data) ->
+ {Dst, Map0} = conv_dst(hipe_rtl:load_dst(I), Map),
+ {Base1, Map1} = conv_src(hipe_rtl:load_src(I), Map0),
+ {Base2, Map2} = conv_src(hipe_rtl:load_offset(I), Map1),
+ LdOp = conv_ldop(hipe_rtl:load_size(I), hipe_rtl:load_sign(I)),
+ {I2, _DidCommute} = mk_alu(LdOp, Base1, Base2, Dst),
+ {I2, Map2, Data}.
+
+conv_ldop(LoadSize, LoadSign) ->
+ case LoadSize of
+ word -> 'lduw'; % XXX: sparc64: ldx
+ int32 -> 'lduw'; % XXX: sparc64: lduw or ldsw
+ int16 ->
+ case LoadSign of
+ signed -> 'ldsh';
+ unsigned -> 'lduh'
+ end;
+ byte ->
+ case LoadSign of
+ signed -> 'ldsb';
+ unsigned -> 'ldub'
+ end
+ end.
+
+conv_load_address(I, Map, Data) ->
+ {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 = {Addr,Type},
+ I2 = [hipe_sparc:mk_pseudo_set(Src, Dst)],
+ {I2, Map0, Data}.
+
+conv_load_atom(I, Map, Data) ->
+ {Dst, Map0} = conv_dst(hipe_rtl:load_atom_dst(I), Map),
+ Src = hipe_rtl:load_atom_atom(I),
+ I2 = [hipe_sparc:mk_pseudo_set(Src, Dst)],
+ {I2, Map0, Data}.
+
+conv_move(I, Map, Data) ->
+ {Dst, Map0} = conv_dst(hipe_rtl:move_dst(I), Map),
+ {Src, Map1} = conv_src(hipe_rtl:move_src(I), Map0),
+ I2 = mk_move(Src, Dst, []),
+ {I2, Map1, Data}.
+
+mk_move(Src, Dst, Tail) ->
+ case hipe_sparc:is_temp(Src) of
+ true -> [hipe_sparc:mk_pseudo_move(Src, Dst) | Tail];
+ _ -> mk_set(Src, Dst, Tail)
+ end.
+
+conv_return(I, Map, Data) ->
+ %% TODO: multiple-value returns
+ {[Arg], Map0} = conv_src_list(hipe_rtl:return_varlist(I), Map),
+ I2 = mk_move(Arg, hipe_sparc:mk_rv(),
+ [hipe_sparc:mk_pseudo_ret()]),
+ {I2, Map0, Data}.
+
+conv_store(I, Map, Data) ->
+ {Base1, Map0} = conv_dst(hipe_rtl:store_base(I), Map), % no immediates allowed
+ {Src, Map1} = conv_src(hipe_rtl:store_src(I), Map0),
+ {Base2, Map2} = conv_src(hipe_rtl:store_offset(I), Map1),
+ StOp = conv_stop(hipe_rtl:store_size(I)),
+ I2 = mk_store(StOp, Src, Base1, Base2),
+ {I2, Map2, Data}.
+
+conv_stop(StoreSize) ->
+ case StoreSize of
+ word -> 'stw'; % XXX: sparc64: stx
+ int32 -> 'stw';
+ byte -> 'stb'
+ end.
+
+mk_store(StOp, Src, Base1, Base2) ->
+ case hipe_sparc:is_temp(Src) of
+ true ->
+ mk_store2(StOp, Src, Base1, Base2);
+ _ ->
+ Tmp = new_untagged_temp(),
+ mk_set(Src, Tmp,
+ mk_store2(StOp, Tmp, Base1, Base2))
+ end.
+
+mk_store2(StOp, Src, Base1, Base2) ->
+ case hipe_sparc:is_temp(Base2) of
+ true ->
+ mk_store_rr(StOp, Src, Base1, Base2);
+ _ ->
+ mk_store_ri(StOp, Src, Base1, Base2)
+ end.
+
+mk_store_ri(StOp, Src, Base, Disp) ->
+ hipe_sparc:mk_store(StOp, Src, Base, Disp, 'new', []).
+
+mk_store_rr(StOp, Src, Base1, Base2) ->
+ [hipe_sparc:mk_store(StOp, Src, Base1, Base2)].
+
+conv_switch(I, Map, Data) ->
+ 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
+ {IndexR, Map1} = conv_dst(hipe_rtl:switch_src(I), Map),
+ JTabR = new_untagged_temp(),
+ OffsetR = new_untagged_temp(),
+ DestR = new_untagged_temp(),
+ I2 =
+ [hipe_sparc:mk_pseudo_set({JTabLab,constant}, JTabR),
+ %% XXX: sparc64: << 3
+ hipe_sparc:mk_alu('sll', IndexR, hipe_sparc:mk_uimm5(2), OffsetR),
+ %% XXX: sparc64: ldx
+ hipe_sparc:mk_alu('lduw', JTabR, OffsetR, DestR),
+ hipe_sparc:mk_jmp(DestR, hipe_sparc:mk_simm13(0), Labels)],
+ {I2, Map1, NewData}.
+
+%%% Create a conditional branch.
+
+mk_pseudo_bp(Cond, TrueLabel, FalseLabel, Pred) ->
+ [hipe_sparc:mk_pseudo_bp(Cond, TrueLabel, FalseLabel, Pred)].
+
+%%% Load an integer constant into a register.
+
+mk_set(Value, Dst) -> mk_set(Value, Dst, []).
+
+mk_set(Value, Dst, Tail) ->
+ hipe_sparc:mk_set(Value, Dst, Tail).
+
+%%% Convert an RTL ALU op.
+
+conv_aluop(RtlAluOp) ->
+ case RtlAluOp of
+ 'add' -> 'add';
+ 'sub' -> 'sub';
+ 'mul' -> 'mulx';
+ 'or' -> 'or';
+ 'and' -> 'and';
+ 'xor' -> 'xor';
+ 'sll' -> 'sll'; % XXX: sparc64: sllx
+ 'srl' -> 'srl'; % XXX: sparc64: srlx
+ 'sra' -> 'sra' % XXX: sparc64: srax
+ end.
+
+%%% Check if an extended SPARC AluOp commutes.
+
+xaluop_commutes(XAluOp) ->
+ case XAluOp of
+ 'cmp' -> true;
+ 'cmpcc' -> true;
+ 'add' -> true;
+ 'addcc' -> true;
+ 'and' -> true;
+ 'andcc' -> true;
+ 'or' -> true;
+ 'orcc' -> true;
+ 'xor' -> true;
+ 'xorcc' -> true;
+ 'sub' -> false;
+ 'subcc' -> false;
+ 'mulx' -> true;
+ 'smul' -> true;
+ 'sll' -> false;
+ 'srl' -> false;
+ 'sra' -> false;
+ 'sllx' -> false;
+ 'srlx' -> false;
+ 'srax' -> false;
+ 'ldsb' -> true;
+ 'ldsh' -> true;
+ 'ldsw' -> true;
+ 'ldub' -> true;
+ 'lduh' -> true;
+ 'lduw' -> true;
+ 'ldx' -> true
+ end.
+
+%%% Check if an extended SPARC AluOp is a shift.
+
+xaluop_is_shift(XAluOp) ->
+ case XAluOp of
+ 'sll' -> true;
+ 'srl' -> true;
+ 'sra' -> true;
+ 'sllx' -> true;
+ 'srlx' -> true;
+ 'srax' -> true;
+ _ -> false
+ end.
+
+%%% Convert an extended SPARC AluOp back to a plain AluOp.
+%%% This just maps cmp{,cc} to sub{,cc}.
+
+xaluop_normalise(XAluOp) ->
+ case XAluOp of
+ 'cmp' -> 'sub';
+ 'cmpcc' -> 'subcc';
+ _ -> XAluOp
+ end.
+
+%%% Convert an RTL condition code.
+
+conv_cond(RtlCond) ->
+ case RtlCond of
+ eq -> 'e';
+ ne -> 'ne';
+ gt -> 'g';
+ gtu -> 'gu'; % >u
+ ge -> 'ge';
+ geu -> 'geu'; % >=u
+ lt -> 'l';
+ ltu -> 'lu'; % <u
+ le -> 'le';
+ leu -> 'leu'; % <=u
+ overflow -> 'vs';
+ not_overflow -> 'vc'
+ end.
+
+%%% Commute a SPARC condition code.
+
+commute_cond(Cond) -> % if x Cond y, then y commute_cond(Cond) x
+ case Cond of
+ 'e' -> 'e'; % ==, ==
+ 'ne' -> 'ne'; % !=, !=
+ 'g' -> 'l'; % >, <
+ 'ge' -> 'le'; % >=, <=
+ 'l' -> 'g'; % <, >
+ 'le' -> 'ge'; % <=, >=
+ 'gu' -> 'lu'; % >u, <u
+ 'geu' -> 'leu'; % >=u, <=u
+ 'lu' -> 'gu'; % <u, >u
+ 'leu' -> 'geu' % <=u, >=u
+ %% vs/vc: n/a
+ end.
+
+%%% Split a list of formal or actual parameters into the
+%%% part passed in registers and the part passed on the stack.
+%%% The parameters passed in registers are also tagged with
+%%% the corresponding registers.
+
+split_args(Args) ->
+ split_args(0, hipe_sparc_registers:nr_args(), Args, []).
+
+split_args(I, N, [Arg|Args], RegArgs) when I < N ->
+ Reg = hipe_sparc_registers:arg(I),
+ Temp = hipe_sparc:mk_temp(Reg, 'tagged'),
+ split_args(I+1, N, Args, [{Arg,Temp}|RegArgs]);
+split_args(_, _, StkArgs, RegArgs) ->
+ {RegArgs, StkArgs}.
+
+%%% Convert a list of actual parameters passed in
+%%% registers (from split_args/1) to a list of moves.
+
+move_actuals([{Src,Dst}|Actuals], Rest) ->
+ move_actuals(Actuals, mk_move(Src, Dst, Rest));
+move_actuals([], Rest) ->
+ Rest.
+
+%%% Convert a list of formal parameters passed in
+%%% registers (from split_args/1) to a list of moves.
+
+move_formals([{Dst,Src}|Formals], Rest) ->
+ move_formals(Formals, [hipe_sparc:mk_pseudo_move(Src, Dst) | Rest]);
+move_formals([], Rest) ->
+ Rest.
+
+%%% 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 ->
+ if is_atom(Fun) ->
+ {hipe_sparc:mk_prim(Fun), Map};
+ true ->
+ {conv_mfa(Fun), Map}
+ end
+ end
+ end.
+
+%%% Convert an MFA operand.
+
+conv_mfa({M,F,A}) when is_atom(M), is_atom(F), is_integer(A) ->
+ hipe_sparc:mk_mfa(M, F, A).
+
+%%% Convert an RTL source operand (imm/var/reg).
+%%% Returns a temp or a naked integer.
+
+conv_src(Opnd, Map) ->
+ case hipe_rtl:is_imm(Opnd) of
+ true ->
+ Value = hipe_rtl:imm_value(Opnd),
+ if is_integer(Value) ->
+ {Value, Map}
+ end;
+ false ->
+ conv_dst(Opnd, Map)
+ end.
+
+conv_src_list([O|Os], Map) ->
+ {V, Map1} = conv_src(O, Map),
+ {Vs, Map2} = conv_src_list(Os, Map1),
+ {[V|Vs], Map2};
+conv_src_list([], Map) ->
+ {[], Map}.
+
+%%% Convert an RTL destination operand (var/reg).
+
+conv_fpreg(Opnd, Map) ->
+ true = hipe_rtl:is_fpreg(Opnd),
+ conv_dst(Opnd, Map).
+
+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,
+ IsPrecoloured =
+ case Type of
+ 'double' -> false; %hipe_sparc_registers:is_precoloured_fpr(Name);
+ _ -> hipe_sparc_registers:is_precoloured_gpr(Name)
+ end,
+ case IsPrecoloured of
+ true ->
+ {hipe_sparc:mk_temp(Name, Type), Map};
+ false ->
+ case vmap_lookup(Map, Opnd) of
+ {value, NewTemp} ->
+ {NewTemp, Map};
+ _ ->
+ NewTemp = hipe_sparc: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_sparc_registers:nr_args(), Os, Map, []).
+
+conv_formals(N, [O|Os], Map, Res) ->
+ Type =
+ case hipe_rtl:is_var(O) of
+ true -> 'tagged';
+ _ -> 'untagged'
+ end,
+ Dst =
+ if N > 0 -> hipe_sparc:mk_new_temp(Type); % allocatable
+ true -> hipe_sparc: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}.
+
+%%% new_untagged_temp -- conjure up an untagged scratch reg
+
+new_untagged_temp() ->
+ hipe_sparc:mk_new_temp('untagged').
+
+%%% new_tagged_temp -- conjure up a tagged scratch reg
+
+new_tagged_temp() ->
+ hipe_sparc:mk_new_temp('tagged').
+
+%%% Map from RTL var/reg operands to 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).
+
+word_size() ->
+ hipe_rtl_arch:word_size().