mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-15 09:23:44 +00:00
mdbcomp/feedback.feedback_info.m:
mdbcomp/feedback.m:
Move all the contents of feedback.m other than include_module items
into the new module feedback_info.m, leaving feedback.m a package.
mdbcomp/feedback.automatic_parallelism.m:
Group the types exported by this module into groups of related types.
Delete a totally unused type.
deep_profiler/Mmakefile:
slice/Mmakefile:
Copy the new module.
compiler/check_options.m:
compiler/globals.m:
compiler/handle_options.m:
compiler/introduce_parallelism.m:
deep_profiler/autopar_reports.m:
deep_profiler/autopar_search_callgraph.m:
deep_profiler/mdprof_create_feedback.m:
deep_profiler/mdprof_report_feedback.m:
Import the new module.
592 lines
23 KiB
Mathematica
592 lines
23 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 2008-2011 The University of Melbourne.
|
|
% Copyright (C) 2014-2015, 2017-2018, 2021, 2023, 2025 The Mercury team.
|
|
% This file is distributed under the terms specified in COPYING.LIB.
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% File: feedback.m.
|
|
% Main author: pbone.
|
|
%
|
|
% This module defines data structures for representing feedback information
|
|
% in Mercury code, as well as procedures for reading and writing the feedback
|
|
% files that represent such information on disk.
|
|
%
|
|
% This module is included both in the compiler and in the tools that
|
|
% generate this feedback data.
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module mdbcomp.feedback.feedback_info.
|
|
|
|
:- interface.
|
|
|
|
:- import_module mdbcomp.feedback.automatic_parallelism.
|
|
:- import_module mdbcomp.program_representation.
|
|
|
|
:- import_module assoc_list.
|
|
:- import_module io.
|
|
:- import_module maybe.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% The feedback_info type stores the data that may be fed back
|
|
% into the compiler. For a detailed description, see the comment
|
|
% on the non-abstract definition in the implementation section.
|
|
%
|
|
:- type feedback_info.
|
|
|
|
% init_feedback_info(ProfiledProgramName) = FeedbackInfo:
|
|
%
|
|
% Create a new empty feedback info structure, recording that it is
|
|
% intended to hold feedback information for the program with the given
|
|
% name.
|
|
%
|
|
% XXX The predicates that add information to feedback_infos now require
|
|
% their callers to specify what profiled program their information is for,
|
|
% and this is checked against the profiled program name in the
|
|
% feedback_info. We could instead initialize feedback_infos *without*
|
|
% storing the program name, and record that name on the first update
|
|
% of the feedback_info instead. This would remove one source of possible
|
|
% name mismatches.
|
|
%
|
|
:- func init_feedback_info(string) = feedback_info.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% The kinds of information that we can record in a feedback file.
|
|
%
|
|
% Historically, we also supported a feedback type that was used by
|
|
% Jerome Tannier's attempt at discovering a useful set of conjunctions
|
|
% to parallelise. However, this early attempt at automatic parallelisation
|
|
% yielded results that were much inferior to our current system, so it
|
|
% should interest only historians. The code supporting Tanner's feedback
|
|
% type was removed from the feedback system on 2014 december 1;
|
|
% if you want it, look in the git archives for commits on that date.
|
|
%
|
|
|
|
% Values of this type represent a list of candidate conjunctions
|
|
% for implicit parallelism.
|
|
%
|
|
:- type feedback_info_candidate_parallel_conjunctions
|
|
---> feedback_info_candidate_parallel_conjunctions(
|
|
cpc_parameters :: candidate_par_conjunctions_params,
|
|
|
|
% For each procedure that has some candidate parallel
|
|
% conjunctions, list those candidates.
|
|
cpc_conjunctions :: assoc_list(string_proc_label,
|
|
candidate_par_conjunctions_proc)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% The getter predicates of feedback_info.
|
|
%
|
|
|
|
% Get the name of the program whose profiled execution the given
|
|
% feedback_info was derived from.
|
|
%
|
|
:- func get_feedback_profiled_program_name(feedback_info) = string.
|
|
|
|
% get_feedback_*(Info) = Data:
|
|
%
|
|
% Get any feedback data of the given kind from the given feedback_info.
|
|
%
|
|
:- func get_feedback_candidate_parallel_conjunctions(feedback_info) =
|
|
maybe(feedback_info_candidate_parallel_conjunctions).
|
|
|
|
% Get all the information held in the given feedback info. Callers should
|
|
% call this predicate, instead of the ones above, if they want to guarantee
|
|
% that even if the feedback_info type is updated, they will still get
|
|
% all the information present in the given feedback_info.
|
|
%
|
|
:- pred get_all_feedback_info(feedback_info::in,
|
|
string::out, maybe(feedback_info_candidate_parallel_conjunctions)::out)
|
|
is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% The setter predicates of feedback_info.
|
|
%
|
|
|
|
% add_feedback_*(ProfiledProgramName, Data, !Info)
|
|
%
|
|
% Put Data into the selected field of the feedback_info, which must hold
|
|
% information about ProfiledProgramName. Requires the old feedback_info
|
|
% to have no previous information in that field.
|
|
%
|
|
:- pred add_feedback_candidate_parallel_conjunctions(string::in,
|
|
feedback_info_candidate_parallel_conjunctions::in,
|
|
feedback_info::in, feedback_info::out) is det.
|
|
|
|
% replace_feedback_*(ProfiledProgramName, Data, !Info)
|
|
%
|
|
% Put Data into the selected field of the feedback_info, which must hold
|
|
% information about ProfiledProgramName. Requires the old feedback_info
|
|
% Any previous information in that field of feedback_info
|
|
% is replaced by Data.
|
|
%
|
|
:- pred replace_feedback_candidate_parallel_conjunctions(string::in,
|
|
feedback_info_candidate_parallel_conjunctions::in,
|
|
feedback_info::in, feedback_info::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Reading in feedback files.
|
|
%
|
|
|
|
:- type feedback_read_result(T) == maybe_error(T, feedback_read_error).
|
|
|
|
:- type feedback_read_error
|
|
---> fre_open_error(io.error)
|
|
; fre_read_error(io.error)
|
|
; fre_parse_error(
|
|
fre_pe_message :: string,
|
|
fre_pe_line_no :: int
|
|
)
|
|
; fre_unexpected_eof
|
|
; fre_incorrect_version(string)
|
|
; fre_incorrect_first_line
|
|
; fre_incorrect_profiled_program_name(
|
|
fre_ippn_expected :: string,
|
|
fre_ippn_got :: string
|
|
)
|
|
; fre_repeated_component(
|
|
fre_component_name :: string
|
|
).
|
|
|
|
% feedback_read_error_message_string(File, Error, Message):
|
|
%
|
|
% Create a string describing the read error.
|
|
%
|
|
:- pred feedback_read_error_message_string(string::in, feedback_read_error::in,
|
|
string::out) is det.
|
|
|
|
% read_or_create_feedback_file(Path, ProfiledProgramName, Result, !IO):
|
|
%
|
|
% If Path stores a feedback file for ProfiledProgramName, read it in.
|
|
% If it does not exist, return an empty feedback state for
|
|
% ProfiledProgramName, and return that. Return an error if Path does exist,
|
|
% but either does not contain a valid feedback file, or contains a valid
|
|
% feedback file for some other profiled program.
|
|
%
|
|
% ProfiledProgramName is the name of the program whose profiled execution
|
|
% the feedback file was (or should be) generated from.
|
|
% We record this to avoid mixing the feedback information of unrelated
|
|
% executables.
|
|
%
|
|
:- pred read_or_create_feedback_file(string::in, string::in,
|
|
feedback_read_result(feedback_info)::out, io::di, io::uo) is det.
|
|
|
|
% read_feedback_file(Path, MaybeProfiledProgramName, FeedbackInfo, !IO):
|
|
%
|
|
% This predicate attempts to read in feedback data from Path. If
|
|
% MaybeProfiledProgramName is yes(ProfiledProgramName), generate an
|
|
% error if the feedback data is not for ProfiledProgramName.
|
|
%
|
|
% This predicate should be called once per compiler invocation.
|
|
%
|
|
:- pred read_feedback_file(string::in, maybe(string)::in,
|
|
feedback_read_result(feedback_info)::out, io::di, io::uo) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Writing out feedback files.
|
|
%
|
|
|
|
:- type feedback_write_result
|
|
---> fwr_ok
|
|
; fwr_open_error(io.error)
|
|
; fwr_write_error(io.error).
|
|
|
|
% write_feedback_file(Path, FeedbackInfo, FeedbackWriteResult, !IO):
|
|
%
|
|
% Write out the feedback data in FeedbackWriteResult to Path.
|
|
%
|
|
:- pred write_feedback_file(string::in, feedback_info::in,
|
|
feedback_write_result::out, io::di, io::uo) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module list.
|
|
:- import_module require.
|
|
:- import_module string.
|
|
|
|
% There are several kinds of information that we may be interested in
|
|
% feeding back to the compiler. Our design for representing feedback
|
|
% information allows for a tool to generate an arbitrary subset of
|
|
% the possible kinds of information. A feedback-generating tool will
|
|
% put each kind of information that it generates in its assigned slot
|
|
% with a yes(...) wrapped around it, while it will put a "no" in all
|
|
% other slots.
|
|
%
|
|
% If you want add a new kind of feedback information, you will need to
|
|
%
|
|
% - add a new maybe field to the feedback_info type;
|
|
% - add a getter predicate and two setter predicates for that field;
|
|
% - update the init_feedback_info predicate;
|
|
% - update the get_all_feedback_info predicate;
|
|
% - update the print_feedback_report predicate;
|
|
% - add a new alternative to the feedback_component_wrapper type;
|
|
% - add code to add_feedback_components to read in the new kind of
|
|
% information;
|
|
% - add code to actually_write_feedback_file to write out the new kind of
|
|
% information;
|
|
% - increment the file format version number at the bottom of this file.
|
|
%
|
|
% You will also need to increment the file format version number
|
|
% if you change the definition of any of the types referred to, directly
|
|
% or indirectly, by the feedback_component_wrapper type, including the types
|
|
% in mdbcomp.program_representation.
|
|
|
|
:- type feedback_info
|
|
---> feedback_info(
|
|
% The name of the program whose execution generated
|
|
% the profiling data file that the feedback is derived from,
|
|
% and therefore the program whose compilation the feedback
|
|
% is intended for.
|
|
fi_profiled_program_name :: string,
|
|
|
|
% The actual feedback data as read from the feedback file.
|
|
% Should be set to yes(...) iff feedback of the given sort
|
|
% is present in the file.
|
|
fi_maybe_candidate_parallel_conjunctions ::
|
|
maybe(feedback_info_candidate_parallel_conjunctions)
|
|
).
|
|
|
|
:- type feedback_component_wrapper
|
|
---> fcw_candidate_parallel_conjunctions(
|
|
feedback_info_candidate_parallel_conjunctions
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Initialization and getter and setter predicates for feedback_infos.
|
|
%
|
|
|
|
init_feedback_info(ProgramName) = feedback_info(ProgramName, no).
|
|
|
|
get_feedback_profiled_program_name(Info) = Info ^ fi_profiled_program_name.
|
|
get_feedback_candidate_parallel_conjunctions(Info) =
|
|
Info ^ fi_maybe_candidate_parallel_conjunctions.
|
|
|
|
get_all_feedback_info(Info, ProfiledProgramName,
|
|
MaybeCandidateParallelConjs) :-
|
|
Info = feedback_info(ProfiledProgramName, MaybeCandidateParallelConjs).
|
|
|
|
add_feedback_candidate_parallel_conjunctions(ProfiledProgramName, Data,
|
|
!Info) :-
|
|
expect(unify(!.Info ^ fi_profiled_program_name, ProfiledProgramName),
|
|
$pred, "adding candidate parallel conjunctions for wrong program"),
|
|
expect(unify(!.Info ^ fi_maybe_candidate_parallel_conjunctions, no),
|
|
$pred, "overwriting old candidate_parallel_conjunctions data"),
|
|
!Info ^ fi_maybe_candidate_parallel_conjunctions := yes(Data).
|
|
|
|
replace_feedback_candidate_parallel_conjunctions(ProfiledProgramName, Data,
|
|
!Info) :-
|
|
expect(unify(!.Info ^ fi_profiled_program_name, ProfiledProgramName),
|
|
$pred, "replacing candidate parallel conjunctions for wrong program"),
|
|
!Info ^ fi_maybe_candidate_parallel_conjunctions := yes(Data).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Interpreting the errors that can happen when reading in feedback files.
|
|
%
|
|
|
|
feedback_read_error_message_string(File, Error, Message) :-
|
|
(
|
|
( Error = fre_open_error(Code)
|
|
; Error = fre_read_error(Code)
|
|
),
|
|
error_message(Code, MessagePart)
|
|
;
|
|
Error = fre_parse_error(ParseMessage, Line),
|
|
MessagePart = ParseMessage ++ " on line " ++ string(Line)
|
|
;
|
|
Error = fre_unexpected_eof,
|
|
MessagePart = "Unexpected end of file"
|
|
;
|
|
Error = fre_incorrect_version(Expected),
|
|
MessagePart = "Incorrect file format version; expected " ++ Expected
|
|
;
|
|
Error = fre_incorrect_first_line,
|
|
MessagePart = "Incorrect file format"
|
|
;
|
|
Error = fre_incorrect_profiled_program_name(Expected, Got),
|
|
MessagePart =
|
|
"The name of the program the feedback is for didn't match,"
|
|
++ " is this the right feedback file?\n"
|
|
++ string.format("Expected: '%s' Got: '%s'", [s(Expected), s(Got)])
|
|
;
|
|
Error = fre_repeated_component(ComponentName),
|
|
MessagePart = "File contains more than one "
|
|
++ ComponentName ++ " component"
|
|
),
|
|
string.format("%s: %s\n", [s(File), s(MessagePart)], Message).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Reading feedback files.
|
|
%
|
|
|
|
read_or_create_feedback_file(Path, ExpectedProfiledProgramName,
|
|
FeedbackResult, !IO) :-
|
|
read_feedback_file(Path, yes(ExpectedProfiledProgramName),
|
|
ReadResult, !IO),
|
|
(
|
|
ReadResult = ok(_Feedback),
|
|
FeedbackResult = ReadResult
|
|
;
|
|
ReadResult = error(Error),
|
|
(
|
|
% XXX We assume that an open error is probably caused by the file
|
|
% not existing, but we can't be sure because io.error is a string,
|
|
% and the message string for any error may change.
|
|
% XXX Given that ReadResult is given to us by the predicate
|
|
% just below, we could get it to tell us *directly* whether
|
|
% the error is caused by trying to open a non-existent file.
|
|
Error = fre_open_error(_),
|
|
FeedbackResult = ok(
|
|
init_feedback_info(ExpectedProfiledProgramName))
|
|
;
|
|
( Error = fre_read_error(_)
|
|
; Error = fre_parse_error(_, _)
|
|
; Error = fre_unexpected_eof
|
|
; Error = fre_incorrect_version(_)
|
|
; Error = fre_incorrect_first_line
|
|
; Error = fre_incorrect_profiled_program_name(_, _)
|
|
; Error = fre_repeated_component(_)
|
|
),
|
|
FeedbackResult = ReadResult
|
|
)
|
|
).
|
|
|
|
read_feedback_file(Path, MaybeExpectedProfiledProgramName,
|
|
ResultFeedbackInfo, !IO) :-
|
|
io.open_input(Path, PathResult, !IO),
|
|
(
|
|
PathResult = ok(PathStream),
|
|
some [!MaybeError] (
|
|
% Each predicate we call below stops reading when given
|
|
% !.MaybeError = yes(...).
|
|
!:MaybeError = no,
|
|
read_check_line(PathStream, feedback_first_line,
|
|
fre_incorrect_first_line, !MaybeError, !IO),
|
|
read_check_line(PathStream, feedback_version,
|
|
fre_incorrect_version(feedback_version), !MaybeError, !IO),
|
|
read_profiled_program_name(PathStream,
|
|
MaybeExpectedProfiledProgramName,
|
|
!.MaybeError, MaybeActualProfiledProgram, !IO),
|
|
read_all_feedback_data(PathStream, MaybeActualProfiledProgram,
|
|
ResultFeedbackInfo, !IO)
|
|
),
|
|
io.close_input(PathStream, !IO)
|
|
;
|
|
PathResult = error(ErrorCode),
|
|
ResultFeedbackInfo = error(fre_open_error(ErrorCode))
|
|
).
|
|
|
|
% Read and check a line of the file.
|
|
%
|
|
:- pred read_check_line(io.text_input_stream::in, string::in,
|
|
feedback_read_error::in,
|
|
maybe(feedback_read_error)::in, maybe(feedback_read_error)::out,
|
|
io::di, io::uo) is det.
|
|
|
|
read_check_line(Stream, TestLine, NotMatchError, !MaybeError, !IO) :-
|
|
(
|
|
!.MaybeError = yes(_)
|
|
;
|
|
!.MaybeError = no,
|
|
io.read_line_as_string(Stream, LineResult, !IO),
|
|
(
|
|
LineResult = ok(Line),
|
|
( if
|
|
( Line = TestLine
|
|
; Line = TestLine ++ "\n"
|
|
)
|
|
then
|
|
!:MaybeError = no
|
|
else
|
|
!:MaybeError = yes(NotMatchError)
|
|
)
|
|
;
|
|
LineResult = eof,
|
|
!:MaybeError = yes(fre_unexpected_eof)
|
|
;
|
|
LineResult = error(Error),
|
|
!:MaybeError = yes(fre_read_error(Error))
|
|
)
|
|
).
|
|
|
|
:- pred read_profiled_program_name(io.text_input_stream::in, maybe(string)::in,
|
|
maybe(feedback_read_error)::in,
|
|
maybe_error(string, feedback_read_error)::out, io::di, io::uo) is det.
|
|
|
|
read_profiled_program_name(Stream, MaybeExpectedProfiledProgram,
|
|
MaybeError0, MaybeActualProfiledProgram, !IO) :-
|
|
(
|
|
MaybeError0 = yes(Error),
|
|
MaybeActualProfiledProgram = error(Error)
|
|
;
|
|
MaybeError0 = no,
|
|
io.read_line_as_string(Stream, LineResult, !IO),
|
|
(
|
|
LineResult = ok(String),
|
|
ActualProfiledProgram = string.strip(String),
|
|
(
|
|
MaybeExpectedProfiledProgram = no,
|
|
MaybeActualProfiledProgram = ok(ActualProfiledProgram)
|
|
;
|
|
MaybeExpectedProfiledProgram = yes(ExpectedProfiledProgram),
|
|
( if ActualProfiledProgram = ExpectedProfiledProgram then
|
|
MaybeActualProfiledProgram = ok(ActualProfiledProgram)
|
|
else
|
|
MaybeActualProfiledProgram =
|
|
error(fre_incorrect_profiled_program_name(
|
|
ExpectedProfiledProgram, ActualProfiledProgram))
|
|
)
|
|
)
|
|
;
|
|
LineResult = eof,
|
|
MaybeActualProfiledProgram = error(fre_unexpected_eof)
|
|
;
|
|
LineResult = error(Error),
|
|
MaybeActualProfiledProgram = error(fre_read_error(Error))
|
|
)
|
|
).
|
|
|
|
% Read the feedback data from the file.
|
|
%
|
|
% The feedback data in the file should be a single large term.
|
|
% This term should be a list, each element of which is an identifying
|
|
% wrapper around a feedback component.
|
|
%
|
|
% The overall term is handled by read_all_feedback_data, while
|
|
% the list elements are handled by add_feedback_components.
|
|
%
|
|
:- pred read_all_feedback_data(io.text_input_stream::in,
|
|
maybe_error(string, feedback_read_error)::in,
|
|
feedback_read_result(feedback_info)::out, io::di, io::uo) is det.
|
|
|
|
read_all_feedback_data(Stream, MaybeActualProfiledProgram, Result, !IO) :-
|
|
(
|
|
MaybeActualProfiledProgram = error(Error),
|
|
Result = error(Error)
|
|
;
|
|
MaybeActualProfiledProgram = ok(ActualProfiledProgram),
|
|
% XXX Note that the use of io.read here, and io.write in
|
|
% actually_write_feedback_file below, are accident-prone, because
|
|
% any change to any of the function symbols in the type tree of
|
|
% feedback_info will result in a de-facto change in the format
|
|
% of feedback files, *without this fact necessarily being apparent
|
|
% to the person making the change*, which can thus easily result
|
|
% in the file format version number NOT being incremented.
|
|
io.read(Stream, ReadResult, !IO),
|
|
(
|
|
ReadResult = ok(Components),
|
|
Info0 = init_feedback_info(ActualProfiledProgram),
|
|
add_feedback_components(Components, Info0, Result)
|
|
% XXX We should check there nothing in the input after this term.
|
|
% XXX This would be easier to do if we read in the whole file
|
|
% using io.read_named_file_as_lines, and parsed this last line
|
|
% using read_term_from_string.
|
|
;
|
|
ReadResult = eof,
|
|
Result = error(fre_unexpected_eof)
|
|
;
|
|
ReadResult = error(Error, Line),
|
|
Result = error(fre_parse_error(Error, Line))
|
|
)
|
|
).
|
|
|
|
:- pred add_feedback_components(list(feedback_component_wrapper)::in,
|
|
feedback_info::in, feedback_read_result(feedback_info)::out) is det.
|
|
|
|
add_feedback_components([], !.Info, Result) :-
|
|
Result = ok(!.Info).
|
|
add_feedback_components([Wrapper | Wrappers], !.Info, Result) :-
|
|
(
|
|
Wrapper = fcw_candidate_parallel_conjunctions(Candidates),
|
|
MaybeCandidates0 = !.Info ^ fi_maybe_candidate_parallel_conjunctions,
|
|
(
|
|
MaybeCandidates0 = no,
|
|
!Info ^ fi_maybe_candidate_parallel_conjunctions
|
|
:= yes(Candidates),
|
|
add_feedback_components(Wrappers, !.Info, Result)
|
|
;
|
|
MaybeCandidates0 = yes(_),
|
|
Result = error(fre_repeated_component(
|
|
"candidate_parallel_conjunctions"))
|
|
)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Writing feedback files.
|
|
%
|
|
|
|
write_feedback_file(Path, Feedback, Result, !IO) :-
|
|
io.open_output(Path, PathResult, !IO),
|
|
(
|
|
PathResult = ok(PathStream),
|
|
actually_write_feedback_file(PathStream, Feedback, !IO),
|
|
io.close_output(PathStream, !IO),
|
|
Result = fwr_ok
|
|
;
|
|
PathResult = error(ErrorCode),
|
|
Result = fwr_open_error(ErrorCode)
|
|
).
|
|
|
|
% Write out the data. This is called by try_io to catch any exceptions
|
|
% that close_output and the other predicates we call here (e.g. io.write)
|
|
% may throw.
|
|
% XXX This should NOT be necessary.
|
|
%
|
|
:- pred actually_write_feedback_file(io.text_output_stream::in,
|
|
feedback_info::in, io::di, io::uo) is det.
|
|
|
|
actually_write_feedback_file(Stream, FeedbackInfo, !IO) :-
|
|
FeedbackInfo = feedback_info(ProfiledProgramName,
|
|
MaybeCandidateParallelConjs),
|
|
io.format(Stream, "%s\n%s\n%s\n",
|
|
[s(feedback_first_line), s(feedback_version), s(ProfiledProgramName)],
|
|
!IO),
|
|
% In the future, we expect to support more than one kind of feedback.
|
|
some [!RevComponents] (
|
|
!:RevComponents = [],
|
|
(
|
|
MaybeCandidateParallelConjs = no
|
|
;
|
|
MaybeCandidateParallelConjs = yes(Candidates),
|
|
CandComponent = fcw_candidate_parallel_conjunctions(Candidates),
|
|
!:RevComponents = [CandComponent | !.RevComponents]
|
|
),
|
|
list.reverse(!.RevComponents, Components)
|
|
),
|
|
% XXX See the comment on the corresponding io.read above.
|
|
io.write(Stream, Components, !IO),
|
|
io.write_string(Stream, ".\n", !IO).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% The identifying marks of feedback files.
|
|
%
|
|
|
|
:- func feedback_first_line = string.
|
|
|
|
feedback_first_line = "Mercury Compiler Feedback".
|
|
|
|
:- func feedback_version = string.
|
|
|
|
feedback_version = "19".
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module mdbcomp.feedback.feedback_info.
|
|
%---------------------------------------------------------------------------%
|