mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-12 20:34:19 +00:00
compiler/liveness.m:
Instead of looking up the pred_info for the procedure being processed,
get our caller to give it to us.
Put the arguments of some predicates in a more logical order.
compiler/stack_alloc.m:
compiler/stack_opt.m:
compiler/store_alloc.m:
compiler/tupling.m:
Pass the pred_info instead of a pred_id to initial_liveness. In all
these callers, the pred_info was already available.
401 lines
16 KiB
Mathematica
401 lines
16 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 2002-2007, 2010-2012 The University of Melbourne.
|
|
% This file may only be copied under the terms of the GNU General
|
|
% Public License - see the file COPYING in the Mercury distribution.
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% File stack_alloc.m.
|
|
% Authors: zs, conway.
|
|
%
|
|
% This module allocates stack slots to the variables that need to be saved
|
|
% across a call, across a goal that may fail, or in a parallel conjunction.
|
|
%
|
|
% The jobs is done in two steps. First we traverse the predicate definition
|
|
% looking for sets of variables that must be saved on the stack at the same
|
|
% time. If --optimize-stack-slots is set, then this phase is done by
|
|
% stack_opt.m; if --optimize-stack-slots is not set, then it is done by this
|
|
% module. Then we use a graph colouring algorithm to find an allocation of
|
|
% stack slots (colours) to variables such that in each set of variables that
|
|
% must be saved at the same time, each variable has a different colour.
|
|
%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- module ll_backend.stack_alloc.
|
|
:- interface.
|
|
|
|
:- import_module hlds.
|
|
:- import_module hlds.hlds_module.
|
|
:- import_module hlds.hlds_pred.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred allocate_stack_slots_in_proc(module_info::in, pred_proc_id::in,
|
|
proc_info::in, proc_info::out) is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module check_hlds.
|
|
:- import_module check_hlds.type_util.
|
|
:- import_module hlds.code_model.
|
|
:- import_module hlds.hlds_llds.
|
|
:- import_module hlds.vartypes.
|
|
:- import_module libs.
|
|
:- import_module libs.globals.
|
|
:- import_module libs.options.
|
|
:- import_module libs.trace_params.
|
|
:- import_module ll_backend.live_vars.
|
|
:- import_module ll_backend.liveness.
|
|
:- import_module ll_backend.llds.
|
|
:- import_module ll_backend.trace_gen.
|
|
:- import_module parse_tree.
|
|
:- import_module parse_tree.builtin_lib_types.
|
|
:- import_module parse_tree.prog_data.
|
|
:- import_module parse_tree.set_of_var.
|
|
|
|
:- import_module array.
|
|
:- import_module bool.
|
|
:- import_module enum.
|
|
:- import_module int.
|
|
:- import_module list.
|
|
:- import_module map.
|
|
:- import_module maybe.
|
|
:- import_module pair.
|
|
:- import_module require.
|
|
:- import_module set.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
allocate_stack_slots_in_proc(ModuleInfo, proc(PredId, ProcId), !ProcInfo) :-
|
|
module_info_pred_info(ModuleInfo, PredId, PredInfo),
|
|
initial_liveness(ModuleInfo, PredInfo, !.ProcInfo, Liveness0),
|
|
module_info_get_globals(ModuleInfo, Globals),
|
|
globals.get_trace_level(Globals, TraceLevel),
|
|
NeedFailVars = eff_trace_level_needs_fail_vars(ModuleInfo, PredInfo,
|
|
!.ProcInfo, TraceLevel),
|
|
(
|
|
NeedFailVars = yes,
|
|
trace_fail_vars(ModuleInfo, !.ProcInfo, FailVars)
|
|
;
|
|
NeedFailVars = no,
|
|
FailVars = set_of_var.init
|
|
),
|
|
body_should_use_typeinfo_liveness(PredInfo, Globals, TypeInfoLiveness),
|
|
globals.lookup_bool_option(Globals, opt_no_return_calls,
|
|
OptNoReturnCalls),
|
|
proc_info_get_vartypes(!.ProcInfo, VarTypes),
|
|
build_dummy_type_array(ModuleInfo, VarTypes, DummyTypeArray, DummyVars),
|
|
AllocData = alloc_data(ModuleInfo, !.ProcInfo, proc(PredId, ProcId),
|
|
TypeInfoLiveness, OptNoReturnCalls, DummyTypeArray),
|
|
NondetLiveness0 = set_of_var.init,
|
|
SimpleStackAlloc0 = stack_alloc(set.make_singleton_set(FailVars)),
|
|
proc_info_get_goal(!.ProcInfo, Goal0),
|
|
build_live_sets_in_goal_no_par_stack(Goal0, Goal, FailVars, AllocData,
|
|
SimpleStackAlloc0, SimpleStackAlloc, Liveness0, _Liveness,
|
|
NondetLiveness0, _NondetLiveness),
|
|
proc_info_set_goal(Goal, !ProcInfo),
|
|
SimpleStackAlloc = stack_alloc(LiveSets0),
|
|
|
|
do_we_need_maxfr_slot(Globals, ModuleInfo, PredInfo, !ProcInfo),
|
|
trace_reserved_slots(ModuleInfo, PredInfo, !.ProcInfo, Globals,
|
|
NumReservedSlots, MaybeReservedVarInfo),
|
|
(
|
|
MaybeReservedVarInfo = yes(ResVar - _),
|
|
ResVarSet = set_of_var.make_singleton(ResVar),
|
|
set.insert(ResVarSet, LiveSets0, LiveSets)
|
|
;
|
|
MaybeReservedVarInfo = no,
|
|
LiveSets = LiveSets0
|
|
),
|
|
graph_colour_group_elements(LiveSets, ColourSets),
|
|
set.to_sorted_list(ColourSets, ColourList),
|
|
|
|
CodeModel = proc_info_interface_code_model(!.ProcInfo),
|
|
MainStack = code_model_to_main_stack(CodeModel),
|
|
allocate_stack_slots(Globals, VarTypes, MainStack, NumReservedSlots,
|
|
MaybeReservedVarInfo, ColourList, StackSlots1),
|
|
allocate_dummy_stack_slots(MainStack, DummyVars, -1,
|
|
StackSlots1, StackSlots),
|
|
proc_info_set_stack_slots(StackSlots, !ProcInfo).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- type stack_alloc
|
|
---> stack_alloc(
|
|
% Each element of this set is a set of variables
|
|
% that need to be on the stack at the same time.
|
|
set(set_of_progvar)
|
|
).
|
|
|
|
:- instance stack_alloc_info(stack_alloc) where [
|
|
pred(at_call_site/4) is alloc_at_call_site,
|
|
pred(at_resume_site/4) is alloc_at_resume_site,
|
|
pred(at_par_conj/4) is alloc_at_par_conj,
|
|
pred(at_recursive_call_for_loop_control/4) is
|
|
alloc_at_recursive_call_for_loop_control
|
|
].
|
|
|
|
:- pred alloc_at_call_site(need_across_call::in, alloc_data::in,
|
|
stack_alloc::in, stack_alloc::out) is det.
|
|
|
|
alloc_at_call_site(NeedAtCall, AllocData, !StackAlloc) :-
|
|
NeedAtCall = need_across_call(ForwardVars, ResumeVars, NondetLiveVars),
|
|
LiveSet0 = set_of_var.union_list([ForwardVars, ResumeVars,
|
|
NondetLiveVars]),
|
|
filter_out_dummy_vars(AllocData, LiveSet0, LiveSet),
|
|
|
|
!.StackAlloc = stack_alloc(LiveSets0),
|
|
LiveSets = set.insert(LiveSets0, LiveSet),
|
|
!:StackAlloc = stack_alloc(LiveSets).
|
|
|
|
:- pred alloc_at_resume_site(need_in_resume::in, alloc_data::in,
|
|
stack_alloc::in, stack_alloc::out) is det.
|
|
|
|
alloc_at_resume_site(NeedAtResume, AllocData, !StackAlloc) :-
|
|
NeedAtResume = need_in_resume(ResumeOnStack, ResumeVars, NondetLiveVars),
|
|
(
|
|
ResumeOnStack = no
|
|
;
|
|
ResumeOnStack = yes,
|
|
LiveSet0 = set_of_var.union(ResumeVars, NondetLiveVars),
|
|
filter_out_dummy_vars(AllocData, LiveSet0, LiveSet),
|
|
|
|
!.StackAlloc = stack_alloc(LiveSets0),
|
|
LiveSets = set.insert(LiveSets0, LiveSet),
|
|
!:StackAlloc = stack_alloc(LiveSets)
|
|
).
|
|
|
|
:- pred alloc_at_par_conj(need_in_par_conj::in, alloc_data::in,
|
|
stack_alloc::in, stack_alloc::out) is det.
|
|
|
|
alloc_at_par_conj(NeedParConj, AllocData, !StackAlloc) :-
|
|
NeedParConj = need_in_par_conj(StackVars0),
|
|
filter_out_dummy_vars(AllocData, StackVars0, StackVars),
|
|
|
|
!.StackAlloc = stack_alloc(LiveSets0),
|
|
LiveSets = set.insert(LiveSets0, StackVars),
|
|
!:StackAlloc = stack_alloc(LiveSets).
|
|
|
|
:- pred alloc_at_recursive_call_for_loop_control(need_for_loop_control::in,
|
|
alloc_data::in, stack_alloc::in, stack_alloc::out) is det.
|
|
|
|
alloc_at_recursive_call_for_loop_control(NeedLC, AllocData, !StackAlloc) :-
|
|
NeedLC = need_for_loop_control(StackVarsSets),
|
|
list.foldl(set_for_loop_control(AllocData), StackVarsSets, !StackAlloc).
|
|
|
|
:- pred set_for_loop_control(alloc_data::in, set_of_progvar::in,
|
|
stack_alloc::in, stack_alloc::out) is det.
|
|
|
|
set_for_loop_control(AllocData, Set0, !StackAlloc) :-
|
|
!.StackAlloc = stack_alloc(LiveSets0),
|
|
filter_out_dummy_vars(AllocData, Set0, Set),
|
|
LiveSets = set.insert(LiveSets0, Set),
|
|
!:StackAlloc = stack_alloc(LiveSets).
|
|
|
|
:- pred filter_out_dummy_vars(alloc_data::in,
|
|
set_of_progvar::in, set_of_progvar::out) is det.
|
|
|
|
filter_out_dummy_vars(AllocData, Vars, NonDummyVars) :-
|
|
DummyVarArray = AllocData ^ ad_dummy_var_array,
|
|
set_of_var.filter(var_is_not_dummy(DummyVarArray), Vars, NonDummyVars).
|
|
|
|
:- pred var_is_not_dummy(array(is_dummy_type)::in, prog_var::in) is semidet.
|
|
|
|
var_is_not_dummy(DummyVarArray, Var) :-
|
|
VarNum = to_int(Var),
|
|
array.lookup(DummyVarArray, VarNum, IsDummy),
|
|
IsDummy = is_not_dummy_type.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred allocate_stack_slots(globals::in, vartypes::in,
|
|
main_stack::in, int::in, maybe(pair(prog_var, int))::in,
|
|
list(set_of_progvar)::in, stack_slots::out) is det.
|
|
|
|
allocate_stack_slots(Globals, VarTypes, MainStack, NumReservedSlots,
|
|
MaybeReservedVarInfo, Colours, StackSlots) :-
|
|
( if float_width_on_stack(Globals, MainStack) = double_width then
|
|
MaybeDoubleWidthFloats = yes(VarTypes)
|
|
else
|
|
MaybeDoubleWidthFloats = no
|
|
),
|
|
% The reserved slots are referred to by fixed number
|
|
% (e.g. framevar(1)) in trace.setup.
|
|
FirstFreeSlot = NumReservedSlots + 1,
|
|
allocate_stack_slots_to_colours(MainStack, MaybeDoubleWidthFloats,
|
|
MaybeReservedVarInfo, Colours, FirstFreeSlot, map.init, StackSlots).
|
|
|
|
:- pred allocate_stack_slots_to_colours(main_stack::in, maybe(vartypes)::in,
|
|
maybe(pair(prog_var, int))::in, list(set_of_progvar)::in,
|
|
int::in, stack_slots::in, stack_slots::out) is det.
|
|
|
|
allocate_stack_slots_to_colours(_, _, _, [], _, !StackSlots).
|
|
allocate_stack_slots_to_colours(MainStack, MaybeDoubleWidthFloats,
|
|
MaybeReservedVarInfo, [FirstColour | LaterColours],
|
|
!.FirstFreeSlot, !StackSlots) :-
|
|
allocate_stack_slots_to_colour(MainStack, MaybeDoubleWidthFloats,
|
|
MaybeReservedVarInfo, FirstColour, !FirstFreeSlot, !StackSlots),
|
|
allocate_stack_slots_to_colours(MainStack, MaybeDoubleWidthFloats,
|
|
MaybeReservedVarInfo, LaterColours, !.FirstFreeSlot, !StackSlots).
|
|
|
|
:- pred allocate_stack_slots_to_colour(main_stack::in, maybe(vartypes)::in,
|
|
maybe(pair(prog_var, int))::in, set_of_progvar::in,
|
|
int::in, int::out, stack_slots::in, stack_slots::out) is det.
|
|
|
|
allocate_stack_slots_to_colour(MainStack, MaybeDoubleWidthFloats,
|
|
MaybeReservedVarInfo, ColourVars, !FirstFreeSlot, !StackSlots) :-
|
|
(
|
|
MaybeDoubleWidthFloats = no,
|
|
% Treat all variables in ColourVars as single_width.
|
|
allocate_next_stack_slot(MainStack, single_width,
|
|
MaybeReservedVarInfo, ColourVars, !FirstFreeSlot, !StackSlots)
|
|
;
|
|
MaybeDoubleWidthFloats = yes(VarTypes),
|
|
% XXX We do NOT currently allow single-width vars to overlap with
|
|
% double-width vars, because the code generator does not understand
|
|
% that clobbering one slot in a pair destroys any double_width values
|
|
% in that slot pair, even if its officially-recorded slot number
|
|
% is the number of the *other* slot.
|
|
set_of_var.divide(var_is_float(VarTypes), ColourVars,
|
|
DoubleWidthVars, SingleWidthVars),
|
|
( if set_of_var.is_non_empty(SingleWidthVars) then
|
|
allocate_next_stack_slot(MainStack, single_width,
|
|
MaybeReservedVarInfo, SingleWidthVars,
|
|
!FirstFreeSlot, !StackSlots)
|
|
else
|
|
true
|
|
),
|
|
( if set_of_var.is_non_empty(DoubleWidthVars) then
|
|
align_double_width_slots(!FirstFreeSlot),
|
|
allocate_next_stack_slot(MainStack, double_width,
|
|
MaybeReservedVarInfo, DoubleWidthVars,
|
|
!FirstFreeSlot, !StackSlots)
|
|
else
|
|
true
|
|
)
|
|
).
|
|
|
|
:- pred allocate_next_stack_slot(main_stack::in, stack_slot_width::in,
|
|
maybe(pair(prog_var, int))::in, set_of_progvar::in, int::in, int::out,
|
|
stack_slots::in, stack_slots::out) is det.
|
|
|
|
allocate_next_stack_slot(MainStack, StackSlotWidth, MaybeReservedVarInfo, Vars,
|
|
!FirstFreeSlot, !StackSlots) :-
|
|
( if
|
|
MaybeReservedVarInfo = yes(ResVar - ResSlotNum),
|
|
set_of_var.member(Vars, ResVar)
|
|
then
|
|
expect(unify(StackSlotWidth, single_width), $module, $pred,
|
|
"reserved multiple stack slots"),
|
|
SlotNum = ResSlotNum
|
|
else
|
|
SlotNum = !.FirstFreeSlot,
|
|
next_slot(StackSlotWidth, !FirstFreeSlot)
|
|
),
|
|
(
|
|
MainStack = det_stack,
|
|
Locn = det_slot(SlotNum, StackSlotWidth)
|
|
;
|
|
MainStack = nondet_stack,
|
|
Locn = nondet_slot(SlotNum)
|
|
),
|
|
VarList = set_of_var.to_sorted_list(Vars),
|
|
allocate_given_stack_slot(Locn, VarList, !StackSlots).
|
|
|
|
:- pred allocate_given_stack_slot(stack_slot::in, list(prog_var)::in,
|
|
stack_slots::in, stack_slots::out) is det.
|
|
|
|
allocate_given_stack_slot(_Slot, [], !StackSlots).
|
|
allocate_given_stack_slot(Slot, [Var | Vars], !StackSlots) :-
|
|
map.det_insert(Var, Slot, !StackSlots),
|
|
allocate_given_stack_slot(Slot, Vars, !StackSlots).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% We must not allocate the same stack slot to different dummy variables.
|
|
% If we do, then the code that saves variables on the stack at calls
|
|
% will get confused. After saving one dummy variable on the stack,
|
|
% it will try to save the next in the same stack slot; believing
|
|
% the first variable to still be live, it will move it away.
|
|
%
|
|
% In ordinary grades, it is possible to have one value of type io.state
|
|
% and another of type store.store live at the same time; in debugging
|
|
% grades, due to our policy of extending variable lifetimes, more than
|
|
% one io.state may be live at the same time.
|
|
%
|
|
:- pred allocate_dummy_stack_slots(main_stack::in, list(prog_var)::in,
|
|
int::in, stack_slots::in, stack_slots::out) is det.
|
|
|
|
allocate_dummy_stack_slots(_, [], _, !StackSlots).
|
|
allocate_dummy_stack_slots(MainStack, [DummyVar | DummyVars],
|
|
N0, !StackSlots) :-
|
|
(
|
|
MainStack = det_stack,
|
|
Locn = det_slot(N0, single_width)
|
|
;
|
|
MainStack = nondet_stack,
|
|
Locn = nondet_slot(N0)
|
|
),
|
|
allocate_given_stack_slot(Locn, [DummyVar], !StackSlots),
|
|
allocate_dummy_stack_slots(MainStack, DummyVars, N0 - 1, !StackSlots).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- func float_width_on_stack(globals, main_stack) = stack_slot_width.
|
|
|
|
float_width_on_stack(Globals, Stack) = FloatWidth :-
|
|
% We only store unboxed double-width floats on the det stack.
|
|
% It would be possible to do on the nondet stack but we would probably
|
|
% need to pad the frame allocation at run time to ensure that any double
|
|
% variables in the frame will be at aligned memory addresses.
|
|
( if
|
|
Stack = det_stack,
|
|
double_width_floats_on_det_stack(Globals, yes)
|
|
then
|
|
FloatWidth = double_width
|
|
else
|
|
FloatWidth = single_width
|
|
).
|
|
|
|
:- pred var_is_float(vartypes::in, prog_var::in) is semidet.
|
|
|
|
var_is_float(VarTypes, Var) :-
|
|
lookup_var_type(VarTypes, Var, float_type).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Conform to memory alignment requirements for double-word values.
|
|
% We maintain the invariant that the stack pointer is double-aligned.
|
|
% The first stack variable is numbered 1 so all odd-numbered stack slots
|
|
% are aligned. In a downwards-growing stack a higher slot number has a
|
|
% lower address. When allocating two consecutive slots, we therefore want
|
|
% the highered-numbered slot to be ODD.
|
|
%
|
|
% [!:FirstFreeSlot, !:FirstFreeSlot+1] shall be the next slots to be
|
|
% allocated, therefore !:FirstFreeSlot must be EVEN and !:FirstFreeSlot+1
|
|
% must be ODD.
|
|
%
|
|
:- pred align_double_width_slots(int::in, int::out) is det.
|
|
|
|
align_double_width_slots(!FirstFreeSlot) :-
|
|
( if int.even(!.FirstFreeSlot) then
|
|
true
|
|
else
|
|
!:FirstFreeSlot= !.FirstFreeSlot + 1
|
|
).
|
|
|
|
:- pred next_slot(stack_slot_width::in, int::in, int::out) is det.
|
|
|
|
next_slot(single_width, FirstFreeSlot, FirstFreeSlot + 1).
|
|
next_slot(double_width, FirstFreeSlot, FirstFreeSlot + 2).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
:- end_module ll_backend.stack_alloc.
|
|
%-----------------------------------------------------------------------------%
|