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