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