Files
mercury/compiler/proc_gen.m
Zoltan Somogyi eccd0bfab4 Implement mutual tail call optimization for the MLDS.
It does not (yet) optimize all the calls that the LLDS backend optimizes,
with the most significant limitation being that it handles only calls to
procedures that return all their arguments by reference. This rules out
det functions (thought it includes *semidet* functions), as well as the
backends that use copy-out. Lifting those limitations is future work.

compiler/ml_proc_gen.m:
    Make the change described above.

compiler/mlds.m:
    Add two new kinds of compiler-generated local variables to represent
    the input and output arguments of procedures in TSCCs respectively.
    They are key to allowing the code generation scheme used by ml_proc_gen.m
    to work *without* having to rename apart either sets of HLDS variables
    or set of MLDS variables. (Both those renames would be a lot of work.)

    Add a new kind of compiler-generated local variable to represent
    the identity of the tail-called procedure if the code generator is asked
    not to use labels and gotos.

compiler/ml_args_util.m:
    Provide a predicate that ml_proc_gen.m uses to generate argument handling
    code for mutually recursive procedures.

compiler/ml_gen_info.m:
    Generalize the existing support for self-tail-recursive calls
    to also handle mutually-tail-recursive calls.

compiler/ml_call_gen.m:
    Use the new generalized support to generate code for
    mutually-tail-recursive calls as well as for self-tail-recursive calls.
    For each call to a procedure that the ml_gen_info records as a potential
    target for a tail recursive call (i.e. for each procedure in the TSCC
    we are generating code for), record what kinds of calls we *do* generate
    to it, for use by ml_proc_gen.m.

compiler/ml_code_util.m:
    Add a slightly different form of an existing utility function,
    to allow new code in ml_proc_gen.m to use it without doing redundant work.

    Expand out a function's definition to allow mdb breakpoints on its
    components.

compiler/options.m:
    Provide developer-only options to control aspects of how ml_proc_gen.m
    handles tail call optimization.

compiler/mlds_to_c.m:
compiler/mlds_to_cs.m:
compiler/mlds_to_java.m:
    Provide a way for the MLDS to specify the printing of a blank line:
    a comment containing an empty string. Some code in ml_proc_gen.m uses this
    to make the comments it generates look nicer.

compiler/hlds_desc.m:
    Add two utility functions that we now use to include descriptions
    of TSCCs in the generated MLDS code, to help debug that code.

compiler/proc_gen.m:
    Delete code that can now be replaced by calling one of the new utility
    functions in hlds_desc.m. (The deleted code was used as the original
    template for the code of that function.)

    Delete an unused type.

compiler/rtti_to_mlds.m:
    Conform to the changes in ml_gen_info.m.
2017-08-31 14:38:45 +10:00

1388 lines
56 KiB
Mathematica

%---------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%---------------------------------------------------------------------------%
% Copyright (C) 1994-2012 The University of Melbourne.
% This file may only be copied under the terms of the GNU General
% Public License - see the file COPYING in the Mercury distribution.
%---------------------------------------------------------------------------%
%
% File: code_gen.m.
% Main authors: conway, zs.
%
% Code generation - convert from HLDS to LLDS.
%
% The two main tasks of this module are
%
% 1 to look after the aspects of generating code for a procedure
% that do not involve generating code for a specific goal, and
%
% 2 to provide a generic predicate that can be called from anywhere in
% the code generator to generate code for a goal.
%
% Code_gen forwards most of the actual construction of code for particular
% goals to other modules. The generation of code for unifications is done
% by unify_gen, for calls, higher-order calls and method calls by call_gen,
% for commits by commit_gen, for if-then-elses and negations by ite_gen,
% for switches by switch_gen and its subsidiary modules, for disjunctions
% by disj_gen, and for pragma_c_codes by pragma_c_gen. The only kind of goal
% handled directly by code_gen is the conjunction.
%
%---------------------------------------------------------------------------%
:- module ll_backend.proc_gen.
:- interface.
:- import_module hlds.
:- import_module hlds.hlds_module.
:- import_module hlds.hlds_pred.
:- import_module ll_backend.global_data.
:- import_module ll_backend.llds.
:- import_module list.
%---------------------------------------------------------------------------%
% Translate a HLDS module to LLDS.
%
:- pred generate_module_code(module_info::in, list(c_procedure)::out,
global_data::in, global_data::out) is det.
% Translate a HLDS procedure to LLDS, threading through the data structure
% that records information about layout structures.
%
:- pred generate_proc_code(module_info::in, const_struct_map::in,
pred_id::in, pred_info::in, proc_id::in, proc_info::in,
c_procedure::out, global_data::in, global_data::out) is det.
% Return the message that identifies the procedure to pass to
% the incr_sp_push_msg macro in the generated C code.
%
:- func push_msg(module_info, pred_id, proc_id) = string.
% Add all the global variables required for tabling by the procedures
% of the module.
%
:- pred add_all_tabling_info_structs(module_info::in,
global_data::in, global_data::out) is det.
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
:- implementation.
:- import_module backend_libs.
:- import_module backend_libs.builtin_ops.
:- import_module backend_libs.proc_label.
:- import_module hlds.code_model.
:- import_module hlds.goal_form.
:- import_module hlds.goal_path.
:- import_module hlds.goal_util.
:- import_module hlds.hlds_clauses.
:- import_module hlds.hlds_desc.
:- import_module hlds.hlds_goal.
:- import_module hlds.hlds_llds.
:- import_module hlds.hlds_out.
:- import_module hlds.hlds_out.hlds_out_util.
:- import_module hlds.hlds_rtti.
:- import_module hlds.instmap.
:- import_module libs.
:- import_module libs.file_util.
:- import_module libs.globals.
:- import_module libs.options.
:- import_module libs.trace_params.
:- import_module ll_backend.code_gen.
:- import_module ll_backend.code_info.
:- import_module ll_backend.code_loc_dep.
:- import_module ll_backend.code_util.
:- import_module ll_backend.continuation_info.
:- import_module ll_backend.layout.
:- import_module ll_backend.middle_rec.
:- import_module ll_backend.stack_layout.
:- import_module ll_backend.trace_gen.
:- import_module ll_backend.unify_gen.
:- import_module mdbcomp.
:- import_module mdbcomp.prim_data.
:- import_module mdbcomp.program_representation.
:- import_module mdbcomp.sym_name.
:- import_module parse_tree.
:- import_module parse_tree.prog_data.
:- import_module parse_tree.prog_data_foreign.
:- import_module parse_tree.prog_data_pragma.
:- import_module parse_tree.set_of_var.
:- import_module assoc_list.
:- import_module bool.
:- import_module cord.
:- import_module counter.
:- import_module int.
:- import_module io.
:- import_module map.
:- import_module maybe.
:- import_module pair.
:- import_module require.
:- import_module set.
:- import_module string.
:- import_module term.
%---------------------------------------------------------------------------%
generate_module_code(ModuleInfo, CProcs, !GlobalData) :-
% Get a list of all the predicate ids for which we will generate code.
module_info_get_valid_pred_ids(ModuleInfo, PredIds),
% Check if we want to use parallel code generation.
module_info_get_globals(ModuleInfo, Globals),
globals.lookup_bool_option(Globals, parallel_code_gen, ParallelCodeGen),
globals.lookup_bool_option(Globals, very_verbose, VeryVerbose),
globals.lookup_bool_option(Globals, detailed_statistics, Statistics),
(
VeryVerbose = yes,
trace [io(!IO)] (
io.write_string("% Generating constant structures\n", !IO)
),
generate_const_structs(ModuleInfo, ConstStructMap, !GlobalData),
trace [io(!IO)] (
maybe_report_stats(Statistics, !IO)
)
;
VeryVerbose = no,
generate_const_structs(ModuleInfo, ConstStructMap, !GlobalData)
),
( if
ParallelCodeGen = yes,
% Can't do parallel code generation if I/O is required.
VeryVerbose = no,
Statistics = no
then
generate_module_code_par(ModuleInfo, ConstStructMap, PredIds,
CProcsCord, !GlobalData)
else
generate_module_code_seq(ModuleInfo, VeryVerbose, Statistics,
ConstStructMap, PredIds, CProcsCord, !GlobalData)
),
CProcs = cord.list(CProcsCord).
:- pred generate_module_code_seq(module_info::in, bool::in, bool::in,
const_struct_map::in, list(pred_id)::in, cord(c_procedure)::out,
global_data::in, global_data::out) is det.
generate_module_code_seq(ModuleInfo, VeryVerbose, Statistics, ConstStructMap,
PredIds, CProcsCord, !GlobalData) :-
list.foldl2(
generate_pred_code_seq(ModuleInfo, VeryVerbose, Statistics,
ConstStructMap),
PredIds, cord.init, CProcsCord, !GlobalData).
%-----------------------------------------------------------------------------%
:- pred generate_module_code_par(module_info::in, const_struct_map::in,
list(pred_id)::in, cord(c_procedure)::out,
global_data::in, global_data::out) is det.
generate_module_code_par(ModuleInfo, ConstStructMap, PredIds, CProcsCord,
!GlobalData) :-
% Split up the list of predicates into pieces for processing in parallel.
% Splitting the list in the middle does not work well as the load will be
% unbalanced. Splitting the list in any other way (as we do) does mean
% that the generated code will be slightly different due to the static
% data being reordered.
%
% We only try to make use of two processors (threads) for now. Using more
% processors efficiently probably requires knowing how many processors are
% available, so we can divide the pred list whilst minimise the time
% merging global_datas and updating static cell references.
%
list.chunk(PredIds, pred_list_chunk_size, ListsOfPredIds),
interleave(ListsOfPredIds, ListsOfPredIdsA, ListsOfPredIdsB),
GlobalData0 = !.GlobalData,
(
list.condense(ListsOfPredIdsA, PredIdsA),
list.foldl2(generate_pred_code_par(ModuleInfo, ConstStructMap),
PredIdsA, cord.init, CProcsCordA, GlobalData0, GlobalDataA)
% XXX the following should be a parallel conjunction
,
list.condense(ListsOfPredIdsB, PredIdsB),
bump_type_num_counter(type_num_skip, GlobalData0, GlobalData1),
list.foldl2(generate_pred_code_par(ModuleInfo, ConstStructMap),
PredIdsB, cord.init, CProcsCordB0, GlobalData1, GlobalDataB)
),
merge_global_datas(GlobalDataA, GlobalDataB, !:GlobalData, Remap),
cord.map_pred(remap_references_to_global_data(Remap),
CProcsCordB0, CProcsCordB),
CProcsCord = CProcsCordA ++ CProcsCordB.
% These numbers are rather arbitrary.
%
:- func pred_list_chunk_size = int.
pred_list_chunk_size = 50.
:- func type_num_skip = int.
type_num_skip = 10000.
:- pred interleave(list(T)::in, list(T)::out, list(T)::out) is det.
:- pred interleave_2(list(T)::in, list(T)::in, list(T)::out,
list(T)::in, list(T)::out) is det.
interleave(L, reverse(As), reverse(Bs)) :-
interleave_2(L, [], As, [], Bs).
interleave_2([], !As, !Bs).
interleave_2([H | T], As0, As, Bs0, Bs) :-
interleave_2(T, Bs0, Bs, [H | As0], As).
%-----------------------------------------------------------------------------%
:- pred generate_pred_code_seq(module_info::in, bool::in, bool::in,
const_struct_map::in, pred_id::in,
cord(c_procedure)::in, cord(c_procedure)::out,
global_data::in, global_data::out) is det.
generate_pred_code_seq(ModuleInfo, VeryVerbose, Statistics, ConstStructMap,
PredId, !CProcsCord, !GlobalData) :-
% Note that some of the logic of generate_maybe_pred_code is duplicated
% by mercury_compile.backend_pass_by_preds, so modifications here may
% also need to be repeated there.
module_info_get_preds(ModuleInfo, PredInfos),
map.lookup(PredInfos, PredId, PredInfo),
ProcIds = pred_info_non_imported_procids(PredInfo),
(
ProcIds = []
;
ProcIds = [_ | _],
(
VeryVerbose = yes,
trace [io(!IO)] (
io.write_string("% Generating code for ", !IO),
write_pred_id(ModuleInfo, PredId, !IO),
io.write_string("\n", !IO)
),
generate_pred_code(ModuleInfo, ConstStructMap, PredId, PredInfo,
ProcIds, !CProcsCord, !GlobalData),
trace [io(!IO)] (
maybe_report_stats(Statistics, !IO)
)
;
VeryVerbose = no,
generate_pred_code(ModuleInfo, ConstStructMap, PredId, PredInfo,
ProcIds, !CProcsCord, !GlobalData)
)
).
:- pred generate_pred_code_par(module_info::in, const_struct_map::in,
pred_id::in, cord(c_procedure)::in, cord(c_procedure)::out,
global_data::in, global_data::out) is det.
generate_pred_code_par(ModuleInfo, ConstStructMap, PredId,
!CProcsCord, !GlobalData) :-
module_info_get_preds(ModuleInfo, PredInfos),
map.lookup(PredInfos, PredId, PredInfo),
ProcIds = pred_info_non_imported_procids(PredInfo),
generate_pred_code(ModuleInfo, ConstStructMap, PredId, PredInfo,
ProcIds, !CProcsCord, !GlobalData).
% Translate a HLDS predicate to LLDS.
%
:- pred generate_pred_code(module_info::in, const_struct_map::in,
pred_id::in, pred_info::in, list(proc_id)::in,
cord(c_procedure)::in, cord(c_procedure)::out,
global_data::in, global_data::out) is det.
generate_pred_code(ModuleInfo, ConstStructMap, PredId, PredInfo, ProcIds,
!CProcsCord, !GlobalData) :-
generate_proc_list_code(ModuleInfo, ConstStructMap, PredId, PredInfo,
ProcIds, !CProcsCord, !GlobalData).
% Translate all the procedures of a HLDS predicate to LLDS.
%
:- pred generate_proc_list_code(module_info::in, const_struct_map::in,
pred_id::in, pred_info::in, list(proc_id)::in,
cord(c_procedure)::in, cord(c_procedure)::out,
global_data::in, global_data::out) is det.
generate_proc_list_code(_, _, _, _, [], !CProcsCord, !GlobalData).
generate_proc_list_code(ModuleInfo, ConstStructMap, PredId, PredInfo,
[ProcId | ProcIds], !CProcsCord, !GlobalData) :-
pred_info_get_proc_table(PredInfo, ProcInfos),
map.lookup(ProcInfos, ProcId, ProcInfo),
generate_proc_code(ModuleInfo, ConstStructMap, PredId, PredInfo,
ProcId, ProcInfo, CProc, !GlobalData),
!:CProcsCord = cord.snoc(!.CProcsCord, CProc),
generate_proc_list_code(ModuleInfo, ConstStructMap, PredId, PredInfo,
ProcIds, !CProcsCord, !GlobalData).
%---------------------------------------------------------------------------%
% A value of this type holds information about a procedure's stack frame.
% It is generated when generating a procedure's prologue, and is used both
% when generating the procedure's epilogue, and when massaging its code
% to add the slot containing the succip to the sets of lvals live across
% calls.
%
:- type proc_frame_slots
---> proc_frame_slots(
% Number of slots in frame.
int,
% Slot number of succip if succip is present in a general slot.
maybe(int)
).
%---------------------------------------------------------------------------%
generate_proc_code(ModuleInfo0, ConstStructMap, PredId, PredInfo,
ProcId, ProcInfo0, CProc, !GlobalData) :-
% The modified module_info and proc_info are both discarded
% on return from generate_proc_code.
maybe_set_trace_level(PredInfo, ModuleInfo0, ModuleInfo),
ensure_all_headvars_are_named(ProcInfo0, ProcInfo1),
module_info_get_globals(ModuleInfo, Globals),
globals.get_trace_level(Globals, TraceLevel),
proc_info_get_has_parallel_conj(ProcInfo1, HasParConj),
globals.lookup_bool_option(Globals, parallel, Parallel),
( if
% Make the containing goal map available if we need it.
% It is needed for execution tracing, and for parallel conjunctions.
( given_trace_level_is_none(TraceLevel) = no
; HasParConj = has_parallel_conj
)
then
(
HasParConj = has_parallel_conj,
% In sequential grades, any parallel conjunctions should have been
% converted to sequential conjunctions by parallel_to_plain_conj.m.
expect(unify(Parallel, yes), $module, $pred,
"found parallel conjunction in non-parallel grade")
;
HasParConj = has_no_parallel_conj
),
fill_goal_id_slots_in_proc(ModuleInfo, ContainingGoalMap,
ProcInfo1, ProcInfo),
MaybeContainingGoalMap = yes(ContainingGoalMap)
else
MaybeContainingGoalMap = no,
ProcInfo = ProcInfo1
),
% Find out the appropriate context for the predicate's interface events.
pred_info_get_clauses_info(PredInfo, ClausesInfo),
( if get_first_clause(ClausesInfo ^ cli_rep, FirstClause) then
ProcContext = FirstClause ^ clause_context
else
% This predicate must have been created by the compiler. In that case,
% the context of the body goal is the best we can do.
ProcContext = goal_info_get_context(GoalInfo)
),
proc_info_interface_determinism(ProcInfo, Detism),
CodeModel = proc_info_interface_code_model(ProcInfo),
proc_info_get_goal(ProcInfo, Goal),
Goal = hlds_goal(_, GoalInfo),
goal_info_get_follow_vars(GoalInfo, MaybeFollowVars),
(
MaybeFollowVars = yes(FollowVars)
;
MaybeFollowVars = no,
map.init(FollowVarsMap),
FollowVars = abs_follow_vars(FollowVarsMap, 1, 1)
),
basic_stack_layout_for_proc(PredInfo, Globals, BasicStackLayout,
ForceProcId),
SaveSuccip = BasicStackLayout,
% Initialise the code_info structure. Generate_category_code below will use
% the returned OutsideResumePoint as the entry to the code that handles
% the failure of the procedure, if such code is needed. It is never needed
% for model_det procedures, always needed for model_semi procedures, and
% needed for model_non procedures only if we are doing execution tracing.
global_data_get_static_cell_info(!.GlobalData, StaticCellInfo0),
global_data_get_threadscope_rev_string_table(!.GlobalData,
TSRevStringTable0, TSStringTableSize0),
code_info_init(ModuleInfo, PredId, ProcId, PredInfo, ProcInfo,
SaveSuccip, StaticCellInfo0, ConstStructMap, MaybeContainingGoalMap,
TSRevStringTable0, TSStringTableSize0, TraceSlotInfo, CodeInfo0),
code_loc_dep_init(FollowVars, OutsideResumePoint,
CodeInfo0, CodeInfo1, CodeLocDep0),
% Generate code for the procedure.
generate_category_code(CodeModel, ProcContext, Goal, OutsideResumePoint,
TraceSlotInfo, CodeTree0, MaybeTraceCallLabel, ProcFrameSlots,
CodeInfo1, CodeInfo, CodeLocDep0),
get_out_of_line_code(CodeInfo, OutOfLineCode),
CodeTree = CodeTree0 ++ OutOfLineCode,
get_max_regs_in_use_at_trace(CodeInfo, MaxTraceRegR, MaxTraceRegF),
get_static_cell_info(CodeInfo, StaticCellInfo),
global_data_set_static_cell_info(StaticCellInfo, !GlobalData),
get_threadscope_rev_string_table(CodeInfo,
TSRevStringTable, TSStringTableSize),
global_data_set_threadscope_rev_string_table(TSRevStringTable,
TSStringTableSize, !GlobalData),
get_created_temp_frame(CodeInfo, CreatedTempFrame),
get_proc_trace_events(CodeInfo, ProcTraceEvents),
% You can have user trace events even if the effective trace level is none.
( if
ProcTraceEvents = yes,
CreatedTempFrame = yes,
CodeModel \= model_non
then
% If tracing is enabled, the procedure lives on the det stack and the
% code created any temporary nondet stack frames, then we must have
% reserved a stack slot for storing the value of maxfr; if we didn't,
% a retry command in the debugger from a point in the middle of this
% procedure will do the wrong thing.
proc_info_get_needs_maxfr_slot(ProcInfo, NeedsMaxfrSlot),
expect(unify(NeedsMaxfrSlot, needs_maxfr_slot), $module, $pred,
"should have reserved a slot for maxfr, but didn't")
else
true
),
Instructions0 = cord.list(CodeTree),
ProcFrameSlots = proc_frame_slots(TotalSlots, MaybeSuccipSlot),
(
MaybeSuccipSlot = yes(SuccipSlot),
% The set of recorded live values at calls (for value numbering)
% and returns (for accurate gc and execution tracing) do not yet record
% the stack slot holding the succip, so add it to those sets.
add_saved_succip(Instructions0, SuccipSlot, Instructions)
;
MaybeSuccipSlot = no,
Instructions = Instructions0
),
proc_info_get_maybe_proc_table_io_info(ProcInfo, MaybeTableIOInfo),
( if
( BasicStackLayout = yes
; MaybeTableIOInfo = yes(_TableIODeclInfo)
)
then
% Create the procedure layout structure.
RttiProcLabel = make_rtti_proc_label(ModuleInfo, PredId, ProcId),
get_layout_info(CodeInfo, InternalMap),
EntryLabel = make_local_entry_label(ModuleInfo, PredId, ProcId, no),
proc_info_get_eval_method(ProcInfo, EvalMethod),
proc_info_get_initial_instmap(ProcInfo, ModuleInfo, InstMap0),
proc_info_get_headvars(ProcInfo, HeadVars),
proc_info_get_varset(ProcInfo, VarSet),
proc_info_get_argmodes(ProcInfo, ArgModes),
proc_info_get_vartypes(ProcInfo, VarTypes),
globals.get_trace_suppress(Globals, TraceSuppress),
NeedBodyReps = eff_trace_needs_proc_body_reps(ModuleInfo,
PredInfo, ProcInfo, TraceLevel, TraceSuppress),
(
NeedBodyReps = yes,
NeedGoalRep = trace_needs_body_rep
;
NeedBodyReps = no,
NeedGoalRep = trace_does_not_need_body_rep
),
NeedsAllNames = eff_trace_needs_all_var_names(ModuleInfo, PredInfo,
ProcInfo, TraceLevel, TraceSuppress),
proc_info_get_maybe_deep_profile_info(ProcInfo, MaybeHLDSDeepInfo),
(
MaybeHLDSDeepInfo = yes(HLDSDeepInfo),
MaybeDeepProfInfo =
maybe_generate_deep_prof_info(ProcInfo, HLDSDeepInfo)
;
MaybeHLDSDeepInfo = no,
MaybeDeepProfInfo = no
),
EffTraceLevel = eff_trace_level(ModuleInfo, PredInfo, ProcInfo,
TraceLevel),
module_info_get_table_struct_map(ModuleInfo, TableStructMap),
PredProcId = proc(PredId, ProcId),
(
MaybeTableIOInfo = no,
( if map.search(TableStructMap, PredProcId, TableStructInfo) then
TableStructInfo = table_struct_info(ProcTableStructInfo,
_Attributes),
MaybeTableInfo = yes(proc_table_struct(ProcTableStructInfo))
else
MaybeTableInfo = no
)
;
MaybeTableIOInfo = yes(TableIOInfo),
( if map.search(TableStructMap, PredProcId, _TableStructInfo) then
unexpected($module, $pred, "conflicting kinds of tabling")
else
MaybeTableInfo = yes(proc_table_io_entry(TableIOInfo))
)
),
proc_info_get_oisu_kind_fors(ProcInfo, OISUKindFors),
ProcLayout = proc_layout_info(RttiProcLabel, EntryLabel,
Detism, TotalSlots, MaybeSuccipSlot, EvalMethod,
EffTraceLevel, MaybeTraceCallLabel, MaxTraceRegR, MaxTraceRegF,
HeadVars, ArgModes, Goal, NeedGoalRep, InstMap0,
TraceSlotInfo, ForceProcId, VarSet, VarTypes,
InternalMap, MaybeTableInfo, NeedsAllNames, OISUKindFors,
MaybeDeepProfInfo),
global_data_add_new_proc_layout(proc(PredId, ProcId), ProcLayout,
!GlobalData)
else
true
),
get_closure_layouts(CodeInfo, ClosureLayouts),
global_data_add_new_closure_layouts(ClosureLayouts, !GlobalData),
ProcLabel = make_proc_label(ModuleInfo, PredId, ProcId),
get_alloc_sites(CodeInfo, AllocSites),
global_data_add_new_alloc_sites(AllocSites, !GlobalData),
Name = pred_info_name(PredInfo),
Arity = pred_info_orig_arity(PredInfo),
get_label_counter(CodeInfo, LabelCounter),
% You can have user trace events even if the effective trace level is none.
(
ProcTraceEvents = no,
MayAlterRtti = may_alter_rtti
;
ProcTraceEvents = yes,
MayAlterRtti = must_not_alter_rtti
),
globals.lookup_bool_option(Globals, generate_bytecode, GenBytecode),
( if
% XXX: There is a mass of calls above that the bytecode doesn't need;
% work out which is and isn't needed and put inside the else case
% below.
GenBytecode = yes,
% We don't generate bytecode for unify and compare preds.
% The automatically generated unify and compare predicates
% are correct by construction; for user-defined unify and
% compare predicates, we *assume* their correctness for now
% (perhaps not wisely).
not is_unify_or_compare_pred(PredInfo),
% Don't generate bytecode for procs with foreign code.
goal_has_foreign(Goal) = no
then
bytecode_stub(ModuleInfo, PredId, ProcId, ProcInstructions),
ProcLabelCounter = counter.init(0)
else
ProcInstructions = Instructions,
ProcLabelCounter = LabelCounter
),
get_used_env_vars(CodeInfo, UsedEnvVars),
CProc = c_procedure(Name, Arity, proc(PredId, ProcId), ProcLabel,
CodeModel, ProcInstructions, ProcLabelCounter, MayAlterRtti,
UsedEnvVars).
:- pred maybe_set_trace_level(pred_info::in,
module_info::in, module_info::out) is det.
maybe_set_trace_level(PredInfo, !ModuleInfo) :-
module_info_get_globals(!.ModuleInfo, Globals0),
( if
PredModule = pred_info_module(PredInfo),
PredName = pred_info_name(PredInfo),
PredArity = pred_info_orig_arity(PredInfo),
no_type_info_builtin(PredModule, PredName, PredArity)
then
% These predicates should never be traced, since they do not obey
% typeinfo_liveness. Since they may be opt_imported into other
% modules, we must switch off the tracing of such preds on a
% pred-by-pred basis.
globals.set_trace_level_none(Globals0, Globals1),
module_info_set_globals(Globals1, !ModuleInfo)
else if
pred_info_get_origin(PredInfo, origin_special_pred(_, _))
then
globals.get_trace_level(Globals0, TraceLevel),
UC_TraceLevel = trace_level_for_unify_compare(TraceLevel),
globals.set_trace_level(UC_TraceLevel, Globals0, Globals1),
module_info_set_globals(Globals1, !ModuleInfo)
else
true
).
:- func maybe_generate_deep_prof_info(proc_info, deep_profile_proc_info)
= maybe(proc_deep_prof_info).
maybe_generate_deep_prof_info(ProcInfo, HLDSDeepInfo) = MaybeDeepProfInfo :-
HLDSDeepInfo ^ deep_layout = MaybeHLDSDeepLayout,
(
MaybeHLDSDeepLayout = yes(HLDSDeepLayout),
HLDSDeepLayout = hlds_deep_layout(HLDSProcStatic, HLDSExcpVars),
HLDSDeepInfo ^ deep_orig_body = OriginalProcBody,
HLDSExcpVars = hlds_deep_excp_vars(TopCSDVar, MiddleCSDVar,
MaybeOldOutermostVar),
proc_info_get_stack_slots(ProcInfo, StackSlots),
( if map.search(StackSlots, TopCSDVar, TopCSDSlot) then
TopCSDSlotNum = stack_slot_num(TopCSDSlot),
map.lookup(StackSlots, MiddleCSDVar, MiddleCSDSlot),
MiddleCSDSlotNum = stack_slot_num(MiddleCSDSlot),
(
MaybeOldOutermostVar = yes(OldOutermostVar),
map.lookup(StackSlots, OldOutermostVar, OldOutermostSlot),
OldOutermostSlotNum = stack_slot_num(OldOutermostSlot)
;
MaybeOldOutermostVar = no,
OldOutermostSlotNum = -1
)
else
TopCSDSlotNum = -1,
MiddleCSDSlotNum = -1,
OldOutermostSlotNum = -1
),
DeepExcpSlots = deep_excp_slots(TopCSDSlotNum, MiddleCSDSlotNum,
OldOutermostSlotNum),
DeepProfInfo = proc_deep_prof_info(HLDSProcStatic, DeepExcpSlots,
OriginalProcBody),
MaybeDeepProfInfo = yes(DeepProfInfo)
;
MaybeHLDSDeepLayout = no,
MaybeDeepProfInfo = no
).
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
% Generate_category_code generates code for an entire procedure.
% Its algorithm has three or four main stages:
%
% - generate code for the body goal
% - generate code for the procedure entry
% - generate code for the procedure exit
% - generate code for the procedure fail (if needed)
%
% The first three tasks are forwarded to other procedures.
% The fourth task, if needed, is done by generate_category_code.
%
% The only caller of generate_category_code, generate_proc_code,
% has set up the code generator state to reflect what the machine
% state will be on entry to the procedure. Ensuring that the
% machine state at exit will conform to the expectation
% of the caller is the job of generate_exit.
%
% The reason why we generate the entry code after the body is that
% information such as the total number of stack slots needed,
% which is needed in the procedure entry prologue, cannot be
% conveniently obtained before generating the body, since the
% code generator may allocate temporary variables to hold values
% such as saved heap and trail pointers.
%
% Code_gen.generate_entry cannot depend on the code generator
% state, since when it is invoked this state is not appropriate
% for the procedure entry. Nor can it change the code generator state,
% since that would confuse generate_exit.
%
% Generating CALL trace events is done by generate_category_code,
% since only on entry to generate_category_code is the code generator
% state set up right. Generating EXIT trace events is done by
% generate_exit. Generating FAIL trace events is done
% by generate_category_code, since this requires modifying how
% we generate code for the body of the procedure (failures must
% now branch to a different place). Since FAIL trace events are
% part of the failure continuation, generate_category_code takes
% care of the failure continuation as well. (Model_det procedures
% of course have no failure continuation. Model_non procedures have
% a failure continuation, but in the absence of tracing this
% continuation needs no code. Only model_semi procedures need code
% for the failure continuation at all times.)
%
:- pred generate_category_code(code_model::in, prog_context::in, hlds_goal::in,
resume_point_info::in, trace_slot_info::in, llds_code::out,
maybe(label)::out, proc_frame_slots::out,
code_info::in, code_info::out, code_loc_dep::in) is det.
generate_category_code(CodeModel, ProcContext, Goal, ResumePoint,
TraceSlotInfo, Code, MaybeTraceCallLabel, ProcFrameSlots,
!CI, !.CLD) :-
(
CodeModel = model_det,
% Generate the code for the body of the procedure.
get_globals(!.CI, Globals),
globals.lookup_bool_option(Globals, middle_rec, MiddleRec),
( if
MiddleRec = yes,
middle_rec.match_and_generate(Goal, MiddleRecCode, !CI, !CLD)
then
Code = MiddleRecCode,
MaybeTraceCallLabel = no,
ProcFrameSlots = proc_frame_slots(0, no)
else
get_maybe_trace_info(!.CI, MaybeTraceInfo),
(
MaybeTraceInfo = yes(TraceInfo),
generate_call_event(TraceInfo, ProcContext,
MaybeTraceCallLabel, TraceCallCode, !CI, !CLD),
get_trace_maybe_tail_rec_info(TraceInfo, MaybeTailRecInfo),
(
MaybeTailRecInfo = yes(_TailRecLval - TailRecLabel),
TailRecLabelCode = singleton(
llds_instr(label(TailRecLabel),
"tail recursion label, nofulljump")
)
;
MaybeTailRecInfo = no,
TailRecLabelCode = empty
)
;
MaybeTraceInfo = no,
MaybeTraceCallLabel = no,
TraceCallCode = empty,
TailRecLabelCode = empty
),
generate_goal(model_det, Goal, BodyCode, !CI, !CLD),
generate_entry(!.CI, model_det, Goal, ResumePoint, ProcFrameSlots,
EntryCode),
generate_exit(model_det, ProcFrameSlots, TraceSlotInfo,
ProcContext, _, ExitCode, !CI, !.CLD),
Code = EntryCode ++ TraceCallCode ++ TailRecLabelCode ++
BodyCode ++ ExitCode
)
;
CodeModel = model_semi,
FailureLiveRegs = set.make_singleton_set(reg(reg_r, 1)),
FailCode = from_list([
llds_instr(assign(reg(reg_r, 1), const(llconst_false)), "Fail"),
llds_instr(livevals(FailureLiveRegs), ""),
llds_instr(goto(code_succip), "Return from procedure call")
]),
get_maybe_trace_info(!.CI, MaybeTraceInfo),
(
MaybeTraceInfo = yes(TraceInfo),
remember_position(!.CLD, BeforeBody),
generate_call_event(TraceInfo, ProcContext, MaybeTraceCallLabel,
TraceCallCode, !CI, !CLD),
get_trace_maybe_tail_rec_info(TraceInfo, MaybeTailRecInfo),
(
MaybeTailRecInfo = yes(_TailRecLval - TailRecLabel),
TailRecLabelCode = singleton(
llds_instr(label(TailRecLabel), "tail recursion label")
)
;
MaybeTailRecInfo = no,
TailRecLabelCode = empty
),
generate_goal(model_semi, Goal, BodyCode, !CI, !CLD),
generate_entry(!.CI, model_semi, Goal, ResumePoint,
ProcFrameSlots, EntryCode),
generate_exit(model_semi, ProcFrameSlots, TraceSlotInfo,
ProcContext, RestoreDeallocCode, ExitCode, !CI, !.CLD),
reset_to_position(BeforeBody, !.CI, !:CLD),
generate_resume_point(ResumePoint, ResumeCode, !CI, !CLD),
resume_point_vars(ResumePoint, ResumeVarList),
ResumeVars = set_of_var.list_to_set(ResumeVarList),
set_forward_live_vars(ResumeVars, !CLD),
% XXX A context that gives the end of the procedure definition
% would be better than ProcContext.
generate_external_event_code(external_port_fail, TraceInfo,
ProcContext, MaybeFailExternalInfo,
!CI, !.CLD, _CLDAfterEvent),
(
MaybeFailExternalInfo = yes(FailExternalInfo),
FailExternalInfo = external_event_info(_, _, TraceFailCode)
;
MaybeFailExternalInfo = no,
TraceFailCode = empty
),
Code = EntryCode ++ TraceCallCode ++ TailRecLabelCode ++
BodyCode ++ ExitCode ++ ResumeCode ++ TraceFailCode ++
RestoreDeallocCode ++ FailCode
;
MaybeTraceInfo = no,
MaybeTraceCallLabel = no,
remember_position(!.CLD, BeforeBody),
generate_goal(model_semi, Goal, BodyCode, !CI, !CLD),
generate_entry(!.CI, model_semi, Goal, ResumePoint,
ProcFrameSlots, EntryCode),
generate_exit(model_semi, ProcFrameSlots, TraceSlotInfo,
ProcContext, RestoreDeallocCode, ExitCode, !CI, !.CLD),
reset_to_position(BeforeBody, !.CI, !:CLD),
generate_resume_point(ResumePoint, ResumeCode,
!CI, !.CLD, _CLDAfterResume),
Code = EntryCode ++ BodyCode ++ ExitCode ++ ResumeCode ++
RestoreDeallocCode ++ FailCode
)
;
CodeModel = model_non,
get_maybe_trace_info(!.CI, MaybeTraceInfo),
(
MaybeTraceInfo = yes(TraceInfo),
generate_call_event(TraceInfo, ProcContext, MaybeTraceCallLabel,
TraceCallCode, !CI, !CLD),
get_trace_maybe_tail_rec_info(TraceInfo, MaybeTailRecInfo),
expect(unify(MaybeTailRecInfo, no), $module, $pred,
"tail recursive call in model_non code"),
remember_position(!.CLD, BeforeBody),
generate_goal(model_non, Goal, BodyCode, !CI, !CLD),
generate_entry(!.CI, model_non, Goal, ResumePoint,
ProcFrameSlots, EntryCode),
generate_exit(model_non, ProcFrameSlots, TraceSlotInfo,
ProcContext, _, ExitCode, !CI, !.CLD),
reset_to_position(BeforeBody, !.CI, !:CLD),
generate_resume_point(ResumePoint, ResumeCode, !CI, !CLD),
resume_point_vars(ResumePoint, ResumeVarList),
ResumeVars = set_of_var.list_to_set(ResumeVarList),
set_forward_live_vars(ResumeVars, !CLD),
% XXX A context that gives the end of the procedure definition
% would be better than ProcContext.
generate_external_event_code(external_port_fail, TraceInfo,
ProcContext, MaybeFailExternalInfo,
!CI, !.CLD, _CLDAfterEvent),
(
MaybeFailExternalInfo = yes(FailExternalInfo),
FailExternalInfo = external_event_info(_, _, TraceFailCode)
;
MaybeFailExternalInfo = no,
TraceFailCode = empty
),
MaybeTrailSlot = TraceSlotInfo ^ slot_trail,
(
MaybeTrailSlot = yes(_),
MaybeFromFull = TraceSlotInfo ^ slot_from_full,
(
MaybeFromFull = yes(FromFullSlot),
% Generate code which discards the ticket only if it was
% allocated, i.e. only if MR_trace_from_full was true
% on entry.
FromFullSlotLval =
llds.stack_slot_num_to_lval(nondet_stack,
FromFullSlot),
get_next_label(SkipLabel, !CI),
DiscardTraceTicketCode = from_list([
llds_instr(
if_val(unop(logical_not, lval(FromFullSlotLval)),
code_label(SkipLabel)), ""),
llds_instr(discard_ticket, "discard retry ticket"),
llds_instr(label(SkipLabel), "")
])
;
MaybeFromFull = no,
DiscardTraceTicketCode = singleton(
llds_instr(discard_ticket, "discard retry ticket")
)
)
;
MaybeTrailSlot = no,
DiscardTraceTicketCode = empty
),
FailCode = singleton(
llds_instr(goto(do_fail), "fail after fail trace port")
),
Code = EntryCode ++ TraceCallCode ++ BodyCode ++ ExitCode ++
ResumeCode ++ TraceFailCode ++ DiscardTraceTicketCode ++
FailCode
;
MaybeTraceInfo = no,
MaybeTraceCallLabel = no,
generate_goal(model_non, Goal, BodyCode, !CI, !CLD),
generate_entry(!.CI, model_non, Goal, ResumePoint,
ProcFrameSlots, EntryCode),
generate_exit(model_non, ProcFrameSlots, TraceSlotInfo,
ProcContext, _, ExitCode, !CI, !.CLD),
Code = EntryCode ++ BodyCode ++ ExitCode
)
).
:- pred generate_call_event(trace_info::in, prog_context::in,
maybe(label)::out, llds_code::out,
code_info::in, code_info::out, code_loc_dep::in, code_loc_dep::out) is det.
generate_call_event(TraceInfo, ProcContext, MaybeTraceCallLabel, TraceCallCode,
!CI, !CLD) :-
generate_external_event_code(external_port_call, TraceInfo,
ProcContext, MaybeCallExternalInfo, !CI, !CLD),
(
MaybeCallExternalInfo = yes(CallExternalInfo),
CallExternalInfo = external_event_info(TraceCallLabel, _,
TraceCallCode),
MaybeTraceCallLabel = yes(TraceCallLabel)
;
MaybeCallExternalInfo = no,
% This can happen for procedures containing user events
% in shallow traced modules.
TraceCallCode = empty,
MaybeTraceCallLabel = no
).
%---------------------------------------------------------------------------%
% Generate the prologue for a procedure.
%
% The prologue will contain
%
% a comment to mark prologue start
% a comment explaining the stack layout
% the procedure entry label
% code to allocate a stack frame
% code to fill in some special slots in the stack frame
% a comment to mark prologue end
%
% At the moment the only special slots are the succip slot, and
% the slots holding the call number and call depth for tracing.
%
% Not all frames will have all these components. For example, the code
% to allocate a stack frame will be missing if the procedure doesn't
% need a stack frame, and if the procedure is nondet, then the code
% to fill in the succip slot is subsumed by the mkframe.
:- pred generate_entry(code_info::in, code_model::in, hlds_goal::in,
resume_point_info::in, proc_frame_slots::out, llds_code::out) is det.
generate_entry(CI, CodeModel, Goal, OutsideResumePoint, ProcFrameSlots,
EntryCode) :-
get_stack_slots(CI, StackSlots),
get_varset(CI, VarSet),
SlotsComment = explain_stack_slots(StackSlots, VarSet),
StartComment = from_list([
llds_instr(comment("Start of procedure prologue"), ""),
llds_instr(comment(SlotsComment), "")
]),
get_total_stackslot_count(CI, MainSlots),
get_pred_id(CI, PredId),
get_proc_id(CI, ProcId),
get_module_info(CI, ModuleInfo),
EntryLabel = make_local_entry_label(ModuleInfo, PredId, ProcId, no),
LabelCode = singleton(
llds_instr(label(EntryLabel), "Procedure entry point")
),
get_succip_used(CI, Used),
( if
% Do we need to save the succip across calls?
Used = yes,
% Do we need to use a general slot for storing succip?
CodeModel \= model_non
then
SuccipSlot = maybe_round_frame_size(CI, CodeModel, MainSlots + 1),
SaveSuccipCode = singleton(
llds_instr(assign(stackvar(SuccipSlot), lval(succip)),
"Save the success ip")
),
TotalSlots = SuccipSlot,
MaybeSuccipSlot = yes(SuccipSlot)
else
SaveSuccipCode = empty,
TotalSlots = maybe_round_frame_size(CI, CodeModel, MainSlots),
MaybeSuccipSlot = no
),
get_maybe_trace_info(CI, MaybeTraceInfo),
(
MaybeTraceInfo = yes(TraceInfo),
generate_slot_fill_code(CI, TraceInfo, TraceFillCode)
;
MaybeTraceInfo = no,
TraceFillCode = empty
),
PushMsg = push_msg(ModuleInfo, PredId, ProcId),
(
CodeModel = model_non,
resume_point_stack_addr(OutsideResumePoint, OutsideResumeAddress),
NondetFrameInfo = ordinary_frame(PushMsg, TotalSlots),
AllocCode = singleton(
llds_instr(mkframe(NondetFrameInfo, yes(OutsideResumeAddress)),
"Allocate stack frame")
)
;
( CodeModel = model_det
; CodeModel = model_semi
),
IsLeaf = proc_body_is_leaf(Goal),
(
IsLeaf = is_not_leaf,
StackIncrKind = stack_incr_nonleaf
;
IsLeaf = is_leaf,
StackIncrKind = stack_incr_leaf
),
( if TotalSlots > 0 then
AllocCode = singleton(
llds_instr(incr_sp(TotalSlots, PushMsg, StackIncrKind),
"Allocate stack frame")
)
else
AllocCode = empty
)
),
ProcFrameSlots = proc_frame_slots(TotalSlots, MaybeSuccipSlot),
EndComment = singleton(
llds_instr(comment("End of procedure prologue"), "")
),
EntryCode = StartComment ++ LabelCode ++ AllocCode ++
SaveSuccipCode ++ TraceFillCode ++ EndComment.
:- func maybe_round_frame_size(code_info, code_model, int) = int.
maybe_round_frame_size(CI, CodeModel, NumSlots0) = NumSlots :-
(
( CodeModel = model_det
; CodeModel = model_semi
),
NumSlots = round_det_stack_frame_size(CI, NumSlots0)
;
CodeModel = model_non,
NumSlots = NumSlots0
).
%---------------------------------------------------------------------------%
% Generate the success epilogue for a procedure.
%
% The success epilogue will contain
%
% a comment to mark epilogue start
% code to place the output arguments where their caller expects
% code to restore registers from some special slots
% code to deallocate the stack frame
% code to set r1 to MR_TRUE (for semidet procedures only)
% a jump back to the caller, including livevals information
% a comment to mark epilogue end
%
% The parts of this that restore registers and deallocate the stack
% frame are also part of the failure epilog, which is handled by
% our caller; this is why we return RestoreDeallocCode.
%
% At the moment the only special slots are the succip slot, and
% the tracing slots (holding the call sequence number, call event
% number, call depth, from-full indication, and trail state).
%
% Not all frames will have all these components. For example, for
% nondet procedures we don't deallocate the stack frame before
% success.
%
% Epilogues for procedures defined by nondet pragma C codes do not
% follow the rules above. For such procedures, the normal functions
% of the epilogue are handled when traversing the pragma C code goal;
% we need only #undef a macro defined by the procedure prologue.
:- pred generate_exit(code_model::in, proc_frame_slots::in,
trace_slot_info::in, prog_context::in, llds_code::out, llds_code::out,
code_info::in, code_info::out, code_loc_dep::in) is det.
generate_exit(CodeModel, ProcFrameSlots, TraceSlotInfo, ProcContext,
RestoreDeallocCode, ExitCode, !CI, !.CLD) :-
StartComment = singleton(
llds_instr(comment("Start of procedure epilogue"), "")
),
EndComment = singleton(
llds_instr(comment("End of procedure epilogue"), "")
),
ProcFrameSlots = proc_frame_slots(TotalSlots, MaybeSuccipSlot),
get_instmap(!.CLD, InstMap),
ArgModes = get_arginfo(!.CI),
HeadVars = get_headvars(!.CI),
assoc_list.from_corresponding_lists(HeadVars, ArgModes, Args),
( if instmap_is_unreachable(InstMap) then
OutLvals = set.init,
FlushCode = empty
else
setup_return(Args, OutLvals, FlushCode, !.CI, !CLD)
),
(
MaybeSuccipSlot = yes(SuccipSlot),
RestoreSuccipCode = singleton(
llds_instr(assign(succip, lval(stackvar(SuccipSlot))),
"restore the success ip")
)
;
MaybeSuccipSlot = no,
RestoreSuccipCode = empty
),
( if
( TotalSlots = 0
; CodeModel = model_non
)
then
DeallocCode = empty
else
DeallocCode = singleton(
llds_instr(decr_sp(TotalSlots), "Deallocate stack frame")
)
),
( if
TraceSlotInfo ^ slot_trail = yes(_),
CodeModel \= model_non
then
MaybeFromFull = TraceSlotInfo ^ slot_from_full,
(
MaybeFromFull = yes(FromFullSlot),
% Generate code which prunes the ticket only if it was allocated,
% i.e. only if MR_trace_from_full was true on entry.
%
% Note that to avoid duplicating label names, we need to generate
% two different copies of this with different labels; this is
% needed for semidet code, which will get one copy in the
% success epilogue and one copy in the failure epilogue.
StackId = code_model_to_main_stack(CodeModel),
FromFullSlotLval =
llds.stack_slot_num_to_lval(StackId, FromFullSlot),
get_next_label(SkipLabel, !CI),
get_next_label(SkipLabelCopy, !CI),
PruneTraceTicketCode = from_list([
llds_instr(
if_val(unop(logical_not, lval(FromFullSlotLval)),
code_label(SkipLabel)), ""),
llds_instr(prune_ticket, "prune retry ticket"),
llds_instr(label(SkipLabel), "")
]),
PruneTraceTicketCodeCopy = from_list([
llds_instr(
if_val(unop(logical_not, lval(FromFullSlotLval)),
code_label(SkipLabelCopy)), ""),
llds_instr(prune_ticket, "prune retry ticket"),
llds_instr(label(SkipLabelCopy), "")
])
;
MaybeFromFull = no,
PruneTraceTicketCode = singleton(
llds_instr(prune_ticket, "prune retry ticket")
),
PruneTraceTicketCodeCopy = PruneTraceTicketCode
)
else
PruneTraceTicketCode = empty,
PruneTraceTicketCodeCopy = empty
),
RestoreDeallocCode = RestoreSuccipCode ++
PruneTraceTicketCode ++ DeallocCode,
RestoreDeallocCodeCopy = RestoreSuccipCode ++
PruneTraceTicketCodeCopy ++ DeallocCode,
get_maybe_trace_info(!.CI, MaybeTraceInfo),
(
MaybeTraceInfo = yes(TraceInfo),
% XXX A context that gives the end of the procedure definition
% would be better than CallContext.
generate_external_event_code(external_port_exit, TraceInfo,
ProcContext, MaybeExitExternalInfo, !CI, !.CLD, _CLDAfterExit),
(
MaybeExitExternalInfo = yes(ExitExternalInfo),
ExitExternalInfo = external_event_info(_, TypeInfoDatas,
TraceExitCode),
map.foldl(add_type_info_lvals, TypeInfoDatas, OutLvals, LiveLvals)
;
MaybeExitExternalInfo = no,
LiveLvals = OutLvals,
TraceExitCode = empty
)
;
MaybeTraceInfo = no,
TraceExitCode = empty,
LiveLvals = OutLvals
),
get_proc_info(!.CI, ProcInfo),
proc_info_get_maybe_special_return(ProcInfo, MaybeSpecialReturn),
(
CodeModel = model_det,
expect(unify(MaybeSpecialReturn, no), $module, $pred,
"det special_return"),
SuccessCode = from_list([
llds_instr(livevals(LiveLvals), ""),
llds_instr(goto(code_succip), "Return from procedure call")
]),
AllSuccessCode = TraceExitCode ++ RestoreDeallocCodeCopy ++ SuccessCode
;
CodeModel = model_semi,
expect(unify(MaybeSpecialReturn, no), $module, $pred,
"semi special_return"),
set.insert(reg(reg_r, 1), LiveLvals, SuccessLiveRegs),
SuccessCode = from_list([
llds_instr(assign(reg(reg_r, 1), const(llconst_true)),
"Succeed"),
llds_instr(livevals(SuccessLiveRegs), ""),
llds_instr(goto(code_succip), "Return from procedure call")
]),
AllSuccessCode = TraceExitCode ++ RestoreDeallocCodeCopy ++ SuccessCode
;
CodeModel = model_non,
(
MaybeTraceInfo = yes(TraceInfo2),
maybe_setup_redo_event(TraceInfo2, SetupRedoCode)
;
MaybeTraceInfo = no,
SetupRedoCode = empty
),
(
MaybeSpecialReturn = yes(SpecialReturn),
SpecialReturn = generator_return(GeneratorLocnStr, DebugStr),
ReturnMacroName = "MR_tbl_mmos_return_answer",
ReturnCodeStr = "\t" ++ ReturnMacroName ++ "(" ++
DebugStr ++ ", " ++ GeneratorLocnStr ++ ");\n",
Component = foreign_proc_user_code(no,
proc_does_not_affect_liveness, ReturnCodeStr),
MD = proc_may_not_duplicate,
SuccessCode = from_list([
llds_instr(livevals(LiveLvals), ""),
llds_instr(foreign_proc_code([], [Component],
proc_may_call_mercury, no, no, no, no, no, no, MD), "")
])
;
MaybeSpecialReturn = no,
SuccessCode = from_list([
llds_instr(livevals(LiveLvals), ""),
llds_instr(goto(do_succeed(no)),
"Return from procedure call")
])
),
AllSuccessCode = SetupRedoCode ++ TraceExitCode ++ SuccessCode
),
ExitCode = StartComment ++ FlushCode ++ AllSuccessCode ++ EndComment.
:- pred add_type_info_lvals(tvar::in, set(layout_locn)::in,
set(lval)::in, set(lval)::out) is det.
add_type_info_lvals(_TVar, TypeInfoLocnSets, !LiveLvals) :-
TypeInfoLvals = set.map(project_layout_locn_lval, TypeInfoLocnSets),
set.union(TypeInfoLvals, !LiveLvals).
:- func project_layout_locn_lval(layout_locn) = lval.
project_layout_locn_lval(locn_direct(Lval)) = Lval.
project_layout_locn_lval(locn_indirect(Lval, _)) = Lval.
%---------------------------------------------------------------------------%
% Add the succip to the livevals before and after calls. Traverses the list
% of instructions looking for livevals and calls, adding succip in the
% stackvar number given as an argument.
%
:- pred add_saved_succip(list(instruction)::in, int::in,
list(instruction)::out) is det.
add_saved_succip([], _StackLoc, []).
add_saved_succip([Instr0 | Instrs0], StackLoc, [Instr | Instrs]) :-
Instr0 = llds_instr(Uinstr0, Comment),
( if
Uinstr0 = livevals(LiveVals0),
Instrs0 \= [llds_instr(goto(code_succip), _) | _]
% XXX We should also test for tailcalls
% once we start generating them directly.
then
set.insert(stackvar(StackLoc), LiveVals0, LiveVals1),
Uinstr = livevals(LiveVals1),
Instr = llds_instr(Uinstr, Comment)
else if
Uinstr0 = llcall(Target, ReturnLabel, LiveVals0, Context, GP, CM)
then
map.init(Empty),
LiveVals = [live_lvalue(locn_direct(stackvar(StackLoc)),
live_value_succip, Empty) | LiveVals0],
Uinstr = llcall(Target, ReturnLabel, LiveVals, Context, GP, CM),
Instr = llds_instr(Uinstr, Comment)
else
Instr = Instr0
),
add_saved_succip(Instrs0, StackLoc, Instrs).
%---------------------------------------------------------------------------%
:- pred bytecode_stub(module_info::in, pred_id::in, proc_id::in,
list(instruction)::out) is det.
bytecode_stub(ModuleInfo, PredId, ProcId, BytecodeInstructions) :-
module_info_pred_info(ModuleInfo, PredId, PredInfo),
ModuleSymName = pred_info_module(PredInfo),
ModuleName = sym_name_to_string_sep(ModuleSymName, "__"),
EntryLabel = make_local_entry_label(ModuleInfo, PredId, ProcId, no),
PredName = pred_info_name(PredInfo),
proc_id_to_int(ProcId, ProcNum),
string.int_to_string(ProcNum, ProcStr),
Arity = pred_info_orig_arity(PredInfo),
int_to_string(Arity, ArityStr),
PredOrFunc = pred_info_is_pred_or_func(PredInfo),
CallStructName = "bytecode_call_info",
(
PredOrFunc = pf_function,
IsFuncStr = "MR_TRUE"
;
PredOrFunc = pf_predicate,
IsFuncStr = "MR_FALSE"
),
string.append_list([
"\t\tstatic MB_Call ", CallStructName, " = {\n",
"\t\t\t(MB_Word)NULL,\n",
"\t\t\t""", ModuleName, """,\n",
"\t\t\t""", PredName, """,\n",
"\t\t\t", ProcStr, ",\n",
"\t\t\t", ArityStr, ",\n",
"\t\t\t", IsFuncStr, "\n",
"\t\t};\n"
], CallStruct),
string.append_list([
"\t\tMB_Native_Addr return_addr;\n",
"\t\tMR_save_registers();\n",
"\t\treturn_addr = MB_bytecode_call_entry(", "&",CallStructName,");\n",
"\t\tMR_restore_registers();\n",
"\t\tMR_GOTO(return_addr);\n"
], BytecodeCall),
BytecodeInstructionsComponents = [
foreign_proc_raw_code(cannot_branch_away,
proc_does_not_affect_liveness, live_lvals_info(set.init), "\t{\n"),
foreign_proc_raw_code(cannot_branch_away,
proc_does_not_affect_liveness, live_lvals_info(set.init),
CallStruct),
foreign_proc_raw_code(cannot_branch_away,
proc_does_not_affect_liveness, no_live_lvals_info, BytecodeCall),
foreign_proc_raw_code(cannot_branch_away,
proc_does_not_affect_liveness, live_lvals_info(set.init), "\t}\n")
],
MD = proc_may_not_duplicate,
BytecodeInstructions = [
llds_instr(label(EntryLabel), "Procedure entry point"),
llds_instr(foreign_proc_code([], BytecodeInstructionsComponents,
proc_may_call_mercury, no, no, no, no, no, no, MD), "Entry stub")
].
%---------------------------------------------------------------------------%
push_msg(ModuleInfo, PredId, ProcId) = PushMsg :-
module_info_pred_info(ModuleInfo, PredId, PredInfo),
PushMsg = describe_proc(PredInfo, ProcId).
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
add_all_tabling_info_structs(ModuleInfo, !GlobalData) :-
module_info_get_table_struct_map(ModuleInfo, TableStructMap),
map.to_assoc_list(TableStructMap, TableStructs),
list.foldl(add_tabling_info_struct, TableStructs, !GlobalData).
:- pred add_tabling_info_struct(pair(pred_proc_id, table_struct_info)::in,
global_data::in, global_data::out) is det.
add_tabling_info_struct(PredProcId - TableStructInfo, !GlobalData) :-
TableStructInfo = table_struct_info(ProcTableStructInfo, TableAttributes),
ProcTableStructInfo = proc_table_struct_info(RttiProcLabel, _TVarSet,
_Context, NumInputs, NumOutputs, InputSteps, MaybeOutputSteps,
ArgInfos, EvalMethod),
global_data_get_static_cell_info(!.GlobalData, StaticCellInfo0),
convert_table_arg_info(ArgInfos, NumPTIs, PTIVectorRval,
TVarVectorRval, StaticCellInfo0, StaticCellInfo),
global_data_set_static_cell_info(StaticCellInfo, !GlobalData),
NumArgs = NumInputs + NumOutputs,
expect(unify(NumArgs, NumPTIs), $module, $pred, "args mismatch"),
MaybeSizeLimit = TableAttributes ^ table_attr_size_limit,
Statistics = TableAttributes ^ table_attr_statistics,
ProcLabel = make_proc_label_from_rtti(RttiProcLabel),
Var = tabling_info_struct(ProcLabel, EvalMethod,
NumInputs, NumOutputs, InputSteps, MaybeOutputSteps, PTIVectorRval,
TVarVectorRval, MaybeSizeLimit, Statistics),
global_data_add_new_proc_var(PredProcId, Var, !GlobalData).
%---------------------------------------------------------------------------%
:- end_module ll_backend.proc_gen.
%---------------------------------------------------------------------------%