mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-19 15:54:18 +00:00
Previously, the only situation in which we could pack two or more arguments
of a term into a single word was when all those arguments are enums. This diff
changes that, so that the arguments can also be sub-word-sized integers
(signed or unsigned), or values of dummy types (which occupy zero bits).
This diff also records, for each argument of a function symbol, not just
whether, and if yes, how it is packed into a word, but also at *what offset*
that word is in the term's heap cell. It is more economical to compute this
once, when the representation of the type is being decided, than to compute
it over and over again when terms with that function symbol are being
constructed or deconstructed. However, for a transition period, we compute
these offsets at *both* times, to check the consistency of the new algorithm
for computing offsets that is run at "decide representation time" with
the old algorithms run at "generate code for a unification time".
compiler/du_type_layout.m:
Make the changes described above: pack sub-word-sized integers and
dummy values into argument words, if possible, and if the relevant
new option allows it. These options are temporary. If we find no problems
with the new packing algorithm in a few weeks, we should be able to
delete them.
Allow 64 bit ints and uints to be stored in unboxed in two words
on 32 bit platforms, if the relevant new option allows it. Support
for this is not yet complete, but it makes sense to implement the
RTTI changes for both this change and one described in the above
paragraph together.
For each packed argument, record not just its width, its shift and
the mask, but also the number of bits the argument takes. Previously,
we computed this on demand from the mask, but there is no real need
for that when simply storing this info is so cheap.
For all arguments, packed or not, record its offset, relative to both
the start of the arguments, and the start of the memory cell. (The two
are different if the arguments are preceded by either a remote secondary
tag, the typeinfos and/or typeclass_infos describing some existentially
typed arguments, or both.) The reason for this is given at the top.
Centralize the decision of the parameters of packing in one predicate.
If the option --inform-suboptimal-packing is given, print an informational
message whenever the code deciding type representations finds that
reordering the arguments of a function symbol would allow it to pack
the arguments of that function symbol into less space.
compiler/options.m:
Add the option --allow-packing-ints which controls whether
du_type_layout.m will attempt to pack {int,uint}{8,16,32} arguments
alongside enum arguments.
Add the option --allow-packing-dummies which controls whether
du_type_layout.m will optimize away (in other words, represent in 0 bits)
arguments of dummy types.
Add the option --allow-double-word-ints which controls whether
du_type_layout.m will store arguments of the types int64 and uint64
unboxed in two words on 32 bit platforms, the way it currently stores
double precision floats.
All three those options are off by default, which preserves binary
compatibility with existing code. However, the first two are ready
to be switched on (the third is not).
All three options are intended to be present in the compiler
only until these changes are tested. Once we deem them sufficiently
tested, I will modify the compiler to always do the packing they control,
at which point we can delete these options. This is why they are not
documented.
Add the option --inform-suboptimal-packing, whose meaning is described
above.
doc/user_guide.texi:
Document --inform-suboptimal-packing.
compiler/prog_data.m:
For each argument of a function symbol in a type definition, use
a new type called arg_pos_width to record the extra information
mentioned above in (offsets for all arguments, and number of bits
for packed arguments).
For each function symbol that has some existential type constraints,
record the extra information mentioned for parse_type_defn.m below.
compiler/hlds_data.m:
Include the position, as well as the width, in the representation
of the arguments of function symbols.
Previously, we used the integer 0 as a tag for dummies. Add a tag to
represent dummy values, since this gives more information to any code
that sees that tag.
compiler/ml_unify_gen.m:
compiler/unify_gen.m:
Handle the packing of dummy values, and of sub-word-sized ints and uints.
Compare the cell offset of each argument computed using existing
algorithms here with the cell offset recorded in the argument's
representation, and abort if they are different.
In some cases, restructure code a bit to make it possible.
For example, for tuples and closures, this means that instead of
simply recording that each tuple argument or closure element
is a full word, we must record its correct offset as well.
Handle the new dummy_tag.
Add prelim (not yet finished) support for double-word int64s/uint64s
on 32 bit platforms.
When packing the values of two or more variables (or constants) into a
single word in a memory cell, optimize away operations that are no-ops,
such as shifting anything by zero bits, shifting the constant zero
by any number of bits, and ORing anything with zero. This makes the
generated code easier to read. It is probably also faster for us
to do it here than to write out a bigger expression, have the C compiler
read in the bigger expression, and then later make the same optimization.
In ml_unify_gen.m, avoid the unnecessary use of a list of the argument
variables' types separate from the list of the argument variables
themselves; just look up the type of each argument variable when it is
processed.
compiler/add_special_pred.m:
When creating special (unify and compare) predicates for tuples,
include the offsets in the representation of their arguments.
Delete an unused predicate.
compiler/llds.m:
Add a new way to create an rval: a cast. We use it to implement
the extraction of signed sub-word-sized integers from packed argument
words in terms. Masking the right N bits out of the packed word
leaves the other 32-N or 64-N bits as zeroes; a cast to int8_t,
int16_t or int32_t will copy the sign bit to these bits.
Likewise, when we pack signed int{8,16,32} values into words,
we cast them to their unsigned versions to throw away any sign-extension
bits in their original word-sized representations.
No similar change is needed for the MLDS, since that already had
a mechanism for casts.
compiler/mlds.m:
Note a potential simplification in the MLDS.
compiler/builtin_lib_types.m:
Add functions to return the Mercury representation of the int64
and uint64 types.
compiler/foreign.m:
Export a specialized version of an existing predicate, to allow
ml_unify_gen.m to avoid the costs of the more general version.
compiler/hlds_out_module.m:
Always print the representations of all arguments, since the
inclusion of position information in those representation means that
the representations of even all-full-word-argument terms are of potential
interest when debugging term representations.
compiler/lco.m:
Do not try to apply LCO to arguments of dummy types. (We could optimize
them differently, by filling them in before they are "computed", but
that is a separate optimization, which is of *very* low priority.)
compiler/liveness.m:
Do not include variables of dummy types in resume points.
The reason for this is that the code that establishes a resume point
returns, for each such variable, a list of *lvals* where that variable
can be found. The new code in unify_gen.m will optimize away assignments
to values of dummy types, so there is *no* lval where they can be found.
We could allocate one, but doing so would be a pessimization. Instead,
we simply don't save and restore such values. When their value (which is
always 0) is needed, we can create them out of thin air.
compiler/ml_global_data.m:
Include the target language in the ml_global_data structure, to prevent
some of its users having to look it up in the module_info.
Add notes about the specializing the implementation of arrays of
int64s/uint64s on 32 bit platforms.
compiler/check_typeclass.m:
compiler/ml_type_gen.m:
Add sanity checks of the new precomputed fields of exist_constraints.
Conform to the changes above.
compiler/mlds_to_c.m:
Add prelim (not yet finished) support for double-word int64s/uint64s
on 32 bit platforms.
Add notes about possible optimizations.
compiler/parse_type_defn.m:
When a function symbol in a type definition contains existential
arguments, precompute and store the set of constrained and unconstrained
type variables. The code in du_type_layout.m needs this information
to compute the number of slots occupied by typeinfos and typeclass_infos
in memory cells for this function symbol, and several other places
in the compiler do too. It is easier and faster to compute this
information just once, and this is the earliest time what that can be done.
compiler/type_ctor_info.m:
Use the prerecorded information about existential types to simplify
the code here
compiler/polymorphism.m:
Add an XXX about possibly using the extra info we now record in
exist_constraints to simplify the job of polymorphism.m.
compiler/pragma_c_gen.m:
compiler/var_locn.m:
Create the values of dummy variables from scratch, if needed.
compiler/rtti.m:
Replace a bool with a bespoke type.
compiler/rtti_out.m:
compiler/rtti_to_mlds.m:
When generating RTTI information for the LLDS and MLDS backends
respectively, record new kinds of arguments as needing special
treatment. These are int64s and uint64s stored unboxed in two words
on 32 bit platforms, {int,uint}{8,16,32} values packed into words,
and dummy arguments. Each of these has a special code: its own negative
negative value in the num_bits field of the argument.
Generate slightly better formatted output.
compiler/type_util.m:
Delete a predicate that isn't needed anymore.
compiler/opt_util.m:
Delete a function that hasn't been needed for a while.
Conform to the changes above.
compiler/arg_pack.m:
compiler/bytecode_gen.m:
compiler/call_gen.m:
compiler/code_util.m:
compiler/ctgc.selector.m:
compiler/dupelim.m:
compiler/dupproc.m:
compiler/equiv_type.m:
compiler/equiv_type_hlds.m:
compiler/erl_code_gen.m:
compiler/erl_rtti.m:
compiler/export.m:
compiler/exprn_aux.m:
compiler/global_data.m:
compiler/jumpopt.m:
compiler/livemap.m:
compiler/llds_out_data.m:
compiler/middle_rec.m:
compiler/ml_closure_gen.m:
compiler/ml_switch_gen.m:
compiler/ml_top_gen.m:
compiler/module_qual.qualify_items.m:
compiler/opt_debug.m:
compiler/parse_tree_out.m:
compiler/peephole.m:
compiler/recompilation.usage.m:
compiler/resolve_unify_functor.m:
compiler/stack_layout.m:
compiler/structure_reuse.direct.choose_reuse.m:
compiler/switch_util.m:
compiler/typecheck.m:
compiler/unify_proc.m:
compiler/unused_imports.m:
compiler/xml_documentation.m:
Conform to the changes above.
compiler/llds_out_util.m:
Add a comment.
compiler/ml_code_util.m:
Factor out some common code.
runtime/mercury_type_info.h:
Allocate special values of the MR_arg_bits field of the MR_DuArgLocn type
to designate arguments as two word int64/uint64s, as sub-word-sized
arguments of types {int,uint}{8,16,32}, or as arguments of dummy types.
(We already had a special value for two word float arguments.)
Document the list of places that know about this code, so that they
can be updated if and when it changes.
library/construct.m:
Handle the construction of terms with two-word int64/uint64 arguments,
with packed {int,uint}{8,16,32} arguments, and with dummy arguments.
Factor out the code common to the sectag-present and sectag-absent cases,
to make it possible to do the above in just *one* place.
library/store.m:
Add an XXX to a place that I don't think handles two word arguments
correctly. (I think this is an old bug.)
runtime/mercury_deconstruct.c:
Handle the deconstruction of terms with two-word int64/uint64 arguments,
with packed {int,uint}{8,16,32} arguments, and with dummy arguments.
runtime/mercury_deep_copy_body.h:
Handle the copying of terms with two-word int64/uint64 arguments,
with packed {int,uint}{8,16,32} arguments, and with dummy arguments.
Give a macro a more descriptive name.
runtime/mercury_type_info.c:
Handle taking the size of terms with two-word int64/uint64 arguments,
with packed {int,uint}{8,16,32} arguments, and with dummy arguments.
runtime/mercury.h:
Put related definitions next to each other.
runtime/mercury_deconstruct.h:
runtime/mercury_ml_expand_body.h:
Fix indentation.
tests/hard_coded/construct_test.{m,exp}:
Add to this test case a test of the construction, via the library's
construct.m module, of terms containing packed sub-word-sized integers,
and packed dummies.
tests/hard_coded/deconstruct_arg.{m,exp}:
Convert the source code of this test case to state variable notation,
and update the line number references (in the names of predicates created
from lambda expressions) accordingly.
tests/hard_coded/uint64_ground_term.{m,exp}:
A new test case to check that uint64 values too large to be int64 values
can be stored in static structures.
tests/hard_coded/Mmakefile:
Enable the new test case.
2649 lines
105 KiB
Mathematica
2649 lines
105 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.
|
|
:- 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.
|
|
:- import_module mdbcomp.prim_data.
|
|
:- import_module mdbcomp.program_representation.
|
|
:- import_module mdbcomp.sym_name.
|
|
:- import_module parse_tree.
|
|
:- 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.
|
|
:- import_module backend_libs.proc_label.
|
|
:- import_module backend_libs.rtti.
|
|
:- import_module check_hlds.
|
|
:- 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.
|
|
:- 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_data_event.
|
|
:- import_module parse_tree.prog_data_pragma.
|
|
:- 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
|
|
),
|
|
( if
|
|
MaybeDeepProfInfo = no,
|
|
MaybeDebugInfo = no
|
|
then
|
|
ModuleLayouts = []
|
|
else
|
|
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, _),
|
|
not 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),
|
|
( if
|
|
( ProcIdLayout = yes
|
|
; MaybeTableIoEntry = yes(_)
|
|
)
|
|
then
|
|
UserOrUci = proc_label_user_or_uci(ProcLabel),
|
|
Kind = proc_layout_proc_id(UserOrUci)
|
|
else
|
|
Kind = proc_layout_traversal
|
|
),
|
|
ProcLayoutName = proc_layout(RttiProcLabel, Kind),
|
|
( if
|
|
( Params ^ slp_agc_stack_layout = yes
|
|
; Params ^ slp_trace_stack_layout = yes
|
|
),
|
|
valid_proc_layout(PLI)
|
|
then
|
|
list.map_foldl3(
|
|
construct_internal_layout(Params, ProcLabel, ProcLayoutName,
|
|
VarNumMap),
|
|
Internals, InternalLabelInfos,
|
|
!StringTable, !StaticCellInfo, !LabelLayoutInfo)
|
|
else
|
|
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),
|
|
( if
|
|
Return = yes(return_layout_info(TargetsContexts, _)),
|
|
find_valid_return_context(TargetsContexts, Target, Context, _GoalPath)
|
|
then
|
|
( if Target = code_label(TargetLabel) then
|
|
IsReturn = known_callee(TargetLabel)
|
|
else
|
|
IsReturn = unknown_callee
|
|
),
|
|
update_label_table_2(LabelVars, Slot, Context, IsReturn, !LabelTables)
|
|
else if
|
|
Port = yes(trace_port_layout_info(Context, _, _, _, _, _)),
|
|
context_is_valid(Context)
|
|
then
|
|
update_label_table_2(LabelVars, Slot, Context, not_a_return,
|
|
!LabelTables)
|
|
else
|
|
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),
|
|
( if map.search(!.LabelTables, File, LabelTable0) then
|
|
LabelLayout = layout_slot(label_layout_array(LabelVars), Slot),
|
|
( if map.search(LabelTable0, Line, LineInfo0) then
|
|
LineInfo = [LabelLayout - IsReturn | LineInfo0],
|
|
map.det_update(Line, LineInfo, LabelTable0, LabelTable),
|
|
map.det_update(File, LabelTable, !LabelTables)
|
|
else
|
|
LineInfo = [LabelLayout - IsReturn],
|
|
map.det_insert(Line, LineInfo, LabelTable0, LabelTable),
|
|
map.det_update(File, LabelTable, !LabelTables)
|
|
)
|
|
else
|
|
( if context_is_valid(Context) then
|
|
LabelLayout = layout_slot(label_layout_array(LabelVars), Slot),
|
|
LineInfo = [LabelLayout - IsReturn],
|
|
LabelTable = map.singleton(Line, LineInfo),
|
|
map.det_insert(File, LabelTable, !LabelTables)
|
|
else
|
|
% 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),
|
|
( if context_is_valid(Context) then
|
|
ValidTarget = Target,
|
|
ValidContext = Context,
|
|
ValidGoalPath = GoalPath
|
|
else
|
|
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,
|
|
( if
|
|
TraceStackLayout = yes,
|
|
not map.is_empty(InternalMap),
|
|
valid_proc_layout(PLI)
|
|
then
|
|
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)
|
|
else
|
|
MaybeExecTraceSlotName = no
|
|
),
|
|
ModuleInfo = Params ^ slp_module_info,
|
|
module_info_get_name(ModuleInfo, ModuleName),
|
|
DeepProfiling = Params ^ slp_deep_profiling,
|
|
( if
|
|
( NeedGoalRep = trace_needs_body_rep
|
|
; DeepProfiling = yes
|
|
)
|
|
then
|
|
(
|
|
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)
|
|
else
|
|
(
|
|
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),
|
|
( if determinism_components(Detism, _, at_most_many) then
|
|
SuccipLval = framevar(Location)
|
|
else
|
|
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($pred, "MaybeTableSlotName = no")
|
|
)
|
|
;
|
|
TableInfo = proc_table_struct(_),
|
|
expect(unify(MaybeTableSlotName, no), $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),
|
|
( if
|
|
some [CompressionLimit] (
|
|
CompressArrays = yes(CompressionLimit),
|
|
!.ExecTraceInfo ^ eti_next_proc_head_var_num =<
|
|
CompressionLimit
|
|
),
|
|
find_sequence(RevHeadVarNumVector, RevHeadVarNums0,
|
|
0, OldHeadVarNumOffset)
|
|
then
|
|
HeadVarNumSlot =
|
|
NextHeadVarNum0 - OldHeadVarNumOffset - NumHeadVars
|
|
else
|
|
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),
|
|
( if
|
|
some [CompressionLimit] (
|
|
CompressArrays = yes(CompressionLimit),
|
|
!.ExecTraceInfo ^ eti_next_proc_var_name =< CompressionLimit
|
|
),
|
|
find_sequence(RevVarNameVector, RevVarNames0, 0, OldVarNameOffset)
|
|
then
|
|
VarNameSlot = NextVarName0 - OldVarNameOffset - NumVarNames
|
|
else
|
|
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,
|
|
( if
|
|
proc_info_has_io_state_pair_from_details(ModuleInfo, HeadVars,
|
|
ArgModes, VarTypes, _, _)
|
|
then
|
|
!:Flags = !.Flags + 1
|
|
else
|
|
true
|
|
),
|
|
( if
|
|
proc_info_has_higher_order_arg_from_details(ModuleInfo, VarTypes,
|
|
HeadVars)
|
|
then
|
|
!:Flags = !.Flags + 2
|
|
else
|
|
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)
|
|
;
|
|
SortedVarNames = [],
|
|
% 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) :-
|
|
( if Var = CurNum then
|
|
lookup_string_in_table(Name, Offset, !StringTable),
|
|
!:MaxNum = Var,
|
|
VarNames = VarNamesTail
|
|
else
|
|
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) :-
|
|
( if varset.search_name(VarSet, Var, VarName) then
|
|
Name = VarName
|
|
else
|
|
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) :-
|
|
( if map.search(!.VarNumMap, Var, _) then
|
|
% Name shouldn't differ from the name recorded in !.VarNumMap.
|
|
true
|
|
else
|
|
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, _),
|
|
( if find_valid_return_context(TargetsContexts, _, _, GoalPath) then
|
|
GoalPathStr = goal_path_to_string(GoalPath),
|
|
lookup_string_in_table(GoalPathStr, GoalPathNum, !StringTable),
|
|
MaybeGoalPath = yes(GoalPathNum)
|
|
else
|
|
% 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($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)
|
|
)
|
|
),
|
|
( if
|
|
Trace = no,
|
|
Resume = no,
|
|
Return = no
|
|
then
|
|
MaybeVarInfo = no_var_info
|
|
else
|
|
% 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.
|
|
( if LabelNumber0 < (1 << 16) then
|
|
LabelNumber = LabelNumber0
|
|
else
|
|
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_int(int_type_uint)),
|
|
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) :-
|
|
( if LiveType = live_value_var(_, NamePrime, _, _) then
|
|
Name = NamePrime
|
|
else
|
|
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,
|
|
( if TVarNum = CurSlot then
|
|
( if set.remove_least(LeastLocn, Locns, _) then
|
|
Locn = LeastLocn
|
|
else
|
|
unexpected($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_int(int_type_uint)) | VectorTail]
|
|
else if TVarNum > CurSlot then
|
|
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_int(int_type_uint)) | VectorTail]
|
|
else
|
|
unexpected($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), $pred,
|
|
"NumPTIs != NumHLDSVarNums"),
|
|
expect(unify(NumPTIs, NumLongLocns + NumShortLocns), $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,
|
|
( if NumPTIs > 0 then
|
|
AllRevPTIs0 = !.LabelLayoutInfo ^ lli_rev_ptis,
|
|
NextPTISlot0 = !.LabelLayoutInfo ^ lli_next_pti,
|
|
list.reverse(PTIs, RevPTIs),
|
|
( if
|
|
some [CompressionLimit] (
|
|
CompressArrays = yes(CompressionLimit),
|
|
!.LabelLayoutInfo ^ lli_next_pti =< CompressionLimit
|
|
),
|
|
find_sequence(RevPTIs, AllRevPTIs0, 0, OldPTIOffset)
|
|
then
|
|
PTISlot = NextPTISlot0 - OldPTIOffset - NumPTIs
|
|
else
|
|
AllRevPTIs = RevPTIs ++ AllRevPTIs0,
|
|
!LabelLayoutInfo ^ lli_rev_ptis := AllRevPTIs,
|
|
|
|
PTISlot = NextPTISlot0,
|
|
NextPTISlot = NextPTISlot0 + NumPTIs,
|
|
!LabelLayoutInfo ^ lli_next_pti := NextPTISlot
|
|
)
|
|
else
|
|
PTISlot = -1
|
|
),
|
|
% The garbage collector does not need HLDS variable numbers; only the
|
|
% debugger does.
|
|
( if
|
|
NumHLDSVarNums > 0,
|
|
Params ^ slp_trace_stack_layout = yes
|
|
then
|
|
AllRevHLDSVarNums0 = !.LabelLayoutInfo ^ lli_rev_hlds_var_nums,
|
|
NextHLDSVarNumSlot0 = !.LabelLayoutInfo ^ lli_next_hlds_var_num,
|
|
list.reverse(HLDSVarNums, RevHLDSVarNums),
|
|
( if
|
|
some [CompressionLimit] (
|
|
CompressArrays = yes(CompressionLimit),
|
|
!.LabelLayoutInfo ^ lli_next_hlds_var_num =< CompressionLimit
|
|
),
|
|
find_sequence(RevHLDSVarNums, AllRevHLDSVarNums0,
|
|
0, OldHLDSVarNumsOffset)
|
|
then
|
|
HLDSVarNumSlot = NextHLDSVarNumSlot0 - OldHLDSVarNumsOffset -
|
|
NumHLDSVarNums
|
|
else
|
|
AllRevHLDSVarNums = RevHLDSVarNums ++ AllRevHLDSVarNums0,
|
|
!LabelLayoutInfo ^ lli_rev_hlds_var_nums := AllRevHLDSVarNums,
|
|
|
|
HLDSVarNumSlot = NextHLDSVarNumSlot0,
|
|
NextHLDSVarNumSlot = NextHLDSVarNumSlot0 + NumHLDSVarNums,
|
|
!LabelLayoutInfo ^ lli_next_hlds_var_num := NextHLDSVarNumSlot
|
|
)
|
|
else
|
|
HLDSVarNumSlot = -1
|
|
),
|
|
( if NumShortLocns > 0 then
|
|
AllRevShortLocns0 = !.LabelLayoutInfo ^ lli_rev_short_locns,
|
|
NextShortLocnSlot0 = !.LabelLayoutInfo ^ lli_next_short_locn,
|
|
list.reverse(ShortLocns, RevShortLocns),
|
|
( if
|
|
some [CompressionLimit] (
|
|
CompressArrays = yes(CompressionLimit),
|
|
!.LabelLayoutInfo ^ lli_next_short_locn =< CompressionLimit
|
|
),
|
|
find_sequence(RevShortLocns, AllRevShortLocns0,
|
|
0, OldShortLocnsOffset)
|
|
then
|
|
ShortLocnSlot = NextShortLocnSlot0 - OldShortLocnsOffset -
|
|
NumShortLocns
|
|
else
|
|
AllRevShortLocns = RevShortLocns ++ AllRevShortLocns0,
|
|
!LabelLayoutInfo ^ lli_rev_short_locns := AllRevShortLocns,
|
|
|
|
ShortLocnSlot = NextShortLocnSlot0,
|
|
NextShortLocnSlot = NextShortLocnSlot0 + NumShortLocns,
|
|
!LabelLayoutInfo ^ lli_next_short_locn := NextShortLocnSlot
|
|
)
|
|
else
|
|
ShortLocnSlot = -1
|
|
),
|
|
( if NumLongLocns > 0 then
|
|
AllRevLongLocns0 = !.LabelLayoutInfo ^ lli_rev_long_locns,
|
|
NextLongLocnSlot0 = !.LabelLayoutInfo ^ lli_next_long_locn,
|
|
list.reverse(LongLocns, RevLongLocns),
|
|
( if
|
|
some [CompressionLimit] (
|
|
CompressArrays = yes(CompressionLimit),
|
|
!.LabelLayoutInfo ^ lli_next_long_locn =< CompressionLimit
|
|
),
|
|
find_sequence(RevLongLocns, AllRevLongLocns0,
|
|
0, OldLongLocnsOffset)
|
|
then
|
|
LongLocnSlot = NextLongLocnSlot0 - OldLongLocnsOffset -
|
|
NumLongLocns
|
|
else
|
|
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)
|
|
else
|
|
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),
|
|
( if
|
|
LiveValueType = live_value_var(_, _, Type, _),
|
|
is_type_a_dummy(ModuleInfo, Type) = is_dummy_type,
|
|
% We want to preserve I/O states in registers.
|
|
not (
|
|
Locn = locn_direct(reg(_, _))
|
|
)
|
|
then
|
|
unexpected($pred, "unexpected reference to dummy value")
|
|
else if
|
|
BytesSoFar < BytesLimit,
|
|
represent_locn_as_byte(Locn, ShortLocn)
|
|
then
|
|
ArraySlot = liveval_array_slot(TypeRval, VarNum, ShortLocn),
|
|
construct_liveval_array_slots(VarInfos, ModuleInfo, VarNumMap,
|
|
BytesSoFar + 1, BytesLimit, LongArraySlots, ShortArraySlots0,
|
|
!StaticCellInfo),
|
|
ShortArraySlots = [ArraySlot | ShortArraySlots0]
|
|
else
|
|
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) :-
|
|
( if map.is_empty(TVarLocnMap) then
|
|
TypeParamRval = const(llconst_int(0))
|
|
else
|
|
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_int(int_type_uint)) | 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_int(int_type_int)),
|
|
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_int(int_type_uint)
|
|
;
|
|
LvalOrConst = const(_Const),
|
|
UnboxedFloats = Params ^ slp_unboxed_floats,
|
|
UnboxedInt64s = Params ^ slp_unboxed_int64s,
|
|
NumWords = one_word,
|
|
LLDSType = rval_type_as_arg(UnboxedFloats, UnboxedInt64s, NumWords,
|
|
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 = cast(_, _)
|
|
; LvalOrConst = unop(_, _)
|
|
; LvalOrConst = binop(_, _, _)
|
|
; LvalOrConst = mem_addr(_)
|
|
; LvalOrConst = var(_)
|
|
; LvalOrConst = mkword_hole(_)
|
|
),
|
|
unexpected($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, $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, $pred, "bad stackvar"),
|
|
make_tagged_word(lval_stackvar, Num, Word).
|
|
represent_lval(parent_stackvar(Num), Word) :-
|
|
expect(Num > 0, $pred, "bad parent_stackvar"),
|
|
make_tagged_word(lval_parent_stackvar, Num, Word).
|
|
represent_lval(framevar(Num), Word) :-
|
|
expect(Num > 0, $pred, "bad framevar"),
|
|
make_tagged_word(lval_framevar, Num, Word).
|
|
represent_lval(double_stackvar(StackType, Num), Word) :-
|
|
expect(Num > 0, $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($pred, "continuation live value stored in temp register").
|
|
|
|
represent_lval(succip_slot(_), _) :-
|
|
unexpected($pred, "continuation live value stored in fixed slot").
|
|
represent_lval(redoip_slot(_), _) :-
|
|
unexpected($pred, "continuation live value stored in fixed slot").
|
|
represent_lval(redofr_slot(_), _) :-
|
|
unexpected($pred, "continuation live value stored in fixed slot").
|
|
represent_lval(succfr_slot(_), _) :-
|
|
unexpected($pred, "continuation live value stored in fixed slot").
|
|
represent_lval(prevfr_slot(_), _) :-
|
|
unexpected($pred, "continuation live value stored in fixed slot").
|
|
|
|
represent_lval(field(_, _, _), _) :-
|
|
unexpected($pred, "continuation live value stored in field").
|
|
represent_lval(mem_ref(_), _) :-
|
|
unexpected($pred, "continuation live value stored in mem_ref").
|
|
represent_lval(global_var_ref(_), _) :-
|
|
unexpected($pred,
|
|
"continuation live value stored in global_var_ref").
|
|
represent_lval(lvar(_), _) :-
|
|
unexpected($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, $pred, "bad reg"),
|
|
make_tagged_byte(0, Num, Byte).
|
|
represent_lval_as_byte(stackvar(Num), Byte) :-
|
|
expect(Num > 0, $pred, "bad stackvar"),
|
|
make_tagged_byte(1, Num, Byte).
|
|
represent_lval_as_byte(parent_stackvar(Num), Byte) :-
|
|
expect(Num > 0, $pred, "bad parent_stackvar"),
|
|
make_tagged_byte(1, Num, Byte). % XXX placeholder only
|
|
represent_lval_as_byte(framevar(Num), Byte) :-
|
|
expect(Num > 0, $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) :-
|
|
( if find_sequence_attempt(Search, [Head | Tail]) then
|
|
FoundAtOffset = CurOffset
|
|
else
|
|
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,
|
|
|
|
% Do we have unboxed 64-bit integer types?
|
|
slp_unboxed_int64s :: have_unboxed_int64s
|
|
).
|
|
|
|
:- 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),
|
|
globals.lookup_bool_option(Globals, unboxed_int64s, UnboxedInt64sOpt),
|
|
(
|
|
UnboxedFloatOpt = no,
|
|
UnboxedFloat = do_not_have_unboxed_floats
|
|
;
|
|
UnboxedFloatOpt = yes,
|
|
UnboxedFloat = have_unboxed_floats
|
|
),
|
|
(
|
|
CommonLayoutData = no,
|
|
CompressArrays = no
|
|
;
|
|
CommonLayoutData = yes,
|
|
CompressArrays = yes(CompressLimit)
|
|
),
|
|
(
|
|
UnboxedInt64sOpt = no,
|
|
UnboxedInt64s = do_not_have_unboxed_int64s
|
|
;
|
|
UnboxedInt64sOpt = yes,
|
|
UnboxedInt64s = have_unboxed_int64s
|
|
),
|
|
Params = stack_layout_params(ModuleInfo, TraceLevel, TraceSuppress,
|
|
DeepProfiling, AgcLayout, TraceLayout, ProcIdLayout, CompressArrays,
|
|
StaticCodeAddr, UnboxedFloat, RttiLineNumbers, UnboxedInt64s).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module ll_backend.stack_layout.
|
|
%---------------------------------------------------------------------------%
|