mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-15 01:13:30 +00:00
3070 lines
114 KiB
Mathematica
3070 lines
114 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 2000-2012 The University of Melbourne.
|
|
% Copyright (C) 2013-2018, 2020-2025 The Mercury team.
|
|
% 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 hlds.
|
|
:- import_module hlds.hlds_data.
|
|
:- import_module hlds.hlds_goal.
|
|
:- import_module hlds.hlds_llds.
|
|
:- import_module ll_backend.global_data.
|
|
:- import_module ll_backend.llds.
|
|
:- import_module parse_tree.
|
|
:- import_module parse_tree.prog_data.
|
|
:- import_module parse_tree.set_of_var.
|
|
:- import_module parse_tree.var_table.
|
|
|
|
:- import_module assoc_list.
|
|
:- import_module bool.
|
|
:- import_module list.
|
|
:- import_module map.
|
|
:- import_module maybe.
|
|
:- import_module set.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% The var_locn_info type and its initialization.
|
|
%
|
|
|
|
:- type var_locn_info.
|
|
|
|
% init_var_locn_state(VarTable, FloatRegType, StackSlots, FollowVars,
|
|
% Liveness, Arguments, 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). FloatRegType gives the preferred register type
|
|
% for floats. 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.
|
|
%
|
|
:- pred init_var_locn_state(var_table::in, reg_type::in, stack_slots::in,
|
|
abs_follow_vars::in, set_of_progvar::in, assoc_list(prog_var, lval)::in,
|
|
var_locn_info::out) is det.
|
|
|
|
% reinit_var_locn_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_var_locn_state(assoc_list(prog_var, lval)::in,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Getter predicates for var_locn_infos.
|
|
%
|
|
|
|
% var_locn_get_stack_slots(VarLocnInfo, StackSlots):
|
|
%
|
|
% Returns the table mapping each variable to its stack slot (if any).
|
|
%
|
|
:- pred var_locn_get_stack_slots(var_locn_info::in, stack_slots::out) is det.
|
|
|
|
% var_locn_get_follow_var_map(VarLocnInfo, FollowVars):
|
|
%
|
|
% Returns the table mapping each variable to the lval (if any)
|
|
% where it is desired next.
|
|
%
|
|
:- pred var_locn_get_follow_var_map(var_locn_info::in,
|
|
abs_follow_vars_map::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Nontrivial getter predicates for var_locn_infos.
|
|
%
|
|
|
|
% var_locn_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 var_locn_get_var_locations(var_locn_info::in,
|
|
map(prog_var, set(lval))::out) is det.
|
|
|
|
% var_locn_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 var_locn_lval_in_use(var_locn_info::in, lval::in) is semidet.
|
|
|
|
% var_locn_max_reg_in_use(MaxRegR, MaxRegF):
|
|
%
|
|
% Returns the number of the highest numbered rN and fN registers in use.
|
|
%
|
|
:- pred var_locn_max_reg_in_use(var_locn_info::in, int::out, int::out)
|
|
is det.
|
|
|
|
% var_locn_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 var_locn_get_next_non_reserved(var_locn_info::in, reg_type::in,
|
|
int::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Nontrivial setter predicates for var_locn_infos.
|
|
%
|
|
|
|
% var_locn_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 var_locn_set_follow_vars(abs_follow_vars::in,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Assigning values to variables.
|
|
%
|
|
|
|
% var_locn_assign_var_to_var(Var, AssignedVar, !VarLocnInfo):
|
|
%
|
|
% Reflects the effect of the assignment Var := AssignedVar in the
|
|
% state of !VarLocnInfo.
|
|
%
|
|
:- pred var_locn_assign_var_to_var(prog_var::in, prog_var::in,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
% var_locn_assign_lval_to_var(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 var_locn_assign_lval_to_var(prog_var::in, lval::in,
|
|
static_cell_info::in, llds_code::out,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
% var_locn_assign_field_lval_expr_to_var(ModuleInfo, Var, BaseVar, Expr,
|
|
% StaticCellInfo, Code, !VarLocnInfo);
|
|
%
|
|
% Reflects the effect of the assignment Var := Expr,
|
|
% where Expr contains only field lvals with the base BaseVar.
|
|
% Any code required to effect the assignment will be returned in Code.
|
|
%
|
|
:- pred var_locn_assign_field_lval_expr_to_var(prog_var::in,
|
|
prog_var::in, rval::in, llds_code::out,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
% var_locn_assign_const_to_var(ExprnOpts, Var, ConstRval,
|
|
% !VarLocnInfo):
|
|
%
|
|
% Reflects the effect of the assignment Var := const(ConstRval)
|
|
% in the state of !VarLocnInfo.
|
|
%
|
|
:- pred var_locn_assign_const_to_var(exprn_opts::in, prog_var::in, rval::in,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
% var_locn_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 var_locn_assign_expr_to_var(prog_var::in, rval::in, llds_code::out,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
% var_locn_reassign_mkword_hole_var(Var, Ptag, Rval, Code, !VarLocnInfo):
|
|
%
|
|
% Generates code to execute the assignment Var := mkword(Ptag, Rval), and
|
|
% updates the state of !VarLocnInfo accordingly. Var must previously have
|
|
% been assigned the constant expression mkword_hole(Ptag).
|
|
%
|
|
:- pred var_locn_reassign_mkword_hole_var(prog_var::in, ptag::in, rval::in,
|
|
llds_code::out, var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
% var_locn_reassign_tagword_var(Var, ToOrMask, ToOrRval, Code,
|
|
% !VarLocnInfo):
|
|
%
|
|
% Generates code to assign (Var & \ToOrMask) | ToOrRval to Var.
|
|
% Obviously, Var must have previously been assigned a value.
|
|
% ToOrRval may (and typically will) contain references to other variables
|
|
% in var(_) form; it should *not* contain references directly to lvals.
|
|
%
|
|
:- pred var_locn_reassign_tagword_var(prog_var::in, uint::in, rval::in,
|
|
llds_code::out, var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
% var_locn_assign_cell_to_var(ExprnOpts, Var, ReserveWordAtStart, Ptag,
|
|
% MaybeRvals, MaybeSize, FieldAddrs, TypeMsg,
|
|
% MayUseAtomic, Label, 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 MaybeSize 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 MaybeSize should not be yes / yes(_), because that will cause an
|
|
% obvious conflict.) Label can be used in the generated code if necessary.
|
|
%
|
|
:- pred var_locn_assign_cell_to_var(exprn_opts::in, prog_var::in, bool::in,
|
|
ptag::in, list(cell_arg)::in, how_to_construct::in,
|
|
maybe(term_size_value)::in, maybe(alloc_site_id)::in,
|
|
may_use_atomic_alloc::in, label::in,
|
|
llds_code::out, static_cell_info::in, static_cell_info::out,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Assign nonexistent ("magical") values to variables at code points
|
|
% that cannot be reached, to provide a consistent view of the state
|
|
% of each variable at each branch end of a branched control structure.
|
|
%
|
|
|
|
% var_locn_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 var_locn_set_magic_var_location(prog_var::in, lval::in,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
% var_locn_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 var_locn_check_and_set_magic_var_location(prog_var::in, lval::in,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Placing the values of variables in unspecified or incompletely
|
|
% specified locations.
|
|
%
|
|
|
|
% var_locn_produce_var(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 var_locn_produce_var(prog_var::in, rval::out, llds_code::out,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
% var_locn_produce_var_in_reg(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 var_locn_produce_var_in_reg(prog_var::in, lval::out, llds_code::out,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
% var_locn_produce_var_in_reg_or_stack(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 var_locn_produce_var_in_reg_or_stack(prog_var::in,
|
|
lval::out, llds_code::out, var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Placing the values of variables in specified locations.
|
|
%
|
|
|
|
% var_locn_place_var(Var, Lval, Code, !VarLocnInfo):
|
|
%
|
|
% Produces Code to place the value of Var in Lval, and update !VarLocnInfo
|
|
% to reflect this.
|
|
%
|
|
:- pred var_locn_place_var(prog_var::in, lval::in, llds_code::out,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
% var_locn_place_vars(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 var_locn_place_vars(assoc_list(prog_var, lval)::in, llds_code::out,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Materializing the values of variables.
|
|
%
|
|
|
|
% var_locn_materialize_vars_in_lval(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 var_locn_materialize_vars_in_lval(lval::in, lval::out,
|
|
llds_code::out, var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
% The equivalent of var_locn_materialize_vars_in_lval for rvals.
|
|
%
|
|
:- pred var_locn_materialize_vars_in_rval(rval::in, rval::out,
|
|
llds_code::out, var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Freeing up locations for new uses.
|
|
%
|
|
|
|
% var_locn_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
|
|
% foreign_procs.
|
|
%
|
|
:- pred var_locn_clear_r1(llds_code::out,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
% var_locn_save_cell_fields(ReuseVar, ReuseLval, Code, TempRegs,
|
|
% !VarLocnInfo)
|
|
%
|
|
% Save any variables which depend on ReuseLval into temporary registers,
|
|
% so that they are available after ReuseLval is clobbered.
|
|
%
|
|
:- pred var_locn_save_cell_fields(prog_var::in, lval::in,
|
|
llds_code::out, list(lval)::out,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Record the clobbering of registers (e.g. by calls).
|
|
%
|
|
|
|
% var_locn_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 var_locn_clobber_all_regs(bool::in,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
% var_locn_clobber_reg(Reg, !VarLocnInfo):
|
|
%
|
|
% Modifies VarLocnInfo to show that all variables stored in Reg
|
|
% (an lval which should be a register) are clobbered.
|
|
%
|
|
:- pred var_locn_clobber_reg(lval::in,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
% var_locn_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 var_locn_clobber_regs(list(lval)::in,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Record variables becoming dead.
|
|
%
|
|
|
|
:- type maybe_first_death
|
|
---> first_death
|
|
; maybe_not_first_death.
|
|
|
|
% var_locn_var_becomes_dead(FirstDeath, Var, !VarLocnInfo):
|
|
%
|
|
% Free any code generator resources used by Var in !VarLocnInfo.
|
|
% FirstDeath should be maybe_not_first_death if this same operation
|
|
% may already have been executed on Var. If FirstDeath is first_death,
|
|
% this predicate will throw an exception if Var is unknown
|
|
% in the initial var_locn_info (since you cannot kill something
|
|
% that is not initially alive).
|
|
%
|
|
% Note: The value of FirstDeath is used only for this sanity check,
|
|
% and not for any other purpose.
|
|
%
|
|
:- pred var_locn_var_becomes_dead(maybe_first_death::in, prog_var::in,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Acquiring and releasing registers.
|
|
%
|
|
|
|
% var_locn_acquire_reg(Lval, !VarLocnInfo):
|
|
%
|
|
% Finds an unused register and marks it as 'in use'.
|
|
%
|
|
:- pred var_locn_acquire_reg(reg_type::in, lval::out,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
% var_locn_acquire_reg_require_given(Reg, Lval, !VarLocInfo):
|
|
%
|
|
% Marks Reg, which must be an unused register, as 'in use'.
|
|
%
|
|
:- pred var_locn_acquire_reg_require_given(lval::in,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
% var_locn_acquire_reg_prefer_given(Type, Pref, Lval, !VarLocInfo):
|
|
%
|
|
% Finds an unused register, and marks it as 'in use'.
|
|
% If Pref itself is free, assigns that.
|
|
%
|
|
:- pred var_locn_acquire_reg_prefer_given(reg_type::in, int::in, lval::out,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
% var_locn_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 var_locn_acquire_reg_start_at_given(reg_type::in, int::in, lval::out,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
% var_locn_release_reg(Lval, !VarLocnInfo):
|
|
%
|
|
% Marks a previously acquired reg as no longer 'in use'.
|
|
%
|
|
:- pred var_locn_release_reg(lval::in, var_locn_info::in, var_locn_info::out)
|
|
is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Locking and unlocking registers.
|
|
%
|
|
|
|
% var_locn_lock_regs(R, F, Exceptions, !VarLocnInfo):
|
|
%
|
|
% Prevents registers r1 through rR and registers f1 through fF 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 var_locn_lock_regs(int::in, int::in, assoc_list(prog_var, lval)::in,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
% var_locn_unlock_regs(!VarLocnInfo):
|
|
%
|
|
% Undoes a lock operation.
|
|
%
|
|
:- pred var_locn_unlock_regs(var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module backend_libs.
|
|
:- import_module backend_libs.builtin_ops.
|
|
:- import_module ll_backend.code_util.
|
|
:- import_module ll_backend.exprn_aux.
|
|
:- import_module parse_tree.builtin_lib_types.
|
|
:- import_module parse_tree.prog_type.
|
|
|
|
:- import_module cord.
|
|
:- import_module int.
|
|
:- import_module pair.
|
|
:- import_module require.
|
|
:- import_module string.
|
|
:- import_module term.
|
|
:- import_module uint.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% The var_locn_info type and its initialization.
|
|
%
|
|
|
|
:- type dead_or_alive
|
|
---> doa_dead
|
|
; doa_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
|
|
---> var_state(
|
|
% Must not contain any rval of the form var(_).
|
|
locs :: set(lval),
|
|
|
|
% Must not contain any rval of the form var(_);
|
|
% must be constant.
|
|
const_rval :: maybe(rval),
|
|
|
|
% Will contain var(_), must not contain lvals.
|
|
expr_rval :: maybe(rval),
|
|
|
|
% The set of vars whose expr_rval field refers to this var.
|
|
using_vars :: set_of_progvar,
|
|
|
|
% A dead variable should be removed from var_state_map
|
|
% when its using_vars field becomes empty.
|
|
dead_or_alive :: dead_or_alive
|
|
).
|
|
|
|
:- 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_of_progvar).
|
|
|
|
:- type var_locn_info
|
|
---> var_locn_info(
|
|
% The var_table from the proc_info.
|
|
% XXX This field is redundant; it is also stored
|
|
% in the code_info.
|
|
vli_var_table :: var_table,
|
|
|
|
% The register type to use for float vars.
|
|
vli_float_reg_type :: reg_type,
|
|
|
|
% Maps each var to its stack slot, if it has one.
|
|
vli_stack_slots :: stack_slots,
|
|
|
|
% Where vars are needed next.
|
|
vli_follow_vars_map :: abs_follow_vars_map,
|
|
|
|
% Next rN, fN register that isn't reserved in follow_vars_map.
|
|
vli_next_non_res_r :: int,
|
|
vli_next_non_res_f :: int,
|
|
|
|
% Documented above.
|
|
vli_var_state_map :: var_state_map,
|
|
vli_loc_var_map :: loc_var_map,
|
|
|
|
% Locations that are temporarily reserved for purposes such as
|
|
% holding the tags of variables during switches.
|
|
vli_acquired :: set(lval),
|
|
|
|
% If these slots contain R and F then registers r1 through rR
|
|
% and f1 through fF 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.
|
|
vli_locked_r :: int,
|
|
vli_locked_f :: int,
|
|
|
|
% See the documentation of the locked field above.
|
|
vli_exceptions :: assoc_list(prog_var, lval)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
init_var_locn_state(VarTable, FloatRegType, StackSlots, FollowVars, Liveness,
|
|
VarLocs, VarLocnInfo) :-
|
|
map.init(VarStateMap0),
|
|
map.init(LocVarMap0),
|
|
init_var_locn_state_loop(VarLocs, Liveness,
|
|
VarStateMap0, VarStateMap, LocVarMap0, LocVarMap),
|
|
FollowVars = abs_follow_vars(FollowVarMap, NextNonReservedR,
|
|
NextNonReservedF),
|
|
set.init(AcquiredRegs),
|
|
VarLocnInfo = var_locn_info(VarTable, FloatRegType, StackSlots,
|
|
FollowVarMap, NextNonReservedR, NextNonReservedF,
|
|
VarStateMap, LocVarMap, AcquiredRegs, 0, 0, []).
|
|
|
|
:- pred init_var_locn_state_loop(assoc_list(prog_var, lval)::in,
|
|
set_of_progvar::in, var_state_map::in, var_state_map::out,
|
|
loc_var_map::in, loc_var_map::out) is det.
|
|
|
|
init_var_locn_state_loop([], _, !VarStateMap, !LocVarMap).
|
|
init_var_locn_state_loop([Var - Lval | VarLocns], Liveness,
|
|
!VarStateMap, !LocVarMap) :-
|
|
( if set_of_var.member(Liveness, Var) then
|
|
add_var_locn_state(Var, Lval, !VarStateMap, !LocVarMap)
|
|
else
|
|
% 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
|
|
),
|
|
init_var_locn_state_loop(VarLocns, Liveness, !VarStateMap, !LocVarMap).
|
|
|
|
%---------------------%
|
|
|
|
reinit_var_locn_state(VarLocs, !VarLocnInfo) :-
|
|
map.init(VarStateMap0),
|
|
map.init(LocVarMap0),
|
|
reinit_var_locn_state_loop(VarLocs,
|
|
VarStateMap0, VarStateMap, LocVarMap0, LocVarMap),
|
|
set.init(AcquiredRegs),
|
|
!.VarLocnInfo = var_locn_info(VarTable, FloatRegType, StackSlots,
|
|
FollowVarMap, NextNonReservedR, NextNonReservedF,
|
|
_, _, _, _, _, _),
|
|
!:VarLocnInfo = var_locn_info(VarTable, FloatRegType, StackSlots,
|
|
FollowVarMap, NextNonReservedR, NextNonReservedF,
|
|
VarStateMap, LocVarMap, AcquiredRegs, 0, 0, []).
|
|
|
|
:- pred reinit_var_locn_state_loop(assoc_list(prog_var, lval)::in,
|
|
var_state_map::in, var_state_map::out,
|
|
loc_var_map::in, loc_var_map::out) is det.
|
|
|
|
reinit_var_locn_state_loop([], !VarStateMap, !LocVarMap).
|
|
reinit_var_locn_state_loop([Var - Lval | VarLocns],
|
|
!VarStateMap, !LocVarMap) :-
|
|
add_var_locn_state(Var, Lval, !VarStateMap, !LocVarMap),
|
|
reinit_var_locn_state_loop(VarLocns, !VarStateMap, !LocVarMap).
|
|
|
|
%---------------------%
|
|
|
|
:- pred add_var_locn_state(prog_var::in, lval::in,
|
|
var_state_map::in, var_state_map::out,
|
|
loc_var_map::in, loc_var_map::out) is det.
|
|
|
|
add_var_locn_state(Var, Lval, !VarStateMap, !LocVarMap) :-
|
|
expect(is_root_lval(Lval), $pred, "unexpected lval"),
|
|
NewLocs = set.make_singleton_set(Lval),
|
|
set_of_var.init(Using),
|
|
State = var_state(NewLocs, no, no, Using, doa_alive),
|
|
map.det_insert(Var, State, !VarStateMap),
|
|
make_var_depend_on_lval_roots(Var, Lval, !LocVarMap).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Getter and setter predicates for var_locn_infos.
|
|
%
|
|
|
|
:- pred var_locn_get_var_table(var_locn_info::in, var_table::out) is det.
|
|
:- pred var_locn_get_float_reg_type(var_locn_info::in, reg_type::out) is det.
|
|
:- pred var_locn_get_var_state_map(var_locn_info::in, var_state_map::out)
|
|
is det.
|
|
:- pred var_locn_get_loc_var_map(var_locn_info::in, loc_var_map::out) is det.
|
|
:- pred var_locn_get_acquired(var_locn_info::in, set(lval)::out) is det.
|
|
:- pred var_locn_get_locked(var_locn_info::in, int::out, int::out) is det.
|
|
:- pred var_locn_get_exceptions(var_locn_info::in,
|
|
assoc_list(prog_var, lval)::out) is det.
|
|
|
|
:- pred var_locn_set_follow_var_map(abs_follow_vars_map::in,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
:- pred var_locn_set_next_non_reserved(int::in, int::in,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
:- pred var_locn_set_var_state_map(var_state_map::in,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
:- pred var_locn_set_loc_var_map(loc_var_map::in,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
:- pred var_locn_set_acquired(set(lval)::in,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
:- pred var_locn_set_locked(int::in, int::in,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
:- pred var_locn_set_exceptions(assoc_list(prog_var, lval)::in,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
var_locn_get_var_table(VI, VI ^ vli_var_table).
|
|
var_locn_get_float_reg_type(VI, VI ^ vli_float_reg_type).
|
|
var_locn_get_stack_slots(VI, VI ^ vli_stack_slots).
|
|
var_locn_get_follow_var_map(VI, VI ^ vli_follow_vars_map).
|
|
var_locn_get_var_state_map(VI, VI ^ vli_var_state_map).
|
|
var_locn_get_loc_var_map(VI, VI ^ vli_loc_var_map).
|
|
var_locn_get_acquired(VI, VI ^ vli_acquired).
|
|
var_locn_get_locked(VI, VI ^ vli_locked_r, VI ^ vli_locked_f).
|
|
var_locn_get_exceptions(VI, VI ^ vli_exceptions).
|
|
|
|
var_locn_set_follow_var_map(FVM, !VI) :-
|
|
!VI ^ vli_follow_vars_map := FVM.
|
|
var_locn_set_next_non_reserved(NNR, NNF, !VI) :-
|
|
!VI ^ vli_next_non_res_r := NNR,
|
|
!VI ^ vli_next_non_res_f := NNF.
|
|
var_locn_set_var_state_map(VSM, !VI) :-
|
|
!VI ^ vli_var_state_map := VSM.
|
|
var_locn_set_loc_var_map(LVM, !VI) :-
|
|
!VI ^ vli_loc_var_map := LVM.
|
|
var_locn_set_acquired(A, !VI) :-
|
|
!VI ^ vli_acquired := A.
|
|
var_locn_set_locked(R, F, !VI) :-
|
|
!VI ^ vli_locked_r := R,
|
|
!VI ^ vli_locked_f := F.
|
|
var_locn_set_exceptions(E, !VI) :-
|
|
!VI ^ vli_exceptions := E.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Nontrivial getter predicates for var_locn_infos.
|
|
%
|
|
|
|
var_locn_get_var_locations(VLI, VarLocations) :-
|
|
var_locn_get_var_state_map(VLI, VarStateMap),
|
|
map.to_assoc_list(VarStateMap, VarLocList),
|
|
list.filter_map(convert_live_to_lval_set, VarLocList, LiveVarLocList),
|
|
map.from_sorted_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 = var_state(Lvals, _, _, _, doa_alive).
|
|
|
|
var_locn_lval_in_use(VLI, Lval) :-
|
|
var_locn_get_loc_var_map(VLI, LocVarMap),
|
|
var_locn_get_acquired(VLI, Acquired),
|
|
var_locn_get_locked(VLI, LockedR, LockedF),
|
|
(
|
|
map.search(LocVarMap, Lval, UsingVars),
|
|
set_of_var.is_non_empty(UsingVars)
|
|
;
|
|
set.member(Lval, Acquired)
|
|
;
|
|
Lval = reg(reg_r, N),
|
|
N =< LockedR
|
|
;
|
|
Lval = reg(reg_f, N),
|
|
N =< LockedF
|
|
).
|
|
|
|
var_locn_max_reg_in_use(VLI, MaxR, MaxF) :-
|
|
var_locn_get_loc_var_map(VLI, LocVarMap),
|
|
map.keys(LocVarMap, VarLocs),
|
|
code_util.max_mentioned_regs(VarLocs, MaxR1, MaxF1),
|
|
var_locn_get_acquired(VLI, Acquired),
|
|
set.to_sorted_list(Acquired, AcquiredList),
|
|
code_util.max_mentioned_regs(AcquiredList, MaxR2, MaxF2),
|
|
int.max(MaxR1, MaxR2, MaxR),
|
|
int.max(MaxF1, MaxF2, MaxF).
|
|
|
|
var_locn_get_next_non_reserved(VI, reg_r, VI ^ vli_next_non_res_r).
|
|
var_locn_get_next_non_reserved(VI, reg_f, VI ^ vli_next_non_res_f).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Nontrivial setter predicates for var_locn_infos.
|
|
%
|
|
|
|
var_locn_set_follow_vars(abs_follow_vars(FollowVarMap, NextNonReservedR,
|
|
NextNonReservedF), !VLI) :-
|
|
var_locn_set_follow_var_map(FollowVarMap, !VLI),
|
|
var_locn_set_next_non_reserved(NextNonReservedR, NextNonReservedF, !VLI).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Assigning values to variables.
|
|
%
|
|
|
|
var_locn_assign_var_to_var(Var, OldVar, !VLI) :-
|
|
check_var_is_unknown(!.VLI, Var),
|
|
var_locn_get_var_state_map(!.VLI, VarStateMap0),
|
|
( if map.search(VarStateMap0, OldVar, OldState0) then
|
|
OldState0 = var_state(Lvals, MaybeConstRval, MaybeExprRval,
|
|
Using0, DeadOrAlive),
|
|
(
|
|
MaybeExprRval = yes(_),
|
|
State = var_state(Lvals, MaybeConstRval, yes(var(OldVar)),
|
|
set_of_var.init, doa_alive),
|
|
set_of_var.insert(Var, Using0, Using),
|
|
OldState = var_state(Lvals, MaybeConstRval, MaybeExprRval,
|
|
Using, DeadOrAlive),
|
|
map.det_update(OldVar, OldState, VarStateMap0, VarStateMap1)
|
|
;
|
|
MaybeExprRval = no,
|
|
State = var_state(Lvals, MaybeConstRval, no, set_of_var.init,
|
|
doa_alive),
|
|
VarStateMap1 = VarStateMap0
|
|
),
|
|
map.det_insert(Var, State, VarStateMap1, VarStateMap),
|
|
var_locn_set_var_state_map(VarStateMap, !VLI),
|
|
|
|
var_locn_get_loc_var_map(!.VLI, LocVarMap0),
|
|
make_var_depend_on_lvals_roots(Var, Lvals, LocVarMap0, LocVarMap),
|
|
var_locn_set_loc_var_map(LocVarMap, !VLI)
|
|
else
|
|
var_locn_get_var_table(!.VLI, VarTable),
|
|
lookup_var_entry(VarTable, OldVar, OldVarEntry),
|
|
OldVarEntry = vte(_N, _T, OldVarIsDummy),
|
|
expect(unify(OldVarIsDummy, is_dummy_type), $pred,
|
|
"assigning value of nondummy variable without a state")
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
var_locn_assign_lval_to_var(Var, Lval0, StaticCellInfo, Code, !VLI) :-
|
|
check_var_is_unknown(!.VLI, Var),
|
|
( if
|
|
Lval0 = field(yes(Ptag), var(BaseVar), const(llconst_int(Offset)))
|
|
then
|
|
var_locn_get_var_state_map(!.VLI, VarStateMap0),
|
|
map.lookup(VarStateMap0, BaseVar, BaseState),
|
|
BaseState = var_state(BaseVarLvals, MaybeConstBaseVarRval,
|
|
_MaybeExprRval, _UsingVars, _DeadOrAlive),
|
|
( if
|
|
MaybeConstBaseVarRval = yes(BaseVarRval),
|
|
BaseVarRval = mkword(Ptag, BaseConst),
|
|
BaseConst = const(llconst_data_addr(DataAddr)),
|
|
search_scalar_static_cell_offset(StaticCellInfo, DataAddr, Offset,
|
|
SelectedArgRval)
|
|
then
|
|
MaybeConstRval = yes(SelectedArgRval),
|
|
Lvals = set.map(add_field_offset(yes(Ptag),
|
|
const(llconst_int(Offset))), BaseVarLvals),
|
|
State = var_state(Lvals, MaybeConstRval, no, set_of_var.init,
|
|
doa_alive),
|
|
map.det_insert(Var, State, VarStateMap0, VarStateMap),
|
|
var_locn_set_var_state_map(VarStateMap, !VLI),
|
|
|
|
var_locn_get_loc_var_map(!.VLI, LocVarMap0),
|
|
make_var_depend_on_lvals_roots(Var, Lvals, LocVarMap0, LocVarMap),
|
|
var_locn_set_loc_var_map(LocVarMap, !VLI)
|
|
else
|
|
set.init(Lvals),
|
|
Expr = lval(Lval0),
|
|
State = var_state(Lvals, no, yes(Expr), set_of_var.init,
|
|
doa_alive),
|
|
map.det_insert(Var, State, VarStateMap0, VarStateMap1),
|
|
add_use_ref(BaseVar, Var, VarStateMap1, VarStateMap),
|
|
var_locn_set_var_state_map(VarStateMap, !VLI)
|
|
),
|
|
Code = empty
|
|
else
|
|
var_locn_materialize_vars_in_lval(Lval0, Lval, Code, !VLI),
|
|
|
|
var_locn_get_var_state_map(!.VLI, VarStateMap0),
|
|
LvalSet = set.make_singleton_set(Lval),
|
|
State = var_state(LvalSet, no, no, set_of_var.init, doa_alive),
|
|
map.det_insert(Var, State, VarStateMap0, VarStateMap),
|
|
var_locn_set_var_state_map(VarStateMap, !VLI),
|
|
|
|
var_locn_get_loc_var_map(!.VLI, LocVarMap0),
|
|
make_var_depend_on_lval_roots(Var, Lval, LocVarMap0, LocVarMap),
|
|
var_locn_set_loc_var_map(LocVarMap, !VLI)
|
|
).
|
|
|
|
:- func add_field_offset(maybe(ptag), rval, lval) = lval.
|
|
|
|
add_field_offset(Ptag, Offset, Base) =
|
|
field(Ptag, lval(Base), Offset).
|
|
|
|
var_locn_assign_field_lval_expr_to_var(Var, BaseVar, Expr, Code, !VLI) :-
|
|
check_var_is_unknown(!.VLI, Var),
|
|
var_locn_get_var_state_map(!.VLI, VarStateMap0),
|
|
State = var_state(set.init, no, yes(Expr), set_of_var.init, doa_alive),
|
|
map.det_insert(Var, State, VarStateMap0, VarStateMap1),
|
|
add_use_ref(BaseVar, Var, VarStateMap1, VarStateMap),
|
|
var_locn_set_var_state_map(VarStateMap, !VLI),
|
|
Code = empty.
|
|
|
|
%---------------------%
|
|
|
|
var_locn_assign_const_to_var(ExprnOpts, Var, ConstRval0, !VLI) :-
|
|
check_var_is_unknown(!.VLI, Var),
|
|
|
|
var_locn_get_var_state_map(!.VLI, VarStateMap0),
|
|
( if expr_is_constant(VarStateMap0, ExprnOpts, ConstRval0, ConstRval) then
|
|
State = var_state(set.init, yes(ConstRval), no, set_of_var.init,
|
|
doa_alive),
|
|
map.det_insert(Var, State, VarStateMap0, VarStateMap),
|
|
var_locn_set_var_state_map(VarStateMap, !VLI)
|
|
else
|
|
unexpected($pred, "supposed constant isn't")
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
var_locn_assign_expr_to_var(Var, Rval, empty, !VLI) :-
|
|
check_var_is_unknown(!.VLI, Var),
|
|
|
|
var_locn_get_var_state_map(!.VLI, VarStateMap0),
|
|
State = var_state(set.init, no, yes(Rval), set_of_var.init, doa_alive),
|
|
map.det_insert(Var, State, VarStateMap0, VarStateMap1),
|
|
|
|
exprn_aux.vars_in_rval(Rval, ContainedVars0),
|
|
list.remove_dups(ContainedVars0, ContainedVars),
|
|
add_use_refs(ContainedVars, Var, VarStateMap1, VarStateMap),
|
|
var_locn_set_var_state_map(VarStateMap, !VLI).
|
|
|
|
%---------------------%
|
|
|
|
var_locn_reassign_mkword_hole_var(Var, Ptag, Rval, Code, !VLI) :-
|
|
var_locn_get_var_state_map(!.VLI, VarStateMap0),
|
|
map.lookup(VarStateMap0, Var, State0),
|
|
( if
|
|
State0 = var_state(Lvals, MaybeConstRval, MaybeExprRval, Using0,
|
|
DeadOrAlive0),
|
|
(
|
|
MaybeConstRval = yes(mkword_hole(Ptag))
|
|
;
|
|
MaybeConstRval = no
|
|
% Already stored value.
|
|
),
|
|
MaybeExprRval = no,
|
|
set_of_var.is_empty(Using0),
|
|
DeadOrAlive0 = doa_alive
|
|
then
|
|
set.fold(clobber_old_lval(Var), Lvals, !VLI),
|
|
|
|
var_locn_get_var_state_map(!.VLI, VarStateMap1),
|
|
map.det_remove(Var, _State1, VarStateMap1, VarStateMap),
|
|
var_locn_set_var_state_map(VarStateMap, !VLI),
|
|
|
|
% Note that if Var was initially bound to a cons_id with a
|
|
% direct_arg_tag cons_tag, then
|
|
%
|
|
% - the argument we are putting into the mkword hole already
|
|
% has a primary tag on it, and
|
|
%
|
|
% - now we are putting another primary tag (Ptag) on top of it.
|
|
%
|
|
% This works because we use direct_arg_tags only when the
|
|
% first ptag is guaranteed to be zero.
|
|
var_locn_assign_expr_to_var(Var, mkword(Ptag, Rval), Code, !VLI)
|
|
else
|
|
unexpected($pred, "unexpected var_state")
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
var_locn_reassign_tagword_var(Var, ToOrMask, ToOrRval0, Code, !VLI) :-
|
|
var_locn_produce_var_in_reg_or_stack(Var, VarLval, VarCode, !VLI),
|
|
var_locn_get_var_state_map(!.VLI, VarStateMap0),
|
|
map.lookup(VarStateMap0, Var, State0),
|
|
( if
|
|
State0 = var_state(Lvals, _MaybeConstRval, _MaybeExprRval, Using0,
|
|
DeadOrAlive0),
|
|
set_of_var.is_empty(Using0),
|
|
DeadOrAlive0 = doa_alive
|
|
then
|
|
% After the assignment we are generating, the new value of Var
|
|
% will be available *only* in VarLval.
|
|
set.delete(VarLval, Lvals, OtherLvals),
|
|
set.fold(clobber_old_lval(Var), OtherLvals, !VLI),
|
|
|
|
ComplementMask = const(llconst_uint(\ ToOrMask)),
|
|
MaskedOldVarRval = binop(bitwise_and(int_type_uint),
|
|
lval(VarLval), ComplementMask),
|
|
var_locn_materialize_vars_in_rval(ToOrRval0, ToOrRval,
|
|
MaterializeCode, !VLI),
|
|
NewVarRval = binop(bitwise_or(int_type_uint),
|
|
MaskedOldVarRval, ToOrRval),
|
|
Comment = "updating tagword",
|
|
AssignCode = singleton(
|
|
llds_instr(assign(VarLval, NewVarRval), Comment)),
|
|
Code = VarCode ++ MaterializeCode ++ AssignCode,
|
|
|
|
var_locn_get_var_state_map(!.VLI, VarStateMap1),
|
|
State = var_state(set.make_singleton_set(VarLval), no, no,
|
|
Using0, DeadOrAlive0),
|
|
map.det_update(Var, State, VarStateMap1, VarStateMap),
|
|
var_locn_set_var_state_map(VarStateMap, !VLI)
|
|
else
|
|
unexpected($pred, "unexpected var_state")
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
var_locn_assign_cell_to_var(ExprnOpts, Var, ReserveWordAtStart,
|
|
Ptag, CellArgs0, HowToConstruct, MaybeSize, MaybeAllocId, MayUseAtomic,
|
|
Label, Code, !StaticCellInfo, !VLI) :-
|
|
(
|
|
MaybeSize = yes(SizeSource),
|
|
(
|
|
SizeSource = known_size(Size),
|
|
SizeRval = const(llconst_int(Size))
|
|
;
|
|
SizeSource = dynamic_size(SizeVar),
|
|
SizeRval = var(SizeVar)
|
|
),
|
|
CellArgs = [cell_arg_full_word(SizeRval, complete) | CellArgs0],
|
|
MaybeOffset = yes(1)
|
|
;
|
|
MaybeSize = no,
|
|
CellArgs = CellArgs0,
|
|
MaybeOffset = no
|
|
),
|
|
var_locn_get_var_state_map(!.VLI, VarStateMap),
|
|
StaticGroundCells = get_static_ground_cells(ExprnOpts),
|
|
% We can make the cell a constant only if all its fields are filled in,
|
|
% and they are all constants.
|
|
( if
|
|
StaticGroundCells = have_static_ground_cells,
|
|
cell_is_constant(VarStateMap, ExprnOpts, CellArgs, TypedRvals)
|
|
then
|
|
add_scalar_static_cell(TypedRvals, DataAddr, !StaticCellInfo),
|
|
(
|
|
MaybeSize = yes(_),
|
|
CellPtrConst = const(llconst_data_addr_word_offset(DataAddr, 1))
|
|
;
|
|
MaybeSize = no,
|
|
CellPtrConst = const(llconst_data_addr(DataAddr))
|
|
),
|
|
CellPtrRval = mkword(Ptag, CellPtrConst),
|
|
var_locn_assign_const_to_var(ExprnOpts, Var, CellPtrRval, !VLI),
|
|
Code = empty
|
|
else
|
|
var_locn_assign_dynamic_cell_to_var(Var, ReserveWordAtStart, Ptag,
|
|
CellArgs, HowToConstruct, MaybeOffset, MaybeAllocId, MayUseAtomic,
|
|
Label, Code, !VLI)
|
|
).
|
|
|
|
:- pred var_locn_assign_dynamic_cell_to_var(prog_var::in, bool::in, ptag::in,
|
|
list(cell_arg)::in, how_to_construct::in, maybe(int)::in,
|
|
maybe(alloc_site_id)::in, may_use_atomic_alloc::in, label::in,
|
|
llds_code::out, var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
var_locn_assign_dynamic_cell_to_var(Var, ReserveWordAtStart, Ptag, CellArgs,
|
|
HowToConstruct, MaybeOffset, MaybeAllocId, MayUseAtomic,
|
|
Label, Code, !VLI) :-
|
|
check_var_is_unknown(!.VLI, Var),
|
|
|
|
select_preferred_reg_or_stack(!.VLI, Var, reg_r, Lval),
|
|
get_var_name(!.VLI, Var, VarName),
|
|
Size = size_of_cell_args(CellArgs),
|
|
(
|
|
ReserveWordAtStart = yes,
|
|
(
|
|
MaybeOffset = yes(_),
|
|
% Accurate GC and term profiling both want to own the word
|
|
% before this object.
|
|
sorry($pred, "accurate GC combined with term size profiling")
|
|
;
|
|
MaybeOffset = no,
|
|
TotalOffset = yes(1)
|
|
),
|
|
TotalSize = Size + 1
|
|
;
|
|
ReserveWordAtStart = no,
|
|
TotalOffset = MaybeOffset,
|
|
TotalSize = Size
|
|
),
|
|
(
|
|
MaybeOffset = yes(Offset),
|
|
StartOffset = -Offset
|
|
;
|
|
MaybeOffset = no,
|
|
StartOffset = 0
|
|
),
|
|
% This must appear before the call to `var_locn_save_cell_fields' in the
|
|
% reused_cell case, otherwise `var_locn_save_cell_fields' won't know not to
|
|
% use Lval as a temporary register (if Lval is a register).
|
|
var_locn_set_magic_var_location(Var, Lval, !VLI),
|
|
(
|
|
(
|
|
HowToConstruct = construct_in_region(RegionVar),
|
|
LldsComment = "Allocating region for ",
|
|
var_locn_produce_var(RegionVar, RegionRval, RegionVarCode, !VLI),
|
|
MaybeRegionRval = yes(RegionRval)
|
|
;
|
|
HowToConstruct = construct_dynamically,
|
|
LldsComment = "Allocating heap for ",
|
|
RegionVarCode = empty,
|
|
MaybeRegionRval = no
|
|
;
|
|
HowToConstruct = construct_statically(_),
|
|
% construct_statically is not normally used in LLDS grades, but
|
|
% it may be set by mark_static_terms.m to support loop invariant
|
|
% hoisting. If we get here then have we missed an opportunity to
|
|
% construct a cell statically?
|
|
% Currently this is reachable when cell_is_constant fails on a
|
|
% dummy type argument (cell_arg_skip_one_word).
|
|
LldsComment = "Allocating heap (unnecessarily?) for ",
|
|
RegionVarCode = empty,
|
|
MaybeRegionRval = no
|
|
),
|
|
assign_all_cell_args(CellArgs, yes(Ptag), lval(Lval), StartOffset,
|
|
ArgsCode, !VLI),
|
|
SetupReuseCode = empty,
|
|
MaybeReuse = no_llds_reuse
|
|
;
|
|
HowToConstruct = reuse_cell(CellToReuse),
|
|
CellToReuse = cell_to_reuse(ReuseVar, _ReuseConsId, _NeedsUpdates0),
|
|
var_locn_produce_var(ReuseVar, ReuseRval, ReuseVarCode, !VLI),
|
|
( if ReuseRval = lval(ReuseLval) then
|
|
LldsComment = "Reusing cell on heap for ",
|
|
assign_reused_cell_to_var(Lval, Ptag, CellArgs, CellToReuse,
|
|
ReuseLval, ReuseVarCode, StartOffset, Label, MaybeReuse,
|
|
SetupReuseCode, ArgsCode, !VLI),
|
|
MaybeRegionRval = no,
|
|
RegionVarCode = empty
|
|
else
|
|
% This can happen if ReuseVar actually points to static data, which
|
|
% the structure reuse analysis wouldn't have known about.
|
|
RegionVarCode = empty,
|
|
MaybeRegionRval = no,
|
|
LldsComment = "Allocating heap for ",
|
|
assign_all_cell_args(CellArgs, yes(Ptag), lval(Lval), StartOffset,
|
|
ArgsCode, !VLI),
|
|
SetupReuseCode = empty,
|
|
MaybeReuse = no_llds_reuse
|
|
)
|
|
),
|
|
CellCode = singleton(
|
|
llds_instr(
|
|
incr_hp(Lval, yes(Ptag), TotalOffset,
|
|
const(llconst_int(TotalSize)), MaybeAllocId, MayUseAtomic,
|
|
MaybeRegionRval, MaybeReuse),
|
|
LldsComment ++ VarName)
|
|
),
|
|
Code = SetupReuseCode ++ CellCode ++ RegionVarCode ++ ArgsCode.
|
|
|
|
:- pred assign_reused_cell_to_var(lval::in, ptag::in, list(cell_arg)::in,
|
|
cell_to_reuse::in, lval::in, llds_code::in, int::in, label::in,
|
|
llds_reuse::out, llds_code::out, llds_code::out,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
assign_reused_cell_to_var(Lval, Ptag, CellArgs, CellToReuse,
|
|
ReuseLval, ReuseVarCode, StartOffset, Label, MaybeReuse,
|
|
SetupReuseCode, ArgsCode, !VLI) :-
|
|
CellToReuse = cell_to_reuse(ReuseVar, _ReuseConsId, NeedsUpdates0),
|
|
|
|
% Save any variables which are available only in the reused cell into
|
|
% temporary registers.
|
|
var_locn_save_cell_fields(ReuseVar, ReuseLval, SaveArgsCode,
|
|
TempRegs0, !VLI),
|
|
SetupReuseCode = ReuseVarCode ++ SaveArgsCode,
|
|
|
|
% If it's possible to avoid some field assignments, we'll need an extra
|
|
% temporary register to record whether we actually are reusing a structure
|
|
% or if a new object was allocated.
|
|
( if list.member(does_not_need_update, NeedsUpdates0) then
|
|
var_locn_acquire_reg(reg_r, FlagReg, !VLI),
|
|
MaybeFlag = yes(FlagReg),
|
|
TempRegs = [FlagReg | TempRegs0]
|
|
else
|
|
MaybeFlag = no,
|
|
TempRegs = TempRegs0
|
|
),
|
|
|
|
% XXX Optimise the stripping of the ptag when the ptags are the same
|
|
% or the old ptag is known, as we do in the high level backend.
|
|
MaybeReuse = llds_reuse(unop(strip_tag, lval(ReuseLval)), MaybeFlag),
|
|
|
|
% NeedsUpdates0 can be shorter than CellArgs due to extra fields.
|
|
Padding = list.length(CellArgs) - list.length(NeedsUpdates0),
|
|
( if Padding >= 0 then
|
|
NeedsUpdates = list.duplicate(Padding, needs_update) ++ NeedsUpdates0
|
|
else
|
|
unexpected($pred, "Padding < 0")
|
|
),
|
|
|
|
(
|
|
MaybeFlag = yes(FlagLval),
|
|
assign_some_cell_args(CellArgs, NeedsUpdates, yes(Ptag), lval(Lval),
|
|
StartOffset, CannotSkipArgsCode, CanSkipArgsCode, !VLI),
|
|
ArgsCode =
|
|
singleton(
|
|
llds_instr(if_val(lval(FlagLval), code_label(Label)),
|
|
"skip some field assignments")
|
|
) ++
|
|
CanSkipArgsCode ++
|
|
singleton(
|
|
llds_instr(label(Label),
|
|
"past skipped field assignments")
|
|
) ++
|
|
CannotSkipArgsCode
|
|
;
|
|
MaybeFlag = no,
|
|
assign_all_cell_args(CellArgs, yes(Ptag), lval(Lval), StartOffset,
|
|
ArgsCode, !VLI)
|
|
),
|
|
|
|
list.foldl(var_locn_release_reg, TempRegs, !VLI).
|
|
|
|
:- pred assign_all_cell_args(list(cell_arg)::in, maybe(ptag)::in,
|
|
rval::in, int::in, llds_code::out,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
assign_all_cell_args([], _, _, _, empty, !VLI).
|
|
assign_all_cell_args([CellArg | CellArgs], Ptag, Base, Offset, Code, !VLI) :-
|
|
(
|
|
( CellArg = cell_arg_full_word(Rval, _Completeness)
|
|
; CellArg = cell_arg_take_addr_one_word(_, yes(Rval))
|
|
),
|
|
assign_cell_arg(Rval, Ptag, Base, Offset, ThisCode, !VLI),
|
|
NextOffset = Offset + 1
|
|
;
|
|
(
|
|
CellArg = cell_arg_double_word(Rval0),
|
|
materialize_if_var(Rval0, EvalCode, Rval, !VLI),
|
|
RvalA = unop(dword_float_get_word0, Rval),
|
|
RvalB = unop(dword_float_get_word1, Rval)
|
|
;
|
|
CellArg = cell_arg_take_addr_two_words(_, yes({RvalA, RvalB})),
|
|
EvalCode = cord.init
|
|
),
|
|
assign_cell_arg(RvalA, Ptag, Base, Offset, ThisCodeA, !VLI),
|
|
assign_cell_arg(RvalB, Ptag, Base, Offset + 1, ThisCodeB, !VLI),
|
|
ThisCode = EvalCode ++ ThisCodeA ++ ThisCodeB,
|
|
NextOffset = Offset + 2
|
|
;
|
|
( CellArg = cell_arg_skip_one_word
|
|
; CellArg = cell_arg_take_addr_one_word(_, no)
|
|
),
|
|
ThisCode = empty,
|
|
NextOffset = Offset + 1
|
|
;
|
|
( CellArg = cell_arg_skip_two_words
|
|
; CellArg = cell_arg_take_addr_two_words(_, no)
|
|
),
|
|
ThisCode = empty,
|
|
NextOffset = Offset + 2
|
|
),
|
|
assign_all_cell_args(CellArgs, Ptag, Base, NextOffset, RestCode, !VLI),
|
|
Code = ThisCode ++ RestCode.
|
|
|
|
:- pred assign_some_cell_args(list(cell_arg)::in, list(needs_update)::in,
|
|
maybe(ptag)::in, rval::in, int::in, llds_code::out,
|
|
llds_code::out, var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
assign_some_cell_args([], [], _, _, _, empty, empty, !VLI).
|
|
assign_some_cell_args([], [_ | _], _, _, _, _, _, !VLI) :-
|
|
unexpected($pred, "mismatch lists").
|
|
assign_some_cell_args([_ | _], [], _, _, _, _, _, !VLI) :-
|
|
unexpected($pred, "mismatch lists").
|
|
assign_some_cell_args([CellArg | CellArgs], [NeedsUpdate | NeedsUpdates],
|
|
Ptag, Base, Offset, CannotSkipArgsCode, CanSkipArgsCode, !VLI) :-
|
|
(
|
|
( CellArg = cell_arg_full_word(Rval, _Completeness)
|
|
; CellArg = cell_arg_take_addr_one_word(_, yes(Rval))
|
|
),
|
|
assign_cell_arg(Rval, Ptag, Base, Offset, ThisCode, !VLI),
|
|
NextOffset = Offset + 1
|
|
;
|
|
(
|
|
CellArg = cell_arg_double_word(Rval0),
|
|
materialize_if_var(Rval0, EvalCode, Rval, !VLI),
|
|
RvalA = unop(dword_float_get_word0, Rval),
|
|
RvalB = unop(dword_float_get_word1, Rval)
|
|
;
|
|
CellArg = cell_arg_take_addr_two_words(_, yes({RvalA, RvalB})),
|
|
EvalCode = cord.init
|
|
),
|
|
assign_cell_arg(RvalA, Ptag, Base, Offset, ThisCodeA, !VLI),
|
|
assign_cell_arg(RvalB, Ptag, Base, Offset + 1, ThisCodeB, !VLI),
|
|
ThisCode = EvalCode ++ ThisCodeA ++ ThisCodeB,
|
|
NextOffset = Offset + 2
|
|
;
|
|
( CellArg = cell_arg_skip_one_word
|
|
; CellArg = cell_arg_take_addr_one_word(_, no)
|
|
),
|
|
ThisCode = empty,
|
|
NextOffset = Offset + 1
|
|
;
|
|
( CellArg = cell_arg_skip_two_words
|
|
; CellArg = cell_arg_take_addr_two_words(_, no)
|
|
),
|
|
ThisCode = empty,
|
|
NextOffset = Offset + 2
|
|
),
|
|
assign_some_cell_args(CellArgs, NeedsUpdates, Ptag, Base,
|
|
NextOffset, RestCannotSkipArgsCode, RestCanSkipArgsCode, !VLI),
|
|
(
|
|
NeedsUpdate = needs_update,
|
|
CannotSkipArgsCode = ThisCode ++ RestCannotSkipArgsCode,
|
|
CanSkipArgsCode = RestCanSkipArgsCode
|
|
;
|
|
NeedsUpdate = does_not_need_update,
|
|
CannotSkipArgsCode = RestCannotSkipArgsCode,
|
|
CanSkipArgsCode = ThisCode ++ RestCanSkipArgsCode
|
|
).
|
|
|
|
:- pred assign_cell_arg(rval::in, maybe(ptag)::in, rval::in,
|
|
int::in, llds_code::out, var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
assign_cell_arg(Rval0, Ptag, Base, Offset, Code, !VLI) :-
|
|
Target = field(Ptag, Base, const(llconst_int(Offset))),
|
|
(
|
|
Rval0 = var(Var),
|
|
materialize_if_var(Rval0, EvalCode, Rval, !VLI),
|
|
var_locn_get_var_table(!.VLI, VarTable),
|
|
lookup_var_entry(VarTable, Var, VarEntry),
|
|
VarEntry = vte(_N, _T, VarIsDummy),
|
|
(
|
|
VarIsDummy = is_dummy_type,
|
|
AssignCode = empty
|
|
;
|
|
VarIsDummy = is_not_dummy_type,
|
|
add_additional_lval_for_var(Var, Target, !VLI),
|
|
get_var_name(!.VLI, Var, VarName),
|
|
Comment = "assigning from " ++ VarName,
|
|
AssignCode = singleton(llds_instr(assign(Target, Rval), Comment))
|
|
)
|
|
;
|
|
(
|
|
Rval0 = const(_),
|
|
Comment = "assigning field from const"
|
|
;
|
|
Rval0 = mkword(_, _),
|
|
Comment = "assigning field from tagged pointer"
|
|
;
|
|
Rval0 = cast(_, _),
|
|
Comment = "assigning field from cast"
|
|
;
|
|
Rval0 = unop(_, _),
|
|
Comment = "assigning field from unary op"
|
|
;
|
|
Rval0 = binop(_, _, _),
|
|
Comment = "assigning field from binary op"
|
|
;
|
|
Rval0 = lval(_),
|
|
Comment = "assigning field"
|
|
),
|
|
EvalCode = empty,
|
|
AssignCode = singleton(llds_instr(assign(Target, Rval0), Comment))
|
|
;
|
|
Rval0 = mkword_hole(_),
|
|
unexpected($pred, "mkword_hole")
|
|
;
|
|
Rval0 = mem_addr(_),
|
|
unexpected($pred, "unknown rval")
|
|
),
|
|
Code = EvalCode ++ AssignCode.
|
|
|
|
%---------------------%
|
|
|
|
:- pred cell_is_constant(var_state_map::in, exprn_opts::in,
|
|
list(cell_arg)::in, list(typed_rval)::out) is semidet.
|
|
|
|
cell_is_constant(_VarStateMap, _ExprnOpts, [], []).
|
|
cell_is_constant(VarStateMap, ExprnOpts, [CellArg | CellArgs],
|
|
[typed_rval(Rval, LldsType) | TypedRvals]) :-
|
|
require_complete_switch [CellArg]
|
|
(
|
|
CellArg = cell_arg_full_word(Rval0, complete),
|
|
NumWords = one_word
|
|
;
|
|
CellArg = cell_arg_double_word(Rval0),
|
|
NumWords = two_words
|
|
;
|
|
( CellArg = cell_arg_take_addr_one_word(_, _)
|
|
; CellArg = cell_arg_take_addr_two_words(_, _)
|
|
; CellArg = cell_arg_skip_two_words
|
|
; CellArg = cell_arg_skip_one_word
|
|
),
|
|
fail
|
|
),
|
|
expr_is_constant(VarStateMap, ExprnOpts, Rval0, Rval),
|
|
UnboxedFloats = get_unboxed_floats(ExprnOpts),
|
|
UnboxedInt64s = get_unboxed_int64s(ExprnOpts),
|
|
LldsType = rval_type_as_arg(UnboxedFloats, UnboxedInt64s, NumWords, Rval),
|
|
cell_is_constant(VarStateMap, ExprnOpts, CellArgs, TypedRvals).
|
|
|
|
% 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.
|
|
%
|
|
:- pred expr_is_constant(var_state_map::in, exprn_opts::in,
|
|
rval::in, rval::out) is semidet.
|
|
|
|
expr_is_constant(VarStateMap, ExprnOpts, Rval0, Rval) :-
|
|
require_complete_switch [Rval0]
|
|
(
|
|
Rval0 = const(Const),
|
|
exprn_aux.const_is_constant(Const, ExprnOpts, yes),
|
|
Rval = Rval0
|
|
;
|
|
Rval0 = cast(Type, SubRval0),
|
|
expr_is_constant(VarStateMap, ExprnOpts, SubRval0, SubRval),
|
|
Rval = cast(Type, SubRval)
|
|
;
|
|
Rval0 = unop(UnOp, SubRval0),
|
|
expr_is_constant(VarStateMap, ExprnOpts, SubRval0, SubRval),
|
|
Rval = unop(UnOp, SubRval)
|
|
;
|
|
Rval0 = binop(BinOp, SubRvalA0, SubRvalB0),
|
|
expr_is_constant(VarStateMap, ExprnOpts, SubRvalA0, SubRvalA),
|
|
expr_is_constant(VarStateMap, ExprnOpts, SubRvalB0, SubRvalB),
|
|
Rval = binop(BinOp, SubRvalA, SubRvalB)
|
|
;
|
|
Rval0 = mkword(Tag, SubRval0),
|
|
expr_is_constant(VarStateMap, ExprnOpts, SubRval0, SubRval),
|
|
Rval = mkword(Tag, SubRval)
|
|
;
|
|
Rval0 = mkword_hole(_Tag),
|
|
Rval = Rval0
|
|
;
|
|
Rval0 = var(Var),
|
|
map.search(VarStateMap, Var, State),
|
|
State = var_state(_, yes(Rval), _, _, _),
|
|
expect(expr_is_constant(VarStateMap, ExprnOpts, Rval, _),
|
|
$pred, "non-constant rval in variable state")
|
|
;
|
|
( Rval0 = lval(_)
|
|
; Rval0 = mem_addr(_)
|
|
),
|
|
fail
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
:- pred check_var_is_unknown(var_locn_info::in, prog_var::in) is det.
|
|
|
|
check_var_is_unknown(VLI, Var) :-
|
|
var_locn_get_var_state_map(VLI, VarStateMap0),
|
|
( if map.search(VarStateMap0, Var, _) then
|
|
get_var_name(VLI, Var, Name),
|
|
unexpected($pred, "existing definition of variable " ++ Name)
|
|
else
|
|
true
|
|
).
|
|
|
|
% 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) :-
|
|
var_locn_get_loc_var_map(!.VLI, LocVarMap0),
|
|
make_var_depend_on_lval_roots(Var, Lval, LocVarMap0, LocVarMap),
|
|
var_locn_set_loc_var_map(LocVarMap, !VLI),
|
|
|
|
var_locn_get_var_state_map(!.VLI, VarStateMap0),
|
|
map.lookup(VarStateMap0, Var, State0),
|
|
State0 = var_state(LvalSet0, MaybeConstRval, MaybeExprRval0,
|
|
Using, DeadOrAlive),
|
|
set.insert(Lval, LvalSet0, LvalSet),
|
|
State = var_state(LvalSet, MaybeConstRval, no, Using, DeadOrAlive),
|
|
map.det_update(Var, State, VarStateMap0, VarStateMap),
|
|
var_locn_set_var_state_map(VarStateMap, !VLI),
|
|
|
|
remove_use_refs(MaybeExprRval0, Var, !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 = var_state(Lvals, MaybeConstRval, MaybeExprRval, Using0,
|
|
DeadOrAlive),
|
|
set_of_var.insert(UsingVar, Using0, Using),
|
|
State = var_state(Lvals, MaybeConstRval, MaybeExprRval, Using,
|
|
DeadOrAlive),
|
|
map.det_update(ContainedVar, State, !VarStateMap).
|
|
|
|
:- 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_loop(ContainedVars, UsingVar, !VLI)
|
|
;
|
|
MaybeExprRval = no
|
|
).
|
|
|
|
:- pred remove_use_refs_loop(list(prog_var)::in, prog_var::in,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
remove_use_refs_loop([], _, !VLI).
|
|
remove_use_refs_loop([ContainedVar | ContainedVars], UsingVar, !VLI) :-
|
|
var_locn_get_var_state_map(!.VLI, VarStateMap0),
|
|
map.lookup(VarStateMap0, ContainedVar, State0),
|
|
State0 = var_state(Lvals, MaybeConstRval, MaybeExprRval, Using0,
|
|
DeadOrAlive),
|
|
( if set_of_var.remove(UsingVar, Using0, Using1) then
|
|
Using = Using1
|
|
else
|
|
unexpected($pred, "using ref not present")
|
|
),
|
|
State = var_state(Lvals, MaybeConstRval, MaybeExprRval, Using,
|
|
DeadOrAlive),
|
|
map.det_update(ContainedVar, State, VarStateMap0, VarStateMap),
|
|
var_locn_set_var_state_map(VarStateMap, !VLI),
|
|
( if
|
|
set_of_var.is_empty(Using),
|
|
DeadOrAlive = doa_dead
|
|
then
|
|
var_locn_var_becomes_dead(maybe_not_first_death, ContainedVar, !VLI)
|
|
else
|
|
true
|
|
),
|
|
remove_use_refs_loop(ContainedVars, UsingVar, !VLI).
|
|
|
|
:- pred clobber_old_lval(prog_var::in, lval::in,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
clobber_old_lval(Var, Lval, !VLI) :-
|
|
record_clobbering(Lval, [Var], !VLI).
|
|
|
|
:- pred record_clobbering(lval::in, list(prog_var)::in,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
record_clobbering(Target, Assigns, !VLI) :-
|
|
var_locn_get_loc_var_map(!.VLI, LocVarMap1),
|
|
( if map.search(LocVarMap1, Target, DependentVarsSet) then
|
|
set_of_var.to_sorted_list(DependentVarsSet, DependentVars),
|
|
map.delete(Target, LocVarMap1, LocVarMap),
|
|
var_locn_set_loc_var_map(LocVarMap, !VLI),
|
|
|
|
var_locn_get_var_state_map(!.VLI, VarStateMap2),
|
|
list.foldl(clobber_lval_in_var_state_map(Target, Assigns, no),
|
|
DependentVars, VarStateMap2, VarStateMap),
|
|
var_locn_set_var_state_map(VarStateMap, !VLI)
|
|
else
|
|
true
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Assign nonexistent ("magical") values to variables at code points
|
|
% that cannot be reached, to provide a consistent view of the state
|
|
% of each variable at each branch end of a branched control structure.
|
|
%
|
|
|
|
var_locn_set_magic_var_location(Var, Lval, !VLI) :-
|
|
var_locn_get_loc_var_map(!.VLI, LocVarMap0),
|
|
make_var_depend_on_lval_roots(Var, Lval, LocVarMap0, LocVarMap),
|
|
var_locn_set_loc_var_map(LocVarMap, !VLI),
|
|
|
|
var_locn_get_var_state_map(!.VLI, VarStateMap0),
|
|
LvalSet = set.make_singleton_set(Lval),
|
|
State = var_state(LvalSet, no, no, set_of_var.init, doa_alive),
|
|
map.det_insert(Var, State, VarStateMap0, VarStateMap),
|
|
var_locn_set_var_state_map(VarStateMap, !VLI).
|
|
|
|
var_locn_check_and_set_magic_var_location(Var, Lval, !VLI) :-
|
|
( if var_locn_lval_in_use(!.VLI, Lval) then
|
|
unexpected($pred, "in use")
|
|
else
|
|
var_locn_set_magic_var_location(Var, Lval, !VLI)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Placing the values of variables in unspecified or incompletely
|
|
% specified locations.
|
|
%
|
|
|
|
var_locn_produce_var(Var, Rval, Code, !VLI) :-
|
|
var_locn_get_var_state_map(!.VLI, VarStateMap),
|
|
map.lookup(VarStateMap, Var, State),
|
|
State = var_state(Lvals, MaybeConstRval, MaybeExprRval, _, _),
|
|
set.to_sorted_list(Lvals, LvalsList),
|
|
( if
|
|
maybe_select_lval_or_rval(LvalsList, MaybeConstRval, Rval1)
|
|
then
|
|
Rval = Rval1,
|
|
Code = empty
|
|
else if
|
|
MaybeExprRval = yes(var(ExprVar)),
|
|
map.lookup(VarStateMap, ExprVar, ExprState),
|
|
ExprState = var_state(ExprLvals, ExprMaybeConstRval, _, _, _),
|
|
set.to_sorted_list(ExprLvals, ExprLvalsList),
|
|
maybe_select_lval_or_rval(ExprLvalsList, ExprMaybeConstRval, Rval2)
|
|
then
|
|
% This path is designed to generate efficient code
|
|
% mainly for variables produced by unsafe_cast goals.
|
|
Rval = Rval2,
|
|
Code = empty
|
|
else
|
|
reg_type_for_var(!.VLI, Var, RegType),
|
|
select_preferred_reg(!.VLI, Var, RegType, Lval),
|
|
var_locn_place_var(Var, Lval, Code, !VLI),
|
|
Rval = lval(Lval)
|
|
).
|
|
|
|
var_locn_produce_var_in_reg(Var, Lval, Code, !VLI) :-
|
|
var_locn_get_var_state_map(!.VLI, VarStateMap),
|
|
map.lookup(VarStateMap, Var, State),
|
|
State = var_state(Lvals, _, _, _, _),
|
|
set.to_sorted_list(Lvals, LvalList),
|
|
( if select_reg_lval(LvalList, SelectLval) then
|
|
Lval = SelectLval,
|
|
Code = empty
|
|
else
|
|
reg_type_for_var(!.VLI, Var, RegType),
|
|
select_preferred_reg(!.VLI, Var, RegType, Lval),
|
|
var_locn_place_var(Var, Lval, Code, !VLI)
|
|
).
|
|
|
|
var_locn_produce_var_in_reg_or_stack(Var, Lval, Code, !VLI) :-
|
|
var_locn_get_var_state_map(!.VLI, VarStateMap),
|
|
map.lookup(VarStateMap, Var, State),
|
|
State = var_state(Lvals, _, _, _, _),
|
|
set.to_sorted_list(Lvals, LvalList),
|
|
( if select_reg_or_stack_lval(LvalList, SelectLval) then
|
|
Lval = SelectLval,
|
|
Code = empty
|
|
else
|
|
reg_type_for_var(!.VLI, Var, RegType),
|
|
select_preferred_reg_or_stack(!.VLI, Var, RegType, Lval),
|
|
var_locn_place_var(Var, Lval, Code, !VLI)
|
|
).
|
|
|
|
:- pred reg_type_for_var(var_locn_info::in, prog_var::in, reg_type::out)
|
|
is det.
|
|
|
|
reg_type_for_var(VLI, Var, RegType) :-
|
|
var_locn_get_float_reg_type(VLI, FloatRegType),
|
|
(
|
|
FloatRegType = reg_r,
|
|
RegType = reg_r
|
|
;
|
|
FloatRegType = reg_f,
|
|
var_locn_get_var_table(VLI, VarTable),
|
|
lookup_var_type(VarTable, Var, VarType),
|
|
( if VarType = float_type then
|
|
RegType = reg_f
|
|
else
|
|
RegType = reg_r
|
|
)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Placing the values of variables in specified locations.
|
|
%
|
|
|
|
var_locn_place_var(Var, Target, Code, !VLI) :-
|
|
actually_place_var(Var, Target, [], Code, !VLI).
|
|
|
|
var_locn_place_vars(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_regs(Lvals, MaxRegR, MaxRegF),
|
|
var_locn_lock_regs(MaxRegR, MaxRegF, VarLocns, !VLI),
|
|
actually_place_vars(VarLocns, Code, !VLI),
|
|
var_locn_unlock_regs(!VLI).
|
|
|
|
:- pred actually_place_vars(assoc_list(prog_var, lval)::in, llds_code::out,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
actually_place_vars([], empty, !VLI).
|
|
actually_place_vars([Var - Lval | Rest], Code, !VLI) :-
|
|
var_locn_place_var(Var, Lval, FirstCode, !VLI),
|
|
actually_place_vars(Rest, RestCode, !VLI),
|
|
Code = FirstCode ++ RestCode.
|
|
|
|
:- pred actually_place_var(prog_var::in, lval::in, list(lval)::in,
|
|
llds_code::out, var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
actually_place_var(Var, Target, ForbiddenLvals, Code, !VLI) :-
|
|
var_locn_get_acquired(!.VLI, Acquired),
|
|
( if set.member(Target, Acquired) then
|
|
unexpected($pred, "target is acquired reg")
|
|
else
|
|
true
|
|
),
|
|
var_locn_get_var_state_map(!.VLI, VarStateMap0),
|
|
( if map.search(VarStateMap0, Var, State0) then
|
|
State0 = var_state(Lvals0, _, _, _, _),
|
|
( if set.member(Target, Lvals0) then
|
|
Code = empty
|
|
else
|
|
free_up_lval(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),
|
|
FreeEvalCode = FreeCode,
|
|
( if Rval = lval(SourceLval) then
|
|
record_copy(SourceLval, Target, !VLI)
|
|
else
|
|
record_clobbering(Target, [Var], !VLI)
|
|
)
|
|
;
|
|
Avail = needs_materialization,
|
|
materialize_var_general(Var, yes(Target), do_not_store_var,
|
|
[Target], Rval, EvalCode, !VLI),
|
|
record_clobbering(Target, [Var], !VLI),
|
|
FreeEvalCode = FreeCode ++ EvalCode
|
|
),
|
|
|
|
% Record that Var is now in Target.
|
|
add_additional_lval_for_var(Var, Target, !VLI),
|
|
|
|
( if Rval = lval(Target) then
|
|
Code = FreeEvalCode
|
|
else
|
|
var_locn_get_var_table(!.VLI, VarTable),
|
|
lookup_var_entry(VarTable, Var, VarEntry),
|
|
VarEntry = vte(_N, _T, VarIsDummy),
|
|
(
|
|
VarIsDummy = is_dummy_type,
|
|
Code = FreeEvalCode
|
|
;
|
|
VarIsDummy = is_not_dummy_type,
|
|
VarName = var_entry_name(Var, VarEntry),
|
|
(
|
|
ForbiddenLvals = [],
|
|
string.format("Placing %s", [s(VarName)], Msg)
|
|
;
|
|
ForbiddenLvals = [_ | _],
|
|
string.format("Placing %s (depth %d)",
|
|
[s(VarName), i(list.length(ForbiddenLvals))], Msg)
|
|
),
|
|
AssignCode = singleton(
|
|
llds_instr(assign(Target, Rval), Msg)),
|
|
Code = FreeEvalCode ++ AssignCode
|
|
)
|
|
)
|
|
)
|
|
else
|
|
var_locn_get_var_table(!.VLI, VarTable),
|
|
lookup_var_entry(VarTable, Var, VarEntry),
|
|
VarEntry = vte(_N, _T, VarIsDummy),
|
|
(
|
|
VarIsDummy = is_dummy_type
|
|
;
|
|
VarIsDummy = is_not_dummy_type,
|
|
string.format("placing nondummy var %d which has no state",
|
|
[i(var_to_int(Var))], NonDummyMsg),
|
|
unexpected($pred, NonDummyMsg)
|
|
),
|
|
Code = empty
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
% 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(lval::in, list(prog_var)::in, list(lval)::in,
|
|
llds_code::out, var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
free_up_lval(Lval, ToBeAssignedVars, ForbiddenLvals, Code, !VLI) :-
|
|
( if
|
|
var_locn_get_loc_var_map(!.VLI, LocVarMap0),
|
|
map.search(LocVarMap0, Lval, AffectedVarSet),
|
|
set_of_var.to_sorted_list(AffectedVarSet, AffectedVars),
|
|
var_locn_get_var_state_map(!.VLI, VarStateMap0),
|
|
not list.foldl(
|
|
try_clobber_lval_in_var_state_map(Lval, ToBeAssignedVars, no),
|
|
AffectedVars, VarStateMap0, _)
|
|
then
|
|
free_up_lval_with_copy(Lval, ToBeAssignedVars, ForbiddenLvals,
|
|
Code, !VLI)
|
|
else
|
|
Code = empty
|
|
).
|
|
|
|
% If we must copy the value in Lval somewhere else to prevent it from being
|
|
% lost when Lval is 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(lval::in, list(prog_var)::in, list(lval)::in,
|
|
llds_code::out, var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
free_up_lval_with_copy(Lval, ToBeAssignedVars, ForbiddenLvals, Code, !VLI) :-
|
|
( if
|
|
var_locn_get_loc_var_map(!.VLI, LocVarMap0),
|
|
map.search(LocVarMap0, Lval, AffectedVarSet),
|
|
set_of_var.delete_list(ToBeAssignedVars, AffectedVarSet,
|
|
EffAffectedVarSet),
|
|
set_of_var.to_sorted_list(EffAffectedVarSet, EffAffectedVars),
|
|
|
|
var_locn_get_var_state_map(!.VLI, VarStateMap0),
|
|
( if
|
|
find_one_occupying_var(EffAffectedVars, Lval, VarStateMap0,
|
|
OccupyingVar, OtherSources)
|
|
then
|
|
MovedVar = OccupyingVar,
|
|
list.delete_all(EffAffectedVars, MovedVar, OtherVars),
|
|
list.foldl(ensure_copies_are_present(Lval, OtherSources),
|
|
OtherVars, !VLI)
|
|
else
|
|
EffAffectedVars = [MovedVar]
|
|
),
|
|
|
|
reg_type_for_var(!.VLI, MovedVar, RegType),
|
|
select_preferred_reg_or_stack(!.VLI, MovedVar, RegType, Pref),
|
|
not Pref = Lval,
|
|
not list.member(Pref, ForbiddenLvals),
|
|
( if var_locn_lval_in_use(!.VLI, Pref) then
|
|
% 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(PrefRegType, RegNum),
|
|
reg_is_not_locked_for_var(!.VLI, PrefRegType, RegNum, MovedVar)
|
|
else
|
|
true
|
|
)
|
|
then
|
|
actually_place_var(MovedVar, Pref, [Lval | ForbiddenLvals], Code, !VLI)
|
|
else
|
|
RegType = lval_spare_reg_type(Lval),
|
|
get_spare_reg(!.VLI, RegType, Target),
|
|
record_copy(Lval, Target, !VLI),
|
|
( if
|
|
( Lval = stackvar(N)
|
|
; Lval = framevar(N)
|
|
),
|
|
N < 0
|
|
then
|
|
% 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
|
|
else
|
|
Code = singleton(
|
|
llds_instr(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 = var_state(LvalSet, _, _, _, _),
|
|
( if set.member(Lval, LvalSet) then
|
|
OccupyingVar = Var,
|
|
set.delete(Lval, LvalSet, OtherSourceSet),
|
|
set.to_sorted_list(OtherSourceSet, OtherSources)
|
|
else
|
|
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) :-
|
|
var_locn_get_var_state_map(!.VLI, VarStateMap0),
|
|
map.lookup(VarStateMap0, Var, State0),
|
|
State0 = var_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 = var_state(LvalSet, MaybeConstRval, MaybeExprRval, Using,
|
|
DeadOrAlive),
|
|
map.det_update(Var, State, VarStateMap0, VarStateMap),
|
|
var_locn_set_var_state_map(VarStateMap, !VLI),
|
|
|
|
var_locn_get_loc_var_map(!.VLI, LocVarMap0),
|
|
record_change_in_root_dependencies(LvalSet0, LvalSet, Var,
|
|
LocVarMap0, LocVarMap),
|
|
var_locn_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) :-
|
|
substitute_lval_in_lval(OneSource, OtherSource, Lval, SubstLval),
|
|
set.insert(SubstLval, !LvalSet),
|
|
ensure_copies_are_present_lval(OtherSources, OneSource, Lval, !LvalSet).
|
|
|
|
:- func lval_spare_reg_type(lval) = reg_type.
|
|
|
|
lval_spare_reg_type(Lval) = RegType :-
|
|
( if Lval = reg(reg_f, _) then
|
|
RegType = reg_f
|
|
else if Lval = double_stackvar(_, _) then
|
|
RegType = reg_f
|
|
else
|
|
RegType = reg_r
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
% 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), $pred, "non-root New lval"),
|
|
var_locn_get_var_state_map(!.VLI, VarStateMap0),
|
|
var_locn_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_of_var.union, AffectedVarSets,
|
|
set_of_var.init, AffectedVarSet),
|
|
|
|
% Convert the union set to a list of affected vars.
|
|
set_of_var.to_sorted_list(AffectedVarSet, AffectedVars),
|
|
list.foldl2(record_copy_for_var(Old, New), AffectedVars,
|
|
VarStateMap0, VarStateMap, LocVarMap0, LocVarMap),
|
|
var_locn_set_loc_var_map(LocVarMap, !VLI),
|
|
var_locn_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 = var_state(LvalSet0, MaybeConstRval, MaybeExprRval,
|
|
Using, DeadOrAlive),
|
|
Token = reg(reg_r, -42),
|
|
set.map(substitute_lval_in_lval(Old, Token), LvalSet0, LvalSet1),
|
|
set.union(LvalSet0, LvalSet1, LvalSet2),
|
|
LvalSet3 = set.filter(lval_does_not_support_lval(New), LvalSet2),
|
|
set.map(substitute_lval_in_lval(Token, New), LvalSet3, LvalSet),
|
|
State = var_state(LvalSet, MaybeConstRval, MaybeExprRval,
|
|
Using, DeadOrAlive),
|
|
expect(nonempty_state(State), $pred, "empty state"),
|
|
map.det_update(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).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Materializing the values of variables.
|
|
%
|
|
|
|
var_locn_materialize_vars_in_lval(Lval0, Lval, Code, !VLI) :-
|
|
materialize_vars_in_lval_avoid(Lval0, [], Lval, Code, !VLI).
|
|
|
|
var_locn_materialize_vars_in_rval(Rval0, Rval, Code, !VLI) :-
|
|
materialize_vars_in_rval_avoid(Rval0, no, [], Rval, Code, !VLI).
|
|
|
|
:- pred materialize_vars_in_lval_avoid(lval::in, list(lval)::in,
|
|
lval::out, llds_code::out,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
materialize_vars_in_lval_avoid(Lval0, Avoid, Lval, Code, !VLI) :-
|
|
(
|
|
( Lval0 = reg(_, _)
|
|
; Lval0 = stackvar(_)
|
|
; Lval0 = parent_stackvar(_)
|
|
; Lval0 = framevar(_)
|
|
; Lval0 = double_stackvar(_, _)
|
|
; Lval0 = global_var_ref(_)
|
|
; Lval0 = succip
|
|
; Lval0 = maxfr
|
|
; Lval0 = curfr
|
|
; Lval0 = hp
|
|
; Lval0 = sp
|
|
; Lval0 = parent_sp
|
|
),
|
|
Lval = Lval0,
|
|
Code = empty
|
|
;
|
|
Lval0 = succip_slot(Rval0),
|
|
materialize_vars_in_rval_avoid(Rval0, no, Avoid, Rval, Code, !VLI),
|
|
Lval = succip_slot(Rval)
|
|
;
|
|
Lval0 = redoip_slot(Rval0),
|
|
materialize_vars_in_rval_avoid(Rval0, no, Avoid, Rval, Code, !VLI),
|
|
Lval = redoip_slot(Rval)
|
|
;
|
|
Lval0 = succfr_slot(Rval0),
|
|
materialize_vars_in_rval_avoid(Rval0, no, Avoid, Rval, Code, !VLI),
|
|
Lval = succfr_slot(Rval)
|
|
;
|
|
Lval0 = redofr_slot(Rval0),
|
|
materialize_vars_in_rval_avoid(Rval0, no, Avoid, Rval, Code, !VLI),
|
|
Lval = redofr_slot(Rval)
|
|
;
|
|
Lval0 = prevfr_slot(Rval0),
|
|
materialize_vars_in_rval_avoid(Rval0, no, Avoid, Rval, Code, !VLI),
|
|
Lval = prevfr_slot(Rval)
|
|
;
|
|
Lval0 = mem_ref(Rval0),
|
|
materialize_vars_in_rval_avoid(Rval0, no, Avoid, Rval, Code, !VLI),
|
|
Lval = mem_ref(Rval)
|
|
;
|
|
Lval0 = field(Tag, RvalA0, RvalB0),
|
|
materialize_vars_in_rval_avoid(RvalA0, no, Avoid, RvalA, CodeA, !VLI),
|
|
materialize_vars_in_rval_avoid(RvalB0, no, Avoid, RvalB, CodeB, !VLI),
|
|
Lval = field(Tag, RvalA, RvalB),
|
|
Code = CodeA ++ CodeB
|
|
;
|
|
Lval0 = temp(_, _),
|
|
unexpected($pred, "temp")
|
|
;
|
|
Lval0 = lvar(_),
|
|
unexpected($pred, "lvar")
|
|
).
|
|
|
|
% Rval is Rval0 with all variables in Rval0 replaced by their values.
|
|
%
|
|
:- pred materialize_vars_in_rval_avoid(rval::in, maybe(lval)::in,
|
|
list(lval)::in, rval::out, llds_code::out,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
materialize_vars_in_rval_avoid(Rval0, MaybePrefer, Avoid, Rval, Code, !VLI) :-
|
|
(
|
|
Rval0 = lval(Lval0),
|
|
materialize_vars_in_lval_avoid(Lval0, Avoid, Lval, Code, !VLI),
|
|
Rval = lval(Lval)
|
|
;
|
|
Rval0 = mkword(Tag, SubRval0),
|
|
materialize_vars_in_rval_avoid(SubRval0, no,
|
|
Avoid, SubRval, Code, !VLI),
|
|
Rval = mkword(Tag, SubRval)
|
|
;
|
|
Rval0 = mkword_hole(_Tag),
|
|
Rval = Rval0,
|
|
Code = empty
|
|
;
|
|
Rval0 = cast(Type, SubRval0),
|
|
materialize_vars_in_rval_avoid(SubRval0, no,
|
|
Avoid, SubRval, Code, !VLI),
|
|
Rval = cast(Type, SubRval)
|
|
;
|
|
Rval0 = unop(Unop, SubRval0),
|
|
materialize_vars_in_rval_avoid(SubRval0, no,
|
|
Avoid, SubRval, Code, !VLI),
|
|
Rval = unop(Unop, SubRval)
|
|
;
|
|
Rval0 = binop(Binop, SubRvalA0, SubRvalB0),
|
|
materialize_vars_in_rval_avoid(SubRvalA0, no,
|
|
Avoid, SubRvalA, CodeA, !VLI),
|
|
materialize_vars_in_rval_avoid(SubRvalB0, no,
|
|
Avoid, SubRvalB, CodeB, !VLI),
|
|
Rval = binop(Binop, SubRvalA, SubRvalB),
|
|
Code = CodeA ++ CodeB
|
|
;
|
|
Rval0 = const(_),
|
|
Rval = Rval0,
|
|
Code = empty
|
|
;
|
|
Rval0 = mem_addr(MemRef0),
|
|
materialize_vars_in_mem_ref_avoid(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_general(Var, MaybePrefer, store_var,
|
|
Avoid, Rval, Code, !VLI)
|
|
)
|
|
).
|
|
|
|
% MemRef is MemRef0 with all variables in MemRef replaced by their values.
|
|
%
|
|
:- pred materialize_vars_in_mem_ref_avoid(mem_ref::in, mem_ref::out,
|
|
list(lval)::in, llds_code::out,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
materialize_vars_in_mem_ref_avoid(MemRef0, MemRef, Avoid, Code, !VLI) :-
|
|
(
|
|
( MemRef0 = stackvar_ref(_)
|
|
; MemRef0 = framevar_ref(_)
|
|
),
|
|
MemRef = MemRef0,
|
|
Code = empty
|
|
;
|
|
MemRef0 = heap_ref(PtrRval0, MaybeTag, FieldNumRval0),
|
|
materialize_vars_in_rval_avoid(PtrRval0, no,
|
|
Avoid, PtrRval, PtrCode, !VLI),
|
|
materialize_vars_in_rval_avoid(FieldNumRval0, no,
|
|
Avoid, FieldNumRval, FieldNumCode, !VLI),
|
|
Code = PtrCode ++ FieldNumCode,
|
|
MemRef = heap_ref(PtrRval, MaybeTag, 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) :-
|
|
var_locn_get_var_state_map(VLI, VarStateMap),
|
|
map.lookup(VarStateMap, Var, State),
|
|
State = var_state(Lvals, MaybeConstRval, _, _, _),
|
|
set.to_sorted_list(Lvals, LvalsList),
|
|
( if
|
|
MaybePrefer = yes(Prefer),
|
|
list.member(Prefer, LvalsList)
|
|
then
|
|
Rval = lval(Prefer),
|
|
Avail = available(Rval)
|
|
else if
|
|
maybe_select_lval_or_rval(LvalsList, MaybeConstRval, Rval)
|
|
then
|
|
Avail = available(Rval)
|
|
else
|
|
Avail = needs_materialization
|
|
).
|
|
|
|
:- pred materialize_if_var(rval::in, llds_code::out, rval::out,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
materialize_if_var(Rval0, EvalCode, Rval, !VLI) :-
|
|
(
|
|
Rval0 = var(Var),
|
|
materialize_var(Var, EvalCode, Rval, !VLI)
|
|
;
|
|
( Rval0 = const(_)
|
|
; Rval0 = mkword(_, _)
|
|
; Rval0 = mkword_hole(_)
|
|
; Rval0 = cast(_, _)
|
|
; Rval0 = unop(_, _)
|
|
; Rval0 = binop(_, _, _)
|
|
; Rval0 = lval(_)
|
|
; Rval0 = mem_addr(_)
|
|
),
|
|
EvalCode = empty,
|
|
Rval = Rval0
|
|
).
|
|
|
|
:- pred materialize_var(prog_var::in, llds_code::out, rval::out,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
materialize_var(Var, EvalCode, Rval, !VLI) :-
|
|
find_var_availability(!.VLI, Var, no, Avail),
|
|
(
|
|
Avail = available(Rval),
|
|
EvalCode = empty
|
|
;
|
|
Avail = needs_materialization,
|
|
materialize_var_general(Var, no, do_not_store_var, [],
|
|
Rval, EvalCode, !VLI)
|
|
).
|
|
|
|
:- type store_var_if_required
|
|
---> do_not_store_var
|
|
; store_var.
|
|
|
|
:- pred materialize_var_general(prog_var::in, maybe(lval)::in,
|
|
store_var_if_required::in, list(lval)::in, rval::out, llds_code::out,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
materialize_var_general(Var, MaybePrefer, StoreIfReq, Avoid,
|
|
Rval, Code, !VLI) :-
|
|
var_locn_get_var_state_map(!.VLI, VarStateMap),
|
|
map.lookup(VarStateMap, Var, State),
|
|
State = var_state(_Lvals, _MaybeConstRval, MaybeExprRval,
|
|
UsingVars, _DeadOrAlive),
|
|
(
|
|
MaybeExprRval = yes(ExprRval)
|
|
;
|
|
MaybeExprRval = no,
|
|
unexpected($pred, "no expr")
|
|
),
|
|
materialize_vars_in_rval_avoid(ExprRval, MaybePrefer,
|
|
Avoid, Rval0, ExprCode, !VLI),
|
|
( if
|
|
StoreIfReq = store_var,
|
|
NumUsingVars = set_of_var.count(UsingVars),
|
|
NumUsingVars > 1
|
|
then
|
|
reg_type_for_var(!.VLI, Var, RegType),
|
|
select_preferred_reg_avoid(!.VLI, Var, RegType, Avoid, Lval),
|
|
var_locn_place_var(Var, Lval, PlaceCode, !VLI),
|
|
Rval = lval(Lval),
|
|
Code = ExprCode ++ PlaceCode
|
|
else
|
|
Rval = Rval0,
|
|
Code = ExprCode
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Freeing up locations for new uses.
|
|
%
|
|
|
|
var_locn_clear_r1(Code, !VLI) :-
|
|
free_up_lval(reg(reg_r, 1), [], [], Code, !VLI),
|
|
var_locn_get_loc_var_map(!.VLI, LocVarMap0),
|
|
var_locn_get_var_state_map(!.VLI, VarStateMap0),
|
|
clobber_regs_in_maps([reg(reg_r, 1)], no,
|
|
LocVarMap0, LocVarMap, VarStateMap0, VarStateMap),
|
|
var_locn_set_loc_var_map(LocVarMap, !VLI),
|
|
var_locn_set_var_state_map(VarStateMap, !VLI).
|
|
|
|
%---------------------%
|
|
|
|
var_locn_save_cell_fields(ReuseVar, ReuseLval, Code, Regs, !VLI) :-
|
|
var_locn_get_var_state_map(!.VLI, VarStateMap),
|
|
map.lookup(VarStateMap, ReuseVar, ReuseVarState0),
|
|
DepVarsSet = ReuseVarState0 ^ using_vars,
|
|
DepVars = set_of_var.to_sorted_list(DepVarsSet),
|
|
list.map_foldl2(var_locn_save_cell_fields_dep_var(ReuseLval),
|
|
DepVars, SaveArgsCode, [], Regs, !VLI),
|
|
Code = cord_list_to_cord(SaveArgsCode).
|
|
|
|
:- pred var_locn_save_cell_fields_dep_var(lval::in, prog_var::in,
|
|
llds_code::out, list(lval)::in, list(lval)::out,
|
|
var_locn_info::in, var_locn_info::out) is det.
|
|
|
|
var_locn_save_cell_fields_dep_var(ReuseLval, DepVar, SaveDepVarCode,
|
|
!Regs, !VLI) :-
|
|
find_var_availability(!.VLI, DepVar, no, Avail),
|
|
(
|
|
Avail = available(DepVarRval),
|
|
EvalCode = empty
|
|
;
|
|
Avail = needs_materialization,
|
|
materialize_var_general(DepVar, no, do_not_store_var, [],
|
|
DepVarRval, EvalCode, !VLI)
|
|
),
|
|
var_locn_get_var_table(!.VLI, VarTable),
|
|
lookup_var_entry(VarTable, DepVar, DepVarEntry),
|
|
DepVarEntry = vte(_N, DepVarType, DepVarIsDummy),
|
|
(
|
|
DepVarIsDummy = is_dummy_type,
|
|
AssignCode = empty
|
|
;
|
|
DepVarIsDummy = is_not_dummy_type,
|
|
( if
|
|
rval_depends_on_search_lval(DepVarRval,
|
|
specific_reg_or_stack(ReuseLval))
|
|
then
|
|
reg_type_for_type(!.VLI, DepVarType, RegType),
|
|
var_locn_acquire_reg(RegType, Target, !VLI),
|
|
add_additional_lval_for_var(DepVar, Target, !VLI),
|
|
DepVarName = var_entry_name(DepVar, DepVarEntry),
|
|
AssignCode = singleton(
|
|
llds_instr(assign(Target, DepVarRval),
|
|
"saving " ++ DepVarName)
|
|
),
|
|
!:Regs = [Target | !.Regs]
|
|
else
|
|
AssignCode = empty
|
|
)
|
|
),
|
|
SaveDepVarCode = EvalCode ++ AssignCode.
|
|
|
|
:- pred reg_type_for_type(var_locn_info::in, mer_type::in, reg_type::out)
|
|
is det.
|
|
|
|
reg_type_for_type(VLI, Type, RegType) :-
|
|
( if Type = float_type then
|
|
var_locn_get_float_reg_type(VLI, RegType)
|
|
else
|
|
RegType = reg_r
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Record the clobbering of registers (e.g. by calls).
|
|
%
|
|
|
|
var_locn_clobber_all_regs(OkToDeleteAny, !VLI) :-
|
|
var_locn_set_acquired(set.init, !VLI),
|
|
var_locn_set_locked(0, 0, !VLI),
|
|
var_locn_set_exceptions([], !VLI),
|
|
var_locn_get_loc_var_map(!.VLI, LocVarMap0),
|
|
var_locn_get_var_state_map(!.VLI, VarStateMap0),
|
|
map.keys(LocVarMap0, Locs),
|
|
clobber_regs_in_maps(Locs, OkToDeleteAny,
|
|
LocVarMap0, LocVarMap, VarStateMap0, VarStateMap),
|
|
var_locn_set_loc_var_map(LocVarMap, !VLI),
|
|
var_locn_set_var_state_map(VarStateMap, !VLI).
|
|
|
|
var_locn_clobber_reg(Reg, !VLI) :-
|
|
var_locn_get_acquired(!.VLI, Acquired0),
|
|
Acquired = set.delete(Acquired0, Reg),
|
|
var_locn_set_acquired(Acquired, !VLI),
|
|
var_locn_get_loc_var_map(!.VLI, LocVarMap0),
|
|
var_locn_get_var_state_map(!.VLI, VarStateMap0),
|
|
clobber_reg_in_maps(Reg, no,
|
|
LocVarMap0, LocVarMap, VarStateMap0, VarStateMap),
|
|
var_locn_set_loc_var_map(LocVarMap, !VLI),
|
|
var_locn_set_var_state_map(VarStateMap, !VLI).
|
|
|
|
var_locn_clobber_regs(Regs, !VLI) :-
|
|
var_locn_get_acquired(!.VLI, Acquired0),
|
|
Acquired = set.delete_list(Acquired0, Regs),
|
|
var_locn_set_acquired(Acquired, !VLI),
|
|
var_locn_get_loc_var_map(!.VLI, LocVarMap0),
|
|
var_locn_get_var_state_map(!.VLI, VarStateMap0),
|
|
clobber_regs_in_maps(Regs, no,
|
|
LocVarMap0, LocVarMap, VarStateMap0, VarStateMap),
|
|
var_locn_set_loc_var_map(LocVarMap, !VLI),
|
|
var_locn_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) :-
|
|
clobber_reg_in_maps(Lval, OkToDeleteAny, !LocVarMap, !VarStateMap),
|
|
clobber_regs_in_maps(Lvals, OkToDeleteAny, !LocVarMap, !VarStateMap).
|
|
|
|
:- pred clobber_reg_in_maps(lval::in, bool::in,
|
|
loc_var_map::in, loc_var_map::out,
|
|
var_state_map::in, var_state_map::out) is det.
|
|
|
|
clobber_reg_in_maps(Lval, OkToDeleteAny, !LocVarMap, !VarStateMap) :-
|
|
( if
|
|
Lval = reg(_, _),
|
|
map.search(!.LocVarMap, Lval, DependentVarsSet)
|
|
then
|
|
map.delete(Lval, !LocVarMap),
|
|
set_of_var.fold(
|
|
clobber_lval_in_var_state_map(Lval, [], OkToDeleteAny),
|
|
DependentVarsSet, !VarStateMap)
|
|
else
|
|
true
|
|
).
|
|
|
|
:- 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) :-
|
|
( if
|
|
try_clobber_lval_in_var_state_map(Lval, OkToDeleteVars, OkToDeleteAny,
|
|
Var, !VarStateMap)
|
|
then
|
|
true
|
|
else
|
|
unexpected($pred, "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 = var_state(LvalSet0, MaybeConstRval, MaybeExprRval, Using,
|
|
DeadOrAlive),
|
|
LvalSet = set.filter(lval_does_not_support_lval(Lval), LvalSet0),
|
|
State = var_state(LvalSet, MaybeConstRval, MaybeExprRval, Using,
|
|
DeadOrAlive),
|
|
(
|
|
nonempty_state(State)
|
|
;
|
|
list.member(Var, OkToDeleteVars)
|
|
;
|
|
OkToDeleteAny = yes
|
|
;
|
|
DeadOrAlive = doa_dead,
|
|
set_of_var.to_sorted_list(Using, UsingVars),
|
|
recursive_using_vars_dead_and_ok_to_delete(UsingVars,
|
|
!.VarStateMap, OkToDeleteVars)
|
|
),
|
|
map.det_update(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 = var_state(_, _, _, Using, DeadOrAlive),
|
|
DeadOrAlive = doa_dead,
|
|
set_of_var.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).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Record variables becoming dead.
|
|
%
|
|
|
|
var_locn_var_becomes_dead(FirstDeath, Var, !VLI) :-
|
|
% 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 FirstDeath = maybe_not_first_death, then it is possible that this
|
|
% predicate has already been called for Var. If FirstDeath = first_death,
|
|
% then as a consistency check we insist on Var being alive.
|
|
|
|
var_locn_get_var_state_map(!.VLI, VarStateMap0),
|
|
( if map.search(VarStateMap0, Var, State0) then
|
|
State0 = var_state(Lvals, MaybeConstRval, MaybeExprRval, Using,
|
|
DeadOrAlive0),
|
|
(
|
|
DeadOrAlive0 = doa_dead,
|
|
expect(unify(FirstDeath, maybe_not_first_death), $pred,
|
|
"already dead")
|
|
;
|
|
DeadOrAlive0 = doa_alive
|
|
),
|
|
( if set_of_var.is_empty(Using) then
|
|
map.det_remove(Var, _, VarStateMap0, VarStateMap),
|
|
var_locn_set_var_state_map(VarStateMap, !VLI),
|
|
|
|
var_locn_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),
|
|
var_locn_set_loc_var_map(LocVarMap, !VLI),
|
|
|
|
remove_use_refs(MaybeExprRval, Var, !VLI)
|
|
else
|
|
State = var_state(Lvals, MaybeConstRval, MaybeExprRval, Using,
|
|
doa_dead),
|
|
map.det_update(Var, State, VarStateMap0, VarStateMap),
|
|
var_locn_set_var_state_map(VarStateMap, !VLI)
|
|
)
|
|
else
|
|
var_locn_get_var_table(!.VLI, VarTable),
|
|
lookup_var_entry(VarTable, Var, VarEntry),
|
|
VarEntry = vte(_N, _T, VarIsDummy),
|
|
( if
|
|
( VarIsDummy = is_dummy_type
|
|
; FirstDeath = maybe_not_first_death
|
|
)
|
|
then
|
|
true
|
|
else
|
|
unexpected($pred, "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),
|
|
AllLvals = LvalList ++ lvals_in_lvals(LvalList),
|
|
list.filter(is_root_lval, AllLvals, RootLvals),
|
|
list.sort_and_remove_dups(RootLvals, NoDupRootLvals).
|
|
|
|
%---------------------%
|
|
|
|
:- pred maybe_select_lval_or_rval(list(lval)::in, maybe(rval)::in,
|
|
rval::out) is semidet.
|
|
|
|
maybe_select_lval_or_rval(Lvals, MaybeConstRval, Rval) :-
|
|
( if select_reg_lval(Lvals, Lval1) then
|
|
Rval = lval(Lval1)
|
|
else if select_stack_lval(Lvals, Lval2) then
|
|
Rval = lval(Lval2)
|
|
else if MaybeConstRval = yes(ConstRval) then
|
|
Rval = ConstRval
|
|
else if select_cheapest_lval(Lvals, Lval3) then
|
|
Rval = lval(Lval3)
|
|
else
|
|
fail
|
|
).
|
|
|
|
:- pred select_reg_lval(list(lval)::in, lval::out) is semidet.
|
|
|
|
select_reg_lval([Lval0 | Lvals0], Lval) :-
|
|
( if Lval0 = reg(_, _) then
|
|
Lval = Lval0
|
|
else
|
|
select_reg_lval(Lvals0, Lval)
|
|
).
|
|
|
|
:- pred select_stack_lval(list(lval)::in, lval::out) is semidet.
|
|
|
|
select_stack_lval([Lval0 | Lvals0], Lval) :-
|
|
( if
|
|
( Lval0 = stackvar(_)
|
|
; Lval0 = framevar(_)
|
|
; Lval0 = double_stackvar(_, _)
|
|
)
|
|
then
|
|
Lval = Lval0
|
|
else
|
|
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) :-
|
|
( if
|
|
( Lval0 = reg(_, _)
|
|
; Lval0 = stackvar(_)
|
|
; Lval0 = framevar(_)
|
|
; Lval0 = double_stackvar(_, _)
|
|
)
|
|
then
|
|
Lval = Lval0
|
|
else
|
|
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(var_locn_info::in, prog_var::in, reg_type::in,
|
|
lval::out) is det.
|
|
|
|
select_preferred_reg(VLI, Var, RegType, Lval) :-
|
|
select_preferred_reg_avoid(VLI, Var, RegType, [], 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.
|
|
%
|
|
:- pred select_preferred_reg_avoid(var_locn_info::in, prog_var::in,
|
|
reg_type::in, list(lval)::in, lval::out) is det.
|
|
|
|
select_preferred_reg_avoid(VLI, Var, RegType, Avoid, Lval) :-
|
|
var_locn_get_follow_var_map(VLI, FollowVarMap),
|
|
( if
|
|
map.search(FollowVarMap, Var, PrefLocn),
|
|
( PrefLocn = abs_reg(_, _)
|
|
; PrefLocn = any_reg
|
|
)
|
|
then
|
|
( if
|
|
PrefLocn = abs_reg(PrefRegType, N),
|
|
PrefLval = reg(PrefRegType, N),
|
|
not var_locn_lval_in_use(VLI, PrefLval),
|
|
not list.member(PrefLval, Avoid)
|
|
then
|
|
Lval = PrefLval
|
|
else
|
|
get_spare_reg_avoid(VLI, RegType, Avoid, Lval)
|
|
)
|
|
else
|
|
get_spare_reg_avoid(VLI, RegType, 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. 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. If all else fails, we get
|
|
% a 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(var_locn_info::in, prog_var::in,
|
|
reg_type::in, lval::out) is det.
|
|
|
|
select_preferred_reg_or_stack(VLI, Var, RegType, Lval) :-
|
|
var_locn_get_follow_var_map(VLI, FollowVarMap),
|
|
( if
|
|
map.search(FollowVarMap, Var, PrefLocn),
|
|
( PrefLocn = abs_reg(_, _)
|
|
; PrefLocn = any_reg
|
|
)
|
|
then
|
|
( if
|
|
PrefLocn = abs_reg(RegType, N),
|
|
PrefLval = reg(RegType, N),
|
|
not var_locn_lval_in_use(VLI, PrefLval)
|
|
then
|
|
Lval = PrefLval
|
|
else
|
|
get_spare_reg(VLI, RegType, Lval)
|
|
)
|
|
else
|
|
( if
|
|
var_locn_get_stack_slots(VLI, StackSlots),
|
|
map.search(StackSlots, Var, StackSlotLocn),
|
|
StackSlot = stack_slot_to_lval(StackSlotLocn),
|
|
not var_locn_lval_in_use(VLI, StackSlot)
|
|
then
|
|
Lval = StackSlot
|
|
else
|
|
get_spare_reg(VLI, RegType, Lval)
|
|
)
|
|
).
|
|
|
|
%---------------------%
|
|
|
|
% 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, reg_type::in, list(lval)::in,
|
|
lval::out) is det.
|
|
|
|
get_spare_reg_avoid(VLI, RegType, Avoid, Lval) :-
|
|
var_locn_get_next_non_reserved(VLI, RegType, NextNonReserved),
|
|
get_spare_reg_loop(VLI, RegType, Avoid, NextNonReserved, Lval).
|
|
|
|
:- pred get_spare_reg(var_locn_info::in, reg_type::in, lval::out) is det.
|
|
|
|
get_spare_reg(VLI, RegType, Lval) :-
|
|
var_locn_get_next_non_reserved(VLI, RegType, NextNonReserved),
|
|
get_spare_reg_loop(VLI, RegType, [], NextNonReserved, Lval).
|
|
|
|
:- pred get_spare_reg_loop(var_locn_info::in, reg_type::in, list(lval)::in,
|
|
int::in, lval::out) is det.
|
|
|
|
get_spare_reg_loop(VLI, RegType, Avoid, N0, Lval) :-
|
|
TryLval = reg(RegType, N0),
|
|
( if
|
|
( var_locn_lval_in_use(VLI, TryLval)
|
|
; list.member(TryLval, Avoid)
|
|
)
|
|
then
|
|
get_spare_reg_loop(VLI, RegType, Avoid, N0 + 1, Lval)
|
|
else
|
|
Lval = TryLval
|
|
).
|
|
|
|
% 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, reg_type::in, int::in,
|
|
prog_var::in) is semidet.
|
|
|
|
reg_is_not_locked_for_var(VLI, RegType, RegNum, Var) :-
|
|
var_locn_get_acquired(VLI, Acquired),
|
|
var_locn_get_exceptions(VLI, Exceptions),
|
|
(
|
|
RegType = reg_r,
|
|
var_locn_get_locked(VLI, Locked, _LockedF)
|
|
;
|
|
RegType = reg_f,
|
|
var_locn_get_locked(VLI, _LockedR, Locked)
|
|
),
|
|
Reg = reg(RegType, RegNum),
|
|
not set.member(Reg, Acquired),
|
|
RegNum =< Locked => list.member(Var - Reg, Exceptions).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
var_locn_acquire_reg(Type, Lval, !VLI) :-
|
|
get_spare_reg(!.VLI, Type, Lval),
|
|
var_locn_get_acquired(!.VLI, Acquired0),
|
|
set.insert(Lval, Acquired0, Acquired),
|
|
var_locn_set_acquired(Acquired, !VLI).
|
|
|
|
var_locn_acquire_reg_require_given(Lval, !VLI) :-
|
|
( if var_locn_lval_in_use(!.VLI, Lval) then
|
|
unexpected($pred, "lval in use")
|
|
else
|
|
true
|
|
),
|
|
var_locn_get_acquired(!.VLI, Acquired0),
|
|
set.insert(Lval, Acquired0, Acquired),
|
|
var_locn_set_acquired(Acquired, !VLI).
|
|
|
|
var_locn_acquire_reg_prefer_given(Type, Pref, Lval, !VLI) :-
|
|
PrefLval = reg(Type, Pref),
|
|
( if var_locn_lval_in_use(!.VLI, PrefLval) then
|
|
get_spare_reg(!.VLI, Type, Lval)
|
|
else
|
|
Lval = PrefLval
|
|
),
|
|
var_locn_get_acquired(!.VLI, Acquired0),
|
|
set.insert(Lval, Acquired0, Acquired),
|
|
var_locn_set_acquired(Acquired, !VLI).
|
|
|
|
var_locn_acquire_reg_start_at_given(Type, Start, Lval, !VLI) :-
|
|
StartLval = reg(Type, Start),
|
|
( if var_locn_lval_in_use(!.VLI, StartLval) then
|
|
var_locn_acquire_reg_start_at_given(Type, Start + 1, Lval, !VLI)
|
|
else
|
|
Lval = StartLval,
|
|
var_locn_get_acquired(!.VLI, Acquired0),
|
|
set.insert(Lval, Acquired0, Acquired),
|
|
var_locn_set_acquired(Acquired, !VLI)
|
|
).
|
|
|
|
var_locn_release_reg(Lval, !VLI) :-
|
|
var_locn_get_acquired(!.VLI, Acquired0),
|
|
( if set.member(Lval, Acquired0) then
|
|
set.delete(Lval, Acquired0, Acquired),
|
|
var_locn_set_acquired(Acquired, !VLI)
|
|
else
|
|
unexpected($pred, "unacquired reg")
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
var_locn_lock_regs(R, F, Exceptions, !VLI) :-
|
|
var_locn_set_locked(R, F, !VLI),
|
|
var_locn_set_exceptions(Exceptions, !VLI).
|
|
|
|
var_locn_unlock_regs(!VLI) :-
|
|
var_locn_set_locked(0, 0, !VLI),
|
|
var_locn_set_exceptions([], !VLI).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% 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) :-
|
|
Lvals = set.make_singleton_set(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), $pred, "non-root lval"),
|
|
( if map.search(!.LocVarMap, Lval, Vars0) then
|
|
set_of_var.insert(Var, Vars0, Vars),
|
|
map.det_update(Lval, Vars, !LocVarMap)
|
|
else
|
|
Vars = set_of_var.make_singleton(Var),
|
|
map.det_insert(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), $pred, "non-root lval"),
|
|
( if map.search(!.LocVarMap, Lval, Vars0) then
|
|
set_of_var.delete(Var, Vars0, Vars),
|
|
( if set_of_var.is_empty(Vars) then
|
|
map.det_remove(Lval, _, !LocVarMap)
|
|
else
|
|
map.det_update(Lval, Vars, !LocVarMap)
|
|
)
|
|
else
|
|
unexpected($pred, "no record")
|
|
).
|
|
|
|
:- pred is_root_lval(lval::in) is semidet.
|
|
|
|
is_root_lval(reg(_, _)).
|
|
is_root_lval(stackvar(_)).
|
|
is_root_lval(parent_stackvar(_)).
|
|
is_root_lval(framevar(_)).
|
|
is_root_lval(double_stackvar(_, _)).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- 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) :-
|
|
not 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(Rval, SearchLval) :-
|
|
require_complete_switch [Rval]
|
|
(
|
|
Rval = lval(Lval),
|
|
lval_depends_on_search_lval(Lval, SearchLval)
|
|
;
|
|
( Rval = mkword(_Tag, SubRval)
|
|
; Rval = cast(_Type, SubRval)
|
|
; Rval = unop(_UnOp, SubRval)
|
|
),
|
|
rval_depends_on_search_lval(SubRval, SearchLval)
|
|
;
|
|
Rval = binop(_BinOp, SubRvalA, SubRvalB),
|
|
(
|
|
rval_depends_on_search_lval(SubRvalA, SearchLval)
|
|
;
|
|
rval_depends_on_search_lval(SubRvalB, SearchLval)
|
|
)
|
|
;
|
|
Rval = mem_addr(MemRef),
|
|
require_complete_switch [MemRef]
|
|
(
|
|
( MemRef = stackvar_ref(SubRval)
|
|
; MemRef = framevar_ref(SubRval)
|
|
),
|
|
rval_depends_on_search_lval(SubRval, SearchLval)
|
|
;
|
|
MemRef = heap_ref(CellRval, _MaybeTag, FieldNumRval),
|
|
rval_depends_on_search_lval(CellRval, SearchLval),
|
|
rval_depends_on_search_lval(FieldNumRval, SearchLval)
|
|
)
|
|
;
|
|
( Rval = const(_Const)
|
|
; Rval = mkword_hole(_Tag)
|
|
),
|
|
fail
|
|
;
|
|
Rval = var(_Var),
|
|
unexpected($pred, "var")
|
|
).
|
|
|
|
:- pred lval_depends_on_search_lval(lval::in, dep_search_lval::in) is semidet.
|
|
|
|
lval_depends_on_search_lval(Lval, SearchLval) :-
|
|
require_complete_switch [Lval]
|
|
(
|
|
Lval = reg(_Type, _Num),
|
|
(
|
|
SearchLval = all_regs
|
|
;
|
|
SearchLval = specific_reg_or_stack(Lval)
|
|
)
|
|
;
|
|
Lval = stackvar(_Num),
|
|
SearchLval = specific_reg_or_stack(Lval)
|
|
;
|
|
Lval = framevar(_Num),
|
|
SearchLval = specific_reg_or_stack(Lval)
|
|
;
|
|
Lval = field(_Tag, Rval0, Rval1),
|
|
(
|
|
rval_depends_on_search_lval(Rval0, SearchLval)
|
|
;
|
|
rval_depends_on_search_lval(Rval1, SearchLval)
|
|
)
|
|
;
|
|
Lval = double_stackvar(_, _),
|
|
SearchLval = specific_reg_or_stack(Lval)
|
|
;
|
|
( Lval = succip
|
|
; Lval = maxfr
|
|
; Lval = curfr
|
|
; Lval = hp
|
|
; Lval = sp
|
|
; Lval = parent_sp
|
|
; Lval = temp(_, _)
|
|
; Lval = parent_stackvar(_)
|
|
; Lval = succip_slot(_)
|
|
; Lval = succfr_slot(_)
|
|
; Lval = redoip_slot(_)
|
|
; Lval = redofr_slot(_)
|
|
; Lval = redofr_slot(_)
|
|
; Lval = prevfr_slot(_)
|
|
; Lval = mem_ref(_)
|
|
; Lval = global_var_ref(_)
|
|
),
|
|
fail
|
|
;
|
|
Lval = lvar(_Var),
|
|
unexpected($pred, "lvar")
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred get_var_name(var_locn_info::in, prog_var::in, string::out) is det.
|
|
|
|
get_var_name(VLI, Var, Name) :-
|
|
var_locn_get_var_table(VLI, VarTable),
|
|
Name = var_table_entry_name(VarTable, Var).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred nonempty_state(var_state::in) is semidet.
|
|
|
|
nonempty_state(State) :-
|
|
State = var_state(LvalSet, MaybeConstRval, MaybeExprRval, _, _),
|
|
( set.is_non_empty(LvalSet)
|
|
; MaybeConstRval = yes(_)
|
|
; MaybeExprRval = yes(_)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module ll_backend.var_locn.
|
|
%---------------------------------------------------------------------------%
|