mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-18 07:15:19 +00:00
Estimated hours taken: 120 Branches: main The algorithm that decides whether the order independent state update transformation is applicable in a given module needs access to the list of oisu pragmas in that module, and to information about the types of variables in the procedures named in those pragmas. This diff puts this information in Deep.procrep files, to make them available to the autoparallelization feedback program, to which that algorithm will later be added. Compilers that have this diff will generate Deep.procrep files in a new, slightly different format, but the deep profiler will be able to read Deep.procrep files not just in the new format, but in the old format as well. runtime/mercury_stack_layout.h: Add to module layout structures the fields holding the new information we want to put into Deep.procrep files. This means three things: - a bytecode array in module layout structures encoding the list of oisu pragmas in the module; - additions to the bytecode arrays in procedure layout structures mapping the procedure's variables to their types; and - a bytecode array containing the encoded versions of those types themselves in the module layout structure. This allows us to represent each type used in the module just once. Since there is now information in module layout structures that is needed only for deep profiling, as well as information that is needed only for debugging, the old arrangement that split a module's information between two structures, MR_ModuleLayout (debug specific info) and MR_ModuleCommonLayout (info used by both debugging and profiling), is no longer approriate. We could add a third structure containing profiling-specific info, but it is simpler to move all the info into just one structure, some of whose fields may not be used. This wastes only a few words of memory per module, but allows the runtime system to avoid unnecessary indirections. runtime/mercury_types.h: Remove the type synonym for the deleted type. runtime/mercury_grade.h: The change in mercury_stack_layout.h destroys binary compatibility with previous versions of Mercury for debug and deep profiling grades, so bump their grade-component-specific version numbers. runtime/mercury_deep_profiling.c: Write out the information in the new fields in module layout structures, if they are filled in. Since this changes the format of the Deep.procrep file, bump its version number. runtime/mercury_deep_profiling.h: runtime/mercury_stack_layout.c: Conform to the change to mercury_stack_layout.h. mdbcomp/program_representation.m: Add to module representations information about the oisu pragmas defined in that module, and the type table of the module. Optionally add to procedure representations a map mapping the variables of the procedure to their types. Rename the old var_table type to be the var_name_table type, since it contains just names. Make the var to type map separate, since it will be there only for selected procedures. Modify the predicates reading in module and procedure representations to allow them to read in the new representation, while still accepting the old one. Use the version number in the Deep.procrep file to decide which format to expect. mdbcomp/rtti_access.m: Add functions to encode the data representations that this module also decodes. Conform to the changes above. mdbcomp/feedback.automatic_parallelism.m: Conform the changes above. mdbcomp/prim_data.m: Fix layout. compiler/layout.m: Update the compiler's representation of layout structures to conform to the change to runtime/mercury_stack_layout.h. compiler/layout_out.m: Output the new parts of module layout structures. compiler/opt_debug.m: Allow the debugging of code referring to the new parts of module layout structures. compiler/llds_out_file.m: Conform to the move to a single module layout structure. compiler/prog_rep_tables.m: This new module provided mechanisms for building the string table and the type table components of module layouts. The string table part is old (it is moved here from stack_layout.m); the type table part is new. Putting this code in a module of its own allows us to remove a circular dependency between prog_rep.m and stack_layout.m; instead, both now just depend on prog_rep_tables.m. compiler/ll_backend.m: Add the new module. compiler/notes/compiler_design.html: Describe the new module. compiler/prog_rep.m: When generating the representation of a module for deep profiling, include the information needed by the order independent state update analysis: the list of oisu pragmas in the module, if any, and information about the types of variables in selected procedures. To avoid having these additions increasing the size of the bytecode representation too much, convert some fixed 32 bit numbers in the bytecode to use variable sized numbers, which will usually be 8 or 16 bits. Do not use predicates from bytecode_gen.m to encode numbers, since there is nothing keeping these in sync with the code that reads them in mdbcomp/program_representation.m. Instead, use new predicates in program_representation.m itself. compiler/stack_layout.m: Generate the new parts of module layouts. Remove the code moved to prog_rep_tables.m. compiler/continuation_info.m: compiler/proc_gen.m: Make some more information available to stack_layout.m. compiler/prog_data.m: Fix some formatting. compiler/introduce_parallelism.m: Conform to the renaming of the var_table type. compiler/follow_code.m: Fix the bug that used to cause the failure of the hard_coded/mode_check_clauses test case in deep profiling grades. deep_profiler/program_representation_utils.m: Output the new parts of module and procedure representations, to allow the correctness of this change to be tested. deep_profiler/mdprof_create_feedback.m: If we cannot read the Deep.procrep file, print a single error message and exit, instead of continuing with an analysis that will generate a whole bunch of error messages, one for each attempt to access a procedure's representation. deep_profiler/mdprof_procrep.m: Give this program an option that specifies what file it is to look at; do not hardwire in "Deep.procrep" in the current directory. deep_profiler/report.m: Add a report type that just prints the representation of a module. It returns the same information as mdprof_procrep, but from within the deep profiler, which can be more convenient. deep_profiler/create_report.m: deep_profiler/display_report.m: Respectively create and display the new report type. deep_profiler/query.m: Recognize a query asking for the new report type. deep_profiler/autopar_calc_overlap.m: deep_profiler/autopar_find_best_par.m: deep_profiler/autopar_reports.m: deep_profiler/autopar_search_callgraph.m: deep_profiler/autopar_search_goals.m: deep_profiler/autopar_types.m: deep_profiler/branch_and_bound.m: deep_profiler/coverage.m: deep_profiler/display.m: deep_profiler/html_format.m: deep_profiler/mdprof_test.m: deep_profiler/measurements.m: deep_profiler/query.m: deep_profiler/read_profile.m: deep_profiler/recursion_patterns.m: deep_profiler/top_procs.m: deep_profiler/top_procs.m: Conform to the changes above. Fix layout. tests/debugger/declarative/dependency.exp2: Add this file as a possible expected output. It contains the new field added to module representations.
403 lines
15 KiB
Mathematica
403 lines
15 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 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: prog_rep_tables.m.
|
|
% Author: zs.
|
|
%
|
|
% This module contains predicates to build the tables included in program
|
|
% representations for debugging and/or deep profiling (mostly the latter).
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module ll_backend.prog_rep_tables.
|
|
:- interface.
|
|
|
|
:- import_module parse_tree.prog_data.
|
|
|
|
:- import_module list.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- type string_table_info.
|
|
|
|
:- func init_string_table_info = string_table_info.
|
|
|
|
:- pred lookup_string_in_table(string::in, int::out,
|
|
string_table_info::in, string_table_info::out) is det.
|
|
|
|
:- pred get_string_table_contents(string_table_info::in,
|
|
list(string)::out, int::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- type type_table_info.
|
|
|
|
:- func init_type_table_info = type_table_info.
|
|
|
|
:- pred lookup_type_in_table(mer_type::in, int::out,
|
|
string_table_info::in, string_table_info::out,
|
|
type_table_info::in, type_table_info::out) is det.
|
|
|
|
:- pred get_type_table_contents(type_table_info::in, int::out,
|
|
list(int)::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module mdbcomp.
|
|
:- import_module mdbcomp.prim_data.
|
|
:- import_module mdbcomp.rtti_access.
|
|
|
|
:- import_module char.
|
|
:- import_module cord.
|
|
:- import_module int.
|
|
:- import_module map.
|
|
:- import_module maybe.
|
|
:- import_module require.
|
|
:- import_module string.
|
|
:- import_module term.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- type string_table_info
|
|
---> string_table_info(
|
|
% Maps strings to their offsets.
|
|
map(string, int),
|
|
|
|
% The list of strings so far, in reverse order.
|
|
list(string),
|
|
|
|
% The next available offset.
|
|
int
|
|
).
|
|
|
|
init_string_table_info = !:StringTable :-
|
|
map.init(StringMap0),
|
|
!:StringTable = string_table_info(StringMap0, [], 0),
|
|
lookup_string_in_table("", _, !StringTable),
|
|
lookup_string_in_table("<too many variables>", _, !StringTable).
|
|
|
|
lookup_string_in_table(String, StringCode, !StringTable) :-
|
|
% The encoding used here is decoded by MR_name_in_string_table
|
|
% in runtime/mercury_stack_layout.c. The code here and there
|
|
% must be kept in sync.
|
|
(
|
|
is_var_name_in_special_form(String, KindCode, MaybeBaseName, N),
|
|
N < int.unchecked_left_shift(1, 10),
|
|
(
|
|
MaybeBaseName = yes(BaseName),
|
|
lookup_raw_string_in_table(BaseName, MaybeOffset, !StringTable),
|
|
MaybeOffset = yes(Offset),
|
|
Offset < int.unchecked_left_shift(1, 16)
|
|
;
|
|
MaybeBaseName = no,
|
|
Offset = 0
|
|
)
|
|
->
|
|
% | ... offset ... | ... N ... | Kind | 1 |
|
|
% special form indication: 1 bit: bit 0
|
|
% kind indication: 5 bits: bits 1-5
|
|
% N: 10 bits: bits 6-15
|
|
% Offset: 16 bits: bits 16-31
|
|
StringCode = 1 \/
|
|
int.unchecked_left_shift(KindCode, 1) \/
|
|
int.unchecked_left_shift(N, 6) \/
|
|
int.unchecked_left_shift(Offset, 16)
|
|
;
|
|
lookup_raw_string_in_table(String, MaybeOffset, !StringTable),
|
|
(
|
|
MaybeOffset = yes(Offset)
|
|
;
|
|
MaybeOffset = no,
|
|
% Says that the name of the variable is "TOO_MANY_VARIABLES".
|
|
Offset = 1
|
|
),
|
|
StringCode = int.unchecked_left_shift(Offset, 1)
|
|
).
|
|
|
|
:- pred is_var_name_in_special_form(string::in,
|
|
int::out, maybe(string)::out, int::out) is semidet.
|
|
|
|
is_var_name_in_special_form(String, KindCode, MaybeBaseName, N) :-
|
|
% state_var.m constructs variable names that always contain
|
|
% the state var name, and usually but not always a numeric suffix.
|
|
% The numeric suffic may be zero or positive. We could represent
|
|
% the lack of a suffix using a negative number, but mixing unsigned
|
|
% and signed fields in a single word is tricky, especially since
|
|
% the size of the variable name descriptor word we generate (32 bits)
|
|
% may or may not be the same as the word size of the compiler.
|
|
% Instead, we simply add one to any actual suffix values, and use
|
|
% zero to represent the absence of a numeric suffix.
|
|
|
|
% polymorphism.m adds a numeric suffix but no type name
|
|
% to type_ctor_infos and type_infos.
|
|
|
|
% polymorphism.m adds a class id but no numeric suffix to
|
|
% base_typeclass_infos and typeclass_infos. Since the usual string table
|
|
% already does a good enough job for these, the code for handling them
|
|
% specially is commented out.
|
|
|
|
( string.remove_prefix("STATE_VARIABLE_", String, NoPrefix) ->
|
|
KindCode = 0,
|
|
string.to_char_list(NoPrefix, NoPrefixChars),
|
|
( find_number_suffix(NoPrefixChars, BaseNameChars, Num) ->
|
|
string.from_char_list(BaseNameChars, BaseName),
|
|
MaybeBaseName = yes(BaseName),
|
|
N = Num + 1
|
|
;
|
|
MaybeBaseName = yes(NoPrefix),
|
|
N = 0
|
|
)
|
|
; string.remove_prefix("TypeCtorInfo_", String, NoPrefix) ->
|
|
( string.to_int(NoPrefix, Num) ->
|
|
KindCode = 1,
|
|
MaybeBaseName = no,
|
|
N = Num
|
|
;
|
|
fail
|
|
)
|
|
; string.remove_prefix("TypeInfo_", String, NoPrefix) ->
|
|
( string.to_int(NoPrefix, Num) ->
|
|
KindCode = 2,
|
|
MaybeBaseName = no,
|
|
N = Num
|
|
;
|
|
fail
|
|
)
|
|
% ; string.remove_prefix("BaseTypeClassInfo_for_", String, NoPrefix) ->
|
|
% KindCode = 3,
|
|
% MaybeBaseName = yes(NoPrefix),
|
|
% N = 0
|
|
% ; string.remove_prefix("TypeClassInfo_for_", String, NoPrefix) ->
|
|
% KindCode = 4,
|
|
% MaybeBaseName = yes(NoPrefix),
|
|
% N = 0
|
|
; string.remove_prefix("PolyConst", String, NoPrefix) ->
|
|
( string.to_int(NoPrefix, Num) ->
|
|
KindCode = 5,
|
|
MaybeBaseName = no,
|
|
N = Num
|
|
;
|
|
fail
|
|
)
|
|
;
|
|
fail
|
|
).
|
|
|
|
% Given e.g. "Info_15" as input, we return "Info" as BeforeNum
|
|
% and 15 as Num.
|
|
%
|
|
:- pred find_number_suffix(list(char)::in, list(char)::out, int::out)
|
|
is semidet.
|
|
|
|
find_number_suffix(String, BeforeNum, Num) :-
|
|
list.reverse(String, RevString),
|
|
rev_find_number_suffix(RevString, 0, Num, 1, Scale, RevRest),
|
|
Scale > 1,
|
|
list.reverse(RevRest, BeforeNum).
|
|
|
|
:- pred rev_find_number_suffix(list(char)::in, int::in, int::out,
|
|
int::in, int::out, list(char)::out) is semidet.
|
|
|
|
rev_find_number_suffix([RevHead | RevTail], !Num, !Scale, RevRest) :-
|
|
( char.digit_to_int(RevHead, Digit) ->
|
|
!:Num = !.Num + (!.Scale * Digit),
|
|
!:Scale = !.Scale * 10,
|
|
rev_find_number_suffix(RevTail, !Num, !Scale, RevRest)
|
|
; RevHead = '_' ->
|
|
RevRest = RevTail
|
|
;
|
|
fail
|
|
).
|
|
|
|
:- pred lookup_raw_string_in_table(string::in, maybe(int)::out,
|
|
string_table_info::in, string_table_info::out) is det.
|
|
|
|
lookup_raw_string_in_table(String, MaybeOffset, !StringTable) :-
|
|
!.StringTable = string_table_info(TableMap0, TableList0, TableOffset0),
|
|
( map.search(TableMap0, String, OldOffset) ->
|
|
MaybeOffset = yes(OldOffset)
|
|
;
|
|
Length = string.count_utf8_code_units(String),
|
|
TableOffset = TableOffset0 + Length + 1,
|
|
% We use a 32 bit unsigned integer to represent the offset. Computing
|
|
% that limit exactly without getting an overflow or using unportable
|
|
% code isn't trivial. The code below is overly conservative, requiring
|
|
% the offset to be representable in only 30 bits. The over-conservatism
|
|
% should not be an issue; the machine will run out of virtual memory
|
|
% before the test below fails, for the next several years anyway.
|
|
% (Compiling a module that has a 1 Gb string table will require
|
|
% several tens of Gb of other compiler structures.)
|
|
TableOffset < (1 << 30)
|
|
->
|
|
MaybeOffset = yes(TableOffset0),
|
|
map.det_insert(String, TableOffset0, TableMap0, TableMap),
|
|
TableList = [String | TableList0],
|
|
!:StringTable = string_table_info(TableMap, TableList, TableOffset)
|
|
;
|
|
MaybeOffset = no
|
|
).
|
|
|
|
get_string_table_contents(StringTable, Strings, StringTableSize) :-
|
|
StringTable = string_table_info(_, RevStrings, StringTableSize),
|
|
list.reverse(RevStrings, Strings).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% The type table data structure.
|
|
%
|
|
|
|
:- type type_table_info
|
|
---> type_table_info(
|
|
% Maps the types in the type table to their representations.
|
|
map(mer_type, int),
|
|
|
|
% The bytecode representing the types in the first field.
|
|
cord(int),
|
|
|
|
% The next available type number.
|
|
int
|
|
).
|
|
|
|
init_type_table_info = type_table_info(map.init, cord.init, 0).
|
|
|
|
lookup_type_in_table(Type, TypeCode, !StringTable, !TypeTable) :-
|
|
!.TypeTable = type_table_info(TypeMap0, _, _),
|
|
( map.search(TypeMap0, Type, TypeCodePrime) ->
|
|
TypeCode = TypeCodePrime
|
|
;
|
|
add_type_to_table(Type, TypeCode, !StringTable, !TypeTable)
|
|
).
|
|
|
|
:- pred add_type_to_table(mer_type::in, int::out,
|
|
string_table_info::in, string_table_info::out,
|
|
type_table_info::in, type_table_info::out) is det.
|
|
|
|
add_type_to_table(Type, TypeCode, !StringTable, !TypeTable) :-
|
|
% The encoding used here is decoded by read_encoded_type in mdbcomp/
|
|
% program_representation.m. The code here and there must be kept in sync.
|
|
(
|
|
Type = defined_type(TypeCtorSymName, ArgTypes, _Kind),
|
|
list.map_foldl2(lookup_type_in_table, ArgTypes, ArgTypeCodes,
|
|
!StringTable, !TypeTable),
|
|
TypeCtorSymNameStr = sym_name_to_string(TypeCtorSymName),
|
|
lookup_string_in_table(TypeCtorSymNameStr, TypeCtorSymNameCode,
|
|
!StringTable),
|
|
encode_int32_det(TypeCtorSymNameCode, TypeCtorSymNameBytes),
|
|
(
|
|
ArgTypeCodes = [],
|
|
Selector = 0,
|
|
ArgTypeBytesCord = cord.init
|
|
;
|
|
ArgTypeCodes = [ArgTypeCode1],
|
|
Selector = 1,
|
|
ArgTypeBytesCord = cord.from_list(encode_num_func(ArgTypeCode1))
|
|
;
|
|
ArgTypeCodes = [ArgTypeCode1, ArgTypeCode2],
|
|
Selector = 2,
|
|
ArgTypeBytesCord = cord.from_list(encode_num_func(ArgTypeCode1))
|
|
++ cord.from_list(encode_num_func(ArgTypeCode2))
|
|
;
|
|
ArgTypeCodes = [ArgTypeCode1, ArgTypeCode2, ArgTypeCode3],
|
|
Selector = 3,
|
|
ArgTypeBytesCord = cord.from_list(encode_num_func(ArgTypeCode1))
|
|
++ cord.from_list(encode_num_func(ArgTypeCode2))
|
|
++ cord.from_list(encode_num_func(ArgTypeCode3))
|
|
;
|
|
ArgTypeCodes = [_, _, _, _ | _],
|
|
Selector = 4,
|
|
encode_arg_type_codes(ArgTypeCodes, ArgTypeBytesCord)
|
|
),
|
|
TypeBytesCord =
|
|
cord.from_list([Selector | TypeCtorSymNameBytes])
|
|
++ ArgTypeBytesCord
|
|
;
|
|
Type = builtin_type(BuiltinType),
|
|
(
|
|
BuiltinType = builtin_type_int,
|
|
Selector = 5
|
|
;
|
|
BuiltinType = builtin_type_float,
|
|
Selector = 6
|
|
;
|
|
BuiltinType = builtin_type_string,
|
|
Selector = 7
|
|
;
|
|
BuiltinType = builtin_type_char,
|
|
Selector = 8
|
|
),
|
|
TypeBytesCord = cord.singleton(Selector)
|
|
;
|
|
Type = tuple_type(ArgTypes, _Kind),
|
|
Selector = 9,
|
|
list.map_foldl2(lookup_type_in_table, ArgTypes, ArgTypeCodes,
|
|
!StringTable, !TypeTable),
|
|
encode_arg_type_codes(ArgTypeCodes, ArgTypeBytesCord),
|
|
TypeBytesCord = cord.singleton(Selector) ++ ArgTypeBytesCord
|
|
;
|
|
Type = higher_order_type(ArgTypes, MaybeReturnType,
|
|
_Purity, _EvalMethod),
|
|
list.map_foldl2(lookup_type_in_table, ArgTypes, ArgTypeCodes,
|
|
!StringTable, !TypeTable),
|
|
encode_arg_type_codes(ArgTypeCodes, ArgTypeBytesCord),
|
|
(
|
|
MaybeReturnType = no,
|
|
Selector = 10,
|
|
TypeBytesCord = cord.singleton(Selector)
|
|
++ ArgTypeBytesCord
|
|
;
|
|
MaybeReturnType = yes(ReturnType),
|
|
Selector = 11,
|
|
lookup_type_in_table(ReturnType, ReturnTypeCode,
|
|
!StringTable, !TypeTable),
|
|
encode_num_det(ReturnTypeCode, ReturnTypeBytes),
|
|
TypeBytesCord = cord.singleton(Selector)
|
|
++ ArgTypeBytesCord
|
|
++ cord.from_list(ReturnTypeBytes)
|
|
)
|
|
;
|
|
Type = apply_n_type(_TVar, _ArgTypes, _Kind),
|
|
unexpected($module, $pred, "apply_n_type")
|
|
;
|
|
Type = kinded_type(_Kind, _SubType),
|
|
unexpected($module, $pred, "kinded_type")
|
|
;
|
|
Type = type_variable(TVar, _Kind),
|
|
Selector = 12,
|
|
var_to_int(TVar, TVarNum),
|
|
encode_num_det(TVarNum, TVarNumBytes),
|
|
TypeBytesCord = cord.singleton(Selector) ++
|
|
cord.from_list(TVarNumBytes)
|
|
),
|
|
!.TypeTable = type_table_info(TypeMap0, TypeTableCord0, NextTypeNum0),
|
|
TypeCode = NextTypeNum0,
|
|
NextTypeNum = NextTypeNum0 + 1,
|
|
map.det_insert(Type, TypeCode, TypeMap0, TypeMap),
|
|
TypeTableCord = TypeTableCord0 ++ TypeBytesCord,
|
|
!:TypeTable = type_table_info(TypeMap, TypeTableCord, NextTypeNum).
|
|
|
|
:- pred encode_arg_type_codes(list(int)::in, cord(int)::out) is det.
|
|
|
|
encode_arg_type_codes(ArgTypeCodes, ArgTypeBytesCord) :-
|
|
list.map(encode_num_det, ArgTypeCodes, ArgTypeByteLists),
|
|
ArgTypeByteCords = list.map(cord.from_list, ArgTypeByteLists),
|
|
ArgTypeBytesCord0 = cord.cord_list_to_cord(ArgTypeByteCords),
|
|
list.length(ArgTypeCodes, NumArgTypeCodes),
|
|
ArgTypeBytesCord = cord.from_list(encode_num_func(NumArgTypeCodes))
|
|
++ ArgTypeBytesCord0.
|
|
|
|
get_type_table_contents(TypeTable, NumTypes, TypeBytes) :-
|
|
TypeTable = type_table_info(_, TypeBytesCord, NumTypes),
|
|
TypeBytes = cord.list(TypeBytesCord).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module ll_backend.prog_rep_tables.
|
|
%---------------------------------------------------------------------------%
|