%---------------------------------------------------------------------------% % vim: ft=mercury ts=4 sw=4 et %---------------------------------------------------------------------------% % Copyright (C) 2014-2017, 2019-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: 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_dodgy_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_dodgy_simple_code ---> do_not_warn_dodgy_simple_code ; warn_dodgy_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_dodgy_simple_code :: maybe_warn_dodgy_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(WarnDodgySimple, WarnDupCalls, WarnImplicitStreamCalls, DoFormatCalls, WarnObsolete, MarkCodeModelChanges, AfterFrontEnd, ExcessAssign, MergeCodeAfterSwitch, ElimRemovableScopes, OptDuplicateCalls, ConstantProp, OptCommonStructs, OptExtraStructs, TryOptConstStructs, _OptConstStructs, IgnoreParConjs, WarnSuspiciousRecursion, WarnNoSolutionDisjunct, SplitSwitchArms), !:List = [], ( if WarnDodgySimple = warn_dodgy_simple_code then list.cons(simptask_warn_dodgy_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_dodgy_simple_code, List) then warn_dodgy_simple_code else do_not_warn_dodgy_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_dodgy_simple_code, WarnDodgySimple), 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 WarnDodgySimple = yes, WarnThisPass = generate_warnings then warn_dodgy_simple_code else do_not_warn_dodgy_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 WarnDodgySimple = yes, WarnThisPass = generate_warnings then warn_no_soln_disjunct else do_not_warn_no_soln_disjunct ), SplitSwitchArms ). %---------------------------------------------------------------------------% :- end_module check_hlds.simplify.simplify_tasks. %---------------------------------------------------------------------------%