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