mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-17 14:57:03 +00:00
Estimated hours taken: 500 Branches: main Implement deep profiling; merge the changes on the deep2 branch back onto the trunk. The main documentation on the general architecture of the deep profiler is the deep profiling paper. doc/user_guide.texi: Document how to use the deep profiler. deep_profiler: deep_profiler/Mmakefile: A new directory holding the deep profiler and its mmakefile. Mmakefile: Add targets for the new directory. Add support for removing inappropriate files from directories. deep_profiler/interface.m: The deep profiler consists of two programs: mdprof_cgi.m, which acts as a CGI "script", and mdprof_server.m, which implements the server process that the CGI script talks to. Interface.m defines the interface between them. script/mdprof.in: A shell script template. ../configure uses it to generate mdprof, which is a wrapper around mdprof_cgi that tells it how to find mdprof_server. deep_profiler/mdprof_cgi.m: The CGI "script" program. deep_profiler/mdprof_server.m: The top level predicates of the server. deep_profiler/profile.m: The main data structures of the server and their operations. deep_profiler/read_profile.m: Code for reading in profiling data files. deep_profiler/startup.m: Code for post-processing the information in profiling data files, propagating costs from procedures to their ancestors and performing various kinds of summaries. deep_profiler/server.m: Code for responding to requests from the CGI script. deep_profiler/cliques.m: Code to find cliques in graphs. deep_profiler/array_util.m: deep_profiler/util.m: Utility predicates. deep_profiler/dense_bitset.m: An implementation of (part of) the set ADT with dense bit vectors. deep_profiler/measurements.m: Operations on profiling measurements. deep_profiler/timeout.m: An implementation of a timeout facility. deep_profiler/conf.m: Functions that depend on autoconfigured settings. configure.in: Find out what command to use to find the name of the local host. Install deep profiling versions of the standard library along with the other profiling versions. runtime/mercury_conf.h.in: Add some macros for deep_profiler/conf.m to use. library/profiling_builtin.m: runtime/mercury_deep_call_port_body.h: runtime/mercury_deep_leave_port_body.h: runtime/mercury_deep_redo_port_body.h: A new library module that implements deep profiling primitives. Some of these primitives have many versions, whose common code is factor is factored out in three new include files in the runtime. compiler/deep_profiling.m: New module to perform the program transformations described in the paper. compiler/notes/compiler_design.html: Document the new compiler module. compiler/mercury_compiler.m: Invoke the new module in deep profiling grades. Allow global static data to be generated by deep_profiling.m. compiler/options.m: Add options to turn on deep profiling and (for benchmarking purposes) control its implementation. Add an optiooption disable tailcall optimization in the LLDS backend, to help benchmarking deep profiling. compiler/jumpopt.m: compiler/optimize.m: Obey the option to disable tailcalls. compiler/handle_options.m: Handle the implications of deep profiling. compiler/modules.m: In deep profiling grades, automatically import profiling_builtin.m. compiler/prog_util.m: doc/Makefile: library/library.m: Handle the new builtin module. compiler/export.m: In deep profiling grades, wrap deep profiling code around exported procedures to handle the "unscheduled call" aspects of callbacks to Mercury from the foreign language. compiler/higher_order.m: profiler/demangle.m: util/demangle.c: When creating a name for a higher-order-specialized predicate, include the mode number in the name. compiler/add_trail_ops.m: compiler/type_util.m: Move c_pointer_type from add_trail_ops to type_util, so it can also be used by deep_profiling.m. compiler/hlds_goal.m: Add a new goal feature that marks a tail call, for use by deep_profiling.m. compiler/hlds_pred.m: Add a new field to proc_info structures for use by deep_profiling.m. Add a mechanism for getting proc_ids for procedure clones. Remove next_proc_id, an obsolete and unused predicate. compiler/hlds_data.m: Add a new cons_id to refer to the proc_static structure of a procedure. compiler/bytecode_gen.m: compiler/code_util.m: compiler/dependency_graph.m: compiler/hlds_out.m: compiler/mercury_to_mercury.m: compiler/ml_unify_gen.m: compiler/opt_debug.m: compiler/prog_rep.m: compiler/rl_exprn.m: compiler/switch_util.m: compiler/unify_gen.m: Trivial changes to handle the new cons_id, goal feature and/or proc_info argument. compiler/rtti.m: Add a utility predicate for extracting pred_id and proc_id from an rtti_proc_label, for use by hlds_out.m compiler/layout.m: compiler/layout_out.m: compiler/llds.m: compiler/llds_common.m: Add support for proc_static and call_site_static structures. compiler/layout_out.m: compiler/llds_out.m: Add code for the output of proc_static structures. compiler/code_util.m: Make code_util__make_proc_label_from_rtti a function, and export it. util/mkinit.c: compiler/llds_out.m: compiler/layout.m: compiler/modules.m: Add support for a fourth per-module C function, for writing out proc_static structures (and the call_site_static structures they contains). Since proc_static structures can be referred to from LLDS code (and not just from other static structures and compiler-generated C code), reorganize the declarations of static structures slightly. Change the schema for the name of the first per-module C function slightly, to make it the addition of the fourth function easier. The scheme now is: mercury__<modulename>__init mercury__<modulename>__init_type_tables mercury__<modulename>__init_debugger mercury__<modulename>__write_out_proc_statics Improve formatting of the generated C code. library/*.m: runtime/mercury.c: runtime/mercury_context.c: runtime/mercury_engine.c: runtime/mercury_ho_call.c: runtime/mercury_tabling.c: runtime/mercury_trace_base.c: runtime/mercury_wrapper.c: trace/mercrury_trace.[ch]: trace/mercrury_trace_declarative.c: trace/mercrury_trace_external.c: trace/mercrury_trace_internal.c: Conform to the new scheme for initialization functions for hand-written modules. compiler/mercury_compile.m: library/benchmarking.m: runtime/mercury_conf_param.h: runtime/mercury.h: runtime/mercury_engine.c: runtime/mercury_goto.c: runtime/mercury_grade.h: runtime/mercury_ho_call.c: runtime/mercury_label.[ch]: runtime/mercury_prof.[ch]: Add an MR_MPROF_ prefix in front of the C macros used to control the old profiler. compiler/handle_options.m: runtime/mercury_grade.h: scripts/canonical_grade.sh-subr: scripts/init_grade_options.sh-subr: scripts/parse_grade_options.sh-subr: Make deep profiling completely separate from the old profiling system, by making the deep profiling grade independent of MR_MPROF_PROFILE_TIME and the compiler option --profile-time. library/array.m: library/builtin.m: library/std_util.m: runtime/mercury_hand_unify_body.h: runtime/mercury_hand_compare_body.h: In deep profiling grades, wrap the deep profiling call, exit, fail and redo codes around the bodies of hand-written unification and comparison procedures. Make the reporting of array bounds violations switchable between making them fatal errors, as we currently, and reporting them by throwing an exception. Throwing an exception makes debugging code using arrays easier, but since exceptions aren't (yet) propagated across engine boundaries, we keep the old behaviour as the default; the new behaviour is for implementors. runtime/mercury_deep_profiling_hand.h: New file that defines macros for use in Mercury predicates whose definition is in hand-written C code. library/exception.m: runtime/mercury_exception_catch_body.h: runtime/mercury_stacks.h: In deep profiling grades, wrap the deep profiling call, exit, fail and redo codes around the bodies of the various modes of builtin_catch. Provide a function that C code can use to throw exceptions. library/benchmarking.m: library/exception.m: library/gc.m: library/std_util.m: runtime/mercury_context.[ch]: runtime/mercury_engine.[ch]: runtime/mercury_debug.c: runtime/mercury_deep_copy.c: runtime/mercury_overflow.h: runtime/mercury_regs.h: runtime/mercury_stacks.h: runtime/mercury_thread.c: runtime/mercury_wrapper.c: Add prefixes to the names of the fields in the engine and context structures, to make code using them easier to understand and modify. runtime/mercury_deep_profiling.[ch]: New module containing support functions for deep profiling and functions for writing out a deep profiling data file at the end of execution. runtime/mercury_debug.[ch]: Add support for debugging deep profiling. Add support for watching the value at a given address. Make the buffered/unbuffered nature of debugging output controllable via the -du option. Print register contents only if -dr is specified. runtime/mercury_goto.h: runtime/mercury_std.h: Use the macros in mercury_std.h instead of defining local variants. runtime/mercury_goto.h: runtime/mercury_stack_layout.h: runtime/mercury_stack_trace.c: runtime/mercury_tabling.c: trace/mercury_trace.c: trace/mercury_trace_declarative.c: trace/mercury_trace_external.c: trace/mercury_trace_vars.c: Standardize some of the macro names with those used in the debugger paper. runtime/mercury_heap.h: Add support for memory profiling with the deep profiler. runtime/mercury_prof.[ch]: runtime/mercury_prof_time.[ch]: Move the functionality that both the old profiler and the deep profiler need into the new module mercury_prof_time. Leave mercury_prof containing stuff that is only relevant to the old profiler. runtime/mercury_prof.[ch]: runtime/mercury_strerror.[ch]: Move the definition of strerror from mercury_prof to its own file. runtime/mercury_wrapper.[ch]: Add support for deep profiling. Add suppory for controlling whether debugging output is buffered or not. Add support for watching the value at a given address. runtime/Mmakefile: Mention all the added files. scripts/mgnuc.in: Add an option for turning on deep profiling. Add options for controlling the details of deep profiling. These are not documented because they are intended only for benchmarking the deep profiler itself, for the paper; they are not for general use. tools/bootcheck: Compile the deep_profiler directory as well as the other directories containing Mercury code. Turn off the creation of deep profiling data files during bootcheck, since all but one of these in each directory will be overwritten anyway. Add support for turning on --keep-objs by default in a workspace. tools/speedtest: Preserve any deep profiling data files created by the tests. trace/mercury_trace.c: Trap attempts to perform retries in deep profiling grades, since they would lead to core dumps otherwise. util/Mmakefile: Avoid compile-time warnings when compiling getopt. tests/*/Mmakefile: tests/*/*/Mmakefile: In deep profiling grades, switch off the tests that test features that don't work with deep profiling, either by design or because the combination hasn't been implemented yet.
554 lines
20 KiB
Mathematica
554 lines
20 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 2000-2001 The University of Melbourne.
|
|
% This file may only be copied under the terms of the GNU General
|
|
% Public License - see the file COPYING in the Mercury distribution.
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% File: switch_util.m
|
|
% Author: fjh
|
|
%
|
|
% This module defines stuff for generating switches that is shared
|
|
% between the MLDS and LLDS back-ends.
|
|
%
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- module switch_util.
|
|
:- interface.
|
|
:- import_module prog_data, hlds_goal, hlds_data, hlds_module, type_util.
|
|
:- import_module list, assoc_list, map, std_util.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% Stuff for categorizing switches
|
|
%
|
|
|
|
% An extended_case is an HLDS case annotated with some additional info.
|
|
% The first (int) field is the priority, as computed by switch_priority/2.
|
|
:- type extended_case ---> case(int, cons_tag, cons_id, hlds_goal).
|
|
:- type cases_list == list(extended_case).
|
|
|
|
:- type switch_category
|
|
---> atomic_switch % a switch on int/char/enum
|
|
; string_switch
|
|
; tag_switch
|
|
; other_switch.
|
|
|
|
:- pred switch_util__type_cat_to_switch_cat(builtin_type, switch_category).
|
|
:- mode switch_util__type_cat_to_switch_cat(in, out) is det.
|
|
|
|
% Return the priority of a constructor test.
|
|
% A low number here indicates a high priority.
|
|
% We prioritize the tag tests so that the cheapest
|
|
% (most efficient) ones come first.
|
|
%
|
|
:- pred switch_util__switch_priority(cons_tag, int).
|
|
:- mode switch_util__switch_priority(in, out) is det.
|
|
|
|
% switch_util__type_range(TypeCategory, Type, ModuleInfo, Min, Max):
|
|
% Determine the range [Min..Max] of an atomic type.
|
|
% Fail if the type isn't the sort of type that has a range
|
|
% or if the type's range is too big to switch on (e.g. int).
|
|
%
|
|
:- pred switch_util__type_range(builtin_type, type, module_info, int, int).
|
|
:- mode switch_util__type_range(in, in, in, out, out) is semidet.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% Stuff for string hash switches
|
|
%
|
|
|
|
% for a string switch, compute the hash value for each case
|
|
% in the list of cases, and store the cases in a map
|
|
% from hash values to cases.
|
|
|
|
:- pred switch_util__string_hash_cases(cases_list, int,
|
|
map(int, cases_list)).
|
|
:- mode switch_util__string_hash_cases(in, in, out) is det.
|
|
|
|
% switch_util__calc_hash_slots(AssocList, HashMap, Map) :-
|
|
% For each (HashVal - Case) pair in AssocList,
|
|
% allocate a hash slot in Map for the case.
|
|
% If the hash slot corresponding to HashVal is not
|
|
% already used, then use that one. Otherwise, find
|
|
% the next spare slot (making sure that we don't
|
|
% use slots which can be used for a direct match with
|
|
% the hash value for one of the other cases), and
|
|
% use it instead.
|
|
|
|
:- type hash_slot ---> hash_slot(extended_case, int).
|
|
|
|
:- pred switch_util__calc_hash_slots(assoc_list(int, cases_list),
|
|
map(int, cases_list), map(int, hash_slot)).
|
|
:- mode switch_util__calc_hash_slots(in, in, out) is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% Stuff for tag switches
|
|
%
|
|
|
|
% where is the secondary tag (if any) for this primary tag value
|
|
:- type stag_loc ---> none ; local ; remote.
|
|
|
|
% map secondary tag values (-1 stands for none) to their goal
|
|
:- type stag_goal_map == map(int, hlds_goal).
|
|
:- type stag_goal_list == assoc_list(int, hlds_goal).
|
|
|
|
% map primary tag values to the set of their goals
|
|
:- type ptag_case_map == map(tag_bits, pair(stag_loc, stag_goal_map)).
|
|
:- type ptag_case_list == assoc_list(tag_bits,
|
|
pair(stag_loc, stag_goal_map)).
|
|
|
|
% map primary tag values to the number of constructors sharing them
|
|
:- type ptag_count_map == map(tag_bits, pair(stag_loc, int)).
|
|
:- type ptag_count_list == assoc_list(tag_bits, pair(stag_loc, int)).
|
|
|
|
% Group together all the cases that depend on the given variable
|
|
% having the same primary tag value.
|
|
|
|
:- pred switch_util__group_cases_by_ptag(cases_list,
|
|
ptag_case_map, ptag_case_map).
|
|
:- mode switch_util__group_cases_by_ptag(in, in, out) is det.
|
|
|
|
% Order the primary tags based on the number of secondary tags
|
|
% associated with them, putting the ones with the most secondary tags
|
|
% first. We use selection sort.
|
|
% Note that it is not an error for a primary tag to have no case list;
|
|
% this can happen in semidet switches, or in det switches
|
|
% where the initial inst of the switch variable is a bound(...) inst
|
|
% representing a subtype.
|
|
|
|
:- pred switch_util__order_ptags_by_count(ptag_count_list, ptag_case_map,
|
|
ptag_case_list).
|
|
:- mode switch_util__order_ptags_by_count(in, in, out) is det.
|
|
|
|
% switch_util__order_ptags_by_value(FirstPtag, MaxPtag,
|
|
% PtagCaseMap0, PtagCaseList):
|
|
% Order the primary tags based on their value, lowest value first.
|
|
% We scan through the primary tags values from zero to maximum.
|
|
% Note that it is not an error for a primary tag to have no case list,
|
|
% since this can happen in semidet switches.
|
|
|
|
:- pred switch_util__order_ptags_by_value(int, int, ptag_case_map,
|
|
ptag_case_list).
|
|
:- mode switch_util__order_ptags_by_value(in, in, in, out) is det.
|
|
|
|
% Find out how many secondary tags share each primary tag
|
|
% of the given variable.
|
|
|
|
:- pred switch_util__get_ptag_counts(type, module_info, int,
|
|
ptag_count_map).
|
|
:- mode switch_util__get_ptag_counts(in, in, out, out) is det.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
:- import_module char, int, string, require.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
switch_util__string_hash_cases([], _, Map) :-
|
|
map__init(Map).
|
|
switch_util__string_hash_cases([Case | Cases], HashMask, Map) :-
|
|
switch_util__string_hash_cases(Cases, HashMask, Map0),
|
|
( Case = case(_, string_constant(String0), _, _) ->
|
|
String = String0
|
|
;
|
|
error("switch_util__string_hash_cases: non-string case?")
|
|
),
|
|
string__hash(String, HashVal0),
|
|
HashVal is HashVal0 /\ HashMask,
|
|
( map__search(Map0, HashVal, CaseList0) ->
|
|
map__det_update(Map0, HashVal, [Case | CaseList0], Map)
|
|
;
|
|
map__det_insert(Map0, HashVal, [Case], Map)
|
|
).
|
|
|
|
% switch_util__calc_hash_slots(AssocList, HashMap, Map) :-
|
|
% For each (HashVal - Case) pair in AssocList,
|
|
% allocate a hash slot in Map for the case, as follows.
|
|
% If the hash slot corresponding to HashVal is not
|
|
% already used, then use that one. Otherwise, find
|
|
% the next spare slot (making sure that we don't
|
|
% use slots which can be used for a direct match with
|
|
% the hash value for one of the other cases), and
|
|
% use it instead. Keep track of the hash chains
|
|
% as we do this.
|
|
|
|
switch_util__calc_hash_slots(HashValList, HashMap, Map) :-
|
|
map__init(Map0),
|
|
switch_util__calc_hash_slots_1(HashValList, HashMap, Map0, 0,
|
|
Map, _).
|
|
|
|
:- pred switch_util__calc_hash_slots_1(assoc_list(int, cases_list),
|
|
map(int, cases_list), map(int, hash_slot), int,
|
|
map(int, hash_slot), int).
|
|
:- mode switch_util__calc_hash_slots_1(in, in, in, in, out, out) is det.
|
|
|
|
switch_util__calc_hash_slots_1([], _, Map, LastUsed, Map, LastUsed).
|
|
switch_util__calc_hash_slots_1([HashVal-Cases | Rest], HashMap, Map0,
|
|
LastUsed0, Map, LastUsed) :-
|
|
switch_util__calc_hash_slots_2(Cases, HashVal, HashMap, Map0,
|
|
LastUsed0, Map1, LastUsed1),
|
|
switch_util__calc_hash_slots_1(Rest, HashMap, Map1,
|
|
LastUsed1, Map, LastUsed).
|
|
|
|
:- pred switch_util__calc_hash_slots_2(cases_list, int,
|
|
map(int, cases_list), map(int, hash_slot), int,
|
|
map(int, hash_slot), int).
|
|
:- mode switch_util__calc_hash_slots_2(in, in, in, in, in, out, out) is det.
|
|
|
|
switch_util__calc_hash_slots_2([], _HashVal, _HashMap, Map, LastUsed,
|
|
Map, LastUsed).
|
|
switch_util__calc_hash_slots_2([Case | Cases], HashVal, HashMap, Map0,
|
|
LastUsed0, Map, LastUsed) :-
|
|
switch_util__calc_hash_slots_2(Cases, HashVal, HashMap, Map0,
|
|
LastUsed0, Map1, LastUsed1),
|
|
( map__contains(Map1, HashVal) ->
|
|
switch_util__follow_hash_chain(Map1, HashVal, ChainEnd),
|
|
switch_util__next_free_hash_slot(Map1, HashMap, LastUsed1,
|
|
Next),
|
|
map__lookup(Map1, ChainEnd, hash_slot(PrevCase, _)),
|
|
map__det_update(Map1, ChainEnd, hash_slot(PrevCase, Next),
|
|
Map2),
|
|
map__det_insert(Map2, Next, hash_slot(Case, -1), Map),
|
|
LastUsed = Next
|
|
;
|
|
map__det_insert(Map1, HashVal, hash_slot(Case, -1), Map),
|
|
LastUsed = LastUsed1
|
|
).
|
|
|
|
:- pred switch_util__follow_hash_chain(map(int, hash_slot), int, int).
|
|
:- mode switch_util__follow_hash_chain(in, in, out) is det.
|
|
|
|
switch_util__follow_hash_chain(Map, Slot, LastSlot) :-
|
|
map__lookup(Map, Slot, hash_slot(_, NextSlot)),
|
|
(
|
|
NextSlot >= 0,
|
|
map__contains(Map, NextSlot)
|
|
->
|
|
switch_util__follow_hash_chain(Map, NextSlot, LastSlot)
|
|
;
|
|
LastSlot = Slot
|
|
).
|
|
|
|
% next_free_hash_slot(M, H_M, LastUsed, FreeSlot) :-
|
|
% Find the next available slot FreeSlot in the hash table
|
|
% which is not already used (contained in M) and which is not
|
|
% going to be used a primary slot (contained in H_M),
|
|
% starting at the slot after LastUsed.
|
|
|
|
:- pred switch_util__next_free_hash_slot(map(int, hash_slot),
|
|
map(int, cases_list), int, int).
|
|
:- mode switch_util__next_free_hash_slot(in, in, in, out) is det.
|
|
|
|
switch_util__next_free_hash_slot(Map, H_Map, LastUsed, FreeSlot) :-
|
|
NextSlot is LastUsed + 1,
|
|
(
|
|
\+ map__contains(Map, NextSlot),
|
|
\+ map__contains(H_Map, NextSlot)
|
|
->
|
|
FreeSlot = NextSlot
|
|
;
|
|
switch_util__next_free_hash_slot(Map, H_Map, NextSlot,
|
|
FreeSlot)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
%
|
|
% Stuff for categorizing switches
|
|
%
|
|
|
|
% Convert a type category to a switch category
|
|
switch_util__type_cat_to_switch_cat(enum_type, atomic_switch).
|
|
switch_util__type_cat_to_switch_cat(int_type, atomic_switch).
|
|
switch_util__type_cat_to_switch_cat(char_type, atomic_switch).
|
|
switch_util__type_cat_to_switch_cat(float_type, other_switch).
|
|
switch_util__type_cat_to_switch_cat(str_type, string_switch).
|
|
switch_util__type_cat_to_switch_cat(pred_type, other_switch).
|
|
switch_util__type_cat_to_switch_cat(user_type, tag_switch).
|
|
switch_util__type_cat_to_switch_cat(polymorphic_type, other_switch).
|
|
switch_util__type_cat_to_switch_cat(tuple_type, other_switch).
|
|
|
|
% Return the priority of a constructor test.
|
|
% A low number here indicates a high priority.
|
|
% We prioritize the tag tests so that the cheapest
|
|
% (most efficient) ones come first.
|
|
%
|
|
switch_util__switch_priority(no_tag, 0). % should never occur
|
|
switch_util__switch_priority(int_constant(_), 1).
|
|
switch_util__switch_priority(shared_local_tag(_, _), 1).
|
|
switch_util__switch_priority(unshared_tag(_), 2).
|
|
switch_util__switch_priority(float_constant(_), 3).
|
|
switch_util__switch_priority(shared_remote_tag(_, _), 4).
|
|
switch_util__switch_priority(string_constant(_), 5).
|
|
% The following tags should all never occur in switches.
|
|
switch_util__switch_priority(pred_closure_tag(_, _, _), 6).
|
|
switch_util__switch_priority(code_addr_constant(_, _), 6).
|
|
switch_util__switch_priority(type_ctor_info_constant(_, _, _), 6).
|
|
switch_util__switch_priority(base_typeclass_info_constant(_, _, _), 6).
|
|
switch_util__switch_priority(tabling_pointer_constant(_, _), 6).
|
|
switch_util__switch_priority(deep_profiling_proc_static_tag(_), 6).
|
|
|
|
% Determine the range of an atomic type.
|
|
% Fail if the type isn't the sort of type that has a range
|
|
% or if the type's range is to big to switch on (e.g. int).
|
|
%
|
|
switch_util__type_range(char_type, _, _, MinChar, MaxChar) :-
|
|
% XXX the following code uses the host's character size,
|
|
% not the target's, so it won't work if cross-compiling
|
|
% to a machine with a different character size.
|
|
% Note also that the code in dense_switch.m and the code
|
|
% in lookup_switch.m assume that char__min_char_value is 0.
|
|
char__min_char_value(MinChar),
|
|
char__max_char_value(MaxChar).
|
|
switch_util__type_range(enum_type, Type, ModuleInfo, 0, MaxEnum) :-
|
|
( type_to_type_id(Type, TypeId0, _) ->
|
|
TypeId = TypeId0
|
|
;
|
|
error("dense_switch__type_range: invalid enum type?")
|
|
),
|
|
module_info_types(ModuleInfo, TypeTable),
|
|
map__lookup(TypeTable, TypeId, TypeDefn),
|
|
hlds_data__get_type_defn_body(TypeDefn, TypeBody),
|
|
( TypeBody = du_type(_, ConsTable, _, _) ->
|
|
map__count(ConsTable, TypeRange),
|
|
MaxEnum = TypeRange - 1
|
|
;
|
|
error("dense_switch__type_range: enum type is not d.u. type?")
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Find out how many secondary tags share each primary tag
|
|
% of the given variable.
|
|
|
|
switch_util__get_ptag_counts(Type, ModuleInfo, MaxPrimary, PtagCountMap) :-
|
|
( type_to_type_id(Type, TypeIdPrime, _) ->
|
|
TypeId = TypeIdPrime
|
|
;
|
|
error("unknown type in switch_util__get_ptag_counts")
|
|
),
|
|
module_info_types(ModuleInfo, TypeTable),
|
|
map__lookup(TypeTable, TypeId, TypeDefn),
|
|
hlds_data__get_type_defn_body(TypeDefn, Body),
|
|
( Body = du_type(_, ConsTable, _, _) ->
|
|
map__to_assoc_list(ConsTable, ConsList),
|
|
switch_util__cons_list_to_tag_list(ConsList, TagList)
|
|
;
|
|
error("non-du type in switch_util__get_ptag_counts")
|
|
),
|
|
map__init(PtagCountMap0),
|
|
switch_util__get_ptag_counts_2(TagList, -1, MaxPrimary,
|
|
PtagCountMap0, PtagCountMap).
|
|
|
|
:- pred switch_util__get_ptag_counts_2(list(cons_tag), int, int,
|
|
ptag_count_map, ptag_count_map).
|
|
:- mode switch_util__get_ptag_counts_2(in, in, out, in, out) is det.
|
|
|
|
switch_util__get_ptag_counts_2([], Max, Max, PtagCountMap, PtagCountMap).
|
|
switch_util__get_ptag_counts_2([ConsTag | TagList], MaxPrimary0, MaxPrimary,
|
|
PtagCountMap0, PtagCountMap) :-
|
|
( ConsTag = unshared_tag(Primary) ->
|
|
int__max(MaxPrimary0, Primary, MaxPrimary1),
|
|
( map__search(PtagCountMap0, Primary, _) ->
|
|
error("unshared tag is shared")
|
|
;
|
|
map__det_insert(PtagCountMap0, Primary, none - (-1),
|
|
PtagCountMap1)
|
|
)
|
|
; ConsTag = shared_remote_tag(Primary, Secondary) ->
|
|
int__max(MaxPrimary0, Primary, MaxPrimary1),
|
|
( map__search(PtagCountMap0, Primary, Target) ->
|
|
Target = TagType - MaxSoFar,
|
|
( TagType = remote ->
|
|
true
|
|
;
|
|
error("remote tag is shared with non-remote")
|
|
),
|
|
int__max(Secondary, MaxSoFar, Max),
|
|
map__det_update(PtagCountMap0, Primary, remote - Max,
|
|
PtagCountMap1)
|
|
;
|
|
map__det_insert(PtagCountMap0, Primary,
|
|
remote - Secondary, PtagCountMap1)
|
|
)
|
|
; ConsTag = shared_local_tag(Primary, Secondary) ->
|
|
int__max(MaxPrimary0, Primary, MaxPrimary1),
|
|
( map__search(PtagCountMap0, Primary, Target) ->
|
|
Target = TagType - MaxSoFar,
|
|
( TagType = local ->
|
|
true
|
|
;
|
|
error("local tag is shared with non-local")
|
|
),
|
|
int__max(Secondary, MaxSoFar, Max),
|
|
map__det_update(PtagCountMap0, Primary, local - Max,
|
|
PtagCountMap1)
|
|
;
|
|
map__det_insert(PtagCountMap0, Primary,
|
|
local - Secondary, PtagCountMap1)
|
|
)
|
|
;
|
|
error("non-du tag in switch_util__get_ptag_counts_2")
|
|
),
|
|
switch_util__get_ptag_counts_2(TagList, MaxPrimary1, MaxPrimary,
|
|
PtagCountMap1, PtagCountMap).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Group together all the cases that depend on the given variable
|
|
% having the same primary tag value.
|
|
|
|
switch_util__group_cases_by_ptag([], PtagCaseMap, PtagCaseMap).
|
|
switch_util__group_cases_by_ptag([Case0 | Cases0], PtagCaseMap0, PtagCaseMap) :-
|
|
Case0 = case(_Priority, Tag, _ConsId, Goal),
|
|
( Tag = unshared_tag(Primary) ->
|
|
( map__search(PtagCaseMap0, Primary, _Group) ->
|
|
error("unshared tag is shared")
|
|
;
|
|
map__init(StagGoalMap0),
|
|
map__det_insert(StagGoalMap0, -1, Goal, StagGoalMap),
|
|
map__det_insert(PtagCaseMap0, Primary,
|
|
none - StagGoalMap, PtagCaseMap1)
|
|
)
|
|
; Tag = shared_remote_tag(Primary, Secondary) ->
|
|
( map__search(PtagCaseMap0, Primary, Group) ->
|
|
Group = StagLoc - StagGoalMap0,
|
|
( StagLoc = remote ->
|
|
true
|
|
;
|
|
error("remote tag is shared with non-remote")
|
|
),
|
|
map__det_insert(StagGoalMap0, Secondary, Goal,
|
|
StagGoalMap),
|
|
map__det_update(PtagCaseMap0, Primary,
|
|
remote - StagGoalMap, PtagCaseMap1)
|
|
;
|
|
map__init(StagGoalMap0),
|
|
map__det_insert(StagGoalMap0, Secondary, Goal,
|
|
StagGoalMap),
|
|
map__det_insert(PtagCaseMap0, Primary,
|
|
remote - StagGoalMap, PtagCaseMap1)
|
|
)
|
|
; Tag = shared_local_tag(Primary, Secondary) ->
|
|
( map__search(PtagCaseMap0, Primary, Group) ->
|
|
Group = StagLoc - StagGoalMap0,
|
|
( StagLoc = local ->
|
|
true
|
|
;
|
|
error("local tag is shared with non-local")
|
|
),
|
|
map__det_insert(StagGoalMap0, Secondary, Goal,
|
|
StagGoalMap),
|
|
map__det_update(PtagCaseMap0, Primary,
|
|
local - StagGoalMap, PtagCaseMap1)
|
|
;
|
|
map__init(StagGoalMap0),
|
|
map__det_insert(StagGoalMap0, Secondary, Goal,
|
|
StagGoalMap),
|
|
map__det_insert(PtagCaseMap0, Primary,
|
|
local - StagGoalMap, PtagCaseMap1)
|
|
)
|
|
;
|
|
error("non-du tag in switch_util__group_cases_by_ptag")
|
|
),
|
|
switch_util__group_cases_by_ptag(Cases0, PtagCaseMap1, PtagCaseMap).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Order the primary tags based on the number of secondary tags
|
|
% associated with them, putting the ones with the most secondary tags
|
|
% first.
|
|
% Note that it is not an error for a primary tag to have no case list;
|
|
% this can happen in semidet switches, or in det switches
|
|
% where the initial inst of the switch variable is a bound(...) inst
|
|
% representing a subtype.
|
|
%
|
|
% We use selection sort.
|
|
|
|
switch_util__order_ptags_by_count(PtagCountList0, PtagCaseMap0, PtagCaseList) :-
|
|
(
|
|
switch_util__select_frequent_ptag(PtagCountList0,
|
|
Primary, _, PtagCountList1)
|
|
->
|
|
( map__search(PtagCaseMap0, Primary, PtagCase) ->
|
|
map__delete(PtagCaseMap0, Primary, PtagCaseMap1),
|
|
switch_util__order_ptags_by_count(PtagCountList1,
|
|
PtagCaseMap1, PtagCaseList1),
|
|
PtagCaseList = [Primary - PtagCase | PtagCaseList1]
|
|
;
|
|
switch_util__order_ptags_by_count(PtagCountList1,
|
|
PtagCaseMap0, PtagCaseList)
|
|
)
|
|
;
|
|
( map__is_empty(PtagCaseMap0) ->
|
|
PtagCaseList = []
|
|
;
|
|
error("PtagCaseMap0 is not empty in switch_util__order_ptags_by_count")
|
|
)
|
|
).
|
|
|
|
% Select the most frequently used primary tag based on the number of
|
|
% secondary tags associated with it.
|
|
|
|
:- pred switch_util__select_frequent_ptag(ptag_count_list, tag_bits, int,
|
|
ptag_count_list).
|
|
:- mode switch_util__select_frequent_ptag(in, out, out, out) is semidet.
|
|
|
|
switch_util__select_frequent_ptag([PtagCount0 | PtagCountList1], Primary, Count,
|
|
PtagCountList) :-
|
|
PtagCount0 = Primary0 - (_ - Count0),
|
|
(
|
|
switch_util__select_frequent_ptag(PtagCountList1,
|
|
Primary1, Count1, PtagCountList2),
|
|
Count1 > Count0
|
|
->
|
|
Primary = Primary1,
|
|
Count = Count1,
|
|
PtagCountList = [PtagCount0 | PtagCountList2]
|
|
;
|
|
Primary = Primary0,
|
|
Count = Count0,
|
|
PtagCountList = PtagCountList1
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% Order the primary tags based on their value, lowest value first.
|
|
% We scan through the primary tags values from zero to maximum.
|
|
% Note that it is not an error for a primary tag to have no case list,
|
|
% since this can happen in semidet switches.
|
|
|
|
switch_util__order_ptags_by_value(Ptag, MaxPtag, PtagCaseMap0, PtagCaseList) :-
|
|
( MaxPtag >= Ptag ->
|
|
NextPtag is Ptag + 1,
|
|
( map__search(PtagCaseMap0, Ptag, PtagCase) ->
|
|
map__delete(PtagCaseMap0, Ptag, PtagCaseMap1),
|
|
switch_util__order_ptags_by_value(NextPtag, MaxPtag,
|
|
PtagCaseMap1, PtagCaseList1),
|
|
PtagCaseList = [Ptag - PtagCase | PtagCaseList1]
|
|
;
|
|
switch_util__order_ptags_by_value(NextPtag, MaxPtag,
|
|
PtagCaseMap0, PtagCaseList)
|
|
)
|
|
;
|
|
( map__is_empty(PtagCaseMap0) ->
|
|
PtagCaseList = []
|
|
;
|
|
error("PtagCaseMap0 is not empty in order_ptags_by_value")
|
|
)
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- pred switch_util__cons_list_to_tag_list(assoc_list(cons_id, cons_tag),
|
|
list(cons_tag)).
|
|
:- mode switch_util__cons_list_to_tag_list(in, out) is det.
|
|
|
|
switch_util__cons_list_to_tag_list([], []).
|
|
switch_util__cons_list_to_tag_list([_ConsId - ConsTag | ConsList],
|
|
[ConsTag | Tagslist]) :-
|
|
switch_util__cons_list_to_tag_list(ConsList, Tagslist).
|
|
|
|
%-----------------------------------------------------------------------------%
|