mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-15 09:23:44 +00:00
This fixes the performance problem reported in Mantis bug #562. compiler/hlds_pred.m: Instead of storing a varset and a vartypes in each proc_info, store just a var_table. Update the predicates that create or clone procedures accordingly. Where we had operations on proc_infos that had two versions, one operating on a varset/vartypes pair and one operating on var_table, keep only the latter, with the (shorter) name of the former. Delete the arity argument of proc_info_init, because the only valid value of that argument is the length of the list of the argument types. (In other words, this arg has been redundant all along.) Change the operations that create new variables in a procedure to get the caller to specify the (base) name of the new variable up front. Delete the unused predicate proc_info_ensure_unique_names. compiler/type_util.m: Due to the change above, we now construct var_tables during the construction of the HLDS. The code that does that needs to fill in the field that says whether the type of each variable in the table is a dummy type or not. However, at this time, the pass that decides type representations has not been run yet. The code of is_type_a_dummy used to throw an exception in such situations. Change this so that in such situations, is_type_a_dummy returns a placeholder, not-guaranteed-to-be-correct value. Document why this is ok. compiler/post_typecheck.m: Replace the placeholder values in vte_is_dummy fields in all the entries in the var_tables in all (valid) predicates with valid data. (If there are any invalid predicates, the compilation will fail anyway.) The clause_to_proc pass will copy these updated var_tables to be the initial var_tables in procedures. compiler/make_goal.m: Change the operations that create new variables in a procedure to get the caller to specify the (base) name of the new variable up front. This is simpler than the old method, which created new variables without a name, and had the caller give them a name as a separate operation. And since var_tables need this info, get the caller to also specify whether the type is a dummy, if the type is not a builtin type which is known not to be a dummy. compiler/var_table.m: Document the times when the types and is_dummy fields in var_table entries become meaningful. Fix a potential bug: when performing type substitutions in var_table entries, updating a variable's type may change whether that variable is a dummy or not, so recompute that info. It is quite possible that we *never* replace a nondummy type with a dummy type or vice versa, but in the absence of a convincing correctness argument for that proposition, better safe than sorry. Export the previously-private predicate transform_var_table to post_typecheck. Add code to implement the unused predicate deleted from hlds_pred.m: at the time I wrote it, I haven't yet realised that it was unused. The code I wrote here is therefore unused as well, so it is commented out. I did not delete it, because it may be useful later on. compiler/direct_arg_in_out.m: Don't make and split var_tables, since it is no longer needed. compiler/accumulator.m: compiler/add_class.m: compiler/add_clause.m: compiler/add_heap_ops.m: compiler/add_pred.m: compiler/add_special_pred.m: compiler/add_trail_ops.m: compiler/arg_info.m: compiler/build_mode_constraints.m: compiler/bytecode_gen.m: compiler/check_typeclass.m: compiler/clause_to_proc.m: compiler/closure_analysis.m: compiler/code_gen.m: compiler/code_loc_dep.m: compiler/complexity.m: compiler/continuation_info.m: compiler/cse_detection.m: compiler/ctgc.livedata.m: compiler/deep_profiling.m: compiler/default_func_mode.m: compiler/deforest.m: compiler/delay_construct.m: compiler/delay_partial_inst.m: compiler/dep_par_conj.m: compiler/det_analysis.m: compiler/det_report.m: compiler/distance_granularity.m: compiler/equiv_type_hlds.m: compiler/exception_analysis.m: compiler/float_regs.m: compiler/follow_code.m: compiler/goal_mode.m: compiler/goal_path.m: compiler/higher_order.m: compiler/hlds_out_pred.m: compiler/hlds_rtti.m: compiler/hlds_statistics.m: compiler/inlining.m: compiler/intermod.m: compiler/intermod_analysis.m: compiler/introduce_exists_casts.m: compiler/introduce_parallelism.m: compiler/lambda.m: compiler/lco.m: compiler/live_vars.m: compiler/liveness.m: compiler/loop_inv.m: compiler/mark_tail_calls.m: compiler/ml_accurate_gc.m: compiler/ml_args_util.m: compiler/ml_closure_gen.m: compiler/ml_gen_info.m: compiler/ml_proc_gen.m: compiler/mode_errors.m: compiler/mode_info.m: compiler/modecheck_goal.m: compiler/par_loop_control.m: compiler/pd_debug.m: compiler/pd_info.m: compiler/pd_util.m: compiler/polymorphism_info.m: compiler/post_typecheck.m: compiler/proc_gen.m: compiler/proc_requests.m: compiler/purity.m: compiler/push_goals_together.m: compiler/quantification.m: compiler/rbmm.add_rbmm_goal_infos.m: compiler/rbmm.live_variable_analysis.m: compiler/rbmm.points_to_analysis.m: compiler/rbmm.points_to_graph.m: compiler/rbmm.points_to_info.m: compiler/rbmm.region_liveness_info.m: compiler/rbmm.region_transformation.m: compiler/recompute_instmap_deltas.m: compiler/saved_vars.m: compiler/simplify_goal_unify.m: compiler/simplify_info.m: compiler/simplify_proc.m: compiler/size_prof.m: compiler/ssdebug.m: compiler/stack_alloc.m: compiler/stack_layout.m: compiler/stack_opt.m: compiler/stm_expand.m: compiler/store_alloc.m: compiler/structure_reuse.analysis.m: compiler/structure_reuse.direct.choose_reuse.m: compiler/structure_reuse.direct.detect_garbage.m: compiler/structure_reuse.domain.m: compiler/structure_reuse.indirect.m: compiler/structure_reuse.lbu.m: compiler/structure_reuse.lfu.m: compiler/structure_reuse.versions.m: compiler/structure_sharing.analysis.m: compiler/structure_sharing.domain.m: compiler/switch_detection.m: compiler/table_gen.m: compiler/tabling_analysis.m: compiler/term_constr_build.m: compiler/term_constr_initial.m: compiler/term_errors.m: compiler/term_pass1.m: compiler/term_pass2.m: compiler/trace_gen.m: compiler/trailing_analysis.m: compiler/try_expand.m: compiler/tupling.m: compiler/unneeded_code.m: compiler/untupling.m: compiler/unused_args.m: compiler/unused_imports.m: Conform to the changes above. Mostly this means - not passing a module_info to get a var_table out of a proc_info, but - having to pass a module_info to code that either constructs a var_table, or adds entries to a var_table (since we now need the type table to figure out whether variables' types are dummies).
415 lines
16 KiB
Mathematica
415 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 hlds.code_model.
|
|
:- import_module hlds.hlds_llds.
|
|
:- 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.prog_type.
|
|
:- import_module parse_tree.set_of_var.
|
|
:- import_module parse_tree.var_table.
|
|
|
|
:- import_module assoc_list.
|
|
:- import_module bool.
|
|
:- 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),
|
|
EffTraceLevel =
|
|
eff_trace_level_for_proc(ModuleInfo, PredInfo, !.ProcInfo, TraceLevel),
|
|
NeedFailVars = eff_trace_level_needs_fail_vars(EffTraceLevel),
|
|
(
|
|
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_var_table(!.ProcInfo, VarTable),
|
|
AllocData = alloc_data(ModuleInfo, !.ProcInfo, proc(PredId, ProcId),
|
|
some_var_may_be_dummy(VarTable), TypeInfoLiveness, OptNoReturnCalls),
|
|
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(AllocData, FailVars, Goal0, Goal,
|
|
SimpleStackAlloc0, SimpleStackAlloc, Liveness0, _Liveness,
|
|
NondetLiveness0, _NondetLiveness),
|
|
proc_info_set_goal(Goal, !ProcInfo),
|
|
SimpleStackAlloc = stack_alloc(LiveSets0),
|
|
|
|
do_we_need_maxfr_slot(EffTraceLevel, !ProcInfo),
|
|
trace_reserved_slots(Globals, !.ProcInfo, EffTraceLevel,
|
|
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, VarTable, MainStack, NumReservedSlots,
|
|
MaybeReservedVarInfo, ColourList, StackSlots1),
|
|
var_table_to_sorted_assoc_list(VarTable, VarTableAL),
|
|
allocate_stack_slots_for_dummy_vars(MainStack, VarTableAL, -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) :-
|
|
DummyVarInfo = AllocData ^ ad_dummy_var_info,
|
|
(
|
|
DummyVarInfo = no_var_is_dummy,
|
|
NonDummyVars = Vars
|
|
;
|
|
DummyVarInfo = some_var_may_be_dummy(VarTable),
|
|
set_of_var.filter(var_is_not_dummy(VarTable), Vars, NonDummyVars)
|
|
).
|
|
|
|
:- pred var_is_not_dummy(var_table::in, prog_var::in) is semidet.
|
|
|
|
var_is_not_dummy(VarTable, Var) :-
|
|
lookup_var_entry(VarTable, Var, VarEntry),
|
|
VarEntry ^ vte_is_dummy = is_not_dummy_type.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred allocate_stack_slots(globals::in, var_table::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, VarTable, MainStack, NumReservedSlots,
|
|
MaybeReservedVarInfo, Colours, StackSlots) :-
|
|
( if float_width_on_stack(Globals, MainStack) = double_width then
|
|
MaybeDoubleWidthFloats = yes(VarTable)
|
|
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(var_table)::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(var_table)::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(VarTable),
|
|
% 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(VarTable), 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), $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_stack_slots_for_dummy_vars(main_stack::in,
|
|
assoc_list(prog_var, var_table_entry)::in,
|
|
int::in, stack_slots::in, stack_slots::out) is det.
|
|
|
|
allocate_stack_slots_for_dummy_vars(_, [], _, !StackSlots).
|
|
allocate_stack_slots_for_dummy_vars(MainStack, [Var - Entry | VarsEntries],
|
|
N0, !StackSlots) :-
|
|
Entry = vte(_N, _T, IsDummy),
|
|
(
|
|
IsDummy = is_not_dummy_type,
|
|
N1 = N0
|
|
;
|
|
IsDummy = is_dummy_type,
|
|
(
|
|
MainStack = det_stack,
|
|
Locn = det_slot(N0, single_width)
|
|
;
|
|
MainStack = nondet_stack,
|
|
Locn = nondet_slot(N0)
|
|
),
|
|
allocate_given_stack_slot(Locn, [Var], !StackSlots),
|
|
N1 = N0 - 1
|
|
),
|
|
allocate_stack_slots_for_dummy_vars(MainStack, VarsEntries,
|
|
N1, !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(var_table::in, prog_var::in) is semidet.
|
|
|
|
var_is_float(VarTable, Var) :-
|
|
lookup_var_type(VarTable, 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.
|
|
%-----------------------------------------------------------------------------%
|