Files
mercury/compiler/switch_case.m
Zoltan Somogyi 5e687dd6af Generate more direct jump tables on primary tags.
(Even before this diff, we already generated as-direct-as-possible
jump tables on secondary tags.

compiler/tag_switch.m:
    When using the jump table method to implement switches on primary tags,
    we used to generate suboptimal code for shared ptag values. If a switch
    wanted to execute the same code for e.g. ptags 3 and 5, we generated
    code that looked like this:

            computed_goto (..., 3 -> label A, 4 -> ..., 5 -> label B, ...)

        label A:
            code for the case we want for both ptags 3 and 5
            goto switch_end_label

            ...

        label B:
            goto label A

    Change the algorithm to generate code like this:

            computed_goto (..., 3 -> label A, 4 -> ..., 5 -> label A, ...)

        label A:
            code for the case we want for both ptags 3 and 5
            goto switch_end_label

compiler/switch_case.m:
    Replace a predicate that is used only as part of one use case
    with a predicate that implements the *whole* of that use case,
    and generalize it (by returning the updated case label map)
    to be useful for the new code in tag_switch.m.

compiler/string_switch.m:
    Conform to the change in switch_case.m.
2024-03-14 19:16:18 +11:00

172 lines
6.6 KiB
Mathematica

%-----------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%-----------------------------------------------------------------------------%
% Copyright (C) 2007, 2009-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: switch_case.m.
% Author: zs.
%
% Utility predicates for handling switch cases, especially those representing
% more than one cons_id, for the LLDS backend.
%
%-----------------------------------------------------------------------------%
:- module ll_backend.switch_case.
:- 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 map.
:- import_module unit.
:- type represent_params
---> represent_params(
switch_var_name :: string,
switch_goal_info :: hlds_goal_info,
switch_code_model :: code_model,
starting_position :: position_info,
switch_end_label :: label
).
:- type case_code_included
---> case_code_not_yet_included
; case_code_already_included.
:- type case_label_info
---> case_label_info(
case_description :: string,
case_code :: llds_code,
case_code_included :: case_code_included
).
:- type case_label_map == map(label, case_label_info).
% represent_tagged_case_for_llds(Params, TaggedCase, Label,
% !CaseLabelMap, !MaybeEnd, !CI):
%
% Given TaggedCase, generate code for it (using the information in Params,
% and updating MaybeEnd and CI). The code will start with the newly
% allocated label Label. This label will represent the case in
% CaseLabelMap. The corresponding case_label_info will contain a comment
% describing the case in terms of the cons_ids it handles, the generated
% code (starting with the label instruction for Label and ending with
% the jump to the end label of the switch), and an indication that this
% code has not yet been included anywhere.
%
:- pred represent_tagged_case_for_llds(represent_params::in,
tagged_case::in, label::out, case_label_map::in, case_label_map::out,
branch_end::in, branch_end::out, code_info::in, code_info::out,
unit::in, unit::out) is det.
%-----------------------------------------------------------------------------%
% generate_case_code_or_jump(CaseLabel, Code, !CaseLabelMap):
%
:- pred generate_case_code_or_jump(label::in, llds_code::out,
case_label_map::in, case_label_map::out) is det.
%-----------------------------------------------------------------------------%
:- pred add_not_yet_included_cases(llds_code::out,
case_label_map::in, case_label_map::out) is det.
%-----------------------------------------------------------------------------%
:- implementation.
:- import_module hlds.hlds_llds.
:- import_module hlds.hlds_out.
:- import_module hlds.hlds_out.hlds_out_goal.
:- import_module ll_backend.code_gen.
:- import_module ll_backend.trace_gen.
:- import_module cord.
:- import_module list.
:- import_module string.
%-----------------------------------------------------------------------------%
represent_tagged_case_for_llds(Params, TaggedCase, Label, !CaseLabelMap,
!MaybeEnd, !CI, _, unit) :-
some [!CLD] (
Params = represent_params(SwitchVarName, SwitchGoalInfo, CodeModel,
BranchStart, EndLabel),
TaggedCase =
tagged_case(MainTaggedConsId, OtherTaggedConsIds, _, Goal),
project_cons_name_and_tag(MainTaggedConsId, MainConsName, _),
list.map2(project_cons_name_and_tag, OtherTaggedConsIds,
OtherConsNames, _),
Comment = case_comment(SwitchVarName, MainConsName, OtherConsNames),
reset_to_position(BranchStart, !.CI, !:CLD),
get_next_label(Label, !CI),
LabelCode = singleton(
llds_instr(label(Label), Comment)
),
maybe_generate_internal_event_code(Goal, SwitchGoalInfo, TraceCode,
!CI, !CLD),
generate_goal(CodeModel, Goal, GoalCode, !CI, !CLD),
goal_info_get_store_map(SwitchGoalInfo, StoreMap),
generate_branch_end(StoreMap, !MaybeEnd, SaveCode, !.CLD),
GotoEndCode = singleton(
llds_instr(goto(code_label(EndLabel)),
"goto end of switch on " ++ SwitchVarName)
),
Code = LabelCode ++ TraceCode ++ GoalCode ++ SaveCode ++ GotoEndCode,
CaseInfo = case_label_info(Comment, Code, case_code_not_yet_included),
map.det_insert(Label, CaseInfo, !CaseLabelMap)
).
%-----------------------------------------------------------------------------%
generate_case_code_or_jump(CaseLabel, Code, !CaseLabelMap) :-
map.lookup(!.CaseLabelMap, CaseLabel, CaseInfo0),
CaseInfo0 = case_label_info(Comment, CaseCode, CaseIncluded),
(
CaseIncluded = case_code_not_yet_included,
Code = CaseCode,
CaseInfo = CaseInfo0 ^ case_code_included
:= case_code_already_included,
map.det_update(CaseLabel, CaseInfo, !CaseLabelMap)
;
CaseIncluded = case_code_already_included,
% We cannot include the case's code, since it has already been included
% somewhere else.
Code = singleton(
llds_instr(goto(code_label(CaseLabel)), "goto " ++ Comment)
)
).
%-----------------------------------------------------------------------------%
add_not_yet_included_cases(Code, !CaseLabelMap) :-
map.map_values_foldl(add_not_yet_included_case,
!CaseLabelMap, empty, Code).
:- pred add_not_yet_included_case(case_label_info::in, case_label_info::out,
llds_code::in, llds_code::out) is det.
add_not_yet_included_case(CaseInfo0, CaseInfo, !Code) :-
CaseInfo0 = case_label_info(Comment, CaseCode, CaseIncluded0),
(
CaseIncluded0 = case_code_not_yet_included,
!:Code = !.Code ++ CaseCode,
CaseIncluded = case_code_already_included,
CaseInfo = case_label_info(Comment, CaseCode, CaseIncluded)
;
CaseIncluded0 = case_code_already_included,
CaseInfo = CaseInfo0
).
%-----------------------------------------------------------------------------%
:- end_module ll_backend.switch_case.
%-----------------------------------------------------------------------------%