%% -*- erlang-indent-level: 2 -*-
%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
%%
%% 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.
%%
%% %CopyrightEnd%
%%
-module(hipe_arm_defuse).
-export([insn_def_all/1, insn_use_all/1]).
-export([insn_def_gpr/1, insn_use_gpr/1]).
-include("hipe_arm.hrl").
%%%
%%% Defs and uses for both general-purpose and floating-point registers.
%%% This is needed for the frame module, alas.
%%%
insn_def_all(I) ->
insn_def_gpr(I).
insn_use_all(I) ->
insn_use_gpr(I).
%%%
%%% Defs and uses for general-purpose (integer) registers only.
%%%
insn_def_gpr(I) ->
case I of
#alu{dst=Dst} -> [Dst];
#load{dst=Dst} -> [Dst];
#ldrsb{dst=Dst} -> [Dst];
#move{dst=Dst} -> [Dst];
#pseudo_call{} -> call_clobbered_gpr();
#pseudo_li{dst=Dst} -> [Dst];
#pseudo_move{dst=Dst} -> [Dst];
#pseudo_tailcall_prepare{} -> tailcall_clobbered_gpr();
#smull{dstlo=DstLo,dsthi=DstHi,src1=Src1} ->
%% ARM requires DstLo, DstHi, and Src1 to be distinct.
%% Add fake DEF of Src1 to prevent regalloc from reusing
%% it as DstLo or DstHi.
[DstLo, DstHi, Src1];
_ -> []
end.
call_clobbered_gpr() ->
[hipe_arm:mk_temp(R, T)
|| {R,T} <- hipe_arm_registers:call_clobbered() ++ all_fp_pseudos()].
all_fp_pseudos() -> []. % XXX: for now
tailcall_clobbered_gpr() ->
[hipe_arm:mk_temp(R, T)
|| {R,T} <- hipe_arm_registers:tailcall_clobbered() ++ all_fp_pseudos()].
insn_use_gpr(I) ->
case I of
#alu{src=Src,am1=Am1} -> am1_use(Am1, [Src]);
#blx{src=Src} -> [Src];
#cmp{src=Src,am1=Am1} -> am1_use(Am1, [Src]);
#load{am2=Am2} -> am2_use(Am2, []);
#ldrsb{am3=Am3} -> am3_use(Am3, []);
#move{am1=Am1} -> am1_use(Am1, []);
#pseudo_blr{} ->
LR = hipe_arm:mk_temp(hipe_arm_registers:lr(), 'untagged'),
RV = hipe_arm:mk_temp(hipe_arm_registers:return_value(), 'tagged'),
[RV, LR];
#pseudo_bx{src=Src} ->
io:format("~w: whoa there! insn_use of ~w occurred\n", [?MODULE,I]),
[Src];
#pseudo_call{funv=FunV,sdesc=#arm_sdesc{arity=Arity}} ->
funv_use(FunV, arity_use_gpr(Arity));
#pseudo_move{src=Src} -> [Src];
#pseudo_switch{jtab=JTabR,index=IndexR} -> addtemp(JTabR, [IndexR]);
#pseudo_tailcall{funv=FunV,arity=Arity,stkargs=StkArgs} ->
addargs(StkArgs, addtemps(tailcall_clobbered_gpr(), funv_use(FunV, arity_use_gpr(Arity))));
#smull{src1=Src1,src2=Src2} -> addtemp(Src1, [Src2]);
#store{src=Src,am2=Am2} -> am2_use(Am2, [Src]);
_ -> []
end.
addargs([Arg|Args], Set) ->
addargs(Args, addarg(Arg, Set));
addargs([], Set) ->
Set.
addarg(Arg, Set) ->
case Arg of
#arm_temp{} -> addtemp(Arg, Set);
_ -> Set
end.
arity_use_gpr(Arity) ->
[hipe_arm:mk_temp(R, 'tagged')
|| R <- hipe_arm_registers:args(Arity)].
funv_use(FunV, Set) ->
case FunV of
#arm_temp{} -> addtemp(FunV, Set);
_ -> Set
end.
am1_use(Am1, Set) ->
case Am1 of
#arm_temp{} -> addtemp(Am1, Set);
{Src,rrx} -> addtemp(Src, Set);
{Src,_,ShiftArg} ->
Set1 = addtemp(Src, Set),
case ShiftArg of
#arm_temp{} -> addtemp(ShiftArg, Set1);
_ -> Set1
end;
_ -> Set
end.
am2_use(#am2{src=Src,offset=Am2Offset}, Set) ->
Set1 = addtemp(Src, Set),
case Am2Offset of
#arm_temp{} -> addtemp(Am2Offset, Set1);
{Src2,_} -> addtemp(Src2, Set1);
{Src2,_,_} -> addtemp(Src2, Set1);
_ -> Set1
end.
am3_use(#am3{src=Src,offset=Am3Offset}, Set) ->
Set1 = addtemp(Src, Set),
case Am3Offset of
#arm_temp{} -> addtemp(Am3Offset, Set1);
_ -> Set1
end.
%%%
%%% Auxiliary operations on sets of temps
%%% These sets are small. No point using gb_trees, right?
%%%
addtemps([Arg|Args], Set) ->
addtemps(Args, addtemp(Arg, Set));
addtemps([], Set) ->
Set.
addtemp(Temp, Set) ->
case lists:member(Temp, Set) of
false -> [Temp|Set];
_ -> Set
end.