aboutsummaryrefslogtreecommitdiffstats
path: root/lib/hipe/arm
diff options
context:
space:
mode:
Diffstat (limited to 'lib/hipe/arm')
-rw-r--r--lib/hipe/arm/Makefile116
-rw-r--r--lib/hipe/arm/TODO20
-rw-r--r--lib/hipe/arm/hipe_arm.erl380
-rw-r--r--lib/hipe/arm/hipe_arm.hrl124
-rw-r--r--lib/hipe/arm/hipe_arm_assemble.erl665
-rw-r--r--lib/hipe/arm/hipe_arm_cfg.erl131
-rw-r--r--lib/hipe/arm/hipe_arm_defuse.erl157
-rw-r--r--lib/hipe/arm/hipe_arm_encode.erl994
-rw-r--r--lib/hipe/arm/hipe_arm_finalise.erl73
-rw-r--r--lib/hipe/arm/hipe_arm_frame.erl639
-rw-r--r--lib/hipe/arm/hipe_arm_liveness_gpr.erl38
-rw-r--r--lib/hipe/arm/hipe_arm_main.erl58
-rw-r--r--lib/hipe/arm/hipe_arm_pp.erl351
-rw-r--r--lib/hipe/arm/hipe_arm_ra.erl56
-rw-r--r--lib/hipe/arm/hipe_arm_ra_finalise.erl285
-rw-r--r--lib/hipe/arm/hipe_arm_ra_ls.erl56
-rw-r--r--lib/hipe/arm/hipe_arm_ra_naive.erl29
-rw-r--r--lib/hipe/arm/hipe_arm_ra_postconditions.erl278
-rw-r--r--lib/hipe/arm/hipe_arm_registers.erl207
-rw-r--r--lib/hipe/arm/hipe_rtl_to_arm.erl836
20 files changed, 5493 insertions, 0 deletions
diff --git a/lib/hipe/arm/Makefile b/lib/hipe/arm/Makefile
new file mode 100644
index 0000000000..571a1da0fc
--- /dev/null
+++ b/lib/hipe/arm/Makefile
@@ -0,0 +1,116 @@
+#
+# %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%
+#
+
+
+ifndef EBIN
+EBIN = ../ebin
+endif
+
+ifndef DOCS
+DOCS = ../doc
+endif
+
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Application version
+# ----------------------------------------------------
+include ../vsn.mk
+VSN=$(HIPE_VSN)
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/lib/hipe-$(VSN)
+
+# ----------------------------------------------------
+# Target Specs
+# ----------------------------------------------------
+# Please keep this list sorted.
+MODULES=hipe_arm \
+ hipe_arm_assemble \
+ hipe_arm_cfg \
+ hipe_arm_defuse \
+ hipe_arm_encode \
+ hipe_arm_finalise \
+ hipe_arm_frame \
+ hipe_arm_liveness_gpr \
+ hipe_arm_main \
+ hipe_arm_pp \
+ hipe_arm_ra \
+ hipe_arm_ra_finalise \
+ hipe_arm_ra_ls \
+ hipe_arm_ra_naive \
+ hipe_arm_ra_postconditions \
+ hipe_arm_registers \
+ hipe_rtl_to_arm
+
+HRL_FILES=hipe_arm.hrl
+ERL_FILES=$(MODULES:%=%.erl)
+TARGET_FILES=$(MODULES:%=$(EBIN)/%.$(EMULATOR))
+DOC_FILES= $(MODULES:%=$(DOCS)/%.html)
+
+# ----------------------------------------------------
+# FLAGS
+# ----------------------------------------------------
+
+include ../native.mk
+
+ERL_COMPILE_FLAGS += +warn_exported_vars
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+
+debug opt: $(TARGET_FILES)
+
+docs: $(DOC_FILES)
+
+clean:
+ rm -f $(TARGET_FILES)
+ rm -f core
+
+$(DOCS)/%.html:%.erl
+ erl -noshell -run edoc_run file '"$<"' '[{dir, "$(DOCS)"}]' -s init stop
+
+# ----------------------------------------------------
+# Special Build Targets
+# ----------------------------------------------------
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec: opt
+ $(INSTALL_DIR) $(RELSYSDIR)/ebin
+ $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin
+
+release_docs_spec:
+
+# Please keep this list sorted.
+$(EBIN)/hipe_arm_assemble.beam: ../main/hipe.hrl ../../kernel/src/hipe_ext_format.hrl ../rtl/hipe_literals.hrl
+$(EBIN)/hipe_arm_cfg.beam: ../flow/cfg.hrl ../flow/cfg.inc
+$(EBIN)/hipe_arm_frame.beam: ../rtl/hipe_literals.hrl
+$(EBIN)/hipe_arm_liveness_gpr.beam: ../flow/liveness.inc
+$(EBIN)/hipe_arm_registers.beam: ../rtl/hipe_literals.hrl
+$(EBIN)/hipe_rtl_to_arm.beam: ../rtl/hipe_rtl.hrl
+
+$(TARGET_FILES): hipe_arm.hrl ../misc/hipe_consttab.hrl
diff --git a/lib/hipe/arm/TODO b/lib/hipe/arm/TODO
new file mode 100644
index 0000000000..546d22737a
--- /dev/null
+++ b/lib/hipe/arm/TODO
@@ -0,0 +1,20 @@
+Assembler:
+
+Peephole optimiser:
+- Could e.g. turn "ldr lr,[sp,#OFF]; mov pc,lr"
+ into "ldr pc,[sp#OFF]", but then the LR save slot must
+ be in the caller's frame not the callee's.
+- Also kill "mov r0,r0" which seems to occur often.
+
+hipe_arm:
+- Handle more non-trivial immediates in mk_li/mk_load/mk_store.
+ See e.g. big_list, which has many 11-bit character constants.
+
+Floating point:
+- Drop no_inline_fp. Implement FP ops as calls to C or ASM
+ primops. All FP values passed by reference in memory.
+ This should at least reduce consing costs.
+
+Linear scan:
+- Do not hardcode temp1/temp2/temp3. Instead just take three
+ regs from (All\Fixed)\Params. (Ditto in PowerPC.)
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.
diff --git a/lib/hipe/arm/hipe_arm.hrl b/lib/hipe/arm/hipe_arm.hrl
new file mode 100644
index 0000000000..9ee2cb3d06
--- /dev/null
+++ b/lib/hipe/arm/hipe_arm.hrl
@@ -0,0 +1,124 @@
+%% -*- 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%
+%%
+
+%%%--------------------------------------------------------------------
+%%% Basic Values:
+%%%
+%%% temp ::= #arm_temp{reg, type, allocatable}
+%%% reg ::= <token from hipe_arm_registers>
+%%% type ::= tagged | untagged
+%%% allocatable ::= true | false
+%%%
+%%% sdesc ::= #arm_sdesc{exnlab, fsize, arity, live}
+%%% exnlab ::= [] | label
+%%% fsize ::= int32 (frame size in words)
+%%% live ::= <tuple of int32> (word offsets)
+%%% arity ::= uint8
+%%%
+%%% mfa ::= #arm_mfa{atom, atom, arity}
+%%% prim ::= #arm_prim{atom}
+
+-record(arm_mfa, {m::atom(), f::atom(), a::arity()}).
+-record(arm_prim, {prim}).
+-record(arm_sdesc, {exnlab, fsize, arity::arity(), live}).
+-record(arm_temp, {reg, type, allocatable}).
+
+%%% Instruction Operands:
+%%%
+%%% aluop ::= adc | add | and | bic | eor | orr | rsb | rsc | sbc | sub
+%%% cmpop ::= cmn | cmp | tst | teq (alu with s flag and no dst)
+%%% cond ::= eq | ne | hs | lo | mi | pl | vs | vc | hi | ls | ge | lt | gt | le | al
+%%% ldop ::= ldr | ldrb (am2)
+%%% movop ::= mov | mvn (alu with no src)
+%%% stop ::= str | strb (am2)
+%%%
+%%% dst ::= temp
+%%% src ::= temp
+%%%
+%%% s ::= true | false
+%%%
+%%% imm<N> ::= <an N-bit non-negative integer>
+%%%
+%%% Note: am1 represents all 11 variants of "Adressing Mode 1".
+%%%
+%%% am1 ::= {imm8,imm4} imm8 rotated right 2*imm4 bits
+%%% | src
+%%% | {src,rrx}
+%%% | {src,shiftop,imm5}
+%%% | {src,shiftop,src}
+%%% shiftop ::= lsl | lsr | asr | ror
+%%%
+%%% Note: am2 can represent the first 3 variants of "Addressing Mode 2",
+%%% i.e., not the pre- or post-indexed variants.
+%%%
+%%% am2 ::= #am2{src, sign, am2offset}
+%%% am2offset ::= imm12 | src | {src,rrx} | {src,shiftop,imm5}
+%%% sign ::= + | -
+%%%
+%%% Note: am3 can represent the first 2 variants of "Addressing Mode 3",
+%%% i.e., not the pre- or post-indexed variants.
+%%%
+%%% am3 ::= #am3{src, sign, am3offset}
+%%% am3offset ::= imm8 | src
+%%%
+%%% fun ::= mfa | prim
+%%% funv ::= mfa | prim | temp
+%%%
+%%% immediate ::= int32 | atom | {label,label_type}
+%%% label_type ::= constant | closure | c_const
+
+-record(am2, {src, sign, offset}).
+-record(am3, {src, sign, offset}).
+
+%%% Instructions:
+
+-record(alu, {aluop, s, dst, src, am1}).% cond not included
+-record(b_fun, {'fun', linkage}). % known tailcall; cond not included
+-record(b_label, {'cond', label}). % local jump
+-record(bl, {'fun', sdesc, linkage}). % known recursive call; cond not included
+-record(blx, {src, sdesc}). % computed recursive call; cond not included
+-record(cmp, {cmpop, src, am1}). % cond not included
+-record(comment, {term}).
+-record(label, {label}).
+-record(load, {ldop, dst, am2}). % cond not included; ldrh/ldrsh not included
+-record(ldrsb, {dst, am3}). % cond not included
+-record(move, {movop, s, dst, am1}). % cond not included
+-record(pseudo_bc, {'cond', true_label, false_label, pred}).
+-record(pseudo_blr, {}). % alias for "mov pc,lr" to help cfg
+-record(pseudo_bx, {src}). % alias for "mov pc,src" to help cfg
+-record(pseudo_call, {funv, sdesc, contlab, linkage}).
+-record(pseudo_call_prepare, {nrstkargs}).
+-record(pseudo_li, {dst, imm, label}). % pre-generated label for use by the assembler
+-record(pseudo_move, {dst, src}).
+-record(pseudo_switch, {jtab, index, labels}).
+-record(pseudo_tailcall, {funv, arity, stkargs, linkage}).
+-record(pseudo_tailcall_prepare, {}).
+-record(smull, {dstlo, dsthi, src1, src2}). % cond not included, s not included
+-record(store, {stop, src, am2}). % cond not included; strh not included
+
+%%% Function definitions.
+
+-include("../misc/hipe_consttab.hrl").
+
+-record(defun, {mfa :: mfa(), formals, code,
+ data :: hipe_consttab(),
+ isclosure :: boolean(),
+ isleaf :: boolean(),
+ var_range, label_range}).
diff --git a/lib/hipe/arm/hipe_arm_assemble.erl b/lib/hipe/arm/hipe_arm_assemble.erl
new file mode 100644
index 0000000000..2af786994e
--- /dev/null
+++ b/lib/hipe/arm/hipe_arm_assemble.erl
@@ -0,0 +1,665 @@
+%% -*- 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_assemble).
+-export([assemble/4]).
+
+-include("../main/hipe.hrl"). % for VERSION_STRING, when_option
+-include("hipe_arm.hrl").
+-include("../../kernel/src/hipe_ext_format.hrl").
+-include("../rtl/hipe_literals.hrl").
+-undef(ASSERT).
+-define(ASSERT(G), if G -> [] ; true -> exit({assertion_failed,?MODULE,?LINE,??G}) end).
+
+assemble(CompiledCode, Closures, Exports, Options) ->
+ print("****************** Assembling *******************\n", [], Options),
+ %%
+ Code = [{MFA,
+ hipe_arm:defun_code(Defun),
+ hipe_arm:defun_data(Defun)}
+ || {MFA, Defun} <- CompiledCode],
+ %%
+ {ConstAlign,ConstSize,ConstMap,RefsFromConsts} =
+ hipe_pack_constants:pack_constants(Code, 4),
+ %%
+ {CodeSize,CodeBinary,AccRefs,LabelMap,ExportMap} =
+ encode(translate(Code, ConstMap), Options),
+ print("Total num bytes=~w\n", [CodeSize], Options),
+ %%
+ SC = hipe_pack_constants:slim_constmap(ConstMap),
+ DataRelocs = mk_data_relocs(RefsFromConsts, LabelMap),
+ SSE = slim_sorted_exportmap(ExportMap,Closures,Exports),
+ SlimRefs = hipe_pack_constants:slim_refs(AccRefs),
+ Bin = term_to_binary([{?VERSION_STRING(),?HIPE_SYSTEM_CRC},
+ ConstAlign, ConstSize,
+ SC,
+ DataRelocs, % nee LM, LabelMap
+ SSE,
+ CodeSize,CodeBinary,SlimRefs,
+ 0,[] % ColdCodeSize, SlimColdRefs
+ ]),
+ %%
+ Bin.
+
+%%%
+%%% Assembly Pass 1.
+%%% Process initial {MFA,Code,Data} list.
+%%% Translate each MFA's body, choosing operand & instruction kinds.
+%%% Manage placement of large immediates in the code segment. (ARM-specific)
+%%%
+%%% Assembly Pass 2.
+%%% Perform short/long form optimisation for jumps.
+%%% (Trivial on ARM.)
+%%%
+%%% Result is {MFA,NewCode,CodeSize,LabelMap} list.
+%%%
+
+translate(Code, ConstMap) ->
+ translate_mfas(Code, ConstMap, []).
+
+translate_mfas([{MFA,Insns,_Data}|Code], ConstMap, NewCode) ->
+ {NewInsns,CodeSize,LabelMap} = translate_insns(Insns, MFA, ConstMap),
+ translate_mfas(Code, ConstMap, [{MFA,NewInsns,CodeSize,LabelMap}|NewCode]);
+translate_mfas([], _ConstMap, NewCode) ->
+ lists:reverse(NewCode).
+
+translate_insns(Insns, MFA, ConstMap) ->
+ translate_insns(Insns, MFA, ConstMap, gb_trees:empty(), 0, [],
+ previous_empty(), pending_empty()).
+
+translate_insns([I|Is] = Insns, MFA, ConstMap, LabelMap, Address, NewInsns, PrevImms, PendImms) ->
+ IsNotFallthroughInsn = is_not_fallthrough_insn(I),
+ MustFlushPending = must_flush_pending(PendImms, Address),
+ {NewIs,Insns1,PendImms1,DoFlushPending} =
+ case {MustFlushPending,IsNotFallthroughInsn} of
+ {true,false} ->
+ %% To avoid having to create new symbolic labels, which is problematic
+ %% in the assembler, we emit a forward branch with an offset computed
+ %% from the size of the pending literals.
+ N = pending_size(PendImms), % N >= 1 since MustFlushPending is true
+ BranchOffset = N - 1, % in units of 32-bit words!
+ NewIs0 = [{b, {do_cond('al'),{imm24,BranchOffset}}, #comment{term='skip'}}],
+ %% io:format("~w: forced flush of pending literals in ~w at ~w\n", [?MODULE,MFA,Address]),
+ {NewIs0,Insns,PendImms,true};
+ {_,_} ->
+ {NewIs0,PendImms0} = translate_insn(I, MFA, ConstMap, Address, PrevImms, PendImms),
+ {NewIs0,Is,PendImms0,IsNotFallthroughInsn}
+ end,
+ add_insns(NewIs, Insns1, MFA, ConstMap, LabelMap, Address, NewInsns, PrevImms, PendImms1, DoFlushPending);
+translate_insns([], _MFA, _ConstMap, LabelMap, Address, NewInsns, PrevImms, PendImms) ->
+ {LabelMap1, Address1, NewInsns1, _PrevImms1} = % at end-of-function we ignore PrevImms1
+ flush_pending(PendImms, LabelMap, Address, NewInsns, PrevImms),
+ {lists:reverse(NewInsns1), Address1, LabelMap1}.
+
+add_insns([I|Is], Insns, MFA, ConstMap, LabelMap, Address, NewInsns, PrevImms, PendImms, DoFlushPending) ->
+ NewLabelMap =
+ case I of
+ {'.label',L,_} ->
+ gb_trees:insert(L, Address, LabelMap);
+ _ ->
+ LabelMap
+ end,
+ Address1 = Address + insn_size(I),
+ add_insns(Is, Insns, MFA, ConstMap, NewLabelMap, Address1, [I|NewInsns], PrevImms, PendImms, DoFlushPending);
+add_insns([], Insns, MFA, ConstMap, LabelMap, Address, NewInsns, PrevImms, PendImms, DoFlushPending) ->
+ {LabelMap1, Address1, NewInsns1, PrevImms1, PendImms1} =
+ case DoFlushPending of
+ true ->
+ {LabelMap0,Address0,NewInsns0,PrevImms0} =
+ flush_pending(PendImms, LabelMap, Address, NewInsns, PrevImms),
+ {LabelMap0,Address0,NewInsns0,PrevImms0,pending_empty()};
+ false ->
+ PrevImms0 = expire_previous(PrevImms, Address),
+ {LabelMap,Address,NewInsns,PrevImms0,PendImms}
+ end,
+ translate_insns(Insns, MFA, ConstMap, LabelMap1, Address1, NewInsns1, PrevImms1, PendImms1).
+
+must_flush_pending(PendImms, Address) ->
+ case pending_firstref(PendImms) of
+ [] -> false;
+ LP0 ->
+ Distance = Address - LP0,
+ %% In "LP0: ldr R,[PC +/- imm12]", the PC value is LP0+8 so the
+ %% range for the ldr is [LP0-4084, LP0+4100] (32-bit alignment!).
+ %% LP0+4096 is the last point where we can emit a branch (4 bytes)
+ %% followed by the pending immediates.
+ %%
+ %% The translation of an individual instruction must not advance
+ %% . by more than 4 bytes, because that could cause us to miss
+ %% the point where PendImms must be flushed.
+ ?ASSERT(Distance =< 4096),
+ Distance =:= 4096
+ end.
+
+flush_pending(PendImms, LabelMap, Address, Insns, PrevImms) ->
+ Address1 = Address + 4*pending_size(PendImms),
+ PrevImms1 = expire_previous(PrevImms, Address1),
+ {LabelMap1,Address1,Insns1,PrevImms2} =
+ flush_pending2(pending_to_list(PendImms), LabelMap, Address, Insns, PrevImms1),
+ PrevImms3 = expire_previous(PrevImms2, Address1),
+ {LabelMap1,Address1,Insns1,PrevImms3}.
+
+flush_pending2([{Lab,RelocOrInt,Imm}|Imms], LabelMap, Address, Insns, PrevImms) ->
+ PrevImms1 = previous_append(PrevImms, Address, Lab, Imm),
+ LabelMap1 = gb_trees:insert(Lab, Address, LabelMap),
+ {RelocOpt,LongVal} =
+ if is_integer(RelocOrInt) ->
+ {[],RelocOrInt};
+ true ->
+ {[RelocOrInt],0}
+ end,
+ Insns1 =
+ [{'.long', LongVal, #comment{term=Imm}} |
+ RelocOpt ++
+ [{'.label', Lab, #comment{term=Imm}} |
+ Insns]],
+ flush_pending2(Imms, LabelMap1, Address+4, Insns1, PrevImms1);
+flush_pending2([], LabelMap, Address, Insns, PrevImms) ->
+ {LabelMap, Address, Insns, PrevImms}.
+
+expire_previous(PrevImms, CodeAddress) ->
+ case previous_findmin(PrevImms) of
+ [] -> PrevImms;
+ {ImmAddress,_Imm} ->
+ if CodeAddress - ImmAddress > 4084 ->
+ expire_previous(previous_delmin(PrevImms), CodeAddress);
+ true ->
+ PrevImms
+ end
+ end.
+
+is_not_fallthrough_insn(I) ->
+ case I of
+ #b_fun{} -> true;
+ #b_label{'cond'='al'} -> true;
+ %% bl and blx are not included since they return to ".+4"
+ %% a load to PC was originally a pseudo_switch insn
+ #load{dst=#arm_temp{reg=15,type=Type}} when Type =/= 'double' -> true;
+ %% a move to PC was originally a pseudo_blr or pseudo_bx insn
+ #move{dst=#arm_temp{reg=15,type=Type}} when Type =/= 'double' -> true;
+ _ -> false
+ end.
+
+insn_size(I) ->
+ case I of
+ {'.label',_,_} -> 0;
+ {'.reloc',_,_} -> 0;
+ _ -> 4
+ end.
+
+translate_insn(I, MFA, ConstMap, Address, PrevImms, PendImms) ->
+ case I of
+ %% pseudo_li is the only insn using MFA, ConstMap, Address, PrevImms, or PendLits
+ #pseudo_li{} -> do_pseudo_li(I, MFA, ConstMap, Address, PrevImms, PendImms);
+ _ -> {translate_insn(I), PendImms}
+ end.
+
+translate_insn(I) -> % -> [{Op,Opnd,OrigI}]
+ case I of
+ #alu{} -> do_alu(I);
+ #b_fun{} -> do_b_fun(I);
+ #b_label{} -> do_b_label(I);
+ #bl{} -> do_bl(I);
+ #blx{} -> do_blx(I);
+ #cmp{} -> do_cmp(I);
+ #comment{} -> [];
+ #label{} -> do_label(I);
+ #load{} -> do_load(I);
+ #ldrsb{} -> do_ldrsb(I);
+ #move{} -> do_move(I);
+ %% pseudo_b: eliminated by finalise
+ %% pseudo_blr: eliminated by finalise
+ %% pseudo_call: eliminated by finalise
+ %% pseudo_call_prepare: eliminated by frame
+ %% pseudo_li: handled separately
+ %% pseudo_move: eliminated by frame
+ %% pseudo_switch: eliminated by finalise
+ %% pseudo_tailcall: eliminated by frame
+ %% pseudo_tailcall_prepare: eliminated by finalise
+ #smull{} -> do_smull(I);
+ #store{} -> do_store(I)
+ end.
+
+do_alu(I) ->
+ #alu{aluop=AluOp,s=S,dst=Dst,src=Src,am1=Am1} = I,
+ NewCond = do_cond('al'),
+ NewS = do_s(S),
+ NewDst = do_reg(Dst),
+ NewSrc = do_reg(Src),
+ NewAm1 = do_am1(Am1),
+ {NewI,NewOpnds} = {AluOp, {NewCond,NewS,NewDst,NewSrc,NewAm1}},
+ [{NewI, NewOpnds, I}].
+
+do_b_fun(I) ->
+ #b_fun{'fun'=Fun,linkage=Linkage} = I,
+ [{'.reloc', {b_fun,Fun,Linkage}, #comment{term='fun'}},
+ {b, {do_cond('al'),{imm24,0}}, I}].
+
+do_b_label(I) ->
+ #b_label{'cond'=Cond,label=Label} = I,
+ [{b, {do_cond(Cond),do_label_ref(Label)}, I}].
+
+do_bl(I) ->
+ #bl{'fun'=Fun,sdesc=SDesc,linkage=Linkage} = I,
+ [{'.reloc', {b_fun,Fun,Linkage}, #comment{term='fun'}},
+ {bl, {do_cond('al'),{imm24,0}}, I},
+ {'.reloc', {sdesc,SDesc}, #comment{term=sdesc}}].
+
+do_blx(I) ->
+ #blx{src=Src,sdesc=SDesc} = I,
+ [{blx, {do_cond('al'),do_reg(Src)}, I},
+ {'.reloc', {sdesc,SDesc}, #comment{term=sdesc}}].
+
+do_cmp(I) ->
+ #cmp{cmpop=CmpOp,src=Src,am1=Am1} = I,
+ NewCond = do_cond('al'),
+ NewSrc = do_reg(Src),
+ NewAm1 = do_am1(Am1),
+ [{CmpOp, {NewCond,NewSrc,NewAm1}, I}].
+
+do_label(I) ->
+ #label{label=Label} = I,
+ [{'.label', Label, I}].
+
+do_load(I) ->
+ #load{ldop=LdOp,dst=Dst,am2=Am2} = I,
+ NewCond = do_cond('al'),
+ NewDst = do_reg(Dst),
+ NewAm2 = do_am2(Am2),
+ [{LdOp, {NewCond,NewDst,NewAm2}, I}].
+
+do_ldrsb(I) ->
+ #ldrsb{dst=Dst,am3=Am3} = I,
+ NewCond = do_cond('al'),
+ NewDst = do_reg(Dst),
+ NewAm3 = do_am3(Am3),
+ [{'ldrsb', {NewCond,NewDst,NewAm3}, I}].
+
+do_move(I) ->
+ #move{movop=MovOp,s=S,dst=Dst,am1=Am1} = I,
+ NewCond = do_cond('al'),
+ NewS = do_s(S),
+ NewDst = do_reg(Dst),
+ NewAm1 = do_am1(Am1),
+ [{MovOp, {NewCond,NewS,NewDst,NewAm1}, I}].
+
+do_pseudo_li(I, MFA, ConstMap, Address, PrevImms, PendImms) ->
+ #pseudo_li{dst=Dst,imm=Imm,label=Label0} = I,
+ {Label1,PendImms1} =
+ case previous_lookup(PrevImms, Imm) of
+ {value,Lab} -> {Lab,PendImms};
+ none ->
+ case pending_lookup(PendImms, Imm) of
+ {value,Lab} -> {Lab,PendImms};
+ none ->
+ RelocOrInt =
+ if is_integer(Imm) ->
+ %% This is for immediates that require too much work
+ %% to reconstruct using only arithmetic instructions.
+ Imm;
+ true ->
+ RelocData =
+ case Imm of
+ Atom when is_atom(Atom) ->
+ {load_atom, Atom};
+ {Label,constant} ->
+ ConstNo = find_const({MFA,Label}, ConstMap),
+ {load_address, {constant,ConstNo}};
+ {Label,closure} ->
+ {load_address, {closure,Label}};
+ {Label,c_const} ->
+ {load_address, {c_const,Label}}
+ end,
+ {'.reloc', RelocData, #comment{term=reloc}}
+ end,
+ Lab = Label0, % preallocated: creating labels in the assembler doesn't work
+ {Lab, pending_append(PendImms, Address, Lab, RelocOrInt, Imm)}
+ end
+ end,
+ NewDst = do_reg(Dst),
+ {[{'.pseudo_li', {NewDst,do_label_ref(Label1)}, I}], PendImms1}.
+
+do_smull(I) ->
+ #smull{dstlo=DstLo,dsthi=DstHi,src1=Src1,src2=Src2} = I,
+ NewCond = do_cond('al'),
+ NewS = do_s(false),
+ NewDstLo = do_reg(DstLo),
+ NewDstHi = do_reg(DstHi),
+ NewSrc1 = do_reg(Src1),
+ NewSrc2 = do_reg(Src2),
+ [{'smull', {NewCond,NewS,NewDstLo,NewDstHi,NewSrc1,NewSrc2}, I}].
+
+do_store(I) ->
+ #store{stop=StOp,src=Src,am2=Am2} = I,
+ NewCond = do_cond('al'),
+ NewSrc = do_reg(Src),
+ NewAm2 = do_am2(Am2),
+ [{StOp, {NewCond,NewSrc,NewAm2}, I}].
+
+do_reg(#arm_temp{reg=Reg,type=Type})
+ when is_integer(Reg), 0 =< Reg, Reg < 16, Type =/= 'double' ->
+ {r,Reg}.
+
+do_cond(Cond) -> {'cond',Cond}.
+
+do_s(S) -> {'s', case S of false -> 0; true -> 1 end}.
+
+do_label_ref(Label) when is_integer(Label) ->
+ {label,Label}. % symbolic, since offset is not yet computable
+
+do_am1(Am1) ->
+ case Am1 of
+ #arm_temp{} -> do_reg(Am1);
+ {Src1,'rrx'} -> {do_reg(Src1),'rrx'};
+ {Src1,ShiftOp,Src2=#arm_temp{}} -> {do_reg(Src1),{ShiftOp,do_reg(Src2)}};
+ {Src1,ShiftOp,Imm5} -> {do_reg(Src1),{ShiftOp,{imm5,Imm5}}};
+ {Imm8,Imm4} -> {{imm8,Imm8},{imm4,Imm4}}
+ end.
+
+do_am2(#am2{src=Src,sign=Sign,offset=Offset}) ->
+ NewSrc = do_reg(Src),
+ case Offset of
+ #arm_temp{} -> {'register_offset',NewSrc,Sign,do_reg(Offset)};
+ {Src3,'rrx'} -> {'scaled_register_offset',NewSrc,Sign,do_reg(Src3),'rrx'};
+ {Src3,ShiftOp,Imm5} -> {'scaled_register_offset',NewSrc,Sign,do_reg(Src3),{ShiftOp,{imm5,Imm5}}};
+ Imm12 -> {'immediate_offset',NewSrc,Sign,{imm12,Imm12}}
+ end.
+
+do_am3(#am3{src=Src,sign=Sign,offset=Offset}) ->
+ NewSrc = do_reg(Src),
+ case Offset of
+ #arm_temp{} -> {'register_offset',NewSrc,Sign,do_reg(Offset)};
+ _ -> {'immediate_offset',NewSrc,Sign,{'imm8',Offset}}
+ end.
+
+%%%
+%%% Assembly Pass 3.
+%%% Process final {MFA,Code,CodeSize,LabelMap} list from pass 2.
+%%% Translate to a single binary code segment.
+%%% Collect relocation patches.
+%%% Build ExportMap (MFA-to-address mapping).
+%%% Combine LabelMaps to a single one (for mk_data_relocs/2 compatibility).
+%%% Return {CombinedCodeSize,BinaryCode,Relocs,CombinedLabelMap,ExportMap}.
+%%%
+
+encode(Code, Options) ->
+ CodeSize = compute_code_size(Code, 0),
+ ExportMap = build_export_map(Code, 0, []),
+ {AccCode,Relocs} = encode_mfas(Code, 0, [], [], Options),
+ CodeBinary = list_to_binary(lists:reverse(AccCode)),
+ ?ASSERT(CodeSize =:= byte_size(CodeBinary)),
+ CombinedLabelMap = combine_label_maps(Code, 0, gb_trees:empty()),
+ {CodeSize,CodeBinary,Relocs,CombinedLabelMap,ExportMap}.
+
+compute_code_size([{_MFA,_Insns,CodeSize,_LabelMap}|Code], Size) ->
+ compute_code_size(Code, Size+CodeSize);
+compute_code_size([], Size) -> Size.
+
+build_export_map([{{M,F,A},_Insns,CodeSize,_LabelMap}|Code], Address, ExportMap) ->
+ build_export_map(Code, Address+CodeSize, [{Address,M,F,A}|ExportMap]);
+build_export_map([], _Address, ExportMap) -> ExportMap.
+
+combine_label_maps([{MFA,_Insns,CodeSize,LabelMap}|Code], Address, CLM) ->
+ NewCLM = merge_label_map(gb_trees:to_list(LabelMap), MFA, Address, CLM),
+ combine_label_maps(Code, Address+CodeSize, NewCLM);
+combine_label_maps([], _Address, CLM) -> CLM.
+
+merge_label_map([{Label,Offset}|Rest], MFA, Address, CLM) ->
+ NewCLM = gb_trees:insert({MFA,Label}, Address+Offset, CLM),
+ merge_label_map(Rest, MFA, Address, NewCLM);
+merge_label_map([], _MFA, _Address, CLM) -> CLM.
+
+encode_mfas([{MFA,Insns,CodeSize,LabelMap}|Code], Address, AccCode, Relocs, Options) ->
+ print("Generating code for: ~w\n", [MFA], Options),
+ print("Offset | Opcode | Instruction\n", [], Options),
+ {Address1,Relocs1,AccCode1} =
+ encode_insns(Insns, Address, Address, LabelMap, Relocs, AccCode, Options),
+ ExpectedAddress = Address + CodeSize,
+ ?ASSERT(Address1 =:= ExpectedAddress),
+ print("Finished.\n", [], Options),
+ encode_mfas(Code, Address1, AccCode1, Relocs1, Options);
+encode_mfas([], _Address, AccCode, Relocs, _Options) ->
+ {AccCode,Relocs}.
+
+encode_insns([I|Insns], Address, FunAddress, LabelMap, Relocs, AccCode, Options) ->
+ case I of
+ {'.label',L,_} ->
+ LabelAddress = gb_trees:get(L, LabelMap) + FunAddress,
+ ?ASSERT(Address =:= LabelAddress), % sanity check
+ print_insn(Address, [], I, Options),
+ encode_insns(Insns, Address, FunAddress, LabelMap, Relocs, AccCode, Options);
+ {'.reloc',Data,_} ->
+ print_insn(Address, [], I, Options),
+ Reloc = encode_reloc(Data, Address, FunAddress, LabelMap),
+ encode_insns(Insns, Address, FunAddress, LabelMap, [Reloc|Relocs], AccCode, Options);
+ {'.long',Value,_} ->
+ print_insn(Address, Value, I, Options),
+ Segment = <<Value:32/integer-native>>,
+ NewAccCode = [Segment|AccCode],
+ encode_insns(Insns, Address+4, FunAddress, LabelMap, Relocs, NewAccCode, Options);
+ _ ->
+ {Op,Arg,_} = fix_pc_refs(I, Address, FunAddress, LabelMap),
+ Word = hipe_arm_encode:insn_encode(Op, Arg),
+ print_insn(Address, Word, I, Options),
+ Segment = <<Word:32/integer-native>>,
+ NewAccCode = [Segment|AccCode],
+ encode_insns(Insns, Address+4, FunAddress, LabelMap, Relocs, NewAccCode, Options)
+ end;
+encode_insns([], Address, _FunAddress, _LabelMap, Relocs, AccCode, _Options) ->
+ {Address,Relocs,AccCode}.
+
+encode_reloc(Data, Address, FunAddress, LabelMap) ->
+ case Data of
+ {b_fun,MFAorPrim,Linkage} ->
+ %% b and bl are patched the same, so no need to distinguish
+ %% call from tailcall
+ PatchTypeExt =
+ case Linkage of
+ remote -> ?CALL_REMOTE;
+ not_remote -> ?CALL_LOCAL
+ end,
+ {PatchTypeExt, Address, untag_mfa_or_prim(MFAorPrim)};
+ {load_atom,Atom} ->
+ {?LOAD_ATOM, Address, Atom};
+ {load_address,X} ->
+ {?LOAD_ADDRESS, Address, X};
+ {sdesc,SDesc} ->
+ #arm_sdesc{exnlab=ExnLab,fsize=FSize,arity=Arity,live=Live} = SDesc,
+ ExnRA =
+ case ExnLab of
+ [] -> []; % don't cons up a new one
+ ExnLab -> gb_trees:get(ExnLab, LabelMap) + FunAddress
+ end,
+ {?SDESC, Address,
+ ?STACK_DESC(ExnRA, FSize, Arity, Live)}
+ end.
+
+untag_mfa_or_prim(#arm_mfa{m=M,f=F,a=A}) -> {M,F,A};
+untag_mfa_or_prim(#arm_prim{prim=Prim}) -> Prim.
+
+fix_pc_refs(I, InsnAddress, FunAddress, LabelMap) ->
+ case I of
+ {b, {Cond,{label,L}}, OrigI} ->
+ LabelAddress = gb_trees:get(L, LabelMap) + FunAddress,
+ Imm24 = (LabelAddress - (InsnAddress+8)) div 4,
+ %% ensure Imm24 fits in a 24 bit sign-extended field
+ ?ASSERT(Imm24 =< 16#7FFFFF),
+ ?ASSERT(Imm24 >= -(16#800000)),
+ {b, {Cond,{imm24,Imm24 band 16#FFFFFF}}, OrigI};
+ {'.pseudo_li', {Dst,{label,L}}, OrigI} ->
+ LabelAddress = gb_trees:get(L, LabelMap) + FunAddress,
+ Offset = LabelAddress - (InsnAddress+8),
+ {Sign,Imm12} =
+ if Offset < 0 -> {'-', -Offset};
+ true -> {'+', Offset}
+ end,
+ ?ASSERT(Imm12 =< 16#FFF),
+ Am2 = {'immediate_offset',{r,15},Sign,{imm12,Imm12}},
+ {ldr, {do_cond('al'),Dst,Am2}, OrigI};
+ _ -> I
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+mk_data_relocs(RefsFromConsts, LabelMap) ->
+ lists:flatten(mk_data_relocs(RefsFromConsts, LabelMap, [])).
+
+mk_data_relocs([{MFA,Labels} | Rest], LabelMap, Acc) ->
+ Map = [case Label of
+ {L,Pos} ->
+ Offset = find({MFA,L}, LabelMap),
+ {Pos,Offset};
+ {sorted,Base,OrderedLabels} ->
+ {sorted, Base, [begin
+ Offset = find({MFA,L}, LabelMap),
+ {Order, Offset}
+ end
+ || {L,Order} <- OrderedLabels]}
+ end
+ || Label <- Labels],
+ %% msg("Map: ~w Map\n",[Map]),
+ mk_data_relocs(Rest, LabelMap, [Map,Acc]);
+mk_data_relocs([],_,Acc) -> Acc.
+
+find({_MFA,_L} = MFAL, LabelMap) ->
+ gb_trees:get(MFAL, LabelMap).
+
+slim_sorted_exportmap([{Addr,M,F,A}|Rest], Closures, Exports) ->
+ IsClosure = lists:member({M,F,A}, Closures),
+ IsExported = is_exported(F, A, Exports),
+ [Addr,M,F,A,IsClosure,IsExported | slim_sorted_exportmap(Rest, Closures, Exports)];
+slim_sorted_exportmap([],_,_) -> [].
+
+is_exported(F, A, Exports) -> lists:member({F,A}, Exports).
+
+%%%
+%%% Assembly listing support (pp_asm option).
+%%%
+
+print(String, Arglist, Options) ->
+ ?when_option(pp_asm, Options, io:format(String, Arglist)).
+
+print_insn(Address, Word, I, Options) ->
+ ?when_option(pp_asm, Options, print_insn_2(Address, Word, I)).
+
+print_insn_2(Address, Word, {NewI,NewArgs,OrigI}) ->
+ io:format("~8.16.0b | ", [Address]),
+ print_code_list(word_to_bytes(Word), 0),
+ case NewI of
+ '.long' ->
+ io:format("\t.long ~.16x\n", [Word, "0x"]);
+ '.reloc' ->
+ io:format("\t.reloc ~w\n", [NewArgs]);
+ _ ->
+ hipe_arm_pp:pp_insn(OrigI)
+ end.
+
+word_to_bytes(W) ->
+ case W of
+ [] -> []; % label or other pseudo instruction
+ _ -> [(W bsr 24) band 16#FF, (W bsr 16) band 16#FF,
+ (W bsr 8) band 16#FF, W band 16#FF]
+ end.
+
+print_code_list([Byte|Rest], Len) ->
+ print_byte(Byte),
+ print_code_list(Rest, Len+1);
+print_code_list([], Len) ->
+ fill_spaces(8-(Len*2)),
+ io:format(" | ").
+
+print_byte(Byte) ->
+ io:format("~2.16.0b", [Byte band 16#FF]).
+
+fill_spaces(N) when N > 0 ->
+ io:format(" "),
+ fill_spaces(N-1);
+fill_spaces(0) ->
+ [].
+
+%%%
+%%% Lookup a constant in a ConstMap.
+%%%
+
+find_const({MFA,Label},[{pcm_entry,MFA,Label,ConstNo,_,_,_}|_]) ->
+ ConstNo;
+find_const(N,[_|R]) ->
+ find_const(N,R);
+find_const(C,[]) ->
+ ?EXIT({constant_not_found,C}).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+%%%
+%%% ADT for previous immediates.
+%%% This is a queue (fifo) of the previously defined immediates,
+%%% plus a mapping from these immediates to their labels.
+%%%
+-record(previous, {set, head, tail}). % INV: tail=[] if head=[]
+
+previous_empty() -> #previous{set=gb_trees:empty(), head=[], tail=[]}.
+
+previous_lookup(#previous{set=S}, Imm) -> gb_trees:lookup(Imm, S).
+
+previous_findmin(#previous{head=H}) ->
+ case H of
+ [X|_] -> X;
+ _ -> []
+ end.
+
+previous_delmin(#previous{set=S, head=[{_Address,Imm}|H], tail=T}) ->
+ {NewH,NewT} =
+ case H of
+ [] -> {lists:reverse(T), []};
+ _ -> {H, T}
+ end,
+ #previous{set=gb_trees:delete(Imm, S), head=NewH, tail=NewT}.
+
+previous_append(#previous{set=S, head=H, tail=T}, Address, Lab, Imm) ->
+ {NewH,NewT} =
+ case H of
+ [] -> {[{Address,Imm}], []};
+ _ -> {H, [{Address,Imm}|T]}
+ end,
+ #previous{set=gb_trees:insert(Imm, Lab, S), head=NewH, tail=NewT}.
+
+%%%
+%%% ADT for pending immediates.
+%%% This is a queue (fifo) of immediates pending definition,
+%%% plus a mapping from these immediates to their labels,
+%%% and a recording of the first (lowest) code address referring
+%%% to a pending immediate.
+%%%
+-record(pending, {set, list, firstref}).
+
+pending_empty() -> #pending{set=gb_trees:empty(), list=[], firstref=[]}.
+
+pending_to_list(#pending{list=L}) -> lists:reverse(L).
+
+pending_lookup(#pending{set=S}, Imm) -> gb_trees:lookup(Imm, S).
+
+pending_firstref(#pending{firstref=F}) -> F.
+
+pending_append(#pending{set=S, list=L, firstref=F}, Address, Lab, RelocOrInt, Imm) ->
+ #pending{set=gb_trees:insert(Imm, Lab, S),
+ list=[{Lab,RelocOrInt,Imm}|L],
+ firstref=case F of [] -> Address; _ -> F end}.
+
+pending_size(#pending{list=L}) -> length(L).
diff --git a/lib/hipe/arm/hipe_arm_cfg.erl b/lib/hipe/arm/hipe_arm_cfg.erl
new file mode 100644
index 0000000000..984a3ccf9e
--- /dev/null
+++ b/lib/hipe/arm/hipe_arm_cfg.erl
@@ -0,0 +1,131 @@
+%% -*- 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_cfg).
+
+-export([init/1,
+ labels/1, start_label/1,
+ succ/2,
+ bb/2, bb_add/3]).
+-export([postorder/1]).
+-export([linearise/1]).
+-export([params/1, reverse_postorder/1]).
+-export([arity/1]). % for linear scan
+%%-export([redirect_jmp/3]).
+
+%%% these tell cfg.inc what to define (ugly as hell)
+-define(BREADTH_ORDER,true). % for linear scan
+-define(PARAMS_NEEDED,true).
+-define(START_LABEL_UPDATE_NEEDED,true).
+
+-include("hipe_arm.hrl").
+-include("../flow/cfg.hrl").
+-include("../flow/cfg.inc").
+
+init(Defun) ->
+ Code = hipe_arm:defun_code(Defun),
+ StartLab = hipe_arm:label_label(hd(Code)),
+ Data = hipe_arm:defun_data(Defun),
+ IsClosure = hipe_arm:defun_is_closure(Defun),
+ Name = hipe_arm:defun_mfa(Defun),
+ IsLeaf = hipe_arm:defun_is_leaf(Defun),
+ Formals = hipe_arm:defun_formals(Defun),
+ CFG0 = mk_empty_cfg(Name, StartLab, Data, IsClosure, IsLeaf, Formals),
+ take_bbs(Code, CFG0).
+
+is_branch(I) ->
+ case I of
+ #b_fun{} -> true;
+ #b_label{'cond'='al'} -> true;
+ #pseudo_bc{} -> true;
+ #pseudo_blr{} -> true;
+ #pseudo_bx{} -> true;
+ #pseudo_call{} -> true;
+ #pseudo_switch{} -> true;
+ #pseudo_tailcall{} -> true;
+ _ -> false
+ end.
+
+branch_successors(Branch) ->
+ case Branch of
+ #b_fun{} -> [];
+ #b_label{'cond'='al',label=Label} -> [Label];
+ #pseudo_bc{true_label=TrueLab,false_label=FalseLab} -> [FalseLab,TrueLab];
+ #pseudo_blr{} -> [];
+ #pseudo_bx{} -> [];
+ #pseudo_call{contlab=ContLab, sdesc=#arm_sdesc{exnlab=ExnLab}} ->
+ case ExnLab of
+ [] -> [ContLab];
+ _ -> [ContLab,ExnLab]
+ end;
+ #pseudo_switch{labels=Labels} -> Labels;
+ #pseudo_tailcall{} -> []
+ end.
+
+-ifdef(REMOVE_TRIVIAL_BBS_NEEDED).
+fails_to(_Instr) -> [].
+-endif.
+
+-ifdef(notdef).
+redirect_jmp(I, Old, New) ->
+ case I of
+ #b_label{label=Label} ->
+ if Old =:= Label -> I#b_label{label=New};
+ true -> I
+ end;
+ #pseudo_bc{true_label=TrueLab, false_label=FalseLab} ->
+ I1 = if Old =:= TrueLab -> I#pseudo_bc{true_label=New};
+ true -> I
+ end,
+ if Old =:= FalseLab -> I1#pseudo_bc{false_label=New};
+ true -> I1
+ end;
+ %% handle pseudo_call too?
+ _ -> I
+ end.
+-endif.
+
+mk_goto(Label) ->
+ hipe_arm:mk_b_label(Label).
+
+is_label(I) ->
+ hipe_arm:is_label(I).
+
+label_name(Label) ->
+ hipe_arm:label_label(Label).
+
+mk_label(Name) ->
+ hipe_arm:mk_label(Name).
+
+linearise(CFG) -> % -> defun, not insn list
+ MFA = function(CFG),
+ Formals = params(CFG),
+ Code = linearize_cfg(CFG),
+ Data = data(CFG),
+ VarRange = hipe_gensym:var_range(arm),
+ LabelRange = hipe_gensym:label_range(arm),
+ IsClosure = is_closure(CFG),
+ IsLeaf = is_leaf(CFG),
+ hipe_arm:mk_defun(MFA, Formals, IsClosure, IsLeaf,
+ Code, Data, VarRange, LabelRange).
+
+arity(CFG) ->
+ {_M, _F, A} = function(CFG),
+ A.
diff --git a/lib/hipe/arm/hipe_arm_defuse.erl b/lib/hipe/arm/hipe_arm_defuse.erl
new file mode 100644
index 0000000000..8d6efebc21
--- /dev/null
+++ b/lib/hipe/arm/hipe_arm_defuse.erl
@@ -0,0 +1,157 @@
+%% -*- 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_defuse).
+-export([insn_def_all/1, insn_use_all/1]).
+-export([insn_def_gpr/1, insn_use_gpr/1]).
+-include("hipe_arm.hrl").
+
+%%%
+%%% Defs and uses for both general-purpose and floating-point registers.
+%%% This is needed for the frame module, alas.
+%%%
+insn_def_all(I) ->
+ insn_def_gpr(I).
+
+insn_use_all(I) ->
+ insn_use_gpr(I).
+
+%%%
+%%% Defs and uses for general-purpose (integer) registers only.
+%%%
+insn_def_gpr(I) ->
+ case I of
+ #alu{dst=Dst} -> [Dst];
+ #load{dst=Dst} -> [Dst];
+ #ldrsb{dst=Dst} -> [Dst];
+ #move{dst=Dst} -> [Dst];
+ #pseudo_call{} -> call_clobbered_gpr();
+ #pseudo_li{dst=Dst} -> [Dst];
+ #pseudo_move{dst=Dst} -> [Dst];
+ #pseudo_tailcall_prepare{} -> tailcall_clobbered_gpr();
+ #smull{dstlo=DstLo,dsthi=DstHi,src1=Src1} ->
+ %% ARM requires DstLo, DstHi, and Src1 to be distinct.
+ %% Add fake DEF of Src1 to prevent regalloc from reusing
+ %% it as DstLo or DstHi.
+ [DstLo, DstHi, Src1];
+ _ -> []
+ end.
+
+call_clobbered_gpr() ->
+ [hipe_arm:mk_temp(R, T)
+ || {R,T} <- hipe_arm_registers:call_clobbered() ++ all_fp_pseudos()].
+
+all_fp_pseudos() -> []. % XXX: for now
+
+tailcall_clobbered_gpr() ->
+ [hipe_arm:mk_temp(R, T)
+ || {R,T} <- hipe_arm_registers:tailcall_clobbered() ++ all_fp_pseudos()].
+
+insn_use_gpr(I) ->
+ case I of
+ #alu{src=Src,am1=Am1} -> am1_use(Am1, [Src]);
+ #blx{src=Src} -> [Src];
+ #cmp{src=Src,am1=Am1} -> am1_use(Am1, [Src]);
+ #load{am2=Am2} -> am2_use(Am2, []);
+ #ldrsb{am3=Am3} -> am3_use(Am3, []);
+ #move{am1=Am1} -> am1_use(Am1, []);
+ #pseudo_blr{} ->
+ LR = hipe_arm:mk_temp(hipe_arm_registers:lr(), 'untagged'),
+ RV = hipe_arm:mk_temp(hipe_arm_registers:return_value(), 'tagged'),
+ [RV, LR];
+ #pseudo_bx{src=Src} ->
+ io:format("~w: whoa there! insn_use of ~w occurred\n", [?MODULE,I]),
+ [Src];
+ #pseudo_call{funv=FunV,sdesc=#arm_sdesc{arity=Arity}} ->
+ funv_use(FunV, arity_use_gpr(Arity));
+ #pseudo_move{src=Src} -> [Src];
+ #pseudo_switch{jtab=JTabR,index=IndexR} -> addtemp(JTabR, [IndexR]);
+ #pseudo_tailcall{funv=FunV,arity=Arity,stkargs=StkArgs} ->
+ addargs(StkArgs, addtemps(tailcall_clobbered_gpr(), funv_use(FunV, arity_use_gpr(Arity))));
+ #smull{src1=Src1,src2=Src2} -> addtemp(Src1, [Src2]);
+ #store{src=Src,am2=Am2} -> am2_use(Am2, [Src]);
+ _ -> []
+ end.
+
+addargs([Arg|Args], Set) ->
+ addargs(Args, addarg(Arg, Set));
+addargs([], Set) ->
+ Set.
+
+addarg(Arg, Set) ->
+ case Arg of
+ #arm_temp{} -> addtemp(Arg, Set);
+ _ -> Set
+ end.
+
+arity_use_gpr(Arity) ->
+ [hipe_arm:mk_temp(R, 'tagged')
+ || R <- hipe_arm_registers:args(Arity)].
+
+funv_use(FunV, Set) ->
+ case FunV of
+ #arm_temp{} -> addtemp(FunV, Set);
+ _ -> Set
+ end.
+
+am1_use(Am1, Set) ->
+ case Am1 of
+ #arm_temp{} -> addtemp(Am1, Set);
+ {Src,rrx} -> addtemp(Src, Set);
+ {Src,_,ShiftArg} ->
+ Set1 = addtemp(Src, Set),
+ case ShiftArg of
+ #arm_temp{} -> addtemp(ShiftArg, Set1);
+ _ -> Set1
+ end;
+ _ -> Set
+ end.
+
+am2_use(#am2{src=Src,offset=Am2Offset}, Set) ->
+ Set1 = addtemp(Src, Set),
+ case Am2Offset of
+ #arm_temp{} -> addtemp(Am2Offset, Set1);
+ {Src2,_} -> addtemp(Src2, Set1);
+ {Src2,_,_} -> addtemp(Src2, Set1);
+ _ -> Set1
+ end.
+
+am3_use(#am3{src=Src,offset=Am3Offset}, Set) ->
+ Set1 = addtemp(Src, Set),
+ case Am3Offset of
+ #arm_temp{} -> addtemp(Am3Offset, Set1);
+ _ -> Set1
+ end.
+
+%%%
+%%% Auxiliary operations on sets of temps
+%%% These sets are small. No point using gb_trees, right?
+%%%
+
+addtemps([Arg|Args], Set) ->
+ addtemps(Args, addtemp(Arg, Set));
+addtemps([], Set) ->
+ Set.
+
+addtemp(Temp, Set) ->
+ case lists:member(Temp, Set) of
+ false -> [Temp|Set];
+ _ -> Set
+ end.
diff --git a/lib/hipe/arm/hipe_arm_encode.erl b/lib/hipe/arm/hipe_arm_encode.erl
new file mode 100644
index 0000000000..19e507fdbd
--- /dev/null
+++ b/lib/hipe/arm/hipe_arm_encode.erl
@@ -0,0 +1,994 @@
+%%% -*- 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%
+%%%
+%%% Encode symbolic ARM instructions to binary form.
+%%% Copyright (C) 2005 Mikael Pettersson
+%%%
+%%% Implementation Notes:
+%%% - The Thumb instruction set is a different entity, and is
+%%% not and never will be supported by this module.
+%%% - Instructions and instruction forms that are unpredictable
+%%% or useless in User mode are not supported. They include:
+%%% + Data Processing Instructions with S=1 and Rd=15.
+%%% + The LDM(2), LDM(3), and STM(2) instructions.
+%%% + MRS instructions that access the SPSR.
+%%% + MSR instructions that access the SPSR.
+%%% + The LDBRT, LDRT, STBRT, and STRT instructions.
+%%%
+%%% Instruction Operands:
+%%%
+%%% S ::= {s,0} | {s,1}
+%%% L ::= {l,0} | {l,1}
+%%% R ::= {r,RNum}
+%%% CR ::= {cr,CRNum}
+%%%
+%%% Cond ::= {cond,CondName}
+%%% CondName ::= eq | ne | cs | hs | cc | lo | mi | pl | vs
+%%% | vc | hi | ls | ge | lt | gt | ge | al
+%%%
+%%% Imm<N> ::= {imm<N>,<N bits>} for N in 4, 5, 8, 12, 16, 24, and 25
+%%%
+%%% Am1ShifterOperand
+%%% ::= {Imm8,Imm4}
+%%% | Rm
+%%% | {Rm,Am1ShiftOp}
+%%% Am1ShiftOp ::= {ShiftOp,Imm5}
+%%% | {ShiftOp,Rs}
+%%% | rrx
+%%% ShiftOp ::= lsl | lsr | asr | ror
+%%%
+%%% Am2LSWUBOperand ::= {immediate_offset,Rn,Sign,Imm12}
+%%% | {register_offset,Rn,Sign,Rm} // redundant
+%%% | {scaled_register_offset,Rn,Sign,Rm,Am2ShiftOp}
+%%% | {immediate_pre_indexed,Rn,Sign,Imm12}
+%%% | {register_pre_indexed,Rn,Sign,Rm} // redundant
+%%% | {scaled_register_pre_indexed,Rn,Sign,Rm,Am2ShiftOp}
+%%% | {immediate_post_indexed,Rn,Sign,Imm12}
+%%% | {register_post_indexed,Rn,Sign,Rm} // redundant
+%%% | {scaled_register_post_indexed,Rn,Sign,Rm,Am2ShiftOp}
+%%% Am2ShiftOp ::= {ShiftOp,Imm5}
+%%% | rrx
+%%% Sign ::= + | -
+%%%
+%%% Am3MiscLSOperand::= {immediate_offset,Rn,Sign,Imm8}
+%%% | {register_offset,Rn,Sign,Rm}
+%%% | {immediate_pre_indexed,Rn,Sign,Imm8}
+%%% | {register_pre_indexed,Rn,Sign,Rm}
+%%% | {immediate_post_indexed,Rn,Sign,Imm8}
+%%% | {register_post_indexed,Rn,Sign,Rm}
+%%%
+%%% Am4LSMultiple ::= ia | ib | da | db
+%%% | fd | ed | fa | ea
+%%%
+%%% Am5LSCoprocessor::= {offset,Rn,Sign,Imm8}
+%%% | {pre_indexed,Rn,Sign,Imm8}
+%%% | {post_indexed,Rn,Sign,Imm8}
+%%% | {unindexed,Rn,Imm8}
+
+-module(hipe_arm_encode).
+
+-export([insn_encode/2]).
+
+%%-define(TESTING,1).
+-ifdef(TESTING).
+-export([dotest/0, dotest/1]).
+-endif.
+
+-define(ASSERT(G),
+ if G -> [];
+ true -> exit({assertion_failed,?MODULE,?LINE,??G})
+ end).
+
+bf(LeftBit, RightBit, Value) ->
+ ?ASSERT(32 > LeftBit),
+ ?ASSERT(LeftBit >= RightBit),
+ ?ASSERT(RightBit >= 0),
+ ?ASSERT(Value >= 0),
+ ?ASSERT(Value < (1 bsl ((LeftBit - RightBit) + 1))),
+ Value bsl RightBit.
+
+-define(BF(LB,RB,V), bf(LB,RB,V)).
+-define(BIT(Pos,Val), ?BF(Pos,Pos,Val)).
+%%-define(BITS(N,Val), ?BF(N,0,Val)).
+
+%%%
+%%% Addressing Modes
+%%%
+
+am1_shifter_operand(Rn, Rd, ShifterOperand) ->
+ case ShifterOperand of
+ {{imm8,Imm8},{imm4,RotImm4}} ->
+ ?BIT(25,1) bor ?BF(11,8,RotImm4) bor ?BF(7,0,Imm8);
+ {r,Rm} ->
+ %% same as Rm LSL #0
+ ?BF(3,0,Rm);
+ {{r,Rm},ShiftOp} ->
+ am1_shift_op(Rn, Rd, Rm, ShiftOp) bor ?BF(3,0,Rm)
+ end.
+
+am1_shift_op(_Rn, _Rd, _Rm, {ShiftOp,{imm5,ShiftImm5}}) ->
+ case ShiftOp of
+ 'ror' -> ?ASSERT(ShiftImm5 =/= 0); % denotes RRX form
+ _ -> []
+ end,
+ ?BF(11,7,ShiftImm5) bor shift_op_bits65(ShiftOp);
+am1_shift_op(Rn, Rd, Rm, {ShiftOp,{r,Rs}}) ->
+ ?ASSERT(Rn =/= 15), % UNPREDICTABLE
+ ?ASSERT(Rd =/= 15), % UNPREDICTABLE
+ ?ASSERT(Rm =/= 15), % UNPREDICTABLE
+ ?ASSERT(Rs =/= 15), % UNPREDICTABLE
+ ?BF(11,8,Rs) bor shift_op_bits65(ShiftOp) bor ?BIT(4,1);
+am1_shift_op(_Rn, _Rd, _Rm, 'rrx') ->
+ ?BF(6,5,2#11).
+
+shift_op_bits65(ShiftOp) ->
+ case ShiftOp of
+ 'lsl' -> ?BF(6,5,2#00);
+ 'lsr' -> ?BF(6,5,2#01);
+ 'asr' -> ?BF(6,5,2#10);
+ 'ror' -> ?BF(6,5,2#11)
+ end.
+
+sign('+') -> ?BIT(23,1);
+sign('-') -> 0.
+
+am2_lswub(Rd, AddressingMode) ->
+ case AddressingMode of
+ {immediate_offset,{r,Rn},Sign,{imm12,Imm12}} ->
+ ?BIT(24,1) bor sign(Sign) bor ?BF(19,16,Rn) bor ?BF(11,0,Imm12);
+ {register_offset,{r,Rn},Sign,{r,Rm}} ->
+ %% same as scaled_register_offset LSL #0
+ ?ASSERT(Rm =/= 15), % UNPREDICTABLE
+ ?BIT(25,1) bor ?BIT(24,1) bor sign(Sign) bor ?BF(19,16,Rn) bor ?BF(3,0,Rm);
+ {scaled_register_offset,{r,Rn},Sign,{r,Rm},ShiftOp} ->
+ ?ASSERT(Rm =/= 15), % UNPREDICTABLE
+ ?BIT(25,1) bor ?BIT(24,1) bor sign(Sign) bor ?BF(19,16,Rn) bor am2_shift_op(ShiftOp) bor ?BF(3,0,Rm);
+ {immediate_pre_indexed,{r,Rn},Sign,{imm12,Imm12}} ->
+ ?ASSERT(Rd =/= Rn), % UNPREDICTABLE
+ ?ASSERT(Rn =/= 15), % UNPREDICTABLE
+ ?BIT(24,1) bor sign(Sign) bor ?BIT(21,1) bor ?BF(19,16,Rn) bor ?BF(11,0,Imm12);
+ {register_pre_indexed,{r,Rn},Sign,{r,Rm}} ->
+ %% same as scaled_register_pre_indexed LSL #0
+ ?ASSERT(Rd =/= Rn), % UNPREDICTABLE
+ ?ASSERT(Rn =/= 15), % UNPREDICTABLE
+ ?ASSERT(Rm =/= 15), % UNPREDICTABLE
+ ?ASSERT(Rn =/= Rm), % UNPREDICTABLE
+ ?BIT(25,1) bor ?BIT(24,1) bor sign(Sign) bor ?BIT(21,1) bor ?BF(19,16,Rn) bor ?BF(3,0,Rm);
+ {scaled_register_pre_indexed,{r,Rn},Sign,{r,Rm},ShiftOp} ->
+ ?ASSERT(Rd =/= Rn), % UNPREDICTABLE
+ ?ASSERT(Rn =/= 15), % UNPREDICTABLE
+ ?ASSERT(Rm =/= 15), % UNPREDICTABLE
+ ?ASSERT(Rn =/= Rm), % UNPREDICTABLE
+ ?BIT(25,1) bor ?BIT(24,1) bor sign(Sign) bor ?BIT(21,1) bor ?BF(19,16,Rn) bor am2_shift_op(ShiftOp) bor ?BF(3,0,Rm);
+ {immediate_post_indexed,{r,Rn},Sign,{imm12,Imm12}} ->
+ ?ASSERT(Rd =/= Rn), % UNPREDICTABLE
+ ?ASSERT(Rn =/= 15), % UNPREDICTABLE
+ sign(Sign) bor ?BF(19,16,Rn) bor ?BF(11,0,Imm12);
+ {register_post_indexed,{r,Rn},Sign,{r,Rm}} ->
+ %% same as scaled_register_post_indexed LSL #0
+ ?ASSERT(Rd =/= Rn), % UNPREDICTABLE
+ ?BIT(25,1) bor sign(Sign) bor ?BF(19,6,Rn) bor ?BF(3,0,Rm);
+ {scaled_register_post_indexed,{r,Rn},Sign,{r,Rm},ShiftOp} ->
+ ?ASSERT(Rd =/= Rn), % UNPREDICTABLE
+ ?ASSERT(Rn =/= 15), % UNPREDICTABLE
+ ?ASSERT(Rm =/= 15), % UNPREDICTABLE
+ ?ASSERT(Rn =/= Rm), % UNPREDICTABLE
+ ?BIT(25,1) bor sign(Sign) bor ?BF(19,16,Rn) bor am2_shift_op(ShiftOp) bor ?BF(3,0,Rm)
+ end.
+
+am2_shift_op({ShiftOp,{imm5,ShiftImm5}}) ->
+ case ShiftOp of
+ 'ror' -> ?ASSERT(ShiftImm5 =/= 0); % denotes RRX form
+ _ -> []
+ end,
+ ?BF(11,7,ShiftImm5) bor shift_op_bits65(ShiftOp);
+am2_shift_op('rrx') ->
+ ?BF(6,5,2#11).
+
+am3_miscls(Rd, AddressingMode) ->
+ case AddressingMode of
+ {immediate_offset,{r,Rn},Sign,{imm8,Imm8}} ->
+ ?BIT(24,1) bor sign(Sign) bor ?BF(22,21,2#10) bor ?BF(19,16,Rn) bor ?BF(11,8,Imm8 bsr 4) bor ?BF(3,0,Imm8 band 2#1111);
+ {register_offset,{r,Rn},Sign,{r,Rm}} ->
+ ?ASSERT(Rm =/= 15), % UNPREDICTABLE
+ ?BIT(24,1) bor sign(Sign) bor ?BF(22,21,2#00) bor ?BF(19,16,Rn) bor ?BF(3,0,Rm);
+ {immediate_pre_indexed,{r,Rn},Sign,{imm8,Imm8}} ->
+ ?ASSERT(Rd =/= Rn), % UNPREDICTABLE
+ ?ASSERT(Rn =/= 15), % UNPREDICTABLE
+ ?BIT(24,1) bor sign(Sign) bor ?BF(22,21,2#11) bor ?BF(19,16,Rn) bor ?BF(11,8,Imm8 bsr 4) bor ?BF(3,0,Imm8 band 2#1111);
+ {register_pre_indexed,{r,Rn},Sign,{r,Rm}} ->
+ ?ASSERT(Rd =/= Rn), % UNPREDICTABLE
+ ?ASSERT(Rm =/= 15), % UNPREDICTABLE
+ ?ASSERT(Rn =/= 15), % UNPREDICTABLE
+ ?ASSERT(Rm =/= Rn), % UNPREDICTABLE
+ ?BIT(24,1) bor sign(Sign) bor ?BF(22,21,2#01) bor ?BF(19,16,Rn) bor ?BF(3,0,Rm);
+ {immediate_post_indexed,{r,Rn},Sign,{imm8,Imm8}} ->
+ ?ASSERT(Rd =/= Rn), % UNPREDICTABLE
+ ?ASSERT(Rn =/= 15), % UNPREDICTABLE
+ ?BIT(24,0) bor sign(Sign) bor ?BF(22,21,2#10) bor ?BF(19,16,Rn) bor ?BF(11,8,Imm8 bsr 4) bor ?BF(3,0,Imm8 band 2#1111);
+ {register_post_indexed,{r,Rn},Sign,{r,Rm}} ->
+ ?ASSERT(Rd =/= Rn), % UNPREDICTABLE
+ ?ASSERT(Rm =/= 15), % UNPREDICTABLE
+ ?ASSERT(Rn =/= 15), % UNPREDICTABLE
+ ?ASSERT(Rm =/= Rn), % UNPREDICTABLE
+ ?BIT(24,0) bor sign(Sign) bor ?BF(22,21,2#00) bor ?BF(19,16,Rn) bor ?BF(3,0,Rm)
+ end.
+
+am4_ls_multiple(L, AddressingMode) ->
+ case AddressingMode of
+ 'ia' -> ?BF(24,23,2#01);
+ 'ib' -> ?BF(24,23,2#11);
+ 'da' -> ?BF(24,23,2#00);
+ 'db' -> ?BF(24,23,2#10);
+ _ ->
+ %% context-sensitive alias crap
+ case {L,AddressingMode} of
+ {1,'fa'} -> ?BF(24,23,2#00);
+ {1,'fd'} -> ?BF(24,23,2#01);
+ {1,'ea'} -> ?BF(24,23,2#10);
+ {1,'ed'} -> ?BF(24,23,2#11);
+ {0,'ed'} -> ?BF(24,23,2#00);
+ {0,'ea'} -> ?BF(24,23,2#01);
+ {0,'fd'} -> ?BF(24,23,2#10);
+ {0,'fa'} -> ?BF(24,23,2#11)
+ end
+ end.
+
+am5_ls_coprocessor(AddressingMode) ->
+ case AddressingMode of
+ {offset,{r,Rn},Sign,{imm8,Imm8}} ->
+ ?BIT(24,1) bor sign(Sign) bor ?BF(19,16,Rn) bor ?BF(7,0,Imm8);
+ {pre_indexed,{r,Rn},Sign,{imm8,Imm8}} ->
+ ?ASSERT(Rn =/= 15), % UNPREDICTABLE
+ ?BIT(24,1) bor sign(Sign) bor ?BIT(21,1) bor ?BF(19,16,Rn) bor ?BF(7,0,Imm8);
+ {post_indexed,{r,Rn},Sign,{imm8,Imm8}} ->
+ ?ASSERT(Rn =/= 15), % UNPREDICTABLE
+ sign(Sign) bor ?BIT(21,1) bor ?BF(19,16,Rn) bor ?BF(7,0,Imm8);
+ {unindexed,{r,Rn},{imm8,Imm8}} ->
+ ?BIT(23,1) bor ?BF(19,16,Rn) bor ?BF(7,0,Imm8)
+ end.
+
+%%%
+
+'cond'(Cond) ->
+ case Cond of
+ 'eq' -> ?BF(31,28,2#0000); % equal
+ 'ne' -> ?BF(31,28,2#0001); % not equal
+ 'cs' -> ?BF(31,28,2#0010); % carry set
+ 'hs' -> ?BF(31,28,2#0010); % unsigned higher or same
+ 'cc' -> ?BF(31,28,2#0011); % carry clear
+ 'lo' -> ?BF(31,28,2#0011); % unsigned lower
+ 'mi' -> ?BF(31,28,2#0100); % minus/negative
+ 'pl' -> ?BF(31,28,2#0101); % plus/positive or zero
+ 'vs' -> ?BF(31,28,2#0110); % overflow
+ 'vc' -> ?BF(31,28,2#0111); % no overflow
+ 'hi' -> ?BF(31,28,2#1000); % unsigned higher
+ 'ls' -> ?BF(31,28,2#1001); % unsigned lower or same
+ 'ge' -> ?BF(31,28,2#1010); % signed greater than or equal
+ 'lt' -> ?BF(31,28,2#1011); % signed less than
+ 'gt' -> ?BF(31,28,2#1100); % signed greater than
+ 'le' -> ?BF(31,28,2#1101); % signed less than or equal
+ 'al' -> ?BF(31,28,2#1110) % always
+ end.
+
+%%%
+%%% ARM Instructions
+%%%
+
+data_processing_form(Cond, OpCode, S, Rn, Rd, ShifterOperand) ->
+ case S of
+ 1 -> ?ASSERT(Rd =/= 15); % UNPREDICTABLE in User or System mode
+ _ -> []
+ end,
+ 'cond'(Cond) bor ?BF(24,21,OpCode) bor ?BIT(20,S) bor ?BF(19,16,Rn) bor ?BF(15,12,Rd) bor am1_shifter_operand(Rn,Rd,ShifterOperand).
+
+data_processing_form(OpCode, {{'cond',Cond},{s,S},{r,Rd},{r,Rn},ShifterOperand}) ->
+ data_processing_form(Cond, OpCode, S, Rn, Rd, ShifterOperand).
+
+adc(Opnds) -> data_processing_form(2#0101, Opnds).
+add(Opnds) -> data_processing_form(2#0100, Opnds).
+'and'(Opnds) -> data_processing_form(2#0000, Opnds).
+bic(Opnds) -> data_processing_form(2#1110, Opnds).
+eor(Opnds) -> data_processing_form(2#0001, Opnds).
+orr(Opnds) -> data_processing_form(2#1100, Opnds).
+rsb(Opnds) -> data_processing_form(2#0011, Opnds).
+rsc(Opnds) -> data_processing_form(2#0111, Opnds).
+sbc(Opnds) -> data_processing_form(2#0110, Opnds).
+sub(Opnds) -> data_processing_form(2#0010, Opnds).
+
+cmp_form(OpCode, {{'cond',Cond},{r,Rn},ShifterOperand}) ->
+ data_processing_form(Cond, OpCode, 1, Rn, 0, ShifterOperand).
+
+cmn(Opnds) -> cmp_form(2#1011, Opnds).
+cmp(Opnds) -> cmp_form(2#1010, Opnds).
+teq(Opnds) -> cmp_form(2#1001, Opnds).
+tst(Opnds) -> cmp_form(2#1000, Opnds).
+
+mov_form(OpCode, {{'cond',Cond},{s,S},{r,Rd},ShifterOperand}) ->
+ data_processing_form(Cond, OpCode, S, 0, Rd, ShifterOperand).
+
+mov(Opnds) -> mov_form(2#1101, Opnds).
+mvn(Opnds) -> mov_form(2#1111, Opnds).
+
+%%%
+
+b_form(L, {{'cond',Cond},{imm24,Imm24}}) ->
+ 'cond'(Cond) bor ?BF(27,25,2#101) bor ?BIT(24,L) bor ?BF(23,0,Imm24).
+
+b(Opnds) -> b_form(0, Opnds).
+bl(Opnds) -> b_form(1, Opnds).
+
+bkpt({{imm16,Imm16}}) ->
+ ?BF(31,28,2#1110) bor ?BF(27,20,2#00010010) bor ?BF(19,8,Imm16 bsr 4) bor ?BF(7,4,2#0111) bor ?BF(3,0,Imm16 band 2#1111).
+
+bx_form(SubOpcode, {{'cond',Cond},{r,Rm}}, IsBlx) ->
+ case IsBlx of
+ true -> ?ASSERT(Rm =/= 15); % UNPREDICTABLE
+ _ -> []
+ end,
+ 'cond'(Cond) bor ?BF(27,20,2#00010010) bor ?BF(19,16,2#1111) bor ?BF(15,12,2#1111) bor ?BF(11,8,2#1111) bor ?BF(7,4,SubOpcode) bor ?BF(3,0,Rm).
+
+blx(Opnds) ->
+ case Opnds of
+ {{imm25,Imm25}} -> % u16-offset!
+ ?BF(31,28,2#1111) bor ?BF(27,25,2#101) bor ?BIT(24,Imm25 band 1) bor ?BF(23,0,Imm25 bsr 1);
+ _ ->
+ bx_form(2#0011, Opnds, true)
+ end.
+
+bx(Opnds) -> bx_form(2#0001, Opnds, false).
+
+cdp_form(Cond, CpOp4, CRn, CRd, CpNum, CpOp3, CRm) ->
+ Cond bor ?BF(27,24,2#1110) bor ?BF(23,20,CpOp4) bor ?BF(19,16,CRn) bor ?BF(15,12,CRd) bor ?BF(11,8,CpNum) bor ?BF(7,5,CpOp3) bor ?BF(3,0,CRm).
+
+cdp({{'cond',Cond},{cpnum,CpNum},{cpop4,CpOp4},{cr,CRd},{cr,CRn},{cr,CRm},{cpop3,CpOp3}}) ->
+ cdp_form('cond'(Cond), CpOp4, CRn, CRd, CpNum, CpOp3, CRm).
+
+cdp2({{cpnum,CpNum},{cpop4,CpOp4},{cr,CRd},{cr,CRn},{cr,CRm},{cpop3,CpOp3}}) ->
+ cdp_form(?BF(31,28,2#1111), CpOp4, CRn, CRd, CpNum, CpOp3, CRm).
+
+clz({{'cond',Cond},{r,Rd},{r,Rm}}) ->
+ ?ASSERT(Rd =/= 15), % UNPREDICTABLE
+ ?ASSERT(Rm =/= 15), % UNPREDICTABLE
+ 'cond'(Cond) bor ?BF(27,20,2#00010110) bor ?BF(19,16,2#1111) bor ?BF(15,12,Rd) bor ?BF(11,8,2#1111) bor ?BF(7,4,2#0001) bor ?BF(3,0,Rm).
+
+ldstc_form(Cond, L, B20, CRd, CpNum, AddressingMode) ->
+ Cond bor ?BF(27,25,2#110) bor ?BIT(22,L) bor ?BIT(20,B20) bor ?BF(15,12,CRd) bor ?BF(11,8,CpNum) bor am5_ls_coprocessor(AddressingMode).
+
+ldstc(B20, {{'cond',Cond},{l,L},{cpnum,CpNum},{cr,CRd},AddressingMode}) ->
+ ldstc_form('cond'(Cond), L, B20, CRd, CpNum, AddressingMode).
+
+ldc(Opnds) -> ldstc(1, Opnds).
+stc(Opnds) -> ldstc(0, Opnds).
+
+ldstc2(B20, {{l,L},{cpnum,CpNum},{cr,CRd},AddressingMode}) ->
+ ldstc_form(?BF(31,28,2#1111), L, B20, CRd, CpNum, AddressingMode).
+
+ldc2(Opnds) -> ldstc2(1, Opnds).
+stc2(Opnds) -> ldstc2(0, Opnds).
+
+ldstm_form(Cond, AddressingMode, W, L, Rn, Registers) ->
+ RegisterList = register_list(Registers),
+ ?ASSERT(RegisterList =/= 0), % UNPREDICTABLE
+ ?ASSERT(Rn =/= 15), % UNPREDICTABLE
+ case W of
+ 1 ->
+ BitRn = 1 bsl Rn,
+ case L of
+ 1 ->
+ %% LDM! Rn in Registers is UNPREDICTABLE
+ ?ASSERT((RegisterList band BitRn) =:= 0);
+ 0 ->
+ %% STM! Rn in Registers and not lowest is UNPREDICTABLE
+ case RegisterList band BitRn of
+ 0 -> [];
+ _ ->
+ ?ASSERT((RegisterList band (-RegisterList)) =:= BitRn)
+ end
+ end;
+ _ -> []
+ end,
+ 'cond'(Cond) bor ?BF(27,25,2#100) bor am4_ls_multiple(L, AddressingMode) bor ?BIT(21,W) bor ?BIT(20,L) bor ?BF(19,16,Rn) bor ?BF(15,0,RegisterList).
+
+register_list(Registers) -> register_list(Registers, 0).
+
+register_list([{r,R}|Rs], Mask) -> register_list(Rs, Mask bor (1 bsl R));
+register_list([], Mask) -> Mask.
+
+ldstm(L, Opnds) ->
+ case Opnds of
+ {{'cond',Cond},AddressingMode,{r,Rn},'!',Registers} ->
+ ldstm_form(Cond, AddressingMode, 1, L, Rn, Registers);
+ {{'cond',Cond},AddressingMode,{r,Rn},Registers} ->
+ ldstm_form(Cond, AddressingMode, 0, L, Rn, Registers)
+ %% the ldm(2), ldm(3), and stm(2) forms are UNPREDICTABLE
+ %% in User or System mode
+ end.
+
+ldm(Opnds) -> ldstm(1, Opnds).
+stm(Opnds) -> ldstm(0, Opnds).
+
+ldstr_form2(B, L, {{'cond',Cond},{r,Rd},AddressingMode}) ->
+ 'cond'(Cond) bor ?BF(27,26,2#01) bor am2_lswub(Rd, AddressingMode) bor ?BIT(22,B) bor ?BIT(20,L) bor ?BF(15,12,Rd).
+
+ldr(Opnds) -> ldstr_form2(0, 1, Opnds).
+ldrb(Opnds) -> ldstr_form2(1, 1, Opnds).
+str(Opnds) -> ldstr_form2(0, 0, Opnds).
+strb(Opnds) -> ldstr_form2(1, 0, Opnds).
+
+ldstr_form3(L, SubOpcode, {{'cond',Cond},{r,Rd},AddressingMode}) ->
+ ?ASSERT(Rd =/= 15), % UNPREDICTABLE
+ 'cond'(Cond) bor am3_miscls(Rd, AddressingMode) bor ?BIT(20,L) bor ?BF(15,12,Rd) bor ?BF(7,4,SubOpcode).
+
+ldrh(Opnds) -> ldstr_form3(1, 2#1011, Opnds).
+ldrsb(Opnds) -> ldstr_form3(1, 2#1101, Opnds).
+ldrsh(Opnds) -> ldstr_form3(1, 2#1111, Opnds).
+strh(Opnds) -> ldstr_form3(0, 2#1011, Opnds).
+
+mcr_form(Cond, OP1, CRn, Rd, CpNum, OP2, CRm) ->
+ ?ASSERT(Rd =/= 15), % UNPREDICTABLE
+ Cond bor ?BF(27,24,2#1110) bor ?BF(23,21,OP1) bor ?BF(19,16,CRn) bor ?BF(15,12,Rd) bor ?BF(11,8,CpNum) bor ?BF(7,5,OP2) bor ?BIT(4,1) bor ?BF(3,0,CRm).
+
+mcr({{'cond',Cond},{cpnum,CpNum},{cpop3,OP1},{r,Rd},{cr,CRn},{cr,CRm},{cpop3,OP2}}) ->
+ mcr_form('cond'(Cond), OP1, CRn, Rd, CpNum, OP2, CRm).
+
+mcr2({{cpnum,CpNum},{cpop3,OP1},{r,Rd},{cr,CRn},{cr,CRm},{cpop3,OP2}}) ->
+ mcr_form(?BF(31,28,2#1111), OP1, CRn, Rd, CpNum, OP2, CRm).
+
+mla({{'cond',Cond},{s,S},{r,Rd},{r,Rm},{r,Rs},{r,Rn}}) ->
+ ?ASSERT(Rd =/= 15), % UNPREDICTABLE
+ ?ASSERT(Rm =/= 15), % UNPREDICTABLE
+ ?ASSERT(Rs =/= 15), % UNPREDICTABLE
+ ?ASSERT(Rn =/= 15), % UNPREDICTABLE
+ ?ASSERT(Rd =/= Rm), % UNPREDICTABLE
+ 'cond'(Cond) bor ?BIT(21,1) bor ?BIT(20,S) bor ?BF(19,16,Rd) bor ?BF(15,12,Rn) bor ?BF(11,8,Rs) bor ?BF(7,4,2#1001) bor ?BF(3,0,Rm).
+
+mrc_form(Cond, OP1, CRn, Rd, CpNum, OP2, CRm) ->
+ Cond bor ?BF(27,24,2#1110) bor ?BF(23,21,OP1) bor ?BIT(20,1) bor ?BF(19,16,CRn) bor ?BF(15,12,Rd) bor ?BF(11,8,CpNum) bor ?BF(7,5,OP2) bor ?BIT(4,1) bor ?BF(3,0,CRm).
+
+mrc({{'cond',Cond},{cpnum,CpNum},{cpop3,OP1},{r,Rd},{cr,CRn},{cr,CRm},{cpop3,OP2}}) ->
+ mrc_form('cond'(Cond), OP1, CRn, Rd, CpNum, OP2, CRm).
+
+mrc2({{cpnum,CpNum},{cpop3,OP1},{r,Rd},{cr,CRn},{cr,CRm},{cpop3,OP2}}) ->
+ mrc_form(?BF(31,28,2#1111), OP1, CRn, Rd, CpNum, OP2, CRm).
+
+mrs({{'cond',Cond},{r,Rd},'cpsr'}) ->
+ %% the SPSR form is UNPREDICTABLE in User or System mode
+ ?ASSERT(Rd =/= 15), % UNPREDICTABLE
+ 'cond'(Cond) bor ?BIT(24,1) bor ?BF(19,16,2#1111) bor ?BF(15,12,Rd).
+
+msr_form(Cond, FieldMask4, Operand) ->
+ 'cond'(Cond) bor ?BIT(24,1) bor ?BIT(21,1) bor ?BF(19,16,FieldMask4) bor ?BF(15,12,2#1111) bor Operand.
+
+msr(Opnds) ->
+ %% the SPSR form is UNPREDICTABLE in User or System mode
+ case Opnds of
+ {{'cond',Cond},'cpsr',{field_mask,FieldMask4},{imm8,Imm8},{imm4,RotImm4}} ->
+ msr_form(Cond, FieldMask4, ?BIT(25,1) bor ?BF(11,8,RotImm4) bor ?BF(7,0,Imm8));
+ {{'cond',Cond},'cpsr',{field_mask,FieldMask4},{r,Rm}} ->
+ msr_form(Cond, FieldMask4, ?BF(3,0,Rm))
+ end.
+
+mul({{'cond',Cond},{s,S},{r,Rd},{r,Rm},{r,Rs}}) ->
+ ?ASSERT(Rd =/= 15), % UNPREDICTABLE
+ ?ASSERT(Rm =/= 15), % UNPREDICTABLE
+ ?ASSERT(Rs =/= 15), % UNPREDICTABLE
+ ?ASSERT(Rd =/= Rm), % UNPREDICTABLE
+ 'cond'(Cond) bor ?BIT(20,S) bor ?BF(19,16,Rd) bor ?BF(11,8,Rs) bor ?BF(7,4,2#1001) bor ?BF(3,0,Rm).
+
+ml_form2(OpCode, Cond, S, RdLo, RdHi, Rm, Rs) ->
+ ?ASSERT(RdHi =/= 15), % UNPREDICTABLE
+ ?ASSERT(RdLo =/= 15), % UNPREDICTABLE
+ ?ASSERT(Rm =/= 15), % UNPREDICTABLE
+ ?ASSERT(Rs =/= 15), % UNPREDICTABLE
+ ?ASSERT(RdHi =/= RdLo),% UNPREDICTABLE
+ ?ASSERT(RdHi =/= Rm), % UNPREDICTABLE
+ ?ASSERT(RdLo =/= Rm), % UNPREDICTABLE
+ 'cond'(Cond) bor ?BF(27,21,OpCode) bor ?BIT(20,S) bor ?BF(19,16,RdHi) bor ?BF(15,12,RdLo) bor ?BF(11,8,Rs) bor ?BF(7,4,2#1001) bor ?BF(3,0,Rm).
+
+ml_form(OpCode, {{'cond',Cond},{s,S},{r,RdLo},{r,RdHi},{r,Rm},{r,Rs}}) ->
+ ml_form2(OpCode, Cond, S, RdLo, RdHi, Rm, Rs).
+
+%%smlal(Opnds) -> ml_form(2#0000111, Opnds).
+smull(Opnds) -> ml_form(2#0000110, Opnds).
+umlal(Opnds) -> ml_form(2#0000101, Opnds).
+umull(Opnds) -> ml_form(2#0000100, Opnds).
+
+swi({{'cond',Cond},{imm24,Imm24}}) ->
+ 'cond'(Cond) bor ?BF(27,24,2#1111) bor ?BF(23,0,Imm24).
+
+swp_form(B22, {{'cond',Cond},{r,Rd},{r,Rm},{r,Rn}}) ->
+ ?ASSERT(Rd =/= 15), % UNPREDICTABLE
+ ?ASSERT(Rm =/= 15), % UNPREDICTABLE
+ ?ASSERT(Rn =/= 15), % UNPREDICTABLE
+ ?ASSERT(Rn =/= Rm), % UNPREDICTABLE
+ ?ASSERT(Rn =/= Rd), % UNPREDICTABLE
+ 'cond'(Cond) bor ?BIT(24,1) bor ?BIT(22,B22) bor ?BF(19,16,Rn) bor ?BF(15,12,Rd) bor ?BF(7,4,2#1001) bor ?BF(3,0,Rm).
+
+swp(Opnds) -> swp_form(0, Opnds).
+swpb(Opnds) -> swp_form(1, Opnds).
+
+%%%
+%%% Enhanced DSP Extension Instructions
+%%%
+
+ldstrd_form(OpCode, {{'cond',Cond},{r,Rd},AddressingMode}) ->
+ ?ASSERT(Rd =/= 14), % UNPREDICTABLE
+ ?ASSERT((Rd band 1) =:= 0), % UNDEFINED
+ %% XXX: unpredictable if write-back and base reg Rn equals Rd or Rd+1
+ %% XXX: if is load then unpredictable if index reg Rm and Rm equals Rd or Rd+1
+ 'cond'(Cond) bor am3_miscls(Rd, AddressingMode) bor ?BF(15,12,Rd) bor ?BF(7,4,OpCode).
+
+ldrd(Opnds) -> ldstrd_form(2#1101, Opnds).
+strd(Opnds) -> ldstrd_form(2#1111, Opnds).
+
+mcrr({{'cond',Cond},{cpnum,CpNum},{cpop4,OP},{r,Rd},{r,Rn},{cr,CRm}}) ->
+ ?ASSERT(Rd =/= 15), % UNPREDICTABLE
+ ?ASSERT(Rn =/= 15), % UNPREDICTABLE
+ 'cond'(Cond) bor ?BF(27,20,2#11000100) bor ?BF(19,16,Rn) bor ?BF(15,12,Rd) bor ?BF(11,8,CpNum) bor ?BF(7,4,OP) bor ?BF(3,0,CRm).
+
+mrrc({{'cond',Cond},{cpnum,CpNum},{cpop4,OP},{r,Rd},{r,Rn},{cr,CRm}}) ->
+ ?ASSERT(Rd =/= 15), % UNPREDICTABLE
+ ?ASSERT(Rn =/= 15), % UNPREDICTABLE
+ ?ASSERT(Rd =/= Rn), % UNPREDICTABLE
+ 'cond'(Cond) bor ?BF(27,20,2#11000101) bor ?BF(19,16,Rn) bor ?BF(15,12,Rd) bor ?BF(11,8,CpNum) bor ?BF(7,4,OP) bor ?BF(3,0,CRm).
+
+pld({AddressingMode}) ->
+ AM = am2_lswub(42, AddressingMode), % 42 is a dummy reg nr
+ %% not all adressing modes are allowed: bit 24 must be 1
+ %% and bit 21 must be 0
+ ?ASSERT(((AM bsr 21) band 2#1001) =:= 2#1000),
+ 16#F550F000 bor AM.
+
+q_form(OpCode, {{'cond',Cond},{r,Rd},{r,Rm},{r,Rn}}) ->
+ ?ASSERT(Rd =/= 15), % UNPREDICTABLE
+ ?ASSERT(Rm =/= 15), % UNPREDICTABLE
+ ?ASSERT(Rn =/= 15), % UNPREDICTABLE
+ 'cond'(Cond) bor ?BF(27,20,OpCode) bor ?BF(19,16,Rn) bor ?BF(15,11,Rd) bor ?BF(7,4,2#0101) bor ?BF(3,0,Rm).
+
+qadd(Opnds) -> q_form(2#00010000, Opnds).
+qdadd(Opnds) -> q_form(2#00010100, Opnds).
+qdsub(Opnds) -> q_form(2#00010110, Opnds).
+qsub(Opnds) -> q_form(2#00010010, Opnds).
+
+smlaxy_form(Cond, OpCode, Rd, Rn, Rs, Y, X, Rm) ->
+ ?ASSERT(Rd =/= 15), % UNPREDICTABLE
+ ?ASSERT(Rm =/= 15), % UNPREDICTABLE
+ ?ASSERT(Rs =/= 15), % UNPREDICTABLE
+ ?ASSERT(Rn =/= 15), % UNPREDICTABLE
+ 'cond'(Cond) bor ?BF(27,20,OpCode) bor ?BF(19,16,Rd) bor ?BF(15,12,Rn) bor ?BF(11,8,Rs) bor ?BIT(7,1) bor ?BIT(6,Y) bor ?BIT(5,X) bor ?BF(3,0,Rm).
+
+smla({{bt,X},{bt,Y},{'cond',Cond},{r,Rd},{r,Rm},{r,Rs},{r,Rn}}) ->
+ smlaxy_form(Cond, 2#00010000, Rd, Rn, Rs, Y, X, Rm).
+
+smlal(Opnds) -> % may be regular ARM or DSP insn :-(
+ case Opnds of
+ {{'cond',Cond},{s,S},{r,RdLo},{r,RdHi},{r,Rm},{r,Rs}} ->
+ ml_form2(2#0000111, Cond, S, RdLo, RdHi, Rm, Rs);
+ {{bt,X},{bt,Y},{'cond',Cond},{r,RdLo},{r,RdHi},{r,Rm},{r,Rs}} ->
+ ?ASSERT(RdLo =/= RdHi), % UNPREDICTABLE
+ smlaxy_form(Cond, 2#00010100, RdHi, RdLo, Rs, Y, X, Rm)
+ end.
+
+smlaw({{bt,Y},{'cond',Cond},{r,Rd},{r,Rm},{r,Rs},{r,Rn}}) ->
+ smlaxy_form(Cond, 2#00010010, Rd, Rn, Rs, Y, 0, Rm).
+
+smul({{bt,X},{bt,Y},{'cond',Cond},{r,Rd},{r,Rm},{r,Rs}}) ->
+ smlaxy_form(Cond, 2#00010110, Rd, 0, Rs, Y, X, Rm).
+
+smulw({{bt,Y},{'cond',Cond},{r,Rd},{r,Rm},{r,Rs}}) ->
+ smlaxy_form(Cond, 2#00010010, Rd, 0, Rs, Y, 1, Rm).
+
+%%%
+%%% Main Encode Dispatch
+%%%
+
+insn_encode(Op, Opnds) ->
+ case Op of
+ 'adc' -> adc(Opnds);
+ 'add' -> add(Opnds);
+ 'and' -> 'and'(Opnds);
+ 'b' -> b(Opnds);
+ 'bic' -> bic(Opnds);
+ 'bkpt' -> bkpt(Opnds);
+ 'bl' -> bl(Opnds);
+ 'blx' -> blx(Opnds);
+ 'bx' -> bx(Opnds);
+ 'cdp' -> cdp(Opnds);
+ 'cdp2' -> cdp2(Opnds);
+ 'clz' -> clz(Opnds);
+ 'cmn' -> cmn(Opnds);
+ 'cmp' -> cmp(Opnds);
+ 'eor' -> eor(Opnds);
+ 'ldc' -> ldc(Opnds);
+ 'ldc2' -> ldc2(Opnds);
+ 'ldm' -> ldm(Opnds);
+ 'ldr' -> ldr(Opnds);
+ 'ldrb' -> ldrb(Opnds);
+ 'ldrd' -> ldrd(Opnds);
+ %% ldrbt: omitted
+ 'ldrh' -> ldrh(Opnds);
+ 'ldrsb' -> ldrsb(Opnds);
+ 'ldrsh' -> ldrsh(Opnds);
+ %% ldrt: omitted
+ 'mcr' -> mcr(Opnds);
+ 'mcr2' -> mcr2(Opnds);
+ 'mcrr' -> mcrr(Opnds);
+ 'mla' -> mla(Opnds);
+ 'mov' -> mov(Opnds);
+ 'mrc' -> mrc(Opnds);
+ 'mrc2' -> mrc2(Opnds);
+ 'mrrc' -> mrrc(Opnds);
+ 'mrs' -> mrs(Opnds);
+ 'msr' -> msr(Opnds);
+ 'mul' -> mul(Opnds);
+ 'mvn' -> mvn(Opnds);
+ 'orr' -> orr(Opnds);
+ 'pld' -> pld(Opnds);
+ 'qadd' -> qadd(Opnds);
+ 'qdadd' -> qdadd(Opnds);
+ 'qdsub' -> qdsub(Opnds);
+ 'qsub' -> qsub(Opnds);
+ 'rsb' -> rsb(Opnds);
+ 'rsc' -> rsc(Opnds);
+ 'sbc' -> sbc(Opnds);
+ 'smla' -> smla(Opnds);
+ 'smlal' -> smlal(Opnds); % may be regular ARM or DSP insn :-(
+ 'smlaw' -> smlaw(Opnds);
+ 'smull' -> smull(Opnds);
+ 'smul' -> smul(Opnds);
+ 'smulw' -> smulw(Opnds);
+ 'stc' -> stc(Opnds);
+ 'stc2' -> stc2(Opnds);
+ 'stm' -> stm(Opnds);
+ 'str' -> str(Opnds);
+ 'strb' -> strb(Opnds);
+ %% strbt: omitted
+ 'strd' -> strd(Opnds);
+ 'strh' -> strh(Opnds);
+ %% strt: omitted
+ 'sub' -> sub(Opnds);
+ 'swi' -> swi(Opnds);
+ 'swp' -> swp(Opnds);
+ 'swpb' -> swpb(Opnds);
+ 'teq' -> teq(Opnds);
+ 'tst' -> tst(Opnds);
+ 'umlal' -> umlal(Opnds);
+ 'umull' -> umull(Opnds);
+ _ -> exit({?MODULE,insn_encode,Op})
+ end.
+
+%%%
+%%% Testing Interface
+%%%
+
+-ifdef(TESTING).
+
+say(OS, Str) ->
+ file:write(OS, Str).
+
+hex_digit(Dig0) ->
+ Dig = Dig0 band 16#F,
+ if Dig >= 16#A -> $A + (Dig - 16#A);
+ true -> $0 + Dig
+ end.
+
+say_byte(OS, Byte) ->
+ say(OS, [hex_digit(Byte bsr 4)]),
+ say(OS, [hex_digit(Byte)]).
+
+say_word(OS, Word) ->
+ say(OS, "0x"),
+ say_byte(OS, Word bsr 24),
+ say_byte(OS, Word bsr 16),
+ say_byte(OS, Word bsr 8),
+ say_byte(OS, Word).
+
+t(OS, Op, Opnds) ->
+ Word = insn_encode(Op, Opnds),
+ say(OS, "\t.long "),
+ say_word(OS, Word),
+ say(OS, "\n").
+
+dotest1(OS) ->
+ say(OS, "\t.text\n\t.align 4\n"),
+ %%
+ Rn = {r,9},
+ Rd = {r,8}, % must be even and less than 14 for some insns
+ Rm = {r,7},
+ Rs = {r,6},
+ RdLo = Rn,
+ RdHi = Rd,
+ Registers = [Rm,Rs,Rd], % must exclude Rn for some insns
+ CRd = {cr,15},
+ CRn = {cr,14},
+ CRm = {cr,13},
+ BT0 = {bt,0},
+ BT1 = {bt,1},
+ CpNum = {cpnum,15},
+ CpOp3 = {cpop3,16#3},
+ CpOp4 = {cpop4,16#F},
+ L0 = {l,0},
+ L1 = {l,1},
+ S0 = {s,0},
+ S1 = {s,1},
+ FieldMask4 = {field_mask,16#F},
+ Imm4 = {imm4,16#F},
+ Imm5 = {imm5,16#1F},
+ Imm8 = {imm8,16#FF},
+ Imm12 = {imm12,16#FFF},
+ Imm16 = {imm16,16#FFFF},
+ Imm24 = {imm24,16#FFFFF},
+ Imm25 = {imm25,16#FFFFF1},
+ %%
+ AM1_1 = {Imm8,Imm4},
+ AM1_2 = Rm,
+ AM1_3_1 = {Rm,{'lsl',Imm5}},
+ AM1_3_2 = {Rm,{'lsr',Imm5}},
+ AM1_3_3 = {Rm,{'asr',Imm5}},
+ AM1_3_4 = {Rm,{'ror',Imm5}},
+ AM1_3_5 = {Rm,{'lsl',Rs}},
+ AM1_3_6 = {Rm,{'lsr',Rs}},
+ AM1_3_7 = {Rm,{'asr',Rs}},
+ AM1_3_8 = {Rm,{'ror',Rs}},
+ AM1_3_9 = {Rm,'rrx'},
+ %%
+ AM2ShiftOp1 = {'lsl',Imm5},
+ AM2ShiftOp2 = {'lsr',Imm5},
+ AM2ShiftOp3 = {'asr',Imm5},
+ AM2ShiftOp4 = {'ror',Imm5},
+ AM2ShiftOp5 = 'rrx',
+ SignP = '+',
+ SignM = '-',
+ AM2_1_1 = {immediate_offset,Rn,SignP,Imm12},
+ AM2_1_2 = {immediate_offset,Rn,SignM,Imm12},
+ AM2_2_1 = {register_offset,Rn,SignP,Rm},
+ AM2_2_2 = {register_offset,Rn,SignM,Rm},
+ AM2_3_1 = {scaled_register_offset,Rn,SignP,Rm,AM2ShiftOp1},
+ AM2_3_2 = {scaled_register_offset,Rn,SignM,Rm,AM2ShiftOp2},
+ AM2_3_3 = {scaled_register_offset,Rn,SignP,Rm,AM2ShiftOp3},
+ AM2_3_4 = {scaled_register_offset,Rn,SignM,Rm,AM2ShiftOp4},
+ AM2_3_5 = {scaled_register_offset,Rn,SignP,Rm,AM2ShiftOp5},
+ AM2_4_1 = {immediate_pre_indexed,Rn,SignP,Imm12},
+ AM2_4_2 = {immediate_pre_indexed,Rn,SignM,Imm12},
+ AM2_5_1 = {register_pre_indexed,Rn,SignP,Rm},
+ AM2_5_2 = {register_pre_indexed,Rn,SignM,Rm},
+ AM2_6_1 = {scaled_register_pre_indexed,Rn,SignP,Rm,AM2ShiftOp1},
+ AM2_6_2 = {scaled_register_pre_indexed,Rn,SignM,Rm,AM2ShiftOp2},
+ AM2_6_3 = {scaled_register_pre_indexed,Rn,SignP,Rm,AM2ShiftOp3},
+ AM2_6_4 = {scaled_register_pre_indexed,Rn,SignM,Rm,AM2ShiftOp4},
+ AM2_6_5 = {scaled_register_pre_indexed,Rn,SignP,Rm,AM2ShiftOp5},
+ AM2_7_1 = {immediate_post_indexed,Rn,SignP,Imm12},
+ AM2_7_2 = {immediate_post_indexed,Rn,SignM,Imm12},
+ AM2_8_1 = {register_post_indexed,Rn,SignP,Rm},
+ AM2_8_2 = {register_post_indexed,Rn,SignM,Rm},
+ AM2_9_1 = {scaled_register_post_indexed,Rn,SignP,Rm,AM2ShiftOp1},
+ AM2_9_2 = {scaled_register_post_indexed,Rn,SignM,Rm,AM2ShiftOp2},
+ AM2_9_3 = {scaled_register_post_indexed,Rn,SignP,Rm,AM2ShiftOp3},
+ AM2_9_4 = {scaled_register_post_indexed,Rn,SignM,Rm,AM2ShiftOp4},
+ AM2_9_5 = {scaled_register_post_indexed,Rn,SignP,Rm,AM2ShiftOp5},
+ %%
+ AM3_1_1 = {immediate_offset,Rn,SignP,Imm8},
+ AM3_1_2 = {immediate_offset,Rn,SignM,Imm8},
+ AM3_2_1 = {register_offset,Rn,SignP,Rm},
+ AM3_2_2 = {register_offset,Rn,SignM,Rm},
+ AM3_3_1 = {immediate_pre_indexed,Rn,SignP,Imm8},
+ AM3_3_2 = {immediate_pre_indexed,Rn,SignM,Imm8},
+ AM3_4_1 = {register_pre_indexed,Rn,SignP,Rm},
+ AM3_4_2 = {register_pre_indexed,Rn,SignM,Rm},
+ AM3_5_1 = {immediate_post_indexed,Rn,SignP,Imm8},
+ AM3_5_2 = {immediate_post_indexed,Rn,SignM,Imm8},
+ AM3_6_1 = {register_post_indexed,Rn,SignP,Rm},
+ AM3_6_2 = {register_post_indexed,Rn,SignM,Rm},
+ %%
+ AM4_1 = 'ia',
+ AM4_2 = 'ib',
+ AM4_3 = 'da',
+ AM4_4 = 'db',
+ AM4_5 = 'fa',
+ AM4_6 = 'fd',
+ AM4_7 = 'ea',
+ AM4_8 = 'ed',
+ %%
+ AM5_1_1 = {offset,Rn,SignP,Imm8},
+ AM5_1_2 = {offset,Rn,SignM,Imm8},
+ AM5_2_1 = {pre_indexed,Rn,SignP,Imm8},
+ AM5_2_2 = {pre_indexed,Rn,SignM,Imm8},
+ AM5_3_1 = {post_indexed,Rn,SignP,Imm8},
+ AM5_3_2 = {post_indexed,Rn,SignM,Imm8},
+ AM5_4 = {unindexed,Rn,Imm8},
+ %%
+ Cond_eq = {'cond','eq'},
+ Cond_ne = {'cond','ne'},
+ Cond_cs = {'cond','cs'},
+ Cond_hs = {'cond','hs'},
+ Cond_cc = {'cond','cc'},
+ Cond_lo = {'cond','lo'},
+ Cond_mi = {'cond','mi'},
+ Cond_pl = {'cond','pl'},
+ Cond_vs = {'cond','vs'},
+ Cond_vc = {'cond','vc'},
+ Cond_hi = {'cond','hi'},
+ Cond_ls = {'cond','ls'},
+ Cond_ge = {'cond','ge'},
+ Cond_lt = {'cond','lt'},
+ Cond_gt = {'cond','gt'},
+ Cond_le = {'cond','le'},
+ Cond_al = {'cond','al'},
+ %%
+ t(OS,'adc',{Cond_al,S0,Rd,Rn,AM1_1}), % test all AM1 operands
+ t(OS,'adc',{Cond_al,S0,Rd,Rn,AM1_2}),
+ t(OS,'adc',{Cond_al,S0,Rd,Rn,AM1_3_1}),
+ t(OS,'adc',{Cond_al,S0,Rd,Rn,AM1_3_2}),
+ t(OS,'adc',{Cond_al,S0,Rd,Rn,AM1_3_3}),
+ t(OS,'adc',{Cond_al,S0,Rd,Rn,AM1_3_4}),
+ t(OS,'adc',{Cond_al,S0,Rd,Rn,AM1_3_5}),
+ t(OS,'adc',{Cond_al,S0,Rd,Rn,AM1_3_6}),
+ t(OS,'adc',{Cond_al,S0,Rd,Rn,AM1_3_7}),
+ t(OS,'adc',{Cond_al,S0,Rd,Rn,AM1_3_8}),
+ t(OS,'adc',{Cond_al,S0,Rd,Rn,AM1_3_9}),
+ t(OS,'add',{Cond_al,S0,Rd,Rn,AM1_1}), % test all S operands
+ t(OS,'add',{Cond_al,S1,Rd,Rn,AM1_1}),
+ t(OS,'and',{Cond_eq,S0,Rd,Rn,AM1_1}), % test all Cond operands
+ t(OS,'and',{Cond_ne,S0,Rd,Rn,AM1_1}),
+ t(OS,'and',{Cond_cs,S0,Rd,Rn,AM1_1}),
+ t(OS,'and',{Cond_hs,S0,Rd,Rn,AM1_1}),
+ t(OS,'and',{Cond_cc,S0,Rd,Rn,AM1_1}),
+ t(OS,'and',{Cond_lo,S0,Rd,Rn,AM1_1}),
+ t(OS,'and',{Cond_mi,S0,Rd,Rn,AM1_1}),
+ t(OS,'and',{Cond_pl,S0,Rd,Rn,AM1_1}),
+ t(OS,'and',{Cond_vs,S0,Rd,Rn,AM1_1}),
+ t(OS,'and',{Cond_vc,S0,Rd,Rn,AM1_1}),
+ t(OS,'and',{Cond_hi,S0,Rd,Rn,AM1_1}),
+ t(OS,'and',{Cond_ls,S0,Rd,Rn,AM1_1}),
+ t(OS,'and',{Cond_ge,S0,Rd,Rn,AM1_1}),
+ t(OS,'and',{Cond_lt,S0,Rd,Rn,AM1_1}),
+ t(OS,'and',{Cond_gt,S0,Rd,Rn,AM1_1}),
+ t(OS,'and',{Cond_le,S0,Rd,Rn,AM1_1}),
+ t(OS,'and',{Cond_al,S0,Rd,Rn,AM1_1}),
+ t(OS,'b',{Cond_al,Imm24}),
+ t(OS,'bic',{Cond_al,S0,Rd,Rn,AM1_1}),
+ t(OS,'bkpt',{Imm16}),
+ t(OS,'bl',{Cond_al,Imm24}),
+ t(OS,'blx',{Imm25}),
+ t(OS,'blx',{Cond_al,Rm}),
+ t(OS,'bx',{Cond_al,Rm}),
+ t(OS,'cdp',{Cond_al,CpNum,CpOp4,CRd,CRn,CRm,CpOp3}),
+ t(OS,'cdp2',{CpNum,CpOp4,CRd,CRn,CRm,CpOp3}),
+ t(OS,'clz',{Cond_al,Rd,Rm}),
+ t(OS,'cmn',{Cond_al,Rn,AM1_1}),
+ t(OS,'cmp',{Cond_al,Rn,AM1_1}),
+ t(OS,'eor',{Cond_al,S0,Rd,Rn,AM1_1}),
+ t(OS,'ldc',{Cond_al,L0,CpNum,CRd,AM5_1_1}), % test all AM5 operands
+ t(OS,'ldc',{Cond_al,L1,CpNum,CRd,AM5_1_2}),
+ t(OS,'ldc',{Cond_al,L0,CpNum,CRd,AM5_2_1}),
+ t(OS,'ldc',{Cond_al,L1,CpNum,CRd,AM5_2_2}),
+ t(OS,'ldc',{Cond_al,L0,CpNum,CRd,AM5_3_1}),
+ t(OS,'ldc',{Cond_al,L1,CpNum,CRd,AM5_3_2}),
+ t(OS,'ldc',{Cond_al,L0,CpNum,CRd,AM5_4}),
+ t(OS,'ldc2',{L0,CpNum,CRd,AM5_1_1}),
+ t(OS,'ldm',{Cond_al,AM4_1,Rn,'!',Registers}),
+ t(OS,'ldm',{Cond_al,AM4_1,Rn,Registers}), % test all AM4 operands
+ t(OS,'ldm',{Cond_al,AM4_2,Rn,Registers}), % test all AM4 operands
+ t(OS,'ldm',{Cond_al,AM4_3,Rn,Registers}), % test all AM4 operands
+ t(OS,'ldm',{Cond_al,AM4_4,Rn,Registers}), % test all AM4 operands
+ t(OS,'ldm',{Cond_al,AM4_5,Rn,Registers}), % test all AM4 operands
+ t(OS,'ldm',{Cond_al,AM4_6,Rn,Registers}), % test all AM4 operands
+ t(OS,'ldm',{Cond_al,AM4_7,Rn,Registers}), % test all AM4 operands
+ t(OS,'ldm',{Cond_al,AM4_8,Rn,Registers}), % test all AM4 operands
+ t(OS,'ldr',{Cond_al,Rd,AM2_1_1}), % test all AM2 operands
+ t(OS,'ldr',{Cond_al,Rd,AM2_1_2}),
+ t(OS,'ldr',{Cond_al,Rd,AM2_2_1}),
+ t(OS,'ldr',{Cond_al,Rd,AM2_2_2}),
+ t(OS,'ldr',{Cond_al,Rd,AM2_3_1}),
+ t(OS,'ldr',{Cond_al,Rd,AM2_3_2}),
+ t(OS,'ldr',{Cond_al,Rd,AM2_3_3}),
+ t(OS,'ldr',{Cond_al,Rd,AM2_3_4}),
+ t(OS,'ldr',{Cond_al,Rd,AM2_3_5}),
+ t(OS,'ldr',{Cond_al,Rd,AM2_4_1}),
+ t(OS,'ldr',{Cond_al,Rd,AM2_4_2}),
+ t(OS,'ldr',{Cond_al,Rd,AM2_5_1}),
+ t(OS,'ldr',{Cond_al,Rd,AM2_5_2}),
+ t(OS,'ldr',{Cond_al,Rd,AM2_6_1}),
+ t(OS,'ldr',{Cond_al,Rd,AM2_6_2}),
+ t(OS,'ldr',{Cond_al,Rd,AM2_6_3}),
+ t(OS,'ldr',{Cond_al,Rd,AM2_6_4}),
+ t(OS,'ldr',{Cond_al,Rd,AM2_6_5}),
+ t(OS,'ldr',{Cond_al,Rd,AM2_7_1}),
+ t(OS,'ldr',{Cond_al,Rd,AM2_7_2}),
+ t(OS,'ldr',{Cond_al,Rd,AM2_8_1}),
+ t(OS,'ldr',{Cond_al,Rd,AM2_8_2}),
+ t(OS,'ldr',{Cond_al,Rd,AM2_9_1}),
+ t(OS,'ldr',{Cond_al,Rd,AM2_9_2}),
+ t(OS,'ldr',{Cond_al,Rd,AM2_9_3}),
+ t(OS,'ldr',{Cond_al,Rd,AM2_9_4}),
+ t(OS,'ldr',{Cond_al,Rd,AM2_9_5}),
+ t(OS,'ldrb',{Cond_al,Rd,AM2_1_1}),
+ t(OS,'ldrd',{Cond_al,Rd,AM3_1_1}),
+ t(OS,'ldrh',{Cond_al,Rd,AM3_1_1}), % test all AM3 operands
+ t(OS,'ldrh',{Cond_al,Rd,AM3_1_2}),
+ t(OS,'ldrh',{Cond_al,Rd,AM3_2_1}),
+ t(OS,'ldrh',{Cond_al,Rd,AM3_2_2}),
+ t(OS,'ldrh',{Cond_al,Rd,AM3_3_1}),
+ t(OS,'ldrh',{Cond_al,Rd,AM3_3_2}),
+ t(OS,'ldrh',{Cond_al,Rd,AM3_4_1}),
+ t(OS,'ldrh',{Cond_al,Rd,AM3_4_2}),
+ t(OS,'ldrh',{Cond_al,Rd,AM3_5_1}),
+ t(OS,'ldrh',{Cond_al,Rd,AM3_5_2}),
+ t(OS,'ldrh',{Cond_al,Rd,AM3_6_1}),
+ t(OS,'ldrh',{Cond_al,Rd,AM3_6_2}),
+ t(OS,'ldrsb',{Cond_al,Rd,AM3_1_1}),
+ t(OS,'ldrsh',{Cond_al,Rd,AM3_1_1}),
+ t(OS,'mcr',{Cond_al,CpNum,CpOp3,Rd,CRn,CRm,CpOp3}),
+ t(OS,'mcr2',{CpNum,CpOp3,Rd,CRn,CRm,CpOp3}),
+ t(OS,'mcrr',{Cond_al,CpNum,CpOp4,Rd,Rn,CRm}),
+ t(OS,'mla',{Cond_al,S0,Rd,Rm,Rs,Rn}),
+ t(OS,'mov',{Cond_al,S0,Rd,AM1_1}),
+ t(OS,'mrc',{Cond_al,CpNum,CpOp3,Rd,CRn,CRm,CpOp3}),
+ t(OS,'mrc2',{CpNum,CpOp3,Rd,CRn,CRm,CpOp3}),
+ t(OS,'mrrc',{Cond_al,CpNum,CpOp4,Rd,Rn,CRm}),
+ t(OS,'mrs',{Cond_al,Rd,'cpsr'}),
+ t(OS,'msr',{Cond_al,'cpsr',FieldMask4,Imm8,Imm4}),
+ t(OS,'msr',{Cond_al,'cpsr',FieldMask4,Rm}),
+ t(OS,'mul',{Cond_al,S0,Rd,Rm,Rs}),
+ t(OS,'mvn',{Cond_al,S1,Rd,AM1_1}),
+ t(OS,'orr',{Cond_al,S0,Rd,Rn,AM1_1}),
+ t(OS,'pld',{AM2_1_1}),
+ t(OS,'qadd',{Cond_al,Rd,Rm,Rn}),
+ t(OS,'qdadd',{Cond_al,Rd,Rm,Rn}),
+ t(OS,'qdsub',{Cond_al,Rd,Rm,Rn}),
+ t(OS,'qsub',{Cond_al,Rd,Rm,Rn}),
+ t(OS,'rsb',{Cond_al,S0,Rd,Rn,AM1_1}),
+ t(OS,'rsc',{Cond_al,S0,Rd,Rn,AM1_1}),
+ t(OS,'sbc',{Cond_al,S0,Rd,Rn,AM1_1}),
+ t(OS,'smla',{BT0,BT0,Cond_al,Rd,Rm,Rs,Rn}),
+ t(OS,'smla',{BT0,BT1,Cond_al,Rd,Rm,Rs,Rn}),
+ t(OS,'smla',{BT1,BT0,Cond_al,Rd,Rm,Rs,Rn}),
+ t(OS,'smla',{BT1,BT1,Cond_al,Rd,Rm,Rs,Rn}),
+ t(OS,'smlal',{Cond_al,S0,RdLo,RdHi,Rm,Rs}),
+ t(OS,'smlal',{BT0,BT1,Cond_al,RdLo,RdHi,Rm,Rs}),
+ t(OS,'smlaw',{BT1,Cond_al,Rd,Rm,Rs,Rn}),
+ t(OS,'smull',{Cond_al,S0,RdLo,RdHi,Rm,Rs}),
+ t(OS,'smul',{BT1,BT0,Cond_al,Rd,Rm,Rs}),
+ t(OS,'smulw',{BT1,Cond_al,Rd,Rm,Rs}),
+ t(OS,'stc',{Cond_al,L0,CpNum,CRd,AM5_1_1}),
+ t(OS,'stc2',{L0,CpNum,CRd,AM5_1_1}),
+ t(OS,'stm',{Cond_al,AM4_1,Rn,Registers}),
+ t(OS,'str',{Cond_al,Rd,AM2_1_1}),
+ t(OS,'strb',{Cond_al,Rd,AM2_1_1}),
+ t(OS,'strd',{Cond_al,Rd,AM3_1_1}),
+ t(OS,'strh',{Cond_al,Rd,AM3_1_1}),
+ t(OS,'sub',{Cond_al,S0,Rd,Rn,AM1_1}),
+ t(OS,'swi',{Cond_al,Imm24}),
+ t(OS,'swp',{Cond_al,Rd,Rm,Rn}),
+ t(OS,'swpb',{Cond_al,Rd,Rm,Rn}),
+ t(OS,'teq',{Cond_al,Rn,AM1_1}),
+ t(OS,'tst',{Cond_al,Rn,AM1_1}),
+ t(OS,'umlal',{Cond_al,S0,RdLo,RdHi,Rm,Rs}),
+ t(OS,'umull',{Cond_al,S0,RdLo,RdHi,Rm,Rs}),
+ [].
+
+dotest() -> dotest1(group_leader()).
+
+dotest(File) ->
+ {ok,OS} = file:open(File, [write]),
+ dotest1(OS),
+ file:close(OS).
+
+-endif.
diff --git a/lib/hipe/arm/hipe_arm_finalise.erl b/lib/hipe/arm/hipe_arm_finalise.erl
new file mode 100644
index 0000000000..38e3efd223
--- /dev/null
+++ b/lib/hipe/arm/hipe_arm_finalise.erl
@@ -0,0 +1,73 @@
+%% -*- 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_finalise).
+-export([finalise/1]).
+-include("hipe_arm.hrl").
+
+finalise(Defun) ->
+ #defun{code=Code0} = Defun,
+ Code1 = peep(expand(Code0)),
+ Defun#defun{code=Code1}.
+
+expand(Insns) ->
+ expand_list(Insns, []).
+
+expand_list([I|Insns], Accum) ->
+ expand_list(Insns, expand_insn(I, Accum));
+expand_list([], Accum) ->
+ lists:reverse(Accum).
+
+expand_insn(I, Accum) ->
+ case I of
+ #pseudo_bc{'cond'=Cond,true_label=TrueLab,false_label=FalseLab} ->
+ [hipe_arm:mk_b_label(FalseLab),
+ hipe_arm:mk_b_label(Cond, TrueLab) |
+ Accum];
+ #pseudo_blr{} ->
+ [hipe_arm:mk_move(hipe_arm:mk_pc(), hipe_arm:mk_lr()) | Accum];
+ #pseudo_bx{src=Src} ->
+ [hipe_arm:mk_move(hipe_arm:mk_pc(), Src) | Accum];
+ #pseudo_call{funv=FunV,sdesc=SDesc,contlab=ContLab,linkage=Linkage} ->
+ [hipe_arm:mk_b_label(ContLab),
+ case FunV of
+ #arm_temp{} -> hipe_arm:mk_blx(FunV, SDesc);
+ _ -> hipe_arm:mk_bl(FunV, SDesc, Linkage)
+ end |
+ Accum];
+ #pseudo_switch{jtab=JTab,index=Index} ->
+ PC = hipe_arm:mk_pc(),
+ Am2 = hipe_arm:mk_am2(JTab, '+', {Index,'lsl',2}),
+ [hipe_arm:mk_load('ldr', PC, Am2) | Accum];
+ #pseudo_tailcall_prepare{} ->
+ Accum;
+ _ ->
+ [I|Accum]
+ end.
+
+peep(Insns) ->
+ peep_list(Insns, []).
+
+peep_list([#b_label{'cond'='al',label=Label} | (Insns = [#label{label=Label}|_])], Accum) ->
+ peep_list(Insns, Accum);
+peep_list([I|Insns], Accum) ->
+ peep_list(Insns, [I|Accum]);
+peep_list([], Accum) ->
+ lists:reverse(Accum).
diff --git a/lib/hipe/arm/hipe_arm_frame.erl b/lib/hipe/arm/hipe_arm_frame.erl
new file mode 100644
index 0000000000..316aa2ef82
--- /dev/null
+++ b/lib/hipe/arm/hipe_arm_frame.erl
@@ -0,0 +1,639 @@
+%% -*- 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_frame).
+-export([frame/1]).
+
+-include("hipe_arm.hrl").
+-include("../rtl/hipe_literals.hrl").
+
+-define(LIVENESS_ALL, hipe_arm_liveness_gpr). % since we have no FP yet
+
+frame(Defun) ->
+ Formals = fix_formals(hipe_arm:defun_formals(Defun)),
+ Temps0 = all_temps(hipe_arm:defun_code(Defun), Formals),
+ MinFrame = defun_minframe(Defun),
+ Temps = ensure_minframe(MinFrame, Temps0),
+ ClobbersLR = clobbers_lr(hipe_arm:defun_code(Defun)),
+ CFG0 = hipe_arm_cfg:init(Defun),
+ Liveness = ?LIVENESS_ALL:analyse(CFG0),
+ CFG1 = do_body(CFG0, Liveness, Formals, Temps, ClobbersLR),
+ hipe_arm_cfg:linearise(CFG1).
+
+fix_formals(Formals) ->
+ fix_formals(hipe_arm_registers:nr_args(), Formals).
+
+fix_formals(0, Rest) -> Rest;
+fix_formals(N, [_|Rest]) -> fix_formals(N-1, Rest);
+fix_formals(_, []) -> [].
+
+do_body(CFG0, Liveness, Formals, Temps, ClobbersLR) ->
+ Context = mk_context(Liveness, Formals, Temps, ClobbersLR),
+ CFG1 = do_blocks(CFG0, Context),
+ do_prologue(CFG1, Context).
+
+do_blocks(CFG, Context) ->
+ Labels = hipe_arm_cfg:labels(CFG),
+ do_blocks(Labels, CFG, Context).
+
+do_blocks([Label|Labels], CFG, Context) ->
+ Liveness = context_liveness(Context),
+ LiveOut = ?LIVENESS_ALL:liveout(Liveness, Label),
+ Block = hipe_arm_cfg:bb(CFG, Label),
+ Code = hipe_bb:code(Block),
+ NewCode = do_block(Code, LiveOut, Context),
+ NewBlock = hipe_bb:code_update(Block, NewCode),
+ NewCFG = hipe_arm_cfg:bb_add(CFG, Label, NewBlock),
+ do_blocks(Labels, NewCFG, Context);
+do_blocks([], CFG, _) ->
+ CFG.
+
+do_block(Insns, LiveOut, Context) ->
+ do_block(Insns, LiveOut, Context, context_framesize(Context), []).
+
+do_block([I|Insns], LiveOut, Context, FPoff0, RevCode) ->
+ {NewIs, FPoff1} = do_insn(I, LiveOut, Context, FPoff0),
+ do_block(Insns, LiveOut, Context, FPoff1, lists:reverse(NewIs, RevCode));
+do_block([], _, Context, FPoff, RevCode) ->
+ FPoff0 = context_framesize(Context),
+ if FPoff =:= FPoff0 -> [];
+ true -> exit({?MODULE,do_block,FPoff})
+ end,
+ lists:reverse(RevCode, []).
+
+do_insn(I, LiveOut, Context, FPoff) ->
+ case I of
+ #pseudo_blr{} ->
+ {do_pseudo_blr(I, Context, FPoff), context_framesize(Context)};
+ #pseudo_call{} ->
+ do_pseudo_call(I, LiveOut, Context, FPoff);
+ #pseudo_call_prepare{} ->
+ do_pseudo_call_prepare(I, FPoff);
+ #pseudo_move{} ->
+ {do_pseudo_move(I, Context, FPoff), FPoff};
+ #pseudo_tailcall{} ->
+ {do_pseudo_tailcall(I, Context), context_framesize(Context)};
+ _ ->
+ {[I], FPoff}
+ end.
+
+%%%
+%%% Moves, with Dst or Src possibly a pseudo
+%%%
+
+do_pseudo_move(I, Context, FPoff) ->
+ Dst = hipe_arm:pseudo_move_dst(I),
+ Src = hipe_arm:pseudo_move_src(I),
+ case temp_is_pseudo(Dst) of
+ true ->
+ Offset = pseudo_offset(Dst, FPoff, Context),
+ mk_store('str', Src, Offset, mk_sp(), []);
+ _ ->
+ case temp_is_pseudo(Src) of
+ true ->
+ Offset = pseudo_offset(Src, FPoff, Context),
+ mk_load('ldr', Dst, Offset, mk_sp(), []);
+ _ ->
+ [hipe_arm:mk_move(Dst, Src)]
+ end
+ end.
+
+pseudo_offset(Temp, FPoff, Context) ->
+ FPoff + context_offset(Context, Temp).
+
+%%%
+%%% Return - deallocate frame and emit 'ret $N' insn.
+%%%
+
+do_pseudo_blr(I, Context, FPoff) ->
+ %% XXX: perhaps use explicit pseudo_move;mtlr,
+ %% avoiding the need to hard-code Temp1 here
+ %% XXX: typically only one instruction between
+ %% the mtlr and the blr, ouch
+ restore_lr(FPoff, Context,
+ adjust_sp(FPoff + word_size() * context_arity(Context),
+ [I])).
+
+restore_lr(FPoff, Context, Rest) ->
+ case context_clobbers_lr(Context) of
+ false -> Rest;
+ true ->
+ LR = hipe_arm:mk_lr(),
+ mk_load('ldr', LR, FPoff - word_size(), mk_sp(),
+ Rest)
+ end.
+
+adjust_sp(N, Rest) ->
+ if N =:= 0 ->
+ Rest;
+ true ->
+ SP = mk_sp(),
+ hipe_arm:mk_addi(SP, SP, N, Rest)
+ end.
+
+%%%
+%%% Recursive calls.
+%%%
+
+do_pseudo_call_prepare(I, FPoff0) ->
+ %% Create outgoing arguments area on the stack.
+ NrStkArgs = hipe_arm:pseudo_call_prepare_nrstkargs(I),
+ Offset = NrStkArgs * word_size(),
+ {adjust_sp(-Offset, []), FPoff0 + Offset}.
+
+do_pseudo_call(I, LiveOut, Context, FPoff0) ->
+ #arm_sdesc{exnlab=ExnLab,arity=OrigArity} = hipe_arm:pseudo_call_sdesc(I),
+ FunV = hipe_arm:pseudo_call_funv(I),
+ LiveTemps = [Temp || Temp <- LiveOut, temp_is_pseudo(Temp)],
+ SDesc = mk_sdesc(ExnLab, Context, LiveTemps),
+ ContLab = hipe_arm:pseudo_call_contlab(I),
+ Linkage = hipe_arm:pseudo_call_linkage(I),
+ CallCode = [hipe_arm:mk_pseudo_call(FunV, SDesc, ContLab, Linkage)],
+ StkArity = erlang:max(0, OrigArity - hipe_arm_registers:nr_args()),
+ context_need_stack(Context, stack_need(FPoff0, StkArity, FunV)),
+ ArgsBytes = word_size() * StkArity,
+ {CallCode, FPoff0 - ArgsBytes}.
+
+stack_need(FPoff, StkArity, FunV) ->
+ case FunV of
+ #arm_prim{} -> FPoff;
+ #arm_mfa{m=M,f=F,a=A} ->
+ case erlang:is_builtin(M, F, A) of
+ true -> FPoff;
+ false -> stack_need_general(FPoff, StkArity)
+ end;
+ _ -> stack_need_general(FPoff, StkArity)
+ end.
+
+stack_need_general(FPoff, StkArity) ->
+ erlang:max(FPoff, FPoff + (?ARM_LEAF_WORDS - StkArity) * word_size()).
+
+%%%
+%%% Create stack descriptors for call sites.
+%%%
+
+mk_sdesc(ExnLab, Context, Temps) -> % for normal calls
+ Temps0 = only_tagged(Temps),
+ Live = mk_live(Context, Temps0),
+ Arity = context_arity(Context),
+ FSize = context_framesize(Context),
+ hipe_arm:mk_sdesc(ExnLab, (FSize div word_size())-1, Arity,
+ list_to_tuple(Live)).
+
+only_tagged(Temps)->
+ [X || X <- Temps, hipe_arm:temp_type(X) =:= 'tagged'].
+
+mk_live(Context, Temps) ->
+ lists:sort([temp_to_slot(Context, Temp) || Temp <- Temps]).
+
+temp_to_slot(Context, Temp) ->
+ (context_framesize(Context) + context_offset(Context, Temp))
+ div word_size().
+
+mk_minimal_sdesc(Context) -> % for inc_stack_0 calls
+ hipe_arm:mk_sdesc([], 0, context_arity(Context), {}).
+
+%%%
+%%% Tailcalls.
+%%%
+
+do_pseudo_tailcall(I, Context) -> % always at FPoff=context_framesize(Context)
+ Arity = context_arity(Context),
+ Args = hipe_arm:pseudo_tailcall_stkargs(I),
+ FunV = hipe_arm:pseudo_tailcall_funv(I),
+ Linkage = hipe_arm:pseudo_tailcall_linkage(I),
+ {Insns, FPoff1} = do_tailcall_args(Args, Context),
+ context_need_stack(Context, FPoff1),
+ StkArity = length(Args),
+ FPoff2 = FPoff1 + (Arity - StkArity) * word_size(),
+ context_need_stack(Context, stack_need(FPoff2, StkArity, FunV)),
+ I2 =
+ case FunV of
+ #arm_temp{} ->
+ hipe_arm:mk_bx(FunV);
+ Fun ->
+ hipe_arm:mk_b_fun(Fun, Linkage)
+ end,
+ %% XXX: break out the LR restore, just like for pseudo_blr?
+ restore_lr(context_framesize(Context), Context,
+ Insns ++ adjust_sp(FPoff2, [I2])).
+
+do_tailcall_args(Args, Context) ->
+ FPoff0 = context_framesize(Context),
+ Arity = context_arity(Context),
+ FrameTop = word_size()*Arity,
+ DangerOff = FrameTop - word_size()*length(Args),
+ %%
+ Moves = mk_moves(Args, FrameTop, []),
+ %%
+ {Stores, Simple, Conflict} =
+ split_moves(Moves, Context, DangerOff, [], [], []),
+ %% sanity check (shouldn't trigger any more)
+ if DangerOff < -FPoff0 ->
+ exit({?MODULE,do_tailcall_args,DangerOff,-FPoff0});
+ true -> []
+ end,
+ FPoff1 = FPoff0,
+ %%
+ {Pushes, Pops, FPoff2} = split_conflict(Conflict, FPoff1, [], []),
+ %%
+ TempReg = hipe_arm_registers:temp1(),
+ %%
+ {adjust_sp(-(FPoff2 - FPoff1),
+ simple_moves(Pushes, FPoff2, TempReg,
+ store_moves(Stores, FPoff2, TempReg,
+ simple_moves(Simple, FPoff2, TempReg,
+ simple_moves(Pops, FPoff2, TempReg,
+ []))))),
+ FPoff2}.
+
+mk_moves([Arg|Args], Off, Moves) ->
+ Off1 = Off - word_size(),
+ mk_moves(Args, Off1, [{Arg,Off1}|Moves]);
+mk_moves([], _, Moves) ->
+ Moves.
+
+split_moves([Move|Moves], Context, DangerOff, Stores, Simple, Conflict) ->
+ {Src,DstOff} = Move,
+ case src_is_pseudo(Src) of
+ false ->
+ split_moves(Moves, Context, DangerOff, [Move|Stores],
+ Simple, Conflict);
+ true ->
+ SrcOff = context_offset(Context, Src),
+ Type = typeof_temp(Src),
+ if SrcOff =:= DstOff ->
+ split_moves(Moves, Context, DangerOff, Stores,
+ Simple, Conflict);
+ SrcOff >= DangerOff ->
+ split_moves(Moves, Context, DangerOff, Stores,
+ Simple, [{SrcOff,DstOff,Type}|Conflict]);
+ true ->
+ split_moves(Moves, Context, DangerOff, Stores,
+ [{SrcOff,DstOff,Type}|Simple], Conflict)
+ end
+ end;
+split_moves([], _, _, Stores, Simple, Conflict) ->
+ {Stores, Simple, Conflict}.
+
+split_conflict([{SrcOff,DstOff,Type}|Conflict], FPoff, Pushes, Pops) ->
+ FPoff1 = FPoff + word_size(),
+ Push = {SrcOff,-FPoff1,Type},
+ Pop = {-FPoff1,DstOff,Type},
+ split_conflict(Conflict, FPoff1, [Push|Pushes], [Pop|Pops]);
+split_conflict([], FPoff, Pushes, Pops) ->
+ {lists:reverse(Pushes), Pops, FPoff}.
+
+simple_moves([{SrcOff,DstOff,Type}|Moves], FPoff, TempReg, Rest) ->
+ Temp = hipe_arm:mk_temp(TempReg, Type),
+ SP = mk_sp(),
+ LoadOff = FPoff+SrcOff,
+ StoreOff = FPoff+DstOff,
+ simple_moves(Moves, FPoff, TempReg,
+ mk_load('ldr', Temp, LoadOff, SP,
+ mk_store('str', Temp, StoreOff, SP,
+ Rest)));
+simple_moves([], _, _, Rest) ->
+ Rest.
+
+store_moves([{Src,DstOff}|Moves], FPoff, TempReg, Rest) ->
+ %%Type = typeof_temp(Src),
+ SP = mk_sp(),
+ StoreOff = FPoff+DstOff,
+ {NewSrc,FixSrc} =
+ case hipe_arm:is_temp(Src) of
+ true ->
+ {Src, []};
+ _ ->
+ Temp = hipe_arm:mk_temp(TempReg, 'untagged'),
+ {Temp, hipe_arm:mk_li(Temp, Src)}
+ end,
+ store_moves(Moves, FPoff, TempReg,
+ FixSrc ++ mk_store('str', NewSrc, StoreOff, SP, Rest));
+store_moves([], _, _, Rest) ->
+ Rest.
+
+%%%
+%%% Contexts
+%%%
+
+-record(context, {liveness, framesize, arity, map, clobbers_lr, ref_maxstack}).
+
+mk_context(Liveness, Formals, Temps, ClobbersLR) ->
+ {Map, MinOff} = mk_temp_map(Formals, ClobbersLR, Temps),
+ FrameSize = (-MinOff),
+ RefMaxStack = hipe_bifs:ref(FrameSize),
+ #context{liveness=Liveness,
+ framesize=FrameSize, arity=length(Formals),
+ map=Map, clobbers_lr=ClobbersLR, ref_maxstack=RefMaxStack}.
+
+context_need_stack(#context{ref_maxstack=RM}, N) ->
+ M = hipe_bifs:ref_get(RM),
+ if N > M -> hipe_bifs:ref_set(RM, N);
+ true -> []
+ end.
+
+context_maxstack(#context{ref_maxstack=RM}) ->
+ hipe_bifs:ref_get(RM).
+
+context_arity(#context{arity=Arity}) ->
+ Arity.
+
+context_framesize(#context{framesize=FrameSize}) ->
+ FrameSize.
+
+context_liveness(#context{liveness=Liveness}) ->
+ Liveness.
+
+context_offset(#context{map=Map}, Temp) ->
+ tmap_lookup(Map, Temp).
+
+context_clobbers_lr(#context{clobbers_lr=ClobbersLR}) -> ClobbersLR.
+
+mk_temp_map(Formals, ClobbersLR, Temps) ->
+ {Map, 0} = enter_vars(Formals, word_size() * length(Formals),
+ tmap_empty()),
+ TempsList = tset_to_list(Temps),
+ AllTemps =
+ case ClobbersLR of
+ false -> TempsList;
+ true ->
+ RA = hipe_arm:mk_new_temp('untagged'),
+ [RA|TempsList]
+ end,
+ enter_vars(AllTemps, 0, Map).
+
+enter_vars([V|Vs], PrevOff, Map) ->
+ Off =
+ case hipe_arm:temp_type(V) of
+ 'double' -> PrevOff - 2*word_size();
+ _ -> PrevOff - word_size()
+ end,
+ enter_vars(Vs, Off, tmap_bind(Map, V, Off));
+enter_vars([], Off, Map) ->
+ {Map, Off}.
+
+tmap_empty() ->
+ gb_trees:empty().
+
+tmap_bind(Map, Key, Val) ->
+ gb_trees:insert(Key, Val, Map).
+
+tmap_lookup(Map, Key) ->
+ gb_trees:get(Key, Map).
+
+%%%
+%%% do_prologue: prepend stack frame allocation code.
+%%%
+%%% NewStart:
+%%% temp1 = *(P + P_SP_LIMIT)
+%%% temp2 = SP - MaxStack
+%%% cmp temp2, temp1
+%%% if (ltu) goto IncStack else goto AllocFrame
+%%% AllocFrame:
+%%% SP = temp2 [if FrameSize == MaxStack]
+%%% SP -= FrameSize [if FrameSize != MaxStack]
+%%% *(SP + FrameSize-WordSize) = LR [if ClobbersLR]
+%%% goto OldStart
+%%% OldStart:
+%%% ...
+%%% IncStack:
+%%% temp1 = LR
+%%% bl inc_stack
+%%% LR = temp1
+%%% goto NewStart
+
+do_prologue(CFG, Context) ->
+ MaxStack = context_maxstack(Context),
+ if MaxStack > 0 ->
+ FrameSize = context_framesize(Context),
+ OldStartLab = hipe_arm_cfg:start_label(CFG),
+ NewStartLab = hipe_gensym:get_next_label(arm),
+ %%
+ P = hipe_arm:mk_temp(hipe_arm_registers:proc_pointer(), 'untagged'),
+ Temp1 = mk_temp1(),
+ SP = mk_sp(),
+ %%
+ LR = hipe_arm:mk_lr(),
+ ClobbersLR = context_clobbers_lr(Context),
+ GotoOldStartCode = [hipe_arm:mk_b_label(OldStartLab)],
+ AllocFrameCodeTail =
+ case ClobbersLR of
+ false -> GotoOldStartCode;
+ true -> mk_store('str', LR, FrameSize-word_size(), SP, GotoOldStartCode)
+ end,
+ %%
+ Arity = context_arity(Context),
+ Guaranteed = erlang:max(0, (?ARM_LEAF_WORDS - Arity) * word_size()),
+ %%
+ {CFG1,NewStartCode} =
+ if MaxStack =< Guaranteed ->
+ %% io:format("~w: MaxStack ~w =< Guaranteed ~w :-)\n", [?MODULE,MaxStack,Guaranteed]),
+ AllocFrameCode = adjust_sp(-FrameSize, AllocFrameCodeTail),
+ NewStartCode0 = AllocFrameCode, % no mflr needed
+ {CFG,NewStartCode0};
+ true ->
+ %% io:format("~w: MaxStack ~w > Guaranteed ~w :-(\n", [?MODULE,MaxStack,Guaranteed]),
+ AllocFrameLab = hipe_gensym:get_next_label(arm),
+ IncStackLab = hipe_gensym:get_next_label(arm),
+ Temp2 = mk_temp2(),
+ %%
+ NewStartCodeTail2 =
+ [hipe_arm:mk_pseudo_bc('lo', IncStackLab, AllocFrameLab, 0.01)],
+ NewStartCodeTail1 = NewStartCodeTail2, % no mflr needed
+ NewStartCode0 =
+ mk_load('ldr', Temp1, ?P_NSP_LIMIT, P,
+ hipe_arm:mk_addi(Temp2, SP, -MaxStack,
+ [hipe_arm:mk_cmp('cmp', Temp2, Temp1) |
+ NewStartCodeTail1])),
+ %%
+ AllocFrameCode =
+ if MaxStack =:= FrameSize ->
+ %% io:format("~w: MaxStack =:= FrameSize =:= ~w :-)\n", [?MODULE,MaxStack]),
+ [hipe_arm:mk_move(SP, Temp2) |
+ AllocFrameCodeTail];
+ true ->
+ %% io:format("~w: MaxStack ~w =/= FrameSize ~w :-(\n", [?MODULE,MaxStack,FrameSize]),
+ adjust_sp(-FrameSize, AllocFrameCodeTail)
+ end,
+ %%
+ IncStackCodeTail =
+ [hipe_arm:mk_bl(hipe_arm:mk_prim('inc_stack_0'),
+ mk_minimal_sdesc(Context), not_remote),
+ hipe_arm:mk_mtlr(Temp1),
+ hipe_arm:mk_b_label(NewStartLab)],
+ IncStackCode =
+ [hipe_arm:mk_mflr(Temp1) | IncStackCodeTail], % mflr always needed
+ %%
+ CFG0a = hipe_arm_cfg:bb_add(CFG, AllocFrameLab,
+ hipe_bb:mk_bb(AllocFrameCode)),
+ CFG0b = hipe_arm_cfg:bb_add(CFG0a, IncStackLab,
+ hipe_bb:mk_bb(IncStackCode)),
+ %%
+ {CFG0b,NewStartCode0}
+ end,
+ %%
+ CFG2 = hipe_arm_cfg:bb_add(CFG1, NewStartLab,
+ hipe_bb:mk_bb(NewStartCode)),
+ hipe_arm_cfg:start_label_update(CFG2, NewStartLab);
+ true ->
+ CFG
+ end.
+
+%%% Create a load instruction.
+%%% May clobber Dst early for large offsets. In principle we could
+%%% clobber TEMP2 if Dst =:= Base, but Dst =/= Base here in frame.
+
+mk_load(LdOp, Dst, Offset, Base, Rest) ->
+ hipe_arm:mk_load(LdOp, Dst, Base, Offset, 'error', Rest).
+
+%%% Create a store instruction.
+%%% May clobber TEMP2 for large offsets.
+
+mk_store(StOp, Src, Offset, Base, Rest) ->
+ hipe_arm:mk_store(StOp, Src, Base, Offset, 'temp2', Rest).
+
+%%% typeof_temp -- what's temp's type?
+
+typeof_temp(Temp) ->
+ hipe_arm:temp_type(Temp).
+
+%%% Cons up an 'SP' Temp.
+
+mk_sp() ->
+ hipe_arm:mk_temp(hipe_arm_registers:stack_pointer(), 'untagged').
+
+%%% Cons up a 'TEMP1' Temp.
+
+mk_temp1() ->
+ hipe_arm:mk_temp(hipe_arm_registers:temp1(), 'untagged').
+
+%%% Cons up a 'TEMP2' Temp.
+
+mk_temp2() ->
+ hipe_arm:mk_temp(hipe_arm_registers:temp2(), 'untagged').
+
+%%% Check if an operand is a pseudo-Temp.
+
+src_is_pseudo(Src) ->
+ hipe_arm:is_temp(Src) andalso temp_is_pseudo(Src).
+
+temp_is_pseudo(Temp) ->
+ not(hipe_arm:temp_is_precoloured(Temp)).
+
+%%%
+%%% Detect if a Defun's body clobbers LR.
+%%%
+
+clobbers_lr(Insns) ->
+ LRreg = hipe_arm_registers:lr(),
+ LRtagged = hipe_arm:mk_temp(LRreg, 'tagged'),
+ LRuntagged = hipe_arm:mk_temp(LRreg, 'untagged'),
+ clobbers_lr(Insns, LRtagged, LRuntagged).
+
+clobbers_lr([I|Insns], LRtagged, LRuntagged) ->
+ Defs = hipe_arm_defuse:insn_def_gpr(I),
+ case lists:member(LRtagged, Defs) of
+ true -> true;
+ false ->
+ case lists:member(LRuntagged, Defs) of
+ true -> true;
+ false -> clobbers_lr(Insns, LRtagged, LRuntagged)
+ end
+ end;
+clobbers_lr([], _LRtagged, _LRuntagged) -> false.
+
+%%%
+%%% Build the set of all temps used in a Defun's body.
+%%%
+
+all_temps(Code, Formals) ->
+ S0 = find_temps(Code, tset_empty()),
+ S1 = tset_del_list(S0, Formals),
+ tset_filter(S1, fun(T) -> temp_is_pseudo(T) end).
+
+find_temps([I|Insns], S0) ->
+ S1 = tset_add_list(S0, hipe_arm_defuse:insn_def_all(I)),
+ S2 = tset_add_list(S1, hipe_arm_defuse:insn_use_all(I)),
+ find_temps(Insns, S2);
+find_temps([], S) ->
+ S.
+
+tset_empty() ->
+ gb_sets:new().
+
+tset_size(S) ->
+ gb_sets:size(S).
+
+tset_insert(S, T) ->
+ gb_sets:add_element(T, S).
+
+tset_add_list(S, Ts) ->
+ gb_sets:union(S, gb_sets:from_list(Ts)).
+
+tset_del_list(S, Ts) ->
+ gb_sets:subtract(S, gb_sets:from_list(Ts)).
+
+tset_filter(S, F) ->
+ gb_sets:filter(F, S).
+
+tset_to_list(S) ->
+ gb_sets:to_list(S).
+
+%%%
+%%% Compute minimum permissible frame size, ignoring spilled temps.
+%%% This is done to ensure that we won't have to adjust the frame size
+%%% in the middle of a tailcall.
+%%%
+
+defun_minframe(Defun) ->
+ MaxTailArity = body_mta(hipe_arm:defun_code(Defun), 0),
+ MyArity = length(fix_formals(hipe_arm:defun_formals(Defun))),
+ erlang:max(MaxTailArity - MyArity, 0).
+
+body_mta([I|Code], MTA) ->
+ body_mta(Code, insn_mta(I, MTA));
+body_mta([], MTA) ->
+ MTA.
+
+insn_mta(I, MTA) ->
+ case I of
+ #pseudo_tailcall{arity=Arity} ->
+ erlang:max(MTA, Arity - hipe_arm_registers:nr_args());
+ _ -> MTA
+ end.
+
+%%%
+%%% Ensure that we have enough temps to satisfy the minimum frame size,
+%%% if necessary by prepending unused dummy temps.
+%%%
+
+ensure_minframe(MinFrame, Temps) ->
+ ensure_minframe(MinFrame, tset_size(Temps), Temps).
+
+ensure_minframe(MinFrame, Frame, Temps) ->
+ if MinFrame > Frame ->
+ Temp = hipe_arm:mk_new_temp('untagged'),
+ ensure_minframe(MinFrame, Frame+1, tset_insert(Temps, Temp));
+ true -> Temps
+ end.
+
+word_size() ->
+ 4.
diff --git a/lib/hipe/arm/hipe_arm_liveness_gpr.erl b/lib/hipe/arm/hipe_arm_liveness_gpr.erl
new file mode 100644
index 0000000000..cab81c47a1
--- /dev/null
+++ b/lib/hipe/arm/hipe_arm_liveness_gpr.erl
@@ -0,0 +1,38 @@
+%% -*- 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_liveness_gpr).
+-export([analyse/1]).
+-export([liveout/2]).
+
+-include("hipe_arm.hrl").
+-include("../flow/liveness.inc").
+
+analyse(CFG) -> analyze(CFG).
+cfg_bb(CFG, L) -> hipe_arm_cfg:bb(CFG, L).
+cfg_postorder(CFG) -> hipe_arm_cfg:postorder(CFG).
+cfg_succ(CFG, L) -> hipe_arm_cfg:succ(CFG, L).
+uses(Insn) -> hipe_arm_defuse:insn_use_gpr(Insn).
+defines(Insn) -> hipe_arm_defuse:insn_def_gpr(Insn).
+liveout_no_succ() ->
+ ordsets:from_list(lists:map(fun({Reg,Type}) ->
+ hipe_arm:mk_temp(Reg, Type)
+ end,
+ hipe_arm_registers:live_at_return())).
diff --git a/lib/hipe/arm/hipe_arm_main.erl b/lib/hipe/arm/hipe_arm_main.erl
new file mode 100644
index 0000000000..5243b3579e
--- /dev/null
+++ b/lib/hipe/arm/hipe_arm_main.erl
@@ -0,0 +1,58 @@
+%% -*- 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_main).
+-export([rtl_to_arm/3]).
+
+rtl_to_arm(MFA, RTL, Options) ->
+ Defun1 = hipe_rtl_to_arm:translate(RTL),
+ %% io:format("~w: after translate\n", [?MODULE]),
+ %% hipe_arm_pp:pp(Defun1),
+ Defun2 = hipe_arm_ra:ra(Defun1, Options),
+ %% io:format("~w: after regalloc\n", [?MODULE]),
+ %% hipe_arm_pp:pp(Defun2),
+ Defun3 = hipe_arm_frame:frame(Defun2),
+ %% io:format("~w: after frame\n", [?MODULE]),
+ %% hipe_arm_pp:pp(Defun3),
+ Defun4 = hipe_arm_finalise:finalise(Defun3),
+ %% io:format("~w: after finalise\n", [?MODULE]),
+ pp(Defun4, MFA, Options),
+ {native, arm, {unprofiled, Defun4}}.
+
+pp(Defun, MFA, Options) ->
+ case proplists:get_value(pp_native, Options) of
+ true ->
+ hipe_arm_pp:pp(Defun);
+ {only,Lst} when is_list(Lst) ->
+ case lists:member(MFA,Lst) of
+ true ->
+ hipe_arm_pp:pp(Defun);
+ false ->
+ ok
+ end;
+ {only,MFA} ->
+ hipe_arm_pp:pp(Defun);
+ {file,FileName} ->
+ {ok, File} = file:open(FileName, [write,append]),
+ hipe_arm_pp:pp(File, Defun),
+ ok = file:close(File);
+ _ ->
+ ok
+ end.
diff --git a/lib/hipe/arm/hipe_arm_pp.erl b/lib/hipe/arm/hipe_arm_pp.erl
new file mode 100644
index 0000000000..7ce8421994
--- /dev/null
+++ b/lib/hipe/arm/hipe_arm_pp.erl
@@ -0,0 +1,351 @@
+%% -*- 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_pp).
+-export([pp/1, pp/2, pp_insn/1]).
+
+-include("hipe_arm.hrl").
+
+pp(Defun) ->
+ pp(standard_io, Defun).
+
+pp(Dev, #defun{mfa={M,F,A}, code=Code, data=Data}) ->
+ Fname = atom_to_list(M)++"_"++atom_to_list(F)++"_"++integer_to_list(A),
+ io:format(Dev, "\t.text\n", []),
+ io:format(Dev, "\t.align 4\n", []),
+ io:format(Dev, "\t.global ~s\n", [Fname]),
+ io:format(Dev, "~s:\n", [Fname]),
+ pp_insns(Dev, Code, Fname),
+ io:format(Dev, "\t.rodata\n", []),
+ io:format(Dev, "\t.align 4\n", []),
+ hipe_data_pp:pp(Dev, Data, arm, Fname),
+ io:format(Dev, "\n", []).
+
+pp_insns(Dev, [I|Is], Fname) ->
+ pp_insn(Dev, I, Fname),
+ pp_insns(Dev, Is, Fname);
+pp_insns(_, [], _) ->
+ [].
+
+pp_insn(I) ->
+ pp_insn(standard_io, I, "").
+
+pp_insn(Dev, I, Pre) ->
+ case I of
+ #alu{aluop=AluOp, s=S, dst=Dst, src=Src, am1=Am1} ->
+ io:format(Dev, "\t~s~s ", [alu_op_name(AluOp), s_name(S)]),
+ pp_temp(Dev, Dst),
+ io:format(Dev, ", ", []),
+ pp_temp(Dev, Src),
+ io:format(Dev, ", ", []),
+ pp_am1(Dev, Am1),
+ io:format(Dev, "\n", []);
+ #b_fun{'fun'=Fun, linkage=Linkage} ->
+ io:format(Dev, "\tb ", []),
+ pp_fun(Dev, Fun),
+ io:format(Dev, " # ~w\n", [Linkage]);
+ #b_label{'cond'=Cond, label=Label} ->
+ io:format(Dev, "\tb~s .~s_~w\n", [cond_name(Cond), Pre, Label]);
+ #bl{'fun'=Fun, sdesc=SDesc, linkage=Linkage} ->
+ io:format(Dev, "\tbl ", []),
+ pp_fun(Dev, Fun),
+ io:format(Dev, " #", []),
+ pp_sdesc(Dev, Pre, SDesc),
+ io:format(Dev, " ~w\n", [Linkage]);
+ #blx{src=Src, sdesc=SDesc} ->
+ io:format(Dev, "\tblx ", []),
+ pp_temp(Dev, Src),
+ io:format(Dev, " # ", []),
+ pp_sdesc(Dev, Pre, SDesc),
+ io:format(Dev, "\n", []);
+ #cmp{cmpop=CmpOp, src=Src, am1=Am1} ->
+ io:format(Dev, "\t~s ", [cmp_op_name(CmpOp)]),
+ pp_temp(Dev, Src),
+ io:format(Dev, ", ", []),
+ pp_am1(Dev, Am1),
+ io:format(Dev, "\n", []);
+ #comment{term=Term} ->
+ io:format(Dev, "\t# ~p\n", [Term]);
+ #label{label=Label} ->
+ io:format(Dev, ".~s_~w:~n", [Pre, Label]);
+ #load{ldop=LdOp, dst=Dst, am2=Am2} ->
+ io:format(Dev, "\t~w ", [ldop_name(LdOp)]),
+ pp_temp(Dev, Dst),
+ io:format(Dev, ", ", []),
+ pp_am2(Dev, Am2),
+ io:format(Dev, "\n", []);
+ #ldrsb{dst=Dst, am3=Am3} ->
+ io:format(Dev, "\tldrsb ", []),
+ pp_temp(Dev, Dst),
+ io:format(Dev, ", ", []),
+ pp_am3(Dev, Am3),
+ io:format(Dev, "\n", []);
+ #move{movop=MovOp, s=S, dst=Dst, am1=Am1} ->
+ io:format(Dev, "\t~s~s ", [mov_op_name(MovOp), s_name(S)]),
+ pp_temp(Dev, Dst),
+ io:format(Dev, ", ", []),
+ pp_am1(Dev, Am1),
+ io:format(Dev, "\n", []);
+ #pseudo_bc{'cond'=Cond, true_label=TrueLab, false_label=FalseLab, pred=Pred} ->
+ io:format(Dev, "\tpseudo_bc ~w, .~s_~w # .~s_~w ~.2f\n",
+ [cond_name(Cond), Pre, TrueLab, Pre, FalseLab, Pred]);
+ #pseudo_blr{} ->
+ io:format(Dev, "\tpseudo_blr\n", []);
+ #pseudo_bx{src=Src} ->
+ io:format(Dev, "\tpseudo_bx ", []),
+ pp_temp(Dev, Src),
+ io:format(Dev, "\n", []);
+ #pseudo_call{funv=FunV, sdesc=SDesc, contlab=ContLab, linkage=Linkage} ->
+ io:format(Dev, "\tpseudo_call ", []),
+ pp_funv(Dev, FunV),
+ io:format(Dev, " # contlab .~s_~w", [Pre, ContLab]),
+ pp_sdesc(Dev, Pre, SDesc),
+ io:format(Dev, " ~w\n", [Linkage]);
+ #pseudo_call_prepare{nrstkargs=NrStkArgs} ->
+ SP = hipe_arm_registers:reg_name_gpr(hipe_arm_registers:stack_pointer()),
+ io:format(Dev, "\tsub ~s, ~s, ~w # pseudo_call_prepare\n",
+ [SP, SP, (4*NrStkArgs)]);
+ #pseudo_li{dst=Dst, imm=Imm} ->
+ io:format(Dev, "\tpseudo_li ", []),
+ pp_temp(Dev, Dst),
+ io:format(Dev, ", ", []),
+ pp_imm(Dev, Imm),
+ io:format(Dev, "\n", []);
+ #pseudo_move{dst=Dst, src=Src} ->
+ io:format(Dev, "\tpseudo_move ", []),
+ pp_temp(Dev, Dst),
+ io:format(Dev, ", ", []),
+ pp_temp(Dev, Src),
+ io:format(Dev, "\n", []);
+ #pseudo_switch{jtab=JTab, index=Index, labels=Labels} ->
+ io:format(Dev, "\tpseudo_switch ", []),
+ pp_temp(Dev, JTab),
+ io:format(Dev, "[", []),
+ pp_temp(Dev, Index),
+ io:format(Dev, "]", []),
+ case Labels of
+ [] -> [];
+ _ ->
+ io:format(Dev, " #", []),
+ pp_labels(Dev, Labels, Pre)
+ end,
+ io:format(Dev, "\n", []);
+ #pseudo_tailcall{funv=FunV, arity=Arity, stkargs=StkArgs, linkage=Linkage} ->
+ io:format(Dev, "\tpseudo_tailcall ", []),
+ pp_funv(Dev, FunV),
+ io:format(Dev, "/~w (", [Arity]),
+ pp_args(Dev, StkArgs),
+ io:format(Dev, ") ~w\n", [Linkage]);
+ #pseudo_tailcall_prepare{} ->
+ io:format(Dev, "\tpseudo_tailcall_prepare\n", []);
+ #smull{dstlo=DstLo, dsthi=DstHi, src1=Src1, src2=Src2} ->
+ io:format(Dev, "\tsmull ", []),
+ pp_temp(Dev, DstLo),
+ io:format(Dev, ", ", []),
+ pp_temp(Dev, DstHi),
+ io:format(Dev, ", ", []),
+ pp_temp(Dev, Src1),
+ io:format(Dev, ", ", []),
+ pp_temp(Dev, Src2),
+ io:format(Dev, "\n", []);
+ #store{stop=StOp, src=Src, am2=Am2} ->
+ io:format(Dev, "\tstr~s ", [stop_suffix(StOp)]),
+ pp_temp(Dev, Src),
+ io:format(Dev, ", ", []),
+ pp_am2(Dev, Am2),
+ io:format(Dev, "\n", []);
+ _ ->
+ exit({?MODULE, pp_insn, I})
+ end.
+
+to_hex(N) ->
+ io_lib:format("~.16x", [N, "0x"]).
+
+pp_sdesc(Dev, Pre, #arm_sdesc{exnlab=ExnLab,fsize=FSize,arity=Arity,live=Live}) ->
+ pp_sdesc_exnlab(Dev, Pre, ExnLab),
+ io:format(Dev, " ~s ~w [", [to_hex(FSize), Arity]),
+ pp_sdesc_live(Dev, Live),
+ io:format(Dev, "]", []).
+
+pp_sdesc_exnlab(Dev, _, []) -> io:format(Dev, " []", []);
+pp_sdesc_exnlab(Dev, Pre, ExnLab) -> io:format(Dev, " .~s_~w", [Pre, ExnLab]).
+
+pp_sdesc_live(_, {}) -> [];
+pp_sdesc_live(Dev, Live) -> pp_sdesc_live(Dev, Live, 1).
+
+pp_sdesc_live(Dev, Live, I) ->
+ io:format(Dev, "~s", [to_hex(element(I, Live))]),
+ if I < tuple_size(Live) ->
+ io:format(Dev, ",", []),
+ pp_sdesc_live(Dev, Live, I+1);
+ true -> []
+ end.
+
+pp_labels(Dev, [Label|Labels], Pre) ->
+ io:format(Dev, " .~s_~w", [Pre, Label]),
+ pp_labels(Dev, Labels, Pre);
+pp_labels(_, [], _) ->
+ [].
+
+pp_fun(Dev, Fun) ->
+ case Fun of
+ #arm_mfa{m=M, f=F, a=A} ->
+ io:format(Dev, "~w:~w/~w", [M, F, A]);
+ #arm_prim{prim=Prim} ->
+ io:format(Dev, "~w", [Prim])
+ end.
+
+pp_funv(Dev, FunV) ->
+ case FunV of
+ #arm_temp{} ->
+ pp_temp(Dev, FunV);
+ Fun ->
+ pp_fun(Dev, Fun)
+ end.
+
+alu_op_name(Op) -> Op.
+
+cond_name(Cond) ->
+ case Cond of
+ 'al' -> "";
+ _ -> Cond
+ end.
+
+s_name(S) ->
+ case S of
+ true -> "s";
+ false -> ""
+ end.
+
+cmp_op_name(Op) -> Op.
+
+mov_op_name(Op) -> Op.
+
+ldop_name(LdOp) -> LdOp.
+
+stop_suffix(StOp) ->
+ case StOp of
+ 'str' -> "";
+ 'strb' -> "b"
+ end.
+
+pp_temp(Dev, Temp=#arm_temp{reg=Reg, type=Type}) ->
+ case hipe_arm:temp_is_precoloured(Temp) of
+ true ->
+ Name =
+%%% case Type of
+%%% 'double' ->
+%%% hipe_arm_registers:reg_name_fpr(Reg);
+%%% _ ->
+ hipe_arm_registers:reg_name_gpr(Reg)
+%%% end
+ ,
+ io:format(Dev, "~s", [Name]);
+ false ->
+ Tag =
+ case Type of
+%%% double -> "f";
+ tagged -> "t";
+ untagged -> "u"
+ end,
+ io:format(Dev, "~s~w", [Tag, Reg])
+ end.
+
+pp_hex(Dev, Value) -> io:format(Dev, "~s", [to_hex(Value)]).
+
+pp_imm(Dev, Value) ->
+ if is_integer(Value) -> pp_hex(Dev, Value);
+ true -> io:format(Dev, "~w", [Value])
+ end.
+
+pp_am1(Dev, Am1) ->
+ case Am1 of
+ #arm_temp{} ->
+ pp_temp(Dev, Am1);
+ {Src,rrx} ->
+ pp_temp(Dev, Src),
+ io:format(Dev, ", rrx", []);
+ {Src,ShiftOp,ShiftArg} ->
+ pp_temp(Dev, Src),
+ io:format(Dev, ", ~w ", [ShiftOp]),
+ case ShiftArg of
+ #arm_temp{} ->
+ pp_temp(Dev, ShiftArg);
+ Imm5 ->
+ io:format(Dev, "#~w", [Imm5])
+ end;
+ {Imm8,Imm4} ->
+ io:format(Dev, "#~w, 2*~w", [Imm8,Imm4])
+ end.
+
+pp_am2(Dev, #am2{src=Src,sign=Sign,offset=Am2Offset}) ->
+ io:format(Dev, "[", []),
+ pp_temp(Dev, Src),
+ io:format(Dev, ",~s", [sign_name(Sign)]),
+ case Am2Offset of
+ #arm_temp{} ->
+ pp_temp(Dev, Am2Offset);
+ {Src2,rrx} ->
+ pp_temp(Dev, Src2),
+ io:format(Dev, ", rrx", []);
+ {Src2,ShiftOp,Imm5} ->
+ pp_temp(Dev, Src2),
+ io:format(Dev, ",~w #~w", [ShiftOp,Imm5]);
+ Imm12 ->
+ io:format(Dev, "#~w", [Imm12])
+ end,
+ io:format(Dev, "]", []).
+
+pp_am3(Dev, #am3{src=Src,sign=Sign,offset=Am3Offset}) ->
+ io:format(Dev, "[", []),
+ pp_temp(Dev, Src),
+ io:format(Dev, ",~s", [sign_name(Sign)]),
+ case Am3Offset of
+ #arm_temp{} -> pp_temp(Dev, Am3Offset);
+ Imm8 -> io:format(Dev, "~w", [Imm8])
+ end,
+ io:format(Dev, "]", []).
+
+sign_name(Sign) ->
+ case Sign of
+ '+' -> "";
+ '-' -> "-"
+ end.
+
+pp_arg(Dev, Arg) ->
+ case Arg of
+ #arm_temp{} ->
+ pp_temp(Dev, Arg);
+ _ ->
+ pp_hex(Dev, Arg)
+ end.
+
+pp_args(Dev, [A|As]) ->
+ pp_arg(Dev, A),
+ pp_comma_args(Dev, As);
+pp_args(_, []) ->
+ [].
+
+pp_comma_args(Dev, [A|As]) ->
+ io:format(Dev, ", ", []),
+ pp_arg(Dev, A),
+ pp_comma_args(Dev, As);
+pp_comma_args(_, []) ->
+ [].
diff --git a/lib/hipe/arm/hipe_arm_ra.erl b/lib/hipe/arm/hipe_arm_ra.erl
new file mode 100644
index 0000000000..bdd9e228e0
--- /dev/null
+++ b/lib/hipe/arm/hipe_arm_ra.erl
@@ -0,0 +1,56 @@
+%% -*- 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_ra).
+-export([ra/2]).
+
+ra(Defun0, Options) ->
+ %% hipe_arm_pp:pp(Defun0),
+ {Defun1, Coloring_fp, SpillIndex}
+ = case proplists:get_bool(inline_fp, Options) of
+%% true ->
+%% hipe_regalloc_loop:ra_fp(Defun0, Options,
+%% hipe_coalescing_regalloc,
+%% hipe_arm_specific_fp);
+ false ->
+ {Defun0,[],0}
+ end,
+ %% hipe_arm_pp:pp(Defun1),
+ {Defun2, Coloring}
+ = case proplists:get_value(regalloc, Options, coalescing) of
+ coalescing ->
+ ra(Defun1, SpillIndex, Options, hipe_coalescing_regalloc);
+ optimistic ->
+ ra(Defun1, SpillIndex, Options, hipe_optimistic_regalloc);
+ graph_color ->
+ ra(Defun1, SpillIndex, Options, hipe_graph_coloring_regalloc);
+ linear_scan ->
+ hipe_arm_ra_ls:ra(Defun1, SpillIndex, Options);
+ naive ->
+ hipe_arm_ra_naive:ra(Defun1, Coloring_fp, Options);
+ _ ->
+ exit({unknown_regalloc_compiler_option,
+ proplists:get_value(regalloc,Options)})
+ end,
+ %% hipe_arm_pp:pp(Defun2),
+ hipe_arm_ra_finalise:finalise(Defun2, Coloring, Coloring_fp).
+
+ra(Defun, SpillIndex, Options, RegAllocMod) ->
+ hipe_regalloc_loop:ra(Defun, SpillIndex, Options, RegAllocMod, hipe_arm_specific).
diff --git a/lib/hipe/arm/hipe_arm_ra_finalise.erl b/lib/hipe/arm/hipe_arm_ra_finalise.erl
new file mode 100644
index 0000000000..9edc362e90
--- /dev/null
+++ b/lib/hipe/arm/hipe_arm_ra_finalise.erl
@@ -0,0 +1,285 @@
+%% -*- 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_ra_finalise).
+-export([finalise/3]).
+-include("hipe_arm.hrl").
+
+finalise(Defun, TempMap, _FPMap0=[]) ->
+ Code = hipe_arm:defun_code(Defun),
+ {_, SpillLimit} = hipe_arm:defun_var_range(Defun),
+ Map = mk_ra_map(TempMap, SpillLimit),
+ NewCode = ra_code(Code, Map, []),
+ Defun#defun{code=NewCode}.
+
+ra_code([I|Insns], Map, Accum) ->
+ ra_code(Insns, Map, [ra_insn(I, Map) | Accum]);
+ra_code([], _Map, Accum) ->
+ lists:reverse(Accum).
+
+ra_insn(I, Map) ->
+ case I of
+ #alu{} -> ra_alu(I, Map);
+ #cmp{} -> ra_cmp(I, Map);
+ #load{} -> ra_load(I, Map);
+ #ldrsb{} -> ra_ldrsb(I, Map);
+ #move{} -> ra_move(I, Map);
+ #pseudo_call{} -> ra_pseudo_call(I, Map);
+ #pseudo_li{} -> ra_pseudo_li(I, Map);
+ #pseudo_move{} -> ra_pseudo_move(I, Map);
+ #pseudo_switch{} -> ra_pseudo_switch(I, Map);
+ #pseudo_tailcall{} -> ra_pseudo_tailcall(I, Map);
+ #smull{} -> ra_smull(I, Map);
+ #store{} -> ra_store(I, Map);
+ _ -> I
+ end.
+
+ra_alu(I=#alu{dst=Dst,src=Src,am1=Am1}, Map) ->
+ NewDst = ra_temp(Dst, Map),
+ NewSrc = ra_temp(Src, Map),
+ NewAm1 = ra_am1(Am1, Map),
+ I#alu{dst=NewDst,src=NewSrc,am1=NewAm1}.
+
+ra_cmp(I=#cmp{src=Src,am1=Am1}, Map) ->
+ NewSrc = ra_temp(Src, Map),
+ NewAm1 = ra_am1(Am1, Map),
+ I#cmp{src=NewSrc,am1=NewAm1}.
+
+ra_load(I=#load{dst=Dst,am2=Am2}, Map) ->
+ NewDst = ra_temp(Dst, Map),
+ NewAm2 = ra_am2(Am2, Map),
+ I#load{dst=NewDst,am2=NewAm2}.
+
+ra_ldrsb(I=#ldrsb{dst=Dst,am3=Am3}, Map) ->
+ NewDst = ra_temp(Dst, Map),
+ NewAm3 = ra_am3(Am3, Map),
+ I#ldrsb{dst=NewDst,am3=NewAm3}.
+
+ra_move(I=#move{dst=Dst,am1=Am1}, Map) ->
+ NewDst = ra_temp(Dst, Map),
+ NewAm1 = ra_am1(Am1, Map),
+ I#move{dst=NewDst,am1=NewAm1}.
+
+ra_pseudo_call(I=#pseudo_call{funv=FunV}, Map) ->
+ NewFunV = ra_funv(FunV, Map),
+ I#pseudo_call{funv=NewFunV}.
+
+ra_pseudo_li(I=#pseudo_li{dst=Dst}, Map) ->
+ NewDst = ra_temp(Dst, Map),
+ I#pseudo_li{dst=NewDst}.
+
+ra_pseudo_move(I=#pseudo_move{dst=Dst,src=Src}, Map) ->
+ NewDst = ra_temp(Dst, Map),
+ NewSrc = ra_temp(Src, Map),
+ I#pseudo_move{dst=NewDst,src=NewSrc}.
+
+ra_pseudo_switch(I=#pseudo_switch{jtab=JTab,index=Index}, Map) ->
+ NewJTab = ra_temp(JTab, Map),
+ NewIndex = ra_temp(Index, Map),
+ I#pseudo_switch{jtab=NewJTab,index=NewIndex}.
+
+ra_pseudo_tailcall(I=#pseudo_tailcall{funv=FunV,stkargs=StkArgs}, Map) ->
+ NewFunV = ra_funv(FunV, Map),
+ NewStkArgs = ra_args(StkArgs, Map),
+ I#pseudo_tailcall{funv=NewFunV,stkargs=NewStkArgs}.
+
+ra_smull(I=#smull{dstlo=DstLo,dsthi=DstHi,src1=Src1,src2=Src2}, Map) ->
+ NewDstLo = ra_temp(DstLo, Map),
+ NewDstHi = ra_temp(DstHi, Map),
+ NewSrc1 = ra_temp(Src1, Map),
+ NewSrc2 = ra_temp(Src2, Map),
+ I#smull{dstlo=NewDstLo,dsthi=NewDstHi,src1=NewSrc1,src2=NewSrc2}.
+
+ra_store(I=#store{src=Src,am2=Am2}, Map) ->
+ NewSrc = ra_temp(Src, Map),
+ NewAm2 = ra_am2(Am2, Map),
+ I#store{src=NewSrc,am2=NewAm2}.
+
+%%% Tailcall stack arguments.
+
+ra_args([Arg|Args], Map) ->
+ [ra_temp_or_imm(Arg, Map) | ra_args(Args, Map)];
+ra_args([], _) ->
+ [].
+
+ra_temp_or_imm(Arg, Map) ->
+ case hipe_arm:is_temp(Arg) of
+ true ->
+ ra_temp(Arg, Map);
+ false ->
+ Arg
+ end.
+
+%%% FunV, Am, and Temp operands.
+
+ra_funv(FunV, Map) ->
+ case FunV of
+ #arm_temp{} -> ra_temp(FunV, Map);
+ _ -> FunV
+ end.
+
+ra_am1(Am1, Map) ->
+ case Am1 of
+ #arm_temp{} ->
+ ra_temp(Am1, Map);
+ {Src2,rrx} ->
+ NewSrc2 = ra_temp(Src2, Map),
+ {NewSrc2,rrx};
+ {Src2,ShiftOp,ShiftArg} ->
+ NewSrc2 = ra_temp(Src2, Map),
+ NewArg =
+ case ShiftArg of
+ #arm_temp{} -> ra_temp(ShiftArg, Map);
+ _ -> ShiftArg
+ end,
+ {NewSrc2,ShiftOp,NewArg};
+ _ ->
+ Am1
+ end.
+
+ra_am2(Am2=#am2{src=Src2,offset=Offset}, Map) ->
+ NewSrc2 = ra_temp(Src2, Map),
+ NewOffset = ra_am2offset(Offset, Map),
+ Am2#am2{src=NewSrc2,offset=NewOffset}.
+
+ra_am2offset(Offset, Map) ->
+ case Offset of
+ #arm_temp{} ->
+ ra_temp(Offset, Map);
+ {Src3,rrx} ->
+ NewSrc3 = ra_temp(Src3, Map),
+ {NewSrc3,rrx};
+ {Src3,ShiftOp,Imm5} ->
+ NewSrc3 = ra_temp(Src3, Map),
+ {NewSrc3,ShiftOp,Imm5};
+ _ ->
+ Offset
+ end.
+
+ra_am3(Am3=#am3{src=Src2,offset=Offset}, Map) ->
+ NewSrc2 = ra_temp(Src2, Map),
+ NewOffset = ra_am3offset(Offset, Map),
+ Am3#am3{src=NewSrc2,offset=NewOffset}.
+
+ra_am3offset(Offset, Map) ->
+ case Offset of
+ #arm_temp{} -> ra_temp(Offset, Map);
+ _ -> Offset
+ end.
+
+-ifdef(notdef). % for FP regalloc
+ra_temp_fp(Temp, FPMap) ->
+ Reg = hipe_arm:temp_reg(Temp),
+ case hipe_arm:temp_type(Temp) of
+ 'double' ->
+ case hipe_arm_registers:is_precoloured_fpr(Reg) of
+ true -> Temp;
+ _ -> ra_temp_common(Reg, Temp, FPMap)
+ end
+ end.
+-endif.
+
+ra_temp(Temp, Map) ->
+ Reg = hipe_arm:temp_reg(Temp),
+ case hipe_arm:temp_type(Temp) of
+ 'double' ->
+ exit({?MODULE,ra_temp,Temp});
+ _ ->
+ case hipe_arm_registers:is_precoloured_gpr(Reg) of
+ true -> Temp;
+ _ -> ra_temp_common(Reg, Temp, Map)
+ end
+ end.
+
+ra_temp_common(Reg, Temp, Map) ->
+ case gb_trees:lookup(Reg, Map) of
+ {value,NewReg} -> Temp#arm_temp{reg=NewReg};
+ _ -> Temp
+ end.
+
+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_arm_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_gpr),
+ gb_trees:insert(Key, Val, Map)
+ end,
+ gb_trees:empty(),
+ TempMap).
+
+conv_ra_maplet(MapLet = {From,To}, SpillLimit, IsPrecoloured) ->
+ %% From should be a pseudo, or a hard reg mapped to itself.
+ if is_integer(From), From =< SpillLimit ->
+ case hipe_arm_registers:IsPrecoloured(From) of
+ false -> [];
+ _ ->
+ case To of
+ {reg, From} -> [];
+ _ -> exit({?MODULE,conv_ra_maplet,MapLet})
+ end
+ end;
+ true -> exit({?MODULE,conv_ra_maplet,MapLet})
+ end,
+ %% end of From check
+ case To of
+ {reg, NewReg} ->
+ %% NewReg should be a hard reg, or a pseudo mapped
+ %% to itself (formals are handled this way).
+ if is_integer(NewReg) ->
+ case hipe_arm_registers:IsPrecoloured(NewReg) of
+ true -> [];
+ _ -> if From =:= NewReg -> [];
+ true ->
+ exit({?MODULE,conv_ra_maplet,MapLet})
+ end
+ end;
+ true -> exit({?MODULE,conv_ra_maplet,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,MapLet})
+ end,
+ %% end of SpillIndex check
+ ToTempNum = SpillLimit+SpillIndex+1,
+ MaxTempNum = hipe_gensym:get_var(arm),
+ if MaxTempNum >= ToTempNum -> ok;
+ true -> hipe_gensym:set_var(arm, ToTempNum)
+ end,
+ {From, ToTempNum};
+ _ -> exit({?MODULE,conv_ra_maplet,MapLet})
+ end.
+
+-ifdef(notdef). % for FP regalloc
+mk_ra_map_fp(FPMap, SpillLimit) ->
+ lists:foldl(fun(MapLet, Map) ->
+ {Key,Val} = conv_ra_maplet(MapLet, SpillLimit,
+ is_precoloured_fpr),
+ gb_trees:insert(Key, Val, Map)
+ end,
+ gb_trees:empty(),
+ FPMap).
+-endif.
diff --git a/lib/hipe/arm/hipe_arm_ra_ls.erl b/lib/hipe/arm/hipe_arm_ra_ls.erl
new file mode 100644
index 0000000000..53bfd5b2a3
--- /dev/null
+++ b/lib/hipe/arm/hipe_arm_ra_ls.erl
@@ -0,0 +1,56 @@
+%%% -*- erlang-indent-level: 2 -*-
+%%%
+%%% %CopyrightBegin%
+%%%
+%%% Copyright Ericsson AB 2006-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%
+%%%
+%%% Linear Scan register allocator for ARM
+
+-module(hipe_arm_ra_ls).
+-export([ra/3]).
+
+ra(Defun, SpillIndex, Options) ->
+ NewDefun = Defun, %% hipe_${ARCH}_ra_rename:rename(Defun,Options),
+ CFG = hipe_arm_cfg:init(NewDefun),
+ SpillLimit = hipe_arm_specific:number_of_temporaries(CFG),
+ alloc(NewDefun, SpillIndex, SpillLimit, Options).
+
+alloc(Defun, SpillIndex, SpillLimit, Options) ->
+ CFG = hipe_arm_cfg:init(Defun),
+ {Coloring, _NewSpillIndex} =
+ regalloc(
+ CFG,
+ hipe_arm_registers:allocatable_gpr()--
+ [hipe_arm_registers:temp3(),
+ hipe_arm_registers:temp2(),
+ hipe_arm_registers:temp1()],
+ [hipe_arm_cfg:start_label(CFG)],
+ SpillIndex, SpillLimit, Options,
+ hipe_arm_specific),
+ {NewDefun, _DidSpill} =
+ hipe_arm_ra_postconditions:check_and_rewrite(
+ Defun, Coloring, 'linearscan'),
+ TempMap = hipe_temp_map:cols2tuple(Coloring, hipe_arm_specific),
+ {SpillMap, _NewSpillIndex2} =
+ hipe_spillmin:stackalloc(CFG, [], SpillIndex, Options,
+ hipe_arm_specific, TempMap),
+ Coloring2 =
+ hipe_spillmin:mapmerge(hipe_temp_map:to_substlist(TempMap), SpillMap),
+ {NewDefun, Coloring2}.
+
+regalloc(CFG, PhysRegs, Entrypoints, SpillIndex, DontSpill, Options, Target) ->
+ hipe_ls_regalloc:regalloc(
+ CFG, PhysRegs, Entrypoints, SpillIndex, DontSpill, Options, Target).
diff --git a/lib/hipe/arm/hipe_arm_ra_naive.erl b/lib/hipe/arm/hipe_arm_ra_naive.erl
new file mode 100644
index 0000000000..786895f2ca
--- /dev/null
+++ b/lib/hipe/arm/hipe_arm_ra_naive.erl
@@ -0,0 +1,29 @@
+%% -*- 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_ra_naive).
+-export([ra/3]).
+
+-include("hipe_arm.hrl").
+
+ra(Defun, _Coloring_fp, _Options) -> % -> {Defun, Coloring}
+ {NewDefun,_DidSpill} =
+ hipe_arm_ra_postconditions:check_and_rewrite2(Defun, [], 'naive'),
+ {NewDefun, []}.
diff --git a/lib/hipe/arm/hipe_arm_ra_postconditions.erl b/lib/hipe/arm/hipe_arm_ra_postconditions.erl
new file mode 100644
index 0000000000..96b0d5733f
--- /dev/null
+++ b/lib/hipe/arm/hipe_arm_ra_postconditions.erl
@@ -0,0 +1,278 @@
+%% -*- 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_ra_postconditions).
+
+-export([check_and_rewrite/3, check_and_rewrite2/3]).
+
+-include("hipe_arm.hrl").
+
+check_and_rewrite(Defun, Coloring, Allocator) ->
+ TempMap = hipe_temp_map:cols2tuple(Coloring, hipe_arm_specific),
+ check_and_rewrite2(Defun, TempMap, Allocator).
+
+check_and_rewrite2(Defun, TempMap, Allocator) ->
+ Strategy = strategy(Allocator),
+ #defun{code=Code0} = Defun,
+ {Code1,DidSpill} = do_insns(Code0, TempMap, Strategy, [], false),
+ VarRange = {0, hipe_gensym:get_var(arm)},
+ {Defun#defun{code=Code1, var_range=VarRange},
+ DidSpill}.
+
+strategy(Allocator) ->
+ case Allocator of
+ 'normal' -> 'new';
+ 'linearscan' -> 'fixed';
+ 'naive' -> 'fixed'
+ end.
+
+do_insns([I|Insns], TempMap, Strategy, Accum, DidSpill0) ->
+ {NewIs, DidSpill1} = do_insn(I, TempMap, Strategy),
+ do_insns(Insns, TempMap, Strategy, lists:reverse(NewIs, Accum), DidSpill0 or DidSpill1);
+do_insns([], _TempMap, _Strategy, Accum, DidSpill) ->
+ {lists:reverse(Accum), DidSpill}.
+
+do_insn(I, TempMap, Strategy) ->
+ case I of
+ #alu{} -> do_alu(I, TempMap, Strategy);
+ #cmp{} -> do_cmp(I, TempMap, Strategy);
+ #load{} -> do_load(I, TempMap, Strategy);
+ #ldrsb{} -> do_ldrsb(I, TempMap, Strategy);
+ #move{} -> do_move(I, TempMap, Strategy);
+ #pseudo_call{} -> do_pseudo_call(I, TempMap, Strategy);
+ #pseudo_li{} -> do_pseudo_li(I, TempMap, Strategy);
+ #pseudo_move{} -> do_pseudo_move(I, TempMap, Strategy);
+ #pseudo_switch{} -> do_pseudo_switch(I, TempMap, Strategy);
+ #pseudo_tailcall{} -> do_pseudo_tailcall(I, TempMap, Strategy);
+ #smull{} -> do_smull(I, TempMap, Strategy);
+ #store{} -> do_store(I, TempMap, Strategy);
+ _ -> {[I], false}
+ end.
+
+%%% Fix relevant instruction types.
+
+do_alu(I=#alu{dst=Dst,src=Src,am1=Am1}, TempMap, Strategy) ->
+ {FixDst,NewDst,DidSpill1} = fix_dst(Dst, TempMap, Strategy),
+ {FixSrc,NewSrc,DidSpill2} = fix_src1(Src, TempMap, Strategy),
+ {FixAm1,NewAm1,DidSpill3} = fix_am1(Am1, TempMap, Strategy),
+ NewI = I#alu{dst=NewDst,src=NewSrc,am1=NewAm1},
+ {FixSrc ++ FixAm1 ++ [NewI | FixDst], DidSpill1 or DidSpill2 or DidSpill3}.
+
+do_cmp(I=#cmp{src=Src,am1=Am1}, TempMap, Strategy) ->
+ {FixSrc,NewSrc,DidSpill1} = fix_src1(Src, TempMap, Strategy),
+ {FixAm1,NewAm1,DidSpill2} = fix_am1(Am1, TempMap, Strategy),
+ NewI = I#cmp{src=NewSrc,am1=NewAm1},
+ {FixSrc ++ FixAm1 ++ [NewI], DidSpill1 or DidSpill2}.
+
+do_load(I=#load{dst=Dst,am2=Am2}, TempMap, Strategy) ->
+ {FixDst,NewDst,DidSpill1} = fix_dst(Dst, TempMap, Strategy),
+ {FixAm2,NewAm2,DidSpill2} = fix_am2(Am2, TempMap, Strategy),
+ NewI = I#load{dst=NewDst,am2=NewAm2},
+ {FixAm2 ++ [NewI | FixDst], DidSpill1 or DidSpill2}.
+
+do_ldrsb(I=#ldrsb{dst=Dst,am3=Am3}, TempMap, Strategy) ->
+ {FixDst,NewDst,DidSpill1} = fix_dst(Dst, TempMap, Strategy),
+ {FixAm3,NewAm3,DidSpill2} = fix_am3(Am3, TempMap, Strategy),
+ NewI = I#ldrsb{dst=NewDst,am3=NewAm3},
+ {FixAm3 ++ [NewI | FixDst], DidSpill1 or DidSpill2}.
+
+do_move(I=#move{dst=Dst,am1=Am1}, TempMap, Strategy) ->
+ {FixDst,NewDst,DidSpill1} = fix_dst(Dst, TempMap, Strategy),
+ {FixAm1,NewAm1,DidSpill2} = fix_am1(Am1, TempMap, Strategy),
+ NewI = I#move{dst=NewDst,am1=NewAm1},
+ {FixAm1 ++ [NewI | FixDst], DidSpill1 or DidSpill2}.
+
+do_pseudo_call(I=#pseudo_call{funv=FunV}, TempMap, Strategy) ->
+ {FixFunV,NewFunV,DidSpill} = fix_funv(FunV, TempMap, Strategy),
+ NewI = I#pseudo_call{funv=NewFunV},
+ {FixFunV ++ [NewI], DidSpill}.
+
+do_pseudo_li(I=#pseudo_li{dst=Dst}, TempMap, Strategy) ->
+ {FixDst,NewDst,DidSpill} = fix_dst(Dst, TempMap, Strategy),
+ NewI = I#pseudo_li{dst=NewDst},
+ {[NewI | FixDst], DidSpill}.
+
+do_pseudo_move(I=#pseudo_move{dst=Dst,src=Src}, TempMap, Strategy) ->
+ %% Either Dst or Src (but not both) may be a pseudo temp.
+ %% pseudo_move and pseudo_tailcall are special cases: in
+ %% all other instructions, all temps must be non-pseudos
+ %% after register allocation.
+ case temp_is_spilled(Dst, TempMap) of
+ true -> % Src must not be a pseudo
+ {FixSrc,NewSrc,DidSpill} = fix_src1(Src, TempMap, Strategy),
+ NewI = I#pseudo_move{src=NewSrc},
+ {FixSrc ++ [NewI], DidSpill};
+ _ ->
+ {[I], false}
+ end.
+
+do_pseudo_switch(I=#pseudo_switch{jtab=JTab,index=Index}, TempMap, Strategy) ->
+ {FixJTab,NewJTab,DidSpill1} = fix_src1(JTab, TempMap, Strategy),
+ {FixIndex,NewIndex,DidSpill2} = fix_src2(Index, TempMap, Strategy),
+ NewI = I#pseudo_switch{jtab=NewJTab,index=NewIndex},
+ {FixJTab ++ FixIndex ++ [NewI], DidSpill1 or DidSpill2}.
+
+do_pseudo_tailcall(I=#pseudo_tailcall{funv=FunV}, TempMap, Strategy) ->
+ {FixFunV,NewFunV,DidSpill} = fix_funv(FunV, TempMap, Strategy),
+ NewI = I#pseudo_tailcall{funv=NewFunV},
+ {FixFunV ++ [NewI], DidSpill}.
+
+do_smull(I=#smull{dstlo=DstLo,dsthi=DstHi,src1=Src1,src2=Src2}, TempMap, Strategy) ->
+ %% ARM requires that DstLo, DstHi, and Src1 are distinct.
+ %% We furthermore require Src1 and Src2 to be different in the fixed strategy.
+ {FixDstLo,NewDstLo,DidSpill1} = fix_dst(DstLo, TempMap, Strategy), % temp1
+ {FixDstHi,NewDstHi,DidSpill2} = fix_dst2(DstHi, TempMap, Strategy), % temp3
+ {FixSrc1,NewSrc1,DidSpill3} = fix_src2(Src1, TempMap, Strategy), % temp2
+ {FixSrc2,NewSrc2,DidSpill4} = fix_src1(Src2, TempMap, Strategy), % temp1; temp3 would be OK
+ NewI = I#smull{dstlo=NewDstLo,dsthi=NewDstHi,src1=NewSrc1,src2=NewSrc2},
+ {FixSrc1 ++ FixSrc2 ++ [NewI | FixDstLo ++ FixDstHi],
+ DidSpill1 or DidSpill2 or DidSpill3 or DidSpill4}.
+
+do_store(I=#store{src=Src,am2=Am2}, TempMap, Strategy) ->
+ {FixSrc,NewSrc,DidSpill1} = fix_src1(Src, TempMap, Strategy),
+ {FixAm2,NewAm2,DidSpill2} = fix_am2(Am2, TempMap, Strategy),
+ NewI = I#store{src=NewSrc,am2=NewAm2},
+ {FixSrc ++ FixAm2 ++ [NewI], DidSpill1 or DidSpill2}.
+
+%%% Fix Dst and Src operands.
+
+fix_funv(FunV, TempMap, Strategy) ->
+ case FunV of
+ #arm_temp{} -> fix_src3(FunV, TempMap, Strategy);
+ _ -> {[], FunV, false}
+ end.
+
+fix_am1(Am1, TempMap, Strategy) ->
+ case Am1 of
+ #arm_temp{} ->
+ fix_src2(Am1, TempMap, Strategy);
+ {Src2,rrx} ->
+ {Fix,New,DidSpill} = fix_src2(Src2, TempMap, Strategy),
+ {Fix, {New,rrx}, DidSpill};
+ {Src2,ShiftOp,ShiftArg} ->
+ {FixSrc2,NewSrc2,DidSpill1} = fix_src2(Src2, TempMap, Strategy),
+ {FixArg,NewArg,DidSpill2} =
+ case ShiftArg of
+ #arm_temp{} -> fix_src3(ShiftArg, TempMap, Strategy);
+ _ -> {[], ShiftArg, false}
+ end,
+ %% order matters: FixArg may clobber temp2/Src2
+ {FixArg ++ FixSrc2, {NewSrc2,ShiftOp,NewArg}, DidSpill1 or DidSpill2};
+ _ -> {[], Am1, false}
+ end.
+
+fix_am2(Am2=#am2{src=Src2,offset=Offset}, TempMap, Strategy) ->
+ {FixSrc2,NewSrc2,DidSpill1} = fix_src2(Src2, TempMap, Strategy),
+ {FixOffset,NewOffset,DidSpill2} = fix_am2offset(Offset, TempMap, Strategy),
+ NewAm2 = Am2#am2{src=NewSrc2,offset=NewOffset},
+ %% order matters: FixOffset may clobber temp2/Src2
+ {FixOffset ++ FixSrc2, NewAm2, DidSpill1 or DidSpill2}.
+
+fix_am2offset(Offset, TempMap, Strategy) ->
+ case Offset of
+ #arm_temp{} ->
+ fix_src3(Offset, TempMap, Strategy);
+ {Src3,rrx} ->
+ {Fix,New,DidSpill} = fix_src3(Src3, TempMap, Strategy),
+ {Fix, {New,rrx}, DidSpill};
+ {Src3,ShiftOp,Imm5} ->
+ {Fix,New,DidSpill} = fix_src3(Src3, TempMap, Strategy),
+ {Fix, {New,ShiftOp,Imm5}, DidSpill};
+ _ ->
+ {[], Offset, false}
+ end.
+
+fix_am3(Am3=#am3{src=Src2,offset=Offset}, TempMap, Strategy) ->
+ {FixSrc2,NewSrc2,DidSpill1} = fix_src2(Src2, TempMap, Strategy),
+ {FixOffset,NewOffset,DidSpill2} = fix_am3offset(Offset, TempMap, Strategy),
+ NewAm3 = Am3#am3{src=NewSrc2,offset=NewOffset},
+ %% order matters: FixOffset may clobber temp2/Src2
+ {FixOffset ++ FixSrc2, NewAm3, DidSpill1 or DidSpill2}.
+
+fix_am3offset(Offset, TempMap, Strategy) ->
+ case Offset of
+ #arm_temp{} -> fix_src3(Offset, TempMap, Strategy);
+ _ -> {[], Offset, false}
+ end.
+
+fix_src1(Src, TempMap, Strategy) ->
+ fix_src(Src, TempMap, temp1(Strategy)).
+
+temp1('new') -> [];
+temp1('fixed') -> hipe_arm_registers:temp1().
+
+fix_src2(Src, TempMap, Strategy) ->
+ fix_src(Src, TempMap, temp2(Strategy)).
+
+temp2('new') -> [];
+temp2('fixed') -> hipe_arm_registers:temp2().
+
+fix_src3(Src, TempMap, Strategy) ->
+ fix_src(Src, TempMap, temp3(Strategy)).
+
+temp3('new') -> [];
+temp3('fixed') -> hipe_arm_registers:temp3().
+
+fix_src(Src, TempMap, RegOpt) ->
+ case temp_is_spilled(Src, TempMap) of
+ true ->
+ NewSrc = clone(Src, RegOpt),
+ {[hipe_arm:mk_pseudo_move(NewSrc, Src)],
+ NewSrc,
+ true};
+ _ ->
+ {[], Src, false}
+ end.
+
+fix_dst(Dst, TempMap, Strategy) ->
+ fix_dst_common(Dst, TempMap, temp1(Strategy)).
+
+fix_dst2(Dst, TempMap, Strategy) -> % only used for smull's DstHi
+ fix_dst_common(Dst, TempMap, temp3(Strategy)).
+
+fix_dst_common(Dst, TempMap, RegOpt) ->
+ case temp_is_spilled(Dst, TempMap) of
+ true ->
+ NewDst = clone(Dst, RegOpt),
+ {[hipe_arm:mk_pseudo_move(Dst, NewDst)], NewDst, true};
+ _ ->
+ {[], Dst, false}
+ end.
+
+%%% Check if an operand is a pseudo-temp.
+
+temp_is_spilled(Temp, []) -> % special case for naive regalloc
+ not(hipe_arm:temp_is_precoloured(Temp));
+temp_is_spilled(Temp, TempMap) ->
+ case hipe_arm:temp_is_allocatable(Temp) of
+ true ->
+ Reg = hipe_arm:temp_reg(Temp),
+ tuple_size(TempMap) > Reg andalso hipe_temp_map:is_spilled(Reg, TempMap);
+ false -> true
+ end.
+
+%%% Make a certain reg into a clone of Temp.
+
+clone(Temp, RegOpt) ->
+ Type = hipe_arm:temp_type(Temp),
+ case RegOpt of
+ [] -> hipe_arm:mk_new_temp(Type);
+ Reg -> hipe_arm:mk_temp(Reg, Type)
+ end.
diff --git a/lib/hipe/arm/hipe_arm_registers.erl b/lib/hipe/arm/hipe_arm_registers.erl
new file mode 100644
index 0000000000..ff6a163e9c
--- /dev/null
+++ b/lib/hipe/arm/hipe_arm_registers.erl
@@ -0,0 +1,207 @@
+%% -*- 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_registers).
+
+-export([reg_name_gpr/1,
+ first_virtual/0,
+ is_precoloured_gpr/1,
+ all_precoloured/0, % for coalescing
+ return_value/0,
+ temp1/0, % C callee-save, not parameter, may be allocatable
+ temp2/0, % not parameter, must not be allocatable (frame)
+ temp3/0, % not parameter, may be allocatable
+ heap_pointer/0,
+ stack_pointer/0,
+ proc_pointer/0,
+ lr/0,
+ pc/0,
+ %% heap_limit/0,
+ %% fcalls/0,
+ allocatable_gpr/0, % for coalescing
+ is_fixed/1, % for graph coloring
+ nr_args/0,
+ arg/1,
+ args/1,
+ is_arg/1, % for linear scan
+ call_clobbered/0,
+ tailcall_clobbered/0,
+ live_at_return/0
+ ]).
+
+-include("../rtl/hipe_literals.hrl").
+
+-define(R0, 0).
+-define(R1, 1).
+-define(R2, 2).
+-define(R3, 3).
+-define(R4, 4).
+-define(R5, 5).
+-define(R6, 6).
+-define(R7, 7).
+-define(R8, 8).
+-define(R9, 9).
+-define(R10, 10).
+-define(R11, 11).
+-define(R12, 12).
+-define(R13, 13). % XXX: see all_precoloured()
+-define(R14, 14).
+-define(R15, 15).
+-define(LAST_PRECOLOURED, 15). % must handle both GPR and FPR ranges
+
+-define(ARG0, ?R1).
+-define(ARG1, ?R2).
+-define(ARG2, ?R3).
+-define(ARG3, ?R4).
+-define(ARG4, ?R5).
+-define(ARG5, ?R6).
+
+-define(TEMP1, ?R8). % stores LR around inc_stack calls, must be C calleE-save
+-define(TEMP2, ?R12).
+-define(TEMP3, ?R7).
+
+-define(RETURN_VALUE, ?R0).
+-define(HEAP_POINTER, ?R9).
+-define(STACK_POINTER, ?R10).
+-define(PROC_POINTER, ?R11).
+
+reg_name_gpr(R) -> [$r | integer_to_list(R)].
+
+%%% Must handle both GPR and FPR ranges.
+first_virtual() -> ?LAST_PRECOLOURED + 1.
+
+%%% These two tests have the same implementation, but that's
+%%% not something we should cast in stone in the interface.
+is_precoloured_gpr(R) -> R =< ?LAST_PRECOLOURED.
+
+all_precoloured() ->
+ %% XXX: R13 should be skipped as it never is used anywhere.
+ %% Unfortunately, gaps in the list of precoloured registers
+ %% cause the graph_color register allocator to create bogus
+ %% assignments for those "registers", which in turn causes
+ %% the "precoloured reg must map to itself" sanity check in
+ %% the frame module to signal errors.
+ [ ?R0, ?R1, ?R2, ?R3, ?R4, ?R5, ?R6, ?R7,
+ ?R8, ?R9, ?R10, ?R11, ?R12, ?R13, ?R14, ?R15].
+
+return_value() -> ?RETURN_VALUE.
+
+temp1() -> ?TEMP1.
+temp2() -> ?TEMP2.
+temp3() -> ?TEMP3. % for base2 in storeix :-(
+
+heap_pointer() -> ?HEAP_POINTER.
+
+stack_pointer() -> ?STACK_POINTER.
+
+proc_pointer() -> ?PROC_POINTER.
+
+lr() -> ?R14.
+
+pc() -> ?R15.
+
+allocatable_gpr() ->
+ %% r9, r10, and r11 are fixed global registers.
+ %% r12 may be used by the frame module for large load/store offsets.
+ %% r13 is reserved for C.
+ %% r15 is the PC, and is not usable as a variable.
+ [ ?R0, ?R1, ?R2, ?R3, ?R4, ?R5, ?R6, ?R7,
+ ?R8, ?R14].
+
+%% Needed for hipe_graph_coloring_regalloc.
+%% Presumably true for Reg in AllPrecoloured \ Allocatable.
+is_fixed(Reg) ->
+ case Reg of
+ ?HEAP_POINTER -> true;
+ ?STACK_POINTER -> true;
+ ?PROC_POINTER -> true;
+ %% The following cases are required for linear scan:
+ %% it gets confused if it sees a register which is
+ %% neither allocatable nor global (fixed or one of
+ %% the scratch registers set aside for linear scan).
+ ?R15 -> true;
+ ?R13 -> true; % XXX: see all_precoloured()
+ ?R12 -> true;
+ _ -> false
+ end.
+
+nr_args() -> ?ARM_NR_ARG_REGS.
+
+args(Arity) when is_integer(Arity) ->
+ N = erlang:min(Arity, ?ARM_NR_ARG_REGS),
+ args(N-1, []).
+
+args(-1, Rest) -> Rest;
+args(I, Rest) -> args(I-1, [arg(I) | Rest]).
+
+arg(N) ->
+ if N < ?ARM_NR_ARG_REGS ->
+ case N of
+ 0 -> ?ARG0;
+ 1 -> ?ARG1;
+ 2 -> ?ARG2;
+ 3 -> ?ARG3;
+ 4 -> ?ARG4;
+ 5 -> ?ARG5;
+ _ -> exit({?MODULE, arg, N})
+ end;
+ true ->
+ exit({?MODULE, arg, N})
+ end.
+
+is_arg(R) ->
+ case R of
+ ?ARG0 -> ?ARM_NR_ARG_REGS > 0;
+ ?ARG1 -> ?ARM_NR_ARG_REGS > 1;
+ ?ARG2 -> ?ARM_NR_ARG_REGS > 2;
+ ?ARG3 -> ?ARM_NR_ARG_REGS > 3;
+ ?ARG4 -> ?ARM_NR_ARG_REGS > 4;
+ ?ARG5 -> ?ARM_NR_ARG_REGS > 5;
+ _ -> false
+ end.
+
+call_clobbered() -> % does the RA strip the type or not?
+ [{?R0,tagged},{?R0,untagged},
+ {?R1,tagged},{?R1,untagged},
+ {?R2,tagged},{?R2,untagged},
+ {?R3,tagged},{?R3,untagged},
+ {?R4,tagged},{?R4,untagged},
+ {?R5,tagged},{?R5,untagged},
+ {?R6,tagged},{?R6,untagged},
+ {?R7,tagged},{?R7,untagged},
+ {?R8,tagged},{?R8,untagged},
+ %% R9 is fixed (HP)
+ %% R10 is fixed (NSP)
+ %% R11 is fixed (P)
+ {?R12,tagged},{?R12,untagged},
+ %% R13 is reserved for C
+ {?R14,tagged},{?R14,untagged}
+ %% R15 is the non-allocatable PC
+ ].
+
+tailcall_clobbered() -> % tailcall crapola needs one temp
+ [{?TEMP1,tagged},{?TEMP1,untagged}].
+
+live_at_return() ->
+ [%%{?LR,untagged},
+ {?HEAP_POINTER,untagged},
+ {?STACK_POINTER,untagged},
+ {?PROC_POINTER,untagged}
+ ].
diff --git a/lib/hipe/arm/hipe_rtl_to_arm.erl b/lib/hipe/arm/hipe_rtl_to_arm.erl
new file mode 100644
index 0000000000..a4dc5db978
--- /dev/null
+++ b/lib/hipe/arm/hipe_rtl_to_arm.erl
@@ -0,0 +1,836 @@
+%% -*- 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_rtl_to_arm).
+-export([translate/1]).
+
+-include("../rtl/hipe_rtl.hrl").
+
+translate(RTL) ->
+ hipe_gensym:init(arm),
+ hipe_gensym:set_var(arm, hipe_arm_registers:first_virtual()),
+ hipe_gensym:set_label(arm, 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_arm:mk_label(hipe_gensym:get_next_label(arm)) |
+ move_formals(RegFormals, Code0)]
+ end,
+ IsClosure = hipe_rtl:rtl_is_closure(RTL),
+ IsLeaf = hipe_rtl:rtl_is_leaf(RTL),
+ hipe_arm: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);
+ #branch{} -> conv_branch(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);
+ _ -> exit({?MODULE,conv_insn,I})
+ end.
+
+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),
+ S = false,
+ I2 = mk_alu(S, Dst, Src1, RtlAluOp, Src2),
+ {I2, Map2, Data}.
+
+conv_shift(RtlShiftOp) ->
+ case RtlShiftOp of
+ 'sll' -> 'lsl';
+ 'srl' -> 'lsr';
+ 'sra' -> 'asr'
+ end.
+
+conv_arith(RtlAluOp) -> % RtlAluOp \ RtlShiftOp -> ArmArithOp
+ case RtlAluOp of
+ 'add' -> 'add';
+ 'sub' -> 'sub';
+ 'mul' -> 'mul';
+ 'or' -> 'orr';
+ 'and' -> 'and';
+ 'xor' -> 'eor'
+ end.
+
+commute_arithop(ArithOp) ->
+ case ArithOp of
+ 'sub' -> 'rsb';
+ _ -> ArithOp
+ end.
+
+mk_alu(S, Dst, Src1, RtlAluOp, Src2) ->
+ case hipe_rtl:is_shift_op(RtlAluOp) of
+ true ->
+ mk_shift(S, Dst, Src1, conv_shift(RtlAluOp), Src2);
+ false ->
+ mk_arith(S, Dst, Src1, conv_arith(RtlAluOp), Src2)
+ end.
+
+mk_shift(S, Dst, Src1, ShiftOp, Src2) ->
+ case hipe_arm:is_temp(Src1) of
+ true ->
+ case hipe_arm:is_temp(Src2) of
+ true ->
+ mk_shift_rr(S, Dst, Src1, ShiftOp, Src2);
+ _ ->
+ mk_shift_ri(S, Dst, Src1, ShiftOp, Src2)
+ end;
+ _ ->
+ case hipe_arm:is_temp(Src2) of
+ true ->
+ mk_shift_ir(S, Dst, Src1, ShiftOp, Src2);
+ _ ->
+ mk_shift_ii(S, Dst, Src1, ShiftOp, Src2)
+ end
+ end.
+
+mk_shift_ii(S, Dst, Src1, ShiftOp, Src2) ->
+ io:format("~w: RTL alu with two immediates\n", [?MODULE]),
+ Tmp = new_untagged_temp(),
+ mk_li(Tmp, Src1,
+ mk_shift_ri(S, Dst, Tmp, ShiftOp, Src2)).
+
+mk_shift_ir(S, Dst, Src1, ShiftOp, Src2) ->
+ Tmp = new_untagged_temp(),
+ mk_li(Tmp, Src1,
+ mk_shift_rr(S, Dst, Tmp, ShiftOp, Src2)).
+
+mk_shift_ri(S, Dst, Src1, ShiftOp, Src2) when is_integer(Src2) ->
+ if Src2 >= 0, Src2 < 32 -> ok;
+ true -> io:format("~w: excessive immediate shift ~w\n", [?MODULE,Src2])
+ end,
+ Am1 = {Src1,ShiftOp,Src2},
+ [hipe_arm:mk_move(S, Dst, Am1)].
+
+mk_shift_rr(S, Dst, Src1, ShiftOp, Src2) ->
+ Am1 = {Src1,ShiftOp,Src2},
+ [hipe_arm:mk_move(S, Dst, Am1)].
+
+mk_arith(S, Dst, Src1, ArithOp, Src2) ->
+ case hipe_arm:is_temp(Src1) of
+ true ->
+ case hipe_arm:is_temp(Src2) of
+ true ->
+ mk_arith_rr(S, Dst, Src1, ArithOp, Src2);
+ _ ->
+ mk_arith_ri(S, Dst, Src1, ArithOp, Src2)
+ end;
+ _ ->
+ case hipe_arm:is_temp(Src2) of
+ true ->
+ mk_arith_ir(S, Dst, Src1, ArithOp, Src2);
+ _ ->
+ mk_arith_ii(S, Dst, Src1, ArithOp, Src2)
+ end
+ end.
+
+mk_arith_ii(S, Dst, Src1, ArithOp, Src2) ->
+ io:format("~w: RTL alu with two immediates\n", [?MODULE]),
+ Tmp = new_untagged_temp(),
+ mk_li(Tmp, Src1,
+ mk_arith_ri(S, Dst, Tmp, ArithOp, Src2)).
+
+mk_arith_ir(S, Dst, Src1, ArithOp, Src2) ->
+ mk_arith_ri(S, Dst, Src2, commute_arithop(ArithOp), Src1).
+
+mk_arith_ri(S, Dst, Src1, ArithOp, Src2) ->
+ case ArithOp of
+ 'mul' -> % mul/smull only take reg/reg operands
+ Tmp = new_untagged_temp(),
+ mk_li(Tmp, Src2,
+ mk_arith_rr(S, Dst, Src1, ArithOp, Tmp));
+ _ -> % add/sub/orr/and/eor have reg/am1 operands
+ {FixAm1,NewArithOp,Am1} = fix_aluop_imm(ArithOp, Src2),
+ FixAm1 ++ [hipe_arm:mk_alu(NewArithOp, S, Dst, Src1, Am1)]
+ end.
+
+mk_arith_rr(S, Dst, Src1, ArithOp, Src2) ->
+ case {ArithOp,S} of
+ {'mul',true} ->
+ %% To check for overflow in 32x32->32 multiplication:
+ %% smull Dst,TmpHi,Src1,Src2
+ %% mov TmpSign,Dst,ASR #31
+ %% cmp TmpSign,TmpHi
+ %% [bne OverflowLabel]
+ TmpHi = new_untagged_temp(),
+ TmpSign = new_untagged_temp(),
+ [hipe_arm:mk_smull(Dst, TmpHi, Src1, Src2),
+ hipe_arm:mk_move(TmpSign, {Dst,'asr',31}),
+ hipe_arm:mk_cmp('cmp', TmpSign, TmpHi)];
+ _ ->
+ [hipe_arm:mk_alu(ArithOp, S, Dst, Src1, Src2)]
+ end.
+
+fix_aluop_imm(AluOp, Imm) -> % {FixAm1,NewAluOp,Am1}
+ case hipe_arm:try_aluop_imm(AluOp, Imm) of
+ {NewAluOp,Am1} -> {[], NewAluOp, Am1};
+ [] ->
+ Tmp = new_untagged_temp(),
+ {mk_li(Tmp, Imm), AluOp, Tmp}
+ end.
+
+conv_alub(I, Map, Data) ->
+ %% dst = src1 aluop src2; if COND goto label
+ {Dst, Map0} = conv_dst(hipe_rtl:alub_dst(I), Map),
+ {Src1, Map1} = conv_src(hipe_rtl:alub_src1(I), Map0),
+ {Src2, Map2} = conv_src(hipe_rtl:alub_src2(I), Map1),
+ RtlAluOp = hipe_rtl:alub_op(I),
+ Cond0 = conv_alub_cond(RtlAluOp, hipe_rtl:alub_cond(I)),
+ Cond =
+ case {RtlAluOp,Cond0} of
+ {'mul','vs'} -> 'ne'; % overflow becomes not-equal
+ {'mul','vc'} -> 'eq'; % no-overflow becomes equal
+ {'mul',_} -> exit({?MODULE,I});
+ {_,_} -> Cond0
+ end,
+ I2 = mk_pseudo_bc(
+ Cond,
+ hipe_rtl:alub_true_label(I),
+ hipe_rtl:alub_false_label(I),
+ hipe_rtl:alub_pred(I)),
+ S = true,
+ I1 = mk_alu(S, Dst, Src1, RtlAluOp, Src2),
+ {I1 ++ I2, Map2, Data}.
+
+conv_branch(I, Map, Data) ->
+ %% <unused> = src1 - src2; if COND goto label
+ {Src1, Map0} = conv_src(hipe_rtl:branch_src1(I), Map),
+ {Src2, Map1} = conv_src(hipe_rtl:branch_src2(I), Map0),
+ Cond = conv_branch_cond(hipe_rtl:branch_cond(I)),
+ I2 = mk_branch(Src1, Cond, Src2,
+ hipe_rtl:branch_true_label(I),
+ hipe_rtl:branch_false_label(I),
+ hipe_rtl:branch_pred(I)),
+ {I2, Map1, Data}.
+
+mk_branch(Src1, Cond, Src2, TrueLab, FalseLab, Pred) ->
+ case hipe_arm:is_temp(Src1) of
+ true ->
+ case hipe_arm:is_temp(Src2) of
+ true ->
+ mk_branch_rr(Src1, Src2, Cond, TrueLab, FalseLab, Pred);
+ _ ->
+ mk_branch_ri(Src1, Cond, Src2, TrueLab, FalseLab, Pred)
+ end;
+ _ ->
+ case hipe_arm:is_temp(Src2) of
+ true ->
+ NewCond = commute_cond(Cond),
+ mk_branch_ri(Src2, NewCond, Src1, TrueLab, FalseLab, Pred);
+ _ ->
+ mk_branch_ii(Src1, Cond, Src2, TrueLab, FalseLab, Pred)
+ end
+ end.
+
+mk_branch_ii(Imm1, Cond, Imm2, TrueLab, FalseLab, Pred) ->
+ io:format("~w: RTL branch with two immediates\n", [?MODULE]),
+ Tmp = new_untagged_temp(),
+ mk_li(Tmp, Imm1,
+ mk_branch_ri(Tmp, Cond, Imm2,
+ TrueLab, FalseLab, Pred)).
+
+mk_branch_ri(Src, Cond, Imm, TrueLab, FalseLab, Pred) ->
+ {FixAm1,NewCmpOp,Am1} = fix_aluop_imm('cmp', Imm),
+ FixAm1 ++ mk_cmp_bc(NewCmpOp, Src, Am1, Cond, TrueLab, FalseLab, Pred).
+
+mk_branch_rr(Src1, Src2, Cond, TrueLab, FalseLab, Pred) ->
+ mk_cmp_bc('cmp', Src1, Src2, Cond, TrueLab, FalseLab, Pred).
+
+mk_cmp_bc(CmpOp, Src, Am1, Cond, TrueLab, FalseLab, Pred) ->
+ [hipe_arm:mk_cmp(CmpOp, Src, Am1) |
+ mk_pseudo_bc(Cond, 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_arm: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_arm:prim_prim(Prim) of
+ %% no ARM-specific primops defined yet
+ _ ->
+ mk_general_call(Dsts, Prim, Args, ContLab, ExnLab, Linkage)
+ end.
+
+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(arm),
+ {NewContLab, [hipe_arm: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(arm),
+ case ContLab of
+ [] ->
+ %% This is just a fallthrough
+ %% No jump back after the moves.
+ {NewContLab,
+ [hipe_arm:mk_label(NewContLab) |
+ Moves]};
+ _ ->
+ %% The call has a continuation. Jump to it.
+ {NewContLab,
+ [hipe_arm:mk_label(NewContLab) |
+ Moves ++
+ [hipe_arm:mk_b_label(ContLab)]]}
+ end
+ end,
+ SDesc = hipe_arm:mk_sdesc(ExnLab, 0, length(Args), {}),
+ CallInsn = hipe_arm:mk_pseudo_call(Fun, SDesc, RealContLab, Linkage),
+ {RegArgs,StkArgs} = split_args(Args),
+ mk_push_args(StkArgs, move_actuals(RegArgs, [CallInsn | Tail])).
+
+mk_call_results([]) ->
+ [];
+mk_call_results([Dst]) ->
+ RV = hipe_arm:mk_temp(hipe_arm_registers:return_value(), 'tagged'),
+ [hipe_arm:mk_pseudo_move(Dst, RV)];
+mk_call_results(Dsts) ->
+ exit({?MODULE,mk_call_results,Dsts}).
+
+mk_push_args(StkArgs, Tail) ->
+ case length(StkArgs) of
+ 0 ->
+ Tail;
+ NrStkArgs ->
+ [hipe_arm: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_arm:is_temp(Arg) of
+ true ->
+ {Arg, []};
+ _ ->
+ Tmp = new_tagged_temp(),
+ {Tmp, mk_li(Tmp, Arg)}
+ end,
+ NewTail = hipe_arm:mk_store('str', Src, mk_sp(), Offset, 'new', Tail),
+ mk_store_args(Args, Offset, FixSrc ++ NewTail);
+mk_store_args([], _, Tail) ->
+ Tail.
+
+conv_comment(I, Map, Data) ->
+ I2 = [hipe_arm: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) ->
+ Arity = length(Args),
+ {RegArgs,StkArgs} = split_args(Args),
+ move_actuals(RegArgs,
+ [hipe_arm:mk_pseudo_tailcall_prepare(),
+ hipe_arm:mk_pseudo_tailcall(Fun, Arity, StkArgs, Linkage)]).
+
+conv_goto(I, Map, Data) ->
+ I2 = [hipe_arm:mk_b_label(hipe_rtl:goto_label(I))],
+ {I2, Map, Data}.
+
+conv_label(I, Map, Data) ->
+ I2 = [hipe_arm: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) ->
+ case {LoadSize,LoadSign} of
+ {byte,signed} ->
+ case hipe_arm:is_temp(Base1) of
+ true ->
+ case hipe_arm:is_temp(Base2) of
+ true ->
+ mk_ldrsb_rr(Dst, Base1, Base2);
+ _ ->
+ mk_ldrsb_ri(Dst, Base1, Base2)
+ end;
+ _ ->
+ case hipe_arm:is_temp(Base2) of
+ true ->
+ mk_ldrsb_ri(Dst, Base2, Base1);
+ _ ->
+ mk_ldrsb_ii(Dst, Base1, Base2)
+ end
+ end;
+ _ ->
+ LdOp =
+ case LoadSize of
+ byte -> 'ldrb';
+ int32 -> 'ldr';
+ word -> 'ldr'
+ end,
+ case hipe_arm:is_temp(Base1) of
+ true ->
+ case hipe_arm:is_temp(Base2) of
+ true ->
+ mk_load_rr(Dst, Base1, Base2, LdOp);
+ _ ->
+ mk_load_ri(Dst, Base1, Base2, LdOp)
+ end;
+ _ ->
+ case hipe_arm:is_temp(Base2) of
+ true ->
+ mk_load_ri(Dst, Base2, Base1, LdOp);
+ _ ->
+ mk_load_ii(Dst, Base1, Base2, LdOp)
+ end
+ end
+ end.
+
+mk_load_ii(Dst, Base1, Base2, LdOp) ->
+ 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)).
+
+mk_load_ri(Dst, Base, Offset, LdOp) ->
+ hipe_arm:mk_load(LdOp, Dst, Base, Offset, 'new', []).
+
+mk_load_rr(Dst, Base1, Base2, LdOp) ->
+ Am2 = hipe_arm:mk_am2(Base1, '+', Base2),
+ [hipe_arm:mk_load(LdOp, Dst, Am2)].
+
+mk_ldrsb_ii(Dst, Base1, Base2) ->
+ io:format("~w: RTL load signed byte with two immediates\n", [?MODULE]),
+ Tmp = new_untagged_temp(),
+ mk_li(Tmp, Base1,
+ mk_ldrsb_ri(Dst, Tmp, Base2)).
+
+mk_ldrsb_ri(Dst, Base, Offset) when is_integer(Offset) ->
+ {Sign,AbsOffset} =
+ if Offset < 0 -> {'-', -Offset};
+ true -> {'+', Offset}
+ end,
+ if AbsOffset =< 255 ->
+ Am3 = hipe_arm:mk_am3(Base, Sign, AbsOffset),
+ [hipe_arm:mk_ldrsb(Dst, Am3)];
+ true ->
+ Index = new_untagged_temp(),
+ Am3 = hipe_arm:mk_am3(Base, Sign, Index),
+ mk_li(Index, AbsOffset,
+ [hipe_arm:mk_ldrsb(Dst, Am3)])
+ end.
+
+mk_ldrsb_rr(Dst, Base1, Base2) ->
+ Am3 = hipe_arm:mk_am3(Base1, '+', Base2),
+ [hipe_arm:mk_ldrsb(Dst, Am3)].
+
+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_arm: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_arm: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_arm:is_temp(Src) of
+ true -> [hipe_arm: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_arm:mk_pseudo_blr()]),
+ {I2, Map0, Data}.
+
+conv_store(I, Map, Data) ->
+ {Base, Map0} = conv_dst(hipe_rtl:store_base(I), Map),
+ {Src, Map1} = conv_src(hipe_rtl:store_src(I), Map0),
+ {Offset, Map2} = conv_src(hipe_rtl:store_offset(I), Map1),
+ StoreSize = hipe_rtl:store_size(I),
+ I2 = mk_store(Src, Base, Offset, StoreSize),
+ {I2, Map2, Data}.
+
+mk_store(Src, Base, Offset, StoreSize) ->
+ StOp =
+ case StoreSize of
+ byte -> 'strb';
+ int32 -> 'str';
+ word -> 'str'
+ end,
+ case hipe_arm:is_temp(Src) of
+ true ->
+ mk_store2(Src, Base, Offset, StOp);
+ _ ->
+ Tmp = new_untagged_temp(),
+ mk_li(Tmp, Src,
+ mk_store2(Tmp, Base, Offset, StOp))
+ end.
+
+mk_store2(Src, Base, Offset, StOp) ->
+ case hipe_arm:is_temp(Offset) of
+ true ->
+ mk_store_rr(Src, Base, Offset, StOp);
+ _ ->
+ mk_store_ri(Src, Base, Offset, StOp)
+ end.
+
+mk_store_ri(Src, Base, Offset, StOp) ->
+ hipe_arm:mk_store(StOp, Src, Base, Offset, 'new', []).
+
+mk_store_rr(Src, Base, Index, StOp) ->
+ Am2 = hipe_arm:mk_am2(Base, '+', Index),
+ [hipe_arm:mk_store(StOp, Src, Am2)].
+
+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(),
+ I2 =
+ [hipe_arm:mk_pseudo_li(JTabR, {JTabLab,constant}),
+ hipe_arm:mk_pseudo_switch(JTabR, IndexR, Labels)],
+ {I2, Map1, NewData}.
+
+%%% Create a conditional branch.
+
+mk_pseudo_bc(Cond, TrueLabel, FalseLabel, Pred) ->
+ [hipe_arm:mk_pseudo_bc(Cond, TrueLabel, FalseLabel, Pred)].
+
+%%% Load an integer constant into a register.
+
+mk_li(Dst, Value) -> mk_li(Dst, Value, []).
+
+mk_li(Dst, Value, Tail) ->
+ hipe_arm:mk_li(Dst, Value, Tail).
+
+%%% Convert an RTL condition code.
+
+conv_alub_cond(RtlAluOp, Cond) -> % may be unsigned, depends on aluop
+ %% Note: ARM has a non-standard definition of the Carry flag:
+ %% 'cmp', 'sub', and 'rsb' define Carry as the NEGATION of Borrow.
+ %% This means that the mapping between C/Z combinations and
+ %% conditions like "lower" and "higher" becomes non-standard.
+ %% (See conv_branch_cond/1 which maps ltu to lo/carry-clear,
+ %% while x86 maps ltu to b/carry-set.)
+ %% Here in conv_alub_cond/2 it means that the mapping of unsigned
+ %% conditions also has to consider the alu operator, since e.g.
+ %% 'add' and 'sub' behave differently with regard to Carry.
+ case {RtlAluOp, Cond} of % handle allowed alub unsigned conditions
+ {'add', 'ltu'} -> 'hs'; % add+ltu == unsigned overflow == carry set == hs
+ %% add more cases when needed
+ _ -> conv_cond(Cond)
+ end.
+
+conv_cond(Cond) -> % only signed
+ case Cond of
+ eq -> 'eq';
+ ne -> 'ne';
+ gt -> 'gt';
+ ge -> 'ge';
+ lt -> 'lt';
+ le -> 'le';
+ overflow -> 'vs';
+ not_overflow -> 'vc'
+ end.
+
+conv_branch_cond(Cond) -> % may be unsigned
+ case Cond of
+ gtu -> 'hi';
+ geu -> 'hs';
+ ltu -> 'lo';
+ leu -> 'ls';
+ _ -> conv_cond(Cond)
+ end.
+
+%%% Commute an ARM condition code.
+
+commute_cond(Cond) -> % if x Cond y, then y commute_cond(Cond) x
+ case Cond of
+ 'eq' -> 'eq'; % ==, ==
+ 'ne' -> 'ne'; % !=, !=
+ 'gt' -> 'lt'; % >, <
+ 'ge' -> 'le'; % >=, <=
+ 'lt' -> 'gt'; % <, >
+ 'le' -> 'ge'; % <=, >=
+ 'hi' -> 'lo'; % >u, <u
+ 'hs' -> 'ls'; % >=u, <=u
+ 'lo' -> 'hi'; % <u, >u
+ 'ls' -> 'hs'; % <=u, >=u
+ %% vs/vc: n/a
+ _ -> exit({?MODULE,commute_cond,Cond})
+ 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_arm_registers:nr_args(), Args, []).
+
+split_args(I, N, [Arg|Args], RegArgs) when I < N ->
+ Reg = hipe_arm_registers:arg(I),
+ Temp = hipe_arm: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_arm: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_arm: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_arm: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_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' -> false; %hipe_arm_registers:is_precoloured_fpr(Name);
+ _ -> hipe_arm_registers:is_precoloured_gpr(Name)
+ end,
+ case IsPrecoloured of
+ true ->
+ {hipe_arm:mk_temp(Name, Type), Map};
+ false ->
+ case vmap_lookup(Map, Opnd) of
+ {value, NewTemp} ->
+ {NewTemp, Map};
+ _ ->
+ NewTemp = hipe_arm: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_arm_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_arm:mk_new_temp(Type); % allocatable
+ true -> hipe_arm: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_arm:mk_temp(hipe_arm_registers:stack_pointer(), 'untagged').
+
+%%% Create a temp representing the return value register.
+
+mk_rv() ->
+ hipe_arm:mk_temp(hipe_arm_registers:return_value(), 'tagged').
+
+%%% new_untagged_temp -- conjure up an untagged scratch reg
+
+new_untagged_temp() ->
+ hipe_arm:mk_new_temp('untagged').
+
+%%% new_tagged_temp -- conjure up a tagged scratch reg
+
+new_tagged_temp() ->
+ hipe_arm: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() ->
+ 4.