aboutsummaryrefslogtreecommitdiffstats
path: root/lib/hipe/ppc/hipe_ppc_assemble.erl
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/ppc/hipe_ppc_assemble.erl
downloadotp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz
otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2
otp-84adefa331c4159d432d22840663c38f155cd4c1.zip
The R13B03 release.OTP_R13B03
Diffstat (limited to 'lib/hipe/ppc/hipe_ppc_assemble.erl')
-rw-r--r--lib/hipe/ppc/hipe_ppc_assemble.erl603
1 files changed, 603 insertions, 0 deletions
diff --git a/lib/hipe/ppc/hipe_ppc_assemble.erl b/lib/hipe/ppc/hipe_ppc_assemble.erl
new file mode 100644
index 0000000000..6f06f8b841
--- /dev/null
+++ b/lib/hipe/ppc/hipe_ppc_assemble.erl
@@ -0,0 +1,603 @@
+%% -*- 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_ppc_assemble).
+-export([assemble/4]).
+
+-include("../main/hipe.hrl"). % for VERSION_STRING, when_option
+-include("hipe_ppc.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_ppc:defun_code(Defun),
+ hipe_ppc: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);
+ {bc_sdi,{_,{label,L},_},_} ->
+ SdiInfo = #sdi_info{incr=(8-4),lb=-16#2000*4,ub=16#1FFF*4},
+ hipe_sdi:pass1_add_sdi(SdiPass1, Address, L, SdiInfo);
+ _ ->
+ 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).
+
+insn_size(I) ->
+ case I of
+ {'.label',_,_} -> 0;
+ {'.reloc',_,_} -> 0;
+ _ -> 4 % bc_sdi included in this case
+ end.
+
+translate_insn(I, MFA, ConstMap) -> % -> [{Op,Opnd,OrigI}]
+ case I of
+ #alu{} -> do_alu(I);
+ #b_fun{} -> do_b_fun(I);
+ #b_label{} -> do_b_label(I);
+ #bc{} -> do_bc(I);
+ #bctr{} -> do_bctr(I);
+ #bctrl{} -> do_bctrl(I);
+ #bl{} -> do_bl(I);
+ #blr{} -> do_blr(I);
+ #comment{} -> [];
+ #cmp{} -> do_cmp(I);
+ #label{} -> do_label(I);
+ #load{} -> do_load(I);
+ #loadx{} -> do_loadx(I);
+ #mfspr{} -> do_mfspr(I);
+ #mtcr{} -> do_mtcr(I);
+ #mtspr{} -> do_mtspr(I);
+ %% pseudo_bc: eliminated before assembly
+ %% pseudo_call: eliminated before assembly
+ %% pseudo_call_prepare: eliminated before assembly
+ #pseudo_li{} -> do_pseudo_li(I, MFA, ConstMap);
+ %% pseudo_move: eliminated before assembly
+ %% pseudo_tailcall: eliminated before assembly
+ %% pseudo_tailcall_prepare: eliminated before assembly
+ #store{} -> do_store(I);
+ #storex{} -> do_storex(I);
+ #unary{} -> do_unary(I);
+ #lfd{} -> do_lfd(I);
+ #stfd{} -> do_stfd(I);
+ #fp_binary{} -> do_fp_binary(I);
+ #fp_unary{} -> do_fp_unary(I);
+ _ -> exit({?MODULE,translate_insn,I})
+ end.
+
+do_alu(I) ->
+ #alu{aluop=AluOp,dst=Dst,src1=Src1,src2=Src2} = I,
+ NewDst = do_reg(Dst),
+ NewSrc1 = do_reg(Src1),
+ NewSrc2 = do_reg_or_imm(Src2),
+ {NewI,NewOpnds} =
+ case AluOp of
+ 'slwi' -> {'rlwinm', do_slwi_opnds(NewDst, NewSrc1, NewSrc2)};
+ 'slwi.' -> {'rlwinm.', do_slwi_opnds(NewDst, NewSrc1, NewSrc2)};
+ 'srwi' -> {'rlwinm', do_srwi_opnds(NewDst, NewSrc1, NewSrc2)};
+ 'srwi.' -> {'rlwinm.', do_srwi_opnds(NewDst, NewSrc1, NewSrc2)};
+ 'srawi' -> {'srawi', {NewDst,NewSrc1,do_srawi_src2(NewSrc2)}};
+ 'srawi.' -> {'srawi.', {NewDst,NewSrc1,do_srawi_src2(NewSrc2)}};
+ _ -> {AluOp, {NewDst,NewSrc1,NewSrc2}}
+ end,
+ [{NewI, NewOpnds, I}].
+
+do_slwi_opnds(Dst, Src1, {uimm,N}) when is_integer(N), 0 =< N, N < 32 ->
+ {Dst, Src1, {sh,N}, {mb,0}, {me,31-N}}.
+
+do_srwi_opnds(Dst, Src1, {uimm,N}) when is_integer(N), 0 =< N, N < 32 ->
+ {Dst, Src1, {sh,32-N}, {mb,N}, {me,31}}.
+
+do_srawi_src2({uimm,N}) when is_integer(N), 0 =< N, N < 32 -> {sh,N}.
+
+do_b_fun(I) ->
+ #b_fun{'fun'=Fun,linkage=Linkage} = I,
+ [{'.reloc', {b_fun,Fun,Linkage}, #comment{term='fun'}},
+ {b, {{li,0}}, I}].
+
+do_b_label(I) ->
+ #b_label{label=Label} = I,
+ [{b, do_label_ref(Label), I}].
+
+do_bc(I) ->
+ #bc{bcond=BCond,label=Label,pred=Pred} = I,
+ [{bc_sdi, {{bcond,BCond},do_label_ref(Label),{pred,Pred}}, I}].
+
+do_bctr(I) ->
+ [{bcctr, {{bo,2#10100},{bi,0}}, I}].
+
+do_bctrl(I) ->
+ #bctrl{sdesc=SDesc} = I,
+ [{bcctrl, {{bo,2#10100},{bi,0}}, I},
+ {'.reloc', {sdesc,SDesc}, #comment{term=sdesc}}].
+
+do_bl(I) ->
+ #bl{'fun'=Fun,sdesc=SDesc,linkage=Linkage} = I,
+ [{'.reloc', {b_fun,Fun,Linkage}, #comment{term='fun'}},
+ {bl, {{li,0}}, I},
+ {'.reloc', {sdesc,SDesc}, #comment{term=sdesc}}].
+
+do_blr(I) ->
+ [{bclr, {{bo,2#10100},{bi,0}}, I}].
+
+do_cmp(I) ->
+ #cmp{cmpop=CmpOp,src1=Src1,src2=Src2} = I,
+ NewSrc1 = do_reg(Src1),
+ NewSrc2 = do_reg_or_imm(Src2),
+ [{CmpOp, {{crf,0},0,NewSrc1,NewSrc2}, I}].
+
+do_label(I) ->
+ #label{label=Label} = I,
+ [{'.label', Label, I}].
+
+do_load(I) ->
+ #load{ldop=LdOp,dst=Dst,disp=Disp,base=Base} = I,
+ NewDst = do_reg(Dst),
+ NewDisp = do_disp(Disp),
+ NewBase = do_reg(Base),
+ [{LdOp, {NewDst,NewDisp,NewBase}, I}].
+
+do_loadx(I) ->
+ #loadx{ldxop=LdxOp,dst=Dst,base1=Base1,base2=Base2} = I,
+ NewDst = do_reg(Dst),
+ NewBase1 = do_reg(Base1),
+ NewBase2 = do_reg(Base2),
+ [{LdxOp, {NewDst,NewBase1,NewBase2}, I}].
+
+do_mfspr(I) ->
+ #mfspr{dst=Dst,spr=SPR} = I,
+ NewDst = do_reg(Dst),
+ NewSPR = do_spr(SPR),
+ [{mfspr, {NewDst,NewSPR}, I}].
+
+do_mtcr(I) ->
+ #mtcr{src=Src} = I,
+ NewSrc = do_reg(Src),
+ [{mtcrf, {{crm,16#80},NewSrc}, I}].
+
+do_mtspr(I) ->
+ #mtspr{spr=SPR,src=Src} = I,
+ NewSPR = do_spr(SPR),
+ NewSrc = do_reg(Src),
+ [{mtspr, {NewSPR,NewSrc}, I}].
+
+do_pseudo_li(I, MFA, ConstMap) ->
+ #pseudo_li{dst=Dst,imm=Imm} = 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),
+ Simm0 = {simm,0},
+ [{'.reloc', RelocData, #comment{term=reloc}},
+ {addi, {NewDst,{r,0},Simm0}, I},
+ {addis, {NewDst,NewDst,Simm0}, I}].
+
+do_store(I) ->
+ #store{stop=StOp,src=Src,disp=Disp,base=Base} = I,
+ NewSrc = do_reg(Src),
+ NewDisp = do_disp(Disp),
+ NewBase = do_reg(Base),
+ [{StOp, {NewSrc,NewDisp,NewBase}, I}].
+
+do_storex(I) ->
+ #storex{stxop=StxOp,src=Src,base1=Base1,base2=Base2} = I,
+ NewSrc = do_reg(Src),
+ NewBase1 = do_reg(Base1),
+ NewBase2 = do_reg(Base2),
+ [{StxOp, {NewSrc,NewBase1,NewBase2}, I}].
+
+do_unary(I) ->
+ #unary{unop=UnOp,dst=Dst,src=Src} = I,
+ NewDst = do_reg(Dst),
+ NewSrc = do_reg(Src),
+ {NewI,NewOpnds} =
+ case UnOp of
+ {RLWINM,SH,MB,ME} -> {RLWINM, {NewDst,NewSrc,{sh,SH},{mb,MB},{me,ME}}};
+ _ -> {UnOp, {NewDst,NewSrc}}
+ end,
+ [{NewI, NewOpnds, I}].
+
+do_lfd(I) ->
+ #lfd{dst=Dst,disp=Disp,base=Base} = I,
+ NewDst = do_fpreg(Dst),
+ NewDisp = do_disp(Disp),
+ NewBase = do_reg(Base),
+ [{lfd, {NewDst,NewDisp,NewBase}, I}].
+
+do_stfd(I) ->
+ #stfd{src=Src,disp=Disp,base=Base} = I,
+ NewSrc = do_fpreg(Src),
+ NewDisp = do_disp(Disp),
+ NewBase = do_reg(Base),
+ [{stfd, {NewSrc,NewDisp,NewBase}, I}].
+
+do_fp_binary(I) ->
+ #fp_binary{fp_binop=FpBinOp,dst=Dst,src1=Src1,src2=Src2} = I,
+ NewDst = do_fpreg(Dst),
+ NewSrc1 = do_fpreg(Src1),
+ NewSrc2 = do_fpreg(Src2),
+ [{FpBinOp, {NewDst,NewSrc1,NewSrc2}, I}].
+
+do_fp_unary(I) ->
+ #fp_unary{fp_unop=FpUnOp,dst=Dst,src=Src} = I,
+ NewDst = do_fpreg(Dst),
+ NewSrc = do_fpreg(Src),
+ [{FpUnOp, {NewDst,NewSrc}, I}].
+
+do_fpreg(#ppc_temp{reg=Reg,type='double'}) when is_integer(Reg), 0 =< Reg, Reg < 32 ->
+ {fr,Reg}.
+
+do_reg(#ppc_temp{reg=Reg,type=Type})
+ when is_integer(Reg), 0 =< Reg, Reg < 32, Type =/= 'double' ->
+ {r,Reg}.
+
+do_label_ref(Label) when is_integer(Label) ->
+ {label,Label}. % symbolic, since offset is not yet computable
+
+do_reg_or_imm(Src) ->
+ case Src of
+ #ppc_temp{} ->
+ do_reg(Src);
+ #ppc_simm16{value=Value} when is_integer(Value), -32768 =< Value, Value =< 32767 ->
+ {simm, Value band 16#ffff};
+ #ppc_uimm16{value=Value} when is_integer(Value), 0 =< Value, Value =< 65535 ->
+ {uimm, Value}
+ end.
+
+do_disp(Disp) when is_integer(Disp), -32768 =< Disp, Disp =< 32767 ->
+ {d, Disp band 16#ffff}.
+
+do_spr(SPR) ->
+ SPR_NR =
+ case SPR of
+ 'xer' -> 1;
+ 'lr' -> 8;
+ 'ctr' -> 9
+ end,
+ {spr,SPR_NR}.
+
+%%%
+%%% 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);
+ {bc_sdi,_,_} ->
+ encode_insns(fix_bc_sdi(I, Insns, Address, FunAddress, LabelMap),
+ Address, FunAddress, LabelMap, Relocs, AccCode, Options);
+ _ ->
+ {Op,Arg,_} = fix_jumps(I, Address, FunAddress, LabelMap),
+ Word = hipe_ppc_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
+ {b_fun,MFAorPrim,Linkage} ->
+ %% b and bl 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} ->
+ #ppc_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(#ppc_mfa{m=M,f=F,a=A}) -> {M,F,A};
+untag_mfa_or_prim(#ppc_prim{prim=Prim}) -> Prim.
+
+fix_bc_sdi(I, Insns, InsnAddress, FunAddress, LabelMap) ->
+ {bc_sdi,Opnds,OrigI} = I,
+ {{bcond,BCond},Label,{pred,Pred}} = Opnds,
+ {label,L} = Label,
+ LabelAddress = gb_trees:get(L, LabelMap) + FunAddress,
+ BD = (LabelAddress - InsnAddress) div 4,
+ if BD >= -(16#2000), BD =< 16#1FFF ->
+ [{bc, Opnds, OrigI} | Insns];
+ true ->
+ NewBCond = hipe_ppc:negate_bcond(BCond),
+ NewPred = 1.0 - Pred,
+ [{bc,
+ {{bcond,NewBCond},'.+8',{pred,NewPred}},
+ #bc{bcond=NewBCond,label='.+8',pred=NewPred}}, %% pp will be ugly
+ {b, Label, #b_label{label=L}} |
+ Insns]
+ end.
+
+fix_jumps(I, InsnAddress, FunAddress, LabelMap) ->
+ case I of
+ {b, {label,L}, OrigI} ->
+ LabelAddress = gb_trees:get(L, LabelMap) + FunAddress,
+ LI = (LabelAddress - InsnAddress) div 4,
+ %% ensure LI fits in a 24 bit sign-extended field
+ ?ASSERT(LI =< 16#7FFFFF),
+ ?ASSERT(LI >= -(16#800000)),
+ {b, {{li,LI band 16#FFFFFF}}, OrigI};
+ {bc, {{bcond,BCond},Target,{pred,Pred}}, OrigI} ->
+ LabelAddress =
+ case Target of
+ {label,L} -> gb_trees:get(L, LabelMap) + FunAddress;
+ '.+8' -> InsnAddress + 8
+ end,
+ BD = (LabelAddress - InsnAddress) div 4,
+ %% ensure BD fits in a 14 bit sign-extended field
+ ?ASSERT(BD =< 16#1FFF),
+ ?ASSERT(BD >= -(16#2000)),
+ {BO1,BI} = split_bcond(BCond),
+ BO = mk_bo(BO1, Pred, BD),
+ {bc, {{bo,BO},{bi,BI},{bd,BD band 16#3FFF}}, OrigI};
+ _ -> I
+ end.
+
+split_bcond(BCond) -> % {BO[1], BI for CR0}
+ case BCond of
+ 'lt' -> {1, 2#0000};
+ 'ge' -> {0, 2#0000}; % not lt
+ 'gt' -> {1, 2#0001};
+ 'le' -> {0, 2#0001}; % not gt
+ 'eq' -> {1, 2#0010};
+ 'ne' -> {0, 2#0010}; % not eq
+ 'so' -> {1, 2#0011};
+ 'ns' -> {0, 2#0011} % not so
+ end.
+
+mk_bo(BO1, Pred, BD) ->
+ (BO1 bsl 3) bor 2#00100 bor mk_y(Pred, BD).
+
+mk_y(Pred, BD) ->
+ if Pred < 0.5 -> % not taken
+ if BD < 0 -> 1; true -> 0 end;
+ true -> % taken
+ if BD < 0 -> 0; true -> 1 end
+ 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_ppc_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}).