mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-16 14:25:56 +00:00
Estimated hours taken: 3 Branches: main compiler/lookup_switch.m: Fix two bugs that caused test case failures in deep profiling grades. One bug was that an acquired register wasn't being released before the creation of a resume point, which rebuilds the code generator state (and thus forgets about acquired registers). The other bug was that is_lookup_switch wasn't performing the actions generate_goal would have when processing goals. In particular, it wasn't invoking pre_goal_update and post_goal_update on disjunctions inside the switch. compiler/lookup_util.m: Do not standardize goals by removing scopes from around other goals, because this could also remove the effects of the code generator annotations (e.g. liveness changes such as pre-births) on the scope goal. compiler/simplify.m: Eliminate those redundant scopes if asked to do so. Since this is done before the code generator annotations are put on goals, this is safe. compiler/code_gen.m: compiler/proc_gen.m: Divide the old code_gen.m into two modules: the new code_gen.m concerned with generating code for goals, and the new module proc_gen.m concerned with generating code for procedures. Without this, the code for handling goals is lost inside the old code_gen.m module. compiler/ll_backend.m: Include the new module. compiler/mercury_compile.m: Import proc_gen instead of code_gen, and ask simplify to eliminate unnecessary scopes before code generation. compiler/middle_rec.m: Update a reference to a predicate now in proc_gen.m. compiler/notes/compiler_design.html: Document the new module.
2344 lines
88 KiB
Mathematica
2344 lines
88 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 2000-2006 The University of Melbourne.
|
|
% This file may only be copied under the terms of the GNU General
|
|
% Public License - see the file COPYING in the Mercury distribution.
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% File: var_locn.m
|
|
% Author: zs.
|
|
|
|
% This module defines a set of predicates that operate on the abstract
|
|
% 'var_locn_info' structure which maintains information about where variables
|
|
% are stored, what their values are if they are not stored anywhere,
|
|
% and which registers are reserved for purposes such as holding the arguments
|
|
% of calls and tags that are to be switched upon.
|
|
|
|
%----------------------------------------------------------------------------%
|
|
|
|
:- module ll_backend.var_locn.
|
|
:- interface.
|
|
|
|
:- import_module parse_tree.prog_data.
|
|
:- import_module hlds.hlds_module.
|
|
:- import_module hlds.hlds_goal.
|
|
:- import_module hlds.hlds_llds.
|
|
:- import_module ll_backend.global_data.
|
|
:- import_module ll_backend.llds.
|
|
:- import_module libs.options.
|
|
|
|
:- import_module assoc_list.
|
|
:- import_module bool.
|
|
:- import_module list.
|
|
:- import_module map.
|
|
:- import_module maybe.
|
|
:- import_module set.
|
|
|
|
%----------------------------------------------------------------------------%
|
|
|
|
:- type var_locn_info.
|
|
|
|
% init_state(Arguments, Liveness, VarSet, VarTypes, StackSlots,
|
|
% FollowVars, Opts, VarLocnInfo):
|
|
%
|
|
% Produces an initial state of the VarLocnInfo given
|
|
% an association list of variables and lvalues. The initial
|
|
% state places the given variables at their corresponding
|
|
% locations, with the exception of variables which are not in
|
|
% Liveness (this corresponds to input arguments that are not
|
|
% used in the body). The VarSet parameter contains a mapping from
|
|
% variables to names, which is used when code is generated
|
|
% to provide meaningful comments. VarTypes gives the types of
|
|
% of all the procedure's variables. StackSlots maps each variable
|
|
% to its stack slot, if it has one. FollowVars is the initial
|
|
% follow_vars set; such sets give guidance as to what lvals
|
|
% (if any) each variable will be needed in next. Opts gives
|
|
% the table of options; this is used to decide what expressions
|
|
% are considered constants.
|
|
%
|
|
:- pred init_state(assoc_list(prog_var, lval)::in, set(prog_var)::in,
|
|
prog_varset::in, vartypes::in, stack_slots::in, abs_follow_vars::in,
|
|
option_table::in, var_locn_info::out) is det.
|
|
|
|
% reinit_state(VarLocs, !VarLocnInfo):
|
|
%
|
|
% Produces a new state of the VarLocnInfo in which the static
|
|
% and mostly static information (stack slot map, follow vars map,
|
|
% varset, option settings) comes from VarLocnInfo0 but the
|
|
% dynamic state regarding variable locations is thrown away
|
|
% and then rebuilt from the information in VarLocs, an
|
|
% association list of variables and lvals. The new state
|
|
% places the given variables at their corresponding locations.
|
|
%
|
|
:- pred reinit_state(assoc_list(prog_var, lval)::in,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
% clobber_all_regs(OkToDeleteAny, !VarLocnInfo):
|
|
%
|
|
% Modifies VarLocnInfo to show that all variables stored in registers
|
|
% have been clobbered. Aborts if this deletes the last record of the
|
|
% state of a variable unless OkToDeleteAny is `yes'.
|
|
%
|
|
:- pred clobber_all_regs(bool::in,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
% clobber_regs(Regs, !VarLocnInfo):
|
|
%
|
|
% Modifies VarLocnInfo to show that all variables stored in Regs
|
|
% (a list of lvals which should contain only registers) are clobbered.
|
|
%
|
|
:- pred clobber_regs(list(lval)::in,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
% set_magic_var_location(Var, Lval, !VarLocnInfo):
|
|
%
|
|
% Updates !VarLocnInfo to show that Var is *magically* stored in Lval.
|
|
% Does not care if Lval is already in use; it overwrites it with the
|
|
% new information. Var must not have been previously known. Used to
|
|
% implement the ends of erroneous branches.
|
|
%
|
|
:- pred set_magic_var_location(prog_var::in, lval::in,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
% check_and_set_magic_var_location(Var, Lval, !VarLocnInfo):
|
|
%
|
|
% Updates VarLocnInfo to show that Var has been *magically* stored in Lval.
|
|
% (The caller usually generates code to perform this magic.) Aborts if Lval
|
|
% is already in use, or if Var was previously known.
|
|
%
|
|
:- pred check_and_set_magic_var_location(prog_var::in, lval::in,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
% lval_in_use(VarLocnInfo, Lval)
|
|
%
|
|
% Succeeds iff Lval, which should be a register or stack slot,
|
|
% holds (a path to) a variable or is otherwise reserved.
|
|
%
|
|
:- pred lval_in_use(var_locn_info::in, lval::in) is semidet.
|
|
|
|
% var_becomes_dead(Var, FirstTime, !VarLocnInfo):
|
|
%
|
|
% Frees any code generator resources used by Var in !VarLocnInfo.
|
|
% FirstTime should be no if this same operation may already have been
|
|
% executed on Var; otherwise, var_becomes_dead will throw an exception
|
|
% if it does not know about Var.
|
|
%
|
|
:- pred var_becomes_dead(prog_var::in, bool::in,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
% assign_var_to_var(Var, AssignedVar, !VarLocnInfo):
|
|
%
|
|
% Reflects the effect of the assignment Var := AssignedVar in the
|
|
% state of !VarLocnInfo.
|
|
%
|
|
:- pred assign_var_to_var(prog_var::in, prog_var::in,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
% assign_lval_to_var(ModuleInfo, Var, Lval, StaticCellInfo, Code,
|
|
% !VarLocnInfo);
|
|
%
|
|
% Reflects the effect of the assignment Var := lval(Lval) in the
|
|
% state of !VarLocnInfo; any code required to effect the assignment
|
|
% will be returned in Code.
|
|
%
|
|
:- pred assign_lval_to_var(module_info::in, prog_var::in, lval::in,
|
|
static_cell_info::in, code_tree::out,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
% assign_const_to_var(Var, ConstRval, !VarLocnInfo):
|
|
%
|
|
% Reflects the effect of the assignment Var := const(ConstRval)
|
|
% in the state of !VarLocnInfo.
|
|
%
|
|
:- pred assign_const_to_var(prog_var::in, rval::in,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
% assign_expr_to_var(Var, Rval, Code, !VarLocnInfo):
|
|
%
|
|
% Generates code to execute the assignment Var := Expr, and
|
|
% updates the state of !VarLocnInfo accordingly.
|
|
%
|
|
% Expr must contain no lvals, although it may (and typically will) refer
|
|
% to the values of other variables through rvals of the form var(_).
|
|
%
|
|
:- pred assign_expr_to_var(prog_var::in, rval::in, code_tree::out,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
% assign_cell_to_var(ModuleInfo, Var, ReserveWordAtStart, Ptag, Vector,
|
|
% SizeInfo, TypeMsg, Where, Code, !StaticCellInfo, !VarLocnInfo):
|
|
%
|
|
% Generates code to assign to Var a pointer, tagged by Ptag, to the cell
|
|
% whose contents are given by the other arguments, and updates the state
|
|
% of !VarLocnInfo accordingly. If ReserveWordAtStart is yes, and the cell
|
|
% is allocated on the heap (rather than statically), then reserve an extra
|
|
% word immediately before the allocated object, for the garbage collector
|
|
% to use to hold a forwarding pointer. If SizeInfo is yes(SizeVal), then
|
|
% reserve an extra word immediately before the allocated object (regardless
|
|
% of whether it is allocated statically or dynamically), and initialize
|
|
% this word with the value determined by SizeVal. (NOTE: ReserveWordAtStart
|
|
% and SizeInfo should not be yes / yes(_), because that will cause an
|
|
% obvious conflict.) Where will say where the created cell is.
|
|
%
|
|
:- pred assign_cell_to_var(module_info::in, prog_var::in, bool::in, tag::in,
|
|
list(maybe(rval))::in, maybe(term_size_value)::in, string::in,
|
|
code_tree::out, static_cell_info::in, static_cell_info::out,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
% place_var(ModuleInfo, Var, Lval, Code, !VarLocnInfo):
|
|
%
|
|
% Produces Code to place the value of Var in Lval, and update !VarLocnInfo
|
|
% to reflect this.
|
|
%
|
|
:- pred place_var(module_info::in, prog_var::in, lval::in, code_tree::out,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
% place_vars(ModuleInfo, VarLocns, Code, !VarLocnInfo):
|
|
%
|
|
% Produces Code to place the value of each variable mentioned in VarLocns
|
|
% into the corresponding location, and update !VarLocnInfo to reflect this.
|
|
%
|
|
:- pred place_vars(module_info::in, assoc_list(prog_var, lval)::in,
|
|
code_tree::out, var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
% produce_var(ModuleInfo, Var, Rval, Code, !VarLocnInfo):
|
|
%
|
|
% Return the preferred way to refer to the value of Var
|
|
% (which may be a const rval, or the value in an lval).
|
|
%
|
|
% If Var is currently a cached expression, then produce_var will generate
|
|
% Code to evaluate the expression and put it into an lval. (Since the code
|
|
% generator can ask for a variable to be produced more than once, this is
|
|
% necessary to prevent the expression, which may involve a possibly large
|
|
% number of operations, from being evaluated several times.) Otherwise,
|
|
% Code will be empty.
|
|
%
|
|
:- pred produce_var(module_info::in, prog_var::in, rval::out, code_tree::out,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
% produce_var_in_reg(ModuleInfo, Var, Lval, Code, !VarLocnInfo):
|
|
%
|
|
% Produces a code fragment Code to evaluate Var if necessary
|
|
% and provide it as an Lval of the form reg(_).
|
|
%
|
|
:- pred produce_var_in_reg(module_info::in, prog_var::in, lval::out,
|
|
code_tree::out, var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
% produce_var_in_reg_or_stack(ModuleInfo, Var, FollowVars, Lval, Code,
|
|
% !VarLocnInfo):
|
|
%
|
|
% Produces a code fragment Code to evaluate Var if necessary and provide it
|
|
% as an Lval of the form reg(_), stackvar(_), or framevar(_).
|
|
%
|
|
:- pred produce_var_in_reg_or_stack(module_info::in, prog_var::in, lval::out,
|
|
code_tree::out, var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
% acquire_reg(Lval, !VarLocnInfo):
|
|
%
|
|
% Finds an unused register and marks it as 'in use'.
|
|
%
|
|
:- pred acquire_reg(lval::out,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
% acquire_reg_require_given(Reg, Lval, !VarLocInfo):
|
|
%
|
|
% Marks Reg, which must be an unused register, as 'in use'.
|
|
%
|
|
:- pred acquire_reg_require_given(lval::in,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
% acquire_reg_prefer_given(Pref, Lval, !VarLocInfo):
|
|
%
|
|
% Finds an unused register, and marks it as 'in use'.
|
|
% If Pref itself is free, assigns that.
|
|
%
|
|
:- pred acquire_reg_prefer_given(int::in, lval::out,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
% acquire_reg_start_at_given(Start, Lval, !VarLocInfo):
|
|
%
|
|
% Finds an unused register, and marks it as 'in use'.
|
|
% It starts the search at the one numbered Start,
|
|
% continuing towards higher register numbers.
|
|
%
|
|
:- pred acquire_reg_start_at_given(int::in, lval::out,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
% release_reg(Lval, !VarLocnInfo):
|
|
%
|
|
% Marks a previously acquired reg as no longer 'in use'.
|
|
%
|
|
:- pred release_reg(lval::in, var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
% lock_regs(N, Exceptions, !VarLocnInfo):
|
|
%
|
|
% Prevents registers r1 through rN from being reused, even if there are
|
|
% no variables referring to them, with the exceptions of the registers
|
|
% named in Exceptions, which however can only be used to store their
|
|
% corresponding variables. Should be followed by a call to unlock_regs.
|
|
%
|
|
:- pred lock_regs(int::in, assoc_list(prog_var, lval)::in,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
% unlock_regs(!VarLocnInfo):
|
|
%
|
|
% Undoes a lock operation.
|
|
%
|
|
:- pred unlock_regs(var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
% clear_r1(ModuleInfo, Code, !VarLocnInfo):
|
|
%
|
|
% Produces a code fragment Code to move whatever is in r1 to some other
|
|
% register, if r1 is live. This is used prior to semidet pragma c_codes.
|
|
%
|
|
:- pred clear_r1(module_info::in, code_tree::out,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
% materialize_vars_in_lval(ModuleInfo, Lval, FinalLval, Code,
|
|
% !VarLocnInfo):
|
|
%
|
|
% For every variable in Lval, substitutes the value of the variable and
|
|
% returns it as FinalLval. If we need to save the values of some of the
|
|
% substituted variables somewhere so as to prevent them from being
|
|
% evaluated again (and again ...), the required code will be returned
|
|
% in Code.
|
|
%
|
|
:- pred materialize_vars_in_lval(module_info::in, lval::in, lval::out,
|
|
code_tree::out, var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
% get_var_locations(VarLocnInfo, Locations):
|
|
%
|
|
% Returns a map from each live variable that occurs in VarLocnInfo
|
|
% to the set of locations in which it may be found (which may be empty,
|
|
% if the variable's value is either a known constant, or an as-yet
|
|
% unevaluated expression).
|
|
%
|
|
:- pred get_var_locations(var_locn_info::in, map(prog_var, set(lval))::out)
|
|
is det.
|
|
|
|
% get_stack_slots(VarLocnInfo, StackSlots):
|
|
%
|
|
% Returns the table mapping each variable to its stack slot (if any).
|
|
%
|
|
:- pred get_stack_slots(var_locn_info::in, stack_slots::out) is det.
|
|
|
|
% get_follow_vars(VarLocnInfo, FollowVars):
|
|
%
|
|
% Returns the table mapping each variable to the lval (if any)
|
|
% where it is desired next.
|
|
%
|
|
:- pred get_follow_var_map(var_locn_info::in, abs_follow_vars_map::out) is det.
|
|
|
|
% get_next_non_reserved(VarLocnInfo, NonRes):
|
|
%
|
|
% Returns the number of the first register which is free for general use.
|
|
% It does not reserve the register.
|
|
%
|
|
:- pred get_next_non_reserved(var_locn_info::in, int::out) is det.
|
|
|
|
% set_follow_vars(FollowVars):
|
|
%
|
|
% Sets the table mapping each variable to the lval (if any) where it is
|
|
% desired next, and the number of the first non-reserved register.
|
|
%
|
|
:- pred set_follow_vars(abs_follow_vars::in,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
% max_reg_in_use(MaxReg):
|
|
%
|
|
% Returns the number of the highest numbered rN register in use.
|
|
%
|
|
:- pred max_reg_in_use(var_locn_info::in, int::out) is det.
|
|
|
|
%----------------------------------------------------------------------------%
|
|
%----------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module check_hlds.type_util.
|
|
:- import_module libs.compiler_util.
|
|
:- import_module libs.options.
|
|
:- import_module libs.tree.
|
|
:- import_module ll_backend.code_util.
|
|
:- import_module ll_backend.exprn_aux.
|
|
|
|
:- import_module bag.
|
|
:- import_module getopt_io.
|
|
:- import_module int.
|
|
:- import_module pair.
|
|
:- import_module string.
|
|
:- import_module term.
|
|
:- import_module varset.
|
|
|
|
%----------------------------------------------------------------------------%
|
|
|
|
:- type dead_or_alive ---> dead ; alive.
|
|
|
|
% The state of a variable can be one of three kinds: const, cached
|
|
% and general.
|
|
%
|
|
% 1 The value of the variable is a known constant. In this case,
|
|
% the const_rval field will be yes, and the expr_rval field
|
|
% will be no. Both the empty set and nonempty sets are valid
|
|
% for the locs field. It will start out empty, will become
|
|
% nonempty if the variable is placed in some lval, and may
|
|
% become empty again if that lval is later overwritten.
|
|
%
|
|
% 2 The value of the variable is not stored anywhere, but its
|
|
% definition (an expression involving other variables) is cached.
|
|
% In this case, the const_rval field will be no, and the locs
|
|
% field will contain the empty set, but the expr_rval field
|
|
% will be yes. The variables referred to in the expr_rval field
|
|
% will include this variable in their using_vars sets, which
|
|
% protects them from deletion from the code generator state until
|
|
% the using variable is produced or placed in an lval. When that
|
|
% happens, the using variable's state will be transformed to the
|
|
% general, third kind, releasing this variable's hold on the
|
|
% variables contained in its expr_rval field.
|
|
%
|
|
% 3 The value of the variable is not a constant, nor is the
|
|
% variable cached. The locs field will be nonempty, and both
|
|
% const_rval and expr_rval will be no.
|
|
|
|
:- type var_state
|
|
---> state(
|
|
locs :: set(lval),
|
|
% Must not contain var(_).
|
|
|
|
const_rval :: maybe(rval),
|
|
% Must not contain var(_), must be constant.
|
|
|
|
expr_rval :: maybe(rval),
|
|
% Will contain var(_), must not contain lvals.
|
|
|
|
using_vars :: set(prog_var),
|
|
% The set of vars whose expr_rval field refers
|
|
% to this var.
|
|
|
|
dead_or_alive :: dead_or_alive
|
|
% A dead variable should be removed from
|
|
% var_state_map when its using_vars field
|
|
% becomes empty.
|
|
).
|
|
|
|
:- type var_state_map == map(prog_var, var_state).
|
|
|
|
% The loc_var_map maps each root lval (register or stack slot)
|
|
% to the set of variables that depend on that location,
|
|
% either because they are stored there or because the location
|
|
% contains a part of the pointer chain that leads to their address.
|
|
% In concrete terms, this means the set of variables whose var_state's
|
|
% locs field includes an lval that contains that root lval.
|
|
%
|
|
% If a root lval stack slot is unused, then it will either not appear
|
|
% in the var_loc_map or it will be mapped to an empty set. Allowing
|
|
% unused root lvals to be mapped to the empty set, and not requiring
|
|
% their deletion from the map, makes it simpler to manipulate
|
|
% loc_var_maps using higher-order code.
|
|
|
|
:- type loc_var_map == map(lval, set(prog_var)).
|
|
|
|
:- type var_locn_info
|
|
---> var_locn_info(
|
|
varset :: prog_varset,
|
|
% The varset from the proc_info.
|
|
|
|
vartypes :: vartypes,
|
|
% The vartypes from the proc_info.
|
|
|
|
stack_slots :: stack_slots,
|
|
% Maps each var to its stack slot,
|
|
% if it has one.
|
|
|
|
exprn_opts :: exprn_opts,
|
|
% The values of the options that are relevant
|
|
% to decisions about which rvals are constants.
|
|
|
|
follow_vars_map :: abs_follow_vars_map,
|
|
% Where vars are needed next.
|
|
|
|
next_non_res :: int,
|
|
% Next register that isn't reserved in
|
|
% follow_vars_map.
|
|
|
|
var_state_map :: var_state_map,
|
|
% Documented above.
|
|
|
|
loc_var_map :: loc_var_map,
|
|
% Documented above.
|
|
|
|
acquired :: set(lval),
|
|
% Locations that are temporarily reserved
|
|
% for purposes such as holding the tags of
|
|
% variables during switches.
|
|
|
|
locked :: int,
|
|
% If this slot contains N, then registers
|
|
% r1 through rN can only be modified by
|
|
% a place_var operation, or by a free_up_lval
|
|
% operation that moves a variable to the
|
|
% (free or freeable) lval associated with it
|
|
% in the exceptions field. Used to implement
|
|
% calls, foreign_procs and the store_maps
|
|
% at the ends of branched control structures.
|
|
|
|
exceptions :: assoc_list(prog_var, lval)
|
|
% See the documentation of the locked field
|
|
% above.
|
|
).
|
|
|
|
%----------------------------------------------------------------------------%
|
|
|
|
init_state(VarLocs, Liveness, VarSet, VarTypes, StackSlots, FollowVars,
|
|
Options, VarLocnInfo) :-
|
|
map.init(VarStateMap0),
|
|
map.init(LocVarMap0),
|
|
init_state_2(VarLocs, yes(Liveness), VarStateMap0, VarStateMap,
|
|
LocVarMap0, LocVarMap),
|
|
exprn_aux.init_exprn_opts(Options, ExprnOpts),
|
|
FollowVars = abs_follow_vars(FollowVarMap, NextNonReserved),
|
|
set.init(AcquiredRegs),
|
|
VarLocnInfo = var_locn_info(VarSet, VarTypes, StackSlots, ExprnOpts,
|
|
FollowVarMap, NextNonReserved, VarStateMap, LocVarMap,
|
|
AcquiredRegs, 0, []).
|
|
|
|
reinit_state(VarLocs, !VarLocnInfo) :-
|
|
map.init(VarStateMap0),
|
|
map.init(LocVarMap0),
|
|
init_state_2(VarLocs, no, VarStateMap0, VarStateMap,
|
|
LocVarMap0, LocVarMap),
|
|
set.init(AcquiredRegs),
|
|
!.VarLocnInfo = var_locn_info(VarSet, VarTypes, StackSlots, ExprnOpts,
|
|
FollowVarMap, NextNonReserved, _, _, _, _, _),
|
|
!:VarLocnInfo = var_locn_info(VarSet, VarTypes, StackSlots, ExprnOpts,
|
|
FollowVarMap, NextNonReserved, VarStateMap, LocVarMap,
|
|
AcquiredRegs, 0, []).
|
|
|
|
:- pred init_state_2(assoc_list(prog_var, lval)::in,
|
|
maybe(set(prog_var))::in, var_state_map::in, var_state_map::out,
|
|
loc_var_map::in, loc_var_map::out) is det.
|
|
|
|
init_state_2([], _, !VarStateMap, !LocVarMap).
|
|
init_state_2([Var - Lval | Rest], MaybeLiveness, !VarStateMap, !LocVarMap) :-
|
|
expect(is_root_lval(Lval), this_file, "init_state_2: unexpected lval"),
|
|
(
|
|
MaybeLiveness = yes(Liveness),
|
|
\+ set.member(Var, Liveness)
|
|
->
|
|
% If a variable is not live, then we do not record its
|
|
% state. If we did, then the variable will never die
|
|
% (since it is already dead), and the next call to
|
|
% clobber_regs would throw an exception, since it would
|
|
% believe that it is throwing away the last location
|
|
% storing the value of a "live" variable.
|
|
true
|
|
;
|
|
( map.search(!.VarStateMap, Var, _) ->
|
|
unexpected(this_file, "init_state_2: repeated variable")
|
|
;
|
|
set.singleton_set(NewLocs, Lval),
|
|
set.init(Using),
|
|
State = state(NewLocs, no, no, Using, alive),
|
|
map.det_insert(!.VarStateMap, Var, State, !:VarStateMap)
|
|
),
|
|
make_var_depend_on_lval_roots(Var, Lval, !LocVarMap)
|
|
),
|
|
init_state_2(Rest, MaybeLiveness, !VarStateMap, !LocVarMap).
|
|
|
|
%----------------------------------------------------------------------------%
|
|
|
|
get_var_locations(VLI, VarLocations) :-
|
|
get_var_state_map(VLI, VarStateMap),
|
|
map.to_assoc_list(VarStateMap, VarLocList),
|
|
list.filter_map(convert_live_to_lval_set, VarLocList, LiveVarLocList),
|
|
map.from_assoc_list(LiveVarLocList, VarLocations).
|
|
|
|
:- pred convert_live_to_lval_set(pair(prog_var, var_state)::in,
|
|
pair(prog_var, set(lval))::out) is semidet.
|
|
|
|
convert_live_to_lval_set(Var - State, Var - Lvals) :-
|
|
State = state(Lvals, _, _, _, alive).
|
|
|
|
%----------------------------------------------------------------------------%
|
|
|
|
clobber_all_regs(OkToDeleteAny, !VLI) :-
|
|
set_acquired(set.init, !VLI),
|
|
set_locked(0, !VLI),
|
|
set_exceptions([], !VLI),
|
|
get_loc_var_map(!.VLI, LocVarMap0),
|
|
get_var_state_map(!.VLI, VarStateMap0),
|
|
map.keys(LocVarMap0, Locs),
|
|
clobber_regs_in_maps(Locs, OkToDeleteAny,
|
|
LocVarMap0, LocVarMap, VarStateMap0, VarStateMap),
|
|
set_loc_var_map(LocVarMap, !VLI),
|
|
set_var_state_map(VarStateMap, !VLI).
|
|
|
|
clobber_regs(Regs, !VLI) :-
|
|
get_acquired(!.VLI, Acquired0),
|
|
Acquired = set.delete_list(Acquired0, Regs),
|
|
set_acquired(Acquired, !VLI),
|
|
get_loc_var_map(!.VLI, LocVarMap0),
|
|
get_var_state_map(!.VLI, VarStateMap0),
|
|
clobber_regs_in_maps(Regs, no,
|
|
LocVarMap0, LocVarMap, VarStateMap0, VarStateMap),
|
|
set_loc_var_map(LocVarMap, !VLI),
|
|
set_var_state_map(VarStateMap, !VLI).
|
|
|
|
:- pred clobber_regs_in_maps(list(lval)::in, bool::in,
|
|
loc_var_map::in, loc_var_map::out,
|
|
var_state_map::in, var_state_map::out) is det.
|
|
|
|
clobber_regs_in_maps([], _, !LocVarMap, !VarStateMap).
|
|
clobber_regs_in_maps([Lval | Lvals], OkToDeleteAny,
|
|
!LocVarMap, !VarStateMap) :-
|
|
(
|
|
Lval = reg(_, _),
|
|
map.search(!.LocVarMap, Lval, DependentVarsSet)
|
|
->
|
|
map.delete(!.LocVarMap, Lval, !:LocVarMap),
|
|
set.to_sorted_list(DependentVarsSet, DependentVars),
|
|
list.foldl(clobber_lval_in_var_state_map(Lval, [], OkToDeleteAny),
|
|
DependentVars, !VarStateMap)
|
|
;
|
|
true
|
|
),
|
|
clobber_regs_in_maps(Lvals, OkToDeleteAny, !LocVarMap, !VarStateMap).
|
|
|
|
:- pred clobber_lval_in_var_state_map(lval::in, list(prog_var)::in,
|
|
bool::in, prog_var::in, var_state_map::in, var_state_map::out) is det.
|
|
|
|
clobber_lval_in_var_state_map(Lval, OkToDeleteVars, OkToDeleteAny, Var,
|
|
!VarStateMap) :-
|
|
(
|
|
try_clobber_lval_in_var_state_map(Lval, OkToDeleteVars, OkToDeleteAny,
|
|
Var, !VarStateMap)
|
|
->
|
|
true
|
|
;
|
|
unexpected(this_file, "clobber_lval_in_var_state_map: empty state")
|
|
).
|
|
|
|
% Try to record in VarStateMap that Var is no longer reachable through
|
|
% (paths including) Lval. If this deletes the last possible place
|
|
% where the value of Var can be found, and Var is not in OkToDeleteVars,
|
|
% then fail.
|
|
%
|
|
:- pred try_clobber_lval_in_var_state_map(lval::in, list(prog_var)::in,
|
|
bool::in, prog_var::in, var_state_map::in, var_state_map::out) is semidet.
|
|
|
|
try_clobber_lval_in_var_state_map(Lval, OkToDeleteVars, OkToDeleteAny, Var,
|
|
!VarStateMap) :-
|
|
map.lookup(!.VarStateMap, Var, State0),
|
|
State0 = state(LvalSet0, MaybeConstRval, MaybeExprRval, Using,
|
|
DeadOrAlive),
|
|
LvalSet = set.filter(lval_does_not_support_lval(Lval), LvalSet0),
|
|
State = state(LvalSet, MaybeConstRval, MaybeExprRval, Using,
|
|
DeadOrAlive),
|
|
(
|
|
nonempty_state(State)
|
|
;
|
|
list.member(Var, OkToDeleteVars)
|
|
;
|
|
OkToDeleteAny = yes
|
|
;
|
|
DeadOrAlive = dead,
|
|
set.to_sorted_list(Using, UsingVars),
|
|
recursive_using_vars_dead_and_ok_to_delete(UsingVars,
|
|
!.VarStateMap, OkToDeleteVars)
|
|
),
|
|
map.det_update(!.VarStateMap, Var, State, !:VarStateMap).
|
|
|
|
:- pred recursive_using_vars_dead_and_ok_to_delete(
|
|
list(prog_var)::in, var_state_map::in, list(prog_var)::in) is semidet.
|
|
|
|
recursive_using_vars_dead_and_ok_to_delete([], _, _).
|
|
recursive_using_vars_dead_and_ok_to_delete([Var | Vars], VarStateMap,
|
|
OkToDeleteVars) :-
|
|
(
|
|
list.member(Var, OkToDeleteVars)
|
|
;
|
|
map.lookup(VarStateMap, Var, State),
|
|
State = state(_, _, _, Using, DeadOrAlive),
|
|
DeadOrAlive = dead,
|
|
set.to_sorted_list(Using, UsingVars),
|
|
recursive_using_vars_dead_and_ok_to_delete(UsingVars,
|
|
VarStateMap, OkToDeleteVars)
|
|
),
|
|
recursive_using_vars_dead_and_ok_to_delete(Vars,
|
|
VarStateMap, OkToDeleteVars).
|
|
|
|
%----------------------------------------------------------------------------%
|
|
|
|
assign_var_to_var(Var, OldVar, !VLI) :-
|
|
check_var_is_unknown(!.VLI, Var),
|
|
get_var_state_map(!.VLI, VarStateMap0),
|
|
map.lookup(VarStateMap0, OldVar, OldState0),
|
|
OldState0 = state(Lvals, MaybeConstRval, MaybeExprRval,
|
|
Using0, DeadOrAlive),
|
|
(
|
|
MaybeExprRval = yes(_),
|
|
State = state(Lvals, MaybeConstRval, yes(var(OldVar)), set.init,
|
|
alive),
|
|
set.insert(Using0, Var, Using),
|
|
OldState = state(Lvals, MaybeConstRval, MaybeExprRval, Using,
|
|
DeadOrAlive),
|
|
map.det_update(VarStateMap0, OldVar, OldState, VarStateMap1)
|
|
;
|
|
MaybeExprRval = no,
|
|
set.init(Empty),
|
|
State = state(Lvals, MaybeConstRval, no, Empty, alive),
|
|
VarStateMap1 = VarStateMap0
|
|
),
|
|
map.det_insert(VarStateMap1, Var, State, VarStateMap),
|
|
set_var_state_map(VarStateMap, !VLI),
|
|
|
|
get_loc_var_map(!.VLI, LocVarMap0),
|
|
make_var_depend_on_lvals_roots(Var, Lvals, LocVarMap0, LocVarMap),
|
|
set_loc_var_map(LocVarMap, !VLI).
|
|
|
|
%----------------------------------------------------------------------------%
|
|
|
|
assign_lval_to_var(ModuleInfo, Var, Lval0, StaticCellInfo, Code, !VLI) :-
|
|
check_var_is_unknown(!.VLI, Var),
|
|
( Lval0 = field(yes(Ptag), var(BaseVar), const(int_const(Offset))) ->
|
|
get_var_state_map(!.VLI, VarStateMap0),
|
|
map.lookup(VarStateMap0, BaseVar, BaseState),
|
|
BaseState = state(BaseVarLvals, MaybeConstBaseVarRval,
|
|
_MaybeExprRval, _UsingVars, _DeadOrAlive),
|
|
(
|
|
MaybeConstBaseVarRval = yes(BaseVarRval),
|
|
BaseVarRval = mkword(Ptag, BaseConst),
|
|
BaseConst = const(data_addr_const(DataAddr, MaybeBaseOffset)),
|
|
% XXX We could drop the MaybeBaseOffset = no condition,
|
|
% but this would require more complex code below.
|
|
MaybeBaseOffset = no,
|
|
search_scalar_static_cell_offset(StaticCellInfo, DataAddr, Offset,
|
|
SelectedArgRval)
|
|
->
|
|
MaybeConstRval = yes(SelectedArgRval),
|
|
Lvals = set.map(add_field_offset(yes(Ptag),
|
|
const(int_const(Offset))), BaseVarLvals),
|
|
set.init(Using),
|
|
State = state(Lvals, MaybeConstRval, no, Using, alive),
|
|
map.det_insert(VarStateMap0, Var, State, VarStateMap),
|
|
set_var_state_map(VarStateMap, !VLI),
|
|
|
|
get_loc_var_map(!.VLI, LocVarMap0),
|
|
make_var_depend_on_lvals_roots(Var, Lvals, LocVarMap0, LocVarMap),
|
|
set_loc_var_map(LocVarMap, !VLI)
|
|
;
|
|
set.init(Lvals),
|
|
Expr = lval(Lval0),
|
|
set.init(Using),
|
|
State = state(Lvals, no, yes(Expr), Using, alive),
|
|
map.det_insert(VarStateMap0, Var, State, VarStateMap1),
|
|
add_use_ref(BaseVar, Var, VarStateMap1, VarStateMap),
|
|
set_var_state_map(VarStateMap, !VLI)
|
|
),
|
|
Code = empty
|
|
;
|
|
materialize_vars_in_lval(ModuleInfo, Lval0, Lval, Code, !VLI),
|
|
|
|
get_var_state_map(!.VLI, VarStateMap0),
|
|
set.singleton_set(LvalSet, Lval),
|
|
State = state(LvalSet, no, no, set.init, alive),
|
|
map.det_insert(VarStateMap0, Var, State, VarStateMap),
|
|
set_var_state_map(VarStateMap, !VLI),
|
|
|
|
get_loc_var_map(!.VLI, LocVarMap0),
|
|
make_var_depend_on_lval_roots(Var, Lval, LocVarMap0, LocVarMap),
|
|
set_loc_var_map(LocVarMap, !VLI)
|
|
).
|
|
|
|
:- func add_field_offset(maybe(tag), rval, lval) = lval.
|
|
|
|
add_field_offset(Ptag, Offset, Base) =
|
|
field(Ptag, lval(Base), Offset).
|
|
|
|
%----------------------------------------------------------------------------%
|
|
|
|
assign_const_to_var(Var, ConstRval0, !VLI) :-
|
|
check_var_is_unknown(!.VLI, Var),
|
|
|
|
get_var_state_map(!.VLI, VarStateMap0),
|
|
get_exprn_opts(!.VLI, ExprnOpts),
|
|
( expr_is_constant(VarStateMap0, ExprnOpts, ConstRval0, ConstRval) ->
|
|
State = state(set.init, yes(ConstRval), no, set.init, alive),
|
|
map.det_insert(VarStateMap0, Var, State, VarStateMap),
|
|
set_var_state_map(VarStateMap, !VLI)
|
|
;
|
|
unexpected(this_file, "set_var_state_map: supposed constant isn't")
|
|
).
|
|
|
|
%----------------------------------------------------------------------------%
|
|
|
|
assign_expr_to_var(Var, Rval, empty, !VLI) :-
|
|
check_var_is_unknown(!.VLI, Var),
|
|
|
|
get_var_state_map(!.VLI, VarStateMap0),
|
|
State = state(set.init, no, yes(Rval), set.init, alive),
|
|
map.det_insert(VarStateMap0, Var, State, VarStateMap1),
|
|
|
|
exprn_aux.vars_in_rval(Rval, ContainedVars0),
|
|
list.remove_dups(ContainedVars0, ContainedVars),
|
|
add_use_refs(ContainedVars, Var, VarStateMap1, VarStateMap),
|
|
set_var_state_map(VarStateMap, !VLI).
|
|
|
|
:- pred add_use_refs(list(prog_var)::in, prog_var::in,
|
|
var_state_map::in, var_state_map::out) is det.
|
|
|
|
add_use_refs([], _, !VarStateMap).
|
|
add_use_refs([ContainedVar | ContainedVars], UsingVar, !VarStateMap) :-
|
|
add_use_ref(ContainedVar, UsingVar, !VarStateMap),
|
|
add_use_refs(ContainedVars, UsingVar, !VarStateMap).
|
|
|
|
:- pred add_use_ref(prog_var::in, prog_var::in,
|
|
var_state_map::in, var_state_map::out) is det.
|
|
|
|
add_use_ref(ContainedVar, UsingVar, !VarStateMap) :-
|
|
map.lookup(!.VarStateMap, ContainedVar, State0),
|
|
State0 = state(Lvals, MaybeConstRval, MaybeExprRval, Using0, DeadOrAlive),
|
|
set.insert(Using0, UsingVar, Using),
|
|
State = state(Lvals, MaybeConstRval, MaybeExprRval, Using, DeadOrAlive),
|
|
map.det_update(!.VarStateMap, ContainedVar, State, !:VarStateMap).
|
|
|
|
%----------------------------------------------------------------------------%
|
|
|
|
assign_cell_to_var(ModuleInfo, Var, ReserveWordAtStart, Ptag, MaybeRvals0,
|
|
SizeInfo, TypeMsg, Code, !StaticCellInfo, !VLI) :-
|
|
(
|
|
SizeInfo = yes(SizeSource),
|
|
(
|
|
SizeSource = known_size(Size),
|
|
SizeRval = const(int_const(Size))
|
|
;
|
|
SizeSource = dynamic_size(SizeVar),
|
|
SizeRval = var(SizeVar)
|
|
),
|
|
MaybeRvals = [yes(SizeRval) | MaybeRvals0],
|
|
MaybeOffset = yes(1)
|
|
;
|
|
SizeInfo = no,
|
|
MaybeRvals = MaybeRvals0,
|
|
MaybeOffset = no
|
|
),
|
|
get_var_state_map(!.VLI, VarStateMap),
|
|
get_exprn_opts(!.VLI, ExprnOpts),
|
|
( cell_is_constant(VarStateMap, ExprnOpts, MaybeRvals, RvalsTypes) ->
|
|
add_scalar_static_cell(RvalsTypes, DataAddr, !StaticCellInfo),
|
|
CellPtrConst = const(data_addr_const(DataAddr, MaybeOffset)),
|
|
CellPtrRval = mkword(Ptag, CellPtrConst),
|
|
assign_const_to_var(Var, CellPtrRval, !VLI),
|
|
Code = empty
|
|
;
|
|
assign_dynamic_cell_to_var(ModuleInfo, Var, ReserveWordAtStart, Ptag,
|
|
MaybeRvals, MaybeOffset, TypeMsg, Code, !VLI)
|
|
).
|
|
|
|
:- pred assign_dynamic_cell_to_var(module_info::in, prog_var::in, bool::in,
|
|
tag::in, list(maybe(rval))::in, maybe(int)::in, string::in,
|
|
code_tree::out, var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
assign_dynamic_cell_to_var(ModuleInfo, Var, ReserveWordAtStart, Ptag, Vector,
|
|
MaybeOffset, TypeMsg, Code, !VLI) :-
|
|
check_var_is_unknown(!.VLI, Var),
|
|
|
|
select_preferred_reg_or_stack_check(!.VLI, Var, Lval),
|
|
get_var_name(!.VLI, Var, VarName),
|
|
list.length(Vector, Size),
|
|
(
|
|
ReserveWordAtStart = yes,
|
|
(
|
|
MaybeOffset = yes(_),
|
|
% Accurate GC and term profiling both want to own the word
|
|
% before this object.
|
|
sorry(this_file, "accurate GC combined with term size profiling")
|
|
;
|
|
MaybeOffset = no,
|
|
TotalOffset = yes(1)
|
|
),
|
|
TotalSize = Size + 1
|
|
;
|
|
ReserveWordAtStart = no,
|
|
TotalOffset = MaybeOffset,
|
|
TotalSize = Size
|
|
),
|
|
CellCode = node([
|
|
incr_hp(Lval, yes(Ptag), TotalOffset,
|
|
const(int_const(TotalSize)), TypeMsg)
|
|
- string.append("Allocating heap for ", VarName)
|
|
]),
|
|
set_magic_var_location(Var, Lval, !VLI),
|
|
(
|
|
MaybeOffset = yes(Offset),
|
|
StartOffset = -Offset
|
|
;
|
|
MaybeOffset = no,
|
|
StartOffset = 0
|
|
),
|
|
assign_cell_args(ModuleInfo, Vector, yes(Ptag), lval(Lval), StartOffset,
|
|
ArgsCode, !VLI),
|
|
Code = tree(CellCode, ArgsCode).
|
|
|
|
:- pred assign_cell_args(module_info::in, list(maybe(rval))::in,
|
|
maybe(tag)::in, rval::in, int::in, code_tree::out,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
assign_cell_args(_, [], _, _, _, empty, !VLI).
|
|
assign_cell_args(ModuleInfo, [MaybeRval0 | MaybeRvals0], Ptag, Base, Offset,
|
|
Code, !VLI) :-
|
|
(
|
|
MaybeRval0 = yes(Rval0),
|
|
Target = field(Ptag, Base, const(int_const(Offset))),
|
|
( Rval0 = var(Var) ->
|
|
find_var_availability(!.VLI, Var, no, Avail),
|
|
(
|
|
Avail = available(Rval),
|
|
EvalCode = empty
|
|
;
|
|
Avail = needs_materialization,
|
|
materialize_var(ModuleInfo, Var, no, no, [], Rval, EvalCode,
|
|
!VLI)
|
|
),
|
|
get_vartypes(!.VLI, VarTypes),
|
|
map.lookup(VarTypes, Var, Type),
|
|
( is_dummy_argument_type(ModuleInfo, Type) ->
|
|
AssignCode = empty
|
|
;
|
|
add_additional_lval_for_var(Var, Target, !VLI),
|
|
get_var_name(!.VLI, Var, VarName),
|
|
Comment = "assigning from " ++ VarName,
|
|
AssignCode = node([assign(Target, Rval) - Comment])
|
|
)
|
|
; Rval0 = const(_) ->
|
|
EvalCode = empty,
|
|
Comment = "assigning field from const",
|
|
AssignCode = node([assign(Target, Rval0) - Comment])
|
|
;
|
|
unexpected(this_file, "assign_cell_args: unknown rval")
|
|
),
|
|
ThisCode = tree(EvalCode, AssignCode)
|
|
;
|
|
MaybeRval0 = no,
|
|
ThisCode = empty
|
|
),
|
|
assign_cell_args(ModuleInfo, MaybeRvals0, Ptag, Base, Offset + 1, RestCode,
|
|
!VLI),
|
|
Code = tree(ThisCode, RestCode).
|
|
|
|
%----------------------------------------------------------------------------%
|
|
|
|
% Record that Var is now available in Lval, as well as in the locations
|
|
% where it was available before.
|
|
|
|
:- pred add_additional_lval_for_var(prog_var::in, lval::in,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
add_additional_lval_for_var(Var, Lval, !VLI) :-
|
|
get_loc_var_map(!.VLI, LocVarMap0),
|
|
make_var_depend_on_lval_roots(Var, Lval, LocVarMap0, LocVarMap),
|
|
set_loc_var_map(LocVarMap, !VLI),
|
|
|
|
get_var_state_map(!.VLI, VarStateMap0),
|
|
map.lookup(VarStateMap0, Var, State0),
|
|
State0 = state(LvalSet0, MaybeConstRval, MaybeExprRval0,
|
|
Using, DeadOrAlive),
|
|
set.insert(LvalSet0, Lval, LvalSet),
|
|
State = state(LvalSet, MaybeConstRval, no, Using, DeadOrAlive),
|
|
map.det_update(VarStateMap0, Var, State, VarStateMap),
|
|
set_var_state_map(VarStateMap, !VLI),
|
|
|
|
remove_use_refs(MaybeExprRval0, Var, !VLI).
|
|
|
|
:- pred remove_use_refs(maybe(rval)::in, prog_var::in,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
remove_use_refs(MaybeExprRval, UsingVar, !VLI) :-
|
|
(
|
|
MaybeExprRval = yes(ExprRval),
|
|
exprn_aux.vars_in_rval(ExprRval, ContainedVars0),
|
|
list.remove_dups(ContainedVars0, ContainedVars),
|
|
remove_use_refs_2(ContainedVars, UsingVar, !VLI)
|
|
;
|
|
MaybeExprRval = no
|
|
).
|
|
|
|
:- pred remove_use_refs_2(list(prog_var)::in, prog_var::in,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
remove_use_refs_2([], _, !VLI).
|
|
remove_use_refs_2([ContainedVar | ContainedVars], UsingVar, !VLI) :-
|
|
get_var_state_map(!.VLI, VarStateMap0),
|
|
map.lookup(VarStateMap0, ContainedVar, State0),
|
|
State0 = state(Lvals, MaybeConstRval, MaybeExprRval, Using0, DeadOrAlive),
|
|
( set.remove(Using0, UsingVar, Using1) ->
|
|
Using = Using1
|
|
;
|
|
unexpected(this_file, "remove_use_refs_2: using ref not present")
|
|
),
|
|
State = state(Lvals, MaybeConstRval, MaybeExprRval, Using, DeadOrAlive),
|
|
map.det_update(VarStateMap0, ContainedVar, State, VarStateMap),
|
|
set_var_state_map(VarStateMap, !VLI),
|
|
(
|
|
set.empty(Using),
|
|
DeadOrAlive = dead
|
|
->
|
|
var_becomes_dead(ContainedVar, no, !VLI)
|
|
;
|
|
true
|
|
),
|
|
remove_use_refs_2(ContainedVars, UsingVar, !VLI).
|
|
|
|
%----------------------------------------------------------------------------%
|
|
|
|
check_and_set_magic_var_location(Var, Lval, !VLI) :-
|
|
( lval_in_use(!.VLI, Lval) ->
|
|
unexpected(this_file, "check_and_set_magic_var_location: in use")
|
|
;
|
|
set_magic_var_location(Var, Lval, !VLI)
|
|
).
|
|
|
|
set_magic_var_location(Var, Lval, !VLI) :-
|
|
get_loc_var_map(!.VLI, LocVarMap0),
|
|
make_var_depend_on_lval_roots(Var, Lval, LocVarMap0, LocVarMap),
|
|
set_loc_var_map(LocVarMap, !VLI),
|
|
|
|
get_var_state_map(!.VLI, VarStateMap0),
|
|
set.singleton_set(LvalSet, Lval),
|
|
State = state(LvalSet, no, no, set.init, alive),
|
|
map.det_insert(VarStateMap0, Var, State, VarStateMap),
|
|
set_var_state_map(VarStateMap, !VLI).
|
|
|
|
%----------------------------------------------------------------------------%
|
|
|
|
:- pred check_var_is_unknown(var_locn_info::in, prog_var::in) is det.
|
|
|
|
check_var_is_unknown(VLI, Var) :-
|
|
get_var_state_map(VLI, VarStateMap0),
|
|
( map.search(VarStateMap0, Var, _) ->
|
|
get_var_name(VLI, Var, Name),
|
|
Msg = "assign_to_var: existing definition of variable " ++ Name,
|
|
unexpected(this_file, Msg)
|
|
;
|
|
true
|
|
).
|
|
|
|
%----------------------------------------------------------------------------%
|
|
|
|
produce_var(ModuleInfo, Var, Rval, Code, !VLI) :-
|
|
get_var_state_map(!.VLI, VarStateMap),
|
|
map.lookup(VarStateMap, Var, State),
|
|
State = state(Lvals, MaybeConstRval, MaybeExprRval, _, _),
|
|
set.to_sorted_list(Lvals, LvalsList),
|
|
(
|
|
maybe_select_lval_or_rval(LvalsList, MaybeConstRval, Rval1)
|
|
->
|
|
Rval = Rval1,
|
|
Code = empty
|
|
;
|
|
MaybeExprRval = yes(var(ExprVar)),
|
|
map.lookup(VarStateMap, ExprVar, ExprState),
|
|
ExprState = state(ExprLvals, ExprMaybeConstRval, _, _, _),
|
|
set.to_sorted_list(ExprLvals, ExprLvalsList),
|
|
maybe_select_lval_or_rval(ExprLvalsList, ExprMaybeConstRval, Rval2)
|
|
->
|
|
% This path is designed to generate efficient code
|
|
% mainly for variables produced by unsafe_cast goals.
|
|
Rval = Rval2,
|
|
Code = empty
|
|
;
|
|
select_preferred_reg(!.VLI, Var, Lval),
|
|
place_var(ModuleInfo, Var, Lval, Code, !VLI),
|
|
Rval = lval(Lval)
|
|
).
|
|
|
|
produce_var_in_reg(ModuleInfo, Var, Lval, Code, !VLI) :-
|
|
get_var_state_map(!.VLI, VarStateMap),
|
|
map.lookup(VarStateMap, Var, State),
|
|
State = state(Lvals, _, _, _, _),
|
|
set.to_sorted_list(Lvals, LvalList),
|
|
( select_reg_lval(LvalList, SelectLval) ->
|
|
Lval = SelectLval,
|
|
Code = empty
|
|
;
|
|
select_preferred_reg(!.VLI, Var, Lval),
|
|
place_var(ModuleInfo, Var, Lval, Code, !VLI)
|
|
).
|
|
|
|
produce_var_in_reg_or_stack(ModuleInfo, Var, Lval, Code, !VLI) :-
|
|
get_var_state_map(!.VLI, VarStateMap),
|
|
map.lookup(VarStateMap, Var, State),
|
|
State = state(Lvals, _, _, _, _),
|
|
set.to_sorted_list(Lvals, LvalList),
|
|
( select_reg_or_stack_lval(LvalList, SelectLval) ->
|
|
Lval = SelectLval,
|
|
Code = empty
|
|
;
|
|
select_preferred_reg_or_stack_check(!.VLI, Var, Lval),
|
|
place_var(ModuleInfo, Var, Lval, Code, !VLI)
|
|
).
|
|
|
|
%----------------------------------------------------------------------------%
|
|
|
|
clear_r1(ModuleInfo, Code, !VLI) :-
|
|
free_up_lval(ModuleInfo, reg(r, 1), [], [], Code, !VLI),
|
|
get_loc_var_map(!.VLI, LocVarMap0),
|
|
get_var_state_map(!.VLI, VarStateMap0),
|
|
clobber_regs_in_maps([reg(r, 1)], no,
|
|
LocVarMap0, LocVarMap, VarStateMap0, VarStateMap),
|
|
set_loc_var_map(LocVarMap, !VLI),
|
|
set_var_state_map(VarStateMap, !VLI).
|
|
|
|
place_vars(ModuleInfo, VarLocns, Code, !VLI) :-
|
|
% If we are asked to place several variables, then we must make sure that
|
|
% in the process of freeing up an lval for one variable, we do not save its
|
|
% previous contents to a location that VarLocns assigns to another
|
|
% variable. This is why we lock the registers used by VarLocns.
|
|
% (We don't need to lock stack slots, since stack slot allocation is
|
|
% required to ensure that the sets of variables that need to be saved
|
|
% across calls or at entries to goals with resume points all have distinct
|
|
% stack slots.) However, we do make one exception: if the variable being
|
|
% moved by a freeing up operation is in VarLocns, then it is OK to move it
|
|
% to the location assigned to it by VarLocns.
|
|
assoc_list.values(VarLocns, Lvals),
|
|
code_util.max_mentioned_reg(Lvals, MaxReg),
|
|
lock_regs(MaxReg, VarLocns, !VLI),
|
|
actually_place_vars(ModuleInfo, VarLocns, Code, !VLI),
|
|
unlock_regs(!VLI).
|
|
|
|
:- pred actually_place_vars(module_info::in, assoc_list(prog_var, lval)::in,
|
|
code_tree::out, var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
actually_place_vars(_, [], empty, !VLI).
|
|
actually_place_vars(ModuleInfo, [Var - Lval | Rest], Code, !VLI) :-
|
|
place_var(ModuleInfo, Var, Lval, FirstCode, !VLI),
|
|
actually_place_vars(ModuleInfo, Rest, RestCode, !VLI),
|
|
Code = tree(FirstCode, RestCode).
|
|
|
|
place_var(ModuleInfo, Var, Target, Code, !VLI) :-
|
|
actually_place_var(ModuleInfo, Var, Target, [], Code, !VLI).
|
|
|
|
:- pred actually_place_var(module_info::in, prog_var::in, lval::in,
|
|
list(lval)::in, code_tree::out, var_locn_info::in, var_locn_info::out)
|
|
is det.
|
|
|
|
actually_place_var(ModuleInfo, Var, Target, ForbiddenLvals, Code, !VLI) :-
|
|
get_acquired(!.VLI, Acquired),
|
|
( set.member(Target, Acquired) ->
|
|
unexpected(this_file, "actually_place_var: target is acquired reg")
|
|
;
|
|
true
|
|
),
|
|
get_var_state_map(!.VLI, VarStateMap0),
|
|
map.lookup(VarStateMap0, Var, State0),
|
|
State0 = state(Lvals0, _, _, _, _),
|
|
( set.member(Target, Lvals0) ->
|
|
Code = empty
|
|
;
|
|
free_up_lval(ModuleInfo, Target, [Var], ForbiddenLvals, FreeCode,
|
|
!VLI),
|
|
|
|
% If Var's value is cached, Lvals0 must be empty. However, the cached
|
|
% value may simply be var(Other), and Other may already be in Target.
|
|
% However, it may also be in another lval, so we say we prefer the
|
|
% copy in Target.
|
|
find_var_availability(!.VLI, Var, yes(Target), Avail),
|
|
(
|
|
Avail = available(Rval),
|
|
EvalCode = empty,
|
|
( Rval = lval(SourceLval) ->
|
|
record_copy(SourceLval, Target, !VLI)
|
|
;
|
|
record_clobbering(Target, [Var], !VLI)
|
|
)
|
|
;
|
|
Avail = needs_materialization,
|
|
materialize_var(ModuleInfo, Var, yes(Target), no, [Target], Rval,
|
|
EvalCode, !VLI),
|
|
record_clobbering(Target, [Var], !VLI)
|
|
),
|
|
|
|
% Record that Var is now in Target.
|
|
add_additional_lval_for_var(Var, Target, !VLI),
|
|
|
|
( Rval = lval(Target) ->
|
|
AssignCode = empty
|
|
;
|
|
get_var_name(!.VLI, Var, VarName),
|
|
(
|
|
ForbiddenLvals = [],
|
|
string.append("Placing ", VarName, Msg)
|
|
;
|
|
ForbiddenLvals = [_ | _],
|
|
string.int_to_string(list.length(ForbiddenLvals),
|
|
LengthStr),
|
|
string.append_list(["Placing ", VarName,
|
|
" (depth ", LengthStr, ")"], Msg)
|
|
),
|
|
get_vartypes(!.VLI, VarTypes),
|
|
map.lookup(VarTypes, Var, Type),
|
|
( is_dummy_argument_type(ModuleInfo, Type) ->
|
|
AssignCode = empty
|
|
;
|
|
AssignCode = node([assign(Target, Rval) - Msg])
|
|
)
|
|
),
|
|
Code = tree(FreeCode, tree(EvalCode, AssignCode))
|
|
).
|
|
|
|
:- pred record_clobbering(lval::in, list(prog_var)::in,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
record_clobbering(Target, Assigns, !VLI) :-
|
|
get_loc_var_map(!.VLI, LocVarMap1),
|
|
( map.search(LocVarMap1, Target, DependentVarsSet) ->
|
|
set.to_sorted_list(DependentVarsSet, DependentVars),
|
|
map.delete(LocVarMap1, Target, LocVarMap),
|
|
set_loc_var_map(LocVarMap, !VLI),
|
|
|
|
get_var_state_map(!.VLI, VarStateMap2),
|
|
list.foldl(clobber_lval_in_var_state_map(Target, Assigns, no),
|
|
DependentVars, VarStateMap2, VarStateMap),
|
|
set_var_state_map(VarStateMap, !VLI)
|
|
;
|
|
true
|
|
).
|
|
|
|
% Make Lval available, i.e. make sure that the values of all variables
|
|
% that are stored in an lval involving Lval are also available in places
|
|
% not dependent on Lval. However, this requirement does not apply to the
|
|
% variables (if any) in ToBeAssignedVars, since this lists the variable
|
|
% that is to be assigned to Lval after it is freed up. (If ToBeAssignedVars
|
|
% contains more than one variable, then those variables must be guaranteed
|
|
% to be equal.) Nor does it apply to dead variables whose only use is as
|
|
% components of AssignedVars.
|
|
%
|
|
% The point of this exception is to eliminate unnecessary shuffles.
|
|
% If place_var wants to put Var in Lval and Var is currently in (e.g)
|
|
% field(Ptag, Lval, Offset), it will ask free_up_lval to free up
|
|
% Lval. However, if all the other variables affected variables are also
|
|
% available independently of Lval, there should be no need to move
|
|
% the value now in Lval somewhere else, since our caller can simply
|
|
% generate an assignment such as Lval := field(Ptag, Lval, Offset).
|
|
%
|
|
:- pred free_up_lval(module_info::in, lval::in, list(prog_var)::in,
|
|
list(lval)::in, code_tree::out, var_locn_info::in, var_locn_info::out)
|
|
is det.
|
|
|
|
free_up_lval(ModuleInfo, Lval, ToBeAssignedVars, ForbiddenLvals, Code, !VLI) :-
|
|
(
|
|
get_loc_var_map(!.VLI, LocVarMap0),
|
|
map.search(LocVarMap0, Lval, AffectedVarSet),
|
|
set.to_sorted_list(AffectedVarSet, AffectedVars),
|
|
get_var_state_map(!.VLI, VarStateMap0),
|
|
\+ list.foldl(
|
|
try_clobber_lval_in_var_state_map(Lval, ToBeAssignedVars, no),
|
|
AffectedVars, VarStateMap0, _)
|
|
->
|
|
free_up_lval_with_copy(ModuleInfo, Lval, ToBeAssignedVars,
|
|
ForbiddenLvals, Code, !VLI)
|
|
;
|
|
Code = empty
|
|
).
|
|
|
|
% If we must copy the value in Lval somewhere else to prevent it from
|
|
% being lost when Lval overwritten, then we try to put it into a location
|
|
% where it will be needed next. First we find a variable that is stored
|
|
% in Lval directly, and not just in some location whose path includes Lval
|
|
% (the set of all variables affected by the update of Lval is
|
|
% AffectedVarSet). Then we look up where that variable (OccupyingVar)
|
|
% ought to be. If its desired location is Lval itself, then the copy
|
|
% would be a null operation and would not free up Lval, so in that case
|
|
% we get a spare register. If the desired location is on the forbidden
|
|
% list, then we again get a spare register to avoid infinite recursion
|
|
% (see the documentation of free_up_lval above). If the desired location
|
|
% (Pref) is neither Lval nor or on the forbidden list, then we can possibly
|
|
% copy Lval there. If Pref is neither in use nor locked, then moving Lval
|
|
% there requires just an assignment. If Pref is locked, then it is possible
|
|
% that it is locked for use by OccupyingVar. If this is so, we first
|
|
% recursively free up Pref, and then move OccupyingVar there.
|
|
%
|
|
:- pred free_up_lval_with_copy(module_info::in, lval::in, list(prog_var)::in,
|
|
list(lval)::in, code_tree::out, var_locn_info::in, var_locn_info::out)
|
|
is det.
|
|
|
|
free_up_lval_with_copy(ModuleInfo, Lval, ToBeAssignedVars, ForbiddenLvals,
|
|
Code, !VLI) :-
|
|
(
|
|
get_loc_var_map(!.VLI, LocVarMap0),
|
|
map.search(LocVarMap0, Lval, AffectedVarSet),
|
|
set.delete_list(AffectedVarSet, ToBeAssignedVars, EffAffectedVarSet),
|
|
set.to_sorted_list(EffAffectedVarSet, EffAffectedVars),
|
|
|
|
get_var_state_map(!.VLI, VarStateMap0),
|
|
(
|
|
find_one_occupying_var(EffAffectedVars, Lval, VarStateMap0,
|
|
OccupyingVar, OtherSources)
|
|
->
|
|
MovedVar = OccupyingVar,
|
|
list.delete_all(EffAffectedVars, MovedVar, OtherVars),
|
|
list.foldl(ensure_copies_are_present(Lval, OtherSources),
|
|
OtherVars, !VLI)
|
|
;
|
|
EffAffectedVars = [MovedVar]
|
|
),
|
|
|
|
CheckInUse = no,
|
|
select_preferred_reg_or_stack(!.VLI, MovedVar, Pref, CheckInUse),
|
|
\+ Pref = Lval,
|
|
\+ list.member(Pref, ForbiddenLvals),
|
|
( \+ lval_in_use(!.VLI, Pref) ->
|
|
true
|
|
;
|
|
% The code generator assumes that values in stack slots don't get
|
|
% clobbered without an explicit assignment (via a place_var
|
|
% operation with a stack var as a target).
|
|
Pref = reg(r, RegNum),
|
|
reg_is_not_locked_for_var(!.VLI, RegNum, MovedVar)
|
|
)
|
|
->
|
|
actually_place_var(ModuleInfo, MovedVar, Pref, [Lval | ForbiddenLvals],
|
|
Code, !VLI)
|
|
;
|
|
get_spare_reg(!.VLI, Target),
|
|
record_copy(Lval, Target, !VLI),
|
|
(
|
|
( Lval = stackvar(N)
|
|
; Lval = framevar(N)
|
|
),
|
|
N < 0
|
|
->
|
|
% We must not copy from invalid lvals. The value we would copy
|
|
% is a dummy in any case, so Target won't be any more valid
|
|
% if we assigned Lval to it.
|
|
Code = empty
|
|
;
|
|
Code = node([
|
|
assign(Target, lval(Lval)) - "Freeing up the source lval"
|
|
])
|
|
)
|
|
).
|
|
|
|
% Find a variable in the given list that is currently stored directly
|
|
% in Lval (not just in some location who address includes Lval).
|
|
%
|
|
:- pred find_one_occupying_var(list(prog_var)::in, lval::in,
|
|
var_state_map::in, prog_var::out, list(lval)::out) is semidet.
|
|
|
|
find_one_occupying_var([Var | Vars], Lval, VarStateMap, OccupyingVar,
|
|
OtherSources) :-
|
|
map.lookup(VarStateMap, Var, State),
|
|
State = state(LvalSet, _, _, _, _),
|
|
( set.member(Lval, LvalSet) ->
|
|
OccupyingVar = Var,
|
|
set.delete(LvalSet, Lval, OtherSourceSet),
|
|
set.to_sorted_list(OtherSourceSet, OtherSources)
|
|
;
|
|
find_one_occupying_var(Vars, Lval, VarStateMap, OccupyingVar,
|
|
OtherSources)
|
|
).
|
|
|
|
:- pred ensure_copies_are_present(lval::in, list(lval)::in,
|
|
prog_var::in, var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
ensure_copies_are_present(OneSource, OtherSources, Var, !VLI) :-
|
|
get_var_state_map(!.VLI, VarStateMap0),
|
|
map.lookup(VarStateMap0, Var, State0),
|
|
State0 = state(LvalSet0, MaybeConstRval, MaybeExprRval, Using,
|
|
DeadOrAlive),
|
|
set.to_sorted_list(LvalSet0, Lvals0),
|
|
list.foldl(ensure_copies_are_present_lval(OtherSources, OneSource),
|
|
Lvals0, LvalSet0, LvalSet),
|
|
State = state(LvalSet, MaybeConstRval, MaybeExprRval, Using, DeadOrAlive),
|
|
map.det_update(VarStateMap0, Var, State, VarStateMap),
|
|
set_var_state_map(VarStateMap, !VLI),
|
|
|
|
get_loc_var_map(!.VLI, LocVarMap0),
|
|
record_change_in_root_dependencies(LvalSet0, LvalSet, Var,
|
|
LocVarMap0, LocVarMap),
|
|
set_loc_var_map(LocVarMap, !VLI).
|
|
|
|
:- pred ensure_copies_are_present_lval(list(lval)::in, lval::in,
|
|
lval::in, set(lval)::in, set(lval)::out) is det.
|
|
|
|
ensure_copies_are_present_lval([], _, _, !LvalSet).
|
|
ensure_copies_are_present_lval([OtherSource | OtherSources], OneSource, Lval,
|
|
!LvalSet) :-
|
|
SubstLval = substitute_lval_in_lval(OneSource, OtherSource, Lval),
|
|
set.insert(!.LvalSet, SubstLval, !:LvalSet),
|
|
ensure_copies_are_present_lval(OtherSources, OneSource, Lval, !LvalSet).
|
|
|
|
%----------------------------------------------------------------------------%
|
|
|
|
% Record the effect of the assignment New := Old on the state of all the
|
|
% affected variables.
|
|
%
|
|
% We find the set of affected variables by finding all the root lvals in
|
|
% New and Old, and finding all the variables that depend on them. This
|
|
% requires significant numbers of term traversals and lookup operations.
|
|
% We could eliminate this cost by considering *all* variables to be
|
|
% affected. Even though it would obviously call record_copy_for_var
|
|
% on more variables, this may be faster overall. The reason why we
|
|
% don't do that is that its worst case behavior can be pretty bad.
|
|
%
|
|
:- pred record_copy(lval::in, lval::in,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
record_copy(Old, New, !VLI) :-
|
|
expect(is_root_lval(New), this_file, "record_copy: non-root New lval"),
|
|
get_var_state_map(!.VLI, VarStateMap0),
|
|
get_loc_var_map(!.VLI, LocVarMap0),
|
|
set.list_to_set([Old, New], AssignSet),
|
|
get_var_set_roots(AssignSet, NoDupRootLvals),
|
|
% Convert the list of root lvals to the list of sets of
|
|
% affected vars; if a root lval is not in LocVarMap0,
|
|
% then it does not affect any variables.
|
|
list.filter_map(map.search(LocVarMap0), NoDupRootLvals,
|
|
AffectedVarSets),
|
|
% Take the union of the list of sets of affected vars.
|
|
list.foldl(set.union, AffectedVarSets,
|
|
set.init, AffectedVarSet),
|
|
% Convert the union set to a list of affected vars.
|
|
set.to_sorted_list(AffectedVarSet, AffectedVars),
|
|
list.foldl2(record_copy_for_var(Old, New), AffectedVars,
|
|
VarStateMap0, VarStateMap, LocVarMap0, LocVarMap),
|
|
set_loc_var_map(LocVarMap, !VLI),
|
|
set_var_state_map(VarStateMap, !VLI).
|
|
|
|
% Record the effect of the assignment New := Old on the state of the given
|
|
% variable.
|
|
%
|
|
% The main complication is that New and Old are not necessarily
|
|
% independent: it is possible e.g. for New to be r1 and Old to be
|
|
% field(0, r1, 2). This is why we perform the update in three steps.
|
|
%
|
|
% 1 For each lval in original LvalSet that contains Old, we add an
|
|
% additional lval in which Old is replaced by a unique Token lval
|
|
% (which cannot be a component of any legitimate lval). The Token
|
|
% represents the value being assigned, and prevents us from forgetting
|
|
% the path to the original value of Var from Old during step 2.
|
|
%
|
|
% 2 We delete from the set generated by step 1 all lvals that depend
|
|
% on New, to reflect the fact that New no longer contains what it
|
|
% used to contain.
|
|
%
|
|
% 3 We substitute New for all occurrences of Token, to reflect the fact
|
|
% that the assigned value is now in New.
|
|
%
|
|
% For example, with New and Old being as above and LvalSet0 being the set
|
|
% r5, field(3, field(0, r1, 2), 4):
|
|
%
|
|
% - Step 1 will set LvalSet1 to r5, field(3, Token, 4), and LvalSet2
|
|
% to r5, field(3, field(0, r1, 2), 4), field(3, Token, 4).
|
|
%
|
|
% - Step 2 will set LvalSet3 r5, field(3, Token, 4).
|
|
%
|
|
% - Step 3 will set LvalSet r5, field(3, r1, 4).
|
|
%
|
|
% The reason why we don't need to modify the MaybeExprRval field in the
|
|
% variable state is that the only lvals these fields can refer to are
|
|
% of the form var(_).
|
|
%
|
|
:- pred record_copy_for_var(lval::in, lval::in, prog_var::in,
|
|
var_state_map::in, var_state_map::out,
|
|
loc_var_map::in, loc_var_map::out) is det.
|
|
|
|
record_copy_for_var(Old, New, Var, !VarStateMap, !LocVarMap) :-
|
|
map.lookup(!.VarStateMap, Var, State0),
|
|
State0 = state(LvalSet0, MaybeConstRval, MaybeExprRval,
|
|
Using, DeadOrAlive),
|
|
Token = reg(r, -42),
|
|
LvalSet1 = set.map(substitute_lval_in_lval(Old, Token), LvalSet0),
|
|
set.union(LvalSet0, LvalSet1, LvalSet2),
|
|
LvalSet3 = set.filter(lval_does_not_support_lval(New), LvalSet2),
|
|
LvalSet = set.map(substitute_lval_in_lval(Token, New), LvalSet3),
|
|
State = state(LvalSet, MaybeConstRval, MaybeExprRval,
|
|
Using, DeadOrAlive),
|
|
expect(nonempty_state(State), this_file,
|
|
"record_copy_for_var: empty state"),
|
|
map.det_update(!.VarStateMap, Var, State, !:VarStateMap),
|
|
record_change_in_root_dependencies(LvalSet0, LvalSet, Var, !LocVarMap).
|
|
|
|
:- pred record_change_in_root_dependencies(set(lval)::in,
|
|
set(lval)::in, prog_var::in, loc_var_map::in, loc_var_map::out) is det.
|
|
|
|
record_change_in_root_dependencies(OldLvalSet, NewLvalSet, Var, !LocVarMap) :-
|
|
get_var_set_roots(OldLvalSet, OldRootLvals),
|
|
get_var_set_roots(NewLvalSet, NewRootLvals),
|
|
set.list_to_set(OldRootLvals, OldRootLvalSet),
|
|
set.list_to_set(NewRootLvals, NewRootLvalSet),
|
|
set.difference(NewRootLvalSet, OldRootLvalSet, InsertSet),
|
|
set.difference(OldRootLvalSet, NewRootLvalSet, DeleteSet),
|
|
set.to_sorted_list(InsertSet, Inserts),
|
|
set.to_sorted_list(DeleteSet, Deletes),
|
|
list.foldl(make_var_depend_on_root_lval(Var), Inserts, !LocVarMap),
|
|
list.foldl(make_var_not_depend_on_root_lval(Var), Deletes, !LocVarMap).
|
|
|
|
:- func substitute_lval_in_lval(lval, lval, lval) = lval.
|
|
|
|
substitute_lval_in_lval(Old, New, Lval0) = Lval :-
|
|
exprn_aux.substitute_lval_in_lval(Old, New, Lval0, Lval).
|
|
|
|
%----------------------------------------------------------------------------%
|
|
|
|
% Var has become dead. If there are no expressions that depend on its
|
|
% value, delete the record of its state, thus freeing up the resources
|
|
% it has tied down: the locations it occupies, or the variables whose
|
|
% values its own expression refers to. If there *are* expressions that
|
|
% depend on its value, merely update the state of the variable to say
|
|
% that it is dead, which means that its resources will be freed when
|
|
% the last reference to its value is deleted.
|
|
%
|
|
% If FirstTime = no, then it is possible that this predicate has already
|
|
% been called for Var, if FirstTime = yes, then as a consistency check
|
|
% we would like to insist on Var being alive (but don't (yet) due to bugs
|
|
% in liveness).
|
|
%
|
|
var_becomes_dead(Var, FirstTime, !VLI) :-
|
|
get_var_state_map(!.VLI, VarStateMap0),
|
|
( map.search(VarStateMap0, Var, State0) ->
|
|
State0 = state(Lvals, MaybeConstRval, MaybeExprRval, Using,
|
|
DeadOrAlive0),
|
|
(
|
|
DeadOrAlive0 = dead,
|
|
expect(unify(FirstTime, no), this_file,
|
|
"var_becomes_dead: already dead")
|
|
;
|
|
DeadOrAlive0 = alive
|
|
),
|
|
( set.empty(Using) ->
|
|
map.det_remove(VarStateMap0, Var, _, VarStateMap),
|
|
set_var_state_map(VarStateMap, !VLI),
|
|
|
|
get_loc_var_map(!.VLI, LocVarMap0),
|
|
get_var_set_roots(Lvals, NoDupRootLvals),
|
|
list.foldl(make_var_not_depend_on_root_lval(Var),
|
|
NoDupRootLvals, LocVarMap0, LocVarMap),
|
|
set_loc_var_map(LocVarMap, !VLI),
|
|
|
|
remove_use_refs(MaybeExprRval, Var, !VLI)
|
|
;
|
|
State = state(Lvals, MaybeConstRval, MaybeExprRval, Using, dead),
|
|
map.det_update(VarStateMap0, Var, State, VarStateMap),
|
|
set_var_state_map(VarStateMap, !VLI)
|
|
)
|
|
;
|
|
expect(unify(FirstTime, no), this_file,
|
|
"var_becomes_dead: premature deletion")
|
|
).
|
|
|
|
% Given a set of lvals, return the set of root lvals among them and inside
|
|
% them.
|
|
%
|
|
:- pred get_var_set_roots(set(lval)::in, list(lval)::out) is det.
|
|
|
|
get_var_set_roots(Lvals, NoDupRootLvals) :-
|
|
set.to_sorted_list(Lvals, LvalList),
|
|
code_util.lvals_in_lvals(LvalList, ContainedLvals),
|
|
list.append(LvalList, ContainedLvals, AllLvals),
|
|
list.filter(is_root_lval, AllLvals, RootLvals),
|
|
list.sort_and_remove_dups(RootLvals, NoDupRootLvals).
|
|
|
|
%----------------------------------------------------------------------------%
|
|
|
|
% Select the cheapest way to refer to the value of the variable.
|
|
% From the given list of lvals, select the cheapest one to use.
|
|
%
|
|
:- pred select_lval(list(lval)::in, lval::out) is det.
|
|
|
|
select_lval(Lvals, Lval) :-
|
|
( select_reg_lval(Lvals, Lval1) ->
|
|
Lval = Lval1
|
|
; select_stack_lval(Lvals, Lval2) ->
|
|
Lval = Lval2
|
|
; select_cheapest_lval(Lvals, Lval3) ->
|
|
Lval = Lval3
|
|
;
|
|
unexpected(this_file, "select_lval: nothing to select")
|
|
).
|
|
|
|
% From the given list of lvals and maybe a constant rval, select the
|
|
% cheapest one to use.
|
|
%
|
|
:- pred select_lval_or_rval(list(lval)::in, maybe(rval)::in, rval::out) is det.
|
|
|
|
select_lval_or_rval(Lvals, MaybeConstRval, Rval) :-
|
|
( maybe_select_lval_or_rval(Lvals, MaybeConstRval, Rval1) ->
|
|
Rval = Rval1
|
|
;
|
|
unexpected(this_file, "select_lval_or_rval: nothing to select")
|
|
).
|
|
|
|
:- pred maybe_select_lval_or_rval(list(lval)::in, maybe(rval)::in,
|
|
rval::out) is semidet.
|
|
|
|
maybe_select_lval_or_rval(Lvals, MaybeConstRval, Rval) :-
|
|
( select_reg_lval(Lvals, Lval1) ->
|
|
Rval = lval(Lval1)
|
|
; select_stack_lval(Lvals, Lval2) ->
|
|
Rval = lval(Lval2)
|
|
; MaybeConstRval = yes(ConstRval) ->
|
|
Rval = ConstRval
|
|
; select_cheapest_lval(Lvals, Lval3) ->
|
|
Rval = lval(Lval3)
|
|
;
|
|
fail
|
|
).
|
|
|
|
:- pred select_reg_lval(list(lval)::in, lval::out) is semidet.
|
|
|
|
select_reg_lval([Lval0 | Lvals0], Lval) :-
|
|
( Lval0 = reg(_, _) ->
|
|
Lval = Lval0
|
|
;
|
|
select_reg_lval(Lvals0, Lval)
|
|
).
|
|
|
|
:- pred select_stack_lval(list(lval)::in, lval::out) is semidet.
|
|
|
|
select_stack_lval([Lval0 | Lvals0], Lval) :-
|
|
( ( Lval0 = stackvar(_) ; Lval0 = framevar(_)) ->
|
|
Lval = Lval0
|
|
;
|
|
select_stack_lval(Lvals0, Lval)
|
|
).
|
|
|
|
:- pred select_reg_or_stack_lval(list(lval)::in, lval::out)
|
|
is semidet.
|
|
|
|
select_reg_or_stack_lval([Lval0 | Lvals0], Lval) :-
|
|
(
|
|
( Lval0 = reg(_, _)
|
|
; Lval0 = stackvar(_)
|
|
; Lval0 = framevar(_)
|
|
)
|
|
->
|
|
Lval = Lval0
|
|
;
|
|
select_reg_or_stack_lval(Lvals0, Lval)
|
|
).
|
|
|
|
% From the given list of lvals, select the cheapest one to use.
|
|
% Since none of the lvals will be a register or stack variable,
|
|
% in almost all cases, the given list will be a singleton.
|
|
%
|
|
:- pred select_cheapest_lval(list(lval)::in, lval::out) is semidet.
|
|
|
|
select_cheapest_lval([Lval | _], Lval).
|
|
|
|
%----------------------------------------------------------------------------%
|
|
|
|
:- pred select_preferred_reg_avoid(var_locn_info::in, prog_var::in,
|
|
list(lval)::in, lval::out) is det.
|
|
|
|
select_preferred_reg_avoid(VLI, Var, Avoid, Lval) :-
|
|
select_preferred_reg(VLI, Var, yes, Avoid, Lval).
|
|
|
|
:- pred select_preferred_reg(var_locn_info::in, prog_var::in,
|
|
lval::out) is det.
|
|
|
|
select_preferred_reg(VLI, Var, Lval) :-
|
|
select_preferred_reg(VLI, Var, yes, [], Lval).
|
|
|
|
% Select the register into which Var should be put. If the follow_vars map
|
|
% maps Var to a register, then select that register, unless it is already
|
|
% in use, and CheckInUse = yes.
|
|
%
|
|
:- pred select_preferred_reg(var_locn_info::in, prog_var::in,
|
|
bool::in, list(lval)::in, lval::out) is det.
|
|
|
|
select_preferred_reg(VLI, Var, CheckInUse, Avoid, Lval) :-
|
|
get_follow_var_map(VLI, FollowVarMap),
|
|
(
|
|
map.search(FollowVarMap, Var, PrefLocn),
|
|
( PrefLocn = abs_reg(_)
|
|
; PrefLocn = any_reg
|
|
)
|
|
->
|
|
(
|
|
PrefLocn = abs_reg(N),
|
|
PrefLval = reg(r, N),
|
|
(
|
|
CheckInUse = yes,
|
|
\+ lval_in_use(VLI, PrefLval)
|
|
;
|
|
CheckInUse = no
|
|
),
|
|
\+ list.member(PrefLval, Avoid)
|
|
->
|
|
Lval = PrefLval
|
|
;
|
|
get_spare_reg_avoid(VLI, Avoid, Lval)
|
|
)
|
|
;
|
|
get_spare_reg_avoid(VLI, Avoid, Lval)
|
|
).
|
|
|
|
% Select the register or stack slot into which Var should be put. If the
|
|
% follow_vars map maps Var to a register, then select that register,
|
|
% unless it is already in use and CheckInUse = yes. If the follow_vars map
|
|
% does not contain Var, then Var is not needed in a register in the near
|
|
% future, and this we select Var's stack slot, unless it is in use and
|
|
% CheckInUse = yes. If all else fails, we get spare, unused register.
|
|
% (Note that if the follow_vars pass has not been run, then all follow vars
|
|
% maps will be empty, which would cause this predicate to try to put far
|
|
% too many things in stack slots.)
|
|
%
|
|
:- pred select_preferred_reg_or_stack_check(var_locn_info::in,
|
|
prog_var::in, lval::out) is det.
|
|
|
|
select_preferred_reg_or_stack_check(VLI, Var, Lval) :-
|
|
select_preferred_reg_or_stack(VLI, Var, Lval, yes).
|
|
|
|
:- pred select_preferred_reg_or_stack(var_locn_info::in,
|
|
prog_var::in, lval::out, bool::in) is det.
|
|
|
|
select_preferred_reg_or_stack(VLI, Var, Lval, CheckInUse) :-
|
|
get_follow_var_map(VLI, FollowVarMap),
|
|
(
|
|
map.search(FollowVarMap, Var, PrefLocn),
|
|
( PrefLocn = abs_reg(_)
|
|
; PrefLocn = any_reg
|
|
)
|
|
->
|
|
(
|
|
PrefLocn = abs_reg(N),
|
|
PrefLval = reg(r, N),
|
|
(
|
|
CheckInUse = yes,
|
|
\+ lval_in_use(VLI, PrefLval)
|
|
;
|
|
CheckInUse = no
|
|
)
|
|
->
|
|
Lval = PrefLval
|
|
;
|
|
get_spare_reg(VLI, Lval)
|
|
)
|
|
;
|
|
(
|
|
get_stack_slots(VLI, StackSlots),
|
|
map.search(StackSlots, Var, StackSlotLocn),
|
|
StackSlot = stack_slot_to_lval(StackSlotLocn),
|
|
(
|
|
CheckInUse = yes,
|
|
\+ lval_in_use(VLI, StackSlot)
|
|
;
|
|
CheckInUse = no
|
|
)
|
|
->
|
|
Lval = StackSlot
|
|
;
|
|
get_spare_reg(VLI, Lval)
|
|
)
|
|
).
|
|
|
|
:- pred real_lval(lval::in) is semidet.
|
|
|
|
real_lval(Lval) :-
|
|
\+ (
|
|
Lval = reg(_, N),
|
|
N < 1
|
|
).
|
|
|
|
%----------------------------------------------------------------------------%
|
|
|
|
% Get a register that is not in use. We start the search at the next
|
|
% register that is needed for the next call.
|
|
%
|
|
:- pred get_spare_reg_avoid(var_locn_info::in, list(lval)::in,
|
|
lval::out) is det.
|
|
|
|
get_spare_reg_avoid(VLI, Avoid, Lval) :-
|
|
get_next_non_reserved(VLI, NextNonReserved),
|
|
get_spare_reg_2(VLI, Avoid, NextNonReserved, Lval).
|
|
|
|
:- pred get_spare_reg(var_locn_info::in, lval::out) is det.
|
|
|
|
get_spare_reg(VLI, Lval) :-
|
|
get_next_non_reserved(VLI, NextNonReserved),
|
|
get_spare_reg_2(VLI, [], NextNonReserved, Lval).
|
|
|
|
:- pred get_spare_reg_2(var_locn_info::in, list(lval)::in, int::in,
|
|
lval::out) is det.
|
|
|
|
get_spare_reg_2(VLI, Avoid, N0, Lval) :-
|
|
TryLval = reg(r, N0),
|
|
( lval_in_use(VLI, TryLval) ->
|
|
get_spare_reg_2(VLI, Avoid, N0 + 1, Lval)
|
|
; list.member(TryLval, Avoid) ->
|
|
get_spare_reg_2(VLI, Avoid, N0 + 1, Lval)
|
|
;
|
|
Lval = TryLval
|
|
).
|
|
|
|
lval_in_use(VLI, Lval) :-
|
|
get_loc_var_map(VLI, LocVarMap),
|
|
get_acquired(VLI, Acquired),
|
|
get_locked(VLI, Locked),
|
|
(
|
|
map.search(LocVarMap, Lval, UsingVars),
|
|
\+ set.empty(UsingVars)
|
|
;
|
|
set.member(Lval, Acquired)
|
|
;
|
|
Lval = reg(r, N),
|
|
N =< Locked
|
|
).
|
|
|
|
% Succeeds if Var may be stored in Reg, possibly after copying its contents
|
|
% somewhere else. This requires Reg to be either not locked, or if it is
|
|
% locked, to be locked for Var.
|
|
%
|
|
:- pred reg_is_not_locked_for_var(var_locn_info::in, int::in, prog_var::in)
|
|
is semidet.
|
|
|
|
reg_is_not_locked_for_var(VLI, RegNum, Var) :-
|
|
get_acquired(VLI, Acquired),
|
|
get_locked(VLI, Locked),
|
|
get_exceptions(VLI, Exceptions),
|
|
Reg = reg(r, RegNum),
|
|
\+ set.member(Reg, Acquired),
|
|
RegNum =< Locked => list.member(Var - Reg, Exceptions).
|
|
|
|
%----------------------------------------------------------------------------%
|
|
|
|
acquire_reg(Lval, !VLI) :-
|
|
get_spare_reg(!.VLI, Lval),
|
|
get_acquired(!.VLI, Acquired0),
|
|
set.insert(Acquired0, Lval, Acquired),
|
|
set_acquired(Acquired, !VLI).
|
|
|
|
acquire_reg_require_given(Lval, !VLI) :-
|
|
( lval_in_use(!.VLI, Lval) ->
|
|
unexpected(this_file, "acquire_reg_require_given: lval in use")
|
|
;
|
|
true
|
|
),
|
|
get_acquired(!.VLI, Acquired0),
|
|
set.insert(Acquired0, Lval, Acquired),
|
|
set_acquired(Acquired, !VLI).
|
|
|
|
acquire_reg_prefer_given(Pref, Lval, !VLI) :-
|
|
PrefLval = reg(r, Pref),
|
|
( lval_in_use(!.VLI, PrefLval) ->
|
|
get_spare_reg(!.VLI, Lval)
|
|
;
|
|
Lval = PrefLval
|
|
),
|
|
get_acquired(!.VLI, Acquired0),
|
|
set.insert(Acquired0, Lval, Acquired),
|
|
set_acquired(Acquired, !VLI).
|
|
|
|
acquire_reg_start_at_given(Start, Lval, !VLI) :-
|
|
StartLval = reg(r, Start),
|
|
( lval_in_use(!.VLI, StartLval) ->
|
|
acquire_reg_start_at_given(Start + 1, Lval, !VLI)
|
|
;
|
|
Lval = StartLval,
|
|
get_acquired(!.VLI, Acquired0),
|
|
set.insert(Acquired0, Lval, Acquired),
|
|
set_acquired(Acquired, !VLI)
|
|
).
|
|
|
|
release_reg(Lval, !VLI) :-
|
|
get_acquired(!.VLI, Acquired0),
|
|
( set.member(Lval, Acquired0) ->
|
|
set.delete(Acquired0, Lval, Acquired),
|
|
set_acquired(Acquired, !VLI)
|
|
;
|
|
unexpected(this_file, "release_reg: unacquired reg")
|
|
).
|
|
|
|
%----------------------------------------------------------------------------%
|
|
|
|
lock_regs(N, Exceptions, !VLI) :-
|
|
set_locked(N, !VLI),
|
|
set_exceptions(Exceptions, !VLI).
|
|
|
|
unlock_regs(!VLI) :-
|
|
set_locked(0, !VLI),
|
|
set_exceptions([], !VLI).
|
|
|
|
%----------------------------------------------------------------------------%
|
|
|
|
max_reg_in_use(VLI, Max) :-
|
|
get_loc_var_map(VLI, LocVarMap),
|
|
map.keys(LocVarMap, VarLocs),
|
|
code_util.max_mentioned_reg(VarLocs, Max1),
|
|
get_acquired(VLI, Acquired),
|
|
set.to_sorted_list(Acquired, AcquiredList),
|
|
code_util.max_mentioned_reg(AcquiredList, Max2),
|
|
int.max(Max1, Max2, Max).
|
|
|
|
%----------------------------------------------------------------------------%
|
|
|
|
:- pred cell_is_constant(var_state_map::in, exprn_opts::in,
|
|
list(maybe(rval))::in, assoc_list(rval, llds_type)::out) is semidet.
|
|
|
|
cell_is_constant(_VarStateMap, _ExprnOpts, [], []).
|
|
cell_is_constant(VarStateMap, ExprnOpts, [yes(Rval0) | MaybeRvals],
|
|
[Rval - LldsType | RvalsTypes]) :-
|
|
expr_is_constant(VarStateMap, ExprnOpts, Rval0, Rval),
|
|
rval_type_as_arg(Rval, ExprnOpts, LldsType),
|
|
cell_is_constant(VarStateMap, ExprnOpts, MaybeRvals, RvalsTypes).
|
|
|
|
% expr_is_constant(VarStateMap, ExprnOpts, Rval0, Rval):
|
|
% Check if Rval0 is a constant rval, after substituting the values of the
|
|
% variables inside it. Returns the substituted, ground rval in Rval.
|
|
% Note that this predicate is similar to code_exprn.expr_is_constant,
|
|
% but it uses its own version of the variable state data structure.
|
|
%
|
|
:- pred expr_is_constant(var_state_map::in, exprn_opts::in,
|
|
rval::in, rval::out) is semidet.
|
|
|
|
expr_is_constant(_, ExprnOpts, const(Const), const(Const)) :-
|
|
exprn_aux.const_is_constant(Const, ExprnOpts, yes).
|
|
expr_is_constant(VarStateMap, ExprnOpts,
|
|
unop(Op, Expr0), unop(Op, Expr)) :-
|
|
expr_is_constant(VarStateMap, ExprnOpts, Expr0, Expr).
|
|
expr_is_constant(VarStateMap, ExprnOpts,
|
|
binop(Op, Expr1, Expr2), binop(Op, Expr3, Expr4)) :-
|
|
expr_is_constant(VarStateMap, ExprnOpts, Expr1, Expr3),
|
|
expr_is_constant(VarStateMap, ExprnOpts, Expr2, Expr4).
|
|
expr_is_constant(VarStateMap, ExprnOpts,
|
|
mkword(Tag, Expr0), mkword(Tag, Expr)) :-
|
|
expr_is_constant(VarStateMap, ExprnOpts, Expr0, Expr).
|
|
expr_is_constant(VarStateMap, ExprnOpts, var(Var), Rval) :-
|
|
map.search(VarStateMap, Var, State),
|
|
State = state(_, yes(Rval), _, _, _),
|
|
expect(expr_is_constant(VarStateMap, ExprnOpts, Rval, _),
|
|
this_file, "non-constant rval in variable state").
|
|
|
|
%----------------------------------------------------------------------------%
|
|
|
|
materialize_vars_in_lval(ModuleInfo, Lval0, Lval, Code, !VLI) :-
|
|
materialize_vars_in_lval(ModuleInfo, Lval0, [], Lval, Code, !VLI).
|
|
|
|
:- pred materialize_vars_in_lval(module_info::in, lval::in, list(lval)::in,
|
|
lval::out, code_tree::out,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
materialize_vars_in_lval(ModuleInfo, Lval0, Avoid, Lval, Code, !VLI) :-
|
|
(
|
|
Lval0 = reg(_, _),
|
|
Lval = Lval0,
|
|
Code = empty
|
|
;
|
|
Lval0 = stackvar(_),
|
|
Lval = Lval0,
|
|
Code = empty
|
|
;
|
|
Lval0 = framevar(_),
|
|
Lval = Lval0,
|
|
Code = empty
|
|
;
|
|
Lval0 = succip,
|
|
Lval = Lval0,
|
|
Code = empty
|
|
;
|
|
Lval0 = maxfr,
|
|
Lval = Lval0,
|
|
Code = empty
|
|
;
|
|
Lval0 = curfr,
|
|
Lval = Lval0,
|
|
Code = empty
|
|
;
|
|
Lval0 = hp,
|
|
Lval = Lval0,
|
|
Code = empty
|
|
;
|
|
Lval0 = sp,
|
|
Lval = Lval0,
|
|
Code = empty
|
|
;
|
|
Lval0 = succip_slot(Rval0),
|
|
materialize_vars_in_rval(ModuleInfo, Rval0, no, Avoid, Rval, Code,
|
|
!VLI),
|
|
Lval = succip_slot(Rval)
|
|
;
|
|
Lval0 = redoip_slot(Rval0),
|
|
materialize_vars_in_rval(ModuleInfo, Rval0, no, Avoid, Rval, Code,
|
|
!VLI),
|
|
Lval = redoip_slot(Rval)
|
|
;
|
|
Lval0 = succfr_slot(Rval0),
|
|
materialize_vars_in_rval(ModuleInfo, Rval0, no, Avoid, Rval, Code,
|
|
!VLI),
|
|
Lval = succfr_slot(Rval)
|
|
;
|
|
Lval0 = redofr_slot(Rval0),
|
|
materialize_vars_in_rval(ModuleInfo, Rval0, no, Avoid, Rval, Code,
|
|
!VLI),
|
|
Lval = redofr_slot(Rval)
|
|
;
|
|
Lval0 = prevfr_slot(Rval0),
|
|
materialize_vars_in_rval(ModuleInfo, Rval0, no, Avoid, Rval, Code,
|
|
!VLI),
|
|
Lval = prevfr_slot(Rval)
|
|
;
|
|
Lval0 = mem_ref(Rval0),
|
|
materialize_vars_in_rval(ModuleInfo, Rval0, no, Avoid, Rval, Code,
|
|
!VLI),
|
|
Lval = mem_ref(Rval)
|
|
;
|
|
Lval0 = field(Tag, RvalA0, RvalB0),
|
|
materialize_vars_in_rval(ModuleInfo, RvalA0, no, Avoid, RvalA, CodeA,
|
|
!VLI),
|
|
materialize_vars_in_rval(ModuleInfo, RvalB0, no, Avoid, RvalB, CodeB,
|
|
!VLI),
|
|
Lval = field(Tag, RvalA, RvalB),
|
|
Code = tree(CodeA, CodeB)
|
|
;
|
|
Lval0 = temp(_, _),
|
|
unexpected(this_file, "materialize_vars_in_lval: temp")
|
|
;
|
|
Lval0 = lvar(_),
|
|
unexpected(this_file, "materialize_vars_in_lval: lvar")
|
|
).
|
|
|
|
% Rval is Rval0 with all variables in Rval0 replaced by their values.
|
|
%
|
|
:- pred materialize_vars_in_rval(module_info::in, rval::in, maybe(lval)::in,
|
|
list(lval)::in, rval::out, code_tree::out,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
materialize_vars_in_rval(ModuleInfo, Rval0, MaybePrefer, Avoid, Rval, Code,
|
|
!VLI) :-
|
|
(
|
|
Rval0 = lval(Lval0),
|
|
materialize_vars_in_lval(ModuleInfo, Lval0, Avoid, Lval, Code, !VLI),
|
|
Rval = lval(Lval)
|
|
;
|
|
Rval0 = mkword(Tag, SubRval0),
|
|
materialize_vars_in_rval(ModuleInfo, SubRval0, no, Avoid, SubRval,
|
|
Code, !VLI),
|
|
Rval = mkword(Tag, SubRval)
|
|
;
|
|
Rval0 = unop(Unop, SubRval0),
|
|
materialize_vars_in_rval(ModuleInfo, SubRval0, no, Avoid,
|
|
SubRval, Code, !VLI),
|
|
Rval = unop(Unop, SubRval)
|
|
;
|
|
Rval0 = binop(Binop, SubRvalA0, SubRvalB0),
|
|
materialize_vars_in_rval(ModuleInfo, SubRvalA0, no, Avoid, SubRvalA,
|
|
CodeA, !VLI),
|
|
materialize_vars_in_rval(ModuleInfo, SubRvalB0, no, Avoid, SubRvalB,
|
|
CodeB, !VLI),
|
|
Rval = binop(Binop, SubRvalA, SubRvalB),
|
|
Code = tree(CodeA, CodeB)
|
|
;
|
|
Rval0 = const(_),
|
|
Rval = Rval0,
|
|
Code = empty
|
|
;
|
|
Rval0 = mem_addr(MemRef0),
|
|
materialize_vars_in_mem_ref(ModuleInfo, MemRef0, MemRef, Avoid, Code,
|
|
!VLI),
|
|
Rval = mem_addr(MemRef)
|
|
;
|
|
Rval0 = var(Var),
|
|
find_var_availability(!.VLI, Var, MaybePrefer, Avail),
|
|
(
|
|
Avail = available(Rval),
|
|
Code = empty
|
|
;
|
|
Avail = needs_materialization,
|
|
materialize_var(ModuleInfo, Var, MaybePrefer, yes, Avoid, Rval,
|
|
Code, !VLI)
|
|
)
|
|
).
|
|
|
|
% MemRef is MemRef0 with all variables in MemRef replaced by their values.
|
|
%
|
|
:- pred materialize_vars_in_mem_ref(module_info::in, mem_ref::in, mem_ref::out,
|
|
list(lval)::in, code_tree::out,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
materialize_vars_in_mem_ref(ModuleInfo, MemRef0, MemRef, Avoid, Code, !VLI) :-
|
|
(
|
|
MemRef0 = stackvar_ref(_),
|
|
MemRef = MemRef0,
|
|
Code = empty
|
|
;
|
|
MemRef0 = framevar_ref(_),
|
|
MemRef = MemRef0,
|
|
Code = empty
|
|
;
|
|
MemRef0 = heap_ref(PtrRval0, Ptag, FieldNumRval0),
|
|
materialize_vars_in_rval(ModuleInfo, PtrRval0, no, Avoid,
|
|
PtrRval, PtrCode, !VLI),
|
|
materialize_vars_in_rval(ModuleInfo, FieldNumRval0, no, Avoid,
|
|
FieldNumRval, FieldNumCode, !VLI),
|
|
Code = tree(PtrCode, FieldNumCode),
|
|
MemRef = heap_ref(PtrRval, Ptag, FieldNumRval)
|
|
).
|
|
|
|
:- type var_avail
|
|
---> available(rval)
|
|
; needs_materialization.
|
|
|
|
:- pred find_var_availability(var_locn_info::in, prog_var::in,
|
|
maybe(lval)::in, var_avail::out) is det.
|
|
|
|
find_var_availability(VLI, Var, MaybePrefer, Avail) :-
|
|
get_var_state_map(VLI, VarStateMap),
|
|
map.lookup(VarStateMap, Var, State),
|
|
State = state(Lvals, MaybeConstRval, _, _, _),
|
|
set.to_sorted_list(Lvals, LvalsList),
|
|
(
|
|
MaybePrefer = yes(Prefer),
|
|
list.member(Prefer, LvalsList)
|
|
->
|
|
Rval = lval(Prefer),
|
|
Avail = available(Rval)
|
|
;
|
|
maybe_select_lval_or_rval(LvalsList, MaybeConstRval, Rval)
|
|
->
|
|
Avail = available(Rval)
|
|
;
|
|
Avail = needs_materialization
|
|
).
|
|
|
|
:- pred materialize_var(module_info::in, prog_var::in, maybe(lval)::in,
|
|
bool::in, list(lval)::in, rval::out, code_tree::out,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
materialize_var(ModuleInfo, Var, MaybePrefer, StoreIfReq, Avoid, Rval, Code,
|
|
!VLI) :-
|
|
get_var_state_map(!.VLI, VarStateMap),
|
|
map.lookup(VarStateMap, Var, State),
|
|
State = state(_Lvals, _MaybeConstRval, MaybeExprRval, UsingVars,
|
|
_DeadOrAlive),
|
|
(
|
|
MaybeExprRval = yes(ExprRval)
|
|
;
|
|
MaybeExprRval = no,
|
|
unexpected(this_file, "materialize_var: no expr")
|
|
),
|
|
materialize_vars_in_rval(ModuleInfo, ExprRval, MaybePrefer, Avoid, Rval0,
|
|
ExprCode, !VLI),
|
|
(
|
|
StoreIfReq = yes,
|
|
set.count(UsingVars, NumUsingVars),
|
|
NumUsingVars > 1
|
|
->
|
|
select_preferred_reg_avoid(!.VLI, Var, Avoid, Lval),
|
|
place_var(ModuleInfo, Var, Lval, PlaceCode, !VLI),
|
|
Rval = lval(Lval),
|
|
Code = tree(ExprCode, PlaceCode)
|
|
;
|
|
Rval = Rval0,
|
|
Code = ExprCode
|
|
).
|
|
|
|
%----------------------------------------------------------------------------%
|
|
|
|
% Update LocVarMap0 to reflect the dependence of Var on all the root lvals
|
|
% among Lvals or contained inside Lvals.
|
|
%
|
|
:- pred make_var_depend_on_lvals_roots(prog_var::in,
|
|
set(lval)::in, loc_var_map::in, loc_var_map::out) is det.
|
|
|
|
make_var_depend_on_lvals_roots(Var, Lvals, !LocVarMap) :-
|
|
get_var_set_roots(Lvals, NoDupRootLvals),
|
|
list.foldl(make_var_depend_on_root_lval(Var),
|
|
NoDupRootLvals, !LocVarMap).
|
|
|
|
:- pred make_var_depend_on_lval_roots(prog_var::in,
|
|
lval::in, loc_var_map::in, loc_var_map::out) is det.
|
|
|
|
make_var_depend_on_lval_roots(Var, Lval, !LocVarMap) :-
|
|
set.singleton_set(Lvals, Lval),
|
|
make_var_depend_on_lvals_roots(Var, Lvals, !LocVarMap).
|
|
|
|
:- pred make_var_depend_on_root_lval(prog_var::in, lval::in,
|
|
loc_var_map::in, loc_var_map::out) is det.
|
|
|
|
make_var_depend_on_root_lval(Var, Lval, !LocVarMap) :-
|
|
expect(is_root_lval(Lval),
|
|
this_file, "make_var_depend_on_root_lval: non-root lval"),
|
|
( map.search(!.LocVarMap, Lval, Vars0) ->
|
|
set.insert(Vars0, Var, Vars),
|
|
map.det_update(!.LocVarMap, Lval, Vars, !:LocVarMap)
|
|
;
|
|
set.singleton_set(Vars, Var),
|
|
map.det_insert(!.LocVarMap, Lval, Vars, !:LocVarMap)
|
|
).
|
|
|
|
% Update LocVarMap0 to reflect that Var is no longer dependent
|
|
% on the root lval Lval.
|
|
%
|
|
:- pred make_var_not_depend_on_root_lval(prog_var::in, lval::in,
|
|
loc_var_map::in, loc_var_map::out) is det.
|
|
|
|
make_var_not_depend_on_root_lval(Var, Lval, !LocVarMap) :-
|
|
expect(is_root_lval(Lval), this_file,
|
|
"make_var_depend_on_root_lval: non-root lval"),
|
|
( map.search(!.LocVarMap, Lval, Vars0) ->
|
|
set.delete(Vars0, Var, Vars),
|
|
( set.empty(Vars) ->
|
|
map.det_remove(!.LocVarMap, Lval, _, !:LocVarMap)
|
|
;
|
|
map.det_update(!.LocVarMap, Lval, Vars, !:LocVarMap)
|
|
)
|
|
;
|
|
unexpected(this_file, "make_var_not_depend_on_root_lval: no record")
|
|
).
|
|
|
|
:- pred is_root_lval(lval::in) is semidet.
|
|
|
|
is_root_lval(reg(r, _)).
|
|
is_root_lval(stackvar(_)).
|
|
is_root_lval(framevar(_)).
|
|
|
|
%----------------------------------------------------------------------------%
|
|
|
|
:- type dep_search_lval
|
|
---> all_regs
|
|
; specific_reg_or_stack(lval).
|
|
|
|
:- pred lval_does_not_support_lval(lval::in, lval::in) is semidet.
|
|
|
|
lval_does_not_support_lval(Lval1, Lval2) :-
|
|
\+ lval_depends_on_search_lval(Lval2, specific_reg_or_stack(Lval1)).
|
|
|
|
:- pred rval_depends_on_search_lval(rval::in, dep_search_lval::in) is semidet.
|
|
|
|
rval_depends_on_search_lval(lval(Lval), SearchLval) :-
|
|
lval_depends_on_search_lval(Lval, SearchLval).
|
|
rval_depends_on_search_lval(var(_Var), _SearchLval) :-
|
|
unexpected(this_file, "rval_depends_on_search_lval: var").
|
|
rval_depends_on_search_lval(mkword(_Tag, Rval), SearchLval) :-
|
|
rval_depends_on_search_lval(Rval, SearchLval).
|
|
rval_depends_on_search_lval(const(_Const), _SearchLval) :-
|
|
fail.
|
|
rval_depends_on_search_lval(unop(_Op, Rval), SearchLval) :-
|
|
rval_depends_on_search_lval(Rval, SearchLval).
|
|
rval_depends_on_search_lval(binop(_Op, Rval0, Rval1), SearchLval) :-
|
|
(
|
|
rval_depends_on_search_lval(Rval0, SearchLval)
|
|
;
|
|
rval_depends_on_search_lval(Rval1, SearchLval)
|
|
).
|
|
|
|
:- pred lval_depends_on_search_lval(lval::in, dep_search_lval::in) is semidet.
|
|
|
|
lval_depends_on_search_lval(reg(Type, Num), SearchLval) :-
|
|
(
|
|
SearchLval = all_regs
|
|
;
|
|
SearchLval = specific_reg_or_stack(Lval),
|
|
Lval = reg(Type, Num)
|
|
).
|
|
lval_depends_on_search_lval(stackvar(Num), SearchLval) :-
|
|
SearchLval = specific_reg_or_stack(Lval),
|
|
Lval = stackvar(Num).
|
|
lval_depends_on_search_lval(framevar(Num), SearchLval) :-
|
|
SearchLval = specific_reg_or_stack(Lval),
|
|
Lval = framevar(Num).
|
|
lval_depends_on_search_lval(lvar(_Var), _SearchLval) :-
|
|
unexpected(this_file, "lval_depends_on_search_lval: lvar").
|
|
lval_depends_on_search_lval(field(_Tag, Rval0, Rval1), SearchLval) :-
|
|
(
|
|
rval_depends_on_search_lval(Rval0, SearchLval)
|
|
;
|
|
rval_depends_on_search_lval(Rval1, SearchLval)
|
|
).
|
|
|
|
:- pred args_depend_on_search_lval(list(maybe(rval))::in, dep_search_lval::in)
|
|
is semidet.
|
|
|
|
args_depend_on_search_lval([], _SearchLval) :-
|
|
fail.
|
|
args_depend_on_search_lval([Arg | Args], SearchLval) :-
|
|
(
|
|
Arg = yes(Rval),
|
|
rval_depends_on_search_lval(Rval, SearchLval)
|
|
;
|
|
args_depend_on_search_lval(Args, SearchLval)
|
|
).
|
|
|
|
%----------------------------------------------------------------------------%
|
|
|
|
set_follow_vars(abs_follow_vars(FollowVarMap, NextNonReserved), !VLI) :-
|
|
set_follow_var_map(FollowVarMap, !VLI),
|
|
set_next_non_reserved(NextNonReserved, !VLI).
|
|
|
|
%----------------------------------------------------------------------------%
|
|
|
|
:- pred get_var_name(var_locn_info::in, prog_var::in, string::out) is det.
|
|
|
|
get_var_name(VLI, Var, Name) :-
|
|
get_varset(VLI, VarSet),
|
|
varset.lookup_name(VarSet, Var, Name).
|
|
|
|
%----------------------------------------------------------------------------%
|
|
|
|
:- pred nonempty_state(var_state::in) is semidet.
|
|
|
|
nonempty_state(State) :-
|
|
State = state(LvalSet, MaybeConstRval, MaybeExprRval, _, _),
|
|
( set.non_empty(LvalSet)
|
|
; MaybeConstRval = yes(_)
|
|
; MaybeExprRval = yes(_)
|
|
).
|
|
|
|
%----------------------------------------------------------------------------%
|
|
|
|
:- pred get_varset(var_locn_info::in, prog_varset::out) is det.
|
|
:- pred get_vartypes(var_locn_info::in, vartypes::out) is det.
|
|
:- pred get_exprn_opts(var_locn_info::in, exprn_opts::out) is det.
|
|
:- pred get_var_state_map(var_locn_info::in, var_state_map::out) is det.
|
|
:- pred get_loc_var_map(var_locn_info::in, loc_var_map::out) is det.
|
|
:- pred get_acquired(var_locn_info::in, set(lval)::out) is det.
|
|
:- pred get_locked(var_locn_info::in, int::out) is det.
|
|
:- pred get_exceptions(var_locn_info::in, assoc_list(prog_var, lval)::out)
|
|
is det.
|
|
|
|
:- pred set_follow_var_map(abs_follow_vars_map::in,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
:- pred set_next_non_reserved(int::in,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
:- pred set_var_state_map(var_state_map::in,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
:- pred set_loc_var_map(loc_var_map::in,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
:- pred set_acquired(set(lval)::in,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
:- pred set_locked(int::in,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
:- pred set_exceptions(assoc_list(prog_var, lval)::in,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
get_varset(VI, VI ^ varset).
|
|
get_vartypes(VI, VI ^ vartypes).
|
|
get_stack_slots(VI, VI ^ stack_slots).
|
|
get_exprn_opts(VI, VI ^ exprn_opts).
|
|
get_follow_var_map(VI, VI ^ follow_vars_map).
|
|
get_next_non_reserved(VI, VI ^ next_non_res).
|
|
get_var_state_map(VI, VI ^ var_state_map).
|
|
get_loc_var_map(VI, VI ^ loc_var_map).
|
|
get_acquired(VI, VI ^ acquired).
|
|
get_locked(VI, VI ^ locked).
|
|
get_exceptions(VI, VI ^ exceptions).
|
|
|
|
set_follow_var_map(FVM, VI, VI ^ follow_vars_map := FVM).
|
|
set_next_non_reserved(NNR, VI, VI ^ next_non_res := NNR).
|
|
set_var_state_map(VSM, VI, VI ^ var_state_map := VSM).
|
|
set_loc_var_map(LVM, VI, VI ^ loc_var_map := LVM).
|
|
set_acquired(A, VI, VI ^ acquired := A).
|
|
set_locked(L, VI, VI ^ locked := L).
|
|
set_exceptions(E, VI, VI ^ exceptions := E).
|
|
|
|
%----------------------------------------------------------------------------%
|
|
|
|
:- func this_file = string.
|
|
|
|
this_file = "var_locn.m".
|
|
|
|
%----------------------------------------------------------------------------%
|
|
:- end_module var_locn.
|
|
%----------------------------------------------------------------------------%
|