for review: switches via binary search
Zoltan Somogyi
zs at cs.mu.oz.au
Tue Dec 30 20:55:26 AEDT 1997
Fergus, can you please review this? I'd ask Tom, but he won't be around
for a while.
compiler/tag_switch.m:
Add a new way of generating code for switches, binary search chains.
These have the form:
if (tag(var)) > 1) goto L23
if (tag(var)) != 0) goto L1
code for tag 0
goto end
L1: code for tag 1
goto end
L23: if (tag(var)) != 2) goto L3
code for tag 2
goto end
L3: code for tag 3
goto end
These have a lower number of expected comparisons than try chains,
especially for machines with three tag bits, although some of the
tests, requiring a subtraction, may be slightly more expensive.
They can be useful for switches where the number of alternatives is
not high enough to justify the overhead of usinga jump table.
compiler/options.m:
Add a new option --binary-switch-size, that controls when we use
the new method.
doc/user_guide.texi:
Document the new option.
Zoltan.
cvs diff: Diffing .
Index: options.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/options.m,v
retrieving revision 1.214
diff -u -u -r1.214 options.m
--- options.m 1997/12/22 09:56:12 1.214
+++ options.m 1997/12/23 05:03:19
@@ -215,6 +215,7 @@
; string_switch_size
; tag_switch_size
; try_switch_size
+ ; binary_switch_size
; static_ground_terms
; middle_rec
; simple_neg
@@ -491,6 +492,7 @@
string_switch_size - int(8),
tag_switch_size - int(3),
try_switch_size - int(3),
+ binary_switch_size - int(4),
static_ground_terms - bool(no),
middle_rec - bool(no),
simple_neg - bool(no),
@@ -785,6 +787,7 @@
long_option("string-switch-size", string_switch_size).
long_option("tag-switch-size", tag_switch_size).
long_option("try-switch-size", try_switch_size).
+long_option("binary-switch-size", binary_switch_size).
long_option("static-ground-terms", static_ground_terms).
long_option("middle-rec", middle_rec).
long_option("simple_neg", simple_neg).
@@ -1657,6 +1660,9 @@
io__write_string("\t--try-switch-size <n>\n"),
io__write_string("\t\tThe number of alternatives in a try/retry chain switch\n"),
io__write_string("\t\tmust be at least this number (default: 3).\n"),
+ io__write_string("\t--binary-switch-size <n>\n"),
+ io__write_string("\t\tThe number of alternatives in a binary search chain switch\n"),
+ io__write_string("\t\tmust be at least this number (default: 4).\n"),
io__write_string("\t--no-static-ground-terms\n"),
io__write_string("\t\tDisable the optimization of constructing constant ground terms\n"),
io__write_string("\t\tat compile time and storing them as static constants.\n"),
Index: tag_switch.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/tag_switch.m,v
retrieving revision 1.41
diff -u -u -r1.41 tag_switch.m
--- tag_switch.m 1997/12/22 06:58:38 1.41
+++ tag_switch.m 1997/12/30 09:36:45
@@ -27,7 +27,8 @@
:- import_module hlds_module, hlds_pred, hlds_data, code_gen, trace.
:- import_module options, globals, type_util, prog_data.
-:- import_module assoc_list, bool, map, tree, int, require, std_util, term.
+:- import_module assoc_list, map, tree, bool, int, string.
+:- import_module require, std_util, term.
% where is the secondary tag (if any) for this primary tag value
:- type stag_loc ---> none ; local ; remote.
@@ -52,11 +53,11 @@
% symbol can be eliminated by a failed primary tag test, this reduces
% the expected the number of comparisons required before finding the
% code corresponding to the actual value of the switch variable.
- % We also get a speedup compared to non-tag tests by extracting
+ % We also get a speedup compared to non-tag switches by extracting
% the primary and secondary tags once instead of repeatedly for
% each functor test.
%
- % We have three methods we can use for generating the code for the
+ % We have four methods we can use for generating the code for the
% switches on both primary and secondary tags.
%
% 1. try-me-else chains have the form
@@ -93,19 +94,56 @@
% goto end
% ...
%
- % Note that for a det switch with two tag values, try-me-else chains
- % and and try chains are equivalent.
+ % 4. binary search chains have the form
+ %
+ % if (tag(var)) > 1) goto L23
+ % if (tag(var)) != 0) goto L1
+ % code for tag 0
+ % goto end
+ % L1: code for tag 1
+ % goto end
+ % L23: if (tag(var)) != 2) goto L3
+ % code for tag 2
+ % goto end
+ % L3: code for tag 3
+ % goto end
%
+ % Note that for a det switch with two tag values, try-me-else chains
+ % and try chains are equivalent.
+
% Which method is best depends on the number of possible tag values,
- % the costs of taken jumps and table lookups on the given architecture
- % and on the frequency with which the various alternatives are taken.
+ % on the costs of taken/untaken branches and table lookups on the given
+ % architecture, and on the frequency with which the various
+ % alternatives are taken.
%
% While the first two are in principle known at compile time,
% the third is not. Nevertheless, for switches on primary tags
% we can use the heuristic that the more secondary tags assigned to
- % a primary tag, the more likely that switch variable will have that
- % primary tag at runtime.
+ % a primary tag, the more likely that the switch variable will have
+ % that primary tag at runtime.
+ %
+ % Try chains are good for switches with small numbers of alternatives
+ % on architectures where untaken branches are cheaper than taken
+ % branches.
%
+ % Try-me-else chains are good for switches with very small numbers of
+ % alternatives on architectures where taken branches are cheaper than
+ % untaken branches (which are rare these days).
+ %
+ % Jump tables are good for switches with large numbers of alternatives.
+ % The cost of jumping through a jump table is relatively high, since
+ % it involves a memory access and an indirect branch (which most
+ % current architectures do not handle well), but this cost is
+ % independent of the number of alternatives.
+ %
+ % Binary search chains are good for switches where the number of
+ % alternatives is large enough for the reduced expected number of
+ % branches executed to overcome the extra overhead of the subtraction
+ % required for some conditional branches (compared to try chains
+ % and try-me-else chains), but not large enough to make the
+ % expected cost of the expected number of comparisons exceed the
+ % expected cost of a jump table lookup and dispatch.
+
% For try-me-else chains, we want tag1 to be the most frequent case,
% tag 2 the next most frequent case, etc.
%
@@ -124,8 +162,19 @@
% edge in that no goto is needed to reach the code following the
% switch. If there is no code following the switch (which happens
% very frequently), then even this advantage is nullified.
-
-:- type switch_method ---> try_me_else_chain ; try_chain ; jump_table.
+ %
+ % For binary search chains, we want the case of the most frequently
+ % occurring tag to be the first, since this code is reached with no
+ % taken branches and ends with an unconditional branch, whereas
+ % reaching the code of the other cases requires at least one taken
+ % *conditional* branch. In general, at each binary decision we
+ % want the more frequently reached cases to be in the half that
+ % immediately follows the if statement implementing the decision.
+
+:- type switch_method ---> try_me_else_chain
+ ; try_chain
+ ; jump_table
+ ; binary_chain.
tag_switch__generate(Cases, Var, CodeModel, CanFail, StoreMap, EndLabel, Code)
-->
@@ -148,9 +197,13 @@
DenseSwitchSize) },
{ globals__lookup_int_option(Globals, try_switch_size,
TrySwitchSize) },
- ( { PtagsUsed > DenseSwitchSize } ->
+ { globals__lookup_int_option(Globals, binary_switch_size,
+ BinarySwitchSize) },
+ ( { PtagsUsed >= DenseSwitchSize } ->
{ PrimaryMethod = jump_table }
- ; { PtagsUsed > TrySwitchSize } ->
+ ; { PtagsUsed >= BinarySwitchSize } ->
+ { PrimaryMethod = binary_chain }
+ ; { PtagsUsed >= TrySwitchSize } ->
{ PrimaryMethod = try_chain }
;
{ PrimaryMethod = try_me_else_chain }
@@ -193,8 +246,8 @@
PtagRval = unop(tag, VarRval)
},
- % we generate FailCode and EndCode here because the last case within
- % a primary tag may not be the last case overall
+ % We generate FailCode and EndCode here because the last case within
+ % a primary tag may not be the last case overall.
code_info__get_next_label(FailLabel),
{ FailLabelCode = node([
@@ -215,6 +268,19 @@
{ EndCode = node([label(EndLabel) - "end of tag switch"]) },
(
+ { PrimaryMethod = binary_chain },
+ { tag_switch__order_ptags_by_value(0, MaxPrimary, PtagCaseMap,
+ PtagCaseList) },
+ tag_switch__generate_primary_binary_chain(PtagCaseList,
+ 0, MaxPrimary, PtagRval, VarRval, CodeModel, CanFail,
+ StoreMap, EndLabel, FailLabel, PtagCountMap,
+ no, MaybeFinalCodeInfo, CasesCode),
+ ( { MaybeFinalCodeInfo = yes(FinalCodeInfo) } ->
+ code_info__slap_code_info(FinalCodeInfo)
+ ;
+ { error("binary chain tag switch has no useful cases") }
+ )
+ ;
{ PrimaryMethod = jump_table },
{ tag_switch__order_ptags_by_value(0, MaxPrimary, PtagCaseMap,
PtagCaseList) },
@@ -475,6 +541,121 @@
%-----------------------------------------------------------------------------%
+ % Generate the cases for a primary tag using a binary search chain.
+ % This invocation looks after primary tag values in the range
+ % MinPtag to MaxPtag (including both boundary values).
+
+:- pred tag_switch__generate_primary_binary_chain(ptag_case_list, int, int,
+ rval, rval, code_model, can_fail, store_map, label, label,
+ ptag_count_map, maybe(code_info), maybe(code_info),
+ code_tree, code_info, code_info).
+:- mode tag_switch__generate_primary_binary_chain(in, in, in,
+ in, in, in, in, in, in, in, in, in, out, out, in, out) is det.
+
+tag_switch__generate_primary_binary_chain(PtagGroups, MinPtag, MaxPtag,
+ PtagRval, VarRval, CodeModel, CanFail, StoreMap,
+ EndLabel, FailLabel, PtagCountMap,
+ MaybeFinalCodeInfo0, MaybeFinalCodeInfo, Code) -->
+ ( { MinPtag = MaxPtag } ->
+ { CurPrimary = MinPtag },
+ ( { PtagGroups = [] } ->
+ % there is no code for this tag
+ (
+ { CanFail = can_fail },
+ { string__int_to_string(CurPrimary, PtagStr) },
+ { string__append("no code for ptag ", PtagStr,
+ Comment) },
+ { Code = node([
+ goto(label(FailLabel)) -
+ Comment
+ ]) }
+ ;
+ { CanFail = cannot_fail },
+ { Code = empty }
+ ),
+ { MaybeFinalCodeInfo = MaybeFinalCodeInfo0 }
+ ; { PtagGroups = [CurPrimary - PrimaryInfo] } ->
+ { PrimaryInfo = StagLoc - StagGoalMap },
+ { map__lookup(PtagCountMap, CurPrimary, CountInfo) },
+ { CountInfo = StagLoc1 - MaxSecondary },
+ { StagLoc = StagLoc1 ->
+ true
+ ;
+ error("secondary tag locations differ in generate_primary_jump_table")
+ },
+ tag_switch__generate_primary_tag_code(
+ StagGoalMap, CurPrimary, MaxSecondary,
+ StagLoc, VarRval, CodeModel,
+ StoreMap, EndLabel, FailLabel, Code),
+ code_info__grab_code_info(CodeInfo),
+ { MaybeFinalCodeInfo = yes(CodeInfo) }
+ ;
+ { error("caselist not singleton or empty when binary search ends") }
+ )
+ ;
+ { LowRangeEnd is (MinPtag + MaxPtag) // 2 },
+ { HighRangeStart is LowRangeEnd + 1 },
+ { tag_switch__divide_ptag_range(PtagGroups, LowRangeEnd,
+ LowPtagGroups, HighPtagGroups) },
+ code_info__get_next_label(NewLabel),
+ { string__int_to_string(MinPtag, LowStartStr) },
+ { string__int_to_string(LowRangeEnd, LowEndStr) },
+ { string__int_to_string(HighRangeStart, HighStartStr) },
+ { string__int_to_string(MaxPtag, HighEndStr) },
+ { string__append_list(["fallthrough for ptags ",
+ LowStartStr, " to ", LowEndStr], IfComment) },
+ { string__append_list(["code for ptags ", HighStartStr,
+ " to ", HighEndStr], LabelComment) },
+ { LowRangeEndConst = const(int_const(LowRangeEnd)) },
+ { TestRval = binop(>, PtagRval, LowRangeEndConst) },
+ { IfCode = node([
+ if_val(TestRval, label(NewLabel)) -
+ IfComment
+ ]) },
+ { LabelCode = node([
+ label(NewLabel) -
+ LabelComment
+ ]) },
+
+ code_info__grab_code_info(CodeInfo),
+ tag_switch__generate_primary_binary_chain(LowPtagGroups,
+ MinPtag, LowRangeEnd, PtagRval, VarRval, CodeModel,
+ CanFail, StoreMap, EndLabel, FailLabel, PtagCountMap,
+ MaybeFinalCodeInfo0, MaybeFinalCodeInfo1, LowRangeCode),
+ code_info__slap_code_info(CodeInfo),
+ tag_switch__generate_primary_binary_chain(HighPtagGroups,
+ HighRangeStart, MaxPtag, PtagRval, VarRval, CodeModel,
+ CanFail, StoreMap, EndLabel, FailLabel, PtagCountMap,
+ MaybeFinalCodeInfo1, MaybeFinalCodeInfo, HighRangeCode),
+
+ { Code =
+ tree(IfCode,
+ tree(LowRangeCode,
+ tree(LabelCode,
+ HighRangeCode)))
+ }
+ ).
+
+:- pred tag_switch__divide_ptag_range(ptag_case_list, tag_bits,
+ ptag_case_list, ptag_case_list).
+:- mode tag_switch__divide_ptag_range(in, in, out, out) is det.
+
+tag_switch__divide_ptag_range([], _LowRangeEnd, [], []).
+tag_switch__divide_ptag_range([PtagGroup | PtagGroups], LowRangeEnd,
+ LowRange, HighRange) :-
+ PtagGroup = Ptag - _,
+ ( Ptag > LowRangeEnd ->
+ tag_switch__divide_ptag_range(PtagGroups, LowRangeEnd,
+ LowRange, HighRange0),
+ HighRange = [PtagGroup | HighRange0]
+ ;
+ tag_switch__divide_ptag_range(PtagGroups, LowRangeEnd,
+ LowRange0, HighRange),
+ LowRange = [PtagGroup | LowRange0]
+ ).
+
+%-----------------------------------------------------------------------------%
+
% Generate the code corresponding to a primary tag.
% If this primary tag has secondary tags, decide whether we should
% use a jump table to implement the secondary switch.
@@ -525,10 +706,14 @@
code_info__get_globals(Globals),
{ globals__lookup_int_option(Globals, dense_switch_size,
DenseSwitchSize) },
+ { globals__lookup_int_option(Globals, binary_switch_size,
+ BinarySwitchSize) },
{ globals__lookup_int_option(Globals, try_switch_size,
TrySwitchSize) },
{ MaxSecondary > DenseSwitchSize ->
SecondaryMethod = jump_table
+ ; MaxSecondary > BinarySwitchSize ->
+ SecondaryMethod = binary_chain
; MaxSecondary > TrySwitchSize ->
SecondaryMethod = try_chain
;
@@ -593,6 +778,17 @@
]) },
{ Code = tree(SwitchCode, CasesCode) }
;
+ { SecondaryMethod = binary_chain },
+ tag_switch__generate_secondary_binary_chain(GoalList,
+ 0, MaxSecondary, StagRval, CodeModel, CanFail,
+ StoreMap, EndLabel, FailLabel,
+ no, MaybeFinalCodeInfo, Code),
+ ( { MaybeFinalCodeInfo = yes(FinalCodeInfo) } ->
+ code_info__slap_code_info(FinalCodeInfo)
+ ;
+ { error("binary chain tag switch has no useful cases") }
+ )
+ ;
{ SecondaryMethod = try_chain },
tag_switch__generate_secondary_try_chain(GoalList,
StagRval, CodeModel, CanFail, StoreMap,
@@ -841,6 +1037,125 @@
Code),
{ Labels = [FailLabel | OtherLabels] }
)
+ ).
+
+%-----------------------------------------------------------------------------%
+
+ % Generate the cases for a secondary tag using a binary search chain.
+ % This invocation looks after secondary tag values in the range
+ % MinPtag to MaxPtag (including both boundary values).
+
+:- pred tag_switch__generate_secondary_binary_chain(stag_goal_list, int, int,
+ rval, code_model, can_fail, store_map, label, label,
+ maybe(code_info), maybe(code_info),
+ code_tree, code_info, code_info).
+:- mode tag_switch__generate_secondary_binary_chain(in, in, in,
+ in, in, in, in, in, in, in, out, out, in, out) is det.
+
+tag_switch__generate_secondary_binary_chain(StagGoals, MinStag, MaxStag,
+ StagRval, CodeModel, CanFail, StoreMap, EndLabel, FailLabel,
+ MaybeFinalCodeInfo0, MaybeFinalCodeInfo, Code) -->
+ ( { MinStag = MaxStag } ->
+ { CurSec = MinStag },
+ ( { StagGoals = [] } ->
+ % there is no code for this tag
+ (
+ { CanFail = can_fail },
+ { string__int_to_string(CurSec, StagStr) },
+ { string__append("no code for ptag ", StagStr,
+ Comment) },
+ { Code = node([
+ goto(label(FailLabel)) -
+ Comment
+ ]) }
+ ;
+ { CanFail = cannot_fail },
+ { Code = empty }
+ ),
+ { MaybeFinalCodeInfo = MaybeFinalCodeInfo0 }
+ ; { StagGoals = [CurSec - Goal] } ->
+ code_info__get_maybe_trace_info(MaybeTraceInfo),
+ ( { MaybeTraceInfo = yes(TraceInfo) } ->
+ { Goal = _ - GoalInfo },
+ { goal_info_get_goal_path(GoalInfo, Path) },
+ trace__generate_event_code(switch(Path),
+ TraceInfo, TraceCode)
+ ;
+ { TraceCode = empty }
+ ),
+ code_gen__generate_goal(CodeModel, Goal, GoalCode),
+ code_info__generate_branch_end(CodeModel, StoreMap,
+ SaveCode),
+ { Code =
+ tree(TraceCode,
+ tree(GoalCode,
+ SaveCode))
+ },
+ code_info__grab_code_info(CodeInfo),
+ { MaybeFinalCodeInfo = yes(CodeInfo) }
+ ;
+ { error("goallist not singleton or empty when binary search ends") }
+ )
+ ;
+ { LowRangeEnd is (MinStag + MaxStag) // 2 },
+ { HighRangeStart is LowRangeEnd + 1 },
+ { tag_switch__divide_stag_range(StagGoals, LowRangeEnd,
+ LowStagGoals, HighStagGoals) },
+ code_info__get_next_label(NewLabel),
+ { string__int_to_string(MinStag, LowStartStr) },
+ { string__int_to_string(LowRangeEnd, LowEndStr) },
+ { string__int_to_string(HighRangeStart, HighStartStr) },
+ { string__int_to_string(MaxStag, HighEndStr) },
+ { string__append_list(["fallthrough for stags ",
+ LowStartStr, " to ", LowEndStr], IfComment) },
+ { string__append_list(["code for stags ", HighStartStr,
+ " to ", HighEndStr], LabelComment) },
+ { LowRangeEndConst = const(int_const(LowRangeEnd)) },
+ { TestRval = binop(>, StagRval, LowRangeEndConst) },
+ { IfCode = node([
+ if_val(TestRval, label(NewLabel)) -
+ IfComment
+ ]) },
+ { LabelCode = node([
+ label(NewLabel) -
+ LabelComment
+ ]) },
+
+ code_info__grab_code_info(CodeInfo),
+ tag_switch__generate_secondary_binary_chain(LowStagGoals,
+ MinStag, LowRangeEnd, StagRval, CodeModel,
+ CanFail, StoreMap, EndLabel, FailLabel,
+ MaybeFinalCodeInfo0, MaybeFinalCodeInfo1, LowRangeCode),
+ code_info__slap_code_info(CodeInfo),
+ tag_switch__generate_secondary_binary_chain(HighStagGoals,
+ HighRangeStart, MaxStag, StagRval, CodeModel,
+ CanFail, StoreMap, EndLabel, FailLabel,
+ MaybeFinalCodeInfo1, MaybeFinalCodeInfo, HighRangeCode),
+
+ { Code =
+ tree(IfCode,
+ tree(LowRangeCode,
+ tree(LabelCode,
+ HighRangeCode)))
+ }
+ ).
+
+:- pred tag_switch__divide_stag_range(stag_goal_list, int,
+ stag_goal_list, stag_goal_list).
+:- mode tag_switch__divide_stag_range(in, in, out, out) is det.
+
+tag_switch__divide_stag_range([], _LowRangeEnd, [], []).
+tag_switch__divide_stag_range([PtagGroup | PtagGroups], LowRangeEnd,
+ LowRange, HighRange) :-
+ PtagGroup = Ptag - _,
+ ( Ptag > LowRangeEnd ->
+ tag_switch__divide_stag_range(PtagGroups, LowRangeEnd,
+ LowRange, HighRange0),
+ HighRange = [PtagGroup | HighRange0]
+ ;
+ tag_switch__divide_stag_range(PtagGroups, LowRangeEnd,
+ LowRange0, HighRange),
+ LowRange = [PtagGroup | LowRange0]
).
%-----------------------------------------------------------------------------%
+ tree(LowRangeCode,
+ tree(LabelCode,
+ HighRangeCode)))
+ }
+ ).
+
+:- pred tag_switch__divide_ptag_range(ptag_case_list, tag_bits,
+ ptag_case_list, ptag_case_list).
+:- mode tag_switch__divide_ptag_range(in, in, out, out) is det.
+
+tag_switch__divide_ptag_range([], _LowRangeEnd, [], []).
+tag_switch__divide_ptag_range([PtagGroup | PtagGroups], LowRangeEnd,
+ LowRange, HighRange) :-
+ PtagGroup = Ptag - _,
+ ( Ptag > LowRangeEnd ->
+ tag_switch__divide_ptag_range(PtagGroups, LowRangeEnd,
+ LowRange, HighRange0),
+ HighRange = [PtagGroup | HighRange0]
+ ;
+ tag_switch__divide_ptag_range(PtagGroups, LowRangeEnd,
+ LowRange0, HighRange),
+ LowRange = [PtagGroup | LowRange0]
+ ).
+
+%-----------------------------------------------------------------------------%
+
% Generate the code corresponding to a primary tag.
% If this primary tag has secondary tags, decide whether we should
% use a jump table to implement the secondary switch.
@@ -525,10 +706,14 @@
code_info__get_globals(Globals),
{ globals__lookup_int_option(Globals, dense_switch_size,
DenseSwitchSize) },
+ { globals__lookup_int_option(Globals, binary_switch_size,
+ BinarySwitchSize) },
{ globals__lookup_int_option(Globals, try_switch_size,
TrySwitchSize) },
{ MaxSecondary > DenseSwitchSize ->
SecondaryMethod = jump_table
+ ; MaxSecondary > BinarySwitchSize ->
+ SecondaryMethod = binary_chain
; MaxSecondary > TrySwitchSize ->
SecondaryMethod = try_chain
;
@@ -593,6 +778,17 @@
]) },
{ Code = tree(SwitchCode, CasesCode) }
;
+ { SecondaryMethod = binary_chain },
+ tag_switch__generate_secondary_binary_chain(GoalList,
+ 0, MaxSecondary, StagRval, CodeModel, CanFail,
+ StoreMap, EndLabel, FailLabel,
+ no, MaybeFinalCodeInfo, Code),
+ ( { MaybeFinalCodeInfo = yes(FinalCodeInfo) } ->
+ code_info__slap_code_info(FinalCodeInfo)
+ ;
+ { error("binary chain tag switch has no useful cases") }
+ )
+ ;
{ SecondaryMethod = try_chain },
tag_switch__generate_secondary_try_chain(GoalList,
StagRval, CodeModel, CanFail, StoreMap,
@@ -841,6 +1037,125 @@
Code),
{ Labels = [FailLabel | OtherLabels] }
)
+ ).
+
+%-----------------------------------------------------------------------------%
+
+ % Generate the cases for a secondary tag using a binary search chain.
+ % This invocation looks after secondary tag values in the range
+ % MinPtag to MaxPtag (including both boundary values).
+
+:- pred tag_switch__generate_secondary_binary_chain(stag_goal_list, int, int,
+ rval, code_model, can_fail, store_map, label, label,
+ maybe(code_info), maybe(code_info),
+ code_tree, code_info, code_info).
+:- mode tag_switch__generate_secondary_binary_chain(in, in, in,
+ in, in, in, in, in, in, in, out, out, in, out) is det.
+
+tag_switch__generate_secondary_binary_chain(StagGoals, MinStag, MaxStag,
+ StagRval, CodeModel, CanFail, StoreMap, EndLabel, FailLabel,
+ MaybeFinalCodeInfo0, MaybeFinalCodeInfo, Code) -->
+ ( { MinStag = MaxStag } ->
+ { CurSec = MinStag },
+ ( { StagGoals = [] } ->
+ % there is no code for this tag
+ (
+ { CanFail = can_fail },
+ { string__int_to_string(CurSec, StagStr) },
+ { string__append("no code for ptag ", StagStr,
+ Comment) },
+ { Code = node([
+ goto(label(FailLabel)) -
+ Comment
+ ]) }
+ ;
+ { CanFail = cannot_fail },
+ { Code = empty }
+ ),
+ { MaybeFinalCodeInfo = MaybeFinalCodeInfo0 }
+ ; { StagGoals = [CurSec - Goal] } ->
+ code_info__get_maybe_trace_info(MaybeTraceInfo),
+ ( { MaybeTraceInfo = yes(TraceInfo) } ->
+ { Goal = _ - GoalInfo },
+ { goal_info_get_goal_path(GoalInfo, Path) },
+ trace__generate_event_code(switch(Path),
+ TraceInfo, TraceCode)
+ ;
+ { TraceCode = empty }
+ ),
+ code_gen__generate_goal(CodeModel, Goal, GoalCode),
+ code_info__generate_branch_end(CodeModel, StoreMap,
+ SaveCode),
+ { Code =
+ tree(TraceCode,
+ tree(GoalCode,
+ SaveCode))
+ },
+ code_info__grab_code_info(CodeInfo),
+ { MaybeFinalCodeInfo = yes(CodeInfo) }
+ ;
+ { error("goallist not singleton or empty when binary search ends") }
+ )
+ ;
+ { LowRangeEnd is (MinStag + MaxStag) // 2 },
+ { HighRangeStart is LowRangeEnd + 1 },
+ { tag_switch__divide_stag_range(StagGoals, LowRangeEnd,
+ LowStagGoals, HighStagGoals) },
+ code_info__get_next_label(NewLabel),
+ { string__int_to_string(MinStag, LowStartStr) },
+ { string__int_to_string(LowRangeEnd, LowEndStr) },
+ { string__int_to_string(HighRangeStart, HighStartStr) },
+ { string__int_to_string(MaxStag, HighEndStr) },
+ { string__append_list(["fallthrough for stags ",
+ LowStartStr, " to ", LowEndStr], IfComment) },
+ { string__append_list(["code for stags ", HighStartStr,
+ " to ", HighEndStr], LabelComment) },
+ { LowRangeEndConst = const(int_const(LowRangeEnd)) },
+ { TestRval = binop(>, StagRval, LowRangeEndConst) },
+ { IfCode = node([
+ if_val(TestRval, label(NewLabel)) -
+ IfComment
+ ]) },
+ { LabelCode = node([
+ label(NewLabel) -
+ LabelComment
+ ]) },
+
+ code_info__grab_code_info(CodeInfo),
+ tag_switch__generate_secondary_binary_chain(LowStagGoals,
+ MinStag, LowRangeEnd, StagRval, CodeModel,
+ CanFail, StoreMap, EndLabel, FailLabel,
+ MaybeFinalCodeInfo0, MaybeFinalCodeInfo1, LowRangeCode),
+ code_info__slap_code_info(CodeInfo),
+ tag_switch__generate_secondary_binary_chain(HighStagGoals,
+ HighRangeStart, MaxStag, StagRval, CodeModel,
+ CanFail, StoreMap, EndLabel, FailLabel,
+ MaybeFinalCodeInfo1, MaybeFinalCodeInfo, HighRangeCode),
+
+ { Code =
+ tree(IfCode,
+ tree(LowRangeCode,
+ tree(LabelCode,
+ HighRangeCode)))
+ }
+ ).
+
+:- pred tag_switch__divide_stag_range(stag_goal_list, int,
+ stag_goal_list, stag_goal_list).
+:- mode tag_switch__divide_stag_range(in, in, out, out) is det.
+
+tag_switch__divide_stag_range([], _LowRangeEnd, [], []).
+tag_switch__divide_stag_range([PtagGroup | PtagGroups], LowRangeEnd,
+ LowRange, HighRange) :-
+ PtagGroup = Ptag - _,
+ ( Ptag > LowRangeEnd ->
+ tag_switch__divide_stag_range(PtagGroups, LowRangeEnd,
+ LowRange, HighRange0),
+ HighRange = [PtagGroup | HighRange0]
+ ;
+ tag_switch__divide_stag_range(PtagGroups, LowRangeEnd,
+ LowRange0, HighRange),
+ LowRange = [PtagGroup | LowRange0]
).
%-----------------------------------------------------------------------------%
cvs diff: Diffing doc
Index: doc/user_guide.texi
===================================================================
RCS file: /home/mercury1/repository/mercury/doc/user_guide.texi,v
retrieving revision 1.109
diff -u -u -r1.109 user_guide.texi
--- user_guide.texi 1997/12/22 10:01:33 1.109
+++ user_guide.texi 1997/12/30 08:05:48
@@ -2352,6 +2352,11 @@
must be at least this number (default: 3).
@sp 1
+ at item --binary-switch-size @var{size}
+The number of alternatives in a binary search chain switch
+must be at least this number (default: 4).
+
+ at sp 1
@item --no-middle-rec
Disable the middle recursion optimization.
More information about the developers
mailing list