%---------------------------------------------------------------------------% % vim: ft=mercury ff=unix ts=4 sw=4 et %---------------------------------------------------------------------------% % Copyright (C) 2006-2012 The University of Melbourne. % Copyright (C) 2017 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: structure_reuse.indirect.m. % Main authors: nancy, wangp. % % Determine the indirect reuse. This requires a fixpoint computation. % %---------------------------------------------------------------------------% :- module transform_hlds.ctgc.structure_reuse.indirect. :- interface. :- import_module hlds. :- import_module hlds.hlds_module. :- import_module hlds.hlds_pred. :- import_module transform_hlds.ctgc.structure_reuse.domain. :- import_module transform_hlds.ctgc.structure_sharing. :- import_module transform_hlds.ctgc.structure_sharing.domain. :- import_module set. %---------------------------------------------------------------------------% % Represents a request to perform analyses of a procedure with % restriction on which arguments may be clobbered. % :- type sr_request ---> sr_request( srreq_ppid :: pred_proc_id, srreq_args :: no_clobber_args ). % Direct reuse analysis derives information about deconstructions that % under certain circumstances (formalised as "reuse conditions") form % the last ever (memory) access to the deconstructed term. % % Indirect reuse analysis is about verifying procedure calls to see % whether these procedure calls satisfy these reuse conditions for % direct reuse, and under what circumstances (again expressed as % reuse conditions). Using a fixpoint computation, we determine the % overall reuse conditions for a procedure call to be replaced by a % call to an optimised version of that procedure w.r.t. its memory usage. % % The results of the analysis are primarily stored in the reuse table, yet % also involves annotations at the level of the individual procedure calls, % which explains the need for updating the HLDS as well. % % Returns requests for analyses of procedures with specific call patterns, % both for procedures defined in this module and externally. % :- pred indirect_reuse_pass(sharing_as_table::in, module_info::in, module_info::out, reuse_as_table::in, reuse_as_table::out, set(ppid_no_clobbers)::out, set(sr_request)::out, set(sr_request)::out) is det. % Repeat the indirect structure reuse analysis. % :- pred indirect_reuse_rerun(sharing_as_table::in, module_info::in, module_info::out, reuse_as_table::in, reuse_as_table::out, set(ppid_no_clobbers)::out, set(sr_request)::out, set(sr_request)::in, set(sr_request)::out) is det. %---------------------------------------------------------------------------% %---------------------------------------------------------------------------% :- implementation. :- import_module analysis. :- import_module check_hlds. :- import_module check_hlds.type_util. :- import_module hlds.hlds_dependency_graph. :- import_module hlds.hlds_goal. :- import_module hlds.hlds_out. :- import_module hlds.hlds_out.hlds_out_util. :- import_module hlds.pred_name. :- import_module hlds.status. :- import_module libs. :- import_module libs.dependency_graph. :- import_module libs.globals. :- import_module libs.options. :- import_module parse_tree. :- import_module parse_tree.parse_tree_out_misc. :- import_module parse_tree.parse_tree_out_term. :- import_module parse_tree.prog_data. :- import_module parse_tree.prog_data_pragma. :- import_module parse_tree.set_of_var. :- import_module parse_tree.var_table. :- import_module transform_hlds.ctgc.datastruct. :- import_module transform_hlds.ctgc.fixpoint_table. :- import_module transform_hlds.ctgc.livedata. :- import_module transform_hlds.ctgc.util. :- import_module bimap. :- import_module bool. :- import_module int. :- import_module io. :- import_module list. :- import_module map. :- import_module maybe. :- import_module require. :- import_module solutions. :- import_module string. %---------------------------------------------------------------------------% :- type dep_procs == set(ppid_no_clobbers). %---------------------------------------------------------------------------% indirect_reuse_pass(SharingTable, !ModuleInfo, !ReuseTable, DepProcs, Requests, IntermodRequests) :- % Perform a bottom-up traversal of the SCCs in the module, % analysing indirect structure reuse in each one as we go. module_info_ensure_dependency_info(!ModuleInfo, DepInfo), SCCs = dependency_info_get_bottom_up_sccs(DepInfo), list.foldl5(indirect_reuse_analyse_scc(SharingTable), SCCs, !ModuleInfo, !ReuseTable, set.init, DepProcs, set.init, Requests, set.init, IntermodRequests). :- pred indirect_reuse_analyse_scc(sharing_as_table::in, scc::in, module_info::in, module_info::out, reuse_as_table::in, reuse_as_table::out, dep_procs::in, dep_procs::out, set(sr_request)::in, set(sr_request)::out, set(sr_request)::in, set(sr_request)::out) is det. indirect_reuse_analyse_scc(SharingTable, SCC, !ModuleInfo, !ReuseTable, !DepProcs, !Requests, !IntermodRequests) :- set.to_sorted_list(SCC, SCCProcs), ( if some_preds_require_no_analysis(!.ModuleInfo, SCC) then true else FixpointTable0 = sr_fixpoint_table_init(SCCProcs, !.ReuseTable), indirect_reuse_analyse_scc_until_fixpoint(SharingTable, SCCProcs, !.ReuseTable, !ModuleInfo, FixpointTable0, FixpointTable, !DepProcs, !Requests, !IntermodRequests), set.foldl(update_reuse_in_table(FixpointTable), SCC, !ReuseTable) ). :- pred update_reuse_in_table(sr_fixpoint_table::in, pred_proc_id::in, reuse_as_table::in, reuse_as_table::out) is det. update_reuse_in_table(FixpointTable, PPId, !ReuseTable) :- FinalAs = sr_fixpoint_table_get_final_as(PPId, FixpointTable), reuse_as_table_set(PPId, FinalAs, !ReuseTable). %---------------------------------------------------------------------------% indirect_reuse_rerun(SharingTable, !ModuleInfo, !ReuseTable, DepProcs, Requests, !IntermodRequests) :- module_info_rebuild_dependency_info(!ModuleInfo, DepInfo), SCCs = dependency_info_get_bottom_up_sccs(DepInfo), list.foldl5(indirect_reuse_rerun_analyse_scc(SharingTable), SCCs, !ModuleInfo, !ReuseTable, set.init, DepProcs, set.init, Requests, !IntermodRequests). :- pred indirect_reuse_rerun_analyse_scc(sharing_as_table::in, scc::in, module_info::in, module_info::out, reuse_as_table::in, reuse_as_table::out, dep_procs::in, dep_procs::out, set(sr_request)::in, set(sr_request)::out, set(sr_request)::in, set(sr_request)::out) is det. indirect_reuse_rerun_analyse_scc(SharingTable, SCC, !ModuleInfo, !ReuseTable, !DepProcs, !Requests, !IntermodRequests) :- ( if some_preds_require_no_analysis(!.ModuleInfo, SCC) then true else % Also analyse reuse versions of any procedures in the SCC % at the same time. set.to_sorted_list(SCC, SCCProcs), extend_scc_with_reuse_procs(!.ReuseTable, SCCProcs, ExtendedSCC), FixpointTable0 = sr_fixpoint_table_init(ExtendedSCC, !.ReuseTable), indirect_reuse_analyse_scc_until_fixpoint(SharingTable, ExtendedSCC, !.ReuseTable, !ModuleInfo, FixpointTable0, FixpointTable, !DepProcs, !Requests, !IntermodRequests), list.foldl(update_reuse_in_table(FixpointTable), ExtendedSCC, !ReuseTable) ). :- pred extend_scc_with_reuse_procs(reuse_as_table::in, list(pred_proc_id)::in, list(pred_proc_id)::out) is det. extend_scc_with_reuse_procs(ReuseTable, SCC, ExtendedSCC) :- ReuseVersionMap = bimap.forward_map(ReuseTable ^ reuse_version_map), solutions( ( pred(NewPPId::out) is nondet :- member(OrigPPId, SCC), map.member(ReuseVersionMap, ppid_no_clobbers(OrigPPId, _), NewPPId) ), Extension), ExtendedSCC = SCC ++ Extension. %---------------------------------------------------------------------------% :- pred indirect_reuse_analyse_scc_until_fixpoint(sharing_as_table::in, list(pred_proc_id)::in, reuse_as_table::in, module_info::in, module_info::out, sr_fixpoint_table::in, sr_fixpoint_table::out, dep_procs::in, dep_procs::out, set(sr_request)::in, set(sr_request)::out, set(sr_request)::in, set(sr_request)::out) is det. indirect_reuse_analyse_scc_until_fixpoint(SharingTable, SCC, ReuseTable, !ModuleInfo, !FixpointTable, !DepProcs, !Requests, !IntermodRequests) :- list.foldl5(indirect_reuse_analyse_pred_proc(SharingTable, ReuseTable), SCC, !ModuleInfo, !FixpointTable, !DepProcs, !Requests, !IntermodRequests), ( if sr_fixpoint_table_stable(!.FixpointTable) then true else sr_fixpoint_table_new_run(!FixpointTable), disable_warning [suspicious_recursion] ( indirect_reuse_analyse_scc_until_fixpoint(SharingTable, SCC, ReuseTable, !ModuleInfo, !FixpointTable, !DepProcs, !Requests, !IntermodRequests) ) ). %---------------------------------------------------------------------------% :- pred indirect_reuse_analyse_pred_proc(sharing_as_table::in, reuse_as_table::in, pred_proc_id::in, module_info::in, module_info::out, sr_fixpoint_table::in, sr_fixpoint_table::out, dep_procs::in, dep_procs::out, set(sr_request)::in, set(sr_request)::out, set(sr_request)::in, set(sr_request)::out) is det. indirect_reuse_analyse_pred_proc(SharingTable, ReuseTable, PPId, !ModuleInfo, !FixpointTable, !DepProcs, !Requests, !IntermodRequests) :- PPId = proc(PredId, _), module_info_pred_info(!.ModuleInfo, PredId, PredInfo), pred_info_get_origin(PredInfo, Origin), ( if Origin = origin_compiler(made_for_uci(_, _)) then % We can't analyse compiler generated special predicates. true else indirect_reuse_analyse_pred_proc_2(SharingTable, ReuseTable, PPId, !ModuleInfo, !FixpointTable, !DepProcs, !Requests, !IntermodRequests) ). :- pred indirect_reuse_analyse_pred_proc_2(sharing_as_table::in, reuse_as_table::in, pred_proc_id::in, module_info::in, module_info::out, sr_fixpoint_table::in, sr_fixpoint_table::out, dep_procs::in, dep_procs::out, set(sr_request)::in, set(sr_request)::out, set(sr_request)::in, set(sr_request)::out) is det. indirect_reuse_analyse_pred_proc_2(SharingTable, ReuseTable, PPId, !ModuleInfo, !FixpointTable, !DepProcs, !Requests, !IntermodRequests) :- module_info_get_globals(!.ModuleInfo, Globals), globals.lookup_bool_option(Globals, very_verbose, VeryVerbose), globals.lookup_bool_option(Globals, debug_indirect_reuse, DebugIndirect), PPId = proc(PredId, ProcId), % Some feedback.. Run = sr_fixpoint_table_which_run(!.FixpointTable), ( if ( VeryVerbose = yes ; DebugIndirect = yes ) then trace [io(!IO)] ( io.stderr_stream(StdErr, !IO), ProcStr = pred_proc_id_pair_to_dev_string(!.ModuleInfo, PredId, ProcId), io.format(StdErr, "%% Indirect reuse analysis (run %d) %s\n", [i(Run), s(ProcStr)], !IO) ) else true ), % Some initialisation work... module_info_pred_proc_info(!.ModuleInfo, PPId, PredInfo0, ProcInfo0), proc_info_get_goal(ProcInfo0, Goal0), BaseInfo = ir_background_info_init(!.ModuleInfo, PPId, PredInfo0, ProcInfo0, SharingTable, ReuseTable), IrInfo0 = ir_analysis_info_init(PPId, !.FixpointTable, !.DepProcs, !.Requests, !.IntermodRequests), % The actual analysis of the goal: indirect_reuse_analyse_goal(BaseInfo, Goal0, Goal, IrInfo0, IrInfo), IrInfo = ir_analysis_info(_, _, _, _, !:FixpointTable, !:DepProcs, !:Requests, !:IntermodRequests), % Some feedback. ( if ( VeryVerbose = yes ; DebugIndirect = yes ) then trace [io(!IO)] ( io.stderr_stream(StdErr, !IO), Desc = sr_fixpoint_table_get_short_description(PPId, !.FixpointTable), io.format(StdErr, "%% FPT: %s\n", [s(Desc)], !IO), NumConditions = reuse_as_count_conditions(IrInfo ^ ira_reuse_as), io.format(StdErr, "%% Number of conditions: %d\n", [i(NumConditions)], !IO) ) else true ), % Record the obtained reuse description in the fixpoint table... ReuseAs_Status = reuse_as_and_status(IrInfo ^ ira_reuse_as, IrInfo ^ ira_analysis_status), sr_fixpoint_table_new_as(!.ModuleInfo, ProcInfo0, PPId, ReuseAs_Status, !FixpointTable), % As the analysis changes the goal, we must update proc_info and % module_info: proc_info_set_goal(Goal, ProcInfo0, ProcInfo), module_info_set_pred_proc_info(PPId, PredInfo0, ProcInfo, !ModuleInfo). %---------------------------------------------------------------------------% % The type ir_background_info has the purpose to collect all the necessary % background information needed to be able to perform the indirect reuse % (ir) analysis of a specific procedure. % :- type ir_background_info ---> ir_background_info( irb_module_info :: module_info, irb_pred_proc_id :: pred_proc_id, irb_pred_info :: pred_info, irb_proc_info :: proc_info, irb_sharing_table :: sharing_as_table, irb_reuse_table :: reuse_as_table, irb_headvars :: list(prog_var), irb_max_conditions :: int, irb_very_verbose :: bool, irb_debug_indirect :: bool ). % The type ir_analysis_info gathers the analysis information that may % change from goal to goal. % :- type ir_analysis_info ---> ir_analysis_info( ira_sharing_as :: sharing_as, ira_reuse_as :: reuse_as, ira_analysis_status :: analysis_status, ira_static_vars :: set(prog_var), ira_fptable :: sr_fixpoint_table, ira_dep_procs :: dep_procs, ira_requests :: set(sr_request), % Requests to locally-defined procedures. ira_inter_requests :: set(sr_request) % Requests to imported procedures. ). :- func ir_background_info_init(module_info, pred_proc_id, pred_info, proc_info, sharing_as_table, reuse_as_table) = ir_background_info. ir_background_info_init(ModuleInfo, PPId, PredInfo, ProcInfo, SharingTable, ReuseTable) = BG :- % We don't need to keep track of any information regarding inserted % type-info arguments and alike, so we remove them from the list % of head variables: proc_info_get_headvars(ProcInfo, HeadVars), proc_info_get_var_table(ProcInfo, VarTable), HeadVarsOfInterest = remove_typeinfo_vars(VarTable, HeadVars), module_info_get_globals(ModuleInfo, Globals), globals.lookup_int_option(Globals, structure_reuse_max_conditions, MaxConditions), globals.lookup_bool_option(Globals, very_verbose, VeryVerbose), globals.lookup_bool_option(Globals, debug_indirect_reuse, DebugIndirect), BG = ir_background_info(ModuleInfo, PPId, PredInfo, ProcInfo, SharingTable, ReuseTable, HeadVarsOfInterest, MaxConditions, VeryVerbose, DebugIndirect). :- func ir_analysis_info_init(pred_proc_id, sr_fixpoint_table, dep_procs, set(sr_request), set(sr_request)) = ir_analysis_info. ir_analysis_info_init(PPId, FixpointTable, DepProcs, Requests, IntermodRequests) = Info :- ReuseAs_Sharing = sr_fixpoint_table_get_final_as(PPId, FixpointTable), ReuseAs_Sharing = reuse_as_and_status(ReuseAs, Status), Info = ir_analysis_info(sharing_as_init, ReuseAs, Status, set.init, FixpointTable, DepProcs, Requests, IntermodRequests). % When analysing disjuncts (or switches) each branch yields its own % analysis information. This needs to be combined to form one single % analysis information to continue the analysis with. % :- pred ir_analysis_info_combine(ir_background_info::in, list(ir_analysis_info)::in, sr_fixpoint_table::in, ir_analysis_info::in, ir_analysis_info::out) is det. ir_analysis_info_combine(BaseInfo, IrInfoList, FixpointTable, !IrInfo) :- % If the IrInfoList = [], then the disjunct was simply empty, hence % nothing to be done. Otherwise, compute the lub of each of the components % of ir_analysis_info. ( IrInfoList = [] ; IrInfoList = [_ | _], list.foldl(ir_analysis_info_lub(BaseInfo), IrInfoList, !IrInfo), !IrInfo ^ ira_fptable := FixpointTable ). :- pred ir_analysis_info_lub(ir_background_info::in, ir_analysis_info::in, ir_analysis_info::in, ir_analysis_info::out) is det. ir_analysis_info_lub(BaseInfo, IrInfo0, !IrInfo):- ModuleInfo = BaseInfo ^ irb_module_info, ProcInfo = BaseInfo ^ irb_proc_info, % Lub of the sharing NewSharing = sharing_as_least_upper_bound(ModuleInfo, ProcInfo, !.IrInfo ^ ira_sharing_as, IrInfo0 ^ ira_sharing_as), % Lub of the reuse NewReuse = reuse_as_least_upper_bound(ModuleInfo, ProcInfo, !.IrInfo ^ ira_reuse_as, IrInfo0 ^ ira_reuse_as), % Lub of the analysis status. NewStatus = lub(!.IrInfo ^ ira_analysis_status, IrInfo0 ^ ira_analysis_status), % Union of the static vars NewStaticVars = set.union(!.IrInfo ^ ira_static_vars, IrInfo0 ^ ira_static_vars), % Union of the dependencies. NewDepProcs = set.union(!.IrInfo ^ ira_dep_procs, IrInfo0 ^ ira_dep_procs), % Union of the requests. NewRequests = set.union(!.IrInfo ^ ira_requests, IrInfo0 ^ ira_requests), NewIntermodRequests = set.union(!.IrInfo ^ ira_inter_requests, IrInfo0 ^ ira_inter_requests), % The fixpoint table field is updated in ir_analysis_info_combine. !:IrInfo = ir_analysis_info(NewSharing, NewReuse, NewStatus, NewStaticVars, !.IrInfo ^ ira_fptable, NewDepProcs, NewRequests, NewIntermodRequests). %---------------------------------------------------------------------------% :- pred indirect_reuse_analyse_goal(ir_background_info::in, hlds_goal::in, hlds_goal::out, ir_analysis_info::in, ir_analysis_info::out) is det. indirect_reuse_analyse_goal(BaseInfo, !Goal, !IrInfo) :- ModuleInfo = BaseInfo ^ irb_module_info, PredInfo = BaseInfo ^ irb_pred_info, ProcInfo = BaseInfo ^ irb_proc_info, !.Goal = hlds_goal(GoalExpr0, GoalInfo0), ( GoalExpr0 = conj(ConjType, Goals0), list.map_foldl(indirect_reuse_analyse_goal(BaseInfo), Goals0, Goals, !IrInfo), GoalExpr = conj(ConjType, Goals), !:Goal = hlds_goal(GoalExpr, GoalInfo0) ; GoalExpr0 = plain_call(_, _, _, _, _, _), indirect_reuse_analyse_plain_call(BaseInfo, hlds_goal(GoalExpr0, GoalInfo0), !:Goal, !IrInfo) ; GoalExpr0 = generic_call(GenDetails, CallArgs, Modes, _MaybeArgRegs, _Detism), indirect_reuse_analyse_generic_call(BaseInfo, GenDetails, CallArgs, Modes, GoalInfo0, !IrInfo) ; GoalExpr0 = unify(_, _, _, Unification, _), % Record the statically constructed variables. ( Unification = construct(Var, _, _, _, HowToConstruct, _, _), ( HowToConstruct = construct_statically(_), !IrInfo ^ ira_static_vars := set.insert(!.IrInfo ^ ira_static_vars, Var) ; ( HowToConstruct = construct_dynamically ; HowToConstruct = reuse_cell(_) ; HowToConstruct = construct_in_region(_) ) ) ; ( Unification = deconstruct(_, _, _, _, _, _) ; Unification = assign(_, _) ; Unification = simple_test(_, _) ) ; Unification = complicated_unify(_, _, _), unexpected($pred, "complicated unification") ), OldSharing = !.IrInfo ^ ira_sharing_as, NewSharing = add_unify_sharing(ModuleInfo, ProcInfo, Unification, GoalInfo0, OldSharing), update_sharing_as(BaseInfo, OldSharing, NewSharing, !IrInfo) ; GoalExpr0 = disj(Goals0), list.map2_foldl(indirect_reuse_analyse_disj(BaseInfo, !.IrInfo), Goals0, Goals, IrInfoList, !.IrInfo ^ ira_fptable, NewFixpointTable), ir_analysis_info_combine(BaseInfo, IrInfoList, NewFixpointTable, !IrInfo), GoalExpr = disj(Goals), !:Goal = hlds_goal(GoalExpr, GoalInfo0) ; GoalExpr0 = switch(A, B, Cases0), list.map2_foldl( indirect_reuse_analyse_case(BaseInfo, !.IrInfo), Cases0, Cases, IrInfoList, !.IrInfo ^ ira_fptable, NewFixpointTable), ir_analysis_info_combine(BaseInfo, IrInfoList, NewFixpointTable, !IrInfo), GoalExpr = switch(A, B, Cases), !:Goal = hlds_goal(GoalExpr, GoalInfo0) ; % XXX To check and compare with the theory. GoalExpr0 = negation(_Goal) ; GoalExpr0 = scope(Reason, SubGoal0), % XXX We should special-case the handling of from_ground_term_construct % scopes. indirect_reuse_analyse_goal(BaseInfo, SubGoal0, SubGoal, !IrInfo), GoalExpr = scope(Reason, SubGoal), !:Goal = hlds_goal(GoalExpr, GoalInfo0) ; % Brief sketch: % * IrInfo0 --> IfGoal --> IrInfoIfGoal, % * IrInfoIfGoal --> ThenGoal --> IrInfoThenGoal, % * update IrInfo0 to include the latest state of the fixpoint % table, yields IrInfoElseGoal0 % * IrInfoElseGoal0 --> ElseGoal --> IrInfoElseGoal % * and then compute the lub of IrInfoThenGoal, % and IrInfoElseGoal. Make sure that the result contains % the latest state of the fixpoint table, i.e., the one recorded % in IrInfoElseGoal. GoalExpr0 = if_then_else(A, IfGoal0, ThenGoal0, ElseGoal0), IrInfo0 = !.IrInfo, indirect_reuse_analyse_goal(BaseInfo, IfGoal0, IfGoal, IrInfo0, IrInfoIfGoal), indirect_reuse_analyse_goal(BaseInfo, ThenGoal0, ThenGoal, IrInfoIfGoal, IrInfoThenGoal), IrInfoElseGoal0 = IrInfo0 ^ ira_fptable := IrInfoThenGoal ^ ira_fptable, indirect_reuse_analyse_goal(BaseInfo, ElseGoal0, ElseGoal, IrInfoElseGoal0, IrInfoElseGoal), ir_analysis_info_lub(BaseInfo, IrInfoThenGoal, IrInfoElseGoal, !:IrInfo), GoalExpr = if_then_else(A, IfGoal, ThenGoal, ElseGoal), !:Goal = hlds_goal(GoalExpr, GoalInfo0) ; GoalExpr0 = call_foreign_proc(Attributes, ForeignPredId, ForeignProcId, Args, _ExtraArgs, _MaybeTraceRuntimeCond, _Impl), ForeignPPId = proc(ForeignPredId, ForeignProcId), Context = goal_info_get_context(GoalInfo0), OldSharing = !.IrInfo ^ ira_sharing_as, add_foreign_proc_sharing(ModuleInfo, PredInfo, ProcInfo, ForeignPPId, Attributes, Args, Context, OldSharing, NewSharing), update_sharing_as(BaseInfo, OldSharing, NewSharing, !IrInfo) ; GoalExpr0 = shorthand(_), % These should have been expanded out by now. unexpected($pred, "shorthand") ). :- pred indirect_reuse_analyse_plain_call(ir_background_info::in, hlds_goal::in(goal_plain_call), hlds_goal::out(goal_plain_call), ir_analysis_info::in, ir_analysis_info::out) is det. indirect_reuse_analyse_plain_call(BaseInfo, !Goal, !IrInfo) :- ModuleInfo = BaseInfo ^ irb_module_info, PredInfo = BaseInfo ^ irb_pred_info, ProcInfo = BaseInfo ^ irb_proc_info, SharingTable = BaseInfo ^ irb_sharing_table, !.Goal = hlds_goal(GoalExpr0, GoalInfo0), GoalExpr0 = plain_call(CalleePredId, CalleeProcId, CalleeArgs, _Builtin, _Context, _Sym), Reuse0 = goal_info_get_reuse(GoalInfo0), ( Reuse0 = no_reuse_info, Verify = yes ; Reuse0 = no_possible_reuse, Verify = no ; ( Reuse0 = missed_reuse(_) ; Reuse0 = potential_reuse(_) ; Reuse0 = reuse(_) % It's possible that the called procedure had "unconditional % reuse only" previously but has since gained reuse conditions. ), Verify = yes % For imported procedures, even though we know its reuse information % can't have changed since the last pass (so verification is guaranteed % to succeed a second time), we still need to call % `verify_indirect_reuse' so that its conditions will be added to the % reuse_as. ), ( Verify = yes, % Attempt to limit the number of reuse conditions on a procedure. % If there are too many conditions already, don't make any more % calls to reuse procedures which have conditions on them. MaxConditions = BaseInfo ^ irb_max_conditions, ( if reuse_as_count_conditions(!.IrInfo ^ ira_reuse_as) >= MaxConditions then CondReuseHandling = ignore_conditional_reuse else CondReuseHandling = allow_conditional_reuse ), NoClobbers = [], verify_indirect_reuse(BaseInfo, proc(CalleePredId, CalleeProcId), NoClobbers, CalleeArgs, CondReuseHandling, GoalInfo0, GoalInfo, !IrInfo), !:Goal = hlds_goal(GoalExpr0, GoalInfo) ; Verify = no ), OldSharing = !.IrInfo ^ ira_sharing_as, lookup_sharing_and_comb(ModuleInfo, PredInfo, ProcInfo, SharingTable, CalleePredId, CalleeProcId, CalleeArgs, OldSharing, NewSharing), update_sharing_as(BaseInfo, OldSharing, NewSharing, !IrInfo). :- pred indirect_reuse_analyse_generic_call(ir_background_info::in, generic_call::in, prog_vars::in, list(mer_mode)::in, hlds_goal_info::in, ir_analysis_info::in, ir_analysis_info::out) is det. indirect_reuse_analyse_generic_call(BaseInfo, GenDetails, CallArgs, Modes, GoalInfo, !IrInfo) :- ModuleInfo = BaseInfo ^ irb_module_info, ProcInfo = BaseInfo ^ irb_proc_info, ( ( GenDetails = higher_order(_, _, _, _) ; GenDetails = class_method(_, _, _, _) ), proc_info_get_var_table(ProcInfo, CallerVarTable), lookup_var_types(CallerVarTable, CallArgs, ActualTypes), ( if bottom_sharing_is_safe_approximation_by_args(ModuleInfo, Modes, ActualTypes) then SetToTop = no else SetToTop = yes ) ; ( GenDetails = event_call(_) % XXX too conservative ; GenDetails = cast(_) ), SetToTop = yes ), ( SetToTop = no ; SetToTop = yes, Context = goal_info_get_context(GoalInfo), context_to_string(Context, ContextString), Msg = "generic call (" ++ ContextString ++ ")", OldSharing = !.IrInfo ^ ira_sharing_as, NewSharing = sharing_as_top_sharing_accumulate( top_cannot_improve(Msg), OldSharing), update_sharing_as(BaseInfo, OldSharing, NewSharing, !IrInfo) ). % Analyse each branch of a disjunction with respect to an input % ir_analysis_info, producing a resulting ir_analysis_info, and possibly % updating the state of the sr_fixpoint_table. % :- pred indirect_reuse_analyse_disj(ir_background_info::in, ir_analysis_info::in, hlds_goal::in, hlds_goal::out, ir_analysis_info::out, sr_fixpoint_table::in, sr_fixpoint_table::out) is det. indirect_reuse_analyse_disj(BaseInfo, IrInfo0, Goal0, Goal, IrInfo, !FixpointTable) :- % Replace the state of the fixpoint_table in IrInfo0: NewIrInfo = IrInfo0 ^ ira_fptable := !.FixpointTable, indirect_reuse_analyse_goal(BaseInfo, Goal0, Goal, NewIrInfo, IrInfo), !:FixpointTable = IrInfo ^ ira_fptable. % Similar to indirect_reuse_analyse_disj. :- pred indirect_reuse_analyse_case(ir_background_info::in, ir_analysis_info::in, case::in, case::out, ir_analysis_info::out, sr_fixpoint_table::in, sr_fixpoint_table::out) is det. indirect_reuse_analyse_case(BaseInfo, IrInfo0, Case0, Case, IrInfo, !FixpointTable) :- Case0 = case(MainConsId, OtherConsIds, Goal0), % Replace the state of the fixpoint_table in IrInfo0: NewIrInfo = IrInfo0 ^ ira_fptable := !.FixpointTable, indirect_reuse_analyse_goal(BaseInfo, Goal0, Goal, NewIrInfo, IrInfo), !:FixpointTable = IrInfo ^ ira_fptable, Case = case(MainConsId, OtherConsIds, Goal). :- pred update_sharing_as(ir_background_info::in, sharing_as::in, sharing_as::in, ir_analysis_info::in, ir_analysis_info::out) is det. update_sharing_as(BaseInfo, OldSharing, NewSharing, !IrInfo) :- DebugIndirect = BaseInfo ^ irb_debug_indirect, ( DebugIndirect = yes, trace [io(!IO)] ( ( if sharing_as_is_top(NewSharing), not sharing_as_is_top(OldSharing) then io.stderr_stream(StdErr, !IO), io.write_string(StdErr, "\tsharing is now top\n", !IO) else true ) ) ; DebugIndirect = no ), !IrInfo ^ ira_sharing_as := NewSharing. %---------------------------------------------------------------------------% % % Verification of a reuse calls. % XXX zs: what does that mean? % :- type conditional_reuse_handling ---> allow_conditional_reuse ; ignore_conditional_reuse. :- type verify_indirect_reuse_reason ---> callee_has_no_reuses ; callee_has_only_unconditional_reuse ; current_sharing_is_top ; reuse_is_unsafe(prog_vars) ; reuse_is_unconditional ; reuse_is_conditional. % CalleePPId refers to the original procedure, not the procedure of any % reuse version of another procedure. % :- pred verify_indirect_reuse(ir_background_info::in, pred_proc_id::in, list(int)::in, prog_vars::in, conditional_reuse_handling::in, hlds_goal_info::in, hlds_goal_info::out, ir_analysis_info::in, ir_analysis_info::out) is det. verify_indirect_reuse(BaseInfo, CalleePPId, NoClobbers, CalleeArgs, CondReuseHandling, !GoalInfo, !IrInfo) :- % Find the reuse information of the called procedure in the reuse table: % XXX if we can't find an exact match for NoClobbers, we could try % procedures which have no-clobber sets which are supersets of NoClobbers. lookup_reuse_as(BaseInfo, CalleePPId, NoClobbers, !IrInfo, FormalReuseAs), ( if % If there is no reuse, then nothing can be done. reuse_as_no_reuses(FormalReuseAs) then Reason = callee_has_no_reuses, % Setting `no_possible_reuse' here would have adverse effects % because the reuse_as from `lookup_reuse_as' is not definitive. % It may return something else later. trace [io(!IO)] ( io.stderr_stream(StdErr, !IO), maybe_write_verify_indirect_reuse_reason(StdErr, BaseInfo, CalleePPId, NoClobbers, !.GoalInfo, Reason, !IO) ) else if reuse_as_all_unconditional_reuses(FormalReuseAs) then % With unconditional reuse, we need to mark that the call is always % a reuse call. reuse_as_add_unconditional(!.IrInfo ^ ira_reuse_as, NewReuseAs), !IrInfo ^ ira_reuse_as := NewReuseAs, goal_info_set_reuse(reuse(reuse_call(unconditional_reuse, NoClobbers)), !GoalInfo), trace [io(!IO)] ( io.stderr_stream(StdErr, !IO), maybe_write_verify_indirect_reuse_reason(StdErr, BaseInfo, CalleePPId, NoClobbers, !.GoalInfo, callee_has_only_unconditional_reuse, !IO) ) else ( CondReuseHandling = allow_conditional_reuse, % With a conditional reuse, we need to check the conditions. % If they are satisfied, these conditions need to be translated % to the callers environment. This translation can result in % the reuse being unconditional (this is the case if the reused % data structures are local to the procedure in which the call % appears), or conditional. ( if % If the current sharing is top, then there is no use in % verifying reuse explicitly, as we don't have any information % anymore about existing (and therefore non-existing) sharing % pairs. In this case, reuse is not allowed. sharing_as_is_top(!.IrInfo ^ ira_sharing_as) then goal_info_set_reuse(no_possible_reuse, !GoalInfo), trace [io(!IO)] ( io.stderr_stream(StdErr, !IO), maybe_write_verify_indirect_reuse_reason(StdErr, BaseInfo, CalleePPId, NoClobbers, !.GoalInfo, current_sharing_is_top, !IO) ) else verify_indirect_reuse_conditional(BaseInfo, CalleePPId, NoClobbers, CalleeArgs, FormalReuseAs, !GoalInfo, !IrInfo) ) ; CondReuseHandling = ignore_conditional_reuse, goal_info_set_reuse(no_possible_reuse, !GoalInfo) ) ). :- pred verify_indirect_reuse_conditional(ir_background_info::in, pred_proc_id::in, no_clobber_args::in, prog_vars::in, reuse_as::in, hlds_goal_info::in, hlds_goal_info::out, ir_analysis_info::in, ir_analysis_info::out) is det. verify_indirect_reuse_conditional(BaseInfo, CalleePPId, NoClobbers, CalleeArgs, FormalReuseAs, !GoalInfo, !IrInfo) :- verify_indirect_reuse_for_call(BaseInfo, !.IrInfo, !.GoalInfo, CalleePPId, CalleeArgs, FormalReuseAs, NewAndRenamedReuseAs, NotDeadVars), ( if reuse_as_no_reuses(NewAndRenamedReuseAs) then get_var_indices(NotDeadVars, CalleeArgs, 1, NotDeadArgNums0), NotDeadArgNums = list.sort_and_remove_dups(NotDeadArgNums0 ++ NoClobbers), ( if NotDeadArgNums = NoClobbers then % Don't do anything. Don't even request a new version. goal_info_set_reuse(no_possible_reuse, !GoalInfo), trace [io(!IO)] ( io.stderr_stream(StdErr, !IO), maybe_write_verify_indirect_reuse_reason(StdErr, BaseInfo, CalleePPId, NoClobbers, !.GoalInfo, reuse_is_unsafe(NotDeadVars), !IO) ) else if % If there is already an entry for the callee procedure with the % same set of no-clobber arguments we don't need to make a request. % XXX might we look up the result for the procedures we're % currently analysing, and would that be a problem? reuse_as_table_search_reuse_version_proc( BaseInfo ^ irb_reuse_table, CalleePPId, NotDeadArgNums, _ReusePPId) then verify_indirect_reuse(BaseInfo, CalleePPId, NotDeadArgNums, CalleeArgs, allow_conditional_reuse, !GoalInfo, !IrInfo) else % Request another version of the procedure. add_request(BaseInfo, CalleePPId, NotDeadArgNums, IntraModule, !IrInfo), ( IntraModule = yes, goal_info_set_reuse(no_reuse_info, !GoalInfo) ; IntraModule = no, goal_info_set_reuse(no_possible_reuse, !GoalInfo) ), trace [io(!IO)] ( io.stderr_stream(StdErr, !IO), maybe_write_verify_indirect_reuse_reason(StdErr, BaseInfo, CalleePPId, NoClobbers, !.GoalInfo, reuse_is_unsafe(NotDeadVars), !IO) ) ) else if reuse_as_all_unconditional_reuses(NewAndRenamedReuseAs) then % Update reuse information and goal_info: reuse_as_add_unconditional(!.IrInfo ^ ira_reuse_as, NewReuseAs), !IrInfo ^ ira_reuse_as := NewReuseAs, goal_info_set_reuse(reuse(reuse_call(unconditional_reuse, NoClobbers)), !GoalInfo), trace [io(!IO)] ( io.stderr_stream(StdErr, !IO), maybe_write_verify_indirect_reuse_reason(StdErr, BaseInfo, CalleePPId, NoClobbers, !.GoalInfo, reuse_is_unconditional, !IO) ) else if reuse_as_conditional_reuses(NewAndRenamedReuseAs) then % Update reuse information and goal_info: reuse_as_least_upper_bound(BaseInfo ^ irb_module_info, BaseInfo ^ irb_proc_info, !.IrInfo ^ ira_reuse_as, NewAndRenamedReuseAs, NewReuseAs), !IrInfo ^ ira_reuse_as := NewReuseAs, goal_info_set_reuse( potential_reuse(reuse_call(conditional_reuse, NoClobbers)), !GoalInfo), trace [io(!IO)] ( io.stderr_stream(StdErr, !IO), maybe_write_verify_indirect_reuse_reason(StdErr, BaseInfo, CalleePPId, NoClobbers, !.GoalInfo, reuse_is_conditional, !IO) ) else unexpected($pred, "unknown NewReuseAs") ). % Verify whether the caller's environment satisfies the reuse conditions % stated in the reuse description of the called procedure. If this % succeeds, then translate those reuse conditions to this caller's % environment. % % Pre-conditions: The sharing is not top, and reuse_as contains at least % one conditional reuse condition. % :- pred verify_indirect_reuse_for_call(ir_background_info::in, ir_analysis_info::in, hlds_goal_info::in, pred_proc_id::in, list(prog_var)::in, reuse_as::in, reuse_as::out, prog_vars::out) is det. verify_indirect_reuse_for_call(BaseInfo, IrInfo, GoalInfo, CalleePPId, CalleeArgs, FormalReuseAs, NewReuseAs, NotDeadVars) :- ModuleInfo = BaseInfo ^ irb_module_info, PredInfo = BaseInfo ^ irb_pred_info, ProcInfo = BaseInfo ^ irb_proc_info, SharingAs = IrInfo ^ ira_sharing_as, proc_info_get_var_table(ProcInfo, ActualVarTable), pred_info_get_typevarset(PredInfo, CallerTypeVarSet), pred_info_get_univ_quant_tvars(PredInfo, CallerHeadTypeParams), lookup_var_types(ActualVarTable, CalleeArgs, CalleeTypes), reuse_as_rename_using_module_info(ModuleInfo, CalleePPId, CalleeArgs, CalleeTypes, CallerTypeVarSet, CallerHeadTypeParams, FormalReuseAs, ActualReuseAs), LiveData = livedata_init_at_goal(ModuleInfo, ActualVarTable, GoalInfo, SharingAs), ProjectedLiveData = livedata_project(CalleeArgs, LiveData), StaticVars = set.to_sorted_list(IrInfo ^ ira_static_vars), reuse_as_satisfied(ModuleInfo, ProcInfo, ProjectedLiveData, SharingAs, StaticVars, ActualReuseAs, Result), ( Result = reuse_possible, LFU = goal_info_get_lfu(GoalInfo), LBU = goal_info_get_lbu(GoalInfo), LU = set_of_var.union(LFU, LBU), LuList = set_of_var.to_sorted_list(LU), LuData = list.map(datastruct_init, LuList), NewReuseAs = reuse_as_from_called_procedure_to_local_reuse_as( ModuleInfo, ProcInfo, BaseInfo ^ irb_headvars, LuData, SharingAs, ActualReuseAs), NotDeadVars = [] ; Result = reuse_not_possible(Reason), NewReuseAs = reuse_as_init, % no reuse ( ( Reason = no_reuse ; Reason = unknown_livedata ), NotDeadVars = [] ; Reason = reuse_condition_violated(NotDeadVars) ; Reason = reuse_nodes_have_sharing(NotDeadVars) ) ). :- pred lookup_reuse_as(ir_background_info::in, pred_proc_id::in, list(int)::in, ir_analysis_info::in, ir_analysis_info::out, reuse_as::out) is det. lookup_reuse_as(BaseInfo, OrigPPId, NoClobbers, !IrInfo, ReuseAs) :- ( if reuse_as_table_search_reuse_version_proc(BaseInfo ^ irb_reuse_table, OrigPPId, NoClobbers, PPId) then lookup_reuse_as_2(BaseInfo, OrigPPId, PPId, NoClobbers, !IrInfo, ReuseAs) else if NoClobbers = [] then lookup_reuse_as_2(BaseInfo, OrigPPId, OrigPPId, NoClobbers, !IrInfo, ReuseAs) else unexpected($pred, "conditions failed") ). :- pred lookup_reuse_as_2(ir_background_info::in, pred_proc_id::in, pred_proc_id::in, list(int)::in, ir_analysis_info::in, ir_analysis_info::out, reuse_as::out) is det. lookup_reuse_as_2(BaseInfo, OrigPPId, PPId, NoClobbers, !IrInfo, ReuseAs) :- ( if % Check in the fixpoint table sr_fixpoint_table_get_as(PPId, ReuseAs_Status0, !.IrInfo ^ ira_fptable, NewFixpointTable) then ReuseAs_Status = ReuseAs_Status0, !IrInfo ^ ira_fptable := NewFixpointTable else % Or check in the reuse table ReuseAs_Status = get_reuse_as(BaseInfo ^ irb_reuse_table, PPId) ), ReuseAs_Status = reuse_as_and_status(ReuseAs, Status), % Combine the status of the reuse information with the status of the % current analysis. !IrInfo ^ ira_analysis_status := lub(Status, !.IrInfo ^ ira_analysis_status), % If the called procedure was imported (not opt_imported) then remember % that this module depends on the results for that procedure. OrigPPId = proc(CalleePredId, _), module_info_pred_info(BaseInfo ^ irb_module_info, CalleePredId, CalleePredInfo), ( if pred_info_is_imported_not_external(CalleePredInfo), not is_unify_index_or_compare_pred(CalleePredInfo) then Dep = ppid_no_clobbers(OrigPPId, NoClobbers), !IrInfo ^ ira_dep_procs := set.insert(!.IrInfo ^ ira_dep_procs, Dep) else true ). % Output the reasoning behind the result. % :- pred maybe_write_verify_indirect_reuse_reason(io.text_output_stream::in, ir_background_info::in, pred_proc_id::in, list(int)::in, hlds_goal_info::in, verify_indirect_reuse_reason::in, io::di, io::uo) is det. maybe_write_verify_indirect_reuse_reason(Stream, BaseInfo, CalleePPId, NoClobbers, GoalInfo, Reason, !IO) :- DebugIndirect = BaseInfo ^ irb_debug_indirect, ( DebugIndirect = yes, ModuleInfo = BaseInfo ^ irb_module_info, GoalReuse = goal_info_get_reuse(GoalInfo), Context = goal_info_get_context(GoalInfo), proc_info_get_var_table(BaseInfo ^ irb_proc_info, VarTable), CalleeStr = pred_proc_id_to_dev_string(ModuleInfo, CalleePPId), context_to_string(Context, ContextStr), io.format(Stream, "\tcall to %s\n\tfrom %s\n", [s(CalleeStr), s(ContextStr)], !IO), io.format(Stream, "\twith NoClobbers = %s\n", [s(string.string(NoClobbers))], !IO), io.format(Stream, "\t\treuse: %s\n", [s(string.string(GoalReuse))], !IO), io.format(Stream, "\t\treason: %s\n", [s(verify_indirect_reuse_reason_to_string(VarTable, Reason))], !IO) ; DebugIndirect = no ). :- func verify_indirect_reuse_reason_to_string(var_table, verify_indirect_reuse_reason) = string. verify_indirect_reuse_reason_to_string(VarTable, Reason) = Str :- ( ( Reason = callee_has_no_reuses ; Reason = callee_has_only_unconditional_reuse ; Reason = current_sharing_is_top ; Reason = reuse_is_unconditional ; Reason = reuse_is_conditional ), Str = string.string(Reason) ; Reason = reuse_is_unsafe(Vars), VarsStr = mercury_vars_to_string(VarTable, print_name_and_num, Vars), string.format("reuse_is_unsafe(%s)\n", [s(VarsStr)], Str) ). %---------------------------------------------------------------------------% :- pred get_var_indices(prog_vars::in, prog_vars::in, int::in, list(int)::out) is det. get_var_indices(_, [], _, []). get_var_indices(List, [Var | Vars], Index, Indices) :- get_var_indices(List, Vars, Index + 1, Indices0), ( if list.member(Var, List) then Indices = [Index | Indices0] else Indices = Indices0 ). % Add an intra- or inter-module request for the called procedure. % :- pred add_request(ir_background_info::in, pred_proc_id::in, list(int)::in, bool::out, ir_analysis_info::in, ir_analysis_info::out) is det. add_request(BaseInfo, CalleePPId, NotDeadArgNums, IntraModule, !IrInfo) :- CalleePPId = proc(CalleePredId, _), ModuleInfo = BaseInfo ^ irb_module_info, module_info_pred_info(ModuleInfo, CalleePredId, PredInfo), pred_info_get_status(PredInfo, PredStatus), ( if ( pred_status_defined_in_this_module(PredStatus) = yes ; PredStatus = pred_status(status_opt_imported) ) then IntraModule = yes, Request = sr_request(CalleePPId, NotDeadArgNums), !IrInfo ^ ira_requests := set.insert(!.IrInfo ^ ira_requests, Request) else IntraModule = no, module_info_get_globals(ModuleInfo, Globals), globals.lookup_bool_option(Globals, intermodule_analysis, IntermoduleAnalysis), ( IntermoduleAnalysis = yes, Request = sr_request(CalleePPId, NotDeadArgNums), !IrInfo ^ ira_inter_requests := set.insert(!.IrInfo ^ ira_inter_requests, Request) ; IntermoduleAnalysis = no ) ). %---------------------------------------------------------------------------% % % Structure reuse fixpoint table % :- type sr_fixpoint_table == fixpoint_table(pred_proc_id, reuse_as_and_status). % Initialise the fixpoint table for the given set of pred_proc_id's. % :- func sr_fixpoint_table_init(list(pred_proc_id), reuse_as_table) = sr_fixpoint_table. sr_fixpoint_table_init(Keys, ReuseTable) = Table :- Table = init_fixpoint_table(get_reuse_as(ReuseTable), Keys). % Add the results of a new analysis pass to the already existing % fixpoint table. % :- pred sr_fixpoint_table_new_run(sr_fixpoint_table::in, sr_fixpoint_table::out) is det. sr_fixpoint_table_new_run(!Table) :- fixpoint_table.new_run(!Table). % The fixpoint table keeps track of the number of analysis passes. % This predicate returns this number. % :- func sr_fixpoint_table_which_run(sr_fixpoint_table) = int. sr_fixpoint_table_which_run(Tin) = fixpoint_table.which_run(Tin). % A fixpoint is reached if all entries in the table are stable, % i.e. haven't been modified by the last analysis pass. % :- pred sr_fixpoint_table_stable(sr_fixpoint_table::in) is semidet. sr_fixpoint_table_stable(Table) :- fixpoint_table.fixpoint_reached(Table). % Give a string description of the state of the fixpoint table. % :- func sr_fixpoint_table_description(sr_fixpoint_table) = string. :- pragma consider_used(func(sr_fixpoint_table_description/1)). sr_fixpoint_table_description(Table) = fixpoint_table.description(Table). % Enter the newly computed structure reuse description for a given % procedure. If the description is different from the one that was % already stored for that procedure, the stability of the fixpoint % table is set to "unstable". % Software error if the procedure is not in the fixpoint table. % :- pred sr_fixpoint_table_new_as(module_info::in, proc_info::in, pred_proc_id::in, reuse_as_and_status::in, sr_fixpoint_table::in, sr_fixpoint_table::out) is det. sr_fixpoint_table_new_as(ModuleInfo, ProcInfo, Id, ReuseAs, !Table) :- add_to_fixpoint_table( reuse_as_and_status_subsumed_by(ModuleInfo, ProcInfo), Id, ReuseAs, !Table). % Retrieve the structure reuse information for a given pred_proc_id. % % If the id is part of the fixpoint table, but does not yet record any % reuse information about that pred_proc_id, then this means that the % set of pred_proc_id's to which the fixpoint table relates is mutually % recursive, hence the table is characterised as recursive. % % If the id is not part of the fixpoint table: fail. % :- pred sr_fixpoint_table_get_as(pred_proc_id::in, reuse_as_and_status::out, sr_fixpoint_table::in, sr_fixpoint_table::out) is semidet. sr_fixpoint_table_get_as(PPId, ReuseAs, !Table) :- get_from_fixpoint_table(PPId, ReuseAs, !Table). :- func sr_fixpoint_table_get_short_description(pred_proc_id, sr_fixpoint_table) = string. sr_fixpoint_table_get_short_description(PPId, Table) = Descr :- ( if fixpoint_table.is_recursive(Table) then Rec = "(rec)" else Rec = "(non-rec)" ), ( if sr_fixpoint_table_get_final_as_semidet(PPId, Table, reuse_as_and_status(ReuseAs, _)) then Descr0 = reuse_as_short_description(ReuseAs) else Descr0 = "-" ), Descr = Descr0 ++ " " ++ Rec. % Retrieve the structure reuse information without changing the table. % To be used after fixpoint has been reached. % Software error if the procedure is not in the table. % :- func sr_fixpoint_table_get_final_as(pred_proc_id, sr_fixpoint_table) = reuse_as_and_status. sr_fixpoint_table_get_final_as(PPId, T) = get_from_fixpoint_table_final(PPId, T). % Same as sr_fixpoint_table_get_final_as, yet fails instead of aborting % if the procedure is not in the table. % :- pred sr_fixpoint_table_get_final_as_semidet(pred_proc_id::in, sr_fixpoint_table::in, reuse_as_and_status::out) is semidet. sr_fixpoint_table_get_final_as_semidet(PPId, T, Elem) :- get_from_fixpoint_table_final_semidet(PPId, T, Elem). %---------------------------------------------------------------------------% :- func get_reuse_as(reuse_as_table, pred_proc_id) = reuse_as_and_status. get_reuse_as(ReuseTable, PPId) = ReuseAs :- ( if reuse_as_table_search(ReuseTable, PPId, ReuseAs0) then ReuseAs = ReuseAs0 else % We assume an unknown answer is `optimal' otherwise we would not be % able to get mutually recursive procedures out of the `suboptimal' % state. ReuseAs = reuse_as_and_status(reuse_as_init, optimal) ). %---------------------------------------------------------------------------% :- end_module transform_hlds.ctgc.structure_reuse.indirect. %---------------------------------------------------------------------------%