mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-21 12:23:44 +00:00
If a module has two or more import_module or use_module declarations
for the same module, (typically, but not always, one being in its interface
and one in its implementation), generate an informational message about
each redundant declaration if --warn-unused-imports is enabled.
compiler/hlds_module.m:
We used to record the set of imported/used modules, and the set of
modules imported/used in the interface of the current module. However,
these sets
- did not record the distinction between imports and uses;
- did not allow distinction between single and multiple imports/uses;
- did not record the locations of the imports/uses.
The first distinction was needed only by module_qual.m, which *did*
pay attention to it; the other two were not needed at all.
To generate messages for imports/uses shadowing other imports/uses,
we need all three, so change the data structure storing such information
for *direct* imports to one that records all three of the above kinds
of information. (For imports made by read-in interface and optimization
files, the old set of modules approach is fine, and this diff leaves
the set of thus *indirectly* imported module names alone.)
compiler/unused_imports.m:
Use the extra information now available to generate a
severity_informational message about any import or use that is made
redundant by an earlier, more general import or use.
Fix two bugs in the code that generated warnings for just plain unused
modules.
(1) It did not consider that a use of the builtin type char justified
an import of char.m, but without that import, the type is not visible.
(2) It scanned cons_ids in goals in procedure bodies, but did not scan
cons_ids that have been put into the const_struct_db. (I did not update
the code here when I added the const_struct_db.)
Also, add a (hopefully temporary) workaround for a bug in
make_hlds_passes.m, which is noted below.
However, there are at least three problems that prevent us from enabling
--warn-unused-imports by default.
(1) In some places, the import of a module is used only by clauses for
a predicate that also has foreign procs. When compiled in a grade that
selects one of those foreign_procs as the implementation of the predicate,
the clauses are discarded *without* being added to the HLDS at all.
This leads unused_imports.m to generate an uncalled-for warning in such
cases. To fix this, we would need to preserve the Mercury clauses for
*all* predicates, even those with foreign procs, and do all the semantic
checks on them before throwing them away. (I tried to do this once, and
failed, but the task should be easier after the item list change.)
(2) We have two pieces of code to generate import warnings. The one in
unused_imports.m operates on the HLDS after type and mode checking,
while module_qual.m operates on the parse tree before the creation of
the HLDS. The former is more powerful, since it knows e.g. what types and
modes are used in the bodies of predicates, and hence can generate warnings
about an import being unused *anywhere* in a module, as opposed to just
unused in its interface.
If --warn-unused-imports is enabled, we will get two separate set of
reports about an interface import being unused in the interface,
*unless* we get a type or mode error, in which case unused_imports.m
won't be invoked. But in case we do get such errors, we don't want to
throw away the warnings from module_qual.m. We could store them and
throw them away only after we know we won't need them, or just get
the two modules to generate identical error_specs for each warning,
so that the sort_and_remove_dups of the error specs will do the
throwing away for us for free, if we get that far.
(3) The valid/bug100.m test case was added as a regression test for a bug
that was fixed in module_qual.m. However the bug is still present in
unused_imports.m.
compiler/make_hlds_passes.m:
Give hlds_module.m the extra information it now needs for each item_avail.
Add an XXX for a bug that cannot be fixed right now: the setting of
the status of abstract instances to abstract_imported. (The "abstract"
part is correct; the "imported" part may not be.)
compiler/intermod.m:
compiler/try_expand.m:
compiler/xml_documentation.m:
Conform to the change in hlds_module.m.
compiler/module_qual.m:
Update the documentation of the relationship of this module
with unused_imports.m.
compiler/hlds_data.m:
Document a problem with the status of instance definitions.
compiler/hlds_out_module.m:
Update the code that prints out the module_info to conform to the change
to hlds_module.m.
Print status information about instances, which was needed to diagnose
one of the bugs in unused_imports.m. Format the output for instances
nicer.
compiler/prog_item.m:
Add a convenience predicate.
compiler/prog_data.m:
Remove a type synonym that makes things harder to understand, not easier.
compiler/modules.m:
Delete an XXX that asks for the feature this diff implements.
Add another XXX about how that feature could be improved.
compiler/Mercury.options.m:
Add some more modules to the list of modules on which the compiler
should be invoked with --no-warn-unused-imports.
compiler/*.m:
library/*.m:
mdbcomp/*.m:
browser/*.m:
deep_profiler/*.m:
mfilterjavac/*.m:
Delete unneeded imports. Many of these shadow other imports, and some
are just plain unneeded, as shown by --warn-unused-imports. In a few
modules, there were a *lot* of unneeded imports, but most had just
one or two.
In a few cases, removing an import from a module, because it *itself*
does not need it, required adding that same import to those of its
submodules which *do* need it.
In a few cases, conform to other changes above.
tests/invalid/Mercury.options:
Test the generation of messages about import shadowing on the existing
import_in_parent.m test case (although it was also tested very thoroughly
when giving me the information needed for the deletion of all the
unneeded imports above).
tests/*/*.{m,*exp}:
Delete unneeded imports, and update any expected error messages
to expect the now-smaller line numbers.
2623 lines
104 KiB
Mathematica
2623 lines
104 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 1997-2012 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_layout.m.
|
|
% Main authors: trd, zs.
|
|
%
|
|
% This module generates label, procedure, module and closure layout structures
|
|
% for code in the current module for the LLDS backend. Layout structures are
|
|
% used by the parts of the runtime system that need to look at the stacks
|
|
% (and sometimes the registers) and make sense of their contents. The parts
|
|
% of the runtime system that need to do this include exception handling,
|
|
% the debugger, and (eventually) the accurate garbage collector.
|
|
%
|
|
% The tables we generate are mostly of (Mercury) types defined in layout.m,
|
|
% which are turned into C code (global variable declarations and
|
|
% initializations) by layout_out.m.
|
|
%
|
|
% The C types of the structures we generate are defined and documented in
|
|
% runtime/mercury_stack_layout.h.
|
|
%
|
|
% TODO: Handle the parent_sp register and parent stack variables.
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module ll_backend.stack_layout.
|
|
:- interface.
|
|
|
|
:- import_module hlds.hlds_goal.
|
|
:- import_module hlds.hlds_module.
|
|
:- import_module hlds.hlds_pred.
|
|
:- import_module ll_backend.continuation_info.
|
|
:- import_module ll_backend.global_data.
|
|
:- import_module ll_backend.layout.
|
|
:- import_module ll_backend.llds.
|
|
:- import_module ll_backend.prog_rep.
|
|
:- import_module mdbcomp.prim_data.
|
|
:- import_module mdbcomp.program_representation.
|
|
:- import_module mdbcomp.sym_name.
|
|
:- import_module parse_tree.prog_data.
|
|
|
|
:- import_module assoc_list.
|
|
:- import_module list.
|
|
:- import_module map.
|
|
:- import_module maybe.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Process all the continuation information stored in the HLDS,
|
|
% converting it into LLDS data structures.
|
|
%
|
|
:- pred generate_llds_layout_data(module_info::in,
|
|
global_data::in, global_data::out,
|
|
list(rval)::out, list(int)::out, list(int)::out, list(int)::out,
|
|
list(maybe(int))::out, list(user_event_data)::out,
|
|
list(label_layout_no_vars)::out,
|
|
list(label_layout_short_vars)::out, list(label_layout_long_vars)::out,
|
|
map(label, layout_slot_name)::out, map(label, data_id)::out,
|
|
list(call_site_static_data)::out, list(coverage_point_info)::out,
|
|
list(proc_layout_proc_static)::out,
|
|
list(int)::out, list(int)::out, list(int)::out,
|
|
list(table_io_entry_data)::out, map(pred_proc_id, layout_slot_name)::out,
|
|
list(layout_slot_name)::out, list(proc_layout_exec_trace)::out,
|
|
list(proc_layout_data)::out, list(module_layout_data)::out) is det.
|
|
|
|
:- pred construct_closure_layout(proc_label::in, int::in,
|
|
closure_layout_info::in, proc_label::in, module_name::in,
|
|
string::in, int::in, pred_origin::in, string::in,
|
|
static_cell_info::in, static_cell_info::out,
|
|
list(typed_rval)::out, closure_proc_id_data::out) is det.
|
|
|
|
:- pred convert_table_arg_info(table_arg_infos::in, int::out,
|
|
rval::out, rval::out, static_cell_info::in, static_cell_info::out) is det.
|
|
|
|
% Construct a representation of a variable location as a 32-bit
|
|
% integer.
|
|
%
|
|
:- pred represent_locn_as_int(layout_locn::in, int::out) is det.
|
|
|
|
% Construct a representation of the interface determinism of a procedure.
|
|
%
|
|
:- pred represent_determinism_rval(determinism::in, rval::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred compute_var_number_map(list(prog_var)::in, prog_varset::in,
|
|
assoc_list(int, internal_layout_info)::in, hlds_goal::in,
|
|
var_num_map::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module backend_libs.proc_label.
|
|
:- import_module backend_libs.rtti.
|
|
:- import_module check_hlds.type_util.
|
|
:- import_module hlds.code_model.
|
|
:- import_module hlds.goal_util.
|
|
:- import_module hlds.hlds_llds.
|
|
:- import_module hlds.hlds_rtti.
|
|
:- import_module hlds.vartypes.
|
|
:- import_module libs.globals.
|
|
:- import_module libs.options.
|
|
:- import_module libs.trace_params.
|
|
:- import_module ll_backend.layout_out.
|
|
:- import_module ll_backend.ll_pseudo_type_info.
|
|
:- import_module ll_backend.prog_rep_tables.
|
|
:- import_module ll_backend.trace_gen.
|
|
:- import_module mdbcomp.goal_path.
|
|
:- import_module mdbcomp.rtti_access.
|
|
:- import_module parse_tree.prog_event.
|
|
:- import_module parse_tree.set_of_var.
|
|
|
|
:- import_module bool.
|
|
:- import_module cord.
|
|
:- import_module counter.
|
|
:- import_module int.
|
|
:- import_module pair.
|
|
:- import_module require.
|
|
:- import_module set.
|
|
:- import_module term.
|
|
:- import_module varset.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
generate_llds_layout_data(ModuleInfo, !GlobalData,
|
|
PseudoTypeInfoRvals, HLDSVarNums, ShortLocns, LongLocns,
|
|
UserEventVarNums, UserEvents,
|
|
NoVarLabelLayouts, SVarLabelLayouts, LVarLabelLayouts,
|
|
InternalLabelToLayoutMap, ProcLabelToLayoutMap,
|
|
CallSiteStatics, CoveragePoints, ProcStatics,
|
|
ProcHeadVarNums, ProcVarNames, ProcBodyBytecodes,
|
|
TableIoEntries, TableIoEntryMap, ProcEventLayouts, ExecTraces,
|
|
ProcLayouts, ModuleLayouts) :-
|
|
|
|
Params = init_stack_layout_params(ModuleInfo),
|
|
map.init(LabelTables0),
|
|
StringTable0 = init_string_table_info,
|
|
TypeTable0 = init_type_table_info,
|
|
global_data_get_static_cell_info(!.GlobalData, StaticCellInfo0),
|
|
LabelLayoutInfo0 = init_label_layouts_info,
|
|
ProcLayoutInfo0 = init_proc_layouts_info,
|
|
|
|
global_data_get_all_proc_layouts(!.GlobalData, ProcLayoutList),
|
|
list.foldl6(construct_proc_and_label_layouts_for_proc(Params),
|
|
ProcLayoutList,
|
|
LabelTables0, LabelTables,
|
|
StringTable0, StringTable,
|
|
TypeTable0, TypeTable,
|
|
StaticCellInfo0, StaticCellInfo1,
|
|
LabelLayoutInfo0, LabelLayoutInfo,
|
|
ProcLayoutInfo0, ProcLayoutInfo),
|
|
|
|
LabelsCounter = LabelLayoutInfo ^ lli_label_counter,
|
|
counter.allocate(NumLabels, LabelsCounter, _),
|
|
|
|
RevCallSiteStatics =
|
|
ProcLayoutInfo ^ pli_proc_statics ^ psi_rev_call_sites,
|
|
RevCoveragePoints =
|
|
ProcLayoutInfo ^ pli_proc_statics ^ psi_rev_coverage_points,
|
|
RevProcStatics =
|
|
ProcLayoutInfo ^ pli_proc_statics ^ psi_rev_proc_statics,
|
|
list.reverse(RevCallSiteStatics, CallSiteStatics),
|
|
list.reverse(RevCoveragePoints, CoveragePoints),
|
|
list.reverse(RevProcStatics, ProcStatics),
|
|
|
|
RevProcHeadVarNums =
|
|
ProcLayoutInfo ^ pli_exec_traces ^ eti_rev_proc_head_var_nums,
|
|
RevProcVarNames =
|
|
ProcLayoutInfo ^ pli_exec_traces ^ eti_rev_proc_var_names,
|
|
RevProcEventLayouts =
|
|
ProcLayoutInfo ^ pli_exec_traces ^ eti_rev_proc_event_layouts,
|
|
RevTableIoEntries =
|
|
ProcLayoutInfo ^ pli_exec_traces ^ eti_rev_table_io_entry_datas,
|
|
RevExecTraces =
|
|
ProcLayoutInfo ^ pli_exec_traces ^ eti_rev_exec_traces,
|
|
list.reverse(RevProcHeadVarNums, ProcHeadVarNums),
|
|
list.reverse(RevProcVarNames, ProcVarNames),
|
|
list.reverse(RevProcEventLayouts, ProcEventLayouts),
|
|
list.reverse(RevTableIoEntries, TableIoEntries),
|
|
list.reverse(RevExecTraces, ExecTraces),
|
|
|
|
TableIoEntryMap =
|
|
ProcLayoutInfo ^ pli_exec_traces ^ eti_table_io_entry_map,
|
|
|
|
RevProcBodyBytecodes = ProcLayoutInfo ^ pli_rev_proc_bytes,
|
|
RevProcLayouts = ProcLayoutInfo ^ pli_rev_proc_layouts,
|
|
RevProcLayoutNames = ProcLayoutInfo ^ pli_rev_proc_layout_names,
|
|
list.reverse(RevProcBodyBytecodes, ProcBodyBytecodes),
|
|
list.reverse(RevProcLayouts, ProcLayouts),
|
|
list.reverse(RevProcLayoutNames, ProcLayoutNames),
|
|
|
|
RevPseudoTypeInfoRvals = LabelLayoutInfo ^ lli_rev_ptis,
|
|
RevLongLocns = LabelLayoutInfo ^ lli_rev_long_locns,
|
|
RevShortLocns = LabelLayoutInfo ^ lli_rev_short_locns,
|
|
RevHLDSVarNums = LabelLayoutInfo ^ lli_rev_hlds_var_nums,
|
|
list.reverse(RevPseudoTypeInfoRvals, PseudoTypeInfoRvals),
|
|
list.reverse(RevLongLocns, LongLocns),
|
|
list.reverse(RevShortLocns, ShortLocns),
|
|
list.reverse(RevHLDSVarNums, HLDSVarNums),
|
|
|
|
UserEventVarNumsCord = LabelLayoutInfo ^ lli_user_event_var_nums,
|
|
UserEventsCord = LabelLayoutInfo ^ lli_user_events,
|
|
UserEventVarNums = cord.list(UserEventVarNumsCord),
|
|
UserEvents = cord.list(UserEventsCord),
|
|
|
|
RevNoVarLabelLayouts = LabelLayoutInfo ^ lli_rev_no_var_label_layouts,
|
|
RevSVarLabelLayouts = LabelLayoutInfo ^ lli_rev_svar_label_layouts,
|
|
RevLVarLabelLayouts = LabelLayoutInfo ^ lli_rev_lvar_label_layouts,
|
|
list.reverse(RevNoVarLabelLayouts, NoVarLabelLayouts),
|
|
list.reverse(RevSVarLabelLayouts, SVarLabelLayouts),
|
|
list.reverse(RevLVarLabelLayouts, LVarLabelLayouts),
|
|
|
|
InternalLabelToLayoutMap = LabelLayoutInfo ^ lli_i_label_to_layout_map,
|
|
ProcLabelToLayoutMap = ProcLayoutInfo ^ pli_p_label_to_layout_map,
|
|
|
|
DeepProfiling = Params ^ slp_deep_profiling,
|
|
(
|
|
DeepProfiling = yes,
|
|
module_info_get_oisu_map(ModuleInfo, OISUMap),
|
|
map.to_assoc_list(OISUMap, OISUPairs),
|
|
encode_oisu_type_procs(ModuleInfo, OISUPairs,
|
|
NumOISUTypes, OISUBytesCord),
|
|
OISUBytes0 = cord.list(OISUBytesCord),
|
|
(
|
|
OISUBytes0 = [],
|
|
OISUBytes = []
|
|
;
|
|
OISUBytes0 = [_ | _],
|
|
encode_int32_det(list.length(OISUBytes0) + 4, OISULimitBytes),
|
|
OISUBytes = OISULimitBytes ++ OISUBytes0
|
|
),
|
|
|
|
get_type_table_contents(TypeTable, NumTypes, TypeBytes0),
|
|
(
|
|
TypeBytes0 = [],
|
|
TypeBytes = []
|
|
;
|
|
TypeBytes0 = [_ | _],
|
|
encode_int32_det(list.length(TypeBytes0) + 4, TypeTableSizeBytes),
|
|
TypeBytes = TypeTableSizeBytes ++ TypeBytes0
|
|
),
|
|
DeepProfInfo = module_layout_deep_prof(NumOISUTypes, OISUBytes,
|
|
NumTypes, TypeBytes),
|
|
MaybeDeepProfInfo = yes(DeepProfInfo)
|
|
;
|
|
DeepProfiling = no,
|
|
MaybeDeepProfInfo = no
|
|
),
|
|
|
|
TraceLayout = Params ^ slp_trace_stack_layout,
|
|
(
|
|
TraceLayout = yes,
|
|
RttiLineNumbers = Params ^ slp_rtti_line_numbers,
|
|
(
|
|
RttiLineNumbers = yes,
|
|
EffLabelTables = LabelTables
|
|
;
|
|
RttiLineNumbers = no,
|
|
map.init(EffLabelTables)
|
|
),
|
|
format_label_tables(EffLabelTables, SourceFileLayouts),
|
|
TraceSuppress = Params ^ slp_trace_suppress,
|
|
SuppressedEvents = encode_suppressed_events(TraceSuppress),
|
|
(
|
|
UserEvents = [],
|
|
HasUserEvent = no
|
|
;
|
|
UserEvents = [_ | _],
|
|
HasUserEvent = yes
|
|
),
|
|
(
|
|
HasUserEvent = no,
|
|
MaybeEventSet = no,
|
|
StaticCellInfo = StaticCellInfo1
|
|
;
|
|
HasUserEvent = yes,
|
|
module_info_get_event_set(ModuleInfo, EventSet),
|
|
EventSetData = derive_event_set_data(EventSet),
|
|
list.foldl2(build_event_arg_type_info_map,
|
|
EventSetData ^ event_set_data_specs,
|
|
map.init, EventArgTypeInfoMap,
|
|
StaticCellInfo1, StaticCellInfo),
|
|
EventSetLayoutData = event_set_layout_data(EventSetData,
|
|
EventArgTypeInfoMap),
|
|
MaybeEventSet = yes(EventSetLayoutData)
|
|
),
|
|
|
|
TraceLevel = Params ^ slp_trace_level,
|
|
DebugInfo = module_layout_debug(ProcLayoutNames, SourceFileLayouts,
|
|
TraceLevel, SuppressedEvents, NumLabels, MaybeEventSet),
|
|
MaybeDebugInfo = yes(DebugInfo)
|
|
;
|
|
TraceLayout = no,
|
|
StaticCellInfo = StaticCellInfo1,
|
|
MaybeDebugInfo = no
|
|
),
|
|
(
|
|
MaybeDeepProfInfo = no,
|
|
MaybeDebugInfo = no
|
|
->
|
|
ModuleLayouts = []
|
|
;
|
|
module_info_get_name(ModuleInfo, ModuleName),
|
|
get_string_table_contents(StringTable, StringList, StringTableSize),
|
|
StringTableContents = string_with_0s(StringList),
|
|
ModuleLayout = module_layout_data(ModuleName,
|
|
StringTableSize, StringTableContents,
|
|
MaybeDeepProfInfo, MaybeDebugInfo),
|
|
ModuleLayouts = [ModuleLayout]
|
|
),
|
|
global_data_set_static_cell_info(StaticCellInfo, !GlobalData).
|
|
|
|
:- pred valid_proc_layout(proc_layout_info::in) is semidet.
|
|
|
|
valid_proc_layout(ProcLayoutInfo) :-
|
|
EntryLabel = ProcLayoutInfo ^ pli_entry_label,
|
|
ProcLabel = get_proc_label(EntryLabel),
|
|
(
|
|
ProcLabel = ordinary_proc_label(_, _, DeclModule, Name, Arity, _),
|
|
\+ no_type_info_builtin(DeclModule, Name, Arity)
|
|
;
|
|
ProcLabel = special_proc_label(_, _, _, _, _, _)
|
|
).
|
|
|
|
:- pred build_event_arg_type_info_map(event_spec::in,
|
|
map(int, rval)::in, map(int, rval)::out,
|
|
static_cell_info::in, static_cell_info::out) is det.
|
|
|
|
build_event_arg_type_info_map(EventSpec, !EventArgTypeInfoMap,
|
|
!StaticCellInfo) :-
|
|
EventNumber = EventSpec ^ event_spec_num,
|
|
Attrs = EventSpec ^ event_spec_attrs,
|
|
list.map_foldl(build_event_arg_type_info, Attrs, RvalsAndTypes,
|
|
!StaticCellInfo),
|
|
add_scalar_static_cell(RvalsAndTypes, TypesDataAddr, !StaticCellInfo),
|
|
Rval = const(llconst_data_addr(TypesDataAddr, no)),
|
|
map.det_insert(EventNumber, Rval, !EventArgTypeInfoMap).
|
|
|
|
:- pred build_event_arg_type_info(event_attribute::in, typed_rval::out,
|
|
static_cell_info::in, static_cell_info::out) is det.
|
|
|
|
build_event_arg_type_info(Attr, TypeRvalAndType, !StaticCellInfo) :-
|
|
Type = Attr ^ attr_type,
|
|
ExistQTvars = [],
|
|
NumUnivQTvars = -1,
|
|
ll_pseudo_type_info.construct_typed_llds_pseudo_type_info(Type,
|
|
NumUnivQTvars, ExistQTvars, !StaticCellInfo, TypeRval, TypeRvalType),
|
|
TypeRvalAndType = typed_rval(TypeRval, TypeRvalType).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred format_label_tables(map(string, file_label_table)::in,
|
|
list(file_layout_data)::out) is det.
|
|
|
|
format_label_tables(LabelTableMap, SourceFileLayouts) :-
|
|
map.to_assoc_list(LabelTableMap, LabelTableList),
|
|
list.map(format_label_table, LabelTableList, SourceFileLayouts).
|
|
|
|
:- pred format_label_table(pair(string, file_label_table)::in,
|
|
file_layout_data::out) is det.
|
|
|
|
format_label_table(FileName - LineNoMap, FileLayoutData) :-
|
|
% This step should produce a list ordered on line numbers.
|
|
map.to_assoc_list(LineNoMap, LineNoList),
|
|
% And this step should preserve that order.
|
|
flatten_label_table(LineNoList, [], FlatLineNoList),
|
|
Filter = (pred(LineNoInfo::in, FilteredLineNoInfo::out) is det :-
|
|
LineNoInfo = LineNo - (Label - _IsReturn),
|
|
FilteredLineNoInfo = LineNo - Label
|
|
),
|
|
list.map(Filter, FlatLineNoList, FilteredList),
|
|
FileLayoutData = file_layout_data(FileName, FilteredList).
|
|
|
|
:- pred flatten_label_table(assoc_list(int, list(line_no_info))::in,
|
|
assoc_list(int, line_no_info)::in,
|
|
assoc_list(int, line_no_info)::out) is det.
|
|
|
|
flatten_label_table([], RevList, List) :-
|
|
list.reverse(RevList, List).
|
|
flatten_label_table([LineNo - LinesInfos | Lines], RevList0, List) :-
|
|
list.foldl(add_line_no(LineNo), LinesInfos, RevList0, RevList1),
|
|
flatten_label_table(Lines, RevList1, List).
|
|
|
|
:- pred add_line_no(int::in, line_no_info::in,
|
|
assoc_list(int, line_no_info)::in,
|
|
assoc_list(int, line_no_info)::out) is det.
|
|
|
|
add_line_no(LineNo, LineInfo, RevList0, RevList) :-
|
|
RevList = [LineNo - LineInfo | RevList0].
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% The per-sourcefile label table maps line numbers to the list of
|
|
% labels that correspond to that line. Each label is accompanied
|
|
% by a flag that says whether the label is the return site of a call
|
|
% or not, and if it is, whether the called procedure is known.
|
|
%
|
|
:- type is_label_return
|
|
---> known_callee(label)
|
|
; unknown_callee
|
|
; not_a_return.
|
|
|
|
:- type line_no_info == pair(layout_slot_name, is_label_return).
|
|
|
|
:- type file_label_table == map(int, list(line_no_info)).
|
|
|
|
:- type label_tables == map(string, file_label_table).
|
|
|
|
% Construct the layouts that concern a single procedure: the procedure
|
|
% layout and the layouts of the labels inside that procedure. Also update
|
|
% the module-wide label table with the labels defined in this procedure.
|
|
%
|
|
:- pred construct_proc_and_label_layouts_for_proc(stack_layout_params::in,
|
|
proc_layout_info::in,
|
|
label_tables::in, label_tables::out,
|
|
string_table_info::in, string_table_info::out,
|
|
type_table_info::in, type_table_info::out,
|
|
static_cell_info::in, static_cell_info::out,
|
|
label_layouts_info::in, label_layouts_info::out,
|
|
proc_layouts_info::in, proc_layouts_info::out) is det.
|
|
|
|
construct_proc_and_label_layouts_for_proc(Params, PLI, !LabelTables,
|
|
!StringTable, !TypeTable, !StaticCellInfo,
|
|
!LabelLayoutInfo, !ProcLayoutInfo) :-
|
|
PLI = proc_layout_info(RttiProcLabel, EntryLabel,
|
|
_Detism, _StackSlots, _SuccipLoc, _EvalMethod, _EffTraceLevel,
|
|
_MaybeCallLabel, _MaxTraceRegR, _MaxTraceRegF, HeadVars, _ArgModes,
|
|
Goal, _NeedGoalRep, _InstMap,
|
|
_TraceSlotInfo, ForceProcIdLayout, VarSet, _VarTypes,
|
|
InternalMap, MaybeTableIoEntry, _NeedsAllNames, _OISUKindFors,
|
|
_MaybeDeepProfInfo),
|
|
map.to_assoc_list(InternalMap, Internals),
|
|
compute_var_number_map(HeadVars, VarSet, Internals, Goal, VarNumMap),
|
|
|
|
ProcLabel = get_proc_label(EntryLabel),
|
|
bool.or(Params ^ slp_procid_stack_layout, ForceProcIdLayout, ProcIdLayout),
|
|
(
|
|
( ProcIdLayout = yes
|
|
; MaybeTableIoEntry = yes(_)
|
|
)
|
|
->
|
|
UserOrUci = proc_label_user_or_uci(ProcLabel),
|
|
Kind = proc_layout_proc_id(UserOrUci)
|
|
;
|
|
Kind = proc_layout_traversal
|
|
),
|
|
ProcLayoutName = proc_layout(RttiProcLabel, Kind),
|
|
(
|
|
( Params ^ slp_agc_stack_layout = yes
|
|
; Params ^ slp_trace_stack_layout = yes
|
|
),
|
|
valid_proc_layout(PLI)
|
|
->
|
|
list.map_foldl3(
|
|
construct_internal_layout(Params, ProcLabel, ProcLayoutName,
|
|
VarNumMap),
|
|
Internals, InternalLabelInfos,
|
|
!StringTable, !StaticCellInfo, !LabelLayoutInfo)
|
|
;
|
|
InternalLabelInfos = []
|
|
),
|
|
list.foldl(update_label_table, InternalLabelInfos, !LabelTables),
|
|
construct_proc_layout(Params, PLI, ProcLayoutName, Kind,
|
|
InternalLabelInfos, VarNumMap, !.LabelLayoutInfo,
|
|
!StringTable, !TypeTable, !StaticCellInfo, !ProcLayoutInfo).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- type internal_label_info
|
|
---> internal_label_info(
|
|
containing_proc :: proc_label,
|
|
label_num_in_proc :: int,
|
|
maybe_has_var_info :: label_vars,
|
|
slot_in_array :: int,
|
|
internal_layout_info :: internal_layout_info
|
|
).
|
|
|
|
% Add the given label layout to the module-wide label tables.
|
|
%
|
|
:- pred update_label_table(internal_label_info::in,
|
|
map(string, file_label_table)::in, map(string, file_label_table)::out)
|
|
is det.
|
|
|
|
update_label_table(InternalLabelInfo, !LabelTables) :-
|
|
InternalLabelInfo = internal_label_info(_ProcLabel, _LabelNum, LabelVars,
|
|
Slot, InternalInfo),
|
|
InternalInfo = internal_layout_info(Port, _, Return),
|
|
(
|
|
Return = yes(return_layout_info(TargetsContexts, _)),
|
|
find_valid_return_context(TargetsContexts, Target, Context, _GoalPath)
|
|
->
|
|
( Target = code_label(TargetLabel) ->
|
|
IsReturn = known_callee(TargetLabel)
|
|
;
|
|
IsReturn = unknown_callee
|
|
),
|
|
update_label_table_2(LabelVars, Slot, Context, IsReturn, !LabelTables)
|
|
;
|
|
Port = yes(trace_port_layout_info(Context, _, _, _, _, _)),
|
|
context_is_valid(Context)
|
|
->
|
|
update_label_table_2(LabelVars, Slot, Context, not_a_return,
|
|
!LabelTables)
|
|
;
|
|
true
|
|
).
|
|
|
|
:- pred update_label_table_2(label_vars::in, int::in, context::in,
|
|
is_label_return::in,
|
|
map(string, file_label_table)::in, map(string, file_label_table)::out)
|
|
is det.
|
|
|
|
update_label_table_2(LabelVars, Slot, Context, IsReturn, !LabelTables) :-
|
|
term.context_file(Context, File),
|
|
term.context_line(Context, Line),
|
|
( map.search(!.LabelTables, File, LabelTable0) ->
|
|
LabelLayout = layout_slot(label_layout_array(LabelVars), Slot),
|
|
( map.search(LabelTable0, Line, LineInfo0) ->
|
|
LineInfo = [LabelLayout - IsReturn | LineInfo0],
|
|
map.det_update(Line, LineInfo, LabelTable0, LabelTable),
|
|
map.det_update(File, LabelTable, !LabelTables)
|
|
;
|
|
LineInfo = [LabelLayout - IsReturn],
|
|
map.det_insert(Line, LineInfo, LabelTable0, LabelTable),
|
|
map.det_update(File, LabelTable, !LabelTables)
|
|
)
|
|
;
|
|
( context_is_valid(Context) ->
|
|
LabelLayout = layout_slot(label_layout_array(LabelVars), Slot),
|
|
LineInfo = [LabelLayout - IsReturn],
|
|
LabelTable = map.singleton(Line, LineInfo),
|
|
map.det_insert(File, LabelTable, !LabelTables)
|
|
;
|
|
% We don't have a valid context for this label,
|
|
% so we don't enter it into any tables.
|
|
true
|
|
)
|
|
).
|
|
|
|
:- pred find_valid_return_context(
|
|
assoc_list(code_addr, pair(prog_context, forward_goal_path))::in,
|
|
code_addr::out, prog_context::out, forward_goal_path::out) is semidet.
|
|
|
|
find_valid_return_context([TargetContext | TargetContexts],
|
|
ValidTarget, ValidContext, ValidGoalPath) :-
|
|
TargetContext = Target - (Context - GoalPath),
|
|
( context_is_valid(Context) ->
|
|
ValidTarget = Target,
|
|
ValidContext = Context,
|
|
ValidGoalPath = GoalPath
|
|
;
|
|
find_valid_return_context(TargetContexts, ValidTarget, ValidContext,
|
|
ValidGoalPath)
|
|
).
|
|
|
|
:- pred context_is_valid(prog_context::in) is semidet.
|
|
|
|
context_is_valid(Context) :-
|
|
term.context_file(Context, File),
|
|
term.context_line(Context, Line),
|
|
File \= "",
|
|
Line > 0.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Construct proc layouts.
|
|
%
|
|
|
|
% Construct a procedure-specific layout.
|
|
%
|
|
:- pred construct_proc_layout(stack_layout_params::in, proc_layout_info::in,
|
|
layout_name::in, proc_layout_kind::in, list(internal_label_info)::in,
|
|
var_num_map::in, label_layouts_info::in,
|
|
string_table_info::in, string_table_info::out,
|
|
type_table_info::in, type_table_info::out,
|
|
static_cell_info::in, static_cell_info::out,
|
|
proc_layouts_info::in, proc_layouts_info::out) is det.
|
|
|
|
construct_proc_layout(Params, PLI, ProcLayoutName, Kind, InternalLabelInfos,
|
|
VarNumMap, LabelLayoutInfo, !StringTable, !TypeTable,
|
|
!StaticCellInfo, !ProcLayoutInfo) :-
|
|
PLI = proc_layout_info(RttiProcLabel, EntryLabel,
|
|
Detism, StackSlots, SuccipLoc, EvalMethod, EffTraceLevel,
|
|
MaybeCallLabel, MaxTraceRegR, MaxTraceRegF, HeadVars, ArgModes,
|
|
Goal, NeedGoalRep, InstMap,
|
|
TraceSlotInfo, _ForceProcIdLayout, VarSet, VarTypes,
|
|
InternalMap, MaybeTableInfo, NeedsAllNames, OISUKindFors,
|
|
MaybeDeepProfInfo),
|
|
construct_proc_traversal(Params, EntryLabel, Detism, StackSlots,
|
|
SuccipLoc, Traversal),
|
|
PredId = RttiProcLabel ^ rpl_pred_id,
|
|
ProcId = RttiProcLabel ^ rpl_proc_id,
|
|
PredProcId = proc(PredId, ProcId),
|
|
(
|
|
MaybeTableInfo = no,
|
|
MaybeTableSlotName = no
|
|
;
|
|
MaybeTableInfo = yes(TableInfo),
|
|
TableExecTraceInfo0 = !.ProcLayoutInfo ^ pli_exec_traces,
|
|
construct_exec_trace_table_data(PredProcId, ProcLayoutName, TableInfo,
|
|
MaybeTableSlotName, !StaticCellInfo,
|
|
TableExecTraceInfo0, TableExecTraceInfo),
|
|
!ProcLayoutInfo ^ pli_exec_traces := TableExecTraceInfo
|
|
),
|
|
(
|
|
Kind = proc_layout_traversal,
|
|
More = no_proc_id_and_more
|
|
;
|
|
Kind = proc_layout_proc_id(_),
|
|
TraceStackLayout = Params ^ slp_trace_stack_layout,
|
|
(
|
|
TraceStackLayout = yes,
|
|
not map.is_empty(InternalMap),
|
|
valid_proc_layout(PLI)
|
|
->
|
|
ExecTraceInfo0 = !.ProcLayoutInfo ^ pli_exec_traces,
|
|
construct_exec_trace_layout(Params, RttiProcLabel,
|
|
EvalMethod, EffTraceLevel, MaybeCallLabel, MaybeTableSlotName,
|
|
MaxTraceRegR, MaxTraceRegF, HeadVars, ArgModes, TraceSlotInfo,
|
|
VarSet, VarTypes, MaybeTableInfo, NeedsAllNames,
|
|
VarNumMap, InternalLabelInfos, ExecTraceSlotName,
|
|
LabelLayoutInfo, !StringTable,
|
|
ExecTraceInfo0, ExecTraceInfo),
|
|
!ProcLayoutInfo ^ pli_exec_traces := ExecTraceInfo,
|
|
MaybeExecTraceSlotName = yes(ExecTraceSlotName)
|
|
;
|
|
MaybeExecTraceSlotName = no
|
|
),
|
|
ModuleInfo = Params ^ slp_module_info,
|
|
module_info_get_name(ModuleInfo, ModuleName),
|
|
DeepProfiling = Params ^ slp_deep_profiling,
|
|
(
|
|
( NeedGoalRep = trace_needs_body_rep
|
|
; DeepProfiling = yes
|
|
)
|
|
->
|
|
(
|
|
DeepProfiling = yes,
|
|
IncludeVarNameTable = include_var_name_table,
|
|
(
|
|
OISUKindFors = [],
|
|
IncludeVarTypes = do_not_include_var_types
|
|
;
|
|
OISUKindFors = [_ | _],
|
|
IncludeVarTypes = include_var_types
|
|
)
|
|
;
|
|
DeepProfiling = no,
|
|
IncludeVarNameTable = do_not_include_var_name_table,
|
|
IncludeVarTypes = do_not_include_var_types
|
|
),
|
|
|
|
% When the program is compiled with deep profiling, use
|
|
% the version of the procedure saved *before* the deep profiling
|
|
% transformation as the program representation.
|
|
(
|
|
MaybeDeepProfInfo = yes(DeepProfInfo),
|
|
ProcStaticInfo0 = !.ProcLayoutInfo ^ pli_proc_statics,
|
|
construct_proc_static_layout(DeepProfInfo, ProcStaticSlotName,
|
|
ProcStaticInfo0, ProcStaticInfo),
|
|
!ProcLayoutInfo ^ pli_proc_statics := ProcStaticInfo,
|
|
MaybeProcStaticSlotName = yes(ProcStaticSlotName),
|
|
|
|
DeepOriginalBody = DeepProfInfo ^ pdpi_orig_body,
|
|
DeepOriginalBody = deep_original_body(BytecodeBody,
|
|
BytecodeHeadVars, BytecodeInstMap, BytecodeVarTypes,
|
|
BytecodeDetism, BytecodeVarSet),
|
|
compute_var_number_map(BytecodeHeadVars, BytecodeVarSet, [],
|
|
BytecodeBody, BytecodeVarNumMap)
|
|
;
|
|
MaybeDeepProfInfo = no,
|
|
MaybeProcStaticSlotName = no,
|
|
BytecodeHeadVars = HeadVars,
|
|
BytecodeBody = Goal,
|
|
BytecodeInstMap = InstMap,
|
|
BytecodeVarTypes = VarTypes,
|
|
BytecodeDetism = Detism,
|
|
BytecodeVarNumMap = VarNumMap
|
|
),
|
|
represent_proc_as_bytecodes(BytecodeHeadVars, BytecodeBody,
|
|
BytecodeInstMap, BytecodeVarTypes, BytecodeVarNumMap,
|
|
ModuleInfo, IncludeVarNameTable, IncludeVarTypes,
|
|
BytecodeDetism, !StringTable, !TypeTable, ProcBytes),
|
|
|
|
% Calling find_sequence on ProcBytes is very unlikely to find
|
|
% any matching sequences, though there is no reason why we
|
|
% cannot try.
|
|
list.reverse(ProcBytes, RevProcBytes),
|
|
list.length(ProcBytes, NumProcBytes),
|
|
|
|
RevAllProcBytes0 = !.ProcLayoutInfo ^ pli_rev_proc_bytes,
|
|
RevAllProcBytes = RevProcBytes ++ RevAllProcBytes0,
|
|
!ProcLayoutInfo ^ pli_rev_proc_bytes := RevAllProcBytes,
|
|
|
|
NextProcByte0 = !.ProcLayoutInfo ^ pli_next_proc_byte,
|
|
ProcByteSlot = NextProcByte0,
|
|
NextProcByte = NumProcBytes + NextProcByte0,
|
|
!ProcLayoutInfo ^ pli_next_proc_byte := NextProcByte,
|
|
|
|
ProcBytesSlotName = layout_slot(proc_body_bytecodes_array,
|
|
ProcByteSlot),
|
|
MaybeProcBytesSlotName = yes(ProcBytesSlotName)
|
|
;
|
|
(
|
|
MaybeDeepProfInfo = yes(DeepProfInfo),
|
|
ProcStaticInfo0 = !.ProcLayoutInfo ^ pli_proc_statics,
|
|
construct_proc_static_layout(DeepProfInfo, ProcStaticSlotName,
|
|
ProcStaticInfo0, ProcStaticInfo),
|
|
!ProcLayoutInfo ^ pli_proc_statics := ProcStaticInfo,
|
|
MaybeProcStaticSlotName = yes(ProcStaticSlotName)
|
|
;
|
|
MaybeDeepProfInfo = no,
|
|
MaybeProcStaticSlotName = no
|
|
),
|
|
MaybeProcBytesSlotName = no
|
|
),
|
|
ModuleLayoutName = module_layout(ModuleName),
|
|
More = proc_id_and_more(MaybeProcStaticSlotName,
|
|
MaybeExecTraceSlotName, MaybeProcBytesSlotName, ModuleLayoutName)
|
|
),
|
|
|
|
ProcLayout = proc_layout_data(RttiProcLabel, Traversal, More),
|
|
|
|
RevProcLayouts0 = !.ProcLayoutInfo ^ pli_rev_proc_layouts,
|
|
RevProcLayouts = [ProcLayout | RevProcLayouts0],
|
|
!ProcLayoutInfo ^ pli_rev_proc_layouts := RevProcLayouts,
|
|
|
|
RevProcLayoutNames0 = !.ProcLayoutInfo ^ pli_rev_proc_layout_names,
|
|
RevProcLayoutNames = [ProcLayoutName | RevProcLayoutNames0],
|
|
!ProcLayoutInfo ^ pli_rev_proc_layout_names := RevProcLayoutNames,
|
|
|
|
LabelToLayoutMap0 = !.ProcLayoutInfo ^ pli_p_label_to_layout_map,
|
|
map.det_insert(EntryLabel, layout_id(ProcLayoutName),
|
|
LabelToLayoutMap0, LabelToLayoutMap),
|
|
!ProcLayoutInfo ^ pli_p_label_to_layout_map := LabelToLayoutMap.
|
|
|
|
:- pred construct_proc_traversal(stack_layout_params::in, label::in,
|
|
determinism::in, int::in, maybe(int)::in, proc_layout_stack_traversal::out)
|
|
is det.
|
|
|
|
construct_proc_traversal(Params, EntryLabel, Detism, NumStackSlots,
|
|
MaybeSuccipLoc, Traversal) :-
|
|
(
|
|
MaybeSuccipLoc = yes(Location),
|
|
( determinism_components(Detism, _, at_most_many) ->
|
|
SuccipLval = framevar(Location)
|
|
;
|
|
SuccipLval = stackvar(Location)
|
|
),
|
|
represent_locn_as_int(locn_direct(SuccipLval), SuccipInt),
|
|
MaybeSuccipInt = yes(SuccipInt)
|
|
;
|
|
MaybeSuccipLoc = no,
|
|
% Use a dummy location if there is no succip slot on the stack.
|
|
%
|
|
% This case can arise in two circumstances. First, procedures that
|
|
% use the nondet stack have a special slot for the succip, so the
|
|
% succip is not stored in a general purpose slot. Second, procedures
|
|
% that use the det stack but which do not call other procedures
|
|
% do not save the succip on the stack.
|
|
%
|
|
% The tracing system does not care about the location of the saved
|
|
% succip. The accurate garbage collector does. It should know from
|
|
% the determinism that the procedure uses the nondet stack, which
|
|
% takes care of the first possibility above. Procedures that do not
|
|
% call other procedures do not establish resumption points and thus
|
|
% agc is not interested in them. As far as stack dumps go, calling
|
|
% error counts as a call, so any procedure that may call error
|
|
% (directly or indirectly) will have its saved succip location
|
|
% recorded, so the stack dump will work.
|
|
%
|
|
% Future uses of stack layouts will have to have similar constraints.
|
|
MaybeSuccipInt = no
|
|
),
|
|
StaticCodeAddr = Params ^ slp_static_code_addresses,
|
|
(
|
|
StaticCodeAddr = yes,
|
|
MaybeEntryLabel = yes(EntryLabel)
|
|
;
|
|
StaticCodeAddr = no,
|
|
MaybeEntryLabel = no
|
|
),
|
|
Traversal = proc_layout_stack_traversal(MaybeEntryLabel,
|
|
MaybeSuccipInt, NumStackSlots, Detism).
|
|
|
|
:- type proc_layouts_info
|
|
---> proc_layouts_info(
|
|
pli_proc_statics :: proc_statics_info,
|
|
pli_exec_traces :: exec_traces_info,
|
|
|
|
pli_next_proc_byte :: int,
|
|
pli_rev_proc_bytes :: list(int),
|
|
|
|
% The list of proc_layouts in the module.
|
|
pli_rev_proc_layouts :: list(proc_layout_data),
|
|
pli_rev_proc_layout_names :: list(layout_name),
|
|
|
|
% This maps each proc label with a layout
|
|
% to the id of its layout structure.
|
|
pli_p_label_to_layout_map :: map(label, data_id)
|
|
).
|
|
|
|
:- func init_proc_layouts_info = proc_layouts_info.
|
|
|
|
init_proc_layouts_info = Info :-
|
|
ProcStaticInfo = init_proc_statics_info,
|
|
ExecTraceInfo = init_exec_traces_info,
|
|
Info = proc_layouts_info(ProcStaticInfo, ExecTraceInfo,
|
|
0, [], [], [], map.init).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Construct proc static structures.
|
|
%
|
|
|
|
:- pred construct_proc_static_layout(proc_deep_prof_info::in,
|
|
layout_slot_name::out,
|
|
proc_statics_info::in, proc_statics_info::out) is det.
|
|
|
|
construct_proc_static_layout(DeepProfInfo, ProcStaticSlotName,
|
|
!ProcStaticInfo) :-
|
|
DeepProfInfo = proc_deep_prof_info(HLDSProcStatic, DeepExcpSlots,
|
|
_OrigBody),
|
|
HLDSProcStatic = hlds_proc_static(FileName, LineNumber, IsInInterface,
|
|
CallSites, CoveragePoints),
|
|
(
|
|
CallSites = [],
|
|
MaybeCallSitesTuple = no
|
|
;
|
|
CallSites = [_ | _],
|
|
list.reverse(CallSites, RevCallSites),
|
|
list.length(CallSites, NumCallSites),
|
|
|
|
RevAllCallSites0 = !.ProcStaticInfo ^ psi_rev_call_sites,
|
|
RevAllCallSites = RevCallSites ++ RevAllCallSites0,
|
|
!ProcStaticInfo ^ psi_rev_call_sites := RevAllCallSites,
|
|
|
|
NextCallSite0 = !.ProcStaticInfo ^ psi_next_call_site,
|
|
CallSiteSlot = NextCallSite0,
|
|
NextCallSite = NextCallSite0 + NumCallSites,
|
|
!ProcStaticInfo ^ psi_next_call_site := NextCallSite,
|
|
|
|
MaybeCallSitesTuple = yes({CallSiteSlot, NumCallSites})
|
|
),
|
|
(
|
|
CoveragePoints = [],
|
|
MaybeCoveragePointsTuple = no
|
|
;
|
|
CoveragePoints = [_ | _],
|
|
list.reverse(CoveragePoints, RevCoveragePoints),
|
|
list.length(CoveragePoints, NumCoveragePoints),
|
|
|
|
RevAllCoveragePoints0 = !.ProcStaticInfo ^ psi_rev_coverage_points,
|
|
RevAllCoveragePoints = RevCoveragePoints ++ RevAllCoveragePoints0,
|
|
!ProcStaticInfo ^ psi_rev_coverage_points := RevAllCoveragePoints,
|
|
|
|
NextCoveragePoint0 = !.ProcStaticInfo ^ psi_next_coverage_point,
|
|
CoveragePointSlot = NextCoveragePoint0,
|
|
NextCoveragePoint = NextCoveragePoint0 + NumCoveragePoints,
|
|
!ProcStaticInfo ^ psi_next_coverage_point := NextCoveragePoint,
|
|
|
|
MaybeCoveragePointsTuple = yes({CoveragePointSlot, NumCoveragePoints})
|
|
),
|
|
|
|
ProcStatic = proc_layout_proc_static(FileName, LineNumber, IsInInterface,
|
|
DeepExcpSlots, MaybeCallSitesTuple, MaybeCoveragePointsTuple),
|
|
|
|
RevProcStatics0 = !.ProcStaticInfo ^ psi_rev_proc_statics,
|
|
RevProcStatics = [ProcStatic | RevProcStatics0],
|
|
!ProcStaticInfo ^ psi_rev_proc_statics := RevProcStatics,
|
|
|
|
ProcStaticCounter0 = !.ProcStaticInfo ^ psi_next_proc_static,
|
|
counter.allocate(ProcStaticSlot, ProcStaticCounter0, ProcStaticCounter),
|
|
ProcStaticSlotName = layout_slot(proc_static_array, ProcStaticSlot),
|
|
!ProcStaticInfo ^ psi_next_proc_static := ProcStaticCounter.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- type proc_statics_info
|
|
---> proc_statics_info(
|
|
% The arrays that hold (components of) proc static structures.
|
|
psi_next_call_site :: int,
|
|
psi_next_coverage_point :: int,
|
|
psi_next_proc_static :: counter,
|
|
|
|
psi_rev_call_sites :: list(call_site_static_data),
|
|
psi_rev_coverage_points :: list(coverage_point_info),
|
|
psi_rev_proc_statics :: list(proc_layout_proc_static)
|
|
).
|
|
|
|
:- func init_proc_statics_info = proc_statics_info.
|
|
|
|
init_proc_statics_info = Info :-
|
|
Info = proc_statics_info(0, 0, counter.init(0), [], [], []).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Construct exec trace structures.
|
|
%
|
|
|
|
:- pred construct_exec_trace_layout(stack_layout_params::in,
|
|
rtti_proc_label::in, eval_method::in, trace_level::in, maybe(label)::in,
|
|
maybe(layout_slot_name)::in, int::in, int::in,
|
|
list(prog_var)::in, list(mer_mode)::in,
|
|
trace_slot_info::in, prog_varset::in, vartypes::in,
|
|
maybe(proc_layout_table_info)::in, bool::in, var_num_map::in,
|
|
list(internal_label_info)::in, layout_slot_name::out,
|
|
label_layouts_info::in,
|
|
string_table_info::in, string_table_info::out,
|
|
exec_traces_info::in, exec_traces_info::out) is det.
|
|
|
|
construct_exec_trace_layout(Params, RttiProcLabel, EvalMethod,
|
|
EffTraceLevel, MaybeCallLabel, MaybeTableSlotName,
|
|
MaxTraceRegR, MaxTraceRegF,
|
|
HeadVars, ArgModes, TraceSlotInfo, _VarSet, VarTypes, MaybeTableInfo,
|
|
NeedsAllNames, VarNumMap, InternalLabelInfos, ExecTraceName,
|
|
LabelLayoutInfo, !StringTable, !ExecTraceInfo) :-
|
|
collect_event_data_addrs(InternalLabelInfos,
|
|
[], RevInterfaceEventLayoutNames, [], RevInternalEventLayoutNames),
|
|
list.reverse(RevInterfaceEventLayoutNames, InterfaceEventLayoutNames),
|
|
list.reverse(RevInternalEventLayoutNames, InternalEventLayoutNames),
|
|
construct_var_name_vector(VarNumMap,
|
|
NeedsAllNames, MaxVarNum, VarNameVector, !StringTable),
|
|
list.map(convert_var_to_int(VarNumMap), HeadVars, HeadVarNumVector),
|
|
TraceSlotInfo = trace_slot_info(MaybeFromFullSlot, MaybeIoSeqSlot,
|
|
MaybeTrailSlots, MaybeMaxfrSlot, MaybeCallTableSlot, MaybeTailRecSlot),
|
|
ModuleInfo = Params ^ slp_module_info,
|
|
(
|
|
MaybeCallLabel = yes(CallLabel),
|
|
map.lookup(LabelLayoutInfo ^ lli_i_label_to_layout_map, CallLabel,
|
|
CallLabelSlotName),
|
|
MaybeCallLabelSlotName = yes(CallLabelSlotName)
|
|
;
|
|
MaybeCallLabel = no,
|
|
MaybeCallLabelSlotName = no
|
|
),
|
|
(
|
|
MaybeTableInfo = no,
|
|
MaybeTable = no
|
|
;
|
|
MaybeTableInfo = yes(TableInfo),
|
|
(
|
|
TableInfo = proc_table_io_entry(_),
|
|
(
|
|
MaybeTableSlotName = yes(TableSlotName),
|
|
MaybeTable = yes(data_or_slot_is_slot(TableSlotName))
|
|
;
|
|
MaybeTableSlotName = no,
|
|
unexpected($module, $pred, "MaybeTableSlotName = no")
|
|
)
|
|
;
|
|
TableInfo = proc_table_struct(_),
|
|
expect(unify(MaybeTableSlotName, no), $module, $pred,
|
|
"MaybeTableSlotName != no"),
|
|
ProcLabel = make_proc_label_from_rtti(RttiProcLabel),
|
|
TableDataId = proc_tabling_data_id(ProcLabel, tabling_info),
|
|
MaybeTable = yes(data_or_slot_is_data(TableDataId))
|
|
)
|
|
),
|
|
|
|
RevEventLayouts0 = !.ExecTraceInfo ^ eti_rev_proc_event_layouts,
|
|
ProcEventLayouts = InterfaceEventLayoutNames ++ InternalEventLayoutNames,
|
|
list.reverse(ProcEventLayouts, RevProcEventLayouts),
|
|
RevEventLayouts = RevProcEventLayouts ++ RevEventLayouts0,
|
|
!ExecTraceInfo ^ eti_rev_proc_event_layouts := RevEventLayouts,
|
|
|
|
NextEventLayout0 = !.ExecTraceInfo ^ eti_next_proc_event_layout,
|
|
EventLayoutsSlot = NextEventLayout0,
|
|
list.length(ProcEventLayouts, NumProcEventLayouts),
|
|
NextEventLayout = NextEventLayout0 + NumProcEventLayouts,
|
|
!ExecTraceInfo ^ eti_next_proc_event_layout := NextEventLayout,
|
|
EventLayoutsSlotName = layout_slot(proc_event_layouts_array,
|
|
EventLayoutsSlot),
|
|
|
|
CompressArrays = Params ^ slp_compress_arrays,
|
|
(
|
|
HeadVarNumVector = [_ | _],
|
|
RevHeadVarNums0 = !.ExecTraceInfo ^ eti_rev_proc_head_var_nums,
|
|
NextHeadVarNum0 = !.ExecTraceInfo ^ eti_next_proc_head_var_num,
|
|
list.reverse(HeadVarNumVector, RevHeadVarNumVector),
|
|
list.length(HeadVarNumVector, NumHeadVars),
|
|
(
|
|
some [CompressionLimit] (
|
|
CompressArrays = yes(CompressionLimit),
|
|
!.ExecTraceInfo ^ eti_next_proc_head_var_num =<
|
|
CompressionLimit
|
|
),
|
|
find_sequence(RevHeadVarNumVector, RevHeadVarNums0,
|
|
0, OldHeadVarNumOffset)
|
|
->
|
|
HeadVarNumSlot =
|
|
NextHeadVarNum0 - OldHeadVarNumOffset - NumHeadVars
|
|
;
|
|
RevHeadVarNums = RevHeadVarNumVector ++ RevHeadVarNums0,
|
|
!ExecTraceInfo ^ eti_rev_proc_head_var_nums := RevHeadVarNums,
|
|
|
|
HeadVarNumSlot = NextHeadVarNum0,
|
|
NextHeadVarNum = NextHeadVarNum0 + NumHeadVars,
|
|
!ExecTraceInfo ^ eti_next_proc_head_var_num := NextHeadVarNum
|
|
),
|
|
HeadVarNumSlotName = layout_slot(proc_head_var_nums_array,
|
|
HeadVarNumSlot),
|
|
MaybeHeadVarsSlotName = yes(HeadVarNumSlotName)
|
|
;
|
|
HeadVarNumVector = [],
|
|
MaybeHeadVarsSlotName = no,
|
|
NumHeadVars = 0
|
|
),
|
|
|
|
(
|
|
VarNameVector = [_ | _],
|
|
RevVarNames0 = !.ExecTraceInfo ^ eti_rev_proc_var_names,
|
|
NextVarName0 = !.ExecTraceInfo ^ eti_next_proc_var_name,
|
|
list.reverse(VarNameVector, RevVarNameVector),
|
|
list.length(VarNameVector, NumVarNames),
|
|
(
|
|
some [CompressionLimit] (
|
|
CompressArrays = yes(CompressionLimit),
|
|
!.ExecTraceInfo ^ eti_next_proc_var_name =< CompressionLimit
|
|
),
|
|
find_sequence(RevVarNameVector, RevVarNames0, 0, OldVarNameOffset)
|
|
->
|
|
VarNameSlot = NextVarName0 - OldVarNameOffset - NumVarNames
|
|
;
|
|
RevVarNames = RevVarNameVector ++ RevVarNames0,
|
|
!ExecTraceInfo ^ eti_rev_proc_var_names := RevVarNames,
|
|
|
|
VarNameSlot = NextVarName0,
|
|
NextVarName = NextVarName0 + NumVarNames,
|
|
!ExecTraceInfo ^ eti_next_proc_var_name := NextVarName
|
|
),
|
|
VarNameSlotName = layout_slot(proc_var_names_array, VarNameSlot),
|
|
MaybeVarNamesSlotName = yes(VarNameSlotName)
|
|
;
|
|
VarNameVector = [],
|
|
MaybeVarNamesSlotName = no
|
|
),
|
|
|
|
encode_exec_trace_flags(ModuleInfo, HeadVars, ArgModes, VarTypes, Flags),
|
|
ExecTrace = proc_layout_exec_trace(MaybeCallLabelSlotName,
|
|
EventLayoutsSlotName, NumProcEventLayouts, MaybeTable,
|
|
MaybeHeadVarsSlotName, NumHeadVars, MaybeVarNamesSlotName,
|
|
MaxVarNum, MaxTraceRegR, MaxTraceRegF, MaybeFromFullSlot,
|
|
MaybeIoSeqSlot, MaybeTrailSlots, MaybeMaxfrSlot, EvalMethod,
|
|
MaybeCallTableSlot, MaybeTailRecSlot, EffTraceLevel, Flags),
|
|
|
|
RevExecTraces0 = !.ExecTraceInfo ^ eti_rev_exec_traces,
|
|
RevExecTraces = [ExecTrace | RevExecTraces0],
|
|
!ExecTraceInfo ^ eti_rev_exec_traces := RevExecTraces,
|
|
|
|
ExecTraceCounter0 = !.ExecTraceInfo ^ eti_next_exec_trace,
|
|
counter.allocate(ExecTraceSlot, ExecTraceCounter0, ExecTraceCounter),
|
|
ExecTraceName = layout_slot(proc_exec_trace_array, ExecTraceSlot),
|
|
!ExecTraceInfo ^ eti_next_exec_trace := ExecTraceCounter.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred construct_exec_trace_table_data(pred_proc_id::in, layout_name::in,
|
|
proc_layout_table_info::in, maybe(layout_slot_name)::out,
|
|
static_cell_info::in, static_cell_info::out,
|
|
exec_traces_info::in, exec_traces_info::out) is det.
|
|
|
|
construct_exec_trace_table_data(PredProcId, ProcLayoutName, TableInfo,
|
|
MaybeTableSlotName, !StaticCellInfo, !ExecTraceInfo) :-
|
|
(
|
|
TableInfo = proc_table_io_entry(TableIOInfo),
|
|
TableIOInfo = proc_table_io_info(MaybeTableArgInfos),
|
|
(
|
|
MaybeTableArgInfos = no,
|
|
TableIoEntryData = table_io_entry_data(ProcLayoutName, no)
|
|
;
|
|
MaybeTableArgInfos = yes(TableArgInfos),
|
|
convert_table_arg_info(TableArgInfos, NumPTIs, PTIVectorRval,
|
|
TVarVectorRval, !StaticCellInfo),
|
|
TableIoArgsData = table_io_args_data(NumPTIs, PTIVectorRval,
|
|
TVarVectorRval),
|
|
TableIoEntryData = table_io_entry_data(ProcLayoutName,
|
|
yes(TableIoArgsData))
|
|
),
|
|
|
|
RevTableIoEntryDatas0 = !.ExecTraceInfo ^ eti_rev_table_io_entry_datas,
|
|
RevTableIoEntryDatas = [TableIoEntryData | RevTableIoEntryDatas0],
|
|
!ExecTraceInfo ^ eti_rev_table_io_entry_datas := RevTableIoEntryDatas,
|
|
|
|
TableDataCounter0 = !.ExecTraceInfo ^ eti_next_table_io_entry_data,
|
|
counter.allocate(Slot, TableDataCounter0, TableDataCounter),
|
|
!ExecTraceInfo ^ eti_next_table_io_entry_data := TableDataCounter,
|
|
|
|
TableDataSlotName = layout_slot(proc_table_io_entry_array, Slot),
|
|
MaybeTableSlotName = yes(TableDataSlotName),
|
|
|
|
TableIoEntryMap0 = !.ExecTraceInfo ^ eti_table_io_entry_map,
|
|
map.det_insert(PredProcId, TableDataSlotName,
|
|
TableIoEntryMap0, TableIoEntryMap),
|
|
!ExecTraceInfo ^ eti_table_io_entry_map := TableIoEntryMap
|
|
;
|
|
TableInfo = proc_table_struct(_TableStructInfo),
|
|
% This structure is generated by add_tabling_info_struct in proc_gen.m.
|
|
MaybeTableSlotName = no
|
|
).
|
|
|
|
convert_table_arg_info(TableArgInfos, NumPTIs,
|
|
PTIVectorRval, TVarVectorRval, !StaticCellInfo) :-
|
|
TableArgInfos = table_arg_infos(Args, TVarSlotMap),
|
|
list.length(Args, NumPTIs),
|
|
list.map_foldl(construct_table_arg_pti_rval, Args, PTIRvalsTypes,
|
|
!StaticCellInfo),
|
|
add_scalar_static_cell(PTIRvalsTypes, PTIVectorAddr, !StaticCellInfo),
|
|
PTIVectorRval = const(llconst_data_addr(PTIVectorAddr, no)),
|
|
map.map_values_only(convert_slot_to_locn_map, TVarSlotMap, TVarLocnMap),
|
|
construct_tvar_vector(TVarLocnMap, TVarVectorRval, !StaticCellInfo).
|
|
|
|
:- pred convert_slot_to_locn_map(table_locn::in, set(layout_locn)::out) is det.
|
|
|
|
convert_slot_to_locn_map(SlotLocn, LvalLocns) :-
|
|
(
|
|
SlotLocn = table_locn_direct(SlotNum),
|
|
LvalLocn = locn_direct(reg(reg_r, SlotNum))
|
|
;
|
|
SlotLocn = table_locn_indirect(SlotNum, Offset),
|
|
LvalLocn = locn_indirect(reg(reg_r, SlotNum), Offset)
|
|
),
|
|
LvalLocns = set.make_singleton_set(LvalLocn).
|
|
|
|
:- pred construct_table_arg_pti_rval(table_arg_info::in, typed_rval::out,
|
|
static_cell_info::in, static_cell_info::out) is det.
|
|
|
|
construct_table_arg_pti_rval(ClosureArg, typed_rval(ArgRval, ArgRvalType),
|
|
!StaticCellInfo) :-
|
|
ClosureArg = table_arg_info(_, _, _, Type),
|
|
ExistQTvars = [],
|
|
NumUnivQTvars = -1,
|
|
ll_pseudo_type_info.construct_typed_llds_pseudo_type_info(Type,
|
|
NumUnivQTvars, ExistQTvars, !StaticCellInfo, ArgRval, ArgRvalType).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred collect_event_data_addrs(list(internal_label_info)::in,
|
|
list(layout_slot_name)::in, list(layout_slot_name)::out,
|
|
list(layout_slot_name)::in, list(layout_slot_name)::out) is det.
|
|
|
|
collect_event_data_addrs([], !RevInterfaces, !RevInternals).
|
|
collect_event_data_addrs([Info | Infos], !RevInterfaces, !RevInternals) :-
|
|
Info = internal_label_info(_ProcLabel, _LabelNum, LabelVars, Slot,
|
|
InternalInfo),
|
|
InternalInfo = internal_layout_info(MaybePortInfo, _, _),
|
|
(
|
|
MaybePortInfo = no
|
|
;
|
|
MaybePortInfo = yes(PortInfo),
|
|
Port = PortInfo ^ port_type,
|
|
(
|
|
( Port = port_call
|
|
; Port = port_exit
|
|
; Port = port_redo
|
|
; Port = port_fail
|
|
; Port = port_tailrec_call
|
|
),
|
|
LayoutName = layout_slot(label_layout_array(LabelVars), Slot),
|
|
!:RevInterfaces = [LayoutName | !.RevInterfaces]
|
|
;
|
|
( Port = port_ite_cond
|
|
; Port = port_ite_then
|
|
; Port = port_ite_else
|
|
; Port = port_neg_enter
|
|
; Port = port_neg_success
|
|
; Port = port_neg_failure
|
|
; Port = port_disj_first
|
|
; Port = port_disj_later
|
|
; Port = port_switch
|
|
; Port = port_user
|
|
),
|
|
LayoutName = layout_slot(label_layout_array(LabelVars), Slot),
|
|
!:RevInternals = [LayoutName | !.RevInternals]
|
|
;
|
|
Port = port_exception
|
|
% This port is attached to call sites, so there is no event here.
|
|
)
|
|
),
|
|
collect_event_data_addrs(Infos, !RevInterfaces, !RevInternals).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred encode_exec_trace_flags(module_info::in, list(prog_var)::in,
|
|
list(mer_mode)::in, vartypes::in, int::out) is det.
|
|
|
|
encode_exec_trace_flags(ModuleInfo, HeadVars, ArgModes, VarTypes, !:Flags) :-
|
|
% The values of the flags are defined in runtime/mercury_stack_layout.h;
|
|
% look for the reference to this function.
|
|
!:Flags = 0,
|
|
(
|
|
proc_info_has_io_state_pair_from_details(ModuleInfo, HeadVars,
|
|
ArgModes, VarTypes, _, _)
|
|
->
|
|
!:Flags = !.Flags + 1
|
|
;
|
|
true
|
|
),
|
|
(
|
|
proc_info_has_higher_order_arg_from_details(ModuleInfo, VarTypes,
|
|
HeadVars)
|
|
->
|
|
!:Flags = !.Flags + 2
|
|
;
|
|
true
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred construct_var_name_vector(var_num_map::in,
|
|
bool::in, int::out, list(int)::out,
|
|
string_table_info::in, string_table_info::out) is det.
|
|
|
|
construct_var_name_vector(VarNumMap, NeedsAllNames, MaxVarNum, Offsets,
|
|
!StringTable) :-
|
|
map.values(VarNumMap, VarNames0),
|
|
(
|
|
NeedsAllNames = yes,
|
|
VarNames = VarNames0
|
|
;
|
|
NeedsAllNames = no,
|
|
list.filter(var_has_name, VarNames0, VarNames)
|
|
),
|
|
list.sort(VarNames, SortedVarNames),
|
|
( SortedVarNames = [FirstVarNum - _ | _] ->
|
|
MaxVarNum0 = FirstVarNum,
|
|
construct_var_name_rvals(SortedVarNames, 1, MaxVarNum0, MaxVarNum,
|
|
Offsets, !StringTable)
|
|
;
|
|
% Since variable numbers start at 1, MaxVarNum = 0 implies
|
|
% an empty array.
|
|
MaxVarNum = 0,
|
|
Offsets = []
|
|
).
|
|
|
|
:- pred var_has_name(pair(int, string)::in) is semidet.
|
|
|
|
var_has_name(_VarNum - VarName) :-
|
|
VarName \= "".
|
|
|
|
:- pred construct_var_name_rvals(assoc_list(int, string)::in,
|
|
int::in, int::in, int::out, list(int)::out,
|
|
string_table_info::in, string_table_info::out) is det.
|
|
|
|
construct_var_name_rvals([], _CurNum, MaxNum, MaxNum, [], !StringTable).
|
|
construct_var_name_rvals(VarNamesHeadTail @ [Var - Name | VarNamesTail],
|
|
CurNum, !MaxNum, [Offset | OffsetsTail], !StringTable) :-
|
|
( Var = CurNum ->
|
|
lookup_string_in_table(Name, Offset, !StringTable),
|
|
!:MaxNum = Var,
|
|
VarNames = VarNamesTail
|
|
;
|
|
Offset = 0,
|
|
VarNames = VarNamesHeadTail
|
|
),
|
|
construct_var_name_rvals(VarNames,
|
|
CurNum + 1, !MaxNum, OffsetsTail, !StringTable).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
compute_var_number_map(HeadVars, VarSet, Internals, Goal, VarNumMap) :-
|
|
some [!VarNumMap, !Counter] (
|
|
!:VarNumMap = map.init,
|
|
!:Counter = counter.init(1), % to match term.var_supply_init
|
|
goal_util.goal_vars(Goal, GoalVarSet),
|
|
set_of_var.to_sorted_list(GoalVarSet, GoalVars),
|
|
list.foldl2(add_var_to_var_number_map(VarSet), GoalVars,
|
|
!VarNumMap, !Counter),
|
|
list.foldl2(add_var_to_var_number_map(VarSet), HeadVars,
|
|
!VarNumMap, !Counter),
|
|
list.foldl2(internal_var_number_map(VarSet), Internals, !VarNumMap,
|
|
!.Counter, _),
|
|
VarNumMap = !.VarNumMap
|
|
).
|
|
|
|
:- pred internal_var_number_map(prog_varset::in,
|
|
pair(int, internal_layout_info)::in,
|
|
var_num_map::in, var_num_map::out, counter::in, counter::out) is det.
|
|
|
|
internal_var_number_map(VarSet, _Label - Internal, !VarNumMap, !Counter) :-
|
|
Internal = internal_layout_info(MaybeTrace, MaybeResume, MaybeReturn),
|
|
(
|
|
MaybeTrace = yes(Trace),
|
|
Trace = trace_port_layout_info(_, _, _, _, MaybeUser, TraceLayout),
|
|
label_layout_var_number_map(TraceLayout, !VarNumMap, !Counter),
|
|
(
|
|
MaybeUser = no
|
|
;
|
|
MaybeUser = yes(UserEvent),
|
|
UserEvent = user_event_info(_UserEventNumber, Attributes),
|
|
list.foldl2(user_attribute_var_num_map(VarSet), Attributes,
|
|
!VarNumMap, !Counter)
|
|
)
|
|
;
|
|
MaybeTrace = no
|
|
),
|
|
(
|
|
MaybeResume = yes(ResumeLayout),
|
|
label_layout_var_number_map(ResumeLayout, !VarNumMap, !Counter)
|
|
;
|
|
MaybeResume = no
|
|
),
|
|
(
|
|
MaybeReturn = yes(Return),
|
|
Return = return_layout_info(_, ReturnLayout),
|
|
label_layout_var_number_map(ReturnLayout, !VarNumMap, !Counter)
|
|
;
|
|
MaybeReturn = no
|
|
).
|
|
|
|
:- pred label_layout_var_number_map(layout_label_info::in,
|
|
var_num_map::in, var_num_map::out, counter::in, counter::out) is det.
|
|
|
|
label_layout_var_number_map(LabelLayout, !VarNumMap, !Counter) :-
|
|
LabelLayout = layout_label_info(VarInfoSet, _),
|
|
VarInfos = set.to_sorted_list(VarInfoSet),
|
|
FindVar = (pred(VarInfo::in, Var - Name::out) is semidet :-
|
|
VarInfo = layout_var_info(_, LiveValueType, _),
|
|
LiveValueType = live_value_var(Var, Name, _, _)
|
|
),
|
|
list.filter_map(FindVar, VarInfos, VarsNames),
|
|
list.foldl2(add_named_var_to_var_number_map, VarsNames,
|
|
!VarNumMap, !Counter).
|
|
|
|
:- pred user_attribute_var_num_map(prog_varset::in, maybe(user_attribute)::in,
|
|
var_num_map::in, var_num_map::out, counter::in, counter::out) is det.
|
|
|
|
user_attribute_var_num_map(VarSet, MaybeAttribute, !VarNumMap, !Counter) :-
|
|
(
|
|
MaybeAttribute = yes(Attribute),
|
|
Attribute = user_attribute(_Locn, Var),
|
|
add_var_to_var_number_map(VarSet, Var, !VarNumMap, !Counter)
|
|
;
|
|
MaybeAttribute = no
|
|
).
|
|
|
|
:- pred add_var_to_var_number_map(prog_varset::in, prog_var::in,
|
|
var_num_map::in, var_num_map::out, counter::in, counter::out) is det.
|
|
|
|
add_var_to_var_number_map(VarSet, Var, !VarNumMap, !Counter) :-
|
|
( varset.search_name(VarSet, Var, VarName) ->
|
|
Name = VarName
|
|
;
|
|
Name = ""
|
|
),
|
|
add_named_var_to_var_number_map(Var - Name, !VarNumMap, !Counter).
|
|
|
|
:- pred add_named_var_to_var_number_map(pair(prog_var, string)::in,
|
|
var_num_map::in, var_num_map::out, counter::in, counter::out) is det.
|
|
|
|
add_named_var_to_var_number_map(Var - Name, !VarNumMap, !Counter) :-
|
|
( map.search(!.VarNumMap, Var, _) ->
|
|
% Name shouldn't differ from the name recorded in !.VarNumMap.
|
|
true
|
|
;
|
|
counter.allocate(VarNum, !Counter),
|
|
map.det_insert(Var, VarNum - Name, !VarNumMap)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- type exec_traces_info
|
|
---> exec_traces_info(
|
|
% The arrays that hold (components of) exec trace structures.
|
|
eti_next_proc_head_var_num :: int,
|
|
eti_next_proc_var_name :: int,
|
|
eti_next_proc_event_layout :: int,
|
|
eti_next_table_io_entry_data :: counter,
|
|
eti_next_exec_trace :: counter,
|
|
|
|
eti_rev_proc_head_var_nums :: list(int),
|
|
eti_rev_proc_var_names :: list(int),
|
|
eti_rev_proc_event_layouts :: list(layout_slot_name),
|
|
eti_rev_table_io_entry_datas :: list(table_io_entry_data),
|
|
eti_rev_exec_traces :: list(proc_layout_exec_trace),
|
|
|
|
eti_table_io_entry_map :: map(pred_proc_id,
|
|
layout_slot_name)
|
|
).
|
|
|
|
:- func init_exec_traces_info = exec_traces_info.
|
|
|
|
init_exec_traces_info = Info :-
|
|
Info = exec_traces_info(0, 0, 0, counter.init(0), counter.init(0),
|
|
[], [], [], [], [], map.init).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Construct label layout structures.
|
|
%
|
|
|
|
% Construct the layout describing a single internal label
|
|
% for accurate GC and/or execution tracing.
|
|
%
|
|
:- pred construct_internal_layout(stack_layout_params::in,
|
|
proc_label::in, layout_name::in, var_num_map::in,
|
|
pair(int, internal_layout_info)::in, internal_label_info::out,
|
|
string_table_info::in, string_table_info::out,
|
|
static_cell_info::in, static_cell_info::out,
|
|
label_layouts_info::in, label_layouts_info::out) is det.
|
|
|
|
construct_internal_layout(Params, ProcLabel, ProcLayoutName, VarNumMap,
|
|
LabelNum - Internal, LabelLayout,
|
|
!StringTable, !StaticCellInfo, !LabelLayoutInfo) :-
|
|
Internal = internal_layout_info(Trace, Resume, Return),
|
|
(
|
|
Trace = no,
|
|
set.init(TraceLiveVarSet),
|
|
map.init(TraceTypeVarMap),
|
|
MaybeUserInfo = no
|
|
;
|
|
Trace = yes(trace_port_layout_info(_, _, _, _, MaybeUserInfo,
|
|
TraceLayout)),
|
|
TraceLayout = layout_label_info(TraceLiveVarSet, TraceTypeVarMap)
|
|
),
|
|
(
|
|
Resume = no,
|
|
set.init(ResumeLiveVarSet),
|
|
map.init(ResumeTypeVarMap)
|
|
;
|
|
Resume = yes(ResumeLayout),
|
|
ResumeLayout = layout_label_info(ResumeLiveVarSet, ResumeTypeVarMap)
|
|
),
|
|
(
|
|
Trace = yes(trace_port_layout_info(_, Port, IsHidden, GoalPath, _, _)),
|
|
Return = no,
|
|
MaybePort = yes(Port),
|
|
MaybeIsHidden = yes(IsHidden),
|
|
GoalPathStr = goal_path_to_string(GoalPath),
|
|
lookup_string_in_table(GoalPathStr, GoalPathNum, !StringTable),
|
|
MaybeGoalPath = yes(GoalPathNum)
|
|
;
|
|
Trace = no,
|
|
Return = yes(ReturnInfo),
|
|
% We only ever use the port fields of these layout structures
|
|
% when we process exception events. (Since exception events are
|
|
% interface events, the goal path field is not meaningful then.)
|
|
MaybePort = yes(port_exception),
|
|
MaybeIsHidden = yes(no),
|
|
% We only ever use the goal path fields of these layout structures
|
|
% when we process "fail" commands in the debugger.
|
|
ReturnInfo = return_layout_info(TargetsContexts, _),
|
|
( find_valid_return_context(TargetsContexts, _, _, GoalPath) ->
|
|
GoalPathStr = goal_path_to_string(GoalPath),
|
|
lookup_string_in_table(GoalPathStr, GoalPathNum, !StringTable),
|
|
MaybeGoalPath = yes(GoalPathNum)
|
|
;
|
|
% If tracing is enabled, then exactly one of the calls for which
|
|
% this label is a return site would have had a valid context.
|
|
% If none do, then tracing is not enabled, and therefore the goal
|
|
% path of this label will not be accessed.
|
|
MaybeGoalPath = no
|
|
)
|
|
;
|
|
Trace = no,
|
|
Return = no,
|
|
MaybePort = no,
|
|
MaybeIsHidden = no,
|
|
MaybeGoalPath = no
|
|
;
|
|
Trace = yes(_),
|
|
Return = yes(_),
|
|
unexpected($module, $pred,
|
|
"label has both trace and return layout info")
|
|
),
|
|
AgcStackLayout = Params ^ slp_agc_stack_layout,
|
|
(
|
|
Return = no,
|
|
set.init(ReturnLiveVarSet),
|
|
map.init(ReturnTypeVarMap)
|
|
;
|
|
Return = yes(return_layout_info(_, ReturnLayout)),
|
|
ReturnLayout = layout_label_info(ReturnLiveVarSet0, ReturnTypeVarMap),
|
|
(
|
|
AgcStackLayout = yes,
|
|
ReturnLiveVarSet = ReturnLiveVarSet0
|
|
;
|
|
AgcStackLayout = no,
|
|
% This set of variables must be for uplevel printing in execution
|
|
% tracing, so we are interested only in (a) variables, not
|
|
% temporaries, (b) only named variables, and (c) only those
|
|
% on the stack, not the return values.
|
|
%
|
|
% At the moment, we use ReturnTypeVarMap unchanged. Due to the
|
|
% deletions done by this filtering, this map may be bigger than
|
|
% necessary, but this fact does not compromise correctness.
|
|
% We use the unchanged ReturnTypeVarMap to avoid having to scan
|
|
% the types of all the selected layout_var_infos.
|
|
set.filter(select_trace_return,
|
|
ReturnLiveVarSet0, ReturnLiveVarSet)
|
|
)
|
|
),
|
|
(
|
|
Trace = no,
|
|
Resume = no,
|
|
Return = no
|
|
->
|
|
MaybeVarInfo = no_var_info
|
|
;
|
|
% XXX Ignore differences in insts inside layout_var_infos.
|
|
set.union(TraceLiveVarSet, ResumeLiveVarSet, LiveVarSet0),
|
|
set.union(LiveVarSet0, ReturnLiveVarSet, LiveVarSet),
|
|
map.union(set.intersect, TraceTypeVarMap, ResumeTypeVarMap,
|
|
TypeVarMap0),
|
|
map.union(set.intersect, TypeVarMap0, ReturnTypeVarMap, TypeVarMap),
|
|
construct_label_var_info(Params, LiveVarSet, VarNumMap, TypeVarMap,
|
|
MaybeVarInfo, !StaticCellInfo, !LabelLayoutInfo)
|
|
),
|
|
(
|
|
MaybeUserInfo = no,
|
|
MaybeUserDataSlot = no
|
|
;
|
|
MaybeUserInfo = yes(UserInfo),
|
|
UserInfo = user_event_info(UserEventNumber, Attributes),
|
|
construct_user_data_array(Params, VarNumMap, Attributes,
|
|
UserLocnsArray, UserAttrMaybeVarNums, !StaticCellInfo),
|
|
|
|
add_scalar_static_cell(UserLocnsArray, UserLocnsDataAddr,
|
|
!StaticCellInfo),
|
|
UserLocnsRval = const(llconst_data_addr(UserLocnsDataAddr, no)),
|
|
|
|
list.length(UserAttrMaybeVarNums, NumVarNums),
|
|
VarNumSlotNum0 = !.LabelLayoutInfo ^ lli_next_user_event_var_num,
|
|
VarNumSlotNum = VarNumSlotNum0 + NumVarNums,
|
|
!LabelLayoutInfo ^ lli_next_user_event_var_num := VarNumSlotNum,
|
|
UserAttrVarNumsSlot = layout_slot(user_event_var_nums_array,
|
|
VarNumSlotNum0),
|
|
|
|
VarNums0 = !.LabelLayoutInfo ^ lli_user_event_var_nums,
|
|
VarNums = VarNums0 ++ cord.from_list(UserAttrMaybeVarNums),
|
|
!LabelLayoutInfo ^ lli_user_event_var_nums := VarNums,
|
|
|
|
UserEventCounter0 = !.LabelLayoutInfo ^ lli_next_user_event,
|
|
counter.allocate(UserEventSlotNum,
|
|
UserEventCounter0, UserEventCounter),
|
|
!LabelLayoutInfo ^ lli_next_user_event := UserEventCounter,
|
|
UserEventSlot = layout_slot(user_event_layout_array, UserEventSlotNum),
|
|
|
|
UserData = user_event_data(UserEventNumber, UserLocnsRval,
|
|
UserAttrVarNumsSlot),
|
|
UserEvents0 = !.LabelLayoutInfo ^ lli_user_events,
|
|
UserEvents = UserEvents0 ++ cord.singleton(UserData),
|
|
!LabelLayoutInfo ^ lli_user_events := UserEvents,
|
|
|
|
MaybeUserDataSlot = yes(UserEventSlot)
|
|
),
|
|
|
|
(
|
|
Trace = yes(_),
|
|
LabelNumCounter0 = !.LabelLayoutInfo ^ lli_label_counter,
|
|
counter.allocate(LabelNumber0, LabelNumCounter0, LabelNumCounter),
|
|
!LabelLayoutInfo ^ lli_label_counter := LabelNumCounter,
|
|
|
|
% MR_ml_label_exec_count[0] is never written out; it is reserved for
|
|
% cases like this, for labels without events, and for handwritten
|
|
% labels.
|
|
( LabelNumber0 < (1 << 16) ->
|
|
LabelNumber = LabelNumber0
|
|
;
|
|
LabelNumber = 0
|
|
)
|
|
;
|
|
Trace = no,
|
|
LabelNumber = 0
|
|
),
|
|
Label = internal_label(LabelNum, ProcLabel),
|
|
BasicLayout = basic_label_layout(ProcLabel, LabelNum, ProcLayoutName,
|
|
MaybePort, MaybeIsHidden, LabelNumber, MaybeGoalPath,
|
|
MaybeUserDataSlot),
|
|
(
|
|
MaybeVarInfo = no_var_info,
|
|
LabelVars = label_has_no_var_info,
|
|
NoVarsLayout = label_layout_no_vars(BasicLayout),
|
|
add_no_vars_internal_layout_data(Label, NoVarsLayout, Slot,
|
|
!LabelLayoutInfo)
|
|
;
|
|
MaybeVarInfo = short_var_info(SLayoutVarInfo),
|
|
LabelVars = label_has_short_var_info,
|
|
SVarsLayout = label_layout_short_vars(BasicLayout, SLayoutVarInfo),
|
|
add_short_vars_internal_layout_data(Label, SVarsLayout, Slot,
|
|
!LabelLayoutInfo)
|
|
;
|
|
MaybeVarInfo = long_var_info(LLayoutVarInfo),
|
|
LabelVars = label_has_long_var_info,
|
|
LVarsLayout = label_layout_long_vars(BasicLayout, LLayoutVarInfo),
|
|
add_long_vars_internal_layout_data(Label, LVarsLayout, Slot,
|
|
!LabelLayoutInfo)
|
|
),
|
|
LabelLayout = internal_label_info(ProcLabel, LabelNum, LabelVars, Slot,
|
|
Internal).
|
|
|
|
:- pred add_no_vars_internal_layout_data(label::in, label_layout_no_vars::in,
|
|
int::out, label_layouts_info::in, label_layouts_info::out) is det.
|
|
|
|
add_no_vars_internal_layout_data(Label, Layout, Slot, !LLI) :-
|
|
RevLayouts0 = !.LLI ^ lli_rev_no_var_label_layouts,
|
|
RevLayouts = [Layout | RevLayouts0],
|
|
|
|
Counter0 = !.LLI ^ lli_next_no_var_label_layout,
|
|
counter.allocate(Slot, Counter0, Counter),
|
|
LayoutName = layout_slot(label_layout_array(label_has_no_var_info), Slot),
|
|
|
|
LabelToLayoutMap0 = !.LLI ^ lli_i_label_to_layout_map,
|
|
map.det_insert(Label, LayoutName, LabelToLayoutMap0, LabelToLayoutMap),
|
|
|
|
!LLI ^ lli_rev_no_var_label_layouts := RevLayouts,
|
|
!LLI ^ lli_next_no_var_label_layout := Counter,
|
|
!LLI ^ lli_i_label_to_layout_map := LabelToLayoutMap.
|
|
|
|
:- pred add_short_vars_internal_layout_data(label::in,
|
|
label_layout_short_vars::in, int::out,
|
|
label_layouts_info::in, label_layouts_info::out) is det.
|
|
|
|
add_short_vars_internal_layout_data(Label, Layout, Slot, !LLI) :-
|
|
RevLayouts0 = !.LLI ^ lli_rev_svar_label_layouts,
|
|
RevLayouts = [Layout | RevLayouts0],
|
|
|
|
Counter0 = !.LLI ^ lli_next_svar_label_layout,
|
|
counter.allocate(Slot, Counter0, Counter),
|
|
LayoutArray = label_layout_array(label_has_short_var_info),
|
|
LayoutName = layout_slot(LayoutArray, Slot),
|
|
|
|
LabelToLayoutMap0 = !.LLI ^ lli_i_label_to_layout_map,
|
|
map.det_insert(Label, LayoutName, LabelToLayoutMap0, LabelToLayoutMap),
|
|
|
|
!LLI ^ lli_rev_svar_label_layouts := RevLayouts,
|
|
!LLI ^ lli_next_svar_label_layout := Counter,
|
|
!LLI ^ lli_i_label_to_layout_map := LabelToLayoutMap.
|
|
|
|
:- pred add_long_vars_internal_layout_data(label::in,
|
|
label_layout_long_vars::in, int::out,
|
|
label_layouts_info::in, label_layouts_info::out) is det.
|
|
|
|
add_long_vars_internal_layout_data(Label, Layout, Slot, !LLI) :-
|
|
RevLayouts0 = !.LLI ^ lli_rev_lvar_label_layouts,
|
|
RevLayouts = [Layout | RevLayouts0],
|
|
|
|
Counter0 = !.LLI ^ lli_next_lvar_label_layout,
|
|
counter.allocate(Slot, Counter0, Counter),
|
|
LayoutArray = label_layout_array(label_has_long_var_info),
|
|
LayoutName = layout_slot(LayoutArray, Slot),
|
|
|
|
LabelToLayoutMap0 = !.LLI ^ lli_i_label_to_layout_map,
|
|
map.det_insert(Label, LayoutName, LabelToLayoutMap0, LabelToLayoutMap),
|
|
|
|
!LLI ^ lli_rev_lvar_label_layouts := RevLayouts,
|
|
!LLI ^ lli_next_lvar_label_layout := Counter,
|
|
!LLI ^ lli_i_label_to_layout_map := LabelToLayoutMap.
|
|
|
|
:- pred construct_user_data_array(stack_layout_params::in, var_num_map::in,
|
|
list(maybe(user_attribute))::in,
|
|
list(typed_rval)::out, list(maybe(int))::out,
|
|
static_cell_info::in, static_cell_info::out) is det.
|
|
|
|
construct_user_data_array(_, _, [], [], [], !Info).
|
|
construct_user_data_array(Params, VarNumMap, [MaybeAttr | MaybeAttrs],
|
|
[LocnTypedRval | LocnTypedRvals], [MaybeVarNum | MaybeVarNums],
|
|
!StaticCellInfo) :-
|
|
(
|
|
MaybeAttr = yes(Attr),
|
|
Attr = user_attribute(Locn, Var),
|
|
represent_locn_or_const_as_int_rval(Params, Locn, LocnRval,
|
|
LocnRvalType, !StaticCellInfo),
|
|
LocnTypedRval = typed_rval(LocnRval, LocnRvalType),
|
|
convert_var_to_int(VarNumMap, Var, VarNum),
|
|
MaybeVarNum = yes(VarNum)
|
|
;
|
|
MaybeAttr = no,
|
|
LocnTypedRval = typed_rval(const(llconst_int(0)), lt_unsigned),
|
|
MaybeVarNum = no
|
|
),
|
|
construct_user_data_array(Params, VarNumMap, MaybeAttrs,
|
|
LocnTypedRvals, MaybeVarNums, !StaticCellInfo).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Given a list of layout_var_infos and the type variables that occur
|
|
% in them, select only the layout_var_infos that may be required
|
|
% by up-level printing in the trace-based debugger.
|
|
%
|
|
:- pred select_trace_return(layout_var_info::in) is semidet.
|
|
|
|
select_trace_return(LocnInfo) :-
|
|
LocnInfo = layout_var_info(Locn, LvalType, _),
|
|
LvalType = live_value_var(_, Name, _, _),
|
|
Name \= "",
|
|
(
|
|
Locn = locn_direct(Lval)
|
|
;
|
|
Locn = locn_indirect(Lval, _)
|
|
),
|
|
(
|
|
Lval = stackvar(_)
|
|
;
|
|
Lval = framevar(_)
|
|
;
|
|
Lval = double_stackvar(double_stackvar, _)
|
|
).
|
|
|
|
% Given a list of layout_var_infos, put the ones that tracing can be
|
|
% interested in (whether at an internal port or for uplevel printing)
|
|
% in a block at the start, and both this block and the remaining
|
|
% block. The division into two blocks can make the job of the
|
|
% debugger somewhat easier, the sorting of the named var block makes
|
|
% the output of the debugger look nicer, and the sorting of the both
|
|
% blocks makes it more likely that different labels' layout structures
|
|
% will have common parts (e.g. name vectors).
|
|
%
|
|
:- pred sort_livevals(list(layout_var_info)::in, list(layout_var_info)::out)
|
|
is det.
|
|
|
|
sort_livevals(OrigInfos, FinalInfos) :-
|
|
IsNamedVar = (pred(LvalInfo::in) is semidet :-
|
|
LvalInfo = layout_var_info(_Lval, LvalType, _),
|
|
LvalType = live_value_var(_, Name, _, _),
|
|
Name \= ""
|
|
),
|
|
list.filter(IsNamedVar, OrigInfos, NamedVarInfos0, OtherInfos0),
|
|
CompareVarInfos = (pred(Var1::in, Var2::in, Result::out) is det :-
|
|
Var1 = layout_var_info(Lval1, LiveType1, _),
|
|
Var2 = layout_var_info(Lval2, LiveType2, _),
|
|
get_name_from_live_value_type(LiveType1, Name1),
|
|
get_name_from_live_value_type(LiveType2, Name2),
|
|
compare(NameResult, Name1, Name2),
|
|
(
|
|
NameResult = (=),
|
|
compare(Result, Lval1, Lval2)
|
|
;
|
|
( NameResult = (<)
|
|
; NameResult = (>)
|
|
),
|
|
Result = NameResult
|
|
)
|
|
),
|
|
list.sort(CompareVarInfos, NamedVarInfos0, NamedVarInfos),
|
|
list.sort(CompareVarInfos, OtherInfos0, OtherInfos),
|
|
FinalInfos = NamedVarInfos ++ OtherInfos.
|
|
|
|
:- pred get_name_from_live_value_type(live_value_type::in,
|
|
string::out) is det.
|
|
|
|
get_name_from_live_value_type(LiveType, Name) :-
|
|
( LiveType = live_value_var(_, NamePrime, _, _) ->
|
|
Name = NamePrime
|
|
;
|
|
Name = ""
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Given a association list of type variables and their locations
|
|
% sorted on the type variables, represent them in an array of
|
|
% location descriptions indexed by the type variable. The next
|
|
% slot to fill is given by the second argument.
|
|
%
|
|
:- pred construct_type_param_locn_vector(
|
|
assoc_list(tvar, set(layout_locn))::in,
|
|
int::in, list(typed_rval)::out) is det.
|
|
|
|
construct_type_param_locn_vector([], _, []).
|
|
construct_type_param_locn_vector([TVar - Locns | TVarLocns], CurSlot,
|
|
Vector) :-
|
|
term.var_to_int(TVar, TVarNum),
|
|
NextSlot = CurSlot + 1,
|
|
( TVarNum = CurSlot ->
|
|
( set.remove_least(LeastLocn, Locns, _) ->
|
|
Locn = LeastLocn
|
|
;
|
|
unexpected($module, $pred, "tvar has empty set of locations")
|
|
),
|
|
represent_locn_as_int_rval(Locn, Rval),
|
|
construct_type_param_locn_vector(TVarLocns, NextSlot, VectorTail),
|
|
Vector = [typed_rval(Rval, lt_unsigned) | VectorTail]
|
|
; TVarNum > CurSlot ->
|
|
construct_type_param_locn_vector([TVar - Locns | TVarLocns], NextSlot,
|
|
VectorTail),
|
|
% This slot will never be referred to.
|
|
Vector = [typed_rval(const(llconst_int(0)), lt_unsigned) | VectorTail]
|
|
;
|
|
unexpected($module, $pred, "unsorted tvars")
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- type liveval_array_slot
|
|
---> liveval_array_slot(
|
|
% This rval is the pseudo_type_info for the type of the
|
|
% live value.
|
|
lai_type :: rval,
|
|
|
|
% This is the variable number of a live value. Contains zero
|
|
% if the live value is not a variable, and contains the
|
|
% hightest possible uint_least16 value if the variable number
|
|
% does not fit in 16 bits.
|
|
lai_hlds_var_num :: int,
|
|
|
|
% This describes the location of the live value as either
|
|
% a MR_LongLval or MR_ShortLval.
|
|
lai_locn_desc :: int
|
|
).
|
|
|
|
:- func project_array_slot_pti(liveval_array_slot) = rval.
|
|
|
|
project_array_slot_pti(liveval_array_slot(PTI, _, _)) = PTI.
|
|
|
|
:- func project_array_slot_hlds_var_num(liveval_array_slot) = int.
|
|
|
|
project_array_slot_hlds_var_num(liveval_array_slot(_, VarNum, _)) = VarNum.
|
|
|
|
:- func project_array_slot_locn(liveval_array_slot) = int.
|
|
|
|
project_array_slot_locn(liveval_array_slot(_, _, Locn)) = Locn.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- type maybe_var_info
|
|
---> no_var_info
|
|
; short_var_info(label_short_var_info)
|
|
; long_var_info(label_long_var_info).
|
|
|
|
:- pred construct_label_var_info(stack_layout_params::in,
|
|
set(layout_var_info)::in, var_num_map::in, map(tvar, set(layout_locn))::in,
|
|
maybe_var_info::out, static_cell_info::in, static_cell_info::out,
|
|
label_layouts_info::in, label_layouts_info::out) is det.
|
|
|
|
construct_label_var_info(Params, VarInfoSet, VarNumMap, TVarLocnMap,
|
|
MaybeVarInfo, !StaticCellInfo, !LabelLayoutInfo) :-
|
|
construct_tvar_vector(TVarLocnMap, TypeParamsRval, !StaticCellInfo),
|
|
|
|
set.to_sorted_list(VarInfoSet, VarInfos0),
|
|
sort_livevals(VarInfos0, VarInfos),
|
|
|
|
int.pow(2, short_count_bits, BytesLimit),
|
|
ModuleInfo = Params ^ slp_module_info,
|
|
construct_liveval_array_slots(VarInfos, ModuleInfo, VarNumMap,
|
|
0, BytesLimit, LongArrayInfos, ShortArrayInfos, !StaticCellInfo),
|
|
|
|
AllArrayInfos = LongArrayInfos ++ ShortArrayInfos,
|
|
PTIs = list.map(project_array_slot_pti, AllArrayInfos),
|
|
HLDSVarNums = list.map(project_array_slot_hlds_var_num, AllArrayInfos),
|
|
ShortLocns = list.map(project_array_slot_locn, ShortArrayInfos),
|
|
LongLocns = list.map(project_array_slot_locn, LongArrayInfos),
|
|
|
|
list.length(PTIs, NumPTIs),
|
|
list.length(HLDSVarNums, NumHLDSVarNums),
|
|
list.length(ShortLocns, NumShortLocns),
|
|
list.length(LongLocns, NumLongLocns),
|
|
expect(unify(NumPTIs, NumHLDSVarNums), $module, $pred,
|
|
"NumPTIs != NumHLDSVarNums"),
|
|
expect(unify(NumPTIs, NumLongLocns + NumShortLocns), $module, $pred,
|
|
"NumPTIs != NumLongLocns + NumShortLocns"),
|
|
|
|
EncodedLength = NumLongLocns << short_count_bits + NumShortLocns,
|
|
|
|
% XXX As an optimization, instead of always adding PTIs, HLDSVarNums,
|
|
% ShortLocns and LongLocns to their respective arrays, we could see
|
|
% whether those arrays already contain the required sequences.
|
|
|
|
CompressArrays = Params ^ slp_compress_arrays,
|
|
( NumPTIs > 0 ->
|
|
AllRevPTIs0 = !.LabelLayoutInfo ^ lli_rev_ptis,
|
|
NextPTISlot0 = !.LabelLayoutInfo ^ lli_next_pti,
|
|
list.reverse(PTIs, RevPTIs),
|
|
(
|
|
some [CompressionLimit] (
|
|
CompressArrays = yes(CompressionLimit),
|
|
!.LabelLayoutInfo ^ lli_next_pti =< CompressionLimit
|
|
),
|
|
find_sequence(RevPTIs, AllRevPTIs0, 0, OldPTIOffset)
|
|
->
|
|
PTISlot = NextPTISlot0 - OldPTIOffset - NumPTIs
|
|
;
|
|
AllRevPTIs = RevPTIs ++ AllRevPTIs0,
|
|
!LabelLayoutInfo ^ lli_rev_ptis := AllRevPTIs,
|
|
|
|
PTISlot = NextPTISlot0,
|
|
NextPTISlot = NextPTISlot0 + NumPTIs,
|
|
!LabelLayoutInfo ^ lli_next_pti := NextPTISlot
|
|
)
|
|
;
|
|
PTISlot = -1
|
|
),
|
|
% The garbage collector does not need HLDS variable numbers; only the
|
|
% debugger does.
|
|
(
|
|
NumHLDSVarNums > 0,
|
|
Params ^ slp_trace_stack_layout = yes
|
|
->
|
|
AllRevHLDSVarNums0 = !.LabelLayoutInfo ^ lli_rev_hlds_var_nums,
|
|
NextHLDSVarNumSlot0 = !.LabelLayoutInfo ^ lli_next_hlds_var_num,
|
|
list.reverse(HLDSVarNums, RevHLDSVarNums),
|
|
(
|
|
some [CompressionLimit] (
|
|
CompressArrays = yes(CompressionLimit),
|
|
!.LabelLayoutInfo ^ lli_next_hlds_var_num =< CompressionLimit
|
|
),
|
|
find_sequence(RevHLDSVarNums, AllRevHLDSVarNums0,
|
|
0, OldHLDSVarNumsOffset)
|
|
->
|
|
HLDSVarNumSlot = NextHLDSVarNumSlot0 - OldHLDSVarNumsOffset -
|
|
NumHLDSVarNums
|
|
;
|
|
AllRevHLDSVarNums = RevHLDSVarNums ++ AllRevHLDSVarNums0,
|
|
!LabelLayoutInfo ^ lli_rev_hlds_var_nums := AllRevHLDSVarNums,
|
|
|
|
HLDSVarNumSlot = NextHLDSVarNumSlot0,
|
|
NextHLDSVarNumSlot = NextHLDSVarNumSlot0 + NumHLDSVarNums,
|
|
!LabelLayoutInfo ^ lli_next_hlds_var_num := NextHLDSVarNumSlot
|
|
)
|
|
;
|
|
HLDSVarNumSlot = -1
|
|
),
|
|
( NumShortLocns > 0 ->
|
|
AllRevShortLocns0 = !.LabelLayoutInfo ^ lli_rev_short_locns,
|
|
NextShortLocnSlot0 = !.LabelLayoutInfo ^ lli_next_short_locn,
|
|
list.reverse(ShortLocns, RevShortLocns),
|
|
(
|
|
some [CompressionLimit] (
|
|
CompressArrays = yes(CompressionLimit),
|
|
!.LabelLayoutInfo ^ lli_next_short_locn =< CompressionLimit
|
|
),
|
|
find_sequence(RevShortLocns, AllRevShortLocns0,
|
|
0, OldShortLocnsOffset)
|
|
->
|
|
ShortLocnSlot = NextShortLocnSlot0 - OldShortLocnsOffset -
|
|
NumShortLocns
|
|
;
|
|
AllRevShortLocns = RevShortLocns ++ AllRevShortLocns0,
|
|
!LabelLayoutInfo ^ lli_rev_short_locns := AllRevShortLocns,
|
|
|
|
ShortLocnSlot = NextShortLocnSlot0,
|
|
NextShortLocnSlot = NextShortLocnSlot0 + NumShortLocns,
|
|
!LabelLayoutInfo ^ lli_next_short_locn := NextShortLocnSlot
|
|
)
|
|
;
|
|
ShortLocnSlot = -1
|
|
),
|
|
( NumLongLocns > 0 ->
|
|
AllRevLongLocns0 = !.LabelLayoutInfo ^ lli_rev_long_locns,
|
|
NextLongLocnSlot0 = !.LabelLayoutInfo ^ lli_next_long_locn,
|
|
list.reverse(LongLocns, RevLongLocns),
|
|
(
|
|
some [CompressionLimit] (
|
|
CompressArrays = yes(CompressionLimit),
|
|
!.LabelLayoutInfo ^ lli_next_long_locn =< CompressionLimit
|
|
),
|
|
find_sequence(RevLongLocns, AllRevLongLocns0,
|
|
0, OldLongLocnsOffset)
|
|
->
|
|
LongLocnSlot = NextLongLocnSlot0 - OldLongLocnsOffset -
|
|
NumLongLocns
|
|
;
|
|
AllRevLongLocns = RevLongLocns ++ AllRevLongLocns0,
|
|
!LabelLayoutInfo ^ lli_rev_long_locns := AllRevLongLocns,
|
|
|
|
LongLocnSlot = NextLongLocnSlot0,
|
|
NextLongLocnSlot = NextLongLocnSlot0 + NumLongLocns,
|
|
!LabelLayoutInfo ^ lli_next_long_locn := NextLongLocnSlot
|
|
),
|
|
|
|
LVarInfo = label_long_var_info(EncodedLength, TypeParamsRval,
|
|
PTISlot, HLDSVarNumSlot, ShortLocnSlot, LongLocnSlot),
|
|
MaybeVarInfo = long_var_info(LVarInfo)
|
|
;
|
|
SVarInfo = label_short_var_info(EncodedLength, TypeParamsRval,
|
|
PTISlot, HLDSVarNumSlot, ShortLocnSlot),
|
|
MaybeVarInfo = short_var_info(SVarInfo)
|
|
).
|
|
|
|
:- pred construct_liveval_array_slots(list(layout_var_info)::in,
|
|
module_info::in, var_num_map::in, int::in, int::in,
|
|
list(liveval_array_slot)::out, list(liveval_array_slot)::out,
|
|
static_cell_info::in, static_cell_info::out) is det.
|
|
|
|
construct_liveval_array_slots([], _, _, _, _, [], [], !Info).
|
|
construct_liveval_array_slots([VarInfo | VarInfos], ModuleInfo, VarNumMap,
|
|
BytesSoFar, BytesLimit, LongArraySlots, ShortArraySlots,
|
|
!StaticCellInfo) :-
|
|
VarInfo = layout_var_info(Locn, LiveValueType, _),
|
|
represent_live_value_type_and_var_num(VarNumMap, LiveValueType,
|
|
TypeRval, VarNum, !StaticCellInfo),
|
|
(
|
|
LiveValueType = live_value_var(_, _, Type, _),
|
|
check_dummy_type(ModuleInfo, Type) = is_dummy_type,
|
|
% We want to preserve I/O states in registers.
|
|
\+ (
|
|
Locn = locn_direct(reg(_, _))
|
|
)
|
|
->
|
|
unexpected($module, $pred, "unexpected reference to dummy value")
|
|
;
|
|
BytesSoFar < BytesLimit,
|
|
represent_locn_as_byte(Locn, ShortLocn)
|
|
->
|
|
ArraySlot = liveval_array_slot(TypeRval, VarNum, ShortLocn),
|
|
construct_liveval_array_slots(VarInfos, ModuleInfo, VarNumMap,
|
|
BytesSoFar + 1, BytesLimit, LongArraySlots, ShortArraySlots0,
|
|
!StaticCellInfo),
|
|
ShortArraySlots = [ArraySlot | ShortArraySlots0]
|
|
;
|
|
represent_locn_as_int(Locn, LongLocn),
|
|
ArraySlot = liveval_array_slot(TypeRval, VarNum, LongLocn),
|
|
construct_liveval_array_slots(VarInfos, ModuleInfo, VarNumMap,
|
|
BytesSoFar, BytesLimit, LongArraySlots0, ShortArraySlots,
|
|
!StaticCellInfo),
|
|
LongArraySlots = [ArraySlot | LongArraySlots0]
|
|
).
|
|
|
|
:- pred construct_tvar_vector(map(tvar, set(layout_locn))::in,
|
|
rval::out, static_cell_info::in, static_cell_info::out) is det.
|
|
|
|
construct_tvar_vector(TVarLocnMap, TypeParamRval, !StaticCellInfo) :-
|
|
( map.is_empty(TVarLocnMap) ->
|
|
TypeParamRval = const(llconst_int(0))
|
|
;
|
|
construct_tvar_rvals(TVarLocnMap, Vector),
|
|
add_scalar_static_cell(Vector, DataAddr, !StaticCellInfo),
|
|
TypeParamRval = const(llconst_data_addr(DataAddr, no))
|
|
).
|
|
|
|
:- pred construct_tvar_rvals(map(tvar, set(layout_locn))::in,
|
|
list(typed_rval)::out) is det.
|
|
|
|
construct_tvar_rvals(TVarLocnMap, Vector) :-
|
|
map.to_assoc_list(TVarLocnMap, TVarLocns),
|
|
construct_type_param_locn_vector(TVarLocns, 1, TypeParamLocs),
|
|
list.length(TypeParamLocs, TypeParamsLength),
|
|
LengthRval = const(llconst_int(TypeParamsLength)),
|
|
Vector = [typed_rval(LengthRval, lt_unsigned) | TypeParamLocs].
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Construct closure layout structures.
|
|
%
|
|
|
|
construct_closure_layout(CallerProcLabel, SeqNo,
|
|
ClosureLayoutInfo, ClosureProcLabel, ModuleName,
|
|
FileName, LineNumber, Origin, GoalPath, !StaticCellInfo,
|
|
TypedRvals, Data) :-
|
|
% The representation we build here should be kept in sync
|
|
% with runtime/mercury_ho_call.h, which contains macros to access
|
|
% the data structures we build here.
|
|
%
|
|
ClosureId = closure_proc_id(CallerProcLabel, SeqNo, ClosureProcLabel),
|
|
DataId = layout_id(ClosureId),
|
|
Data = closure_proc_id_data(CallerProcLabel, SeqNo, ClosureProcLabel,
|
|
ModuleName, FileName, LineNumber, Origin, GoalPath),
|
|
ProcIdRval = const(llconst_data_addr(DataId, no)),
|
|
ProcIdTypedRval = typed_rval(ProcIdRval, lt_data_ptr),
|
|
ClosureLayoutInfo = closure_layout_info(ClosureArgs, TVarLocnMap),
|
|
construct_closure_arg_rvals(ClosureArgs,
|
|
ClosureArgTypedRvals, !StaticCellInfo),
|
|
construct_tvar_vector(TVarLocnMap, TVarVectorRval, !StaticCellInfo),
|
|
TVarVectorTypedRval = typed_rval(TVarVectorRval, lt_data_ptr),
|
|
TypedRvals = [ProcIdTypedRval, TVarVectorTypedRval | ClosureArgTypedRvals].
|
|
|
|
:- pred construct_closure_arg_rvals(list(closure_arg_info)::in,
|
|
list(typed_rval)::out, static_cell_info::in, static_cell_info::out) is det.
|
|
|
|
construct_closure_arg_rvals(ClosureArgs, ClosureArgTypedRvals,
|
|
!StaticCellInfo) :-
|
|
list.map_foldl(construct_closure_arg_rval, ClosureArgs, ArgTypedRvals,
|
|
!StaticCellInfo),
|
|
list.length(ArgTypedRvals, Length),
|
|
LengthTypedRval = typed_rval(const(llconst_int(Length)), lt_integer),
|
|
ClosureArgTypedRvals = [LengthTypedRval| ArgTypedRvals].
|
|
|
|
:- pred construct_closure_arg_rval(closure_arg_info::in,
|
|
typed_rval::out, static_cell_info::in, static_cell_info::out) is det.
|
|
|
|
construct_closure_arg_rval(ClosureArg, typed_rval(ArgRval, ArgRvalType),
|
|
!StaticCellInfo) :-
|
|
ClosureArg = closure_arg_info(Type, _Inst),
|
|
% For a stack layout, we can treat all type variables as universally
|
|
% quantified. This is not the argument of a constructor, so we do not need
|
|
% to distinguish between type variables that are and aren't in scope;
|
|
% we can take the variable number directly from the procedure's tvar set.
|
|
ExistQTvars = [],
|
|
NumUnivQTvars = -1,
|
|
ll_pseudo_type_info.construct_typed_llds_pseudo_type_info(Type,
|
|
NumUnivQTvars, ExistQTvars, !StaticCellInfo, ArgRval, ArgRvalType).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Construct a representation of the type of a value.
|
|
%
|
|
% For values representing variables, this will be a pseudo_type_info
|
|
% describing the type of the variable.
|
|
%
|
|
% For the kinds of values used internally by the compiler,
|
|
% this will be a pointer to a specific type_ctor_info (acting as a
|
|
% type_info) defined by hand in builtin.m to stand for values of
|
|
% each such kind; one for succips, one for hps, etc.
|
|
%
|
|
:- pred represent_live_value_type_and_var_num(var_num_map::in,
|
|
live_value_type::in, rval::out, int::out,
|
|
static_cell_info::in, static_cell_info::out) is det.
|
|
|
|
represent_live_value_type_and_var_num(VarNumMap, LiveValueType, TypeRval,
|
|
VarNum, !StaticCellInfo) :-
|
|
(
|
|
(
|
|
LiveValueType = live_value_succip,
|
|
Name = "succip"
|
|
;
|
|
LiveValueType = live_value_hp,
|
|
Name = "hp"
|
|
;
|
|
LiveValueType = live_value_curfr,
|
|
Name = "curfr"
|
|
;
|
|
LiveValueType = live_value_maxfr,
|
|
Name = "maxfr"
|
|
;
|
|
LiveValueType = live_value_redofr,
|
|
Name = "redofr"
|
|
;
|
|
LiveValueType = live_value_redoip,
|
|
Name = "redoip"
|
|
;
|
|
LiveValueType = live_value_trail_ptr,
|
|
Name = "trailptr"
|
|
;
|
|
LiveValueType = live_value_ticket,
|
|
Name = "ticket"
|
|
;
|
|
% Neither the garbage collector nor the debugger need info about
|
|
% regions.
|
|
LiveValueType = live_value_region_ite,
|
|
Name = "unwanted"
|
|
;
|
|
LiveValueType = live_value_region_disj,
|
|
Name = "unwanted"
|
|
;
|
|
LiveValueType = live_value_region_commit,
|
|
Name = "unwanted"
|
|
;
|
|
LiveValueType = live_value_unwanted,
|
|
Name = "unwanted"
|
|
),
|
|
represent_special_live_value_type(Name, TypeRval),
|
|
VarNum = 0
|
|
;
|
|
LiveValueType = live_value_var(Var, _, Type, _),
|
|
% For a stack layout, we can treat all type variables as universally
|
|
% quantified. This is not the argument of a constructor, so we do not
|
|
% need to distinguish between type variables that are and are not
|
|
% in scope; we can take the variable number directly from the
|
|
% procedure's tvar set.
|
|
ExistQTvars = [],
|
|
NumUnivQTvars = -1,
|
|
ll_pseudo_type_info.construct_llds_pseudo_type_info(Type,
|
|
NumUnivQTvars, ExistQTvars, !StaticCellInfo, TypeRval),
|
|
convert_var_to_int(VarNumMap, Var, VarNum)
|
|
).
|
|
|
|
:- pred represent_special_live_value_type(string::in, rval::out) is det.
|
|
|
|
represent_special_live_value_type(SpecialTypeName, Rval) :-
|
|
RttiTypeCtor = rtti_type_ctor(unqualified(""), SpecialTypeName, 0),
|
|
DataId =
|
|
rtti_data_id(ctor_rtti_id(RttiTypeCtor, type_ctor_type_ctor_info)),
|
|
Rval = const(llconst_data_addr(DataId, no)).
|
|
|
|
:- pred convert_var_to_int(var_num_map::in, prog_var::in, int::out) is det.
|
|
|
|
convert_var_to_int(VarNumMap, Var, VarNum) :-
|
|
map.lookup(VarNumMap, Var, VarNum0 - _),
|
|
% The variable number has to fit into two bytes. We reserve the largest
|
|
% such number (Limit) to mean that the variable number is too large
|
|
% to be represented. This ought not to happen, since compilation
|
|
% would be glacial at best for procedures with that many variables.
|
|
Limit = (1 << (2 * byte_bits)) - 1,
|
|
int.min(VarNum0, Limit, VarNum).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred represent_locn_or_const_as_int_rval(stack_layout_params::in,
|
|
rval::in, rval::out, llds_type::out,
|
|
static_cell_info::in, static_cell_info::out) is det.
|
|
|
|
represent_locn_or_const_as_int_rval(Params, LvalOrConst, Rval, Type,
|
|
!StaticCellInfo) :-
|
|
(
|
|
LvalOrConst = lval(Lval),
|
|
represent_locn_as_int_rval(locn_direct(Lval), Rval),
|
|
Type = lt_unsigned
|
|
;
|
|
LvalOrConst = const(_Const),
|
|
UnboxedFloats = Params ^ slp_unboxed_floats,
|
|
ArgWidth = full_word,
|
|
LLDSType = rval_type_as_arg(UnboxedFloats, ArgWidth, LvalOrConst),
|
|
add_scalar_static_cell([typed_rval(LvalOrConst, LLDSType)], DataId,
|
|
!StaticCellInfo),
|
|
Rval = const(llconst_data_addr(DataId, no)),
|
|
Type = lt_data_ptr
|
|
;
|
|
LvalOrConst = mkword(Tag, LvalOrConstBase),
|
|
represent_locn_or_const_as_int_rval(Params, LvalOrConstBase, BaseRval,
|
|
Type, !StaticCellInfo),
|
|
Rval = mkword(Tag, BaseRval)
|
|
;
|
|
( LvalOrConst = binop(_, _, _)
|
|
; LvalOrConst = unop(_, _)
|
|
; LvalOrConst = mem_addr(_)
|
|
; LvalOrConst = var(_)
|
|
; LvalOrConst = mkword_hole(_)
|
|
),
|
|
unexpected($module, $pred, "bad rval")
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Construct a representation of a variable location as a 32-bit integer.
|
|
%
|
|
% Most of the time, a layout specifies a location as an lval.
|
|
% However, a type_info variable may be hidden inside a typeclass_info,
|
|
% In this case, accessing the type_info requires indirection.
|
|
% The address of the typeclass_info is given as an lval, and
|
|
% the location of the typeinfo within the typeclass_info as an index;
|
|
% private_builtin.type_info_from_typeclass_info interprets the index.
|
|
%
|
|
% This one level of indirection is sufficient, since type_infos
|
|
% cannot be nested inside typeclass_infos any deeper than this.
|
|
% A more general representation that would allow more indirection
|
|
% would be much harder to fit into one machine word.
|
|
%
|
|
:- pred represent_locn_as_int_rval(layout_locn::in, rval::out) is det.
|
|
|
|
represent_locn_as_int_rval(Locn, Rval) :-
|
|
represent_locn_as_int(Locn, Word),
|
|
Rval = const(llconst_int(Word)).
|
|
|
|
represent_locn_as_int(locn_direct(Lval), Word) :-
|
|
represent_lval(Lval, Word).
|
|
represent_locn_as_int(locn_indirect(Lval, Offset), Word) :-
|
|
represent_lval(Lval, BaseWord),
|
|
expect((1 << long_lval_offset_bits) > Offset, $module, $pred,
|
|
"offset too large to be represented"),
|
|
BaseAndOffset = (BaseWord << long_lval_offset_bits) + Offset,
|
|
make_tagged_word(lval_indirect, BaseAndOffset, Word).
|
|
|
|
% Construct a four byte representation of an lval.
|
|
%
|
|
:- pred represent_lval(lval::in, int::out) is det.
|
|
|
|
represent_lval(reg(reg_r, Num), Word) :-
|
|
make_tagged_word(lval_r_reg, Num, Word).
|
|
represent_lval(reg(reg_f, Num), Word) :-
|
|
make_tagged_word(lval_f_reg, Num, Word).
|
|
represent_lval(stackvar(Num), Word) :-
|
|
expect(Num > 0, $module, $pred, "bad stackvar"),
|
|
make_tagged_word(lval_stackvar, Num, Word).
|
|
represent_lval(parent_stackvar(Num), Word) :-
|
|
expect(Num > 0, $module, $pred, "bad parent_stackvar"),
|
|
make_tagged_word(lval_parent_stackvar, Num, Word).
|
|
represent_lval(framevar(Num), Word) :-
|
|
expect(Num > 0, $module, $pred, "bad framevar"),
|
|
make_tagged_word(lval_framevar, Num, Word).
|
|
represent_lval(double_stackvar(StackType, Num), Word) :-
|
|
expect(Num > 0, $module, $pred, "bad stackvar"),
|
|
(
|
|
StackType = double_stackvar,
|
|
make_tagged_word(lval_double_stackvar, Num, Word)
|
|
;
|
|
StackType = double_parent_stackvar,
|
|
make_tagged_word(lval_double_parent_stackvar, Num, Word)
|
|
).
|
|
represent_lval(succip, Word) :-
|
|
make_tagged_word(lval_succip, 0, Word).
|
|
represent_lval(maxfr, Word) :-
|
|
make_tagged_word(lval_maxfr, 0, Word).
|
|
represent_lval(curfr, Word) :-
|
|
make_tagged_word(lval_curfr, 0, Word).
|
|
represent_lval(hp, Word) :-
|
|
make_tagged_word(lval_hp, 0, Word).
|
|
represent_lval(sp, Word) :-
|
|
make_tagged_word(lval_sp, 0, Word).
|
|
represent_lval(parent_sp, Word) :-
|
|
make_tagged_word(lval_parent_sp, 0, Word).
|
|
|
|
represent_lval(temp(_, _), _) :-
|
|
unexpected($module, $pred,
|
|
"continuation live value stored in temp register").
|
|
|
|
represent_lval(succip_slot(_), _) :-
|
|
unexpected($module, $pred, "continuation live value stored in fixed slot").
|
|
represent_lval(redoip_slot(_), _) :-
|
|
unexpected($module, $pred, "continuation live value stored in fixed slot").
|
|
represent_lval(redofr_slot(_), _) :-
|
|
unexpected($module, $pred, "continuation live value stored in fixed slot").
|
|
represent_lval(succfr_slot(_), _) :-
|
|
unexpected($module, $pred, "continuation live value stored in fixed slot").
|
|
represent_lval(prevfr_slot(_), _) :-
|
|
unexpected($module, $pred, "continuation live value stored in fixed slot").
|
|
|
|
represent_lval(field(_, _, _), _) :-
|
|
unexpected($module, $pred, "continuation live value stored in field").
|
|
represent_lval(mem_ref(_), _) :-
|
|
unexpected($module, $pred, "continuation live value stored in mem_ref").
|
|
represent_lval(global_var_ref(_), _) :-
|
|
unexpected($module, $pred,
|
|
"continuation live value stored in global_var_ref").
|
|
represent_lval(lvar(_), _) :-
|
|
unexpected($module, $pred, "continuation live value stored in lvar").
|
|
|
|
% Some things in this module are encoded using a low tag. This is not done
|
|
% using the normal compiler mkword, but by doing the bit shifting here.
|
|
%
|
|
% This allows us to use more than the usual 2 or 3 bits, but we have to
|
|
% use low tags and cannot tag pointers this way.
|
|
%
|
|
:- pred make_tagged_word(locn_type::in, int::in, int::out) is det.
|
|
|
|
make_tagged_word(Locn, Value, TaggedValue) :-
|
|
locn_type_code(Locn, Tag),
|
|
TaggedValue = (Value << long_lval_tag_bits) + Tag.
|
|
|
|
:- type locn_type
|
|
---> lval_r_reg
|
|
; lval_f_reg
|
|
; lval_stackvar
|
|
; lval_framevar
|
|
; lval_succip
|
|
; lval_maxfr
|
|
; lval_curfr
|
|
; lval_hp
|
|
; lval_sp
|
|
; lval_indirect
|
|
; lval_parent_sp
|
|
; lval_parent_stackvar
|
|
; lval_double_stackvar
|
|
; lval_double_parent_stackvar
|
|
; lval_double_framevar.
|
|
|
|
:- pred locn_type_code(locn_type::in, int::out) is det.
|
|
|
|
% The code of this predicate should be kept in sync with the enum type
|
|
% MR_LongLvalType in runtime/mercury_stack_layout.h. Note that the values
|
|
% equal to 0 modulo 4 are reserved for representing constants (aligned
|
|
% pointers to static data).
|
|
|
|
locn_type_code(lval_r_reg, 1).
|
|
locn_type_code(lval_f_reg, 2).
|
|
locn_type_code(lval_stackvar, 3).
|
|
locn_type_code(lval_parent_stackvar, 3). % XXX placeholder only
|
|
locn_type_code(lval_framevar, 5).
|
|
locn_type_code(lval_succip, 6).
|
|
locn_type_code(lval_maxfr, 7).
|
|
locn_type_code(lval_curfr, 9).
|
|
locn_type_code(lval_hp, 10).
|
|
locn_type_code(lval_sp, 11).
|
|
locn_type_code(lval_parent_sp, 11). % XXX placeholder only
|
|
locn_type_code(lval_double_stackvar, 13).
|
|
locn_type_code(lval_double_parent_stackvar, 13). % XXX placeholder only
|
|
locn_type_code(lval_double_framevar, 14). % unused now
|
|
locn_type_code(lval_indirect, 15).
|
|
|
|
% This number of tag bits must be able to encode all values of
|
|
% locn_type_code.
|
|
%
|
|
:- func long_lval_tag_bits = int.
|
|
|
|
long_lval_tag_bits = 5.
|
|
|
|
% This number of tag bits must be able to encode the largest offset
|
|
% of a type_info within a typeclass_info.
|
|
%
|
|
:- func long_lval_offset_bits = int.
|
|
|
|
long_lval_offset_bits = 6.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Construct a representation of a variable location as a byte,
|
|
% if this is possible.
|
|
%
|
|
:- pred represent_locn_as_byte(layout_locn::in, int::out) is semidet.
|
|
|
|
represent_locn_as_byte(LayoutLocn, Byte) :-
|
|
LayoutLocn = locn_direct(Lval),
|
|
represent_lval_as_byte(Lval, Byte),
|
|
0 =< Byte,
|
|
Byte < 256.
|
|
|
|
% Construct a representation of an lval in a byte, if possible.
|
|
%
|
|
:- pred represent_lval_as_byte(lval::in, int::out) is semidet.
|
|
|
|
represent_lval_as_byte(reg(reg_r, Num), Byte) :-
|
|
expect(Num > 0, $module, $pred, "bad reg"),
|
|
make_tagged_byte(0, Num, Byte).
|
|
represent_lval_as_byte(stackvar(Num), Byte) :-
|
|
expect(Num > 0, $module, $pred, "bad stackvar"),
|
|
make_tagged_byte(1, Num, Byte).
|
|
represent_lval_as_byte(parent_stackvar(Num), Byte) :-
|
|
expect(Num > 0, $module, $pred, "bad parent_stackvar"),
|
|
make_tagged_byte(1, Num, Byte). % XXX placeholder only
|
|
represent_lval_as_byte(framevar(Num), Byte) :-
|
|
expect(Num > 0, $module, $pred, "bad framevar"),
|
|
make_tagged_byte(2, Num, Byte).
|
|
represent_lval_as_byte(succip, Byte) :-
|
|
locn_type_code(lval_succip, Val),
|
|
make_tagged_byte(3, Val, Byte).
|
|
represent_lval_as_byte(maxfr, Byte) :-
|
|
locn_type_code(lval_maxfr, Val),
|
|
make_tagged_byte(3, Val, Byte).
|
|
represent_lval_as_byte(curfr, Byte) :-
|
|
locn_type_code(lval_curfr, Val),
|
|
make_tagged_byte(3, Val, Byte).
|
|
represent_lval_as_byte(hp, Byte) :-
|
|
locn_type_code(lval_hp, Val),
|
|
make_tagged_byte(3, Val, Byte).
|
|
represent_lval_as_byte(sp, Byte) :-
|
|
locn_type_code(lval_sp, Val),
|
|
make_tagged_byte(3, Val, Byte).
|
|
represent_lval_as_byte(parent_sp, Byte) :-
|
|
locn_type_code(lval_parent_sp, Val),
|
|
make_tagged_byte(3, Val, Byte). % XXX placeholder only
|
|
|
|
:- pred make_tagged_byte(int::in, int::in, int::out) is det.
|
|
|
|
make_tagged_byte(Tag, Value, TaggedValue) :-
|
|
TaggedValue = unchecked_left_shift(Value, short_lval_tag_bits) + Tag.
|
|
|
|
:- func short_lval_tag_bits = int.
|
|
|
|
short_lval_tag_bits = 2.
|
|
|
|
:- func short_count_bits = int.
|
|
|
|
short_count_bits = 10.
|
|
|
|
:- func byte_bits = int.
|
|
|
|
byte_bits = 8.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- type label_layouts_info
|
|
---> label_layouts_info(
|
|
% The arrays that hold components of label layouts.
|
|
lli_next_pti :: int,
|
|
lli_next_long_locn :: int,
|
|
lli_next_short_locn :: int,
|
|
lli_next_hlds_var_num :: int,
|
|
lli_rev_ptis :: list(rval),
|
|
lli_rev_long_locns :: list(int),
|
|
lli_rev_short_locns :: list(int),
|
|
lli_rev_hlds_var_nums :: list(int),
|
|
|
|
lli_next_user_event :: counter,
|
|
lli_next_user_event_var_num :: int,
|
|
lli_user_events :: cord(user_event_data),
|
|
lli_user_event_var_nums :: cord(maybe(int)),
|
|
|
|
% The list of slots in the with-var-info and without-var-info
|
|
% label layout arrays.
|
|
lli_next_no_var_label_layout:: counter,
|
|
lli_next_svar_label_layout :: counter,
|
|
lli_next_lvar_label_layout :: counter,
|
|
lli_rev_no_var_label_layouts:: list(label_layout_no_vars),
|
|
lli_rev_svar_label_layouts :: list(label_layout_short_vars),
|
|
lli_rev_lvar_label_layouts :: list(label_layout_long_vars),
|
|
|
|
lli_label_counter :: counter,
|
|
|
|
% This maps each internal label with a layout
|
|
% to the id of its layout structure.
|
|
lli_i_label_to_layout_map :: map(label, layout_slot_name)
|
|
).
|
|
|
|
:- func init_label_layouts_info = label_layouts_info.
|
|
|
|
init_label_layouts_info = Info :-
|
|
Info = label_layouts_info(0, 0, 0, 0, [], [], [], [],
|
|
counter.init(0), 0, cord.empty, cord.empty,
|
|
counter.init(0), counter.init(0), counter.init(0), [], [], [],
|
|
counter.init(1), map.init).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Look for Search as a sub-sequence in the second argument, and
|
|
% return its offset if it exists. We use this to avoid adding existing
|
|
% sequences to arrays.
|
|
%
|
|
% When we stored information about pseudo-typeinfos, variable numbers,
|
|
% and variable locations in separate structures (not arrays), our use
|
|
% of common structures for them ensured that any identical copies of
|
|
% such structures would be optimized away. This optimization is even
|
|
% more effective, since it it allows the sequence we need for one label
|
|
% to be found as (a) a subsequence of the longer sequence for another
|
|
% label, and (b) straddling the sequences of two (or possibly even more)
|
|
% other labels.
|
|
%
|
|
% We use the straightforward, brute force algorithm instead of more
|
|
% sophisticated algorithms like Knuth-Morris-Pratt because (a) those
|
|
% algorithms are nontrivial to adapt to working on lists instead of arrays
|
|
% without losing their edge, (b) this algorithm is fast enough in most
|
|
% cases, and when it isn't, even KMP is unlikely to be fast enough.
|
|
%
|
|
% Turning on this optimization reduces the size of the generated .c and .o
|
|
% files by about 12% and 11% respectively (measured on the files in the
|
|
% library, mdbcomp and compiler directories in grade asm_fast.gc.debug),
|
|
% but even without the type_spec pragmas, it increases sequential bootcheck
|
|
% time only a little bit, from 3hr2m to 3hr6m (on alys, which is a Dell
|
|
% Inspiron 9400 laptop).
|
|
%
|
|
% Another possible optimization would be to look whether some sequence of
|
|
% elements at the current end of the array could be extended into the
|
|
% search sequence, instead of adding the whole search sequence to the
|
|
% array.
|
|
%
|
|
% Because of the brute force nature of this algorithm, it should not
|
|
% be invoked on lists that are longer than the compression limit.
|
|
% Ironically, the bigger the array, the more we want to compress it,
|
|
% but the less we can afford to do so.
|
|
%
|
|
:- pred find_sequence(list(T)::in, list(T)::in, int::in, int::out) is semidet.
|
|
:- pragma type_spec(find_sequence/4, T = int).
|
|
:- pragma type_spec(find_sequence/4, T = rval).
|
|
|
|
find_sequence(Search, [Head | Tail], CurOffset, FoundAtOffset) :-
|
|
( find_sequence_attempt(Search, [Head | Tail]) ->
|
|
FoundAtOffset = CurOffset
|
|
;
|
|
find_sequence(Search, Tail, CurOffset + 1, FoundAtOffset)
|
|
).
|
|
|
|
:- pred find_sequence_attempt(list(T)::in, list(T)::in) is semidet.
|
|
:- pragma type_spec(find_sequence_attempt/2, T = int).
|
|
:- pragma type_spec(find_sequence_attempt/2, T = rval).
|
|
|
|
find_sequence_attempt([], _).
|
|
find_sequence_attempt([SearchHead | SearchTail], [Head | Tail]) :-
|
|
SearchHead = Head,
|
|
find_sequence_attempt(SearchTail, Tail).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
represent_determinism_rval(Detism,
|
|
const(llconst_int(code_model.represent_determinism(Detism)))).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% The structure holding all the relevant parameters.
|
|
%
|
|
|
|
:- type stack_layout_params
|
|
---> stack_layout_params(
|
|
slp_module_info :: module_info,
|
|
|
|
% What kind of execution tracing, if any, are we doing?
|
|
slp_trace_level :: trace_level,
|
|
slp_trace_suppress :: trace_suppress_items,
|
|
|
|
% Is deep profiling enabled?
|
|
slp_deep_profiling :: bool,
|
|
|
|
% Should we generate agc info?
|
|
slp_agc_stack_layout :: bool,
|
|
|
|
% Should we generate tracing info?
|
|
slp_trace_stack_layout :: bool,
|
|
|
|
% Should we generate proc id info?
|
|
slp_procid_stack_layout :: bool,
|
|
|
|
% Should we try to compress arrays?
|
|
slp_compress_arrays :: maybe(int),
|
|
|
|
% Do we have static code addresses or unboxed floats?
|
|
slp_static_code_addresses :: bool,
|
|
slp_unboxed_floats :: have_unboxed_floats,
|
|
|
|
% Do we want to include information about labels' line numbers?
|
|
slp_rtti_line_numbers :: bool
|
|
).
|
|
|
|
:- func init_stack_layout_params(module_info) = stack_layout_params.
|
|
|
|
init_stack_layout_params(ModuleInfo) = Params :-
|
|
module_info_get_globals(ModuleInfo, Globals),
|
|
globals.get_trace_level(Globals, TraceLevel),
|
|
globals.get_trace_suppress(Globals, TraceSuppress),
|
|
globals.lookup_bool_option(Globals, profile_deep, DeepProfiling),
|
|
globals.lookup_bool_option(Globals, agc_stack_layout, AgcLayout),
|
|
globals.lookup_bool_option(Globals, trace_stack_layout, TraceLayout),
|
|
globals.lookup_bool_option(Globals, procid_stack_layout, ProcIdLayout),
|
|
globals.lookup_bool_option(Globals, common_layout_data, CommonLayoutData),
|
|
globals.lookup_bool_option(Globals, static_code_addresses, StaticCodeAddr),
|
|
globals.lookup_bool_option(Globals, unboxed_float, UnboxedFloatOpt),
|
|
globals.lookup_bool_option(Globals, rtti_line_numbers, RttiLineNumbers),
|
|
globals.lookup_int_option(Globals, layout_compression_limit,
|
|
CompressLimit),
|
|
(
|
|
UnboxedFloatOpt = no,
|
|
UnboxedFloat = do_not_have_unboxed_floats
|
|
;
|
|
UnboxedFloatOpt = yes,
|
|
UnboxedFloat = have_unboxed_floats
|
|
),
|
|
(
|
|
CommonLayoutData = no,
|
|
CompressArrays = no
|
|
;
|
|
CommonLayoutData = yes,
|
|
CompressArrays = yes(CompressLimit)
|
|
),
|
|
Params = stack_layout_params(ModuleInfo, TraceLevel, TraceSuppress,
|
|
DeepProfiling, AgcLayout, TraceLayout, ProcIdLayout, CompressArrays,
|
|
StaticCodeAddr, UnboxedFloat, RttiLineNumbers).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module ll_backend.stack_layout.
|
|
%---------------------------------------------------------------------------%
|