%---------------------------------------------------------------------------% % vim: ft=mercury ts=4 sw=4 et %---------------------------------------------------------------------------% % Copyright (C) 2014-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_proc.m. % % This module handles top level invocations of simplification. % % Most such invocations simplify the body of a procedure, or the bodies % of all the procedures in a predicate. However, in some cases some other % compiler passes (such as deforestation or partial evaluation) want to % simplify a goal that is not the body of a procedure. % %---------------------------------------------------------------------------% :- module check_hlds.simplify.simplify_proc. :- interface. :- import_module check_hlds.simplify.simplify_tasks. :- import_module hlds. :- import_module hlds.hlds_goal. :- import_module hlds.hlds_module. :- import_module hlds.hlds_pred. :- import_module hlds.instmap. :- import_module parse_tree. :- import_module parse_tree.error_util. :- import_module io. :- import_module list. :- import_module maybe. %---------------------------------------------------------------------------% % Simplify all the given procedures of the given predicate. % Add any resulting messages to the error spec accumulator. % % Used by mercury_compiler_front_end.m when doing compilation pass-by-pass. % :- pred simplify_pred_procs(io.text_output_stream::in, simplify_tasks::in, pred_id::in, list(proc_id)::in, pred_info::in, pred_info::out, module_info::in, module_info::out, error_spec_accumulator::in, error_spec_accumulator::out) is det. % Simplify the given procedure. Throw away any resulting error messages. % % Used by compiler passes after the front end that need (or maybe just % want) to eliminate unnecessary parts of the procedure. % :- pred simplify_proc(maybe(io.text_output_stream)::in, io.text_output_stream::in, simplify_tasks::in, pred_id::in, proc_id::in, proc_info::in, proc_info::out, module_info::in, module_info::out) is det. % simplify_goal_update_vars_in_proc(ProgressStream, SimplifyTasks, % PredId, ProcId, InstMap0, CostDelta, !Goal, !ProcInfo, !ModuleInfo): % % Perform the specified simplification tasks on !Goal, which should be % part of the procedure identified by PredId and ProcId. InstMap0 % should be the instmap immediately before !.Goal. % % We may update !ModuleInfo during the course of updating instmaps % to reflect any changes made to the code. If the modifications to !Goal % add any new variables, add these to !ProcInfo. % % !.Goal does NOT need to be the entire body of the procedure it appears % in; it can be just a part of it. This is why we return the updated goal % in !:Goal, not in !:ProcInfo. % % Used by partial evaluation. % :- pred simplify_goal_update_vars_in_proc(io.text_output_stream::in, simplify_tasks::in, pred_id::in, proc_id::in, instmap::in, int::out, hlds_goal::in, hlds_goal::out, proc_info::in, proc_info::out, module_info::in, module_info::out) is det. %---------------------------------------------------------------------------% %---------------------------------------------------------------------------% :- implementation. :- import_module check_hlds.det_infer_goal. :- import_module check_hlds.det_util. :- import_module check_hlds.recompute_instmap_deltas. :- import_module check_hlds.simplify.common. :- import_module check_hlds.simplify.mark_trace_goals. :- import_module check_hlds.simplify.opt_format_call. :- import_module check_hlds.simplify.simplify_goal. :- import_module check_hlds.simplify.simplify_info. :- import_module check_hlds.simplify.split_switch_arms. :- import_module hlds.code_model. :- import_module hlds.hlds_markers. :- import_module hlds.hlds_out. :- import_module hlds.hlds_out.hlds_out_goal. :- import_module hlds.hlds_proc_util. :- import_module hlds.passes_aux. :- import_module hlds.quantification. :- import_module hlds.status. :- import_module libs. :- import_module libs.globals. :- import_module libs.optimization_options. :- import_module libs.options. :- import_module parse_tree.error_spec. :- import_module parse_tree.parse_tree_out_term. :- import_module parse_tree.prog_data. :- import_module parse_tree.prog_data_foreign. :- import_module parse_tree.set_of_var. :- import_module parse_tree.var_db. :- import_module parse_tree.var_table. :- import_module transform_hlds. :- import_module transform_hlds.direct_arg_in_out. :- import_module bool. :- import_module cord. :- import_module int. :- import_module map. :- import_module require. :- import_module set. :- import_module string. :- import_module varset. %---------------------------------------------------------------------------% simplify_pred_procs(_, _, _, [], !PredInfo, !ModuleInfo, !Specs). simplify_pred_procs(ProgressStream, SimplifyTasks, PredId, [ProcId | ProcIds], !PredInfo, !ModuleInfo, !Specs) :- simplify_pred_proc(ProgressStream, SimplifyTasks, PredId, ProcId, !PredInfo, !ModuleInfo, !Specs), simplify_pred_procs(ProgressStream, SimplifyTasks, PredId, ProcIds, !PredInfo, !ModuleInfo, !Specs). :- pred simplify_pred_proc(io.text_output_stream::in, simplify_tasks::in, pred_id::in, proc_id::in, pred_info::in, pred_info::out, module_info::in, module_info::out, error_spec_accumulator::in, error_spec_accumulator::out) is det. simplify_pred_proc(ProgressStream, SimplifyTasks, PredId, ProcId, !PredInfo, !ModuleInfo, !Specs) :- % XXX It is strange that simplify_proc prints progress messages, % but simplify_pred_proc does not. pred_info_get_proc_table(!.PredInfo, ProcTable0), map.lookup(ProcTable0, ProcId, ProcInfo0), simplify_proc_return_msgs(ProgressStream, SimplifyTasks, PredId, ProcId, ProcSpecs, ProcInfo0, ProcInfo, !ModuleInfo), % This is ugly, but we want to avoid running the dependent parallel % conjunction pass on predicates and even modules that do not contain % parallel conjunctions (nearly all of them). Since simplification % is always done, we use it to mark modules and procedures containing % parallel conjunctions. proc_info_get_has_parallel_conj(ProcInfo, HasParallelConj), ( HasParallelConj = has_parallel_conj, module_info_set_has_parallel_conj(!ModuleInfo) ; HasParallelConj = has_no_parallel_conj ), proc_info_get_has_user_event(ProcInfo, HasUserEvent), ( HasUserEvent = has_user_event, module_info_set_has_user_event(!ModuleInfo) ; HasUserEvent = has_no_user_event ), map.det_update(ProcId, ProcInfo, ProcTable0, ProcTable), pred_info_set_proc_table(ProcTable, !PredInfo), accumulate_error_specs_for_proc(ProcSpecs, !Specs). simplify_proc(MaybeProgressStream, ProgressStream, SimplifyTasks, PredId, ProcId, !ProcInfo, !ModuleInfo) :- trace [io(!IO)] ( ( MaybeProgressStream = no ; MaybeProgressStream = yes(Stream), maybe_write_pred_progress_message(Stream, !.ModuleInfo, "Simplifying", PredId, !IO) ) ), simplify_proc_return_msgs(ProgressStream, SimplifyTasks, PredId, ProcId, _, !ProcInfo, !ModuleInfo). simplify_goal_update_vars_in_proc(ProgressStream, SimplifyTasks, PredId, ProcId, InstMap0, CostDelta, !Goal, !ProcInfo, !ModuleInfo) :- simplify_info_init(ProgressStream, !.ModuleInfo, PredId, ProcId, !.ProcInfo, SimplifyTasks, SimplifyInfo0), % The nested context we construct is probably a lie; we don't actually % know whether we are inside a goal duplicated for a switch, or a lambda, % or a model_non procedure. However, this should be ok. The first three % fields of the nested context are used for deciding what warnings and % errors to generate, and we are not interested in those, while the fourth % is there to support an optimization that we explicitly disallow % below by passing do_not_allow_splitting_switch_arms. InsideDuplForSwitch = no, ProcIsModelNon = no, NumEnclosingBarriers = 0u, SwitchArmContext = [], NestedContext0 = simplify_nested_context(InsideDuplForSwitch, ProcIsModelNon, NumEnclosingBarriers, SwitchArmContext), % Passing do_not_allow_splitting_switch_arms here is conservative. simplify_top_level_goal(NestedContext0, InstMap0, do_not_allow_splitting_switch_arms, !Goal, SimplifyInfo0, SimplifyInfo), simplify_info_get_module_info(SimplifyInfo, !:ModuleInfo), simplify_info_get_var_table(SimplifyInfo, VarTable), simplify_info_get_rtti_varmaps(SimplifyInfo, RttiVarMaps), proc_info_set_var_table(VarTable, !ProcInfo), proc_info_set_rtti_varmaps(RttiVarMaps, !ProcInfo), simplify_info_get_cost_delta(SimplifyInfo, CostDelta). %---------------------------------------------------------------------------% % Simplify the given procedure. Return the resulting error messages. % :- pred simplify_proc_return_msgs(io.text_output_stream::in, simplify_tasks::in, pred_id::in, proc_id::in, list(error_spec)::out, proc_info::in, proc_info::out, module_info::in, module_info::out) is det. simplify_proc_return_msgs(ProgressStream, SimplifyTasks0, PredId, ProcId, !:Specs, !ProcInfo, !ModuleInfo) :- simplify_proc_maybe_vary_parameters(!.ModuleInfo, PredId, !.ProcInfo, SimplifyTasks0, SimplifyTasks), module_info_pred_info(!.ModuleInfo, PredId, PredInfo0), pred_info_get_markers(PredInfo0, Markers0), ( if marker_is_present(Markers0, marker_mode_check_clauses) then simplify_proc_maybe_mark_modecheck_clauses(!ProcInfo) else true ), % We must invoke analyze_and_optimize_format_calls before % simplify_top_level_goal, for two reasons. % % First, excess assignment optimization may delete some of the % unifications that build the format strings or values, % which means that the goal it generates may not contain the % information that analyze_and_optimize_format_calls needs to avoid % spurious messages about unknown format strings or values. % % Second, analyze_and_optimize_format_calls generates nested % conjunctions, which simplify_top_level_goal can eliminate. % % We therefore get determinism analysis to mark the procedure % if its body contains any calls relevant to format_calls.m. ( if marker_is_present(Markers0, marker_has_format_call), SimplifyTasks ^ do_invoke_format_call = invoke_format_call then ( SimplifyTasks ^ do_warn_implicit_streams = do_not_warn_implicit_streams, ImplicitStreamWarnings = do_not_generate_implicit_stream_warnings ; SimplifyTasks ^ do_warn_implicit_streams = warn_implicit_streams, ImplicitStreamWarnings = generate_implicit_stream_warnings ), simplify_proc_analyze_and_format_calls(ProgressStream, ImplicitStreamWarnings, !ModuleInfo, PredId, PredInfo0, ProcId, !ProcInfo, FormatSpecs) else % Either there are no format calls to check, or we don't want to % optimize them and would ignore the added messages anyway. FormatSpecs = [] ), simplify_info_init(ProgressStream, !.ModuleInfo, PredId, ProcId, !.ProcInfo, SimplifyTasks, Info0), InsideDuplForSwitch = no, CodeModel = proc_info_interface_code_model(!.ProcInfo), ( ( CodeModel = model_det ; CodeModel = model_semi ), ProcIsModelNon = no ; CodeModel = model_non, ProcIsModelNon = yes(imp_whole_proc) ), NumEnclosingBarriers = 0u, SwitchArmContext = [], NestedContext0 = simplify_nested_context(InsideDuplForSwitch, ProcIsModelNon, NumEnclosingBarriers, SwitchArmContext), proc_info_get_initial_instmap(!.ModuleInfo, !.ProcInfo, InstMap0), proc_info_get_goal(!.ProcInfo, Goal0), simplify_top_level_goal(NestedContext0, InstMap0, allow_splitting_switch_arms, Goal0, Goal1, Info0, Info1), % Get the list of error_specs to print from the first invocation % of simplify_top_level_goal, ignoring any error_specs added by any % second invocation below. Most of these would probably be duplicates % that end up being ignored, but any non-duplicates would complain % about code that is compiler-generated at least in part, meaning % that not only would those diagnostics be in effect invalid, % users would also have no way of acting on them. simplify_info_get_error_specs(Info1, !:Specs), simplify_info_get_rerun_simplify_no_warn_simple(Info1, RerunSimplifyNoWarnSimple), ( RerunSimplifyNoWarnSimple = do_not_rerun_simplify_no_warn_simple, Goal = Goal1, Info = Info1 ; RerunSimplifyNoWarnSimple = rerun_simplify_no_warn_simple, simplify_info_get_simplify_tasks(Info1, Tasks1), Tasks2 = Tasks1 ^ do_warn_dodgy_simple_code := do_not_warn_dodgy_simple_code, simplify_info_set_simplify_tasks(Tasks2, Info1, Info2), simplify_top_level_goal(NestedContext0, InstMap0, allow_splitting_switch_arms, Goal1, Goal, Info2, Info) ), proc_info_set_goal(Goal, !ProcInfo), simplify_info_get_var_table(Info, VarTable0), simplify_info_get_rtti_varmaps(Info, RttiVarMaps), simplify_info_get_elim_vars(Info, ElimVarsLists0), % We sort the lists basically on the number of the first variable. list.sort(ElimVarsLists0, ElimVarsLists), list.condense(ElimVarsLists, ElimVars), delete_var_entries(ElimVars, VarTable0, VarTable1), simplify_info_get_module_info(Info, !:ModuleInfo), % We only eliminate vars that cannot occur in RttiVarMaps. ( if simplify_do_after_front_end(Info) then proc_info_get_var_name_remap(!.ProcInfo, VarNameRemap), RenameVar = ( pred(V::in, N::in, VT0::in, VT::out) is det :- lookup_var_entry(VT0, V, E0), E = E0 ^ vte_name := N, update_var_entry(V, E, VT0, VT) ), map.foldl(RenameVar, VarNameRemap, VarTable1, VarTable), proc_info_set_var_name_remap(map.init, !ProcInfo), proc_info_get_headvars(!.ProcInfo, HeadVars), proc_info_get_argmodes(!.ProcInfo, ArgModes), find_and_record_any_direct_arg_in_out_posns(PredId, ProcId, VarTable, HeadVars, ArgModes, !ModuleInfo) else VarTable = VarTable1 ), proc_info_set_var_table(VarTable, !ProcInfo), proc_info_set_rtti_varmaps(RttiVarMaps, !ProcInfo), simplify_info_get_has_parallel_conj(Info, HasParallelConj), proc_info_set_has_parallel_conj(HasParallelConj, !ProcInfo), simplify_info_get_has_user_event(Info, HasUserEvent), proc_info_set_has_user_event(HasUserEvent, !ProcInfo), simplify_info_get_deleted_call_callees(Info, CurDeletedCallCallees), proc_info_get_deleted_call_callees(!.ProcInfo, DeletedCallCallees0), set.union(CurDeletedCallCallees, DeletedCallCallees0, DeletedCallCallees), proc_info_set_deleted_call_callees(DeletedCallCallees, !ProcInfo), !:Specs = FormatSpecs ++ !.Specs, simplify_proc_maybe_warn_attribute_conflict(!.ModuleInfo, PredId, !.ProcInfo, !Specs), pred_info_get_status(PredInfo0, Status), IsDefinedHere = pred_status_defined_in_this_module(Status), ( IsDefinedHere = no, % Don't generate any warnings or even errors if the predicate isn't % defined here; any such messages will be generated when we compile % the module the predicate comes from. !:Specs = [] ; IsDefinedHere = yes ). :- pred simplify_proc_maybe_vary_parameters(module_info::in, pred_id::in, proc_info::in, simplify_tasks::in, simplify_tasks::out) is det. simplify_proc_maybe_vary_parameters(ModuleInfo, PredId, ProcInfo, !SimplifyTasks) :- module_info_get_globals(ModuleInfo, Globals), globals.lookup_string_option(Globals, debug_common_struct_preds, DebugCommonStructPreds), ( if DebugCommonStructPreds = "" then TurnOffCommonStructByRequest = no else CommonStructPredIdStrs = string.split_at_char(',', DebugCommonStructPreds), ( if list.map(string.to_int, CommonStructPredIdStrs, CommonStructPredIdInts) then PredIdInt = pred_id_to_int(PredId), ( if list.member(PredIdInt, CommonStructPredIdInts) then TurnOffCommonStructByRequest = no else TurnOffCommonStructByRequest = yes ) else TurnOffCommonStructByRequest = no ) ), proc_info_get_var_table(ProcInfo, VarTable0), var_table_count(VarTable0, NumVars), ( if ( TurnOffCommonStructByRequest = yes ; NumVars > turn_off_common_struct_threshold ) then !SimplifyTasks ^ do_opt_common_structs := do_not_opt_common_structs else true ). % If we have too many variables, common_struct used to take so long that % either the compiler runs out of memory, or the user runs out of patience. % In such cases, the fact that we would generate better code if the % compilation finished is therefore of limited interest. % % However, since this limit was first imposed, we have optimized % the compiler's infrastructure for such things, e.g. by using much more % compact representations for sets of variables, which permit much faster % operations on them. These changes do not eliminate the danger described % above completely, but they do raise the threshold at which they can % appear. % % As of 2020 october 11, the code of convert_options_to_globals in % handle_options.m has just shy of 9,000 variables at the time of the % first simplify pass (HLDS dump stage 65). The compiler can handle that % easily, so the setting below allows for some growth. % :- func turn_off_common_struct_threshold = int. turn_off_common_struct_threshold = 12000. :- pred simplify_proc_maybe_mark_modecheck_clauses( proc_info::in, proc_info::out) is det. simplify_proc_maybe_mark_modecheck_clauses(!ProcInfo) :- proc_info_get_goal(!.ProcInfo, Goal0), Goal0 = hlds_goal(GoalExpr0, GoalInfo0), ( if ( GoalExpr0 = disj(_) ; GoalExpr0 = switch(_, _, _) ) then goal_info_add_feature(feature_mode_check_clauses_goal, GoalInfo0, GoalInfo), Goal = hlds_goal(GoalExpr0, GoalInfo), proc_info_set_goal(Goal, !ProcInfo) else true ). %---------------------------------------------------------------------------% :- pred simplify_proc_analyze_and_format_calls(io.text_output_stream::in, maybe_generate_implicit_stream_warnings::in, module_info::in, module_info::out, pred_id::in, pred_info::in, proc_id::in, proc_info::in, proc_info::out, list(error_spec)::out) is det. simplify_proc_analyze_and_format_calls(ProgressStream, ImplicitStreamWarnings, !ModuleInfo, PredId, PredInfo0, ProcId, !ProcInfo, FormatSpecs) :- proc_info_get_goal(!.ProcInfo, Goal0), proc_info_get_var_table(!.ProcInfo, VarTable0), analyze_and_optimize_format_calls(ProgressStream, ImplicitStreamWarnings, !.ModuleInfo, PredInfo0, !.ProcInfo, Goal0, MaybeGoal1, FormatSpecs1, VarTable0, VarTable1), ( if had_some_unknown_format_calls(FormatSpecs1), % We found some format calls that we couldn't optimize because % we don't know either the format string or the list of values % to be printed. Try to fix this by moving copies of the format call % into the tail ends of the immediately previous branched control % structure, since this will fix the problem *if* the missing info % is available in each branch. If it is not, then the transformation % does not help, but it does not hurt either. (Even if we get one copy % per branch of e.g. a warning about the format string being unknown, % write_error_spec.m will print only one copy.) % % Note that we do *not* test the value of warn_unknown_format_calls. % Even if the warning is not enabled, knowing the format string % allows us to generate better code. push_format_calls_into_branches_in_goal(!.ModuleInfo, Goal0, Goal1), % Don't call analyze_and_optimize_format_calls again with the same % input goal; the results won't change. not Goal0 = Goal1 then % Repeat the call to analyze_and_optimize_format_calls on the % transformed procedure body, once we have fixed up the goal_infos. % The code here does most of the same things as the % "MaybeGoal = yes(Goal)" case below, though the reasons for % e.g. the nonlocals fields needing recomputation are different. proc_info_set_goal(Goal1, !ProcInfo), proc_info_set_var_table(VarTable1, !ProcInfo), requantify_proc_general(ord_nl_maybe_lambda, !ProcInfo), recompute_instmap_delta_proc(no_recomp_atomics, !ProcInfo, !ModuleInfo), pred_info_set_proc_info(ProcId, !.ProcInfo, PredInfo0, PredInfo1), analyze_and_optimize_format_calls(ProgressStream, ImplicitStreamWarnings, !.ModuleInfo, PredInfo1, !.ProcInfo, Goal1, MaybeGoal, FormatSpecs, VarTable0, VarTable) else MaybeGoal = MaybeGoal1, FormatSpecs = FormatSpecs1, VarTable = VarTable1, PredInfo1 = PredInfo0 ), ( MaybeGoal = yes(Goal), proc_info_set_goal(Goal, !ProcInfo), proc_info_set_var_table(VarTable, !ProcInfo), % The goals we replace format calls with are created with the % correct nonlocals, but analyze_and_optimize_format_calls can % take code for building a list of string.poly_types out of one % scope (e.g. the condition of an if-then-else) and replace it % with code to build the string directly in another scope % (such as the then part of that if-then-else, if that is where % the format call is). This can leave variables missing from % the nonlocal fields of the original scopes. And since % instmap_deltas are restricted to the goal's nonlocals, % they need to be recomputed as well. requantify_proc_general(ord_nl_maybe_lambda, !ProcInfo), recompute_instmap_delta_proc(no_recomp_atomics, !ProcInfo, !ModuleInfo), % Put the new proc_info back into !ModuleInfo, since some of the % following code could otherwise find obsolete information in there. pred_info_set_proc_info(ProcId, !.ProcInfo, PredInfo1, PredInfo2), % Remove the has_format_call marker from the pred_info before % putting it back, since any optimizable format calls will already % have been optimized. Since currently there is no program % transformation that inserts calls to these predicates, % there is no point in trying to optimize format_calls again later. pred_info_get_markers(PredInfo2, Markers2), remove_marker(marker_has_format_call, Markers2, Markers), pred_info_set_markers(Markers, PredInfo2, PredInfo), module_info_set_pred_info(PredId, PredInfo, !ModuleInfo) ; MaybeGoal = no % There should not be any updates to the var_table, % but even if there are, throw them away, since they apply to a version % of the goal that we will not be using. ). :- pred had_some_unknown_format_calls(list(error_spec)::in) is semidet. had_some_unknown_format_calls([]) :- fail. had_some_unknown_format_calls([Spec | Specs]) :- require_complete_switch [Spec] ( Spec = spec(_, Severity, _, _, _), ( if Severity = severity_warning(warn_unknown_format_calls) then true else had_some_unknown_format_calls(Specs) ) ; ( Spec = no_ctxt_spec(_, _, _, _) ; Spec = error_spec(_, _, _, _) ), unexpected($pred, "unexpected form of error_spec") ). :- pred push_format_calls_into_branches_in_goal(module_info::in, hlds_goal::in, hlds_goal::out) is det. push_format_calls_into_branches_in_goal(ModuleInfo, Goal0, Goal) :- Goal0 = hlds_goal(GoalExpr0, GoalInfo0), ( ( GoalExpr0 = unify(_, _, _, _, _) ; GoalExpr0 = generic_call(_, _, _, _, _) ; GoalExpr0 = plain_call(_, _, _, _, _, _) ; GoalExpr0 = call_foreign_proc(_, _, _, _, _, _, _) ), Goal = Goal0 ; GoalExpr0 = conj(ConjType0, Conjuncts0), list.map(push_format_calls_into_branches_in_goal(ModuleInfo), Conjuncts0, Conjuncts1), ( ConjType0 = plain_conj, % It is simpler to have the list.map above process % each conjunct, before this call does the pushing, % Separation-of-concerns works. push_format_calls_into_branches_in_conjunction(ModuleInfo, Conjuncts1, Conjuncts) ; ConjType0 = parallel_conj, Conjuncts = Conjuncts1 ), GoalExpr = conj(ConjType0, Conjuncts), Goal = hlds_goal(GoalExpr, GoalInfo0) ; GoalExpr0 = disj(Disjuncts0), list.map(push_format_calls_into_branches_in_goal(ModuleInfo), Disjuncts0, Disjuncts), GoalExpr = disj(Disjuncts), Goal = hlds_goal(GoalExpr, GoalInfo0) ; GoalExpr0 = switch(Var0, CanFail0, Cases0), list.map(push_format_calls_into_branches_in_case(ModuleInfo), Cases0, Cases), GoalExpr = switch(Var0, CanFail0, Cases), Goal = hlds_goal(GoalExpr, GoalInfo0) ; GoalExpr0 = if_then_else(Vars0, Cond0, Then0, Else0), push_format_calls_into_branches_in_goal(ModuleInfo, Cond0, Cond), push_format_calls_into_branches_in_goal(ModuleInfo, Then0, Then), push_format_calls_into_branches_in_goal(ModuleInfo, Else0, Else), GoalExpr = if_then_else(Vars0, Cond, Then, Else), Goal = hlds_goal(GoalExpr, GoalInfo0) ; GoalExpr0 = negation(SubGoal0), push_format_calls_into_branches_in_goal(ModuleInfo, SubGoal0, SubGoal), GoalExpr = negation(SubGoal), Goal = hlds_goal(GoalExpr, GoalInfo0) ; GoalExpr0 = scope(Reason0, SubGoal0), ( Reason0 = from_ground_term(_, _), Goal = Goal0 ; ( Reason0 = exist_quant(_, _) ; Reason0 = disable_warnings(_, _) ; Reason0 = promise_solutions(_, _) ; Reason0 = promise_purity(_) ; Reason0 = require_detism(_) ; Reason0 = commit(_) ; Reason0 = barrier(_) ; Reason0 = trace_goal(_, _, _, _, _) ; Reason0 = loop_control(_, _, _) ; Reason0 = require_complete_switch(_) ; Reason0 = require_switch_arms_detism(_, _) ), push_format_calls_into_branches_in_goal(ModuleInfo, SubGoal0, SubGoal), GoalExpr = scope(Reason0, SubGoal), Goal = hlds_goal(GoalExpr, GoalInfo0) ) ; GoalExpr0 = shorthand(ShortHand0), ( ShortHand0 = atomic_goal(GoalType0, Outer0, Inner0, MaybeOutputVars0, MainGoal0, OrElseGoals0, OrElseInners0), push_format_calls_into_branches_in_goal(ModuleInfo, MainGoal0, MainGoal), list.map(push_format_calls_into_branches_in_goal(ModuleInfo), OrElseGoals0, OrElseGoals), ShortHand = atomic_goal(GoalType0, Outer0, Inner0, MaybeOutputVars0, MainGoal, OrElseGoals, OrElseInners0) ; ShortHand0 = try_goal(MaybeIO0, ResultVar0, SubGoal0), push_format_calls_into_branches_in_goal(ModuleInfo, SubGoal0, SubGoal), ShortHand = try_goal(MaybeIO0, ResultVar0, SubGoal) ; ShortHand0 = bi_implication(_, _), % These should have been expanded out by now. unexpected($pred, "bi_implication") ), GoalExpr = shorthand(ShortHand), Goal = hlds_goal(GoalExpr, GoalInfo0) ). :- pred push_format_calls_into_branches_in_case(module_info::in, case::in, case::out) is det. push_format_calls_into_branches_in_case(ModuleInfo, Case0, Case) :- Case0 = case(MainConsId0, OtherConsIds0, Goal0), push_format_calls_into_branches_in_goal(ModuleInfo, Goal0, Goal), Case = case(MainConsId0, OtherConsIds0, Goal). %---------------------% % This predicate pushes format calls into the tail ends % of the last branched goal that preceded it in a conjunction. % % The algorithm has two stages. % % The first stage partitions the conjuncts into segments, where % % - each segment consists of a contiguous sequence of the conjuncts % of the original conjunctions, % % - concatenating the contents of the segments together would yield back % the original conjunction, and % % - conjuncts that are either branched goals or format calls can occur % only as the distinguished last conjunct in a segment. % % The result is a sequence of segments that each either in a branched goal % or in a format call, with the last segment ending with the end of % the original conjunction. % % The second stage then looks for situations where a segment that ends with % a branched goal is followed by a segment that ends with a format call. % When it finds one, it copies the latter segment into each branch % of the branched goal in the former segment, calls the result an updated % segment that ends with a branched goal, and then keeps looking for % more such situations. % :- pred push_format_calls_into_branches_in_conjunction(module_info::in, list(hlds_goal)::in, list(hlds_goal)::out) is det. push_format_calls_into_branches_in_conjunction(ModuleInfo, Conjuncts0, Conjuncts) :- segment_conjunction(ModuleInfo, Conjuncts0, cord.init, SegmentsCord, cord.init, LeftOverCord), Segments = cord.list(SegmentsCord), ( Segments = [], Conjuncts = Conjuncts0 ; Segments = [HeadSegment | TailSegments], push_format_segments_into_branched_goals(cord.init, HeadSegment, TailSegments, SegmentGoalsCord), Conjuncts = cord.list(SegmentGoalsCord ++ LeftOverCord) ). %---------------------% % The data structure that the first stage computes and % the second stage processes. Its semantics are explained by % the big comment on push_format_calls_into_branches_in_conjunction. :- type conjunction_segment ---> segment_branched(segment_ends_with_branched) ; segment_format(segment_ends_with_format). :- type segment_ends_with_branched ---> segment_ends_with_branched(cord(hlds_goal), branched_goal). :- type segment_ends_with_format ---> segment_ends_with_format(cord(hlds_goal), hlds_goal). :- type branched_goal_expr =< hlds_goal_expr ---> disj(list(hlds_goal)) ; switch(prog_var, can_fail, list(case)) ; if_then_else(list(prog_var), hlds_goal, hlds_goal, hlds_goal). :- type branched_goal =< hlds_goal ---> hlds_goal(branched_goal_expr, hlds_goal_info). %---------------------% % Partition the given list of conjuncts into a cord of segments, % followed by a cord of leftover goals. % :- pred segment_conjunction(module_info::in, list(hlds_goal)::in, cord(conjunction_segment)::in, cord(conjunction_segment)::out, cord(hlds_goal)::in, cord(hlds_goal)::out) is det. segment_conjunction(_, [], !SegmentsCord, !.AfterLastSegmentCord, LeftOverCord) :- LeftOverCord = !.AfterLastSegmentCord. segment_conjunction(ModuleInfo, [HeadConjunct | TailConjuncts], !SegmentsCord, !.AfterLastSegmentCord, LeftOverCord) :- HeadConjunct = hlds_goal(GoalExpr, GoalInfo), ( ( GoalExpr = unify(_, _, _, _, _) ; GoalExpr = generic_call(_, _, _, _, _) ; GoalExpr = call_foreign_proc(_, _, _, _, _, _, _) ; GoalExpr = negation(_) ; GoalExpr = scope(_, _) ; GoalExpr = shorthand(_) ), cord.snoc(HeadConjunct, !AfterLastSegmentCord), NextConjuncts = TailConjuncts ; GoalExpr = plain_call(CalleePredId, _, ArgVars, _, _, _), module_info_pred_info(ModuleInfo, CalleePredId, CalleePredInfo), ( if is_format_call(CalleePredInfo, ArgVars) then SegmentFormat = segment_ends_with_format(!.AfterLastSegmentCord, HeadConjunct), cord.snoc(segment_format(SegmentFormat), !SegmentsCord), !:AfterLastSegmentCord = cord.init else cord.snoc(HeadConjunct, !AfterLastSegmentCord) ), NextConjuncts = TailConjuncts ; GoalExpr = conj(ConjType, SubConjuncts), ( ConjType = plain_conj, NextConjuncts = SubConjuncts ++ TailConjuncts ; ConjType = parallel_conj, cord.snoc(HeadConjunct, !AfterLastSegmentCord), NextConjuncts = TailConjuncts ) ; ( GoalExpr = disj(_) ; GoalExpr = switch(_, _, _) ; GoalExpr = if_then_else(_, _, _, _) ), SegmentBranched = segment_ends_with_branched(!.AfterLastSegmentCord, coerce(hlds_goal(GoalExpr, GoalInfo))), cord.snoc(segment_branched(SegmentBranched), !SegmentsCord), !:AfterLastSegmentCord = cord.init, NextConjuncts = TailConjuncts ), segment_conjunction(ModuleInfo, NextConjuncts, !SegmentsCord, !.AfterLastSegmentCord, LeftOverCord). % Look for a segment ending in a branched goal followed immediately % by a segment ending in a format call, and then push the latter segment % into each branch of the branched goal ending the former segment. % Keep doing this until there are no such segment pairs are left. % :- pred push_format_segments_into_branched_goals(cord(hlds_goal)::in, conjunction_segment::in, list(conjunction_segment)::in, cord(hlds_goal)::out) is det. push_format_segments_into_branched_goals(!.DoneCord, HeadSegment, TailSegments, AllCord) :- ( HeadSegment = segment_format(SegmentFormat), SegmentFormat = segment_ends_with_format(FormatStartCord, FormatGoal), !:DoneCord = !.DoneCord ++ FormatStartCord, cord.snoc(FormatGoal, !DoneCord), ( TailSegments = [], AllCord = !.DoneCord ; TailSegments = [HeadTailSegment | TailTailSegments], push_format_segments_into_branched_goals(!.DoneCord, HeadTailSegment, TailTailSegments, AllCord) ) ; HeadSegment = segment_branched(SegmentBranched0), SegmentBranched0 = segment_ends_with_branched(BranchedStartCord, BranchedGoal0), ( TailSegments = [], !:DoneCord = !.DoneCord ++ BranchedStartCord, cord.snoc(coerce(BranchedGoal0), !DoneCord), AllCord = !.DoneCord ; TailSegments = [HeadTailSegment | TailTailSegments], ( HeadTailSegment = segment_branched(_), % HeadSegment and HeadTailSegment both end in branched goals. % We only ever push format calls segments into the last % branched goal, so we now consider HeadSegment to be all done. !:DoneCord = !.DoneCord ++ BranchedStartCord, cord.snoc(coerce(BranchedGoal0), !DoneCord), push_format_segments_into_branched_goals(!.DoneCord, HeadTailSegment, TailTailSegments, AllCord) ; HeadTailSegment = segment_format(SegmentFormat), SegmentFormat = segment_ends_with_format(FormatStartCord, FormatGoal), % XXX Consider doing the following merge only if % the nonlocals sets of BranchedGoal0 and FormatGoal overlap. cord.snoc(FormatGoal, FormatStartCord, GoalsToAppendCord), GoalsToAppend = cord.list(GoalsToAppendCord), BranchedGoal0 = hlds_goal(BranchedGoalExpr0, BranchedGoalInfo), ( BranchedGoalExpr0 = disj(Disjuncts0), list.map(append_goals_to_goal(GoalsToAppend), Disjuncts0, Disjuncts), BranchedGoalExpr = disj(Disjuncts) ; BranchedGoalExpr0 = switch(Var, CanFail, Cases0), list.map(append_goals_to_case(GoalsToAppend), Cases0, Cases), BranchedGoalExpr = switch(Var, CanFail, Cases) ; BranchedGoalExpr0 = if_then_else(Vars0, Cond0, Then0, Else0), append_goals_to_goal(GoalsToAppend, Then0, Then), append_goals_to_goal(GoalsToAppend, Else0, Else), BranchedGoalExpr = if_then_else(Vars0, Cond0, Then, Else) ), BranchedGoal = hlds_goal(BranchedGoalExpr, BranchedGoalInfo), SegmentBranched = segment_ends_with_branched(BranchedStartCord, BranchedGoal), UpdatedHeadSegment = segment_branched(SegmentBranched), push_format_segments_into_branched_goals(!.DoneCord, UpdatedHeadSegment, TailTailSegments, AllCord) ) ) ). :- pred append_goals_to_goal(list(hlds_goal)::in, hlds_goal::in, hlds_goal::out) is det. append_goals_to_goal(GoalsToAppend, Goal0, Goal) :- Goal0 = hlds_goal(GoalExpr0, GoalInfo), ( if GoalExpr0 = conj(plain_conj, Conjuncts0) then GoalExpr = conj(plain_conj, Conjuncts0 ++ GoalsToAppend) else GoalExpr = conj(plain_conj, [Goal0 | GoalsToAppend]) ), Goal = hlds_goal(GoalExpr, GoalInfo). :- pred append_goals_to_case(list(hlds_goal)::in, case::in, case::out) is det. append_goals_to_case(GoalsToAppend, Case0, Case) :- Case0 = case(MainConsId, OtherConsIds, Goal0), append_goals_to_goal(GoalsToAppend, Goal0, Goal), Case = case(MainConsId, OtherConsIds, Goal). %---------------------------------------------------------------------------% :- pred simplify_proc_maybe_warn_attribute_conflict(module_info::in, pred_id::in, proc_info::in, list(error_spec)::in, list(error_spec)::out) is det. simplify_proc_maybe_warn_attribute_conflict(ModuleInfo, PredId, ProcInfo, !Specs) :- module_info_pred_info(ModuleInfo, PredId, PredInfo), pred_info_get_markers(PredInfo, Markers), % The alternate goal by definition cannot be a call_foreign_proc. proc_info_get_goal(ProcInfo, Goal), Goal = hlds_goal(GoalExpr, GoalInfo), ( if GoalExpr = call_foreign_proc(Attributes, _, _, _, _, _, _) then Context = goal_info_get_context(GoalInfo), MaybeMayDuplicate = get_may_duplicate(Attributes), ( MaybeMayDuplicate = yes(MayDuplicate), maybe_warn_about_may_duplicate_attributes(MayDuplicate, Markers, Context, !Specs) ; MaybeMayDuplicate = no ), MaybeMayExportBody = get_may_export_body(Attributes), ( MaybeMayExportBody = yes(MayExportBody), maybe_warn_about_may_export_body_attribute(MayExportBody, Markers, Context, !Specs) ; MaybeMayExportBody = no ) else true ). :- pred maybe_warn_about_may_duplicate_attributes(proc_may_duplicate::in, pred_markers::in, prog_context::in, list(error_spec)::in, list(error_spec)::out) is det. maybe_warn_about_may_duplicate_attributes(MayDuplicate, Markers, Context, !Specs) :- ( MayDuplicate = proc_may_duplicate, ( if marker_is_present(Markers, marker_user_marked_no_inline) then AttrPieces = [quote("may_duplicate"), words("attribute")], PragmaPieces = [pragma_decl("no_inline"), words("declaration")], Pieces = [words("Error: the")] ++ color_as_inconsistent(AttrPieces) ++ [words("on the foreign_proc is")] ++ color_as_incorrect([words("not compatible")]) ++ [words("with the")] ++ color_as_inconsistent(PragmaPieces) ++ [words("on the predicate."), nl], Spec = spec($pred, severity_error, phase_simplify(report_in_any_mode), Context, Pieces), !:Specs = [Spec | !.Specs] else true ) ; MayDuplicate = proc_may_not_duplicate, ( if marker_is_present(Markers, marker_user_marked_inline) then AttrPieces = [quote("may_not_duplicate"), words("attribute")], PragmaPieces = [pragma_decl("inline"), words("declaration")], Pieces = [words("Error: the")] ++ color_as_inconsistent(AttrPieces) ++ [words("on the foreign_proc is")] ++ color_as_incorrect([words("not compatible")]) ++ [words("with the")] ++ color_as_inconsistent(PragmaPieces) ++ [words("on the predicate."), nl], Spec = spec($pred, severity_error, phase_simplify(report_in_any_mode), Context, Pieces), !:Specs = [Spec | !.Specs] else true ) ). :- pred maybe_warn_about_may_export_body_attribute(proc_may_export_body::in, pred_markers::in, prog_context::in, list(error_spec)::in, list(error_spec)::out) is det. maybe_warn_about_may_export_body_attribute(MayExportBody, Markers, Context, !Specs) :- ( MayExportBody = proc_may_export_body, ( if marker_is_present(Markers, marker_user_marked_no_inline) then AttrPieces = [quote("may_export_body"), words("attribute")], PragmaPieces = [pragma_decl("inline"), words("declaration")], Pieces = [words("Error: the")] ++ color_as_inconsistent(AttrPieces) ++ [words("on the foreign_proc is")] ++ color_as_incorrect([words("not compatible")]) ++ [words("with the")] ++ color_as_inconsistent(PragmaPieces) ++ [words("on the predicate."), nl], Spec = spec($pred, severity_error, phase_simplify(report_in_any_mode), Context, Pieces), !:Specs = [Spec | !.Specs] else true ) ; MayExportBody = proc_may_not_export_body % Inlining is allowed within the same target file. ). %---------------------------------------------------------------------------% :- type maybe_allow_splitting_switch_arms ---> do_not_allow_splitting_switch_arms ; allow_splitting_switch_arms. :- pred simplify_top_level_goal(simplify_nested_context::in, instmap::in, maybe_allow_splitting_switch_arms::in, hlds_goal::in, hlds_goal::out, simplify_info::in, simplify_info::out) is det. simplify_top_level_goal(NestedContext0, InstMap0, AllowSplitSwitchArms, !Goal, !Info) :- % Simplification is done in two main passes, which is we have two calls % to do_simplify_top_level_goal below. The first pass performs common % structure and duplicate call elimination. The second pass performs % excess assignment elimination, and cleans up the code after the % first pass. % % Two passes are required because some parts of the first pass can % obsolete existing nonlocals sets and instmap deltas, while some parts % of the second pass may need this information to be up-to-date. % Therefore we have to be prepared to recompute this information % between the two passes. % % After the two main passes that both invoke do_simplify_top_level_goal, % we have some optional other passes as well. some [!SimplifyTasks] ( simplify_info_get_simplify_tasks(!.Info, !:SimplifyTasks), OriginalSimplifyTasks = !.SimplifyTasks, ( if ( !.SimplifyTasks ^ do_opt_common_structs = opt_common_structs ; !.SimplifyTasks ^ do_opt_const_structs = opt_const_structs ; !.SimplifyTasks ^ do_opt_duplicate_calls = opt_dup_calls ) then !SimplifyTasks ^ do_mark_code_model_changes := do_not_mark_code_model_changes, !SimplifyTasks ^ do_excess_assign := do_not_elim_excess_assigns, simplify_info_set_simplify_tasks(!.SimplifyTasks, !Info), % PASS 1. do_simplify_top_level_goal(NestedContext0, InstMap0, !Goal, !Info), !:SimplifyTasks = OriginalSimplifyTasks, % Disable the generation of error, warning and info messages % for the second pass. % - We can do this because any warnings we want to generate % will have been generated by the first pass. % - And we *must* do this, because the excess assign transformation % in pass 2 may replace a variable (say X) with a different % variable (say Y). So if in pass 1, we generated e.g. a message % involving X, then in pass 2 we would generate a version % of that same message, but referring to Y instead of X, % which is very likely to confuse the reader. simplify_info_set_allow_messages(do_not_allow_messages, !Info), % If requested, these tasks were done in pass 1. Repeating them % in pass 2 would serve no purpose, since we do nothing in pass 1 % that would generate new occurrences of the situations % that these tasks seek to optimize. !SimplifyTasks ^ do_opt_common_structs := do_not_opt_common_structs, !SimplifyTasks ^ do_opt_const_structs := do_not_opt_const_structs, !SimplifyTasks ^ do_opt_duplicate_calls := do_not_opt_dup_calls, simplify_info_reinit(!.SimplifyTasks, !Info) else % We have not been asked to perform PASS 1. true ), % PASS 2. % In this pass, do excess assignment elimination and % some cleaning up after the common structure pass. do_simplify_top_level_goal(NestedContext0, InstMap0, !Goal, !Info), % OPTIONAL PASS 3. simplify_info_get_switch_arms_to_split(!.Info, ToSplitArms), ( if set.is_non_empty(ToSplitArms), OriginalSimplifyTasks ^ do_switch_split_arms = split_switch_arms, AllowSplitSwitchArms = allow_splitting_switch_arms then simplify_info_get_tvarset(!.Info, TVarSet), simplify_info_get_inst_varset(!.Info, InstVarSet), trace [compile_time(flag("split_switch_arms")), runtime(env("SPLIT_SWITCH_ARMS")), io(!IO)] ( io.stderr_stream(StdErr, !IO), io.write_string(StdErr, "BEFORE split_switch_arms\n", !IO), simplify_info_get_module_info(!.Info, ModuleInfo), simplify_info_get_var_table(!.Info, VarTable), dump_goal_nl(StdErr, ModuleInfo, vns_var_table(VarTable), TVarSet, InstVarSet, !.Goal, !IO) ), split_switch_arms_in_goal(ToSplitArms, !Goal), trace [compile_time(flag("split_switch_arms")), runtime(env("SPLIT_SWITCH_ARMS")), io(!IO)] ( io.stderr_stream(StdErr, !IO), io.write_string(StdErr, "AFTER split_switch_arms\n", !IO), simplify_info_get_module_info(!.Info, ModuleInfo), simplify_info_get_var_table(!.Info, VarTable), dump_goal_nl(StdErr, ModuleInfo, vns_var_table(VarTable), TVarSet, InstVarSet, !.Goal, !IO) ), !.Goal = hlds_goal(_, GoalInfo0), simplify_info_set_rerun_quant_instmap_delta(!Info), simplify_info_set_rerun_det(!Info), maybe_recompute_fields_after_top_level_goal(GoalInfo0, InstMap0, !Goal, !Info) else true ), % OPTIONAL PASS 4. ( if simplify_do_after_front_end(!.Info), simplify_info_get_found_contains_trace(!.Info, yes) then set_goal_contains_trace_features_in_goal(!Goal, _ContainsTraceGoal, map.init, _LastNonTraceGoal, [], TraceSpecs), % Note that we ignore the setting of do_not_allow_messages above, % since it applies only to pass 2. simplify_info_get_error_specs(!.Info, Specs0), Specs = TraceSpecs ++ Specs0, simplify_info_set_error_specs(Specs, !Info) else true ) % We do not have to put OriginalSimplifyTasks back into !:Info % at the end, because neither of our two callers will look at % the tasks field of the !:Info value we return. We could return % just the fields of !:Info they *do* look at, but there are % many of them. ). :- pred do_simplify_top_level_goal(simplify_nested_context::in, instmap::in, hlds_goal::in, hlds_goal::out, simplify_info::in, simplify_info::out) is det. do_simplify_top_level_goal(NestedContext0, InstMap0, !Goal, !Info) :- !.Goal = hlds_goal(_, GoalInfo0), simplify_info_get_simplify_tasks(!.Info, SimplifyTasks), Common0 = common_info_init(SimplifyTasks), simplify_goal(!Goal, NestedContext0, InstMap0, Common0, _Common, !Info), maybe_recompute_fields_after_top_level_goal(GoalInfo0, InstMap0, !Goal, !Info). :- pred maybe_recompute_fields_after_top_level_goal(hlds_goal_info::in, instmap::in, hlds_goal::in, hlds_goal::out, simplify_info::in, simplify_info::out) is det. maybe_recompute_fields_after_top_level_goal(GoalInfo0, InstMap0, !Goal, !Info) :- simplify_info_get_rerun_quant_instmap_delta(!.Info, RerunQuantDelta), ( RerunQuantDelta = rerun_quant_instmap_deltas, NonLocals = goal_info_get_nonlocals(GoalInfo0), some [!ModuleInfo, !VarTable, !RttiVarMaps] ( simplify_info_get_tvarset(!.Info, TVarSet), simplify_info_get_inst_varset(!.Info, InstVarSet), simplify_info_get_var_table(!.Info, !:VarTable), trace [compile_time(flag("simplify_recompute_after")), io(!IO)] ( io.stderr_stream(StdErr, !IO), set_of_var.to_sorted_list(NonLocals, NonLocalsList), VarNameSrc = vns_var_table(!.VarTable), NonLocalsStr = mercury_vars_to_string_src(VarNameSrc, print_name_and_num, NonLocalsList), io.write_string(StdErr, "\nBEFORE QUANTIFY\n\n", !IO), io.format(StdErr, "NONLOCALS: %s\n", [s(NonLocalsStr)], !IO), simplify_info_get_module_info(!.Info, TraceModuleInfo), dump_goal_nl(StdErr, TraceModuleInfo, VarNameSrc, TVarSet, InstVarSet, !.Goal, !IO) ), simplify_info_get_rtti_varmaps(!.Info, !:RttiVarMaps), implicitly_quantify_goal_general(ord_nl_maybe_lambda, NonLocals, _, !Goal, !VarTable, !RttiVarMaps), trace [compile_time(flag("simplify_recompute_after")), io(!IO)] ( io.stderr_stream(StdErr, !IO), io.write_string(StdErr, "\nAFTER QUANTIFY\n\n", !IO), simplify_info_get_module_info(!.Info, TraceModuleInfo), VarNameSrc = vns_var_table(!.VarTable), dump_goal_nl(StdErr, TraceModuleInfo, VarNameSrc, TVarSet, InstVarSet, !.Goal, !IO) ), simplify_info_set_var_table(!.VarTable, !Info), simplify_info_set_rtti_varmaps(!.RttiVarMaps, !Info), % Always recompute instmap_deltas for atomic goals - this is safer % in the case where unused variables should no longer be included % in the instmap_delta for a goal. simplify_info_get_module_info(!.Info, !:ModuleInfo), recompute_instmap_delta(recomp_atomics, !.VarTable, InstVarSet, InstMap0, !Goal, !ModuleInfo), simplify_info_set_module_info(!.ModuleInfo, !Info), trace [compile_time(flag("simplify_recompute_after")), io(!IO)] ( io.stderr_stream(StdErr, !IO), io.write_string(StdErr, "\nAFTER INSTMAP DELTAS\n\n", !IO), simplify_info_get_module_info(!.Info, TraceModuleInfo), VarNameSrc = vns_var_table(!.VarTable), dump_goal_nl(StdErr, TraceModuleInfo, VarNameSrc, TVarSet, InstVarSet, !.Goal, !IO) ) ) ; RerunQuantDelta = do_not_rerun_quant_instmap_deltas ), simplify_info_get_rerun_det(!.Info, RerunDet), ( RerunDet = rerun_det, Detism = goal_info_get_determinism(GoalInfo0), det_get_soln_context(Detism, SolnContext), some [!ModuleInfo, !ProcInfo, !VarTable, !RttiVarMaps] ( % det_infer_proc_goal looks up the proc_info in the module_info for % the var_table, so we have to put all the proc_info components % we have updated back in the proc_info, which we have to put back % in the module_info. simplify_info_get_module_info(!.Info, !:ModuleInfo), simplify_info_get_var_table(!.Info, !:VarTable), simplify_info_get_rtti_varmaps(!.Info, !:RttiVarMaps), simplify_info_get_pred_proc_id(!.Info, PredProcId), module_info_pred_proc_info(!.ModuleInfo, PredProcId, PredInfo, !:ProcInfo), proc_info_set_var_table(!.VarTable, !ProcInfo), proc_info_set_rtti_varmaps(!.RttiVarMaps, !ProcInfo), module_info_set_pred_proc_info(PredProcId, PredInfo, !.ProcInfo, !ModuleInfo), simplify_info_set_module_info(!.ModuleInfo, !Info), det_info_init(!.ModuleInfo, PredProcId, !.VarTable, pess_extra_vars_report, [], DetInfo0), det_infer_proc_goal(InstMap0, SolnContext, _Detism, !Goal, DetInfo0, DetInfo), det_info_get_module_info(DetInfo, !:ModuleInfo), det_info_get_var_table(DetInfo, !:VarTable), simplify_info_set_module_info(!.ModuleInfo, !Info), simplify_info_set_var_table(!.VarTable, !Info) ) ; RerunDet = do_not_rerun_det ). % The call to simplify_info_reinit in our caller will reset !:Info % to do_not_rerun_quant_instmap_deltas and do_not_rerun_det before % the next pass, if there is a next pass. %---------------------------------------------------------------------------% :- end_module check_hlds.simplify.simplify_proc. %---------------------------------------------------------------------------%