Files
mercury/compiler/ml_lookup_switch.m
Zoltan Somogyi b261c5fcb2 Enable dense switches on sized and unsigned ints.
Until now, the only integer type we generated dense switches
(including lookup switches) for was int itself; we did not do so
for any sized and/or unsigned integer types.

compiler/switch_util.m:
    Simplify the representation of whether a switch is on an integer type.
    The new version makes it impossible to make the mistake that caused
    bug582 in the first place: not having a single representation for
    for the switch being on *any* kind of integer type.

    This fix requires solving a problem we never had to solve before:
    representing information such as the min and max case values
    in a way that works for every integer type, signed or unsigned,
    sized or not. The solution that this diff adopts is to use int32s
    to represent those limits, which implies that whatever the type
    of the integer value being switched on, we will generate a dense
    or a lookup switch for it only if all case values fit into an int32.
    Since case values over two billion are vanishingly rare, this should be
    an acceptable restriction.

    Use uints instead of ints to represent counts of things.

    Delete an unneeded pair of arguments.

compiler/lookup_switch_util.m:
    Conform to the changes in switch_util.m. Use some of the new types
    there to make arguments in arguments lists less confusable.

    Provide some new utility operations.

    Add XXXs where the basic operations we need seem not to exist.

compiler/dense_switch.m:
compiler/lookup_switch.m:
    Use the new types in switch_util.m that can represent switches
    on any integer type.

compiler/ml_lookup_switch.m:
compiler/ml_simplify_switch.m:
compiler/ml_string_switch.m:
compiler/ml_switch_gen.m:
compiler/switch_gen.m:
    Conform to the changes above, and thereby gain the ability
    to generate switches on integer types other than int itself.

library/int64.m:
    Add a (commmented-out) declaration of an operation that could
    help resolve one of the issues in new code in the modules above.
    Similar operations would be needed in the modules of other
    sized integer types as well.

library/library.m:
    Fix a typo.

tests/hard_coded/bug582.{m,exp}:
    Add a test case for this issue. Note that while we test whether
    we get the expected output, there is no simple automatic way
    to detect whether it was generated using a lookup table.

tests/hard_coded/Mmakefile:
    Enable the new test case.
2026-03-04 19:51:35 +11:00

920 lines
39 KiB
Mathematica

%---------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%---------------------------------------------------------------------------%
% Copyright (C) 2009-2012 The University of Melbourne.
% Copyright (C) 2014-2018, 2020-2026 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: ml_lookup_switch.m
% Author: zs.
%
% This module implements lookup switches for the MLDS backend.
% Much of its structure is modelled after the structure of lookup_switch.m,
% which does the same thing for the LLDS backend. Most of the documentation
% you may need is in that module.
%
% Any changes here may need to be reflected in lookup_switch.m as well.
%
%---------------------------------------------------------------------------%
:- module ml_backend.ml_lookup_switch.
:- interface.
:- import_module backend_libs.
:- import_module backend_libs.lookup_switch_util.
:- import_module hlds.
:- import_module hlds.code_model.
:- import_module hlds.hlds_data.
:- import_module hlds.hlds_goal.
:- import_module ml_backend.ml_gen_info.
:- import_module ml_backend.mlds.
:- import_module parse_tree.
:- import_module parse_tree.prog_data.
:- import_module list.
:- import_module map.
:- import_module maybe.
:- import_module unit.
%---------------------------------------------------------------------------%
:- type ml_lookup_switch_info
---> ml_lookup_switch_info(
% The map from the case_id of each switch arm to the
% values of the variables in that switch arm.
mllsi_cases :: case_consts(case_id, mlds_rval,
unit),
% The output variables, which become (some of) the fields
% in each row of a lookup table.
mllsi_out_variables :: list(prog_var),
% The types of the fields holding output variables.
mllsi_out_types :: list(mlds_type),
mllsi_gen_info :: ml_gen_info
).
% Is the given list of cases implementable as a lookup switch?
%
:- pred ml_is_lookup_switch(prog_var::in, list(tagged_case)::in,
hlds_goal_info::in, code_model::in, ml_gen_info::in,
maybe(ml_lookup_switch_info)::out) is det.
% Given a case_id->V map, create the corresponding cons_id->V map.
% Given an entry caseid1->values1 in the case_id->V map, if the case
% with case_id caseid1 has main and other cons_ids consid1a, consid1b
% and consid1c, the cons_id->V map will have the corresponding entries
% consid1a->values1, consid1b->values1 and consid1c->values1.
%
:- pred ml_case_id_soln_consts_to_tag_soln_consts((func(cons_tag) = T)::in,
list(tagged_case)::in, map(case_id, V)::in, map(T, V)::out) is det.
%---------------------------------------------------------------------------%
% Generate MLDS code for the lookup switch.
%
:- pred ml_gen_int_max_32_lookup_switch(prog_var::in,
list(tagged_case)::in, ml_lookup_switch_info::in,
code_model::in, prog_context::in, int::in, int::in,
need_bit_vec_check::in, need_range_check::in, mlds_stmt::out,
ml_gen_info::out) is det.
%---------------------------------------------------------------------------%
%
% These types and predicates are exported because they are useful in the
% implementation of string lookup switches.
%
:- pred ml_gen_several_soln_lookup_code(prog_context::in,
mlds_rval::in, list(prog_var)::in, list(mlds_type)::in,
mlds_type::in, mlds_type::in, mlds_field_id::in, mlds_field_id::in,
list(mlds_field_id)::in, list(mlds_field_id)::in,
mlds_vector_common::in, mlds_vector_common::in, need_bit_vec_check::in,
list(mlds_local_var_defn)::out, list(mlds_stmt)::out,
ml_gen_info::in, ml_gen_info::out) is det.
:- type ml_several_soln_lookup_vars
---> ml_several_soln_lookup_vars(
msslv_num_later_solns_var :: mlds_local_var_name_type,
msslv_later_slot_var :: mlds_local_var_name_type,
msslv_limit_var :: mlds_local_var_name_type,
msslv_limit_assign_statement :: mlds_stmt,
msslv_incr_later_slot_statement :: mlds_stmt,
msslv_denfs :: list(mlds_local_var_defn)
).
:- pred make_several_soln_lookup_vars(prog_context::in,
ml_several_soln_lookup_vars::out,
ml_gen_info::in, ml_gen_info::out) is det.
%---------------------------------------------------------------------------%
:- func ml_construct_later_soln_row(mlds_type, list(mlds_rval)) =
mlds_initializer.
:- func ml_default_value_for_type(mlds_type) = mlds_rval.
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
:- implementation.
:- import_module backend_libs.builtin_ops.
:- import_module backend_libs.switch_util.
:- import_module hlds.goal_form.
:- import_module hlds.hlds_module.
:- import_module libs.
:- import_module libs.globals.
:- import_module libs.optimization_options.
:- import_module ml_backend.ml_code_util.
:- import_module ml_backend.ml_global_data.
:- import_module ml_backend.ml_util.
:- import_module parse_tree.set_of_var.
:- import_module parse_tree.var_table.
:- import_module assoc_list.
:- import_module bool.
:- import_module cord.
:- import_module int.
:- import_module pair.
:- import_module require.
:- import_module uint.
%---------------------------------------------------------------------------%
ml_is_lookup_switch(SwitchVar, TaggedCases, GoalInfo, CodeModel, Info0,
MaybeLookupSwitchInfo) :-
NonLocals = goal_info_get_nonlocals(GoalInfo),
% SwitchVar must be nonlocal to the switch, since it must be bound
% *before* the switch.
set_of_var.delete(SwitchVar, NonLocals, OtherNonLocals),
set_of_var.to_sorted_list(OtherNonLocals, OutVars),
ml_gen_info_get_module_info(Info0, ModuleInfo),
module_info_get_globals(ModuleInfo, Globals),
globals.get_opt_tuple(Globals, OptTuple),
( if
OptTuple ^ ot_use_static_ground_cells = use_static_ground_cells,
ml_generate_constants_for_lookup_switch(CodeModel, OutVars,
OtherNonLocals, TaggedCases, map.init, CaseSolnMap, Info0, Info)
then
% While the LLDS backend has to worry about implementing trailing
% for model_non lookup switches, we do not. The MLDS backend implements
% trailing by a HLDS-to-HLDS transform (which is in add_trail_ops.m),
% so we can get here only if trailing is not enabled, since otherwise
% the calls or foreign_procs inserted into all non-first disjuncts
% would cause ml_generate_constants_for_lookup_switch to fail.
( if project_all_to_one_solution(CaseSolnMap, CaseValuePairMap) then
CaseConsts = all_one_soln(CaseValuePairMap)
else
CaseConsts = some_several_solns(CaseSolnMap, unit)
),
ml_gen_info_get_var_table(Info, VarTable),
lookup_var_entries(VarTable, OutVars, OutVarEntries),
FieldTypes =
list.map(var_table_entry_to_mlds_type(ModuleInfo), OutVarEntries),
LookupSwitchInfo =
ml_lookup_switch_info(CaseConsts, OutVars, FieldTypes, Info),
MaybeLookupSwitchInfo = yes(LookupSwitchInfo)
else
% We keep the original !.Info.
MaybeLookupSwitchInfo = no
).
:- pred ml_generate_constants_for_lookup_switch(code_model::in,
list(prog_var)::in, set_of_progvar::in, list(tagged_case)::in,
map(case_id, soln_consts(mlds_rval))::in,
map(case_id, soln_consts(mlds_rval))::out,
ml_gen_info::in, ml_gen_info::out) is semidet.
ml_generate_constants_for_lookup_switch(_CodeModel, _OutVars, _ArmNonLocals,
[], !IndexMap, !Info).
ml_generate_constants_for_lookup_switch(CodeModel, OutVars, ArmNonLocals,
[TaggedCase | TaggedCases], !CaseIdMap, !Info) :-
TaggedCase = tagged_case(_TaggedMainConsId, _TaggedOtherConsIds,
CaseId, Goal),
Goal = hlds_goal(GoalExpr, _GoalInfo),
( if GoalExpr = disj(Disjuncts) then
(
Disjuncts = []
;
Disjuncts = [FirstDisjunct | LaterDisjuncts],
goal_is_conj_of_unify(ArmNonLocals, FirstDisjunct),
all_disjuncts_are_conj_of_unify(ArmNonLocals, LaterDisjuncts),
ml_generate_constants_for_arm(OutVars, FirstDisjunct, FirstSoln,
!Info),
ml_generate_constants_for_arms(OutVars, LaterDisjuncts, LaterSolns,
!Info),
SolnConsts = several_solns(FirstSoln, LaterSolns),
map.det_insert(CaseId, SolnConsts, !CaseIdMap)
)
else
goal_is_conj_of_unify(ArmNonLocals, Goal),
ml_generate_constants_for_arm(OutVars, Goal, Soln, !Info),
SolnConsts = one_soln(Soln),
map.det_insert(CaseId, SolnConsts, !CaseIdMap)
),
ml_generate_constants_for_lookup_switch(CodeModel,
OutVars, ArmNonLocals, TaggedCases, !CaseIdMap, !Info).
%---------------------------------------------------------------------------%
ml_case_id_soln_consts_to_tag_soln_consts(GetTag, TaggedCases, CaseIdMap,
TagMap) :-
ml_case_id_soln_consts_to_tag_soln_consts_loop(GetTag, TaggedCases,
CaseIdMap, DepletedCaseIdMap, map.init, TagMap),
expect(map.is_empty(DepletedCaseIdMap), $pred,
"DepletedCaseIdMap not empty").
:- pred ml_case_id_soln_consts_to_tag_soln_consts_loop(
(func(cons_tag) = Key)::in, list(tagged_case)::in,
map(case_id, V)::in, map(case_id, V)::out,
map(Key, V)::in, map(Key, V)::out) is det.
ml_case_id_soln_consts_to_tag_soln_consts_loop(_GetTag, [],
!CaseIdMap, !TagMap).
ml_case_id_soln_consts_to_tag_soln_consts_loop(GetTag,
[TaggedCase | TaggedCases], !CaseIdMap, !TagMap) :-
TaggedCase = tagged_case(TaggedMainConsId, TaggedOtherConsIds,
CaseId, _Goal),
map.det_remove(CaseId, SolnConsts, !CaseIdMap),
ml_record_lookup_for_tagged_cons_id(GetTag, SolnConsts,
TaggedMainConsId, !TagMap),
list.foldl(ml_record_lookup_for_tagged_cons_id(GetTag, SolnConsts),
TaggedOtherConsIds, !TagMap),
ml_case_id_soln_consts_to_tag_soln_consts_loop(GetTag, TaggedCases,
!CaseIdMap, !TagMap).
:- pred ml_record_lookup_for_tagged_cons_id((func(cons_tag) = Key)::in,
V::in, tagged_cons_id::in, map(Key, V)::in, map(Key, V)::out) is det.
ml_record_lookup_for_tagged_cons_id(GetTag, SolnConsts, TaggedConsId,
!IndexMap) :-
TaggedConsId = tagged_cons_id(_ConsId, ConsTag),
Index = GetTag(ConsTag),
map.det_insert(Index, SolnConsts, !IndexMap).
%---------------------------------------------------------------------------%
ml_gen_int_max_32_lookup_switch(SwitchVar, TaggedCases, LookupSwitchInfo,
CodeModel, Context, StartVal, EndVal, NeedBitVecCheck, NeedRangeCheck,
Stmt, !:Info) :-
LookupSwitchInfo =
ml_lookup_switch_info(CaseIdConstMap, OutVars, FieldTypes, !:Info),
ml_gen_var_direct(!.Info, SwitchVar, SwitchVarLval),
SwitchVarRval = ml_lval(SwitchVarLval),
( if StartVal = 0 then
IndexRval = SwitchVarRval
else
StartRval = ml_const(mlconst_int(StartVal)),
IndexRval = ml_binop(int_arith(int_type_int, ao_sub),
SwitchVarRval, StartRval)
),
(
CaseIdConstMap = all_one_soln(CaseIdValueMap),
ml_case_id_soln_consts_to_tag_soln_consts(get_int_in_cons_tag,
TaggedCases, CaseIdValueMap, IntValueMap),
map.to_assoc_list(IntValueMap, IntValues),
ml_gen_simple_atomic_lookup_switch(IndexRval, OutVars, FieldTypes,
IntValues, CodeModel, Context, StartVal, EndVal,
NeedBitVecCheck, NeedRangeCheck, Stmt, !Info)
;
CaseIdConstMap = some_several_solns(CaseIdSolnMap, _Unit),
expect(unify(CodeModel, model_non), $pred, "CodeModel != model_non"),
ml_case_id_soln_consts_to_tag_soln_consts(get_int_in_cons_tag,
TaggedCases, CaseIdSolnMap, IntSolnMap),
map.to_assoc_list(IntSolnMap, IntSolns),
ml_gen_several_soln_atomic_lookup_switch(IndexRval, OutVars,
FieldTypes, IntSolns, Context, StartVal, EndVal,
NeedBitVecCheck, NeedRangeCheck, Stmt, !Info)
).
%---------------------------------------------------------------------------%
:- pred ml_gen_simple_atomic_lookup_switch(mlds_rval::in, list(prog_var)::in,
list(mlds_type)::in, assoc_list(int, list(mlds_rval))::in, code_model::in,
prog_context::in, int::in, int::in,
need_bit_vec_check::in, need_range_check::in, mlds_stmt::out,
ml_gen_info::in, ml_gen_info::out) is det.
ml_gen_simple_atomic_lookup_switch(IndexRval, OutVars, OutTypes, CaseValues,
CodeModel, Context, StartVal, EndVal, NeedBitVecCheck, NeedRangeCheck,
Stmt, !Info) :-
ml_gen_info_get_module_info(!.Info, ModuleInfo),
module_info_get_name(ModuleInfo, ModuleName),
MLDS_ModuleName = mercury_module_name_to_mlds(ModuleName),
ml_gen_info_get_target(!.Info, Target),
(
OutTypes = [],
% There are no output parameters, so there is nothing to look up.
% Generating a structure with no fields would cause problems for
% Visual C, which cannot handle such structures.
%
% This should happen only for model_semi switches. If it happens for
% a model_det switch, that switch is effectively a no-op.
LookupStmts = []
;
OutTypes = [_ | _],
ml_gen_info_get_global_data(!.Info, GlobalData0),
ml_gen_static_vector_type(MLDS_ModuleName, Context, Target,
OutTypes, StructTypeNum, StructType, FieldIds,
GlobalData0, GlobalData1),
ml_construct_simple_switch_vector(ModuleInfo, StructType,
OutTypes, StartVal, CaseValues, RowInitializers),
ml_gen_static_vector_defn(MLDS_ModuleName, StructTypeNum,
RowInitializers, VectorCommon, GlobalData1, GlobalData),
ml_gen_info_set_global_data(GlobalData, !Info),
ml_generate_field_assigns(OutVars, OutTypes, FieldIds, VectorCommon,
StructType, IndexRval, Context, LookupStmts, !Info)
),
(
CodeModel = model_det,
expect(unify(NeedRangeCheck, do_not_need_range_check), $pred,
"model_det need_range_check"),
(
NeedBitVecCheck = need_bit_vec_check,
unexpected($pred, "model_det need_bit_vec_check")
;
( NeedBitVecCheck = do_not_need_bit_vec_check_no_gaps
; NeedBitVecCheck = do_not_need_bit_vec_check_with_gaps
)
),
Stmt = ml_stmt_block([], [], LookupStmts, Context)
;
CodeModel = model_semi,
ml_gen_set_success(ml_const(mlconst_true), Context, SetSuccessTrueStmt,
!Info),
LookupSucceedStmt = ml_stmt_block([], [],
LookupStmts ++ [SetSuccessTrueStmt], Context),
(
NeedRangeCheck = do_not_need_range_check,
(
( NeedBitVecCheck = do_not_need_bit_vec_check_no_gaps
; NeedBitVecCheck = do_not_need_bit_vec_check_with_gaps
),
Stmt = LookupSucceedStmt
;
NeedBitVecCheck = need_bit_vec_check,
ml_generate_bitvec_test(MLDS_ModuleName, Context, IndexRval,
CaseValues, StartVal, EndVal, BitVecCheckCond, !Info),
ml_gen_set_success(ml_const(mlconst_false), Context,
SetSuccessFalseStmt, !Info),
Stmt = ml_stmt_if_then_else(BitVecCheckCond,
LookupSucceedStmt, yes(SetSuccessFalseStmt), Context)
)
;
NeedRangeCheck = need_range_check,
Difference = EndVal - StartVal,
RangeCheckCond = ml_binop(int_as_uint_cmp(le), IndexRval,
ml_const(mlconst_int(Difference))),
ml_gen_set_success(ml_const(mlconst_false), Context,
SetSuccessFalseStmt, !Info),
(
( NeedBitVecCheck = do_not_need_bit_vec_check_no_gaps
; NeedBitVecCheck = do_not_need_bit_vec_check_with_gaps
),
RangeCheckSuccessStmt = LookupSucceedStmt
;
NeedBitVecCheck = need_bit_vec_check,
ml_generate_bitvec_test(MLDS_ModuleName, Context, IndexRval,
CaseValues, StartVal, EndVal, BitVecCheckCond, !Info),
RangeCheckSuccessStmt = ml_stmt_if_then_else(BitVecCheckCond,
LookupSucceedStmt, yes(SetSuccessFalseStmt), Context)
),
% We want to execute the bit vector test only if the range check
% succeeded, since otherwise the bit vector test will probably
% access the bit vector outside its bounds.
Stmt = ml_stmt_if_then_else(RangeCheckCond,
RangeCheckSuccessStmt, yes(SetSuccessFalseStmt), Context)
)
;
CodeModel = model_non,
% If all the switch arms have exactly one solution, then the switch
% as a whole cannot be model_non.
unexpected($pred, "model_non")
).
%---------------------------------------------------------------------------%
:- pred ml_gen_several_soln_atomic_lookup_switch(mlds_rval::in,
list(prog_var)::in, list(mlds_type)::in,
assoc_list(int, soln_consts(mlds_rval))::in, prog_context::in,
int::in, int::in, need_bit_vec_check::in, need_range_check::in,
mlds_stmt::out, ml_gen_info::in, ml_gen_info::out) is det.
ml_gen_several_soln_atomic_lookup_switch(IndexRval, OutVars, OutTypes,
CaseSolns, Context, StartVal, EndVal, NeedBitVecCheck, NeedRangeCheck,
Stmt, !Info) :-
ml_gen_info_get_module_info(!.Info, ModuleInfo),
module_info_get_name(ModuleInfo, ModuleName),
MLDS_ModuleName = mercury_module_name_to_mlds(ModuleName),
ml_gen_info_get_target(!.Info, Target),
MLDS_IntType = mlds_builtin_type_int(int_type_int),
FirstSolnFieldTypes = [MLDS_IntType, MLDS_IntType | OutTypes],
ml_gen_info_get_global_data(!.Info, GlobalData0),
ml_gen_static_vector_type(MLDS_ModuleName, Context, Target,
FirstSolnFieldTypes, FirstSolnStructTypeNum, FirstSolnStructType,
FirstSolnTableFieldIds, GlobalData0, GlobalData1),
ml_gen_static_vector_type(MLDS_ModuleName, Context, Target,
OutTypes, LaterSolnStructTypeNum, LaterSolnStructType,
LaterSolnOutFieldIds, GlobalData1, GlobalData2),
(
( FirstSolnTableFieldIds = []
; FirstSolnTableFieldIds = [_]
),
unexpected($pred, "not enough field_ids")
;
FirstSolnTableFieldIds =
[NumLaterSolnsFieldId, FirstLaterSolnRowFieldId
| FirstSolnOutFieldIds]
),
ml_construct_model_non_switch_vector(ModuleInfo, StartVal, EndVal,
0, CaseSolns, FirstSolnStructType, LaterSolnStructType, OutTypes,
[], RevFirstSolnRowInitializers,
cord.init, LaterSolnRowInitializersCord, no, HadDummyRows),
list.reverse(RevFirstSolnRowInitializers, FirstSolnRowInitializers),
LaterSolnRowInitializers = cord.list(LaterSolnRowInitializersCord),
ml_gen_static_vector_defn(MLDS_ModuleName, FirstSolnStructTypeNum,
FirstSolnRowInitializers, FirstSolnVectorCommon,
GlobalData2, GlobalData3),
ml_gen_static_vector_defn(MLDS_ModuleName, LaterSolnStructTypeNum,
LaterSolnRowInitializers, LaterSolnVectorCommon,
GlobalData3, GlobalData),
ml_gen_info_set_global_data(GlobalData, !Info),
(
NeedBitVecCheck = do_not_need_bit_vec_check_no_gaps,
expect(unify(HadDummyRows, no), $pred,
"bad do_not_need_bit_vec_check_no_gaps")
;
NeedBitVecCheck = do_not_need_bit_vec_check_with_gaps,
expect(unify(HadDummyRows, yes), $pred,
"bad do_not_need_bit_vec_check_with_gaps")
;
NeedBitVecCheck = need_bit_vec_check,
expect(unify(HadDummyRows, yes), $pred, "bad need_bit_vec_check")
),
ml_gen_several_soln_lookup_code(Context, IndexRval,
OutVars, OutTypes, FirstSolnStructType, LaterSolnStructType,
NumLaterSolnsFieldId, FirstLaterSolnRowFieldId,
FirstSolnOutFieldIds, LaterSolnOutFieldIds,
FirstSolnVectorCommon, LaterSolnVectorCommon, NeedBitVecCheck,
MatchDefns, InRangeStmts, !Info),
InRangeStmt = ml_stmt_block(MatchDefns, [], InRangeStmts, Context),
(
NeedRangeCheck = do_not_need_range_check,
Stmt = InRangeStmt
;
NeedRangeCheck = need_range_check,
Difference = EndVal - StartVal,
RangeCheckCond = ml_binop(int_as_uint_cmp(le), IndexRval,
ml_const(mlconst_int(Difference))),
Stmt = ml_stmt_if_then_else(RangeCheckCond, InRangeStmt, no, Context)
).
%---------------------------------------------------------------------------%
ml_gen_several_soln_lookup_code(Context, SlotVarRval,
OutVars, OutTypes, FirstSolnStructType, LaterSolnStructType,
NumLaterSolnsFieldId, FirstLaterSolnRowFieldId,
FirstSolnOutFieldIds, LaterSolnOutFieldIds,
FirstSolnVectorCommon, LaterSolnVectorCommon, NeedBitVecCheck,
MatchDefns, Stmts, !Info) :-
make_several_soln_lookup_vars(Context, SeveralSolnLookupVars, !Info),
SeveralSolnLookupVars = ml_several_soln_lookup_vars(
NumLaterSolnsVarNameType, LaterSlotVarNameType, LimitVarNameType,
LimitAssignStmt, IncrLaterSlotVarStmt, MatchDefns),
NumLaterSolnsVarNameType =
mlds_local_var_name_type(NumLaterSolnsVarName, NumLaterSolnsType),
NumLaterSolnsVarLval =
ml_local_var(NumLaterSolnsVarName, NumLaterSolnsType),
LaterSlotVarNameType =
mlds_local_var_name_type(LaterSlotVarName, LaterSlotType),
LaterSlotVarLval =
ml_local_var(LaterSlotVarName, LaterSlotType),
LimitVarNameType =
mlds_local_var_name_type(LimitVarName, LimitType),
LimitVarLval =
ml_local_var(LimitVarName, LimitType),
NumLaterSolnsVarRval = ml_lval(NumLaterSolnsVarLval),
LaterSlotVarRval = ml_lval(LaterSlotVarLval),
LimitVarRval = ml_lval(LimitVarLval),
MLDS_IntType = mlds_builtin_type_int(int_type_int),
ml_generate_field_assign(NumLaterSolnsVarLval, MLDS_IntType,
NumLaterSolnsFieldId, FirstSolnVectorCommon, FirstSolnStructType,
SlotVarRval, Context, NumLaterSolnsAssignStmt),
ml_generate_field_assign(LaterSlotVarLval, MLDS_IntType,
FirstLaterSolnRowFieldId, FirstSolnVectorCommon, FirstSolnStructType,
SlotVarRval, Context, LaterSlotVarAssignStmt),
ml_generate_field_assigns(OutVars, OutTypes, FirstSolnOutFieldIds,
FirstSolnVectorCommon, FirstSolnStructType,
SlotVarRval, Context, FirstSolnLookupStmts, !Info),
ml_generate_field_assigns(OutVars, OutTypes, LaterSolnOutFieldIds,
LaterSolnVectorCommon, LaterSolnStructType,
LaterSlotVarRval, Context, LaterSolnLookupStmts, !Info),
ml_gen_call_current_success_cont(!.Info, Context, CallContStmt),
FirstLookupSucceedStmt = ml_stmt_block([], [],
FirstSolnLookupStmts ++ [CallContStmt], Context),
LaterLookupSucceedStmt = ml_stmt_block([], [],
LaterSolnLookupStmts ++ [CallContStmt, IncrLaterSlotVarStmt], Context),
MoreSolnsLoopCond = ml_binop(int_cmp(int_type_int, lt),
LaterSlotVarRval, LimitVarRval),
MoreSolnsLoopStmt = ml_stmt_while(may_loop_zero_times, MoreSolnsLoopCond,
LaterLookupSucceedStmt, [LaterSlotVarName], Context),
OneOrMoreSolnsStmts = [FirstLookupSucceedStmt, LaterSlotVarAssignStmt,
LimitAssignStmt, MoreSolnsLoopStmt],
(
( NeedBitVecCheck = do_not_need_bit_vec_check_no_gaps
; NeedBitVecCheck = do_not_need_bit_vec_check_with_gaps
),
Stmts = [NumLaterSolnsAssignStmt | OneOrMoreSolnsStmts]
;
NeedBitVecCheck = need_bit_vec_check,
OneOrMoreSolnsBlockStmt =
ml_stmt_block([], [], OneOrMoreSolnsStmts, Context),
AnySolnsCond = ml_binop(int_cmp(int_type_int, ge),
NumLaterSolnsVarRval, ml_const(mlconst_int(0))),
ZeroOrMoreSolnsStmt = ml_stmt_if_then_else(AnySolnsCond,
OneOrMoreSolnsBlockStmt, no, Context),
Stmts = [NumLaterSolnsAssignStmt, ZeroOrMoreSolnsStmt]
).
make_several_soln_lookup_vars(Context, SeveralSolnLookupVars, !Info) :-
ml_gen_info_new_aux_var_name(mcav_num_later_solns, NumLaterSolnsVarName,
!Info),
% We never need to trace ints.
IntType = mlds_builtin_type_int(int_type_int),
NumLaterSolnsVarDefn = ml_gen_mlds_var_decl(NumLaterSolnsVarName, IntType,
gc_no_stmt, Context),
NumLaterSolnsVarNameType =
mlds_local_var_name_type(NumLaterSolnsVarName, IntType),
NumLaterSolnsVarLval = ml_local_var(NumLaterSolnsVarName, IntType),
ml_gen_info_new_aux_var_name(mcav_later_slot, LaterSlotVarName, !Info),
% We never need to trace ints.
LaterSlotVarDefn = ml_gen_mlds_var_decl(LaterSlotVarName, IntType,
gc_no_stmt, Context),
LaterSlotVarNameType = mlds_local_var_name_type(LaterSlotVarName, IntType),
LaterSlotVarLval = ml_local_var(LaterSlotVarName, IntType),
ml_gen_info_new_aux_var_name(mcav_limit, LimitVarName, !Info),
% We never need to trace ints.
LimitVarDefn = ml_gen_mlds_var_decl(LimitVarName, IntType,
gc_no_stmt, Context),
LimitVarNameType = mlds_local_var_name_type(LimitVarName, IntType),
LimitVarLval = ml_local_var(LimitVarName, IntType),
Defns = [NumLaterSolnsVarDefn, LaterSlotVarDefn, LimitVarDefn],
LaterSlotVarRval = ml_lval(LaterSlotVarLval),
NumLaterSolnsVarRval = ml_lval(NumLaterSolnsVarLval),
LimitAssign = assign(LimitVarLval,
ml_binop(int_arith(int_type_int, ao_add),
LaterSlotVarRval, NumLaterSolnsVarRval)),
LimitAssignStmt = ml_stmt_atomic(LimitAssign, Context),
IncrLaterSlotVar = assign(LaterSlotVarLval,
ml_binop(int_arith(int_type_int, ao_add), LaterSlotVarRval,
ml_const(mlconst_int(1)))),
IncrLaterSlotVarStmt = ml_stmt_atomic(IncrLaterSlotVar, Context),
SeveralSolnLookupVars = ml_several_soln_lookup_vars(
NumLaterSolnsVarNameType, LaterSlotVarNameType, LimitVarNameType,
LimitAssignStmt, IncrLaterSlotVarStmt, Defns).
%---------------------------------------------------------------------------%
% The bitvector is an array of words (where we use the first 32 bits
% of each word). Each bit represents a tag value for the (range checked)
% input to the lookup switch. The bit is `1' iff we have a case for that
% tag value.
%
:- pred ml_generate_bitvec_test(mlds_module_name::in, prog_context::in,
mlds_rval::in, assoc_list(int, T)::in, int::in, int::in,
mlds_rval::out, ml_gen_info::in, ml_gen_info::out) is det.
ml_generate_bitvec_test(MLDS_ModuleName, Context, IndexRval, CaseVals,
Start, _End, CheckRval, !Info) :-
ml_gen_info_get_globals(!.Info, Globals),
get_target_host_min_word_size(Globals, WordSize),
( WordSize = word_size_32, WordBits = 32, Log2WordBits = 5
; WordSize = word_size_64, WordBits = 64, Log2WordBits = 6
),
ml_gen_info_get_global_data(!.Info, GlobalData0),
ml_generate_bit_vec(MLDS_ModuleName, Context, CaseVals, Start, WordBits,
BitVecArgRvals, BitVecRval, GlobalData0, GlobalData),
% Optimize the single-word case: if all the cases fit into a single word,
% then the word to use is always that word, and the index specifies which
% bit; we don't need the array.
( if BitVecArgRvals = [SingleWordRval] then
% Do not save GlobalData back into !Info.
WordRval = SingleWordRval,
BitNumRval = IndexRval
else
% Otherwise, the high bits of the index specify which word in the array
% to use and the low bits specify which bit in that word.
ml_gen_info_set_global_data(GlobalData, !Info),
% This is the same as
% WordNumRval = ml_binop(int_div, IndexRval,
% ml_const(mlconst_int(WordBits)))
% except that it can generate more efficient code.
WordNumRval =
ml_binop(unchecked_right_shift(int_type_int, shift_by_int),
IndexRval, ml_const(mlconst_int(Log2WordBits))),
ArrayElemType = array_elem_scalar(scalar_elem_int),
WordRval = ml_binop(array_index(ArrayElemType),
BitVecRval, WordNumRval),
% This is the same as
% BitNumRval = ml_binop(int_mod, IndexRval,
% ml_const(mlconst_int(WordBits)))
% except that it can generate more efficient code.
BitNumRval = ml_binop(bitwise_and(int_type_int), IndexRval,
ml_const(mlconst_int(WordBits - 1)))
),
CheckRval = ml_binop(bitwise_and(int_type_int), WordRval,
ml_binop(unchecked_left_shift(int_type_int, shift_by_int),
ml_const(mlconst_int(1)), BitNumRval)).
% We generate the bitvector by iterating through the cases marking the bit
% for each case. We represent the bitvector here as a map from the word
% number in the vector to the bits for that word.
%
:- pred ml_generate_bit_vec(mlds_module_name::in, prog_context::in,
assoc_list(int, T)::in, int::in, int::in,
list(mlds_rval)::out, mlds_rval::out,
ml_global_data::in, ml_global_data::out) is det.
ml_generate_bit_vec(MLDS_ModuleName, Context, CaseVals, Start, WordBits,
WordRvals, BitVecRval, !GlobalData) :-
map.init(BitMap0),
ml_generate_bit_vec_2(CaseVals, Start, WordBits, BitMap0, BitMap),
map.to_assoc_list(BitMap, WordVals),
ml_generate_bit_vec_initializers(WordVals, 0, WordRvals, WordInitializers),
Initializer = init_array(WordInitializers),
ConstType = mlds_array_type(mlds_builtin_type_int(int_type_int)),
ml_gen_static_scalar_const_value(MLDS_ModuleName, mgcv_bit_vector,
ConstType, Initializer, Context, BitVecRval, !GlobalData).
:- pred ml_generate_bit_vec_2(assoc_list(int, T)::in, int::in, int::in,
map(int, uint)::in, map(int, uint)::out) is det.
ml_generate_bit_vec_2([], _, _, !BitMap).
ml_generate_bit_vec_2([Tag - _ | Rest], Start, WordBits, !BitMap) :-
Val = Tag - Start,
WordNum = Val // WordBits,
Offset = Val mod WordBits,
( if map.search(!.BitMap, WordNum, X0) then
X1 = X0 \/ (1u << Offset),
map.det_update(WordNum, X1, !BitMap)
else
X1 = (1u << Offset),
map.det_insert(WordNum, X1, !BitMap)
),
ml_generate_bit_vec_2(Rest, Start, WordBits, !BitMap).
:- pred ml_generate_bit_vec_initializers(list(pair(int, uint))::in, int::in,
list(mlds_rval)::out, list(mlds_initializer)::out) is det.
ml_generate_bit_vec_initializers([], _, [], []).
ml_generate_bit_vec_initializers(All @ [WordNum - Bits | Rest], Count,
[Rval | Rvals], [Initializer | Initializers]) :-
( if Count < WordNum then
WordVal = 0u,
Remainder = All
else
WordVal = Bits,
Remainder = Rest
),
Rval = ml_const(mlconst_uint(WordVal)),
Initializer = init_obj(Rval),
ml_generate_bit_vec_initializers(Remainder, Count + 1,
Rvals, Initializers).
%---------------------------------------------------------------------------%
:- pred ml_construct_simple_switch_vector(module_info::in,
mlds_type::in, list(mlds_type)::in, int::in,
assoc_list(int, list(mlds_rval))::in, list(mlds_initializer)::out) is det.
ml_construct_simple_switch_vector(_, _, _, _, [], []).
ml_construct_simple_switch_vector(ModuleInfo, StructType, FieldTypes,
CurIndex, [Pair | Pairs], [RowInitializer | RowInitializers]) :-
Pair = Index - Rvals,
( if CurIndex < Index then
FieldRvals = list.map(ml_default_value_for_type, FieldTypes),
RemainingPairs = [Pair | Pairs]
else
FieldRvals = Rvals,
RemainingPairs = Pairs
),
FieldInitializers = list.map(wrap_init_obj, FieldRvals),
RowInitializer = init_struct(StructType, FieldInitializers),
ml_construct_simple_switch_vector(ModuleInfo, StructType, FieldTypes,
CurIndex + 1, RemainingPairs, RowInitializers).
:- pred ml_construct_model_non_switch_vector(module_info::in,
int::in, int::in, int::in, assoc_list(int, soln_consts(mlds_rval))::in,
mlds_type::in, mlds_type::in, list(mlds_type)::in,
list(mlds_initializer)::in, list(mlds_initializer)::out,
cord(mlds_initializer)::in, cord(mlds_initializer)::out,
bool::in, bool::out) is det.
ml_construct_model_non_switch_vector(ModuleInfo, CurIndex, EndVal,
!.NextLaterSolnRow, [],
FirstSolnStructType, LaterSolnStructType, FieldTypes,
!RevFirstSolnRowInitializers, !LaterSolnRowInitializersCord,
!HadDummyRows) :-
( if CurIndex > EndVal then
true
else
make_dummy_first_soln_row(FirstSolnStructType, FieldTypes,
!RevFirstSolnRowInitializers),
!:HadDummyRows = yes,
ml_construct_model_non_switch_vector(ModuleInfo, CurIndex + 1, EndVal,
!.NextLaterSolnRow, [],
FirstSolnStructType, LaterSolnStructType, FieldTypes,
!RevFirstSolnRowInitializers, !LaterSolnRowInitializersCord,
!HadDummyRows)
).
ml_construct_model_non_switch_vector(ModuleInfo, CurIndex, EndVal,
!.NextLaterSolnRow, [Pair | Pairs],
FirstSolnStructType, LaterSolnStructType, FieldTypes,
!RevFirstSolnRowInitializers, !LaterSolnRowInitializersCord,
!HadDummyRows) :-
Pair = Index - Soln,
( if CurIndex < Index then
make_dummy_first_soln_row(FirstSolnStructType, FieldTypes,
!RevFirstSolnRowInitializers),
!:HadDummyRows = yes,
NextPairs = [Pair | Pairs]
else
(
Soln = one_soln(FieldRvals),
FieldInitializers = list.map(wrap_init_obj, FieldRvals),
NumLaterSolnsInitializer = gen_init_int(0),
FirstLaterSlotInitializer = gen_init_int(-1),
FirstSolnFieldInitializers =
[NumLaterSolnsInitializer, FirstLaterSlotInitializer
| FieldInitializers],
FirstSolnRowInitializer =
init_struct(FirstSolnStructType, FirstSolnFieldInitializers),
!:RevFirstSolnRowInitializers =
[FirstSolnRowInitializer | !.RevFirstSolnRowInitializers]
;
Soln = several_solns(FirstSolnRvals, LaterSolns),
FieldInitializers = list.map(wrap_init_obj, FirstSolnRvals),
list.length(LaterSolns, NumLaterSolns),
NumLaterSolnsInitializer = gen_init_int(NumLaterSolns),
FirstLaterSlotInitializer = gen_init_int(!.NextLaterSolnRow),
FirstSolnFieldInitializers =
[NumLaterSolnsInitializer, FirstLaterSlotInitializer
| FieldInitializers],
FirstSolnRowInitializer =
init_struct(FirstSolnStructType, FirstSolnFieldInitializers),
!:RevFirstSolnRowInitializers =
[FirstSolnRowInitializer | !.RevFirstSolnRowInitializers],
LaterSolnRowInitializers = list.map(
ml_construct_later_soln_row(LaterSolnStructType),
LaterSolns),
!:LaterSolnRowInitializersCord = !.LaterSolnRowInitializersCord ++
from_list(LaterSolnRowInitializers),
!:NextLaterSolnRow = !.NextLaterSolnRow + NumLaterSolns
),
NextPairs = Pairs
),
ml_construct_model_non_switch_vector(ModuleInfo, CurIndex + 1, EndVal,
!.NextLaterSolnRow, NextPairs,
FirstSolnStructType, LaterSolnStructType, FieldTypes,
!RevFirstSolnRowInitializers, !LaterSolnRowInitializersCord,
!HadDummyRows).
ml_construct_later_soln_row(StructType, Rvals) = RowInitializer :-
FieldInitializers = list.map(wrap_init_obj, Rvals),
RowInitializer = init_struct(StructType, FieldInitializers).
%---------------------------------------------------------------------------%
:- pred make_dummy_first_soln_row(mlds_type::in, list(mlds_type)::in,
list(mlds_initializer)::in, list(mlds_initializer)::out) is det.
make_dummy_first_soln_row(FirstSolnStructType, FieldTypes,
!RevFirstSolnRowInitializers) :-
FieldRvals = list.map(ml_default_value_for_type, FieldTypes),
FieldInitializers = list.map(wrap_init_obj, FieldRvals),
NumLaterSolnsInitializer = gen_init_int(-1),
FirstLaterSlotInitializer = gen_init_int(-1),
FirstSolnFieldInitializers =
[NumLaterSolnsInitializer, FirstLaterSlotInitializer
| FieldInitializers],
FirstSolnRowInitializer =
init_struct(FirstSolnStructType, FirstSolnFieldInitializers),
!:RevFirstSolnRowInitializers =
[FirstSolnRowInitializer | !.RevFirstSolnRowInitializers].
ml_default_value_for_type(MLDS_Type) = DefaultRval :-
(
MLDS_Type = mlds_builtin_type_int(IntType),
(
IntType = int_type_int,
DefaultRval = ml_const(mlconst_int(0))
;
IntType = int_type_int8,
DefaultRval = ml_const(mlconst_int8(0i8))
;
IntType = int_type_int16,
DefaultRval = ml_const(mlconst_int16(0i16))
;
IntType = int_type_int32,
DefaultRval = ml_const(mlconst_int32(0i32))
;
IntType = int_type_int64,
DefaultRval = ml_const(mlconst_int64(0i64))
;
IntType = int_type_uint,
DefaultRval = ml_const(mlconst_uint(0u))
;
IntType = int_type_uint8,
DefaultRval = ml_const(mlconst_uint8(0u8))
;
IntType = int_type_uint16,
DefaultRval = ml_const(mlconst_uint16(0u16))
;
IntType = int_type_uint32,
DefaultRval = ml_const(mlconst_uint32(0u32))
;
IntType = int_type_uint64,
DefaultRval = ml_const(mlconst_uint64(0u64))
)
;
MLDS_Type = mlds_builtin_type_float,
DefaultRval = ml_const(mlconst_float(0.0))
;
MLDS_Type = mlds_builtin_type_string,
DefaultRval = ml_cast(MLDS_Type, ml_const(mlconst_int(0)))
;
MLDS_Type = mlds_builtin_type_char,
DefaultRval = ml_const(mlconst_char(0))
;
MLDS_Type = mlds_native_bool_type,
DefaultRval = ml_const(mlconst_false)
;
( MLDS_Type = mercury_nb_type(_, _)
; MLDS_Type = mlds_mercury_array_type(_)
; MLDS_Type = mlds_foreign_type(_)
; MLDS_Type = mlds_class_type(_)
; MLDS_Type = mlds_enum_class_type(_)
; MLDS_Type = mlds_env_type(_)
; MLDS_Type = mlds_struct_type(_)
; MLDS_Type = mlds_array_type(_)
; MLDS_Type = mlds_mostly_generic_array_type(_)
; MLDS_Type = mlds_func_type(_)
; MLDS_Type = mlds_generic_type
; MLDS_Type = mlds_type_info_type
; MLDS_Type = mlds_pseudo_type_info_type
),
DefaultRval = ml_cast(MLDS_Type, ml_const(mlconst_int(0)))
;
( MLDS_Type = mlds_cont_type(_)
; MLDS_Type = mlds_commit_type
; MLDS_Type = mlds_ptr_type(_)
; MLDS_Type = mlds_generic_env_ptr_type
; MLDS_Type = mlds_rtti_type(_)
; MLDS_Type = mlds_tabling_type(_)
; MLDS_Type = mlds_unknown_type
),
unexpected($pred, "unexpected MLDS_Type")
).
%---------------------------------------------------------------------------%
:- end_module ml_backend.ml_lookup_switch.
%---------------------------------------------------------------------------%