%%% -*- 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.
%%%
%%% simple local x86 regalloc
-ifdef(HIPE_AMD64).
-define(HIPE_X86_RA_NAIVE, hipe_amd64_ra_naive).
-define(HIPE_X86_REGISTERS, hipe_amd64_registers).
-define(HIPE_X86_SPECIFIC_FP, hipe_amd64_specific_sse2).
-define(ECX, rcx).
-else.
-define(HIPE_X86_RA_NAIVE, hipe_x86_ra_naive).
-define(HIPE_X86_REGISTERS, hipe_x86_registers).
-define(HIPE_X86_SPECIFIC_FP, hipe_x86_specific_x87).
-define(ECX, ecx).
-endif.
-module(?HIPE_X86_RA_NAIVE).
-export([ra/4]).
-include("../x86/hipe_x86.hrl").
-define(HIPE_INSTRUMENT_COMPILER, true). % enable instrumentation
-include("../main/hipe.hrl").
ra(CFG0, Liveness, Coloring_fp, Options) ->
CFG = hipe_x86_cfg:map_bbs(fun do_bb/2, CFG0),
NofSpilledFloats = count_non_float_spills(Coloring_fp),
NofFloats = length(Coloring_fp),
?add_spills(Options, hipe_gensym:get_var(x86) -
?HIPE_X86_REGISTERS:first_virtual()-
NofSpilledFloats -
NofFloats),
TempMap = [],
{CFG, Liveness,
TempMap}.
do_bb(_Lbl, BB) ->
hipe_bb:code_update(BB, do_insns(hipe_bb:code(BB))).
count_non_float_spills(Coloring_fp) ->
count_non_float_spills(Coloring_fp, 0).
count_non_float_spills([{_,To}|Tail], Num) ->
case ?HIPE_X86_SPECIFIC_FP:is_precoloured(To, no_context) of
true ->
count_non_float_spills(Tail, Num);
false ->
count_non_float_spills(Tail, Num+1)
end;
count_non_float_spills([], Num) ->
Num.
do_insns([I|Insns]) ->
do_insn(I) ++ do_insns(Insns);
do_insns([]) ->
[].
do_insn(I) -> % Insn -> Insn list
case I of
#alu{} ->
do_alu(I);
#cmp{} ->
do_cmp(I);
#imul{} ->
do_imul(I);
#jmp_switch{} ->
do_jmp_switch(I);
#lea{} ->
do_lea(I);
#move{} ->
do_move(I);
#move64{} ->
do_move64(I);
#movzx{} ->
do_movx(I);
#movsx{} ->
do_movx(I);
#fmove{} ->
do_fmove(I);
#fp_unop{} ->
do_fp_unop(I);
#fp_binop{} ->
do_fp_binop(I);
#shift{} ->
do_shift(I);
#test{} ->
do_test(I);
#label{} ->
[I];
#pseudo_jcc{} ->
[I];
#pseudo_call{} ->
[I];
#ret{} ->
[I];
#pseudo_tailcall_prepare{} ->
[I];
#pseudo_tailcall{} ->
[I];
#push{} ->
[I];
#jmp_label{} ->
[I];
#comment{} ->
[I];
_ ->
io:format("Unknown Instruction = ~w\n", [I]),
exit({?MODULE, unknown_instruction, I})
end.
%%% Fix an alu op.
do_alu(I) ->
#alu{src=Src0,dst=Dst0} = I,
{FixSrc,Src,FixDst,Dst} = do_binary(Src0, Dst0),
FixSrc ++ FixDst ++ [I#alu{src=Src,dst=Dst}].
%%% Fix a cmp op.
do_cmp(I) ->
#cmp{src=Src0,dst=Dst0} = I,
{FixSrc, Src, FixDst, Dst} = do_binary(Src0, Dst0),
FixSrc ++ FixDst ++ [I#cmp{src=Src,dst=Dst}].
%%% Fix an imul op.
do_imul(I) ->
#imul{imm_opt=ImmOpt,src=Src0,temp=Temp0} = I,
{FixSrc,Src} = fix_src_operand(Src0), % may use temp0
{FixTempSrc,Temp,FixTempDst} =
case temp_is_pseudo(Temp0) of
false ->
{[], Temp0, []};
true ->
Reg = hipe_x86:mk_temp(?HIPE_X86_REGISTERS:temp1(), 'untagged'),
{case ImmOpt of
[] -> [hipe_x86:mk_move(Temp0, Reg)]; % temp *= src
_ -> [] % temp = src * imm
end,
Reg,
[hipe_x86:mk_move(Reg, Temp0)]}
end,
FixSrc ++ FixTempSrc ++ [I#imul{src=Src,temp=Temp}] ++ FixTempDst.
%%% Fix a jmp_switch op.
-ifdef(HIPE_AMD64).
do_jmp_switch(I) ->
#jmp_switch{temp=Temp, jtab=Tab} = I,
case temp_is_pseudo(Temp) of
false ->
case temp_is_pseudo(Tab) of
false ->
[I];
true ->
Reg = hipe_x86:mk_temp(hipe_amd64_registers:temp0(), 'untagged'),
[hipe_x86:mk_move(Temp, Reg), I#jmp_switch{jtab=Reg}]
end;
true ->
Reg = hipe_x86:mk_temp(hipe_amd64_registers:temp1(), 'untagged'),
case temp_is_pseudo(Tab) of
false ->
[hipe_x86:mk_move(Temp, Reg), I#jmp_switch{temp=Reg}];
true ->
Reg2 = hipe_x86:mk_temp(hipe_amd64_registers:temp0(), 'untagged'),
[hipe_x86:mk_move(Temp, Reg),
hipe_x86:mk_move(Tab, Reg2),
I#jmp_switch{temp=Reg, jtab=Reg2}]
end
end.
-else.
do_jmp_switch(I) ->
#jmp_switch{temp=Temp} = I,
case temp_is_pseudo(Temp) of
false ->
[I];
true ->
Reg = hipe_x86:mk_temp(?HIPE_X86_REGISTERS:temp0(), 'untagged'),
[hipe_x86:mk_move(Temp, Reg), I#jmp_switch{temp=Reg}]
end.
-endif.
%%% Fix a lea op.
do_lea(I) ->
#lea{temp=Temp} = I,
case temp_is_pseudo(Temp) of
false ->
[I];
true ->
Reg = hipe_x86:mk_temp(?HIPE_X86_REGISTERS:temp0(), 'untagged'),
[I#lea{temp=Reg}, hipe_x86:mk_move(Reg, Temp)]
end.
%%% Fix a move op.
do_move(I) ->
#move{src=Src0,dst=Dst0} = I,
{FixSrc, Src, FixDst, Dst} = do_binary(Src0, Dst0),
FixSrc ++ FixDst ++ [I#move{src=Src,dst=Dst}].
-ifdef(HIPE_AMD64).
do_move64(I) ->
#move64{dst=Dst} = I,
case is_mem_opnd(Dst) of
false ->
[I];
true ->
Reg = hipe_amd64_registers:temp1(),
NewDst = clone(Dst, Reg),
[I#move64{dst=NewDst}, hipe_x86:mk_move(NewDst, Dst)]
end.
-else.
do_move64(I) -> exit({?MODULE, I}).
-endif.
do_movx(I) ->
{{FixSrc, Src}, {FixDst, Dst}} =
case I of
#movsx{src=Src0,dst=Dst0} ->
{fix_src_operand(Src0), fix_dst_operand(Dst0)};
#movzx{src=Src0,dst=Dst0} ->
{fix_src_operand(Src0), fix_dst_operand(Dst0)}
end,
Reg = ?HIPE_X86_REGISTERS:temp0(),
Dst2 = clone(Dst, Reg),
I2 = case is_mem_opnd(Dst) of
true ->
Reg = ?HIPE_X86_REGISTERS:temp0(),
Dst2 = clone(Dst, Reg),
case I of
#movsx{} ->
[hipe_x86:mk_movsx(Src, Dst2), hipe_x86:mk_move(Dst2, Dst)];
#movzx{} ->
[hipe_x86:mk_movzx(Src, Dst2), hipe_x86:mk_move(Dst2, Dst)]
end;
false ->
case I of
#movsx{} ->
[hipe_x86:mk_movsx(Src, Dst)];
#movzx{} ->
[hipe_x86:mk_movzx(Src, Dst)]
end
end,
FixSrc ++ FixDst ++ I2.
%%% Fix a fmove op.
%% conv_to_float
do_fmove(I=#fmove{src=#x86_temp{type=untagged},
dst=#x86_temp{type=double}}) ->
#fmove{src=Src0,dst=Dst0} = I,
Src = clone(Src0, ?HIPE_X86_REGISTERS:temp0()),
Dst = clone(Dst0, ?HIPE_X86_REGISTERS:temp1()),
[hipe_x86:mk_move(Src0, Src),
I#fmove{src=Src, dst=Dst},
hipe_x86:mk_fmove(Dst, Dst0)];
%% fmove
do_fmove(I) ->
#fmove{src=Src0,dst=Dst0} = I,
{FixSrc, Src, FixDst, Dst} = do_binary(Src0, Dst0),
FixSrc ++ FixDst ++ [I#fmove{src=Src,dst=Dst}].
do_fp_unop(I) ->
#fp_unop{arg=Arg} = I,
case is_mem_opnd(Arg) of
false ->
[I];
true ->
Reg = ?HIPE_X86_REGISTERS:temp1(),
NewArg = clone(Arg, Reg),
[hipe_x86:mk_fmove(Arg, NewArg),
I#fp_unop{arg=NewArg},
hipe_x86:mk_fmove(NewArg, Arg)]
end.
do_fp_binop(I) ->
#fp_binop{src=Src0, dst=Dst0} = I,
{FixSrc, Src} = fix_src_operand(Src0),
{FixDst, Dst} = fix_dst_operand(Dst0),
Reg = ?HIPE_X86_REGISTERS:temp1(),
Dst2 = clone(Dst, Reg),
FixSrc ++ FixDst ++ [hipe_x86:mk_fmove(Dst, Dst2),
I#fp_binop{src=Src, dst=Dst2},
hipe_x86:mk_fmove(Dst2, Dst)].
do_shift(I) ->
#shift{src=Src0,dst=Dst0} = I,
{FixDst, Dst} = fix_dst_operand(Dst0),
Reg = ?HIPE_X86_REGISTERS:?ECX(),
case Src0 of
#x86_imm{} ->
FixDst ++ [I#shift{dst=Dst}];
#x86_temp{reg=Reg} ->
FixDst ++ [I#shift{dst=Dst}]
end.
do_test(I) ->
#test{src=Src0,dst=Dst0} = I,
{FixSrc, Src, FixDst, Dst} = do_binary(Src0, Dst0),
FixSrc ++ FixDst ++ [I#test{src=Src,dst=Dst}].
%%% Fix the operands of a binary op.
%%% 1. remove pseudos from any explicit memory operands
%%% 2. if both operands are (implicit or explicit) memory operands,
%%% move src to a reg and use reg as src in the original insn
do_binary(Src0, Dst0) ->
{FixSrc, Src} = fix_src_operand(Src0),
{FixDst, Dst} = fix_dst_operand(Dst0),
{FixSrc3, Src3} =
case is_mem_opnd(Src) of
false ->
{FixSrc, Src};
true ->
case is_mem_opnd(Dst) of
false ->
{FixSrc, Src};
true ->
Reg = ?HIPE_X86_REGISTERS:temp0(),
Src2 = clone(Src, Reg),
FixSrc2 = FixSrc ++ [mk_move(Src, Src2)],
{FixSrc2, Src2}
end
end,
{FixSrc3, Src3, FixDst, Dst}.
%%% Fix any x86_mem operand to not refer to any pseudos.
%%% The fixup may use additional instructions and registers.
%%% 'src' operands may clobber '%temp0'.
%%% 'dst' operands may clobber '%temp1'.
fix_src_operand(Opnd) ->
fix_mem_operand(Opnd, ?HIPE_X86_REGISTERS:temp0()).
fix_dst_operand(Opnd) ->
fix_mem_operand(Opnd, ?HIPE_X86_REGISTERS:temp1()).
fix_mem_operand(Opnd, Reg) -> % -> {[fixupcode], newop}
case Opnd of
#x86_mem{base=Base,off=Off} ->
case is_mem_opnd(Base) of
false ->
case src_is_pseudo(Off) of
false ->
{[], Opnd};
true -> % pseudo(reg)
Temp = clone(Off, Reg),
{[hipe_x86:mk_move(Off, Temp)],
Opnd#x86_mem{off=Temp}}
end;
true ->
Temp = clone(Base, Reg),
case src_is_pseudo(Off) of
false -> % imm/reg(pseudo)
{[hipe_x86:mk_move(Base, Temp)],
Opnd#x86_mem{base=Temp}};
true -> % pseudo1(pseudo0)
{[hipe_x86:mk_move(Base, Temp),
hipe_x86:mk_alu('add', Off, Temp)],
Opnd#x86_mem{base=Temp, off=hipe_x86:mk_imm(0)}}
end
end;
_ ->
{[], Opnd}
end.
%%% Check if an operand denotes a memory cell (mem or pseudo).
is_mem_opnd(Opnd) ->
case Opnd of
#x86_mem{} -> true;
#x86_temp{} -> temp_is_pseudo(Opnd);
_ -> false
end.
%%% Check if an operand is a pseudo-Temp.
src_is_pseudo(Src) ->
case hipe_x86:is_temp(Src) of
true -> temp_is_pseudo(Src);
false -> false
end.
temp_is_pseudo(Temp) ->
not(?HIPE_X86_REGISTERS:is_precoloured(hipe_x86:temp_reg(Temp))).
%%% Make Reg a clone of Dst (attach Dst's type to Reg).
clone(Dst, Reg) ->
Type =
case Dst of
#x86_mem{} -> hipe_x86:mem_type(Dst);
#x86_temp{} -> hipe_x86:temp_type(Dst)
end,
hipe_x86:mk_temp(Reg, Type).
mk_move(Src, Dst=#x86_temp{type=double}) ->
hipe_x86:mk_fmove(Src, Dst);
mk_move(Src, Dst) ->
hipe_x86:mk_move(Src, Dst).