mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-11 03:45:33 +00:00
Estimated hours taken: 4 Branches: main This diff makes code_info.m and many callers of its predicates easier to read and to maintain, but contains no changes in algorithms whatsoever. compiler/code_info.m: Bring this module into line with our current coding standards. Use predmode declarations, functions, and state variable syntax when appropriate. Reorder arguments of predicates where necessary for the use of state variable syntax, and where this improves readability. Where a predicate returned its input code_info unchanged, purely to allow the convenient use of DCG notation in the caller, delete the unnecessary output argument. This should make the caller somewhat more efficient, since it can avoid updating the stack slot holding the current code_info. Replace old-style lambdas with new-style lambdas or with partially applied named procedures. compiler/*.m: Conform to the changes in code_info.m. This mostly means using the new argument orders of predicates exported by hlds_pred.m, and using state variable notation.
242 lines
8.0 KiB
Mathematica
242 lines
8.0 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 1994-2003 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.
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
% dense_switch.m
|
|
|
|
% For switches on atomic types, generate code using a dense jump table.
|
|
|
|
% Author: fjh.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- module ll_backend__dense_switch.
|
|
|
|
:- interface.
|
|
|
|
:- import_module backend_libs__code_model.
|
|
:- import_module backend_libs__switch_util.
|
|
:- import_module check_hlds__type_util.
|
|
:- import_module hlds__hlds_data.
|
|
:- import_module hlds__hlds_goal.
|
|
:- import_module ll_backend__code_info.
|
|
:- import_module ll_backend__llds.
|
|
:- import_module parse_tree__prog_data.
|
|
|
|
% 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 dense_switch__is_dense_switch(code_info::in, prog_var::in,
|
|
cases_list::in, can_fail::in, int::in, int::out, int::out,
|
|
can_fail::out) is semidet.
|
|
|
|
% Generate code for a switch using a dense jump table.
|
|
|
|
:- pred dense_switch__generate(cases_list::in, int::in, int::in, prog_var::in,
|
|
code_model::in, can_fail::in, hlds_goal_info::in, label::in,
|
|
branch_end::in, branch_end::out, code_tree::out,
|
|
code_info::in, code_info::out) is det.
|
|
|
|
% also used by lookup_switch
|
|
:- pred dense_switch__calc_density(int::in, int::in, int::out) is det.
|
|
|
|
% also used by lookup_switch
|
|
:- pred dense_switch__type_range(code_info::in, type_category::in, (type)::in,
|
|
int::out) is semidet.
|
|
|
|
%-----------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module backend_libs__builtin_ops.
|
|
:- import_module hlds__hlds_goal.
|
|
:- import_module hlds__hlds_llds.
|
|
:- import_module hlds__hlds_module.
|
|
:- import_module ll_backend__code_gen.
|
|
:- import_module ll_backend__trace.
|
|
|
|
:- import_module char, map, libs__tree, int, std_util, require, list.
|
|
|
|
dense_switch__is_dense_switch(CI, 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 = LastCaseVal - FirstCaseVal,
|
|
Range = Span + 1,
|
|
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.
|
|
Type = code_info__variable_type(CI, CaseVar),
|
|
code_info__get_module_info(CI, ModuleInfo),
|
|
classify_type(ModuleInfo, Type) = TypeCategory,
|
|
(
|
|
dense_switch__type_range(CI, TypeCategory, Type,
|
|
TypeRange),
|
|
dense_switch__calc_density(NumCases, TypeRange,
|
|
DetDensity),
|
|
DetDensity > ReqDensity
|
|
->
|
|
CanFail = cannot_fail,
|
|
FirstVal = 0,
|
|
LastVal = 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.
|
|
|
|
dense_switch__calc_density(NumCases, Range, Density) :-
|
|
N1 = NumCases * 100,
|
|
Density = 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).
|
|
|
|
dense_switch__type_range(CI, TypeCategory, Type, Range) :-
|
|
code_info__get_module_info(CI, ModuleInfo),
|
|
switch_util__type_range(TypeCategory, Type, ModuleInfo, Min, Max),
|
|
Range = Max - Min + 1.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
dense_switch__generate(Cases, StartVal, EndVal, Var, CodeModel, CanFail,
|
|
SwitchGoalInfo, EndLabel, MaybeEnd0, MaybeEnd, Code, !CI) :-
|
|
% Evaluate the variable which we are going to be switching on
|
|
code_info__produce_variable(Var, VarCode, Rval, !CI),
|
|
% 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)))
|
|
),
|
|
% 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 = EndVal - StartVal,
|
|
code_info__fail_if_rval_is_false(
|
|
binop(unsigned_le, Index,
|
|
const(int_const(Difference))), RangeCheck, !CI)
|
|
;
|
|
CanFail = cannot_fail,
|
|
RangeCheck = empty
|
|
),
|
|
% Now generate the jump table and the cases
|
|
dense_switch__generate_cases(Cases, StartVal, EndVal, CodeModel,
|
|
SwitchGoalInfo, EndLabel, MaybeEnd0, MaybeEnd, Labels,
|
|
CasesCode, !CI),
|
|
|
|
% XXX
|
|
% We keep track of the code_info at the end of one of
|
|
% the non-fail cases. We have to do this because
|
|
% generating a `fail' slot last would yield the
|
|
% wrong liveness and would not unset the failure cont
|
|
% for a nondet switch.
|
|
DoJump = node([
|
|
computed_goto(Index, Labels)
|
|
- "switch (using dense jump table)"
|
|
]),
|
|
% Assemble the code together
|
|
Code = tree(VarCode, tree(RangeCheck, tree(DoJump, CasesCode))).
|
|
|
|
:- pred dense_switch__generate_cases(cases_list::in, int::in, int::in,
|
|
code_model::in, hlds_goal_info::in, label::in, branch_end::in,
|
|
branch_end::out, list(label)::out, code_tree::out,
|
|
code_info::in, code_info::out) is det.
|
|
|
|
dense_switch__generate_cases(Cases0, NextVal, EndVal, CodeModel,
|
|
SwitchGoalInfo, EndLabel, !MaybeEnd, Labels, Code, !CI) :-
|
|
( NextVal > EndVal ->
|
|
Labels = [],
|
|
Code = node([
|
|
label(EndLabel)
|
|
- "End of dense switch"
|
|
])
|
|
;
|
|
code_info__get_next_label(ThisLabel, !CI),
|
|
dense_switch__generate_case(Cases0, Cases1, NextVal, CodeModel,
|
|
SwitchGoalInfo, !MaybeEnd, ThisCode, Comment, !CI),
|
|
LabelCode = node([
|
|
label(ThisLabel)
|
|
- Comment
|
|
]),
|
|
JumpCode = node([
|
|
goto(label(EndLabel))
|
|
- "branch to end of dense switch"
|
|
]),
|
|
% generate the rest of the cases.
|
|
NextVal1 = NextVal + 1,
|
|
dense_switch__generate_cases(Cases1, NextVal1, EndVal,
|
|
CodeModel, SwitchGoalInfo, EndLabel,
|
|
!MaybeEnd, Labels1, OtherCasesCode, !CI),
|
|
Labels = [ThisLabel | Labels1],
|
|
Code =
|
|
tree(LabelCode,
|
|
tree(ThisCode,
|
|
tree(JumpCode,
|
|
OtherCasesCode)))
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred dense_switch__generate_case(cases_list::in, cases_list::out, int::in,
|
|
code_model::in, hlds_goal_info::in, branch_end::in, branch_end::out,
|
|
code_tree::out, string::out, code_info::in, code_info::out) is det.
|
|
|
|
dense_switch__generate_case(!Cases, NextVal, CodeModel, SwitchGoalInfo,
|
|
!MaybeEnd, Code, Comment, !CI) :-
|
|
(
|
|
!.Cases = [Case | !:Cases],
|
|
Case = case(_, int_constant(NextVal), _, Goal)
|
|
->
|
|
Comment = "case of dense switch",
|
|
% We need to save the expression cache, etc.,
|
|
% and restore them when we've finished.
|
|
code_info__remember_position(!.CI, BranchStart),
|
|
trace__maybe_generate_internal_event_code(Goal, SwitchGoalInfo,
|
|
TraceCode, !CI),
|
|
code_gen__generate_goal(CodeModel, Goal, GoalCode, !CI),
|
|
goal_info_get_store_map(SwitchGoalInfo, StoreMap),
|
|
code_info__generate_branch_end(StoreMap, !MaybeEnd, SaveCode,
|
|
!CI),
|
|
Code =
|
|
tree(TraceCode,
|
|
tree(GoalCode,
|
|
SaveCode)),
|
|
code_info__reset_to_position(BranchStart, !CI)
|
|
;
|
|
% This case didn't occur in the original case list
|
|
% - just generate a `fail' for it.
|
|
Comment = "compiler-introduced `fail' case of dense switch",
|
|
code_info__generate_failure(Code, !CI)
|
|
).
|