Files
mercury/compiler/ml_string_switch.m
Zoltan Somogyi 7ca1a07296 Allow the MLDS backend to generate indexing switches (switches implemented
Estimated hours taken: 16
Branches: main

Allow the MLDS backend to generate indexing switches (switches implemented
more efficiently than just a if-then-else chain) for strings even if the target
language does not support gotos.

Previously, we use always used gotos to break out of search loops
after we found a match:

    do {
        if (we have a match) {
            ... handle the match ...
            goto end
        } else {
            ... handle nonmatches ...
        }
    } while (loop should continue);
    maybe some code to handle the failure of the search
end:

Now, if the "maybe some code" is empty, we prefer to use break statements
if the target language supports this:

    do {
        if (we have a match) {
            ... handle the match ...
            break;
        } else {
            ... handle nonmatches ...
        }
    } while (loop should continue)

If we cannot use either gotos or break statements, we instead use
a boolean variable named "stop_loop":

    stop_loop = 0;
    do {
        if (we have a match) {
            ... handle the match ...
            stop_loop = 1;
        } else {
            ... handle nonmatches ...
        }
    } while (stop_loop == 0 && loop should continue)
    if (stop_loop == 0) {
    	maybe some code to handle the failure of the search
    }

We omit the final if statement if the then-part would be empty.

The break method generates the smallest code, followed by the goto code.
I don't have information on speed, since we don't have a benchmark that
runs long enough, and the compiler itself does not spend any significant
amount of time on string switches. Probably the break method is also the
fastest, simply because it leaves the code looking most like normal C code.
(Some optimizations are harder to apply to code containing gotos, and some
optimizer writers do not bother.)

For C, we now normally prefer to generate code using the second method
(breaks), if we can, though normally "maybe some code" is not empty,
in which case we use the first method (goto).

However, if the value of the --experiment option is set to "use_stop_loop",
we always use the third method, and if it is set to "use_end_label", we always
use the first, even when we could use the second. This allow us to test all
three approaches using the C back end.

With backends that support neither gotos nor break, we always use the third
method (stop_loop).

With backends that don't support gotos but do support breaks, we also always
use the third method. This is because trying to use the second method would
require us to commit to not creating the stop_loop variable BEFORE we know
that the "maybe some code to handle the failure of the search" is empty,
and if it isn't empty, then we don't have the goto method to fall back on.

compiler/ml_string_switch.m:
	Make the change described above. Where possible, make the required
	change not to the original code, but to a version in which common
	parts have been factored out. (Previously, the duplicated code was
	small; now, it would be big.)

compiler/ml_target_util.m:
	A new module containing existing functions that test various properties
	of the target language. Keeping some of those functions in their
	original modules would have introduced a circular dependency.

compiler/ml_switch_gen.m:
	Enable the new functionality by removing the tests that previously
	prevented the compiler from using indexing switches on strings
	if the target language did not support gotos.

	Remove the code moved to ml_target_util.m.

compiler/ml_optimize.m:
compiler/ml_unify_gen.m:
	Remove the code moved to ml_target_util.m.

compiler/ml_backend.m:
compiler/notes/compiler_design.m:
	Add the new module.

compiler/ml_proc_gen.m:
	Delete a predicate that hasn't been used for a long time.

tools/makebatch:
	Fix an old pair of typos.
2011-08-15 06:23:20 +00:00

1713 lines
73 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 either
% - 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, but should use fewer comparisons,
% so it is preferred for bigger tables.
%
% 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.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.prog_data.
:- import_module list.
:- pred ml_generate_string_hash_jump_switch(list(tagged_case)::in,
prog_var::in, code_model::in, can_fail::in, prog_context::in,
list(mlds_defn)::out, list(statement)::out,
ml_gen_info::in, ml_gen_info::out) is det.
:- pred ml_generate_string_hash_lookup_switch(prog_var::in,
ml_lookup_switch_info(string)::in,
code_model::in, can_fail::in, prog_context::in,
list(mlds_defn)::out, list(statement)::out,
ml_gen_info::in, ml_gen_info::out) is det.
:- pred ml_generate_string_binary_jump_switch(list(tagged_case)::in,
prog_var::in, code_model::in, can_fail::in, prog_context::in,
list(mlds_defn)::out, list(statement)::out,
ml_gen_info::in, ml_gen_info::out) is det.
:- pred ml_generate_string_binary_lookup_switch(prog_var::in,
ml_lookup_switch_info(string)::in,
code_model::in, can_fail::in, prog_context::in,
list(mlds_defn)::out, list(statement)::out,
ml_gen_info::in, ml_gen_info::out) is det.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
:- implementation.
:- import_module backend_libs.builtin_ops.
:- import_module backend_libs.switch_util.
:- import_module hlds.hlds_data.
:- import_module hlds.hlds_module.
:- 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.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
ml_generate_string_hash_jump_switch(Cases, Var, CodeModel, CanFail, Context,
Defns, Statements, !Info) :-
MLDS_Context = mlds_make_context(Context),
gen_tagged_case_codes_for_string_switch(CodeModel, Cases, StrsCaseNums,
map.init, CodeMap, !Info),
% Compute the hash table.
construct_string_hash_cases(StrsCaseNums, 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,
( NumCollisions = 0 ->
MLDS_ArgTypes = [MLDS_StringType],
LoopPresent = no
;
MLDS_ArgTypes = [MLDS_StringType, MLDS_IntType],
LoopPresent = yes
),
ml_gen_string_hash_switch_search_vars(CodeModel, CanFail, LoopPresent,
Context, MLDS_Context, Var, HashSearchInfo, !Info),
HashSearchInfo = ml_hash_search_info(_CodeModel, _LoopPresent,
_Context, _VarRval, SlotVarLval, _StringVarLval,
_MaybeStopLoopLval, _FailStatements, Defns),
ml_gen_info_get_global_data(!.Info, GlobalData0),
ml_gen_static_vector_type(MLDS_ModuleName, MLDS_Context, Target,
MLDS_ArgTypes, StructTypeNum, StructType, FieldIds,
GlobalData0, GlobalData1),
( NumCollisions = 0 ->
( FieldIds = [StringFieldIdPrime] ->
StringFieldId = StringFieldIdPrime,
MaybeNextSlotFieldId = no
;
unexpected($module, $pred, "bad FieldIds")
)
;
( FieldIds = [StringFieldIdPrime, NextSlotFieldIdPrime] ->
StringFieldId = StringFieldIdPrime,
MaybeNextSlotFieldId = yes(NextSlotFieldIdPrime)
;
unexpected($module, $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),
ml_simplify_switch(SwitchStmt0, MLDS_Context, SwitchStatement, !Info),
FoundMatchComment = "we found a match; dispatch to the corresponding code",
FoundMatchStatements = [
statement(ml_stmt_atomic(comment(FoundMatchComment)), MLDS_Context),
SwitchStatement
],
InitialComment = "hashed string jump switch",
ml_gen_string_hash_switch_search(MLDS_Context, InitialComment,
HashSearchInfo, HashOp, VectorCommon, StructType,
StringFieldId, MaybeNextSlotFieldId, HashMask,
[], FoundMatchStatements, Statements, !Info).
%-----------------------------------------------------------------------------%
:- pred gen_tagged_case_codes_for_string_switch(code_model::in,
list(tagged_case)::in, assoc_list(string, int)::out,
map(int, statement)::in, map(int, statement)::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],
!:StrsCaseNums, !CodeMap, !Info) :-
gen_tagged_case_code_for_string_switch(CodeModel,
TaggedCase, CaseNum, !CodeMap, !Info),
gen_tagged_case_codes_for_string_switch(CodeModel,
TaggedCases, !:StrsCaseNums, !CodeMap, !Info),
TaggedCase = tagged_case(MainTaggedConsId, OtherTaggedConsIds, _, _),
add_to_strs_casenums(CaseNum, MainTaggedConsId, !StrsCaseNums),
list.foldl(add_to_strs_casenums(CaseNum), OtherTaggedConsIds,
!StrsCaseNums).
:- pred add_to_strs_casenums(int::in, tagged_cons_id::in,
assoc_list(string, int)::in, assoc_list(string, int)::out) is det.
add_to_strs_casenums(CaseNum, TaggedConsId, !StrsCaseNums) :-
TaggedConsId = tagged_cons_id(_ConsId, ConsTag),
( ConsTag = string_tag(String) ->
!:StrsCaseNums = [String - CaseNum | !.StrsCaseNums]
;
unexpected($module, $pred, "non-string tag")
).
:- pred gen_tagged_case_code_for_string_switch_dummy(code_model::in,
tagged_case::in, int::out,
map(int, statement)::in, map(int, statement)::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, CaseNum,
!CodeMap, !Info, !Dummy) :-
gen_tagged_case_code_for_string_switch(CodeModel, TaggedCase, CaseNum,
!CodeMap, !Info).
:- pred gen_tagged_case_code_for_string_switch(code_model::in, tagged_case::in,
int::out, map(int, statement)::in, map(int, statement)::out,
ml_gen_info::in, ml_gen_info::out) is det.
gen_tagged_case_code_for_string_switch(CodeModel, TaggedCase, CaseNum,
!CodeMap, !Info) :-
TaggedCase = tagged_case(MainTaggedConsId, OtherTaggedConsIds,
CaseNum, Goal),
ml_gen_goal_as_branch_block(CodeModel, Goal, GoalStatement, !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
% CaseStatement 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),
MLDS_Context = mlds_make_context(Context),
Comment = statement(ml_stmt_atomic(comment(CommentString)),
MLDS_Context),
CaseStatement = statement(ml_stmt_block([], [Comment, GoalStatement]),
MLDS_Context),
map.det_insert(CaseNum, CaseStatement, !CodeMap).
:- func gen_string_switch_case_comment(tagged_cons_id) = string.
gen_string_switch_case_comment(TaggedConsId) = String :-
TaggedConsId = tagged_cons_id(_ConsId, ConsTag),
( ConsTag = string_tag(ConsString) ->
String = """" ++ ConsString ++ """"
;
unexpected($module, $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(int, hash_slots).
:- pred ml_gen_string_hash_jump_slots(int::in, int::in,
map(int, string_hash_slot(int))::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) :-
( Slot = TableSize ->
true
;
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(int))::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) :-
( map.search(HashSlotMap, Slot, HashSlotMapEntry) ->
HashSlotMapEntry = string_hash_slot(String, Next, CaseNum),
StringRval = ml_const(mlconst_string(String)),
NextSlotRval = ml_const(mlconst_int(Next)),
( map.search(!.RevMap, CaseNum, OldEntry) ->
OldEntry = hash_slots(OldFirstSlot, OldLaterSlots),
NewEntry = hash_slots(OldFirstSlot, [Slot | OldLaterSlots]),
map.det_update(CaseNum, NewEntry, !RevMap)
;
NewEntry = hash_slots(Slot, []),
map.det_insert(CaseNum, NewEntry, !RevMap)
)
;
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(int, statement)::in,
assoc_list(int, 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 = CaseNum - 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, CaseNum, CaseStatement),
Case = mlds_switch_case(FirstMatchCond, LaterMatchConds, CaseStatement),
!: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))).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
ml_generate_string_hash_lookup_switch(Var, LookupSwitchInfo, CodeModel,
CanFail, Context, Defns, Statements, !Info) :-
LookupSwitchInfo = ml_lookup_switch_info(CaseConsts, OutVars, OutTypes),
(
CaseConsts = all_one_soln(CaseValues),
ml_generate_string_hash_simple_lookup_switch(Var,
CodeModel, CanFail, CaseValues, OutVars, OutTypes, Context,
Defns, Statements, !Info)
;
CaseConsts = some_several_solns(CaseSolns, _Unit),
expect(unify(CodeModel, model_non), $module, $pred,
"CodeModel != model_non"),
ml_generate_string_hash_several_soln_lookup_switch(Var,
CodeModel, CanFail, CaseSolns, OutVars, OutTypes, Context,
Defns, Statements, !Info)
).
%-----------------------------------------------------------------------------%
:- pred ml_generate_string_hash_simple_lookup_switch(prog_var::in,
code_model::in, can_fail::in,
assoc_list(string, list(mlds_rval))::in,
list(prog_var)::in, list(mlds_type)::in, prog_context::in,
list(mlds_defn)::out, list(statement)::out,
ml_gen_info::in, ml_gen_info::out) is det.
ml_generate_string_hash_simple_lookup_switch(Var, CodeModel, CanFail,
CaseValues, OutVars, OutTypes, Context, Defns, Statements, !Info) :-
MLDS_Context = mlds_make_context(Context),
% 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,
( NumCollisions = 0 ->
MLDS_ArgTypes = [MLDS_StringType | OutTypes],
LoopPresent = no
;
MLDS_ArgTypes = [MLDS_StringType, MLDS_IntType | OutTypes],
LoopPresent = yes
),
ml_gen_string_hash_switch_search_vars(CodeModel, CanFail, LoopPresent,
Context, MLDS_Context, Var, HashSearchInfo, !Info),
HashSearchInfo = ml_hash_search_info(_CodeModel, _LoopPresent,
_Context, _VarRval, SlotVarLval, _StringVarLval, _MaybeStopLoopLval,
_FailStatements, Defns),
SlotVarRval = ml_lval(SlotVarLval),
ml_gen_info_get_global_data(!.Info, GlobalData0),
ml_gen_static_vector_type(MLDS_ModuleName, MLDS_Context, Target,
MLDS_ArgTypes, StructTypeNum, StructType, FieldIds,
GlobalData0, GlobalData1),
( NumCollisions = 0 ->
( FieldIds = [StringFieldIdPrime | OutFieldIdsPrime] ->
StringFieldId = StringFieldIdPrime,
OutFieldIds = OutFieldIdsPrime,
MaybeNextSlotFieldId = no
;
unexpected($module, $pred, "bad FieldIds")
)
;
(
FieldIds =
[StringFieldIdPrime, NextSlotFieldIdPrime | OutFieldIdsPrime]
->
StringFieldId = StringFieldIdPrime,
OutFieldIds = OutFieldIdsPrime,
MaybeNextSlotFieldId = yes(NextSlotFieldIdPrime)
;
unexpected($module, $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, MLDS_Context, LookupStatements, !Info),
FoundMatchComment = "we found a match; look up the results",
FoundMatchCommentStatement = statement(
ml_stmt_atomic(comment(FoundMatchComment)), MLDS_Context),
(
CodeModel = model_det,
MatchStatements = [FoundMatchCommentStatement | LookupStatements]
;
CodeModel = model_semi,
ml_gen_set_success(!.Info, ml_const(mlconst_true), Context,
SetSuccessTrueStatement),
MatchStatements = [FoundMatchCommentStatement | LookupStatements] ++
[SetSuccessTrueStatement]
;
CodeModel = model_non,
unexpected($module, $pred, "model_non")
),
InitialComment = "hashed string simple lookup switch",
ml_gen_string_hash_switch_search(MLDS_Context, InitialComment,
HashSearchInfo, HashOp, VectorCommon, StructType,
StringFieldId, MaybeNextSlotFieldId, HashMask,
[], MatchStatements, Statements, !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) :-
( Slot = TableSize ->
true
;
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) :-
( map.search(HashSlotMap, Slot, HashSlotMapEntry) ->
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)
;
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(prog_var::in,
code_model::in, can_fail::in,
assoc_list(string, soln_consts(mlds_rval))::in,
list(prog_var)::in, list(mlds_type)::in, prog_context::in,
list(mlds_defn)::out, list(statement)::out,
ml_gen_info::in, ml_gen_info::out) is det.
ml_generate_string_hash_several_soln_lookup_switch(Var, CodeModel, CanFail,
CaseSolns, OutVars, OutTypes, Context, Defns, Statements, !Info) :-
MLDS_Context = mlds_make_context(Context),
make_several_soln_lookup_vars(MLDS_Context, SeveralSolnLookupVars, !Info),
SeveralSolnLookupVars = ml_several_soln_lookup_vars(NumLaterSolnsVarLval,
LaterSlotVarLval, LimitVarLval,
LimitAssignStatement, IncrLaterSlotVarStatement, MatchDefns),
LaterSlotVarRval = ml_lval(LaterSlotVarLval),
LimitVarRval = ml_lval(LimitVarLval),
% 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,
( NumCollisions = 0 ->
FirstSolnFieldTypes = [MLDS_StringType,
MLDS_IntType, MLDS_IntType | OutTypes],
LoopPresent = no
;
FirstSolnFieldTypes = [MLDS_StringType, MLDS_IntType,
MLDS_IntType, MLDS_IntType | OutTypes],
LoopPresent = yes
),
ml_gen_string_hash_switch_search_vars(CodeModel, CanFail, LoopPresent,
Context, MLDS_Context, Var, HashSearchInfo, !Info),
HashSearchInfo = ml_hash_search_info(_CodeModel, _LoopPresent,
_Context, _VarRval, SlotVarLval, _StringVarLval, _MaybeStopLoopLval,
_FailStatements, Defns),
SlotVarRval = ml_lval(SlotVarLval),
ml_gen_info_get_global_data(!.Info, GlobalData0),
ml_gen_static_vector_type(MLDS_ModuleName, MLDS_Context, Target,
FirstSolnFieldTypes, FirstSolnStructTypeNum, FirstSolnStructType,
FirstSolnFieldIds, GlobalData0, GlobalData1),
ml_gen_static_vector_type(MLDS_ModuleName, MLDS_Context, Target,
OutTypes, LaterSolnStructTypeNum, LaterSolnStructType,
LaterSolnFieldIds, GlobalData1, GlobalData2),
( NumCollisions = 0 ->
(
FirstSolnFieldIds = [StringFieldIdPrime,
NumLaterSolnsFieldIdPrime, FirstLaterSolnRowFieldIdPrime
| OutFieldIdsPrime]
->
StringFieldId = StringFieldIdPrime,
NumLaterSolnsFieldId = NumLaterSolnsFieldIdPrime,
FirstLaterSolnRowFieldId = FirstLaterSolnRowFieldIdPrime,
OutFieldIds = OutFieldIdsPrime,
MaybeNextSlotFieldId = no
;
unexpected($module, $pred, "bad FieldIds")
)
;
(
FirstSolnFieldIds =
[StringFieldIdPrime, NextSlotFieldIdPrime,
NumLaterSolnsFieldIdPrime, FirstLaterSolnRowFieldIdPrime
| OutFieldIdsPrime]
->
StringFieldId = StringFieldIdPrime,
NumLaterSolnsFieldId = NumLaterSolnsFieldIdPrime,
FirstLaterSolnRowFieldId = FirstLaterSolnRowFieldIdPrime,
OutFieldIds = OutFieldIdsPrime,
MaybeNextSlotFieldId = yes(NextSlotFieldIdPrime)
;
unexpected($module, $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_generate_field_assign(NumLaterSolnsVarLval, MLDS_IntType,
NumLaterSolnsFieldId, FirstSolnVectorCommon, FirstSolnStructType,
SlotVarRval, MLDS_Context, NumLaterSolnsAssignStatement, !Info),
ml_generate_field_assign(LaterSlotVarLval, MLDS_IntType,
FirstLaterSolnRowFieldId, FirstSolnVectorCommon, FirstSolnStructType,
SlotVarRval, MLDS_Context, LaterSlotVarAssignStatement, !Info),
ml_generate_field_assigns(OutVars, OutTypes, OutFieldIds,
FirstSolnVectorCommon, FirstSolnStructType,
SlotVarRval, MLDS_Context, FirstSolnLookupStatements, !Info),
ml_generate_field_assigns(OutVars, OutTypes, LaterSolnFieldIds,
LaterSolnVectorCommon, LaterSolnStructType,
LaterSlotVarRval, MLDS_Context, LaterSolnLookupStatements, !Info),
ml_gen_call_current_success_cont(Context, CallContStatement, !Info),
FirstLookupSucceedStmt = ml_stmt_block([],
FirstSolnLookupStatements ++ [CallContStatement]),
FirstLookupSucceedStatement =
statement(FirstLookupSucceedStmt, MLDS_Context),
LaterLookupSucceedStmt = ml_stmt_block([],
LaterSolnLookupStatements ++
[CallContStatement, IncrLaterSlotVarStatement]),
LaterLookupSucceedStatement =
statement(LaterLookupSucceedStmt, MLDS_Context),
MoreSolnsLoopCond = ml_binop(int_lt, LaterSlotVarRval, LimitVarRval),
MoreSolnsLoopStmt = ml_stmt_while(may_loop_zero_times, MoreSolnsLoopCond,
LaterLookupSucceedStatement),
MoreSolnsLoopStatement = statement(MoreSolnsLoopStmt, MLDS_Context),
SuccessStatements = [NumLaterSolnsAssignStatement,
FirstLookupSucceedStatement, LaterSlotVarAssignStatement,
LimitAssignStatement, MoreSolnsLoopStatement],
InitialComment = "hashed string several_soln lookup switch",
ml_gen_string_hash_switch_search(MLDS_Context, InitialComment,
HashSearchInfo, HashOp, FirstSolnVectorCommon,
FirstSolnStructType, StringFieldId, MaybeNextSlotFieldId, HashMask,
MatchDefns, SuccessStatements, Statements, !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) :-
( Slot = TableSize ->
true
;
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) :-
( map.search(HashSlotMap, Slot, HashSlotMapEntry) ->
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
)
;
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(statement),
mhsi_defns :: list(mlds_defn)
).
:- pred ml_gen_string_hash_switch_search_vars(code_model::in, can_fail::in,
bool::in, prog_context::in, mlds_context::in, prog_var::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, MLDS_Context, Var, HashSearchInfo, !Info) :-
ml_gen_var(!.Info, Var, VarLval),
VarRval = ml_lval(VarLval),
% Generate the following local variable declarations:
% int slot;
% MR_String str;
ml_gen_info_new_aux_var_name("slot", SlotVar, !Info),
SlotVarType = mlds_native_int_type,
% We never need to trace ints.
SlotVarDefn = ml_gen_mlds_var_decl(mlds_data_var(SlotVar), SlotVarType,
gc_no_stmt, MLDS_Context),
ml_gen_var_lval(!.Info, SlotVar, SlotVarType, SlotVarLval),
ml_gen_info_new_aux_var_name("str", StringVar, !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(mlds_data_var(StringVar),
StringVarType, gc_no_stmt, MLDS_Context),
ml_gen_var_lval(!.Info, StringVar, StringVarType, StringVarLval),
AlwaysDefns = [SlotVarDefn, StringVarDefn],
ml_should_use_stop_loop(MLDS_Context, LoopPresent,
MaybeStopLoopLval, StopLoopVarDefns, !Info),
Defns = AlwaysDefns ++ StopLoopVarDefns,
% Generate the code for when the lookup fails.
ml_gen_maybe_switch_failure(CodeModel, CanFail, Context, FailStatements,
!Info),
HashSearchInfo = ml_hash_search_info(CodeModel, LoopPresent, Context,
VarRval, SlotVarLval, StringVarLval, MaybeStopLoopLval,
FailStatements, Defns).
:- pred ml_gen_string_hash_switch_search(mlds_context::in, 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_defn)::in, list(statement)::in, list(statement)::out,
ml_gen_info::in, ml_gen_info::out) is det.
ml_gen_string_hash_switch_search(MLDS_Context, InitialComment,
HashSearchInfo, HashOp, VectorCommon, StructType,
StringFieldId, MaybeNextSlotFieldId, HashMask,
MatchDefns, MatchStatements, Statements, !Info) :-
HashSearchInfo = ml_hash_search_info(CodeModel, LoopPresent,
Context, VarRval, SlotVarLval, StringVarLval,
MaybeStopLoopVarLval, FailStatements, _Defns),
SlotVarRval = ml_lval(SlotVarLval),
StringVarRval = ml_lval(StringVarLval),
SlotVarType = mlds_native_int_type,
StringVarType = ml_string_type,
ml_wrap_loop_break(CodeModel, LoopPresent,
MLDS_Context, MaybeStopLoopVarLval,
MatchDefns, MatchStatements, FailStatements,
SetupForFailStatements, SuccessStatement, AfterStatements, !Info),
InitialCommentStatement =
statement(ml_stmt_atomic(comment(InitialComment)), MLDS_Context),
PrepareForMatchStatements = [
statement(ml_stmt_atomic(comment(
"compute the hash value of the input string")), MLDS_Context),
statement(
ml_stmt_atomic(assign(SlotVarLval,
ml_binop(bitwise_and,
ml_unop(std_unop(HashOp), VarRval),
ml_const(mlconst_int(HashMask))))),
MLDS_Context)
],
FoundMatchCond =
ml_binop(logical_and,
ml_binop(ne, StringVarRval, ml_const(mlconst_null(StringVarType))),
ml_binop(str_eq, StringVarRval, VarRval)
),
LookForMatchPrepareStatements = [
statement(ml_stmt_atomic(comment(
"lookup the string for this hash slot")), MLDS_Context),
statement(
ml_stmt_atomic(assign(StringVarLval,
ml_lval(ml_field(yes(0),
ml_vector_common_row(VectorCommon, SlotVarRval),
StringFieldId, StringVarType, StructType)))),
MLDS_Context),
statement(ml_stmt_atomic(comment("did we find a match?")),
MLDS_Context)
],
SlotTest = ml_binop(int_ge, SlotVarRval, ml_const(mlconst_int(0))),
(
MaybeStopLoopVarLval = no,
InitStopLoopVarStatements = [],
InitSuccessStatements = [],
LoopTest = SlotTest
;
MaybeStopLoopVarLval = yes(StopLoopVarLval),
InitStopLoopVarStatement = statement(ml_stmt_atomic(
assign(StopLoopVarLval, ml_const(mlconst_int(0)))),
MLDS_Context),
InitStopLoopVarStatements = [InitStopLoopVarStatement],
(
CodeModel = model_det,
% If the switch is model_det, the value of `succeeded'
% is irrelevant, as it will not be consulted.
InitSuccessStatements = []
;
CodeModel = model_semi,
ml_gen_set_success(!.Info, ml_const(mlconst_false), Context,
InitSuccessStatement),
InitSuccessStatements = [InitSuccessStatement]
;
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.
InitSuccessStatements = []
),
StopLoopTest = ml_binop(eq,
ml_lval(StopLoopVarLval), ml_const(mlconst_int(0))),
LoopTest = ml_binop(logical_and, StopLoopTest, SlotTest)
),
(
MaybeNextSlotFieldId = yes(NextSlotFieldId),
NoMatchStatement = statement(ml_stmt_block([], [
statement(ml_stmt_atomic(comment(
"no match yet, so get next slot in hash chain")),
MLDS_Context),
statement(
ml_stmt_atomic(assign(SlotVarLval,
ml_lval(ml_field(yes(0),
ml_vector_common_row(VectorCommon, SlotVarRval),
NextSlotFieldId, SlotVarType, StructType)))),
MLDS_Context)
]), MLDS_Context),
LookForMatchStatement = statement(
ml_stmt_if_then_else(FoundMatchCond, SuccessStatement,
yes(NoMatchStatement)),
MLDS_Context),
LoopBody = statement(ml_stmt_block([],
LookForMatchPrepareStatements ++ [LookForMatchStatement]),
MLDS_Context),
LoopStatements = [
statement(ml_stmt_atomic(comment("hash chain loop")),
MLDS_Context),
statement(ml_stmt_while(loop_at_least_once, LoopTest, LoopBody),
MLDS_Context)
],
SearchStatements = PrepareForMatchStatements ++
InitStopLoopVarStatements ++ InitSuccessStatements ++
LoopStatements,
Statements = [InitialCommentStatement | SetupForFailStatements] ++
SearchStatements ++ AfterStatements
;
MaybeNextSlotFieldId = no,
NoLoopCommentStatement = statement(ml_stmt_atomic(
comment("no collisions; no hash chain loop")), MLDS_Context),
LookForMatchStatement = statement(
ml_stmt_if_then_else(FoundMatchCond, SuccessStatement, no),
MLDS_Context),
SearchStatements = PrepareForMatchStatements ++
InitSuccessStatements ++
[NoLoopCommentStatement | LookForMatchPrepareStatements] ++
[LookForMatchStatement],
Statements = [InitialCommentStatement | SearchStatements] ++
AfterStatements
).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
ml_generate_string_binary_jump_switch(Cases, Var, CodeModel, CanFail, Context,
Defns, Statements, !Info) :-
MLDS_Context = mlds_make_context(Context),
ml_gen_string_binary_switch_search_vars(CodeModel, CanFail,
Context, MLDS_Context, Var, BinarySearchInfo, !Info),
BinarySearchInfo = ml_binary_search_info(_CodeModel,
_VarRval, _LoVarLval, _HiVarLval, MidVarLval, _ResultVarLval,
_MaybeStopLoopVarLval, _FailStatements, 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, MLDS_Context, Target,
MLDS_ArgTypes, StructTypeNum, StructType, FieldIds,
GlobalData0, GlobalData1),
ml_gen_info_set_global_data(GlobalData1, !Info),
( FieldIds = [StringFieldIdPrime, CaseNumFieldIdPrime] ->
StringFieldId = StringFieldIdPrime,
CaseNumFieldId = CaseNumFieldIdPrime
;
unexpected($module, $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(VectorCommon, ml_lval(MidVarLval)),
CaseNumFieldId, MLDS_StringType, StructType)),
mlds_switch_range(0, TableSize - 1), SwitchCases,
default_is_unreachable),
ml_simplify_switch(SwitchStmt0, MLDS_Context, SwitchStatement, !Info),
% Generate the code that searches the table.
InitialComment = "binary string jump switch",
ml_gen_string_binary_switch_search(MLDS_Context, InitialComment,
BinarySearchInfo, VectorCommon, TableSize, StructType, StringFieldId,
[], [SwitchStatement], Statements, !Info).
:- pred ml_gen_string_binary_jump_initializers(assoc_list(string, int)::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 - CaseNum | StrCaseNums],
StructType, !RevRowInitializers, !CurIndex) :-
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(StrCaseNums,
StructType, !RevRowInitializers, !CurIndex).
:- pred ml_gen_string_binary_jump_switch_arms(assoc_list(int, statement)::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([CaseNumStmt | CaseNumsStmts],
!SwitchCases) :-
CaseNumStmt = CaseNum - Statement,
MatchCond = match_value(ml_const(mlconst_int(CaseNum))),
SwitchCase = mlds_switch_case(MatchCond, [], Statement),
!:SwitchCases = [SwitchCase | !.SwitchCases],
ml_gen_string_binary_jump_switch_arms(CaseNumsStmts, !SwitchCases).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
ml_generate_string_binary_lookup_switch(Var, LookupSwitchInfo, CodeModel,
CanFail, Context, Defns, Statements, !Info) :-
MLDS_Context = mlds_make_context(Context),
LookupSwitchInfo = ml_lookup_switch_info(CaseConsts, OutVars, OutTypes),
(
CaseConsts = all_one_soln(CaseValues),
ml_generate_string_binary_simple_lookup_switch(Var,
CodeModel, CanFail, CaseValues, OutVars, OutTypes,
Context, MLDS_Context, Defns, Statements, !Info)
;
CaseConsts = some_several_solns(CaseSolns, _Unit),
expect(unify(CodeModel, model_non), $module, $pred,
"CodeModel != model_non"),
ml_generate_string_binary_several_soln_lookup_switch(Var,
CodeModel, CanFail, CaseSolns, OutVars, OutTypes,
Context, MLDS_Context, Defns, Statements, !Info)
).
%-----------------------------------------------------------------------------%
:- pred ml_generate_string_binary_simple_lookup_switch(prog_var::in,
code_model::in, can_fail::in,
assoc_list(string, list(mlds_rval))::in,
list(prog_var)::in, list(mlds_type)::in,
prog_context::in, mlds_context::in,
list(mlds_defn)::out, list(statement)::out,
ml_gen_info::in, ml_gen_info::out) is det.
ml_generate_string_binary_simple_lookup_switch(Var, CodeModel, CanFail,
CaseValues0, OutVars, OutTypes, Context, MLDS_Context,
Defns, Statements, !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, MLDS_Context, Target,
MLDS_ArgTypes, StructTypeNum, StructType, FieldIds,
GlobalData0, GlobalData1),
( FieldIds = [StringFieldIdPrime | OutFieldIdsPrime] ->
StringFieldId = StringFieldIdPrime,
OutFieldIds = OutFieldIdsPrime
;
unexpected($module, $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, MLDS_Context, Var, BinarySearchInfo, !Info),
BinarySearchInfo = ml_binary_search_info(_CodeModel, _VarRval,
_LoVarLval, _HiVarLval, MidVarLval, _ResultVarLval, _MaybeStopLoopLval,
_FailStatements, Defns),
MidVarRval = ml_lval(MidVarLval),
ml_generate_field_assigns(OutVars, OutTypes, OutFieldIds,
VectorCommon, StructType, MidVarRval, MLDS_Context,
GetArgStatements, !Info),
(
CodeModel = model_det,
MatchStatements = GetArgStatements
;
CodeModel = model_semi,
ml_gen_set_success(!.Info, ml_const(mlconst_true), Context,
SetSuccessTrueStatement),
MatchStatements = GetArgStatements ++ [SetSuccessTrueStatement]
;
CodeModel = model_non,
unexpected($module, $pred, "model_non")
),
% Generate the code that searches the table.
InitialComment = "binary string simple lookup switch",
ml_gen_string_binary_switch_search(MLDS_Context, InitialComment,
BinarySearchInfo, VectorCommon, TableSize, StructType, StringFieldId,
[], MatchStatements, Statements, !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(prog_var::in,
code_model::in, can_fail::in,
assoc_list(string, soln_consts(mlds_rval))::in,
list(prog_var)::in, list(mlds_type)::in,
prog_context::in, mlds_context::in,
list(mlds_defn)::out, list(statement)::out,
ml_gen_info::in, ml_gen_info::out) is det.
ml_generate_string_binary_several_soln_lookup_switch(Var, CodeModel, CanFail,
CaseSolns0, OutVars, OutTypes, Context, MLDS_Context,
Defns, Statements, !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),
make_several_soln_lookup_vars(MLDS_Context, SeveralSolnLookupVars, !Info),
SeveralSolnLookupVars = ml_several_soln_lookup_vars(NumLaterSolnsVarLval,
LaterSlotVarLval, LimitVarLval,
LimitAssignStatement, IncrLaterSlotVarStatement, MatchDefns),
LaterSlotVarRval = ml_lval(LaterSlotVarLval),
LimitVarRval = ml_lval(LimitVarLval),
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, MLDS_Context, Target,
FirstSolnFieldTypes, FirstSolnStructTypeNum, FirstSolnStructType,
FirstSolnFieldIds, GlobalData0, GlobalData1),
ml_gen_static_vector_type(MLDS_ModuleName, MLDS_Context, Target,
LaterSolnFieldTypes, LaterSolnStructTypeNum, LaterSolnStructType,
LaterSolnFieldIds, GlobalData1, GlobalData2),
(
FirstSolnFieldIds = [StringFieldIdPrime, NumLaterSolnsFieldIdPrime,
FirstLaterRowFieldIdPrime | FirstSolnArgIdsPrime]
->
StringFieldId = StringFieldIdPrime,
NumLaterSolnsFieldId = NumLaterSolnsFieldIdPrime,
FirstLaterRowFieldId = FirstLaterRowFieldIdPrime,
FirstSolnArgIds = FirstSolnArgIdsPrime
;
unexpected($module, $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, MLDS_Context, Var, BinarySearchInfo, !Info),
BinarySearchInfo = ml_binary_search_info(_CodeModel, _VarRval,
_LoVarLval, _HiVarLval, MidVarLval, _ResultVarLval, _MaybeStopLoopLval,
_FailStatements, Defns),
MidVarRval = ml_lval(MidVarLval),
ml_generate_field_assign(NumLaterSolnsVarLval, MLDS_IntType,
NumLaterSolnsFieldId,
FirstSolnVectorCommon, FirstSolnStructType, MidVarRval,
MLDS_Context, NumLaterSolnsAssignStatement, !Info),
ml_generate_field_assign(LaterSlotVarLval, MLDS_IntType,
FirstLaterRowFieldId,
FirstSolnVectorCommon, FirstSolnStructType, MidVarRval,
MLDS_Context, LaterSlotVarAssignStatement, !Info),
ml_generate_field_assigns(OutVars, OutTypes, FirstSolnArgIds,
FirstSolnVectorCommon, FirstSolnStructType, MidVarRval,
MLDS_Context, FirstSolnLookupStatements, !Info),
ml_generate_field_assigns(OutVars, OutTypes, LaterSolnFieldIds,
LaterSolnVectorCommon, LaterSolnStructType, LaterSlotVarRval,
MLDS_Context, LaterSolnLookupStatements, !Info),
ml_gen_call_current_success_cont(Context, CallContStatement, !Info),
FirstLookupSucceedStmt = ml_stmt_block([],
FirstSolnLookupStatements ++ [CallContStatement]),
FirstLookupSucceedStatement =
statement(FirstLookupSucceedStmt, MLDS_Context),
LaterLookupSucceedStmt = ml_stmt_block([],
LaterSolnLookupStatements ++
[CallContStatement, IncrLaterSlotVarStatement]),
LaterLookupSucceedStatement =
statement(LaterLookupSucceedStmt, MLDS_Context),
MoreSolnsLoopCond = ml_binop(int_lt, LaterSlotVarRval, LimitVarRval),
MoreSolnsLoopStmt = ml_stmt_while(may_loop_zero_times, MoreSolnsLoopCond,
LaterLookupSucceedStatement),
MoreSolnsLoopStatement = statement(MoreSolnsLoopStmt, MLDS_Context),
MatchStatements = [
NumLaterSolnsAssignStatement, FirstLookupSucceedStatement,
LaterSlotVarAssignStatement, LimitAssignStatement,
MoreSolnsLoopStatement
],
% Generate the code that searches the table.
InitialComment = "binary string several soln lookup switch",
ml_gen_string_binary_switch_search(MLDS_Context, InitialComment,
BinarySearchInfo, FirstSolnVectorCommon,FirstSolnTableSize,
FirstSolnStructType, StringFieldId,
MatchDefns, MatchStatements, Statements, !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(statement),
mbsi_defns :: list(mlds_defn)
).
:- pred ml_gen_string_binary_switch_search_vars(code_model::in, can_fail::in,
prog_context::in, mlds_context::in, prog_var::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, MLDS_Context, Var, BinarySearchInfo, !Info) :-
ml_gen_var(!.Info, Var, VarLval),
VarRval = ml_lval(VarLval),
% 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.
IndexGCStatement = gc_no_stmt,
ResultGCStatement = gc_no_stmt,
ml_gen_info_new_aux_var_name("lo", LoVar, !Info),
LoVarDefn = ml_gen_mlds_var_decl(mlds_data_var(LoVar), IndexType,
IndexGCStatement, MLDS_Context),
ml_gen_var_lval(!.Info, LoVar, IndexType, LoVarLval),
ml_gen_info_new_aux_var_name("hi", HiVar, !Info),
HiVarDefn = ml_gen_mlds_var_decl(mlds_data_var(HiVar), IndexType,
IndexGCStatement, MLDS_Context),
ml_gen_var_lval(!.Info, HiVar, IndexType, HiVarLval),
ml_gen_info_new_aux_var_name("mid", MidVar, !Info),
MidVarDefn = ml_gen_mlds_var_decl(mlds_data_var(MidVar), IndexType,
IndexGCStatement, MLDS_Context),
ml_gen_var_lval(!.Info, MidVar, IndexType, MidVarLval),
ml_gen_info_new_aux_var_name("result", ResultVar, !Info),
ResultVarDefn = ml_gen_mlds_var_decl(mlds_data_var(ResultVar), ResultType,
ResultGCStatement, MLDS_Context),
ml_gen_var_lval(!.Info, ResultVar, ResultType, ResultVarLval),
AlwaysDefns = [LoVarDefn, HiVarDefn, MidVarDefn, ResultVarDefn],
ml_should_use_stop_loop(MLDS_Context, yes,
MaybeStopLoopLval, StopLoopVarDefns, !Info),
Defns = AlwaysDefns ++ StopLoopVarDefns,
% Generate the code for when the lookup fails.
ml_gen_maybe_switch_failure(CodeModel, CanFail, Context, FailStatements,
!Info),
BinarySearchInfo = ml_binary_search_info(CodeModel, VarRval,
LoVarLval, HiVarLval, MidVarLval, ResultVarLval, MaybeStopLoopLval,
FailStatements, Defns).
:- pred ml_gen_string_binary_switch_search(mlds_context::in, string::in,
ml_binary_search_info::in, mlds_vector_common::in, int::in, mlds_type::in,
mlds_field_id::in, list(mlds_defn)::in, list(statement)::in,
list(statement)::out, ml_gen_info::in, ml_gen_info::out) is det.
ml_gen_string_binary_switch_search(MLDS_Context, InitialComment,
BinarySearchInfo, VectorCommon, TableSize, StructType, StringFieldId,
MatchDefns, MatchStatement, Statements, !Info) :-
BinarySearchInfo = ml_binary_search_info(CodeModel, VarRval,
LoVarLval, HiVarLval, MidVarLval, ResultVarLval, MaybeStopLoopVarLval,
FailStatements, _Defns),
LoVarRval = ml_lval(LoVarLval),
HiVarRval = ml_lval(HiVarLval),
MidVarRval = ml_lval(MidVarLval),
ResultVarRval = ml_lval(ResultVarLval),
ml_wrap_loop_break(CodeModel, yes, MLDS_Context, MaybeStopLoopVarLval,
MatchDefns, MatchStatement, FailStatements,
SetupForFailStatements, SuccessStatement, AfterStatements, !Info),
ml_gen_info_get_module_info(!.Info, ModuleInfo),
MLDS_StringType = mercury_type_to_mlds_type(ModuleInfo, string_type),
InitLoVarStatement = statement(ml_stmt_atomic(
assign(LoVarLval, ml_const(mlconst_int(0)))),
MLDS_Context),
InitHiVarStatement = statement(ml_stmt_atomic(
assign(HiVarLval, ml_const(mlconst_int(TableSize - 1)))),
MLDS_Context),
CrossingTest = ml_binop(int_le, LoVarRval, HiVarRval),
AssignMidVarStatement = statement(ml_stmt_atomic(
assign(MidVarLval,
ml_binop(int_div,
ml_binop(int_add, LoVarRval, HiVarRval),
ml_const(mlconst_int(2))))),
MLDS_Context),
AssignResultVarStatement = statement(ml_stmt_atomic(
assign(ResultVarLval,
ml_binop(str_cmp,
VarRval,
ml_lval(ml_field(yes(0),
ml_vector_common_row(VectorCommon, MidVarRval),
StringFieldId, MLDS_StringType, StructType))))),
MLDS_Context),
ResultTest = ml_binop(eq, ResultVarRval, ml_const(mlconst_int(0))),
UpdateLoOrHiVarStatement = statement(
ml_stmt_if_then_else(
ml_binop(int_lt, ResultVarRval, ml_const(mlconst_int(0))),
statement(ml_stmt_atomic(
assign(HiVarLval,
ml_binop(int_sub, MidVarRval, ml_const(mlconst_int(1))))),
MLDS_Context),
yes(statement(ml_stmt_atomic(
assign(LoVarLval,
ml_binop(int_add, MidVarRval, ml_const(mlconst_int(1))))),
MLDS_Context))),
MLDS_Context),
(
MaybeStopLoopVarLval = no,
LoopBodyStatements = [
AssignMidVarStatement,
AssignResultVarStatement,
statement(ml_stmt_if_then_else(ResultTest,
SuccessStatement, yes(UpdateLoOrHiVarStatement)),
MLDS_Context)
],
SearchStatements = [
InitLoVarStatement,
InitHiVarStatement,
statement(ml_stmt_while(loop_at_least_once,
CrossingTest,
statement(ml_stmt_block([], LoopBodyStatements),
MLDS_Context)),
MLDS_Context)
]
;
MaybeStopLoopVarLval = yes(StopLoopVarLval),
InitStopLoopVarStatement = statement(ml_stmt_atomic(
assign(StopLoopVarLval, ml_const(mlconst_int(0)))),
MLDS_Context),
StopLoopTest = ml_binop(eq,
ml_lval(StopLoopVarLval), ml_const(mlconst_int(0))),
LoopBodyStatements = [
AssignMidVarStatement,
AssignResultVarStatement,
% SuccessStatement should set StopLoopVarLval to 1.
statement(ml_stmt_if_then_else(ResultTest,
SuccessStatement, yes(UpdateLoOrHiVarStatement)),
MLDS_Context)
],
SearchStatements = [
InitLoVarStatement,
InitHiVarStatement,
InitStopLoopVarStatement,
statement(ml_stmt_while(loop_at_least_once,
ml_binop(logical_and, StopLoopTest, CrossingTest),
statement(ml_stmt_block([], LoopBodyStatements),
MLDS_Context)),
MLDS_Context)
]
),
InitialCommentStatement =
statement(ml_stmt_atomic(comment(InitialComment)), MLDS_Context),
Statements = [InitialCommentStatement | SetupForFailStatements] ++
SearchStatements ++ AfterStatements.
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%
%
% Code useful for all kinds of string switches.
%
:- pred ml_should_use_stop_loop(mlds_context::in, bool::in,
maybe(mlds_lval)::out, list(mlds_defn)::out,
ml_gen_info::in, ml_gen_info::out) is det.
ml_should_use_stop_loop(MLDS_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,
( Experiment = "use_stop_loop" ->
UseStopLoop = yes
;
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.
StopLoopGCStatement = gc_no_stmt,
ml_gen_info_new_aux_var_name("stop_loop", StopLoopVar, !Info),
StopLoopVarDefn = ml_gen_mlds_var_decl(mlds_data_var(StopLoopVar),
StopLoopType, StopLoopGCStatement, MLDS_Context),
ml_gen_var_lval(!.Info, StopLoopVar, StopLoopType, StopLoopVarLval),
MaybeStopLoopLval = yes(StopLoopVarLval),
StopLoopLvalDefns = [StopLoopVarDefn]
).
:- pred ml_gen_maybe_switch_failure(code_model::in, can_fail::in,
prog_context::in, list(statement)::out,
ml_gen_info::in, ml_gen_info::out) is det.
ml_gen_maybe_switch_failure(CodeModel, CanFail, Context, FailStatements,
!Info) :-
% We used to include comments in FailStatements. However, that would
% complicate the task of ml_wrap_loop_break, which needs to decide
% whether FailStatements 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.
FailStatements = []
;
CanFail = can_fail,
ml_gen_failure(CodeModel, Context, FailStatements, !Info)
).
% ml_wrap_loop_break(CodeModel, LoopPresent,
% MLDS_Context, MaybeStopLoopVarLval, MatchDefns, MatchStatements,
% SetupForFailStatements, BodyStatement, AfterStatements, !Info)
%
% MatchStatements should be the statements that we execute once we find
% a match, and OnlyFailAfterStatements 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 MatchStatements with both MatchDefns and with
% other following code that causes execution to exit the loop
% after a match, and returns the resulting code as BodyStatement.
%
% We also return SetupForFailStatements and AfterStatements.
% SetupForFailStatements will be code to put before the loop, to set up
% for possible failure to find a match.
% AfterStatements will be code to put after the loop. It will contain
% OnlyFailAfterStatements, wrapped up in a test if necessary, as well as
% any code needed to enable BodyStatement to break out of the loop
% on a match.
%
:- pred ml_wrap_loop_break(code_model::in, bool::in, mlds_context::in,
maybe(mlds_lval)::in, list(mlds_defn)::in, list(statement)::in,
list(statement)::in,
list(statement)::out, statement::out, list(statement)::out,
ml_gen_info::in, ml_gen_info::out) is det.
ml_wrap_loop_break(CodeModel, LoopPresent, MLDS_Context, MaybeStopLoopVarLval,
MatchDefns, MatchStatements, FailStatements,
SetupForFailStatements, BodyStatement, AfterStatements, !Info) :-
(
CodeModel = model_det,
SetupForFailStatements = [],
expect(unify(FailStatements, []), $module, $pred,
"model_det, but FailStatements is not empty"),
OnlyFailAfterStatements = []
;
CodeModel = model_semi,
(
MaybeStopLoopVarLval = no,
SetupForFailStatements = [],
OnlyFailAfterStatements = FailStatements
;
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.
SetupForFailStatements = FailStatements,
OnlyFailAfterStatements = []
)
;
CodeModel = model_non,
SetupForFailStatements = [],
expect(unify(FailStatements, []), $module, $pred,
"model_non, but FailStatements is not empty"),
OnlyFailAfterStatements = []
),
(
MaybeStopLoopVarLval = no,
(
LoopPresent = no,
OnlyFailAfterStatements = []
->
BodyStatement =
statement(ml_stmt_block(MatchDefns, MatchStatements),
MLDS_Context),
AfterStatements = []
;
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),
(
SupportsBreakContinue = yes,
OnlyFailAfterStatements = [],
Experiment \= "use_end_label"
->
BreakCommentStatement = statement(ml_stmt_atomic(
comment("break out of search loop")), MLDS_Context),
BreakStatement =
statement(ml_stmt_goto(goto_break), MLDS_Context),
BodyStatement =
statement(ml_stmt_block(MatchDefns,
MatchStatements ++
[BreakCommentStatement, BreakStatement]),
MLDS_Context),
AfterStatements = []
;
ml_gen_new_label(EndLabel, !Info),
GotoCommentStatement = statement(ml_stmt_atomic(
comment("jump out of search loop")), MLDS_Context),
GotoEndStatement =
statement(ml_stmt_goto(goto_label(EndLabel)),
MLDS_Context),
BodyStatement =
statement(ml_stmt_block(MatchDefns,
MatchStatements ++
[GotoCommentStatement, GotoEndStatement]),
MLDS_Context),
EndLabelStatement =
statement(ml_stmt_label(EndLabel), MLDS_Context),
AfterStatements =
OnlyFailAfterStatements ++ [EndLabelStatement]
)
)
;
MaybeStopLoopVarLval = yes(StopLoopVarLval),
(
LoopPresent = no,
OnlyFailAfterStatements = []
->
BodyStatement =
statement(ml_stmt_block(MatchDefns, MatchStatements),
MLDS_Context)
;
SetStopLoopStatement =
statement(ml_stmt_atomic(
assign(StopLoopVarLval, ml_const(mlconst_int(1)))),
MLDS_Context),
BodyStatement =
statement(ml_stmt_block(MatchDefns,
MatchStatements ++ [SetStopLoopStatement]),
MLDS_Context)
),
(
OnlyFailAfterStatements = [],
AfterStatements = []
;
(
OnlyFailAfterStatements = [OnlyFailAfterStatement]
;
OnlyFailAfterStatements = [_, _ | _],
OnlyFailAfterStatement = statement(
ml_stmt_block([], OnlyFailAfterStatements),
MLDS_Context)
),
SuccessTest = ml_binop(eq,
ml_lval(StopLoopVarLval),
ml_const(mlconst_int(0))),
AfterStatement =
statement(
ml_stmt_if_then_else(SuccessTest,
OnlyFailAfterStatement, no),
MLDS_Context),
AfterStatements = [AfterStatement]
)
).
%-----------------------------------------------------------------------------%
:- end_module ml_backend.ml_string_switch.
%-----------------------------------------------------------------------------%