Files
mercury/compiler/ml_switch_gen.m
Fergus Henderson 7597790760 Use sub-modules to structure the modules in the Mercury compiler directory.
The main aim of this change is to make the overall, high-level structure
of the compiler clearer, and to encourage better encapsulation of the
major components.

compiler/libs.m:
compiler/backend_libs.m:
compiler/parse_tree.m:
compiler/hlds.m:
compiler/check_hlds.m:
compiler/transform_hlds.m:
compiler/bytecode_backend.m:
compiler/aditi_backend.m:
compiler/ml_backend.m:
compiler/ll_backend.m:
compiler/top_level.m:
	New files.  One module for each of the major components of the
	Mercury compiler.  These modules contain (as separate sub-modules)
	all the other modules in the Mercury compiler, except gcc.m and
	mlds_to_gcc.m.

Mmakefile:
compiler/Mmakefile:
	Handle the fact that the top-level module is now `top_level',
	not `mercury_compile' (since `mercury_compile' is a sub-module
	of `top_level').

compiler/Mmakefile:
	Update settings of *FLAGS-<modulename> to use the appropriate
	nested module names.

compiler/recompilation_check.m:
compiler/recompilation_version.m:
compiler/recompilation_usage.m:
compiler/recompilation.check.m:
compiler/recompilation.version.m:
compiler/recompilation.version.m:
	Convert the `recompilation_*' modules into sub-modules of the
	`recompilation' module.

compiler/*.m:
compiler/*.pp:
	Module-qualify the module names in `:- module', `:- import_module',
	and `:- use_module' declarations.

compiler/base_type_info.m:
compiler/base_type_layout.m:
	Deleted these unused empty modules.

compiler/prog_data.m:
compiler/globals.m:
	Move the `foreign_language' type from prog_data to globals.

compiler/mlds.m:
compiler/ml_util.m:
compiler/mlds_to_il.m:
	Import `globals', for `foreign_language'.

Mmake.common.in:
trace/Mmakefile:
runtime/Mmakefile:
	Rename the %.check.c targets as %.check_hdr.c,
	to avoid conflicts with compiler/recompilation.check.c.
2002-03-20 12:37:56 +00:00

471 lines
17 KiB
Mathematica

%-----------------------------------------------------------------------------%
% Copyright (C) 1994-2002 The University of Melbourne.
% This file may only be copied under the terms of the GNU General
% Public License - see the file COPYING in the Mercury distribution.
%-----------------------------------------------------------------------------%
%
% File: ml_switch_gen.m
% Author: fjh (adapted from switch_gen.m)
%
% This module handles the generation of code for switches for the MLDS
% back-end. Switches are disjunctions that do not require backtracking.
% They are detected in switch_detection.m. This is the module that determines
% what sort of indexing to use for each switch and then actually generates the
% code. The code here is quite similar to the code in switch_gen.m, which
% does the same thing for the LLDS back-end.
%
% The following describes the different forms of indexing that we could use.
% Note that currently most of these are not implemented!
% The ones that are not are marked NYI (for "not yet implemented").
%
% 1. For switches on atomic data types (int, char, enums), there are
% several possibilities.
% a) If all the alternative goals for a switch on an atomic data type
% contain only construction unifications of constants, then we
% should generate a dense lookup table (an array) for each output
% variable of the switch, rather than computed goto, so that
% executing the switch becomes a matter of doing an array index for
% each output variable. (NYI)
% b) If the cases are not sparse, and the target supports computed
% gotos, we should use a computed_goto, unless the target supports
% switch statements and the `--prefer-switch' option is set. (NYI)
% c) If the target supports switch statements,
% we generate an MLDS switch statement.
%
% 2. For switches on strings, there are several possibilities.
% a) If the target supports indirect gotos, we should look up the
% address to jump to in a hash table (e.g. using open addressing to
% resolve hash collisions), and then jump to it using an indirect
% goto, unless the target supports string switch statements and
% the `--prefer-switch' option is set. (NYI)
% c) If the target supports string switches,
% we generate an MLDS switch statement.
%
% 3. For switches on discriminated union types, we generate code that does
% indexing first on the primary tag, and then on the secondary tag (if
% the primary tag is shared between several function symbols). The
% indexing code for switches on both primary and secondary tags can be
% in the form of a try-me-else chain, a try chain, a dense jump table
% or a binary search. (NYI)
%
% For all other cases (or if the --smart-indexing option was
% disabled), we just generate a chain of if-then-elses.
%
% TODO:
% - implement the things marked NYI above
% - optimize switches so that the recursive case comes first
% (see switch_gen.m).
%
%-----------------------------------------------------------------------------%
:- module ml_backend__ml_switch_gen.
:- interface.
:- import_module parse_tree__prog_data.
:- import_module hlds__hlds_goal, hlds__hlds_data.
:- import_module backend_libs__code_model.
:- import_module ml_backend__mlds, ml_backend__ml_code_util.
:- import_module libs__globals.
:- import_module list.
% Generate MLDS code for a switch.
%
:- pred ml_gen_switch(prog_var, can_fail, list(case), code_model, prog_context,
mlds__defns, mlds__statements,
ml_gen_info, ml_gen_info).
:- mode ml_gen_switch(in, in, in, in, in, out, out, in, out) is det.
% The following types are exported to the modules that implement
% specialized kinds of switches.
% Generate an appropriate default for a switch.
%
:- pred ml_switch_generate_default(can_fail::in, code_model::in,
prog_context::in, switch_default::out,
ml_gen_info::in, ml_gen_info::out) is det.
% Succeed iff the target supports the specified construct.
:- pred target_supports_int_switch(globals::in) is semidet.
:- pred target_supports_string_switch(globals::in) is semidet.
:- pred target_supports_goto(globals::in) is semidet.
:- pred target_supports_computed_goto(globals::in) is semidet.
%-----------------------------------------------------------------------------%
:- implementation.
:- import_module ml_backend__ml_tag_switch, ml_backend__ml_string_switch.
:- import_module ml_backend__ml_code_gen, ml_backend__ml_unify_gen.
:- import_module ml_backend__ml_code_util, ml_backend__ml_simplify_switch.
:- import_module backend_libs__switch_util, check_hlds__type_util.
:- import_module backend_libs__foreign, libs__options.
:- import_module bool, int, string, map, libs__tree, std_util, require.
%-----------------------------------------------------------------------------%
% Choose which method to use to generate the switch.
% CanFail says whether the switch covers all cases.
ml_gen_switch(CaseVar, CanFail, Cases, CodeModel, Context,
MLDS_Decls, MLDS_Statements) -->
%
% Lookup the representation of the constructors for the tag tests
% and their corresponding priorities.
%
ml_switch_lookup_tags(Cases, CaseVar, TaggedCases0),
%
% Sort the cases according to the priority of their tag tests.
%
{ list__sort_and_remove_dups(TaggedCases0, TaggedCases) },
%
% Figure out what kind of switch this is.
%
ml_switch_gen__determine_category(CaseVar, SwitchCategory),
ml_gen_info_get_globals(Globals),
{ globals__lookup_bool_option(Globals, smart_indexing, Indexing) },
(
% Check for a switch on a type whose representation
% uses reserved addresses.
{ list__member(Case, TaggedCases) },
{ Case = case(_Priority, Tag, _ConsId, _Goal) },
{
Tag = reserved_address(_)
;
Tag = shared_with_reserved_addresses(_, _)
}
->
% XXX This may be inefficient in some cases.
ml_switch_generate_if_else_chain(TaggedCases, CaseVar,
CodeModel, CanFail, Context,
MLDS_Decls, MLDS_Statements)
;
/**************
XXX Lookup switches are NYI
When we do get around to implementing them,
they should probably be handled in ml_simplify_switch rather than here.
{ Indexing = yes },
{ SwitchCategory = atomic_switch },
% Note that if/when the MLDS back-end supports execution
% tracing, we would also need to check that tracing is not
% enabled.
{ list__length(TaggedCases, NumCases) },
{ globals__lookup_int_option(Globals, lookup_switch_size,
LookupSize) },
{ NumCases >= LookupSize },
{ globals__lookup_int_option(Globals, lookup_switch_req_density,
ReqDensity) },
lookup_switch__is_lookup_switch(CaseVar, TaggedCases, GoalInfo,
CanFail, ReqDensity,
CodeModel, FirstVal, LastVal, NeedRangeCheck,
NeedBitVecCheck, OutVars, CaseVals)
->
{ MaybeEnd = MaybeEndPrime },
ml_lookup_switch__generate(CaseVar, OutVars, CaseVals,
FirstVal, LastVal, NeedRangeCheck, NeedBitVecCheck,
MLDS_Decls, MLDS_Statements)
;
**************/
%
% Try using a string hash switch.
%
{ Indexing = yes },
{ SwitchCategory = string_switch },
{ list__length(TaggedCases, NumCases) },
{ globals__lookup_int_option(Globals, string_switch_size,
StringSize) },
{ NumCases >= StringSize },
% We can implement string hash switches using either
% computed gotos or int switches.
(
{ target_supports_computed_goto(Globals) }
;
{ target_supports_int_switch(Globals) }
),
% XXX Currently string hash switches always use gotos
% (to break out of the hash chain loop).
% We should change that, so that we can use string hash
% switches for the Java back-end too.
{ target_supports_goto(Globals) },
% OK, we could use a string hash switch. But should we?
% We may prefer to do a direct-mapped string switch.
\+ {
target_supports_string_switch(Globals),
globals__lookup_bool_option(Globals, prefer_switch, yes)
}
->
ml_string_switch__generate(TaggedCases, CaseVar, CodeModel,
CanFail, Context, MLDS_Decls, MLDS_Statements)
;
%
% Try using a tag switch.
%
{ Indexing = yes },
{ SwitchCategory = tag_switch },
{ list__length(TaggedCases, NumCases) },
{ globals__lookup_int_option(Globals, tag_switch_size,
TagSize) },
{ NumCases >= TagSize },
{ target_supports_int_switch(Globals) }
->
ml_tag_switch__generate(TaggedCases, CaseVar, CodeModel,
CanFail, Context, MLDS_Decls, MLDS_Statements)
;
%
% Try using a "direct-mapped" switch.
% This also handles dense (computed goto) switches --
% for those, we first generate a direct-mapped switch,
% and then convert it into a computed goto switch
% in ml_simplify_switch.
%
{ Indexing = yes },
(
{ target_supports_switch(SwitchCategory, Globals) }
;
{ SwitchCategory = atomic_switch },
{ target_supports_computed_goto(Globals) }
)
->
ml_switch_generate_mlds_switch(TaggedCases, CaseVar,
CodeModel, CanFail, Context,
MLDS_Decls, MLDS_Statements)
;
%
% The fallback method: if all else fails, generate an
% if-then-else chain which tests each of the cases in turn.
%
ml_switch_generate_if_else_chain(TaggedCases, CaseVar,
CodeModel, CanFail, Context,
MLDS_Decls, MLDS_Statements)
).
%-----------------------------------------------------------------------------%
:- pred target_supports_switch(switch_category::in, globals::in)
is semidet.
target_supports_switch(SwitchCategory, Globals) :-
(
SwitchCategory = atomic_switch,
target_supports_int_switch(Globals)
;
SwitchCategory = string_switch,
target_supports_string_switch(Globals)
).
target_supports_int_switch(Globals) :-
globals__get_target(Globals, Target),
target_supports_int_switch_2(Target) = yes.
target_supports_string_switch(Globals) :-
globals__get_target(Globals, Target),
target_supports_string_switch_2(Target) = yes.
target_supports_goto(Globals) :-
globals__get_target(Globals, Target),
target_supports_goto_2(Target) = yes.
target_supports_computed_goto(Globals) :-
globals__get_target(Globals, Target),
target_supports_computed_goto_2(Target) = yes.
:- func target_supports_int_switch_2(compilation_target) = bool.
:- func target_supports_string_switch_2(compilation_target) = bool.
:- func target_supports_goto_2(compilation_target) = bool.
:- func target_supports_computed_goto_2(compilation_target) = bool.
target_supports_int_switch_2(c) = yes.
target_supports_int_switch_2(asm) = yes. % asm means via gnu back-end
target_supports_int_switch_2(il) = no.
target_supports_int_switch_2(java) = yes.
% target_supports_int_switch_2(c_sharp) = yes.
target_supports_string_switch_2(c) = no.
target_supports_string_switch_2(asm) = no. % asm means via gnu back-end
target_supports_string_switch_2(il) = no.
target_supports_string_switch_2(java) = no.
% target_supports_string_switch_2(c_sharp) = yes.
target_supports_computed_goto_2(c) = yes.
target_supports_computed_goto_2(asm) = no. % asm means via gnu back-end
% XXX for asm, it should be `yes', but currently
% computed gotos are not yet implemented in gcc.m.
target_supports_computed_goto_2(il) = yes.
target_supports_computed_goto_2(java) = no.
% target_supports_computed_goto_2(c_sharp) = no.
target_supports_goto_2(c) = yes.
target_supports_goto_2(asm) = yes. % asm means via gnu back-end
target_supports_goto_2(il) = yes.
target_supports_goto_2(java) = no.
% target_supports_goto_2(c_sharp) = yes.
%-----------------------------------------------------------------------------%
% We categorize switches according to whether the value
% being switched on is an atomic type, a string, or
% something more complicated.
:- pred ml_switch_gen__determine_category(prog_var, switch_category,
ml_gen_info, ml_gen_info).
:- mode ml_switch_gen__determine_category(in, out, in, out) is det.
ml_switch_gen__determine_category(CaseVar, SwitchCategory) -->
ml_variable_type(CaseVar, Type),
=(MLGenInfo),
{ ml_gen_info_get_module_info(MLGenInfo, ModuleInfo) },
{ type_util__classify_type(Type, ModuleInfo, TypeCategory) },
{ switch_util__type_cat_to_switch_cat(TypeCategory, SwitchCategory) }.
%-----------------------------------------------------------------------------%
% Look up the representation (tag) for the cons_id in each case.
% Also look up the priority of each tag test.
%
:- pred ml_switch_lookup_tags(list(case), prog_var, cases_list,
ml_gen_info, ml_gen_info).
:- mode ml_switch_lookup_tags(in, in, out, in, out) is det.
ml_switch_lookup_tags([], _, []) --> [].
ml_switch_lookup_tags([Case | Cases], Var, [TaggedCase | TaggedCases]) -->
{ Case = case(ConsId, Goal) },
ml_variable_type(Var, Type),
ml_cons_id_to_tag(ConsId, Type, Tag),
{ switch_util__switch_priority(Tag, Priority) },
{ TaggedCase = case(Priority, Tag, ConsId, Goal) },
ml_switch_lookup_tags(Cases, Var, TaggedCases).
%-----------------------------------------------------------------------------%
% Generate a chain of if-then-elses to test each case in turn.
%
:- pred ml_switch_generate_if_else_chain(list(extended_case), prog_var,
code_model, can_fail, prog_context, mlds__defns, mlds__statements,
ml_gen_info, ml_gen_info).
:- mode ml_switch_generate_if_else_chain(in, in, in, in, in, out, out,
in, out) is det.
ml_switch_generate_if_else_chain([], _Var, CodeModel, CanFail, Context,
[], MLDS_Statements) -->
( { CanFail = can_fail } ->
ml_gen_failure(CodeModel, Context, MLDS_Statements)
;
{ error("switch failure") }
).
ml_switch_generate_if_else_chain([Case | Cases], Var, CodeModel, CanFail,
Context, MLDS_Decls, MLDS_Statements) -->
{ Case = case(_, _Tag, ConsId, Goal) },
(
{ Cases = [], CanFail = cannot_fail }
->
ml_gen_goal(CodeModel, Goal, MLDS_Decls, MLDS_Statements)
;
ml_gen_tag_test(Var, ConsId, TagTestDecls, TagTestStatements,
TagTestExpression),
ml_gen_goal(CodeModel, Goal, GoalStatement),
ml_switch_generate_if_else_chain(Cases, Var, CodeModel,
CanFail, Context, RestDecls, RestStatements),
{ Rest = ml_gen_block(RestDecls, RestStatements, Context) },
{ IfStmt = if_then_else(TagTestExpression,
GoalStatement, yes(Rest)) },
{ IfStatement = mlds__statement(IfStmt,
mlds__make_context(Context)) },
{ MLDS_Decls = TagTestDecls },
{ MLDS_Statements = list__append(TagTestStatements,
[IfStatement]) }
).
%-----------------------------------------------------------------------------%
% Generate an MLDS switch.
% This is used for "direct-mapped" switches, where we map a
% Mercury switch directly to a switch in the target language.
%
:- pred ml_switch_generate_mlds_switch(list(extended_case), prog_var,
code_model, can_fail, prog_context, mlds__defns, mlds__statements,
ml_gen_info, ml_gen_info).
:- mode ml_switch_generate_mlds_switch(in, in, in, in, in, out, out,
in, out) is det.
ml_switch_generate_mlds_switch(Cases, Var, CodeModel, CanFail,
Context, MLDS_Decls, MLDS_Statements) -->
ml_variable_type(Var, Type),
ml_gen_type(Type, MLDS_Type),
ml_gen_var(Var, Lval),
{ Rval = mlds__lval(Lval) },
ml_switch_gen_range(MLDS_Type, Range),
ml_switch_generate_mlds_cases(Cases, CodeModel, MLDS_Cases),
ml_switch_generate_default(CanFail, CodeModel, Context, Default),
{ SwitchStmt0 = switch(MLDS_Type, Rval, Range, MLDS_Cases, Default) },
{ MLDS_Context = mlds__make_context(Context) },
ml_simplify_switch(SwitchStmt0, MLDS_Context, SwitchStatement),
{ MLDS_Decls = [] },
{ MLDS_Statements = [SwitchStatement] }.
:- pred ml_switch_gen_range(mlds__type, mlds__switch_range,
ml_gen_info, ml_gen_info).
:- mode ml_switch_gen_range(in, out, in, out) is det.
ml_switch_gen_range(MLDS_Type, Range) -->
=(MLGenInfo),
{
ml_gen_info_get_module_info(MLGenInfo, ModuleInfo),
ExportedType = to_exported_type(ModuleInfo, Type),
MLDS_Type = mercury_type(Type, TypeCategory, ExportedType),
switch_util__type_range(TypeCategory, Type, ModuleInfo,
MinRange, MaxRange)
->
Range = range(MinRange, MaxRange)
;
Range = range_unknown
}.
:- pred ml_switch_generate_mlds_cases(list(extended_case),
code_model, list(mlds__switch_case), ml_gen_info, ml_gen_info).
:- mode ml_switch_generate_mlds_cases(in, in, out, in, out) is det.
ml_switch_generate_mlds_cases([], _, []) --> [].
ml_switch_generate_mlds_cases([Case | Cases], CodeModel,
[MLDS_Case | MLDS_Cases]) -->
ml_switch_generate_mlds_case(Case, CodeModel, MLDS_Case),
ml_switch_generate_mlds_cases(Cases, CodeModel, MLDS_Cases).
:- pred ml_switch_generate_mlds_case(extended_case, code_model,
mlds__switch_case, ml_gen_info, ml_gen_info).
:- mode ml_switch_generate_mlds_case(in, in, out, in, out) is det.
ml_switch_generate_mlds_case(Case, CodeModel, MLDS_Case) -->
{ Case = case(_Priority, Tag, _ConsId, Goal) },
( { Tag = int_constant(Int) } ->
{ Rval = const(int_const(Int)) }
; { Tag = string_constant(String) } ->
{ Rval = const(string_const(String)) }
;
{ error("ml_switch_gen.m: invalid tag type") }
),
ml_gen_goal(CodeModel, Goal, MLDS_Statement),
{ MLDS_Case = [match_value(Rval)] - MLDS_Statement }.
% Generate an appropriate default for a switch.
%
ml_switch_generate_default(CanFail, CodeModel, Context, Default) -->
(
{ CanFail = can_fail },
ml_gen_failure(CodeModel, Context, FailStatements),
( { FailStatements = [] } ->
{ Default = default_do_nothing }
;
{ Fail = ml_gen_block([], FailStatements, Context) },
{ Default = default_case(Fail) }
)
;
{ CanFail = cannot_fail },
{ Default = default_is_unreachable }
).
%-----------------------------------------------------------------------------%
%-----------------------------------------------------------------------------%