diff options
Diffstat (limited to 'lib/hipe/sparc')
22 files changed, 5268 insertions, 0 deletions
diff --git a/lib/hipe/sparc/Makefile b/lib/hipe/sparc/Makefile new file mode 100644 index 0000000000..efd4996046 --- /dev/null +++ b/lib/hipe/sparc/Makefile @@ -0,0 +1,120 @@ +# +# %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% +# + +ifndef EBIN +EBIN = ../ebin +endif + +ifndef DOCS +DOCS = ../doc +endif + +include $(ERL_TOP)/make/target.mk +include $(ERL_TOP)/make/$(TARGET)/otp.mk + +# ---------------------------------------------------- +# Application version +# ---------------------------------------------------- +include ../vsn.mk +VSN=$(HIPE_VSN) + +# ---------------------------------------------------- +# Release directory specification +# ---------------------------------------------------- +RELSYSDIR = $(RELEASE_PATH)/lib/hipe-$(VSN) + +# ---------------------------------------------------- +# Target Specs +# ---------------------------------------------------- +# Please keep this list sorted. +MODULES=hipe_rtl_to_sparc \ + hipe_sparc \ + hipe_sparc_assemble \ + hipe_sparc_cfg \ + hipe_sparc_defuse \ + hipe_sparc_encode \ + hipe_sparc_finalise \ + hipe_sparc_frame \ + hipe_sparc_liveness_all \ + hipe_sparc_liveness_fpr \ + hipe_sparc_liveness_gpr \ + hipe_sparc_main \ + hipe_sparc_pp \ + hipe_sparc_ra \ + hipe_sparc_ra_finalise \ + hipe_sparc_ra_ls \ + hipe_sparc_ra_naive \ + hipe_sparc_ra_postconditions \ + hipe_sparc_ra_postconditions_fp \ + hipe_sparc_registers + +HRL_FILES=hipe_sparc.hrl +ERL_FILES=$(MODULES:%=%.erl) +TARGET_FILES=$(MODULES:%=$(EBIN)/%.$(EMULATOR)) +DOC_FILES= $(MODULES:%=$(DOCS)/%.html) + +# ---------------------------------------------------- +# FLAGS +# ---------------------------------------------------- + +include ../native.mk + +ERL_COMPILE_FLAGS += +warn_exported_vars + +# ---------------------------------------------------- +# Targets +# ---------------------------------------------------- + +debug opt: $(TARGET_FILES) + +docs: $(DOC_FILES) + +clean: + rm -f $(TARGET_FILES) + rm -f core + +$(DOCS)/%.html:%.erl + erl -noshell -run edoc_run file '"$<"' '[{dir, "$(DOCS)"}]' -s init stop + +# ---------------------------------------------------- +# Special Build Targets +# ---------------------------------------------------- + +# ---------------------------------------------------- +# Release Target +# ---------------------------------------------------- +include $(ERL_TOP)/make/otp_release_targets.mk + +release_spec: opt + $(INSTALL_DIR) $(RELSYSDIR)/ebin + $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + +release_docs_spec: + +# Please keep this list sorted. +$(EBIN)/hipe_rtl_to_sparc.beam: ../rtl/hipe_rtl.hrl +$(EBIN)/hipe_sparc_assemble.beam: ../main/hipe.hrl ../../kernel/src/hipe_ext_format.hrl ../rtl/hipe_literals.hrl ../misc/hipe_sdi.hrl +$(EBIN)/hipe_sparc_cfg.beam: ../flow/cfg.hrl ../flow/cfg.inc +$(EBIN)/hipe_sparc_frame.beam: ../rtl/hipe_literals.hrl +$(EBIN)/hipe_sparc_liveness_all.beam: ../flow/liveness.inc +$(EBIN)/hipe_sparc_liveness_fpr.beam: ../flow/liveness.inc +$(EBIN)/hipe_sparc_liveness_gpr.beam: ../flow/liveness.inc +$(EBIN)/hipe_sparc_registers.beam: ../rtl/hipe_literals.hrl + +$(TARGET_FILES): hipe_sparc.hrl ../misc/hipe_consttab.hrl 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(). diff --git a/lib/hipe/sparc/hipe_sparc.erl b/lib/hipe/sparc/hipe_sparc.erl new file mode 100644 index 0000000000..9fcb94afb6 --- /dev/null +++ b/lib/hipe/sparc/hipe_sparc.erl @@ -0,0 +1,407 @@ +%% -*- 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% +%% + +-module(hipe_sparc). +-export([ + mk_temp/2, + mk_new_temp/1, + mk_new_nonallocatable_temp/1, + is_temp/1, + temp_reg/1, + temp_type/1, + temp_is_allocatable/1, + temp_is_precoloured/1, + + mk_g0/0, + mk_ra/0, + mk_rv/0, + mk_sp/0, + mk_temp1/0, + mk_temp2/0, + + mk_simm13/1, + mk_uimm5/1, + + mk_mfa/3, + + mk_prim/1, + is_prim/1, + prim_prim/1, + + mk_sdesc/4, + + mk_alu/4, + mk_mov/2, + mk_load/6, + + mk_bp/3, + mk_b_label/1, + + %% mk_br/4, + + mk_call_rec/3, + + mk_call_tail/2, + + mk_comment/1, + + mk_label/1, + is_label/1, + label_label/1, + + mk_jmp/3, + mk_jmpl/2, + + mk_pseudo_bp/4, + negate_cond/1, + + %% mk_pseudo_br/5, + %% negate_rcond/1, + + mk_pseudo_call/4, + pseudo_call_contlab/1, + pseudo_call_funv/1, + pseudo_call_linkage/1, + pseudo_call_sdesc/1, + + mk_pseudo_call_prepare/1, + pseudo_call_prepare_nrstkargs/1, + + mk_pseudo_move/2, + is_pseudo_move/1, + pseudo_move_dst/1, + pseudo_move_src/1, + + mk_pseudo_ret/0, + + mk_pseudo_set/2, + + mk_pseudo_tailcall/4, + pseudo_tailcall_funv/1, + pseudo_tailcall_linkage/1, + pseudo_tailcall_stkargs/1, + + mk_pseudo_tailcall_prepare/0, + + mk_rdy/1, + + %% mk_sethi/2, + mk_nop/0, + mk_set/2, + mk_set/3, + mk_addi/4, + + mk_store/4, + mk_store/6, + + mk_fp_binary/4, + + mk_fp_unary/3, + + mk_pseudo_fload/4, + mk_fload/4, + + mk_pseudo_fmove/2, + is_pseudo_fmove/1, + pseudo_fmove_src/1, + pseudo_fmove_dst/1, + + mk_pseudo_fstore/3, + mk_fstore/4, + + mk_defun/8, + defun_code/1, + defun_data/1, + defun_formals/1, + defun_is_closure/1, + defun_is_leaf/1, + defun_mfa/1, + defun_var_range/1 + ]). + +-include("hipe_sparc.hrl"). + +mk_temp(Reg, Type, Allocatable) -> + #sparc_temp{reg=Reg, type=Type, allocatable=Allocatable}. +mk_temp(Reg, Type) -> mk_temp(Reg, Type, true). +mk_new_temp(Type, Allocatable) -> + mk_temp(hipe_gensym:get_next_var(sparc), Type, Allocatable). +mk_new_temp(Type) -> mk_new_temp(Type, true). +mk_new_nonallocatable_temp(Type) -> mk_new_temp(Type, false). +is_temp(X) -> case X of #sparc_temp{} -> true; _ -> false end. +temp_reg(#sparc_temp{reg=Reg}) -> Reg. +temp_type(#sparc_temp{type=Type}) -> Type. +temp_is_allocatable(#sparc_temp{allocatable=A}) -> A. +temp_is_precoloured(#sparc_temp{reg=Reg,type=Type}) -> + case Type of + %% 'double' -> hipe_sparc_registers:is_precoloured_fpr(Reg); + _ -> hipe_sparc_registers:is_precoloured_gpr(Reg) + end. + +mk_g0() -> mk_temp(hipe_sparc_registers:g0(), 'untagged'). +mk_ra() -> mk_temp(hipe_sparc_registers:return_address(), 'untagged'). +mk_rv() -> mk_temp(hipe_sparc_registers:return_value(), 'tagged'). +mk_sp() -> mk_temp(hipe_sparc_registers:stack_pointer(), 'untagged'). +mk_temp1() -> mk_temp(hipe_sparc_registers:temp1(), 'untagged'). +mk_temp2() -> mk_temp(hipe_sparc_registers:temp2(), 'untagged'). + +mk_simm13(Value) -> #sparc_simm13{value=Value}. +mk_uimm5(Value) -> #sparc_uimm5{value=Value}. +mk_uimm22(Value) -> #sparc_uimm22{value=Value}. + +mk_mfa(M, F, A) -> #sparc_mfa{m=M, f=F, a=A}. + +mk_prim(Prim) -> #sparc_prim{prim=Prim}. +is_prim(X) -> case X of #sparc_prim{} -> true; _ -> false end. +prim_prim(#sparc_prim{prim=Prim}) -> Prim. + +mk_sdesc(ExnLab, FSize, Arity, Live) -> + #sparc_sdesc{exnlab=ExnLab, fsize=FSize, arity=Arity, live=Live}. + +mk_alu(AluOp, Src1, Src2, Dst) -> + #alu{aluop=AluOp, src1=Src1, src2=Src2, dst=Dst}. +mk_mov(Src, Dst) -> mk_alu('or', mk_g0(), Src, Dst). + +mk_bp(Cond, Label, Pred) -> #bp{'cond'=Cond, label=Label, pred=Pred}. +mk_b_label(Label) -> mk_bp('a', Label, 1.0). + +-ifdef(notdef). % XXX: only for sparc64, alas +mk_br(RCond, Src, Label, Pred) -> + #br{rcond=RCond, src=Src, label=Label, pred=Pred}. +-endif. + +mk_call_rec(Fun, SDesc, Linkage) -> + #call_rec{'fun'=Fun, sdesc=SDesc, linkage=Linkage}. + +mk_call_tail(Fun, Linkage) -> #call_tail{'fun'=Fun, linkage=Linkage}. + +mk_comment(Term) -> #comment{term=Term}. + +mk_label(Label) -> #label{label=Label}. +is_label(I) -> case I of #label{} -> true; _ -> false end. +label_label(#label{label=Label}) -> Label. + +mk_jmp(Src1, Src2, Labels) -> #jmp{src1=Src1, src2=Src2, labels=Labels}. + +mk_jmpl(Src, SDesc) -> #jmpl{src=Src, sdesc=SDesc}. + +mk_pseudo_bp(Cond, TrueLab, FalseLab, Pred) -> + if Pred >= 0.5 -> + mk_pseudo_bp_simple(negate_cond(Cond), FalseLab, + TrueLab, 1.0-Pred); + true -> + mk_pseudo_bp_simple(Cond, TrueLab, FalseLab, Pred) + end. + +mk_pseudo_bp_simple(Cond, TrueLab, FalseLab, Pred) when Pred =< 0.5 -> + #pseudo_bp{'cond'=Cond, true_label=TrueLab, + false_label=FalseLab, pred=Pred}. + +negate_cond(Cond) -> + case Cond of + 'l' -> 'ge'; % <, >= + 'ge' -> 'l'; % >=, < + 'g' -> 'le'; % >, <= + 'le' -> 'g'; % <=, > + 'e' -> 'ne'; % ==, != + 'ne' -> 'e'; % !=, == + 'gu' -> 'leu'; % >u, <=u + 'leu'-> 'gu'; % <=u, >u + 'geu'-> 'lu'; % >=u, <u + 'lu' -> 'geu'; % <u, >=u + 'vs' -> 'vc'; % overflow, not_overflow + 'vc' -> 'vs' % not_overflow, overflow + end. + +-ifdef(notdef). % XXX: only for sparc64, alas +mk_pseudo_br(RCond, Src, TrueLab, FalseLab, Pred) -> + if Pred >= 0.5 -> + mk_pseudo_br_simple(negate_rcond(RCond), Src, FalseLab, + TrueLab, 1.0-Pred); + true -> + mk_pseudo_br_simple(RCond, Src, TrueLab, FalseLab, Pred) + end. + +mk_pseudo_br_simple(RCond, Src, TrueLab, FalseLab, Pred) when Pred =< 0.5 -> + #pseudo_br{rcond=RCond, src=Src, true_label=TrueLab, + false_label=FalseLab, pred=Pred}. + +negate_rcond(RCond) -> + case RCond of + 'z' -> 'nz'; % ==, != + 'nz' -> 'z'; % !=, == + 'gz' -> 'lez'; % >, <= + 'lez' -> 'gz'; % <=, > + 'gez' -> 'lz'; % >=, < + 'lz' -> 'gez' % <, >= + end. +-endif. + +mk_pseudo_call(FunV, SDesc, ContLab, Linkage) -> + #pseudo_call{funv=FunV, sdesc=SDesc, contlab=ContLab, linkage=Linkage}. +pseudo_call_funv(#pseudo_call{funv=FunV}) -> FunV. +pseudo_call_contlab(#pseudo_call{contlab=ContLab}) -> ContLab. +pseudo_call_linkage(#pseudo_call{linkage=Linkage}) -> Linkage. +pseudo_call_sdesc(#pseudo_call{sdesc=SDesc}) -> SDesc. + +mk_pseudo_call_prepare(NrStkArgs) -> + #pseudo_call_prepare{nrstkargs=NrStkArgs}. +pseudo_call_prepare_nrstkargs(#pseudo_call_prepare{nrstkargs=NrStkArgs}) -> + NrStkArgs. + +mk_pseudo_move(Src, Dst) -> #pseudo_move{src=Src, dst=Dst}. +is_pseudo_move(I) -> case I of #pseudo_move{} -> true; _ -> false end. +pseudo_move_dst(#pseudo_move{dst=Dst}) -> Dst. +pseudo_move_src(#pseudo_move{src=Src}) -> Src. + +mk_pseudo_ret() -> #pseudo_ret{}. + +mk_pseudo_set(Imm, Dst) -> #pseudo_set{imm=Imm, dst=Dst}. + +mk_pseudo_tailcall(FunV, Arity, StkArgs, Linkage) -> + #pseudo_tailcall{funv=FunV, arity=Arity, stkargs=StkArgs, linkage=Linkage}. +pseudo_tailcall_funv(#pseudo_tailcall{funv=FunV}) -> FunV. +pseudo_tailcall_linkage(#pseudo_tailcall{linkage=Linkage}) -> Linkage. +pseudo_tailcall_stkargs(#pseudo_tailcall{stkargs=StkArgs}) -> StkArgs. + +mk_pseudo_tailcall_prepare() -> #pseudo_tailcall_prepare{}. + +mk_rdy(Dst) -> #rdy{dst=Dst}. + +mk_sethi(UImm22, Dst) -> #sethi{uimm22=UImm22, dst=Dst}. +mk_nop() -> mk_sethi(mk_uimm22(0), mk_g0()). + +%%% Load an integer constant into a register. +mk_set(Value, Dst) -> mk_set(Value, Dst, []). + +mk_set(Value, Dst, Tail) -> + if -4096 =< Value, Value < 4096 -> + [mk_alu('or', mk_g0(), mk_simm13(Value), Dst) | Tail]; + true -> + Hi22 = mk_uimm22((Value bsr 10) band 16#003FFFFF), + case (Value band 16#3FF) of + 0 -> + [mk_sethi(Hi22, Dst) | Tail]; + Lo10 -> + [mk_sethi(Hi22, Dst), + mk_alu('or', Dst, mk_simm13(Lo10), Dst) | + Tail] + end + end. + +%%% Add an integer constant. Dst may equal Src, +%%% in which case temp2 may be clobbered. +mk_addi(Src, Value, Dst, Tail) -> + if -4096 =< Value, Value < 4096 -> + [mk_alu('add', Src, mk_simm13(Value), Dst) | Tail]; + true -> + Tmp = + begin + DstReg = temp_reg(Dst), + SrcReg = temp_reg(Src), + if DstReg =:= SrcReg -> mk_temp2(); + true -> Dst + end + end, + mk_set(Value, Tmp, [mk_alu('add', Src, Tmp, Dst) | Tail]) + end. + +mk_store(StOp, Src, Base, Disp) -> + #store{stop=StOp, src=Src, base=Base, disp=Disp}. + +mk_store(StOp, Src, Base, Offset, Scratch, Rest) when is_integer(Offset) -> + if -4096 =< Offset, Offset < 4096 -> + [mk_store(StOp, Src, Base, mk_simm13(Offset)) | Rest]; + true -> + Index = mk_scratch(Scratch), + mk_set(Offset, Index, [mk_store(StOp, Src, Base, Index) | Rest]) + end. + +mk_load(LdOp, Base, Disp, Dst) -> + mk_alu(LdOp, Base, Disp, Dst). + +mk_load(LdOp, Base, Offset, Dst, Scratch, Rest) when is_integer(Offset) -> + if -4096 =< Offset, Offset < 4096 -> + [mk_load(LdOp, Base, mk_simm13(Offset), Dst) | Rest]; + true -> + Index = + begin + DstReg = temp_reg(Dst), + BaseReg = temp_reg(Base), + if DstReg =/= BaseReg -> Dst; + true -> mk_scratch(Scratch) + end + end, + mk_set(Offset, Index, [mk_load(LdOp, Base, Index, Dst) | Rest]) + end. + +mk_scratch(Scratch) -> + case Scratch of + 'temp2' -> mk_temp2(); + 'new' -> mk_new_temp('untagged') + end. + +mk_fp_binary(FpBinOp, Src1, Src2, Dst) -> + #fp_binary{fp_binop=FpBinOp, src1=Src1, src2=Src2, dst=Dst}. + +mk_fp_unary(FpUnOp, Src, Dst) -> #fp_unary{fp_unop=FpUnOp, src=Src, dst=Dst}. + +mk_pseudo_fload(Base, Disp, Dst, IsSingle) -> + #pseudo_fload{base=Base, disp=Disp, dst=Dst, is_single=IsSingle}. + +mk_fload(Base, Disp, Dst, Scratch) when is_integer(Disp) -> + if -4096 =< Disp, Disp < (4096-4) -> + [mk_pseudo_fload(Base, mk_simm13(Disp), Dst, false)]; + true -> + Tmp = mk_scratch(Scratch), + mk_set(Disp, Tmp, + [mk_alu('add', Tmp, Base, Tmp), + mk_pseudo_fload(Tmp, mk_simm13(0), Dst, false)]) + end. + +mk_pseudo_fmove(Src, Dst) -> #pseudo_fmove{src=Src, dst=Dst}. +is_pseudo_fmove(I) -> case I of #pseudo_fmove{} -> true; _ -> false end. +pseudo_fmove_src(#pseudo_fmove{src=Src}) -> Src. +pseudo_fmove_dst(#pseudo_fmove{dst=Dst}) -> Dst. + +mk_pseudo_fstore(Src, Base, Disp) -> + #pseudo_fstore{src=Src, base=Base, disp=Disp}. + +mk_fstore(Src, Base, Disp, Scratch) when is_integer(Disp) -> + if -4096 =< Disp, Disp < (4096-4) -> + [mk_pseudo_fstore(Src, Base, hipe_sparc:mk_simm13(Disp))]; + true -> + Tmp = mk_scratch(Scratch), + mk_set(Disp, Tmp, + [mk_alu('add', Tmp, Base, Tmp), + mk_pseudo_fstore(Src, Tmp, mk_simm13(0))]) + end. + +mk_defun(MFA, Formals, IsClosure, IsLeaf, Code, Data, VarRange, LabelRange) -> + #defun{mfa=MFA, formals=Formals, code=Code, data=Data, + isclosure=IsClosure, isleaf=IsLeaf, + var_range=VarRange, label_range=LabelRange}. +defun_code(#defun{code=Code}) -> Code. +defun_data(#defun{data=Data}) -> Data. +defun_formals(#defun{formals=Formals}) -> Formals. +defun_is_closure(#defun{isclosure=IsClosure}) -> IsClosure. +defun_is_leaf(#defun{isleaf=IsLeaf}) -> IsLeaf. +defun_mfa(#defun{mfa=MFA}) -> MFA. +defun_var_range(#defun{var_range=VarRange}) -> VarRange. diff --git a/lib/hipe/sparc/hipe_sparc.hrl b/lib/hipe/sparc/hipe_sparc.hrl new file mode 100644 index 0000000000..107541f96a --- /dev/null +++ b/lib/hipe/sparc/hipe_sparc.hrl @@ -0,0 +1,116 @@ +%% -*- 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% +%% + +%%%-------------------------------------------------------------------- +%%% Basic Values: +%%% +%%% temp ::= #sparc_temp{reg, type, allocatable} +%%% reg ::= <token from hipe_sparc_registers> +%%% type ::= tagged | untagged | double +%%% allocatable ::= true | false +%%% +%%% sdesc ::= #sparc_sdesc{exnlab, fsize, arity, live} +%%% exnlab ::= [] | label +%%% fsize ::= int32 (frame size in words) +%%% live ::= <tuple of int32> (word offsets) +%%% arity ::= uint8 +%%% +%%% mfa ::= #sparc_mfa{atom, atom, arity} +%%% prim ::= #sparc_prim{atom} + +-record(sparc_mfa, {m::atom(), f::atom(), a::arity()}). +-record(sparc_prim, {prim}). +-record(sparc_sdesc, {exnlab, fsize, arity::arity(), live}). +-record(sparc_temp, {reg, type, allocatable}). +-record(sparc_simm13, {value}). +-record(sparc_uimm5, {value}). +-record(sparc_uimm6, {value}). % shift counts in 64-bit mode +-record(sparc_uimm22, {value}). + +%%% Instruction Operands: +%%% +%%% aluop ::= add | addcc | and | andcc | or | orcc +%%% | xor | xorcc | sub | subcc | mulx | smul +%%% | sll | srl | sra | sllx | srlx | srax +%%% | ldsb | ldsh | ldsw | ldub | lduh | lduw | ldx +%%% (HW has andn{,cc}, orn{,cc}, xnor{,cc}, addc{,cc}, +%%% and subc{,cc}, but we don't use them) +%%% cond ::= n | e | le | l | leu | lu | neg | vs | +%%% | a | ne | g | ge | gu | geu | pos | vc +%%% rcond ::= z | lez | lz | nz | gz | gez +%%% stop ::= stb | stw | stx (HW has sth, but we don't use it) +%%% +%%% immediate ::= int32 | atom | {label, label_type} +%%% label_type ::= constant | closure | c_const +%%% +%%% dst ::= temp +%%% src ::= temp +%%% src1 ::= temp +%%% src2 ::= temp +%%% | simm13 (only in alu.src2, jmp.src2, jmpl.src2) +%%% base ::= src1 +%%% disp ::= src2 +%%% +%%% fun ::= mfa | prim +%%% funv ::= fun | temp +%%% +%%% fp_binop ::= faddd | fdivd | fmuld | fsubd +%%% fp_unop ::= fitod | fmovd | fnegd + +%%% Instructions: + +-record(alu, {aluop, src1, src2, dst}). +-record(bp, {'cond', label, pred}). % local jump on %icc +-ifdef(notdef). % XXX: only for sparc64, alas +-record(br, {rcond, src, label, pred}). % local jump on register +-endif. +-record(call_rec, {'fun', sdesc, linkage}). % known recursive call +-record(call_tail, {'fun', linkage}). % known tailcall +-record(comment, {term}). +-record(jmp, {src1, src2, labels}). % return, switch, or computed tailcall +-record(jmpl, {src, sdesc}). % computed recursive call (jmpl [src+0],%o7) +-record(label, {label}). +-record(pseudo_bp, {'cond', true_label, false_label, pred}). +%%-record(pseudo_br, {rcond, src, true_label, false_label, pred}). +-record(pseudo_call, {funv, sdesc, contlab, linkage}). +-record(pseudo_call_prepare, {nrstkargs}). +-record(pseudo_move, {src, dst}). +-record(pseudo_ret, {}). +-record(pseudo_set, {imm, dst}). +-record(pseudo_tailcall, {funv, arity, stkargs, linkage}). +-record(pseudo_tailcall_prepare, {}). +-record(rdy, {dst}). +-record(sethi, {uimm22, dst}). +-record(store, {stop, src, base, disp}). +-record(fp_binary, {fp_binop, src1, src2, dst}). +-record(fp_unary, {fp_unop, src, dst}). +-record(pseudo_fload, {base, disp, dst, is_single}). +-record(pseudo_fmove, {src, dst}). +-record(pseudo_fstore, {src, base, disp}). + +%%% Function definitions. + +-include("../misc/hipe_consttab.hrl"). + +-record(defun, {mfa :: mfa(), formals, code, + data :: hipe_consttab(), + isclosure :: boolean(), + isleaf :: boolean(), + var_range, label_range}). diff --git a/lib/hipe/sparc/hipe_sparc_assemble.erl b/lib/hipe/sparc/hipe_sparc_assemble.erl new file mode 100644 index 0000000000..b534fe20ec --- /dev/null +++ b/lib/hipe/sparc/hipe_sparc_assemble.erl @@ -0,0 +1,588 @@ +%% -*- 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% +%% + +-module(hipe_sparc_assemble). +-export([assemble/4]). + +-include("../main/hipe.hrl"). % for VERSION_STRING, when_option +-include("hipe_sparc.hrl"). +-include("../../kernel/src/hipe_ext_format.hrl"). +-include("../rtl/hipe_literals.hrl"). +-include("../misc/hipe_sdi.hrl"). +-undef(ASSERT). +-define(ASSERT(G), if G -> [] ; true -> exit({assertion_failed,?MODULE,?LINE,??G}) end). + +assemble(CompiledCode, Closures, Exports, Options) -> + print("****************** Assembling *******************\n", [], Options), + %% + Code = [{MFA, + hipe_sparc:defun_code(Defun), + hipe_sparc:defun_data(Defun)} + || {MFA, Defun} <- CompiledCode], + %% + {ConstAlign,ConstSize,ConstMap,RefsFromConsts} = + hipe_pack_constants:pack_constants(Code, 4), + %% + {CodeSize,CodeBinary,AccRefs,LabelMap,ExportMap} = + encode(translate(Code, ConstMap), Options), + print("Total num bytes=~w\n", [CodeSize], Options), + %% + SC = hipe_pack_constants:slim_constmap(ConstMap), + DataRelocs = mk_data_relocs(RefsFromConsts, LabelMap), + SSE = slim_sorted_exportmap(ExportMap,Closures,Exports), + SlimRefs = hipe_pack_constants:slim_refs(AccRefs), + Bin = term_to_binary([{?VERSION_STRING(),?HIPE_SYSTEM_CRC}, + ConstAlign, ConstSize, + SC, + DataRelocs, % nee LM, LabelMap + SSE, + CodeSize,CodeBinary,SlimRefs, + 0,[] % ColdCodeSize, SlimColdRefs + ]), + %% + Bin. + +%%% +%%% Assembly Pass 1. +%%% Process initial {MFA,Code,Data} list. +%%% Translate each MFA's body, choosing operand & instruction kinds. +%%% +%%% Assembly Pass 2. +%%% Perform short/long form optimisation for jumps. +%%% +%%% Result is {MFA,NewCode,CodeSize,LabelMap} list. +%%% + +translate(Code, ConstMap) -> + translate_mfas(Code, ConstMap, []). + +translate_mfas([{MFA,Insns,_Data}|Code], ConstMap, NewCode) -> + {NewInsns,CodeSize,LabelMap} = + translate_insns(Insns, MFA, ConstMap, hipe_sdi:pass1_init(), 0, []), + translate_mfas(Code, ConstMap, [{MFA,NewInsns,CodeSize,LabelMap}|NewCode]); +translate_mfas([], _ConstMap, NewCode) -> + lists:reverse(NewCode). + +translate_insns([I|Insns], MFA, ConstMap, SdiPass1, Address, NewInsns) -> + NewIs = translate_insn(I, MFA, ConstMap), + add_insns(NewIs, Insns, MFA, ConstMap, SdiPass1, Address, NewInsns); +translate_insns([], _MFA, _ConstMap, SdiPass1, Address, NewInsns) -> + {LabelMap,CodeSizeIncr} = hipe_sdi:pass2(SdiPass1), + {lists:reverse(NewInsns), Address+CodeSizeIncr, LabelMap}. + +add_insns([I|Is], Insns, MFA, ConstMap, SdiPass1, Address, NewInsns) -> + NewSdiPass1 = + case I of + {'.label',L,_} -> + hipe_sdi:pass1_add_label(SdiPass1, Address, L); + {bp_sdi,{_,_,{label,L}},_} -> % BP has 19-bit offset + SdiInfo = #sdi_info{incr=(12-4),lb=-16#40000*4,ub=16#3FFFF*4}, + hipe_sdi:pass1_add_sdi(SdiPass1, Address, L, SdiInfo); + %% {br_sdi,_,_} -> add_insns_br(I, SdiPass1, Address); + _ -> + SdiPass1 + end, + Address1 = Address + insn_size(I), + add_insns(Is, Insns, MFA, ConstMap, NewSdiPass1, Address1, [I|NewInsns]); +add_insns([], Insns, MFA, ConstMap, SdiPass1, Address, NewInsns) -> + translate_insns(Insns, MFA, ConstMap, SdiPass1, Address, NewInsns). + +-ifdef(notdef). % XXX: only for sparc64, alas +add_insns_br(I, SdiPass1, Address) -> % BR has 16-bit offset + {br_sdi,{_,_,_,{label,L}},_} = I, + SdiInfo = #sdi_info{incr=(12-4),lb=-16#8000*4,ub=16#7FFF*4}, + hipe_sdi:pass1_add_sdi(SdiPass1, Address, L, SdiInfo). +-endif. + +insn_size(I) -> + case I of + {'.label',_,_} -> 0; + {'.reloc',_,_} -> 0; + _ -> 4 % b{p,r}_sdi included in this case + end. + +translate_insn(I, MFA, ConstMap) -> % -> [{Op,Opnd,OrigI}] + case I of + #alu{} -> do_alu(I); + #bp{} -> do_bp(I); + %% #br{} -> do_br(I); + #call_rec{} -> do_call_rec(I); + #call_tail{} -> do_call_tail(I); + #comment{} -> []; + #jmp{} -> do_jmp(I); + #jmpl{} -> do_jmpl(I); + #label{} -> do_label(I); + %% pseudo_bp: eliminated before assembly + %% pseudo_br: eliminated before assembly + %% pseudo_call: eliminated before assembly + %% pseudo_call_prepare: eliminated before assembly + %% pseudo_move: eliminated before assembly + %% pseudo_ret: eliminated before assembly + #pseudo_set{} -> do_pseudo_set(I, MFA, ConstMap); + %% pseudo_tailcall: eliminated before assembly + %% pseudo_tailcall_prepare: eliminated before assembly + #rdy{} -> do_rdy(I); + #sethi{} -> do_sethi(I); + #store{} -> do_store(I); + #fp_binary{} -> do_fp_binary(I); + #fp_unary{} -> do_fp_unary(I); + #pseudo_fload{} -> do_pseudo_fload(I); + %% #pseudo_fmove: eliminated before assembly + #pseudo_fstore{} -> do_pseudo_fstore(I); + _ -> exit({?MODULE,translate_insn,I}) + end. + +do_alu(I) -> + #alu{aluop=AluOp,src1=Src1,src2=Src2,dst=Dst} = I, + NewDst = do_reg(Dst), + NewSrc1 = do_reg(Src1), + NewSrc2 = do_reg_or_imm(Src2), + [{AluOp, {NewSrc1,NewSrc2,NewDst}, I}]. + +do_bp(I) -> + #bp{'cond'=Cond,pred=Pred,label=Label} = I, + NewLabel = {label,Label}, + case Cond of + 'a' -> + [{ba, NewLabel, I}]; % 3 more offset bits + _ -> + NewCond = {'cond',Cond}, + NewPred = {pred,Pred}, + [{bp_sdi, {NewCond,NewPred,NewLabel}, I}] + end. + +-ifdef(notdef). % XXX: only for sparc64, alas +do_br(I) -> + #br{rcond=RCond,pred=Pred,src=Src,label=Label} = I, + NewRCond = {rcond,RCond}, + NewPred = {pred,Pred}, + NewSrc = do_reg(Src), + NewLabel = {label,Label}, + [{br_sdi, {NewRCond,NewPred,NewSrc,NewLabel}, I}]. +-endif. + +do_call_rec(I) -> + #call_rec{'fun'=Fun,sdesc=SDesc,linkage=Linkage} = I, + [{'.reloc', {call,Fun,Linkage}, #comment{term='fun'}}, + {'.reloc', {sdesc,SDesc}, #comment{term=sdesc}}, + {call, {disp30,0}, I}]. + +do_call_tail(I) -> + #call_tail{'fun'=Fun,linkage=Linkage} = I, + [{'.reloc', {call,Fun,Linkage}, #comment{term='fun'}}, + {call, {disp30,0}, I}]. + +do_jmp(I) -> + #jmp{src1=Src1,src2=Src2} = I, + NewSrc1 = do_reg(Src1), + NewSrc2 = do_reg_or_imm(Src2), + NewDst = {r,0}, + [{jmpl, {NewSrc1,NewSrc2,NewDst}, I}]. + +do_jmpl(I) -> + #jmpl{src=Src,sdesc=SDesc} = I, + NewSrc1 = do_reg(Src), + NewSrc2 = {simm13,0}, + NewDst = {r,15}, % %o7 + [{'.reloc', {sdesc,SDesc}, #comment{term=sdesc}}, + {jmpl, {NewSrc1,NewSrc2,NewDst}, I}]. + +do_label(I) -> + #label{label=Label} = I, + [{'.label', Label, I}]. + +do_pseudo_set(I, MFA, ConstMap) -> + #pseudo_set{imm=Imm,dst=Dst} = I, + RelocData = + case Imm of + Atom when is_atom(Atom) -> + {load_atom, Atom}; +%%% {mfa,MFAorPrim,Linkage} -> +%%% Tag = +%%% case Linkage of +%%% remote -> remote_function; +%%% not_remote -> local_function +%%% end, +%%% {load_address, {Tag,untag_mfa_or_prim(MFAorPrim)}}; + {Label,constant} -> + ConstNo = find_const({MFA,Label}, ConstMap), + {load_address, {constant,ConstNo}}; + {Label,closure} -> + {load_address, {closure,Label}}; + {Label,c_const} -> + {load_address, {c_const,Label}} + end, + NewDst = do_reg(Dst), + [{'.reloc', RelocData, #comment{term=reloc}}, + {sethi, {{uimm22,0},NewDst}, I}, + {'or', {NewDst,{simm13,0},NewDst}, I}]. + +do_rdy(I) -> + #rdy{dst=Dst} = I, + NewDst = do_reg(Dst), + [{rd, {y,NewDst}, I}]. + +do_sethi(I) -> + #sethi{uimm22=#sparc_uimm22{value=UImm22},dst=Dst} = I, + NewUImm22 = {uimm22,UImm22}, + NewDst = do_reg(Dst), + [{sethi, {NewUImm22,NewDst}, I}]. + +do_store(I) -> + #store{stop=StOp,src=Src,base=Base,disp=Disp} = I, + NewSrc = do_reg(Src), + NewBase = do_reg(Base), + NewDisp = do_reg_or_imm(Disp), + [{StOp, {NewSrc,NewBase,NewDisp}, I}]. + +do_fp_binary(I) -> + #fp_binary{fp_binop=FpBinOp,src1=Src1,src2=Src2,dst=Dst} = I, + NewSrc1 = do_fpreg(Src1), + NewSrc2 = do_fpreg(Src2), + NewDst = do_fpreg(Dst), + [{FpBinOp, {NewSrc1,NewSrc2,NewDst}, I}]. + +do_fp_unary(I) -> + #fp_unary{fp_unop=FpUnOp,src=Src,dst=Dst} = I, + NewSrc = do_fpreg(Src), + NewDst = do_fpreg(Dst), + [{FpUnOp, {NewSrc,NewDst}, I}]. + +do_pseudo_fload(I) -> + #pseudo_fload{base=Base,disp=Disp,dst=Dst,is_single=IsSingle} = I, + NewBase = do_reg(Base), + #sparc_simm13{value=RawDisp} = Disp, + {fr,RawDst} = FrRawDst = do_fpreg(Dst), + case IsSingle of + true -> + [{'ldf', {NewBase,{simm13,RawDisp},FrRawDst}, I}]; + _ -> + [{'ldf', {NewBase,{simm13,RawDisp},FrRawDst}, I}, + {'ldf', {NewBase,{simm13,RawDisp+4},{fr,RawDst+1}}, I}] + end. + +do_pseudo_fstore(I) -> + #pseudo_fstore{src=Src,base=Base,disp=Disp} = I, + {fr,RawSrc} = FrRawSrc = do_fpreg(Src), + NewBase = do_reg(Base), + #sparc_simm13{value=RawDisp} = Disp, + [{'stf', {FrRawSrc,NewBase,{simm13,RawDisp}}, I}, + {'stf', {{fr,RawSrc+1},NewBase,{simm13,RawDisp+4}}, I}]. + +%% map a virtual double-precision fp reg in [0,15] to its +%% corresponding single-precision fp reg in [0,2,4,...,28,30] +do_fpreg(#sparc_temp{reg=Reg,type='double'}) + when is_integer(Reg), 0 =< Reg, Reg < 16 -> + {fr,2*Reg}. + +do_reg(#sparc_temp{reg=Reg,type=Type}) + when is_integer(Reg), 0 =< Reg, Reg < 32, Type =/= 'double' -> + {r,Reg}. + +do_reg_or_imm(Src) -> + case Src of + #sparc_temp{} -> + do_reg(Src); + #sparc_simm13{value=Value} when is_integer(Value), -4096 =< Value, Value =< 4095 -> + {simm13, Value band 16#1fff}; + #sparc_uimm5{value=Value} when is_integer(Value), 0 =< Value, Value =< 31 -> + {uimm5, Value}; + #sparc_uimm6{value=Value} when is_integer(Value), 0 =< Value, Value =< 63 -> + {uimm6, Value} + end. + +%%% +%%% Assembly Pass 3. +%%% Process final {MFA,Code,CodeSize,LabelMap} list from pass 2. +%%% Translate to a single binary code segment. +%%% Collect relocation patches. +%%% Build ExportMap (MFA-to-address mapping). +%%% Combine LabelMaps to a single one (for mk_data_relocs/2 compatibility). +%%% Return {CombinedCodeSize,BinaryCode,Relocs,CombinedLabelMap,ExportMap}. +%%% + +encode(Code, Options) -> + CodeSize = compute_code_size(Code, 0), + ExportMap = build_export_map(Code, 0, []), + {AccCode,Relocs} = encode_mfas(Code, 0, [], [], Options), + CodeBinary = list_to_binary(lists:reverse(AccCode)), + ?ASSERT(CodeSize =:= byte_size(CodeBinary)), + CombinedLabelMap = combine_label_maps(Code, 0, gb_trees:empty()), + {CodeSize,CodeBinary,Relocs,CombinedLabelMap,ExportMap}. + +compute_code_size([{_MFA,_Insns,CodeSize,_LabelMap}|Code], Size) -> + compute_code_size(Code, Size+CodeSize); +compute_code_size([], Size) -> Size. + +build_export_map([{{M,F,A},_Insns,CodeSize,_LabelMap}|Code], Address, ExportMap) -> + build_export_map(Code, Address+CodeSize, [{Address,M,F,A}|ExportMap]); +build_export_map([], _Address, ExportMap) -> ExportMap. + +combine_label_maps([{MFA,_Insns,CodeSize,LabelMap}|Code], Address, CLM) -> + NewCLM = merge_label_map(gb_trees:to_list(LabelMap), MFA, Address, CLM), + combine_label_maps(Code, Address+CodeSize, NewCLM); +combine_label_maps([], _Address, CLM) -> CLM. + +merge_label_map([{Label,Offset}|Rest], MFA, Address, CLM) -> + NewCLM = gb_trees:insert({MFA,Label}, Address+Offset, CLM), + merge_label_map(Rest, MFA, Address, NewCLM); +merge_label_map([], _MFA, _Address, CLM) -> CLM. + +encode_mfas([{MFA,Insns,CodeSize,LabelMap}|Code], Address, AccCode, Relocs, Options) -> + print("Generating code for: ~w\n", [MFA], Options), + print("Offset | Opcode | Instruction\n", [], Options), + {Address1,Relocs1,AccCode1} = + encode_insns(Insns, Address, Address, LabelMap, Relocs, AccCode, Options), + ExpectedAddress = Address + CodeSize, + ?ASSERT(Address1 =:= ExpectedAddress), + print("Finished.\n", [], Options), + encode_mfas(Code, Address1, AccCode1, Relocs1, Options); +encode_mfas([], _Address, AccCode, Relocs, _Options) -> + {AccCode,Relocs}. + +encode_insns([I|Insns], Address, FunAddress, LabelMap, Relocs, AccCode, Options) -> + case I of + {'.label',L,_} -> + LabelAddress = gb_trees:get(L, LabelMap) + FunAddress, + ?ASSERT(Address =:= LabelAddress), % sanity check + print_insn(Address, [], I, Options), + encode_insns(Insns, Address, FunAddress, LabelMap, Relocs, AccCode, Options); + {'.reloc',Data,_} -> + Reloc = encode_reloc(Data, Address, FunAddress, LabelMap), + encode_insns(Insns, Address, FunAddress, LabelMap, [Reloc|Relocs], AccCode, Options); + {bp_sdi,_,_} -> + encode_insns(fix_bp_sdi(I, Insns, Address, FunAddress, LabelMap), + Address, FunAddress, LabelMap, Relocs, AccCode, Options); + %% {br_sdi,_,_} -> + %% encode_insns(fix_br_sdi(I, Insns, Address, FunAddress, LabelMap), + %% Address, FunAddress, LabelMap, Relocs, AccCode, Options); + _ -> + {Op,Arg,_} = fix_jumps(I, Address, FunAddress, LabelMap), + Word = hipe_sparc_encode:insn_encode(Op, Arg), + print_insn(Address, Word, I, Options), + Segment = <<Word:32/integer-big>>, + NewAccCode = [Segment|AccCode], + encode_insns(Insns, Address+4, FunAddress, LabelMap, Relocs, NewAccCode, Options) + end; +encode_insns([], Address, _FunAddress, _LabelMap, Relocs, AccCode, _Options) -> + {Address,Relocs,AccCode}. + +encode_reloc(Data, Address, FunAddress, LabelMap) -> + case Data of + {call,MFAorPrim,Linkage} -> + %% call_rec and call_tail are patched the same, so no need to distinguish + %% call from tailcall + PatchTypeExt = + case Linkage of + remote -> ?CALL_REMOTE; + not_remote -> ?CALL_LOCAL + end, + {PatchTypeExt, Address, untag_mfa_or_prim(MFAorPrim)}; + {load_atom,Atom} -> + {?LOAD_ATOM, Address, Atom}; + {load_address,X} -> + {?LOAD_ADDRESS, Address, X}; + {sdesc,SDesc} -> + #sparc_sdesc{exnlab=ExnLab,fsize=FSize,arity=Arity,live=Live} = SDesc, + ExnRA = + case ExnLab of + [] -> []; % don't cons up a new one + ExnLab -> gb_trees:get(ExnLab, LabelMap) + FunAddress + end, + {?SDESC, Address, + ?STACK_DESC(ExnRA, FSize, Arity, Live)} + end. + +untag_mfa_or_prim(#sparc_mfa{m=M,f=F,a=A}) -> {M,F,A}; +untag_mfa_or_prim(#sparc_prim{prim=Prim}) -> Prim. + +fix_bp_sdi(I, Insns, InsnAddress, FunAddress, LabelMap) -> + {bp_sdi,Opnds,OrigI} = I, + {{'cond',Cond},{pred,Pred},Label} = Opnds, + {label,L} = Label, + LabelAddress = gb_trees:get(L, LabelMap) + FunAddress, + BD = (LabelAddress - InsnAddress) div 4, + if BD >= -16#40000, BD =< 16#3FFFF -> + [{bp, Opnds, OrigI} | Insns]; + true -> + %% bp<cond>,<pred> L; Delay + %% --> + %% bp<!cond>,<!pred> 1f; Delay; ba L; nop; 1: + [Delay|Rest] = Insns, + NewCond = hipe_sparc:negate_cond(Cond), + NewPred = 1.0 - Pred, + [{bp, + {{'cond',NewCond},{pred,NewPred},'.+16'}, + #bp{'cond'=NewCond,pred=NewPred,label='.+16'}}, % pp will be ugly + Delay, % should be a NOP + {ba, Label, #bp{'cond'='a',pred=1.0,label=L}}, + {sethi, {{uimm22,0},{r,0}}, #comment{term=nop}} | + Rest] + end. + +-ifdef(notdef). % XXX: only for sparc64, alas +fix_br_sdi(I, Insns, InsnAddress, FunAddress, LabelMap) -> + {br_sdi,Opnds,OrigI} = I, + {{rcond,RCond},{pred,Pred},Src,{label,L}} = Opnds, + LabelAddress = gb_trees:get(L, LabelMap) + FunAddress, + BD = (LabelAddress - InsnAddress) div 4, + if BD >= -16#8000, BD =< 16#7FFF -> + [{br, Opnds, OrigI} | Insns]; + true -> + %% br<rcond>,<pred> reg, L; Delay + %% --> + %% br<!rcond>,<!pred> reg, 1f; Delay; ba L; nop; 1: + [Delay|Rest] = Insns, + {reg,SrcReg} = Src, + NewRCond = hipe_sparc:negate_rcond(RCond), + NewPred = 1.0 - Pred, + [{br, + {{rcond,NewRCond},{pred,NewPred},Src,'.+16'}, + #br{rcond=NewRCond,pred=NewPred,src=SrcReg,label='.+16'}}, % pp will be ugly + Delay, % should be a NOP + {ba, {label,L}, #bp{'cond'='a',pred=1.0,label=L}}, + {sethi, {{uimm22,0},{r,0}}, #comment{term=nop}} | + Rest] + end. +-endif. + +fix_jumps(I, InsnAddress, FunAddress, LabelMap) -> + case I of + {ba, {label,L}, OrigI} -> + LabelAddress = gb_trees:get(L, LabelMap) + FunAddress, + BD = (LabelAddress - InsnAddress) div 4, + %% ensure BD fits in a 22 bit sign-extended field + ?ASSERT(BD =< 16#1FFFFF), + ?ASSERT(BD >= -16#200000), + {ba, {disp22,BD band 16#3FFFFF}, OrigI}; + {bp, {Cond,Pred,Target}, OrigI} -> + LabelAddress = + case Target of + {label,L} -> gb_trees:get(L, LabelMap) + FunAddress; + '.+16' -> InsnAddress + 16 + end, + BD = (LabelAddress - InsnAddress) div 4, + %% ensure BD fits in a 19 bit sign-extended field + ?ASSERT(BD =< 16#3FFFF), + ?ASSERT(BD >= -16#40000), + {bp, {Cond,px(Pred),{disp19,BD band 16#7FFFF}}, OrigI}; + %% {br, _, _} -> fix_br(I, InsnAddress, FunAddress, LabelMap); + _ -> I + end. + +-ifdef(notdef). % XXX: only for sparc64, alas +fix_br(I, InsnAddress, FunAddress, LabelMap) -> + {br, {RCond,Pred,Src,Target}, OrigI} = I, + LabelAddress = + case Target of + {label,L} -> gb_trees:get(L, LabelMap) + FunAddress; + '.+16' -> InsnAddress + 16 + end, + BD = (LabelAddress - InsnAddress) div 4, + %% ensure BD fits in a 16 bit sign-extended field + ?ASSERT(BD =< 16#7FFF), + ?ASSERT(BD >= -16#8000), + {br, {RCond,px(Pred),Src,{disp16,BD band 16#FFFF}}, OrigI}. +-endif. + +px({pred,Pred}) -> % XXX: use pt/pn throughout entire backend + {pred, if Pred >= 0.5 -> 'pt'; true -> 'pn' end}. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +mk_data_relocs(RefsFromConsts, LabelMap) -> + lists:flatten(mk_data_relocs(RefsFromConsts, LabelMap, [])). + +mk_data_relocs([{MFA,Labels} | Rest], LabelMap, Acc) -> + Map = [case Label of + {L,Pos} -> + Offset = find({MFA,L}, LabelMap), + {Pos,Offset}; + {sorted,Base,OrderedLabels} -> + {sorted, Base, [begin + Offset = find({MFA,L}, LabelMap), + {Order, Offset} + end + || {L,Order} <- OrderedLabels]} + end + || Label <- Labels], + %% msg("Map: ~w Map\n",[Map]), + mk_data_relocs(Rest, LabelMap, [Map,Acc]); +mk_data_relocs([],_,Acc) -> Acc. + +find({_MFA,_L} = MFAL, LabelMap) -> + gb_trees:get(MFAL, LabelMap). + +slim_sorted_exportmap([{Addr,M,F,A}|Rest], Closures, Exports) -> + IsClosure = lists:member({M,F,A}, Closures), + IsExported = is_exported(F, A, Exports), + [Addr,M,F,A,IsClosure,IsExported | slim_sorted_exportmap(Rest, Closures, Exports)]; +slim_sorted_exportmap([],_,_) -> []. + +is_exported(F, A, Exports) -> lists:member({F,A}, Exports). + +%%% +%%% Assembly listing support (pp_asm option). +%%% + +print(String, Arglist, Options) -> + ?when_option(pp_asm, Options, io:format(String, Arglist)). + +print_insn(Address, Word, I, Options) -> + ?when_option(pp_asm, Options, print_insn_2(Address, Word, I)). + +print_insn_2(Address, Word, {_,_,OrigI}) -> + io:format("~8.16.0b | ", [Address]), + print_code_list(word_to_bytes(Word), 0), + hipe_sparc_pp:pp_insn(OrigI). + +word_to_bytes(W) -> + case W of + [] -> []; % label or other pseudo instruction + _ -> [(W bsr 24) band 16#FF, (W bsr 16) band 16#FF, + (W bsr 8) band 16#FF, W band 16#FF] + end. + +print_code_list([Byte|Rest], Len) -> + print_byte(Byte), + print_code_list(Rest, Len+1); +print_code_list([], Len) -> + fill_spaces(8-(Len*2)), + io:format(" | "). + +print_byte(Byte) -> + io:format("~2.16.0b", [Byte band 16#FF]). + +fill_spaces(N) when N > 0 -> + io:format(" "), + fill_spaces(N-1); +fill_spaces(0) -> + []. + +%%% +%%% Lookup a constant in a ConstMap. +%%% + +find_const({MFA,Label},[{pcm_entry,MFA,Label,ConstNo,_,_,_}|_]) -> + ConstNo; +find_const(N,[_|R]) -> + find_const(N,R); +find_const(C,[]) -> + ?EXIT({constant_not_found,C}). diff --git a/lib/hipe/sparc/hipe_sparc_cfg.erl b/lib/hipe/sparc/hipe_sparc_cfg.erl new file mode 100644 index 0000000000..d938a3bdf1 --- /dev/null +++ b/lib/hipe/sparc/hipe_sparc_cfg.erl @@ -0,0 +1,134 @@ +%% -*- 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% +%% + +-module(hipe_sparc_cfg). + +-export([init/1, + labels/1, start_label/1, + succ/2, + bb/2, bb_add/3]). +-export([postorder/1, reverse_postorder/1]). +-export([linearise/1]). +-export([params/1]). +-export([arity/1]). % for linear scan + +-define(SPARC_CFG, true). % needed for cfg.inc + +-include("../main/hipe.hrl"). +-include("hipe_sparc.hrl"). +-include("../flow/cfg.hrl"). +-include("../flow/cfg.inc"). + +%%---------------------------------------------------------------------------- +%% CFG interface to SPARC +%%---------------------------------------------------------------------------- + +init(Defun) -> + Code = hipe_sparc:defun_code(Defun), + StartLab = hipe_sparc:label_label(hd(Code)), + Data = hipe_sparc:defun_data(Defun), + IsClosure = hipe_sparc:defun_is_closure(Defun), + Name = hipe_sparc:defun_mfa(Defun), + IsLeaf = hipe_sparc:defun_is_leaf(Defun), + Formals = hipe_sparc:defun_formals(Defun), + CFG = mk_empty_cfg(Name, StartLab, Data, IsClosure, IsLeaf, Formals), + take_bbs(Code, CFG). + +is_branch(I) -> + case I of + #bp{'cond'='a'} -> true; + %% not br + #call_tail{} -> true; + #jmp{} -> true; + %% not jmpl + #pseudo_bp{} -> true; + %% #pseudo_br{} -> true; + #pseudo_call{} -> true; + #pseudo_ret{} -> true; + #pseudo_tailcall{} -> true; + _ -> false + end. + +branch_successors(Branch) -> + case Branch of + #bp{'cond'='a',label=Label} -> [Label]; + #call_tail{} -> []; + #jmp{labels=Labels} -> Labels; + #pseudo_bp{true_label=TrueLab,false_label=FalseLab} -> [FalseLab,TrueLab]; + %% #pseudo_br{true_label=TrueLab,false_label=FalseLab} -> [FalseLab,TrueLab]; + #pseudo_call{contlab=ContLab, sdesc=#sparc_sdesc{exnlab=ExnLab}} -> + case ExnLab of + [] -> [ContLab]; + _ -> [ContLab,ExnLab] + end; + #pseudo_ret{} -> []; + #pseudo_tailcall{} -> [] + end. + +-ifdef(REMOVE_TRIVIAL_BBS_NEEDED). +fails_to(_Instr) -> []. +-endif. + +-ifdef(notdef). +redirect_jmp(I, Old, New) -> + case I of + #b_label{label=Label} -> + if Old =:= Label -> I#b_label{label=New}; + true -> I + end; + #pseudo_bc{true_label=TrueLab, false_label=FalseLab} -> + I1 = if Old =:= TrueLab -> I#pseudo_bc{true_label=New}; + true -> I + end, + if Old =:= FalseLab -> I1#pseudo_bc{false_label=New}; + true -> I1 + end; + %% handle pseudo_call too? + _ -> I + end. +-endif. + +mk_goto(Label) -> + hipe_sparc:mk_b_label(Label). + +is_label(I) -> + hipe_sparc:is_label(I). + +label_name(Label) -> + hipe_sparc:label_label(Label). + +mk_label(Name) -> + hipe_sparc:mk_label(Name). + +linearise(CFG) -> % -> defun, not insn list + MFA = function(CFG), + Formals = params(CFG), + Code = linearize_cfg(CFG), + Data = data(CFG), + VarRange = hipe_gensym:var_range(sparc), + LabelRange = hipe_gensym:label_range(sparc), + IsClosure = is_closure(CFG), + IsLeaf = is_leaf(CFG), + hipe_sparc:mk_defun(MFA, Formals, IsClosure, IsLeaf, + Code, Data, VarRange, LabelRange). + +arity(CFG) -> + {_M, _F, A} = function(CFG), + A. diff --git a/lib/hipe/sparc/hipe_sparc_defuse.erl b/lib/hipe/sparc/hipe_sparc_defuse.erl new file mode 100644 index 0000000000..d59ad436b5 --- /dev/null +++ b/lib/hipe/sparc/hipe_sparc_defuse.erl @@ -0,0 +1,143 @@ +%% -*- erlang-indent-level: 2 -*- +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2007-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_sparc_defuse). +-export([insn_def_all/1, insn_use_all/1]). +-export([insn_def_gpr/1, insn_use_gpr/1]). +-export([insn_def_fpr/1, insn_use_fpr/1]). +-include("hipe_sparc.hrl"). + +%%% +%%% Defs and uses for both general-purpose and floating-point registers. +%%% This is needed for the frame module, alas. +%%% +insn_def_all(I) -> + addtemps(insn_def_fpr(I), insn_def_gpr(I)). + +insn_use_all(I) -> + addtemps(insn_use_fpr(I), insn_use_gpr(I)). + +%%% +%%% Defs and uses for general-purpose (integer) registers only. +%%% +insn_def_gpr(I) -> + case I of + #alu{dst=Dst} -> [Dst]; + %% #jmpl{} -> [hipe_sparc:mk_ra()]; % XXX: can jmpl occur this early? + #pseudo_call{} -> call_clobbered_gpr(); + #pseudo_move{dst=Dst} -> [Dst]; + #pseudo_set{dst=Dst} -> [Dst]; + #pseudo_tailcall_prepare{} -> tailcall_clobbered_gpr(); + #rdy{dst=Dst} -> [Dst]; + #sethi{dst=Dst} -> [Dst]; + _ -> [] + end. + +call_clobbered_gpr() -> + [hipe_sparc:mk_temp(R, T) + || {R,T} <- hipe_sparc_registers:call_clobbered() ++ all_fp_pseudos()]. + +all_fp_pseudos() -> []. % XXX: for now + +tailcall_clobbered_gpr() -> + [hipe_sparc:mk_temp(R, T) + || {R,T} <- hipe_sparc_registers:tailcall_clobbered() ++ all_fp_pseudos()]. + +insn_use_gpr(I) -> + case I of + #alu{src1=Src1,src2=Src2} -> addsrc(Src2, [Src1]); + %% #br{src=Src} -> [Src]; % XXX: can br occur this early? + #jmp{src1=Src1,src2=Src2} -> addsrc(Src2, [Src1]); + %% #jmpl{src=Src} -> [Src]; % XXX: can jmpl occur this early? + %% #pseudo_br{src=Src} -> [Src]; + #pseudo_call{funv=FunV,sdesc=#sparc_sdesc{arity=Arity}} -> + funv_use(FunV, arity_use_gpr(Arity)); + #pseudo_move{src=Src} -> [Src]; + #pseudo_ret{} -> [hipe_sparc:mk_rv()]; + #pseudo_tailcall{funv=FunV,arity=Arity,stkargs=StkArgs} -> + addsrcs(StkArgs, addtemps(tailcall_clobbered_gpr(), funv_use(FunV, arity_use_gpr(Arity)))); + #store{src=Src,base=Base,disp=Disp} -> + addtemp(Src, addsrc(Disp, [Base])); + #pseudo_fload{base=Base} -> [Base]; + #pseudo_fstore{base=Base} -> [Base]; + _ -> [] + end. + +arity_use_gpr(Arity) -> + [hipe_sparc:mk_temp(R, 'tagged') + || R <- hipe_sparc_registers:args(Arity)]. + +funv_use(FunV, Set) -> + case FunV of + #sparc_temp{} -> addtemp(FunV, Set); + _ -> Set + end. + +addsrcs([Arg|Args], Set) -> + addsrcs(Args, addsrc(Arg, Set)); +addsrcs([], Set) -> + Set. + +addsrc(Src, Set) -> + case Src of + #sparc_temp{} -> addtemp(Src, Set); + _ -> Set + end. + +%%% +%%% Defs and uses for floating-point registers only. +%%% +insn_def_fpr(I) -> + case I of + #pseudo_call{} -> call_clobbered_fpr(); + #fp_binary{dst=Dst} -> [Dst]; + #fp_unary{dst=Dst} -> [Dst]; + #pseudo_fload{dst=Dst} -> [Dst]; + #pseudo_fmove{dst=Dst} -> [Dst]; + _ -> [] + end. + +call_clobbered_fpr() -> + [hipe_sparc:mk_temp(R, 'double') || R <- hipe_sparc_registers:allocatable_fpr()]. + +insn_use_fpr(I) -> + case I of + #fp_binary{src1=Src1,src2=Src2} -> addtemp(Src1, [Src2]); + #fp_unary{src=Src} -> [Src]; + #pseudo_fmove{src=Src} -> [Src]; + #pseudo_fstore{src=Src} -> [Src]; + _ -> [] + end. + +%%% +%%% Auxiliary operations on sets of temps +%%% These sets are small. No point using gb_trees, right? +%%% + +addtemps([Arg|Args], Set) -> + addtemps(Args, addtemp(Arg, Set)); +addtemps([], Set) -> + Set. + +addtemp(Temp, Set) -> + case lists:member(Temp, Set) of + false -> [Temp|Set]; + _ -> Set + end. diff --git a/lib/hipe/sparc/hipe_sparc_encode.erl b/lib/hipe/sparc/hipe_sparc_encode.erl new file mode 100644 index 0000000000..8a28f33ab9 --- /dev/null +++ b/lib/hipe/sparc/hipe_sparc_encode.erl @@ -0,0 +1,476 @@ +%%% -*- erlang-indent-level: 2 -*- +%%% +%%% %CopyrightBegin% +%%% +%%% Copyright Ericsson AB 2007-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% +%%% +%%% Encode symbolic SPARC instructions to binary form. +%%% Copyright (C) 2007-2008 Mikael Pettersson + +-module(hipe_sparc_encode). + +-export([insn_encode/2]). + +%%-define(TESTING,1). +-ifdef(TESTING). +-export([dotest/0, dotest/1]). +-endif. + +-define(ASSERT(G), + if G -> []; + true -> exit({assertion_failed,?MODULE,?LINE,??G}) + end). + +bf(LeftBit, RightBit, Value) -> + ?ASSERT(32 > LeftBit), + ?ASSERT(LeftBit >= RightBit), + ?ASSERT(RightBit >= 0), + ?ASSERT(Value >= 0), + ?ASSERT(Value < (1 bsl ((LeftBit - RightBit) + 1))), + Value bsl RightBit. + +-define(BF(LB,RB,V), bf(LB,RB,V)). +-define(BIT(Pos,Val), ?BF(Pos,Pos,Val)). +%%-define(BITS(N,Val), ?BF(N,0,Val)). + +%%% +%%% Instruction Formats +%%% + +format1(Disp30) -> + ?BIT(30,1) bor ?BF(29,0,Disp30). + +format2a(Rd, Op2, Imm22) -> + ?BF(29,25,Rd) bor ?BF(24,22,Op2) bor ?BF(21,0,Imm22). + +format2b(A, Cond, Op2, Disp22) -> + ?BIT(29,A) bor ?BF(28,25,Cond) bor ?BF(24,22,Op2) bor ?BF(21,0,Disp22). + +format2c(A, Cond, Op2, CC1, CC0, P, Disp19) -> + ?BIT(29,A) bor ?BF(28,25,Cond) bor ?BF(24,22,Op2) bor ?BIT(21,CC1) + bor ?BIT(20,CC0) bor ?BIT(19,P) bor ?BF(18,0,Disp19). + +format2d(A, RCond, Op2, P, Rs1, Disp16) -> + D16Hi = Disp16 bsr 14, + D16Lo = Disp16 band 16#3FFF, + ?BIT(29,A) bor ?BF(27,25,RCond) bor ?BF(24,22,Op2) bor ?BF(21,20,D16Hi) + bor ?BIT(19,P) bor ?BF(18,14,Rs1) bor ?BF(13,0,D16Lo). + +format3common(Op, Rd, Op3, Rs1) -> % format 3, bits 31..14 + ?BF(31,30,Op) bor ?BF(29,25,Rd) bor ?BF(24,19,Op3) bor ?BF(18,14,Rs1). + +format3a(Op, Rd, Op3, Rs1, Rs2) -> + format3common(Op, Rd, Op3, Rs1) bor ?BF(4,0,Rs2). + +format3ax(Op, Rd, Op3, Rs1, Rs2) -> + format3a(Op, Rd, Op3, Rs1, Rs2) bor ?BIT(12,1). + +format3b(Op, Rd, Op3, Rs1, Simm13) -> + format3common(Op, Rd, Op3, Rs1) bor ?BIT(13,1) bor ?BF(12,0,Simm13). + +format3b32(Op, Rd, Op3, Rs1, Shcnt32) -> + format3a(Op, Rd, Op3, Rs1, Shcnt32) bor ?BIT(13,1). + +format3b64(Op, Rd, Op3, Rs1, Shcnt64) -> + format3common(Op, Rd, Op3, Rs1) bor ?BIT(13,1) bor ?BF(5,0,Shcnt64). + +format3ab(Op, {r,Rd}, Op3, {r,Rs1}, Src2) -> + case Src2 of + {r,Rs2} -> + format3a(Op, Rd, Op3, Rs1, Rs2); + {simm13,Simm13} -> + format3b(Op, Rd, Op3, Rs1, Simm13) + end. + +format3ab({Rs1,Src2,Rd}, Op3, Op) -> format3ab(Op, Rd, Op3, Rs1, Src2). + +-ifdef(notdef). +format3c(Op, Rd, Op3, Rs1, Opf, Rs2) -> + format3h(Op, Rd, Op3, Rs1) bor (Opf bsl 5) bor Rs2. + +format3d(Op, Rd, Op3, Rs1, I, Rs2) -> + format3h(Op, Rd, Op3, Rs1) bor (I bsl 13) bor Rs2. +-endif. + +%%% +%%% Instruction Operands +%%% + +'cond'(Cond) -> + case Cond of + 'n' -> 2#0000; + 'e' -> 2#0001; + 'le' -> 2#0010; + 'l' -> 2#0011; + 'leu' -> 2#0100; + 'lu' -> 2#0101; % a.k.a. 'cs' + 'neg' -> 2#0110; + 'vs' -> 2#0111; + 'a' -> 2#1000; + 'ne' -> 2#1001; + 'g' -> 2#1010; + 'ge' -> 2#1011; + 'gu' -> 2#1100; + 'geu' -> 2#1101; % a.k.a. 'cc' + 'pos' -> 2#1110; + 'vc' -> 2#1111 + end. + +rcond(RCond) -> + case RCond of + 'z' -> 2#001; + 'lez' -> 2#010; + 'lz' -> 2#011; + 'nz' -> 2#101; + 'gz' -> 2#110; + 'gez' -> 2#111 + end. + +pred(Pred) -> + case Pred of + 'pt' -> 1; + 'pn' -> 0 + end. + +%%% +%%% Branch Instructions +%%% + +call({disp30,Disp30}) -> + format1(Disp30). + +ba({disp22,Disp22}) -> % V7 Bicc, only used for unconditional branches + format2b(0, 'cond'('a'), 2#010, Disp22). + +bp({{'cond',Cond},{pred,Pred},{disp19,Disp19}}) -> + %% XXX: sparc64 will need CC1=1 here + format2c(0, 'cond'(Cond), 2#001, 0, 0, pred(Pred), Disp19). + +br({{rcond,RCond},{pred,Pred},{r,Rs1},{disp16,Disp16}}) -> + format2d(0, rcond(RCond), 2#011, pred(Pred), Rs1, Disp16). + +%%% +%%% Integer Arithmetic Instructions +%%% + +alu(Opnds, Op3) -> format3ab(Opnds, Op3, 2#10). + +add(Opnds) -> alu(Opnds, 2#000000). +addcc(Opnds) -> alu(Opnds, 2#010000). +%%addc(Opnds) -> alu(Opnds, 2#001000). +%%addccc(Opnds) -> alu(Opnds, 2#011000). + +sub(Opnds) -> alu(Opnds, 2#000100). +subcc(Opnds) -> alu(Opnds, 2#010100). +%%subc(Opnds) -> alu(Opnds, 2#001100). % XXX: hipe_sparc_op has bug here +%%subccc(Opnds) -> alu(Opnds, 2#011100). % XXX: hipe_sparc_op has bug here + +%%taddcc(Opnds) -> alu(Opnds, 2#100000). +%%taddcctv(Opnds) -> alu(Opnds, 2#100010). + +%%tsubcc(Opnds) -> alu(Opnds, 2#100001). +%%tsubcctv(Opnds) -> alu(Opnds, 2#100011). + +mulx(Opnds) -> alu(Opnds, 2#001001). +%%sdivx(Opnds) -> alu(Opnds, 2#101101). +%%udivx(Opnds) -> alu(Opnds, 2#001101). + +%%umul(Opnds) -> alu(Opnds, 2#001010). +smul(Opnds) -> alu(Opnds, 2#001011). +%%umulcc(Opnds) -> alu(Opnds, 2#011010). +%%smulcc(Opnds) -> alu(Opnds, 2#011011). + +'and'(Opnds) -> alu(Opnds, 2#000001). +andcc(Opnds) -> alu(Opnds, 2#010001). +%%andn(Opnds) -> alu(Opnds, 2#000101). +%%andncc(Opnds) -> alu(Opnds, 2#010101). + +'or'(Opnds) -> alu(Opnds, 2#000010). +orcc(Opnds) -> alu(Opnds, 2#010010). +%%orn(Opnds) -> alu(Opnds, 2#000110). +%%orncc(Opnds) -> alu(Opnds, 2#010110). + +'xor'(Opnds) -> alu(Opnds, 2#000011). +xorcc(Opnds) -> alu(Opnds, 2#010011). +%%xnor(Opnds) -> alu(Opnds, 2#000111). +%%xnorcc(Opnds) -> alu(Opnds, 2#010111). + +shift32({{r,Rs1},Src2,{r,Rd}}, Op3) -> + case Src2 of + {r,Rs2} -> + format3a(2#10, Rd, Op3, Rs1, Rs2); + {uimm5,Shcnt32} -> + format3b32(2#10, Rd, Op3, Rs1, Shcnt32) + end. + +shift64({{r,Rs1},Src2,{r,Rd}}, Op3) -> + case Src2 of + {r,Rs2} -> + format3ax(2#10, Rd, Op3, Rs1, Rs2); + {uimm6,Shcnt64} -> + format3b64(2#10, Rd, Op3, Rs1, Shcnt64) + end. + +sll(Opnds) -> shift32(Opnds, 2#100101). +sllx(Opnds) -> shift64(Opnds, 2#100101). +srl(Opnds) -> shift32(Opnds, 2#100110). +srlx(Opnds) -> shift64(Opnds, 2#100110). +sra(Opnds) -> shift32(Opnds, 2#100111). +srax(Opnds) -> shift64(Opnds, 2#100111). + +jmpl(Opnds) -> alu(Opnds, 2#111000). + +rd({y,{r,Rd}}) -> format3a(2#10, Rd, 2#101000, 0, 0). + +sethi({{uimm22,UImm22},{r,Rd}}) -> format2a(Rd, 2#100, UImm22). + +ld(Opnds, Op3) -> format3ab(Opnds, Op3, 2#11). + +ldsb(Opnds) -> ld(Opnds, 2#001001). +ldsh(Opnds) -> ld(Opnds, 2#001010). +ldsw(Opnds) -> ld(Opnds, 2#001000). +ldub(Opnds) -> ld(Opnds, 2#000001). +lduh(Opnds) -> ld(Opnds, 2#000010). +lduw(Opnds) -> ld(Opnds, 2#000000). +ldx(Opnds) -> ld(Opnds, 2#001011). +%%ldd(Opnds) -> ld(Opnds, 2#000011). + +st({Rd,Rs1,Src2}, Op3) -> format3ab(2#11, Rd, Op3, Rs1, Src2). + +stb(Opnds) -> st(Opnds, 2#000101). +%%sth(Opnds) -> st(Opnds, 2#000110). +stw(Opnds) -> st(Opnds, 2#000100). +stx(Opnds) -> st(Opnds, 2#001110). +%%std(Opnds) -> st(Opnds, 2#000111). + +%%% +%%% Floating-Point Instructions +%%% + +format3f(Rd, Rs1, Opf, Rs2) -> + format3a(2#10, Rd, 2#110100, Rs1, Rs2) bor ?BF(13,5,Opf). + +fpop1binary(Opf, {{fr,Rs1},{fr,Rs2},{fr,Rd}}) -> + format3f(Rd, Rs1, Opf, Rs2). + +faddd(Opnds) -> fpop1binary(2#001000010, Opnds). +fdivd(Opnds) -> fpop1binary(2#001001110, Opnds). +fmuld(Opnds) -> fpop1binary(2#001001010, Opnds). +fsubd(Opnds) -> fpop1binary(2#001000110, Opnds). + +fpop1unary(Opf, {{fr,Rs2},{fr,Rd}}) -> + format3f(Rd, 0, Opf, Rs2). + +fitod(Opnds) -> fpop1unary(2#011001000, Opnds). +fmovd(Opnds) -> fpop1unary(2#000000010, Opnds). +fnegd(Opnds) -> fpop1unary(2#000000110, Opnds). + +ldf({{r,Rs1},{simm13,Simm13},{fr,Rd}}) -> + format3b(2#11, Rd, 2#100000, Rs1, Simm13). + +stf({{fr,Rd},{r,Rs1},{simm13,Simm13}}) -> + format3b(2#11, Rd, 2#100100, Rs1, Simm13). + +-ifdef(notdef). +fpop1(Rs1,Opf,Rs2,Rd) -> format3a(2#10, Rd, 2#110100, Rs1, Opf, Rs2). +%% fpop2(Rs1,Opf,Rs2,Rd) -> format3a(2#10, Rd, 2#110101, Rs1, Opf, Rs2). + +%% fxtos(Rs2, Rd) -> fpop1(0,2#010000100,Rs2,Rd). +%% fxtod(Rs2, Rd) -> fpop1(0,2#010001000,Rs2,Rd). +%% fxtoq(Rs2, Rd) -> fpop1(0,2#010001100,Rs2,Rd). +fitos(Rs2, Rd) -> fpop1(0,2#011000100,Rs2,Rd). +fitoq(Rs2, Rd) -> fpop1(0,2#011001100,Rs2,Rd). + +%% fstox(Rs2, Rd) -> fpop1(0,2#010000001,Rs2,Rd). +%% fdtox(Rs2, Rd) -> fpop1(0,2#010000010,Rs2,Rd). +%% fqtox(Rs2, Rd) -> fpop1(0,2#010000011,Rs2,Rd). +%% fstoi(Rs2, Rd) -> fpop1(0,2#011010001,Rs2,Rd). +%% fdtoi(Rs2, Rd) -> fpop1(0,2#011010010,Rs2,Rd). +%% fqtoi(Rs2, Rd) -> fpop1(0,2#011010011,Rs2,Rd). + +%% fstod(Rs2, Rd) -> fpop1(0,2#011001001,Rs2,Rd). +%% fstoq(Rs2, Rd) -> fpop1(0,2#011001101,Rs2,Rd). +%% fdtos(Rs2, Rd) -> fpop1(0,2#011000110,Rs2,Rd). +%% fdtoq(Rs2, Rd) -> fpop1(0,2#011001110,Rs2,Rd). +%% fqtos(Rs2, Rd) -> fpop1(0,2#011000111,Rs2,Rd). +%% fqtod(Rs2, Rd) -> fpop1(0,2#011001011,Rs2,Rd). + +fmovs(Rs2, Rd) -> fpop1(0,2#000000001,Rs2,Rd). +fnegs(Rs2, Rd) -> fpop1(0,2#000000101,Rs2,Rd). +fabss(Rs2, Rd) -> fpop1(0,2#000001001,Rs2,Rd). +fabsd(Rs2, Rd) -> fpop1(0,2#000001010,Rs2,Rd). +fmovq(Rs2, Rd) -> fpop1(0,2#000000011,Rs2,Rd). +fnegq(Rs2, Rd) -> fpop1(0,2#000000111,Rs2,Rd). +fabsq(Rs2, Rd) -> fpop1(0,2#000001011,Rs2,Rd). + +%% fsqrts(Rs2, Rd) -> fpop1(0,2#000101001,Rs2,Rd). +%% fsqrtd(Rs2, Rd) -> fpop1(0,2#000101010,Rs2,Rd). +%% fsqrtq(Rs2, Rd) -> fpop1(0,2#000101011,Rs2,Rd). + +fadds(Rs1, Rs2, Rd) -> fpop1(Rs1,2#001000001,Rs2,Rd). +faddq(Rs1, Rs2, Rd) -> fpop1(Rs1,2#001000011,Rs2,Rd). +fsubs(Rs1, Rs2, Rd) -> fpop1(Rs1,2#001000101,Rs2,Rd). +fsubq(Rs1, Rs2, Rd) -> fpop1(Rs1,2#001000111,Rs2,Rd). + +fmuls(Rs1, Rs2, Rd) -> fpop1(Rs1,2#001001001,Rs2,Rd). +fmulq(Rs1, Rs2, Rd) -> fpop1(Rs1,2#001001011,Rs2,Rd). +%% fsmuld(Rs1, Rs2, Rd) -> fpop1(Rs1,2#001101001,Rs2,Rd). +%% fdmulq(Rs1, Rs2, Rd) -> fpop1(Rs1,2#001101110,Rs2,Rd). +fdivs(Rs1, Rs2, Rd) -> fpop1(Rs1,2#001001101,Rs2,Rd). +fdivq(Rs1, Rs2, Rd) -> fpop1(Rs1,2#001001111,Rs2,Rd). + +%% Uses fcc0 +%% fcmps(Rs1, Rs2) -> fpop2(Rs1,2#001010001,Rs2,0). +%% fcmpd(Rs1, Rs2) -> fpop2(Rs1,2#001010010,Rs2,0). +%% fcmpq(Rs1, Rs2) -> fpop2(Rs1,2#001010011,Rs2,0). +%% fcmpes(Rs1, Rs2) -> fpop2(Rs1,2#001010101,Rs2,0). +%% fcmped(Rs1, Rs2) -> fpop2(Rs1,2#001010110,Rs2,0). +%% fcmpeq(Rs1, Rs2) -> fpop2(Rs1,2#001010111,Rs2,0). + +%% fcmps(N, Rs1, Rs2) -> fpcn(N,2#001010001,Rs1,Rs2). +%% fcmpd(N, Rs1, Rs2) -> fpcn(N,2#001010010,Rs1,Rs2). +%% fcmpq(N, Rs1, Rs2) -> fpcn(N,2#001010011,Rs1,Rs2). +%% fcmpes(N, Rs1, Rs2) -> fpcn(N,2#001010101,Rs1,Rs2). +%% fcmped(N, Rs1, Rs2) -> fpcn(N,2#001010110,Rs1,Rs2). +%% fcmpeq(N, Rs1, Rs2) -> fpcn(N,2#001010111,Rs1,Rs2). + +stfi(Rd, Rs1, Offset) -> format3b(2#11, Rd, 2#100100, Rs1, Offset). +stdf(Rd, Rs1, Rs2) -> format3a(2#11, Rd, 2#100111, Rs1, 0, Rs2). +stdfi(Rd, Rs1, Offset) -> format3b(2#11, Rd, 2#100111, Rs1, Offset). +stqf(Rd, Rs1, Rs2) -> format3a(2#11, Rd, 2#100110, Rs1, 0, Rs2). +stqfi(Rd, Rs1, Offset) -> format3b(2#11, Rd, 2#100110, Rs1, Offset). +%% stfsr(Rd, Rs1, Rs2) -> format3a(2#11, Rd, 2#100101, Rs1, 0, Rs2). +%% stfsri(Rd, Rs1, Offset) -> format3b(2#11, Rd, 2#100101, Rs1, Offset). + +ldfi(Rd, Rs1, Offset) -> format3b(2#11, Rd, 2#100000, Rs1, Offset). +lddf(Rd, Rs1, Rs2) -> format3a(2#11, Rd, 2#100011, Rs1, 0, Rs2). +lddfi(Rd, Rs1, Offset) -> format3b(2#11, Rd, 2#100011, Rs1, Offset). +ldqf(Rd, Rs1, Rs2) -> format3a(2#11, Rd, 2#100010, Rs1, 0, Rs2). +ldqfi(Rd, Rs1, Offset) -> format3b(2#11, Rd, 2#100010, Rs1, Offset). +%% ldxfsr(Rs1, Rs2) -> format3a(2#11, 1, 2#100001, Rs1, 0, Rs2). +%% ldxfsri(Rs1, Offset) -> format3b(2#11, 1, 2#100001, Rs1, Offset). + +%% fpcn(N, Opf, Rs1, Rs2) -> +%% case N of +%% 0 -> fpc0(Opf, Rs1, Rs2); +%% 1 -> fpc1(Opf, Rs1, Rs2); +%% 2 -> fpc2(Opf, Rs1, Rs2); +%% 3 -> fpc3(Opf, Rs1, Rs2) +%% end. + +%% fpc0(Opf, Rs1, Rs2) -> format3c(2#10, 2#00000, 2#110101, Rs1, Opf, Rs2). +%% fpc1(Opf, Rs1, Rs2) -> format3c(2#10, 2#00001, 2#110101, Rs1, Opf, Rs2). +%% fpc2(Opf, Rs1, Rs2) -> format3c(2#10, 2#00010, 2#110101, Rs1, Opf, Rs2). +%% fpc3(Opf, Rs1, Rs2) -> format3c(2#10, 2#00011, 2#110101, Rs1, Opf, Rs2). +-endif. % FP insns + +%%% +%%% Main Encode Dispatch +%%% + +insn_encode(Op, Opnds) -> + case Op of + 'add' -> add(Opnds); + 'addcc' -> addcc(Opnds); + 'and' -> 'and'(Opnds); + 'andcc' -> andcc(Opnds); + 'ba' -> ba(Opnds); + 'bp' -> bp(Opnds); + 'br' -> br(Opnds); + 'call' -> call(Opnds); + 'jmpl' -> jmpl(Opnds); + 'ldsb' -> ldsb(Opnds); + 'ldsh' -> ldsh(Opnds); + 'ldsw' -> ldsw(Opnds); + 'ldub' -> ldub(Opnds); + 'lduh' -> lduh(Opnds); + 'lduw' -> lduw(Opnds); + 'ldx' -> ldx(Opnds); + 'mulx' -> mulx(Opnds); + 'or' -> 'or'(Opnds); + 'orcc' -> orcc(Opnds); + 'rd' -> rd(Opnds); + 'sethi' -> sethi(Opnds); + 'sll' -> sll(Opnds); + 'sllx' -> sllx(Opnds); + 'smul' -> smul(Opnds); + 'sra' -> sra(Opnds); + 'srax' -> srax(Opnds); + 'srl' -> srl(Opnds); + 'srlx' -> srlx(Opnds); + 'stb' -> stb(Opnds); + 'stw' -> stw(Opnds); + 'stx' -> stx(Opnds); + 'sub' -> sub(Opnds); + 'subcc' -> subcc(Opnds); + 'xor' -> 'xor'(Opnds); + 'xorcc' -> xorcc(Opnds); + 'faddd' -> faddd(Opnds); + 'fdivd' -> fdivd(Opnds); + 'fmuld' -> fmuld(Opnds); + 'fsubd' -> fsubd(Opnds); + 'fitod' -> fitod(Opnds); + 'fmovd' -> fmovd(Opnds); + 'fnegd' -> fnegd(Opnds); + 'ldf' -> ldf(Opnds); + 'stf' -> stf(Opnds); + _ -> exit({?MODULE,insn_encode,Op}) + end. + +%%% +%%% Testing Interface +%%% + +-ifdef(TESTING). + +say(OS, Str) -> + file:write(OS, Str). + +hex_digit(Dig0) -> + Dig = Dig0 band 16#F, + if Dig >= 16#A -> $A + (Dig - 16#A); + true -> $0 + Dig + end. + +say_byte(OS, Byte) -> + say(OS, [hex_digit(Byte bsr 4)]), + say(OS, [hex_digit(Byte)]). + +say_word(OS, Word) -> + say(OS, "0x"), + say_byte(OS, Word bsr 24), + say_byte(OS, Word bsr 16), + say_byte(OS, Word bsr 8), + say_byte(OS, Word). + +t(OS, Op, Opnds) -> + Word = insn_encode(Op, Opnds), + say(OS, "\t.long "), + say_word(OS, Word), + say(OS, "\n"). + +dotest1(OS) -> + say(OS, "\t.text\n\t.align 4\n"), + []. + +dotest() -> dotest1(group_leader()). + +dotest(File) -> + {ok,OS} = file:open(File, [write]), + dotest1(OS), + file:close(OS). + +-endif. diff --git a/lib/hipe/sparc/hipe_sparc_finalise.erl b/lib/hipe/sparc/hipe_sparc_finalise.erl new file mode 100644 index 0000000000..b44a21f7c0 --- /dev/null +++ b/lib/hipe/sparc/hipe_sparc_finalise.erl @@ -0,0 +1,138 @@ +%% -*- erlang-indent-level: 2 -*- +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2007-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_sparc_finalise). +-export([finalise/1]). +-include("hipe_sparc.hrl"). + +finalise(Defun) -> + #defun{code=Code0} = Defun, + Code1 = peep(expand(Code0)), + Defun#defun{code=Code1}. + +expand(Insns) -> + expand_list(Insns, []). + +expand_list([I|Insns], Accum) -> + expand_list(Insns, expand_insn(I, Accum)); +expand_list([], Accum) -> + lists:reverse(Accum). + +expand_insn(I, Accum) -> + case I of + #bp{'cond'='a'} -> + [hipe_sparc:mk_nop(), + I | + Accum]; + #call_rec{} -> + [hipe_sparc:mk_nop(), + I | + Accum]; + #call_tail{} -> + RA = hipe_sparc:mk_ra(), + TempRA = hipe_sparc:mk_temp1(), + [hipe_sparc:mk_mov(TempRA, RA), + I, % becomes a call, which clobbers RA + hipe_sparc:mk_mov(RA, TempRA) | + Accum]; + #jmp{} -> + [hipe_sparc:mk_nop(), + I | + Accum]; + #pseudo_bp{'cond'=Cond,true_label=TrueLab,false_label=FalseLab, pred=Pred} -> + [hipe_sparc:mk_nop(), + hipe_sparc:mk_b_label(FalseLab), + hipe_sparc:mk_nop(), + hipe_sparc:mk_bp(Cond, TrueLab, Pred) | + Accum]; + %% #pseudo_br{} -> expand_pseudo_br(I, Accum); + #pseudo_call{funv=FunV,sdesc=SDesc,contlab=ContLab,linkage=Linkage} -> + [hipe_sparc:mk_nop(), + hipe_sparc:mk_b_label(ContLab), + hipe_sparc:mk_nop(), + case FunV of + #sparc_temp{} -> + hipe_sparc:mk_jmpl(FunV, SDesc); + _ -> + hipe_sparc:mk_call_rec(FunV, SDesc, Linkage) + end | + Accum]; + #pseudo_ret{} -> + RA = hipe_sparc:mk_ra(), + [hipe_sparc:mk_nop(), + hipe_sparc:mk_jmp(RA, hipe_sparc:mk_simm13(8), []) | + Accum]; + #pseudo_tailcall_prepare{} -> + Accum; + _ -> + XXX = + case I of + #alu{} -> true; + #comment{} -> true; + #label{} -> true; + #pseudo_set{} -> true; + #rdy{} -> true; + #sethi{} -> true; + #store{} -> true; + #bp{} -> false; + %% #br{} -> false; + #call_rec{} -> false; + #call_tail{} -> false; + #jmp{} -> false; + #jmpl{} -> false; + #pseudo_bp{} -> false; + %% #pseudo_br{} -> false; + #pseudo_call{} -> false; + #pseudo_call_prepare{} -> false; + #pseudo_move{} -> false; + #pseudo_ret{} -> false; + #pseudo_tailcall{} -> false; + #pseudo_tailcall_prepare{} -> false; + #fp_binary{} -> true; + #fp_unary{} -> true; + #pseudo_fload{} -> true; + #pseudo_fstore{} -> true + end, + case XXX of + true -> []; + false -> exit({?MODULE,expand_insn,I}) + end, + [I|Accum] + end. + +-ifdef(notdef). % XXX: only for sparc64, alas +expand_pseudo_br(I, Accum) -> + #pseudo_br{rcond=RCond,src=Src,true_label=TrueLab,false_label=FalseLab, pred=Pred} = I, + [hipe_sparc:mk_nop(), + hipe_sparc:mk_b_label(FalseLab), + hipe_sparc:mk_nop(), + hipe_sparc:mk_br(RCond, Src, TrueLab, Pred) | + Accum]. +-endif. + +peep(Insns) -> + peep_list(Insns, []). + +peep_list([#bp{'cond'='a',label=Label}, #sethi{uimm22=#sparc_uimm22{value=0},dst=#sparc_temp{reg=0}} | (Insns = [#label{label=Label}|_])], Accum) -> + peep_list(Insns, Accum); +peep_list([I|Insns], Accum) -> + peep_list(Insns, [I|Accum]); +peep_list([], Accum) -> + lists:reverse(Accum). diff --git a/lib/hipe/sparc/hipe_sparc_frame.erl b/lib/hipe/sparc/hipe_sparc_frame.erl new file mode 100644 index 0000000000..f7d7f40df3 --- /dev/null +++ b/lib/hipe/sparc/hipe_sparc_frame.erl @@ -0,0 +1,636 @@ +%% -*- erlang-indent-level: 2 -*- +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2002-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_sparc_frame). +-export([frame/1]). + +-include("hipe_sparc.hrl"). +-include("../rtl/hipe_literals.hrl"). + +frame(Defun) -> + Formals = fix_formals(hipe_sparc:defun_formals(Defun)), + Temps0 = all_temps(hipe_sparc:defun_code(Defun), Formals), + MinFrame = defun_minframe(Defun), + Temps = ensure_minframe(MinFrame, Temps0), + ClobbersRA = clobbers_ra(hipe_sparc:defun_code(Defun)), + CFG0 = hipe_sparc_cfg:init(Defun), + Liveness = hipe_sparc_liveness_all:analyse(CFG0), + CFG1 = do_body(CFG0, Liveness, Formals, Temps, ClobbersRA), + hipe_sparc_cfg:linearise(CFG1). + +fix_formals(Formals) -> + fix_formals(hipe_sparc_registers:nr_args(), Formals). + +fix_formals(0, Rest) -> Rest; +fix_formals(N, [_|Rest]) -> fix_formals(N-1, Rest); +fix_formals(_, []) -> []. + +do_body(CFG0, Liveness, Formals, Temps, ClobbersRA) -> + Context = mk_context(Liveness, Formals, Temps, ClobbersRA), + CFG1 = do_blocks(CFG0, Context), + do_prologue(CFG1, Context). + +do_blocks(CFG, Context) -> + Labels = hipe_sparc_cfg:labels(CFG), + do_blocks(Labels, CFG, Context). + +do_blocks([Label|Labels], CFG, Context) -> + Liveness = context_liveness(Context), + LiveOut = hipe_sparc_liveness_all:liveout(Liveness, Label), + Block = hipe_sparc_cfg:bb(CFG, Label), + Code = hipe_bb:code(Block), + NewCode = do_block(Code, LiveOut, Context), + NewBlock = hipe_bb:code_update(Block, NewCode), + NewCFG = hipe_sparc_cfg:bb_add(CFG, Label, NewBlock), + do_blocks(Labels, NewCFG, Context); +do_blocks([], CFG, _) -> + CFG. + +do_block(Insns, LiveOut, Context) -> + do_block(Insns, LiveOut, Context, context_framesize(Context), []). + +do_block([I|Insns], LiveOut, Context, FPoff0, RevCode) -> + {NewIs, FPoff1} = do_insn(I, LiveOut, Context, FPoff0), + do_block(Insns, LiveOut, Context, FPoff1, lists:reverse(NewIs, RevCode)); +do_block([], _, Context, FPoff, RevCode) -> + FPoff0 = context_framesize(Context), + if FPoff =:= FPoff0 -> []; + true -> exit({?MODULE,do_block,FPoff}) + end, + lists:reverse(RevCode, []). + +do_insn(I, LiveOut, Context, FPoff) -> + case I of + #pseudo_call{} -> + do_pseudo_call(I, LiveOut, Context, FPoff); + #pseudo_call_prepare{} -> + do_pseudo_call_prepare(I, FPoff); + #pseudo_move{} -> + {do_pseudo_move(I, Context, FPoff), FPoff}; + #pseudo_ret{} -> + {do_pseudo_ret(I, Context, FPoff), context_framesize(Context)}; + #pseudo_tailcall{} -> + {do_pseudo_tailcall(I, Context), context_framesize(Context)}; + #pseudo_fmove{} -> + {do_pseudo_fmove(I, Context, FPoff), FPoff}; + _ -> + {[I], FPoff} + end. + +%%% +%%% Moves, with Dst or Src possibly a pseudo +%%% + +do_pseudo_move(I, Context, FPoff) -> + Dst = hipe_sparc:pseudo_move_dst(I), + Src = hipe_sparc:pseudo_move_src(I), + case temp_is_pseudo(Dst) of + true -> + Offset = pseudo_offset(Dst, FPoff, Context), + mk_store(Src, hipe_sparc:mk_sp(), Offset, []); + _ -> + case temp_is_pseudo(Src) of + true -> + Offset = pseudo_offset(Src, FPoff, Context), + mk_load(hipe_sparc:mk_sp(), Offset, Dst, []); + _ -> + [hipe_sparc:mk_mov(Src, Dst)] + end + end. + +do_pseudo_fmove(I, Context, FPoff) -> + Dst = hipe_sparc:pseudo_fmove_dst(I), + Src = hipe_sparc:pseudo_fmove_src(I), + case temp_is_pseudo(Dst) of + true -> + Offset = pseudo_offset(Dst, FPoff, Context), + mk_fstore(Src, hipe_sparc:mk_sp(), Offset); + _ -> + case temp_is_pseudo(Src) of + true -> + Offset = pseudo_offset(Src, FPoff, Context), + mk_fload(hipe_sparc:mk_sp(), Offset, Dst); + _ -> + [hipe_sparc:mk_fp_unary('fmovd', Src, Dst)] + end + end. + +pseudo_offset(Temp, FPoff, Context) -> + FPoff + context_offset(Context, Temp). + +%%% +%%% Return - deallocate frame and emit 'ret $N' insn. +%%% + +do_pseudo_ret(I, Context, FPoff) -> + %% XXX: typically only one instruction between + %% the move-to-RA and the jmp-via-RA, ouch + restore_ra(FPoff, Context, + adjust_sp(FPoff + word_size() * context_arity(Context), + [I])). + +restore_ra(FPoff, Context, Rest) -> + case context_clobbers_ra(Context) of + false -> Rest; + true -> + RA = hipe_sparc:mk_ra(), + mk_load(hipe_sparc:mk_sp(), FPoff - word_size(), RA, Rest) + end. + +adjust_sp(N, Rest) -> + if N =:= 0 -> + Rest; + true -> + SP = hipe_sparc:mk_sp(), + hipe_sparc:mk_addi(SP, N, SP, Rest) + end. + +%%% +%%% Recursive calls. +%%% + +do_pseudo_call_prepare(I, FPoff0) -> + %% Create outgoing arguments area on the stack. + NrStkArgs = hipe_sparc:pseudo_call_prepare_nrstkargs(I), + Offset = NrStkArgs * word_size(), + {adjust_sp(-Offset, []), FPoff0 + Offset}. + +do_pseudo_call(I, LiveOut, Context, FPoff0) -> + #sparc_sdesc{exnlab=ExnLab,arity=OrigArity} = hipe_sparc:pseudo_call_sdesc(I), + FunV = hipe_sparc:pseudo_call_funv(I), + LiveTemps = [Temp || Temp <- LiveOut, temp_is_pseudo(Temp)], + SDesc = mk_sdesc(ExnLab, Context, LiveTemps), + ContLab = hipe_sparc:pseudo_call_contlab(I), + Linkage = hipe_sparc:pseudo_call_linkage(I), + CallCode = [hipe_sparc:mk_pseudo_call(FunV, SDesc, ContLab, Linkage)], + StkArity = erlang:max(0, OrigArity - hipe_sparc_registers:nr_args()), + context_need_stack(Context, stack_need(FPoff0, StkArity, FunV)), + ArgsBytes = word_size() * StkArity, + {CallCode, FPoff0 - ArgsBytes}. + +stack_need(FPoff, StkArity, FunV) -> + case FunV of + #sparc_prim{} -> FPoff; + #sparc_mfa{m=M,f=F,a=A} -> + case erlang:is_builtin(M, F, A) of + true -> FPoff; + false -> stack_need_general(FPoff, StkArity) + end; + _ -> stack_need_general(FPoff, StkArity) + end. + +stack_need_general(FPoff, StkArity) -> + erlang:max(FPoff, FPoff + (?SPARC_LEAF_WORDS - StkArity) * word_size()). + +%%% +%%% Create stack descriptors for call sites. +%%% + +mk_sdesc(ExnLab, Context, Temps) -> % for normal calls + Temps0 = only_tagged(Temps), + Live = mk_live(Context, Temps0), + Arity = context_arity(Context), + FSize = context_framesize(Context), + hipe_sparc:mk_sdesc(ExnLab, (FSize div word_size())-1, Arity, + list_to_tuple(Live)). + +only_tagged(Temps)-> + [X || X <- Temps, hipe_sparc:temp_type(X) =:= 'tagged']. + +mk_live(Context, Temps) -> + lists:sort([temp_to_slot(Context, Temp) || Temp <- Temps]). + +temp_to_slot(Context, Temp) -> + (context_framesize(Context) + context_offset(Context, Temp)) + div word_size(). + +mk_minimal_sdesc(Context) -> % for inc_stack_0 calls + hipe_sparc:mk_sdesc([], 0, context_arity(Context), {}). + +%%% +%%% Tailcalls. +%%% + +do_pseudo_tailcall(I, Context) -> % always at FPoff=context_framesize(Context) + Arity = context_arity(Context), + Args = hipe_sparc:pseudo_tailcall_stkargs(I), + FunV = hipe_sparc:pseudo_tailcall_funv(I), + Linkage = hipe_sparc:pseudo_tailcall_linkage(I), + {Insns, FPoff1} = do_tailcall_args(Args, Context), + context_need_stack(Context, FPoff1), + StkArity = length(Args), + FPoff2 = FPoff1 + (Arity - StkArity) * word_size(), + context_need_stack(Context, stack_need(FPoff2, StkArity, FunV)), + I2 = + case FunV of + #sparc_temp{} -> + hipe_sparc:mk_jmp(FunV, hipe_sparc:mk_simm13(0), []); + Fun -> + hipe_sparc:mk_call_tail(Fun, Linkage) + end, + %% XXX: break out the RA restore, just like for pseudo_ret? + restore_ra(context_framesize(Context), Context, + Insns ++ adjust_sp(FPoff2, [I2])). + +do_tailcall_args(Args, Context) -> + FPoff0 = context_framesize(Context), + Arity = context_arity(Context), + FrameTop = word_size()*Arity, + DangerOff = FrameTop - word_size()*length(Args), + %% + Moves = mk_moves(Args, FrameTop, []), + %% + {Stores, Simple, Conflict} = + split_moves(Moves, Context, DangerOff, [], [], []), + %% sanity check (shouldn't trigger any more) + if DangerOff < -FPoff0 -> + exit({?MODULE,do_tailcall_args,DangerOff,-FPoff0}); + true -> [] + end, + FPoff1 = FPoff0, + %% + {Pushes, Pops, FPoff2} = split_conflict(Conflict, FPoff1, [], []), + %% + TempReg = hipe_sparc_registers:temp1(), + %% + {adjust_sp(-(FPoff2 - FPoff1), + simple_moves(Pushes, FPoff2, TempReg, + store_moves(Stores, FPoff2, TempReg, + simple_moves(Simple, FPoff2, TempReg, + simple_moves(Pops, FPoff2, TempReg, + []))))), + FPoff2}. + +mk_moves([Arg|Args], Off, Moves) -> + Off1 = Off - word_size(), + mk_moves(Args, Off1, [{Arg,Off1}|Moves]); +mk_moves([], _, Moves) -> + Moves. + +split_moves([Move|Moves], Context, DangerOff, Stores, Simple, Conflict) -> + {Src,DstOff} = Move, + case src_is_pseudo(Src) of + false -> + split_moves(Moves, Context, DangerOff, [Move|Stores], + Simple, Conflict); + true -> + SrcOff = context_offset(Context, Src), + Type = typeof_temp(Src), + if SrcOff =:= DstOff -> + split_moves(Moves, Context, DangerOff, Stores, + Simple, Conflict); + SrcOff >= DangerOff -> + split_moves(Moves, Context, DangerOff, Stores, + Simple, [{SrcOff,DstOff,Type}|Conflict]); + true -> + split_moves(Moves, Context, DangerOff, Stores, + [{SrcOff,DstOff,Type}|Simple], Conflict) + end + end; +split_moves([], _, _, Stores, Simple, Conflict) -> + {Stores, Simple, Conflict}. + +split_conflict([{SrcOff,DstOff,Type}|Conflict], FPoff, Pushes, Pops) -> + FPoff1 = FPoff + word_size(), + Push = {SrcOff,-FPoff1,Type}, + Pop = {-FPoff1,DstOff,Type}, + split_conflict(Conflict, FPoff1, [Push|Pushes], [Pop|Pops]); +split_conflict([], FPoff, Pushes, Pops) -> + {lists:reverse(Pushes), Pops, FPoff}. + +simple_moves([{SrcOff,DstOff,Type}|Moves], FPoff, TempReg, Rest) -> + Temp = hipe_sparc:mk_temp(TempReg, Type), + SP = hipe_sparc:mk_sp(), + LoadOff = FPoff+SrcOff, + StoreOff = FPoff+DstOff, + simple_moves(Moves, FPoff, TempReg, + mk_load(SP, LoadOff, Temp, + mk_store(Temp, SP, StoreOff, + Rest))); +simple_moves([], _, _, Rest) -> + Rest. + +store_moves([{Src,DstOff}|Moves], FPoff, TempReg, Rest) -> + %% Type = typeof_temp(Src), + SP = hipe_sparc:mk_sp(), + StoreOff = FPoff+DstOff, + {NewSrc,FixSrc} = + case hipe_sparc:is_temp(Src) of + true -> + {Src, []}; + _ -> + Temp = hipe_sparc:mk_temp(TempReg, 'untagged'), + {Temp, hipe_sparc:mk_set(Src, Temp)} + end, + store_moves(Moves, FPoff, TempReg, + FixSrc ++ mk_store(NewSrc, SP, StoreOff, Rest)); +store_moves([], _, _, Rest) -> + Rest. + +%%% +%%% Contexts +%%% + +-record(context, {liveness, framesize, arity, map, clobbers_ra, ref_maxstack}). + +mk_context(Liveness, Formals, Temps, ClobbersRA) -> + {Map, MinOff} = mk_temp_map(Formals, ClobbersRA, Temps), + FrameSize = (-MinOff), + RefMaxStack = hipe_bifs:ref(FrameSize), + #context{liveness=Liveness, + framesize=FrameSize, arity=length(Formals), + map=Map, clobbers_ra=ClobbersRA, ref_maxstack=RefMaxStack}. + +context_need_stack(#context{ref_maxstack=RM}, N) -> + M = hipe_bifs:ref_get(RM), + if N > M -> hipe_bifs:ref_set(RM, N); + true -> [] + end. + +context_maxstack(#context{ref_maxstack=RM}) -> + hipe_bifs:ref_get(RM). + +context_arity(#context{arity=Arity}) -> + Arity. + +context_framesize(#context{framesize=FrameSize}) -> + FrameSize. + +context_liveness(#context{liveness=Liveness}) -> + Liveness. + +context_offset(#context{map=Map}, Temp) -> + tmap_lookup(Map, Temp). + +context_clobbers_ra(#context{clobbers_ra=ClobbersRA}) -> ClobbersRA. + +mk_temp_map(Formals, ClobbersRA, Temps) -> + {Map, 0} = enter_vars(Formals, word_size() * length(Formals), + tmap_empty()), + TempsList = tset_to_list(Temps), + AllTemps = + case ClobbersRA of + false -> TempsList; + true -> + RA = hipe_sparc:mk_new_temp('untagged'), + [RA|TempsList] + end, + enter_vars(AllTemps, 0, Map). + +enter_vars([V|Vs], PrevOff, Map) -> + Off = + case hipe_sparc:temp_type(V) of + 'double' -> PrevOff - 2*word_size(); % XXX: sparc64: 1*word_size() + _ -> PrevOff - word_size() + end, + enter_vars(Vs, Off, tmap_bind(Map, V, Off)); +enter_vars([], Off, Map) -> + {Map, Off}. + +tmap_empty() -> + gb_trees:empty(). + +tmap_bind(Map, Key, Val) -> + gb_trees:insert(Key, Val, Map). + +tmap_lookup(Map, Key) -> + gb_trees:get(Key, Map). + +%%% +%%% do_prologue: prepend stack frame allocation code. +%%% +%%% NewStart: +%%% temp1 = *(P + P_SP_LIMIT) +%%% temp2 = SP - MaxStack +%%% cmp temp2, temp1 +%%% if (ltu) goto IncStack else goto AllocFrame +%%% AllocFrame: +%%% SP = temp2 [if FrameSize == MaxStack] +%%% SP -= FrameSize [if FrameSize != MaxStack] +%%% *(SP + FrameSize-WordSize) = RA [if ClobbersRA] +%%% goto OldStart +%%% OldStart: +%%% ... +%%% IncStack: +%%% temp1 = RA +%%% call inc_stack; nop +%%% RA = temp1 +%%% goto NewStart + +do_prologue(CFG, Context) -> + MaxStack = context_maxstack(Context), + if MaxStack > 0 -> + FrameSize = context_framesize(Context), + OldStartLab = hipe_sparc_cfg:start_label(CFG), + NewStartLab = hipe_gensym:get_next_label(sparc), + %% + P = hipe_sparc:mk_temp(hipe_sparc_registers:proc_pointer(), 'untagged'), + Temp1 = hipe_sparc:mk_temp1(), + SP = hipe_sparc:mk_sp(), + %% + RA = hipe_sparc:mk_ra(), + ClobbersRA = context_clobbers_ra(Context), + GotoOldStartCode = [hipe_sparc:mk_b_label(OldStartLab)], + AllocFrameCodeTail = + case ClobbersRA of + false -> GotoOldStartCode; + true -> mk_store(RA, SP, FrameSize-word_size(), GotoOldStartCode) + end, + %% + Arity = context_arity(Context), + Guaranteed = erlang:max(0, (?SPARC_LEAF_WORDS - Arity) * word_size()), + %% + {CFG1,NewStartCode} = + if MaxStack =< Guaranteed -> + %% io:format("~w: MaxStack ~w =< Guaranteed ~w :-)\n", [?MODULE,MaxStack,Guaranteed]), + AllocFrameCode = adjust_sp(-FrameSize, AllocFrameCodeTail), + NewStartCode0 = AllocFrameCode, % no mflr needed + {CFG,NewStartCode0}; + true -> + %% io:format("~w: MaxStack ~w > Guaranteed ~w :-(\n", [?MODULE,MaxStack,Guaranteed]), + AllocFrameLab = hipe_gensym:get_next_label(sparc), + IncStackLab = hipe_gensym:get_next_label(sparc), + Temp2 = hipe_sparc:mk_temp2(), + %% + NewStartCodeTail2 = + [hipe_sparc:mk_pseudo_bp('lu', IncStackLab, AllocFrameLab, 0.01)], + NewStartCodeTail1 = NewStartCodeTail2, % no mflr needed + NewStartCode0 = + mk_load(P, ?P_NSP_LIMIT, Temp1, + hipe_sparc:mk_addi(SP, -MaxStack, Temp2, + [hipe_sparc:mk_alu('subcc', Temp2, Temp1, hipe_sparc:mk_g0()) | + NewStartCodeTail1])), + %% + AllocFrameCode = + if MaxStack =:= FrameSize -> + %% io:format("~w: MaxStack =:= FrameSize =:= ~w :-)\n", [?MODULE,MaxStack]), + [hipe_sparc:mk_mov(Temp2, SP) | + AllocFrameCodeTail]; + true -> + %% io:format("~w: MaxStack ~w =/= FrameSize ~w :-(\n", [?MODULE,MaxStack,FrameSize]), + adjust_sp(-FrameSize, AllocFrameCodeTail) + end, + %% + IncStackCodeTail = + [hipe_sparc:mk_call_rec(hipe_sparc:mk_prim('inc_stack_0'), + mk_minimal_sdesc(Context), not_remote), + hipe_sparc:mk_mov(Temp1, RA), + hipe_sparc:mk_b_label(NewStartLab)], + IncStackCode = + [hipe_sparc:mk_mov(RA, Temp1) | IncStackCodeTail], % mflr always needed + %% + CFG0a = hipe_sparc_cfg:bb_add(CFG, AllocFrameLab, + hipe_bb:mk_bb(AllocFrameCode)), + CFG0b = hipe_sparc_cfg:bb_add(CFG0a, IncStackLab, + hipe_bb:mk_bb(IncStackCode)), + %% + {CFG0b,NewStartCode0} + end, + %% + CFG2 = hipe_sparc_cfg:bb_add(CFG1, NewStartLab, + hipe_bb:mk_bb(NewStartCode)), + hipe_sparc_cfg:start_label_update(CFG2, NewStartLab); + true -> + CFG + end. + +%%% Create a load instruction. +%%% May clobber Dst early for large offsets. In principle we could +%%% clobber TEMP2 if Dst =:= Base, but Dst =/= Base here in frame. + +mk_load(Base, Offset, Dst, Rest) -> + LdOp = 'lduw', % XXX: sparc64: ldx + hipe_sparc:mk_load(LdOp, Base, Offset, Dst, 'error', Rest). + +mk_fload(Base, Offset, Dst) -> + hipe_sparc:mk_fload(Base, Offset, Dst, 'temp2'). + +%%% Create a store instruction. +%%% May clobber TEMP2 for large offsets. + +mk_store(Src, Base, Offset, Rest) -> + StOp = 'stw', % XXX: sparc64: stx + hipe_sparc:mk_store(StOp, Src, Base, Offset, 'temp2', Rest). + +mk_fstore(Src, Base, Offset) -> + hipe_sparc:mk_fstore(Src, Base, Offset, 'temp2'). + +%%% typeof_temp -- what's temp's type? + +typeof_temp(Temp) -> + hipe_sparc:temp_type(Temp). + +%%% Check if an operand is a pseudo-Temp. + +src_is_pseudo(Src) -> + hipe_sparc:is_temp(Src) andalso temp_is_pseudo(Src). + +temp_is_pseudo(Temp) -> + not(hipe_sparc:temp_is_precoloured(Temp)). + +%%% +%%% Detect if a Defun's body clobbers RA. +%%% + +clobbers_ra(Insns) -> + case Insns of + [#pseudo_call{}|_] -> true; + %% moves to RA cannot occur yet + [_|Rest] -> clobbers_ra(Rest); + [] -> false + end. + +%%% +%%% Build the set of all temps used in a Defun's body. +%%% + +all_temps(Code, Formals) -> + S0 = find_temps(Code, tset_empty()), + S1 = tset_del_list(S0, Formals), + tset_filter(S1, fun(T) -> temp_is_pseudo(T) end). + +find_temps([I|Insns], S0) -> + S1 = tset_add_list(S0, hipe_sparc_defuse:insn_def_all(I)), + S2 = tset_add_list(S1, hipe_sparc_defuse:insn_use_all(I)), + find_temps(Insns, S2); +find_temps([], S) -> + S. + +tset_empty() -> + gb_sets:new(). + +tset_size(S) -> + gb_sets:size(S). + +tset_insert(S, T) -> + gb_sets:add_element(T, S). + +tset_add_list(S, Ts) -> + gb_sets:union(S, gb_sets:from_list(Ts)). + +tset_del_list(S, Ts) -> + gb_sets:subtract(S, gb_sets:from_list(Ts)). + +tset_filter(S, F) -> + gb_sets:filter(F, S). + +tset_to_list(S) -> + gb_sets:to_list(S). + +%%% +%%% Compute minimum permissible frame size, ignoring spilled temps. +%%% This is done to ensure that we won't have to adjust the frame size +%%% in the middle of a tailcall. +%%% + +defun_minframe(Defun) -> + MaxTailArity = body_mta(hipe_sparc:defun_code(Defun), 0), + MyArity = length(fix_formals(hipe_sparc:defun_formals(Defun))), + erlang:max(MaxTailArity - MyArity, 0). + +body_mta([I|Code], MTA) -> + body_mta(Code, insn_mta(I, MTA)); +body_mta([], MTA) -> + MTA. + +insn_mta(I, MTA) -> + case I of + #pseudo_tailcall{arity=Arity} -> + erlang:max(MTA, Arity - hipe_sparc_registers:nr_args()); + _ -> MTA + end. + +%%% +%%% Ensure that we have enough temps to satisfy the minimum frame size, +%%% if necessary by prepending unused dummy temps. +%%% + +ensure_minframe(MinFrame, Temps) -> + ensure_minframe(MinFrame, tset_size(Temps), Temps). + +ensure_minframe(MinFrame, Frame, Temps) -> + if MinFrame > Frame -> + Temp = hipe_sparc:mk_new_temp('untagged'), + ensure_minframe(MinFrame, Frame+1, tset_insert(Temps, Temp)); + true -> Temps + end. + +word_size() -> + hipe_rtl_arch:word_size(). diff --git a/lib/hipe/sparc/hipe_sparc_liveness_all.erl b/lib/hipe/sparc/hipe_sparc_liveness_all.erl new file mode 100644 index 0000000000..c6f78f9f7a --- /dev/null +++ b/lib/hipe/sparc/hipe_sparc_liveness_all.erl @@ -0,0 +1,38 @@ +%% -*- erlang-indent-level: 2 -*- +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2008-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_sparc_liveness_all). +-export([analyse/1]). +-export([liveout/2]). + +-include("hipe_sparc.hrl"). +-include("../flow/liveness.inc"). + +analyse(CFG) -> analyze(CFG). +cfg_bb(CFG, L) -> hipe_sparc_cfg:bb(CFG, L). +cfg_postorder(CFG) -> hipe_sparc_cfg:postorder(CFG). +cfg_succ(CFG, L) -> hipe_sparc_cfg:succ(CFG, L). +uses(Insn) -> hipe_sparc_defuse:insn_use_all(Insn). +defines(Insn) -> hipe_sparc_defuse:insn_def_all(Insn). +liveout_no_succ() -> + ordsets:from_list(lists:map(fun({Reg,Type}) -> + hipe_sparc:mk_temp(Reg, Type) + end, + hipe_sparc_registers:live_at_return())). diff --git a/lib/hipe/sparc/hipe_sparc_liveness_fpr.erl b/lib/hipe/sparc/hipe_sparc_liveness_fpr.erl new file mode 100644 index 0000000000..ac67e499ad --- /dev/null +++ b/lib/hipe/sparc/hipe_sparc_liveness_fpr.erl @@ -0,0 +1,34 @@ +%% -*- erlang-indent-level: 2 -*- +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2008-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_sparc_liveness_fpr). +-export([analyse/1]). +-export([liveout/2]). + +-include("hipe_sparc.hrl"). +-include("../flow/liveness.inc"). + +analyse(CFG) -> analyze(CFG). +cfg_bb(CFG, L) -> hipe_sparc_cfg:bb(CFG, L). +cfg_postorder(CFG) -> hipe_sparc_cfg:postorder(CFG). +cfg_succ(CFG, L) -> hipe_sparc_cfg:succ(CFG, L). +uses(Insn) -> hipe_sparc_defuse:insn_use_fpr(Insn). +defines(Insn) -> hipe_sparc_defuse:insn_def_fpr(Insn). +liveout_no_succ() -> []. diff --git a/lib/hipe/sparc/hipe_sparc_liveness_gpr.erl b/lib/hipe/sparc/hipe_sparc_liveness_gpr.erl new file mode 100644 index 0000000000..0b07ae5c9d --- /dev/null +++ b/lib/hipe/sparc/hipe_sparc_liveness_gpr.erl @@ -0,0 +1,38 @@ +%% -*- erlang-indent-level: 2 -*- +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2007-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_sparc_liveness_gpr). +-export([analyse/1]). +-export([liveout/2]). + +-include("hipe_sparc.hrl"). +-include("../flow/liveness.inc"). + +analyse(CFG) -> analyze(CFG). +cfg_bb(CFG, L) -> hipe_sparc_cfg:bb(CFG, L). +cfg_postorder(CFG) -> hipe_sparc_cfg:postorder(CFG). +cfg_succ(CFG, L) -> hipe_sparc_cfg:succ(CFG, L). +uses(Insn) -> hipe_sparc_defuse:insn_use_gpr(Insn). +defines(Insn) -> hipe_sparc_defuse:insn_def_gpr(Insn). +liveout_no_succ() -> + ordsets:from_list(lists:map(fun({Reg,Type}) -> + hipe_sparc:mk_temp(Reg, Type) + end, + hipe_sparc_registers:live_at_return())). diff --git a/lib/hipe/sparc/hipe_sparc_main.erl b/lib/hipe/sparc/hipe_sparc_main.erl new file mode 100644 index 0000000000..2e5c8e0494 --- /dev/null +++ b/lib/hipe/sparc/hipe_sparc_main.erl @@ -0,0 +1,58 @@ +%% -*- erlang-indent-level: 2 -*- +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2004-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_sparc_main). +-export([rtl_to_sparc/3]). + +rtl_to_sparc(MFA, RTL, Options) -> + Defun1 = hipe_rtl_to_sparc:translate(RTL), + %% io:format("~w: after translate\n", [?MODULE]), + %% hipe_sparc_pp:pp(Defun1), + Defun2 = hipe_sparc_ra:ra(Defun1, Options), + %% io:format("~w: after regalloc\n", [?MODULE]), + %% hipe_sparc_pp:pp(Defun2), + Defun3 = hipe_sparc_frame:frame(Defun2), + %% io:format("~w: after frame\n", [?MODULE]), + %% hipe_sparc_pp:pp(Defun3), + Defun4 = hipe_sparc_finalise:finalise(Defun3), + %% io:format("~w: after finalise\n", [?MODULE]), + pp(Defun4, MFA, Options), + {native, sparc, {unprofiled, Defun4}}. + +pp(Defun, MFA, Options) -> + case proplists:get_value(pp_native, Options) of + true -> + hipe_sparc_pp:pp(Defun); + {only,Lst} when is_list(Lst) -> + case lists:member(MFA,Lst) of + true -> + hipe_sparc_pp:pp(Defun); + false -> + ok + end; + {only,MFA} -> + hipe_sparc_pp:pp(Defun); + {file,FileName} -> + {ok, File} = file:open(FileName, [write,append]), + hipe_sparc_pp:pp(File, Defun), + ok = file:close(File); + _ -> + ok + end. diff --git a/lib/hipe/sparc/hipe_sparc_pp.erl b/lib/hipe/sparc/hipe_sparc_pp.erl new file mode 100644 index 0000000000..6b49acdd11 --- /dev/null +++ b/lib/hipe/sparc/hipe_sparc_pp.erl @@ -0,0 +1,342 @@ +%% -*- erlang-indent-level: 2 -*- +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2002-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_sparc_pp). +-export([pp/1, pp/2, pp_insn/1]). +-include("hipe_sparc.hrl"). + +pp(Defun) -> + pp(standard_io, Defun). + +pp(Dev, #defun{mfa={M,F,A}, code=Code, data=Data}) -> + Fname = atom_to_list(M)++"_"++atom_to_list(F)++"_"++integer_to_list(A), + io:format(Dev, "\t.text\n", []), + io:format(Dev, "\t.align 4\n", []), + io:format(Dev, "\t.global ~s\n", [Fname]), + io:format(Dev, "~s:\n", [Fname]), + pp_insns(Dev, Code, Fname), + io:format(Dev, "\t.rodata\n", []), + io:format(Dev, "\t.align 4\n", []), + hipe_data_pp:pp(Dev, Data, sparc, Fname), + io:format(Dev, "\n", []). + +pp_insns(Dev, [I|Is], Fname) -> + pp_insn(Dev, I, Fname), + pp_insns(Dev, Is, Fname); +pp_insns(_, [], _) -> + []. + +pp_insn(I) -> + pp_insn(standard_io, I, ""). + +pp_insn(Dev, I, Pre) -> + case I of + #alu{aluop=AluOp, dst=Dst, src1=Src1, src2=Src2} -> + io:format(Dev, "\t~s ", [alu_op_name(AluOp)]), + case aluop_is_ldop(AluOp) of + true -> + io:format(Dev, "[", []), + pp_temp(Dev, Src1), + io:format(Dev, " + ", []), + pp_src(Dev, Src2), + io:format(Dev, "]", []); + false -> + pp_temp(Dev, Src1), + io:format(Dev, ", ", []), + pp_src(Dev, Src2) + end, + io:format(Dev, ", ", []), + pp_temp(Dev, Dst), + io:format(Dev, "\n", []); + #bp{'cond'=Cond, label=Label, pred=Pred} -> + io:format(Dev, "\tb~w,~w .~s_~w\n", + [cond_name(Cond), pred_name(Pred), Pre, Label]); + %% #br{} -> pp_br(Dev, I, Pre); + #call_rec{'fun'=Fun, sdesc=SDesc, linkage=Linkage} -> + io:format(Dev, "\tcall ", []), + pp_fun(Dev, Fun), + io:format(Dev, " #", []), + pp_sdesc(Dev, Pre, SDesc), + io:format(Dev, " ~w\n", [Linkage]); + #call_tail{'fun'=Fun, linkage=Linkage} -> + io:format(Dev, "\tb ", []), + pp_fun(Dev, Fun), + io:format(Dev, " # ~w\n", [Linkage]); + #comment{term=Term} -> + io:format(Dev, "\t# ~p\n", [Term]); + #jmp{src1=Src1, src2=Src2, labels=Labels} -> + io:format(Dev, "\tjmp [", []), + pp_temp(Dev, Src1), + io:format(Dev, " + ", []), + pp_src(Dev, Src2), + io:format(Dev, "]", []), + case Labels of + [] -> []; + _ -> + io:format(Dev, " #", []), + pp_labels(Dev, Labels, Pre) + end, + io:format(Dev, "\n", []); + #jmpl{src=Src, sdesc=SDesc} -> + io:format(Dev, "\tjmpl [", []), + pp_temp(Dev, Src), + io:format(Dev, " + 0], %o7 # ", []), + pp_sdesc(Dev, Pre, SDesc), + io:format(Dev, "\n", []); + #label{label=Label} -> + io:format(Dev, ".~s_~w:~n", [Pre, Label]); + #pseudo_bp{'cond'=Cond, true_label=TrueLab, false_label=FalseLab, pred=Pred} -> + io:format(Dev, "\tpseudo_b~w,~w .~s_~w # .~s_~w\n", + [cond_name(Cond), pred_name(Pred), Pre, TrueLab, Pre, FalseLab]); + %% #pseudo_br{} -> pp_pseudo_br(Dev, I, Pre); + #pseudo_call{funv=FunV, sdesc=SDesc, contlab=ContLab, linkage=Linkage} -> + io:format(Dev, "\tpseudo_call ", []), + pp_funv(Dev, FunV), + io:format(Dev, " # contlab .~s_~w", [Pre, ContLab]), + pp_sdesc(Dev, Pre, SDesc), + io:format(Dev, " ~w\n", [Linkage]); + #pseudo_call_prepare{nrstkargs=NrStkArgs} -> + SP = hipe_sparc_registers:reg_name_gpr(hipe_sparc_registers:stack_pointer()), + io:format(Dev, "\tsub ~s, ~w, ~s # pseudo_call_prepare\n", + [SP, 4*NrStkArgs, SP]); + #pseudo_move{src=Src, dst=Dst} -> + io:format(Dev, "\tpseudo_move ", []), + pp_temp(Dev, Src), + io:format(Dev, ", ", []), + pp_temp(Dev, Dst), + io:format(Dev, "\n", []); + #pseudo_ret{} -> + io:format(Dev, "\tpseudo_ret\n", []); + #pseudo_set{imm=Imm, dst=Dst} -> + io:format(Dev, "\tpseudo_set ", []), + pp_imm(Dev, Imm), + io:format(Dev, ", ", []), + pp_temp(Dev, Dst), + io:format(Dev, "\n", []); + #pseudo_tailcall{funv=FunV, arity=Arity, stkargs=StkArgs, linkage=Linkage} -> + io:format(Dev, "\tpseudo_tailcall ", []), + pp_funv(Dev, FunV), + io:format(Dev, "/~w (", [Arity]), + pp_args(Dev, StkArgs), + io:format(Dev, ") ~w\n", [Linkage]); + #pseudo_tailcall_prepare{} -> + io:format(Dev, "\tpseudo_tailcall_prepare\n", []); + #rdy{dst=Dst} -> + io:format(Dev, "\trd %y, ", []), + pp_temp(Dev, Dst), + io:format(Dev, "\n", []); + #sethi{dst=Dst, uimm22=#sparc_uimm22{value=Value}} -> + io:format(Dev, "\tsethi ", []), + pp_hex(Dev, Value), + io:format(Dev, ", ", []), + pp_temp(Dev, Dst), + io:format(Dev, "\n", []); + #store{stop=StOp, src=Src, base=Base, disp=Disp} -> + io:format(Dev, "\t~s ", [stop_name(StOp)]), + pp_temp(Dev, Src), + io:format(Dev, ", [", []), + pp_temp(Dev, Base), + io:format(Dev, " + ", []), + pp_src(Dev, Disp), + io:format(Dev, "]\n", []); + #fp_binary{fp_binop=FpBinOp, src1=Src1, src2=Src2, dst=Dst} -> + io:format(Dev, "\t~s ", [FpBinOp]), + pp_temp(Dev, Src1), + io:format(Dev, ", ", []), + pp_temp(Dev, Src2), + io:format(Dev, ", ", []), + pp_temp(Dev, Dst), + io:format(Dev, "\n", []); + #fp_unary{fp_unop=FpUnOp, src=Src, dst=Dst} -> + io:format(Dev, "\t~s ", [FpUnOp]), + pp_temp(Dev, Src), + io:format(Dev, ", ", []), + pp_temp(Dev, Dst), + io:format(Dev, "\n", []); + #pseudo_fload{base=Base, disp=Disp, dst=Dst, is_single=IsSingle} -> + io:format(Dev, "\t~s [", + [case IsSingle of + true -> 'ldf'; + _ -> 'pseudo_fload' end]), + pp_temp(Dev, Base), + io:format(Dev, " + ", []), + pp_simm13(Dev, Disp), + io:format(Dev, "], ", []), + pp_temp(Dev, Dst), + io:format(Dev, "\n", []); + #pseudo_fmove{src=Src, dst=Dst} -> + io:format(Dev, "\tpseudo_fmove ", []), + pp_temp(Dev, Src), + io:format(Dev, ", ", []), + pp_temp(Dev, Dst), + io:format(Dev, "\n", []); + #pseudo_fstore{src=Src, base=Base, disp=Disp} -> + io:format(Dev, "\tpseudo_fstore ", []), + pp_temp(Dev, Src), + io:format(Dev, ", [", []), + pp_temp(Dev, Base), + io:format(Dev, " + ", []), + pp_simm13(Dev, Disp), + io:format(Dev, "]\n", []); + _ -> + exit({?MODULE, pp_insn, I}) + end. + +-ifdef(notdef). % XXX: only for sparc64, alas +pp_br(Dev, I, Pre) -> + #br{rcond=RCond, src=Src, label=Label, pred=Pred} = I, + io:format(Dev, "\tbr~w,~w ", [rcond_name(RCond), pred_name(Pred)]), + pp_temp(Dev, Src), + io:format(Dev, ", .~s_~w\n", [Pre, Label]). + +pp_pseudo_br(Dev, I, Pre) -> + #pseudo_br{rcond=RCond, src=Src, true_label=TrueLab, false_label=FalseLab, pred=Pred} = I, + io:format(Dev, "\tpseudo_br~w,~w ", [rcond_name(RCond), pred_name(Pred)]), + pp_src(Dev, Src), + io:format(Dev, ", .~s_~w # .~s_~w\n", [Pre, TrueLab, Pre, FalseLab]). +-endif. + +to_hex(N) -> + io_lib:format("~.16x", [N, "0x"]). + +pp_sdesc(Dev, Pre, #sparc_sdesc{exnlab=ExnLab,fsize=FSize,arity=Arity,live=Live}) -> + pp_sdesc_exnlab(Dev, Pre, ExnLab), + io:format(Dev, " ~s ~w [", [to_hex(FSize), Arity]), + pp_sdesc_live(Dev, Live), + io:format(Dev, "]", []). + +pp_sdesc_exnlab(Dev, _, []) -> io:format(Dev, " []", []); +pp_sdesc_exnlab(Dev, Pre, ExnLab) -> io:format(Dev, " .~s_~w", [Pre, ExnLab]). + +pp_sdesc_live(_, {}) -> []; +pp_sdesc_live(Dev, Live) -> pp_sdesc_live(Dev, Live, 1). + +pp_sdesc_live(Dev, Live, I) -> + io:format(Dev, "~s", [to_hex(element(I, Live))]), + if I < tuple_size(Live) -> + io:format(Dev, ",", []), + pp_sdesc_live(Dev, Live, I+1); + true -> [] + end. + +pp_labels(Dev, [Label|Labels], Pre) -> + io:format(Dev, " .~s_~w", [Pre, Label]), + pp_labels(Dev, Labels, Pre); +pp_labels(_, [], _) -> + []. + +pp_fun(Dev, Fun) -> + case Fun of + #sparc_mfa{m=M, f=F, a=A} -> + io:format(Dev, "~w:~w/~w", [M, F, A]); + #sparc_prim{prim=Prim} -> + io:format(Dev, "~w", [Prim]) + end. + +pp_funv(Dev, FunV) -> + case FunV of + #sparc_temp{} -> + pp_temp(Dev, FunV); + Fun -> + pp_fun(Dev, Fun) + end. + +alu_op_name(Op) -> Op. + +aluop_is_ldop(AluOp) -> + case AluOp of + 'ldsb' -> true; + 'ldsh' -> true; + 'ldsw' -> true; + 'ldub' -> true; + 'lduh' -> true; + 'lduw' -> true; + 'ldx' -> true; + _ -> false + end. + +cond_name(Cond) -> Cond. +%%rcond_name(RCond) -> RCond. + +pred_name(Pred) -> + if Pred >= 0.5 -> 'pt'; + true -> 'pn' + end. + +stop_name(StOp) -> StOp. + +pp_temp(Dev, Temp=#sparc_temp{reg=Reg, type=Type}) -> + case hipe_sparc:temp_is_precoloured(Temp) of + true -> + Name = + case Type of + double -> hipe_sparc_registers:reg_name_fpr(Reg); + _ -> hipe_sparc_registers:reg_name_gpr(Reg) + end, + io:format(Dev, "~s", [Name]); + false -> + Tag = + case Type of + double -> "f"; + tagged -> "t"; + untagged -> "u" + end, + io:format(Dev, "~s~w", [Tag, Reg]) + end. + +pp_hex(Dev, Value) -> io:format(Dev, "~s", [to_hex(Value)]). +pp_simm13(Dev, #sparc_simm13{value=Value}) -> pp_hex(Dev, Value). +pp_uimm5(Dev, #sparc_uimm5{value=Value}) -> pp_hex(Dev, Value). + +pp_imm(Dev, Value) -> + if is_integer(Value) -> pp_hex(Dev, Value); + true -> io:format(Dev, "~w", [Value]) + end. + +pp_src(Dev, Src) -> + case Src of + #sparc_temp{} -> + pp_temp(Dev, Src); + #sparc_simm13{} -> + pp_simm13(Dev, Src); + #sparc_uimm5{} -> % XXX: sparc64: uimm6 + pp_uimm5(Dev, Src) + end. + +pp_arg(Dev, Arg) -> + case Arg of + #sparc_temp{} -> + pp_temp(Dev, Arg); + _ -> + pp_hex(Dev, Arg) + end. + +pp_args(Dev, [A|As]) -> + pp_arg(Dev, A), + pp_comma_args(Dev, As); +pp_args(_, []) -> + []. + +pp_comma_args(Dev, [A|As]) -> + io:format(Dev, ", ", []), + pp_arg(Dev, A), + pp_comma_args(Dev, As); +pp_comma_args(_, []) -> + []. diff --git a/lib/hipe/sparc/hipe_sparc_ra.erl b/lib/hipe/sparc/hipe_sparc_ra.erl new file mode 100644 index 0000000000..40360e97fe --- /dev/null +++ b/lib/hipe/sparc/hipe_sparc_ra.erl @@ -0,0 +1,56 @@ +%% -*- 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% +%% + +-module(hipe_sparc_ra). +-export([ra/2]). + +ra(Defun0, Options) -> + %% hipe_sparc_pp:pp(Defun0), + {Defun1, Coloring_fp, SpillIndex} + = case proplists:get_bool(inline_fp, Options) of + true -> + hipe_regalloc_loop:ra_fp(Defun0, Options, + hipe_coalescing_regalloc, + hipe_sparc_specific_fp); + false -> + {Defun0,[],0} + end, + %% hipe_sparc_pp:pp(Defun1), + {Defun2, Coloring} + = case proplists:get_value(regalloc, Options, coalescing) of + coalescing -> + ra(Defun1, SpillIndex, Options, hipe_coalescing_regalloc); + optimistic -> + ra(Defun1, SpillIndex, Options, hipe_optimistic_regalloc); + graph_color -> + ra(Defun1, SpillIndex, Options, hipe_graph_coloring_regalloc); + linear_scan -> + hipe_sparc_ra_ls:ra(Defun1, SpillIndex, Options); + naive -> + hipe_sparc_ra_naive:ra(Defun1, Coloring_fp, Options); + _ -> + exit({unknown_regalloc_compiler_option, + proplists:get_value(regalloc,Options)}) + end, + %% hipe_sparc_pp:pp(Defun2), + hipe_sparc_ra_finalise:finalise(Defun2, Coloring, Coloring_fp). + +ra(Defun, SpillIndex, Options, RegAllocMod) -> + hipe_regalloc_loop:ra(Defun, SpillIndex, Options, RegAllocMod, hipe_sparc_specific). diff --git a/lib/hipe/sparc/hipe_sparc_ra_finalise.erl b/lib/hipe/sparc/hipe_sparc_ra_finalise.erl new file mode 100644 index 0000000000..3403636118 --- /dev/null +++ b/lib/hipe/sparc/hipe_sparc_ra_finalise.erl @@ -0,0 +1,254 @@ +%% -*- erlang-indent-level: 2 -*- +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2008-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_sparc_ra_finalise). +-export([finalise/3]). +-include("hipe_sparc.hrl"). + +finalise(Defun, TempMap, FPMap0) -> + Code = hipe_sparc:defun_code(Defun), + {_, SpillLimit} = hipe_sparc:defun_var_range(Defun), + Map = mk_ra_map(TempMap, SpillLimit), + FPMap1 = mk_ra_map_fp(FPMap0, SpillLimit), + NewCode = ra_code(Code, Map, FPMap1, []), + Defun#defun{code=NewCode}. + +ra_code([I|Insns], Map, FPMap, Accum) -> + ra_code(Insns, Map, FPMap, [ra_insn(I, Map, FPMap) | Accum]); +ra_code([], _Map, _FPMap, Accum) -> + lists:reverse(Accum). + +ra_insn(I, Map, FPMap) -> + case I of + #alu{} -> ra_alu(I, Map); + #jmp{} -> ra_jmp(I, Map); + %% #pseudo_br{} -> ra_pseudo_br(I, Map); + #pseudo_call{} -> ra_pseudo_call(I, Map); + #pseudo_move{} -> ra_pseudo_move(I, Map); + #pseudo_set{} -> ra_pseudo_set(I, Map); + #pseudo_tailcall{} -> ra_pseudo_tailcall(I, Map); + #rdy{} -> ra_rdy(I, Map); + #sethi{} -> ra_sethi(I, Map); + #store{} -> ra_store(I, Map); + #fp_binary{} -> ra_fp_binary(I, FPMap); + #fp_unary{} -> ra_fp_unary(I, FPMap); + #pseudo_fload{} -> ra_pseudo_fload(I, Map, FPMap); + #pseudo_fmove{} -> ra_pseudo_fmove(I, FPMap); + #pseudo_fstore{} -> ra_pseudo_fstore(I, Map, FPMap); + _ -> I + end. + +ra_alu(I=#alu{src1=Src1,src2=Src2,dst=Dst}, Map) -> + NewSrc1 = ra_temp(Src1, Map), + NewSrc2 = ra_src(Src2, Map), + NewDst = ra_temp(Dst, Map), + I#alu{src1=NewSrc1,src2=NewSrc2,dst=NewDst}. + +ra_jmp(I=#jmp{src1=Src1,src2=Src2}, Map) -> + NewSrc1 = ra_temp(Src1, Map), + NewSrc2 = ra_src(Src2, Map), + I#jmp{src1=NewSrc1,src2=NewSrc2}. + +-ifdef(notdef). % XXX: only for sparc64, alas +ra_pseudo_br(I=#pseudo_br{src=Src}, Map) -> + NewSrc = ra_temp(Src, Map), + I#pseudo_br{src=NewSrc}. +-endif. + +ra_pseudo_call(I=#pseudo_call{funv=FunV}, Map) -> + NewFunV = ra_funv(FunV, Map), + I#pseudo_call{funv=NewFunV}. + +ra_pseudo_move(I=#pseudo_move{src=Src,dst=Dst}, Map) -> + NewSrc = ra_temp(Src, Map), + NewDst = ra_temp(Dst, Map), + I#pseudo_move{src=NewSrc,dst=NewDst}. + +ra_pseudo_set(I=#pseudo_set{dst=Dst}, Map) -> + NewDst = ra_temp(Dst, Map), + I#pseudo_set{dst=NewDst}. + +ra_pseudo_tailcall(I=#pseudo_tailcall{funv=FunV,stkargs=StkArgs}, Map) -> + NewFunV = ra_funv(FunV, Map), + NewStkArgs = ra_args(StkArgs, Map), + I#pseudo_tailcall{funv=NewFunV,stkargs=NewStkArgs}. + +ra_rdy(I=#rdy{dst=Dst}, Map) -> + NewDst = ra_temp(Dst, Map), + I#rdy{dst=NewDst}. + +ra_sethi(I=#sethi{dst=Dst}, Map) -> + NewDst = ra_temp(Dst, Map), + I#sethi{dst=NewDst}. + +ra_store(I=#store{src=Src,base=Base,disp=Disp}, Map) -> + NewSrc = ra_temp(Src, Map), + NewBase = ra_temp(Base, Map), + NewDisp = ra_src(Disp, Map), + I#store{src=NewSrc,base=NewBase,disp=NewDisp}. + +ra_fp_binary(I=#fp_binary{src1=Src1,src2=Src2,dst=Dst}, FPMap) -> + NewSrc1 = ra_temp_fp(Src1, FPMap), + NewSrc2 = ra_temp_fp(Src2, FPMap), + NewDst = ra_temp_fp(Dst, FPMap), + I#fp_binary{src1=NewSrc1,src2=NewSrc2,dst=NewDst}. + +ra_fp_unary(I=#fp_unary{src=Src,dst=Dst}, FPMap) -> + NewSrc = ra_temp_fp(Src, FPMap), + NewDst = ra_temp_fp(Dst, FPMap), + I#fp_unary{src=NewSrc,dst=NewDst}. + +ra_pseudo_fload(I=#pseudo_fload{base=Base,dst=Dst}, Map, FPMap) -> + NewBase = ra_temp(Base, Map), + NewDst = ra_temp_fp(Dst, FPMap), + I#pseudo_fload{base=NewBase,dst=NewDst}. + +ra_pseudo_fmove(I=#pseudo_fmove{src=Src,dst=Dst}, FPMap) -> + NewSrc = ra_temp_fp(Src, FPMap), + NewDst = ra_temp_fp(Dst, FPMap), + I#pseudo_fmove{src=NewSrc,dst=NewDst}. + +ra_pseudo_fstore(I=#pseudo_fstore{src=Src,base=Base}, Map, FPMap) -> + NewSrc = ra_temp_fp(Src, FPMap), + NewBase = ra_temp(Base, Map), + I#pseudo_fstore{src=NewSrc,base=NewBase}. + +%%% Tailcall stack arguments. + +ra_args([Arg|Args], Map) -> + [ra_temp_or_imm(Arg, Map) | ra_args(Args, Map)]; +ra_args([], _) -> + []. + +ra_temp_or_imm(Arg, Map) -> + case hipe_sparc:is_temp(Arg) of + true -> + ra_temp(Arg, Map); + false -> + Arg + end. + +%%% FunV, Src, and Temp operands. + +ra_funv(FunV, Map) -> + case FunV of + #sparc_temp{} -> ra_temp(FunV, Map); + _ -> FunV + end. + +ra_src(Src, Map) -> + case Src of + #sparc_temp{} -> ra_temp(Src, Map); + _ -> Src + end. + +ra_temp_fp(Temp, FPMap) -> + Reg = hipe_sparc:temp_reg(Temp), + double = hipe_sparc:temp_type(Temp), + case hipe_sparc_registers:is_precoloured_fpr(Reg) of + true -> Temp; + _ -> ra_temp_common(Reg, Temp, FPMap) + end. + +ra_temp(Temp, Map) -> + Reg = hipe_sparc:temp_reg(Temp), + case hipe_sparc:temp_type(Temp) of + 'double' -> + exit({?MODULE,ra_temp,Temp}); + _ -> + case hipe_sparc_registers:is_precoloured_gpr(Reg) of + true -> Temp; + _ -> ra_temp_common(Reg, Temp, Map) + end + end. + +ra_temp_common(Reg, Temp, Map) -> + case gb_trees:lookup(Reg, Map) of + {value, NewReg} -> Temp#sparc_temp{reg=NewReg}; + _ -> Temp + end. + +mk_ra_map(TempMap, SpillLimit) -> + %% Build a partial map from pseudo to reg or spill. + %% Spills are represented as pseudos with indices above SpillLimit. + %% (I'd prefer to use negative indices, but that breaks + %% hipe_sparc_registers:is_precoloured/1.) + %% The frame mapping proper is unchanged, since spills look just like + %% ordinary (un-allocated) pseudos. + lists:foldl(fun(MapLet, Map) -> + {Key,Val} = conv_ra_maplet(MapLet, SpillLimit, is_precoloured_gpr), + gb_trees:insert(Key, Val, Map) + end, + gb_trees:empty(), + TempMap). + +conv_ra_maplet(MapLet = {From,To}, SpillLimit, IsPrecoloured) -> + %% From should be a pseudo, or a hard reg mapped to itself. + if is_integer(From), From =< SpillLimit -> + case hipe_sparc_registers:IsPrecoloured(From) of + false -> []; + _ -> + case To of + {reg, From} -> []; + _ -> exit({?MODULE,conv_ra_maplet,MapLet}) + end + end; + true -> exit({?MODULE,conv_ra_maplet,MapLet}) + end, + %% end of From check + case To of + {reg, NewReg} -> + %% NewReg should be a hard reg, or a pseudo mapped + %% to itself (formals are handled this way). + if is_integer(NewReg) -> + case hipe_sparc_registers:IsPrecoloured(NewReg) of + true -> []; + _ -> if From =:= NewReg -> []; + true -> + exit({?MODULE,conv_ra_maplet,MapLet}) + end + end; + true -> exit({?MODULE,conv_ra_maplet,MapLet}) + end, + %% end of NewReg check + {From, NewReg}; + {spill, SpillIndex} -> + %% SpillIndex should be >= 0. + if is_integer(SpillIndex), SpillIndex >= 0 -> []; + true -> exit({?MODULE,conv_ra_maplet,MapLet}) + end, + %% end of SpillIndex check + ToTempNum = SpillLimit+SpillIndex+1, + MaxTempNum = hipe_gensym:get_var(sparc), + if MaxTempNum >= ToTempNum -> ok; + true -> hipe_gensym:set_var(sparc, ToTempNum) + end, + {From, ToTempNum}; + _ -> exit({?MODULE,conv_ra_maplet,MapLet}) + end. + +mk_ra_map_fp(FPMap, SpillLimit) -> + lists:foldl(fun(MapLet, Map) -> + {Key,Val} = conv_ra_maplet(MapLet, SpillLimit, + is_precoloured_fpr), + gb_trees:insert(Key, Val, Map) + end, + gb_trees:empty(), + FPMap). diff --git a/lib/hipe/sparc/hipe_sparc_ra_ls.erl b/lib/hipe/sparc/hipe_sparc_ra_ls.erl new file mode 100644 index 0000000000..cdb15e738c --- /dev/null +++ b/lib/hipe/sparc/hipe_sparc_ra_ls.erl @@ -0,0 +1,56 @@ +%% -*- erlang-indent-level: 2 -*- +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2008-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% +%% +%% Linear Scan register allocator for SPARC + +-module(hipe_sparc_ra_ls). +-export([ra/3]). + +ra(Defun, SpillIndex, Options) -> + NewDefun = Defun, %% hipe_${ARCH}_ra_rename:rename(Defun,Options), + CFG = hipe_sparc_cfg:init(NewDefun), + SpillLimit = hipe_sparc_specific:number_of_temporaries(CFG), + alloc(NewDefun, SpillIndex, SpillLimit, Options). + +alloc(Defun, SpillIndex, SpillLimit, Options) -> + CFG = hipe_sparc_cfg:init(Defun), + {Coloring, _NewSpillIndex} = + regalloc( + CFG, + hipe_sparc_registers:allocatable_gpr()-- + [hipe_sparc_registers:temp3(), + hipe_sparc_registers:temp2(), + hipe_sparc_registers:temp1()], + [hipe_sparc_cfg:start_label(CFG)], + SpillIndex, SpillLimit, Options, + hipe_sparc_specific), + {NewDefun, _DidSpill} = + hipe_sparc_ra_postconditions:check_and_rewrite( + Defun, Coloring, 'linearscan'), + TempMap = hipe_temp_map:cols2tuple(Coloring, hipe_sparc_specific), + {TempMap2,_NewSpillIndex2} = + hipe_spillmin:stackalloc(CFG, [], SpillIndex, Options, + hipe_sparc_specific, TempMap), + Coloring2 = + hipe_spillmin:mapmerge(hipe_temp_map:to_substlist(TempMap), TempMap2), + {NewDefun, Coloring2}. + +regalloc(CFG, PhysRegs, Entrypoints, SpillIndex, DontSpill, Options, Target) -> + hipe_ls_regalloc:regalloc( + CFG, PhysRegs, Entrypoints, SpillIndex, DontSpill, Options, Target). diff --git a/lib/hipe/sparc/hipe_sparc_ra_naive.erl b/lib/hipe/sparc/hipe_sparc_ra_naive.erl new file mode 100644 index 0000000000..8c378c4850 --- /dev/null +++ b/lib/hipe/sparc/hipe_sparc_ra_naive.erl @@ -0,0 +1,29 @@ +%% -*- 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_sparc_ra_naive). +-export([ra/3]). + +-include("hipe_sparc.hrl"). + +ra(Defun, _Coloring_fp, _Options) -> % -> {Defun, Coloring} + {NewDefun,_DidSpill} = + hipe_sparc_ra_postconditions:check_and_rewrite2(Defun, [], 'naive'), + {NewDefun, []}. diff --git a/lib/hipe/sparc/hipe_sparc_ra_postconditions.erl b/lib/hipe/sparc/hipe_sparc_ra_postconditions.erl new file mode 100644 index 0000000000..f7fdae0491 --- /dev/null +++ b/lib/hipe/sparc/hipe_sparc_ra_postconditions.erl @@ -0,0 +1,222 @@ +%% -*- erlang-indent-level: 2 -*- +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2002-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_sparc_ra_postconditions). + +-export([check_and_rewrite/3, check_and_rewrite2/3]). + +-include("hipe_sparc.hrl"). + +check_and_rewrite(Defun, Coloring, Allocator) -> + TempMap = hipe_temp_map:cols2tuple(Coloring, hipe_sparc_specific), + check_and_rewrite2(Defun, TempMap, Allocator). + +check_and_rewrite2(Defun, TempMap, Allocator) -> + Strategy = strategy(Allocator), + #defun{code=Code0} = Defun, + {Code1,DidSpill} = do_insns(Code0, TempMap, Strategy, [], false), + VarRange = {0, hipe_gensym:get_var(sparc)}, + {Defun#defun{code=Code1, var_range=VarRange}, + DidSpill}. + +strategy(Allocator) -> + case Allocator of + 'normal' -> 'new'; + 'linearscan' -> 'fixed'; + 'naive' -> 'fixed' + end. + +do_insns([I|Insns], TempMap, Strategy, Accum, DidSpill0) -> + {NewIs, DidSpill1} = do_insn(I, TempMap, Strategy), + do_insns(Insns, TempMap, Strategy, lists:reverse(NewIs, Accum), DidSpill0 or DidSpill1); +do_insns([], _TempMap, _Strategy, Accum, DidSpill) -> + {lists:reverse(Accum), DidSpill}. + +do_insn(I, TempMap, Strategy) -> + case I of + #alu{} -> do_alu(I, TempMap, Strategy); + #jmp{} -> do_jmp(I, TempMap, Strategy); + %% #pseudo_br{} -> do_pseudo_br(I, TempMap, Strategy); + #pseudo_call{} -> do_pseudo_call(I, TempMap, Strategy); + #pseudo_move{} -> do_pseudo_move(I, TempMap, Strategy); + #pseudo_set{} -> do_pseudo_set(I, TempMap, Strategy); + #pseudo_tailcall{} -> do_pseudo_tailcall(I, TempMap, Strategy); + #rdy{} -> do_rdy(I, TempMap, Strategy); + #sethi{} -> do_sethi(I, TempMap, Strategy); + #store{} -> do_store(I, TempMap, Strategy); + #pseudo_fload{} -> do_pseudo_fload(I, TempMap, Strategy); + #pseudo_fstore{} -> do_pseudo_fstore(I, TempMap, Strategy); + _ -> {[I], false} + end. + +%%% Fix relevant instruction types. + +do_alu(I=#alu{dst=Dst,src1=Src1,src2=Src2}, TempMap, Strategy) -> + {FixDst,NewDst,DidSpill1} = fix_dst(Dst, TempMap, Strategy), + {FixSrc1,NewSrc1,DidSpill2} = fix_src1(Src1, TempMap, Strategy), + {FixSrc2,NewSrc2,DidSpill3} = fix_src2_or_imm(Src2, TempMap, Strategy), + NewI = I#alu{dst=NewDst,src1=NewSrc1,src2=NewSrc2}, + {FixSrc1 ++ FixSrc2 ++ [NewI | FixDst], DidSpill1 or DidSpill2 or DidSpill3}. + +do_jmp(I=#jmp{src1=Src1,src2=Src2}, TempMap, Strategy) -> + {FixSrc1,NewSrc1,DidSpill1} = fix_src1(Src1, TempMap, Strategy), + {FixSrc2,NewSrc2,DidSpill2} = fix_src2_or_imm(Src2, TempMap, Strategy), + NewI = I#jmp{src1=NewSrc1,src2=NewSrc2}, + {FixSrc1 ++ FixSrc2 ++ [NewI], DidSpill1 or DidSpill2}. + +-ifdef(notdef). % XXX: only for sparc64, alas +do_pseudo_br(I=#pseudo_br{src=Src}, TempMap, Strategy) -> + {FixSrc,NewSrc,DidSpill} = fix_src1(Src, TempMap, Strategy), + NewI = I#pseudo_br{src=NewSrc}, + {FixSrc ++ [NewI], DidSpill}. +-endif. + +do_pseudo_call(I=#pseudo_call{funv=FunV}, TempMap, Strategy) -> + {FixFunV,NewFunV,DidSpill} = fix_funv(FunV, TempMap, Strategy), + NewI = I#pseudo_call{funv=NewFunV}, + {FixFunV ++ [NewI], DidSpill}. + +do_pseudo_move(I=#pseudo_move{src=Src,dst=Dst}, TempMap, Strategy) -> + %% Either Dst or Src (but not both) may be a pseudo temp. + %% pseudo_move is a special case: in [XXX: not pseudo_tailcall] + %% all other instructions, all temps must be non-pseudos + %% after register allocation. + case temp_is_spilled(Dst, TempMap) of + true -> % Src must not be a pseudo + {FixSrc,NewSrc,DidSpill} = fix_src1(Src, TempMap, Strategy), + NewI = I#pseudo_move{src=NewSrc}, + {FixSrc ++ [NewI], DidSpill}; + _ -> + {[I], false} + end. + +do_pseudo_set(I=#pseudo_set{dst=Dst}, TempMap, Strategy) -> + {FixDst,NewDst,DidSpill} = fix_dst(Dst, TempMap, Strategy), + NewI = I#pseudo_set{dst=NewDst}, + {[NewI | FixDst], DidSpill}. + +do_pseudo_tailcall(I=#pseudo_tailcall{funv=FunV}, TempMap, Strategy) -> + {FixFunV,NewFunV,DidSpill} = fix_funv(FunV, TempMap, Strategy), + NewI = I#pseudo_tailcall{funv=NewFunV}, + {FixFunV ++ [NewI], DidSpill}. + +do_rdy(I=#rdy{dst=Dst}, TempMap, Strategy) -> + {FixDst,NewDst,DidSpill} = fix_dst(Dst, TempMap, Strategy), + NewI = I#rdy{dst=NewDst}, + {[NewI | FixDst], DidSpill}. + +do_sethi(I=#sethi{dst=Dst}, TempMap, Strategy) -> + {FixDst,NewDst,DidSpill} = fix_dst(Dst, TempMap, Strategy), + NewI = I#sethi{dst=NewDst}, + {[NewI | FixDst], DidSpill}. + +do_store(I=#store{src=Src,base=Base,disp=Disp}, TempMap, Strategy) -> + {FixSrc,NewSrc,DidSpill1} = fix_src1(Src, TempMap, Strategy), + {FixBase,NewBase,DidSpill2} = fix_src2(Base, TempMap, Strategy), + {FixDisp,NewDisp,DidSpill3} = fix_src3_or_imm(Disp, TempMap, Strategy), + NewI = I#store{src=NewSrc,base=NewBase,disp=NewDisp}, + {FixSrc ++ FixBase ++ FixDisp ++ [NewI], DidSpill1 or DidSpill2 or DidSpill3}. + +do_pseudo_fload(I=#pseudo_fload{base=Base}, TempMap, Strategy) -> + {FixBase,NewBase,DidSpill} = fix_src1(Base, TempMap, Strategy), + NewI = I#pseudo_fload{base=NewBase}, + {FixBase ++ [NewI], DidSpill}. + +do_pseudo_fstore(I=#pseudo_fstore{base=Base}, TempMap, Strategy) -> + {FixBase,NewBase,DidSpill} = fix_src1(Base, TempMap, Strategy), + NewI = I#pseudo_fstore{base=NewBase}, + {FixBase ++ [NewI], DidSpill}. + +%%% Fix Dst and Src operands. + +fix_funv(FunV, TempMap, Strategy) -> + case FunV of + #sparc_temp{} -> fix_src3(FunV, TempMap, Strategy); + _ -> {[], FunV, false} + end. + +fix_src2_or_imm(Src2, TempMap, Strategy) -> + case Src2 of + #sparc_temp{} -> fix_src2(Src2, TempMap, Strategy); + _ -> {[], Src2, false} + end. + +fix_src3_or_imm(Src3, TempMap, Strategy) -> + case Src3 of + #sparc_temp{} -> fix_src3(Src3, TempMap, Strategy); + _ -> {[], Src3, false} + end. + +fix_src1(Src, TempMap, Strategy) -> + fix_src(Src, TempMap, temp1(Strategy)). + +temp1('new') -> []; +temp1('fixed') -> hipe_sparc_registers:temp1(). + +fix_src2(Src, TempMap, Strategy) -> + fix_src(Src, TempMap, temp2(Strategy)). + +temp2('new') -> []; +temp2('fixed') -> hipe_sparc_registers:temp2(). + +fix_src3(Src, TempMap, Strategy) -> + fix_src(Src, TempMap, temp3(Strategy)). + +temp3('new') -> []; +temp3('fixed') -> hipe_sparc_registers:temp3(). + +fix_src(Src, TempMap, RegOpt) -> + case temp_is_spilled(Src, TempMap) of + true -> + NewSrc = clone(Src, RegOpt), + {[hipe_sparc:mk_pseudo_move(Src, NewSrc)], NewSrc, true}; + _ -> + {[], Src, false} + end. + +fix_dst(Dst, TempMap, Strategy) -> + case temp_is_spilled(Dst, TempMap) of + true -> + NewDst = clone(Dst, temp1(Strategy)), + {[hipe_sparc:mk_pseudo_move(NewDst, Dst)], NewDst, true}; + _ -> + {[], Dst, false} + end. + +%%% Check if an operand is a pseudo-temp. + +temp_is_spilled(Temp, []) -> % special case for naive regalloc + not(hipe_sparc:temp_is_precoloured(Temp)); +temp_is_spilled(Temp, TempMap) -> + case hipe_sparc:temp_is_allocatable(Temp) of + true -> + Reg = hipe_sparc:temp_reg(Temp), + tuple_size(TempMap) > Reg andalso hipe_temp_map:is_spilled(Reg, TempMap); + false -> true + end. + +%%% Make a certain reg into a clone of Temp. + +clone(Temp, RegOpt) -> + Type = hipe_sparc:temp_type(Temp), + case RegOpt of + [] -> hipe_sparc:mk_new_temp(Type); + Reg -> hipe_sparc:mk_temp(Reg, Type) + end. diff --git a/lib/hipe/sparc/hipe_sparc_ra_postconditions_fp.erl b/lib/hipe/sparc/hipe_sparc_ra_postconditions_fp.erl new file mode 100644 index 0000000000..17dc0f88d5 --- /dev/null +++ b/lib/hipe/sparc/hipe_sparc_ra_postconditions_fp.erl @@ -0,0 +1,120 @@ +%% -*- erlang-indent-level: 2 -*- +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2008-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_sparc_ra_postconditions_fp). + +-export([check_and_rewrite/2]). + +-include("hipe_sparc.hrl"). + +check_and_rewrite(Defun, Coloring) -> + TempMap = hipe_temp_map:cols2tuple(Coloring, hipe_sparc_specific_fp), + #defun{code=Code0} = Defun, + {Code1,DidSpill} = do_insns(Code0, TempMap, [], false), + VarRange = {0, hipe_gensym:get_var(sparc)}, + {Defun#defun{code=Code1, var_range=VarRange}, + DidSpill}. + +do_insns([I|Insns], TempMap, Accum, DidSpill0) -> + {NewIs, DidSpill1} = do_insn(I, TempMap), + do_insns(Insns, TempMap, lists:reverse(NewIs, Accum), DidSpill0 or DidSpill1); +do_insns([], _TempMap, Accum, DidSpill) -> + {lists:reverse(Accum), DidSpill}. + +do_insn(I, TempMap) -> + case I of + #fp_binary{} -> do_fp_binary(I, TempMap); + #fp_unary{} -> do_fp_unary(I, TempMap); + #pseudo_fload{} -> do_pseudo_fload(I, TempMap); + #pseudo_fmove{} -> do_pseudo_fmove(I, TempMap); + #pseudo_fstore{} -> do_pseudo_fstore(I, TempMap); + _ -> {[I], false} + end. + +%%% Fix relevant instruction types. + +do_fp_binary(I=#fp_binary{src1=Src1,src2=Src2,dst=Dst}, TempMap) -> + {FixSrc1,NewSrc1,DidSpill1} = fix_src(Src1, TempMap), + {FixSrc2,NewSrc2,DidSpill2} = fix_src(Src2, TempMap), + {FixDst,NewDst,DidSpill3} = fix_dst(Dst, TempMap), + NewI = I#fp_binary{src1=NewSrc1,src2=NewSrc2,dst=NewDst}, + {FixSrc1 ++ FixSrc2 ++ [NewI | FixDst], DidSpill1 or DidSpill2 or DidSpill3}. + +do_fp_unary(I=#fp_unary{src=Src,dst=Dst}, TempMap) -> + {FixSrc,NewSrc,DidSpill1} = fix_src(Src, TempMap), + {FixDst,NewDst,DidSpill2} = fix_dst(Dst, TempMap), + NewI = I#fp_unary{src=NewSrc,dst=NewDst}, + {FixSrc ++ [NewI | FixDst], DidSpill1 or DidSpill2}. + +do_pseudo_fload(I=#pseudo_fload{dst=Dst}, TempMap) -> + {FixDst,NewDst,DidSpill} = fix_dst(Dst, TempMap), + NewI = I#pseudo_fload{dst=NewDst}, + {[NewI | FixDst], DidSpill}. + +do_pseudo_fmove(I=#pseudo_fmove{src=Src,dst=Dst}, TempMap) -> + case temp_is_spilled(Dst, TempMap) of + true -> + {FixSrc,NewSrc,DidSpill} = fix_src(Src, TempMap), + NewI = I#pseudo_fmove{src=NewSrc}, + {FixSrc ++ [NewI], DidSpill}; + _ -> + {[I], false} + end. + +do_pseudo_fstore(I=#pseudo_fstore{src=Src}, TempMap) -> + {FixSrc,NewSrc,DidSpill} = fix_src(Src, TempMap), + NewI = I#pseudo_fstore{src=NewSrc}, + {FixSrc ++ [NewI], DidSpill}. + +%%% Fix Dst and Src operands. + +fix_src(Src, TempMap) -> + case temp_is_spilled(Src, TempMap) of + true -> + NewSrc = clone(Src), + {[hipe_sparc:mk_pseudo_fmove(Src, NewSrc)], NewSrc, true}; + _ -> + {[], Src, false} + end. + +fix_dst(Dst, TempMap) -> + case temp_is_spilled(Dst, TempMap) of + true -> + NewDst = clone(Dst), + {[hipe_sparc:mk_pseudo_fmove(NewDst, Dst)], NewDst, true}; + _ -> + {[], Dst, false} + end. + +%%% Check if an operand is a pseudo-temp. + +temp_is_spilled(Temp, TempMap) -> + case hipe_sparc:temp_is_allocatable(Temp) of + true -> + Reg = hipe_sparc:temp_reg(Temp), + tuple_size(TempMap) > Reg andalso hipe_temp_map:is_spilled(Reg, TempMap); + false -> true + end. + +%%% Create a new temp with the same type as an old one. + +clone(Temp) -> + Type = hipe_sparc:temp_type(Temp), % XXX: always double? + hipe_sparc:mk_new_temp(Type). diff --git a/lib/hipe/sparc/hipe_sparc_registers.erl b/lib/hipe/sparc/hipe_sparc_registers.erl new file mode 100644 index 0000000000..adb01a65ca --- /dev/null +++ b/lib/hipe/sparc/hipe_sparc_registers.erl @@ -0,0 +1,291 @@ +%% -*- 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% +%% + +-module(hipe_sparc_registers). + +-export([reg_name_gpr/1, + reg_name_fpr/1, + first_virtual/0, + is_precoloured_gpr/1, + is_precoloured_fpr/1, + all_precoloured/0, % for coalescing ra + return_value/0, + temp1/0, + temp2/0, + temp3/0, + heap_pointer/0, + stack_pointer/0, + proc_pointer/0, + return_address/0, + g0/0, + %% heap_limit/0, + %% fcalls/0, + allocatable_gpr/0, % for coalescing ra + allocatable_fpr/0, + is_fixed/1, % for graph_coloring ra + nr_args/0, + arg/1, + args/1, + is_arg/1, % for linear_scan ra + call_clobbered/0, + tailcall_clobbered/0, + live_at_return/0 + ]). + +-include("../rtl/hipe_literals.hrl"). + +-define(G0, 0). +-define(G1, 1). +-define(G2, 2). +-define(G3, 3). +-define(G4, 4). +-define(G5, 5). +-define(G6, 6). +-define(G7, 7). +-define(O0, 8). +-define(O1, 9). +-define(O2, 10). +-define(O3, 11). +-define(O4, 12). +-define(O5, 13). +-define(O6, 14). +-define(O7, 15). +-define(L0, 16). +-define(L1, 17). +-define(L2, 18). +-define(L3, 19). +-define(L4, 20). +-define(L5, 21). +-define(L6, 22). +-define(L7, 23). +-define(I0, 24). +-define(I1, 25). +-define(I2, 26). +-define(I3, 27). +-define(I4, 28). +-define(I5, 29). +-define(I6, 30). +-define(I7, 31). +-define(LAST_PRECOLOURED,31). % must handle both GRP and FPR ranges + +-define(ARG0, ?O1). +-define(ARG1, ?O2). +-define(ARG2, ?O3). +-define(ARG3, ?O4). +-define(ARG4, ?O5). +-define(ARG5, ?O0). + +-define(TEMP1, ?I3). % stores RA around inc_stack calls, must be C calleE-save +-define(TEMP2, ?I4). +-define(TEMP3, ?I5). + +-define(RETURN_VALUE, ?O0). +-define(HEAP_POINTER, ?I2). +-define(STACK_POINTER, ?I1). +-define(PROC_POINTER, ?I0). + +reg_name_gpr(R) -> + case R of + ?G0 -> "%g0"; + ?G1 -> "%g1"; + ?G2 -> "%g2"; + ?G3 -> "%g3"; + ?G4 -> "%g4"; + ?G5 -> "%g5"; + ?G6 -> "%g6"; + ?G7 -> "%g7"; + ?O0 -> "%o0"; + ?O1 -> "%o1"; + ?O2 -> "%o2"; + ?O3 -> "%o3"; + ?O4 -> "%o4"; + ?O5 -> "%o5"; + ?O6 -> "%sp"; + ?O7 -> "%o7"; + ?L0 -> "%l0"; + ?L1 -> "%l1"; + ?L2 -> "%l2"; + ?L3 -> "%l3"; + ?L4 -> "%l4"; + ?L5 -> "%l5"; + ?L6 -> "%l6"; + ?L7 -> "%l7"; + ?I0 -> "%i0"; + ?I1 -> "%i1"; + ?I2 -> "%i2"; + ?I3 -> "%i3"; + ?I4 -> "%i4"; + ?I5 -> "%i5"; + ?I6 -> "%fp"; + ?I7 -> "%i7"; + %% to handle code before regalloc: + _ -> "%r" ++ integer_to_list(R) + end. + +reg_name_fpr(R) -> [$f | integer_to_list(2*R)]. + +%%% Must handle both GPR and FPR ranges. +first_virtual() -> ?LAST_PRECOLOURED + 1. + +%%% These two tests have the same implementation, but that's +%%% not something we should cast in stone in the interface. +is_precoloured_gpr(R) -> R =< ?LAST_PRECOLOURED. +is_precoloured_fpr(R) -> R =< ?LAST_PRECOLOURED. + +all_precoloured() -> + %% <%g6, %g7, %o6, %i6> should be skipped as they are unused. + %% Unfortunately, gaps in the list of precoloured registers + %% cause the graph_color register allocator to create bogus + %% assignments for those "registers", which in turn causes + %% the "precoloured reg must map to itself" sanity check in + %% the frame module to signal errors. + [?G0, ?G1, ?G2, ?G3, ?G4, ?G5, ?G6, ?G7, + ?O0, ?O1, ?O2, ?O3, ?O4, ?O5, ?O6, ?O7, + ?L0, ?L1, ?L2, ?L3, ?L4, ?L5, ?L6, ?L7, + ?I0, ?I1, ?I2, ?I3, ?I4, ?I5, ?I6, ?I7]. + +return_value() -> ?RETURN_VALUE. + +temp1() -> ?TEMP1. +temp2() -> ?TEMP2. +temp3() -> ?TEMP3. + +heap_pointer() -> ?HEAP_POINTER. + +stack_pointer() -> ?STACK_POINTER. + +proc_pointer() -> ?PROC_POINTER. + +return_address() -> ?O7. + +g0() -> ?G0. + +allocatable_gpr() -> + %% %g0 is not writable + %% %g6, %g7, %o6, and %i6 are reserved for C + %% %i0, %i1, and %i2 are fixed global registers + %% %i4 may be used by the frame module for large load/store offsets + [ ?G1, ?G2, ?G3, ?G4, ?G5, + ?O0, ?O1, ?O2, ?O3, ?O4, ?O5, ?O7, + ?L0, ?L1, ?L2, ?L3, ?L4, ?L5, ?L6, ?L7, + ?I3, ?I5, ?I7]. + +allocatable_fpr() -> + %% We expose 16 virtual fp regs, 0-15, corresponding to the + %% f0/f2/f4/.../f28/f30 double-precision hardware fp regs. + %% The mapping is done by reg_name_fpr/1 and the assembler. + %% We ignore f32/.../f60 since they cannot be used in loads + %% or stores for non 8-byte aligned addresses. + [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15]. + +%% Needed for hipe_graph_coloring_regalloc. +%% Presumably true for Reg in AllPrecoloured \ Allocatable. +is_fixed(Reg) -> + case Reg of + ?HEAP_POINTER -> true; + ?STACK_POINTER -> true; + ?PROC_POINTER -> true; + %% The following cases are required for linear scan: + %% it gets confused if it sees a register which is + %% neither allocatable nor global (fixed or one of + %% the scratch registers set aside for linear scan). + ?G0 -> true; + ?G6 -> true; + ?G7 -> true; + ?O6 -> true; + ?I6 -> true; + _ -> false + end. + +nr_args() -> ?SPARC_NR_ARG_REGS. + +args(Arity) when is_integer(Arity) -> + N = erlang:min(Arity, ?SPARC_NR_ARG_REGS), + args(N-1, []). + +args(I, Rest) when is_integer(I), I < 0 -> Rest; +args(I, Rest) -> args(I-1, [arg(I) | Rest]). + +arg(N) -> + if N < ?SPARC_NR_ARG_REGS -> + case N of + 0 -> ?ARG0; + 1 -> ?ARG1; + 2 -> ?ARG2; + 3 -> ?ARG3; + 4 -> ?ARG4; + 5 -> ?ARG5 + end + end. + +is_arg(R) -> + case R of + ?ARG0 -> ?SPARC_NR_ARG_REGS > 0; + ?ARG1 -> ?SPARC_NR_ARG_REGS > 1; + ?ARG2 -> ?SPARC_NR_ARG_REGS > 2; + ?ARG3 -> ?SPARC_NR_ARG_REGS > 3; + ?ARG4 -> ?SPARC_NR_ARG_REGS > 4; + ?ARG5 -> ?SPARC_NR_ARG_REGS > 5; + _ -> false + end. + +call_clobbered() -> % does the RA strip the type or not? + [%% ?G0 is the non-allocatable constant zero + {?G1,tagged},{?G1,untagged}, + {?G2,tagged},{?G2,untagged}, + {?G3,tagged},{?G3,untagged}, + {?G4,tagged},{?G4,untagged}, + {?G5,tagged},{?G5,untagged}, + %% ?G6 is reserved for C + %% ?G7 is reserved for C + {?O0,tagged},{?O0,untagged}, + {?O1,tagged},{?O1,untagged}, + {?O2,tagged},{?O2,untagged}, + {?O3,tagged},{?O3,untagged}, + {?O4,tagged},{?O4,untagged}, + {?O5,tagged},{?O5,untagged}, + %% ?O6 is reserved for C + {?O7,tagged},{?O7,untagged}, + {?L0,tagged},{?L0,untagged}, + {?L1,tagged},{?L1,untagged}, + {?L2,tagged},{?L2,untagged}, + {?L3,tagged},{?L3,untagged}, + {?L4,tagged},{?L4,untagged}, + {?L5,tagged},{?L5,untagged}, + {?L6,tagged},{?L6,untagged}, + {?L7,tagged},{?L7,untagged}, + %% ?I0 is fixed (P) + %% ?I1 is fixed (NSP) + %% ?I2 is fixed (HP) + {?I3,tagged},{?I3,untagged}, + {?I4,tagged},{?I4,untagged}, + {?I5,tagged},{?I5,untagged}, + %% ?I6 is reserved for C + {?I7,tagged},{?I7,untagged} + ]. + +tailcall_clobbered() -> % tailcall crapola needs one temp + [{?TEMP1,tagged},{?TEMP1,untagged}]. + +live_at_return() -> + [{?HEAP_POINTER,untagged}, + {?STACK_POINTER,untagged}, + {?PROC_POINTER,untagged} + ]. |