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%%% File    : hipe_icode_bincomp.erl
17%%% Author  : Per Gustafsson <pergu@it.uu.se>
18%%% Description :
19%%%
20%%% Created : 12 Sep 2005 by Per Gustafsson <pergu@it.uu.se>
21%%%-------------------------------------------------------------------
22
23-module(hipe_icode_bincomp).
24
25-export([cfg/1]).
26
27%%--------------------------------------------------------------------
28
29-include("hipe_icode.hrl").
30-include("../flow/cfg.hrl").
31
32%%--------------------------------------------------------------------
33
34-spec cfg(cfg()) -> cfg().
35
36cfg(Cfg1) ->
37  StartLbl = hipe_icode_cfg:start_label(Cfg1),
38  find_bs_get_integer([StartLbl], Cfg1, set_from_list([StartLbl])).
39
40find_bs_get_integer([Lbl|Rest], Cfg, Visited) ->
41  BB = hipe_icode_cfg:bb(Cfg, Lbl),
42  Last = hipe_bb:last(BB),
43  NewCfg =
44     case ok(Last, Cfg) of
45       {ok,{Type, FakeFail, RealFail, SuccLbl, MsIn, MsOut}} ->
46	 {Cont, Info, OldLbl, LastMsOut} =
47	   collect_info(SuccLbl, Cfg, [Type], Lbl, RealFail, MsOut),
48	 update_code(Lbl, OldLbl, Cfg, Info, Cont, FakeFail, MsIn, LastMsOut);
49       not_ok ->
50	 Cfg
51     end,
52  Succs = hipe_icode_cfg:succ(NewCfg, Lbl),
53  NewSuccs = not_visited(Succs, Visited),
54  NewLbls = NewSuccs ++ Rest,
55  NewVisited = set_union(set_from_list(NewSuccs), Visited),
56  find_bs_get_integer(NewLbls, NewCfg, NewVisited);
57find_bs_get_integer([], Cfg, _) ->
58  Cfg.
59
60ok(I, Cfg) ->
61  case hipe_icode:is_call(I) of
62    true ->
63      case hipe_icode:call_fun(I) of
64	{hipe_bs_primop, {bs_get_integer, Size, Flags}} when (Flags band 6) =:= 0 ->
65	  case {hipe_icode:call_dstlist(I), hipe_icode:call_args(I)} of
66	    {[Dst, MsOut] = DstList, [MsIn]} ->
67	      Cont = hipe_icode:call_continuation(I),
68	      FirstFail = hipe_icode:call_fail_label(I),
69	      FirstFailBB = hipe_icode_cfg:bb(Cfg, FirstFail),
70	      case check_for_restore_block(FirstFailBB, DstList) of
71		{restore_block, RealFail} ->
72		  {ok, {{Dst, Size}, FirstFail, RealFail, Cont, MsIn, MsOut}};
73		not_restore_block ->
74		  not_ok
75	      end;
76	    _ ->
77	      not_ok
78	  end;
79	_ ->
80	  not_ok
81      end;
82    false ->
83      not_ok
84  end.
85
86check_for_restore_block(FirstFailBB, DefVars) ->
87  Moves = hipe_bb:butlast(FirstFailBB),
88  case [Instr || Instr <- Moves, is_badinstr(Instr, DefVars)] of
89    [] ->
90      Last = hipe_bb:last(FirstFailBB),
91      case hipe_icode:is_goto(Last) of
92	true ->
93	  {restore_block, hipe_icode:goto_label(Last)};
94	false ->
95	  not_restore_block
96      end;
97    [_|_] ->
98      not_restore_block
99  end.
100
101is_badinstr(Instr, DefVars) ->
102  not(hipe_icode:is_move(Instr) andalso
103      lists:member(hipe_icode:move_dst(Instr), DefVars)).
104
105collect_info(Lbl, Cfg, Acc, OldLbl, FailLbl, MsOut) ->
106  case do_collect_info(Lbl, Cfg, Acc, FailLbl, MsOut) of
107    done ->
108      {Lbl, Acc, OldLbl, MsOut};
109    {cont, NewAcc, NewLbl, NewMsOut} ->
110      collect_info(NewLbl, Cfg, NewAcc, Lbl, FailLbl, NewMsOut)
111  end.
112
113do_collect_info(Lbl, Cfg, Acc, FailLbl, MsOut) ->
114  BB = hipe_icode_cfg:bb(Cfg,Lbl),
115  case hipe_bb:code(BB) of
116    [I] ->
117      case hipe_icode_cfg:pred(Cfg,Lbl) of
118	[_] ->
119	  case ok(I, Cfg) of
120	   {ok, {Type,_FakeFail,FailLbl,SuccLbl,MsOut,NewMsOut}} ->
121	      NewAcc = [Type|Acc],
122	      MaxSize = hipe_rtl_arch:word_size() * 8 - 5,
123	      case calc_size(NewAcc) of
124		Size when Size =< MaxSize ->
125		  {cont,NewAcc,SuccLbl,NewMsOut};
126	        _ ->
127		  done
128	      end;
129	    _ ->
130	      done
131	  end;
132	_ ->
133	  done
134      end;
135    _ ->
136      done
137  end.
138
139calc_size([{_,Size}|Rest]) when is_integer(Size) ->
140  Size + calc_size(Rest);
141calc_size([]) -> 0.
142
143update_code(_Lbl, _, Cfg, [_Info], _Cont, _LastFail, _MsIn, _MsOut) ->
144  Cfg;
145update_code(Lbl, OldLbl, Cfg, Info, Cont, LastFail, MsIn, MsOut) ->
146  BB = hipe_icode_cfg:bb(Cfg, Lbl),
147  ButLast = hipe_bb:butlast(BB),
148  NewVar = hipe_icode:mk_new_var(),
149  Size = calc_size(Info),
150  NewLast =
151    hipe_icode:mk_primop([NewVar,MsOut],
152			 {hipe_bs_primop, {bs_get_integer,Size,0}},
153			 [MsIn],
154			 OldLbl,
155			 LastFail),
156  NewBB = hipe_bb:mk_bb(ButLast++[NewLast]),
157  NewCfg = hipe_icode_cfg:bb_add(Cfg, Lbl, NewBB),
158  fix_rest(Info, NewVar, OldLbl, Cont, NewCfg).
159
160fix_rest(Info, Var, Lbl, Cont, Cfg) ->
161  ButLast = make_butlast(Info, Var),
162  Last = hipe_icode:mk_goto(Cont),
163  NewBB = hipe_bb:mk_bb(ButLast++[Last]),
164  hipe_icode_cfg:bb_add(Cfg, Lbl, NewBB).
165
166make_butlast([{Res,_Size}], Var) ->
167  [hipe_icode:mk_move(Res, Var)];
168make_butlast([{Res, Size}|Rest], Var) ->
169  NewVar = hipe_icode:mk_new_var(),
170  [hipe_icode:mk_primop([Res], 'band',
171			[Var, hipe_icode:mk_const((1 bsl Size)-1)]),
172   hipe_icode:mk_primop([NewVar], 'bsr', [Var, hipe_icode:mk_const(Size)])
173   |make_butlast(Rest, NewVar)].
174
175%%--------------------------------------------------------------------
176%% Sets
177
178set_from_list([]) -> #{};
179set_from_list(L) ->
180  maps:from_list([{E, []} || E <- L]).
181
182not_visited([], _) -> [];
183not_visited([E|T], M) ->
184  case M of
185    #{E := _} -> not_visited(T, M);
186    _ -> [E|not_visited(T, M)]
187  end.
188
189set_union(A, B) -> maps:merge(A, B).
190