Files
mercury/compiler/continuation_info.m
Zoltan Somogyi 625ec287f1 Carve five new modules out of prog_type.m.
compiler/prog_type_construct.m:
    New module for constructing types.

compiler/prog_type_repn.m:
    New module for testing things related to type representation.

compiler/prog_type_scan.m:
    New module for gather type vars in types.

compiler/prog_type_test.m:
    New module containing simple tests on types.

compiler/prog_type_unify.m:
    New module for testing whether two types unify, or whether
    one type subsumes another.

compiler/prog_type.m:
    Delete the code moved to the new modules.

compiler/parse_tree.m:
    Include the new modules.

compiler/notes/compiler_design.html:
    Document the new modules.

compiler/*.m:
    Conform to the changes above, by adjusting imports as needed,
    and by deleting any explicit module qualifications that
    this diff makes obsolete.
2023-10-06 08:42:43 +11:00

993 lines
41 KiB
Mathematica

%-----------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%-----------------------------------------------------------------------------%
% Copyright (C) 1997-2000,2002-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: continuation_info.m.
% Main author: trd.
% Extensive modifications by zs.
%
% This file defines the data structures the code generator uses to collect
% information that will later be converted into layout tables for accurate
% garbage collection, stack tracing, execution tracing, deep profiling and
% perhaps other purposes.
%
% Information is collected in several passes.
%
% 1 Before we start generating code for a procedure,
% we initialize the set of internal labels for which we have
% layout information to the empty set. This set is stored in
% the code generator state.
%
% 2 During code generation for the procedure, provided the option
% trace_stack_layouts is set, we add layout information for labels
% that represent trace ports to the code generator state. If
% agc_stack_layouts is set, we add layout information for the stack
% label in each resumption point. And regardless of option settings,
% we also generate layouts to be attached to any closures we create.
%
% 3 After we finish generating code for a procedure, we record
% all the static information about the procedure (some of which
% is available only after code generation), together with the
% info about internal labels accumulated in the code generator state,
% in the global_data structure.
%
% 4 If agc_stack_layouts is set, we make a pass over the
% optimized code recorded in the final LLDS instructions.
% In this pass, we collect information from call instructions
% about the internal labels to which calls can return.
% This info will also go straight into the global_data.
%
% This module defines the data structures used by all passes. It also
% implements the whole of pass 4, and various fractions of the other passes.
%
% stack_layout.m converts the information collected in this module into
% stack_layout tables.
%
%-----------------------------------------------------------------------------%
:- module ll_backend.continuation_info.
:- interface.
:- import_module hlds.
:- import_module hlds.hlds_goal.
:- import_module hlds.hlds_module.
:- import_module hlds.hlds_pred.
:- import_module hlds.hlds_rtti.
:- import_module hlds.instmap.
:- import_module libs.
:- import_module libs.globals.
:- import_module libs.trace_params.
:- import_module ll_backend.global_data.
:- import_module ll_backend.layout.
:- import_module ll_backend.llds.
:- import_module ll_backend.trace_gen.
:- import_module mdbcomp.
:- import_module mdbcomp.goal_path.
:- import_module mdbcomp.prim_data.
:- import_module parse_tree.
:- import_module parse_tree.prog_data.
:- import_module parse_tree.prog_data_pragma.
:- import_module parse_tree.var_table.
:- import_module assoc_list.
:- import_module bool.
:- import_module list.
:- import_module map.
:- import_module maybe.
:- import_module pair.
:- import_module set.
%-----------------------------------------------------------------------------%
% Information for any procedure, includes information about the
% procedure itself, and any internal labels within it.
%
:- type proc_layout_info
---> proc_layout_info(
% Determines which stack is used.
pli_detism :: determinism,
% The effective trace level of the procedure.
pli_eff_trace_level :: eff_trace_level,
% The evaluation method of the procedure.
pli_eval_method :: eval_method,
% Does the level of execution tracing of this procedure require
% a representation of the procedure body in the layout
% structures? Note that even if this field is set to
% trace_does_not_need_body_rep, other options (such as deep
% profiling) may still ask for the body to be included.
pli_trace_body_rep :: trace_needs_body_rep,
% Do we require the procedure id section of the procedure
% layout to be present, even if the option procid_stack_layout
% is not set?
pli_need_proc_id :: bool,
% True iff we need the names of all the variables.
pli_need_all_names :: bool,
% The identity of the procedure.
pli_rtti_proc_label :: rtti_proc_label,
pli_entry_label :: label,
% Number of stack slots.
pli_stack_slot_count :: int,
% Location of succip on stack.
pli_succip_slot :: maybe(int),
% If the trace level is not none, this contains the label
% associated with the call event, whose stack layout says
% which variables were live and where on entry.
pli_call_label :: maybe(label),
% The number of the highest numbered rN and fN registers that
% can contain useful information during a call to MR_trace from
% within this procedure.
pli_max_trace_reg_r :: int,
pli_max_trace_reg_f :: int,
% The head variables, in order, including the ones introduced
% by the compiler.
pli_head_vars :: list(prog_var),
% The modes of the head variables.
pli_arg_modes :: list(mer_mode),
% The body of the procedure.
pli_proc_body :: hlds_goal,
% The instmap at the start of the procedure body.
pli_initial_instmap :: instmap,
% Info about the stack slots used for tracing.
pli_trace_slot_info :: trace_slot_info,
% The names and types of all the variables.
pli_var_table :: var_table,
% Info for each internal label, needed for basic_stack_layouts.
pli_internal_map :: proc_label_layout_info,
pli_maybe_table_info :: maybe(proc_layout_table_info),
pli_oisu_kind_fors :: list(oisu_pred_kind_for),
pli_deep_prof :: maybe(proc_deep_prof_info)
).
:- type proc_deep_prof_info
---> proc_deep_prof_info(
pdpi_proc_static :: hlds_proc_static,
pdpi_excp_slots :: deep_excp_slots,
pdpi_orig_body :: deep_original_body
).
:- type proc_layout_table_info
---> proc_table_io_entry(
proc_table_io_info
)
; proc_table_struct(
proc_table_struct_info
).
:- type trace_needs_body_rep
---> trace_needs_body_rep
; trace_does_not_need_body_rep.
% Information about the labels internal to a procedure.
%
:- type proc_label_layout_info == map(int, internal_layout_info).
% Information for an internal label.
%
% There are three ways for the compiler to generate labels for
% which layouts may be required:
%
% (a) as the label associated with a trace port,
% (b) as the label associated with resume point that gets stored
% as a redoip in a nondet stack frame, and
% (c) as the return label of some kind of call (plain, method or h-o).
%
% Label optimizations may redirect a call return away from the
% originally generated label to another label, possibly one
% that is associated with a trace port. This optimization may
% also direct returns from more than one call to the same label.
%
% We may be interested in the layout of things at a label for three
% different reasons: for stack tracing, for accurate gc, and for
% execution tracing (which may include up-level printing from the
% debugger).
%
% - For stack tracing, we are interested only in call return labels.
% Even for these, we need only the pointer to the procedure layout
% info; we do not need any information about variables.
%
% - For accurate gc, we are interested only in resume point labels
% and call return labels. We need to know about all the variables
% that can be accessed after the label; this is the intersection of
% all the variables denoted as live in the respective labels.
% (Variables which are not in the intersection are not guaranteed
% to have a meaningful value on all execution paths that lead to the
% label.)
%
% - For execution tracing, our primary interest is in trace port
% labels. At these labels we only want info about named variables,
% but we may want this info even if the variable will never be
% referred to again.
%
% When the trace level requires support for up-level printing,
% execution tracing also requires information about return labels.
% The variables about which we want info at these labels is a subset
% of the variables agc is interested in (the named subset).
% We do not collect this set explicitly. Instead, if we are doing
% execution tracing, we collect agc layout info as usual, and
% (if we not really doing agc) remove the unnamed variables
% in stack_layout.m.
%
% For labels which correspond to a trace port (part (a) above),
% we record information in the first field. Since trace.m generates
% a unique label for each trace port, this field is never updated
% once it is set in pass 2.
%
% For labels which correspond to redoips (part (b) above), we record
% information in the second field. Since code_info.m generates
% unique labels for each resumption point, this field is never updated
% once it is set in pass 2.
%
% For labels which correspond to a call return (part (c) above),
% we record information in the third field during pass 4. If execution
% tracing is turned on, then jumpopt.m will not redirect call return
% addresses, and thus each label will correspond to at most one call
% return. If execution tracing is turned off, jumpopt.m may redirect
% call return addresses, which means that a label can serve as the
% return label for more than one call. In that case, this field can be
% updated after it is set. This updating requires taking the
% intersection of the sets of live variables, and gathering up all the
% contexts into a list. Later, stack_layout.m will pick one (valid)
% context essentially at random, which is OK because the picked
% context will not be used for anything, except possibly for debugging
% native gc.
%
% Since a call may return to the label of an internal port, it is
% possible for both fields to be set. In this case, stack_layout.m
% will take the union of the relevant info. If neither field is set,
% then the label's layout is required only for stack tracing.
%
:- type internal_layout_info
---> internal_layout_info(
maybe(trace_port_layout_info),
maybe(layout_label_info),
maybe(return_layout_info)
).
:- type trace_port_layout_info
---> trace_port_layout_info(
port_context :: prog_context,
port_type :: trace_port,
port_is_hidden :: bool,
port_path :: forward_goal_path,
port_user :: maybe(user_event_info),
port_label :: layout_label_info
).
:- type return_layout_info
---> return_layout_info(
assoc_list(code_addr, pair(prog_context, forward_goal_path)),
layout_label_info
).
% Information about the layout of live data for a label.
%
:- type layout_label_info
---> layout_label_info(
set(layout_var_info),
% Live vars and their locations/names.
map(tvar, set(layout_locn))
% Locations of polymorphic type vars.
).
:- type layout_var_info
---> layout_var_info(
layout_locn, % The location of the variable.
live_value_type, % Info about the variable.
string % Where in the compiler this
% layout_var_info was created
).
:- type user_attribute
---> user_attribute(
attr_locn :: rval,
attr_var :: prog_var
).
:- type user_event_info
---> user_event_info(
user_port_number :: int,
user_attributes :: list(maybe(user_attribute))
).
:- type closure_layout_info
---> closure_layout_info(
list(closure_arg_info),
% There is one closure_arg_info for each argument of the called
% procedure, even the args which are not in the closure
map(tvar, set(layout_locn))
% Locations of polymorphic type vars,
% encoded so that rN refers to argument N.
).
:- type closure_arg_info
---> closure_arg_info(
mer_type, % The type of the argument.
mer_inst % The initial inst of the argument.
% It may be useful in the future to include
% info about the final insts and about
% the determinism. This would allow us
% to implement checked dynamic inst casts,
% which may be helpful for dynamic loading.
% It may also be useful for printing
% closures and for providing user-level
% RTTI access.
).
:- type slot_contents
---> slot_ticket % A ticket (trail pointer).
; slot_ticket_counter % A copy of the ticket counter.
; slot_trace_data
; slot_lookup_disj_cur
; slot_lookup_switch_cur
; slot_lookup_switch_max
; slot_sync_term % A syncronization term used
% at the end of par_conjs.
% See par_conj_gen.m for details.
; slot_region_ite
; slot_region_disj
; slot_region_commit
; slot_success_record % A record of whether a piece of code
% has ever succeeded.
; slot_lval(lval).
% Call maybe_collect_call_continuations_in_cproc on every procedure
% in the list.
%
:- pred maybe_collect_call_continuations_in_cprocs(module_info::in,
list(c_procedure)::in, global_data::in, global_data::out) is det.
% Check whether this procedure ought to have any layout structures
% generated for it. If yes, then update the global_data to
% include all the continuation labels within a proc. Whether or not
% the information about a continuation label includes the variables
% live at that label depends on the values of options.
%
:- pred maybe_collect_call_continuations_in_cproc(module_info::in,
c_procedure::in, global_data::in, global_data::out) is det.
% Check whether the given procedure should have at least (a) a basic
% stack layout, and (b) a procedure id layout generated for it.
% The two bools returned answer these two questions respectively.
%
:- pred basic_stack_layout_for_proc(globals::in, pred_info::in,
bool::out, bool::out) is det.
% Generate the layout information we need for the return point of a call.
%
:- pred cont_info_generate_return_live_lvalues(globals::in, proc_info::in,
eff_trace_level::in, assoc_list(prog_var, arg_loc)::in,
instmap::in, list(prog_var)::in, map(prog_var, set(lval))::in,
assoc_list(lval, slot_contents)::in, bool::in, list(liveinfo)::out) is det.
% Generate the layout information we need for a resumption point,
% a label where forward execution can restart after backtracking.
%
:- pred generate_resume_layout(proc_info::in, instmap::in,
map(prog_var, set(lval))::in, assoc_list(lval, slot_contents)::in,
layout_label_info::out) is det.
% Generate the layout information we need to include in a closure.
%
:- pred generate_closure_layout(module_info::in, pred_id::in, proc_id::in,
closure_layout_info::out) is det.
% For each type variable in the given list, find out where the
% typeinfo var for that type variable is.
%
:- pred find_typeinfos_for_tvars(var_table::in, rtti_varmaps::in,
list(tvar)::in, map(prog_var, set(lval))::in,
map(tvar, set(layout_locn))::out) is det.
:- pred generate_table_arg_type_info(var_table::in, rtti_varmaps::in,
assoc_list(prog_var, int)::in, table_arg_infos::out) is det.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- implementation.
:- import_module hlds.hlds_llds.
:- import_module hlds.hlds_proc_util.
:- import_module libs.options.
:- import_module ll_backend.code_util.
:- import_module parse_tree.builtin_lib_types.
:- import_module parse_tree.prog_type.
:- import_module parse_tree.prog_type_scan.
:- import_module parse_tree.prog_type_test.
:- import_module int.
:- import_module require.
:- import_module string.
:- import_module term.
%-----------------------------------------------------------------------------%
maybe_collect_call_continuations_in_cprocs(_, [], !GlobalData).
maybe_collect_call_continuations_in_cprocs(ModuleInfo, [CProc | CProcs],
!GlobalData) :-
maybe_collect_call_continuations_in_cproc(ModuleInfo, CProc,
!GlobalData),
maybe_collect_call_continuations_in_cprocs(ModuleInfo, CProcs,
!GlobalData).
maybe_collect_call_continuations_in_cproc(ModuleInfo, CProc, !GlobalData) :-
CProc ^ cproc_id = proc(PredId, _),
module_info_pred_info(ModuleInfo, PredId, PredInfo),
module_info_get_globals(ModuleInfo, Globals),
basic_stack_layout_for_proc(Globals, PredInfo, Layout, _),
(
Layout = yes,
EffTraceLevel = CProc ^ cproc_eff_trace_level,
globals.want_return_var_layouts(Globals, EffTraceLevel,
WantReturnLayout),
collect_call_continuations_in_cproc(WantReturnLayout, CProc,
!GlobalData)
;
Layout = no
).
:- type call_info
---> call_info(
call_return_label :: label,
call_target :: code_addr,
call_live_on_return :: list(liveinfo),
call_context :: term.context,
% The position of the call in the body if tracing is enabled.
call_goal_path :: maybe(forward_goal_path)
).
% Process the list of instructions for this proc, adding
% all internal label information to global_data.
%
:- pred collect_call_continuations_in_cproc(bool::in, c_procedure::in,
global_data::in, global_data::out) is det.
collect_call_continuations_in_cproc(WantReturnInfo, CProc, !GlobalData) :-
PredProcId = CProc ^ cproc_id,
global_data_get_proc_layout(!.GlobalData, PredProcId, ProcLayoutInfo0),
Instrs = CProc ^ cproc_code,
Internals0 = ProcLayoutInfo0 ^ pli_internal_map,
list.filter_map(get_call_info, Instrs, CallInfos),
list.foldl(collect_continuation(WantReturnInfo), CallInfos,
Internals0, Internals),
ProcLayoutInfo = ProcLayoutInfo0 ^ pli_internal_map := Internals,
global_data_update_proc_layout(PredProcId, ProcLayoutInfo, !GlobalData).
:- pred get_call_info(instruction::in, call_info::out) is semidet.
get_call_info(Instr, CallInfo) :-
Instr = llds_instr(Uinstr, _Comment),
Uinstr = llcall(Target, Return, LiveInfo, Context, GoalPath, _),
Return = code_label(ReturnLabel),
CallInfo = call_info(ReturnLabel, Target, LiveInfo, Context, GoalPath).
%-----------------------------------------------------------------------------%
% Collect the liveness information from a single return label
% and add it to the internals.
%
:- pred collect_continuation(bool::in, call_info::in,
proc_label_layout_info::in, proc_label_layout_info::out) is det.
collect_continuation(WantReturnInfo, CallInfo, !Internals) :-
CallInfo = call_info(ReturnLabel, Target, LiveInfoList, Context,
MaybeGoalPath),
% We could check not only that the return label is an internal label,
% but also that it belongs to the current procedure, but this would be
% serious paranoia.
(
ReturnLabel = internal_label(ReturnLabelNum, _)
;
ReturnLabel = entry_label(_, _),
unexpected($pred, "bad return")
),
( if map.search(!.Internals, ReturnLabelNum, Internal0) then
Internal0 = internal_layout_info(Port0, Resume0, Return0)
else
Port0 = no,
Resume0 = no,
Return0 = no
),
(
WantReturnInfo = yes,
(
MaybeGoalPath = no,
% XXX We used to handle these situations by using an empty path.
% XXX Should we throw an exception?
Return = Return0
;
MaybeGoalPath = yes(GoalPath),
convert_return_data(LiveInfoList, VarInfoSet, TypeInfoMap),
(
Return0 = no,
Layout = layout_label_info(VarInfoSet, TypeInfoMap),
ReturnInfo = return_layout_info(
[Target - (Context - GoalPath)], Layout),
Return = yes(ReturnInfo)
;
% If a var is known to be dead on return from one call,
% it cannot be accessed on returning from the other calls
% that reach the same return address either.
Return0 = yes(ReturnInfo0),
ReturnInfo0 = return_layout_info(TargetsContexts0, Layout0),
Layout0 = layout_label_info(LV0, TV0),
set.intersect(LV0, VarInfoSet, LV),
map.intersect(set.intersect, TV0, TypeInfoMap, TV),
Layout = layout_label_info(LV, TV),
TargetContexts = [Target - (Context - GoalPath)
| TargetsContexts0],
ReturnInfo = return_layout_info(TargetContexts, Layout),
Return = yes(ReturnInfo)
)
)
;
WantReturnInfo = no,
Return = Return0
),
Internal = internal_layout_info(Port0, Resume0, Return),
map.set(ReturnLabelNum, Internal, !Internals).
:- pred convert_return_data(list(liveinfo)::in,
set(layout_var_info)::out, map(tvar, set(layout_locn))::out) is det.
convert_return_data(LiveInfos, VarInfoSet, TypeInfoMap) :-
GetVarInfo = (pred(LiveLval::in, VarInfo::out) is det :-
LiveLval = live_lvalue(Lval, LiveValueType, _),
VarInfo = layout_var_info(Lval, LiveValueType, "convert_return_data")
),
list.map(GetVarInfo, LiveInfos, VarInfoList),
GetTypeInfo = (pred(LiveLval::in, LiveTypeInfoMap::out) is det :-
LiveLval = live_lvalue(_, _, LiveTypeInfoMap)
),
list.map(GetTypeInfo, LiveInfos, TypeInfoMapList),
map.init(Empty),
list.foldl((pred(TIM1::in, TIM2::in, TIM::out) is det :-
map.union(set.intersect, TIM1, TIM2, TIM)
), TypeInfoMapList, Empty, TypeInfoMap),
set.list_to_set(VarInfoList, VarInfoSet).
%-----------------------------------------------------------------------------%
basic_stack_layout_for_proc(Globals, PredInfo, BasicLayout,
ForceProcIdLayout) :-
( if
globals.lookup_bool_option(Globals, stack_trace_higher_order, yes),
some_arg_is_higher_order(PredInfo)
then
BasicLayout = yes,
ForceProcIdLayout = yes
else if
globals.lookup_bool_option(Globals, basic_stack_layout, yes)
then
BasicLayout = yes,
ForceProcIdLayout = no
else
BasicLayout = no,
ForceProcIdLayout = no
).
:- pred some_arg_is_higher_order(pred_info::in) is semidet.
some_arg_is_higher_order(PredInfo) :-
pred_info_get_arg_types(PredInfo, ArgTypes),
some [Type] (
list.member(Type, ArgTypes),
type_is_higher_order(Type)
).
%-----------------------------------------------------------------------------%
cont_info_generate_return_live_lvalues(Globals, ProcInfo,
EffTraceLevel, OutputArgLocs, ReturnInstMap, Vars, VarLocs, Temps,
OkToDeleteAny, LiveLvalues) :-
globals.want_return_var_layouts(Globals, EffTraceLevel,
WantReturnVarLayout),
proc_info_get_stack_slots(ProcInfo, StackSlots),
find_return_var_lvals(StackSlots, OkToDeleteAny, OutputArgLocs,
Vars, VarLvals),
proc_info_get_var_table(ProcInfo, VarTable),
proc_info_get_rtti_varmaps(ProcInfo, RttiVarMaps),
generate_var_live_lvalues(VarTable, RttiVarMaps, WantReturnVarLayout,
ReturnInstMap, VarLocs, VarLvals, VarLiveLvalues),
generate_temp_live_lvalues(Temps, TempLiveLvalues),
LiveLvalues = VarLiveLvalues ++ TempLiveLvalues.
:- pred find_return_var_lvals(stack_slots::in, bool::in,
assoc_list(prog_var, arg_loc)::in, list(prog_var)::in,
assoc_list(prog_var, lval)::out) is det.
find_return_var_lvals(_, _, _, [], []).
find_return_var_lvals(StackSlots, OkToDeleteAny, OutputArgLocs,
[Var | Vars], VarLvals) :-
find_return_var_lvals(StackSlots, OkToDeleteAny, OutputArgLocs,
Vars, TailVarLvals),
( if assoc_list.search(OutputArgLocs, Var, ArgLoc) then
% On return, output arguments are in their registers.
code_util.arg_loc_to_register(ArgLoc, Lval),
VarLvals = [Var - Lval | TailVarLvals]
else if map.search(StackSlots, Var, Slot) then
% On return, other live variables are in their stack slots.
VarLvals = [Var - stack_slot_to_lval(Slot) | TailVarLvals]
else
(
OkToDeleteAny = yes,
VarLvals = TailVarLvals
;
OkToDeleteAny = no,
unexpected($pred, "no slot")
)
).
:- pred generate_temp_live_lvalues(assoc_list(lval, slot_contents)::in,
list(liveinfo)::out) is det.
generate_temp_live_lvalues([], []).
generate_temp_live_lvalues([Temp | Temps], [Live | Lives]) :-
Temp = Slot - Contents,
live_value_type(Contents, LiveLvalueType),
map.init(Empty),
Live = live_lvalue(locn_direct(Slot), LiveLvalueType, Empty),
generate_temp_live_lvalues(Temps, Lives).
:- pred generate_var_live_lvalues(var_table::in, rtti_varmaps::in, bool::in,
instmap::in, map(prog_var, set(lval))::in,
assoc_list(prog_var, lval)::in, list(liveinfo)::out) is det.
generate_var_live_lvalues(_, _, _, _, _, [], []).
generate_var_live_lvalues(VarTable, RttiVarMaps, WantReturnVarLayout,
InstMap, VarLocs, [Var - Lval | VarLvals], [Live | Lives]) :-
(
WantReturnVarLayout = yes,
generate_layout_for_var(VarTable, InstMap, Var, LiveValueType,
TypeVars),
find_typeinfos_for_tvars(VarTable, RttiVarMaps, TypeVars, VarLocs,
TypeParams),
Live = live_lvalue(locn_direct(Lval), LiveValueType, TypeParams)
;
WantReturnVarLayout = no,
map.init(Empty),
Live = live_lvalue(locn_direct(Lval), live_value_unwanted, Empty)
),
generate_var_live_lvalues(VarTable, RttiVarMaps, WantReturnVarLayout,
InstMap, VarLocs, VarLvals, Lives).
%---------------------------------------------------------------------------%
generate_resume_layout(ProcInfo, InstMap, ResumeMap, Temps, Layout) :-
map.to_assoc_list(ResumeMap, ResumeList),
set.init(TVars0),
proc_info_get_var_table(ProcInfo, VarTable),
proc_info_get_rtti_varmaps(ProcInfo, RttiVarMaps),
generate_resume_layout_for_vars(VarTable, InstMap, ResumeList,
[], VarInfos, TVars0, TVars),
set.list_to_set(VarInfos, VarInfoSet),
set.to_sorted_list(TVars, TVarList),
find_typeinfos_for_tvars(VarTable, RttiVarMaps, TVarList, ResumeMap,
TVarInfoMap),
generate_temp_var_infos(Temps, TempInfos),
set.list_to_set(TempInfos, TempInfoSet),
set.union(VarInfoSet, TempInfoSet, AllInfoSet),
Layout = layout_label_info(AllInfoSet, TVarInfoMap).
:- pred generate_resume_layout_for_vars(var_table::in, instmap::in,
assoc_list(prog_var, set(lval))::in,
list(layout_var_info)::in, list(layout_var_info)::out,
set(tvar)::in, set(tvar)::out) is det.
generate_resume_layout_for_vars(_, _, [], !VarInfos, !TVars).
generate_resume_layout_for_vars(VarTable, InstMap, [Var - LvalSet | VarLvals],
!VarInfos, !TVars) :-
lookup_var_entry(VarTable, Var, Entry),
Entry = vte(_Name, _Type, IsDummy),
(
IsDummy = is_dummy_type
;
IsDummy = is_not_dummy_type,
generate_resume_layout_for_var(VarTable, InstMap, Var, LvalSet,
VarInfo, TypeVars),
set.insert_list(TypeVars, !TVars),
!:VarInfos = [VarInfo | !.VarInfos]
),
generate_resume_layout_for_vars(VarTable, InstMap, VarLvals,
!VarInfos, !TVars).
:- pred generate_resume_layout_for_var(var_table::in, instmap::in,
prog_var::in, set(lval)::in, layout_var_info::out, list(tvar)::out) is det.
generate_resume_layout_for_var(VarTable, InstMap, Var, LvalSet,
VarInfo, TypeVars) :-
set.to_sorted_list(LvalSet, LvalList),
( if LvalList = [LvalPrime] then
Lval = LvalPrime
else
unexpected($pred, "var has more than one lval in stack resume map")
),
( if Lval = stackvar(N) then
expect(N > 0, $pred, "bad stackvar")
else if Lval = framevar(N) then
expect(N > 0, $pred, "bad framevar")
else if Lval = double_stackvar(_, N) then
expect(N > 0, $pred, "bad double_stackvar")
else
true
),
generate_layout_for_var(VarTable, InstMap, Var, LiveValueType, TypeVars),
VarInfo = layout_var_info(locn_direct(Lval), LiveValueType,
"generate_result_layout_for_var").
:- pred generate_temp_var_infos(assoc_list(lval, slot_contents)::in,
list(layout_var_info)::out) is det.
generate_temp_var_infos([], []).
generate_temp_var_infos([Temp | Temps], [Live | Lives]) :-
Temp = Slot - Contents,
live_value_type(Contents, LiveLvalueType),
Live = layout_var_info(locn_direct(Slot), LiveLvalueType,
"generate_temp_var_infos"),
generate_temp_var_infos(Temps, Lives).
%---------------------------------------------------------------------------%
:- pred generate_layout_for_var(var_table::in, instmap::in, prog_var::in,
live_value_type::out, list(tvar)::out) is det.
generate_layout_for_var(VarTable, _InstMap, Var, LiveValueType, TypeVars) :-
lookup_var_entry(VarTable, Var, VarEntry),
VarEntry = vte(Name, Type, _IsDummy),
% In some programs, specifically zm_enum.m, the Mercury program generated
% for the enum.zinc g12 test case, this call to inst_is_ground can be
% a huge performance problem. The reason for that is the following.
%
% - The program builds a huge data structure.
% - The nth variable in this data structure is built from the previous
% n-1 variables,
% - The size of the inst of the nth variable is therefore proportional to n.
% - The total sizes of the n insts is therefore proportional to n^2.
% - The value of n can be huge.
%
% Since we do not yet use the inst field in live_value_vars, there is
% no point in incurring the expense of filling it in.
%
% instmap_lookup_var(InstMap, Var, Inst),
% ( if inst_match.inst_is_ground(ModuleInfo, Inst) then
% LldsInst = llds_inst_ground
% else
% LldsInst = llds_inst_partial(Inst)
% ),
LldsInst = llds_inst_better_be_ground,
LiveValueType = live_value_var(Var, Name, Type, LldsInst),
type_vars_in_type(Type, TypeVars).
%---------------------------------------------------------------------------%
generate_closure_layout(ModuleInfo, PredId, ProcId, ClosureLayout) :-
module_info_get_globals(ModuleInfo, Globals),
globals.lookup_bool_option(Globals, use_float_registers, UseFloatRegs),
module_info_pred_proc_info(ModuleInfo, PredId, ProcId, PredInfo, ProcInfo),
proc_info_get_var_table(ProcInfo, VarTable),
proc_info_get_rtti_varmaps(ProcInfo, RttiVarMaps),
proc_info_get_headvars(ProcInfo, HeadVars),
proc_info_arg_info(ProcInfo, ArgInfos),
pred_info_get_arg_types(PredInfo, ArgTypes),
proc_info_get_initial_instmap(ModuleInfo, ProcInfo, InstMap),
map.init(VarLocs0),
set.init(TypeVars0),
( if
build_closure_info(HeadVars, ArgTypes, ArgInfos, ArgLayouts, InstMap,
UseFloatRegs, VarLocs0, VarLocs, TypeVars0, TypeVars)
then
set.to_sorted_list(TypeVars, TypeVarsList),
find_typeinfos_for_tvars(VarTable, RttiVarMaps, TypeVarsList, VarLocs,
TypeInfoDataMap),
ClosureLayout = closure_layout_info(ArgLayouts, TypeInfoDataMap)
else
unexpected($pred, "proc headvars and pred argtypes disagree on arity")
).
:- pred build_closure_info(list(prog_var)::in,
list(mer_type)::in, list(arg_info)::in, list(closure_arg_info)::out,
instmap::in, bool::in,
map(prog_var, set(lval))::in, map(prog_var, set(lval))::out,
set(tvar)::in, set(tvar)::out) is semidet.
build_closure_info([], [], [], [], _, _, !VarLocs, !TypeVars).
build_closure_info([Var | Vars], [Type0 | Types],
[ArgInfo | ArgInfos], [Layout | Layouts], InstMap, UseFloatRegs,
!VarLocs, !TypeVars) :-
ArgInfo = arg_info(ArgLoc, _ArgMode),
% If the float argument is passed via a regular register then replace the
% type_ctor_info in the closure layout so that we can distinguish those
% arguments from float arguments passed via float registers.
( if
UseFloatRegs = yes,
Type0 = float_type,
ArgLoc = reg(reg_r, _)
then
Type = float_box_type
else
Type = Type0
),
instmap_lookup_var(InstMap, Var, Inst),
Layout = closure_arg_info(Type, Inst),
arg_loc_to_register(ArgLoc, Reg),
Locations = set.make_singleton_set(Reg),
map.det_insert(Var, Locations, !VarLocs),
type_vars_in_type(Type, VarTypeVars),
set.insert_list(VarTypeVars, !TypeVars),
build_closure_info(Vars, Types, ArgInfos, Layouts, InstMap, UseFloatRegs,
!VarLocs, !TypeVars).
%---------------------------------------------------------------------------%
find_typeinfos_for_tvars(VarTable, RttiVarMaps, TypeVars, VarLocs,
TypeInfoDataMap) :-
list.foldl(
gather_type_info_layout_locns_for_tvar(VarTable, RttiVarMaps, VarLocs),
TypeVars, map.init, TypeInfoDataMap).
:- pred gather_type_info_layout_locns_for_tvar(var_table::in, rtti_varmaps::in,
map(prog_var, set(lval))::in, tvar::in,
map(tvar, set(layout_locn))::in, map(tvar, set(layout_locn))::out) is det.
gather_type_info_layout_locns_for_tvar(VarTable, RttiVarMaps, VarLocs, TypeVar,
!TypeInfoDataMap) :-
rtti_lookup_type_info_locn(RttiVarMaps, TypeVar, TypeInfoLocn),
type_info_locn_var(TypeInfoLocn, TypeInfoVar),
( if map.search(VarLocs, TypeInfoVar, TypeInfoLvalSet) then
set.fold(gather_type_info_layout_locn(TypeInfoLocn), TypeInfoLvalSet,
set.init, Locns),
map.det_insert(TypeVar, Locns, !TypeInfoDataMap)
else
TypeInfoVarName = var_table_entry_name(VarTable, TypeInfoVar),
unexpected($pred,
"can't find rval for type_info var " ++ TypeInfoVarName)
).
:- pred gather_type_info_layout_locn(type_info_locn::in, lval::in,
set(layout_locn)::in, set(layout_locn)::out) is det.
gather_type_info_layout_locn(TypeInfoLocn, Lval, !Locns) :-
(
TypeInfoLocn = typeclass_info(_, FieldNum),
Locn = locn_indirect(Lval, FieldNum)
;
TypeInfoLocn = type_info(_),
Locn = locn_direct(Lval)
),
set.insert(Locn, !Locns).
%---------------------------------------------------------------------------%
generate_table_arg_type_info(VarTable, RttiVarMaps, NumberedVars,
TableArgInfos) :-
set.init(TypeVars0),
build_table_arg_info(VarTable, NumberedVars, ArgLayouts,
TypeVars0, TypeVars),
set.to_sorted_list(TypeVars, TypeVarsList),
find_typeinfos_for_tvars_table(VarTable, RttiVarMaps, TypeVarsList,
NumberedVars, TypeInfoDataMap),
TableArgInfos = table_arg_infos(ArgLayouts, TypeInfoDataMap).
:- pred build_table_arg_info(var_table::in,
assoc_list(prog_var, int)::in, list(table_arg_info)::out,
set(tvar)::in, set(tvar)::out) is det.
build_table_arg_info(_, [], [], !TypeVars).
build_table_arg_info(VarTable, [Var - SlotNum | NumberedVars],
[ArgLayout | ArgLayouts], !TypeVars) :-
term.var_to_int(Var, VarNum),
lookup_var_entry(VarTable, Var, Entry),
VarName = var_entry_name(Var, Entry),
Type = Entry ^ vte_type,
ArgLayout = table_arg_info(VarNum, VarName, SlotNum, Type),
set_of_type_vars_in_type(Type, VarTypeVars),
set.union(VarTypeVars, !TypeVars),
build_table_arg_info(VarTable, NumberedVars, ArgLayouts, !TypeVars).
%---------------------------------------------------------------------------%
:- pred find_typeinfos_for_tvars_table(var_table::in, rtti_varmaps::in,
list(tvar)::in, assoc_list(prog_var, int)::in,
map(tvar, table_locn)::out) is det.
find_typeinfos_for_tvars_table(VarTable, RttiVarMaps, TypeVars, NumberedVars,
TypeInfoDataMap) :-
list.map(rtti_lookup_type_info_locn(RttiVarMaps), TypeVars,
TypeInfoLocns),
FindLocn =
( pred(TypeInfoLocn::in, Locn::out) is det :-
( if
(
TypeInfoLocn = typeclass_info(TypeInfoVar, FieldNum),
assoc_list.search(NumberedVars, TypeInfoVar, Slot),
LocnPrime = table_locn_indirect(Slot, FieldNum)
;
TypeInfoLocn = type_info(TypeInfoVar),
assoc_list.search(NumberedVars, TypeInfoVar, Slot),
LocnPrime = table_locn_direct(Slot)
)
then
Locn = LocnPrime
else
type_info_locn_var(TypeInfoLocn, TypeInfoVar),
VarName = var_table_entry_name(VarTable, TypeInfoVar),
unexpected($pred,
"can't find slot for type_info var " ++ VarName)
)
),
list.map(FindLocn, TypeInfoLocns, TypeInfoVarLocns),
map.from_corresponding_lists(TypeVars, TypeInfoVarLocns, TypeInfoDataMap).
%-----------------------------------------------------------------------------%
:- pred live_value_type(slot_contents::in, live_value_type::out) is det.
live_value_type(slot_lval(succip), live_value_succip).
live_value_type(slot_lval(hp), live_value_hp).
live_value_type(slot_lval(maxfr), live_value_maxfr).
live_value_type(slot_lval(curfr), live_value_curfr).
live_value_type(slot_lval(succfr_slot(_)), live_value_unwanted).
live_value_type(slot_lval(prevfr_slot(_)), live_value_unwanted).
live_value_type(slot_lval(redofr_slot(_)), live_value_unwanted).
live_value_type(slot_lval(redoip_slot(_)), live_value_unwanted).
live_value_type(slot_lval(succip_slot(_)), live_value_unwanted).
live_value_type(slot_lval(sp), live_value_unwanted).
live_value_type(slot_lval(parent_sp), live_value_unwanted).
live_value_type(slot_lval(lvar(_)), live_value_unwanted).
live_value_type(slot_lval(field(_, _, _)), live_value_unwanted).
live_value_type(slot_lval(temp(_, _)), live_value_unwanted).
live_value_type(slot_lval(reg(_, _)), live_value_unwanted).
live_value_type(slot_lval(stackvar(_)), live_value_unwanted).
live_value_type(slot_lval(parent_stackvar(_)), live_value_unwanted).
live_value_type(slot_lval(framevar(_)), live_value_unwanted).
live_value_type(slot_lval(double_stackvar(_, _)), live_value_unwanted).
live_value_type(slot_lval(mem_ref(_)), live_value_unwanted). % XXX
live_value_type(slot_lval(global_var_ref(_)), live_value_unwanted).
live_value_type(slot_success_record, live_value_unwanted).
live_value_type(slot_ticket, live_value_unwanted).
% XXX we may need to modify this, if the GC is going to garbage-collect
% the trail.
live_value_type(slot_ticket_counter, live_value_unwanted).
live_value_type(slot_lookup_disj_cur, live_value_unwanted).
live_value_type(slot_lookup_switch_cur, live_value_unwanted).
live_value_type(slot_lookup_switch_max, live_value_unwanted).
live_value_type(slot_sync_term, live_value_unwanted).
live_value_type(slot_trace_data, live_value_unwanted).
live_value_type(slot_region_ite, live_value_region_ite).
live_value_type(slot_region_disj, live_value_region_disj).
live_value_type(slot_region_commit, live_value_region_commit).
%-----------------------------------------------------------------------------%
:- end_module ll_backend.continuation_info.
%-----------------------------------------------------------------------------%