Files
mercury/compiler/string_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

247 lines
7.9 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.
%-----------------------------------------------------------------------------%
% string_switch.m
% For switches on strings, we generate a hash table using open addressing
% to resolve hash conflicts.
% Author: fjh.
%-----------------------------------------------------------------------------%
:- module string_switch.
:- interface.
:- import_module prog_data, hlds_data, hlds_goal.
:- import_module switch_util, code_model.
:- import_module llds, code_info.
:- pred string_switch__generate(cases_list, prog_var, code_model,
can_fail, store_map, label, branch_end, branch_end, code_tree,
code_info, code_info).
:- mode string_switch__generate(in, in, in, in, in, in, in, out, out, in, out)
is det.
%-----------------------------------------------------------------------------%
:- implementation.
:- import_module builtin_ops, code_gen, trace, tree.
:- import_module bool, int, string, list, map, std_util, assoc_list, require.
string_switch__generate(Cases, Var, CodeModel, _CanFail, StoreMap,
EndLabel, MaybeEnd0, MaybeEnd, Code) -->
code_info__produce_variable(Var, VarCode, VarRval),
code_info__acquire_reg(r, SlotReg),
code_info__acquire_reg(r, StringReg),
code_info__get_next_label(LoopLabel),
code_info__get_next_label(FailLabel),
code_info__get_next_label(JumpLabel),
code_info__get_next_cell_number(NextSlotsTableNo),
code_info__get_next_cell_number(StringTableNo),
{
% Determine how big to make the hash table.
% Currently we round the number of cases up to the nearest power
% of two, and then double it. This should hopefully ensure that
% we don't get too many hash collisions.
%
list__length(Cases, NumCases),
int__log2(NumCases, LogNumCases),
int__pow(2, LogNumCases, RoundedNumCases),
TableSize is 2 * RoundedNumCases,
HashMask is TableSize - 1,
% Compute the hash table
%
switch_util__string_hash_cases(Cases, HashMask, HashValsMap),
map__to_assoc_list(HashValsMap, HashValsList),
switch_util__calc_hash_slots(HashValsList, HashValsMap,
HashSlotsMap)
},
% Note that it is safe to release the registers now,
% even though we haven't yet generated all the code
% which uses them, because that code will be executed
% before the code for the cases (which might reuse those
% registers), and because that code is generated manually
% (below) so we don't need the reg info to be valid when
% we generate it.
code_info__release_reg(SlotReg),
code_info__release_reg(StringReg),
% Generate the code for when the hash lookup fails.
% This must be done before gen_hash_slots, since
% we want to use the exprn_info corresponding to
% the start of the switch, not to the end of the last case.
code_info__generate_failure(FailCode),
% Generate the code etc. for the hash table
%
string_switch__gen_hash_slots(0, TableSize, HashSlotsMap, CodeModel,
StoreMap, FailLabel, EndLabel, MaybeEnd0, MaybeEnd,
Strings, Labels, NextSlots, SlotsCode),
% Generate code which does the hash table lookup
{
Reuse = no,
NextSlotsTable = create(0, NextSlots, uniform(no),
must_be_static, NextSlotsTableNo,
"string_switch_next_slots_table", Reuse),
StringTable = create(0, Strings, uniform(no),
must_be_static, StringTableNo,
"string_switch_string_table", Reuse),
HashLookupCode = node([
comment("hashed string switch") -
"",
assign(SlotReg, binop(&, unop(hash_string, VarRval),
const(int_const(HashMask)))) -
"compute the hash value of the input string",
label(LoopLabel) -
"begin hash chain loop",
assign(StringReg, binop(array_index, StringTable,
lval(SlotReg))) -
"lookup the string for this hash slot",
if_val(binop(and, lval(StringReg),
binop(str_eq, lval(StringReg), VarRval)),
label(JumpLabel)) -
"did we find a match?",
assign(SlotReg, binop(array_index, NextSlotsTable,
lval(SlotReg))) -
"not yet, so get next slot in hash chain",
if_val(binop(>=, lval(SlotReg), const(int_const(0))),
label(LoopLabel)) -
"keep searching until we reach the end of the chain",
label(FailLabel) -
"no match, so fail"
])
},
{
JumpCode = node([
label(JumpLabel) -
"we found a match",
computed_goto(lval(SlotReg), Labels) -
"jump to the corresponding code"
])
},
% Collect all the generated code fragments together
{ Code =
tree(VarCode,
tree(HashLookupCode,
tree(FailCode,
tree(JumpCode,
SlotsCode))))
}.
:- pred string_switch__gen_hash_slots(int, int, map(int, hash_slot),
code_model, store_map, label, label, branch_end, branch_end,
list(maybe(rval)), list(label), list(maybe(rval)), code_tree,
code_info, code_info).
:- mode string_switch__gen_hash_slots(in, in, in, in, in, in, in,
in, out, out, out, out, out, in, out) is det.
string_switch__gen_hash_slots(Slot, TableSize, HashSlotMap, CodeModel,
StoreMap, FailLabel, EndLabel, MaybeEnd0, MaybeEnd,
Strings, Labels, NextSlots, Code) -->
( { Slot = TableSize } ->
{
MaybeEnd = MaybeEnd0,
Strings = [],
Labels = [],
NextSlots = [],
Code = node([
label(EndLabel) - "end of hashed string switch"
])
}
;
string_switch__gen_hash_slot(Slot, TableSize, HashSlotMap,
CodeModel, StoreMap, FailLabel, EndLabel,
MaybeEnd0, MaybeEnd1,
String, Label, NextSlot, SlotCode),
{ Slot1 is Slot + 1 },
{
Strings = [String | Strings0],
Labels = [Label | Labels0],
NextSlots = [NextSlot | NextSlots0],
Code = tree(SlotCode, Code0)
},
string_switch__gen_hash_slots(Slot1, TableSize, HashSlotMap,
CodeModel, StoreMap, FailLabel, EndLabel,
MaybeEnd1, MaybeEnd,
Strings0, Labels0, NextSlots0, Code0)
).
:- pred string_switch__gen_hash_slot(int, int, map(int, hash_slot),
code_model, store_map, label, label, branch_end, branch_end,
maybe(rval), label, maybe(rval), code_tree,
code_info, code_info).
:- mode string_switch__gen_hash_slot(in, in, in, in, in, in, in,
in, out, out, out, out, out, in, out) is det.
string_switch__gen_hash_slot(Slot, TblSize, HashSlotMap, CodeModel, StoreMap,
FailLabel, EndLabel, MaybeEnd0, MaybeEnd,
yes(StringRval), Label, yes(NextSlotRval), Code) -->
(
{ map__search(HashSlotMap, Slot, hash_slot(Case, Next)) }
->
{ NextSlotRval = const(int_const(Next)) },
{ Case = case(_, ConsTag, _, Goal) },
{ ConsTag = string_constant(String0) ->
String = String0
;
error("string_switch__gen_hash_slots: string expected")
},
{ StringRval = const(string_const(String)) },
code_info__get_next_label(Label),
{ string__append_list(["case """, String, """"], Comment) },
{ LabelCode = node([
label(Label) - Comment
]) },
code_info__remember_position(BranchStart),
trace__maybe_generate_internal_event_code(Goal, TraceCode),
code_gen__generate_goal(CodeModel, Goal, GoalCode),
code_info__generate_branch_end(StoreMap, MaybeEnd0, MaybeEnd,
SaveCode),
(
{ string_switch__this_is_last_case(Slot, TblSize,
HashSlotMap) }
->
[]
;
code_info__reset_to_position(BranchStart)
),
{ FinishCode = node([
goto(label(EndLabel)) - "jump to end of switch"
]) },
{ Code =
tree(LabelCode,
tree(TraceCode,
tree(GoalCode,
tree(SaveCode,
FinishCode))))
}
;
{ MaybeEnd = MaybeEnd0 },
{ StringRval = const(int_const(0)) },
{ Label = FailLabel },
{ NextSlotRval = const(int_const(-2)) },
{ Code = empty }
).
:- pred string_switch__this_is_last_case(int, int, map(int, hash_slot)).
:- mode string_switch__this_is_last_case(in, in, in) is semidet.
string_switch__this_is_last_case(Slot, TableSize, Table) :-
Slot1 is Slot + 1,
( Slot1 >= TableSize ->
true
;
\+ map__contains(Table, Slot1),
string_switch__this_is_last_case(Slot1, TableSize, Table)
).
%-----------------------------------------------------------------------------%