mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-15 01:13:30 +00:00
Make type_range also succeed for discontiguous range of enum values.
compiler/switch_util.m:
Make type_range succeed for a subtype where the constructors are
assigned enum values in a discontiguous range. I checked that its
callers do not expect all values in the range to necessarily
be used by the type.
Clarify find_int_lookup_switch_params a bit.
Clarify comments.
compiler/dense_switch.m:
Use need_range_check type instead of can_fail in dense_switch_info,
as that is what the field actually means.
compiler/ml_simplify_switch.m:
Use need_range_check type instead of bool.
tests/hard_coded/dense_lookup_switch_non2.m:
Delete obsoleted XXX.
This commit is contained in:
@@ -76,13 +76,13 @@
|
||||
|
||||
:- type dense_switch_info
|
||||
---> dense_switch_info(
|
||||
first_value :: int,
|
||||
last_value :: int,
|
||||
new_can_fail :: can_fail
|
||||
first_value :: int,
|
||||
last_value :: int,
|
||||
need_range_check :: need_range_check
|
||||
).
|
||||
|
||||
tagged_case_list_is_dense_switch(CI, VarType, TaggedCases,
|
||||
LowerLimit, UpperLimit, NumValues, ReqDensity, CanFail0,
|
||||
LowerLimit, UpperLimit, NumValues, ReqDensity, CanFail,
|
||||
DenseSwitchInfo) :-
|
||||
list.length(TaggedCases, NumCases),
|
||||
NumCases > 2,
|
||||
@@ -92,7 +92,7 @@ tagged_case_list_is_dense_switch(CI, VarType, TaggedCases,
|
||||
Density = switch_density(NumValues, Range),
|
||||
Density > ReqDensity,
|
||||
(
|
||||
CanFail0 = can_fail,
|
||||
CanFail = can_fail,
|
||||
% For semidet switches, we normally need to check that the variable
|
||||
% is in range before we index into the jump table. However, if the
|
||||
% range of the type is sufficiently small, we can make the jump table
|
||||
@@ -105,21 +105,21 @@ tagged_case_list_is_dense_switch(CI, VarType, TaggedCases,
|
||||
DetDensity = switch_density(NumValues, TypeRange),
|
||||
DetDensity > ReqDensity
|
||||
then
|
||||
CanFail = cannot_fail,
|
||||
NeedRangeCheck = dont_need_range_check,
|
||||
FirstVal = TypeMin,
|
||||
LastVal = TypeMax
|
||||
else
|
||||
CanFail = CanFail0,
|
||||
NeedRangeCheck = need_range_check,
|
||||
FirstVal = LowerLimit,
|
||||
LastVal = UpperLimit
|
||||
)
|
||||
;
|
||||
CanFail0 = cannot_fail,
|
||||
CanFail = cannot_fail,
|
||||
NeedRangeCheck = dont_need_range_check,
|
||||
FirstVal = LowerLimit,
|
||||
LastVal = UpperLimit
|
||||
),
|
||||
DenseSwitchInfo = dense_switch_info(FirstVal, LastVal, CanFail).
|
||||
DenseSwitchInfo = dense_switch_info(FirstVal, LastVal, NeedRangeCheck).
|
||||
|
||||
%---------------------------------------------------------------------------%
|
||||
|
||||
@@ -128,23 +128,23 @@ generate_dense_switch(TaggedCases, VarRval, VarName, CodeModel, SwitchGoalInfo,
|
||||
% Evaluate the variable which we are going to be switching on.
|
||||
% If the case values start at some number other than 0,
|
||||
% then subtract that number to give us a zero-based index.
|
||||
DenseSwitchInfo = dense_switch_info(FirstVal, LastVal, CanFail),
|
||||
DenseSwitchInfo = dense_switch_info(FirstVal, LastVal, NeedRangeCheck),
|
||||
( if FirstVal = 0 then
|
||||
IndexRval = VarRval
|
||||
else
|
||||
IndexRval = binop(int_sub(int_type_int), VarRval,
|
||||
const(llconst_int(FirstVal)))
|
||||
),
|
||||
% If the switch is not locally deterministic, we need to check that
|
||||
% the value of the variable lies within the appropriate range.
|
||||
% Check that the value of the variable lies within the appropriate range
|
||||
% if necessary.
|
||||
(
|
||||
CanFail = can_fail,
|
||||
NeedRangeCheck = need_range_check,
|
||||
Difference = LastVal - FirstVal,
|
||||
fail_if_rval_is_false(
|
||||
binop(unsigned_le, IndexRval, const(llconst_int(Difference))),
|
||||
RangeCheckCode, !CI, !CLD)
|
||||
;
|
||||
CanFail = cannot_fail,
|
||||
NeedRangeCheck = dont_need_range_check,
|
||||
RangeCheckCode = empty
|
||||
),
|
||||
|
||||
|
||||
@@ -206,7 +206,7 @@ is_dense_switch(Cases, ReqDensity) :-
|
||||
%
|
||||
:- pred maybe_eliminate_default(mlds_switch_range::in,
|
||||
list(mlds_switch_case)::in, mlds_switch_default::in, int::in,
|
||||
int::out, int::out, bool::out) is det.
|
||||
int::out, int::out, need_range_check::out) is det.
|
||||
|
||||
maybe_eliminate_default(Range, Cases, Default, ReqDensity,
|
||||
FirstVal, LastVal, NeedRangeCheck) :-
|
||||
@@ -218,18 +218,18 @@ maybe_eliminate_default(Range, Cases, Default, ReqDensity,
|
||||
NoDefaultDensity = switch_density(NumCases, TypeRange),
|
||||
NoDefaultDensity > ReqDensity
|
||||
then
|
||||
NeedRangeCheck = no,
|
||||
NeedRangeCheck = dont_need_range_check,
|
||||
FirstVal = Min,
|
||||
LastVal = Max
|
||||
else
|
||||
(
|
||||
Default = default_is_unreachable,
|
||||
NeedRangeCheck = no
|
||||
NeedRangeCheck = dont_need_range_check
|
||||
;
|
||||
( Default = default_do_nothing
|
||||
; Default = default_case(_)
|
||||
),
|
||||
NeedRangeCheck = yes
|
||||
NeedRangeCheck = need_range_check
|
||||
),
|
||||
find_min_and_max_in_cases(Cases, FirstCaseVal, LastCaseVal),
|
||||
FirstVal = FirstCaseVal,
|
||||
@@ -283,7 +283,7 @@ find_min_and_max_in_case_cond(match_range(MinRval, MaxRval), !Min, !Max) :-
|
||||
% Generate code for a switch using a dense jump table.
|
||||
%
|
||||
:- pred generate_dense_switch(list(mlds_switch_case)::in,
|
||||
mlds_switch_default::in, int::in, int::in, bool::in,
|
||||
mlds_switch_default::in, int::in, int::in, need_range_check::in,
|
||||
mlds_type::in, mlds_rval::in, prog_context::in, list(mlds_stmt)::out,
|
||||
ml_gen_info::in, ml_gen_info::out) is det.
|
||||
|
||||
@@ -329,7 +329,7 @@ generate_dense_switch(Cases, Default, FirstVal, LastVal, NeedRangeCheck,
|
||||
% We may need to check that the value of the variable lies within the
|
||||
% appropriate range.
|
||||
(
|
||||
NeedRangeCheck = yes,
|
||||
NeedRangeCheck = need_range_check,
|
||||
Difference = LastVal - FirstVal,
|
||||
InRange = ml_binop(unsigned_le,
|
||||
Index, ml_const(mlconst_int(Difference))),
|
||||
@@ -338,7 +338,7 @@ generate_dense_switch(Cases, Default, FirstVal, LastVal, NeedRangeCheck,
|
||||
DoSwitch = ml_stmt_if_then_else(InRange, SwitchBody, Else, Context),
|
||||
Stmts = [StartComment, DoSwitch, EndLabelStmt, EndComment]
|
||||
;
|
||||
NeedRangeCheck = no,
|
||||
NeedRangeCheck = dont_need_range_check,
|
||||
Stmts =
|
||||
[StartComment, DoJump | CasesCode] ++
|
||||
DefaultStmts ++
|
||||
|
||||
@@ -126,16 +126,20 @@
|
||||
% Stuff for dense switches.
|
||||
%
|
||||
|
||||
% type_range(ModuleInfo, TypeCtorCategory, Type, Min, Max, NumValues):
|
||||
% type_range(ModuleInfo, TypeCtorCategory, Type, Min, Max,
|
||||
% NumValuesInRange):
|
||||
%
|
||||
% Determine the range [Min..Max] of an atomic type, and the number of
|
||||
% values in that range (including both endpoints).
|
||||
% values in that range (including both endpoints). Values within the range
|
||||
% are not necessarily used by the type.
|
||||
% Fail if the type isn't the sort of type that has a range
|
||||
% or if the type's range is too big to switch on (e.g. int).
|
||||
%
|
||||
:- pred type_range(module_info::in, type_ctor_category::in, mer_type::in,
|
||||
int::out, int::out, int::out) is semidet.
|
||||
|
||||
% switch_density(NumCases, NumValuesInRange):
|
||||
%
|
||||
% Calculate the percentage density given the range and the number of cases.
|
||||
%
|
||||
:- func switch_density(int, int) = int.
|
||||
@@ -406,7 +410,6 @@
|
||||
:- import_module io.
|
||||
:- import_module maybe.
|
||||
:- import_module one_or_more.
|
||||
:- import_module ranges.
|
||||
:- import_module require.
|
||||
:- import_module string.
|
||||
:- import_module uint.
|
||||
@@ -922,7 +925,7 @@ is_smart_indexing_allowed_for_category(Globals, SwitchCategory) = Allowed :-
|
||||
% Stuff for dense switches.
|
||||
%
|
||||
|
||||
type_range(ModuleInfo, TypeCtorCat, Type, Min, Max, NumValues) :-
|
||||
type_range(ModuleInfo, TypeCtorCat, Type, Min, Max, NumValuesInRange) :-
|
||||
(
|
||||
TypeCtorCat = ctor_cat_builtin(cat_builtin_char),
|
||||
% Note also that some code in both dense_switch.m and in
|
||||
@@ -957,10 +960,7 @@ type_range(ModuleInfo, TypeCtorCat, Type, Min, Max, NumValues) :-
|
||||
% A subtype enum does not necessarily use all values from 0 to
|
||||
% the max.
|
||||
CtorRepns = Repn ^ dur_ctor_repns,
|
||||
Ranges0 = ranges.empty,
|
||||
list.foldl(insert_enum_cons_tag, CtorRepns, Ranges0, Ranges),
|
||||
% XXX SUBTYPE make callers not expect one contiguous range
|
||||
ranges.is_contiguous(Ranges, Min, Max)
|
||||
ctor_repns_int_tag_range(CtorRepns, Min, Max)
|
||||
)
|
||||
;
|
||||
( TypeBody = hlds_eqv_type(_)
|
||||
@@ -971,18 +971,27 @@ type_range(ModuleInfo, TypeCtorCat, Type, Min, Max, NumValues) :-
|
||||
unexpected($pred, "enum type is not d.u. type?")
|
||||
)
|
||||
),
|
||||
NumValues = Max - Min + 1.
|
||||
NumValuesInRange = Max - Min + 1.
|
||||
|
||||
:- pred insert_enum_cons_tag(constructor_repn::in, ranges::in, ranges::out)
|
||||
is det.
|
||||
:- pred ctor_repns_int_tag_range(list(constructor_repn)::in,
|
||||
int::out, int::out) is semidet.
|
||||
|
||||
insert_enum_cons_tag(CtorRepn, !Ranges) :-
|
||||
ctor_repns_int_tag_range([CtorRepn | CtorRepns], Min, Max) :-
|
||||
ConsTag = CtorRepn ^ cr_tag,
|
||||
get_int_tag(ConsTag, Int),
|
||||
ranges.insert(Int, !Ranges).
|
||||
list.foldl2(add_to_ctor_repn_int_tag_range, CtorRepns, Int, Min, Int, Max).
|
||||
|
||||
switch_density(NumCases, Range) = Density :-
|
||||
Density = (NumCases * 100) // Range.
|
||||
:- pred add_to_ctor_repn_int_tag_range(constructor_repn::in,
|
||||
int::in, int::out, int::in, int::out) is det.
|
||||
|
||||
add_to_ctor_repn_int_tag_range(CtorRepn, !Min, !Max) :-
|
||||
ConsTag = CtorRepn ^ cr_tag,
|
||||
get_int_tag(ConsTag, Int),
|
||||
int.min(Int, !Min),
|
||||
int.max(Int, !Max).
|
||||
|
||||
switch_density(NumCases, NumValuesInRange) = Density :-
|
||||
Density = (NumCases * 100) // NumValuesInRange.
|
||||
|
||||
%-----------------------------------------------------------------------------%
|
||||
%-----------------------------------------------------------------------------%
|
||||
@@ -1041,17 +1050,9 @@ find_int_lookup_switch_params(ModuleInfo, SwitchVarType, SwitchCanFail,
|
||||
% For can_fail switches, we normally need to check that the variable
|
||||
% is in range before we index into the jump table. However, if the
|
||||
% range of the type is sufficiently small, we can make the jump table
|
||||
% large enough to hold all of the values for the type, but then we
|
||||
% will need to do the bitvector test.
|
||||
% large enough to hold all of the values for the type (with gaps),
|
||||
% but then we will need to do the bitvector test.
|
||||
classify_type(ModuleInfo, SwitchVarType) = TypeCategory,
|
||||
% If there are going to be no gaps in the lookup table, then we
|
||||
% won't need a bitvector test to see if this switch has a value
|
||||
% for this case.
|
||||
( if NumValues = Range then
|
||||
NeedBitVecCheck0 = dont_need_bit_vec_check_no_gaps
|
||||
else
|
||||
NeedBitVecCheck0 = need_bit_vec_check
|
||||
),
|
||||
( if
|
||||
type_range(ModuleInfo, TypeCategory, SwitchVarType,
|
||||
TypeMin, TypeMax, TypeRange),
|
||||
@@ -1063,8 +1064,15 @@ find_int_lookup_switch_params(ModuleInfo, SwitchVarType, SwitchCanFail,
|
||||
FirstVal = TypeMin,
|
||||
LastVal = TypeMax
|
||||
else
|
||||
% First check the variable is in range.
|
||||
NeedRangeCheck = need_range_check,
|
||||
NeedBitVecCheck = NeedBitVecCheck0,
|
||||
% We will need to perform the bitvector test if the lookup table
|
||||
% is going to contain any gaps.
|
||||
( if NumValues = Range then
|
||||
NeedBitVecCheck = dont_need_bit_vec_check_no_gaps
|
||||
else
|
||||
NeedBitVecCheck = need_bit_vec_check
|
||||
),
|
||||
FirstVal = LowerLimit,
|
||||
LastVal = UpperLimit
|
||||
)
|
||||
@@ -1074,8 +1082,7 @@ find_int_lookup_switch_params(ModuleInfo, SwitchVarType, SwitchCanFail,
|
||||
% but are not covered by any of the cases won't actually be reached.
|
||||
NeedRangeCheck = dont_need_range_check,
|
||||
% There may be gaps in the lookup table if switching on a variable of
|
||||
% a subtype which does not use some values between the min and max
|
||||
% values.
|
||||
% a subtype which does not use some values in the range.
|
||||
( if NumValues = Range then
|
||||
NeedBitVecCheck = dont_need_bit_vec_check_no_gaps
|
||||
else
|
||||
|
||||
@@ -127,9 +127,6 @@ p1(h, "p1_eight", f1, 8.0).
|
||||
:- pragma no_inline(p2/4).
|
||||
|
||||
% This predicate needs a bitvec check but not a range check.
|
||||
% XXX SUBTYPE But a range check will be be generated because the presence of
|
||||
% base_only_2 breaks the values of foo into two ranges, and currently
|
||||
% type_range will only succeed of a single contiguous range.
|
||||
p2(a, "p2_one", f1, 1.1).
|
||||
p2(c, "p2_three", f1, 3.3).
|
||||
p2(d, "p2_four", f1, 4.4).
|
||||
|
||||
Reference in New Issue
Block a user