Files
mercury/compiler/ml_string_switch.m
Zoltan Somogyi 386160f937 s/dont/do_not/ in the compiler directory.
compiler/*.m:
    Standardize on the do_not spelling over the dont contraction
    in the compiler directory. (We used to have a lot of both spellings.)
2024-08-12 12:49:23 +02:00

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 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_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_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_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_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.
%---------------------------------------------------------------------------%