mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-18 19:03:45 +00:00
compiler/simplify_goal_conj.m:
When a switch on a variable is followed by a switch on the *same* variable,
merge the two switches together, if doing so looks like it can save
the execution of some branches for at least some input values.
This transform is the second one to merge the goal following a switch
into the switch itself. Move the parts of the code involved that are
common between the two transformation to its caller, to reduce runtime
overhead.
compiler/simplify_goal.m:
Add infrastructure for printing the pre- and post-simplification versions
of specific goals. This helped debug the new code in simplify_goal_conj.m.
Fix an inadvertent unification between two module_infos.
This was caused by code that
- defined the variable ModuleInfo, and then
- reuses the "ModuleInfo" variable name for a slightly different purpose,
but did so in the condition of an if-then-else, where the semidet nature
of the implicit unification between the new value intended to be assigned
to ModuleInfo, and its old value, was expected.
compiler/simplify_proc.m:
Add infrastructure for printing versions of the procedure body
before and after quantification, instmap delta recomputation, and
determinism analysis have been rerun. This helped debug the new code
in simplify_goal_conj.m.
The rest of the changes concern the option that
- previously called only for the existing transformation to merge a test
unification following a switch into a switch
- and which now also calls for the merging of two succesive switches
on the same variable.
compiler/options.m:
The option was named test_after_switch; this diff updates its name to
merge_code_after_switch.
compiler/handle_options.m:
compiler/simplify_info.m:
compiler/simplify_tasks.m:
compiler/optimization_options.m:
tools/make_optimization_options_db:
tools/make_optimization_options_end:
Conform to this name change.
414 lines
18 KiB
Mathematica
414 lines
18 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 2014 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: simplify_tasks.m.
|
|
%
|
|
% This module handles the specification of what tasks the submodules of
|
|
% simplify.m should perform.
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module check_hlds.simplify.simplify_tasks.
|
|
:- interface.
|
|
|
|
:- import_module libs.
|
|
:- import_module libs.globals.
|
|
:- import_module libs.optimization_options.
|
|
|
|
:- import_module list.
|
|
|
|
% Each value of this type represents a task, or a group of related tasks,
|
|
% that simplification should perform.
|
|
:- type simplify_task
|
|
---> simptask_warn_simple_code
|
|
% --warn-simple-code
|
|
|
|
; simptask_warn_duplicate_calls
|
|
% --warn-duplicate-calls
|
|
|
|
; simptask_warn_implicit_stream_calls
|
|
% --warn-implicit-stream-calls
|
|
|
|
; simptask_format_calls
|
|
% Invoke format_call.m.
|
|
|
|
; simptask_warn_obsolete
|
|
% --warn-obsolete
|
|
|
|
; simptask_mark_code_model_changes
|
|
% Some compiler passes generate HLDS in which changes in code model
|
|
% are implicit in the determinisms stored in the goal_infos of
|
|
% nested goals (such as a conjunction without outputs being
|
|
% semidet, and some conjuncts being nondet). Make these code model
|
|
% changes visible by adding a scope wrapper.
|
|
|
|
; simptask_after_front_end
|
|
% Do the tasks that should be done at the end of the front end.
|
|
|
|
; simptask_excess_assigns
|
|
% Remove excess assignment unifications.
|
|
|
|
; simptask_merge_code_after_switch
|
|
% Merge the goal that occurs just after a switch into the switch,
|
|
% if we can. There are two kinds of situations in which we can
|
|
% do so. These are described by the comments on the predicates
|
|
% implementing the transforms, try_to_merge_unify_after_switch
|
|
% and try_to_merge_switch_after_switch.
|
|
|
|
; simptask_elim_removable_scopes
|
|
% Remove scopes that do not need processing during LLDS code
|
|
% generation.
|
|
|
|
; simptask_opt_duplicate_calls
|
|
% Optimize duplicate calls.
|
|
|
|
; simptask_constant_prop
|
|
% Partially evaluate calls.
|
|
|
|
; simptask_common_structs
|
|
% Common structure elimination.
|
|
|
|
; simptask_extra_common_structs
|
|
% Do common structure elimination even when it might
|
|
% increase stack usage (used by deforestation).
|
|
|
|
; simptask_try_opt_const_structs
|
|
% If the backend and the options allow it, replace unifications
|
|
% that construct constant terms with the assignment of a
|
|
% ground_term_const cons_id to the relevant variable.
|
|
|
|
; simptask_ignore_par_conjs
|
|
% Replace parallel conjunctions with plain conjunctions.
|
|
|
|
; simptask_warn_suspicious_recursion
|
|
% With simptask_warn_simple_code, we generate warnings
|
|
% for recursive calls in which all input arguments are
|
|
% the same as in the clause head. These calls are guaranteed
|
|
% to represent an infinite loop.
|
|
%
|
|
% If simptask_warn_suspicious_recursion is also set, we also
|
|
% generate a weaker version of that warning for recursive calls
|
|
% in which all *non-state-var* input arguments are the same as
|
|
% in the clause head. In the usual case where the recursion is
|
|
% controlled by the non-state-var input arguments, these calls
|
|
% also represent an infinite loop, but we cannot say that for
|
|
% certain, because in some cases, the predicate is recursing
|
|
% on a data structure that the code *does* refer to using
|
|
% state variable notation.
|
|
|
|
; simptask_warn_no_solution_disjunct
|
|
% Warn about disjuncts that can have no solution.
|
|
|
|
; simptask_split_switch_arms.
|
|
% Invoke split_switch_arms.m to perform its transformation,
|
|
% if the main part of simplification discovers that it has
|
|
% some redundant switches to optimize.
|
|
%
|
|
% For the details of the transformation done by
|
|
% split_switch_arms.m, see its top-of-module comment.
|
|
|
|
%---------------------%
|
|
|
|
:- type maybe_warn_simple_code
|
|
---> do_not_warn_simple_code
|
|
; warn_simple_code.
|
|
|
|
:- type maybe_warn_duplicate_calls
|
|
---> do_not_warn_duplicate_calls
|
|
; warn_duplicate_calls.
|
|
|
|
:- type maybe_warn_implicit_streams
|
|
---> do_not_warn_implicit_streams
|
|
; warn_implicit_streams.
|
|
|
|
:- type maybe_invoke_format_call
|
|
---> do_not_invoke_format_call
|
|
; invoke_format_call.
|
|
|
|
:- type maybe_warn_obsolete
|
|
---> do_not_warn_obsolete
|
|
; warn_obsolete.
|
|
|
|
:- type maybe_mark_cm_changes
|
|
---> do_not_mark_code_model_changes
|
|
; mark_code_model_changes.
|
|
|
|
:- type maybe_after_front_end
|
|
---> not_after_front_end
|
|
; after_front_end.
|
|
|
|
:- type maybe_elim_removable_scopes
|
|
---> do_not_elim_removable_scopes
|
|
; elim_removable_scopes.
|
|
|
|
:- type maybe_opt_extra_structs
|
|
---> do_not_opt_extra_structs
|
|
; opt_extra_structs.
|
|
|
|
:- type maybe_try_opt_const_structs
|
|
---> do_not_try_opt_const_structs
|
|
; try_opt_const_structs.
|
|
|
|
:- type maybe_opt_const_structs
|
|
---> do_not_opt_const_structs
|
|
; opt_const_structs.
|
|
|
|
:- type maybe_ignore_par_conjs
|
|
---> do_not_ignore_par_conjs
|
|
; ignore_par_conjs.
|
|
|
|
:- type maybe_warn_suspicious_rec
|
|
---> do_not_warn_suspicious_rec
|
|
; warn_suspicious_rec.
|
|
|
|
:- type maybe_warn_no_soln_disjunct
|
|
---> do_not_warn_no_soln_disjunct
|
|
; warn_no_soln_disjunct.
|
|
|
|
:- type maybe_opt_split_switch_arms
|
|
---> do_not_opt_split_switch_arms
|
|
; split_opt_switch_arms.
|
|
|
|
% Each value of this type represents the full set of tasks
|
|
% that simplification should perform. The submodules of simplify.m
|
|
% use it to find out whether they should perform a specific task
|
|
% without having to search a list of simplifications.
|
|
%
|
|
% The definition of this type does not need to be visible to modules
|
|
% outside simplify.m, but avoiding such visibility would cost more
|
|
% in extra complexity than it would gain.
|
|
:- type simplify_tasks
|
|
---> simplify_tasks(
|
|
do_warn_simple_code :: maybe_warn_simple_code,
|
|
do_warn_duplicate_calls :: maybe_warn_duplicate_calls,
|
|
do_warn_implicit_streams :: maybe_warn_implicit_streams,
|
|
do_invoke_format_call :: maybe_invoke_format_call,
|
|
do_warn_obsolete :: maybe_warn_obsolete,
|
|
do_mark_code_model_changes :: maybe_mark_cm_changes,
|
|
do_after_front_end :: maybe_after_front_end,
|
|
do_excess_assign :: maybe_elim_excess_assigns,
|
|
do_merge_code_after_switch :: maybe_merge_code_after_switch,
|
|
do_elim_removable_scopes :: maybe_elim_removable_scopes,
|
|
do_opt_duplicate_calls :: maybe_opt_dup_calls,
|
|
do_constant_prop :: maybe_prop_constants,
|
|
do_opt_common_structs :: maybe_opt_common_structs,
|
|
do_opt_extra_structs :: maybe_opt_extra_structs,
|
|
do_try_opt_const_structs :: maybe_try_opt_const_structs,
|
|
do_opt_const_structs :: maybe_opt_const_structs,
|
|
do_ignore_par_conjunctions :: maybe_ignore_par_conjs,
|
|
do_warn_suspicious_recursion :: maybe_warn_suspicious_rec,
|
|
do_warn_no_solution_disjunct :: maybe_warn_no_soln_disjunct,
|
|
do_switch_split_arms :: maybe_split_switch_arms
|
|
).
|
|
|
|
% XXX Now that each field of the simplify_tasks structure
|
|
% has its own type and thus fields cannot be mistaken for each other,
|
|
% we should consider requiring the compiler modules that invoke
|
|
% simplification to construct a simplify_tasks structure directly,
|
|
% *without* going through simplify task list.
|
|
%
|
|
:- func simplify_tasks_to_list(simplify_tasks) = list(simplify_task).
|
|
:- func list_to_simplify_tasks(globals, list(simplify_task)) = simplify_tasks.
|
|
|
|
:- type maybe_generate_warnings
|
|
---> do_not_generate_warnings
|
|
; generate_warnings.
|
|
|
|
% Find out which simplifications should be run from the options table
|
|
% stored in the globals. The second argument states whether we should
|
|
% generate warnings during this pass of simplification.
|
|
%
|
|
:- pred find_simplify_tasks(globals::in, maybe_generate_warnings::in,
|
|
simplify_tasks::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module libs.options.
|
|
|
|
:- import_module bool.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
simplify_tasks_to_list(SimplifyTasks) = !:List :-
|
|
SimplifyTasks = simplify_tasks(WarnSimpleCode, WarnDupCalls,
|
|
WarnImplicitStreamCalls, DoFormatCalls, WarnObsolete,
|
|
MarkCodeModelChanges, AfterFrontEnd, ExcessAssign,
|
|
MergeCodeAfterSwitch, ElimRemovableScopes,
|
|
OptDuplicateCalls, ConstantProp, OptCommonStructs, OptExtraStructs,
|
|
TryOptConstStructs, _OptConstStructs, IgnoreParConjs,
|
|
WarnSuspiciousRecursion, WarnNoSolutionDisjunct, SplitSwitchArms),
|
|
!:List = [],
|
|
( if WarnSimpleCode = warn_simple_code
|
|
then list.cons(simptask_warn_simple_code, !List) else true ),
|
|
( if WarnDupCalls = warn_duplicate_calls
|
|
then list.cons(simptask_warn_duplicate_calls, !List) else true ),
|
|
( if WarnImplicitStreamCalls = warn_implicit_streams
|
|
then list.cons(simptask_warn_implicit_stream_calls, !List) else true ),
|
|
( if DoFormatCalls = invoke_format_call
|
|
then list.cons(simptask_format_calls, !List) else true ),
|
|
( if WarnObsolete = warn_obsolete
|
|
then list.cons(simptask_warn_obsolete, !List) else true ),
|
|
( if MarkCodeModelChanges = mark_code_model_changes
|
|
then list.cons(simptask_mark_code_model_changes, !List) else true ),
|
|
( if AfterFrontEnd = after_front_end
|
|
then list.cons(simptask_after_front_end, !List) else true ),
|
|
( if ExcessAssign = elim_excess_assigns
|
|
then list.cons(simptask_excess_assigns, !List) else true ),
|
|
( if MergeCodeAfterSwitch = merge_code_after_switch
|
|
then list.cons(simptask_merge_code_after_switch, !List) else true ),
|
|
( if ElimRemovableScopes = elim_removable_scopes
|
|
then list.cons(simptask_elim_removable_scopes, !List) else true ),
|
|
( if OptDuplicateCalls = opt_dup_calls
|
|
then list.cons(simptask_opt_duplicate_calls, !List) else true ),
|
|
( if ConstantProp = prop_constants
|
|
then list.cons(simptask_constant_prop, !List) else true ),
|
|
( if OptCommonStructs = opt_common_structs
|
|
then list.cons(simptask_common_structs, !List) else true ),
|
|
( if OptExtraStructs = opt_extra_structs
|
|
then list.cons(simptask_extra_common_structs, !List) else true ),
|
|
( if TryOptConstStructs = try_opt_const_structs
|
|
then list.cons(simptask_try_opt_const_structs, !List) else true ),
|
|
( if IgnoreParConjs = ignore_par_conjs
|
|
then list.cons(simptask_ignore_par_conjs, !List) else true ),
|
|
( if WarnSuspiciousRecursion = warn_suspicious_rec
|
|
then list.cons(simptask_warn_suspicious_recursion, !List) else true ),
|
|
( if WarnNoSolutionDisjunct = warn_no_soln_disjunct
|
|
then list.cons(simptask_warn_no_solution_disjunct, !List) else true ),
|
|
( if SplitSwitchArms = split_switch_arms
|
|
then list.cons(simptask_split_switch_arms, !List) else true ).
|
|
|
|
list_to_simplify_tasks(Globals, List) = Tasks :-
|
|
globals.get_opt_tuple(Globals, OptTuple),
|
|
Tasks = simplify_tasks(
|
|
( if list.member(simptask_warn_simple_code, List)
|
|
then warn_simple_code else do_not_warn_simple_code ),
|
|
( if list.member(simptask_warn_duplicate_calls, List)
|
|
then warn_duplicate_calls else do_not_warn_duplicate_calls ),
|
|
( if list.member(simptask_warn_implicit_stream_calls, List)
|
|
then warn_implicit_streams else do_not_warn_implicit_streams ),
|
|
( if list.member(simptask_format_calls, List)
|
|
then invoke_format_call else do_not_invoke_format_call ),
|
|
( if list.member(simptask_warn_obsolete, List)
|
|
then warn_obsolete else do_not_warn_obsolete ),
|
|
( if list.member(simptask_mark_code_model_changes, List)
|
|
then mark_code_model_changes else do_not_mark_code_model_changes ),
|
|
( if list.member(simptask_after_front_end, List)
|
|
then after_front_end else not_after_front_end ),
|
|
( if list.member(simptask_excess_assigns, List)
|
|
then elim_excess_assigns else do_not_elim_excess_assigns ),
|
|
( if list.member(simptask_merge_code_after_switch, List)
|
|
then merge_code_after_switch else do_not_merge_code_after_switch ),
|
|
( if list.member(simptask_elim_removable_scopes, List)
|
|
then elim_removable_scopes else do_not_elim_removable_scopes ),
|
|
( if list.member(simptask_opt_duplicate_calls, List)
|
|
then opt_dup_calls else do_not_opt_dup_calls ),
|
|
( if list.member(simptask_constant_prop, List)
|
|
then prop_constants else do_not_prop_constants ),
|
|
( if list.member(simptask_common_structs, List)
|
|
then opt_common_structs else do_not_opt_common_structs ),
|
|
( if list.member(simptask_extra_common_structs, List)
|
|
then opt_extra_structs else do_not_opt_extra_structs ),
|
|
( if list.member(simptask_try_opt_const_structs, List)
|
|
then try_opt_const_structs else do_not_try_opt_const_structs ),
|
|
( if
|
|
list.member(simptask_try_opt_const_structs, List),
|
|
OptTuple ^ ot_enable_const_struct_user = enable_const_struct_user
|
|
then
|
|
opt_const_structs
|
|
else
|
|
do_not_opt_const_structs
|
|
),
|
|
( if list.member(simptask_ignore_par_conjs, List)
|
|
then ignore_par_conjs else do_not_ignore_par_conjs ),
|
|
( if list.member(simptask_warn_suspicious_recursion, List)
|
|
then warn_suspicious_rec else do_not_warn_suspicious_rec ),
|
|
( if list.member(simptask_warn_no_solution_disjunct, List)
|
|
then warn_no_soln_disjunct else do_not_warn_no_soln_disjunct ),
|
|
( if list.member(simptask_split_switch_arms, List)
|
|
then split_switch_arms else do_not_split_switch_arms )
|
|
).
|
|
|
|
find_simplify_tasks(Globals, WarnThisPass, SimplifyTasks) :-
|
|
globals.lookup_bool_option(Globals, warn_simple_code, WarnSimple),
|
|
globals.lookup_bool_option(Globals, warn_duplicate_calls, WarnDupCalls),
|
|
globals.lookup_bool_option(Globals, warn_implicit_stream_calls,
|
|
WarnImplicitStreamCalls),
|
|
globals.lookup_bool_option(Globals, warn_known_bad_format_calls,
|
|
WarnKnownBadFormat),
|
|
globals.lookup_bool_option(Globals, warn_unknown_format_calls,
|
|
WarnUnknownFormat),
|
|
globals.get_opt_tuple(Globals, OptTuple),
|
|
OptFormatCalls = OptTuple ^ ot_opt_format_calls,
|
|
( if
|
|
(
|
|
WarnThisPass = generate_warnings,
|
|
( WarnKnownBadFormat = yes ; WarnUnknownFormat = yes )
|
|
;
|
|
OptFormatCalls = opt_format_calls
|
|
)
|
|
then
|
|
InvokeFormatCall = invoke_format_call
|
|
else
|
|
InvokeFormatCall = do_not_invoke_format_call
|
|
),
|
|
globals.lookup_bool_option(Globals, warn_obsolete, WarnObsolete),
|
|
ExcessAssign = OptTuple ^ ot_elim_excess_assigns,
|
|
MergeCodeAfterSwitch = OptTuple ^ ot_merge_code_after_switch,
|
|
OptDuplicateCalls = OptTuple ^ ot_opt_dup_calls,
|
|
ConstantProp = OptTuple ^ ot_prop_constants,
|
|
MarkCodeModelChanges = do_not_mark_code_model_changes,
|
|
AfterFrontEnd = not_after_front_end,
|
|
ElimRemovableScopes = do_not_elim_removable_scopes,
|
|
CommonStructs = OptTuple ^ ot_opt_common_structs,
|
|
ExtraCommonStructs = do_not_opt_extra_structs,
|
|
TryOptConstStructs = do_not_try_opt_const_structs,
|
|
OptConstStructs = do_not_opt_const_structs,
|
|
globals.lookup_bool_option(Globals, ignore_par_conjunctions,
|
|
RemoveParConjunctions),
|
|
globals.lookup_bool_option(Globals, warn_suspicious_recursion,
|
|
WarnSuspiciousRecursion),
|
|
SplitSwitchArms = OptTuple ^ ot_split_switch_arms,
|
|
|
|
SimplifyTasks = simplify_tasks(
|
|
( if WarnSimple = yes, WarnThisPass = generate_warnings
|
|
then warn_simple_code else do_not_warn_simple_code ),
|
|
( if WarnDupCalls = yes, WarnThisPass = generate_warnings
|
|
then warn_duplicate_calls else do_not_warn_duplicate_calls ),
|
|
( if WarnImplicitStreamCalls = yes, WarnThisPass = generate_warnings
|
|
then warn_implicit_streams else do_not_warn_implicit_streams ),
|
|
InvokeFormatCall,
|
|
( if WarnObsolete = yes, WarnThisPass = generate_warnings
|
|
then warn_obsolete else do_not_warn_obsolete ),
|
|
MarkCodeModelChanges,
|
|
AfterFrontEnd,
|
|
ExcessAssign,
|
|
MergeCodeAfterSwitch,
|
|
ElimRemovableScopes,
|
|
OptDuplicateCalls,
|
|
ConstantProp,
|
|
CommonStructs,
|
|
ExtraCommonStructs,
|
|
TryOptConstStructs,
|
|
OptConstStructs,
|
|
( if RemoveParConjunctions = yes
|
|
then ignore_par_conjs else do_not_ignore_par_conjs ),
|
|
( if WarnSuspiciousRecursion = yes
|
|
then warn_suspicious_rec else do_not_warn_suspicious_rec ),
|
|
% Warnings about "no solution disjuncts" are a category of warnings
|
|
% about simple code that happens to have its own disabling mechanism.
|
|
( if WarnSimple = yes, WarnThisPass = generate_warnings
|
|
then warn_no_soln_disjunct else do_not_warn_no_soln_disjunct ),
|
|
SplitSwitchArms
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module check_hlds.simplify.simplify_tasks.
|
|
%---------------------------------------------------------------------------%
|