%%% -*- 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.
%%%
%%% - apply temp -> reg/spill map from RA
-ifdef(HIPE_AMD64).
-define(HIPE_X86_RA_FINALISE, hipe_amd64_ra_finalise).
-define(HIPE_X86_REGISTERS, hipe_amd64_registers).
-define(HIPE_X86_X87, hipe_amd64_x87).
-define(HIPE_X86_SSE2, hipe_amd64_sse2).
-define(IF_HAS_SSE2(Expr), Expr).
-else.
-define(HIPE_X86_RA_FINALISE, hipe_x86_ra_finalise).
-define(HIPE_X86_REGISTERS, hipe_x86_registers).
-define(HIPE_X86_X87, hipe_x86_x87).
-define(IF_HAS_SSE2(Expr),).
-endif.
-module(?HIPE_X86_RA_FINALISE).
-export([finalise/4]).
-include("../x86/hipe_x86.hrl").
finalise(CFG0, TempMap, FpMap, Options) ->
CFG1 = finalise_ra(CFG0, TempMap, FpMap, Options),
case proplists:get_bool(x87, Options) of
true ->
?HIPE_X86_X87:map(CFG1);
_ ->
case
proplists:get_bool(inline_fp, Options)
and (proplists:get_value(regalloc, Options) =:= linear_scan)
of
%% Ugly, but required to avoid Dialyzer complaints about "Unknown
%% function" hipe_x86_sse2:map/1
?IF_HAS_SSE2(true ->
?HIPE_X86_SSE2:map(CFG1);)
false ->
CFG1
end
end.
%%%
%%% Finalise the temp->reg/spill mapping.
%%% (XXX: maybe this should be merged with the main pass,
%%% but I just want this to work now)
%%%
finalise_ra(CFG, [], [], _Options) ->
CFG;
finalise_ra(CFG, TempMap, FpMap, Options) ->
{_, SpillLimit} = hipe_gensym:var_range(x86),
Map = mk_ra_map(TempMap, SpillLimit),
FpMap0 = mk_ra_map_fp(FpMap, SpillLimit, Options),
hipe_x86_cfg:map_bbs(fun(_Lbl, BB) -> ra_bb(BB, Map, FpMap0) end, CFG).
ra_bb(BB, Map, FpMap) ->
hipe_bb:code_update(BB, ra_code(hipe_bb:code(BB), Map, FpMap)).
ra_code(Code, Map, FpMap) ->
[ra_insn(I, Map, FpMap) || I <- Code].
ra_insn(I, Map, FpMap) ->
case I of
#alu{src=Src0,dst=Dst0} ->
Src = ra_opnd(Src0, Map),
Dst = ra_opnd(Dst0, Map),
I#alu{src=Src,dst=Dst};
#call{} ->
I;
#cmovcc{src=Src0,dst=Dst0} ->
Src = ra_opnd(Src0, Map),
Dst = ra_opnd(Dst0, Map),
I#cmovcc{src=Src,dst=Dst};
#cmp{src=Src0,dst=Dst0} ->
Src = ra_opnd(Src0, Map),
Dst = ra_opnd(Dst0, Map),
I#cmp{src=Src,dst=Dst};
#comment{} ->
I;
#fmove{src=Src0,dst=Dst0} ->
Src = ra_opnd(Src0, Map, FpMap),
Dst = ra_opnd(Dst0, Map, FpMap),
I#fmove{src=Src,dst=Dst};
#fp_unop{arg=Arg0} ->
Arg = ra_opnd(Arg0, Map, FpMap),
I#fp_unop{arg=Arg};
#fp_binop{src=Src0,dst=Dst0} ->
Src = ra_opnd(Src0, Map, FpMap),
Dst = ra_opnd(Dst0, Map, FpMap),
I#fp_binop{src=Src,dst=Dst};
#imul{src=Src0,temp=Temp0} ->
Src = ra_opnd(Src0, Map),
Temp = ra_temp(Temp0, Map),
I#imul{src=Src,temp=Temp};
#jcc{} ->
I;
#jmp_fun{'fun'=Fun0} ->
Fun = ra_opnd(Fun0, Map),
I#jmp_fun{'fun'=Fun};
#jmp_label{} ->
I;
#jmp_switch{temp=Temp0,jtab=JTab0} ->
Temp = ra_opnd(Temp0, Map),
JTab = ra_opnd(JTab0, Map),
I#jmp_switch{temp=Temp,jtab=JTab};
#label{} ->
I;
#lea{mem=Mem0,temp=Temp0} ->
Mem = ra_mem(Mem0, Map),
Temp = ra_temp(Temp0, Map),
I#lea{mem=Mem,temp=Temp};
#move{src=Src0,dst=Dst0} ->
Src = ra_opnd(Src0, Map),
Dst = ra_opnd(Dst0, Map),
I#move{src=Src,dst=Dst};
#move64{dst=Dst0} ->
Dst = ra_opnd(Dst0, Map),
I#move64{dst=Dst};
#movsx{src=Src0,dst=Dst0} ->
Src = ra_opnd(Src0, Map),
Dst = ra_opnd(Dst0, Map),
I#movsx{src=Src,dst=Dst};
#movzx{src=Src0,dst=Dst0} ->
Src = ra_opnd(Src0, Map),
Dst = ra_opnd(Dst0, Map),
I#movzx{src=Src,dst=Dst};
#pseudo_call{'fun'=Fun0} ->
Fun = ra_opnd(Fun0, Map),
I#pseudo_call{'fun'=Fun};
#pseudo_jcc{} ->
I;
#pseudo_spill_fmove{src=Src0, temp=Temp0, dst=Dst0} ->
Src = ra_opnd(Src0, Map, FpMap),
Temp = ra_opnd(Temp0, Map, FpMap),
Dst = ra_opnd(Dst0, Map, FpMap),
I#pseudo_spill_fmove{src=Src, temp=Temp, dst=Dst};
#pseudo_spill_move{src=Src0, temp=Temp0, dst=Dst0} ->
Src = ra_opnd(Src0, Map),
Temp = ra_opnd(Temp0, Map),
Dst = ra_opnd(Dst0, Map),
I#pseudo_spill_move{src=Src, temp=Temp, dst=Dst};
#pseudo_tailcall{'fun'=Fun0,stkargs=StkArgs0} ->
Fun = ra_opnd(Fun0, Map),
StkArgs = ra_args(StkArgs0, Map),
I#pseudo_tailcall{'fun'=Fun,stkargs=StkArgs};
#pseudo_tailcall_prepare{} ->
I;
#push{src=Src0} ->
Src = ra_opnd(Src0, Map),
I#push{src=Src};
#ret{} ->
I;
#shift{src=Src0,dst=Dst0} ->
Src = ra_opnd(Src0, Map),
Dst = ra_opnd(Dst0, Map),
I#shift{src=Src,dst=Dst};
#test{src=Src0,dst=Dst0} ->
Src = ra_opnd(Src0, Map),
Dst = ra_opnd(Dst0, Map),
I#test{src=Src,dst=Dst};
_ ->
exit({?MODULE,ra_insn,I})
end.
ra_args(Args, Map) ->
[ra_opnd(Opnd, Map) || Opnd <- Args].
ra_opnd(Opnd, Map) ->
ra_opnd(Opnd, Map, gb_trees:empty()).
ra_opnd(Opnd, Map, FpMap) ->
case Opnd of
#x86_temp{} -> ra_temp(Opnd, Map, FpMap);
#x86_mem{} -> ra_mem(Opnd, Map);
_ -> Opnd
end.
ra_mem(Mem, Map) ->
#x86_mem{base=Base0,off=Off0} = Mem,
Base = ra_opnd(Base0, Map),
Off = ra_opnd(Off0, Map),
Mem#x86_mem{base=Base,off=Off}.
ra_temp(Temp, Map) ->
ra_temp(Temp, Map, gb_trees:empty()).
ra_temp(Temp, Map, FpMap) ->
Reg = hipe_x86:temp_reg(Temp),
case hipe_x86:temp_type(Temp) of
double ->
ra_temp_double(Temp, Reg, FpMap);
_->
case ?HIPE_X86_REGISTERS:is_precoloured(Reg) of
true ->
Temp;
_ ->
case gb_trees:lookup(Reg, Map) of
{value,NewReg} -> Temp#x86_temp{reg=NewReg};
_ -> Temp
end
end
end.
-ifdef(HIPE_AMD64).
ra_temp_double(Temp, Reg, FpMap) ->
case hipe_amd64_registers:is_precoloured_sse2(Reg) of
true ->
Temp;
_ ->
case gb_trees:lookup(Reg, FpMap) of
{value,NewReg} -> Temp#x86_temp{reg=NewReg};
_ -> Temp
end
end.
-else.
ra_temp_double(Temp, Reg, FpMap) ->
case gb_trees:lookup(Reg, FpMap) of
{value,NewReg} ->
case hipe_x86_registers:is_precoloured_x87(NewReg) of
true -> hipe_x86:mk_fpreg(NewReg);
false ->
Temp#x86_temp{reg=NewReg}
end;
_ ->
Temp
end.
-endif.
mk_ra_map(TempMap, SpillLimit) ->
%% Build a partial map from pseudo to reg or spill.
%% Spills are represented as pseudos with indices above SpillLimit.
%% (I'd prefer to use negative indices, but that breaks
%% ?HIPE_X86_REGISTERS:is_precoloured/1.)
%% The frame mapping proper is unchanged, since spills look just like
%% ordinary (un-allocated) pseudos.
lists:foldl(fun(MapLet, Map) ->
{Key,Val} = conv_ra_maplet(MapLet, SpillLimit,
is_precoloured),
gb_trees:insert(Key, Val, Map)
end,
gb_trees:empty(),
TempMap).
conv_ra_maplet({From,To}, SpillLimit, IsPrecoloured)
when is_integer(From), From =< SpillLimit ->
%% From should be a pseudo, or a hard reg mapped to itself.
case ?HIPE_X86_REGISTERS:IsPrecoloured(From) of
false -> ok;
_ -> To = {reg, From}, ok
end,
%% end of From check
case To of
{reg, NewReg} when is_integer(NewReg) ->
%% NewReg should be a hard reg, or a pseudo mapped
%% to itself (formals are handled this way).
true = (?HIPE_X86_REGISTERS:IsPrecoloured(NewReg) orelse From =:= NewReg),
{From, NewReg};
{spill, SpillIndex} when is_integer(SpillIndex), SpillIndex >= 0 ->
ToTempNum = SpillLimit+SpillIndex+1,
MaxTempNum = hipe_gensym:get_var(x86),
if MaxTempNum >= ToTempNum -> ok;
true -> hipe_gensym:set_var(x86, ToTempNum)
end,
{From, ToTempNum}
end.
mk_ra_map_x87(FpMap, SpillLimit) ->
lists:foldl(fun(MapLet, Map) ->
{Key,Val} = conv_ra_maplet(MapLet, SpillLimit,
is_precoloured_x87),
gb_trees:insert(Key, Val, Map)
end,
gb_trees:empty(),
FpMap).
-ifdef(HIPE_AMD64).
mk_ra_map_sse2(FpMap, SpillLimit) ->
lists:foldl(fun(MapLet, Map) ->
{Key,Val} = conv_ra_maplet(MapLet, SpillLimit,
is_precoloured_sse2),
gb_trees:insert(Key, Val, Map)
end,
gb_trees:empty(),
FpMap).
mk_ra_map_fp(FpMap, SpillLimit, Options) ->
case proplists:get_bool(x87, Options) of
true -> mk_ra_map_x87(FpMap, SpillLimit);
false -> mk_ra_map_sse2(FpMap, SpillLimit)
end.
-else.
mk_ra_map_fp(FpMap, SpillLimit, _Options) ->
mk_ra_map_x87(FpMap, SpillLimit).
-endif.
-ifdef(notdef).
conv_ra_maplet_fp(MapLet = {From,To}, SpillLimit) ->
%% From should be a pseudo
if is_integer(From), From =< SpillLimit -> [];
true -> exit({?MODULE,conv_ra_maplet_fp,MapLet})
end,
%% end of From check
case To of
{reg, NewReg} ->
case hipe_x86_registers:is_precoloured_x87(NewReg) of
true-> [];
false -> exit({?MODULE,conv_ra_maplet_fp,MapLet})
end,
%% end of NewReg check.
{From, NewReg};
{spill, SpillIndex} ->
%% SpillIndex should be >= 0.
if is_integer(SpillIndex), SpillIndex >= 0 -> [];
true -> exit({?MODULE,conv_ra_maplet_fp,MapLet})
end,
%% end of SpillIndex check
ToTempNum = SpillLimit+SpillIndex+1,
MaxTempNum = hipe_gensym:get_var(x86),
if MaxTempNum >= ToTempNum -> [];
true -> hipe_gensym:set_var(x86, ToTempNum)
end,
{From, ToTempNum};
_ -> exit({?MODULE,conv_ra_maplet_fp,MapLet})
end.
-endif.