mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-15 09:23:44 +00:00
library/cord.m:
Add those two new predicates.
NEWS.md:
Announce the additions.
compiler/*.m:
Use snoc_list in many places.
tests/hard_coded/test_cord_2.{m,exp}:
Extend this test case to test cons_list.
1757 lines
75 KiB
Mathematica
1757 lines
75 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 1994-2007, 2009-2011 The University of Melbourne.
|
|
% Copyright (C) 2015, 2017-2018, 2020, 2022, 2024-2025 The Mercury team.
|
|
% This file may only be copied under the terms of the GNU General
|
|
% Public License - see the file COPYING in the Mercury distribution.
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% File: 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).
|
|
%
|
|
% When the switch arms are general code, what we put into the hash table
|
|
% or binary search table for each case is the offset of the relevant arm
|
|
% in a computed_goto. The generated code would be faster (due to better
|
|
% locality) if we included the actual target address instead. Unfortunately,
|
|
% that would require two extensions to the LLDS. The first and relatively
|
|
% easy change would be a new LLDS instruction that represents a goto
|
|
% to an arbitrary rval (in this case, the rval taken from the selected
|
|
% table row). The second and substantially harder change would be making
|
|
% the internal labels of the switch arms actually storable in static data.
|
|
% We do not currently have any way to refer to internal labels from data,
|
|
% and optimizations that manipulate labels (such as frameopt, which can
|
|
% duplicate them, and dupelim, which can replace them with other labels)
|
|
% would have to be taught to reflect any changes they make in the global
|
|
% data. It is the last step that is the killer in terms of difficulty
|
|
% of implementation.
|
|
%
|
|
% One possible way around the problem would be to do the code generation
|
|
% and optimization as we do now, just recording a bit more information
|
|
% during code generation about which numbers in static data refer to
|
|
% which computed_gotos, and then, after all the optimizations are done,
|
|
% to go back and replace all the indicated numbers with the corresponding
|
|
% final labels.
|
|
%
|
|
% WARNING: the code here is quite similar to the code in ml_string_switch.m.
|
|
% Any changes here may require similar changes there, and vice versa.
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module ll_backend.string_switch.
|
|
:- interface.
|
|
|
|
:- import_module hlds.
|
|
:- import_module hlds.code_model.
|
|
:- import_module hlds.hlds_goal.
|
|
:- import_module ll_backend.code_info.
|
|
:- import_module ll_backend.code_loc_dep.
|
|
:- import_module ll_backend.llds.
|
|
:- import_module ll_backend.lookup_switch.
|
|
:- import_module parse_tree.
|
|
:- import_module parse_tree.prog_data.
|
|
|
|
:- import_module list.
|
|
|
|
%---------------------%
|
|
|
|
:- pred generate_string_trie_jump_switch(rval::in, string::in,
|
|
list(tagged_case)::in, code_model::in, can_fail::in, hlds_goal_info::in,
|
|
label::in, branch_end::out, llds_code::out,
|
|
code_info::in, code_info::out, code_loc_dep::in) is det.
|
|
|
|
:- pred generate_string_trie_lookup_switch(rval::in,
|
|
lookup_switch_info(string)::in, can_fail::in, label::in,
|
|
branch_end::out, llds_code::out, code_info::out) is det.
|
|
|
|
%---------------------%
|
|
|
|
:- pred generate_string_hash_jump_switch(rval::in, string::in,
|
|
list(tagged_case)::in, code_model::in, can_fail::in, hlds_goal_info::in,
|
|
label::in, branch_end::out, llds_code::out,
|
|
code_info::in, code_info::out, code_loc_dep::in) is det.
|
|
|
|
:- pred generate_string_hash_lookup_switch(rval::in,
|
|
lookup_switch_info(string)::in, can_fail::in, label::in,
|
|
branch_end::out, llds_code::out, code_info::out) is det.
|
|
|
|
%---------------------%
|
|
|
|
:- pred generate_string_binary_jump_switch(rval::in, string::in,
|
|
list(tagged_case)::in, code_model::in, can_fail::in, hlds_goal_info::in,
|
|
label::in, branch_end::out, llds_code::out,
|
|
code_info::in, code_info::out, code_loc_dep::in) is det.
|
|
|
|
:- pred generate_string_binary_lookup_switch(rval::in,
|
|
lookup_switch_info(string)::in, can_fail::in, label::in,
|
|
branch_end::out, llds_code::out, code_info::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module backend_libs.
|
|
:- import_module backend_libs.builtin_ops.
|
|
:- import_module backend_libs.lookup_switch_util.
|
|
:- import_module backend_libs.string_encoding.
|
|
:- import_module backend_libs.string_switch_util.
|
|
:- import_module hlds.hlds_data.
|
|
:- import_module hlds.hlds_llds.
|
|
:- import_module libs.
|
|
:- import_module libs.globals.
|
|
:- import_module ll_backend.code_util.
|
|
:- import_module ll_backend.lookup_util.
|
|
:- import_module ll_backend.switch_case.
|
|
|
|
:- import_module assoc_list.
|
|
:- import_module cord.
|
|
:- import_module int.
|
|
:- import_module map.
|
|
:- import_module maybe.
|
|
:- import_module pair.
|
|
:- import_module require.
|
|
:- import_module string.
|
|
:- import_module unit.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
generate_string_trie_jump_switch(VarRval, VarName, TaggedCases,
|
|
CodeModel, CanFail, SwitchGoalInfo, EndLabel, MaybeEnd, Code,
|
|
!CI, CLD) :-
|
|
init_string_trie_switch_info_jump(CanFail, JumpInfo0, !CI, CLD),
|
|
BranchStart = JumpInfo0 ^ stsij_branch_start,
|
|
Params = represent_params(VarName, SwitchGoalInfo, CodeModel, BranchStart,
|
|
EndLabel),
|
|
|
|
% Generate code for the cases, and remember the label of each case.
|
|
map.init(CaseIdToLabelMap0),
|
|
map.init(CaseLabelMap0),
|
|
represent_tagged_cases_in_string_trie_switch(Params, TaggedCases,
|
|
CaseIdToLabelMap0, CaseIdToLabelMap, CaseLabelMap0, CaseLabelMap,
|
|
no, MaybeEnd, !CI),
|
|
JumpInfo = JumpInfo0 ^ stsij_case_id_to_label_map := CaseIdToLabelMap,
|
|
|
|
Info = stsi_jump(JumpInfo),
|
|
build_str_case_id_list(TaggedCases, _MaxCaseNum, StrsCaseIds),
|
|
create_nested_trie_switch(Info, VarRval, StrsCaseIds, TrieCode, !CI),
|
|
|
|
% Generate the code for the cases.
|
|
add_not_yet_included_cases(CasesCode, CaseLabelMap, _),
|
|
|
|
FailLabel = JumpInfo ^ stsij_fail_label,
|
|
FailLabelCode = singleton(
|
|
llds_instr(label(FailLabel), "fail label")
|
|
),
|
|
FailCode = JumpInfo ^ stsij_fail_code,
|
|
|
|
MainCode = TrieCode ++ CasesCode ++ FailLabelCode ++ FailCode,
|
|
SwitchKindStr = "string trie jump switch",
|
|
add_switch_kind_comment_and_end_label(SwitchKindStr, EndLabel,
|
|
MainCode, Code).
|
|
|
|
:- pred represent_tagged_cases_in_string_trie_switch(represent_params::in,
|
|
list(tagged_case)::in, map(case_id, label)::in, map(case_id, label)::out,
|
|
case_label_map::in, case_label_map::out,
|
|
branch_end::in, branch_end::out, code_info::in, code_info::out) is det.
|
|
|
|
represent_tagged_cases_in_string_trie_switch(_, [],
|
|
!CaseIdToLabelMap, !CaseLabelMap, !MaybeEnd, !CI).
|
|
represent_tagged_cases_in_string_trie_switch(Params,
|
|
[TaggedCase | TaggedCases],
|
|
!CaseIdToLabelMap, !CaseLabelMap, !MaybeEnd, !CI) :-
|
|
represent_tagged_case_for_llds(Params, TaggedCase, Label,
|
|
!CaseLabelMap, !MaybeEnd, !CI, unit, _),
|
|
TaggedCase = tagged_case(_, _, CaseId, _),
|
|
map.det_insert(CaseId, Label, !CaseIdToLabelMap),
|
|
represent_tagged_cases_in_string_trie_switch(Params, TaggedCases,
|
|
!CaseIdToLabelMap, !CaseLabelMap, !MaybeEnd, !CI).
|
|
|
|
%---------------------%
|
|
|
|
:- type code_unit_to_action
|
|
---> code_unit_to_action(int, code_unit_action).
|
|
|
|
:- type code_unit_action
|
|
---> action_nested_trie(label)
|
|
; action_case(case_id).
|
|
|
|
:- pred create_nested_trie_switch(string_trie_switch_info::in, rval::in,
|
|
assoc_list(string, case_id)::in, llds_code::out,
|
|
code_info::in, code_info::out) is det.
|
|
|
|
create_nested_trie_switch(Info, VarRval, StrsCaseIds, TrieCode, !CI) :-
|
|
get_encoding(Info, Encoding),
|
|
create_trie(Encoding, StrsCaseIds, TopTrieNode),
|
|
convert_trie_to_nested_switches(Info, VarRval, 0,
|
|
TopTrieNode, TopTrieLabel, TrieCode0, !CI),
|
|
( if
|
|
cord.head_tail(TrieCode0, HeadTrieCodeInstr, TailTrieCodeInstrs),
|
|
HeadTrieCodeInstr = llds_instr(label(TopTrieLabel), _)
|
|
then
|
|
TrieCode = TailTrieCodeInstrs
|
|
else
|
|
unexpected($pred, "TrieCode0 does not start with TopTrieLabel")
|
|
).
|
|
|
|
:- pred convert_trie_to_nested_switches(string_trie_switch_info::in,
|
|
rval::in, int::in, trie_node::in,
|
|
label::out, llds_code::out, code_info::in, code_info::out) is det.
|
|
|
|
convert_trie_to_nested_switches(Info, VarRval, NumMatched, TrieNode,
|
|
TrieNodeLabel, Code, !CI) :-
|
|
get_next_label(TrieNodeLabel, !CI),
|
|
(
|
|
TrieNode = trie_leaf(RevMatchedCodeUnits, NotYetMatchedCodeUnits,
|
|
CaseId),
|
|
list.reverse(RevMatchedCodeUnits, MatchedCodeUnits),
|
|
AllCodeUnits = MatchedCodeUnits ++ NotYetMatchedCodeUnits,
|
|
list.length(MatchedCodeUnits, NumMatchedCodeUnits),
|
|
expect(unify(NumMatchedCodeUnits, NumMatched), $pred,
|
|
"NumevMatchedCodeUnits != NumMatched"),
|
|
get_encoding(Info, Encoding),
|
|
det_from_code_unit_list_in_encoding_allow_ill_formed(Encoding,
|
|
AllCodeUnits, EndStr),
|
|
NodeComment = "AllCodeUnits " ++ string.string(AllCodeUnits),
|
|
CondRval = binop(offset_str_eq(NumMatched, no_size),
|
|
VarRval, const(llconst_string(EndStr))),
|
|
generate_trie_case_or_fall_through(Info, CondRval, CaseId,
|
|
CaseRepCode, !CI),
|
|
generate_trie_goto_fail_code(Info, GotoFailCode),
|
|
TrieNodeLabelCode = singleton(
|
|
llds_instr(label(TrieNodeLabel), NodeComment)
|
|
),
|
|
Code = TrieNodeLabelCode ++ CaseRepCode ++ GotoFailCode
|
|
;
|
|
TrieNode = trie_choice(RevMatchedCodeUnits, _ChoiceMap, MaybeEnd),
|
|
list.length(RevMatchedCodeUnits, NumRevMatchedCodeUnits),
|
|
expect(unify(NumRevMatchedCodeUnits, NumMatched), $pred,
|
|
"NumRevMatchedCodeUnits != NumMatched"),
|
|
|
|
get_code_unit_reg(Info, CodeUnitRegLval),
|
|
GetCurCodeUnitRval = binop(string_unsafe_index_code_unit,
|
|
VarRval, const(llconst_int(NumMatched))),
|
|
LabelCode = singleton(
|
|
llds_instr(label(TrieNodeLabel), "")
|
|
),
|
|
SetCodeUnitCode = singleton(
|
|
llds_instr(assign(CodeUnitRegLval, GetCurCodeUnitRval), "")
|
|
),
|
|
CodeUnitRval = lval(CodeUnitRegLval),
|
|
generate_trie_goto_fail_code(Info, GotoFailCode),
|
|
|
|
chase_any_stick_in_trie(TrieNode, ChoicePairs,
|
|
StickCodeUnits, TrieNodeAfterStick),
|
|
(
|
|
StickCodeUnits = [_, _ | _],
|
|
list.length(StickCodeUnits, NumStickCodeUnits),
|
|
CmpOp = offset_str_eq(NumMatched, size(NumStickCodeUnits)),
|
|
list.reverse(RevMatchedCodeUnits, MatchedCodeUnits),
|
|
get_encoding(Info, Encoding),
|
|
MatchedStickCodeUnits = MatchedCodeUnits ++ StickCodeUnits,
|
|
det_from_code_unit_list_in_encoding_allow_ill_formed(Encoding,
|
|
MatchedStickCodeUnits, MatchedStickStr),
|
|
TestComment =
|
|
"MatchedCodeUnits " ++ string.string(MatchedCodeUnits) ++
|
|
" StickCodeUnits " ++ string.string(StickCodeUnits),
|
|
TestRval = binop(CmpOp,
|
|
VarRval, const(llconst_string(MatchedStickStr))),
|
|
convert_trie_to_nested_switches(Info, VarRval,
|
|
NumMatched + NumStickCodeUnits,
|
|
TrieNodeAfterStick, TrieNodeLabelAfterStick,
|
|
CodeAfterStick, !CI),
|
|
TrieNodeCodeAddrAfterStick = code_label(TrieNodeLabelAfterStick),
|
|
TestCode = singleton(
|
|
llds_instr(if_val(TestRval, TrieNodeCodeAddrAfterStick),
|
|
TestComment)
|
|
),
|
|
TestChainCode = TestCode ++ GotoFailCode,
|
|
Code = LabelCode ++ TestChainCode ++ CodeAfterStick
|
|
;
|
|
( StickCodeUnits = []
|
|
; StickCodeUnits = [_]
|
|
),
|
|
convert_trie_choices_to_nested_switches(Info, VarRval,
|
|
NumMatched + 1, ChoicePairs,
|
|
cord.init, NestedTrieInfosCord, 0, NumActions0,
|
|
empty, NestedTrieCode, !CI),
|
|
NestedTrieInfos0 = cord.list(NestedTrieInfosCord),
|
|
(
|
|
MaybeEnd = no,
|
|
NestedTrieInfos = NestedTrieInfos0,
|
|
NumActions = NumActions0
|
|
;
|
|
MaybeEnd = yes(EndCaseId),
|
|
EndNestedTrieInfo =
|
|
code_unit_to_action(0, action_case(EndCaseId)),
|
|
NestedTrieInfos = [EndNestedTrieInfo | NestedTrieInfos0],
|
|
NumActions = NumActions0 + 1
|
|
),
|
|
( if NumActions =< 3 then
|
|
generate_nested_trie_try_chain(Info, CodeUnitRval,
|
|
NestedTrieInfos, empty, TestCode, !CI)
|
|
else
|
|
generate_nested_trie_binary_search(Info, CodeUnitRval,
|
|
NumActions, NestedTrieInfos, TestCode, !CI)
|
|
),
|
|
Code = LabelCode ++ SetCodeUnitCode ++ TestCode ++ NestedTrieCode
|
|
)
|
|
).
|
|
|
|
:- pred convert_trie_choices_to_nested_switches(string_trie_switch_info::in,
|
|
rval::in, int::in, assoc_list(int, trie_node)::in,
|
|
cord(code_unit_to_action)::in, cord(code_unit_to_action)::out,
|
|
int::in, int::out, llds_code::in, llds_code::out,
|
|
code_info::in, code_info::out) is det.
|
|
|
|
convert_trie_choices_to_nested_switches(_, _, _, [],
|
|
!CodeUnitToActionsCord, !NumActions, !NestedTrieCode, !CI).
|
|
convert_trie_choices_to_nested_switches(Info, VarRval,
|
|
NumMatched, [Choice | Choices],
|
|
!CodeUnitToActionsCord, !NumActions, !NestedTrieCode, !CI) :-
|
|
Choice = CodeUnit - TrieNode,
|
|
convert_trie_to_nested_switches(Info, VarRval, NumMatched,
|
|
TrieNode, TrieNodeLabel, TrieNodeCode, !CI),
|
|
CodeUnitToAction =
|
|
code_unit_to_action(CodeUnit, action_nested_trie(TrieNodeLabel)),
|
|
cord.snoc(CodeUnitToAction, !CodeUnitToActionsCord),
|
|
!:NumActions = !.NumActions + 1,
|
|
!:NestedTrieCode = !.NestedTrieCode ++ TrieNodeCode,
|
|
convert_trie_choices_to_nested_switches(Info, VarRval, NumMatched, Choices,
|
|
!CodeUnitToActionsCord, !NumActions, !NestedTrieCode, !CI).
|
|
|
|
%---------------------%
|
|
|
|
:- pred generate_nested_trie_try_chain(string_trie_switch_info::in, rval::in,
|
|
list(code_unit_to_action)::in, llds_code::in, llds_code::out,
|
|
code_info::in, code_info::out) is det.
|
|
|
|
generate_nested_trie_try_chain(Info, _, [], !TryChainCode, !CI) :-
|
|
generate_trie_goto_fail_code(Info, GotoFailCode),
|
|
!:TryChainCode = !.TryChainCode ++ GotoFailCode.
|
|
generate_nested_trie_try_chain(Info, CodeUnitRval,
|
|
[CodeUnitToAction | CodeUnitToActions], !TryChainCode, !CI) :-
|
|
CodeUnitToAction = code_unit_to_action(CodeUnit, Action),
|
|
CondRval = binop(int_cmp(int_type_int, eq),
|
|
CodeUnitRval, const(llconst_int(CodeUnit))),
|
|
(
|
|
Action = action_nested_trie(NestedTrieNodeLabel),
|
|
TestCodeUnitCode = singleton(
|
|
llds_instr(if_val(CondRval, code_label(NestedTrieNodeLabel)), "")
|
|
)
|
|
;
|
|
Action = action_case(CaseId),
|
|
generate_trie_case_or_fall_through(Info, CondRval, CaseId,
|
|
TestCodeUnitCode, !CI)
|
|
),
|
|
!:TryChainCode = !.TryChainCode ++ TestCodeUnitCode,
|
|
generate_nested_trie_try_chain(Info, CodeUnitRval,
|
|
CodeUnitToActions, !TryChainCode, !CI).
|
|
|
|
%---------------------%
|
|
|
|
:- pred generate_nested_trie_binary_search(string_trie_switch_info::in,
|
|
rval::in, int::in, list(code_unit_to_action)::in, llds_code::out,
|
|
code_info::in, code_info::out) is det.
|
|
|
|
generate_nested_trie_binary_search(Info, CodeUnitRval,
|
|
NumActions, CodeUnitToActions, TestCode, !CI) :-
|
|
( if NumActions =< 3 then
|
|
generate_nested_trie_try_chain(Info, CodeUnitRval,
|
|
CodeUnitToActions, empty, TestCode, !CI)
|
|
else
|
|
NumActionsR = NumActions / 2,
|
|
NumActionsL = NumActions - NumActionsR,
|
|
list.det_split_list(NumActionsL, CodeUnitToActions,
|
|
CodeUnitToActionsL, CodeUnitToActionsR),
|
|
list.det_head(CodeUnitToActionsR, HeadCodeUnitToActions),
|
|
HeadCodeUnitToActions = code_unit_to_action(LeastCodeUnitR, _),
|
|
code_info.get_next_label(LabelR, !CI),
|
|
|
|
TestRvalLR = binop(int_cmp(int_type_int, ge),
|
|
CodeUnitRval, const(llconst_int(LeastCodeUnitR))),
|
|
CommentLR = "binary search on code unit",
|
|
TestCodeLR = singleton(
|
|
llds_instr(if_val(TestRvalLR, code_label(LabelR)), CommentLR)
|
|
),
|
|
generate_nested_trie_binary_search(Info, CodeUnitRval,
|
|
NumActionsL, CodeUnitToActionsL, TestCodeL, !CI),
|
|
LabelCodeR = singleton(
|
|
llds_instr(label(LabelR), "")
|
|
),
|
|
generate_nested_trie_binary_search(Info, CodeUnitRval,
|
|
NumActionsR, CodeUnitToActionsR, TestCodeR, !CI),
|
|
TestCode = TestCodeLR ++ TestCodeL ++ LabelCodeR ++ TestCodeR
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- type string_trie_switch_info_jump
|
|
---> string_trie_switch_info_jump(
|
|
stsij_encoding :: string_encoding,
|
|
stsij_case_id_to_label_map :: map(case_id, label),
|
|
stsij_branch_start :: position_info,
|
|
stsij_code_unit_reg :: lval,
|
|
stsij_fail_label :: label,
|
|
stsij_fail_code :: llds_code,
|
|
stsij_goto_fail_code :: llds_code
|
|
).
|
|
|
|
:- pred init_string_trie_switch_info_jump(can_fail::in,
|
|
string_trie_switch_info_jump::out,
|
|
code_info::in, code_info::out, code_loc_dep::in) is det.
|
|
|
|
init_string_trie_switch_info_jump(CanFail, Info, !CI, !.CLD) :-
|
|
Encoding = target_string_encoding(target_c),
|
|
|
|
% See the comment about acquire_reg/release_reg in the
|
|
% init_string_binary_switch_info predicate below.
|
|
acquire_reg(reg_r, CodeUnitReg, !CLD),
|
|
release_reg(CodeUnitReg, !CLD),
|
|
|
|
remember_position(!.CLD, BranchStart),
|
|
|
|
% We generate a fail label even if CanFail = cannot_fail, so that
|
|
% we have somewhere to go to throw an exception at runtime.
|
|
get_next_label(FailLabel, !CI),
|
|
% We must generate the failure code in the context in which
|
|
% none of the switch arms have been executed yet.
|
|
generate_string_switch_fail(CanFail, FailCode, !.CI, !.CLD),
|
|
|
|
GotoFailCode = singleton(
|
|
llds_instr(goto(code_label(FailLabel)), "no match; goto fail")
|
|
),
|
|
|
|
% The code that creates the case_id to label map needs the
|
|
% other fields of the string_trie_switch_info_jump structure as its inputs,
|
|
% so this field will be filled in with *real* data by our caller.
|
|
map.init(CaseIdToLabelMap0),
|
|
Info = string_trie_switch_info_jump(Encoding, CaseIdToLabelMap0,
|
|
BranchStart, CodeUnitReg, FailLabel, FailCode, GotoFailCode).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
generate_string_trie_lookup_switch(VarRval, LookupSwitchInfo, CanFail,
|
|
EndLabel, !:MaybeEnd, Code, !:CI) :-
|
|
LookupSwitchInfo = lookup_switch_info(KeyToCaseIdMap, CaseConsts,
|
|
OutVars, OutTypes, EndBranch, !:MaybeEnd, !:CI, CLD),
|
|
init_string_trie_switch_info_lookup(CanFail, LookupInfo, !CI, CLD),
|
|
CaseNumRegLval = LookupInfo ^ stsil_case_id_reg,
|
|
InitCaseNumRegCode = singleton(
|
|
llds_instr(assign(CaseNumRegLval, const(llconst_int(-1))),
|
|
"initialize case id to invalid")
|
|
),
|
|
Info = stsi_lookup(LookupInfo),
|
|
map.to_sorted_assoc_list(KeyToCaseIdMap, StrsCaseIds),
|
|
create_nested_trie_switch(Info, VarRval, StrsCaseIds, TrieCode, !CI),
|
|
AfterLabel = LookupInfo ^ stsil_after_label,
|
|
AfterLabelCode = singleton(
|
|
llds_instr(label(AfterLabel), "after the trie search")
|
|
),
|
|
SetCaseNumCode = InitCaseNumRegCode ++ TrieCode ++ AfterLabelCode,
|
|
% We must generate the failure code in the context in which
|
|
% none of the switch arms have been executed yet.
|
|
(
|
|
CanFail = cannot_fail,
|
|
SetAndCheckCaseNumCode = SetCaseNumCode
|
|
;
|
|
CanFail = can_fail,
|
|
get_next_label(NonFailLabel, !CI),
|
|
CaseNumIsValid = binop(int_cmp(int_type_int, ge),
|
|
lval(CaseNumRegLval), const(llconst_int(0))),
|
|
TestForFailCode = singleton(
|
|
llds_instr(if_val(CaseNumIsValid, code_label(NonFailLabel)),
|
|
"branch around fail code")
|
|
),
|
|
FailCode = LookupInfo ^ stsil_fail_code,
|
|
NonFailLabelCode = singleton(
|
|
llds_instr(label(NonFailLabel), "non-fail label")
|
|
),
|
|
SetAndCheckCaseNumCode = SetCaseNumCode ++
|
|
TestForFailCode ++ FailCode ++ NonFailLabelCode
|
|
),
|
|
|
|
(
|
|
CaseConsts = all_one_soln(CaseIdToValuesMap),
|
|
map.to_assoc_list(CaseIdToValuesMap, CaseIdToValuesAL),
|
|
generate_string_trie_simple_lookup_switch(LookupInfo, CaseIdToValuesAL,
|
|
OutVars, OutTypes, EndLabel, EndBranch,
|
|
SetAndCheckCaseNumCode, Code, !MaybeEnd, !CI, CLD)
|
|
;
|
|
CaseConsts = some_several_solns(CaseIdToValuesListMap,
|
|
CaseConstsSeveralLlds),
|
|
map.to_assoc_list(CaseIdToValuesListMap, CaseIdToValuesListAL),
|
|
generate_string_trie_several_soln_lookup_switch(LookupInfo,
|
|
CaseConstsSeveralLlds, CaseIdToValuesListAL,
|
|
OutVars, OutTypes, EndLabel, EndBranch,
|
|
SetAndCheckCaseNumCode, Code, !MaybeEnd, !CI, CLD)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred generate_string_trie_simple_lookup_switch(
|
|
string_trie_switch_info_lookup::in,
|
|
assoc_list(case_id, list(rval))::in, list(prog_var)::in,
|
|
list(llds_type)::in, label::in, end_branch_info::in,
|
|
llds_code::in, llds_code::out, branch_end::in, branch_end::out,
|
|
code_info::in, code_info::out, code_loc_dep::in) is det.
|
|
|
|
generate_string_trie_simple_lookup_switch(LookupInfo, CaseValues,
|
|
OutVars, OutTypes, EndLabel, EndBranch, SetAndCheckCaseNumCode, Code,
|
|
!MaybeEnd, !CI, CLD) :-
|
|
(
|
|
OutVars = [],
|
|
generate_single_soln_table_lookup_code_no_vars(EndBranch, LookupCode,
|
|
!MaybeEnd, CLD)
|
|
;
|
|
OutVars = [_ | _],
|
|
NumPrevColumns = 0,
|
|
MainRowTypes = OutTypes,
|
|
|
|
construct_string_trie_simple_lookup_vector(CaseValues, 0,
|
|
cord.init, MainTableRvalsCord),
|
|
MainTableRvals = cord.list(MainTableRvalsCord),
|
|
add_vector_static_cell(MainRowTypes, MainTableRvals,
|
|
MainTableDataId, !CI),
|
|
|
|
CaseIdRegLval = LookupInfo ^ stsil_case_id_reg,
|
|
MainRowSelect = main_row_number_reg(lval(CaseIdRegLval), OutTypes),
|
|
generate_single_soln_table_lookup_code_some_vars(MainTableDataId,
|
|
MainRowSelect, NumPrevColumns, OutVars, EndBranch, LookupCode,
|
|
!MaybeEnd, !.CI, CLD)
|
|
),
|
|
MainCode = SetAndCheckCaseNumCode ++ LookupCode,
|
|
SwitchKindStr = "string trie single soln lookup switch",
|
|
add_switch_kind_comment_and_end_label(SwitchKindStr, EndLabel,
|
|
MainCode, Code).
|
|
|
|
:- pred construct_string_trie_simple_lookup_vector(
|
|
assoc_list(case_id, list(rval))::in, int::in,
|
|
cord(list(rval))::in, cord(list(rval))::out) is det.
|
|
|
|
construct_string_trie_simple_lookup_vector([], _, !RvalsCord).
|
|
construct_string_trie_simple_lookup_vector([CaseValues | CasesValues], RowNum,
|
|
!RvalsCord) :-
|
|
CaseValues = CaseId - OutVarRvals,
|
|
CaseId = case_id(CaseIdNum),
|
|
expect(unify(RowNum, CaseIdNum), $pred, "RowNum != CaseIdNum"),
|
|
RowRvals = OutVarRvals,
|
|
cord.snoc(RowRvals, !RvalsCord),
|
|
construct_string_trie_simple_lookup_vector(CasesValues, RowNum + 1,
|
|
!RvalsCord).
|
|
|
|
%---------------------%
|
|
|
|
:- pred generate_string_trie_several_soln_lookup_switch(
|
|
string_trie_switch_info_lookup::in, case_consts_several_llds::in,
|
|
assoc_list(case_id, soln_consts(rval))::in,
|
|
list(prog_var)::in, list(llds_type)::in, label::in, end_branch_info::in,
|
|
llds_code::in, llds_code::out, branch_end::in, branch_end::out,
|
|
code_info::in, code_info::out, code_loc_dep::in) is det.
|
|
|
|
generate_string_trie_several_soln_lookup_switch(LookupInfo,
|
|
CaseConstsSeveralLlds, CaseIdToValuesListAL, OutVars, OutTypes,
|
|
EndLabel, EndBranch, SetAndCheckCaseNumCode, Code,
|
|
!MaybeEnd, !CI, !.CLD) :-
|
|
NumPrevColumns = 0,
|
|
MainRowTypes = [lt_int(int_type_int), lt_int(int_type_int) | OutTypes],
|
|
|
|
list.length(OutVars, NumOutVars),
|
|
% The later solutions table has one column for each output var,
|
|
% and no other columns.
|
|
NumLaterColumns = NumOutVars,
|
|
LaterSolnsRowNumber0 = 1,
|
|
DummyOutRvals = list.map(default_value_for_type, OutTypes),
|
|
LaterSolnsRowsCord0 = cord.singleton(DummyOutRvals),
|
|
construct_string_trie_several_soln_lookup_vector(NumLaterColumns,
|
|
CaseIdToValuesListAL, 0, cord.init, MainRvalsCord,
|
|
LaterSolnsRowNumber0, LaterSolnsRowsCord0, LaterSolnsRowsCord,
|
|
0, OneSolnCaseCount, 0, SeveralSolnsCaseCount),
|
|
MainRvals = cord.list(MainRvalsCord),
|
|
LaterSolnsRows = cord.list(LaterSolnsRowsCord),
|
|
|
|
add_vector_static_cell(MainRowTypes, MainRvals, MainTableDataId, !CI),
|
|
add_vector_static_cell(OutTypes, LaterSolnsRows, LaterTableDataId, !CI),
|
|
LaterTableAddrRval = const(llconst_data_addr(LaterTableDataId)),
|
|
|
|
CaseIdRegLval = LookupInfo ^ stsil_case_id_reg,
|
|
MainRowSelect = main_row_number_reg(lval(CaseIdRegLval), MainRowTypes),
|
|
acquire_and_setup_lookup_base_reg(MainTableDataId, EndBranch,
|
|
MainRowSelect, BaseRegLval, SetBaseRegCode, !CLD),
|
|
|
|
generate_multi_soln_table_lookup_code(CaseConstsSeveralLlds,
|
|
OneSolnCaseCount - kind_one_soln,
|
|
[SeveralSolnsCaseCount - kind_several_solns],
|
|
NumPrevColumns, OutVars, EndLabel, BaseRegLval, LaterTableAddrRval,
|
|
EndBranch, LookupResultsCode, !MaybeEnd, !CI, !.CLD),
|
|
|
|
MainCode = SetAndCheckCaseNumCode ++ SetBaseRegCode ++ LookupResultsCode,
|
|
SwitchKindStr = "string trie multi soln lookup switch",
|
|
add_switch_kind_comment_and_end_label(SwitchKindStr, EndLabel,
|
|
MainCode, Code).
|
|
|
|
:- pred construct_string_trie_several_soln_lookup_vector(int::in,
|
|
assoc_list(case_id, soln_consts(rval))::in, int::in,
|
|
cord(list(rval))::in, cord(list(rval))::out,
|
|
int::in, cord(list(rval))::in, cord(list(rval))::out,
|
|
int::in, int::out, int::in, int::out) is det.
|
|
|
|
construct_string_trie_several_soln_lookup_vector(_, [],
|
|
_, !MainRvalsCord, _, !LaterSolnsRowsCord,
|
|
!OneSolnCaseCount, !SeveralSolnsCaseCount).
|
|
construct_string_trie_several_soln_lookup_vector(NumLaterColumns,
|
|
[CaseValues | CasesValues], MainRowNum, !MainRvalsCord,
|
|
!.LaterNextRowNum, !LaterSolnsRowsCord,
|
|
!OneSolnCaseCount, !SeveralSolnsCaseCount) :-
|
|
CaseValues = CaseId - SolnConsts,
|
|
CaseId = case_id(CaseIdNum),
|
|
expect(unify(MainRowNum, CaseIdNum), $pred, "RowNum != CaseIdNum"),
|
|
(
|
|
SolnConsts = one_soln(OutVarRvals),
|
|
!:OneSolnCaseCount = !.OneSolnCaseCount + 1,
|
|
ZeroRval = const(llconst_int(0)),
|
|
% The first ZeroRval means there is exactly one solution for
|
|
% this case; the second ZeroRval is a dummy that won't be
|
|
% referenced.
|
|
MainRowRvals = [ZeroRval, ZeroRval | OutVarRvals]
|
|
;
|
|
SolnConsts = several_solns(FirstSolnRvals, LaterSolns),
|
|
!:SeveralSolnsCaseCount = !.SeveralSolnsCaseCount + 1,
|
|
list.length(LaterSolns, NumLaterSolns),
|
|
FirstRowOffset = !.LaterNextRowNum * NumLaterColumns,
|
|
LastRowOffset = (!.LaterNextRowNum + NumLaterSolns - 1) *
|
|
NumLaterColumns,
|
|
FirstRowRval = const(llconst_int(FirstRowOffset)),
|
|
LastRowRval = const(llconst_int(LastRowOffset)),
|
|
MainRowRvals = [FirstRowRval, LastRowRval | FirstSolnRvals],
|
|
!:LaterNextRowNum = !.LaterNextRowNum + NumLaterSolns,
|
|
cord.snoc_list(LaterSolns, !LaterSolnsRowsCord)
|
|
),
|
|
cord.snoc(MainRowRvals, !MainRvalsCord),
|
|
construct_string_trie_several_soln_lookup_vector(NumLaterColumns,
|
|
CasesValues, MainRowNum + 1, !MainRvalsCord,
|
|
!.LaterNextRowNum, !LaterSolnsRowsCord,
|
|
!OneSolnCaseCount, !SeveralSolnsCaseCount).
|
|
|
|
%---------------------%
|
|
|
|
:- type string_trie_switch_info_lookup
|
|
---> string_trie_switch_info_lookup(
|
|
stsil_encoding :: string_encoding,
|
|
stsil_branch_start :: position_info,
|
|
stsil_code_unit_reg :: lval,
|
|
stsil_case_id_reg :: lval,
|
|
stsil_after_label :: label,
|
|
stsil_after_code_addr :: code_addr,
|
|
stsil_goto_after_code :: llds_code,
|
|
stsil_fail_code :: llds_code
|
|
).
|
|
|
|
:- pred init_string_trie_switch_info_lookup(can_fail::in,
|
|
string_trie_switch_info_lookup::out,
|
|
code_info::in, code_info::out, code_loc_dep::in) is det.
|
|
|
|
init_string_trie_switch_info_lookup(CanFail, Info, !CI, !.CLD) :-
|
|
Encoding = target_string_encoding(target_c),
|
|
|
|
% See the comment about acquire_reg/release_reg in the
|
|
% init_string_binary_switch_info predicate below.
|
|
acquire_reg(reg_r, CodeUnitReg, !CLD),
|
|
acquire_reg(reg_r, CaseIdReg, !CLD),
|
|
release_reg(CodeUnitReg, !CLD),
|
|
release_reg(CaseIdReg, !CLD),
|
|
|
|
remember_position(!.CLD, BranchStart),
|
|
|
|
get_next_label(AfterLabel, !CI),
|
|
AfterCodeAddr = code_label(AfterLabel),
|
|
|
|
GotoAfterCode = singleton(
|
|
llds_instr(goto(AfterCodeAddr), "go to the code after the trie")
|
|
),
|
|
|
|
(
|
|
CanFail = cannot_fail,
|
|
FailCode = empty
|
|
;
|
|
CanFail = can_fail,
|
|
generate_failure(FailCode, !.CI, !.CLD)
|
|
),
|
|
|
|
Info = string_trie_switch_info_lookup(Encoding, BranchStart,
|
|
CodeUnitReg, CaseIdReg, AfterLabel, AfterCodeAddr, GotoAfterCode,
|
|
FailCode).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- type string_trie_switch_info
|
|
---> stsi_jump(string_trie_switch_info_jump)
|
|
; stsi_lookup(string_trie_switch_info_lookup).
|
|
|
|
%---------------------%
|
|
|
|
:- pred generate_trie_case_or_fall_through(string_trie_switch_info::in,
|
|
rval::in, case_id::in, llds_code::out, code_info::in, code_info::out)
|
|
is det.
|
|
|
|
generate_trie_case_or_fall_through(Info, CondRval, CaseId, CaseCode, !CI) :-
|
|
(
|
|
Info = stsi_jump(JumpInfo),
|
|
JumpInfo = string_trie_switch_info_jump(_Encoding, CaseIdToLabelMap,
|
|
_CodeUnitRegLval, _BranchStart, _FailLabel, _FailCode,
|
|
_GotoFailCode),
|
|
map.lookup(CaseIdToLabelMap, CaseId, CaseLabel),
|
|
CaseCodeAddr = code_label(CaseLabel),
|
|
CaseCode = singleton(
|
|
llds_instr(if_val(CondRval, CaseCodeAddr), "if match; goto case")
|
|
)
|
|
% If the test fails, fall through
|
|
;
|
|
Info = stsi_lookup(LookupInfo),
|
|
get_next_label(FallThroughLabel, !CI),
|
|
CaseIdRegLval = LookupInfo ^ stsil_case_id_reg,
|
|
AfterCodeAddr = LookupInfo ^ stsil_after_code_addr,
|
|
negate_rval(CondRval, NegCondRval),
|
|
CaseId = case_id(CaseIdNum),
|
|
CaseIdRval = const(llconst_int(CaseIdNum)),
|
|
CaseCode = from_list([
|
|
llds_instr(if_val(NegCondRval, code_label(FallThroughLabel)),
|
|
"if not match; fall through"),
|
|
llds_instr(assign(CaseIdRegLval, CaseIdRval), "assign CaseId"),
|
|
llds_instr(goto(AfterCodeAddr), "match; goto end"),
|
|
llds_instr(label(FallThroughLabel), "fall through label")
|
|
])
|
|
).
|
|
|
|
:- pred generate_trie_goto_fail_code(string_trie_switch_info::in,
|
|
llds_code::out) is det.
|
|
|
|
generate_trie_goto_fail_code(Info, GotoFailCode) :-
|
|
(
|
|
Info = stsi_jump(JumpInfo),
|
|
GotoFailCode = JumpInfo ^ stsij_goto_fail_code
|
|
;
|
|
Info = stsi_lookup(LookupInfo),
|
|
AfterCodeAddr = LookupInfo ^ stsil_after_code_addr,
|
|
GotoFailCode = singleton(
|
|
llds_instr(goto(AfterCodeAddr),
|
|
"no match; goto after with no case id set")
|
|
)
|
|
).
|
|
|
|
:- pred get_encoding(string_trie_switch_info::in, string_encoding::out) is det.
|
|
|
|
get_encoding(Info, Encoding) :-
|
|
(
|
|
Info = stsi_jump(JumpInfo),
|
|
Encoding = JumpInfo ^ stsij_encoding
|
|
;
|
|
Info = stsi_lookup(LookupInfo),
|
|
Encoding = LookupInfo ^ stsil_encoding
|
|
).
|
|
|
|
:- pred get_code_unit_reg(string_trie_switch_info::in, lval::out) is det.
|
|
|
|
get_code_unit_reg(Info, CodeUnitReg) :-
|
|
(
|
|
Info = stsi_jump(JumpInfo),
|
|
CodeUnitReg = JumpInfo ^ stsij_code_unit_reg
|
|
;
|
|
Info = stsi_lookup(LookupInfo),
|
|
CodeUnitReg = LookupInfo ^ stsil_code_unit_reg
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
generate_string_hash_jump_switch(VarRval, VarName, TaggedCases,
|
|
CodeModel, CanFail, SwitchGoalInfo, EndLabel, MaybeEnd, Code,
|
|
!CI, CLD) :-
|
|
init_string_hash_switch_info(CanFail, HashSwitchInfo, !CI, CLD),
|
|
BranchStart = HashSwitchInfo ^ shsi_branch_start,
|
|
Params = represent_params(VarName, SwitchGoalInfo, CodeModel, BranchStart,
|
|
EndLabel),
|
|
|
|
% Generate code for the cases, and remember the label of each case.
|
|
map.init(CaseLabelMap0),
|
|
represent_tagged_cases_in_string_hash_switch(Params, TaggedCases,
|
|
[], StrsLabels, CaseLabelMap0, CaseLabelMap, no, MaybeEnd, !CI),
|
|
|
|
% Compute the hash table.
|
|
construct_string_hash_cases(StrsLabels, allow_doubling,
|
|
TableSize, HashSlotsMap, HashOp, NumCollisions),
|
|
HashMask = TableSize - 1,
|
|
|
|
% Generate the data structures for the hash table.
|
|
FailLabel = HashSwitchInfo ^ shsi_fail_label,
|
|
construct_string_hash_jump_vectors(0, TableSize, HashSlotsMap, FailLabel,
|
|
NumCollisions, cord.init, MainTableRvalsCord,
|
|
cord.init, MaybeTargetsCord),
|
|
MainTableRvals = cord.list(MainTableRvalsCord),
|
|
MaybeTargets = cord.list(MaybeTargetsCord),
|
|
|
|
% Generate the code for the hash table lookup.
|
|
( if NumCollisions = 0 then
|
|
NumColumns = 1,
|
|
MainRowTypes = [lt_string],
|
|
ArrayElemTypes = [scalar_elem_string]
|
|
else
|
|
NumColumns = 2,
|
|
MainRowTypes = [lt_string, lt_int(int_type_int)],
|
|
ArrayElemTypes = [scalar_elem_string, scalar_elem_int]
|
|
),
|
|
add_vector_static_cell(MainRowTypes, MainTableRvals, MainTableDataId, !CI),
|
|
MainTableAddrRval = const(llconst_data_addr(MainTableDataId)),
|
|
ArrayElemType = array_elem_struct(ArrayElemTypes),
|
|
|
|
SlotReg = HashSwitchInfo ^ shsi_slot_reg,
|
|
MaxIndex = list.length(MaybeTargets) - 1,
|
|
MatchCode = from_list([
|
|
% See the comment at the top of the module about why we use
|
|
% a computed_goto here.
|
|
llds_instr(computed_goto(lval(SlotReg), yes(MaxIndex), MaybeTargets),
|
|
"jump to the corresponding code")
|
|
]),
|
|
|
|
% Generate the code for the cases, and put it all together.
|
|
add_not_yet_included_cases(CasesCode, CaseLabelMap, _),
|
|
generate_string_hash_switch_search(HashSwitchInfo, VarRval,
|
|
MainTableAddrRval, ArrayElemType, NumColumns, HashOp, HashMask,
|
|
NumCollisions, EndLabel, "jump", CasesCode, MatchCode, Code).
|
|
|
|
:- pred construct_string_hash_jump_vectors(int::in, int::in,
|
|
map(int, string_hash_slot(label))::in, label::in, int::in,
|
|
cord(list(rval))::in, cord(list(rval))::out,
|
|
cord(maybe(label))::in, cord(maybe(label))::out) is det.
|
|
|
|
construct_string_hash_jump_vectors(Slot, TableSize, HashSlotMap, FailLabel,
|
|
NumCollisions, !TableRvalsCord, !MaybeTargetsCord) :-
|
|
( if Slot = TableSize then
|
|
true
|
|
else
|
|
( if map.search(HashSlotMap, Slot, SlotInfo) then
|
|
SlotInfo = string_hash_slot(String, Next, CaseLabel),
|
|
NextSlotRval = const(llconst_int(Next)),
|
|
StringRval = const(llconst_string(String)),
|
|
Target = CaseLabel
|
|
else
|
|
StringRval = const(llconst_int(0)),
|
|
NextSlotRval = const(llconst_int(-2)),
|
|
Target = FailLabel
|
|
),
|
|
( if NumCollisions = 0 then
|
|
TableRowRvals = [StringRval]
|
|
else
|
|
TableRowRvals = [StringRval, NextSlotRval]
|
|
),
|
|
cord.snoc(TableRowRvals, !TableRvalsCord),
|
|
cord.snoc(yes(Target), !MaybeTargetsCord),
|
|
construct_string_hash_jump_vectors(Slot + 1, TableSize, HashSlotMap,
|
|
FailLabel, NumCollisions, !TableRvalsCord, !MaybeTargetsCord)
|
|
).
|
|
|
|
:- pred represent_tagged_cases_in_string_hash_switch(represent_params::in,
|
|
list(tagged_case)::in,
|
|
assoc_list(string, label)::in, assoc_list(string, label)::out,
|
|
case_label_map::in, case_label_map::out,
|
|
branch_end::in, branch_end::out, code_info::in, code_info::out) is det.
|
|
|
|
represent_tagged_cases_in_string_hash_switch(_, [],
|
|
!StrsLabels, !CaseLabelMap, !MaybeEnd, !CI).
|
|
represent_tagged_cases_in_string_hash_switch(Params,
|
|
[TaggedCase | TaggedCases],
|
|
!StrsLabels, !CaseLabelMap, !MaybeEnd, !CI) :-
|
|
represent_tagged_case_for_llds(Params, TaggedCase, Label,
|
|
!CaseLabelMap, !MaybeEnd, !CI, unit, _),
|
|
TaggedCase = tagged_case(MainTaggedConsId, OtherTaggedConsIds, _, _),
|
|
record_label_for_string(Label, MainTaggedConsId, !StrsLabels),
|
|
list.foldl(record_label_for_string(Label), OtherTaggedConsIds,
|
|
!StrsLabels),
|
|
represent_tagged_cases_in_string_hash_switch(Params, TaggedCases,
|
|
!StrsLabels, !CaseLabelMap, !MaybeEnd, !CI).
|
|
|
|
:- pred record_label_for_string(label::in, tagged_cons_id::in,
|
|
assoc_list(string, label)::in, assoc_list(string, label)::out) is det.
|
|
|
|
record_label_for_string(Label, TaggedConsId, !StrsLabels) :-
|
|
TaggedConsId = tagged_cons_id(_ConsId, Tag),
|
|
( if Tag = string_tag(String) then
|
|
!:StrsLabels = [String - Label | !.StrsLabels]
|
|
else
|
|
unexpected($pred, "non-string tag")
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
generate_string_hash_lookup_switch(VarRval, LookupSwitchInfo,
|
|
CanFail, EndLabel, !:MaybeEnd, Code, !:CI) :-
|
|
LookupSwitchInfo = lookup_switch_info(KeyToCaseIdMap, CaseConsts,
|
|
OutVars, OutTypes, EndBranch, !:MaybeEnd, !:CI, CLD),
|
|
(
|
|
CaseConsts = all_one_soln(CaseIdToValuesMap),
|
|
compose_maps(KeyToCaseIdMap, CaseIdToValuesMap, KeyToValuesMap),
|
|
map.to_assoc_list(KeyToValuesMap, KeyValuesAL),
|
|
generate_string_hash_simple_lookup_switch(VarRval, KeyValuesAL,
|
|
OutVars, OutTypes, CanFail, EndLabel, EndBranch, Code,
|
|
!MaybeEnd, !CI, CLD)
|
|
;
|
|
CaseConsts = some_several_solns(CaseIdToSolnsMap,
|
|
CaseConstsSeveralLlds),
|
|
compose_maps(KeyToCaseIdMap, CaseIdToSolnsMap, KeyToSolnsMap),
|
|
map.to_assoc_list(KeyToSolnsMap, KeySolnsAL),
|
|
generate_string_hash_several_soln_lookup_switch(CaseConstsSeveralLlds,
|
|
VarRval, KeySolnsAL, OutVars, OutTypes, CanFail, EndLabel,
|
|
EndBranch, Code, !MaybeEnd, !CI, CLD)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred generate_string_hash_simple_lookup_switch(rval::in,
|
|
assoc_list(string, list(rval))::in, list(prog_var)::in,
|
|
list(llds_type)::in, can_fail::in, label::in, end_branch_info::in,
|
|
llds_code::out, branch_end::in, branch_end::out,
|
|
code_info::in, code_info::out, code_loc_dep::in) is det.
|
|
|
|
generate_string_hash_simple_lookup_switch(VarRval, CaseValues,
|
|
OutVars, OutTypes, CanFail, EndLabel, EndBranch, Code,
|
|
!MaybeEnd, !CI, CLD) :-
|
|
% This predicate, generate_string_hash_several_soln_lookup_switch,
|
|
% and generate_string_hash_lookup_switch do similar tasks using
|
|
% similar code, so if you need to update one, you probably need to
|
|
% update them all.
|
|
init_string_hash_switch_info(CanFail, HashSwitchInfo, !CI, CLD),
|
|
|
|
% Compute the hash table.
|
|
construct_string_hash_cases(CaseValues, allow_doubling, TableSize,
|
|
HashSlotsMap, HashOp, NumCollisions),
|
|
HashMask = TableSize - 1,
|
|
|
|
list.length(OutVars, NumOutVars),
|
|
% For the LLDS backend, array indexing ops don't need the element types,
|
|
% so it is ok to lie for OutElemTypes.
|
|
list.duplicate(NumOutVars, scalar_elem_generic, OutElemTypes),
|
|
DummyOutRvals = list.map(default_value_for_type, OutTypes),
|
|
( if NumCollisions = 0 then
|
|
NumPrevColumns = 1,
|
|
NumColumns = 1 + NumOutVars,
|
|
ArrayElemTypes = [scalar_elem_string | OutElemTypes],
|
|
MainRowTypes = [lt_string | OutTypes]
|
|
else
|
|
NumPrevColumns = 2,
|
|
NumColumns = 2 + NumOutVars,
|
|
ArrayElemTypes = [scalar_elem_string, scalar_elem_int | OutElemTypes],
|
|
MainRowTypes = [lt_string, lt_int(int_type_int) | OutTypes]
|
|
),
|
|
ArrayElemType = array_elem_struct(ArrayElemTypes),
|
|
|
|
% Generate the static lookup table for this switch.
|
|
construct_string_hash_simple_lookup_vector(0, TableSize, HashSlotsMap,
|
|
NumCollisions, DummyOutRvals, cord.init, MainTableRvalsCord),
|
|
MainTableRvals = cord.list(MainTableRvalsCord),
|
|
add_vector_static_cell(MainRowTypes, MainTableRvals,
|
|
MainTableDataId, !CI),
|
|
MainTableAddrRval = const(llconst_data_addr(MainTableDataId)),
|
|
|
|
(
|
|
OutVars = [],
|
|
generate_single_soln_table_lookup_code_no_vars(EndBranch, LookupCode,
|
|
!MaybeEnd, CLD)
|
|
;
|
|
OutVars = [_ | _],
|
|
RowStartRegLval = HashSwitchInfo ^ shsi_row_start_reg,
|
|
MainRowSelect = main_row_start_offset_reg(lval(RowStartRegLval)),
|
|
generate_single_soln_table_lookup_code_some_vars(MainTableDataId,
|
|
MainRowSelect, NumPrevColumns, OutVars, EndBranch, LookupCode,
|
|
!MaybeEnd, !.CI, CLD)
|
|
),
|
|
|
|
append_goto_end(EndLabel, LookupCode, LookupGotoEndCode),
|
|
generate_string_hash_switch_search(HashSwitchInfo, VarRval,
|
|
MainTableAddrRval, ArrayElemType, NumColumns, HashOp, HashMask,
|
|
NumCollisions, EndLabel, "single soln lookup",
|
|
empty, LookupGotoEndCode, Code).
|
|
|
|
:- pred construct_string_hash_simple_lookup_vector(int::in, int::in,
|
|
map(int, string_hash_slot(list(rval)))::in, int::in, list(rval)::in,
|
|
cord(list(rval))::in, cord(list(rval))::out) is det.
|
|
|
|
construct_string_hash_simple_lookup_vector(Slot, TableSize, HashSlotMap,
|
|
NumCollisions, DummyOutRvals, !RvalsCord) :-
|
|
( if Slot = TableSize then
|
|
true
|
|
else
|
|
( if map.search(HashSlotMap, Slot, SlotInfo) then
|
|
SlotInfo = string_hash_slot(String, Next, OutVarRvals),
|
|
NextSlotRval = const(llconst_int(Next)),
|
|
StringRval = const(llconst_string(String))
|
|
else
|
|
StringRval = const(llconst_int(0)),
|
|
NextSlotRval = const(llconst_int(-2)),
|
|
OutVarRvals = DummyOutRvals
|
|
),
|
|
( if NumCollisions = 0 then
|
|
RowRvals = [StringRval | OutVarRvals]
|
|
else
|
|
RowRvals = [StringRval, NextSlotRval | OutVarRvals]
|
|
),
|
|
cord.snoc(RowRvals, !RvalsCord),
|
|
construct_string_hash_simple_lookup_vector(Slot + 1, TableSize,
|
|
HashSlotMap, NumCollisions, DummyOutRvals, !RvalsCord)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred generate_string_hash_several_soln_lookup_switch(
|
|
case_consts_several_llds::in, rval::in,
|
|
assoc_list(string, soln_consts(rval))::in,
|
|
list(prog_var)::in, list(llds_type)::in,
|
|
can_fail::in, label::in, end_branch_info::in,
|
|
llds_code::out, branch_end::in, branch_end::out,
|
|
code_info::in, code_info::out, code_loc_dep::in) is det.
|
|
|
|
generate_string_hash_several_soln_lookup_switch(CaseConstsSeveralLlds,
|
|
VarRval, CaseSolns, OutVars, OutTypes, CanFail, EndLabel, EndBranch,
|
|
Code, !MaybeEnd, !CI, !.CLD) :-
|
|
% This predicate, generate_string_hash_simple_lookup_switch,
|
|
% and generate_string_hash_lookup_switch do similar tasks using
|
|
% similar code, so if you need to update one, you probably need to
|
|
% update them all.
|
|
init_string_hash_switch_info(CanFail, HashSwitchInfo, !CI, !.CLD),
|
|
|
|
% Compute the hash table.
|
|
construct_string_hash_cases(CaseSolns, allow_doubling, TableSize,
|
|
HashSlotsMap, HashOp, NumCollisions),
|
|
HashMask = TableSize - 1,
|
|
|
|
list.length(OutVars, NumOutVars),
|
|
% For the LLDS backend, array indexing ops don't need the element types,
|
|
% so it is ok to lie for OutElemTypes.
|
|
list.duplicate(NumOutVars, scalar_elem_generic, OutElemTypes),
|
|
( if NumCollisions = 0 then
|
|
NumColumns = 3 + NumOutVars,
|
|
NumPrevColumns = 1,
|
|
ArrayElemTypes = [scalar_elem_string,
|
|
scalar_elem_int, scalar_elem_int | OutElemTypes],
|
|
MainRowTypes = [lt_string, lt_int(int_type_int),
|
|
lt_int(int_type_int) | OutTypes]
|
|
else
|
|
NumColumns = 4 + NumOutVars,
|
|
NumPrevColumns = 2,
|
|
ArrayElemTypes = [scalar_elem_string, scalar_elem_int,
|
|
scalar_elem_int, scalar_elem_int | OutElemTypes],
|
|
MainRowTypes = [lt_string, lt_int(int_type_int), lt_int(int_type_int),
|
|
lt_int(int_type_int) | OutTypes]
|
|
),
|
|
ArrayElemType = array_elem_struct(ArrayElemTypes),
|
|
|
|
% Generate the static lookup table for this switch.
|
|
InitLaterSolnRowNumber = 1,
|
|
DummyOutRvals = list.map(default_value_for_type, OutTypes),
|
|
LaterSolnsRowsCord0 = singleton(DummyOutRvals),
|
|
construct_string_hash_several_soln_lookup_vector(0, TableSize,
|
|
HashSlotsMap, DummyOutRvals, NumOutVars, NumCollisions,
|
|
cord.init, MainTableRvalsCord, InitLaterSolnRowNumber,
|
|
LaterSolnsRowsCord0, LaterSolnsRowsCord,
|
|
0, OneSolnCaseCount, 0, SeveralSolnsCaseCount),
|
|
MainTableRvals = cord.list(MainTableRvalsCord),
|
|
LaterSolnsRows = cord.list(LaterSolnsRowsCord),
|
|
|
|
add_vector_static_cell(MainRowTypes, MainTableRvals, MainTableDataId, !CI),
|
|
add_vector_static_cell(OutTypes, LaterSolnsRows, LaterTableDataId, !CI),
|
|
MainTableAddrRval = const(llconst_data_addr(MainTableDataId)),
|
|
LaterTableAddrRval = const(llconst_data_addr(LaterTableDataId)),
|
|
|
|
RowStartRegLval = HashSwitchInfo ^ shsi_row_start_reg,
|
|
MainRowSelect = main_row_start_offset_reg(lval(RowStartRegLval)),
|
|
acquire_and_setup_lookup_base_reg(MainTableDataId, EndBranch,
|
|
MainRowSelect, BaseRegLval, SetBaseRegCode, !CLD),
|
|
|
|
generate_multi_soln_table_lookup_code(CaseConstsSeveralLlds,
|
|
OneSolnCaseCount - kind_one_soln,
|
|
[SeveralSolnsCaseCount - kind_several_solns],
|
|
NumPrevColumns, OutVars, EndLabel, BaseRegLval, LaterTableAddrRval,
|
|
EndBranch, LookupResultsCode, !MaybeEnd, !CI, !.CLD),
|
|
MatchCode = SetBaseRegCode ++ LookupResultsCode,
|
|
generate_string_hash_switch_search(HashSwitchInfo, VarRval,
|
|
MainTableAddrRval, ArrayElemType, NumColumns, HashOp, HashMask,
|
|
NumCollisions, EndLabel, "multi soln lookup", empty, MatchCode, Code).
|
|
|
|
:- pred construct_string_hash_several_soln_lookup_vector(int::in, int::in,
|
|
map(int, string_hash_slot(soln_consts(rval)))::in, list(rval)::in,
|
|
int::in, int::in, cord(list(rval))::in, cord(list(rval))::out,
|
|
int::in, cord(list(rval))::in, cord(list(rval))::out,
|
|
int::in, int::out, int::in, int::out) is det.
|
|
|
|
construct_string_hash_several_soln_lookup_vector(Slot, TableSize, HashSlotMap,
|
|
DummyOutRvals, NumOutVars, NumCollisions,
|
|
!MainRvalsCord, !.LaterNextRow, !LaterSolnRowsCord,
|
|
!OneSolnCaseCount, !SeveralSolnsCaseCount) :-
|
|
( if Slot = TableSize then
|
|
true
|
|
else
|
|
( if map.search(HashSlotMap, Slot, SlotInfo) then
|
|
SlotInfo = string_hash_slot(String, Next, Soln),
|
|
StringRval = const(llconst_string(String)),
|
|
NextSlotRval = const(llconst_int(Next)),
|
|
(
|
|
Soln = one_soln(OutVarRvals),
|
|
!:OneSolnCaseCount = !.OneSolnCaseCount + 1,
|
|
ZeroRval = const(llconst_int(0)),
|
|
% The first ZeroRval means there is exactly one solution for
|
|
% this case; the second ZeroRval is a dummy that won't be
|
|
% referenced.
|
|
MainRowTail = [ZeroRval, ZeroRval | OutVarRvals],
|
|
( if NumCollisions = 0 then
|
|
MainRowRvals = [StringRval | MainRowTail]
|
|
else
|
|
MainRowRvals = [StringRval, NextSlotRval | MainRowTail]
|
|
)
|
|
;
|
|
Soln = several_solns(FirstSolnRvals, LaterSolns),
|
|
!:SeveralSolnsCaseCount = !.SeveralSolnsCaseCount + 1,
|
|
list.length(LaterSolns, NumLaterSolns),
|
|
FirstRowOffset = !.LaterNextRow * NumOutVars,
|
|
LastRowOffset = (!.LaterNextRow + NumLaterSolns - 1)
|
|
* NumOutVars,
|
|
FirstRowRval = const(llconst_int(FirstRowOffset)),
|
|
LastRowRval = const(llconst_int(LastRowOffset)),
|
|
MainRowTail = [FirstRowRval, LastRowRval | FirstSolnRvals],
|
|
( if NumCollisions = 0 then
|
|
MainRowRvals = [StringRval | MainRowTail]
|
|
else
|
|
MainRowRvals = [StringRval, NextSlotRval | MainRowTail]
|
|
),
|
|
!:LaterNextRow = !.LaterNextRow + NumLaterSolns,
|
|
!:LaterSolnRowsCord =
|
|
!.LaterSolnRowsCord ++ from_list(LaterSolns)
|
|
)
|
|
else
|
|
% The zero in the StringRval slot means that this bucket is empty.
|
|
StringRval = const(llconst_int(0)),
|
|
NextSlotRval = const(llconst_int(-2)),
|
|
ZeroRval = const(llconst_int(0)),
|
|
MainRowTail = [ZeroRval, ZeroRval | DummyOutRvals],
|
|
( if NumCollisions = 0 then
|
|
MainRowRvals = [StringRval | MainRowTail]
|
|
else
|
|
MainRowRvals = [StringRval, NextSlotRval | MainRowTail]
|
|
)
|
|
),
|
|
cord.snoc(MainRowRvals, !MainRvalsCord),
|
|
construct_string_hash_several_soln_lookup_vector(Slot + 1, TableSize,
|
|
HashSlotMap, DummyOutRvals, NumOutVars, NumCollisions,
|
|
!MainRvalsCord, !.LaterNextRow, !LaterSolnRowsCord,
|
|
!OneSolnCaseCount, !SeveralSolnsCaseCount)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- type string_hash_switch_info
|
|
---> string_hash_switch_info(
|
|
shsi_slot_reg :: lval,
|
|
shsi_row_start_reg :: lval,
|
|
shsi_string_reg :: lval,
|
|
|
|
shsi_loop_start_label :: label,
|
|
shsi_no_match_label :: label,
|
|
shsi_fail_label :: label,
|
|
|
|
shsi_branch_start :: position_info,
|
|
shsi_fail_code :: llds_code
|
|
).
|
|
|
|
:- pred init_string_hash_switch_info(can_fail::in,
|
|
string_hash_switch_info::out,
|
|
code_info::in, code_info::out, code_loc_dep::in) is det.
|
|
|
|
init_string_hash_switch_info(CanFail, Info, !CI, !.CLD) :-
|
|
% See the comment about acquire_reg/release_reg in the
|
|
% init_string_binary_switch_info predicate below.
|
|
acquire_reg(reg_r, SlotReg, !CLD),
|
|
acquire_reg(reg_r, RowStartReg, !CLD),
|
|
acquire_reg(reg_r, StringReg, !CLD),
|
|
release_reg(SlotReg, !CLD),
|
|
release_reg(RowStartReg, !CLD),
|
|
release_reg(StringReg, !CLD),
|
|
|
|
get_next_label(LoopStartLabel, !CI),
|
|
get_next_label(FailLabel, !CI),
|
|
get_next_label(NoMatchLabel, !CI),
|
|
|
|
% We must generate the failure code in the context in which
|
|
% none of the switch arms have been executed yet.
|
|
remember_position(!.CLD, BranchStart),
|
|
generate_string_switch_fail(CanFail, FailCode, !.CI, !.CLD),
|
|
|
|
Info = string_hash_switch_info(SlotReg, RowStartReg, StringReg,
|
|
LoopStartLabel, NoMatchLabel, FailLabel, BranchStart, FailCode).
|
|
|
|
:- pred generate_string_hash_switch_search(string_hash_switch_info::in,
|
|
rval::in, rval::in, array_elem_type::in, int::in, unary_op::in, int::in,
|
|
int::in, label::in, string::in, llds_code::in, llds_code::in,
|
|
llds_code::out) is det.
|
|
|
|
generate_string_hash_switch_search(Info, VarRval, TableAddrRval,
|
|
ArrayElemType, NumColumns, HashOp, HashMask, NumCollisions,
|
|
EndLabel, KindStr, CasesCode, MatchCode, Code) :-
|
|
SlotReg = Info ^ shsi_slot_reg,
|
|
RowStartReg = Info ^ shsi_row_start_reg,
|
|
StringReg = Info ^ shsi_string_reg,
|
|
LoopStartLabel = Info ^ shsi_loop_start_label,
|
|
NoMatchLabel = Info ^ shsi_no_match_label,
|
|
FailLabel = Info ^ shsi_fail_label,
|
|
FailCode = Info ^ shsi_fail_code,
|
|
|
|
( if NumCollisions = 0 then
|
|
( if NumColumns = 1 then
|
|
BaseReg = SlotReg,
|
|
MultiplyInstrs = []
|
|
else
|
|
BaseReg = RowStartReg,
|
|
MultiplyInstrs = [
|
|
llds_instr(
|
|
assign(RowStartReg,
|
|
binop(int_arith(int_type_int, ao_mul),
|
|
lval(SlotReg), const(llconst_int(NumColumns)))),
|
|
"find the start of the row")
|
|
]
|
|
),
|
|
HashSearchCode =
|
|
from_list([
|
|
llds_instr(
|
|
assign(SlotReg,
|
|
binop(bitwise_and(int_type_int),
|
|
unop(HashOp, VarRval),
|
|
const(llconst_int(HashMask)))),
|
|
"compute the hash value of the input string") |
|
|
MultiplyInstrs]) ++
|
|
from_list([
|
|
llds_instr(
|
|
assign(StringReg,
|
|
binop(array_index(ArrayElemType),
|
|
TableAddrRval, lval(BaseReg))),
|
|
"lookup the string for this hash slot"),
|
|
llds_instr(
|
|
if_val(
|
|
binop(logical_or,
|
|
binop(int_cmp(int_type_int, eq),
|
|
lval(StringReg), const(llconst_int(0))),
|
|
binop(str_cmp(ne), lval(StringReg), VarRval)),
|
|
code_label(FailLabel)),
|
|
"did we find a match? nofulljump")
|
|
]) ++
|
|
MatchCode ++
|
|
from_list([
|
|
llds_instr(label(FailLabel),
|
|
"handle the failure of the table search")
|
|
])
|
|
else
|
|
HashSearchCode =
|
|
from_list([
|
|
llds_instr(
|
|
assign(SlotReg,
|
|
binop(bitwise_and(int_type_int),
|
|
unop(HashOp, VarRval),
|
|
const(llconst_int(HashMask)))),
|
|
"compute the hash value of the input string"),
|
|
llds_instr(label(LoopStartLabel),
|
|
"begin hash chain loop, nofulljump"),
|
|
llds_instr(
|
|
assign(RowStartReg,
|
|
binop(int_arith(int_type_int, ao_mul),
|
|
lval(SlotReg), const(llconst_int(NumColumns)))),
|
|
"find the start of the row"),
|
|
llds_instr(
|
|
assign(StringReg,
|
|
binop(array_index(ArrayElemType),
|
|
TableAddrRval, lval(RowStartReg))),
|
|
"lookup the string for this hash slot"),
|
|
llds_instr(
|
|
if_val(
|
|
binop(logical_or,
|
|
binop(int_cmp(int_type_int, eq),
|
|
lval(StringReg), const(llconst_int(0))),
|
|
binop(str_cmp(ne), lval(StringReg), VarRval)),
|
|
code_label(NoMatchLabel)),
|
|
"did we find a match? nofulljump")
|
|
]) ++
|
|
MatchCode ++
|
|
from_list([
|
|
llds_instr(label(NoMatchLabel),
|
|
"no match yet, nofulljump"),
|
|
llds_instr(
|
|
assign(SlotReg,
|
|
binop(array_index(ArrayElemType),
|
|
TableAddrRval,
|
|
binop(int_arith(int_type_int, ao_add),
|
|
lval(RowStartReg), const(llconst_int(1))))),
|
|
"get next slot in hash chain"),
|
|
llds_instr(
|
|
if_val(
|
|
binop(int_cmp(int_type_int, ge),
|
|
lval(SlotReg), const(llconst_int(0))),
|
|
code_label(LoopStartLabel)),
|
|
"if not at the end of the chain, keep searching"),
|
|
llds_instr(label(FailLabel),
|
|
"handle the failure of the table search")
|
|
])
|
|
),
|
|
|
|
MainCode = HashSearchCode ++ FailCode ++ CasesCode,
|
|
string.format("string hash %s switch", [s(KindStr)], SwitchKindStr),
|
|
add_switch_kind_comment_and_end_label(SwitchKindStr, EndLabel,
|
|
MainCode, Code).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
generate_string_binary_jump_switch(VarRval, VarName, TaggedCases,
|
|
CodeModel, CanFail, SwitchGoalInfo, EndLabel, MaybeEnd, Code,
|
|
!CI, CLD) :-
|
|
init_string_binary_switch_info(CanFail, BinarySwitchInfo, !CI, CLD),
|
|
BranchStart = BinarySwitchInfo ^ sbsi_branch_start,
|
|
Params = represent_params(VarName, SwitchGoalInfo, CodeModel, BranchStart,
|
|
EndLabel),
|
|
|
|
% Compute and generate the binary search table.
|
|
map.init(CaseLabelMap0),
|
|
string_binary_cases(TaggedCases, represent_tagged_case_for_llds(Params),
|
|
CaseLabelMap0, CaseLabelMap, no, MaybeEnd, !CI, unit, _, SortedTable),
|
|
|
|
gen_string_binary_jump_slots(SortedTable,
|
|
cord.init, MainTableRvalsCord, cord.init, TargetsCord,
|
|
0, TableSize),
|
|
MainTableRvals = cord.list(MainTableRvalsCord),
|
|
Targets = cord.list(TargetsCord),
|
|
NumColumns = 2,
|
|
MainRowTypes = [lt_string, lt_int(int_type_int)],
|
|
add_vector_static_cell(MainRowTypes, MainTableRvals, TableAddr, !CI),
|
|
ArrayElemTypes = [scalar_elem_string, scalar_elem_int],
|
|
ArrayElemType = array_elem_struct(ArrayElemTypes),
|
|
TableAddrRval = const(llconst_data_addr(TableAddr)),
|
|
|
|
generate_string_binary_switch_search(BinarySwitchInfo,
|
|
VarRval, TableAddrRval, ArrayElemType, TableSize, NumColumns,
|
|
BinarySearchCode),
|
|
|
|
MidReg = BinarySwitchInfo ^ sbsi_mid_reg,
|
|
% See the comment at the top about why we use a computed_goto here.
|
|
ComputedGotoCode = singleton(
|
|
llds_instr(
|
|
computed_goto(
|
|
binop(array_index(ArrayElemType),
|
|
TableAddrRval,
|
|
binop(int_arith(int_type_int, ao_add),
|
|
binop(int_arith(int_type_int, ao_mul),
|
|
lval(MidReg),
|
|
const(llconst_int(NumColumns))),
|
|
const(llconst_int(1)))),
|
|
yes(TableSize),
|
|
Targets),
|
|
"jump to the matching case")
|
|
),
|
|
|
|
% Generate the code for the cases, and put it all together.
|
|
add_not_yet_included_cases(CasesCode, CaseLabelMap, _),
|
|
MainCode = BinarySearchCode ++ ComputedGotoCode ++ CasesCode,
|
|
SwitchKindStr = "string binary jump switch",
|
|
add_switch_kind_comment_and_end_label(SwitchKindStr, EndLabel,
|
|
MainCode, Code).
|
|
|
|
:- pred gen_string_binary_jump_slots(assoc_list(string, label)::in,
|
|
cord(list(rval))::in, cord(list(rval))::out,
|
|
cord(maybe(label))::in, cord(maybe(label))::out,
|
|
int::in, int::out) is det.
|
|
|
|
gen_string_binary_jump_slots([],
|
|
!TableRvalsCord, !MaybeTargetsCord, !CurIndex).
|
|
gen_string_binary_jump_slots([Str - Label | StrLabels],
|
|
!TableRvalsCord, !MaybeTargetsCord, !CurIndex) :-
|
|
Row = [const(llconst_string(Str)), const(llconst_int(!.CurIndex))],
|
|
cord.snoc(Row, !TableRvalsCord),
|
|
cord.snoc(yes(Label), !MaybeTargetsCord),
|
|
!:CurIndex = !.CurIndex + 1,
|
|
gen_string_binary_jump_slots(StrLabels,
|
|
!TableRvalsCord, !MaybeTargetsCord, !CurIndex).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
generate_string_binary_lookup_switch(VarRval, LookupSwitchInfo,
|
|
CanFail, EndLabel, !:MaybeEnd, Code, !:CI) :-
|
|
LookupSwitchInfo = lookup_switch_info(KeyToCaseIdMap, CaseConsts,
|
|
OutVars, OutTypes, EndBranch, !:MaybeEnd, !:CI, CLD),
|
|
(
|
|
CaseConsts = all_one_soln(CaseIdToValuesMap),
|
|
compose_maps(KeyToCaseIdMap, CaseIdToValuesMap, KeyToValuesMap),
|
|
map.to_assoc_list(KeyToValuesMap, KeyValuesAL),
|
|
generate_string_binary_simple_lookup_switch(VarRval, KeyValuesAL,
|
|
OutVars, OutTypes, CanFail, EndLabel, EndBranch, Code,
|
|
!MaybeEnd, !CI, CLD)
|
|
;
|
|
CaseConsts = some_several_solns(CaseIdToSolnsMap,
|
|
CaseConstsSeveralLlds),
|
|
compose_maps(KeyToCaseIdMap, CaseIdToSolnsMap, KeyToSolnsMap),
|
|
map.to_assoc_list(KeyToSolnsMap, KeySolnsAL),
|
|
generate_string_binary_several_soln_lookup_switch(
|
|
CaseConstsSeveralLlds, VarRval, KeySolnsAL, OutVars, OutTypes,
|
|
CanFail, EndLabel, EndBranch, Code, !MaybeEnd, !CI, CLD)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred generate_string_binary_simple_lookup_switch(rval::in,
|
|
assoc_list(string, list(rval))::in, list(prog_var)::in,
|
|
list(llds_type)::in, can_fail::in, label::in, end_branch_info::in,
|
|
llds_code::out, branch_end::in, branch_end::out,
|
|
code_info::in, code_info::out, code_loc_dep::in) is det.
|
|
|
|
generate_string_binary_simple_lookup_switch(VarRval, CaseValues,
|
|
OutVars, OutTypes, CanFail, EndLabel, EndBranch, Code,
|
|
!MaybeEnd, !CI, CLD) :-
|
|
% This predicate, generate_string_binary_several_soln_lookup_switch,
|
|
% and generate_string_binary_lookup_switch do similar tasks using
|
|
% similar code, so if you need to update one, you probably need to
|
|
% update them all.
|
|
|
|
init_string_binary_switch_info(CanFail, BinarySwitchInfo, !CI, CLD),
|
|
|
|
list.length(CaseValues, TableSize),
|
|
list.length(OutVars, NumOutVars),
|
|
% For the LLDS backend, array indexing ops don't need the element types,
|
|
% so it is ok to lie here.
|
|
list.duplicate(NumOutVars, scalar_elem_generic, OutElemTypes),
|
|
ArrayElemTypes = [scalar_elem_string | OutElemTypes],
|
|
ArrayElemType = array_elem_struct(ArrayElemTypes),
|
|
NumPrevColumns = 1,
|
|
NumColumns = NumPrevColumns + NumOutVars,
|
|
|
|
% Generate the static lookup table for this switch.
|
|
construct_string_binary_simple_lookup_vector(CaseValues,
|
|
cord.init, MainTableRvalsCord),
|
|
MainTableRvals = cord.list(MainTableRvalsCord),
|
|
MainRowTypes = [lt_string | OutTypes],
|
|
add_vector_static_cell(MainRowTypes, MainTableRvals, MainTableDataId, !CI),
|
|
MainTableAddrRval = const(llconst_data_addr(MainTableDataId)),
|
|
|
|
generate_string_binary_switch_search(BinarySwitchInfo, VarRval,
|
|
MainTableAddrRval, ArrayElemType, TableSize, NumColumns,
|
|
BinarySearchCode),
|
|
(
|
|
OutVars = [],
|
|
generate_single_soln_table_lookup_code_no_vars(EndBranch, LookupCode,
|
|
!MaybeEnd, CLD)
|
|
;
|
|
OutVars = [_ | _],
|
|
MidRegLval = BinarySwitchInfo ^ sbsi_mid_reg,
|
|
MainRowSelect = main_row_number_reg(lval(MidRegLval), MainRowTypes),
|
|
generate_single_soln_table_lookup_code_some_vars(MainTableDataId,
|
|
MainRowSelect, NumPrevColumns, OutVars, EndBranch,
|
|
LookupCode, !MaybeEnd, !.CI, CLD)
|
|
),
|
|
|
|
MainCode = BinarySearchCode ++ LookupCode,
|
|
SwitchKindStr = "string binary single soln lookup switch",
|
|
add_switch_kind_comment_and_end_label(SwitchKindStr, EndLabel,
|
|
MainCode, Code).
|
|
|
|
:- pred construct_string_binary_simple_lookup_vector(
|
|
assoc_list(string, list(rval))::in,
|
|
cord(list(rval))::in, cord(list(rval))::out) is det.
|
|
|
|
construct_string_binary_simple_lookup_vector([], !RvalsCord).
|
|
construct_string_binary_simple_lookup_vector([Str - OutRvals | StrsOutRvals],
|
|
!RvalsCord) :-
|
|
RowRvals = [const(llconst_string(Str)) | OutRvals],
|
|
cord.snoc(RowRvals, !RvalsCord),
|
|
construct_string_binary_simple_lookup_vector(StrsOutRvals, !RvalsCord).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred generate_string_binary_several_soln_lookup_switch(
|
|
case_consts_several_llds::in, rval::in,
|
|
assoc_list(string, soln_consts(rval))::in,
|
|
list(prog_var)::in, list(llds_type)::in,
|
|
can_fail::in, label::in, end_branch_info::in,
|
|
llds_code::out, branch_end::in, branch_end::out,
|
|
code_info::in, code_info::out, code_loc_dep::in) is det.
|
|
|
|
generate_string_binary_several_soln_lookup_switch(CaseConstsSeveralLlds,
|
|
VarRval, CaseSolns, OutVars, OutTypes,
|
|
CanFail, EndLabel, EndBranch, Code, !MaybeEnd, !CI, !.CLD) :-
|
|
% This predicate, generate_string_binary_simple_lookup_switch,
|
|
% and generate_string_binary_lookup_switch do similar tasks using
|
|
% similar code, so if you need to update one, you probably need to
|
|
% update them all.
|
|
|
|
init_string_binary_switch_info(CanFail, BinarySwitchInfo, !CI, !.CLD),
|
|
|
|
list.length(CaseSolns, MainTableSize),
|
|
list.length(OutVars, NumOutVars),
|
|
% For the LLDS backend, array indexing ops don't need the element types,
|
|
% so it is ok to lie here.
|
|
list.duplicate(NumOutVars, scalar_elem_generic, OutElemTypes),
|
|
ArrayElemTypes =
|
|
[scalar_elem_string, scalar_elem_int, scalar_elem_int | OutElemTypes],
|
|
ArrayElemType = array_elem_struct(ArrayElemTypes),
|
|
|
|
% Now generate the static cells into which we do the lookups of the values
|
|
% of the output variables, if there are any.
|
|
%
|
|
% We put a dummy row at the start of the later solns table, so that
|
|
% a zero in the "later solns start row" column of the main table can mean
|
|
% "no later solutions".
|
|
InitLaterSolnRowNumber = 1,
|
|
DummyLaterSolnRow = list.map(default_value_for_type, OutTypes),
|
|
LaterSolnArrayCord0 = singleton(DummyLaterSolnRow),
|
|
construct_string_binary_several_soln_lookup_vector(CaseSolns,
|
|
NumOutVars, cord.init, MainRvalsCord,
|
|
InitLaterSolnRowNumber, LaterSolnArrayCord0, LaterSolnArrayCord,
|
|
0, OneSolnCaseCount, 0, SeveralSolnsCaseCount),
|
|
MainRvals = cord.list(MainRvalsCord),
|
|
LaterSolnArray = cord.list(LaterSolnArrayCord),
|
|
|
|
MainRowTypes =
|
|
[lt_string, lt_int(int_type_int), lt_int(int_type_int) | OutTypes],
|
|
list.length(MainRowTypes, MainNumColumns),
|
|
add_vector_static_cell(MainRowTypes, MainRvals, MainTableDataId, !CI),
|
|
MainTableAddrRval = const(llconst_data_addr(MainTableDataId)),
|
|
add_vector_static_cell(OutTypes, LaterSolnArray, LaterTableDataId, !CI),
|
|
LaterTableAddrRval = const(llconst_data_addr(LaterTableDataId)),
|
|
|
|
generate_string_binary_switch_search(BinarySwitchInfo, VarRval,
|
|
MainTableAddrRval, ArrayElemType, MainTableSize, MainNumColumns,
|
|
BinarySearchCode),
|
|
|
|
MidRegLval = BinarySwitchInfo ^ sbsi_mid_reg,
|
|
MainRowSelect = main_row_number_reg(lval(MidRegLval), MainRowTypes),
|
|
acquire_and_setup_lookup_base_reg(MainTableDataId, EndBranch,
|
|
MainRowSelect, BaseRegLval, SetBaseRegCode, !CLD),
|
|
|
|
NumPrevColumns = 1,
|
|
generate_multi_soln_table_lookup_code(CaseConstsSeveralLlds,
|
|
OneSolnCaseCount - kind_one_soln,
|
|
[SeveralSolnsCaseCount - kind_several_solns],
|
|
NumPrevColumns, OutVars, EndLabel, BaseRegLval, LaterTableAddrRval,
|
|
EndBranch, LookupResultsCode, !MaybeEnd, !CI, !.CLD),
|
|
|
|
MainCode = BinarySearchCode ++ SetBaseRegCode ++ LookupResultsCode,
|
|
SwitchKindStr = "string binary multi soln lookup switch",
|
|
add_switch_kind_comment_and_end_label(SwitchKindStr, EndLabel,
|
|
MainCode, Code).
|
|
|
|
:- pred construct_string_binary_several_soln_lookup_vector(
|
|
assoc_list(string, soln_consts(rval))::in, int::in,
|
|
cord(list(rval))::in, cord(list(rval))::out,
|
|
int::in, cord(list(rval))::in, cord(list(rval))::out,
|
|
int::in, int::out, int::in, int::out) is det.
|
|
|
|
construct_string_binary_several_soln_lookup_vector([],
|
|
_NumOutVars, !MainRvalsCord, _LaterNextRow, !LaterSolnArray,
|
|
!OneSolnCaseCount, !SeveralSolnCaseCount).
|
|
construct_string_binary_several_soln_lookup_vector([Str - Soln | StrSolns],
|
|
NumOutVars, !MainRvalsCord, !.LaterNextRow, !LaterSolnArray,
|
|
!OneSolnCaseCount, !SeveralSolnsCaseCount) :-
|
|
StrRval = const(llconst_string(Str)),
|
|
(
|
|
Soln = one_soln(OutRvals),
|
|
!:OneSolnCaseCount = !.OneSolnCaseCount + 1,
|
|
ZeroRval = const(llconst_int(0)),
|
|
% The first ZeroRval means there is exactly one solution for this case;
|
|
% the second ZeroRval is a dummy that won't be referenced.
|
|
MainRow = [StrRval, ZeroRval, ZeroRval | OutRvals]
|
|
;
|
|
Soln = several_solns(FirstSolnRvals, LaterSolns),
|
|
!:SeveralSolnsCaseCount = !.SeveralSolnsCaseCount + 1,
|
|
list.length(LaterSolns, NumLaterSolns),
|
|
FirstRowOffset = !.LaterNextRow * NumOutVars,
|
|
LastRowOffset = (!.LaterNextRow + NumLaterSolns - 1) * NumOutVars,
|
|
FirstRowRval = const(llconst_int(FirstRowOffset)),
|
|
LastRowRval = const(llconst_int(LastRowOffset)),
|
|
MainRow = [StrRval, FirstRowRval, LastRowRval | FirstSolnRvals],
|
|
!:LaterNextRow = !.LaterNextRow + NumLaterSolns,
|
|
!:LaterSolnArray = !.LaterSolnArray ++ from_list(LaterSolns)
|
|
),
|
|
cord.snoc(MainRow, !MainRvalsCord),
|
|
construct_string_binary_several_soln_lookup_vector(StrSolns, NumOutVars,
|
|
!MainRvalsCord, !.LaterNextRow, !LaterSolnArray,
|
|
!OneSolnCaseCount, !SeveralSolnsCaseCount).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- type string_binary_switch_info
|
|
---> string_binary_switch_info(
|
|
sbsi_lo_reg :: lval,
|
|
sbsi_hi_reg :: lval,
|
|
sbsi_mid_reg :: lval,
|
|
sbsi_result_reg :: lval,
|
|
|
|
sbsi_loop_start_label :: label,
|
|
sbsi_gt_eq_label :: label,
|
|
sbsi_eq_label :: label,
|
|
sbsi_fail_label :: label,
|
|
|
|
sbsi_branch_start :: position_info,
|
|
sbsi_fail_code :: llds_code
|
|
).
|
|
|
|
:- pred init_string_binary_switch_info(can_fail::in,
|
|
string_binary_switch_info::out,
|
|
code_info::in, code_info::out, code_loc_dep::in) is det.
|
|
|
|
init_string_binary_switch_info(CanFail, Info, !CI, !.CLD) :-
|
|
% We get the registers we use as working storage in the implementation
|
|
% of binary search, and in the implementation of trie and hash switches
|
|
% above. We get them now, before we generate the code of the switch arms,
|
|
% because the set of free registers will in general be different
|
|
% before and after that action. However, it is safe to release them
|
|
% immediately, even though we haven't yet generated all the code
|
|
% which uses them, because with all three implementation methods, that
|
|
% code will *only* be executed before the code for the cases.
|
|
% Releasing the registers early allows the code we generate for the cases
|
|
% to make use of them.
|
|
acquire_reg(reg_r, LoReg, !CLD),
|
|
acquire_reg(reg_r, HiReg, !CLD),
|
|
acquire_reg(reg_r, MidReg, !CLD),
|
|
acquire_reg(reg_r, ResultReg, !CLD),
|
|
release_reg(LoReg, !CLD),
|
|
release_reg(HiReg, !CLD),
|
|
release_reg(MidReg, !CLD),
|
|
release_reg(ResultReg, !CLD),
|
|
|
|
get_next_label(LoopStartLabel, !CI),
|
|
get_next_label(GtEqLabel, !CI),
|
|
get_next_label(EqLabel, !CI),
|
|
get_next_label(FailLabel, !CI),
|
|
|
|
% We must generate the failure code in the context in which
|
|
% none of the switch arms have been executed yet.
|
|
remember_position(!.CLD, BranchStart),
|
|
generate_string_switch_fail(CanFail, FailCode, !.CI, !.CLD),
|
|
|
|
Info = string_binary_switch_info(LoReg, HiReg, MidReg, ResultReg,
|
|
LoopStartLabel, GtEqLabel, EqLabel, FailLabel, BranchStart, FailCode).
|
|
|
|
% Generate code for the binary search. This code will execute FailCode
|
|
% if the key is not in the table, and will fall through if it is, leaving
|
|
% the index of the matching row in the register specified by
|
|
% Info ^ sbsi_mid_reg.
|
|
%
|
|
:- pred generate_string_binary_switch_search(string_binary_switch_info::in,
|
|
rval::in, rval::in, array_elem_type::in, int::in, int::in,
|
|
llds_code::out) is det.
|
|
|
|
generate_string_binary_switch_search(Info, VarRval, TableAddrRval,
|
|
ArrayElemType, TableSize, NumColumns, Code) :-
|
|
Info = string_binary_switch_info(LoReg, HiReg, MidReg, ResultReg,
|
|
LoopStartLabel, GtEqLabel, EqLabel, FailLabel, _BranchStart, FailCode),
|
|
|
|
MaxIndex = TableSize - 1,
|
|
Code =
|
|
from_list([
|
|
llds_instr(assign(LoReg, const(llconst_int(0))), ""),
|
|
llds_instr(assign(HiReg, const(llconst_int(MaxIndex))), ""),
|
|
|
|
% LoopStartLabel
|
|
llds_instr(label(LoopStartLabel),
|
|
"begin table search loop, nofulljump"),
|
|
llds_instr(
|
|
if_val(
|
|
binop(int_cmp(int_type_int, gt), lval(LoReg), lval(HiReg)),
|
|
code_label(FailLabel)),
|
|
"have we searched all of the table?"),
|
|
llds_instr(
|
|
assign(MidReg,
|
|
binop(int_arith(int_type_int, ao_div),
|
|
binop(int_arith(int_type_int, ao_add),
|
|
lval(LoReg), lval(HiReg)),
|
|
const(llconst_int(2)))), ""),
|
|
llds_instr(
|
|
assign(ResultReg,
|
|
binop(str_nzp,
|
|
VarRval,
|
|
binop(array_index(ArrayElemType),
|
|
TableAddrRval,
|
|
binop(int_arith(int_type_int, ao_mul),
|
|
lval(MidReg),
|
|
const(llconst_int(NumColumns)))))),
|
|
"compare with the middle element"),
|
|
llds_instr(
|
|
if_val(
|
|
binop(int_cmp(int_type_int, ge),
|
|
lval(ResultReg), const(llconst_int(0))),
|
|
code_label(GtEqLabel)),
|
|
"branch away unless key is in lo half"),
|
|
llds_instr(
|
|
assign(HiReg,
|
|
binop(int_arith(int_type_int, ao_sub),
|
|
lval(MidReg), const(llconst_int(1)))),
|
|
""),
|
|
llds_instr(goto(code_label(LoopStartLabel)),
|
|
"go back to search the remaining lo half"),
|
|
|
|
% GtEqLabel
|
|
llds_instr(label(GtEqLabel), "nofulljump"),
|
|
llds_instr(
|
|
if_val(
|
|
binop(int_cmp(int_type_int, le),
|
|
lval(ResultReg), const(llconst_int(0))),
|
|
code_label(EqLabel)),
|
|
"branch away unless key is in hi half"),
|
|
llds_instr(
|
|
assign(LoReg,
|
|
binop(int_arith(int_type_int, ao_add),
|
|
lval(MidReg), const(llconst_int(1)))),
|
|
""),
|
|
llds_instr(goto(code_label(LoopStartLabel)),
|
|
"go back to search the remaining hi half"),
|
|
|
|
% FailLabel
|
|
llds_instr(label(FailLabel),
|
|
"handle the failure of the table search")
|
|
]) ++
|
|
FailCode ++
|
|
% EqLabel
|
|
singleton(
|
|
llds_instr(label(EqLabel), "we found the key")
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred generate_string_switch_fail(can_fail::in, llds_code::out,
|
|
code_info::in, code_loc_dep::in) is det.
|
|
|
|
generate_string_switch_fail(CanFail, FailCode, CI, CLD) :-
|
|
(
|
|
CanFail = can_fail,
|
|
generate_failure(FailCode, CI, CLD)
|
|
;
|
|
CanFail = cannot_fail,
|
|
FailCode = singleton(
|
|
llds_instr(
|
|
comment("unreachable; fail code in cannot_fail switch"), "")
|
|
)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module ll_backend.string_switch.
|
|
%---------------------------------------------------------------------------%
|