%% -*- 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.
-module(hipe_sparc).
-export([
mk_temp/2,
mk_new_temp/1,
mk_new_nonallocatable_temp/1,
is_temp/1,
temp_reg/1,
temp_type/1,
temp_is_allocatable/1,
temp_is_precoloured/1,
mk_g0/0,
mk_ra/0,
mk_rv/0,
mk_sp/0,
mk_temp1/0,
mk_temp2/0,
mk_simm13/1,
mk_uimm5/1,
mk_mfa/3,
mk_prim/1,
is_prim/1,
prim_prim/1,
mk_sdesc/4,
mk_alu/4,
mk_mov/2,
mk_load/6,
mk_bp/3,
mk_b_label/1,
%% mk_br/4,
mk_call_rec/3,
mk_call_tail/2,
mk_comment/1,
mk_label/1,
is_label/1,
label_label/1,
mk_jmp/3,
mk_jmpl/2,
mk_pseudo_bp/4,
negate_cond/1,
%% mk_pseudo_br/5,
%% negate_rcond/1,
mk_pseudo_call/4,
pseudo_call_contlab/1,
pseudo_call_funv/1,
pseudo_call_linkage/1,
pseudo_call_sdesc/1,
mk_pseudo_call_prepare/1,
pseudo_call_prepare_nrstkargs/1,
mk_pseudo_move/2,
is_pseudo_move/1,
pseudo_move_dst/1,
pseudo_move_src/1,
mk_pseudo_ret/0,
mk_pseudo_set/2,
mk_pseudo_spill_move/3,
is_pseudo_spill_move/1,
mk_pseudo_tailcall/4,
pseudo_tailcall_funv/1,
pseudo_tailcall_linkage/1,
pseudo_tailcall_stkargs/1,
mk_pseudo_tailcall_prepare/0,
mk_rdy/1,
%% mk_sethi/2,
mk_nop/0,
mk_set/2,
mk_set/3,
mk_addi/4,
mk_store/4,
mk_store/6,
mk_fp_binary/4,
mk_fp_unary/3,
mk_pseudo_fload/4,
mk_fload/4,
mk_pseudo_fmove/2,
is_pseudo_fmove/1,
pseudo_fmove_src/1,
pseudo_fmove_dst/1,
mk_pseudo_spill_fmove/3,
is_pseudo_spill_fmove/1,
mk_pseudo_fstore/3,
mk_fstore/4,
mk_defun/8,
defun_code/1,
defun_data/1,
defun_formals/1,
defun_is_closure/1,
defun_is_leaf/1,
defun_mfa/1,
defun_var_range/1
]).
-include("hipe_sparc.hrl").
mk_temp(Reg, Type, Allocatable) ->
#sparc_temp{reg=Reg, type=Type, allocatable=Allocatable}.
mk_temp(Reg, Type) -> mk_temp(Reg, Type, true).
mk_new_temp(Type, Allocatable) ->
mk_temp(hipe_gensym:get_next_var(sparc), Type, Allocatable).
mk_new_temp(Type) -> mk_new_temp(Type, true).
mk_new_nonallocatable_temp(Type) -> mk_new_temp(Type, false).
is_temp(X) -> case X of #sparc_temp{} -> true; _ -> false end.
temp_reg(#sparc_temp{reg=Reg}) -> Reg.
temp_type(#sparc_temp{type=Type}) -> Type.
temp_is_allocatable(#sparc_temp{allocatable=A}) -> A.
temp_is_precoloured(#sparc_temp{reg=Reg,type=Type}) ->
case Type of
%% 'double' -> hipe_sparc_registers:is_precoloured_fpr(Reg);
_ -> hipe_sparc_registers:is_precoloured_gpr(Reg)
end.
mk_g0() -> mk_temp(hipe_sparc_registers:g0(), 'untagged').
mk_ra() -> mk_temp(hipe_sparc_registers:return_address(), 'untagged').
mk_rv() -> mk_temp(hipe_sparc_registers:return_value(), 'tagged').
mk_sp() -> mk_temp(hipe_sparc_registers:stack_pointer(), 'untagged').
mk_temp1() -> mk_temp(hipe_sparc_registers:temp1(), 'untagged').
mk_temp2() -> mk_temp(hipe_sparc_registers:temp2(), 'untagged').
mk_simm13(Value) -> #sparc_simm13{value=Value}.
mk_uimm5(Value) -> #sparc_uimm5{value=Value}.
mk_uimm22(Value) -> #sparc_uimm22{value=Value}.
mk_mfa(M, F, A) -> #sparc_mfa{m=M, f=F, a=A}.
mk_prim(Prim) -> #sparc_prim{prim=Prim}.
is_prim(X) -> case X of #sparc_prim{} -> true; _ -> false end.
prim_prim(#sparc_prim{prim=Prim}) -> Prim.
mk_sdesc(ExnLab, FSize, Arity, Live) ->
#sparc_sdesc{exnlab=ExnLab, fsize=FSize, arity=Arity, live=Live}.
mk_alu(AluOp, Src1, Src2, Dst) ->
#alu{aluop=AluOp, src1=Src1, src2=Src2, dst=Dst}.
mk_mov(Src, Dst) -> mk_alu('or', mk_g0(), Src, Dst).
mk_bp(Cond, Label, Pred) -> #bp{'cond'=Cond, label=Label, pred=Pred}.
mk_b_label(Label) -> mk_bp('a', Label, 1.0).
-ifdef(notdef). % XXX: only for sparc64, alas
mk_br(RCond, Src, Label, Pred) ->
#br{rcond=RCond, src=Src, label=Label, pred=Pred}.
-endif.
mk_call_rec(Fun, SDesc, Linkage) ->
#call_rec{'fun'=Fun, sdesc=SDesc, linkage=Linkage}.
mk_call_tail(Fun, Linkage) -> #call_tail{'fun'=Fun, linkage=Linkage}.
mk_comment(Term) -> #comment{term=Term}.
mk_label(Label) -> #label{label=Label}.
is_label(I) -> case I of #label{} -> true; _ -> false end.
label_label(#label{label=Label}) -> Label.
mk_jmp(Src1, Src2, Labels) -> #jmp{src1=Src1, src2=Src2, labels=Labels}.
mk_jmpl(Src, SDesc) -> #jmpl{src=Src, sdesc=SDesc}.
mk_pseudo_bp(Cond, TrueLab, FalseLab, Pred) ->
if Pred >= 0.5 ->
mk_pseudo_bp_simple(negate_cond(Cond), FalseLab,
TrueLab, 1.0-Pred);
true ->
mk_pseudo_bp_simple(Cond, TrueLab, FalseLab, Pred)
end.
mk_pseudo_bp_simple(Cond, TrueLab, FalseLab, Pred) when Pred =< 0.5 ->
#pseudo_bp{'cond'=Cond, true_label=TrueLab,
false_label=FalseLab, pred=Pred}.
negate_cond(Cond) ->
case Cond of
'l' -> 'ge'; % <, >=
'ge' -> 'l'; % >=, <
'g' -> 'le'; % >, <=
'le' -> 'g'; % <=, >
'e' -> 'ne'; % ==, !=
'ne' -> 'e'; % !=, ==
'gu' -> 'leu'; % >u, <=u
'leu'-> 'gu'; % <=u, >u
'geu'-> 'lu'; % >=u, <u
'lu' -> 'geu'; % <u, >=u
'vs' -> 'vc'; % overflow, not_overflow
'vc' -> 'vs' % not_overflow, overflow
end.
-ifdef(notdef). % XXX: only for sparc64, alas
mk_pseudo_br(RCond, Src, TrueLab, FalseLab, Pred) ->
if Pred >= 0.5 ->
mk_pseudo_br_simple(negate_rcond(RCond), Src, FalseLab,
TrueLab, 1.0-Pred);
true ->
mk_pseudo_br_simple(RCond, Src, TrueLab, FalseLab, Pred)
end.
mk_pseudo_br_simple(RCond, Src, TrueLab, FalseLab, Pred) when Pred =< 0.5 ->
#pseudo_br{rcond=RCond, src=Src, true_label=TrueLab,
false_label=FalseLab, pred=Pred}.
negate_rcond(RCond) ->
case RCond of
'z' -> 'nz'; % ==, !=
'nz' -> 'z'; % !=, ==
'gz' -> 'lez'; % >, <=
'lez' -> 'gz'; % <=, >
'gez' -> 'lz'; % >=, <
'lz' -> 'gez' % <, >=
end.
-endif.
mk_pseudo_call(FunV, SDesc, ContLab, Linkage) ->
#pseudo_call{funv=FunV, sdesc=SDesc, contlab=ContLab, linkage=Linkage}.
pseudo_call_funv(#pseudo_call{funv=FunV}) -> FunV.
pseudo_call_contlab(#pseudo_call{contlab=ContLab}) -> ContLab.
pseudo_call_linkage(#pseudo_call{linkage=Linkage}) -> Linkage.
pseudo_call_sdesc(#pseudo_call{sdesc=SDesc}) -> SDesc.
mk_pseudo_call_prepare(NrStkArgs) ->
#pseudo_call_prepare{nrstkargs=NrStkArgs}.
pseudo_call_prepare_nrstkargs(#pseudo_call_prepare{nrstkargs=NrStkArgs}) ->
NrStkArgs.
mk_pseudo_move(Src, Dst) -> #pseudo_move{src=Src, dst=Dst}.
is_pseudo_move(I) -> case I of #pseudo_move{} -> true; _ -> false end.
pseudo_move_dst(#pseudo_move{dst=Dst}) -> Dst.
pseudo_move_src(#pseudo_move{src=Src}) -> Src.
mk_pseudo_ret() -> #pseudo_ret{}.
mk_pseudo_set(Imm, Dst) -> #pseudo_set{imm=Imm, dst=Dst}.
mk_pseudo_spill_move(Src, Temp, Dst) ->
#pseudo_spill_move{src=Src, temp=Temp, dst=Dst}.
is_pseudo_spill_move(I) -> is_record(I, pseudo_spill_move).
mk_pseudo_tailcall(FunV, Arity, StkArgs, Linkage) ->
#pseudo_tailcall{funv=FunV, arity=Arity, stkargs=StkArgs, linkage=Linkage}.
pseudo_tailcall_funv(#pseudo_tailcall{funv=FunV}) -> FunV.
pseudo_tailcall_linkage(#pseudo_tailcall{linkage=Linkage}) -> Linkage.
pseudo_tailcall_stkargs(#pseudo_tailcall{stkargs=StkArgs}) -> StkArgs.
mk_pseudo_tailcall_prepare() -> #pseudo_tailcall_prepare{}.
mk_rdy(Dst) -> #rdy{dst=Dst}.
mk_sethi(UImm22, Dst) -> #sethi{uimm22=UImm22, dst=Dst}.
mk_nop() -> mk_sethi(mk_uimm22(0), mk_g0()).
%%% Load an integer constant into a register.
mk_set(Value, Dst) -> mk_set(Value, Dst, []).
mk_set(Value, Dst, Tail) ->
if -4096 =< Value, Value < 4096 ->
[mk_alu('or', mk_g0(), mk_simm13(Value), Dst) | Tail];
true ->
Hi22 = mk_uimm22((Value bsr 10) band 16#003FFFFF),
case (Value band 16#3FF) of
0 ->
[mk_sethi(Hi22, Dst) | Tail];
Lo10 ->
[mk_sethi(Hi22, Dst),
mk_alu('or', Dst, mk_simm13(Lo10), Dst) |
Tail]
end
end.
%%% Add an integer constant. Dst may equal Src,
%%% in which case temp2 may be clobbered.
mk_addi(Src, Value, Dst, Tail) ->
if -4096 =< Value, Value < 4096 ->
[mk_alu('add', Src, mk_simm13(Value), Dst) | Tail];
true ->
Tmp =
begin
DstReg = temp_reg(Dst),
SrcReg = temp_reg(Src),
if DstReg =:= SrcReg -> mk_temp2();
true -> Dst
end
end,
mk_set(Value, Tmp, [mk_alu('add', Src, Tmp, Dst) | Tail])
end.
mk_store(StOp, Src, Base, Disp) ->
#store{stop=StOp, src=Src, base=Base, disp=Disp}.
mk_store(StOp, Src, Base, Offset, Scratch, Rest) when is_integer(Offset) ->
if -4096 =< Offset, Offset < 4096 ->
[mk_store(StOp, Src, Base, mk_simm13(Offset)) | Rest];
true ->
Index = mk_scratch(Scratch),
mk_set(Offset, Index, [mk_store(StOp, Src, Base, Index) | Rest])
end.
mk_load(LdOp, Base, Disp, Dst) ->
mk_alu(LdOp, Base, Disp, Dst).
mk_load(LdOp, Base, Offset, Dst, Scratch, Rest) when is_integer(Offset) ->
if -4096 =< Offset, Offset < 4096 ->
[mk_load(LdOp, Base, mk_simm13(Offset), Dst) | Rest];
true ->
Index =
begin
DstReg = temp_reg(Dst),
BaseReg = temp_reg(Base),
if DstReg =/= BaseReg -> Dst;
true -> mk_scratch(Scratch)
end
end,
mk_set(Offset, Index, [mk_load(LdOp, Base, Index, Dst) | Rest])
end.
mk_scratch(Scratch) ->
case Scratch of
'temp2' -> mk_temp2();
'new' -> mk_new_temp('untagged')
end.
mk_fp_binary(FpBinOp, Src1, Src2, Dst) ->
#fp_binary{fp_binop=FpBinOp, src1=Src1, src2=Src2, dst=Dst}.
mk_fp_unary(FpUnOp, Src, Dst) -> #fp_unary{fp_unop=FpUnOp, src=Src, dst=Dst}.
mk_pseudo_fload(Base, Disp, Dst, IsSingle) ->
#pseudo_fload{base=Base, disp=Disp, dst=Dst, is_single=IsSingle}.
mk_fload(Base, Disp, Dst, Scratch) when is_integer(Disp) ->
if -4096 =< Disp, Disp < (4096-4) ->
[mk_pseudo_fload(Base, mk_simm13(Disp), Dst, false)];
true ->
Tmp = mk_scratch(Scratch),
mk_set(Disp, Tmp,
[mk_alu('add', Tmp, Base, Tmp),
mk_pseudo_fload(Tmp, mk_simm13(0), Dst, false)])
end.
mk_pseudo_fmove(Src, Dst) -> #pseudo_fmove{src=Src, dst=Dst}.
is_pseudo_fmove(I) -> case I of #pseudo_fmove{} -> true; _ -> false end.
pseudo_fmove_src(#pseudo_fmove{src=Src}) -> Src.
pseudo_fmove_dst(#pseudo_fmove{dst=Dst}) -> Dst.
mk_pseudo_spill_fmove(Src, Temp, Dst) ->
#pseudo_spill_fmove{src=Src, temp=Temp, dst=Dst}.
is_pseudo_spill_fmove(I) -> is_record(I, pseudo_spill_fmove).
mk_pseudo_fstore(Src, Base, Disp) ->
#pseudo_fstore{src=Src, base=Base, disp=Disp}.
mk_fstore(Src, Base, Disp, Scratch) when is_integer(Disp) ->
if -4096 =< Disp, Disp < (4096-4) ->
[mk_pseudo_fstore(Src, Base, hipe_sparc:mk_simm13(Disp))];
true ->
Tmp = mk_scratch(Scratch),
mk_set(Disp, Tmp,
[mk_alu('add', Tmp, Base, Tmp),
mk_pseudo_fstore(Src, Tmp, mk_simm13(0))])
end.
mk_defun(MFA, Formals, IsClosure, IsLeaf, Code, Data, VarRange, LabelRange) ->
#defun{mfa=MFA, formals=Formals, code=Code, data=Data,
isclosure=IsClosure, isleaf=IsLeaf,
var_range=VarRange, label_range=LabelRange}.
defun_code(#defun{code=Code}) -> Code.
defun_data(#defun{data=Data}) -> Data.
defun_formals(#defun{formals=Formals}) -> Formals.
defun_is_closure(#defun{isclosure=IsClosure}) -> IsClosure.
defun_is_leaf(#defun{isleaf=IsLeaf}) -> IsLeaf.
defun_mfa(#defun{mfa=MFA}) -> MFA.
defun_var_range(#defun{var_range=VarRange}) -> VarRange.