%% -*- 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.
%%
%%--------------------------------------------------------------------
%% File : hipe_icode_inline_bifs.erl
%% Author : Per Gustafsson <[email protected]>
%% Purpose : Inlines BIFs which can be expressed easily in ICode.
%% This allows for optimizations in later ICode passes
%% and makes the code faster.
%%
%% Created : 14 May 2007 by Per Gustafsson <[email protected]>
%%--------------------------------------------------------------------
%% Currently inlined BIFs:
%% and, or, xor, not, <, >, >=, =<, ==, /=, =/=, =:=
%% is_atom, is_boolean, is_binary, is_float, is_function,
%% is_integer, is_list, is_pid, is_port, is_reference, is_tuple
-module(hipe_icode_inline_bifs).
-export([cfg/1]).
-include("hipe_icode.hrl").
-include("../flow/cfg.hrl").
%%--------------------------------------------------------------------
-spec cfg(#cfg{}) -> #cfg{}.
cfg(Cfg) ->
Linear = hipe_icode_cfg:cfg_to_linear(Cfg),
#icode{code = StraightCode} = Linear,
FinalCode = lists:flatten([inline_bif(I) || I <- StraightCode]),
Cfg1 = hipe_icode_cfg:linear_to_cfg(Linear#icode{code = FinalCode}),
hipe_icode_cfg:remove_unreachable_code(Cfg1).
inline_bif(I = #icode_call{}) ->
try_conditional(I);
inline_bif(I) ->
I.
try_conditional(I = #icode_call{dstlist = [Dst], 'fun' = {erlang, Name, 2},
args = [Arg1, Arg2],
continuation = Cont}) ->
case is_conditional(Name) of
true ->
inline_conditional(Dst, Name, Arg1, Arg2, Cont);
false ->
try_bool(I)
end;
try_conditional(I) ->
try_bool(I).
is_conditional(Name) ->
case Name of
'=:=' -> true;
'=/=' -> true;
'==' -> true;
'/=' -> true;
'>' -> true;
'<' -> true;
'>=' -> true;
'=<' -> true;
_ -> false
end.
try_bool(I = #icode_call{dstlist = [Dst], 'fun' = Name,
args = [Arg1, Arg2],
continuation = Cont, fail_label = Fail}) ->
case is_binary_bool(Name) of
{true, Results, ResLbls} ->
inline_binary_bool(Dst, Results, ResLbls, Arg1, Arg2, Cont, Fail, I);
false ->
try_type_tests(I)
end;
try_bool(I = #icode_call{dstlist = [Dst], 'fun' = {erlang, 'not', 1},
args = [Arg1],
continuation = Cont,
fail_label = Fail}) ->
inline_unary_bool(Dst, {false, true}, Arg1, Cont, Fail, I);
try_bool(I) -> try_type_tests(I).
is_binary_bool({erlang, Name, 2}) ->
ResTLbl = hipe_icode:mk_new_label(),
ResFLbl = hipe_icode:mk_new_label(),
ResTL = hipe_icode:label_name(ResTLbl),
ResFL = hipe_icode:label_name(ResFLbl),
case Name of
'and' -> {true, {ResTL, ResFL, ResFL}, {ResTLbl, ResFLbl}};
'or' -> {true, {ResTL, ResTL, ResFL}, {ResTLbl, ResFLbl}};
'xor' -> {true, {ResFL, ResTL, ResFL}, {ResTLbl, ResFLbl}};
_ -> false
end;
is_binary_bool(_) -> false.
try_type_tests(I = #icode_call{dstlist=[Dst], 'fun' = {erlang, Name, 1},
args = Args, continuation = Cont}) ->
case is_type_test(Name) of
{true, Type} ->
inline_type_test(Dst, Type, Args, Cont);
false ->
I
end;
try_type_tests(I) -> I.
is_type_test(Name) ->
case Name of
is_integer -> {true, integer};
is_float -> {true, float};
is_tuple -> {true, tuple};
is_binary -> {true, binary};
is_list -> {true, list};
is_pid -> {true, pid};
is_atom -> {true, atom};
is_boolean -> {true, boolean};
is_function -> {true, function};
is_reference -> {true, reference};
is_port -> {true, port};
_ -> false
end.
inline_type_test(BifRes, Type, Src, Cont) ->
{NewCont, NewEnd} = get_cont_lbl(Cont),
TLbl = hipe_icode:mk_new_label(),
FLbl = hipe_icode:mk_new_label(),
TL = hipe_icode:label_name(TLbl),
FL = hipe_icode:label_name(FLbl),
[hipe_icode:mk_type(Src, Type, TL, FL),
TLbl,
hipe_icode:mk_move(BifRes, hipe_icode:mk_const(true)),
hipe_icode:mk_goto(NewCont),
FLbl,
hipe_icode:mk_move(BifRes, hipe_icode:mk_const(false)),
hipe_icode:mk_goto(NewCont)|
NewEnd].
inline_conditional(BifRes, Op, Src1, Src2, Cont) ->
{NewCont, NewEnd} = get_cont_lbl(Cont),
TLbl = hipe_icode:mk_new_label(),
FLbl = hipe_icode:mk_new_label(),
TL = hipe_icode:label_name(TLbl),
FL = hipe_icode:label_name(FLbl),
[hipe_icode:mk_if(Op, [Src1, Src2], TL, FL),
TLbl,
hipe_icode:mk_move(BifRes, hipe_icode:mk_const(true)),
hipe_icode:mk_goto(NewCont),
FLbl,
hipe_icode:mk_move(BifRes, hipe_icode:mk_const(false)),
hipe_icode:mk_goto(NewCont)|
NewEnd].
%%
%% The TTL TFL FFL labelnames points to either ResTLbl or ResFLbl
%% Depending on what boolean expression we are inlining
%%
inline_binary_bool(Dst, {TTL, TFL, FFL}, {ResTLbl, ResFLbl},
Arg1, Arg2, Cont, Fail, I) ->
{NewCont, NewEnd} = get_cont_lbl(Cont),
{NewFail, FailCode} = get_fail_lbl(Fail, I),
EndCode = FailCode++NewEnd,
TLbl = hipe_icode:mk_new_label(),
FLbl = hipe_icode:mk_new_label(),
NotTLbl = hipe_icode:mk_new_label(),
NotTTLbl = hipe_icode:mk_new_label(),
NotTFLbl = hipe_icode:mk_new_label(),
TL = hipe_icode:label_name(TLbl),
FL = hipe_icode:label_name(FLbl),
NotTL = hipe_icode:label_name(NotTLbl),
NotTTL = hipe_icode:label_name(NotTTLbl),
NotTFL = hipe_icode:label_name(NotTFLbl),
[hipe_icode:mk_type([Arg1], {atom, true}, TL, NotTL, 0.5),
NotTLbl,
hipe_icode:mk_type([Arg1], {atom, false}, FL, NewFail, 0.99),
TLbl,
hipe_icode:mk_type([Arg2], {atom, true}, TTL, NotTTL, 0.5),
NotTTLbl,
hipe_icode:mk_type([Arg2], {atom, false}, TFL, NewFail, 0.99),
FLbl,
hipe_icode:mk_type([Arg2], {atom, true}, TFL, NotTFL, 0.5),
NotTFLbl,
hipe_icode:mk_type([Arg2], {atom, false}, FFL, NewFail, 0.99),
ResTLbl,
hipe_icode:mk_move(Dst, hipe_icode:mk_const(true)),
hipe_icode:mk_goto(NewCont),
ResFLbl,
hipe_icode:mk_move(Dst, hipe_icode:mk_const(false)),
hipe_icode:mk_goto(NewCont)|
EndCode].
inline_unary_bool(Dst, {T,F}, Arg1, Cont, Fail, I) ->
TLbl = hipe_icode:mk_new_label(),
NotTLbl = hipe_icode:mk_new_label(),
FLbl = hipe_icode:mk_new_label(),
TL = hipe_icode:label_name(TLbl),
NotTL = hipe_icode:label_name(NotTLbl),
FL = hipe_icode:label_name(FLbl),
{NewCont, NewEnd} = get_cont_lbl(Cont),
{NewFail, FailCode} = get_fail_lbl(Fail, I),
EndCode = FailCode ++ NewEnd,
Arg1L = [Arg1],
[hipe_icode:mk_type(Arg1L, {atom, true}, TL, NotTL, 0.5),
NotTLbl,
hipe_icode:mk_type(Arg1L, {atom, false}, FL, NewFail, 0.99),
TLbl,
hipe_icode:mk_move(Dst, hipe_icode:mk_const(T)),
hipe_icode:mk_goto(NewCont),
FLbl,
hipe_icode:mk_move(Dst, hipe_icode:mk_const(F)),
hipe_icode:mk_goto(NewCont)|
EndCode].
get_cont_lbl([]) ->
NL = hipe_icode:mk_new_label(),
{hipe_icode:label_name(NL), [NL]};
get_cont_lbl(Cont) ->
{Cont, []}.
get_fail_lbl([], I) ->
NL = hipe_icode:mk_new_label(),
{hipe_icode:label_name(NL), [NL, I]};
get_fail_lbl(Fail, _) ->
{Fail, []}.