mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-14 13:23:53 +00:00
compiler/mlds.m:
Replace goto_break with goto_break_loop and goto_break_switch, each
intended to break from a particular construct. It was confusion between
the two kinds of breaks that led to the earlier bug that broke
--prefer-while-loop-over-jump-mutual; this separation should make
such bugs easy to detect.
Rename goto_continue as goto_continue_loop to match the new naming scheme.
compiler/mlds_to_c.m:
compiler/mlds_to_cs.m:
compiler/mlds_to_java.m:
When emitting goto_break_loop and goto_break_switch, check whether
the nearest enclosing break-able scope is a loop or switch respectively.
To make this check possible, record the nearest break-able scope.
While these additions make the compiler do extra work, the performance
impact is negligible.
compiler/mlds_to_target_util.m:
Add the type that mlds_to_{c,cs,java}.m all use to identify
break-able scopes.
compiler/ml_call_gen.m:
compiler/ml_proc_gen.m:
compiler/ml_string_switch.m:
Update the code that generates gotos.
2277 lines
96 KiB
Mathematica
2277 lines
96 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% 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 gave 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, 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, 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, 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,
|
|
Context, Stmts, !Info) :-
|
|
gen_tagged_case_codes_for_string_switch(CodeModel, 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_native_int_type,
|
|
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_native_int_type,
|
|
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_native_int_type,
|
|
% 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_native_int_type, 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,
|
|
Context, Defns, Stmts, !Info) :-
|
|
gen_tagged_case_codes_for_string_switch(CodeModel, 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 = mercury_type_to_mlds_type(ModuleInfo, string_type),
|
|
MLDS_IntType = mlds_native_int_type,
|
|
|
|
( 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, SlotVarLval, _StringVarLval,
|
|
_MaybeStopLoopLval, _FailStmts, Defns),
|
|
|
|
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),
|
|
SlotVarType = mlds_native_int_type,
|
|
|
|
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,
|
|
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, [],
|
|
!CodeMap, !Info).
|
|
gen_tagged_case_codes_for_string_switch(CodeModel, [TaggedCase | TaggedCases],
|
|
!CodeMap, !Info) :-
|
|
gen_tagged_case_code_for_string_switch(CodeModel, TaggedCase,
|
|
!CodeMap, !Info),
|
|
gen_tagged_case_codes_for_string_switch(CodeModel, TaggedCases,
|
|
!CodeMap, !Info).
|
|
|
|
:- pred gen_tagged_case_code_for_string_switch_dummy(code_model::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, TaggedCase, CaseId,
|
|
!CodeMap, !Info, !Dummy) :-
|
|
TaggedCase = tagged_case(_, _, CaseId, _),
|
|
gen_tagged_case_code_for_string_switch(CodeModel, TaggedCase,
|
|
!CodeMap, !Info).
|
|
|
|
:- pred gen_tagged_case_code_for_string_switch(code_model::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, TaggedCase,
|
|
!CodeMap, !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(ml_string_type)),
|
|
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 = mercury_type_to_mlds_type(ModuleInfo, string_type),
|
|
MLDS_IntType = mlds_native_int_type,
|
|
|
|
( 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, SlotVarLval, _StringVarLval, _MaybeStopLoopLval,
|
|
_FailStmts, Defns),
|
|
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(ml_string_type)),
|
|
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 = mercury_type_to_mlds_type(ModuleInfo, string_type),
|
|
MLDS_IntType = mlds_native_int_type,
|
|
|
|
( 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, SlotVarLval, _StringVarLval, _MaybeStopLoopLval,
|
|
_FailStmts, Defns),
|
|
|
|
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(ml_string_type)),
|
|
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_lval,
|
|
mhsi_string_var :: mlds_lval,
|
|
mhsi_stop_loop_var :: maybe(mlds_lval),
|
|
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_native_int_type,
|
|
% We never need to trace ints.
|
|
SlotVarDefn = ml_gen_mlds_var_decl(SlotVarName, SlotVarType,
|
|
gc_no_stmt, Context),
|
|
SlotVarLval = ml_local_var(SlotVarName, SlotVarType),
|
|
|
|
ml_gen_info_new_aux_var_name(mcav_str, StringVarName, !Info),
|
|
StringVarType = ml_string_type,
|
|
% 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),
|
|
StringVarLval = ml_local_var(StringVarName, StringVarType),
|
|
|
|
AlwaysDefns = [SlotVarDefn, StringVarDefn],
|
|
ml_should_use_stop_loop(Context, LoopPresent,
|
|
MaybeStopLoopLval, 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, SlotVarLval, StringVarLval, MaybeStopLoopLval,
|
|
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, SlotVarLval, StringVarLval,
|
|
MaybeStopLoopVarLval, FailStmts, _Defns),
|
|
SlotVarRval = ml_lval(SlotVarLval),
|
|
StringVarRval = ml_lval(StringVarLval),
|
|
SlotVarType = mlds_native_int_type,
|
|
StringVarType = ml_string_type,
|
|
|
|
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(std_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(0),
|
|
ml_vector_common_row_addr(VectorCommon, SlotVarRval),
|
|
StringFieldId, StringVarType, StructType))),
|
|
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(0),
|
|
ml_vector_common_row_addr(VectorCommon, SlotVarRval),
|
|
NextSlotFieldId, SlotVarType, StructType))),
|
|
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, 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,
|
|
Context, Defns, Stmts, !Info) :-
|
|
ml_gen_string_binary_switch_search_vars(CodeModel, CanFail,
|
|
Context, VarRval, BinarySearchInfo, !Info),
|
|
BinarySearchInfo = ml_binary_search_info(_CodeModel,
|
|
_VarRval, _LoVarLval, _HiVarLval, MidVarLval, _ResultVarLval,
|
|
_MaybeStopLoopVarLval, _FailStmts, Defns),
|
|
|
|
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_CaseNumType = mlds_native_int_type,
|
|
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),
|
|
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_native_int_type,
|
|
ml_lval(ml_field(yes(0),
|
|
ml_vector_common_row_addr(VectorCommon, ml_lval(MidVarLval)),
|
|
CaseNumFieldId, MLDS_StringType, StructType)),
|
|
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,
|
|
_LoVarLval, _HiVarLval, MidVarLval, _ResultVarLval, _MaybeStopLoopLval,
|
|
_FailStmts, Defns),
|
|
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 = mercury_type_to_mlds_type(ModuleInfo, string_type),
|
|
MLDS_IntType = mlds_native_int_type,
|
|
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,
|
|
_LoVarLval, _HiVarLval, MidVarLval, _ResultVarLval, _MaybeStopLoopLval,
|
|
_FailStmts, Defns),
|
|
|
|
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_lval,
|
|
mbsi_hi_var :: mlds_lval,
|
|
mbsi_mid_var :: mlds_lval,
|
|
mbsi_result_var :: mlds_lval,
|
|
mbsi_stop_loop_var :: maybe(mlds_lval),
|
|
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_native_int_type,
|
|
ResultType = mlds_native_int_type,
|
|
% 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),
|
|
LoVarLval = ml_local_var(LoVarName, IndexType),
|
|
|
|
ml_gen_info_new_aux_var_name(mcav_hi, HiVarName, !Info),
|
|
HiVarDefn = ml_gen_mlds_var_decl(HiVarName, IndexType,
|
|
IndexGCStmt, Context),
|
|
HiVarLval = ml_local_var(HiVarName, IndexType),
|
|
|
|
ml_gen_info_new_aux_var_name(mcav_mid, MidVarName, !Info),
|
|
MidVarDefn = ml_gen_mlds_var_decl(MidVarName, IndexType,
|
|
IndexGCStmt, Context),
|
|
MidVarLval = ml_local_var(MidVarName, IndexType),
|
|
|
|
ml_gen_info_new_aux_var_name(mcav_result, ResultVarName, !Info),
|
|
ResultVarDefn = ml_gen_mlds_var_decl(ResultVarName, ResultType,
|
|
ResultGCStmt, Context),
|
|
ResultVarLval = ml_local_var(ResultVarName, ResultType),
|
|
|
|
AlwaysDefns = [LoVarDefn, HiVarDefn, MidVarDefn, ResultVarDefn],
|
|
ml_should_use_stop_loop(Context, yes,
|
|
MaybeStopLoopLval, 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,
|
|
LoVarLval, HiVarLval, MidVarLval, ResultVarLval, MaybeStopLoopLval,
|
|
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,
|
|
LoVarLval, HiVarLval, MidVarLval, ResultVarLval, MaybeStopLoopVarLval,
|
|
FailStmts, _Defns),
|
|
LoVarRval = ml_lval(LoVarLval),
|
|
HiVarRval = ml_lval(HiVarLval),
|
|
MidVarRval = ml_lval(MidVarLval),
|
|
ResultVarRval = ml_lval(ResultVarLval),
|
|
|
|
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(
|
|
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(0),
|
|
ml_vector_common_row_addr(VectorCommon, MidVarRval),
|
|
StringFieldId, MLDS_StringType, StructType)))),
|
|
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),
|
|
|
|
(
|
|
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),
|
|
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),
|
|
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_lval)::out, list(mlds_local_var_defn)::out,
|
|
ml_gen_info::in, ml_gen_info::out) is det.
|
|
|
|
ml_should_use_stop_loop(Context, LoopPresent,
|
|
MaybeStopLoopLval, 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,
|
|
MaybeStopLoopLval = 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_native_int_type,
|
|
% 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),
|
|
StopLoopVarLval = ml_local_var(StopLoopVarName, StopLoopType),
|
|
MaybeStopLoopLval = yes(StopLoopVarLval),
|
|
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.
|
|
%---------------------------------------------------------------------------%
|