diff options
Diffstat (limited to 'lib/hipe/arm')
-rw-r--r-- | lib/hipe/arm/Makefile | 116 | ||||
-rw-r--r-- | lib/hipe/arm/TODO | 20 | ||||
-rw-r--r-- | lib/hipe/arm/hipe_arm.erl | 380 | ||||
-rw-r--r-- | lib/hipe/arm/hipe_arm.hrl | 124 | ||||
-rw-r--r-- | lib/hipe/arm/hipe_arm_assemble.erl | 665 | ||||
-rw-r--r-- | lib/hipe/arm/hipe_arm_cfg.erl | 131 | ||||
-rw-r--r-- | lib/hipe/arm/hipe_arm_defuse.erl | 157 | ||||
-rw-r--r-- | lib/hipe/arm/hipe_arm_encode.erl | 994 | ||||
-rw-r--r-- | lib/hipe/arm/hipe_arm_finalise.erl | 73 | ||||
-rw-r--r-- | lib/hipe/arm/hipe_arm_frame.erl | 639 | ||||
-rw-r--r-- | lib/hipe/arm/hipe_arm_liveness_gpr.erl | 38 | ||||
-rw-r--r-- | lib/hipe/arm/hipe_arm_main.erl | 58 | ||||
-rw-r--r-- | lib/hipe/arm/hipe_arm_pp.erl | 351 | ||||
-rw-r--r-- | lib/hipe/arm/hipe_arm_ra.erl | 56 | ||||
-rw-r--r-- | lib/hipe/arm/hipe_arm_ra_finalise.erl | 285 | ||||
-rw-r--r-- | lib/hipe/arm/hipe_arm_ra_ls.erl | 56 | ||||
-rw-r--r-- | lib/hipe/arm/hipe_arm_ra_naive.erl | 29 | ||||
-rw-r--r-- | lib/hipe/arm/hipe_arm_ra_postconditions.erl | 278 | ||||
-rw-r--r-- | lib/hipe/arm/hipe_arm_registers.erl | 207 | ||||
-rw-r--r-- | lib/hipe/arm/hipe_rtl_to_arm.erl | 836 |
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. |