mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-15 01:13:30 +00:00
681 lines
29 KiB
Mathematica
681 lines
29 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 1994-2012 The University of Melbourne.
|
|
% Copyright (C) 2013-2020, 2022-2026 The Mercury team.
|
|
% This file may only be copied under the terms of the GNU General
|
|
% Public License - see the file COPYING in the Mercury distribution.
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% File: ml_switch_gen.m
|
|
% Authors: fjh, zs.
|
|
%
|
|
% This module determines how we should generate code for a switch, primarily
|
|
% by deciding what sort of indexing, if any, we should use.
|
|
% NOTE The code here is quite similar to the code in switch_gen.m,
|
|
% which does the same thing for the LLDS back-end. Any changes here
|
|
% probably also require similar changes there.
|
|
%
|
|
% The following describes the different forms of indexing that we can use.
|
|
% Note that currently some of these are not implemented; these are marked
|
|
% NYI (for "not yet implemented").
|
|
%
|
|
% 1 For switches on atomic data types (int, char, enums), we can use
|
|
% three smart indexing strategies.
|
|
%
|
|
% a) If all the alternative goals for a switch on an atomic data type
|
|
% contain only construction unifications of constants, then we
|
|
% generate a dense lookup table (an array) in which we look up
|
|
% the values of the output variables.
|
|
% Implemented by ml_lookup_switch.m
|
|
%
|
|
% b) If the cases are not sparse, and the target supports computed
|
|
% gotos, we should use a computed_goto, unless the target supports
|
|
% switch statements and the `--prefer-switch' option is set.
|
|
% NYI.
|
|
%
|
|
% c) If the target supports switch statements, we generate an MLDS
|
|
% switch statement.
|
|
% Implemented by this module.
|
|
%
|
|
% 2 For switches on strings, we can use seven smart indexing strategies,
|
|
% six of which are the possible combinations of two possible implementation
|
|
% strategies of each of two aspects on the switch.
|
|
%
|
|
% a) One basic implementation strategy is the use of a trie. We convert
|
|
% the string being switched on into a list of code units (NOT code
|
|
% points), and examine each code unit in sequence, narrowing the
|
|
% range of possible matching switch arms at each step until only
|
|
% one is left, at which point we compare the remaining characters.
|
|
%
|
|
% b) The second basic implementation strategy is the use of a hash table
|
|
% with open addressing. Since the contents of the hash table is fixed,
|
|
% the open addressing can select buckets that are not the home bucket
|
|
% of any string in the table. And if we know that no two strings in
|
|
% the table share the same home address, we can dispense with open
|
|
% addressing altogether.
|
|
%
|
|
% This strategy requires the target to support either computed gotos
|
|
% or int switches. We prefer computed gotos, in which case table entries
|
|
% contain the address to jump to, but we can also use it switches,
|
|
% in which case entries contain an integer to give to a switch.
|
|
% We don't use this strategy if the target supports string switch
|
|
% statements and the `--prefer-switch' option is set.
|
|
%
|
|
% c) The third basic implementation approach is the use of binary search.
|
|
% We generate a table containing all the strings in the switch cases in
|
|
% order, and search it using binary search. Again, we don't use this
|
|
% strategy if the target supports string switch statements and the
|
|
% `--prefer-switch' option is set.
|
|
%
|
|
% d) If the target supports string switches, we generate an MLDS switch
|
|
% statement directly on the string variable.
|
|
%
|
|
% The second aspect is whether we use a lookup table. If all the switch arms
|
|
% contain only construction unifications of constants, we can store the
|
|
% values to be returned in a table for approach (a), or in extensions of
|
|
% either the hash table (for approach (b)) or the binary search table
|
|
% (for approach (c)).
|
|
%
|
|
% All these indexing strategies are implemented by ml_string_switch.m.
|
|
%
|
|
% 3 For switches on discriminated union types, we generate code that does
|
|
% indexing first on the primary tag, and then on the secondary tag (if
|
|
% the primary tag is shared between several function symbols). The
|
|
% indexing code for switches on both primary and secondary tags can be
|
|
% in the form of a try-me-else chain, a try chain, a dense jump table
|
|
% or a binary search.
|
|
% At the moment, ml_tag_switch implements only try-me-else chains;
|
|
% the other indexing forms are NYI.
|
|
%
|
|
% 4 For switches on floats, we could generate code that does binary search.
|
|
% However, this is not yet implemented, as switches on floats can be
|
|
% expected to be rare.
|
|
%
|
|
% If we cannot apply any of the above smart indexing strategies, or if the
|
|
% --smart-indexing option was disabled, then this module just generates
|
|
% a chain of if-then-elses.
|
|
%
|
|
% TODO:
|
|
% - implement the things marked NYI above
|
|
% - optimize if-then-else chain switches so that the recursive case
|
|
% comes first (see switch_gen.m).
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module ml_backend.ml_switch_gen.
|
|
:- interface.
|
|
|
|
:- import_module hlds.
|
|
:- import_module hlds.code_model.
|
|
:- import_module hlds.hlds_goal.
|
|
:- import_module ml_backend.ml_gen_info.
|
|
:- import_module ml_backend.mlds.
|
|
:- import_module parse_tree.
|
|
:- import_module parse_tree.prog_data.
|
|
|
|
:- import_module list.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Generate MLDS code for a switch.
|
|
%
|
|
:- pred ml_gen_switch(prog_var::in, can_fail::in, code_model::in,
|
|
hlds_goal_info::in, prog_context::in, list(case)::in,
|
|
list(mlds_local_var_defn)::out, list(mlds_stmt)::out,
|
|
ml_gen_info::in, ml_gen_info::out) is det.
|
|
|
|
% Generate an appropriate default for a switch.
|
|
% (Exported to ml_tag_switch.m.)
|
|
%
|
|
:- pred ml_switch_generate_default(can_fail::in, code_model::in,
|
|
prog_context::in, mlds_switch_default::out,
|
|
ml_gen_info::in, ml_gen_info::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module backend_libs.
|
|
:- import_module backend_libs.lookup_switch_util.
|
|
:- import_module backend_libs.switch_util.
|
|
:- import_module hlds.hlds_data.
|
|
:- import_module hlds.hlds_module.
|
|
:- import_module libs.
|
|
:- import_module libs.globals.
|
|
:- import_module libs.optimization_options.
|
|
:- import_module ml_backend.ml_code_gen.
|
|
:- import_module ml_backend.ml_code_util.
|
|
:- import_module ml_backend.ml_lookup_switch.
|
|
:- import_module ml_backend.ml_simplify_switch.
|
|
:- import_module ml_backend.ml_string_switch.
|
|
:- import_module ml_backend.ml_tag_switch.
|
|
:- import_module ml_backend.ml_target_util.
|
|
:- import_module ml_backend.ml_unify_gen_test.
|
|
:- import_module parse_tree.var_table.
|
|
|
|
:- import_module assoc_list.
|
|
:- import_module bool.
|
|
:- import_module int.
|
|
:- import_module int32.
|
|
:- import_module maybe.
|
|
:- import_module pair.
|
|
:- import_module require.
|
|
:- import_module string.
|
|
:- import_module uint.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
ml_gen_switch(SwitchVar, CanFail, CodeModel, GoalInfo, Context, Cases,
|
|
Defns, Stmts, !Info) :-
|
|
ml_gen_info_get_var_table(!.Info, VarTable),
|
|
lookup_var_entry(VarTable, SwitchVar, SwitchVarEntry),
|
|
SwitchVarType = SwitchVarEntry ^ vte_type,
|
|
% Lookup the representation of the constructors for the tag tests.
|
|
% Note that you cannot have a switch on a variable whose type's main
|
|
% type constructor is not known.
|
|
ml_gen_info_get_module_info(!.Info, ModuleInfo),
|
|
tag_cases(ModuleInfo, SwitchVarType, Cases, TaggedCases,
|
|
MaybeIntSwitchInfo),
|
|
|
|
% Start every disjunct with EntryPackedWordMap to prevent later
|
|
% switch arms from trying to use map entries added by earlier switch arms.
|
|
ml_gen_info_get_packed_word_map(!.Info, EntryPackedWordMap),
|
|
|
|
find_switch_category(ModuleInfo, SwitchVarType, SwitchCategory),
|
|
(
|
|
( SwitchCategory = ite_chain_switch
|
|
; SwitchCategory = float_switch
|
|
),
|
|
ml_switch_generate_if_then_else_chain(SwitchVar, TaggedCases,
|
|
CodeModel, CanFail, EntryPackedWordMap, Context, Stmts, !Info),
|
|
Defns = []
|
|
;
|
|
SwitchCategory = int_max_32_switch,
|
|
ml_gen_smart_int_max_32_switch(SwitchVar, SwitchVarType,
|
|
TaggedCases, MaybeIntSwitchInfo, CanFail, CodeModel, GoalInfo,
|
|
EntryPackedWordMap, Context, Stmts, !Info),
|
|
Defns = []
|
|
;
|
|
SwitchCategory = int_64_switch,
|
|
module_info_get_globals(ModuleInfo, Globals),
|
|
Int64SwitchSupported = globals_target_supports_int64_switch(Globals),
|
|
(
|
|
Int64SwitchSupported = yes,
|
|
ml_switch_generate_mlds_switch(SwitchVar, TaggedCases,
|
|
CodeModel, CanFail, EntryPackedWordMap, Context, Stmts, !Info)
|
|
;
|
|
Int64SwitchSupported = no,
|
|
ml_switch_generate_if_then_else_chain(SwitchVar, TaggedCases,
|
|
CodeModel, CanFail, EntryPackedWordMap, Context, Stmts, !Info)
|
|
),
|
|
Defns = []
|
|
;
|
|
SwitchCategory = string_switch,
|
|
ml_gen_string_switch(SwitchVar, SwitchVarEntry, TaggedCases,
|
|
CanFail, CodeModel, GoalInfo, Context,
|
|
EntryPackedWordMap, Defns, Stmts, !Info)
|
|
;
|
|
SwitchCategory = tag_switch,
|
|
num_cons_ids_in_tagged_cases(TaggedCases, NumConsIds, NumArms),
|
|
module_info_get_globals(ModuleInfo, Globals),
|
|
globals.get_opt_tuple(Globals, OptTuple),
|
|
TagSize = OptTuple ^ ot_tag_switch_size,
|
|
( if
|
|
NumConsIds >= TagSize,
|
|
NumArms > 1,
|
|
ml_generate_tag_switch_if_possible(SwitchVar, SwitchVarEntry,
|
|
TaggedCases, CodeModel, CanFail,
|
|
Context, EntryPackedWordMap, StmtsPrime, !Info)
|
|
then
|
|
Stmts = StmtsPrime
|
|
else
|
|
ml_switch_generate_if_then_else_chain(SwitchVar, TaggedCases,
|
|
CodeModel, CanFail, EntryPackedWordMap, Context, Stmts, !Info)
|
|
),
|
|
Defns = []
|
|
),
|
|
% Start the code *after* the whole switch with EntryPackedWordMap as well,
|
|
% to prevent that code from trying to use map entries added by a switch arm
|
|
% that may not have been taken.
|
|
ml_gen_info_set_packed_word_map(EntryPackedWordMap, !Info).
|
|
|
|
:- pred ml_gen_smart_int_max_32_switch(prog_var::in, mer_type::in,
|
|
list(tagged_case)::in, maybe_int_switch_info::in,
|
|
can_fail::in, code_model::in, hlds_goal_info::in,
|
|
packed_word_map::in, prog_context::in, list(mlds_stmt)::out,
|
|
ml_gen_info::in, ml_gen_info::out) is det.
|
|
|
|
ml_gen_smart_int_max_32_switch(SwitchVar, SwitchVarType, TaggedCases,
|
|
MaybeIntSwitchInfo, CanFail, CodeModel, GoalInfo,
|
|
EntryPackedWordMap, Context, Stmts, !Info) :-
|
|
num_cons_ids_in_tagged_cases(TaggedCases, NumConsIds, NumArms),
|
|
ml_gen_info_get_module_info(!.Info, ModuleInfo),
|
|
module_info_get_globals(ModuleInfo, Globals),
|
|
ml_gen_info_get_high_level_data(!.Info, HighLevelData),
|
|
( if
|
|
HighLevelData = no,
|
|
% XXX It should be possible to implement lookup switches
|
|
% for Java and C# (HighLevelData = yes) as well.
|
|
MaybeIntSwitchInfo = int_switch(IntSwitchInfo),
|
|
globals.get_opt_tuple(Globals, OptTuple),
|
|
OptTuple ^ ot_use_static_ground_cells = use_static_ground_cells,
|
|
LookupSize = OptTuple ^ ot_lookup_switch_size,
|
|
NumConsIds >= LookupSize,
|
|
NumArms > 1,
|
|
ReqDensityI = OptTuple ^ ot_lookup_switch_req_density,
|
|
uint.from_int(ReqDensityI, ReqDensityU),
|
|
filter_out_failing_cases_if_needed(CodeModel,
|
|
TaggedCases, FilteredTaggedCases, CanFail, FilteredCanFail),
|
|
find_int_lookup_switch_params(ModuleInfo, SwitchVarType,
|
|
FilteredCanFail, IntSwitchInfo, ReqDensityU,
|
|
NeedBitVecCheck, NeedRangeCheck, Limits),
|
|
ml_is_lookup_switch(SwitchVar, FilteredTaggedCases, GoalInfo,
|
|
CodeModel, !.Info, MaybeLookupSwitchInfo),
|
|
MaybeLookupSwitchInfo = yes(LookupSwitchInfo)
|
|
then
|
|
Limits = int_switch_limits(FirstValI32, LastValI32),
|
|
FirstValI = int32.cast_to_int(FirstValI32),
|
|
LastValI = int32.cast_to_int(LastValI32),
|
|
ml_gen_int_max_32_lookup_switch(SwitchVar, TaggedCases,
|
|
LookupSwitchInfo, CodeModel, Context, FirstValI, LastValI,
|
|
NeedBitVecCheck, NeedRangeCheck, LookupStmt, !:Info),
|
|
Stmts = [LookupStmt]
|
|
else
|
|
% All MLDS targets (as of 2023 april) support switches on integers
|
|
% whose size is at most 32 bits.
|
|
ml_switch_generate_mlds_switch(SwitchVar, TaggedCases,
|
|
CodeModel, CanFail, EntryPackedWordMap, Context, Stmts, !Info)
|
|
).
|
|
|
|
:- pred ml_gen_string_switch(prog_var::in, var_table_entry::in,
|
|
list(tagged_case)::in, can_fail::in, code_model::in, hlds_goal_info::in,
|
|
prog_context::in, packed_word_map::in,
|
|
list(mlds_local_var_defn)::out, list(mlds_stmt)::out,
|
|
ml_gen_info::in, ml_gen_info::out) is det.
|
|
|
|
ml_gen_string_switch(SwitchVar, SwitchVarEntry, TaggedCases,
|
|
CanFail, CodeModel, GoalInfo, Context,
|
|
EntryPackedWordMap, Defns, Stmts, !Info) :-
|
|
filter_out_failing_cases_if_needed(CodeModel,
|
|
TaggedCases, FilteredTaggedCases, CanFail, FilteredCanFail),
|
|
num_cons_ids_in_tagged_cases(FilteredTaggedCases, NumConsIds, NumArms),
|
|
% If we don't have at least two arms, an if-then-else chain
|
|
% (which will contain at most one if-then-else) will be faster than
|
|
% any other method for implementing the "switch".
|
|
( if NumArms < 2 then
|
|
ml_switch_generate_if_then_else_chain(SwitchVar, FilteredTaggedCases,
|
|
CodeModel, FilteredCanFail, EntryPackedWordMap, Context,
|
|
Stmts, !Info),
|
|
Defns = []
|
|
else
|
|
ml_gen_info_get_module_info(!.Info, ModuleInfo),
|
|
module_info_get_globals(ModuleInfo, Globals),
|
|
globals.get_target(Globals, Target),
|
|
(
|
|
( Target = target_java
|
|
; Target = target_csharp
|
|
),
|
|
% We always take this path for these two target languages, because
|
|
% - both support switches on strings, and
|
|
% - both represent strings using UTF-16, which (currently)
|
|
% cannot be handled by any of the Mercury compiler's
|
|
% smart string switch implementations,
|
|
% We therefore leave any implementation of smart indexing
|
|
% to the Java/C# compilers.
|
|
ml_switch_generate_mlds_switch(SwitchVar, FilteredTaggedCases,
|
|
CodeModel, FilteredCanFail, EntryPackedWordMap, Context,
|
|
Stmts, !Info),
|
|
Defns = []
|
|
;
|
|
Target = target_c,
|
|
ml_gen_smart_string_switch(Globals, SwitchVar, SwitchVarEntry,
|
|
FilteredTaggedCases, CodeModel, FilteredCanFail, GoalInfo,
|
|
Context, NumConsIds, EntryPackedWordMap, Defns, Stmts, !Info)
|
|
)
|
|
).
|
|
|
|
:- pred ml_gen_smart_string_switch(globals::in,
|
|
prog_var::in, var_table_entry::in, list(tagged_case)::in,
|
|
code_model::in, can_fail::in, hlds_goal_info::in,
|
|
prog_context::in, int::in, packed_word_map::in,
|
|
list(mlds_local_var_defn)::out, list(mlds_stmt)::out,
|
|
ml_gen_info::in, ml_gen_info::out) is det.
|
|
|
|
ml_gen_smart_string_switch(Globals, SwitchVar, SwitchVarEntry,
|
|
FilteredTaggedCases, CodeModel, FilteredCanFail, GoalInfo,
|
|
Context, NumConsIds, EntryPackedWordMap, Defns, Stmts, !Info) :-
|
|
ml_gen_var(!.Info, SwitchVar, SwitchVarEntry, SwitchVarLval),
|
|
SwitchVarRval = ml_lval(SwitchVarLval),
|
|
% We can use a trie, a hash switch or binary switch, and we prefer
|
|
% them in that order.
|
|
%
|
|
% We can implement all three methods (tries, hash tables and binary
|
|
% searches) using either computed gotos or int switches.
|
|
ml_is_lookup_switch(SwitchVar, FilteredTaggedCases, GoalInfo,
|
|
CodeModel, !.Info, MaybeLookupSwitchInfo),
|
|
globals.get_opt_tuple(Globals, OptTuple),
|
|
globals.get_target(Globals, Target),
|
|
( if
|
|
% For now, string trie switches have been implemented only for C,
|
|
% and the code assumes that the host and the target use the same
|
|
% string encoding. This effectively requires that both the host and
|
|
% the target compile to C.
|
|
internal_string_encoding = utf8, % host is C
|
|
Target = target_c, % target is C
|
|
StringTrieSwitchSize = OptTuple ^ ot_string_trie_switch_size,
|
|
NumConsIds >= StringTrieSwitchSize
|
|
then
|
|
(
|
|
MaybeLookupSwitchInfo = yes(LookupSwitchInfo),
|
|
ml_generate_string_trie_lookup_switch(SwitchVarRval,
|
|
FilteredTaggedCases, LookupSwitchInfo, CodeModel,
|
|
FilteredCanFail, Context, Stmts, !:Info)
|
|
;
|
|
MaybeLookupSwitchInfo = no,
|
|
ml_generate_string_trie_jump_switch(SwitchVarRval,
|
|
FilteredTaggedCases, CodeModel, FilteredCanFail,
|
|
EntryPackedWordMap, Context, Stmts, !Info)
|
|
),
|
|
Defns = []
|
|
else if
|
|
StringHashSwitchSize = OptTuple ^ ot_string_hash_switch_size,
|
|
NumConsIds >= StringHashSwitchSize
|
|
then
|
|
(
|
|
MaybeLookupSwitchInfo = yes(LookupSwitchInfo),
|
|
ml_generate_string_hash_lookup_switch(SwitchVarRval,
|
|
FilteredTaggedCases, LookupSwitchInfo, CodeModel,
|
|
FilteredCanFail, Context, Defns, Stmts, !:Info)
|
|
;
|
|
MaybeLookupSwitchInfo = no,
|
|
ml_generate_string_hash_jump_switch(SwitchVarRval,
|
|
FilteredTaggedCases, CodeModel, FilteredCanFail,
|
|
EntryPackedWordMap, Context, Defns, Stmts, !Info)
|
|
)
|
|
else if
|
|
StringBinarySwitchSize = OptTuple ^ ot_string_binary_switch_size,
|
|
NumConsIds >= StringBinarySwitchSize
|
|
then
|
|
(
|
|
MaybeLookupSwitchInfo = yes(LookupSwitchInfo),
|
|
ml_generate_string_binary_lookup_switch(SwitchVarRval,
|
|
FilteredTaggedCases, LookupSwitchInfo, CodeModel,
|
|
FilteredCanFail, Context, Defns, Stmts, !:Info)
|
|
;
|
|
MaybeLookupSwitchInfo = no,
|
|
ml_generate_string_binary_jump_switch(SwitchVarRval,
|
|
FilteredTaggedCases, CodeModel, FilteredCanFail,
|
|
EntryPackedWordMap, Context, Defns, Stmts, !Info)
|
|
)
|
|
else
|
|
ml_switch_generate_if_then_else_chain(SwitchVar, FilteredTaggedCases,
|
|
CodeModel, FilteredCanFail, EntryPackedWordMap, Context, Stmts,
|
|
!Info),
|
|
Defns = []
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Look up the representation (tag) for the cons_id in each case.
|
|
% Also look up the priority of each tag test.
|
|
%
|
|
:- pred mark_tag_test_cost(tagged_case::in, pair(int, tagged_case)::out)
|
|
is det.
|
|
|
|
mark_tag_test_cost(TaggedCase, Cost - TaggedCase) :-
|
|
TaggedCase = tagged_case(TaggedMainConsId, TaggedOtherConsIds,
|
|
_CaseNum, _Goal),
|
|
estimate_cons_id_tag_test_cost(TaggedMainConsId, 0, MainCost),
|
|
list.foldl(estimate_cons_id_tag_test_cost, TaggedOtherConsIds,
|
|
MainCost, Cost).
|
|
|
|
:- pred estimate_cons_id_tag_test_cost(tagged_cons_id::in,
|
|
int::in, int::out) is det.
|
|
|
|
estimate_cons_id_tag_test_cost(TaggedConsId, !CaseCost) :-
|
|
TaggedConsId = tagged_cons_id(_ConsId, ConsTag),
|
|
ConsIdCost = estimate_switch_tag_test_cost(ConsTag),
|
|
!:CaseCost = !.CaseCost + ConsIdCost.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Generate a chain of if-then-elses to test each case in turn.
|
|
%
|
|
:- pred ml_switch_generate_if_then_else_chain(prog_var::in,
|
|
list(tagged_case)::in, code_model::in, can_fail::in, packed_word_map::in,
|
|
prog_context::in, list(mlds_stmt)::out,
|
|
ml_gen_info::in, ml_gen_info::out) is det.
|
|
|
|
ml_switch_generate_if_then_else_chain(Var, TaggedCases0,
|
|
CodeModel, CanFail, EntryPackedWordMap, Context, Stmts, !Info) :-
|
|
% Associate each tagged case with the estimated cost of its tag tests.
|
|
list.map(mark_tag_test_cost, TaggedCases0, CostTaggedCases0),
|
|
|
|
% Sort the cases according to the priority of their tag tests.
|
|
list.sort(CostTaggedCases0, CostTaggedCases),
|
|
assoc_list.values(CostTaggedCases, TaggedCases),
|
|
(
|
|
TaggedCases = [],
|
|
unexpected($pred, "empty switch")
|
|
;
|
|
TaggedCases = [FirstTaggedCase | LaterTaggedCases],
|
|
ml_switch_generate_if_then_else_chain_ites(Var,
|
|
FirstTaggedCase, LaterTaggedCases, CodeModel, CanFail,
|
|
EntryPackedWordMap, Context, Stmts,
|
|
[], ReachableConstVarMaps, !Info),
|
|
ml_gen_record_consensus_const_var_map(ReachableConstVarMaps, !Info)
|
|
).
|
|
|
|
:- pred ml_switch_generate_if_then_else_chain_ites(prog_var::in,
|
|
tagged_case::in, list(tagged_case)::in, code_model::in, can_fail::in,
|
|
packed_word_map::in, prog_context::in, list(mlds_stmt)::out,
|
|
list(ml_ground_term_map)::in, list(ml_ground_term_map)::out,
|
|
ml_gen_info::in, ml_gen_info::out) is det.
|
|
|
|
ml_switch_generate_if_then_else_chain_ites(Var, TaggedCase, TaggedCases,
|
|
CodeModel, CanFail, EntryPackedWordMap, Context, [Stmt],
|
|
!ReachableConstVarMaps, !Info) :-
|
|
ml_gen_info_set_packed_word_map(EntryPackedWordMap, !Info),
|
|
TaggedCase = tagged_case(_, _, _, Goal),
|
|
(
|
|
TaggedCases = [],
|
|
(
|
|
CanFail = cannot_fail,
|
|
% If execution has got to the code we are generating here, then
|
|
% previously-executed tests must have ruled out all the cons_ids
|
|
% that Var could be bound to, other than the one in TaggedCase.
|
|
ml_gen_goal_as_branch_block(CodeModel, Goal, Stmt,
|
|
!ReachableConstVarMaps, !Info)
|
|
;
|
|
CanFail = can_fail,
|
|
% We handle this case as if we still had later cases, cases
|
|
% representing the cons_ids that the switch does not cover.
|
|
|
|
% Generate the test for checking whether we are in the first
|
|
% tagged case.
|
|
ml_switch_generate_if_then_else_cond(!.Info, Var, TaggedCase,
|
|
Cond),
|
|
|
|
% Generate code for the first tagged case.
|
|
ml_gen_goal_as_branch_block(CodeModel, Goal, GoalBlock,
|
|
!ReachableConstVarMaps, !Info),
|
|
|
|
% Generate code for the non-covered tagged cases.
|
|
ml_gen_failure(CodeModel, Context, FailStmts, !Info),
|
|
FailBlock = ml_gen_block([], [], FailStmts, Context),
|
|
|
|
% Put the codes for the first and non-covered tagged cases
|
|
% together.
|
|
Stmt = ml_stmt_if_then_else(Cond, GoalBlock, yes(FailBlock),
|
|
Context)
|
|
)
|
|
;
|
|
TaggedCases = [LaterTaggedCase | LaterTaggedCases],
|
|
|
|
% Generate the test for checking whether we are in the first
|
|
% tagged case.
|
|
ml_switch_generate_if_then_else_cond(!.Info, Var, TaggedCase, Cond),
|
|
|
|
% Generate code for the first tagged case.
|
|
ml_gen_goal_as_branch_block(CodeModel, Goal, GoalBlock,
|
|
!ReachableConstVarMaps, !Info),
|
|
|
|
% Generate code for the later tagged cases.
|
|
ml_switch_generate_if_then_else_chain_ites(Var,
|
|
LaterTaggedCase, LaterTaggedCases, CodeModel, CanFail,
|
|
EntryPackedWordMap, Context, LaterStmts,
|
|
!ReachableConstVarMaps, !Info),
|
|
LaterBlock = ml_gen_block([], [], LaterStmts, Context),
|
|
|
|
% Put the codes for the first and later tagged cases together.
|
|
Stmt = ml_stmt_if_then_else(Cond, GoalBlock, yes(LaterBlock), Context)
|
|
).
|
|
|
|
:- pred ml_switch_generate_if_then_else_cond(ml_gen_info::in, prog_var::in,
|
|
tagged_case::in, mlds_rval::out) is det.
|
|
|
|
ml_switch_generate_if_then_else_cond(Info, Var, TaggedCase, CondRval) :-
|
|
TaggedCase = tagged_case(MainTaggedConsId, OtherTaggedConsIds, _, _),
|
|
ml_generate_test_var_has_one_tagged_cons_id(Info, Var,
|
|
MainTaggedConsId, OtherTaggedConsIds, CondRval).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Generate an MLDS switch. This is used for "direct-mapped" switches,
|
|
% where we map a Mercury switch directly to a switch in the target
|
|
% language. (But see the post-processing done by ml_simplify_switch.)
|
|
%
|
|
:- pred ml_switch_generate_mlds_switch(prog_var::in, list(tagged_case)::in,
|
|
code_model::in, can_fail::in, packed_word_map::in, prog_context::in,
|
|
list(mlds_stmt)::out, ml_gen_info::in, ml_gen_info::out) is det.
|
|
|
|
ml_switch_generate_mlds_switch(Var, Cases, CodeModel, CanFail,
|
|
EntryPackedWordMap, Context, Stmts, !Info) :-
|
|
ml_gen_info_get_var_table(!.Info, VarTable),
|
|
lookup_var_entry(VarTable, Var, VarEntry),
|
|
Type = VarEntry ^ vte_type,
|
|
ml_gen_mlds_type(!.Info, Type, MLDS_Type),
|
|
ml_gen_var(!.Info, Var, VarEntry, Lval),
|
|
Rval = ml_lval(Lval),
|
|
ml_switch_gen_range(!.Info, MLDS_Type, Range),
|
|
ml_switch_generate_mlds_cases(Type, MLDS_Type, CodeModel,
|
|
EntryPackedWordMap, Cases, MLDS_Cases,
|
|
[], ReachableConstVarMaps, !Info),
|
|
ml_gen_record_consensus_const_var_map(ReachableConstVarMaps, !Info),
|
|
ml_switch_generate_default(CanFail, CodeModel, Context, Default, !Info),
|
|
SwitchStmt0 = ml_stmt_switch(MLDS_Type, Rval, Range, MLDS_Cases, Default,
|
|
Context),
|
|
ml_simplify_switch(SwitchStmt0, SwitchStmt, !Info),
|
|
Stmts = [SwitchStmt].
|
|
|
|
:- pred ml_switch_gen_range(ml_gen_info::in, mlds_type::in,
|
|
mlds_switch_range::out) is det.
|
|
|
|
ml_switch_gen_range(Info, MLDS_Type, Range) :-
|
|
ml_gen_info_get_module_info(Info, ModuleInfo),
|
|
( if
|
|
MLDS_Type = mercury_nb_type(Type, TypeCategory),
|
|
type_range(ModuleInfo, coerce(TypeCategory), Type,
|
|
_NumValuesInRange, Limits)
|
|
then
|
|
Limits = int_switch_limits(MinI32, MaxI32),
|
|
MinI = int32.cast_to_int(MinI32),
|
|
MaxI = int32.cast_to_int(MaxI32),
|
|
Range = mlds_switch_range(MinI, MaxI)
|
|
else
|
|
Range = mlds_switch_range_unknown
|
|
).
|
|
|
|
:- pred ml_switch_generate_mlds_cases(mer_type::in, mlds_type::in,
|
|
code_model::in, packed_word_map::in,
|
|
list(tagged_case)::in, list(mlds_switch_case)::out,
|
|
list(ml_ground_term_map)::in, list(ml_ground_term_map)::out,
|
|
ml_gen_info::in, ml_gen_info::out) is det.
|
|
|
|
ml_switch_generate_mlds_cases(_, _, _, _, [], [],
|
|
!ReachableConstVarMaps, !Info).
|
|
ml_switch_generate_mlds_cases(MerType, MLDS_Type, CodeModel,
|
|
EntryPackedWordMap, [TaggedCase | TaggedCases],
|
|
[MLDS_Case | MLDS_Cases], !ReachableConstVarMaps, !Info) :-
|
|
ml_switch_generate_mlds_case(MerType, MLDS_Type, CodeModel,
|
|
EntryPackedWordMap, TaggedCase, MLDS_Case,
|
|
!ReachableConstVarMaps, !Info),
|
|
ml_switch_generate_mlds_cases(MerType, MLDS_Type, CodeModel,
|
|
EntryPackedWordMap, TaggedCases, MLDS_Cases,
|
|
!ReachableConstVarMaps, !Info).
|
|
|
|
:- pred ml_switch_generate_mlds_case(mer_type::in, mlds_type::in,
|
|
code_model::in, packed_word_map::in,
|
|
tagged_case::in, mlds_switch_case::out,
|
|
list(ml_ground_term_map)::in, list(ml_ground_term_map)::out,
|
|
ml_gen_info::in, ml_gen_info::out) is det.
|
|
|
|
ml_switch_generate_mlds_case(MerType, MLDS_Type, CodeModel, EntryPackedWordMap,
|
|
TaggedCase, MLDS_Case, !ReachableConstVarMaps, !Info) :-
|
|
ml_gen_info_set_packed_word_map(EntryPackedWordMap, !Info),
|
|
TaggedCase = tagged_case(TaggedMainConsId, TaggedOtherConsIds, _, Goal),
|
|
ml_tagged_cons_id_to_match_cond(MerType, MLDS_Type, TaggedMainConsId,
|
|
MainCond),
|
|
list.map(ml_tagged_cons_id_to_match_cond(MerType, MLDS_Type),
|
|
TaggedOtherConsIds, OtherConds),
|
|
ml_gen_goal_as_branch_block(CodeModel, Goal, Stmt,
|
|
!ReachableConstVarMaps, !Info),
|
|
MLDS_Case = mlds_switch_case(MainCond, OtherConds, Stmt).
|
|
|
|
:- pred ml_tagged_cons_id_to_match_cond(mer_type::in, mlds_type::in,
|
|
tagged_cons_id::in, mlds_case_match_cond::out) is det.
|
|
|
|
ml_tagged_cons_id_to_match_cond(MerType, MLDS_Type, TaggedConsId, MatchCond) :-
|
|
TaggedConsId = tagged_cons_id(_ConsId, Tag),
|
|
(
|
|
Tag = int_tag(IntTag),
|
|
Rval = ml_int_tag_to_rval_const(IntTag, MerType, MLDS_Type)
|
|
;
|
|
Tag = string_tag(String),
|
|
Rval = ml_const(mlconst_string(String))
|
|
;
|
|
Tag = foreign_tag(ForeignLang, ForeignTag),
|
|
Rval = ml_const(mlconst_foreign(ForeignLang, ForeignTag, MLDS_Type))
|
|
;
|
|
( Tag = dummy_tag
|
|
; Tag = float_tag(_)
|
|
; Tag = type_ctor_info_tag(_, _, _)
|
|
; Tag = base_typeclass_info_tag(_, _, _)
|
|
; Tag = type_info_const_tag(_)
|
|
; Tag = typeclass_info_const_tag(_)
|
|
; Tag = ground_term_const_tag(_, _)
|
|
; Tag = tabling_info_tag(_, _)
|
|
; Tag = deep_profiling_proc_layout_tag(_, _)
|
|
; Tag = table_io_entry_tag(_, _)
|
|
; Tag = no_tag
|
|
; Tag = direct_arg_tag(_)
|
|
; Tag = remote_args_tag(_)
|
|
; Tag = shared_local_tag_no_args(_, _, _)
|
|
; Tag = local_args_tag(_)
|
|
; Tag = closure_tag(_, _)
|
|
),
|
|
unexpected($pred, "invalid tag type")
|
|
),
|
|
MatchCond = match_value(Rval).
|
|
|
|
ml_switch_generate_default(CanFail, CodeModel, Context, Default, !Info) :-
|
|
(
|
|
CanFail = can_fail,
|
|
ml_gen_failure(CodeModel, Context, FailStmts, !Info),
|
|
( if is_empty(FailStmts) then
|
|
Default = default_do_nothing
|
|
else
|
|
Fail = ml_gen_block([], [], FailStmts, Context),
|
|
Default = default_case(Fail)
|
|
)
|
|
;
|
|
CanFail = cannot_fail,
|
|
Default = default_is_unreachable
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module ml_backend.ml_switch_gen.
|
|
%---------------------------------------------------------------------------%
|