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%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16%%@doc
17%%	       RESTORE REUSE LIVE RANGE SPLITTING PASS
18%%
19%% This is a simple live range splitter that tries to avoid sequences where a
20%% temporary is accessed on stack multiple times by keeping a copy of that temp
21%% around in a register.
22%%
23%% At any point where a temporary that is expected to be spilled (see uses of
24%% spills_add_list/2) is defined or used, this pass considers that temporary
25%% "available".
26%%
27%% Limitations:
28%%   * If a live range part starts with several different restores, this module
29%%     will introduce a new temp number for each of them, and later be forced to
30%%     generate phi blocks. It would be more efficient to introduce just a
31%%     single temp number. That would also remove the need for the phi blocks.
32%%   * If a live range part ends in a definition, that definition should just
33%%     define the base temp rather than the substitution, since some CISC
34%%     targets might be able to inline the memory access in the instruction.
35-module(hipe_restore_reuse).
36
37-export([split/4]).
38
39%% Exports for hipe_range_split, which uses restore_reuse as one possible spill
40%% "mode"
41-export([analyse/3
42	,renamed_in_block/2
43	,split_in_block/2
44	]).
45-export_type([avail/0]).
46
47-compile(inline).
48
49%% -define(DO_ASSERT, 1).
50-include("../main/hipe.hrl").
51
52-type target_cfg()       :: any().
53-type liveness()         :: any().
54-type target_module()    :: module().
55-type target_context()   :: any().
56-type target()           :: {target_module(), target_context()}.
57-type label()            :: non_neg_integer().
58-type reg()              :: non_neg_integer().
59-type instr()            :: any().
60-type temp()             :: any().
61
62-spec split(target_cfg(), liveness(), target_module(), target_context())
63	   -> target_cfg().
64split(CFG, Liveness, TargetMod, TargetContext) ->
65  Target = {TargetMod, TargetContext},
66  Avail = analyse(CFG, Liveness, Target),
67  rewrite(CFG, Target, Avail).
68
69%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
70-opaque avail() :: #{label() => avail_bb()}.
71
72-record(avail_bb, {
73	  %% Blocks where HasCall is true are considered to have too high
74	  %% register pressure to support a register copy of a temp
75	  has_call :: boolean(),
76	  %% AvailOut: Temps that can be split (are available)
77	  out      :: availset(),
78	  %% Gen: AvailOut generated locally
79	  gen      :: availset(),
80	  %% WantIn: Temps that are split
81	  want     :: regset(),
82	  %% Self: Temps with avail-want pairs locally
83	  self     :: regset(),
84	  %% DefIn: Temps shadowed by later def in same live range part
85	  defin    :: regset(),
86	  pred     :: [label()],
87	  succ     :: [label()]
88	 }).
89-type avail_bb() :: #avail_bb{}.
90
91avail_get(L, Avail) -> maps:get(L, Avail).
92avail_set(L, Val, Avail) -> maps:put(L, Val, Avail).
93avail_has_call(L, Avail) -> (avail_get(L, Avail))#avail_bb.has_call.
94avail_out(L, Avail) -> (avail_get(L, Avail))#avail_bb.out.
95avail_self(L, Avail) -> (avail_get(L, Avail))#avail_bb.self.
96avail_pred(L, Avail) -> (avail_get(L, Avail))#avail_bb.pred.
97avail_succ(L, Avail) -> (avail_get(L, Avail))#avail_bb.succ.
98
99avail_in(L, Avail) ->
100  case avail_pred(L, Avail) of
101    [] -> availset_empty(); % entry
102    Pred ->
103      lists:foldl(fun(P, ASet) ->
104		      availset_intersect(avail_out(P, Avail), ASet)
105		  end, availset_top(), Pred)
106  end.
107
108want_in(L, Avail) -> (avail_get(L, Avail))#avail_bb.want.
109want_out(L, Avail) ->
110  lists:foldl(fun(S, Set) ->
111		  ordsets:union(want_in(S, Avail), Set)
112	      end, ordsets:new(), avail_succ(L, Avail)).
113
114def_in(L, Avail) -> (avail_get(L, Avail))#avail_bb.defin.
115def_out(L, Avail) ->
116  case avail_succ(L, Avail) of
117    [] -> ordsets:new(); % entry
118    Succ ->
119      ordsets:intersection([def_in(S, Avail) || S <- Succ])
120  end.
121
122-type regset()  :: ordsets:ordset(reg()).
123-type availset() :: top | regset().
124availset_empty() -> [].
125availset_top() -> top.
126availset_intersect(top, B) -> B;
127availset_intersect(A, top) -> A;
128availset_intersect(A, B) -> ordsets:intersection(A, B).
129availset_union(top, _) -> top;
130availset_union(_, top) -> top;
131availset_union(A, B) -> ordsets:union(A, B).
132ordset_intersect_availset(OS, top) -> OS;
133ordset_intersect_availset(OS, AS) -> ordsets:intersection(OS, AS).
134
135%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
136%% Analysis pass
137%%
138%% The analysis pass collects the set of temps we're interested in splitting
139%% (Spills), and computes three dataflow analyses for this subset of temps.
140%%
141%% Avail, which is the set of temps which are available in register from a
142%%   previous (potential) spill or restore without going through a HasCall
143%%   block.
144%% Want, which is a liveness analysis for the subset of temps used by an
145%%   instruction that are also in Avail at that point. In other words, Want is
146%%   the set of temps that are split (has a register copy) at a particular
147%%   point.
148%% Def, which are the temps that are already going to be spilled later, and so
149%%   need not be spilled when they're defined.
150%%
151%% Lastly, it computes the set Self for each block, which is the temps that have
152%% avail-want pairs in the same block, and so should be split in that block even
153%% if they're not in WantIn for the block.
154
155-spec analyse(target_cfg(), liveness(), target()) -> avail().
156analyse(CFG, Liveness, Target) ->
157  Avail0 = analyse_init(CFG, Liveness, Target),
158  RPO = reverse_postorder(CFG, Target),
159  AvailLs = [L || L <- RPO, not avail_has_call(L, Avail0)],
160  Avail1 = avail_dataf(AvailLs, Avail0),
161  Avail2 = analyse_filter_want(maps:keys(Avail1), Avail1),
162  PO = lists:reverse(RPO),
163  want_dataf(PO, Avail2).
164
165-spec analyse_init(target_cfg(), liveness(), target()) -> avail().
166analyse_init(CFG, Liveness, Target) ->
167  analyse_init(labels(CFG, Target), CFG, Liveness, Target, #{}, []).
168
169-spec analyse_init([label()], target_cfg(), liveness(), target(), spillset(),
170		   [{label(), avail_bb()}])
171		  -> avail().
172analyse_init([], _CFG, _Liveness, Target, Spills0, Acc) ->
173  %% Precoloured temps can't be spilled
174  Spills = spills_filter(fun(R) -> not is_precoloured(R, Target) end, Spills0),
175  analyse_init_1(Acc, Spills, []);
176analyse_init([L|Ls], CFG, Liveness, Target, Spills0, Acc) ->
177  {DefIn, Gen, Self, Want, HasCall0} =
178    analyse_scan(hipe_bb:code(bb(CFG, L, Target)), Target,
179		 ordsets:new(), ordsets:new(), ordsets:new(),
180		 ordsets:new()),
181  {Spills, Out, HasCall} =
182    case HasCall0 of
183      false -> {Spills0, availset_top(), false};
184      {true, CallDefs} ->
185	Spill = ordsets:subtract(liveout(Liveness, L, Target), CallDefs),
186	{spills_add_list(Spill, Spills0), Gen, true}
187    end,
188  Pred = hipe_gen_cfg:pred(CFG, L),
189  Succ = hipe_gen_cfg:succ(CFG, L),
190  Val = #avail_bb{gen=Gen, want=Want, self=Self, out=Out, has_call=HasCall,
191		  pred=Pred, succ=Succ, defin=DefIn},
192  analyse_init(Ls, CFG, Liveness, Target, Spills, [{L, Val} | Acc]).
193
194-spec analyse_init_1([{label(), avail_bb()}], spillset(),
195		     [{label(), avail_bb()}])
196		    -> avail().
197analyse_init_1([], _Spills, Acc) -> maps:from_list(Acc);
198analyse_init_1([{L, Val0}|Vs], Spills, Acc) ->
199  #avail_bb{out=Out,gen=Gen,want=Want,self=Self} = Val0,
200  Val = Val0#avail_bb{
201	  out  = spills_filter_availset(Out,  Spills),
202	  gen  = spills_filter_availset(Gen,  Spills),
203	  want = spills_filter_availset(Want, Spills),
204	  self = spills_filter_availset(Self, Spills)},
205  analyse_init_1(Vs, Spills, [{L, Val} | Acc]).
206
207-type spillset() :: #{reg() => []}.
208-spec spills_add_list([reg()], spillset()) -> spillset().
209spills_add_list([], Spills) -> Spills;
210spills_add_list([R|Rs], Spills) -> spills_add_list(Rs, Spills#{R => []}).
211
212-spec spills_filter_availset(availset(), spillset()) -> availset().
213spills_filter_availset([E|Es], Spills) ->
214  case Spills of
215    #{E := _} -> [E|spills_filter_availset(Es, Spills)];
216    #{} ->          spills_filter_availset(Es, Spills)
217  end;
218spills_filter_availset([], _) -> [];
219spills_filter_availset(top, _) -> top.
220
221spills_filter(Fun, Spills) -> maps:filter(fun(K, _) -> Fun(K) end, Spills).
222
223-spec analyse_scan([instr()], target(), Defset, Gen, Self, Want)
224		  -> {Defset, Gen, Self, Want, HasCall} when
225    HasCall :: false | {true, regset()},
226    Defset  :: regset(),
227    Gen     :: availset(),
228    Self    :: regset(),
229    Want    :: regset().
230analyse_scan([], _Target, Defs, Gen, Self, Want) ->
231  {Defs, Gen, Self, Want, false};
232analyse_scan([I|Is], Target, Defs0, Gen0, Self0, Want0) ->
233  {DefL, UseL} = reg_def_use(I, Target),
234  Use = ordsets:from_list(UseL),
235  Def = ordsets:from_list(DefL),
236  Self = ordsets:union(ordsets:intersection(Use, Gen0), Self0),
237  Want = ordsets:union(ordsets:subtract(Use, Defs0), Want0),
238  Defs = ordsets:union(Def, Defs0),
239  case defines_all_alloc(I, Target) of
240    true ->
241      [] = Is, %assertion
242      {Defs, ordsets:new(), Self, Want, {true, Def}};
243    false ->
244      Gen = ordsets:union(ordsets:union(Def, Use), Gen0),
245      analyse_scan(Is, Target, Defs, Gen, Self, Want)
246  end.
247
248-spec avail_dataf([label()], avail()) -> avail().
249avail_dataf(RPO, Avail0) ->
250  case avail_dataf_once(RPO, Avail0, 0) of
251    {Avail, 0} -> Avail;
252    {Avail, _Changed} ->
253      avail_dataf(RPO, Avail)
254  end.
255
256-spec avail_dataf_once([label()], avail(), non_neg_integer())
257		      -> {avail(), non_neg_integer()}.
258avail_dataf_once([], Avail, Changed) -> {Avail, Changed};
259avail_dataf_once([L|Ls], Avail0, Changed0) ->
260  ABB = #avail_bb{out=OldOut, gen=Gen} = avail_get(L, Avail0),
261  In = avail_in(L, Avail0),
262  {Changed, Avail} =
263    case availset_union(In, Gen) of
264      OldOut -> {Changed0, Avail0};
265      Out -> {Changed0+1, avail_set(L, ABB#avail_bb{out=Out}, Avail0)}
266    end,
267  avail_dataf_once(Ls, Avail, Changed).
268
269-spec analyse_filter_want([label()], avail()) -> avail().
270analyse_filter_want([], Avail) -> Avail;
271analyse_filter_want([L|Ls], Avail0) ->
272  ABB = #avail_bb{want=Want0, defin=DefIn0} = avail_get(L, Avail0),
273  In = avail_in(L, Avail0),
274  Want = ordset_intersect_availset(Want0, In),
275  DefIn = ordset_intersect_availset(DefIn0, In),
276  Avail = avail_set(L, ABB#avail_bb{want=Want, defin=DefIn}, Avail0),
277  analyse_filter_want(Ls, Avail).
278
279-spec want_dataf([label()], avail()) -> avail().
280want_dataf(PO, Avail0) ->
281  case want_dataf_once(PO, Avail0, 0) of
282    {Avail, 0} -> Avail;
283    {Avail, _Changed} ->
284      want_dataf(PO, Avail)
285  end.
286
287-spec want_dataf_once([label()], avail(), non_neg_integer())
288		     -> {avail(), non_neg_integer()}.
289want_dataf_once([], Avail, Changed) -> {Avail, Changed};
290want_dataf_once([L|Ls], Avail0, Changed0) ->
291  ABB0 = #avail_bb{want=OldIn,defin=OldDef} = avail_get(L, Avail0),
292  AvailIn = avail_in(L, Avail0),
293  Out = want_out(L, Avail0),
294  DefOut = def_out(L, Avail0),
295  {Changed, Avail} =
296    case {ordsets:union(ordset_intersect_availset(Out,    AvailIn), OldIn),
297	  ordsets:union(ordset_intersect_availset(DefOut, AvailIn), OldDef)}
298    of
299      {OldIn, OldDef} -> {Changed0, Avail0};
300      {In, DefIn} ->
301	ABB = ABB0#avail_bb{want=In,defin=DefIn},
302	{Changed0+1, avail_set(L, ABB, Avail0)}
303    end,
304  want_dataf_once(Ls, Avail, Changed).
305
306%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
307%% Rewrite pass
308-type subst_dict() :: orddict:orddict(reg(), reg()).
309-type input()      :: #{label() => subst_dict()}.
310
311-spec rewrite(target_cfg(), target(), avail()) -> target_cfg().
312rewrite(CFG, Target, Avail) ->
313  RPO = reverse_postorder(CFG, Target),
314  rewrite(RPO, Target, Avail, #{}, CFG).
315
316-spec rewrite([label()], target(), avail(), input(), target_cfg())
317	     -> target_cfg().
318rewrite([], _Target, _Avail, _Input, CFG) -> CFG;
319rewrite([L|Ls], Target, Avail, Input0, CFG0) ->
320  SplitHere = split_in_block(L, Avail),
321  {Input1, LInput} =
322    case Input0 of
323      #{L := LInput0} -> {Input0, LInput0};
324      #{} -> {Input0#{L => []}, []} % entry block
325    end,
326  ?ASSERT([] =:= [X || X <- SplitHere, orddict:is_key(X, LInput)]),
327  ?ASSERT(want_in(L, Avail) =:= orddict:fetch_keys(LInput)),
328  {CFG1, LOutput} =
329    case {SplitHere, LInput} of
330      {[], []} -> % optimisation (rewrite will do nothing, so skip it)
331	{CFG0, LInput};
332      _ ->
333	Code0 = hipe_bb:code(BB=bb(CFG0, L, Target)),
334	DefOut = def_out(L, Avail),
335	{Code, LOutput0, _DefIn} =
336	  rewrite_instrs(Code0, Target, LInput, DefOut, SplitHere),
337	{update_bb(CFG0, L, hipe_bb:code_update(BB, Code), Target), LOutput0}
338    end,
339  {Input, CFG} = rewrite_succs(avail_succ(L, Avail), Target, L, LOutput, Avail,
340			       Input1, CFG1),
341  rewrite(Ls, Target, Avail, Input, CFG).
342
343-spec renamed_in_block(label(), avail()) -> ordsets:ordset(reg()).
344renamed_in_block(L, Avail) ->
345  ordsets:union([avail_self(L, Avail), want_in(L, Avail),
346		 want_out(L, Avail)]).
347
348-spec split_in_block(label(), avail()) -> ordsets:ordset(reg()).
349split_in_block(L, Avail) ->
350  ordsets:subtract(ordsets:union(avail_self(L, Avail), want_out(L, Avail)),
351		   want_in(L, Avail)).
352
353-spec rewrite_instrs([instr()], target(), subst_dict(), regset(), [reg()])
354		    ->  {[instr()], subst_dict(), regset()}.
355rewrite_instrs([], _Target, Output, DefOut, []) ->
356  {[], Output, DefOut};
357rewrite_instrs([I|Is], Target, Input0, BBDefOut, SplitHere0) ->
358  {TDef, TUse} = def_use(I, Target),
359  {Def, Use} = {reg_names(TDef, Target), reg_names(TUse, Target)},
360  %% Restores are generated in forward order by picking temps from SplitHere as
361  %% they're used or defined. After the last instruction, all temps have been
362  %% picked.
363  {ISplits, SplitHere} =
364    lists:partition(fun(R) ->
365			lists:member(R, Def) orelse lists:member(R, Use)
366		    end, SplitHere0),
367  {Input, Restores} =
368    case ISplits of
369      [] -> {Input0, []};
370      _ ->
371	make_splits(ISplits, Target, TDef, TUse, Input0, [])
372    end,
373  %% Here's the recursive call
374  {Acc0, Output, DefOut} =
375    rewrite_instrs(Is, Target, Input, BBDefOut, SplitHere),
376  %% From here we're processing instructions in reverse order, because to avoid
377  %% redundant spills we need to walk the 'def' dataflow, which is in reverse.
378  SubstFun = fun(Temp) ->
379		 case orddict:find(reg_nr(Temp, Target), Input) of
380		   {ok, NewTemp} -> NewTemp;
381		   error -> Temp
382		 end
383	     end,
384  Acc1 = insert_spills(TDef, Target, Input, DefOut, Acc0),
385  Acc = Restores ++ [subst_temps(SubstFun, I, Target) | Acc1],
386  DefIn = ordsets:union(DefOut, ordsets:from_list(Def)),
387  {Acc, Output, DefIn}.
388
389-spec make_splits([reg()], target(), [temp()], [temp()], subst_dict(),
390		  [instr()])
391		 -> {subst_dict(), [instr()]}.
392make_splits([], _Target, _TDef, _TUse, Input, Acc) ->
393  {Input, Acc};
394make_splits([S|Ss], Target, TDef, TUse, Input0, Acc0) ->
395  SubstReg = new_reg_nr(Target),
396  {Acc, Subst} =
397    case find_reg_temp(S, TUse, Target) of
398      error ->
399	{ok, Temp} = find_reg_temp(S, TDef, Target),
400	{Acc0, update_reg_nr(SubstReg, Temp, Target)};
401      {ok, Temp} ->
402	Subst0 = update_reg_nr(SubstReg, Temp, Target),
403	Acc1 = [mk_move(Temp, Subst0, Target) | Acc0],
404	{Acc1, Subst0}
405    end,
406  Input = orddict:store(S, Subst, Input0),
407  make_splits(Ss, Target, TDef, TUse, Input, Acc).
408
409-spec find_reg_temp(reg(), [temp()], target()) -> error | {ok, temp()}.
410find_reg_temp(_Reg, [], _Target) -> error;
411find_reg_temp(Reg, [T|Ts], Target) ->
412  case reg_nr(T, Target) of
413    Reg -> {ok, T};
414    _ -> find_reg_temp(Reg, Ts, Target)
415  end.
416
417-spec insert_spills([temp()], target(), subst_dict(), regset(), [instr()])
418		   -> [instr()].
419insert_spills([], _Target, _Input, _DefOut, Acc) -> Acc;
420insert_spills([T|Ts], Target, Input, DefOut, Acc0) ->
421  R = reg_nr(T, Target),
422  Acc =
423    case orddict:find(R, Input) of
424      error -> Acc0;
425      {ok, Subst} ->
426	case lists:member(R, DefOut) of
427	  true -> Acc0;
428	  false -> [mk_move(Subst, T, Target) | Acc0]
429	end
430    end,
431  insert_spills(Ts, Target, Input, DefOut, Acc).
432
433-spec rewrite_succs([label()], target(), label(), subst_dict(), avail(),
434		    input(), target_cfg()) -> {input(), target_cfg()}.
435rewrite_succs([], _Target, _P, _POutput, _Avail, Input, CFG) -> {Input, CFG};
436rewrite_succs([L|Ls], Target, P, POutput, Avail, Input0, CFG0) ->
437  NewLInput = orddict_with_ordset(want_in(L, Avail), POutput),
438  {Input, CFG} =
439    case Input0 of
440      #{L := LInput} ->
441	CFG2 =
442	  case required_phi_moves(LInput, NewLInput) of
443	    [] -> CFG0;
444	    ReqMovs ->
445	      PhiLb = new_label(Target),
446	      Code = [mk_move(S,D,Target) || {S,D} <- ReqMovs]
447		++ [mk_goto(L, Target)],
448	      PhiBB = hipe_bb:mk_bb(Code),
449	      CFG1 = update_bb(CFG0, PhiLb, PhiBB, Target),
450	      bb_redirect_jmp(L, PhiLb, P, CFG1, Target)
451	  end,
452	{Input0, CFG2};
453      #{} ->
454	{Input0#{L => NewLInput}, CFG0}
455    end,
456  rewrite_succs(Ls, Target, P, POutput, Avail, Input, CFG).
457
458-spec bb_redirect_jmp(label(), label(), label(), target_cfg(), target())
459		     -> target_cfg().
460bb_redirect_jmp(From, To, Lb, CFG, Target) ->
461  BB0 = bb(CFG, Lb, Target),
462  Last = redirect_jmp(hipe_bb:last(BB0), From, To, Target),
463  BB = hipe_bb:code_update(BB0, hipe_bb:butlast(BB0) ++ [Last]),
464  update_bb(CFG, Lb, BB, Target).
465
466-spec required_phi_moves(subst_dict(), subst_dict()) -> [{reg(), reg()}].
467required_phi_moves([], []) -> [];
468required_phi_moves([P|Is], [P|Os]) -> required_phi_moves(Is, Os);
469required_phi_moves([{K, In}|Is], [{K, Out}|Os]) ->
470  [{Out, In}|required_phi_moves(Is, Os)].
471
472%% @doc Returns a new orddict with the keys in Set and their associated values.
473-spec orddict_with_ordset(ordsets:ordset(K), orddict:orddict(K, V))
474			 -> orddict:orddict(K, V).
475orddict_with_ordset([S|Ss], [{K, _}|_]=Dict) when S < K ->
476  orddict_with_ordset(Ss, Dict);
477orddict_with_ordset([S|_]=Set, [{K, _}|Ds]) when S > K ->
478  orddict_with_ordset(Set, Ds);
479orddict_with_ordset([_S|Ss], [{_K, _}=P|Ds]) -> % _S == _K
480  [P|orddict_with_ordset(Ss, Ds)];
481orddict_with_ordset([], _) -> [];
482orddict_with_ordset(_, []) -> [].
483
484%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
485%% Target module interface functions
486%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
487-define(TGT_IFACE_0(N), N(         {M,C}) -> M:N(         C)).
488-define(TGT_IFACE_1(N), N(A1,      {M,C}) -> M:N(A1,      C)).
489-define(TGT_IFACE_2(N), N(A1,A2,   {M,C}) -> M:N(A1,A2,   C)).
490-define(TGT_IFACE_3(N), N(A1,A2,A3,{M,C}) -> M:N(A1,A2,A3,C)).
491
492?TGT_IFACE_2(bb).
493?TGT_IFACE_1(def_use).
494?TGT_IFACE_1(defines_all_alloc).
495?TGT_IFACE_1(is_precoloured).
496?TGT_IFACE_1(labels).
497?TGT_IFACE_1(mk_goto).
498?TGT_IFACE_2(mk_move).
499?TGT_IFACE_0(new_label).
500?TGT_IFACE_0(new_reg_nr).
501?TGT_IFACE_3(redirect_jmp).
502?TGT_IFACE_1(reg_nr).
503?TGT_IFACE_1(reverse_postorder).
504?TGT_IFACE_2(subst_temps).
505?TGT_IFACE_3(update_bb).
506?TGT_IFACE_2(update_reg_nr).
507
508liveout(Liveness, L, Target={TgtMod,TgtCtx}) ->
509  ordsets:from_list(reg_names(TgtMod:liveout(Liveness, L, TgtCtx), Target)).
510
511reg_names(Regs, {TgtMod,TgtCtx}) ->
512  [TgtMod:reg_nr(X,TgtCtx) || X <- Regs].
513
514reg_def_use(I, Target) ->
515  {TDef, TUse} = def_use(I, Target),
516  {reg_names(TDef, Target), reg_names(TUse, Target)}.
517