%%% -*- erlang-indent-level: 2 -*-
%%%
%%% %CopyrightBegin%
%%%
%%% Copyright Ericsson AB 2006-2009. All Rights Reserved.
%%%
%%% The contents of this file are subject to the Erlang Public License,
%%% Version 1.1, (the "License"); you may not use this file except in
%%% compliance with the License. You should have received a copy of the
%%% Erlang Public License along with this software. If not, it can be
%%% retrieved online at http://www.erlang.org/.
%%%
%%% Software distributed under the License is distributed on an "AS IS"
%%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%%% the License for the specific language governing rights and limitations
%%% under the License.
%%%
%%% %CopyrightEnd%
%%%
%%%-------------------------------------------------------------------
%%% File : hipe_icode_bincomp.erl
%%% Author : Per Gustafsson <[email protected]>
%%% Description :
%%%
%%% Created : 12 Sep 2005 by Per Gustafsson <[email protected]>
%%%-------------------------------------------------------------------
-module(hipe_icode_bincomp).
-export([cfg/1]).
%%--------------------------------------------------------------------
-include("hipe_icode.hrl").
-include("../flow/cfg.hrl").
%%--------------------------------------------------------------------
-spec cfg(cfg()) -> cfg().
cfg(Cfg1) ->
StartLbls = ordsets:from_list([hipe_icode_cfg:start_label(Cfg1)]),
find_bs_get_integer(StartLbls, Cfg1, StartLbls).
find_bs_get_integer([Lbl|Rest], Cfg, Visited) ->
BB = hipe_icode_cfg:bb(Cfg, Lbl),
Last = hipe_bb:last(BB),
NewCfg =
case ok(Last, Cfg) of
{ok,{Type, FakeFail, RealFail, SuccLbl, MsIn, MsOut}} ->
{Cont, Info, OldLbl, LastMsOut} =
collect_info(SuccLbl, Cfg, [Type], Lbl, RealFail, MsOut),
update_code(Lbl, OldLbl, Cfg, Info, Cont, FakeFail, MsIn, LastMsOut);
not_ok ->
Cfg
end,
Succs = ordsets:from_list(hipe_icode_cfg:succ(NewCfg, Lbl)),
NewSuccs = ordsets:subtract(Succs, Visited),
NewLbls = ordsets:union(NewSuccs, Rest),
NewVisited = ordsets:union(NewSuccs, Visited),
find_bs_get_integer(NewLbls, NewCfg, NewVisited);
find_bs_get_integer([], Cfg, _) ->
Cfg.
ok(I, Cfg) ->
case hipe_icode:is_call(I) of
true ->
case hipe_icode:call_fun(I) of
{hipe_bs_primop, {bs_get_integer, Size, Flags}} when (Flags band 6) =:= 0 ->
case {hipe_icode:call_dstlist(I), hipe_icode:call_args(I)} of
{[Dst, MsOut] = DstList, [MsIn]} ->
Cont = hipe_icode:call_continuation(I),
FirstFail = hipe_icode:call_fail_label(I),
FirstFailBB = hipe_icode_cfg:bb(Cfg, FirstFail),
case check_for_restore_block(FirstFailBB, DstList) of
{restore_block, RealFail} ->
{ok, {{Dst, Size}, FirstFail, RealFail, Cont, MsIn, MsOut}};
not_restore_block ->
not_ok
end;
_ ->
not_ok
end;
_ ->
not_ok
end;
false ->
not_ok
end.
check_for_restore_block(FirstFailBB, DefVars) ->
Moves = hipe_bb:butlast(FirstFailBB),
case [Instr || Instr <- Moves, is_badinstr(Instr, DefVars)] of
[] ->
Last = hipe_bb:last(FirstFailBB),
case hipe_icode:is_goto(Last) of
true ->
{restore_block, hipe_icode:goto_label(Last)};
false ->
not_restore_block
end;
[_|_] ->
not_restore_block
end.
is_badinstr(Instr, DefVars) ->
not(hipe_icode:is_move(Instr) andalso
lists:member(hipe_icode:move_dst(Instr), DefVars)).
collect_info(Lbl, Cfg, Acc, OldLbl, FailLbl, MsOut) ->
case do_collect_info(Lbl, Cfg, Acc, FailLbl, MsOut) of
done ->
{Lbl, Acc, OldLbl, MsOut};
{cont, NewAcc, NewLbl, NewMsOut} ->
collect_info(NewLbl, Cfg, NewAcc, Lbl, FailLbl, NewMsOut)
end.
do_collect_info(Lbl, Cfg, Acc, FailLbl, MsOut) ->
BB = hipe_icode_cfg:bb(Cfg,Lbl),
case hipe_bb:code(BB) of
[I] ->
case hipe_icode_cfg:pred(Cfg,Lbl) of
[_] ->
case ok(I, Cfg) of
{ok, {Type,_FakeFail,FailLbl,SuccLbl,MsOut,NewMsOut}} ->
NewAcc = [Type|Acc],
MaxSize = hipe_rtl_arch:word_size() * 8 - 5,
case calc_size(NewAcc) of
Size when Size =< MaxSize ->
{cont,NewAcc,SuccLbl,NewMsOut};
_ ->
done
end;
_ ->
done
end;
_ ->
done
end;
_ ->
done
end.
calc_size([{_,Size}|Rest]) when is_integer(Size) ->
Size + calc_size(Rest);
calc_size([]) -> 0.
update_code(_Lbl, _, Cfg, [_Info], _Cont, _LastFail, _MsIn, _MsOut) ->
Cfg;
update_code(Lbl, OldLbl, Cfg, Info, Cont, LastFail, MsIn, MsOut) ->
BB = hipe_icode_cfg:bb(Cfg, Lbl),
ButLast = hipe_bb:butlast(BB),
NewVar = hipe_icode:mk_new_var(),
Size = calc_size(Info),
NewLast =
hipe_icode:mk_primop([NewVar,MsOut],
{hipe_bs_primop, {bs_get_integer,Size,0}},
[MsIn],
OldLbl,
LastFail),
NewBB = hipe_bb:mk_bb(ButLast++[NewLast]),
NewCfg = hipe_icode_cfg:bb_add(Cfg, Lbl, NewBB),
fix_rest(Info, NewVar, OldLbl, Cont, NewCfg).
fix_rest(Info, Var, Lbl, Cont, Cfg) ->
ButLast = make_butlast(Info, Var),
Last = hipe_icode:mk_goto(Cont),
NewBB = hipe_bb:mk_bb(ButLast++[Last]),
hipe_icode_cfg:bb_add(Cfg, Lbl, NewBB).
make_butlast([{Res,_Size}], Var) ->
[hipe_icode:mk_move(Res, Var)];
make_butlast([{Res, Size}|Rest], Var) ->
NewVar = hipe_icode:mk_new_var(),
[hipe_icode:mk_primop([Res], 'band',
[Var, hipe_icode:mk_const((1 bsl Size)-1)]),
hipe_icode:mk_primop([NewVar], 'bsr', [Var, hipe_icode:mk_const(Size)])
|make_butlast(Rest, NewVar)].