Files
mercury/compiler/mercury_compile_llds_back_end.m
Paul Bone 3452e25fd6 Improve tail call warnings for the low level C grade.
This patch reduces spurious tail call warnings by warning only for the last
of a sequence of recursive calls along an execution path, for example it
no-longer emits a warning for the first of the two recursive calls in
quicksort.  I have already made a change to this effect for the MLDS
backends.

This patch also implements the new the require_tail_recursion pragma.  This
change has already been made for the MLDS backends.

The require_tail_recursion pragma does not yet handle mutual recursion or
raise an error if the code is not recursive at all.  These will be my next
tasks.

compiler/mark_tail_calls.m:
    Combine marking tail calls with warning about tail calls.

    Handle scopes more accurately.

compiler/mercury_compile_llds_back_end.m:
    Conform to changes in mark_tail_calls.m

    No longer warn for tail calls, this is now done in the same pass that
    marks tail calls.  The decision to run this pass is now made on a
    per-procedure basis and in mark_tail_calls.m

tests/invalid/require_tailrec_1.m:
tests/invalid/require_tailrec_2.m:
tests/invalid/Mmakefile:
tests/invalid/Mercury.options:
tests/valid/require_tailrec_1.m:
tests/valid/require_tailrec_2.m:
tests/valid/Mmakefile:
tests/valid/Mercury.options:
    Add new tests.

tests/invalid/Mmakefile:
tests/invalid/Mercury.options:
tests/invalid/require_tail_recursion.{m,err_exp} ->
        require_tailrec_invalid.{m,exp}:
    Rename existing test.
2015-12-16 22:47:08 +11:00

892 lines
36 KiB
Mathematica

%---------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%---------------------------------------------------------------------------%
% Copyright (C) 2009-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: mercury_compile_llds_back_end.m.
%
% This module implements the LLDS backend for the top level of the Mercury
% compiler. It invokes the different passes of this backend as appropriate.
%
%---------------------------------------------------------------------------%
:- module top_level.mercury_compile_llds_back_end.
:- interface.
:- import_module hlds.
:- import_module hlds.hlds_module.
:- import_module hlds.passes_aux.
:- import_module mdbcomp.
:- import_module mdbcomp.sym_name.
:- import_module ll_backend.
:- import_module ll_backend.global_data.
:- import_module ll_backend.llds.
:- import_module bool.
:- import_module io.
:- import_module list.
:- pred llds_backend_pass(module_info::in, module_info::out,
global_data::out, list(c_procedure)::out, dump_info::in, dump_info::out,
io::di, io::uo) is det.
:- pred llds_output_pass(module_info::in, global_data::in,
list(c_procedure)::in, module_name::in, bool::out,
list(string)::out, io::di, io::uo) is det.
:- pred map_args_to_regs(bool::in, bool::in,
module_info::in, module_info::out, io::di, io::uo) is det.
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
:- implementation.
:- import_module backend_libs.
:- import_module backend_libs.base_typeclass_info.
:- import_module backend_libs.compile_target_code.
:- import_module backend_libs.export.
:- import_module backend_libs.foreign.
:- import_module backend_libs.proc_label.
:- import_module backend_libs.type_class_info.
:- import_module backend_libs.type_ctor_info.
:- import_module check_hlds.
:- import_module check_hlds.simplify.
:- import_module check_hlds.simplify.simplify_proc.
:- import_module check_hlds.simplify.simplify_tasks.
:- import_module hlds.arg_info.
:- import_module hlds.hlds_goal.
:- import_module hlds.hlds_out.
:- import_module hlds.hlds_out.hlds_out_util.
:- import_module hlds.hlds_pred.
:- import_module hlds.mark_tail_calls.
:- import_module libs.
:- import_module libs.file_util.
:- import_module libs.globals.
:- import_module libs.options.
:- import_module ll_backend.continuation_info.
:- import_module ll_backend.dupproc.
:- import_module ll_backend.follow_code.
:- import_module ll_backend.liveness.
:- import_module ll_backend.llds_out.
:- import_module ll_backend.llds_out.llds_out_file.
:- import_module ll_backend.optimize.
:- import_module ll_backend.proc_gen.
:- import_module ll_backend.saved_vars.
:- import_module ll_backend.stack_alloc.
:- import_module ll_backend.stack_layout.
:- import_module ll_backend.stack_opt.
:- import_module ll_backend.store_alloc.
:- import_module ll_backend.transform_llds.
:- import_module ll_backend.unify_gen.
:- import_module mdbcomp.prim_data.
:- import_module mdbcomp.program_representation.
:- import_module parse_tree.error_util.
:- import_module parse_tree.file_names.
:- import_module parse_tree.module_cmds.
:- import_module parse_tree.prog_data.
:- import_module parse_tree.prog_foreign.
:- import_module top_level.mercury_compile_front_end.
:- import_module transform_hlds.
:- import_module transform_hlds.dependency_graph.
:- import_module assoc_list.
:- import_module cord.
:- import_module int.
:- import_module map.
:- import_module maybe.
:- import_module pair.
:- import_module require.
:- import_module set.
:- import_module string.
:- import_module term.
%---------------------------------------------------------------------------%
llds_backend_pass(!HLDS, !:GlobalData, LLDS, !DumpInfo, !IO) :-
module_info_get_name(!.HLDS, ModuleName),
module_info_get_globals(!.HLDS, Globals),
globals.lookup_bool_option(Globals, unboxed_float, OptUnboxFloat),
globals.lookup_bool_option(Globals, common_data, DoCommonData),
(
OptUnboxFloat = yes,
UnboxFloats = have_unboxed_floats
;
OptUnboxFloat = no,
UnboxFloats = do_not_have_unboxed_floats
),
StaticCellInfo0 = init_static_cell_info(ModuleName, UnboxFloats,
DoCommonData),
module_info_get_ts_rev_string_table(!.HLDS, TSStringTableSize,
TSRevStringTable),
global_data_init(StaticCellInfo0, TSStringTableSize, TSRevStringTable,
!:GlobalData),
globals.lookup_bool_option(Globals, verbose, Verbose),
globals.lookup_bool_option(Globals, statistics, Stats),
% map_args_to_regs affects the interface to a predicate,
% so it must be done in one phase immediately before code generation.
map_args_to_regs(Verbose, Stats, !HLDS, !IO),
maybe_dump_hlds(!.HLDS, 305, "args_to_regs", !DumpInfo, !IO),
globals.lookup_bool_option(Globals, trad_passes, TradPasses),
add_all_tabling_info_structs(!.HLDS, !GlobalData),
(
TradPasses = no,
llds_backend_pass_by_phases(!HLDS, LLDS, !GlobalData, [], Specs,
!DumpInfo, !IO)
;
TradPasses = yes,
llds_backend_pass_by_preds(!HLDS, LLDS, !GlobalData, [], Specs)
),
% XXX _NumErrors
write_error_specs(Specs, Globals, 0, _NumWarnings, 0, _NumErrors, !IO).
%---------------------------------------------------------------------------%
:- pred llds_backend_pass_by_phases(module_info::in, module_info::out,
list(c_procedure)::out, global_data::in, global_data::out,
list(error_spec)::in, list(error_spec)::out,
dump_info::in, dump_info::out, io::di, io::uo) is det.
llds_backend_pass_by_phases(!HLDS, !:LLDS, !GlobalData, !Specs,
!DumpInfo, !IO) :-
module_info_get_globals(!.HLDS, Globals),
globals.lookup_bool_option(Globals, verbose, Verbose),
globals.lookup_bool_option(Globals, statistics, Stats),
maybe_saved_vars(Verbose, Stats, !HLDS, !IO),
maybe_dump_hlds(!.HLDS, 310, "saved_vars_const", !DumpInfo, !IO),
maybe_stack_opt(Verbose, Stats, !HLDS, !IO),
maybe_dump_hlds(!.HLDS, 315, "saved_vars_cell", !DumpInfo, !IO),
maybe_followcode(Verbose, Stats, !HLDS, !IO),
maybe_dump_hlds(!.HLDS, 320, "followcode", !DumpInfo, !IO),
maybe_simplify(no, simplify_pass_ll_backend, Verbose, Stats, !HLDS,
[], SimplifySpecs, !IO),
expect(unify(contains_errors(Globals, SimplifySpecs), no), $module, $pred,
"simplify has errors"),
maybe_dump_hlds(!.HLDS, 325, "ll_backend_simplify", !DumpInfo, !IO),
compute_liveness(Verbose, Stats, !HLDS, !IO),
maybe_dump_hlds(!.HLDS, 330, "liveness", !DumpInfo, !IO),
maybe_mark_tail_rec_calls(Verbose, Stats, !HLDS, !Specs, !IO),
maybe_dump_hlds(!.HLDS, 332, "mark_debug_tailrec_calls", !DumpInfo, !IO),
compute_stack_vars(Verbose, Stats, !HLDS, !IO),
maybe_dump_hlds(!.HLDS, 335, "stackvars", !DumpInfo, !IO),
allocate_store_map(Verbose, Stats, !HLDS, !IO),
maybe_dump_hlds(!.HLDS, 340, "store_map", !DumpInfo, !IO),
generate_llds_code_for_module(!.HLDS, Verbose, Stats, !GlobalData, !:LLDS,
!IO),
maybe_optimize_llds(!.HLDS, !.GlobalData, Verbose, Stats, !LLDS, !IO),
maybe_generate_stack_layouts(!.HLDS, !.LLDS, Verbose, Stats, !GlobalData,
!IO).
% maybe_dump_global_data(!.GlobalData, !IO).
:- pred llds_backend_pass_by_preds(module_info::in, module_info::out,
list(c_procedure)::out, global_data::in, global_data::out,
list(error_spec)::in, list(error_spec)::out) is det.
llds_backend_pass_by_preds(!HLDS, LLDS, !GlobalData, !Specs) :-
module_info_get_valid_pred_ids(!.HLDS, PredIds),
module_info_get_globals(!.HLDS, Globals),
globals.lookup_bool_option(Globals, optimize_proc_dups, ProcDups),
(
ProcDups = no,
OrderedPredIds = PredIds,
MaybeDupProcMap = no
;
ProcDups = yes,
dependency_graph.build_pred_dependency_graph(!.HLDS, PredIds,
do_not_include_imported, DepInfo),
hlds_dependency_info_get_dependency_ordering(DepInfo, PredSCCs),
list.condense(PredSCCs, OrderedPredIds),
MaybeDupProcMap = yes(map.init)
),
generate_const_structs(!.HLDS, ConstStructMap, !GlobalData),
llds_backend_pass_by_preds_loop_over_preds(!HLDS, ConstStructMap,
OrderedPredIds, MaybeDupProcMap, cord.init, CProcsCord,
!GlobalData, !Specs),
LLDS = cord.list(CProcsCord).
:- type dup_proc_label_map ==
map(mdbcomp.prim_data.proc_label, mdbcomp.prim_data.proc_label).
:- pred llds_backend_pass_by_preds_loop_over_preds(
module_info::in, module_info::out, const_struct_map::in, list(pred_id)::in,
maybe(dup_proc_label_map)::in,
cord(c_procedure)::in, cord(c_procedure)::out,
global_data::in, global_data::out,
list(error_spec)::in, list(error_spec)::out) is det.
llds_backend_pass_by_preds_loop_over_preds(!HLDS, _,
[], _, !CProcsCord, !GlobalData, !Specs).
llds_backend_pass_by_preds_loop_over_preds(!HLDS, ConstStructMap,
[PredId | PredIds], !.MaybeDupProcMap,
!CProcsCord, !GlobalData, !Specs) :-
llds_backend_pass_by_preds_do_one_pred(!HLDS, ConstStructMap, PredId,
!MaybeDupProcMap, !CProcsCord, !GlobalData, !Specs),
llds_backend_pass_by_preds_loop_over_preds(!HLDS, ConstStructMap, PredIds,
!.MaybeDupProcMap, !CProcsCord, !GlobalData, !Specs).
:- pred llds_backend_pass_by_preds_do_one_pred(
module_info::in, module_info::out, const_struct_map::in, pred_id::in,
maybe(dup_proc_label_map)::in, maybe(dup_proc_label_map)::out,
cord(c_procedure)::in, cord(c_procedure)::out,
global_data::in, global_data::out,
list(error_spec)::in, list(error_spec)::out) is det.
llds_backend_pass_by_preds_do_one_pred(!HLDS, ConstStructMap, PredId,
!MaybeDupProcMap, !CProcsCord, !GlobalData, !Specs) :-
module_info_get_preds(!.HLDS, PredTable),
map.lookup(PredTable, PredId, PredInfo),
ProcIds = pred_info_non_imported_procids(PredInfo),
(
ProcIds = []
;
ProcIds = [_ | _],
module_info_get_globals(!.HLDS, Globals0),
globals.lookup_bool_option(Globals0, verbose, Verbose),
(
Verbose = yes,
trace [io(!IO)] (
io.write_string("% Generating code for ", !IO),
write_pred_id(!.HLDS, PredId, !IO),
io.write_string("\n", !IO),
maybe_flush_output(Verbose, !IO)
)
;
Verbose = no
),
( 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; module-by-module wouldn't work.
globals.get_trace_level(Globals0, TraceLevel),
globals.set_trace_level_none(Globals0, Globals1),
module_info_set_globals(Globals1, !HLDS),
llds_backend_pass_for_pred(!HLDS, ConstStructMap, PredId, PredInfo,
ProcIds, IdCProcs, !GlobalData, !Specs),
module_info_get_globals(!.HLDS, Globals2),
globals.set_trace_level(TraceLevel, Globals2, Globals),
module_info_set_globals(Globals, !HLDS)
else
llds_backend_pass_for_pred(!HLDS, ConstStructMap,
PredId, PredInfo, ProcIds, IdCProcs, !GlobalData, !Specs)
),
(
!.MaybeDupProcMap = no,
assoc_list.values(IdCProcs, CProcs)
;
!.MaybeDupProcMap = yes(DupProcMap0),
eliminate_duplicate_procs(IdCProcs, CProcs,
DupProcMap0, DupProcMap),
!:MaybeDupProcMap = yes(DupProcMap)
),
!:CProcsCord = !.CProcsCord ++ cord.from_list(CProcs),
globals.lookup_bool_option(Globals0, statistics, Stats),
trace [io(!IO)] (
maybe_write_string(Verbose, "% done.\n", !IO),
maybe_report_stats(Stats, !IO)
)
).
:- pred llds_backend_pass_for_pred(module_info::in, module_info::out,
const_struct_map::in, pred_id::in, pred_info::in, list(proc_id)::in,
assoc_list(mdbcomp.prim_data.proc_label, c_procedure)::out,
global_data::in, global_data::out,
list(error_spec)::in, list(error_spec)::out) is det.
llds_backend_pass_for_pred(!HLDS, _, _, _, [], [], !GlobalData, !Specs).
llds_backend_pass_for_pred(!HLDS, ConstStructMap, PredId, PredInfo,
[ProcId | ProcIds], [ProcLabel - CProc | ProcLabelsCProcs],
!GlobalData, !Specs) :-
ProcLabel = make_proc_label(!.HLDS, PredId, ProcId),
pred_info_get_proc_table(PredInfo, ProcTable),
map.lookup(ProcTable, ProcId, ProcInfo),
llds_backend_pass_for_proc(!HLDS, ConstStructMap, PredId, PredInfo,
ProcId, ProcInfo, CProc, !GlobalData, !Specs),
llds_backend_pass_for_pred(!HLDS, ConstStructMap, PredId, PredInfo,
ProcIds, ProcLabelsCProcs, !GlobalData, !Specs).
:- pred llds_backend_pass_for_proc( module_info::in, module_info::out,
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,
list(error_spec)::in, list(error_spec)::out) is det.
llds_backend_pass_for_proc(!HLDS, ConstStructMap, PredId, PredInfo,
ProcId, !.ProcInfo, CProc, !GlobalData, !Specs) :-
PredProcId = proc(PredId, ProcId),
module_info_get_globals(!.HLDS, Globals),
globals.lookup_bool_option(Globals, optimize_saved_vars_const,
SavedVarsConst),
(
SavedVarsConst = yes,
saved_vars_proc(proc(PredId, ProcId), !ProcInfo, !HLDS)
;
SavedVarsConst = no
),
globals.lookup_bool_option(Globals, optimize_saved_vars_cell,
SavedVarsCell),
(
SavedVarsCell = yes,
stack_opt_cell(PredProcId, !ProcInfo, !HLDS)
;
SavedVarsCell = no
),
globals.lookup_bool_option(Globals, follow_code, FollowCode),
(
FollowCode = yes,
move_follow_code_in_proc(PredProcId, !ProcInfo, !HLDS)
;
FollowCode = no
),
find_simplify_tasks(no, Globals, SimplifyTasks0),
SimpList0 = simplify_tasks_to_list(SimplifyTasks0),
globals.lookup_bool_option(Globals, constant_propagation, ConstProp),
globals.lookup_bool_option(Globals, profile_deep, DeepProf),
globals.lookup_bool_option(Globals, record_term_sizes_as_words, TSWProf),
globals.lookup_bool_option(Globals, record_term_sizes_as_cells, TSCProf),
ProfTrans = bool.or_list([DeepProf, TSWProf, TSCProf]),
% Don't run constant propagation if any of the profiling
% transformations has been applied.
%
% NOTE: Any changes here may also need to be made to
% mercury_compile.simplify.
( if
ConstProp = yes,
ProfTrans = no
then
list.cons(simptask_constant_prop, SimpList0, SimpList1)
else
SimpList1 = list.delete_all(SimpList0, simptask_constant_prop)
),
SimpList = [simptask_mark_code_model_changes,
simptask_elim_removable_scopes | SimpList1],
SimplifyTasks = list_to_simplify_tasks(SimpList),
trace [io(!IO)] (
write_proc_progress_message("% Simplifying ", PredId, ProcId,
!.HLDS, !IO)
),
simplify_proc(SimplifyTasks, PredId, ProcId, !HLDS, !ProcInfo),
trace [io(!IO)] (
write_proc_progress_message("% Computing liveness in ", PredId, ProcId,
!.HLDS, !IO)
),
detect_liveness_proc(!.HLDS, PredProcId, !ProcInfo),
trace [io(!IO)] (
write_proc_progress_message(
"% Marking directly tail recursive calls in ", PredId, ProcId,
!.HLDS, !IO)
),
mark_tail_calls_in_proc(!.HLDS, PredProcId, PredInfo, TCallSpecs,
_, !ProcInfo),
!:Specs = TCallSpecs ++ !.Specs,
trace [io(!IO)] (
write_proc_progress_message("% Allocating stack slots in ", PredId,
ProcId, !.HLDS, !IO)
),
allocate_stack_slots_in_proc(!.HLDS, PredProcId, !ProcInfo),
trace [io(!IO)] (
write_proc_progress_message(
"% Allocating storage locations for live vars in ",
PredId, ProcId, !.HLDS, !IO)
),
allocate_store_maps(final_allocation, !.HLDS, PredProcId, !ProcInfo),
trace [io(!IO)] (
write_proc_progress_message("% Generating low-level (LLDS) code for ",
PredId, ProcId, !.HLDS, !IO)
),
generate_proc_code(!.HLDS, ConstStructMap, PredId, PredInfo,
ProcId, !.ProcInfo, CProc0, !GlobalData),
globals.lookup_bool_option(Globals, optimize, Optimize),
(
Optimize = yes,
optimize_proc(Globals, !.GlobalData, CProc0, CProc)
;
Optimize = no,
CProc = CProc0
),
trace [io(!IO)] (
write_proc_progress_message(
"% Generating call continuation information for ",
PredId, ProcId, !.HLDS, !IO)
),
maybe_collect_call_continuations_in_cproc(!.HLDS, CProc, !GlobalData).
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
%
% The various passes of the LLDS backend.
%
map_args_to_regs(Verbose, Stats, !HLDS, !IO) :-
maybe_write_string(Verbose, "% Mapping args to regs...", !IO),
maybe_flush_output(Verbose, !IO),
generate_arg_info(!HLDS),
maybe_write_string(Verbose, " done.\n", !IO),
maybe_report_stats(Stats, !IO).
:- pred maybe_saved_vars(bool::in, bool::in,
module_info::in, module_info::out, io::di, io::uo) is det.
maybe_saved_vars(Verbose, Stats, !HLDS, !IO) :-
module_info_get_globals(!.HLDS, Globals),
globals.lookup_bool_option(Globals, optimize_saved_vars_const, SavedVars),
(
SavedVars = yes,
maybe_write_string(Verbose,
"% Minimizing variable saves using constants...\n", !IO),
maybe_flush_output(Verbose, !IO),
process_all_nonimported_procs(update_module(saved_vars_proc), !HLDS),
maybe_write_string(Verbose, "% done.\n", !IO),
maybe_report_stats(Stats, !IO)
;
SavedVars = no
).
:- pred maybe_stack_opt(bool::in, bool::in,
module_info::in, module_info::out, io::di, io::uo) is det.
maybe_stack_opt(Verbose, Stats, !HLDS, !IO) :-
module_info_get_globals(!.HLDS, Globals),
globals.lookup_bool_option(Globals, optimize_saved_vars_cell, SavedVars),
(
SavedVars = yes,
maybe_write_string(Verbose,
"% Minimizing variable saves using cells...\n", !IO),
maybe_flush_output(Verbose, !IO),
process_all_nonimported_procs(update_module(stack_opt_cell), !HLDS),
maybe_write_string(Verbose, "% done.\n", !IO),
maybe_report_stats(Stats, !IO)
;
SavedVars = no
).
:- pred maybe_followcode(bool::in, bool::in,
module_info::in, module_info::out, io::di, io::uo) is det.
maybe_followcode(Verbose, Stats, !HLDS, !IO) :-
module_info_get_globals(!.HLDS, Globals),
globals.lookup_bool_option(Globals, follow_code, FollowCode),
(
FollowCode = yes,
maybe_write_string(Verbose, "% Migrating branch code...", !IO),
maybe_flush_output(Verbose, !IO),
process_all_nonimported_procs(update_module(move_follow_code_in_proc),
!HLDS),
maybe_write_string(Verbose, " done.\n", !IO),
maybe_report_stats(Stats, !IO)
;
FollowCode = no
).
:- pred compute_liveness(bool::in, bool::in,
module_info::in, module_info::out, io::di, io::uo) is det.
compute_liveness(Verbose, Stats, !HLDS, !IO) :-
module_info_get_globals(!.HLDS, Globals),
globals.lookup_bool_option(Globals, parallel_liveness, ParallelLiveness),
globals.lookup_int_option(Globals, debug_liveness, DebugLiveness),
maybe_write_string(Verbose, "% Computing liveness...\n", !IO),
maybe_flush_output(Verbose, !IO),
( if
ParallelLiveness = yes,
DebugLiveness = -1
then
detect_liveness_preds_parallel(!HLDS)
else
process_all_nonimported_procs(update_proc_ids(detect_liveness_proc),
!HLDS)
),
maybe_write_string(Verbose, "% done.\n", !IO),
maybe_report_stats(Stats, !IO).
:- pred maybe_mark_tail_rec_calls(bool::in, bool::in,
module_info::in, module_info::out,
list(error_spec)::in, list(error_spec)::out, io::di, io::uo) is det.
maybe_mark_tail_rec_calls(Verbose, Stats, !HLDS, !Specs, !IO) :-
maybe_write_string(Verbose,
"% Marking directly tail recursive calls...", !IO),
maybe_flush_output(Verbose, !IO),
process_all_nonimported_preds_errors(
update_pred_error(mark_tail_calls_in_pred),
!HLDS, !Specs, !IO),
maybe_write_string(Verbose, " done.\n", !IO),
maybe_report_stats(Stats, !IO).
:- pred compute_stack_vars(bool::in, bool::in,
module_info::in, module_info::out, io::di, io::uo) is det.
compute_stack_vars(Verbose, Stats, !HLDS, !IO) :-
maybe_write_string(Verbose, "% Computing stack vars...", !IO),
maybe_flush_output(Verbose, !IO),
process_all_nonimported_procs(
update_proc_ids(allocate_stack_slots_in_proc), !HLDS),
maybe_write_string(Verbose, " done.\n", !IO),
maybe_report_stats(Stats, !IO).
:- pred allocate_store_map(bool::in, bool::in,
module_info::in, module_info::out, io::di, io::uo) is det.
allocate_store_map(Verbose, Stats, !HLDS, !IO) :-
maybe_write_string(Verbose, "% Allocating store map...", !IO),
maybe_flush_output(Verbose, !IO),
process_all_nonimported_procs(
update_proc_ids(allocate_store_maps(final_allocation)), !HLDS),
maybe_write_string(Verbose, " done.\n", !IO),
maybe_report_stats(Stats, !IO).
:- pred generate_llds_code_for_module(module_info::in, bool::in, bool::in,
global_data::in, global_data::out, list(c_procedure)::out,
io::di, io::uo) is det.
generate_llds_code_for_module(HLDS, Verbose, Stats, !GlobalData, LLDS, !IO) :-
maybe_write_string(Verbose, "% Generating code...\n", !IO),
maybe_flush_output(Verbose, !IO),
generate_module_code(HLDS, LLDS, !GlobalData),
maybe_write_string(Verbose, "% done.\n", !IO),
maybe_report_stats(Stats, !IO).
:- pred maybe_optimize_llds(module_info::in, global_data::in,
bool::in, bool::in, list(c_procedure)::in, list(c_procedure)::out,
io::di, io::uo) is det.
maybe_optimize_llds(HLDS, GlobalData, Verbose, Stats, !LLDS, !IO) :-
module_info_get_globals(HLDS, Globals),
globals.lookup_bool_option(Globals, optimize, Optimize),
(
Optimize = yes,
maybe_write_string(Verbose, "% Doing optimizations...\n", !IO),
maybe_flush_output(Verbose, !IO),
optimize_procs(Globals, GlobalData, !LLDS),
maybe_write_string(Verbose, "% done.\n", !IO),
maybe_report_stats(Stats, !IO)
;
Optimize = no
).
:- pred maybe_generate_stack_layouts(module_info::in, list(c_procedure)::in,
bool::in, bool::in, global_data::in, global_data::out, io::di, io::uo)
is det.
maybe_generate_stack_layouts(HLDS, LLDS, Verbose, Stats, !GlobalData, !IO) :-
maybe_write_string(Verbose,
"% Generating call continuation information...", !IO),
maybe_flush_output(Verbose, !IO),
maybe_collect_call_continuations_in_cprocs(HLDS, LLDS, !GlobalData),
maybe_write_string(Verbose, " done.\n", !IO),
maybe_report_stats(Stats, !IO).
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
llds_output_pass(HLDS, GlobalData0, Procs, ModuleName, Succeeded,
FactTableObjFiles, !IO) :-
module_info_get_globals(HLDS, Globals),
globals.lookup_bool_option(Globals, verbose, Verbose),
globals.lookup_bool_option(Globals, statistics, Stats),
maybe_write_string(Verbose,
"% Generating layout data...", !IO),
maybe_flush_output(Verbose, !IO),
% Here we generate the LLDS representations for various data structures
% used for RTTI, type classes, and stack layouts.
% XXX This should perhaps be part of backend_pass rather than output_pass.
type_ctor_info.generate_rtti(HLDS, TypeCtorRttiData),
generate_base_typeclass_info_rtti(HLDS, OldTypeClassInfoRttiData),
globals.lookup_bool_option(Globals, new_type_class_rtti, NewTypeClassRtti),
generate_type_class_info_rtti(HLDS, NewTypeClassRtti,
NewTypeClassInfoRttiData),
TypeClassInfoRttiData =
OldTypeClassInfoRttiData ++ NewTypeClassInfoRttiData,
stack_layout.generate_llds_layout_data(HLDS, GlobalData0, GlobalData,
PseudoTypeInfos, HLDSVarNums, ShortLocns, LongLocns,
UserEventVarNums, UserEvents,
NoVarLabelLayouts, SVarLabelLayouts, LVarLabelLayouts,
InternalLabelToLayoutMap, ProcLabelToLayoutMap,
CallSites, CoveragePoints, ProcStatics,
ProcHeadVarNums, ProcVarNames, ProcBodyBytecodes,
TableIoEntries, TableIoEntryMap, ProcEventLayouts,
ExecTraces, ProcLayoutDatas, ModuleLayoutDatas),
maybe_write_string(Verbose, " done.\n", !IO),
maybe_report_stats(Stats, !IO),
% Here we perform some optimizations on the LLDS data.
% XXX This should perhaps be part of backend_pass rather than output_pass.
% XXX We assume that the foreign language we use is C.
llds_get_c_interface_info(HLDS, lang_c, C_InterfaceInfo),
global_data_get_all_proc_vars(GlobalData, TablingInfoStructs),
global_data_get_all_closure_layouts(GlobalData, ClosureLayoutDatas),
global_data_get_static_cell_info(GlobalData, StaticCellInfo),
get_static_cells(StaticCellInfo,
ScalarCommonCellDatas, VectorCommonCellDatas),
global_data_get_all_alloc_sites(GlobalData, AllocSites, AllocIdMap),
global_data_get_threadscope_string_table(GlobalData, TSStringTable),
% Next we put it all together and output it to one or more C files.
RttiDatas = TypeCtorRttiData ++ TypeClassInfoRttiData,
module_info_get_complexity_proc_infos(HLDS, ComplexityProcs),
C_InterfaceInfo = foreign_interface_info(ModuleSymName,
C_HeaderCodes0, C_BodyCodes, C_Includes,
_C_ExportDecls, C_ExportDefns),
MangledModuleName = sym_name_mangle(ModuleSymName),
CModuleName = MangledModuleName ++ "_module",
% Split the code up into bite-size chunks for the C compiler.
%
globals.lookup_int_option(Globals, procs_per_c_function, ProcsPerFunc),
( if ProcsPerFunc = 0 then
% ProcsPerFunc = 0 really means infinity -
% we store all the procs in a single function.
ChunkedModules = [comp_gen_c_module(CModuleName, Procs)]
else
list.chunk(Procs, ProcsPerFunc, ChunkedProcs),
combine_chunks(ChunkedProcs, CModuleName, ChunkedModules)
),
list.map_foldl(make_foreign_import_header_code(Globals), C_Includes,
C_IncludeHeaderCodes, !IO),
% We don't want to put C_LocalHeaderCodes between Start and End, because
% C_IncludeHeaderCodes may include our own header file, which defines
% the module's guard macro, which in turn #ifdefs out the stuff between
% Start and End.
list.filter(foreign_decl_code_is_local, C_HeaderCodes0,
C_LocalHeaderCodes, C_ExportedHeaderCodes),
make_decl_guards(ModuleSymName, Start, End),
C_HeaderCodes = C_IncludeHeaderCodes ++ C_LocalHeaderCodes ++
[Start | C_ExportedHeaderCodes] ++ [End],
module_info_user_init_pred_c_names(HLDS, UserInitPredCNames),
module_info_user_final_pred_c_names(HLDS, UserFinalPredCNames),
CFile = c_file(ModuleSymName, C_HeaderCodes, C_BodyCodes, C_ExportDefns,
TablingInfoStructs, ScalarCommonCellDatas, VectorCommonCellDatas,
RttiDatas, PseudoTypeInfos, HLDSVarNums, ShortLocns, LongLocns,
UserEventVarNums, UserEvents,
NoVarLabelLayouts, SVarLabelLayouts, LVarLabelLayouts,
InternalLabelToLayoutMap, ProcLabelToLayoutMap,
CallSites, CoveragePoints, ProcStatics,
ProcHeadVarNums, ProcVarNames, ProcBodyBytecodes, TSStringTable,
TableIoEntries, TableIoEntryMap, ProcEventLayouts, ExecTraces,
ProcLayoutDatas, ModuleLayoutDatas, ClosureLayoutDatas,
AllocSites, AllocIdMap, ChunkedModules,
UserInitPredCNames, UserFinalPredCNames, ComplexityProcs),
output_llds_file(Globals, CFile, TargetCodeSucceeded, !IO),
(
TargetCodeSucceeded = yes,
C_InterfaceInfo = foreign_interface_info(_, _, _, _, C_ExportDecls, _),
export.produce_header_file(HLDS, C_ExportDecls, ModuleName, !IO),
% Finally we invoke the C compiler to compile it.
globals.lookup_bool_option(Globals, target_code_only, TargetCodeOnly),
(
TargetCodeOnly = no,
io.output_stream(OutputStream, !IO),
llds_c_to_obj(Globals, OutputStream, ModuleName, CompileOK, !IO),
module_get_fact_table_file_names(HLDS, FactTableBaseFiles),
list.map2_foldl(compile_fact_table_file(Globals, OutputStream),
FactTableBaseFiles, FactTableObjFiles, FactTableCompileOKs,
!IO),
bool.and_list([CompileOK | FactTableCompileOKs], Succeeded),
maybe_set_exit_status(Succeeded, !IO)
;
TargetCodeOnly = yes,
Succeeded = yes,
FactTableObjFiles = []
)
;
TargetCodeSucceeded = no,
Succeeded = no,
FactTableObjFiles = []
).
% Foreign_interface_info holds information used when generating
% code that uses the foreign language interface.
%
:- type foreign_interface_info
---> foreign_interface_info(
module_name,
% Info about stuff imported from C:
list(foreign_decl_code),
list(foreign_body_code),
list(foreign_import_module_info),
% Info about stuff exported to C:
foreign_export_decls,
foreign_export_defns
).
% Gather together the information from the HLDS, given the foreign
% language we are going to use, that is used for the foreign language
% interface.
% This stuff mostly just gets passed directly to the LLDS unchanged, but
% we do do a bit of code generation -- for example, we call
% export.get_foreign_export_{decls,defns} here, which do the generation
% of C code for `pragma foreign_export' declarations.
%
:- pred llds_get_c_interface_info(module_info::in, foreign_language::in,
foreign_interface_info::out) is det.
llds_get_c_interface_info(HLDS, UseForeignLanguage, ForeignInterfaceInfo) :-
module_info_get_name(HLDS, ModuleName),
ForeignSelfImport =
foreign_import_module_info(UseForeignLanguage, ModuleName),
module_info_get_foreign_decl_codes(HLDS, ForeignDeclCodeCord),
module_info_get_foreign_body_codes(HLDS, ForeignBodyCodeCord),
module_info_get_foreign_import_modules(HLDS, ForeignImportsModules0),
ForeignDeclCodes = cord.list(ForeignDeclCodeCord),
ForeignBodyCodes = cord.list(ForeignBodyCodeCord),
add_foreign_import_module_info(ForeignSelfImport,
ForeignImportsModules0, ForeignImportsModules),
% Always include the module we are compiling amongst the foreign import
% modules so that pragma foreign_exported procedures are visible to
% foreign code in this module.
%
% XXX The frontend should really handle this but it is quite inconsistent
% in its treatment of self-imports. Both this backend (the LLDS)
% and the MLDS backend currently handle self foreign imports directly.
foreign.filter_decls(UseForeignLanguage, ForeignDeclCodes,
WantedForeignDeclCodes, _OtherDeclCodes),
foreign.filter_bodys(UseForeignLanguage, ForeignBodyCodes,
WantedForeignBodyCodes, _OtherBodyCodes),
WantedForeignImports = set.to_sorted_list(
get_lang_foreign_import_module_infos(ForeignImportsModules,
UseForeignLanguage)),
export.get_foreign_export_decls(HLDS, ForeignExportDecls),
export.get_foreign_export_defns(HLDS, ForeignExportDefns),
ForeignInterfaceInfo = foreign_interface_info(ModuleName,
WantedForeignDeclCodes, WantedForeignBodyCodes,
WantedForeignImports, ForeignExportDecls, ForeignExportDefns).
:- pred foreign_decl_code_is_local(foreign_decl_code::in) is semidet.
foreign_decl_code_is_local(Decl) :-
Decl = foreign_decl_code(_, foreign_decl_is_local, _, _).
:- pred make_decl_guards(sym_name::in, foreign_decl_code::out,
foreign_decl_code::out) is det.
make_decl_guards(ModuleName, StartGuard, EndGuard) :-
Define = decl_guard(ModuleName),
Start = "#ifndef " ++ Define ++ "\n#define " ++ Define ++ "\n",
End = "\n#endif",
StartGuard = foreign_decl_code(lang_c, foreign_decl_is_exported,
literal(Start), term.context_init),
EndGuard = foreign_decl_code(lang_c, foreign_decl_is_exported,
literal(End), term.context_init).
:- pred make_foreign_import_header_code(globals::in,
foreign_import_module_info::in, foreign_decl_code::out,
io::di, io::uo) is det.
make_foreign_import_header_code(Globals, ForeignImportModule, Include, !IO) :-
ForeignImportModule = foreign_import_module_info(Lang, ModuleName),
(
Lang = lang_c,
module_name_to_search_file_name(Globals, ModuleName, ".mh",
HeaderFileName, !IO),
IncludeString = "#include """ ++ HeaderFileName ++ """\n",
Include = foreign_decl_code(lang_c, foreign_decl_is_exported,
literal(IncludeString), term.context_init)
;
Lang = lang_csharp,
sorry($module, $pred, ":- import_module not yet implemented: " ++
"`:- pragma foreign_import_module' for C#")
;
Lang = lang_java,
sorry($module, $pred, ":- import_module not yet implemented: " ++
"`:- pragma foreign_import_module' for Java")
;
Lang = lang_erlang,
sorry($module, $pred, ":- import_module not yet implemented: " ++
"`:- pragma foreign_import_module' for Erlang")
).
:- pred combine_chunks(list(list(c_procedure))::in, string::in,
list(comp_gen_c_module)::out) is det.
combine_chunks(ChunkList, ModName, Modules) :-
combine_chunks_2(ChunkList, ModName, 0, Modules).
:- pred combine_chunks_2(list(list(c_procedure))::in,
string::in, int::in, list(comp_gen_c_module)::out) is det.
combine_chunks_2([], _ModName, _N, []).
combine_chunks_2([Chunk | Chunks], ModuleName, Num, [Module | Modules]) :-
string.int_to_string(Num, NumString),
ThisModuleName = ModuleName ++ NumString,
Module = comp_gen_c_module(ThisModuleName, Chunk),
Num1 = Num + 1,
combine_chunks_2(Chunks, ModuleName, Num1, Modules).
:- pred output_llds_file(globals::in, c_file::in, bool::out, io::di, io::uo)
is det.
output_llds_file(Globals, LLDS0, Succeeded, !IO) :-
transform_llds(Globals, LLDS0, LLDS),
output_llds(Globals, LLDS, Succeeded, !IO).
:- pred llds_c_to_obj(globals::in, io.output_stream::in, module_name::in,
bool::out, io::di, io::uo) is det.
llds_c_to_obj(Globals, ErrorStream, ModuleName, Succeeded, !IO) :-
get_linked_target_type(Globals, LinkedTargetType),
get_object_code_type(Globals, LinkedTargetType, PIC),
maybe_pic_object_file_extension(Globals, PIC, Obj),
module_name_to_file_name(Globals, ModuleName, ".c", do_not_create_dirs,
C_File, !IO),
module_name_to_file_name(Globals, ModuleName, Obj, do_create_dirs,
O_File, !IO),
compile_target_code.do_compile_c_file(Globals, ErrorStream, PIC,
C_File, O_File, Succeeded, !IO).
:- pred compile_fact_table_file(globals::in, io.output_stream::in, string::in,
string::out, bool::out, io::di, io::uo) is det.
compile_fact_table_file(Globals, ErrorStream, BaseName, O_File, Succeeded,
!IO) :-
get_linked_target_type(Globals, LinkedTargetType),
get_object_code_type(Globals, LinkedTargetType, PIC),
maybe_pic_object_file_extension(Globals, PIC, Obj),
C_File = BaseName ++ ".c",
O_File = BaseName ++ Obj,
compile_target_code.do_compile_c_file(Globals, ErrorStream, PIC,
C_File, O_File, Succeeded, !IO).
%---------------------------------------------------------------------------%
:- end_module top_level.mercury_compile_llds_back_end.
%---------------------------------------------------------------------------%