%---------------------------------------------------------------------------% % vim: ft=mercury ts=4 sw=4 et %---------------------------------------------------------------------------% % Copyright (C) 1994-2011 The University of Melbourne. % This file may only be copied under the terms of the GNU General % Public License - see the file COPYING in the Mercury distribution. %---------------------------------------------------------------------------% % % File: 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 soluion. % % 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::in, 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::in, 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::in, ml_gen_info::out) is det. %---------------------------------------------------------------------------% %---------------------------------------------------------------------------% :- implementation. :- import_module backend_libs. :- import_module backend_libs.builtin_ops. :- import_module backend_libs.string_encoding. :- import_module backend_libs.switch_util. :- import_module hlds.hlds_data. :- import_module hlds.hlds_module. :- import_module libs. :- import_module libs.globals. :- import_module libs.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) :- gen_tagged_case_codes_for_string_switch(CodeModel, EntryPackedArgsMap, TaggedCases, map.init, CodeMap, !Info), create_nested_switch_trie(TaggedCases, Context, VarRval, MaxCaseNum, CaseNumVarLval, CaseNumVarDefn, InitCaseNumVarStmt, GetCaseNumSwitchStmt, !Info), map.to_assoc_list(CodeMap, CodeCases), generate_trie_arms(CodeCases, [], RevCaseNumSwitchArms), list.reverse(RevCaseNumSwitchArms, CaseNumSwitchArms), 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) ), 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]. %---------------------------------------------------------------------------% %---------------------------------------------------------------------------% % % The implementation of lookup tries. % ml_generate_string_trie_lookup_switch(VarRval, TaggedCases, LookupSwitchInfo, CodeModel, CanFail, Context, Stmts, !Info) :- create_nested_switch_trie(TaggedCases, Context, VarRval, MaxCaseNum, CaseNumVarLval, CaseNumVarDefn, InitCaseNumVarStmt, GetCaseNumSwitchStmt, !Info), LookupSwitchInfo = ml_lookup_switch_info(CaseIdConsts, OutVars, OutTypes), ( 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, [], RevRowInitializers), expect(unify(MaxCaseNum + 1, AfterLastCaseNum), $pred, "MaxCaseNum + 1 != AfterLastCaseNum"), list.reverse(RevRowInitializers, RowInitializers), 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_lt(int_type_int), 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, list(mlds_initializer)::in, list(mlds_initializer)::out) is det. ml_gen_string_trie_simple_lookup_slots(_StructType, [], !CurCaseNum, !RevRowInitializers). ml_gen_string_trie_simple_lookup_slots(StructType, [CaseIdValue | CaseIdValues], !CurCaseNum, !RevRowInitializers) :- 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), !:RevRowInitializers = [RowInitializer | !.RevRowInitializers], !:CurCaseNum = !.CurCaseNum + 1, ml_gen_string_trie_simple_lookup_slots(StructType, CaseIdValues, !CurCaseNum, !RevRowInitializers). %---------------------------------------------------------------------------% :- 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, [], RevFirstSolnRowInitializers, cord.init, LaterSolnRowInitializerCord), expect(unify(MaxCaseNum + 1, AfterLastCaseNum), $pred, "MaxCaseNum + 1 != AfterLastCaseNum"), list.reverse(RevFirstSolnRowInitializers, FirstSolnRowInitializers), 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, dont_need_bit_vec_check, MatchDefns, SuccessStmts, !Info), SuccessBlockStmt = ml_stmt_block(MatchDefns, [], SuccessStmts, Context), ( CanFail = can_fail, IsCaseNumNonNegCond = ml_binop(int_ge(int_type_int), 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, list(mlds_initializer)::in, list(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, !RevFirstSolnRowInitializers, !LaterSolnRowInitializersCord). ml_gen_string_trie_several_soln_lookup_slots( FirstSolnStructType, LaterSolnStructType, [CaseIdSolns | CaseIdsSolns], !CurCaseNum, !.CurLaterSolnIndex, !RevFirstSolnRowInitializers, !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)), !:RevFirstSolnRowInitializers = [FirstSolnRowInitializer | !.RevFirstSolnRowInitializers], !:CurCaseNum = !.CurCaseNum + 1, ml_gen_string_trie_several_soln_lookup_slots( FirstSolnStructType, LaterSolnStructType, CaseIdsSolns, !CurCaseNum, !.CurLaterSolnIndex, !RevFirstSolnRowInitializers, !LaterSolnRowInitializersCord). %---------------------------------------------------------------------------% %---------------------------------------------------------------------------% % % Code useful for both the jump and lookup versions of tries. % :- type trie_node ---> trie_leaf( leaf_matched :: list(int), % The already matched code units, in reverse order. leaf_unmatched :: list(int), % The not-yet matched code units, in forward order. % Invariant: applying from_code_unit_list to % list.reverse(leaf_matched) ++ leaf_unmatched % should yield the original string. case_id % The case_id of the switch arm. ) ; trie_choice( choice_matched :: list(int), % The already matched code units, in reverse order. choice_next_level :: map(int, trie_node), % Maps the next code unit to the trie node reachable % through it. choice_end :: maybe(case_id) % The case number of the switch arm whose string ends here, % if there is one. ). :- pred 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. 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), create_trie(Encoding, TaggedCases, MaxCaseNum, TopTrieNode), ml_gen_trie_case_num_var_and_init(Context, CaseNumVarLval, CaseNumVarDefn, InitCaseNumVarStmt, !Info), convert_trie_to_nested_switches(Encoding, VarRval, CaseNumVarLval, Context, 0, TopTrieNode, GetCaseNumSwitchStmt). %---------------------------------------------------------------------------% :- pred create_trie(string_encoding::in, list(tagged_case)::in, int::out, trie_node::out) is det. create_trie(Encoding, TaggedCases, MaxCaseNum, TopTrieNode) :- build_str_case_id_assoc_list(TaggedCases, -1, MaxCaseNum, [], StrsCaseIds), % The order of StrsCaseIds does not matter; we will build the same trie % regardless of the order. ( StrsCaseIds = [], TopTrieNode = trie_choice([], map.init, no) ; StrsCaseIds = [HeadStrCaseId | TailStrCaseIds], HeadStrCaseId = HeadStr - HeadCaseId, to_code_unit_list(Encoding, HeadStr, HeadStrCodeUnits), TopTrieNode1 = trie_leaf([], HeadStrCodeUnits, HeadCaseId), insert_cases_into_trie(Encoding, TailStrCaseIds, TopTrieNode1, TopTrieNode) ). :- pred insert_cases_into_trie(string_encoding::in, assoc_list(string, case_id)::in, trie_node::in, trie_node::out) is det. insert_cases_into_trie(_Encoding, [], !TrieNode). insert_cases_into_trie(Encoding, [Case | Cases], !TrieNode) :- Case = Str - CaseId, to_code_unit_list(Encoding, Str, StrCodeUnits), insert_case_into_trie_node([], StrCodeUnits, CaseId, !TrieNode), insert_cases_into_trie(Encoding, Cases, !TrieNode). :- pred insert_case_into_trie_node(list(int)::in, list(int)::in, case_id::in, trie_node::in, trie_node::out) is det. insert_case_into_trie_node(InsertMatched, InsertNotYetMatched, InsertCaseId, TrieNode0, TrieNode) :- ( TrieNode0 = trie_leaf(LeafMatched, LeafNotYetMatched, LeafCaseId), expect(unify(LeafMatched, InsertMatched), $pred, "LeafMatched didn't"), ( LeafNotYetMatched = [], ChoiceMap0 = map.init, MaybeEnd0 = yes(LeafCaseId) ; LeafNotYetMatched = [LeafFirstCodeUnit | LeafLaterCodeUnits], NewLeaf = trie_leaf([LeafFirstCodeUnit | LeafMatched], LeafLaterCodeUnits, LeafCaseId), ChoiceMap0 = map.singleton(LeafFirstCodeUnit, NewLeaf), MaybeEnd0 = no ) ; TrieNode0 = trie_choice(ChoiceMatched, ChoiceMap0, MaybeEnd0), expect(unify(ChoiceMatched, InsertMatched), $pred, "ChoiceMatched didn't") ), insert_case_into_trie_choice(InsertMatched, InsertNotYetMatched, InsertCaseId, ChoiceMap0, ChoiceMap, MaybeEnd0, MaybeEnd), TrieNode = trie_choice(InsertMatched, ChoiceMap, MaybeEnd). :- pred insert_case_into_trie_choice(list(int)::in, list(int)::in, case_id::in, map(int, trie_node)::in, map(int, trie_node)::out, maybe(case_id)::in, maybe(case_id)::out) is det. insert_case_into_trie_choice(InsertMatched, InsertNotYetMatched, InsertCaseId, ChoiceMap0, ChoiceMap, MaybeEnd0, MaybeEnd) :- ( InsertNotYetMatched = [], ChoiceMap = ChoiceMap0, ( MaybeEnd0 = no, MaybeEnd = yes(InsertCaseId) ; MaybeEnd0 = yes(_), % You can't have more than one occurrence of a string % as a cons_id in a switch. unexpected($pred, "two strings end at same trie node") ) ; InsertNotYetMatched = [InsertFirstCodeUnit | InsertLaterCodeUnits], MaybeEnd = MaybeEnd0, ( if map.search(ChoiceMap0, InsertFirstCodeUnit, SubTrieNode0) then insert_case_into_trie_node([InsertFirstCodeUnit | InsertMatched], InsertLaterCodeUnits, InsertCaseId, SubTrieNode0, SubTrieNode), map.det_update(InsertFirstCodeUnit, SubTrieNode, ChoiceMap0, ChoiceMap) else SubTrieNode = trie_leaf([InsertFirstCodeUnit | InsertMatched], InsertLaterCodeUnits, InsertCaseId), map.det_insert(InsertFirstCodeUnit, SubTrieNode, ChoiceMap0, ChoiceMap) ) ). % 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 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. convert_trie_to_nested_switches(Encoding, VarRval, CaseNumVarLval, Context, NumMatched, TrieNode, Stmt) :- ( TrieNode = trie_leaf(RevMatchedCodeUnits, NotYetMatchedCodeUnits, CaseId), CaseId = case_id(CaseNum), CaseNumRval = ml_const(mlconst_int(CaseNum)), SetCaseNumVarAssign = assign(CaseNumVarLval, CaseNumRval), SetCaseNumVarStmt = ml_stmt_atomic(SetCaseNumVarAssign, Context), AllCodeUnits = list.reverse(RevMatchedCodeUnits) ++ NotYetMatchedCodeUnits, list.length(RevMatchedCodeUnits, NumRevMatchedCodeUnits), expect(unify(NumRevMatchedCodeUnits, NumMatched), $pred, "NumRevMatchedCodeUnits != NumMatched"), ( if from_code_unit_list(Encoding, AllCodeUnits, String) then StringRval = ml_const(mlconst_string(String)) else unexpected($pred, "code units cannot be turned back into string") ), CondRval = ml_binop(offset_str_eq(NumMatched), VarRval, StringRval), Stmt = ml_stmt_if_then_else(CondRval, SetCaseNumVarStmt, no, Context) ; TrieNode = trie_choice(RevMatchedCodeUnits, ChoiceMap, MaybeEnd), CurCodeUnitRval = ml_binop(string_unsafe_index_code_unit, VarRval, ml_const(mlconst_int(NumMatched))), map.to_assoc_list(ChoiceMap, ChoicePairs), ( if ChoicePairs = [OneChoicePair], MaybeEnd = no then OneChoicePair = OneCodeUnit - OneSubTrieNode, OneCodeUnitConst = ml_const(mlconst_int(OneCodeUnit)), FirstCond = ml_binop(eq(int_type_int), CurCodeUnitRval, OneCodeUnitConst), chase_one_cond_trie_nodes(Encoding, VarRval, CaseNumVarLval, Context, NumMatched + 1, OneSubTrieNode, FirstCond, AllCond, ThenStmt), Stmt = ml_stmt_if_then_else(AllCond, ThenStmt, no, Context) else 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), EndCaseId = case_id(EndCaseNum), EndCaseNumRval = ml_const(mlconst_int(EndCaseNum)), EndSetCaseNumVarAssign = assign(CaseNumVarLval, EndCaseNumRval), EndSetCaseNumVarStmt = ml_stmt_atomic(EndSetCaseNumVarAssign, Context), NullCodeUnit = 0, % Match the terminating NUL character. NullMatchCond = match_value(ml_const(mlconst_int(NullCodeUnit))), EndSwitchArm = mlds_switch_case(NullMatchCond, [], EndSetCaseNumVarStmt), SwitchArms = [EndSwitchArm | SwitchArms0] ), list.length(RevMatchedCodeUnits, NumRevMatchedCodeUnits), expect(unify(NumRevMatchedCodeUnits, NumMatched), $pred, "NumRevMatchedCodeUnits != NumMatched"), SwitchCodeUnitRval = CurCodeUnitRval, % 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 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. convert_trie_choices_to_nested_switches(_, _, _, _, _, [], !SwitchArmsCord). convert_trie_choices_to_nested_switches(Encoding, VarRval, CaseNumVarLval, Context, NumMatched, [ChoicePair | ChoicePairs], !SwitchArmsCord) :- ChoicePair = CodeUnit - SubTrieNode, 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), !:SwitchArmsCord = cord.snoc(!.SwitchArmsCord, SwitchArm), convert_trie_choices_to_nested_switches(Encoding, VarRval, CaseNumVarLval, Context, NumMatched, ChoicePairs, !SwitchArmsCord). :- pred generate_trie_arms(assoc_list(case_id, mlds_stmt)::in, list(mlds_switch_case)::in, list(mlds_switch_case)::out) is det. generate_trie_arms([], !RevSwitchCases). generate_trie_arms([CasePair | CasePairs], !RevSwitchCases) :- CasePair = CaseId - CaseStmt, CaseId = case_id(CaseNum), MatchCond = match_value(ml_const(mlconst_int(CaseNum))), Case = mlds_switch_case(MatchCond, [], CaseStmt), !:RevSwitchCases = [Case | !.RevSwitchCases], generate_trie_arms(CasePairs, !RevSwitchCases). :- pred chase_one_cond_trie_nodes(string_encoding::in, mlds_rval::in, mlds_lval::in, prog_context::in, int::in, trie_node::in, mlds_rval::in, mlds_rval::out, mlds_stmt::out) is det. chase_one_cond_trie_nodes(Encoding, VarRval, CaseNumVarLval, Context, NumMatched, TrieNode, RevCond0, RevCond, ThenStmt) :- ( if TrieNode = trie_choice(RevMatchedCodeUnits, ChoiceMap, MaybeEnd), map.to_assoc_list(ChoiceMap, ChoicePairs), ChoicePairs = [OneChoicePair], MaybeEnd = no then expect(unify(list.length(RevMatchedCodeUnits), NumMatched), $pred, "length(RevMatchedCodeUnits) != NumMatched"), OneChoicePair = OneCodeUnit - OneSubTrieNode, CurCodeUnitRval = ml_binop(string_unsafe_index_code_unit, VarRval, ml_const(mlconst_int(NumMatched))), OneCodeUnitConst = ml_const(mlconst_int(OneCodeUnit)), CurCond = ml_binop(eq(int_type_int), CurCodeUnitRval, OneCodeUnitConst), RevCond1 = ml_binop(logical_and, RevCond0, CurCond), chase_one_cond_trie_nodes(Encoding, VarRval, CaseNumVarLval, Context, NumMatched + 1, OneSubTrieNode, RevCond1, RevCond, ThenStmt) else RevCond = RevCond0, convert_trie_to_nested_switches(Encoding, VarRval, CaseNumVarLval, Context, NumMatched, TrieNode, ThenStmt) ). %---------------------------------------------------------------------------% %---------------------------------------------------------------------------% % % The implementation of jump hash tables. % ml_generate_string_hash_jump_switch(VarRval, TaggedCases, CodeModel, CanFail, EntryPackedArgsMap, Context, Defns, Stmts, !Info) :- gen_tagged_case_codes_for_string_switch(CodeModel, EntryPackedArgsMap, TaggedCases, map.init, CodeMap, !Info), build_str_case_id_assoc_list(TaggedCases, -1, _MaxCaseNum, [], RevStrsCaseIds), list.reverse(RevStrsCaseIds, 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, [], RevRowInitializers, map.init, RevMap), list.reverse(RevRowInitializers, RowInitializers), % 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, RevList), generate_string_jump_switch_arms(CodeMap, RevList, [], 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 build_str_case_id_assoc_list(list(tagged_case)::in, int::in, int::out, assoc_list(string, case_id)::in, assoc_list(string, case_id)::out) is det. build_str_case_id_assoc_list([], !MaxCaseNum, !RevStrsCaseIds). build_str_case_id_assoc_list([TaggedCase | TaggedCases], !MaxCaseNum, !RevStrsCaseIds) :- TaggedCase = tagged_case(MainTaggedConsId, OtherTaggedConsIds, CaseId, _), CaseId = case_id(CaseNum), int.max(CaseNum, !MaxCaseNum), add_to_strs_case_ids(CaseId, MainTaggedConsId, !RevStrsCaseIds), list.foldl(add_to_strs_case_ids(CaseId), OtherTaggedConsIds, !RevStrsCaseIds), build_str_case_id_assoc_list(TaggedCases, !MaxCaseNum, !RevStrsCaseIds). :- pred add_to_strs_case_ids(case_id::in, tagged_cons_id::in, assoc_list(string, case_id)::in, assoc_list(string, case_id)::out) is det. add_to_strs_case_ids(CaseId, TaggedConsId, !RevStrsCaseIds) :- TaggedConsId = tagged_cons_id(_ConsId, ConsTag), ( if ConsTag = string_tag(String) then !:RevStrsCaseIds = [String - CaseId | !.RevStrsCaseIds] else unexpected($pred, "non-string tag") ). %---------------------------------------------------------------------------% :- pred 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, ml_gen_info::in, ml_gen_info::out) is det. gen_tagged_case_codes_for_string_switch(_CodeModel, _EntryPackedArgsMap, [], !CodeMap, !Info). gen_tagged_case_codes_for_string_switch(CodeModel, EntryPackedArgsMap, [TaggedCase | TaggedCases], !CodeMap, !Info) :- gen_tagged_case_code_for_string_switch(CodeModel, EntryPackedArgsMap, TaggedCase, !CodeMap, !Info), gen_tagged_case_codes_for_string_switch(CodeModel, EntryPackedArgsMap, TaggedCases, !CodeMap, !Info). :- pred gen_tagged_case_code_for_string_switch_dummy(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, ml_gen_info::in, ml_gen_info::out, unit::in, unit::out) is det. gen_tagged_case_code_for_string_switch_dummy(CodeModel, EntryPackedArgsMap, TaggedCase, CaseId, !CodeMap, !Info, !Dummy) :- TaggedCase = tagged_case(_, _, CaseId, _), gen_tagged_case_code_for_string_switch(CodeModel, EntryPackedArgsMap, TaggedCase, !CodeMap, !Info). :- pred 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, ml_gen_info::in, ml_gen_info::out) is det. gen_tagged_case_code_for_string_switch(CodeModel, EntryPackedArgsMap, TaggedCase, !CodeMap, !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, !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. The list is % stored in reversed form. % :- type hash_slots ---> hash_slots(int, list(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, list(mlds_initializer)::in, list(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, !RevRowInitializers, !RevMap) :- ( if Slot = TableSize then true else ml_gen_string_hash_jump_slot(Slot, HashSlotMap, StructType, MaybeNextSlotId, RowInitializer, !RevMap), !:RevRowInitializers = [RowInitializer | !.RevRowInitializers], ml_gen_string_hash_jump_slots(Slot + 1, TableSize, HashSlotMap, StructType, MaybeNextSlotId, !RevRowInitializers, !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, OldLaterSlots), NewEntry = hash_slots(OldFirstSlot, [Slot | OldLaterSlots]), map.det_update(CaseId, NewEntry, !RevMap) else NewEntry = hash_slots(Slot, []), 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, RevLaterHashSlots), list.reverse(RevLaterHashSlots, LaterHashSlots), 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), ( 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, [], RevRowInitializers), list.reverse(RevRowInitializers, RowInitializers), % 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, list(mlds_initializer)::in, list(mlds_initializer)::out) is det. ml_gen_string_hash_simple_lookup_slots(Slot, TableSize, StructType, HashSlotMap, MaybeNextSlotId, DummyOutInitializers, !RevRowInitializers) :- ( if Slot = TableSize then true else ml_gen_string_hash_simple_lookup_slot(Slot, StructType, HashSlotMap, MaybeNextSlotId, DummyOutInitializers, RowInitializer), !:RevRowInitializers = [RowInitializer | !.RevRowInitializers], ml_gen_string_hash_simple_lookup_slots(Slot + 1, TableSize, StructType, HashSlotMap, MaybeNextSlotId, DummyOutInitializers, !RevRowInitializers) ). :- 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, [], RevFirstSolnRowInitializers, cord.init, LaterSolnRowInitializersCord, 0), list.reverse(RevFirstSolnRowInitializers, FirstSolnRowInitializers), 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, dont_need_bit_vec_check, 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, list(mlds_initializer)::in, list(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, !RevFirstSolnRowInitializers, !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), !:RevFirstSolnRowInitializers = [FirstSolnsRowInitializer | !.RevFirstSolnRowInitializers], ml_gen_string_hash_several_soln_lookup_slots(Slot + 1, TableSize, HashSlotMap, FirstSolnStructType, LaterSolnStructType, MaybeNextSlotId, DummyOutInitializers, !RevFirstSolnRowInitializers, !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(ne(int_type_int), StringVarRval, ml_const(mlconst_null(StringVarType))), ml_binop(str_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_ge(int_type_int), 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(eq(int_type_int), 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), switch_util.string_binary_cases(Cases, gen_tagged_case_code_for_string_switch_dummy(CodeModel, EntryPackedArgsMap), CaseLabelMap0, CaseLabelMap, !Info, unit, _, SortedTable), ml_gen_string_binary_jump_initializers(SortedTable, StructType, [], RevRowInitializers, 0, TableSize), list.reverse(RevRowInitializers, RowInitializers), % 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, list(mlds_initializer)::in, list(mlds_initializer)::out, int::in, int::out) is det. ml_gen_string_binary_jump_initializers([], _StructType, !RevRowInitializers, !CurIndex). ml_gen_string_binary_jump_initializers([Str - CaseId | StrCaseIds], StructType, !RevRowInitializers, !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)]), !:RevRowInitializers = [RowInitializer | !.RevRowInitializers], !:CurIndex = !.CurIndex + 1, ml_gen_string_binary_jump_initializers(StrCaseIds, StructType, !RevRowInitializers, !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), ( 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, [], RevRowInitializers, 0, TableSize), list.reverse(RevRowInitializers, RowInitializers), 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, list(mlds_initializer)::in, list(mlds_initializer)::out, int::in, int::out) is det. ml_gen_string_binary_simple_lookup_initializers([], _StructType, !RevRowInitializers, !CurIndex). ml_gen_string_binary_simple_lookup_initializers([Str - Rvals | StrRvals], StructType, !RevRowInitializers, !CurIndex) :- StrRval = ml_const(mlconst_string(Str)), RowInitializer = init_struct(StructType, [init_obj(StrRval) | list.map(wrap_init_obj, Rvals)]), !:RevRowInitializers = [RowInitializer | !.RevRowInitializers], !:CurIndex = !.CurIndex + 1, ml_gen_string_binary_simple_lookup_initializers(StrRvals, StructType, !RevRowInitializers, !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, [], RevFirstSolnRowInitializers, cord.init, LaterSolnRowInitializersCord, 0, FirstSolnTableSize, 0), list.reverse(RevFirstSolnRowInitializers, FirstSolnRowInitializers), 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, dont_need_bit_vec_check, 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, list(mlds_initializer)::in, list(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, !RevFirstSolnRowInitializers, !LaterSolnRowInitializersCord, !CurFirstSolnIndex, _CurLaterSolnIndex). ml_gen_string_binary_several_lookup_initializers([Str - Solns | StrSolns], FirstSolnStructType, LaterSolnStructType, !RevFirstSolnRowInitializers, !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 ), !:RevFirstSolnRowInitializers = [FirstSolnRowInitializer | !.RevFirstSolnRowInitializers], !:CurFirstSolnIndex = !.CurFirstSolnIndex + 1, ml_gen_string_binary_several_lookup_initializers(StrSolns, FirstSolnStructType, LaterSolnStructType, !RevFirstSolnRowInitializers, !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_le(int_type_int), 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_div(int_type_int), ml_binop(int_add(int_type_int), LoVarRval, HiVarRval), ml_const(mlconst_int(2)))), Context), AssignResultVarStmt = ml_stmt_atomic( assign(ResultVarLval, ml_binop(str_cmp, VarRval, ml_lval(ml_field(yes(ptag(0u8)), ml_vector_common_row_addr(VectorCommon, MidVarRval), StructType, StringFieldId, MLDS_StringType)))), Context), ResultTest = ml_binop(eq(int_type_int), ResultVarRval, ml_const(mlconst_int(0))), UpdateLoOrHiVarStmt = ml_stmt_if_then_else( ml_binop(int_lt(int_type_int), ResultVarRval, ml_const(mlconst_int(0))), ml_stmt_atomic( assign(HiVarLval, ml_binop(int_sub(int_type_int), MidVarRval, ml_const(mlconst_int(1)))), Context), yes(ml_stmt_atomic( assign(LoVarLval, ml_binop(int_add(int_type_int), 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(eq(int_type_int), 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(eq(int_type_int), 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. %---------------------------------------------------------------------------%