1%% -*- erlang-indent-level: 2 -*-
2%%
3%% Licensed under the Apache License, Version 2.0 (the "License");
4%% you may not use this file except in compliance with the License.
5%% You may obtain a copy of the License at
6%%
7%%     http://www.apache.org/licenses/LICENSE-2.0
8%%
9%% Unless required by applicable law or agreed to in writing, software
10%% distributed under the License is distributed on an "AS IS" BASIS,
11%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12%% See the License for the specific language governing permissions and
13%% limitations under the License.
14
15-module(hipe_sparc_assemble).
16-export([assemble/4]).
17
18-include("../main/hipe.hrl").	% for VERSION_STRING, when_option
19-include("hipe_sparc.hrl").
20-include("../../kernel/src/hipe_ext_format.hrl").
21-include("../rtl/hipe_literals.hrl").
22-include("../misc/hipe_sdi.hrl").
23-undef(ASSERT).
24-define(ASSERT(G), if G -> [] ; true -> exit({assertion_failed,?MODULE,?LINE,??G}) end).
25
26assemble(CompiledCode, Closures, Exports, Options) ->
27  print("****************** Assembling *******************\n", [], Options),
28  %%
29  Code = [{MFA,
30	   hipe_sparc:defun_code(Defun),
31	   hipe_sparc:defun_data(Defun)}
32	  || {MFA, Defun} <- CompiledCode],
33  %%
34  {ConstAlign,ConstSize,ConstMap,RefsFromConsts} =
35    hipe_pack_constants:pack_constants(Code),
36  %%
37  {CodeSize,CodeBinary,AccRefs,LabelMap,ExportMap} =
38    encode(translate(Code, ConstMap), Options),
39  print("Total num bytes=~w\n", [CodeSize], Options),
40  %%
41  SC = hipe_pack_constants:slim_constmap(ConstMap),
42  DataRelocs = hipe_pack_constants:mk_data_relocs(RefsFromConsts, LabelMap),
43  SSE = hipe_pack_constants:slim_sorted_exportmap(ExportMap,Closures,Exports),
44  SlimRefs = hipe_pack_constants:slim_refs(AccRefs),
45  Bin = term_to_binary([{?VERSION_STRING(),?HIPE_ERTS_CHECKSUM},
46			ConstAlign, ConstSize,
47			SC,
48			DataRelocs, % nee LM, LabelMap
49			SSE,
50			CodeSize,CodeBinary,SlimRefs,
51			0,[] % ColdCodeSize, SlimColdRefs
52		       ]),
53  %%
54  Bin.
55
56%%%
57%%% Assembly Pass 1.
58%%% Process initial {MFA,Code,Data} list.
59%%% Translate each MFA's body, choosing operand & instruction kinds.
60%%%
61%%% Assembly Pass 2.
62%%% Perform short/long form optimisation for jumps.
63%%%
64%%% Result is {MFA,NewCode,CodeSize,LabelMap} list.
65%%%
66
67translate(Code, ConstMap) ->
68  translate_mfas(Code, ConstMap, []).
69
70translate_mfas([{MFA,Insns,_Data}|Code], ConstMap, NewCode) ->
71  {NewInsns,CodeSize,LabelMap} =
72    translate_insns(Insns, MFA, ConstMap, hipe_sdi:pass1_init(), 0, []),
73  translate_mfas(Code, ConstMap, [{MFA,NewInsns,CodeSize,LabelMap}|NewCode]);
74translate_mfas([], _ConstMap, NewCode) ->
75  lists:reverse(NewCode).
76
77translate_insns([I|Insns], MFA, ConstMap, SdiPass1, Address, NewInsns) ->
78  NewIs = translate_insn(I, MFA, ConstMap),
79  add_insns(NewIs, Insns, MFA, ConstMap, SdiPass1, Address, NewInsns);
80translate_insns([], _MFA, _ConstMap, SdiPass1, Address, NewInsns) ->
81  {LabelMap,CodeSizeIncr} = hipe_sdi:pass2(SdiPass1),
82  {lists:reverse(NewInsns), Address+CodeSizeIncr, LabelMap}.
83
84add_insns([I|Is], Insns, MFA, ConstMap, SdiPass1, Address, NewInsns) ->
85  NewSdiPass1 =
86    case I of
87      {'.label',L,_} ->
88	hipe_sdi:pass1_add_label(SdiPass1, Address, L);
89      {bp_sdi,{_,_,{label,L}},_} ->	% BP has 19-bit offset
90	SdiInfo = #sdi_info{incr=(12-4),lb=-16#40000*4,ub=16#3FFFF*4},
91	hipe_sdi:pass1_add_sdi(SdiPass1, Address, L, SdiInfo);
92      %% {br_sdi,_,_} -> add_insns_br(I, SdiPass1, Address);
93      _ ->
94	SdiPass1
95    end,
96  Address1 = Address + insn_size(I),
97  add_insns(Is, Insns, MFA, ConstMap, NewSdiPass1, Address1, [I|NewInsns]);
98add_insns([], Insns, MFA, ConstMap, SdiPass1, Address, NewInsns) ->
99  translate_insns(Insns, MFA, ConstMap, SdiPass1, Address, NewInsns).
100
101-ifdef(notdef).	% XXX: only for sparc64, alas
102add_insns_br(I, SdiPass1, Address) ->	% BR has 16-bit offset
103  {br_sdi,{_,_,_,{label,L}},_} = I,
104  SdiInfo = #sdi_info{incr=(12-4),lb=-16#8000*4,ub=16#7FFF*4},
105  hipe_sdi:pass1_add_sdi(SdiPass1, Address, L, SdiInfo).
106-endif.
107
108insn_size(I) ->
109  case I of
110    {'.label',_,_} -> 0;
111    {'.reloc',_,_} -> 0;
112    _ -> 4	% b{p,r}_sdi included in this case
113  end.
114
115translate_insn(I, MFA, ConstMap) ->	% -> [{Op,Opnd,OrigI}]
116  case I of
117    #alu{} -> do_alu(I);
118    #bp{} -> do_bp(I);
119    %% #br{} -> do_br(I);
120    #call_rec{} -> do_call_rec(I);
121    #call_tail{} -> do_call_tail(I);
122    #comment{} -> [];
123    #jmp{} -> do_jmp(I);
124    #jmpl{} -> do_jmpl(I);
125    #label{} -> do_label(I);
126    %% pseudo_bp: eliminated before assembly
127    %% pseudo_br: eliminated before assembly
128    %% pseudo_call: eliminated before assembly
129    %% pseudo_call_prepare: eliminated before assembly
130    %% pseudo_move: eliminated before assembly
131    %% pseudo_ret: eliminated before assembly
132    #pseudo_set{} -> do_pseudo_set(I, MFA, ConstMap);
133    %% pseudo_tailcall: eliminated before assembly
134    %% pseudo_tailcall_prepare: eliminated before assembly
135    #rdy{} -> do_rdy(I);
136    #sethi{} -> do_sethi(I);
137    #store{} -> do_store(I);
138    #fp_binary{} -> do_fp_binary(I);
139    #fp_unary{} -> do_fp_unary(I);
140    #pseudo_fload{} -> do_pseudo_fload(I);
141    %% #pseudo_fmove: eliminated before assembly
142    #pseudo_fstore{} -> do_pseudo_fstore(I);
143    _ -> exit({?MODULE,translate_insn,I})
144  end.
145
146do_alu(I) ->
147  #alu{aluop=AluOp,src1=Src1,src2=Src2,dst=Dst} = I,
148  NewDst = do_reg(Dst),
149  NewSrc1 = do_reg(Src1),
150  NewSrc2 = do_reg_or_imm(Src2),
151  [{AluOp, {NewSrc1,NewSrc2,NewDst}, I}].
152
153do_bp(I) ->
154  #bp{'cond'=Cond,pred=Pred,label=Label} = I,
155  NewLabel = {label,Label},
156  case Cond of
157    'a' ->
158      [{ba, NewLabel, I}];	% 3 more offset bits
159    _ ->
160      NewCond = {'cond',Cond},
161      NewPred = {pred,Pred},
162      [{bp_sdi, {NewCond,NewPred,NewLabel}, I}]
163  end.
164
165-ifdef(notdef).	% XXX: only for sparc64, alas
166do_br(I) ->
167  #br{rcond=RCond,pred=Pred,src=Src,label=Label} = I,
168  NewRCond = {rcond,RCond},
169  NewPred = {pred,Pred},
170  NewSrc = do_reg(Src),
171  NewLabel = {label,Label},
172  [{br_sdi, {NewRCond,NewPred,NewSrc,NewLabel}, I}].
173-endif.
174
175do_call_rec(I) ->
176  #call_rec{'fun'=Fun,sdesc=SDesc,linkage=Linkage} = I,
177  [{'.reloc', {call,Fun,Linkage}, #comment{term='fun'}},
178   {'.reloc', {sdesc,SDesc}, #comment{term=sdesc}},
179   {call, {disp30,0}, I}].
180
181do_call_tail(I) ->
182  #call_tail{'fun'=Fun,linkage=Linkage} = I,
183  [{'.reloc', {call,Fun,Linkage}, #comment{term='fun'}},
184   {call, {disp30,0}, I}].
185
186do_jmp(I) ->
187  #jmp{src1=Src1,src2=Src2} = I,
188  NewSrc1 = do_reg(Src1),
189  NewSrc2 = do_reg_or_imm(Src2),
190  NewDst = {r,0},
191  [{jmpl, {NewSrc1,NewSrc2,NewDst}, I}].
192
193do_jmpl(I) ->
194  #jmpl{src=Src,sdesc=SDesc} = I,
195  NewSrc1 = do_reg(Src),
196  NewSrc2 = {simm13,0},
197  NewDst = {r,15},	% %o7
198  [{'.reloc', {sdesc,SDesc}, #comment{term=sdesc}},
199   {jmpl, {NewSrc1,NewSrc2,NewDst}, I}].
200
201do_label(I) ->
202  #label{label=Label} = I,
203  [{'.label', Label, I}].
204
205do_pseudo_set(I, MFA, ConstMap) ->
206  #pseudo_set{imm=Imm,dst=Dst} = I,
207  RelocData =
208    case Imm of
209      Atom when is_atom(Atom) ->
210	{load_atom, Atom};
211%%%      {mfa,MFAorPrim,Linkage} ->
212%%%	Tag =
213%%%	  case Linkage of
214%%%	    remote -> remote_function;
215%%%	    not_remote -> local_function
216%%%	  end,
217%%%	{load_address, {Tag,untag_mfa_or_prim(MFAorPrim)}};
218      {Label,constant} ->
219	ConstNo = hipe_pack_constants:find_const({MFA,Label}, ConstMap),
220	{load_address, {constant,ConstNo}};
221      {Label,closure} ->
222	{load_address, {closure,Label}};
223      {Label,c_const} ->
224	{load_address, {c_const,Label}}
225    end,
226  NewDst = do_reg(Dst),
227  [{'.reloc', RelocData, #comment{term=reloc}},
228   {sethi, {{uimm22,0},NewDst}, I},
229   {'or', {NewDst,{simm13,0},NewDst}, I}].
230
231do_rdy(I) ->
232  #rdy{dst=Dst} = I,
233  NewDst = do_reg(Dst),
234  [{rd, {y,NewDst}, I}].
235
236do_sethi(I) ->
237  #sethi{uimm22=#sparc_uimm22{value=UImm22},dst=Dst} = I,
238  NewUImm22 = {uimm22,UImm22},
239  NewDst = do_reg(Dst),
240  [{sethi, {NewUImm22,NewDst}, I}].
241
242do_store(I) ->
243  #store{stop=StOp,src=Src,base=Base,disp=Disp} = I,
244  NewSrc = do_reg(Src),
245  NewBase = do_reg(Base),
246  NewDisp = do_reg_or_imm(Disp),
247  [{StOp, {NewSrc,NewBase,NewDisp}, I}].
248
249do_fp_binary(I) ->
250  #fp_binary{fp_binop=FpBinOp,src1=Src1,src2=Src2,dst=Dst} = I,
251  NewSrc1 = do_fpreg(Src1),
252  NewSrc2 = do_fpreg(Src2),
253  NewDst = do_fpreg(Dst),
254  [{FpBinOp, {NewSrc1,NewSrc2,NewDst}, I}].
255
256do_fp_unary(I) ->
257  #fp_unary{fp_unop=FpUnOp,src=Src,dst=Dst} = I,
258  NewSrc = do_fpreg(Src),
259  NewDst = do_fpreg(Dst),
260  [{FpUnOp, {NewSrc,NewDst}, I}].
261
262do_pseudo_fload(I) ->
263  #pseudo_fload{base=Base,disp=Disp,dst=Dst,is_single=IsSingle} = I,
264  NewBase = do_reg(Base),
265  #sparc_simm13{value=RawDisp} = Disp,
266  {fr,RawDst} = FrRawDst = do_fpreg(Dst),
267  case IsSingle of
268    true ->
269      [{'ldf', {NewBase,{simm13,RawDisp},FrRawDst}, I}];
270    _ ->
271      [{'ldf', {NewBase,{simm13,RawDisp},FrRawDst}, I},
272       {'ldf', {NewBase,{simm13,RawDisp+4},{fr,RawDst+1}}, I}]
273  end.
274
275do_pseudo_fstore(I) ->
276  #pseudo_fstore{src=Src,base=Base,disp=Disp} = I,
277  {fr,RawSrc} = FrRawSrc = do_fpreg(Src),
278  NewBase = do_reg(Base),
279  #sparc_simm13{value=RawDisp} = Disp,
280  [{'stf', {FrRawSrc,NewBase,{simm13,RawDisp}}, I},
281   {'stf', {{fr,RawSrc+1},NewBase,{simm13,RawDisp+4}}, I}].
282
283%% map a virtual double-precision fp reg in [0,15] to its
284%% corresponding single-precision fp reg in [0,2,4,...,28,30]
285do_fpreg(#sparc_temp{reg=Reg,type='double'})
286  when is_integer(Reg), 0 =< Reg, Reg < 16 ->
287  {fr,2*Reg}.
288
289do_reg(#sparc_temp{reg=Reg,type=Type})
290  when is_integer(Reg), 0 =< Reg, Reg < 32, Type =/= 'double' ->
291  {r,Reg}.
292
293do_reg_or_imm(Src) ->
294  case Src of
295    #sparc_temp{} ->
296      do_reg(Src);
297    #sparc_simm13{value=Value} when is_integer(Value), -4096 =< Value, Value =< 4095 ->
298      {simm13, Value band 16#1fff};
299    #sparc_uimm5{value=Value} when is_integer(Value), 0 =< Value, Value =< 31 ->
300      {uimm5, Value};
301    #sparc_uimm6{value=Value} when is_integer(Value), 0 =< Value, Value =< 63 ->
302      {uimm6, Value}
303  end.
304
305%%%
306%%% Assembly Pass 3.
307%%% Process final {MFA,Code,CodeSize,LabelMap} list from pass 2.
308%%% Translate to a single binary code segment.
309%%% Collect relocation patches.
310%%% Build ExportMap (MFA-to-address mapping).
311%%% Combine LabelMaps to a single one (for mk_data_relocs/2 compatibility).
312%%% Return {CombinedCodeSize,BinaryCode,Relocs,CombinedLabelMap,ExportMap}.
313%%%
314
315encode(Code, Options) ->
316  CodeSize = compute_code_size(Code, 0),
317  ExportMap = build_export_map(Code, 0, []),
318  {AccCode,Relocs} = encode_mfas(Code, 0, [], [], Options),
319  CodeBinary = list_to_binary(lists:reverse(AccCode)),
320  ?ASSERT(CodeSize =:= byte_size(CodeBinary)),
321  CombinedLabelMap = combine_label_maps(Code, 0, gb_trees:empty()),
322  {CodeSize,CodeBinary,Relocs,CombinedLabelMap,ExportMap}.
323
324compute_code_size([{_MFA,_Insns,CodeSize,_LabelMap}|Code], Size) ->
325  compute_code_size(Code, Size+CodeSize);
326compute_code_size([], Size) -> Size.
327
328build_export_map([{{M,F,A},_Insns,CodeSize,_LabelMap}|Code], Address, ExportMap) ->
329  build_export_map(Code, Address+CodeSize, [{Address,M,F,A}|ExportMap]);
330build_export_map([], _Address, ExportMap) -> ExportMap.
331
332combine_label_maps([{MFA,_Insns,CodeSize,LabelMap}|Code], Address, CLM) ->
333  NewCLM = merge_label_map(gb_trees:to_list(LabelMap), MFA, Address, CLM),
334  combine_label_maps(Code, Address+CodeSize, NewCLM);
335combine_label_maps([], _Address, CLM) -> CLM.
336
337merge_label_map([{Label,Offset}|Rest], MFA, Address, CLM) ->
338  NewCLM = gb_trees:insert({MFA,Label}, Address+Offset, CLM),
339  merge_label_map(Rest, MFA, Address, NewCLM);
340merge_label_map([], _MFA, _Address, CLM) -> CLM.
341
342encode_mfas([{MFA,Insns,CodeSize,LabelMap}|Code], Address, AccCode, Relocs, Options) ->
343  print("Generating code for: ~w\n", [MFA], Options),
344  print("Offset   | Opcode   | Instruction\n", [], Options),
345  {Address1,Relocs1,AccCode1} =
346    encode_insns(Insns, Address, Address, LabelMap, Relocs, AccCode, Options),
347  ExpectedAddress = Address + CodeSize,
348  ?ASSERT(Address1 =:= ExpectedAddress),
349  print("Finished.\n", [], Options),
350  encode_mfas(Code, Address1, AccCode1, Relocs1, Options);
351encode_mfas([], _Address, AccCode, Relocs, _Options) ->
352  {AccCode,Relocs}.
353
354encode_insns([I|Insns], Address, FunAddress, LabelMap, Relocs, AccCode, Options) ->
355  case I of
356    {'.label',L,_} ->
357      LabelAddress = gb_trees:get(L, LabelMap) + FunAddress,
358      ?ASSERT(Address =:= LabelAddress),	% sanity check
359      print_insn(Address, [], I, Options),
360      encode_insns(Insns, Address, FunAddress, LabelMap, Relocs, AccCode, Options);
361    {'.reloc',Data,_} ->
362      Reloc = encode_reloc(Data, Address, FunAddress, LabelMap),
363      encode_insns(Insns, Address, FunAddress, LabelMap, [Reloc|Relocs], AccCode, Options);
364    {bp_sdi,_,_} ->
365      encode_insns(fix_bp_sdi(I, Insns, Address, FunAddress, LabelMap),
366		   Address, FunAddress, LabelMap, Relocs, AccCode, Options);
367    %% {br_sdi,_,_} ->
368    %%  encode_insns(fix_br_sdi(I, Insns, Address, FunAddress, LabelMap),
369    %%		   Address, FunAddress, LabelMap, Relocs, AccCode, Options);
370    _ ->
371      {Op,Arg,_} = fix_jumps(I, Address, FunAddress, LabelMap),
372      Word = hipe_sparc_encode:insn_encode(Op, Arg),
373      print_insn(Address, Word, I, Options),
374      Segment = <<Word:32/integer-big>>,
375      NewAccCode = [Segment|AccCode],
376      encode_insns(Insns, Address+4, FunAddress, LabelMap, Relocs, NewAccCode, Options)
377  end;
378encode_insns([], Address, _FunAddress, _LabelMap, Relocs, AccCode, _Options) ->
379  {Address,Relocs,AccCode}.
380
381encode_reloc(Data, Address, FunAddress, LabelMap) ->
382  case Data of
383    {call,MFAorPrim,Linkage} ->
384      %% call_rec and call_tail are patched the same, so no need to distinguish
385      %% call from tailcall
386      PatchTypeExt =
387	case Linkage of
388	  remote -> ?CALL_REMOTE;
389	  not_remote -> ?CALL_LOCAL
390	end,
391      {PatchTypeExt, Address, untag_mfa_or_prim(MFAorPrim)};
392    {load_atom,Atom} ->
393      {?LOAD_ATOM, Address, Atom};
394    {load_address,X} ->
395      {?LOAD_ADDRESS, Address, X};
396    {sdesc,SDesc} ->
397      #sparc_sdesc{exnlab=ExnLab,fsize=FSize,arity=Arity,live=Live} = SDesc,
398      ExnRA =
399	case ExnLab of
400	  [] -> [];	% don't cons up a new one
401	  ExnLab -> gb_trees:get(ExnLab, LabelMap) + FunAddress
402	end,
403      {?SDESC, Address,
404       ?STACK_DESC(ExnRA, FSize, Arity, Live)}
405  end.
406
407untag_mfa_or_prim(#sparc_mfa{m=M,f=F,a=A}) -> {M,F,A};
408untag_mfa_or_prim(#sparc_prim{prim=Prim}) -> Prim.
409
410fix_bp_sdi(I, Insns, InsnAddress, FunAddress, LabelMap) ->
411  {bp_sdi,Opnds,OrigI} = I,
412  {{'cond',Cond},{pred,Pred},Label} = Opnds,
413  {label,L} = Label,
414  LabelAddress = gb_trees:get(L, LabelMap) + FunAddress,
415  BD = (LabelAddress - InsnAddress) div 4,
416  if BD >= -16#40000, BD =< 16#3FFFF ->
417      [{bp, Opnds, OrigI} | Insns];
418     true ->
419      %% bp<cond>,<pred> L; Delay
420      %% -->
421      %% bp<!cond>,<!pred> 1f; Delay; ba L; nop; 1:
422      [Delay|Rest] = Insns,
423      NewCond = hipe_sparc:negate_cond(Cond),
424      NewPred = 1.0 - Pred,
425      [{bp,
426	{{'cond',NewCond},{pred,NewPred},'.+16'},
427	#bp{'cond'=NewCond,pred=NewPred,label='.+16'}}, % pp will be ugly
428       Delay,	% should be a NOP
429       {ba, Label, #bp{'cond'='a',pred=1.0,label=L}},
430       {sethi, {{uimm22,0},{r,0}}, #comment{term=nop}} |
431       Rest]
432  end.
433
434-ifdef(notdef).	% XXX: only for sparc64, alas
435fix_br_sdi(I, Insns, InsnAddress, FunAddress, LabelMap) ->
436  {br_sdi,Opnds,OrigI} = I,
437  {{rcond,RCond},{pred,Pred},Src,{label,L}} = Opnds,
438  LabelAddress = gb_trees:get(L, LabelMap) + FunAddress,
439  BD = (LabelAddress - InsnAddress) div 4,
440  if BD >= -16#8000, BD =< 16#7FFF ->
441      [{br, Opnds, OrigI} | Insns];
442     true ->
443      %% br<rcond>,<pred> reg, L; Delay
444      %% -->
445      %% br<!rcond>,<!pred> reg, 1f; Delay; ba L; nop; 1:
446      [Delay|Rest] = Insns,
447      {reg,SrcReg} = Src,
448      NewRCond = hipe_sparc:negate_rcond(RCond),
449      NewPred = 1.0 - Pred,
450      [{br,
451	{{rcond,NewRCond},{pred,NewPred},Src,'.+16'},
452	#br{rcond=NewRCond,pred=NewPred,src=SrcReg,label='.+16'}}, % pp will be ugly
453       Delay,	% should be a NOP
454       {ba, {label,L}, #bp{'cond'='a',pred=1.0,label=L}},
455       {sethi, {{uimm22,0},{r,0}}, #comment{term=nop}} |
456       Rest]
457  end.
458-endif.
459
460fix_jumps(I, InsnAddress, FunAddress, LabelMap) ->
461  case I of
462    {ba, {label,L}, OrigI} ->
463      LabelAddress = gb_trees:get(L, LabelMap) + FunAddress,
464      BD = (LabelAddress - InsnAddress) div 4,
465      %% ensure BD fits in a 22 bit sign-extended field
466      ?ASSERT(BD =<  16#1FFFFF),
467      ?ASSERT(BD >= -16#200000),
468      {ba, {disp22,BD band 16#3FFFFF}, OrigI};
469    {bp, {Cond,Pred,Target}, OrigI} ->
470      LabelAddress =
471	case Target of
472	  {label,L} -> gb_trees:get(L, LabelMap) + FunAddress;
473	  '.+16' -> InsnAddress + 16
474	end,
475      BD = (LabelAddress - InsnAddress) div 4,
476      %% ensure BD fits in a 19 bit sign-extended field
477      ?ASSERT(BD =<  16#3FFFF),
478      ?ASSERT(BD >= -16#40000),
479      {bp, {Cond,px(Pred),{disp19,BD band 16#7FFFF}}, OrigI};
480    %% {br, _, _} -> fix_br(I, InsnAddress, FunAddress, LabelMap);
481    _ -> I
482  end.
483
484-ifdef(notdef).	% XXX: only for sparc64, alas
485fix_br(I, InsnAddress, FunAddress, LabelMap) ->
486  {br, {RCond,Pred,Src,Target}, OrigI} = I,
487  LabelAddress =
488    case Target of
489      {label,L} -> gb_trees:get(L, LabelMap) + FunAddress;
490      '.+16' -> InsnAddress + 16
491    end,
492  BD = (LabelAddress - InsnAddress) div 4,
493  %% ensure BD fits in a 16 bit sign-extended field
494  ?ASSERT(BD =<  16#7FFF),
495  ?ASSERT(BD >= -16#8000),
496  {br, {RCond,px(Pred),Src,{disp16,BD band 16#FFFF}}, OrigI}.
497-endif.
498
499px({pred,Pred}) ->	% XXX: use pt/pn throughout entire backend
500  {pred, if Pred >= 0.5 -> 'pt'; true -> 'pn' end}.
501
502%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
503
504%%%
505%%% Assembly listing support (pp_asm option).
506%%%
507
508print(String, Arglist, Options) ->
509  ?when_option(pp_asm, Options, io:format(String, Arglist)).
510
511print_insn(Address, Word, I, Options) ->
512  ?when_option(pp_asm, Options, print_insn_2(Address, Word, I)).
513
514print_insn_2(Address, Word, {_,_,OrigI}) ->
515  io:format("~8.16.0b | ", [Address]),
516  print_code_list(word_to_bytes(Word), 0),
517  hipe_sparc_pp:pp_insn(OrigI).
518
519word_to_bytes(W) ->
520  case W of
521    [] -> [];	% label or other pseudo instruction
522    _ -> [(W bsr 24) band 16#FF, (W bsr 16) band 16#FF,
523	  (W bsr 8) band 16#FF, W band 16#FF]
524  end.
525
526print_code_list([Byte|Rest], Len) ->
527  print_byte(Byte),
528  print_code_list(Rest, Len+1);
529print_code_list([], Len) ->
530  fill_spaces(8-(Len*2)),
531  io:format(" | ").
532
533print_byte(Byte) ->
534  io:format("~2.16.0b", [Byte band 16#FF]).
535
536fill_spaces(N) when N > 0 ->
537  io:format(" "),
538  fill_spaces(N-1);
539fill_spaces(0) ->
540  [].
541