mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-15 09:23:44 +00:00
Until now, the only integer type we generated dense switches
(including lookup switches) for was int itself; we did not do so
for any sized and/or unsigned integer types.
compiler/switch_util.m:
Simplify the representation of whether a switch is on an integer type.
The new version makes it impossible to make the mistake that caused
bug582 in the first place: not having a single representation for
for the switch being on *any* kind of integer type.
This fix requires solving a problem we never had to solve before:
representing information such as the min and max case values
in a way that works for every integer type, signed or unsigned,
sized or not. The solution that this diff adopts is to use int32s
to represent those limits, which implies that whatever the type
of the integer value being switched on, we will generate a dense
or a lookup switch for it only if all case values fit into an int32.
Since case values over two billion are vanishingly rare, this should be
an acceptable restriction.
Use uints instead of ints to represent counts of things.
Delete an unneeded pair of arguments.
compiler/lookup_switch_util.m:
Conform to the changes in switch_util.m. Use some of the new types
there to make arguments in arguments lists less confusable.
Provide some new utility operations.
Add XXXs where the basic operations we need seem not to exist.
compiler/dense_switch.m:
compiler/lookup_switch.m:
Use the new types in switch_util.m that can represent switches
on any integer type.
compiler/ml_lookup_switch.m:
compiler/ml_simplify_switch.m:
compiler/ml_string_switch.m:
compiler/ml_switch_gen.m:
compiler/switch_gen.m:
Conform to the changes above, and thereby gain the ability
to generate switches on integer types other than int itself.
library/int64.m:
Add a (commmented-out) declaration of an operation that could
help resolve one of the issues in new code in the modules above.
Similar operations would be needed in the modules of other
sized integer types as well.
library/library.m:
Fix a typo.
tests/hard_coded/bug582.{m,exp}:
Add a test case for this issue. Note that while we test whether
we get the expected output, there is no simple automatic way
to detect whether it was generated using a lookup table.
tests/hard_coded/Mmakefile:
Enable the new test case.
2202 lines
94 KiB
Mathematica
2202 lines
94 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 1994-2011 The University of Melbourne.
|
|
% Copyright (C) 2015, 2017-2018, 2021-2024, 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_string_switch.m.
|
|
% Authors: fjh, zs.
|
|
%
|
|
% For switches on strings, we can generate
|
|
%
|
|
% - a trie;
|
|
% - a hash table using open addressing to resolve hash conflicts; or
|
|
% - a sorted table for binary search.
|
|
%
|
|
% The hash table has a higher startup cost than binary search, but should use
|
|
% fewer comparisons, so it is preferred for bigger tables. The trie approach
|
|
% does not need any startup code and examines each character just once,
|
|
% so it does the least work, but it does do a hard-to-predict jump
|
|
% for each character in the trie (which usually *won't* be all the characters
|
|
% in the string being switched on).
|
|
%
|
|
% All of these techniques are implemented both for switches in which
|
|
% each arm executes code (we call these the "jump" versions, since the task
|
|
% of the indexing method is to decide which piece of code to jump to),
|
|
% and for switches in which each arm looks up the data to return in tables
|
|
% (we call these the "lookup" versions). The lookup versions themselves
|
|
% come in two distinct flavours: those in which each arm has at most
|
|
% one solution, and those in which some arms have more than one solution.
|
|
%
|
|
% WARNING: the code here is quite similar to the code in string_switch.m.
|
|
% Any changes here may require similar changes there, and vice versa.
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module ml_backend.ml_string_switch.
|
|
:- 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.ml_lookup_switch.
|
|
:- import_module ml_backend.mlds.
|
|
:- import_module parse_tree.
|
|
:- import_module parse_tree.prog_data.
|
|
|
|
:- import_module list.
|
|
|
|
:- pred ml_generate_string_trie_jump_switch(mlds_rval::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.
|
|
|
|
:- pred ml_generate_string_trie_lookup_switch(mlds_rval::in,
|
|
list(tagged_case)::in, ml_lookup_switch_info::in,
|
|
code_model::in, can_fail::in, prog_context::in,
|
|
list(mlds_stmt)::out, ml_gen_info::out) is det.
|
|
|
|
:- pred ml_generate_string_hash_jump_switch(mlds_rval::in,
|
|
list(tagged_case)::in, code_model::in, can_fail::in, packed_word_map::in,
|
|
prog_context::in, list(mlds_local_var_defn)::out, list(mlds_stmt)::out,
|
|
ml_gen_info::in, ml_gen_info::out) is det.
|
|
|
|
:- pred ml_generate_string_hash_lookup_switch(mlds_rval::in,
|
|
list(tagged_case)::in, ml_lookup_switch_info::in,
|
|
code_model::in, can_fail::in, prog_context::in,
|
|
list(mlds_local_var_defn)::out, list(mlds_stmt)::out,
|
|
ml_gen_info::out) is det.
|
|
|
|
:- pred ml_generate_string_binary_jump_switch(mlds_rval::in,
|
|
list(tagged_case)::in, code_model::in, can_fail::in, packed_word_map::in,
|
|
prog_context::in, list(mlds_local_var_defn)::out, list(mlds_stmt)::out,
|
|
ml_gen_info::in, ml_gen_info::out) is det.
|
|
|
|
:- pred ml_generate_string_binary_lookup_switch(mlds_rval::in,
|
|
list(tagged_case)::in, ml_lookup_switch_info::in,
|
|
code_model::in, can_fail::in, prog_context::in,
|
|
list(mlds_local_var_defn)::out, list(mlds_stmt)::out,
|
|
ml_gen_info::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module backend_libs.
|
|
:- import_module backend_libs.builtin_ops.
|
|
:- import_module backend_libs.lookup_switch_util.
|
|
:- import_module backend_libs.string_encoding.
|
|
:- import_module backend_libs.string_switch_util.
|
|
:- import_module hlds.hlds_data.
|
|
:- import_module hlds.hlds_module.
|
|
:- import_module libs.
|
|
:- import_module libs.globals.
|
|
:- import_module libs.options.
|
|
:- import_module ml_backend.ml_code_gen.
|
|
:- import_module ml_backend.ml_code_util.
|
|
:- import_module ml_backend.ml_global_data.
|
|
:- import_module ml_backend.ml_simplify_switch.
|
|
:- import_module ml_backend.ml_target_util.
|
|
:- import_module ml_backend.ml_util.
|
|
:- import_module parse_tree.builtin_lib_types.
|
|
|
|
:- import_module assoc_list.
|
|
:- import_module bool.
|
|
:- import_module cord.
|
|
:- import_module int.
|
|
:- import_module map.
|
|
:- import_module maybe.
|
|
:- import_module pair.
|
|
:- import_module require.
|
|
:- import_module string.
|
|
:- import_module unit.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% The implementation of jump tries.
|
|
%
|
|
|
|
ml_generate_string_trie_jump_switch(VarRval, TaggedCases, CodeModel, CanFail,
|
|
EntryPackedArgsMap, Context, Stmts, !Info) :-
|
|
ml_gen_tagged_case_codes_for_string_switch(CodeModel, EntryPackedArgsMap,
|
|
TaggedCases, map.init, CodeMap, [], ReachableConstVarMaps, !Info),
|
|
ml_gen_record_consensus_const_var_map(ReachableConstVarMaps, !Info),
|
|
ml_create_nested_switch_trie(TaggedCases, Context, VarRval, MaxCaseNum,
|
|
CaseNumVarLval, CaseNumVarDefn,
|
|
InitCaseNumVarStmt, GetCaseNumSwitchStmt, !Info),
|
|
|
|
map.to_assoc_list(CodeMap, CodeCases),
|
|
ml_generate_arms_of_switch_on_case_id(CodeCases,
|
|
cord.init, CaseNumSwitchArmsCord),
|
|
CaseNumSwitchArms = cord.list(CaseNumSwitchArmsCord),
|
|
ml_gen_default_of_switch_on_case_id(CodeModel, CanFail, Context,
|
|
CaseNumDefault, !Info),
|
|
CaseNumSwitchRange = mlds_switch_range(0, MaxCaseNum),
|
|
CaseSwitchStmt = ml_stmt_switch(mlds_builtin_type_int(int_type_int),
|
|
ml_lval(CaseNumVarLval), CaseNumSwitchRange, CaseNumSwitchArms,
|
|
CaseNumDefault, Context),
|
|
|
|
Stmt = ml_stmt_block([CaseNumVarDefn], [],
|
|
[InitCaseNumVarStmt, GetCaseNumSwitchStmt, CaseSwitchStmt], Context),
|
|
Stmts = [Stmt].
|
|
|
|
:- pred ml_gen_default_of_switch_on_case_id(code_model::in, can_fail::in,
|
|
prog_context::in, mlds_switch_default::out,
|
|
ml_gen_info::in, ml_gen_info::out) is det.
|
|
|
|
ml_gen_default_of_switch_on_case_id(CodeModel, CanFail, Context,
|
|
CaseNumDefault, !Info) :-
|
|
(
|
|
CanFail = cannot_fail,
|
|
CaseNumDefault = default_is_unreachable
|
|
;
|
|
CanFail = can_fail,
|
|
ml_gen_maybe_switch_failure(CodeModel, CanFail, Context, FailStmts,
|
|
!Info),
|
|
(
|
|
FailStmts = [],
|
|
(
|
|
CodeModel = model_det,
|
|
% For model_det switches, the default must be unreachable.
|
|
CaseNumDefault = default_is_unreachable
|
|
;
|
|
CodeModel = model_semi,
|
|
% For model_semi switches, the default should contain at least
|
|
% an assignment of FALSE to the "succeeded" flag.
|
|
unexpected($pred, "failure does not assign to succeeded")
|
|
;
|
|
CodeModel = model_non,
|
|
FailStmt = ml_stmt_block([], [], [], Context),
|
|
CaseNumDefault = default_case(FailStmt)
|
|
)
|
|
;
|
|
(
|
|
FailStmts = [FailStmt]
|
|
;
|
|
FailStmts = [_, _ | _],
|
|
FailStmt = ml_stmt_block([], [], FailStmts, Context)
|
|
),
|
|
CaseNumDefault = default_case(FailStmt)
|
|
)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% The implementation of lookup tries.
|
|
%
|
|
|
|
ml_generate_string_trie_lookup_switch(VarRval, TaggedCases, LookupSwitchInfo,
|
|
CodeModel, CanFail, Context, Stmts, !:Info) :-
|
|
LookupSwitchInfo = ml_lookup_switch_info(CaseIdConsts, OutVars, OutTypes,
|
|
!:Info),
|
|
ml_create_nested_switch_trie(TaggedCases, Context, VarRval, MaxCaseNum,
|
|
CaseNumVarLval, CaseNumVarDefn,
|
|
InitCaseNumVarStmt, GetCaseNumSwitchStmt, !Info),
|
|
(
|
|
CaseIdConsts = all_one_soln(CaseIdValueMap),
|
|
map.to_assoc_list(CaseIdValueMap, CaseIdValues),
|
|
ml_generate_string_trie_simple_lookup_switch(MaxCaseNum,
|
|
CaseNumVarLval, CaseNumVarDefn,
|
|
InitCaseNumVarStmt, GetCaseNumSwitchStmt,
|
|
CaseIdValues, OutVars, OutTypes, CodeModel, CanFail, Context,
|
|
Stmts, !Info)
|
|
;
|
|
CaseIdConsts = some_several_solns(CaseIdSolnMap, _Unit),
|
|
expect(unify(CodeModel, model_non), $pred, "CodeModel != model_non"),
|
|
map.to_assoc_list(CaseIdSolnMap, CaseIdSolns),
|
|
ml_generate_string_trie_several_soln_lookup_switch(MaxCaseNum,
|
|
CaseNumVarLval, CaseNumVarDefn,
|
|
InitCaseNumVarStmt, GetCaseNumSwitchStmt,
|
|
CaseIdSolns, OutVars, OutTypes, CanFail, Context,
|
|
Stmts, !Info)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred ml_generate_string_trie_simple_lookup_switch(int::in,
|
|
mlds_lval::in, mlds_local_var_defn::in, mlds_stmt::in, mlds_stmt::in,
|
|
assoc_list(case_id, list(mlds_rval))::in,
|
|
list(prog_var)::in, list(mlds_type)::in,
|
|
code_model::in, can_fail::in, prog_context::in,
|
|
list(mlds_stmt)::out, ml_gen_info::in, ml_gen_info::out) is det.
|
|
|
|
ml_generate_string_trie_simple_lookup_switch(MaxCaseNum,
|
|
CaseNumVarLval, CaseNumVarDefn,
|
|
InitCaseNumVarStmt, GetCaseNumSwitchStmt,
|
|
CaseIdValues, OutVars, OutTypes, CodeModel, CanFail, Context,
|
|
Stmts, !Info) :-
|
|
ml_gen_info_get_module_info(!.Info, ModuleInfo),
|
|
module_info_get_name(ModuleInfo, ModuleName),
|
|
MLDS_ModuleName = mercury_module_name_to_mlds(ModuleName),
|
|
ml_gen_info_get_target(!.Info, Target),
|
|
|
|
(
|
|
OutTypes = [_ | _],
|
|
ml_gen_info_get_global_data(!.Info, GlobalData0),
|
|
ml_gen_static_vector_type(MLDS_ModuleName, Context, Target,
|
|
OutTypes, StructTypeNum, StructType, OutFieldIds,
|
|
GlobalData0, GlobalData1),
|
|
ml_gen_string_trie_simple_lookup_slots(StructType, CaseIdValues,
|
|
0, AfterLastCaseNum, cord.init, RowInitializersCord),
|
|
expect(unify(MaxCaseNum + 1, AfterLastCaseNum), $pred,
|
|
"MaxCaseNum + 1 != AfterLastCaseNum"),
|
|
RowInitializers = cord.list(RowInitializersCord),
|
|
ml_gen_static_vector_defn(MLDS_ModuleName, StructTypeNum,
|
|
RowInitializers, VectorCommon, GlobalData1, GlobalData),
|
|
ml_gen_info_set_global_data(GlobalData, !Info),
|
|
|
|
ml_generate_field_assigns(OutVars, OutTypes, OutFieldIds, VectorCommon,
|
|
StructType, ml_lval(CaseNumVarLval), Context,
|
|
FieldAssignStmts, !Info)
|
|
;
|
|
OutTypes = [],
|
|
FieldAssignStmts = []
|
|
),
|
|
|
|
FoundMatchComment = "we found a match; look up the results",
|
|
FoundMatchCommentStmt =
|
|
ml_stmt_atomic(comment(FoundMatchComment), Context),
|
|
CommentedFieldAssignStmts = [FoundMatchCommentStmt | FieldAssignStmts],
|
|
(
|
|
CodeModel = model_det,
|
|
LookupStmts = CommentedFieldAssignStmts
|
|
;
|
|
CodeModel = model_semi,
|
|
ml_gen_set_success(ml_const(mlconst_true), Context, SetSuccessTrueStmt,
|
|
!Info),
|
|
LookupStmts = CommentedFieldAssignStmts ++ [SetSuccessTrueStmt]
|
|
;
|
|
CodeModel = model_non,
|
|
unexpected($pred, "model_non")
|
|
),
|
|
LookupStmt = ml_stmt_block([], [], LookupStmts, Context),
|
|
|
|
ml_gen_maybe_switch_failure(CodeModel, CanFail, Context, FailStmts,
|
|
!Info),
|
|
(
|
|
FailStmts = [],
|
|
ResultStmt = LookupStmt
|
|
;
|
|
(
|
|
FailStmts = [FailStmt]
|
|
;
|
|
FailStmts = [_, _ | _],
|
|
FailStmt = ml_stmt_block([], [], FailStmts, Context)
|
|
),
|
|
IsCaseNumNegCond = ml_binop(int_cmp(int_type_int, lt),
|
|
ml_lval(CaseNumVarLval), ml_const(mlconst_int(0))),
|
|
ResultStmt = ml_stmt_if_then_else(IsCaseNumNegCond, FailStmt,
|
|
yes(LookupStmt), Context)
|
|
),
|
|
|
|
Stmt = ml_stmt_block([CaseNumVarDefn], [],
|
|
[InitCaseNumVarStmt, GetCaseNumSwitchStmt, ResultStmt], Context),
|
|
Stmts = [Stmt].
|
|
|
|
:- pred ml_gen_string_trie_simple_lookup_slots(mlds_type::in,
|
|
assoc_list(case_id, list(mlds_rval))::in, int::in, int::out,
|
|
cord(mlds_initializer)::in, cord(mlds_initializer)::out) is det.
|
|
|
|
ml_gen_string_trie_simple_lookup_slots(_StructType, [],
|
|
!CurCaseNum, !RowInitializersCord).
|
|
ml_gen_string_trie_simple_lookup_slots(StructType,
|
|
[CaseIdValue | CaseIdValues], !CurCaseNum, !RowInitializersCord) :-
|
|
CaseIdValue = CaseId - OutRvals,
|
|
CaseId = case_id(CaseIdNum),
|
|
expect(unify(CaseIdNum, !.CurCaseNum), $pred, "CaseIdNum != !.CurCaseNum"),
|
|
OutInitializers = list.map(wrap_init_obj, OutRvals),
|
|
RowInitializer = init_struct(StructType, OutInitializers),
|
|
cord.snoc(RowInitializer, !RowInitializersCord),
|
|
!:CurCaseNum = !.CurCaseNum + 1,
|
|
ml_gen_string_trie_simple_lookup_slots(StructType, CaseIdValues,
|
|
!CurCaseNum, !RowInitializersCord).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred ml_generate_string_trie_several_soln_lookup_switch(int::in,
|
|
mlds_lval::in, mlds_local_var_defn::in, mlds_stmt::in, mlds_stmt::in,
|
|
assoc_list(case_id, soln_consts(mlds_rval))::in,
|
|
list(prog_var)::in, list(mlds_type)::in, can_fail::in, prog_context::in,
|
|
list(mlds_stmt)::out, ml_gen_info::in, ml_gen_info::out) is det.
|
|
|
|
ml_generate_string_trie_several_soln_lookup_switch(MaxCaseNum,
|
|
CaseNumVarLval, CaseNumVarDefn,
|
|
InitCaseNumVarStmt, GetCaseNumSwitchStmt,
|
|
CaseIdSolns, OutVars, OutTypes, CanFail, Context, Stmts, !Info) :-
|
|
ml_gen_info_get_module_info(!.Info, ModuleInfo),
|
|
module_info_get_name(ModuleInfo, ModuleName),
|
|
MLDS_ModuleName = mercury_module_name_to_mlds(ModuleName),
|
|
ml_gen_info_get_target(!.Info, Target),
|
|
|
|
ml_gen_info_get_global_data(!.Info, GlobalData0),
|
|
MLDS_IntType = mlds_builtin_type_int(int_type_int),
|
|
FirstSolnFieldTypes = [MLDS_IntType, MLDS_IntType | OutTypes],
|
|
ml_gen_static_vector_type(MLDS_ModuleName, Context, Target,
|
|
FirstSolnFieldTypes, FirstSolnStructTypeNum, FirstSolnStructType,
|
|
FirstSolnFieldIds, GlobalData0, GlobalData1),
|
|
ml_gen_static_vector_type(MLDS_ModuleName, Context, Target,
|
|
OutTypes, LaterSolnStructTypeNum, LaterSolnStructType,
|
|
LaterSolnOutFieldIds, GlobalData1, GlobalData2),
|
|
( if
|
|
FirstSolnFieldIds =
|
|
[NumLaterSolnsFieldIdPrime, FirstLaterSolnRowFieldIdPrime |
|
|
FirstSolnOutFieldIdsPrime]
|
|
then
|
|
NumLaterSolnsFieldId = NumLaterSolnsFieldIdPrime,
|
|
FirstLaterSolnRowFieldId = FirstLaterSolnRowFieldIdPrime,
|
|
FirstSolnOutFieldIds = FirstSolnOutFieldIdsPrime
|
|
else
|
|
unexpected($pred, "bad FieldIds")
|
|
),
|
|
|
|
ml_gen_string_trie_several_soln_lookup_slots(
|
|
FirstSolnStructType, LaterSolnStructType, CaseIdSolns,
|
|
0, AfterLastCaseNum, 0, cord.init, FirstSolnRowInitializersCord,
|
|
cord.init, LaterSolnRowInitializerCord),
|
|
expect(unify(MaxCaseNum + 1, AfterLastCaseNum), $pred,
|
|
"MaxCaseNum + 1 != AfterLastCaseNum"),
|
|
FirstSolnRowInitializers = cord.list(FirstSolnRowInitializersCord),
|
|
LaterSolnRowInitializers = cord.list(LaterSolnRowInitializerCord),
|
|
ml_gen_static_vector_defn(MLDS_ModuleName, FirstSolnStructTypeNum,
|
|
FirstSolnRowInitializers, FirstSolnVectorCommon,
|
|
GlobalData2, GlobalData3),
|
|
ml_gen_static_vector_defn(MLDS_ModuleName, LaterSolnStructTypeNum,
|
|
LaterSolnRowInitializers, LaterSolnVectorCommon,
|
|
GlobalData3, GlobalData),
|
|
ml_gen_info_set_global_data(GlobalData, !Info),
|
|
|
|
ml_gen_several_soln_lookup_code(Context, ml_lval(CaseNumVarLval),
|
|
OutVars, OutTypes, FirstSolnStructType, LaterSolnStructType,
|
|
NumLaterSolnsFieldId, FirstLaterSolnRowFieldId,
|
|
FirstSolnOutFieldIds, LaterSolnOutFieldIds,
|
|
FirstSolnVectorCommon, LaterSolnVectorCommon,
|
|
do_not_need_bit_vec_check_no_gaps,
|
|
MatchDefns, SuccessStmts, !Info),
|
|
SuccessBlockStmt = ml_stmt_block(MatchDefns, [], SuccessStmts, Context),
|
|
(
|
|
CanFail = can_fail,
|
|
IsCaseNumNonNegCond = ml_binop(int_cmp(int_type_int, ge),
|
|
ml_lval(CaseNumVarLval), ml_const(mlconst_int(0))),
|
|
ResultStmt = ml_stmt_if_then_else(IsCaseNumNonNegCond,
|
|
SuccessBlockStmt, no, Context)
|
|
;
|
|
CanFail = cannot_fail,
|
|
ResultStmt = SuccessBlockStmt
|
|
),
|
|
Stmt = ml_stmt_block([CaseNumVarDefn], [],
|
|
[InitCaseNumVarStmt, GetCaseNumSwitchStmt, ResultStmt], Context),
|
|
Stmts = [Stmt].
|
|
|
|
:- pred ml_gen_string_trie_several_soln_lookup_slots(
|
|
mlds_type::in, mlds_type::in,
|
|
assoc_list(case_id, soln_consts(mlds_rval))::in, int::in, int::out,
|
|
int::in,
|
|
cord(mlds_initializer)::in, cord(mlds_initializer)::out,
|
|
cord(mlds_initializer)::in, cord(mlds_initializer)::out) is det.
|
|
|
|
ml_gen_string_trie_several_soln_lookup_slots(
|
|
_FirstSolnStructType, _LaterSolnStructType, [], !CurCaseNum,
|
|
_CurLaterSolnIndex,
|
|
!FirstSolnRowInitializersCord, !LaterSolnRowInitializersCord).
|
|
ml_gen_string_trie_several_soln_lookup_slots(
|
|
FirstSolnStructType, LaterSolnStructType,
|
|
[CaseIdSolns | CaseIdsSolns], !CurCaseNum, !.CurLaterSolnIndex,
|
|
!FirstSolnRowInitializersCord, !LaterSolnRowInitializersCord) :-
|
|
CaseIdSolns = CaseId - Solns,
|
|
CaseId = case_id(CaseIdNum),
|
|
expect(unify(CaseIdNum, !.CurCaseNum), $pred, "CaseIdNum != !.CurCaseNum"),
|
|
(
|
|
Solns = one_soln(FirstSolnRvals),
|
|
NumLaterSolnsRval = ml_const(mlconst_int(0)),
|
|
FirstLaterSlotRval = ml_const(mlconst_int(-1))
|
|
;
|
|
Solns = several_solns(FirstSolnRvals, LaterSolns),
|
|
list.length(LaterSolns, NumLaterSolns),
|
|
NumLaterSolnsRval = ml_const(mlconst_int(NumLaterSolns)),
|
|
FirstLaterSlotRval = ml_const(mlconst_int(!.CurLaterSolnIndex)),
|
|
LaterSolnRowInitializers = list.map(
|
|
ml_construct_later_soln_row(LaterSolnStructType),
|
|
LaterSolns),
|
|
!:LaterSolnRowInitializersCord = !.LaterSolnRowInitializersCord ++
|
|
cord.from_list(LaterSolnRowInitializers),
|
|
!:CurLaterSolnIndex = !.CurLaterSolnIndex + NumLaterSolns
|
|
),
|
|
FirstSolnRowRvals =
|
|
[NumLaterSolnsRval, FirstLaterSlotRval | FirstSolnRvals],
|
|
FirstSolnRowInitializer = init_struct(FirstSolnStructType,
|
|
list.map(wrap_init_obj, FirstSolnRowRvals)),
|
|
cord.snoc(FirstSolnRowInitializer, !FirstSolnRowInitializersCord),
|
|
!:CurCaseNum = !.CurCaseNum + 1,
|
|
ml_gen_string_trie_several_soln_lookup_slots(
|
|
FirstSolnStructType, LaterSolnStructType,
|
|
CaseIdsSolns, !CurCaseNum, !.CurLaterSolnIndex,
|
|
!FirstSolnRowInitializersCord, !LaterSolnRowInitializersCord).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Code useful for both the jump and lookup versions of tries.
|
|
%
|
|
|
|
:- pred ml_create_nested_switch_trie(list(tagged_case)::in, prog_context::in,
|
|
mlds_rval::in, int::out, mlds_lval::out, mlds_local_var_defn::out,
|
|
mlds_stmt::out, mlds_stmt::out, ml_gen_info::in, ml_gen_info::out) is det.
|
|
|
|
ml_create_nested_switch_trie(TaggedCases, Context, VarRval, MaxCaseNum,
|
|
CaseNumVarLval, CaseNumVarDefn,
|
|
InitCaseNumVarStmt, GetCaseNumSwitchStmt, !Info) :-
|
|
ml_gen_info_get_target(!.Info, Target),
|
|
( Target = ml_target_c, CompilationTarget = target_c
|
|
; Target = ml_target_java, CompilationTarget = target_java
|
|
; Target = ml_target_csharp, CompilationTarget = target_csharp
|
|
),
|
|
Encoding = target_string_encoding(CompilationTarget),
|
|
build_str_case_id_list(TaggedCases, MaxCaseNum, StrsCaseIds),
|
|
create_trie(Encoding, StrsCaseIds, TopTrieNode),
|
|
ml_gen_trie_case_num_var_and_init(Context, CaseNumVarLval,
|
|
CaseNumVarDefn, InitCaseNumVarStmt, !Info),
|
|
ml_convert_trie_to_nested_switches(Encoding, VarRval, CaseNumVarLval,
|
|
Context, 0, TopTrieNode, GetCaseNumSwitchStmt).
|
|
|
|
% Generate the following local variable declaration:
|
|
% int case_num = -1;
|
|
%
|
|
:- pred ml_gen_trie_case_num_var_and_init(prog_context::in,
|
|
mlds_lval::out, mlds_local_var_defn::out, mlds_stmt::out,
|
|
ml_gen_info::in, ml_gen_info::out) is det.
|
|
|
|
ml_gen_trie_case_num_var_and_init(Context, CaseNumVarLval, CaseNumVarDefn,
|
|
InitStmt, !Info) :-
|
|
ml_gen_info_new_aux_var_name(mcav_case_num, CaseNumVarName, !Info),
|
|
CaseNumVarType = mlds_builtin_type_int(int_type_int),
|
|
% We never need to trace ints.
|
|
CaseNumVarDefn = ml_gen_mlds_var_decl(CaseNumVarName, CaseNumVarType,
|
|
gc_no_stmt, Context),
|
|
CaseNumVarLval = ml_local_var(CaseNumVarName, CaseNumVarType),
|
|
|
|
InitAssign = assign(CaseNumVarLval, ml_const(mlconst_int(-1))),
|
|
InitStmt = ml_stmt_atomic(InitAssign, Context).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred ml_convert_trie_to_nested_switches(string_encoding::in, mlds_rval::in,
|
|
mlds_lval::in, prog_context::in, int::in, trie_node::in, mlds_stmt::out)
|
|
is det.
|
|
|
|
ml_convert_trie_to_nested_switches(Encoding, VarRval, CaseNumVarLval, Context,
|
|
NumMatched, TrieNode, Stmt) :-
|
|
(
|
|
TrieNode = trie_leaf(RevMatchedCodeUnits, NotYetMatchedCodeUnits,
|
|
CaseId),
|
|
list.reverse(RevMatchedCodeUnits, MatchedCodeUnits),
|
|
AllCodeUnits = MatchedCodeUnits ++ NotYetMatchedCodeUnits,
|
|
list.length(MatchedCodeUnits, NumMatchedCodeUnits),
|
|
expect(unify(NumMatchedCodeUnits, NumMatched), $pred,
|
|
"NumMatchedCodeUnits != NumMatched"),
|
|
det_from_code_unit_list_in_encoding_allow_ill_formed(Encoding,
|
|
AllCodeUnits, EndStr),
|
|
CondRval = ml_binop(offset_str_eq(NumMatched, no_size),
|
|
VarRval, ml_const(mlconst_string(EndStr))),
|
|
|
|
CaseId = case_id(CaseNum),
|
|
CaseNumRval = ml_const(mlconst_int(CaseNum)),
|
|
SetCaseNumVarAssign = assign(CaseNumVarLval, CaseNumRval),
|
|
SetCaseNumVarStmt = ml_stmt_atomic(SetCaseNumVarAssign, Context),
|
|
|
|
Stmt = ml_stmt_if_then_else(CondRval, SetCaseNumVarStmt, no, Context)
|
|
;
|
|
TrieNode = trie_choice(RevMatchedCodeUnits, _ChoiceMap, MaybeEnd),
|
|
list.length(RevMatchedCodeUnits, NumRevMatchedCodeUnits),
|
|
expect(unify(NumRevMatchedCodeUnits, NumMatched), $pred,
|
|
"NumRevMatchedCodeUnits != NumMatched"),
|
|
|
|
chase_any_stick_in_trie(TrieNode, ChoicePairs,
|
|
StickCodeUnits, TrieNodeAfterStick),
|
|
(
|
|
StickCodeUnits = [_, _ | _],
|
|
list.length(StickCodeUnits, NumStickCodeUnits),
|
|
CmpOp = offset_str_eq(NumMatched, size(NumStickCodeUnits)),
|
|
list.reverse(RevMatchedCodeUnits, MatchedCodeUnits),
|
|
det_from_code_unit_list_in_encoding_allow_ill_formed(Encoding,
|
|
MatchedCodeUnits ++ StickCodeUnits, MatchedStickStr),
|
|
CondRval = ml_binop(CmpOp,
|
|
VarRval, ml_const(mlconst_string(MatchedStickStr))),
|
|
ml_convert_trie_to_nested_switches(Encoding, VarRval,
|
|
CaseNumVarLval, Context, NumMatched + NumStickCodeUnits,
|
|
TrieNodeAfterStick, AfterStickStmt),
|
|
Stmt = ml_stmt_if_then_else(CondRval, AfterStickStmt, no, Context)
|
|
;
|
|
( StickCodeUnits = []
|
|
; StickCodeUnits = [_]
|
|
),
|
|
ml_convert_trie_choices_to_nested_switches(Encoding, VarRval,
|
|
CaseNumVarLval, Context, NumMatched + 1, ChoicePairs,
|
|
cord.init, SwitchArmsCord0),
|
|
SwitchArms0 = cord.list(SwitchArmsCord0),
|
|
(
|
|
MaybeEnd = no,
|
|
SwitchArms = SwitchArms0
|
|
;
|
|
MaybeEnd = yes(EndCaseId),
|
|
|
|
NullCodeUnit = 0, % Match the terminating NUL character.
|
|
NullMatchCond =
|
|
match_value(ml_const(mlconst_int(NullCodeUnit))),
|
|
|
|
EndCaseId = case_id(EndCaseNum),
|
|
EndCaseNumRval = ml_const(mlconst_int(EndCaseNum)),
|
|
EndSetCaseNumVarAssign =
|
|
assign(CaseNumVarLval, EndCaseNumRval),
|
|
EndSetCaseNumVarStmt =
|
|
ml_stmt_atomic(EndSetCaseNumVarAssign, Context),
|
|
EndSwitchArm = mlds_switch_case(NullMatchCond, [],
|
|
EndSetCaseNumVarStmt),
|
|
SwitchArms = [EndSwitchArm | SwitchArms0]
|
|
),
|
|
SwitchCodeUnitRval = ml_binop(string_unsafe_index_code_unit,
|
|
VarRval, ml_const(mlconst_int(NumMatched))),
|
|
% Could we set this to a known range? If we could,
|
|
% would it be useful?
|
|
SwitchRange = mlds_switch_range_unknown,
|
|
Stmt = ml_stmt_switch( mlds_builtin_type_int(int_type_int),
|
|
SwitchCodeUnitRval, SwitchRange, SwitchArms,
|
|
default_do_nothing, Context)
|
|
)
|
|
).
|
|
|
|
:- pred ml_convert_trie_choices_to_nested_switches(string_encoding::in,
|
|
mlds_rval::in, mlds_lval::in, prog_context::in, int::in,
|
|
assoc_list(int, trie_node)::in,
|
|
cord(mlds_switch_case)::in, cord(mlds_switch_case)::out) is det.
|
|
|
|
ml_convert_trie_choices_to_nested_switches(_, _, _, _, _, [], !SwitchArmsCord).
|
|
ml_convert_trie_choices_to_nested_switches(Encoding, VarRval, CaseNumVarLval,
|
|
Context, NumMatched, [ChoicePair | ChoicePairs], !SwitchArmsCord) :-
|
|
ChoicePair = CodeUnit - SubTrieNode,
|
|
ml_convert_trie_to_nested_switches(Encoding, VarRval, CaseNumVarLval,
|
|
Context, NumMatched, SubTrieNode, SwitchArmStmt),
|
|
MatchCond = match_value(ml_const(mlconst_int(CodeUnit))),
|
|
SwitchArm = mlds_switch_case(MatchCond, [], SwitchArmStmt),
|
|
cord.snoc(SwitchArm, !SwitchArmsCord),
|
|
ml_convert_trie_choices_to_nested_switches(Encoding, VarRval,
|
|
CaseNumVarLval, Context, NumMatched, ChoicePairs, !SwitchArmsCord).
|
|
|
|
:- pred ml_generate_arms_of_switch_on_case_id(
|
|
assoc_list(case_id, mlds_stmt)::in,
|
|
cord(mlds_switch_case)::in, cord(mlds_switch_case)::out) is det.
|
|
|
|
ml_generate_arms_of_switch_on_case_id([], !CaseCord).
|
|
ml_generate_arms_of_switch_on_case_id([CasePair | CasePairs], !CaseCord) :-
|
|
CasePair = CaseId - CaseStmt,
|
|
CaseId = case_id(CaseNum),
|
|
MatchCond = match_value(ml_const(mlconst_int(CaseNum))),
|
|
Case = mlds_switch_case(MatchCond, [], CaseStmt),
|
|
cord.snoc(Case, !CaseCord),
|
|
ml_generate_arms_of_switch_on_case_id(CasePairs, !CaseCord).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% The implementation of jump hash tables.
|
|
%
|
|
|
|
ml_generate_string_hash_jump_switch(VarRval, TaggedCases, CodeModel, CanFail,
|
|
EntryPackedArgsMap, Context, Defns, Stmts, !Info) :-
|
|
ml_gen_tagged_case_codes_for_string_switch(CodeModel, EntryPackedArgsMap,
|
|
TaggedCases, map.init, CodeMap, [], ReachableConstVarMaps, !Info),
|
|
ml_gen_record_consensus_const_var_map(ReachableConstVarMaps, !Info),
|
|
build_str_case_id_list(TaggedCases, _MaxCaseNum, StrsCaseIds),
|
|
|
|
% Compute the hash table.
|
|
construct_string_hash_cases(StrsCaseIds, allow_doubling,
|
|
TableSize, HashSlotMap, HashOp, NumCollisions),
|
|
HashMask = TableSize - 1,
|
|
|
|
ml_gen_info_get_module_info(!.Info, ModuleInfo),
|
|
module_info_get_name(ModuleInfo, ModuleName),
|
|
MLDS_ModuleName = mercury_module_name_to_mlds(ModuleName),
|
|
ml_gen_info_get_target(!.Info, Target),
|
|
|
|
MLDS_StringType = mlds_builtin_type_string,
|
|
MLDS_IntType = mlds_builtin_type_int(int_type_int),
|
|
|
|
( if NumCollisions = 0 then
|
|
MLDS_ArgTypes = [MLDS_StringType],
|
|
LoopPresent = no
|
|
else
|
|
MLDS_ArgTypes = [MLDS_StringType, MLDS_IntType],
|
|
LoopPresent = yes
|
|
),
|
|
|
|
ml_gen_string_hash_switch_search_vars(CodeModel, CanFail, LoopPresent,
|
|
Context, VarRval, HashSearchInfo, !Info),
|
|
HashSearchInfo = ml_hash_search_info(_CodeModel, _LoopPresent,
|
|
_Context, _VarRval, SlotVarNameType, _StringVarNameType,
|
|
_MaybeStopLoopNameType, _FailStmts, Defns),
|
|
|
|
SlotVarNameType = mlds_local_var_name_type(SlotVarName, SlotVarType),
|
|
SlotVarLval = ml_local_var(SlotVarName, SlotVarType),
|
|
|
|
ml_gen_info_get_global_data(!.Info, GlobalData0),
|
|
ml_gen_static_vector_type(MLDS_ModuleName, Context, Target,
|
|
MLDS_ArgTypes, StructTypeNum, StructType, FieldIds,
|
|
GlobalData0, GlobalData1),
|
|
( if NumCollisions = 0 then
|
|
(
|
|
FieldIds = [StringFieldId],
|
|
MaybeNextSlotFieldId = no
|
|
;
|
|
( FieldIds = []
|
|
; FieldIds = [_, _ | _]
|
|
),
|
|
unexpected($pred, "bad FieldIds")
|
|
)
|
|
else
|
|
(
|
|
FieldIds = [StringFieldId, NextSlotFieldId],
|
|
MaybeNextSlotFieldId = yes(NextSlotFieldId)
|
|
;
|
|
( FieldIds = []
|
|
; FieldIds = [_]
|
|
; FieldIds = [_, _, _ | _]
|
|
),
|
|
unexpected($pred, "bad FieldIds")
|
|
)
|
|
),
|
|
% Generate the rows of the hash table.
|
|
ml_gen_string_hash_jump_slots(0, TableSize, HashSlotMap, StructType,
|
|
MaybeNextSlotFieldId, cord.init, RowInitializersCord,
|
|
map.init, RevMap),
|
|
RowInitializers = cord.list(RowInitializersCord),
|
|
% Generate the hash table.
|
|
ml_gen_static_vector_defn(MLDS_ModuleName, StructTypeNum, RowInitializers,
|
|
VectorCommon, GlobalData1, GlobalData),
|
|
ml_gen_info_set_global_data(GlobalData, !Info),
|
|
|
|
% Generate code which does the hash table lookup.
|
|
map.to_assoc_list(RevMap, RevMapEntries),
|
|
generate_string_jump_switch_arms(CodeMap, RevMapEntries, [], SlotsCases0),
|
|
list.sort(SlotsCases0, SlotsCases),
|
|
|
|
SwitchStmt0 = ml_stmt_switch(SlotVarType, ml_lval(SlotVarLval),
|
|
mlds_switch_range(0, TableSize - 1), SlotsCases,
|
|
default_is_unreachable, Context),
|
|
ml_simplify_switch(SwitchStmt0, SwitchStmt, !Info),
|
|
|
|
FoundMatchComment = "we found a match; dispatch to the corresponding code",
|
|
FoundMatchStmts = [
|
|
ml_stmt_atomic(comment(FoundMatchComment), Context),
|
|
SwitchStmt
|
|
],
|
|
|
|
InitialComment = "hashed string jump switch",
|
|
ml_gen_string_hash_switch_search(InitialComment, HashSearchInfo,
|
|
HashOp, VectorCommon, StructType,
|
|
StringFieldId, MaybeNextSlotFieldId, HashMask,
|
|
[], FoundMatchStmts, Stmts, !Info).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred ml_gen_tagged_case_codes_for_string_switch(code_model::in,
|
|
packed_word_map::in, list(tagged_case)::in,
|
|
map(case_id, mlds_stmt)::in, map(case_id, 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_gen_tagged_case_codes_for_string_switch(_CodeModel, _EntryPackedArgsMap, [],
|
|
!CodeMap, !ReachableConstVarMaps, !Info).
|
|
ml_gen_tagged_case_codes_for_string_switch(CodeModel, EntryPackedArgsMap,
|
|
[TaggedCase | TaggedCases], !CodeMap, !ReachableConstVarMaps, !Info) :-
|
|
ml_gen_tagged_case_code_for_string_switch(CodeModel, EntryPackedArgsMap,
|
|
TaggedCase, !CodeMap, !ReachableConstVarMaps, !Info),
|
|
ml_gen_tagged_case_codes_for_string_switch(CodeModel, EntryPackedArgsMap,
|
|
TaggedCases, !CodeMap, !ReachableConstVarMaps, !Info).
|
|
|
|
:- pred ml_gen_tagged_case_code_for_string_switch_return_case_id(
|
|
code_model::in, packed_word_map::in, tagged_case::in, case_id::out,
|
|
map(case_id, mlds_stmt)::in, map(case_id, mlds_stmt)::out,
|
|
list(ml_ground_term_map)::in, list(ml_ground_term_map)::out,
|
|
ml_gen_info::in, ml_gen_info::out, unit::in, unit::out) is det.
|
|
|
|
ml_gen_tagged_case_code_for_string_switch_return_case_id(CodeModel,
|
|
EntryPackedArgsMap, TaggedCase, CaseId,
|
|
!CodeMap, !ReachableConstVarMaps, !Info, _, unit) :-
|
|
TaggedCase = tagged_case(_, _, CaseId, _),
|
|
ml_gen_tagged_case_code_for_string_switch(CodeModel, EntryPackedArgsMap,
|
|
TaggedCase, !CodeMap, !ReachableConstVarMaps, !Info).
|
|
|
|
:- pred ml_gen_tagged_case_code_for_string_switch(code_model::in,
|
|
packed_word_map::in, tagged_case::in,
|
|
map(case_id, mlds_stmt)::in, map(case_id, 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_gen_tagged_case_code_for_string_switch(CodeModel, EntryPackedArgsMap,
|
|
TaggedCase, !CodeMap, !ReachableConstVarMaps, !Info) :-
|
|
ml_gen_info_set_packed_word_map(EntryPackedArgsMap, !Info),
|
|
TaggedCase = tagged_case(MainTaggedConsId, OtherTaggedConsIds,
|
|
CaseId, Goal),
|
|
ml_gen_goal_as_branch_block(CodeModel, Goal, GoalStmt,
|
|
!ReachableConstVarMaps, !Info),
|
|
MainString = gen_string_switch_case_comment(MainTaggedConsId),
|
|
OtherStrings = list.map(gen_string_switch_case_comment,
|
|
OtherTaggedConsIds),
|
|
Strings = string.join_list(", ", [MainString | OtherStrings]),
|
|
% Note that the order of the strings in the comment will in general
|
|
% not match the order of the hash slots for which the case applies.
|
|
% In other words, if e.g. OtherTaggedConsIds has two elements and
|
|
% CaseStmt has the C code "case Slot1: case Slot2: case Slot3:"
|
|
% generated in front of it, Slot1 can be the slot of any of
|
|
% MainTaggedConsId and the two OtherTaggedConsIds; it will be the slot
|
|
% of MainTaggedConsId only by accident.
|
|
CommentString = "case " ++ Strings,
|
|
Goal = hlds_goal(_GoalExpr, GoalInfo),
|
|
Context = goal_info_get_context(GoalInfo),
|
|
Comment = ml_stmt_atomic(comment(CommentString), Context),
|
|
CaseStmt = ml_stmt_block([], [], [Comment, GoalStmt], Context),
|
|
map.det_insert(CaseId, CaseStmt, !CodeMap).
|
|
|
|
:- func gen_string_switch_case_comment(tagged_cons_id) = string.
|
|
|
|
gen_string_switch_case_comment(TaggedConsId) = String :-
|
|
TaggedConsId = tagged_cons_id(_ConsId, ConsTag),
|
|
( if ConsTag = string_tag(ConsString) then
|
|
String = """" ++ ConsString ++ """"
|
|
else
|
|
unexpected($pred, "non-string tag")
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% A list of all the hash slots that all have the same code.
|
|
%
|
|
:- type hash_slots
|
|
---> hash_slots(int, cord(int)).
|
|
|
|
% Maps case numbers (each of which identifies the code of one switch arm)
|
|
% to the hash slots that share that code.
|
|
%
|
|
:- type hash_slot_rev_map == map(case_id, hash_slots).
|
|
|
|
:- pred ml_gen_string_hash_jump_slots(int::in, int::in,
|
|
map(int, string_hash_slot(case_id))::in,
|
|
mlds_type::in, maybe(mlds_field_id)::in,
|
|
cord(mlds_initializer)::in, cord(mlds_initializer)::out,
|
|
hash_slot_rev_map::in, hash_slot_rev_map::out) is det.
|
|
|
|
ml_gen_string_hash_jump_slots(Slot, TableSize, HashSlotMap, StructType,
|
|
MaybeNextSlotId, !RowInitializersCord, !RevMap) :-
|
|
( if Slot = TableSize then
|
|
true
|
|
else
|
|
ml_gen_string_hash_jump_slot(Slot, HashSlotMap,
|
|
StructType, MaybeNextSlotId, RowInitializer, !RevMap),
|
|
cord.snoc(RowInitializer, !RowInitializersCord),
|
|
ml_gen_string_hash_jump_slots(Slot + 1, TableSize, HashSlotMap,
|
|
StructType, MaybeNextSlotId, !RowInitializersCord, !RevMap)
|
|
).
|
|
|
|
:- pred ml_gen_string_hash_jump_slot(int::in,
|
|
map(int, string_hash_slot(case_id))::in,
|
|
mlds_type::in, maybe(mlds_field_id)::in, mlds_initializer::out,
|
|
hash_slot_rev_map::in, hash_slot_rev_map::out) is det.
|
|
|
|
ml_gen_string_hash_jump_slot(Slot, HashSlotMap, StructType,
|
|
MaybeNextSlotId, RowInitializer, !RevMap) :-
|
|
( if map.search(HashSlotMap, Slot, HashSlotMapEntry) then
|
|
HashSlotMapEntry = string_hash_slot(String, Next, CaseId),
|
|
StringRval = ml_const(mlconst_string(String)),
|
|
NextSlotRval = ml_const(mlconst_int(Next)),
|
|
( if map.search(!.RevMap, CaseId, OldEntry) then
|
|
OldEntry = hash_slots(OldFirstSlot, LaterSlotsCord0),
|
|
cord.snoc(Slot, LaterSlotsCord0, LaterSlotsCord),
|
|
NewEntry = hash_slots(OldFirstSlot, LaterSlotsCord),
|
|
map.det_update(CaseId, NewEntry, !RevMap)
|
|
else
|
|
NewEntry = hash_slots(Slot, cord.init),
|
|
map.det_insert(CaseId, NewEntry, !RevMap)
|
|
)
|
|
else
|
|
StringRval = ml_const(mlconst_null(mlds_builtin_type_string)),
|
|
NextSlotRval = ml_const(mlconst_int(-2))
|
|
),
|
|
(
|
|
MaybeNextSlotId = yes(_),
|
|
RowInitializer = init_struct(StructType,
|
|
[init_obj(StringRval), init_obj(NextSlotRval)])
|
|
;
|
|
MaybeNextSlotId = no,
|
|
RowInitializer = init_struct(StructType,
|
|
[init_obj(StringRval)])
|
|
).
|
|
|
|
:- pred generate_string_jump_switch_arms(map(case_id, mlds_stmt)::in,
|
|
assoc_list(case_id, hash_slots)::in,
|
|
list(mlds_switch_case)::in, list(mlds_switch_case)::out) is det.
|
|
|
|
generate_string_jump_switch_arms(_, [], !Cases).
|
|
generate_string_jump_switch_arms(CodeMap, [Entry | Entries], !Cases) :-
|
|
Entry = CaseId - HashSlots,
|
|
HashSlots = hash_slots(FirstHashSlot, LaterHashSlotsCord),
|
|
LaterHashSlots = cord.list(LaterHashSlotsCord),
|
|
FirstMatchCond = make_hash_match(FirstHashSlot),
|
|
LaterMatchConds = list.map(make_hash_match, LaterHashSlots),
|
|
map.lookup(CodeMap, CaseId, CaseStmt),
|
|
Case = mlds_switch_case(FirstMatchCond, LaterMatchConds, CaseStmt),
|
|
!:Cases = [Case | !.Cases],
|
|
generate_string_jump_switch_arms(CodeMap, Entries, !Cases).
|
|
|
|
:- func make_hash_match(int) = mlds_case_match_cond.
|
|
|
|
make_hash_match(Slot) = match_value(ml_const(mlconst_int(Slot))).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% The implementation of lookup hash tables.
|
|
%
|
|
|
|
ml_generate_string_hash_lookup_switch(VarRval, TaggedCases, LookupSwitchInfo,
|
|
CodeModel, CanFail, Context, Defns, Stmts, !:Info) :-
|
|
LookupSwitchInfo =
|
|
ml_lookup_switch_info(CaseIdConsts, OutVars, OutTypes, !:Info),
|
|
(
|
|
CaseIdConsts = all_one_soln(CaseIdValueMap),
|
|
ml_case_id_soln_consts_to_tag_soln_consts(get_string_in_cons_tag,
|
|
TaggedCases, CaseIdValueMap, StrValueMap),
|
|
map.to_assoc_list(StrValueMap, StrValues),
|
|
ml_generate_string_hash_simple_lookup_switch(VarRval,
|
|
StrValues, CodeModel, CanFail, OutVars, OutTypes, Context,
|
|
Defns, Stmts, !Info)
|
|
;
|
|
CaseIdConsts = some_several_solns(CaseIdSolnMap, _Unit),
|
|
expect(unify(CodeModel, model_non), $pred, "CodeModel != model_non"),
|
|
ml_case_id_soln_consts_to_tag_soln_consts(get_string_in_cons_tag,
|
|
TaggedCases, CaseIdSolnMap, StrSolnMap),
|
|
map.to_assoc_list(StrSolnMap, StrSolns),
|
|
ml_generate_string_hash_several_soln_lookup_switch(VarRval,
|
|
StrSolns, CodeModel, CanFail, OutVars, OutTypes, Context,
|
|
Defns, Stmts, !Info)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred ml_generate_string_hash_simple_lookup_switch(mlds_rval::in,
|
|
assoc_list(string, list(mlds_rval))::in, code_model::in, can_fail::in,
|
|
list(prog_var)::in, list(mlds_type)::in, prog_context::in,
|
|
list(mlds_local_var_defn)::out, list(mlds_stmt)::out,
|
|
ml_gen_info::in, ml_gen_info::out) is det.
|
|
|
|
ml_generate_string_hash_simple_lookup_switch(VarRval, CaseValues,
|
|
CodeModel, CanFail, OutVars, OutTypes, Context, Defns, Stmts, !Info) :-
|
|
% Compute the hash table.
|
|
construct_string_hash_cases(CaseValues, allow_doubling,
|
|
TableSize, HashSlotMap, HashOp, NumCollisions),
|
|
HashMask = TableSize - 1,
|
|
|
|
ml_gen_info_get_module_info(!.Info, ModuleInfo),
|
|
module_info_get_name(ModuleInfo, ModuleName),
|
|
MLDS_ModuleName = mercury_module_name_to_mlds(ModuleName),
|
|
ml_gen_info_get_target(!.Info, Target),
|
|
|
|
MLDS_StringType = mlds_builtin_type_string,
|
|
MLDS_IntType = mlds_builtin_type_int(int_type_int),
|
|
|
|
( if NumCollisions = 0 then
|
|
MLDS_ArgTypes = [MLDS_StringType | OutTypes],
|
|
LoopPresent = no
|
|
else
|
|
MLDS_ArgTypes = [MLDS_StringType, MLDS_IntType | OutTypes],
|
|
LoopPresent = yes
|
|
),
|
|
|
|
ml_gen_string_hash_switch_search_vars(CodeModel, CanFail, LoopPresent,
|
|
Context, VarRval, HashSearchInfo, !Info),
|
|
HashSearchInfo = ml_hash_search_info(_CodeModel, _LoopPresent,
|
|
_Context, _VarRval, SlotVarNameType, _StringVarNameType,
|
|
_MaybeStopLoopVarNameType, _FailStmts, Defns),
|
|
|
|
SlotVarNameType = mlds_local_var_name_type(SlotVarName, SlotVarType),
|
|
SlotVarLval = ml_local_var(SlotVarName, SlotVarType),
|
|
SlotVarRval = ml_lval(SlotVarLval),
|
|
|
|
ml_gen_info_get_global_data(!.Info, GlobalData0),
|
|
ml_gen_static_vector_type(MLDS_ModuleName, Context, Target,
|
|
MLDS_ArgTypes, StructTypeNum, StructType, FieldIds,
|
|
GlobalData0, GlobalData1),
|
|
( if NumCollisions = 0 then
|
|
(
|
|
FieldIds = [StringFieldId | OutFieldIds],
|
|
MaybeNextSlotFieldId = no
|
|
;
|
|
FieldIds = [],
|
|
unexpected($pred, "bad FieldIds")
|
|
)
|
|
else
|
|
(
|
|
FieldIds = [StringFieldId, NextSlotFieldId | OutFieldIds],
|
|
MaybeNextSlotFieldId = yes(NextSlotFieldId)
|
|
;
|
|
( FieldIds = []
|
|
; FieldIds = [_]
|
|
),
|
|
unexpected($pred, "bad FieldIds")
|
|
)
|
|
),
|
|
% Generate the rows of the hash table.
|
|
DummyOutRvals = list.map(ml_default_value_for_type, OutTypes),
|
|
DummyOutInitializers = list.map(wrap_init_obj, DummyOutRvals),
|
|
ml_gen_string_hash_simple_lookup_slots(0, TableSize, StructType,
|
|
HashSlotMap, MaybeNextSlotFieldId, DummyOutInitializers,
|
|
cord.init, RowInitializersCord),
|
|
RowInitializers = cord.list(RowInitializersCord),
|
|
% Generate the hash table.
|
|
ml_gen_static_vector_defn(MLDS_ModuleName, StructTypeNum, RowInitializers,
|
|
VectorCommon, GlobalData1, GlobalData),
|
|
ml_gen_info_set_global_data(GlobalData, !Info),
|
|
|
|
ml_generate_field_assigns(OutVars, OutTypes, OutFieldIds, VectorCommon,
|
|
StructType, SlotVarRval, Context, LookupStmts, !Info),
|
|
|
|
FoundMatchComment = "we found a match; look up the results",
|
|
FoundMatchCommentStmt =
|
|
ml_stmt_atomic(comment(FoundMatchComment), Context),
|
|
|
|
(
|
|
CodeModel = model_det,
|
|
MatchStmts = [FoundMatchCommentStmt | LookupStmts]
|
|
;
|
|
CodeModel = model_semi,
|
|
ml_gen_set_success(ml_const(mlconst_true), Context, SetSuccessTrueStmt,
|
|
!Info),
|
|
MatchStmts = [FoundMatchCommentStmt | LookupStmts] ++
|
|
[SetSuccessTrueStmt]
|
|
;
|
|
CodeModel = model_non,
|
|
unexpected($pred, "model_non")
|
|
),
|
|
|
|
InitialComment = "hashed string simple lookup switch",
|
|
ml_gen_string_hash_switch_search(InitialComment, HashSearchInfo,
|
|
HashOp, VectorCommon, StructType,
|
|
StringFieldId, MaybeNextSlotFieldId, HashMask,
|
|
[], MatchStmts, Stmts, !Info).
|
|
|
|
:- pred ml_gen_string_hash_simple_lookup_slots(int::in, int::in, mlds_type::in,
|
|
map(int, string_hash_slot(list(mlds_rval)))::in, maybe(mlds_field_id)::in,
|
|
list(mlds_initializer)::in,
|
|
cord(mlds_initializer)::in, cord(mlds_initializer)::out) is det.
|
|
|
|
ml_gen_string_hash_simple_lookup_slots(Slot, TableSize, StructType,
|
|
HashSlotMap, MaybeNextSlotId, DummyOutInitializers,
|
|
!RowInitializersCord) :-
|
|
( if Slot = TableSize then
|
|
true
|
|
else
|
|
ml_gen_string_hash_simple_lookup_slot(Slot, StructType, HashSlotMap,
|
|
MaybeNextSlotId, DummyOutInitializers, RowInitializer),
|
|
cord.snoc(RowInitializer, !RowInitializersCord),
|
|
ml_gen_string_hash_simple_lookup_slots(Slot + 1, TableSize,
|
|
StructType, HashSlotMap, MaybeNextSlotId,
|
|
DummyOutInitializers, !RowInitializersCord)
|
|
).
|
|
|
|
:- pred ml_gen_string_hash_simple_lookup_slot(int::in, mlds_type::in,
|
|
map(int, string_hash_slot(list(mlds_rval)))::in,
|
|
maybe(mlds_field_id)::in,
|
|
list(mlds_initializer)::in, mlds_initializer::out) is det.
|
|
|
|
ml_gen_string_hash_simple_lookup_slot(Slot, StructType, HashSlotMap,
|
|
MaybeNextSlotId, DummyOutInitializers, RowInitializer) :-
|
|
( if map.search(HashSlotMap, Slot, HashSlotMapEntry) then
|
|
HashSlotMapEntry = string_hash_slot(String, Next, OutRvals),
|
|
StringRval = ml_const(mlconst_string(String)),
|
|
NextSlotRval = ml_const(mlconst_int(Next)),
|
|
OutInitializers = list.map(wrap_init_obj, OutRvals)
|
|
else
|
|
StringRval = ml_const(mlconst_null(mlds_builtin_type_string)),
|
|
NextSlotRval = ml_const(mlconst_int(-2)),
|
|
OutInitializers = DummyOutInitializers
|
|
),
|
|
(
|
|
MaybeNextSlotId = yes(_),
|
|
RowInitializer = init_struct(StructType,
|
|
[init_obj(StringRval), init_obj(NextSlotRval) | OutInitializers])
|
|
;
|
|
MaybeNextSlotId = no,
|
|
RowInitializer = init_struct(StructType,
|
|
[init_obj(StringRval) | OutInitializers])
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred ml_generate_string_hash_several_soln_lookup_switch(mlds_rval::in,
|
|
assoc_list(string, soln_consts(mlds_rval))::in,
|
|
code_model::in, can_fail::in,
|
|
list(prog_var)::in, list(mlds_type)::in, prog_context::in,
|
|
list(mlds_local_var_defn)::out, list(mlds_stmt)::out,
|
|
ml_gen_info::in, ml_gen_info::out) is det.
|
|
|
|
ml_generate_string_hash_several_soln_lookup_switch(VarRval, CaseSolns,
|
|
CodeModel, CanFail, OutVars, OutTypes, Context, Defns, Stmts, !Info) :-
|
|
% Compute the hash table.
|
|
construct_string_hash_cases(CaseSolns, allow_doubling,
|
|
TableSize, HashSlotMap, HashOp, NumCollisions),
|
|
HashMask = TableSize - 1,
|
|
|
|
ml_gen_info_get_module_info(!.Info, ModuleInfo),
|
|
module_info_get_name(ModuleInfo, ModuleName),
|
|
MLDS_ModuleName = mercury_module_name_to_mlds(ModuleName),
|
|
ml_gen_info_get_target(!.Info, Target),
|
|
|
|
MLDS_StringType = mlds_builtin_type_string,
|
|
MLDS_IntType = mlds_builtin_type_int(int_type_int),
|
|
|
|
( if NumCollisions = 0 then
|
|
FirstSolnFieldTypes = [MLDS_StringType,
|
|
MLDS_IntType, MLDS_IntType | OutTypes],
|
|
LoopPresent = no
|
|
else
|
|
FirstSolnFieldTypes = [MLDS_StringType, MLDS_IntType,
|
|
MLDS_IntType, MLDS_IntType | OutTypes],
|
|
LoopPresent = yes
|
|
),
|
|
|
|
ml_gen_string_hash_switch_search_vars(CodeModel, CanFail, LoopPresent,
|
|
Context, VarRval, HashSearchInfo, !Info),
|
|
HashSearchInfo = ml_hash_search_info(_CodeModel, _LoopPresent,
|
|
_Context, _VarRval, SlotVarNameType, _StringVarNameType,
|
|
_MaybeStopLoopVarNameType, _FailStmts, Defns),
|
|
|
|
SlotVarNameType = mlds_local_var_name_type(SlotVarName, SlotVarType),
|
|
SlotVarLval = ml_local_var(SlotVarName, SlotVarType),
|
|
|
|
ml_gen_info_get_global_data(!.Info, GlobalData0),
|
|
ml_gen_static_vector_type(MLDS_ModuleName, Context, Target,
|
|
FirstSolnFieldTypes, FirstSolnStructTypeNum, FirstSolnStructType,
|
|
FirstSolnFieldIds, GlobalData0, GlobalData1),
|
|
ml_gen_static_vector_type(MLDS_ModuleName, Context, Target,
|
|
OutTypes, LaterSolnStructTypeNum, LaterSolnStructType,
|
|
LaterSolnOutFieldIds, GlobalData1, GlobalData2),
|
|
( if NumCollisions = 0 then
|
|
( if
|
|
FirstSolnFieldIds = [StringFieldIdPrime,
|
|
NumLaterSolnsFieldIdPrime, FirstLaterSolnRowFieldIdPrime
|
|
| FirstSolnOutFieldIdsPrime]
|
|
then
|
|
StringFieldId = StringFieldIdPrime,
|
|
NumLaterSolnsFieldId = NumLaterSolnsFieldIdPrime,
|
|
FirstLaterSolnRowFieldId = FirstLaterSolnRowFieldIdPrime,
|
|
FirstSolnOutFieldIds = FirstSolnOutFieldIdsPrime,
|
|
MaybeNextSlotFieldId = no
|
|
else
|
|
unexpected($pred, "bad FieldIds")
|
|
)
|
|
else
|
|
( if
|
|
FirstSolnFieldIds =
|
|
[StringFieldIdPrime, NextSlotFieldIdPrime,
|
|
NumLaterSolnsFieldIdPrime, FirstLaterSolnRowFieldIdPrime
|
|
| FirstSolnOutFieldIdsPrime]
|
|
then
|
|
StringFieldId = StringFieldIdPrime,
|
|
NumLaterSolnsFieldId = NumLaterSolnsFieldIdPrime,
|
|
FirstLaterSolnRowFieldId = FirstLaterSolnRowFieldIdPrime,
|
|
FirstSolnOutFieldIds = FirstSolnOutFieldIdsPrime,
|
|
MaybeNextSlotFieldId = yes(NextSlotFieldIdPrime)
|
|
else
|
|
unexpected($pred, "bad FieldIds")
|
|
)
|
|
),
|
|
|
|
% Generate the rows of the first soln hash table.
|
|
DummyOutRvals = list.map(ml_default_value_for_type, OutTypes),
|
|
DummyOutInitializers = list.map(wrap_init_obj, DummyOutRvals),
|
|
ml_gen_string_hash_several_soln_lookup_slots(0, TableSize, HashSlotMap,
|
|
FirstSolnStructType, LaterSolnStructType,
|
|
MaybeNextSlotFieldId, DummyOutInitializers,
|
|
cord.init, FirstSolnRowInitializersCord,
|
|
cord.init, LaterSolnRowInitializersCord, 0),
|
|
FirstSolnRowInitializers = cord.list(FirstSolnRowInitializersCord),
|
|
LaterSolnRowInitializers = cord.list(LaterSolnRowInitializersCord),
|
|
% Generate the hash table.
|
|
ml_gen_static_vector_defn(MLDS_ModuleName, FirstSolnStructTypeNum,
|
|
FirstSolnRowInitializers, FirstSolnVectorCommon,
|
|
GlobalData2, GlobalData3),
|
|
ml_gen_static_vector_defn(MLDS_ModuleName, LaterSolnStructTypeNum,
|
|
LaterSolnRowInitializers, LaterSolnVectorCommon,
|
|
GlobalData3, GlobalData),
|
|
ml_gen_info_set_global_data(GlobalData, !Info),
|
|
|
|
ml_gen_several_soln_lookup_code(Context,
|
|
ml_lval(SlotVarLval), OutVars, OutTypes,
|
|
FirstSolnStructType, LaterSolnStructType,
|
|
NumLaterSolnsFieldId, FirstLaterSolnRowFieldId,
|
|
FirstSolnOutFieldIds, LaterSolnOutFieldIds,
|
|
FirstSolnVectorCommon, LaterSolnVectorCommon,
|
|
do_not_need_bit_vec_check_no_gaps,
|
|
MatchDefns, SuccessStmts, !Info),
|
|
|
|
InitialComment = "hashed string several_soln lookup switch",
|
|
ml_gen_string_hash_switch_search(InitialComment, HashSearchInfo,
|
|
HashOp, FirstSolnVectorCommon, FirstSolnStructType,
|
|
StringFieldId, MaybeNextSlotFieldId, HashMask,
|
|
MatchDefns, SuccessStmts, Stmts, !Info).
|
|
|
|
:- pred ml_gen_string_hash_several_soln_lookup_slots(int::in, int::in,
|
|
map(int, string_hash_slot(soln_consts(mlds_rval)))::in,
|
|
mlds_type::in, mlds_type::in,
|
|
maybe(mlds_field_id)::in, list(mlds_initializer)::in,
|
|
cord(mlds_initializer)::in, cord(mlds_initializer)::out,
|
|
cord(mlds_initializer)::in, cord(mlds_initializer)::out, int::in) is det.
|
|
|
|
ml_gen_string_hash_several_soln_lookup_slots(Slot, TableSize,
|
|
HashSlotMap, FirstSolnStructType, LaterSolnStructType,
|
|
MaybeNextSlotId, DummyOutInitializers,
|
|
!FirstSolnRowInitializersCord, !LaterSolnRowInitializersCord,
|
|
!.CurLaterSolnIndex) :-
|
|
( if Slot = TableSize then
|
|
true
|
|
else
|
|
ml_gen_string_hash_several_soln_lookup_slot(Slot, HashSlotMap,
|
|
FirstSolnStructType, LaterSolnStructType, MaybeNextSlotId,
|
|
DummyOutInitializers, FirstSolnsRowInitializer,
|
|
!LaterSolnRowInitializersCord, !CurLaterSolnIndex),
|
|
cord.snoc(FirstSolnsRowInitializer, !FirstSolnRowInitializersCord),
|
|
ml_gen_string_hash_several_soln_lookup_slots(Slot + 1, TableSize,
|
|
HashSlotMap, FirstSolnStructType, LaterSolnStructType,
|
|
MaybeNextSlotId,
|
|
DummyOutInitializers, !FirstSolnRowInitializersCord,
|
|
!LaterSolnRowInitializersCord, !.CurLaterSolnIndex)
|
|
).
|
|
|
|
:- pred ml_gen_string_hash_several_soln_lookup_slot(int::in,
|
|
map(int, string_hash_slot(soln_consts(mlds_rval)))::in,
|
|
mlds_type::in, mlds_type::in, maybe(mlds_field_id)::in,
|
|
list(mlds_initializer)::in, mlds_initializer::out,
|
|
cord(mlds_initializer)::in, cord(mlds_initializer)::out,
|
|
int::in, int::out) is det.
|
|
|
|
ml_gen_string_hash_several_soln_lookup_slot(Slot, HashSlotMap,
|
|
FirstSolnStructType, LaterSolnStructType, MaybeNextSlotId,
|
|
DummyOutInitializers, FirstSolnsRowInitializer,
|
|
!LaterSolnRowInitializersCord, !CurLaterSolnIndex) :-
|
|
( if map.search(HashSlotMap, Slot, HashSlotMapEntry) then
|
|
HashSlotMapEntry = string_hash_slot(String, Next, Solns),
|
|
StringRval = ml_const(mlconst_string(String)),
|
|
NextSlotRval = ml_const(mlconst_int(Next)),
|
|
(
|
|
Solns = one_soln(FirstSolnRvals),
|
|
NumLaterSolnsRval = ml_const(mlconst_int(0)),
|
|
FirstLaterSlotRval = ml_const(mlconst_int(-1)),
|
|
FirstSolnOutInitializers = list.map(wrap_init_obj, FirstSolnRvals)
|
|
;
|
|
Solns = several_solns(FirstSolnRvals, LaterSolns),
|
|
list.length(LaterSolns, NumLaterSolns),
|
|
NumLaterSolnsRval = ml_const(mlconst_int(NumLaterSolns)),
|
|
FirstLaterSlotRval = ml_const(mlconst_int(!.CurLaterSolnIndex)),
|
|
FirstSolnOutInitializers = list.map(wrap_init_obj, FirstSolnRvals),
|
|
LaterSolnRowInitializers = list.map(
|
|
ml_construct_later_soln_row(LaterSolnStructType),
|
|
LaterSolns),
|
|
!:LaterSolnRowInitializersCord = !.LaterSolnRowInitializersCord ++
|
|
from_list(LaterSolnRowInitializers),
|
|
!:CurLaterSolnIndex = !.CurLaterSolnIndex + NumLaterSolns
|
|
)
|
|
else
|
|
StringRval = ml_const(mlconst_null(mlds_builtin_type_string)),
|
|
NextSlotRval = ml_const(mlconst_int(-2)),
|
|
NumLaterSolnsRval = ml_const(mlconst_int(-1)),
|
|
FirstLaterSlotRval = ml_const(mlconst_int(-1)),
|
|
FirstSolnOutInitializers = DummyOutInitializers
|
|
),
|
|
(
|
|
MaybeNextSlotId = yes(_),
|
|
FirstSolnsRowInitializer = init_struct(FirstSolnStructType,
|
|
[init_obj(StringRval), init_obj(NextSlotRval),
|
|
init_obj(NumLaterSolnsRval), init_obj(FirstLaterSlotRval)
|
|
| FirstSolnOutInitializers])
|
|
;
|
|
MaybeNextSlotId = no,
|
|
FirstSolnsRowInitializer = init_struct(FirstSolnStructType,
|
|
[init_obj(StringRval),
|
|
init_obj(NumLaterSolnsRval), init_obj(FirstLaterSlotRval)
|
|
| FirstSolnOutInitializers])
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Code useful for both jump and lookup hash string switches.
|
|
%
|
|
|
|
:- type ml_hash_search_info
|
|
---> ml_hash_search_info(
|
|
mhsi_code_model :: code_model,
|
|
mhsi_loop_present :: bool,
|
|
mhsi_context :: prog_context,
|
|
mhsi_switch_var :: mlds_rval,
|
|
mhsi_slot_var :: mlds_local_var_name_type,
|
|
mhsi_string_var :: mlds_local_var_name_type,
|
|
mhsi_stop_loop_var :: maybe(mlds_local_var_name_type),
|
|
mhsi_fail_statements :: list(mlds_stmt),
|
|
mhsi_defns :: list(mlds_local_var_defn)
|
|
).
|
|
|
|
:- pred ml_gen_string_hash_switch_search_vars(code_model::in, can_fail::in,
|
|
bool::in, prog_context::in, mlds_rval::in,
|
|
ml_hash_search_info::out,
|
|
ml_gen_info::in, ml_gen_info::out) is det.
|
|
|
|
ml_gen_string_hash_switch_search_vars(CodeModel, CanFail, LoopPresent,
|
|
Context, VarRval, HashSearchInfo, !Info) :-
|
|
% Generate the following local variable declarations:
|
|
% int slot;
|
|
% MR_String str;
|
|
|
|
ml_gen_info_new_aux_var_name(mcav_slot, SlotVarName, !Info),
|
|
SlotVarType = mlds_builtin_type_int(int_type_int),
|
|
% We never need to trace ints.
|
|
SlotVarDefn = ml_gen_mlds_var_decl(SlotVarName, SlotVarType,
|
|
gc_no_stmt, Context),
|
|
SlotVarNameType = mlds_local_var_name_type(SlotVarName, SlotVarType),
|
|
|
|
ml_gen_info_new_aux_var_name(mcav_str, StringVarName, !Info),
|
|
StringVarType = mlds_builtin_type_string,
|
|
% StringVar always points to an element of the string_table array.
|
|
% All those elements are static constants; they can never point into
|
|
% the heap. So GC never needs to trace StringVar.
|
|
StringVarDefn = ml_gen_mlds_var_decl(StringVarName, StringVarType,
|
|
gc_no_stmt, Context),
|
|
StringVarNameType = mlds_local_var_name_type(StringVarName, StringVarType),
|
|
|
|
AlwaysDefns = [SlotVarDefn, StringVarDefn],
|
|
ml_should_use_stop_loop(Context, LoopPresent,
|
|
MaybeStopLoopVarNameType, StopLoopVarDefns, !Info),
|
|
Defns = AlwaysDefns ++ StopLoopVarDefns,
|
|
|
|
% Generate the code for when the lookup fails.
|
|
ml_gen_maybe_switch_failure(CodeModel, CanFail, Context, FailStmts, !Info),
|
|
|
|
HashSearchInfo = ml_hash_search_info(CodeModel, LoopPresent, Context,
|
|
VarRval, SlotVarNameType, StringVarNameType, MaybeStopLoopVarNameType,
|
|
FailStmts, Defns).
|
|
|
|
:- pred ml_gen_string_hash_switch_search(string::in, ml_hash_search_info::in,
|
|
unary_op::in, mlds_vector_common::in, mlds_type::in,
|
|
mlds_field_id::in, maybe(mlds_field_id)::in, int::in,
|
|
list(mlds_local_var_defn)::in, list(mlds_stmt)::in, list(mlds_stmt)::out,
|
|
ml_gen_info::in, ml_gen_info::out) is det.
|
|
|
|
ml_gen_string_hash_switch_search(InitialComment,
|
|
HashSearchInfo, HashOp, VectorCommon, StructType,
|
|
StringFieldId, MaybeNextSlotFieldId, HashMask,
|
|
MatchDefns, MatchStmts, Stmts, !Info) :-
|
|
HashSearchInfo = ml_hash_search_info(CodeModel, LoopPresent,
|
|
Context, VarRval, SlotVarNameType, StringVarNameType,
|
|
MaybeStopLoopVarNameType, FailStmts, _Defns),
|
|
|
|
SlotVarNameType = mlds_local_var_name_type(SlotVarName, SlotVarType),
|
|
SlotVarLval = ml_local_var(SlotVarName, SlotVarType),
|
|
SlotVarRval = ml_lval(SlotVarLval),
|
|
|
|
StringVarNameType = mlds_local_var_name_type(StringVarName, StringVarType),
|
|
StringVarLval = ml_local_var(StringVarName, StringVarType),
|
|
StringVarRval = ml_lval(StringVarLval),
|
|
|
|
(
|
|
MaybeStopLoopVarNameType = no,
|
|
MaybeStopLoopVarLval = no,
|
|
StopLoopVarNameList = []
|
|
;
|
|
MaybeStopLoopVarNameType = yes(StopLoopVarNameType),
|
|
StopLoopVarNameType =
|
|
mlds_local_var_name_type(StopLoopVarName, StopLoopVarType),
|
|
StopLoopVarLval0 = ml_local_var(StopLoopVarName, StopLoopVarType),
|
|
MaybeStopLoopVarLval = yes(StopLoopVarLval0),
|
|
StopLoopVarNameList = [StopLoopVarName]
|
|
),
|
|
|
|
ml_wrap_loop_break(CodeModel, LoopPresent, Context, MaybeStopLoopVarLval,
|
|
MatchDefns, MatchStmts, FailStmts,
|
|
SetupForFailStmts, SuccessStmt, AfterStmts, !Info),
|
|
|
|
InitialCommentStmt = ml_stmt_atomic(comment(InitialComment), Context),
|
|
PrepareForMatchStmts = [
|
|
ml_stmt_atomic(comment("compute the hash value of the input string"),
|
|
Context),
|
|
ml_stmt_atomic(assign(SlotVarLval,
|
|
ml_binop(bitwise_and(int_type_int),
|
|
ml_unop(HashOp, VarRval),
|
|
ml_const(mlconst_int(HashMask)))),
|
|
Context)
|
|
],
|
|
FoundMatchCond =
|
|
ml_binop(logical_and,
|
|
ml_binop(int_cmp(int_type_int, ne), StringVarRval,
|
|
ml_const(mlconst_null(StringVarType))),
|
|
ml_binop(str_cmp(eq), StringVarRval, VarRval)
|
|
),
|
|
LookForMatchPrepareStmts = [
|
|
ml_stmt_atomic(comment("lookup the string for this hash slot"),
|
|
Context),
|
|
ml_stmt_atomic(assign(StringVarLval,
|
|
ml_lval(ml_field(yes(ptag(0u8)),
|
|
ml_vector_common_row_addr(VectorCommon, SlotVarRval),
|
|
StructType, StringFieldId, StringVarType))),
|
|
Context),
|
|
ml_stmt_atomic(comment("did we find a match?"), Context)
|
|
],
|
|
SlotTest = ml_binop(int_cmp(int_type_int, ge), SlotVarRval,
|
|
ml_const(mlconst_int(0))),
|
|
(
|
|
MaybeStopLoopVarLval = no,
|
|
InitStopLoopVarStmts = [],
|
|
InitSuccessStmts = [],
|
|
LoopTest = SlotTest
|
|
;
|
|
MaybeStopLoopVarLval = yes(StopLoopVarLval),
|
|
InitStopLoopVarStmt = ml_stmt_atomic(
|
|
assign(StopLoopVarLval, ml_const(mlconst_int(0))),
|
|
Context),
|
|
InitStopLoopVarStmts = [InitStopLoopVarStmt],
|
|
(
|
|
CodeModel = model_det,
|
|
% If the switch is model_det, the value of `succeeded'
|
|
% is irrelevant, as it will not be consulted.
|
|
InitSuccessStmts = []
|
|
;
|
|
CodeModel = model_semi,
|
|
ml_gen_set_success(ml_const(mlconst_false), Context,
|
|
InitSuccessStmt, !Info),
|
|
InitSuccessStmts = [InitSuccessStmt]
|
|
;
|
|
CodeModel = model_non,
|
|
% If the switch is model_non, we get to the end of search code
|
|
% (which may or my not be a loop) for both matches and non-matches,
|
|
% though in the case of matches we get there only after invoking
|
|
% the success continuation for each solution.
|
|
InitSuccessStmts = []
|
|
),
|
|
StopLoopTest = ml_binop(int_cmp(int_type_int, eq),
|
|
ml_lval(StopLoopVarLval), ml_const(mlconst_int(0))),
|
|
LoopTest = ml_binop(logical_and, StopLoopTest, SlotTest)
|
|
),
|
|
(
|
|
MaybeNextSlotFieldId = yes(NextSlotFieldId),
|
|
NoMatchStmt = ml_stmt_block([], [], [
|
|
ml_stmt_atomic(comment(
|
|
"no match yet, so get next slot in hash chain"),
|
|
Context),
|
|
ml_stmt_atomic(assign(SlotVarLval,
|
|
ml_lval(ml_field(yes(ptag(0u8)),
|
|
ml_vector_common_row_addr(VectorCommon, SlotVarRval),
|
|
StructType, NextSlotFieldId, SlotVarType))),
|
|
Context)
|
|
], Context),
|
|
LookForMatchStmt =
|
|
ml_stmt_if_then_else(FoundMatchCond, SuccessStmt,
|
|
yes(NoMatchStmt), Context),
|
|
LoopBody = ml_stmt_block([], [],
|
|
LookForMatchPrepareStmts ++ [LookForMatchStmt], Context),
|
|
LoopStmts = [
|
|
ml_stmt_atomic(comment("hash chain loop"), Context),
|
|
ml_stmt_while(loop_at_least_once, LoopTest, LoopBody,
|
|
[SlotVarName, StringVarName | StopLoopVarNameList], Context)
|
|
],
|
|
SearchStmts = PrepareForMatchStmts ++
|
|
InitStopLoopVarStmts ++ InitSuccessStmts ++ LoopStmts,
|
|
Stmts = [InitialCommentStmt | SetupForFailStmts] ++
|
|
SearchStmts ++ AfterStmts
|
|
;
|
|
MaybeNextSlotFieldId = no,
|
|
NoLoopCommentStmt = ml_stmt_atomic(
|
|
comment("no collisions; no hash chain loop"), Context),
|
|
LookForMatchStmt =
|
|
ml_stmt_if_then_else(FoundMatchCond, SuccessStmt, no, Context),
|
|
SearchStmts = PrepareForMatchStmts ++ InitSuccessStmts ++
|
|
[NoLoopCommentStmt | LookForMatchPrepareStmts] ++
|
|
[LookForMatchStmt],
|
|
Stmts = [InitialCommentStmt | SearchStmts] ++ AfterStmts
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% The implementation of jump binary search tables.
|
|
%
|
|
|
|
ml_generate_string_binary_jump_switch(VarRval, Cases, CodeModel, CanFail,
|
|
EntryPackedArgsMap, Context, Defns, Stmts, !Info) :-
|
|
ml_gen_string_binary_switch_search_vars(CodeModel, CanFail,
|
|
Context, VarRval, BinarySearchInfo, !Info),
|
|
BinarySearchInfo = ml_binary_search_info(_CodeModel,
|
|
_VarRval, _LoVarNameType, _HiVarNameType, MidVarNameType,
|
|
_ResultVarNameType, _MaybeStopLoopVarNameType, _FailStmts, Defns),
|
|
|
|
MidVarNameType = mlds_local_var_name_type(MidVarName, MidVarType),
|
|
MidVarLval = ml_local_var(MidVarName, MidVarType),
|
|
|
|
ml_gen_info_get_module_info(!.Info, ModuleInfo),
|
|
module_info_get_name(ModuleInfo, ModuleName),
|
|
MLDS_ModuleName = mercury_module_name_to_mlds(ModuleName),
|
|
ml_gen_info_get_target(!.Info, Target),
|
|
|
|
MLDS_StringType = mlds_builtin_type_string,
|
|
MLDS_CaseNumType = mlds_builtin_type_int(int_type_int),
|
|
MLDS_ArgTypes = [MLDS_StringType, MLDS_CaseNumType],
|
|
|
|
% Generate the binary search table.
|
|
ml_gen_info_get_global_data(!.Info, GlobalData0),
|
|
ml_gen_static_vector_type(MLDS_ModuleName, Context, Target,
|
|
MLDS_ArgTypes, StructTypeNum, StructType, FieldIds,
|
|
GlobalData0, GlobalData1),
|
|
ml_gen_info_set_global_data(GlobalData1, !Info),
|
|
(
|
|
FieldIds = [StringFieldId, CaseNumFieldId]
|
|
;
|
|
( FieldIds = []
|
|
; FieldIds = [_]
|
|
; FieldIds = [_, _, _ | _]
|
|
),
|
|
unexpected($pred, "bad FieldIds")
|
|
),
|
|
map.init(CaseLabelMap0),
|
|
string_binary_cases(Cases,
|
|
ml_gen_tagged_case_code_for_string_switch_return_case_id(CodeModel,
|
|
EntryPackedArgsMap),
|
|
CaseLabelMap0, CaseLabelMap, [], ReachableConstVarMaps, !Info,
|
|
unit, _, SortedTable),
|
|
ml_gen_record_consensus_const_var_map(ReachableConstVarMaps, !Info),
|
|
ml_gen_string_binary_jump_initializers(SortedTable, StructType,
|
|
cord.init, RowInitializersCord, 0, TableSize),
|
|
RowInitializers = cord.list(RowInitializersCord),
|
|
% We need to get the globaldata out of !Info again, since the generation
|
|
% of code for the switch arms can generate global data.
|
|
ml_gen_info_get_global_data(!.Info, GlobalData2),
|
|
ml_gen_static_vector_defn(MLDS_ModuleName, StructTypeNum, RowInitializers,
|
|
VectorCommon, GlobalData2, GlobalData),
|
|
ml_gen_info_set_global_data(GlobalData, !Info),
|
|
|
|
% Generate the switch that uses the final value of MidVar (the one that,
|
|
% when used as an index into the binary search table, matches the current
|
|
% value of the switched-on variable) to select the piece of code to
|
|
% execute.
|
|
map.to_assoc_list(CaseLabelMap, CaseLabelList),
|
|
ml_gen_string_binary_jump_switch_arms(CaseLabelList, [], SwitchCases0),
|
|
list.sort(SwitchCases0, SwitchCases),
|
|
SwitchStmt0 = ml_stmt_switch(mlds_builtin_type_int(int_type_int),
|
|
ml_lval(ml_field(yes(ptag(0u8)),
|
|
ml_vector_common_row_addr(VectorCommon, ml_lval(MidVarLval)),
|
|
StructType, CaseNumFieldId, MLDS_StringType)),
|
|
mlds_switch_range(0, TableSize - 1), SwitchCases,
|
|
default_is_unreachable, Context),
|
|
ml_simplify_switch(SwitchStmt0, SwitchStmt, !Info),
|
|
|
|
% Generate the code that searches the table.
|
|
InitialComment = "binary string jump switch",
|
|
ml_gen_string_binary_switch_search(Context, InitialComment,
|
|
BinarySearchInfo, VectorCommon, TableSize, StructType, StringFieldId,
|
|
[], [SwitchStmt], Stmts, !Info).
|
|
|
|
:- pred ml_gen_string_binary_jump_initializers(assoc_list(string, case_id)::in,
|
|
mlds_type::in,
|
|
cord(mlds_initializer)::in, cord(mlds_initializer)::out,
|
|
int::in, int::out) is det.
|
|
|
|
ml_gen_string_binary_jump_initializers([],
|
|
_StructType, !RowInitializersCord, !CurIndex).
|
|
ml_gen_string_binary_jump_initializers([Str - CaseId | StrCaseIds],
|
|
StructType, !RowInitializersCord, !CurIndex) :-
|
|
CaseId = case_id(CaseNum),
|
|
StrRval = ml_const(mlconst_string(Str)),
|
|
CaseNumRval = ml_const(mlconst_int(CaseNum)),
|
|
RowInitializer = init_struct(StructType,
|
|
[init_obj(StrRval), init_obj(CaseNumRval)]),
|
|
cord.snoc(RowInitializer, !RowInitializersCord),
|
|
!:CurIndex = !.CurIndex + 1,
|
|
ml_gen_string_binary_jump_initializers(StrCaseIds,
|
|
StructType, !RowInitializersCord, !CurIndex).
|
|
|
|
:- pred ml_gen_string_binary_jump_switch_arms(
|
|
assoc_list(case_id, mlds_stmt)::in,
|
|
list(mlds_switch_case)::in, list(mlds_switch_case)::out) is det.
|
|
|
|
ml_gen_string_binary_jump_switch_arms([], !SwitchCases).
|
|
ml_gen_string_binary_jump_switch_arms([CaseIdStmt | CaseIdsStmts],
|
|
!SwitchCases) :-
|
|
CaseIdStmt = CaseId - Stmt,
|
|
CaseId = case_id(CaseNum),
|
|
MatchCond = match_value(ml_const(mlconst_int(CaseNum))),
|
|
SwitchCase = mlds_switch_case(MatchCond, [], Stmt),
|
|
!:SwitchCases = [SwitchCase | !.SwitchCases],
|
|
ml_gen_string_binary_jump_switch_arms(CaseIdsStmts, !SwitchCases).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% The implementation of lookup binary search tables.
|
|
%
|
|
|
|
ml_generate_string_binary_lookup_switch(VarRval, TaggedCases, LookupSwitchInfo,
|
|
CodeModel, CanFail, Context, Defns, Stmts, !:Info) :-
|
|
LookupSwitchInfo =
|
|
ml_lookup_switch_info(CaseIdConsts, OutVars, OutTypes, !:Info),
|
|
(
|
|
CaseIdConsts = all_one_soln(CaseIdValueMap),
|
|
ml_case_id_soln_consts_to_tag_soln_consts(get_string_in_cons_tag,
|
|
TaggedCases, CaseIdValueMap, StrValueMap),
|
|
map.to_assoc_list(StrValueMap, StrValues),
|
|
ml_generate_string_binary_simple_lookup_switch(VarRval,
|
|
StrValues, CodeModel, CanFail, OutVars, OutTypes,
|
|
Context, Defns, Stmts, !Info)
|
|
;
|
|
CaseIdConsts = some_several_solns(CaseIdSolnMap, _Unit),
|
|
expect(unify(CodeModel, model_non), $pred, "CodeModel != model_non"),
|
|
ml_case_id_soln_consts_to_tag_soln_consts(get_string_in_cons_tag,
|
|
TaggedCases, CaseIdSolnMap, StrSolnMap),
|
|
map.to_assoc_list(StrSolnMap, StrSolns),
|
|
ml_generate_string_binary_several_soln_lookup_switch(VarRval,
|
|
StrSolns, CodeModel, CanFail, OutVars, OutTypes,
|
|
Context, Defns, Stmts, !Info)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred ml_generate_string_binary_simple_lookup_switch(mlds_rval::in,
|
|
assoc_list(string, list(mlds_rval))::in, code_model::in, can_fail::in,
|
|
list(prog_var)::in, list(mlds_type)::in, prog_context::in,
|
|
list(mlds_local_var_defn)::out, list(mlds_stmt)::out,
|
|
ml_gen_info::in, ml_gen_info::out) is det.
|
|
|
|
ml_generate_string_binary_simple_lookup_switch(VarRval, CaseValues0,
|
|
CodeModel, CanFail, OutVars, OutTypes, Context, Defns, Stmts, !Info) :-
|
|
ml_gen_info_get_module_info(!.Info, ModuleInfo),
|
|
module_info_get_name(ModuleInfo, ModuleName),
|
|
MLDS_ModuleName = mercury_module_name_to_mlds(ModuleName),
|
|
ml_gen_info_get_target(!.Info, Target),
|
|
|
|
MLDS_StringType = mercury_type_to_mlds_type(ModuleInfo, string_type),
|
|
MLDS_ArgTypes = [MLDS_StringType | OutTypes],
|
|
|
|
% Generate the binary search table.
|
|
list.sort(CaseValues0, CaseValues),
|
|
ml_gen_info_get_global_data(!.Info, GlobalData0),
|
|
ml_gen_static_vector_type(MLDS_ModuleName, Context, Target,
|
|
MLDS_ArgTypes, StructTypeNum, StructType, FieldIds,
|
|
GlobalData0, GlobalData1),
|
|
(
|
|
FieldIds = [StringFieldId | OutFieldIds]
|
|
;
|
|
FieldIds = [],
|
|
unexpected($pred, "bad FieldIds")
|
|
),
|
|
ml_gen_string_binary_simple_lookup_initializers(CaseValues, StructType,
|
|
cord.init, RowInitializersCord, 0, TableSize),
|
|
RowInitializers = cord.list(RowInitializersCord),
|
|
ml_gen_static_vector_defn(MLDS_ModuleName, StructTypeNum, RowInitializers,
|
|
VectorCommon, GlobalData1, GlobalData),
|
|
ml_gen_info_set_global_data(GlobalData, !Info),
|
|
|
|
ml_gen_string_binary_switch_search_vars(CodeModel, CanFail,
|
|
Context, VarRval, BinarySearchInfo, !Info),
|
|
BinarySearchInfo = ml_binary_search_info(_CodeModel, _VarRval,
|
|
_LoVarNameType, _HiVarNameType, MidVarNameType,
|
|
_ResultVarNameType, _MaybeStopLoopNameType, _FailStmts, Defns),
|
|
|
|
MidVarNameType = mlds_local_var_name_type(MidVarName, MidVarType),
|
|
MidVarLval = ml_local_var(MidVarName, MidVarType),
|
|
MidVarRval = ml_lval(MidVarLval),
|
|
|
|
ml_generate_field_assigns(OutVars, OutTypes, OutFieldIds,
|
|
VectorCommon, StructType, MidVarRval, Context, GetArgStmts, !Info),
|
|
(
|
|
CodeModel = model_det,
|
|
MatchStmts = GetArgStmts
|
|
;
|
|
CodeModel = model_semi,
|
|
ml_gen_set_success(ml_const(mlconst_true), Context, SetSuccessTrueStmt,
|
|
!Info),
|
|
MatchStmts = GetArgStmts ++ [SetSuccessTrueStmt]
|
|
;
|
|
CodeModel = model_non,
|
|
unexpected($pred, "model_non")
|
|
),
|
|
|
|
% Generate the code that searches the table.
|
|
InitialComment = "binary string simple lookup switch",
|
|
ml_gen_string_binary_switch_search(Context, InitialComment,
|
|
BinarySearchInfo, VectorCommon, TableSize, StructType, StringFieldId,
|
|
[], MatchStmts, Stmts, !Info).
|
|
|
|
:- pred ml_gen_string_binary_simple_lookup_initializers(
|
|
assoc_list(string, list(mlds_rval))::in, mlds_type::in,
|
|
cord(mlds_initializer)::in, cord(mlds_initializer)::out,
|
|
int::in, int::out) is det.
|
|
|
|
ml_gen_string_binary_simple_lookup_initializers([],
|
|
_StructType, !RowInitializersCord, !CurIndex).
|
|
ml_gen_string_binary_simple_lookup_initializers([Str - Rvals | StrRvals],
|
|
StructType, !RowInitializersCord, !CurIndex) :-
|
|
StrRval = ml_const(mlconst_string(Str)),
|
|
RowInitializer = init_struct(StructType,
|
|
[init_obj(StrRval) | list.map(wrap_init_obj, Rvals)]),
|
|
cord.snoc(RowInitializer, !RowInitializersCord),
|
|
!:CurIndex = !.CurIndex + 1,
|
|
ml_gen_string_binary_simple_lookup_initializers(StrRvals,
|
|
StructType, !RowInitializersCord, !CurIndex).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred ml_generate_string_binary_several_soln_lookup_switch(mlds_rval::in,
|
|
assoc_list(string, soln_consts(mlds_rval))::in,
|
|
code_model::in, can_fail::in,
|
|
list(prog_var)::in, list(mlds_type)::in, prog_context::in,
|
|
list(mlds_local_var_defn)::out, list(mlds_stmt)::out,
|
|
ml_gen_info::in, ml_gen_info::out) is det.
|
|
|
|
ml_generate_string_binary_several_soln_lookup_switch(VarRval, CaseSolns0,
|
|
CodeModel, CanFail, OutVars, OutTypes, Context, Defns, Stmts, !Info) :-
|
|
ml_gen_info_get_module_info(!.Info, ModuleInfo),
|
|
module_info_get_name(ModuleInfo, ModuleName),
|
|
MLDS_ModuleName = mercury_module_name_to_mlds(ModuleName),
|
|
ml_gen_info_get_target(!.Info, Target),
|
|
MLDS_StringType = mlds_builtin_type_string,
|
|
MLDS_IntType = mlds_builtin_type_int(int_type_int),
|
|
FirstSolnFieldTypes =
|
|
[MLDS_StringType, MLDS_IntType, MLDS_IntType | OutTypes],
|
|
LaterSolnFieldTypes = OutTypes,
|
|
|
|
% Generate the binary search table.
|
|
list.sort(CaseSolns0, CaseSolns),
|
|
ml_gen_info_get_global_data(!.Info, GlobalData0),
|
|
ml_gen_static_vector_type(MLDS_ModuleName, Context, Target,
|
|
FirstSolnFieldTypes, FirstSolnStructTypeNum, FirstSolnStructType,
|
|
FirstSolnFieldIds, GlobalData0, GlobalData1),
|
|
ml_gen_static_vector_type(MLDS_ModuleName, Context, Target,
|
|
LaterSolnFieldTypes, LaterSolnStructTypeNum, LaterSolnStructType,
|
|
LaterSolnOutFieldIds, GlobalData1, GlobalData2),
|
|
( if
|
|
FirstSolnFieldIds = [StringFieldIdPrime, NumLaterSolnsFieldIdPrime,
|
|
FirstLaterSolnRowFieldIdPrime | FirstSolnOutFieldIdsPrime]
|
|
then
|
|
StringFieldId = StringFieldIdPrime,
|
|
NumLaterSolnsFieldId = NumLaterSolnsFieldIdPrime,
|
|
FirstLaterSolnRowFieldId = FirstLaterSolnRowFieldIdPrime,
|
|
FirstSolnOutFieldIds = FirstSolnOutFieldIdsPrime
|
|
else
|
|
unexpected($pred, "bad FieldIds")
|
|
),
|
|
ml_gen_string_binary_several_lookup_initializers(CaseSolns,
|
|
FirstSolnStructType, LaterSolnStructType,
|
|
cord.init, FirstSolnRowInitializersCord,
|
|
cord.init, LaterSolnRowInitializersCord,
|
|
0, FirstSolnTableSize, 0),
|
|
FirstSolnRowInitializers = cord.list(FirstSolnRowInitializersCord),
|
|
LaterSolnRowInitializers = cord.list(LaterSolnRowInitializersCord),
|
|
ml_gen_static_vector_defn(MLDS_ModuleName, FirstSolnStructTypeNum,
|
|
FirstSolnRowInitializers, FirstSolnVectorCommon,
|
|
GlobalData2, GlobalData3),
|
|
ml_gen_static_vector_defn(MLDS_ModuleName, LaterSolnStructTypeNum,
|
|
LaterSolnRowInitializers, LaterSolnVectorCommon,
|
|
GlobalData3, GlobalData),
|
|
ml_gen_info_set_global_data(GlobalData, !Info),
|
|
|
|
ml_gen_string_binary_switch_search_vars(CodeModel, CanFail,
|
|
Context, VarRval, BinarySearchInfo, !Info),
|
|
BinarySearchInfo = ml_binary_search_info(_CodeModel, _VarRval,
|
|
_LoVarNameType, _HiVarNameType, MidVarNameType,
|
|
_ResultVarNameType, _MaybeStopLoopNameType, _FailStmts, Defns),
|
|
MidVarNameType = mlds_local_var_name_type(MidVarName, MidVarType),
|
|
MidVarLval = ml_local_var(MidVarName, MidVarType),
|
|
|
|
ml_gen_several_soln_lookup_code(Context,
|
|
ml_lval(MidVarLval), OutVars, OutTypes,
|
|
FirstSolnStructType, LaterSolnStructType,
|
|
NumLaterSolnsFieldId, FirstLaterSolnRowFieldId,
|
|
FirstSolnOutFieldIds, LaterSolnOutFieldIds,
|
|
FirstSolnVectorCommon, LaterSolnVectorCommon,
|
|
do_not_need_bit_vec_check_no_gaps,
|
|
MatchDefns, SuccessStmts, !Info),
|
|
|
|
% Generate the code that searches the table.
|
|
InitialComment = "binary string several soln lookup switch",
|
|
ml_gen_string_binary_switch_search(Context, InitialComment,
|
|
BinarySearchInfo, FirstSolnVectorCommon,FirstSolnTableSize,
|
|
FirstSolnStructType, StringFieldId,
|
|
MatchDefns, SuccessStmts, Stmts, !Info).
|
|
|
|
:- pred ml_gen_string_binary_several_lookup_initializers(
|
|
assoc_list(string, soln_consts(mlds_rval))::in,
|
|
mlds_type::in, mlds_type::in,
|
|
cord(mlds_initializer)::in, cord(mlds_initializer)::out,
|
|
cord(mlds_initializer)::in, cord(mlds_initializer)::out,
|
|
int::in, int::out, int::in) is det.
|
|
|
|
ml_gen_string_binary_several_lookup_initializers([],
|
|
_FirstSolnStructType, _LaterSolnStructType,
|
|
!FirstSolnRowInitializersCord, !LaterSolnRowInitializersCord,
|
|
!CurFirstSolnIndex, _CurLaterSolnIndex).
|
|
ml_gen_string_binary_several_lookup_initializers([Str - Solns | StrSolns],
|
|
FirstSolnStructType, LaterSolnStructType,
|
|
!FirstSolnRowInitializersCord, !LaterSolnRowInitializersCord,
|
|
!CurFirstSolnIndex, !.CurLaterSolnIndex) :-
|
|
StrRval = ml_const(mlconst_string(Str)),
|
|
(
|
|
Solns = one_soln(FirstSolnRvals),
|
|
NumLaterSolnsInitializer = gen_init_int(0),
|
|
FirstLaterSlotInitializer = gen_init_int(-1),
|
|
FirstSolnRowInitializer = init_struct(FirstSolnStructType,
|
|
[init_obj(StrRval),
|
|
NumLaterSolnsInitializer, FirstLaterSlotInitializer
|
|
| list.map(wrap_init_obj, FirstSolnRvals)])
|
|
;
|
|
Solns = several_solns(FirstSolnRvals, LaterSolns),
|
|
list.length(LaterSolns, NumLaterSolns),
|
|
NumLaterSolnsInitializer = gen_init_int(NumLaterSolns),
|
|
FirstLaterSlotInitializer = gen_init_int(!.CurLaterSolnIndex),
|
|
FirstSolnRowInitializer = init_struct(FirstSolnStructType,
|
|
[init_obj(StrRval),
|
|
NumLaterSolnsInitializer, FirstLaterSlotInitializer
|
|
| list.map(wrap_init_obj, FirstSolnRvals)]),
|
|
LaterSolnRowInitializers = list.map(
|
|
ml_construct_later_soln_row(LaterSolnStructType),
|
|
LaterSolns),
|
|
!:LaterSolnRowInitializersCord = !.LaterSolnRowInitializersCord ++
|
|
from_list(LaterSolnRowInitializers),
|
|
!:CurLaterSolnIndex = !.CurLaterSolnIndex + NumLaterSolns
|
|
),
|
|
cord.snoc(FirstSolnRowInitializer, !FirstSolnRowInitializersCord),
|
|
!:CurFirstSolnIndex = !.CurFirstSolnIndex + 1,
|
|
ml_gen_string_binary_several_lookup_initializers(StrSolns,
|
|
FirstSolnStructType, LaterSolnStructType,
|
|
!FirstSolnRowInitializersCord, !LaterSolnRowInitializersCord,
|
|
!CurFirstSolnIndex, !.CurLaterSolnIndex).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Code useful for both jump and lookup binary string switches.
|
|
%
|
|
|
|
:- type ml_binary_search_info
|
|
---> ml_binary_search_info(
|
|
mbsi_code_model :: code_model,
|
|
mbsi_switch_var :: mlds_rval,
|
|
mbsi_lo_var :: mlds_local_var_name_type,
|
|
mbsi_hi_var :: mlds_local_var_name_type,
|
|
mbsi_mid_var :: mlds_local_var_name_type,
|
|
mbsi_result_var :: mlds_local_var_name_type,
|
|
mbsi_stop_loop_var :: maybe(mlds_local_var_name_type),
|
|
mbsi_fail_statements :: list(mlds_stmt),
|
|
mbsi_defns :: list(mlds_local_var_defn)
|
|
).
|
|
|
|
:- pred ml_gen_string_binary_switch_search_vars(code_model::in, can_fail::in,
|
|
prog_context::in, mlds_rval::in, ml_binary_search_info::out,
|
|
ml_gen_info::in, ml_gen_info::out) is det.
|
|
|
|
ml_gen_string_binary_switch_search_vars(CodeModel, CanFail,
|
|
Context, VarRval, BinarySearchInfo, !Info) :-
|
|
% Generate the following local variable declarations:
|
|
% int lo;
|
|
% int hi;
|
|
% int mid;
|
|
% MR_String str;
|
|
|
|
IndexType = mlds_builtin_type_int(int_type_int),
|
|
ResultType = mlds_builtin_type_int(int_type_int),
|
|
% We never need to trace ints.
|
|
IndexGCStmt = gc_no_stmt,
|
|
ResultGCStmt = gc_no_stmt,
|
|
|
|
ml_gen_info_new_aux_var_name(mcav_lo, LoVarName, !Info),
|
|
LoVarDefn = ml_gen_mlds_var_decl(LoVarName, IndexType,
|
|
IndexGCStmt, Context),
|
|
LoVarNameType = mlds_local_var_name_type(LoVarName, IndexType),
|
|
|
|
ml_gen_info_new_aux_var_name(mcav_hi, HiVarName, !Info),
|
|
HiVarDefn = ml_gen_mlds_var_decl(HiVarName, IndexType,
|
|
IndexGCStmt, Context),
|
|
HiVarNameType = mlds_local_var_name_type(HiVarName, IndexType),
|
|
|
|
ml_gen_info_new_aux_var_name(mcav_mid, MidVarName, !Info),
|
|
MidVarDefn = ml_gen_mlds_var_decl(MidVarName, IndexType,
|
|
IndexGCStmt, Context),
|
|
MidVarNameType = mlds_local_var_name_type(MidVarName, IndexType),
|
|
|
|
ml_gen_info_new_aux_var_name(mcav_result, ResultVarName, !Info),
|
|
ResultVarDefn = ml_gen_mlds_var_decl(ResultVarName, ResultType,
|
|
ResultGCStmt, Context),
|
|
ResultVarNameType = mlds_local_var_name_type(ResultVarName, ResultType),
|
|
|
|
AlwaysDefns = [LoVarDefn, HiVarDefn, MidVarDefn, ResultVarDefn],
|
|
ml_should_use_stop_loop(Context, yes,
|
|
MaybeStopLoopNameType, StopLoopVarDefns, !Info),
|
|
Defns = AlwaysDefns ++ StopLoopVarDefns,
|
|
|
|
% Generate the code for when the lookup fails.
|
|
ml_gen_maybe_switch_failure(CodeModel, CanFail, Context, FailStmts, !Info),
|
|
|
|
BinarySearchInfo = ml_binary_search_info(CodeModel, VarRval,
|
|
LoVarNameType, HiVarNameType, MidVarNameType,
|
|
ResultVarNameType, MaybeStopLoopNameType, FailStmts, Defns).
|
|
|
|
:- pred ml_gen_string_binary_switch_search(prog_context::in, string::in,
|
|
ml_binary_search_info::in, mlds_vector_common::in, int::in, mlds_type::in,
|
|
mlds_field_id::in, list(mlds_local_var_defn)::in, list(mlds_stmt)::in,
|
|
list(mlds_stmt)::out, ml_gen_info::in, ml_gen_info::out) is det.
|
|
|
|
ml_gen_string_binary_switch_search(Context, InitialComment,
|
|
BinarySearchInfo, VectorCommon, TableSize, StructType, StringFieldId,
|
|
MatchDefns, MatchStmt, Stmts, !Info) :-
|
|
BinarySearchInfo = ml_binary_search_info(CodeModel, VarRval,
|
|
LoVarNameType, HiVarNameType, MidVarNameType,
|
|
ResultVarNameType, MaybeStopLoopVarNameType, FailStmts, _Defns),
|
|
|
|
LoVarNameType = mlds_local_var_name_type(LoVarName, LoVarType),
|
|
LoVarLval = ml_local_var(LoVarName, LoVarType),
|
|
LoVarRval = ml_lval(LoVarLval),
|
|
|
|
HiVarNameType = mlds_local_var_name_type(HiVarName, HiVarType),
|
|
HiVarLval = ml_local_var(HiVarName, HiVarType),
|
|
HiVarRval = ml_lval(HiVarLval),
|
|
|
|
MidVarNameType = mlds_local_var_name_type(MidVarName, MidVarType),
|
|
MidVarLval = ml_local_var(MidVarName, MidVarType),
|
|
MidVarRval = ml_lval(MidVarLval),
|
|
|
|
ResultVarNameType = mlds_local_var_name_type(ResultVarName, ResultVarType),
|
|
ResultVarLval = ml_local_var(ResultVarName, ResultVarType),
|
|
ResultVarRval = ml_lval(ResultVarLval),
|
|
|
|
(
|
|
MaybeStopLoopVarNameType = no,
|
|
MaybeStopLoopVarLval = no,
|
|
StopLoopVarNameList = []
|
|
;
|
|
MaybeStopLoopVarNameType = yes(StopLoopVarNameType),
|
|
StopLoopVarNameType =
|
|
mlds_local_var_name_type(StopLoopVarName, StopLoopVarType),
|
|
StopLoopVarLval0 = ml_local_var(StopLoopVarName, StopLoopVarType),
|
|
MaybeStopLoopVarLval = yes(StopLoopVarLval0),
|
|
StopLoopVarNameList = [StopLoopVarName]
|
|
),
|
|
|
|
ml_wrap_loop_break(CodeModel, yes, Context, MaybeStopLoopVarLval,
|
|
MatchDefns, MatchStmt, FailStmts,
|
|
SetupForFailStmts, SuccessStmt, AfterStmts, !Info),
|
|
|
|
ml_gen_info_get_module_info(!.Info, ModuleInfo),
|
|
MLDS_StringType = mercury_type_to_mlds_type(ModuleInfo, string_type),
|
|
|
|
InitLoVarStmt = ml_stmt_atomic(
|
|
assign(LoVarLval, ml_const(mlconst_int(0))),
|
|
Context),
|
|
InitHiVarStmt = ml_stmt_atomic(
|
|
assign(HiVarLval, ml_const(mlconst_int(TableSize - 1))),
|
|
Context),
|
|
CrossingTest = ml_binop(int_cmp(int_type_int, le), LoVarRval, HiVarRval),
|
|
|
|
AssignMidVarStmt = ml_stmt_atomic(
|
|
% NOTE This assumes that LoVarRval + HiVarRval cannot overflow.
|
|
% This is a safe assumption; I (zs) am pretty sure that if we ever
|
|
% get a string switch with around 2^30 cases, then the compiler
|
|
% will run out of memory before we get here :-(
|
|
assign(MidVarLval,
|
|
ml_binop(int_arith(int_type_int, ao_div),
|
|
ml_binop(int_arith(int_type_int, ao_add),
|
|
LoVarRval, HiVarRval),
|
|
ml_const(mlconst_int(2)))),
|
|
Context),
|
|
AssignResultVarStmt = ml_stmt_atomic(
|
|
assign(ResultVarLval,
|
|
ml_binop(str_nzp,
|
|
VarRval,
|
|
ml_lval(ml_field(yes(ptag(0u8)),
|
|
ml_vector_common_row_addr(VectorCommon, MidVarRval),
|
|
StructType, StringFieldId, MLDS_StringType)))),
|
|
Context),
|
|
ResultTest = ml_binop(int_cmp(int_type_int, eq), ResultVarRval,
|
|
ml_const(mlconst_int(0))),
|
|
UpdateLoOrHiVarStmt =
|
|
ml_stmt_if_then_else(
|
|
ml_binop(int_cmp(int_type_int, lt), ResultVarRval,
|
|
ml_const(mlconst_int(0))),
|
|
ml_stmt_atomic(
|
|
assign(HiVarLval,
|
|
ml_binop(int_arith(int_type_int, ao_sub), MidVarRval,
|
|
ml_const(mlconst_int(1)))),
|
|
Context),
|
|
yes(ml_stmt_atomic(
|
|
assign(LoVarLval,
|
|
ml_binop(int_arith(int_type_int, ao_add), MidVarRval,
|
|
ml_const(mlconst_int(1)))),
|
|
Context)),
|
|
Context),
|
|
|
|
LoHiMidResultMaybeStopVarNames = [LoVarName, MidVarName, HiVarName,
|
|
ResultVarName | StopLoopVarNameList],
|
|
(
|
|
MaybeStopLoopVarLval = no,
|
|
LoopBodyStmts = [
|
|
AssignMidVarStmt,
|
|
AssignResultVarStmt,
|
|
ml_stmt_if_then_else(ResultTest,
|
|
SuccessStmt, yes(UpdateLoOrHiVarStmt), Context)
|
|
],
|
|
SearchStmts = [
|
|
InitLoVarStmt,
|
|
InitHiVarStmt,
|
|
ml_stmt_while(loop_at_least_once,
|
|
CrossingTest,
|
|
ml_stmt_block([], [], LoopBodyStmts, Context),
|
|
LoHiMidResultMaybeStopVarNames, Context)
|
|
]
|
|
;
|
|
MaybeStopLoopVarLval = yes(StopLoopVarLval),
|
|
InitStopLoopVarStmt = ml_stmt_atomic(
|
|
assign(StopLoopVarLval, ml_const(mlconst_int(0))),
|
|
Context),
|
|
StopLoopTest = ml_binop(int_cmp(int_type_int, eq),
|
|
ml_lval(StopLoopVarLval), ml_const(mlconst_int(0))),
|
|
LoopBodyStmts = [
|
|
AssignMidVarStmt,
|
|
AssignResultVarStmt,
|
|
% SuccessStmt should set StopLoopVarLval to 1.
|
|
ml_stmt_if_then_else(ResultTest,
|
|
SuccessStmt, yes(UpdateLoOrHiVarStmt), Context)
|
|
],
|
|
SearchStmts = [
|
|
InitLoVarStmt,
|
|
InitHiVarStmt,
|
|
InitStopLoopVarStmt,
|
|
ml_stmt_while(loop_at_least_once,
|
|
ml_binop(logical_and, StopLoopTest, CrossingTest),
|
|
ml_stmt_block([], [], LoopBodyStmts, Context),
|
|
LoHiMidResultMaybeStopVarNames, Context)
|
|
]
|
|
),
|
|
|
|
InitialCommentStmt = ml_stmt_atomic(comment(InitialComment), Context),
|
|
Stmts = [InitialCommentStmt | SetupForFailStmts] ++
|
|
SearchStmts ++ AfterStmts.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Code useful for more than one kind of string switch.
|
|
%
|
|
|
|
:- pred ml_should_use_stop_loop(prog_context::in, bool::in,
|
|
maybe(mlds_local_var_name_type)::out, list(mlds_local_var_defn)::out,
|
|
ml_gen_info::in, ml_gen_info::out) is det.
|
|
|
|
ml_should_use_stop_loop(Context, LoopPresent,
|
|
MaybeStopLoopVarNameType, StopLoopLvalDefns, !Info) :-
|
|
(
|
|
LoopPresent = no,
|
|
UseStopLoop = no
|
|
;
|
|
LoopPresent = yes,
|
|
ml_gen_info_get_module_info(!.Info, ModuleInfo),
|
|
module_info_get_globals(ModuleInfo, Globals),
|
|
SupportsGoto = globals_target_supports_goto(Globals),
|
|
globals.lookup_string_option(Globals, experiment, Experiment),
|
|
(
|
|
SupportsGoto = yes,
|
|
( if Experiment = "use_stop_loop" then
|
|
UseStopLoop = yes
|
|
else
|
|
UseStopLoop = no
|
|
)
|
|
;
|
|
SupportsGoto = no,
|
|
UseStopLoop = yes
|
|
)
|
|
),
|
|
(
|
|
UseStopLoop = no,
|
|
MaybeStopLoopVarNameType = no,
|
|
StopLoopLvalDefns = []
|
|
;
|
|
UseStopLoop = yes,
|
|
% On targets that do not support gotos or break, after we have
|
|
% handled a match, we set the stop loop flag, which will cause the
|
|
% next test of the loop condition to fail.
|
|
|
|
StopLoopType = mlds_builtin_type_int(int_type_int),
|
|
% We never need to trace ints.
|
|
StopLoopGCStmt = gc_no_stmt,
|
|
|
|
ml_gen_info_new_aux_var_name(mcav_stop_loop, StopLoopVarName, !Info),
|
|
StopLoopVarDefn = ml_gen_mlds_var_decl(StopLoopVarName,
|
|
StopLoopType, StopLoopGCStmt, Context),
|
|
StopLoopVarNameType =
|
|
mlds_local_var_name_type(StopLoopVarName, StopLoopType),
|
|
MaybeStopLoopVarNameType = yes(StopLoopVarNameType),
|
|
StopLoopLvalDefns = [StopLoopVarDefn]
|
|
).
|
|
|
|
:- pred ml_gen_maybe_switch_failure(code_model::in, can_fail::in,
|
|
prog_context::in, list(mlds_stmt)::out,
|
|
ml_gen_info::in, ml_gen_info::out) is det.
|
|
|
|
ml_gen_maybe_switch_failure(CodeModel, CanFail, Context, FailStmts, !Info) :-
|
|
% We used to include comments in FailStmts. However, that would complicate
|
|
% the task of ml_wrap_loop_break, which needs to decide whether FailStmts
|
|
% actually contains executable code.
|
|
(
|
|
CanFail = cannot_fail,
|
|
% This can happen if the initial inst of the switched-on variable
|
|
% shows that we know a finite set of strings that the variable can be
|
|
% bound to.
|
|
FailStmts = []
|
|
;
|
|
CanFail = can_fail,
|
|
ml_gen_failure(CodeModel, Context, FailStmts, !Info)
|
|
).
|
|
|
|
% ml_wrap_loop_break(CodeModel, LoopPresent,
|
|
% Context, MaybeStopLoopVarLval, MatchDefns, MatchStmts,
|
|
% SetupForFailStmts, BodyStmt, AfterStmts, !Info)
|
|
%
|
|
% MatchStmts should be the statements that we execute once we find
|
|
% a match, and OnlyFailAfterStmts should be the statements that we
|
|
% want to execute after the search loop if the loop did NOT find a match.
|
|
%
|
|
% This predicate wraps up MatchStmts with both MatchDefns and with
|
|
% other following code that causes execution to exit the loop
|
|
% after a match, and returns the resulting code as BodyStmt.
|
|
%
|
|
% We also return SetupForFailStmts and AfterStmts.
|
|
% SetupForFailStmts will be code to put before the loop, to set up
|
|
% for possible failure to find a match.
|
|
%
|
|
% AfterStmts will be code to put after the loop. It will contain
|
|
% OnlyFailAfterStmts, wrapped up in a test if necessary, as well as
|
|
% any code needed to enable BodyStmt to break out of the loop on a match.
|
|
%
|
|
:- pred ml_wrap_loop_break(code_model::in, bool::in, prog_context::in,
|
|
maybe(mlds_lval)::in, list(mlds_local_var_defn)::in,
|
|
list(mlds_stmt)::in, list(mlds_stmt)::in,
|
|
list(mlds_stmt)::out, mlds_stmt::out, list(mlds_stmt)::out,
|
|
ml_gen_info::in, ml_gen_info::out) is det.
|
|
|
|
ml_wrap_loop_break(CodeModel, LoopPresent, Context, MaybeStopLoopVarLval,
|
|
MatchDefns, MatchStmts, FailStmts,
|
|
SetupForFailStmts, BodyStmt, AfterStmts, !Info) :-
|
|
(
|
|
CodeModel = model_det,
|
|
SetupForFailStmts = [],
|
|
expect(unify(FailStmts, []), $pred,
|
|
"model_det, but FailStmts is not empty"),
|
|
OnlyFailAfterStmts = []
|
|
;
|
|
CodeModel = model_semi,
|
|
(
|
|
MaybeStopLoopVarLval = no,
|
|
SetupForFailStmts = [],
|
|
OnlyFailAfterStmts = FailStmts
|
|
;
|
|
MaybeStopLoopVarLval = yes(_),
|
|
% It is more efficient to set up the default value of the succeeded
|
|
% variable (FALSE) with an unconditional assignment than it is
|
|
% to set up its actual value with a conditional assignment.
|
|
SetupForFailStmts = FailStmts,
|
|
OnlyFailAfterStmts = []
|
|
)
|
|
;
|
|
CodeModel = model_non,
|
|
SetupForFailStmts = [],
|
|
expect(unify(FailStmts, []), $pred,
|
|
"model_non, but FailStmts is not empty"),
|
|
OnlyFailAfterStmts = []
|
|
),
|
|
(
|
|
MaybeStopLoopVarLval = no,
|
|
( if
|
|
LoopPresent = no,
|
|
OnlyFailAfterStmts = []
|
|
then
|
|
BodyStmt = ml_stmt_block(MatchDefns, [], MatchStmts, Context),
|
|
AfterStmts = []
|
|
else
|
|
ml_gen_info_get_module_info(!.Info, ModuleInfo),
|
|
module_info_get_globals(ModuleInfo, Globals),
|
|
SupportsBreakContinue =
|
|
globals_target_supports_break_and_continue(Globals),
|
|
globals.lookup_string_option(Globals, experiment, Experiment),
|
|
( if
|
|
SupportsBreakContinue = yes,
|
|
OnlyFailAfterStmts = [],
|
|
Experiment \= "use_end_label"
|
|
then
|
|
BreakCommentStmt = ml_stmt_atomic(
|
|
comment("break out of search loop"), Context),
|
|
BreakStmt = ml_stmt_goto(goto_break_loop, Context),
|
|
BodyStmt =
|
|
ml_stmt_block(MatchDefns, [],
|
|
MatchStmts ++ [BreakCommentStmt, BreakStmt], Context),
|
|
AfterStmts = []
|
|
else
|
|
ml_gen_new_label(EndLabel, !Info),
|
|
GotoCommentStmt = ml_stmt_atomic(
|
|
comment("jump out of search loop"), Context),
|
|
GotoEndStmt = ml_stmt_goto(goto_label(EndLabel), Context),
|
|
BodyStmt =
|
|
ml_stmt_block(MatchDefns, [],
|
|
MatchStmts ++ [GotoCommentStmt, GotoEndStmt], Context),
|
|
EndLabelStmt = ml_stmt_label(EndLabel, Context),
|
|
AfterStmts = OnlyFailAfterStmts ++ [EndLabelStmt]
|
|
)
|
|
)
|
|
;
|
|
MaybeStopLoopVarLval = yes(StopLoopVarLval),
|
|
( if
|
|
LoopPresent = no,
|
|
OnlyFailAfterStmts = []
|
|
then
|
|
BodyStmt =
|
|
ml_stmt_block(MatchDefns, [], MatchStmts, Context)
|
|
else
|
|
SetStopLoopStmt =
|
|
ml_stmt_atomic(
|
|
assign(StopLoopVarLval, ml_const(mlconst_int(1))),
|
|
Context),
|
|
BodyStmt =
|
|
ml_stmt_block(MatchDefns, [],
|
|
MatchStmts ++ [SetStopLoopStmt], Context)
|
|
),
|
|
(
|
|
OnlyFailAfterStmts = [],
|
|
AfterStmts = []
|
|
;
|
|
(
|
|
OnlyFailAfterStmts = [OnlyFailAfterStmt]
|
|
;
|
|
OnlyFailAfterStmts = [_, _ | _],
|
|
OnlyFailAfterStmt =
|
|
ml_stmt_block([], [], OnlyFailAfterStmts, Context)
|
|
),
|
|
SuccessTest = ml_binop(int_cmp(int_type_int, eq),
|
|
ml_lval(StopLoopVarLval),
|
|
ml_const(mlconst_int(0))),
|
|
AfterStmt =
|
|
ml_stmt_if_then_else(SuccessTest, OnlyFailAfterStmt, no,
|
|
Context),
|
|
AfterStmts = [AfterStmt]
|
|
)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module ml_backend.ml_string_switch.
|
|
%---------------------------------------------------------------------------%
|