mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-15 01:13:30 +00:00
compiler/switch_detection.m:
The existing switch detection algorithm does a single forward
traversal of the procedure body, doing only very limited lookahead.
This prevents it from recognizing some switches. To fix this, add
a new prepass that provides unlimited lookahead.
Add one use of this lookahead information. Later diffs should add
more uses.
compiler/print_help.m:
Fix the code that served as motivation for this change.
It was a block of unifications that was intended to supply
the lookahead that the old algorithm needed but did not have,
but it actually lied: it said that the the following disjunction
covered cons_ids that it actually did not cover. The fact that
the compiler did not diagnose this lie until now was a bug.
(After this diff, the compiler now reports an error.)
deep_profiler/message.m:
Avoid the limitation of the new algorithm that was discussed today
on m-dev.
compiler/options.m:
Add a way to test for the presence of this new capability
in the installed compiler.
tests/warnings/help_text.err_exp:
Expect the new option name.
431 lines
16 KiB
Mathematica
431 lines
16 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 2009-2011 The University of Melbourne.
|
|
% Copyright (C) 2014-2015, 2017-2018, 2023, 2025 The Mercury team.
|
|
% This file may only be copied under the terms of the GNU General
|
|
% Public License - see the file COPYING in the Mercury distribution.
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% File: message.m.
|
|
% Author: pbone.
|
|
%
|
|
% This module contains types and predicates for building messages used by the
|
|
% mdprof_create_feedback tool. These messages can represent information such as
|
|
% warnings and errors. Code is also included here to print them out.
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module message.
|
|
:- interface.
|
|
|
|
:- import_module mdbcomp.
|
|
:- import_module mdbcomp.goal_path.
|
|
:- import_module mdbcomp.program_representation.
|
|
:- import_module profile.
|
|
|
|
:- import_module cord.
|
|
:- import_module io.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% A message to be displayed to the user.
|
|
%
|
|
:- type message
|
|
---> message(
|
|
message_location :: program_location,
|
|
message_type :: message_type
|
|
).
|
|
|
|
% The 'importance' of a message. Debug messages are not covered here since
|
|
% they should be implemented via trace goals. Neither are critical messages
|
|
% since we use exceptions in that case.
|
|
%
|
|
:- type message_level
|
|
---> message_info
|
|
; message_notice
|
|
; message_warning
|
|
; message_error.
|
|
|
|
:- type program_location
|
|
---> pl_proc(string_proc_label)
|
|
; pl_goal(string_proc_label, reverse_goal_path)
|
|
; pl_clique(clique_ptr)
|
|
; pl_csd(call_site_dynamic_ptr).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- func message_get_level(message) = message_level.
|
|
|
|
:- func message_level_to_int(message_level) = int.
|
|
|
|
:- pred message_to_string(message::in, string::out) is det.
|
|
|
|
% location_to_string(IndentLevel, Location, String).
|
|
%
|
|
% Pretty-print a location to a cord of strings.
|
|
%
|
|
:- pred location_to_string(int::in, program_location::in, cord(string)::out)
|
|
is det.
|
|
|
|
:- pred append_message(program_location::in, message_type::in,
|
|
cord(message)::in, cord(message)::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- type message_type
|
|
---> info_found_candidate_conjunction
|
|
% A candidate parallel conjunction has been found.
|
|
|
|
; info_found_conjs_above_callsite_threshold(int)
|
|
% There are a number of conjuncts containing calls above the
|
|
% configured call site threshold, we are considering them for
|
|
% parallelisation against one another.
|
|
|
|
; info_found_n_conjunctions_with_positive_speedup(int)
|
|
% There are N conjunctions whose speedup due to parallelisation
|
|
% is positive.
|
|
|
|
; info_split_conjunction_into_partitions(int)
|
|
% The conjunction being considered for parallelisation had to be
|
|
% split into several 'partitions' because it contains some
|
|
% nonatomic goals; this can limit the amount of parallelism
|
|
% available.
|
|
|
|
; info_found_pushed_conjs_above_callsite_threshold
|
|
% There are two of conjuncts containing calls above the
|
|
% configured call site threshold that can be pushed together,
|
|
% we are considering them for parallelisation against one another.
|
|
|
|
; notice_duplicate_instantiation(
|
|
% This occurs when a variable is instantiated twice in a
|
|
% procedure body (different instantiation states are used).
|
|
% We don't bother parallelising such procedures.
|
|
%
|
|
% The number of conjunctions that could have been
|
|
% parallelised.
|
|
int
|
|
)
|
|
|
|
; notice_callpair_has_more_than_one_dependant_var
|
|
% A pair of calls that could be parallelised have many
|
|
% dependent variables. We don't yet calculate the speedup in
|
|
% these situations.
|
|
|
|
; notice_partition_does_not_have_costly_calls(int, int)
|
|
% A partition does not enough costly calls (>1) and
|
|
% could not be parallelised, we could have parallelised them
|
|
% if we could parallelise over non-atomic code.
|
|
%
|
|
% The parameters are the partition number and the number of
|
|
% costly calls found.
|
|
|
|
; notice_candidate_conjunction_not_det(detism_rep)
|
|
% The candidate conjunction has goals that are not
|
|
% deterministic or cc_multi amongst the costly calls.
|
|
|
|
; warning_cannot_lookup_proc_defn
|
|
% Couldn't find the proc defn in the progrep data, maybe the
|
|
% procedure is built-in.
|
|
|
|
; warning_cannot_compute_procrep_coverage_fallback(string)
|
|
% Couldn't compute the coverage annotation for a procedure
|
|
% representation. A fallback method will be used but without
|
|
% this information it may be less accurate.
|
|
|
|
; warning_cannot_compute_cost_of_recursive_calls(string)
|
|
% Couldn't compute the cost of recursive calls.
|
|
%
|
|
% The parameter contains extra information about this error.
|
|
|
|
; warning_cannot_compute_first_use_time(string)
|
|
% Couldn't compute the time at which a variable is produced
|
|
% or consumed.
|
|
%
|
|
% The parameter contains extra information about this error.
|
|
|
|
; error_extra_proc_dynamics_in_clique_proc
|
|
% We don't yet handle clique_proc_reports with multiple proc
|
|
% dynamics.
|
|
|
|
; error_cannot_lookup_coverage_points
|
|
% An error in the generation of a coverage_procrep report.
|
|
|
|
; error_exception_thrown(string).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Create an indentation of the appropriate amount. Indentation is two
|
|
% spaces per indentation level.
|
|
%
|
|
:- func indent(int) = cord(string).
|
|
|
|
% Create a new line proceeded by an indentation.
|
|
%
|
|
:- func nl_indent(int) = cord(string).
|
|
|
|
% A newline symbol.
|
|
:- func nl = cord(string).
|
|
|
|
% The size of an indentation level. 2 x the input.
|
|
%
|
|
:- func indent_size(int) = int.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Write out messages.
|
|
%
|
|
:- pred write_out_messages(io.text_output_stream::in, cord(message)::in,
|
|
io::di, io::uo) is det.
|
|
|
|
% Set the verbosity level to use above. Higher levels print out more
|
|
% information. Levels are in the inclusive range 0..4.
|
|
%
|
|
:- pred set_verbosity_level(int::in, io::di, io::uo) is det.
|
|
|
|
% The default verbosity level if set_verbosity_level is never called.
|
|
%
|
|
:- func default_verbosity_level = int.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module program_representation_utils.
|
|
|
|
:- import_module int.
|
|
:- import_module list.
|
|
:- import_module require.
|
|
:- import_module string.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
message_get_level(message(_, Type)) =
|
|
message_type_to_level(Type).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
message_level_to_int(message_info) = 4.
|
|
message_level_to_int(message_notice) = 3.
|
|
message_level_to_int(message_warning) = 2.
|
|
message_level_to_int(message_error) = 1.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
message_to_string(message(Location, MessageType), String) :-
|
|
location_to_string(1, Location, LocationString),
|
|
Level = message_type_to_level(MessageType),
|
|
LevelString = message_level_to_string(Level),
|
|
MessageStr = message_type_to_string(MessageType),
|
|
Cord = LevelString ++ singleton(":\n") ++ LocationString ++
|
|
indent(1) ++ MessageStr ++ singleton("\n"),
|
|
append_list(cord.list(Cord), String).
|
|
|
|
location_to_string(Level, Location, String) :-
|
|
(
|
|
Location = pl_proc(ProcLabel),
|
|
print_proc_label_to_string(ProcLabel, ProcLabelString),
|
|
String = indent(Level) ++ singleton("Proc: ") ++
|
|
singleton(ProcLabelString) ++ singleton("\n")
|
|
;
|
|
Location = pl_goal(ProcLabel, RevGoalPath),
|
|
location_to_string(Level, pl_proc(ProcLabel), FirstLine),
|
|
(
|
|
RevGoalPath = rgp_nil,
|
|
GoalPathString = singleton("Root goal")
|
|
;
|
|
RevGoalPath = rgp_cons(_, _),
|
|
GoalPathString = singleton("Goal: ") ++
|
|
singleton(rev_goal_path_to_string(RevGoalPath))
|
|
),
|
|
SecondLine = indent(Level) ++ GoalPathString ++ singleton("\n"),
|
|
String = FirstLine ++ SecondLine
|
|
;
|
|
Location = pl_clique(clique_ptr(Id)),
|
|
format("clique %d", [i(Id)], String0),
|
|
String = indent(Level) ++ singleton(String0)
|
|
;
|
|
Location = pl_csd(CSDPtr),
|
|
CSDPtr = call_site_dynamic_ptr(CSDNum),
|
|
format("call site dynamic %d", [i(CSDNum)], String0),
|
|
String = indent(Level) ++ singleton(String0)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
append_message(Location, MessageType, !Messages) :-
|
|
Message = message(Location, MessageType),
|
|
!:Messages = cord.snoc(!.Messages, Message).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- func message_level_to_string(message_level) = cord(string).
|
|
|
|
message_level_to_string(message_info) = singleton("Info").
|
|
message_level_to_string(message_notice) = singleton("Notice").
|
|
message_level_to_string(message_warning) = singleton("Warning").
|
|
message_level_to_string(message_error) = singleton("Error").
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- func message_type_to_level(message_type) = message_level.
|
|
|
|
message_type_to_level(MsgType) = MsgLevel :-
|
|
(
|
|
( MsgType = info_found_candidate_conjunction
|
|
; MsgType = info_found_conjs_above_callsite_threshold(_)
|
|
; MsgType = info_found_pushed_conjs_above_callsite_threshold
|
|
; MsgType = info_found_n_conjunctions_with_positive_speedup(_)
|
|
; MsgType = info_split_conjunction_into_partitions(_)
|
|
),
|
|
MsgLevel = message_info
|
|
;
|
|
( MsgType = notice_duplicate_instantiation(_)
|
|
; MsgType = notice_callpair_has_more_than_one_dependant_var
|
|
; MsgType = notice_partition_does_not_have_costly_calls(_, _)
|
|
; MsgType = notice_candidate_conjunction_not_det(_)
|
|
),
|
|
MsgLevel = message_notice
|
|
;
|
|
( MsgType = warning_cannot_lookup_proc_defn
|
|
; MsgType = warning_cannot_compute_procrep_coverage_fallback(_)
|
|
; MsgType = warning_cannot_compute_cost_of_recursive_calls(_)
|
|
; MsgType = warning_cannot_compute_first_use_time(_)
|
|
),
|
|
MsgLevel = message_warning
|
|
;
|
|
( MsgType = error_extra_proc_dynamics_in_clique_proc
|
|
; MsgType = error_exception_thrown(_)
|
|
; MsgType = error_cannot_lookup_coverage_points
|
|
),
|
|
MsgLevel = message_error
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- func message_type_to_string(message_type) = cord(string).
|
|
:- pragma require_switch_arms_in_type_order(func(message_type_to_string/1)).
|
|
|
|
message_type_to_string(MessageType) = Cord :-
|
|
(
|
|
MessageType = info_found_candidate_conjunction,
|
|
String = "Found candidate conjunction"
|
|
;
|
|
MessageType = info_found_conjs_above_callsite_threshold(Num),
|
|
MessageStr = "Found %d conjuncts above callsite threshold",
|
|
string.format(MessageStr, [i(Num)], String)
|
|
;
|
|
MessageType = info_found_n_conjunctions_with_positive_speedup(Num),
|
|
MessageStr = "Found %d conjunctions with a positive speedup due"
|
|
++ " to parallelisation",
|
|
string.format(MessageStr, [i(Num)], String)
|
|
;
|
|
MessageType = info_split_conjunction_into_partitions(Num),
|
|
MessageStr = "Split conjunction into %d partitions, "
|
|
++ "this may reduce parallelism",
|
|
string.format(MessageStr, [i(Num)], String)
|
|
;
|
|
MessageType = info_found_pushed_conjs_above_callsite_threshold,
|
|
String = "Found pushed conjuncts above callsite threshold"
|
|
;
|
|
MessageType = notice_duplicate_instantiation(CandidateConjuncts),
|
|
string.format(
|
|
"%d conjunctions not parallelised: Seen duplicate instantiations",
|
|
[i(CandidateConjuncts)], String)
|
|
;
|
|
MessageType = notice_callpair_has_more_than_one_dependant_var,
|
|
String = "Parallelising call pairs that have more than one "
|
|
++ "dependent variable is not yet supported."
|
|
;
|
|
MessageType = notice_partition_does_not_have_costly_calls(PartNum,
|
|
NumCalls),
|
|
string.format("Partition %d has only %d costly calls and cannot be"
|
|
++ " parallelised",
|
|
[i(PartNum), i(NumCalls)], String)
|
|
;
|
|
MessageType = notice_candidate_conjunction_not_det(Detism),
|
|
string.format("There are %s goals amongst goals"
|
|
++ " above the parallelisation overhead.",
|
|
[s(string(Detism))], String)
|
|
;
|
|
MessageType = warning_cannot_lookup_proc_defn,
|
|
String = "Could not look up proc defn, perhaps this procedure is"
|
|
++ " built-in"
|
|
;
|
|
MessageType = warning_cannot_compute_procrep_coverage_fallback(Msg),
|
|
String = "Cannot compute procrep coverage annotation: " ++ Msg
|
|
++ "\n falling back to some other method"
|
|
;
|
|
MessageType =
|
|
warning_cannot_compute_cost_of_recursive_calls(WarnStr),
|
|
Template = "Cannot compute cost of recursive calls: %s",
|
|
string.format(Template, [s(WarnStr)], String)
|
|
;
|
|
MessageType =
|
|
warning_cannot_compute_first_use_time(WarnStr),
|
|
Template = "Cannot compute the production or consumption time "
|
|
++ "of a variable: %s",
|
|
string.format(Template, [s(WarnStr)], String)
|
|
;
|
|
MessageType = error_extra_proc_dynamics_in_clique_proc,
|
|
String = "extra proc dynamics for a clique proc are not currently"
|
|
++ " handled."
|
|
;
|
|
MessageType = error_cannot_lookup_coverage_points,
|
|
String = "Cannot lookup coverage points"
|
|
;
|
|
MessageType = error_exception_thrown(ErrorStr),
|
|
Template = "Exception thrown: %s",
|
|
string.format(Template, [s(ErrorStr)], String)
|
|
),
|
|
Cord = singleton(String).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
indent(N) = Indent :-
|
|
( if N < 0 then
|
|
error("automatic_parallelism: Negative indent")
|
|
else if N = 0 then
|
|
Indent = empty
|
|
else
|
|
Indent = snoc(indent(N - 1), " ")
|
|
).
|
|
|
|
nl_indent(N) = nl ++ indent(N).
|
|
|
|
nl = singleton("\n").
|
|
|
|
indent_size(N) = 2 * N.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- mutable(verbosity_level_mut, int, default_verbosity_level, ground,
|
|
[attach_to_io_state, untrailed]).
|
|
|
|
write_out_messages(Stream, Messages, !IO) :-
|
|
cord.foldl_pred(write_out_message(Stream), Messages, !IO).
|
|
|
|
:- pred write_out_message(io.text_output_stream::in, message::in,
|
|
io::di, io::uo) is det.
|
|
|
|
write_out_message(Stream, Message, !IO) :-
|
|
Level = message_get_level(Message),
|
|
get_verbosity_level_mut(VerbosityLevel, !IO),
|
|
( if message_level_to_int(Level) =< VerbosityLevel then
|
|
message_to_string(Message, MessageStr),
|
|
io.write_string(Stream, MessageStr, !IO),
|
|
io.nl(Stream, !IO)
|
|
else
|
|
true
|
|
).
|
|
|
|
set_verbosity_level(VerbosityLevel, !IO) :-
|
|
set_verbosity_level_mut(VerbosityLevel, !IO).
|
|
|
|
default_verbosity_level = 2.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module message.
|
|
%---------------------------------------------------------------------------%
|