%% -*- erlang-indent-level: 2 -*-
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
-module(hipe_ppc_ra_postconditions).
-export([check_and_rewrite/3, check_and_rewrite2/3]).
-include("hipe_ppc.hrl").
check_and_rewrite(CFG, Coloring, Allocator) ->
TempMap = hipe_temp_map:cols2tuple(Coloring, hipe_ppc_specific, no_context),
check_and_rewrite2(CFG, TempMap, Allocator).
check_and_rewrite2(CFG, TempMap, Allocator) ->
Strategy = strategy(Allocator),
do_bbs(hipe_ppc_cfg:labels(CFG), TempMap, Strategy, CFG, false).
strategy(Allocator) ->
case Allocator of
'normal' -> 'new';
'linearscan' -> 'fixed';
'naive' -> 'fixed'
end.
do_bbs([], _, _, CFG, DidSpill) -> {CFG, DidSpill};
do_bbs([Lbl|Lbls], TempMap, Strategy, CFG0, DidSpill0) ->
Code0 = hipe_bb:code(BB = hipe_ppc_cfg:bb(CFG0, Lbl)),
{Code, DidSpill} = do_insns(Code0, TempMap, Strategy, [], DidSpill0),
CFG = hipe_ppc_cfg:bb_add(CFG0, Lbl, hipe_bb:code_update(BB, Code)),
do_bbs(Lbls, TempMap, Strategy, CFG, DidSpill).
do_insns([I|Insns], TempMap, Strategy, Accum, DidSpill0) ->
{NewIs, DidSpill1} = do_insn(I, TempMap, Strategy),
do_insns(Insns, TempMap, Strategy, lists:reverse(NewIs, Accum), DidSpill0 or DidSpill1);
do_insns([], _TempMap, _Strategy, Accum, DidSpill) ->
{lists:reverse(Accum), DidSpill}.
do_insn(I, TempMap, Strategy) ->
case I of
#alu{} -> do_alu(I, TempMap, Strategy);
#cmp{} -> do_cmp(I, TempMap, Strategy);
#load{} -> do_load(I, TempMap, Strategy);
#loadx{} -> do_loadx(I, TempMap, Strategy);
#mfspr{} -> do_mfspr(I, TempMap, Strategy);
#mtcr{} -> do_mtcr(I, TempMap, Strategy);
#mtspr{} -> do_mtspr(I, TempMap, Strategy);
#pseudo_li{} -> do_pseudo_li(I, TempMap, Strategy);
#pseudo_move{} -> do_pseudo_move(I, TempMap, Strategy);
#store{} -> do_store(I, TempMap, Strategy);
#storex{} -> do_storex(I, TempMap, Strategy);
#unary{} -> do_unary(I, TempMap, Strategy);
#lfd{} -> do_lfd(I, TempMap, Strategy);
#lfdx{} -> do_lfdx(I, TempMap, Strategy);
#stfd{} -> do_stfd(I, TempMap, Strategy);
#stfdx{} -> do_stfdx(I, TempMap, Strategy);
_ -> {[I], false}
end.
%%% Fix relevant instruction types.
do_alu(I=#alu{dst=Dst,src1=Src1,src2=Src2}, TempMap, Strategy) ->
{FixDst,NewDst,DidSpill1} = fix_dst(Dst, TempMap, Strategy),
{FixSrc1,NewSrc1,DidSpill2} = fix_src1(Src1, TempMap, Strategy),
{FixSrc2,NewSrc2,DidSpill3} = fix_src2_or_imm(Src2, TempMap, Strategy),
NewI = I#alu{dst=NewDst,src1=NewSrc1,src2=NewSrc2},
{FixSrc1 ++ FixSrc2 ++ [NewI | FixDst], DidSpill1 or DidSpill2 or DidSpill3}.
do_cmp(I=#cmp{src1=Src1,src2=Src2}, TempMap, Strategy) ->
{FixSrc1,NewSrc1,DidSpill1} = fix_src1(Src1, TempMap, Strategy),
{FixSrc2,NewSrc2,DidSpill2} = fix_src2_or_imm(Src2, TempMap, Strategy),
NewI = I#cmp{src1=NewSrc1,src2=NewSrc2},
{FixSrc1 ++ FixSrc2 ++ [NewI], DidSpill1 or DidSpill2}.
do_load(I=#load{dst=Dst,base=Base}, TempMap, Strategy) ->
{FixDst,NewDst,DidSpill1} = fix_dst(Dst, TempMap, Strategy),
{FixBase,NewBase,DidSpill2} = fix_src1(Base, TempMap, Strategy),
NewI = I#load{dst=NewDst,base=NewBase},
{FixBase ++ [NewI | FixDst], DidSpill1 or DidSpill2}.
do_loadx(I=#loadx{dst=Dst,base1=Base1,base2=Base2}, TempMap, Strategy) ->
{FixDst,NewDst,DidSpill1} = fix_dst(Dst, TempMap, Strategy),
{FixBase1,NewBase1,DidSpill2} = fix_src1(Base1, TempMap, Strategy),
{FixBase2,NewBase2,DidSpill3} = fix_src2(Base2, TempMap, Strategy),
NewI = I#loadx{dst=NewDst,base1=NewBase1,base2=NewBase2},
{FixBase1 ++ FixBase2 ++ [NewI | FixDst], DidSpill1 or DidSpill2 or DidSpill3}.
do_mfspr(I=#mfspr{dst=Dst}, TempMap, Strategy) ->
{FixDst,NewDst,DidSpill} = fix_dst(Dst, TempMap, Strategy),
NewI = I#mfspr{dst=NewDst},
{[NewI | FixDst], DidSpill}.
do_mtcr(I=#mtcr{src=Src}, TempMap, Strategy) ->
{FixSrc,NewSrc,DidSpill} = fix_src1(Src, TempMap, Strategy),
NewI = I#mtcr{src=NewSrc},
{FixSrc ++ [NewI], DidSpill}.
do_mtspr(I=#mtspr{src=Src}, TempMap, Strategy) ->
{FixSrc,NewSrc,DidSpill} = fix_src1(Src, TempMap, Strategy),
NewI = I#mtspr{src=NewSrc},
{FixSrc ++ [NewI], DidSpill}.
do_pseudo_li(I=#pseudo_li{dst=Dst}, TempMap, Strategy) ->
{FixDst,NewDst,DidSpill} = fix_dst(Dst, TempMap, Strategy),
NewI = I#pseudo_li{dst=NewDst},
{[NewI | FixDst], DidSpill}.
do_pseudo_move(I=#pseudo_move{dst=Dst,src=Src}, TempMap, Strategy) ->
%% Either Dst or Src (but not both) may be a pseudo temp.
%% pseudo_move and pseudo_tailcall are special cases: in
%% all other instructions, all temps must be non-pseudos
%% after register allocation.
case temp_is_spilled(Dst, TempMap) of
true -> % Src must not be a pseudo
{FixSrc,NewSrc,DidSpill} = fix_src1(Src, TempMap, Strategy),
NewI = I#pseudo_move{src=NewSrc},
{FixSrc ++ [NewI], DidSpill};
_ ->
{[I], false}
end.
do_store(I=#store{src=Src,base=Base}, TempMap, Strategy) ->
{FixSrc,NewSrc,DidSpill1} = fix_src1(Src, TempMap, Strategy),
{FixBase,NewBase,DidSpill2} = fix_src2(Base, TempMap, Strategy),
NewI = I#store{src=NewSrc,base=NewBase},
{FixSrc ++ FixBase ++ [NewI], DidSpill1 or DidSpill2}.
do_storex(I=#storex{src=Src,base1=Base1,base2=Base2}, TempMap, Strategy) ->
{FixSrc,NewSrc,DidSpill1} = fix_src1(Src, TempMap, Strategy),
{FixBase1,NewBase1,DidSpill2} = fix_src2(Base1, TempMap, Strategy),
{FixBase2,NewBase2,DidSpill3} = fix_src3(Base2, TempMap, Strategy),
NewI = I#storex{src=NewSrc,base1=NewBase1,base2=NewBase2},
{FixSrc ++ FixBase1 ++ FixBase2 ++ [NewI], DidSpill1 or DidSpill2 or DidSpill3}.
do_unary(I=#unary{dst=Dst,src=Src}, TempMap, Strategy) ->
{FixDst,NewDst,DidSpill1} = fix_dst(Dst, TempMap, Strategy),
{FixSrc,NewSrc,DidSpill2} = fix_src1(Src, TempMap, Strategy),
NewI = I#unary{dst=NewDst,src=NewSrc},
{FixSrc ++ [NewI | FixDst], DidSpill1 or DidSpill2}.
do_lfd(I=#lfd{base=Base}, TempMap, Strategy) ->
{FixBase,NewBase,DidSpill} = fix_src1(Base, TempMap, Strategy),
NewI = I#lfd{base=NewBase},
{FixBase ++ [NewI], DidSpill}.
do_lfdx(I=#lfdx{base1=Base1,base2=Base2}, TempMap, Strategy) ->
{FixBase1,NewBase1,DidSpill1} = fix_src1(Base1, TempMap, Strategy),
{FixBase2,NewBase2,DidSpill2} = fix_src2(Base2, TempMap, Strategy),
NewI = I#lfdx{base1=NewBase1,base2=NewBase2},
{FixBase1 ++ FixBase2 ++ [NewI], DidSpill1 or DidSpill2}.
do_stfd(I=#stfd{base=Base}, TempMap, Strategy) ->
{FixBase,NewBase,DidSpill} = fix_src1(Base, TempMap, Strategy),
NewI = I#stfd{base=NewBase},
{FixBase ++ [NewI], DidSpill}.
do_stfdx(I=#stfdx{base1=Base1,base2=Base2}, TempMap, Strategy) ->
{FixBase1,NewBase1,DidSpill1} = fix_src1(Base1, TempMap, Strategy),
{FixBase2,NewBase2,DidSpill2} = fix_src2(Base2, TempMap, Strategy),
NewI = I#stfdx{base1=NewBase1,base2=NewBase2},
{FixBase1 ++ FixBase2 ++ [NewI], DidSpill1 or DidSpill2}.
%%% Fix Dst and Src operands.
fix_src2_or_imm(Src2, TempMap, Strategy) ->
case Src2 of
#ppc_temp{} -> fix_src2(Src2, TempMap, Strategy);
_ -> {[], Src2, false}
end.
fix_src1(Src, TempMap, Strategy) ->
fix_src(Src, TempMap, temp1(Strategy)).
temp1('new') -> [];
temp1('fixed') -> hipe_ppc_registers:temp1().
fix_src2(Src, TempMap, Strategy) ->
fix_src(Src, TempMap, temp2(Strategy)).
temp2('new') -> [];
temp2('fixed') -> hipe_ppc_registers:temp2().
fix_src3(Src, TempMap, Strategy) -> % storex :-(
fix_src(Src, TempMap, temp3(Strategy)).
temp3('new') -> [];
temp3('fixed') -> hipe_ppc_registers:temp3().
fix_src(Src, TempMap, RegOpt) ->
case temp_is_spilled(Src, TempMap) of
true ->
NewSrc = clone(Src, RegOpt),
{[hipe_ppc:mk_pseudo_move(NewSrc, Src)],
NewSrc,
true};
_ ->
{[], Src, false}
end.
fix_dst(Dst, TempMap, Strategy) ->
case temp_is_spilled(Dst, TempMap) of
true ->
NewDst = clone(Dst, temp3(Strategy)),
{[hipe_ppc:mk_pseudo_move(Dst, NewDst)],
NewDst,
true};
_ ->
{[], Dst, false}
end.
%%% Check if an operand is a pseudo-temp.
temp_is_spilled(Temp, []) -> % special case for naive regalloc
not(hipe_ppc:temp_is_precoloured(Temp));
temp_is_spilled(Temp, TempMap) ->
case hipe_ppc:temp_is_allocatable(Temp) of
true ->
Reg = hipe_ppc:temp_reg(Temp),
tuple_size(TempMap) > Reg andalso hipe_temp_map:is_spilled(Reg, TempMap);
false -> true
end.
%%% Make a certain reg into a clone of Temp.
clone(Temp, RegOpt) ->
Type = hipe_ppc:temp_type(Temp),
case RegOpt of
[] -> hipe_ppc:mk_new_temp(Type);
Reg -> hipe_ppc:mk_temp(Reg, Type)
end.