aboutsummaryrefslogtreecommitdiffstats
path: root/lib/hipe/sparc
diff options
context:
space:
mode:
authorErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
committerErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
commit84adefa331c4159d432d22840663c38f155cd4c1 (patch)
treebff9a9c66adda4df2106dfd0e5c053ab182a12bd /lib/hipe/sparc
downloadotp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz
otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2
otp-84adefa331c4159d432d22840663c38f155cd4c1.zip
The R13B03 release.OTP_R13B03
Diffstat (limited to 'lib/hipe/sparc')
-rw-r--r--lib/hipe/sparc/Makefile120
-rw-r--r--lib/hipe/sparc/hipe_rtl_to_sparc.erl972
-rw-r--r--lib/hipe/sparc/hipe_sparc.erl407
-rw-r--r--lib/hipe/sparc/hipe_sparc.hrl116
-rw-r--r--lib/hipe/sparc/hipe_sparc_assemble.erl588
-rw-r--r--lib/hipe/sparc/hipe_sparc_cfg.erl134
-rw-r--r--lib/hipe/sparc/hipe_sparc_defuse.erl143
-rw-r--r--lib/hipe/sparc/hipe_sparc_encode.erl476
-rw-r--r--lib/hipe/sparc/hipe_sparc_finalise.erl138
-rw-r--r--lib/hipe/sparc/hipe_sparc_frame.erl636
-rw-r--r--lib/hipe/sparc/hipe_sparc_liveness_all.erl38
-rw-r--r--lib/hipe/sparc/hipe_sparc_liveness_fpr.erl34
-rw-r--r--lib/hipe/sparc/hipe_sparc_liveness_gpr.erl38
-rw-r--r--lib/hipe/sparc/hipe_sparc_main.erl58
-rw-r--r--lib/hipe/sparc/hipe_sparc_pp.erl342
-rw-r--r--lib/hipe/sparc/hipe_sparc_ra.erl56
-rw-r--r--lib/hipe/sparc/hipe_sparc_ra_finalise.erl254
-rw-r--r--lib/hipe/sparc/hipe_sparc_ra_ls.erl56
-rw-r--r--lib/hipe/sparc/hipe_sparc_ra_naive.erl29
-rw-r--r--lib/hipe/sparc/hipe_sparc_ra_postconditions.erl222
-rw-r--r--lib/hipe/sparc/hipe_sparc_ra_postconditions_fp.erl120
-rw-r--r--lib/hipe/sparc/hipe_sparc_registers.erl291
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}
+ ].