Files
mercury/compiler/ml_dense_switch.m
Fergus Henderson 4be69fa961 Eliminated a lot of the dependencies on the the `code_model' type,
Estimated hours taken: 6

Eliminated a lot of the dependencies on the the `code_model' type,
and move that type from llds.m into a new module `code_model'.
The aim of this change is to improve the modularity of the compiler by
reducing the number of places in the compiler front-end that depend
on back-end concepts and the number of places in the MLDS back-end
which depend on the LLDS.

compiler/code_model.m:
	New module.  Contains the code_model type and associated
	procedures.

compiler/llds.m:
	Move the code_model type into code_model.m.

compiler/hlds_goal.m:
	Move the goal_info_get_code_model procedure into code_model.m,
	to avoid having the HLDS modules import code_model.

compiler/hlds_out.m:
	Delete `hlds_out__write_code_model', since it wasn't being used.

compiler/hlds_pred.m:
	Move the proc_info_interface_code_model procedure into code_model.m,
	to avoid having the HLDS modules import code_model.

compiler/goal_path.m:
	When computing the `maybe_cut' field for `some' goals,
	compute it by comparing the determinism rather than by
	comparing the goal_infos.

compiler/unique_modes.m:
	Use determinism and test for soln_count = at_most_many
	rather than using code_model and testing for model_non.

compiler/inlining.m:
	Test for determinism nondet/multi rather than testing
	for code_model model_non.

compiler/hlds_pred.m:
compiler/det_report.m:
	Change valid_code_model_for_eval_method, which succeeded unless
	the eval_method was minimal_model and the code_model was model_det,
	to valid_determinism_for_eval_method, which succeeds unless the
	eval_method is minimal_model and the determinism cannot fail.
	As well as avoiding a dependency on code_model in the HLDS
	modules, this also fixes a bug where det_report could give
	misleading error messages, saying that `multi' was a valid
	determinism for `minimal_model' predicates, when in fact the
	compiler will always report a determinism error if you declare
	a `minimal_model' predicate with determinism `multi'.
	(Actually the code in which this bug occurs is in fact
	unreachable, but this is no doubt also a bug... I'll address
	that one in a separate change.)

compiler/lookup_switch.m:
	Simplify the code a bit by using globals__lookup_*_option
	rather than globals__get_option and then getopt__lookup_option.

compiler/*.m:
	Add `import_module' declarations for `code_model', and in some
	cases remove `import_module' declarations for `llds'.
2000-11-23 04:32:51 +00:00

284 lines
9.8 KiB
Mathematica

%-----------------------------------------------------------------------------%
% Copyright (C) 1994-2000 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_dense_switch.m
% author: fjh
% For switches on atomic types, generate code using a computed_goto
% dense jump table.
% The code here is quite similar to the code in dense_switch.m.
%-----------------------------------------------------------------------------%
:- module ml_dense_switch.
:- interface.
:- import_module prog_data.
:- import_module hlds_data, switch_util, type_util.
:- import_module code_model.
:- import_module mlds, ml_code_util.
% Should this switch be implemented as a dense jump table?
% If so, we return the starting and ending values for the table,
% and whether the switch is not covers all cases or not
% (we may convert locally semidet switches into locally det
% switches by adding extra cases whose body is just `fail').
:- pred ml_dense_switch__is_dense_switch(prog_var::in, cases_list::in,
can_fail::in, int::in, int::out, int::out, can_fail::out,
ml_gen_info::in, ml_gen_info::out) is semidet.
% Generate code for a switch using a dense jump table.
:- pred ml_dense_switch__generate(cases_list::in, int::in, int::in,
prog_var::in, code_model::in, can_fail::in,
prog_context::in, mlds__defns::out, mlds__statements::out,
ml_gen_info::in, ml_gen_info::out) is det.
% also used by ml_lookup_switch
:- pred ml_dense_switch__calc_density(int, int, int).
:- mode ml_dense_switch__calc_density(in, in, out) is det.
% also used by ml_lookup_switch
:- pred ml_dense_switch__type_range(builtin_type, prog_type, int,
ml_gen_info, ml_gen_info).
:- mode ml_dense_switch__type_range(in, in, out, in, out) is semidet.
%-----------------------------------------------------------------------------%
:- implementation.
:- import_module hlds_module, ml_code_gen, builtin_ops.
:- import_module char, map, int, std_util, require, list.
ml_dense_switch__is_dense_switch(CaseVar, TaggedCases, CanFail0, ReqDensity,
FirstVal, LastVal, CanFail) -->
{
list__length(TaggedCases, NumCases),
NumCases > 2,
TaggedCases = [FirstCase | _],
FirstCase = case(_, int_constant(FirstCaseVal), _, _),
list__index1_det(TaggedCases, NumCases, LastCase),
LastCase = case(_, int_constant(LastCaseVal), _, _),
Span is LastCaseVal - FirstCaseVal,
Range is Span + 1,
ml_dense_switch__calc_density(NumCases, Range, Density),
Density > ReqDensity
},
( { CanFail0 = 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 large enough to hold all
% of the values for the type.
ml_variable_type(CaseVar, Type),
=(MLGenInfo),
{ ml_gen_info_get_module_info(MLGenInfo, ModuleInfo) },
{ type_util__classify_type(Type, ModuleInfo, TypeCategory) },
(
ml_dense_switch__type_range(TypeCategory, Type,
TypeRange),
{ ml_dense_switch__calc_density(NumCases, TypeRange,
DetDensity) },
{ DetDensity > ReqDensity }
->
{ CanFail = cannot_fail },
{ FirstVal = 0 },
{ LastVal is TypeRange - 1 }
;
{ CanFail = CanFail0 },
{ FirstVal = FirstCaseVal },
{ LastVal = LastCaseVal }
)
;
{ CanFail = CanFail0 },
{ FirstVal = FirstCaseVal },
{ LastVal = LastCaseVal }
).
%-----------------------------------------------------------------------------%
% Calculate the percentage density given the range
% and the number of cases.
ml_dense_switch__calc_density(NumCases, Range, Density) :-
N1 is NumCases * 100,
Density is N1 // Range.
%-----------------------------------------------------------------------------%
% Determine the range of an atomic type.
% Fail if the type isn't the sort of type that has a range
% or if the type's range is to big to switch on (e.g. int).
ml_dense_switch__type_range(char_type, _, CharRange) -->
% XXX the following code uses the host's character size,
% not the target's, so it won't work if cross-compiling
% to a machine with a different character size.
% Note also that the code above in dense_switch.m and the code
% in lookup_switch.m assume that char__min_char_value is 0.
{ char__max_char_value(MaxChar) },
{ char__min_char_value(MinChar) },
{ CharRange is MaxChar - MinChar + 1 }.
ml_dense_switch__type_range(enum_type, Type, TypeRange) -->
{ type_to_type_id(Type, TypeId0, _) ->
TypeId = TypeId0
;
error("dense_switch__type_range: invalid enum type?")
},
=(MLGenInfo),
{ ml_gen_info_get_module_info(MLGenInfo, ModuleInfo) },
{ module_info_types(ModuleInfo, TypeTable) },
{ map__lookup(TypeTable, TypeId, TypeDefn) },
{ hlds_data__get_type_defn_body(TypeDefn, TypeBody) },
{ TypeBody = du_type(_, ConsTable, _, _) ->
map__count(ConsTable, TypeRange)
;
error("dense_switch__type_range: enum type is not d.u. type?")
}.
%-----------------------------------------------------------------------------%
ml_dense_switch__generate(Cases, StartVal, EndVal, Var, CodeModel, CanFail,
Context, MLDS_Decls, MLDS_Statements) -->
% Evaluate the variable which we are going to be switching on
ml_gen_var(Var, Lval),
{ Rval = lval(Lval) },
% If the case values start at some number other than 0,
% then subtract that number to give us a zero-based index
{ StartVal = 0 ->
Index = Rval
;
Index = binop(-, Rval, const(int_const(StartVal)))
},
% Now generate the jump table and the cases
ml_gen_new_label(EndLabel),
ml_dense_switch__generate_cases(Cases, StartVal, EndVal, CodeModel,
Context, EndLabel, CaseLabels, CasesDecls, CasesCode),
{ Comment = mlds__statement(
atomic(comment("switch (using dense jump table)")),
mlds__make_context(Context)) },
{ DoJump = mlds__statement(
computed_goto(Index, CaseLabels),
mlds__make_context(Context)) },
{ EndLabelStatement = mlds__statement(label(EndLabel),
mlds__make_context(Context)) },
% If the switch is not locally deterministic, we need to
% check that the value of the variable lies within the
% appropriate range
(
{ CanFail = can_fail },
{ Difference is EndVal - StartVal },
{ InRange = binop(<=, unop(std_unop(cast_to_unsigned), Index),
const(int_const(Difference))) },
ml_gen_failure(CodeModel, Context, DoFailStatements),
{ DoFailStatements = [] ->
Else = no
;
Else = yes(ml_gen_block([], DoFailStatements, Context))
},
{ SwitchBody = ml_gen_block([], [DoJump | CasesCode],
Context) },
{ DoSwitch = mlds__statement(
if_then_else(InRange, SwitchBody, Else),
mlds__make_context(Context)) },
{ MLDS_Statements = [Comment, DoSwitch] ++
[EndLabelStatement] }
;
{ CanFail = cannot_fail },
{ MLDS_Statements = [Comment, DoJump | CasesCode] ++
[EndLabelStatement] }
),
{ MLDS_Decls = CasesDecls }.
:- pred ml_dense_switch__generate_cases(cases_list::in, int::in, int::in,
code_model::in, prog_context::in,
mlds__label::in, list(mlds__label)::out,
mlds__defns::out, mlds__statements::out,
ml_gen_info::in, ml_gen_info::out) is det.
ml_dense_switch__generate_cases(Cases0, NextVal, EndVal, CodeModel, Context,
EndLabel, CaseLabels, MLDS_Decls, MLDS_Statements) -->
(
{ NextVal > EndVal }
->
{ EndComment = mlds__statement(
atomic(comment("End of dense switch")),
mlds__make_context(Context)) },
{ CaseLabels = [] },
{ MLDS_Decls = [] },
{ MLDS_Statements = [EndComment] }
;
% generate the next case
ml_dense_switch__generate_case(Cases0, NextVal,
CodeModel, Context, EndLabel,
Cases1, ThisLabel, CaseStatements),
% generate the rest of the cases.
ml_dense_switch__generate_cases(Cases1, NextVal + 1, EndVal,
CodeModel, Context, EndLabel,
CaseLabels1, MLDS_Decls, MLDS_Statements1),
% package it all up together
{ CaseLabels = [ThisLabel | CaseLabels1] },
{ MLDS_Statements = list__append(CaseStatements,
MLDS_Statements1) }
).
%-----------------------------------------------------------------------------%
:- pred ml_dense_switch__generate_case(cases_list::in, int::in,
code_model::in, prog_context::in, mlds__label::in,
cases_list::out, mlds__label::out, mlds__statements::out,
ml_gen_info::in, ml_gen_info::out) is det.
ml_dense_switch__generate_case(Cases0, NextVal, CodeModel, Context,
EndLabel, Cases1, ThisLabel, MLDS_Statements) -->
ml_gen_new_label(ThisLabel),
{ LabelComment = mlds__statement(
atomic(comment(Comment)),
mlds__make_context(Context)) },
{ LabelCode = mlds__statement(
label(ThisLabel),
mlds__make_context(Context)) },
ml_dense_switch__generate_case_body(Cases0, NextVal, CodeModel,
Context, Cases1, Comment, CaseStatement),
{ JumpComment = mlds__statement(
atomic(comment("branch to end of dense switch")),
mlds__make_context(Context)) },
{ JumpCode = mlds__statement(
goto(EndLabel),
mlds__make_context(Context)) },
{ MLDS_Statements = [LabelComment, LabelCode, CaseStatement,
JumpComment, JumpCode] }.
:- pred ml_dense_switch__generate_case_body(cases_list::in, int::in,
code_model::in, prog_context::in, cases_list::out,
string::out, mlds__statement::out,
ml_gen_info::in, ml_gen_info::out) is det.
ml_dense_switch__generate_case_body(Cases0, NextVal, CodeModel, Context,
Cases, Comment, MLDS_Statement) -->
(
{ Cases0 = [Case | Cases1] },
{ Case = case(_, int_constant(NextVal), _, Goal) }
->
{ Comment = "case of dense switch" },
ml_gen_goal(CodeModel, Goal, MLDS_Statement),
{ Cases = Cases1 }
;
% This case didn't occur in the original case list
% - just generate a `fail' for it.
{ Comment = "compiler-introduced `fail' case of dense switch" },
ml_gen_failure(CodeModel, Context, FailStatements),
{ MLDS_Statement = ml_gen_block([], FailStatements, Context) },
{ Cases = Cases0 }
).
%-----------------------------------------------------------------------------%