1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
|
%-----------------------------------------------------------------------------%
% Copyright (C) 2000 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.
%-----------------------------------------------------------------------------%
% ml_tag_switch.m - generate switches based on primary and secondary tags,
% for the MLDS back-end.
% Author: fjh.
%-----------------------------------------------------------------------------%
:- module ml_tag_switch.
:- interface.
:- import_module prog_data.
:- import_module hlds_data, switch_util.
:- import_module code_model.
:- import_module mlds, ml_code_util.
:- import_module list.
% Generate efficient indexing code for tag based switches.
:- pred ml_tag_switch__generate(list(extended_case)::in, prog_var::in,
code_model::in, can_fail::in, prog_context::in,
mlds__defns::out, mlds__statements::out,
ml_gen_info::in, ml_gen_info::out) is det.
:- implementation.
:- import_module hlds_goal, hlds_module.
:- import_module ml_code_gen, ml_switch_gen, ml_unify_gen, ml_simplify_switch.
:- import_module builtin_ops, type_util.
:- import_module assoc_list, map, int, string, require, std_util.
%-----------------------------------------------------------------------------%
ml_tag_switch__generate(Cases, Var, CodeModel, CanFail, Context,
MLDS_Decls, MLDS_Statements) -->
% generate the rval for the primary tag
ml_gen_var(Var, VarLval),
{ VarRval = lval(VarLval) },
{ PTagRval = unop(std_unop(tag), VarRval) },
% group the cases based on primary tag value,
% find out how many constructors share each primary tag value,
% and sort the cases so that the most frequently occurring
% primary tag values come first.
=(Info),
{ ml_gen_info_get_module_info(Info, ModuleInfo) },
ml_variable_type(Var, Type),
{ switch_util__get_ptag_counts(Type, ModuleInfo,
MaxPrimary, PtagCountMap) },
{ map__to_assoc_list(PtagCountMap, PtagCountList) },
{ map__init(PtagCaseMap0) },
{ switch_util__group_cases_by_ptag(Cases, PtagCaseMap0,
PtagCaseMap) },
{ switch_util__order_ptags_by_count(PtagCountList, PtagCaseMap,
PtagCaseList) },
% generate the switch on the primary tag
ml_tag_switch__gen_ptag_cases(PtagCaseList, Var, CanFail, CodeModel,
PtagCountMap, Context, MLDS_Cases),
ml_switch_generate_default(CanFail, CodeModel, Context, Default),
% package up the results into a switch statement
{ Range = range(0, MaxPrimary) },
{ SwitchStmt0 = switch(mlds__native_int_type, PTagRval, Range,
MLDS_Cases, Default) },
{ MLDS_Context = mlds__make_context(Context) },
ml_simplify_switch(SwitchStmt0, MLDS_Context, SwitchStatement),
{ MLDS_Decls = [] },
{ MLDS_Statements = [SwitchStatement] }.
:- pred ml_tag_switch__gen_ptag_cases(ptag_case_list::in, prog_var::in,
can_fail::in, code_model::in, ptag_count_map::in,
prog_context::in, list(mlds__switch_case)::out,
ml_gen_info::in, ml_gen_info::out) is det.
ml_tag_switch__gen_ptag_cases([], _, _, _, _, _, []) --> [].
ml_tag_switch__gen_ptag_cases([Case | Cases], Var, CanFail, CodeModel,
PtagCountMap, Context, [MLDS_Case | MLDS_Cases]) -->
ml_tag_switch__gen_ptag_case(Case, Var, CanFail, CodeModel,
PtagCountMap, Context, MLDS_Case),
ml_tag_switch__gen_ptag_cases(Cases, Var, CanFail, CodeModel,
PtagCountMap, Context, MLDS_Cases).
:- pred ml_tag_switch__gen_ptag_case(
pair(tag_bits, pair(stag_loc, stag_goal_map))::in,
prog_var::in, can_fail::in, code_model::in, ptag_count_map::in,
prog_context::in, mlds__switch_case::out,
ml_gen_info::in, ml_gen_info::out) is det.
ml_tag_switch__gen_ptag_case(Case, Var, CanFail, CodeModel, PtagCountMap,
Context, MLDS_Case) -->
{ Case = PrimaryTag - (SecTagLocn - GoalMap) },
{ map__lookup(PtagCountMap, PrimaryTag, CountInfo) },
{ CountInfo = SecTagLocn1 - MaxSecondary },
{ SecTagLocn = SecTagLocn1 ->
true
;
error("ml_tag_switch.m: secondary tag locations differ")
},
{ map__to_assoc_list(GoalMap, GoalList) },
( { SecTagLocn = none } ->
% There is no secondary tag, so there is no switch on it
( { GoalList = [_ - Goal] } ->
ml_gen_goal(CodeModel, Goal, MLDS_Statement)
; { GoalList = [] } ->
{ error("no goal for non-shared tag") }
;
{ error("more than one goal for non-shared tag") }
)
;
(
{ CanFail = cannot_fail }
->
{ CaseCanFail = cannot_fail }
;
{ list__length(GoalList, GoalCount) },
{ FullGoalCount is MaxSecondary + 1 },
{ FullGoalCount = GoalCount }
->
{ CaseCanFail = cannot_fail }
;
{ CaseCanFail = can_fail }
),
( { GoalList = [_ - Goal], CaseCanFail = cannot_fail } ->
% There is only one possible matching goal,
% so we don't need to switch on it
ml_gen_goal(CodeModel, Goal, MLDS_Statement)
;
ml_tag_switch__gen_stag_switch(GoalList, PrimaryTag,
SecTagLocn, Var, CodeModel, CaseCanFail,
Context, MLDS_Statement)
)
),
{ PrimaryTagRval = const(int_const(PrimaryTag)) },
{ MLDS_Case = [match_value(PrimaryTagRval)] - MLDS_Statement }.
:- pred ml_tag_switch__gen_stag_switch(stag_goal_list, int, stag_loc,
prog_var, code_model, can_fail, prog_context,
mlds__statement, ml_gen_info, ml_gen_info).
:- mode ml_tag_switch__gen_stag_switch(in, in, in, in, in, in, in, out,
in, out) is det.
ml_tag_switch__gen_stag_switch(Cases, PrimaryTag, StagLocn, Var,
CodeModel, CanFail, Context, MLDS_Statement) -->
% generate the rval for the secondary tag
=(Info),
{ ml_gen_info_get_module_info(Info, ModuleInfo) },
ml_variable_type(Var, VarType),
ml_gen_var(Var, VarLval),
{ VarRval = lval(VarLval) },
(
{ StagLocn = local },
{ STagRval = unop(std_unop(unmkbody), VarRval) }
;
{ StagLocn = remote },
{ STagRval = ml_gen_secondary_tag_rval(PrimaryTag,
VarType, ModuleInfo, VarRval) }
;
{ StagLocn = none },
{ error("ml_tag_switch__gen_stag_switch: no stag") }
),
% generate the switch on the secondary tag
ml_tag_switch__gen_stag_cases(Cases, CodeModel, MLDS_Cases),
ml_switch_generate_default(CanFail, CodeModel, Context, Default),
% package up the results into a switch statement
{ Range = range_unknown }, % XXX could do better
{ SwitchStmt = switch(mlds__native_int_type, STagRval, Range,
MLDS_Cases, Default) },
{ MLDS_Context = mlds__make_context(Context) },
ml_simplify_switch(SwitchStmt, MLDS_Context, MLDS_Statement).
:- pred ml_tag_switch__gen_stag_cases(stag_goal_list, code_model,
list(mlds__switch_case), ml_gen_info, ml_gen_info).
:- mode ml_tag_switch__gen_stag_cases(in, in, out, in, out) is det.
ml_tag_switch__gen_stag_cases([], _, []) --> [].
ml_tag_switch__gen_stag_cases([Case | Cases], CodeModel,
[MLDS_Case | MLDS_Cases]) -->
ml_tag_switch__gen_stag_case(Case, CodeModel, MLDS_Case),
ml_tag_switch__gen_stag_cases(Cases, CodeModel, MLDS_Cases).
:- pred ml_tag_switch__gen_stag_case(pair(tag_bits, hlds_goal), code_model,
mlds__switch_case, ml_gen_info, ml_gen_info).
:- mode ml_tag_switch__gen_stag_case(in, in, out, in, out) is det.
ml_tag_switch__gen_stag_case(Case, CodeModel, MLDS_Case) -->
{ Case = Stag - Goal },
{ StagRval = const(int_const(Stag)) },
ml_gen_goal(CodeModel, Goal, MLDS_Statement),
{ MLDS_Case = [match_value(StagRval)] - MLDS_Statement }.
%-----------------------------------------------------------------------------%
|