diff options
author | Erlang/OTP <[email protected]> | 2009-11-20 14:54:40 +0000 |
---|---|---|
committer | Erlang/OTP <[email protected]> | 2009-11-20 14:54:40 +0000 |
commit | 84adefa331c4159d432d22840663c38f155cd4c1 (patch) | |
tree | bff9a9c66adda4df2106dfd0e5c053ab182a12bd /lib/hipe/rtl | |
download | otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2 otp-84adefa331c4159d432d22840663c38f155cd4c1.zip |
The R13B03 release.OTP_R13B03
Diffstat (limited to 'lib/hipe/rtl')
25 files changed, 15210 insertions, 0 deletions
diff --git a/lib/hipe/rtl/Makefile b/lib/hipe/rtl/Makefile new file mode 100644 index 0000000000..beab8da547 --- /dev/null +++ b/lib/hipe/rtl/Makefile @@ -0,0 +1,142 @@ +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 2001-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 + +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 +# ---------------------------------------------------- +ifdef HIPE_ENABLED +HIPE_MODULES = hipe_rtl hipe_rtl_cfg \ + hipe_rtl_liveness \ + hipe_icode2rtl hipe_rtl_mk_switch \ + hipe_rtl_primops \ + hipe_rtl_varmap hipe_rtl_exceptions \ + hipe_rtl_binary_match hipe_rtl_binary_construct \ + hipe_rtl_arith_32 hipe_rtl_arith_64 \ + hipe_rtl_ssa hipe_rtl_ssa_const_prop \ + hipe_rtl_cleanup_const hipe_rtl_symbolic hipe_rtl_lcm \ + hipe_rtl_ssapre hipe_rtl_binary hipe_rtl_ssa_avail_expr \ + hipe_rtl_arch hipe_tagscheme +else +HIPE_MODULES = +endif + +MODULES = $(HIPE_MODULES) + +HRL_FILES= hipe_literals.hrl +ERL_FILES= $(MODULES:%=%.erl) +TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) + +# APP_FILE= +# App_SRC= $(APP_FILE).src +# APP_TARGET= $(EBIN)/$(APP_FILE) +# +# APPUP_FILE= +# APPUP_SRC= $(APPUP_FILE).src +# APPUP_TARGET= $(EBIN)/$(APPUP_FILE) + +# ---------------------------------------------------- +# FLAGS: Please keep +inline below +# ---------------------------------------------------- + +include ../native.mk + +ERL_COMPILE_FLAGS += +inline + +# ---------------------------------------------------- +# Targets +# ---------------------------------------------------- + +debug opt: $(TARGET_FILES) + +docs: + +clean: + rm -f hipe_literals.hrl + rm -f $(TARGET_FILES) + rm -f core erl_crash.dump + +# ---------------------------------------------------- +# Special Build Targets +# ---------------------------------------------------- + + +# ---------------------------------------------------- +# Release Target +# ---------------------------------------------------- +include $(ERL_TOP)/make/otp_release_targets.mk + +release_spec: opt + $(INSTALL_DIR) $(RELSYSDIR)/rtl + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR)/rtl + $(INSTALL_DIR) $(RELSYSDIR)/ebin + $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + +release_docs_spec: + + +HIPE_MKLITERALS=$(ERL_TOP)/bin/$(TARGET)/hipe_mkliterals + +hipe_literals.hrl: $(HIPE_MKLITERALS) + $(HIPE_MKLITERALS) -e > hipe_literals.hrl + +../main/hipe.hrl: ../vsn.mk ../main/hipe.hrl.src + sed -e "s;%VSN%;$(HIPE_VSN);" ../main/hipe.hrl.src > ../main/hipe.hrl + +$(EBIN)/hipe_rtl.beam: hipe_rtl.hrl ../main/hipe.hrl +$(EBIN)/hipe_rtl_arch.beam: hipe_rtl.hrl hipe_literals.hrl +$(EBIN)/hipe_rtl_binary.beam: hipe_rtl.hrl hipe_literals.hrl +$(EBIN)/hipe_rtl_bin_util.beam: hipe_rtl.hrl hipe_literals.hrl +$(EBIN)/hipe_rtl_cfg.beam: hipe_rtl.hrl ../flow/cfg.hrl ../flow/cfg.inc ../main/hipe.hrl +$(EBIN)/hipe_rtl_cleanup_const.beam: hipe_rtl.hrl +$(EBIN)/hipe_rtl_liveness.beam: hipe_rtl.hrl ../flow/cfg.hrl ../flow/liveness.inc +$(EBIN)/hipe_icode2rtl.beam: hipe_literals.hrl ../main/hipe.hrl ../icode/hipe_icode.hrl +$(EBIN)/hipe_tagscheme.beam: hipe_rtl.hrl hipe_literals.hrl +$(EBIN)/hipe_rtl_primops.beam: hipe_rtl.hrl ../icode/hipe_icode_primops.hrl hipe_literals.hrl ../main/hipe.hrl +$(EBIN)/hipe_rtl_arith_32.beam: ../main/hipe.hrl hipe_rtl_arith.inc +$(EBIN)/hipe_rtl_arith_64.beam: ../main/hipe.hrl hipe_rtl_arith.inc +$(EBIN)/hipe_rtl_bs_ops.beam: hipe_literals.hrl ../main/hipe.hrl +$(EBIN)/hipe_rtl_cerl_bs_ops.beam: ../main/hipe.hrl hipe_literals.hrl hipe_rtl.hrl +$(EBIN)/hipe_rtl_exceptions.beam: hipe_literals.hrl ../main/hipe.hrl +$(EBIN)/hipe_rtl_inline_bs_ops.beam: hipe_rtl.hrl hipe_literals.hrl ../main/hipe.hrl +$(EBIN)/hipe_rtl_mk_switch.beam: ../main/hipe.hrl +$(EBIN)/hipe_rtl_lcm.beam: ../flow/cfg.hrl hipe_rtl.hrl +$(EBIN)/hipe_rtl_symbolic.beam: hipe_rtl.hrl hipe_literals.hrl ../flow/cfg.hrl ../icode/hipe_icode_primops.hrl +$(EBIN)/hipe_rtl_varmap.beam: ../main/hipe.hrl ../icode/hipe_icode.hrl + +$(EBIN)/hipe_rtl_ssa.beam: ../ssa/hipe_ssa.inc ../main/hipe.hrl ../ssa/hipe_ssa_liveness.inc hipe_rtl.hrl +$(EBIN)/hipe_rtl_ssa_const_prop.beam: hipe_rtl.hrl ../main/hipe.hrl ../flow/cfg.hrl ../ssa/hipe_ssa_const_prop.inc +$(EBIN)/hipe_rtl_ssapre.beam: ../main/hipe.hrl ../flow/cfg.hrl hipe_rtl.hrl diff --git a/lib/hipe/rtl/hipe_icode2rtl.erl b/lib/hipe/rtl/hipe_icode2rtl.erl new file mode 100644 index 0000000000..034153a3cb --- /dev/null +++ b/lib/hipe/rtl/hipe_icode2rtl.erl @@ -0,0 +1,727 @@ +%% -*- erlang-indent-level: 2 -*- +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2001-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% +%% +%%======================================================================= +%% File : hipe_icode2rtl.erl +%% Author(s) : Erik Johansson +%% Description : Translates Icode to RTL +%%======================================================================= +%% +%% $Id$ +%% +%% TODO: Better handling of switches... + +-module(hipe_icode2rtl). + +-export([translate/2]). +-export([translate_instrs/4]). %% used in hipe_rtl_mk_switch + +%%------------------------------------------------------------------------- + +%% -define(DEBUG,1). % used by hipe.hrl below + +-include("../main/hipe.hrl"). +-include("../icode/hipe_icode.hrl"). +-include("hipe_literals.hrl"). + +%%------------------------------------------------------------------------- + +%% @spec translate(IcodeRecord::#icode{}, Options::options()) -> term() +%% +%% options() = [option()] +%% option() = term() +%% +%% @doc Translates a linear form of Icode for a single function to a +%% linear form of RTL-code. +%% +translate(IcodeRecord = #icode{}, Options) -> + ?IF_DEBUG_LEVEL(2, put(hipe_mfa, hipe_icode:icode_fun(IcodeRecord)), ok), + %% hipe_icode_pp:pp(Fun), + + %% Initialize gensym and varmap + {Args, VarMap} = hipe_rtl_varmap:init(IcodeRecord), + %% Get the name and other info of the function to translate. + MFA = hipe_icode:icode_fun(IcodeRecord), + ConstTab = hipe_consttab:new(), % hipe_icode:icode_data(IcodeRecord), + %% io:format("~w\n", [ConstTab]), + Icode = hipe_icode:icode_code(IcodeRecord), + IsClosure = hipe_icode:icode_is_closure(IcodeRecord), + IsLeaf = hipe_icode:icode_is_leaf(IcodeRecord), + IcodeInfo = hipe_icode:icode_info(IcodeRecord), + + %% Translate Icode instructions to RTL instructions + ?opt_start_timer("Icode to nested RTL"), + {Code, _VarMap1, ConstTab1} = + translate_instrs(Icode, VarMap, ConstTab, Options), + ?opt_stop_timer("Icode to nested RTL"), + %% We build the code as list of lists of... + %% in order to avoid appends. + ?opt_start_timer("Flatten RTL"), + Code1 = lists:flatten(Code), + ?opt_stop_timer("Flatten RTL"), + %% Build the RTL structure. + Rtl = hipe_rtl:mk_rtl(MFA, + Args, + IsClosure, + IsLeaf, + Code1, + ConstTab1, + {1, hipe_gensym:get_var(rtl)}, + {1, hipe_gensym:get_label(rtl)}), + %% hipe_rtl:pp(Rtl), + %% Propagate info from Icode to RTL. + hipe_rtl:rtl_info_update(Rtl, IcodeInfo). + +%%------------------------------------------------------------------------- + +%% +%% @doc Translates a list of Icode instructions to a list of RTL instructions. +%% +translate_instrs(Is, VarMap, ConstTab, Options) -> + translate_instrs(Is, VarMap, [], ConstTab, Options). + +translate_instrs([], VarMap, RTL_Code, ConstTab, _Options) -> + {RTL_Code, VarMap, ConstTab}; +translate_instrs([I|Is], VarMap, AccCode, ConstTab, Options) -> + %% Translate one instruction. + {Code, VarMap0, ConstTab0} = + translate_instruction(I, VarMap, ConstTab, Options), + %% ?IF_DEBUG_LEVEL(3,?msg(" To Instr: ~w~n",[Code]),no_debug), + ?IF_DEBUG(?when_option(rtl_show_translation, Options, + ?msg(" To Instr: ~w~n", [Code])), ok), + translate_instrs(Is, VarMap0, [AccCode,Code], ConstTab0, Options). + +%% +%% @doc Translates an Icode instruction to one or more RTL instructions. +%% + +translate_instruction(I, VarMap, ConstTab, Options) -> + %% ?IF_DEBUG_LEVEL(3,?msg("From Instr: ~w~n",[I]),no_debug), + ?IF_DEBUG(?when_option(rtl_show_translation, Options, + ?msg("From Instr: ~w~n", [I])), ok), + case I of + #icode_call{} -> + gen_call(I, VarMap, ConstTab); + #icode_comment{} -> + {hipe_rtl:mk_comment(hipe_icode:comment_text(I)), VarMap, ConstTab}; + #icode_enter{} -> + gen_enter(I, VarMap, ConstTab); + #icode_fail{} -> + gen_fail(I, VarMap, ConstTab); + #icode_goto{} -> + gen_goto(I, VarMap, ConstTab); + #icode_if{} -> + gen_if(I, VarMap, ConstTab); + #icode_label{} -> + gen_label(I, VarMap, ConstTab); + #icode_move{} -> + gen_move(I, VarMap, ConstTab); + #icode_begin_handler{} -> + hipe_rtl_exceptions:gen_begin_handler(I, VarMap, ConstTab); + #icode_return{} -> + gen_return(I, VarMap, ConstTab); + #icode_switch_val{} -> + gen_switch_val(I, VarMap, ConstTab, Options); + #icode_switch_tuple_arity{} -> + gen_switch_tuple(I, VarMap, ConstTab, Options); + #icode_type{} -> + gen_type(I, VarMap, ConstTab); + X -> + exit({?MODULE,{"unknown Icode instruction",X}}) + end. + +%%------------------------------------------------------------------------- + +%% +%% CALL +%% + +gen_call(I, VarMap, ConstTab) -> + Fun = hipe_icode:call_fun(I), + {Dst, VarMap0} = hipe_rtl_varmap:ivs2rvs(hipe_icode:call_dstlist(I), VarMap), + Fail = hipe_icode:call_fail_label(I), + + {Args, VarMap1, ConstTab1, InitCode} = + args_to_vars(hipe_icode:call_args(I), VarMap0, ConstTab), + + IsGuard = hipe_icode:call_in_guard(I), + + {FailLblName, VarMap3} = + case Fail of + [] -> %% Not in a catch + {[], VarMap1}; + _ -> + {FLbl, VarMap2} = + hipe_rtl_varmap:icode_label2rtl_label(Fail, VarMap1), + {hipe_rtl:label_name(FLbl), VarMap2} + end, + + {ContLblName, ContLbl, VarMap4} = + case hipe_icode:call_continuation(I) of + [] -> %% This call does not end a BB. + CLbl = hipe_rtl:mk_new_label(), + {hipe_rtl:label_name(CLbl), CLbl, VarMap3}; + Cont -> + {CLbl, NewVarMap} = + hipe_rtl_varmap:icode_label2rtl_label(Cont, VarMap3), + {hipe_rtl:label_name(CLbl), [], NewVarMap} + end, + + {Code, ConstTab2} = + case hipe_icode:call_type(I) of + primop -> + hipe_rtl_primops:gen_primop( + {Fun, Dst, Args, ContLblName, FailLblName}, + IsGuard, ConstTab1); + Type -> + Call = gen_call_1(Fun, Dst, Args, IsGuard, ContLblName, + FailLblName, Type), + {Call, ConstTab1} + end, + {[InitCode,Code,ContLbl], VarMap4, ConstTab2}. + +%% This catches those standard functions that we inline expand + +gen_call_1(Fun={_M,_F,_A}, Dst, Args, IsGuard, Cont, Fail, Type) -> + case hipe_rtl_primops:gen_call_builtin(Fun, Dst, Args, IsGuard, Cont, + Fail) of + [] -> + hipe_rtl:mk_call(Dst, Fun, Args, Cont, Fail, conv_call_type(Type)); + Code -> + Code + end. + +conv_call_type(remote) -> remote; +conv_call_type(local) -> not_remote. + +%% -------------------------------------------------------------------- + +%% +%% ENTER +%% + +gen_enter(I, VarMap, ConstTab) -> + Fun = hipe_icode:enter_fun(I), + {Args, VarMap1, ConstTab1, InitCode} = + args_to_vars(hipe_icode:enter_args(I), VarMap, ConstTab), + {Code1, ConstTab2} = + case hipe_icode:enter_type(I) of + primop -> + IsGuard = false, % enter can not happen in a guard + hipe_rtl_primops:gen_enter_primop({Fun, Args}, IsGuard, ConstTab1); + Type -> + Call = gen_enter_1(Fun, Args, Type), + {Call, ConstTab1} + end, + {[InitCode,Code1], VarMap1, ConstTab2}. + +%% This catches those standard functions that we inline expand + +gen_enter_1(Fun, Args, Type) -> + case hipe_rtl_primops:gen_enter_builtin(Fun, Args) of + [] -> + hipe_rtl:mk_enter(Fun, Args, conv_call_type(Type)); + Code -> + Code + end. + +%% -------------------------------------------------------------------- + +%% +%% FAIL +%% + +gen_fail(I, VarMap, ConstTab) -> + Fail = hipe_icode:fail_label(I), + {Label, VarMap0} = + if Fail =:= [] -> + %% not in a catch + {[], VarMap}; + true -> + {Lbl, Map} = hipe_rtl_varmap:icode_label2rtl_label(Fail, VarMap), + {hipe_rtl:label_name(Lbl), Map} + end, + {Args, VarMap1, ConstTab1, InitCode} = + args_to_vars(hipe_icode:fail_args(I), VarMap0, ConstTab), + Class = hipe_icode:fail_class(I), + FailCode = hipe_rtl_exceptions:gen_fail(Class, Args, Label), + {[InitCode, FailCode], VarMap1, ConstTab1}. + +%% -------------------------------------------------------------------- + +%% +%% GOTO +%% + +gen_goto(I, VarMap, ConstTab) -> + {Label, Map0} = + hipe_rtl_varmap:icode_label2rtl_label(hipe_icode:goto_label(I), VarMap), + {hipe_rtl:mk_goto(hipe_rtl:label_name(Label)), Map0, ConstTab}. + +%% -------------------------------------------------------------------- + +%% +%% IF +%% + +gen_if(I, VarMap, ConstTab) -> + {Args, VarMap1, ConstTab1, InitCode} = + args_to_vars(hipe_icode:if_args(I), VarMap, ConstTab), + {TrueLbl, VarMap2} = + hipe_rtl_varmap:icode_label2rtl_label(hipe_icode:if_true_label(I), VarMap1), + {FalseLbl, VarMap3} = + hipe_rtl_varmap:icode_label2rtl_label(hipe_icode:if_false_label(I),VarMap2), + CondCode = + gen_cond(hipe_icode:if_op(I), + Args, + hipe_rtl:label_name(TrueLbl), + hipe_rtl:label_name(FalseLbl), + hipe_icode:if_pred(I)), + {[InitCode,CondCode], VarMap3, ConstTab1}. + + +%% -------------------------------------------------------------------- + +%% +%% LABEL +%% + +gen_label(I, VarMap, ConstTab) -> + LabelName = hipe_icode:label_name(I), + {NewLabel,Map0} = hipe_rtl_varmap:icode_label2rtl_label(LabelName, VarMap), + {NewLabel,Map0,ConstTab}. + +%% -------------------------------------------------------------------- + +%% +%% MOVE +%% + +gen_move(I, VarMap, ConstTab) -> + MovedSrc = hipe_icode:move_src(I), + {Dst, VarMap0} = + hipe_rtl_varmap:icode_var2rtl_var(hipe_icode:move_dst(I), VarMap), + case hipe_icode:is_const(MovedSrc) of + true -> + {Code, NewConstMap} = gen_const_move(Dst, MovedSrc, ConstTab), + {[Code], VarMap0, NewConstMap}; + false -> + {Src, VarMap1} = hipe_rtl_varmap:icode_var2rtl_var(MovedSrc, VarMap0), + Code = + case hipe_icode:is_fvar(MovedSrc) of + true -> + hipe_rtl:mk_fmove(Dst, Src); + false -> % It is a var or reg + hipe_rtl:mk_move(Dst, Src) + end, + {[Code], VarMap1, ConstTab} + end. + +%% -------------------------------------------------------------------- + +%% +%% RETURN +%% + +gen_return(I, VarMap, ConstTab) -> + {RetVars, VarMap0, ConstTab0, Code} = + args_to_vars(hipe_icode:return_vars(I), VarMap, ConstTab), + {Code ++ [hipe_rtl:mk_return(RetVars)], VarMap0, ConstTab0}. + +%% -------------------------------------------------------------------- + +%% +%% SWITCH +%% + +%% +%% Rewrite switch_val to the equivalent Icode if-then-else sequence, +%% then translate that sequence instead. +%% Doing this at the RTL level would generate the exact same code, +%% but would also require _a_lot_ more work. +%% (Don't believe me? Try it. I did, and threw the code away in disgust. +%% The main ugliness comes from (1) maintaining ConstTab for the constants +%% that may be added there [switch_val is not limited to immediates!], +%% (2) maintaining Map for the translated labels, and (3) expanding +%% equality tests to eq-or-call-primop-exact_eqeq_2.) +%% +%% TODO: +%% - separate immediate and non-immediate cases, +%% and translate each list separately +%% +-ifdef(usesjumptable). +-define(uumess,?msg("~w Use jtab: ~w\n", + [Options,proplists:get_bool(use_jumptable, Options)])). +-else. +-define(uumess,ok). +-endif. + +gen_switch_val(I, VarMap, ConstTab, Options) -> + %% If you want to see whether jumptables are used or not... + ?uumess, + hipe_rtl_mk_switch:gen_switch_val(I, VarMap, ConstTab, Options). + +gen_switch_tuple(I, Map, ConstTab, Options) -> + hipe_rtl_mk_switch:gen_switch_tuple(I, Map, ConstTab, Options). + +%% -------------------------------------------------------------------- + +%% +%% TYPE +%% + +gen_type(I, VarMap, ConstTab) -> + {Vars, Map0, NewConstTab, Code1} = + args_to_vars(hipe_icode:type_args(I), VarMap, ConstTab), + {TrueLbl, Map1} = + hipe_rtl_varmap:icode_label2rtl_label(hipe_icode:type_true_label(I), Map0), + {FalseLbl, Map2} = + hipe_rtl_varmap:icode_label2rtl_label(hipe_icode:type_false_label(I), Map1), + {Code2, NewConstTab1} = gen_type_test(Vars, hipe_icode:type_test(I), + hipe_rtl:label_name(TrueLbl), + hipe_rtl:label_name(FalseLbl), + hipe_icode:type_pred(I), + NewConstTab), + {Code1 ++ Code2, Map2, NewConstTab1}. + +%% -------------------------------------------------------------------- + +%% +%% Generate code for a type test. If X is not of type Type then goto Label. +%% + +gen_type_test([X], Type, TrueLbl, FalseLbl, Pred, ConstTab) -> + case Type of + atom -> + {hipe_tagscheme:test_atom(X, TrueLbl, FalseLbl, Pred), ConstTab}; + bignum -> + {hipe_tagscheme:test_bignum(X, TrueLbl, FalseLbl, Pred), ConstTab}; + binary -> + {hipe_tagscheme:test_binary(X, TrueLbl, FalseLbl, Pred), ConstTab}; + bitstr -> + {hipe_tagscheme:test_bitstr(X, TrueLbl, FalseLbl, Pred), ConstTab}; + boolean -> + TmpT = hipe_rtl:mk_new_var(), + TmpF = hipe_rtl:mk_new_var(), + Lbl = hipe_rtl:mk_new_label(), + {[hipe_rtl:mk_load_atom(TmpT, true), + hipe_rtl:mk_branch(X, eq, TmpT, TrueLbl,hipe_rtl:label_name(Lbl),Pred), + Lbl, + hipe_rtl:mk_load_atom(TmpF, false), + hipe_rtl:mk_branch(X, eq, TmpF, TrueLbl, FalseLbl, Pred)], ConstTab}; + cons -> + {hipe_tagscheme:test_cons(X, TrueLbl, FalseLbl, Pred), ConstTab}; + constant -> + {hipe_tagscheme:test_constant(X, TrueLbl, FalseLbl, Pred), ConstTab}; + fixnum -> + {hipe_tagscheme:test_fixnum(X, TrueLbl, FalseLbl, Pred), ConstTab}; + float -> + {hipe_tagscheme:test_flonum(X, TrueLbl, FalseLbl, Pred), ConstTab}; + function -> + {hipe_tagscheme:test_fun(X, TrueLbl, FalseLbl, Pred), ConstTab}; + integer -> + {hipe_tagscheme:test_integer(X, TrueLbl, FalseLbl, Pred), ConstTab}; + list -> + {hipe_tagscheme:test_list(X, TrueLbl, FalseLbl, Pred), ConstTab}; + nil -> + {hipe_tagscheme:test_nil(X, TrueLbl, FalseLbl, Pred), ConstTab}; + number -> + {hipe_tagscheme:test_number(X, TrueLbl, FalseLbl, Pred), ConstTab}; + pid -> + {hipe_tagscheme:test_any_pid(X, TrueLbl, FalseLbl, Pred), ConstTab}; + port -> + {hipe_tagscheme:test_any_port(X, TrueLbl, FalseLbl, Pred), ConstTab}; + reference -> + {hipe_tagscheme:test_ref(X, TrueLbl, FalseLbl, Pred), ConstTab}; + tuple -> + {hipe_tagscheme:test_tuple(X, TrueLbl, FalseLbl, Pred), ConstTab}; + {atom, Atom} -> + Tmp = hipe_rtl:mk_new_var(), + {[hipe_rtl:mk_load_atom(Tmp, Atom), + hipe_rtl:mk_branch(X, eq, Tmp, TrueLbl, FalseLbl, Pred)], ConstTab}; + {integer, N} when is_integer(N) -> + %% XXX: warning, does not work for bignums + case hipe_tagscheme:is_fixnum(N) of + true -> + Int = hipe_tagscheme:mk_fixnum(N), + {hipe_rtl:mk_branch(X, eq, hipe_rtl:mk_imm(Int), + TrueLbl, FalseLbl, Pred), + ConstTab}; + false -> + BignumLbl = hipe_rtl:mk_new_label(), + RetLbl = hipe_rtl:mk_new_label(), + BigN = hipe_rtl:mk_new_var(), + Tmp = hipe_rtl:mk_new_var(), + {BigCode,NewConstTab} = gen_big_move(BigN, N, ConstTab), + {[hipe_tagscheme:test_fixnum(X, FalseLbl, + hipe_rtl:label_name(BignumLbl),1-Pred), + BignumLbl, BigCode] + ++ + [hipe_rtl:mk_call([Tmp], op_exact_eqeq_2 , [X,BigN], + hipe_rtl:label_name(RetLbl),[],not_remote), + RetLbl, + hipe_rtl:mk_branch(Tmp, ne, hipe_rtl:mk_imm(0), + TrueLbl, FalseLbl, Pred)], + NewConstTab} + end; + {record, A, S} -> + TupleLbl = hipe_rtl:mk_new_label(), + TupleLblName = hipe_rtl:label_name(TupleLbl), + AtomLab = hipe_rtl:mk_new_label(), + AtomLabName = hipe_rtl:label_name(AtomLab), + TagVar = hipe_rtl:mk_new_var(), + TmpAtomVar = hipe_rtl:mk_new_var(), + {UntagCode, ConstTab1} = + hipe_rtl_primops:gen_primop({{unsafe_element,1},[TagVar],[X], + AtomLabName,[]}, + false, ConstTab), + Code = + hipe_tagscheme:test_tuple_N(X, S, TupleLblName, FalseLbl, Pred) ++ + [TupleLbl|UntagCode] ++ + [AtomLab, + hipe_rtl:mk_load_atom(TmpAtomVar, A), + hipe_rtl:mk_branch(TagVar, eq, TmpAtomVar, TrueLbl, FalseLbl, Pred)], + {Code, + ConstTab1}; + {tuple, N} -> + {hipe_tagscheme:test_tuple_N(X, N, TrueLbl, FalseLbl, Pred), ConstTab}; + Other -> + exit({?MODULE,{"unknown type",Other}}) + end; +gen_type_test(Z = [X,Y], Type, TrueLbl, FalseLbl, Pred, ConstTab) -> + case Type of + function2 -> + {hipe_tagscheme:test_fun2(X, Y, TrueLbl, FalseLbl, Pred), ConstTab}; + fixnum -> + {hipe_tagscheme:test_fixnums(Z, TrueLbl, FalseLbl, Pred), ConstTab}; + Other -> + exit({?MODULE,{"unknown type",Other}}) + end; +gen_type_test(X, Type, TrueLbl, FalseLbl, Pred, ConstTab) -> + case Type of + fixnum -> + {hipe_tagscheme:test_fixnums(X, TrueLbl, FalseLbl, Pred), ConstTab}; + Other -> + exit({?MODULE,{"type cannot have several arguments",Other}}) + end. + + +%% -------------------------------------------------------------------- +%% +%% Generate code for the if-conditional. +%% + +gen_cond(CondOp, Args, TrueLbl, FalseLbl, Pred) -> + Tmp = hipe_rtl:mk_new_reg_gcsafe(), + GenLbl = hipe_rtl:mk_new_label(), + TestRetLbl = hipe_rtl:mk_new_label(), + TestRetName = hipe_rtl:label_name(TestRetLbl), + + case CondOp of + 'fixnum_eq' -> + [Arg1, Arg2] = Args, + [hipe_rtl:mk_branch(Arg1, eq, Arg2, TrueLbl, + FalseLbl, Pred)]; + '=:=' -> + [Arg1, Arg2] = Args, + [hipe_rtl:mk_branch(Arg1, eq, Arg2, TrueLbl, + hipe_rtl:label_name(GenLbl), Pred), + GenLbl, + hipe_rtl:mk_call([Tmp], op_exact_eqeq_2, Args, + TestRetName, [], not_remote), + TestRetLbl, + hipe_rtl:mk_branch(Tmp, ne, hipe_rtl:mk_imm(0), + TrueLbl, FalseLbl, Pred)]; + 'fixnum_neq' -> + [Arg1, Arg2] = Args, + [hipe_rtl:mk_branch(Arg1, eq, Arg2, FalseLbl, + TrueLbl, 1-Pred)]; + '=/=' -> + [Arg1, Arg2] = Args, + [hipe_rtl:mk_branch(Arg1, eq, Arg2, FalseLbl, + hipe_rtl:label_name(GenLbl), 1-Pred), + GenLbl, + hipe_rtl:mk_call([Tmp], op_exact_eqeq_2, Args, + TestRetName, [], not_remote), + TestRetLbl, + hipe_rtl:mk_branch(Tmp, ne, hipe_rtl:mk_imm(0), + FalseLbl, TrueLbl, Pred)]; + '==' -> + [Arg1, Arg2] = Args, + [hipe_rtl:mk_branch(Arg1, eq, Arg2, + TrueLbl, hipe_rtl:label_name(GenLbl), Pred), + GenLbl, + hipe_rtl:mk_call([Tmp], cmp_2, Args, TestRetName, [], not_remote), + TestRetLbl, + hipe_rtl:mk_branch(Tmp, eq, hipe_rtl:mk_imm(0), + TrueLbl, FalseLbl, Pred)]; + '/=' -> + [Arg1, Arg2] = Args, + [hipe_rtl:mk_branch(Arg1, eq, Arg2, + FalseLbl, hipe_rtl:label_name(GenLbl), 1-Pred), + GenLbl, + hipe_rtl:mk_call([Tmp], cmp_2, Args, TestRetName, [], not_remote), + TestRetLbl, + hipe_rtl:mk_branch(Tmp, ne, hipe_rtl:mk_imm(0), + TrueLbl, FalseLbl, Pred)]; + 'fixnum_gt' -> + [Arg1, Arg2] = Args, + [hipe_tagscheme:fixnum_gt(Arg1, Arg2, TrueLbl, FalseLbl, Pred)]; + 'fixnum_ge' -> + [Arg1, Arg2] = Args, + [hipe_tagscheme:fixnum_ge(Arg1, Arg2, TrueLbl, FalseLbl, Pred)]; + 'fixnum_lt' -> + [Arg1, Arg2] = Args, + [hipe_tagscheme:fixnum_lt(Arg1, Arg2, TrueLbl, FalseLbl, Pred)]; + 'fixnum_le' -> + [Arg1, Arg2] = Args, + [hipe_tagscheme:fixnum_le(Arg1, Arg2, TrueLbl, FalseLbl, Pred)]; + '>' -> + [Arg1, Arg2] = Args, + [hipe_tagscheme:test_two_fixnums(Arg1, Arg2, + hipe_rtl:label_name(GenLbl)), + hipe_tagscheme:fixnum_gt(Arg1, Arg2, TrueLbl, FalseLbl, Pred), + GenLbl, + hipe_rtl:mk_call([Tmp], cmp_2, Args, TestRetName, [], not_remote), + TestRetLbl, + hipe_rtl:mk_branch(Tmp, gt, hipe_rtl:mk_imm(0), + TrueLbl, FalseLbl, Pred)]; + '<' -> + [Arg1, Arg2] = Args, + [hipe_tagscheme:test_two_fixnums(Arg1, Arg2, + hipe_rtl:label_name(GenLbl)), + hipe_tagscheme:fixnum_lt(Arg1, Arg2, TrueLbl, FalseLbl, Pred), + GenLbl, + hipe_rtl:mk_call([Tmp], cmp_2, Args, TestRetName, [], not_remote), + TestRetLbl, + hipe_rtl:mk_branch(Tmp, lt, hipe_rtl:mk_imm(0), + TrueLbl, FalseLbl, Pred)]; + '>=' -> + [Arg1, Arg2] = Args, + [hipe_tagscheme:test_two_fixnums(Arg1, Arg2, + hipe_rtl:label_name(GenLbl)), + hipe_tagscheme:fixnum_ge(Arg1, Arg2, TrueLbl, FalseLbl, Pred), + GenLbl, + hipe_rtl:mk_call([Tmp], cmp_2, Args, TestRetName, [], not_remote), + TestRetLbl, + hipe_rtl:mk_branch(Tmp, ge, hipe_rtl:mk_imm(0), + TrueLbl, FalseLbl, Pred)]; + '=<' -> + [Arg1, Arg2] = Args, + [hipe_tagscheme:test_two_fixnums(Arg1, Arg2, + hipe_rtl:label_name(GenLbl)), + hipe_tagscheme:fixnum_le(Arg1, Arg2, TrueLbl, FalseLbl, Pred), + GenLbl, + hipe_rtl:mk_call([Tmp], cmp_2, Args, TestRetName, [], not_remote), + TestRetLbl, + hipe_rtl:mk_branch(Tmp, le, hipe_rtl:mk_imm(0), + TrueLbl, FalseLbl, Pred)]; + _Other -> + [hipe_rtl:mk_call([Tmp], CondOp, Args, TestRetName, [], not_remote), + TestRetLbl, + hipe_rtl:mk_branch(Tmp, ne, hipe_rtl:mk_imm(0), + TrueLbl, FalseLbl, Pred)] + end. + +%% -------------------------------------------------------------------- +%% +%% Translate a list argument list of icode vars to rtl vars. Also +%% handles constants in arguments. +%% + +args_to_vars([Arg|Args],VarMap, ConstTab) -> + {Vars, VarMap1, ConstTab1, Code} = + args_to_vars(Args, VarMap, ConstTab), + case hipe_icode:is_variable(Arg) of + true -> + {Var, VarMap2} = hipe_rtl_varmap:icode_var2rtl_var(Arg, VarMap1), + {[Var|Vars], VarMap2, ConstTab1, Code}; + false -> + case type_of_const(Arg) of + big -> + ConstVal = hipe_icode:const_value(Arg), + {ConstTab2, Label} = hipe_consttab:insert_term(ConstTab1, ConstVal), + NewArg = hipe_rtl:mk_const_label(Label), + {[NewArg|Vars], VarMap1, ConstTab2, Code}; + fixnum -> + ConstVal = hipe_icode:const_value(Arg), + NewArg = hipe_rtl:mk_imm(tagged_val_of(ConstVal)), + {[NewArg|Vars], VarMap1, ConstTab1, Code}; + nil -> + NewArg = hipe_rtl:mk_imm(tagged_val_of([])), + {[NewArg|Vars], VarMap1, ConstTab1, Code}; + _ -> + Var = hipe_rtl:mk_new_var(), + {Code2, ConstTab2} = gen_const_move(Var, Arg, ConstTab1), + {[Var|Vars], VarMap1, ConstTab2, [Code2,Code]} + end + end; +args_to_vars([], VarMap, ConstTab) -> + {[], VarMap, ConstTab, []}. + +%% -------------------------------------------------------------------- + +%% +%% Translate a move where the source is a constant +%% + +gen_const_move(Dst, Const, ConstTab) -> + ConstVal = hipe_icode:const_value(Const), + case type_of_const(Const) of + %% const_fun -> + %% gen_fun_move(Dst, ConstVal, ConstTab); + nil -> + Src = hipe_rtl:mk_imm(tagged_val_of([])), + {hipe_rtl:mk_move(Dst, Src), ConstTab}; + fixnum -> + Src = hipe_rtl:mk_imm(tagged_val_of(ConstVal)), + {hipe_rtl:mk_move(Dst, Src), ConstTab}; + atom -> + {hipe_rtl:mk_load_atom(Dst, ConstVal), ConstTab}; + big -> + gen_big_move(Dst, ConstVal, ConstTab) + end. + +%% gen_fun_move(Dst, Fun, ConstTab) -> +%% ?WARNING_MSG("Funmove ~w! -- NYI\n", [Fun]), +%% {NewTab, Label} = hipe_consttab:insert_fun(ConstTab, Fun), +%% {hipe_rtl:mk_load_address(Dst, Label, constant), NewTab}. + +gen_big_move(Dst, Big, ConstTab) -> + {NewTab, Label} = hipe_consttab:insert_term(ConstTab, Big), + {hipe_rtl:mk_move(Dst, hipe_rtl:mk_const_label(Label)), + NewTab}. + +type_of_const(Const) -> + case hipe_icode:const_value(Const) of + [] -> + nil; + X when is_integer(X) -> + case hipe_tagscheme:is_fixnum(X) of + true -> fixnum; + false -> big + end; + A when is_atom(A) -> + atom; + _ -> + big + end. + +tagged_val_of([]) -> hipe_tagscheme:mk_nil(); +tagged_val_of(X) when is_integer(X) -> hipe_tagscheme:mk_fixnum(X). diff --git a/lib/hipe/rtl/hipe_rtl.erl b/lib/hipe/rtl/hipe_rtl.erl new file mode 100644 index 0000000000..ef06b2abf8 --- /dev/null +++ b/lib/hipe/rtl/hipe_rtl.erl @@ -0,0 +1,1655 @@ +%% -*- erlang-indent-level: 2 -*- +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2001-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% +%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% @doc +%% +%% Provides an abstract datatype for HiPE's RTL (Register Transfer Language). +%% +%% <h3> RTL - Register Transfer Language </h3> +%% +%% Consists of the instructions: +%% <ul> +%% <li> {alu, Dst, Src1, Op, Src2} </li> +%% <li> {alub, Dst, Src1, Op, Src2, RelOp, TrueLabel, FalseLabel, P} </li> +%% <li> {branch, Src1, Src2, RelOp, TrueLabel, FalseLabel, P} </li> +%% <li> {call, DsListt, Fun, ArgList, Type, Continuation, FailContinuation} +%% Type is one of {local, remote, primop, closure} </li> +%% <li> {comment, Text} </li> +%% <li> {enter, Fun, ArgList, Type} +%% Type is one of {local, remote, primop, closure} </li> +%% <li> {fconv, Dst, Src} </li> +%% <li> {fload, Dst, Src, Offset} </li> +%% <li> {fmove, Dst, Src} </li> +%% <li> {fp, Dst, Src1, Op, Src2} </li> +%% <li> {fp_unop, Dst, Src, Op} </li> +%% <li> {fstore, Base, Offset, Src} </li> +%% <li> {gctest, Words} </li> +%% <li> {goto, Label} </li> +%% <li> {goto_index, Block, Index, LabelList} </li> +%% <li> {label, Name} </li> +%% <li> {load, Dst, Src, Offset, Size, Sign} </li> +%% <li> {load_address, Dst, Addr, Type} </li> +%% <li> {load_atom, Dst, Atom} </li> +%% <li> {load_word_index, Dst, Block, Index} </li> +%% <li> {move, Dst, Src} </li> +%% <li> {multimove, [Dst1, ..., DstN], [Src1, ..., SrcN]} </li> +%% <li> {phi, Dst, Id, [Src1, ..., SrcN]} </li> +%% <li> {return, VarList} </li> +%% <li> {store, Base, Offset, Src, Size} </li> +%% <li> {switch, Src1, Labels, SortedBy} </li> +%% </ul> +%% +%% There are three kinds of 'registers' in RTL. +%% <ol> +%% <li> Variables containing tagged data that are traced by the GC. </li> +%% <li> Registers that are ignored by the GC. </li> +%% <li> Floating point registers. </li> +%% </ol> +%% These registers all share the same namespace. +%% +%% IMPORTANT: +%% +%% The variables contain tagged Erlang terms, the registers +%% contain untagged values (that can be all sorts of things) and +%% the floating point registers contain untagged floating point +%% values. This means that the different kinds of 'registers' are +%% incompatible and CANNOT be assigned to each other unless the +%% proper conversions are made. +%% +%% When performing optimizations, it is reasonably safe to move +%% values stored in variables. However, when moving around untagged +%% values from either registers or floating point registers make +%% sure you know what you are doing. +%% +%% Example 1: A register might contain the untagged pointer to +%% something on the heap. If this value is moved across +%% a program point where a garbage collection might +%% occur, the pointer can be invalid. If you are lucky +%% you will end up with a segmentation fault; if unlucky, +%% you will be stuck on a wild goose chase. +%% +%% Example 2: Floating point arithmetic instructions must occur in +%% a floating point block. Otherwise, exceptions can be +%% masked. +%% +%% @end +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-module(hipe_rtl). +-include("../main/hipe.hrl"). + +-export([mk_rtl/8, + rtl_fun/1, + rtl_params/1, + rtl_is_closure/1, + rtl_is_leaf/1, + rtl_code/1, + rtl_code_update/2, + rtl_data/1, + %% rtl_data_update/2, + %% rtl_var_range/1, + %% rtl_var_range_update/2, + %% rtl_label_range/1, + %% rtl_label_range_update/2, + rtl_info/1, + rtl_info_update/2]). + +-export([mk_move/2, + move_dst/1, + move_src/1, + %% move_src_update/2, + %% is_move/1, + + mk_multimove/2, + multimove_dstlist/1, + multimove_srclist/1, + %% multimove_srclist_update/2, + %% is_multimove/1, + + mk_phi/1, + phi_dst/1, + phi_id/1, + phi_arg/2, + phi_arglist/1, + is_phi/1, + phi_enter_pred/3, + phi_remove_pred/2, + + mk_alu/4, + alu_dst/1, + alu_src1/1, + alu_src1_update/2, + alu_src2/1, + alu_src2_update/2, + alu_op/1, + %% is_alu_op/1, + is_shift_op/1, + + mk_load/3, + mk_load/5, + load_dst/1, + load_src/1, + load_offset/1, + load_size/1, + load_sign/1, + + mk_load_atom/2, + load_atom_dst/1, + load_atom_atom/1, + + mk_load_word_index/3, + load_word_index_dst/1, + %% load_word_index_index/1, + %% load_word_index_block/1, + + mk_goto_index/3, + goto_index_index/1, + %% goto_index_block/1, + goto_index_labels/1, + + mk_load_address/3, + load_address_dst/1, + %% load_address_dst_update/2, + load_address_addr/1, + load_address_addr_update/2, + load_address_type/1, + %% load_address_type_update/2, + + mk_store/3, + mk_store/4, + store_base/1, + store_src/1, + store_offset/1, + store_size/1, + + mk_label/1, + mk_new_label/0, + label_name/1, + is_label/1, + + mk_branch/5, + mk_branch/6, + branch_src1/1, + branch_src2/1, + branch_cond/1, + branch_true_label/1, + branch_false_label/1, + branch_pred/1, + %% is_branch/1, + %% branch_true_label_update/2, + %% branch_false_label_update/2, + + mk_alub/7, + mk_alub/8, + alub_dst/1, + alub_src1/1, + alub_op/1, + alub_src2/1, + alub_cond/1, + alub_true_label/1, + %% alub_true_label_update/2, + alub_false_label/1, + %% alub_false_label_update/2, + alub_pred/1, + %% is_alub/1, + + mk_switch/2, + %% mk_switch/3, + mk_sorted_switch/3, + switch_src/1, + %% switch_src_update/2, + switch_labels/1, + %% switch_labels_update/2, + switch_sort_order/1, + %% switch_sort_order_update/2, + + mk_goto/1, + goto_label/1, + is_goto/1, + %% goto_label_update/2, + + mk_call/6, + call_fun/1, + call_dstlist/1, + call_dstlist_update/2, + call_arglist/1, + call_continuation/1, + call_fail/1, + call_type/1, + %% call_continuation_update/2, + %% call_fail_update/2, + is_call/1, + + mk_enter/3, + enter_fun/1, + enter_arglist/1, + enter_type/1, + + mk_return/1, + return_varlist/1, + + mk_gctest/1, + gctest_words/1, + + mk_comment/1, + comment_text/1, + is_comment/1, + + mk_fload/3, + fload_dst/1, + fload_src/1, + %% fload_src_update/2, + fload_offset/1, + %% fload_offset_update/2, + + mk_fstore/3, + fstore_base/1, + fstore_src/1, + fstore_offset/1, + + mk_fp/4, + fp_dst/1, + fp_src1/1, + %% fp_src1_update/2, + fp_src2/1, + %% fp_src2_update/2, + fp_op/1, + + mk_fp_unop/3, + fp_unop_dst/1, + fp_unop_src/1, + %% fp_unop_src_update/2, + fp_unop_op/1, + + mk_fmove/2, + fmove_dst/1, + fmove_src/1, + %% fmove_src_update/2, + %% is_fmove/1, + + mk_fconv/2, + fconv_dst/1, + fconv_src/1, + %% fconv_src_update/2, + %% is_fconv/1, + + %% mk_var/1, + mk_new_var/0, + is_var/1, + var_index/1, + + %% change_vars_to_regs/1, + + mk_fixnumop/3, + fixnumop_dst/1, + fixnumop_src/1, + fixnumop_type/1, + + mk_reg/1, % assumes non gc-safe + mk_reg_gcsafe/1, + mk_new_reg/0, % assumes non gc-safe + mk_new_reg_gcsafe/0, + is_reg/1, + reg_index/1, + reg_is_gcsafe/1, + + %% mk_fpreg/1, + mk_new_fpreg/0, + is_fpreg/1, + fpreg_index/1, + + mk_imm/1, + is_imm/1, + imm_value/1, + + mk_const_label/1, + const_label_label/1, + is_const_label/1, + + args/1, + uses/1, + %% subst/2, + subst_uses/2, + subst_defines/2, + defines/1, + redirect_jmp/3, + is_safe/1, + %% highest_var/1, + pp/1, + pp/2, + pp_block/1, + + %% FIXME _dst_update command. Ok to export these? + alu_dst_update/2, + fconv_dst_update/2, + fload_dst_update/2, + %% fmove_dst_update/2, + fp_dst_update/2, + fp_unop_dst_update/2, + load_dst_update/2, + load_address_dst_update/2, + load_atom_dst_update/2, + load_word_index_dst_update/2, + %% move_dst_update/2, + fixnumop_dst_update/2, + pp_instr/2, + %% pp_arg/2, + phi_arglist_update/2, + phi_redirect_pred/3]). + +%% +%% RTL +%% + +-record(rtl, {'fun', %% Name of the function (MFA) + arglist, %% List of argument names (formals) + is_closure, %% True if this is code for a closure. + is_leaf, %% True if this is a leaf function. + code, %% Linear list of RTL-instructions. + data, %% Data segment + var_range, %% {Min,Max} First and last name used for + %% regs, fpregs, or vars. + %% (they use a common namespace) + label_range, %% {Min,Max} First and last name used for labels + info=[] %% A keylist with arbitrary information. + }). + +mk_rtl(Fun, ArgList, Closure, Leaf, Code, Data, VarRange, LabelRange) -> + #rtl{'fun'=Fun, arglist=ArgList, code=Code, + data=Data, is_closure=Closure, is_leaf=Leaf, + var_range=VarRange, label_range=LabelRange}. +rtl_fun(#rtl{'fun'=Fun}) -> Fun. +rtl_params(#rtl{arglist=ArgList}) -> ArgList. +rtl_is_closure(#rtl{is_closure=Closure}) -> Closure. +rtl_is_leaf(#rtl{is_leaf=Leaf}) -> Leaf. +rtl_code(#rtl{code=Code}) -> Code. +rtl_code_update(Rtl, Code) -> Rtl#rtl{code=Code}. +rtl_data(#rtl{data=Data}) -> Data. +%% rtl_data_update(Rtl, Data) -> Rtl#rtl{data=Data}. +%% rtl_var_range(#rtl{var_range=VarRange}) -> VarRange. +%% rtl_var_range_update(Rtl, VarRange) -> Rtl#rtl{var_range=VarRange}. +%% rtl_label_range(#rtl{label_range=LabelRange}) -> LabelRange. +%% rtl_label_range_update(Rtl, LabelRange) -> Rtl#rtl{label_range=LabelRange}. +rtl_info(#rtl{info=Info}) -> Info. +rtl_info_update(Rtl, Info) -> Rtl#rtl{info=Info}. + +%%----------------------------------------------------------------------------- + +-include("hipe_rtl.hrl"). + +%%----------------------------------------------------------------------------- + +%% +%% move +%% + +mk_move(Dst, Src) -> #move{dst=Dst, src=Src}. +move_dst(#move{dst=Dst}) -> Dst. +move_dst_update(M, NewDst) -> M#move{dst=NewDst}. +move_src(#move{src=Src}) -> Src. +move_src_update(M, NewSrc) -> M#move{src=NewSrc}. +%% is_move(#move{}) -> true; +%% is_move(_) -> false. + +%% +%% multimove +%% + +mk_multimove(DstList, SrcList) -> + case length(DstList) =:= length(SrcList) of + true -> true; + false -> + exit({?MODULE,mk_multimove, + {"different arities",{dstlist,DstList},{srclist,SrcList}}}) + end, + #multimove{dstlist=DstList, srclist=SrcList}. +multimove_dstlist(#multimove{dstlist=DstList}) -> DstList. +multimove_dstlist_update(M, NewDstList) -> M#multimove{dstlist=NewDstList}. +multimove_srclist(#multimove{srclist=SrcList}) -> SrcList. +multimove_srclist_update(M, NewSrcList) -> M#multimove{srclist=NewSrcList}. +%% is_multimove(#multimove{}) -> true; +%% is_multimove(_) -> false. + +%% +%% phi +%% + +%% The id field is not entirely redundant. It is used in mappings +%% in the SSA pass since the dst field can change. +mk_phi(Var) -> #phi{dst = Var, id = Var, arglist = []}. +%% mk_phi(Var, ArgList) -> #phi{dst = Var, id = Var, arglist = ArgList}. +phi_dst(#phi{dst=Dst}) -> Dst. +phi_dst_update(Phi, NewDst) -> Phi#phi{dst = NewDst}. +phi_id(#phi{id=Id}) -> Id. +phi_args(Phi) -> [X || {_,X} <- phi_arglist(Phi)]. +phi_arg(Phi, Pred) -> + case lists:keyfind(Pred, 1, phi_arglist(Phi)) of + false -> + exit({?MODULE,phi_arg,{"Uknown Phi predecessor",Phi,{pred,Pred}}}); + {_, Var} -> Var + end. +phi_arglist(#phi{arglist=ArgList}) -> ArgList. +phi_arglist_update(P,NewArgList) ->P#phi{arglist=NewArgList}. +is_phi(#phi{}) -> true; +is_phi(_) -> false. +phi_enter_pred(Phi, Pred, Var) -> + Phi#phi{arglist=[{Pred,Var}|lists:keydelete(Pred, 1, phi_arglist(Phi))]}. +phi_remove_pred(Phi, Pred) -> + NewArgList = lists:keydelete(Pred, 1, phi_arglist(Phi)), + case NewArgList of + [Arg] -> %% the phi should be turned into a move instruction + {_Label,Var} = Arg, + mk_move(phi_dst(Phi), Var); + %% io:format("~nPhi (~w) turned into move (~w) when removing pred ~w~n",[Phi,Move,Pred]), + [_|_] -> + Phi#phi{arglist=NewArgList} + end. +phi_argvar_subst(Phi, Subst) -> + NewArgList = [{Pred,subst1(Subst, Var)} || {Pred,Var} <- phi_arglist(Phi)], + Phi#phi{arglist=NewArgList}. +phi_redirect_pred(P, OldPred, NewPred)-> + Subst = [{OldPred, NewPred}], + NewArgList = [{subst1(Subst, Pred), Var} || {Pred,Var} <- phi_arglist(P)], + P#phi{arglist=NewArgList}. + + +%% +%% alu +%% + +mk_alu(Dst, Src1, Op, Src2) -> + #alu{dst=Dst, src1=Src1, op=Op, src2=Src2}. +alu_dst(#alu{dst=Dst}) -> Dst. +alu_dst_update(Alu, NewDst) -> Alu#alu{dst=NewDst}. +alu_src1(#alu{src1=Src1}) -> Src1. +alu_src1_update(Alu, NewSrc) -> Alu#alu{src1=NewSrc}. +alu_src2(#alu{src2=Src2}) -> Src2. +alu_src2_update(Alu, NewSrc) -> Alu#alu{src2=NewSrc}. +alu_op(#alu{op=Op}) -> Op. + +%% +%% load +%% + +mk_load(Dst, Src, Offset) -> mk_load(Dst, Src, Offset, word, unsigned). +mk_load(Dst, Src, Offset, Size, Sign) -> + ?ASSERT((Sign =:= unsigned) orelse (Sign =:= signed)), + ?ASSERT((Size =:= word) orelse (Size =:= int32) orelse + (Size =:= int16) orelse (Size =:= byte)), + #load{dst=Dst, src=Src, offset=Offset, size=Size, sign=Sign}. +load_dst(#load{dst=Dst}) -> Dst. +load_dst_update(L, NewDst) -> L#load{dst=NewDst}. +load_src(#load{src=Src}) -> Src. +load_src_update(L, NewSrc) -> L#load{src=NewSrc}. +load_offset(#load{offset=Offset}) -> Offset. +load_offset_update(L, NewOffset) -> L#load{offset=NewOffset}. +load_size(#load{size=Size}) -> Size. +load_sign(#load{sign=Sign}) -> Sign. + +%% +%% load_atom +%% + +mk_load_atom(Dst, Atom) -> #load_atom{dst=Dst,atom=Atom}. +load_atom_dst(#load_atom{dst=Dst}) -> Dst. +load_atom_dst_update(L, NewDst) -> L#load_atom{dst=NewDst}. +load_atom_atom(#load_atom{atom=Atom}) -> Atom. + +mk_load_word_index(Dst, Block, Index) -> + #load_word_index{dst=Dst, block=Block, index=Index}. +load_word_index_dst(#load_word_index{dst=Dst}) -> Dst. +load_word_index_dst_update(L, NewDst) -> L#load_word_index{dst=NewDst}. +load_word_index_block(#load_word_index{block=Block}) -> Block. +load_word_index_index(#load_word_index{index=Index}) -> Index. + +mk_goto_index(Block, Index, Labels) -> + #goto_index{block=Block, index=Index, labels=Labels}. +goto_index_block(#goto_index{block=Block}) -> Block. +goto_index_index(#goto_index{index=Index}) -> Index. +goto_index_labels(#goto_index{labels=Labels}) -> Labels. + +%% +%% load_address +%% + +mk_load_address(Dst, Addr, Type) -> + #load_address{dst=Dst, addr=Addr, type=Type}. +load_address_dst(#load_address{dst=Dst}) -> Dst. +load_address_dst_update(LA, NewDst) -> LA#load_address{dst=NewDst}. +load_address_addr(#load_address{addr=Addr}) -> Addr. +load_address_addr_update(LoadAddress, NewAdr) -> + LoadAddress#load_address{addr=NewAdr}. +load_address_type(#load_address{type=Type}) -> Type. +%% load_address_type_update(LA, NewType) -> LA#load_address{type=NewType}. + +%% +%% store +%% + +mk_store(Base, Offset, Src) -> mk_store(Base, Offset, Src, word). +mk_store(Base, Offset, Src, Size) -> + ?ASSERT((Size =:= word) orelse (Size =:= int32) orelse + (Size =:= int16) orelse (Size =:= byte)), + #store{base=Base, src=Src, offset=Offset, size=Size}. +store_base(#store{base=Base}) -> Base. +store_base_update(S, NewBase) -> S#store{base=NewBase}. +store_offset(#store{offset=Offset}) -> Offset. +store_offset_update(S, NewOffset) -> S#store{offset=NewOffset}. +store_src(#store{src=Src}) -> Src. +store_src_update(S, NewSrc) -> S#store{src=NewSrc}. +store_size(#store{size=Size}) -> Size. + +%% +%% label +%% + +mk_label(Name) -> #label{name=Name}. +mk_new_label() -> mk_label(hipe_gensym:get_next_label(rtl)). +label_name(#label{name=Name}) -> Name. +is_label(#label{}) -> true; +is_label(_) -> false. + +%% +%% branch +%% + +mk_branch(Src1, Op, Src2, True, False) -> + mk_branch(Src1, Op, Src2, True, False, 0.5). +mk_branch(Src1, Op, Src2, True, False, P) -> + #branch{src1=Src1, 'cond'=Op, src2=Src2, true_label=True, + false_label=False, p=P}. +branch_src1(#branch{src1=Src1}) -> Src1. +branch_src1_update(Br, NewSrc) -> Br#branch{src1=NewSrc}. +branch_src2(#branch{src2=Src2}) -> Src2. +branch_src2_update(Br, NewSrc) -> Br#branch{src2=NewSrc}. +branch_cond(#branch{'cond'=Cond}) -> Cond. +branch_true_label(#branch{true_label=TrueLbl}) -> TrueLbl. +branch_true_label_update(Br, NewTrue) -> Br#branch{true_label=NewTrue}. +branch_false_label(#branch{false_label=FalseLbl}) -> FalseLbl. +branch_false_label_update(Br, NewFalse) -> Br#branch{false_label=NewFalse}. +branch_pred(#branch{p=P}) -> P. + +%% +%% alub +%% + +mk_alub(Dst, Src1, Op, Src2, Cond, True, False) -> + mk_alub(Dst, Src1, Op, Src2, Cond, True, False, 0.5). +mk_alub(Dst, Src1, Op, Src2, Cond, True, False, P) -> + #alub{dst=Dst, src1=Src1, op=Op, src2=Src2, 'cond'=Cond, + true_label=True, false_label=False, p=P}. +alub_dst(#alub{dst=Dst}) -> Dst. +alub_dst_update(A, NewDst) -> A#alub{dst=NewDst}. +alub_src1(#alub{src1=Src1}) -> Src1. +alub_src1_update(A, NewSrc) -> A#alub{src1=NewSrc}. +alub_op(#alub{op=Op}) -> Op. +alub_src2(#alub{src2=Src2}) -> Src2. +alub_src2_update(A, NewSrc) -> A#alub{src2=NewSrc}. +alub_cond(#alub{'cond'=Cond}) -> Cond. +alub_true_label(#alub{true_label=TrueLbl}) -> TrueLbl. +alub_true_label_update(A, NewTrue) -> A#alub{true_label=NewTrue}. +alub_false_label(#alub{false_label=FalseLbl}) -> FalseLbl. +alub_false_label_update(A, NewFalse) -> A#alub{false_label=NewFalse}. +alub_pred(#alub{p=P}) -> P. + +%% +%% switch +%% + +mk_switch(Src, Labels) -> #switch{src=Src, labels=Labels}. +mk_sorted_switch(Src, Labels, Order) -> + #switch{src=Src, labels=Labels, sorted_by=Order}. +switch_src(#switch{src=Src}) -> Src. +switch_src_update(I, N) -> I#switch{src=N}. +switch_labels(#switch{labels=Labels}) -> Labels. +switch_labels_update(I,N) -> I#switch{labels=N}. +switch_sort_order(#switch{sorted_by=Order}) -> Order. +%% switch_sort_order_update(I,N) -> I#switch{sorted_by=N}. + +%% +%% goto +%% + +mk_goto(Label) -> #goto{label=Label}. +goto_label(#goto{label=Label}) -> Label. +goto_label_update(I, NewLabel) -> + I#goto{label=NewLabel}. +is_goto(#goto{}) -> true; +is_goto(_) -> false. + +%% +%% call +%% + +mk_call(DstList, Fun, ArgList, Continuation, FailContinuation, Type) -> + case Type of + remote -> ok; + not_remote -> ok + end, + #call{dstlist=DstList, 'fun'=Fun, arglist=ArgList, type=Type, + continuation=Continuation, + failcontinuation=FailContinuation}. +call_dstlist(#call{dstlist=DstList}) -> DstList. +call_dstlist_update(C, NewDstList) -> C#call{dstlist=NewDstList}. +call_fun(#call{'fun'=Fun}) -> Fun. +call_fun_update(C, F) -> C#call{'fun'=F}. +call_arglist(#call{arglist=ArgList}) -> ArgList. +call_arglist_update(C, NewArgList) -> C#call{arglist=NewArgList}. +call_continuation(#call{continuation=Continuation}) -> Continuation. +call_fail(#call{failcontinuation=FailContinuation}) -> FailContinuation. +call_type(#call{type=Type}) -> Type. +call_continuation_update(C, NewCont) -> C#call{continuation=NewCont}. +call_fail_update(C, NewCont) -> C#call{failcontinuation=NewCont}. +is_call(#call{}) -> true; +is_call(_) -> false. +call_is_known(C) -> + Fun = call_fun(C), + call_or_enter_fun_is_known(Fun). + +call_or_enter_fun_is_known(Fun) -> + case is_atom(Fun) of + true -> true; %% make the expected common case fast + false -> + case is_reg(Fun) of + true -> false; + false -> + case is_var(Fun) of + true -> false; + false -> + case Fun of + {M,F,A} when is_atom(M), is_atom(F), is_integer(A), A >= 0 -> + true; + {F,A} when is_atom(F), is_integer(A), A >= 0 -> + true; + _ -> %% colored versions of rtl_reg or rtl_var (used in SSA) + false + end + end + end + end. + +%% +%% enter +%% + +mk_enter(Fun, ArgList, Type) -> + case Type of + remote -> ok; + not_remote -> ok % {local,primop,closure,pointer} + end, + #enter{'fun'=Fun, arglist=ArgList, type=Type}. +enter_fun(#enter{'fun'=Fun}) -> Fun. +enter_fun_update(I, F) -> I#enter{'fun' = F}. +enter_arglist(#enter{arglist=ArgList}) -> ArgList. +enter_arglist_update(E, NewArgList) -> E#enter{arglist=NewArgList}. +enter_type(#enter{type=Type}) -> Type. +enter_is_known(E) -> + Fun = enter_fun(E), + call_or_enter_fun_is_known(Fun). + +%% +%% return +%% + +mk_return(VarList) -> #return{varlist=VarList}. +return_varlist(#return{varlist=VarList}) -> VarList. +return_varlist_update(R, NewVarList) -> R#return{varlist=NewVarList}. + +%% +%% gctests +%% + +mk_gctest(Words) when is_integer(Words) -> #gctest{words=mk_imm(Words)}; +mk_gctest(Reg) -> #gctest{words=Reg}. % This handles rtl_regs and rtl_vars +gctest_words(#gctest{words=Words}) -> Words. +gctest_words_update(S, NewWords) -> S#gctest{words=NewWords}. + + +%% +%% fixnumop +%% + +mk_fixnumop(Dst, Src, Type) -> + #fixnumop{dst=Dst, src=Src, type=Type}. +fixnumop_dst(#fixnumop{dst=Dst}) -> Dst. +fixnumop_dst_update(S, Dst) -> S#fixnumop{dst=Dst}. +fixnumop_src(#fixnumop{src=Src}) -> Src. +fixnumop_src_update(S, Src) -> S#fixnumop{src=Src}. +fixnumop_type(#fixnumop{type=Type}) -> Type. + +%% +%% comments +%% + +mk_comment(Text) -> #comment{text=Text}. +comment_text(#comment{text=Text}) -> Text. +is_comment(#comment{}) -> true; +is_comment(_) -> false. + +%%------------------------------------------------------------------------- +%% Floating point stuff. +%%------------------------------------------------------------------------- + +%% +%% fload +%% + +mk_fload(Dst, Src, Offset) -> #fload{dst=Dst, src=Src, offset=Offset}. +fload_dst(#fload{dst=Dst}) -> Dst. +fload_dst_update(L, NewDst) -> L#fload{dst=NewDst}. +fload_src(#fload{src=Src}) -> Src. +fload_src_update(L, NewSrc) -> L#fload{src=NewSrc}. +fload_offset(#fload{offset=Offset}) -> Offset. +fload_offset_update(L, NewOffset) -> L#fload{offset=NewOffset}. + +%% +%% fstore +%% + +mk_fstore(Base, Offset, Src) -> + #fstore{base=Base, offset=Offset, src=Src}. +fstore_base(#fstore{base=Base}) -> Base. +fstore_base_update(F, NewBase) -> F#fstore{base=NewBase}. +fstore_offset(#fstore{offset=Offset}) -> Offset. +fstore_offset_update(F, NewOff) -> F#fstore{offset=NewOff}. +fstore_src(#fstore{src=Src}) -> Src. +fstore_src_update(F, NewSrc) -> F#fstore{src=NewSrc}. + +%% +%% fp +%% + +mk_fp(Dst, Src1, Op, Src2) -> + #fp{dst=Dst, src1=Src1, op=Op, src2=Src2}. +fp_dst(#fp{dst=Dst}) -> Dst. +fp_dst_update(Fp, NewDst) -> Fp#fp{dst=NewDst}. +fp_src1(#fp{src1=Src1}) -> Src1. +fp_src1_update(Fp, NewSrc) -> Fp#fp{src1=NewSrc}. +fp_src2(#fp{src2=Src2}) -> Src2. +fp_src2_update(Fp, NewSrc) -> Fp#fp{src2=NewSrc}. +fp_op(#fp{op=Op}) -> Op. + +%% +%% fp_unop +%% + +mk_fp_unop(Dst, Src, Op) -> + #fp_unop{dst=Dst, src=Src, op=Op}. +fp_unop_dst(#fp_unop{dst=Dst}) -> Dst. +fp_unop_dst_update(Fp, NewDst) -> Fp#fp_unop{dst=NewDst}. +fp_unop_src(#fp_unop{src=Src}) -> Src. +fp_unop_src_update(Fp, NewSrc) -> Fp#fp_unop{src=NewSrc}. +fp_unop_op(#fp_unop{op=Op}) -> Op. + +%% +%% fmove +%% + +mk_fmove(X, Y) -> #fmove{dst=X, src=Y}. +fmove_dst(#fmove{dst=Dst}) -> Dst. +fmove_dst_update(M, NewDst) -> M#fmove{dst=NewDst}. +fmove_src(#fmove{src=Src}) -> Src. +fmove_src_update(M, NewSrc) -> M#fmove{src=NewSrc}. + +%% +%% fconv +%% + +mk_fconv(X, Y) -> #fconv{dst=X, src=Y}. +fconv_dst(#fconv{dst=Dst}) -> Dst. +fconv_dst_update(C, NewDst) -> C#fconv{dst=NewDst}. +fconv_src(#fconv{src=Src}) -> Src. +fconv_src_update(C, NewSrc) -> C#fconv{src=NewSrc}. + +%% +%% The values +%% +%% change_vars_to_regs(Vars) -> +%% change_vars_to_regs(Vars, []). +%% change_vars_to_regs([Var|Rest], Acc) -> +%% change_vars_to_regs(Rest,[change_var_to_reg(Var)|Acc]); +%% change_vars_to_regs([], Acc) -> +%% lists:reverse(Acc). +%% +%% change_var_to_reg(Var) -> +%% mk_reg(var_index(Var)). + +-record(rtl_reg, {index :: integer(), + is_gc_safe :: boolean()}). + +mk_reg(Num, IsGcSafe) when is_integer(Num), Num >= 0 -> + #rtl_reg{index=Num,is_gc_safe=IsGcSafe}. +mk_reg(Num) -> mk_reg(Num, false). +mk_reg_gcsafe(Num) -> mk_reg(Num, true). +mk_new_reg() -> mk_reg(hipe_gensym:get_next_var(rtl), false). +mk_new_reg_gcsafe() -> mk_reg(hipe_gensym:get_next_var(rtl), true). +reg_index(#rtl_reg{index=Index}) -> Index. +reg_is_gcsafe(#rtl_reg{is_gc_safe=IsGcSafe}) -> IsGcSafe. +is_reg(#rtl_reg{}) -> true; +is_reg(_) -> false. + +-record(rtl_var, {index :: non_neg_integer()}). + +mk_var(Num) when is_integer(Num), Num >= 0 -> #rtl_var{index=Num}. +mk_new_var() -> mk_var(hipe_gensym:get_next_var(rtl)). +var_index(#rtl_var{index=Index}) -> Index. +is_var(#rtl_var{}) -> true; +is_var(_) -> false. + +-record(rtl_fpreg, {index :: non_neg_integer()}). + +mk_fpreg(Num) when is_integer(Num), Num >= 0 -> #rtl_fpreg{index=Num}. +mk_new_fpreg() -> mk_fpreg(hipe_gensym:get_next_var(rtl)). +fpreg_index(#rtl_fpreg{index=Index}) -> Index. +is_fpreg(#rtl_fpreg{}) -> true; +is_fpreg(_) -> false. + +-record(rtl_imm, {value}). + +mk_imm(Value) -> #rtl_imm{value=Value}. +imm_value(#rtl_imm{value=Value}) -> Value. +is_imm(#rtl_imm{}) -> true; +is_imm(_) -> false. + +-record(rtl_const_lbl, {label}). + +mk_const_label(Label) -> #rtl_const_lbl{label=Label}. +const_label_label(#rtl_const_lbl{label=Label}) -> Label. +is_const_label(#rtl_const_lbl{}) -> true; +is_const_label(_) -> false. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% +%% Utilities - no representation visible below this point +%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% +%% @doc Returns the list of variables, constant labels and immediates +%% an RTL instruction uses. +%% + +uses(I) -> + remove_imms_and_const_lbls(args(I)). + +%% +%% @doc Returns the list of variables an RTL instruction uses. +%% + +args(I) -> + case I of + #alu{} -> [alu_src1(I), alu_src2(I)]; + #alub{} -> [alub_src1(I), alub_src2(I)]; + #branch{} -> [branch_src1(I), branch_src2(I)]; + #call{} -> + Args = call_arglist(I) ++ hipe_rtl_arch:call_used(), + case call_is_known(I) of + false -> [call_fun(I) | Args]; + true -> Args + end; + #comment{} -> []; + #enter{} -> + Args = enter_arglist(I) ++ hipe_rtl_arch:tailcall_used(), + case enter_is_known(I) of + false -> [enter_fun(I) | Args]; + true -> Args + end; + #fconv{} -> [fconv_src(I)]; + #fixnumop{} -> [fixnumop_src(I)]; + #fload{} -> [fload_src(I), fload_offset(I)]; + #fmove{} -> [fmove_src(I)]; + #fp{} -> [fp_src1(I), fp_src2(I)]; + #fp_unop{} -> [fp_unop_src(I)]; + #fstore{} -> [fstore_base(I), fstore_offset(I), fstore_src(I)]; + #goto{} -> []; + #goto_index{} -> []; + #gctest{} -> [gctest_words(I)]; + #label{} -> []; + #load{} -> [load_src(I), load_offset(I)]; + #load_address{} -> []; + #load_atom{} -> []; + #load_word_index{} -> []; + #move{} -> [move_src(I)]; + #multimove{} -> multimove_srclist(I); + #phi{} -> phi_args(I); + #return{} -> return_varlist(I) ++ hipe_rtl_arch:return_used(); + #store{} -> [store_base(I), store_offset(I), store_src(I)]; + #switch{} -> [switch_src(I)] + end. + +%% +%% @doc Returns a list of variables that an RTL instruction defines. +%% + +defines(Instr) -> + Defs = case Instr of + #alu{} -> [alu_dst(Instr)]; + #alub{} -> [alub_dst(Instr)]; + #branch{} -> []; + #call{} -> call_dstlist(Instr) ++ hipe_rtl_arch:call_defined(); + #comment{} -> []; + #enter{} -> []; + #fconv{} -> [fconv_dst(Instr)]; + #fixnumop{} -> [fixnumop_dst(Instr)]; + #fload{} -> [fload_dst(Instr)]; + #fmove{} -> [fmove_dst(Instr)]; + #fp{} -> [fp_dst(Instr)]; + #fp_unop{} -> [fp_unop_dst(Instr)]; + #fstore{} -> []; + #gctest{} -> []; + #goto{} -> []; + #goto_index{} -> []; + #label{} -> []; + #load{} -> [load_dst(Instr)]; + #load_address{} -> [load_address_dst(Instr)]; + #load_atom{} -> [load_atom_dst(Instr)]; + #load_word_index{} -> [load_word_index_dst(Instr)]; + #move{} -> [move_dst(Instr)]; + #multimove{} -> multimove_dstlist(Instr); + #phi{} -> [phi_dst(Instr)]; + #return{} -> []; + #store{} -> []; + #switch{} -> [] + end, + remove_imms_and_const_lbls(Defs). + +%% @spec remove_imms_and_const_lbls([rtl_argument()]) -> [rtl_argument()] +%% +%% @doc Removes all RTL immediates and constant labels from a list of arguments. + +remove_imms_and_const_lbls([]) -> + []; +remove_imms_and_const_lbls([Arg|Args]) -> + case is_imm(Arg) orelse is_const_label(Arg) of + true -> remove_imms_and_const_lbls(Args); + false -> [Arg | remove_imms_and_const_lbls(Args)] + end. + +%% +%% Substitution: replace occurrences of X by Y if {X,Y} is in Subst. +%% +%% subst(Subst, X) -> +%% subst_defines(Subst, subst_uses(Subst,X)). + +subst_uses(Subst, I) -> + case I of + #alu{} -> + I0 = alu_src1_update(I, subst1(Subst, alu_src1(I))), + alu_src2_update(I0, subst1(Subst, alu_src2(I))); + #alub{} -> + I0 = alub_src1_update(I, subst1(Subst, alub_src1(I))), + alub_src2_update(I0, subst1(Subst, alub_src2(I))); + #branch{} -> + I0 = branch_src1_update(I, subst1(Subst, branch_src1(I))), + branch_src2_update(I0, subst1(Subst, branch_src2(I))); + #call{} -> + case call_is_known(I) of + false -> + I0 = call_fun_update(I, subst1(Subst, call_fun(I))), + call_arglist_update(I0, subst_list(Subst, call_arglist(I0))); + true -> + call_arglist_update(I, subst_list(Subst, call_arglist(I))) + end; + #comment{} -> + I; + #enter{} -> + case enter_is_known(I) of + false -> + I0 = enter_fun_update(I, subst1(Subst, enter_fun(I))), + enter_arglist_update(I0, subst_list(Subst, enter_arglist(I0))); + true -> + enter_arglist_update(I, subst_list(Subst, enter_arglist(I))) + end; + #fconv{} -> + fconv_src_update(I, subst1(Subst, fconv_src(I))); + #fixnumop{} -> + fixnumop_src_update(I, subst1(Subst, fixnumop_src(I))); + #fload{} -> + I0 = fload_src_update(I, subst1(Subst, fload_src(I))), + fload_offset_update(I0, subst1(Subst, fload_offset(I))); + #fmove{} -> + fmove_src_update(I, subst1(Subst, fmove_src(I))); + #fp{} -> + I0 = fp_src1_update(I, subst1(Subst, fp_src1(I))), + fp_src2_update(I0, subst1(Subst, fp_src2(I))); + #fp_unop{} -> + fp_unop_src_update(I, subst1(Subst, fp_unop_src(I))); + #fstore{} -> + I0 = fstore_src_update(I, subst1(Subst, fstore_src(I))), + I1 = fstore_base_update(I0, subst1(Subst, fstore_base(I))), + fstore_offset_update(I1, subst1(Subst, fstore_offset(I))); + #goto{} -> + I; + #goto_index{} -> + I; + #gctest{} -> + gctest_words_update(I, subst1(Subst, gctest_words(I))); + #label{} -> + I; + #load{} -> + I0 = load_src_update(I, subst1(Subst, load_src(I))), + load_offset_update(I0, subst1(Subst, load_offset(I))); + #load_address{} -> + I; + #load_atom{} -> + I; + #load_word_index{} -> + I; + #move{} -> + move_src_update(I, subst1(Subst, move_src(I))); + #multimove{} -> + multimove_srclist_update(I, subst_list(Subst, multimove_srclist(I))); + #phi{} -> + phi_argvar_subst(I, Subst); + #return{} -> + return_varlist_update(I, subst_list(Subst, return_varlist(I))); + #store{} -> + I0 = store_src_update(I, subst1(Subst, store_src(I))), + I1 = store_base_update(I0, subst1(Subst, store_base(I))), + store_offset_update(I1, subst1(Subst, store_offset(I))); + #switch{} -> + switch_src_update(I, subst1(Subst, switch_src(I))) + end. + +subst_defines(Subst, I)-> + case I of + #alu{} -> + alu_dst_update(I, subst1(Subst, alu_dst(I))); + #alub{} -> + alub_dst_update(I, subst1(Subst, alub_dst(I))); + #branch{} -> + I; + #call{} -> + call_dstlist_update(I, subst_list(Subst, call_dstlist(I))); + #comment{} -> + I; + #enter{} -> + I; + #fconv{} -> + fconv_dst_update(I, subst1(Subst, fconv_dst(I))); + #fixnumop{} -> + fixnumop_dst_update(I, subst1(Subst, fixnumop_dst(I))); + #fload{} -> + fload_dst_update(I, subst1(Subst, fload_dst(I))); + #fmove{} -> + fmove_dst_update(I, subst1(Subst, fmove_dst(I))); + #fp{} -> + fp_dst_update(I, subst1(Subst, fp_dst(I))); + #fp_unop{} -> + fp_unop_dst_update(I, subst1(Subst, fp_unop_dst(I))); + #fstore{} -> + I; + #gctest{} -> + I; + #goto{} -> + I; + #goto_index{} -> + I; + #label{} -> + I; + #load{} -> + load_dst_update(I, subst1(Subst, load_dst(I))); + #load_address{} -> + load_address_dst_update(I, subst1(Subst, load_address_dst(I))); + #load_atom{} -> + load_atom_dst_update(I, subst1(Subst, load_atom_dst(I))); + #load_word_index{} -> + load_word_index_dst_update(I, subst1(Subst, load_word_index_dst(I))); + #move{} -> + move_dst_update(I, subst1(Subst, move_dst(I))); + #multimove{} -> + multimove_dstlist_update(I, subst_list(Subst, multimove_dstlist(I))); + #phi{} -> + phi_dst_update(I, subst1(Subst, phi_dst(I))); + #return{} -> + I; + #store{} -> + I; + #switch{} -> + I + end. + +subst_list(S, Xs) -> + [subst1(S, X) || X <- Xs]. + +subst1([], X) -> X; +subst1([{X,Y}|_], X) -> Y; +subst1([_|Xs], X) -> subst1(Xs,X). + +%% @spec is_safe(rtl_instruction()) -> boolean() +%% +%% @doc Succeeds if an RTL instruction is safe and can be deleted if the +%% result is not used. + +is_safe(Instr) -> + case Instr of + #alu{} -> true; + #alub{} -> false; + #branch{} -> false; + #call{} -> false; + #comment{} -> false; + #enter{} -> false; + #fconv{} -> true; + #fixnumop{} -> true; + #fload{} -> true; + #fmove{} -> true; + #fp{} -> false; + #fp_unop{} -> false; + #fstore{} -> false; + #gctest{} -> false; + #goto{} -> false; + #goto_index{} -> false; % ??? + #label{} -> true; + #load{} -> true; + #load_address{} -> true; + #load_atom{} -> true; + #load_word_index{} -> true; + #move{} -> true; + #multimove{} -> true; + #phi{} -> true; + #return{} -> false; + #store{} -> false; + #switch{} -> false %% Maybe this is safe... + end. + +%% +%% True if argument is an alu-operator +%% + +%% is_alu_op(add) -> true; +%% is_alu_op(sub) -> true; +%% is_alu_op('or') -> true; +%% is_alu_op('and') -> true; +%% is_alu_op('xor') -> true; +%% is_alu_op(andnot) -> true; +%% is_alu_op(sll) -> true; +%% is_alu_op(srl) -> true; +%% is_alu_op(sra) -> true; +%% is_alu_op(_) -> false. + +%% @spec is_shift_op(rtl_operator()) -> boolean() +%% +%% @doc Succeeds if its argument is an RTL operator. +is_shift_op(sll) -> true; +is_shift_op(srl) -> true; +is_shift_op(sra) -> true; +is_shift_op(_) -> false. + + +%% +%% True if argument is an relational operator +%% + +%% is_rel_op(eq) -> true; +%% is_rel_op(ne) -> true; +%% is_rel_op(gt) -> true; +%% is_rel_op(gtu) -> true; +%% is_rel_op(ge) -> true; +%% is_rel_op(geu) -> true; +%% is_rel_op(lt) -> true; +%% is_rel_op(ltu) -> true; +%% is_rel_op(le) -> true; +%% is_rel_op(leu) -> true; +%% is_rel_op(overflow) -> true; +%% is_rel_op(not_overflow) -> true; +%% is_rel_op(_) -> false. + +redirect_jmp(Jmp, ToOld, ToNew) -> + %% OBS: In a jmp instruction more than one labels may be identical + %% and thus need redirection! + case Jmp of + #branch{} -> + TmpJmp = case branch_true_label(Jmp) of + ToOld -> branch_true_label_update(Jmp, ToNew); + _ -> Jmp + end, + case branch_false_label(TmpJmp) of + ToOld -> + branch_false_label_update(TmpJmp, ToNew); + _ -> + TmpJmp + end; + #switch{} -> + NewLbls = [case Lbl =:= ToOld of + true -> ToNew; + false -> Lbl + end || Lbl <- switch_labels(Jmp)], + switch_labels_update(Jmp, NewLbls); + #alub{} -> + TmpJmp = case alub_true_label(Jmp) of + ToOld -> alub_true_label_update(Jmp, ToNew); + _ -> Jmp + end, + case alub_false_label(TmpJmp) of + ToOld -> alub_false_label_update(TmpJmp, ToNew); + _ -> TmpJmp + end; + #goto{} -> + case goto_label(Jmp) of + ToOld -> goto_label_update(Jmp, ToNew); + _ -> Jmp + end; + #call{} -> + TmpJmp = case call_continuation(Jmp) of + ToOld -> call_continuation_update(Jmp, ToNew); + _ -> Jmp + end, + case call_fail(TmpJmp) of + ToOld -> call_fail_update(TmpJmp, ToNew); + _ -> TmpJmp + end; + _ -> + Jmp + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% highest_var(Code) -> +%% highest_var(Code,0). +%% +%% highest_var([I|Is],Max) -> +%% Defs = defines(I), +%% Uses = uses(I), +%% highest_var(Is,new_max(Defs++Uses,Max)); +%% highest_var([],Max) -> +%% Max. +%% +%% new_max([V|Vs],Max) -> +%% VName = +%% case is_var(V) of +%% true -> +%% var_index(V); +%% false -> +%% case is_fpreg(V) of +%% true -> +%% fpreg_index(V); +%% _ -> +%% reg_index(V) +%% end +%% end, +%% if VName > Max -> +%% new_max(Vs, VName); +%% true -> +%% new_max(Vs, Max) +%% end; +%% new_max([],Max) -> +%% Max. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% +%% @doc Pretty-printer for RTL. +%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +pp(Rtl) -> + pp(standard_io, Rtl). + +pp_block(Instrs) -> + pp_instrs(standard_io, Instrs). + +pp(Dev, Rtl) -> + io:format(Dev, "~w(", [rtl_fun(Rtl)]), + pp_args(Dev, rtl_params(Rtl)), + io:format(Dev, ") ->~n", []), + case rtl_is_closure(Rtl) of + true -> + io:format(Dev, ";; Closure\n", []); + false -> ok + end, + case rtl_is_leaf(Rtl) of + true -> + io:format(Dev, ";; Leaf function\n", []); + false -> ok + end, + io:format(Dev, ";; Info: ~w\n", [rtl_info(Rtl)]), + io:format(Dev, ".DataSegment\n", []), + hipe_data_pp:pp(Dev, rtl_data(Rtl), rtl, ""), + io:format(Dev, ".CodeSegment\n", []), + pp_instrs(Dev, rtl_code(Rtl)). + +pp_instrs(_Dev, []) -> + ok; +pp_instrs(Dev, [I|Is]) -> + try pp_instr(Dev, I) + catch _:_ -> io:format("*** ~w ***\n", [I]) + end, + pp_instrs(Dev, Is). + +pp_instr(Dev, I) -> + case I of + #phi{} -> + io:format(Dev, " ", []), + pp_arg(Dev, phi_dst(I)), + io:format(Dev, " <- phi(", []), + pp_phi_args(Dev, phi_arglist(I)), + io:format(Dev, ")~n", []); + #move{} -> + io:format(Dev, " ", []), + pp_arg(Dev, move_dst(I)), + io:format(Dev, " <- ", []), + pp_arg(Dev, move_src(I)), + io:format(Dev, "~n", []); + #multimove{} -> + io:format(Dev, " ", []), + pp_args(Dev, multimove_dstlist(I)), + io:format(Dev, " <= ", []), + pp_args(Dev, multimove_srclist(I)), + io:format(Dev, "~n", []); + #alu{} -> + io:format(Dev, " ", []), + pp_arg(Dev, alu_dst(I)), + io:format(Dev, " <- ", []), + pp_arg(Dev, alu_src1(I)), + io:format(Dev, " ~w ", [alu_op(I)]), + pp_arg(Dev, alu_src2(I)), + io:format(Dev, "~n", []); + #load{} -> + io:format(Dev, " ", []), + pp_arg(Dev, load_dst(I)), + io:format(Dev, " <- [", []), + pp_arg(Dev, load_src(I)), + io:format(Dev, "+", []), + pp_arg(Dev, load_offset(I)), + io:format(Dev, "]", []), + case load_sign(I) of + signed -> io:format(Dev, " -signed",[]); + _ -> ok + end, + case load_size(I) of + byte -> io:format(Dev, " -byte",[]); + int16 -> io:format(Dev, " -int16",[]); + int32 -> io:format(Dev, " -int32",[]); + _ -> ok + end, + io:format(Dev, "~n", []); + #load_atom{} -> + io:format(Dev, " ", []), + pp_arg(Dev, load_atom_dst(I)), + io:format(Dev, " <- atom_no(\'~s\')~n", [load_atom_atom(I)]); + #load_word_index{} -> + io:format(Dev, " ", []), + pp_arg(Dev, load_word_index_dst(I)), + io:format(Dev, " <- word_index_no( DL~p[~p] )~n", + [load_word_index_block(I),load_word_index_index(I)]); + #goto_index{} -> + io:format(Dev, " ", []), + io:format(Dev, "goto_index DL~p[~p]~n", + [goto_index_block(I), goto_index_index(I)]); + #load_address{} -> + io:format(Dev, " ", []), + pp_arg(Dev, load_address_dst(I)), + case load_address_type(I) of + constant -> + io:format(Dev, " <- DL~p~n", [load_address_addr(I)]); + closure -> + io:format(Dev, " <- L~p [closure]~n", [load_address_addr(I)]); + Type -> + io:format(Dev, " <- L~p [~p]~n", [load_address_addr(I),Type]) + end; + #store{} -> + io:format(Dev, " [", []), + pp_arg(Dev, store_base(I)), + io:format(Dev, "+", []), + pp_arg(Dev, store_offset(I)), + io:format(Dev, "] <- ", []), + pp_arg(Dev, store_src(I)), + case store_size(I) of + byte -> io:format(Dev, " -byte",[]); + int16 -> io:format(Dev, " -int16",[]); + int32 -> io:format(Dev, " -int32",[]); + _ -> ok + end, + io:format(Dev, "~n", []); + #label{} -> + io:format(Dev, "L~w:~n", [label_name(I)]); + #branch{} -> + io:format(Dev, " if (", []), + pp_arg(Dev, branch_src1(I)), + io:format(Dev, " ~w ", [branch_cond(I)]), + pp_arg(Dev, branch_src2(I)), + io:format(Dev, ") then L~w (~.2f) else L~w~n", + [branch_true_label(I), branch_pred(I), branch_false_label(I)]); + #switch{} -> + io:format(Dev, " switch (", []), + pp_arg(Dev, switch_src(I)), + io:format(Dev, ") <", []), + pp_switch_labels(Dev, switch_labels(I)), + io:format(Dev, ">\n", []); + #alub{} -> + io:format(Dev, " ", []), + pp_arg(Dev, alub_dst(I)), + io:format(Dev, " <- ", []), + pp_arg(Dev, alub_src1(I)), + io:format(Dev, " ~w ", [alub_op(I)]), + pp_arg(Dev, alub_src2(I)), + io:format(Dev, " if",[]), + io:format(Dev, " ~w ", [alub_cond(I)]), + io:format(Dev, "then L~w (~.2f) else L~w~n", + [alub_true_label(I), alub_pred(I), alub_false_label(I)]); + #goto{} -> + io:format(Dev, " goto L~w~n", [goto_label(I)]); + #call{} -> + io:format(Dev, " ", []), + pp_args(Dev, call_dstlist(I)), + io:format(Dev, " <- ", []), + case call_is_known(I) of + true -> + case call_fun(I) of + F when is_atom(F) -> + io:format(Dev, "~w(", [F]); + {M,F,A} when is_atom(M), is_atom(F), is_integer(A), A >= 0 -> + io:format(Dev, "~w:~w(", [M, F]); + {F,A} when is_atom(F), is_integer(A), A >=0 -> + io:format(Dev, "~w(", [F]) + end; + false -> + io:format(Dev, "(",[]), + pp_arg(Dev, call_fun(I)), + io:format(Dev, ")(",[]) + end, + pp_args(Dev, call_arglist(I)), + io:format(Dev, ")", []), + case call_continuation(I) of + [] -> true; + CC -> + io:format(Dev, " then L~w", [CC]) + end, + case call_fail(I) of + [] -> true; + L -> + io:format(Dev, " fail to L~w", [L]) + end, + io:format(Dev, "~n", []); + #enter{} -> + io:format(Dev, " ", []), + case enter_is_known(I) of + true -> + case enter_fun(I) of + F when is_atom(F) -> + io:format(Dev, "~w(", [F]); + {M,F,A} when is_atom(M), is_atom(F), is_integer(A), A >= 0 -> + io:format(Dev, "~w:~w(", [M, F]); + {F,A} when is_atom(F), is_integer(A), A >= 0 -> + io:format(Dev, "~w(", [F]) + end; + false -> + io:format(Dev, "(",[]), + pp_arg(Dev, enter_fun(I)), + io:format(Dev, ")(",[]) + end, + pp_args(Dev, enter_arglist(I)), + io:format(Dev, ")~n", []); + #return{} -> + io:format(Dev, " return(", []), + pp_args(Dev, return_varlist(I)), + io:format(Dev, ")~n", []); + #comment{} -> + io:format(Dev, " ;; ~p~n", [comment_text(I)]); + #fixnumop{} -> + io:format(Dev, " ", []), + pp_arg(Dev, fixnumop_dst(I)), + io:format(Dev, " <- ", []), + case fixnumop_type(I) of + tag -> + io:format(Dev, "fixnum_tag(", []); + untag -> + io:format(Dev, "fixnum_untag(", []) + end, + pp_arg(Dev, fixnumop_src(I)), + io:format(Dev, ")~n", []); + #gctest{} -> + io:format(Dev, " gctest(", []), + pp_arg(Dev, gctest_words(I)), + io:format(Dev, ")~n", []); + %% Floating point handling instructions below + #fload{} -> + io:format(Dev, " ", []), + pp_arg(Dev, fload_dst(I)), + io:format(Dev, " <-f [", []), + pp_arg(Dev, fload_src(I)), + io:format(Dev, "+", []), + pp_arg(Dev, fload_offset(I)), + io:format(Dev, "]~n", []); + #fstore{} -> + io:format(Dev, " [", []), + pp_arg(Dev, fstore_base(I)), + io:format(Dev, "+", []), + pp_arg(Dev, fstore_offset(I)), + io:format(Dev, "] <- ", []), + pp_arg(Dev, fstore_src(I)), + io:format(Dev, "~n", []); + #fp{} -> + io:format(Dev, " ", []), + pp_arg(Dev, fp_dst(I)), + io:format(Dev, " <- ", []), + pp_arg(Dev, fp_src1(I)), + io:format(Dev, " ~w ", [fp_op(I)]), + pp_arg(Dev, fp_src2(I)), + io:format(Dev, "~n", []); + #fp_unop{} -> + io:format(Dev, " ", []), + pp_arg(Dev, fp_unop_dst(I)), + io:format(Dev, " <- ", []), + io:format(Dev, " ~w ", [fp_unop_op(I)]), + pp_arg(Dev, fp_unop_src(I)), + io:format(Dev, "~n", []); + #fmove{} -> + io:format(Dev, " ", []), + pp_arg(Dev, fmove_dst(I)), + io:format(Dev, " <- ", []), + pp_arg(Dev, fmove_src(I)), + io:format(Dev, "~n", []); + #fconv{} -> + io:format(Dev, " ", []), + pp_arg(Dev, fconv_dst(I)), + io:format(Dev, " <-fconv ", []), + pp_arg(Dev, fconv_src(I)), + io:format(Dev, "~n", []); + Other -> + exit({?MODULE,pp_instr,{"unknown RTL instruction",Other}}) + end. + +pp_args(_Dev, []) -> + ok; +pp_args(Dev, [A]) -> + pp_arg(Dev, A); +pp_args(Dev, [A|As]) -> + pp_arg(Dev, A), + io:format(Dev, ", ", []), + pp_args(Dev, As). + +pp_phi_args(_Dev, []) -> ok; +pp_phi_args(Dev, [{Pred,A}]) -> + io:format(Dev, "{~w, ", [Pred]), + pp_arg(Dev, A), + io:format(Dev, "}", []); +pp_phi_args(Dev, [{Pred,A}|Args]) -> + io:format(Dev, "{~w, ", [Pred]), + pp_arg(Dev, A), + io:format(Dev, "}, ", []), + pp_phi_args(Dev, Args); +pp_phi_args(Dev, Args) -> + pp_args(Dev, Args). + +pp_hard_reg(Dev, N) -> + io:format(Dev, "~s", [hipe_rtl_arch:reg_name(N)]). + +pp_reg(Dev, Arg) -> + case hipe_rtl_arch:is_precoloured(Arg) of + true -> + pp_hard_reg(Dev, reg_index(Arg)); + false -> + io:format(Dev, "r~w", [reg_index(Arg)]) + end. + +pp_var(Dev, Arg) -> + case hipe_rtl_arch:is_precoloured(Arg) of + true -> + pp_hard_reg(Dev, var_index(Arg)); + false -> + io:format(Dev, "v~w", [var_index(Arg)]) + end. + +pp_arg(Dev, A) -> + case is_var(A) of + true -> + pp_var(Dev, A); + false -> + case is_reg(A) of + true -> + pp_reg(Dev, A); + false -> + case is_imm(A) of + true -> + io:format(Dev, "~w", [imm_value(A)]); + false -> + case is_fpreg(A) of + true -> + io:format(Dev, "f~w", [fpreg_index(A)]); + false -> + case is_const_label(A) of + true -> + io:format(Dev, "DL~w", [const_label_label(A)]); + false -> + exit({?MODULE,pp_arg,{"bad RTL arg",A}}) + end + end + end + end + end. + +pp_switch_labels(Dev,Lbls) -> + pp_switch_labels(Dev,Lbls,1). + +pp_switch_labels(Dev, [L], _Pos) -> + io:format(Dev, "L~w", [L]); +pp_switch_labels(Dev, [L|Ls], Pos) -> + io:format(Dev, "L~w, ", [L]), + NewPos = + case Pos of + 5 -> io:format(Dev, "\n ",[]), + 0; + N -> N + 1 + end, + pp_switch_labels(Dev, Ls, NewPos); +pp_switch_labels(_Dev, [], _) -> + ok. diff --git a/lib/hipe/rtl/hipe_rtl.hrl b/lib/hipe/rtl/hipe_rtl.hrl new file mode 100644 index 0000000000..974e40f830 --- /dev/null +++ b/lib/hipe/rtl/hipe_rtl.hrl @@ -0,0 +1,61 @@ +%% -*- 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% +%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% +%% Provides abstract datatypes for HiPE's RTL (Register Transfer Language). +%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%--------------------------------------------------------------------- + +-record(alu, {dst, src1, op, src2}). +-record(alub, {dst, src1, op, src2, 'cond', true_label, false_label, p}). +-record(branch, {src1, src2, 'cond', true_label, false_label, p}). +-record(call, {dstlist, 'fun', arglist, type, continuation, failcontinuation}). +-record(comment, {text}). +-record(enter, {'fun', arglist, type}). +-record(fconv, {dst, src}). +-record(fixnumop, {dst, src, type}). +-record(fload, {dst, src, offset}). +-record(fmove, {dst, src}). +-record(fp, {dst, src1, op, src2}). +-record(fp_unop, {dst, src, op}). +-record(fstore, {base, offset, src}). +-record(gctest, {words}). +-record(goto, {label}). +-record(goto_index, {block, index, labels}). +-record(label, {name}). +-record(load, {dst, src, offset, size, sign}). +-record(load_address, {dst, addr, type}). +-record(load_atom, {dst, atom}). +-record(load_word_index, {dst, block, index}). +-record(move, {dst, src}). +-record(multimove, {dstlist, srclist}). +-record(phi, {dst, id, arglist}). +-record(return, {varlist}). +-record(store, {base, offset, src, size}). +-record(switch, {src, labels, sorted_by=[]}). + +%%--------------------------------------------------------------------- + +%% An efficient macro to convert byte sizes to bit sizes +-define(bytes_to_bits(Bytes), ((Bytes) bsl 3)). % (N * 8) + +%%--------------------------------------------------------------------- diff --git a/lib/hipe/rtl/hipe_rtl_arch.erl b/lib/hipe/rtl/hipe_rtl_arch.erl new file mode 100644 index 0000000000..2afdf4eb6b --- /dev/null +++ b/lib/hipe/rtl/hipe_rtl_arch.erl @@ -0,0 +1,612 @@ +%% -*- erlang-indent-level: 2 -*- +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2001-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% +%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Copyright (c) 2001 by Erik Johansson. +%%===================================================================== +%% Filename : hipe_rtl_arch.erl +%% History : * 2001-04-10 Erik Johansson ([email protected]): Created. +%%===================================================================== +%% @doc +%% +%% This module contains interface functions whose semantics and +%% implementation depend on the target architecture. +%% +%% @end +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-module(hipe_rtl_arch). + +-export([first_virtual_reg/0, + heap_pointer/0, + heap_limit/0, + fcalls/0, + reg_name/1, + is_precoloured/1, + call_defined/0, + call_used/0, + tailcall_used/0, + return_used/0, + live_at_return/0, + endianess/0, + load_big_2/4, + load_little_2/4, + load_big_4/4, + load_little_4/4, + %% store_4/3, + eval_alu/3, + %% eval_alub/4, + eval_cond/3, + eval_cond_bits/5, + fwait/0, + handle_fp_exception/0, + pcb_load/2, + pcb_load/3, + pcb_store/2, + pcb_store/3, + pcb_address/2, + call_bif/5, + %% alignment/0, + nr_of_return_regs/0, + log2_word_size/0, + word_size/0 + ]). + +-include("hipe_literals.hrl"). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% ____________________________________________________________________ +%% +%% ARCH-specific stuff +%% ____________________________________________________________________ +%% +%% +%% XXX: x86 might not _have_ real registers for some of these things +%% + +first_virtual_reg() -> + case get(hipe_target_arch) of + ultrasparc -> + hipe_sparc_registers:first_virtual(); + powerpc -> + hipe_ppc_registers:first_virtual(); + arm -> + hipe_arm_registers:first_virtual(); + x86 -> + hipe_x86_registers:first_virtual(); + amd64 -> + hipe_amd64_registers:first_virtual() + end. + +heap_pointer() -> % {GetHPInsn, HPReg, PutHPInsn} + case get(hipe_target_arch) of + ultrasparc -> + heap_pointer_from_reg(hipe_sparc_registers:heap_pointer()); + powerpc -> + heap_pointer_from_reg(hipe_ppc_registers:heap_pointer()); + arm -> + heap_pointer_from_reg(hipe_arm_registers:heap_pointer()); + x86 -> + x86_heap_pointer(); + amd64 -> + amd64_heap_pointer() + end. + +heap_pointer_from_reg(Reg) -> + {hipe_rtl:mk_comment('get_heap_pointer'), + hipe_rtl:mk_reg(Reg), + hipe_rtl:mk_comment('put_heap_pointer')}. + +-ifdef(AMD64_HP_IN_REGISTER). +amd64_heap_pointer() -> + heap_pointer_from_reg(hipe_amd64_registers:heap_pointer()). +-else. +-define(HEAP_POINTER_FROM_PCB_NEEDED,1). +amd64_heap_pointer() -> + heap_pointer_from_pcb(). +-endif. + +-ifdef(X86_HP_IN_ESI). +x86_heap_pointer() -> + heap_pointer_from_reg(hipe_x86_registers:heap_pointer()). +-else. +-define(HEAP_POINTER_FROM_PCB_NEEDED,1). +x86_heap_pointer() -> + heap_pointer_from_pcb(). +-endif. + +-ifdef(HEAP_POINTER_FROM_PCB_NEEDED). +heap_pointer_from_pcb() -> + Reg = hipe_rtl:mk_new_reg(), + {pcb_load(Reg, ?P_HP), Reg, pcb_store(?P_HP, Reg)}. +-endif. + +heap_limit() -> % {GetHLIMITInsn, HLIMITReg} + case get(hipe_target_arch) of + ultrasparc -> + heap_limit_from_pcb(); + powerpc -> + heap_limit_from_pcb(); + arm -> + heap_limit_from_pcb(); + x86 -> + heap_limit_from_reg(hipe_x86_registers:heap_limit()); + amd64 -> + heap_limit_from_reg(hipe_amd64_registers:heap_limit()) + end. + +heap_limit_from_reg(Reg) -> + {hipe_rtl:mk_comment('get_heap_limit'), + hipe_rtl:mk_reg(Reg)}. + +heap_limit_from_pcb() -> + Reg = hipe_rtl:mk_new_reg(), + {pcb_load(Reg, ?P_HP_LIMIT), Reg}. + +fcalls() -> % {GetFCallsInsn, FCallsReg, PutFCallsInsn} + case get(hipe_target_arch) of + ultrasparc -> + fcalls_from_pcb(); + powerpc -> + fcalls_from_pcb(); + arm -> + fcalls_from_pcb(); + x86 -> + fcalls_from_reg(hipe_x86_registers:fcalls()); + amd64 -> + fcalls_from_reg(hipe_amd64_registers:fcalls()) + end. + +fcalls_from_reg(Reg) -> + {hipe_rtl:mk_comment('get_fcalls'), + hipe_rtl:mk_reg(Reg), + hipe_rtl:mk_comment('put_fcalls')}. + +fcalls_from_pcb() -> + Reg = hipe_rtl:mk_new_reg(), + {pcb_load(Reg, ?P_FCALLS), Reg, pcb_store(?P_FCALLS, Reg)}. + +reg_name(Reg) -> + case get(hipe_target_arch) of + ultrasparc -> + hipe_sparc_registers:reg_name_gpr(Reg); + powerpc -> + hipe_ppc_registers:reg_name_gpr(Reg); + arm -> + hipe_arm_registers:reg_name_gpr(Reg); + x86 -> + hipe_x86_registers:reg_name(Reg); + amd64 -> + hipe_amd64_registers:reg_name(Reg) + end. + +%% @spec is_precoloured(rtl_arg()) -> boolean() +%% +%% @doc Succeeds if Arg is mapped to a precoloured register in the target. +%% +is_precoloured(Arg) -> + case hipe_rtl:is_reg(Arg) of + true -> + is_precolored_regnum(hipe_rtl:reg_index(Arg)); + false -> + hipe_rtl:is_var(Arg) andalso + is_precolored_regnum(hipe_rtl:var_index(Arg)) + end. + +is_precolored_regnum(RegNum) -> + case get(hipe_target_arch) of + ultrasparc -> + hipe_sparc_registers:is_precoloured_gpr(RegNum); + powerpc -> + hipe_ppc_registers:is_precoloured_gpr(RegNum); + arm -> + hipe_arm_registers:is_precoloured_gpr(RegNum); + x86 -> + hipe_x86_registers:is_precoloured(RegNum); + amd64 -> + hipe_amd64_registers:is_precoloured(RegNum) + end. + +call_defined() -> + call_used(). + +call_used() -> + live_at_return(). + +tailcall_used() -> + call_used(). + +return_used() -> + tailcall_used(). + +live_at_return() -> + case get(hipe_target_arch) of + ultrasparc -> + ordsets:from_list([hipe_rtl:mk_reg(R) + || {R,_} <- hipe_sparc_registers:live_at_return()]); + powerpc -> + ordsets:from_list([hipe_rtl:mk_reg(R) + || {R,_} <- hipe_ppc_registers:live_at_return()]); + arm -> + ordsets:from_list([hipe_rtl:mk_reg(R) + || {R,_} <- hipe_arm_registers:live_at_return()]); + x86 -> + ordsets:from_list([hipe_rtl:mk_reg(R) + || {R,_} <- hipe_x86_registers:live_at_return()]); + amd64 -> + ordsets:from_list([hipe_rtl:mk_reg(R) + || {R,_} <- hipe_amd64_registers:live_at_return()]) + end. + +%% @spec word_size() -> integer() +%% +%% @doc Returns the target's word size. +%% +word_size() -> + case get(hipe_target_arch) of + ultrasparc -> 4; + powerpc -> 4; + arm -> 4; + x86 -> 4; + amd64 -> 8 + end. + +%% alignment() -> +%% case get(hipe_target_arch) of +%% ultrasparc -> 4; +%% powerpc -> 4; +%% arm -> 4; +%% x86 -> 4; +%% amd64 -> 8 +%% end. + +%% @spec log2_word_size() -> integer() +%% +%% @doc Returns log2 of the target's word size. +%% +log2_word_size() -> + case get(hipe_target_arch) of + ultrasparc -> 2; + powerpc -> 2; + arm -> 2; + x86 -> 2; + amd64 -> 3 + end. + +%% @spec endianess() -> big | little +%% +%% @doc Returns the target's endianess. +%% +endianess() -> + case get(hipe_target_arch) of + ultrasparc -> big; + powerpc -> big; + x86 -> little; + amd64 -> little; + arm -> ?ARM_ENDIANESS + end. + +%%%------------------------------------------------------------------------ +%%% Reading integers from binaries, in various sizes and endianesses. +%%% Operand-sized alignment is NOT guaranteed, only byte alignment. +%%%------------------------------------------------------------------------ + +%%% Load a 2-byte big-endian integer from a binary. +%%% Increment Offset by 2. +load_big_2(Dst, Base, Offset, Signedness) -> + case get(hipe_target_arch) of + powerpc -> + load_2_directly(Dst, Base, Offset, Signedness); + %% Note: x86 could use a "load;xchgb" or "load;rol $8,<16-bit reg>" + %% sequence here. This has been implemented, but unfortunately didn't + %% make consistent improvements to our benchmarks. + _ -> + load_big_2_in_pieces(Dst, Base, Offset, Signedness) + end. + +%%% Load a 2-byte little-endian integer from a binary. +%%% Increment Offset by 2. +load_little_2(Dst, Base, Offset, Signedness) -> + case get(hipe_target_arch) of + x86 -> + load_2_directly(Dst, Base, Offset, Signedness); + powerpc -> + [hipe_rtl:mk_call([Dst], 'lhbrx', [Base,Offset], [], [], not_remote), + hipe_rtl:mk_alu(Offset, Offset, add, hipe_rtl:mk_imm(2)) | + case Signedness of + unsigned -> []; + signed -> [hipe_rtl:mk_call([Dst], 'extsh', [Dst], [], [], not_remote)] + end]; + _ -> + load_little_2_in_pieces(Dst, Base, Offset, Signedness) + end. + +load_2_directly(Dst, Base, Offset, Signedness) -> + [hipe_rtl:mk_load(Dst, Base, Offset, int16, Signedness), + hipe_rtl:mk_alu(Offset, Offset, add, hipe_rtl:mk_imm(2))]. + +load_big_2_in_pieces(Dst, Base, Offset, Signedness) -> + Tmp1 = hipe_rtl:mk_new_reg(), + [hipe_rtl:mk_load(Dst, Base, Offset, byte, Signedness), + hipe_rtl:mk_alu(Offset, Offset, add, hipe_rtl:mk_imm(1)), + hipe_rtl:mk_alu(Dst, Dst, sll, hipe_rtl:mk_imm(8)), + hipe_rtl:mk_load(Tmp1, Base, Offset, byte, unsigned), + hipe_rtl:mk_alu(Dst, Dst, 'or', Tmp1), + hipe_rtl:mk_alu(Offset, Offset, add, hipe_rtl:mk_imm(1))]. + +load_little_2_in_pieces(Dst, Base, Offset, Signedness) -> + Tmp1 = hipe_rtl:mk_new_reg(), + [hipe_rtl:mk_load(Dst, Base, Offset, byte, unsigned), + hipe_rtl:mk_alu(Offset, Offset, add, hipe_rtl:mk_imm(1)), + hipe_rtl:mk_load(Tmp1, Base, Offset, byte, Signedness), + hipe_rtl:mk_alu(Tmp1, Tmp1, sll, hipe_rtl:mk_imm(8)), + hipe_rtl:mk_alu(Dst, Dst, 'or', Tmp1), + hipe_rtl:mk_alu(Offset, Offset, add, hipe_rtl:mk_imm(1))]. + +%%% Load a 4-byte big-endian integer from a binary. +%%% Increment Offset by 4. +load_big_4(Dst, Base, Offset, Signedness) -> + case get(hipe_target_arch) of + powerpc -> + load_4_directly(Dst, Base, Offset, Signedness); + %% Note: x86 could use a "load;bswap" sequence here. + %% This has been implemented, but unfortunately didn't + %% make any noticeable improvements in our benchmarks. + arm -> + %% When loading 4 bytes into a 32-bit register, the + %% signedness of the high-order byte doesn't matter. + %% ARM prefers unsigned byte loads so we'll use that. + load_big_4_in_pieces(Dst, Base, Offset, unsigned); + _ -> + load_big_4_in_pieces(Dst, Base, Offset, Signedness) + end. + +%%% Load a 4-byte little-endian integer from a binary. +%%% Increment Offset by 4. +load_little_4(Dst, Base, Offset, Signedness) -> + case get(hipe_target_arch) of + x86 -> + load_4_directly(Dst, Base, Offset, Signedness); + powerpc -> + [hipe_rtl:mk_call([Dst], 'lwbrx', [Base,Offset], [], [], not_remote), + hipe_rtl:mk_alu(Offset, Offset, add, hipe_rtl:mk_imm(4))]; + arm -> + %% When loading 4 bytes into a 32-bit register, the + %% signedness of the high-order byte doesn't matter. + %% ARM prefers unsigned byte loads so we'll use that. + load_little_4_in_pieces(Dst, Base, Offset, unsigned); + _ -> + load_little_4_in_pieces(Dst, Base, Offset, Signedness) + end. + +load_4_directly(Dst, Base, Offset, Signedness) -> + [hipe_rtl:mk_load(Dst, Base, Offset, word, Signedness), + hipe_rtl:mk_alu(Offset, Offset, add, hipe_rtl:mk_imm(4))]. + +load_big_4_in_pieces(Dst, Base, Offset, Signedness) -> + Tmp1 = hipe_rtl:mk_new_reg(), + [hipe_rtl:mk_load(Dst, Base, Offset, byte, Signedness), + hipe_rtl:mk_alu(Offset, Offset, add, hipe_rtl:mk_imm(1)), + hipe_rtl:mk_alu(Dst, Dst, sll, hipe_rtl:mk_imm(8)), + hipe_rtl:mk_load(Tmp1, Base, Offset, byte, unsigned), + hipe_rtl:mk_alu(Dst, Dst, 'or', Tmp1), + hipe_rtl:mk_alu(Offset, Offset, add, hipe_rtl:mk_imm(1)), + hipe_rtl:mk_alu(Dst, Dst, sll, hipe_rtl:mk_imm(8)), + hipe_rtl:mk_load(Tmp1, Base, Offset, byte, unsigned), + hipe_rtl:mk_alu(Dst, Dst, 'or', Tmp1), + hipe_rtl:mk_alu(Offset, Offset, add, hipe_rtl:mk_imm(1)), + hipe_rtl:mk_alu(Dst, Dst, sll, hipe_rtl:mk_imm(8)), + hipe_rtl:mk_load(Tmp1, Base, Offset, byte, unsigned), + hipe_rtl:mk_alu(Dst, Dst, 'or', Tmp1), + hipe_rtl:mk_alu(Offset, Offset, add, hipe_rtl:mk_imm(1))]. + +load_little_4_in_pieces(Dst, Base, Offset, Signedness) -> + Tmp1 = hipe_rtl:mk_new_reg(), + [hipe_rtl:mk_load(Dst, Base, Offset, byte, unsigned), + hipe_rtl:mk_alu(Offset, Offset, add, hipe_rtl:mk_imm(1)), + hipe_rtl:mk_load(Tmp1, Base, Offset, byte, unsigned), + hipe_rtl:mk_alu(Tmp1, Tmp1, sll, hipe_rtl:mk_imm(8)), + hipe_rtl:mk_alu(Dst, Dst, 'or', Tmp1), + hipe_rtl:mk_alu(Offset, Offset, add, hipe_rtl:mk_imm(1)), + hipe_rtl:mk_load(Tmp1, Base, Offset, byte, unsigned), + hipe_rtl:mk_alu(Tmp1, Tmp1, sll, hipe_rtl:mk_imm(16)), + hipe_rtl:mk_alu(Dst, Dst, 'or', Tmp1), + hipe_rtl:mk_alu(Offset, Offset, add, hipe_rtl:mk_imm(1)), + hipe_rtl:mk_load(Tmp1, Base, Offset, byte, Signedness), + hipe_rtl:mk_alu(Tmp1, Tmp1, sll, hipe_rtl:mk_imm(24)), + hipe_rtl:mk_alu(Dst, Dst, 'or', Tmp1), + hipe_rtl:mk_alu(Offset, Offset, add, hipe_rtl:mk_imm(1))]. + +-ifdef(STORE_4_NEEDED). +store_4(Base, Offset, Src) -> + case get(hipe_target_arch) of + x86 -> + store_4_directly(Base, Offset, Src); + powerpc -> + store_4_directly(Base, Offset, Src); + arm -> + store_big_4_in_pieces(Base, Offset, Src); + ultrasparc -> + store_big_4_in_pieces(Base, Offset, Src); + amd64 -> + store_4_directly(Base, Offset, Src) + end. + +store_4_directly(Base, Offset, Src) -> + [hipe_rtl:mk_store(Base, Offset, Src, int32), + hipe_rtl:mk_alu(Offset, Offset, add, hipe_rtl:mk_imm(4))]. + +store_big_4_in_pieces(Base, Offset, Src) -> + [hipe_rtl:mk_alu(Offset, Offset, add, hipe_rtl:mk_imm(3)), + hipe_rtl:mk_store(Base, Offset, Src, byte), + hipe_rtl:mk_alu(Offset, Offset, sub, hipe_rtl:mk_imm(1)), + hipe_rtl:mk_alu(Src, Src, srl, hipe_rtl:mk_imm(8)), + hipe_rtl:mk_store(Base, Offset, Src, byte), + hipe_rtl:mk_alu(Offset, Offset, sub, hipe_rtl:mk_imm(1)), + hipe_rtl:mk_alu(Src, Src, srl, hipe_rtl:mk_imm(8)), + hipe_rtl:mk_store(Base, Offset, Src, byte), + hipe_rtl:mk_alu(Offset, Offset, sub, hipe_rtl:mk_imm(1)), + hipe_rtl:mk_alu(Src, Src, srl, hipe_rtl:mk_imm(8)), + hipe_rtl:mk_store(Base, Offset, Src, byte), + hipe_rtl:mk_alu(Offset, Offset, add, hipe_rtl:mk_imm(4))]. +-endif. + +%%---------------------------------------------------------------------- +%% Handling of arithmetic -- depends on the size of word. +%%---------------------------------------------------------------------- + +eval_alu(Op, Arg1, Arg2) -> + %% io:format("Evaluated alu: ~w ~w ~w = ",[Arg1, Op, Arg2]), + Res = case word_size() of + 4 -> + hipe_rtl_arith_32:eval_alu(Op, Arg1, Arg2); + 8 -> + hipe_rtl_arith_64:eval_alu(Op, Arg1, Arg2) + end, + %% io:format("~w~n ",[Res]), + Res. + +-ifdef(EVAL_ALUB_NEEDED). +eval_alub(Op, Cond, Arg1, Arg2) -> + %% io:format("Evaluated alub: ~w ~w ~w cond ~w = ",[Arg1, Op, Arg2, Cond]), + Res = case word_size() of + 4 -> + hipe_rtl_arith_32:eval_alub(Op, Cond, Arg1, Arg2); + 8 -> + hipe_rtl_arith_64:eval_alub(Op, Cond, Arg1, Arg2) + end, + %% io:format("~w~n ",[Res]), + Res. +-endif. + +eval_cond(Cond, Arg1, Arg2) -> + %% io:format("Evaluated cond: ~w ~w ~w = ",[Arg1, Cond, Arg2]), + Res = case word_size() of + 4 -> + hipe_rtl_arith_32:eval_cond(Cond, Arg1, Arg2); + 8 -> + hipe_rtl_arith_64:eval_cond(Cond, Arg1, Arg2) + end, + %% io:format("~w~n ",[Res]), + Res. + +eval_cond_bits(Cond, N, Z, V, C) -> + %% io:format("Evaluated cond: ~w ~w ~w = ",[Arg1, Cond, Arg2]), + Res = case word_size() of + 4 -> + hipe_rtl_arith_32:eval_cond_bits(Cond, N, Z, V, C); + 8 -> + hipe_rtl_arith_64:eval_cond_bits(Cond, N, Z, V, C) + end, + %% io:format("~w~n ",[Res]), + Res. + +%%---------------------------------------------------------------------- + +fwait() -> + case get(hipe_target_arch) of + x86 -> [hipe_rtl:mk_call([], 'fwait', [], [], [], not_remote)]; + amd64 -> [hipe_rtl:mk_call([], 'fwait', [], [], [], not_remote)]; + arm -> []; + powerpc -> []; + ultrasparc -> [] + end. + +%% @spec handle_fp_exception() -> [term()] +%% +%% @doc +%% Returns RTL code to restore the FPU after a floating-point exception. +%% @end +handle_fp_exception() -> + case get(hipe_target_arch) of + x86 -> + ContLbl = hipe_rtl:mk_new_label(), + [hipe_rtl:mk_call([], handle_fp_exception, [], + hipe_rtl:label_name(ContLbl), [], not_remote), + ContLbl]; + amd64 -> + ContLbl = hipe_rtl:mk_new_label(), + [hipe_rtl:mk_call([], handle_fp_exception, [], + hipe_rtl:label_name(ContLbl), [], not_remote), + ContLbl]; + arm -> + []; + powerpc -> + []; + ultrasparc -> + [] + end. + +%% +%% PCB accesses. +%% Wrapped to avoid leaking the PCB pointer to the wrong places. +%% + +pcb_load(Dst, Off) -> pcb_load(Dst, Off, word). + +pcb_load(Dst, Off, Size) -> + hipe_rtl:mk_load(Dst, proc_pointer(), hipe_rtl:mk_imm(Off), Size, unsigned). + +pcb_store(Off, Src) -> pcb_store(Off, Src, word). + +pcb_store(Off, Src, Size) -> + hipe_rtl:mk_store(proc_pointer(), hipe_rtl:mk_imm(Off), Src, Size). + +pcb_address(Dst, Off) -> + hipe_rtl:mk_alu(Dst, proc_pointer(), 'add', hipe_rtl:mk_imm(Off)). + +proc_pointer() -> % must not be exported + case get(hipe_target_arch) of + ultrasparc -> + hipe_rtl:mk_reg_gcsafe(hipe_sparc_registers:proc_pointer()); + powerpc -> + hipe_rtl:mk_reg_gcsafe(hipe_ppc_registers:proc_pointer()); + arm -> + hipe_rtl:mk_reg_gcsafe(hipe_arm_registers:proc_pointer()); + x86 -> + hipe_rtl:mk_reg_gcsafe(hipe_x86_registers:proc_pointer()); + amd64 -> + hipe_rtl:mk_reg_gcsafe(hipe_amd64_registers:proc_pointer()) + end. + +%% +%% Special BIF calls. +%% Wrapped to avoid leaking the PCB pointer to the wrong places, +%% and to allow ARCH-specific expansion. +%% + +call_bif(Dst, Name, Args, Cont, Fail) -> + hipe_rtl:mk_call(Dst, Name, Args, Cont, Fail, not_remote). + +nr_of_return_regs() -> + case get(hipe_target_arch) of + ultrasparc -> + 1; + %% hipe_sparc_registers:nr_rets(); + powerpc -> + 1; + %% hipe_ppc_registers:nr_rets(); + arm -> + 1; + x86 -> + hipe_x86_registers:nr_rets(); + amd64 -> + 1 + %% hipe_amd64_registers:nr_rets(); + end. diff --git a/lib/hipe/rtl/hipe_rtl_arith.inc b/lib/hipe/rtl/hipe_rtl_arith.inc new file mode 100644 index 0000000000..31fedd927e --- /dev/null +++ b/lib/hipe/rtl/hipe_rtl_arith.inc @@ -0,0 +1,177 @@ +%% -*- Erlang -*- +%% -*- erlang-indent-level: 2 -*- +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2004-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% +%% +%%---------------------------------------------------------------------- +%% File : hipe_rtl_arith.inc +%% Created : Feb 2004 +%% Purpose : Implements arithmetic which is parameterized by the size +%% of the word of the target architecture (given as defines). +%%---------------------------------------------------------------------- + + +%% Returns a tuple +%% {Res, Sign, Zero, Overflow, Carry} +%% Res will be a number in the range +%% MAX_SIGNED_INT >= Res >= MIN_SIGNED_INT +%% The other four values are flags that are either true or false +%% +eval_alu(Op, Arg1, Arg2) + when Arg1 =< ?MAX_SIGNED_INT, + Arg1 >= ?MIN_SIGNED_INT, + Arg2 =< ?MAX_SIGNED_INT, + Arg2 >= ?MIN_SIGNED_INT -> + + Sign1 = sign_bit(Arg1), + Sign2 = sign_bit(Arg2), + + case Op of + 'sub' -> + Res = (Arg1 - Arg2) band ?WORDMASK, + N = sign_bit(Res), + Z = zero(Res), + V = (Sign1 and (not Sign2) and (not N)) + or + ((not Sign1) and Sign2 and N), + C = ((not Sign1) and Sign2) + or + (N and ((not Sign1) or Sign2)); + 'add' -> + Res = (Arg1 + Arg2) band ?WORDMASK, + N = sign_bit(Res), + Z = zero(Res), + V = (Sign1 and Sign2 and (not N)) + or + ((not Sign1) and (not Sign2) and N), + C = (Sign1 and Sign2) + or + ((not N) and (Sign1 or Sign2)); + 'mul' -> + FullRes = Arg1 * Arg2, + Res = FullRes band ?WORDMASK, + ResHi = FullRes bsr ?BITS, + N = sign_bit(Res), + Z = zero(Res), + V = (N and (ResHi =/= -1)) or ((not N) and (ResHi =/= 0)), + C = V; + 'sra' -> + Res = (Arg1 bsr Arg2) band ?WORDMASK, + N = sign_bit(Res), + Z = zero(Res), + V = 0, + C = 0; + 'srl' -> + Res = (Arg1 bsr Arg2) band shiftmask(Arg2), + N = sign_bit(Res), + Z = zero(Res), + V = 0, + C = 0; + 'sll' -> + Res = (Arg1 bsl Arg2) band ?WORDMASK, + N = sign_bit(Res), + Z = zero(Res), + V = 0, + C = 0; + 'or' -> + Res = (Arg1 bor Arg2) band ?WORDMASK, + N = sign_bit(Res), + Z = zero(Res), + V = 0, + C = 0; + 'and' -> + Res = (Arg1 band Arg2) band ?WORDMASK, + N = sign_bit(Res), + Z = zero(Res), + V = 0, + C = 0; + 'xor' -> + Res = (Arg1 bxor Arg2) band ?WORDMASK, + N = sign_bit(Res), + Z = zero(Res), + V = 0, + C = 0; + Op -> + Res = N = Z = V = C = 0, + ?EXIT({"unknown alu op", Op}) + end, + {two_comp_to_erl(Res), N, Z, V, C}; +eval_alu(Op, Arg1, Arg2) -> + ?EXIT({argument_overflow,Op,Arg1,Arg2}). + +%% Bj�rn & Bjarni: +%% We need to be able to do evaluations based only on the bits, since +%% there are cases where we can evaluate a subset of the bits, but can +%% not do a full eval-alub call (eg. a + 0 gives no carry) +%% +-spec eval_cond_bits(atom(), boolean(), boolean(), boolean(), boolean()) -> boolean(). + +eval_cond_bits(Cond, N, Z, V, C) -> + case Cond of + 'eq' -> + Z; + 'ne' -> + not Z; + 'gt' -> + not (Z or (N xor V)); + 'gtu' -> + not (C or Z); + 'ge' -> + not (N xor V); + 'geu'-> + not C; + 'lt' -> + N xor V; + 'ltu'-> + C; + 'le' -> + Z or (N xor V); + 'leu'-> + C or Z; + 'overflow' -> + V; + 'not_overflow' -> + not V; + _ -> + ?EXIT({'condition code not handled',Cond}) + end. + +eval_alub(Op, Cond, Arg1, Arg2) -> + {Res, N, Z, V, C} = eval_alu(Op, Arg1, Arg2), + {Res, eval_cond_bits(Cond, N, Z, V, C)}. + +eval_cond(Cond, Arg1, Arg2) -> + {_, Bool} = eval_alub('sub', Cond, Arg1, Arg2), + Bool. + +sign_bit(Val) -> + ((Val bsr ?SIGN_BIT) band 1) =:= 1. + +two_comp_to_erl(V) -> + if V > ?MAX_SIGNED_INT -> + - ((?MAX_UNSIGNED_INT + 1) - V); + true -> V + end. + +shiftmask(Arg) -> + Setbits = ?BITS - Arg, + (1 bsl Setbits) - 1. + +zero(Val) -> + Val =:= 0. + diff --git a/lib/hipe/rtl/hipe_rtl_arith_32.erl b/lib/hipe/rtl/hipe_rtl_arith_32.erl new file mode 100644 index 0000000000..a8a6043cda --- /dev/null +++ b/lib/hipe/rtl/hipe_rtl_arith_32.erl @@ -0,0 +1,50 @@ +%% -*- erlang-indent-level: 2 -*- +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2003-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% +%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Copyright (c) 2002 by Erik Johansson. +%% ==================================================================== +%% Filename : hipe_rtl_arith_32.erl +%% Module : hipe_rtl_arith_32 +%% Purpose : To implement 32-bit RTL-arithmetic +%% Notes : The arithmetic works on 32-bit signed integers. +%% The implementation is taken from the implementation +%% of arithmetic on SPARC. +%% XXX: This code is seldom used, and hence also +%% seldom tested. +%% Look here for strange bugs appearing when +%% turning on rtl_prop. +%% +%% History : * 2002-10-23 Erik Stenman ([email protected]): Created. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-module(hipe_rtl_arith_32). + +-export([eval_alu/3, eval_alub/4, eval_cond/3, eval_cond_bits/5]). + +-define(BITS, 32). +-define(SIGN_BIT, 31). +-define(WORDMASK, 16#ffffffff). +-define(MAX_SIGNED_INT, 16#7fffffff). +-define(MIN_SIGNED_INT, -16#80000000). +-define(MAX_UNSIGNED_INT, 16#ffffffff). + +-include("../main/hipe.hrl"). %% for ?EXIT + +-include("hipe_rtl_arith.inc"). diff --git a/lib/hipe/rtl/hipe_rtl_arith_64.erl b/lib/hipe/rtl/hipe_rtl_arith_64.erl new file mode 100644 index 0000000000..d0d576b65e --- /dev/null +++ b/lib/hipe/rtl/hipe_rtl_arith_64.erl @@ -0,0 +1,38 @@ +%% -*- erlang-indent-level: 2 -*- +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2004-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% +%% +%%---------------------------------------------------------------------- +%% File : hipe_rtl_arith_64.erl +%% Created : Feb 2004 +%% Purpose : Implements arithmetic for 64-bit target architectures. +%%---------------------------------------------------------------------- + +-module(hipe_rtl_arith_64). +-export([eval_alu/3, eval_alub/4, eval_cond/3, eval_cond_bits/5]). + +-define(BITS, 64). +-define(SIGN_BIT, 63). +-define(WORDMASK, 16#ffffffffffffffff). +-define(MAX_SIGNED_INT, 16#7fffffffffffffff). +-define(MIN_SIGNED_INT, -16#8000000000000000). +-define(MAX_UNSIGNED_INT,16#ffffffffffffffff). + +-include("../main/hipe.hrl"). %% for ?EXIT + +-include("hipe_rtl_arith.inc"). diff --git a/lib/hipe/rtl/hipe_rtl_binary.erl b/lib/hipe/rtl/hipe_rtl_binary.erl new file mode 100644 index 0000000000..5ea51acedb --- /dev/null +++ b/lib/hipe/rtl/hipe_rtl_binary.erl @@ -0,0 +1,80 @@ +%%% +%%% %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% +%%% +%%%------------------------------------------------------------------- +%%% File : hipe_rtl_binary_2.erl +%%% Author : Per Gustafsson <[email protected]> +%%% Description : +%%% +%%% Created : 5 Mar 2007 by Per Gustafsson <[email protected]> +%%%------------------------------------------------------------------- +-module(hipe_rtl_binary). + +-export([gen_rtl/7]). + +gen_rtl(BsOP, Dst, Args, TrueLblName, FalseLblName, SysLimName, ConstTab) -> + case type_of_operation(BsOP) of + match -> + {hipe_rtl_binary_match:gen_rtl( + BsOP, Dst, Args, TrueLblName, FalseLblName),ConstTab}; + construct -> + hipe_rtl_binary_construct:gen_rtl( + BsOP, Dst, Args, TrueLblName, FalseLblName, SysLimName, ConstTab) + end. + +type_of_operation({bs_start_match,_}) -> match; +type_of_operation({{bs_start_match,_},_}) -> match; +type_of_operation({bs_get_binary,_,_}) -> match; +type_of_operation({bs_get_binary_all,_,_}) -> match; +type_of_operation({bs_get_binary_all_2,_,_}) -> match; +type_of_operation({bs_get_integer,_,_}) -> match; +type_of_operation({bs_get_float,_,_}) -> match; +type_of_operation({bs_skip_bits,_}) -> match; +type_of_operation({bs_skip_bits_all,_,_}) -> match; +type_of_operation({bs_test_tail,_}) -> match; +type_of_operation({bs_restore,_}) -> match; +type_of_operation({bs_save,_}) -> match; +type_of_operation({bs_test_unit,_}) -> match; +type_of_operation({bs_match_string,_,_}) -> match; +type_of_operation(bs_context_to_binary) -> match; +type_of_operation({bs_add,_}) -> construct; +type_of_operation({bs_add,_,_}) -> construct; +type_of_operation(bs_bits_to_bytes) -> construct; +type_of_operation(bs_bits_to_bytes2) -> construct; +type_of_operation({bs_init,_}) -> construct; +type_of_operation({bs_init,_,_}) -> construct; +type_of_operation({bs_init_bits,_}) -> construct; +type_of_operation({bs_init_bits,_,_}) -> construct; +type_of_operation({bs_put_binary,_,_}) -> construct; +type_of_operation({bs_put_binary_all,_}) -> construct; +type_of_operation({bs_put_float,_,_,_}) -> construct; +type_of_operation({bs_put_integer,_,_,_}) -> construct; +type_of_operation({bs_put_string,_,_}) -> construct; +type_of_operation({unsafe_bs_put_integer,_,_,_}) -> construct; +type_of_operation(bs_utf8_size) -> construct; +type_of_operation(bs_put_utf8) -> construct; +type_of_operation(bs_get_utf8) -> match; +type_of_operation(bs_utf16_size) -> construct; +type_of_operation({bs_put_utf16,_}) -> construct; +type_of_operation({bs_get_utf16,_}) -> match; +type_of_operation(bs_validate_unicode) -> construct; +type_of_operation(bs_validate_unicode_retract) -> match; +type_of_operation(bs_final) -> construct; +type_of_operation({bs_append,_,_,_,_}) -> construct; +type_of_operation({bs_private_append,_,_}) -> construct; +type_of_operation(bs_init_writable) -> construct. diff --git a/lib/hipe/rtl/hipe_rtl_binary_construct.erl b/lib/hipe/rtl/hipe_rtl_binary_construct.erl new file mode 100644 index 0000000000..29993b9715 --- /dev/null +++ b/lib/hipe/rtl/hipe_rtl_binary_construct.erl @@ -0,0 +1,1363 @@ +%% -*- erlang-indent-level: 2 -*- +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2007-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_inline_bs_ops +%% Purpose : +%% Notes : +%% History : * 2001-06-14 Erik Johansson ([email protected]): Created. +%% ==================================================================== +%% Exports : +%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-module(hipe_rtl_binary_construct). +-export([gen_rtl/7]). +-import(hipe_tagscheme, [set_field_from_term/3, + get_field_from_term/3, + set_field_from_pointer/3, + get_field_from_pointer/3]). +%%------------------------------------------------------------------------- + +-include("../main/hipe.hrl"). +-include("hipe_rtl.hrl"). +-include("hipe_literals.hrl"). + +-define(BYTE_SHIFT, hipe_rtl:mk_imm(3)). %% Turn bits into bytes or vice versa +-define(LOW_BITS, hipe_rtl:mk_imm(7)). %% Three lowest bits set +-define(LOW_BITS_INT, 7). +-define(BYTE_SIZE, 8). +-define(MAX_BINSIZE, ((1 bsl ((hipe_rtl_arch:word_size()*?BYTE_SIZE)-3)) - 1)). + + +%% ------------------------------------------------------------------------- +%% The code is generated as a list of lists, it will be flattened later. +%% + +gen_rtl(BsOP, Dst, Args, TrueLblName, FalseLblName, SystemLimitLblName, ConstTab) -> + %%io:format("~w, ~w, ~w~n", [BsOP, Args, Dst]), + case BsOP of + {bs_put_string, String, SizeInBytes} -> + [NewOffset] = get_real(Dst), + [Base, Offset] = Args, + put_string(NewOffset, ConstTab, String, SizeInBytes, Base, Offset, + TrueLblName); + _ -> + Code = + case BsOP of + {bs_init, Size, _Flags} -> + [] = Args, + [Dst0, Base, Offset] = Dst, + case is_illegal_const(Size bsl 3) of + true -> + hipe_rtl:mk_goto(SystemLimitLblName); + false -> + const_init2(Size, Dst0, Base, Offset, TrueLblName) + end; + + {bs_init, _Flags} -> + [Size] = Args, + [Dst0, Base, Offset] = Dst, + var_init2(Size, Dst0, Base, Offset, TrueLblName, + SystemLimitLblName, FalseLblName); + + {bs_init_bits, Size, _Flags} -> + [] = Args, + [Dst0, Base, Offset] = Dst, + case is_illegal_const(Size) of + true -> + hipe_rtl:mk_goto(SystemLimitLblName); + false -> + const_init_bits(Size, Dst0, Base, Offset, TrueLblName) + end; + + {bs_init_bits, _Flags} -> + [Size] = Args, + [Dst0, Base, Offset] = Dst, + var_init_bits(Size, Dst0, Base, Offset, TrueLblName, + SystemLimitLblName, FalseLblName); + + {bs_put_binary_all, _Flags} -> + [Src, Base, Offset] = Args, + [NewOffset] = get_real(Dst), + put_binary_all(NewOffset, Src, Base, Offset, TrueLblName, FalseLblName); + + {bs_put_binary, Size, _Flags} -> + case is_illegal_const(Size) of + true -> + [hipe_rtl:mk_goto(FalseLblName)]; + false -> + [NewOffset] = get_real(Dst), + case Args of + [Src, Base, Offset] -> + put_static_binary(NewOffset, Src, Size, Base, Offset, + TrueLblName, FalseLblName); + [Src, Bits, Base, Offset] -> + {SizeCode, SizeReg} = make_size(Size, Bits, FalseLblName), + InCode = put_dynamic_binary(NewOffset, Src, SizeReg, Base, + Offset, TrueLblName, FalseLblName), + SizeCode ++ InCode + end + end; + + {bs_put_float, Size, Flags, ConstInfo} -> + [NewOffset] = get_real(Dst), + Aligned = aligned(Flags), + LittleEndian = littleendian(Flags), + case is_illegal_const(Size) of + true -> + [hipe_rtl:mk_goto(FalseLblName)]; + false -> + case Args of + [Src, Base, Offset] -> + CCode = static_float_c_code(NewOffset, Src, Base, Offset, Size, Flags, + TrueLblName, FalseLblName), + put_float(NewOffset, Src, Base, Offset, Size, CCode, Aligned, + LittleEndian, ConstInfo, TrueLblName); + [Src, Bits, Base, Offset] -> + {SizeCode, SizeReg} = make_size(Size, Bits, FalseLblName), + InCode = float_c_code(NewOffset, Src, Base, Offset, SizeReg, + Flags, TrueLblName, FalseLblName), + SizeCode ++ InCode + end + end; + + {bs_put_integer, Size, Flags, ConstInfo} -> + Aligned = aligned(Flags), + LittleEndian = littleendian(Flags), + [NewOffset] = get_real(Dst), + case is_illegal_const(Size) of + true -> + [hipe_rtl:mk_goto(FalseLblName)]; + false -> + case ConstInfo of + fail -> + [hipe_rtl:mk_goto(FalseLblName)]; + _ -> + case Args of + [Src, Base, Offset] -> + CCode = static_int_c_code(NewOffset, Src, + Base, Offset, Size, + Flags, TrueLblName, + FalseLblName), + put_static_int(NewOffset, Src, Base, Offset, Size, + CCode, Aligned, LittleEndian, TrueLblName); + [Src, Bits, Base, Offset] -> + {SizeCode, SizeReg} = make_size(Size, Bits, + FalseLblName), + CCode = int_c_code(NewOffset, Src, Base, + Offset, SizeReg, Flags, + TrueLblName, FalseLblName), + InCode = + put_dynamic_int(NewOffset, Src, Base, Offset, + SizeReg, CCode, Aligned, + LittleEndian, TrueLblName), + SizeCode ++ InCode + end + end + end; + + {unsafe_bs_put_integer, 0, _Flags, _ConstInfo} -> + [NewOffset] = get_real(Dst), + case Args of + [_Src, _Base, Offset] -> + [hipe_rtl:mk_move(NewOffset,Offset), + hipe_rtl:mk_goto(TrueLblName)]; + [_Src, _Bits, _Base, Offset] -> + [hipe_rtl:mk_move(NewOffset,Offset), + hipe_rtl:mk_goto(TrueLblName)] + end; + + {unsafe_bs_put_integer, Size, Flags, ConstInfo} -> + case is_illegal_const(Size) of + true -> + [hipe_rtl:mk_goto(FalseLblName)]; + false -> + Aligned = aligned(Flags), + LittleEndian = littleendian(Flags), + [NewOffset] = get_real(Dst), + case ConstInfo of + fail -> + [hipe_rtl:mk_goto(FalseLblName)]; + _ -> + case Args of + [Src, Base, Offset] -> + CCode = static_int_c_code(NewOffset, Src, + Base, Offset, Size, + Flags, TrueLblName, + FalseLblName), + put_unsafe_static_int(NewOffset, Src, Base, + Offset, Size, + CCode, Aligned, LittleEndian, + TrueLblName); + [Src, Bits, Base, Offset] -> + {SizeCode, SizeReg} = make_size(Size, Bits, + FalseLblName), + CCode = int_c_code(NewOffset, Src, Base, + Offset, SizeReg, Flags, + TrueLblName, FalseLblName), + InCode = + put_unsafe_dynamic_int(NewOffset, Src, Base, + Offset, SizeReg, CCode, + Aligned, LittleEndian, + TrueLblName), + SizeCode ++ InCode + end + end + end; + + bs_utf8_size -> + case Dst of + [_DstVar] -> + [_Arg] = Args, + [hipe_rtl:mk_call(Dst, bs_utf8_size, Args, + TrueLblName, [], not_remote)]; + [] -> + [hipe_rtl:mk_goto(TrueLblName)] + end; + + bs_put_utf8 -> + [_Src, _Base, _Offset] = Args, + NewDsts = get_real(Dst), + [hipe_rtl:mk_call(NewDsts, bs_put_utf8, Args, + TrueLblName, FalseLblName, not_remote)]; + + bs_utf16_size -> + case Dst of + [_DstVar] -> + [_Arg] = Args, + [hipe_rtl:mk_call(Dst, bs_utf16_size, Args, + TrueLblName, [], not_remote)]; + [] -> + [hipe_rtl:mk_goto(TrueLblName)] + end; + + {bs_put_utf16, Flags} -> + [_Src, _Base, _Offset] = Args, + NewDsts = get_real(Dst), + PrimOp = % workaround for bif/primop arity restrictions + case littleendian(Flags) of + false -> bs_put_utf16be; + true -> bs_put_utf16le + end, + [hipe_rtl:mk_call(NewDsts, PrimOp, Args, + TrueLblName, FalseLblName, not_remote)]; + + bs_validate_unicode -> + [_Arg] = Args, + [hipe_rtl:mk_call([], bs_validate_unicode, Args, + TrueLblName, FalseLblName, not_remote)]; + + bs_final -> + Zero = hipe_rtl:mk_imm(0), + [Src, Offset] = Args, + [BitSize, ByteSize] = create_regs(2), + [ShortLbl, LongLbl] = create_lbls(2), + case Dst of + [DstVar] -> + [hipe_rtl:mk_alub(BitSize, Offset, 'and', ?LOW_BITS, eq, + hipe_rtl:label_name(ShortLbl), + hipe_rtl:label_name(LongLbl)), ShortLbl, + hipe_rtl:mk_move(DstVar, Src), + hipe_rtl:mk_goto(TrueLblName), + LongLbl, + hipe_rtl:mk_alu(ByteSize, Offset, 'srl', ?BYTE_SHIFT), + hipe_tagscheme:mk_sub_binary(DstVar, ByteSize, + Zero, BitSize, Zero, Src), + hipe_rtl:mk_goto(TrueLblName)]; + [] -> + [hipe_rtl:mk_goto(TrueLblName)] + end; + + bs_init_writable -> + Zero = hipe_rtl:mk_imm(0), + [Size] = Args, + [DstVar] = Dst, + [SizeReg] = create_regs(1), + [Base] = create_unsafe_regs(1), + [hipe_rtl:mk_gctest(?PROC_BIN_WORDSIZE + ?SUB_BIN_WORDSIZE), + check_and_untag_fixnum(Size, SizeReg, FalseLblName), + allocate_writable(DstVar, Base, SizeReg, Zero, Zero), + hipe_rtl:mk_goto(TrueLblName)]; + + {bs_private_append, _U, _F} -> + [Size, Bin] = Args, + [DstVar, Base, Offset] = Dst, + [ProcBin] = create_vars(1), + [SubSize, SizeReg, EndSubSize, EndSubBitSize] = create_regs(4), + SubBinSize = {sub_binary, binsize}, + [get_field_from_term({sub_binary, orig}, Bin, ProcBin), + get_field_from_term(SubBinSize, Bin, SubSize), + check_and_untag_fixnum(Size, SizeReg, FalseLblName), + realloc_binary(SizeReg, ProcBin, Base), + calculate_sizes(Bin, SizeReg, Offset, EndSubSize, EndSubBitSize), + set_field_from_term(SubBinSize, Bin, EndSubSize), + set_field_from_term({sub_binary, bitsize}, Bin, EndSubBitSize), + hipe_rtl:mk_move(DstVar, Bin), + hipe_rtl:mk_goto(TrueLblName)]; + + {bs_append, _U, _F, _B, _Bla} -> + [Size, Bin] = Args, + [DstVar, Base, Offset] = Dst, + [ProcBin] = create_vars(1), + [Flags, SizeReg, IsWritable, EndSubSize, EndSubBitSize] = + create_regs(5), + [ContLbl,ContLbl2,ContLbl3,WritableLbl,NotWritableLbl] = Lbls = + create_lbls(5), + [ContLblName, ContLbl2Name, ContLbl3Name, Writable, NotWritable] = + [hipe_rtl:label_name(Lbl) || Lbl <- Lbls], + Zero = hipe_rtl:mk_imm(0), + SubIsWritable = {sub_binary, is_writable}, + [hipe_rtl:mk_gctest(?SUB_BIN_WORDSIZE + ?PROC_BIN_WORDSIZE), + check_and_untag_fixnum(Size, SizeReg, FalseLblName), + hipe_tagscheme:test_bitstr(Bin, ContLblName, FalseLblName, 0.99), + ContLbl, + hipe_tagscheme:test_subbinary(Bin,ContLbl2Name, NotWritable), + ContLbl2, + get_field_from_term(SubIsWritable, Bin, IsWritable), + hipe_rtl:mk_branch(IsWritable, 'ne', Zero, + ContLbl3Name, NotWritable), + ContLbl3, + get_field_from_term({sub_binary, orig}, Bin, ProcBin), + get_field_from_term({proc_bin, flags}, ProcBin, Flags), + hipe_rtl:mk_alub(Flags, Flags, 'and', + hipe_rtl:mk_imm(?PB_IS_WRITABLE), + eq, NotWritable, Writable, 0.01), + WritableLbl, + set_field_from_term(SubIsWritable, Bin, Zero), + realloc_binary(SizeReg, ProcBin, Base), + calculate_sizes(Bin, SizeReg, Offset, EndSubSize, EndSubBitSize), + hipe_tagscheme:mk_sub_binary(DstVar, EndSubSize, Zero, + EndSubBitSize, Zero, + hipe_rtl:mk_imm(1), ProcBin), + hipe_rtl:mk_goto(TrueLblName), + NotWritableLbl, + not_writable_code(Bin, SizeReg, DstVar, Base, Offset, + TrueLblName, FalseLblName)] + end, + {Code, ConstTab} + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% +%% Code that is used in the append and init writeable functions +%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +not_writable_code(Bin, SizeReg, Dst, Base, Offset, + TrueLblName, FalseLblName) -> + [SrcBase] = create_unsafe_regs(1), + [SrcOffset, SrcSize, TotSize, TotBytes, UsedBytes] = create_regs(5), + [IncLbl,AllLbl] = Lbls = create_lbls(2), + [IncLblName,AllLblName] = get_label_names(Lbls), + [get_base_offset_size(Bin, SrcBase, SrcOffset, SrcSize, FalseLblName), + hipe_rtl:mk_alu(TotSize, SrcSize, add, SizeReg), + hipe_rtl:mk_alu(TotBytes, TotSize, add, ?LOW_BITS), + hipe_rtl:mk_alu(TotBytes, TotBytes, srl, ?BYTE_SHIFT), + hipe_rtl:mk_alu(UsedBytes, TotBytes, sll, hipe_rtl:mk_imm(1)), + hipe_rtl:mk_branch(UsedBytes, ge, hipe_rtl:mk_imm(256), + AllLblName, IncLblName), + IncLbl, + hipe_rtl:mk_move(UsedBytes, hipe_rtl:mk_imm(256)), + AllLbl, + allocate_writable(Dst, Base, UsedBytes, TotBytes, TotSize), + put_binary_all(Offset, Bin, Base, hipe_rtl:mk_imm(0), + TrueLblName, FalseLblName)]. + +allocate_writable(Dst, Base, UsedBytes, TotBytes, TotSize) -> + Zero = hipe_rtl:mk_imm(0), + [NextLbl] = create_lbls(1), + [EndSubSize, EndSubBitSize, ProcBin] = create_regs(3), + [hipe_rtl:mk_call([Base], bs_allocate, [UsedBytes], + hipe_rtl:label_name(NextLbl), [], not_remote), + NextLbl, + hipe_tagscheme:create_refc_binary(Base, TotBytes, + hipe_rtl:mk_imm(?PB_IS_WRITABLE bor + ?PB_ACTIVE_WRITER), + ProcBin), + hipe_rtl:mk_alu(EndSubSize, TotSize, srl, ?BYTE_SHIFT), + hipe_rtl:mk_alu(EndSubBitSize, TotSize, 'and', ?LOW_BITS), + hipe_tagscheme:mk_sub_binary(Dst, EndSubSize, Zero, EndSubBitSize, + Zero, hipe_rtl:mk_imm(1), ProcBin)]. + +check_and_untag_fixnum(Size, SizeReg, FalseLblName) -> + [ContLbl,NextLbl] = Lbls = create_lbls(2), + [ContLblName,NextLblName] = get_label_names(Lbls), + [hipe_tagscheme:test_fixnum(Size, ContLblName, FalseLblName, 0.99), + ContLbl, + hipe_tagscheme:untag_fixnum(SizeReg,Size), + hipe_rtl:mk_branch(SizeReg, ge, hipe_rtl:mk_imm(0), NextLblName, + FalseLblName), + NextLbl]. + +realloc_binary(SizeReg, ProcBin, Base) -> + [NoReallocLbl, ReallocLbl, NextLbl, ContLbl] = Lbls = create_lbls(4), + [NoReallocLblName, ReallocLblName, NextLblName, ContLblName] = + [hipe_rtl:label_name(Lbl) || Lbl <- Lbls], + [PBSize, Tmp, ByteSize, NewSize, Flags, ResultingSize, OrigSize, + BinPointer] = create_regs(8), + ProcBinSizeTag = {proc_bin, binsize}, + ProcBinFlagsTag = {proc_bin, flags}, + ProcBinValTag = {proc_bin, val}, + ProcBinBytesTag = {proc_bin, bytes}, + BinOrigSizeTag = {binary, orig_size}, + [get_field_from_term(ProcBinSizeTag, ProcBin, PBSize), + hipe_rtl:mk_alu(Tmp, SizeReg, 'add', ?LOW_BITS), + hipe_rtl:mk_alu(ByteSize, Tmp, 'srl', ?BYTE_SHIFT), + hipe_rtl:mk_alu(ResultingSize, ByteSize, 'add', PBSize), + set_field_from_term(ProcBinSizeTag, ProcBin, ResultingSize), + get_field_from_term(ProcBinFlagsTag, ProcBin, Flags), + hipe_rtl:mk_alu(Flags, Flags, 'or', hipe_rtl:mk_imm(?PB_ACTIVE_WRITER)), + set_field_from_term(ProcBinFlagsTag, ProcBin, Flags), + get_field_from_term(ProcBinValTag, ProcBin, BinPointer), + get_field_from_pointer(BinOrigSizeTag, BinPointer, OrigSize), + hipe_rtl:mk_branch(OrigSize, 'lt', ResultingSize, + ReallocLblName, NoReallocLblName), + NoReallocLbl, + get_field_from_term(ProcBinBytesTag, ProcBin, Base), + hipe_rtl:mk_goto(ContLblName), + ReallocLbl, + hipe_rtl:mk_alu(NewSize, ResultingSize, 'sll', hipe_rtl:mk_imm(1)), + hipe_rtl:mk_call([BinPointer], bs_reallocate, [BinPointer, NewSize], + NextLblName, [], not_remote), + NextLbl, + set_field_from_pointer(BinOrigSizeTag, BinPointer, NewSize), + set_field_from_term(ProcBinValTag, ProcBin, BinPointer), + hipe_tagscheme:extract_binary_bytes(BinPointer, Base), + set_field_from_term(ProcBinBytesTag, ProcBin, Base), + ContLbl]. + +calculate_sizes(Bin, SizeReg, Offset, EndSubSize, EndSubBitSize) -> + [SubSize, SubBitSize, EndSize] = create_regs(3), + [get_field_from_term({sub_binary, binsize}, Bin, SubSize), + get_field_from_term({sub_binary, bitsize}, Bin, SubBitSize), + hipe_rtl:mk_alu(Offset, SubSize, 'sll', ?BYTE_SHIFT), + hipe_rtl:mk_alu(Offset, Offset, 'add', SubBitSize), + hipe_rtl:mk_alu(EndSize, Offset, 'add', SizeReg), + hipe_rtl:mk_alu(EndSubSize, EndSize, srl, ?BYTE_SHIFT), + hipe_rtl:mk_alu(EndSubBitSize, EndSize, 'and', ?LOW_BITS)]. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% +%% Code that is used to create calls to beam functions +%% +%% X_c_code/8, used for putting terms into binaries +%% +%% X_get_c_code/10, used for getting terms from binaries +%% +%% - gen_test_sideffect_bs_call/4 is used to make a C-call that might +%% fail but doesn't return an erlang value. +%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +static_float_c_code(NewOffset, Src, Base, Offset, Size, Flags, + TrueLblName, FalseLblName) -> + [SizeReg] = create_regs(1), + [hipe_rtl:mk_move(SizeReg, hipe_rtl:mk_imm(Size))| + float_c_code(NewOffset, Src, Base, Offset, SizeReg, Flags, + TrueLblName, FalseLblName)]. + +float_c_code(NewOffset, Src, Base, Offset, SizeReg, Flags, + TrueLblName, FalseLblName) -> + put_c_code(bs_put_small_float, NewOffset, Src, Base, Offset, SizeReg, + Flags, TrueLblName, FalseLblName). + +static_int_c_code(NewOffset, Src, Base, Offset, Size, Flags, + TrueLblName, FalseLblName) -> + [SizeReg] = create_regs(1), + [hipe_rtl:mk_move(SizeReg, hipe_rtl:mk_imm(Size))| + int_c_code(NewOffset, Src, Base, Offset, SizeReg, Flags, + TrueLblName, FalseLblName)]. + +int_c_code(NewOffset, Src, Base, Offset, SizeReg, Flags, + TrueLblName, FalseLblName) -> + put_c_code(bs_put_big_integer, NewOffset, Src, Base, Offset, SizeReg, + Flags, TrueLblName, FalseLblName). + +binary_c_code(NewOffset, Src, Base, Offset, Size, TrueLblName) -> + PassedLbl = hipe_rtl:mk_new_label(), + [SizeReg, FlagsReg] = create_regs(2), + [hipe_rtl:mk_move(FlagsReg, hipe_rtl:mk_imm(0)), + hipe_rtl:mk_move(SizeReg, Size), + hipe_rtl:mk_call([], bs_put_bits, [Src, SizeReg, Base, Offset, FlagsReg], + hipe_rtl:label_name(PassedLbl),[],not_remote), + PassedLbl, + hipe_rtl:mk_alu(NewOffset, Offset, add, SizeReg), + hipe_rtl:mk_goto(TrueLblName)]. + +put_c_code(Func, NewOffset, Src, Base, Offset, SizeReg, Flags, + TrueLblName, FalseLblName) -> + PassedLbl = hipe_rtl:mk_new_label(), + [FlagsReg] = create_regs(1), + [hipe_rtl:mk_move(FlagsReg, hipe_rtl:mk_imm(Flags)), + gen_test_sideffect_bs_call(Func, [Src, SizeReg, Base, Offset, FlagsReg], + hipe_rtl:label_name(PassedLbl), FalseLblName), + PassedLbl, + hipe_rtl:mk_alu(NewOffset, Offset, add, SizeReg), + hipe_rtl:mk_goto(TrueLblName)]. + +gen_test_sideffect_bs_call(Name, Args, TrueLblName, FalseLblName) -> + [Tmp1] = create_regs(1), + RetLbl = hipe_rtl:mk_new_label(), + [hipe_rtl:mk_call([Tmp1], Name, Args, + hipe_rtl:label_name(RetLbl), [], not_remote), + RetLbl, + hipe_rtl:mk_branch(Tmp1, eq, hipe_rtl:mk_imm(0), + FalseLblName, TrueLblName, 0.01)]. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% +%% Small utility functions: +%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +create_regs(X) when X > 0 -> + [hipe_rtl:mk_new_reg_gcsafe()|create_regs(X-1)]; +create_regs(0) -> + []. + +create_unsafe_regs(X) when X > 0 -> + [hipe_rtl:mk_new_reg()|create_unsafe_regs(X-1)]; +create_unsafe_regs(0) -> + []. + +create_vars(X) when X > 0 -> + [hipe_rtl:mk_new_var()|create_vars(X-1)]; +create_vars(0) -> + []. + +create_lbls(X) when X > 0 -> + [hipe_rtl:mk_new_label()|create_lbls(X-1)]; +create_lbls(0) -> + []. + +get_label_names(Lbls) -> + [hipe_rtl:label_name(Lbl) || Lbl <- Lbls]. + +aligned(Flags) -> + case Flags band ?BSF_ALIGNED of + 1 -> true; + 0 -> false + end. + +littleendian(Flags) -> + case Flags band 2 of + 2 -> true; + 0 -> false + end. + +is_illegal_const(Const) -> + Const >= (1 bsl (hipe_rtl_arch:word_size() * ?BYTE_SIZE)) orelse Const < 0. + +get_real(Dst) -> + case Dst of + [_NewOffset] -> Dst; + [] -> create_regs(1) + end. + +%%----------------------------------------------------------------------------- +%% Help functions implementing the bs operations in rtl code. +%% +%% The following functions are called from the translation switch: +%% +%% - put_string/7 creates code to copy a string to a binary +%% starting at base+offset and ending at base+newoffset +%% +%% - const_init2/6 initializes the creation of a binary of constant size +%% +%% - var_init2/6 initializes the creation of a binary of variable size +%% +%% - get_int_from_unaligned_bin/11 creates code to extract a fixed +%% size integer from a binary or makes a c-call if it does not +%% conform to some certain rules. +%% +%% - get_unknown_size_int/11 creates code to extract a variable size +%% byte-aligned integer from a binary or makes a c-call if it +%% does not conform to some certain rules. +%% +%% - skip_no_of_bits/5 creates code to skip a variable amount of bits +%% in a binary. +%% +%% - load_match_buffer/7 reloads the C-matchbuffer to RTL registers. +%% +%% - expand_runtime/4 creates code that calculates a maximal heap need +%% before a binary match +%%----------------------------------------------------------------------------- + +put_string(NewOffset, ConstTab, String, SizeInBytes, Base, Offset, TLName) -> + [StringBase] = create_regs(1), + {NewTab, Lbl} = hipe_consttab:insert_block(ConstTab, byte, String), + {[hipe_rtl:mk_load_address(StringBase, Lbl, constant)| + copy_string(StringBase, SizeInBytes, Base, Offset, + NewOffset, TLName)], + NewTab}. + +const_init2(Size, Dst, Base, Offset, TrueLblName) -> + Log2WordSize = hipe_rtl_arch:log2_word_size(), + WordSize = hipe_rtl_arch:word_size(), + NextLbl = hipe_rtl:mk_new_label(), + case Size =< ?MAX_HEAP_BIN_SIZE of + true -> + [hipe_rtl:mk_gctest(((Size + 3*WordSize-1) bsr Log2WordSize)+?SUB_BIN_WORDSIZE), + hipe_tagscheme:create_heap_binary(Base, Size, Dst), + hipe_rtl:mk_move(Offset, hipe_rtl:mk_imm(0)), + hipe_rtl:mk_goto(TrueLblName)]; + false -> + ByteSize = hipe_rtl:mk_new_reg(), + [hipe_rtl:mk_gctest(?PROC_BIN_WORDSIZE+?SUB_BIN_WORDSIZE), + hipe_rtl:mk_move(Offset, hipe_rtl:mk_imm(0)), + hipe_rtl:mk_move(ByteSize, hipe_rtl:mk_imm(Size)), + hipe_rtl:mk_call([Base], bs_allocate, [ByteSize], + hipe_rtl:label_name(NextLbl), [], not_remote), + NextLbl, + hipe_tagscheme:create_refc_binary(Base, ByteSize, Dst), + hipe_rtl:mk_goto(TrueLblName)] + end. + +const_init_bits(Size, Dst, Base, Offset, TrueLblName) -> + Log2WordSize = hipe_rtl_arch:log2_word_size(), + WordSize = hipe_rtl_arch:word_size(), + [NextLbl] = create_lbls(1), + TmpDst = hipe_rtl:mk_new_var(), + Zero = hipe_rtl:mk_imm(0), + {ExtraSpace, SubBinCode} = + if (Size rem ?BYTE_SIZE) =:= 0 -> + {0,[hipe_rtl:mk_move(Dst, TmpDst)]}; + true -> + {?SUB_BIN_WORDSIZE, + hipe_tagscheme:mk_sub_binary(Dst, hipe_rtl:mk_imm(Size bsr 3), Zero, + hipe_rtl:mk_imm(Size band ?LOW_BITS_INT), + Zero, TmpDst)} + end, + BaseBinCode = + if Size =< (?MAX_HEAP_BIN_SIZE * 8) -> + ByteSize = (Size + 7) div 8, + [hipe_rtl:mk_gctest(((ByteSize+ 3*WordSize-1) bsr Log2WordSize)+ ExtraSpace), + hipe_tagscheme:create_heap_binary(Base, ByteSize, TmpDst), + hipe_rtl:mk_move(Offset, Zero)]; + true -> + ByteSize = hipe_rtl:mk_new_reg(), + [hipe_rtl:mk_gctest(?PROC_BIN_WORDSIZE+ExtraSpace), + hipe_rtl:mk_move(Offset, Zero), + hipe_rtl:mk_move(ByteSize, hipe_rtl:mk_imm((Size+7) bsr 3)), + hipe_rtl:mk_call([Base], bs_allocate, [ByteSize], + hipe_rtl:label_name(NextLbl),[],not_remote), + NextLbl, + hipe_tagscheme:create_refc_binary(Base, ByteSize, TmpDst)] + end, + [BaseBinCode, SubBinCode, hipe_rtl:mk_goto(TrueLblName)]. + +var_init2(Size, Dst, Base, Offset, TrueLblName, SystemLimitLblName, FalseLblName) -> + Log2WordSize = hipe_rtl_arch:log2_word_size(), + WordSize = hipe_rtl_arch:word_size(), + [ContLbl,HeapLbl,REFCLbl,NextLbl] = create_lbls(4), + [USize,Tmp] = create_unsafe_regs(2), + [get_32_bit_value(Size, USize, SystemLimitLblName, FalseLblName), + hipe_rtl:mk_branch(USize, le, hipe_rtl:mk_imm(?MAX_BINSIZE), + hipe_rtl:label_name(ContLbl), + SystemLimitLblName), + ContLbl, + hipe_rtl:mk_move(Offset, hipe_rtl:mk_imm(0)), + hipe_rtl:mk_branch(USize, le, hipe_rtl:mk_imm(?MAX_HEAP_BIN_SIZE), + hipe_rtl:label_name(HeapLbl), + hipe_rtl:label_name(REFCLbl)), + HeapLbl, + hipe_rtl:mk_alu(Tmp, USize, add, hipe_rtl:mk_imm(3*WordSize-1)), + hipe_rtl:mk_alu(Tmp, Tmp, srl, hipe_rtl:mk_imm(Log2WordSize)), + hipe_rtl:mk_alu(Tmp, Tmp, add, hipe_rtl:mk_imm(?SUB_BIN_WORDSIZE)), + hipe_rtl:mk_gctest(Tmp), + hipe_tagscheme:create_heap_binary(Base, USize, Dst), + hipe_rtl:mk_goto(TrueLblName), + REFCLbl, + hipe_rtl:mk_gctest(?PROC_BIN_WORDSIZE+?SUB_BIN_WORDSIZE), + hipe_rtl:mk_call([Base], bs_allocate, [USize], + hipe_rtl:label_name(NextLbl), [], not_remote), + NextLbl, + hipe_tagscheme:create_refc_binary(Base, USize, Dst), + hipe_rtl:mk_goto(TrueLblName)]. + +var_init_bits(Size, Dst, Base, Offset, TrueLblName, SystemLimitLblName, FalseLblName) -> + [HeapLbl,REFCLbl,NextLbl,NoSubLbl,SubLbl,ContLbl, + NoCreateSubBin, CreateSubBin, JoinLbl, JoinLbl2] = create_lbls(10), + [USize,ByteSize,TotByteSize,OffsetBits] = create_regs(4), + [TmpDst] = create_unsafe_regs(1), + Log2WordSize = hipe_rtl_arch:log2_word_size(), + WordSize = hipe_rtl_arch:word_size(), + MaximumWords = + erlang:max((?MAX_HEAP_BIN_SIZE + 3*WordSize) bsr Log2WordSize, + ?PROC_BIN_WORDSIZE) + ?SUB_BIN_WORDSIZE, + Zero = hipe_rtl:mk_imm(0), + [hipe_rtl:mk_gctest(MaximumWords), + get_32_bit_value(Size, USize, SystemLimitLblName, FalseLblName), + hipe_rtl:mk_alu(ByteSize, USize, srl, ?BYTE_SHIFT), + hipe_rtl:mk_alub(OffsetBits, USize, 'and', ?LOW_BITS, eq, + hipe_rtl:label_name(NoSubLbl), + hipe_rtl:label_name(SubLbl)), + NoSubLbl, + hipe_rtl:mk_move(TotByteSize, ByteSize), + hipe_rtl:mk_goto(hipe_rtl:label_name(JoinLbl)), + SubLbl, + hipe_rtl:mk_alu(TotByteSize, ByteSize, 'add', hipe_rtl:mk_imm(1)), + JoinLbl, + hipe_rtl:mk_branch(USize, le, hipe_rtl:mk_imm(?MAX_BINSIZE), + hipe_rtl:label_name(ContLbl), + SystemLimitLblName), + ContLbl, + hipe_rtl:mk_branch(TotByteSize, 'le', hipe_rtl:mk_imm(?MAX_HEAP_BIN_SIZE), + hipe_rtl:label_name(HeapLbl), + hipe_rtl:label_name(REFCLbl)), + HeapLbl, + hipe_tagscheme:create_heap_binary(Base, TotByteSize, TmpDst), + hipe_rtl:mk_goto(hipe_rtl:label_name(JoinLbl2)), + REFCLbl, + hipe_rtl:mk_call([Base], bs_allocate, [TotByteSize], + hipe_rtl:label_name(NextLbl),[],not_remote), + NextLbl, + hipe_tagscheme:create_refc_binary(Base, TotByteSize, TmpDst), + JoinLbl2, + hipe_rtl:mk_move(Offset, Zero), + hipe_rtl:mk_branch(OffsetBits, 'eq', Zero, + hipe_rtl:label_name(NoCreateSubBin), + hipe_rtl:label_name(CreateSubBin)), + CreateSubBin, + hipe_tagscheme:mk_sub_binary(Dst, ByteSize, Zero, OffsetBits, Zero, TmpDst), + hipe_rtl:mk_goto(TrueLblName), + NoCreateSubBin, + hipe_rtl:mk_move(Dst, TmpDst), + hipe_rtl:mk_goto(TrueLblName)]. + +put_binary_all(NewOffset, Src, Base, Offset, TLName, FLName) -> + [SrcBase,SrcOffset,NumBits] = create_regs(3), + CCode = binary_c_code(NewOffset, Src, Base, Offset, NumBits, TLName), + AlignedCode = copy_aligned_bytes(SrcBase, SrcOffset, NumBits, Base, Offset, + NewOffset, TLName), + get_base_offset_size(Src, SrcBase, SrcOffset, NumBits,FLName) ++ + test_alignment(SrcOffset, NumBits, Offset, AlignedCode, CCode). + +test_alignment(SrcOffset, NumBits, Offset, AlignedCode, CCode) -> + [Tmp] = create_regs(1), + [AlignedLbl,CLbl] = create_lbls(2), + [hipe_rtl:mk_alu(Tmp, SrcOffset, 'or', NumBits), + hipe_rtl:mk_alu(Tmp, Tmp, 'or', Offset), + hipe_rtl:mk_alub(Tmp, Tmp, 'and', ?LOW_BITS, 'eq', + hipe_rtl:label_name(AlignedLbl), + hipe_rtl:label_name(CLbl)), + AlignedLbl, + AlignedCode, + CLbl, + CCode]. + +put_static_binary(NewOffset, Src, Size, Base, Offset, TLName, FLName) -> + [SrcBase] = create_unsafe_regs(1), + [SrcOffset, SrcSize] = create_regs(2), + case Size of + 0 -> + get_base_offset_size(Src, SrcBase, SrcOffset, SrcSize, FLName) ++ + [hipe_rtl:mk_move(NewOffset, Offset), + hipe_rtl:mk_goto(TLName)]; + _ -> + SizeImm = hipe_rtl:mk_imm(Size), + CCode = binary_c_code(NewOffset, Src, Base, Offset, SizeImm, TLName), + AlignedCode = copy_aligned_bytes(SrcBase, SrcOffset, SizeImm, Base, + Offset, NewOffset, TLName), + get_base_offset_size(Src, SrcBase, SrcOffset, SrcSize, FLName) ++ + small_check(SizeImm, SrcSize, FLName) ++ + test_alignment(SrcOffset, SizeImm, Offset, AlignedCode, CCode) + end. + +put_dynamic_binary(NewOffset, Src, SizeReg, Base, Offset, TLName, FLName) -> + [SrcBase] = create_unsafe_regs(1), + [SrcOffset, SrcSize] = create_regs(2), + CCode = binary_c_code(NewOffset, Src, Base, Offset, SizeReg, TLName), + AlignedCode = copy_aligned_bytes(SrcBase, SrcOffset, SizeReg, Base, Offset, + NewOffset, TLName), + get_base_offset_size(Src, SrcBase, SrcOffset, SrcSize, FLName) ++ + small_check(SizeReg, SrcSize, FLName) ++ + test_alignment(SrcOffset, SizeReg, Offset, AlignedCode, CCode). + +put_float(NewOffset, Src, Base, Offset, 64, CCode, Aligned, LittleEndian, + ConstInfo, TrueLblName) -> + [CLbl] = create_lbls(1), + case {Aligned, LittleEndian} of + {true, false} -> + copy_float_big(Base, Offset, NewOffset, Src, + hipe_rtl:label_name(CLbl), TrueLblName, ConstInfo) ++ + [CLbl|CCode]; + {true, true} -> + copy_float_little(Base, Offset, NewOffset, Src, + hipe_rtl:label_name(CLbl), TrueLblName, ConstInfo) ++ + [CLbl|CCode]; + {false, _} -> + CCode + end; +put_float(_NewOffset, _Src, _Base, _Offset, _Size, CCode, _Aligned, + _LittleEndian, _ConstInfo, _TrueLblName) -> + CCode. + +put_static_int(NewOffset, Src, Base, Offset, Size, CCode, Aligned, + LittleEndian, TrueLblName) -> + {Init, End, UntaggedSrc} = make_init_end(Src, CCode, TrueLblName), + case {Aligned, LittleEndian} of + {true, true} -> + Init ++ + copy_int_little(Base, Offset, NewOffset, Size, UntaggedSrc) ++ + End; + {true, false} -> + Init ++ + copy_int_big(Base, Offset, NewOffset, Size, UntaggedSrc) ++ + End; + {false, true} -> + CCode; + {false, false} -> + Init ++ + copy_offset_int_big(Base, Offset, NewOffset, Size, UntaggedSrc) ++ + End + end. + +put_unsafe_static_int(NewOffset, Src, Base, Offset, Size, CCode, Aligned, + LittleEndian, TrueLblName) -> + {Init, End, UntaggedSrc} = make_init_end(Src, TrueLblName), + case {Aligned, LittleEndian} of + {true, true} -> + Init ++ + copy_int_little(Base, Offset, NewOffset, Size, UntaggedSrc) ++ + End; + {true, false} -> + Init ++ + copy_int_big(Base, Offset, NewOffset, Size, UntaggedSrc) ++ + End; + {false, true} -> + CCode; + {false, false} -> + Init ++ + copy_offset_int_big(Base, Offset, NewOffset, Size, UntaggedSrc) ++ + End + end. + +put_dynamic_int(NewOffset, Src, Base, Offset, SizeReg, CCode, Aligned, + LittleEndian, TrueLblName) -> + {Init, End, UntaggedSrc} = make_init_end(Src, CCode, TrueLblName), + case Aligned of + true -> + case LittleEndian of + true -> + Init ++ + copy_int_little(Base, Offset, NewOffset, SizeReg, UntaggedSrc) ++ + End; + false -> + Init ++ + copy_int_big(Base, Offset, NewOffset, SizeReg, UntaggedSrc) ++ + End + end; + false -> + CCode + end. + +put_unsafe_dynamic_int(NewOffset, Src, Base, Offset, SizeReg, CCode, Aligned, + LittleEndian, TrueLblName) -> + {Init, End, UntaggedSrc} = make_init_end(Src, TrueLblName), + case Aligned of + true -> + case LittleEndian of + true -> + Init ++ + copy_int_little(Base, Offset, NewOffset, SizeReg, UntaggedSrc) ++ + End; + false -> + Init ++ + copy_int_big(Base, Offset, NewOffset, SizeReg, UntaggedSrc) ++ + End + end; + false -> + CCode + end. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% +%% Help functions used by the above +%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +make_init_end(Src, CCode, TrueLblName) -> + [CLbl, SuccessLbl] = create_lbls(2), + [UntaggedSrc] = create_regs(1), + Init = [hipe_tagscheme:test_fixnum(Src, hipe_rtl:label_name(SuccessLbl), + hipe_rtl:label_name(CLbl), 0.99), + SuccessLbl, + hipe_tagscheme:untag_fixnum(UntaggedSrc,Src)], + End = [hipe_rtl:mk_goto(TrueLblName), CLbl| CCode], + {Init, End, UntaggedSrc}. + +make_init_end(Src, TrueLblName) -> + [UntaggedSrc] = create_regs(1), + Init = [hipe_tagscheme:untag_fixnum(UntaggedSrc,Src)], + End = [hipe_rtl:mk_goto(TrueLblName)], + {Init, End, UntaggedSrc}. + +get_base_offset_size(Binary, SrcBase, SrcOffset, SrcSize, FLName) -> + [JoinLbl, EndLbl, SuccessLbl, SubLbl, OtherLbl, HeapLbl, REFCLbl] = + Lbls = create_lbls(7), + [JoinLblName, EndLblName, SuccessLblName, SubLblName, + OtherLblName, HeapLblName, REFCLblName] = get_label_names(Lbls), + [BitSize,BitOffset] = create_regs(2), + [Orig] = create_vars(1), + [hipe_tagscheme:test_bitstr(Binary, SuccessLblName, FLName, 0.99), + SuccessLbl, + get_field_from_term({sub_binary,binsize}, Binary, SrcSize), + hipe_rtl:mk_alu(SrcSize, SrcSize, sll, ?BYTE_SHIFT), + hipe_tagscheme:test_subbinary(Binary, SubLblName, OtherLblName), + SubLbl, + get_field_from_term({sub_binary,bitsize}, Binary, BitSize), + get_field_from_term({sub_binary,offset}, Binary, SrcOffset), + hipe_rtl:mk_alu(SrcSize, SrcSize, add, BitSize), + get_field_from_term({sub_binary,bitoffset}, Binary, BitOffset), + hipe_rtl:mk_alu(SrcOffset, SrcOffset, sll, ?BYTE_SHIFT), + hipe_rtl:mk_alu(SrcOffset, SrcOffset, add, BitOffset), + get_field_from_term({sub_binary,orig}, Binary, Orig), + hipe_rtl:mk_goto(JoinLblName), + OtherLbl, + hipe_rtl:mk_move(SrcOffset, hipe_rtl:mk_imm(0)), + hipe_rtl:mk_move(Orig, Binary), + JoinLbl, + hipe_tagscheme:test_heap_binary(Orig, HeapLblName, REFCLblName), + HeapLbl, + hipe_rtl:mk_alu(SrcBase, Orig, add, hipe_rtl:mk_imm(?HEAP_BIN_DATA-2)), + hipe_rtl:mk_goto(EndLblName), + REFCLbl, + get_field_from_term({proc_bin,bytes}, Orig, SrcBase), + EndLbl]. + +copy_aligned_bytes(CopyBase, CopyOffset, Size, Base, Offset, NewOffset, TrueLblName) -> + [BaseDst, BaseSrc] = create_unsafe_regs(2), + [Iter, Extra, BothOffset] = create_regs(3), + initializations(BaseSrc, BaseDst, BothOffset, CopyOffset, Offset, CopyBase, Base) ++ + [hipe_rtl:mk_alu(Extra, Size, 'and', ?LOW_BITS), + hipe_rtl:mk_alu(Iter, Size, srl, ?BYTE_SHIFT), + hipe_rtl:mk_alu(NewOffset, Offset, 'add', Size)] ++ + easy_loop(BaseSrc, BaseDst, BothOffset, Iter, Extra, TrueLblName). + +copy_string(StringBase, StringSize, BinBase, BinOffset, NewOffset, TrueLblName) -> + [TmpOffset,BothOffset,InitOffs] = create_regs(3), + [NewBinBase] = create_unsafe_regs(1), + [EasyLbl,HardLbl] = create_lbls(2), + [hipe_rtl:mk_alu(TmpOffset, BinOffset, srl, ?BYTE_SHIFT), + hipe_rtl:mk_alu(NewBinBase, BinBase, add, TmpOffset), + hipe_rtl:mk_move(BothOffset, hipe_rtl:mk_imm(0)), + hipe_rtl:mk_alub(InitOffs, BinOffset, 'and', ?LOW_BITS, eq, + hipe_rtl:label_name(EasyLbl), hipe_rtl:label_name(HardLbl)), + EasyLbl, + hipe_rtl:mk_alu(NewOffset, BinOffset, add, + hipe_rtl:mk_imm(?bytes_to_bits(StringSize)))] ++ + easy_loop(StringBase, NewBinBase, BothOffset, + hipe_rtl:mk_imm(StringSize), hipe_rtl:mk_imm(0), TrueLblName) ++ + [HardLbl, + hipe_rtl:mk_alu(NewOffset, BinOffset, add, + hipe_rtl:mk_imm(?bytes_to_bits(StringSize)))] ++ + hard_loop(StringBase, NewBinBase, BothOffset, hipe_rtl:mk_imm(StringSize), + InitOffs, TrueLblName). + +small_check(SizeVar, CopySize, FalseLblName) -> + SuccessLbl = hipe_rtl:mk_new_label(), + [hipe_rtl:mk_branch(SizeVar, le, CopySize, + hipe_rtl:label_name(SuccessLbl), FalseLblName), + SuccessLbl]. + +easy_loop(BaseSrc, BaseDst, BothOffset, Iterations, Extra, TrueLblName) -> + [Tmp1,Shift] = create_regs(2), + [LoopLbl,TopLbl,EndLbl,ExtraLbl] = create_lbls(4), + [TopLbl, + hipe_rtl:mk_branch(BothOffset, ne, Iterations, hipe_rtl:label_name(LoopLbl), + hipe_rtl:label_name(EndLbl), 0.99), + LoopLbl, + hipe_rtl:mk_load(Tmp1, BaseSrc, BothOffset, byte, unsigned), + hipe_rtl:mk_store(BaseDst, BothOffset, Tmp1, byte), + hipe_rtl:mk_alu(BothOffset, BothOffset, add, hipe_rtl:mk_imm(1)), + hipe_rtl:mk_goto(hipe_rtl:label_name(TopLbl)), + EndLbl, + hipe_rtl:mk_branch(Extra, eq, hipe_rtl:mk_imm(0), TrueLblName, + hipe_rtl:label_name(ExtraLbl)), + ExtraLbl, + hipe_rtl:mk_load(Tmp1, BaseSrc, BothOffset, byte, unsigned), + hipe_rtl:mk_alu(Shift, hipe_rtl:mk_imm(?BYTE_SIZE), sub, Extra), + hipe_rtl:mk_alu(Tmp1, Tmp1, srl, Shift), + hipe_rtl:mk_alu(Tmp1, Tmp1, sll, Shift), + hipe_rtl:mk_store(BaseDst, BothOffset, Tmp1, byte), + hipe_rtl:mk_goto(TrueLblName)]. + +hard_loop(BaseSrc, BaseDst, BothOffset, Iterations, + InitOffset, TrueLblName) -> + [Tmp1, Tmp2, OldByte, NewByte, SaveByte] = create_regs(5), + [LoopLbl,EndLbl,TopLbl] = create_lbls(3), + [hipe_rtl:mk_load(OldByte, BaseDst, BothOffset, byte, unsigned), + hipe_rtl:mk_alu(Tmp1, hipe_rtl:mk_imm(?BYTE_SIZE), sub, InitOffset), + TopLbl, + hipe_rtl:mk_branch(BothOffset, ne, Iterations, + hipe_rtl:label_name(LoopLbl), + hipe_rtl:label_name(EndLbl)), + LoopLbl, + hipe_rtl:mk_load(NewByte, BaseSrc, BothOffset, byte, unsigned), + hipe_rtl:mk_alu(Tmp2, NewByte, srl, InitOffset), + hipe_rtl:mk_alu(SaveByte, OldByte, 'or', Tmp2), + hipe_rtl:mk_store(BaseDst, BothOffset, SaveByte, byte), + hipe_rtl:mk_alu(OldByte, NewByte, sll, Tmp1), + hipe_rtl:mk_alu(BothOffset, BothOffset, 'add', hipe_rtl:mk_imm(1)), + hipe_rtl:mk_goto(hipe_rtl:label_name(TopLbl)), + EndLbl, + hipe_rtl:mk_store(BaseDst, BothOffset, OldByte, byte), + hipe_rtl:mk_goto(TrueLblName)]. + +initializations(BaseTmp1, BaseTmp2, BothOffset, CopyOffset, Offset, CopyBase, Base) -> + [OffsetTmp1,OffsetTmp2] = create_regs(2), + [hipe_rtl:mk_alu(OffsetTmp1, CopyOffset, srl, ?BYTE_SHIFT), + hipe_rtl:mk_alu(OffsetTmp2, Offset, srl, ?BYTE_SHIFT), + hipe_rtl:mk_alu(BaseTmp1, CopyBase, add, OffsetTmp1), + hipe_rtl:mk_alu(BaseTmp2, Base, add, OffsetTmp2), + hipe_rtl:mk_move(BothOffset, hipe_rtl:mk_imm(0))]. + +copy_int_little(Base, Offset, NewOffset, Size, Tmp1) when is_integer(Size) -> + [Tmp2,TmpOffset] = create_regs(2), + ByteSize = Size div ?BYTE_SIZE, + [hipe_rtl:mk_alu(TmpOffset, Offset, srl, ?BYTE_SHIFT), + hipe_rtl:mk_alu(Tmp2, hipe_rtl:mk_imm(ByteSize), 'add', TmpOffset)] ++ + + little_loop(Tmp1, Tmp2, TmpOffset, Base) ++ + + case Size band 7 of + 0 -> + [hipe_rtl:mk_alu(NewOffset, Offset, 'add', hipe_rtl:mk_imm(Size))]; + Bits -> + [hipe_rtl:mk_alu(Tmp1, Tmp1, sll, hipe_rtl:mk_imm(?BYTE_SIZE-Bits)), + hipe_rtl:mk_store(Base, TmpOffset, Tmp1, byte), + hipe_rtl:mk_alu(NewOffset, Offset, 'add', hipe_rtl:mk_imm(Size))] + end; + +copy_int_little(Base, Offset, NewOffset, Size, Tmp1) -> + [Tmp2, Tmp3, Tmp4, TmpOffset] = create_regs(4), + + [hipe_rtl:mk_alu(Tmp2, Size, srl, ?BYTE_SHIFT), + hipe_rtl:mk_alu(TmpOffset, Offset, srl, ?BYTE_SHIFT), + hipe_rtl:mk_alu(Tmp3, Tmp2, 'add', TmpOffset)] ++ + + little_loop(Tmp1, Tmp3, TmpOffset, Base) ++ + + [hipe_rtl:mk_alu(Tmp4, Size, 'and', ?LOW_BITS), + hipe_rtl:mk_alu(Tmp4, hipe_rtl:mk_imm(?BYTE_SIZE), 'sub', Tmp4), + hipe_rtl:mk_alu(Tmp1, Tmp1, sll, Tmp4), + hipe_rtl:mk_store(Base, TmpOffset, Tmp1, byte), + hipe_rtl:mk_alu(NewOffset, Offset, 'add', Size)]. + +little_loop(Tmp1, Tmp3, TmpOffset, Base) -> + [BranchLbl, BodyLbl, EndLbl] = create_lbls(3), + [BranchLbl, + hipe_rtl:mk_branch(TmpOffset, 'ne', Tmp3, + hipe_rtl:label_name(BodyLbl), + hipe_rtl:label_name(EndLbl)), + BodyLbl, + hipe_rtl:mk_store(Base, TmpOffset, Tmp1, byte), + hipe_rtl:mk_alu(Tmp1, Tmp1, 'sra', hipe_rtl:mk_imm(?BYTE_SIZE)), + hipe_rtl:mk_alu(TmpOffset, TmpOffset, 'add', hipe_rtl:mk_imm(1)), + hipe_rtl:mk_goto(hipe_rtl:label_name(BranchLbl)), + EndLbl]. + +big_loop(Tmp1, Tmp3, TmpOffset, Base) -> + [BranchLbl, BodyLbl, EndLbl] = create_lbls(3), + [BranchLbl, + hipe_rtl:mk_branch(TmpOffset, 'ne', Tmp3, + hipe_rtl:label_name(BodyLbl), + hipe_rtl:label_name(EndLbl)), + BodyLbl, + hipe_rtl:mk_alu(TmpOffset, TmpOffset, 'sub', hipe_rtl:mk_imm(1)), + hipe_rtl:mk_store(Base, TmpOffset, Tmp1, byte), + hipe_rtl:mk_alu(Tmp1, Tmp1, 'sra', hipe_rtl:mk_imm(?BYTE_SIZE)), + hipe_rtl:mk_goto(hipe_rtl:label_name(BranchLbl)), + EndLbl]. + +copy_int_big(_Base, Offset, NewOffset, 0, _Tmp1) -> + [hipe_rtl:mk_move(NewOffset, Offset)]; +copy_int_big(Base, Offset, NewOffset, ?BYTE_SIZE, Tmp1) -> + TmpOffset = hipe_rtl:mk_new_reg(), + [hipe_rtl:mk_alu(TmpOffset, Offset, 'srl', hipe_rtl:mk_imm(3)), + hipe_rtl:mk_store(Base, TmpOffset, Tmp1, byte), + hipe_rtl:mk_alu(NewOffset, Offset, 'add', hipe_rtl:mk_imm(8))]; +copy_int_big(Base, Offset, NewOffset, 2*?BYTE_SIZE, Tmp1) -> + TmpOffset = hipe_rtl:mk_new_reg(), + [hipe_rtl:mk_alu(TmpOffset, Offset, 'srl', hipe_rtl:mk_imm(3)), + hipe_rtl:mk_alu(TmpOffset, TmpOffset, 'add', hipe_rtl:mk_imm(1)), + hipe_rtl:mk_store(Base, TmpOffset, Tmp1, byte), + hipe_rtl:mk_alu(TmpOffset, TmpOffset, sub, hipe_rtl:mk_imm(1)), + hipe_rtl:mk_alu(Tmp1, Tmp1, 'sra', hipe_rtl:mk_imm(8)), + hipe_rtl:mk_store(Base, TmpOffset, Tmp1, byte), + hipe_rtl:mk_alu(NewOffset, Offset, 'add', hipe_rtl:mk_imm(16))]; +copy_int_big(Base, Offset, NewOffset, 3*?BYTE_SIZE, Tmp1) -> + TmpOffset = hipe_rtl:mk_new_reg(), + [hipe_rtl:mk_alu(TmpOffset, Offset, srl, hipe_rtl:mk_imm(3)), + hipe_rtl:mk_alu(TmpOffset, TmpOffset, add, hipe_rtl:mk_imm(2)), + hipe_rtl:mk_store(Base, TmpOffset, Tmp1, byte), + hipe_rtl:mk_alu(TmpOffset, TmpOffset, sub, hipe_rtl:mk_imm(1)), + hipe_rtl:mk_alu(Tmp1, Tmp1, sra, hipe_rtl:mk_imm(8)), + hipe_rtl:mk_store(Base, TmpOffset, Tmp1, byte), + hipe_rtl:mk_alu(TmpOffset, TmpOffset, sub, hipe_rtl:mk_imm(1)), + hipe_rtl:mk_alu(Tmp1, Tmp1, sra, hipe_rtl:mk_imm(8)), + hipe_rtl:mk_store(Base, TmpOffset, Tmp1, byte), + hipe_rtl:mk_alu(NewOffset, Offset, add, hipe_rtl:mk_imm(24))]; +copy_int_big(Base, Offset,NewOffset, 4*?BYTE_SIZE, Tmp1) -> + copy_big_word(Base, Offset, NewOffset, Tmp1); +copy_int_big(Base, Offset, NewOffset, Size, Tmp1) when is_integer(Size) -> + [OldOffset, TmpOffset, Bits] = create_regs(3), + ByteSize = (Size + 7) div ?BYTE_SIZE, + case Size band 7 of + 0 -> + [hipe_rtl:mk_alu(OldOffset, Offset, sra, hipe_rtl:mk_imm(3)), + hipe_rtl:mk_alu(TmpOffset, OldOffset, add, hipe_rtl:mk_imm(ByteSize))]; + Rest -> + [hipe_rtl:mk_alu(OldOffset, Offset, sra, hipe_rtl:mk_imm(3)), + hipe_rtl:mk_alu(TmpOffset, OldOffset, add, hipe_rtl:mk_imm(ByteSize-1)), + hipe_rtl:mk_alu(Bits, Tmp1, sll, hipe_rtl:mk_imm(?BYTE_SIZE-Rest)), + hipe_rtl:mk_store(Base, TmpOffset, Bits, byte), + hipe_rtl:mk_alu(Tmp1, Tmp1, sra, hipe_rtl:mk_imm(Rest))] + end ++ + big_loop(Tmp1, OldOffset, TmpOffset, Base) ++ + [hipe_rtl:mk_alu(NewOffset, Offset, 'add', hipe_rtl:mk_imm(Size))]; +copy_int_big(Base, Offset, NewOffset, Size, Tmp1) -> + Tmp2 = hipe_rtl:mk_new_reg(), + Tmp3 = hipe_rtl:mk_new_reg(), + Tmp4 = hipe_rtl:mk_new_reg(), + Tmp5 = hipe_rtl:mk_new_reg(), + Tmp6 = hipe_rtl:mk_new_reg(), + TmpOffset = hipe_rtl:mk_new_reg(), + EvenLbl = hipe_rtl:mk_new_label(), + OddLbl = hipe_rtl:mk_new_label(), + [hipe_rtl:mk_alu(Tmp2, Size, 'srl', hipe_rtl:mk_imm(3)), + hipe_rtl:mk_alu(Tmp3, Offset, 'srl', hipe_rtl:mk_imm(3)), + hipe_rtl:mk_alu(TmpOffset, Tmp2, 'add', Tmp3), + hipe_rtl:mk_alub(Tmp4, Size, 'and', hipe_rtl:mk_imm(7), 'eq', + hipe_rtl:label_name(EvenLbl), hipe_rtl:label_name(OddLbl)), + OddLbl, + hipe_rtl:mk_alu(Tmp6, hipe_rtl:mk_imm(8), 'sub', Tmp4), + hipe_rtl:mk_alu(Tmp5, Tmp1, 'sll', Tmp6), + hipe_rtl:mk_store(Base, TmpOffset, Tmp5, byte), + EvenLbl, + hipe_rtl:mk_alu(Tmp1, Tmp1, srl, Tmp4)] ++ + + big_loop(Tmp1, Tmp3, TmpOffset, Base) ++ + + [hipe_rtl:mk_alu(NewOffset, Offset, 'add', Size)]. + +copy_big_word(Base, Offset, NewOffset, Word) -> + TmpOffset = hipe_rtl:mk_new_reg(), + [hipe_rtl:mk_alu(TmpOffset, Offset, 'srl', hipe_rtl:mk_imm(3)), + hipe_rtl:mk_alu(TmpOffset, TmpOffset, 'add', hipe_rtl:mk_imm(3)), + hipe_rtl:mk_store(Base, TmpOffset, Word, byte), + hipe_rtl:mk_alu(TmpOffset, TmpOffset, 'sub', hipe_rtl:mk_imm(1)), + hipe_rtl:mk_alu(Word, Word, 'sra', hipe_rtl:mk_imm(8)), + hipe_rtl:mk_store(Base, TmpOffset, Word, byte), + hipe_rtl:mk_alu(TmpOffset, TmpOffset, 'sub', hipe_rtl:mk_imm(1)), + hipe_rtl:mk_alu(Word, Word, 'sra', hipe_rtl:mk_imm(8)), + hipe_rtl:mk_store(Base, TmpOffset, Word, byte), + hipe_rtl:mk_alu(TmpOffset, TmpOffset, 'sub', hipe_rtl:mk_imm(1)), + hipe_rtl:mk_alu(Word, Word, 'sra', hipe_rtl:mk_imm(8)), + hipe_rtl:mk_store(Base, TmpOffset, Word, byte), + hipe_rtl:mk_alu(NewOffset, Offset, 'add', hipe_rtl:mk_imm(32))]. + +copy_little_word(Base, Offset, NewOffset, Word) -> + TmpOffset = hipe_rtl:mk_new_reg(), + [hipe_rtl:mk_alu(TmpOffset, Offset, 'srl', ?BYTE_SHIFT), + hipe_rtl:mk_store(Base, TmpOffset, Word, byte), + hipe_rtl:mk_alu(TmpOffset, TmpOffset, 'add', hipe_rtl:mk_imm(1)), + hipe_rtl:mk_alu(Word, Word, 'sra', hipe_rtl:mk_imm(?BYTE_SIZE)), + hipe_rtl:mk_store(Base, TmpOffset, Word, byte), + hipe_rtl:mk_alu(TmpOffset, TmpOffset, 'add', hipe_rtl:mk_imm(1)), + hipe_rtl:mk_alu(Word, Word, 'sra', hipe_rtl:mk_imm(?BYTE_SIZE)), + hipe_rtl:mk_store(Base, TmpOffset, Word, byte), + hipe_rtl:mk_alu(TmpOffset, TmpOffset, 'add', hipe_rtl:mk_imm(1)), + hipe_rtl:mk_alu(Word, Word, 'sra', hipe_rtl:mk_imm(?BYTE_SIZE)), + hipe_rtl:mk_store(Base, TmpOffset, Word, byte), + hipe_rtl:mk_alu(NewOffset, Offset, 'add', hipe_rtl:mk_imm(32))]. + +copy_offset_int_big(Base, Offset, NewOffset, Size, Tmp1) when is_integer(Size) -> + Tmp2 = hipe_rtl:mk_new_reg(), + Tmp3 = hipe_rtl:mk_new_reg(), + Tmp4 = hipe_rtl:mk_new_reg(), + Tmp5 = hipe_rtl:mk_new_reg(), + Tmp6 = hipe_rtl:mk_new_reg(), + Tmp7 = hipe_rtl:mk_new_reg(), + Tmp8 = hipe_rtl:mk_new_reg(), + Tmp9 = hipe_rtl:mk_new_reg(), + OldByte = hipe_rtl:mk_new_reg(), + TmpOffset = hipe_rtl:mk_new_reg(), + BranchLbl = hipe_rtl:mk_new_label(), + BodyLbl = hipe_rtl:mk_new_label(), + EndLbl = hipe_rtl:mk_new_label(), + NextLbl = hipe_rtl:mk_new_label(), + WordSize = hipe_rtl_arch:word_size(), + [hipe_rtl:mk_alu(Tmp2, Offset, 'and', ?LOW_BITS), + hipe_rtl:mk_alu(Tmp3, Offset, srl, ?BYTE_SHIFT), + hipe_rtl:mk_alu(NewOffset, Offset, 'add', hipe_rtl:mk_imm(Size)), + hipe_rtl:mk_alu(Tmp9, NewOffset, 'sub', hipe_rtl:mk_imm(1)), + hipe_rtl:mk_alu(TmpOffset, Tmp9, srl, ?BYTE_SHIFT), + hipe_rtl:mk_alu(Tmp4, NewOffset, 'and', ?LOW_BITS), + hipe_rtl:mk_alu(Tmp6, hipe_rtl:mk_imm(?BYTE_SIZE), 'sub', Tmp4), + hipe_rtl:mk_alu(Tmp6, Tmp6, 'and', ?LOW_BITS), + hipe_rtl:mk_alu(Tmp4, hipe_rtl:mk_imm(?BYTE_SIZE), 'sub', Tmp6), + hipe_rtl:mk_move(Tmp5, Tmp1), + hipe_rtl:mk_alu(Tmp1, Tmp1, 'sll', Tmp6), + hipe_rtl:mk_branch(TmpOffset, 'ne', Tmp3, hipe_rtl:label_name(NextLbl), + hipe_rtl:label_name(EndLbl)), + NextLbl, + hipe_rtl:mk_store(Base, TmpOffset, Tmp1, byte), + hipe_rtl:mk_move(Tmp1, Tmp5), + hipe_rtl:mk_alu(Tmp1, Tmp1, 'sra', Tmp4), + hipe_rtl:mk_alu(TmpOffset, TmpOffset, 'sub', hipe_rtl:mk_imm(1)), + BranchLbl, + hipe_rtl:mk_branch(TmpOffset, 'ne', Tmp3, hipe_rtl:label_name(BodyLbl), + hipe_rtl:label_name(EndLbl)), + BodyLbl, + hipe_rtl:mk_store(Base, TmpOffset, Tmp1, byte), + hipe_rtl:mk_alu(Tmp1, Tmp1, 'sra', hipe_rtl:mk_imm(?BYTE_SIZE)), + hipe_rtl:mk_alu(TmpOffset, TmpOffset, 'sub', hipe_rtl:mk_imm(1)), + hipe_rtl:mk_goto(hipe_rtl:label_name(BranchLbl)), + EndLbl, + hipe_rtl:mk_load(OldByte, Base, TmpOffset, byte, unsigned), + hipe_rtl:mk_alu(Tmp8, hipe_rtl:mk_imm(?BYTE_SIZE), 'sub', Tmp2), + hipe_rtl:mk_alu(OldByte, OldByte, 'srl', Tmp8), + hipe_rtl:mk_alu(OldByte, OldByte, 'sll', Tmp8), + hipe_rtl:mk_alu(Tmp7, Tmp2, 'add', + hipe_rtl:mk_imm(?bytes_to_bits(WordSize-1))), + hipe_rtl:mk_alu(Tmp1, Tmp1, 'sll', Tmp7), + hipe_rtl:mk_alu(Tmp1, Tmp1, 'srl', Tmp7), + hipe_rtl:mk_alu(Tmp1, Tmp1, 'or', OldByte), + hipe_rtl:mk_store(Base, TmpOffset, Tmp1, byte)]. + +copy_float_little(_Base, _Offset, _NewOffset, _Src, FalseLblName, _TrueLblName, fail) -> + [hipe_rtl:mk_goto(FalseLblName)]; +copy_float_little(Base, Offset, NewOffset, Src, _FalseLblName, TrueLblName, pass) -> + FloatLo = hipe_rtl:mk_new_reg(), + FloatHi = hipe_rtl:mk_new_reg(), + TmpOffset = hipe_rtl:mk_new_reg(), + hipe_tagscheme:unsafe_load_float(FloatLo, FloatHi, Src) ++ + copy_little_word(Base, Offset, TmpOffset, FloatLo) ++ + copy_little_word(Base, TmpOffset, NewOffset, FloatHi) ++ + [hipe_rtl:mk_goto(TrueLblName)]; +copy_float_little(Base, Offset, NewOffset, Src, FalseLblName, TrueLblName, var) -> + SuccessLbl = hipe_rtl:mk_new_label(), + hipe_tagscheme:test_flonum(Src, hipe_rtl:label_name(SuccessLbl), FalseLblName, 0.99) ++ + [SuccessLbl|copy_float_little(Base, Offset, NewOffset, Src, FalseLblName, TrueLblName, pass)]. + +copy_float_big(_Base, _Offset, _NewOffset, _Src, FalseLblName, _TrueLblName, fail) -> + [hipe_rtl:mk_goto(FalseLblName)]; +copy_float_big(Base, Offset, NewOffset, Src, _FalseLblName, TrueLblName,pass) -> + FloatLo = hipe_rtl:mk_new_reg(), + FloatHi = hipe_rtl:mk_new_reg(), + TmpOffset =hipe_rtl:mk_new_reg(), + hipe_tagscheme:unsafe_load_float(FloatLo, FloatHi, Src) ++ + copy_big_word(Base, Offset, TmpOffset, FloatHi) ++ + copy_big_word(Base, TmpOffset, NewOffset, FloatLo) ++ + [hipe_rtl:mk_goto(TrueLblName)]; +copy_float_big(Base, Offset, NewOffset, Src, FalseLblName, TrueLblName, var) -> + SuccessLbl = hipe_rtl:mk_new_label(), + hipe_tagscheme:test_flonum(Src, hipe_rtl:label_name(SuccessLbl), FalseLblName, 0.99) ++ + [SuccessLbl|copy_float_big(Base, Offset, NewOffset, Src, FalseLblName, TrueLblName, pass)]. + +make_size(1, BitsVar, FalseLblName) -> + [DstReg] = create_regs(1), + {first_part(BitsVar, DstReg, FalseLblName), DstReg}; +make_size(?BYTE_SIZE, BitsVar, FalseLblName) -> + [DstReg] = create_regs(1), + Code = + first_part(BitsVar, DstReg, FalseLblName) ++ + [hipe_rtl:mk_alu(DstReg, DstReg, 'sll', ?BYTE_SHIFT)], + {Code, DstReg}; +make_size(UnitImm, BitsVar, FalseLblName) -> + [DstReg] = create_regs(1), + UnitList = number2list(UnitImm), + Code = multiply_code(UnitList, BitsVar, DstReg, FalseLblName), + {Code, DstReg}. + +multiply_code(List=[Head|_Tail], Variable, Result, FalseLblName) -> + Test = set_high(Head), + Tmp1 = hipe_rtl:mk_new_reg(), + SuccessLbl = hipe_rtl:mk_new_label(), + Register = hipe_rtl:mk_new_reg(), + Code = [hipe_rtl:mk_move(Result, hipe_rtl:mk_imm(0))| + first_part(Variable, Register, FalseLblName)] + ++ + [hipe_rtl:mk_alub(Tmp1, Register, 'and', hipe_rtl:mk_imm(Test), + 'eq', hipe_rtl:label_name(SuccessLbl), + FalseLblName, 0.99), + SuccessLbl], + multiply_code(List, Register, Result, FalseLblName, Tmp1, Code). + +multiply_code([ShiftSize|Rest], Register, Result, FalseLblName, Tmp1, OldCode) -> + SuccessLbl = hipe_rtl:mk_new_label(), + Code = OldCode ++ [hipe_rtl:mk_alu(Tmp1, Register, 'sll', + hipe_rtl:mk_imm(ShiftSize)), + hipe_rtl:mk_alub(Result, Tmp1, 'add', Result, not_overflow, hipe_rtl:label_name(SuccessLbl), FalseLblName, 0.99), + SuccessLbl], + multiply_code(Rest, Register, Result, FalseLblName, Tmp1, Code); +multiply_code([], _Register, _Result, _FalseLblName, _Tmp1, Code) -> + Code. + +number2list(X) when is_integer(X), X >= 0 -> + number2list(X, []). + +number2list(1, Acc) -> + lists:reverse([0|Acc]); +number2list(0, Acc) -> + lists:reverse(Acc); +number2list(X, Acc) -> + F = floorlog2(X), + number2list(X-(1 bsl F), [F|Acc]). + +floorlog2(X) -> + round(math:log(X)/math:log(2)-0.5). + +set_high(X) -> + set_high(X, 0). + +set_high(0, Y) -> + Y; +set_high(X, Y) -> + set_high(X-1, Y+(1 bsl (27-X))). + +get_32_bit_value(Size, USize, SystemLimitLblName, NegLblName) -> + Lbls = [FixLbl, BigLbl, OkLbl, PosBigLbl] = create_lbls(4), + [FixLblName, BigLblName, OkLblName, PosBigLblName] = [hipe_rtl:label_name(Lbl) || Lbl <- Lbls], + [hipe_tagscheme:test_fixnum(Size, FixLblName, BigLblName, 0.99), + FixLbl, + hipe_tagscheme:untag_fixnum(USize, Size), + hipe_rtl:mk_branch(USize, ge, hipe_rtl:mk_imm(0), OkLblName, NegLblName), + BigLbl, + hipe_tagscheme:test_pos_bignum(Size, PosBigLblName, NegLblName, 0.99), + PosBigLbl, + hipe_tagscheme:get_one_word_pos_bignum(USize, Size, SystemLimitLblName), + OkLbl]. + + +first_part(Var, Register, FalseLblName) -> + [SuccessLbl1, SuccessLbl2] = create_lbls(2), + [hipe_tagscheme:test_fixnum(Var, hipe_rtl:label_name(SuccessLbl1), + FalseLblName, 0.99), + SuccessLbl1, + hipe_tagscheme:fixnum_ge(Var, hipe_rtl:mk_imm(hipe_tagscheme:mk_fixnum(0)), + hipe_rtl:label_name(SuccessLbl2), FalseLblName, 0.99), + SuccessLbl2, + hipe_tagscheme:untag_fixnum(Register, Var)]. + + diff --git a/lib/hipe/rtl/hipe_rtl_binary_match.erl b/lib/hipe/rtl/hipe_rtl_binary_match.erl new file mode 100644 index 0000000000..d147bed6d8 --- /dev/null +++ b/lib/hipe/rtl/hipe_rtl_binary_match.erl @@ -0,0 +1,1134 @@ +%%% -*- erlang-indent-level: 2 -*- +%%% +%%% %CopyrightBegin% +%%% +%%% Copyright Ericsson AB 2007-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% +%%% +%%%------------------------------------------------------------------- +%%% File : hipe_rtl_binary_match.erl +%%% Author : Per Gustafsson <[email protected]> +%%% Description : +%%% +%%% Created : 5 Mar 2007 by Per Gustafsson <[email protected]> +%%%------------------------------------------------------------------- +-module(hipe_rtl_binary_match). + +-export([gen_rtl/5]). + +-import(hipe_tagscheme, [set_field_from_term/3, get_field_from_term/3]). + +-include("hipe_literals.hrl"). + +%%-------------------------------------------------------------------- + +-define(MAX_BINSIZE, trunc(?MAX_HEAP_BIN_SIZE / hipe_rtl_arch:word_size()) + 2). +-define(BYTE_SHIFT, 3). %% Turn bits into bytes or vice versa +-define(LOW_BITS, 7). %% Three lowest bits set +-define(BYTE_SIZE, 8). +-define(MAX_SMALL_BITS, (hipe_rtl_arch:word_size() * ?BYTE_SIZE - 5)). + +%%-------------------------------------------------------------------- + +gen_rtl({bs_start_match, 0}, [Ms], [Binary], TrueLblName, FalseLblName) -> + ReInitLbl = hipe_rtl:mk_new_label(), + BinaryLbl = hipe_rtl:mk_new_label(), + TestCode = + [hipe_rtl:mk_move(Ms,Binary), + hipe_tagscheme:test_matchstate(Binary, + hipe_rtl:label_name(ReInitLbl), + hipe_rtl:label_name(BinaryLbl), + 0.99)], + ReInitCode = reinit_matchstate(Ms, TrueLblName), + OrdinaryCode = make_matchstate(Binary, 0, Ms, TrueLblName, FalseLblName), + [TestCode,[ReInitLbl|ReInitCode],[BinaryLbl|OrdinaryCode]]; +gen_rtl({bs_start_match, Max}, [Ms], [Binary], TrueLblName, FalseLblName) -> + MatchStateLbl = hipe_rtl:mk_new_label(), + BinaryLbl = hipe_rtl:mk_new_label(), + ReSizeLbl = hipe_rtl:mk_new_label(), + ReInitLbl = hipe_rtl:mk_new_label(), + TestCode = + [hipe_rtl:mk_move(Ms,Binary), + hipe_tagscheme:test_matchstate(Binary, + hipe_rtl:label_name(MatchStateLbl), + hipe_rtl:label_name(BinaryLbl), + 0.99)], + MatchStateTestCode = + [hipe_tagscheme:compare_matchstate(Max, Ms, + hipe_rtl:label_name(ReInitLbl), + hipe_rtl:label_name(ReSizeLbl))], + ReSizeCode = resize_matchstate(Ms, Max, TrueLblName), + ReInitCode = reinit_matchstate(Ms, TrueLblName), + OrdinaryCode = make_matchstate(Binary, Max, Ms, TrueLblName, FalseLblName), + [TestCode, [MatchStateLbl|MatchStateTestCode], [ReSizeLbl|ReSizeCode], + [ReInitLbl|ReInitCode], [BinaryLbl|OrdinaryCode]]; +gen_rtl({bs_start_match, _Max}, [], [Binary], TrueLblName, FalseLblName) -> + MatchStateLbl = hipe_rtl:mk_new_label(), + [hipe_tagscheme:test_bitstr(Binary, TrueLblName, + hipe_rtl:label_name(MatchStateLbl), 0.99), + MatchStateLbl, + hipe_tagscheme:test_matchstate(Binary, TrueLblName, FalseLblName, 0.99)]; +gen_rtl({{bs_start_match, bitstr}, Max}, [Ms], [Binary], + TrueLblName, FalseLblName) -> + make_matchstate(Binary, Max, Ms, TrueLblName, FalseLblName); +gen_rtl({{bs_start_match, bitstr}, _Max}, [], [_Binary], + TrueLblName, _FalseLblName) -> + [hipe_rtl:mk_goto(TrueLblName)]; +gen_rtl({{bs_start_match,ok_matchstate}, Max}, [Ms], [Binary], + TrueLblName, FalseLblName) -> + MatchStateLbl = hipe_rtl:mk_new_label(), + BinaryLbl = hipe_rtl:mk_new_label(), + TestCode = + [hipe_rtl:mk_move(Ms,Binary), + hipe_tagscheme:test_matchstate(Binary, + hipe_rtl:label_name(MatchStateLbl), + hipe_rtl:label_name(BinaryLbl), + 0.99)], + MatchStateCode = reinit_matchstate(Ms, TrueLblName), + OrdinaryCode = make_matchstate(Binary, Max, Ms, TrueLblName, FalseLblName), + TestCode ++ [MatchStateLbl|MatchStateCode] ++ [BinaryLbl|OrdinaryCode]; +gen_rtl({{bs_start_match, ok_matchstate}, _Max}, [], [Binary], + TrueLblName, FalseLblName) -> + MatchStateLbl = hipe_rtl:mk_new_label(), + [hipe_tagscheme:test_bitstr(Binary, TrueLblName, + hipe_rtl:label_name(MatchStateLbl), 0.99), + MatchStateLbl, + hipe_tagscheme:test_matchstate(Binary, TrueLblName, FalseLblName, 0.99)]; +gen_rtl({bs_get_integer, 0, _Flags}, [Dst, NewMs], [Ms], + TrueLblName, _FalseLblName) -> + update_ms(NewMs, Ms) ++ + [hipe_rtl:mk_move(Dst, hipe_rtl:mk_imm(15)), + hipe_rtl:mk_goto(TrueLblName)]; +gen_rtl({bs_get_integer,Size,Flags}, [Dst,NewMs], Args, + TrueLblName, FalseLblName) -> + case is_illegal_const(Size) of + true -> + [hipe_rtl:mk_goto(FalseLblName)]; + false -> + Signed = signed(Flags), + LittleEndian = littleendian(Flags), + Aligned = aligned(Flags), + UnSafe = unsafe(Flags), + case Args of + [Ms] -> + CCode= int_get_c_code(Dst, Ms, hipe_rtl:mk_imm(Size), + Flags, TrueLblName, FalseLblName), + update_ms(NewMs, Ms) ++ + get_static_int(Dst, Ms, Size, CCode, + Signed, LittleEndian, Aligned, UnSafe, + TrueLblName, FalseLblName); + [Ms, Arg] -> + {SizeCode1, SizeReg1} = + make_size(Size, Arg, FalseLblName), + CCode = int_get_c_code(Dst, Ms, SizeReg1, Flags, + TrueLblName, FalseLblName), + InCode = get_dynamic_int(Dst, Ms, SizeReg1, CCode, + Signed, LittleEndian, Aligned, + TrueLblName, FalseLblName), + update_ms(NewMs, Ms) ++ SizeCode1 ++ InCode + end + end; +gen_rtl({bs_get_float,Size,Flags}, [Dst1,NewMs], Args, + TrueLblName, FalseLblName) -> + case is_illegal_const(Size) of + true -> + [hipe_rtl:mk_goto(FalseLblName)]; + false -> + [hipe_rtl:mk_gctest(3)] ++ + case Args of + [Ms] -> + CCode = float_get_c_code(Dst1, Ms, hipe_rtl:mk_imm(Size), Flags, + TrueLblName, FalseLblName), + update_ms(NewMs, Ms) ++ CCode; + [Ms,Arg] -> + {SizeCode, SizeReg} = make_size(Size, Arg, + FalseLblName), + CCode = float_get_c_code(Dst1, Ms, SizeReg, Flags, + TrueLblName, FalseLblName), + update_ms(NewMs, Ms) ++ SizeCode ++ CCode + end + end; +gen_rtl({bs_get_binary_all, Unit, _Flags}, [Dst], [Ms], + TrueLblName, FalseLblName) -> + [hipe_rtl:mk_gctest(?SUB_BIN_WORDSIZE)] ++ + get_binary_all(Dst, Unit, Ms, TrueLblName,FalseLblName); +gen_rtl({bs_get_binary_all_2, Unit, _Flags}, [Dst,NewMs], [Ms], + TrueLblName, FalseLblName) -> + [hipe_rtl:mk_gctest(?SUB_BIN_WORDSIZE)] ++ + update_ms(NewMs, Ms) ++ + get_binary_all(Dst, Unit, Ms, TrueLblName, FalseLblName); +gen_rtl({bs_get_binary,Size,Flags}, [Dst,NewMs], Args, + TrueLblName, FalseLblName) -> + case is_illegal_const(Size) of + true -> + [hipe_rtl:mk_goto(FalseLblName)]; + false -> + Unsafe = unsafe(Flags), + case Args of + [Ms] -> + SizeReg = hipe_rtl:mk_new_reg(), + SizeCode = [hipe_rtl:mk_move(SizeReg, hipe_rtl:mk_imm(Size))]; + [Ms, BitsVar] -> + {SizeCode, SizeReg} = make_size(Size, BitsVar, FalseLblName) + end, + InCode = get_binary(Dst, Ms, SizeReg, Unsafe, + TrueLblName, FalseLblName), + [hipe_rtl:mk_gctest(?SUB_BIN_WORDSIZE)] ++ + update_ms(NewMs, Ms) ++ SizeCode ++ InCode + end; +gen_rtl(bs_get_utf8, [Dst,NewMs], [Ms], TrueLblName, FalseLblName) -> + update_ms(NewMs, Ms) ++ utf8_get_c_code(Dst, Ms, TrueLblName, FalseLblName); +gen_rtl({bs_get_utf16,Flags}, [Dst,NewMs], [Ms], TrueLblName, FalseLblName) -> + update_ms(NewMs, Ms) ++ utf16_get_c_code(Flags, Dst, Ms, TrueLblName, FalseLblName); +gen_rtl(bs_validate_unicode_retract, [NewMs], [Src,Ms], TrueLblName, FalseLblName) -> + update_ms(NewMs, Ms) ++ validate_unicode_retract_c_code(Src, Ms, TrueLblName, FalseLblName); +gen_rtl({bs_test_tail, NumBits}, [NewMs], [Ms], TrueLblName, FalseLblName) -> + {[Offset,BinSize], ExCode} = extract_matchstate_vars([offset,binsize], Ms), + update_ms(NewMs, Ms) ++ ExCode ++ + [add_to_offset(Offset, Offset, hipe_rtl:mk_imm(NumBits), FalseLblName), + hipe_rtl:mk_branch(Offset, eq, BinSize, TrueLblName, FalseLblName)]; +gen_rtl({bs_test_unit, Unit}, [], [Ms], TrueLblName, FalseLblName) -> + {[Offset,BinSize], ExCode} = extract_matchstate_vars([offset,binsize], Ms), + SizeReg = hipe_rtl:mk_new_reg(), + ExCode ++ + [hipe_rtl:mk_alu(SizeReg, BinSize, sub, Offset)| + test_alignment_code(SizeReg, Unit, TrueLblName, FalseLblName)]; +gen_rtl({bs_test_tail, NumBits}, [], [Ms], TrueLblName, FalseLblName) -> + {[Offset,BinSize], ExCode} = extract_matchstate_vars([offset,binsize], Ms), + ExCode ++ + [add_to_offset(Offset, Offset, hipe_rtl:mk_imm(NumBits), FalseLblName), + hipe_rtl:mk_branch(Offset, eq, BinSize, TrueLblName, FalseLblName)]; +gen_rtl({bs_skip_bits_all, Unit, _Flags}, Dst, [Ms], + TrueLblName, FalseLblName) -> + opt_update_ms(Dst, Ms) ++ + skip_bits_all(Unit, Ms, TrueLblName, FalseLblName); +gen_rtl({bs_skip_bits, Bits}, Dst, [Ms|Args], TrueLblName, FalseLblName) -> + opt_update_ms(Dst,Ms) ++ + case Args of + [] -> + skip_bits2(Ms, hipe_rtl:mk_imm(Bits), TrueLblName, FalseLblName); + [Arg] -> + {SizeCode, SizeReg} = make_size(Bits, Arg, FalseLblName), + InCode = skip_bits2(Ms, SizeReg, TrueLblName, FalseLblName), + SizeCode ++ InCode + end; +gen_rtl({bs_restore, Slot}, [NewMs], [Ms], TrueLblName, _FalseLblName) -> + Tmp1 = hipe_rtl:mk_new_reg_gcsafe(), + update_ms(NewMs, Ms) ++ + [get_field_from_term({matchstate, {saveoffset, Slot}}, Ms, Tmp1), + set_field_from_term({matchstate, {matchbuffer, offset}}, Ms, Tmp1), + hipe_rtl:mk_goto(TrueLblName)]; +gen_rtl({bs_save, Slot}, [NewMs], [Ms], TrueLblName, _FalseLblName) -> + {Offset, Instr} = extract_matchstate_var(offset, Ms), + update_ms(NewMs, Ms) ++ + [Instr, + set_field_from_term({matchstate, {saveoffset, Slot}}, Ms, Offset), + hipe_rtl:mk_goto(TrueLblName)]; +gen_rtl({bs_match_string, String, ByteSize}, [NewMs], + [Ms], TrueLblName, FalseLblName) -> + {[Offset, BinSize, Base], Instrs} = + extract_matchstate_vars([offset, binsize, base], Ms), + [SuccessLbl, ALbl, ULbl] = create_lbls(3), + [NewOffset,BitOffset] = create_gcsafe_regs(2), + Unit = hipe_rtl_arch:word_size() - 1, + Loops = ByteSize div Unit, + Init = + [Instrs, + update_ms(NewMs,Ms), + check_size(Offset, hipe_rtl:mk_imm(ByteSize*?BYTE_SIZE), BinSize, + NewOffset, hipe_rtl:label_name(SuccessLbl), FalseLblName), + SuccessLbl], + SplitCode = + [hipe_rtl:mk_alub(BitOffset, Offset, 'and', hipe_rtl:mk_imm(?LOW_BITS), eq, + hipe_rtl:label_name(ALbl), hipe_rtl:label_name(ULbl))], + Loops = ByteSize div Unit, + SkipSize = Loops * Unit, + {ACode1,UCode1} = + case Loops of + 0 -> + {[],[]}; + _ -> + create_loops(Loops, Unit, String, Base, + Offset, BitOffset, FalseLblName) + end, + <<_:SkipSize/binary, RestString/binary>> = String, + {ACode2, UCode2} = + case ByteSize rem Unit of + 0 -> + {[],[]}; + Rem -> + create_rests(Rem, RestString, Base, Offset, BitOffset, FalseLblName) + end, + End = [update_offset(NewOffset, NewMs), hipe_rtl:mk_goto(TrueLblName)], + [Init, SplitCode, ALbl, ACode1, ACode2, End, ULbl, UCode1, UCode2,End]; +gen_rtl(bs_context_to_binary, [Bin], [Var], TrueLblName, _FalseLblName) -> + MSLabel = hipe_rtl:mk_new_label(), + [hipe_rtl:mk_move(Bin, Var), + hipe_tagscheme:test_matchstate(Var, hipe_rtl:label_name(MSLabel), + TrueLblName, 0.5), + MSLabel, + hipe_tagscheme:convert_matchstate(Bin), + hipe_rtl:mk_goto(TrueLblName)]. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Calls to C %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +int_get_c_code(Dst1, Ms, Size, Flags, TrueLblName, FalseLblName) -> + make_int_gc_code(Size) ++ + get_c_code(bs_get_integer_2, Dst1, Ms, Size, Flags, + TrueLblName, FalseLblName). + +float_get_c_code(Dst1, Ms, Size, Flags, TrueLblName, FalseLblName) -> + get_c_code(bs_get_float_2, Dst1, Ms, Size, Flags, TrueLblName, FalseLblName). + +get_c_code(Func, Dst1, Ms, Size, Flags, TrueLblName, FalseLblName) -> + SizeReg = hipe_rtl:mk_new_reg_gcsafe(), + FlagsReg = hipe_rtl:mk_new_reg_gcsafe(), + MatchBuf = hipe_rtl:mk_new_reg(), + RetLabel = hipe_rtl:mk_new_label(), + NonVal = hipe_rtl:mk_imm(hipe_tagscheme:mk_non_value()), + [hipe_rtl:mk_move(SizeReg, Size), + hipe_rtl:mk_move(FlagsReg, hipe_rtl:mk_imm(Flags)), + hipe_tagscheme:extract_matchbuffer(MatchBuf, Ms), + hipe_rtl_arch:call_bif([Dst1], Func, [SizeReg, FlagsReg, MatchBuf], + hipe_rtl:label_name(RetLabel), FalseLblName), + RetLabel, + hipe_rtl:mk_branch(Dst1, eq, NonVal, + FalseLblName, + TrueLblName, 0.01)]. + +utf8_get_c_code(Dst, Ms, TrueLblName, FalseLblName) -> + MatchBuf = hipe_rtl:mk_new_reg(), + NonVal = hipe_rtl:mk_imm(hipe_tagscheme:mk_non_value()), + [hipe_tagscheme:extract_matchbuffer(MatchBuf, Ms), + hipe_rtl_arch:call_bif([Dst], bs_get_utf8, [MatchBuf], [], []), + hipe_rtl:mk_branch(Dst, eq, NonVal, FalseLblName, TrueLblName, 0.01)]. + +utf16_get_c_code(Flags, Dst, Ms, TrueLblName, FalseLblName) -> + MatchBuf = hipe_rtl:mk_new_reg(), + NonVal = hipe_rtl:mk_imm(hipe_tagscheme:mk_non_value()), + FlagsReg = hipe_rtl:mk_new_reg_gcsafe(), + [hipe_tagscheme:extract_matchbuffer(MatchBuf, Ms), + hipe_rtl:mk_move(FlagsReg, hipe_rtl:mk_imm(Flags)), + hipe_rtl_arch:call_bif([Dst], bs_get_utf16, [MatchBuf, FlagsReg], [], []), + hipe_rtl:mk_branch(Dst, eq, NonVal, FalseLblName, TrueLblName, 0.01)]. + +validate_unicode_retract_c_code(Src, Ms, TrueLblName, FalseLblName) -> + MatchBuf = hipe_rtl:mk_new_reg(), + Zero = hipe_rtl:mk_imm(0), + Tmp = hipe_rtl:mk_new_reg(), + [hipe_tagscheme:extract_matchbuffer(MatchBuf, Ms), + hipe_rtl_arch:call_bif([Tmp], bs_validate_unicode_retract, + [MatchBuf,Src], [], []), + hipe_rtl:mk_branch(Tmp, eq, Zero, FalseLblName, TrueLblName, 0.01)]. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Int Code %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +create_loops(Loops, Unit, String, Base, Offset, BitOffset, FalseLblName) -> + [Reg] = create_gcsafe_regs(1), + AlignedFun = fun(Value) -> + [get_int_to_reg(Reg, Unit*?BYTE_SIZE, Base, Offset, 'srl', + {unsigned, big}), + update_and_test(Reg, Unit, Offset, Value, FalseLblName)] + end, + UnAlignedFun = fun(Value) -> + [get_unaligned_int_to_reg(Reg, Unit*?BYTE_SIZE, + Base, Offset, BitOffset, + 'srl', {unsigned, big})| + update_and_test(Reg, Unit, Offset, Value, FalseLblName)] + end, + {create_loops(Loops, Unit, String, AlignedFun), + create_loops(Loops, Unit, String, UnAlignedFun)}. + +create_rests(Rem, String, Base, Offset, BitOffset, FalseLblName) -> + [Reg] = create_gcsafe_regs(1), + AlignedFun = fun(Value) -> + [get_int_to_reg(Reg, Rem*?BYTE_SIZE, Base, Offset, 'srl', + {unsigned, big})| + just_test(Reg, Value, FalseLblName)] + end, + UnAlignedFun = fun(Value) -> + [get_unaligned_int_to_reg(Reg, Rem*?BYTE_SIZE, + Base, Offset, BitOffset, + 'srl', {unsigned, big})| + just_test(Reg, Value, FalseLblName)] + end, + {create_loops(1, Rem, String, AlignedFun), + create_loops(1, Rem, String, UnAlignedFun)}. + +create_loops(0, _Unit, _String, _IntFun) -> + []; +create_loops(N, Unit, String, IntFun) -> + {Value, RestString} = get_value(Unit,String), + [IntFun(Value), + create_loops(N-1, Unit, RestString, IntFun)]. + +update_and_test(Reg, Unit, Offset, Value, FalseLblName) -> + [add_to_offset(Offset, Offset, hipe_rtl:mk_imm(Unit*?BYTE_SIZE), FalseLblName), + just_test(Reg, Value, FalseLblName)]. + +just_test(Reg, Value, FalseLblName) -> + [ContLbl] = create_lbls(1), + [hipe_rtl:mk_branch(Reg, eq, hipe_rtl:mk_imm(Value), + hipe_rtl:label_name(ContLbl), FalseLblName), + ContLbl]. + +get_value(N,String) -> + <<I:N/integer-unit:8, Rest/binary>> = String, + {I, Rest}. + +make_int_gc_code(I) when is_integer(I) -> + case hipe_tagscheme:bignum_sizeneed(I) of + 0 -> []; + X when is_integer(X) -> [hipe_rtl:mk_gctest(X)] + end; +make_int_gc_code(SReg) -> + FixNumLbl = hipe_rtl:mk_new_label(), + FixNumLblName = hipe_rtl:label_name(FixNumLbl), + {ResReg,Code} = hipe_tagscheme:bignum_sizeneed_code(SReg, FixNumLblName), + Code ++ + [hipe_rtl:mk_gctest(ResReg), + hipe_rtl:mk_goto(FixNumLblName), + FixNumLbl]. + +get_static_int(Dst1, Ms, Size, CCode, Signed, LittleEndian, Aligned, + Unsafe, TrueLblName, FalseLblName) -> + WordSize = hipe_rtl_arch:word_size(), + case Size =< WordSize*?BYTE_SIZE of + true -> + case {Aligned, LittleEndian} of + {true, false} -> + get_int_from_bin(Ms, Size, Dst1,Signed, LittleEndian, + Unsafe, FalseLblName, TrueLblName); + {true, true} -> + case Size rem ?BYTE_SIZE of + 0 -> + get_int_from_bin(Ms, Size, Dst1, Signed, LittleEndian, + Unsafe, FalseLblName, TrueLblName); + _ -> + CCode + end; + {false, false} -> + get_int_from_unaligned_bin(Ms, Size, Dst1, Signed, + Unsafe, FalseLblName, TrueLblName); + {false, true} -> + CCode + end; + false -> + CCode + end. + +get_dynamic_int(Dst1, Ms, SizeReg, CCode, Signed, LittleEndian, true, + TrueLblName, FalseLblName) -> + {Init, End} = make_dyn_prep(SizeReg, CCode), + Init ++ + get_unknown_size_int(SizeReg, Ms, Dst1, Signed, LittleEndian, + FalseLblName, TrueLblName) ++ + End; +get_dynamic_int(_Dst1, _Ms, _SizeReg, CCode, _Signed, _LittleEndian, false, + _TrueLblName, _FalseLblName) -> + CCode. + +get_int_from_bin(Ms, Size, Dst1, Signed, LittleEndian, + Unsafe, FalseLblName, TrueLblName) -> + Shiftr = shift_type(Signed), + Type = get_type(Signed, LittleEndian), + NewOffset = hipe_rtl:mk_new_reg_gcsafe(), + [SuccessLbl] = create_lbls(1), + {[Base,Offset,BinSize], ExCode} = + extract_matchstate_vars([base,offset,binsize], Ms), + ExCode ++ + [check_size(Offset, hipe_rtl:mk_imm(Size), BinSize, NewOffset, + Unsafe, hipe_rtl:label_name(SuccessLbl), FalseLblName), + SuccessLbl] ++ + [update_offset(NewOffset, Ms)] ++ + get_int(Dst1, Size, Base, Offset, Shiftr, Type, TrueLblName). + +get_int_from_unaligned_bin(Ms, Size, Dst1, Signed, + UnSafe, FalseLblName, TrueLblName) -> + Shiftr = shift_type(Signed), + Type = get_type(Signed, false), + NewOffset = hipe_rtl:mk_new_reg_gcsafe(), + [SuccessLbl] = create_lbls(1), + {[Base,Offset,BinSize], ExCode} = + extract_matchstate_vars([base,offset,binsize], Ms), + ExCode ++ + [check_size(Offset, hipe_rtl:mk_imm(Size), BinSize, NewOffset, + UnSafe, hipe_rtl:label_name(SuccessLbl), FalseLblName), + SuccessLbl] ++ + [update_offset(NewOffset, Ms)] ++ + get_unaligned_int(Dst1, Size, Base, Offset, Shiftr, Type, TrueLblName). + +get_unknown_size_int(SizeReg, Ms, Dst1, Signed, Little, + FalseLblName, TrueLblName) -> + Shiftr = shift_type(Signed), + Type = get_type(Signed, false), + [NewOffset] = create_gcsafe_regs(1), + [SuccessLbl] = create_lbls(1), + {[Base,Offset,BinSize], ExCode} = + extract_matchstate_vars([base,offset,binsize], Ms), + ExCode ++ + [check_size(Offset, SizeReg, BinSize, NewOffset, + hipe_rtl:label_name(SuccessLbl), FalseLblName), + SuccessLbl, + update_offset(NewOffset, Ms)] ++ + case Little of + true -> + get_little_unknown_int(Dst1, Base, Offset, NewOffset, + Shiftr, Type, TrueLblName); + false -> + get_big_unknown_int(Dst1, Base, Offset, NewOffset, + Shiftr, Type, TrueLblName) + end. + +make_matchstate(Binary, Max, Ms, TrueLblName, FalseLblName) -> + Base = hipe_rtl:mk_new_reg(), + Orig = hipe_rtl:mk_new_var(), + BinSize = hipe_rtl:mk_new_reg_gcsafe(), + Offset = hipe_rtl:mk_new_reg_gcsafe(), + Lbl = hipe_rtl:mk_new_label(), + [hipe_rtl:mk_gctest(?MS_MIN_SIZE+Max), + get_binary_bytes(Binary, BinSize, Base, Offset, + Orig, hipe_rtl:label_name(Lbl), FalseLblName), + Lbl, + hipe_tagscheme:create_matchstate(Max, BinSize, Base, Offset, Orig, Ms), + hipe_rtl:mk_goto(TrueLblName)]. + +resize_matchstate(Ms, Max, TrueLblName) -> + Base = hipe_rtl:mk_new_reg(), + Orig = hipe_rtl:mk_new_var(), + BinSize = hipe_rtl:mk_new_reg_gcsafe(), + Offset = hipe_rtl:mk_new_reg_gcsafe(), + [hipe_rtl:mk_gctest(?MS_MIN_SIZE+Max), + get_field_from_term({matchstate, {matchbuffer, binsize}}, Ms, BinSize), + get_field_from_term({matchstate, {matchbuffer, base}}, Ms, Base), + get_field_from_term({matchstate, {matchbuffer, orig}}, Ms, Orig), + get_field_from_term({matchstate, {matchbuffer, offset}}, Ms, Offset), + hipe_tagscheme:create_matchstate(Max, BinSize, Base, Offset, Orig, Ms), + hipe_rtl:mk_goto(TrueLblName)]. + +reinit_matchstate(Ms, TrueLblName) -> + Tmp = hipe_rtl:mk_new_reg_gcsafe(), + [get_field_from_term({matchstate, {matchbuffer, offset}}, Ms, Tmp), + set_field_from_term({matchstate, {saveoffset, 0}}, Ms, Tmp), + hipe_rtl:mk_goto(TrueLblName)]. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%% Binary Code %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +get_binary_all(Dst1, 1, Ms, TrueLblName, _FalseLblName) -> + [SizeReg] = create_gcsafe_regs(1), + {[Offset,BinSize,Orig], ExCode} = + extract_matchstate_vars([offset,binsize,orig], Ms), + MakeCode = + [hipe_rtl:mk_alu(SizeReg, BinSize, sub, Offset)| + construct_subbin(Dst1,SizeReg,Offset,Orig)] ++ + [update_offset(BinSize, Ms), + hipe_rtl:mk_goto(TrueLblName)], + ExCode ++ MakeCode; +get_binary_all(Dst1, Unit, Ms, TrueLblName, FalseLblName) -> + [SizeReg] = create_gcsafe_regs(1), + [SuccessLbl] = create_lbls(1), + SLblName = hipe_rtl:label_name(SuccessLbl), + {[Offset,BinSize,Orig], ExCode} = + extract_matchstate_vars([offset,binsize,orig], Ms), + MakeCode = + [hipe_rtl:mk_alu(SizeReg, BinSize, sub, Offset)| + test_alignment_code(SizeReg,Unit,SLblName,FalseLblName)] ++ + [SuccessLbl| + construct_subbin(Dst1,SizeReg,Offset,Orig)] ++ + [update_offset(BinSize, Ms), + hipe_rtl:mk_goto(TrueLblName)], + ExCode ++ MakeCode. + +get_binary(Dst1, Ms, SizeReg, + UnSafe, TrueLblName, FalseLblName) -> + [SuccessLbl] = create_lbls(1), + [EndOffset] = create_gcsafe_regs(1), + {[Offset,BinSize,Orig], ExCode} = + extract_matchstate_vars([offset,binsize,orig], Ms), + CheckCode = + [check_size(Offset, SizeReg, BinSize, EndOffset, + UnSafe, hipe_rtl:label_name(SuccessLbl), + FalseLblName), + SuccessLbl], + MakeCode = + construct_subbin(Dst1,SizeReg,Offset,Orig) + ++ [update_offset(EndOffset, Ms), + hipe_rtl:mk_goto(TrueLblName)], + ExCode ++ CheckCode ++ MakeCode. + +construct_subbin(Dst,Size,Offset,Orig) -> + [BitOffset, ByteOffset, BitSize, ByteSize] = create_gcsafe_regs(4), + [hipe_rtl:mk_alu(ByteSize, Size, srl, hipe_rtl:mk_imm(?BYTE_SHIFT)), + hipe_rtl:mk_alu(BitSize, Size, 'and', hipe_rtl:mk_imm(?LOW_BITS)), + hipe_rtl:mk_alu(ByteOffset, Offset, srl, hipe_rtl:mk_imm(?BYTE_SHIFT)), + hipe_rtl:mk_alu(BitOffset, Offset, 'and', hipe_rtl:mk_imm(?LOW_BITS)), + hipe_tagscheme:mk_sub_binary(Dst, ByteSize, ByteOffset, + BitSize, BitOffset, Orig)]. + +%%%%%%%%%%%%%%%%%%%%%%%%% Skip Bits %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +skip_bits_all(1, Ms, TrueLblName,_FalseLblName) -> + {[BinSize], ExCode} = extract_matchstate_vars([binsize], Ms), + ExCode ++ + [update_offset(BinSize,Ms), + hipe_rtl:mk_goto(TrueLblName)]; +skip_bits_all(Unit,Ms, TrueLblName,FalseLblName) -> + [Size] = create_gcsafe_regs(1), + [SuccessLbl] = create_lbls(1), + SLblName = hipe_rtl:label_name(SuccessLbl), + {[Offset,BinSize], ExCode} = extract_matchstate_vars([offset,binsize], Ms), + ExCode ++ + [hipe_rtl:mk_alu(Size,BinSize,sub,Offset)] + ++ + test_alignment_code(Size,Unit,SLblName,FalseLblName) ++ + [SuccessLbl, + update_offset(BinSize,Ms), + hipe_rtl:mk_goto(TrueLblName)]. + +test_alignment_code(Size,Unit,SLblName,FalseLblName) -> + case Unit of + 1 -> [hipe_rtl:mk_goto(SLblName)]; + 2 -> get_fast_test_code(Size,1,SLblName,FalseLblName); + 4 -> get_fast_test_code(Size,3,SLblName,FalseLblName); + 8 -> get_fast_test_code(Size,7,SLblName,FalseLblName); + 16 -> get_fast_test_code(Size,15,SLblName,FalseLblName); + 32 -> get_fast_test_code(Size,31,SLblName,FalseLblName); + _ -> get_slow_test_code(Size,Unit,SLblName,FalseLblName) + end. + +get_fast_test_code(Size,AndTest,SLblName,FalseLblName) -> + [Tmp] = create_gcsafe_regs(1), + [hipe_rtl:mk_alub(Tmp,Size,'and',hipe_rtl:mk_imm(AndTest), + eq,SLblName,FalseLblName)]. + +%% This is really slow +get_slow_test_code(Size,Unit,SLblName,FalseLblName) -> + [Tmp] = create_gcsafe_regs(1), + [LoopLbl,Lbl1,Lbl2] = create_lbls(3), + LoopLblName = hipe_rtl:label_name(LoopLbl), + Lbl1Name = hipe_rtl:label_name(Lbl1), + Lbl2Name = hipe_rtl:label_name(Lbl2), + [hipe_rtl:mk_move(Tmp,Size), + LoopLbl, + hipe_rtl:mk_branch(Tmp, eq, hipe_rtl:mk_imm(0), SLblName, Lbl1Name), + Lbl1, + hipe_rtl:mk_branch(Tmp, lt, hipe_rtl:mk_imm(0), FalseLblName, Lbl2Name), + Lbl2, + hipe_rtl:mk_alu(Tmp,Tmp,sub,hipe_rtl:mk_imm(Unit)), + hipe_rtl:mk_goto(LoopLblName)]. + +skip_bits2(Ms, NoOfBits, TrueLblName, FalseLblName) -> + [NewOffset] = create_gcsafe_regs(1), + [TempLbl] = create_lbls(1), + {[Offset,BinSize], ExCode} = extract_matchstate_vars([offset,binsize], Ms), + ExCode ++ + add_to_offset(NewOffset, NoOfBits, Offset, FalseLblName) ++ + [hipe_rtl:mk_branch(BinSize, 'ltu', NewOffset, FalseLblName, + hipe_rtl:label_name(TempLbl), 0.01), + TempLbl, + update_offset(NewOffset,Ms), + hipe_rtl:mk_goto(TrueLblName)]. + +add_to_offset(Result, Extra, Original, FalseLblName) -> + TrueLbl = hipe_rtl:mk_new_label(), + %% Note: 'ltu' means 'unsigned overflow'. + [hipe_rtl:mk_alub(Result, Extra, 'add', Original, 'ltu', + FalseLblName, hipe_rtl:label_name(TrueLbl)), + TrueLbl]. + +%%%%%%%%%%%%%%%%%%%%%%% Code for start match %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +get_binary_bytes(Binary, BinSize, Base, Offset, Orig, + TrueLblName, FalseLblName) -> + [OrigOffset,BitSize,BitOffset] = create_gcsafe_regs(3), + [SuccessLbl,SubLbl,OtherLbl,JoinLbl] = create_lbls(4), + [hipe_tagscheme:test_bitstr(Binary, hipe_rtl:label_name(SuccessLbl), + FalseLblName, 0.99), + SuccessLbl, + get_field_from_term({sub_binary, binsize}, Binary, BinSize), + hipe_rtl:mk_alu(BinSize, BinSize, sll, hipe_rtl:mk_imm(?BYTE_SHIFT)), + hipe_tagscheme:test_subbinary(Binary, hipe_rtl:label_name(SubLbl), + hipe_rtl:label_name(OtherLbl)), + SubLbl, + get_field_from_term({sub_binary, offset}, Binary, OrigOffset), + hipe_rtl:mk_alu(Offset, OrigOffset, sll, hipe_rtl:mk_imm(?BYTE_SHIFT)), + get_field_from_term({sub_binary, bitoffset}, Binary, BitOffset), + hipe_rtl:mk_alu(Offset, Offset, add, BitOffset), + get_field_from_term({sub_binary, bitsize}, Binary, BitSize), + hipe_rtl:mk_alu(BinSize, BinSize, add, Offset), + hipe_rtl:mk_alu(BinSize, BinSize, add, BitSize), + get_field_from_term({sub_binary, orig}, Binary, Orig), + hipe_rtl:mk_goto(hipe_rtl:label_name(JoinLbl)), + OtherLbl, + hipe_rtl:mk_move(Offset, hipe_rtl:mk_imm(0)), + hipe_rtl:mk_move(Orig, Binary), + JoinLbl] ++ + get_base(Orig,Base) ++ + [hipe_rtl:mk_goto(TrueLblName)]. + +%%%%%%%%%%%%%%%%%%%%%%%%% UTILS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +get_base(Orig,Base) -> + [HeapLbl,REFCLbl,EndLbl] = create_lbls(3), + [hipe_tagscheme:test_heap_binary(Orig, hipe_rtl:label_name(HeapLbl), + hipe_rtl:label_name(REFCLbl)), + HeapLbl, + hipe_rtl:mk_alu(Base, Orig, add, hipe_rtl:mk_imm(?HEAP_BIN_DATA-2)), + hipe_rtl:mk_goto(hipe_rtl:label_name(EndLbl)), + REFCLbl, + hipe_rtl:mk_load(Base, Orig, hipe_rtl:mk_imm(?PROC_BIN_BYTES-2)), + EndLbl]. + +extract_matchstate_var(binsize, Ms) -> + BinSize = hipe_rtl:mk_new_reg_gcsafe(), + {BinSize, + get_field_from_term({matchstate, {matchbuffer, binsize}}, Ms, BinSize)}; +extract_matchstate_var(offset, Ms) -> + Offset = hipe_rtl:mk_new_reg_gcsafe(), + {Offset, + get_field_from_term({matchstate, {matchbuffer, offset}}, Ms, Offset)}; +extract_matchstate_var(base, Ms) -> + Base = hipe_rtl:mk_new_reg(), + {Base, + get_field_from_term({matchstate, {matchbuffer, base}}, Ms, Base)}; +extract_matchstate_var(orig, Ms) -> + Orig = hipe_rtl:mk_new_var(), + {Orig, + get_field_from_term({matchstate, {matchbuffer, orig}}, Ms, Orig)}. + +extract_matchstate_vars(List, Ms) -> + lists:unzip([extract_matchstate_var(Name, Ms) || Name <- List]). + +check_size(Offset, Size, BinSize, Tmp1, ContLblName, FalseLblName) -> + [add_to_offset(Tmp1, Offset, Size, FalseLblName), + hipe_rtl:mk_branch(Tmp1, leu, BinSize, ContLblName, FalseLblName, 0.99)]. + +check_size(Offset, Size, _BinSize, Tmp1, true, ContLblName, _FalseLblName) -> + [hipe_rtl:mk_alu(Tmp1, Offset, add, Size), + hipe_rtl:mk_goto(ContLblName)]; +check_size(Offset, Size, BinSize, Tmp1, false, ContLblName, FalseLblName) -> + check_size(Offset, Size, BinSize, Tmp1, ContLblName, FalseLblName). + +shift_type(true) -> + sra; +shift_type(false) -> + srl. + +get_type(true, LittleEndian) -> + {signed, endianess(LittleEndian)}; +get_type(false, LittleEndian) -> + {unsigned, endianess(LittleEndian)}. + +endianess(true) -> + little; +endianess(false) -> + big. + +aligned(Flags) -> + case Flags band ?BSF_ALIGNED of + 1 -> true; + 0 -> false + end. + +littleendian(Flags) -> + case Flags band 2 of + 2 -> true; + 0 -> false + end. + +signed(Flags) -> + case Flags band 4 of + 4 -> true; + 0 -> false + end. + +unsafe(Flags) -> + case Flags band 16 of + 16 -> true; + 0 -> false + end. + +update_offset(NewOffset, Ms) -> + set_field_from_term({matchstate,{matchbuffer,offset}}, + Ms, NewOffset). + +opt_update_ms([NewMs], OldMs) -> + [hipe_rtl:mk_move(NewMs, OldMs)]; +opt_update_ms([], _OldMs) -> + []. + +update_ms(NewMs, OldMs) -> + [hipe_rtl:mk_move(NewMs, OldMs)]. + +create_lbls(0) -> + []; +create_lbls(X) when X > 0-> + [hipe_rtl:mk_new_label()|create_lbls(X-1)]. + +make_dyn_prep(SizeReg, CCode) -> + [CLbl, SuccessLbl] = create_lbls(2), + Init = [hipe_rtl:mk_branch(SizeReg, le, hipe_rtl:mk_imm(?MAX_SMALL_BITS), + hipe_rtl:label_name(SuccessLbl), + hipe_rtl:label_name(CLbl)), + SuccessLbl], + End = [CLbl|CCode], + {Init, End}. + +%%------------------------------------------------------------------------ +%% From hipe_rtl_binutil.erl +%%------------------------------------------------------------------------ + +get_unaligned_int(Dst1, Size, Base, Offset, Shiftr, Type, TrueLblName) -> + [Reg] = create_regs(1), + [get_maybe_unaligned_int_to_reg(Reg, Size, Base, Offset, Shiftr, Type), + do_bignum_code(Size, Type, Reg, Dst1, TrueLblName)]. + +get_maybe_unaligned_int_to_reg(Reg, Size, Base, Offset, Shiftr, Type) -> + [LowBits] = create_regs(1), + [AlignedLbl, UnAlignedLbl, EndLbl] = create_lbls(3), + [hipe_rtl:mk_alub(LowBits, Offset, 'and', hipe_rtl:mk_imm(?LOW_BITS), + eq, hipe_rtl:label_name(AlignedLbl), + hipe_rtl:label_name(UnAlignedLbl)), + AlignedLbl, + get_int_to_reg(Reg, Size, Base, Offset, Shiftr, Type), + hipe_rtl:mk_goto(hipe_rtl:label_name(EndLbl)), + UnAlignedLbl, + get_unaligned_int_to_reg(Reg, Size, Base, Offset, LowBits, Shiftr, Type), + EndLbl]. + +get_unaligned_int_to_reg(Reg, Size, Base, Offset, LowBits, Shiftr, Type) -> + [ByteOffset, ShiftBits, LoadDst, Tmp, TotBits] = create_gcsafe_regs(5), + [MoreLbl, LessLbl, JoinLbl] = create_lbls(3), + WordSize = hipe_rtl_arch:word_size(), + MinLoad = (Size-1) div ?BYTE_SIZE +1, + MaxLoad = MinLoad + 1, + Code1 = + [hipe_rtl:mk_alu(TotBits, LowBits, 'add', hipe_rtl:mk_imm(Size)), + hipe_rtl:mk_alu(ByteOffset, Offset, 'srl', hipe_rtl:mk_imm(?BYTE_SHIFT))], + Code2 = + case {Size rem ?BYTE_SIZE, MinLoad} of + {1, _} -> + [load_bytes(LoadDst, Base, ByteOffset, Type, MinLoad), + hipe_rtl:mk_alu(ShiftBits, LowBits, 'add', + hipe_rtl:mk_imm((WordSize-MinLoad)*?BYTE_SIZE))]; + {_, WordSize} -> + UnsignedBig = {unsigned, big}, + [hipe_rtl:mk_branch(TotBits, le, hipe_rtl:mk_imm(MinLoad*?BYTE_SIZE), + hipe_rtl:label_name(LessLbl), + hipe_rtl:label_name(MoreLbl)), + LessLbl, + load_bytes(LoadDst, Base, ByteOffset, Type, MinLoad), + hipe_rtl:mk_alu(ShiftBits, LowBits, 'add', + hipe_rtl:mk_imm((WordSize-MinLoad)*?BYTE_SIZE)), + hipe_rtl:mk_goto(hipe_rtl:label_name(JoinLbl)), + MoreLbl, + load_bytes(LoadDst, Base, ByteOffset, UnsignedBig, MinLoad), + hipe_rtl:mk_alu(LoadDst, LoadDst, 'sll', LowBits), + load_bytes(Tmp, Base, ByteOffset, UnsignedBig, 1), + hipe_rtl:mk_alu(LowBits, hipe_rtl:mk_imm(?BYTE_SIZE), 'sub', LowBits), + hipe_rtl:mk_alu(Tmp, Tmp, 'srl', LowBits), + hipe_rtl:mk_alu(LoadDst, LoadDst, 'or', Tmp), + hipe_rtl:mk_move(ShiftBits, hipe_rtl:mk_imm(0)), + JoinLbl]; + {_, _} -> + [load_bytes(LoadDst, Base, ByteOffset, Type, MaxLoad), + hipe_rtl:mk_alu(ShiftBits, LowBits, 'add', + hipe_rtl:mk_imm((WordSize-MaxLoad)*?BYTE_SIZE))] + end, + Code3 = + [hipe_rtl:mk_alu(Tmp, LoadDst, sll, ShiftBits), + hipe_rtl:mk_alu(Reg, Tmp, Shiftr, + hipe_rtl:mk_imm(WordSize*?BYTE_SIZE-Size))], + Code1 ++ Code2 ++ Code3. + +get_int(Dst1, Size, Base, Offset, Shiftr, Type, TrueLblName) -> + [Reg] = create_gcsafe_regs(1), + [get_int_to_reg(Reg, Size, Base, Offset, Shiftr, Type), + do_bignum_code(Size, Type, Reg, Dst1, TrueLblName)]. + +get_int_to_reg(Reg, Size, Base, Offset, Shiftr, Type) -> + [ByteOffset] = create_gcsafe_regs(1), + Code1 = + [hipe_rtl:mk_alu(ByteOffset, Offset, srl, hipe_rtl:mk_imm(?BYTE_SHIFT)), + load_bytes(Reg, Base, ByteOffset, Type, ((Size-1) div ?BYTE_SIZE +1))], + Code2 = + case Size rem ?BYTE_SIZE of + 0 -> + []; + _ -> + [hipe_rtl:mk_alu(Reg, Reg, Shiftr, + hipe_rtl:mk_imm(?BYTE_SIZE -Size rem ?BYTE_SIZE))] + end, + Code1 ++ Code2. + +get_big_unknown_int(Dst1, Base, Offset, NewOffset, + Shiftr, Type, TrueLblName) -> + [LoadDst, ByteOffset, Limit, Tmp, LowBits] = create_gcsafe_regs(5), + [ContLbl, BackLbl, LoopLbl, TagLbl, LastLbl, EndLbl] = create_lbls(6), + [hipe_rtl:mk_move(LoadDst, hipe_rtl:mk_imm(0)), + hipe_rtl:mk_branch(NewOffset, ne, Offset, hipe_rtl:label_name(ContLbl), + hipe_rtl:label_name(TagLbl), 0.99), + ContLbl, + hipe_rtl:mk_alu(Limit, NewOffset, sub, hipe_rtl:mk_imm(1)), + hipe_rtl:mk_alu(Limit, Limit, srl, hipe_rtl:mk_imm(?BYTE_SHIFT)), + hipe_rtl:mk_alu(ByteOffset, Offset, srl, hipe_rtl:mk_imm(?BYTE_SHIFT)), + load_bytes(LoadDst, Base, ByteOffset, Type, 1), + BackLbl, + hipe_rtl:mk_branch(ByteOffset, le, Limit, hipe_rtl:label_name(LoopLbl), + hipe_rtl:label_name(EndLbl)), + LoopLbl, + load_bytes(Tmp, Base, ByteOffset, {unsigned, big}, 1), + hipe_rtl:mk_alu(LoadDst, LoadDst, sll, hipe_rtl:mk_imm(?BYTE_SIZE)), + hipe_rtl:mk_alu(LoadDst, LoadDst, 'or', Tmp), + hipe_rtl:mk_goto(hipe_rtl:label_name(BackLbl)), + EndLbl, + hipe_rtl:mk_alub(LowBits, NewOffset, 'and', hipe_rtl:mk_imm(?LOW_BITS), eq, + hipe_rtl:label_name(TagLbl), hipe_rtl:label_name(LastLbl)), + LastLbl, + hipe_rtl:mk_alu(LowBits, hipe_rtl:mk_imm(?BYTE_SIZE), 'sub', LowBits), + hipe_rtl:mk_alu(LoadDst, LoadDst, Shiftr, LowBits), + TagLbl] ++ + do_bignum_code(64, Type, LoadDst, Dst1, TrueLblName). + +get_little_unknown_int(Dst1, Base, Offset, NewOffset, + Shiftr, Type, TrueLblName) -> + [LoadDst, ByteOffset, Limit, ShiftReg, LowBits, Tmp] = create_gcsafe_regs(6), + [ContLbl, BackLbl, LoopLbl, DoneLbl, TagLbl] = create_lbls(5), + [hipe_rtl:mk_move(LoadDst, hipe_rtl:mk_imm(0)), + hipe_rtl:mk_branch(NewOffset, ne, Offset, hipe_rtl:label_name(ContLbl), + hipe_rtl:label_name(TagLbl), 0.99), + ContLbl, + hipe_rtl:mk_alu(Tmp, NewOffset, sub, hipe_rtl:mk_imm(1)), + hipe_rtl:mk_alu(ByteOffset, Offset, srl, hipe_rtl:mk_imm(?BYTE_SHIFT)), + hipe_rtl:mk_alu(Limit, Tmp, srl, hipe_rtl:mk_imm(?BYTE_SHIFT)), + hipe_rtl:mk_move(ShiftReg, hipe_rtl:mk_imm(0)), + BackLbl, + hipe_rtl:mk_branch(ByteOffset, lt, Limit, + hipe_rtl:label_name(LoopLbl), + hipe_rtl:label_name(DoneLbl)), + LoopLbl, + load_bytes(Tmp, Base, ByteOffset, {unsigned, big}, 1), + hipe_rtl:mk_alu(Tmp, Tmp, sll, ShiftReg), + hipe_rtl:mk_alu(ShiftReg, ShiftReg, add, hipe_rtl:mk_imm(?BYTE_SIZE)), + hipe_rtl:mk_alu(LoadDst, LoadDst, 'or', Tmp), + hipe_rtl:mk_goto(hipe_rtl:label_name(BackLbl)), + DoneLbl, + hipe_rtl:mk_alu(LowBits, NewOffset, 'and', hipe_rtl:mk_imm(?LOW_BITS)), + hipe_rtl:mk_alu(LowBits, hipe_rtl:mk_imm(?BYTE_SIZE), sub, LowBits), + hipe_rtl:mk_alu(LowBits, LowBits, 'and', hipe_rtl:mk_imm(?LOW_BITS)), + load_bytes(Tmp, Base, ByteOffset, Type, 1), + hipe_rtl:mk_alu(Tmp, Tmp, Shiftr, LowBits), + hipe_rtl:mk_alu(Tmp, Tmp, sll, ShiftReg), + hipe_rtl:mk_alu(LoadDst, LoadDst, 'or', Tmp), + TagLbl] ++ + do_bignum_code(64, Type, LoadDst, Dst1, TrueLblName). + +do_bignum_code(Size, {Signedness,_}, Src, Dst1, TrueLblName) + when is_integer(Size) -> + case {Size > ?MAX_SMALL_BITS, Signedness} of + {false, _} -> + [hipe_tagscheme:tag_fixnum(Dst1, Src), + hipe_rtl:mk_goto(TrueLblName)]; + {true, signed} -> + make_int_gc_code(Size) ++ + signed_bignum(Dst1, Src, TrueLblName); + {true, unsigned} -> + make_int_gc_code(Size) ++ + unsigned_bignum(Dst1, Src, TrueLblName) + end. + +signed_bignum(Dst1, Src, TrueLblName) -> + Tmp1 = hipe_rtl:mk_new_reg(), + BignumLabel = hipe_rtl:mk_new_label(), + [hipe_tagscheme:realtag_fixnum(Dst1, Src), + hipe_tagscheme:realuntag_fixnum(Tmp1, Dst1), + hipe_rtl:mk_branch(Tmp1, eq, Src, TrueLblName, + hipe_rtl:label_name(BignumLabel)), + BignumLabel, + hipe_tagscheme:unsafe_mk_big(Dst1, Src, signed), + hipe_rtl:mk_goto(TrueLblName)]. + +unsigned_bignum(Dst1, Src, TrueLblName) -> + Tmp1 = hipe_rtl:mk_new_reg_gcsafe(), + BignumLbl = hipe_rtl:mk_new_label(), + BignumLblName = hipe_rtl:label_name(BignumLbl), + NxtLbl = hipe_rtl:mk_new_label(), + NxtLblName = hipe_rtl:label_name(NxtLbl), + [hipe_rtl:mk_branch(Src, lt, hipe_rtl:mk_imm(0), BignumLblName, NxtLblName), + NxtLbl, + hipe_tagscheme:realtag_fixnum(Dst1, Src), + hipe_tagscheme:realuntag_fixnum(Tmp1, Dst1), + hipe_rtl:mk_branch(Tmp1, eq, Src, TrueLblName, BignumLblName), + BignumLbl, + hipe_tagscheme:unsafe_mk_big(Dst1, Src, unsigned), + hipe_rtl:mk_goto(TrueLblName)]. + +load_bytes(Dst, Base, Offset, {Signedness, _Endianess},1) -> + [hipe_rtl:mk_load(Dst, Base, Offset, byte, Signedness), + hipe_rtl:mk_alu(Offset, Offset, add, hipe_rtl:mk_imm(1))]; +load_bytes(Dst, Base, Offset, {Signedness, Endianess},2) -> + case Endianess of + big -> + hipe_rtl_arch:load_big_2(Dst, Base, Offset, Signedness); + little -> + hipe_rtl_arch:load_little_2(Dst, Base, Offset, Signedness) + end; +load_bytes(Dst, Base, Offset, {Signedness, Endianess},3) -> + Tmp1 = hipe_rtl:mk_new_reg(), + case Endianess of + big -> + [hipe_rtl:mk_load(Dst, Base, Offset, byte, Signedness), + hipe_rtl:mk_alu(Offset, Offset, add, hipe_rtl:mk_imm(1)), + hipe_rtl:mk_alu(Dst, Dst, sll, hipe_rtl:mk_imm(8)), + hipe_rtl:mk_load(Tmp1, Base, Offset, byte, unsigned), + hipe_rtl:mk_alu(Dst, Dst, 'or', Tmp1), + hipe_rtl:mk_alu(Offset, Offset, add, hipe_rtl:mk_imm(1)), + hipe_rtl:mk_alu(Dst, Dst, sll, hipe_rtl:mk_imm(8)), + hipe_rtl:mk_load(Tmp1, Base, Offset, byte, unsigned), + hipe_rtl:mk_alu(Dst, Dst, 'or', Tmp1), + hipe_rtl:mk_alu(Offset, Offset, add, hipe_rtl:mk_imm(1))]; + little -> + [hipe_rtl:mk_load(Dst, Base, Offset, byte, unsigned), + hipe_rtl:mk_alu(Offset, Offset, add, hipe_rtl:mk_imm(1)), + hipe_rtl:mk_load(Tmp1, Base, Offset, byte,unsigned), + hipe_rtl:mk_alu(Tmp1, Tmp1, sll, hipe_rtl:mk_imm(8)), + hipe_rtl:mk_alu(Dst, Dst, 'or', Tmp1), + hipe_rtl:mk_alu(Offset, Offset, add, hipe_rtl:mk_imm(1)), + hipe_rtl:mk_load(Tmp1, Base, Offset, byte,Signedness), + hipe_rtl:mk_alu(Tmp1, Tmp1, sll, hipe_rtl:mk_imm(16)), + hipe_rtl:mk_alu(Dst, Dst, 'or', Tmp1), + hipe_rtl:mk_alu(Offset, Offset, add, hipe_rtl:mk_imm(1))] + end; +load_bytes(Dst, Base, Offset, {Signedness, Endianess}, 4) -> + case Endianess of + big -> + hipe_rtl_arch:load_big_4(Dst, Base, Offset, Signedness); + little -> + hipe_rtl_arch:load_little_4(Dst, Base, Offset, Signedness) + end; + +load_bytes(Dst, Base, Offset, {Signedness, Endianess}, X) when X > 1 -> + [LoopLbl, EndLbl] = create_lbls(2), + [Tmp1, Limit, TmpOffset] = create_regs(3), + case Endianess of + big -> + [hipe_rtl:mk_alu(Limit, Offset, add, hipe_rtl:mk_imm(X)), + hipe_rtl:mk_load(Dst, Base, Offset, byte, Signedness), + hipe_rtl:mk_alu(Offset, Offset, add, hipe_rtl:mk_imm(1)), + LoopLbl, + hipe_rtl:mk_load(Tmp1, Base, Offset, byte, unsigned), + hipe_rtl:mk_alu(Offset, Offset, add, hipe_rtl:mk_imm(1)), + hipe_rtl:mk_alu(Dst, Dst, sll, hipe_rtl:mk_imm(8)), + hipe_rtl:mk_alu(Dst, Dst, 'or', Tmp1), + hipe_rtl:mk_branch(Offset, lt, Limit, hipe_rtl:label_name(LoopLbl), + hipe_rtl:label_name(EndLbl)), + EndLbl]; + little -> + [hipe_rtl:mk_alu(Limit, Offset, add, hipe_rtl:mk_imm(X)), + hipe_rtl:mk_alu(TmpOffset, Limit, sub, hipe_rtl:mk_imm(1)), + hipe_rtl:mk_load(Dst, Base, TmpOffset, byte, Signedness), + LoopLbl, + hipe_rtl:mk_alu(TmpOffset, TmpOffset, sub, hipe_rtl:mk_imm(1)), + hipe_rtl:mk_load(Tmp1, Base, TmpOffset, byte, Signedness), + hipe_rtl:mk_alu(Dst, Dst, sll, hipe_rtl:mk_imm(8)), + hipe_rtl:mk_alu(Dst, Dst, 'or', Tmp1), + hipe_rtl:mk_branch(Offset, lt, TmpOffset, hipe_rtl:label_name(LoopLbl), + hipe_rtl:label_name(EndLbl)), + EndLbl, + hipe_rtl:mk_move(Offset, Limit)] + end. + +create_regs(X) when X > 0 -> + [hipe_rtl:mk_new_reg()|create_regs(X-1)]; +create_regs(0) -> + []. + +create_gcsafe_regs(X) when X > 0 -> + [hipe_rtl:mk_new_reg_gcsafe()|create_gcsafe_regs(X-1)]; +create_gcsafe_regs(0) -> + []. + +first_part(Var, Register, FalseLblName) -> + [SuccessLbl1, SuccessLbl2] = create_lbls(2), + [hipe_tagscheme:test_fixnum(Var, hipe_rtl:label_name(SuccessLbl1), + FalseLblName, 0.99), + SuccessLbl1, + hipe_tagscheme:fixnum_ge(Var, hipe_rtl:mk_imm(hipe_tagscheme:mk_fixnum(0)), + hipe_rtl:label_name(SuccessLbl2), FalseLblName, 0.99), + SuccessLbl2, + hipe_tagscheme:untag_fixnum(Register, Var)]. + +make_size(1, BitsVar, FalseLblName) -> + [DstReg] = create_regs(1), + {first_part(BitsVar, DstReg, FalseLblName), DstReg}; +make_size(?BYTE_SIZE, BitsVar, FalseLblName) -> + [DstReg] = create_regs(1), + Code = + first_part(BitsVar, DstReg, FalseLblName) ++ + [hipe_rtl:mk_alu(DstReg, DstReg, sll, hipe_rtl:mk_imm(?BYTE_SHIFT))], + {Code, DstReg}; +make_size(UnitImm, BitsVar, FalseLblName) -> + [DstReg] = create_regs(1), + UnitList = number2list(UnitImm), + Code = multiply_code(UnitList, BitsVar, DstReg, FalseLblName), + {Code, DstReg}. + +multiply_code(List=[Head|_Tail], Variable, Result, FalseLblName) -> + Test = set_high(Head), + Tmp1 = hipe_rtl:mk_new_reg(), + SuccessLbl = hipe_rtl:mk_new_label(), + Register = hipe_rtl:mk_new_reg(), + Code = [hipe_rtl:mk_move(Result, hipe_rtl:mk_imm(0))| + first_part(Variable, Register, FalseLblName)] + ++ + [hipe_rtl:mk_alub(Tmp1, Register, 'and', hipe_rtl:mk_imm(Test), + eq, hipe_rtl:label_name(SuccessLbl), + FalseLblName, 0.99), + SuccessLbl], + multiply_code(List, Register, Result, FalseLblName, Tmp1, Code). + +multiply_code([ShiftSize|Rest], Register, Result, FalseLblName, Tmp1, OldCode) -> + SuccessLbl = hipe_rtl:mk_new_label(), + Code = OldCode ++ [hipe_rtl:mk_alu(Tmp1, Register, sll, hipe_rtl:mk_imm(ShiftSize)), + hipe_rtl:mk_alub(Result, Tmp1, 'add', Result, not_overflow, hipe_rtl:label_name(SuccessLbl), FalseLblName, 0.99), + SuccessLbl], + multiply_code(Rest, Register, Result, FalseLblName, Tmp1, Code); +multiply_code([], _Register, _Result, _FalseLblName, _Tmp1, Code) -> + Code. + +number2list(X) when is_integer(X), X >= 0 -> + number2list(X, []). + +number2list(1, Acc) -> + lists:reverse([0|Acc]); +number2list(0, Acc) -> + lists:reverse(Acc); +number2list(X, Acc) -> + F = floorlog2(X), + number2list(X-(1 bsl F), [F|Acc]). + +floorlog2(X) -> + round(math:log(X)/math:log(2)-0.5). + +set_high(X) -> + set_high(X, 0). + +set_high(0, Y) -> + Y; +set_high(X, Y) -> + set_high(X-1, Y+(1 bsl (27-X))). + +is_illegal_const(Const) -> + Const >= 1 bsl (hipe_rtl_arch:word_size() * ?BYTE_SIZE) orelse Const < 0. diff --git a/lib/hipe/rtl/hipe_rtl_cfg.erl b/lib/hipe/rtl/hipe_rtl_cfg.erl new file mode 100644 index 0000000000..b6c1d63262 --- /dev/null +++ b/lib/hipe/rtl/hipe_rtl_cfg.erl @@ -0,0 +1,201 @@ +%% -*- erlang-indent-level: 2 -*- +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2001-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_cfg). + +-export([init/1, + labels/1, + params/1, params_update/2, + start_label/1, + succ/2, + pred/2, + bb/2, bb_add/3, bb_insert_between/5, + redirect/4, + remove_trivial_bbs/1, remove_unreachable_code/1, + linearize/1, + pp/1, pp/2]). +-export([preorder/1, postorder/1, reverse_postorder/1]). + +-define(RTL_CFG, true). % needed for cfg.inc below + +-include("../main/hipe.hrl"). +-include("hipe_rtl.hrl"). +-include("../flow/cfg.hrl"). +-include("../flow/cfg.inc"). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% +%% CFG interface to RTL. +%% + +init(Rtl) -> + %% hipe_rtl:pp(Rtl), + Code = hipe_rtl:rtl_code(Rtl), + StartLabel = hipe_rtl:label_name(hd(Code)), + CFG0 = mk_empty_cfg(hipe_rtl:rtl_fun(Rtl), + StartLabel, + hipe_rtl:rtl_data(Rtl), + hipe_rtl:rtl_is_closure(Rtl), + hipe_rtl:rtl_is_leaf(Rtl), + hipe_rtl:rtl_params(Rtl)), + CFG = info_update(CFG0, hipe_rtl:rtl_info(Rtl)), + take_bbs(Code, CFG). + +%% @spec is_comment(hipe_rtl:rtl_instruction()) -> boolean() +%% @doc Succeeds if Instr has no effect. +is_comment(Instr) -> + hipe_rtl:is_comment(Instr). + +%% @spec is_goto(hipe_rtl:rtl_instruction()) -> boolean() +%% @doc Succeeds if Instr is just a jump (no side-effects). +is_goto(Instr) -> + hipe_rtl:is_goto(Instr). + +is_label(Instr) -> + hipe_rtl:is_label(Instr). + +label_name(Instr) -> + hipe_rtl:label_name(Instr). + +mk_label(Name) -> + hipe_rtl:mk_label(Name). + +mk_goto(Name) -> + hipe_rtl:mk_goto(Name). + +branch_successors(Instr) -> + case Instr of + #branch{} -> [hipe_rtl:branch_true_label(Instr), + hipe_rtl:branch_false_label(Instr)]; + #alub{} -> [hipe_rtl:alub_true_label(Instr), + hipe_rtl:alub_false_label(Instr)]; + #switch{} -> hipe_rtl:switch_labels(Instr); + #call{} -> + case hipe_rtl:call_fail(Instr) of + [] -> [hipe_rtl:call_continuation(Instr)]; + Fail -> [hipe_rtl:call_continuation(Instr),Fail] + end; + #goto{} -> [hipe_rtl:goto_label(Instr)]; + #goto_index{} -> hipe_rtl:goto_index_labels(Instr); + _ -> [] + end. + +fails_to(Instr) -> + case Instr of + #call{} -> [hipe_rtl:call_fail(Instr)]; + _ -> [] + end. + +is_branch(Instr) -> + case Instr of + #branch{} -> true; + #alub{} -> true; + #switch{} -> true; + #goto{} -> true; + #goto_index{} -> true; + #enter{} -> true; + #return{} -> true; + #call{} -> + case hipe_rtl:call_fail(Instr) of + [] -> + case hipe_rtl:call_continuation(Instr) of + [] -> false; + _ -> true + end; + _ -> true + end; + _ -> false + end. + +is_pure_branch(Instr) -> + case Instr of + #branch{} -> true; + #switch{} -> true; + #goto{} -> true; + _ -> false + end. + +redirect_jmp(Jmp, ToOld, ToNew) -> + hipe_rtl:redirect_jmp(Jmp, ToOld, ToNew). + +redirect_ops([Label|Labels], CFG, Map) -> + BB = bb(CFG, Label), + Code = hipe_bb:code(BB), + NewCode = [rewrite(I,Map) || I <- Code], + NewCFG = bb_add(CFG, Label, hipe_bb:code_update(BB, NewCode)), + redirect_ops(Labels, NewCFG, Map); +redirect_ops([],CFG,_) -> CFG. + +rewrite(I, Map) -> + case I of + #load_address{} -> + case hipe_rtl:load_address_type(I) of + constant -> I; + _ -> + NewL = + find_new_label(hipe_rtl:load_address_addr(I), Map), + hipe_rtl:load_address_addr_update(I, NewL) + end; + _ -> I + end. + + +pp(CFG) -> + hipe_rtl:pp(linearize(CFG)). + +pp(Dev, CFG) -> + hipe_rtl:pp(Dev, linearize(CFG)). + +linearize(CFG) -> + Code = linearize_cfg(CFG), + Rtl = hipe_rtl:mk_rtl(function(CFG), + params(CFG), + is_closure(CFG), + is_leaf(CFG), + Code, + data(CFG), + hipe_gensym:var_range(rtl), + hipe_gensym:label_range(rtl)), + hipe_rtl:rtl_info_update(Rtl, info(CFG)). + +%% %% Warning: this arity might not be the true arity. +%% %% The true arity of a closure usually differs. +%% arity(CFG) -> +%% {_M,_F,A} = function(CFG), +%% A. + +%% init_gensym(CFG)-> +%% HighestVar = find_highest_var(CFG), +%% HighestLabel = find_highest_label(CFG), +%% hipe_gensym:init(), +%% hipe_gensym:set_var(rtl, HighestVar), +%% hipe_gensym:set_label(rtl, HighestLabel). +%% +%% highest_var(Code)-> +%% hipe_rtl:highest_var(Code). + +is_phi(I) -> + hipe_rtl:is_phi(I). + +phi_remove_pred(I, Pred) -> + hipe_rtl:phi_remove_pred(I, Pred). + +phi_redirect_pred(I, OldPred, NewPred) -> + hipe_rtl:phi_redirect_pred(I, OldPred, NewPred). diff --git a/lib/hipe/rtl/hipe_rtl_cleanup_const.erl b/lib/hipe/rtl/hipe_rtl_cleanup_const.erl new file mode 100644 index 0000000000..d3e71a56c1 --- /dev/null +++ b/lib/hipe/rtl/hipe_rtl_cleanup_const.erl @@ -0,0 +1,85 @@ +%%% -*- erlang-indent-level: 2 -*- +%%% +%%% %CopyrightBegin% +%%% +%%% Copyright Ericsson AB 2004-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% +%%% +%%%------------------------------------------------------------------- +%%% File : hipe_rtl_cleanup_const.erl +%%% Author : Tobias Lindahl <[email protected]> +%%% Description : +%%% +%%% Created : 5 Mar 2004 by Tobias Lindahl <[email protected]> +%%%------------------------------------------------------------------- + +%% Big constants (floats, bignums) can be used as arguments to +%% arbitrary instructions in RTL. Since these are located in the +%% constants area and the only instruction that currently can access +%% them is load_address, the constants have to be moved out of the +%% instruction and loaded into temporary variables before the +%% instruction. +%% +%% Some backends can make use of the information that the arguments +%% are really constants. Here is the place to add new backend-specific +%% behaviour depending on this. + +%%-------------------------------------------------------------------- + +-module(hipe_rtl_cleanup_const). + +-export([cleanup/1]). + +-include("hipe_rtl.hrl"). + +%%-------------------------------------------------------------------- + +%%-spec cleanup(#rtl{}) -> #rtl{}. + +cleanup(Rtl) -> + Code = cleanup(hipe_rtl:rtl_code(Rtl), []), + hipe_rtl:rtl_code_update(Rtl, Code). + +cleanup([I|Left], Acc) -> + Args = hipe_rtl:args(I), + case [X || X <- Args, hipe_rtl:is_const_label(X)] of + [] -> + cleanup(Left, [I|Acc]); + ConstArgs -> + NewIns = cleanup_instr(ConstArgs, I), + cleanup(Left, NewIns ++ Acc) + end; +cleanup([], Acc) -> + lists:reverse(Acc). + +cleanup_instr(Consts, I) -> + cleanup_instr(ordsets:from_list(Consts), I, []). + +cleanup_instr([Const|Left], I, Acc) -> + Dst = hipe_rtl:mk_new_var(), + ConstLabel = hipe_rtl:const_label_label(Const), + Load = hipe_rtl:mk_load_address(Dst, ConstLabel, constant), + case I of + X when is_record(X, fp_unop) orelse is_record(X, fp) -> + Fdst = hipe_rtl:mk_new_fpreg(), + Fconv = hipe_tagscheme:unsafe_untag_float(Fdst, Dst), + NewI = hipe_rtl:subst_uses([{Const, Fdst}], I), + cleanup_instr(Left, NewI, Fconv ++ [Load|Acc]); + _ -> + NewI = hipe_rtl:subst_uses([{Const, Dst}], I), + cleanup_instr(Left, NewI, [Load|Acc]) + end; +cleanup_instr([], I, Acc) -> + [I|Acc]. diff --git a/lib/hipe/rtl/hipe_rtl_exceptions.erl b/lib/hipe/rtl/hipe_rtl_exceptions.erl new file mode 100644 index 0000000000..879b84c0b0 --- /dev/null +++ b/lib/hipe/rtl/hipe_rtl_exceptions.erl @@ -0,0 +1,120 @@ +%% -*- erlang-indent-level: 2 -*- +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2001-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% +%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Copyright (c) 2001 by Erik Johansson. All Rights Reserved +%% ==================================================================== +%% Filename : hipe_rtl_exceptions.erl +%% Module : hipe_rtl_exceptions +%% Purpose : +%% Notes : +%% History : * 2001-04-10 Erik Johansson ([email protected]): +%% Created. +%% CVS : +%% $Id$ +%% ==================================================================== +%% Exports : +%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-module(hipe_rtl_exceptions). + +-export([gen_fail/3, gen_begin_handler/3]). + +-include("../main/hipe.hrl"). +-include("hipe_literals.hrl"). + +%% -------------------------------------------------------------------- +%% Handle the Icode instruction +%% FAIL +%% +gen_fail(Class, Args, L) -> + case Args of + [Reason] -> + case Class of + exit -> + gen_exit(Reason, L); + throw -> + gen_throw(Reason, L); + error -> + gen_error(Reason, L) + end; + [Arg1,Arg2] -> + case Class of + error -> + Reason = Arg1, ArgList = Arg2, + gen_error(Reason, ArgList, L); + rethrow -> + Exception = Arg1, Reason = Arg2, + gen_rethrow(Exception, Reason, L) + end + end. + +%% -------------------------------------------------------------------- +%% Exception handler glue; interfaces between the runtime system's +%% exception state and the Icode view of exception handling. + +gen_begin_handler(I, VarMap, ConstTab) -> + Ds = hipe_icode:begin_handler_dstlist(I), + {Vars, VarMap1} = hipe_rtl_varmap:ivs2rvs(Ds, VarMap), + [FTagVar,FValueVar,FTraceVar] = Vars, + {[hipe_rtl:mk_comment('begin_handler'), + hipe_rtl_arch:pcb_load(FValueVar, ?P_FVALUE), + hipe_rtl_arch:pcb_load(FTraceVar, ?P_FTRACE), + %% synthesized from P->freason by hipe_handle_exception() + hipe_rtl_arch:pcb_load(FTagVar, ?P_ARG0) + ], + VarMap1, ConstTab}. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% +%% Exceptions + +gen_exit(Reason, L) -> + gen_fail_call({erlang,exit,1}, [Reason], L). + +gen_throw(Reason, L) -> + gen_fail_call({erlang,throw,1}, [Reason], L). + +gen_error(Reason, L) -> + gen_fail_call({erlang,error,1}, [Reason], L). + +gen_error(Reason, ArgList, L) -> + gen_fail_call({erlang,error,2}, [Reason,ArgList], L). + +gen_rethrow(Exception, Reason, L) -> + gen_fail_call(rethrow, [Exception,Reason], L). + +%% Generic fail. We can't use 'enter' with a fail label (there can be no +%% stack descriptor info for an enter), so for a non-nil fail label we +%% generate a call followed by a dummy return. +%% +%% Update: The runtime system now interprets the return address of +%% the BIF call in order to list the invoking MFA in the stack trace. +%% Generating tailcalls here defeats that purpose, so we no longer do that. + +%%gen_fail_call(Fun, Args, []) -> +%% [hipe_rtl:mk_enter(Fun, Args, remote)]; +gen_fail_call(Fun, Args, L) -> + ContLbl = hipe_rtl:mk_new_label(), + Cont = hipe_rtl:label_name(ContLbl), + Zero = hipe_rtl:mk_imm(hipe_tagscheme:mk_fixnum(0)), + [hipe_rtl:mk_call([], Fun, Args, Cont, L, remote), + ContLbl, + hipe_rtl:mk_return([Zero])]. diff --git a/lib/hipe/rtl/hipe_rtl_lcm.erl b/lib/hipe/rtl/hipe_rtl_lcm.erl new file mode 100644 index 0000000000..5d65389d48 --- /dev/null +++ b/lib/hipe/rtl/hipe_rtl_lcm.erl @@ -0,0 +1,1696 @@ +%% -*- erlang-indent-level: 2 -*- +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2004-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% +%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% File : hipe_rtl_lcm.erl +%% Author : Henrik Nyman and Erik Cedheim +%% Description : Performs Lazy Code Motion on RTL +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% @doc +%% +%% This module implements Lazy Code Motion on RTL. +%% +%% @end +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-module(hipe_rtl_lcm). + +-export([rtl_lcm/2]). + +-define(SETS, ordsets). %% Which set implementation module to use + %% We have tried gb_sets, sets and ordsets and + %% ordsets seems to be a lot faster according to + %% our test runs. + +-include("../main/hipe.hrl"). +-include("hipe_rtl.hrl"). +-include("../flow/cfg.hrl"). + +%%-define(LCM_DEBUG, true). %% When defined and true, produces debug printouts + +%%============================================================================= + +%% +%% @doc Performs Lazy Code Motion on RTL. +%% + +-spec rtl_lcm(cfg(), comp_options()) -> cfg(). + +rtl_lcm(CFG, Options) -> + %% Perform pre-calculation of the data sets. + ?opt_start_timer("RTL LCM precalc"), + {NodeInfo, EdgeInfo, AllExpr, ExprMap, IdMap, Labels} = lcm_precalc(CFG, Options), + ?opt_stop_timer("RTL LCM precalc"), + %% {NodeInfo, EdgeInfo, AllExpr, ExprMap, Labels} = + %% ?option_time(lcm_precalc(CFG, Options), "RTL LCM precalc", Options), + + pp_debug("-------------------------------------------------~n",[]), + %% pp_debug( "~w~n", [MFA]), + + %% A check if we should pretty print the result. + case proplists:get_bool(pp_rtl_lcm, Options) of + true-> + pp_debug("-------------------------------------------------~n",[]), + %% pp_debug("AllExpr: ~w~n", [AllExpr]), + pp_debug("AllExpr:~n", []), + pp_exprs(ExprMap, IdMap, ?SETS:to_list(AllExpr)), + %% pp_sets(ExprMap, NodeInfo, EdgeInfo, AllExpr, CFG2<-ERROR!, Labels); + pp_sets(ExprMap, IdMap, NodeInfo, EdgeInfo, AllExpr, CFG, Labels); + _ -> + ok + end, + + pp_debug("-------------------------------------------------~n",[]), + ?option_time({CFG1, MoveSet} = perform_lcm(CFG, NodeInfo, EdgeInfo, ExprMap, + IdMap, AllExpr, mk_edge_bb_map(), + ?SETS:new(), Labels), + "RTL LCM perform_lcm", Options), + + %% Scan through list of moved expressions and replace their + %% assignments with the new temporary created for that expression + MoveList = ?SETS:to_list(MoveSet), + ?option_time(CFG2 = moved_expr_replace_assignments(CFG1, ExprMap, IdMap, + MoveList), + "RTL LCM moved_expr_replace_assignments", Options), + pp_debug("-------------------------------------------------~n~n",[]), + + CFG2. + +%%============================================================================= +%% Performs lazy code motion given the pre-calculated data sets. +perform_lcm(CFG, _, _, _, _, _, _, MoveSet, []) -> + {CFG, MoveSet}; +perform_lcm(CFG0, NodeInfo, EdgeInfo, ExprMap, IdMap, AllExp, BetweenMap, + MoveSet0, [Label|Labels]) -> + Code0 = hipe_bb:code(hipe_rtl_cfg:bb(CFG0, Label)), + DeleteSet = delete(NodeInfo, Label), + + %% Check if something should be deleted from this block. + {CFG1, MoveSet1} = + case ?SETS:size(DeleteSet) > 0 of + true -> + pp_debug("Label ~w: Expressions Deleted: ~n", [Label]), + Code1 = delete_exprs(Code0, ExprMap, IdMap, ?SETS:to_list(DeleteSet)), + BB = hipe_bb:mk_bb(Code1), + {hipe_rtl_cfg:bb_add(CFG0, Label, BB), + ?SETS:union(MoveSet0, DeleteSet)}; + false -> + {CFG0, MoveSet0} + end, + + Succs = hipe_rtl_cfg:succ(CFG1, Label), + + %% Go through the list of successors and insert expression where needed. + %% Also collect a list of expressions that are inserted somewhere + {CFG2, NewBetweenMap, MoveSet2} = + lists:foldl(fun(Succ, {CFG, BtwMap, MoveSet}) -> + InsertSet = calc_insert_edge(NodeInfo, EdgeInfo, + Label, Succ), + %% Check if something should be inserted on this edge. + case ?SETS:size(InsertSet) > 0 of + true -> + pp_debug("Label ~w: Expressions Inserted for Successor: ~w~n", [Label, Succ]), + InsertList = ?SETS:to_list(InsertSet), + {NewCFG, NewBtwMap} = + insert_exprs(CFG, Label, Succ, ExprMap, IdMap, + BtwMap, InsertList), + {NewCFG, NewBtwMap, ?SETS:union(MoveSet, InsertSet)}; + false -> + {CFG, BtwMap, MoveSet} + end + end, + {CFG1, BetweenMap, MoveSet1}, Succs), + + perform_lcm(CFG2, NodeInfo, EdgeInfo, ExprMap, IdMap, AllExp, NewBetweenMap, + MoveSet2, Labels). + +%%============================================================================= +%% Scan through list of moved expressions and replace their +%% assignments with the new temporary created for that expression. +moved_expr_replace_assignments(CFG, _, _, []) -> + CFG; +moved_expr_replace_assignments(CFG0, ExprMap, IdMap, [ExprId|Exprs]) -> + Expr = expr_id_map_get_expr(IdMap, ExprId), + case expr_map_lookup(ExprMap, Expr) of + {value, {_, ReplaceList, NewReg}} -> + CFG1 = lists:foldl(fun({Label, Reg}, CFG) -> + %% Find and replace expression in block + pp_debug("Label ~w: Expressions Replaced:~n", [Label]), + Code0 = hipe_bb:code(hipe_rtl_cfg:bb(CFG, Label)), + Code1 = + moved_expr_do_replacement(expr_set_dst(Expr, Reg), + Reg, NewReg, Code0), + hipe_rtl_cfg:bb_add(CFG, Label, hipe_bb:mk_bb(Code1)) + end, CFG0, ReplaceList), + moved_expr_replace_assignments(CFG1, ExprMap, IdMap, Exprs); + none -> + moved_expr_replace_assignments(CFG0, ExprMap, IdMap, Exprs) + end. + +moved_expr_do_replacement(_, _, _, []) -> + []; +moved_expr_do_replacement(Expr, Reg, NewReg, [Expr|Instrs]) -> + NewExpr = expr_set_dst(Expr, NewReg), + Move = mk_expr_move_instr(Reg, NewReg), + pp_debug(" Replacing:~n", []), + pp_debug_instr(Expr), + pp_debug(" With:~n", []), + pp_debug_instr(NewExpr), + pp_debug_instr(Move), + [NewExpr, Move | moved_expr_do_replacement(Expr, Reg, NewReg, Instrs)]; +moved_expr_do_replacement(Expr, Reg, NewReg, [Instr|Instrs]) -> + [Instr | moved_expr_do_replacement(Expr, Reg, NewReg, Instrs)]. + +%%============================================================================= +%% Goes through the given list of expressions and deletes them from the code. +%% NOTE We do not actually delete an expression, but instead we replace it +%% with an assignment from the new temporary containing the result of the +%% expressions which is guaranteed to have been calculated earlier in +%% the code. +delete_exprs(Code, _, _, []) -> + Code; +delete_exprs(Code, ExprMap, IdMap, [ExprId|Exprs]) -> + Expr = expr_id_map_get_expr(IdMap, ExprId), + %% Perform a foldl that goes through the code and deletes all + %% occurences of the expression. + NewCode = + lists:reverse + (lists:foldl(fun(CodeExpr, Acc) -> + case is_expr(CodeExpr) of + true -> + case expr_clear_dst(CodeExpr) =:= Expr of + true -> + pp_debug(" Deleting: ", []), + pp_debug_instr(CodeExpr), + %% Lookup expression entry. + Defines = + case expr_map_lookup(ExprMap, Expr) of + {value, {_, _, Defs}} -> + Defs; + none -> + exit({?MODULE, expr_map_lookup, + "expression missing"}) + end, + MoveCode = + mk_expr_move_instr(hipe_rtl:defines(CodeExpr), + Defines), + pp_debug(" Replacing with: ", []), + pp_debug_instr(MoveCode), + [MoveCode|Acc]; + false -> + [CodeExpr|Acc] + end; + false -> + [CodeExpr|Acc] + end + end, + [], Code)), + delete_exprs(NewCode, ExprMap, IdMap, Exprs). + +%%============================================================================= +%% Goes through the given list of expressions and inserts them at +%% appropriate places in the code. +insert_exprs(CFG, _, _, _, _, BetweenMap, []) -> + {CFG, BetweenMap}; +insert_exprs(CFG, Pred, Succ, ExprMap, IdMap, BetweenMap, [ExprId|Exprs]) -> + Expr = expr_id_map_get_expr(IdMap, ExprId), + Instr = expr_map_get_instr(ExprMap, Expr), + case hipe_rtl_cfg:succ(CFG, Pred) of + [_] -> + pp_debug(" Inserted last: ", []), + pp_debug_instr(Instr), + NewCFG = insert_expr_last(CFG, Pred, Instr), + insert_exprs(NewCFG, Pred, Succ, ExprMap, IdMap, BetweenMap, Exprs); + _ -> + case hipe_rtl_cfg:pred(CFG, Succ) of + [_] -> + pp_debug(" Inserted first: ", []), + pp_debug_instr(Instr), + NewCFG = insert_expr_first(CFG, Succ, Instr), + insert_exprs(NewCFG, Pred, Succ, ExprMap, IdMap, BetweenMap, Exprs); + _ -> + pp_debug(" Inserted between: ", []), + pp_debug_instr(Instr), + {NewCFG, NewBetweenMap} = + insert_expr_between(CFG, BetweenMap, Pred, Succ, Instr), + insert_exprs(NewCFG, Pred, Succ, ExprMap, IdMap, NewBetweenMap, Exprs) + end + end. + +%%============================================================================= +%% Recursively goes through the code in a block and returns a new block +%% with the new code inserted second to last (assuming the last expression +%% is a branch operation). +insert_expr_last(CFG0, Label, Instr) -> + Code0 = hipe_bb:code(hipe_rtl_cfg:bb(CFG0, Label)), + %% FIXME: Use hipe_bb:butlast() instead? + Code1 = insert_expr_last_work(Label, Instr, Code0), + hipe_rtl_cfg:bb_add(CFG0, Label, hipe_bb:mk_bb(Code1)). + +%%============================================================================= +%% Recursively goes through the code in a block and returns a new block +%% with the new code inserted second to last (assuming the last expression +%% is a branch operation). +insert_expr_last_work(_, Instr, []) -> + %% This case should not happen since this means that block was completely + %% empty when the function was called. For compability we insert it last. + [Instr]; +insert_expr_last_work(_, Instr, [Code1]) -> + %% We insert the code next to last. + [Instr, Code1]; +insert_expr_last_work(Label, Instr, [Code|Codes]) -> + [Code|insert_expr_last_work(Label, Instr, Codes)]. + +%%============================================================================= +%% Inserts expression first in the block for the given label. +insert_expr_first(CFG0, Label, Instr) -> + %% The first instruction is always a label + [Lbl|Code0] = hipe_bb:code(hipe_rtl_cfg:bb(CFG0, Label)), + Code1 = [Lbl, Instr | Code0], + hipe_rtl_cfg:bb_add(CFG0, Label, hipe_bb:mk_bb(Code1)). + +%%============================================================================= +%% Inserts an expression on and edge between two existing blocks. +%% It creates a new basic block to hold the expression. +%% Created bbs are inserted into BetweenMap to be able to reuse them for +%% multiple inserts on the same edge. +%% NOTE Currently creates multiple blocks for identical expression with the +%% same successor. Since the new bb usually contains very few instructions +%% this should not be a problem. +insert_expr_between(CFG0, BetweenMap, Pred, Succ, Instr) -> + PredSucc = {Pred, Succ}, + case edge_bb_map_lookup(BetweenMap, PredSucc) of + none -> + NewLabel = hipe_rtl:mk_new_label(), + NewLabelName = hipe_rtl:label_name(NewLabel), + pp_debug(" Creating new bb ~w~n", [NewLabel]), + Code = [Instr, hipe_rtl:mk_goto(Succ)], + CFG1 = hipe_rtl_cfg:bb_add(CFG0, NewLabelName, hipe_bb:mk_bb(Code)), + CFG2 = hipe_rtl_cfg:redirect(CFG1, Pred, Succ, NewLabelName), + NewBetweenMap = edge_bb_map_insert(BetweenMap, PredSucc, NewLabelName), + pp_debug(" Mapping edge (~w,~w) to label ~w~n", + [Pred, Succ, NewLabelName]), + {CFG2, NewBetweenMap}; + {value, Label} -> + pp_debug(" Using existing new bb for edge (~w,~w) with label ~w~n", + [Pred, Succ, Label]), + {insert_expr_last(CFG0, Label, Instr), BetweenMap} + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%% GENERAL UTILITY FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%============================================================================= +%% Returns true if the list of registers only contains virtual registers and +%% no machine registers. +no_machine_regs([]) -> + true; +no_machine_regs([Reg|Regs]) -> + case hipe_rtl:is_reg(Reg) of + true -> + N = hipe_rtl:reg_index(Reg), + (N >= hipe_rtl_arch:first_virtual_reg()) andalso no_machine_regs(Regs); + _ -> + case hipe_rtl:is_fpreg(Reg) of + true -> + N = hipe_rtl:fpreg_index(Reg), + (N >= hipe_rtl_arch:first_virtual_reg()) andalso no_machine_regs(Regs); + _ -> + no_machine_regs(Regs) + end + end. + +%%============================================================================= +%% Returns true if an RTL instruction is an expression. +%% +is_expr(I) -> + Defines = hipe_rtl:defines(I), + Uses = hipe_rtl:uses(I), + + %% We don't cosider something that doesn't define anything as an expression. + %% Also we don't consider machine registers to be expressions. + case length(Defines) > 0 andalso no_machine_regs(Defines) + andalso no_machine_regs(Uses) of + true -> + case I of + #alu{} -> true; +%% #alu{} -> +%% Dst = hipe_rtl:alu_dst(I), +%% Src1 = hipe_rtl:alu_src1(I), +%% Src2 = hipe_rtl:alu_src2(I), + + %% Check if dst updates src +%% case Dst =:= Src1 orelse Dst =:= Src2 of +%% true -> +%% false; +%% false -> +%% true +%% end; + + %% Check if alu expression is untagging of boxed (rX <- vX sub 2) +%% case hipe_rtl:is_reg(Dst) andalso hipe_rtl:is_var(Src1) andalso +%% (hipe_rtl:alu_op(I) =:= sub) andalso hipe_rtl:is_imm(Src2) of +%% true -> +%% case hipe_rtl:imm_value(Src2) of +%% 2 -> false; %% Tag for boxed. TODO: Should not be hardcoded... +%% _ -> true +%% end; +%% false -> +%% true +%% end; + + #alub{} -> false; %% TODO: Split instruction to consider alu expression? + #branch{} -> false; + #call{} -> false; %% We cannot prove that a call has no side-effects + #comment{} -> false; + #enter{} -> false; + %% #fail_to{} -> false; %% Deprecated? + #fconv{} -> true; + #fixnumop{} -> true; + #fload{} -> true; + #fmove{} -> false; + #fp{} -> true; + #fp_unop{} -> true; + #fstore{} -> false; + #goto{} -> false; + #goto_index{} -> false; + #gctest{} -> false; + #label{} -> false; + #load{} -> true; + #load_address{} -> + case hipe_rtl:load_address_type(I) of + c_const -> false; + closure -> false; %% not sure whether safe to move; + %% also probably not worth it + constant -> true + end; + #load_atom{} -> true; + #load_word_index{} -> true; + #move{} -> false; + #multimove{} -> false; + #phi{} -> false; + #return{} -> false; + #store{} -> false; + #switch{} -> false + end; + false -> + false + end. + +%%============================================================================= +%% Replaces destination of RTL expression with empty list. +%% +expr_set_dst(I, [Dst|_Dsts] = DstList) -> + case I of + #alu{} -> hipe_rtl:alu_dst_update(I, Dst); + #call{} -> hipe_rtl:call_dstlist_update(I, DstList); + #fconv{} -> hipe_rtl:fconv_dst_update(I, Dst); + #fixnumop{} -> hipe_rtl:fixnumop_dst_update(I, Dst); + #fload{} -> hipe_rtl:fload_dst_update(I, Dst); + %% #fmove{} -> hipe_rtl:fmove_dst_update(I, Dst); + #fp{} -> hipe_rtl:fp_dst_update(I, Dst); + #fp_unop{} -> hipe_rtl:fp_unop_dst_update(I, Dst); + #load{} -> hipe_rtl:load_dst_update(I, Dst); + #load_address{} -> hipe_rtl:load_address_dst_update(I, Dst); + #load_atom{} -> hipe_rtl:load_atom_dst_update(I, Dst); + #load_word_index{} -> hipe_rtl:load_word_index_dst_update(I, Dst); + %% #move{} -> hipe_rtl:move_dst_update(I, Dst); + _ -> exit({?MODULE, expr_set_dst, "bad expression"}) + end. + +%%============================================================================= +%% Replaces destination of RTL expression with empty list. +%% +expr_clear_dst(I) -> + case I of + #alu{} -> hipe_rtl:alu_dst_update(I, nil); + #call{} -> hipe_rtl:call_dstlist_update(I, nil); + #fconv{} -> hipe_rtl:fconv_dst_update(I, nil); + #fixnumop{} -> hipe_rtl:fixnumop_dst_update(I, nil); + #fload{} -> hipe_rtl:fload_dst_update(I, nil); + %% #fmove{} -> hipe_rtl:fmove_dst_update(I, nil); + #fp{} -> hipe_rtl:fp_dst_update(I, nil); + #fp_unop{} -> hipe_rtl:fp_unop_dst_update(I, nil); + #load{} -> hipe_rtl:load_dst_update(I, nil); + #load_address{} -> hipe_rtl:load_address_dst_update(I, nil); + #load_atom{} -> hipe_rtl:load_atom_dst_update(I, nil); + #load_word_index{} -> hipe_rtl:load_word_index_dst_update(I, nil); + %% #move{} -> hipe_rtl:move_dst_update(I, nil); + _ -> exit({?MODULE, expr_clear_dst, "bad expression"}) + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%% PRECALC FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%============================================================================= +%% Pre-calculates the flow analysis and puts the calculated sets in maps for +%% easy access later. +lcm_precalc(CFG, Options) -> + %% Calculate use map and expression map. + ?option_time({ExprMap, IdMap} = mk_expr_map(CFG), + "RTL LCM mk_expr_map", Options), + ?option_time(UseMap = mk_use_map(CFG, ExprMap), + "RTL LCM mk_use_map", Options), + %% Labels = hipe_rtl_cfg:reverse_postorder(CFG), + Labels = hipe_rtl_cfg:labels(CFG), + %% StartLabel = hipe_rtl_cfg:start_label(CFG), + %% AllExpr = all_exprs(CFG, Labels), + AllExpr = ?SETS:from_list(gb_trees:keys(IdMap)), + + %% Calculate the data sets. + ?option_time(NodeInfo0 = mk_node_info(Labels), "RTL LCM mk_node_info", + Options), + %% ?option_time(EdgeInfo0 = mk_edge_info(), "RTL LCM mk_edge_info", + %% Options), + EdgeInfo0 = mk_edge_info(), + ?option_time(NodeInfo1 = calc_up_exp(CFG, ExprMap, NodeInfo0, Labels), + "RTL LCM calc_up_exp", Options), + ?option_time(NodeInfo2 = calc_down_exp(CFG, ExprMap, NodeInfo1, Labels), + "RTL LCM calc_down_exp", Options), + ?option_time(NodeInfo3 = calc_killed_expr(CFG, NodeInfo2, UseMap, AllExpr, + Labels), + "RTL LCM calc_killed_exp", Options), + ?option_time(NodeInfo4 = calc_avail(CFG, NodeInfo3), + "RTL LCM calc_avail", Options), + ?option_time(NodeInfo5 = calc_antic(CFG, NodeInfo4, AllExpr), + "RTL LCM calc_antic", Options), + ?option_time(EdgeInfo1 = calc_earliest(CFG, NodeInfo5, EdgeInfo0, Labels), + "RTL LCM calc_earliest", Options), + ?option_time({NodeInfo6, EdgeInfo2} = calc_later(CFG, NodeInfo5, EdgeInfo1), + "RTL LCM calc_later", Options), + ?option_time(NodeInfo7 = calc_delete(CFG, NodeInfo6, Labels), + "RTL LCM calc_delete", Options), + {NodeInfo7, EdgeInfo2, AllExpr, ExprMap, IdMap, Labels}. + +%%%%%%%%%%%%%%%%%%% AVAILABLE IN/OUT FLOW ANALYSIS %%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Fixpoint calculation of anticipated in/out sets. +%% Uses a worklist algorithm. +%% Performs the avail in/out flow analysis. + +%%============================================================================= +%% Calculates the available in/out sets, and returns an updated NodeInfo. + +calc_avail(CFG, NodeInfo) -> + StartLabel = hipe_rtl_cfg:start_label(CFG), + Work = init_work([StartLabel]), + %% Initialize start node + NewNodeInfo = set_avail_in(NodeInfo, StartLabel, ?SETS:new()), + calc_avail_fixpoint(Work, CFG, NewNodeInfo). + +calc_avail_fixpoint(Work, CFG, NodeInfo) -> + case get_work(Work) of + fixpoint -> + NodeInfo; + {Label, NewWork} -> + {NewNodeInfo, NewLabels} = calc_avail_node(Label, CFG, NodeInfo), + NewWork2 = add_work(NewWork, NewLabels), + calc_avail_fixpoint(NewWork2, CFG, NewNodeInfo) + end. + +calc_avail_node(Label, CFG, NodeInfo) -> + %% Get avail in + AvailIn = avail_in(NodeInfo, Label), + + %% Calculate avail out + AvailOut = ?SETS:union(down_exp(NodeInfo, Label), + ?SETS:subtract(AvailIn, + killed_expr(NodeInfo, Label))), + + {Changed, NodeInfo2} = + case avail_out(NodeInfo, Label) of + none -> + %% If there weren't any old avail out we use this one. + {true, set_avail_out(NodeInfo, Label, AvailOut)}; + OldAvailOut -> + %% Check if the avail outs are equal. + case AvailOut =:= OldAvailOut of + true -> + {false, NodeInfo}; + false -> + {true, set_avail_out(NodeInfo, Label, AvailOut)} + end + end, + + case Changed of + true -> + %% Update AvailIn-sets of successors and add them to worklist + Succs = hipe_rtl_cfg:succ(CFG, Label), + NodeInfo3 = + lists:foldl + (fun(Succ, NewNodeInfo) -> + case avail_in(NewNodeInfo, Succ) of + none -> + %% Initialize avail in to all expressions + set_avail_in(NewNodeInfo, Succ, AvailOut); + OldAvailIn -> + set_avail_in(NewNodeInfo, Succ, + ?SETS:intersection(OldAvailIn, AvailOut)) + end + end, + NodeInfo2, Succs), + {NodeInfo3, Succs}; + false -> + {NodeInfo2, []} + end. + +%%%%%%%%%%%%%%%%%% ANTICIPATED IN/OUT FLOW ANALYSIS %%%%%%%%%%%%%%%%%%%%%%%%%% +%% Fixpoint calculation of anticipated in/out sets. +%% Uses a worklist algorithm. + +%%============================================================================= +%% Calculates the anicipated in/out sets, and returns an updated NodeInfo. +calc_antic(CFG, NodeInfo, AllExpr) -> + %% Initialize worklist with all nodes in postorder + Labels = hipe_rtl_cfg:postorder(CFG), + Work = init_work(Labels), + calc_antic_fixpoint(Work, CFG, NodeInfo, AllExpr). + +calc_antic_fixpoint(Work, CFG, NodeInfo, AllExpr) -> + case get_work(Work) of + fixpoint -> + NodeInfo; + {Label, NewWork} -> + {NewNodeInfo, NewLabels} = calc_antic_node(Label, CFG, NodeInfo, AllExpr), + NewWork2 = add_work(NewWork, NewLabels), + calc_antic_fixpoint(NewWork2, CFG, NewNodeInfo, AllExpr) + end. + +calc_antic_node(Label, CFG, NodeInfo, AllExpr) -> + %% Get antic out + AnticOut = + case antic_out(NodeInfo, Label) of + none -> + case is_exit_label(CFG, Label) of + true -> + ?SETS:new(); + false -> + AllExpr + end; + + AnticOutTemp -> AnticOutTemp + end, + + %% Calculate antic in + AnticIn = ?SETS:union(up_exp(NodeInfo, Label), + ?SETS:subtract(AnticOut, + killed_expr(NodeInfo, Label))), + {Changed, NodeInfo2} = + case antic_in(NodeInfo, Label) of + %% If there weren't any old antic in we use this one. + none -> + {true, set_antic_in(NodeInfo, Label, AnticIn)}; + + OldAnticIn -> + %% Check if the antic in:s are equal. + case AnticIn =:= OldAnticIn of + true -> + {false, NodeInfo}; + false -> + {true, + set_antic_in(NodeInfo, Label, AnticIn)} + end + end, + + case Changed of + true -> + %% Update AnticOut-sets of predecessors and add them to worklist + Preds = hipe_rtl_cfg:pred(CFG, Label), + NodeInfo3 = + lists:foldl + (fun(Pred, NewNodeInfo) -> + case antic_out(NewNodeInfo, Pred) of + none -> + %% Initialize antic out to all expressions + set_antic_out(NewNodeInfo, Pred, AnticIn); + OldAnticOut -> + set_antic_out(NewNodeInfo, Pred, + ?SETS:intersection(OldAnticOut, AnticIn)) + end + end, + NodeInfo2, Preds), + {NodeInfo3, Preds}; + false -> + {NodeInfo2, []} + end. + +%%%%%%%%%%%%%%%%%%%%% LATER / LATER IN FLOW ANALYSIS %%%%%%%%%%%%%%%%%%%%%%%%%% +%% Fixpoint calculations of Later and LaterIn sets. +%% Uses a worklist algorithm. +%% Note that the Later set is calculated on edges. + +%%============================================================================= +%% Calculates the Later and LaterIn sets, and returns updates of both +%% NodeInfo (with LaterIn sets) and EdgeInfo (with Later sets). + +calc_later(CFG, NodeInfo, EdgeInfo) -> + StartLabel = hipe_rtl_cfg:start_label(CFG), + Work = init_work([{node, StartLabel}]), + %% Initialize start node + NewNodeInfo = set_later_in(NodeInfo, StartLabel, ?SETS:new()), + calc_later_fixpoint(Work, CFG, NewNodeInfo, EdgeInfo). + +calc_later_fixpoint(Work, CFG, NodeInfo, EdgeInfo) -> + case get_work(Work) of + {{edge, From, To}, Work2} -> + {NewNodeInfo, NewEdgeInfo, AddWork} = + calc_later_edge(From, To, CFG, NodeInfo, EdgeInfo), + Work3 = add_work(Work2, AddWork), + calc_later_fixpoint(Work3, CFG, NewNodeInfo, NewEdgeInfo); + {{node, Label}, Work2} -> + AddWork = calc_later_node(Label, CFG), + Work3 = add_work(Work2, AddWork), + calc_later_fixpoint(Work3, CFG, NodeInfo, EdgeInfo); + fixpoint -> + {NodeInfo, EdgeInfo} + end. + +calc_later_node(Label, CFG) -> + Succs = hipe_rtl_cfg:succ(CFG, Label), + [{edge, Label, Succ} || Succ <- Succs]. + +calc_later_edge(From, To, _CFG, NodeInfo, EdgeInfo) -> + FromTo = {From, To}, + Earliest = earliest(EdgeInfo, FromTo), + LaterIn = later_in(NodeInfo, From), + UpExp = up_exp(NodeInfo, From), + Later = ?SETS:union(Earliest, ?SETS:subtract(LaterIn, UpExp)), + {Changed, EdgeInfo2} = + case lookup_later(EdgeInfo, FromTo) of + none -> {true, set_later(EdgeInfo, FromTo, Later)}; + Later -> {false, EdgeInfo}; + _Old -> {true, set_later(EdgeInfo, FromTo, Later)} + end, + case Changed of + true -> + %% Update later in set of To-node + case lookup_later_in(NodeInfo, To) of + %% If the data isn't set initialize to all expressions + none -> + {set_later_in(NodeInfo, To, Later), EdgeInfo2, [{node, To}]}; + OldLaterIn -> + NewLaterIn = ?SETS:intersection(OldLaterIn, Later), + %% Check if something changed + %% FIXME: Implement faster equality test? + case NewLaterIn =:= OldLaterIn of + true -> + {NodeInfo, EdgeInfo2, []}; + false -> + {set_later_in(NodeInfo, To, NewLaterIn), + EdgeInfo2, [{node, To}]} + end + end; + false -> + {NodeInfo, EdgeInfo2, []} + end. + +%%%%%%%%%%%%%%%%%% UPWARDS/DOWNWARDS EXPOSED EXPRESSIONS %%%%%%%%%%%%%%%%%%%%%% +%% Calculates upwards and downwards exposed expressions. + +%%============================================================================= +%% Calculates the downwards exposed expression sets for the given labels in +%% the CFG. +calc_down_exp(_, _, NodeInfo, []) -> + NodeInfo; +calc_down_exp(CFG, ExprMap, NodeInfo, [Label|Labels]) -> + Code = hipe_bb:code(hipe_rtl_cfg:bb(CFG, Label)), + %% Data = ?SETS:from_list(lists:map(fun expr_clear_dst/1, exp_work(Code))), + Data = ?SETS:from_list(get_expr_ids(ExprMap, exp_work(Code))), + NewNodeInfo = set_down_exp(NodeInfo, Label, Data), + calc_down_exp(CFG, ExprMap, NewNodeInfo, Labels). + +%%============================================================================= +%% Calculates the upwards exposed expressions sets for the given labels in +%% the CFG. +calc_up_exp(_, _, NodeInfo, []) -> + NodeInfo; +calc_up_exp(CFG, ExprMap, NodeInfo, [Label|Labels]) -> + BB = hipe_rtl_cfg:bb(CFG, Label), + RevCode = lists:reverse(hipe_bb:code(BB)), + Data = ?SETS:from_list(get_expr_ids(ExprMap, exp_work(RevCode))), + NewNodeInfo = set_up_exp(NodeInfo, Label, Data), + calc_up_exp(CFG, ExprMap, NewNodeInfo, Labels). + +%%============================================================================= +%% Given a list of expression instructions, gets a list of expression ids +%% from an expression map. +get_expr_ids(ExprMap, Instrs) -> + [expr_map_get_id(ExprMap, expr_clear_dst(I)) || I <- Instrs]. + +%%============================================================================= +%% Does the work of the calc_*_exp functions. +exp_work(Code) -> + exp_work([], Code). + +exp_work([], [Instr|Instrs]) -> + case is_expr(Instr) of + true -> + exp_work([Instr], Instrs); + false -> + exp_work([], Instrs) + end; +exp_work(Exprs, []) -> + Exprs; +exp_work(Exprs, [Instr|Instrs]) -> + NewExprs = case is_expr(Instr) of + true -> + exp_kill_expr(Instr, [Instr|Exprs]); + false -> + exp_kill_expr(Instr, Exprs) + end, + exp_work(NewExprs, Instrs). + +%%============================================================================= +%% Checks if the given instruction redefines any operands of +%% instructions in the instruction list. +%% It returns the list of expressions with those instructions that has +%% operands redefined removed. +exp_kill_expr(_Instr, []) -> + []; +exp_kill_expr(Instr, [CheckedExpr|Exprs]) -> + %% Calls, gctests and stores potentially clobber everything + case Instr of + #call{} -> []; + #gctest{} -> []; + #store{} -> []; %% FIXME: Only regs and vars clobbered, not fregs... + #fstore{} -> + %% fstore potentially clobber float expressions + [ExprDefine|_] = hipe_rtl:defines(CheckedExpr), + case hipe_rtl:is_fpreg(ExprDefine) of + true -> + exp_kill_expr(Instr, Exprs); + false -> + [CheckedExpr | exp_kill_expr(Instr, Exprs)] + end; + _ -> + InstrDefines = hipe_rtl:defines(Instr), + ExprUses = hipe_rtl:uses(CheckedExpr), + Diff = ExprUses -- InstrDefines, + case length(Diff) < length(ExprUses) of + true -> + exp_kill_expr(Instr, Exprs); + false -> + [CheckedExpr | exp_kill_expr(Instr, Exprs)] + end + end. + +%%%%%%%%%%%%%%%%%%%%%%%% KILLED EXPRESSIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%============================================================================= +%% Calculates the killed expression sets for all given labels. +calc_killed_expr(_, NodeInfo, _, _, []) -> + NodeInfo; +calc_killed_expr(CFG, NodeInfo, UseMap, AllExpr, [Label|Labels]) -> + Code = hipe_bb:code(hipe_rtl_cfg:bb(CFG, Label)), + KilledExprs = calc_killed_expr_bb(Code, UseMap, AllExpr, ?SETS:new()), + NewNodeInfo = set_killed_expr(NodeInfo, Label, KilledExprs), + calc_killed_expr(CFG, NewNodeInfo, UseMap, AllExpr, Labels). + +%%============================================================================= +%% Calculates the killed expressions set for one basic block. +calc_killed_expr_bb([], _UseMap, _AllExpr, KilledExprs) -> + KilledExprs; +calc_killed_expr_bb([Instr|Instrs], UseMap, AllExpr, KilledExprs) -> + %% Calls, gctests and stores potentially clobber everything + case Instr of + #call{} -> AllExpr; + #gctest{} -> AllExpr; + #store{} -> AllExpr; %% FIXME: Only regs and vars clobbered, not fregs... + #fstore{} -> + %% Kill all float expressions + %% FIXME: Make separate function is_fp_expr + ?SETS:from_list + (lists:foldl(fun(Expr, Fexprs) -> + [Define|_] = hipe_rtl:defines(Expr), + case hipe_rtl:is_fpreg(Define) of + true -> + [Expr|Fexprs]; + false -> + Fexprs + end + end, [], ?SETS:to_list(AllExpr))); + _ -> + case hipe_rtl:defines(Instr) of + [] -> + calc_killed_expr_bb(Instrs, UseMap, AllExpr, KilledExprs); + [Define|_] -> + NewKilledExprs = use_map_get_expr_uses(UseMap, Define), + calc_killed_expr_bb(Instrs, UseMap, AllExpr, + ?SETS:union(NewKilledExprs, KilledExprs)) + end + end. +%%%%%%%%%%%%%%%%%%%%%%%%%%%% EARLIEST %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%============================================================================= +%% Calculates the earliest set for all edges in the CFG. + +calc_earliest(_, _, EdgeInfo, []) -> + EdgeInfo; +calc_earliest(CFG, NodeInfo, EdgeInfo, [To|Labels]) -> + EmptySet = ?SETS:new(), + Preds = hipe_rtl_cfg:pred(CFG, To), + NewEdgeInfo = + case EmptySet =:= antic_in(NodeInfo, To) of + true -> + %% Earliest is empty for all edges into this block. + lists:foldl(fun(From, EdgeInfoAcc) -> + set_earliest(EdgeInfoAcc, {From, To}, EmptySet) + end, EdgeInfo, Preds); + false -> + lists:foldl(fun(From, EdgeInfoAcc) -> + IsStartLabel = (From =:= hipe_rtl_cfg:start_label(CFG)), + Earliest = + calc_earliest_edge(NodeInfo, IsStartLabel, From, To), + set_earliest(EdgeInfoAcc, {From, To}, Earliest) + end, EdgeInfo, Preds) + end, + calc_earliest(CFG, NodeInfo, NewEdgeInfo, Labels). + +%%============================================================================= +%% Calculates the earliest set for one edge. + +calc_earliest_edge(NodeInfo, IsStartLabel, From, To) -> + AnticIn = antic_in(NodeInfo, To), + AvailOut = avail_out(NodeInfo, From), + + case IsStartLabel of + true -> + ?SETS:subtract(AnticIn, AvailOut); + false -> + AnticOut = antic_out(NodeInfo, From), + ExprKill = killed_expr(NodeInfo, From), + ?SETS:subtract(?SETS:subtract(AnticIn, AvailOut), + ?SETS:subtract(AnticOut, ExprKill)) + end. +%% The above used to be: +%% +%% ?SETS:intersection(?SETS:subtract(AnticIn, AvailOut), +%% ?SETS:union(ExprKill, ?SETS:subtract(AllExpr, AnticOut))) +%% +%% But it is costly to use the AllExpr, so let's do some tricky set algebra. +%% +%% Let A = AnticIn, B = AvailOut, C = ExprKill, D = AnticOut, U = AllExpr +%% Let n = intersection, u = union, ' = inverse +%% +%% Then +%% (A - B) n (C u (U - D)) = <Remove D unless it is in C> +%% = (A - B) n ((C u U) - (D - C)) = <But U is the whole universe> +%% = (A - B) n (U - (D - C)) = <We are really meaning the complement> +%% = (A - B) n (D - C)' = <Intersection w complement is subtraction> +%% = (A - B) - (D - C) <Simple enough, let's stop> +%% +%% or in other words +%% ?SETS:subtract(?SETS:subtract(AnticIn, AvailOut), +%% ?SETS:subtract(AnticOut, ExprKill)) + + + +%%%%%%%%%%%%%%%%%%%%%%%% INSERT / DELETE %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%============================================================================= +%% Calculates the insert set for one edge and returns the resulting set. +%% NOTE This does not modify the EdgeInfo set, since the resulting set is +%% returned and used immediately, instead of being pre-calculated as are +%% the other sets. +calc_insert_edge(NodeInfo, EdgeInfo, From, To) -> + Later = later(EdgeInfo, {From, To}), + LaterIn = later_in(NodeInfo, To), + ?SETS:subtract(Later, LaterIn). + +%%============================================================================= +%% Calculates the delete set for all given labels in a CFG. +calc_delete(_, NodeInfo, []) -> + NodeInfo; +calc_delete(CFG, NodeInfo, [Label|Labels]) -> + case Label =:= hipe_rtl_cfg:start_label(CFG) of + true -> + NewNodeInfo = set_delete(NodeInfo, Label, ?SETS:new()); + false -> + UpExp = up_exp(NodeInfo, Label), + LaterIn = later_in(NodeInfo, Label), + Delete = ?SETS:subtract(UpExp, LaterIn), + NewNodeInfo = set_delete(NodeInfo, Label, Delete) + end, + calc_delete(CFG, NewNodeInfo, Labels). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%% FIXPOINT FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%============================================================================= +%% Worklist used by the fixpoint calculations. +%% +%% We use gb_sets here, which is optimized for continuous inserts and +%% membership tests. + +init_work(Labels) -> + {Labels, [], gb_sets:from_list(Labels)}. + +get_work({[Label|Left], List, Set}) -> + NewWork = {Left, List, gb_sets:delete(Label, Set)}, + {Label, NewWork}; +get_work({[], [], _Set}) -> + fixpoint; +get_work({[], List, Set}) -> + get_work({lists:reverse(List), [], Set}). + +add_work(Work = {List1, List2, Set}, [Label|Labels]) -> + case gb_sets:is_member(Label, Set) of + true -> + add_work(Work, Labels); + false -> + %%io:format("Adding work: ~w\n", [Label]), + add_work({List1, [Label|List2], gb_sets:insert(Label, Set)}, Labels) + end; +add_work(Work, []) -> + Work. + +%%============================================================================= +%% Calculates the labels that are the exit labels. +%% FIXME We do not detect dead-end loops spanning more than one block. +%% This could potentially cause a bug in the future... +%% exit_labels(CFG) -> +%% Labels = hipe_rtl_cfg:labels(CFG), +%% lists:foldl(fun(Label, ExitLabels) -> +%% Succs = hipe_rtl_cfg:succ(CFG, Label), +%% case Succs of +%% [] -> +%% [Label|ExitLabels]; +%% [Label] -> %% Count single bb dead-end loops as exit labels +%% [Label|ExitLabels]; +%% _ -> +%% ExitLabels +%% end +%% end, [], Labels ). + +%%============================================================================= +%% Return true if label is an exit label, +%% i.e. its bb has no successors or itself as only successor. +is_exit_label(CFG, Label) -> + case hipe_rtl_cfg:succ(CFG, Label) of + [] -> true; + [Label] -> true; + _ -> false + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%% DATASET FUNCTIONS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% The dataset is a collection of data about the CFG. +%% It is divided into two parts, NodeInfo and EdgeInfo. +%% The pre-calculation step stores the calculated sets here. + +-record(node_data, {up_exp = none, + down_exp = none, + killed_expr = none, + avail_in = none, + avail_out = none, + antic_in = none, + antic_out = none, + later_in = none, + delete = none}). + +-record(edge_data, {earliest = none, + later = none, + insert = none}). + +%%============================================================================= +%% Creates a node info from a CFG (one entry for each Label). +mk_node_info(Labels) -> + lists:foldl(fun(Label, DataTree) -> + gb_trees:insert(Label, #node_data{}, DataTree) + %%gb_trees:enter(Label, #node_data{}, DataTree) + end, + gb_trees:empty(), Labels). + +%%mk_edge_info(Labels) -> +%% FIXME Should we traverse cfg and initialize edges? +mk_edge_info() -> + gb_trees:empty(). + +%%============================================================================= +%% Get methods +up_exp(NodeInfo, Label) -> + Data = gb_trees:get(Label, NodeInfo), + Data#node_data.up_exp. + +down_exp(NodeInfo, Label) -> + Data = gb_trees:get(Label, NodeInfo), + Data#node_data.down_exp. + +killed_expr(NodeInfo, Label) -> + Data = gb_trees:get(Label, NodeInfo), + Data#node_data.killed_expr. + +avail_in(NodeInfo, Label) -> + Data = gb_trees:get(Label, NodeInfo), + Data#node_data.avail_in. + +avail_out(NodeInfo, Label) -> + Data = gb_trees:get(Label, NodeInfo), + Data#node_data.avail_out. + +antic_in(NodeInfo, Label) -> + Data = gb_trees:get(Label, NodeInfo), + Data#node_data.antic_in. + +antic_out(NodeInfo, Label) -> + Data = gb_trees:get(Label, NodeInfo), + Data#node_data.antic_out. + +later_in(NodeInfo, Label) -> + Data = gb_trees:get(Label, NodeInfo), + Data#node_data.later_in. + +lookup_later_in(NodeInfo, Label) -> + case gb_trees:lookup(Label, NodeInfo) of + none -> + none; + {value, #node_data{later_in = Data}} -> + Data + end. + +delete(NodeInfo, Label) -> + Data = gb_trees:get(Label, NodeInfo), + Data#node_data.delete. + +earliest(EdgeInfo, Edge) -> + Data = gb_trees:get(Edge, EdgeInfo), + Data#edge_data.earliest. + +-ifdef(LOOKUP_EARLIEST_NEEDED). +lookup_earliest(EdgeInfo, Edge) -> + case gb_trees:lookup(Edge, EdgeInfo) of + none -> + none; + {value, #edge_data{earliest = Data}} -> + Data + end. +-endif. + +later(EdgeInfo, Edge) -> + Data = gb_trees:get(Edge, EdgeInfo), + Data#edge_data.later. + +lookup_later(EdgeInfo, Edge) -> + case gb_trees:lookup(Edge, EdgeInfo) of + none -> + none; + {value, #edge_data{later = Data}} -> + Data + end. + +%% insert(EdgeInfo, Edge) -> +%% case gb_trees:lookup(Edge, EdgeInfo) of +%% none -> +%% exit({?MODULE, insert, "edge info not found"}), +%% none; +%% {value, #edge_data{insert = Data}} -> +%% Data +%% end. + +%%============================================================================= +%% Set methods +set_up_exp(NodeInfo, Label, Data) -> + NodeData = + case gb_trees:lookup(Label, NodeInfo) of + none -> + #node_data{up_exp = Data}; + {value, OldNodeData} -> + OldNodeData#node_data{up_exp = Data} + end, + gb_trees:enter(Label, NodeData, NodeInfo). + +set_down_exp(NodeInfo, Label, Data) -> + NodeData = + case gb_trees:lookup(Label, NodeInfo) of + none -> + #node_data{down_exp = Data}; + {value, OldNodeData} -> + OldNodeData#node_data{down_exp = Data} + end, + gb_trees:enter(Label, NodeData, NodeInfo). + +set_killed_expr(NodeInfo, Label, Data) -> + NodeData = + case gb_trees:lookup(Label, NodeInfo) of + none -> + #node_data{killed_expr = Data}; + {value, OldNodeData} -> + OldNodeData#node_data{killed_expr = Data} + end, + gb_trees:enter(Label, NodeData, NodeInfo). + +set_avail_in(NodeInfo, Label, Data) -> + NodeData = + case gb_trees:lookup(Label, NodeInfo) of + none -> + #node_data{avail_in = Data}; + {value, OldNodeData} -> + OldNodeData#node_data{avail_in = Data} + end, + gb_trees:enter(Label, NodeData, NodeInfo). + +set_avail_out(NodeInfo, Label, Data) -> + NodeData = + case gb_trees:lookup(Label, NodeInfo) of + none -> + #node_data{avail_out = Data}; + {value, OldNodeData} -> + OldNodeData#node_data{avail_out = Data} + end, + gb_trees:enter(Label, NodeData, NodeInfo). + +set_antic_in(NodeInfo, Label, Data) -> + NodeData = + case gb_trees:lookup(Label, NodeInfo) of + none -> + #node_data{antic_in = Data}; + {value, OldNodeData} -> + OldNodeData#node_data{antic_in = Data} + end, + gb_trees:enter(Label, NodeData, NodeInfo). + +set_antic_out(NodeInfo, Label, Data) -> + NodeData = + case gb_trees:lookup(Label, NodeInfo) of + none -> + #node_data{antic_out = Data}; + {value, OldNodeData} -> + OldNodeData#node_data{antic_out = Data} + end, + gb_trees:enter(Label, NodeData, NodeInfo). + +set_later_in(NodeInfo, Label, Data) -> + NodeData = + case gb_trees:lookup(Label, NodeInfo) of + none -> + #node_data{later_in = Data}; + {value, OldNodeData} -> + OldNodeData#node_data{later_in = Data} + end, + gb_trees:enter(Label, NodeData, NodeInfo). + +set_delete(NodeInfo, Label, Data) -> + NodeData = + case gb_trees:lookup(Label, NodeInfo) of + none -> + #node_data{delete = Data}; + {value, OldNodeData} -> + OldNodeData#node_data{delete = Data} + end, + gb_trees:enter(Label, NodeData, NodeInfo). + +set_earliest(EdgeInfo, Edge, Data) -> + EdgeData = + case gb_trees:lookup(Edge, EdgeInfo) of + none -> + #edge_data{earliest = Data}; + {value, OldEdgeData} -> + OldEdgeData#edge_data{earliest = Data} + end, + gb_trees:enter(Edge, EdgeData, EdgeInfo). + +set_later(EdgeInfo, Edge, Data) -> + EdgeData = + case gb_trees:lookup(Edge, EdgeInfo) of + none -> + #edge_data{later = Data}; + {value, OldEdgeData} -> + OldEdgeData#edge_data{later = Data} + end, + gb_trees:enter(Edge, EdgeData, EdgeInfo). + +%% set_insert(EdgeInfo, Edge, Data) -> +%% EdgeData = +%% case gb_trees:lookup(Edge, EdgeInfo) of +%% none -> +%% #edge_data{insert = Data}; +%% {value, OldEdgeData} -> +%% OldEdgeData#edge_data{insert = Data} +%% end, +%% gb_trees:enter(Edge, EdgeData, EdgeInfo). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%% USE MAP %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% The use map is a mapping from "use" (which is an rtl register/variable) +%% to a set of expressions (IDs) where that register/variable is used. +%% It is used by calc_killed_expr to know what expressions are affected by +%% a definition. + +%%============================================================================= +%% Creates and calculates the use map for a CFG. +%% It uses ExprMap to lookup the expression IDs. +mk_use_map(CFG, ExprMap) -> + Labels = hipe_rtl_cfg:reverse_postorder(CFG), + NewMap = mk_use_map(gb_trees:empty(), CFG, ExprMap, Labels), + gb_trees:balance(NewMap). + +mk_use_map(Map, _, _, []) -> + Map; +mk_use_map(Map, CFG, ExprMap, [Label|Labels]) -> + Code = hipe_bb:code(hipe_rtl_cfg:bb(CFG, Label)), + NewMap = mk_use_map_bb(Map, ExprMap, Code), + mk_use_map(NewMap, CFG, ExprMap, Labels). + +mk_use_map_bb(UseMap, _, []) -> + UseMap; +mk_use_map_bb(UseMap, ExprMap, [Instr|Instrs]) -> + case is_expr(Instr) of + true -> + Uses = hipe_rtl:uses(Instr), + ExprId = expr_map_get_id(ExprMap, expr_clear_dst(Instr)), + NewUseMap = mk_use_map_insert_uses(UseMap, ExprId, Uses), + mk_use_map_bb(NewUseMap, ExprMap, Instrs); + false -> + mk_use_map_bb(UseMap, ExprMap, Instrs) + end. + +%%============================================================================= +%% Worker function for mk_use_map that inserts the expression id for every +%% rtl register the expression uses in a use map. +mk_use_map_insert_uses(Map, _, []) -> + Map; +mk_use_map_insert_uses(Map, Expr, [Use|Uses]) -> + case gb_trees:lookup(Use, Map) of + {value, UseSet} -> + NewUseSet = ?SETS:add_element(Expr, UseSet), + mk_use_map_insert_uses(gb_trees:update(Use, NewUseSet, Map), Expr, Uses); + none -> + UseSet = ?SETS:new(), + NewUseSet = ?SETS:add_element(Expr, UseSet), + mk_use_map_insert_uses(gb_trees:insert(Use, NewUseSet, Map), Expr, Uses) + end. + +%%============================================================================= +%% Gets a set of expressions where the given rtl register is used. +use_map_get_expr_uses(Map, Reg) -> + case gb_trees:lookup(Reg, Map) of + {value, UseSet} -> + UseSet; + none -> + ?SETS:new() + end. + +%%%%%%%%%%%%%%%%%%%%%% EXPRESSION MAP %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% The expression map is a mapping from expression to +%% (1) Expression Id (Integer used to speed up set operations) +%% (2) List of definitions (labels where the expression is defined and the +%% list of registers or variables defined by an instruction in that label, +%% represented as a tuple {Label, Defines}) +%% (3) The list of replacement registers created for the expression + +%%============================================================================= +%% Creates and calculates the expression map for a CFG. +mk_expr_map(CFG) -> + init_expr_id(), + Labels = hipe_rtl_cfg:reverse_postorder(CFG), + {ExprMap, IdMap} = mk_expr_map(gb_trees:empty(), gb_trees:empty(), + CFG, Labels), + {gb_trees:balance(ExprMap), gb_trees:balance(IdMap)}. + +mk_expr_map(ExprMap, IdMap, _, []) -> + {ExprMap, IdMap}; +mk_expr_map(ExprMap, IdMap, CFG, [Label|Labels]) -> + Code = hipe_bb:code(hipe_rtl_cfg:bb(CFG, Label)), + {NewExprMap, NewIdMap} = mk_expr_map_bb(ExprMap, IdMap, Label, Code), + mk_expr_map(NewExprMap, NewIdMap, CFG, Labels). + +mk_expr_map_bb(ExprMap, IdMap, _, []) -> + {ExprMap, IdMap}; +mk_expr_map_bb(ExprMap, IdMap, Label, [Instr|Instrs]) -> + case is_expr(Instr) of + true -> + Expr = expr_clear_dst(Instr), + Defines = hipe_rtl:defines(Instr), + case gb_trees:lookup(Expr, ExprMap) of + {value, {ExprId, DefinesList, ReplRegs}} -> + NewExprMap = gb_trees:update(Expr, {ExprId, + [{Label, Defines}|DefinesList], + ReplRegs}, ExprMap), + mk_expr_map_bb(NewExprMap, IdMap, Label, Instrs); + none -> + NewExprId = new_expr_id(), + NewReplRegs = mk_replacement_regs(Defines), + NewExprMap = gb_trees:insert(Expr, {NewExprId, + [{Label, Defines}], + NewReplRegs}, ExprMap), + NewIdMap = gb_trees:insert(NewExprId, Expr, IdMap), + mk_expr_map_bb(NewExprMap, NewIdMap, Label, Instrs) + end; + false -> + mk_expr_map_bb(ExprMap, IdMap, Label, Instrs) + end. + +%%============================================================================= +%% Creates new temporaries to replace defines in moved expressions. +mk_replacement_regs([]) -> + []; +mk_replacement_regs(Defines) -> + mk_replacement_regs(Defines, []). + +mk_replacement_regs([], NewRegs) -> + lists:reverse(NewRegs); +mk_replacement_regs([Define|Defines], NewRegs) -> + case hipe_rtl:is_reg(Define) of + true -> + NewReg = + case hipe_rtl:reg_is_gcsafe(Define) of + true -> hipe_rtl:mk_new_reg_gcsafe(); + false -> hipe_rtl:mk_new_reg() + end, + mk_replacement_regs(Defines, [NewReg|NewRegs]); + false -> + case hipe_rtl:is_var(Define) of + true -> + mk_replacement_regs(Defines, [hipe_rtl:mk_new_var()|NewRegs]); + false -> + true = hipe_rtl:is_fpreg(Define), + mk_replacement_regs(Defines, [hipe_rtl:mk_new_fpreg()|NewRegs]) + end + end. + +%%============================================================================= +%% Performs a lookup, which returns a tuple +%% {expression ID, list of definitions, list of replacement registers} +expr_map_lookup(Map, Expr) -> + gb_trees:lookup(Expr, Map). + +%%============================================================================= +%% Gets the actual RTL instruction to be generated for insertions of an +%% expression. +expr_map_get_instr(Map, Expr) -> + case gb_trees:lookup(Expr, Map) of + {value, {_, _, Regs}} -> + expr_set_dst(Expr, Regs); + none -> + exit({?MODULE, expr_map_get_instr, "expression missing"}) + end. + +%%============================================================================= +%% Gets expression id. +expr_map_get_id(Map, Expr) -> + case gb_trees:lookup(Expr, Map) of + {value, {ExprId, _, _}} -> + ExprId; + none -> + exit({?MODULE, expr_map_get_instr, "expression missing"}) + end. + +%%============================================================================= +%% Creates an rtl instruction that moves a value +mk_expr_move_instr([Reg], [Define]) -> + case hipe_rtl:is_fpreg(Reg) of + true -> + hipe_rtl:mk_fmove(Reg, Define); + false -> + %% FIXME Check is_var() orelse is_reg() ? + hipe_rtl:mk_move(Reg, Define) + end; +mk_expr_move_instr([_Reg|_Regs] = RegList, Defines) -> + %% FIXME Does this really work? What about floats... + %% (Multiple defines does not seem to be used by any of the + %% instructions considered by rtl_lcm at the moment so this is pretty much + %% untested/unused.) + hipe_rtl:mk_multimove(RegList, Defines); +mk_expr_move_instr(_, []) -> + exit({?MODULE, mk_expr_move_instr, "bad match"}). + +%%============================================================================= +%% Returns a set of all expressions in the code. +%% all_exprs(_CFG, []) -> +%% ?SETS:new(); +%% all_exprs(CFG, [Label|Labels]) -> +%% BB = hipe_rtl_cfg:bb(CFG, Label), +%% Code = hipe_bb:code(BB), +%% ?SETS:union(all_exprs_bb(Code), +%% all_exprs(CFG, Labels)). + +%%============================================================================= +%% Returns a set of expressions in a basic block. +%% all_exprs_bb([]) -> +%% ?SETS:new(); +%% all_exprs_bb([Instr|Instrs]) -> +%% case is_expr(Instr) of +%% true -> +%% Expr = expr_clear_dst(Instr), +%% ExprSet = all_exprs_bb(Instrs), +%% ?SETS:add_element(Expr, ExprSet); +%% false -> +%% all_exprs_bb(Instrs) +%% end. + +%%%%%%%%%%%%%%%%%% EXPRESSION ID -> EXPRESSION MAP %%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Map from expression IDs to expressions. +%%============================================================================= +%% mk_expr_id_map() -> +%% gb_trees:empty(). + +%% expr_id_map_insert(Map, ExprId, Expr) -> +%% gb_trees:insert(ExprId, Expr, Map). + +%% expr_id_map_lookup(Map, ExprId) -> +%% gb_trees:lookup(ExprId, Map). + +%%============================================================================= +%% Given expression id, gets expression. +expr_id_map_get_expr(Map, ExprId) -> + case gb_trees:lookup(ExprId, Map) of + {value, Expr} -> + Expr; + none -> + exit({?MODULE, expr_id_map_get_expr, "expression id missing"}) + end. + +%%============================================================================= +%% Expression ID counter +init_expr_id() -> + put({rtl_lcm,expr_id_count}, 0), + ok. + +-spec new_expr_id() -> non_neg_integer(). +new_expr_id() -> + Obj = {rtl_lcm, expr_id_count}, + V = get(Obj), + put(Obj, V+1), + V. + +%%%%%%%%%%%%%%%%%% EDGE BB (INSERT BETWEEN) MAP %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Map from edges to labels. +%% This is used by insert_expr_between to remember what new bbs it has created +%% for insertions on edges, and thus for multiple insertions on the same edge +%% to end up in the same bb. +%%============================================================================= +mk_edge_bb_map() -> + gb_trees:empty(). + +edge_bb_map_insert(Map, Edge, Label) -> + gb_trees:enter(Edge, Label, Map). + +edge_bb_map_lookup(Map, Edge) -> + gb_trees:lookup(Edge, Map). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%% PRETTY-PRINTING %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%============================================================================= +%% Prints debug messages. +-ifdef(LCM_DEBUG). + +pp_debug(Str, Args) -> + case ?LCM_DEBUG of + true -> + io:format(standard_io, Str, Args); + false -> + ok + end. + +pp_debug_instr(Instr) -> + case ?LCM_DEBUG of + true -> + hipe_rtl:pp_instr(standard_io, Instr); + false -> + ok + end. + +-else. + +pp_debug(_, _) -> + ok. + +pp_debug_instr(_) -> + ok. + +-endif. %% DEBUG + +%%============================================================================= +%% Pretty-prints the calculated sets for the lazy code motion. +pp_sets(_, _, _, _, _, _, []) -> + ok; +pp_sets(ExprMap, IdMap, NodeInfo, EdgeInfo, AllExpr, CFG, [Label|Labels]) -> + Preds = hipe_rtl_cfg:pred(CFG, Label), + Succs = hipe_rtl_cfg:succ(CFG, Label), + + io:format(standard_io, "Label ~w~n", [Label]), + io:format(standard_io, " Preds: ~w~n", [Preds]), + io:format(standard_io, " Succs: ~w~n", [Succs]), + + case up_exp(NodeInfo, Label) of + none -> ok; + UpExp -> + case ?SETS:size(UpExp) =:= 0 of + false -> + io:format(standard_io, " UEExpr: ~n", []), + pp_exprs(ExprMap, IdMap, ?SETS:to_list(UpExp)); + true -> ok + end + end, + case down_exp(NodeInfo, Label) of + none -> ok; + DownExp -> + case ?SETS:size(DownExp) =:= 0 of + false -> + io:format(standard_io, " DEExpr: ~n", []), + pp_exprs(ExprMap, IdMap, ?SETS:to_list(DownExp)); + true -> ok + end + end, + case killed_expr(NodeInfo, Label) of + none -> ok; + KilledExpr -> + case ?SETS:size(KilledExpr) =:= 0 of + false -> + io:format(standard_io, " ExprKill: ~n", []), + pp_exprs(ExprMap, IdMap, ?SETS:to_list(KilledExpr)); + true -> ok + end + end, + case avail_in(NodeInfo, Label) of + none -> ok; + AvailIn -> + case ?SETS:size(AvailIn) =:= 0 of + false -> + io:format(standard_io, " AvailIn: ~n", []), + pp_exprs(ExprMap, IdMap, ?SETS:to_list(AvailIn)); + true -> ok + end + end, + case avail_out(NodeInfo, Label) of + none -> ok; + AvailOut -> + case ?SETS:size(AvailOut) =:= 0 of + false -> + io:format(standard_io, " AvailOut: ~n", []), + pp_exprs(ExprMap, IdMap, ?SETS:to_list(AvailOut)); + true -> ok + end + end, + case antic_in(NodeInfo, Label) of + none -> ok; + AnticIn -> + case ?SETS:size(AnticIn) =:= 0 of + false -> + io:format(standard_io, " AnticIn: ~n", []), + pp_exprs(ExprMap, IdMap, ?SETS:to_list(AnticIn)); + true -> ok + end + end, + case antic_out(NodeInfo, Label) of + none -> ok; + AnticOut -> + case ?SETS:size(AnticOut) =:= 0 of + false -> + io:format(standard_io, " AnticOut: ~n", []), + pp_exprs(ExprMap, IdMap, ?SETS:to_list(AnticOut)); + true -> ok + end + end, + case later_in(NodeInfo, Label) of + none -> ok; + LaterIn -> + case ?SETS:size(LaterIn) =:= 0 of + false -> + io:format(standard_io, " LaterIn: ~n", []), + pp_exprs(ExprMap, IdMap, ?SETS:to_list(LaterIn)); + true -> ok + end + end, + + pp_earliest(ExprMap, IdMap, EdgeInfo, Label, Succs), + pp_later(ExprMap, IdMap, EdgeInfo, Label, Succs), + + case delete(NodeInfo, Label) of + none -> ok; + Delete -> + case ?SETS:size(Delete) =:= 0 of + false -> + io:format(standard_io, " Delete: ~n", []), + pp_exprs(ExprMap, IdMap, ?SETS:to_list(Delete)); + true -> ok + end + end, + pp_sets(ExprMap, IdMap, NodeInfo, EdgeInfo, AllExpr, CFG, Labels). + +%%============================================================================= +%% Pretty-prints the later set. +pp_later(_, _, _, _, []) -> + ok; +pp_later(ExprMap, IdMap, EdgeInfo, Pred, [Succ|Succs]) -> + case later(EdgeInfo, {Pred, Succ}) of + none -> ok; + Later -> + case ?SETS:size(Later) =:= 0 of + false -> + io:format(standard_io, " Later(~w->~w): ~n", [Pred,Succ]), + pp_exprs(ExprMap, IdMap, ?SETS:to_list(Later)); + true -> ok + end + end, + pp_later(ExprMap, IdMap, EdgeInfo, Pred, Succs). + +%%============================================================================= +%% Pretty-prints the earliest set. +pp_earliest(_, _, _, _, []) -> + ok; +pp_earliest(ExprMap, IdMap, EdgeInfo, Pred, [Succ|Succs]) -> + case earliest(EdgeInfo, {Pred, Succ}) of + none -> ok; + Earliest -> + case ?SETS:size(Earliest) =:= 0 of + false -> + io:format(standard_io, " Earliest(~w->~w): ~n", [Pred,Succ]), + pp_exprs(ExprMap, IdMap, ?SETS:to_list(Earliest)); + true -> ok + end + end, + pp_earliest(ExprMap, IdMap, EdgeInfo, Pred, Succs). + +%%============================================================================= +%% Pretty-prints an expression +pp_expr(ExprMap, IdMap, ExprId) -> + Expr = expr_id_map_get_expr(IdMap, ExprId), + hipe_rtl:pp_instr(standard_io, expr_map_get_instr(ExprMap, Expr)). + +pp_exprs(_, _, []) -> + ok; +pp_exprs(ExprMap, IdMap, [E|Es]) -> + pp_expr(ExprMap, IdMap, E), + pp_exprs(ExprMap, IdMap, Es). diff --git a/lib/hipe/rtl/hipe_rtl_liveness.erl b/lib/hipe/rtl/hipe_rtl_liveness.erl new file mode 100644 index 0000000000..3cfada9d6c --- /dev/null +++ b/lib/hipe/rtl/hipe_rtl_liveness.erl @@ -0,0 +1,145 @@ +%% $Id$ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2001-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% +%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% +%% LIVENESS ANALYSIS +%% +%% Exports: +%% ~~~~~~~ +%% analyze(CFG) - returns a liveness analysis of CFG. +%% liveout(Liveness, Label) - returns a set of variables that are live on +%% exit from basic block named Label. +%% livein(Liveness, Label) - returns a set of variables that are live on +%% entry to the basic block named Label. +%% list(Instructions, LiveOut) - Given a list of instructions and a liveout +%% set, returns a set of variables live at the first instruction. +%% + +-module(hipe_rtl_liveness). + +%% -define(LIVEOUT_NEEDED,true). % needed for liveness.inc below. +-define(PRETTY_PRINT,false). + +-include("hipe_rtl.hrl"). +-include("../flow/liveness.inc"). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% +%% Interface to CFG and RTL. +%% + +cfg_bb(CFG, L) -> + hipe_rtl_cfg:bb(CFG, L). + +cfg_postorder(CFG) -> + hipe_rtl_cfg:postorder(CFG). + +cfg_succ(CFG, L) -> + hipe_rtl_cfg:succ(CFG, L). + +uses(Instr) -> + hipe_rtl:uses(Instr). + +defines(Instr) -> + hipe_rtl:defines(Instr). + +%% +%% This is the list of registers that are live at exit from a function +%% + +liveout_no_succ() -> + hipe_rtl_arch:live_at_return(). + +%% +%% The following are used only if annotation of the code is requested. +%% + +cfg_labels(CFG) -> + hipe_rtl_cfg:reverse_postorder(CFG). + +pp_block(Label, CFG) -> + BB=hipe_rtl_cfg:bb(CFG, Label), + Code=hipe_bb:code(BB), + hipe_rtl:pp_block(Code). + +pp_liveness_info(LiveList) -> + NewList=remove_precoloured(LiveList), + print_live_list(NewList). + +print_live_list([]) -> + io:format(" none~n", []); +print_live_list([Last]) -> + io:format(" ", []), + print_var(Last), + io:format("~n", []); +print_live_list([Var|Rest]) -> + io:format(" ", []), + print_var(Var), + io:format(",", []), + print_live_list(Rest). + +print_var(A) -> + case hipe_rtl:is_var(A) of + true -> + pp_var(A); + false -> + case hipe_rtl:is_reg(A) of + true -> + pp_reg(A); + false -> + case hipe_rtl:is_fpreg(A) of + true -> + io:format("f~w", [hipe_rtl:fpreg_index(A)]); + false -> + io:format("unknown:~w", [A]) + end + end + end. + +pp_hard_reg(N) -> + io:format("~s", [hipe_rtl_arch:reg_name(N)]). + +pp_reg(Arg) -> + case hipe_rtl_arch:is_precoloured(Arg) of + true -> + pp_hard_reg(hipe_rtl:reg_index(Arg)); + false -> + io:format("r~w", [hipe_rtl:reg_index(Arg)]) + end. + +pp_var(Arg) -> + case hipe_rtl_arch:is_precoloured(Arg) of + true -> + pp_hard_reg(hipe_rtl:var_index(Arg)); + false -> + io:format("v~w", [hipe_rtl:var_index(Arg)]) + end. + +remove_precoloured(List) -> + List. + %% [X || X <- List, not hipe_rtl_arch:is_precoloured(X)]. + +-ifdef(DEBUG_LIVENESS). +cfg_bb_add(CFG, L, NewBB) -> + hipe_rtl_cfg:bb_add(CFG, L, NewBB). + +mk_comment(Text) -> + hipe_rtl:mk_comment(Text). +-endif. diff --git a/lib/hipe/rtl/hipe_rtl_mk_switch.erl b/lib/hipe/rtl/hipe_rtl_mk_switch.erl new file mode 100644 index 0000000000..e5175217d6 --- /dev/null +++ b/lib/hipe/rtl/hipe_rtl_mk_switch.erl @@ -0,0 +1,985 @@ +%% -*- erlang-indent-level: 2 -*- +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2001-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% +%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Copyright (c) 2001 by Erik Johansson. All Rights Reserved +%% ==================================================================== +%% Filename : hipe_rtl_mk_switch.erl +%% Module : hipe_rtl_mk_switch +%% Purpose : Implements switching on Erlang values. +%% Notes : Only fixnums are supported well, +%% atoms work with table search, +%% the inline search of atoms might have some bugs. +%% Should be extended to handle bignums and floats. +%% +%% History : * 2001-02-28 Erik Johansson ([email protected]): +%% Created. +%% * 2001-04-01 Erik Trulsson ([email protected]): +%% Stefan Lindstr�m ([email protected]): +%% Added clustering and inlined binary search trees. +%% * 2001-07-30 EJ ([email protected]): +%% Fixed some bugs and started cleanup. +%% ==================================================================== +%% Exports : +%% gen_switch_val(I, VarMap, ConstTab, Options) +%% gen_switch_tuple(I, Map, ConstTab, Options) +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-module(hipe_rtl_mk_switch). + +-export([gen_switch_val/4, gen_switch_tuple/4]). + +%%------------------------------------------------------------------------- + +-include("../main/hipe.hrl"). + +%%------------------------------------------------------------------------- + +-define(MINFORJUMPTABLE,9). + % Minimum number of integers needed to use something else than an inline search. +-define(MINFORINTSEARCHTREE,65). % Must be at least 3 + % Minimum number of integer elements needed to use a non-inline binary search. + +-define(MININLINEATOMSEARCH,8). + % Minimum number of atoms needed to use an inline binary search instead + % of a fast linear search. + +-define(MINFORATOMSEARCHTREE,20). % Must be at least 3 + % Minimum number of atoms needed to use a non-inline binary search instead + % of a linear search. + +-define(MAXINLINEATOMSEARCH,64). % Must be at least 3 + % The cutoff point between inlined and non-inlined binary search for atoms + +-define(WORDSIZE, hipe_rtl_arch:word_size()). +-define(MINDENSITY, 0.5). + % Minimum density required to use a jumptable instead of a binary search. + +%% The reason why MINFORINTSEARCHTREE and MINFORATOMSEARCHTREE must be +%% at least 3 is that the function tab/5 will enter an infinite loop +%% and hang when faced with a switch of size 1 or 2. + + +%% Options used by this module: +%% +%% [no_]use_indexing +%% Determines if any indexing be should be done at all. Turned on +%% by default at optimization level o2 and higher. +%% +%% [no_]use_clusters +%% Controls whether we attempt to divide sparse integer switches +%% into smaller dense clusters for which jumptables are practical. +%% Turned off by default since it can increase compilation time +%% considerably and most programs will gain little benefit from it. +%% +%% [no_]use_inline_atom_search +%% Controls whether we use an inline binary search for small number +%% of atoms. Turned off by default since this is currently only +%% supported on SPARC (and not on x86) and probably needs a bit +%% more testing before it can be turned on by default. + +gen_switch_val(I, VarMap, ConstTab, Options) -> + case proplists:get_bool(use_indexing, Options) of + false -> gen_slow_switch_val(I, VarMap, ConstTab, Options); + true -> gen_fast_switch_val(I, VarMap, ConstTab, Options) + end. + +gen_fast_switch_val(I, VarMap, ConstTab, Options) -> + {Arg, VarMap0} = + hipe_rtl_varmap:icode_var2rtl_var(hipe_icode:switch_val_term(I), VarMap), + IcodeFail = hipe_icode:switch_val_fail_label(I), + {Fail, VarMap1} = hipe_rtl_varmap:icode_label2rtl_label(IcodeFail, VarMap0), + %% Important that the list of cases is sorted when handling integers. + UnsortedCases = hipe_icode:switch_val_cases(I), + Cases = lists:sort(UnsortedCases), + + check_duplicates(Cases), + %% This check is currently not really necessary. The checking + %% happens at an earlier phase of the compilation. + {Types, InitCode} = split_types(Cases, Arg), + handle_types(Types, InitCode, VarMap1, ConstTab, Arg, {I, Fail, Options}). + +handle_types([{Type,Lbl,Cases}|Types], Code, VarMap, ConstTab, Arg, Info) -> + {Code1,VarMap1,ConstTab1} = gen_fast_switch_on(Type, Cases, + VarMap, + ConstTab, Arg, Info), + handle_types(Types, [Code,Lbl,Code1], VarMap1, ConstTab1, Arg, Info); +handle_types([], Code, VarMap, ConstTab, _, _) -> + {Code, VarMap, ConstTab}. + + +gen_fast_switch_on(integer, Cases, VarMap, ConstTab, Arg, {I, Fail, Options}) -> + {First,_} = hd(Cases), + Min = hipe_icode:const_value(First), + if length(Cases) < ?MINFORJUMPTABLE -> + gen_small_switch_val(Arg,Cases,Fail,VarMap,ConstTab,Options); + true -> + case proplists:get_bool(use_clusters, Options) of + false -> + M = list_to_tuple(Cases), + D = density(M, 1, tuple_size(M)), + if + D >= ?MINDENSITY -> + gen_jump_table(Arg,Fail,hipe_icode:switch_val_fail_label(I),VarMap,ConstTab,Cases,Min); + true -> + gen_search_switch_val(Arg, Cases, Fail, VarMap, ConstTab, Options) + end; + true -> + MC = minclusters(Cases), + Cl = cluster_split(Cases,MC), + CM = cluster_merge(Cl), + find_cluster(CM,VarMap,ConstTab,Options,Arg,Fail,hipe_icode:switch_val_fail_label(I)) + end + end; +gen_fast_switch_on(atom, Cases, VarMap, ConstTab, Arg, {_I, Fail, Options}) -> + case proplists:get_bool(use_inline_atom_search, Options) of + true -> + if + length(Cases) < ?MININLINEATOMSEARCH -> + gen_linear_switch_val(Arg, Cases, Fail, VarMap, ConstTab, Options); + length(Cases) > ?MAXINLINEATOMSEARCH -> + gen_search_switch_val(Arg, Cases, Fail, VarMap, ConstTab, Options); + true -> + gen_atom_switch_val(Arg,Cases,Fail,VarMap,ConstTab,Options) + end; + false -> + if length(Cases) < ?MINFORATOMSEARCHTREE -> + gen_linear_switch_val(Arg, Cases, Fail, VarMap, ConstTab, Options); + true -> + gen_search_switch_val(Arg, Cases, Fail, VarMap, ConstTab, Options) + end + end; +gen_fast_switch_on(_, _, VarMap, ConstTab, _, {I,_Fail,Options}) -> + %% We can only handle smart indexing of integers and atoms + %% TODO: Consider bignum + gen_slow_switch_val(I, VarMap, ConstTab, Options). + + +%% Split different types into separate switches. +split_types([Case|Cases], Arg) -> + Type1 = casetype(Case), + Types = split(Cases,Type1,[Case],[]), + switch_on_types(Types,[], [], Arg); +split_types([],_) -> + %% Cant happen. + ?EXIT({empty_caselist}). + +switch_on_types([{Type,Cases}], AccCode, AccCases, _Arg) -> + Lbl = hipe_rtl:mk_new_label(), + I = hipe_rtl:mk_goto(hipe_rtl:label_name(Lbl)), + {[{Type,Lbl,lists:reverse(Cases)} | AccCases], lists:reverse([I|AccCode])}; +switch_on_types([{other,Cases} | Rest], AccCode, AccCases, Arg) -> + %% Make sure the general case is handled last. + switch_on_types(Rest ++ [{other,Cases}], AccCode, AccCases, Arg); +switch_on_types([{Type,Cases} | Rest], AccCode, AccCases, Arg) -> + TLab = hipe_rtl:mk_new_label(), + FLab = hipe_rtl:mk_new_label(), + TestCode = + case Type of + integer -> + hipe_tagscheme:test_fixnum(Arg, hipe_rtl:label_name(TLab), + hipe_rtl:label_name(FLab), 0.5); + atom -> + hipe_tagscheme:test_atom(Arg, hipe_rtl:label_name(TLab), + hipe_rtl:label_name(FLab), 0.5); + bignum -> + hipe_tagscheme:test_bignum(Arg, hipe_rtl:label_name(TLab), + hipe_rtl:label_name(FLab), 0.5); + _ -> ?EXIT({ooops, type_not_handled, Type}) + end, + switch_on_types(Rest, [[TestCode,FLab] | AccCode], + [{Type,TLab,lists:reverse(Cases)} | AccCases], Arg). + +split([Case|Cases], Type, Current, Rest) -> + case casetype(Case) of + Type -> + split(Cases, Type, [Case|Current],Rest); + Other -> + split(Cases, Other, [Case], [{Type,Current}|Rest]) + end; +split([], Type, Current, Rest) -> + [{Type, Current} | Rest]. + +%% Determine what type an entry in the caselist has + +casetype({Const,_}) -> + casetype(hipe_icode:const_value(Const)); +casetype(A) -> + if + is_integer(A) -> + case hipe_tagscheme:is_fixnum(A) of + true -> integer; + false -> bignum + end; + is_float(A) -> float; + is_atom(A) -> atom; + true -> other + end. + +%% check that no duplicate values occur in the case list and also +%% check that all case values have the same type. +check_duplicates([]) -> true; +check_duplicates([_]) -> true; +check_duplicates([{Const1,_},{Const2,L2}|T]) -> + C1 = hipe_icode:const_value(Const1), + C2 = hipe_icode:const_value(Const2), + %% T1 = casetype(C1), + %% T2 = casetype(C2), + if C1 =/= C2 -> %% , T1 =:= T2 -> + check_duplicates([{Const2,L2}|T]); + true -> + ?EXIT({bad_values_in_switchval,C1}) + end. + +%% +%% Determine the optimal way to divide Cases into clusters such that each +%% cluster is dense. +%% +%% See: +%% Producing Good Code for the Case Statement, Robert L. Bernstein +%% Software - Practice and Experience vol 15, 1985, no 10, pp 1021--1024 +%% And +%% Correction to "Producing Good Code for the Case Statement" +%% Sampath Kannan and Todd A. Proebsting, +%% Software - Practice and Experience vol 24, 1994, no 2, p 233 +%% +%% (The latter is where the algorithm comes from.) + +%% This function will return a tuple with the first element being 0 +%% The rest of the elements being integers. A value of M at index N +%% (where the first element is considered to have index 0) means that +%% the first N cases can be divided into M (but no fewer) clusters where +%% each cluster is dense. + +minclusters(Cases) when is_list(Cases) -> + minclusters(list_to_tuple(Cases)); +minclusters(Cases) when is_tuple(Cases) -> + N = tuple_size(Cases), + MinClusters = list_to_tuple([0|n_list(N,inf)]), + i_loop(1,N,MinClusters,Cases). + +%% Create a list with N elements initialized to Init +n_list(0,_) -> []; +n_list(N,Init) -> [Init | n_list(N-1,Init)]. + +%% Do the dirty work of minclusters +i_loop(I,N,MinClusters,_Cases) when I > N -> + MinClusters; +i_loop(I,N,MinClusters,Cases) when I =< N -> + M = j_loop(0, I-1, MinClusters, Cases), + i_loop(I+1, N, M, Cases). + +%% More dirty work +j_loop(J,I1,MinClusters,_Cases) when J > I1 -> + MinClusters; +j_loop(J,I1,MinClusters,Cases) when J =< I1 -> + D = density(Cases,J+1,I1+1), + A0 = element(J+1,MinClusters), + A = if + is_number(A0) -> + A0+1; + true -> + A0 + end, + B = element(I1+2,MinClusters), + M = if + D >= ?MINDENSITY, A<B -> + setelement(I1+2,MinClusters,A); + true -> + MinClusters + end, + j_loop(J+1,I1,M,Cases). + + +%% Determine the density of a (subset of a) case list +%% A is a tuple with the cases in order from smallest to largest +%% I is the index of the first element and J of the last + +density(A,I,J) -> + {AI,_} = element(I,A), + {AJ,_} = element(J,A), + (J-I+1)/(hipe_icode:const_value(AJ)-hipe_icode:const_value(AI)+1). + + +%% Split a case list into dense clusters +%% Returns a list of lists of cases. +%% +%% Cases is the case list and Clust is a list describing the optimal +%% clustering as returned by minclusters +%% +%% If the value in the last place in minclusters is M then we can +%% split the case list into M clusters. We then search for the last +%% (== right-most) occurance of the value M-1 in minclusters. That +%% indicates the largest number of cases that can be split into M-1 +%% clusters. This means that the cases in between constitute one +%% cluster. Then we recurse on the remainder of the cases. +%% +%% The various calls to lists:reverse are just to ensure that the +%% cases remain in the correct, sorted order. + +cluster_split(Cases, Clust) -> + A = tl(tuple_to_list(Clust)), + Max = element(tuple_size(Clust), Clust), + L1 = lists:reverse(Cases), + L2 = lists:reverse(A), + cluster_split(Max, [], [], L1, L2). + +cluster_split(0, [], Res, Cases, _Clust) -> + L = lists:reverse(Cases), + {H,_} = hd(L), + {T,_} = hd(Cases), + [{dense,hipe_icode:const_value(H),hipe_icode:const_value(T),L}|Res]; +cluster_split(N, [], Res, Cases, [N|_] = Clust) -> + cluster_split(N-1, [], Res, Cases, Clust); +cluster_split(N,Sofar,Res,Cases,[N|Clust]) -> + {H,_} = hd(Sofar), + {T,_} = lists:last(Sofar), + cluster_split(N-1,[],[{dense,hipe_icode:const_value(H),hipe_icode:const_value(T),Sofar}|Res],Cases,[N|Clust]); +cluster_split(N,Sofar,Res,[C|Cases],[_|Clust]) -> + cluster_split(N,[C|Sofar],Res,Cases,Clust). + +%% +%% Merge adjacent small clusters into larger sparse clusters +%% +cluster_merge([C]) -> [C]; +cluster_merge([{dense,Min,Max,C}|T]) when length(C) >= ?MINFORJUMPTABLE -> + C2 = cluster_merge(T), + [{dense,Min,Max,C}|C2]; +cluster_merge([{sparse,Min,_,C},{sparse,_,Max,D}|T]) -> + R = {sparse,Min,Max,C ++ D}, + cluster_merge([R|T]); +cluster_merge([{sparse,Min,_,C},{dense,_,Max,D}|T]) when length(D) < ?MINFORJUMPTABLE -> + R = {sparse,Min,Max,C ++ D}, + cluster_merge([R|T]); +cluster_merge([{dense,Min,_,C},{dense,_,Max,D}|T]) when length(C) < ?MINFORJUMPTABLE, length(D) < ?MINFORJUMPTABLE -> + R = {sparse,Min,Max,C ++ D}, + cluster_merge([R|T]); +cluster_merge([{dense,Min,_,D},{sparse,_,Max,C}|T]) when length(D) < ?MINFORJUMPTABLE -> + R = {sparse,Min,Max,C ++ D}, + cluster_merge([R|T]); +cluster_merge([A,{dense,Min,Max,C}|T]) when length(C) >= ?MINFORJUMPTABLE -> + R = cluster_merge([{dense,Min,Max,C}|T]), + [A|R]. + + +%% Generate code to search for the correct cluster + +find_cluster([{sparse,_Min,_Max,C}],VarMap,ConstTab,Options,Arg,Fail,_IcodeFail) -> + case length(C) < ?MINFORINTSEARCHTREE of + true -> + gen_small_switch_val(Arg,C,Fail,VarMap,ConstTab,Options); + _ -> + gen_search_switch_val(Arg,C,Fail,VarMap,ConstTab,Options) + end; +find_cluster([{dense,Min,_Max,C}],VarMap,ConstTab,Options,Arg,Fail,IcodeFail) -> + case length(C) < ?MINFORJUMPTABLE of + true -> + gen_small_switch_val(Arg,C,Fail,VarMap,ConstTab,Options); + _ -> + gen_jump_table(Arg,Fail,IcodeFail,VarMap,ConstTab,C,Min) + end; +find_cluster([{Density,Min,Max,C}|T],VarMap,ConstTab,Options,Arg,Fail,IcodeFail) -> + ClustLab = hipe_rtl:mk_new_label(), + NextLab = hipe_rtl:mk_new_label(), + {ClustCode,V1,C1} = find_cluster([{Density,Min,Max,C}],VarMap,ConstTab,Options,Arg,Fail,IcodeFail), + + {Rest,V2,C2} = find_cluster(T,V1,C1,Options,Arg,Fail,IcodeFail), + + {[ + hipe_rtl:mk_branch(Arg, gt, hipe_rtl:mk_imm(hipe_tagscheme:mk_fixnum(Max)), + hipe_rtl:label_name(NextLab), + hipe_rtl:label_name(ClustLab), 0.50), + ClustLab + ] ++ + ClustCode ++ + [NextLab] ++ + Rest, + V2,C2}. + +%% Generate efficient code for a linear search through the case list. +%% Only works for atoms and integer. +gen_linear_switch_val(Arg,Cases,Fail,VarMap,ConstTab,_Options) -> + {Values,_Labels} = split_cases(Cases), + {LabMap,VarMap1} = lbls_from_cases(Cases,VarMap), + Code = fast_linear_search(Arg,Values,LabMap,Fail), + {Code,VarMap1,ConstTab}. + +fast_linear_search(_Arg,[],[],Fail) -> + [hipe_rtl:mk_goto(hipe_rtl:label_name(Fail))]; +fast_linear_search(Arg,[Case|Cases],[Label|Labels],Fail) -> + Reg = hipe_rtl:mk_new_reg_gcsafe(), + NextLab = hipe_rtl:mk_new_label(), + C2 = fast_linear_search(Arg,Cases,Labels,Fail), + C1 = + if + is_integer(Case) -> + TVal = hipe_tagscheme:mk_fixnum(Case), + [ + hipe_rtl:mk_move(Reg,hipe_rtl:mk_imm(TVal)), + hipe_rtl:mk_branch(Arg,eq,Reg, + Label, + hipe_rtl:label_name(NextLab), 0.5), + NextLab + ]; + is_atom(Case) -> + [ + hipe_rtl:mk_load_atom(Reg,Case), + hipe_rtl:mk_branch(Arg,eq,Reg, + Label, + hipe_rtl:label_name(NextLab), 0.5), + NextLab + ]; + true -> % This should never happen ! + ?EXIT({internal_error_in_switch_val,Case}) + end, + [C1,C2]. + + +%% Generate code to search through a small cluster of integers using +%% binary search +gen_small_switch_val(Arg,Cases,Fail,VarMap,ConstTab,_Options) -> + {Values,_Labels} = split_cases(Cases), + {LabMap,VarMap1} = lbls_from_cases(Cases,VarMap), + Keys = [hipe_tagscheme:mk_fixnum(X) % Add tags to the values + || X <- Values], + Code = inline_search(Keys, LabMap, Arg, Fail), + {Code, VarMap1, ConstTab}. + + +%% Generate code to search through a small cluster of atoms +gen_atom_switch_val(Arg,Cases,Fail,VarMap,ConstTab,_Options) -> + {Values, _Labels} = split_cases(Cases), + {LabMap,VarMap1} = lbls_from_cases(Cases,VarMap), + LMap = [{label,L} || L <- LabMap], + {NewConstTab,Id} = hipe_consttab:insert_sorted_block(ConstTab, Values), + {NewConstTab2,LabId} = + hipe_consttab:insert_sorted_block(NewConstTab, word, LMap, Values), + Code = inline_atom_search(0, length(Cases)-1, Id, LabId, Arg, Fail, LabMap), + {Code, VarMap1, NewConstTab2}. + + +%% calculate the middle position of a list (+ 1 because of 1-indexing of lists) +get_middle(List) -> + N = length(List), + N div 2 + 1. + +%% get element [N1, N2] from a list +get_cases(_, 0, 0) -> + []; +get_cases([H|T], 0, N) -> + [H | get_cases(T, 0, N - 1)]; +get_cases([_|T], N1, N2) -> + get_cases(T, N1 - 1, N2 - 1). + + +%% inline_search/4 creates RTL code for a inlined binary search. +%% It requires two sorted tables - one with the keys to search +%% through and one with the corresponding labels to jump to. +%% +%% Input: +%% KeyList - A list of keys to search through. +%% LableList - A list of labels to jump to. +%% KeyReg - A register containing the key to search for. +%% Default - A label to jump to if the key is not found. +%% + +inline_search([], _LabelList, _KeyReg, _Default) -> []; +inline_search(KeyList, LabelList, KeyReg, Default) -> + %% Create some registers and labels that we need. + Reg = hipe_rtl:mk_new_reg_gcsafe(), + Lab1 = hipe_rtl:mk_new_label(), + Lab2 = hipe_rtl:mk_new_label(), + Lab3 = hipe_rtl:mk_new_label(), + + Length = length(KeyList), + + if + Length >= 3 -> + %% Get middle element and keys/labels before that and after + Middle_pos = get_middle(KeyList), + Middle_key = lists:nth(Middle_pos, KeyList), + Keys_beginning = get_cases(KeyList, 0, Middle_pos - 1), + Labels_beginning = get_cases(LabelList, 0, Middle_pos - 1), + Keys_ending = get_cases(KeyList, Middle_pos, Length), + Labels_ending = get_cases(LabelList, Middle_pos, Length), + + %% Create the code. + + %% Get the label and build it up properly + Middle_label = lists:nth(Middle_pos, LabelList), + + A = [hipe_rtl:mk_move(Reg, hipe_rtl:mk_imm(Middle_key)), + hipe_rtl:mk_branch(KeyReg, lt, Reg, + hipe_rtl:label_name(Lab2), + hipe_rtl:label_name(Lab1), 0.5), + Lab1, + hipe_rtl:mk_branch(KeyReg, gt, Reg, + hipe_rtl:label_name(Lab3), + Middle_label , 0.5), + Lab2], + %% build search tree for keys less than the middle element + B = inline_search(Keys_beginning, Labels_beginning, KeyReg, Default), + %% ...and for keys bigger than the middle element + D = inline_search(Keys_ending, Labels_ending, KeyReg, Default), + + %% append the code and return it + A ++ B ++ [Lab3] ++ D; + + Length =:= 2 -> + %% get the first and second elements and theirs labels + Key_first = hd(KeyList), + First_label = hd(LabelList), + + %% Key_second = hipe_tagscheme:mk_fixnum(lists:nth(2, KeyList)), + Key_second = lists:nth(2, KeyList), + Second_label = lists:nth(2, LabelList), + + NewLab = hipe_rtl:mk_new_label(), + + %% compare them + A = [hipe_rtl:mk_move(Reg,hipe_rtl:mk_imm(Key_first)), + hipe_rtl:mk_branch(KeyReg, eq, Reg, + First_label, + hipe_rtl:label_name(NewLab) , 0.5), + NewLab], + + B = [hipe_rtl:mk_move(Reg,hipe_rtl:mk_imm(Key_second)), + hipe_rtl:mk_branch(KeyReg, eq, Reg, + Second_label, + hipe_rtl:label_name(Default) , 0.5)], + A ++ B; + + Length =:= 1 -> + Key = hd(KeyList), + Label = hd(LabelList), + + [hipe_rtl:mk_move(Reg,hipe_rtl:mk_imm(Key)), + hipe_rtl:mk_branch(KeyReg, eq, Reg, + Label, + hipe_rtl:label_name(Default) , 0.5)] + end. + + +inline_atom_search(Start, End, Block, LBlock, KeyReg, Default, Labels) -> + Reg = hipe_rtl:mk_new_reg_gcsafe(), + + Length = (End - Start) + 1, + + if + Length >= 3 -> + Lab1 = hipe_rtl:mk_new_label(), + Lab2 = hipe_rtl:mk_new_label(), + Lab3 = hipe_rtl:mk_new_label(), + Lab4 = hipe_rtl:mk_new_label(), + + Mid = ((End-Start) div 2)+Start, + End1 = Mid-1, + Start1 = Mid+1, + A = [ + hipe_rtl:mk_load_word_index(Reg,Block,Mid), + hipe_rtl:mk_branch(KeyReg, lt, Reg, + hipe_rtl:label_name(Lab2), + hipe_rtl:label_name(Lab1), 0.5), + Lab1, + hipe_rtl:mk_branch(KeyReg, gt, Reg, + hipe_rtl:label_name(Lab3), + hipe_rtl:label_name(Lab4), 0.5), + Lab4, + hipe_rtl:mk_goto_index(LBlock, Mid, Labels), + Lab2 + ], + B = [inline_atom_search(Start,End1,Block,LBlock,KeyReg,Default,Labels)], + C = [inline_atom_search(Start1,End,Block,LBlock,KeyReg,Default,Labels)], + A ++ B ++ [Lab3] ++ C; + + Length =:= 2 -> + L1 = hipe_rtl:mk_new_label(), + L2 = hipe_rtl:mk_new_label(), + L3 = hipe_rtl:mk_new_label(), + [ + hipe_rtl:mk_load_word_index(Reg,Block,Start), + hipe_rtl:mk_branch(KeyReg,eq,Reg, + hipe_rtl:label_name(L1), + hipe_rtl:label_name(L2), 0.5), + L1, + hipe_rtl:mk_goto_index(LBlock,Start,Labels), + + L2, + hipe_rtl:mk_load_word_index(Reg,Block,End), + hipe_rtl:mk_branch(KeyReg,eq,Reg, + hipe_rtl:label_name(L3), + hipe_rtl:label_name(Default), 0.5), + L3, + hipe_rtl:mk_goto_index(LBlock, End, Labels) + ]; + + Length =:= 1 -> + NewLab = hipe_rtl:mk_new_label(), + [ + hipe_rtl:mk_load_word_index(Reg,Block,Start), + hipe_rtl:mk_branch(KeyReg, eq, Reg, + hipe_rtl:label_name(NewLab), + hipe_rtl:label_name(Default), 0.9), + NewLab, + hipe_rtl:mk_goto_index(LBlock, Start, Labels) + ] + end. + + +%% Create a jumptable +gen_jump_table(Arg,Fail,IcodeFail,VarMap,ConstTab,Cases,Min) -> + %% Map is a rtl mapping of Dense + {Max,DenseTbl} = dense_interval(Cases,Min,IcodeFail), + {Map,VarMap2} = lbls_from_cases(DenseTbl,VarMap), + + %% Make some labels and registers that we need. + BelowLab = hipe_rtl:mk_new_label(), + UntaggedR = hipe_rtl:mk_new_reg_gcsafe(), + StartR = hipe_rtl:mk_new_reg_gcsafe(), + + %% Generate the code to do the switch... + {[ + %% Untag the index. + hipe_tagscheme:untag_fixnum(UntaggedR, Arg)| + %% Check that the index is within Min and Max. + case Min of + 0 -> %% First element is 0 this is simple. + [hipe_rtl:mk_branch(UntaggedR, gtu, hipe_rtl:mk_imm(Max), + hipe_rtl:label_name(Fail), + hipe_rtl:label_name(BelowLab), 0.01), + BelowLab, + %% StartR contains the index into the jumptable + hipe_rtl:mk_switch(UntaggedR, Map)]; + _ -> %% First element is not 0 + [hipe_rtl:mk_alu(StartR, UntaggedR, sub, + hipe_rtl:mk_imm(Min)), + hipe_rtl:mk_branch(StartR, gtu, hipe_rtl:mk_imm(Max-Min), + hipe_rtl:label_name(Fail), + hipe_rtl:label_name(BelowLab), 0.01), + BelowLab, + %% StartR contains the index into the jumptable + hipe_rtl:mk_switch(StartR, Map)] + end], + VarMap2, + ConstTab}. + + +%% Generate the jumptable for Cases while filling in unused positions +%% with the fail label + +dense_interval(Cases, Min, IcodeFail) -> + dense_interval(Cases, Min, IcodeFail, 0, 0). +dense_interval([Pair = {Const,_}|Rest], Pos, Fail, Range, NoEntries) -> + Val = hipe_icode:const_value(Const), + if + Pos < Val -> + {Max, Res} = + dense_interval([Pair|Rest], Pos+1, Fail, Range+1, NoEntries), + {Max,[{hipe_icode:mk_const(Pos), Fail}|Res]}; + true -> + {Max, Res} = dense_interval(Rest, Pos+1, Fail, Range+1, NoEntries+1), + {Max, [Pair | Res]} + end; +dense_interval([], Max, _, _, _) -> + {Max-1, []}. + + +%%------------------------------------------------------------------------- +%% switch_val without jumptable +%% + +gen_slow_switch_val(I, VarMap, ConstTab, Options) -> + Is = rewrite_switch_val(I), + ?IF_DEBUG_LEVEL(3,?msg("Switch: ~w\n", [Is]), no_debug), + hipe_icode2rtl:translate_instrs(Is, VarMap, ConstTab, Options). + +rewrite_switch_val(I) -> + Var = hipe_icode:switch_val_term(I), + Fail = hipe_icode:switch_val_fail_label(I), + Cases = hipe_icode:switch_val_cases(I), + rewrite_switch_val_cases(Cases, Fail, Var). + +rewrite_switch_val_cases([{C,L}|Cases], Fail, Arg) -> + Tmp = hipe_icode:mk_new_var(), + NextLab = hipe_icode:mk_new_label(), + [hipe_icode:mk_move(Tmp, C), + hipe_icode:mk_if(op_exact_eqeq_2, [Arg, Tmp], L, + hipe_icode:label_name(NextLab)), + NextLab | + rewrite_switch_val_cases(Cases, Fail, Arg)]; +rewrite_switch_val_cases([], Fail, _Arg) -> + [hipe_icode:mk_goto(Fail)]. + + +%%------------------------------------------------------------------------- +%% switch_val with binary search jumptable +%% + +gen_search_switch_val(Arg, Cases, Default, VarMap, ConstTab, _Options) -> + ValTableR = hipe_rtl:mk_new_reg_gcsafe(), + + {Values,_Labels} = split_cases(Cases), + {NewConstTab,Id} = hipe_consttab:insert_sorted_block(ConstTab, Values), + {LabMap,VarMap1} = lbls_from_cases(Cases,VarMap), + + Code = + [hipe_rtl:mk_load_address(ValTableR, Id, constant)| + tab(Values,LabMap,Arg,ValTableR,Default)], + {Code, VarMap1, NewConstTab}. + + +%%------------------------------------------------------------------------- +%% +%% tab/5 creates RTL code for a binary search. +%% It requires two sorted tables one with the keys to search +%% through and one with the corresponding labels to jump to. +%% +%% The implementation is derived from John Bentlys +%% Programming Pearls. +%% +%% Input: +%% KeyList - A list of keys to search through. +%% (Just used to calculate the number of elements.) +%% LableList - A list of labels to jump to. +%% KeyReg - A register containing the key to search for. +%% TablePntrReg - A register containing a pointer to the +%% tables with keys +%% Default - A lable to jump to if the key is not found. +%% +%% Example: +%% KeyTbl: < a, b, d, f, h, i, z > +%% Lbls: < 5, 3, 2, 4, 1, 7, 6 > +%% Default: 8 +%% KeyReg: v37 +%% TablePntrReg: r41 +%% +%% should give code like: +%% r41 <- KeyTbl +%% r42 <- 0 +%% r43 <- [r41+16] +%% if (r43 gt v37) then L17 (0.50) else L16 +%% L16: +%% r42 <- 16 +%% goto L17 +%% L17: +%% r46 <- r42 add 16 +%% r45 <- [r41+r46] +%% if (r45 gt v37) then L21 (0.50) else L20 +%% L20: +%% r42 <- r46 +%% goto L21 +%% L21: +%% r48 <- r42 add 8 +%% r47 <- [r41+r48] +%% if (r47 gt v37) then L23 (0.50) else L22 +%% L22: +%% r42 <- r48 +%% goto L23 +%% L23: +%% r50 <- r42 add 4 +%% r49 <- [r41+r50] +%% if (r49 gt v37) then L25 (0.50) else L24 +%% L24: +%% r42 <- r42 add 4 +%% goto L25 +%% L25: +%% if (r42 gt 28) then L6 (0.50) else L18 +%% L18: +%% r44 <- [r41+r42] +%% if (r44 eq v37) then L19 (0.90) else L8 +%% L19: +%% r42 <- r42 sra 2 +%% switch (r42) <L5, L3, L2, L4, L1, +%% L7, L6> + +%% +%% The search is done like a rolled out binary search, +%% but instead of starting in the middle we start at +%% the power of two closest above the middle. +%% +%% We let IndexReg point to the lower bound of our +%% search, and then we speculatively look at a +%% position at IndexReg + I where I is a power of 2. +%% +%% Example: Looking for 'h' in +%% KeyTbl: < a, b, d, f, h, i, z > +%% +%% We start with IndexReg=0 and I=4 +%% < a, b, d, f, h, i, z > +%% ^ ^ +%% IndexReg + I +%% +%% 'f' < 'h' so we add I to IndexReg and divide I with 2 +%% IndexReg=4 and I=2 +%% < a, b, d, f, h, i, z > +%% ^ ^ +%% IndexReg + I +%% +%% 'i' > 'h' so we keep IndexReg and divide I with 2 +%% IndexReg=4 and I=1 +%% < a, b, d, f, h, i, z > +%% ^ ^ +%% IndexReg+ I +%% Now we have found 'h' so we add I to IndexReg -> 5 +%% And we can load switch to the label at position 5 in +%% the label table. +%% +%% Now since the wordsize is 4 all numbers above are +%% Multiples of 4. + +tab(KeyList, LabelList, KeyReg, TablePntrReg, Default) -> + %% Calculate the size of the table: + %% the number of keys * wordsize + LastOffset = (length(KeyList)-1)*?WORDSIZE, + + %% Calculate the power of two closest to the size of the table. + Pow2 = 1 bsl trunc(math:log(LastOffset) / math:log(2)), + + %% Create some registers and lables that we need + IndexReg = hipe_rtl:mk_new_reg_gcsafe(), + Temp = hipe_rtl:mk_new_reg_gcsafe(), + Temp2 = hipe_rtl:mk_new_reg_gcsafe(), + Lab1 = hipe_rtl:mk_new_label(), + Lab2 = hipe_rtl:mk_new_label(), + Lab3 = hipe_rtl:mk_new_label(), + Lab4 = hipe_rtl:mk_new_label(), + + %% Calculate the position to start looking at + Init = (LastOffset)-Pow2, + + %% Create the code + [ + hipe_rtl:mk_move(IndexReg,hipe_rtl:mk_imm(0)), + hipe_rtl:mk_load(Temp,TablePntrReg,hipe_rtl:mk_imm(Init)), + hipe_rtl:mk_branch(Temp, geu, KeyReg, + hipe_rtl:label_name(Lab2), + hipe_rtl:label_name(Lab1), 0.5), + Lab1, + hipe_rtl:mk_alu(IndexReg, IndexReg, add, hipe_rtl:mk_imm(Init+?WORDSIZE)), + hipe_rtl:mk_goto(hipe_rtl:label_name(Lab2)), + Lab2] ++ + + step(Pow2 div 2, TablePntrReg, IndexReg, KeyReg) ++ + + [hipe_rtl:mk_branch(IndexReg, gt, hipe_rtl:mk_imm(LastOffset), + hipe_rtl:label_name(Default), + hipe_rtl:label_name(Lab3), 0.5), + Lab3, + hipe_rtl:mk_load(Temp2,TablePntrReg,IndexReg), + hipe_rtl:mk_branch(Temp2, eq, KeyReg, + hipe_rtl:label_name(Lab4), + hipe_rtl:label_name(Default), 0.9), + Lab4, + hipe_rtl:mk_alu(IndexReg, IndexReg, sra, + hipe_rtl:mk_imm(hipe_rtl_arch:log2_word_size())), + hipe_rtl:mk_sorted_switch(IndexReg, LabelList, KeyList) + ]. + +step(I,TablePntrReg,IndexReg,KeyReg) -> + Temp = hipe_rtl:mk_new_reg_gcsafe(), + TempIndex = hipe_rtl:mk_new_reg_gcsafe(), + Lab1 = hipe_rtl:mk_new_label(), + Lab2 = hipe_rtl:mk_new_label(), + [hipe_rtl:mk_alu(TempIndex, IndexReg, add, hipe_rtl:mk_imm(I)), + hipe_rtl:mk_load(Temp,TablePntrReg,TempIndex), + hipe_rtl:mk_branch(Temp, gtu, KeyReg, + hipe_rtl:label_name(Lab2), + hipe_rtl:label_name(Lab1) , 0.5), + Lab1] ++ + case ?WORDSIZE of + I -> %% Recursive base case + [hipe_rtl:mk_alu(IndexReg, IndexReg, add, hipe_rtl:mk_imm(I)), + hipe_rtl:mk_goto(hipe_rtl:label_name(Lab2)), + Lab2 + ]; + _ -> %% Recursion case + [hipe_rtl:mk_move(IndexReg, TempIndex), + hipe_rtl:mk_goto(hipe_rtl:label_name(Lab2)), + Lab2 + | step(I div 2, TablePntrReg, IndexReg, KeyReg) + ] + end. + +%%------------------------------------------------------------------------- + +lbls_from_cases([{_,L}|Rest], VarMap) -> + {Map,VarMap1} = lbls_from_cases(Rest, VarMap), + {RtlL, VarMap2} = hipe_rtl_varmap:icode_label2rtl_label(L,VarMap1), + %% {[{label,hipe_rtl:label_name(RtlL)}|Map],VarMap2}; + {[hipe_rtl:label_name(RtlL)|Map],VarMap2}; +lbls_from_cases([], VarMap) -> + {[], VarMap}. + +%%------------------------------------------------------------------------- + +split_cases(L) -> + split_cases(L, [], []). + +split_cases([], Vs, Ls) -> {lists:reverse(Vs),lists:reverse(Ls)}; +split_cases([{V,L}|Rest], Vs, Ls) -> + split_cases(Rest, [hipe_icode:const_value(V)|Vs], [L|Ls]). + +%%------------------------------------------------------------------------- +%% +%% {switch_tuple_arity,X,Fail,N,[{A1,L1},...,{AN,LN}]} +%% +%% if not boxed(X) goto Fail +%% Hdr := *boxed_val(X) +%% switch_int(Hdr,Fail,[{H(A1),L1},...,{H(AN),LN}]) +%% where H(Ai) = make_arityval(Ai) +%% +%%------------------------------------------------------------------------- + +gen_switch_tuple(I, Map, ConstTab, _Options) -> + Var = hipe_icode:switch_tuple_arity_term(I), + {X, Map1} = hipe_rtl_varmap:icode_var2rtl_var(Var, Map), + Fail0 = hipe_icode:switch_tuple_arity_fail_label(I), + {Fail1, Map2} = hipe_rtl_varmap:icode_label2rtl_label(Fail0, Map1), + FailLab = hipe_rtl:label_name(Fail1), + {Cases, Map3} = + lists:foldr(fun({A,L}, {Rest,M}) -> + {L1,M1} = hipe_rtl_varmap:icode_label2rtl_label(L, M), + L2 = hipe_rtl:label_name(L1), + A1 = hipe_icode:const_value(A), + H1 = hipe_tagscheme:mk_arityval(A1), + {[{H1,L2}|Rest], M1} end, + {[], Map2}, + hipe_icode:switch_tuple_arity_cases(I)), + Hdr = hipe_rtl:mk_new_reg_gcsafe(), + IsBoxedLab = hipe_rtl:mk_new_label(), + {[hipe_tagscheme:test_is_boxed(X, hipe_rtl:label_name(IsBoxedLab), + FailLab, 0.9), + IsBoxedLab, + hipe_tagscheme:get_header(Hdr, X) | + gen_switch_int(Hdr, FailLab, Cases)], + Map3, ConstTab}. + +%% +%% RTL-level switch-on-int +%% + +gen_switch_int(X, FailLab, [{C,L}|Rest]) -> + NextLab = hipe_rtl:mk_new_label(), + [hipe_rtl:mk_branch(X, eq, hipe_rtl:mk_imm(C), L, + hipe_rtl:label_name(NextLab), 0.5), + NextLab | + gen_switch_int(X, FailLab, Rest)]; +gen_switch_int(_, FailLab, []) -> + [hipe_rtl:mk_goto(FailLab)]. + diff --git a/lib/hipe/rtl/hipe_rtl_primops.erl b/lib/hipe/rtl/hipe_rtl_primops.erl new file mode 100644 index 0000000000..560e0259f8 --- /dev/null +++ b/lib/hipe/rtl/hipe_rtl_primops.erl @@ -0,0 +1,1259 @@ +%% -*- erlang-indent-level: 2 -*- +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2001-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% +%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Copyright (c) 2001 by Erik Johansson. All Rights Reserved +%% ==================================================================== +%% Filename : hipe_rtl_primops.erl +%% Purpose : +%% Notes : +%% History : * 2001-03-15 Erik Johansson ([email protected]): +%% Created. +%% +%% $Id$ +%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-module(hipe_rtl_primops). + +-export([gen_primop/3, gen_enter_primop/3, gen_call_builtin/6, + gen_enter_builtin/2]). + +%% -------------------------------------------------------------------- + +-include("../main/hipe.hrl"). +-include("../icode/hipe_icode_primops.hrl"). +-include("hipe_rtl.hrl"). +-include("hipe_literals.hrl"). + +%% -------------------------------------------------------------------- +%% Handling of known MFA builtins that are inline expanded + +gen_call_builtin(Fun, Dst, Args, IsGuard, Cont, Fail) -> + case Fun of + {erlang, apply, 3} -> + gen_apply(Dst, Args, Cont, Fail); + + {erlang, element, 2} -> + gen_element(Dst, Args, IsGuard, Cont, Fail); + + {erlang, self, 0} -> + gen_self(Dst, Cont); + + {erlang, is_tuple, 1} -> + gen_is_tuple(Dst, Args, Cont); + + {hipe_bifs, in_native, 0} -> + Dst1 = + case Dst of + [] -> %% The result is not used. + hipe_rtl:mk_new_var(); + [Dst0] -> Dst0 + end, + [hipe_rtl:mk_load_atom(Dst1, true), hipe_rtl:mk_goto(Cont)]; + + _ -> [] % not a builtin + end. + +%% (Recall that enters cannot occur within a catch-region in the same +%% function, so we do not need to consider fail-continuations here.) +%% TODO: should we inline expand more functions here? Cf. above. +gen_enter_builtin(Fun, Args) -> + case Fun of + {erlang, apply, 3} -> + gen_enter_apply(Args); + +%% TODO +%% {erlang, element, 2} -> +%% gen_enter_element(Args, IsGuard); + +%% TODO +%% {erlang, self, 0} -> +%% gen_enter_self(); + + {hipe_bifs, in_native, 0} -> + Dst = hipe_rtl:mk_new_var(), + [hipe_rtl:mk_load_atom(Dst, true), hipe_rtl:mk_return([Dst])]; + + _ -> [] % not a builtin + end. + +%% -------------------------------------------------------------------- +%% Generate code to jump to in case the inlined function fails. + +gen_fail_code(Fail, Type) -> + gen_fail_code(Fail, Type, false). + +gen_fail_code(Fail, Type, IsGuard) -> + case IsGuard of + true when Fail =/= [] -> + {Fail, []}; % go directly to target + false -> + NewLabel = hipe_rtl:mk_new_label(), + NewLabelName = hipe_rtl:label_name(NewLabel), + {NewLabelName, [NewLabel | fail_code(Fail, Type)]} + end. + +fail_code(Fail, Type) when is_atom(Type) -> + Var = hipe_rtl:mk_new_var(), + [hipe_rtl:mk_load_atom(Var, Type), + hipe_rtl_exceptions:gen_fail(error, [Var], Fail)]; +fail_code(Fail, {Type, Value}) when is_atom(Type) -> + Var = hipe_rtl:mk_new_var(), + [hipe_rtl:mk_load_atom(Var, Type), + hipe_rtl:mk_gctest(3), % room for a 2-tuple + gen_mk_tuple(Var,[Var,Value]), + hipe_rtl_exceptions:gen_fail(error, [Var], Fail)]. + +fp_fail_code(TmpFailLbl, FailLbl) -> + [TmpFailLbl | + hipe_rtl_arch:handle_fp_exception() ++ + [fail_code(FailLbl, badarith)]]. + +%% -------------------------------------------------------------------- +%% CALL PRIMOP +%% +%% @doc +%% Generates RTL code for primops. This is mostly a dispatch function. +%% Tail calls to primops (enter_fun, apply, etc.) are not handled here! +%% @end + +gen_primop({Op,Dst,Args,Cont,Fail}, IsGuard, ConstTab) -> + GotoCont = hipe_rtl:mk_goto(Cont), + case Op of + %% + %% Binary Syntax + %% + {hipe_bs_primop, BsOP} -> + {FailLabelName, FailCode1} = gen_fail_code(Fail, badarg, IsGuard), + {SysLimLblName, FailCode2} = gen_fail_code(Fail, system_limit, IsGuard), + {Code1,NewConstTab} = + hipe_rtl_binary:gen_rtl(BsOP, Dst, Args, Cont, FailLabelName, + SysLimLblName, ConstTab), + {[Code1,FailCode1,FailCode2], NewConstTab}; + %% + %% Other primops + %% + _ -> + Code = + case Op of + %% Arithmetic + '+' -> + %gen_extra_unsafe_add_2(Dst, Args, Cont); + gen_add_sub_2(Dst, Args, Cont, Fail, Op, add); + '-' -> + gen_add_sub_2(Dst, Args, Cont, Fail, Op, sub); + '*' -> + gen_mul_2(Dst, Args, Cont, Fail); + '/' -> + %% BIF call: am_Div -> nbif_div_2 -> erts_mixed_div + [hipe_rtl:mk_call(Dst, '/', Args, Cont, Fail, not_remote)]; + 'gen_add' -> + gen_general_add_sub(Dst, Args, Cont, Fail, '+'); + 'gen_sub' -> + gen_general_add_sub(Dst, Args, Cont, Fail, '-'); + 'unsafe_add' -> + %gen_extra_unsafe_add_2(Dst, Args, Cont); + gen_unsafe_add_sub_2(Dst, Args, Cont, Fail, '+', add); + 'extra_unsafe_add' -> + gen_extra_unsafe_add_2(Dst, Args, Cont); + 'unsafe_sub' -> + gen_unsafe_add_sub_2(Dst, Args, Cont, Fail, '-', sub); + 'extra_unsafe_sub' -> + gen_extra_unsafe_sub_2(Dst, Args, Cont); + %'unsafe_mul' -> + % gen_unsafe_mul_2(Dst, Args, Cont, Fail, '*'); + 'div' -> + %% BIF call: am_div -> nbif_intdiv_2 -> intdiv_2 + [hipe_rtl:mk_call(Dst, 'div', Args, Cont, Fail, not_remote)]; + 'rem' -> + %% BIF call: am_rem -> nbif_rem_2 -> rem_2 + [hipe_rtl:mk_call(Dst, 'rem', Args, Cont, Fail, not_remote)]; + 'band' -> + gen_bitop_2(Dst, Args, Cont, Fail, Op, 'and'); + 'bor' -> + gen_bitop_2(Dst, Args, Cont, Fail, Op, 'or'); + 'bxor' -> + gen_bitop_2(Dst, Args, Cont, Fail, Op, 'xor'); + 'bnot' -> + gen_bnot_2(Dst, Args, Cont, Fail, Op); + 'bsr'-> + %% BIF call: am_bsr -> nbif_bsr_2 -> bsr_2 + gen_bsr_2(Dst, Args, Cont, Fail, Op); + %[hipe_rtl:mk_call(Dst, 'bsr', Args, Cont, Fail, not_remote)]; + 'bsl' -> + %% BIF call: am_bsl -> nbif_bsl_2 -> bsl_2 + [hipe_rtl:mk_call(Dst, 'bsl', Args, Cont, Fail, not_remote)]; + unsafe_band -> + gen_unsafe_bitop_2(Dst, Args, Cont, 'and'); + unsafe_bor -> + gen_unsafe_bitop_2(Dst, Args, Cont, 'or'); + unsafe_bxor -> + gen_unsafe_bitop_2(Dst, Args, Cont, 'xor'); + unsafe_bnot -> + gen_unsafe_bnot_2(Dst, Args, Cont); + unsafe_bsr -> + gen_unsafe_bsr_2(Dst, Args, Cont); + unsafe_bsl -> + gen_unsafe_bsl_2(Dst, Args, Cont); + %%--------------------------------------------- + %% List handling + %%--------------------------------------------- + cons -> + case Dst of + [] -> %% The result is not used. + [GotoCont]; + [Dst1] -> + [gen_cons(Dst1, Args), GotoCont] + end; + unsafe_hd -> + case Dst of + [] -> %% The result is not used. + [GotoCont]; + [Dst1] -> + [gen_unsafe_hd(Dst1, Args), GotoCont] + end; + unsafe_tl -> + case Dst of + [] -> %% The result is not used. + [GotoCont]; + [Dst1] -> + [gen_unsafe_tl(Dst1, Args),GotoCont] + end; + %%--------------------------------------------- + %% Tuple handling + %%--------------------------------------------- + mktuple -> + case Dst of + [] -> %% The result is not used. + [GotoCont]; + [Dst1] -> + [gen_mk_tuple(Dst1, Args),GotoCont] + end; + #unsafe_element{index=N} -> + case Dst of + [] -> %% The result is not used. + [GotoCont]; + [Dst1] -> + [Tuple] = Args, + [gen_unsafe_element(Dst1, hipe_rtl:mk_imm(N), Tuple),GotoCont] + end; + #unsafe_update_element{index=N} -> + [Dst1] = Dst, + [Tuple, Value] = Args, + [gen_unsafe_update_element(Tuple, hipe_rtl:mk_imm(N), Value), + hipe_rtl:mk_move(Dst1, Tuple), + GotoCont]; + {element, [TupleInfo, IndexInfo]} -> + Dst1 = + case Dst of + [] -> %% The result is not used. + hipe_rtl:mk_new_var(); + [Dst0] -> Dst0 + end, + [Index, Tuple] = Args, + [gen_element_1(Dst1, Index, Tuple, IsGuard, Cont, Fail, + TupleInfo, IndexInfo)]; + + %%--------------------------------------------- + %% Apply-fixarity + %%--------------------------------------------- + #apply_N{arity = Arity} -> + gen_apply_N(Dst, Arity, Args, Cont, Fail); + + %%--------------------------------------------- + %% GC test + %%--------------------------------------------- + #gc_test{need = Need} -> + [hipe_rtl:mk_gctest(Need), GotoCont]; + + %%--------------------------------------------- + %% Process handling + %%--------------------------------------------- + redtest -> + [gen_redtest(1), GotoCont]; + %%--------------------------------------------- + %% Receives + %%--------------------------------------------- + check_get_msg -> + gen_check_get_msg(Dst, GotoCont, Fail); + next_msg -> + gen_next_msg(Dst, GotoCont); + select_msg -> + gen_select_msg(Dst, Cont); + clear_timeout -> + gen_clear_timeout(Dst, GotoCont); + set_timeout -> + %% BIF call: am_set_timeout -> nbif_set_timeout -> hipe_set_timeout + [hipe_rtl:mk_call(Dst, set_timeout, Args, Cont, Fail, not_remote)]; + suspend_msg -> + gen_suspend_msg(Dst, Cont); + %%--------------------------------------------- + %% Closures + %%--------------------------------------------- + call_fun -> + gen_call_fun(Dst, Args, Cont, Fail); + #mkfun{mfa=MFA, magic_num=MagicNum, index=Index} -> + case Dst of + [] -> %% The result is not used. + [GotoCont]; + _ -> + [gen_mkfun(Dst, MFA, MagicNum, Index, Args), GotoCont] + end; + #closure_element{n=N} -> + case Dst of + [] -> %% The result is not used. + [GotoCont]; + [Dst1] -> + [Closure] = Args, + [gen_closure_element(Dst1, hipe_rtl:mk_imm(N), Closure), + GotoCont] + end; + %%--------------------------------------------- + %% Floating point instructions. + %%--------------------------------------------- + fp_add -> + [Arg1, Arg2] = Args, + case Dst of + [] -> + hipe_rtl:mk_fp(hipe_rtl:mk_new_fpreg(), Arg1, 'fadd', Arg2); + [Dst1] -> + hipe_rtl:mk_fp(Dst1, Arg1, 'fadd', Arg2) + end; + fp_sub -> + [Arg1, Arg2] = Args, + case Dst of + [] -> + hipe_rtl:mk_fp(hipe_rtl:mk_new_fpreg(), Arg1, 'fsub', Arg2); + [Dst1] -> + hipe_rtl:mk_fp(Dst1, Arg1, 'fsub', Arg2) + end; + fp_mul -> + [Arg1, Arg2] = Args, + case Dst of + [] -> + hipe_rtl:mk_fp(hipe_rtl:mk_new_fpreg(), Arg1, 'fmul', Arg2); + [Dst1] -> + hipe_rtl:mk_fp(Dst1, Arg1, 'fmul', Arg2) + end; + fp_div -> + [Arg1, Arg2] = Args, + case Dst of + [] -> + hipe_rtl:mk_fp(hipe_rtl:mk_new_fpreg(), Arg1, 'fdiv', Arg2); + [Dst1] -> + hipe_rtl:mk_fp(Dst1, Arg1, 'fdiv', Arg2) + end; + fnegate -> + [Arg] = Args, + case Dst of + [] -> + hipe_rtl:mk_fp_unop(hipe_rtl:mk_new_fpreg(), Arg, 'fchs'); + [Dst1] -> + hipe_rtl:mk_fp_unop(Dst1, Arg, 'fchs') + end; + fclearerror -> + gen_fclearerror(); + fcheckerror -> + gen_fcheckerror(Cont, Fail); + conv_to_float -> + case Dst of + [] -> + gen_conv_to_float(hipe_rtl:mk_new_fpreg(), Args, Cont, Fail); + [Dst1] -> + gen_conv_to_float(Dst1, Args, Cont, Fail) + end; + unsafe_untag_float -> + [Arg] = Args, + case Dst of + [] -> + hipe_tagscheme:unsafe_untag_float(hipe_rtl:mk_new_fpreg(), + Arg); + [Dst1]-> + hipe_tagscheme:unsafe_untag_float(Dst1, Arg) + end; + unsafe_tag_float -> + [Arg] = Args, + case Dst of + [] -> + hipe_tagscheme:unsafe_tag_float(hipe_rtl:mk_new_var(), Arg); + [Dst1]-> + hipe_tagscheme:unsafe_tag_float(Dst1, Arg) + end; + + %% Only names listed above are accepted! MFA:s are not primops! + _ -> + erlang:error({bad_primop, Op}) + end, + {Code, ConstTab} + end. + +gen_enter_primop({Op, Args}, IsGuard, ConstTab) -> + case Op of + enter_fun -> + %% Tail-call to a closure must preserve tail-callness! + %% (Passing Continuation = [] to gen_call_fun/5 does this.) + Code = gen_call_fun([], Args, [], []), + {Code, ConstTab}; + + #apply_N{arity=Arity} -> + %% Tail-call to a closure must preserve tail-callness! + %% (Passing Continuation = [] to gen_apply_N/5 does this.) + Code = gen_apply_N([], Arity, Args, [], []), + {Code, ConstTab}; + + _ -> + %% All other primop tail calls are converted to call + return. + Dst = [hipe_rtl:mk_new_var()], + OkLab = hipe_rtl:mk_new_label(), + {Code,ConstTab1} = + gen_primop({Op,Dst,Args,hipe_rtl:label_name(OkLab),[]}, + IsGuard, ConstTab), + {Code ++ [OkLab, hipe_rtl:mk_return(Dst)], ConstTab1} + end. + + +%% -------------------------------------------------------------------- +%% ARITHMETIC +%% -------------------------------------------------------------------- + +%% +%% Inline addition & subtraction +%% + +gen_general_add_sub(Dst, Args, Cont, Fail, Op) -> + case Dst of + [] -> + [hipe_rtl:mk_call([hipe_rtl:mk_new_var()], + Op, Args, Cont, Fail, not_remote)]; + [Res] -> + [hipe_rtl:mk_call([Res], Op, Args, Cont, Fail, not_remote)] + end. + +gen_add_sub_2(Dst, Args, Cont, Fail, Op, AluOp) -> + [Arg1, Arg2] = Args, + GenCaseLabel = hipe_rtl:mk_new_label(), + case Dst of + [] -> + [hipe_tagscheme:test_two_fixnums(Arg1, Arg2, + hipe_rtl:label_name(GenCaseLabel))| + gen_op_general_case(hipe_rtl:mk_new_var(), + Op, Args, Cont, Fail, GenCaseLabel)]; + [Res] -> + [hipe_tagscheme:test_two_fixnums(Arg1, Arg2, + hipe_rtl:label_name(GenCaseLabel)), + hipe_tagscheme:fixnum_addsub(AluOp, Arg1, Arg2, Res, GenCaseLabel)| + gen_op_general_case(Res,Op, Args, Cont, Fail, GenCaseLabel)] + end. + +gen_unsafe_add_sub_2(Dst, Args, Cont, Fail, Op, AluOp) -> + [Arg1, Arg2] = Args, + case Dst of + [] -> + [hipe_rtl:mk_goto(Cont)]; + [Res] -> + case Fail of + []-> + GenCaseLabel = hipe_rtl:mk_new_label(), + [hipe_tagscheme:fixnum_addsub(AluOp, Arg1, Arg2, Res, GenCaseLabel)| + gen_op_general_case(Res,Op, Args, Cont, Fail, GenCaseLabel)]; + _ -> + [hipe_tagscheme:fixnum_addsub(AluOp, Arg1, Arg2, Res, + hipe_rtl:mk_label(Fail))] + end + end. + +gen_extra_unsafe_add_2(Dst, Args, Cont) -> + [Arg1, Arg2] = Args, + case Dst of + [] -> + [hipe_rtl:mk_goto(Cont)]; + [Res] -> + hipe_tagscheme:unsafe_fixnum_add(Arg1, Arg2, Res) + end. + +gen_extra_unsafe_sub_2(Dst, Args, Cont) -> + [Arg1, Arg2] = Args, + case Dst of + [] -> + [hipe_rtl:mk_goto(Cont)]; + [Res] -> + hipe_tagscheme:unsafe_fixnum_sub(Arg1, Arg2, Res) + end. + +gen_op_general_case(Res, Op, Args, Cont, Fail, GenCaseLabel) -> + [hipe_rtl:mk_goto(Cont), + GenCaseLabel, + hipe_rtl:mk_call([Res], Op, Args, Cont, Fail, not_remote)]. + +%% +%% Inline multiplication +%% + +gen_mul_2(Dst, Args, Cont, Fail) -> + [Arg1,Arg2] = Args, + GenCaseLabel = hipe_rtl:mk_new_label(), + {Res1,I2} = + case Dst of + [] -> + {hipe_rtl:mk_new_var(), []}; + [Res0] -> + {Res0, hipe_tagscheme:fixnum_mul(Arg1, Arg2, Res0, GenCaseLabel)} + end, + [hipe_tagscheme:test_two_fixnums(Arg1, Arg2, hipe_rtl:label_name(GenCaseLabel)), + I2, + %% BIF call: am_Times -> nbif_mul_2 -> erts_mixed_times + gen_op_general_case(Res1, '*', Args, Cont, Fail, GenCaseLabel)]. + +%% gen_unsafe_mul_2([Res], Args, Cont, Fail, Op) -> +%% [Arg1, Arg2] = Args, +%% GenCaseLabel = hipe_rtl:mk_new_label(), +%% [hipe_tagscheme:test_two_fixnums(Arg1, Arg2, +%% hipe_rtl:label_name(GenCaseLabel)), +%% hipe_tagscheme:fixnum_mul(Arg1, Arg2, Res, GenCaseLabel)| +%% gen_op_general_case(Res, Op, Args, Cont, Fail, GenCaseLabel)]. + +%% +%% Inline bitoperations. +%% Only works for band, bor and bxor. +%% The shift operations are too expensive to inline. +%% + +gen_bitop_2(Res, Args, Cont, Fail, Op, BitOp) -> + [Arg1, Arg2] = Args, + GenCaseLabel = hipe_rtl:mk_new_label(), + case Res of + [] -> %% The result is not used. + [hipe_tagscheme:test_two_fixnums(Arg1, Arg2, + hipe_rtl:label_name(GenCaseLabel))| + gen_op_general_case(hipe_rtl:mk_new_var(), + Op, Args, Cont, Fail, GenCaseLabel)]; + [Res0] -> + [hipe_tagscheme:test_two_fixnums(Arg1, Arg2, + hipe_rtl:label_name(GenCaseLabel)), + hipe_tagscheme:fixnum_andorxor(BitOp, Arg1, Arg2, Res0)| + gen_op_general_case(Res0, Op, Args, Cont, Fail, GenCaseLabel)] + end. + +gen_unsafe_bitop_2(Res, Args, Cont, BitOp) -> + case Res of + [] -> %% The result is not used. + [hipe_rtl:mk_goto(Cont)]; + [Res0] -> + [Arg1, Arg2] = Args, + [hipe_tagscheme:fixnum_andorxor(BitOp, Arg1, Arg2, Res0), + hipe_rtl:mk_goto(Cont)] + end. + +gen_bsr_2(Res, Args, Cont, Fail, Op) -> + [Arg1, Arg2] = Args, + GenCaseLabel = hipe_rtl:mk_new_label(), + case hipe_rtl:is_imm(Arg2) of + true -> + Val = hipe_tagscheme:fixnum_val(hipe_rtl:imm_value(Arg2)), + Limit = ?bytes_to_bits(hipe_rtl_arch:word_size()), + if + Val < Limit, Val >= 0 -> + case Res of + [] -> + FixLabel = hipe_rtl:mk_new_label(), + [hipe_tagscheme:test_fixnum(Arg1, + hipe_rtl:label_name(FixLabel), + hipe_rtl:label_name(GenCaseLabel), + 0.99), + FixLabel, + gen_op_general_case(hipe_rtl:mk_new_var(), Op, Args, Cont, Fail, + GenCaseLabel)]; + [Res0] -> + FixLabel = hipe_rtl:mk_new_label(), + [hipe_tagscheme:test_fixnum(Arg1, + hipe_rtl:label_name(FixLabel), + hipe_rtl:label_name(GenCaseLabel), + 0.99), + FixLabel, + hipe_tagscheme:fixnum_bsr(Arg1, Arg2, Res0), + gen_op_general_case(Res0, Op, Args, Cont, Fail, GenCaseLabel)] + end; + true -> + [hipe_rtl:mk_call(Res, 'bsr', Args, Cont, Fail, not_remote)] + end; + false -> + [hipe_rtl:mk_call(Res, 'bsr', Args, Cont, Fail, not_remote)] + end. + +gen_unsafe_bsr_2(Res, Args, Cont) -> + case Res of + [] -> %% The result is not used. + [hipe_rtl:mk_goto(Cont)]; + [Res0] -> + [Arg1, Arg2] = Args, + [hipe_tagscheme:fixnum_bsr(Arg1, Arg2, Res0), + hipe_rtl:mk_goto(Cont)] + end. + +gen_unsafe_bsl_2(Res, Args, Cont) -> + case Res of + [] -> %% The result is not used. + [hipe_rtl:mk_goto(Cont)]; + [Res0] -> + [Arg1, Arg2] = Args, + [hipe_tagscheme:fixnum_bsl(Arg1, Arg2, Res0), + hipe_rtl:mk_goto(Cont)] + end. + +%% +%% Inline not. +%% + +gen_bnot_2(Res, Args, Cont, Fail, Op) -> + [Arg] = Args, + GenCaseLabel = hipe_rtl:mk_new_label(), + case Res of + [] -> %% The result is not used. + FixLabel = hipe_rtl:mk_new_label(), + [hipe_tagscheme:test_fixnum(Arg, hipe_rtl:label_name(FixLabel), + hipe_rtl:label_name(GenCaseLabel), 0.99), + FixLabel, + gen_op_general_case(hipe_rtl:mk_new_var(), Op, Args, Cont, Fail, + GenCaseLabel)]; + + [Res0] -> + FixLabel = hipe_rtl:mk_new_label(), + [hipe_tagscheme:test_fixnum(Arg, hipe_rtl:label_name(FixLabel), + hipe_rtl:label_name(GenCaseLabel), 0.99), + FixLabel, + hipe_tagscheme:fixnum_not(Arg, Res0), + gen_op_general_case(Res0, Op, Args, Cont, Fail, GenCaseLabel)] + end. + +gen_unsafe_bnot_2(Res, Args, Cont) -> + case Res of + [] -> %% The result is not used. + [hipe_rtl:mk_goto(Cont)]; + [Res0] -> + [Arg1] = Args, + [hipe_tagscheme:fixnum_not(Arg1, Res0), + hipe_rtl:mk_goto(Cont)] + end. + + +%% -------------------------------------------------------------------- +%% + +%% +%% Inline cons +%% + +gen_cons(Dst, [Arg1, Arg2]) -> + Tmp = hipe_rtl:mk_new_reg(), + {GetHPInsn, HP, PutHPInsn} = hipe_rtl_arch:heap_pointer(), + WordSize = hipe_rtl_arch:word_size(), + HeapNeed = 2*WordSize, + [GetHPInsn, + hipe_rtl:mk_store(HP, hipe_rtl:mk_imm(0), Arg1), + hipe_rtl:mk_store(HP, hipe_rtl:mk_imm(WordSize), Arg2), + hipe_rtl:mk_move(Tmp, HP), + hipe_tagscheme:tag_cons(Dst, Tmp), + hipe_rtl:mk_alu(HP, HP, add, hipe_rtl:mk_imm(HeapNeed)), + PutHPInsn]. + +%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% -------------------------------------------------------------------- +%% Handling of closures... +%% -------------------------------------------------------------------- + +%% -------------------------------------------------------------------- +%% gen_mkfun +%% +%% The gc_test should have expanded to +%% unsigned needed = ERL_FUN_SIZE + num_free; +%% ErlFunThing* funp = (ErlFunThing *) HAlloc(p, needed); +%% +%% The code generated should do the equivalent of: +%% Copy arguments to the fun thing +%% Eterm* hp = funp->env; +%% for (i = 0; i < num_free; i++) { +%% *hp++ = reg[i]; +%% } +%% +%% Fill in fileds +%% funp->thing_word = HEADER_FUN; +%% funp->fe = fe; +%% funp->num_free = num_free; +%% funp->creator = p->id; +%% funp->native_code = fe->native_code; +%% Increase refcount +%% fe->refc++; +%% +%% Link to the process off_heap.funs list +%% funp->next = p->off_heap.funs; +%% p->off_heap.funs = funp; +%% +%% Tag the thing +%% return make_fun(funp); +%% +gen_mkfun([Dst], {_Mod, _FunId, _Arity} = MFidA, MagicNr, Index, FreeVars) -> + {GetHPInsn, HP, PutHPInsn} = hipe_rtl_arch:heap_pointer(), + NumFree = length(FreeVars), + + %% Copy arguments to the fun thing + %% Eterm* hp = funp->env; + %% for (i = 0; i < num_free; i++) { + %% *hp++ = reg[i]; + %% } + CopyFreeVarsCode = gen_free_vars(FreeVars, HP), + + %% Fill in fields + %% funp->thing_word = HEADER_FUN; + %% funp->fe = fe; + %% funp->num_free = num_free; + %% funp->creator = p->id; + %% funp->native_code = fe->native_code; + %% Increase refcount + %% fe->refc++; + SkeletonCode = gen_fun_thing_skeleton(HP, MFidA, NumFree, MagicNr, Index), + + %% Link to the process off_heap.funs list + %% funp->next = p->off_heap.funs; + %% p->off_heap.funs = funp; + LinkCode = gen_link_closure(HP), + + %% Tag the thing and increase the heap_pointer. + %% make_fun(funp); + WordSize�= hipe_rtl_arch:word_size(), + HeapNeed = (?ERL_FUN_SIZE + NumFree) * WordSize, + TagCode = [hipe_tagscheme:tag_fun(Dst, HP), + %% AdjustHPCode + hipe_rtl:mk_alu(HP, HP, add, hipe_rtl:mk_imm(HeapNeed)), + PutHPInsn], + [[GetHPInsn | CopyFreeVarsCode], SkeletonCode, LinkCode, TagCode]. + + +gen_fun_thing_skeleton(FunP, FunName={_Mod,_FunId,Arity}, NumFree, + MagicNr, Index) -> + %% Assumes that funp == heap_pointer + %% Fill in fields + %% funp->thing_word = HEADER_FUN; + %% funp->fe = fe; + %% funp->num_free = num_free; + %% funp->creator = p->id; + %% funp->native_code = fe->native_code; + %% And creates a fe (at load time). + FeVar = hipe_rtl:mk_new_reg(), + PidVar = hipe_rtl:mk_new_reg_gcsafe(), + NativeVar = hipe_rtl:mk_new_reg(), + + [hipe_rtl:mk_load_address(FeVar, {FunName, MagicNr, Index}, closure), + store_struct_field(FunP, ?EFT_FE, FeVar), + load_struct_field(NativeVar, FeVar, ?EFE_NATIVE_ADDRESS), + store_struct_field(FunP, ?EFT_NATIVE_ADDRESS, NativeVar), + + store_struct_field(FunP, ?EFT_ARITY, hipe_rtl:mk_imm(Arity-NumFree)), + + gen_inc_refc(FeVar, ?EFE_REFC), + + store_struct_field(FunP, ?EFT_NUM_FREE, hipe_rtl:mk_imm(NumFree)), + load_p_field(PidVar, ?P_ID), + store_struct_field(FunP, ?EFT_CREATOR, PidVar), + store_struct_field(FunP, ?EFT_THING, hipe_tagscheme:mk_fun_header())]. + +gen_inc_refc(Ptr, Offset) -> + case ?ERTS_IS_SMP of + 0 -> gen_inc_refc_notsmp(Ptr, Offset); + 1 -> gen_inc_refc_smp(Ptr, Offset) + end. + +gen_inc_refc_notsmp(Ptr, Offset) -> + Refc = hipe_rtl:mk_new_reg(), + [load_struct_field(Refc, Ptr, Offset, int32), + hipe_rtl:mk_alu(Refc, Refc, add, hipe_rtl:mk_imm(1)), + store_struct_field(Ptr, Offset, Refc, int32)]. + +gen_inc_refc_smp(Ptr, Offset) -> + Refc = hipe_rtl:mk_new_reg(), + [hipe_rtl:mk_alu(Refc, Ptr, 'add', hipe_rtl:mk_imm(Offset)), + hipe_rtl:mk_call([], 'atomic_inc', [Refc], [], [], not_remote)]. + +gen_link_closure(FUNP) -> + case ?P_OFF_HEAP_FUNS of + [] -> gen_link_closure_non_private(FUNP); + _ -> gen_link_closure_private(FUNP) + end. + +gen_link_closure_private(FUNP) -> + %% Link to the process off_heap.funs list + %% funp->next = p->off_heap.funs; + %% p->off_heap.funs = funp; + FunsVar = hipe_rtl:mk_new_reg(), + + [load_p_field(FunsVar,?P_OFF_HEAP_FUNS), + hipe_rtl:mk_store(FUNP, hipe_rtl:mk_imm(?EFT_NEXT), FunsVar), + store_p_field(FUNP,?P_OFF_HEAP_FUNS)]. + +gen_link_closure_non_private(_FUNP) -> []. + +load_p_field(Dst,Offset) -> + hipe_rtl_arch:pcb_load(Dst, Offset). +store_p_field(Src, Offset) -> + hipe_rtl_arch:pcb_store(Offset, Src). + +store_struct_field(StructP, Offset, Src) -> + hipe_rtl:mk_store(StructP, hipe_rtl:mk_imm(Offset), Src). + +load_struct_field(Dest, StructP, Offset) -> + hipe_rtl:mk_load(Dest, StructP, hipe_rtl:mk_imm(Offset)). + +store_struct_field(StructP, Offset, Src, int32) -> + hipe_rtl:mk_store(StructP, hipe_rtl:mk_imm(Offset), Src, int32). + +load_struct_field(Dest, StructP, Offset, int32) -> + hipe_rtl:mk_load(Dest, StructP, hipe_rtl:mk_imm(Offset), int32, signed). + +gen_free_vars(Vars, HPReg) -> + HPVar = hipe_rtl:mk_new_var(), + WordSize�= hipe_rtl_arch:word_size(), + [hipe_rtl:mk_alu(HPVar, HPReg, add, hipe_rtl:mk_imm(?EFT_ENV)) | + gen_free_vars(Vars, HPVar, 0, WordSize, [])]. + +gen_free_vars([Var|Vars], EnvPVar, Offset, WordSize, AccCode) -> + Code = hipe_rtl:mk_store(EnvPVar, hipe_rtl:mk_imm(Offset), Var), + gen_free_vars(Vars, EnvPVar, Offset + WordSize, WordSize, + [Code|AccCode]); +gen_free_vars([], _, _, _, AccCode) -> AccCode. + +%% ------------------------------------------------------------------ +%% +%% call_fun (also handles enter_fun when Continuation = []) + +gen_call_fun(Dst, ArgsAndFun, Continuation, Fail) -> + NAddressReg = hipe_rtl:mk_new_reg(), + ArityReg = hipe_rtl:mk_new_reg_gcsafe(), + [Fun|RevArgs] = lists:reverse(ArgsAndFun), + + %% {BadFunLabName, BadFunCode} = gen_fail_code(Fail, {badfun, Fun}), + Args = lists:reverse(RevArgs), + NonClosureLabel = hipe_rtl:mk_new_label(), + CallNonClosureLabel = hipe_rtl:mk_new_label(), + BadFunLabName = hipe_rtl:label_name(NonClosureLabel), + BadFunCode = + [NonClosureLabel, + hipe_rtl:mk_call([NAddressReg], + 'nonclosure_address', + [Fun, hipe_rtl:mk_imm(length(Args))], + hipe_rtl:label_name(CallNonClosureLabel), + Fail, + not_remote), + CallNonClosureLabel, + case Continuation of + [] -> + hipe_rtl:mk_enter(NAddressReg, Args, not_remote); + _ -> + hipe_rtl:mk_call(Dst, NAddressReg, Args, + Continuation, Fail, not_remote) + end], + + {BadArityLabName, BadArityCode} = gen_fail_code(Fail, {badarity, Fun}), + + CheckGetCode = + hipe_tagscheme:if_fun_get_arity_and_address(ArityReg, NAddressReg, + Fun, BadFunLabName, + 0.9), + CheckArityCode = check_arity(ArityReg, length(RevArgs), BadArityLabName), + CallCode = + case Continuation of + [] -> %% This is a tailcall + [hipe_rtl:mk_enter(NAddressReg, ArgsAndFun, not_remote)]; + _ -> %% Ordinary call + [hipe_rtl:mk_call(Dst, NAddressReg, ArgsAndFun, + Continuation, Fail, not_remote)] + end, + [CheckGetCode, CheckArityCode, CallCode, BadFunCode, BadArityCode]. + +check_arity(ArityReg, Arity, BadArityLab) -> + TrueLab1 = hipe_rtl:mk_new_label(), + [hipe_rtl:mk_branch(ArityReg, eq, hipe_rtl:mk_imm(Arity), + hipe_rtl:label_name(TrueLab1), BadArityLab, 0.9), + TrueLab1]. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% +%% apply +%% +%% The tail call case is not handled here. + +gen_apply(Dst, Args = [_M,_F,_AppArgs], Cont, Fail) -> + %% Dst can be [Res] or []. + [hipe_rtl:mk_call(Dst, hipe_apply, Args, Cont, Fail, not_remote)]. + +gen_enter_apply(Args=[_M,_F,_AppArgs]) -> + %% 'apply' in tail-call context + [hipe_rtl:mk_enter(hipe_apply, Args, not_remote)]. + +%% +%% apply_N +%% also handles tailcall case (Cont=[]) +%% + +gen_apply_N(Dst, Arity, [M,F|CallArgs], Cont, Fail) -> + MM = hipe_rtl:mk_new_var(), + NotModuleLbl = hipe_rtl:mk_new_label(), + NotModuleLblName = hipe_rtl:label_name(NotModuleLbl), + Tuple = M, + Index = hipe_rtl:mk_imm(1), + IndexInfo = 1, + [hipe_tagscheme:element(MM, Index, Tuple, NotModuleLblName, unknown, IndexInfo), + gen_apply_N_common(Dst, Arity+1, MM, F, CallArgs ++ [M], Cont, Fail), + NotModuleLbl, + gen_apply_N_common(Dst, Arity, M, F, CallArgs, Cont, Fail)]. + +gen_apply_N_common(Dst, Arity, M, F, CallArgs, Cont, Fail) -> + CallLabel = hipe_rtl:mk_new_label(), + CodeAddress = hipe_rtl:mk_new_reg(), + [hipe_rtl:mk_call([CodeAddress], find_na_or_make_stub, + [M,F,hipe_rtl:mk_imm(hipe_tagscheme:mk_fixnum(Arity))], + hipe_rtl:label_name(CallLabel), + Fail, not_remote), + CallLabel, + case Cont of + [] -> % tailcall + hipe_rtl:mk_enter(CodeAddress, CallArgs, not_remote); + _ -> % recursive call + hipe_rtl:mk_call(Dst, CodeAddress, CallArgs, Cont, Fail, not_remote) + end]. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% +%% mkTuple +%% + +gen_mk_tuple(Dst, Elements) -> + {GetHPInsn, HP, PutHPInsn} = hipe_rtl_arch:heap_pointer(), + Arity = length(Elements), + WordSize = hipe_rtl_arch:word_size(), + HeapNeed = (Arity+1)*WordSize, + [GetHPInsn, + gen_tuple_header(HP, Arity), + set_tuple_elements(HP, WordSize, WordSize, Elements, []), + hipe_tagscheme:tag_tuple(Dst, HP), + hipe_rtl:mk_alu(HP, HP, add, hipe_rtl:mk_imm(HeapNeed)), + PutHPInsn]. + +set_tuple_elements(HP, Offset, WordSize, [Element|Elements], Stores) -> + Store = hipe_rtl:mk_store(HP, hipe_rtl:mk_imm(Offset), Element), + set_tuple_elements(HP, Offset+WordSize, WordSize, Elements, [Store|Stores]); +set_tuple_elements(_, _, _, [], Stores) -> + lists:reverse(Stores). + +%% +%% @doc Generate RTL code for the reduction test. +%% +gen_redtest(Amount) -> + {GetFCallsInsn, FCalls, PutFCallsInsn} = hipe_rtl_arch:fcalls(), + SuspendLabel = hipe_rtl:mk_new_label(), + StayLabel = hipe_rtl:mk_new_label(), + ContinueLabel = hipe_rtl:mk_new_label(), + [GetFCallsInsn, + hipe_rtl:mk_alub(FCalls, FCalls, 'sub', hipe_rtl:mk_imm(Amount), 'lt', + hipe_rtl:label_name(SuspendLabel), + hipe_rtl:label_name(StayLabel), 0.01), + SuspendLabel, + %% The suspend path should not execute PutFCallsInsn. + hipe_rtl:mk_call([], suspend_0, [], + hipe_rtl:label_name(ContinueLabel), [], not_remote), + StayLabel, + PutFCallsInsn, + ContinueLabel]. + +gen_self(Dst, Cont) -> + case Dst of + [] -> %% The result is not used. + [hipe_rtl:mk_goto(Cont)]; + [Dst1] -> + [load_p_field(Dst1, ?P_ID), + hipe_rtl:mk_goto(Cont)] + end. + +%% +%% @doc Generate is_tuple/1 test +%% +gen_is_tuple(Dst, [Arg], Cont) -> + GotoCont = hipe_rtl:mk_goto(Cont), + case Dst of + [] -> %% The result is not used. + [GotoCont]; + [Dst1] -> + TrueLabel = hipe_rtl:mk_new_label(), + FalseLabel = hipe_rtl:mk_new_label(), + [hipe_tagscheme:test_tuple(Arg, hipe_rtl:label_name(TrueLabel), + hipe_rtl:label_name(FalseLabel), 0.5), + TrueLabel, + hipe_rtl:mk_load_atom(Dst1, true), + GotoCont, + FalseLabel, + hipe_rtl:mk_load_atom(Dst1, false), + GotoCont] + end. + +%% +%% @doc Generate unsafe head +%% +gen_unsafe_hd(Dst, [Arg]) -> hipe_tagscheme:unsafe_car(Dst, Arg). + +%% +%% @doc Generate unsafe tail +%% +gen_unsafe_tl(Dst, [Arg]) -> hipe_tagscheme:unsafe_cdr(Dst, Arg). + +%% +%% element +%% +gen_element(Dst, Args, IsGuard, Cont, Fail) -> + Dst1 = + case Dst of + [] -> %% The result is not used. + hipe_rtl:mk_new_var(); + [Dst0] -> Dst0 + end, + [Index, Tuple] = Args, + gen_element_1(Dst1, Index, Tuple, IsGuard, Cont, Fail, unknown, unknown). + +gen_element_1(Dst, Index, Tuple, IsGuard, Cont, Fail, TupleInfo, IndexInfo) -> + {FailLblName, FailCode} = gen_fail_code(Fail, badarg, IsGuard), + [hipe_tagscheme:element(Dst, Index, Tuple, FailLblName, TupleInfo, IndexInfo), + hipe_rtl:mk_goto(Cont), + FailCode]. + +%% +%% unsafe element +%% +gen_unsafe_element(Dst, Index, Tuple) -> + case hipe_rtl:is_imm(Index) of + true -> hipe_tagscheme:unsafe_constant_element(Dst, Index, Tuple); + false -> ?EXIT({illegal_index_to_unsafe_element,Index}) + end. + +gen_unsafe_update_element(Tuple, Index, Value) -> + case hipe_rtl:is_imm(Index) of + true -> + hipe_tagscheme:unsafe_update_element(Tuple, Index, Value); + false -> + ?EXIT({illegal_index_to_unsafe_update_element,Index}) + end. + + +gen_closure_element(Dst, Index, Closure) -> + hipe_tagscheme:unsafe_closure_element(Dst, Index, Closure). + +%% +%% @doc Generate RTL code that writes a tuple header. +%% +gen_tuple_header(Ptr, Arity) -> + Header = hipe_tagscheme:mk_arityval(Arity), + hipe_rtl:mk_store(Ptr, hipe_rtl:mk_imm(0), hipe_rtl:mk_imm(Header)). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% +%%% Receives + +gen_check_get_msg(Dsts, GotoCont, Fail) -> + gen_check_get_msg_outofline(Dsts, GotoCont, Fail). + +gen_clear_timeout([], GotoCont) -> + case ?ERTS_IS_SMP of + 0 -> gen_clear_timeout_notsmp(GotoCont); + 1 -> gen_clear_timeout_smp(GotoCont) + end. + +-ifdef(notdef). % for reference, currently unused +%%% check_get_msg is: +%%% if (!PEEK_MESSAGE(p)) goto Fail; +%%% Dst = ERL_MESSAGE_TERM(PEEK_MESSAGE(p)); +%%% i.e., +%%% ErlMessage **save = p->msg.save; +%%% ErlMessage *msg = *save; +%%% if (!msg) goto Fail; +%%% Dst = msg->m[0]; +gen_check_get_msg_inline(Dsts, GotoCont, Fail) -> + Save = hipe_rtl:mk_new_reg(), + Msg = hipe_rtl:mk_new_reg(), + TrueLbl = hipe_rtl:mk_new_label(), + [load_p_field(Save, ?P_MSG_SAVE), + load_struct_field(Msg, Save, 0), + hipe_rtl:mk_branch(Msg, eq, hipe_rtl:mk_imm(0), Fail, + hipe_rtl:label_name(TrueLbl), 0.1), + TrueLbl | + case Dsts of + [Dst] -> + [load_struct_field(Dst, Msg, ?MSG_MESSAGE), + GotoCont]; + [] -> % receive which throws away the message + [GotoCont] + end]. +-endif. + +%%% next_msg is: +%%% SAVE_MESSAGE(p); +%%% i.e., +%%% ErlMessage **save = p->msg.save; +%%% ErlMessage *msg = *save; +%%% ErlMessage **next = &msg->next; +%%% p->msg.save = next; +gen_next_msg([], GotoCont) -> + Save = hipe_rtl:mk_new_reg(), + Msg = hipe_rtl:mk_new_reg(), + Next = hipe_rtl:mk_new_reg(), + [load_p_field(Save, ?P_MSG_SAVE), + load_struct_field(Msg, Save, 0), + hipe_rtl:mk_alu(Next, Msg, 'add', hipe_rtl:mk_imm(?MSG_NEXT)), + store_p_field(Next, ?P_MSG_SAVE), + GotoCont]. + +%%% clear_timeout is: +%%% p->flags &= ~F_TIMO; JOIN_MESSAGE(p); +%%% i.e., +%%% p->flags &= ~F_TIMO; +%%% p->msg.save = &p->msg.first; +gen_clear_timeout_notsmp(GotoCont) -> + Flags1 = hipe_rtl:mk_new_reg(), + Flags2 = hipe_rtl:mk_new_reg_gcsafe(), + First = hipe_rtl:mk_new_reg_gcsafe(), + [load_p_field(Flags1, ?P_FLAGS), + hipe_rtl:mk_alu(Flags2, Flags1, 'and', hipe_rtl:mk_imm(bnot(?F_TIMO))), + store_p_field(Flags2, ?P_FLAGS), + hipe_rtl_arch:pcb_address(First, ?P_MSG_FIRST), + store_p_field(First, ?P_MSG_SAVE), + GotoCont]. + +gen_check_get_msg_outofline(Dsts, GotoCont, Fail) -> + RetLbl = hipe_rtl:mk_new_label(), + TrueLbl = hipe_rtl:mk_new_label(), + Tmp = hipe_rtl:mk_new_reg(), + TheNonValue = hipe_rtl:mk_imm(hipe_tagscheme:mk_non_value()), + [hipe_rtl_arch:call_bif([Tmp], check_get_msg, [], + hipe_rtl:label_name(RetLbl), []), + RetLbl, + hipe_rtl:mk_branch(Tmp, eq, TheNonValue, Fail, + hipe_rtl:label_name(TrueLbl), 0.1), + TrueLbl | + case Dsts of + [Dst] -> + [hipe_rtl:mk_move(Dst, Tmp), + GotoCont]; + [] -> % receive which throws away the message + [GotoCont] + end]. + +gen_clear_timeout_smp(GotoCont) -> + RetLbl = hipe_rtl:mk_new_label(), + [hipe_rtl_arch:call_bif([], clear_timeout, [], + hipe_rtl:label_name(RetLbl), []), + RetLbl, + GotoCont]. + +gen_select_msg([], Cont) -> + [hipe_rtl_arch:call_bif([], select_msg, [], Cont, [])]. + +gen_suspend_msg([], Cont) -> + [hipe_rtl:mk_call([], suspend_msg, [], Cont, [], not_remote)]. + +%% -------------------------------------------------------------------- +%% +%% Floating point handling +%% + +gen_fclearerror() -> + case ?P_FP_EXCEPTION of + [] -> + []; + Offset -> + Tmp = hipe_rtl:mk_new_reg(), + FailLbl = hipe_rtl:mk_new_label(), + ContLbl = hipe_rtl:mk_new_label(), + ContLblName = hipe_rtl:label_name(ContLbl), + [hipe_rtl_arch:pcb_load(Tmp, Offset), + hipe_rtl:mk_branch(Tmp, eq, hipe_rtl:mk_imm(0), ContLblName, + hipe_rtl:label_name(FailLbl), 0.9), + FailLbl, + hipe_rtl:mk_call([], 'fclearerror_error', [], [], [], not_remote), + hipe_rtl:mk_goto(ContLblName), + ContLbl] + end. + +gen_fcheckerror(ContLbl, FailLbl) -> + case ?P_FP_EXCEPTION of + [] -> + []; + Offset -> + Tmp = hipe_rtl:mk_new_reg(), + TmpFailLbl0 = hipe_rtl:mk_new_label(), + FailCode = fp_fail_code(TmpFailLbl0, FailLbl), + PreFailLbl = hipe_rtl:mk_new_label(), + hipe_rtl_arch:fwait() ++ + [hipe_rtl_arch:pcb_load(Tmp, Offset), + hipe_rtl:mk_branch(Tmp, eq, hipe_rtl:mk_imm(0), ContLbl, + hipe_rtl:label_name(PreFailLbl), 0.9), + PreFailLbl, + hipe_rtl_arch:pcb_store(Offset, hipe_rtl:mk_imm(0)), + hipe_rtl:mk_goto(hipe_rtl:label_name(TmpFailLbl0)) | + FailCode] + end. + +gen_conv_to_float(Dst, [Src], ContLbl, FailLbl) -> + case hipe_rtl:is_var(Src) of + true -> + Tmp = hipe_rtl:mk_new_var(), + TmpReg = hipe_rtl:mk_new_reg_gcsafe(), + TrueFixNum = hipe_rtl:mk_new_label(), + ContFixNum = hipe_rtl:mk_new_label(), + TrueFp = hipe_rtl:mk_new_label(), + ContFp = hipe_rtl:mk_new_label(), + ContBigNum = hipe_rtl:mk_new_label(), + TestFixNum = hipe_tagscheme:test_fixnum(Src, + hipe_rtl:label_name(TrueFixNum), + hipe_rtl:label_name(ContFixNum), + 0.5), + TestFp = hipe_tagscheme:test_flonum(Src, hipe_rtl:label_name(TrueFp), + hipe_rtl:label_name(ContFp), 0.5), + GotoCont = hipe_rtl:mk_goto(ContLbl), + TmpFailLbl0 = hipe_rtl:mk_new_label(), + FailCode = fp_fail_code(TmpFailLbl0, FailLbl), + + TestFixNum ++ + [TrueFixNum, + hipe_tagscheme:untag_fixnum(TmpReg, Src), + hipe_rtl:mk_fconv(Dst, TmpReg), + GotoCont, + ContFixNum] ++ + TestFp ++ + [TrueFp, + hipe_tagscheme:unsafe_untag_float(Dst, Src), + GotoCont, + ContFp] ++ + [hipe_rtl:mk_call([Tmp], conv_big_to_float, [Src], + hipe_rtl:label_name(ContBigNum), + hipe_rtl:label_name(TmpFailLbl0), not_remote)]++ + FailCode ++ + [ContBigNum, + hipe_tagscheme:unsafe_untag_float(Dst, Tmp)]; + _ -> + %% This must be an attempt to convert an illegal term. + [gen_fail_code(FailLbl, badarith)] + end. + diff --git a/lib/hipe/rtl/hipe_rtl_ssa.erl b/lib/hipe/rtl/hipe_rtl_ssa.erl new file mode 100644 index 0000000000..f55cc0dd5c --- /dev/null +++ b/lib/hipe/rtl/hipe_rtl_ssa.erl @@ -0,0 +1,93 @@ +%% -*- erlang-indent-level: 2 -*- +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2004-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% +%% +%%---------------------------------------------------------------------- +%% File : hipe_rtl_ssa.erl +%% Author : Kostis Sagonas <[email protected]> +%% Created : 30 Jan 2004 +%% Purpose : Provides interface functions for converting RTL code into +%% SSA form and back using the generic SSA converter. +%%---------------------------------------------------------------------- + +-module(hipe_rtl_ssa). + +-export([uses_to_rename/1]). %% needed by hipe_rtl_ssa_const_prop + +%% The following defines are needed by the included file below +-define(CODE, hipe_rtl). +-define(CFG, hipe_rtl_cfg). +-define(LIVENESS, hipe_rtl_liveness). + +-include("hipe_rtl.hrl"). +-include("../ssa/hipe_ssa.inc"). + +%%---------------------------------------------------------------------- +%% Auxiliary operations which seriously differ between Icode and RTL. +%%---------------------------------------------------------------------- + +defs_to_rename(Statement) -> + Defs = hipe_rtl:defines(Statement), + [D || D <- Defs, not hipe_rtl_arch:is_precoloured(D)]. + +uses_to_rename(Statement) -> + Uses = hipe_rtl:uses(Statement), + [U || U <- Uses, not hipe_rtl_arch:is_precoloured(U)]. + +liveout_no_succ() -> + hipe_rtl_arch:live_at_return(). + +%----------------------------------------------------------------------- + +reset_var_indx() -> + hipe_gensym:set_var(rtl, hipe_rtl_arch:first_virtual_reg()). + +%%---------------------------------------------------------------------- + +is_fp_temp(Temp) -> + hipe_rtl:is_fpreg(Temp). + +mk_new_fp_temp() -> + hipe_rtl:mk_new_fpreg(). + +%----------------------------------------------------------------------- +%% Procedure : makePhiMove +%% Purpose : Create an RTL-specific version of a move instruction +%% depending on the type of the arguments. +%% Arguments : Dst, Src - the arguments of a Phi instruction that is +%% to be moved up the predecessor block as part +%% of the SSA un-convert phase. +%% Returns : Code +%% Note : ?CODE here is hipe_rtl +%%---------------------------------------------------------------------- + +makePhiMove(Dst, Src) -> + case hipe_rtl:is_fpreg(Dst) of + false -> + case hipe_rtl:is_fpreg(Src) of %% this test is just a sanity check + false -> + hipe_rtl:mk_move(Dst, Src) + end; + true -> + case hipe_rtl:is_fpreg(Src) of %% this test is just a sanity check + true -> + hipe_rtl:mk_fmove(Dst, Src) + end + end. + +%----------------------------------------------------------------------- diff --git a/lib/hipe/rtl/hipe_rtl_ssa_avail_expr.erl b/lib/hipe/rtl/hipe_rtl_ssa_avail_expr.erl new file mode 100644 index 0000000000..cae6da542f --- /dev/null +++ b/lib/hipe/rtl/hipe_rtl_ssa_avail_expr.erl @@ -0,0 +1,357 @@ +%%% +%%% %CopyrightBegin% +%%% +%%% Copyright Ericsson AB 2007-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% +%%% +%%%------------------------------------------------------------------- +%%% File : hipe_rtl_ssa_avail_expr.erl +%%% Author : Per Gustafsson <[email protected]> +%%% Description : A couple of optimizations on rtl_ssa +%%% 1. Remove unnecessary loads (Global) +%%% 2. Remove unnecessary stores (Local) +%%% 3. Remove unnecessary tag/untag operations +%%% +%%% Changed : 7 Feb 2007 by Per Gustafsson <[email protected]> +%%%------------------------------------------------------------------- +-module(hipe_rtl_ssa_avail_expr). + +-export([cfg/1]). + +-include("../main/hipe.hrl"). +-include("hipe_rtl.hrl"). + +cfg(CFG) -> + CFG1 = remove_loads(CFG), + CFG2 = remove_stores(CFG1), + CFG3 = optimize_fixnums(CFG2), + hipe_rtl_ssa:remove_dead_code(CFG3). + +%%%============================================================================= +%%% +%%% Remove unnecessary loads +%%% +%%%============================================================================= + +remove_loads(CFG) -> + LoadsFun = fun spread_info/2, + Info=fix_point(CFG, LoadsFun), + pass_through(CFG, LoadsFun, Info). + +spread_info(Code, Info) -> + lists:foldl(fun do_instr/2, {[],Info}, Code). + +do_instr(Instr, {Acc,Info}) -> + case Instr of + #call{} -> + {Acc++[Instr], new_env()}; + #store{} -> + {Acc++[Instr], new_env()}; + #gctest{} -> + {Acc++[Instr], new_env()}; + #load{} -> + Dst = hipe_rtl:load_dst(Instr), + LoadType = {hipe_rtl:load_src(Instr), hipe_rtl:load_offset(Instr), + hipe_rtl:load_size(Instr), hipe_rtl:load_sign(Instr)}, + NewInstr = + case lookup_y(LoadType, Info) of + none -> + Instr; + Var -> + hipe_rtl:mk_move(Dst, Var) + end, + Fun = fun load_filter_fun/2, + {Acc++[NewInstr], insert(Dst,LoadType,remove_defines(Instr,Info,Fun))}; + _ -> + {Acc++[Instr],remove_defines(Instr,Info,fun load_filter_fun/2)} + end. + +load_filter_fun({X1,{X2,X3,_,_}},PreColDefs) -> + not (lists:member(X1,PreColDefs) or + lists:member(X2,PreColDefs) or + lists:member(X3,PreColDefs)). + +%%%============================================================================= +%%% +%%% Remove unnecessary stores (local optimization) +%%% +%%%============================================================================= + +remove_stores(CFG) -> + pass_through(CFG, fun remove_store/2, new_info()). + +remove_store(Code,_) -> + remove_store_from_bb(Code). + +remove_store_from_bb(Code) -> + remove_store_from_bb(lists:reverse(Code), new_env(), []). + +remove_store_from_bb([Instr|Instrs], Env, Acc) -> + {NewAcc, NewEnv} = + case Instr of + #call{} -> + {[Instr|Acc],new_env()}; + #gctest{} -> + {[Instr|Acc], new_env()}; + #store{} -> + Base = hipe_rtl:store_base(Instr), + Offset = hipe_rtl:store_offset(Instr), + Size = hipe_rtl:store_size(Instr), + StoreType = {Base, Offset, Size}, + case lookup_y(StoreType, Env) of + none -> + {[Instr|Acc], insert(StoreType, true, Env)}; + true -> + {Acc, Env} + end; + #load{} -> + {[Instr|Acc],new_env()}; + _ -> + {[Instr|Acc],remove_defines(Instr,Env,fun store_filter_fun/2)} + end, + remove_store_from_bb(Instrs, NewEnv, NewAcc); +remove_store_from_bb([], Env, Acc) -> + {Acc,Env}. + +store_filter_fun({{X1,X2,_},_},PreColDefs) -> + not (lists:member(X1,PreColDefs) or + lists:member(X2,PreColDefs)). + +%%%============================================================================= +%%% +%%% Optimize Fixnum Operations +%%% +%%%============================================================================= + +optimize_fixnums(CFG) -> + FixFun = fun fixnum_opt/2, + Info=fix_point(CFG, FixFun), + pass_through(CFG, FixFun, Info). + +fixnum_opt(Code,Info) -> + lists:foldl(fun do_fixnums/2, {[],Info}, Code). + +do_fixnums(Instr, {Acc,Env}) -> + case Instr of + #call{} -> + {Acc++[Instr],Env}; + #gctest{} -> + {Acc++[Instr],Env}; + #fixnumop{dst=Dst,src=Src} -> + case lookup_y(Src,Env) of + none -> + case lookup_x(Src,Env) of + none -> + case hipe_rtl_arch:is_precoloured(Src) or + hipe_rtl_arch:is_precoloured(Dst) of + true -> + {Acc++[Instr],Env}; %% To Avoid non ssa problems + false -> + {Acc++[Instr],insert(Dst,Src,Env)} + end; + OtherSrc -> + {Acc++[hipe_rtl:mk_move(Dst,OtherSrc)],Env} + end; + OtherDst -> + {Acc++[hipe_rtl:mk_move(Dst,OtherDst)],Env} + end; + _ -> + {Acc++[Instr],Env} + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% +%% Code handling functions +%% + +get_code_from_label(Label,CFG) -> + CurrentBB = hipe_rtl_cfg:bb(CFG, Label), + hipe_bb:code(CurrentBB). + +put_code_at_label(Label,Code,CFG) -> + NewBB = hipe_bb:mk_bb(Code), + hipe_rtl_cfg:bb_add(CFG, Label, NewBB). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% +%% The info environment. +%% An info environment is a mapping from labels to info_out +%% + +new_info() -> + gb_trees:empty(). + +get_info(Label,Info) -> + case gb_trees:lookup(Label, Info) of + {value, V} -> V; + none -> none + end. + +add_info(Label, NewInfo, OldInfo) -> + gb_trees:enter(Label, NewInfo, OldInfo). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% +%% Simple worklist utility +%% + +add_succ_to_list(NewList, OldList) -> + RealNew = [New || New <- NewList, lists:member(New,OldList)], + OldList ++ RealNew. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% +%% Generic Fixpoint Code +%% + +fix_point(CFG, Fun) -> + Start = hipe_rtl_cfg:start_label(CFG), + Info = new_info(), + fix_point([Start], CFG, Fun, Info). + +fix_point([Label|Labels], CFG, Fun, Info) -> + case initial_stage(Label,CFG,Fun,Info) of + {true, _, _} -> + fix_point(Labels, CFG, Fun, Info); + {false, _, NewInfoOut} -> + Succ = hipe_rtl_cfg:succ(CFG, Label), + NewList = add_succ_to_list(Succ, Labels), + NewInfo = add_info(Label, NewInfoOut, Info), + fix_point(NewList, CFG, Fun, NewInfo) + end; +fix_point([], _CFG, _Fun, Info) -> + Info. + +pass_through(CFG, Fun, Info) -> + pass_through(hipe_rtl_cfg:reverse_postorder(CFG), + CFG, Fun, Info). + +pass_through([Label|Labels], CFG, Fun, Info) -> + {_, NewCode, _} = initial_stage(Label,CFG,Fun,Info), + NewCFG = put_code_at_label(Label,NewCode,CFG), + pass_through(Labels, NewCFG, Fun, Info); +pass_through([], CFG, _Fun, _Info) -> + CFG. + +initial_stage(Label,CFG,Fun,Info) -> + OldInfoOut = get_info(Label,Info), + Pred = hipe_rtl_cfg:pred(CFG,Label), + InfoEnv = join([get_info(L,Info) || L <- Pred]), + OldCode = get_code_from_label(Label,CFG), + {PhiCode,Code} = split_code(OldCode), + InfoIn = join_phi(PhiCode,Info,InfoEnv), + {NewCode, NewInfoOut} = Fun(Code, InfoIn), + {OldInfoOut=:=NewInfoOut,PhiCode++NewCode, NewInfoOut}. + +join_phi([#phi{dst=Dst,arglist=AList}|Rest], Info, Env) -> + case lists:foldl(fun(Val,Acc) -> + check_label(Val,Info,Acc) + end, none, AList) of + no_val -> + join_phi(Rest,Info,Env); + none -> + join_phi(Rest,Info,Env); + Expr -> + join_phi(Rest,Info,insert(Dst,Expr,Env)) + end; +join_phi([], _Info, Env) -> + Env. + +check_label({Lbl,Var}, Info, Acc) -> + case gb_trees:lookup(Lbl,Info) of + none -> Acc; + {value,Env} -> + case lookup_x(Var,Env) of + none -> no_val; + Acc -> Acc; + V -> + if Acc =:= none -> V; + true -> no_val + end + end + end. + +split_code(Code) -> + Phis = extract_phis(Code), + {Phis,Code--Phis}. + +extract_phis(Code) -> + [I || #phi{}=I <- Code]. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% +%% One2One Environment +%% + +new_env() -> + {gb_trees:empty(),gb_trees:empty()}. + +insert(X,Y,{XtoY,YtoX}) -> + NewYtoX = remove_old_binding(X,XtoY,YtoX), + NewXtoY = remove_old_binding(Y,YtoX,XtoY), + {gb_trees:enter(X,Y,NewXtoY), + gb_trees:enter(Y,X,NewYtoX)}. + +remove_old_binding(Key,LookupTree,ChangeTree) -> + case gb_trees:lookup(Key,LookupTree) of + none -> + ChangeTree; + {value,V} -> + gb_trees:balance(gb_trees:delete(V,ChangeTree)) + end. + +lookup_x(X,{XtoY,_YtoX}) -> + case gb_trees:lookup(X,XtoY) of + none -> none; + {value,Val} -> Val + end. + +lookup_y(Y,{_XtoY,YtoX}) -> + case gb_trees:lookup(Y,YtoX) of + none -> none; + {value,Val} -> Val + end. + +join([]) -> new_env(); +join([none]) -> new_env(); +join([E]) -> E; +join([E1,E2|Rest]) -> join([join(E1,E2)|Rest]). + +join({MapXY1,MapYX1},{MapXY2,MapYX2}) -> + {join_maps(MapXY1,MapXY2), + join_maps(MapYX1,MapYX2)}; +join(none,E) -> E; +join(E,none) -> E. + +join_maps(Map1,Map2) -> + OrdDict = ordsets:intersection(gb_trees:to_list(Map1), + gb_trees:to_list(Map2)), + gb_trees:from_orddict(OrdDict). + +remove_defines(Instr,Info,Fun) -> + Defs = hipe_rtl:defines(Instr), + case [Def || Def <- Defs, hipe_rtl_arch:is_precoloured(Def)] of + [] -> + Info; + PreColDefs -> + filter_environments(PreColDefs,Info,Fun) + end. + +filter_environments(PreColDefs,{M1,_M2},Fun) -> + L1 = gb_trees:to_list(M1), + F1 = [Tup || Tup <- L1, Fun(Tup,PreColDefs)], + F2 = [{Y,X} || {X,Y} <- F1], + {gb_trees:from_orddict(F1),gb_trees:from_orddict(orddict:from_list(F2))}. diff --git a/lib/hipe/rtl/hipe_rtl_ssa_const_prop.erl b/lib/hipe/rtl/hipe_rtl_ssa_const_prop.erl new file mode 100644 index 0000000000..76c0a88933 --- /dev/null +++ b/lib/hipe/rtl/hipe_rtl_ssa_const_prop.erl @@ -0,0 +1,1082 @@ +%% -*- erlang-indent-level: 2 -*- +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2004-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% +%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% +%% ============================================================================ +%% Filename : hipe_rtl_ssa_const_prop.erl +%% Authors : Bjorn Bergman, Bjarni Juliusson +%% Purpose : Perform sparse conditional constant propagation on RTL. +%% Notes : Works on an SSA-converted control-flow graph. +%% +%% History : * 2004-03-14: Blatantly stolen from Icode (code by +%% Daniel Luna and Erik Andersson) and query-replaced for RTL. +%% * 2004-04-30: Added in the repository. +%% ============================================================================ +%% +%% Exports: propagate/1. +%% +%% ============================================================================ +%% +%% Some things to note: +%% +%% 1. All precoloured registers are assumed to contain bottom. We can not +%% do anything with them since they are not in SSA-form. This might be +%% possible to resolve in some way, but we decided to not go there. +%% +%% 2. const_labels are assumed to be bottom, we can not find the address +%% in any nice way (that I know of, maybe someone can help ?). I +%% suppose they don't get a value until linking (or some step that +%% resembles it). They are only affecting bignums and floats (at least +%% as far as I can tell), which are both stored in memory and hence +%% not handled very well by us anyway. +%% +%% 3. can v <- Constant be removed ? I think so. all uses of v will be +%% replaced with an immediate. So why not ? +%% +%% ============================================================================ +%% +%% TODO: +%% +%% Take care of failures in call and replace operation with apropriate +%% failure. +%% +%% Handle ifs with non-binary operators +%% +%% We want multisets for easier (and faster) creation of env->ssa_edges +%% +%% Propagation of constant arguments when some of the arguments are bottom +%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +-module(hipe_rtl_ssa_const_prop). +-export([propagate/1]). + +-include("../main/hipe.hrl"). +-include("hipe_rtl.hrl"). +-include("../flow/cfg.hrl"). + +%-define(DEBUG, true). + +-ifdef(DEBUG). +-define(SCCPDBG(W), W). +-define(DEBUG_TST, true). % make sure that we can use ?DEBUG in if-cases... +-else. +-define(DEBUG_TST, false). % make sure that we can use ?DEBUG in if-cases... +-define(SCCPDBG(W), ok). +-endif. + +%%----------------------------------------------------------------------------- +%% Include stuff shared between SCCP on Icode and RTL. +%% NOTE: Needs to appear after DEBUG is possibly defined. +%%----------------------------------------------------------------------------- + +-define(CODE, hipe_rtl). +-define(CFG, hipe_rtl_cfg). +-include("../ssa/hipe_ssa_const_prop.inc"). + +-type bool_lattice() :: 'true' | 'false' | 'top' | 'bottom'. +-type conditional() :: 'eq' | 'ne' | 'ge' | 'geu' | 'gt' | 'gtu' | 'le' + | 'leu' | 'lt' | 'ltu' | 'overflow' | 'not_overflow'. + +%%----------------------------------------------------------------------------- +%% Procedure : visit_expression/2 +%% Purpose : do a symbolic execution of the given instruction. This is just +%% a wrapper that chooses the right function to handle a particular +%% instruction. +%% Arguments : Instructions - the instruction +%% Environment - have a guess. +%% Returns : {FlowWorkList, SSAWorkList, Environment} +%%----------------------------------------------------------------------------- +visit_expression(Instruction, Environment) -> + case Instruction of + #alu{} -> + visit_alu(Instruction, Environment); + #alub{} -> + visit_alub(Instruction, Environment); + #branch{} -> + visit_branch(Instruction, Environment); + #call{} -> + visit_call(Instruction, Environment); +%% #comment{} -> +%% visit_comment(Instruction, Environment); +%% #enter{} -> +%% visit_enter(Instruction, Environment); + #fconv{} -> + visit_fconv(Instruction, Environment); + #fixnumop{} -> + visit_fixnumop(Instruction, Environment); + #fload{} -> + visit_fload(Instruction, Environment); + #fmove{} -> + visit_fmove(Instruction, Environment); + #fp{} -> + visit_fp(Instruction, Environment); + #fp_unop{} -> + visit_fp_unop(Instruction, Environment); +%% #fstore{} -> +%% visit_fstore(Instruction, Environment); +%% #gctest{} -> +%% visit_gctest(Instruction, Environment); + #goto{} -> + visit_goto(Instruction, Environment); + #goto_index{} -> + visit_goto_index(Instruction, Environment); +%% #label{} -> +%% visit_label(Instruction, Environment); + #load{} -> + visit_load(Instruction, Environment); + #load_address{} -> + visit_load_address(Instruction, Environment); + #load_atom{} -> + visit_load_atom(Instruction, Environment); + #load_word_index{} -> + visit_load_word_index(Instruction, Environment); + #move{} -> + visit_move(Instruction, Environment); + #multimove{} -> + visit_multimove(Instruction, Environment); +%% phi-nodes are handled in scc +%% #phi{} -> +%% visit_phi(Instruction, Environment); +%% #return{} -> +%% visit_return(Instruction, Environment); +%% #store{} -> +%% visit_store(Instruction, Environment); + #switch{} -> + visit_switch(Instruction, Environment); + _ -> + %% label, end_try, comment, return, fail, et al + {[], [], Environment} + end. + + +%%----------------------------------------------------------------------------- +%% Procedure : set_to/3 +%% Purpose : many of the visit_<inst> functions ends in a update of the +%% environment (and resulting SSA-edges) this function does the +%% update in a nice way and formats the result so that it can be +%% imediatly returned to visit_expression +%% Arguments : Dst - the destination may be a list of destinations. +%% Val - the new value (bottom, or some constant). +%% Env - the environment in which the update should be done. +%% Returns : { FlowWorkList, SSAWorkList, NewEnvironment} +%%----------------------------------------------------------------------------- + +set_to(Dst, Val, Env) -> + {Env1, SSAWork} = update_lattice_value({Dst, Val}, Env), + {[], SSAWork, Env1}. + +%%----------------------------------------------------------------------------- +%% Procedure : visit_branch/2 +%% Purpose : do symbolic exection of branch instructions. +%% Arguments : Inst - The instruction +%% Env - The environment +%% Returns : { FlowWorkList, SSAWorkList, NewEnvironment} +%%----------------------------------------------------------------------------- + +visit_branch(Inst, Env) -> %% Titta ocks� p� exekverbarflagga + Val1 = lookup_lattice_value(hipe_rtl:branch_src1(Inst), Env), + Val2 = lookup_lattice_value(hipe_rtl:branch_src2(Inst), Env), + CFGWL = case evaluate_relop(Val1, hipe_rtl:branch_cond(Inst), Val2) of + true -> [hipe_rtl:branch_true_label(Inst)]; + false -> [hipe_rtl:branch_false_label(Inst)]; + bottom -> [hipe_rtl:branch_true_label(Inst), + hipe_rtl:branch_false_label(Inst)]; + top -> [] + end, + {CFGWL, [], Env}. + +%%----------------------------------------------------------------------------- +%% Procedure : evaluate_relop/3 +%% Purpose : evaluate the given relop. While taking care to handle top & +%% bottom in some sane way. +%% Arguments : Val1, Val2 - The operands Integers or top or bottom +%% RelOp - some relop atom from rtl. +%% Returns : bottom, top, true or false +%%----------------------------------------------------------------------------- + +evaluate_relop(Val1, RelOp, Val2) -> + if + (Val1==bottom) or (Val2==bottom) -> bottom ; + (Val1==top) or (Val2==top) -> top; + true -> hipe_rtl_arch:eval_cond(RelOp, Val1, Val2) + end. + +%%----------------------------------------------------------------------------- +%% Procedure : evaluate_fixnumop/2 +%% Purpose : try to evaluate a fixnumop. +%% Arguments : Val1 - operand (an integer, 'top' or 'bottom') +%% Op - the operation. +%% Returns : Result +%% where result is an integer, 'top' or 'bottom' +%%----------------------------------------------------------------------------- + +evaluate_fixnumop(Val1, Op) -> + if Val1 =:= top -> + top; + Val1 =:= bottom -> + bottom; + is_integer(Val1) -> + case Op of + tag -> + hipe_tagscheme:mk_fixnum(Val1); + untag -> + hipe_tagscheme:fixnum_val(Val1) + end + end. + +%%----------------------------------------------------------------------------- +%% Procedure : visit_alu/2 +%% Purpose : do symbolic exection of a alu +%% Arguments : Inst - The instruction +%% Env - The environment +%% Returns : { FlowWorkList, SSAWorkList, NewEnvironment} +%%----------------------------------------------------------------------------- + +visit_alu(Inst, Env) -> + Val1 = lookup_lattice_value(hipe_rtl:alu_src1(Inst), Env), + Val2 = lookup_lattice_value(hipe_rtl:alu_src2(Inst), Env), + {NewVal, _, _, _, _} = evaluate_alu(Val1, hipe_rtl:alu_op(Inst), Val2), + set_to(hipe_rtl:alu_dst(Inst), NewVal, Env). + +%% Here follows the alu-evaluation stuff. This is the most involved part I +%% guess. The function that you may want to use is evaluate_alu/3. The +%% evaluation functions returns +%% { Result, SignFlag, ZeroFlag, Overflow flag, CarryBit} +%% it uses some helpers which are explained breifly: +%% lattice_meet/2 - handles the general case of most alu-operations, called +%% when at least one of the operands is nonconstant, and the +%% operation-specifics have been taken care of. +%% all_ones/0 - returns the value of a rtl-word set to all 1 bits. +%% partial_eval_alu - tries to catch some operation specific special cases +%% when one (or both) of the operands is nonconstant. + +lattice_meet(Val1, Val2) -> + M = if (Val1 =:= top) or (Val2 =:= top) -> top; + (Val1 =:= bottom) or (Val2 =:= bottom) -> bottom + % the check is realy just sanity + end, + {M, M, M, M, M}. + +all_ones() -> + (1 bsl ?bytes_to_bits(hipe_rtl_arch:word_size())) - 1. + +%% when calling partial_eval*() we know that at least one of the Values +%% are bottom or top. They return { Value, Sign, Zero, Overflow, Carry }. +%% (just like hipe_rtl_arch:eval_alu) + +%% logic shifts are very similar each other. Limit is the number of +%% bits in the words. +partial_eval_shift(Limit, Val1, Val2) -> + if + Val2 =:= 0 -> {Val1, Val1, Val1, Val1, Val1}; + Val1 =:= 0 -> {0, false, true, false, false}; + is_integer(Val2), Val2 >= Limit -> % (Val2 =/= top) and (Val2 =/= bottom) + {0, false, true, Val1, Val1}; % OVerflow & carry we dont know about. + true -> lattice_meet(Val1, Val2) + end. + +%%----------------------------------------------------------------------------- +%% Procedure : partial_eval_alu/3 +%% Purpose : try to evaluate as much as possible an alu operation where at +%% least one of the operands is not constant. +%% Arguments : Val1, Val2 - operands (integer, top or bottom) +%% Op - the operation. +%% Returns : {Result, Sign, Zero, Overflow, Carry} +%% where Result is an integer, 'top' or 'bottom' +%% and the others are bool, 'top' or 'bottom'. +%%----------------------------------------------------------------------------- + +partial_eval_alu(Val1, add, Val2) -> + if + (Val1 == 0) -> {Val2, Val2, Val2, false, false}; + (Val2 == 0) -> {Val1, Val1, Val1, false, false}; + true -> lattice_meet(Val1, Val2) + end; +partial_eval_alu(Val1, sub, Val2) -> + if + (Val2 == 0) -> {Val1, Val1, Val1, false, false}; + true -> lattice_meet(Val1, Val2) + end; +partial_eval_alu(Val1, 'or', Val2) -> + All_ones = all_ones(), + if + (Val1 == 0) -> {Val2, Val2, Val2, false, false}; + (Val2 == 0) -> {Val1, Val1, Val1, false, false}; + (Val1 == All_ones) or (Val2 == All_ones) -> + {All_ones, true, false, false, false}; + true -> lattice_meet(Val1, Val2) + end; +partial_eval_alu(Val1, 'and', Val2) -> + All_ones = all_ones(), + if + Val1 == All_ones -> {Val2, Val2, Val2, false, false}; + Val2 == All_ones -> {Val1, Val1, Val1, false, false}; + (Val1 == 0) or (Val2 == 0) -> {0, false, true, false, false}; + true -> lattice_meet(Val1, Val2) + end; +partial_eval_alu(Val1, 'xor', Val2) -> + if + (Val1 == 0) -> {Val2, Val2, Val2, false, false}; + (Val2 == 0) -> {Val1, Val1, Val1, false, false}; + true -> lattice_meet(Val1, Val2) + end; +partial_eval_alu(Val1, 'xornot', Val2) -> + All_ones = all_ones(), + if + Val1 == All_ones -> {Val2, Val2, Val2, false, false}; + Val2 == All_ones -> {Val1, Val1, Val1, false, false}; + true -> lattice_meet(Val1, Val2) + end; +partial_eval_alu(Val1, andnot, Val2) -> + All_ones = all_ones(), + if + (Val2 == 0) -> {Val1, Val1, Val1, false, false}; + (Val1 == 0) or (Val2 == All_ones) -> {0, false, true, false, false}; + true -> lattice_meet(Val1, Val2) + end; +partial_eval_alu(Val1, Op, Val2) when (Op =:= 'sll') or (Op =:= 'srl') -> + BitSize = ?bytes_to_bits(hipe_rtl_arch:word_size()), + partial_eval_shift(BitSize, Val1, Val2); +partial_eval_alu(Val1, Op, Val2) when (Op =:= 'sllx') or (Op =:= 'srlx') -> + partial_eval_shift(64, Val1, Val2); +partial_eval_alu(Val1, mul, Val2) -> lattice_meet(Val1, Val2); % XXX: suboptimal + +% arithmetic shifts are more tricky, shifting something unknown can +% generate all_ones() and 0 depenging on the sign of Val1. +partial_eval_alu(Val1, Op, Val2) when (Op =:= 'sra') or (Op =:= 'srax') -> + if + (Val2 == 0) -> {Val1, Val1, Val1, false, false}; + (Val1 == 0) -> {0, false, true, false, false}; + true -> lattice_meet(Val1, Val2) + end. + +%%----------------------------------------------------------------------------- +%% Procedure : evaluate_alu/3 +%% Purpose : try to evaluate as much as possible of a alu operation. +%% Arguments : Val1, Val2 - operands (an integer, 'top' or 'bottom') +%% Op - the operation. +%% Returns : {Result, Sign, Zero, Overflow, Carry} +%% where result is an integer, 'top' or 'bottom' +%% and the others are Bool, 'top' or 'bottom'. +%%----------------------------------------------------------------------------- + +evaluate_alu(Val1, Op, Val2) -> + if + (Val1 =:= top) or (Val2 =:= top) or + (Val1 =:= bottom) or (Val2 =:= bottom) -> partial_eval_alu(Val1, Op, Val2); + true -> + case Op of + sllx -> hipe_rtl_arith_64:eval_alu('sll', Val1, Val2); + srlx -> hipe_rtl_arith_64:eval_alu('srl', Val1, Val2); + srax -> hipe_rtl_arith_64:eval_alu('sra', Val1, Val2); + _ -> hipe_rtl_arch:eval_alu(Op, Val1, Val2) + end + end. + +maybe_top_or_bottom(List) -> + maybe_top_or_bottom(List, false). + +maybe_top_or_bottom([], TB) -> TB; +maybe_top_or_bottom([top | Rest], _) -> maybe_top_or_bottom(Rest, top); +maybe_top_or_bottom([bottom | _], _) -> bottom; +maybe_top_or_bottom([_ | Rest], TB) -> maybe_top_or_bottom(Rest, TB). + +-spec partial_eval_branch(conditional(), bool_lattice(), bool_lattice(), + bool_lattice() | 0, bool_lattice() | 0) -> + bool_lattice(). +partial_eval_branch(Cond, N0, Z0, V0, C0) -> + {N, Z, V, C} = + if Cond =:= 'eq'; + Cond =:= 'ne' -> {true, Z0, true, true}; + Cond =:= 'gt'; + Cond =:= 'le' -> {N0, Z0, V0, true}; + Cond =:= 'gtu' -> {true, Z0, true, C0 }; + Cond =:= 'lt'; + Cond =:= 'ge' -> {N0, true, V0, true}; + Cond =:= 'geu'; + Cond =:= 'ltu' -> {true, true, true, C0 }; + Cond =:= 'overflow'; + Cond =:= 'not_overflow' -> {true, true, V0, true} + end, + case maybe_top_or_bottom([N, Z, V, C]) of + false -> hipe_rtl_arch:eval_cond_bits(Cond, N, Z, V, C); + top -> top; + bottom -> bottom + end. + +%%----------------------------------------------------------------------------- +%% Procedure : visit_alub/2 +%% Purpose : do symbolic exection of a alub instruction +%% Arguments : Inst - The instruction +%% Env - The environment +%% Returns : { FlowWorkList, SSAWorkList, NewEnvironment} +%%----------------------------------------------------------------------------- + +visit_alub(Inst, Env) -> + Val1 = lookup_lattice_value(hipe_rtl:alub_src1(Inst), Env), + Val2 = lookup_lattice_value(hipe_rtl:alub_src2(Inst), Env), + {NewVal, N, Z, C, V} = evaluate_alu(Val1, hipe_rtl:alub_op(Inst), Val2), + Labels = + case NewVal of + bottom -> [hipe_rtl:alub_true_label(Inst), + hipe_rtl:alub_false_label(Inst)]; + top -> []; + _ -> + %if the partial branch cannot be evaluated we must execute the + % instruction at runtime. + case partial_eval_branch(hipe_rtl:alub_cond(Inst), N, Z, C, V) of + bottom -> [hipe_rtl:alub_true_label(Inst), + hipe_rtl:alub_false_label(Inst)]; + top -> []; + true -> [hipe_rtl:alub_true_label(Inst) ]; + false -> [hipe_rtl:alub_false_label(Inst) ] + end + end, + {[], NewSSA, NewEnv} = set_to(hipe_rtl:alub_dst(Inst), NewVal, Env), + {Labels, NewSSA, NewEnv}. + +%%----------------------------------------------------------------------------- +%% Procedure : visit_fixnumop/2 +%% Purpose : do symbolic exection of a fixnumop instruction. +%% fixnumop is like a specialized alu. +%% Arguments : Inst - The instruction +%% Env - The environment +%% Returns : { FlowWorkList, SSAWorkList, NewEnvironment} +%%----------------------------------------------------------------------------- + +visit_fixnumop(Inst, Env) -> + Val = lookup_lattice_value(hipe_rtl:fixnumop_src(Inst), Env), + Res = evaluate_fixnumop(Val, hipe_rtl:fixnumop_type(Inst)), + set_to(hipe_rtl:fixnumop_dst(Inst), Res, Env). + +%%----------------------------------------------------------------------------- +%% Procedure : visit_f* +%% Purpose : Do symbolic execution of floating point instructions. +%% All floating-point hitngs are mapped to bottom. In order to +%% implement them we would have to add hipe_rtl_arch:eval_f* +%% instructions since floating point is no exact science. +%% Arguments : Inst - The instruction +%% Env - The environment +%% Returns : {FlowWorkList, SSAWorkList, NewEnvironment} +%%----------------------------------------------------------------------------- + +visit_fconv(Inst, Env) -> + set_to(hipe_rtl:fconv_dst(Inst), bottom, Env). + +visit_fp(Inst, Env) -> + set_to(hipe_rtl:fp_dst(Inst), bottom, Env). + +visit_fp_unop(Inst, Env) -> + set_to(hipe_rtl:fp_unop_dst(Inst), bottom, Env). + +visit_fload(Inst, Env) -> + set_to(hipe_rtl:fload_dst(Inst), bottom, Env). + +visit_fmove(Inst, Env) -> + set_to(hipe_rtl:fmove_dst(Inst), bottom, Env). + +%%----------------------------------------------------------------------------- +%% Procedure : visit_move/2 +%% Purpose : execute a register-copy +%% Arguments : Inst - The instruction +%% Env - The environment +%% Returns : {FlowWorkList, SSAWorkList, NewEnvironment} +%%----------------------------------------------------------------------------- + +visit_move(Inst, Env) -> + Src = hipe_rtl:move_src(Inst), + Dst = hipe_rtl:move_dst(Inst), + set_to(Dst, lookup_lattice_value(Src, Env), Env). + +%%----------------------------------------------------------------------------- +%% Procedure : visit_goto/2 +%% Purpose : execute a goto +%% Arguments : Inst - The instruction +%% Env - The environment +%% Returns : {FlowWorkList, SSAWorkList, NewEnvironment} +%%----------------------------------------------------------------------------- + +visit_goto(Instruction, Environment) -> + GotoLabel = hipe_rtl:goto_label(Instruction), + {[GotoLabel], [], Environment}. + +%%----------------------------------------------------------------------------- +%% Procedure : visit_goto_index/2 +%% Purpose : execute a goto_index +%% Arguments : Inst - The instruction +%% Env - The environment +%% Returns : {FlowWorkList, SSAWorkList, NewEnvironment} +%%----------------------------------------------------------------------------- + +visit_goto_index(Inst, Env) -> + Index = hipe_rtl:goto_index_index(Inst), + case lookup_lattice_value(Index, Env) of + top -> { [], [], Env }; + bottom -> %% everything is reachable + { hipe_rtl:goto_index_labels(Inst), [], Env }; + I -> %% only the ith label will be taken. + io:format("hipe_rtl_ssa_const_prop foud goto-index with constant index ~w in ~w\n", + [I, Inst]), + { [ lists:nth(hipe_rtl:goto_index_labels(Inst), I) ], [], Env } + end. + +%%----------------------------------------------------------------------------- +%% Procedure : visit_load/2 +%% Purpose : do a visit_load. Its hard to track whats in memory, and it's +%% not in ssa form, so let's assume bottom-values ! +%% Arguments : Inst - The instruction +%% Env - The environment +%% Returns : {FlowWorkList, SSAWorkList, NewEnvironment} +%%----------------------------------------------------------------------------- + +visit_load(Inst, Env) -> + set_to(hipe_rtl:load_dst(Inst), bottom, Env). + +%%----------------------------------------------------------------------------- +%% Procedure : visit_load_address/2 +%% Purpose : execute a load_address instruction, while there might be things +%% here that are runtime-constant they are not compile-time +%% constant since code loading interferes with addresses. +%% Arguments : Inst - The instruction +%% Env - The environment +%% Returns : {FlowWorkList, SSAWorkList, NewEnvironment} +%%----------------------------------------------------------------------------- + +visit_load_address(Inst, Env) -> + Dst = hipe_rtl:load_address_dst(Inst), + Val = bottom, %% all these are probably run-time, but not + %% compile-time constants + set_to(Dst, Val, Env). + +%%----------------------------------------------------------------------------- +%% Procedure : visit_load_atom/2 +%% Purpose : Like loadadress this one gets something that is not +%% compiletime-constant +%% Arguments : Inst - The instruction +%% Env - The environment +%% Returns : {FlowWorkList, SSAWorkList, NewEnvironment} +%%----------------------------------------------------------------------------- + +visit_load_atom(Inst, Env) -> + set_to(hipe_rtl:load_atom_dst(Inst), bottom, Env). + +%%----------------------------------------------------------------------------- +%% Procedure : visit_load_word_index/2 +%% Purpose : execute a load_word_index. Here is probably room for +%% improvement, we should be able to find some constants here, +%% since we can get the labeled values from the environment, and +%% then find the value with the given index. +%% Arguments : Inst - The instruction +%% Env - The environment +%% Returns : {FlowWorkList, SSAWorkList, NewEnvironment} +%%----------------------------------------------------------------------------- + +visit_load_word_index(Inst, Env) -> + io:format(" this is load word index: ~w\n", [Inst]), + set_to(hipe_rtl:load_word_index_dst(Inst), bottom, Env). + +%%----------------------------------------------------------------------------- +%% Procedure : visit_multimove/2 & visit_multimove/4 +%% Purpose : execute a multimove instruction. +%% Arguments : Inst - The instruction +%% Env - The environment +%% Returns : {FlowWorkList, SSAWorkList, NewEnvironment} +%%----------------------------------------------------------------------------- + +visit_multimove([Dst | Dsts], [Val | Vals], MyEnv, MySSA) -> + {NewEnv, NewSSA} = update_lattice_value({Dst, Val}, MyEnv), + visit_multimove(Dsts, Vals, NewEnv, MySSA ++ NewSSA); +visit_multimove([], [], MyEnv, MySSA) -> + {MyEnv, MySSA}. + +visit_multimove(Inst, Env) -> + Srcs = [lookup_lattice_value(S, Env) || + S <- hipe_rtl:multimove_srclist(Inst)], + {NewEnv, NewSSA} = visit_multimove(hipe_rtl:multimove_dstlist(Inst), + Srcs, Env, []), + {[], NewSSA, NewEnv}. + +%%----------------------------------------------------------------------------- +%% Procedure : visit_call/2 +%% Purpose : execute a call-instruction. All calls return bottom. We make +%% this assumption since the icode-leel have taken care of BIF's +%% and we belive that we are left with the things that can not be +%% done att compile time. +%% Arguments : Inst - The instruction +%% Env - The environment +%% Returns : {FlowWorkList, SSAWorkList, NewEnvironment} +%%----------------------------------------------------------------------------- + +visit_call(Inst, Env) -> + {Env1, SSAWork} = + update_lattice_value({hipe_rtl:call_dstlist(Inst), bottom}, Env), + % remeber to add both continuation & failto things to the cfgwl + Cont = case hipe_rtl:call_continuation(Inst) of + [] -> []; + C -> [C] + end, + Succ = case hipe_rtl:call_fail(Inst) of + [] -> Cont; + Fail -> [Fail | Cont] + end, + {Succ, SSAWork, Env1}. + +%%----------------------------------------------------------------------------- +%% Procedure : visit_switch/2 +%% Purpose : execute a switch-statement. +%% Arguments : Inst - The instruction +%% Env - The environment +%% Returns : {FlowWorkList, SSAWorkList, NewEnvironment} +%%----------------------------------------------------------------------------- + +%% first two helpers that are used to handle the mapping from value to label. +%% why isn't there a function that does this ? + +find_switch_label(Inst, Val) -> + Labels = hipe_rtl:switch_labels(Inst), + ?SCCPDBG(io:format("finding switch_label, ~w in ~w\n", [Val,Inst])), + %% it seems like the index is zero based. nth uses 1-based indexing. + lists:nth(Val + 1, Labels). + +%% Switches seem tricky. the sort-order is a list of key-values to be +%% tested in order. (if elem i matches then we should jump to elem i of +%% the labels-list) +visit_switch(Inst, Env) -> + case lookup_lattice_value(hipe_rtl:switch_src(Inst), Env) of + top -> + {[], [], Env}; + bottom -> + {hipe_rtl:switch_labels(Inst), [], Env}; + Val -> + {[find_switch_label(Inst, Val) ], [], Env} + end. + +%%----------------------------------------------------------------------------- +%% Procedure : update_instruction/2 +%% Purpose : update the given instruction using any information found in +%% the environment. +%% Arguments : Inst - the instruction +%% Environment - in which everything happens. +%% Returns : list of new instructions. +%%----------------------------------------------------------------------------- + +%% idea: what to do with vi <- Constant. wouldn't it be possible to +%% remove those ? (and similarily for alu-instructions. and alub +%% instructions also ! (of course this will be done in some later step dead +%% code elimination ? but it's a simple check.) +update_instruction(Inst, Env) -> + case Inst of + #alu{} -> + update_alu(Inst, Env); + #alub{} -> + update_alub(Inst, Env); + #branch{} -> + update_branch(Inst, Env); + #call{} -> + subst_all_uses(Inst, Env); +%% #comment{} -> +%% [Inst]; + #enter{} -> + subst_all_uses(Inst, Env); + #fconv{} -> + subst_all_uses(Inst, Env); + #fload{} -> + subst_all_uses(Inst, Env); + #fmove{} -> + subst_all_uses(Inst, Env); + #fp{} -> + subst_all_uses(Inst, Env); + #fp_unop{} -> + subst_all_uses(Inst, Env); + #fstore{} -> + subst_all_uses(Inst, Env); + #gctest{} -> + subst_all_uses(Inst, Env); +%% #goto{} -> +%% [ Inst ]; + #goto_index{} -> + update_goto_index(Inst, Env); +%% #label{} -> +%% [ Inst ]; + #load{} -> + subst_all_uses(Inst, Env); + #load_address{} -> + subst_all_uses(Inst, Env); + #load_atom{} -> + subst_all_uses(Inst, Env); + #load_word_index{} -> + subst_all_uses(Inst, Env); + #move{} -> + subst_all_uses(Inst, Env); + #multimove{} -> + subst_all_uses(Inst, Env); + #return{} -> + subst_all_uses(Inst, Env); + #store{} -> + subst_all_uses(Inst, Env); + #switch{} -> + update_switch(Inst, Env); + #phi{} -> + update_phi(Inst, Env); + _ -> % for the others it's sufficient to just update any thing they use. + [ Inst ] + end. + +%%----------------------------------------------------------------------------- +%% Procedure : subst_uses/2 +%% Purpose : looks up all things that an instruction uses and replaces +%% anything that is determined to be constant. +%% Arguments : Inst - the instruction +%% Env - in which everything happen. +%% Returns : list of instructions to replace Inst with. +%%----------------------------------------------------------------------------- + +subst_all_uses(Inst, Env) -> + Uses = hipe_rtl_ssa:uses_to_rename(Inst), + [ hipe_rtl:subst_uses(update_srcs(Uses, Env), Inst) ]. + +%%----------------------------------------------------------------------------- +%% Procedure : update_srcs/2 +%% Purpose : given the things that a instruction use return a list +%% {Src, NewValue} pairs that can be sent to subs_uses. +%% Arguments : Srcs - list of uses +%% Env - in which everything happens. +%% Returns : list of {Src, NewValue} pairs. +%%----------------------------------------------------------------------------- + +update_srcs(Srcs, Env) -> + Update = + fun(Src, Os) -> + case lookup_lattice_value(Src, Env) of + bottom -> Os; + top -> % this would be realy strange. + ?EXIT({"update_src, top", Src }); + Constant -> + [ {Src, hipe_rtl:mk_imm(Constant)} | Os] + end + end, + lists:foldl(Update, [], Srcs ). + +%%----------------------------------------------------------------------------- +%% functions for performing partial evaluation of alu-operations. They can +%% return either an integer (the actual result), move_src1 or move_src2 in +%% which case the alu-operation can be replace with a move, or keep_it in +%% which case the instruction must be kept. + +%%----------------------------------------------------------------------------- +%% Procedure : partial_update_shift/3 +%% Purpose : perform a shift +%% Arguments : Limit - the number of bits in the word to shift. +%% Val1 - the shiftee +%% Val2 - number of bits to shift +%% Returns : Integer, move_src1, keep_it +%%----------------------------------------------------------------------------- + +partial_update_shift(Limit, Val1, Val2) -> + if + (Val1 =:= bottom) and (Val2 =:= 0) -> move_src1; + (Val1 =:= 0) or ((Val2 =/= bottom) and (Val2 >= Limit)) -> 0; + true -> keep_it + end. + +%%----------------------------------------------------------------------------- +%% Procedure : partial_update_alu/3 +%% Purpose : perform as much of alu-operations where exatcly one of the +%% operands is bottom. +%% Arguments : Val1, Val2 - operands +%% Op - the operation. +%% Returns : Integer, move_src1, move_src2, keep_it +%%----------------------------------------------------------------------------- + +%% we know that exactly one of the operands are bottom this one +%% returns what to do with the instruction (it's either replace with +%% src1, replace src2 replace with constant or keep it. + +partial_update_alu(Val1, 'add', Val2) -> + if + (Val1 == 0) -> move_src2; + (Val2 == 0) -> move_src1; + true -> keep_it + end; +partial_update_alu(_Val1, 'sub', Val2) -> + if + (Val2 == 0) -> move_src1; + true -> keep_it + end; +partial_update_alu(Val1, 'or', Val2) -> + All_ones = all_ones(), + if + (Val1 == 0) -> move_src2; + (Val2 == 0) -> move_src1; + (Val1 == All_ones) or (Val2 == All_ones) -> All_ones; + true -> keep_it + end; +partial_update_alu(Val1, 'and', Val2) -> + All_ones = all_ones(), + if + Val1 == All_ones -> move_src2; + Val2 == All_ones -> move_src1; + (Val1 == 0) or (Val2 == 0) -> 0; + true -> keep_it + end; +partial_update_alu(Val1, 'xor', Val2) -> + if + (Val1 == 0) -> move_src2; + (Val2 == 0) -> move_src1; + true -> keep_it + end; +partial_update_alu(Val1, 'xornot', Val2) -> + All_ones = all_ones(), + if + (Val1 == All_ones) -> move_src2; + (Val2 == All_ones) -> move_src1; + true -> keep_it + end; +partial_update_alu(Val1, andnot, Val2) -> + All_ones = all_ones(), + if + Val2 == 0 -> move_src1; + (Val1 == 0) or (Val2 == All_ones) -> 0; + true -> keep_it + end; +partial_update_alu(Val1, Op, Val2) when (Op =:= 'sll') or (Op =:= 'srl') -> + BitSize = ?bytes_to_bits(hipe_rtl_arch:word_size()), + partial_update_shift(BitSize, Val1, Val2); +partial_update_alu(Val1, Op, Val2) when (Op =:= 'sllx') or (Op =:= 'srlx') -> + partial_update_shift(64, Val1, Val2); +partial_update_alu(Val1, Op, Val2) when (Op =:= 'sra') or (Op =:= 'srax') -> + if + Val2 == 0 -> move_src1; + Val1 == 0 -> 0; + true -> keep_it + end. + +%%----------------------------------------------------------------------------- +%% Procedure : update_alu/2 +%% Purpose : update an alu-instruction. +%% Arguments : Inst - the instruction. +%% Env - in which everything happens. +%% Returns : list of new instruction +%%----------------------------------------------------------------------------- + +update_alu(Inst, Env) -> + Val1 = lookup_lattice_value(hipe_rtl:alu_src1(Inst), Env), + Val2 = lookup_lattice_value(hipe_rtl:alu_src2(Inst), Env), + if + (Val1 =:= bottom) and (Val2 =:= bottom) -> + [Inst]; + (Val1 =:= bottom) or (Val2 =:= bottom) -> + NewInst = + case partial_update_alu(Val1, hipe_rtl:alu_op(Inst), Val2) of + move_src1 -> + hipe_rtl:mk_move(hipe_rtl:alu_dst(Inst), hipe_rtl:alu_src1(Inst)); + move_src2 -> + hipe_rtl:mk_move(hipe_rtl:alu_dst(Inst), hipe_rtl:alu_src2(Inst)); + keep_it -> + S1 = make_alub_subst_list(Val1, hipe_rtl:alu_src1(Inst), []), + S2 = make_alub_subst_list(Val2, hipe_rtl:alu_src2(Inst), S1), + hipe_rtl:subst_uses(S2, Inst); + Constant -> + hipe_rtl:mk_move(hipe_rtl:alu_dst(Inst), hipe_rtl:mk_imm(Constant)) + end, + [NewInst]; + true -> + {Val,_,_,_,_} = evaluate_alu(Val1, hipe_rtl:alu_op(Inst), Val2), + [hipe_rtl:mk_move(hipe_rtl:alu_dst(Inst), hipe_rtl:mk_imm(Val))] + end. + +%%----------------------------------------------------------------------------- +%% Procedure : update_branch/2 +%% Purpose : update an branch-instruction +%% Arguments : Inst - the instruction. +%% Env - in which everything happens. +%% Returns : list of new instruction +%%----------------------------------------------------------------------------- + +update_branch(Inst, Env) -> + Src1 = hipe_rtl:branch_src1(Inst), + Src2 = hipe_rtl:branch_src2(Inst), + Val1 = lookup_lattice_value(Src1, Env), + Val2 = lookup_lattice_value(Src2, Env), + if + (Val1 =:= bottom) and (Val2 =:= bottom) -> + [Inst]; + Val1 =:= bottom -> + [hipe_rtl:subst_uses([{Src2, hipe_rtl:mk_imm(Val2)}], Inst)]; + Val2 =:= bottom -> + [hipe_rtl:subst_uses([{Src1, hipe_rtl:mk_imm(Val1)}], Inst)]; + true -> + case hipe_rtl_arch:eval_cond(hipe_rtl:branch_cond(Inst), Val1, Val2) of + true -> [hipe_rtl:mk_goto(hipe_rtl:branch_true_label(Inst))]; + false -> [hipe_rtl:mk_goto(hipe_rtl:branch_false_label(Inst))] + end + end. + +%%----------------------------------------------------------------------------- +%% Procedure : update_alub/2 +%% Purpose : update an alub-instruction. Here are some finer points, we might +%% be able to do the math (think b = a+0), but it's hard to replace +%% the branch, since the mapping b/w AluOp,RelOp to BranchInstr is +%% boring to do. (lazyness is a bliss). +%% Arguments : Inst - the instruction. +%% Env - in which everything happens. +%% Returns : list of new instructions +%%----------------------------------------------------------------------------- + +%% some small helpers. +alub_to_move(Inst, Res, Lab) -> + [ hipe_rtl:mk_move(hipe_rtl:alub_dst(Inst), Res), + hipe_rtl:mk_goto(Lab) ]. + +make_alub_subst_list(bottom, _, Tail) -> Tail; +make_alub_subst_list(top, Src, _) -> + ?EXIT({"~w is top during update",Src }); +make_alub_subst_list(Val, Src, Tail) -> + case hipe_rtl:is_imm(Src) of + true -> Tail; + false -> [{Src, hipe_rtl:mk_imm(Val)} | Tail] + end. + +update_alub(Inst, Env) -> + Src1 = hipe_rtl:alub_src1(Inst), + Src2 = hipe_rtl:alub_src2(Inst), + Val1 = lookup_lattice_value(Src1, Env), + Val2 = lookup_lattice_value(Src2, Env), + {ResVal, N, Z, C, V} = evaluate_alu(Val1, hipe_rtl:alub_op(Inst), Val2), + CondRes = partial_eval_branch(hipe_rtl:alub_cond(Inst), N, Z, C, V), + case CondRes of + bottom -> + %% if we can't evaluate the branch, we have to keep it as a alub isnt + %% since other optimizations might insert other instructions b/w the + %% move and the branch. We can however replace variable with constants: + S1 = make_alub_subst_list(Val1, Src1, []), + S2 = make_alub_subst_list(Val2, Src2, S1), + [ hipe_rtl:subst_uses(S2, Inst) ]; + _ -> % we know where we will be going, let's find out what Dst should be. + % knowing where we are going means that at most one of the values is + % bottom, hence we can replace the alu-instr with a move. + % remember, a = b + 0 can give us enough info to know what jump to + % do without knowing the value of a. (I wonder if this will ever + % actualy happen ;) + Res = case ResVal of + bottom -> % something nonconstant. + if (Val1 =:= bottom) -> Src1; + (Val2 =:= bottom) -> Src2 + end; + _ -> hipe_rtl:mk_imm(ResVal) + end, + case CondRes of + top -> io:format("oops. something VERY bad: ~w ~w V1 & 2 ~w ~w\n", + [Inst, {ResVal, N, Z, C, V} , Val1, Val2]), + [Inst ]; + true -> alub_to_move(Inst, Res, hipe_rtl:alub_true_label(Inst)); + false -> alub_to_move(Inst, Res, hipe_rtl:alub_false_label(Inst)) + end + end. + +%%----------------------------------------------------------------------------- +%% Procedure : update_goto_index/2 +%% Purpose : update a goto_index instruction. +%% Arguments : Inst - the instruction. +%% Env - in which everything happens. +%% Returns : list of new instructions. +%%----------------------------------------------------------------------------- + +update_goto_index(Inst, Env) -> + Index = hipe_rtl:goto_index_index(Inst), + case lookup_lattice_value(Index, Env) of + bottom -> %% everything is reachable + [Inst]; + I -> %% only the ith label will be taken. + [hipe_rtl:mk_goto(lists:nth(hipe_rtl:goto_index_labels(Inst), I))] + end. + +%%----------------------------------------------------------------------------- +%% Procedure : update_switch/2 +%% Purpose : update a switch instruction. +%% Arguments : Inst - the instruction. +%% Env - in which everything happens. +%% Returns : list of new instructions. +%%----------------------------------------------------------------------------- + +update_switch(Inst, Env) -> + case lookup_lattice_value(hipe_rtl:switch_src(Inst), Env) of + bottom -> + [Inst]; + Const -> + Lab = find_switch_label(Inst, Const), + [hipe_rtl:mk_goto(Lab)] + end. + +%%----------------------------------------------------------------------------- +%% Procedure : update_phi/3 +%% Purpose : Update a phi-function w.r.t. constants. do nothing for now. +%% Arguments : Instruction - The instruction +%% Environment - The environment +%% Returns : [NewInstruction] +%%----------------------------------------------------------------------------- + +update_phi(Instruction, Environment) -> + Destination = hipe_rtl:phi_dst(Instruction), + case lookup_lattice_value(Destination, Environment) of + bottom -> + [Instruction]; + top -> + ?WARNING_MSG("The dst of ~w is top after SCCP. Strange\n",[Instruction]), + ?EXIT({"bang !", Instruction}), + [Instruction]; + Value -> + [hipe_rtl:mk_move(Destination, hipe_rtl:mk_imm(Value))] + end. + +%%----------------------------------------------------------------------------- + +%% make sure that all precoloured rgisters are taken out of the equation. +lookup_lattice_value(X, Environment) -> + case hipe_rtl_arch:is_precoloured(X) or hipe_rtl:is_const_label(X) of + true -> + bottom; + false -> + lookup_lattice_value2(X, Environment) + end. + +lookup_lattice_value2(X, Environment) -> + LatticeValues = env__lattice_values(Environment), + case hipe_rtl:is_imm(X) of + true -> + hipe_rtl:imm_value(X); + false -> + case gb_trees:lookup(X, LatticeValues) of + none -> + io:format("~w~n",[LatticeValues]), + ?WARNING_MSG("Earlier compiler steps generated erroneous " + "code for X = ~w. We are ignoring this.\n",[X]), + bottom; + {value, top} -> + ?EXIT({"lookup_lattice_value, top", X}), + top; + {value, Y} -> + Y + end + end. + +%%----------------------------- End of file ----------------------------------- diff --git a/lib/hipe/rtl/hipe_rtl_ssapre.erl b/lib/hipe/rtl/hipe_rtl_ssapre.erl new file mode 100644 index 0000000000..a9e92e5688 --- /dev/null +++ b/lib/hipe/rtl/hipe_rtl_ssapre.erl @@ -0,0 +1,1679 @@ +%% -*- 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% +%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% File : hipe_rtl_ssapre.erl +%% Author : He Bingwen and Fr�d�ric Haziza +%% Description : Performs Partial Redundancy Elimination on SSA form. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% @doc +%% +%% This module implements the <a href="http://cs.wheaton.edu/%7Etvandrun/writings/spessapre.pdf">Anticipation-SSAPRE algorithm</a>, +%% with several modifications for Partial Redundancy Elimination on SSA form. +%% We actually found problems in this algorithm, so +%% we implement another version with several advantages: +%% - No loop for Xsi insertions +%% - No fix point iteration for the downsafety part +%% - Less computations for Will Be Available part +%% - Complexity of the overall algorithm is improved +%% +%% We were supposed to publish these results anyway :D +%% +%% @end +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-module(hipe_rtl_ssapre). + +-export([rtl_ssapre/2]). + +-include("../main/hipe.hrl"). +-include("hipe_rtl.hrl"). + +%%-define(SSAPRE_DEBUG, true ). %% When uncommented, produces debug printouts +-define( SETS, ordsets ). %% Which set implementation module to use +-define( CFG, hipe_rtl_cfg ). +-define( RTL, hipe_rtl ). +-define( BB, hipe_bb ). +-define( ARCH, hipe_rtl_arch ). +-define( GRAPH, digraph ). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Debugging stuff +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-ifndef(SSAPRE_DEBUG). +-define(pp_debug(_Str, _Args), ok). +-else. +-define(pp_debug(Str, Args), io:format(standard_io, Str, Args)). +-endif. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Records / Structures +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-record(xsi_link, {num}). %% Number is the index of the temporary (a Key into the Xsi Tree) +-record(temp, {key, var}). +-record(bottom, {key, var}). +-record(xsi, {inst, %% Associated instruction + def, %% Hypothetical temporary variable + %% that stores the result of the computation + label, %% Block Label where the xsi is inserted + opList, %% List of operands + cba, %% + later, %% + wba + }). + +-record(pre_candidate, {alu, def}). +-record(xsi_op, {pred, op}). + +-record(mp, {xsis, maps, preds, defs, uses, ndsSet}). +-record(block, {type, attributes}). + +-record(eop, {expr, var, stopped_by}). +-record(insertion, {code, from}). + +-record(const_expr, {var, value}). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Main function +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +rtl_ssapre(RtlSSACfg, Options) -> + %% io:format("\n################ Original CFG ################\n"), + %% hipe_rtl_cfg:pp(RtlSSACfg), + %% io:format("\n\n############ SSA-Form CHECK ==> ~w\n",[hipe_rtl_ssa:check(RtlSSACfg)]), + + {CFG2,XsiGraph,CFGGraph,MPs} = perform_Xsi_insertion(RtlSSACfg,Options), + %%?pp_debug("~n~n################ Xsi CFG ################\n",[]),pp_cfg(CFG2,XsiGraph), + XsiList = ?GRAPH:vertices(XsiGraph), + case XsiList of + [] -> + %% No Xsi + ?option_time(?pp_debug("~n~n################ No Xsi Inserted ################~n",[]),"RTL A-SSAPRE No Xsi inserted (skip Downsafety and Will Be Available)",Options), + ok; + _ -> + ?pp_debug("~n############ Downsafety ##########~n",[]), + ?option_time(perform_downsafety(MPs,CFGGraph,XsiGraph),"RTL A-SSAPRE Downsafety",Options), + ?pp_debug("~n~n################ CFG Graph ################~n",[]),pp_cfggraph(CFGGraph), + ?pp_debug("~n############ Will Be Available ##########~n",[]), + ?option_time(perform_will_be_available(XsiGraph,CFGGraph,Options),"RTL A-SSAPRE WillBeAvailable",Options) + end, + + ?pp_debug("~n############ No more need for the CFG Graph....Deleting...",[]),?GRAPH:delete(CFGGraph), + ?pp_debug("~n~n################ Xsi Graph ################~n",[]),pp_xsigraph(XsiGraph), + + ?pp_debug("~n############ Code Motion ##########~n",[]), + Labels = ?CFG:preorder(CFG2), + + ?pp_debug("~n~n################ Xsi CFG ################~n",[]),pp_cfg(CFG2,XsiGraph), + + init_redundancy_count(), + ?option_time(FinalCFG=perform_code_motion(Labels,CFG2,XsiGraph),"RTL A-SSAPRE Code Motion",Options), + + ?pp_debug("\n############ No more need for the Xsi Graph....Deleting...",[]),?GRAPH:delete(XsiGraph), + + %% io:format("\n################ Final CFG ################\n"), + %% hipe_rtl_cfg:pp(FinalCFG), + %% io:format("\n\n############ SSA-Form CHECK ==> ~w\n", + %% [hipe_rtl_ssa:check(FinalCFG)]), + ?pp_debug("\nSSAPRE : ~w redundancies were found\n",[get_redundancy_count()]), + + FinalCFG. + +%% ########################################################################## +%% ######################## XSI INSERTION ################################### +%% ########################################################################## + +perform_Xsi_insertion(Cfg, Options) -> + init_counters(), %% Init counters for Bottoms and Temps + DigraphOpts = [cyclic, private], + XsiGraph = digraph:new(DigraphOpts), + %% Be carefull, the digraph component is NOT garbage collected, + %% so don't create 20 millions of instances! + %% finds the longest depth + %% Depth-first, preorder traversal over Basic Blocks. + %%Labels = ?CFG:reverse_postorder(Cfg), + Labels = ?CFG:preorder(Cfg), + + ?pp_debug("~n~n############# Finding definitions for computation~n~n",[]), + ?option_time({Cfg2,XsiGraph} = find_definition_for_computations(Labels,Cfg,XsiGraph),"RTL A-SSAPRE Xsi Insertion, searching from instructions",Options), + + %% Active List creation + GeneratorXsiList = lists:sort(?GRAPH:vertices(XsiGraph)), + ?pp_debug("~n~n############# Inserted Xsis ~w",[GeneratorXsiList]), + ?pp_debug("~n~n############# Finding operands~n",[]), + ?option_time({Cfg3,XsiGraph} = find_operands(Cfg2,XsiGraph,GeneratorXsiList,0),"RTL A-SSAPRE Xsi Insertion, finding operands",Options), + + %% Creating the CFGGraph + ?pp_debug("~n~n############# Creating CFG Graph",[]), + ?pp_debug("~n############# Labels = ~w",[Labels]), + CFGGraph = digraph:new(DigraphOpts), + [StartLabel|Others] = Labels, % adding the start label as a leaf + ?pp_debug("~nAdding a vertex for the start label: ~w",[StartLabel]), + ?GRAPH:add_vertex(CFGGraph, StartLabel, #block{type = top}), + % Doing the others + ?option_time(MPs=create_cfggraph(Others,Cfg3,CFGGraph,[],[],[],XsiGraph),"RTL A-SSAPRE Xsi Insertion, creating intermediate 'SSAPRE Graph'",Options), + + %% Return the bloody collected information + {Cfg3,XsiGraph,CFGGraph,MPs}. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +find_definition_for_computations([], Cfg, XsiGraph) -> + {Cfg,XsiGraph}; %% No more block to inspect in the depth-first order +find_definition_for_computations([Label|Rest], Cfg, XsiGraph) -> + Code = ?BB:code(?CFG:bb(Cfg,Label)), + {NewCfg,XsiGraph} = find_definition_for_computations_in_block(Label,Code,Cfg,[],XsiGraph), + find_definition_for_computations(Rest, NewCfg, XsiGraph). + +%%=========================================================================== +%% Searches from instruction for one block BlockLabel. +%% We process forward over instructions. + +find_definition_for_computations_in_block(BlockLabel,[],Cfg, + VisitedInstructions,XsiGraph)-> + Code = lists:reverse(VisitedInstructions), + NewBB = ?BB:mk_bb(Code), + NewCfg = ?CFG:bb_add(Cfg,BlockLabel,NewBB), + {NewCfg,XsiGraph}; %% No more instructions to inspect in this block +find_definition_for_computations_in_block(BlockLabel,[Inst|Rest],Cfg, + VisitedInstructions,XsiGraph) -> + %% ?pp_debug(" Inspecting instruction: ",[]),pp_instr(Inst,nil), + case Inst of + #alu{} -> + %% Is Inst interesting for SSAPRE? + %% i.e., is Inst an arithmetic operation which doesn't deal with precoloured? + %% Note that since we parse forward, we have no 'pre_candidate'-type so far. + case check_definition(Inst,VisitedInstructions,BlockLabel,Cfg,XsiGraph) of + {def_found,Def} -> + %% Replacing Inst in Cfg + NewInst = #pre_candidate{alu=Inst,def=Def}, + NewVisited = [NewInst|VisitedInstructions], + %% Recurse forward over instructions, same CFG, same XsiGraph + find_definition_for_computations_in_block(BlockLabel,Rest,Cfg, + NewVisited,XsiGraph); + {merge_point,Xsi} -> + Def = Xsi#xsi.def, + Key = Def#temp.key, + NewInst = #pre_candidate{alu=Inst,def=Def}, + XsiLink = #xsi_link{num=Key}, + + %% Add a vertex to the Xsi Graph + ?GRAPH:add_vertex(XsiGraph,Key,Xsi), + ?pp_debug(" Inserting Xsi: ",[]),pp_xsi(Xsi), + + Label = Xsi#xsi.label, + case BlockLabel =:= Label of + false -> + %% Insert the Xsi in the appropriate block + Code = hipe_bb:code(?CFG:bb(Cfg,Label)), + {BeforeCode,AfterCode} = split_for_xsi(lists:reverse(Code),[]), + NewCode = BeforeCode++[XsiLink|AfterCode], + NewBB = hipe_bb:mk_bb(NewCode), + NewCfg = ?CFG:bb_add(Cfg,Label,NewBB), + NewVisited = [NewInst|VisitedInstructions]; + _-> + {BeforeCode,AfterCode} = split_for_xsi(VisitedInstructions,[]), + TempVisited = BeforeCode++[XsiLink|AfterCode], + TempVisited2 = lists:reverse(TempVisited), + NewVisited = [NewInst|TempVisited2], + NewCfg = Cfg + end, + find_definition_for_computations_in_block(BlockLabel, Rest, NewCfg, + NewVisited, XsiGraph) + end; + _ -> + %%?pp_debug("~n [L~w] Not concerned with: ~w",[BlockLabel,Inst]), + %% If the instruction is not a SSAPRE candidate, we skip it and keep on + %% processing instructions + %% Prepend Inst, so that we have all in reverse order. + %% Easy to parse backwards + find_definition_for_computations_in_block(BlockLabel, Rest, Cfg, + [Inst|VisitedInstructions], XsiGraph) + end. + +%% ############################################################################ +%% We have E as an expression, I has an alu (arithmetic operation), and +%% we inspect backwards the previous instructions to find a definition for E. +%% Since we parse in forward order, we know that the previous SSAPRE +%% instruction will have a definition. + +check_definition(E,[],BlockLabel,Cfg,XsiGraph)-> + %% No more instructions in that block + %% No definition found in that block + %% Search is previous blocks + Preds = ?CFG:pred(Cfg, BlockLabel), + %% ?pp_debug("~n CHECKING DEFINITION ####### Is L~w a merge block? It has ~w preds. So far E=",[BlockLabel,length(Preds)]),pp_expr(E), + case Preds of + [] -> + %% Entry Point + {def_found,bottom}; + [P] -> + %% One predecessor only, we just keep looking for a definition in that block + VisitedInstructions = lists:reverse(hipe_bb:code(?CFG:bb(Cfg,P))), + check_definition(E,VisitedInstructions,P,Cfg,XsiGraph); + _ -> + Temp = new_temp(), + %% It's a merge point + OpList = [#xsi_op{pred=X} || X<-Preds], + Xsi = #xsi{inst=E,def=Temp,label=BlockLabel,opList=OpList}, + {merge_point,Xsi} + end; +check_definition(E,[CC|Rest],BlockLabel,Cfg,XsiGraph) -> + SRC1 = ?RTL:alu_src1(E), + SRC2 = ?RTL:alu_src2(E), + case CC of + #alu{} -> + exit({?MODULE,should_not_be_an_alu, + {"Why the hell do we still have an alu???",CC}}); + #pre_candidate{} -> + %% C is the previous instruction + C = CC#pre_candidate.alu, + DST = ?RTL:alu_dst(C), + case DST =:= SRC1 orelse DST =:= SRC2 of + false -> + case check_match(E,C) of + true -> %% It's a computation of E! + %% Get the dst of the alu + {def_found,DST}; + _-> + check_definition(E,Rest,BlockLabel,Cfg,XsiGraph) + end; + true -> + %% Get the definition of C, since C is PRE-candidate AND has been processed before + DEF = CC#pre_candidate.def, + case DEF of + bottom -> + %% Def(E)=bottom, STOP + {def_found,bottom}; + _ -> + %% Emend E with this def(C) + %%?pp_debug("Parameters are E=~w, DST=~w, DEF=~w",[E,DST,DEF]), + F = emend(E,DST,DEF), + check_definition(F,Rest,BlockLabel,Cfg,XsiGraph) %% Continue the search + end + end; + #move{} -> + %% It's a move, we emend E, and continue the definition search + DST = ?RTL:move_dst(CC), + F = case SRC1 =:= DST orelse SRC2 =:= DST of + true -> + SRC = ?RTL:move_src(CC), + emend(E,DST,SRC); + _ -> + E + end, + check_definition(F,Rest,BlockLabel,Cfg,XsiGraph); %% Continue the search + #xsi_link{} -> + {_K,Xsi} = ?GRAPH:vertex(XsiGraph,CC#xsi_link.num), + C = Xsi#xsi.inst, + case check_match(C,E) of + true -> %% There is a Xsi already with a computation of E! + %% fetch definition of C, and give it to E + {def_found,Xsi#xsi.def}; + _-> + check_definition(E,Rest,BlockLabel,Cfg,XsiGraph) + end; + #phi{} -> + %% skip them. NOTE: Important to separate this case from the next one + check_definition(E,Rest,BlockLabel,Cfg,XsiGraph); + _ -> + %% Note: the function calls or some other instructions can change the pre-coloured registers + %% which are able to be redefined. This breaks of course the SSA form. + %% If there is a redefinition we can give bottom to the computation, and no xsi will be inserted. + %% (In some sens, the result of the computation is new at that point.) + PreColouredTest = ?ARCH:is_precoloured(SRC1) orelse ?ARCH:is_precoloured(SRC2), + + %%RegisterTest = ?RTL:is_reg(?RTL:alu_dst(E)) orelse ?RTL:is_reg(SRC1) orelse ?RTL:is_reg(SRC2), + RegisterTest = ?RTL:is_reg(?RTL:alu_dst(E)), %% That means we cannot reuse the result held in this register... + + case PreColouredTest orelse RegisterTest of + true -> + {def_found,bottom}; + false -> + DC = ?RTL:defines(CC), + case lists:member(SRC1,DC) orelse lists:member(SRC2,DC) of + true -> + {def_found,bottom}; + false -> + %% Orthogonal to E, we continue the search + check_definition(E,Rest,BlockLabel,Cfg,XsiGraph) + end + end + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +check_match(E, C) -> + OpE = ?RTL:alu_op(E), + OpC = ?RTL:alu_op(C), + case OpE =:= OpC of + false -> + false; + true -> + Src1E = ?RTL:alu_src1(E), + Src2E = ?RTL:alu_src2(E), + Src1C = ?RTL:alu_src1(C), + Src2C = ?RTL:alu_src2(C), + case Src1E =:= Src1C of + true -> + Src2E =:= Src2C; + false -> + Src1E =:= Src2C andalso Src2E =:= Src1C + end + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +expr_is_const(E) -> + ?RTL:is_imm(?RTL:alu_src1(E)) andalso ?RTL:is_imm(?RTL:alu_src2(E)). +%% is_number(?RTL:alu_src1(E)) andalso is_number(?RTL:alu_src2(E)). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Must be an arithmetic operation, i.e. #alu{} +emend(Expr, S, Var) -> + SRC1 = ?RTL:alu_src1(Expr), + NewExpr = case SRC1 =:= S of + true -> ?RTL:alu_src1_update(Expr,Var); + false -> Expr + end, + SRC2 = ?RTL:alu_src2(NewExpr), + case SRC2 =:= S of + true -> ?RTL:alu_src2_update(NewExpr,Var); + false -> NewExpr + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +split_for_xsi([], Acc) -> + {[], Acc}; % no_xsi_no_phi_found; +split_for_xsi([I|Is] = Code, Acc) -> %% [I|Is] in backward order, Acc in order + case I of + #xsi_link{} -> + {lists:reverse(Code), Acc}; + #phi{} -> + {lists:reverse(Code), Acc}; + _ -> + split_for_xsi(Is, [I|Acc]) + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Phase 1.B : Search for operands +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +find_operands(Cfg,XsiGraph,[],_Count) -> + {Cfg,XsiGraph}; +find_operands(Cfg,XsiGraph,ActiveList,Count) -> + {NewCfg,TempActiveList} = find_operands_for_active_list(Cfg,XsiGraph,ActiveList,[]), + NewActiveList = lists:reverse(TempActiveList), + ?pp_debug("~n################ Finding operands (iteration ~w): ~w have been introduced. Now ~w in total~n", + [Count+1, length(NewActiveList), length(?GRAPH:vertices(XsiGraph))]), + find_operands(NewCfg,XsiGraph,NewActiveList,Count+1). + +find_operands_for_active_list(Cfg,_XsiGraph,[],ActiveListAcc) -> + {Cfg,ActiveListAcc}; +find_operands_for_active_list(Cfg,XsiGraph,[K|Ks],ActiveListAcc) -> + {_Key,Xsi} = ?GRAPH:vertex(XsiGraph,K), + ?pp_debug("~n Inspecting operands of : ~n",[]),pp_xsi(Xsi), + Preds = ?CFG:pred(Cfg, Xsi#xsi.label), + {NewCfg,NewActiveListAcc}=determine_operands(Xsi,Preds,Cfg,K,XsiGraph,ActiveListAcc), + {_Key2,Xsi2} = ?GRAPH:vertex(XsiGraph,K), + ?pp_debug("~n ** Final Xsi: ~n",[]),pp_xsi(Xsi2), + ?pp_debug("~n #####################################################~n",[]), + find_operands_for_active_list(NewCfg,XsiGraph,Ks,NewActiveListAcc). + +determine_operands(_Xsi,[],Cfg,_K,_XsiGraph,ActiveAcc) -> + %% All operands have been determined. + %% The CFG is not updated, only the XsiGraph + {Cfg,ActiveAcc}; +determine_operands(Xsi,[P|Ps],Cfg,K,XsiGraph,ActiveAcc) -> + Label = Xsi#xsi.label, + ReverseCode = lists:reverse(hipe_bb:code(?CFG:bb(Cfg,Label))), + VisitedInstructions = get_visited_instructions(Xsi,ReverseCode), + Res = determine_e_prime(Xsi#xsi.inst,VisitedInstructions,P,XsiGraph), + case Res of + operand_is_bottom -> + NewXsi = xsi_arg_update(Xsi,P,new_bottom()), + ?GRAPH:add_vertex(XsiGraph,K,NewXsi), + determine_operands(NewXsi,Ps,Cfg,K,XsiGraph,ActiveAcc); + operand_is_const_expr -> + NewXsi = xsi_arg_update(Xsi,P,new_bottom()), + ?GRAPH:add_vertex(XsiGraph,K,NewXsi), + determine_operands(NewXsi,Ps,Cfg,K,XsiGraph,ActiveAcc); + {sharing_operand,Op} -> + NewXsi = xsi_arg_update(Xsi,P,Op), + ?GRAPH:add_vertex(XsiGraph,K,NewXsi), + determine_operands(NewXsi,Ps,Cfg,K,XsiGraph,ActiveAcc); + {revised_expression,E_prime} -> + ?pp_debug(" E' is determined : ",[]),pp_expr(E_prime), + ?pp_debug(" and going along the edge L~w~n",[P]), + %% Go along the edge P + RevCode = lists:reverse(hipe_bb:code(?CFG:bb(Cfg,P))), + case check_one_operand(E_prime,RevCode,P,Cfg,K,XsiGraph) of + {def_found,Def} -> + NewXsi = xsi_arg_update(Xsi,P,Def), + ?GRAPH:add_vertex(XsiGraph,K,NewXsi), + determine_operands(NewXsi,Ps,Cfg,K,XsiGraph,ActiveAcc); + + {expr_found,ChildExpr} -> + NewXsi = xsi_arg_update(Xsi,P,ChildExpr), + ?GRAPH:add_vertex(XsiGraph,K,NewXsi), + determine_operands(NewXsi,Ps,Cfg,K,XsiGraph,ActiveAcc); + + {expr_is_const, Op} -> + %% We detected that the expression is of the form: 'N op M' + %% where N and M are constant. + NewXsi = xsi_arg_update(Xsi,P,Op), + ?GRAPH:add_vertex(XsiGraph,K,NewXsi), + determine_operands(NewXsi,Ps,Cfg,K,XsiGraph,ActiveAcc); + + {merge_point,XsiChild} -> + %% Update that Xsi, give its definition as Operand for the + %% search, and go on + XsiChildDef = XsiChild#xsi.def, + NewXsi = xsi_arg_update(Xsi,P,XsiChildDef), + ?GRAPH:add_vertex(XsiGraph,K,NewXsi), + + KeyChild = XsiChildDef#temp.key, + XsiChildLink = #xsi_link{num=KeyChild}, + ?GRAPH:add_vertex(XsiGraph,KeyChild,XsiChild), + + %% Should not be the same block !!!!!!! + RCode = lists:reverse(hipe_bb:code(?CFG:bb(Cfg,XsiChild#xsi.label))), + {BCode,ACode} = split_code_for_xsi(RCode,[]), + + NewCode = BCode++[XsiChildLink|ACode], + NewBB = hipe_bb:mk_bb(NewCode), + NewCfg = ?CFG:bb_add(Cfg, XsiChild#xsi.label, NewBB), + + ?pp_debug(" -- ",[]),pp_arg(Xsi#xsi.def),?pp_debug(" causes insertion of: ~n",[]),pp_xsi(XsiChild), + ?pp_debug(" -- Adding an edge ",[]),pp_arg(Xsi#xsi.def),?pp_debug(" -> ",[]),pp_arg(XsiChild#xsi.def), + + %% Adding an edge... + %%?GRAPH:add_edge(XsiGraph,K,KeyChild,"family"), + ?GRAPH:add_edge(XsiGraph,K,KeyChild), + determine_operands(NewXsi,Ps,NewCfg,K,XsiGraph,[KeyChild|ActiveAcc]) + end + end. + +determine_e_prime(Expr,VisitedInstructions,Pred,XsiGraph) -> + %% MUST FETCH FROM THE XSI TREE, since Xsis are not updated yet in the CFG + NewExpr = emend_with_phis(Expr,VisitedInstructions,Pred), + emend_with_processed_xsis(NewExpr,VisitedInstructions,Pred,XsiGraph). + +emend_with_phis(EmendedE, [], _) -> + EmendedE; +emend_with_phis(E, [I|Rest], Pred) -> + case I of + #phi{} -> + Dst = ?RTL:phi_dst(I), + UE = ?RTL:uses(E), %% Should we get SRC1 and SRC2 instead? + case lists:member(Dst, UE) of + false -> + emend_with_phis(E, Rest, Pred); + true -> + NewE = emend(E, Dst, ?RTL:phi_arg(I,Pred)), + emend_with_phis(NewE, Rest, Pred) + end; + _ -> + emend_with_phis(E, Rest, Pred) + end. + +emend_with_processed_xsis(EmendedE, [], _, _) -> + {revised_expression,EmendedE}; +emend_with_processed_xsis(E, [I|Rest], Pred, XsiGraph) -> + case I of + #xsi_link{} -> + Key = I#xsi_link.num, + {_KK,Xsi} = ?GRAPH:vertex(XsiGraph,Key), + Def = Xsi#xsi.def, + UE = ?RTL:uses(E), %% Should we get SRC1 and SRC2 instead? + case lists:member(Def,UE) of + false -> + CE = Xsi#xsi.inst, + case check_match(E,CE) of + true -> %% It's a computation of E! + case xsi_arg(Xsi,Pred) of + undetermined_operand -> + exit({?MODULE,check_operand_sharing,"######## �h Dear, we trusted Kostis !!!!!!!!! #############"}); + XsiOp -> + {sharing_operand,XsiOp} %% They share operands + end; + _-> + emend_with_processed_xsis(E,Rest,Pred,XsiGraph) + end; + true -> + A = xsi_arg(Xsi,Pred), + %% ?pp_debug(" ######### xsi_arg(I:~w,Pred:~w) = ~w~n",[I,Pred,A]), + case A of + #bottom{} -> + operand_is_bottom; + #const_expr{} -> + operand_is_const_expr; + #eop{} -> + NewE = emend(E,Def,A#eop.var), + emend_with_processed_xsis(NewE,Rest,Pred,XsiGraph); + undetermined_operand -> + exit({?MODULE,emend_with_processed_xsis,"######## �h Dear, we trusted Kostis, again !!!!!!!!! #############"}); + XsiOp -> + NewE = emend(E,Def,XsiOp), + emend_with_processed_xsis(NewE,Rest,Pred,XsiGraph) + end + end; + _ -> + emend_with_processed_xsis(E,Rest,Pred,XsiGraph) + end. + +%% get_visited_instructions(Xsi,[]) -> +%% ?pp_debug("~nWe don't find this xsi with def ",[]),pp_arg(Xsi#xsi.def),?pp_debug(" in L~w : ",[Xsi#xsi.label]), +%% exit({?MODULE,no_such_xsi_in_block,"We didn't find that Xsi in the block"}); +get_visited_instructions(Xsi, [I|Is]) -> + case I of + #xsi_link{} -> + XsiDef = Xsi#xsi.def, + Key = XsiDef#temp.key, + case I#xsi_link.num =:= Key of + true -> + Is; + false -> + get_visited_instructions(Xsi, Is) + end; + _ -> + get_visited_instructions(Xsi, Is) + end. + +split_code_for_xsi([], Acc) -> + {[],Acc}; +split_code_for_xsi([I|Is] = Code, Acc) -> + case I of + #xsi_link{} -> + {lists:reverse(Code), Acc}; + #phi{} -> + {lists:reverse(Code), Acc}; + _ -> + split_code_for_xsi(Is, [I|Acc]) + end. + +check_one_operand(E, [], BlockLabel, Cfg, XsiKey, XsiGraph) -> + %% No more instructions in that block + %% No definition found in that block + %% Search is previous blocks + Preds = ?CFG:pred(Cfg, BlockLabel), + case Preds of + [] -> + %% Entry Point + {def_found,new_bottom()}; + [P] -> + %% One predecessor only, we just keep looking for a definition in that block + case expr_is_const(E) of + true -> + ?pp_debug("\n\n############## Wow expr is constant: ~w",[E]), + Var = ?RTL:mk_new_var(), + Value = eval_expr(E), + Op = #const_expr{var = Var, value = Value}, + {expr_is_const, Op}; + false -> + VisitedInstructions = lists:reverse(?BB:code(?CFG:bb(Cfg,P))), + check_one_operand(E, VisitedInstructions, P, Cfg, XsiKey, XsiGraph) + end; + _ -> + %% It's a merge point + case expr_is_const(E) of + true -> + ?pp_debug("\n\n############## Wow expr is constant at merge point: ~w",[E]), + Var = ?RTL:mk_new_var(), + Value = eval_expr(E), + Op = #const_expr{var = Var, value = Value}, + {expr_is_const, Op}; + false -> + Temp = new_temp(), + OpList = [#xsi_op{pred = X} || X <- Preds], + Xsi = #xsi{inst = E, def = Temp, label = BlockLabel, opList = OpList}, + {merge_point, Xsi} + end + end; +check_one_operand(E, [CC|Rest], BlockLabel, Cfg, XsiKey, XsiGraph) -> + SRC1 = ?RTL:alu_src1(E), + SRC2 = ?RTL:alu_src2(E), + %% C is the previous instruction + case CC of + #alu{} -> + exit({?MODULE,should_not_be_an_alu, + {"Why the hell do we still have an alu???",CC}}); + #xsi{} -> + exit({?MODULE,should_not_be_a_xsi, + {"Why the hell do we still have a xsi???",CC}}); + #pre_candidate{} -> + C = CC#pre_candidate.alu, + DST = ?RTL:alu_dst(C), + case DST =:= SRC1 orelse DST =:= SRC2 of + true -> + %% Get the definition of C, since C is PRE-candidate AND has + %% been processed before + DEF = CC#pre_candidate.def, + case DEF of + bottom -> + %% Def(E)=bottom, STOP + %% No update of the XsiGraph + {def_found,new_bottom()}; + _-> + %% Simply emend + F = emend(E,DST,DEF), + ?pp_debug("~nEmendation : E= ",[]),pp_expr(E),?pp_debug(" ==> E'= ",[]),pp_expr(F),?pp_debug("~n",[]), + check_one_operand(F,Rest,BlockLabel,Cfg,XsiKey,XsiGraph) + end; + false -> + case check_match(C,E) of + true -> %% It's a computation of E! + %% It should give DST and not Def + %% No update of the XsiGraph, cuz we use DST and not Def + %% The operand is therefore gonna be a real variable + {def_found,DST}; + _-> + %% Nothing to do with E + check_one_operand(E,Rest,BlockLabel,Cfg,XsiKey,XsiGraph) + end + end; + #move{} -> + %% It's a move, we emend E, and continue the definition search + DST = ?RTL:move_dst(CC), + case SRC1 =:= DST orelse SRC2 =:= DST of + true -> + SRC = ?RTL:move_src(CC), + F = emend(E,DST,SRC), + check_one_operand(F,Rest,BlockLabel,Cfg,XsiKey,XsiGraph); %% Continue the search + _ -> + check_one_operand(E,Rest,BlockLabel,Cfg,XsiKey,XsiGraph) %% Continue the search + end; + #xsi_link{} -> + Key = CC#xsi_link.num, + %% Is Key a family member of XsiDef ? + {_KK,Xsi} = ?GRAPH:vertex(XsiGraph,Key), + C = Xsi#xsi.inst, + case check_match(E,C) of + true -> %% There is a Xsi already with a computation of E! + %% fetch definition of C, and give it to E + %% Must update an edge in the XsiGraph, and here, we know it's a Temp + %% Note: this can create a loop (= a cycle of length 1) + ?pp_debug(" -- Found a cycle with match: Adding an edge t~w -> t~w",[XsiKey,Key]), + ?GRAPH:add_edge(XsiGraph,XsiKey,Key), + {def_found,Xsi#xsi.def}; + _ -> + case ?GRAPH:get_path(XsiGraph,Key,XsiKey) of + false -> + %% Is it a loop back to itself??? + case Key =:= XsiKey of + false -> + check_one_operand(E,Rest,BlockLabel,Cfg,XsiKey,XsiGraph); + _ -> + {expr_found,#eop{expr=E,var=?RTL:mk_new_var(),stopped_by=Key}} + end; + _ -> + %% Returning the expression instead of looping + %% And in case of no match + ExprOp = #eop{expr=E,var=?RTL:mk_new_var(),stopped_by=Key}, + {expr_found,ExprOp} + end + end; + #phi{} -> %% skip them + check_one_operand(E,Rest,BlockLabel,Cfg,XsiKey,XsiGraph); + _ -> + PreColouredTest = ?ARCH:is_precoloured(SRC1) orelse ?ARCH:is_precoloured(SRC2), + + %%RegisterTest = ?RTL:is_reg(?RTL:alu_dst(E)) orelse ?RTL:is_reg(SRC1) orelse ?RTL:is_reg(SRC2), + RegisterTest = ?RTL:is_reg(?RTL:alu_dst(E)), + case PreColouredTest orelse RegisterTest of + true -> + {def_found,new_bottom()}; + _-> + DC = ?RTL:defines(CC), + case lists:member(SRC1,DC) orelse lists:member(SRC2,DC) of + true -> + {def_found,new_bottom()}; + _ -> + %% Orthogonal to E, we continue the search + check_one_operand(E,Rest,BlockLabel,Cfg,XsiKey,XsiGraph) + end + end + end. + +eval_expr(E) -> + ?pp_debug("~n Evaluating the result of ~w~n", [E]), + Op1 = ?RTL:alu_src1(E), + Op2 = ?RTL:alu_src2(E), + true = ?RTL:is_imm(Op1), + Val1 = ?RTL:imm_value(Op1), + true = ?RTL:is_imm(Op2), + Val2 = ?RTL:imm_value(Op2), + {Result, _Sign, _Zero, _Overflow, _Carry} = ?ARCH:eval_alu(?RTL:alu_op(E), Val1, Val2), + ?pp_debug("~n Result is then ~w~n", [Result]), + ?RTL:mk_imm(Result). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%% CREATTING CFGGRAPH %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +create_cfggraph([],_Cfg,CFGGraph,ToBeFactorizedAcc,MPAcc,LateEdges,_XsiGraph) -> + ?pp_debug("~n~n ############# PostProcessing ~n~w~n",[LateEdges]), + post_process(LateEdges,CFGGraph), + ?pp_debug("~n~n ############# Factorizing ~n~w~n",[ToBeFactorizedAcc]), + factorize(ToBeFactorizedAcc,CFGGraph), + MPAcc; +create_cfggraph([Label|Ls],Cfg,CFGGraph,ToBeFactorizedAcc,MPAcc,LateEdges,XsiGraph) -> + Preds = ?CFG:pred(Cfg, Label), + case Preds of + [] -> + exit({?MODULE,do_not_call_on_top,{"Why the hell do we call that function on the start label???",Label}}); + [P] -> + Code = ?BB:code(?CFG:bb(Cfg, Label)), + Defs = get_defs_in_non_merge_block(Code, []), + ?pp_debug("~nAdding a vertex for ~w", [Label]), + Succs = ?CFG:succ(Cfg, Label), + case Succs of + [] -> %% Exit point + ?GRAPH:add_vertex(CFGGraph, Label, #block{type = exit}), + NewToBeFactorizedAcc = ToBeFactorizedAcc; + _ -> %% Split point + ?GRAPH:add_vertex(CFGGraph,Label,#block{type=not_mp,attributes={P,Succs}}), + NewToBeFactorizedAcc = [Label|ToBeFactorizedAcc] + end, + ?pp_debug("~nAdding an edge ~w -> ~w (~w)",[P,Label,Defs]), + case ?GRAPH:add_edge(CFGGraph,P,Label,Defs) of + {error,Reason} -> + exit({?MODULE,forget_that_for_christs_sake_bingwen_please,{"Bad edge",Reason}}); + _ -> + ok + end, + create_cfggraph(Ls,Cfg,CFGGraph,NewToBeFactorizedAcc,MPAcc,LateEdges,XsiGraph); + _ -> %% Merge point + Code = ?BB:code(?CFG:bb(Cfg,Label)), + {Defs,Xsis,Maps,Uses} = get_info_in_merge_block(Code,XsiGraph,[],[],gb_trees:empty(),gb_trees:empty()), + Attributes = #mp{preds=Preds,xsis=Xsis,defs=Defs,maps=Maps,uses=Uses}, + MergeBlock = #block{type=mp,attributes=Attributes}, + ?pp_debug("~nAdding a vertex for ~w with Defs= ~w",[Label,Defs]), + ?GRAPH:add_vertex(CFGGraph,Label,MergeBlock), + %% Add edges + NewLateEdges = add_edges_for_mp(Preds,Label,LateEdges), + create_cfggraph(Ls,Cfg,CFGGraph,ToBeFactorizedAcc,[Label|MPAcc],NewLateEdges,XsiGraph) + end. + +get_defs_in_non_merge_block([], Acc) -> + ?SETS:from_list(Acc); +get_defs_in_non_merge_block([Inst|Rest], Acc) -> + case Inst of + #pre_candidate{} -> + Def = Inst#pre_candidate.def, + case Def of + #temp{} -> + %% {temp,Key,_Var} -> + %% get_defs_in_non_merge_block(Rest,[Key|Acc]); + get_defs_in_non_merge_block(Rest, [Def#temp.key|Acc]); + _-> %% Real variables or bottom + get_defs_in_non_merge_block(Rest, Acc) + end; + _ -> + get_defs_in_non_merge_block(Rest, Acc) + end. + +get_info_in_merge_block([],_XsiGraph,Defs,Xsis,Maps,Uses) -> + {?SETS:from_list(Defs),Xsis,Maps,Uses}; %% Xsis are in backward order +get_info_in_merge_block([Inst|Rest],XsiGraph,Defs,Xsis,Maps,Uses) -> + case Inst of + #pre_candidate{} -> + Def = Inst#pre_candidate.def, + case Def of + #temp{} -> + get_info_in_merge_block(Rest,XsiGraph,[Def#temp.key|Defs],Xsis,Maps,Uses); + _ -> + get_info_in_merge_block(Rest,XsiGraph,Defs,Xsis,Maps,Uses) + end; + #xsi_link{} -> + Key = Inst#xsi_link.num, + {_Key,Xsi} = ?GRAPH:vertex(XsiGraph,Key), + OpList = xsi_oplist(Xsi), + {NewMaps,NewUses} = add_map_and_uses(OpList,Key,Maps,Uses), + get_info_in_merge_block(Rest,XsiGraph,Defs,[Key|Xsis],NewMaps,NewUses); + _ -> + get_info_in_merge_block(Rest,XsiGraph,Defs,Xsis,Maps,Uses) + end. + +add_edges_for_mp([], _Label, LateEdges) -> + LateEdges; +add_edges_for_mp([P|Ps], Label, LateEdges) -> + add_edges_for_mp(Ps,Label,[{P,Label}|LateEdges]). + +%% Doesn't do anything so far +add_map_and_uses([], _Key, Maps, Uses) -> + {Maps,Uses}; +add_map_and_uses([XsiOp|Ops], Key, Maps, Uses) -> + case XsiOp#xsi_op.op of + #bottom{} -> + Set = case gb_trees:lookup(XsiOp,Maps) of + {value, V} -> + ?SETS:add_element(Key,V); + none -> + ?SETS:from_list([Key]) + end, + NewMaps = gb_trees:enter(XsiOp,Set,Maps), + NewUses = Uses; + #temp{} -> + Set = case gb_trees:lookup(XsiOp,Maps) of + {value, V} -> + ?SETS:add_element(Key,V); + none -> + ?SETS:from_list([Key]) + end, + NewMaps = gb_trees:enter(XsiOp,Set,Maps), + Pred = XsiOp#xsi_op.pred, + OOP = XsiOp#xsi_op.op, + SSet = case gb_trees:lookup(Pred,Uses) of + {value, VV} -> + ?SETS:add_element(OOP#temp.key,VV); + none -> + ?SETS:from_list([OOP#temp.key]) + end, + NewUses = gb_trees:enter(Pred,SSet,Uses); + #eop{} -> + Set = case gb_trees:lookup(XsiOp,Maps) of + {value, V} -> + ?SETS:add_element(Key,V); + none -> + ?SETS:from_list([Key]) + end, + NewMaps = gb_trees:enter(XsiOp,Set,Maps), + Pred = XsiOp#xsi_op.pred, + Op = XsiOp#xsi_op.op, + SSet = case gb_trees:lookup(Pred,Uses) of + {value, VV} -> + ?SETS:add_element(Op#eop.stopped_by,VV); + none -> + ?SETS:from_list([Op#eop.stopped_by]) + end, + NewUses = gb_trees:enter(Pred,SSet,Uses); + _-> + NewMaps = Maps, + NewUses = Uses + end, + add_map_and_uses(Ops, Key, NewMaps, NewUses). + +post_process([], _CFGGraph) -> ok; +post_process([E|Es], CFGGraph) -> + {Pred,Label} = E, + {_PP,Block} = ?GRAPH:vertex(CFGGraph,Label), + Att = Block#block.attributes, + Uses = Att#mp.uses, + SetToAdd = case gb_trees:lookup(Pred,Uses) of + {value, Set} -> + Set; + none -> + ?SETS:new() + end, + %% ?pp_debug("~nAdding an edge ~w -> ~w (~w)",[Pred,Label,SetToAdd]), + ?GRAPH:add_edge(CFGGraph, Pred, Label, SetToAdd), + post_process(Es, CFGGraph). + +factorize([], _CFGGraph) -> ok; +factorize([P|Ps], CFGGraph) -> + [OE|OEs] = ?GRAPH:out_edges(CFGGraph,P), + %% ?pp_debug("~nIn_degrees ~w : ~w",[P,?GRAPH:in_degree(CFGGraph,P)]), + [InEdge] = ?GRAPH:in_edges(CFGGraph,P), + {E,V1,V2,Label} = ?GRAPH:edge(CFGGraph,InEdge), + {_OEE,_OEV1,_OEV2,LOE} = ?GRAPH:edge(CFGGraph,OE), + List = shoot_info_upwards(OEs,LOE,CFGGraph), + NewLabel = ?SETS:union(Label,List), + ?GRAPH:add_edge(CFGGraph,E,V1,V2,NewLabel), + factorize(Ps, CFGGraph). + +shoot_info_upwards([], Acc, _CFGGraph) -> Acc; +shoot_info_upwards([E|Es], Acc, CFGGraph) -> + {_E,_V1,_V2,Set} = ?GRAPH:edge(CFGGraph,E), + NewAcc = ?SETS:intersection(Acc, Set), + case ?SETS:size(NewAcc) of + 0 -> NewAcc; + _ -> shoot_info_upwards(Es,NewAcc,CFGGraph) + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% DOWNSAFETY %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +perform_downsafety([], _G, _XsiG) -> + ok; +perform_downsafety([MP|MPs], G, XG) -> + {V,Block} = ?GRAPH:vertex(G, MP), + NDS = ?SETS:new(), + Att = Block#block.attributes, + Maps = Att#mp.maps, + Defs = Att#mp.defs, + OutEdges = ?GRAPH:out_edges(G, MP), + %% ?pp_debug("~n Inspection Maps : ~w",[Maps]), + NewNDS = parse_keys(gb_trees:keys(Maps),Maps,OutEdges,G,Defs,NDS,XG), + NewAtt = Att#mp{ndsSet = NewNDS}, + ?GRAPH:add_vertex(G, V, Block#block{attributes = NewAtt}), + ?pp_debug("~n Not Downsafe at L~w: ~w", [V, NewNDS]), + %%io:format(standard_io,"~n Not Downsafe at L~w: ~w",[V,NewNDS]), + perform_downsafety(MPs, G, XG). + +parse_keys([], _Maps, _OutEdges, _G, _Defs, NDS, _XsiG) -> + NDS; +parse_keys([M|Ms], Maps, OutEdges, G, Defs, NDS, XsiG) -> + KillerSet = gb_trees:get(M,Maps), + %% ?pp_debug("~n Inspection ~w -> ~w",[M,KillerSet]), + TempSet = ?SETS:intersection(KillerSet,Defs), + NewNDS = case ?SETS:size(TempSet) of + 0 -> getNDS(M,KillerSet,NDS,OutEdges,G,XsiG); + _ -> + %% One Xsi which has M as operand has killed it + %% M is then Downsafe + %% and is not added to the NotDownsafeSet (NDS) + NDS + end, + parse_keys(Ms, Maps, OutEdges, G, Defs, NewNDS, XsiG). + +getNDS(_M, _KillerSet, NDS, [], _G, _XsiG) -> + NDS; +getNDS(M, KillerSet, NDS, [E|Es], G, XsiG) -> + {_EE,_V1,_V2,Label} = ?GRAPH:edge(G, E), + Set = ?SETS:intersection(KillerSet, Label), + %% ?pp_debug("~n ######## Intersection between KillerSet: ~w and Label: ~w",[KillerSet,Label]), + %% ?pp_debug("~n ######## ~w",[Set]), + case ?SETS:size(Set) of + 0 -> + %% M is not downsafe + ?SETS:add_element(M, NDS); + _ -> + %% Try the other edges + getNDS(M, KillerSet, NDS, Es, G, XsiG) + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%% WILL BE AVAILABLE %%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +perform_will_be_available(XsiGraph,CFGGraph,Options) -> + Keys = ?GRAPH:vertices(XsiGraph), + ?pp_debug("~n############ Can Be Available ##########~n",[]), + ?option_time(perform_can_be_available(Keys,XsiGraph,CFGGraph),"RTL A-SSAPRE WillBeAvailable - Compute CanBeAvailable",Options), + ?pp_debug("~n############ Later ##########~n",[]), + ?option_time(perform_later(Keys,XsiGraph),"RTL A-SSAPRE WillBeAvailable - Compute Later",Options). + +perform_can_be_available([],_XsiGraph,_CFGGraph) -> ok; +perform_can_be_available([Key|Keys],XsiGraph,CFGGraph) -> + {V,Xsi} = ?GRAPH:vertex(XsiGraph,Key), + case Xsi#xsi.cba of + undefined -> + {_VV,Block} = ?GRAPH:vertex(CFGGraph,Xsi#xsi.label), + Att = Block#block.attributes, + NDS = Att#mp.ndsSet, + OpList = ?SETS:from_list(xsi_oplist(Xsi)), + Set = ?SETS:intersection(NDS,OpList), + case ?SETS:size(Set) of + 0 -> + ?GRAPH:add_vertex(XsiGraph, V, Xsi#xsi{cba = true}), + perform_can_be_available(Keys, XsiGraph, CFGGraph); + _ -> + LIST = [X || #temp{key=X} <- ?SETS:to_list(Set)], + case LIST of + [] -> + ?GRAPH:add_vertex(XsiGraph, V, Xsi#xsi{cba = false}), + ImmediateParents = ?GRAPH:in_neighbours(XsiGraph, Key), + propagate_cba(ImmediateParents,XsiGraph,Xsi#xsi.def,CFGGraph); + _ -> + ok + end, + perform_can_be_available(Keys, XsiGraph, CFGGraph) + end; + _ -> %% True or False => recurse + perform_can_be_available(Keys, XsiGraph, CFGGraph) + end. + +propagate_cba([],_XG,_Def,_CFGG) -> ok; +propagate_cba([IPX|IPXs],XsiGraph,XsiDef,CFGGraph) -> + {V,IPXsi} = ?GRAPH:vertex(XsiGraph,IPX), + {_VV,Block} = ?GRAPH:vertex(CFGGraph,IPXsi#xsi.label), + Att = Block#block.attributes, + NDS = Att#mp.ndsSet, + List = ?SETS:to_list(?SETS:intersection(NDS,?SETS:from_list(xsi_oplist(IPXsi)))), + case IPXsi#xsi.cba of + false -> ok; + _ -> + case lists:keymember(XsiDef, #xsi_op.op, List) of + true -> + ?GRAPH:add_vertex(XsiGraph, V, IPXsi#xsi{cba = false}), + ImmediateParents = ?GRAPH:in_neighbours(XsiGraph, IPX), + propagate_cba(ImmediateParents,XsiGraph,IPXsi#xsi.def,CFGGraph); + _ -> + ok + end + end, + propagate_cba(IPXs,XsiGraph,XsiDef,CFGGraph). + +perform_later([], _XsiGraph) -> ok; +perform_later([Key|Keys], XsiGraph) -> + {V, Xsi} = ?GRAPH:vertex(XsiGraph, Key), + %% ?pp_debug("~n DEBUG : inspecting later of ~w (~w)~n",[Key,Xsi#xsi.later]), + case Xsi#xsi.later of + undefined -> + OpList = xsi_oplist(Xsi), + case parse_ops(OpList,fangpi) of %% It means "fart" in chinese :D + has_temp -> + perform_later(Keys,XsiGraph); + has_real -> + case Xsi#xsi.cba of + true -> + ?GRAPH:add_vertex(XsiGraph,V,Xsi#xsi{later=false,wba=true}); + undefined -> + ?GRAPH:add_vertex(XsiGraph,V,Xsi#xsi{later=false,wba=true}); + _ -> + ?GRAPH:add_vertex(XsiGraph,V,Xsi#xsi{later=false,wba=false}) + end, + AllParents = digraph_utils:reaching([Key], XsiGraph), + ?pp_debug("~nPropagating to all parents of t~w: ~w",[Key,AllParents]), + propagate_later(AllParents,XsiGraph), + perform_later(Keys,XsiGraph); + _ -> %% Just contains bottoms and/or expressions + ?GRAPH:add_vertex(XsiGraph,V,Xsi#xsi{later=true}), + perform_later(Keys,XsiGraph) + end; + _ -> %% True or False => recurse + perform_later(Keys,XsiGraph) + end. + +propagate_later([], _XG) -> ok; +propagate_later([IPX|IPXs], XsiGraph) -> + {V,IPXsi} = ?GRAPH:vertex(XsiGraph,IPX), + case IPXsi#xsi.later of + false -> + ?pp_debug("~nThrough propagation, later of t~w is already reset",[IPX]), + propagate_later(IPXs,XsiGraph); + _ -> + ?pp_debug("~nThrough propagation, resetting later of t~w",[IPX]), + case IPXsi#xsi.cba of + true -> + ?GRAPH:add_vertex(XsiGraph,V,IPXsi#xsi{later=false,wba=true}); + undefined -> + ?GRAPH:add_vertex(XsiGraph,V,IPXsi#xsi{later=false,wba=true}); + _ -> + ?GRAPH:add_vertex(XsiGraph,V,IPXsi#xsi{later=false,wba=false}) + end, + propagate_later(IPXs,XsiGraph) + end. + +parse_ops([], Res) -> + Res; +parse_ops([Op|Ops], Res) -> + case Op#xsi_op.op of + #temp{} -> + NewRes = has_temp, + parse_ops(Ops,NewRes); + #bottom{} -> + parse_ops(Ops,Res); + #eop{} -> + parse_ops(Ops,Res); + _ -> + has_real + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% CODE MOTION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +perform_code_motion([], Cfg, _XsiG) -> + Cfg; +perform_code_motion([L|Labels], Cfg, XsiG) -> + Code=?BB:code(?CFG:bb(Cfg,L)), + ?pp_debug("~n################ Code Motion in L~w~n",[L]), + ?pp_debug("~nCode to move ~n",[]), + pp_instrs(Code,XsiG), + NewCfg = code_motion_in_block(L,Code,Cfg,XsiG,[],gb_trees:empty()), + ?pp_debug("~n################ Code Motion successful in L~w~n",[L]), + perform_code_motion(Labels,NewCfg,XsiG). + +code_motion_in_block(Label,[],Cfg,_XsiG,Visited,InsertionsAcc) -> + InsertionsAlong = gb_trees:keys(InsertionsAcc), + Code = lists:reverse(Visited), + NewBB = ?BB:mk_bb(Code), + Cfg2 = ?CFG:bb_add(Cfg,Label,NewBB), + %% Must come after the bb_add, since redirect will update the Phis too... + Cfg3 = make_insertions(Label,InsertionsAlong,InsertionsAcc,Cfg2), + %% ?pp_debug("~nChecking the Code at L~w:~n~p",[Label,?BB:code(?CFG:bb(Cfg3,Label))]), + Cfg3; +code_motion_in_block(L,[Inst|Insts],Cfg,XsiG,Visited,InsertionsAcc) -> + ?pp_debug("~nInspecting Inst : ~n",[]),pp_instr(Inst,XsiG), + case Inst of + #pre_candidate{} -> + Def = Inst#pre_candidate.def, + Alu = Inst#pre_candidate.alu, + case Def of + bottom -> + InstToAdd = Alu; + #temp{} -> + Key = Def#temp.key, + {_V,Xsi} = ?GRAPH:vertex(XsiG,Key), + case Xsi#xsi.wba of + true -> + %% Turn into a move + Dst = ?RTL:alu_dst(Alu), + Move = ?RTL:mk_move(Dst,Def#temp.var), + pp_instr(Inst#pre_candidate.alu,nil), ?pp_debug(" ==> ",[]), pp_instr(Move,nil), + %% Counting redundancies + redundancy_add(), + InstToAdd = Move; + _ -> + InstToAdd = Alu + end; + _ -> %% Def is a real variable + %% Turn into a move + Dst = ?RTL:alu_dst(Alu), + Move = ?RTL:mk_move(Dst,Def), + pp_instr(Alu,nil), ?pp_debug(" ==> ",[]), pp_instr(Move,nil), + %% Counting redundancies + redundancy_add(), + InstToAdd = Move + end, + code_motion_in_block(L,Insts,Cfg,XsiG,[InstToAdd|Visited],InsertionsAcc); + #xsi_link{} -> + Key = Inst#xsi_link.num, + {_V,Xsi} = ?GRAPH:vertex(XsiG,Key), + case Xsi#xsi.wba of + true -> + %% Xsi is a WBA, it might trigger insertions + OpList = xsi_oplist(Xsi), + ?pp_debug(" This Xsi is a 'Will be available'",[]), + %% Cleaning the instruction + Expr = prepare_inst(Xsi#xsi.inst), + {NewOpList,NewInsertionsAcc} = get_insertions(OpList,[],InsertionsAcc,Visited,Expr,XsiG), + %% Making Xsi a Phi with Oplist + PhiOpList = [{Pred,Var} || #xsi_op{pred=Pred,op=Var} <- NewOpList], + Def = Xsi#xsi.def, + Phi = ?RTL:phi_arglist_update(?RTL:mk_phi(Def#temp.var),PhiOpList), + ?pp_debug("~n Xsi is turned into Phi : ~w",[Phi]), + code_motion_in_block(L,Insts,Cfg,XsiG,[Phi|Visited],NewInsertionsAcc); + _ -> + ?pp_debug(" This Xsi is not a 'Will be available'",[]), + code_motion_in_block(L,Insts,Cfg,XsiG,Visited,InsertionsAcc) + end; +%% phi -> +%% code_motion_in_block(L,Insts,Cfg,XsiG,[Inst|Visited],InsertionsAcc); + _ -> + %% Other instructions.... Phis too + code_motion_in_block(L,Insts,Cfg,XsiG,[Inst|Visited],InsertionsAcc) + end. + +prepare_inst(Expr) -> + S1 = ?RTL:alu_src1(Expr), + S2 = ?RTL:alu_src2(Expr), + NewInst = case S1 of + #temp{} -> ?RTL:alu_src1_update(Expr,S1#temp.var); + _ -> Expr + end, + case S2 of + #temp{} -> ?RTL:alu_src2_update(NewInst,S2#temp.var); + _ -> NewInst + end. + +get_insertions([],OpAcc,InsertionsAcc,_Visited,_Expr,_XsiG) -> + {OpAcc,InsertionsAcc}; +get_insertions([XsiOp|Ops],OpAcc,InsertionsAcc,Visited,Expr,XsiG) -> + Pred = XsiOp#xsi_op.pred, + Op = XsiOp#xsi_op.op, + case Op of + #bottom{} -> + case gb_trees:lookup(Pred,InsertionsAcc) of + {value,Insertion} -> + From = Insertion#insertion.from, + case lists:keyfind(Op, 1, From) of + false -> + ?pp_debug("~nThere has been insertions along the edge L~w already, but not for that operand | Op=",[Pred]),pp_arg(Op), + Dst = Op#bottom.var, + Expr2 = ?RTL:alu_dst_update(Expr,Dst), + Inst = manufacture_computation(Pred,Expr2,Visited), + Code = Insertion#insertion.code, + NewInsertion = Insertion#insertion{from=[{Op,Dst}|From],code=[Inst|Code]}, + NewInsertionsAcc = gb_trees:update(Pred,NewInsertion,InsertionsAcc); + {_, Val} -> + ?pp_debug("~nThere has been insertions along the edge L~w already, and for that operand too | Op=",[Pred]),pp_arg(Op), + Dst = Val, + NewInsertionsAcc = InsertionsAcc + end; + none -> + ?pp_debug("~nThere has been no insertion along the edge L~w, (and not for that operand, of course)| Op=",[Pred]),pp_arg(Op), + Dst = Op#bottom.var, + Expr2 = ?RTL:alu_dst_update(Expr,Dst), + Inst = manufacture_computation(Pred,Expr2,Visited), + NewInsertion = #insertion{from=[{Op,Dst}],code=[Inst]}, + NewInsertionsAcc = gb_trees:insert(Pred,NewInsertion,InsertionsAcc) + end; + #const_expr{} -> + case gb_trees:lookup(Pred,InsertionsAcc) of + {value,Insertion} -> + From = Insertion#insertion.from, + case lists:keyfind(Op, 1, From) of + false -> + ?pp_debug("~nThere have been insertions along the edge L~w already, but not for that operand | Op=",[Pred]),pp_arg(Op), + Dst = Op#const_expr.var, + Val = Op#const_expr.value, + Inst = ?RTL:mk_move(Dst,Val), + Code = Insertion#insertion.code, + NewInsertion = Insertion#insertion{from=[{Op,Dst}|From],code=[Inst|Code]}, + NewInsertionsAcc = gb_trees:update(Pred,NewInsertion,InsertionsAcc); + {_, Val} -> + ?pp_debug("~nThere have been insertions along the edge L~w already, and for that operand too | Op=",[Pred]),pp_arg(Op), + Dst = Val, + NewInsertionsAcc = InsertionsAcc + end; + none -> + ?pp_debug("~nThere has been no insertion along the edge L~w, (and not for that operand, of course)| Op=",[Pred]),pp_arg(Op), + Dst = Op#const_expr.var, + Val = Op#const_expr.value, + Inst = ?RTL:mk_move(Dst,Val), + NewInsertion = #insertion{from=[{Op,Dst}],code=[Inst]}, + NewInsertionsAcc = gb_trees:insert(Pred,NewInsertion,InsertionsAcc) + end; + #eop{} -> + %% We treat expressions like bottoms + %% The value must be recomputed, and therefore not available... + case gb_trees:lookup(Pred,InsertionsAcc) of + {value,Insertion} -> + From = Insertion#insertion.from, + case lists:keyfind(Op, 1, From) of + false -> + ?pp_debug("~nThere has been insertions along the edge L~w already, but not for that operand | Op=",[Pred]),pp_arg(Op), + Dst = Op#eop.var, + Expr2 = ?RTL:alu_dst_update(Expr,Dst), + Inst = manufacture_computation(Pred,Expr2,Visited), + Code = Insertion#insertion.code, + NewInsertion = Insertion#insertion{from=[{Op,Dst}|From],code=[Inst|Code]}, + NewInsertionsAcc = gb_trees:update(Pred,NewInsertion,InsertionsAcc); + {_, Val} -> + ?pp_debug("~nThere has been insertions along the edge L~w already, and for that operand too | Op=",[Pred]),pp_arg(Op), + Dst = Val, + NewInsertionsAcc = InsertionsAcc + end; + none -> + ?pp_debug("~nThere has been no insertion along the edge L~w, (and not for that operand, of course)| Op=",[Pred]),pp_arg(Op), + Dst = Op#eop.var, + Expr2 = ?RTL:alu_dst_update(Expr,Dst), + Inst = manufacture_computation(Pred,Expr2,Visited), + NewInsertion = #insertion{from=[{Op,Dst}],code=[Inst]}, + NewInsertionsAcc = gb_trees:insert(Pred,NewInsertion,InsertionsAcc) + end; + #temp{} -> + case gb_trees:lookup(Pred,InsertionsAcc) of + {value,Insertion} -> + From = Insertion#insertion.from, + case lists:keyfind(Op, 1, From) of + false -> + ?pp_debug("~nThere has been insertions along the edge L~w already, but not for that operand | Op=",[Pred]),pp_arg(Op), + Key = Op#temp.key, + {_V,Xsi} = ?GRAPH:vertex(XsiG,Key), + case Xsi#xsi.wba of + true -> + ?pp_debug("~nBut the operand is a WBA Xsi: no need for insertion",[]), + Dst = Op#temp.var, + NewInsertionsAcc = InsertionsAcc; + _ -> + ?pp_debug("~nBut the operand is a NOT WBA Xsi: we must make an insertion",[]), + Dst = ?RTL:mk_new_var(), + Expr2 = ?RTL:alu_dst_update(Expr,Dst), + Inst = manufacture_computation(Pred,Expr2,Visited), + Code = Insertion#insertion.code, + NewInsertion = Insertion#insertion{from=[{Op,Dst}|From],code=[Inst|Code]}, + NewInsertionsAcc = gb_trees:update(Pred,NewInsertion,InsertionsAcc) + end; + {_, Val} -> + ?pp_debug("~nThere has been insertions along the edge L~w already, and for that operand too (Op=~w)",[Pred,Op]), + ?pp_debug("~nThis means, this temp is a WBA Xsi's definition",[]), + Dst = Val, + NewInsertionsAcc = InsertionsAcc + end; + none -> + ?pp_debug("~nThere has been no insertion along the edge L~w, (and not for that operand, of course | Op=",[Pred]),pp_arg(Op), + Key = Op#temp.key, + {_V,Xsi} = ?GRAPH:vertex(XsiG,Key), + case Xsi#xsi.wba of + true -> + ?pp_debug("~nBut the operand is a WBA Xsi: no need for insertion",[]), + Dst = Op#temp.var, + NewInsertionsAcc = InsertionsAcc; + _ -> + ?pp_debug("~nBut the operand is a NOT WBA Xsi: we must make an insertion",[]), + Dst = ?RTL:mk_new_var(), + Expr2 = ?RTL:alu_dst_update(Expr,Dst), + Inst = manufacture_computation(Pred,Expr2,Visited), + NewInsertion = #insertion{from=[{Op,Dst}],code=[Inst]}, + NewInsertionsAcc = gb_trees:insert(Pred,NewInsertion,InsertionsAcc) + end + end; + _ -> + ?pp_debug("~nThe operand (Op=",[]),pp_arg(Op),?pp_debug(") is a real variable, no need for insertion along L~w",[Pred]), + Dst = Op, + NewInsertionsAcc = InsertionsAcc + end, + NewXsiOp = XsiOp#xsi_op{op=Dst}, + get_insertions(Ops, [NewXsiOp|OpAcc], NewInsertionsAcc, Visited, Expr, XsiG). + +manufacture_computation(_Pred, Expr, []) -> + ?pp_debug("~n Manufactured computation : ~w", [Expr]), + Expr; +manufacture_computation(Pred, Expr, [I|Rest]) -> + %% ?pp_debug("~n Expr = ~w",[Expr]), + SRC1 = ?RTL:alu_src1(Expr), + SRC2 = ?RTL:alu_src2(Expr), + case I of + #xsi_link{} -> + exit({?MODULE,should_not_be_a_xsi_link,{"Why the hell do we still have a xsi link???",I}}); + #xsi{} -> + exit({?MODULE,should_not_be_a_xsi,{"Why the hell do we still have a xsi ???",I}}); + #phi{} -> + DST = ?RTL:phi_dst(I), + Arg = ?RTL:phi_arg(I,Pred), + NewInst = case DST =:= SRC1 of + true -> ?RTL:alu_src1_update(Expr,Arg); + false -> Expr + end, + NewExpr = case DST =:= SRC2 of + true -> ?RTL:alu_src2_update(NewInst,Arg); + false -> NewInst + end, + manufacture_computation(Pred,NewExpr,Rest) + end. + +make_insertions(_L, [], _ITree, Cfg) -> + Cfg; +make_insertions(L, [OldPred|Is], ITree, Cfg) -> + NewPred = ?RTL:label_name(?RTL:mk_new_label()), + I = gb_trees:get(OldPred, ITree), + CodeToInsert = lists:reverse([?RTL:mk_goto(L)|I#insertion.code]), + BBToInsert = ?BB:mk_bb(CodeToInsert), + NewCfg = ?CFG:bb_insert_between(Cfg, NewPred, BBToInsert, OldPred, L), + make_insertions(L, Is, ITree, NewCfg). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%% XSI INTERFACE %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +xsi_oplist(#xsi{opList=OpList}) -> + case OpList of undefined -> [] ; _ -> OpList end. +xsi_arg(Xsi, Pred) -> + case lists:keyfind(Pred, #xsi_op.pred, xsi_oplist(Xsi)) of + false -> + undetermined_operand; + R -> + R#xsi_op.op + end. +xsi_arg_update(Xsi, Pred, Op) -> + NewOpList = lists:keyreplace(Pred, #xsi_op.pred, xsi_oplist(Xsi), + #xsi_op{pred=Pred,op=Op}), + Xsi#xsi{opList=NewOpList}. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%% PRETTY-PRINTING %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-ifndef(SSAPRE_DEBUG). + +%%pp_cfg(Cfg,_) -> ?CFG:pp(Cfg). +pp_cfg(_,_) -> ok. +pp_instr(_,_) -> ok. +pp_instrs(_,_) -> ok. +pp_expr(_) -> ok. +pp_xsi(_) -> ok. +pp_arg(_) -> ok. +pp_xsigraph(_) -> ok. +pp_cfggraph(_) -> ok. +%% pp_xsigraph(G) -> +%% Vertices = lists:sort(?GRAPH:vertices(G)), +%% io:format(standard_io, "Size of the Xsi Graph: ~w", [length(Vertices)]). +%% pp_cfggraph(G) -> +%% Vertices = lists:sort(?GRAPH:vertices(G)), +%% io:format(standard_io, "Size of the CFG Graph: ~w", [length(Vertices)]). + +-else. + +pp_cfg(Cfg, Graph) -> + Labels = ?CFG:preorder(Cfg), + pp_blocks(Labels, Cfg, Graph). + +pp_blocks([], _, _) -> + ok; +pp_blocks([L|Ls], Cfg, Graph) -> + Code = hipe_bb:code(?CFG:bb(Cfg,L)), + io:format(standard_io,"~n########## Label L~w~n", [L]), + pp_instrs(Code, Graph), + pp_blocks(Ls, Cfg, Graph). + +pp_instrs([], _) -> + ok; +pp_instrs([I|Is], Graph) -> + pp_instr(I, Graph), + pp_instrs(Is, Graph). + +pp_xsi_link(Key, Graph) -> + {_Key,Xsi} = ?GRAPH:vertex(Graph, Key), + pp_xsi(Xsi). + +pp_xsi(Xsi) -> + io:format(standard_io, " [L~w] ", [Xsi#xsi.label]), + io:format(standard_io, "[", []), pp_expr(Xsi#xsi.inst), + io:format(standard_io, "] Xsi(", []), pp_xsi_args(xsi_oplist(Xsi)), + io:format(standard_io, ") (", []), pp_xsi_def(Xsi#xsi.def), + io:format(standard_io, ") cba=~w, later=~w | wba=~w~n", [Xsi#xsi.cba,Xsi#xsi.later,Xsi#xsi.wba]). + +pp_instr(I, Graph) -> + case I of + #alu{} -> + io:format(standard_io, " ", []), + pp_arg(?RTL:alu_dst(I)), + io:format(standard_io, " <- ", []), + pp_expr(I), + io:format(standard_io, "~n", []); + _ -> + try ?RTL:pp_instr(standard_io, I) + catch _:_ -> + case I of + #pre_candidate{} -> + pp_pre(I); + #xsi{} -> + pp_xsi(I); + #xsi_link{} -> + pp_xsi_link(I#xsi_link.num, Graph); + _-> + io:format(standard_io,"*** ~w ***~n", [I]) + end + end + end. + +pp_pre(I) -> + A = I#pre_candidate.alu, + io:format(standard_io, " ", []), + pp_arg(?RTL:alu_dst(A)), + io:format(standard_io, " <- ", []),pp_expr(A), + io:format(standard_io, " [ ", []),pp_arg(I#pre_candidate.def), + %%io:format(standard_io, "~w", [I#pre_candidate.def]), + io:format(standard_io, " ]~n",[]). + +pp_expr(I) -> + pp_arg(?RTL:alu_dst(I)), + io:format(standard_io, " <- ", []), + pp_arg(?RTL:alu_src1(I)), + io:format(standard_io, " ~w ", [?RTL:alu_op(I)]), + pp_arg(?RTL:alu_src2(I)). + +pp_arg(Arg) -> + case Arg of + bottom -> + io:format(standard_io, "_|_", []); + #bottom{} -> + io:format(standard_io, "_|_:~w (", [Arg#bottom.key]),pp_arg(Arg#bottom.var),io:format(standard_io,")",[]); + #temp{} -> + pp_xsi_def(Arg); + #eop{} -> + io:format(standard_io,"#",[]),pp_expr(Arg#eop.expr),io:format(standard_io,"(",[]),pp_arg(Arg#eop.var),io:format(standard_io,")#",[]); + #const_expr{} -> + io:format(standard_io,"*",[]),pp_arg(Arg#const_expr.var),io:format(standard_io," -> ",[]),pp_arg(Arg#const_expr.value),io:format(standard_io,"*",[]); + undefined -> + io:format(standard_io, "...", []); %%"undefined", []); + _-> + case Arg of + #alu{} -> + pp_expr(Arg); + _-> + ?RTL:pp_arg(standard_io, Arg) + end + end. + +pp_args([]) -> + ok; +pp_args(undefined) -> + io:format(standard_io, "...,...,...", []); +pp_args([A]) -> + pp_arg(A); +pp_args([A|As]) -> + pp_arg(A), + io:format(standard_io, ", ", []), + pp_args(As). + +pp_xsi_args([]) -> ok; +pp_xsi_args([XsiOp]) -> + io:format(standard_io, "{~w| ", [XsiOp#xsi_op.pred]), + pp_arg(XsiOp#xsi_op.op), + io:format(standard_io, "}", []); +pp_xsi_args([XsiOp|Args]) -> + io:format(standard_io, "{~w| ", [XsiOp#xsi_op.pred]), + pp_arg(XsiOp#xsi_op.op), + io:format(standard_io, "}, ", []), + pp_xsi_args(Args); +pp_xsi_args(Args) -> + pp_args(Args). + +pp_xsi_def(Arg) -> + D = Arg#temp.key, + V = Arg#temp.var, + io:format(standard_io, "t~w (", [D]),pp_arg(V),io:format(standard_io,")",[]). + +pp_cfggraph(G) -> + Vertices = lists:sort(?GRAPH:vertices(G)), + io:format(standard_io, "Size of the CFG Graph: ~w ~n", [length(Vertices)]), + pp_cfgvertex(Vertices, G). + +pp_xsigraph(G) -> + Vertices = lists:sort(?GRAPH:vertices(G)), + io:format(standard_io, "Size of the Xsi Graph: ~w ~n", [length(Vertices)]), + pp_xsivertex(Vertices,G). + +pp_xsivertex([], _G) -> + ok; +pp_xsivertex([Key|Keys], G) -> + {V,Xsi} = ?GRAPH:vertex(G, Key), + OutNeighbours = ?GRAPH:out_neighbours(G, V), + ?pp_debug(" ~w -> ~w", [V,OutNeighbours]), pp_xsi(Xsi), + pp_xsivertex(Keys, G). + +pp_cfgvertex([], _G) -> + ok; +pp_cfgvertex([Key|Keys], G) -> + {V,Block} = ?GRAPH:vertex(G,Key), + case Block#block.type of + mp -> + ?pp_debug("~n Block ~w's attributes: ~n", [V]), + pp_attributes(Block), + ?pp_debug("~n Block ~w's edges: ~n", [V]), + pp_edges(G, ?GRAPH:in_edges(G,Key), ?GRAPH:out_edges(G,Key)); + _-> + ok + end, + pp_cfgvertex(Keys, G). + +pp_attributes(Block) -> + Att = Block#block.attributes, + case Att of + undefined -> + ok; + _ -> + ?pp_debug(" Maps: ~n",[]),pp_maps(gb_trees:keys(Att#mp.maps),Att#mp.maps), + ?pp_debug(" Uses: ~n",[]),pp_uses(gb_trees:keys(Att#mp.uses),Att#mp.uses), + ?pp_debug(" Defs: ~w~n",[Att#mp.defs]), + ?pp_debug(" Xsis: ~w~n",[Att#mp.xsis]), + ?pp_debug(" NDS : ",[]),pp_nds(?SETS:to_list(Att#mp.ndsSet)) + end. + +pp_maps([], _Maps) -> ok; +pp_maps([K|Ks], Maps) -> + ?pp_debug(" ",[]),pp_arg(K#xsi_op.op),?pp_debug("-> ~w~n",[?SETS:to_list(gb_trees:get(K,Maps))]), + pp_maps(Ks, Maps). + +pp_uses([], _Maps) -> ok; +pp_uses([K|Ks], Maps) -> + ?pp_debug(" ~w -> ~w~n",[K,?SETS:to_list(gb_trees:get(K,Maps))]), + pp_uses(Ks, Maps). + +pp_nds([]) -> ?pp_debug("~n",[]); +pp_nds(undefined) -> ?pp_debug("None",[]); +pp_nds([K]) -> + pp_arg(K#xsi_op.op), ?pp_debug("~n",[]); +pp_nds([K|Ks]) -> + pp_arg(K#xsi_op.op), ?pp_debug(", ",[]), + pp_nds(Ks). + +pp_edges(_G, [], []) -> ok; +pp_edges(G, [], [OUT|OUTs]) -> + {_E,V1,V2,Label} = ?GRAPH:edge(G,OUT), + ?pp_debug(" Out edge ~w -> ~w (~w)~n", [V1,V2,?SETS:to_list(Label)]), + pp_edges(G, [], OUTs); +pp_edges(G, [IN|INs], Outs) -> + {_E,V1,V2,Label} = ?GRAPH:edge(G,IN), + ?pp_debug(" In edge ~w -> ~w (~w)~n", [V1,V2,?SETS:to_list(Label)]), + pp_edges(G, INs, Outs). + +-endif. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% COUNTERS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +init_counters() -> + put({ssapre_temp,temp_count}, 0), + put({ssapre_index,index_count}, 0). + +new_bottom() -> + IndxCountPair = {ssapre_index, index_count}, + V = get(IndxCountPair), + put(IndxCountPair, V+1), + #bottom{key = V, var = ?RTL:mk_new_var()}. + +new_temp() -> + TmpCountPair = {ssapre_temp, temp_count}, + V = get(TmpCountPair), + put(TmpCountPair, V+1), + #temp{key = V, var = ?RTL:mk_new_var()}. + +init_redundancy_count() -> + put({ssapre_redundancy,redundancy_count}, 0). + +redundancy_add() -> + RedCountPair = {ssapre_redundancy, redundancy_count}, + V = get(RedCountPair), + put(RedCountPair, V+1). + +-ifdef(SSAPRE_DEBUG). +get_redundancy_count() -> + get({ssapre_redundancy,redundancy_count}). +-endif. diff --git a/lib/hipe/rtl/hipe_rtl_symbolic.erl b/lib/hipe/rtl/hipe_rtl_symbolic.erl new file mode 100644 index 0000000000..bc8640dec9 --- /dev/null +++ b/lib/hipe/rtl/hipe_rtl_symbolic.erl @@ -0,0 +1,99 @@ +%% -*- erlang-indent-level: 2 -*- +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2004-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% +%% +%%------------------------------------------------------------------- +%% File : hipe_rtl_symbolic.erl +%% Author : Per Gustafsson <[email protected]> +%% Description : Expansion of symbolic instructions. +%% +%% Created : 18 May 2004 by Per Gustafsson <[email protected]> +%%------------------------------------------------------------------- + +-module(hipe_rtl_symbolic). + +-export([expand/1]). + +-include("hipe_rtl.hrl"). +-include("hipe_literals.hrl"). +-include("../icode/hipe_icode_primops.hrl"). + +expand(Cfg) -> + Linear = hipe_rtl_cfg:linearize(Cfg), + Code = hipe_rtl:rtl_code(Linear), + NonFlatCode = [expand_instr(Instr) || Instr <- Code], + NewCode = lists:flatten(NonFlatCode), + Linear1 = hipe_rtl:rtl_code_update(Linear, NewCode), + hipe_rtl_cfg:init(Linear1). + +expand_instr(Instr) -> + case Instr of + #fixnumop{} -> + expand_fixnumop(Instr); + #gctest{} -> + expand_gctest(Instr); + _ -> + Instr + end. + +expand_fixnumop(Instr) -> + case hipe_rtl:fixnumop_type(Instr) of + untag -> + Dst = hipe_rtl:fixnumop_dst(Instr), + Src = hipe_rtl:fixnumop_src(Instr), + hipe_tagscheme:realuntag_fixnum(Dst, Src); + tag -> + Dst = hipe_rtl:fixnumop_dst(Instr), + Src = hipe_rtl:fixnumop_src(Instr), + hipe_tagscheme:realtag_fixnum(Dst, Src) + end. + +expand_gctest(Instr) -> + HeapNeed = hipe_rtl:gctest_words(Instr), + {GetHPInsn, HP, _PutHPInsn} = hipe_rtl_arch:heap_pointer(), + {GetHLIMITInsn, H_LIMIT} = hipe_rtl_arch:heap_limit(), + ContLabel = hipe_rtl:mk_new_label(), + GCLabel = hipe_rtl:mk_new_label(), + ContLabelName = hipe_rtl:label_name(ContLabel), + GCLabelName = hipe_rtl:label_name(GCLabel), + Tmp = hipe_rtl:mk_new_reg(), % diff between two gc-unsafe pointers + StartCode = + [GetHPInsn, + GetHLIMITInsn, + hipe_rtl:mk_alu(Tmp, H_LIMIT, 'sub', HP)], + {SeparateCode, GCAmount, HPAmount} = + case hipe_rtl:is_reg(HeapNeed) of + true -> + GA = hipe_rtl:mk_new_reg_gcsafe(), + HA = hipe_rtl:mk_new_reg_gcsafe(), + {[hipe_rtl:mk_alu(HA, HeapNeed, sll, + hipe_rtl:mk_imm(hipe_rtl_arch:log2_word_size()))| + hipe_tagscheme:realtag_fixnum(GA, HeapNeed)], GA, HA}; + false -> + WordsNeeded = hipe_rtl:imm_value(HeapNeed), + GA = hipe_rtl:mk_imm(hipe_tagscheme:mk_fixnum(WordsNeeded)), + HA = hipe_rtl:mk_imm(WordsNeeded*hipe_rtl_arch:word_size()), + {[], GA, HA} + end, + EndCode = + [hipe_rtl:mk_branch(Tmp, 'lt', HPAmount, GCLabelName, ContLabelName, 0.01), + GCLabel, + hipe_rtl:mk_call([], 'gc_1', [GCAmount], ContLabelName, [], not_remote), + ContLabel], + StartCode ++ SeparateCode ++ EndCode. + diff --git a/lib/hipe/rtl/hipe_rtl_varmap.erl b/lib/hipe/rtl/hipe_rtl_varmap.erl new file mode 100644 index 0000000000..9bd5e88611 --- /dev/null +++ b/lib/hipe/rtl/hipe_rtl_varmap.erl @@ -0,0 +1,161 @@ +%% -*- erlang-indent-level: 2 -*- +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2001-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% +%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Copyright (c) 2001 by Erik Johansson. All Rights Reserved +%% Time-stamp: <2008-04-20 14:55:35 richard> +%% ==================================================================== +%% Module : hipe_rtl_varmap +%% Purpose : +%% Notes : +%% History : * 2001-04-10 Erik Johansson ([email protected]): Created. +%% ==================================================================== +%% Exports : +%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-module(hipe_rtl_varmap). + +-export([init/1, + ivs2rvs/2, + icode_var2rtl_var/2, + icode_label2rtl_label/2]). + +%------------------------------------------------------------------------- + +-include("../main/hipe.hrl"). +-include("../icode/hipe_icode.hrl"). + +%------------------------------------------------------------------------- + +%% @spec init(IcodeRecord::#icode{}) -> {Args, VarMap} +%% +%% @doc Initializes gensym for RTL. + +-spec init(#icode{}) -> {[_], _}. % XXX: fix me please + +init(IcodeRecord) -> + hipe_gensym:init(rtl), + hipe_gensym:set_var(rtl, hipe_rtl_arch:first_virtual_reg()), + hipe_gensym:set_label(rtl, 0), + VarMap = new_var_map(), + {_Args, _VarMap1} = ivs2rvs(hipe_icode:icode_params(IcodeRecord), VarMap). + + +%%------------------------------------------------------------------------ +%% +%% Mapping of labels and variables from Icode to RTL. +%% +%%------------------------------------------------------------------------ + + +%% @spec icode_label2rtl_label(Icode_Label::term(), LabelMap::term()) -> +%% {RTL_Label, NewLabelMap} +%% +%% @doc Converts an Icode label to an RTL label. + +icode_label2rtl_label(LabelName, Map) -> + case lookup(LabelName, Map) of + {value, NewLabel} -> + {NewLabel, Map}; + none -> + NewLabel = hipe_rtl:mk_new_label(), + {NewLabel, insert(LabelName, NewLabel, Map)} + end. + + +%% @spec ivs2rvs(Icode_Vars::[term()], VarMap::term()) -> {[RTL_Var],NewVarMap} +%% +%% @doc Converts a list of Icode variables to a list of RTL variables. + +ivs2rvs([], VarMap) -> + {[], VarMap}; +ivs2rvs([V|Vs], VarMap) -> + {NewV, VarMap0} = icode_var2rtl_var(V, VarMap), + {NewVs, VarMap1} = ivs2rvs(Vs, VarMap0), + {[NewV|NewVs], VarMap1}. + + +%% @spec icode_var2rtl_var(Icode_Var::term(), VarMap::term()) -> +%% {RTL_Var, NewVarMap} +%% +%% @doc Converts an Icode variable to an RTL variable. + +icode_var2rtl_var(Var, Map) -> + Value = lookup(Var, Map), + case Value of + none -> + case type_of_var(Var) of + fvar -> + NewVar = hipe_rtl:mk_new_fpreg(), + {NewVar, insert(Var, NewVar, Map)}; + var -> + NewVar = hipe_rtl:mk_new_var(), + {NewVar, insert(Var, NewVar, Map)}; + {reg, IsGcSafe} -> + NewVar = + case IsGcSafe of + %% true -> hipe_rtl:mk_new_reg_gcsafe(); + false -> hipe_rtl:mk_new_reg() + end, + {NewVar, insert(Var, NewVar, Map)} + end; + {value, NewVar} -> + {NewVar, Map} + end. + +%% +%% Simple type test +%% + +type_of_var(X) -> + case hipe_icode:is_fvar(X) of + true -> + fvar; + false -> + case hipe_icode:is_var(X) of + true -> + var; + false -> + case hipe_icode:is_reg(X) of + true -> + {reg, hipe_icode:reg_is_gcsafe(X)}; + false -> + %% Sanity check + case hipe_icode:is_const(X) of + true -> const; + false -> + exit({"Unknown Icode variable", X}) + end + end + end + end. + +%% +%% Helping utilities +%% + +new_var_map() -> + gb_trees:empty(). + +lookup(V, Map) -> + gb_trees:lookup(V, Map). + +insert(Key, Val, Map) -> + gb_trees:insert(Key, Val, Map). diff --git a/lib/hipe/rtl/hipe_tagscheme.erl b/lib/hipe/rtl/hipe_tagscheme.erl new file mode 100644 index 0000000000..dc44b803a1 --- /dev/null +++ b/lib/hipe/rtl/hipe_tagscheme.erl @@ -0,0 +1,1209 @@ +%% -*- erlang-indent-level: 2 -*- +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2001-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% +%% +%%======================================================================== +%% +%% Filename : hipe_tagscheme.erl +%% Note : This is specific to Erlang 5.* (i.e. R9 to R13). +%% +%% Modifications: +%% 020904: Happi - added support for external pids and ports. +%% +%%======================================================================== +%% $Id$ +%%======================================================================== + +-module(hipe_tagscheme). + +-export([mk_nil/0, mk_fixnum/1, mk_arityval/1, mk_non_value/0]). +-export([is_fixnum/1]). +-export([tag_tuple/2, tag_cons/2]). +-export([test_is_boxed/4, get_header/2]). +-export([test_nil/4, test_cons/4, test_flonum/4, test_fixnum/4, + test_tuple/4, test_atom/4, test_bignum/4, test_pos_bignum/4, + test_any_pid/4, test_any_port/4, + test_ref/4, test_fun/4, test_fun2/5, test_matchstate/4, + test_binary/4, test_bitstr/4, test_list/4, + test_integer/4, test_number/4, test_constant/4, test_tuple_N/5]). +-export([realtag_fixnum/2, tag_fixnum/2, realuntag_fixnum/2, untag_fixnum/2]). +-export([test_two_fixnums/3, test_fixnums/4, unsafe_fixnum_add/3, + unsafe_fixnum_sub/3, + fixnum_gt/5, fixnum_lt/5, fixnum_ge/5, fixnum_le/5, fixnum_val/1, + fixnum_mul/4, + fixnum_addsub/5, fixnum_andorxor/4, fixnum_not/2, + fixnum_bsr/3, fixnum_bsl/3]). +-export([unsafe_car/2, unsafe_cdr/2, + unsafe_constant_element/3, unsafe_update_element/3, element/6]). +-export([unsafe_closure_element/3]). +-export([mk_fun_header/0, tag_fun/2]). +-export([unsafe_untag_float/2, unsafe_tag_float/2]). +-export([mk_sub_binary/6,mk_sub_binary/7]). +-export([unsafe_mk_big/3, unsafe_load_float/3]). +-export([bignum_sizeneed/1,bignum_sizeneed_code/2, get_one_word_pos_bignum/3]). +-export([test_subbinary/3, test_heap_binary/3]). +-export([create_heap_binary/3, create_refc_binary/3, create_refc_binary/4]). +-export([create_matchstate/6, convert_matchstate/1, compare_matchstate/4]). +-export([get_field_from_term/3, get_field_from_pointer/3, + set_field_from_term/3, set_field_from_pointer/3, + extract_matchbuffer/2, extract_binary_bytes/2]). + +-include("hipe_rtl.hrl"). +-include("hipe_literals.hrl"). + +-ifdef(EFT_NATIVE_ADDRESS). +-export([if_fun_get_arity_and_address/5]). +-endif. + +-undef(TAG_PRIMARY_BOXED). +-undef(TAG_IMMED2_MASK). +-undef(TAG_IMMED2_CATCH). +-undef(TAG_IMMED2_SIZE). + +%%------------------------------------------------------------------------ + +-define(TAG_PRIMARY_SIZE, 2). +-define(TAG_PRIMARY_MASK, 16#3). +-define(TAG_PRIMARY_HEADER, 16#0). +-define(TAG_PRIMARY_LIST, 16#1). +-define(TAG_PRIMARY_BOXED, 16#2). +-define(TAG_PRIMARY_IMMED1, 16#3). + +-define(TAG_IMMED1_SIZE, 4). +-define(TAG_IMMED1_MASK, 16#F). +-define(TAG_IMMED1_PID, ((16#0 bsl ?TAG_PRIMARY_SIZE) bor ?TAG_PRIMARY_IMMED1)). +-define(TAG_IMMED1_PORT, ((16#1 bsl ?TAG_PRIMARY_SIZE) bor ?TAG_PRIMARY_IMMED1)). +-define(TAG_IMMED1_IMMED2,((16#2 bsl ?TAG_PRIMARY_SIZE) bor ?TAG_PRIMARY_IMMED1)). +-define(TAG_IMMED1_SMALL, ((16#3 bsl ?TAG_PRIMARY_SIZE) bor ?TAG_PRIMARY_IMMED1)). + +-define(TAG_IMMED2_SIZE, 6). +-define(TAG_IMMED2_MASK, 16#3F). +-define(TAG_IMMED2_ATOM, ((16#0 bsl ?TAG_IMMED1_SIZE) bor ?TAG_IMMED1_IMMED2)). +-define(TAG_IMMED2_CATCH, ((16#1 bsl ?TAG_IMMED1_SIZE) bor ?TAG_IMMED1_IMMED2)). +-define(TAG_IMMED2_NIL, ((16#3 bsl ?TAG_IMMED1_SIZE) bor ?TAG_IMMED1_IMMED2)). + +-define(TAG_HEADER_ARITYVAL,((16#0 bsl ?TAG_PRIMARY_SIZE) bor ?TAG_PRIMARY_HEADER)). +-define(TAG_HEADER_BIN_MATCHSTATE, ((16#1 bsl ?TAG_PRIMARY_SIZE) bor ?TAG_PRIMARY_HEADER)). +-define(TAG_HEADER_POS_BIG, ((16#2 bsl ?TAG_PRIMARY_SIZE) bor ?TAG_PRIMARY_HEADER)). +-define(TAG_HEADER_NEG_BIG, ((16#3 bsl ?TAG_PRIMARY_SIZE) bor ?TAG_PRIMARY_HEADER)). +-define(BIG_SIGN_BIT, (16#1 bsl ?TAG_PRIMARY_SIZE)). +-define(TAG_HEADER_REF, ((16#4 bsl ?TAG_PRIMARY_SIZE) bor ?TAG_PRIMARY_HEADER)). +-define(TAG_HEADER_FUN, ((16#5 bsl ?TAG_PRIMARY_SIZE) bor ?TAG_PRIMARY_HEADER)). +-define(TAG_HEADER_FLOAT, ((16#6 bsl ?TAG_PRIMARY_SIZE) bor ?TAG_PRIMARY_HEADER)). +-define(TAG_HEADER_EXPORT, ((16#7 bsl ?TAG_PRIMARY_SIZE) bor ?TAG_PRIMARY_HEADER)). +-define(BINARY_XXX_MASK, (16#3 bsl ?TAG_PRIMARY_SIZE)). +-define(TAG_HEADER_REFC_BIN,((16#8 bsl ?TAG_PRIMARY_SIZE) bor ?TAG_PRIMARY_HEADER)). +-define(TAG_HEADER_HEAP_BIN,((16#9 bsl ?TAG_PRIMARY_SIZE) bor ?TAG_PRIMARY_HEADER)). +-define(TAG_HEADER_SUB_BIN, ((16#A bsl ?TAG_PRIMARY_SIZE) bor ?TAG_PRIMARY_HEADER)). +-define(TAG_HEADER_EXTERNAL_PID, ((16#C bsl ?TAG_PRIMARY_SIZE) bor ?TAG_PRIMARY_HEADER)). +-define(TAG_HEADER_EXTERNAL_PORT,((16#D bsl ?TAG_PRIMARY_SIZE) bor ?TAG_PRIMARY_HEADER)). +-define(TAG_HEADER_EXTERNAL_REF, ((16#E bsl ?TAG_PRIMARY_SIZE) bor ?TAG_PRIMARY_HEADER)). + +-define(TAG_HEADER_MASK, 16#3F). +-define(HEADER_ARITY_OFFS, 6). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +mk_header(SZ,TAG) -> (SZ bsl ?HEADER_ARITY_OFFS) + TAG. +mk_arityval(SZ) -> mk_header(SZ, ?TAG_HEADER_ARITYVAL). + +size_from_header(Sz, Header) -> + [hipe_rtl:mk_alu(Sz, Header, 'srl', hipe_rtl:mk_imm(?HEADER_ARITY_OFFS))]. + +mk_var_header(Header, Size, Tag) -> + Tmp = hipe_rtl:mk_new_reg_gcsafe(), + [hipe_rtl:mk_alu(Tmp, Size, sll, hipe_rtl:mk_imm(?HEADER_ARITY_OFFS)), + hipe_rtl:mk_alu(Header, Tmp, 'add', hipe_rtl:mk_imm(Tag))]. + +mk_fixnum(X) -> (X bsl ?TAG_IMMED1_SIZE) + ?TAG_IMMED1_SMALL. + +-define(NIL, ((-1 bsl ?TAG_IMMED2_SIZE) bor ?TAG_IMMED2_NIL)). +mk_nil() -> ?NIL. +%% mk_atom(X) -> (X bsl ?TAG_IMMED2_SIZE) + ?TAG_IMMED2_ATOM. +mk_non_value() -> ?THE_NON_VALUE. + +-spec is_fixnum(integer()) -> boolean(). +is_fixnum(N) when is_integer(N) -> + Bits = ?bytes_to_bits(hipe_rtl_arch:word_size()) - ?TAG_IMMED1_SIZE, + (N =< ((1 bsl (Bits - 1)) - 1)) and (N >= -(1 bsl (Bits - 1))). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-define(HEADER_EXPORT, mk_header(1, ?TAG_HEADER_EXPORT)). +-define(HEADER_FUN, mk_header(?ERL_FUN_SIZE-2, ?TAG_HEADER_FUN)). +-define(HEADER_PROC_BIN, mk_header(?PROC_BIN_WORDSIZE-1, ?TAG_HEADER_REFC_BIN)). +-define(HEADER_SUB_BIN, mk_header(?SUB_BIN_WORDSIZE-2, ?TAG_HEADER_SUB_BIN)). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +tag_boxed(Res, X) -> + hipe_rtl:mk_alu(Res, X, 'add', hipe_rtl:mk_imm(?TAG_PRIMARY_BOXED)). + +%% tag_bignum(Res, X) -> tag_boxed(Res, X). +tag_flonum(Res, X) -> tag_boxed(Res, X). +tag_tuple(Res, X) -> tag_boxed(Res, X). + +tag_cons(Res, X) -> + hipe_rtl:mk_alu(Res, X, 'add', hipe_rtl:mk_imm(?TAG_PRIMARY_LIST)). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%% Operations to test if an object has a known type T. + +test_nil(X, TrueLab, FalseLab, Pred) -> + hipe_rtl:mk_branch(X, eq, hipe_rtl:mk_imm(?NIL), TrueLab, FalseLab, Pred). + +test_cons(X, TrueLab, FalseLab, Pred) -> + Tmp = hipe_rtl:mk_new_reg_gcsafe(), + Mask = hipe_rtl:mk_imm(?TAG_PRIMARY_MASK - ?TAG_PRIMARY_LIST), + hipe_rtl:mk_alub(Tmp, X, 'and', Mask, 'eq', TrueLab, FalseLab, Pred). + +test_is_boxed(X, TrueLab, FalseLab, Pred) -> + Tmp = hipe_rtl:mk_new_reg_gcsafe(), + Mask = hipe_rtl:mk_imm(?TAG_PRIMARY_MASK - ?TAG_PRIMARY_BOXED), + hipe_rtl:mk_alub(Tmp, X, 'and', Mask, 'eq', TrueLab, FalseLab, Pred). + +get_header(Res, X) -> + hipe_rtl:mk_load(Res, X, hipe_rtl:mk_imm(-(?TAG_PRIMARY_BOXED))). + +mask_and_compare(X, Mask, Value, TrueLab, FalseLab, Pred) -> + Tmp = hipe_rtl:mk_new_reg_gcsafe(), + [hipe_rtl:mk_alu(Tmp, X, 'and', hipe_rtl:mk_imm(Mask)), + hipe_rtl:mk_branch(Tmp, 'eq', hipe_rtl:mk_imm(Value), TrueLab, FalseLab, Pred)]. + +test_immed1(X, Value, TrueLab, FalseLab, Pred) -> + mask_and_compare(X, ?TAG_IMMED1_MASK, Value, TrueLab, FalseLab, Pred). + +test_internal_pid(X, TrueLab, FalseLab, Pred) -> + test_immed1(X, ?TAG_IMMED1_PID, TrueLab, FalseLab, Pred). + +test_any_pid(X, TrueLab, FalseLab, Pred) -> + NotInternalPidLab = hipe_rtl:mk_new_label(), + [test_internal_pid(X, TrueLab, hipe_rtl:label_name(NotInternalPidLab), Pred), + NotInternalPidLab, + test_external_pid(X, TrueLab, FalseLab, Pred)]. + +test_external_pid(X, TrueLab, FalseLab, Pred) -> + Tmp = hipe_rtl:mk_new_reg_gcsafe(), + HalfTrueLab = hipe_rtl:mk_new_label(), + ExternalPidMask = ?TAG_HEADER_MASK, + [test_is_boxed(X, hipe_rtl:label_name(HalfTrueLab), FalseLab, Pred), + HalfTrueLab, + get_header(Tmp, X), + mask_and_compare(Tmp, ExternalPidMask, ?TAG_HEADER_EXTERNAL_PID, + TrueLab, FalseLab, Pred)]. + +test_internal_port(X, TrueLab, FalseLab, Pred) -> + test_immed1(X, ?TAG_IMMED1_PORT, TrueLab, FalseLab, Pred). + +test_any_port(X, TrueLab, FalseLab, Pred) -> + NotInternalPortLab = hipe_rtl:mk_new_label(), + [test_internal_port(X, TrueLab, hipe_rtl:label_name(NotInternalPortLab), Pred), + NotInternalPortLab, + test_external_port(X, TrueLab, FalseLab, Pred)]. + +test_external_port(X, TrueLab, FalseLab, Pred) -> + Tmp = hipe_rtl:mk_new_reg_gcsafe(), + HalfTrueLab = hipe_rtl:mk_new_label(), + ExternalPortMask = ?TAG_HEADER_MASK, + [test_is_boxed(X, hipe_rtl:label_name(HalfTrueLab), FalseLab, Pred), + HalfTrueLab, + get_header(Tmp, X), + mask_and_compare(Tmp, ExternalPortMask, ?TAG_HEADER_EXTERNAL_PORT, + TrueLab, FalseLab, Pred)]. + +test_fixnum(X, TrueLab, FalseLab, Pred) -> + test_immed1(X, ?TAG_IMMED1_SMALL, TrueLab, FalseLab, Pred). + +test_atom(X, TrueLab, FalseLab, Pred) -> + mask_and_compare(X, ?TAG_IMMED2_MASK, ?TAG_IMMED2_ATOM, + TrueLab, FalseLab, Pred). + +test_tuple(X, TrueLab, FalseLab, Pred) -> + Tmp = hipe_rtl:mk_new_reg_gcsafe(), + Tmp2 = hipe_rtl:mk_new_reg_gcsafe(), + HalfTrueLab = hipe_rtl:mk_new_label(), + [test_is_boxed(X, hipe_rtl:label_name(HalfTrueLab), FalseLab, Pred), + HalfTrueLab, + get_header(Tmp, X), + hipe_rtl:mk_alub(Tmp2, Tmp, 'and', hipe_rtl:mk_imm(?TAG_HEADER_MASK), 'eq', + TrueLab, FalseLab, Pred)]. + +test_tuple_N(X, N, TrueLab, FalseLab, Pred) -> + Tmp = hipe_rtl:mk_new_reg_gcsafe(), + HalfTrueLab = hipe_rtl:mk_new_label(), + [test_is_boxed(X, hipe_rtl:label_name(HalfTrueLab), FalseLab, Pred), + HalfTrueLab, + get_header(Tmp, X), + hipe_rtl:mk_branch(Tmp, 'eq', hipe_rtl:mk_imm(mk_arityval(N)), + TrueLab, FalseLab, Pred)]. + +test_ref(X, TrueLab, FalseLab, Pred) -> + Hdr = hipe_rtl:mk_new_reg_gcsafe(), + Tag = hipe_rtl:mk_new_reg_gcsafe(), + HalfTrueLab = hipe_rtl:mk_new_label(), + TwoThirdsTrueLab = hipe_rtl:mk_new_label(), + [test_is_boxed(X, hipe_rtl:label_name(HalfTrueLab), FalseLab, Pred), + HalfTrueLab, + get_header(Hdr, X), + hipe_rtl:mk_alu(Tag, Hdr, 'and', hipe_rtl:mk_imm(?TAG_HEADER_MASK)), + hipe_rtl:mk_branch(Tag, 'eq', hipe_rtl:mk_imm(?TAG_HEADER_REF), + TrueLab, hipe_rtl:label_name(TwoThirdsTrueLab), Pred), + TwoThirdsTrueLab, + hipe_rtl:mk_branch(Tag, 'eq', hipe_rtl:mk_imm(?TAG_HEADER_EXTERNAL_REF), + TrueLab, FalseLab, Pred) + ]. + +-ifdef(EFT_NATIVE_ADDRESS). +test_closure(X, TrueLab, FalseLab, Pred) -> + Tmp = hipe_rtl:mk_new_reg_gcsafe(), + HalfTrueLab = hipe_rtl:mk_new_label(), + [test_is_boxed(X, hipe_rtl:label_name(HalfTrueLab), FalseLab, Pred), + HalfTrueLab, + get_header(Tmp, X), + mask_and_compare(Tmp, ?TAG_HEADER_MASK, ?TAG_HEADER_FUN, + TrueLab, FalseLab, Pred)]. +-endif. + +test_fun(X, TrueLab, FalseLab, Pred) -> + Hdr = hipe_rtl:mk_new_reg_gcsafe(), + Tag = hipe_rtl:mk_new_reg_gcsafe(), + HalfTrueLab = hipe_rtl:mk_new_label(), + TwoThirdsTrueLab = hipe_rtl:mk_new_label(), + [test_is_boxed(X, hipe_rtl:label_name(HalfTrueLab), FalseLab, Pred), + HalfTrueLab, + get_header(Hdr, X), + hipe_rtl:mk_alu(Tag, Hdr, 'and', hipe_rtl:mk_imm(?TAG_HEADER_MASK)), + hipe_rtl:mk_branch(Tag, 'eq', hipe_rtl:mk_imm(?TAG_HEADER_FUN), + TrueLab, hipe_rtl:label_name(TwoThirdsTrueLab), Pred), + TwoThirdsTrueLab, + hipe_rtl:mk_branch(Tag, 'eq', hipe_rtl:mk_imm(?TAG_HEADER_EXPORT), + TrueLab, FalseLab, Pred)]. + +test_fun2(X, Arity, TrueLab, FalseLab, Pred) -> + Tmp = hipe_rtl:mk_new_reg_gcsafe(), + TFalse = hipe_rtl:mk_new_reg_gcsafe(), + HalfTrueLab = hipe_rtl:mk_new_label(), + [hipe_rtl:mk_call([Tmp], {erlang,is_function,2}, [X,Arity], + hipe_rtl:label_name(HalfTrueLab), FalseLab, 'not_remote'), + HalfTrueLab, + hipe_rtl:mk_load_atom(TFalse, 'false'), + hipe_rtl:mk_branch(Tmp, 'ne', TFalse, TrueLab, FalseLab, Pred)]. + +flonum_header() -> + mk_header(8 div hipe_rtl_arch:word_size(), ?TAG_HEADER_FLOAT). + +test_flonum(X, TrueLab, FalseLab, Pred) -> + HeaderFlonum = flonum_header(), + Tmp = hipe_rtl:mk_new_reg_gcsafe(), + HalfTrueLab = hipe_rtl:mk_new_label(), + [test_is_boxed(X, hipe_rtl:label_name(HalfTrueLab), FalseLab, Pred), + HalfTrueLab, + get_header(Tmp, X), + hipe_rtl:mk_branch(Tmp, 'eq', hipe_rtl:mk_imm(HeaderFlonum), + TrueLab, FalseLab, Pred)]. + +test_bignum(X, TrueLab, FalseLab, Pred) -> + Tmp = hipe_rtl:mk_new_reg_gcsafe(), + HalfTrueLab = hipe_rtl:mk_new_label(), + BigMask = ?TAG_HEADER_MASK - ?BIG_SIGN_BIT, + [test_is_boxed(X, hipe_rtl:label_name(HalfTrueLab), FalseLab, Pred), + HalfTrueLab, + get_header(Tmp, X), + mask_and_compare(Tmp, BigMask, ?TAG_HEADER_POS_BIG, + TrueLab, FalseLab, Pred)]. + +test_pos_bignum(X, TrueLab, FalseLab, Pred) -> + Tmp = hipe_rtl:mk_new_reg_gcsafe(), + HalfTrueLab = hipe_rtl:mk_new_label(), + BigMask = ?TAG_HEADER_MASK, + [test_is_boxed(X, hipe_rtl:label_name(HalfTrueLab), FalseLab, Pred), + HalfTrueLab, + get_header(Tmp, X), + mask_and_compare(Tmp, BigMask, ?TAG_HEADER_POS_BIG, + TrueLab, FalseLab, Pred)]. + +test_matchstate(X, TrueLab, FalseLab, Pred) -> + Tmp = hipe_rtl:mk_new_reg_gcsafe(), + HalfTrueLab = hipe_rtl:mk_new_label(), + [test_is_boxed(X, hipe_rtl:label_name(HalfTrueLab), FalseLab, Pred), + HalfTrueLab, + get_header(Tmp, X), + mask_and_compare(Tmp, ?TAG_HEADER_MASK, ?TAG_HEADER_BIN_MATCHSTATE, + TrueLab, FalseLab, Pred)]. + +test_bitstr(X, TrueLab, FalseLab, Pred) -> + Tmp = hipe_rtl:mk_new_reg_gcsafe(), + HalfTrueLab = hipe_rtl:mk_new_label(), + Mask = ?TAG_HEADER_MASK - ?BINARY_XXX_MASK, + [test_is_boxed(X, hipe_rtl:label_name(HalfTrueLab), FalseLab, Pred), + HalfTrueLab, + get_header(Tmp, X), + mask_and_compare(Tmp, Mask, ?TAG_HEADER_REFC_BIN, TrueLab, FalseLab, Pred)]. + +test_binary(X, TrueLab, FalseLab, Pred) -> + Tmp1 = hipe_rtl:mk_new_reg_gcsafe(), + Tmp2 = hipe_rtl:mk_new_reg_gcsafe(), + IsBoxedLab = hipe_rtl:mk_new_label(), + IsBitStrLab = hipe_rtl:mk_new_label(), + IsSubBinLab = hipe_rtl:mk_new_label(), + Mask = ?TAG_HEADER_MASK - ?BINARY_XXX_MASK, + [test_is_boxed(X, hipe_rtl:label_name(IsBoxedLab), FalseLab, Pred), + IsBoxedLab, + get_header(Tmp1, X), + mask_and_compare(Tmp1, Mask, ?TAG_HEADER_REFC_BIN, + hipe_rtl:label_name(IsBitStrLab), FalseLab, Pred), + IsBitStrLab, + mask_and_compare(Tmp1, ?TAG_HEADER_MASK, ?TAG_HEADER_SUB_BIN, + hipe_rtl:label_name(IsSubBinLab), TrueLab, 0.5), + IsSubBinLab, + get_field_from_term({sub_binary, bitsize}, X, Tmp2), + hipe_rtl:mk_branch(Tmp2, eq, hipe_rtl:mk_imm(0), TrueLab, FalseLab, Pred)]. + +test_list(X, TrueLab, FalseLab, Pred) -> + Lab = hipe_rtl:mk_new_label(), + [test_cons(X, TrueLab, hipe_rtl:label_name(Lab), 0.5), + Lab, + test_nil(X, TrueLab, FalseLab, Pred)]. + +test_integer(X, TrueLab, FalseLab, Pred) -> + Lab = hipe_rtl:mk_new_label(), + [test_fixnum(X, TrueLab, hipe_rtl:label_name(Lab), 0.5), + Lab, + test_bignum(X, TrueLab, FalseLab, Pred)]. + +test_number(X, TrueLab, FalseLab, Pred) -> + Lab1 = hipe_rtl:mk_new_label(), + Lab2 = hipe_rtl:mk_new_label(), + Lab3 = hipe_rtl:mk_new_label(), + Tmp = hipe_rtl:mk_new_reg_gcsafe(), + BigMask = ?TAG_HEADER_MASK - ?BIG_SIGN_BIT, + HeaderFlonum = flonum_header(), + [test_fixnum(X, TrueLab, hipe_rtl:label_name(Lab1), 0.5), + Lab1, + test_is_boxed(X, hipe_rtl:label_name(Lab2), FalseLab, 0.5), + Lab2, + get_header(Tmp, X), + mask_and_compare(Tmp, BigMask, ?TAG_HEADER_POS_BIG, + TrueLab, hipe_rtl:label_name(Lab3), 0.5), + Lab3, + hipe_rtl:mk_branch(Tmp, 'eq', hipe_rtl:mk_imm(HeaderFlonum), + TrueLab, FalseLab, Pred)]. + +%% CONS, NIL, and TUPLE are not constants, everything else is +test_constant(X, TrueLab, FalseLab, Pred) -> + Lab1 = hipe_rtl:mk_new_label(), + Lab2 = hipe_rtl:mk_new_label(), + Pred1 = 1-Pred, + [test_cons(X, FalseLab, hipe_rtl:label_name(Lab1), Pred1), + Lab1, + test_nil(X, FalseLab, hipe_rtl:label_name(Lab2), Pred1), + Lab2, + test_tuple(X, FalseLab, TrueLab, Pred1)]. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +tag_fixnum(DestVar, SrcReg) -> + [hipe_rtl:mk_fixnumop(DestVar, SrcReg, tag)]. +%% [hipe_rtl:mk_alu(DestVar, SrcReg, sll, hipe_rtl:mk_imm(?TAG_IMMED1_SIZE)), +%% hipe_rtl:mk_alu(DestVar, DestVar, add, hipe_rtl:mk_imm(?TAG_IMMED1_SMALL))]. + +realtag_fixnum(DestVar, SrcReg) -> + [hipe_rtl:mk_alu(DestVar, SrcReg, sll, hipe_rtl:mk_imm(?TAG_IMMED1_SIZE)), + hipe_rtl:mk_alu(DestVar, DestVar, add, hipe_rtl:mk_imm(?TAG_IMMED1_SMALL))]. + +untag_fixnum(DestReg, SrcVar) -> + hipe_rtl:mk_fixnumop(DestReg, SrcVar, untag). +%% hipe_rtl:mk_alu(DestReg, SrcVar, 'sra', hipe_rtl:mk_imm(?TAG_IMMED1_SIZE)). + +realuntag_fixnum(DestReg, SrcVar) -> + hipe_rtl:mk_alu(DestReg, SrcVar, 'sra', hipe_rtl:mk_imm(?TAG_IMMED1_SIZE)). + +fixnum_val(Fixnum) -> + Fixnum bsr ?TAG_IMMED1_SIZE. + +test_fixnums(Args, TrueLab, FalseLab, Pred) -> + {Reg, Ands} = test_fixnums_1(Args, []), + Ands ++ [test_fixnum(Reg, TrueLab, FalseLab, Pred)]. + +test_fixnums_1([Arg1, Arg2], Acc) -> + Tmp = hipe_rtl:mk_new_reg_gcsafe(), + {Tmp, lists:reverse([hipe_rtl:mk_alu(Tmp, Arg1, 'and', Arg2)|Acc])}; +test_fixnums_1([Arg1, Arg2|Args], Acc) -> + Tmp = hipe_rtl:mk_new_reg_gcsafe(), + test_fixnums_1([Tmp|Args], [hipe_rtl:mk_alu(Tmp, Arg1, 'and', Arg2)|Acc]). + +test_two_fixnums(Arg1, Arg2, FalseLab) -> + TrueLab = hipe_rtl:mk_new_label(), + case hipe_rtl:is_imm(Arg2) of + true -> + Value = hipe_rtl:imm_value(Arg2), + case Value band ?TAG_IMMED1_MASK of + ?TAG_IMMED1_SMALL -> + [test_fixnum(Arg1, hipe_rtl:label_name(TrueLab), FalseLab, 0.99), + TrueLab]; + _ -> + [hipe_rtl:mk_goto(FalseLab)] + end; + false -> + Tmp = hipe_rtl:mk_new_reg_gcsafe(), + [hipe_rtl:mk_alu(Tmp, Arg1, 'and', Arg2), + test_fixnum(Tmp, hipe_rtl:label_name(TrueLab), FalseLab, 0.99), + TrueLab] + end. + +fixnum_cmp(Arg1, Arg2, TrueLab, FalseLab, Pred, CmpOp) -> + hipe_rtl:mk_branch(Arg1, CmpOp, Arg2, TrueLab, FalseLab, Pred). + +fixnum_gt(Arg1, Arg2, TrueLab, FalseLab, Pred) -> + fixnum_cmp(Arg1, Arg2, TrueLab, FalseLab, Pred, gt). + +fixnum_lt(Arg1, Arg2, TrueLab, FalseLab, Pred) -> + fixnum_cmp(Arg1, Arg2, TrueLab, FalseLab, Pred, lt). + +fixnum_ge(Arg1, Arg2, TrueLab, FalseLab, Pred) -> + fixnum_cmp(Arg1, Arg2, TrueLab, FalseLab, Pred, ge). + +fixnum_le(Arg1, Arg2, TrueLab, FalseLab, Pred) -> + fixnum_cmp(Arg1, Arg2, TrueLab, FalseLab, Pred, le). + +%% We know the answer will be a fixnum +unsafe_fixnum_add(Arg1, Arg2, Res) -> + Tmp = hipe_rtl:mk_new_reg_gcsafe(), + [hipe_rtl:mk_alu(Tmp, Arg2, sub, hipe_rtl:mk_imm(?TAG_IMMED1_SMALL)), + hipe_rtl:mk_alu(Res, Arg1, add, Tmp)]. + +%% We know the answer will be a fixnum +unsafe_fixnum_sub(Arg1, Arg2, Res) -> + Tmp = hipe_rtl:mk_new_reg_gcsafe(), + [hipe_rtl:mk_alu(Tmp, Arg2, sub, hipe_rtl:mk_imm(?TAG_IMMED1_SMALL)), + hipe_rtl:mk_alu(Res, Arg1, sub, Tmp)]. + +%%% (16X+tag)+((16Y+tag)-tag) = 16X+tag+16Y = 16(X+Y)+tag +%%% (16X+tag)-((16Y+tag)-tag) = 16X+tag-16Y = 16(X-Y)+tag +fixnum_addsub(AluOp, Arg1, Arg2, Res, OtherLab) -> + Tmp = hipe_rtl:mk_new_reg_gcsafe(), + %% XXX: Consider moving this test to the users of fixnum_addsub. + case Arg1 =/= Res andalso Arg2 =/= Res of + true -> + %% Args differ from res. + NoOverflowLab = hipe_rtl:mk_new_label(), + [hipe_rtl:mk_alu(Tmp, Arg2, sub, hipe_rtl:mk_imm(?TAG_IMMED1_SMALL)), + hipe_rtl:mk_alub(Res, Arg1, AluOp, Tmp, not_overflow, + hipe_rtl:label_name(NoOverflowLab), + hipe_rtl:label_name(OtherLab), 0.99), + NoOverflowLab]; + false -> + %% At least one of the arguments is the same as Res. + Tmp2 = hipe_rtl:mk_new_var(), % XXX: shouldn't this var be a reg? + NoOverflowLab = hipe_rtl:mk_new_label(), + [hipe_rtl:mk_alu(Tmp, Arg2, sub, hipe_rtl:mk_imm(?TAG_IMMED1_SMALL)), + hipe_rtl:mk_alub(Tmp2, Arg1, AluOp, Tmp, not_overflow, + hipe_rtl:label_name(NoOverflowLab), + hipe_rtl:label_name(OtherLab), 0.99), + NoOverflowLab, + hipe_rtl:mk_move(Res, Tmp2)] + end. + +%%% ((16X+tag) div 16) * ((16Y+tag)-tag) + tag = X*16Y+tag = 16(XY)+tag +fixnum_mul(Arg1, Arg2, Res, OtherLab) -> + Tmp = hipe_rtl:mk_new_reg_gcsafe(), + U1 = hipe_rtl:mk_new_reg_gcsafe(), + U2 = hipe_rtl:mk_new_reg_gcsafe(), + NoOverflowLab = hipe_rtl:mk_new_label(), + [untag_fixnum(U1, Arg1), + hipe_rtl:mk_alu(U2, Arg2, 'sub', hipe_rtl:mk_imm(?TAG_IMMED1_SMALL)), + hipe_rtl:mk_alub(Tmp, U1, 'mul', U2, overflow, hipe_rtl:label_name(OtherLab), + hipe_rtl:label_name(NoOverflowLab), 0.01), + NoOverflowLab, + hipe_rtl:mk_alu(Res, Tmp, 'add', hipe_rtl:mk_imm(?TAG_IMMED1_SMALL))]. + +fixnum_andorxor(AluOp, Arg1, Arg2, Res) -> + case AluOp of + 'xor' -> + Tmp = hipe_rtl:mk_new_reg_gcsafe(), + [hipe_rtl:mk_alu(Tmp, Arg1, 'xor', Arg2), % clears tag :-( + hipe_rtl:mk_alu(Res, Tmp, 'or', hipe_rtl:mk_imm(?TAG_IMMED1_SMALL))]; + _ -> hipe_rtl:mk_alu(Res, Arg1, AluOp, Arg2) + end. + +fixnum_not(Arg, Res) -> + Mask = (-1 bsl ?TAG_IMMED1_SIZE), + hipe_rtl:mk_alu(Res, Arg, 'xor', hipe_rtl:mk_imm(Mask)). + +fixnum_bsr(Arg1, Arg2, Res) -> + Tmp1 = hipe_rtl:mk_new_reg_gcsafe(), + Tmp2 = hipe_rtl:mk_new_reg_gcsafe(), + [untag_fixnum(Tmp1, Arg2), + hipe_rtl:mk_alu(Tmp2, Arg1, 'sra', Tmp1), + hipe_rtl:mk_alu(Res, Tmp2, 'or', hipe_rtl:mk_imm(?TAG_IMMED1_SMALL))]. + +%% If someone knows how to make this better, please do. +fixnum_bsl(Arg1, Arg2, Res) -> + Tmp1 = hipe_rtl:mk_new_reg_gcsafe(), + Tmp2 = hipe_rtl:mk_new_reg_gcsafe(), + Tmp3 = hipe_rtl:mk_new_reg_gcsafe(), + [untag_fixnum(Tmp2, Arg2), + hipe_rtl:mk_alu(Tmp1, Arg1, 'sub', hipe_rtl:mk_imm(?TAG_IMMED1_SMALL)), + hipe_rtl:mk_alu(Tmp3, Tmp1, 'sll', Tmp2), + hipe_rtl:mk_alu(Res, Tmp3, 'or', hipe_rtl:mk_imm(?TAG_IMMED1_SMALL))]. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +unsafe_car(Dst, Arg) -> + hipe_rtl:mk_load(Dst, Arg, hipe_rtl:mk_imm(-(?TAG_PRIMARY_LIST))). + +unsafe_cdr(Dst, Arg) -> + WordSize = hipe_rtl_arch:word_size(), + hipe_rtl:mk_load(Dst, Arg, hipe_rtl:mk_imm(-(?TAG_PRIMARY_LIST)+WordSize)). + +unsafe_constant_element(Dst, Index, Tuple) -> % Index is an immediate + WordSize = hipe_rtl_arch:word_size(), + Offset = -(?TAG_PRIMARY_BOXED) + WordSize * hipe_rtl:imm_value(Index), + hipe_rtl:mk_load(Dst, Tuple, hipe_rtl:mk_imm(Offset)). + +unsafe_update_element(Tuple, Index, Value) -> % Index is an immediate + WordSize = hipe_rtl_arch:word_size(), + Offset = -(?TAG_PRIMARY_BOXED) + WordSize * hipe_rtl:imm_value(Index), + hipe_rtl:mk_store(Tuple, hipe_rtl:mk_imm(Offset), Value). + +%%% wrong semantics +%% unsafe_variable_element(Dst, Index, Tuple) -> % Index is an unknown fixnum +%% %% Load word at (Tuple - 2) + ((Index >> 4) << 2). +%% %% Offset = ((Index >> 4) << 2) - 2. +%% %% Index = x..x1111 (fixnum tag is 2#1111). +%% %% (Index >> 2) = 00x..x11 and ((Index >> 4) << 2) = 00x..x00. +%% %% Therefore, ((Index >> 4) << 2) = (Index >> 2) - 3. +%% %% So Offset = ((Index >> 4) << 2) - 2 = (Index >> 2) - (3 + 2). +%% Tmp1 = hipe_rtl:mk_new_reg_gcsafe(), +%% Tmp2 = hipe_rtl:mk_new_reg_gcsafe(), +%% Shift = ?TAG_IMMED1_SIZE - 2, +%% OffAdj = (?TAG_IMMED1_SMALL bsr Shift) + ?TAG_PRIMARY_BOXED, +%% [hipe_rtl:mk_alu(Tmp1, Index, 'srl', hipe_rtl:mk_imm(Shift)), +%% hipe_rtl:mk_alu(Tmp2, Tmp1, 'sub', hipe_rtl:mk_imm(OffAdj)), +%% hipe_rtl:mk_load(Dst, Tuple, Tmp2)]. + +element(Dst, Index, Tuple, FailLabName, {tuple, A}, IndexInfo) -> + FixnumOkLab = hipe_rtl:mk_new_label(), + IndexOkLab = hipe_rtl:mk_new_label(), + Ptr = hipe_rtl:mk_new_reg(), % offset from Tuple + UIndex = hipe_rtl:mk_new_reg_gcsafe(), + Arity = hipe_rtl:mk_imm(A), + InvIndex = hipe_rtl:mk_new_reg_gcsafe(), + Offset = hipe_rtl:mk_new_reg_gcsafe(), + case IndexInfo of + valid -> + %% This is no branch, 1 load and 3 alus = 4 instr + [untag_fixnum(UIndex, Index), + hipe_rtl:mk_alu(Ptr, Tuple, 'sub', hipe_rtl:mk_imm(?TAG_PRIMARY_BOXED)), + hipe_rtl:mk_alu(Offset, UIndex, 'sll', + hipe_rtl:mk_imm(hipe_rtl_arch:log2_word_size())), + hipe_rtl:mk_load(Dst, Ptr, Offset)]; + fixnums -> + %% This is 1 branch, 1 load and 4 alus = 6 instr + [untag_fixnum(UIndex, Index), + hipe_rtl:mk_alu(Ptr, Tuple, 'sub',hipe_rtl:mk_imm(?TAG_PRIMARY_BOXED))| + gen_element_tail(Dst, Ptr, InvIndex, Arity, Offset, UIndex, + FailLabName, IndexOkLab)]; + _ -> + %% This is 3 branches, 1 load and 5 alus = 9 instr + [test_fixnum(Index, hipe_rtl:label_name(FixnumOkLab), + FailLabName, 0.99), + FixnumOkLab, + untag_fixnum(UIndex, Index), + hipe_rtl:mk_alu(Ptr, Tuple, 'sub',hipe_rtl:mk_imm(?TAG_PRIMARY_BOXED))| + gen_element_tail(Dst, Ptr, InvIndex, Arity, Offset, UIndex, + FailLabName, IndexOkLab)] + end; +element(Dst, Index, Tuple, FailLabName, tuple, IndexInfo) -> + FixnumOkLab = hipe_rtl:mk_new_label(), + IndexOkLab = hipe_rtl:mk_new_label(), + Ptr = hipe_rtl:mk_new_reg(), % offset from Tuple + Header = hipe_rtl:mk_new_reg_gcsafe(), + UIndex = hipe_rtl:mk_new_reg_gcsafe(), + Arity = hipe_rtl:mk_new_reg_gcsafe(), + InvIndex = hipe_rtl:mk_new_reg_gcsafe(), + Offset = hipe_rtl:mk_new_reg_gcsafe(), + case IndexInfo of + fixnums -> + %% This is 1 branch, 2 loads and 5 alus = 8 instr + [hipe_rtl:mk_alu(Ptr, Tuple, 'sub', hipe_rtl:mk_imm(?TAG_PRIMARY_BOXED)), + hipe_rtl:mk_load(Header, Ptr, hipe_rtl:mk_imm(0)), + untag_fixnum(UIndex, Index), + hipe_rtl:mk_alu(Arity,Header,'srl',hipe_rtl:mk_imm(?HEADER_ARITY_OFFS))| + gen_element_tail(Dst, Ptr, InvIndex, Arity, Offset, UIndex, + FailLabName, IndexOkLab)]; + Num when is_integer(Num) -> + %% This is 1 branch, 1 load and 3 alus = 5 instr + [hipe_rtl:mk_alu(Ptr, Tuple, 'sub', hipe_rtl:mk_imm(?TAG_PRIMARY_BOXED))| + gen_element_tail(Dst, Ptr, InvIndex, hipe_rtl:mk_imm(Num), + Offset, UIndex, FailLabName, IndexOkLab)]; + _ -> + %% This is 2 branches, 2 loads and 6 alus = 10 instr + [test_fixnum(Index, hipe_rtl:label_name(FixnumOkLab), FailLabName, 0.99), + FixnumOkLab, + hipe_rtl:mk_alu(Ptr, Tuple, 'sub', hipe_rtl:mk_imm(?TAG_PRIMARY_BOXED)), + hipe_rtl:mk_load(Header, Ptr, hipe_rtl:mk_imm(0)), + untag_fixnum(UIndex, Index), + hipe_rtl:mk_alu(Arity,Header,'srl',hipe_rtl:mk_imm(?HEADER_ARITY_OFFS))| + gen_element_tail(Dst, Ptr, InvIndex, Arity, Offset, UIndex, + FailLabName, IndexOkLab)] + end; +element(Dst, Index, Tuple, FailLabName, unknown, IndexInfo) -> + FixnumOkLab = hipe_rtl:mk_new_label(), + BoxedOkLab = hipe_rtl:mk_new_label(), + TupleOkLab = hipe_rtl:mk_new_label(), + IndexOkLab = hipe_rtl:mk_new_label(), + Ptr = hipe_rtl:mk_new_reg(), % offset from Tuple + Header = hipe_rtl:mk_new_reg_gcsafe(), + Tmp = hipe_rtl:mk_new_reg_gcsafe(), + UIndex = hipe_rtl:mk_new_reg_gcsafe(), + Arity = hipe_rtl:mk_new_reg_gcsafe(), + InvIndex = hipe_rtl:mk_new_reg_gcsafe(), + Offset = hipe_rtl:mk_new_reg_gcsafe(), + case IndexInfo of + fixnums -> + %% This is 3 branches, 2 loads and 5 alus = 10 instr + [test_is_boxed(Tuple, hipe_rtl:label_name(BoxedOkLab), + FailLabName, 0.99), + BoxedOkLab, + hipe_rtl:mk_alu(Ptr, Tuple, 'sub', hipe_rtl:mk_imm(?TAG_PRIMARY_BOXED)), + hipe_rtl:mk_load(Header, Ptr, hipe_rtl:mk_imm(0)), + hipe_rtl:mk_alub(Tmp, Header, 'and', + hipe_rtl:mk_imm(?TAG_HEADER_MASK), 'eq', + hipe_rtl:label_name(TupleOkLab), FailLabName, 0.99), + TupleOkLab, + untag_fixnum(UIndex, Index), + hipe_rtl:mk_alu(Arity, Header, 'srl', + hipe_rtl:mk_imm(?HEADER_ARITY_OFFS))| + gen_element_tail(Dst, Ptr, InvIndex, Arity, Offset, + UIndex, FailLabName, IndexOkLab)]; + Num when is_integer(Num) -> + %% This is 3 branches, 2 loads and 4 alus = 9 instr + [test_is_boxed(Tuple, hipe_rtl:label_name(BoxedOkLab), + FailLabName, 0.99), + BoxedOkLab, + hipe_rtl:mk_alu(Ptr, Tuple, 'sub', hipe_rtl:mk_imm(?TAG_PRIMARY_BOXED)), + hipe_rtl:mk_load(Header, Ptr, hipe_rtl:mk_imm(0)), + hipe_rtl:mk_alub(Tmp, Header, 'and', + hipe_rtl:mk_imm(?TAG_HEADER_MASK), 'eq', + hipe_rtl:label_name(TupleOkLab), FailLabName, 0.99), + TupleOkLab, + hipe_rtl:mk_alu(Arity, Header, 'srl', + hipe_rtl:mk_imm(?HEADER_ARITY_OFFS))| + gen_element_tail(Dst, Ptr, InvIndex, Arity, Offset, + hipe_rtl:mk_imm(Num), FailLabName, IndexOkLab)]; + _ -> + %% This is 4 branches, 2 loads, and 6 alus = 12 instr :( + [test_fixnum(Index, hipe_rtl:label_name(FixnumOkLab), + FailLabName, 0.99), + FixnumOkLab, + test_is_boxed(Tuple, hipe_rtl:label_name(BoxedOkLab), + FailLabName, 0.99), + BoxedOkLab, + hipe_rtl:mk_alu(Ptr, Tuple, 'sub', hipe_rtl:mk_imm(?TAG_PRIMARY_BOXED)), + hipe_rtl:mk_load(Header, Ptr, hipe_rtl:mk_imm(0)), + hipe_rtl:mk_alub(Tmp, Header, 'and', + hipe_rtl:mk_imm(?TAG_HEADER_MASK), 'eq', + hipe_rtl:label_name(TupleOkLab), FailLabName, 0.99), + TupleOkLab, + untag_fixnum(UIndex, Index), + hipe_rtl:mk_alu(Arity, Header, 'srl', + hipe_rtl:mk_imm(?HEADER_ARITY_OFFS))| + gen_element_tail(Dst, Ptr, InvIndex, Arity, Offset, + UIndex, FailLabName, IndexOkLab)] + end. + +gen_element_tail(Dst, Ptr, InvIndex, Arity, Offset, + UIndex, FailLabName, IndexOkLab) -> + %% now check that 1 <= UIndex <= Arity + %% if UIndex < 1, then (Arity - UIndex) >= Arity + %% if UIndex > Arity, then (Arity - UIndex) < 0, which is >=u Arity + %% otherwise, 0 <= (Arity - UIndex) < Arity + [hipe_rtl:mk_alu(InvIndex, Arity, 'sub', UIndex), + hipe_rtl:mk_branch(InvIndex, 'geu', Arity, FailLabName, + hipe_rtl:label_name(IndexOkLab), 0.01), + IndexOkLab, + hipe_rtl:mk_alu(Offset, UIndex, 'sll', + hipe_rtl:mk_imm(hipe_rtl_arch:log2_word_size())), + hipe_rtl:mk_load(Dst, Ptr, Offset)]. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +unsafe_closure_element(Dst, Index, Closure) -> % Index is an immediate + Offset = -(?TAG_PRIMARY_BOXED) %% Untag + + ?EFT_ENV %% Field offset + %% Index from 1 to N hence -1) + + (hipe_rtl_arch:word_size() * (hipe_rtl:imm_value(Index)-1)), + hipe_rtl:mk_load(Dst, Closure, hipe_rtl:mk_imm(Offset)). + +mk_fun_header() -> + hipe_rtl:mk_imm(?HEADER_FUN). + +tag_fun(Res, X) -> + tag_boxed(Res, X). + +%% untag_fun(Res, X) -> +%% hipe_rtl:mk_alu(Res, X, 'sub', hipe_rtl:mk_imm(?TAG_PRIMARY_BOXED)). + +-ifdef(EFT_NATIVE_ADDRESS). +if_fun_get_arity_and_address(ArityReg, AddressReg, FunP, BadFunLab, Pred) -> + %% EmuAddressPtrReg = hipe_rtl:mk_new_reg(), + %% FEPtrReg = hipe_rtl:mk_new_reg(), + %% ArityReg = hipe_rtl:mk_new_reg(), + %% NumFreeReg = hipe_rtl:mk_new_reg(), + %% RealArityReg = hipe_rtl:mk_new_reg(), + TrueLab0 = hipe_rtl:mk_new_label(), + %% TrueLab1 = hipe_rtl:mk_new_label(), + IsFunCode = test_closure(FunP, hipe_rtl:label_name(TrueLab0), BadFunLab, Pred), + GetArityCode = + [TrueLab0, + %% Funp->arity contains the arity + hipe_rtl:mk_load(ArityReg, FunP, + hipe_rtl:mk_imm(-(?TAG_PRIMARY_BOXED)+ + ?EFT_ARITY)), + hipe_rtl:mk_load(AddressReg, FunP, + hipe_rtl:mk_imm(-(?TAG_PRIMARY_BOXED)+ + ?EFT_NATIVE_ADDRESS))], + IsFunCode ++ GetArityCode. +-endif. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% +%% Binary Code +%% + +create_heap_binary(Base, Size, Dst) when is_integer(Size) -> + {GetHPInsn, HP, PutHPInsn} = hipe_rtl_arch:heap_pointer(), + WordSize = hipe_rtl_arch:word_size(), + NoWords=(Size + 3*WordSize-1) div WordSize, + NoBytes = NoWords*WordSize, + HeapBinHeader = hipe_rtl:mk_imm(mk_header(NoWords-1, + ?TAG_HEADER_HEAP_BIN)), + [GetHPInsn, + tag_boxed(Dst, HP), + set_field_from_pointer({heap_bin, thing_word}, HP, HeapBinHeader), + set_field_from_pointer({heap_bin, binsize}, HP, hipe_rtl:mk_imm(Size)), + hipe_rtl:mk_alu(Base, HP, add, hipe_rtl:mk_imm(?HEAP_BIN_DATA)), + hipe_rtl:mk_alu(HP, HP, add, hipe_rtl:mk_imm(NoBytes)), + PutHPInsn]; + +create_heap_binary(Base, Size, Dst) -> + {GetHPInsn, HP, PutHPInsn} = hipe_rtl_arch:heap_pointer(), + WordSize = hipe_rtl_arch:word_size(), + Log2WordSize = hipe_rtl_arch:log2_word_size(), + EvenWordSize = hipe_rtl:mk_new_reg_gcsafe(), + Tmp1 = hipe_rtl:mk_new_reg_gcsafe(), + Tmp2 = hipe_rtl:mk_new_reg_gcsafe(), + Header = hipe_rtl:mk_new_reg_gcsafe(), + Tmp3 = hipe_rtl:mk_new_reg(), % offset from HP + Tmp4 = hipe_rtl:mk_new_reg(), % offset from HP + [GetHPInsn, + hipe_rtl:mk_alu(Tmp1, Size, add, hipe_rtl:mk_imm(WordSize-1)), + hipe_rtl:mk_alu(EvenWordSize, Tmp1, sra, hipe_rtl:mk_imm(Log2WordSize)), + hipe_rtl:mk_alu(Tmp2, EvenWordSize, add, hipe_rtl:mk_imm(1)), + hipe_rtl:mk_alu(Base, HP, add, hipe_rtl:mk_imm(?HEAP_BIN_DATA)), + mk_var_header(Header, Tmp2, ?TAG_HEADER_HEAP_BIN), + set_field_from_pointer({heap_bin, thing_word}, HP, Header), + set_field_from_pointer({heap_bin, binsize}, HP, Size), + tag_boxed(Dst, HP), + hipe_rtl:mk_alu(Tmp3, HP, add, Size), + hipe_rtl:mk_alu(Tmp4, Tmp3, add, hipe_rtl:mk_imm(3*WordSize-1)), + hipe_rtl:mk_alu(HP, Tmp4, 'and', hipe_rtl:mk_imm(-WordSize)), + PutHPInsn]. + +create_refc_binary(Base, Size, Dst) -> + create_refc_binary(Base, Size, hipe_rtl:mk_imm(0), Dst). + +create_refc_binary(Base, Size, Flags, Dst) -> + {GetHPInsn, HP, PutHPInsn} = hipe_rtl_arch:heap_pointer(), + ProcBinHeader = hipe_rtl:mk_imm(?HEADER_PROC_BIN), + WordSize = hipe_rtl_arch:word_size(), + Val = hipe_rtl:mk_new_reg(), % offset from Base + [GetHPInsn, + tag_boxed(Dst, HP), + set_field_from_pointer({proc_bin, thing_word}, HP, ProcBinHeader), + set_field_from_pointer({proc_bin, binsize}, HP, Size), + heap_arch_spec(HP), + hipe_rtl:mk_alu(Val, Base, sub, hipe_rtl:mk_imm(?BINARY_ORIG_BYTES)), + set_field_from_pointer({proc_bin, val}, HP, Val), + set_field_from_pointer({proc_bin, bytes}, HP, Base), + set_field_from_pointer({proc_bin, flags}, HP, Flags), + hipe_rtl:mk_alu(HP, HP, add, hipe_rtl:mk_imm(?PROC_BIN_WORDSIZE*WordSize)), + PutHPInsn]. + +heap_arch_spec(HP) -> + Tmp1 = hipe_rtl:mk_new_reg(), % MSO state + [hipe_rtl_arch:pcb_load(Tmp1, ?P_OFF_HEAP_MSO), + set_field_from_pointer({proc_bin, next}, HP, Tmp1), + hipe_rtl_arch:pcb_store(?P_OFF_HEAP_MSO, HP)]. + +test_heap_binary(Binary, TrueLblName, FalseLblName) -> + Tmp1 = hipe_rtl:mk_new_reg_gcsafe(), + Tmp2 = hipe_rtl:mk_new_reg_gcsafe(), + [get_header(Tmp1, Binary), + hipe_rtl:mk_alu(Tmp2, Tmp1, 'and', hipe_rtl:mk_imm(?TAG_HEADER_MASK)), + hipe_rtl:mk_branch(Tmp2, eq, hipe_rtl:mk_imm(?TAG_HEADER_HEAP_BIN), + TrueLblName, FalseLblName)]. + +mk_sub_binary(Dst, ByteSize, ByteOffs, BitSize, BitOffs, Orig) -> + mk_sub_binary(Dst, ByteSize, ByteOffs, BitSize, BitOffs, + hipe_rtl:mk_imm(0), Orig). + +mk_sub_binary(Dst, ByteSize, ByteOffs, BitSize, BitOffs, + Writable, Orig) -> + {GetHPInsn, HP, PutHPInsn} = hipe_rtl_arch:heap_pointer(), + WordSize = hipe_rtl_arch:word_size(), + [GetHPInsn, + tag_boxed(Dst, HP), + build_sub_binary(Dst, ByteSize, ByteOffs, BitSize, BitOffs, Writable, Orig), + hipe_rtl:mk_alu(HP, HP, add, hipe_rtl:mk_imm(?SUB_BIN_WORDSIZE*WordSize)), + PutHPInsn]. + +build_sub_binary(Dst, ByteSize, ByteOffs, BitSize, BitOffs, + Writable, Orig) -> + Head = hipe_rtl:mk_imm(?HEADER_SUB_BIN), + [set_field_from_term({sub_binary, thing_word}, Dst, Head), + set_field_from_term({sub_binary, binsize}, Dst, ByteSize), + set_field_from_term({sub_binary, offset}, Dst, ByteOffs), + set_field_from_term({sub_binary, bitsize}, Dst, BitSize), + set_field_from_term({sub_binary, bitoffset}, Dst, BitOffs), + set_field_from_term({sub_binary, is_writable}, Dst, Writable), + set_field_from_term({sub_binary, orig}, Dst, Orig)]. + +test_subbinary(Binary, TrueLblName, FalseLblName) -> + Tmp1 = hipe_rtl:mk_new_reg_gcsafe(), + Tmp2 = hipe_rtl:mk_new_reg_gcsafe(), + [get_header(Tmp1, Binary), + hipe_rtl:mk_alu(Tmp2, Tmp1, 'and', hipe_rtl:mk_imm(?TAG_HEADER_MASK)), + hipe_rtl:mk_branch(Tmp2, eq, hipe_rtl:mk_imm(?TAG_HEADER_SUB_BIN), TrueLblName, FalseLblName)]. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% +%% Float Code + +unsafe_load_float(DstLo, DstHi, Src) -> + WordSize = hipe_rtl_arch:word_size(), + Offset1 = -(?TAG_PRIMARY_BOXED) + WordSize, + Offset2 = Offset1 + 4, %% This should really be 4 and not WordSize + case hipe_rtl_arch:endianess() of + little -> + [hipe_rtl:mk_load(DstLo, Src, hipe_rtl:mk_imm(Offset1), int32, unsigned), + hipe_rtl:mk_load(DstHi, Src, hipe_rtl:mk_imm(Offset2), int32, unsigned)]; + big -> + [hipe_rtl:mk_load(DstHi, Src, hipe_rtl:mk_imm(Offset1), int32, unsigned), + hipe_rtl:mk_load(DstLo, Src, hipe_rtl:mk_imm(Offset2), int32, unsigned)] + end. + +unsafe_untag_float(Dst, Src) -> + Offset = -(?TAG_PRIMARY_BOXED) + hipe_rtl_arch:word_size(), + [hipe_rtl:mk_fload(Dst, Src, hipe_rtl:mk_imm(Offset))]. + +unsafe_tag_float(Dst, Src) -> + {GetHPInsn, HP, PutHPInsn} = hipe_rtl_arch:heap_pointer(), + Head = hipe_rtl:mk_imm(flonum_header()), + WordSize = hipe_rtl_arch:word_size(), + [GetHPInsn, + hipe_rtl:mk_store(HP, hipe_rtl:mk_imm(0), Head), + hipe_rtl:mk_fstore(HP, hipe_rtl:mk_imm(WordSize), Src), + tag_flonum(Dst, HP), + hipe_rtl:mk_alu(HP, HP, add, hipe_rtl:mk_imm(WordSize+8)), + PutHPInsn]. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% +%% BigNum Code + +unsafe_mk_big(Dst, Src, Signedness) -> + WordSize = hipe_rtl_arch:word_size(), + {GetHPInsn, HP, PutHPInsn} = hipe_rtl_arch:heap_pointer(), + PosHead = hipe_rtl:mk_imm(mk_header(1, ?TAG_HEADER_POS_BIG)), + NegHead = hipe_rtl:mk_imm(mk_header(1, ?TAG_HEADER_NEG_BIG)), + PosLabel = hipe_rtl:mk_new_label(), + NegLabel = hipe_rtl:mk_new_label(), + JoinLabel = hipe_rtl:mk_new_label(), + PutHeaderCode = + case Signedness of + unsigned -> + [hipe_rtl:mk_store(HP, hipe_rtl:mk_imm(0*WordSize), PosHead)]; + signed -> + [hipe_rtl:mk_branch(Src, ge, hipe_rtl:mk_imm(0), + hipe_rtl:label_name(PosLabel), + hipe_rtl:label_name(NegLabel)), + PosLabel, + hipe_rtl:mk_store(HP, hipe_rtl:mk_imm(0*WordSize), PosHead), + hipe_rtl:mk_goto(hipe_rtl:label_name(JoinLabel)), + NegLabel, + hipe_rtl:mk_store(HP, hipe_rtl:mk_imm(0*WordSize), NegHead), + JoinLabel] + end, + RestCode = + [hipe_rtl:mk_store(HP, hipe_rtl:mk_imm(1*WordSize), Src), + tag_boxed(Dst, HP), + hipe_rtl:mk_alu(HP, HP, add, hipe_rtl:mk_imm(2*WordSize)), + PutHPInsn], + [GetHPInsn] ++ PutHeaderCode ++ RestCode. + +get_one_word_pos_bignum(USize, Size, Fail) -> + Header = hipe_rtl:mk_new_reg(), + HalfLbl = hipe_rtl:mk_new_label(), + HalfLblName = hipe_rtl:label_name(HalfLbl), + WordSize = hipe_rtl_arch:word_size(), + PosHead = hipe_rtl:mk_imm(mk_header(1, ?TAG_HEADER_POS_BIG)), + [get_header(Header, Size), + hipe_rtl:mk_branch(Header, eq, PosHead, HalfLblName, Fail), + HalfLbl, + hipe_rtl:mk_load(USize, Size, hipe_rtl:mk_imm(1*WordSize + -?TAG_PRIMARY_BOXED))]. + +-spec bignum_sizeneed(non_neg_integer()) -> non_neg_integer(). + +bignum_sizeneed(Size) -> + WordSizeBits = hipe_rtl_arch:word_size() * 8, + case is_fixnum(1 bsl Size) of + true -> + 0; + false -> + ((Size + (WordSizeBits-1)) div WordSizeBits) + 1 + end. + +bignum_sizeneed_code(SizeReg,FixNumLblName) -> + WordSizeBits = hipe_rtl_arch:word_size() * 8, + WordShifts = hipe_rtl_arch:log2_word_size() + 3, + MaxFixNum = WordSizeBits - ?TAG_IMMED1_SIZE - 1, + ResReg = hipe_rtl:mk_new_reg_gcsafe(), + Tmp1 = hipe_rtl:mk_new_reg_gcsafe(), + BigLbl = hipe_rtl:mk_new_label(), + Code = + [hipe_rtl:mk_branch(SizeReg, le, hipe_rtl:mk_imm(MaxFixNum), + FixNumLblName, hipe_rtl:label_name(BigLbl)), + BigLbl, + hipe_rtl:mk_alu(Tmp1,SizeReg,add,hipe_rtl:mk_imm(WordSizeBits-1)), + hipe_rtl:mk_alu(ResReg,Tmp1,srl,hipe_rtl:mk_imm(WordShifts)), + hipe_rtl:mk_alu(ResReg,ResReg,add,hipe_rtl:mk_imm(1))], + {ResReg,Code}. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% +%% MatchState Code + +create_matchstate(Max, BinSize, Base, Offset, Orig, Ms) -> + WordSize = hipe_rtl_arch:word_size(), + {GetHPInsn, HP, PutHPInsn} = hipe_rtl_arch:heap_pointer(), + ByteSize = (Max+1)*WordSize + ?MS_SAVEOFFSET, + SizeInWords = ((ByteSize div WordSize) - 1), + Header = hipe_rtl:mk_imm(mk_header(SizeInWords, ?TAG_HEADER_BIN_MATCHSTATE)), + [GetHPInsn, + hipe_rtl:mk_alu(Ms, HP, add, hipe_rtl:mk_imm(?TAG_PRIMARY_BOXED)), + set_field_from_term({matchstate,thing_word}, Ms, Header), + set_field_from_term({matchstate,{matchbuffer,orig}}, Ms, Orig), + set_field_from_term({matchstate,{matchbuffer,base}}, Ms, Base), + set_field_from_term({matchstate,{matchbuffer,binsize}}, Ms, BinSize), + set_field_from_term({matchstate,{matchbuffer,offset}}, Ms, Offset), + set_field_from_term({matchstate,{saveoffset, 0}}, Ms, Offset), + hipe_rtl:mk_alu(HP, HP, add, hipe_rtl:mk_imm(ByteSize)), + PutHPInsn]. + +convert_matchstate(Ms) -> + WordSize = hipe_rtl_arch:word_size(), + Header = hipe_rtl:mk_new_reg_gcsafe(), + TmpSize = hipe_rtl:mk_new_reg_gcsafe(), + SavedOffset = hipe_rtl:mk_new_reg_gcsafe(), + Orig = hipe_rtl:mk_new_reg_gcsafe(), + BinSize = hipe_rtl:mk_new_reg_gcsafe(), + ByteSize = hipe_rtl:mk_new_reg_gcsafe(), + BitSize = hipe_rtl:mk_new_reg_gcsafe(), + ByteOffset = hipe_rtl:mk_new_reg_gcsafe(), + BitOffset = hipe_rtl:mk_new_reg_gcsafe(), + SizeInWords = hipe_rtl:mk_new_reg_gcsafe(), + Hole = hipe_rtl:mk_new_reg_gcsafe(), + BigIntHeader = hipe_rtl:mk_new_reg_gcsafe(), + [get_field_from_term({matchstate, {matchbuffer, orig}}, Ms, Orig), + get_field_from_term({matchstate, {matchbuffer, binsize}}, Ms, BinSize), + get_field_from_term({matchstate, {saveoffset, 0}}, Ms, SavedOffset), + get_field_from_term({matchstate, thing_word}, Ms, Header), + hipe_rtl:mk_alu(TmpSize, BinSize, sub, SavedOffset), + hipe_rtl:mk_alu(BitSize, TmpSize, 'and', hipe_rtl:mk_imm(7)), + hipe_rtl:mk_alu(BitOffset, SavedOffset, 'and', hipe_rtl:mk_imm(7)), + hipe_rtl:mk_alu(ByteSize, TmpSize, srl, hipe_rtl:mk_imm(3)), + hipe_rtl:mk_alu(ByteOffset, SavedOffset, srl, hipe_rtl:mk_imm(3)), + build_sub_binary(Ms, ByteSize, ByteOffset, BitSize, BitOffset, + hipe_rtl:mk_imm(0), Orig), + size_from_header(SizeInWords, Header), + hipe_rtl:mk_alu(Hole, SizeInWords, sub, hipe_rtl:mk_imm(?SUB_BIN_WORDSIZE-1)), + mk_var_header(BigIntHeader, Hole, ?TAG_HEADER_POS_BIG), + hipe_rtl:mk_store(Ms, hipe_rtl:mk_imm(?SUB_BIN_WORDSIZE*WordSize-?TAG_PRIMARY_BOXED), + BigIntHeader)]. + +compare_matchstate(Max, Ms, LargeEnough, TooSmall) -> + WordSize = hipe_rtl_arch:word_size(), + ByteSize = (Max+1)*WordSize + ?MS_SAVEOFFSET, + SizeInWords = ((ByteSize div WordSize) - 1), + Header = hipe_rtl:mk_imm(mk_header(SizeInWords, ?TAG_HEADER_BIN_MATCHSTATE)), + RealHeader = hipe_rtl:mk_new_reg_gcsafe(), + [hipe_rtl:mk_load(RealHeader, Ms, hipe_rtl:mk_imm(-?TAG_PRIMARY_BOXED)), + hipe_rtl:mk_branch(RealHeader, ge, Header, LargeEnough, TooSmall)]. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% +%% Struct manipulation code + +get_field_offset({matchstate, thing_word}) -> + ?MS_THING_WORD; +get_field_offset({matchstate, matchbuffer}) -> + ?MS_MATCHBUFFER; +get_field_offset({matchstate, {matchbuffer, _} = Field}) -> + ?MS_MATCHBUFFER + get_field_offset(Field); +get_field_offset({matchstate, {saveoffset, N}} = Field) -> + ?MS_SAVEOFFSET + N*get_field_size1(Field); +get_field_offset({sub_binary, thing_word}) -> + ?SUB_BIN_THING_WORD; +get_field_offset({sub_binary, binsize}) -> + ?SUB_BIN_BINSIZE; +get_field_offset({sub_binary, bitsize}) -> + ?SUB_BIN_BITSIZE; +get_field_offset({sub_binary, offset}) -> + ?SUB_BIN_OFFS; +get_field_offset({sub_binary, bitoffset}) -> + ?SUB_BIN_BITOFFS; +get_field_offset({sub_binary, is_writable}) -> + ?SUB_BIN_WRITABLE; +get_field_offset({sub_binary, orig}) -> + ?SUB_BIN_ORIG; +get_field_offset({proc_bin, thing_word}) -> + ?PROC_BIN_THING_WORD; +get_field_offset({proc_bin, binsize}) -> + ?PROC_BIN_BINSIZE; +get_field_offset({proc_bin, next}) -> + ?PROC_BIN_NEXT; +get_field_offset({proc_bin, val}) -> + ?PROC_BIN_VAL; +get_field_offset({proc_bin, bytes}) -> + ?PROC_BIN_BYTES; +get_field_offset({proc_bin, flags}) -> + ?PROC_BIN_FLAGS; +get_field_offset({binary, orig_bytes}) -> + ?BINARY_ORIG_BYTES; +get_field_offset({binary, orig_size}) -> + ?BINARY_ORIG_SIZE; +get_field_offset({heap_bin, thing_word}) -> + ?HEAP_BIN_THING_WORD; +get_field_offset({heap_bin, binsize}) -> + ?HEAP_BIN_SIZE; +get_field_offset({heap_bin, {data, N}} = Field) -> + ?HEAP_BIN_DATA+N*get_field_size1(Field); +get_field_offset({matchbuffer, offset}) -> + ?MB_OFFSET; +get_field_offset({matchbuffer, orig}) -> + ?MB_ORIG; +get_field_offset({matchbuffer, base}) -> + ?MB_BASE; +get_field_offset({matchbuffer, binsize}) -> + ?MB_SIZE. + +get_field_size(Field) -> + size_to_atom(get_field_size1(Field)). + +size_to_atom(Bytes) -> + WordSize = hipe_rtl_arch:word_size(), + case Bytes of + WordSize -> word; + 4 -> int32; + %%2 -> int16; So far there are no 2 byte fields + 1 -> byte + end. + +get_field_size1({matchstate, thing_word}) -> + ?MS_THING_WORD_SIZE; +get_field_size1({matchstate, {matchbuffer, _} = Field}) -> + get_field_size1(Field); +get_field_size1({matchstate, {saveoffset, _N}}) -> + ?MS_SAVEOFFSET_SIZE; +get_field_size1({sub_binary, thing_word}) -> + ?SUB_BIN_THING_WORD_SIZE; +get_field_size1({sub_binary, binsize}) -> + ?SUB_BIN_BINSIZE_SIZE; +get_field_size1({sub_binary, bitsize}) -> + ?SUB_BIN_BITSIZE_SIZE; +get_field_size1({sub_binary, offset}) -> + ?SUB_BIN_OFFS_SIZE; +get_field_size1({sub_binary, bitoffset}) -> + ?SUB_BIN_BITOFFS_SIZE; +get_field_size1({sub_binary, is_writable}) -> + ?SUB_BIN_WRITABLE_SIZE; +get_field_size1({sub_binary, orig}) -> + ?SUB_BIN_ORIG_SIZE; +get_field_size1({proc_bin, thing_word}) -> + ?PROC_BIN_THING_WORD_SIZE; +get_field_size1({proc_bin, binsize}) -> + ?PROC_BIN_BINSIZE_SIZE; +get_field_size1({proc_bin, next}) -> + ?PROC_BIN_NEXT_SIZE; +get_field_size1({proc_bin, val}) -> + ?PROC_BIN_VAL_SIZE; +get_field_size1({proc_bin, bytes}) -> + ?PROC_BIN_BYTES_SIZE; +get_field_size1({proc_bin, flags}) -> + ?PROC_BIN_FLAGS_SIZE; +get_field_size1({binary, orig_bytes}) -> + ?BINARY_ORIG_BYTES_SIZE; +get_field_size1({binary, orig_size}) -> + ?BINARY_ORIG_SIZE_SIZE; +get_field_size1({heap_bin, thing_word}) -> + ?HEAP_BIN_THING_WORD_SIZE; +get_field_size1({heap_bin, binsize}) -> + ?HEAP_BIN_SIZE_SIZE; +get_field_size1({heap_bin, {data, _}}) -> + ?HEAP_BIN_DATA_SIZE; +get_field_size1({matchbuffer, offset}) -> + ?MB_OFFSET_SIZE; +get_field_size1({matchbuffer, orig}) -> + ?MB_ORIG_SIZE; +get_field_size1({matchbuffer, base}) -> + ?MB_BASE_SIZE; +get_field_size1({matchbuffer, binsize}) -> + ?MB_SIZE_SIZE. + +get_field_from_term(Struct, Term, Dst) -> + Offset = hipe_rtl:mk_imm(get_field_offset(Struct) - ?TAG_PRIMARY_BOXED), + Size = get_field_size(Struct), + hipe_rtl:mk_load(Dst, Term, Offset, Size, unsigned). + +set_field_from_term(Struct, Term, Value) -> + Offset = hipe_rtl:mk_imm(get_field_offset(Struct) - ?TAG_PRIMARY_BOXED), + Size = get_field_size(Struct), + hipe_rtl:mk_store(Term, Offset, Value, Size). + +get_field_from_pointer(Struct, Term, Dst) -> + Offset = hipe_rtl:mk_imm(get_field_offset(Struct)), + Size = get_field_size(Struct), + hipe_rtl:mk_load(Dst, Term, Offset, Size, unsigned). + +set_field_from_pointer(Struct, Term, Value) -> + Offset = hipe_rtl:mk_imm(get_field_offset(Struct)), + Size = get_field_size(Struct), + hipe_rtl:mk_store(Term, Offset, Value, Size). + +extract_matchbuffer(Mb, Ms) -> + What = {matchstate, matchbuffer}, + Offset = hipe_rtl:mk_imm(get_field_offset(What) - ?TAG_PRIMARY_BOXED), + hipe_rtl:mk_alu(Mb, Ms, add, Offset). + +extract_binary_bytes(Binary, Base) -> + Offset = hipe_rtl:mk_imm(get_field_offset({binary, orig_bytes})), + hipe_rtl:mk_alu(Base, Binary, add, Offset). |