%---------------------------------------------------------------------------% % Copyright (C) 1997-2000 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. %---------------------------------------------------------------------------% % % This module generates the LLDS code that defines global constants to % hold the `stack_layout' structures of the stack frames defined by the % current module. % % The tables generated have a number of `create' rvals within them. % llds_common.m converts these into static data structures. % % We can create several types of stack layouts. Which kind we generate % depends on the values of several options. % % Main authors: trd, zs. % % NOTE: If you make changes in this file, you may also need to modify % runtime/mercury_stack_layout.h. % %---------------------------------------------------------------------------% % % Data Stucture: stack_layouts % % If the option basic_stack_layout is set, we generate a MR_Stack_Layout_Entry % for each procedure. This will be stored in the global variable whose name is % mercury_data__layout__mercury__. % This structure will always contain stack tracing information: % % code address (Code *) - address of entry % succip stack location (uint_least32_t) actually, type MR_Long_Lval % (the location will be set to -1 % if there is no succip available). % number of stack slots (uint_least16_t) % determinism (uint_least16_t) actually, type MR_Determinism % % If the option procid_stack_layout is set, i.e. if we are doing stack % tracing, execution tracing or profiling, the structure will also include % information on the identity of the procedure. This information will take % one of two forms. Almost all procedures use the first form: % % predicate/function (Integer) actually, MR_pred_func % declaring module name (String) % defining module name (String) % predicate name (String) % predicate arity (int_least16_t) % procedure number (int_least16_t) % % Automatically generated unification, index and comparison predicates % use the second form: % % type name (String) % type module's name (String) % defining module name (String) % predicate name (String) % predicate arity (int_least16_t) % procedure number (int_least16_t) % % The runtime system can figure out which form is present by testing % the value of the first slot. A value of 0 or 1 indicates the first form; % any higher value indicates the second form. The distinguished value -1 % indicates that procid_stack_layout is not set, and that the later fields % are not present. % % The meanings of the fields in both forms are the same as in procedure labels. % % If the option trace_stack_layout is set, i.e. if we are doing execution % tracing, the structure will also include some extra fields: % % call trace info (MR_Stack_Layout_Label *) - points to the % layout structure of the call event % module layout (MR_Module_Layout *) - points to the layout % struct of the containing module. % max reg at trace event (int_least16_t) - the number of the highest % numbered rN register live at a trace event % inside the procedure % maybe from full (int_least8_t) - number of the stack slot of % the from_full flag, if the procedure is % shallow traced % maybe trail (int_least8_t) - number of the first of two % stack slots used for recording the state of % the trail, if trailing is enabled % maybe decl debug (int_least8_t) - number of the first of two % stack slots used by the declarative debugger, % if --trace-decl is set % % The first will point to the per-label layout info for the label associated % with the call event at the entry to the procedure. The purpose of this % information is to allow the runtime debugger to find out which variables % are where on entry, so it can reexecute the procedure if asked to do so % and if the values of the required variables are still available. % (If trace_stack_layout is not set, this field will be present, % but it will be set to NULL.) % % If the procedure is compiled with deep tracing, the fourth field will contain % a negative number. If it is compiled with shallow tracing, it will contain % the number of the stack slot that holds the flag that says whether this % incarnation of the procedure was called from deeply traced code or not. % (The determinism of the procedure decides whether the stack slot refers % to a stackvar or a framevar.) % % If --trace-decl is not set, the sixth field will contain a negative number. % If it is set, it will contain the number of the first of two stack slots % used by the declarative debugger; the other slot is the next higher numbered % one. (The determinism of the procedure decides whether the stack slot refers % to a stackvar or a framevar.) % % If the option basic_stack_layout is set, we generate stack layout tables % for some labels internal to the procedure. This table will be stored in the % global variable whose name is % mercury_data__layout__mercury___i. % This table has the following format: % % proc layout (Word *) - pointer to the layout structure of % the procedure containing this label % trace port (int_least16) - a representation of the trace % port associated with the label, or -1 % goal path (int_least16) - an index into the module's % string table giving the goal path associated % with the trace port of the label, or -1 % # of live data items (Integer) - an encoded representation of % the number of live data items at the label % live data types locns (void *) - pointer to an area of memory % containing information about where the live % data items are and what their types are % live data names (MR_Var_Name *) - pointer to vector of % MR_Var_Name structs giving the HLDS var numbers % as well as the names of live data items % type parameters (MR_Long_Lval *) - pointer to vector of % MR_Long_Lval giving the locations of the % typeinfos for the type parameters that may % be referred to by the types of the live data % items; the first word of the vector is an % integer giving the number of entries in the % vector; a NULL pointer means no type parameters % % The layout of the memory area containing information about the locations % and types of live data items is somewhat complicated, due to our desire % to make this information compact. We can represent a location in one of % two ways, as an 8-bit MR_Short_Lval or as a 32-bit MR_Long_Lval. % We prefer representing a location as an MR_Short_Lval, but of course % not all locations can be represented in this way, so those other locations % are represented as MR_Long_Lvals. % % The field containing the number of live data items is encoded by the % formula (#Long << short_count_bits + #Short), where #Short is the number % data items whose descriptions fit into an MR_Short_Lval and #Long is the % number of data items whose descriptions do not. (The field is not an integer % so that people who attempt to use it without going through the decoding % macros in runtime/mercury_stack_layout.h get an error from the C compiler. % The number of distinct values that fit into a uint_least_t also fits into % 8 bits, but since some locations hold the value of more than one variable % at a time, not all the values need to be distinct; this is why % short_count_bits is more than 8.) % % The memory area contains three vectors back to back. The first vector % has #Long + #Short word-sized elements, each of which is a pointer to a % MR_PseudoTypeInfo giving the type of a live data item, with a small % integer instead of a pointer representing a special kind of live data item % (e.g. a saved succip or hp). The second vector is an array of #Long % MR_Long_Lvals, and the third is an array of #Short MR_Short_Lvals, % each of which describes a location. The pseudotypeinfo pointed to by % the slot at subscript i in the first vector describes the type of % the data stored in slot i in the second vector if i < #Long, and % the type of the data stored in slot i - #Long in the third vector % otherwise. % % The live data pair vector will have an entry for each live variable. % The entry will give the location of the variable and its type. % % The live data name vector pointer may be NULL. If it is not, the vector % will have an entry consisting of two 16-bit numbers for each live data item. % The first is the live data item's HLDS variable number, or one of two % special values. Zero means that the live data item is not a variable % (e.g. it is a saved copy of succip). The largest possible 16-bit number % on the other hand means "the number of this variable does not fit into % 16 bits". With the exception of these special values, the value in this % slot uniquely identifies the variable. The second 16-bit number is an offset % into the module-wide string table; the string at that offset is the % variable's name. If the variable or data item has no name, the offset % will be zero (at which offset one will find an empty string). The string % table is restricted to be small enough to be addressed with 16 bits; % a string is reserved near the start for a string that says "too many % variables". Stack_layout.m will generate a reference to this string % instead of generating an offset that does not fit into 16 bits. % Therefore using the stored offset to index into the string table % is always safe. % % If the number of type parameters is not zero, we store the number, % so that the code that needs the type parameters can materialize % all the type parameters from their location descriptions in one go. % This is an optimization, since the type parameter vector could simply % be indexed on demand by the type variable's variable number stored within % pseudo-typeinfos inside the elements of the live data pairs vectors. % % Since we allocate type variable numbers sequentially, the type parameter % vector will usually be dense. However, after all variables whose types % include e.g. type variable 2 have gone out of scope, variables whose % types include type variable 3 may still be around. In cases like this, % the entry for type variable 2 will be zero; this signals to the code % in the internal debugger that materializes typeinfo structures that % this typeinfo structure need not be materialized. % % We need detailed information about the variables that are live at an % internal label in two kinds of circumstances. Stack layout information % will be present only for labels that fall into one or both of these % circumstances. % % - The option trace_stack_layout is set, and the label represents % a traced event at which variable info is needed (call, exit, % or entrance to one branch of a branched control structure; % fail events have no variable information). % % - The option agc_stack_layout is set or the trace level specifies % a capability for uplevel printing, and the label represents % a point where execution can resume after a procedure call or % after backtracking. % % For labels that do not fall into one of these two categories, the % "# of live vars" field will be negative to indicate the absence of % information about the variables live at this label, and the last % four fields will not be present. % % For labels that do fall into one of these two categories, the % "# of live vars" field will hold the number of live variables, which % will not be negative. If it is zero, the last four fields will not be % present. Even if it is not zero, however, the pointer to the live data % names vector will be NULL unless the label is used in execution tracing. % % XXX: Presently, inst information is ignored. We also do not yet enable % procid stack layouts for profiling, since profiling does not yet use % stack layouts. % %---------------------------------------------------------------------------% :- module stack_layout. :- interface. :- import_module continuation_info, hlds_module, llds. :- import_module std_util, list, set_bbbtree. :- pred stack_layout__generate_llds(module_info::in, module_info::out, global_data::in, list(comp_gen_c_data)::out, list(comp_gen_c_data)::out, set_bbbtree(label)::out) is det. :- pred stack_layout__construct_closure_layout(proc_label::in, closure_layout_info::in, list(maybe(rval))::out, create_arg_types::out, int::in, int::out) is det. :- implementation. :- import_module globals, options, llds_out, trace. :- import_module hlds_data, hlds_pred, base_type_layout, prog_data, prog_out. :- import_module (inst), code_util. :- import_module assoc_list, bool, string, int, require. :- import_module map, term, set. %---------------------------------------------------------------------------% % Process all the continuation information stored in the HLDS, % converting it into LLDS data structures. stack_layout__generate_llds(ModuleInfo0, ModuleInfo, GlobalData, PossiblyDynamicLayouts, StaticLayouts, LayoutLabels) :- global_data_get_all_proc_layouts(GlobalData, ProcLayoutList), module_info_name(ModuleInfo0, ModuleName), module_info_get_cell_count(ModuleInfo0, CellCount), module_info_globals(ModuleInfo0, Globals), globals__lookup_bool_option(Globals, agc_stack_layout, AgcLayout), globals__lookup_bool_option(Globals, trace_stack_layout, TraceLayout), globals__lookup_bool_option(Globals, procid_stack_layout, ProcIdLayout), globals__have_static_code_addresses(Globals, StaticCodeAddr), set_bbbtree__init(LayoutLabels0), map__init(StringMap0), map__init(LabelTables0), StringTable0 = string_table(StringMap0, [], 0), LayoutInfo0 = stack_layout_info(ModuleName, CellCount, AgcLayout, TraceLayout, ProcIdLayout, StaticCodeAddr, [], [], LayoutLabels0, [], StringTable0, LabelTables0), stack_layout__lookup_string_in_table("", _, LayoutInfo0, LayoutInfo1), stack_layout__lookup_string_in_table("", _, LayoutInfo1, LayoutInfo2), list__foldl(stack_layout__construct_layouts, ProcLayoutList, LayoutInfo2, LayoutInfo3), % This version of the layout info structure is final in all % respects except the cell count. LayoutInfo3 = stack_layout_info(_, _, _, _, _, _, ProcLayouts, InternalLayouts, LayoutLabels, ProcLayoutArgs, StringTable, LabelTables), StringTable = string_table(_, RevStringList, StringOffset), list__reverse(RevStringList, StringList), stack_layout__concat_string_list(StringList, StringOffset, ConcatStrings), ( TraceLayout = yes -> Exported = no, % ignored; see linkage/2 in llds_out.m list__length(ProcLayoutList, NumProcLayouts), llds_out__sym_name_mangle(ModuleName, ModuleNameStr), stack_layout__get_next_cell_number(ProcVectorCellNum, LayoutInfo3, LayoutInfo4), Reuse = no, ProcLayoutVector = create(0, ProcLayoutArgs, uniform(yes(data_ptr)), must_be_static, ProcVectorCellNum, "proc_layout_vector", Reuse), globals__lookup_bool_option(Globals, rtti_line_numbers, LineNumbers), ( LineNumbers = yes -> EffLabelTables = LabelTables ; map__init(EffLabelTables) ), stack_layout__format_label_tables(EffLabelTables, NumSourceFiles, SourceFileVectors, LayoutInfo4, LayoutInfo), Rvals = [yes(const(string_const(ModuleNameStr))), yes(const(int_const(StringOffset))), yes(const(multi_string_const(StringOffset, ConcatStrings))), yes(const(int_const(NumProcLayouts))), yes(ProcLayoutVector), yes(const(int_const(NumSourceFiles))), yes(SourceFileVectors)], ModuleLayouts = comp_gen_c_data(ModuleName, module_layout, Exported, Rvals, uniform(no), []), StaticLayouts = [ModuleLayouts | InternalLayouts] ; StaticLayouts = InternalLayouts, LayoutInfo = LayoutInfo3 ), PossiblyDynamicLayouts = ProcLayouts, stack_layout__get_cell_number(FinalCellCount, LayoutInfo, _), module_info_set_cell_count(ModuleInfo0, FinalCellCount, ModuleInfo). %---------------------------------------------------------------------------% :- pred stack_layout__concat_string_list(list(string)::in, int::in, string::out) is det. :- pragma c_code(stack_layout__concat_string_list(StringList::in, ArenaSize::in, Arena::out), [will_not_call_mercury, thread_safe], "{ Word cur_node; Integer cur_offset; Word tmp; incr_hp_atomic(tmp, (ArenaSize + sizeof(Word)) / sizeof(Word)); Arena = (char *) tmp; cur_offset = 0; cur_node = StringList; while (! MR_list_is_empty(cur_node)) { (void) strcpy(&Arena[cur_offset], (char *) MR_list_head(cur_node)); cur_offset += strlen((char *) MR_list_head(cur_node)) + 1; cur_node = MR_list_tail(cur_node); } if (cur_offset != ArenaSize) { char msg[256]; sprintf(msg, ""internal error in creating string table;\\n"" ""cur_offset = %ld, ArenaSize = %ld\\n"", (long) cur_offset, (long) ArenaSize); fatal_error(msg); } }"). %---------------------------------------------------------------------------% :- pred stack_layout__format_label_tables(map(string, label_table)::in, int::out, rval::out, stack_layout_info::in, stack_layout_info::out) is det. stack_layout__format_label_tables(LabelTableMap, NumSourceFiles, SourceFilesVector, LayoutInfo0, LayoutInfo) :- map__to_assoc_list(LabelTableMap, LabelTableList), list__length(LabelTableList, NumSourceFiles), list__map_foldl(stack_layout__format_label_table, LabelTableList, SourceFileRvals, LayoutInfo0, LayoutInfo1), stack_layout__get_next_cell_number(SourceFileVectorCellNum, LayoutInfo1, LayoutInfo), Reuse = no, SourceFilesVector = create(0, SourceFileRvals, uniform(yes(data_ptr)), must_be_static, SourceFileVectorCellNum, "source_files_vector", Reuse). :- pred stack_layout__format_label_table(pair(string, label_table)::in, maybe(rval)::out, stack_layout_info::in, stack_layout_info::out) is det. stack_layout__format_label_table(FileName - LineNoMap, yes(SourceFileVector), LayoutInfo0, LayoutInfo) :- % This step should produce a list ordered on line numbers. map__to_assoc_list(LineNoMap, LineNoList), % And this step should preserve that order. stack_layout__flatten_label_table(LineNoList, [], FlatLineNoList), list__length(FlatLineNoList, VectorLength), stack_layout__get_module_name(CurrentModule, LayoutInfo0, LayoutInfo1), ProjectLineNos = lambda([LabelInfo::in, LineNoRval::out] is det, ( LabelInfo = LineNo - (_Label - _IsReturn), LineNoRval = yes(const(int_const(LineNo))) )), ProjectLabels = lambda([LabelInfo::in, LabelRval::out] is det, ( LabelInfo = _LineNo - (Label - _IsReturn), DataAddr = data_addr(CurrentModule, internal_layout(Label)), LabelRval = yes(const(data_addr_const(DataAddr))) )), % See the comment below. % ProjectCallees = lambda([LabelInfo::in, CalleeRval::out] is det, ( % LabelInfo = _LineNo - (_Label - IsReturn), % ( % IsReturn = not_a_return, % CalleeRval = yes(const(int_const(0))) % ; % IsReturn = unknown_callee, % CalleeRval = yes(const(int_const(1))) % ; % IsReturn = known_callee(Label), % code_util__extract_proc_label_from_label(Label, % ProcLabel), % ( % ProcLabel = proc(ModuleName, _, _, _, _, _) % ; % ProcLabel = special_proc(ModuleName, _, _, % _, _, _) % ), % DataAddr = data_addr(ModuleName, proc_layout(Label)), % CalleeRval = yes(const(data_addr_const(DataAddr))) % ) % )), list__map(ProjectLineNos, FlatLineNoList, LineNoRvals), stack_layout__get_next_cell_number(LineNoVectorCellNum, LayoutInfo1, LayoutInfo2), Reuse = no, LineNoVector = create(0, LineNoRvals, uniform(yes(int_least16)), must_be_static, LineNoVectorCellNum, "line_number_vector", Reuse), list__map(ProjectLabels, FlatLineNoList, LabelRvals), stack_layout__get_next_cell_number(LabelsVectorCellNum, LayoutInfo2, LayoutInfo3), LabelsVector = create(0, LabelRvals, uniform(yes(data_ptr)), must_be_static, LabelsVectorCellNum, "label_vector", Reuse), % We do not include the callees vector in the table because it makes references % to the proc layouts of procedures from other modules without knowing whether % those modules were compiled with debugging. This works only if all procedures % always have a proc layout structure, which we don't want to require yet. % % Callees vectors would allow us to use faster code to check at every event % whether a breakpoint applies to that event, in the usual case that no context % breakpoint is on a line contains a higher order call. Instead of always % searching a separate data structure, as we now do, to check for the % applicability of context breakpoints, the code could search this data % structure only if the proc layout matched the proc layout of the caller % Since we already search a table of proc layouts in order to check for plain, % non-context breakpoints on procedures, this would incur no extra cost % in most cases. % % list__map(ProjectCallees, FlatLineNoList, CalleeRvals), % stack_layout__get_next_cell_number(CalleesVectorCellNum, % LayoutInfo3, LayoutInfo4), % CalleesVector = create(0, CalleeRvals, % uniform(no), must_be_static, % CalleesVectorCellNum, "callee_vector", Reuse), SourceFileRvals = [ yes(const(string_const(FileName))), yes(const(int_const(VectorLength))), yes(LineNoVector), yes(LabelsVector) % yes(CalleesVector) ], stack_layout__get_next_cell_number(SourceFileVectorCellNum, LayoutInfo3, LayoutInfo), SourceFileVector = create(0, SourceFileRvals, initial([1 - yes(string), 1 - yes(integer), 2 - yes(data_ptr)], none), must_be_static, SourceFileVectorCellNum, "source_file_vector", Reuse). :- pred stack_layout__flatten_label_table( assoc_list(int, list(line_no_info))::in, assoc_list(int, line_no_info)::in, assoc_list(int, line_no_info)::out) is det. stack_layout__flatten_label_table([], RevList, List) :- list__reverse(RevList, List). stack_layout__flatten_label_table([LineNo - LinesInfos | Lines], RevList0, List) :- list__foldl(stack_layout__add_line_no(LineNo), LinesInfos, RevList0, RevList1), stack_layout__flatten_label_table(Lines, RevList1, List). :- pred stack_layout__add_line_no(int::in, line_no_info::in, assoc_list(int, line_no_info)::in, assoc_list(int, line_no_info)::out) is det. stack_layout__add_line_no(LineNo, LineInfo, RevList0, RevList) :- RevList = [LineNo - LineInfo | RevList0]. %---------------------------------------------------------------------------% % Construct the layouts that concern a single procedure: % the procedure-specific layout and the layouts of the labels % inside that procedure. Also update the module-wide label table % with the labels defined in this procedure. :- pred stack_layout__construct_layouts(proc_layout_info::in, stack_layout_info::in, stack_layout_info::out) is det. stack_layout__construct_layouts(ProcLayoutInfo) --> { ProcLayoutInfo = proc_layout_info(EntryLabel, Detism, StackSlots, SuccipLoc, MaybeCallLabel, MaxTraceReg, TraceSlotInfo, ForceProcIdLayout, InternalMap) }, stack_layout__construct_proc_layout(EntryLabel, Detism, StackSlots, SuccipLoc, MaybeCallLabel, MaxTraceReg, TraceSlotInfo, ForceProcIdLayout), { map__to_assoc_list(InternalMap, Internals) }, list__foldl(stack_layout__construct_internal_layout(EntryLabel), Internals), list__foldl(stack_layout__update_label_table, Internals). %---------------------------------------------------------------------------% % Add the given label to the module-wide label tables. :- pred stack_layout__update_label_table(pair(label, internal_layout_info)::in, stack_layout_info::in, stack_layout_info::out) is det. stack_layout__update_label_table(Label - InternalInfo) --> { InternalInfo = internal_layout_info(Port, _, Return) }, ( { Return = yes(return_layout_info(TargetsContexts, _)) }, { stack_layout__find_valid_return_context(TargetsContexts, Target, Context) } -> { Target = label(TargetLabel) -> IsReturn = known_callee(TargetLabel) ; IsReturn = unknown_callee }, stack_layout__update_label_table_2(Label, Context, IsReturn) ; { Port = yes(trace_port_layout_info(Context, _, _, _)) }, { stack_layout__context_is_valid(Context) } -> stack_layout__update_label_table_2(Label, Context, not_a_return) ; [] ). :- pred stack_layout__update_label_table_2(label::in, context::in, is_label_return::in, stack_layout_info::in, stack_layout_info::out) is det. stack_layout__update_label_table_2(Label, Context, IsReturn) --> { term__context_file(Context, File) }, { term__context_line(Context, Line) }, stack_layout__get_label_tables(LabelTables0), { map__search(LabelTables0, File, LabelTable0) -> ( map__search(LabelTable0, Line, LineInfo0) -> LineInfo = [Label - IsReturn | LineInfo0], map__det_update(LabelTable0, Line, LineInfo, LabelTable), map__det_update(LabelTables0, File, LabelTable, LabelTables) ; LineInfo = [Label - IsReturn], map__det_insert(LabelTable0, Line, LineInfo, LabelTable), map__det_update(LabelTables0, File, LabelTable, LabelTables) ) ; stack_layout__context_is_valid(Context) -> map__init(LabelTable0), LineInfo = [Label - IsReturn], map__det_insert(LabelTable0, Line, LineInfo, LabelTable), map__det_insert(LabelTables0, File, LabelTable, LabelTables) ; % We don't have a valid context for this label, % so we don't enter it into any tables. LabelTables = LabelTables0 }, stack_layout__set_label_tables(LabelTables). :- pred stack_layout__find_valid_return_context( assoc_list(code_addr, prog_context)::in, code_addr::out, prog_context::out) is semidet. stack_layout__find_valid_return_context([Target - Context | TargetContexts], ValidTarget, ValidContext) :- ( stack_layout__context_is_valid(Context) -> ValidTarget = Target, ValidContext = Context ; stack_layout__find_valid_return_context(TargetContexts, ValidTarget, ValidContext) ). :- pred stack_layout__context_is_valid(prog_context::in) is semidet. stack_layout__context_is_valid(Context) :- term__context_file(Context, File), term__context_line(Context, Line), File \= "", Line > 0. %---------------------------------------------------------------------------% % Construct a procedure-specific layout. :- pred stack_layout__construct_proc_layout(label::in, determinism::in, int::in, maybe(int)::in, maybe(label)::in, int::in, trace_slot_info::in, bool::in, stack_layout_info::in, stack_layout_info::out) is det. stack_layout__construct_proc_layout(EntryLabel, Detism, StackSlots, MaybeSuccipLoc, MaybeCallLabel, MaxTraceReg, TraceSlotInfo, ForceProcIdLayout) --> { MaybeSuccipLoc = yes(Location0) -> Location = Location0 ; % Use a dummy location of -1 if there is % no succip on the stack. % % This case can arise in two circumstances. % First, procedures that use the nondet stack % have a special slot for the succip, so the % succip is not stored in a general purpose % slot. Second, procedures that use the det stack % but which do not call other procedures % do not save the succip on the stack. % % The tracing system does not care about the % location of the saved succip. The accurate % garbage collector does. It should know from % the determinism that the procedure uses the % nondet stack, which takes care of the first % possibility above. Procedures that do not call % other procedures do not establish resumption % points and thus agc is not interested in them. % As far as stack dumps go, calling error counts % as a call, so any procedure that may call error % (directly or indirectly) will have its saved succip % location recorded, so the stack dump will work. % % Future uses of stack layouts will have to have % similar constraints. Location = -1 }, stack_layout__get_static_code_addresses(StaticCodeAddr), { StaticCodeAddr = yes -> CodeAddrRval = const(code_addr_const(label(EntryLabel))) ; % This is a lie; the slot will be filled in for real % at initialization time. CodeAddrRval = const(int_const(0)) }, { determinism_components(Detism, _, at_most_many) -> SuccipLval = framevar(Location) ; SuccipLval = stackvar(Location) }, { stack_layout__represent_locn_as_int(direct(SuccipLval), SuccipRval) }, { StackSlotsRval = const(int_const(StackSlots)) }, { stack_layout__represent_determinism(Detism, DetismRval) }, { TraversalRvals = [yes(CodeAddrRval), yes(SuccipRval), yes(StackSlotsRval), yes(DetismRval)] }, { TraversalArgTypes = [1 - yes(code_ptr), 1 - yes(uint_least32), 2 - yes(uint_least16)] }, stack_layout__get_procid_stack_layout(ProcIdLayout0), { bool__or(ProcIdLayout0, ForceProcIdLayout, ProcIdLayout) }, ( { ProcIdLayout = yes } -> { code_util__extract_proc_label_from_label(EntryLabel, ProcLabel) }, { stack_layout__construct_procid_rvals(ProcLabel, IdRvals, IdArgTypes) }, stack_layout__construct_trace_layout(MaybeCallLabel, MaxTraceReg, TraceSlotInfo, TraceRvals, TraceArgTypes), { list__append(IdRvals, TraceRvals, IdTraceRvals) }, { IdTraceArgTypes = initial(IdArgTypes, TraceArgTypes) } ; % Indicate the absence of the proc id and exec trace fields. { IdTraceRvals = [yes(const(int_const(-1)))] }, { IdTraceArgTypes = initial([1 - yes(integer)], none) } ), { Exported = no }, % XXX With the new profiler, we will need to % set this to `yes' if the profiling option % is given and if the procedure is exported. % Beware however that linkage/2 in llds_out.m % assumes that this is `no'. { list__append(TraversalRvals, IdTraceRvals, Rvals) }, { ArgTypes = initial(TraversalArgTypes, IdTraceArgTypes) }, stack_layout__get_module_name(ModuleName), { CDataName = proc_layout(EntryLabel) }, { CData = comp_gen_c_data(ModuleName, CDataName, Exported, Rvals, ArgTypes, []) }, stack_layout__add_proc_layout_data(CData, CDataName, EntryLabel). :- pred stack_layout__construct_trace_layout(maybe(label)::in, int::in, trace_slot_info::in, list(maybe(rval))::out, create_arg_types::out, stack_layout_info::in, stack_layout_info::out) is det. stack_layout__construct_trace_layout(MaybeCallLabel, MaxTraceReg, TraceSlotInfo, Rvals, ArgTypes) --> stack_layout__get_module_name(ModuleName), stack_layout__get_trace_stack_layout(TraceLayout), { TraceLayout = yes -> ( MaybeCallLabel = yes(CallLabel) -> CallRval = yes(const(data_addr_const( data_addr(ModuleName, internal_layout(CallLabel))))) ; error("stack_layout__construct_trace_layout: call label not present") ), ModuleRval = yes(const(data_addr_const( data_addr(ModuleName, module_layout)))), MaxTraceRegRval = yes(const(int_const(MaxTraceReg))), TraceSlotInfo = trace_slot_info(MaybeFromFullSlot, MaybeDeclSlots, MaybeTrailSlot), ( MaybeFromFullSlot = yes(FromFullSlot) -> FromFullRval = yes(const(int_const(FromFullSlot))) ; FromFullRval = yes(const(int_const(-1))) ), ( MaybeDeclSlots = yes(DeclSlot) -> DeclRval = yes(const(int_const(DeclSlot))) ; DeclRval = yes(const(int_const(-1))) ), ( MaybeTrailSlot = yes(TrailSlot) -> TrailRval = yes(const(int_const(TrailSlot))) ; TrailRval = yes(const(int_const(-1))) ), Rvals = [CallRval, ModuleRval, MaxTraceRegRval, FromFullRval, TrailRval, DeclRval], ArgTypes = initial([ 2 - yes(data_ptr), 1 - yes(int_least16), 3 - yes(int_least8)], none) ; % Indicate the absence of the trace layout fields. Rvals = [yes(const(int_const(0)))], ArgTypes = initial([1 - yes(integer)], none) }. %---------------------------------------------------------------------------% :- pred stack_layout__construct_procid_rvals(proc_label::in, list(maybe(rval))::out, initial_arg_types::out) is det. stack_layout__construct_procid_rvals(ProcLabel, Rvals, ArgTypes) :- ( ProcLabel = proc(DefModule, PredFunc, DeclModule, PredName, Arity, ProcId), stack_layout__represent_pred_or_func(PredFunc, PredFuncCode), prog_out__sym_name_to_string(DefModule, DefModuleString), prog_out__sym_name_to_string(DeclModule, DeclModuleString), proc_id_to_int(ProcId, Mode), Rvals = [ yes(const(int_const(PredFuncCode))), yes(const(string_const(DeclModuleString))), yes(const(string_const(DefModuleString))), yes(const(string_const(PredName))), yes(const(int_const(Arity))), yes(const(int_const(Mode))) ], ArgTypes = [4 - no, 2 - yes(int_least16)] ; ProcLabel = special_proc(DefModule, PredName, TypeModule, TypeName, Arity, ProcId), prog_out__sym_name_to_string(TypeModule, TypeModuleString), prog_out__sym_name_to_string(DefModule, DefModuleString), proc_id_to_int(ProcId, Mode), Rvals = [ yes(const(string_const(TypeName))), yes(const(string_const(TypeModuleString))), yes(const(string_const(DefModuleString))), yes(const(string_const(PredName))), yes(const(int_const(Arity))), yes(const(int_const(Mode))) ], ArgTypes = [4 - no, 2 - yes(int_least16)] ). :- pred stack_layout__represent_pred_or_func(pred_or_func::in, int::out) is det. stack_layout__represent_pred_or_func(predicate, 0). stack_layout__represent_pred_or_func(function, 1). %---------------------------------------------------------------------------% % Construct the layout describing a single internal label. :- pred stack_layout__construct_internal_layout(label::in, pair(label, internal_layout_info)::in, stack_layout_info::in, stack_layout_info::out) is det. stack_layout__construct_internal_layout(EntryLabel, Label - Internal) --> % generate the required rvals stack_layout__get_module_name(ModuleName), { EntryAddrRval = const(data_addr_const(data_addr(ModuleName, proc_layout(EntryLabel)))) }, stack_layout__construct_internal_rvals(Internal, VarInfoRvals, VarInfoRvalTypes), { LayoutRvals = [yes(EntryAddrRval) | VarInfoRvals] }, { ArgTypes = initial([1 - no], VarInfoRvalTypes) }, { CData = comp_gen_c_data(ModuleName, internal_layout(Label), no, LayoutRvals, ArgTypes, []) }, stack_layout__add_internal_layout_data(CData, Label). % Construct the rvals required for accurate GC or for tracing. :- pred stack_layout__construct_internal_rvals(internal_layout_info::in, list(maybe(rval))::out, create_arg_types::out, stack_layout_info::in, stack_layout_info::out) is det. stack_layout__construct_internal_rvals(Internal, RvalList, ArgTypes) --> { Internal = internal_layout_info(Trace, Resume, Return) }, ( { Trace = no }, { set__init(TraceLiveVarSet) }, { map__init(TraceTypeVarMap) } ; { Trace = yes(trace_port_layout_info(_, _, _, TraceLayout)) }, { TraceLayout = layout_label_info(TraceLiveVarSet, TraceTypeVarMap) } ), { TraceArgTypes = [2 - yes(int_least16)] }, { Resume = no, set__init(ResumeLiveVarSet), map__init(ResumeTypeVarMap) ; Resume = yes(ResumeLayout), ResumeLayout = layout_label_info(ResumeLiveVarSet, ResumeTypeVarMap) }, ( { Trace = yes(trace_port_layout_info(_, Port, Path, _)) }, { Return = no }, { llds_out__trace_port_to_num(Port, PortNum) }, { trace__path_to_string(Path, PathStr) }, stack_layout__lookup_string_in_table(PathStr, PathNum) ; { Trace = no }, { Return = yes(_) }, % We only ever use the port and path fields of these % layout structures when we process exception events. { llds_out__trace_port_to_num(exception, PortNum) }, { PathNum = 0 } ; { Trace = no }, { Return = no }, { PortNum = -1 }, { PathNum = -1 } ; { Trace = yes(_) }, { Return = yes(_) }, { error("label has both trace and return layout info") } ), { TraceRvals = [yes(const(int_const(PortNum))), yes(const(int_const(PathNum)))] }, stack_layout__get_agc_stack_layout(AgcStackLayout), { Return = no, set__init(ReturnLiveVarSet), map__init(ReturnTypeVarMap) ; Return = yes(return_layout_info(_, ReturnLayout)), ReturnLayout = layout_label_info(ReturnLiveVarSet0, ReturnTypeVarMap0), ( AgcStackLayout = yes -> ReturnLiveVarSet = ReturnLiveVarSet0, ReturnTypeVarMap = ReturnTypeVarMap0 ; % This set of variables must be for uplevel printing % in execution tracing, so we are interested only % in (a) variables, not temporaries, (b) only named % variables, and (c) only those on the stack, not % the return values. set__to_sorted_list(ReturnLiveVarSet0, ReturnLiveVarList0), stack_layout__select_trace_return( ReturnLiveVarList0, ReturnTypeVarMap0, ReturnLiveVarList, ReturnTypeVarMap), set__list_to_set(ReturnLiveVarList, ReturnLiveVarSet) ) }, ( { Trace = no }, { Resume = no }, { Return = no } -> % The -1 says that there is no info available % about variables at this label. (Zero would say % that there are no variables live at this label, % which may not be true.) { RvalList = [yes(const(int_const(-1)))] }, { ArgTypes = initial([1 - yes(integer)], none) } ; % XXX ignore differences in insts inside var_infos { set__union(TraceLiveVarSet, ResumeLiveVarSet, LiveVarSet0) }, { set__union(LiveVarSet0, ReturnLiveVarSet, LiveVarSet) }, { map__union(set__intersect, TraceTypeVarMap, ResumeTypeVarMap, TypeVarMap0) }, { map__union(set__intersect, TypeVarMap0, ReturnTypeVarMap, TypeVarMap) }, stack_layout__construct_livelval_rvals(LiveVarSet, TypeVarMap, LivelvalRvalList, LivelvalArgTypes), { append(TraceRvals, LivelvalRvalList, RvalList) }, { ArgTypes = initial(TraceArgTypes, LivelvalArgTypes) } ). %---------------------------------------------------------------------------% :- pred stack_layout__construct_livelval_rvals(set(var_info)::in, map(tvar, set(layout_locn))::in, list(maybe(rval))::out, create_arg_types::out, stack_layout_info::in, stack_layout_info::out) is det. stack_layout__construct_livelval_rvals(LiveLvalSet, TVarLocnMap, RvalList, ArgTypes) --> { set__to_sorted_list(LiveLvalSet, LiveLvals) }, { list__length(LiveLvals, Length) }, ( { Length > 0 } -> { stack_layout__sort_livevals(LiveLvals, SortedLiveLvals) }, stack_layout__construct_liveval_arrays(SortedLiveLvals, VarLengthRval, LiveValRval, NamesRval), stack_layout__get_cell_number(CNum0), { stack_layout__construct_tvar_vector(TVarLocnMap, TypeParamRval, CNum0, CNum) }, stack_layout__set_cell_number(CNum), { RvalList = [yes(VarLengthRval), yes(LiveValRval), yes(NamesRval), yes(TypeParamRval)] }, { ArgTypes = initial([1 - yes(integer), 3 - yes(data_ptr)], none) } ; { RvalList = [yes(const(int_const(0)))] }, { ArgTypes = initial([1 - yes(integer)], none) } ). :- pred stack_layout__construct_tvar_vector(map(tvar, set(layout_locn))::in, rval::out, int::in, int::out) is det. stack_layout__construct_tvar_vector(TVarLocnMap, TypeParamRval, CNum0, CNum) :- ( map__is_empty(TVarLocnMap) -> TypeParamRval = const(int_const(0)), CNum = CNum0 ; stack_layout__construct_tvar_rvals(TVarLocnMap, Vector, VectorTypes), CNum is CNum0 + 1, Reuse = no, TypeParamRval = create(0, Vector, VectorTypes, must_be_static, CNum, "stack_layout_type_param_locn_vector", Reuse) ). :- pred stack_layout__construct_tvar_rvals(map(tvar, set(layout_locn))::in, list(maybe(rval))::out, create_arg_types::out) is det. stack_layout__construct_tvar_rvals(TVarLocnMap, Vector, VectorTypes) :- map__to_assoc_list(TVarLocnMap, TVarLocns), stack_layout__construct_type_param_locn_vector(TVarLocns, 1, TypeParamLocs), list__length(TypeParamLocs, TypeParamsLength), LengthRval = const(int_const(TypeParamsLength)), Vector = [yes(LengthRval) | TypeParamLocs], VectorTypes = uniform(yes(uint_least32)). %---------------------------------------------------------------------------% % Given a list of var_infos and the type variables that occur in them, % select only the var_infos that may be required by up-level printing % in the trace-based debugger. At the moment the typeinfo list we % return may be bigger than necessary, but this does not compromise % correctness; we do this to avoid having to scan the types of all % the selected var_infos. :- pred stack_layout__select_trace_return( list(var_info)::in, map(tvar, set(layout_locn))::in, list(var_info)::out, map(tvar, set(layout_locn))::out) is det. stack_layout__select_trace_return(Infos, TVars, TraceReturnInfos, TVars) :- IsNamedReturnVar = lambda([LocnInfo::in] is semidet, ( LocnInfo = var_info(Locn, LvalType), LvalType = var(_, Name, _, _), Name \= "", ( Locn = direct(Lval) ; Locn = indirect(Lval, _)), ( Lval = stackvar(_) ; Lval = framevar(_) ) )), list__filter(IsNamedReturnVar, Infos, TraceReturnInfos). % Given a list of var_infos, put the ones that tracing can be % interested in (whether at an internal port or for uplevel printing) % in a block at the start, and both this block and the remaining % block. The division into two blocks can make the job of the % debugger somewhat easier, the sorting of the named var block makes % the output of the debugger look nicer, and the sorting of the both % blocks makes it more likely that different labels' layout structures % will have common parts (e.g. name vectors) that can be optimized % by llds_common.m. :- pred stack_layout__sort_livevals(list(var_info)::in, list(var_info)::out) is det. stack_layout__sort_livevals(OrigInfos, FinalInfos) :- IsNamedVar = lambda([LvalInfo::in] is semidet, ( LvalInfo = var_info(_Lval, LvalType), LvalType = var(_, Name, _, _), Name \= "" )), list__filter(IsNamedVar, OrigInfos, NamedVarInfos0, OtherInfos0), CompareVarInfos = lambda([Var1::in, Var2::in, Result::out] is det, ( Var1 = var_info(Lval1, LiveType1), Var2 = var_info(Lval2, LiveType2), stack_layout__get_name_from_live_value_type(LiveType1, Name1), stack_layout__get_name_from_live_value_type(LiveType2, Name2), compare(NameResult, Name1, Name2), ( NameResult = (=) -> compare(Result, Lval1, Lval2) ; Result = NameResult ) )), list__sort(CompareVarInfos, NamedVarInfos0, NamedVarInfos), list__sort(CompareVarInfos, OtherInfos0, OtherInfos), list__append(NamedVarInfos, OtherInfos, FinalInfos). :- pred stack_layout__get_name_from_live_value_type(live_value_type::in, string::out) is det. stack_layout__get_name_from_live_value_type(LiveType, Name) :- ( LiveType = var(_, NamePrime, _, _) -> Name = NamePrime ; Name = "" ). %---------------------------------------------------------------------------% % Given a association list of type variables and their locations % sorted on the type variables, represent them in an array of % location descriptions indexed by the type variable. The next % slot to fill is given by the second argument. :- pred stack_layout__construct_type_param_locn_vector( assoc_list(tvar, set(layout_locn))::in, int::in, list(maybe(rval))::out) is det. stack_layout__construct_type_param_locn_vector([], _, []). stack_layout__construct_type_param_locn_vector([TVar - Locns | TVarLocns], CurSlot, Vector) :- term__var_to_int(TVar, TVarNum), NextSlot is CurSlot + 1, ( TVarNum = CurSlot -> ( set__remove_least(Locns, LeastLocn, _) -> Locn = LeastLocn ; error("tvar has empty set of locations") ), stack_layout__represent_locn_as_int(Locn, Rval), stack_layout__construct_type_param_locn_vector(TVarLocns, NextSlot, VectorTail), Vector = [yes(Rval) | VectorTail] ; TVarNum > CurSlot -> stack_layout__construct_type_param_locn_vector(TVarLocns, NextSlot, VectorTail), % This slot will never be referred to. Vector = [yes(const(int_const(0))) | VectorTail] ; error("unsorted tvars in construct_type_param_locn_vector") ). %---------------------------------------------------------------------------% :- type liveval_array_info ---> live_array_info( rval, % Rval describing the location of a live value. % Always of llds type uint_least8 if the cell % is in the byte array, and uint_least32 if it % is in the int array. rval, % Rval describing the type of a live value. llds_type, % The llds type of the rval describing the % type. rval, % Rval describing the variable number of a % live value. Always of llds uint_least16. % Contains zero if the live value is not % a variable. Contains the hightest possible % uint_least16 value if the variable number % does not fit in 16 bits. rval % Rval describing the variable name of a % live value. Always of llds uint_least16. % Contains zero if the live value is not % a variable, or if it is a variable with % no name. ). % Construct a vector of (locn, live_value_type) pairs, % and a corresponding vector of variable names. :- pred stack_layout__construct_liveval_arrays(list(var_info)::in, rval::out, rval::out, rval::out, stack_layout_info::in, stack_layout_info::out) is det. stack_layout__construct_liveval_arrays(VarInfos, LengthRval, TypeLocnVector, NameVector) --> { int__pow(2, stack_layout__short_count_bits, BytesLimit) }, stack_layout__construct_liveval_array_infos(VarInfos, 0, BytesLimit, IntArrayInfo, ByteArrayInfo), { list__length(IntArrayInfo, IntArrayLength) }, { list__length(ByteArrayInfo, ByteArrayLength) }, { list__append(IntArrayInfo, ByteArrayInfo, AllArrayInfo) }, { EncodedLength is IntArrayLength << stack_layout__short_count_bits + ByteArrayLength }, { LengthRval = const(int_const(EncodedLength)) }, { SelectLocns = lambda([ArrayInfo::in, MaybeLocnRval::out] is det, ( ArrayInfo = live_array_info(LocnRval, _, _, _, _), MaybeLocnRval = yes(LocnRval) )) }, { SelectTypes = lambda([ArrayInfo::in, MaybeTypeRval::out] is det, ( ArrayInfo = live_array_info(_, TypeRval, _, _, _), MaybeTypeRval = yes(TypeRval) )) }, { SelectTypeTypes = lambda([ArrayInfo::in, CountTypeType::out] is det,( ArrayInfo = live_array_info(_, _, TypeType, _, _), CountTypeType = 1 - yes(TypeType) )) }, { AddRevNumsNames = lambda([ArrayInfo::in, NumNameRvals0::in, NumNameRvals::out] is det, ( ArrayInfo = live_array_info(_, _, _, NumRval, NameRval), NumNameRvals = [yes(NameRval), yes(NumRval) | NumNameRvals0] )) }, { list__map(SelectTypes, AllArrayInfo, AllTypes) }, { list__map(SelectTypeTypes, AllArrayInfo, AllTypeTypes) }, { list__map(SelectLocns, IntArrayInfo, IntLocns) }, { list__map(SelectLocns, ByteArrayInfo, ByteLocns) }, { list__append(IntLocns, ByteLocns, AllLocns) }, { list__append(AllTypes, AllLocns, TypeLocnVectorRvals) }, { LocnArgTypes = [IntArrayLength - yes(uint_least32), ByteArrayLength - yes(uint_least8)] }, { list__append(AllTypeTypes, LocnArgTypes, ArgTypes) }, stack_layout__get_next_cell_number(CNum1), { Reuse = no }, { TypeLocnVector = create(0, TypeLocnVectorRvals, initial(ArgTypes, none), must_be_static, CNum1, "stack_layout_locn_vector", Reuse) }, stack_layout__get_trace_stack_layout(TraceStackLayout), ( { TraceStackLayout = yes } -> { list__foldl(AddRevNumsNames, AllArrayInfo, [], RevVarNumNameRvals) }, { list__reverse(RevVarNumNameRvals, VarNumNameRvals) }, stack_layout__get_next_cell_number(CNum2), { NameVector = create(0, VarNumNameRvals, uniform(yes(uint_least16)), must_be_static, CNum2, "stack_layout_num_name_vector", Reuse) } ; { NameVector = const(int_const(0)) } ). :- pred stack_layout__construct_liveval_array_infos(list(var_info)::in, int::in, int::in, list(liveval_array_info)::out, list(liveval_array_info)::out, stack_layout_info::in, stack_layout_info::out) is det. stack_layout__construct_liveval_array_infos([], _, _, [], []) --> []. stack_layout__construct_liveval_array_infos([VarInfo | VarInfos], BytesSoFar, BytesLimit, IntVars, ByteVars) --> { VarInfo = var_info(Locn, LiveValueType) }, stack_layout__represent_live_value_type(LiveValueType, TypeRval, TypeRvalType), stack_layout__construct_liveval_name_rvals(VarInfo, VarNumRval, VarNameRval), ( { BytesSoFar < BytesLimit }, { stack_layout__represent_locn_as_byte(Locn, LocnByteRval) } -> { Var = live_array_info(LocnByteRval, TypeRval, TypeRvalType, VarNumRval, VarNameRval) }, stack_layout__construct_liveval_array_infos(VarInfos, BytesSoFar + 1, BytesLimit, IntVars, ByteVars0), { ByteVars = [Var | ByteVars0] } ; { stack_layout__represent_locn_as_int(Locn, LocnRval) }, { Var = live_array_info(LocnRval, TypeRval, TypeRvalType, VarNumRval, VarNameRval) }, stack_layout__construct_liveval_array_infos(VarInfos, BytesSoFar, BytesLimit, IntVars0, ByteVars), { IntVars = [Var | IntVars0] } ). :- pred stack_layout__construct_liveval_name_rvals(var_info::in, rval::out, rval::out, stack_layout_info::in, stack_layout_info::out) is det. stack_layout__construct_liveval_name_rvals(var_info(_, LiveValueType), VarNumRval, VarNameRval, SLI0, SLI) :- ( LiveValueType = var(Var, Name, _, _) -> term__var_to_int(Var, VarNum0), % The variable number has to fit into two bytes. % We reserve the largest such number (Limit) % to mean that the variable number is too large % to be represented. This ought not to happen, % since compilation would be glacial at best % for procedures with that many variables. Limit = (1 << (2 * stack_layout__byte_bits)) - 1, int__min(VarNum0, Limit, VarNum), VarNumRval = const(int_const(VarNum)), stack_layout__lookup_string_in_table(Name, Offset, SLI0, SLI), VarNameRval = const(int_const(Offset)) ; VarNumRval = const(int_const(0)), VarNameRval = const(int_const(0)), SLI = SLI0 ). %---------------------------------------------------------------------------% % The representation we build here should be kept in sync % with runtime/mercury_ho_call.h, which contains macros to access % the data structures we build here. stack_layout__construct_closure_layout(ProcLabel, ClosureLayoutInfo, Rvals, ArgTypes, CNum0, CNum) :- stack_layout__construct_procid_rvals(ProcLabel, ProcIdRvals, ProcIdTypes), ClosureLayoutInfo = closure_layout_info(ClosureArgs, TVarLocnMap), stack_layout__construct_closure_arg_rvals(ClosureArgs, ClosureArgRvals, ClosureArgTypes, CNum0, CNum1), stack_layout__construct_tvar_vector(TVarLocnMap, TVarVectorRval, CNum1, CNum), TVarVectorRvals = [yes(TVarVectorRval)], TVarVectorTypes = [1 - yes(data_ptr)], list__append(TVarVectorRvals, ClosureArgRvals, LayoutRvals), list__append(ProcIdRvals, LayoutRvals, Rvals), ArgTypes = initial(ProcIdTypes, initial(TVarVectorTypes, initial(ClosureArgTypes, none))). :- pred stack_layout__construct_closure_arg_rvals(list(closure_arg_info)::in, list(maybe(rval))::out, initial_arg_types::out, int::in, int::out) is det. stack_layout__construct_closure_arg_rvals(ClosureArgs, ClosureArgRvals, ClosureArgTypes, CNum0, CNum) :- list__map_foldl(stack_layout__construct_closure_arg_rval, ClosureArgs, MaybeArgRvalsTypes, CNum0, CNum), assoc_list__keys(MaybeArgRvalsTypes, MaybeArgRvals), AddOne = lambda([Pair::in, CountLldsType::out] is det, ( Pair = _ - LldsType, CountLldsType = 1 - yes(LldsType) )), list__map(AddOne, MaybeArgRvalsTypes, ArgRvalTypes), list__length(MaybeArgRvals, Length), ClosureArgRvals = [yes(const(int_const(Length))) | MaybeArgRvals], ClosureArgTypes = [1 - yes(integer) | ArgRvalTypes]. :- pred stack_layout__construct_closure_arg_rval(closure_arg_info::in, pair(maybe(rval), llds_type)::out, int::in, int::out) is det. stack_layout__construct_closure_arg_rval(ClosureArg, yes(ArgRval) - ArgRvalType, CNum0, CNum) :- ClosureArg = closure_arg_info(Type, _Inst), % For a stack layout, we can treat all type variables as % universally quantified. This is not the argument of a % constructor, so we do not need to distinguish between type % variables that are and aren't in scope; we can take the % variable number directly from the procedure's tvar set. ExistQTvars = [], base_type_layout__max_varint(Max), NumUnivQTvars = Max - 1, base_type_layout__construct_typed_pseudo_type_info(Type, NumUnivQTvars, ExistQTvars, ArgRval, ArgRvalType, CNum0, CNum). %---------------------------------------------------------------------------% % Construct a representation of the type of a value. % % For values representing variables, this will be a pseudo_type_info % describing the type of the variable. % % For the kinds of values used internally by the compiler, % this will be a pointer to a specific type_ctor_info (acting as a % type_info) defined by hand in builtin.m to stand for values of % each such kind; one for succips, one for hps, etc. :- pred stack_layout__represent_live_value_type(live_value_type, rval, llds_type, stack_layout_info, stack_layout_info). :- mode stack_layout__represent_live_value_type(in, out, out, in, out) is det. stack_layout__represent_live_value_type(succip, Rval, data_ptr) --> { TypeCtor = type_ctor(info, "succip", 0) }, { AddrConst = data_addr_const(data_addr(unqualified(""), TypeCtor)) }, { Rval = const(AddrConst) }. stack_layout__represent_live_value_type(hp, Rval, data_ptr) --> { TypeCtor = type_ctor(info, "hp", 0) }, { AddrConst = data_addr_const(data_addr(unqualified(""), TypeCtor)) }, { Rval = const(AddrConst) }. stack_layout__represent_live_value_type(curfr, Rval, data_ptr) --> { TypeCtor = type_ctor(info, "curfr", 0) }, { AddrConst = data_addr_const(data_addr(unqualified(""), TypeCtor)) }, { Rval = const(AddrConst) }. stack_layout__represent_live_value_type(maxfr, Rval, data_ptr) --> { TypeCtor = type_ctor(info, "maxfr", 0) }, { AddrConst = data_addr_const(data_addr(unqualified(""), TypeCtor)) }, { Rval = const(AddrConst) }. stack_layout__represent_live_value_type(redofr, Rval, data_ptr) --> { TypeCtor = type_ctor(info, "redofr", 0) }, { AddrConst = data_addr_const(data_addr(unqualified(""), TypeCtor)) }, { Rval = const(AddrConst) }. stack_layout__represent_live_value_type(redoip, Rval, data_ptr) --> { TypeCtor = type_ctor(info, "redoip", 0) }, { AddrConst = data_addr_const(data_addr(unqualified(""), TypeCtor)) }, { Rval = const(AddrConst) }. stack_layout__represent_live_value_type(trail_ptr, Rval, data_ptr) --> { TypeCtor = type_ctor(info, "trail_ptr", 0) }, { AddrConst = data_addr_const(data_addr(unqualified(""), TypeCtor)) }, { Rval = const(AddrConst) }. stack_layout__represent_live_value_type(ticket, Rval, data_ptr) --> { TypeCtor = type_ctor(info, "ticket", 0) }, { AddrConst = data_addr_const(data_addr(unqualified(""), TypeCtor)) }, { Rval = const(AddrConst) }. stack_layout__represent_live_value_type(unwanted, Rval, data_ptr) --> { TypeCtor = type_ctor(info, "succip", 0) }, { AddrConst = data_addr_const(data_addr(unqualified(""), TypeCtor)) }, { Rval = const(AddrConst) }. stack_layout__represent_live_value_type(var(_, _, Type, _), Rval, LldsType) --> stack_layout__get_cell_number(CNum0), % For a stack layout, we can treat all type variables as % universally quantified. This is not the argument of a % constructor, so we do not need to distinguish between type % variables that are and aren't in scope; we can take the % variable number directly from the procedure's tvar set. { ExistQTvars = [] }, { base_type_layout__max_varint(Max) }, { NumUnivQTvars = Max - 1 }, { base_type_layout__construct_typed_pseudo_type_info(Type, NumUnivQTvars, ExistQTvars, Rval, LldsType, CNum0, CNum) }, stack_layout__set_cell_number(CNum). %---------------------------------------------------------------------------% % Construct a representation of a variable location as a 32-bit % integer. % % Most of the time, a layout specifies a location as an lval. % However, a type_info variable may be hidden inside a typeclass_info, % In this case, accessing the type_info requires indirection. % The address of the typeclass_info is given as an lval, and % the location of the typeinfo within the typeclass_info as an index; % private_builtin:type_info_from_typeclass_info interprets the index. % % This one level of indirection is sufficient, since type_infos % cannot be nested inside typeclass_infos any deeper than this. % A more general representation that would allow more indirection % would be much harder to fit into one machine word. :- pred stack_layout__represent_locn_as_int(layout_locn, rval). :- mode stack_layout__represent_locn_as_int(in, out) is det. stack_layout__represent_locn_as_int(direct(Lval), Rval) :- stack_layout__represent_lval(Lval, Word), Rval = const(int_const(Word)). stack_layout__represent_locn_as_int(indirect(Lval, Offset), Rval) :- stack_layout__represent_lval(Lval, BaseWord), require((1 << stack_layout__long_lval_offset_bits) > Offset, "stack_layout__represent_locn: offset too large to be represented"), BaseAndOffset is (BaseWord << stack_layout__long_lval_offset_bits) + Offset, stack_layout__make_tagged_word(lval_indirect, BaseAndOffset, Word), Rval = const(int_const(Word)). % Construct a four byte representation of an lval. :- pred stack_layout__represent_lval(lval, int). :- mode stack_layout__represent_lval(in, out) is det. stack_layout__represent_lval(reg(r, Num), Word) :- stack_layout__make_tagged_word(lval_r_reg, Num, Word). stack_layout__represent_lval(reg(f, Num), Word) :- stack_layout__make_tagged_word(lval_f_reg, Num, Word). stack_layout__represent_lval(stackvar(Num), Word) :- stack_layout__make_tagged_word(lval_stackvar, Num, Word). stack_layout__represent_lval(framevar(Num), Word) :- stack_layout__make_tagged_word(lval_framevar, Num, Word). stack_layout__represent_lval(succip, Word) :- stack_layout__make_tagged_word(lval_succip, 0, Word). stack_layout__represent_lval(maxfr, Word) :- stack_layout__make_tagged_word(lval_maxfr, 0, Word). stack_layout__represent_lval(curfr, Word) :- stack_layout__make_tagged_word(lval_curfr, 0, Word). stack_layout__represent_lval(hp, Word) :- stack_layout__make_tagged_word(lval_hp, 0, Word). stack_layout__represent_lval(sp, Word) :- stack_layout__make_tagged_word(lval_sp, 0, Word). stack_layout__represent_lval(temp(_, _), _) :- error("stack_layout: continuation live value stored in temp register"). stack_layout__represent_lval(succip(_), _) :- error("stack_layout: continuation live value stored in fixed slot"). stack_layout__represent_lval(redoip(_), _) :- error("stack_layout: continuation live value stored in fixed slot"). stack_layout__represent_lval(redofr(_), _) :- error("stack_layout: continuation live value stored in fixed slot"). stack_layout__represent_lval(succfr(_), _) :- error("stack_layout: continuation live value stored in fixed slot"). stack_layout__represent_lval(prevfr(_), _) :- error("stack_layout: continuation live value stored in fixed slot"). stack_layout__represent_lval(field(_, _, _), _) :- error("stack_layout: continuation live value stored in field"). stack_layout__represent_lval(mem_ref(_), _) :- error("stack_layout: continuation live value stored in mem_ref"). stack_layout__represent_lval(lvar(_), _) :- error("stack_layout: continuation live value stored in lvar"). % Some things in this module are encoded using a low tag. % This is not done using the normal compiler mkword, but by % doing the bit shifting here. % % This allows us to use more than the usual 2 or 3 bits, but % we have to use low tags and cannot tag pointers this way. :- pred stack_layout__make_tagged_word(locn_type::in, int::in, int::out) is det. stack_layout__make_tagged_word(Locn, Value, TaggedValue) :- stack_layout__locn_type_code(Locn, Tag), TaggedValue is (Value << stack_layout__long_lval_tag_bits) + Tag. :- type locn_type ---> lval_r_reg ; lval_f_reg ; lval_stackvar ; lval_framevar ; lval_succip ; lval_maxfr ; lval_curfr ; lval_hp ; lval_sp ; lval_indirect. :- pred stack_layout__locn_type_code(locn_type::in, int::out) is det. stack_layout__locn_type_code(lval_r_reg, 0). stack_layout__locn_type_code(lval_f_reg, 1). stack_layout__locn_type_code(lval_stackvar, 2). stack_layout__locn_type_code(lval_framevar, 3). stack_layout__locn_type_code(lval_succip, 4). stack_layout__locn_type_code(lval_maxfr, 5). stack_layout__locn_type_code(lval_curfr, 6). stack_layout__locn_type_code(lval_hp, 7). stack_layout__locn_type_code(lval_sp, 8). stack_layout__locn_type_code(lval_indirect, 9). :- func stack_layout__long_lval_tag_bits = int. % This number of tag bits must be able to encode all values of % stack_layout__locn_type_code. stack_layout__long_lval_tag_bits = 4. % This number of tag bits must be able to encode the largest offset % of a type_info within a typeclass_info. :- func stack_layout__long_lval_offset_bits = int. stack_layout__long_lval_offset_bits = 6. %---------------------------------------------------------------------------% % Construct a representation of a variable location as a byte, % if this is possible. :- pred stack_layout__represent_locn_as_byte(layout_locn::in, rval::out) is semidet. stack_layout__represent_locn_as_byte(LayoutLocn, Rval) :- LayoutLocn = direct(Lval), stack_layout__represent_lval_as_byte(Lval, Byte), Rval = const(int_const(Byte)). % Construct a representation of an lval in a byte, if possible. :- pred stack_layout__represent_lval_as_byte(lval::in, int::out) is semidet. stack_layout__represent_lval_as_byte(reg(r, Num), Byte) :- stack_layout__make_tagged_byte(0, Num, Byte). stack_layout__represent_lval_as_byte(stackvar(Num), Byte) :- stack_layout__make_tagged_byte(1, Num, Byte). stack_layout__represent_lval_as_byte(framevar(Num), Byte) :- stack_layout__make_tagged_byte(2, Num, Byte). stack_layout__represent_lval_as_byte(succip, Byte) :- stack_layout__locn_type_code(lval_succip, Val), stack_layout__make_tagged_byte(3, Val, Byte). stack_layout__represent_lval_as_byte(maxfr, Byte) :- stack_layout__locn_type_code(lval_maxfr, Val), stack_layout__make_tagged_byte(3, Val, Byte). stack_layout__represent_lval_as_byte(curfr, Byte) :- stack_layout__locn_type_code(lval_curfr, Val), stack_layout__make_tagged_byte(3, Val, Byte). stack_layout__represent_lval_as_byte(hp, Byte) :- stack_layout__locn_type_code(lval_hp, Val), stack_layout__make_tagged_byte(3, Val, Byte). stack_layout__represent_lval_as_byte(sp, Byte) :- stack_layout__locn_type_code(lval_succip, Val), stack_layout__make_tagged_byte(3, Val, Byte). :- pred stack_layout__make_tagged_byte(int::in, int::in, int::out) is semidet. stack_layout__make_tagged_byte(Tag, Value, TaggedValue) :- Limit = 1 << (stack_layout__byte_bits - stack_layout__short_lval_tag_bits), Value < Limit, TaggedValue is unchecked_left_shift(Value, stack_layout__short_lval_tag_bits) + Tag. :- func stack_layout__short_lval_tag_bits = int. stack_layout__short_lval_tag_bits = 2. :- func stack_layout__short_count_bits = int. stack_layout__short_count_bits = 10. :- func stack_layout__byte_bits = int. stack_layout__byte_bits = 8. %---------------------------------------------------------------------------% % Construct a representation of the interface determinism of a % procedure. The code we have chosen is not sequential; instead % it encodes the various properties of each determinism. % % The 8 bit is set iff the context is first_solution. % The 4 bit is set iff the min number of solutions is more than zero. % The 2 bit is set iff the max number of solutions is more than zero. % The 1 bit is set iff the max number of solutions is more than one. :- pred stack_layout__represent_determinism(determinism::in, rval::out) is det. stack_layout__represent_determinism(Detism, const(int_const(Code))) :- ( Detism = det, Code = 6 /* 0110 */ ; Detism = semidet, /* 0010 */ Code = 2 ; Detism = nondet, Code = 3 /* 0011 */ ; Detism = multidet, Code = 7 /* 0111 */ ; Detism = erroneous, Code = 4 /* 0100 */ ; Detism = failure, Code = 0 /* 0000 */ ; Detism = cc_nondet, Code = 10 /* 1010 */ ; Detism = cc_multidet, Code = 14 /* 1110 */ ). %---------------------------------------------------------------------------% % Access to the stack_layout data structure. % The per-sourcefile label table maps line numbers to the list of % labels that correspond to that line. Each label is accompanied % by a flag that says whether the label is the return site of a call % or not, and if it is, whether the called procedure is known. :- type is_label_return ---> known_callee(label) ; unknown_callee ; not_a_return. :- type line_no_info == pair(label, is_label_return). :- type label_table == map(int, list(line_no_info)). :- type stack_layout_info ---> stack_layout_info( module_name, % module name int, % next available cell number bool, % generate agc layout info? bool, % generate tracing layout info? bool, % generate procedure id layout info? bool, % have static code addresses? list(comp_gen_c_data), % generated proc layouts list(comp_gen_c_data), % generated internal layouts set_bbbtree(label), % the set of labels (both entry and internal) % with layouts list(maybe(rval)), % the list of proc_layouts in the module, % represented as create args string_table, map(string, label_table) % maps each filename that contributes labels % to this module to a table describing those % labels. ). :- pred stack_layout__get_module_name(module_name::out, stack_layout_info::in, stack_layout_info::out) is det. :- pred stack_layout__get_cell_number(int::out, stack_layout_info::in, stack_layout_info::out) is det. :- pred stack_layout__get_agc_stack_layout(bool::out, stack_layout_info::in, stack_layout_info::out) is det. :- pred stack_layout__get_trace_stack_layout(bool::out, stack_layout_info::in, stack_layout_info::out) is det. :- pred stack_layout__get_procid_stack_layout(bool::out, stack_layout_info::in, stack_layout_info::out) is det. :- pred stack_layout__get_static_code_addresses(bool::out, stack_layout_info::in, stack_layout_info::out) is det. :- pred stack_layout__get_proc_layout_data(list(comp_gen_c_data)::out, stack_layout_info::in, stack_layout_info::out) is det. :- pred stack_layout__get_internal_layout_data(list(comp_gen_c_data)::out, stack_layout_info::in, stack_layout_info::out) is det. :- pred stack_layout__get_label_set(set_bbbtree(label)::out, stack_layout_info::in, stack_layout_info::out) is det. :- pred stack_layout__get_string_table(string_table::out, stack_layout_info::in, stack_layout_info::out) is det. :- pred stack_layout__get_label_tables(map(string, label_table)::out, stack_layout_info::in, stack_layout_info::out) is det. stack_layout__get_module_name(A, LayoutInfo, LayoutInfo) :- LayoutInfo = stack_layout_info(A, _, _, _, _, _, _, _, _, _, _, _). stack_layout__get_cell_number(B, LayoutInfo, LayoutInfo) :- LayoutInfo = stack_layout_info(_, B, _, _, _, _, _, _, _, _, _, _). stack_layout__get_agc_stack_layout(C, LayoutInfo, LayoutInfo) :- LayoutInfo = stack_layout_info(_, _, C, _, _, _, _, _, _, _, _, _). stack_layout__get_trace_stack_layout(D, LayoutInfo, LayoutInfo) :- LayoutInfo = stack_layout_info(_, _, _, D, _, _, _, _, _, _, _, _). stack_layout__get_procid_stack_layout(E, LayoutInfo, LayoutInfo) :- LayoutInfo = stack_layout_info(_, _, _, _, E, _, _, _, _, _, _, _). stack_layout__get_static_code_addresses(F, LayoutInfo, LayoutInfo) :- LayoutInfo = stack_layout_info(_, _, _, _, _, F, _, _, _, _, _, _). stack_layout__get_proc_layout_data(G, LayoutInfo, LayoutInfo) :- LayoutInfo = stack_layout_info(_, _, _, _, _, _, G, _, _, _, _, _). stack_layout__get_internal_layout_data(H, LayoutInfo, LayoutInfo) :- LayoutInfo = stack_layout_info(_, _, _, _, _, _, _, H, _, _, _, _). stack_layout__get_label_set(I, LayoutInfo, LayoutInfo) :- LayoutInfo = stack_layout_info(_, _, _, _, _, _, _, _, I, _, _, _). stack_layout__get_string_table(K, LayoutInfo, LayoutInfo) :- LayoutInfo = stack_layout_info(_, _, _, _, _, _, _, _, _, _, K, _). stack_layout__get_label_tables(L, LayoutInfo, LayoutInfo) :- LayoutInfo = stack_layout_info(_, _, _, _, _, _, _, _, _, _, _, L). :- pred stack_layout__add_proc_layout_data(comp_gen_c_data::in, data_name::in, label::in, stack_layout_info::in, stack_layout_info::out) is det. stack_layout__add_proc_layout_data(NewG, NewJ, NewI, LayoutInfo0, LayoutInfo) :- LayoutInfo0 = stack_layout_info(A, B, C, D, E, F, G0, H, I0, J0, K, L), G = [NewG | G0], set_bbbtree__insert(I0, NewI, I), J = [yes(const(data_addr_const(data_addr(A, NewJ)))) | J0], LayoutInfo = stack_layout_info(A, B, C, D, E, F, G , H, I , J , K, L). :- pred stack_layout__add_internal_layout_data(comp_gen_c_data::in, label::in, stack_layout_info::in, stack_layout_info::out) is det. stack_layout__add_internal_layout_data(NewH, NewI, LayoutInfo0, LayoutInfo) :- LayoutInfo0 = stack_layout_info(A, B, C, D, E, F, G, H0, I0, J, K, L), H = [NewH | H0], set_bbbtree__insert(I0, NewI, I), LayoutInfo = stack_layout_info(A, B, C, D, E, F, G, H , I , J, K, L). :- pred stack_layout__get_next_cell_number(int::out, stack_layout_info::in, stack_layout_info::out) is det. stack_layout__get_next_cell_number(B, LayoutInfo0, LayoutInfo) :- LayoutInfo0 = stack_layout_info(A, B0, C, D, E, F, G, H, I, J, K, L), B is B0 + 1, LayoutInfo = stack_layout_info(A, B, C, D, E, F, G, H, I, J, K, L). :- pred stack_layout__set_cell_number(int::in, stack_layout_info::in, stack_layout_info::out) is det. stack_layout__set_cell_number(B, LayoutInfo0, LayoutInfo) :- LayoutInfo0 = stack_layout_info(A, _, C, D, E, F, G, H, I, J, K, L), LayoutInfo = stack_layout_info(A, B, C, D, E, F, G, H, I, J, K, L). :- pred stack_layout__set_string_table(string_table::in, stack_layout_info::in, stack_layout_info::out) is det. stack_layout__set_string_table(K, LayoutInfo0, LayoutInfo) :- LayoutInfo0 = stack_layout_info(A, B, C, D, E, F, G, H, I, J, _, L), LayoutInfo = stack_layout_info(A, B, C, D, E, F, G, H, I, J, K, L). :- pred stack_layout__set_label_tables(map(string, label_table)::in, stack_layout_info::in, stack_layout_info::out) is det. stack_layout__set_label_tables(L, LayoutInfo0, LayoutInfo) :- LayoutInfo0 = stack_layout_info(A, B, C, D, E, F, G, H, I, J, K, _), LayoutInfo = stack_layout_info(A, B, C, D, E, F, G, H, I, J, K, L). %---------------------------------------------------------------------------% % Access to the string_table data structure. :- type string_table ---> string_table( map(string, int), % Maps strings to their offsets. list(string), % List of strings so far, % in reverse order. int % Next available offset ). :- pred stack_layout__lookup_string_in_table(string::in, int::out, stack_layout_info::in, stack_layout_info::out) is det. stack_layout__lookup_string_in_table(String, Offset) --> stack_layout__get_string_table(StringTable0), { StringTable0 = string_table(TableMap0, TableList0, TableOffset0) }, ( { map__search(TableMap0, String, OldOffset) } -> { Offset = OldOffset } ; { string__length(String, Length) }, { TableOffset is TableOffset0 + Length + 1 }, { TableOffset < (1 << (2 * stack_layout__byte_bits)) } -> { Offset = TableOffset0 }, { map__det_insert(TableMap0, String, TableOffset0, TableMap) }, { TableList = [String | TableList0] }, { StringTable = string_table(TableMap, TableList, TableOffset) }, stack_layout__set_string_table(StringTable) ; % Says that the name of the variable is "TOO_MANY_VARIABLES". { Offset = 1 } ).