aboutsummaryrefslogtreecommitdiffstats
path: root/lib/hipe/arm/hipe_arm.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/hipe/arm/hipe_arm.erl')
-rw-r--r--lib/hipe/arm/hipe_arm.erl380
1 files changed, 380 insertions, 0 deletions
diff --git a/lib/hipe/arm/hipe_arm.erl b/lib/hipe/arm/hipe_arm.erl
new file mode 100644
index 0000000000..391f84ca47
--- /dev/null
+++ b/lib/hipe/arm/hipe_arm.erl
@@ -0,0 +1,380 @@
+%%% -*- erlang-indent-level: 2 -*-
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(hipe_arm).
+-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_mfa/3,
+
+ mk_prim/1,
+ is_prim/1,
+ prim_prim/1,
+
+ mk_sdesc/4,
+
+ mk_am2/3,
+ mk_am3/3,
+
+ mk_alu/5,
+
+ mk_b_fun/2,
+
+ mk_b_label/2,
+ mk_b_label/1,
+
+ mk_bl/3,
+
+ mk_blx/2,
+
+ mk_cmp/3,
+
+ mk_comment/1,
+
+ mk_label/1,
+ is_label/1,
+ label_label/1,
+
+ mk_load/3,
+ mk_load/6,
+
+ mk_ldrsb/2,
+
+ mk_move/3,
+ mk_move/2,
+
+ mk_pseudo_bc/4,
+
+ mk_pseudo_call/4,
+ pseudo_call_contlab/1,
+ pseudo_call_funv/1,
+ pseudo_call_sdesc/1,
+ pseudo_call_linkage/1,
+
+ mk_pseudo_call_prepare/1,
+ pseudo_call_prepare_nrstkargs/1,
+
+ mk_pseudo_li/2,
+
+ mk_pseudo_move/2,
+ is_pseudo_move/1,
+ pseudo_move_dst/1,
+ pseudo_move_src/1,
+
+ mk_pseudo_switch/3,
+
+ mk_pseudo_tailcall/4,
+ pseudo_tailcall_funv/1,
+ pseudo_tailcall_stkargs/1,
+ pseudo_tailcall_linkage/1,
+
+ mk_pseudo_tailcall_prepare/0,
+
+ mk_smull/4,
+
+ mk_store/3,
+ mk_store/6,
+
+ mk_pseudo_blr/0,
+ mk_bx/1,
+ mk_mflr/1,
+ mk_mtlr/1,
+ mk_lr/0,
+ mk_pc/0,
+
+ mk_li/2,
+ mk_li/3,
+
+ mk_addi/4,
+
+ try_aluop_imm/2,
+
+ mk_defun/8,
+ defun_mfa/1,
+ defun_formals/1,
+ defun_is_closure/1,
+ defun_is_leaf/1,
+ defun_code/1,
+ defun_data/1,
+ defun_var_range/1
+ ]).
+
+-include("hipe_arm.hrl").
+
+mk_temp(Reg, Type, Allocatable) ->
+ #arm_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(arm), 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 #arm_temp{} -> true; _ -> false end.
+temp_reg(#arm_temp{reg=Reg}) -> Reg.
+temp_type(#arm_temp{type=Type}) -> Type.
+temp_is_allocatable(#arm_temp{allocatable=A}) -> A.
+temp_is_precoloured(#arm_temp{reg=Reg}) ->
+ hipe_arm_registers:is_precoloured_gpr(Reg).
+
+mk_mfa(M, F, A) -> #arm_mfa{m=M, f=F, a=A}.
+
+mk_prim(Prim) -> #arm_prim{prim=Prim}.
+is_prim(X) -> case X of #arm_prim{} -> true; _ -> false end.
+prim_prim(#arm_prim{prim=Prim}) -> Prim.
+
+mk_am2(Src, Sign, Offset) -> #am2{src=Src, sign=Sign, offset=Offset}.
+mk_am3(Src, Sign, Offset) -> #am3{src=Src, sign=Sign, offset=Offset}.
+
+mk_alu(AluOp, S, Dst, Src, Am1) ->
+ #alu{aluop=AluOp, s=S, dst=Dst, src=Src, am1=Am1}.
+mk_alu(AluOp, Dst, Src, Am1) -> mk_alu(AluOp, false, Dst, Src, Am1).
+
+mk_b_fun(Fun, Linkage) -> #b_fun{'fun'=Fun, linkage=Linkage}.
+
+mk_b_label(Cond, Label) -> #b_label{'cond'=Cond, label=Label}.
+mk_b_label(Label) -> mk_b_label('al', Label).
+
+mk_bl(Fun, SDesc, Linkage) -> #bl{'fun'=Fun, sdesc=SDesc, linkage=Linkage}.
+
+mk_blx(Src, SDesc) -> #blx{src=Src, sdesc=SDesc}.
+
+mk_cmp(CmpOp, Src, Am1) -> #cmp{cmpop=CmpOp, src=Src, am1=Am1}.
+
+mk_sdesc(ExnLab, FSize, Arity, Live) ->
+ #arm_sdesc{exnlab=ExnLab, fsize=FSize, arity=Arity, live=Live}.
+
+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_load(LdOp, Dst, Am2) -> #load{ldop=LdOp, dst=Dst, am2=Am2}.
+
+mk_load(LdOp, Dst, Base, Offset, Scratch, Rest) when is_integer(Offset) ->
+ {Sign,AbsOffset} =
+ if Offset < 0 -> {'-', -Offset};
+ true -> {'+', Offset}
+ end,
+ if AbsOffset =< 4095 ->
+ Am2 = #am2{src=Base,sign=Sign,offset=AbsOffset},
+ [mk_load(LdOp, Dst, Am2) | Rest];
+ true ->
+ Index =
+ begin
+ DstReg = temp_reg(Dst),
+ BaseReg = temp_reg(Base),
+ if DstReg =/= BaseReg -> Dst;
+ true -> mk_scratch(Scratch)
+ end
+ end,
+ Am2 = #am2{src=Base,sign=Sign,offset=Index},
+ mk_li(Index, AbsOffset,
+ [mk_load(LdOp, Dst, Am2) | Rest])
+ end.
+
+mk_scratch(Scratch) ->
+ case Scratch of
+ 'temp2' -> mk_temp(hipe_arm_registers:temp2(), 'untagged');
+ 'new' -> mk_new_temp('untagged')
+ end.
+
+mk_ldrsb(Dst, Am3) -> #ldrsb{dst=Dst, am3=Am3}.
+
+mk_move(MovOp, S, Dst, Am1) -> #move{movop=MovOp, s=S, dst=Dst, am1=Am1}.
+mk_move(S, Dst, Am1) -> mk_move('mov', S, Dst, Am1).
+mk_move(Dst, Am1) -> mk_move('mov', false, Dst, Am1).
+
+mk_pseudo_bc(Cond, TrueLab, FalseLab, Pred) ->
+ if Pred >= 0.5 ->
+ mk_pseudo_bc_simple(negate_cond(Cond), FalseLab,
+ TrueLab, 1.0-Pred);
+ true ->
+ mk_pseudo_bc_simple(Cond, TrueLab, FalseLab, Pred)
+ end.
+
+mk_pseudo_bc_simple(Cond, TrueLab, FalseLab, Pred) when Pred =< 0.5 ->
+ #pseudo_bc{'cond'=Cond, true_label=TrueLab,
+ false_label=FalseLab, pred=Pred}.
+
+negate_cond(Cond) ->
+ case Cond of
+ 'lt' -> 'ge'; % <, >=
+ 'ge' -> 'lt'; % >=, <
+ 'gt' -> 'le'; % >, <=
+ 'le' -> 'gt'; % <=, >
+ 'eq' -> 'ne'; % ==, !=
+ 'ne' -> 'eq'; % !=, ==
+ 'hi' -> 'ls'; % >u, <=u
+ 'ls' -> 'hi'; % <=u, >u
+ 'hs' -> 'lo'; % >=u, <u
+ 'lo' -> 'hs'; % <u, >=u
+ 'vs' -> 'vc'; % overflow, not_overflow
+ 'vc' -> 'vs' % not_overflow, overflow
+ end.
+
+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_sdesc(#pseudo_call{sdesc=SDesc}) -> SDesc.
+pseudo_call_contlab(#pseudo_call{contlab=ContLab}) -> ContLab.
+pseudo_call_linkage(#pseudo_call{linkage=Linkage}) -> Linkage.
+
+mk_pseudo_call_prepare(NrStkArgs) ->
+ #pseudo_call_prepare{nrstkargs=NrStkArgs}.
+pseudo_call_prepare_nrstkargs(#pseudo_call_prepare{nrstkargs=NrStkArgs}) ->
+ NrStkArgs.
+
+mk_pseudo_li(Dst, Imm) ->
+ #pseudo_li{dst=Dst, imm=Imm, label=hipe_gensym:get_next_label(arm)}.
+
+mk_pseudo_move(Dst, Src) -> #pseudo_move{dst=Dst, src=Src}.
+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_switch(JTab, Index, Labels) ->
+ #pseudo_switch{jtab=JTab, index=Index, labels=Labels}.
+
+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_stkargs(#pseudo_tailcall{stkargs=StkArgs}) -> StkArgs.
+pseudo_tailcall_linkage(#pseudo_tailcall{linkage=Linkage}) -> Linkage.
+
+mk_pseudo_tailcall_prepare() -> #pseudo_tailcall_prepare{}.
+
+mk_smull(DstLo, DstHi, Src1, Src2) ->
+ #smull{dstlo=DstLo, dsthi=DstHi, src1=Src1, src2=Src2}.
+
+mk_store(StOp, Src, Am2) -> #store{stop=StOp, src=Src, am2=Am2}.
+
+mk_store(StOp, Src, Base, Offset, Scratch, Rest) when is_integer(Offset) ->
+ {Sign,AbsOffset} =
+ if Offset < 0 -> {'-', -Offset};
+ true -> {'+', Offset}
+ end,
+ if AbsOffset =< 4095 ->
+ Am2 = #am2{src=Base,sign=Sign,offset=AbsOffset},
+ [mk_store(StOp, Src, Am2) | Rest];
+ true ->
+ Index = mk_scratch(Scratch),
+ Am2 = #am2{src=Base,sign=Sign,offset=Index},
+ mk_li(Index, AbsOffset,
+ [mk_store(StOp, Src, Am2) | Rest])
+ end.
+
+mk_pseudo_blr() -> #pseudo_blr{}.
+mk_bx(Src) -> #pseudo_bx{src=Src}.
+mk_mflr(Dst) -> mk_move(Dst, mk_lr()).
+mk_mtlr(Src) -> mk_move(mk_lr(), Src).
+mk_lr() -> mk_temp(hipe_arm_registers:lr(), 'untagged').
+mk_pc() -> mk_temp(hipe_arm_registers:pc(), 'untagged').
+
+%%% Load an integer constant into a register.
+mk_li(Dst, Value) -> mk_li(Dst, Value, []).
+
+mk_li(Dst, Value, Rest) ->
+ %% XXX: expand to handle 2-instruction sequences
+ case try_aluop_imm('mov', Value) of
+ {NewMovOp,Am1} ->
+ [mk_move(NewMovOp, false, Dst, Am1) | Rest];
+ [] ->
+ [mk_pseudo_li(Dst, Value) | Rest]
+ end.
+
+%%% Add an integer constant. Dst may equal Src,
+%%% in which case temp2 may be clobbered.
+
+mk_addi(Dst, Src, Value, Rest) ->
+ case try_aluop_imm('add', Value) of
+ {NewAluOp,Am1} ->
+ [mk_alu(NewAluOp, Dst, Src, Am1) | Rest];
+ [] ->
+ Tmp =
+ begin
+ DstReg = temp_reg(Dst),
+ SrcReg = temp_reg(Src),
+ if DstReg =:= SrcReg ->
+ mk_temp(hipe_arm_registers:temp2(), 'untagged');
+ true -> Dst
+ end
+ end,
+ [mk_pseudo_li(Tmp, Value), mk_alu('add', Dst, Src, Tmp) | Rest]
+ end.
+
+try_aluop_imm(AluOp, Imm) -> % -> {NewAluOp,Am1} or []
+ case imm_to_am1(Imm) of
+ (Am1={_Imm8,_Imm4}) -> {AluOp, Am1};
+ [] ->
+ case invert_aluop_imm(AluOp, Imm) of
+ {NewAluOp,NewImm} ->
+ case imm_to_am1(NewImm) of
+ (Am1={_Imm8,_Imm4}) -> {NewAluOp, Am1};
+ [] -> []
+ end;
+ [] -> []
+ end
+ end.
+
+invert_aluop_imm(AluOp, Imm) ->
+ case AluOp of
+ 'mov' -> {'mvn', bnot Imm};
+ 'mvn' -> {'mov', bnot Imm};
+ 'cmp' -> {'cmn', -Imm};
+ 'cmn' -> {'cmp', -Imm};
+ 'and' -> {'bic', bnot Imm};
+ 'bic' -> {'and', bnot Imm};
+ 'orr' -> {'orn', bnot Imm};
+ 'orn' -> {'orr', bnot Imm};
+ 'add' -> {'sub', -Imm};
+ 'sub' -> {'add', -Imm};
+ _ -> [] % no inverted form available
+ end.
+
+imm_to_am1(Imm) -> imm_to_am1(Imm band 16#FFFFFFFF, 16).
+imm_to_am1(Imm, RotCnt) ->
+ if Imm >= 0, Imm =< 255 -> {Imm, RotCnt band 15};
+ true ->
+ NewRotCnt = RotCnt - 1,
+ if NewRotCnt =:= 0 -> []; % full circle, no joy
+ true ->
+ NewImm = (Imm bsr 2) bor ((Imm band 3) bsl 30),
+ imm_to_am1(NewImm, NewRotCnt)
+ end
+ 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_mfa(#defun{mfa=MFA}) -> MFA.
+defun_formals(#defun{formals=Formals}) -> Formals.
+defun_is_closure(#defun{isclosure=IsClosure}) -> IsClosure.
+defun_is_leaf(#defun{isleaf=IsLeaf}) -> IsLeaf.
+defun_code(#defun{code=Code}) -> Code.
+defun_data(#defun{data=Data}) -> Data.
+defun_var_range(#defun{var_range=VarRange}) -> VarRange.