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_ra_postconditions_fp).
16-export([check_and_rewrite/2]).
17-include("hipe_ppc.hrl").
18
19check_and_rewrite(CFG, Coloring) ->
20  TempMap = hipe_temp_map:cols2tuple(Coloring, hipe_ppc_specific_fp, no_context),
21  do_bbs(hipe_ppc_cfg:labels(CFG), TempMap, CFG, false).
22
23do_bbs([], _TempMap, CFG, DidSpill) -> {CFG, DidSpill};
24do_bbs([Lbl|Lbls], TempMap, CFG0, DidSpill0) ->
25  Code0 = hipe_bb:code(BB = hipe_ppc_cfg:bb(CFG0, Lbl)),
26  {Code, DidSpill} = do_insns(Code0, TempMap, [], DidSpill0),
27  CFG = hipe_ppc_cfg:bb_add(CFG0, Lbl, hipe_bb:code_update(BB, Code)),
28  do_bbs(Lbls, TempMap, CFG, DidSpill).
29
30do_insns([I|Insns], TempMap, Accum, DidSpill0) ->
31  {NewIs, DidSpill1} = do_insn(I, TempMap),
32  do_insns(Insns, TempMap, lists:reverse(NewIs, Accum), DidSpill0 or DidSpill1);
33do_insns([], _TempMap, Accum, DidSpill) ->
34  {lists:reverse(Accum), DidSpill}.
35
36do_insn(I, TempMap) ->
37  case I of
38    #lfd{} -> do_lfd(I, TempMap);
39    #lfdx{} -> do_lfdx(I, TempMap);
40    #stfd{} -> do_stfd(I, TempMap);
41    #stfdx{} -> do_stfdx(I, TempMap);
42    #fp_binary{} -> do_fp_binary(I, TempMap);
43    #fp_unary{} -> do_fp_unary(I, TempMap);
44    #pseudo_fmove{} -> do_pseudo_fmove(I, TempMap);
45    #pseudo_spill_fmove{} -> do_pseudo_spill_fmove(I, TempMap);
46    _ -> {[I], false}
47  end.
48
49%%% Fix relevant instruction types.
50
51do_lfd(I=#lfd{dst=Dst}, TempMap) ->
52  {FixDst, NewDst, DidSpill} = fix_dst(Dst, TempMap),
53  NewI = I#lfd{dst=NewDst},
54  {[NewI | FixDst], DidSpill}.
55
56do_lfdx(I=#lfdx{dst=Dst}, TempMap) ->
57  {FixDst, NewDst, DidSpill} = fix_dst(Dst, TempMap),
58  NewI = I#lfdx{dst=NewDst},
59  {[NewI | FixDst], DidSpill}.
60
61do_stfd(I=#stfd{src=Src}, TempMap) ->
62  {FixSrc, NewSrc, DidSpill} = fix_src(Src, TempMap),
63  NewI = I#stfd{src=NewSrc},
64  {FixSrc ++ [NewI], DidSpill}.
65
66do_stfdx(I=#stfdx{src=Src}, TempMap) ->
67  {FixSrc, NewSrc, DidSpill} = fix_src(Src, TempMap),
68  NewI = I#stfdx{src=NewSrc},
69  {FixSrc ++ [NewI], DidSpill}.
70
71do_fp_binary(I=#fp_binary{dst=Dst,src1=Src1,src2=Src2}, TempMap) ->
72  {FixDst,NewDst,DidSpill1} = fix_dst(Dst, TempMap),
73  {FixSrc1,NewSrc1,DidSpill2} = fix_src(Src1, TempMap),
74  {FixSrc2,NewSrc2,DidSpill3} = fix_src(Src2, TempMap),
75  NewI = I#fp_binary{dst=NewDst,src1=NewSrc1,src2=NewSrc2},
76  {FixSrc1 ++ FixSrc2 ++ [NewI | FixDst], DidSpill1 or DidSpill2 or DidSpill3}.
77
78do_fp_unary(I=#fp_unary{dst=Dst,src=Src}, TempMap) ->
79  {FixDst,NewDst,DidSpill1} = fix_dst(Dst, TempMap),
80  {FixSrc,NewSrc,DidSpill2} = fix_src(Src, TempMap),
81  NewI = I#fp_unary{dst=NewDst,src=NewSrc},
82  {FixSrc ++ [NewI | FixDst], DidSpill1 or DidSpill2}.
83
84do_pseudo_fmove(I=#pseudo_fmove{dst=Dst,src=Src}, TempMap) ->
85  case temp_is_spilled(Src, TempMap)
86    andalso temp_is_spilled(Dst, TempMap)
87  of
88    true -> % Turn into pseudo_spill_fmove
89      Temp = clone(Src),
90      NewI = #pseudo_spill_fmove{dst=Dst,temp=Temp,src=Src},
91      {[NewI], true};
92    _ ->
93      {[I], false}
94  end.
95
96do_pseudo_spill_fmove(I=#pseudo_spill_fmove{temp=Temp}, TempMap) ->
97  %% Temp is above the low water mark and must not have been spilled
98  false = temp_is_spilled(Temp, TempMap),
99  {[I], false}.
100
101%%% Fix Dst and Src operands.
102
103fix_src(Src, TempMap) ->
104  case temp_is_spilled(Src, TempMap) of
105    true ->
106      NewSrc = clone(Src),
107      {[hipe_ppc:mk_pseudo_fmove(NewSrc, Src)], NewSrc, true};
108    _ ->
109      {[], Src, false}
110  end.
111
112fix_dst(Dst, TempMap) ->
113  case temp_is_spilled(Dst, TempMap) of
114    true ->
115      NewDst = clone(Dst),
116      {[hipe_ppc:mk_pseudo_fmove(Dst, NewDst)], NewDst, true};
117    _ ->
118      {[], Dst, false}
119  end.
120
121%%% Check if an operand is a pseudo-temp.
122
123temp_is_spilled(Temp, TempMap) ->
124  case hipe_ppc:temp_is_allocatable(Temp) of
125    true ->
126      Reg = hipe_ppc:temp_reg(Temp),
127      tuple_size(TempMap) > Reg andalso hipe_temp_map:is_spilled(Reg, TempMap);
128    false -> true
129  end.
130
131%%% Create a new temp with the same type as an old one.
132
133clone(Temp) ->
134  Type = hipe_ppc:temp_type(Temp),	% XXX: always double?
135  hipe_ppc:mk_new_temp(Type).
136