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_ppc_frame).
16-export([frame/1]).
17-include("hipe_ppc.hrl").
18-include("../rtl/hipe_literals.hrl").
19
20frame(CFG) ->
21  Formals = fix_formals(hipe_ppc_cfg:params(CFG)),
22  Temps0 = all_temps(CFG, Formals),
23  MinFrame = defun_minframe(CFG),
24  Temps = ensure_minframe(MinFrame, Temps0),
25  ClobbersLR = clobbers_lr(CFG),
26  Liveness = hipe_ppc_liveness_all:analyse(CFG),
27  do_body(CFG, Liveness, Formals, Temps, ClobbersLR).
28
29fix_formals(Formals) ->
30  fix_formals(hipe_ppc_registers:nr_args(), Formals).
31
32fix_formals(0, Rest) -> Rest;
33fix_formals(N, [_|Rest]) -> fix_formals(N-1, Rest);
34fix_formals(_, []) -> [].
35
36do_body(CFG0, Liveness, Formals, Temps, ClobbersLR) ->
37  Context = mk_context(Liveness, Formals, Temps, ClobbersLR),
38  CFG1 = hipe_ppc_cfg:map_bbs(
39	   fun(Lbl, BB) -> do_block(Lbl, BB, Context) end, CFG0),
40  do_prologue(CFG1, Context).
41
42do_block(Label, Block, Context) ->
43  Liveness = context_liveness(Context),
44  LiveOut = hipe_ppc_liveness_all:liveout(Liveness, Label),
45  Code = hipe_bb:code(Block),
46  NewCode = do_block(Code, LiveOut, Context, context_framesize(Context), []),
47  hipe_bb:code_update(Block, NewCode).
48
49do_block([I|Insns], LiveOut, Context, FPoff0, RevCode) ->
50  {NewIs, FPoff1} = do_insn(I, LiveOut, Context, FPoff0),
51  do_block(Insns, LiveOut, Context, FPoff1, lists:reverse(NewIs, RevCode));
52do_block([], _, Context, FPoff, RevCode) ->
53  FPoff0 = context_framesize(Context),
54  if FPoff =:= FPoff0 -> [];
55     true -> exit({?MODULE,do_block,FPoff})
56  end,
57  lists:reverse(RevCode, []).
58
59do_insn(I, LiveOut, Context, FPoff) ->
60  case I of
61    #blr{} ->
62      {do_blr(I, Context, FPoff), context_framesize(Context)};
63    #pseudo_call{} ->
64      do_pseudo_call(I, LiveOut, Context, FPoff);
65    #pseudo_call_prepare{} ->
66      do_pseudo_call_prepare(I, FPoff);
67    #pseudo_move{} ->
68      {do_pseudo_move(I, Context, FPoff), FPoff};
69    #pseudo_spill_move{} ->
70      {do_pseudo_spill_move(I, Context, FPoff), FPoff};
71    #pseudo_tailcall{} ->
72      {do_pseudo_tailcall(I, Context), context_framesize(Context)};
73    #pseudo_fmove{} ->
74      {do_pseudo_fmove(I, Context, FPoff), FPoff};
75    #pseudo_spill_fmove{} ->
76      {do_pseudo_spill_fmove(I, Context, FPoff), FPoff};
77    _ ->
78      {[I], FPoff}
79  end.
80
81%%%
82%%% Moves, with Dst or Src possibly a pseudo
83%%%
84
85do_pseudo_move(I, Context, FPoff) ->
86  Dst = hipe_ppc:pseudo_move_dst(I),
87  Src = hipe_ppc:pseudo_move_src(I),
88  case temp_is_pseudo(Dst) of
89    true ->
90      Offset = pseudo_offset(Dst, FPoff, Context),
91      mk_store(hipe_ppc:stop_word(), Src, Offset, mk_sp(), []);
92    _ ->
93      case temp_is_pseudo(Src) of
94	true ->
95	  Offset = pseudo_offset(Src, FPoff, Context),
96	  mk_load(hipe_ppc:ldop_word(), Dst, Offset, mk_sp(), []);
97	_ ->
98	  case hipe_ppc:temp_reg(Dst) =:= hipe_ppc:temp_reg(Src) of
99	    true -> [];
100	    false -> [hipe_ppc:mk_alu('or', Dst, Src, Src)]
101	  end
102      end
103  end.
104
105do_pseudo_spill_move(I, Context, FPoff) ->
106  #pseudo_spill_move{dst=Dst,temp=Temp,src=Src} = I,
107  case temp_is_pseudo(Src) andalso temp_is_pseudo(Dst) of
108    false -> % Register allocator changed its mind, turn back to move
109      do_pseudo_move(hipe_ppc:mk_pseudo_move(Dst, Src), Context, FPoff);
110    true ->
111      SrcOffset = pseudo_offset(Src, FPoff, Context),
112      DstOffset = pseudo_offset(Dst, FPoff, Context),
113      case SrcOffset =:= DstOffset of
114	true -> []; % omit move-to-self
115	false ->
116	  mk_load(hipe_ppc:ldop_word(), Temp, SrcOffset, mk_sp(),
117		  mk_store(hipe_ppc:stop_word(), Temp, DstOffset, mk_sp(), []))
118      end
119  end.
120
121do_pseudo_fmove(I, Context, FPoff) ->
122  Dst = hipe_ppc:pseudo_fmove_dst(I),
123  Src = hipe_ppc:pseudo_fmove_src(I),
124  case temp_is_pseudo(Dst) of
125    true ->
126      Offset = pseudo_offset(Dst, FPoff, Context),
127      hipe_ppc:mk_fstore(Src, Offset, mk_sp(), 0);
128    _ ->
129      case temp_is_pseudo(Src) of
130	true ->
131	  Offset = pseudo_offset(Src, FPoff, Context),
132	  hipe_ppc:mk_fload(Dst, Offset, mk_sp(), 0);
133	_ ->
134	  [hipe_ppc:mk_fp_unary('fmr', Dst, Src)]
135      end
136  end.
137
138do_pseudo_spill_fmove(I, Context, FPoff) ->
139  #pseudo_spill_fmove{dst=Dst,temp=Temp,src=Src} = I,
140  case temp_is_pseudo(Src) andalso temp_is_pseudo(Dst) of
141    false -> % Register allocator changed its mind, turn back to move
142      do_pseudo_fmove(hipe_ppc:mk_pseudo_fmove(Dst, Src), Context, FPoff);
143    true ->
144      SrcOffset = pseudo_offset(Src, FPoff, Context),
145      DstOffset = pseudo_offset(Dst, FPoff, Context),
146      case SrcOffset =:= DstOffset of
147	true -> []; % omit move-to-self
148	false ->
149	  hipe_ppc:mk_fload(Temp, SrcOffset, mk_sp(), 0)
150	    ++ hipe_ppc:mk_fstore(Temp, DstOffset, mk_sp(), 0)
151      end
152  end.
153
154pseudo_offset(Temp, FPoff, Context) ->
155  FPoff + context_offset(Context, Temp).
156
157%%%
158%%% Return - deallocate frame and emit 'ret $N' insn.
159%%%
160
161do_blr(I, Context, FPoff) ->
162  %% XXX: perhaps use explicit pseudo_move;mtlr,
163  %% avoiding the need to hard-code Temp1 here
164  %% XXX: typically only one instruction between
165  %% the mtlr and the blr, ouch
166  restore_lr(FPoff, Context,
167	     adjust_sp(FPoff + word_size() * context_arity(Context),
168		       [I])).
169
170restore_lr(FPoff, Context, Rest) ->
171  case context_clobbers_lr(Context) of
172    false -> Rest;
173    true ->
174      Temp = mk_temp1(),
175      mk_load(hipe_ppc:ldop_word(), Temp, FPoff - word_size(), mk_sp(),
176	      [hipe_ppc:mk_mtspr('lr', Temp) |
177	       Rest])
178  end.
179
180adjust_sp(N, Rest) ->
181  if N =:= 0 ->
182      Rest;
183     true ->
184      SP = mk_sp(),
185      hipe_ppc:mk_addi(SP, SP, N, Rest)
186  end.
187
188%%%
189%%% Recursive calls.
190%%%
191
192do_pseudo_call_prepare(I, FPoff0) ->
193  %% Create outgoing arguments area on the stack.
194  NrStkArgs = hipe_ppc:pseudo_call_prepare_nrstkargs(I),
195  Offset = NrStkArgs * word_size(),
196  {adjust_sp(-Offset, []), FPoff0 + Offset}.
197
198do_pseudo_call(I, LiveOut, Context, FPoff0) ->
199  #ppc_sdesc{exnlab=ExnLab,arity=OrigArity} = hipe_ppc:pseudo_call_sdesc(I),
200  FunC = hipe_ppc:pseudo_call_func(I),
201  LiveTemps = [Temp || Temp <- LiveOut, temp_is_pseudo(Temp)],
202  SDesc = mk_sdesc(ExnLab, Context, LiveTemps),
203  ContLab = hipe_ppc:pseudo_call_contlab(I),
204  Linkage = hipe_ppc:pseudo_call_linkage(I),
205  CallCode = [hipe_ppc:mk_pseudo_call(FunC, SDesc, ContLab, Linkage)],
206  StkArity = erlang:max(0, OrigArity - hipe_ppc_registers:nr_args()),
207  context_need_stack(Context, stack_need(FPoff0, StkArity, FunC)),
208  ArgsBytes = word_size() * StkArity,
209  {CallCode, FPoff0 - ArgsBytes}.
210
211stack_need(FPoff, StkArity, FunC) ->
212  case FunC of
213    #ppc_prim{} -> FPoff;
214    #ppc_mfa{m=M,f=F,a=A} ->
215      case erlang:is_builtin(M, F, A) of
216	true -> FPoff;
217	false -> stack_need_general(FPoff, StkArity)
218      end;
219    'ctr' -> stack_need_general(FPoff, StkArity)
220  end.
221
222stack_need_general(FPoff, StkArity) ->
223  erlang:max(FPoff, FPoff + (?PPC_LEAF_WORDS - StkArity) * word_size()).
224
225%%%
226%%% Create stack descriptors for call sites.
227%%%
228
229mk_sdesc(ExnLab, Context, Temps) ->	% for normal calls
230  Temps0 = only_tagged(Temps),
231  Live = mk_live(Context, Temps0),
232  Arity = context_arity(Context),
233  FSize = context_framesize(Context),
234  hipe_ppc:mk_sdesc(ExnLab, (FSize div word_size())-1, Arity,
235                    list_to_tuple(Live)).
236
237only_tagged(Temps)->
238  [X || X <- Temps, hipe_ppc:temp_type(X) =:= 'tagged'].
239
240mk_live(Context, Temps) ->
241  lists:sort([temp_to_slot(Context, Temp) || Temp <- Temps]).
242
243temp_to_slot(Context, Temp) ->
244  (context_framesize(Context) + context_offset(Context, Temp))
245    div word_size().
246
247mk_minimal_sdesc(Context) ->		% for inc_stack_0 calls
248  hipe_ppc:mk_sdesc([], 0, context_arity(Context), {}).
249
250%%%
251%%% Tailcalls.
252%%%
253
254do_pseudo_tailcall(I, Context) -> % always at FPoff=context_framesize(Context)
255  Arity = context_arity(Context),
256  Args = hipe_ppc:pseudo_tailcall_stkargs(I),
257  FunC = hipe_ppc:pseudo_tailcall_func(I),
258  Linkage = hipe_ppc:pseudo_tailcall_linkage(I),
259  {Insns, FPoff1} = do_tailcall_args(Args, Context),
260  context_need_stack(Context, FPoff1),
261  StkArity = length(Args),
262  FPoff2 = FPoff1 + (Arity - StkArity) * word_size(),
263  context_need_stack(Context, stack_need(FPoff2, StkArity, FunC)),
264  I2 =
265    case FunC of
266      'ctr' ->
267	hipe_ppc:mk_bctr([]);
268      Fun ->
269	hipe_ppc:mk_b_fun(Fun, Linkage)
270    end,
271  %% XXX: break out the LR restore, just like for blr?
272  restore_lr(context_framesize(Context), Context,
273	     Insns ++ adjust_sp(FPoff2, [I2])).
274
275do_tailcall_args(Args, Context) ->
276  FPoff0 = context_framesize(Context),
277  Arity = context_arity(Context),
278  FrameTop = word_size()*Arity,
279  DangerOff = FrameTop - word_size()*length(Args),
280  %%
281  Moves = mk_moves(Args, FrameTop, []),
282  %%
283  {Stores, Simple, Conflict} =
284    split_moves(Moves, Context, DangerOff, [], [], []),
285  %% sanity check (shouldn't trigger any more)
286  if DangerOff < -FPoff0 ->
287      exit({?MODULE,do_tailcall_args,DangerOff,-FPoff0});
288     true -> []
289  end,
290  FPoff1 = FPoff0,
291  %%
292  {Pushes, Pops, FPoff2} = split_conflict(Conflict, FPoff1, [], []),
293  %%
294  TempReg = hipe_ppc_registers:temp1(),
295  %%
296  {adjust_sp(-(FPoff2 - FPoff1),
297	     simple_moves(Pushes, FPoff2, TempReg,
298			  store_moves(Stores, FPoff2, TempReg,
299				      simple_moves(Simple, FPoff2, TempReg,
300						   simple_moves(Pops, FPoff2, TempReg,
301								[]))))),
302   FPoff2}.
303
304mk_moves([Arg|Args], Off, Moves) ->
305  Off1 = Off - word_size(),
306  mk_moves(Args, Off1, [{Arg,Off1}|Moves]);
307mk_moves([], _, Moves) ->
308  Moves.
309
310split_moves([Move|Moves], Context, DangerOff, Stores, Simple, Conflict) ->
311  {Src,DstOff} = Move,
312  case src_is_pseudo(Src) of
313    false ->
314      split_moves(Moves, Context, DangerOff, [Move|Stores],
315		  Simple, Conflict);
316    true ->
317      SrcOff = context_offset(Context, Src),
318      Type = typeof_temp(Src),
319      if SrcOff =:= DstOff ->
320	  split_moves(Moves, Context, DangerOff, Stores,
321		      Simple, Conflict);
322	 SrcOff >= DangerOff ->
323	  split_moves(Moves, Context, DangerOff, Stores,
324		      Simple, [{SrcOff,DstOff,Type}|Conflict]);
325	 true ->
326	  split_moves(Moves, Context, DangerOff, Stores,
327		      [{SrcOff,DstOff,Type}|Simple], Conflict)
328      end
329  end;
330split_moves([], _, _, Stores, Simple, Conflict) ->
331  {Stores, Simple, Conflict}.
332
333split_conflict([{SrcOff,DstOff,Type}|Conflict], FPoff, Pushes, Pops) ->
334  FPoff1 = FPoff + word_size(),
335  Push = {SrcOff,-FPoff1,Type},
336  Pop = {-FPoff1,DstOff,Type},
337  split_conflict(Conflict, FPoff1, [Push|Pushes], [Pop|Pops]);
338split_conflict([], FPoff, Pushes, Pops) ->
339  {lists:reverse(Pushes), Pops, FPoff}.
340
341simple_moves([{SrcOff,DstOff,Type}|Moves], FPoff, TempReg, Rest) ->
342  Temp = hipe_ppc:mk_temp(TempReg, Type),
343  SP = mk_sp(),
344  LoadOff = FPoff+SrcOff,
345  StoreOff = FPoff+DstOff,
346  simple_moves(Moves, FPoff, TempReg,
347	       mk_load(hipe_ppc:ldop_word(), Temp, LoadOff, SP,
348		       mk_store(hipe_ppc:stop_word(), Temp, StoreOff, SP,
349				Rest)));
350simple_moves([], _, _, Rest) ->
351  Rest.
352
353store_moves([{Src,DstOff}|Moves], FPoff, TempReg, Rest) ->
354  %%Type = typeof_temp(Src),
355  SP = mk_sp(),
356  StoreOff = FPoff+DstOff,
357  {NewSrc,FixSrc} =
358    case hipe_ppc:is_temp(Src) of
359      true ->
360	{Src, []};
361      _ ->
362	Temp = hipe_ppc:mk_temp(TempReg, 'untagged'),
363	{Temp, hipe_ppc:mk_li(Temp, Src)}
364    end,
365  store_moves(Moves, FPoff, TempReg,
366	      FixSrc ++ mk_store(hipe_ppc:stop_word(), NewSrc,
367				 StoreOff, SP, Rest));
368store_moves([], _, _, Rest) ->
369  Rest.
370
371%%%
372%%% Contexts
373%%%
374
375-record(context, {liveness, framesize, arity, map, clobbers_lr, ref_maxstack}).
376
377mk_context(Liveness, Formals, Temps, ClobbersLR) ->
378  {Map, MinOff} = mk_temp_map(Formals, ClobbersLR, Temps),
379  FrameSize = (-MinOff),
380  RefMaxStack = hipe_bifs:ref(FrameSize),
381  #context{liveness=Liveness,
382	   framesize=FrameSize, arity=length(Formals),
383	   map=Map, clobbers_lr=ClobbersLR, ref_maxstack=RefMaxStack}.
384
385context_need_stack(#context{ref_maxstack=RM}, N) ->
386  M = hipe_bifs:ref_get(RM),
387  if N > M -> hipe_bifs:ref_set(RM, N);
388     true -> []
389  end.
390
391context_maxstack(#context{ref_maxstack=RM}) ->
392  hipe_bifs:ref_get(RM).
393
394context_arity(#context{arity=Arity}) ->
395  Arity.
396
397context_framesize(#context{framesize=FrameSize}) ->
398  FrameSize.
399
400context_liveness(#context{liveness=Liveness}) ->
401  Liveness.
402
403context_offset(#context{map=Map}, Temp) ->
404  tmap_lookup(Map, Temp).
405
406context_clobbers_lr(#context{clobbers_lr=ClobbersLR}) -> ClobbersLR.
407
408mk_temp_map(Formals, ClobbersLR, Temps) ->
409  {Map, 0} = enter_vars(Formals, word_size() * length(Formals),
410			tmap_empty()),
411  TempsList = tset_to_list(Temps),
412  AllTemps =
413    case ClobbersLR of
414      false -> TempsList;
415      true ->
416	RA = hipe_ppc:mk_new_temp('untagged'),
417	[RA|TempsList]
418    end,
419  enter_vars(AllTemps, 0, Map).
420
421enter_vars([V|Vs], PrevOff, Map) ->
422  Off =
423    case hipe_ppc:temp_type(V) of
424      'double' -> PrevOff - 8;
425      _ -> PrevOff - word_size()
426    end,
427  enter_vars(Vs, Off, tmap_bind(Map, V, Off));
428enter_vars([], Off, Map) ->
429  {Map, Off}.
430
431tmap_empty() ->
432  gb_trees:empty().
433
434tmap_bind(Map, Key, Val) ->
435  gb_trees:insert(Key, Val, Map).
436
437tmap_lookup(Map, Key) ->
438  gb_trees:get(Key, Map).
439
440%%%
441%%% do_prologue: prepend stack frame allocation code.
442%%%
443%%% NewStart:
444%%%	temp1 = *(P + P_SP_LIMIT)
445%%%	temp2 = SP - MaxStack
446%%%	cmp temp2, temp1
447%%%	temp1 = LR				[if ClobbersLR][hoisted]
448%%%	if (ltu) goto IncStack else goto AllocFrame
449%%% AllocFrame:
450%%%	SP = temp2			[if FrameSize == MaxStack]
451%%%	SP -= FrameSize			[if FrameSize != MaxStack]
452%%%	*(SP + FrameSize-WordSize) = temp1	[if ClobbersLR]
453%%%	goto OldStart
454%%% OldStart:
455%%%	...
456%%% IncStack:
457%%%	temp1 = LR				[if not ClobbersLR]
458%%%	bl inc_stack
459%%%	LR = temp1
460%%%	goto NewStart
461
462do_prologue(CFG, Context) ->
463  MaxStack = context_maxstack(Context),
464  if MaxStack > 0 ->
465      FrameSize = context_framesize(Context),
466      OldStartLab = hipe_ppc_cfg:start_label(CFG),
467      NewStartLab = hipe_gensym:get_next_label(ppc),
468      %%
469      P = hipe_ppc:mk_temp(hipe_ppc_registers:proc_pointer(), 'untagged'),
470      Temp1 = mk_temp1(),
471      SP = mk_sp(),
472      %%
473      ClobbersLR = context_clobbers_lr(Context),
474      GotoOldStartCode = [hipe_ppc:mk_b_label(OldStartLab)],
475      AllocFrameCodeTail =
476	case ClobbersLR of
477	  false -> GotoOldStartCode;
478	  true -> mk_store(hipe_ppc:stop_word(), Temp1,
479			   FrameSize-word_size(), SP, GotoOldStartCode)
480	end,
481      %%
482      Arity = context_arity(Context),
483      Guaranteed = erlang:max(0, (?PPC_LEAF_WORDS - Arity) * word_size()),
484      %%
485      {CFG1,NewStartCode} =
486	if MaxStack =< Guaranteed ->
487	    %% io:format("~w: MaxStack ~w =< Guaranteed ~w :-)\n", [?MODULE,MaxStack,Guaranteed]),
488	    AllocFrameCode = adjust_sp(-FrameSize, AllocFrameCodeTail),
489	    NewStartCode0 =
490	      case ClobbersLR of
491		false -> AllocFrameCode;
492		true -> [hipe_ppc:mk_mfspr(Temp1, 'lr') | AllocFrameCode]
493	      end,
494	    {CFG,NewStartCode0};
495	   true ->
496	    %% io:format("~w: MaxStack ~w > Guaranteed ~w :-(\n", [?MODULE,MaxStack,Guaranteed]),
497	    AllocFrameLab = hipe_gensym:get_next_label(ppc),
498	    IncStackLab = hipe_gensym:get_next_label(ppc),
499	    Temp2 = mk_temp2(),
500	    %%
501	    NewStartCodeTail2 =
502	      [hipe_ppc:mk_pseudo_bc('lt', IncStackLab, AllocFrameLab, 0.01)],
503	    NewStartCodeTail1 =
504	      case ClobbersLR of
505		false -> NewStartCodeTail2;
506		true -> [hipe_ppc:mk_mfspr(Temp1, 'lr') | NewStartCodeTail2]
507	      end,
508	    NewStartCode0 =
509	      [hipe_ppc:mk_load(hipe_ppc:ldop_word(), Temp1, ?P_NSP_LIMIT, P) |
510	       hipe_ppc:mk_addi(Temp2, SP, -MaxStack,
511				[hipe_ppc:mk_cmp('cmpl', Temp2, Temp1) |
512				 NewStartCodeTail1])],
513	    %%
514	    AllocFrameCode =
515	      if MaxStack =:= FrameSize ->
516		  %% io:format("~w: MaxStack =:= FrameSize =:= ~w :-)\n", [?MODULE,MaxStack]),
517		  [hipe_ppc:mk_alu('or', SP, Temp2, Temp2) |
518		   AllocFrameCodeTail];
519		 true ->
520		  %% io:format("~w: MaxStack ~w =/= FrameSize ~w :-(\n", [?MODULE,MaxStack,FrameSize]),
521		  adjust_sp(-FrameSize, AllocFrameCodeTail)
522	      end,
523	    %%
524	    IncStackCodeTail =
525	      [hipe_ppc:mk_bl(hipe_ppc:mk_prim('inc_stack_0'),
526			      mk_minimal_sdesc(Context), not_remote),
527	       hipe_ppc:mk_mtspr('lr', Temp1),
528	       hipe_ppc:mk_b_label(NewStartLab)],
529	    IncStackCode =
530	      case ClobbersLR of
531		true -> IncStackCodeTail;
532		false -> [hipe_ppc:mk_mfspr(Temp1, 'lr') | IncStackCodeTail]
533	      end,
534	    %%
535	    CFG0a = hipe_ppc_cfg:bb_add(CFG, AllocFrameLab,
536					hipe_bb:mk_bb(AllocFrameCode)),
537	    CFG0b = hipe_ppc_cfg:bb_add(CFG0a, IncStackLab,
538					hipe_bb:mk_bb(IncStackCode)),
539	    %%
540	    {CFG0b,NewStartCode0}
541	end,
542      %%
543      CFG2 = hipe_ppc_cfg:bb_add(CFG1, NewStartLab,
544                                 hipe_bb:mk_bb(NewStartCode)),
545      hipe_ppc_cfg:start_label_update(CFG2, NewStartLab);
546     true ->
547      CFG
548  end.
549
550%%% Create a load instruction.
551%%% May clobber Dst early for large offsets. In principle we could
552%%% clobber R0 if Dst =:= Base, but Dst =/= Base here in frame.
553
554mk_load(LdOp, Dst, Offset, Base, Rest) ->
555  hipe_ppc:mk_load(LdOp, Dst, Offset, Base, 'error', Rest).
556
557%%% Create a store instruction.
558%%% May clobber R0 for large offsets.
559
560mk_store(StOp, Src, Offset, Base, Rest) ->
561  hipe_ppc:mk_store(StOp, Src, Offset, Base, 0, Rest).
562
563%%% typeof_temp -- what's temp's type?
564
565typeof_temp(Temp) ->
566  hipe_ppc:temp_type(Temp).
567
568%%% Cons up an 'SP' Temp.
569
570mk_sp() ->
571  hipe_ppc:mk_temp(hipe_ppc_registers:stack_pointer(), 'untagged').
572
573%%% Cons up a 'TEMP1' Temp.
574
575mk_temp1() ->
576  hipe_ppc:mk_temp(hipe_ppc_registers:temp1(), 'untagged').
577
578%%% Cons up a 'TEMP2' Temp.
579
580mk_temp2() ->
581  hipe_ppc:mk_temp(hipe_ppc_registers:temp2(), 'untagged').
582
583%%% Check if an operand is a pseudo-Temp.
584
585src_is_pseudo(Src) ->
586  hipe_ppc:is_temp(Src) andalso temp_is_pseudo(Src).
587
588temp_is_pseudo(Temp) ->
589  not(hipe_ppc:temp_is_precoloured(Temp)).
590
591%%%
592%%% Detect if a Defun's body clobbers LR.
593%%%
594
595clobbers_lr(CFG) ->
596  any_insn(fun(#pseudo_call{}) -> true;
597	      (_) -> false
598	   end, CFG).
599
600any_insn(Pred, CFG) ->
601  %% Abuse fold to do an efficient "any"-operation using nonlocal control flow
602  FoundSatisfying = make_ref(),
603  try fold_insns(fun (I, _) ->
604		     case Pred(I) of
605		       true -> throw(FoundSatisfying);
606		       false -> false
607		     end
608		 end, false, CFG)
609  of _ -> false
610  catch FoundSatisfying -> true
611  end.
612
613%%%
614%%% Build the set of all temps used in a Defun's body.
615%%%
616
617all_temps(CFG, Formals) ->
618  S0 = fold_insns(fun find_temps/2, tset_empty(), CFG),
619  S1 = tset_del_list(S0, Formals),
620  tset_filter(S1, fun(T) -> temp_is_pseudo(T) end).
621
622find_temps(I, S0) ->
623  S1 = tset_add_list(S0, hipe_ppc_defuse:insn_def_all(I)),
624  tset_add_list(S1, hipe_ppc_defuse:insn_use_all(I)).
625
626fold_insns(Fun, InitAcc, CFG) ->
627  hipe_ppc_cfg:fold_bbs(
628    fun(_, BB, Acc0) -> lists:foldl(Fun, Acc0, hipe_bb:code(BB)) end,
629    InitAcc, CFG).
630
631tset_empty() ->
632  gb_sets:new().
633
634tset_size(S) ->
635  gb_sets:size(S).
636
637tset_insert(S, T) ->
638  gb_sets:add_element(T, S).
639
640tset_add_list(S, Ts) ->
641  gb_sets:union(S, gb_sets:from_list(Ts)).
642
643tset_del_list(S, Ts) ->
644  gb_sets:subtract(S, gb_sets:from_list(Ts)).
645
646tset_filter(S, F) ->
647  gb_sets:filter(F, S).
648
649tset_to_list(S) ->
650  gb_sets:to_list(S).
651
652%%%
653%%% Compute minimum permissible frame size, ignoring spilled temps.
654%%% This is done to ensure that we won't have to adjust the frame size
655%%% in the middle of a tailcall.
656%%%
657
658defun_minframe(CFG) ->
659  MaxTailArity = fold_insns(fun insn_mta/2, 0, CFG),
660  MyArity = length(fix_formals(hipe_ppc_cfg:params(CFG))),
661  erlang:max(MaxTailArity - MyArity, 0).
662
663insn_mta(I, MTA) ->
664  case I of
665    #pseudo_tailcall{arity=Arity} ->
666      erlang:max(MTA, Arity - hipe_ppc_registers:nr_args());
667    _ -> MTA
668  end.
669
670%%%
671%%% Ensure that we have enough temps to satisfy the minimum frame size,
672%%% if necessary by prepending unused dummy temps.
673%%%
674
675ensure_minframe(MinFrame, Temps) ->
676  ensure_minframe(MinFrame, tset_size(Temps), Temps).
677
678ensure_minframe(MinFrame, Frame, Temps) ->
679  if MinFrame > Frame ->
680      Temp = hipe_ppc:mk_new_temp('untagged'),
681      ensure_minframe(MinFrame, Frame+1, tset_insert(Temps, Temp));
682     true -> Temps
683  end.
684
685word_size() ->
686  hipe_rtl_arch:word_size().
687