mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-18 15:26:31 +00:00
Estimated hours taken: 16 Allow the compiler to handle create rvals whose arguments have a size which is different from the size of a word. Use this capability to reduce the size of RTTI information, in two ways. The first way is by rearranging the way in which we represent information about the live values at a label. Instead of an array with an entry for each live value, the entry being a pair of Words containing a shape representation and a location description respectively, use an array of shape representations (still Words), followed by an array of 32-bit ints (which may be smaller than Word) describing locations whose descriptions don't fit into 8 bits, followed by an array of 8-bit ints describing locations whose descriptions do fit into 8 bits. The second way is by reducing the sizes of some fields in the C structs used for RTTI. Several of these had to be bigger than necessary in the past because their fields were represented by the args of a create rval. On cyclone, this reduces the size of the object file for queens.m by 2.8%. IMPORTANT Until this change is reflected in the installed compiler, you will not be able to use any modules compiled with debugging in your workspaces if the workspace has been updated to include this change. This is because the RTTI data structures generated by the old installed compiler will not be compatible with the new structure definitions. The workaround is simple: if your workspace contains modules compiled with debugging, don't do a cvs update until this change has been installed. configure.in: Check whether <stdint.h> is present. If not, autoconfigure types that are at least 16 and 32 bits in size. runtime/mercury_conf.h.in: Mention the macros used by the configure script, MR_INT_LEAST32_TYPE and MR_INT_LEAST16_TYPE. runtime/mercury_conf_param.h: Document the macros used by the configure script, MR_INT_LEAST32_TYPE and MR_INT_LEAST16_TYPE. runtime/mercury_types.h: If <stdint.h> is available, get the basic integer types (intptr_t, int_least8_t, etc) from there. Otherwise, get them from the autoconfigure script. Define types such as Word in terms of these (eventually) standard types. runtime/mercury_stack_layout.h: Add macros for manipulating short location descriptions, update the types and macros for manipulating long location descriptions. Modify the way the variable count is represented (since it now must count locations with long and short descriptions separately), and move it to the structure containing the arrays it describes. Reduce the size of the some fields in structs. This required some reordering of fields to avoid the insertion of padding by the compiler, and changes to the definitions of some types (e.g. MR_determinism). runtime/mercury_layout_util.[ch]: runtime/mercury_stack_trace.c: runtime/mercury_accurate_gc.c: trace/mercury_trace.c: trace/mercury_trace_external.c: trace/mercury_trace_internal.c: Update the code to conform to the changes to stack_layout.h. compiler/llds.m: Modify the create rval in two ways. First, add an extra argument to represent the types of the arguments, which used to always be implicit always a word in size, but may now be explicit and possibly smaller (e.g. uint_least8). Second, since the code generator would do the wrong thing with creates with smaller than wordsize arguments, replace the old must-be-unique vs may-be-nonunique bool with a three-valued marker, must_be_dynamic vs must_be_static vs can_be_either. Add uint_least8, uint_least16, uint_least32 (and their signed variants) and string as llds_types. Add a couple of utility predicates for checking whether an llds_type denotes a type whose size is the same as word. compiler/llds_out.m: Use explicitly given argument types when declaring and initializing the arguments of a cell, if they are given. compiler/llds_common.m: Don't conflate creates with identical argument values but different C-level argument types. The probability of a match is minuscule anyway. compiler/stack_layout.m: Use the new representation of creates to generate the new versions of RTTI data structures. compiler/code_exprn.m: If a create is marked must_be_static, don't inspect the arguments to decide whether it can be static or not. If it can't, we'll get an abort later on in llds_out or during C compilation anyway. compiler/base_type_layout.m: When creating pseudo typeinfos, return the llds_type of the resulting rval. Minor changes required by the change in create. compiler/base_type_info.m: compiler/base_typeclass_info.m.m: compiler/code_util.m: compiler/dupelim.m: compiler/exprn_aux.m: compiler/jumpopt.m: compiler/livemap.m: compiler/lookup_switch.m: compiler/middle_rec.m: compiler/opt_debug.m: compiler/opt_util.m: compiler/string_switch.m: compiler/unify_gen.m: compiler/vn_cost.m: compiler/vn_filter.m: compiler/vn_flush.m: compiler/vn_order.m: compiler/vn_type.m: compiler/vn_util.m: compiler/vn_verify.m: Minor changes required by the change in create. library/benchmarking.m: library/std_util.m: Use the new macros in hand-constructing proc layout structures. library/Mmakefile: Add explicit dependencies for benchmarking.o and std_util.o on ../runtime/mercury_stack_layout.h. Although this is only a subset of the truth (in reality, all library objects depend on most of the runtime headers), it is a good tradeoff between safety and efficiency. The other runtime header files tend not to change in incompatible ways. trace/Mmakefile: Add explicit dependencies for all the object files on ../runtime/mercury_stack_layout.h, for similar reasons.
356 lines
12 KiB
Mathematica
356 lines
12 KiB
Mathematica
%-----------------------------------------------------------------------------%
|
|
% Copyright (C) 1994-1999 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 hlds_data, hlds_goal, llds, switch_gen, code_info, prog_data.
|
|
|
|
:- 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 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
|
|
%
|
|
string_switch__hash_cases(Cases, HashMask, HashValsMap),
|
|
map__to_assoc_list(HashValsMap, HashValsList),
|
|
string_switch__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
|
|
{
|
|
NextSlotsTable = create(0, NextSlots, uniform(no),
|
|
must_be_static, NextSlotsTableNo,
|
|
"string_switch_next_slots_table"),
|
|
StringTable = create(0, Strings, uniform(no),
|
|
must_be_static, StringTableNo,
|
|
"string_switch_string_table"),
|
|
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__hash_cases(cases_list, int, map(int, cases_list)).
|
|
:- mode string_switch__hash_cases(in, in, out) is det.
|
|
|
|
string_switch__hash_cases([], _, Map) :-
|
|
map__init(Map).
|
|
string_switch__hash_cases([Case | Cases], HashMask, Map) :-
|
|
string_switch__hash_cases(Cases, HashMask, Map0),
|
|
( Case = case(_, string_constant(String0), _, _) ->
|
|
String = String0
|
|
;
|
|
error("string_switch__hash_cases: non-string case?")
|
|
),
|
|
string__hash(String, HashVal0),
|
|
HashVal is HashVal0 /\ HashMask,
|
|
( map__search(Map0, HashVal, CaseList0) ->
|
|
map__det_update(Map0, HashVal, [Case | CaseList0], Map)
|
|
;
|
|
map__det_insert(Map0, HashVal, [Case], Map)
|
|
).
|
|
|
|
:- type hash_slot ---> hash_slot(extended_case, int).
|
|
|
|
:- pred string_switch__calc_hash_slots(assoc_list(int, cases_list),
|
|
map(int, cases_list), map(int, hash_slot)).
|
|
:- mode string_switch__calc_hash_slots(in, in, out) is det.
|
|
|
|
% string_switch__calc_hash_slots(AssocList, HashMap, Map) :-
|
|
% For each (HashVal - Case) pair in AssocList,
|
|
% allocate a hash slot in Map for the case, as follows.
|
|
% If the hash slot corresponding to HashVal is not
|
|
% already used, then use that one. Otherwise, find
|
|
% the next spare slot (making sure that we don't
|
|
% use slots which can be used for a direct match with
|
|
% the hash value for one of the other cases), and
|
|
% use it instead. Keep track of the hash chains
|
|
% as we do this.
|
|
|
|
string_switch__calc_hash_slots(HashValList, HashMap, Map) :-
|
|
map__init(Map0),
|
|
string_switch__calc_hash_slots_1(HashValList, HashMap, Map0, 0, Map, _).
|
|
|
|
:- pred string_switch__calc_hash_slots_1(assoc_list(int, cases_list),
|
|
map(int, cases_list), map(int, hash_slot), int,
|
|
map(int, hash_slot), int).
|
|
:- mode string_switch__calc_hash_slots_1(in, in, in, in, out, out) is det.
|
|
|
|
string_switch__calc_hash_slots_1([], _, Map, LastUsed, Map, LastUsed).
|
|
string_switch__calc_hash_slots_1([HashVal-Cases | Rest], HashMap, Map0,
|
|
LastUsed0, Map, LastUsed) :-
|
|
string_switch__calc_hash_slots_2(Cases, HashVal, HashMap, Map0,
|
|
LastUsed0, Map1, LastUsed1),
|
|
string_switch__calc_hash_slots_1(Rest, HashMap, Map1,
|
|
LastUsed1, Map, LastUsed).
|
|
|
|
:- pred string_switch__calc_hash_slots_2(cases_list, int, map(int, cases_list),
|
|
map(int, hash_slot), int, map(int, hash_slot), int).
|
|
:- mode string_switch__calc_hash_slots_2(in, in, in, in, in, out, out) is det.
|
|
|
|
string_switch__calc_hash_slots_2([], _HashVal, _HashMap, Map, LastUsed,
|
|
Map, LastUsed).
|
|
string_switch__calc_hash_slots_2([Case | Cases], HashVal, HashMap, Map0,
|
|
LastUsed0, Map, LastUsed) :-
|
|
string_switch__calc_hash_slots_2(Cases, HashVal, HashMap, Map0,
|
|
LastUsed0, Map1, LastUsed1),
|
|
( map__contains(Map1, HashVal) ->
|
|
string_switch__follow_hash_chain(Map1, HashVal, ChainEnd),
|
|
string_switch__next_free_hash_slot(Map1, HashMap, LastUsed1,
|
|
Next),
|
|
map__lookup(Map1, ChainEnd, hash_slot(PrevCase, _)),
|
|
map__det_update(Map1, ChainEnd, hash_slot(PrevCase, Next),
|
|
Map2),
|
|
map__det_insert(Map2, Next, hash_slot(Case, -1), Map),
|
|
LastUsed = Next
|
|
;
|
|
map__det_insert(Map1, HashVal, hash_slot(Case, -1), Map),
|
|
LastUsed = LastUsed1
|
|
).
|
|
|
|
:- pred string_switch__follow_hash_chain(map(int, hash_slot), int, int).
|
|
:- mode string_switch__follow_hash_chain(in, in, out) is det.
|
|
|
|
string_switch__follow_hash_chain(Map, Slot, LastSlot) :-
|
|
map__lookup(Map, Slot, hash_slot(_, NextSlot)),
|
|
(
|
|
NextSlot >= 0,
|
|
map__contains(Map, NextSlot)
|
|
->
|
|
string_switch__follow_hash_chain(Map, NextSlot, LastSlot)
|
|
;
|
|
LastSlot = Slot
|
|
).
|
|
|
|
% next_free_hash_slot(M, H_M, LastUsed, FreeSlot) :-
|
|
% Find the next available slot FreeSlot in the hash table
|
|
% which is not already used (contained in M) and which is not
|
|
% going to be used a primary slot (contained in H_M),
|
|
% starting at the slot after LastUsed.
|
|
|
|
:- pred string_switch__next_free_hash_slot(map(int, hash_slot),
|
|
map(int, cases_list), int, int).
|
|
:- mode string_switch__next_free_hash_slot(in, in, in, out) is det.
|
|
|
|
string_switch__next_free_hash_slot(Map, H_Map, LastUsed, FreeSlot) :-
|
|
NextSlot is LastUsed + 1,
|
|
(
|
|
\+ map__contains(Map, NextSlot),
|
|
\+ map__contains(H_Map, NextSlot)
|
|
->
|
|
FreeSlot = NextSlot
|
|
;
|
|
string_switch__next_free_hash_slot(Map, H_Map, NextSlot, FreeSlot)
|
|
).
|
|
|
|
:- 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)
|
|
).
|
|
|