%%% -*- 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.
%%%
%%% The PowerPC instruction set is quite irregular.
%%% The following quirks must be handled by the translation:
%%%
%%% - The instruction names are different for reg/reg and reg/imm
%%% source operands. For some operations, completely different
%%% instructions handle the reg/reg and reg/imm cases.
%%% - The name of an arithmetic instruction depends on whether any
%%% condition codes are to be set or not. Overflow is treated
%%% separately from other conditions.
%%% - Some combinations or RTL ALU operations, source operand shapes,
%%% and requested conditions have no direct correspondence in the
%%% PowerPC instruction set.
%%% - The tagging of immediate operands as simm16 or uimm16 depends
%%% on the actual instruction.
%%% - Conditional branches have no unsigned conditions. Instead there
%%% are signed and unsigned versions of the compare instruction.
%%% - The arithmetic overflow flag XER[SO] is sticky: once set it
%%% remains set until explicitly cleared.
-module(hipe_rtl_to_ppc).
-export([translate/1]).
-include("../rtl/hipe_rtl.hrl").
translate(RTL) ->
hipe_gensym:init(ppc),
hipe_gensym:set_var(ppc, hipe_ppc_registers:first_virtual()),
hipe_gensym:set_label(ppc, hipe_gensym:get_label(rtl)),
Map0 = vmap_empty(),
{Formals, Map1} = conv_formals(hipe_rtl:rtl_params(RTL), Map0),
OldData = hipe_rtl:rtl_data(RTL),
{Code0, NewData} = conv_insn_list(hipe_rtl:rtl_code(RTL), Map1, OldData),
{RegFormals, _} = split_args(Formals),
Code =
case RegFormals of
[] -> Code0;
_ -> [hipe_ppc:mk_label(hipe_gensym:get_next_label(ppc)) |
move_formals(RegFormals, Code0)]
end,
IsClosure = hipe_rtl:rtl_is_closure(RTL),
IsLeaf = hipe_rtl:rtl_is_leaf(RTL),
hipe_ppc:mk_defun(hipe_rtl:rtl_fun(RTL),
Formals,
IsClosure,
IsLeaf,
Code,
NewData,
[],
[]).
conv_insn_list([H|T], Map, Data) ->
{NewH, NewMap, NewData1} = conv_insn(H, Map, Data),
%% io:format("~w \n ==>\n ~w\n- - - - - - - - -\n",[H,NewH]),
{NewT, NewData2} = conv_insn_list(T, NewMap, NewData1),
{NewH ++ NewT, NewData2};
conv_insn_list([], _, Data) ->
{[], Data}.
conv_insn(I, Map, Data) ->
case I of
#alu{} -> conv_alu(I, Map, Data);
#alub{} -> conv_alub(I, Map, Data);
#call{} -> conv_call(I, Map, Data);
#comment{} -> conv_comment(I, Map, Data);
#enter{} -> conv_enter(I, Map, Data);
#goto{} -> conv_goto(I, Map, Data);
#label{} -> conv_label(I, Map, Data);
#load{} -> conv_load(I, Map, Data);
#load_address{} -> conv_load_address(I, Map, Data);
#load_atom{} -> conv_load_atom(I, Map, Data);
#move{} -> conv_move(I, Map, Data);
#return{} -> conv_return(I, Map, Data);
#store{} -> conv_store(I, Map, Data);
#switch{} -> conv_switch(I, Map, Data);
#fconv{} -> conv_fconv(I, Map, Data);
#fmove{} -> conv_fmove(I, Map, Data);
#fload{} -> conv_fload(I, Map, Data);
#fstore{} -> conv_fstore(I, Map, Data);
#fp{} -> conv_fp_binary(I, Map, Data);
#fp_unop{} -> conv_fp_unary(I, Map, Data);
_ -> exit({?MODULE,conv_insn,I})
end.
conv_fconv(I, Map, Data) ->
%% Dst := (double)Src, where Dst is FP reg and Src is GP reg or imm
{Dst, Map0} = conv_fpreg(hipe_rtl:fconv_dst(I), Map),
{Src, Map1} = conv_src(hipe_rtl:fconv_src(I), Map0),
I2 =
case hipe_ppc:is_temp(Src) of
true ->
mk_fconv(Dst, Src);
false ->
Tmp = new_untagged_temp(),
mk_li(Tmp, Src,
mk_fconv(Dst, Tmp))
end,
{I2, Map1, Data}.
mk_fconv(Dst, Src) ->
CSP = hipe_ppc:mk_temp(1, 'untagged'),
case get(hipe_target_arch) of
powerpc ->
R0 = hipe_ppc:mk_temp(0, 'untagged'),
RTmp1 = hipe_ppc:mk_new_temp('untagged'),
RTmp2 = hipe_ppc:mk_new_temp('untagged'),
RTmp3 = hipe_ppc:mk_new_temp('untagged'),
FTmp1 = hipe_ppc:mk_new_temp('double'),
FTmp2 = hipe_ppc:mk_new_temp('double'),
[hipe_ppc:mk_pseudo_li(RTmp1, {fconv_constant,c_const}),
hipe_ppc:mk_lfd(FTmp1, 0, RTmp1),
hipe_ppc:mk_alu('xoris', RTmp2, Src, hipe_ppc:mk_uimm16(16#8000)),
hipe_ppc:mk_store('stw', RTmp2, 28, CSP),
hipe_ppc:mk_alu('addis', RTmp3, R0, hipe_ppc:mk_simm16(16#4330)),
hipe_ppc:mk_store('stw', RTmp3, 24, CSP),
hipe_ppc:mk_lfd(FTmp2, 24, CSP),
hipe_ppc:mk_fp_binary('fsub', Dst, FTmp2, FTmp1)];
ppc64 ->
[hipe_ppc:mk_store('std', Src, 24, CSP),
hipe_ppc:mk_lfd(Dst, 24, CSP),
hipe_ppc:mk_fp_unary('fcfid', Dst, Dst)]
end.
conv_fmove(I, Map, Data) ->
%% Dst := Src, where both Dst and Src are FP regs
{Dst, Map0} = conv_fpreg(hipe_rtl:fmove_dst(I), Map),
{Src, Map1} = conv_fpreg(hipe_rtl:fmove_src(I), Map0),
I2 = mk_fmove(Dst, Src),
{I2, Map1, Data}.
mk_fmove(Dst, Src) ->
[hipe_ppc:mk_pseudo_fmove(Dst, Src)].
conv_fload(I, Map, Data) ->
%% Dst := MEM[Base+Off], where Dst is FP reg
{Dst, Map0} = conv_fpreg(hipe_rtl:fload_dst(I), Map),
{Base1, Map1} = conv_src(hipe_rtl:fload_src(I), Map0),
{Base2, Map2} = conv_src(hipe_rtl:fload_offset(I), Map1),
I2 = mk_fload(Dst, Base1, Base2),
{I2, Map2, Data}.
mk_fload(Dst, Base1, Base2) ->
case hipe_ppc:is_temp(Base1) of
true ->
case hipe_ppc:is_temp(Base2) of
true ->
mk_fload_rr(Dst, Base1, Base2);
_ ->
mk_fload_ri(Dst, Base1, Base2)
end;
_ ->
case hipe_ppc:is_temp(Base2) of
true ->
mk_fload_ri(Dst, Base2, Base1);
_ ->
mk_fload_ii(Dst, Base1, Base2)
end
end.
mk_fload_ii(Dst, Base1, Base2) ->
io:format("~w: RTL fload with two immediates\n", [?MODULE]),
Tmp = new_untagged_temp(),
mk_li(Tmp, Base1,
mk_fload_ri(Dst, Tmp, Base2)).
mk_fload_ri(Dst, Base, Disp) ->
hipe_ppc:mk_fload(Dst, Disp, Base, 'new').
mk_fload_rr(Dst, Base1, Base2) ->
[hipe_ppc:mk_lfdx(Dst, Base1, Base2)].
conv_fstore(I, Map, Data) ->
%% MEM[Base+Off] := Src, where Src is FP reg
{Base1, Map0} = conv_dst(hipe_rtl:fstore_base(I), Map),
{Src, Map1} = conv_fpreg(hipe_rtl:fstore_src(I), Map0),
{Base2, Map2} = conv_src(hipe_rtl:fstore_offset(I), Map1),
I2 = mk_fstore(Src, Base1, Base2),
{I2, Map2, Data}.
mk_fstore(Src, Base1, Base2) ->
case hipe_ppc:is_temp(Base2) of
true ->
mk_fstore_rr(Src, Base1, Base2);
_ ->
mk_fstore_ri(Src, Base1, Base2)
end.
mk_fstore_ri(Src, Base, Disp) ->
hipe_ppc:mk_fstore(Src, Disp, Base, 'new').
mk_fstore_rr(Src, Base1, Base2) ->
[hipe_ppc:mk_stfdx(Src, Base1, Base2)].
conv_fp_binary(I, Map, Data) ->
{Dst, Map0} = conv_fpreg(hipe_rtl:fp_dst(I), Map),
{Src1, Map1} = conv_fpreg(hipe_rtl:fp_src1(I), Map0),
{Src2, Map2} = conv_fpreg(hipe_rtl:fp_src2(I), Map1),
RtlFpOp = hipe_rtl:fp_op(I),
I2 = mk_fp_binary(Dst, Src1, RtlFpOp, Src2),
{I2, Map2, Data}.
mk_fp_binary(Dst, Src1, RtlFpOp, Src2) ->
FpBinOp =
case RtlFpOp of
'fadd' -> 'fadd';
'fdiv' -> 'fdiv';
'fmul' -> 'fmul';
'fsub' -> 'fsub'
end,
[hipe_ppc:mk_fp_binary(FpBinOp, Dst, Src1, Src2)].
conv_fp_unary(I, Map, Data) ->
{Dst, Map0} = conv_fpreg(hipe_rtl:fp_unop_dst(I), Map),
{Src, Map1} = conv_fpreg(hipe_rtl:fp_unop_src(I), Map0),
RtlFpUnOp = hipe_rtl:fp_unop_op(I),
I2 = mk_fp_unary(Dst, Src, RtlFpUnOp),
{I2, Map1, Data}.
mk_fp_unary(Dst, Src, RtlFpUnOp) ->
FpUnOp =
case RtlFpUnOp of
'fchs' -> 'fneg'
end,
[hipe_ppc:mk_fp_unary(FpUnOp, Dst, Src)].
conv_alu(I, Map, Data) ->
%% dst = src1 aluop src2
{Dst, Map0} = conv_dst(hipe_rtl:alu_dst(I), Map),
{Src1, Map1} = conv_src(hipe_rtl:alu_src1(I), Map0),
{Src2, Map2} = conv_src(hipe_rtl:alu_src2(I), Map1),
RtlAluOp = hipe_rtl:alu_op(I),
I2 = mk_alu(Dst, Src1, RtlAluOp, Src2),
{I2, Map2, Data}.
mk_alu(Dst, Src1, RtlAluOp, Src2) ->
case hipe_ppc:is_temp(Src1) of
true ->
case hipe_ppc:is_temp(Src2) of
true ->
mk_alu_rr(Dst, Src1, RtlAluOp, Src2);
_ ->
mk_alu_ri(Dst, Src1, RtlAluOp, Src2)
end;
_ ->
case hipe_ppc:is_temp(Src2) of
true ->
mk_alu_ir(Dst, Src1, RtlAluOp, Src2);
_ ->
mk_alu_ii(Dst, Src1, RtlAluOp, Src2)
end
end.
mk_alu_ii(Dst, Src1, RtlAluOp, Src2) ->
io:format("~w: RTL alu with two immediates (~w ~w ~w)\n",
[?MODULE, Src1, RtlAluOp, Src2]),
Tmp = new_untagged_temp(),
mk_li(Tmp, Src1,
mk_alu_ri(Dst, Tmp, RtlAluOp, Src2)).
mk_alu_ir(Dst, Src1, RtlAluOp, Src2) ->
case rtl_aluop_commutes(RtlAluOp) of
true ->
mk_alu_ri(Dst, Src2, RtlAluOp, Src1);
_ ->
Tmp = new_untagged_temp(),
mk_li(Tmp, Src1,
mk_alu_rr(Dst, Tmp, RtlAluOp, Src2))
end.
mk_alu_ri(Dst, Src1, RtlAluOp, Src2) ->
case RtlAluOp of
'sub' -> % there is no 'subi'
mk_alu_ri_addi(Dst, Src1, -Src2);
'add' -> % 'addi' has a 16-bit simm operand
mk_alu_ri_addi(Dst, Src1, Src2);
'mul' -> % 'mulli' has a 16-bit simm operand
mk_alu_ri_simm16(Dst, Src1, RtlAluOp, 'mulli', Src2);
'and' -> % 'andi.' has a 16-bit uimm operand
if Src2 band (bnot 16#ffffffff) =:= 0 ->
case rlwinm_mask(Src2) of
{MB,ME} ->
[hipe_ppc:mk_unary({'rlwinm',0,MB,ME}, Dst, Src1)];
_ ->
mk_alu_ri_bitop(Dst, Src1, RtlAluOp, 'andi.', Src2)
end;
true ->
mk_alu_ri_bitop(Dst, Src1, RtlAluOp, 'andi.', Src2)
end;
'or' -> % 'ori' has a 16-bit uimm operand
mk_alu_ri_bitop(Dst, Src1, RtlAluOp, 'ori', Src2);
'xor' -> % 'xori' has a 16-bit uimm operand
mk_alu_ri_bitop(Dst, Src1, RtlAluOp, 'xori', Src2);
_ -> % shift ops have 5-bit uimm operands
mk_alu_ri_shift(Dst, Src1, RtlAluOp, Src2)
end.
rlwinm_mask(Imm) ->
Res1 = rlwinm_mask2(Imm),
case Res1 of
{_MB,_ME} -> Res1;
[] ->
case rlwinm_mask2(bnot Imm) of
{MB,ME} -> {ME+1,MB-1};
[] -> []
end
end.
rlwinm_mask2(Imm) ->
case Imm band 16#ffffffff of
0 -> [];
Word ->
MB = lsb_log2(Word), % first 1 bit
case bnot(Word bsr MB) band 16#ffffffff of
0 -> []; % Imm was all-bits-one XXX: we should handle this
Word1 ->
ME1 = lsb_log2(Word1),% first 0 bit after the 1s
case Word bsr (MB+ME1) of
0 ->
ME = MB+ME1-1, % last 1 bit
{31-ME, 31-MB}; % convert to PPC sick and twisted bit numbers
_ ->
[]
end
end
end.
lsb_log2(Word) -> % PRE: Word =/= 0
bitN_log2(Word band -Word, 0).
bitN_log2(BitN, ShiftN) ->
if BitN > 16#ffff ->
bitN_log2(BitN bsr 16, ShiftN + 16);
true ->
ShiftN + hweight16(BitN - 1)
end.
hweight16(Word) -> % PRE: 0 <= Word <= 16#ffff
Res1 = (Word band 16#5555) + ((Word bsr 1) band 16#5555),
Res2 = (Res1 band 16#3333) + ((Res1 bsr 2) band 16#3333),
Res3 = (Res2 band 16#0F0F) + ((Res2 bsr 4) band 16#0F0F),
(Res3 band 16#00FF) + ((Res3 bsr 8) band 16#00FF).
mk_alu_ri_addi(Dst, Src1, Src2) ->
mk_alu_ri_simm16(Dst, Src1, 'add', 'addi', Src2).
mk_alu_ri_simm16(Dst, Src1, RtlAluOp, AluOp, Src2) ->
if is_integer(Src2), -32768 =< Src2, Src2 < 32768 ->
[hipe_ppc:mk_alu(AluOp, Dst, Src1,
hipe_ppc:mk_simm16(Src2))];
true ->
mk_alu_ri_rr(Dst, Src1, RtlAluOp, Src2)
end.
mk_alu_ri_bitop(Dst, Src1, RtlAluOp, AluOp, Src2) ->
if is_integer(Src2), 0 =< Src2, Src2 < 65536 ->
[hipe_ppc:mk_alu(AluOp, Dst, Src1,
hipe_ppc:mk_uimm16(Src2))];
true ->
mk_alu_ri_rr(Dst, Src1, RtlAluOp, Src2)
end.
mk_alu_ri_shift(Dst, Src1, RtlAluOp, Src2) ->
case get(hipe_target_arch) of
ppc64 ->
if Src2 < 64, Src2 >= 0 ->
AluOp =
case RtlAluOp of
'sll' -> 'sldi'; % alias for rldimi %%% buggy
'srl' -> 'srdi'; % alias for rldimi %%% buggy
'sra' -> 'sradi' %%% buggy
end,
[hipe_ppc:mk_alu(AluOp, Dst, Src1,
hipe_ppc:mk_uimm16(Src2))];
true ->
mk_alu_ri_rr(Dst, Src1, RtlAluOp, Src2)
end;
powerpc ->
if Src2 < 32, Src2 >= 0 ->
AluOp =
case RtlAluOp of
'sll' -> 'slwi'; % alias for rlwinm
'srl' -> 'srwi'; % alias for rlwinm
'sra' -> 'srawi'
end,
[hipe_ppc:mk_alu(AluOp, Dst, Src1,
hipe_ppc:mk_uimm16(Src2))];
true ->
mk_alu_ri_rr(Dst, Src1, RtlAluOp, Src2)
end
end.
mk_alu_ri_rr(Dst, Src1, RtlAluOp, Src2) ->
Tmp = new_untagged_temp(),
mk_li(Tmp, Src2,
mk_alu_rr(Dst, Src1, RtlAluOp, Tmp)).
mk_alu_rr(Dst, Src1, RtlAluOp, Src2) ->
case RtlAluOp of
'sub' -> % PPC weirdness
[hipe_ppc:mk_alu('subf', Dst, Src2, Src1)];
_ ->
AluOp =
case {get(hipe_target_arch), RtlAluOp} of
{_, 'add'} -> 'add';
{_, 'or'} -> 'or';
{_, 'and'} -> 'and';
{_, 'xor'} -> 'xor';
{powerpc, 'mul'} -> 'mullw';
{powerpc, 'sll'} -> 'slw';
{powerpc, 'srl'} -> 'srw';
{powerpc, 'sra'} -> 'sraw';
{ppc64, 'mul'} -> 'mulld';
{ppc64, 'sll'} -> 'sld';
{ppc64, 'srl'} -> 'srd';
{ppc64, 'sra'} -> 'srad'
end,
[hipe_ppc:mk_alu(AluOp, Dst, Src1, Src2)]
end.
conv_alub(I, Map, Data) ->
%% dst = src1 aluop src2; if COND goto label
HasDst = hipe_rtl:alub_has_dst(I),
{Src1, Map0} = conv_src(hipe_rtl:alub_src1(I), Map),
{Src2, Map1} = conv_src(hipe_rtl:alub_src2(I), Map0),
RtlAlubOp = hipe_rtl:alub_op(I),
RtlAlubCond = hipe_rtl:alub_cond(I),
case {HasDst, RtlAlubOp} of
{false, sub} ->
{BCond,Sign} = conv_branch_cond(RtlAlubCond),
I2 = mk_branch(Src1, BCond, Sign, Src2,
hipe_rtl:alub_true_label(I),
hipe_rtl:alub_false_label(I),
hipe_rtl:alub_pred(I)),
{I2, Map1, Data};
_ ->
{Dst, Map2} =
case HasDst of
false -> {new_untagged_temp(), Map1};
true -> conv_dst(hipe_rtl:alub_dst(I), Map1)
end,
{AluOp, BCond} =
case {RtlAlubOp, RtlAlubCond} of
{'add', 'ltu'} ->
{'addc', 'eq'};
{_, _} ->
{conv_alub_op(RtlAlubOp), conv_alub_cond(RtlAlubCond)}
end,
BC = mk_pseudo_bc(BCond,
hipe_rtl:alub_true_label(I),
hipe_rtl:alub_false_label(I),
hipe_rtl:alub_pred(I)),
I2 =
case {AluOp, BCond} of
{'addc', 'eq'} -> % copy XER[CA] to CR0[EQ] before the BC
TmpR = new_untagged_temp(),
[hipe_ppc:mk_mfspr(TmpR, 'xer'),
hipe_ppc:mk_mtcr(TmpR) |
BC];
_ -> BC
end,
{NewSrc1, NewSrc2} =
case AluOp of
'subf' -> {Src2, Src1};
_ -> {Src1, Src2}
end,
I1 = mk_alub(Dst, NewSrc1, AluOp, NewSrc2, BCond),
{I1 ++ I2, Map2, Data}
end.
conv_alub_op(RtlAluOp) ->
case {get(hipe_target_arch), RtlAluOp} of
{_, 'add'} -> 'add';
{_, 'sub'} -> 'subf'; % XXX: must swap operands
{_, 'or'} -> 'or';
{_, 'and'} -> 'and';
{_, 'xor'} -> 'xor';
{powerpc, 'mul'} -> 'mullw';
{powerpc, 'sll'} -> 'slw';
{powerpc, 'srl'} -> 'srw';
{powerpc, 'sra'} -> 'sraw';
{ppc64, 'mul'} -> 'mulld';
{ppc64, 'sll'} -> 'sld';
{ppc64, 'srl'} -> 'srd';
{ppc64, 'sra'} -> 'srad'
end.
aluop_commutes(AluOp) ->
case AluOp of
'add' -> true;
'addc' -> true;
'subf' -> false;
'mullw' -> true;
'or' -> true;
'and' -> true;
'xor' -> true;
'slw' -> false;
'srw' -> false;
'sraw' -> false;
'mulld' -> true; % ppc64
'sld' -> false; % ppc64
'srd' -> false; % ppc64
'srad' -> false % ppc64
end.
conv_alub_cond(Cond) -> % only signed
case Cond of
eq -> 'eq';
ne -> 'ne';
gt -> 'gt';
ge -> 'ge';
lt -> 'lt';
le -> 'le';
overflow -> 'so';
not_overflow -> 'ns';
_ -> exit({?MODULE,conv_alub_cond,Cond})
end.
mk_alub(Dst, Src1, AluOp, Src2, BCond) ->
case hipe_ppc:is_temp(Src1) of
true ->
case hipe_ppc:is_temp(Src2) of
true ->
mk_alub_rr(Dst, Src1, AluOp, Src2, BCond);
_ ->
mk_alub_ri(Dst, Src1, AluOp, Src2, BCond)
end;
_ ->
case hipe_ppc:is_temp(Src2) of
true ->
mk_alub_ir(Dst, Src1, AluOp, Src2, BCond);
_ ->
mk_alub_ii(Dst, Src1, AluOp, Src2, BCond)
end
end.
mk_alub_ii(Dst, Src1, AluOp, Src2, BCond) ->
io:format("~w: RTL alub with two immediates\n", [?MODULE]),
Tmp = new_untagged_temp(),
mk_li(Tmp, Src1,
mk_alub_ri(Dst, Tmp, AluOp, Src2, BCond)).
mk_alub_ir(Dst, Src1, AluOp, Src2, BCond) ->
case aluop_commutes(AluOp) of
true ->
mk_alub_ri(Dst, Src2, AluOp, Src1, BCond);
_ ->
Tmp = new_untagged_temp(),
mk_li(Tmp, Src1,
mk_alub_rr(Dst, Tmp, AluOp, Src2, BCond))
end.
mk_alub_ri(Dst, Src1, AluOp, Src2, BCond) ->
true = is_integer(Src2),
case BCond of
'so' -> mk_alub_ri_OE(Dst, Src1, AluOp, Src2);
'ns' -> mk_alub_ri_OE(Dst, Src1, AluOp, Src2);
_ -> mk_alub_ri_Rc(Dst, Src1, AluOp, Src2)
end.
mk_alub_ri_OE(Dst, Src1, AluOp, Src2) ->
%% Only 'add', 'subf', and 'mullw' apply here, and 'subf' becomes 'add'.
%% 'add' and 'mullw' have no immediate+Rc+OE forms.
%% Rewrite to reg/reg form. Sigh.
Tmp = new_untagged_temp(),
mk_li(Tmp, Src2,
mk_alub_rr_OE(Dst, Src1, AluOp, Tmp)).
mk_alub_ri_Rc(Dst, Src1, AluOp, Src2) ->
case AluOp of
'subf' -> % there is no 'subfi.', use 'addic.' or 'add.'
mk_alub_ri_Rc_addi(Dst, Src1, -Src2, 'addic.', 'add.');
'add' -> % 'addic.' has a 16-bit simm operand
mk_alub_ri_Rc_addi(Dst, Src1, Src2, 'addic.', 'add.');
'addc' -> % 'addic' has a 16-bit simm operand
mk_alub_ri_Rc_addi(Dst, Src1, Src2, 'addic', 'addc');
'mullw' -> % there is no 'mulli.'
mk_alub_ri_Rc_rr(Dst, Src1, 'mullw.', Src2);
'mulld' -> % there is no 'mulli.'
mk_alub_ri_Rc_rr(Dst, Src1, 'mulld.', Src2);
'or' -> % there is no 'ori.'
mk_alub_ri_Rc_rr(Dst, Src1, 'or.', Src2);
'xor' -> % there is no 'xori.'
mk_alub_ri_Rc_rr(Dst, Src1, 'xor.', Src2);
'and' -> % 'andi.' has a 16-bit uimm operand
if
Src2 band (bnot 16#ffffffff) =:= 0 ->
case rlwinm_mask(Src2) of
{MB,ME} ->
[hipe_ppc:mk_unary({'rlwinm.',0,MB,ME}, Dst, Src1)];
_ ->
mk_alub_ri_Rc_andi(Dst, Src1, Src2)
end;
true ->
mk_alub_ri_Rc_andi(Dst, Src1, Src2)
end;
_ -> % shift ops have 5-bit uimm operands
mk_alub_ri_Rc_shift(Dst, Src1, AluOp, Src2)
end.
mk_alub_ri_Rc_addi(Dst, Src1, Src2, AddImmOp, AddRegOp) ->
if is_integer(Src2), -32768 =< Src2, Src2 < 32768 ->
[hipe_ppc:mk_alu(AddImmOp, Dst, Src1,
hipe_ppc:mk_simm16(Src2))];
true ->
mk_alub_ri_Rc_rr(Dst, Src1, AddRegOp, Src2)
end.
mk_alub_ri_Rc_andi(Dst, Src1, Src2) ->
if Src2 < 65536, Src2 >= 0 ->
[hipe_ppc:mk_alu('andi.', Dst, Src1,
hipe_ppc:mk_uimm16(Src2))];
true ->
mk_alub_ri_Rc_rr(Dst, Src1, 'and.', Src2)
end.
mk_alub_ri_Rc_shift(Dst, Src1, AluOp, Src2) ->
{AluOpIDot, MaxIShift} =
case AluOp of
'slw' -> {'slwi.', 32}; % alias for rlwinm.
'srw' -> {'srwi.', 32}; % alias for rlwinm.
'sraw' -> {'srawi.', 32};
'sld' -> {'sldi.', 64};
'srd' -> {'srdi.', 64};
'srad' -> {'sradi.', 64}
end,
if Src2 < MaxIShift, Src2 >= 0 ->
[hipe_ppc:mk_alu(AluOpIDot, Dst, Src1,
hipe_ppc:mk_uimm16(Src2))];
true ->
AluOpDot =
case AluOp of
'slw' -> 'slw.';
'srw' -> 'srw.';
'sraw' -> 'sraw.';
'sld' -> 'sld.';
'srd' -> 'srd.';
'srad' -> 'srad.'
end,
mk_alub_ri_Rc_rr(Dst, Src1, AluOpDot, Src2)
end.
mk_alub_ri_Rc_rr(Dst, Src1, AluOp, Src2) ->
Tmp = new_untagged_temp(),
mk_li(Tmp, Src2,
[hipe_ppc:mk_alu(AluOp, Dst, Src1, Tmp)]).
mk_alub_rr(Dst, Src1, AluOp, Src2, BCond) ->
case BCond of
'so' -> mk_alub_rr_OE(Dst, Src1, AluOp, Src2);
'ns' -> mk_alub_rr_OE(Dst, Src1, AluOp, Src2);
_ -> mk_alub_rr_Rc(Dst, Src1, AluOp, Src2)
end.
mk_alub_rr_OE(Dst, Src1, AluOp, Src2) ->
AluOpODot =
case AluOp of
'subf' -> 'subfo.';
'add' -> 'addo.';
'mullw' -> 'mullwo.';
'mulld' -> 'mulldo.'
%% fail for addc, or, and, xor, slw, srw, sraw
end,
[hipe_ppc:mk_alu(AluOpODot, Dst, Src1, Src2)].
mk_alub_rr_Rc(Dst, Src1, AluOp, Src2) ->
AluOpDot =
case AluOp of
'subf' -> 'subf.';
'add' -> 'add.';
'addc' -> 'addc'; % only interested in CA, no Rc needed
'mullw' -> 'mullw.';
'mulld' -> 'mulld.';
'or' -> 'or.';
'and' -> 'and.';
'xor' -> 'xor.';
'slw' -> 'slw.';
'sld' -> 'sld.';
'srw' -> 'srw.';
'srd' -> 'srd.';
'sraw' -> 'sraw.';
'srad' -> 'srad.'
end,
[hipe_ppc:mk_alu(AluOpDot, Dst, Src1, Src2)].
conv_branch_cond(Cond) -> % may be unsigned
case Cond of
gtu -> {'gt', 'unsigned'};
geu -> {'ge', 'unsigned'};
ltu -> {'lt', 'unsigned'};
leu -> {'le', 'unsigned'};
_ -> {conv_alub_cond(Cond), 'signed'}
end.
mk_branch(Src1, BCond, Sign, Src2, TrueLab, FalseLab, Pred) ->
case hipe_ppc:is_temp(Src1) of
true ->
case hipe_ppc:is_temp(Src2) of
true ->
mk_branch_rr(Src1, BCond, Sign, Src2, TrueLab, FalseLab, Pred);
_ ->
mk_branch_ri(Src1, BCond, Sign, Src2, TrueLab, FalseLab, Pred)
end;
_ ->
case hipe_ppc:is_temp(Src2) of
true ->
NewBCond = commute_bcond(BCond),
mk_branch_ri(Src2, NewBCond, Sign, Src1, TrueLab, FalseLab, Pred);
_ ->
mk_branch_ii(Src1, BCond, Sign, Src2, TrueLab, FalseLab, Pred)
end
end.
commute_bcond(BCond) -> % if x BCond y, then y commute_bcond(BCond) x
case BCond of
'eq' -> 'eq'; % ==, ==
'ne' -> 'ne'; % !=, !=
'gt' -> 'lt'; % >, <
'ge' -> 'le'; % >=, <=
'lt' -> 'gt'; % <, >
'le' -> 'ge'; % <=, >=
%% so/ns: n/a
_ -> exit({?MODULE,commute_bcond,BCond})
end.
mk_branch_ii(Src1, BCond, Sign, Src2, TrueLab, FalseLab, Pred) ->
io:format("~w: RTL branch with two immediates\n", [?MODULE]),
Tmp = new_untagged_temp(),
mk_li(Tmp, Src1,
mk_branch_ri(Tmp, BCond, Sign, Src2,
TrueLab, FalseLab, Pred)).
mk_branch_ri(Src1, BCond, Sign, Src2, TrueLab, FalseLab, Pred) ->
{FixSrc2,NewSrc2,CmpOp} =
case Sign of
'signed' ->
if is_integer(Src2), -32768 =< Src2, Src2 < 32768 ->
{[], hipe_ppc:mk_simm16(Src2), hipe_ppc:cmpiop_word()};
true ->
Tmp = new_untagged_temp(),
{mk_li(Tmp, Src2), Tmp, hipe_ppc:cmpop_word()}
end;
'unsigned' ->
if is_integer(Src2), 0 =< Src2, Src2 < 65536 ->
{[], hipe_ppc:mk_uimm16(Src2), hipe_ppc:cmpliop_word()};
true ->
Tmp = new_untagged_temp(),
{mk_li(Tmp, Src2), Tmp, hipe_ppc:cmplop_word()}
end
end,
FixSrc2 ++
mk_cmp_bc(CmpOp, Src1, NewSrc2, BCond, TrueLab, FalseLab, Pred).
mk_branch_rr(Src1, BCond, Sign, Src2, TrueLab, FalseLab, Pred) ->
CmpOp =
case Sign of
'signed' -> hipe_ppc:cmpop_word();
'unsigned' -> hipe_ppc:cmplop_word()
end,
mk_cmp_bc(CmpOp, Src1, Src2, BCond, TrueLab, FalseLab, Pred).
mk_cmp_bc(CmpOp, Src1, Src2, BCond, TrueLab, FalseLab, Pred) ->
[hipe_ppc:mk_cmp(CmpOp, Src1, Src2) |
mk_pseudo_bc(BCond, TrueLab, FalseLab, Pred)].
conv_call(I, Map, Data) ->
{Args, Map0} = conv_src_list(hipe_rtl:call_arglist(I), Map),
{Dsts, Map1} = conv_dst_list(hipe_rtl:call_dstlist(I), Map0),
{Fun, Map2} = conv_fun(hipe_rtl:call_fun(I), Map1),
ContLab = hipe_rtl:call_continuation(I),
ExnLab = hipe_rtl:call_fail(I),
Linkage = hipe_rtl:call_type(I),
I2 = mk_call(Dsts, Fun, Args, ContLab, ExnLab, Linkage),
{I2, Map2, Data}.
mk_call(Dsts, Fun, Args, ContLab, ExnLab, Linkage) ->
case hipe_ppc:is_prim(Fun) of
true ->
mk_primop_call(Dsts, Fun, Args, ContLab, ExnLab, Linkage);
false ->
mk_general_call(Dsts, Fun, Args, ContLab, ExnLab, Linkage)
end.
mk_primop_call(Dsts, Prim, Args, ContLab, ExnLab, Linkage) ->
case hipe_ppc:prim_prim(Prim) of
'extsh' ->
mk_extsh_call(Dsts, Args, ContLab, ExnLab, Linkage);
'lhbrx' ->
mk_lhbrx_call(Dsts, Args, ContLab, ExnLab, Linkage);
'lwbrx' ->
mk_lwbrx_call(Dsts, Args, ContLab, ExnLab, Linkage);
_ ->
mk_general_call(Dsts, Prim, Args, ContLab, ExnLab, Linkage)
end.
mk_extsh_call([Dst], [Src], [], [], not_remote) ->
true = hipe_ppc:is_temp(Src),
[hipe_ppc:mk_unary('extsh', Dst, Src)].
mk_lhbrx_call(Dsts, [Base,Offset], [], [], not_remote) ->
case Dsts of
[Dst] -> mk_loadx('lhbrx', Dst, Base, Offset);
[] -> [] % result unused, cancel the operation
end.
mk_lwbrx_call([Dst], [Base,Offset], [], [], not_remote) ->
mk_loadx('lwbrx', Dst, Base, Offset).
mk_loadx(LdxOp, Dst, Base, Offset) ->
true = hipe_ppc:is_temp(Base),
{FixOff,NewOff} =
case hipe_ppc:is_temp(Offset) of
true -> {[], Offset};
false ->
Tmp = new_untagged_temp(),
{mk_li(Tmp, Offset), Tmp}
end,
FixOff ++ [hipe_ppc:mk_loadx(LdxOp, Dst, Base, NewOff)].
mk_general_call(Dsts, Fun, Args, ContLab, ExnLab, Linkage) ->
%% The backend does not support pseudo_calls without a
%% continuation label, so we make sure each call has one.
{RealContLab, Tail} =
case mk_call_results(Dsts) of
[] ->
%% Avoid consing up a dummy basic block if the moves list
%% is empty, as is typical for calls to suspend/0.
%% This should be subsumed by a general "optimise the CFG"
%% module, and could probably be removed.
case ContLab of
[] ->
NewContLab = hipe_gensym:get_next_label(ppc),
{NewContLab, [hipe_ppc:mk_label(NewContLab)]};
_ ->
{ContLab, []}
end;
Moves ->
%% Change the call to continue at a new basic block.
%% In this block move the result registers to the Dsts,
%% then continue at the call's original continuation.
NewContLab = hipe_gensym:get_next_label(ppc),
case ContLab of
[] ->
%% This is just a fallthrough
%% No jump back after the moves.
{NewContLab,
[hipe_ppc:mk_label(NewContLab) |
Moves]};
_ ->
%% The call has a continuation. Jump to it.
{NewContLab,
[hipe_ppc:mk_label(NewContLab) |
Moves ++
[hipe_ppc:mk_b_label(ContLab)]]}
end
end,
SDesc = hipe_ppc:mk_sdesc(ExnLab, 0, length(Args), {}),
{FixFunC,FunC} = fix_func(Fun),
CallInsn = hipe_ppc:mk_pseudo_call(FunC, SDesc, RealContLab, Linkage),
{RegArgs,StkArgs} = split_args(Args),
FixFunC ++
mk_push_args(StkArgs, move_actuals(RegArgs, [CallInsn | Tail])).
mk_call_results([]) ->
[];
mk_call_results([Dst]) ->
RV = hipe_ppc:mk_temp(hipe_ppc_registers:return_value(), 'tagged'),
[hipe_ppc:mk_pseudo_move(Dst, RV)];
mk_call_results(Dsts) ->
exit({?MODULE,mk_call_results,Dsts}).
fix_func(Fun) ->
case hipe_ppc:is_temp(Fun) of
true -> {[hipe_ppc:mk_mtspr('ctr', Fun)], 'ctr'};
_ -> {[], Fun}
end.
mk_push_args(StkArgs, Tail) ->
case length(StkArgs) of
0 ->
Tail;
NrStkArgs ->
[hipe_ppc:mk_pseudo_call_prepare(NrStkArgs) |
mk_store_args(StkArgs, NrStkArgs * word_size(), Tail)]
end.
mk_store_args([Arg|Args], PrevOffset, Tail) ->
Offset = PrevOffset - word_size(),
{Src,FixSrc} =
case hipe_ppc:is_temp(Arg) of
true ->
{Arg, []};
_ ->
Tmp = new_tagged_temp(),
{Tmp, mk_li(Tmp, Arg)}
end,
Store = hipe_ppc:mk_store(hipe_ppc:stop_word(), Src, Offset, mk_sp()),
mk_store_args(Args, Offset, FixSrc ++ [Store | Tail]);
mk_store_args([], _, Tail) ->
Tail.
conv_comment(I, Map, Data) ->
I2 = [hipe_ppc:mk_comment(hipe_rtl:comment_text(I))],
{I2, Map, Data}.
conv_enter(I, Map, Data) ->
{Args, Map0} = conv_src_list(hipe_rtl:enter_arglist(I), Map),
{Fun, Map1} = conv_fun(hipe_rtl:enter_fun(I), Map0),
I2 = mk_enter(Fun, Args, hipe_rtl:enter_type(I)),
{I2, Map1, Data}.
mk_enter(Fun, Args, Linkage) ->
{FixFunC,FunC} = fix_func(Fun),
Arity = length(Args),
{RegArgs,StkArgs} = split_args(Args),
FixFunC ++
move_actuals(RegArgs,
[hipe_ppc:mk_pseudo_tailcall_prepare(),
hipe_ppc:mk_pseudo_tailcall(FunC, Arity, StkArgs, Linkage)]).
conv_goto(I, Map, Data) ->
I2 = [hipe_ppc:mk_b_label(hipe_rtl:goto_label(I))],
{I2, Map, Data}.
conv_label(I, Map, Data) ->
I2 = [hipe_ppc:mk_label(hipe_rtl:label_name(I))],
{I2, Map, Data}.
conv_load(I, Map, Data) ->
{Dst, Map0} = conv_dst(hipe_rtl:load_dst(I), Map),
{Base1, Map1} = conv_src(hipe_rtl:load_src(I), Map0),
{Base2, Map2} = conv_src(hipe_rtl:load_offset(I), Map1),
LoadSize = hipe_rtl:load_size(I),
LoadSign = hipe_rtl:load_sign(I),
I2 = mk_load(Dst, Base1, Base2, LoadSize, LoadSign),
{I2, Map2, Data}.
mk_load(Dst, Base1, Base2, LoadSize, LoadSign) ->
{LdOp, Rest} =
case {LoadSize, LoadSign} of
{byte, signed} -> {'lbz', [hipe_ppc:mk_unary('extsb', Dst, Dst)]};
{byte, unsigned} -> {'lbz', []};
{int16, signed} -> {'lha', []};
{int16, unsigned} -> {'lhz', []};
{int32, signed} ->
case get(hipe_target_arch) of
powerpc -> {'lwz', []};
ppc64 -> {'lwa', []}
end;
{int32, unsigned} -> {'lwz', []};
{word, _} -> {hipe_ppc:ldop_word(), []}
end,
case hipe_ppc:is_temp(Base1) of
true ->
case hipe_ppc:is_temp(Base2) of
true ->
mk_load_rr(Dst, Base1, Base2, LdOp, Rest);
_ ->
mk_load_ri(Dst, Base1, Base2, LdOp, Rest)
end;
_ ->
case hipe_ppc:is_temp(Base2) of
true ->
mk_load_ri(Dst, Base2, Base1, LdOp, Rest);
_ ->
mk_load_ii(Dst, Base1, Base2, LdOp, Rest)
end
end.
mk_load_ii(Dst, Base1, Base2, LdOp, Rest) ->
io:format("~w: RTL load with two immediates\n", [?MODULE]),
Tmp = new_untagged_temp(),
mk_li(Tmp, Base1,
mk_load_ri(Dst, Tmp, Base2, LdOp, Rest)).
mk_load_ri(Dst, Base, Disp, LdOp, Rest) ->
hipe_ppc:mk_load(LdOp, Dst, Disp, Base, 'new', Rest).
mk_load_rr(Dst, Base1, Base2, LdOp, Rest) ->
LdxOp = hipe_ppc:ldop_to_ldxop(LdOp),
[hipe_ppc:mk_loadx(LdxOp, Dst, Base1, Base2) | Rest].
conv_load_address(I, Map, Data) ->
{Dst, Map0} = conv_dst(hipe_rtl:load_address_dst(I), Map),
Addr = hipe_rtl:load_address_addr(I),
Type = hipe_rtl:load_address_type(I),
Src = {Addr,Type},
I2 = [hipe_ppc:mk_pseudo_li(Dst, Src)],
{I2, Map0, Data}.
conv_load_atom(I, Map, Data) ->
{Dst, Map0} = conv_dst(hipe_rtl:load_atom_dst(I), Map),
Src = hipe_rtl:load_atom_atom(I),
I2 = [hipe_ppc:mk_pseudo_li(Dst, Src)],
{I2, Map0, Data}.
conv_move(I, Map, Data) ->
{Dst, Map0} = conv_dst(hipe_rtl:move_dst(I), Map),
{Src, Map1} = conv_src(hipe_rtl:move_src(I), Map0),
I2 = mk_move(Dst, Src, []),
{I2, Map1, Data}.
mk_move(Dst, Src, Tail) ->
case hipe_ppc:is_temp(Src) of
true -> [hipe_ppc:mk_pseudo_move(Dst, Src) | Tail];
_ -> mk_li(Dst, Src, Tail)
end.
conv_return(I, Map, Data) ->
%% TODO: multiple-value returns
{[Arg], Map0} = conv_src_list(hipe_rtl:return_varlist(I), Map),
I2 = mk_move(mk_rv(), Arg,
[hipe_ppc:mk_blr()]),
{I2, Map0, Data}.
conv_store(I, Map, Data) ->
{Base1, Map0} = conv_src(hipe_rtl:store_base(I), Map),
{Src, Map1} = conv_src(hipe_rtl:store_src(I), Map0),
{Base2, Map2} = conv_src(hipe_rtl:store_offset(I), Map1),
StoreSize = hipe_rtl:store_size(I),
I2 = mk_store(Src, Base1, Base2, StoreSize),
{I2, Map2, Data}.
mk_store(Src, Base1, Base2, StoreSize) ->
StOp =
case StoreSize of
byte -> 'stb';
int16 -> 'sth';
int32 -> 'stw';
word -> hipe_ppc:stop_word()
end,
case hipe_ppc:is_temp(Src) of
true ->
mk_store2(Src, Base1, Base2, StOp);
_ ->
Tmp = new_untagged_temp(),
mk_li(Tmp, Src,
mk_store2(Tmp, Base1, Base2, StOp))
end.
mk_store2(Src, Base1, Base2, StOp) ->
case hipe_ppc:is_temp(Base1) of
true ->
case hipe_ppc:is_temp(Base2) of
true ->
mk_store_rr(Src, Base1, Base2, StOp);
_ ->
mk_store_ri(Src, Base1, Base2, StOp)
end;
_ ->
case hipe_ppc:is_temp(Base2) of
true ->
mk_store_ri(Src, Base2, Base1, StOp);
_ ->
mk_store_ii(Src, Base1, Base2, StOp)
end
end.
mk_store_ii(Src, Base, Disp, StOp) ->
Tmp = new_untagged_temp(),
mk_li(Tmp, Base,
mk_store_ri(Src, Tmp, Disp, StOp)).
mk_store_ri(Src, Base, Disp, StOp) ->
hipe_ppc:mk_store(StOp, Src, Disp, Base, 'new', []).
mk_store_rr(Src, Base1, Base2, StOp) ->
StxOp = hipe_ppc:stop_to_stxop(StOp),
[hipe_ppc:mk_storex(StxOp, Src, Base1, Base2)].
conv_switch(I, Map, Data) ->
Labels = hipe_rtl:switch_labels(I),
LMap = [{label,L} || L <- Labels],
{NewData, JTabLab} =
case hipe_rtl:switch_sort_order(I) of
[] ->
hipe_consttab:insert_block(Data, word, LMap);
SortOrder ->
hipe_consttab:insert_sorted_block(
Data, word, LMap, SortOrder)
end,
%% no immediates allowed here
{IndexR, Map1} = conv_dst(hipe_rtl:switch_src(I), Map),
JTabR = new_untagged_temp(),
OffsetR = new_untagged_temp(),
DestR = new_untagged_temp(),
ShiftInstruction =
case get(hipe_target_arch) of
powerpc -> 'slwi';
ppc64 -> 'sldi'
end,
I2 =
[hipe_ppc:mk_pseudo_li(JTabR, {JTabLab,constant}),
hipe_ppc:mk_alu(ShiftInstruction, OffsetR, IndexR,
hipe_ppc:mk_uimm16(log2_word_size())),
hipe_ppc:mk_loadx(hipe_ppc:ldop_wordx(), DestR, JTabR, OffsetR),
hipe_ppc:mk_mtspr('ctr', DestR),
hipe_ppc:mk_bctr(Labels)],
{I2, Map1, NewData}.
%%% Create a conditional branch.
%%% If the condition tests CR0[SO], rewrite the path
%%% corresponding to SO being set to clear XER[SO].
mk_pseudo_bc(BCond, TrueLabel, FalseLabel, Pred) ->
case BCond of
'so' ->
NewTrueLabel = hipe_gensym:get_next_label(ppc),
ZeroR = new_untagged_temp(),
[hipe_ppc:mk_pseudo_bc(BCond, NewTrueLabel, FalseLabel, Pred),
hipe_ppc:mk_label(NewTrueLabel) |
mk_li(ZeroR, 0,
[hipe_ppc:mk_mtspr('xer', ZeroR),
hipe_ppc:mk_b_label(TrueLabel)])];
'ns' ->
NewFalseLabel = hipe_gensym:get_next_label(ppc),
ZeroR = new_untagged_temp(),
[hipe_ppc:mk_pseudo_bc(BCond, TrueLabel, NewFalseLabel, Pred),
hipe_ppc:mk_label(NewFalseLabel) |
mk_li(ZeroR, 0,
[hipe_ppc:mk_mtspr('xer', ZeroR),
hipe_ppc:mk_b_label(FalseLabel)])];
_ ->
[hipe_ppc:mk_pseudo_bc(BCond, TrueLabel, FalseLabel, Pred)]
end.
%%% Load an integer constant into a register.
mk_li(Dst, Value) -> mk_li(Dst, Value, []).
mk_li(Dst, Value, Tail) ->
hipe_ppc:mk_li(Dst, Value, Tail).
%%% Check if an RTL ALU or ALUB operator commutes.
rtl_aluop_commutes(RtlAluOp) ->
case RtlAluOp of
'add' -> true;
'mul' -> true;
'or' -> true;
'and' -> true;
'xor' -> true;
_ -> false
end.
%%% Split a list of formal or actual parameters into the
%%% part passed in registers and the part passed on the stack.
%%% The parameters passed in registers are also tagged with
%%% the corresponding registers.
split_args(Args) ->
split_args(0, hipe_ppc_registers:nr_args(), Args, []).
split_args(I, N, [Arg|Args], RegArgs) when I < N ->
Reg = hipe_ppc_registers:arg(I),
Temp = hipe_ppc:mk_temp(Reg, 'tagged'),
split_args(I+1, N, Args, [{Arg,Temp}|RegArgs]);
split_args(_, _, StkArgs, RegArgs) ->
{RegArgs, StkArgs}.
%%% Convert a list of actual parameters passed in
%%% registers (from split_args/1) to a list of moves.
move_actuals([{Src,Dst}|Actuals], Rest) ->
move_actuals(Actuals, mk_move(Dst, Src, Rest));
move_actuals([], Rest) ->
Rest.
%%% Convert a list of formal parameters passed in
%%% registers (from split_args/1) to a list of moves.
move_formals([{Dst,Src}|Formals], Rest) ->
move_formals(Formals, [hipe_ppc:mk_pseudo_move(Dst, Src) | Rest]);
move_formals([], Rest) ->
Rest.
%%% Convert a 'fun' operand (MFA, prim, or temp)
conv_fun(Fun, Map) ->
case hipe_rtl:is_var(Fun) of
true ->
conv_dst(Fun, Map);
false ->
case hipe_rtl:is_reg(Fun) of
true ->
conv_dst(Fun, Map);
false ->
if is_atom(Fun) ->
{hipe_ppc:mk_prim(Fun), Map};
true ->
{conv_mfa(Fun), Map}
end
end
end.
%%% Convert an MFA operand.
conv_mfa({M,F,A}) when is_atom(M), is_atom(F), is_integer(A) ->
hipe_ppc:mk_mfa(M, F, A).
%%% Convert an RTL source operand (imm/var/reg).
%%% Returns a temp or a naked integer.
conv_src(Opnd, Map) ->
case hipe_rtl:is_imm(Opnd) of
true ->
Value = hipe_rtl:imm_value(Opnd),
if is_integer(Value) ->
{Value, Map}
end;
false ->
conv_dst(Opnd, Map)
end.
conv_src_list([O|Os], Map) ->
{V, Map1} = conv_src(O, Map),
{Vs, Map2} = conv_src_list(Os, Map1),
{[V|Vs], Map2};
conv_src_list([], Map) ->
{[], Map}.
%%% Convert an RTL destination operand (var/reg).
conv_fpreg(Opnd, Map) ->
case hipe_rtl:is_fpreg(Opnd) of
true -> conv_dst(Opnd, Map)
end.
conv_dst(Opnd, Map) ->
{Name, Type} =
case hipe_rtl:is_var(Opnd) of
true ->
{hipe_rtl:var_index(Opnd), 'tagged'};
false ->
case hipe_rtl:is_fpreg(Opnd) of
true ->
{hipe_rtl:fpreg_index(Opnd), 'double'};
false ->
{hipe_rtl:reg_index(Opnd), 'untagged'}
end
end,
IsPrecoloured =
case Type of
'double' -> hipe_ppc_registers:is_precoloured_fpr(Name);
_ -> hipe_ppc_registers:is_precoloured_gpr(Name)
end,
case IsPrecoloured of
true ->
{hipe_ppc:mk_temp(Name, Type), Map};
false ->
case vmap_lookup(Map, Opnd) of
{value, NewTemp} ->
{NewTemp, Map};
_ ->
NewTemp = hipe_ppc:mk_new_temp(Type),
{NewTemp, vmap_bind(Map, Opnd, NewTemp)}
end
end.
conv_dst_list([O|Os], Map) ->
{Dst, Map1} = conv_dst(O, Map),
{Dsts, Map2} = conv_dst_list(Os, Map1),
{[Dst|Dsts], Map2};
conv_dst_list([], Map) ->
{[], Map}.
conv_formals(Os, Map) ->
conv_formals(hipe_ppc_registers:nr_args(), Os, Map, []).
conv_formals(N, [O|Os], Map, Res) ->
Type =
case hipe_rtl:is_var(O) of
true -> 'tagged';
_ -> 'untagged'
end,
Dst =
if N > 0 -> hipe_ppc:mk_new_temp(Type); % allocatable
true -> hipe_ppc:mk_new_nonallocatable_temp(Type)
end,
Map1 = vmap_bind(Map, O, Dst),
conv_formals(N-1, Os, Map1, [Dst|Res]);
conv_formals(_, [], Map, Res) ->
{lists:reverse(Res), Map}.
%%% Create a temp representing the stack pointer register.
mk_sp() ->
hipe_ppc:mk_temp(hipe_ppc_registers:stack_pointer(), 'untagged').
%%% Create a temp representing the return value register.
mk_rv() ->
hipe_ppc:mk_temp(hipe_ppc_registers:return_value(), 'tagged').
%%% new_untagged_temp -- conjure up an untagged scratch reg
new_untagged_temp() ->
hipe_ppc:mk_new_temp('untagged').
%%% new_tagged_temp -- conjure up a tagged scratch reg
new_tagged_temp() ->
hipe_ppc:mk_new_temp('tagged').
%%% Map from RTL var/reg operands to temps.
vmap_empty() ->
gb_trees:empty().
vmap_lookup(Map, Key) ->
gb_trees:lookup(Key, Map).
vmap_bind(Map, Key, Val) ->
gb_trees:insert(Key, Val, Map).
word_size() ->
hipe_rtl_arch:word_size().
log2_word_size() ->
hipe_rtl_arch:log2_word_size().