Files
mercury/compiler/ml_simplify_switch.m
Zoltan Somogyi ea4f95a7ed Use var_tables in lco.m, and when dumping goals.
Since this is the first converted module that dumps out goals when
debugging trace flags are enabled, this required generalizing the code
that does that, to take either varsets or var_tables as a means of
specifying the names of variables. We do this via a new type,
var_name_source, which contains either a varset or a var_table.

Almost all of this diff is there to implement this generalization.
A large part of it affects code in the parse_tree package that we use
to write out the parts of HLDS goals that are defined by types defined
in that package. Since we want to avoid making any part of the parse_tree
package dependent on the hlds package, this required defining the
var_name_source type in the parse_tree package, which in turn requires
var_table.m to be in that same package.

compiler/lco.m:
    Convert this module to use var_tables instead of varsets and vartypes.

compiler/var_table.m:
    Move this module from the hlds package to the parse_tree package.

    To make this, possible, move the parts that required access to the HLDS
    to hlds_pred.m, from where it was usually invoked.

    Export some utility predicates to allow the moved code to work
    in hlds_pred.m without access to the actual definition of the
    var_table type.

    Define the var_name_source type.

    Add some utility functions for use by code writing out variable names.

compiler/hlds_pred.m:
    Add the code moved from var_table.m.

compiler/vartypes.m:
    Move this module from the hlds package to the parse_tree package,
    for symmetry with var_table.m. It did not depend on being in hlds
    in any way.

compiler/hlds.m:
compiler/parse_tree.m:
    Move vartypes.m and var_table.m from the hlds package
    to the parse_tree package.

compiler/hlds_out_goal.m:
    Change all the predicates in this module to take a var_name_source
    instead of a prog_varset.

    Fix some comments.

compiler/hlds_out_util.m:
    Change some of the predicates in this module (those called from
    hlds_out_goal.m) to take a var_name_source instead of a prog_varset.

compiler/parse_tree_out_term.m:
    Provide variants of some existing predicates and functions that take
    var_name_sources instead of varsets. The code of the copies
    duplicates the logic of the originals, though I hope that this
    duplication can be done away with at the end of the transition.
    (The best solution would be to use a typeclass with methods
    that convert vars to their names, but we would want to ensure
    that the compiler can specialize all the affected predicates
    and functions to the two instances of this typeclass, which is
    something that we cannot do yet. In the meantime, the lack of
    any generalization in the old versions preserves their performance.)

tools/sort_imports:
tools/filter_sort_imports:
    A new tool that automatically sorts any occurrences of consecutive
    ":- import_module" declarations in the named files. The sorting is done
    in filter_sort_imports; sort_imports loops over the named files.

    After automatically replacing all occurrences of hlds.{vartypes,var_table}
    in import_module declarations with their parse_tree versions, the updated
    import_module declarations were usually out of order with respect to
    their neighbours. I used this script to fix that, and some earlier
    out-of-order imports.

compiler/accumulator.m:
compiler/add_class.m:
compiler/add_clause.m:
compiler/add_foreign_proc.m:
compiler/add_heap_ops.m:
compiler/add_pragma_type_spec.m:
compiler/add_pred.m:
compiler/add_trail_ops.m:
compiler/analysis.m:
compiler/arg_info.m:
compiler/build_mode_constraints.m:
compiler/bytecode_gen.m:
compiler/call_gen.m:
compiler/check_promise.m:
compiler/closure_analysis.m:
compiler/closure_gen.m:
compiler/code_info.m:
compiler/code_loc_dep.m:
compiler/common.m:
compiler/compile_target_code.m:
compiler/complexity.m:
compiler/const_prop.m:
compiler/constraint.m:
compiler/continuation_info.m:
compiler/convert_parse_tree.m:
compiler/coverage_profiling.m:
compiler/cse_detection.m:
compiler/ctgc.datastruct.m:
compiler/ctgc.util.m:
compiler/dead_proc_elim.m:
compiler/deep_profiling.m:
compiler/deforest.m:
compiler/delay_construct.m:
compiler/delay_partial_inst.m:
compiler/dep_par_conj.m:
compiler/det_analysis.m:
compiler/det_report.m:
compiler/det_util.m:
compiler/direct_arg_in_out.m:
compiler/disj_gen.m:
compiler/distance_granularity.m:
compiler/equiv_type_hlds.m:
compiler/exception_analysis.m:
compiler/file_names.m:
compiler/float_regs.m:
compiler/follow_vars.m:
compiler/format_call.m:
compiler/generate_dep_d_files.m:
compiler/get_dependencies.m:
compiler/goal_expr_to_goal.m:
compiler/goal_mode.m:
compiler/goal_path.m:
compiler/goal_store.m:
compiler/goal_util.m:
compiler/granularity.m:
compiler/hhf.m:
compiler/higher_order.m:
compiler/hlds_clauses.m:
compiler/hlds_code_util.m:
compiler/hlds_error_util.m:
compiler/hlds_goal.m:
compiler/hlds_llds.m:
compiler/hlds_out_pred.m:
compiler/hlds_rtti.m:
compiler/hlds_statistics.m:
compiler/inlining.m:
compiler/inst_check.m:
compiler/inst_test.m:
compiler/inst_user.m:
compiler/instance_method_clauses.m:
compiler/instmap.m:
compiler/intermod.m:
compiler/intermod_analysis.m:
compiler/interval.m:
compiler/introduce_exists_casts.m:
compiler/introduce_parallelism.m:
compiler/item_util.m:
compiler/lambda.m:
compiler/live_vars.m:
compiler/liveness.m:
compiler/llds.m:
compiler/llds_out_data.m:
compiler/llds_out_file.m:
compiler/llds_out_util.m:
compiler/lookup_switch.m:
compiler/loop_inv.m:
compiler/make.module_target.m:
compiler/make.util.m:
compiler/make_goal.m:
compiler/make_hlds_separate_items.m:
compiler/make_hlds_types.m:
compiler/mark_tail_calls.m:
compiler/mercury_compile_mlds_back_end.m:
compiler/middle_rec.m:
compiler/ml_accurate_gc.m:
compiler/ml_args_util.m:
compiler/ml_call_gen.m:
compiler/ml_closure_gen.m:
compiler/ml_code_gen.m:
compiler/ml_code_util.m:
compiler/ml_commit_gen.m:
compiler/ml_disj_gen.m:
compiler/ml_foreign_proc_gen.m:
compiler/ml_gen_info.m:
compiler/ml_lookup_switch.m:
compiler/ml_proc_gen.m:
compiler/ml_simplify_switch.m:
compiler/ml_switch_gen.m:
compiler/ml_tag_switch.m:
compiler/ml_unify_gen.m:
compiler/ml_unify_gen_construct.m:
compiler/ml_unify_gen_deconstruct.m:
compiler/ml_unify_gen_test.m:
compiler/ml_unify_gen_util.m:
compiler/mlds_to_c_data.m:
compiler/mlds_to_c_func.m:
compiler/mlds_to_c_global.m:
compiler/mlds_to_cs_class.m:
compiler/mlds_to_cs_file.m:
compiler/mlds_to_java_data.m:
compiler/mlds_to_java_file.m:
compiler/mlds_to_java_stmt.m:
compiler/mlds_to_java_type.m:
compiler/mmc_analysis.m:
compiler/mode_comparison.m:
compiler/mode_constraints.m:
compiler/mode_debug.m:
compiler/mode_errors.m:
compiler/mode_info.m:
compiler/mode_ordering.m:
compiler/modecheck_call.m:
compiler/modecheck_coerce.m:
compiler/modecheck_goal.m:
compiler/modecheck_unify.m:
compiler/modecheck_util.m:
compiler/modes.m:
compiler/module_cmds.m:
compiler/old_type_constraints.m:
compiler/opt_debug.m:
compiler/optimize.m:
compiler/options_file.m:
compiler/ordering_mode_constraints.m:
compiler/par_loop_control.m:
compiler/parse_item.m:
compiler/parse_string_format.m:
compiler/parse_tree_out_inst.m:
compiler/parse_tree_to_term.m:
compiler/parse_util.m:
compiler/pd_debug.m:
compiler/pd_info.m:
compiler/pd_util.m:
compiler/peephole.m:
compiler/polymorphism.m:
compiler/polymorphism_info.m:
compiler/polymorphism_lambda.m:
compiler/polymorphism_type_class_info.m:
compiler/polymorphism_type_info.m:
compiler/post_typecheck.m:
compiler/pragma_c_gen.m:
compiler/pred_name.m:
compiler/pred_table.m:
compiler/prog_item.m:
compiler/prog_rep.m:
compiler/prop_mode_constraints.m:
compiler/purity.m:
compiler/push_goals_together.m:
compiler/qual_info.m:
compiler/quantification.m:
compiler/rbmm.execution_path.m:
compiler/rbmm.m:
compiler/rbmm.points_to_analysis.m:
compiler/rbmm.points_to_graph.m:
compiler/rbmm.points_to_info.m:
compiler/rbmm.region_resurrection_renaming.m:
compiler/rbmm.region_transformation.m:
compiler/recompilation.used_file.m:
compiler/recompilation.version.m:
compiler/recompute_instmap_deltas.m:
compiler/resolve_unify_functor.m:
compiler/rtti.m:
compiler/rtti_out.m:
compiler/rtti_to_mlds.m:
compiler/saved_vars.m:
compiler/set_of_var.m:
compiler/simplify_goal_call.m:
compiler/simplify_goal_conj.m:
compiler/simplify_goal_disj.m:
compiler/simplify_goal_ite.m:
compiler/simplify_goal_scope.m:
compiler/simplify_goal_switch.m:
compiler/simplify_goal_unify.m:
compiler/simplify_info.m:
compiler/simplify_proc.m:
compiler/size_prof.m:
compiler/smm_common.m:
compiler/ssdebug.m:
compiler/stack_alloc.m:
compiler/stack_layout.m:
compiler/stack_opt.m:
compiler/stm_expand.m:
compiler/store_alloc.m:
compiler/structure_reuse.analysis.m:
compiler/structure_reuse.direct.choose_reuse.m:
compiler/structure_reuse.direct.detect_garbage.m:
compiler/structure_reuse.domain.m:
compiler/structure_reuse.indirect.m:
compiler/structure_reuse.lbu.m:
compiler/structure_reuse.lfu.m:
compiler/structure_sharing.analysis.m:
compiler/structure_sharing.domain.m:
compiler/superhomogeneous.m:
compiler/switch_detection.m:
compiler/switch_gen.m:
compiler/switch_util.m:
compiler/table_gen.m:
compiler/tabling_analysis.m:
compiler/term_constr_build.m:
compiler/term_constr_data.m:
compiler/term_constr_initial.m:
compiler/term_constr_main.m:
compiler/term_constr_main_types.m:
compiler/term_constr_util.m:
compiler/term_pass1.m:
compiler/term_traversal.m:
compiler/term_util.m:
compiler/trace_gen.m:
compiler/trailing_analysis.m:
compiler/transform_llds.m:
compiler/try_expand.m:
compiler/tupling.m:
compiler/type_assign.m:
compiler/type_ctor_info.m:
compiler/type_util.m:
compiler/typecheck.m:
compiler/typecheck_debug.m:
compiler/typecheck_errors.m:
compiler/typecheck_info.m:
compiler/unify_gen_construct.m:
compiler/unify_gen_deconstruct.m:
compiler/unify_proc.m:
compiler/unique_modes.m:
compiler/unneeded_code.m:
compiler/untupling.m:
compiler/unused_args.m:
compiler/unused_imports.m:
compiler/var_locn.m:
compiler/write_deps_file.m:
compiler/write_module_interface_files.m:
    Conform to the changes above.
2022-04-18 02:00:38 +10:00

510 lines
19 KiB
Mathematica

%---------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%---------------------------------------------------------------------------%
% Copyright (C) 2000-2001, 2003-2011 The University of Melbourne.
% Copyright (C) 2015-2018 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_simplify_switch.m.
% Main author: fjh.
%
% This module, which is invoked by the various parts of the MLDS code generator
% that generate switches, converts MLDS switches into computed gotos
% or if-then-else chains.
%
% We should eventually also handle lookup switches and binary search switches
% here too.
%
% The choice of which exactly which simplifications will get performed
% depends on the target (e.g. whether it understands switches) and the
% --prefer-switch option.
%
%---------------------------------------------------------------------------%
:- module ml_backend.ml_simplify_switch.
:- interface.
:- import_module ml_backend.ml_gen_info.
:- import_module ml_backend.mlds.
:- pred ml_simplify_switch(mlds_stmt::in, mlds_stmt::out,
ml_gen_info::in, ml_gen_info::out) is det.
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
:- implementation.
:- import_module backend_libs.
:- import_module backend_libs.builtin_ops.
:- import_module backend_libs.switch_util.
:- import_module libs.
:- import_module libs.globals.
:- import_module libs.optimization_options.
:- import_module libs.options.
:- import_module ml_backend.ml_code_util.
:- import_module ml_backend.ml_target_util.
:- import_module ml_backend.ml_util.
:- import_module parse_tree.
:- import_module parse_tree.prog_data.
:- import_module parse_tree.prog_type.
:- import_module bool.
:- import_module int.
:- import_module list.
:- import_module map.
:- import_module maybe.
:- import_module require.
%---------------------------------------------------------------------------%
ml_simplify_switch(Stmt0, Stmt, !Info) :-
ml_gen_info_get_globals(!.Info, Globals),
( if
% Convert dense int switches into computed gotos,
% unless the target prefers switches.
% Is this an int switch?
Stmt0 = ml_stmt_switch(Type, Rval, Range, Cases, Default, Context),
is_integral_type(Type, IntType),
% Does the target want us to convert dense int switches
% into computed gotos?
globals_target_supports_computed_goto(Globals) = yes,
not (
globals_target_supports_int_type_switch(Globals, IntType) = yes,
globals.lookup_bool_option(Globals, prefer_switch, yes)
),
% Is the switch big enough?
list.length(Cases, NumCases),
globals.get_opt_tuple(Globals, OptTuple),
DenseSize = OptTuple ^ ot_dense_switch_size,
NumCases >= DenseSize,
% ... and dense enough?
ReqDensity = OptTuple ^ ot_dense_switch_req_density,
is_dense_switch(Cases, ReqDensity)
then
maybe_eliminate_default(Range, Cases, Default, ReqDensity,
FirstVal, LastVal, NeedRangeCheck),
generate_dense_switch(Cases, Default, FirstVal, LastVal,
NeedRangeCheck, Type, Rval, Context, Stmts, !Info),
Stmt = ml_stmt_block([], [], Stmts, Context)
else if
% Convert the remaining (sparse) int switches into if-then-else chains,
% unless the target prefers switches.
Stmt0 = ml_stmt_switch(Type, Rval, _Range, Cases, Default, Context),
is_integral_type(Type, IntType),
not (
globals_target_supports_int_type_switch(Globals, IntType) = yes,
globals.lookup_bool_option(Globals, prefer_switch, yes)
)
then
Stmt = ml_switch_to_if_else_chain(Cases, Default, Rval, Context)
else if
% Optimize away trivial switches (these can occur e.g. with
% --tags none, where the tag test always has only one reachable case)
Stmt0 = ml_stmt_switch(_Type, _Rval, _Range, Cases, Default, _Context),
Cases = [SingleCase],
Default = default_is_unreachable
then
SingleCase = mlds_switch_case(_FirstCond, _LaterConds, CaseStmt),
Stmt = CaseStmt
else
Stmt = Stmt0
).
:- pred is_integral_type(mlds_type::in, int_type::out) is semidet.
is_integral_type(MLDSType, IntType) :-
require_complete_switch [MLDSType]
(
MLDSType = mlds_builtin_type_int(IntType)
;
MLDSType = mlds_builtin_type_char,
IntType = int_type_int
;
( MLDSType = mlds_builtin_type_float
; MLDSType = mlds_builtin_type_string
),
fail
;
( MLDSType = mlds_mercury_array_type(_)
; MLDSType = mlds_cont_type(_)
; MLDSType = mlds_commit_type
; MLDSType = mlds_native_bool_type
; MLDSType = mlds_builtin_type_float
; MLDSType = mlds_foreign_type(_)
; MLDSType = mlds_class_type(_)
; MLDSType = mlds_ptr_type(_)
; MLDSType = mlds_func_type(_)
; MLDSType = mlds_type_info_type
; MLDSType = mlds_generic_type
; MLDSType = mlds_generic_env_ptr_type
; MLDSType = mlds_array_type(_)
; MLDSType = mlds_mostly_generic_array_type(_)
; MLDSType = mlds_pseudo_type_info_type
; MLDSType = mlds_rtti_type(_)
; MLDSType = mlds_tabling_type(_)
; MLDSType = mlds_unknown_type
),
fail
;
MLDSType = mercury_nb_type(_, CtorCat),
require_complete_switch [CtorCat]
(
CtorCat = ctor_cat_enum(cat_enum_mercury),
IntType = int_type_int
;
( CtorCat = ctor_cat_higher_order
; CtorCat = ctor_cat_tuple
; CtorCat = ctor_cat_builtin_dummy
; CtorCat = ctor_cat_variable
; CtorCat = ctor_cat_void
; CtorCat = ctor_cat_system(_)
; CtorCat = ctor_cat_user(cat_user_general)
; CtorCat = ctor_cat_user(cat_user_notag)
; CtorCat = ctor_cat_user(cat_user_abstract_notag)
; CtorCat = ctor_cat_user(cat_user_direct_dummy)
; CtorCat = ctor_cat_user(cat_user_abstract_dummy)
),
fail
;
CtorCat = ctor_cat_enum(cat_enum_foreign),
% XXX We can switch on foreign enumerations in C, but this may
% not be the case for the other target languages.
fail
;
CtorCat = ctor_cat_builtin(_),
unexpected($pred, "mercury_nb_type but ctor_cat_builtin")
)
).
:- pred is_dense_switch(list(mlds_switch_case)::in, int::in) is semidet.
is_dense_switch(Cases, ReqDensity) :-
% Need at least two cases
NumCases = list.length(Cases),
NumCases > 2,
% The switch needs to be dense enough.
find_min_and_max_in_cases(Cases, FirstCaseVal, LastCaseVal),
CasesRange = LastCaseVal - FirstCaseVal + 1,
Density = switch_density(NumCases, CasesRange),
Density > ReqDensity.
% For switches with a default, 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.
%
:- pred maybe_eliminate_default(mlds_switch_range::in,
list(mlds_switch_case)::in, mlds_switch_default::in, int::in,
int::out, int::out, need_range_check::out) is det.
maybe_eliminate_default(Range, Cases, Default, ReqDensity,
FirstVal, LastVal, NeedRangeCheck) :-
( if
Default \= default_is_unreachable,
Range = mlds_switch_range(Min, Max),
TypeRange = Max - Min + 1,
NumCases = list.length(Cases),
NoDefaultDensity = switch_density(NumCases, TypeRange),
NoDefaultDensity > ReqDensity
then
NeedRangeCheck = dont_need_range_check,
FirstVal = Min,
LastVal = Max
else
(
Default = default_is_unreachable,
NeedRangeCheck = dont_need_range_check
;
( Default = default_do_nothing
; Default = default_case(_)
),
NeedRangeCheck = need_range_check
),
find_min_and_max_in_cases(Cases, FirstCaseVal, LastCaseVal),
FirstVal = FirstCaseVal,
LastVal = LastCaseVal
).
%---------------------------------------------------------------------------%
% Find the highest and lowest case values in a list of cases.
%
:- pred find_min_and_max_in_cases(list(mlds_switch_case)::in,
int::out, int::out) is det.
find_min_and_max_in_cases(Cases, Min, Max) :-
list.foldl2(find_min_and_max_in_case, Cases,
int.max_int, Min, int.min_int, Max).
:- pred find_min_and_max_in_case(mlds_switch_case::in,
int::in, int::out, int::in, int::out) is det.
find_min_and_max_in_case(Case, !Min, !Max) :-
Case = mlds_switch_case(FirstCond, LaterConds, _CaseStmt),
find_min_and_max_in_case_cond(FirstCond, !Min, !Max),
list.foldl2(find_min_and_max_in_case_cond, LaterConds, !Min, !Max).
:- pred find_min_and_max_in_case_cond(mlds_case_match_cond::in,
int::in, int::out, int::in, int::out) is det.
find_min_and_max_in_case_cond(match_value(Rval), !Min, !Max) :-
( if
Rval = ml_const(mlconst_int(Val))
then
int.min(Val, !Min),
int.max(Val, !Max)
else
unexpected($pred, "non-int case")
).
find_min_and_max_in_case_cond(match_range(MinRval, MaxRval), !Min, !Max) :-
( if
MinRval = ml_const(mlconst_int(RvalMin)),
MaxRval = ml_const(mlconst_int(RvalMax))
then
int.min(RvalMin, !Min),
int.max(RvalMax, !Max)
else
unexpected($pred, "non-int case")
).
%---------------------------------------------------------------------------%
% 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, 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.
generate_dense_switch(Cases, Default, FirstVal, LastVal, NeedRangeCheck,
_Type, Rval, Context, Stmts, !Info) :-
% If the case values start at some number other than 0,
% then subtract that number to give us a zero-based index.
( if FirstVal = 0 then
Index = Rval
else
Index = ml_binop(int_sub(int_type_int), Rval,
ml_const(mlconst_int(FirstVal)))
),
% Now generate the jump table.
ml_gen_new_label(EndLabel, !Info),
map.init(CaseLabelsMap0),
generate_cases(Cases, EndLabel, CaseLabelsMap0, CaseLabelsMap,
CasesCode, !Info),
ml_gen_new_label(DefaultLabel, !Info),
CaseLabels = get_case_labels(FirstVal, LastVal,
CaseLabelsMap, DefaultLabel),
DefaultLabelStmt = ml_stmt_label(DefaultLabel, Context),
(
Default = default_is_unreachable,
% We still need the label, in case we inserted references to it
% into (unreachable) slots in the jump table.
DefaultStmts = [DefaultLabelStmt]
;
Default = default_do_nothing,
DefaultStmts = [DefaultLabelStmt]
;
Default = default_case(DefaultCase),
DefaultStmts = [DefaultLabelStmt, DefaultCase]
),
StartComment = ml_stmt_atomic(comment("switch (using dense jump table)"),
Context),
DoJump = ml_stmt_computed_goto(Index, CaseLabels, Context),
EndLabelStmt = ml_stmt_label(EndLabel, Context),
EndComment = ml_stmt_atomic(comment("End of dense switch"), Context),
% We may need to check that the value of the variable lies within the
% appropriate range.
(
NeedRangeCheck = need_range_check,
Difference = LastVal - FirstVal,
InRange = ml_binop(unsigned_le,
Index, ml_const(mlconst_int(Difference))),
Else = yes(ml_stmt_block([], [], DefaultStmts, Context)),
SwitchBody = ml_stmt_block([], [], [DoJump | CasesCode], Context),
DoSwitch = ml_stmt_if_then_else(InRange, SwitchBody, Else, Context),
Stmts = [StartComment, DoSwitch, EndLabelStmt, EndComment]
;
NeedRangeCheck = dont_need_range_check,
Stmts =
[StartComment, DoJump | CasesCode] ++
DefaultStmts ++
[EndLabelStmt, EndComment]
).
:- pred generate_cases(list(mlds_switch_case)::in, mlds_label::in,
case_labels_map::in, case_labels_map::out, list(mlds_stmt)::out,
ml_gen_info::in, ml_gen_info::out) is det.
generate_cases([], _EndLabel, !CaseLabelsMap, [], !Info).
generate_cases([Case | Cases], EndLabel, !CaseLabelsMap, Stmts, !Info) :-
generate_case(Case, EndLabel, !CaseLabelsMap, CaseStmts, !Info),
generate_cases(Cases, EndLabel, !CaseLabelsMap, CasesStmts, !Info),
Stmts = CaseStmts ++ CasesStmts.
% This converts an MLDS switch case into code for a dense switch case,
% by adding a label at the front and a `goto <EndLabel>' at the end.
% It also inserts the label for this case into the CaseLabelsMap.
%
:- pred generate_case(mlds_switch_case::in, mlds_label::in,
case_labels_map::in, case_labels_map::out, list(mlds_stmt)::out,
ml_gen_info::in, ml_gen_info::out) is det.
generate_case(Case, EndLabel, !CaseLabelsMap, Stmts, !Info) :-
Case = mlds_switch_case(FirstCond, LaterConds, CaseStmt),
ml_gen_new_label(ThisLabel, !Info),
insert_case_into_map(ThisLabel, FirstCond, !CaseLabelsMap),
list.foldl(insert_case_into_map(ThisLabel), LaterConds, !CaseLabelsMap),
Context = get_mlds_stmt_context(CaseStmt),
LabelComment = ml_stmt_atomic(comment("case of dense switch"), Context),
LabelCode = ml_stmt_label(ThisLabel, Context),
JumpComment = ml_stmt_atomic(comment("branch to end of dense switch"),
Context),
JumpCode = ml_stmt_goto(goto_label(EndLabel), Context),
Stmts = [LabelComment, LabelCode, CaseStmt, JumpComment, JumpCode].
%---------------------------------------------------------------------------%
%
% We build up a map which records which label should be used for
% each case value.
%
:- type case_labels_map == map(int, mlds_label).
:- pred insert_case_into_map(mlds_label::in, mlds_case_match_cond::in,
case_labels_map::in, case_labels_map::out) is det.
insert_case_into_map(ThisLabel, Cond, !CaseLabelsMap) :-
(
Cond = match_value(Rval),
( if Rval = ml_const(mlconst_int(Val)) then
map.det_insert(Val, ThisLabel, !CaseLabelsMap)
else
unexpected($pred, "non-int case")
)
;
Cond = match_range(MinRval, MaxRval),
( if
MinRval = ml_const(mlconst_int(Min)),
MaxRval = ml_const(mlconst_int(Max))
then
insert_range_into_map(Min, Max, ThisLabel, !CaseLabelsMap)
else
unexpected($pred, "non-int case")
)
).
:- pred insert_range_into_map(int::in, int::in, mlds_label::in,
case_labels_map::in, case_labels_map::out) is det.
insert_range_into_map(Min, Max, ThisLabel, !CaseLabelsMap) :-
( if Min > Max then
true
else
map.det_insert(Min, ThisLabel, !CaseLabelsMap),
insert_range_into_map(Min + 1, Max, ThisLabel, !CaseLabelsMap)
).
%---------------------------------------------------------------------------%
% Given the starting and ending case values, the mapping from case values
% to labels, and the default label to use for case values which aren't in
% the map, this function returns the list of labels to use for the case
% values.
%
:- func get_case_labels(int, int, map(int, mlds_label), mlds_label)
= list(mlds_label).
get_case_labels(ThisVal, LastVal, CaseLabelsMap, DefaultLabel) = CaseLabels :-
( if ThisVal > LastVal then
CaseLabels = []
else
( if map.search(CaseLabelsMap, ThisVal, CaseLabel0) then
CaseLabel = CaseLabel0
else
CaseLabel = DefaultLabel
),
CaseLabels1 = get_case_labels(ThisVal + 1, LastVal,
CaseLabelsMap, DefaultLabel),
CaseLabels = [CaseLabel | CaseLabels1]
).
%---------------------------------------------------------------------------%
% Convert an int switch to a chain of if-then-elses that test each case
% in turn.
%
:- func ml_switch_to_if_else_chain(list(mlds_switch_case), mlds_switch_default,
mlds_rval, prog_context) = mlds_stmt.
ml_switch_to_if_else_chain([], Default, _Rval, Context) = Stmt :-
(
Default = default_do_nothing,
Stmt = ml_stmt_block([], [], [], Context)
;
Default = default_is_unreachable,
Stmt = ml_stmt_block([], [], [], Context)
;
Default = default_case(Stmt)
).
ml_switch_to_if_else_chain([Case | Cases], Default, SwitchRval, Context) =
Stmt :-
Case = mlds_switch_case(FirstMatchCond, LaterMatchConds, CaseStmt),
( if
Cases = [],
Default = default_is_unreachable
then
Stmt = CaseStmt
else
AllMatchConds = [FirstMatchCond | LaterMatchConds],
CaseMatchedRval = ml_gen_case_match_conds(AllMatchConds, SwitchRval),
RestStmt = ml_switch_to_if_else_chain(Cases, Default, SwitchRval,
Context),
Stmt = ml_stmt_if_then_else(CaseMatchedRval, CaseStmt, yes(RestStmt),
Context)
).
% Generate an rval which will be true iff any of the specified list of
% case conditions matches the specified rval (which must have integral
% type).
%
:- func ml_gen_case_match_conds(list(mlds_case_match_cond), mlds_rval)
= mlds_rval.
ml_gen_case_match_conds([], _) = ml_const(mlconst_false).
ml_gen_case_match_conds([Cond], SwitchRval) =
ml_gen_case_match_cond(Cond, SwitchRval).
ml_gen_case_match_conds([Cond1, Cond2 | Conds], SwitchRval) =
ml_binop(logical_or,
ml_gen_case_match_cond(Cond1, SwitchRval),
ml_gen_case_match_conds([Cond2 | Conds], SwitchRval)).
% Generate an rval which will be true iff the specified case condition
% matches the specified rval (which must have integral type).
%
:- func ml_gen_case_match_cond(mlds_case_match_cond, mlds_rval) = mlds_rval.
ml_gen_case_match_cond(match_value(CaseRval), SwitchRval) =
ml_binop(eq(int_type_int), CaseRval, SwitchRval).
ml_gen_case_match_cond(match_range(MinRval, MaxRval), SwitchRval) =
ml_binop(logical_and,
ml_binop(int_gt(int_type_int), SwitchRval, MinRval),
ml_binop(int_le(int_type_int), SwitchRval, MaxRval)).
%---------------------------------------------------------------------------%
:- end_module ml_backend.ml_simplify_switch.
%---------------------------------------------------------------------------%