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