aboutsummaryrefslogtreecommitdiffstats
path: root/lib/hipe/arm/hipe_arm_assemble.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/hipe/arm/hipe_arm_assemble.erl')
-rw-r--r--lib/hipe/arm/hipe_arm_assemble.erl665
1 files changed, 665 insertions, 0 deletions
diff --git a/lib/hipe/arm/hipe_arm_assemble.erl b/lib/hipe/arm/hipe_arm_assemble.erl
new file mode 100644
index 0000000000..2af786994e
--- /dev/null
+++ b/lib/hipe/arm/hipe_arm_assemble.erl
@@ -0,0 +1,665 @@
+%% -*- 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_arm_assemble).
+-export([assemble/4]).
+
+-include("../main/hipe.hrl"). % for VERSION_STRING, when_option
+-include("hipe_arm.hrl").
+-include("../../kernel/src/hipe_ext_format.hrl").
+-include("../rtl/hipe_literals.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_arm:defun_code(Defun),
+ hipe_arm: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.
+%%% Manage placement of large immediates in the code segment. (ARM-specific)
+%%%
+%%% Assembly Pass 2.
+%%% Perform short/long form optimisation for jumps.
+%%% (Trivial on ARM.)
+%%%
+%%% 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),
+ translate_mfas(Code, ConstMap, [{MFA,NewInsns,CodeSize,LabelMap}|NewCode]);
+translate_mfas([], _ConstMap, NewCode) ->
+ lists:reverse(NewCode).
+
+translate_insns(Insns, MFA, ConstMap) ->
+ translate_insns(Insns, MFA, ConstMap, gb_trees:empty(), 0, [],
+ previous_empty(), pending_empty()).
+
+translate_insns([I|Is] = Insns, MFA, ConstMap, LabelMap, Address, NewInsns, PrevImms, PendImms) ->
+ IsNotFallthroughInsn = is_not_fallthrough_insn(I),
+ MustFlushPending = must_flush_pending(PendImms, Address),
+ {NewIs,Insns1,PendImms1,DoFlushPending} =
+ case {MustFlushPending,IsNotFallthroughInsn} of
+ {true,false} ->
+ %% To avoid having to create new symbolic labels, which is problematic
+ %% in the assembler, we emit a forward branch with an offset computed
+ %% from the size of the pending literals.
+ N = pending_size(PendImms), % N >= 1 since MustFlushPending is true
+ BranchOffset = N - 1, % in units of 32-bit words!
+ NewIs0 = [{b, {do_cond('al'),{imm24,BranchOffset}}, #comment{term='skip'}}],
+ %% io:format("~w: forced flush of pending literals in ~w at ~w\n", [?MODULE,MFA,Address]),
+ {NewIs0,Insns,PendImms,true};
+ {_,_} ->
+ {NewIs0,PendImms0} = translate_insn(I, MFA, ConstMap, Address, PrevImms, PendImms),
+ {NewIs0,Is,PendImms0,IsNotFallthroughInsn}
+ end,
+ add_insns(NewIs, Insns1, MFA, ConstMap, LabelMap, Address, NewInsns, PrevImms, PendImms1, DoFlushPending);
+translate_insns([], _MFA, _ConstMap, LabelMap, Address, NewInsns, PrevImms, PendImms) ->
+ {LabelMap1, Address1, NewInsns1, _PrevImms1} = % at end-of-function we ignore PrevImms1
+ flush_pending(PendImms, LabelMap, Address, NewInsns, PrevImms),
+ {lists:reverse(NewInsns1), Address1, LabelMap1}.
+
+add_insns([I|Is], Insns, MFA, ConstMap, LabelMap, Address, NewInsns, PrevImms, PendImms, DoFlushPending) ->
+ NewLabelMap =
+ case I of
+ {'.label',L,_} ->
+ gb_trees:insert(L, Address, LabelMap);
+ _ ->
+ LabelMap
+ end,
+ Address1 = Address + insn_size(I),
+ add_insns(Is, Insns, MFA, ConstMap, NewLabelMap, Address1, [I|NewInsns], PrevImms, PendImms, DoFlushPending);
+add_insns([], Insns, MFA, ConstMap, LabelMap, Address, NewInsns, PrevImms, PendImms, DoFlushPending) ->
+ {LabelMap1, Address1, NewInsns1, PrevImms1, PendImms1} =
+ case DoFlushPending of
+ true ->
+ {LabelMap0,Address0,NewInsns0,PrevImms0} =
+ flush_pending(PendImms, LabelMap, Address, NewInsns, PrevImms),
+ {LabelMap0,Address0,NewInsns0,PrevImms0,pending_empty()};
+ false ->
+ PrevImms0 = expire_previous(PrevImms, Address),
+ {LabelMap,Address,NewInsns,PrevImms0,PendImms}
+ end,
+ translate_insns(Insns, MFA, ConstMap, LabelMap1, Address1, NewInsns1, PrevImms1, PendImms1).
+
+must_flush_pending(PendImms, Address) ->
+ case pending_firstref(PendImms) of
+ [] -> false;
+ LP0 ->
+ Distance = Address - LP0,
+ %% In "LP0: ldr R,[PC +/- imm12]", the PC value is LP0+8 so the
+ %% range for the ldr is [LP0-4084, LP0+4100] (32-bit alignment!).
+ %% LP0+4096 is the last point where we can emit a branch (4 bytes)
+ %% followed by the pending immediates.
+ %%
+ %% The translation of an individual instruction must not advance
+ %% . by more than 4 bytes, because that could cause us to miss
+ %% the point where PendImms must be flushed.
+ ?ASSERT(Distance =< 4096),
+ Distance =:= 4096
+ end.
+
+flush_pending(PendImms, LabelMap, Address, Insns, PrevImms) ->
+ Address1 = Address + 4*pending_size(PendImms),
+ PrevImms1 = expire_previous(PrevImms, Address1),
+ {LabelMap1,Address1,Insns1,PrevImms2} =
+ flush_pending2(pending_to_list(PendImms), LabelMap, Address, Insns, PrevImms1),
+ PrevImms3 = expire_previous(PrevImms2, Address1),
+ {LabelMap1,Address1,Insns1,PrevImms3}.
+
+flush_pending2([{Lab,RelocOrInt,Imm}|Imms], LabelMap, Address, Insns, PrevImms) ->
+ PrevImms1 = previous_append(PrevImms, Address, Lab, Imm),
+ LabelMap1 = gb_trees:insert(Lab, Address, LabelMap),
+ {RelocOpt,LongVal} =
+ if is_integer(RelocOrInt) ->
+ {[],RelocOrInt};
+ true ->
+ {[RelocOrInt],0}
+ end,
+ Insns1 =
+ [{'.long', LongVal, #comment{term=Imm}} |
+ RelocOpt ++
+ [{'.label', Lab, #comment{term=Imm}} |
+ Insns]],
+ flush_pending2(Imms, LabelMap1, Address+4, Insns1, PrevImms1);
+flush_pending2([], LabelMap, Address, Insns, PrevImms) ->
+ {LabelMap, Address, Insns, PrevImms}.
+
+expire_previous(PrevImms, CodeAddress) ->
+ case previous_findmin(PrevImms) of
+ [] -> PrevImms;
+ {ImmAddress,_Imm} ->
+ if CodeAddress - ImmAddress > 4084 ->
+ expire_previous(previous_delmin(PrevImms), CodeAddress);
+ true ->
+ PrevImms
+ end
+ end.
+
+is_not_fallthrough_insn(I) ->
+ case I of
+ #b_fun{} -> true;
+ #b_label{'cond'='al'} -> true;
+ %% bl and blx are not included since they return to ".+4"
+ %% a load to PC was originally a pseudo_switch insn
+ #load{dst=#arm_temp{reg=15,type=Type}} when Type =/= 'double' -> true;
+ %% a move to PC was originally a pseudo_blr or pseudo_bx insn
+ #move{dst=#arm_temp{reg=15,type=Type}} when Type =/= 'double' -> true;
+ _ -> false
+ end.
+
+insn_size(I) ->
+ case I of
+ {'.label',_,_} -> 0;
+ {'.reloc',_,_} -> 0;
+ _ -> 4
+ end.
+
+translate_insn(I, MFA, ConstMap, Address, PrevImms, PendImms) ->
+ case I of
+ %% pseudo_li is the only insn using MFA, ConstMap, Address, PrevImms, or PendLits
+ #pseudo_li{} -> do_pseudo_li(I, MFA, ConstMap, Address, PrevImms, PendImms);
+ _ -> {translate_insn(I), PendImms}
+ end.
+
+translate_insn(I) -> % -> [{Op,Opnd,OrigI}]
+ case I of
+ #alu{} -> do_alu(I);
+ #b_fun{} -> do_b_fun(I);
+ #b_label{} -> do_b_label(I);
+ #bl{} -> do_bl(I);
+ #blx{} -> do_blx(I);
+ #cmp{} -> do_cmp(I);
+ #comment{} -> [];
+ #label{} -> do_label(I);
+ #load{} -> do_load(I);
+ #ldrsb{} -> do_ldrsb(I);
+ #move{} -> do_move(I);
+ %% pseudo_b: eliminated by finalise
+ %% pseudo_blr: eliminated by finalise
+ %% pseudo_call: eliminated by finalise
+ %% pseudo_call_prepare: eliminated by frame
+ %% pseudo_li: handled separately
+ %% pseudo_move: eliminated by frame
+ %% pseudo_switch: eliminated by finalise
+ %% pseudo_tailcall: eliminated by frame
+ %% pseudo_tailcall_prepare: eliminated by finalise
+ #smull{} -> do_smull(I);
+ #store{} -> do_store(I)
+ end.
+
+do_alu(I) ->
+ #alu{aluop=AluOp,s=S,dst=Dst,src=Src,am1=Am1} = I,
+ NewCond = do_cond('al'),
+ NewS = do_s(S),
+ NewDst = do_reg(Dst),
+ NewSrc = do_reg(Src),
+ NewAm1 = do_am1(Am1),
+ {NewI,NewOpnds} = {AluOp, {NewCond,NewS,NewDst,NewSrc,NewAm1}},
+ [{NewI, NewOpnds, I}].
+
+do_b_fun(I) ->
+ #b_fun{'fun'=Fun,linkage=Linkage} = I,
+ [{'.reloc', {b_fun,Fun,Linkage}, #comment{term='fun'}},
+ {b, {do_cond('al'),{imm24,0}}, I}].
+
+do_b_label(I) ->
+ #b_label{'cond'=Cond,label=Label} = I,
+ [{b, {do_cond(Cond),do_label_ref(Label)}, I}].
+
+do_bl(I) ->
+ #bl{'fun'=Fun,sdesc=SDesc,linkage=Linkage} = I,
+ [{'.reloc', {b_fun,Fun,Linkage}, #comment{term='fun'}},
+ {bl, {do_cond('al'),{imm24,0}}, I},
+ {'.reloc', {sdesc,SDesc}, #comment{term=sdesc}}].
+
+do_blx(I) ->
+ #blx{src=Src,sdesc=SDesc} = I,
+ [{blx, {do_cond('al'),do_reg(Src)}, I},
+ {'.reloc', {sdesc,SDesc}, #comment{term=sdesc}}].
+
+do_cmp(I) ->
+ #cmp{cmpop=CmpOp,src=Src,am1=Am1} = I,
+ NewCond = do_cond('al'),
+ NewSrc = do_reg(Src),
+ NewAm1 = do_am1(Am1),
+ [{CmpOp, {NewCond,NewSrc,NewAm1}, I}].
+
+do_label(I) ->
+ #label{label=Label} = I,
+ [{'.label', Label, I}].
+
+do_load(I) ->
+ #load{ldop=LdOp,dst=Dst,am2=Am2} = I,
+ NewCond = do_cond('al'),
+ NewDst = do_reg(Dst),
+ NewAm2 = do_am2(Am2),
+ [{LdOp, {NewCond,NewDst,NewAm2}, I}].
+
+do_ldrsb(I) ->
+ #ldrsb{dst=Dst,am3=Am3} = I,
+ NewCond = do_cond('al'),
+ NewDst = do_reg(Dst),
+ NewAm3 = do_am3(Am3),
+ [{'ldrsb', {NewCond,NewDst,NewAm3}, I}].
+
+do_move(I) ->
+ #move{movop=MovOp,s=S,dst=Dst,am1=Am1} = I,
+ NewCond = do_cond('al'),
+ NewS = do_s(S),
+ NewDst = do_reg(Dst),
+ NewAm1 = do_am1(Am1),
+ [{MovOp, {NewCond,NewS,NewDst,NewAm1}, I}].
+
+do_pseudo_li(I, MFA, ConstMap, Address, PrevImms, PendImms) ->
+ #pseudo_li{dst=Dst,imm=Imm,label=Label0} = I,
+ {Label1,PendImms1} =
+ case previous_lookup(PrevImms, Imm) of
+ {value,Lab} -> {Lab,PendImms};
+ none ->
+ case pending_lookup(PendImms, Imm) of
+ {value,Lab} -> {Lab,PendImms};
+ none ->
+ RelocOrInt =
+ if is_integer(Imm) ->
+ %% This is for immediates that require too much work
+ %% to reconstruct using only arithmetic instructions.
+ Imm;
+ true ->
+ RelocData =
+ case Imm of
+ Atom when is_atom(Atom) ->
+ {load_atom, Atom};
+ {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,
+ {'.reloc', RelocData, #comment{term=reloc}}
+ end,
+ Lab = Label0, % preallocated: creating labels in the assembler doesn't work
+ {Lab, pending_append(PendImms, Address, Lab, RelocOrInt, Imm)}
+ end
+ end,
+ NewDst = do_reg(Dst),
+ {[{'.pseudo_li', {NewDst,do_label_ref(Label1)}, I}], PendImms1}.
+
+do_smull(I) ->
+ #smull{dstlo=DstLo,dsthi=DstHi,src1=Src1,src2=Src2} = I,
+ NewCond = do_cond('al'),
+ NewS = do_s(false),
+ NewDstLo = do_reg(DstLo),
+ NewDstHi = do_reg(DstHi),
+ NewSrc1 = do_reg(Src1),
+ NewSrc2 = do_reg(Src2),
+ [{'smull', {NewCond,NewS,NewDstLo,NewDstHi,NewSrc1,NewSrc2}, I}].
+
+do_store(I) ->
+ #store{stop=StOp,src=Src,am2=Am2} = I,
+ NewCond = do_cond('al'),
+ NewSrc = do_reg(Src),
+ NewAm2 = do_am2(Am2),
+ [{StOp, {NewCond,NewSrc,NewAm2}, I}].
+
+do_reg(#arm_temp{reg=Reg,type=Type})
+ when is_integer(Reg), 0 =< Reg, Reg < 16, Type =/= 'double' ->
+ {r,Reg}.
+
+do_cond(Cond) -> {'cond',Cond}.
+
+do_s(S) -> {'s', case S of false -> 0; true -> 1 end}.
+
+do_label_ref(Label) when is_integer(Label) ->
+ {label,Label}. % symbolic, since offset is not yet computable
+
+do_am1(Am1) ->
+ case Am1 of
+ #arm_temp{} -> do_reg(Am1);
+ {Src1,'rrx'} -> {do_reg(Src1),'rrx'};
+ {Src1,ShiftOp,Src2=#arm_temp{}} -> {do_reg(Src1),{ShiftOp,do_reg(Src2)}};
+ {Src1,ShiftOp,Imm5} -> {do_reg(Src1),{ShiftOp,{imm5,Imm5}}};
+ {Imm8,Imm4} -> {{imm8,Imm8},{imm4,Imm4}}
+ end.
+
+do_am2(#am2{src=Src,sign=Sign,offset=Offset}) ->
+ NewSrc = do_reg(Src),
+ case Offset of
+ #arm_temp{} -> {'register_offset',NewSrc,Sign,do_reg(Offset)};
+ {Src3,'rrx'} -> {'scaled_register_offset',NewSrc,Sign,do_reg(Src3),'rrx'};
+ {Src3,ShiftOp,Imm5} -> {'scaled_register_offset',NewSrc,Sign,do_reg(Src3),{ShiftOp,{imm5,Imm5}}};
+ Imm12 -> {'immediate_offset',NewSrc,Sign,{imm12,Imm12}}
+ end.
+
+do_am3(#am3{src=Src,sign=Sign,offset=Offset}) ->
+ NewSrc = do_reg(Src),
+ case Offset of
+ #arm_temp{} -> {'register_offset',NewSrc,Sign,do_reg(Offset)};
+ _ -> {'immediate_offset',NewSrc,Sign,{'imm8',Offset}}
+ 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,_} ->
+ print_insn(Address, [], I, Options),
+ Reloc = encode_reloc(Data, Address, FunAddress, LabelMap),
+ encode_insns(Insns, Address, FunAddress, LabelMap, [Reloc|Relocs], AccCode, Options);
+ {'.long',Value,_} ->
+ print_insn(Address, Value, I, Options),
+ Segment = <<Value:32/integer-native>>,
+ NewAccCode = [Segment|AccCode],
+ encode_insns(Insns, Address+4, FunAddress, LabelMap, Relocs, NewAccCode, Options);
+ _ ->
+ {Op,Arg,_} = fix_pc_refs(I, Address, FunAddress, LabelMap),
+ Word = hipe_arm_encode:insn_encode(Op, Arg),
+ print_insn(Address, Word, I, Options),
+ Segment = <<Word:32/integer-native>>,
+ 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} ->
+ #arm_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(#arm_mfa{m=M,f=F,a=A}) -> {M,F,A};
+untag_mfa_or_prim(#arm_prim{prim=Prim}) -> Prim.
+
+fix_pc_refs(I, InsnAddress, FunAddress, LabelMap) ->
+ case I of
+ {b, {Cond,{label,L}}, OrigI} ->
+ LabelAddress = gb_trees:get(L, LabelMap) + FunAddress,
+ Imm24 = (LabelAddress - (InsnAddress+8)) div 4,
+ %% ensure Imm24 fits in a 24 bit sign-extended field
+ ?ASSERT(Imm24 =< 16#7FFFFF),
+ ?ASSERT(Imm24 >= -(16#800000)),
+ {b, {Cond,{imm24,Imm24 band 16#FFFFFF}}, OrigI};
+ {'.pseudo_li', {Dst,{label,L}}, OrigI} ->
+ LabelAddress = gb_trees:get(L, LabelMap) + FunAddress,
+ Offset = LabelAddress - (InsnAddress+8),
+ {Sign,Imm12} =
+ if Offset < 0 -> {'-', -Offset};
+ true -> {'+', Offset}
+ end,
+ ?ASSERT(Imm12 =< 16#FFF),
+ Am2 = {'immediate_offset',{r,15},Sign,{imm12,Imm12}},
+ {ldr, {do_cond('al'),Dst,Am2}, OrigI};
+ _ -> I
+ 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, {NewI,NewArgs,OrigI}) ->
+ io:format("~8.16.0b | ", [Address]),
+ print_code_list(word_to_bytes(Word), 0),
+ case NewI of
+ '.long' ->
+ io:format("\t.long ~.16x\n", [Word, "0x"]);
+ '.reloc' ->
+ io:format("\t.reloc ~w\n", [NewArgs]);
+ _ ->
+ hipe_arm_pp:pp_insn(OrigI)
+ end.
+
+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}).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%%%
+%%% ADT for previous immediates.
+%%% This is a queue (fifo) of the previously defined immediates,
+%%% plus a mapping from these immediates to their labels.
+%%%
+-record(previous, {set, head, tail}). % INV: tail=[] if head=[]
+
+previous_empty() -> #previous{set=gb_trees:empty(), head=[], tail=[]}.
+
+previous_lookup(#previous{set=S}, Imm) -> gb_trees:lookup(Imm, S).
+
+previous_findmin(#previous{head=H}) ->
+ case H of
+ [X|_] -> X;
+ _ -> []
+ end.
+
+previous_delmin(#previous{set=S, head=[{_Address,Imm}|H], tail=T}) ->
+ {NewH,NewT} =
+ case H of
+ [] -> {lists:reverse(T), []};
+ _ -> {H, T}
+ end,
+ #previous{set=gb_trees:delete(Imm, S), head=NewH, tail=NewT}.
+
+previous_append(#previous{set=S, head=H, tail=T}, Address, Lab, Imm) ->
+ {NewH,NewT} =
+ case H of
+ [] -> {[{Address,Imm}], []};
+ _ -> {H, [{Address,Imm}|T]}
+ end,
+ #previous{set=gb_trees:insert(Imm, Lab, S), head=NewH, tail=NewT}.
+
+%%%
+%%% ADT for pending immediates.
+%%% This is a queue (fifo) of immediates pending definition,
+%%% plus a mapping from these immediates to their labels,
+%%% and a recording of the first (lowest) code address referring
+%%% to a pending immediate.
+%%%
+-record(pending, {set, list, firstref}).
+
+pending_empty() -> #pending{set=gb_trees:empty(), list=[], firstref=[]}.
+
+pending_to_list(#pending{list=L}) -> lists:reverse(L).
+
+pending_lookup(#pending{set=S}, Imm) -> gb_trees:lookup(Imm, S).
+
+pending_firstref(#pending{firstref=F}) -> F.
+
+pending_append(#pending{set=S, list=L, firstref=F}, Address, Lab, RelocOrInt, Imm) ->
+ #pending{set=gb_trees:insert(Imm, Lab, S),
+ list=[{Lab,RelocOrInt,Imm}|L],
+ firstref=case F of [] -> Address; _ -> F end}.
+
+pending_size(#pending{list=L}) -> length(L).