mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-10 11:23:15 +00:00
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'.
284 lines
9.8 KiB
Mathematica
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 }
|
|
).
|
|
|
|
%-----------------------------------------------------------------------------%
|